Last updated at: 2024-05-31 Traefik version: traefik:v3.0
如果你只是使用 traefik 反代到指定 container(或者多个 container 也一样),当 container 重启、更新的时候,就会产生下线时间,导致请求断开和一小段时间的 502。
要解决这个问题,就需要使用的滚动更新,而滚动更新是 swarm 才有的功能,我们需要先开启它。
开启它十分简单:
docker swarm init
如果是单机节点,这就完成了。如果是多机那就去找资料吧。
接下来就是要创建 service。
我们使用 docker stack 来管理多个服务,配置文件和 docker-compose.yaml 基本一致。
如下是一个包含了 traefik 服务的例子:
version: '3.7'
networks:
traefik:
external: true
name: over
services:
reverse-proxy:
image: traefik:v3.0
# Enables the web UI and tells Traefik to listen to docker
command: |
--api.insecure=true
--providers.swarm
--providers.swarm.endpoint=unix:///var/run/docker.sock
--entrypoints.web.address=:80
--entrypoints.websecure.address=:443
[email protected]
--certificatesresolvers.myresolver.acme.storage=/data/acme.json
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
ports:
# The HTTP port
- "80:80"
- "443:443"
# The Web UI (enabled by --api.insecure=true)
- "8080:8080"
labels:
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.routers.redirect-https.rule=HOST(`huglight.cn`)"
- "traefik.http.routers.redirect-https.entrypoints=web"
- "traefik.http.routers.redirect-https.middlewares=redirect-to-https"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ~/data/traefix:/data
deploy:
mode: global
restart_policy:
condition: on-failure
delay: 2s
networks:
- traefik
当然啦,更多资料翻翻再官方文档。
你会看到我声明了 network,这是因为我把应用和基础服务(如 db)分成了多个 stack.yaml,这种情况下如果需要互相联通网络就需要声明一个外部的网络,而不是使用默认情况下创建的网络(默认情况下docker 会为每个 stack.yaml 文件创建一个相互隔离的网络)。
在 Docker network 官方文档中也说了,我们不应该使用默认的网络用作生产环境,而是自己创建一个:
https://docs.docker.com/network/network-tutorial-overlay/
如果你是指用于测试,可以跳过这一步。
docker network create --driver=bridge --attachable --internal=false gateway
networks:
traefik:
name: gateway
external: true
services:
penpot-frontend:
image: xxx/weave:latest
networks:
- traefik
默认情况下你的服务配置应该是这样:
services:
weave:
image: xxx/weave:latest
environment:
DB_HOST: pgsql
command: api
volumes:
- ~/data/weave:/data
labels:
- "traefik.http.routers.weave.rule=(HOST(`huglight.cn`)) && (PathPrefix(`/api`) || PathPrefix(`/static`))"
- "traefik.http.routers.weave.priority=2"
- "traefik.http.services.weave.loadbalancer.server.port=8432"
deploy:
restart_policy:
condition: on-failure
delay: 2s
depends_on:
- pgsql
- redis
networks:
- traefik
如果要启动滚动更新,需要在 deploy 下添加上 update_config:
services:
weave:
deploy:
# https://docs.docker.com/compose/compose-file/deploy/
update_config:
monitor: 5s
order: start-first
start-first
的意思是先启动一个 container 然后关闭之前老的 container,这样就能实现 0 下线。
现在就完了吗?我也以为完了,但在测试的时候发现升级服务的时候还是会 502,仔细想了下发现了问题所在:
traefik 通过监听 container 上的 label 来使用路由更新,在更新服务的时候,会生成两个 container,但 traefik 并不知道应该将流量转发到哪个服务,他会负载均衡两个都转发,如果转发到正在关闭的 container 上时就会产生 502 错误。
而如何通知到 traefik 应该下线哪个 container 呢?翻看文档没有发现端倪。
但发现了的 traefik 文档单独有一篇文章讲了 swarm,仔细看了后明白了 traefik 是如何解决上述问题的
traefik 无法知道哪个 container 需要下线,但是 swarm 是知道的,并且 swarm 自己也提供了机制来自动负载多个服务的副本,这就是 overlay 网络。https://docs.docker.com/network/drivers/overlay/
traefik 不应该直接与 container 通讯,而是先链接到 overlay 定义的服务入口然后再由 overlay 网络链接到 container。
要启用这个网络链路,我们需要做两个改动:
services:
reverse-proxy:
image: traefik:v3.0
command: |
--providers.swarm
--providers.swarm.endpoint=unix:///var/run/docker.sock
services:
weave:
deploy:
labels:
- "traefik.http.routers.weave.rule=(HOST(`huglight.cn`)) && (PathPrefix(`/api`) || PathPrefix(`/static`))"
- "traefik.http.services.weave.loadbalancer.server.port=8432"
traefik.docker.lbswarm=true
label这个属性被我不小心漏掉了(在文档最下面),文档中说明了如果设置为 ture,Traefik 将使用 docker swarm 提供的虚拟 IP,而不是容器 IP。这意味着 Traefik 不会执行任何类型的负载平衡,而是将此任务委托给 swarm。
services:
weave:
deploy:
labels:
- "traefik.http.routers.weave.rule=(HOST(`huglight.cn`)) && (PathPrefix(`/api`) || PathPrefix(`/static`))"
- "traefik.http.services.weave.loadbalancer.server.port=8432"
- "traefik.docker.lbswarm=true"
重新部署服务。
通过 docker service inspect -f '{{json .Endpoint.VirtualIPs}}' service_name
来查看这个服务的虚拟 ip。
然后在 Traefik Dashboard 上查看你的服务的 url 是否是上述的 ip 地址。