1. 为什么选择 Docker 部署 Nginx?
在开始动手之前,我们先聊聊为什么要把 Nginx 装进 Docker 容器里。我刚开始接触 Docker 时也有这个疑问——直接在服务器上安装 Nginx 不香吗?直到有次服务器系统崩溃需要重装,我才真正体会到容器化的价值。
想象一下搬家时的场景:传统部署就像把家具直接钉在墙上,而容器化则是把所有物品打包进标准尺寸的集装箱。去年我帮朋友迁移个人博客,用传统方式花了3小时重新配置环境,而用 Docker 只用了10分钟——因为所有依赖都打包在镜像里了。
Docker 部署 Nginx 的核心优势:
- 环境一致性:再也不用说"在我电脑上是好的"这种话,开发机和生产环境100%一致
- 快速部署:一条命令就能拉起服务,特别适合在云服务器间迁移
- 资源隔离:Nginx 进程不会影响宿主机的其他服务
- 版本管理:可以同时运行多个 Nginx 版本做测试
2. 准备 Docker 环境
2.1 安装 Docker 引擎
新手最容易踩的坑就是 Docker 版本问题。我推荐使用官方脚本安装,避免包管理器带来的版本滞后。以 Ubuntu 为例:
# 卸载旧版本(如果有) sudo apt remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release # 添加官方GPG密钥 sudo mkdir -p /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 $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装Docker引擎 sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin安装完成后,记得把当前用户加入 docker 组,否则每次都要 sudo:
sudo usermod -aG docker $USER newgrp docker # 立即生效验证安装是否成功:
docker run hello-world如果看到"Hello from Docker!"的欢迎信息,说明环境已经就绪。我在阿里云ECS上实测,完整安装过程不超过5分钟。
2.2 Docker 基础概念速成
刚开始接触 Docker 时,我对镜像(Image)和容器(Container)的关系总是搞混。后来发现用快递包裹做类比特别容易理解:
- 镜像就像未拆封的快递包裹,里面包含完整的运行环境
- 容器是拆封后的包裹,正在运行中的实例
- Dockerfile就是包裹的装箱清单,告诉 Docker 如何打包
常用命令备忘录:
docker pull nginx # 下载镜像 docker images # 查看本地镜像 docker run -d nginx # 后台运行容器 docker ps # 查看运行中的容器 docker exec -it [容器ID] bash # 进入容器内部3. 部署 Nginx 容器
3.1 拉取官方镜像
Nginx 官方镜像有多个版本标签,新手建议选择 alpine 版本,体积小安全性高:
docker pull nginx:1.25-alpine拉取完成后可以检查镜像信息:
docker image inspect nginx:1.25-alpine | grep -i size输出显示这个镜像只有23MB,而完整版要超过100MB。我在树莓派上实测,alpine 版本的内存占用也少了30%。
3.2 运行第一个容器
基础运行命令:
docker run -d --name my-nginx -p 8080:80 nginx:1.25-alpine参数解释:
-d:后台运行--name:给容器起个名字-p 8080:80:把容器的80端口映射到宿主机的8080
打开浏览器访问 http://localhost:8080 就能看到 Nginx 欢迎页。如果是在云服务器上,记得在安全组开放8080端口。
3.3 自定义网站内容
默认容器里的网站内容在/usr/share/nginx/html,我们可以通过数据卷映射替换成自己的内容。先在主机创建网站目录:
mkdir -p ~/my-website echo "<h1>Hello Docker!</h1>" > ~/my-website/index.html然后重新运行容器并挂载目录:
docker run -d \ --name my-nginx \ -p 8080:80 \ -v ~/my-website:/usr/share/nginx/html \ nginx:1.25-alpine刷新页面就能看到自定义内容了。我在实际项目中常用这种方式快速调试静态页面,修改主机文件立即生效,不需要重启容器。
4. 使用 Dockerfile 定制镜像
4.1 编写第一个 Dockerfile
虽然直接运行官方镜像很方便,但真实项目通常需要自定义配置。创建一个 Dockerfile:
FROM nginx:1.25-alpine # 设置时区 RUN apk add --no-cache tzdata && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone # 复制自定义配置 COPY nginx.conf /etc/nginx/nginx.conf COPY conf.d/ /etc/nginx/conf.d/ # 复制网站文件 COPY html/ /usr/share/nginx/html/ # 暴露端口 EXPOSE 80 # 启动命令 CMD ["nginx", "-g", "daemon off;"]目录结构示例:
my-nginx/ ├── Dockerfile ├── nginx.conf ├── conf.d/ │ └── default.conf └── html/ ├── index.html └── assets/4.2 构建并运行自定义镜像
构建命令:
docker build -t my-custom-nginx .运行自定义镜像:
docker run -d \ --name my-nginx \ -p 8080:80 \ my-custom-nginx我习惯在 Dockerfile 同级目录放一个 build-and-run.sh 脚本:
#!/bin/bash docker build -t my-nginx . docker run -d --rm --name my-nginx -p 8080:80 my-nginx echo "Nginx is running on http://localhost:8080"4.3 常用 Nginx 配置调优
分享几个我在生产环境验证过的配置项:
- 性能优化(nginx.conf):
worker_processes auto; events { worker_connections 1024; multi_accept on; use epoll; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; server_tokens off; gzip on; gzip_types text/plain text/css application/json application/javascript; }- 虚拟主机配置(conf.d/default.conf):
server { listen 80; server_name example.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ =404; } location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 30d; } }5. 使用 Docker Compose 编排
5.1 编写 compose.yaml
当项目需要多个服务协同工作时,Docker Compose 是更好的选择。创建 compose.yaml:
version: '3.8' services: web: image: nginx:1.25-alpine container_name: my-nginx ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html - ./nginx.conf:/etc/nginx/nginx.conf - ./logs:/var/log/nginx restart: unless-stopped networks: - web-net networks: web-net: driver: bridge5.2 常用管理命令
启动服务:
docker compose up -d查看日志:
docker compose logs -f web停止服务:
docker compose down我习惯用这个命令一键重建服务:
docker compose up -d --force-recreate --build5.3 多容器协作示例
如果需要 Nginx 反向代理其他服务,可以这样配置:
version: '3.8' services: web: image: nginx:1.25-alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf depends_on: - app app: image: my-node-app:latest expose: - "3000" db: image: postgres:15 environment: POSTGRES_PASSWORD: example volumes: - db-data:/var/lib/postgresql/data volumes: db-data:对应的 Nginx 配置:
upstream app_server { server app:3000; } server { location / { proxy_pass http://app_server; proxy_set_header Host $host; } }6. 生产环境最佳实践
6.1 日志管理
默认情况下,Nginx 日志会输出到容器内的 /var/log/nginx。建议通过 volumes 映射到宿主机:
volumes: - ./logs/access.log:/var/log/nginx/access.log - ./logs/error.log:/var/log/nginx/error.log对于访问量大的站点,可以使用 json 格式日志方便分析:
log_format json_combined escape=json '{' '"time_local":"$time_local",' '"remote_addr":"$remote_addr",' '"request":"$request",' '"status": "$status",' '"body_bytes_sent":"$body_bytes_sent",' '"http_referer":"$http_referer",' '"http_user_agent":"$http_user_agent",' '"request_time":"$request_time"' '}'; access_log /var/log/nginx/access.log json_combined;6.2 性能调优
根据服务器配置调整 worker 参数:
worker_processes auto; # 自动匹配CPU核心数 worker_rlimit_nofile 100000; # 每个worker能打开的文件描述符数量 events { worker_connections 4096; # 每个worker的最大连接数 multi_accept on; # 一次性接受所有新连接 use epoll; # Linux高性能事件模型 }在 docker-compose 中限制资源:
deploy: resources: limits: cpus: '2' memory: 512M reservations: memory: 256M6.3 安全加固
- 禁用不必要的信息暴露:
server_tokens off; more_clear_headers Server; more_clear_headers X-Powered-By;- 添加基础安全头:
add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; add_header X-XSS-Protection "1; mode=block"; add_header Referrer-Policy "strict-origin";- 使用非root用户运行(Dockerfile):
RUN chown -R nginx:nginx /var/cache/nginx && \ chown -R nginx:nginx /var/log/nginx && \ chown -R nginx:nginx /etc/nginx USER nginx7. 常见问题排查
7.1 容器启动失败
先查看容器日志:
docker logs my-nginx常见错误1:端口冲突
Error: listen() to 0.0.0.0:80, backlog 511 failed (98: Address already in use)解决方案:更改映射端口或停止占用端口的服务
常见错误2:配置文件语法错误
nginx: [emerg] unknown directive "server_nam" in /etc/nginx/conf.d/default.conf:2解决方案:进入容器检查配置
docker exec -it my-nginx nginx -t7.2 性能问题排查
查看容器资源使用情况:
docker stats my-nginx进入容器分析:
docker exec -it my-nginx sh topNginx 状态监控:
location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; deny all; }7.3 网络连接问题
检查容器网络:
docker network inspect bridge测试容器间连通性:
docker run --rm --network container:my-nginx alpine ping -c 4 google.com查看 iptables 规则:
sudo iptables -L -n -t nat8. 进阶技巧
8.1 多阶段构建
对于需要编译的静态网站,可以使用多阶段构建减小镜像体积:
# 构建阶段 FROM node:18 as builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 运行阶段 FROM nginx:1.25-alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]8.2 动态配置
使用环境变量动态生成配置(需要安装 envsubst):
FROM nginx:1.25-alpine RUN apk add --no-cache gettext COPY nginx.conf.template /etc/nginx/templates/default.conf.template模板文件示例:
server { listen ${NGINX_PORT}; server_name ${NGINX_HOST}; }运行容器时传入变量:
docker run -d \ -e NGINX_PORT=8080 \ -e NGINX_HOST=example.com \ nginx8.3 零停机部署
使用双容器滚动更新:
# 启动新版本容器 docker run -d --name my-nginx-v2 -p 8081:80 my-new-nginx # 切换流量 iptables -t nat -R DOCKER 1 -p tcp --dport 8080 -j DNAT --to-destination 容器IP:80 # 停止旧容器 docker stop my-nginx-v1或者使用 Traefik/Nginx 做流量切换更专业。