phpMyAdmin 在 Swarm 中不能直接 scale,因其依赖 $_SESSION 会话且默认文件存储、blowfish_secret 不统一,需通过外部代理实现 sticky session 并共享 Redis 存储及统一密钥。
phpMyAdmin 在 Swarm 中为什么不能直接 scale?
因为 phpmyadmin 默认用 $_session 存会话,而 swarm 的默认负载均衡(ingress 网络)不保证请求落到同一容器——用户登录后刷新就 403 或跳回登录页。这不是 phpmyadmin 有 bug,是它没设计成无状态部署。
- Swarm 内置的 DNS 轮询 + IPVS 不支持会话亲和(session stickiness)
-
phpMyAdmin的blowfish_secret如果各容器不一致,加密失败直接拒登 - 就算强制
deploy: { replicas: 1 },也失去高可用和横向扩展意义
必须用外部反向代理接管流量入口
Swarm 的 Ingress 网络只负责转发,不处理 cookie 或 header 级别的路由决策。得让 Nginx / Traefik 这类代理来干这事——它们能读 phpMyAdmin 写的 PHPSESSID cookie 并做 sticky route。
- Traefik v2+ 支持
sticky.cookie,但要求后端服务声明traefik.http.services.xxx.loadbalancer.sticky.cookie=true - Nginx 需在 upstream 块里加
ip_hash或hash $cookie_PHPSESSID consistent(后者更准,依赖 phpMyAdmin 实际写这个 cookie) - 别用 Swarm 的
publish暴露 80/443,所有流量先过代理容器或宿主机上的 Nginx
phpMyAdmin 容器必须共享 session 存储
光靠代理粘连还不够:万一某个容器崩了,新请求被分到其他实例,得能续上旧会话。所以不能用默认的文件存储(session.save_handler = files),必须切到 Redis 或 Memcached。
- 在
config.inc.php里加:ini_set('session.save_handler', 'redis'); ini_set('session.save_path', 'tcp://redis:6379'); - 确保所有副本共用同一个 Redis 服务(推荐用 Swarm 外部网络或 overlay 网络连到独立 redis stack)
- 验证是否生效:登录后进
redis-cli执行keys "PHPREDIS_SESSION:*",应能看到 session key - 如果仍失效,检查
session.cookie_httponly和session.cookie_secure是否和代理的 HTTPS 终止配置冲突
Blowfish secret 必须全局统一且不可轮转
$cfg['blowfish_secret'] 是 phpMyAdmin 加密 cookie 和临时 token 的密钥。Swarm 下每个容器若生成随机值,会导致跨实例解密失败,表现为“登录成功但无法操作数据库”或“执行 SQL 后报错 token mismatch”。
- 绝对不要用
RANDOM或openssl rand -base64 32在 docker-compose.yml 的environment里动态生成 - 用固定字符串,通过
configs或secrets注入:configs: - source: pma_blowfish target: /etc/phpmyadmin/blowfish_secret - 在容器启动脚本里读取该文件并写入
config.inc.php,确保所有副本加载完全相同的值
Swarm 本身不理解 Web 应用的会话语义,强行 scale phpMyAdmin 只会让问题从“登不上”变成“登上了却看不见库”。真正要动的不是副本数,是 session 生命周期的归属——把它从容器进程里拎出来,交给 Redis 管,再让代理管路由,剩下的才是可 scale 的部分。
立即学习“PHP免费学习笔记(深入)”;


















