14 KiB
NginxLinux 服务器迁移手册
服务器: 10.1.1.160 (linuxNginx) 系统: Rocky Linux 8.10 更新日期: 2026-06-12 用途: Nginx 域名转发管理平台 — Python API + Nginx 反向代理
一、迁移概览
需要迁移以下内容到新服务器:
| 类别 | 内容 | 位置 |
|---|---|---|
| 目录 | Python API 项目 | /opt/nginx-api/ |
| 目录 | SSL 证书 + 内部 CA | /etc/nginx/ssl/ |
| 目录 | Nginx 转发配置 | /etc/nginx/conf.d/ |
| 目录 | acme.sh | /root/.acme.sh/ |
| 文件 | Nginx 主配置 | /etc/nginx/nginx.conf |
| 文件 | systemd 服务 | /etc/systemd/system/nginx-api.service |
| 文件 | 环境变量 | /root/.bashrc (Ali_Key / Ali_Secret) |
| 文件 | Gunicorn 配置 | /opt/nginx-api/gunicorn_config.py |
| 定时任务 | acme.sh 自动续期 cron | crontab -l |
二、新服务器环境要求
2.1 操作系统
- Rocky Linux 8.x / CentOS 8.x / RHEL 8.x(或其他 systemd Linux 发行版)
- 最小磁盘: 20G(当前使用 3.6G)
- 内存: 2G+(当前 1.7G,用 ~350M)
2.2 安装软件清单
# 1. Nginx (dnf 安装)
dnf install -y nginx
# 2. Python 3 (系统自带 ≥ 3.6)
# Rocky 8 自带 Python 3.6.8,无需额外安装
# 3. pip 包
pip3 install flask==2.0.3 gunicorn==21.2.0
# 注: Python 3.6 不支持 Flask 3.x,实际安装 2.0.x
# 4. acme.sh (从 GitHub clone,get.acme.sh 可能被墙)
git clone https://github.com/acmesh-official/acme.sh.git /root/.acme.sh
cd /root/.acme.sh && ./acme.sh --install --home /root/.acme.sh
# 5. 设置默认 CA
/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt
2.3 需要开放的网络
| 方向 | 端口/地址 | 用途 |
|---|---|---|
| 入站 | 22/tcp | SSH 管理 |
| 入站 | 80/tcp | HTTP 转发 |
| 入站 | 443/tcp | HTTPS 转发 |
| 入站 | 5000/tcp | Python API(仅 Java 后端访问) |
| 出站 | alidns.aliyuncs.com:443 | 阿里云 DNS API(acme.sh + DNS 管理) |
| 出站 | letsencrypt.org:443 | Let's Encrypt 证书签发/续期 |
三、迁移步骤
Step 0: 准备工作
在新服务器上执行:
# 创建目录结构
mkdir -p /etc/nginx/ssl/ca
mkdir -p /etc/nginx/conf.d
mkdir -p /opt/nginx-api/services
# 创建 nginx-api 用户(可选,当前用 root)
useradd -r -s /sbin/nologin nginx-api || true
Step 1: 安装软件
# Nginx
dnf install -y nginx
systemctl enable nginx
systemctl start nginx
# Python pip 包
pip3 install flask==2.0.3 gunicorn==21.2.0
# acme.sh
git clone https://github.com/acmesh-official/acme.sh.git /root/.acme.sh
/root/.acme.sh/acme.sh --install --home /root/.acme.sh
/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt
Step 2: 配置防火墙
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=5000/tcp
firewall-cmd --reload
# SELinux
setsebool -P httpd_can_network_connect 1
Step 3: 迁移 Python API 代码
# 从旧服务器拷贝整个目录
rsync -avz root@10.1.1.160:/opt/nginx-api/ /opt/nginx-api/
# 或手动拷贝文件:
# /opt/nginx-api/
# app.py — Flask 主入口
# gunicorn_config.py — Gunicorn 配置
# deploy.sh — 部署脚本
# requirements.txt — Python 依赖
# services/
# __init__.py
# nginx_service.py — Nginx 配置读写
# ssl_service.py — 证书签发管理
# dns_service.py — DNS 解析管理
# cert_notifier.py — 邮件通知脚本
Step 4: 迁移 SSL 证书目录
# ⚠️ 关键:内部 CA 私钥必须保留
rsync -avz root@10.1.1.160:/etc/nginx/ssl/ /etc/nginx/ssl/
# 确保证书权限正确
chmod 700 /etc/nginx/ssl/ca
chmod 600 /etc/nginx/ssl/ca/ca.key
chmod 644 /etc/nginx/ssl/ca/ca.crt
chmod 600 /etc/nginx/ssl/*.key
chmod 644 /etc/nginx/ssl/*.pem
Step 5: 迁移 Nginx 配置
# 主配置
rsync -avz root@10.1.1.160:/etc/nginx/nginx.conf /etc/nginx/nginx.conf
# 所有域名转发配置
rsync -avz root@10.1.1.160:/etc/nginx/conf.d/ /etc/nginx/conf.d/
# 验证配置
nginx -t
# 重载
systemctl reload nginx
Step 6: 迁移 acme.sh
rsync -avz root@10.1.1.160:/root/.acme.sh/ /root/.acme.sh/
Step 7: 迁移环境变量
将以下内容追加到 /root/.bashrc:
export Ali_Key="LTAI5t98jUxLUadZM1LkaSa8"
export Ali_Secret="K2HyLLXizAgvOMOa0Mzc7ebs4gbAZs"
然后执行:
source /root/.bashrc
Step 8: 安装 systemd 服务
创建 /etc/systemd/system/nginx-api.service:
[Unit]
Description=Nginx Manager Python API
After=network.target nginx.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/nginx-api
Environment=Ali_Key=LTAI5t98jUxLUadZM1LkaSa8
Environment=Ali_Secret=K2HyLLXizAgvOMOa0Mzc7ebs4gbAZs
ExecStart=/usr/bin/python3 -m gunicorn -c gunicorn_config.py app:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable nginx-api
systemctl start nginx-api
Step 9: 迁移 crontab
# 查看旧服务器 crontab
crontab -l
# 添加 acme.sh 自动续期(每天 19:01)
echo '1 19 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null' | crontab -
Step 10: 验证
# 1. 服务状态
systemctl status nginx nginx-api
# 2. 端口监听
ss -tlnp | grep -E ":(80|443|5000)"
# 3. Nginx 配置语法
nginx -t
# 4. Python API 健康检查
curl http://localhost:5000/api/nginx/health
# 5. 证书列表
curl http://localhost:5000/api/nginx/certs
# 6. acme.sh 版本
/root/.acme.sh/acme.sh --version
四、完整配置文件参考
4.1 Nginx 主配置 /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
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 4096;
# 全局 gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css text/xml application/json
application/javascript text/javascript application/xml+rss
application/x-font-ttf image/svg+xml;
# 包含所有域名转发配置
include /etc/nginx/conf.d/*.conf;
}
4.2 Gunicorn 配置 /opt/nginx-api/gunicorn_config.py
import os
bind = "0.0.0.0:5000"
pidfile = "gunicorn.pid"
workers = int(os.environ.get('GUNICORN_WORKERS', 2))
worker_class = "sync"
timeout = 300
graceful_timeout = 10
accesslog = "-"
errorlog = "-"
loglevel = "info"
proc_name = "nginx-manager-api"
daemon = False
4.3 域名转发配置模板
HTTP (/etc/nginx/conf.d/<domain>.conf):
server {
listen 80;
server_name <domain>;
# 静态资源缓存
location ~* \.(js|css|svg|woff2|png|jpg|ico)$ {
proxy_pass <target>;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass <target>;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
HTTPS (/etc/nginx/conf.d/<domain>.conf):
# HTTP → HTTPS 重定向
server {
listen 80;
server_name <domain>;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name <domain>;
ssl_certificate /etc/nginx/ssl/<domain>.pem;
ssl_certificate_key /etc/nginx/ssl/<domain>.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 静态资源缓存
location ~* \.(js|css|svg|woff2|png|jpg|ico)$ {
proxy_pass <target>;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass <target>;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
五、证书体系
5.1 双层证书架构
| 类型 | 适用域名 | 签发方式 | 有效期 | 续期方式 |
|---|---|---|---|---|
| 内部 CA | 非 zgitm 域名 | OpenSSL 自签 | 365天 | 重新签发 |
| Let's Encrypt | *.zgitm.com |
acme.sh DNS-01 | 90天 | 自动续期(每天cron) |
5.2 内部 CA
/etc/nginx/ssl/ca/
ca.key (600) — 根CA私钥 (RSA 2048)
ca.crt (644) — 根CA证书 (有效期10年, 至约2036年)
ca.srl — 序列号文件
首次启动时由 ssl_service.py 的 init_ca() 方法自动生成。迁移时必须保留此目录,否则所有内部 CA 签发的域名的 SSL 证书将不被信任。
5.3 Let's Encrypt (acme.sh)
- 安装目录:
/root/.acme.sh/ - 证书存放:
/etc/nginx/ssl/<domain>.pem+.key - 自动续期 cron:
1 19 * * *(每天) - DNS 验证: 阿里云 DNS API (
dns_ali) - 阿里云 AccessKey 环境变量:
Ali_Key/Ali_Secret - 当前已签发:
zgitm.com,www.zgitm.com(到期 2026-09-09)
5.4 系统保护域名
zgitm.com 和 www.zgitm.com 为系统内置域名,不允许在转发规则页面创建/修改/删除。三层拦截:
- 前端: 操作按钮置灰
- Java:
ForwardingRuleService.SYSTEM_DOMAINS - Python:
ssl_service.is_system_domain()
六、Python API 端点参考
基地址: http://10.1.1.160:5000
转发规则
GET /api/nginx/health → 健康检查
GET /api/nginx/rules → 规则列表
GET /api/nginx/rules/<domain> → 规则详情
POST /api/nginx/rules → 新增规则 {domain, protocol, target}
PUT /api/nginx/rules/<domain> → 修改规则 {domain, protocol, target}
DELETE /api/nginx/rules/<domain> → 删除规则
POST /api/nginx/reload → 重载 Nginx
证书管理
GET /api/nginx/certs → 证书列表(扫描所有 .pem,排除 ca.*)
GET /api/nginx/certs/<domain> → 证书详情
POST /api/nginx/certs/<domain>/issue → 签发 LE 证书
POST /api/nginx/certs/<domain>/renew → 续期 LE 证书
DNS 管理
GET /api/nginx/dns/records?zone= → DNS 记录列表
POST /api/nginx/dns/records → 添加 DNS 记录
PUT /api/nginx/dns/records/<id> → 修改 DNS 记录
DELETE /api/nginx/dns/records/<id> → 删除 DNS 记录
七、外部依赖关系
迁移后需要确保以下连通性:
| 从 → 到 | 地址 | 端口 | 用途 |
|---|---|---|---|
| 新服务器 | MySQL | 10.1.1.122 | 3306 |
| 新服务器 → | Java 后端 | 10.10.1.170 | 4002 |
| Java 后端 → | 新服务器 | 新IP | 5000 |
| 新服务器 → | 阿里云 DNS | alidns.aliyuncs.com | 443 |
| 新服务器 → | Let's Encrypt | acme-v02.api.letsencrypt.org | 443 |
⚠️ 迁移后必须更新的配置
- Java
application-qa.yml/application-prod.yml: 修改python-api.base-url为新服务器 IP - 前端
.env.production: 如果 Nginx 入口域名换了需更新VITE_API_BASE_URL - DNS 解析: 如果
nginx.server.zgitm.com指向旧服务器 IP,需更新 DNS 记录
八、服务清单
| 服务 | systemd unit | 开机自启 | 端口 |
|---|---|---|---|
| Nginx | nginx.service | ✅ | 80, 443 |
| Python API | nginx-api.service | ✅ | 5000 |
| SSH | sshd.service | ✅ | 22 |
| Firewalld | firewalld.service | ✅ | — |
常用命令
# 查看所有服务状态
systemctl status nginx nginx-api
# 重载 Nginx(不中断连接)
systemctl reload nginx
# 重启 Python API
systemctl restart nginx-api
# 查看日志
journalctl -u nginx-api -f
journalctl -u nginx -f
# 测试 Nginx 配置
nginx -t
# 手动签发证书(LE 测试)
/root/.acme.sh/acme.sh --issue --dns dns_ali -d test.zgitm.com --staging
# 手动续期
/root/.acme.sh/acme.sh --renew -d zgitm.com --force
九、当前服务器资源使用
| 指标 | 值 |
|---|---|
| 磁盘 | 17G 总量, 3.6G 已用 (22%) |
| 内存 | 1.7G 总量, ~350M 已用 |
| Python API 大小 | 152K |
| acme.sh 大小 | 3.5M |
| Nginx 配置+证书 | 376K |
| 证书文件数 | 20+ .pem |
| 转发规则数 | 17 .conf |
十、已知注意事项
-
Python 3.6 兼容性: Rocky 8 自带 Python 3.6,Flask 最高只支持到 2.0.x。
requirements.txt写的 flask==3.1.0 实际上被降级安装为 2.0.3。如果新服务器用 Python 3.9+,可以安装 Flask 3.x。 -
acme.sh 安装方式:
curl https://get.acme.sh | sh可能被墙,建议用git clone方式安装。 -
内部 CA 私钥:
/etc/nginx/ssl/ca/ca.key是安全敏感文件,迁移时不要通过不安全渠道传输。RSYNC/SCP 直接传输是安全的。 -
阿里云 AccessKey: 存储在
/root/.bashrc环境变量中,systemd service 文件中也硬编码了一份(Environment=),两处都需要更新。 -
Java 后端 MyBatis-Plus: 已从 JPA 迁移到 MyBatis-Plus(2026-06-12),新服务器上 Python API 代码已包含
list_certs()修复(扫描全部 .pem)。 -
Nginx gzip: 主配置中包含全局 gzip 压缩(
/etc/nginx/nginx.conf中http {}块)。迁移时注意include /etc/nginx/conf.d/*.conf;不要丢失。