亚马逊AWS官方博客

在 Amazon EKS 中应用 Amazon Fargate 的使用介绍

概述

从 2019 年底开始,EKS 已经支持 Fargate 功能,Fargate 是无服务器的计算单元,支持 Pod 的运行,EKS 集群的数据平面可以完全由 Fargate 承载,免除了维护 EC2 实例的弹性扩展、打补丁、和管理等方面的运维难题,而且 Kubernetes pod 运行在单独隔离的环境中,增强了安全性,下面示例介绍 EKS Fargate 的使用过程。

整体实验的架构图如下:

架构图说明:

  1. EKS 是 Amazon 托管的 Kubernetes 服务。
  2. Fargate 中运行 metrics-server,aws-load-balancer-controller,coredns,adot-collector 插件,分别用于收集 pod 指标,管理 Amazon ALB 负载均衡服务,DNS 域名解析服务和日志收集。Nginx pod 用于测试。
  3. CloudWatch 服务将收集和管理 pod 日志和 pod 指标。

详细步骤说明

1. 配置本机环境

本机需配置创建 EKS Cluster 的权限,比如具有 AdministratorAccess Policy。

本机中需安装下列工具:

2. 创建 Pod execution IAM role

创建用于 Fargate 的 IAM role,具有下载 ECR 镜像和上传日志到 CloudWatch Logs 的权限。

参考:https://docs.aws.amazon.com/eks/latest/userguide/pod-execution-role.html

https://raw.githubusercontent.com/aws-samples/amazon-eks-fluent-logging-examples/mainline/examples/fargate/cloudwatchlogs/permissions.json

cat <<EOF > role-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "eks-fargate-pods.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF

cat <<EOF > role-inline-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "logs:DescribeLogStreams",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
EOF

aws iam create-role --role-name EKSFargatePodExecRole --assume-role-policy-document file://role-trust-policy.json

aws iam attach-role-policy --role-name EKSFargatePodExecRole --policy-arn arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy

aws iam put-role-policy --role-name EKSFargatePodExecRole --policy-name cloudwatchlogs --policy-document file://role-inline-policy.json

3. 创建 EKS Cluster

下面命令将创建一个 EKS Cluster,包含 3 个 Fargate Profile(default、kube-system和fargate-container-insights),部署在 Kubernetes namespaces(default、kube-system和fargate-container-insights)中的 pod 将运行在 Fargate 上,如果需要支持更多的 namespace,需要新建 Fargate Profile,指定该 namespace。该命令还将创建 1 个 VPC,3 个 public subnet,3 个 private subnet,并且 private subnet 已经配置了通过 NAT Gateway 访问公网。

Fargate Profile 的介绍见:https://docs.aws.amazon.com/eks/latest/userguide/fargate-profile.html

1)获取 Account ID

export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
echo $ACCOUNT_ID

2)创建 EKS 集群

cat <<EOF > eks_cluster.yml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
    name: eks-fargate
    region: us-east-1
    version: "1.23"
vpc:
    clusterEndpoints:
        publicAccess: true
        privateAccess: true
availabilityZones:
     - us-east-1a
     - us-east-1b
     - us-east-1c
cloudWatch:
    clusterLogging:
      enableTypes: 
        - "all"
      logRetentionInDays: 30
fargateProfiles:
    - name: default
      podExecutionRoleARN: arn:aws:iam::${ACCOUNT_ID}:role/EKSFargatePodExecRole
      selectors:
      - namespace: default
    - name: kube-system
      podExecutionRoleARN: arn:aws:iam::${ACCOUNT_ID}:role/EKSFargatePodExecRole
      selectors:
      - namespace: kube-system
    - name: fargate-container-insights
      podExecutionRoleARN: arn:aws:iam::${ACCOUNT_ID}:role/EKSFargatePodExecRole
      selectors:
      - namespace: fargate-container-insights
EOF

eksctl create cluster -f eks_cluster.yml

3)获取 EKS 集群的 VPC 配置

export EKS_VPC_ID=$(aws eks describe-cluster --name eks-fargate --query "cluster.resourcesVpcConfig.vpcId" --output text)
echo $EKS_VPC_ID

4)更新本地 kubeconfig(可选)

aws eks update-kubeconfig --name eks-fargate

5)创建 OIDC Provider,支持 Amazon IAM roles for service accounts 功能

详细介绍见:https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html

操作代码如下:

eksctl utils associate-iam-oidc-provider --cluster=eks-fargate --approve

4. 设置 Pod 的安全组

Fargate/Pod 将默认使用 EKS Cluster 的 Security Group。

下面演示为 Pod 设置额外的安全组。

1)创建 eks-fargate-sg 安全组

export EKS_VPC_CIDR=$(aws ec2 describe-vpcs --vpc-ids ${EKS_VPC_ID} --query "Vpcs[0].CidrBlock"  --output text)

export EKS_SG_ID=$(aws ec2 create-security-group --group-name eks-fargate-sg --description eks-fargate-sg --vpc-id ${EKS_VPC_ID} --query "GroupId"  --output text)

aws ec2 authorize-security-group-ingress \
    --group-id ${EKS_SG_ID} \
    --protocol tcp \
    --port 80 \
    --cidr ${EKS_VPC_CIDR}

2) 获得 EKS Cluster 的 Security Group

export EKS_CLUSTER_SECURITY_GROUP_ID=$(aws eks describe-cluster --name eks-fargate --query cluster.resourcesVpcConfig.clusterSecurityGroupId --output text)
echo $EKS_CLUSTER_SECURITY_GROUP_ID

3)新建 SecurityGroupPolicy

cat <<EOF > pod-security-group-policy.yaml
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
  name: default-security-group-policy
  namespace: default
spec:
  podSelector: 
    matchLabels:
      
  securityGroups:
    groupIds:
      - ${EKS_SG_ID}
      - ${EKS_CLUSTER_SECURITY_GROUP_ID}
EOF

kubectl apply -f pod-security-group-policy.yaml

5. 创建 nginx 测试应用

1)创建 nginx deployment 和 service

cat <<EOF > nginx-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
 namespace: default
spec:
 selector:
  matchLabels:
   app: nginx
 replicas: 3
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxSurge: 2
     maxUnavailable: 0
 template:
  metadata:
   labels:
    app: nginx
  spec:
   containers:
   - name: nginx
     image: nginx:latest
     ports:
     - containerPort: 80
     resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "256Mi"
        cpu: "250m"
EOF

kubectl apply -f nginx-deploy.yml

cat <<EOF > nginx-service.yml
apiVersion: v1
kind: Service
metadata:
  name: "nginx-service"
spec:
  selector:
    app: nginx
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
EOF

kubectl apply -f nginx-service.yml

2)访问 nginx app

创建 test pod,访问 nginx,后面还将用到 test pod,不要退出。

kubectl run -it --rm test --image=alpine -- /bin/sh
wget -q -O- http://nginx-service.default.svc.cluster.local

6. 收集 pod 指标到 CloudWatch

参考:https://aws-otel.github.io/docs/getting-started/container-insights/eks-fargate

1)创建 namespace 和 serviceaccount

kubectl create namespace fargate-container-insights

eksctl create iamserviceaccount \
--cluster=eks-fargate \
--name=adot-collector \
--namespace=fargate-container-insights \
--role-name=EKS-Fargate-ADOT-SA-Role \
--attach-policy-arn=arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
--approve

2)安装 aws-otel-collector 插件

curl -O https://raw.githubusercontent.com/aws-observability/aws-otel-collector/main/deployment-template/eks/otel-fargate-container-insights.yaml

下载的 otel-fargate-container-insights.yaml 文件中需将 YOUR-EKS-CLUSTER-NAME 替换为您的 EKS 集群名称 eks-fargate,将 region=us-east-1 修改成您使用的region。

kubectl apply -f otel-fargate-container-insights.yaml

3)在浏览器中,登陆 AWS Console,进入 CloudWatch 中。

选择菜单栏的 Container Insights,可以看到 Container map,如下图:

选择菜单栏的 All metrics,可以看到 pod 的指标,如 CPU 利用率和内存利用率,如下图:

7. 收集 pod logs 到 CloudWatch

详细配置参考:https://docs.amazonaws.cn/eks/latest/userguide/fargate-logging.html

1)创建 namespace

cat <<EOF > logging-namespace.yml
kind: Namespace
apiVersion: v1
metadata:
  name: aws-observability
  labels:
    aws-observability: enabled
EOF

kubectl apply -f logging-namespace.yml

2)创建 configmap,只保存 nginx app 的日志

cat <<EOF > logging-config.yml
kind: ConfigMap
apiVersion: v1
metadata:
  name: aws-logging
  namespace: aws-observability
data:
  flb_log_cw: "true"  #ships fluent-bit process logs to CloudWatch
  filters.conf: |
    [FILTER]
        Name parser
        Match *
        Key_name log
        Parser crio
    [FILTER]
        Name kubernetes
        Match kube.*
        Merge_Log On
        Keep_Log Off
        Buffer_Size 0
        Kube_Meta_Cache_TTL 300s
  output.conf: |
    [OUTPUT] 
        Name cloudwatch_logs
        Match   kube.var.log.containers.nginx-*
        region us-east-1
        log_group_name eks-fargate-logs
        log_stream_prefix from-fluent-bit-
        log_retention_days 60
        auto_create_group true
  parsers.conf: |
    [PARSER]
        Name crio
        Format Regex
        Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>P|F) (?<log>.*)$
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L%z
EOF

kubectl apply -f logging-config.yml

3)重启 nginx

kubectl rollout restart deployment nginx

4)在上面创建的 test pod 中,访问 nginx

wget -q -O- http://nginx-service.default.svc.cluster.local

5)在 CloudWatch 的 Log groups 中,可以看到 access log

8. 配置 Amazon ALB 负载均衡器

通过负载均衡器,互联网用户可以访问 EKS 集群中的 POD。

参考:https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html

1)配置 Service Account

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json

aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam_policy.json

eksctl create iamserviceaccount \
  --cluster=eks-fargate \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --role-name AmazonLoadBalancerControllerRole \
  --attach-policy-arn=arn:aws:iam::${ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy \
  --approve

2)安装 aws-load-balancer-controller 插件

helm repo add eks https://aws.github.io/eks-charts

helm repo update

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=eks-fargate \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set vpcId=${EKS_VPC_ID}

3)创建 ALB 使用的 Security Group

export EKS_ALB_SG_ID=$(aws ec2 create-security-group --group-name eks-alb-sg --description eks-fargate-sg --vpc-id ${EKS_VPC_ID} --query "GroupId"  --output text)

aws ec2 authorize-security-group-ingress \
    --group-id ${EKS_ALB_SG_ID} \
    --protocol tcp \
    --port 80 \
    --cidr 0.0.0.0/0

4)创建 ingress

将创建 Amazon ALB 负载均衡器,需等待几分钟的 ALB 初始化过程。

cat <<EOF > alb-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: default
  name: nginx
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/security-groups: ${EKS_ALB_SG_ID}
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: nginx-service
              port:
                number: 80
EOF

kubectl apply -f alb-ingress.yml

5)获取 ALB 的地址

export ALB_URL=$(kubectl get ingress nginx -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')
echo $ALB_URL

6)访问 ALB,获得 nginx 的欢迎页

curl $ALB_URL

9. Pod 水平扩展 HPA

参考:https://docs.aws.amazon.com/eks/latest/userguide/horizontal-pod-autoscaler.html

Kubernetes Horizontal Pod Autoscaler 可根据 Pod 负载情况,自动扩张 Pod 数量,新产生的 Pod 大概需要 1 分钟左右时间上线。HPA 依赖 Metrics Server 插件。

1)安装 Metrics Server 插件

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

2)创建 HorizontalPodAutoscaler 对象

下面是创建 HPA 对象的示例,设定了 CPU 利用率的期待指标和收缩策略。

cat <<EOF > hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 3
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 30
      policies:
        - type: Percent
          value: 100
          periodSeconds: 60
EOF

kubectl apply -f hpa.yaml

3)查看 HPA 的状态,由于没有请求压力,nginx 副本将收缩为 1 个。

4)在上面创建的 test pod 中,进行压力测试,nginx 副本将扩展为 3 个。

while sleep 0.001; do wget -q -O- http://nginx-service.default.svc.cluster.local; done

HPA 的输出结果如下:

10. EKS 集群升级

EKS 集群升级后,需要升级线上 EKS Fargate 的版本。

查看 Fargate 版本,是否和 Cluster 版本一致,如果版本不相同,需要重启 Pod,升级 Fargate 版本。

1)获取 Fargate 的版本

kubectl get node

2)重启 K8S Deployment,从而升级 Fargate 版本

kubectl rollout restart deployment nginx
kubectl rollout restart deployment metrics-server -n kube-system
kubectl rollout restart deployment coredns -n kube-system
kubectl rollout restart deployment aws-load-balancer-controller -n kube-system
kubectl rollout restart statefulset adot-collector -n fargate-container-insights

3)升级时,为保障线上的 Pod 数量,可设置升级策略。

参考:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#proportional-scaling

strategy:
   type: RollingUpdate
   rollingUpdate:
     maxSurge: 2
     maxUnavailable: 0

结论

在这篇文章中,介绍了在 EKS 集群中使用 Fargate,包含创建 EKS 集群、监控、日志收集、负载均衡器配置、水平扩展 Pod 和 EKS 集群升级等功能,用户可以参考该文档快速进行集群配置和测试。

参考资料

本篇作者

薛召兵

AWS 解决方案架构师,负责帮助客户进行上云架构的设计和咨询。同时致力于 AWS 容器服务、媒体服务和机器学习服务在国内和全球商业客户的应用和推广,推进企业服务迁移上云进程。有 10 年以上的软件开发、售前技术支持、系统架构设计等经验。