news 2026/9/20 3:03:58

Docker实战指南:安装部署与nginx反向代理配置全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker实战指南:安装部署与nginx反向代理配置全解析

最近有不少朋友问我,工作环境里总是要跟环境部署打交道,Docker 到底该怎么上手?装好了之后又怎么把服务跑起来、怎么让外部请求正确打到内部服务上?这些问题如果只靠零散搜索,很容易一头扎进细节里出不来。

这篇文章我就用一条完整的实战路径来带你过一遍:从零开始装好 Docker,再拉取 nginx 镜像、启动容器、配置反向代理,最后把常见坑都给你指出来。这条路径我已经在不同机器上反复走过很多次,无论你是后端开发想本地起环境,还是运维同学要快速部署服务,还是刚接触容器化想系统入门,按照文章的顺序操作一遍,基本就能摸清 Docker + nginx 的核心玩法。

1. 核心思路拆解:为什么是 Docker + nginx 这台组合

1.1 容器化到底解决了什么问题

先聊一个底层问题:为什么现在部署服务都爱用 Docker?说白了,就是“环境一致性”这四个字。

我经常打一个比方:以前部署一个 web 项目,你要先装操作系统依赖、装运行时、装中间件、配环境变量,每一步都可能因为系统版本、软件源差异或者某个库的版本不对而挂掉。换一台机器等于重新折腾一遍,这就像搬家的时候连水电管道都要重新铺,累不累?

Docker 做的事,就是把你的应用连同它需要的运行环境一起打包成一个“集装箱”。这个集装箱在任何装了 Docker 的机器上都能跑,不管底层是 Windows、Ubuntu 还是 CentOS。对于 nginx 来说也一样,你不需要关心宿主机上有没有编译依赖、有没有 openssl 版本冲突——容器里自带一套干净、确定性的运行环境。这样你本地测出来的行为和生产环境是一致的,调试成本直线下降。

1.2 为什么反向代理要用 nginx 来做

你可能会有个疑问:我已经有应用服务了,为什么还要在前面挡一层 nginx?

这里得先分清楚两个概念:正向代理和反向代理。正向代理是替客户端去访问服务器,你访问不了某个资源,找一个能访问的代理服务器帮你取回来,这是“代理客户端”;反向代理是替服务器接收请求,客户端只知道 nginx 的地址,不知道背后的应用服务器是谁,这是“代理服务器”。

反向代理在实际部署里有几个非常实用的场景,我在生产环境中都踩过:

  • 统一入口:多个应用(比如前端、后端 API、管理后台)分别跑在不同端口,通过 nginx 按路径或域名分发到不同服务,外部只需要暴露 80/443 一个入口。
  • 负载均衡:同一个服务起了多个实例,nginx 可以把请求轮询或按权重分发到不同实例上,实现水平扩容。
  • SSL 终结:证书配置在 nginx 这一层,内部服务继续走 http,既省事又安全。
  • 静态资源服务:前端打包后的静态文件直接交给 nginx 托管,性能好,还不用占用应用进程的资源。

所以 Docker 负责把你的 nginx 和环境一起打包,nginx 负责流量调度和转发,两者结合,刚好覆盖了从“环境交付”到“流量管理”的完整环节。这也是为什么这套组合在微服务、前后端分离项目里几乎成了标配。

2. 实操准备:在不同系统上把 Docker 装好并跑通

2.1 Windows 篇:使用 Docker Desktop 快速上手

Windows 上目前最正规的姿势就是装 Docker Desktop。它自带图形界面,集成了 Docker Engine、命令行工具、Kubernetes 集群(可选),对新手来说很友好。

安装步骤大致是这样的:

  1. 去 Docker 官网下载 Docker Desktop for Windows 的安装包。
  2. 双击安装,在配置界面勾选 “Use WSL 2 instead of Hyper-V”(推荐,性能更好且兼容性高),然后一路 Next。
  3. 安装完成后重启系统,打开 Docker Desktop,等待右下角鲸鱼图标变成绿色的 Running 状态。
  4. 打开 PowerShell 或 CMD,输入docker version验证安装。

需要注意的坑:安装之前请确保 Windows 的 BIOS 里开启了虚拟化(VT-x / AMD-V),并且系统功能里启用了 “适用于 Linux 的 Windows 子系统” 和 “虚拟机平台”。这两个没开,Docker Desktop 会各种报错。

2.2 Linux 篇:Ubuntu / CentOS 的安装命令

Linux 上装 Docker 就一句话的事,但不同发行版还是有区别。

Ubuntu 上推荐使用官方脚本安装,方便快捷:

curl -fsSL https://get.docker.com | sh

这个脚本会自动配置好软件源、安装 docker-ce 和 containerd,装完以后记得把当前用户加入 docker 组,不然每次执行 docker 命令都要加 sudo:

sudo usermod -aG docker $USER

改完组之后必须重新登录或者执行newgrp docker才能生效,否则会提示 permission denied。

CentOS / RHEL 系可以用官方仓库安装:

sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io sudo systemctl start docker sudo systemctl enable docker

2.3 安装验证与基础命令速记

无论哪个平台,装完之后都建议先跑一遍基础检查:

# 查看版本,确认客户端和服务端都正常 docker version # 查看当前 Docker 运行状态 docker info # 跑一个 hello-world 测试容器 docker run --rm hello-world

hello-world镜像能正常拉取并打印提示,说明 Docker 引擎已经正常工作。

这里顺便把接下来会反复用到的几个核心命令整理一下,你可以先收藏:

# 镜像操作 docker pull nginx:latest # 拉取镜像 docker images # 查看本地镜像列表 docker rmi nginx # 删除镜像 # 容器操作 docker ps # 查看运行中的容器 docker ps -a # 查看所有容器(包括已停止) docker start/stop/restart <容器名> # 启动/停止/重启容器 docker rm -f <容器名> # 强制删除容器 docker logs -f <容器名> # 跟踪容器日志 # 进入容器内部 docker exec -it <容器名> bash

2.4 一个绕不开的问题:镜像下载慢怎么办

第一次拉取镜像的时候,很多人会被下载速度气得摔键盘。这主要是因为官方仓库在国外,跨海传输带宽有限。

解决方案是配置国内镜像加速器。在 Docker Desktop 的 Settings → Docker Engine 里,或者在 Linux 的/etc/docker/daemon.json里,添加镜像源配置:

{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.nju.edu.cn" ] }

修改之后重启 Docker 服务让配置生效:

sudo systemctl restart docker

配置好之后再用docker pull nginx,速度会有天壤之别。镜像源如果失效了,可以换一个,网上随时能找到可用的公共加速地址,但注意一定要选大厂或者高校维护的,稳定性有保障。

3. nginx 容器部署与反向代理的完整配置流程

3.1 拉取镜像与首次启动

环境搞定之后,我们正式进入主题。第一步先拉取 nginx 官方镜像:

docker pull nginx:latest

拉取完成后,先简单启动一个临时容器,验证一下 nginx 能不能正常工作:

docker run -d --name test-nginx -p 8080:80 nginx

这条命令的含义拆开解释:

  • -d:后台运行容器,不占住当前终端。
  • --name test-nginx:给容器起个名字,后面操作方便。
  • -p 8080:80:端口映射,宿主机 8080 端口接收到的请求转发到容器内部的 80 端口。nginx 默认监听 80,所以这里必须映射 80,宿主机的端口可以随意换。
  • nginx:指定使用的镜像名。

启动后在浏览器访问http://宿主机IP:8080,能看到 nginx 默认的欢迎页,说明容器已经跑起来了。这个欢迎页的意义在于:Docker 容器内部的网络是隔离的,能通过宿主机端口访问到容器内服务,说明端口映射链路是通的,后续反向代理配置有了一个可靠的起点。

3.2 理解 nginx 容器内的目录结构

直接启动容器虽然简单,但你随即会遇到一个尴尬的问题:nginx 的配置文件在容器里面,如果直接在容器里改配置,容器一删配置就全没了。这不符合我们的“可复现、可迁移”需求。

所以正式使用前,必须先把容器内的关键目录挂载到宿主机上。nginx 镜像里核心路径有三个:

  • /etc/nginx/nginx.conf:主配置文件,控制全局行为,引用其他配置。
  • /etc/nginx/conf.d/:用来放各个 server 块的配置文件,通常一个站点或一个应用对应一个.conf文件。
  • /usr/share/nginx/html/:默认的静态网页目录。

在宿主机上创建好对应目录,然后启动容器时用-v参数挂载进去:

mkdir -p /opt/nginx/conf.d mkdir -p /opt/nginx/html mkdir -p /opt/nginx/logs docker run -d \ --name web-nginx \ -p 80:80 \ -v /opt/nginx/conf.d:/etc/nginx/conf.d \ -v /opt/nginx/html:/usr/share/nginx/html \ -v /opt/nginx/logs:/var/log/nginx \ nginx

这样之后修改宿主机上的配置和网页文件,容器内会直接同步生效,不需要再跑进容器里折腾编辑器。

3.3 反向代理的核心配置详解

接下来是这篇文章的重头戏:配置反向代理。

假设你的服务器上跑了一个后端应用,监听在 8081 端口(比如一个 Java 服务或者 Node.js 服务),现在需求是:用户访问http://你的服务器/,nginx 把请求转发到http://你的服务器:8081/,同时用户感知不到后端服务的存在。

在宿主机/opt/nginx/conf.d/下新建一个配置文件,命名为myapp.conf,内容如下:

server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8081; 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; } }

逐行说下关键配置的含义:

  • server_name:匹配域名。如果本地测试,填localhost或留空默认也行。
  • proxy_pass:转发目标地址。注意这里写的是127.0.0.1:8081,因为 nginx 容器和宿主机是共享网络的?——不对,这里有个大坑,如果你用默认的 bridge 网络模式,容器内的127.0.0.1指的是容器自己,不是宿主机。所以反向代理后端应用时,地址要写宿主机的内网 IP,或者使用 Docker 的魔法地址host.docker.internal,或者干脆把网络模式设为 host。

这个坑很多人都会踩,我单独在下一节详细说。

  • proxy_set_header系列:这些 header 是让后端服务能拿到客户端真实的 IP 和协议信息。否则后端看到的请求来源全是 nginx 容器,日志排查会一头雾水。

改完配置后重载 nginx 让配置生效:

docker exec web-nginx nginx -s reload

这种方式的优雅之处在于,nginx -s reload可以在不中断服务的情况下应用新配置,不会因为配置错误造成长时间停机。

3.4 bridge 网络模式下代理宿主机服务的正确姿势

如果你按照 3.3 的配置把proxy_pass写成http://127.0.0.1:8081,访问时大概率会得到502 Bad Gateway。原因就是上面提到的:nginx 跑在容器里,容器内的 127.0.0.1 是容器自己的网卡,宿主机上监听 8081 的服务它根本够不着。

解决办法有三条路,按推荐程度排序:

方案一:使用 host 网络模式

启动容器时加--network host,让容器直接共享宿主机的网络栈:

docker run -d \ --name web-nginx \ --network host \ -v /opt/nginx/conf.d:/etc/nginx/conf.d \ nginx

这种模式下,nginx 的127.0.0.1:8081就是宿主机的127.0.0.1:8081,配置简单直接。缺点是端口隔离能力变弱,nginx 占用的是宿主机端口,不过对于单机部署 nginx 的场景,这是最省心的选择。

方案二:使用特殊 DNS 名称

在 Linux 上,Docker 默认没有host.docker.internal这个解析。但在 Docker Desktop(跨平台)和较新版本的 Docker Engine 中,加一段配置可以启用:

docker run -d \ --name web-nginx \ --add-host host.docker.internal:host-gateway \ -p 80:80 \ -v /opt/nginx/conf.d:/etc/nginx/conf.d \ nginx

配置里就可以这样写:

proxy_pass http://host.docker.internal:8081;

方案三:使用宿主机局域网 IP

直接写宿主机的局域网 IP,比如proxy_pass http://192.168.1.100:8081;。缺点是这个 IP 可能变化,不够灵活,但如果你只是本地测试,完全可行。

我个人的建议是:如果只想跑一个 nginx 容器做网关层,优先用 host 网络模式;如果还要和其他容器做联动,再考虑 bridge +host.docker.internal的方案。

3.5 反向代理多站点场景的配置示例

很多时候一个 nginx 要做多个站点的分发,比如:

  • app1.example.com分发到后端 8081
  • app2.example.com分发到后端 8082
  • 测试环境根据路径/api/转发到某个服务

这种场景下,只要在conf.d目录下新建多个.conf文件即可,每个文件一个 server 块,nginx 启动时会自动加载所有配置文件。

再举个例子,一个前端页面 + 后端 API 分离的典型配置:

server { listen 80; server_name www.example.com; # 前端静态资源直接由 nginx 托管 location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } # API 请求全部反向代理到后端服务 location /api/ { proxy_pass http://host.docker.internal:8081/; 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_pass的路径结尾是否带斜杠,会影响转发后路径拼接。location /api/搭配proxy_pass http://xxx:8081/,请求/api/users会被转发为/users,也就是location前缀会被替换掉;如果proxy_pass不带斜杠写http://xxx:8081,请求会被转发为/api/users,路径原样保留。具体用哪种,取决于后端接口的设计,你需要跟后端同事确认好再写。

这种一个 nginx 里同时托管静态资源和处理接口转发的模式,就是典型的前后端分离部署形态,生产环境里大量项目都是这么玩的。

4. 进阶玩法:docker-compose 编排 nginx 和多个后端服务

4.1 为什么要用 docker-compose

当你的部署体量变大,比如要同时启动 nginx、后端服务、数据库、Redis,每启一个容器都要敲一长串docker run命令,这就变得不可维护了。这时候就该上 docker-compose。

docker-compose 的核心思想是“用一份 YAML 文件描述整个应用栈的拓扑结构”,一个命令就能把整套服务启起来。它还解决了服务间通信的问题:在同一 compose 项目里,容器可以通过服务名互相访问,不需要记 IP,不需要做端口映射到宿主机才能互通。

4.2 实战:写一个多服务编排文件

假设我们要编排一个典型 Web 应用:nginx + 后端 API + MySQL。对应docker-compose.yml文件如下:

version: '3.8' services: nginx: image: nginx:latest container_name: gateway-nginx ports: - "80:80" - "443:443" volumes: - /opt/nginx/conf.d:/etc/nginx/conf.d - /opt/nginx/html:/usr/share/nginx/html - /opt/nginx/logs:/var/log/nginx depends_on: - backend networks: - app-net backend: image: my-backend:latest container_name: backend-service environment: - DB_HOST=mysql - DB_PORT=3306 - DB_PASSWORD=secret depends_on: - mysql networks: - app-net mysql: image: mysql:8.0 container_name: app-mysql ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: myapp volumes: - mysql-data:/var/lib/mysql networks: - app-net volumes: mysql-data: networks: app-net: driver: bridge

重点看几个设计要点:

  • networks自定义了一个 bridge 网络app-net,三个服务都在这个网络里。nginx 配置文件里代理后端时,proxy_pass可以直接写http://backend:8081——backend就是 compose 里服务名,由 Docker 内置 DNS 自动解析,这是和单容器部署最大的区别。
  • depends_on控制容器的启动顺序,先启动数据库再启动后端。注意它只保证启动顺序,不保证数据库完全可用,所以后端应用代码里最好有重连机制。
  • mysql 的ports这里映射了 3306,这是为了方便你在宿主机上用数据库客户端连上去调试。实际生产环境如果不希望数据库暴露到外部,可以把ports去掉,只保留内部网络访问。

启动方式和单容器不同,准备工作做完后:

docker-compose up -d

查看服务状态:

docker-compose ps

查看日志:

docker-compose logs -f

停止并删除所有服务:

docker-compose down

这套玩法一旦用熟了,你会发现环境交付的复杂度被压得极低。新同事入职,把代码和这条 compose 文件给他,一条命令搞定环境,省下多少排障时间,不可估量。

5. 常见问题与故障排查技巧实录

5.1 镜像拉取慢、超时

现象docker pull nginx卡很久,或者报net/http: TLS handshake timeout

排查思路:先确认镜像源是否配置成功。执行docker info,在Registry Mirrors一栏能看到当前使用的镜像加速地址。如果没有配置,参照 2.4 节配置后再重启 Docker;如果配置了还是慢,把镜像源换成另一个再试。不同地区对不同镜像源的访问速度差异很大,多试几个总有一个快的。

5.2 端口被占用导致容器启动失败

现象:执行docker runError starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use

排查思路:这说明宿主机 80 端口已经被别的进程占用了。执行:

sudo lsof -i :80

或者:

sudo netstat -tlnp | grep :80

查看是什么进程占用了端口。处理方法有两种:停掉占用端口的程序,或者把 nginx 容器的端口映射换成一个未被占用的端口(比如-p 8080:80)。

5.3 访问 nginx 页面返回 502 Bad Gateway

现象:浏览器能访问到 nginx,但页面显示 502。

排查思路:502 意味着 nginx 无法连接到它要代理的后端服务。按这个顺序排查:

1.后端服务是否还在运行?docker ps看看容器状态。 2.后端服务监听端口是否正确?进入后端容器执行curl http://127.0.0.1:8081验证。 3.nginx 容器里访问后端地址是否通?执行:

docker exec web-nginx curl http://host.docker.internal:8081

这条命令如果超时,大概率是网络模式或地址写法问题。回到 3.4 节检查你的转发目标地址,是不是写了容器自己感知不到的地址。

5.4 配置改了但没生效

现象:修改了宿主机挂载的.conf文件,刷新页面没变化。

排查思路:nginx 不会自动热加载新配置。每次改完配置要执行重载命令:

docker exec web-nginx nginx -s reload

执行后配置立即生效。如果重载时报错,先检查配置文件里是不是少了分号或括号。可以用这条命令在容器里校验配置文件的语法:

docker exec web-nginx nginx -t

5.5 容器日志里反复报链接被拒绝

现象docker logs web-nginx里出现大量connect() failed (111: Connection refused)

排查思路:这个错误表明 nginx 尝试连接后端的 IP 和端口,但没人监听那个端口。最可能的原因就是反向代理地址写错,或者后端服务没有在该地址上启动。用docker exec进入容器手动 curl 一下代理目标,能快速定位是网络不通还是端口不对。

5.6 容器启动后立刻退出

现象:执行docker run后容器没几秒就退出了,docker ps -a看到状态是 Exited。

排查思路:查日志:

docker logs 容器名

nginx 容器如果启动即退出,通常是挂载的配置文件有问题,目录挂载到了错误的位置,或者容器内的配置引用了不存在的路径。此时可以把-v挂载先去掉,用默认配置启动,再逐步挂载排查,定位是哪个挂载项导致问题。

5.7 实际操作中最容易被忽略的三个细节

最后分享几个我踩过多次的坑,提醒你注意:

第一,挂载文件而不是挂载目录时,宿主机的文件必须存在。如果把单个配置文件挂载到容器里,但宿主机上这个文件不存在,Docker 会帮你创建一个目录,结果容器内路径变成了目录,nginx 直接拒绝启动。

第二,修改完配置记得先执行nginx -t再 reload。配置文件写错了直接 reload 会导致 nginx 拒绝加载,整个服务还可能挂掉。先检查语法,再应用,这个习惯能帮你省掉至少一半的排障时间。

第三,容器删除之前确认数据已经持久化。如果你启动容器没有挂载任何数据卷,直接docker rm -f之后,容器内所有配置和数据都会消失。尤其是 nginx 的配置文件,一定挂载到宿主机再操作,否则每次重建容器都要重新配置一遍,那是真的血泪教训。

写在最后

做运维和部署这件事,工具一直在变,但底层的思路没有变:环境要一致,配置要可管,状态要可控。Docker 把这三个诉求收敛成了一套标准化的操作方式,而 nginx 反向代理则是流量入口最基础也最重要的一环。

按我个人的习惯,刚接触这套东西时不用急着啃官方文档,先照着文章把 Docker 装好、把 nginx 容器跑起来、把反向代理配置通。当你亲手把一个请求从浏览器经过 nginx 转发到后端服务、再原路返回看到正确响应的时候,整个链路的工作原理就自然刻在脑子里了。之后再去看官方文档里更细的参数,你会发现一切都是顺理成章的事。

如果你照着这篇实操下来遇到了文章里没提到的问题,建议先执行docker logs 容器名看一下日志,再对照docker inspect 容器名检查网络和挂载配置,90% 的问题都能通过这些线索找到答案。祝愿一次跑通。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 3:03:40

PyTorch Lightning 2.0 升级指南:普通用户迁移清单与源码级解读

人工智能深度学习机器学习预训练分布式训练微调 【免费下载链接】pytorch-lightning Pretrain, finetune ANY AI model of ANY size on 1 or 10,000 GPUs with zero code changes. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/py/pytorch-lightning 点击查看 免费下载…

作者头像 李华
网站建设 2026/9/20 2:59:40

IDEA插件W-Reader:在IDE内高效阅读小说的完整指南

我写代码的时候有个习惯&#xff0c;键盘敲到一半&#xff0c;脑子里突然冒出“这章剧情到底怎么发展”的念头。以前只能切到浏览器偷偷摸摸开个页面&#xff0c;老板一走过来就手忙脚乱切回IDE。后来发现IDEA插件市场里有个叫W-Reader的阅读插件&#xff0c;支持在线搜索小说&…

作者头像 李华
网站建设 2026/9/20 2:59:37

2026年桌面AI办公工具盘点:5款最值得装的效率神器

2026年了&#xff0c;桌面AI办公工具已经不是“要不要用”的问题&#xff0c;而是“怎么选才能不踩坑”的问题。我花了两周时间&#xff0c;把市面上主流的桌面端AI生产力工具挨个装了一遍&#xff0c;每天在真实工作流里高强度试用&#xff0c;最后筛出了5款对普通打工人最友好…

作者头像 李华