本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2020年05月20日 统计字数: 5227字 阅读时间: 11分钟阅读 本文链接: https://soulteary.com/2020/05/20/nginx-basic-use-supplement.html ----- # Nginx 基础用途拾遗 Nginx 是一款大家日常再熟悉不过的软件,稳定高效是这款软件的标签。常见 Nginx 会做为地址转发服务或提供文件托管能力。但是 Nginx 的用法其实不止于此,原生 Nginx 还有许多实用的功能,能够实现一些业务中麻烦的小细节。 本文将介绍三种基础用法,如果你熟悉 Nginx 和容器,阅读时间大概是五分钟。 ## 写在前面 本次使用的示例环境,均使用容器进行模拟。如果你对容器还不是很熟悉,欢迎浏览之前的[Docker](https://soulteary.com/tags/docker.html) 相关文章内容。 ## 配合 compose 轻松创建健康检查 一些前端类的容器,本身并没有服务能力,但是为了能够享受容器服务的基础健康检查,以及提供给外部诸如负载均衡等服务使用,我们可能不得不启动一个语言运行时,比如 Node / PHP / Java,不过如果你使用 Nginx 作为前端使用的 Web 服务软件,他本身就自带了基础的路由功能和定制响应码和内容的能量,可以避免我们引入庞大的语言运行时。 我们只需要在配置内添加一个名为 `/health` 的路由,然后配合 `default_type` 和 `return` 指令就可以完成我们想要的“当服务健康时,返回HTTP CODE 200,并输出一些内容”。 ```bash server { listen 80; location = /health { access_log off; default_type text/html; return 200 'alive'; } } ``` 你或许会说,我们可以让健康检查软件检查业务路由,但是我想告诉你的是,当我们把路由独立之后,你会发现健康检查的响应时间更快了,除此之外,我们还可以对日志进行丢弃。如果你将健康检查路由和业务路由放在一起,海量的健康检查日志和业务日志在一起,会让你调试的时候痛不欲生。 配合 compose 进行健康检查也很容易: ```yaml version: "3.6" services: health.test.soulteary.com: image: nginx:1.18.0-alpine volumes: - ./default.conf:/etc/nginx/conf.d/default.conf:ro healthcheck: # 老版本使用 # test: ["CMD-SHELL", "wget -q --spider --proxy off localhost:80/health || exit 1"] test: ["CMD-SHELL", "curl -f localhost/health || exit 1"] interval: 3s retries: 12 ``` 当你使用 `docker ps` 查看容器进程的时候,你会看到我们的容器会标记为“healthy”,之后可以配合各种策略进行容器服务基础容灾等基本操作。 ```yaml CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2afd71cd562e nginx:1.18.0-alpine "nginx -g 'daemon of…" 10 minutes ago Up 3 seconds (healthy) 80/tcp health.test.soulteary.com_1 ``` ## 使用 Nginx 聚合不同站点内容 日常开发过程中,在遇到“前端跨域”、“短信模版中链接固定”、“既有程序调用来源限制”等场景下,会遇到需要聚合站点域名或路径的情况。 我们知道使用编程语言,可以实现远程内容获取和转写,但是实际上使用 Nginx 的反向代理功能可以更加轻松地做到内容聚合。 比如,在做内容聚合前,我们需要访问的内容在 **apple.test.soulteary.com** 和 **banana.test.soulteary.com** ,而我们希望聚合后的内容在 **test.soulteary.com** 中展示。 为了方便模拟环境,我们先创建一个 compose 配置文件: ```yaml version: "3.6" services: test.soulteary.com: image: nginx:1.18.0-alpine ports: - 8080:8080 volumes: - ./default.conf:/etc/nginx/conf.d/default.conf:ro apple.test.soulteary.com: image: nginx:1.18.0-alpine volumes: - ./apple.test.soulteary.com.conf:/etc/nginx/conf.d/default.conf:ro banana.test.soulteary.com: image: nginx:1.18.0-alpine volumes: - ./banana.test.soulteary.com.conf:/etc/nginx/conf.d/default.conf:ro ``` 配置文件中的 services 的三个子项分别代表聚合后的站点,以及两个需要被聚合的站点,可以想象为现实中网络相通的三台服务器。 继续创建 `apple.test.soulteary.com.conf` 这个 Nginx 配置文件。 ```bash server { listen 80; server_name apple.test.soulteary.com; default_type text/html; location / { return 200 'apple.test.soulteary.com'; } } ``` 可以看到,站点功能很简单,访问 `/` 的时候,返回 HTTP CODE 200,并输出文本内容 `apple.test.soulteary.com`,同理我们创建另外一个站点的 Nginx 配置文件 `banana.test.soulteary.com`。 最后,创建用于聚合配置文件的 Nginx 配置。 ```bash server { listen 8080; server_name test.soulteary.com; location = / { return 302 /apple; } location /apple { proxy_pass http://apple.test.soulteary.com/; proxy_set_header Host "apple.test.soulteary.com"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /banana { proxy_pass http://banana.test.soulteary.com/; proxy_set_header Host "banana.test.soulteary.com"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` 可以看到配置十分简单,通过 `proxy_pass` 配合 `proxy_set_header` 两个指令,我们就将不同站点聚合到了一块。 将 `test.soulteary.com` 绑定至本地,然后启动服务,使用命令行访问站点,可以看到配置符合预期。 ```bash # curl http://test.soulteary.com:8080/apple apple.test.soulteary.com # curl http://test.soulteary.com:8080/banana banana.test.soulteary.com ``` ## 转发 Git SSH 这类 TCP 请求 Nginx 除了能够处理 HTTP 请求外,对于 TCP 类型的数据一样能做到聚合/转发。比如这里我们想将生产环境的某台 GitLab 的访问权限限制到具体的 IP,除了使用云平台的防火墙规则外,使用 Nginx 一样可以做到。 这里要使用的是 Nginx 的 `ngx_stream_proxy_module` 模块。 Nginx 容器默认配置仅包含了 HTTP 服务模式,所以这次修改不能和上面一样,仅修改 “vhost” 配置,需要修改 nginx.conf 主配置,在修改之前,我们先使用脚本看看默认的配置长什么样。 ```bash docker run --rm -it nginx:1.18.0-alpine cat /etc/nginx/nginx.conf ``` 执行上面的命令,可以看到默认的配置很简单: ```bash user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } ``` 为了能够让读者轻松验证,我们假设目标仓库是 GitHub,在对配置文件简单修改后: ```bash user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } stream{ server{ listen 2223; proxy_pass github.com:22; proxy_connect_timeout 10s; proxy_timeout 20s; proxy_buffer_size 512k; } } ``` 我们继续创建一个 `compose` 配置文件: ```yaml version: "3.6" services: proxy-git.test.soulteary.com: image: nginx:1.18.0-alpine ports: - 2223:2223 volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro ``` 使用 `docker-compose up` 将服务启动之后,我们使用经典的 Git 测试命令行对服务进行验证: ```bash # ssh -T git@127.0.0.1 -p 2223 The authenticity of host '[127.0.0.1]:2223 ([127.0.0.1]:2223)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[127.0.0.1]:2223' (RSA) to the list of known hosts. Hi soulteary! You've successfully authenticated, but GitHub does not provide shell access. ``` 你会发现此刻我们想要的目标已经达到了,不信的话,你可以使用相同命令去测试下原始 Git 服务地址。 ```bash # ssh -T git@github.com Hi soulteary! You've successfully authenticated, but GitHub does not provide shell access. ``` 小节开始前提过,想进行来源限制,可以搭配 Nginx 原生的 `allow` / `deny` 指令来完成: ```bash location / { deny 192.168.1.1; allow 192.168.1.0/24; allow 10.1.1.0/16; allow 2001:0db8::/32; deny all; } ``` ## 最后 Nginx 好玩、实用的用法其实还有不少,时间原因,就不在此展开了。 --EOF