Amazon Web Services ブログ

Amazon EKS で Multus を使用した Karpenter ノードのデプロイ

この記事は、こちらのブログ「Deploying Karpenter Nodes with Multus on Amazon EKS 」(2024年5月24日公開)を翻訳したものです。

コンテナベースの通信事業者のワークロードでは、トラフィックやネットワークの分離のために Multus CNIをよく使用します。Amazon Elastic Kubernetes Service(Amazon EKS)は Multus CNI をサポートしており、複数のネットワークインターフェイスをアタッチし、Kubernetes ベースのアプリケーションに対して高度なネットワーク構成や分離を適用することができます。AWS でアプリケーションを実行する利点の 1 つは、リソースの弾力性(スケールアウトおよびスケールイン)です。ノードの弾力性は、Karpenter のようなクラスターオートスケーラーを使用することで実現できます。Karpenter は、アプリケーションの需要に応じて最適なコンピュートリソースを自動的に起動します。Karpenter は、迅速かつシンプルな Kubernetes クラスターのプロビジョニングを目的として設計されており、Amazon EKS ノードグループの外部でグループレスのノードをプロビジョニングします。

目的

この投稿では、Multus インターフェイス付きの EKS ノードプールを Karpenter でプロビジョニングされるデプロイモデルを紹介します。このデプロイモデルは、特に通信事業者のワークロードに推奨されますが、これに限定されるものではありません。このモデルは、Karpenter をグループレスのノードプールおよびオートスケーリングソリューションとして活用することを目的としています。グループレスのワーカー(ノードプール)は、Multus CNI を必要とするアプリケーション Pod 用であり、一方でAmazon EKS マネージド型ノードグループのワーカーは、プラグイン、アドオン、および Karpenter 自体など、Multus CNI を必要としないポッドを実行します。この投稿では、Karpenter が複数の Elastic Network Interface(ENI)を持つワーカーのリアルタイムスケーリングを管理し、スケーラビリティとネットワーク分離の厳しい要件を満たす方法も示します。

なぜこのようなデプロイモデルなのか?

Karpenter の主なユースケースは、デプロイ時やスケールアウトイベント時に Kubernetes クラスターのワーカーノードをプロビジョニングすることです。この投稿では、Karpenter のもう一つのユースケースとして、Multus CNI を使用するアプリケーションをホスティングするノードプールをプロビジョニングする方法を紹介します。このデプロイモデルは、アプリケーションのワーカーノードを Amazon EKS マネージド型ノードグループから切り離します。このアプローチの利点は、1)アプリケーションワーカーは特定のインスタンスタイプやサイズに限定されず、インスタンスのタイプやファミリーの幅広い選択肢を利用できること。2)アプリケーションのスケールアウト機能が Amazon Elastic Compute Cloud(Amazon EC2)Auto Scaling グループに結びつかず、Amazon EKS マネージド型ノードグループを通じて Auto Scaling グループを利用する場合よりも、柔軟にスケールできることです。また、EC2 Auto Scaling グループに依存しないため、Karpenter は Amazon EC2 fleet API を直接使用してワーカーノードを迅速にプロビジョニングできます。これはアプリケーションのスケールアウトシナリオにおいてかなり重要です。標準の Karpenter ベースのノードプロビジョニングは、単一の VPC サブネットに接続されたノードを作成するため、Multus CNI をサポートしていません。この投稿では、EC2NodeClass を使用することにより、user-data を活用した ENI 管理を導入することで、Karpenter を介して Multus をサポートするソリューションを提供します。

デプロイ

このセクションで説明される手順の詳細は、こちらの GitHub リポジトリを参照しています。

このデプロイは、Multus CNI を使用したアプリケーション Pod を Karpenter で柔軟に実行できることを示しています。Karpenter を介して Multus 用の ENI を作成およびアタッチするアプローチを適用し、併せて Karpenter のスケーリング機能も実演します。

前提条件

この投稿の手順を進めるには、以下の前提条件が必要です。

環境設定

このセットアップでは、VPC、EKS クラスター、EKS マネージド型ノードグループ、セキュリティグループ、および関連するVPCコンポーネントを作成します。

1. ここでは CloudShell 環境を使用して EKS クラスターを構成し、サンプルアプリケーションをデプロイします。CloudShell コンソールに移動し、この GitHub リポジトリをダウンロードして、必要なツール(awscli、kubectl、eksctl、helmなど)をインストールするための次のスクリプトを実行します。

sudo chmod +x tools/installTools.sh
./tools/installTools.sh

2. テンプレート `vpc-infra-mng.yaml` を使用して AWS CloudFormation スタックを作成します。2つのアベイラビリティゾーン(例:`us-west-2a` および `us-west-2b`)を選択します。スタック名を `karpenterwithmultus` とします(このスタック名は後で CloudShell 環境で使用します)。他のパラメータはデフォルトのままにしておくことができます。次の AWS Command Line Interface (AWS CLI) コマンドを使用するか、AWS マネジメントコンソールの CloudFormation メニューを使用してスタックを作成します。

aws cloudformation create-stack --stack-name karpenterwithmultus --template-body file://cfn-templates/vpc-infra-mng.yaml --parameters ParameterKey=AvailabilityZones,ParameterValue=us-west-2a\\,us-west-2b --region us-west-2 --capabilities CAPABILITY_NAMED_IAM

次の手順を実行する前に、最初の CloudFormation スタック作成が完了していることを確認してください。EKSクラスターとワーカーノードの構築には約15分かかる場合があります。

結果として得られるアーキテクチャは次の図のようになります。

3. CloudShell で、Karpenter バージョン、EKS クラスター名、および AWS リージョンを定義するために環境変数を作成します。パラメータ値を環境に応じて変更して、CloudShell コンソールに移動して次のコマンドを実行します。

export KARPENTER_NAMESPACE=karpenter
export K8S_VERSION=1.29
export KARPENTER_VERSION=v0.32.3
export AWS_PARTITION="aws"
export VPC_STACK_NAME="karpenterwithmultus"
export CLUSTER_NAME="eks-${VPC_STACK_NAME}"
export AWS_DEFAULT_REGION=us-west-2 
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export TEMPOUT=$(mktemp)

注:

`VPC_STACK_NAME`は、CloudFormationテンプレート`vpc-infra-mng.yaml`で指定した名前を使います。
デフォルトのリージョンに注意してください。後のステップで、`nodepool.yaml`にはアベイラビリティゾーン(AZ)を指定します。
この例では CloudShell を admin ノードとして使用します。CloudShell がタイムアウトすると、環境変数が失われます。CloudShell 以外の admin ノードを使用しても問題ないです。

Kubeconfig を更新し、Amazon EKS コントロールプレーンへのアクセスをテストします。

aws eks update-kubeconfig --name $CLUSTER_NAME
kubectl get nodes
kubectl get pods -A

エラーが表示されなければ、K8Sクラスターへのアクセスが確認されます。

プラグインの設定

4. Multus CNI をインストールします。Multus CNI は、Kubernetes 用のコンテナネットワークインターフェースプラグインであり、Pod に複数のネットワークインターフェースをアタッチできます。Multus CNI と VPC CNI が Amazon EKS でどのように機能するかについて理解したい場合は、こちらの Amazon Container の記事を参照してください。

kubectl apply -f https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/master/config/multus/v4.0.2-eksbuild.1/multus-daemonset-thick.yml
kubectl get daemonsets.apps -n kube-system

5. Multus の IP アドレスレンジを専有させるために Multus サブネット用の CIDR 予約が必要です。CIDR 予約で明示的なフラグを設定すると、VPC が ENI などの VPC リソースを作成する際にこれらの CIDR ブロックに使わないようになります。次の CIDR 予約コマンドを Multus サブネットで実行し、/27 の IP レンジを確保します。

Multus1Az1subnetId=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=Multus1Az1-${VPC_STACK_NAME}" --query "Subnets[*].SubnetId" --output text)
aws ec2 create-subnet-cidr-reservation --subnet-id ${Multus1Az1subnetId} --cidr 10.0.4.32/27 --reservation-type explicit --region ${AWS_DEFAULT_REGION}
Multus1Az2subnetId=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=Multus1Az2-${VPC_STACK_NAME}" --query "Subnets[*].SubnetId" --output text)
aws ec2 create-subnet-cidr-reservation --subnet-id ${Multus1Az2subnetId} --cidr 10.0.5.32/27 --reservation-type explicit --region ${AWS_DEFAULT_REGION}
Multus2Az1subnetId=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=Multus2Az1-${VPC_STACK_NAME}" --query "Subnets[*].SubnetId" --output text)
aws ec2 create-subnet-cidr-reservation --subnet-id ${Multus2Az1subnetId} --cidr 10.0.6.32/27 --reservation-type explicit --region ${AWS_DEFAULT_REGION}
Multus2Az2subnetId=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=Multus2Az2-${VPC_STACK_NAME}" --query "Subnets[*].SubnetId" --output text)
aws ec2 create-subnet-cidr-reservation --subnet-id ${Multus2Az2subnetId} --cidr 10.0.7.32/27 --reservation-type explicit --region ${AWS_DEFAULT_REGION} 

注:エラーが発生した場合、例えば`subnet ID doesn’t exist`のエラーが表示された場合は、正しい AWS_DEFAULT_REGION 環境変数が定義されているかどうかを確認してください。

6. Whereabouts プラグインをインストールします。Whereabouts は、前のステップで設定した Multus Pod IP アドレスを管理するために使用する IPAM CNI プラグインです。Whereabouts を使用した Multus Pod IP アドレスは、Custom Resource Definition(CRD)Network-Attachment-Definitions(NAD)を介して定義されます。

kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/whereabouts/master/doc/crds/daemonset-install.yaml
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/whereabouts/master/doc/crds/whereabouts.cni.cncf.io_ippools.yaml
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/whereabouts/master/doc/crds/whereabouts.cni.cncf.io_overlappingrangeipreservations.yaml
kubectl get daemonsets.apps -n kube-system

7. クラスターに NetworkAttachmentDefinitions を適用します。これは、後でアプリケーション Pod を作成するときに、Pod 上の Multus インターフェースのコンフィグになります。このファイルを確認すると、前のステップで設定した CIDR 予約プレフィックスが定義されていることがわかります。

kubectl apply -f sample-application/multus-nad-az1.yaml
kubectl apply -f sample-application/multus-nad-az2.yaml

IAM ロールの設定

8. Karpenter IAM ロールおよび Karpenter に必要なその他の前提条件を作成します。このステップが完了すると、「Successfully created/updated stack – <Karpenter-${CLUSTER_NAME}>」というメッセージが表示されるはずです。

curl -fsSL https://raw.githubusercontent.com/aws/karpenter/"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml  > $TEMPOUT \
&& aws cloudformation deploy \
  --stack-name "Karpenter-${CLUSTER_NAME}" \
  --template-file "${TEMPOUT}" \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides "ClusterName=${CLUSTER_NAME}"

eksctl create iamidentitymapping \
  --username system:node:{{EC2PrivateDNSName}} \
  --cluster "${CLUSTER_NAME}" \
  --arn "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}" \
  --group system:bootstrappers \
  --group system:nodes
  

eksctl utils associate-iam-oidc-provider \
    --cluster "${CLUSTER_NAME}" \
    --approve

eksctl create iamserviceaccount \
  --cluster "${CLUSTER_NAME}" --name karpenter --namespace karpenter \
  --role-name "${CLUSTER_NAME}-karpenter" \
  --attach-policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}" \
  --role-only \
  --approve

export KARPENTER_IAM_ROLE_ARN="arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"

echo $KARPENTER_IAM_ROLE_ARN

9. Multus に必要な AWS Identity and Access Management (IAM) ポリシーを作成し、それを Karpenter ノードロールにアタッチします。EC2NodeClass の userdata セクションには、Karpenter でプロビジョニングされたノードに ENI を作成、アタッチ、およびコンフィグするためのスクリプトが含まれています。ノードには、Karpenter ノードロールに適切なポリシーをアタッチする必要があります。

aws iam create-policy --policy-name karpenter-multus-policy --policy-document file://config/multus-policy.json | jq -r '.Policy.Arn'
karpentermultuspolicyarn=$(aws iam list-policies | jq -r '.Policies[] | select(.PolicyName=="karpenter-multus-policy") | .Arn')
aws iam attach-role-policy --policy-arn $karpentermultuspolicyarn --role-name KarpenterNodeRole-${CLUSTER_NAME}

10.(オプション)スポットインスタンスを使用するためのロールを、下記のコマンドで作成します。

aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true

既にロールが正常に作成されている場合は、次のようなメッセージが表示されます。

# An error occurred (InvalidInput) when calling the CreateServiceLinkedRole operation: Service role name AWSServiceRoleForEC2Spot has been taken in this account, please try a different suffix.

:このステップはオプションであり、Karpenter ノードプールでスポットインスタンスを使用したい場合にのみ必要です。

Karpenter のインストール

11. Karpenter をインストールします。

helm registry logout public.ecr.aws

helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter --create-namespace \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
  --set settings.aws.clusterName=${CLUSTER_NAME} \
  --set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
  --set settings.aws.interruptionQueueName=${CLUSTER_NAME} \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --set controller.resources.limits.cpu=1 \
  --set controller.resources.limits.memory=1Gi \
  --wait

12. Karpenter Pod が `Running` 状態にあるか確認するために次のコマンドを実行します。

kubectl get pods -n karpenter -o wide

13. `nodepool.yaml` ファイルに含まれる Multus サブネットタグ名、AZ、およびセキュリティグループタグ名を更新します。Karpenter ノードプール構成の詳細については、こちらの Karpenter の記事を参照してください。

sed -i "s/##Multus1SubnetAZ1##/Multus1Az1-${VPC_STACK_NAME}/g" config/nodepool.yaml
sed -i "s/##Multus2SubnetAZ1##/Multus2Az1-${VPC_STACK_NAME}/g" config/nodepool.yaml
sed -i "s/##Multus1SubnetAZ2##/Multus1Az2-${VPC_STACK_NAME}/g" config/nodepool.yaml
sed -i "s/##Multus2SubnetAZ2##/Multus2Az2-${VPC_STACK_NAME}/g" config/nodepool.yaml
sed -i "s/##Multus1SecGrpAZ1##/Vpc1SecurityGroup/g" config/nodepool.yaml
sed -i "s/##Multus2SecGrpAZ1##/Vpc1SecurityGroup/g" config/nodepool.yaml
sed -i "s/##Multus1SecGrpAZ2##/Vpc1SecurityGroup/g" config/nodepool.yaml
sed -i "s/##Multus2SecGrpAZ2##/Vpc1SecurityGroup/g" config/nodepool.yaml

`nodepool.yaml` を正しい AZ 名で更新し、次のコマンドを実行します。この例では、`us-west-2a` および `us-west-2b` を使用しています。

AZ1='us-west-2a'
AZ2='us-west-2b'

sed -i "s/##AVAILABILITY_ZONE1##/${AZ1}/g" config/nodepool.yaml
sed -i "s/##AVAILABILITY_ZONE2##/${AZ2}/g" config/nodepool.yaml

次のコマンドを使用して EKS クラスター名を更新します。

sed -i "s/##CLUSTER_NAME##/${CLUSTER_NAME}/g" config/nodepool.yaml

Karpenter ノードプール構成を適用します。

kubectl apply -f config/nodepool.yaml

注:`config/nodepool.yaml` ファイルを確認すると、EC2NodeClass の userdata セクションがカスタマイズされていることがわかります。userdata 内のスクリプトは、Amazon EC2 ノードプールの作成時に MULTUS ENI をプロビジョニング、アタッチ、およびコンフィグします。これはノードプール上の MULTUS ENI LCM を設定するところです。追加のノードチューニングが必要な場合は、userdata セクションに追加することができます。

ノードプールコンフィグにエラーがある場合は、Karpenter コントローラーのログを確認してください。

kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller

14. ノードプール内のノードで Multus DaemonSet Pod とアプリケーション Pod が同時にスケジュールされると競合状態が発生します。この課題を解決するために、Karpenter のノードプールを `StartupTaints` で構成する必要があります。`StartupTaint` は、Multus が準備完了するまでアプリケーション Pod が新しいノードにスケジュールされるのを防ぎます。Multus が準備できたら Taint が削除されます。Taint の削除を自動化するために、この投稿では DaemonSet ベースのソリューションを使用します。

まず、Taint を削除するまたの DaemonSet 用のネームスペースを作成します。

kubectl create namespace cleartaints

DaemonSet `daemonset-clear-taints` がノード上の Taint を削除できるようにするための RBAC コントロールを提供する必要があります。次のコマンドで、`cleartaints` ネームスペースに限定された Service Account 、Service Account Role 、および Role Binding を作成します。

kubectl apply -f config/sa-role-rolebinding.yaml

`cleartaints` というネームスペースで、`daemonset-clear-taints` という名前の DaemonSet を作成して適用します。

kubectl apply -f config/cleartaints.yaml

DaemonSet Pod を確認します。Karpenter ノードプールでのみ実行されるため、この時点では DaemonSet は表示されません。

kubectl get ds -n cleartaints

Node-Latency-For-K8s のデプロイ

15.(オプション)ノードプールのスケールアウト速度に関するデータを収集するために、Node-Latency-For-K8s ソリューションをデプロイできます。Node-Latency-For-K8s は、ノード起動遅延を分析するためのオープンソースツールです。このツールは、ノードがインスタンス化されるから、アプリケーション Pod が実行されるまでの各フェーズでかかった時間を測定します。後のステップではいくつかの例を取り上げます。

export VERSION="v0.1.10"
SCRIPTS_PATH="https://raw.githubusercontent.com/awslabs/node-latency-for-k8s/${VERSION}/scripts"
TEMP_DIR=$(mktemp -d)
curl -Lo ${TEMP_DIR}/01-create-iam-policy.sh ${SCRIPTS_PATH}/01-create-iam-policy.sh
curl -Lo ${TEMP_DIR}/02-create-service-account.sh ${SCRIPTS_PATH}/02-create-service-account.sh
curl -Lo ${TEMP_DIR}/cloudformation.yaml ${SCRIPTS_PATH}/cloudformation.yaml
chmod +x ${TEMP_DIR}/01-create-iam-policy.sh ${TEMP_DIR}/02-create-service-account.sh
${TEMP_DIR}/01-create-iam-policy.sh && ${TEMP_DIR}/02-create-service-account.sh
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export KNL_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-node-latency-for-k8s"
docker logout public.ecr.aws
helm upgrade --install node-latency-for-k8s oci://public.ecr.aws/g4k0u1s2/node-latency-for-k8s-chart \
	--create-namespace \
 	--version ${VERSION} \
  	--namespace node-latency-for-k8s \
	--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KNL_IAM_ROLE_ARN} \
	--set env[0].name="PROMETHEUS_METRICS" \
	--set-string env[0].value=true \
	--set env[1].name="CLOUDWATCH_METRICS" \
	--set-string env[1].value=false \
	--set env[2].name="OUTPUT" \
	--set-string env[2].value=markdown \
	--set env[3].name="NO_COMMENTS" \
	--set-string env[3].value=false \
	--set env[4].name="TIMEOUT" \
	--set-string env[4].value=250 \
	--set env[5].name="POD_NAMESPACE"\
	--set-string env[5].value=default \
	--set env[6].name="NODE_NAME" \
	--set-string env[6].value=\(v1\:spec\.nodeName\)\
	--wait

サンプルアプリケーションのデプロイ

16. サンプルアプリケーションをデプロイします。次のコマンドは、各 AZ に1つのアプリケーションレプリカを持つ Deployment をインストールします。これにより、Karpenter がアプリケーションをスケジュールできるようにノードプールの作成がトリガーされます。

`multitool-deployment-az1.yaml`、`multitool-deployment-az2.yaml` の各ファイルで、正しい AZ 名に更新します。

sed -i "s/##AVAILABILITY_ZONE1##/${AZ1}/g" sample-application/multitool-deployment-az1.yaml
sed -i "s/##AVAILABILITY_ZONE2##/${AZ2}/g" sample-application/multitool-deployment-az2.yaml

kubectl apply -f  sample-application/multitool-deployment-az1.yaml
kubectl apply -f  sample-application/multitool-deployment-az2.yaml

各 Deployment には `karpenter-node` というアフィニティキーがあり、これによりアプリケーション Pod はラベル「`karpenter-node`」が付いたワーカーノードにのみスケジュールされます。Deployment が作成されるときに、Karpenter ノードプールはラベルを割り当てるため、これらの Deployment の Pod をスケジュール/実行するためにノードをスケールします。

次のコマンドを使用して、Karpenter が新しい Amazon EKS ワーカーノードをスケーリングするのを監視します。

kubectl get nodes -o wide

Karpenter のログを確認します。

kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller

Karpenter が新しい EC2 インスタンスを起動し、EKS クラスターに参加し、`Ready` 状態になると、`Pending` 状態のポッドが `Running` 状態に移行します。

kubectl get pods -o wide

次の図のように、Multus インターフェースを持つ Karpenter ワーカーが追加されたアーキテクチャが完成します。

17. 次のコマンドを使用して、アプリケーション Pod が `Running` 状態であることを確認します。

$ kubectl get node
NAME                         STATUS   ROLES    AGE   VERSION
NAME                                       STATUS   ROLES    AGE     VERSION
ip-10-0-2-110.us-west-2.compute.internal   Ready    <none>   8m25s   v1.28.5-eks-5e0fdde
ip-10-0-2-156.us-west-2.compute.internal   Ready    <none>   29m     v1.28.5-eks-5e0fdde
ip-10-0-3-117.us-west-2.compute.internal   Ready    <none>   8m20s   v1.28.5-eks-5e0fdde
ip-10-0-3-146.us-west-2.compute.internal   Ready    <none>   29m     v1.28.5-eks-5e0fdde

$ kubectl get pod
NAME                                  READY   STATUS    RESTARTS   AGE
scaleouttestappaz1-6695b7f878-xjh66   1/1     Running   0          10m
scaleouttestappaz2-7f88f964b6-8sz9r   1/1     Running   0          10m

`kubectl describe pod` または `kubectl exec` を使用して Pod を確認し、Multus インターフェースとアドレスを確認できます。

kubectl describe pod|grep "multus   "
  Normal   AddedInterface    42s   multus             Add eth0 [10.0.2.73/32] from aws-cni
  Normal   AddedInterface    42s   multus             Add net1 [10.0.4.32/24] from default/nad-multussubnet1az1-ipv4
  Normal   AddedInterface    42s   multus             Add net2 [10.0.6.32/24] from default/nad-multussubnet2az1-ipv4
  Normal   AddedInterface    45s   multus             Add eth0 [10.0.3.212/32] from aws-cni
  Normal   AddedInterface    44s   multus             Add net1 [10.0.5.32/24] from default/nad-multussubnet1az2-ipv4
  Normal   AddedInterface    44s   multus             Add net2 [10.0.7.32/24] from default/nad-multussubnet2az2-ipv4

1つのポッドを選択し、次の `exec` コマンドを実行して Multus Pod の IP アドレスを確認します。`net1` および `net2` インターフェースが Multus インターフェースとして表示されます。Amazon EC2 ワーカー上では、Multus ENI はそれぞれ `eth1` および `eth2` と同じサブネットに属しています。

$ kubectl exec scaleouttestappaz1-6695b7f878-xjh66  -- ifconfig net1
net1      Link encap:Ethernet  HWaddr 02:11:D3:5B:D6:6B  
          inet addr:10.0.4.35  Bcast:10.0.4.255  Mask:255.255.255.0
          inet6 addr: fe80::211:d300:15b:d66b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:9 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:450 (450.0 B)  TX bytes:978 (978.0 B)

$ kubectl exec scaleouttestappaz1-6695b7f878-xjh66  -- ifconfig net2
net2      Link encap:Ethernet  HWaddr 02:8B:1B:57:0D:35  
          inet addr:10.0.6.35  Bcast:10.0.6.255  Mask:255.255.255.0
          inet6 addr: fe80::28b:1b00:157:d35/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:51 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:5070 (4.9 KiB)  TX bytes:558 (558.0 B)

:Multus Pod IP アドレスを確認します。これらの IP は、ステップ 6 で定義した NetworkAttachmentDefinitions ファイルで設定した範囲に属しています。

(オプション)Karpenter ノードプールのプロビジョニングにかかる時間を調べるために、Node-Latency-For-K8s のログを新しく作成されたノードで確認できます。`node-latency-for-k8s-node-latency-for-k8s-chart-xxxxx` Pod のログを取得します。例として、ログの取得に約5分かかる場合があります。

$ kubectl -n node-latency-for-k8s logs node-latency-for-k8s-node-latency-for-k8s-chart-7bzvs
2023/12/22 16:57:09 unable to measure terminal events: [Pod Ready]
### i-0cb4baf2a1aa35077 (10.0.3.7) | c6i.2xlarge | x86_64 | us-east-1d | ami-03eaa1eb8976e21a9
|          EVENT           |      TIMESTAMP       |  T  | COMMENT |
|--------------------------|----------------------|-----|---------|
| Pod Created              | 2023-12-22T16:46:30Z | 0s  |         |
| Instance Pending         | 2023-12-22T16:46:41Z | 11s |         |
| VM Initialized           | 2023-12-22T16:46:51Z | 21s |         |
| Network Start            | 2023-12-22T16:46:53Z | 23s |         |
| Network Ready            | 2023-12-22T16:46:54Z | 24s |         |
| Cloud-Init Initial Start | 2023-12-22T16:46:54Z | 24s |         |
| Containerd Start         | 2023-12-22T16:46:54Z | 24s |         |
| Containerd Initialized   | 2023-12-22T16:46:55Z | 25s |         |
| Cloud-Init Config Start  | 2023-12-22T16:46:55Z | 25s |         |
| Cloud-Init Final Start   | 2023-12-22T16:46:55Z | 25s |         |
| Cloud-Init Final Finish  | 2023-12-22T16:47:16Z | 46s |         |
| Kubelet Start            | 2023-12-22T16:47:16Z | 46s |         |
| Kubelet Initialized      | 2023-12-22T16:47:17Z | 47s |         |
| Kubelet Registered       | 2023-12-22T16:47:17Z | 47s |         |
| Kube-Proxy Start         | 2023-12-22T16:47:20Z | 50s |         |
| VPC CNI Init Start       | 2023-12-22T16:47:20Z | 50s |         |
| AWS Node Start           | 2023-12-22T16:47:25Z | 55s |         |
| Node Ready               | 2023-12-22T16:47:27Z | 57s |         |

:起動時に実行される userdata スクリプト(約20-25秒)は、ノードが Ready になるまでの時間に加算されます。userdata スクリプトを使用しない場合、Karpenterノードの Readby になる時間は約30秒です。

(オプション)Multus Pod IP の自動割り当て

Pod への Multus 接続には、Multus IP をワーカーノード上の対応する Multus ENI のセカンダリIPとして割り当てることが重要です。この GitHub リンクを使用して、InitContainer ベースの IP 管理ソリューションまたは Sidecar ベースの IP 管理ソリューションをアプリケーション Pod 内で使用して、Multus IP を自動的に Multus ENI にセカンダリ IP として割り当てることができます。

スケーリング

18. 次のコマンドを使用してスケールアウトを実行し、Karpenter を使用したノードの追加スケーリングをテストし、ノードのスケーリングを監視します。

kubectl scale --replicas=5 deployment/scaleouttestappaz1

kubectl scale --replicas=5 deployment/scaleouttestappaz2

kubectl get nodes -o wide -w

kubectl get pods

19.(オプション)Karpenter のスケールアウト速度に関するデータをもう一度収集します。新しく作成されたノードプールワーカーで `node-latency-for-k8s-node-latency-for-k8s-chart-xxxxx` Pod のログを取得します。

$ kubectl -n node-latency-for-k8s logs node-latency-for-k8s-node-latency-for-k8s-chart-28wsr 
### i-005dfff5abc7be596 (10.0.2.176) | c6i.2xlarge | x86_64 | us-east-1c | ami-03eaa1eb8976e21a9
2023/12/22 17:14:55 unable to measure terminal events: [Pod Ready]
|          EVENT           |      TIMESTAMP       |  T  | COMMENT |
|--------------------------|----------------------|-----|---------|
| Pod Created              | 2023-12-22T17:04:16Z | 0s  |         |
| Instance Pending         | 2023-12-22T17:04:20Z | 4s  |         |
| VM Initialized           | 2023-12-22T17:04:29Z | 13s |         |
| Network Start            | 2023-12-22T17:04:33Z | 17s |         |
| Network Ready            | 2023-12-22T17:04:33Z | 17s |         |
| Cloud-Init Initial Start | 2023-12-22T17:04:33Z | 17s |         |
| Containerd Start         | 2023-12-22T17:04:33Z | 17s |         |
| Cloud-Init Config Start  | 2023-12-22T17:04:34Z | 18s |         |
| Cloud-Init Final Start   | 2023-12-22T17:04:34Z | 18s |         |
| Containerd Initialized   | 2023-12-22T17:04:35Z | 19s |         |
| Cloud-Init Final Finish  | 2023-12-22T17:05:00Z | 44s |         |
| Kubelet Start            | 2023-12-22T17:05:00Z | 44s |         |
| Kubelet Initialized      | 2023-12-22T17:05:00Z | 44s |         |
| Kubelet Registered       | 2023-12-22T17:05:03Z | 47s |         |
| Kube-Proxy Start         | 2023-12-22T17:05:05Z | 49s |         |
| VPC CNI Init Start       | 2023-12-22T17:05:05Z | 49s |         |
| AWS Node Start           | 2023-12-22T17:05:09Z | 53s |         |
| Node Ready               | 2023-12-22T17:05:11Z | 55s |         |
2023/12/22 17:14:55 Serving Prometheus metrics on :2112

20. Karpenter は、`Pending` 状態のポッドの数に応じて必要なノードを自動的にプロビジョニングする柔軟性があります。レプリカの数を増やすことで、スケールインとスケールアウトをもう一度行うことができます。Karpenter がプロビジョニングしたインスタンスタイプとサイズを注目してください。

スケールイン:

kubectl scale --replicas=1 deployment/scaleouttestappaz1
kubectl scale --replicas=1 deployment/scaleouttestappaz2

Karpenter が既存のノードプールを終了するのを待ち、終了したら再度スケールアウトします。この例では、ネットワークアドレス定義で利用可能な IP アドレスの数の制限まで Pod をスケールアウトできます。

kubectl scale --replicas=10 deployment/scaleouttestappaz1
kubectl scale --replicas=10 deployment/scaleouttestappaz2

クリーンアップ

CloudFormation スタックを逆の順序で全て削除します。

kubectl delete -f sample-application/multitool-deployment-az1.yaml
kubectl delete -f sample-application/multitool-deployment-az2.yaml
sleep 120 # to ensure all the karpenter nodes are terminated
kubectl delete -f config/nodepool.yaml 
kubectl delete -f config/cleartaints.yaml
kubectl delete -f config/sa-role-rolebinding.yaml
helm uninstall karpenter -n karpenter
helm uninstall -n node-latency-for-k8s node-latency-for-k8s
aws iam detach-role-policy --policy-arn $karpentermultuspolicyarn --role-name KarpenterNodeRole-${CLUSTER_NAME}
karpentermultuspolicyarn=$(aws iam list-policies | jq -r '.Policies[] | select(.PolicyName=="karpenter-multus-policy") | .Arn')
aws iam delete-policy --policy-arn $karpentermultuspolicyarn

結論

この投稿では、Karpenter を Multus CNI と組み合わせて使用し、Multus が使用する ENI のライフサイクルをどのように管理するかを示しました。Karpenter でプロビジョニングされたノードプールのワーカーには、Multus 対応のインターフェースがアタッチされます。また、Karpenter をオートスケーリングソリューションとして使用する利点も示しました。Karpenter は、Kubernetes クラスターでワークロードを実行する際に、効率とコストを次のように改善します:

  1. Kubernetes スケジューラーが未スケジュールとしてマークしたポッドを監視する。
  2. Pod によって要求されたスケジューリング制約(リソースリクエスト、ノードセレクター、アフィニティ、トレランス、トポロジスプレッド制約)を評価する。
  3. Pod の要件を満たすノードをプロビジョニングする。
  4. ノードが不要になったときにノードを削除する。

Karpenter は、アプリケーションのスケーリング要件に対応するための柔軟なツールです。

Karpenter とベストプラクティスに関する詳細は、AWS GitHub リンクを参照してください。

著者

Rolando Jr Hilvano

Rolando Jr Hilvano is a Principal Solutions Architect in the Worldwide Telecom Business Unit at Amazon Web Services (AWS). He specializes in 5G space and works with telecom partners and customers in building and deploying Cloud Native Telco workloads on AWS.

Ashutosh Tulsi

Ashutosh Tulsi is a Principal Solutions Architect in the AWS Worldwide Telecom Business unit working with Communication Service Providers (CSPs) and Telco ISV Partners. His goal is to enable CSP’s achieve Operational and Cost efficiencies by providing solutions that assist in migrating the 4G / 5G Networks to the AWS Cloud.

Young Jung

Dr. Young Jung is a Principal Solutions Architect in the AWS Worldwide Telecom Business Unit. His primary focus and mission are to help telco Core/RAN partners and customers to design and build cloud-native NFV solutions on AWS environment. He also specializes in Outposts use cases of telco industry for telco’s edge service implementation powered by AWS services and technologies.

Neb Miljanovic

Neb Miljanovic is an AWS Telco Partner Solutions Architect supporting migration of Telecommunication Vendors into public cloud space. He has extensive experience in 4G/5G/IMS Core architecture and his mission is to apply that experience to migration of 4G/5G/IMS Network Functions to AWS using cloud-native principles.

Sudhir Shet

Sudhir Shet is a Principal Partner Solutions Architect in the AWS Global Telecom IBU team, specializes in IMS and 5G, working with various global telecom partners and CSPs to create cloud-native 5G/IMS NFV solutions on AWS.

翻訳はソリューションアーキテクトの陳誠が担当しました。