亚马逊AWS官方博客

实现 EIP Controller 为 EKS 的 Pod 增加公网访问能力

在当今的技术环境下,越来越多的客户选择使用 Amazon Elastic Kubernetes Service(EKS)来部署和管理容器化的应用程序。在某些场景下,需要 Pod 有公网 IP 地址直接对外提供服务,例如在游戏场景中,游戏服、战斗服通过公网 IP 直接对外连接玩家;在电商平台独立站中,也可以满足独立站对于独享公网 IP 的需求。

对于部署在 Pod 中的应用,为 Pod 自动添加公网 IP 地址(EIP),就可以直接通过 Pod 的公网 IP 访问应用程序。本文实现了一个 Elastic IP Controller,您就可以通过注解的方式自动地将 EIP 附加到 Pod 上。

方案总览

解决方案通过以下步骤为 Pod 处理 EIP:

  1. Informers 通过 List 和 Watch 监听 Pod 事件
  2. Controller 将获取的事件中对应的 Pod key 推送到 WorkQueue
  3. Worker 从 WorkQueue 中获取到 Pod key 并从 Indexer 中获取到 Pod 的相关信息
  4. 根据 Pod 的注解信息,Worker 使用 AWS SDK 为 Pod 分配并关联 EIP 或将 EIP 分离并释放

先决条件

部署指南

创建 EKS 集群

设置当前账户和区域。

export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=<your-region>

注意:使用您的 EKS 集群所在区域进行替换<your-region>。

此命令将同时创建一个名为 main 的节点组,该节点组包含两个 m5.large 类型的实例,并将其部署到公共子网中。

cat << EOF > eip-demo-cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: eip-controller-demo
  region: ${AWS_REGION}
  version: "1.27"

iam:
  withOIDC: true
managedNodeGroups:
  - name: main
    instanceType: m5.large
    desiredCapacity: 2
    privateNetworking: false
EOF
eksctl create cluster -f eip-demo-cluster.yaml
kubectl get nodes

构建镜像并推送到 Amazon Elastic Container Registry

创建 ECR 仓库并登录。

aws ecr create-repository --repository-name aws-pod-eip-controller \
    --region ${AWS_REGION}
aws ecr get-login-password --region ${AWS_REGION} \
    | docker login --username AWS \
    --password-stdin ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

下载示例 Pod EIP controller 代码,构建镜像并推送到 ECR。

git clone https://github.com/aws-samples/aws-pod-eip-controller.git
cd aws-pod-eip-controller
git fetch
git switch blog
docker buildx build \
    --tag ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/aws-pod-eip-controller:latest \
    --platform linux/amd64,linux/arm64 \
    --push .

部署 Pod EIP Controller

创建控制器所需的 IAM 策略。

cat << EOF > iam-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:AllocateAddress",
                "ec2:AssociateAddress",
                "ec2:CreateTags",
                "ec2:ReleaseAddress",
                "ec2:DisassociateAddress",
                "ec2:DeleteTags",
                "ec2:DescribeAddresses",
                "ec2:DescribeNetworkInterfaces"
            ],
            "Resource": "*"
        }
    ]
}
EOF
aws iam create-policy \
    --policy-name AWSPodEIPControllerIAMPolicy \
    --policy-document file://iam-policy.json

创建一个 IAM 角色和 Kubernetes 服务账户供控制器使用。

eksctl create iamserviceaccount \
    --cluster=eip-controller-demo \
    --namespace=kube-system \
    --name=aws-pod-eip-controller \
    --attach-policy-arn=arn:aws:iam::${ACCOUNT_ID}:policy/AWSPodEIPControllerIAMPolicy \
    --override-existing-serviceaccounts \
    --region ${AWS_REGION} \
    --approve

运行以下命令在您的环境中部署 Pod EIP controller。

cat << EOF > myvals.yaml
image: ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/aws-pod-eip-controller
serviceAccountName: aws-pod-eip-controller
logLevel: DEBUG
clusterName: eip-controller-demo
vpcID: ""
region: ""
# set to empty string to watch all namespaces
watchNamespace: nginx-demo-ns
createServiceAccount: false
resyncPeriod: 0
EOF
helm install aws-pod-eip-controller charts/aws-pod-eip-controller \
    -f myvals.yaml \
    -n kube-system
kubectl get deployment -n kube-system

注意:具体配置参数可以在项目 aws-samples/aws-pod-eip-controller 中查看。

这条命令将在 kube-system 命名空间中创建 aws-pod-eip-controller 部署。

使用示例

运行以下命令在您的环境中部署 nginx 应用。

cat << EOF > nginx.demo.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: nginx-demo-ns
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-user
  namespace: nginx-demo-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: nginx-demo-ns
  name: app-nginx-demo
  labels:
    app.kubernetes.io/name: app-nginx-demo
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-nginx-demo
  template:
    metadata:
      labels:
        app: app-nginx-demo
      annotations:
        aws-samples.github.com/aws-pod-eip-controller-type: auto
    spec:
      serviceAccountName: nginx-user
      containers:
      - image: nginx:1.20
        imagePullPolicy: Always
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP
EOF
kubectl apply -f nginx.demo.yaml

运行以下命令查看 Pod 的名字。

kubectl get pods -n nginx-demo-ns

运行以下命令查看附加的弹性 IP。

kubectl get pods <your-pod-name> \
    -o=custom-columns=NAME:.metadata.name,STATUS:.status.phase,PODIP:.status.podIP,EIP:.metadata.labels.aws-pod-eip-controller-public-ip \
    -n nginx-demo-ns \
    -w

注意:需要将您实际的 nginx 的 Pod 的名称进行替换<your-pod-name>。

注意: 在这个 EIP 所在的安全组中,添加 80 端口的访问规则将允许您通过 EIP 访问 Pod。

清理环境

为避免收费,请删除您的 AWS 资源。

kubectl delete -f nginx.demo.yaml

注意:删除该部署的同时也会让控制器释放关联的弹性 IP。

eksctl delete cluster -f eip-demo-cluster.yaml
aws iam delete-policy \
    --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AWSPodEIPControllerIAMPolicy
aws ecr delete-repository \
    --repository-name aws-pod-eip-controller \
    --region ${AWS_REGION} \
    --force

注意:在 eip-demo-cluster.yaml 所在的文件夹中执行命令。

结论

在这篇文章中,您在 EKS 集群中部署了 EIP 控制器。通过监听 Pod 的创建和删除事件,它会为带有特定注释的 Pod 关联和取消关联 EIP。这简化了应用程序访问,Pod 可以直接通过 EIP 访问,而无需额外的负载均衡器或 Ingress 控制器。它实现了自动化操作,注释和控制器方法完全自动化了 EIP 的分配和释放过程,无需人工干预。

本篇作者

黄际东

AWS 解决方案架构师,有过银行、旅游、直播、教育等行业的十多年项目经验。曾独立主导实现从零到千万用户级别的教育类应用,也参与研发过月活跃用户过千万的全球知名直播平台。在互联网领域拥有多年的研发及运维经验,是一个喜欢编程的解决方案架构师。

Peter Reisinger

AWS 专业服务顾问。技能和专长领域包括应用开发、基础设施自动化和安全。支持 AWS 客户部署工作负载,目前与企业客户合作构建精心设计的、成本优化的解决方案。在业余时间,他喜欢骑自行车、跑步以及和两个孩子 Emily 和 Ryan 一起散步。