【问题标题】:How do I make nginx wait for my upstream service to start up in a Docker Swarm?如何让 nginx 等待我的上游服务在 Docker Swarm 中启动?
【发布时间】:2025-11-21 11:30:02
【问题描述】:

我将一个 nginx 代理服务和一个 rails 应用服务部署到一个 docker swarm 中。 nginx 依赖于我的 docker-compose 文件中的应用程序。

我的 nginx.conf 文件将流量定向到我的上游应用程序服务(暴露在端口 3000),就像这样(仅显示上游部分)。

upstream puma {
  server app:3000;
}

我的 docker-compose 文件如下所示:

version: '3.1'

services:

  app:
    image: my/rails-app:latest
    networks:
      - proxy

  web:
    image: my/nginx:1.11.9-alpine
    command: /bin/sh -c "nginx -g 'daemon off;'"
    ports:
      - "80:80"
    depends_on:
      - app
    networks:
      - proxy


networks:

  proxy:
    external: true

我的主机被设置为集群管理器。

这一切都很好 - 没有问题。

然而,即使我的 docker-compose 文件中有一个 depends 部分 - 应用服务可能并不完全 (?)在 nginx 服务启动时准备就绪,因此当上游服务配置部分尝试 DNS 解析“app:3000”时,似乎没有完全找到它。所以当我访问我的网站时,我在我的 nginx 日志中发现以下错误消息:

2017/02/13 10:46:07 [error] 8#8: *6 connect() failed (111: Connection refused) while connecting to upstream, client: 10.255.0.3, server: www.mysite.com, request: "GET / HTTP/1.1", upstream: "http://127.0.53.53:3000/", host: "preprod.local"

如果我杀死正在运行 nginx 服务的 docker 容器,然后 swarm 稍后重新安排它并返回,如果我然后访问相同的 URL,它工作完全正常,并且请求成功地向上传递到 app:3000 .

我怎样才能防止这种情况发生 - 启动时间有点过时并且在 nginx 启动时它还无法正确解析我的名为 app:3000 的群服务 - 相反它正试图通过流量到 IP 地址 ....

顺便说一句 - 如果我重新启动我的虚拟机,也会发生同样的情况 - 当 docker(在 swarm 模式下)再次启动服务时 - 我可能会遇到同样的问题。重启nginx容器即可解决问题。

【问题讨论】:

    标签: ruby-on-rails nginx docker-swarm-mode


    【解决方案1】:

    我已经找到了一种方法来做到这一点 - 这是使用 Dockerfile 或 docker-compose 文件的 HEALTHCHECK 部分。

    首先,似乎 depends_on 选项在部署堆栈时没有真正使用

    docker stack deploy -c docker-compose.yml mystack
    

    集群模式下的 Docker 如果无法正常启动或由于其他原因失败,只会重新启动服务任务。所以 depends_on 选项并没有那么有用。

    所以这最终是我的解决方案,到目前为止效果很好:

    version: '3.1'
    
    services:
    
      app:
        image: my/rails-app:latest
        networks:
          - proxy
    
      web:
        image: my/nginx:1.11.9-alpine
        command: /bin/sh -c "nginx -g 'daemon off;'"
        ports:
          - "80:80"
        networks:
          - proxy
        healthcheck:
            test: ["CMD", "wget", "-qO-", "http://localhost/healthcheck"]
            interval: 5s
            timeout: 3s
            retries: 3
    
    networks:
    
      proxy:
        external: true
    

    所以我所做的是,我尝试从 nginx 服务器访问我的 Rails 应用程序上的路由 - 我创建了一个名为 /healthcheck 的路由,它返回的状态代码为 200。

    所以当我尝试访问它时,结果是失败(应用服务器尚未准备好) - nginx 将重新启动。希望当它再次启动时,应用服务器将可用,并且上游 app:3000 指令将进行正确的 DNS 解析。

    因此,通过这种方式,我将(缺失的)depends_on 行为“破解”在一起,该行为可以在 swarm 模式下工作。

    【讨论】:

    • 您也可以在deploy 部分使用restart 选项。然后 nginx 容器将在 rails 应用程序关闭时重新启动。顺便说一句,你为什么检查http://localhost/healthcheck url 而不是http://app/healthcheckhttp://app/healthcheck 对健康检查是否正确?
    • 我没有尝试过app/healthcheck url - 但我理解它的方式,如果我使用localhost/healthcheck,那么我将通过 nginx 容器访问我的 Rails 应用程序 - 这就是我的连接想检查。所以使用 localhost 版本可能会更健壮一些。作为重启选项 - 我不确定这将如何解决问题 - 你能解释一下吗?
    • > 使用 localhost 版本可能会更健壮一些。我不同意这一点。虽然它有效,但这意味着在所有容器都正确设置之前,几个容器将处于exited 状态。我希望有一个解决方案来确保 nginx 代理准备好之前不会启动,但我想除了使用wait-for-it 或它的兄弟姐妹之外没有其他解决方案。
    • 如果您有多个服务,那将不会真正起作用,而且在部署时不允许在本地和远程使用它
    【解决方案2】:

    depends_on 选项不会等待容器准备好,而是等待容器运行。 https://docs.docker.com/compose/startup-order/

    还有两个选项。

    1. 从 Compose v2.1 开始,可以在 depends_on 选项中包含运行状况检查。 https://docs.docker.com/compose/compose-file/compose-file-v2/#dependson
    2. 您可以使用dockerizewait-for-it 等外部工具执行相同操作。

    【讨论】:

      【解决方案3】:

      您可以使用此图片:https://hub.docker.com/r/atomgraph/nginx

      它支持$UPSTREAM_SERVER$TIMEOUT参数(作为环境变量)。

      【讨论】:

        最近更新 更多