メインコンテンツまでスキップ

クラウドバースティング

前回のデプロイを基に、「クラウドバースティング」のユースケースをシミュレートするシナリオを探ってみましょう。これにより、EKSハイブリッドノードで実行されているワークロードがピーク需要時に弾力的なクラウドキャパシティを活用して、EC2ノードに「バースト」する方法を実証します。

前回の例と同様に、nodeAffinityを使用してハイブリッドノードを優先する新しいワークロードをデプロイします。preferredDuringSchedulingIgnoredDuringExecution戦略は、スケジューリング時にはハイブリッドノードを_優先_するが、実行中は_無視_するようKubernetesに指示します。 これは、単一のハイブリッドノードに空きがなくなった場合、これらのポッドはクラスタ内の他の場所、つまりEC2インスタンスに自由にスケジュールされることを意味します。これは素晴らしいことです!これにより、私たちが望んでいたクラウドバースティングが実現されます。しかし、 _IgnoredDuringExecution_の部分は、スケールダウン時にKubernetesがランダムにポッドを削除し、それが実行されている場所を気にしないことを意味します。これは_実行中は無視される_からです。一般的に、Kubernetesは古いポッドから削除します。これは最初にハイブリッドノード上で実行されているポッドになります。私たちはそれを望みません!

KubernetesのポリシーエンジンであるKyvernoをデプロイします。Kyvernoは、ハイブリッドノード(eks.amazonaws.com/compute-type: hybridというラベルが付けられている)にスケジュールされるポッドを監視し、実行中のポッドにアノテーションを追加するポリシーを設定します。 controller.kubernetes.io/pod-deletion-cost アノテーションは、Kubernetesに対して、最初に_コスト_の低いポッドを削除するように指示します。

さっそく取り組んでみましょう。Helmを使用してKyvernoをインストールし、以下に含まれるポリシーをデプロイします:

~$helm repo add kyverno https://kyverno.github.io/kyverno/
~$helm install kyverno kyverno/kyverno --version 3.3.7 -n kyverno --create-namespace -f ~/environment/eks-workshop/modules/networking/eks-hybrid-nodes/kyverno/values.yaml
 

以下のClusterPolicyマニフェストは、EKSハイブリッドノードインスタンスにランディングするポッドを監視し、pod-deletion-costアノテーションを追加するようにKyvernoに指示します。

~/environment/eks-workshop/modules/networking/eks-hybrid-nodes/kyverno/policy.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: set-pod-deletion-cost
annotations:
policies.kyverno.io/title: Set Pod Deletion Cost
policies.kyverno.io/category: Pod Management
policies.kyverno.io/severity: medium
policies.kyverno.io/description: >-
Sets pod-deletion-cost label on nginx pods scheduled to hybrid compute nodes.
spec:
rules:
- name: set-deletion-cost-for-nginx-on-hybrid
match:
any:
- resources:
kinds:
- Pod/binding
context:
- name: node
variable:
jmesPath: request.object.target.name
default: ""
- name: computeType
apiCall:
urlPath: "/api/v1/nodes/{{node}}"
jmesPath: metadata.labels."eks.amazonaws.com/compute-type" || 'empty'
preconditions:
all:
- key: "{{ computeType }}"
operator: Equals
value: hybrid
mutate:
targets:
- apiVersion: v1
kind: Pod
name: "{{ request.object.metadata.name }}"
namespace: "{{ request.object.metadata.namespace }}"
patchStrategicMerge:
metadata:
annotations:
controller.kubernetes.io/pod-deletion-cost: "1"
A

Pod/bindingリソースを監視し、ポッドがノードにスケジュールされた時点で

B

アドミッションレビューリクエストから対応する値でnode変数を設定

C

Kubernetes APIにクエリを実行して、ポッドがスケジュールされたノードに関する情報からcomputeType変数を設定

D

'hybrid'ノードにスケジュールされたポッドのみを選択

E

ポッドを変更してpod-deletion-costアノテーションを追加

Kyvernoが稼働していることを確認し、ポリシーを適用しましょう:

~$kubectl wait --for=condition=Ready pods --all -n kyverno --timeout=2m
~$kubectl apply -f ~/environment/eks-workshop/modules/networking/eks-hybrid-nodes/kyverno/policy.yaml

次に、サンプルワークロードをデプロイします。これは、前述のnodeAffinityルールを使用して、3つのnginxポッドをハイブリッドノードにデプロイします:

~$kubectl apply -f ~/environment/eks-workshop/modules/networking/eks-hybrid-nodes/deployment.yaml
~/environment/eks-workshop/modules/networking/eks-hybrid-nodes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: eks.amazonaws.com/compute-type
operator: In
values:
- hybrid
containers:
- name: nginx
image: public.ecr.aws/nginx/nginx:1.26
resources:
requests:
cpu: 200m
limits:
cpu: 200m
ports:
- containerPort: 80

そのデプロイメントがロールアウトした後、すべてがハイブリッドノードにデプロイされた3つのnginx-deploymentポッドが表示されます。ノードとアノテーションを一度に見ることができるように、kubectlからのカスタム出力を使用しています。Kyvernoがpod-deletion-costアノテーションを適用したことがわかります!

~$kubectl get pods -o=custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,ANNOTATIONS:.metadata.annotations'
NAME                                NODE                   ANNOTATIONS
nginx-deployment-7474978d4f-9wbgw   mi-0ebe45e33a53e04f2   map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-fjswp   mi-0ebe45e33a53e04f2   map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-k2sjd   mi-0ebe45e33a53e04f2   map[controller.kubernetes.io/pod-deletion-cost:1]

スケールアップしてクラウドにバーストしましょう!ここのnginxデプロイメントは、デモンストレーション目的で不合理な量のCPU(200m)を要求しています。つまり、ハイブリッドノードには約8つのレプリカを配置できます。ポッドを15レプリカにスケールアップすると、それらをスケジュールする余地がなくなります。preferredDuringSchedulingIgnoredDuringExecutionアフィニティポリシーを使用しているため、まずハイブリッドノードから始めます。スケジュールできないものは、他の場所(クラウドインスタンス)にスケジュールすることが許可されます。

通常、スケーリングはCPU、メモリ、GPU可用性、またはキューの深さなどの外部要因に基づいて自動的に行われます。ここでは、強制的にスケールアップします:

~$kubectl scale deployment nginx-deployment --replicas 15

ここで、カスタム列を使用してkubectl get podsを実行すると、追加のポッドがワークショップEKSクラスタに接続されたEC2インスタンスにデプロイされていることがわかります。Kyvernoは、ハイブリッドノードに配置されたすべてのポッドにpod-deletion-costアノテーションを適用し、EC2に配置されたすべてのポッドにはそれを適用していません。スケールダウンすると、Kubernetesはまず_コスト_が低い、つまりアノテーションのないポッドをすべて削除します。その後、Kubernetesは他のすべてのポッドを同等と見なし、通常の削除ロジックが適用されます。それでは実際に見てみましょう:

~$kubectl get pods -o=custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,ANNOTATIONS:.metadata.annotations'
NAME                                NODE                                          ANNOTATIONS
nginx-deployment-7474978d4f-8269p   ip-10-42-108-174.us-west-2.compute.internal   <none>
nginx-deployment-7474978d4f-8f6cg   ip-10-42-163-36.us-west-2.compute.internal    <none>
nginx-deployment-7474978d4f-9wbgw   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-bjbvx   ip-10-42-154-155.us-west-2.compute.internal   <none>
nginx-deployment-7474978d4f-f55rj   ip-10-42-108-174.us-west-2.compute.internal   <none>
nginx-deployment-7474978d4f-fjswp   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-jrcsl   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-k2sjd   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-mstwv   ip-10-42-154-155.us-west-2.compute.internal   <none>
nginx-deployment-7474978d4f-q8nkj   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-smc9f   ip-10-42-163-36.us-west-2.compute.internal    <none>
nginx-deployment-7474978d4f-ss76l   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-tbzf2   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-txxlw   mi-0ebe45e33a53e04f2                          map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-wqbsd   ip-10-42-154-155.us-west-2.compute.internal   <none>

サンプルデプロイメントを再び3にスケールダウンしましょう。これにより、ハイブリッドノード上で実行されている3つのポッドが残り、元の状態に戻ります:

~$kubectl scale deployment nginx-deployment --replicas 3

最後に、確認のために、ハイブリッドノード上で実行されている3つのレプリカに戻っていることを確認しましょう:

~$kubectl get pods -o=custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,ANNOTATIONS:.metadata.annotations'
NAME                                NODE                   ANNOTATIONS
nginx-deployment-7474978d4f-9wbgw   mi-0ebe45e33a53e04f2   map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-fjswp   mi-0ebe45e33a53e04f2   map[controller.kubernetes.io/pod-deletion-cost:1]
nginx-deployment-7474978d4f-k2sjd   mi-0ebe45e33a53e04f2   map[controller.kubernetes.io/pod-deletion-cost:1]