前几天有个朋友问我,说想学 Docker,但折腾了一周还没把环境跑通。我给他的建议很直接:别一上来就整 K8s,也别急着啃官方文档,先装好 Docker,把一个 nginx 容器跑起来,再给它配一个反向代理,你对容器的感觉就会完全不一样。docker + nginx 反向代理这个组合,几乎覆盖了容器技术里最核心的几个概念:镜像、容器、端口映射、数据卷挂载、容器间网络通信,再加上一份 nginx 配置,等于把日常运维里最高频的一类需求完整走了一遍。
这篇文章就把我实测的完整过程写出来,从安装 Docker 开始,到你用 nginx 给多个后端服务做反向代理结束,中间所有命令、配置和坑都会给到。适合刚接触 Docker 的开发者,也适合想快速搭一套本地多环境代理的运维同学。我会尽量把每个操作步骤背后的原因也讲清楚,这样你不仅能把命令敲出来,还能理解为什么这么写,换个场景也知道怎么改。
1. 为什么我建议从这套组合开始
1.1 Docker 到底解决了什么问题
很多人刚开始接触 Docker 时,第一反应是“又一个虚拟机”。这个理解不算错,但不准确。虚拟机虚拟的是整台机器,每个虚拟机里都有完整的操作系统内核,启动慢、占用高;Docker 虚拟的是运行环境,所有容器共享宿主机内核,只隔离进程、文件系统和网络空间,所以一个容器往往几十兆,几秒就能启动。
拿我们最熟悉的场景举例:以前你在本地写了一个 Python 项目,依赖 Python 3.8。后来服务器上装的是 Python 3.11,跑起来一堆语法报错。再往后别人接手这个项目,配置环境又花了两天。Docker 的做法是把 Python 3.8、所有依赖包、项目代码一起打包进一个镜像里,任何人拿到这个镜像,在任何安装了 Docker 的机器上跑起来,环境完全一致。这就叫“一次构建,到处运行”。
容器技术真正的价值在于把“应用”和“环境”打包成了同一个东西。你不再需要关心服务器上有没有装 nginx、Redis、MySQL,只需要拉取对应的镜像,启动容器就行。这也是为什么现代后端开发里,Docker 几乎是必会技能。
1.2 为什么第一个容器项目选 nginx
选 nginx 当入门项目有几个非常实际的原因。
一是轻量。nginx 的官方镜像 alpine 版本只有二十多兆,拉取快,启动只要一两秒,特别适合新手反复折腾。你随便改配置、删容器、重建容器,成本几乎为零。
二是贴近真实场景。nginx 在服务端扮演的角色非常多:静态资源服务器、反向代理、负载均衡、SSL 终止层。你学会用容器跑起来一个 nginx,等于同时学会了部署一个生产级组件,后面直接可以迁移到服务器上使用。
三是它天然适合演示 Docker 的核心机制。比如“端口映射”,nginx 容器默认监听 80 端口,你怎么把宿主机的 8080 端口映射到容器里的 80;再比如“数据卷挂载”,nginx 的配置文件和静态资源目录都在容器里,你怎么把宿主机上的配置目录挂载进去,让修改不依赖容器内部。这些概念全部可以拿 nginx 当载体,直观又容易验证。
我给自己的建议是:跟着这篇文章走完一遍,然后删掉所有容器,自己从头再搭一遍,这次不看文档。能独立搭出来,才算真的入门了。
2. 环境准备:主流平台装 Docker 的实操记录
2.1 Windows 平台:Docker Desktop 安装全流程
Windows 上装 Docker,最省事的方案是 Docker Desktop。它自带图形界面,右下角有个鲸鱼图标,点击就能管理所有容器。安装前有两个前置条件:一是系统版本需要 Windows 10 64 位专业版或更高版本,二是必须开启 CPU 虚拟化,也就是 BIOS 里的 Intel VT-x 或 AMD-V。
下载 Docker Desktop 安装包后,一路点下一步就能装完。装完会提示你重启电脑,然后启动软件。第一次启动如果提示需要启用 WSL 2,比较快的处理方式是打开管理员权限的 PowerShell,执行下面这条命令:
wsl --update执行完重启 Docker Desktop,界面右下角鲸鱼图标变成绿色,就说明 Docker 引擎已经正常启动了。在 PowerShell 里输入docker version,能看到 Client 和 Server 两段信息。这里有个新手特别容易踩的坑:如果只看到 Client 内容,Server 报错或者根本没有,说明 Docker 引擎没起来,多半是 WSL 2 没配好或虚拟化没开。
Docker Desktop 的资源占用不算低,默认分配的内存可能比较高。如果你是 8GB 内存的机器,建议在 Docker Desktop 的设置里把内存调到 4GB 左右,避免电脑变卡。如果公司电脑有安全策略限制,装不了 Docker Desktop,可以退而求其次用 Podman Desktop,但这就是另一个话题了。
2.2 Linux 平台:用命令行完成安装
Linux 上安装 Docker 更接近生产服务器上的实际操作,我以最常用的 Ubuntu 为例。其实安装流程官方给出了一键脚本,但生产环境我不推荐用脚本,因为你不知道它到底执行了什么。更可控的方式是走官方 apt 源:
sudo apt update sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin装完后执行sudo systemctl status docker,能看到 active (running) 就说明服务已经起来了。Linux 下执行 docker 命令需要 root 权限,为了避免每次敲 sudo,可以把当前用户加入 docker 用户组:
sudo usermod -aG docker $USER这条命令执行完需要重新登录终端才能生效。有个安全上的细节必须要提醒:docker 用户组相当于 root 权限,因为容器可以把宿主机目录挂载进来,一旦有 docker 组权限意味着可以访问宿主机文件系统。所以在生产服务器上,一定不要把不信任的用户加入 docker 组。
CentOS 系统的安装方式略有不同,最明显的是默认包管理器是 yum/dnf,需要的依赖名称也不同。但配置逻辑一致,按官方文档走一遍就行。macOS 的安装和 Windows 基本一样,也是用 Docker Desktop,选 Apple Silicon 还是 Intel 芯片对应的安装包即可。
2.3 镜像加速配置,解决 pull 镜像慢的问题
第一次拉取镜像,很多人会卡在“pull access denied”或长时间无响应。这不是因为你网络有问题,而是默认的 Docker Hub 服务器在国外,国内访问确实很慢。解决办法是配置国内镜像加速器,也就是 registry mirror。
Docker Desktop 用户直接在 Settings -> Docker Engine 里的 JSON 配置中加入 mirror 字段:
{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com" ] }保存后 Docker 会自动重启。Linux 用户则需要修改/etc/docker/daemon.json,没有这个文件就新建一个,然后将上面的 JSON 内容写进去,重启 docker 服务:
sudo systemctl restart docker配置完成后,再执行docker pull nginx:alpine,速度会有明显提升。这里要提醒一点:镜像加速只影响拉取镜像的速度,不影响镜像来源的可靠性,你拉下来的镜像依然来自 Docker Hub。我个人的经验是,多配置两到三个镜像源作为备选,有些源偶尔会不稳定,切换一下就好。
3. 镜像、容器与仓库:先把这三件事搞清楚
3.1 镜像、容器、仓库三者关系
Docker 的三个核心概念,用吃饭来类比特别好懂。镜像相当于菜谱,它定义了这道菜的全部内容:食材清单、烹饪步骤、最终形态。容器相当于按菜谱做出来的一道菜,你可以做一份,也可以做十份,每份互相独立。仓库相当于存放菜谱的图书馆,你需要时从中取一本。
具体到命令层面,docker pull nginx就是从 Docker Hub 这个公共仓库,把 nginx 镜像下载到你本地;docker run nginx就是基于这个镜像启动一个容器;docker build是写自己的菜谱,也就是自定义镜像。在这个过程中,你还可以用docker commit把一个容器的状态固化成新的镜像,但在实际生产里不推荐这么做,应该用 Dockerfile 来描述镜像构建过程。
有个细节值得注意:同一个镜像可以启动无数个容器,容器之间完全隔离。你启动的 nginx 容器里改了index.html,不会影响到其他容器,也不会影响镜像本身。这个特性在你做实验的时候特别方便,随便折腾,坏了删除容器重新启动即可,镜像还是最初的样子。
3.2 常用命令速查,够用就好
入门阶段命令不需要背太多,掌握下面这些,足够支撑从安装到反向代理的完整流程。
docker image ls # 查看本地镜像 docker ps # 查看运行中的容器 docker ps -a # 查看所有容器,包括已停止的 docker pull nginx:alpine # 拉取镜像,alpine 是发行版标签 docker run -d -p 8080:80 nginx:alpine # 后台启动 nginx 容器并映射端口 docker stop <容器ID> # 停止容器 docker rm <容器ID> # 删除已停止的容器 docker rmi <镜像ID> # 删除镜像 docker exec -it <容器ID> bash # 进入容器内部执行命令 docker logs -f <容器ID> # 实时查看容器日志看到docker run后面的参数,新手会比较懵。-d表示后台运行;-p 8080:80表示把宿主机的 8080 端口映射到容器内部的 80 端口;nginx:alpine中,冒号前是镜像名,冒号后是标签。没写标签时默认用latest,但这在正式项目里是有风险的,因为 latest 会变动,你无法确定部署的是哪个版本。
这些命令不需要死记硬背,多敲几遍自然就记住了。我现在写脚本时还经常查docker run --help,这很正常,重要的是理解每个参数到底影响了什么。
4. 用 Docker 部署第一个 nginx 容器
4.1 拉取镜像与启动容器
先执行这条命令,把 nginx 镜像拉下来:
docker pull nginx:alpine如果镜像加速配置正常,十几秒就能拉取完成。执行docker image ls确认一下,能看到nginx和alpine两列信息,SIZE 只有几十兆。现在启动第一个容器:
docker run -d --name my-nginx -p 8080:80 nginx:alpine--name参数给容器起个名字,后面管理容器时不用记一串随机 ID。执行完回到浏览器,访问http://localhost:8080,能看到 nginx 的欢迎页面。这里有个关键点:容器内部只有 80 端口,它本身不知道宿主机 8080 端口的存在,所有访问 8080 端口的请求都会被 Docker 转发到容器内的 80 端口。
如果你是第一次配置,页面一直打不开,先执行docker ps看看容器是不是退出状态。如果容器状态是 Up,那大概率是防火墙拦截了端口;如果容器已经退出,执行docker logs my-nginx看日志里输出什么错误,再针对性解决。
4.2 端口映射与目录挂载
上一步的端口映射已经能让 nginx 跑起来,但离真实使用还差一步:现在你想改 nginx 的配置,得进入容器内部,但容器一旦被删除,所有修改都会丢失。这显然不符合实际需求,我们需要把宿主机的配置目录挂载到容器里。
先看一下 nginx 镜像里的目录结构。执行:
docker exec -it my-nginx ls /etc/nginx能看到 conf.d、nginx.conf、sites-enabled 等目录。在实际部署中,我习惯把宿主机上的一个目录,比如~/docker/nginx/conf.d,挂载到容器的/etc/nginx/conf.d。这样处理前提是挂载目录里已经有配置文件了,否则容器里对应目录是空的,nginx 默认配置里引用了该目录下的 default.conf,空目录会导致配置加载失败。
先停止并删除刚才的容器,重新启动一个带挂载参数的容器:
docker stop my-nginx docker rm my-nginx mkdir -p ~/docker/nginx/conf.d ~/docker/nginx/html ~/docker/nginx/logs docker run -d --name my-nginx -p 8080:80 \ -v ~/docker/nginx/conf.d:/etc/nginx/conf.d:ro \ -v ~/docker/nginx/html:/usr/share/nginx/html \ -v ~/docker/nginx/logs:/var/log/nginx \ nginx:alpine这里有三组挂载参数。conf.d目录我加了:ro,表示只读,防止容器内部误修改配置;html目录是 nginx 网站的根目录,你把index.html放进去,访问时就能看到自己的页面;logs目录把容器日志输出到宿主机,排错时直接看宿主机文件,不用进入容器。每次修改宿主机文件,容器内立即生效,不需要重启容器。这是容器开发调试中非常重要的一个能力。
4.3 修改 nginx 默认页面
挂载好目录后,在宿主机~/docker/nginx/html下新建一个index.html,写入任意内容:
<!DOCTYPE html> <html> <head><title>Docker Nginx Test</title></head> <body> <h1>Hello from Docker nginx</h1> </body> </html>刷新http://localhost:8080,页面会显示你写的内容。如果你看到的是 nginx 默认欢迎页,大概率是挂载目录不对,nginx 容器内实际读取的根目录是/usr/share/nginx/html,只要宿主机目录正确挂载到这个路径,就会显示宿主机里的文件。
这个实验能直观理解“数据卷挂载”的价值:容器里的代码、配置、日志都与宿主机共享,你在宿主机上的任何修改,容器内部立即可见。这样做的另一个好处是,部署新版本时只需要替换宿主机上的文件,然后重启容器即可,不需要重新构建镜像。
5. 重头戏:配置 nginx 反向代理
5.1 反向代理是什么,为什么用得上
反向代理这个概念,很多新手听着高大上,理解了其实不复杂。正向代理是替客户端访问外部资源,比如你在浏览器里配置代理,网页请求先经过代理服务器再转发到目标网站,服务器看到的是代理服务器的地址。反向代理恰恰相反,它在服务器端工作,替服务器接收外部请求,再把请求转发到内部的多台服务器上。
举个例子,你有一个域名www.example.com,背后有三台不同的服务:前端页面跑在 3000 端口,API 接口跑在 8080 端口,图片服务跑在 9000 端口。如果直接让用户访问不同端口,体验差而且暴露了服务结构。用 nginx 做反向代理后,所有请求都走 80 端口,nginx 根据路径或域名把请求转发到对应服务。
在 Docker 场景下,nginx 反向代理尤其重要。因为你可能用容器部署了多个应用,这些应用每个占用不同端口,但对外只暴露一个 80 或 443 端口。nginx 容器就成了流量的统一入口,用户访问 80 端口,nginx 负责把/api开头的请求转发到后端服务容器,把/开头的请求转发到前端容器。
5.2 修改 nginx 配置实现反向代理
现在我把刚才启动的 nginx 容器,改造成一个简单的反向代理。先在宿主机~/docker/nginx/conf.d里新建一个关键配置文件,名字叫reverse-proxy.conf:
server { listen 80; server_name localhost; location / { proxy_pass http://my-nginx-html:80; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }这段配置的意思是:所有到本机 80 端口的请求,都会转发给上游地址http://my-nginx-html:80。但这里有个问题:my-nginx-html这个服务名还不能直接访问,需要在 Docker 内创建一个自定义网络,让容器之间可以通过服务名互通。先创建一个网络:
docker network create webnet然后启动一个新的 nginx 容器,命名为my-nginx-html,加入 webnet 网络:
docker run -d --name my-nginx-html --network webnet \ -v ~/docker/nginx/html:/usr/share/nginx/html:ro \ nginx:alpine接着把我原来的my-nginx容器也加入 webnet:
docker network connect webnet my-nginx此时两个容器在同一个自定义网络中,my-nginx就可以通过容器名访问到my-nginx-html。重启my-nginx,浏览器访问http://localhost:8080,请求会转发到my-nginx-html容器的 80 端口,最终显示的还是之前那个自定义页面。
这个验证虽然看起来只是绕了一圈,但它把 Docker 容器间通信的关键讲清楚了。容器互访不使用 IP,而使用容器名。Docker 内置 DNS 会自动解析,这个机制让服务发现变得非常简单,你不需要维护 IP 映射,只要容器在同一个网络里,名字就是地址。
5.3 多项目反向代理:路径分流与域名分流
实际项目中,一个 nginx 往往要代理多个服务。两种常见策略是:不同域名分流和不同路径分流。
域名分流是这样的:www.example.com和api.example.com都解析到同一台服务器,但需要把请求转发到不同后端。nginx 配置里 server_name 来区分:
server { listen 80; server_name www.example.com; location / { proxy_pass http://web-frontend:3000; } } server { listen 80; server_name api.example.com; location / { proxy_pass http://web-backend:8080; } }域名分流的前提是你有这个域名的解析权限,本地测试时可以在/etc/hosts里把域名指到127.0.0.1来模拟。路径分流则是在同一个域名下,按 URI 前缀分流到不同服务:
server { listen 80; server_name localhost; location /api/ { proxy_pass http://api-backend:8080; } location /static/ { alias /usr/share/nginx/html/static/; } location / { proxy_pass http://web-frontend:3000; } }路径分流的顺序有讲究,nginx 会按最长前缀匹配,所以/api/的请求会先命中location /api/而不是location /。这个特性在配置时要特别留意,我曾经因为把location /写在了/api/前面,导致所有请求都被转发到同一个后端,排查了半天才发现是顺序错了。
6. 用 docker-compose 编排整套代理环境
6.1 为什么需要 docker-compose
手动docker run跑几个容器还能应付,但服务一多,命令就变得又长又容易出错。而且容器之间还有先后关系,比如 nginx 必须在后端服务启动之后启动,否则启动时会报“host not found”。管理这些依赖和参数的更好的方式是 docker-compose,它用一份 YAML 文件描述整套服务的拓扑关系,一条命令启动全部。
docker-compose 的定位是“单机多容器的编排工具”。K8s 解决的问题更大,但入门阶段不需要上 K8s,compose 是最合适的中间层。你不需要学会 YAML 的复杂语法,照着写就能跑起来,发现错误也能用docker compose logs快速定位。
6.2 一个完整的 docker-compose.yml 示例
我写一个实际可用的例子:一个 nginx 反向代理,代理两个后端静态站点。
version: '3.8' services: nginx-proxy: image: nginx:alpine ports: - "8080:80" volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro depends_on: - site-a - site-b networks: - webnet site-a: image: nginx:alpine volumes: - ./site-a:/usr/share/nginx/html:ro networks: - webnet site-b: image: nginx:alpine volumes: - ./site-b:/usr/share/nginx/html:ro networks: - webnet networks: webnet: driver: bridge在这个配置文件里,我用networks定义了一个名为webnet的自定义网络,所有服务默认加入这个网络。depends_on指定了服务启动顺序,nginx-proxy 会在 site-a 和 site-b 之后启动。这个配置有一个非常明显的优势:不需要手动设置--network参数,服务之间可以直接通过服务名互相访问。
对应 nginx 的反向代理配置./nginx/conf.d/default.conf:
upstream site_a_backend { server site-a:80; } upstream site_b_backend { server site-b:80; } server { listen 80; server_name localhost; location /a/ { proxy_pass http://site_a_backend; proxy_set_header Host $host; } location /b/ { proxy_pass http://site_b_backend; proxy_set_header Host $host; } }目录结构需要提前创建:
mkdir -p docker-demo/nginx/conf.d docker-demo/site-a docker-demo/site-b在site-a里放一个index.html,写上“我是站点A”;在site-b里放一个index.html,写上“我是站点B”。然后执行:
docker compose up -d浏览器访问http://localhost:8080/a/会看到站点A的内容,访问http://localhost:8080/b/会看到站点B的内容。到这里,你就完成了一套用 nginx 做多服务反向代理的完整环境。
6.3 管理容器生命周期
compose 的常用命令非常少,而且很直观:
docker compose up -d # 启动所有服务 docker compose ps # 查看服务状态 docker compose logs -f nginx-proxy # 查看某个服务的日志 docker compose restart nginx-proxy # 重启某个服务 docker compose down # 停止并删除所有容器 docker compose down -v # 同时删除数据卷,慎用这里要特别注意,docker compose down -v会把 compose 文件里定义的数据卷一起删除,如果里面存了数据库数据,那数据就没了。我在测试环境的 MySQL 容器上栽过一次,生产的教训是要重视的。
还有个细节,修改了 compose 文件或挂载的配置文件后,只执行docker compose restart不一定生效,因为 restart 只是重启容器,不会重新读取 compose 配置。正确的做法是执行docker compose up -d,compose 会检测到配置变化并重新创建容器。
7. 常见问题排查与避坑实录
7.1 端口占用导致容器启动失败
启动 nginx 容器时,最常见的报错是:
Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use这说明宿主机 80 端口已经被占用。常见原因是你之前装了系统自带的 apache,或者另一个 nginx 在挂着。解决方式一:改映射端口,比如-p 8080:80;解决方式二:停掉宿主机的服务。Linux 上可以执行:
sudo lsof -i :80 sudo systemctl stop apache2这个报错也提醒我一件事:生产环境部署 nginx 容器时,80 和 443 端口几乎是必须用的,所以提前在服务器上检查端口占用很有必要。我一般拿到新服务器,第一件事就是netstat -tlnp看一下常用端口的状态。
7.2 容器修改配置不生效
很多人在宿主机改了挂载的 nginx 配置文件,但刷新网页没变化。排查思路是这样的:先确认挂载是否真的生效,执行docker exec -it my-nginx cat /etc/nginx/conf.d/default.conf,对比容器内的文件内容是否与宿主机一致。如果不一致,说明挂载目录不对。
如果一致,再看看 nginx 有没有加载新配置。nginx 配置修改后,需要重载而不是重启容器:
docker exec my-nginx nginx -s reloadreload 是平滑重载,不会中断正在处理的请求,适合生产环境。这也说明了一个重要的习惯:挂载配置到容器之后,修改配置、reload、验证,是一个完整的流程,缺一不可。我曾经改完配置直接刷新页面,发现没变化就怀疑挂载有问题,折腾半天,结果只是忘了 reload。
7.3 镜像拉取过慢或超时
镜像拉取慢,绝大多数情况是镜像源没配置好。在终端执行docker info,找到 Registry Mirrors 一行,看有没有列出镜像源地址。如果为空,回到第 2.3 节配置镜像加速。配置完记得重启 Docker,然后再试。
还有一个经验是:不要在高峰期反复重试同一个镜像,有时换个时段拉取会快很多。如果你多次尝试还是失败,可以检查一下磁盘空间,docker image ls看看是不是缓存了太多无用镜像,执行docker system prune -a清理后重试。
7.4 反向代理 502 Bad Gateway
反向代理配置好后,访问出现 502,说明 nginx 无法连接到上游服务。排查步骤很简单,按顺序来:
- 先确认上游容器是否在运行:
docker ps - 再确认两个容器是否在同一个网络:
docker network inspect webnet - 然后进入 nginx 容器测试连通性:
docker exec -it my-nginx ping site-a如果 ping 不通,说明网络没连上。如果 ping 通了还 502,那就是 nginx 配置里的proxy_pass地址的端口不对。比如上游 nginx 暴露的是 80 端口,你写了 8080,就会连接失败。
还有一个隐蔽的坑:proxy_pass后面的 URL 有没有带 URI。proxy_pass http://site-a;和proxy_pass http://site-a/;在带 location 前缀的情况下,转发路径是不同的。前者会保留完整的原始路径,后者会去掉匹配的前缀再转发。如果前端和后端路由对不上,多数是这里出了问题。
下面整理一张速查表,方便收藏:
| 场景 | 常见原因 | 快速定位命令 |
|---|---|---|
| 容器启动失败 | 端口被占用 / 挂载目录不存在 | docker logs <容器ID> |
| 访问不到页面 | 端口映射错误 / 防火墙拦截 | docker ps、curl localhost:<端口> |
| 配置修改不生效 | 挂载路径错误 / 未执行 reload | docker exec <容器ID> cat <配置文件> |
| 502 Bad Gateway | 上游服务不可达 / 网络不在同一网段 | docker network inspect <网络名> |
| 镜像拉取超时 | 未配置镜像加速 / 磁盘空间不足 | docker info查看 Registry Mirrors |
排错时有个基本思路:从底层往上查。先确认容器在不在跑,然后确认网络通不通,接着确认服务监听端口,最后检查配置文件。一层层缩小范围,比瞎猜快得多。
我个人的体会是,这套从安装到反向代理的流程,本质上是给你建立了一根“容器思维”的脊柱。以后无论你接触什么容器化项目,跑起来一个服务、暴露端口、挂载配置、容器间通信,思路都是一样的。如果跟着文章走到了最后一步,说明你已经能独立掌控一个容器化的 nginx 了,剩下的就是多动手、多折腾、多写配置,踩坑多了自然就熟练了。