如何使用 docker swarm 保持粘性会话(会话持久性)?

Posted

技术标签:

【中文标题】如何使用 docker swarm 保持粘性会话(会话持久性)?【英文标题】:How to maintain sticky session(session persistence) with docker swarm? 【发布时间】:2017-09-09 08:48:51 【问题描述】:

我有一个基于 Java 的 Web 应用程序,它部署在 jboss-10.1.0(wildfly) 中。我正在使用 docker swarm 模式(docker version 1.12.1)来扩展我的应用程序,一切正常,但我面临的唯一问题是会话管理。

现在让我们来看看场景。

我有两个实例正在为我的应用程序运行(即 App1App2)。我正在使用 docker swarm 模式提供的默认负载均衡器和 nginx 将我的应用程序从 chintan.test.com:9080 重定向到 chintan.test.com:80这样我就不需要用我的 url 写下端口,我可以直接使用这个 URL chintan.test.com 访问。

现在默认负载均衡器使用 RR(Round-Robin algorithm) 来服务我的 Web 请求。所以当我第一次访问 chintan.test.com 时,它会转到 App1 实例并显示登录页面,我使用凭据登录,一切正常几分钟后切换到App2,登录页面再次出现。

我可以通过什么方式或工具(应该是开源的)来处理会话?所以至少我登录App1并坚持App1直到我注销。

谢谢!

【问题讨论】:

我强烈建议您不要使用有状态的应用程序,如果您想像现在这样进行扩展。将会话存储在单独的组件中是一种更好的解决方案。 【参考方案1】:

尝试使用 Nginx 和 HA-Proxy,但它们似乎都不能在 SWARM 模式下正常工作。然后我在 Docker Swarm 中使用了 Traefik,它成功了。唯一的限制是 Traefik 应该在管理节点上运行,因为它需要知道添加或删除的新工作节点。即使你扩展服务、添加节点等也不需要重启。

我已经使用 Docker compose version 3 测试了配置,这是最新的并使用 Docker stack deploy 进行部署。Step by step instructions are over here

首先,您需要创建一个 docker-compose.yml(版本 3)并添加负载均衡器 Traefik 映像。看起来是这样的

   loadbalancer:
image: traefik
command: --docker \
  --docker.swarmmode \
  --docker.watch \
  --web \
  --loglevel=DEBUG
ports:
  - 80:80
  - 9090:8080
volumes:
  - /var/run/docker.sock:/var/run/docker.sock
deploy:
  restart_policy:
    condition: any
  mode: replicated
  replicas: 1
  update_config:
    delay: 2s
  placement:
     constraints: [node.role == manager]

然后是您需要会话粘性的图像

whoami:
image: tutum/hello-world
networks:
  - net
ports:
  - "80"
deploy:
  restart_policy:
    condition: any
  mode: replicated
  replicas: 5
  placement:
    constraints: [node.role == worker]
  update_config:
    delay: 2s
  labels:
    - "traefik.docker.network=test_net"
    - "traefik.port=80"
    - "traefik.frontend.rule=PathPrefix:/hello;"
    - "traefik.backend.loadbalancer.sticky=true"

您可以follow this link获取详细说明。

【讨论】:

Sticky 会话目前在 traefik 和 swarm 模式下不起作用:github.com/containous/traefik/issues/1024 我已经测试过了,它对我有用。看看他们的官方文档docs.traefik.io/user-guide/swarm-mode 我什至已经完成了屏幕截图并上传了一个视频。 youtube.com/watch?v=LBG0gnxe7Xc Traefik 不再做 http 层负载均衡,但 docker 会在下一次 tcp 连接时做 tcp 层循环。这看起来可行的原因是浏览器在请求之间保持 tcp 连接打开。 Curl 是一个更好的测试工具。 forums.docker.com/t/swarm-is-not-round-robin-routing-requests/… Traefik 使用 cookie 名称“Traefik_backend”来保留容器 IP 地址,然后在幕后,我猜它会不断通过浏览器将用户发送到同一个容器。你是对的,它不会在 curl 上工作,因为不会调用任何 cookie,但是我们将通过浏览器而不是 curl 向用户出售东西。 :P【参考方案2】:

Docker swarm 目前不支持粘性会话,轮询是通过暴露端口访问服务的唯一方法。

要实现粘性会话,您需要在 docker 内部实现一个反向代理,该代理支持粘性会话并通过容器 id 直接与容器通信(而不是对服务名称进行 DNS 查找,这将再次转到循环负载均衡器)。实现该负载均衡器还需要您实现自己的服务发现工具,以便它知道哪些容器可用。

【讨论】:

感谢@bmitch 的回答!!您能否建议我应该使用哪个反向代理在我的 docker swarm 中使用粘性会话直接与我的容器通信? 有人写了一篇文章直接回答了Chintan的问题并回应了BMitch的评论(文章字面引用它):littlebigextra.com/…【参考方案3】:

我认为在可扩展的环境中实现粘性会话的最佳方式是通过数据库会话存储。这会将您的应用程序与特定节点解开。

我认为这比依赖 Docker Enterprise Sticky 会话 (https://docs.docker.com/datacenter/ucp/2.2/guides/user/services/use-domain-names-to-access-services/#sticky-sessions) 更容易且更具成本效益。

当然,这意味着一些应用程序调整,例如:

出于性能原因尽量减少会话请求(尤其是匿名用户避免 DoS) 我会将会话数据存储在单独的数据库中(不是主应用程序数据库) 会话数据应在一段时间后删除,因此您应该有一些工作定期删除旧会话数据 等

这种技术还可能带来其他好处:

如果您还匹配用户 IP/用户代理,可能会提高安全性 可能会提高性能(因为数据库将能够避免磁盘访问)

希望这会有所帮助。

【讨论】:

以上是关于如何使用 docker swarm 保持粘性会话(会话持久性)?的主要内容,如果未能解决你的问题,请参考以下文章

Swarm 如何存储数据?- 每天5分钟玩转 Docker 容器技术(103)

Swarm 如何存储数据?- 每天5分钟玩转 Docker 容器技术(103)

如何使用 Cloudformation 模板在 AWS Elastic Beanstalk 中包含粘性会话

如何使用未部署在 swarm 中的 docker 容器从 docker swarm 访问服务?

docker swarm英文文档学习-9-使用Docker Configs存储配置数据

如何使用容器对具有(长期)粘性会话的应用程序进行零停机滚动更新