EKS 可销毁环境:集群创建、应用部署到账单归零

本文讲一套用完就拆的 EKS 环境,覆盖版本与权限、成本结构、资源边界、集群与应用部署、销毁顺序、残留复核。
环境:Terraform 1.14.9 + AWS provider 6.x + terraform-aws-eks 21.x,EKS 1.35,kubectl 1.35,区域 us-east-1。

起因:一次练手集群只删了节点组,NAT 网关和两个 ELB 挂了 11 天,账单多出 96 美元。

版本与权限

工具版本先钉死,EKS 相关的返工九成来自版本漂移。

工具 版本 用途
Terraform 1.14.9 建 VPC、EKS、节点组
AWS provider 6.x 资源声明
terraform-aws-eks 21.x 集群与托管节点组模块
Kubernetes 1.35 集群版本
kubectl 1.35 部署与验证
AWS CLI 2.31 残留资源复核
bash 复制代码
terraform version
aws --version
kubectl version --client
aws sts get-caller-identity --query Account --output text

练手环境直接用 AdministratorAccess,跑完连角色一起删,比逐条拼最小权限省事。backend 用 local 就够,state 里只有集群地址和几十个资源 ID,推到远端反而多一层要清的东西。

阶段 需要的权限
建网络 ec2:*Vpc*、ec2:*Subnet*、ec2:*NatGateway*、ec2:AllocateAddress
建集群 eks:*、iam:CreateRole、iam:AttachRolePolicy
建节点组 autoscaling:*、ec2:RunInstances、iam:PassRole
销毁 同创建,外加 ec2:DeleteVpc、logs:DeleteLogGroup

⚠️ EKS 1.33 的标准支持在 2026 年 7 月 29 日结束,新建集群别写 1.33,一上线就落进扩展支持,控制面单价翻倍。

成本结构

哪几行在按小时跳,决定了销毁时要盯什么。

资源 计费口径(us-east-1 按需) 月成本量级 destroy 覆盖
EKS 控制面 $0.10/小时/集群 约 $73 是
托管节点组 m6i.large ×2 $0.096/小时/台 约 $140 是
NAT 网关 0.045/GB 约 $33 是,EIP 除外
ALB / CLB $0.0225/小时 + LCU 约 $18 否
EBS gp3 $0.08/GB·月 按卷算 否
CloudWatch Logs $0.03/GB 存储 按量 否

控制面一创建就计费,跟有没有节点无关;节点组缩到 0 也省不掉这 $73。

价格随区域和合约浮动,列出来只为标出哪几行是每天在跳的。按每天 8 小时算,这套环境跑一周约 60 美元,跑满一个月接近 260 美元。

环境拓扑

公有子网只放 ALB 和 NAT 网关,节点组全在私有子网,出网走单个 NAT。这样节点拿不到公网地址,代价是所有镜像拉取都算 NAT 流量,一个 200MB 的镜像反复拉几次就是几美分。

plain 复制代码
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 6.0"

  name = "${var.cluster_name}-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway   = true
  single_nat_gateway   = true
  enable_dns_hostnames = true

  tags = { Env = "sandbox", TTL = "8h" }
}

single_nat_gateway = true 只建一个 NAT,两个可用区共用,练手环境够用且省一半。

资源边界

销毁失败的根源是把 terraform destroy 当成了全部。

类别 谁创建 谁删除 漏删的代价
VPC、子网、EKS、节点组 Terraform terraform destroy 无
ALB / CLB Service 控制器 kubectl delete svc 每小时 $0.0225 起
EBS 卷 CSI 驱动 kubectl delete pvc $0.08/GB·月
EIP Terraform 分配 aws ec2 release-address 每小时 $0.005
ENI VPC CNI / ELB 随归属资源释放 卡住 VPC 删除
日志组 EKS 控制面 手动删 $0.03/GB 起

destroy 只逆序删 state 里的对象,表里后四行必须单独清,否则账单永远不归零。

这张表决定了销毁脚本要写几行。只跑 destroy 的话,前两行能清掉,后四行会留在账号里继续计费,直到你按标签把它们捞出来。

集群定义

plain 复制代码
terraform {
  required_version = ">= 1.9.0"
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 6.0" }
  }
  backend "local" {}
}

provider "aws" { region = var.region }

variable "region"       { default = "us-east-1" }
variable "cluster_name" { default = "demo-eks" }
plain 复制代码
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 21.0"
  name               = var.cluster_name
  kubernetes_version = "1.35"
  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets
  enable_cluster_creator_admin_permissions = true
  eks_managed_node_groups = {
    default = {
      instance_types = ["m6i.large"]
      capacity_type  = "ON_DEMAND"
      min_size       = 1
      max_size       = 3
      desired_size   = 2
    }
  }

  tags = { Env = "sandbox", TTL = "8h" }
}

kubernetes_version 写主次版本号,补丁版本交给 EKS 自己跟。

创建与接入

bash 复制代码
terraform init -upgrade
terraform plan -out=tfplan
terraform apply tfplan

首次 apply 约 15--20 分钟,控制面占大头。中途 Ctrl+C 会留下半建的控制面,重跑前先 plan 看一眼差异。

latex 复制代码
Apply complete! Resources: 63 added, 0 changed, 0 destroyed.

Outputs:

cluster_endpoint = "https://A1B2C3.gr7.us-east-1.eks.amazonaws.com"
cluster_name     = "demo-eks"
bash 复制代码
aws eks update-kubeconfig --name demo-eks --region us-east-1
kubectl get nodes -o wide
latex 复制代码
NAME                        STATUS   ROLES    AGE   VERSION
ip-10-0-1-24.ec2.internal   Ready    <none>   92s   v1.35.4-eks-1f0ca9d
ip-10-0-2-31.ec2.internal   Ready    <none>   92s   v1.35.4-eks-1f0ca9d

应用部署

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels: { app: web }
  template:
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: web
          image: public.ecr.aws/nginx/nginx:1.27-alpine
          ports:
            - containerPort: 80
          resources:
            requests: { cpu: 100m, memory: 128Mi }
            limits: { cpu: 500m, memory: 256Mi }
yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: LoadBalancer
  selector: { app: web }
  ports:
    - port: 80
      targetPort: 80

type: LoadBalancer 会由控制器在公有子网拉一个 CLB,这个 CLB 不在 state 里,图2 的第①类。镜像用 public.ecr.aws 的 nginx,省掉 imagePullSecret;接私有仓库则要给节点组角色挂 ECR 只读策略,否则 Pod 停在 ImagePullBackOff。

bash 复制代码
kubectl apply -f k8s/web.yaml
kubectl rollout status deploy/web --timeout=180s
kubectl get svc web -w
latex 复制代码
NAME   TYPE           CLUSTER-IP      EXTERNAL-IP                                        PORT(S)
web    LoadBalancer   172.20.11.204   a1b2c3d4e5-1234567890.us-east-1.elb.amazonaws.com   80:31234/TCP

验证

bash 复制代码
LB=$(kubectl get svc web -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
curl -s -o /dev/null -w "%{http_code}\n" "http://$LB"
kubectl get pods -o wide
latex 复制代码
200
NAME                   READY   STATUS    RESTARTS   AGE   IP           NODE
web-6b7c8d9f4b-2xk9p   1/1     Running   0          3m    10.0.1.37    ip-10-0-1-24.ec2.internal
web-6b7c8d9f4b-9wq4n   1/1     Running   0          3m    10.0.2.44    ip-10-0-2-31.ec2.internal

验证只看两件事:Pod 全部 Ready,LB 域名返回 200。缺一件就退回部署,不要进销毁。

销毁顺序

顺序错了会留下删不掉的孤儿资源,按下面五步走。

latex 复制代码
kubectl delete svc web   ──> CLB 释放,约 60 s
kubectl delete pvc --all ──> EBS 卷删除,reclaimPolicy=Delete
kubectl delete ns demo   ──> 剩余对象清空
terraform destroy        ──> state 内 63 个资源逆序删除
aws 复核                  ──> EIP / ENI / 日志组 计数归零
bash 复制代码
kubectl delete svc web --ignore-not-found
kubectl delete pvc --all -n demo --ignore-not-found
sleep 60
terraform destroy -auto-approve

⚠️ 先 destroy 再删 Service 会直接卡死:控制面没了,kubectl 连不上,CLB 也没人回收。

阶段 命令 耗时 失败后果
释放负载均衡 kubectl delete svc 60 s CLB 常驻计费
释放存储 kubectl delete pvc 30 s 卷 in-use 卡住子网删除
删集群 terraform destroy 10--15 min 半删状态难恢复
清残留 aws ec2 describe-* 1 min 账单继续跑

destroy 期间不要关终端。删控制面时会反复轮询,看着像卡住,实际还在跑;中断后 state 里留一批半删对象,下次 apply 直接报冲突。

销毁排障

报错 根因 处理
DependencyViolation: resource sg-xxx has a dependent object 安全组仍被 ENI 引用 按 Name=group-id 反查 ENI,删孤儿网卡
VPC has dependencies and cannot be deleted 子网内还有 ELB、NAT 或 ENI 从 ENI 反查归属资源,逐个删
InvalidParameterException: Cluster has nodegroups attached 节点组未删净 aws eks list-nodegroups 后 delete-nodegroup
VolumeInUse: vol-xxx is still attached Pod 未退或 PVC 未删 先 scale 到 0,再删 PVC
destroy 停在 Still destroying... 20 分钟 控制面删除本身慢 等,中断会留半删状态
EIP 仍计费 NAT 删除后地址未释放 aws ec2 release-address --allocation-id
bash 复制代码
aws ec2 describe-network-interfaces \
  --filters Name=group-id,Values=sg-0123456789abcdef0 \
  --query 'NetworkInterfaces[].[NetworkInterfaceId,Status,Description]' --output table

ENI 反查是解 DependencyViolation 最快的一步,网卡描述里通常直接写着 ELB app/demo/... 或 Amazon EKS。一次反查能捞出两三个孤儿网卡,删完再跑 destroy 基本就通了。

账单复核

bash 复制代码
aws eks list-clusters --query 'length(clusters)'
aws ec2 describe-nat-gateways --filter Name=state,Values=available --query 'length(NatGateways)'
aws ec2 describe-addresses --filters Name=tag:Env,Values=sandbox --query 'length(Addresses)'
aws ec2 describe-volumes --filters Name=status,Values=available --query 'length(Volumes)'
aws logs describe-log-groups --log-group-name-prefix /aws/eks/demo-eks --query 'length(logGroups)'
复核项 期望值 不为 0 时怎么办
EKS 集群数 0 aws eks delete-cluster
NAT 网关数 0 手动 delete-nat-gateway
EIP 数 0 release-address
可用 EBS 卷 0 delete-volume
/aws/eks/demo-eks 日志组 0 logs delete-log-group

EIP 和可用卷建议按 Env=sandbox 标签过滤,账号里有其他资源时全局计数不会归零。五项全 0 才算结束,有一项不为 0 就回到对应的删除命令,别指望下一轮 destroy 补上。

总结

可销毁的核心在顺序:先让 K8s 把自己的云资源收回去,再 destroy,最后按 ENI 反查残留。

检查点 判据
版本 集群版本仍在标准支持期内
标签 所有资源带 TTL 与 Env,方便按标签复核
销毁 先删 Service/PVC,再 destroy
复核 五项计数归零才算结束

参考

相关推荐
每天一道题1 小时前
微服务为什么会变慢?从 QPS、P99 到 Trace 与性能瓶颈
微服务·云原生·架构
时间售卖铺3 小时前
MySQL事务ACID与隔离级别深度解析
运维·数据库·mysql·云原生
云-缩影3 小时前
我用阿里云 AI 动画方案跑通了剧本→视频流水线:ComfyUI + 百炼模型实测
人工智能·阿里云·云计算
每天一道题4 小时前
微服务为什么会发生级联故障?从 Timeout、Retry 到熔断、限流与 Backpressure
微服务·云原生·架构
无心水4 小时前
15、云原生架构设计理论与实践 核心考点与学习笔记
笔记·学习·云原生·架构师·软考·系统架构设计师·软考系统架构设计师
闲云野鹤在人间5 小时前
头秃修炼手册:StorageClass+StatefulSet+ETCD 全套渡劫笔记
linux·数据库·笔记·云原生·kubernetes·etcd
艾伦_耶格宇5 小时前
【kubernetes - 1.28版本】-7 K8S命令
云原生·容器·kubernetes
Z21-X5 小时前
云原生运维笔记:打通OpenStack的任督二脉——从Neutron网络踩坑到Swift与Heat硬核实战
运维·笔记·学习·云原生
你看我名字6 小时前
云计算工程师技能树:从 Linux 基础到云平台架构能力
linux·架构·云计算