云服务器与安全组
本文范围
这篇文章只讲云服务器作为部署入口时必须管住的边界:地域和可用区、VPC 和子网、公网 IP / EIP、NAT 网关、负载均衡、安全组入站和出站、系统防火墙、云盘挂载、快照镜像、端口暴露和外网访问不通排查。
本文不展开云厂商控制台的逐按钮教程,也不讲 Terraform、Pulumi、K8S 节点池、云原生网络插件、数据库内核调优和完整安全合规体系。后面涉及 MySQL、Redis、Docker、Nginx,只从“端口是否应该暴露、流量应该从哪里进出、故障应该按哪条链路排查”的部署运维角度讲。
读完以后你应该能做三件事:买机器前写清网络边界,开放端口前说清来源和目标,外网访问不通时按入口链路逐层定位,而不是在控制台里反复试规则。
前置条件
建议先准备这些东西,再跟着文章操作:
- 一台 Linux 云服务器,Ubuntu 22.04 / 24.04、Debian 12、Rocky Linux 9、AlmaLinux 9、CentOS Stream 9 或同类发行版都可以。
- 一个普通运维账号,能使用
sudo,并且 SSH 入口已经限制在固定办公出口、VPN、堡垒机或临时白名单。 - 云厂商控制台权限至少能查看和修改 VPC、子网、安全组、EIP、云盘、快照、NAT 网关和负载均衡。
- 本机具备
ip、ss、curl、nc、dig或nslookup这类基础排障工具;没有就先通过发行版仓库安装。 - 所有示例 IP、网段、域名和端口都是占位值,落地时必须替换成自己的环境,并先在测试安全组里验证。
先把云服务器这件事说清楚
很多外网访问不通的问题,最后都不是应用代码问题,而是边界没分清楚。
一台云服务器不是“买了一台 Linux 就挂在公网”。它一般在某个地域、某个可用区、某个 VPC、某个子网里。请求进来时,要先经过域名、公网 IP、EIP、负载均衡、网络 ACL、安全组、系统防火墙,最后才到进程监听的端口。
这里不展开某一家云厂商的完整控制台手册,也不展开 Terraform、Pulumi 这类 IaC。我们只抓部署时最容易出事故的边界:服务器、镜像、安全组、端口、磁盘、快照、VPC、NAT、负载均衡和排障顺序。
可以先按这条链路理解:
公网请求进入云主机的分层链路图
这张图用来做上线前设计评审和故障定位分工:DNS、入口地址、负载均衡、网络 ACL、安全组、主机防火墙、进程监听和应用健康分别由谁负责,都要能说清楚。真正排障时也按这个方向逐层缩小范围,先确认请求是否到达云入口,再确认是否进入子网和实例,最后才查进程和应用。
这里要注意,安全组放行只是说明“云边界允许这类流量进来”。它不等于系统防火墙也放行,不等于服务已经启动,也不等于服务监听在公网能访问的地址上。网络 ACL 也不要和安全组混在一起看:它通常在子网或交换机层做更粗的入出方向控制,状态性、默认规则、优先级和适用资源会因厂商不同而不同,落地时要按当前云平台文档和控制台实际规则核对。
拿到一台机器后,不要急着改安全组,先在服务器上执行下面几条命令:
hostnamectl
cat /etc/os-release
ip -brief addr
ip route
ss -lntup
curl -4 --connect-timeout 5 https://ifconfig.me这几条命令解决的是最基础的确认问题。
hostnamectl看主机名、虚拟化类型和内核信息,确认自己没有登录错机器。cat /etc/os-release看系统发行版,后面选择apt、dnf、yum、ufw、firewalld都要靠它判断。ip -brief addr看网卡和内网 IP,云服务器里一般能看到的是私网地址,不一定能直接看到 EIP。ip route看默认路由,没有默认路由的机器通常不能直接出公网。ss -lntup看本机监听端口,如果这里没有:80、:443、:8080,安全组开再大也没用。curl ifconfig.me看当前机器出公网时使用的公网出口,可能是 EIP,也可能是 NAT 网关的公网地址。
如果 curl ifconfig.me 超时,先查出站安全组、NAT、路由表和 DNS,不要一上来怀疑应用。如果 ss -lntup 里没有端口,先查进程和配置,也不要继续在控制台反复点安全组。
下面所有示例都使用保留地址,替换时换成你自己的真实地址:
公网示例 IP:203.0.113.10
内网示例 IP:10.0.1.10
运维出口网段:198.51.100.0/24
应用健康检查:/health一、买服务器前先定边界
云服务器不要先买完再规划。先把“谁能访问谁、从哪里进、从哪里出、数据放哪里”写下来,后面安全组、磁盘、快照和负载均衡才不会乱。
一般可以先写这样一张表:
服务 实例数 规格 子网 是否公网 数据盘 端口
Nginx 2 2C4G web-subnet 否 40G 80,443
App 2 4C8G app-subnet 否 100G 8080
MySQL 1 4C16G db-subnet 否 500G 3306
Redis 1 2C8G cache-subnet 否 100G 6379
堡垒机 1 2C4G ops-subnet 是 40G 22这张表要解决几个问题:
- 哪台机器需要公网入口,哪台只在内网被访问。
- 哪些端口是给用户访问的,哪些端口只给运维或内网服务访问。
- 数据盘挂在哪里,快照策略应该绑在哪块盘上。
- 负载均衡后面挂的是 Nginx、应用服务,还是两层都有。
选型时可以按下面的规则判断。
| 项目 | 一般建议 | 常见坑 |
|---|---|---|
| 地域 | 靠近用户、数据库、对象存储和备案主体 | 应用和数据库跨地域,内网不通或延迟很高 |
| 可用区 | 单机先同可用区,多节点再按高可用跨可用区 | 为了“看起来高可用”随意跨可用区,反而增加延迟 |
| VPC | 生产、测试、开发尽量清晰隔离 | 所有资源塞进默认 VPC,后期路由和审计很痛苦 |
| 子网 | Web、App、DB、Cache 分层 | 全部机器一个子网,安全组只能粗放控制 |
| 规格 | 看 CPU、内存、网络带宽、磁盘 IOPS | 只看 vCPU 和内存,忽略云盘和网络瓶颈 |
| 系统盘 | 放系统、运行时和基础工具 | 把数据库、上传文件、日志都堆系统盘 |
| 数据盘 | 单独挂载,单独快照 | 控制台挂了盘,但 Linux 里没有初始化和挂载 |
| 公网入口 | 小系统可以 EIP,正式入口优先负载均衡 | 每台应用机都有公网 IP,暴露面越来越大 |
这里要注意,测试环境 2C4G 可以跑很多小服务,但生产环境不要只看“能启动”。JVM、数据库、Redis、日志写入、容器数量、峰值流量都会吃资源。尤其数据库和 Redis,不要长期塞在小规格机器里硬扛。
二、系统镜像不要随便点
系统镜像一般有三类:公共镜像、自定义镜像、市场镜像。
公共镜像适合新项目起步,优先选云厂商维护的主流 LTS 系统。自定义镜像适合团队已经固化了安全基线、监控 Agent、日志 Agent、基础目录和初始化脚本。市场镜像要谨慎,它可能预装了你不熟悉的软件、账号、面板和防火墙规则。
新机器启动后,先确认系统和基础资源:
whoami
sudo -v
uname -r
cat /etc/os-release
timedatectl
df -hT
free -h执行后重点看这些结果:
whoami如果不是普通运维账号,而是直接root,生产上要尽快改成普通账号加sudo。sudo -v能成功,说明当前账号有提权能力;失败就先补权限,不然后面装软件、挂盘都会卡住。timedatectl要看时区和 NTP,同一套系统里时间漂移会影响日志排查、证书校验和分布式锁。df -hT要看文件系统类型和系统盘剩余空间,不要等日志写满/才发现。free -h看可用内存,刚启动就所剩不多,后面部署容器和 JVM 要先收敛。
SSH 登录建议从第一天就按生产方式做。
ssh -i ~/.ssh/prod-ops.pem ops@203.0.113.10这条命令解决的是“用密钥登录指定公网入口”。如果连接失败,可以加 -vvv 看详细过程:
ssh -vvv -i ~/.ssh/prod-ops.pem ops@203.0.113.10如果日志停在 Connecting to 203.0.113.10 port 22,多半是网络层不通,去查 EIP、安全组、系统防火墙。如果已经出现 Permission denied,说明网络通了,问题在用户名、密钥、公钥、登录策略。
生产上一般要禁用密码登录和 root 直登。改之前先保留当前 SSH 会话,再开一个新窗口测试,避免把自己锁在门外。
sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin|pubkeyauthentication'如果要调整,可以新建一个单独配置文件:
sudo mkdir -p /etc/ssh/sshd_config.d
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
EOF
sudo sshd -t
sudo systemctl reload sshdsshd -t 没有输出才表示配置语法通过。新窗口能用密钥登录后,再关闭旧窗口。这里不要图省事直接重启服务器,SSH 配置错了,重启也救不了登录问题。
三、VPC、公网 IP、内网 IP、EIP 先分清
内网 IP 是云服务器在 VPC 子网里的地址,服务之间互相访问一般走它。公网 IP 或 EIP 是互联网访问入口,它可能绑定在云服务器上,也可能绑定在负载均衡、NAT 网关或其他云资源上。
先在机器上看自己的私网地址:
ip -4 -brief addr
ip route一般会看到类似结果:
eth0 UP 10.0.1.10/24
default via 10.0.1.1 dev eth0这里 10.0.1.10 是内网 IP,10.0.1.1 是默认网关。很多云上 EIP 不会直接出现在 ip addr 里,所以不要在服务器里找不到 EIP 就以为没绑定。
再看出公网出口:
curl -4 --connect-timeout 5 https://ifconfig.me如果返回 203.0.113.10,说明这台机器出公网时被这个公网地址代表。它可能是实例公网 IP,也可能是 EIP,也可能是 NAT 网关出口。这个结果只说明出站路径,不代表入站也能访问这台机器。
内网连通性可以这样看:
ip route get 10.0.2.20
nc -vz 10.0.2.20 8080ip route get 解决的是“去这个内网地址走哪张网卡、哪个网关”。nc -vz 解决的是“TCP 端口能不能握手”。如果 ip route get 没有合适路由,先查 VPC、子网、路由表。如果路由有但 nc 不通,再查安全组、网络 ACL、系统防火墙和服务监听。
公网入口一般有三种做法:
| 做法 | 适用场景 | 注意点 |
|---|---|---|
| 实例公网 IP | 临时测试、小工具 | 机器释放或重建后地址可能变化,边界容易散 |
| EIP | 需要固定公网入口的小系统 | 可以解绑迁移,但仍要控制安全组和系统防火墙 |
| 负载均衡公网地址 | 正式业务入口 | 后端机器尽量不直接暴露公网,只放行负载均衡到后端端口 |
生产上更推荐“负载均衡公网入口 + 后端云服务器无公网 IP”。这样对外只暴露 80/443,后端 8080、3306、6379 都在内网里收敛。
四、安全组不要一把梭
安全组可以理解成云服务器外面的一道云防火墙。它通常按入站和出站两类规则控制流量。入站管别人能不能访问你,出站管你能不能访问别人。
新手最容易犯的错误是为了省事直接放开:
入站 0.0.0.0/0 ALL 允许这条规则看着方便,实际上等于把所有端口都暴露到公网。生产环境不要这样做。
更合理的规则要按入口分层:
| 入口类型 | 端口 | 来源 | 建议 |
|---|---|---|---|
| 用户入口 | 80,443 | 0.0.0.0/0 | 只放到负载均衡或 Web 层 |
| SSH 运维 | 22 | 固定办公出口、VPN、堡垒机 | 不要长期对全网开放 |
| 应用端口 | 8080,9000 等 | 负载均衡安全组或 Web 层安全组 | 不直接暴露公网 |
| MySQL | 3306 | App 安全组或固定内网网段 | 不开公网 |
| Redis | 6379,16379 | App 安全组或 Redis 集群节点 | 不开公网 |
| 监控 Agent | 自定义 | 监控系统内网地址 | 按监控链路单独放行 |
安全组改完以后,要回到服务器验证服务是否真的监听。
sudo ss -lntup | grep -E ':80|:443|:8080'如果没有输出,说明本机没有进程监听这些端口。此时去控制台继续加规则没有意义,先启动服务。
本机能访问再测内网:
curl -I http://127.0.0.1:8080/health
curl -I http://10.0.1.10:8080/health127.0.0.1 能通但 10.0.1.10 不通,常见原因是服务只监听了本地回环地址。需要把应用监听地址改成内网地址或 0.0.0.0,再靠安全组收口。
从外部机器测公网端口可以用:
nc -vz 203.0.113.10 443
curl -vkI https://example.comWindows 运维机可以用:
Test-NetConnection 203.0.113.10 -Port 443如果 nc 超时,通常是安全组、EIP、路由、系统防火墙有一层没通。如果 nc 显示 refused,说明网络到了机器,但端口没有服务监听,或者服务主动拒绝。
五、入站规则按公网、运维、内网三类写
先看公网入口。普通 Web 系统一般只需要 80 和 443。
方向:入站
协议:TCP
端口:80,443
来源:0.0.0.0/0
目标:负载均衡安全组或 Web 安全组这里要注意,0.0.0.0/0 只适合放在用户入口,不适合放到应用、数据库、缓存端口。互联网用户访问网站需要 80/443,不需要访问你的 8080、3306、6379。
SSH 运维入口建议这样写:
方向:入站
协议:TCP
端口:22
来源:198.51.100.0/24
目标:堡垒机或运维入口机器如果公司有 VPN,就写 VPN 出口地址。如果有堡垒机,就只让堡垒机能访问后端机器的 22。临时排障需要放开自己当前公网 IP,也要设置过期提醒,排完就删。
内网服务之间能用安全组引用时,优先用安全组引用。比如 App 访问 MySQL:
方向:入站
协议:TCP
端口:3306
来源:sg-app
目标:sg-db这样后面 App 扩容机器,只要加入 sg-app,不需要反复维护 IP 白名单。不同云厂商对安全组引用、跨 VPC 引用、企业安全组能力支持不完全一样,落地时以当前厂商文档为准。
内网规则验证时,在 App 机器上执行:
nc -vz 10.0.3.10 3306执行成功只说明 TCP 通了,不代表账号密码和权限正确。继续用客户端验证:
mysql -h 10.0.3.10 -P 3306 -u app_user -p -e "select @@hostname, @@port;"如果 nc 通、mysql 不通,多半是数据库账号、授权主机、TLS 或认证插件问题。如果 nc 都不通,先别查 SQL,回到网络边界。
六、出站规则不要只会全放开
很多云账号默认出站是全放开,测试环境可以先这样跑通。但生产上如果有合规要求,出站也要收敛。
常见出站需求如下:
| 目标 | 端口 | 用途 |
|---|---|---|
| DNS 解析器 | 53 TCP/UDP | 域名解析 |
| NTP 服务 | 123 UDP | 时间同步 |
| 软件源、镜像仓库、对象存储 | 80,443 | 拉包、拉镜像、上传备份 |
| 内网数据库 | 3306,5432 等 | 应用访问数据库 |
| 内网 Redis | 6379,16379 | 应用访问缓存或 Redis Cluster |
| 监控日志平台 | 按平台要求 | 指标、日志、链路追踪上报 |
在服务器上可以这样验证出站:
getent hosts repo.example.com
curl -I --connect-timeout 5 https://repo.example.com
timedatectl timesync-statusgetent hosts 解决 DNS 是否能解析。curl 解决 443 出站是否能握手。timedatectl timesync-status 看时间同步状态。如果 DNS 不通,表现可能是所有域名访问都失败,但直接访问 IP 又能通,这时不要误判成安全组入站问题。
出站收敛时不要一次性把默认全放开删掉。建议先用日志或流量分析确认真实依赖,再按服务逐步收口。否则很容易出现“系统能启动,但拉不到镜像、发不出告警、传不了备份”的隐性故障。
七、系统防火墙是第二道门
安全组在云上,系统防火墙在机器里。流量要同时通过两道门。
Ubuntu 常见是 UFW:
sudo ufw status verbose
sudo ufw allow from 198.51.100.0/24 to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reloadufw status verbose 用来看当前策略。后面三条分别放行固定运维网段的 SSH、HTTP 和 HTTPS。执行后再次查看:
sudo ufw status numbered如果规则顺序不对,可以用编号删除或调整。这里不要随手 ufw disable,排障时可以临时放行指定来源,排完再收回。
CentOS、Rocky Linux、AlmaLinux、RHEL 常见是 firewalld:
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" port protocol="tcp" port="22" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --list-all这组命令解决的是“当前防火墙是否运行、网卡在哪个 zone、永久规则是什么”。--permanent 写入持久配置,--reload 让配置生效。只加 --add-port 不加 --permanent,重启后规则可能丢失。
老系统或某些镜像可能还在用 iptables,可以先只查看:
sudo iptables -L -n --line-numbers
sudo iptables -S看到 DROP、REJECT、自定义链时,先确认这些规则是谁写的。不要直接清空 iptables,很多容器网络、云安全组件和防护软件也会写规则,清空后可能把 Docker、NAT 或安全 Agent 一起打坏。
八、Nginx 只做公网入口和反向代理
Nginx 常见位置是公网入口层或负载均衡后的 Web 层。它对外监听 80/443,对内转发到应用端口。
一个简单的反向代理可以这样写:
server {
listen 80;
server_name example.com;
location /health {
return 200 "ok\n";
}
location / {
proxy_pass http://10.0.2.10: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;
}
}改完以后先检查语法:
sudo nginx -t
sudo systemctl reload nginxnginx -t 看到 syntax is ok 和 test is successful 才 reload。失败时不要重启,先看它提示的文件和行号。
验证时按本机、内网、公网三步来:
curl -I http://127.0.0.1/health
curl -I http://10.0.1.10/health
curl -I http://203.0.113.10/health本机通、内网不通,查监听地址和系统防火墙。内网通、公网不通,查 EIP、安全组、负载均衡和 DNS。公网通但业务接口 502,查 Nginx 到上游应用的内网端口。
看 Nginx 日志时,不要只看最后几行,最好带上状态码和来源过滤:
sudo tail -n 100 /var/log/nginx/access.log
sudo tail -n 100 /var/log/nginx/error.log
sudo grep ' 502 ' /var/log/nginx/access.log | tail -n 20访问后应该能在 access.log 看到对应请求。如果浏览器访问了,但日志完全没有记录,请求大概率没到这台 Nginx,继续查 DNS、CDN、WAF、负载均衡和安全组。
九、Docker 端口映射最容易误暴露
Docker 里应用能跑,不代表外部能访问。容器端口、宿主机端口、安全组端口是三回事。
先看容器当前端口映射:
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
docker port my-app
sudo ss -lntup | grep -E ':8080|:80|:443'docker ps 的 PORTS 列如果出现 0.0.0.0:8080->8080/tcp,说明宿主机所有网卡都在监听 8080。只要安全组和系统防火墙放行,公网就可能访问到它。
如果应用只给本机 Nginx 反代,建议绑定到回环地址:
docker run -d \
--name my-app \
--restart unless-stopped \
-p 127.0.0.1:8080:8080 \
-v /data/my-app/logs:/app/logs \
registry.example.com/team/my-app:1.0.0这里 -p 127.0.0.1:8080:8080 的含义是:宿主机只在 127.0.0.1:8080 接收请求,再转发到容器的 8080。外部机器不能直接访问这个端口,Nginx 在同一台宿主机上可以访问。
如果负载均衡要直接访问宿主机应用端口,可以绑定内网地址:
docker run -d \
--name my-app \
--restart unless-stopped \
-p 10.0.1.10:8080:8080 \
registry.example.com/team/my-app:1.0.0这种方式要配合安全组,只允许负载均衡或 Web 层访问 8080。不要因为“负载均衡要访问”就把 8080 开给 0.0.0.0/0。
Compose 里也一样:
services:
app:
image: registry.example.com/team/my-app:1.0.0
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
volumes:
- /data/my-app/logs:/app/logs启动后验证:
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/health
docker logs --tail=100 my-app如果 curl 127.0.0.1 不通,先看容器日志和应用监听。如果本机通、公网不通,再查安全组和 Nginx,不要反过来。
顺手看一下 Docker 磁盘占用:
docker system df
docker inspect my-app --format '{{.GraphDriver.Name}}'
docker inspect my-app --format '{{.GraphDriver.Data.UpperDir}}'这里讲一下 Overlay2 和 CoW,不是为了背概念,而是为了避免把数据写错地方。镜像层是只读层,容器运行时有自己的可写层。应用在容器里写入的文件,如果没有挂载到宿主机目录或云盘目录,就会落到容器可写层里。容器删掉或重建后,这些数据很容易丢。
所以生产上要记住:
- 镜像里放程序和依赖,不放运行期数据。
- 日志、上传文件、数据库文件要挂载到宿主机目录或云盘目录。
- 大量写入不要长期落在容器可写层,否则
overlay2会越来越大,系统盘容易被打满。 - 镜像来源使用企业镜像仓库、云厂商镜像仓库或已经同步校验过的内网仓库,不要在生产部署里依赖不可控的直连拉取。
十、MySQL 和 Redis 不要开公网
MySQL 端口是 3306。先看它监听在哪:
sudo ss -lntup | grep ':3306'
sudo grep -R "^[[:space:]]*bind-address" /etc/mysql /etc/my.cnf /etc/my.cnf.d 2>/dev/null如果看到 127.0.0.1:3306,说明只允许本机访问。如果看到 0.0.0.0:3306,说明所有网卡都监听,必须依靠安全组和系统防火墙限制来源。更稳妥的做法是监听内网地址,比如 10.0.3.10:3306,再只允许 App 安全组访问。
从应用机器验证:
nc -vz 10.0.3.10 3306
mysql -h 10.0.3.10 -P 3306 -u app_user -p -e "select @@hostname, @@port;"nc 不通,查网络。nc 通但 mysql 登录失败,查账号授权、密码、TLS、认证插件。这里不要把数据库端口开到公网去“试一下”,试完很容易忘记关。
Redis 默认端口是 6379,集群还会用到总线端口,常见是 16379。先看监听和关键配置:
sudo ss -lntup | grep -E ':6379|:16379'
sudo grep -E '^(bind|protected-mode|port|requirepass|aclfile)' /etc/redis/redis.conf 2>/dev/null
redis-cli -h 10.0.4.10 -p 6379 PING正常情况下,PING 返回 PONG。如果返回认证错误,说明网络通了,继续查 Redis ACL 或密码。如果连接超时,先查安全组、系统防火墙、Redis 监听地址。
生产建议很直接:
- Redis 不开放公网。
- MySQL 不开放公网。
- Redis 开启认证或 ACL,保留
protected-mode的防护思路。 - MySQL 用户不要随便用
'%'放行所有来源,至少限制到应用网段或应用机器。 - 跨 VPC、跨机房访问数据库和 Redis 时,要把网络延迟、TLS、连接池、故障切换一起评估。
十一、云盘挂载:控制台挂上不等于系统能用
控制台显示云盘已挂载,只说明云平台把块设备接到了服务器。Linux 里还要分区、格式化、挂载、写入 /etc/fstab。
先看磁盘:
lsblk -f
sudo file -s /dev/vdblsblk -f 解决的是“系统看到了哪些磁盘、有没有文件系统、挂载到了哪里”。file -s 用来判断磁盘上是否已有文件系统。这里要非常小心,如果磁盘里已经有数据,不要执行格式化。
新盘可以这样初始化:
sudo parted /dev/vdb --script mklabel gpt mkpart primary xfs 0% 100%
sudo mkfs.xfs -f /dev/vdb1
sudo mkdir -p /data
sudo blkid /dev/vdb1parted 创建 GPT 分区表和一个主分区。mkfs.xfs 格式化文件系统。blkid 会输出 UUID,后面要用 UUID 挂载,不要用容易变化的 /dev/vdb1。
把下面的 UUID 换成你机器上的真实值:
sudo cp /etc/fstab /etc/fstab.bak.$(date +%F-%H%M%S)
echo 'UUID=11111111-2222-3333-4444-555555555555 /data xfs defaults,nofail 0 2' | sudo tee -a /etc/fstab
sudo mount -a
df -hT /datamount -a 没有输出,一般说明挂载成功。df -hT /data 应该能看到 /data 对应的新云盘和文件系统类型。
如果 mount -a 报错,先恢复 /etc/fstab,不要重启机器。fstab 写错后重启,机器可能进 emergency mode,远程排障会很麻烦。
生产上建议:
- 系统盘只放系统和运行时,业务数据放数据盘。
/data下面按服务拆目录,比如/data/mysql、/data/redis、/data/app/logs。- 数据盘单独配置快照策略。
- 扩容前先做快照,再扩云盘,再扩文件系统。
- 不要把数据库数据目录放在容器可写层里。
十二、快照、镜像和回滚要提前演练
快照是云盘某个时间点的数据副本。镜像更偏向“用当前系统盘创建一台新机器”。它们都不是万能备份,但都是云服务器运维里必须用好的工具。
发布前做快照,至少要覆盖系统盘和关键数据盘。普通文件类数据可以先刷盘:
sync如果是 XFS、ext4 这类文件系统,而且业务允许短暂冻结,可以配合 fsfreeze:
sudo fsfreeze -f /data
# 到云控制台创建 /data 对应云盘快照,创建动作发起后尽快解冻
sudo fsfreeze -u /data这里要注意,冻结期间应用写入会阻塞,不要长时间冻结生产数据盘。数据库不要只靠云盘快照就当完整备份,MySQL、PostgreSQL、Redis 这类服务要结合数据库自身备份、binlog、AOF/RDB、主从或云数据库备份能力。
发布回滚一般有三种:
| 方式 | 适合场景 | 注意点 |
|---|---|---|
| 应用版本回滚 | 代码发布失败 | 最快,优先使用 |
| 云盘快照回滚 | 系统文件、配置、数据盘被误改 | 回滚会覆盖当前盘状态,先确认影响 |
| 自定义镜像重建 | 机器基线损坏或要批量复制环境 | 镜像里的密钥、机器标识、临时文件要清理 |
如果要把一台机器做成自定义镜像模板,先检查正在运行的服务:
systemctl list-units --type=service --state=running模板机里不要保留业务数据、临时密钥、历史日志和机器特有配置。使用 cloud-init 的镜像,在制作模板前要按厂商建议清理实例初始化痕迹。这个动作不要在正在承载业务的生产机上随手执行。
十三、NAT 网关解决无公网机器出网
后端机器不挂公网 IP,并不代表不能访问公网。常见做法是给私有子网配置 NAT 网关,让机器通过 SNAT 出公网。
在无公网机器上验证:
ip route
curl -4 --connect-timeout 5 https://ifconfig.me
curl -I --connect-timeout 5 https://repo.example.com如果 curl ifconfig.me 返回 NAT 的公网地址,说明出站路径基本通了。如果超时,按这个顺序查:
- 子网路由表有没有指向 NAT 网关的默认路由。
- NAT 网关有没有绑定公网 IP 或 EIP。
- 安全组出站是否允许 80/443、DNS、NTP。
- DNS 是否能解析。
- 企业防火墙或云防火墙是否拦截。
NAT 只解决“内网机器主动访问公网”。它不解决“公网主动访问内网机器”。公网要访问后端服务,还是要用负载均衡、EIP、VPN、专线或内网穿透类方案,不要把 NAT 当成反向入口。
十四、负载均衡边界要和安全组配合
负载均衡对外提供一个地址,对内把流量转发到后端服务器。它不只是“分发流量”,还会影响安全组边界、健康检查、源 IP 传递和证书终止位置。
后端服务先在本机验证:
curl -I http://127.0.0.1:8080/health
curl -I http://10.0.2.10:8080/health这两条都通,才说明应用本身和内网监听基本正常。然后从同 VPC 的另一台机器访问:
curl -I http://10.0.2.10:8080/health如果本机通、其他机器不通,常见原因是应用只监听 127.0.0.1,或者系统防火墙、安全组没有放行内网来源。
负载均衡健康检查要和应用真实路径一致。比如检查路径是 /health,应用就要稳定返回 200:
curl -i http://10.0.2.10:8080/health访问后应该看到 HTTP/1.1 200 或 HTTP/2 200。如果返回 301、302、401、403、404,负载均衡可能把后端判成异常。健康检查接口不要依赖登录态,也不要走很重的数据库查询。
后端安全组建议这样放:
方向:入站
协议:TCP
端口:8080
来源:负载均衡安全组或负载均衡后端访问网段
目标:App 安全组不要把后端 8080 对 0.0.0.0/0 开放。用户访问负载均衡的 80/443,负载均衡访问后端 8080,这是两段不同的链路。
502、504 排查时,看三处:
sudo tail -n 100 /var/log/nginx/error.log
curl -I http://10.0.2.10:8080/health
sudo ss -lntup | grep ':8080'Nginx 或负载均衡报 502,通常是后端拒绝、后端超时、健康检查异常、协议不一致。比如负载均衡用 HTTP 转发,但后端端口实际是 HTTPS,就会出现看起来端口通、请求失败的情况。
十五、中国大陆、企业内网、离线环境要换打法
中国大陆环境部署时,最常见的问题不是命令不会写,而是外部依赖不稳定:软件源慢、镜像拉不下来、包版本不一致、DNS 被拦、证书链异常。
生产上一般这样做:
- 操作系统软件源使用云厂商当前官方源或企业内网源。
- 容器镜像先同步到企业镜像仓库或云厂商镜像仓库,比如 ACR、TCR、SWR、自建 Harbor。
- 构建机可以访问外部,生产机只访问内网仓库。
- 基础镜像、系统包、数据库客户端、监控 Agent 固化版本,不在生产发布时临时下载。
- 离线现场提前准备包清单、校验文件、导入脚本和回滚包。
容器镜像离线交付可以这样做:
docker pull registry.example.com/base/nginx:1.27
docker save registry.example.com/base/nginx:1.27 -o nginx-1.27.tar
sha256sum nginx-1.27.tar到离线服务器后:
sha256sum -c nginx-1.27.tar.sha256
docker load -i nginx-1.27.tar
docker images | grep nginxsha256sum -c 解决的是“文件有没有在传输过程中损坏或被替换”。docker load 只是导入镜像,不会自动启动容器。导入后还要按部署脚本启动并验证端口。
系统包也要先确认来源:
apt-cache policy nginx 2>/dev/null || true
dnf repolist 2>/dev/null || yum repolist 2>/dev/null如果软件源不可用,不要在生产机上到处试第三方地址。正确做法是让运维或平台团队提供当前环境认可的软件源、代理或离线包。
企业内网还要特别注意 DNS 和时间:
getent hosts example.com
timedatectlDNS 错了,服务发现、证书校验、对象存储访问都会出问题。时间错了,HTTPS 证书、JWT、签名 URL、数据库审计都会出现很怪的报错。
十六、外网访问不通按这条链路排
先不要猜,按顺序查。
外网访问不通排查决策图
这张图不是替代命令,而是帮现场先判断“卡在哪一层”。评审排障手册时,可以把每个节点都补上本团队的控制台位置、负责人和验证命令;真正故障时,先记录访问域名、端口、源 IP 和时间,再按图分流,避免一边改安全组一边重启应用,最后不知道哪个动作生效。
现象一:浏览器一直转,最后超时
外部机器先测:
curl -v --connect-timeout 5 http://203.0.113.10/
nc -vz 203.0.113.10 80如果都是超时,说明 TCP 连接没有建立。回服务器看监听:
sudo ss -lntup | grep ':80'没有监听就启动 Nginx 或应用。有监听再看系统防火墙:
sudo ufw status verbose 2>/dev/null || true
sudo firewall-cmd --list-all 2>/dev/null || true
sudo iptables -L -n --line-numbers系统防火墙没问题,再查云控制台:EIP 是否绑定、实例是否在正确安全组、入站 80 是否允许、来源是否写错、负载均衡是否转发到这台后端。
现象二:Connection refused
Connection refused 比超时更明确,说明网络到了目标机器,但目标端口没有进程接收,或者进程主动拒绝。
sudo ss -lntup | grep ':8080'
systemctl status my-app --no-pager
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --tail=100 my-app如果应用监听在 127.0.0.1:8080,其他机器访问 10.0.1.10:8080 就会失败。要么让 Nginx 在本机反代,要么把应用监听地址改成内网地址,并用安全组限制来源。
现象三:安全组开了端口仍然不通
这种情况很常见。按下面顺序查:
# 1. 服务是否监听
sudo ss -lntup | grep ':443'
# 2. 本机是否能访问
curl -vkI https://127.0.0.1/
# 3. 内网是否能访问
curl -vkI https://10.0.1.10/
# 4. 系统防火墙是否拦截
sudo ufw status verbose 2>/dev/null || true
sudo firewall-cmd --list-all 2>/dev/null || true
# 5. 云上入口是否正确
curl -vkI https://203.0.113.10/本机不通,查应用。本机通、内网不通,查监听地址和系统防火墙。内网通、公网不通,查 EIP、负载均衡、安全组、网络 ACL、云防火墙。
现象四:SSH 连接不上
客户端先看详细日志:
ssh -vvv -i ~/.ssh/prod-ops.pem ops@203.0.113.10如果是超时,查公网入口和 22 入站规则。如果是 Permission denied,查用户名、公钥和服务端 SSH 配置。如果提示密钥权限问题,在客户端修正:
chmod 600 ~/.ssh/prod-ops.pem服务端能登录时,看 SSH 日志:
sudo journalctl -u ssh -u sshd -n 100 --no-pager
sudo tail -n 100 /var/log/auth.log 2>/dev/null || true
sudo tail -n 100 /var/log/secure 2>/dev/null || true生产上不要因为 SSH 不通就把 22 对全网开放。可以临时放行自己的固定出口 IP,排完删掉。
现象五:负载均衡健康检查失败
在后端机器上先测健康检查路径:
curl -i http://127.0.0.1:8080/health
curl -i http://10.0.2.10:8080/health再从同 VPC 另一台机器测:
curl -i http://10.0.2.10:8080/health如果同 VPC 不通,负载均衡也不会通。常见原因是后端安全组没放行负载均衡来源、应用只监听本机、健康检查路径返回非 200、HTTP/HTTPS 协议配错、后端端口填错。
现象六:Docker 容器里通,宿主机不通
先看容器内部:
docker exec -it my-app sh
curl -I http://127.0.0.1:8080/health退出容器后看宿主机:
docker port my-app
sudo ss -lntup | grep ':8080'
curl -I http://127.0.0.1:8080/health容器内部通、宿主机不通,多半是没有做 ports 映射,或者应用只监听容器内的本地地址。宿主机通、公网不通,再查 Nginx、安全组和系统防火墙。
十七、阿里云、腾讯云、华为云差异速查
三家云的概念大体相同,名字和控制台位置不完全一样。
| 能力 | 阿里云 | 腾讯云 | 华为云 | 落地注意 |
|---|---|---|---|---|
| 云服务器 | ECS | CVM | ECS | 实例规格、带宽计费、默认安全组策略不同 |
| 私有网络 | VPC | VPC | VPC | 同地域、同 VPC 内网最简单,跨 VPC 需要对等连接、云联网或企业路由 |
| 安全组 | ECS 安全组 | 安全组 | 安全组 | 都有入站/出站,规则优先级和默认规则细节要看当前文档 |
| 公网固定入口 | EIP | EIP | EIP | EIP 绑定对象可能是实例、网卡、NAT、负载均衡 |
| NAT | NAT 网关 | NAT 网关 | NAT 网关 | 解决内网主动出公网,不是公网主动入站 |
| 负载均衡 | SLB/ALB/NLB | CLB | ELB | 后端安全组要允许负载均衡访问健康检查端口 |
| 云盘 | 云盘 | 云硬盘 CBS | 云硬盘 EVS | 控制台挂载后还要在系统内分区、格式化、挂载 |
| 快照/镜像 | 快照、自定义镜像 | 快照、自定义镜像 | 快照、私有镜像 | 快照和镜像要演练恢复,不要只创建不验证 |
这里不要追求记住每家控制台菜单。真正要记住的是边界顺序:入口地址、VPC 路由、安全组、系统防火墙、监听端口、应用协议。
十八、生产安全组模板
可以按角色拆安全组,不要一个安全组塞所有规则。
sg-bastion
| 方向 | 协议端口 | 来源/目标 | 说明 |
|---|---|---|---|
| 入站 | TCP 22 | 固定办公出口或 VPN | 运维入口 |
| 出站 | TCP 22 | 内网服务器安全组 | 访问后端机器 |
sg-web
| 方向 | 协议端口 | 来源/目标 | 说明 |
|---|---|---|---|
| 入站 | TCP 80,443 | 0.0.0.0/0 | 用户入口,优先放在负载均衡 |
| 入站 | TCP 22 | sg-bastion | 只允许堡垒机 SSH |
| 出站 | TCP 8080 | sg-app | 转发到应用 |
sg-app
| 方向 | 协议端口 | 来源/目标 | 说明 |
|---|---|---|---|
| 入站 | TCP 8080 | sg-web 或负载均衡 | 应用入口 |
| 入站 | TCP 22 | sg-bastion | 运维入口 |
| 出站 | TCP 3306 | sg-db | 访问数据库 |
| 出站 | TCP 6379 | sg-cache | 访问 Redis |
sg-db
| 方向 | 协议端口 | 来源/目标 | 说明 |
|---|---|---|---|
| 入站 | TCP 3306 | sg-app | 只允许应用访问 |
| 入站 | TCP 22 | sg-bastion | 运维入口 |
| 出站 | 按需 | 备份、监控、时间同步 | 不要默认暴露公网 |
sg-cache
| 方向 | 协议端口 | 来源/目标 | 说明 |
|---|---|---|---|
| 入站 | TCP 6379 | sg-app | Redis 普通访问 |
| 入站 | TCP 16379 | sg-cache | Redis Cluster 总线 |
| 入站 | TCP 22 | sg-bastion | 运维入口 |
这套模板不是让你照抄端口,而是提醒你按角色收口。真正上线时,要结合实际应用端口、健康检查端口、监控端口和备份链路调整。
落地工程深水区
云服务器和安全组真正难的地方,不是把 80、443 打开,而是长期维护后边界不失控。下面这些点如果一开始没定清楚,后面很容易把“临时放行”变成永久风险。
| 深水问题 | 现场表现 | 落地约束 |
|---|---|---|
| 权限漂移 | 每个项目都让云账号临时开端口,安全组规则越来越多 | 安全组变更必须有工单、过期时间、来源网段、业务负责人;禁止多人共用云主账号 |
| 公网入口失控 | 应用机、数据库、Redis、管理端口直接有公网 IP | 只有负载均衡、Nginx、堡垒机这类入口层允许公网;中间件只允许内网安全组访问 |
| 出站依赖不可控 | 服务器突然访问不了镜像仓库、制品仓库、DNS、NTP,发布卡住 | 出站规则要写清 DNS、NTP、仓库、备份、监控目标;内网环境准备代理和私有仓库 |
| 安全组和系统防火墙不一致 | 控制台放通了,但机器仍然超时,或者机器放通了公网仍不通 | 上线清单同时检查安全组、NACL、系统防火墙、监听地址和负载均衡健康检查 |
| 快照只创建不验证 | 磁盘误删后发现快照缺数据盘或恢复时间不可接受 | 快照策略要覆盖系统盘和数据盘,至少按季度演练恢复,记录 RTO 和恢复步骤 |
| 模板机污染 | 自定义镜像里残留 SSH key、业务数据、历史日志、临时密码 | 制作镜像前清理机器唯一身份、临时凭证、业务目录和日志;镜像版本要有说明 |
| 成本失控 | EIP、NAT、负载均衡、云盘快照长期无人清理 | 每月巡检未绑定 EIP、闲置云盘、过期快照、低利用率实例和 NAT 流量费用 |
| 团队职责不清 | 出问题时云平台、系统、应用、网络互相甩锅 | 入口链路按 DNS、LB、安全组、OS 防火墙、Nginx、应用端口分负责人,事故记录到具体层级 |
生产上建议把安全组当成代码资产管理:至少导出规则快照,记录规则说明,发布前做差异审查。临时放行要有自动回收动作,不能只靠“记得删”。
十九、上线前检查清单
上线前可以按下面顺序过一遍。
1. 实例
- 地域、可用区、VPC、子网是否符合规划
- 是否避免应用机、数据库、Redis 直接暴露公网
- 登录方式是否使用密钥、堡垒机或固定出口
2. 镜像
- 是否使用官方公共镜像或团队治理过的自定义镜像
- 时区、NTP、基础 Agent、日志目录是否确认
- 是否清理了模板机里的临时密钥和业务数据
3. 安全组
- 80/443 是否只在入口层开放
- 22 是否只对固定来源开放
- 3306、6379、8080 是否只允许内网或指定安全组访问
- 出站规则是否覆盖 DNS、NTP、软件源、镜像仓库、备份和监控
4. 系统防火墙
- UFW/firewalld/iptables 当前规则是否和安全组一致
- 重启后规则是否仍然生效
- 是否避免直接清空防火墙规则
5. 磁盘
- 数据盘是否分区、格式化、挂载到 /data
- /etc/fstab 是否使用 UUID 和 nofail
- 快照策略是否覆盖系统盘和数据盘
6. 应用
- ss -lntup 是否看到正确监听
- Nginx 配置是否通过 nginx -t
- Docker 端口是否避免误暴露到 0.0.0.0
- /health 是否稳定返回 200
7. 回滚
- 发布前是否有快照、备份或上一版本镜像
- 是否演练过从快照或镜像恢复
- 数据库是否有独立备份和恢复验证常用命令也可以留一份在运维手册里:
# 看监听端口
sudo ss -lntup
# 看网卡和内网 IP
ip -brief addr
# 看默认路由
ip route
# 看公网出口
curl -4 --connect-timeout 5 https://ifconfig.me
# 看 UFW
sudo ufw status verbose
# 看 firewalld
sudo firewall-cmd --list-all
# 看 iptables
sudo iptables -L -n --line-numbers
# 测 TCP 端口
nc -vz 203.0.113.10 443
# 测 HTTP
curl -vkI https://example.com/health
# 看 Docker 端口
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
docker port my-app
# 看磁盘
lsblk -f
df -hT
# 看 Nginx 日志
sudo tail -n 100 /var/log/nginx/error.log
sudo tail -n 100 /var/log/nginx/access.log排障时记住一句话:先确认服务有没有监听,再确认本机能不能访问,再确认内网能不能访问,最后再查公网入口。顺序对了,问题会小很多。
