01/引言
对于实行多云或混合云策略的企业来说,kOps 是一个理想的 Kubernetes 集群管理工具。它通过同一的设置文件(YAML 或 JSON)实现跨多个云环境(如 AWS、GCP、Azure)或本地数据中心的集群管理。
kOps 提供了丰富的自定义选项,包括控制面节点和工作节点的操作体系、网络插件(如 Calico、Cilium)、存储解决方案等,满足企业在复杂场景下的灵活摆设需求。
对于希望进一步优化 Kubernetes 资源服从的企业,开源的 Kubernetes 集群自动扩展工具 Karpenter 则是一个强盛的选择。
Karpenter 能根据 Pod 的资源需求动态设置节点,支持多种实例类型,并可以智能调理 AWS Spot 实例,大幅降低运行成本。其无节点池的设计,使得资源调理更加灵活,特殊适合需要高弹性和高性价比的场景。
然而,kOps 与 Karpenter 的官方集成已克制更新,仅支持较旧的版本。这意味着在最新版本的 kOps 上,用户需要通过手动设置的方式实现与 Karpenter 的集成。
为了解决这一问题,本文将介绍如何在 kOps 摆设的 AWS Kubernetes 集群上摆设 Karpenter,通过具体步骤和技术细节,资助用户在现有架构中实现动态扩展本领,同时最大化利用 Karpenter 的优势,提升集群的弹性与资源利用服从。
为什么选择 kOps
kOps 是一个开源项目,可以创建、销毁、升级和维护一个高可用的生产级 Kubernetes 集群。该项目的开发者将其形貌为“为集群打造的 Kubectl”。
目前,kOps 主要用于摆设 AWS 和 GCE Kubernetes 集群,对 Azure 的支持处于 alpha 阶段。以下是这款工具的主要特性:
- 自动设置高可用 Kubernetes 集群
- 支持集群滚动更新
- 在命令行中自动补齐命令
- 天生 Terraform 设置
- 以状态同步模子为底子,实现 dry-runs 和自动幂等性(idempotency)
- 创建实例组以支持异构集群
相较其他产物 ,kOps更灵活。通过接纳 kOps,企业可以获得 K8s 集群设置的较大控制权,并且能够自定义 K8s 环境以满足特定需求。还能根据自己的偏好设置云环境,并可以完全访问 master 节点以举行微调和故障清除。
别的,对于小型或临时集群,kOps 每每比其他选择更具成本效益。 作为一种开源和免费的解决方案,企业只需付出底层底子设施的费用。
为什么选择 Karpenter
Karpenter 是当前业界最流行的开源 Kubernetes 集群自动扩展工具之一,最初由 AWS 开源,目前已捐献给 CNCF。当前,Karpenter 支持 AWS、Azure及阿里云,针对 GKE 的支持正在紧锣密鼓地开发中。
与传统的自动扩缩不同,Karpenter 能够动态地在及时环境中为集群工作负载提供所需的计算资源。它通过观察未调理 pod 的资源哀求总量,智能决议并启动精准匹配需求的新节点。
Karpenter 项目地址:
https://github.com/kubernetes-sigs/karpenter
02/前期准备
- 具有 IAM 权限的 AWS 账户, 以创建 EC2 Instance.
- 安装并设置 AWS CLI.
- 安装 Kubernetes CLI (kubectl)
- 安装 Helm(Kubernetes 的包管理工具)
- 安装 kOps
03/通过 kOps 创建集群
设置集群
在创建集群前,您需要设置集群所在的 Region,以及集群的名称。为了简化摆设流程,我们会创建基于 Gossip DNS 的集群。
https://kops.sigs.k8s.io/gossip/
如果您希望利用自己的域名创建集群,您可以参阅下文:
https://kops.sigs.k8s.io/getting_started/aws/#configure-dns
- export DEPLOY_REGION="us-west-1"
- export CLUSTER_NAME="demo1"
- export DEPLOY_ZONE="us-west-1a"
- export NAME=${CLUSTER_NAME}.k8s.loca
复制代码 创建 kOps IAM User
为了在 AWS 中创建集群,我们将利用 AWS CLI 为 kOps 创建一个专用的 IAM 用户 kops。
- aws iam create-group --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonSQSFullAccess --group-name kops
- aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonEventBridgeFullAccess --group-name kops
- aws iam create-user --user-name kops
- aws iam add-user-to-group --user-name kops --group-name kops
- aws iam create-access-key --user-name kops
复制代码 导出 AWS AccessKey/SecretKey
我们需要利用 AccessKey 和 SecretKey,需要将其导出。为了简化摆设,本教程中没有切换用户,您可以手动通过 aws configure 切换到 kops 用户。
- export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id)
- export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key)
复制代码 创建集群状态存储 Bucket
为了保存集群的状态信息以及形貌集群的设置信息,我们需要创建一个专用的 S3 存储桶供 kOps 利用。这个存储桶将成为保存集群设置的唯一可信来源。
- export KOPS_STATE_STORE_NAME=kops-state-store-${CLUSTER_NAME}
- export KOPS_OIDC_STORE_NAME=kops-oidc-store-${CLUSTER_NAME}
- export KOPS_STATE_STORE=s3://${KOPS_STATE_STORE_NAME}
- aws s3api create-bucket \
- --bucket ${KOPS_STATE_STORE_NAME} \
- --region ${DEPLOY_REGION} \
- --create-bucket-configuration LocationConstraint=${DEPLOY_REGION}
- aws s3api create-bucket \
- --bucket ${KOPS_OIDC_STORE_NAME} \
- --region ${DEPLOY_REGION} \
- --create-bucket-configuration LocationConstraint=${DEPLOY_REGION} \
- --object-ownership BucketOwnerPreferred
- aws s3api put-public-access-block \
- --bucket ${KOPS_OIDC_STORE_NAME} \
- --public-access-block-configuration BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false
- aws s3api put-bucket-acl \
- --bucket ${KOPS_OIDC_STORE_NAME} \
- --acl public-read
复制代码 创建集群
以下是创建集群的命令。我们将利用最基本的示例,以下命令将天生集群设置,但不会开始构建它。
- kops create cluster \
- --name=${NAME} \
- --cloud=aws \
- --node-count=1 \
- --control-plane-count=1 \
- --zones=${DEPLOY_ZONE} \
- --discovery-store=s3://${KOPS_OIDC_STORE_NAME}/${NAME}/discovery
复制代码
如今我们进入现实构建集群的末了一步,这需要一段时间。完成后,您将需要等待更长时间,直到启动的实例完成下载 Kubernetes 组件并到达“停当”状态。
- kops update cluster --name ${NAME} --yes --admin
- kops export kubeconfig
- # 等待集群状态 Ready
- kops validate cluster --wait 10m --name ${NAME}
复制代码
04/摆设 Karpenter
准备须要的内容
我们需要这些环境变量来摆设 Karpenter 和创建 NodePool/NodeClass。
通过 AWS CLI 获取 OIDC Provider 的信息、Issuer 地址和 AWS 账户 ID,确保后续摆设正常举行。
- export OIDC_PROVIDER_ID=$(aws iam list-open-id-connect-providers \
- --query "OpenIDConnectProviderList[?contains(Arn, '${NAME}')].Arn" \
- --output text | awk -F'/' '{print $NF}')
- export OIDC_ISSUER=${KOPS_OIDC_STORE_NAME}.s3.${DEPLOY_REGION}.amazonaws.com/${NAME}/discovery/${NAME}
- export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' \
- --output text)
- export AWS_INSTANCE_PROFILE_NAME=nodes.${NAME}
- export KARPENTER_ROLE_NAME=karpenter.kube-system.sa.${NAME}
- export CLUSTER_ENDPOINT=$(kubectl config view -o jsonpath="{.clusters[?(@.name=='${NAME}')].cluster.server}")
- # 储存后续需要的临时文件
- export TMP_DIR=$(mktemp -d)
复制代码 创建 Karpenter IAM Role
我们需要为 Karpenter 创建和设置专用的 IAM Role 和 Policy,答应 Karpenter 通过 OIDC 身份验证该角色,并为该角色添加须要的权限,使其能够动态创建、管理和删除AWS资源(如EC2实例),以满足 Kubernetes 工作负载的需求。
- aws iam create-role \
- --role-name ${KARPENTER_ROLE_NAME} \
- --assume-role-policy-document "{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": {
- "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/oidc.eks.${DEPLOY_REGION}.amazonaws.com/id/${OIDC_PROVIDER_ID}"
- },
- "Action": "sts:AssumeRoleWithWebIdentity",
- "Condition": {
- "StringEquals": {
- "oidc.eks.${DEPLOY_REGION}.amazonaws.com/id/${OIDC_PROVIDER_ID}:sub": "system:serviceaccount:kube-system:karpenter"
- }
- }
- }
- ]
- }"
- aws iam create-role \
- --role-name ${KARPENTER_ROLE_NAME} \
- --assume-role-policy-document "{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": {
- "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${KOPS_OIDC_STORE_NAME}.s3.us-west-1.amazonaws.com/${NAME}/discovery/${NAME}"
- },
- "Action": "sts:AssumeRoleWithWebIdentity",
- "Condition": {
- "StringEquals": {
- "${OIDC_ISSUER}:sub": "system:serviceaccount:kube-system:karpenter"
- }
- }
- }
- ]
- }"
- aws iam put-role-policy \
- --role-name ${KARPENTER_ROLE_NAME} \
- --policy-name InlineKarpenterPolicy \
- --policy-document '{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "ec2:CreateFleet",
- "ec2:CreateTags",
- "ec2:DescribeAvailabilityZones",
- "ec2:DescribeImages",
- "ec2:DescribeInstanceTypeOfferings",
- "ec2:DescribeInstanceTypes",
- "ec2:DescribeInstances",
- "ec2:DescribeLaunchTemplates",
- "ec2:DescribeSecurityGroups",
- "ec2:DescribeSpotPriceHistory",
- "ec2:DescribeSubnets",
- "ec2:RunInstances",
- "ec2:TerminateInstances",
- "iam:PassRole",
- "pricing:GetProducts",
- "ssm:GetParameter",
- "ec2:CreateLaunchTemplate",
- "ec2:DeleteLaunchTemplate",
- "sts:AssumeRoleWithWebIdentity"
- ],
- "Resource": "*"
- }
- ]
- }'
复制代码 摆设 Karpenter
起首,我们需要设置一些额外的内容从而限定 Karpenter 仅在控制面运行,并且绑定和传入我们之前设置的内容,如 clusterEndpoint / clusterName 以及最重要的 IAM Role。
- cat <<EOF > ${TMP_DIR}/values.yaml
- serviceAccount:
- annotations:
- "eks.amazonaws.com/role-arn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/${KARPENTER_ROLE_NAME}"
- replicas: 1
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: node-role.kubernetes.io/control-plane
- operator: Exists
- podAntiAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- - topologyKey: "kubernetes.io/hostname"
- tolerations:
- - key: CriticalAddonsOnly
- operator: Exists
- - key: node-role.kubernetes.io/master
- operator: Exists
- - key: node-role.kubernetes.io/control-plane
- operator: Exists
- - effect: NoExecute
- key: node.kubernetes.io/not-ready
- operator: Exists
- tolerationSeconds: 300
- - effect: NoExecute
- key: node.kubernetes.io/unreachable
- operator: Exists
- tolerationSeconds: 300
- extraVolumes:
- - name: token-amazonaws-com
- projected:
- defaultMode: 420
- sources:
- - serviceAccountToken:
- audience: amazonaws.com
- expirationSeconds: 86400
- path: token
- controller:
- containerName: controller
- image:
- repository: docker.io/vacanttt/kops-karpenter-provider-aws
- tag: latest
- digest: sha256:24ef24de6b5565df91539b7782f3ca0e4f899001020f4c528a910cefb3b1c031
- env:
- - name: AWS_REGION
- value: us-west-1
- - name: AWS_DEFAULT_REGION
- value: us-west-1
- - name: AWS_ROLE_ARN
- value: arn:aws:iam::${AWS_ACCOUNT_ID}:role/${KARPENTER_ROLE_NAME}
- - name: AWS_WEB_IDENTITY_TOKEN_FILE
- value: /var/run/secrets/amazonaws.com/token
- extraVolumeMounts:
- - mountPath: /var/run/secrets/amazonaws.com/
- name: token-amazonaws-com
- readOnly: true
- logLevel: debug
- settings:
- clusterName: ${NAME}
- clusterEndpoint: ${CLUSTER_ENDPOINT}
- featureGates:
- spotToSpotConsolidation: true
- nodeRepair: false
- EOF
复制代码 通过 Helm 摆设 Karpenter 到 kube-system Namespace。
- export KARPENTER_NAMESPACE="kube-system"
- helm upgrade --install karpenter \
- oci://public.ecr.aws/karpenter/karpenter \
- --namespace "${KARPENTER_NAMESPACE}" --create-namespace \
- --wait -f $TMP_DIR/values.yaml
复制代码
创建 Nodepool/NodeClass
我们需要获取 kOps 管理的 LaunchTemplate,并且利用它的 userData 来作为Karpenter EC2NodeClass 的 userData,从而注册新节点到我们的集群中。
- export NODE_INSTANCE_GROUP=$(kops get instancegroups --name ${NAME} | grep Node | awk '{print $1}')
- export NODE_LAUNCH_TEMPLATE_NAME=${NODE_INSTANCE_GROUP}.${NAME}
- export USER_DATA=$(aws ec2 describe-launch-templates --region ${DEPLOY_REGION} --filters Name=launch-template-name,Values=${NODE_LAUNCH_TEMPLATE_NAME} \
- --query "LaunchTemplates[].LaunchTemplateId" --output text | \
- xargs -I {} aws ec2 describe-launch-template-versions --launch-template-id {} --region ${DEPLOY_REGION} \
- --query "LaunchTemplateVersions[].LaunchTemplateData.UserData" --output text | base64 --decode)
复制代码 将 NodeClass 以及 NodePool 暂存,您可以在应用前查抄或是设置其他需要的内容。
- cat <<EOF > ${TMP_DIR}/nodeclass.yaml
- apiVersion: karpenter.k8s.aws/v1
- kind: EC2NodeClass
- metadata:
- name: default
- spec:
- associatePublicIPAddress: true
- amiFamily: AL2
- tags:
- kops.k8s.io/instancegroup: ${NODE_INSTANCE_GROUP}
- KubernetesCluster: ${NAME}
- k8s.io/role/node: "1"
- aws-node-termination-handler/managed: ""
- k8s.io/cluster-autoscaler/node-template/label/node-role.kubernetes.io/node: ""
- subnetSelectorTerms:
- - tags:
- KubernetesCluster: ${NAME}
- securityGroupSelectorTerms:
- - tags:
- Name: nodes.${NAME}
- KubernetesCluster: ${NAME}
- amiSelectorTerms:
- - name: "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20241211"
- instanceProfile: nodes.${NAME}
- userData: |
- $(echo "$USER_DATA" | sed 's/^/ /')
- EOF
- cat <<EOF > ${TMP_DIR}/nodepool.yaml
- apiVersion: karpenter.sh/v1
- kind: NodePool
- metadata:
- name: default
- spec:
- template:
- spec:
- requirements:
- - key: kubernetes.io/arch
- operator: In
- values: ["amd64"]
- - key: kubernetes.io/os
- operator: In
- values: ["linux"]
- - key: karpenter.sh/capacity-type
- operator: In
- values: ["on-demand", "spot"]
- nodeClassRef:
- group: karpenter.k8s.aws
- kind: EC2NodeClass
- name: default
- expireAfter: 720h
- limits:
- cpu: 4
- disruption:
- consolidationPolicy: WhenEmptyOrUnderutilized
- consolidateAfter: 1m
- EOF
复制代码 应用 NodeClass 和 NodePool 到集群中。
- kubectl apply -f ${TMP_DIR}/nodeclass.yaml
- kubectl apply -f ${TMP_DIR}/nodepool.yaml
复制代码 05/后续
创建 Workload 测试自动扩缩容
创建一个 4 Replica,并且哀求一定资源的 Workload。在预期的环境下,会有 2 个 Replica 由于资源不敷而 Pending。
- cat <<EOF > ${TMP_DIR}/workload.yaml
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: workload
- namespace: default
- labels:
- app: workload
- spec:
- replicas: 4
- selector:
- matchLabels:
- app: workload
- template:
- metadata:
- labels:
- app: workload
- spec:
- containers:
- - name: pause
- image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
- resources:
- requests:
- cpu: "550m"
- memory: "128Mi"
- EOF
复制代码- kubectl apply -f ${TMP_DIR}/workload.yaml
复制代码 您可以查抄是否有 NodeClaim 被创建,在 NodeClaim 被创建约 70 秒后,新节点将注册到集群中。
删除集群
在 AWS 中运行 Kubernetes 集群显然需要连续投入成本,因此如果您完成实验后可能需要删除集群。
请留意,此命令具有粉碎性,运行后将删除您的集群及其包含的所有内容!
当您确定要删除集群时,请输入带有--yes标记的删除命令。
- kops delete cluster --name ${NAME} --yes
复制代码
kOps + Karpenter 的优势和局限
kOps 和 Karpenter 的联合为 Kubernetes 集群的自动化管理带来了强盛的功能,但同时也存在一些局限性。
在优势方面,Karpenter 能够根据 Pod 的现实需求动态设置节点,这不但提高了资源利用率,还能快速响应工作负载的厘革,制止资源浪费或不敷。
同时,支持多样化的实例类型,用户可以根据不同的利用场景选择最佳的实例类型,从而进一步优化性能和成本。
然而,这种组合也有局限性。
起首,由于无法利用 EKS 的 bootstrap.sh 脚本,Kubelet 的设置受限于 kOps 的控制,这意味着无法在 NodeClass 中自定义与 Kubelet 干系的参数。
其次,集群的控制面节点必须利用 ASG(自动扩展组)而非 Karpenter,这在一定水平上限定了对控制面节点的弹性管理。
别的,Karpenter 的正常运行需要指定至少一个 InstanceGroup,未设置时节点将无法成功注册到集群,这增长了设置的复杂性。
只管云云,kOps 和 Karpenter 的联合仍旧是一个强有力的工具组合,适合需要动态扩展和多实例支持的场景,但在实行时需要留意这些局限性并做好干系规划。
推荐阅读
CA 不敷用了?Azure 推 Karpenter + Spot,让 AKS 便宜 80%!
逐层优化!网安独角兽如何在 AWS 上削减 60% 的 K8s 成本
新手攻略!手把手教你安装设置 Karpenter
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |