kOpsは、マルチクラウドまたはハイブリッドクラウド環境でKubernetesクラスターをデプロイおよび管理するために広く使用されているツールです。統一された構成システム(YAMLまたはJSON)を提供し、AWS、GCP、Azure、およびオンプレミス環境全体でクラスターを簡単にセットアップできます。
柔軟なカスタマイズオプションにより、kOpsではコントロールプレーンとワーカーノードのオペレーティングシステムからネットワークプラグイン(CalicoやCiliumなど)、ストレージソリューションまで、あらゆるものを調整できるため、複雑なセットアップに最適です。
Kubernetesリソースの効率を最適化するために、多くのチームはKarpenterを選択しています。これはワークロードの需要に基づいて動的にノードをプロビジョニングするオープンソースのオートスケーラーです。
複数のインスタンスタイプをサポートし、コスト削減のためにAWS Spotインスタンスをスケジュールし、事前定義されたノードグループの必要性を排除することで、より大きな柔軟性を提供します。
しかし、kOpsはKarpenterの公式サポートを提供していません。つまり、最新バージョンではKarpenterと統合するために手動セットアップが必要です。
このブログでは、kOpsで管理されているAWS Kubernetesクラスターに、Karpenterをデプロイするステップバイステップのプロセスを説明し、自動スケーリングを有効にしてリソース効率を向上させるのに役立ちます。
前提条件
始める前に、以下のものが揃っていることを確認してください:
- EC2インスタンスを作成するためのIAMアクセス権を持つAWSアカウント。
- AWS CLIがインストールおよび設定されている/
- kubectl(Kubernetes CLI)がインストールされている。
- Helm(Kubernetesパッケージマネージャー)がインストールされている。
- kOpsがインストールされている
kOpsでクラスターを作成する
クラスターの設定
クラスターを作成する前に、AWSリージョンとクラスター名を指定する必要があります。デプロイメントを簡素化するために、Gossipベースのクラスターを使用します:
クラスター用に独自のドメインを使用したい場合は、公式ガイドに従ってください:
export DEPLOY_REGION="us-west-1"
export CLUSTER_NAME="demo1"
export DEPLOY_ZONE="us-west-1a"
export NAME=${CLUSTER_NAME}.k8s.local
kOps IAMユーザーの作成
kOpsでKubernetesクラスターを作成するには、必要な権限を持つ専用のIAMユーザーが必要です。このセクションでは、AWS CLIを使用してkopsという名前のIAMユーザーを作成する手順を説明します。
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アクセスキーとシークレットキーのエクスポート
kOpsをAWSで認証するには、アクセスキーとシークレットキーをエクスポートする必要があります。簡略化のため、このガイドでは明示的にユーザーを切り替えません。以下のコマンドを使用して手動で kOps
IAMユーザーに切り替えることができます:
export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id)
export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key)
クラスター状態用のS3バケットの作成
kOpsはクラスターの状態と設定を保存するための専用のS3バケットを必要とします。このバケットはクラスター管理のための唯一の信頼できる情報源として機能します。
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コンポーネントのダウンロードを終了し、Ready
状態に達するまで待つ必要があります。
kops update cluster --name ${NAME} --yes --admin
kops export kubeconfig
# waiting for Ready
kops validate cluster --wait 10m --name ${NAME}
Karpenterのデプロイ
準備
Karpenterをデプロイする前に、NodePoolとNodeClassを設定するためのいくつかの環境変数を設定する必要があります。
スムーズなデプロイを確保するために、AWS CLIを使用してOIDCプロバイダー情報(発行者URLと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}")
# Storage of temporary documents for subsequent needs
export TMP_DIR=$(mktemp -d)
Karpenter IAMロールの作成
KarpenterがKubernetesワークロードの要件に基づいてAWSリソース(EC2インスタンスなど)を動的に管理できるようにするには、適切なポリシーを持つ専用のIAMロールを作成する必要があります。このロールはOIDC
認証を使用して、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/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ロールなど、以前に設定したリソースにバインドするための追加設定が必要です。
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
Karpenterをkube-system
名前空間にデプロイするには、次のHelmコマンドを使用できます:
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を使用し、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
自動スケーリングをテストするためのワークロードの作成
Karpenterの自動スケーリング機能をテストするために、特定のリソースを要求する4つのレプリカを持つワークロードを作成します。このシナリオでは、リソース不足のため2つのレプリカが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の組み合わせはKubernetesクラスター管理に強力な自動化をもたらしますが、一定の制限も伴います。
利点
Karpenterは実際のPod要件に基づいて動的にノードをプロビジョニングし、リソース使用率を向上させ、ワークロードの変更に迅速に対応できるようにします。これによりリソースの無駄と不足の両方を防ぐことができます。
さらに、幅広いインスタンスタイプをサポートしており、ユーザーはワークロードに最も適したオプションを選択してパフォーマンスとコストを最適化できます。
制限事項
ただし、このセットアップにはいくつかの制約があります。EKSのbootstrap.sh
スクリプトが使用できないため、Kubelet構成はkOpsによって制御され、NodeClass内でのカスタムKubeletパラメータの設定が妨げられます。
さらに、コントロールプレーンノードはKarpenterではなくAuto Scaling Groups(ASG)を通じて管理する必要があり、柔軟性が制限されます。
また、Karpenterが適切に機能するには少なくとも1つのInstanceGroupが必要です—これがないと、新しいノードはクラスターに登録できず、構成の複雑さが増します。
これらの制限にもかかわらず、kOpsとKarpenterは動的スケーリングとマルチインスタンスサポートのための強力な組み合わせであり続けています。
ただし、これらの制約に対処し、スムーズなデプロイメントを確保するには慎重な計画が必要です。
Karpenterに関するさらなるチュートリアルに興味がある場合は、LinkedInでAwesome Karpenterをフォローしてください。