Docker 安装 Nginx
大约 9 分钟约 2611 字
Docker 安装 Nginx
简介
Nginx 是一个高性能的 HTTP 服务器和反向代理服务器,由 Igor Sysoev 开发,以其高并发处理能力、低内存占用和丰富的模块生态而著称。它广泛应用于 Web 服务、负载均衡、API 网关、静态资源服务和 SSL 终端等场景。
使用 Docker 部署 Nginx 可以快速搭建 Web 服务环境。由于 Nginx 的配置文件需要频繁变更,且静态文件需要定期更新,因此合理的目录挂载和配置管理是 Docker 化部署的关键。本文将详细介绍使用 Docker 部署 Nginx 的完整流程,包括配置文件管理、反向代理配置、SSL 证书配置、日志管理和性能优化。
环境准备
系统要求
| 项目 | 最低要求 | 推荐配置 |
|---|---|---|
| 操作系统 | CentOS 7+ / Ubuntu 18.04+ | CentOS 7.9 |
| 内存 | 128MB | 512MB+ |
| 磁盘 | 1GB | 10GB+ |
| Docker | 19.03+ | 最新稳定版 |
端口规划
| 端口 | 说明 |
|---|---|
| 80 | HTTP 默认端口 |
| 443 | HTTPS 默认端口 |
| 8080 | 备用 HTTP 端口 |
第一步:拉取 Nginx 镜像
# 拉取最新版本的 Nginx 镜像
docker pull nginx
# 建议指定版本
docker pull nginx:1.24
docker pull nginx:1.25-alpine
# 查看已拉取的镜像
docker images | grep nginx镜像选择
nginx:标准版,基于 Debiannginx:alpine:基于 Alpine Linux,镜像更小(约 25MB vs 142MB)- 生产环境建议固定版本标签
第二步:启动临时容器并导出配置
由于 Nginx 需要经常变更配置并发布静态文件,最佳实践是先启动一个临时容器,将默认配置文件拷贝到宿主机,然后删除临时容器,再通过目录挂载启动正式容器。
启动临时容器
# 启动一个临时的 Nginx 容器
docker run --name nginx_temp -p 80:80 -d nginx
# 查看容器状态
docker ps | grep nginx_temp创建宿主机管理目录
# 创建 Nginx 管理目录结构
mkdir -p /etc/nginx
mkdir -p /etc/nginx/www
mkdir -p /etc/nginx/conf
mkdir -p /etc/nginx/logs
mkdir -p /var/www/html导出默认配置文件
# 从容器中拷贝主配置文件到宿主机
docker cp nginx_temp:/etc/nginx/nginx.conf /etc/nginx/
# 从容器中拷贝子配置目录到宿主机
docker cp nginx_temp:/etc/nginx/conf.d /etc/nginx/conf/
# 从容器中拷贝默认静态页面到宿主机
docker cp nginx_temp:/usr/share/nginx/html/ /etc/nginx/www/
# 从容器中拷贝日志文件到宿主机(可选)
docker cp nginx_temp:/var/log/nginx/ /etc/nginx/logs/
# 停止并删除临时容器
docker stop nginx_temp
docker rm nginx_temp目录结构
完成拷贝后,宿主机的目录结构如下:
/etc/nginx/
├── nginx.conf # Nginx 主配置文件
├── conf/
│ └── conf.d/ # 子配置目录(server 块)
│ └── default.conf # 默认虚拟主机配置
├── www/ # 静态文件目录
│ ├── index.html
│ └── 50x.html
└── logs/ # 日志目录
├── access.log
└── error.log
/var/www/html/ # 额外的静态文件目录第三步:启动正式容器
基础启动命令
docker run --name nginx \
-p 80:80 \
--restart=always \
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /etc/nginx/www/:/usr/share/nginx/html/ \
-v /var/www/html/:/var/www/html/ \
-v /etc/nginx/logs/:/var/log/nginx/ \
-v /etc/nginx/conf/:/etc/nginx/conf.d \
--privileged=true \
-d nginx参数详解
| 参数 | 说明 |
|---|---|
--name nginx | 容器名称 |
-p 80:80 | 端口映射,宿主机 80 -> 容器 80 |
--restart=always | Docker 服务重启后自动启动 |
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf | 主配置文件挂载 |
-v /etc/nginx/www/:/usr/share/nginx/html/ | 静态文件目录挂载 |
-v /var/www/html/:/var/www/html/ | 额外静态文件目录挂载 |
-v /etc/nginx/logs/:/var/log/nginx/ | 日志目录挂载 |
-v /etc/nginx/conf/:/etc/nginx/conf.d | 子配置目录挂载 |
--privileged=true | 以 root 权限运行(解决权限问题) |
-d | 后台运行 |
容器管理
开机自启动配置
# --restart 参数说明:
# no - 容器退出时,不重启容器
# on-failure - 只有在非 0 状态退出时才重新启动容器
# always - 无论退出状态如何,都重启容器
# unless-stopped - 类似 always,但手动停止后不会自动重启如果启动时未指定 restart 参数
# 通过 update 命令修改重启策略
docker update --restart=always nginx
# 查看所有容器
docker ps -a
# 通过容器名称操作容器
docker start nginx # 启动
docker stop nginx # 停止
docker restart nginx # 重启
docker rm nginx # 删除(需先停止)配置热重载
# 修改配置文件后,不需要重启容器,只需 reload 配置
docker exec nginx nginx -s reload
# 重新打开日志文件(日志轮转后使用)
docker exec nginx nginx -s reopen
# 测试配置文件语法
docker exec nginx nginx -t第四步:防火墙配置
# 查看防火墙当前的放行端口列表
firewall-cmd --list-ports
# 添加 HTTP 端口
firewall-cmd --add-port=80/tcp --permanent
# 如果使用 HTTPS
firewall-cmd --add-port=443/tcp --permanent
# 重新加载防火墙
firewall-cmd --reload
# 验证端口
firewall-cmd --list-ports配置示例
Nginx 主配置文件
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto; # 工作进程数,auto 自动匹配 CPU 核数
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024; # 每个 worker 的最大连接数
multi_accept on; # 允许一次接受多个连接
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip 压缩
gzip on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_vary on;
# 包含子配置
include /etc/nginx/conf.d/*.conf;
}反向代理配置
# /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name example.com www.example.com;
# 访问日志
access_log /var/log/nginx/app_access.log;
error_log /var/log/nginx/app_error.log;
# 静态文件
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 反向代理后端 API
location /api/ {
proxy_pass http://backend: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;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
# 健康检查
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}SSL/HTTPS 配置
# /etc/nginx/conf.d/ssl.conf
server {
listen 443 ssl http2;
server_name example.com;
# SSL 证书
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# SSL 安全参数
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# HTTP 到 HTTPS 重定向
location / {
proxy_pass http://backend:8080;
}
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}负载均衡配置
# /etc/nginx/conf.d/upstream.conf
upstream backend {
# 轮询(默认)
server backend1:8080;
server backend2:8080;
server backend3:8080;
# 加权轮询
# server backend1:8080 weight=3;
# server backend2:8080 weight=1;
# IP Hash(会话保持)
# ip_hash;
# 健康检查(需要商业版或第三方模块)
# health_check;
# 备用服务器
# server backup:8080 backup;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
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 Compose 部署
# docker-compose-nginx.yml
version: '3.8'
services:
nginx:
image: nginx:1.24
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/www:/usr/share/nginx/html
- ./nginx/logs:/var/log/nginx
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- backend
networks:
- app-net
backend:
image: myapp:latest
container_name: backend
restart: always
expose:
- "8080"
networks:
- app-net
networks:
app-net:
driver: bridge日志管理
日志轮转配置
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 每天轮转
missingok # 文件不存在不报错
rotate 30 # 保留 30 天
compress # 压缩旧日志
delaycompress # 延迟压缩(保留最近一天的未压缩)
notifempty # 空文件不轮转
create 0640 nginx adm # 创建新文件
sharedscripts # 所有日志处理完后执行一次脚本
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}自定义日志格式
# JSON 格式日志(便于 ELK 收集)
log_format json_log escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access.log json_log;常见问题排查
配置文件语法错误
# 测试配置文件语法
docker exec nginx nginx -t
# 常见错误:
# nginx: [emerg] unknown directive "xxx" → 检查拼写和上下文
# nginx: [emerg] host not found in upstream → 检查 upstream 名称502 Bad Gateway
# 1. 检查后端服务是否运行
docker ps | grep backend
# 2. 检查 upstream 配置中的地址是否正确
# 3. 检查防火墙是否阻止了 Nginx 到后端的通信
# 4. 查看 Nginx 错误日志
docker exec nginx tail -50 /var/log/nginx/error.log静态文件 403 Forbidden
# 1. 检查文件是否存在
docker exec nginx ls -la /usr/share/nginx/html/
# 2. 检查目录权限
ls -la /etc/nginx/www/
# 3. 检查 Nginx 运行用户是否有读取权限
# Docker Nginx 默认以 nginx 用户运行(UID 101)Nginx 性能优化
连接与并发优化
# /etc/nginx/nginx.conf 性能优化部分
user nginx;
worker_processes auto; # 自动匹配 CPU 核数
worker_cpu_affinity auto; # 自动绑定 CPU
worker_rlimit_nofile 65535; # Worker 最大打开文件数
events {
worker_connections 4096; # 每个 Worker 最大连接数
multi_accept on; # 一次接受所有新连接
use epoll; # Linux 使用 epoll(默认)
accept_mutex off; # 高并发时关闭 accept mutex
}
http {
# 连接优化
keepalive_timeout 65;
keepalive_requests 10000; # 单个连接最大请求数
reset_timedout_connection on; # 超时后立即关闭连接
# 上传大小限制
client_max_body_size 50m;
client_body_buffer_size 256k;
# 输出缓冲
output_buffers 4 32k;
postpone_output 1460;
# 文件缓存
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 90s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
}Gzip 压缩优化
# Gzip 深度优化
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 4; # 压缩级别 1-9(4 是性能与压缩率的平衡)
gzip_min_length 1024; # 小于 1KB 不压缩
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/rss+xml
application/atom+xml
image/svg+xml
font/opentype
font/ttf
font/woff
font/woff2
application/vnd.ms-fontobject;静态资源缓存策略
# 静态资源缓存
server {
listen 80;
server_name static.example.com;
root /usr/share/nginx/html;
# 图片 — 长期缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
add_header X-Content-Type-Options "nosniff";
}
# CSS/JS — 带哈希的长期缓存
location ~* \.(css|js)$ {
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
}
# 字体
location ~* \.(woff|woff2|ttf|otf|eot)$ {
expires 180d;
add_header Cache-Control "public, immutable";
access_log off;
add_header Access-Control-Allow-Origin "*";
}
# HTML — 不缓存(始终获取最新)
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires 0;
}
}安全头部配置
# 安全相关 HTTP 头
server {
# HSTS — 强制 HTTPS(生产环境启用)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 防止 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# XSS 保护
add_header X-XSS-Protection "1; mode=block" always;
# 点击劫持保护
add_header X-Frame-Options "SAMEORIGIN" always;
# 内容安全策略
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
# 推荐人策略
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 权限策略
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# 隐藏 Nginx 版本号
server_tokens off;
}反向代理高级配置
# 反向代理高级设置
upstream backend {
# 轮询 + 权重
server backend1:8080 weight=3 max_fails=3 fail_timeout=30s;
server backend2:8080 weight=2 max_fails=3 fail_timeout=30s;
server backend3:8080 backup; # 备用服务器
# 长连接到上游
keepalive 32;
keepalive_requests 100;
keepalive_timeout 60s;
}
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://backend/;
proxy_http_version 1.1;
# 长连接
proxy_set_header Connection "";
# 透传真实信息
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;
# 超时配置
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
# 缓冲配置
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
proxy_busy_buffers_size 16k;
# 大文件上传
client_max_body_size 100m;
client_body_buffer_size 256k;
# 重试策略
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_timeout 10s;
proxy_next_upstream_tries 3;
}
}Docker 健康检查
# 在 Docker Compose 中添加健康检查
services:
nginx:
image: nginx:1.24
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/www:/usr/share/nginx/html
- ./nginx/logs:/var/log/nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- app-net
backend:
image: myapp:latest
container_name: backend
restart: always
expose:
- "8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
networks:
- app-net
networks:
app-net:
driver: bridge