Symfony 7应用Docker Swarm多副本部署登录失败问题排查
问题分析与解决方案
核心原因
你猜的没错,问题出在会话不共享上。Symfony默认用本地文件存储会话,当部署多个backend_app副本时,每个容器都有自己独立的会话文件目录。Nginx默认轮询转发请求到不同副本,登录操作在其中一个容器生成会话后,后续请求可能落到另一个没有该会话的容器,导致登录状态丢失。
另外你的Swarm配置里有个小问题:backend_app的ports字段不需要配置,因为Nginx是通过Swarm服务发现(http://backend_app:80)访问后端,多个副本绑定同一个主机端口8000会引发冲突。
解决方案步骤
1. 配置Symfony使用共享会话存储
推荐用Redis作为共享会话存储,步骤如下:
- 在PHP镜像中安装Redis扩展(如果你的镜像还没装),可以在Dockerfile里加:
RUN pecl install redis && docker-php-ext-enable redis - 修改Symfony的
config/packages/framework.yaml,将会话存储改为Redis:framework: session: handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler cookie_secure: auto cookie_samesite: lax - 添加Redis服务到你的Swarm栈(
swarm_stack.yml):services: # ... 其他服务 redis: image: redis:alpine deploy: replicas: 1 volumes: - redis_data:/data volumes: redis_data:
2. 修正Swarm栈配置
去掉backend_app的ports字段,修改后的swarm_stack.yml:
services: nginx: image: ghcr.io/mygh/nginx:docker deploy: replicas: 1 ports: - "80:80" - "443:443" backend_app: image: ghcr.io/mygh/backend:docker deploy: replicas: 2 # 移除ports字段,避免端口冲突 redis: image: redis:alpine deploy: replicas: 1 volumes: - redis_data:/data volumes: redis_data:
3. 完善Nginx反向代理配置
确保Symfony能正确识别反向代理的请求,避免安全验证失败,修改nginx.conf:
server { listen 80; listen 443 ssl; server_name backendapp.com; # HTTPS相关配置(如果启用) ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://backend_app:80/; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; # 添加协议头,让Symfony知道请求是HTTP还是HTTPS proxy_set_header X-Forwarded-Proto $scheme; # 修正重定向地址 proxy_redirect off; } }
同时在Symfony的config/packages/framework.yaml里配置信任反向代理:
framework: trusted_proxies: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] # 或者直接信任Nginx服务的IP,不过用Swarm的子网更通用
验证
重新部署栈:
docker stack deploy -c swarm_stack.yml your_stack_name
部署后测试登录,多副本环境下会话会统一存储在Redis中,无论请求落到哪个容器都能读取到会话,登录状态就能保持了。
内容的提问来源于stack exchange,提问作者BazylPL
相关产品推荐
相关产品推荐

