
1. 为什么学 Docker 总是“看完就忘”——先想清楚它解决什么问题先聊个现象。很多人学 Docker 都是这么个流程看一遍安装教程敲几个 hello-world觉得“哦原来是这么回事”然后关上电脑三个月后要用的时候连docker run参数都记不全。问题出在哪不是记性差是压根没搞明白 Docker 到底是干嘛的。它不是一个“装完就完事”的软件而是一套改变你部署方式的工作流。你不理解这套工作流硬背命令毫无意义。一句话说清楚Docker 帮你把应用连同它的运行环境一起打包做成一个“集装箱”在任何装 Docker 的机器上都能直接跑起来。这句话怎么理解以前要部署一个 nginx 服务器你得先装系统、装依赖、改配置、开端口每一步都可能出幺蛾子——换个机器就得重来一遍。Docker 的思路是我把 nginx 和它需要的系统环境、依赖库、配置文件全部封装成一个镜像你拿过去docker run一下3 秒启动行为完全一致。用个生活类比你从老家带了一罐腌菜去外地怕路上碎了就用泡沫箱打包里三层外三层贴好标签到了目的地拆开直接吃味道和在老家一模一样。Docker 镜像就是这个泡沫箱你装的是啥、怎么装、里面什么结构全在里头。所以学习 Docker 的正确路径不是把命令当单词背而是带着问题去实践我希望我的服务能一键部署、能快速迁移、能把环境依赖一次搞定。带着这个目标你自然会理解为什么需要镜像、为什么需要容器、为什么需要端口映射而不是“哦原来 docker run 后面要加 -p 啊”。这篇博文就是按这个路径走的先装环境再理解核心概念然后搭一个真正能用的 nginx 反向代理最后把配置管理这套也理顺。整个过程我尽量还原实际踩坑经历你跟着走一遍比看十篇教程有用。2. 安装这一步就有讲究桌面版还是引擎版别一上来就踩坑2.1 不同系统下的安装选择对比很多人直接搜“Docker 安装教程”然后对着一个老掉牙的 Linux 教程在 Windows 上折腾折腾半天装不上——因为压根不在一个频道。先把安装策略理清楚分三种情况操作系统推荐方案原因注意事项Windows 10/11 家庭版/专业版Docker Desktop for Windows官方支持带图形界面配置最简单必须开启 WSL2 和 BIOS 虚拟化否则装不上Windows 老版本/不想装桌面版Docker Toolbox不推荐基于 VirtualBox老古董了性能和兼容性都很差遇到问题别死磕换个方案macOSDocker Desktop for Mac同样官方支持体验最好Intel 和 Apple Silicon 芯片版本不同别下载错Ubuntu/Debian 服务器Docker Engine命令行版服务器上用桌面版没意义占资源通过 apt 安装官方仓库版本别用系统自带的旧版CentOS/RHELDocker Engine同上注意 Docker 官方对 CentOS 7 的支持情况内核太旧会有问题说几个重点。Windows 用户最怕遇到的问题是“Docker Desktop 打不开”。百分之八十的原因是 BIOS 没开虚拟化。怎么查打开任务管理器性能选项卡看右下角“虚拟化”是不是“已启用”。如果显示禁用重启进 BIOS找到 Intel VT-x 或 AMD-V 的选项打开它Windows 的“虚拟机平台”和“适用于 Linux 的 Windows 子系统”这两个功能也要在“启用或关闭 Windows 功能”里勾上。另一个高频问题是 WSL2 版本太老。打开 PowerShell 执行wsl --update确认内核是最新的。我见过不少人卡在这一步Docker Desktop 提示“WSL 2 installation is incomplete”其实就一条命令的事。Ubuntu 服务器用户直接按官方文档跑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 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 docker run hello-world看到 “Hello from Docker!” 就说明装好了。这一步跑通后面基本顺风顺水。2.2 安装完成后必做的两条配置装完不是就完事了有两件事必须做不做后面会烦死。第一件事把当前用户加入 docker 组省得每次敲命令都加 sudosudo usermod -aG docker $USER newgrp docker这一步做完重新登录终端docker ps就不需要 sudo 了。注意加组之后要重新登录会话才生效我当时以为没生效折腾了半天才发现是终端会话没刷新。第二件事配置镜像加速。这个我放在后面“镜像下载”部分细说但先说一句默认源在国内环境下下载镜像非常慢你第一次跑docker pull nginx如果等了五分钟还在转圈别怀疑人生不是网的问题是源的问题。解决办法是配置国内可用的镜像源。常用地址包括 Docker 官方中国镜像、中科大镜像、阿里云容器镜像服务等。配置完重启 Docker 服务即可生效。这个属于老生常谈但具体怎么选、怎么配我在后面“镜像下载加速方案”里给出了实测对比。2.3 Linux 服务器内核和驱动的一个老坑如果你在 Linux 服务器上装 Docker有一个只在特定情况下出现、但出现就很扎心的问题老内核和旧存储驱动不兼容。Docker 的存储驱动用的是 OverlayFS这玩意儿在内核 4.0 以上才能用。CentOS 7 默认内核是 3.10Docker 会退回到旧的 devicemapper 驱动而 devicemapper 有个毛病磁盘空间会快速耗尽容器一多就报no space left on device。解决办法要么升级内核到 4.x 以上要么在 Docker 配置文件/etc/docker/daemon.json里指定存储驱动{ storage-driver: overlay2 }改完重启 Dockersudo systemctl restart docker我在早期的 CentOS 7 服务器上就被这个坑折磨过两天。容器跑着跑着突然“磁盘满了”实际df -h看根分区还有几十 G 空闲——就是这个存储驱动的锅。如果你也遇到这种诡异现象查一下存储驱动准没错。3. 三个核心概念和一组高频命令——够你应付 90% 的日常操作3.1 镜像、容器、仓库用搬家来理解网上讲 Docker 概念的教程很多但大多数讲得跟论文似的越看越糊涂。我用搬家来类比你一分钟就能记住。镜像Image就是一个“封装好的搬家箱子”。箱子里面装的不是你具体搬家的那一天有多少东西而是一整套操作系统的基础环境、软件程序的代码、依赖库、配置文件、启动命令。你把箱子拿过来on打开就是一个能跑的 nginx。箱子本身是只读的你改不动它改完只能重新打包一个新箱子。容器Container就是“箱子被打开运行起来”的那个瞬间。同一个镜像可以打开很多次跑起来的每个实例相互隔离、互不影响。比如你用同一个 nginx 镜像起两个容器一个跑 80 端口一个跑 8080 端口它们互不干扰。你对容器做的任何修改比如改了配置文件只对这个容器生效不会污染镜像。仓库Repository就是“存放箱子的仓库”。你在本地构建好镜像推到仓库里存着换台机器从仓库拉下来就能用。Docker Hub 是最大的公共仓库企业一般搭私有仓库。命令层面高频操作只有这几个# 拉取一个镜像到本地 docker pull nginx # 查看本地有哪些镜像 docker images # 基于镜像运行一个容器 docker run -d --name my-nginx -p 8080:80 nginx # 查看正在运行的容器 docker ps docker ps -a # 包括已停止的 # 进入容器内部执行命令 docker exec -it my-nginx bash # 查看容器日志 docker logs -f my-nginx # 停止、启动、删除容器 docker stop my-nginx docker start my-nginx docker rm my-nginx # 删除镜像 docker rmi nginx这些命令不是背下来的是你实际操作两遍就自然记住了。特别是docker run -d -p --name这个组合后面做 nginx 反代的时候天天用跑几次想忘都忘不掉。3.2 端口映射是怎么回事刚接触 Docker最难理解的一个概念就是端口映射。你可能会有疑问我明明在容器里启动了 nginx默认监听 80 端口为什么宿主机上访问不到原因其实一句话容器在 Docker 的虚拟网络里有自己的 IP比如 172.17.0.2宿主机访问不到容器的 IP也访问不到容器内部的端口。你需要把宿主机的某个端口“映射”到容器的 80 端口上这样访问宿主机的 8080就等于访问容器的 80。所以这条命令docker run -d --name web -p 8080:80 nginx的意思是把宿主机的 8080 端口转发到容器的 80 端口。访问http://宿主机IP:8080就等价于访问容器里的http://172.17.0.2:80。再理解一下-p 80:80如果宿主机的 80 端口空闲你可以直接映射成-p 80:80这样访问http://宿主机IP就能直接看到 nginx 页面不用加端口号。生产环境一般就是这么用的。3.3 挂载目录让配置“留”下来容器默认是临时的。你docker rm删掉容器里面改过的配置、产生的数据全没了。这在生产环境是不可接受的。解决办法是“挂载mount”——把宿主机的某个目录直接“映射”到容器里容器写文件实际写的是宿主机的目录。docker run -d --name nginx \ -p 80:80 \ -v /home/user/nginx/html:/usr/share/nginx/html \ -v /home/user/nginx/conf:/etc/nginx/conf.d \ nginx这条命令的作用是把宿主机的/home/user/nginx/html目录映射为容器的/usr/share/nginx/htmlnginx 存放网页文件的目录把宿主机/home/user/nginx/conf映射为容器的/etc/nginx/conf.dnginx 读取额外配置的目录。这样配置文件的修改就简单了在宿主机上编辑文件容器里同步生效不用进容器敲 vim。而且即使容器删了重建配置和数据还在宿主机上不会丢。我觉得挂载目录这个功能是 Docker 真正“生产力”的来源之一。没有挂载容器就是个玩具有了挂载容器才能承载真实业务。4. 第一次正式实战用 Docker 跑一个 nginx 容器4.1 写一个最简单的 HTML 页面作为验证光说不练假把式。我们从零开始完整走一遍“用 Docker 部署一个 nginx 并访问到它”的过程。先在宿主机创建一个目录放一个简单的 HTML 文件mkdir -p ~/nginx-demo/html cd ~/nginx-demo/html vim index.html内容随意比如!DOCTYPE html html head titleDocker Nginx Demo/title /head body h1Hello from Docker Nginx!/h1 /body /html然后基于 nginx 镜像启动容器把刚才的目录挂载进去并映射端口docker run -d \ --name nginx-demo \ -p 8080:80 \ -v ~/nginx-demo/html:/usr/share/nginx/html \ nginx这里我故意用 8080 来映射避开 80 端口可能的冲突也顺便演示端口映射。然后浏览器访问http://localhost:8080如果你在服务器上操作访问http://服务器IP:8080如果 Windows 下 Docker Desktop访问http://localhost:8080即可。能看到 “Hello from Docker Nginx!” 就成功了。这时候有几个常用命令可以验证一下# 看容器状态 docker ps # 看容器实时日志CtrlC 退出 docker logs -f nginx-demo # 进入容器内部 docker exec -it nginx-demo bash # 在容器里看进程和配置文件你已经进入容器了 ps aux | grep nginx ls /etc/nginx/我特别建议你“进容器内部”这一步多待一会儿把 nginx 的配置目录结构翻一遍。见过真面目之后很多概念自然就通了比如/etc/nginx/nginx.conf是主配置文件/etc/nginx/conf.d/是放自定义配置的地方/usr/share/nginx/html是放网页文件的地方。4.2 端口冲突和镜像老版本这两个小问题第一次跑容器十有八九会遇到端口冲突。错误提示大概长这样docker: Error response from daemon: driver failed programming external connectivity on endpoint nginx-demo: Bind for 0.0.0.0:80 failed: port is already allocated.意思是宿主机的 80 端口已经被占了。怎么排查# 看谁占了宿主机 80 端口 sudo lsof -i :80 # 或者 netstat -tunlp | grep 80查到占用进程后两种处理要么停掉占用的服务要么把映射端口改成别的比如-p 8081:80。另一个容易被忽略的是镜像版本问题。默认docker pull nginx拉取的是 latest 标签这是官方 Dockerfile 里定义的最新稳定版。但最新版在特定机器上可能因为系统 libc 版本或架构不同启动时报错。稳妥做法是指定版本docker pull nginx:1.27-alpine docker run -d --name nginx-demo -p 8080:80 nginx:1.27-alpinealpine这个标签特别推荐它基于 Alpine Linux体积只有十几 MB比标准版小了一个数量级日常用完全够了。生产环境里我基本都用 alpine 变种省磁盘、拉取快、攻击面小缺点是对某些依赖第三方 .so 库的应用可能装起来麻烦nginx 完全没问题。4.3 为什么容器退出后像“消失”了一样很多人第一次用 Docker 都会有这么一个困惑我docker run起了一个容器然后 CtrlC 退出或 CtrlQ 退出怎么再找也找不到了比如有人这么操作docker run --rm -it nginx bash然后输入exit容器就被删除了——因为--rm参数的意思是“容器退出时自动删除”。这本身没问题但新手容易产生误解以为容器丢了。更常见的场景是容器因为错误退出了。比如里面启动的进程崩溃容器停止运行。这时候docker ps看不到它要用docker ps -a才能看到。排查死掉容器的方法# 看所有容器包括已停止的 docker ps -a # 看容器的日志找崩溃原因 docker logs 容器ID或名字我见过太多人一遇到容器退出的情况第一反应就是删掉重建从不看日志。其实绝大多数问题docker logs里都有答案。养成“先看日志、再下结论”的习惯能少走很多弯路。顺便再说一个关键认知容器的生命周期和它里面跑的进程是绑定的。如果容器里跑的是 nginx 这种前台进程nginx 活着容器就活着nginx 挂了容器就停。所以 Docker 里跑服务必须保证是前台进程不能像在系统里那样service nginx start然后后台启动就完事。这也是 nginx 官方镜像的默认 CMD 是nginx -g daemon off;的原因——强制前台运行让容器保持存活。理解了这个原理你就知道为什么自定义 Dockerfile 时最后一条命令通常不能加或者写一个会直接退出的脚本。5. 反向代理的本质与配置从单站点到多站点转发5.1 反向代理到底是什么这一步是整篇博文的核心。部署 nginx 从来不只是为了让你敲个 IP 访问个静态页面真正的日常用途之一是反向代理。概念先搞明白正向代理是你主动去配置通过代理服务器访问外网资源代理服务器代替你请求外部网站然后在把响应返回给你。而反向代理是在服务器端配置的客户端不知道你的请求实际上被转发到了哪台后端机器最后返回结果给客户端。听起来有点绕画个实际场景就秒懂。假设你有一个域名www.example.com服务器上跑着两个应用一个 Java 后端服务监听 8081 端口一个 Python 后端服务监听 8082 端口你希望用户访问http://www.example.com/api/java/的时候请求被转发到 8081访问http://www.example.com/api/python/的时候转发到 8082。这时候 nginx 就充当“调度员 门卫”的角色根据路径把请求分流到对应的后端。用户并不知道也不关心 8081、8082 的存在他们只知道你提供了一个统一的入口www.example.com。这就是反向代理。5.2 nginx 配置文件结构速览nginx 的配置核心是搞清楚两个文件的关系/etc/nginx/nginx.conf主配置文件定义全局行为比如运行用户、进程数、事件模型以及http块。/etc/nginx/conf.d/*.conf额外配置会被主配置里的include /etc/nginx/conf.d/*.conf;语句自动加载。实践中的建议别在主配置文件里堆配置每个站点/服务建一个独立的 conf 文件放在 conf.d 目录结构清晰便于管理。这也正是 Docker 挂载目录的用武之地——宿主机的conf目录挂载成容器里的/etc/nginx/conf.d以后新增反代配置只需要在宿主机新建一个.conf文件重启 nginx 即可。一个最简反向代理配置长这样server { listen 80; server_name www.example.com; location / { proxy_pass http://192.168.1.100:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }逐行解释一下这段配置背后的逻辑server块定义一个虚拟主机。listen 80监听 80 端口server_name指定域名。如果不写server_name那这个 server 块匹配所有请求。location /匹配所有以/开头的请求也就是所有请求。proxy_pass http://192.168.1.100:8080;核心指令把匹配到的请求转发到这个地址。三个proxy_set_header把客户端真实 IP、请求头等信息传给后端。不设置的话后端拿到的 IP 全是 nginx 所在机器的 IP客户端真实 IP 就丢了。很多后端做日志分析、人机校验的应用都是依赖X-Real-IP来获取真实 IP 的这个头必须带上。5.3 实战Docker 里两台 nginx 互做反向代理光看配置不落地等于白学。我们来一个完全基于 Docker 的实战跑两个 nginx 容器然后用第三个 nginx 容器做反向代理把请求转发到前两个。第一步起两个“陆后”服务模拟不同的后端应用# 第一个“后端”容器 docker run -d --name app1 \ -v ~/nginx-demo/app1:/usr/share/nginx/html \ nginx:1.27-alpine # 第二个“后端”容器 docker run -d --name app2 \ -v ~/nginx-demo/app2:/usr/share/nginx/html \ nginx:1.27-alpine分别在这两个 html 目录里放个不同的 index.html内容分别写“This is App 1”和“This is App 2”。注意这里我没有映射端口——因为这两个容器不需要对宿主机暴露它们只在内网被反代容器访问。这演示了 Docker 网络的一个优势容器间可以直接用容器名互相访问不需要走宿主机端口。第二步起反代容器把两个端口暴露出去先写一个反代配置文件proxy.conf放在宿主机~/nginx-demo/proxy/目录下upstream app_cluster { server app1:80; server app2:80; } server { listen 80; location / { proxy_pass http://app_cluster; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }然后启动反代容器docker run -d --name nginx-proxy \ -p 80:80 \ -v ~/nginx-demo/proxy:/etc/nginx/conf.d:ro \ --link app1:app1 \ --link app2:app2 \ nginx:1.27-alpine重点来了这里如果不加--link或者不放到同一个自定义网络里反代容器里是解析不了app1和app2这两个主机名的proxy_pass http://app1:80会直接报 “host not found in upstream”。Docker 有个默认网络叫 bridge容器默认都连在这个网络上但 bridge 网络默认不做 DNS 解析。容器间通信有两种推荐方式老式用--link参数简单粗暴但已经过时了官方不推荐新项目用。推荐创建一个自定义 bridge 网络把这个网络里所有容器加入Docker 自动做 DNS 解析。操作方式docker network create my-net docker run -d --name app1 --network my-net \ -v ~/nginx-demo/app1:/usr/share/nginx/html \ nginx:1.27-alpine docker run -d --name app2 --network my-net \ -v ~/nginx-demo/app2:/usr/share/nginx/html \ nginx:1.27-alpine docker run -d --name nginx-proxy --network my-net \ -p 80:80 \ -v ~/nginx-demo/proxy:/etc/nginx/conf.d:ro \ nginx:1.27-alpine这样容器间通过容器名解析稳定可靠。浏览器访问http://localhost:80多刷新几次你会看到 App1 和 App2 交替出现——这就是upstream的轮询负载均衡。5.4 多域名反向代理一个容器转发到 N 个后端上面演示的是“同一路径、多台后端”的负载均衡下面演示更常见的“不同域名 / 不同路径转发到不同后端”。比如你有两个服务blog.example.com→ 转发到一个静态博客api.example.com→ 转发到一个后端接口服务判断不同站点靠的就是server_name。在同一个反代容器里可以配置多个server块# blog.conf server { listen 80; server_name blog.example.com; location / { proxy_pass http://blog_backend:80; } } # api.conf server { listen 80; server_name api.example.com; location / { proxy_pass http://api_backend:8080; } }这里有个关键细节同一个容器里多个 server 块监听同一个 80 端口时不冲突因为 nginx 根据Host请求头也就是用户访问的域名来路由。请求头里带的 Host 是blog.example.com就走第一个 server 块Host 是api.example.com就走第二个。这就是反向代理的“调度员 门卫”核心理念的进阶版一个入口N 个出口全凭 Host 头来分流。我可以实际告诉你一个经验在proxy_pass和location搭配时是否保留路径前缀是个高频坑。比如# 访问 /api/xxx → 转发到 http://backend:8080/api/xxx location /api/ { proxy_pass http://backend:8080; } # 访问 /api/xxx → 转发到 http://backend:8080/xxx注意路径前缀被去掉了 location /api/ { proxy_pass http://backend:8080/; # 带了这个斜杠 }第二种写法/api/前缀会被“吃掉”后端收到的是/xxx。如果你的后端路由是基于/api前缀的这样写会把接口全部 404 掉。但凡换了反代地址一定要先确认路径映射关系这个坑我踩过不止一次。5.5 重启和验证配置的正确姿势nginx 配置改完不需要重启整个容器发送重载信号即可# 在宿主机执行进入容器 docker exec nginx-proxy nginx -t先nginx -t测试配置语法是否正确然后重载docker exec nginx-proxy nginx -s reloadnginx -t这一步绝对不能省。我见过太多人改完配置直接 reload语法错了 nginx 直接退出服务全挂。尤其是我这种习惯“批量改配置”的人改三四个文件后一起reload一个文件少个分号整个 nginx 全部挂掉回滚的时间比谨慎测试的时间长十倍。更稳妥的做法是在宿主机用docker cp把配置文件复制进容器或者直接在宿主机编辑用了挂载目录就不需要复制然后nginx -t验证无误后nginx -s reload。5.6 反向代理故障排查三板斧反代配置不复杂但真出问题的时候新手容易一头雾水。分享三个排查技巧按顺序执行九成问题都能定位。第一板斧看 nginx 日志。错误日志在容器的/var/log/nginx/error.log或者直接用docker logs nginx-proxy看容器 stdout 输出。最常见的报错有connnect() failed (111: Connection refused)后端服务没启动或者端口不对host not found in upstream app1容器间 DNS 解析不了检查网络是否同一个worker_connections are not enough连接数上限被撑爆调配置第二板斧在宿主机和后端容器上双向验证连通性。在反代容器里docker exec -it nginx-proxy sh # 容器里如果有 curl 或 wget 就直接请求后端没有就安装/换基础镜像 curl http://app1:80 curl http://app2:80如果这里能通说明容器间网络没问题如果连不通那就不是 nginx 配置的问题是 Docker 网络的问题。第三板斧确认后端确实返回了预期内容。有时候反代配置没问题但页面报 502 或 504。502 通常是后端连不上504 通常是后端起响应了但超时。可以请求后端实际地址先直接映射个端口验证看后端口到底通不通。6. 后续怎么进阶镜像、Compose 和日常维护的几条建议6.1 自己写一个 Dockerfile 才是入门的真正分界线能流畅地玩docker run、docker ps、配置 nginx 反代你已经超过很多“半吊子”了。但要说真正入门了我建议动手写一个自己的 Dockerfile——相比直接拉现成镜像自己写 Dockerfile 是理解镜像构建原理的关键一步。为什么这么重要因为直接从 Docker Hub 拉镜像你不关心镜像里面怎么搭的一旦写 Dockerfile你就被迫思考基础镜像选什么、依赖怎么装、文件怎么拷、启动命令怎么写、环境变量怎么传。写一个最简单的例子部署一个静态站点用 nginx。# 用官方 nginx 镜像做基础 FROM nginx:1.27-alpine # 把自己的网页文件复制过去 COPY index.html /usr/share/nginx/html/index.html # 声明端口 EXPOSE 80 # 默认命令 CMD [nginx, -g, daemon off;]构建和使用docker build -t my-static-site:1.0 . docker run -d --name static-site -p 9090:80 my-static-site:1.0这个 Dockerfile 简单到看一眼就懂但它背后的逻辑不简单镜像是一层一层叠加的。FROM拉取的 nginx 镜像是一层COPY命令又叠一层新的。每一层都是只读的构建时会做缓存——如果你改了index.html重新构建只有COPY这一层会重新执行前面的层直接走缓存构建速度快到起飞。这个机制是理解 Docker 镜像管理和优化 Dockerfile 的核心值得专门花时间体会。进阶一点的 Dockerfile 会涉及多阶段构建、环境变量、健康检查探针这些后面可以单独开一篇聊。6.2 镜像下载加速方案对比国内环境拉取 Docker Hub 镜像速度不稳定经常几十 KB/s一个几百 MB 的镜像能下半小时。配置镜像加速几乎是国内用户的必经之路。镜像加速的原理不复杂把 Docker 默认的镜像源替换成一个国内可以较快访问的镜像仓库地址。Docker 支持在/etc/docker/daemon.json里配置registry-mirrors列出多个镜像源地址拉取镜像时 Docker 会按顺序尝试。实测对比基于我自己的网络环境你自己测试可能不同镜像源速度体验稳定性是否需要登录Docker Hub 官方源慢或不稳定不稳定不需要阿里云容器镜像服务快稳定需要实名认证每个账号有专属加速地址中科大镜像较快依赖网络环境不需要Docker 官方中国镜像较快时有波动不需要配置方法如下编辑/etc/docker/daemon.json没有就新建{ registry-mirrors: [ https://your-user-id.mirror.aliyuncs.com, https://docker.mirrors.ustc.edu.cn ] }然后重启 Dockersudo systemctl restart docker配置完可以用docker info查看是否生效输出里会有一行Registry Mirrors:列出你配置的地址。6.3 Docker Compose多容器服务的脚手架单容器玩熟之后很快会遇到多容器协作的场景一个 web 应用前面 nginx 反代后面一个 MySQL、一个 Redis可能还有后端应用。手动一个个docker run也能跑但每次都要回忆参数顺序还容易搞混。Docker Compose 就是来解决这个问题的把多个容器的启动参数写到一个 YAML 文件里一条命令全部启动一条命令全部停止。拿我们前面那个“两台后端 一台反代”的例子来说用 Compose 写是这样的version: 3.8 services: app1: image: nginx:1.27-alpine volumes: - ~/nginx-demo/app1:/usr/share/nginx/html app2: image: nginx:1.27-alpine volumes: - ~/nginx-demo/app2:/usr/share/nginx/html nginx-proxy: image: nginx:1.27-alpine ports: - 80:80 volumes: - ~/nginx-demo/proxy:/etc/nginx/conf.d:ro depends_on: - app1 - app2启动docker compose up -d就这一条命令compose 会帮你在一个独立的网络上创建网络、启动容器、配置容器间 DNS 解析所以app1、app2这些主机名在反代容器里直接可用。停止docker compose downdepends_on的作用是指定启动顺序——先启动 app1 和 app2再启动 nginx-proxy避免反代容器启动时后端还没就绪导致 DNS 解析失败。这个字段在 Compose 里非常实用。顺带说一句Compose 文件里的语法和docker run参数对应关系很好记ports对应-pvolumes对应-venvironment对应-edepends_on对应手动控制启动顺序。熟练了一个另一个基本不用学。6.4 日常维护利器日志清理和容器资源限制容器跑久了你会发现磁盘空间悄无声息地变小。这主要是因为容器日志无限增长默认情况下 Docker 不会自动切割日志构建镜像产生的悬空镜像dangling images堆积停止的容器和旧的构建缓存一条命令处理悬空镜像和停止容器docker system prune -af该命令会删除所有停止的容器、所有没有被使用的网络、所有悬空镜像和构建缓存。注意-a的意思是“删除所有”如果你有不在运行的容器但还想留着别加-a只执行docker system prune。我一般会加--volumes把没用的匿名卷也清掉docker system prune -af --volumes另一个容易忽略的问题是日志增长。给 nginx 反代容器加日志切割配置在启动时指定docker run -d --name nginx-proxy \ --log-driver json-file \ --log-opt max-size10m \ --log-opt max-file3 \ nginx:1.27-alpine这样每个日志文件最多 10MB最多保留 3 个老日志自动轮转不会无限膨胀。生产环境的容器都应该加上这个配置不然后期撑爆磁盘只是时间问题。写在最后的一些零碎经验这篇实战走下来安装、核心概念、跑容器、反向代理、进阶方向基本都覆盖了。最后分享几条纯个人的体会。第一学 Docker 别死记命令要“用场景带动命令”。你折腾一次“容器间互相访问时用容器名互相 ping”比背十遍“docker network create”有用得多。真到用的时候自然想得起来这条命令该用在哪。第二nginx 反代配置的原则是“小步快跑持续验证”。改一个块就nginx -t一次验证有效后再改下一个。别学我一开始那样一口气堆完所有配置再一次性 reload出了问题都不知道是哪一段语法挂了。第三容器化部署不等于无忧运维。Docker 和 nginx 本身不解决高可用问题——容器挂了就是挂了nginx 反代断了就是断了。要真正高可用得搭配健康检查、重启策略、编排系统比如 K8s这些基础设施。初学者阶段先把单机这一套玩明白后面再演进到集群是完全自然的路径。第四也是最重要的一条实践遇到的报错是最宝贵的学习材料。我见过很多人在报错面前选择直接重装一次两次可以但每次都绕过问题就永远学不会排查思维。凡是docker logs能看到的错误都有解凡是nginx -t报的语法错误都在配置里能改。忍住重装的手一步步拆解问题你会发现自己成长得比想象中快。把这些话记在心里配合上面的实战步骤走一遍你就已经具备独立用 Docker 部署 nginx 反向代理的基础能力了。接下来想深挖哪个方向——Docker Compose 编排、Dockerfile 优化、还是 nginx 更多高级功能都是水到渠成的事。