从零到一:基于 Docker 容器化部署 Nginx 个人网站 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 组否则每次都要 sudosudo 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 h1Hello 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虽然直接运行官方镜像很方便但真实项目通常需要自定义配置。创建一个 DockerfileFROM 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:80804.3 常用 Nginx 配置调优分享几个我在生产环境验证过的配置项性能优化nginx.confworker_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.confserver { 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.yamlversion: 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 escapejson { 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; modeblock; add_header Referrer-Policy strict-origin;使用非root用户运行DockerfileRUN 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 --frombuilder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf EXPOSE 80 CMD [nginx, -g, daemon off;]8.2 动态配置使用环境变量动态生成配置需要安装 envsubstFROM 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_PORT8080 \ -e NGINX_HOSTexample.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 做流量切换更专业。