クラウドバースティング
前回のデプロイを基に、「クラウドバースティング」のユースケースをシミュレートするシナリオを探ってみましょう。これにより、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をインストールし、以下に含まれるポリシーをデプロイします:
以下のClusterPolicyマニフェストは、EKSハイブリッドノードインスタンスにランディングするポッドを監視し、pod-deletion-costアノテーションを追加するようにKyvernoに指示します。
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"
Pod/bindingリソースを監視し、ポッドがノードにスケジュールされた時点で
アドミッションレビューリクエストから対応する値でnode変数を設定
Kubernetes APIにクエリを実行して、ポッドがスケジュールされたノードに関する情報からcomputeType変数を設定
'hybrid'ノードにスケジュールされたポッドのみを選択
ポッドを変更してpod-deletion-costアノテーションを追加
Kyvernoが稼働していることを確認し、ポリシーを適用しましょう:
次に、サンプルワークロードをデプロイします。これは、前述のnodeAffinityルールを使用して、3つのnginxポッドをハイブリッドノードにデプロイします:
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アノテーションを適用したことがわかります!
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 get podsを実行すると、追加のポッドがワークショップEKSクラスタに接続されたEC2インスタンスにデプロイされていることがわかります。Kyvernoは、ハイブリッドノードに配置されたすべてのポッドにpod-deletion-costアノテーションを適用し、EC2に配置されたすべてのポッドにはそれを適用していません。スケールダウンすると、Kubernetesはまず_コスト_が低い、つまりアノテーションのないポッドをすべて削除します。その後、Kubernetesは他のすべてのポッドを同等と見なし、通常の削除ロジックが適用されます。それでは実際に見てみましょう:
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つのポッドが残り、元の状態に戻ります:
最後に、確認のために、ハイブリッドノード上で実行されている3つのレプリカに戻っていることを確認しましょう:
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]