多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

AWS EKS集群从零创建到生产实践:托管、部署与避坑指南

AWS EKS集群从零创建到生产实践:托管、部署与避坑指南 上个月帮一位朋友搭环境他手里有一批业务要从 EC2 手动部署迁到 Kubernetes团队里没有专职集群运维问我到底是买托管服务还是自己用 kubeadm 搭。我直接建议用 AWS EKS——Amazon Elastic Kubernetes Service。用 EKS 最大的好处是控制面由 AWS 负责apiserver、etcd、scheduler 这些组件不需要你操心你只需要管节点和应用集群本身又是标准 Kubernetes社区工具和生态都能直接用。这篇文章没有废话从零开始带你创建 EKS 集群包括前置准备、ClusterConfig 模板、节点组扩容、kubectl 配置再深入讲 Namespace 隔离、Redis 集群部署、GPU 调用最后把生产环境最容易踩的坑和排查思路一并整理出来。不管你是刚学 K8s 的新手还是在自建集群和托管集群之间犹豫的老手这篇都可以当作一份能直接照做的手册。1. 为什么我推荐用 EKS托管和自建的差别真的巨大1.1 控制面托管省下的时间成本先把概念理清楚。很多人会把 Docker 和 K8s 混在一起问Docker 是容器运行时解决单个容器怎么打包、怎么启动的问题K8s 是容器编排平台解决成千上万个容器怎么调度、怎么保证服务可用性、怎么做滚动更新的问题。AWS EKS 则是 K8s 的托管版本你给它一个 VPC 区域和节点组它就帮你把 K8s 控制面全部拉起来。如果你的集群是自建的需要准备至少三台 master 节点跑 apiserver、controller-manager、scheduler、etcd光一个 etcd 的备份、恢复、高可用就够你喝一壶。控制面的证书过期、etcd 磁盘空间被打满、apiserver 被大流量请求拖垮这些故障每一个都可能把业务环境搞挂。EKS 把这些风险全部承接过去AWS 负责控制面的高可用、证书轮换、etcd 备份你只需要把精力放到节点组和业务负载上。1.2 EKS 集群的组成和账单有哪些EKS 集群从视角上看分成三部分控制面、节点组、附加组件。控制面就是你执行 kubectl 看到的那个 apiserver endpoint它分布在所选区域内至少两个可用区AWS 会自动管理。节点组是真正运行 Pod 的 EC2 实例可以是 Managed Node Group也可以自带节点到集群。附加组件包括 CoreDNS、VPC CNI、kube-proxyEKS 默认会装上也可以自定义版本。计费方面集群本身是每小时收一定的控制面费用具体金额以 AWS 官方价格页为准。节点组里的 EC2 实例按普通的 EC2 计费EBS 卷、EIP 等资源另外算钱。没有免费的 EKS 集群哪怕你一台节点都不加只要控制面在跑它就在计费。所以实验做完一定要记得删除集群否则钱包会替你记仇。1.3 什么场景不适合 EKSEKS 不是银弹。如果你的场景是离线环境、机房自建网络到不了 AWS那显然没法用。如果团队想深入掌握 Kubernetes 的每一层原理控制面被托管之后反而看不到内部细节。还有成本敏感的小实验环境长期跑一个自建的单节点集群成本可能比 EKS 更低。我列一张简单的对比表方便你按需判断对比维度自建 K8sAWS EKS控制面维护自己部署、监控、备份AWS 托管自动高可用初始门槛需要 3 台 master 容器运行时调优eksctl 一条命令拉起学习价值高能彻底理解 K8s 内部机制中等更多关注业务负载生产可用性需要大量运维投入天然设计为生产级别成本只有 EC2 成本但维护成本高每小时控制面费 EC2 成本所以我的观点是如果你是为了快速上线业务EKS 是更划算的选择如果你是为了学习 K8s一定要找个自建集群练练手但别在生产环境拿自建硬扛。2. 创建集群前这些准备工作一个都不能省2.1 工具链安装awscli、eksctl、kubectl创建 EKS 最常用的工具就三个AWS CLI、eksctl、kubectl。很多教程是从 AWS console 点出来的但点控制台容易漏细节而且不好重复执行。我更推荐用 eksctl 命令行的方式它是 AWS 官方推荐的集群创建工具底层用 CloudFormation 帮你生成资源出错的时候可以通过 CloudFormation 日志精确排错。安装方式按你的系统来。macOS 方便一点brew install awscli brew install kubectl brew install eksctlLinux 上如果没有包管理器可以下载二进制丢到 /usr/local/bincurl --silent --location https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz | tar xz -C /tmp sudo mv /tmp/eksctl /usr/local/bin装完顺手验证一下版本aws --version eksctl version kubectl version --client这里有个容易忽略的小坑kubectl 的客户端版本最好不要太老否则有些新的资源字段没法解析。我建议使用和集群版本接近的客户端版本比如集群是 1.30就用 1.30 或更新的 kubectl。2.2 IAM 权限模型和角色EKS 的角色体系分三层集群 IAM 角色、节点实例角色、以及执行 kubectl 的用户权限。集群 IAM 角色是让 EKS 控制面可以调用 AWS API 创建负载均衡器、安全组等资源。这个角色需要信任 eks.amazonaws.com并且绑定 AmazonEKSClusterPolicy。节点实例角色是让 EC2 节点能向控制面注册从 ECR 拉镜像使用弹性网卡和安全组。一般给节点绑定 AmazonEKSWorkerNodePolicy、AmazonEKS_CNI_Policy、AmazonEC2ContainerRegistryReadOnly 这几个托管策略。创建 EKS 的 AWS 用户还需要 eks:CreateCluster、eks:CreateNodegroup、iam:CreateRole、iam:PassRole 等权限。如果你不是管理员账号建议让管理员直接给你一个最小权限的 IAM Policy省得到创建一半报 AccessDenied。用 eksctl 创建集群时它会自动帮你创建第二个角色不需要你去 IAM 手工开。但如果你想用现有角色在 ClusterConfig 里可以指定 iam 配置生产审计要求严格的团队通常这么做。2.3 VPC 和子网设计EKS 对 VPC 的要求比普通 EC2 严格得多。控制面创建的时候你要给它指定至少两个可用区的子网每个子网必须带特定标签不然节点组会注册失败。如果你用 eksctl 默认方式创建它会新建一个 VPC自动分配 CIDR 和子网并且打上正确的标签省心很多。但生产环境通常要用已有的 VPC就必须手动确认子网标签存在kubernetes.io/cluster/eks-demo: owned kubernetes.io/role/elb: 1第一个标签是让 EKS 识别子网归属第二个标签是让 AWS Load Balancer 找到公网子网来部署负载均衡器。如果你只给私有子网跑节点还需要确认 Pod 使用的网段不会和 VPC 网段冲突。默认的 VPC CNI 会给 Pod 分配 VPC 内空闲 IP如果 VPC 地址规划太紧张集群一大就容易出现 IP 耗尽。另外创建 VPC 时要开启 DNS Hostnames 和 DNS Resolution否则节点解析 apiserver 域名会失败这个问题我在排查节点加入失败时遇到不止一次。2.4 账号配额和后付费确认创建集群之前最容易被忽视的是账号配额。比如一个区域最大 EC2 实例数量只有 20如果你选择 3 个节点组、每组 3 台 t3.large可能直接超过配额导致创建失败。还有 VPC 数量、弹性 IP 数量都有默认配额建议提前到 Service Quotas 页面检查。同时确认账号已经正确关联支付方式。EKS 控制面、EC2 节点都是后付费如果账号欠费创建到一半直接被停掉清理起来比创建更麻烦。3. EKS 集群创建实录一条命令并不神秘3.1 编写可复用的 ClusterConfig我习惯用 ClusterConfig 文件管理集群配置而不是直接敲一大堆参数。它像是一份“基础设施即代码”的模板后续改节点组、加 GPU 节点只要改 YAML 再执行就行。下面是一份我在实验环境常用的模板apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: eks-demo region: ap-southeast-1 version: 1.30 vpc: cidr: 192.168.0.0/16 managedNodeGroups: - name: workers instanceType: t3.large desiredCapacity: 3 minSize: 2 maxSize: 6 privateNetworking: true ssh: allow: true publicKeyName: my-key解释几个关键字段version填你希望创建的 K8s 版本创建前先到 EKS 支持的版本列表里确认。vpc.cidr是自定义 VPC 网段如果你不写eksctl 会用默认 192.168.0.0/16。managedNodeGroups表示托管节点组由 EKS 自动管理 Auto Scaling 和节点更新。privateNetworking: true让节点只使用私有子网公网访问通过负载均衡器出去安全性和网络成本都更好。之后执行eksctl create cluster -f cluster.yaml执行过程大约 15 到 20 分钟。它会先创建一个 CloudFormation 栈里面包含 VPC、IAM、控制面和节点组。中途会输出类似waiting for the control plane to become ready的日志。如果创建失败到 CloudFormation 控制台看具体哪一个资源报错是角色权限还是网络配置定位非常直接。3.2 集群创建后的健康检查创建完不要急着部署应用先做一轮基本健康检查。eksctl get cluster --region ap-southeast-1 kubectl get nodes正常情况下你会看到节点处于 Ready 状态。再看控制面相关的 Pod 是否正常kubectl get pods -n kube-systemkube-system 里通常有 aws-node、coredns、kube-proxy这些 Pod 的 READY 应该都是 1/1。如果 coredns 一直 Pending八成是节点资源不足或者安全组阻止了 DNS 流量。这里要说明一下EKS 是托管控制面你不会在 EKS 里看到 kube-controller-manager、etcd 这些 Pod它们运行在 AWS 托管的组件里。所以如果你看到网上有人用一个命令初始化 Kubernetes master然后报出the api server is not healthy after 4m0.00747357s那是在自建 kubeadm 环境里的情况EKS 控制面不会出现这个报错。3.3 节点组扩容和加 GPU 节点业务量涨了怎么办托管节点组支持两种方式扩容手动调整期望数量或者启用 Cluster Autoscaler。手动扩缩容只要一条命令eksctl scale nodegroup --cluster eks-demo --name workers --nodes 5这是把 desiredCapacity 调整为 5。如果你没有指定--nodes-max实际节点组里还能靠 ASG 自动扩张。想加一个 GPU 节点组比如跑推理任务就在 ClusterConfig 里再加一组- name: gpu-workers instanceType: g4dn.xlarge desiredCapacity: 1 minSize: 1 maxSize: 2 privateNetworking: true然后执行eksctl create nodegroup --config-file cluster.yaml这里需要注意GPU 节点组必须使用 EKS 的 GPU 优化 AMIeksctl 默认会选对。如果你想手动给节点打标签可以在 nodeGroup 里加 labels 字段比如accelerator: nvidia后续调度 GPU 任务会用到。3.4 如果创建到一半失败了怎么办创建失败大概率是角色权限、子网标签、配额、以及安全组规则的问题。第一反应应该是看 CloudFormation 事件。aws cloudformation describe-stack-events \ --stack-name eksctl-eks-demo-cluster \ --region ap-southeast-1查看RESOURCE_STATUS_REASON字段里面通常会直接告诉你哪个资源失败比如VPC does not have DNS hostname attributes enabled。修复之后再重新执行创建命令。删除失败的集群不要直接在 console 里东删一个西删一个用命令清理干净eksctl delete cluster --name eks-demo --region ap-southeast-1它会连带删除 CloudFormation 栈里的负载均衡器、数据库避免留下孤儿资源继续计费。4. 从创建到上线部署应用、隔离、Redis 与 GPU4.1 kubectl 配置和验证用 eksctl 创建集群成功之后它会自动把 kubeconfig 写到默认位置但如果你在别的机器上想连接集群可以用 AWS CLI 重新生成aws eks update-kubeconfig --region ap-southeast-1 --name eks-demo --alias eks-demo然后执行kubectl get namespace如果能看到 kube-system、default、kube-public说明连接成功了。在环境变量或 kubeconfig 里设置 context 别名的好处是多个集群切换的时候不会迷路。实际操作中我发现很多人连不通是因为 IAM 用户没有eks:DescribeCluster权限执行 update-kubeconfig 时报 AccessDenied。这个问题只要给用户加上 eks 相关只读权限就行不用太复杂。4.2 用 Namespace 做环境隔离K8s 里很基础也很重要的一个概念就是 Namespace。很多人会把 Namespace 当成环境隔离的唯一手段但它其实是逻辑隔离不是硬隔离。比如你可以创建 dev、staging、prod 三个 Namespace让业务资源各归各的但底层网络和计算资源还是共享的。在 EKS 里创建 Namespace 直接一条命令kubectl create namespace dev kubectl create namespace prod但我更建议用文件方式管理方便审查apiVersion: v1 kind: Namespace metadata: name: dev --- apiVersion: v1 kind: ResourceQuota metadata: name: dev-quota namespace: dev spec: hard: requests.cpu: 4 requests.memory: 8Gi limits.cpu: 8 limits.memory: 16Gi pods: 10ResourceQuota 是 Namespace 隔离的关键没有配额机制一个 Namespace 里的业务很容易把整个集群的资源吃光。我一般会给 dev、staging、prod 配不同的配额再配合 LimitRange 设置默认 requests 和 limits这样哪怕开发者忘写资源请求也不会裸奔。4.3 部署一个 Web 服务并暴露到公网部署一个简单的 Nginx 服务感受一下完整的应用上线路径。先创建 DeploymentapiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo namespace: prod spec: replicas: 3 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - name: nginx image: nginx:1.27-alpine ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi执行kubectl apply -f nginx-demo.yaml之后Service 用 LoadBalancer 类型暴露出来apiVersion: v1 kind: Service metadata: name: nginx-demo-svc namespace: prod spec: selector: app: nginx-demo ports: - port: 80 targetPort: 80 type: LoadBalancerEKS 会自动创建一个 AWS 负载均衡器等kubectl get svc -n prod看到 EXTERNAL-IP 变成实际地址就可以访问了。这个方案适合单个服务暴露如果服务多了建议装 AWS Load Balancer Controller用 Ingress 统一管理路由和 TLS 证书。4.4 在 EKS 上部署 Redis Cluster很多人问 Redis 能不能跑在 K8s 里答案当然能而且 EKS 上跑 Redis Cluster 是很典型的场景。我这里的方案是使用 StatefulSet给每个 Redis 实例一个稳定的网络标识和独立存储卷。三个 master 节点的 Redis Cluster 最小结构是3 个 StatefulSet 副本每个副本配一个 headless service让 Pod 之间可以通过类似redis-0.redis-headless.prod.svc.cluster.local的域名互相发现。关键文件片段apiVersion: apps/v1 kind: StatefulSet metadata: name: redis-cluster namespace: prod spec: serviceName: redis-headless replicas: 6 selector: matchLabels: app: redis-cluster template: metadata: labels: app: redis-cluster spec: containers: - name: redis image: redis:7-alpine command: - /bin/sh - -c - redis-server --cluster-enabled yes --cluster-config-file /data/nodes.conf --cluster-node-timeout 5000 --appendonly yes记得挂载 PVC不然节点重建秒变空库。Redis 的 Cluster 初始化也很有意思需要先用redis-cli --cluster create把所有节点的 IP 或域名加进去形成槽位分配之后才能真正执行 KV 请求。我踩过的坑是 headless service 的端口没有定义 clusterIP: None或者选举用的 port 没有暴露导致节点间找不到彼此。这种问题用redis-cli --cluster check能很快定位到时哪个节点缺席。4.5 让 K8s 成功调用 GPU跑 AI 推理、模型训练核心诉求是让 K8s 的调度器感知 GPU 资源。在 EKS 上要分三步走。第一步创建 GPU 节点组实例类型选择g4dn.xlarge或者更高型号。这个在 3.3 里提到过不再重复。第二步安装 NVIDIA device plugin让节点上的 GPU 数量以扩展资源的形式暴露给 K8s。官方仓库有一条现成的 DaemonSet执行kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.15.0/nvidia-device-plugin.yml安装完成后查看节点资源kubectl describe node g4dn-instance你应该能看到nvidia.com/gpu: 1这样的可调度资源。第三步在应用里声明 GPU 请求resources: limits: nvidia.com/gpu: 1调度器看到这个字段就会把 Pod 调度到有 GPU 的节点上。EKS 的 GPU 节点组一般预装了 NVIDIA 驱动如果你用的是自定义 AMI还需要自己处理驱动和 ec2 实例类型匹配的问题。5. 那些年我踩过的 EKS/K8s 故障坑5.1 API Server “not healthy” 到底是怎么回事很多人在学习 K8s 初始化时会在 kubeadm init 阶段看到这个报错the api server is not healthy after 4m0.00747357s我为什么对这个时间戳印象深因为第一次在 Rocky 系统上自建集群就卡在这里彻底崩了一次。这个问题几乎都和 kubelet 或容器运行时有关特别是 containerd 的配置没跟上对应 K8s 版本的 pause 镜像。常见排查路径是systemctl status kubelet containerd journalctl -xeu kubelet | tail -200 crictl ps -a重点看 containerd 报的image pull failed如果 pause 镜像版本不匹配就把 sandbox_image 改掉再重启 containerd。说实话这类问题让你难受但你解一遍之后对 K8s 控制面的理解会比原来深很多。在 EKS 里不会看到这个错误因为控制面是 AWS 管的。但如果你自己搭的节点访问不到 EKS 的 apiserver会看到节点状态 NotReadykubelet 日志里一直报连接超时。这种时候优先检查安全组节点到 443 端口是否放行再看节点上的 aws-vpc-cni 是否正常。5.2 节点组加入失败的几个隐蔽原因节点组已经创建但节点一直没有 NotReady是最头疼的。我排查过太多现场发现几个高频原因。子网标签缺失是第一大坑。EKS 是靠子网上的kubernetes.io/cluster/cluster-name标签来识别能不能用于集群的。如果你用的是已有 VPC没打标签节点组创建出来了但控制面不认子网自然注册不了。第二个是节点实例角色的权限不够。如果你只绑定了一个AmazonSSMManagedInstanceCore而漏掉了AmazonEKSWorkerNodePolicy节点无法从控制面拿到调度配置就一直卡在初始化。第三个是节点所在的安全组把 kubelet 到 apiserver 的 443 端口挡了。很多企业安全策略默认只开放 80/443/3389结果 kubelet 连接控制面被拦死。解决方法是把控制面安全组加到节点安全组的入方向白名单里。5.3 Pod 起不来、老被驱逐怎么办Pod 启动不起来最典型的场景是 Pending。执行kubectl describe pod的时候Events 部分会把原因写清楚。最常见的是Insufficient cpu/memory说明节点资源不够或者你的 requests 设得太高。还有一种是FailedScheduling因为没有节点满足 nodeSelector、亲和性、或者容忍度要求。比如你部署 GPU 工作负载Pod 明明声明了 nvidia.com/gpu但集群里没有 GPU 节点调度器当然不会给面子。Pod 被驱逐要分情况看待。如果节点本身内存压力高kubelet 会率先驱逐使用量超过 limits 的容器。生产环境里常见的故障就是开发者忘了设置 requests 和 limits某一个容器疯狂吃内存触发整节点 OOM然后所有 Pod 被拖累。解决方式不复杂所有 workload 都要写资源配额再按 HPA 或 Cluster Autoscaler 应对增量。5.4 生产故障怎么影响用户的我的一次真实 CPU 吃满事故有一回我接手一个 EKS 集群线上用户投诉服务白屏打开监控发现节点 CPU 已经无限接近 100%。原因是多个 Java 服务共享一个节点组而它们的 Deployment 全部没有设置 CPU limitsG1 GC 在某些版本下会疯狂占 CPU节点很快被打穿Pod 全部进入 CPUThrottled 状态延迟飙升。当时排查的顺序是先用kubectl top nodes看节点负载再用kubectl top pods -n prod --sort-bycpu找到吃 CPU 的 Pod最后把服务拆到独立的节点组并加了如下配置resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2 memory: 1Gi这个事给我的教训是节点资源不是无限的应用一定要设置 requests 和 limits同时生产集群必须搭配监控和告警像kubectl top只是临场急救真正的容量规划要靠 Prometheus 的数据积累才能提前发现风险。5.5 Rocky 上自建 K8s 的翻车记录这个话题和 EKS 无关但很多人在学习 K8s 时都会经历从 EKS 到自建的跨越所以还是写一下。朋友在 Rocky Linux 上用 kubeadm 安装某个新版本 K8s集群初始化之后同样报了the api server is not healthy。我远程过去看发现 containerd 启动正常但 kubelet 一直看不到静态 Pod最终查出来是 containerd 的 sandbox_image 拉取 pause 镜像失败。解决方法是修改/etc/containerd/config.toml里的sandbox_image换成与当前 K8s 版本兼容的 pause 镜像地址然后重载 containerd清理临时文件再重新 reset 后 init。自建集群就要接受这种时不时折腾一下的事实但这也反过来提醒我EKS 托管控制面的价值不只是省时间更是把这类基础环境的稳定性外包给 AWS 的 SRE。6. 学习 K8s 的资料和环境建议6.1 别只囤 PDF动手练起来网上经常有人找《K8s 权威指南》的 PDF我应该也翻过老版本内容确实系统但这本书再厚也只是帮你建立概念真正学会 K8s 只有一条路自己在真实集群里反复打命令。我建议照着这篇教程建一个 EKS 集群然后立刻开始跑 Deployment、Service、StatefulSet再故意删掉几个 Pod 看看发生了什么体会一下什么叫自愈。做实验的时候可以把每天用到的命令记成学习笔记。比如记录kubectl get events --sort-by.lastTimestamp怎么看调度事件或者kubectl rollout status deployment/nginx-demo怎么等发布完成。这些笔记积累起来比任何一本教程都有用。6.2 把 EKS 理解为 K8s 的“套餐”用 EKS 越久越发现托管服务虽然方便但它只是 You 获得标准 K8s 的一种途径。如果你只会用 eksctl 创建集群不理解 master 节点在做什么遇到奇奇怪怪的网络问题还是会懵圈。我建议学完 EKS 之后用 kubeadm 在虚拟机或者本地环境自建一个单节点集群体验一下证书生成、etcd 启动、kubelet static Pod 这些过程。等碰到 kubeadm init 报not healthy这些错误时别烦躁那是在给你补课。每一类报错背后都隐藏着 K8s 某个机制的原理解决了它你对集群的理解才算真正到位。我现在电脑里还存着第一次创建 EKS 集群时用的 cluster.yaml每次打开都能想起那天晚上反复删槽、重建、等状态的折腾。如果你正准备创建自己的第一个 EKS 集群希望这篇教程能帮你少踩几个坑。记住创建成功只是开始监控、配额、升级演练这些东西越早准备后面越轻松。
返回列表