6.10 云网络基础——VPC、安全组与 cloud-init

预计阅读时间:13 分钟

📖 目录

学习目标

读完本章后,你将能够:

  • 理解 VPC、子网、CIDR 的概念及规划原则
  • 区分安全组与网络 ACL 的适用场景
  • 配置 SNAT / DNAT 让私有子网访问互联网
  • 编写 cloud-init 用户数据实现服务器初始化自动化
  • 通过元数据服务获取实例运行时信息
  • 用阿里云 CLI / Terraform 完成基础网络资源编排

核心知识

概念一句话定义云厂商对应
VPC云上隔离的私有网络环境aliyun vpc / AWS VPC
子网(vSwitch)VPC 内的 IP 段划分,关联可用区aliyun vswitch / AWS subnet
安全组实例级有状态防火墙(白名单)aliyun SecurityGroup / AWS SG
网络 ACL子网级无状态防火墙(规则有顺序)aliyun NetworkAcl / AWS NACL
NAT 网关让私有子网通过共享公网 IP 上网aliyun NAT Gateway / AWS NAT GW
EIP可独立持有的弹性公网 IP 地址aliyun EIP / AWS Elastic IP
cloud-init实例首次启动的自动化配置引擎跨平台标准(所有云都支持)
元数据服务实例通过内网 HTTP 端点查询自身属性aliyun 100.100.100.200 / AWS 169.254.169.254

知识关联

原理讲解

VPC 隔离模型

物理服务器时代,网络拓扑是固定的——交换机配 VLAN、物理网线连接、机房走线。上云之后,网络变为虚拟化的:物理交换机 → 虚拟交换机(vSwitch),物理路由器 → 虚拟路由器(VRouter),物理防火墙 → 安全组 + ACL。VPC 就是云平台在共享物理网络上划出的逻辑隔离空间,每个 VPC 独占一个 CIDR 网段,互不可达(除非通过对等连接或云企业网打通)。

CIDR 与子网设计

VPC 创建的第一个决策是CIDR 网段——一旦创建无法修改。每个子网必须关联一个可用区,同一 VPC 内的子网通过默认路由互通。每个子网的第一个、第二个和最后一个 IP 被云平台保留(网络地址、网关、广播地址),加上默认路由实际可用 IP = 总数 − 5。

安全组 vs 网络 ACL

维度安全组(Security Group)网络 ACL
作用范围实例级别(ENI)子网级别
状态有状态(出方向响应自动放行)无状态(出入规则各自独立)
规则白名单,仅允许规则白名单 + 黑名单,规则按编号顺序匹配
默认行为入方向拒绝,出方向放行出入均拒绝

生产环境通常组合使用:安全组做实例层精细控制,网络 ACL 做子网层粗粒度容灾(例如批量封禁恶意 IP)。

NAT 网关与 EIP

EIP(弹性公网 IP)是可以独立持有、随时绑定/解绑到云资源的公网 IP。NAT 网关则进一步实现了共享上网:私有子网的多个实例共用 NAT 网关的 EIP 访问互联网。SNAT 将内网源地址转换为公网 IP,DNAT 则将公网端口的入向流量转发到指定内网实例。

cloud-init 执行阶段

cloud-init 按顺序执行以下阶段:

  1. init-local:最早启动,解析数据源(如阿里云元数据服务)
  2. init:网络就绪后运行,应用 cloud-config 指令
  3. modules-config:配置模块如 write_filesusers
  4. modules-final:执行 runcmdpackage_update 等后期动作

元数据服务

云平台在每个实例内部提供一个内网可达的 HTTP 端点,用于查询实例元数据:实例 ID、区域、VPC ID、安全组、公网 IP、主机名等。这是 cloud-init 的底层数据源,也能被任意脚本直接调用。

多可用区与高可用设计

可用区(Availability Zone)是机房级别的故障隔离单元:不同可用区有独立的电力、网络和制冷。单可用区架构遇到机房故障(断电、光纤被挖断)时整体不可用,多可用区架构则能继续服务。生产设计的核心原则:

  • 跨可用区部署:同一层(Web/App)至少部署在两个可用区,前端负载均衡器自动转发健康流量
  • 数据库主备跨可用区:主库在可用区 A,备库在可用区 B,故障自动切换(阿里云 PolarDB/RDS 自动完成,传统自建 MySQL 用半同步复制)
  • 子网与可用区绑定:每个可用区放一个子网,实例按可用区分散——所以规划子网时不要只按"层"划,还要按"可用区 × 层"划
# 多可用区子网规划示例(以 2 个可用区为例)
VPC: 10.0.0.0/16
├── 可用区 A(az-a)
│   ├── 10.0.1.0/24  web-a     ← 3 台 Web
│   ├── 10.0.2.0/24  app-a     ← 3 台 App
│   └── 10.0.3.0/24  db-a      ← 主库/主 Redis
└── 可用区 B(az-b)
    ├── 10.0.11.0/24 web-b     ← 3 台 Web
    ├── 10.0.12.0/24 app-b     ← 3 台 App
    └── 10.0.13.0/24 db-b      ← 备库/备 Redis

# 故障演练:az-a 整体宕机时
# 负载均衡(SLB)自动把流量全部切到 az-b 的实例
# 数据库自动切换到 db-b 的备库(RTO 秒级-分钟级)

VPC 互通——对等连接、云企业网与 VPN

VPC 之间默认隔离。业务发展到多个 VPC(不同环境、不同区域、不同账号)时,需要按需打通:

方案适用场景特点
VPC 对等连接(Peering)同区域、少量 VPC 互通简单免费(流量费另计);CIDR 不能重叠;不能传递(A-B 互通不等于 A-C 互通)
云企业网(CEN)多区域、多 VPC、复杂拓扑星型/网状互联,自动路由分发,支持跨账号
VPN 网关 / 专线本地 IDC 与云上 VPC 互通VPN 走公网加密(低成本慢速);专线走物理链路(高可靠低延迟)
# VPC 互通排查三板斧(ping 不通时按顺序查)
# ① 路由表:对端网段是否在路由表中(VPC 互通=互相加路由条目)
ip route show
# ② 安全组:对端来源是否放行(安全组是最后一道关卡)
# ③ 防火墙/ACL:云防火墙与网络 ACL 是否拦截
# 常见坑:两个 VPC 用了相同 CIDR(10.0.0.0/16 对 10.0.0.0/16),路由无法区分 → 无解,只能重建 VPC

示例代码

CIDR 规划与计算

# 大 VPC + 多子网(推荐)
VPC CIDR: 10.0.0.0/16 (65536 个 IP)
├── 子网 1: 10.0.1.0/24 (公网业务层)     — 256 IP
├── 子网 2: 10.0.2.0/24 (应用层)         — 256 IP
├── 子网 3: 10.0.10.0/24 (数据库层)      — 256 IP
├── 子网 4: 10.0.20.0/24 (缓存层)        — 256 IP
└── 保留子网: 10.0.128.0/17 (未来扩展)    — 32768 IP

# 必须避免的冲突
# 总部 IDC: 10.0.0.0/8
# VPC 1: 10.0.0.0/16       ❌ 冲突!
# VPC 2: 172.16.0.0/16     ✅ 不冲突
# 使用 ipcalc 工具计算 CIDR
# apt install ipcalc
ipcalc 10.0.0.0/16
# 输出: Address:   10.0.0.0
#       Netmask:   255.255.0.0 = 16
#       Network:   10.0.0.0/16
#       HostMin:   10.0.0.1
#       HostMax:   10.255.255.254
#       Broadcast: 10.255.255.255
#       Hosts/Net: 65534

安全组规则(阿里云 CLI)

# 创建安全组
aliyun ecs CreateSecurityGroup --security-group-name sg_web

# 允许 HTTP/HTTPS
aliyun ecs AuthorizeSecurityGroup \
  --SecurityGroupId sg-xxx \
  --IpProtocol tcp \
  --PortRange 80/80 \
  --SourceCidrIp 0.0.0.0/0

# 允许特定安全组访问(sg_app 访问 sg_db 的 3306)
aliyun ecs AuthorizeSecurityGroup \
  --SecurityGroupId sg-db \
  --IpProtocol tcp \
  --PortRange 3306/3306 \
  --SourceGroupId sg-app

NAT 网关(Terraform)

resource "alicloud_nat_gateway" "default" {
  vpc_id       = alicloud_vpc.main.id
  nat_type     = "Enhanced"
  payment_type = "PayAsYouGo"
}

resource "alicloud_snat_entry" "default" {
  snat_table_id     = alicloud_nat_gateway.default.snat_table_ids
  source_vswitch_id = alicloud_vswitch.private.id
  snat_ip           = alicloud_eip.nat.ip_address
}

cloud-init 完整用户数据

#cloud-config
hostname: webserver-01
manage_etc_hosts: true
timezone: Asia/Shanghai

package_update: true
package_upgrade: true
packages:
  - nginx
  - certbot
  - htop
  - fail2ban

write_files:
  - path: /etc/nginx/sites-available/myapp
    content: |
      server {
          listen 80;
          root /var/www/html;
          server_name _;
      }
    permissions: '0644'

runcmd:
  - [systemctl, enable, nginx]
  - [systemctl, start, nginx]
  - [ufw, allow, 22/tcp]
  - [ufw, allow, 80/tcp]
  - [ufw, allow, 443/tcp]
  - [ufw, --force, enable]
  - [ln, -sf, /etc/nginx/sites-available/myapp, /etc/nginx/sites-enabled/]

users:
  - name: deploy
    groups: sudo
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... user@example.com
    sudo: ['ALL=(ALL) NOPASSWD:ALL']

验证 cloud-init 执行状态

cloud-init status
# status: done   ← 全部执行成功
# status: error  ← 部分失败

less /var/log/cloud-init-output.log

# 调试时重新执行
cloud-init clean --logs
cloud-init init

调用元数据服务

# 获取实例 ID
curl -s http://100.100.100.200/latest/meta-data/instance-id

# 获取 VPC ID
curl -s http://100.100.100.200/latest/meta-data/vpc-id

# 获取公网 IP
curl -s http://100.100.100.200/latest/meta-data/public-ipv4

Terraform 完整网络模块(VPC + 子网 + 安全组)

# network.tf —— 一个可复用的基础网络模块
# 阿里云 VPC:逻辑隔离的私有网络,CIDR 一旦创建不可修改
resource "alicloud_vpc" "main" {
  vpc_name   = "prod-vpc"
  cidr_block = "10.0.0.0/16"     # 65536 个 IP,足够中大型集群使用
}

# 多可用区 × 多层子网(用 for_each 避免重复代码)
# 子网绑定可用区 = 故障隔离到机房级别
locals {
  subnets = {
    "web-a" = { zone = "cn-hangzhou-a", cidr = "10.0.1.0/24" }   # Web 层 A 区
    "web-b" = { zone = "cn-hangzhou-b", cidr = "10.0.11.0/24" }  # Web 层 B 区
    "app-a" = { zone = "cn-hangzhou-a", cidr = "10.0.2.0/24" }   # 应用层 A 区
    "app-b" = { zone = "cn-hangzhou-b", cidr = "10.0.12.0/24" }  # 应用层 B 区
    "db-a"  = { zone = "cn-hangzhou-a", cidr = "10.0.3.0/24" }   # 数据库层 A 区(主库)
    "db-b"  = { zone = "cn-hangzhou-b", cidr = "10.0.13.0/24" }  # 数据库层 B 区(备库)
  }
}

# 批量创建子网(for_each 避免每个子网写一遍 resource)
resource "alicloud_vswitch" "this" {
  for_each     = local.subnets
  vpc_id       = alicloud_vpc.main.id
  cidr_block   = each.value.cidr
  zone_id      = each.value.zone
  vswitch_name = each.key
}

# 应用层安全组:只允许来自 Web 安全组的 8080 访问
# 安全组间引用(而非 IP 段)= 网络随资源动态变化而规则不变
resource "alicloud_security_group" "app" {
  name   = "sg-app"
  vpc_id = alicloud_vpc.main.id
}

# 入站规则:源安全组 sg-web → 目标端口 8080
resource "alicloud_security_group_rule" "app_from_web" {
  type              = "ingress"
  ip_protocol       = "tcp"
  port_range        = "8080/8080"
  security_group_id = alicloud_security_group.app.id
  # 引用其他安全组需先定义对应资源(本例仅演示 app 组内互访)
  source_security_group_id = alicloud_security_group.app.id
}

# 用 Terraform 变量实现环境复用(dev/prod 用不同 CIDR)
# variable "env_cidr" { default = "10.0.0.0/16" }
💡 云网络即代码 网络资源(VPC/子网/路由/安全组)的生命周期比实例更长,用 Terraform 管理后:① 环境一键复制(dev/prod 各一套);② 变更可 review(安全组加端口 = 一个 PR);③ 删除环境 = terraform destroy,不会留下孤儿资源。

常见错误

错误后果解决
VPC CIDR 选得过小IP 耗尽无法扩容至少分配 /16,预留 /17 扩展段
CIDR 与 IDC 冲突无法内网互通规划前确认本地及对端网段
安全组出方向全拒绝apt update / yum 超时安全组有状态,出方向至少放行响应
NAT 网关未配 SNAT私有子网无法联网添加 SNAT 条目指向 NAT 的 EIP
cloud-init 格式错误初始化静默跳过cloud-init status --long 查看失败阶段
误用 #! 代替 #cloud-configcloud-init 不识别 YAML用户数据首行必须是 #cloud-config
元数据服务当公网用请求走公网路由超时元数据仅内网可达,走默认路由即可

最佳实践

  • CIDR 规划:为每个环境(dev/staging/prod)分配独立大段(/16),各环境内按层划分子网(/24)并预留至少一段连续大段(/17)供未来扩展
  • 安全组分层:按 Web / App / DB 三层设计安全组,层间通过安全组 ID 而非 IP 段授权;SSH 只放行堡垒机 IP
  • NAT 高可用:生产环境为 NAT 网关附加至少 2 个 EIP 做 SNAT 条目,避免单 IP 被限速或被封
  • cloud-init 幂等:runcmd 内使用 [ -f /flag ] || (command && touch /flag) 模式,保证重复初始化不产生副作用
  • 用户数据分离:将 cloud-init 模板纳入版本控制,配合 Terraform 的 user_data 参数一起管理
  • 元数据走内网:所有通过元数据获取到的信息(如实例 ID)通过日志输出,便于故障时关联工单
  • 避免安全组规则膨胀:定期审计安全组,清理未关联实例的冗余安全组和已废弃的规则

练习题

  1. 规划一个 3 层 Web 应用的 VPC 网络(Web / App / DB),每个层一个子网,CIDR 从 10.0.0.0/16 中分配,并留出 /17 的扩展段
  2. 写一条安全组规则:只允许来自 sg_bastion 的 SSH 访问(端口 22)
  3. 用 cloud-init 在实例首次启动时安装 Docker 并启动一个 nginx 容器
  4. NAT 网关配好了但私有子网仍然无法 apt update,列出至少 3 种排查方向
  5. 通过元数据服务获取当前实例的 vpc-idzone-id,写入 /etc/metadata 文件
  6. 写一条 DNAT 规则将公网 IP 的 8080 端口映射到内网 10.0.1.100 的 80 端口
点击查看答案
  1. 示例规划:Web=10.0.1.0/24, App=10.0.2.0/24, DB=10.0.3.0/24。预留 10.0.128.0/17 为扩展段。
  2. aws ec2 authorize-security-group-ingress --group-id sg-web --protocol tcp --port 22 --source-group sg-bastion(CLI)或云控制台指定源安全组。
  3. user-data:#cloud-config\npackage_update: true\npackages: [docker.io]\nruncmd: [docker run -d -p 80:80 nginx]
  4. 排查方向:① NAT 网关是否绑定 EIP/弹性公网;② 私有子网路由表是否指向 NAT;③ NAT 网关所在公共子网的安全组/ACL 是否放行出站;④ 目标是否在 NAT 支持列表中。
  5. curl -s http://100.100.100.200/latest/meta-data/vpc-id > /etc/metadata && curl -s http://100.100.100.200/latest/meta-data/zone-id >> /etc/metadata(阿里云链路)。
  6. DNAT --to-destination 10.0.1.100:80。云平台配置 DNAT 转发规则映射公网 IP:8080→内网 IP:80。

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释 VPC、子网、路由表、NAT 网关的概念尝试向他人讲解
命令操作能不查文档完成 VPC 网络规划和安全组配置在终端实际执行
原理掌握能说出云网络的流量转发和防火墙过滤原理画出流程图
故障排查能独立排查 VPC 内实例无法互相通信的问题模拟故障并修复
最佳实践能说明为什么需要划分公网子网和私网子网对比不同方案

本章总结

速查表

概念一句话
VPC云上的私有网络,隔离于其他用户
子网VPC 内的 IP 段划分,关联可用区
安全组有状态白名单防火墙,支持安全组级引用
NAT 网关允许私有子网访问互联网(出站)
cloud-init实例首次启动的自动化配置脚本
元数据服务实例查询自身信息的 API(链路本地地址)

云网络的核心思维转变在于:网络不再是物理设备,而是通过 API 创建的虚拟构件。掌握 VPC CIDR 规划、安全组规则设计、NAT 网关共享上网和 cloud-init 自动化配置,是高效使用云资源的基础能力。

上云网络清单

  1. VPC CIDR:选一个不与本地和其他云环境冲突的大段(如 /16)
  2. 子网划分:按层(web/app/db)划分,预留扩展空间
  3. 安全组:最小权限,只放行必要端口,来源限制到指定安全组
  4. NAT 网关:私有子网通过 NAT 正常访问互联网
  5. cloud-init:把服务器初始化自动化,让新 ECS 开箱即用

延伸阅读

常见问题

VPC 网络 CIDR 怎么规划?
私有地址空间:10.0.0.0/8(大型,16M 地址)、172.16.0.0/12(中型,1M 地址)、192.168.0.0/16(小型,65K 地址)。建议:生产环境用 10.x.x.x/16 留够扩展空间。至少分 3 层:公网子网(负载均衡器)、应用子网(后端服务)、数据子网(数据库)。每个子网用 /24(256 地址)。子网之间用安全组和网络 ACL 做隔离。
安全组的设计原则是什么?
最小权限原则:仅开放业务必须的端口。实例粒度的安全组优于网络 ACL(有状态规则,自动处理回程流量)。层次设计:Web 层安全组只允许 80/443 入站,App 层只允许 Web 层安全组访问,DB 层只允许 App 层访问。设置单独的管理安全组(SSH 端口只允许堡垒机 IP)。
cloud-init 能自动完成哪些初始化任务?
cloud-init 是云实例首次启动的自动化配置工具。可自动完成:更新系统包、创建用户和 SSH 密钥、安装软件(apt/yum)、挂载数据盘和格式化文件系统、写入配置文件、设定 hostname 和 DNS、运行自定义脚本(runcmd)。最佳实践:cloud-init 做最小化初始化(账户 + 网络 + 系统设置),应用具体配置交给 Ansible 或 Terraform provisioner。
↑ 回到顶部