Nginx 应用部署实战
本文提供一份完整的 Nginx 应用部署手册,涵盖从安装 Nginx 到配置反向代理、SSL 证书、SELinux 策略和日志轮转的全过程。适用于将 Nginx 作为前端代理转发到 Node.js、Python (Gunicorn/uWSGI) 或 PHP-FPM 后端的典型场景。
- 服务器已完成 新机上线基线清单
- 已有域名并解析到服务器 IP
- 后端应用已部署并可在本地端口(如
127.0.0.1:3000)上运行
第 1 步:安装 Nginx
Section titled “第 1 步:安装 Nginx”使用 AppStream 仓库
Section titled “使用 AppStream 仓库”dnf install -y nginx使用 Nginx 官方仓库(推荐获取最新稳定版)
Section titled “使用 Nginx 官方仓库(推荐获取最新稳定版)”cat > /etc/yum.repos.d/nginx.repo << 'EOF'[nginx-stable]name=nginx stable repobaseurl=https://nginx.org/packages/centos/$releasever/$basearch/gpgcheck=1enabled=1gpgkey=https://nginx.org/keys/nginx_signing.keymodule_hotfixes=trueEOF
dnf install -y nginx启动并启用服务:
systemctl enable --now nginx
# 验证systemctl status nginxcurl -I http://localhost第 2 步:配置 Server Block
Section titled “第 2 步:配置 Server Block”移除默认配置并创建应用专属配置:
# 备份默认配置mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak创建应用配置文件 /etc/nginx/conf.d/myapp.conf:
# 上游后端定义upstream myapp_backend { server 127.0.0.1:3000; keepalive 32;}
server { listen 80; server_name example.com www.example.com;
# 访问日志与错误日志 access_log /var/log/nginx/myapp_access.log; error_log /var/log/nginx/myapp_error.log;
# 安全头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 静态文件(如果有) location /static/ { alias /var/www/myapp/static/; expires 30d; add_header Cache-Control "public, immutable"; }
# 反向代理到后端 location / { proxy_pass http://myapp_backend; proxy_http_version 1.1; 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_set_header Connection "";
# 超时设置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; }
# 健康检查端点 location /health { proxy_pass http://myapp_backend/health; access_log off; }}针对不同后端的配置
Section titled “针对不同后端的配置”PHP-FPM 后端:
upstream php_backend { server unix:/run/php-fpm/www.sock;}
location ~ \.php$ { fastcgi_pass php_backend; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params;}Python (Gunicorn) 后端:
upstream gunicorn_backend { server unix:/run/gunicorn/myapp.sock;}
location / { proxy_pass http://gunicorn_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; proxy_set_header X-Forwarded-Proto $scheme;}测试并重载配置:
nginx -tsystemctl reload nginx第 3 步:SSL 证书(Let’s Encrypt + Certbot)
Section titled “第 3 步:SSL 证书(Let’s Encrypt + Certbot)”3.1 安装 Certbot
Section titled “3.1 安装 Certbot”dnf install -y epel-releasednf install -y certbot python3-certbot-nginx3.2 申请证书
Section titled “3.2 申请证书”certbot --nginx -d example.com -d www.example.comCertbot 会自动修改 Nginx 配置以启用 SSL。验证生成的配置:
nginx -t3.3 自动续期
Section titled “3.3 自动续期”Certbot 安装后会自动创建 systemd timer:
# 查看续期定时器systemctl list-timers | grep certbot
# 手动测试续期(dry-run)certbot renew --dry-run3.4 SSL 优化配置
Section titled “3.4 SSL 优化配置”在 /etc/nginx/conf.d/ssl-params.conf 中添加全局 SSL 优化:
# SSL 协议和密码套件ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';ssl_prefer_server_ciphers on;
# SSL 会话缓存ssl_session_cache shared:SSL:10m;ssl_session_timeout 1d;ssl_session_tickets off;
# OCSP Staplingssl_stapling on;ssl_stapling_verify on;resolver 8.8.8.8 1.1.1.1 valid=300s;resolver_timeout 5s;
# HSTSadd_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;第 4 步:SELinux 策略配置
Section titled “第 4 步:SELinux 策略配置”SELinux 默认不允许 Nginx 向网络后端发起连接。必须配置相应布尔值:
# 允许 Nginx 连接到网络后端setsebool -P httpd_can_network_connect 1
# 如果 Nginx 需要访问用户主目录下的内容setsebool -P httpd_enable_homedirs 1
# 如果 Nginx 需要连接数据库setsebool -P httpd_can_network_connect_db 1如果静态文件位于非标准路径,需要设置 SELinux 上下文:
# 设置 Web 内容标签semanage fcontext -a -t httpd_sys_content_t "/var/www/myapp(/.*)?"restorecon -Rv /var/www/myapp如果 Nginx 监听非标准端口:
# 添加端口标签(例如端口 8080)semanage port -a -t http_port_t -p tcp 8080排查 SELinux 拒绝日志:
# 查看拒绝记录ausearch -m AVC -ts recent
# 自动生成策略建议audit2why < /var/log/audit/audit.log第 5 步:防火墙配置
Section titled “第 5 步:防火墙配置”# 开放 HTTP 和 HTTPSfirewall-cmd --permanent --add-service=httpfirewall-cmd --permanent --add-service=https
# 重新加载firewall-cmd --reload
# 验证firewall-cmd --list-services第 6 步:后端应用的 Systemd 服务
Section titled “第 6 步:后端应用的 Systemd 服务”为后端应用创建 systemd 服务单元,以 Node.js 应用为例:
cat > /etc/systemd/system/myapp.service << 'EOF'[Unit]Description=My ApplicationDocumentation=https://example.com/docsAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=appuserGroup=appuserWorkingDirectory=/opt/myappExecStart=/usr/bin/node /opt/myapp/server.jsRestart=on-failureRestartSec=5StartLimitInterval=60StartLimitBurst=3
# 环境变量Environment=NODE_ENV=productionEnvironment=PORT=3000EnvironmentFile=-/opt/myapp/.env
# 安全加固NoNewPrivileges=trueProtectSystem=strictProtectHome=trueReadWritePaths=/opt/myapp/data /var/log/myappPrivateTmp=true
# 资源限制LimitNOFILE=65536MemoryMax=512M
# 日志StandardOutput=journalStandardError=journalSyslogIdentifier=myapp
[Install]WantedBy=multi-user.targetEOFGunicorn 示例:
cat > /etc/systemd/system/myapp.service << 'EOF'[Unit]Description=Gunicorn Application ServerAfter=network-online.target
[Service]Type=notifyUser=appuserGroup=appuserWorkingDirectory=/opt/myappExecStart=/opt/myapp/venv/bin/gunicorn \ --workers 4 \ --bind unix:/run/gunicorn/myapp.sock \ --access-logfile /var/log/myapp/access.log \ --error-logfile /var/log/myapp/error.log \ wsgi:appExecReload=/bin/kill -s HUP $MAINPIDRestart=on-failureRuntimeDirectory=gunicorn
[Install]WantedBy=multi-user.targetEOF启动服务:
# 创建应用用户useradd -r -s /sbin/nologin appuser
# 设置目录权限chown -R appuser:appuser /opt/myapp
systemctl daemon-reloadsystemctl enable --now myapp
# 验证systemctl status myappcurl http://127.0.0.1:3000/health第 7 步:健康检查
Section titled “第 7 步:健康检查”7.1 应用端健康检查
Section titled “7.1 应用端健康检查”确保后端应用实现了 /health 端点,返回当前服务状态。最小示例(Node.js):
app.get('/health', (req, res) => { res.status(200).json({ status: 'ok', uptime: process.uptime(), timestamp: Date.now() });});7.2 外部监控脚本
Section titled “7.2 外部监控脚本”创建一个简单的健康检查脚本:
cat > /usr/local/bin/health-check.sh << 'SCRIPT'#!/bin/bashURL="http://127.0.0.1/health"HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$URL" --max-time 5)
if [ "$HTTP_CODE" != "200" ]; then echo "[$(date)] Health check failed: HTTP $HTTP_CODE" >> /var/log/health-check.log systemctl restart myapp systemctl reload nginxfiSCRIPT
chmod +x /usr/local/bin/health-check.sh添加定时检查:
cat > /etc/cron.d/health-check << 'EOF'*/5 * * * * root /usr/local/bin/health-check.shEOF7.3 端到端验证
Section titled “7.3 端到端验证”部署完成后,进行完整的端到端测试:
# HTTP 到 HTTPS 重定向curl -I http://example.com
# HTTPS 正常响应curl -I https://example.com
# 后端健康检查curl https://example.com/health
# SSL 证书信息echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates第 8 步:日志轮转
Section titled “第 8 步:日志轮转”Nginx 的默认日志轮转通常已配置。为应用自定义日志添加规则:
cat > /etc/logrotate.d/myapp << 'EOF'/var/log/myapp/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 appuser appuser sharedscripts postrotate /bin/systemctl reload myapp > /dev/null 2>&1 || true endscript}
/var/log/nginx/myapp_*.log { daily missingok rotate 30 compress delaycompress notifempty create 0640 nginx adm sharedscripts postrotate /bin/kill -USR1 $(cat /run/nginx.pid 2>/dev/null) 2>/dev/null || true endscript}EOF测试配置:
logrotate -d /etc/logrotate.d/myapp部署检查清单
Section titled “部署检查清单”| 序号 | 检查项 | 验证命令 | 预期结果 |
|---|---|---|---|
| 1 | Nginx 运行 | systemctl status nginx | active |
| 2 | 配置语法 | nginx -t | syntax is ok |
| 3 | SSL 证书 | certbot certificates | 有效且未过期 |
| 4 | SELinux 布尔值 | getsebool httpd_can_network_connect | on |
| 5 | 防火墙 | firewall-cmd --list-services | http https |
| 6 | 后端服务 | systemctl status myapp | active |
| 7 | 健康检查 | curl http://127.0.0.1/health | 200 OK |
| 8 | HTTPS 访问 | curl -I https://example.com | 200 OK |
| 9 | 日志轮转 | logrotate -d /etc/logrotate.d/myapp | 无错误 |
| 问题 | 排查步骤 |
|---|---|
| 502 Bad Gateway | 检查后端服务是否运行;检查 SELinux httpd_can_network_connect;查看 /var/log/nginx/myapp_error.log |
| 403 Forbidden | 检查文件权限;检查 SELinux 上下文 ls -Z;运行 restorecon |
| SSL 证书过期 | 运行 certbot renew;检查 systemd timer systemctl status certbot-renew.timer |
| 连接被拒 | 检查 firewall-cmd --list-all;确认 Nginx 监听正确端口 ss -tlnp |