首次部署香港服务器,系统初始化、SSH加固与可用性验证如何按顺序完成?
首次部署香港服务器,验收目标不应只是“能远程登录”。一台可以上线的主机至少应达到:管理员可通过密钥登录、系统与时区已统一、SSH 和防火墙不会把自己锁在门外、应用仅通过预期端口对外提供服务,域名解析、HTTP/HTTPS、进程自启和健康检查均能得到明确结果。

下面采用一套可执行的示例环境:Ubuntu Server 24.04 LTS 64 位、systemd、Nginx、Python 3.12 虚拟环境和 Gunicorn,域名使用 example.com 作为占位符。目标是部署一个仅监听本机 127.0.0.1:8000 的 Python Web 服务,由 Nginx 接收 80/443 端口请求;如果实际业务使用 Node.js、Java 或其他运行时,应替换对应运行时和进程启动命令,不要在一台主机上无目的地安装多套依赖。
一、准备条件与上线边界
1. 明确服务器和运行环境
香港服务器的地域只决定实例所在网络位置,首次部署仍应按照公网 IP、操作系统、访问控制和应用监听关系来规划。部署前记录以下信息:
| 项目 | 示例值 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu Server 24.04 LTS amd64 | 本文命令以此版本为准 |
| 初始化账户 | 云厂商提供的 ubuntu 或自定义账户 | 仅用于首次登录和创建管理账户 |
| 管理账户 | deploy | 使用 SSH 公钥登录,加入 sudo 组 |
| Web 入口 | Nginx | 对外监听 80/443 |
| 应用运行时 | Python 3.12、venv、Gunicorn | 应用进程只监听 127.0.0.1:8000 |
| 必要依赖 | python3-venv、python3-pip、nginx、ufw、curl | 按应用实际依赖增减 |
| 对外端口 | TCP 22、80、443 | 22 应限制来源,80/443按业务需要开放 |
| 域名 | example.com | 替换成实际域名 |
| 上线目标 | SSH 密钥登录、Nginx 代理应用、健康检查通过 | 作为最终验收边界 |
演练或轻量服务可以从 2 vCPU、4 GB 内存起步,但这只是部署示例,不代表所有应用的容量要求。数据库、编译任务、消息队列和高并发应用应单独评估资源。
面向香港网站、业务后台和接口服务的部署,A5数据提供入门建站、Xeon Gold及AMD EPYC等物理服务器产品,以不同档位的计算、内存资源搭配SSD或NVMe存储,为应用进程、数据库及多任务运行提供硬件基础。香港产品还提供CN2与国际带宽方案,覆盖面向不同访问人群的网络资源需求,可承载以Nginx为对外入口的Web应用部署。
2. 准备管理入口和回滚手段
正式修改 SSH、防火墙和 Nginx 前,至少确认以下条件:
- 云厂商控制台可以打开 VNC、串口或救援终端等带外管理入口。
- 已记录公网 IPv4;如果启用 IPv6,也记录 IPv6 地址和对应的网段。
- 已保存云主机快照或磁盘备份。快照只能覆盖快照时刻的磁盘状态,不能替代数据库和业务文件的异机备份。
- 已准备一个不会放在服务器上的 SSH 私钥。
- 已知云平台安全组、网络 ACL 与服务器内部防火墙的配置位置。
- 已确定维护窗口。更新内核、重启服务或切换 HTTPS 配置,都可能造成短暂中断。
本地操作终端可以先设置示例变量,实际使用时替换为真实值:
export SERVER_IP='203.0.113.10'
export DOMAIN='example.com'
export SSH_KEY="$HOME/.ssh/id_ed25519_hk_web"
203.0.113.10 属于文档示例地址,不能直接用于实际连接。
3. 先检查云平台安全组
安全组和 UFW 是两层独立控制。建议先在云平台放行:
- TCP 22:仅允许办公出口 IP、堡垒机 IP 或管理员固定网段。
- TCP 80:如果需要 HTTP 访问或申请证书,允许公网访问。
- TCP 443:启用 HTTPS 后允许公网访问。
- 其他端口:默认不放行,尤其是数据库、缓存、应用内部端口和管理面板端口。
如果管理员出口地址经常变化,不能简单地把 SSH 端口永久暴露给全部公网。可以先利用控制台完成初始化,再通过临时规则处理变化;规则调整前必须确认新的出口地址已经加入。
二、首次登录与系统初始化
1. 核验操作系统、架构和网络地址
使用云厂商提供的初始账户登录后,先不要修改 SSH 配置。确认系统版本、内核、地址和默认路由:
ssh -i "$SSH_KEY" ubuntu@"$SERVER_IP"
在服务器上执行:
cat /etc/os-release
uname -m
hostnamectl
ip -br address
ip route
resolvectl status
应重点确认:
PRETTY_NAME为 Ubuntu 24.04 LTS 或实际申请的版本。- 架构与业务软件包匹配,常见为
x86_64。 - 公网 IPv4 位于预期网卡上。
- 默认路由存在。
- DNS 解析器可用。
如果系统不是预期版本,不要继续套用本文命令。不同发行版的服务名、包名和 SSH 默认配置可能不同,应先确认对应文档或重装为预定镜像。
2. 建立系统备份并更新基础软件
先取得 root shell,创建配置备份目录:
sudo -i
BACKUP_DIR="/root/first-deploy-$(date +%Y%m%d-%H%M%S)"
install -d -m 700 "$BACKUP_DIR"
cp -a /etc/ssh "$BACKUP_DIR/ssh-before"
cp -a /etc/nginx "$BACKUP_DIR/nginx-before" 2>/dev/null || true
tar -C /etc -czf "$BACKUP_DIR/etc-selected.tgz" ssh systemd/system
sha256sum "$BACKUP_DIR/etc-selected.tgz" > "$BACKUP_DIR/SHA256SUMS"
如果当前还没有安装 Nginx,cp -a /etc/nginx 报错是正常的。这里没有忽略 SSH 备份;SSH 配置是后续回滚的关键。
更新软件包索引并安装基础依赖:
apt update
apt full-upgrade -y
apt install -y \
ca-certificates \
curl \
nginx \
python3 \
python3-venv \
python3-pip \
ufw \
openssh-server
full-upgrade 可能更新内核或需要重启。执行后检查是否有失败服务:
systemctl --failed
systemctl is-active ssh
systemctl is-active nginx
如果系统提示需要重启,应在 SSH 加固前完成重启验证,避免把“系统更新后无法启动”和“SSH 配置错误”混在一起排查:
reboot
重启会中断当前连接,只有在云控制台可用、初始账户仍可登录的情况下执行。重连后再次检查:
ssh -i "$SSH_KEY" ubuntu@"$SERVER_IP"
3. 设置主机名、时区和时间同步
主机名应能体现地域和用途,例如:
sudo hostnamectl set-hostname hk-web-01
sudo timedatectl set-timezone Asia/Hong_Kong
sudo timedatectl set-ntp true
timedatectl status
如果应用、日志平台和数据库统一使用 UTC,则不要为了地域名称强行设置本地时区。关键不是选哪一个时区,而是所有组件保持一致。采用香港本地时区时,日志时间应显示为 HKT 或 +08:00;采用 UTC 时,应在运维平台统一换算。
4. 创建独立管理账户
不要长期使用云厂商初始账户,更不要让应用进程使用 root 运行。创建一个专用于运维的账户:
sudo adduser --disabled-password --gecos "" deploy
sudo usermod -aG sudo deploy
sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
sudo install -m 600 -o deploy -g deploy /dev/null \
/home/deploy/.ssh/authorized_keys
在本地生成独立密钥。已有可靠密钥时可以复用,不必重复覆盖:
ssh-keygen -t ed25519 -a 100 -f "$SSH_KEY" -C "deploy@hk-web-01"
将公钥写入服务器。下面的命令需要使用当前仍可登录的初始账户执行:
cat "${SSH_KEY}.pub" | ssh -i "$HOME/.ssh/initial-key" ubuntu@"$SERVER_IP" \
'sudo tee -a /home/deploy/.ssh/authorized_keys >/dev/null && \
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys && \
sudo chmod 600 /home/deploy/.ssh/authorized_keys'
如果云平台只提供控制台而没有可用的初始 SSH 密钥,可以在控制台中粘贴公钥,但不要把私钥复制到服务器。
先用新账户开启第二个终端验证:
ssh -o IdentitiesOnly=yes -i "$SSH_KEY" deploy@"$SERVER_IP"
验证 sudo 权限:
sudo -v
sudo id
只有新账户可以密钥登录并获得预期管理权限后,才进入 SSH 加固阶段。当前初始连接不要关闭,它是失败时的回退通道。

三、SSH 加固:先验证密钥,再关闭密码
1. 备份当前 SSH 配置
在服务器上执行:
sudo -i
SSH_BACKUP="/root/ssh-backup-$(date +%Y%m%d-%H%M%S)"
install -d -m 700 "$SSH_BACKUP"
cp -a /etc/ssh/sshd_config "$SSH_BACKUP/"
cp -a /etc/ssh/sshd_config.d "$SSH_BACKUP/"
SSH 配置涉及远程管理入口,任何覆盖、权限变更或语法错误都可能导致新连接失败。必须保留当前会话和带外控制台,不能只依赖即将修改的 SSH 连接。
2. 使用独立 drop-in 文件加固
在 Ubuntu 24.04 上,推荐将自定义内容放进 /etc/ssh/sshd_config.d/,避免直接覆盖主配置:
cat > /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF'
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
MaxAuthTries 3
X11Forwarding no
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
chmod 644 /etc/ssh/sshd_config.d/99-hardening.conf
这些设置的作用分别是:
PubkeyAuthentication yes:启用公钥认证。PasswordAuthentication no:关闭 SSH 密码认证,降低密码撞库风险。KbdInteractiveAuthentication no:关闭键盘交互式认证,避免通过其他交互方式绕过密码策略。PermitRootLogin no:禁止 root 直接远程登录,改由普通账户登录后使用 sudo。MaxAuthTries 3:限制单次连接的认证尝试次数。X11Forwarding no:服务器不需要图形转发时关闭。AllowTcpForwarding no:不需要 SSH 隧道时关闭。若运维工具依赖端口转发,应先确认再启用。ClientAliveInterval和ClientAliveCountMax:清理异常断开的会话,不等同于应用层心跳。
先检查语法和最终生效值:
sshd -t
sshd -T | grep -E \
'^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin|maxauthtries|x11forwarding|allowtcpforwarding|clientaliveinterval|clientalivecountmax) '
只有 sshd -t 没有输出错误时,才重新加载服务:
systemctl reload ssh
3. 开启新连接测试
不要关闭原有终端,另开窗口执行:
ssh \
-o IdentitiesOnly=yes \
-o PasswordAuthentication=no \
-o PreferredAuthentications=publickey \
-i "$SSH_KEY" \
deploy@"$SERVER_IP"
进入后确认:
whoami
sudo id
如果新连接成功,再测试旧初始账户是否已按预期限制。不要只测试当前会话,因为当前会话可能在配置变化前已经完成认证。
SSH 加固完成后的基本状态应是:
| 验证项 | 预期结果 |
|---|---|
deploy 密钥登录 | 成功 |
deploy 执行 sudo | 成功并按系统策略要求输入密码 |
| root 直接登录 | 被拒绝 |
| SSH 密码登录 | 被拒绝 |
| SSH 服务状态 | active (running) |
| 初始连接 | 在确认新连接前保持不关闭 |
四、防火墙配置:先放行管理地址,再启用策略
1. 确认当前管理出口地址
在当前 SSH 会话中查看连接来源:
echo "$SSH_CONNECTION"
输出通常类似:
198.51.100.25 53000 203.0.113.10 22
第一列是客户端地址。可以提取为变量并人工确认:
ADMIN_IP="$(printf '%s\n' "$SSH_CONNECTION" | awk '{print $1}')"
echo "$ADMIN_IP"
如果通过堡垒机连接,第一列是堡垒机地址,而不是个人电脑地址;应允许堡垒机网段,不能误把个人公网地址写入规则。
2. 配置 UFW
先放行 SSH,再设置默认策略:
sudo ufw allow from "$ADMIN_IP" to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw status numbered
确认规则中已经存在正确的 SSH 来源,再启用防火墙:
sudo ufw --force enable
sudo ufw status verbose
ufw --force enable 会立即改变入站访问策略。如果 SSH 规则写错,当前连接可能继续存在,但新连接会被拒绝。因此必须在启用前检查 ufw status numbered,并保留云控制台。
如果管理员没有固定 IP,可以使用更宽的临时 SSH 规则或 ufw limit 22/tcp,但这只是兼容动态地址的折中方案。上线后应尽量改为固定出口、VPN 之外的合规堡垒机或明确的管理网段策略;不要把临时的全公网 SSH 规则当作最终状态。
IPv6 需要单独验证。确认服务器确实启用了 IPv6、云平台安全组允许对应流量、UFW 的 IPv6 策略正确后,再配置 AAAA 记录。只配置 AAAA 但没有完整 IPv6 路由和防火墙规则,会造成部分访问失败。
3. 检查实际监听端口
sudo ss -lntup
sudo ufw status verbose
初始阶段常见的预期是:
0.0.0.0:22 sshd
0.0.0.0:80 nginx
127.0.0.1:8000 gunicorn
应用端口如果显示为 0.0.0.0:8000,意味着它直接暴露在公网或云平台网络上。除非有明确原因,否则应改为 127.0.0.1:8000,由 Nginx 统一处理外部请求。

五、部署运行时和应用进程
1. 创建独立应用账户
应用不应使用 deploy 或 root 账户运行。创建系统账户:
sudo adduser \
--system \
--group \
--home /opt/example-app \
--shell /usr/sbin/nologin \
app
sudo install -d -m 0750 -o app -g app /opt/example-app
下面的 Python WSGI 文件只是用于验证进程、Nginx 和健康检查链路的最小示例。实际上线时,应替换为经过测试的业务代码和锁定版本的 requirements.txt。
创建示例依赖:
sudo tee /opt/example-app/requirements.txt >/dev/null <<'EOF'
gunicorn
EOF
创建健康检查应用:
sudo tee /opt/example-app/wsgi.py >/dev/null <<'EOF'
def app(environ, start_response):
path = environ.get("PATH_INFO", "/")
if path == "/healthz":
status = "200 OK"
body = b'{"status":"ok"}\n'
content_type = "application/json"
else:
status = "200 OK"
body = b"example-app is running\n"
content_type = "text/plain; charset=utf-8"
headers = [
("Content-Type", content_type),
("Content-Length", str(len(body))),
]
start_response(status, headers)
return [body]
EOF
sudo chown -R app:app /opt/example-app
sudo chmod 0750 /opt/example-app
sudo chmod 0640 /opt/example-app/requirements.txt /opt/example-app/wsgi.py
2. 创建 Python 虚拟环境并安装依赖
使用应用账户创建虚拟环境,避免把依赖安装到系统 Python:
sudo -u app python3 -m venv /opt/example-app/.venv
sudo -u app /opt/example-app/.venv/bin/python \
-m pip install --disable-pip-version-check \
-r /opt/example-app/requirements.txt
真实业务上线时,建议使用项目锁定文件或内部制品仓库,并在测试环境先完成安装。不要在生产环境临时升级一个关键框架后直接重启应用。
3. 使用 systemd 管理应用
创建服务单元:
sudo tee /etc/systemd/system/example-app.service >/dev/null <<'EOF'
[Unit]
Description=Example Python Web Application
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/example-app
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/example-app/.venv/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 wsgi:app
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
sudo chmod 0644 /etc/systemd/system/example-app.service
sudo systemctl daemon-reload
sudo systemctl enable --now example-app.service
检查服务状态和本机健康检查:
sudo systemctl status example-app.service --no-pager
sudo systemctl is-enabled example-app.service
curl -fsS http://127.0.0.1:8000/healthz
预期结果类似:
{"status":"ok"}
如果服务启动失败,先查看日志,不要反复执行重启命令:
sudo journalctl -u example-app.service -n 100 --no-pager
sudo systemctl cat example-app.service
sudo ss -lntp | grep ':8000'
六、配置 Nginx 作为对外入口
1. 创建站点目录和反向代理配置
创建站点配置时,把 example.com 和 www.example.com 替换为实际域名:
sudo tee /etc/nginx/sites-available/example.com >/dev/null <<'EOF'
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location = /healthz {
proxy_pass http://127.0.0.1:8000/healthz;
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;
}
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}
EOF
sudo chmod 0644 /etc/nginx/sites-available/example.com
sudo ln -s /etc/nginx/sites-available/example.com \
/etc/nginx/sites-enabled/example.com
如果目标路径已经存在,不要直接覆盖。先检查:
ls -l /etc/nginx/sites-available/
ls -l /etc/nginx/sites-enabled/
2. 验证配置后重新加载
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl enable nginx
sudo systemctl is-active nginx
nginx -t 失败时不要 reload。常见原因包括域名配置拼写错误、花括号不匹配、重复监听 default_server 或上游地址写错。
在 DNS 尚未生效时,可以通过 Host 头验证 Nginx 是否选择了目标站点:
curl -i -H 'Host: example.com' http://127.0.0.1/healthz
也可以从本地终端绕过 DNS,将域名解析到服务器地址:
curl --resolve "$DOMAIN:80:$SERVER_IP" \
-fsS "http://$DOMAIN/healthz"
这个方式同时验证了目标 IP、Host 头和 Nginx 路由,比直接访问 IP 更接近真实上线请求。
七、配置 DNS 和 HTTPS
1. 添加 DNS 记录
在域名管理平台创建:
| 记录类型 | 主机记录 | 指向 | 使用条件 |
|---|---|---|---|
| A | @ 或空主机名 | 香港服务器 IPv4 | 服务器有可达 IPv4 |
| A | www | 香港服务器 IPv4 | 需要 www 域名时 |
| AAAA | @ | 服务器 IPv6 | IPv6 路由和防火墙已验证 |
| AAAA | www | 服务器 IPv6 | 同上 |
使用 dig 检查解析:
dig +short A example.com
dig +short A www.example.com
dig +short AAAA example.com
A 记录已经返回正确地址后,再进行 HTTPS 配置。若 DNS 使用代理或 CDN,证书申请、源站访问和真实客户端 IP 处理方式会不同,应先确认回源端口和证书模式。
2. 申请 HTTPS 证书
HTTPS 不是修改 SSH 或 UFW 的替代品,证书申请前仍需确保 80 端口可以从公网访问。先备份 Nginx 配置:
sudo tar -C /etc -czf \
"/root/nginx-before-certbot-$(date +%Y%m%d-%H%M%S).tgz" \
nginx
安装证书工具:
sudo apt install -y certbot python3-certbot-nginx
申请证书并让工具配置 Nginx:
sudo certbot --nginx \
-d example.com \
-d www.example.com \
--redirect
如果没有使用 www,不要把它加入命令。--redirect 会将 HTTP 请求重定向到 HTTPS,并可能修改站点配置。执行后检查:
sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com/healthz
sudo certbot renew --dry-run
预期应看到 HTTPS 返回 200,且健康检查内容正确。证书申请失败时,先确认:
- DNS 的 A/AAAA 记录是否已经指向当前服务器。
- 云平台安全组和 UFW 是否允许 TCP 80。
- Nginx 是否正在监听 80。
- 域名是否被其他代理、旧服务器或错误的 AAAA 记录接管。
- 证书命令中的域名是否与 Nginx
server_name一致。
八、按层次完成可用性验证
验证顺序应由内到外,但故障排查时通常由外到内。部署结束后,至少执行以下检查:
| 层级 | 检查命令或动作 | 通过标准 |
|---|---|---|
| 系统 | systemctl --failed | 没有与业务相关的失败服务 |
| 时间 | timedatectl status | NTP 已同步,时区符合约定 |
| 磁盘 | df -h、df -ih | 根分区和 inode 有足够余量 |
| 内存 | free -h | 没有持续的内存耗尽或异常 swap |
| SSH | 新终端密钥登录 | deploy 可登录并执行 sudo |
| SSH 加固 | 使用密码测试 | 密码登录被拒绝,密钥登录正常 |
| 防火墙 | ufw status verbose | 22 来源受限,80/443按目标开放 |
| 进程 | systemctl is-active example-app | 返回 active |
| 应用 | curl 127.0.0.1:8000/healthz | 返回 HTTP 200 和预期内容 |
| Nginx | nginx -t | 配置语法通过 |
| 站点 | curl --resolve ... | 域名 Host 路由正常 |
| DNS | dig +short | 返回当前服务器地址 |
| HTTPS | curl -I https://... | TLS 握手成功,健康检查返回 200 |
| 自启 | systemctl is-enabled nginx example-app | 均为 enabled |
还应检查端口是否符合设计:
sudo ss -lntup
理想情况下,Gunicorn 不应监听公网地址:
LISTEN ... 127.0.0.1:8000 ... users:(("gunicorn",...))
如果看到 0.0.0.0:8000 或 [::]:8000,应立即检查 systemd 的 ExecStart 参数和应用自身配置,并在确认不需要公网访问后改为本机监听。
九、常见失败处理
1. SSH 加固后无法登录
先不要重装系统。通过云控制台进入服务器,检查:
sudo sshd -t
sudo systemctl status ssh --no-pager
sudo journalctl -u ssh -n 100 --no-pager
sudo ss -lntp | grep ':22'
如果是密钥权限问题:
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
如果是配置错误,暂时移走自定义文件但不要删除,以便保留证据:
sudo mv \
/etc/ssh/sshd_config.d/99-hardening.conf \
/etc/ssh/sshd_config.d/99-hardening.conf.disabled
sudo sshd -t
sudo systemctl reload ssh
如果恢复后可以登录,说明问题集中在自定义 drop-in。应逐项恢复配置,每次修改后先执行 sshd -t,并使用第二个终端测试。
2. 启用 UFW 后新连接被拒绝
通过云控制台检查规则:
sudo ufw status numbered
如果确认当前出口地址,补充正确的放行规则:
sudo ufw allow from 198.51.100.25 to any port 22 proto tcp
sudo ufw reload
将示例地址替换成实际管理出口。只有在控制台可用且必须紧急恢复访问时,才临时执行:
sudo ufw disable
恢复访问后应立即补充正确规则并重新启用,不能把关闭 UFW 作为最终修复。
3. 应用服务启动失败
先区分“服务没有启动”和“启动后立即退出”:
sudo systemctl status example-app.service --no-pager
sudo journalctl -u example-app.service -n 100 --no-pager
sudo systemctl cat example-app.service
再检查虚拟环境、依赖和端口:
sudo -u app /opt/example-app/.venv/bin/python --version
sudo -u app /opt/example-app/.venv/bin/python -c 'import gunicorn; print(gunicorn.__version__)'
sudo ss -lntp | grep ':8000'
常见原因包括:
WorkingDirectory路径不存在。wsgi:app中的模块名或对象名不匹配。- 依赖安装到了错误的 Python 环境。
- 8000 端口被其他进程占用。
- 应用账户无权读取代码或配置文件。
- 环境变量没有写入 systemd 单元。
不要直接给整个 /opt/example-app 执行 chmod -R 777。应根据日志只修正目录属主和最小读取权限。
4. Nginx 返回 502
502 通常表示 Nginx 无法连接上游应用。依次检查:
sudo systemctl is-active example-app.service
curl -i http://127.0.0.1:8000/healthz
sudo ss -lntp | grep ':8000'
sudo tail -n 100 /var/log/nginx/error.log
判断方式:

- 本机访问 8000 失败:先修复应用服务。
- 本机访问 8000 成功,Nginx 返回 502:检查
proxy_pass、端口和 Nginx 配置。 - Nginx 配置测试失败:不要 reload,修复语法后再执行。
- 本机 Nginx 正常,公网访问失败:检查 DNS、安全组、UFW 和客户端网络路径。
5. DNS 正确但 HTTPS 失败
先使用 curl --resolve 分离 DNS 问题:
curl -vk --resolve example.com:443:203.0.113.10 \
https://example.com/healthz
如果 --resolve 成功而普通访问失败,优先检查 DNS 缓存、AAAA 记录和代理层。如果两者都失败,再检查 Nginx 的 443 监听、证书文件和证书工具输出:
sudo nginx -t
sudo ss -lntp | grep ':443'
sudo journalctl -u nginx -n 100 --no-pager
十、回滚方案与变更边界
1. SSH 配置回滚
如果只是本次新增的 drop-in 导致问题,优先采用保留文件的方式回滚:
sudo mv \
/etc/ssh/sshd_config.d/99-hardening.conf \
/etc/ssh/sshd_config.d/99-hardening.conf.rollback
sudo sshd -t
sudo systemctl reload ssh
如果主配置也被修改,应使用对应时间点的备份恢复。恢复前确认备份目录,不要把不确定的路径直接复制到 /etc/ssh:
sudo find /root -maxdepth 1 -type d -name 'ssh-backup-*' -printf '%f\n'
恢复配置会覆盖当前 SSH 设置,必须在控制台或保持原有管理会话的情况下进行。恢复后仍要执行 sshd -t,确认无语法错误,再 reload。
2. Nginx 和应用回滚
Nginx 配置回滚前先保留当前版本:
sudo cp -a /etc/nginx/sites-available/example.com \
/root/example.com.nginx.failed.$(date +%Y%m%d-%H%M%S)
如果要停用本次新增站点,不要直接删除配置:
sudo mv \
/etc/nginx/sites-enabled/example.com \
/etc/nginx/sites-enabled/example.com.disabled
sudo nginx -t
sudo systemctl reload nginx
应用回滚可以先停止应用,再切换代码目录或恢复已验证的发布版本:
sudo systemctl stop example-app.service
sudo systemctl disable example-app.service
停止服务会使依赖该应用的站点不可用,Nginx 可能返回 502。执行前应确认业务影响,并准备好恢复命令:
sudo systemctl enable --now example-app.service
如果 HTTPS 工具已经改写 Nginx 配置,应优先通过站点配置备份恢复相关文件,而不是覆盖整个 /etc/nginx 目录。证书、私钥和其他站点配置可能位于同一目录,粗暴恢复会造成新的中断。
3. 使用快照恢复的边界
当系统更新、磁盘损坏或多项配置同时失控时,可以考虑恢复部署前快照,但要明确:
- 恢复快照通常会停止或重启实例。
- 快照之后产生的日志、上传文件和数据库写入可能丢失。
- 公网 IP 是否保留取决于云平台资源模型。
- DNS TTL 不会因为快照恢复而自动改变。
- 恢复后仍需重新检查 SSH、UFW、Nginx、应用服务和证书状态。
因此,快照适合回退系统和配置,不适合替代业务数据备份。数据库、上传文件、证书私钥和应用制品应按照各自的备份策略保存。
十一、上线与验收检查清单
上线前逐项确认,未通过的项目不要用“先上线再修”代替:
- [ ] 操作系统版本、CPU 架构、主机名和时区符合部署记录。
- [ ] 已有云平台快照或可恢复备份,且知道恢复入口。
- [ ]
deploy可以使用指定 SSH 私钥登录。 - [ ]
deploy可以通过 sudo 管理服务。 - [ ] root 不能直接 SSH 登录。
- [ ] SSH 密码认证已关闭,且新终端密钥登录测试成功。
- [ ] 当前 SSH 连接来源已经加入 UFW 和云平台安全组。
- [ ] UFW 仅开放必要端口,应用内部端口未对公网监听。
- [ ]
systemctl --failed没有未处理的业务失败服务。 - [ ] 应用进程使用独立账户运行。
- [ ] 应用只监听
127.0.0.1:8000或预定的本机端口。 - [ ]
curl http://127.0.0.1:8000/healthz返回预期结果。 - [ ]
nginx -t通过,Nginx 已设置开机启动。 - [ ] 使用
curl --resolve验证了域名 Host 路由。 - [ ] A/AAAA 记录与实际服务器地址一致,未留下错误旧记录。
- [ ] HTTPS 证书覆盖实际访问域名。
- [ ]
curl -I https://域名/healthz返回成功状态。 - [ ]
certbot renew --dry-run或实际证书续期机制已验证。 - [ ] 应用和 Nginx 日志位置已记录,出现 502、403、连接拒绝时知道查看哪一层。
- [ ] 已记录 SSH、UFW、Nginx 和应用配置的回滚位置。
- [ ] 已安排后续补充项,包括系统安全更新、磁盘空间监控、服务异常告警和异机数据备份。



