部署仓库中 nginx 下游依赖配置改进

在我们基于 docker stack 部署的应用中,nginx 有以下功能

  • 作为前端唯一入口,通过 80 端口暴露服务
  • 通过 proxy_pass,将特定的前缀的请求转发到其他服务
    • /api -> xxxx
    • /doc -> xxxx
    • /job -> xxxx
    • ...

这形成依赖关系:nginx 依赖所有 proxy_pass 配置中的下流服务完成启动,才能开始接受请求。

被依赖的下游容器未正确启动时,前置 nginx 无法提供服务

使用以下内容创建 yaml

version: "3"

networks:
  test:

services:
  nginx:
    image: nginx:1.17
    ports: 
      - 81:80
    networks:
      - test
    volumes: 
      - ./conf.d:/etc/nginx/conf.d
    depends_on: 
      - python

  python:
    image: python
    ports: 
      - 82:80
    networks:
      - test
    entrypoint: ["python", "-u", "-m", "http.server@", "--directory", ".", "80"]
    # entrypoint: ["python", "-u", "-m", "http.server", "--directory", ".", "80"]

修改挂载到 nginx 中的 conf.d/default.conf 文件,添加

    location /python/ {
        proxy_pass http://python:80/;
    }

使用命令 docker stack deploy test -c docker-compose.yml 部署,观察启动状态。

由于 python 的命令参数错误,该容器无法启动,查看服务列表

$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
jhbuarz6ri79        test_nginx          replicated          0/1                 nginx:1.17          *:81->80/tcp
vyv58t3rd3ym        test_python         replicated          0/1                 python:latest       *:82->80/tcp

检查 nginx 启动日志

$ docker service logs test_nginx
test_nginx.1.qy8my321lo8p@docker-desktop    | 2020/07/11 08:35:13 [emerg] 1#1: host not found in upstream "python" in /etc/nginx/conf.d/default.conf:18
test_nginx.1.qy8my321lo8p@docker-desktop    | nginx: [emerg] host not found in upstream "python" in /etc/nginx/conf.d/default.conf:18
test_nginx.1.e4lks4332x68@docker-desktop    | 2020/07/11 08:35:23 [emerg] 1#1: host not found in upstream "python" in /etc/nginx/conf.d/default.conf:18
test_nginx.1.e4lks4332x68@docker-desktop    | nginx: [emerg] host not found in upstream "python" in /etc/nginx/conf.d/default.conf:18

可见无法启动的 python 容器拖累了 nginx。以下情况常常能复现该问题:

  • 应用被初次部署时,部分服务因错误或未完成的配置启动失败,就会导致整体无法运行。
  • 在服务运行中,如果某服务挂掉或资源耗尽,运维人员手动停止对应容器后,nginx 同样可能无法启动,而需要进一步的人工干预。

使用变量与 nameserver 跳过启动阶段的检查

nginx 在遇到域名解析问题时,一般有以下做法

  • 添加 upstream 设置,映射域名与 ip
  • 修改 /etc/resolver.conf 以进行额外的 DNS 设置。

搜索阅读 Nginx will not start with host not found in upstream得知,可以在 proxy_pass 中使用预先声明的变量跳过检查

docker 内置了 DNS 服务,由 docker daemon(127.0.0.11) 提供服务,更新 nginx 配置如下

暂时只考虑默认的网络模式即 bridge 的情况


  + resolver 127.0.0.11 valid=30s ipv6=off;
    
    location /python/ {
      - proxy_pass http://python:80/;
      + set $python_host python;
      + proxy_pass http://$python_host:80/;
    }

重新启动服务,检查运行状态

$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
rs29cu3dacpz        test_nginx          replicated          1/1                 nginx:1.17          *:81->80/tcp
lztoqwurqgh3        test_python         replicated          0/1                 python:latest       *:82->80/tcp

可见运行失败的 python 容器未阻止 nginx 运行。

其他

本质上这里体现了微服务中 dns 的作用,除了进行生硬的 host 映射,我们可以自建 dns 服务;而像 consul 一类使用 dns 功能以提供服务发现能力。

posted @ 2020-08-14 16:20  leoninew  阅读(493)  评论(0编辑  收藏  举报