【问题标题】:Bad gateway with traefik and docker swarm during service update服务更新期间带有 traefik 和 docker swarm 的网关错误
【发布时间】:2019-09-06 23:14:58
【问题描述】:

我正在尝试将 traefik 与 docker swarm 一起使用,但在服务更新期间遇到了问题。我运行 stack deployservice 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 - 卷曲获取服务响应:

$ 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 响应。

如何解决?

【问题讨论】:

    标签: docker deployment load-balancing docker-swarm traefik


    【解决方案1】:

    错误的网关意味着 traefik 被配置为转发请求,但它无法访问它配置为使用的 ip 和端口上的容器。导致此问题的常见问题是:

    • traefik 和不同 docker 网络上的服务
    • 服务存在于多个网络中,traefik 选择了错误的网络
    • 用于连接容器的端口错误(使用容器端口并确保它正在侦听所有接口,即 0.0.0.0)

    从 cmets 来看,这只发生在部署期间,这意味着 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 服务上保留重试选项,以便对停止容器的任何请求,或者只是健康检查未检测到的请求,都有机会重试。


    这是我在环境中使用的生成的 compose 文件:

    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 如上所述。图像名称、端口、网络名称等已更改,以避免与我的环境中的其他内容发生冲突。

    【讨论】:

    • docker network ls -> 网桥、docker_gwbridge、主机、入口、无、网络。我没有stack_name_web
    • @Daniel woops,再次查看并看到您在外部定义了它。那是覆盖网络吗?您可以连接到一台主机上的 81 端口并访问您在另一台主机上运行的 whoami 容器吗?
    • 另外,网关错误是否会自行消失并且仅在部署期间出现?
    • 是的,web 是一个覆盖网络。我只在一个节点上进行了测试。网关错误仅在服务更新期间出现。
    • @Daniel 在部署过程中出现的错误指向缺少或配置错误的运行状况检查。如果没有运行状况检查,请求会在新容器准备就绪之前发送到它。
    【解决方案2】:

    截至今天(2021 年 6 月)Traefik 无法在更新期间耗尽连接。

    要实现零停机滚动更新,您应该将负载平衡委托给 docker swarm 本身:

    # trafik v2
    # docker-compose.yml
    
    services:
      your_service:
        deploy:
          labels:
            - traefik.docker.lbswarm=true
    

    来自文档:

    启用 Swarm 的内置负载平衡器(仅在 Swarm 模式下相关)。

    如果启用此选项,Traefik 将使用 docker swarm 提供的虚拟 IP,而不是容器 IP。这意味着 Traefik 不会执行任何类型的负载平衡,并将此任务委托给 swarm。

    更多信息:

    https://github.com/traefik/traefik/issues/41

    https://github.com/traefik/traefik/issues/1480

    【讨论】:

    • 我将此标签添加到我的服务中,但在重新启动整个 Swarm 堆栈后,它仍然给我一个严重的网关错误。我张贴在stackoverflow.com/q/68054228/958373
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 2022-10-20
    • 1970-01-01
    相关资源
    最近更新 更多