Publish Date

kOpsを使用したAWS Kubernetes上でのKarpenterのデプロイ方法

Details Image

kOpsは、マルチクラウドまたはハイブリッドクラウド環境でKubernetesクラスターをデプロイおよび管理するために広く使用されているツールです。統一された構成システム(YAMLまたはJSON)を提供し、AWS、GCP、Azure、およびオンプレミス環境全体でクラスターを簡単にセットアップできます。

柔軟なカスタマイズオプションにより、kOpsではコントロールプレーンとワーカーノードのオペレーティングシステムからネットワークプラグイン(CalicoCiliumなど)、ストレージソリューションまで、あらゆるものを調整できるため、複雑なセットアップに最適です。

Kubernetesリソースの効率を最適化するために、多くのチームはKarpenterを選択しています。これはワークロードの需要に基づいて動的にノードをプロビジョニングするオープンソースのオートスケーラーです。

複数のインスタンスタイプをサポートし、コスト削減のためにAWS Spotインスタンスをスケジュールし、事前定義されたノードグループの必要性を排除することで、より大きな柔軟性を提供します。

しかし、kOpsはKarpenterの公式サポートを提供していません。つまり、最新バージョンではKarpenterと統合するために手動セットアップが必要です。

このブログでは、kOpsで管理されているAWS Kubernetesクラスターに、Karpenterをデプロイするステップバイステップのプロセスを説明し、自動スケーリングを有効にしてリソース効率を向上させるのに役立ちます。

前提条件

始める前に、以下のものが揃っていることを確認してください:

kOpsでクラスターを作成する

クラスターの設定

クラスターを作成する前に、AWSリージョンとクラスター名を指定する必要があります。デプロイメントを簡素化するために、Gossipベースのクラスターを使用します:

🔗 kOps Gossipドキュメント

クラスター用に独自のドメインを使用したい場合は、公式ガイドに従ってください:

🔗 AWS上のkOps用DNSの設定

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}

kops-update-cluster

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をコントロールプレーンでのみ実行するように制限し、clusterEndpointclusterName、そして最も重要な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

export-karpenter

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秒後、新しいノードがクラスターに登録されます。

get-pods

クラスターの削除

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をフォローしてください。

クラウドでのスマートな節約、
数分で無料で始める

30分のデモで、CloudPilot AIがクラウドコストを削減しながら効率性を高める方法をご紹介します。

デモを予約して今すぐ始める

Cta Image
Cta Image
Footer Logo

自動化されたクラウド節約を解放し、無駄を収益性に変えましょう。

SlackDiscordLinkedInXYoutube
580 California Street, 12th & 16th Floors, San Francisco, California, 94104, USA

Copyright © 2025 CloudPilot AI, Inc. All Rights Reserved.