在使用 AWS Wavelength(移动边缘计算区域)时,手动配置网络环境往往是一个极其繁琐且容易出错的过程。你需要手动开启区域 Opt-in、创建 Carrier Gateway、配置路由表、计算子网 CIDR 防止冲突,还得面对 AWS API 著名的“最终一致性”带来的各种报错。
为了解决这些痛点,我编写了一个运行在 AWS CloudShell 上的全自动 Bash 脚本。它不仅能一键部署所有基础设施,还内置了智能重试和网段冲突避让机制。
痛点:手动配置 Wavelength 有多坑?
如果你尝试过手动配置,你一定遇到过以下问题:
- Gateway ID Not Found: 刚创建完 Carrier Gateway 立刻添加路由,AWS 报错说 ID 不存在(API 同步延迟)。
- 路由添加失败: AWS 强制要求在添加 Carrier Gateway 路由之前,VPC 内必须先存在 Wavelength 子网,顺序搞反直接报错。
- CIDR 网段冲突: 想创建
172.31.200.0/24,发现被占用了,又要手动去算下一个空闲网段。 - 繁琐的操作: 每个 Wavelength Zone 都要重复一遍“创建子网 -> 关联路由表”的操作。
解决方案:全自动部署脚本
这个脚本通过以下核心功能完美解决了上述所有问题:
🚀 脚本核心功能
- 全自动 Opt-in: 自动扫描当前 Region 所有 Wavelength Zone 并启用。
- 智能 VPC 选择: 优先使用默认 VPC,无需手动输入 ID。
- 死磕模式 (Retry): 针对 AWS API 延迟,内置高强度重试机制,直到资源就绪。
- 参数修正: 自动修正
--carrier-gateway-id参数,避免通用网关错误。 - 智能避让 CIDR: 创建子网时如果遇到网段冲突,自动递增网段(如 .200 -> .201 -> .202),直到创建成功。
- 正确顺序: 遵循 AWS 最佳实践:先建网关 -> 先建子网 -> 后建路由,确保 100% 成功率。
脚本代码
在 AWS CloudShell 中创建一个新文件(例如 wl_setup.sh),将以下代码粘贴进去即可:
#!/bin/zsh
# ==========================================
# AWS Wavelength 部署工具 (Zsh 防崩溃版)
# 修复:CreateTags InternalError 导致的脚本中断
# ==========================================
setopt shwordsplit
setopt no_nomatch
set -euo pipefail
# 颜色定义
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
echo -e "${BLUE}======================================================${NC}"
echo -e "${BLUE} AWS Wavelength 部署工具 (防崩溃版) ${NC}"
echo -e "${BLUE}======================================================${NC}"
# 0. 环境检查
REGION="${AWS_REGION:-$(aws configure get region 2>/dev/null || true)}"
if [[ -z "$REGION" ]]; then echo -e "${RED}未检测到 Region。${NC}"; exit 1; fi
echo -e "当前区域: ${GREEN}$REGION${NC}"
# ==========================================
# [1/5] 开启 Wavelength Zone Groups
# ==========================================
echo
echo -e "${YELLOW}[1/5] 扫描并启用 Wavelength 区域...${NC}"
WL_AZS=$(aws ec2 describe-availability-zones \
--region "$REGION" \
--all-availability-zones \
--filters "Name=zone-type,Values=wavelength-zone" \
--query "AvailabilityZones[].ZoneName" \
--output text | tr '\t' '\n' | sort)
if [[ -z "$WL_AZS" ]]; then echo -e "${RED}该区域无 Wavelength 资源。${NC}"; exit 0; fi
WL_GROUPS=$(aws ec2 describe-availability-zones \
--region "$REGION" \
--all-availability-zones \
--filters "Name=zone-type,Values=wavelength-zone" \
--query "AvailabilityZones[].GroupName" \
--output text | tr '\t' '\n' | sort -u)
for group in $WL_GROUPS; do
echo -n "启用组 $group ... "
aws ec2 modify-availability-zone-group --group-name "$group" --opt-in-status opted-in --region "$REGION" >/dev/null 2>&1 || true
echo -e "${GREEN}OK${NC}"
done
echo -n "⏳ 等待区域生效 (15秒)... "
sleep 15
echo -e "${GREEN}继续${NC}"
# ==========================================
# [2/5] 自动选择 VPC
# ==========================================
echo
echo -e "${YELLOW}[2/5] 获取 VPC 信息...${NC}"
VPC_INFO=$(aws ec2 describe-vpcs --region "$REGION" --query "Vpcs[].[VpcId, IsDefault, CidrBlock]" --output text)
if [[ -z "$VPC_INFO" ]]; then echo -e "${RED}无 VPC。${NC}"; exit 1; fi
TARGET_VPC=$(echo "$VPC_INFO" | grep "True" | head -n 1 || true)
if [[ -z "$TARGET_VPC" ]]; then
TARGET_VPC=$(echo "$VPC_INFO" | head -n 1)
fi
VPC_ID=$(echo "$TARGET_VPC" | awk '{print $1}')
VPC_CIDR=$(echo "$TARGET_VPC" | awk '{print $3}')
echo -e "选中 VPC: ${GREEN}$VPC_ID${NC} (CIDR: $VPC_CIDR)"
CIDR_PREFIX=$(echo "$VPC_CIDR" | cut -d'.' -f1,2)
# ==========================================
# [3/5] 配置 Carrier Gateway
# ==========================================
echo
echo -e "${YELLOW}[3/5] 配置 Carrier Gateway${NC}"
EXISTING_CAGW=$(aws ec2 describe-carrier-gateways --filters "Name=vpc-id,Values=$VPC_ID" "Name=state,Values=available" --region "$REGION" --query "CarrierGateways[0].CarrierGatewayId" --output text)
if [[ "$EXISTING_CAGW" != "None" && -n "$EXISTING_CAGW" ]]; then
CAGW_ID="$EXISTING_CAGW"
echo -e "✅ 复用网关: ${GREEN}$CAGW_ID${NC}"
else
CAGW_ID=$(aws ec2 create-carrier-gateway --vpc-id "$VPC_ID" --region "$REGION" --query "CarrierGateway.CarrierGatewayId" --output text)
aws ec2 create-tags --resources "$CAGW_ID" --tags Key=Name,Value="Auto-Wavelength-CAGW" --region "$REGION"
echo -e "✅ 创建网关: ${GREEN}$CAGW_ID${NC}"
fi
echo -n "⏳ 等待网关就绪..."
while true; do
STATE=$(aws ec2 describe-carrier-gateways --carrier-gateway-ids "$CAGW_ID" --region "$REGION" --query "CarrierGateways[0].State" --output text)
if [[ "$STATE" == "available" ]]; then echo -e " ${GREEN}OK!${NC}"; break; else echo -n "."; sleep 2; fi
done
# ==========================================
# [4/5] 配置路由表
# ==========================================
echo
echo -e "${YELLOW}[4/5] 配置路由表${NC}"
RT_ID=$(aws ec2 describe-route-tables --filters "Name=vpc-id,Values=$VPC_ID" "Name=tag:Name,Values=Wavelength-RouteTable-Auto" --region "$REGION" --query "RouteTables[0].RouteTableId" --output text)
if [[ "$RT_ID" != "None" && -n "$RT_ID" ]]; then
echo -e "复用路由表: ${GREEN}$RT_ID${NC}"
else
RT_ID=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --region "$REGION" --query "RouteTable.RouteTableId" --output text)
aws ec2 create-tags --resources "$RT_ID" --tags Key=Name,Value="Wavelength-RouteTable-Auto" --region "$REGION"
echo -e "✅ 创建路由表: ${GREEN}$RT_ID${NC}"
fi
# ==========================================
# [5/5] 创建子网并配置
# ==========================================
echo
echo -e "${YELLOW}[5/5] 创建子网并关联路由...${NC}"
CURRENT_OCTET=200
for az in $WL_AZS; do
echo -e " ----------------------------------------"
echo -e " 正在处理区域: ${BLUE}$az${NC}"
TARGET_SUBNET=""
EXISTING_SUBNET=$(aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=$VPC_ID" "Name=availability-zone,Values=$az" \
--region "$REGION" \
--query "Subnets[0].SubnetId" --output text)
if [[ "$EXISTING_SUBNET" != "None" && -n "$EXISTING_SUBNET" ]]; then
echo -e " ✅ 已存在子网: $EXISTING_SUBNET (复用)"
TARGET_SUBNET="$EXISTING_SUBNET"
else
ATTEMPTS=0
MAX_ATTEMPTS=20
SUCCESS=false
while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do
NEW_CIDR="${CIDR_PREFIX}.${CURRENT_OCTET}.0/24"
if [ $CURRENT_OCTET -gt 254 ]; then CURRENT_OCTET=100; NEW_CIDR="${CIDR_PREFIX}.${CURRENT_OCTET}.0/24"; fi
echo -n " 🛠 尝试创建网段 $NEW_CIDR ... "
CREATE_OUT=$(aws ec2 create-subnet \
--vpc-id "$VPC_ID" \
--cidr-block "$NEW_CIDR" \
--availability-zone "$az" \
--region "$REGION" \
--output json 2>/dev/null || echo "ERROR")
if [[ "$CREATE_OUT" == "ERROR" ]]; then
echo -e "${YELLOW}失败 -> 重试...${NC}"
CURRENT_OCTET=$((CURRENT_OCTET+1))
ATTEMPTS=$((ATTEMPTS+1))
sleep 1
else
TARGET_SUBNET=$(echo "$CREATE_OUT" | grep '"SubnetId":' | cut -d'"' -f4)
echo -e "${GREEN}成功! ($TARGET_SUBNET)${NC}"
# === 关键修复:添加等待 + 忽略标签错误 ===
sleep 2
SHORT_AZ=${az##*-}
aws ec2 create-tags --resources "$TARGET_SUBNET" --tags Key=Name,Value="Auto-Subnet-$SHORT_AZ" --region "$REGION" 2>/dev/null || true
CURRENT_OCTET=$((CURRENT_OCTET+1))
SUCCESS=true
break
fi
done
if [ "$SUCCESS" = false ]; then
echo -e " ${RED}❌ 严重错误: 无法创建子网。${NC}"
continue
fi
fi
if [[ -n "$TARGET_SUBNET" ]]; then
echo -n " 🔗 关联路由表... "
aws ec2 associate-route-table --route-table-id "$RT_ID" --subnet-id "$TARGET_SUBNET" --region "$REGION" >/dev/null 2>&1 || true
echo -e "${GREEN}完成${NC}"
fi
done
# ==========================================
# 最后一步:添加 Carrier Gateway 路由
# ==========================================
echo
echo -e "${YELLOW}检查并添加最终路由...${NC}"
CURRENT_ROUTE_GW=$(aws ec2 describe-route-tables --route-table-id "$RT_ID" --region "$REGION" --query "RouteTables[0].Routes[?DestinationCidrBlock=='0.0.0.0/0'].GatewayId" --output text)
if [[ "$CURRENT_ROUTE_GW" != "None" && -n "$CURRENT_ROUTE_GW" && "$CURRENT_ROUTE_GW" != "$CAGW_ID" ]]; then
echo -e "${YELLOW}清理旧路由...${NC}"
aws ec2 delete-route --route-table-id "$RT_ID" --destination-cidr-block 0.0.0.0/0 --region "$REGION" 2>/dev/null || true
fi
OUTPUT=$(aws ec2 create-route \
--route-table-id "$RT_ID" \
--destination-cidr-block 0.0.0.0/0 \
--carrier-gateway-id "$CAGW_ID" \
--region "$REGION" 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] || echo "$OUTPUT" | grep -q "RouteAlreadyExists"; then
echo -e "✅ 路由添加成功 (0.0.0.0/0 -> $CAGW_ID)"
else
echo -e "${RED}❌ 路由添加失败: $OUTPUT ${NC}"
fi
echo
echo -e "${BLUE}======================================================${NC}"
echo -e "${GREEN}🎉 全部配置完成!(防崩溃版)${NC}"
echo -e "${BLUE}======================================================${NC}"
使用方法
- 登录 AWS 控制台,点击右上角的 CloudShell 图标。
- 输入命令
nano wl_setup.sh创建文件。 - 将上方代码粘贴进去,按
Ctrl+O保存,Ctrl+X退出。 - 赋予执行权限:
chmod +x wl_setup.sh。 - 运行脚本:
./wl_setup.sh。
脚本运行结束后,你可以直接在 EC2 启动向导中选择名称为 Auto-Subnet-... 的子网,该子网已自动连接到运营商网络 (Carrier Network),可直接用于获取运营商 IP。此脚本也可通过AWS CLI使用。