我正在尝试使用带有docker swarm的traefik,但我在服务更新期间遇到了麻烦。我运行stack deploy
或service update
服务停止了几秒钟
如何重现:
1 - 创建Dockerfile:
FROM jwilder/whoami
RUN echo $(date) > daniel.txt
2 - 构建2个演示图像:
$ docker build -t whoami:01 .
$ docker build -t whoami:02 .
3 - 创建docker-compose.yml:
version: '3.5'
services:
app:
image: whoami:01
ports:
- 81:8000
deploy:
replicas: 2
restart_policy:
condition: on-failure
update_config:
parallelism: 1
failure_action: rollback
labels:
- traefik.enable=true
- traefik.backend=app
- traefik.frontend.rule=Host:localhost
- traefik.port=8000
- traefik.docker.network=web
networks:
- web
reverse-proxy:
image: traefik
command:
- "--api"
- "--docker"
- "--docker.swarmMode"
- "--docker.domain=localhost"
- "--docker.watch"
- "--docker.exposedbydefault=false"
- "--docker.network=web"
deploy:
replicas: 1
restart_policy:
condition: on-failure
update_config:
parallelism: 1
failure_action: rollback
placement:
constraints:
- node.role == manager
networks:
- web
ports:
- 80:80
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
web:
external: true
4 - 部署堆栈:
$ docker stack deploy -c docker-compose.yml stack_name
5 - 获取服务响应的Curl:
$ while true ; do sleep .1; curl localhost; done
你应该看到这样的东西:
I'm adc1473258e9
I'm bc82ea92b560
I'm adc1473258e9
I'm bc82ea92b560
这意味着负载平衡正常
6 - 更新服务
$ docker service update --image whoami:02 got_app
traefik响应Bad Gateway
应该是零停机时间。
怎么解决?
糟糕的网关意味着traefik被配置为转发请求,但它无法到达其配置使用的ip和端口上的容器。导致此问题的常见问题是:
从评论中,这只发生在部署期间,这意味着traefik在准备好接收请求之前或者在它们被停止时正在攻击容器。
您可以使用运行状况检查配置容器,并使用类似于以下内容的Dockerfile通过swarm模式的VIP发送请求:
FROM jwilder/whoami
RUN echo $(date) >/build-date.txt
HEALTHCHECK --start-period=30s --retries=1 CMD wget -O - -q http://localhost:8000
然后在docker-compose.yml中:
labels:
- traefik.enable=true
- traefik.backend=app
- traefik.backend.loadbalancer.swarm=true
...
我还将使用以下选项配置traefik服务:
- "--retry.attempts=2"
- "--forwardingTimeouts.dialTimeout=1s"
但是,traefik将保持连接打开,VIP将继续通过同一连接将所有请求发送到同一后端容器。你可以做的是让traefik自己执行健康检查:
labels:
- traefik.enable=true
- traefik.backend=app
- traefik.backend.healthcheck.path=/
...
我仍然将健康检查留在容器本身上,因此Docker在停止另一个容器之前给出了容器启动时间。并且在traefik服务上保留重试选项,因此对停止容器的任何请求,或者只有健康检查未检测到的容器,都有机会尝试再次尝试。
这是我在我的环境中使用的结果撰写文件:
version: '3.5'
services:
app:
image: test-whoami:1
ports:
- 6081:8000
deploy:
replicas: 2
restart_policy:
condition: on-failure
update_config:
parallelism: 1
failure_action: rollback
labels:
- traefik.enable=true
- traefik.backend=app
- traefik.backend.healthcheck.path=/
- traefik.frontend.rule=Path:/
- traefik.port=8000
- traefik.docker.network=test_web
networks:
- web
reverse-proxy:
image: traefik
command:
- "--api"
- "--retry.attempts=2"
- "--forwardingTimeouts.dialTimeout=1s"
- "--docker"
- "--docker.swarmMode"
- "--docker.domain=localhost"
- "--docker.watch"
- "--docker.exposedbydefault=false"
- "--docker.network=test_web"
deploy:
replicas: 1
restart_policy:
condition: on-failure
update_config:
parallelism: 1
failure_action: rollback
placement:
constraints:
- node.role == manager
networks:
- web
ports:
- 6080:80
- 6880:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
web:
Dockerfile如上所述。更改了映像名称,端口,网络名称等,以避免与我的环境中的其他内容冲突。