部署仓库中 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 功能以提供服务发现能力。