この記事は基本的にGKEでの利用を想定して解説していますが、AWS等他のプロバイダでも大きくは変わりません。適時読み替えてもらえると。
クラスタオートスケーラーの課題点
kubernetesでNodeのオートスケールは、ざっくり説明すると以下のような流れになる訳ですが・・・
- Podの負荷が上がり、HPAによって新しいPodがスケジューリングされる
- PodをスケジューリングさせるためのNodeが不足して、新しいNodeが立ち上がる
- 新しいNodeにPodが立ち上がる
このスケーリングには少々問題がありまして、新しいNodeがすぐに有効活用されるかと言えば、そうでも無いんですよね・・・
時間が経つに連れて新しいNodeにもPodが増えますし、負荷も分散されて行くでしょう。しかし、すでに負荷が上がりきっている既存Nodeに配置されているPodが再スケジューリングされる訳では無いので、急なスパイク等には対応できません。GKEだとNodeの立ち上がり自体は早いんですけどね・・・
deschedulerで負荷が高いNodeのPodを再スケジューリングする
そこでdeschedulerの出番です。deschedulerは条件に応じてPodを削除するためのリソースですね。
上手く利用することで、負荷の高いNodeから負荷の低いNodeにPodを移動させることが可能になります。
導入
基本的にはGithubのREADMEに書いてある通りに進めればOKです
Docker Imageの作成
以下にImageの作成方法が説明してあります。
Imageが完成したら、GCPならGCR、AWSだったらECR等に適当にアップロードします。
権限周りの設定
各クラスタに応じて、権限周りの設定を行います。適宜必要な設定を済ませておいて下さい。
ConfigMapによるDeschedulerPolicyの設定
deschedulerにはいくつかの機能が搭載されていますが、今回利用するのはLowNodeUtilizationという機能です。Nodeの消費済みリソースを閾値としてPodを再スケジューリングさせるものですね。
LowNodeUtilizationには、二つの設定項目があります。一つ目はthresholds
で、再スケジューリング先Nodeの条件を指定するものです。もう一つはtargetThresholds
で、これはPodを削除するNodeの条件を指定します。以下は設定の一例です。
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
"cpu" : 20
"memory": 30
"pods": 10
targetThresholds:
"cpu" : 60
"memory": 80
"pods": 15
ちょっと分かりにくいので細分化して説明します。
thresholdsの詳しい説明
まず、上の例でthresholds
は"cpu": 20
、"memory": 30
、"pods": 10
となっていますね。
これは日本語で説明すると、「リクエスト済みのリソースがNodeの総リソースに対してCPUが20%以内、メモリが30%以内、尚且つ配置済みのPodが10台以内のNodeを再スケジューリング対象とする」と、なります。ここで注意して欲しいのは、cpuとmemoryは割合であること、podsは台数であること、そして、全ての条件を満たすNodeだけが対象になるということです。
targetThresholdsの詳しい説明
次に、targetThresholds
は"cpu": 60
、"memory": 80
、"pods": 15
となっていますね。これも日本語で説明すると、「リクエスト済みのリソースがNodeの総リソースに対してCPUが60%以上、メモリが80%以上、あるいは配置済みのPodが15台以上のNodeをPod削除対象とする」と、なります。注意点としては、cpu、memory、pods、3つの項目の内、いずれか一つの項目を満たせば対象になるという点です。例えばリクエスト済みのcpu率が60%を超えていれば、memoryが20%とかでも対象になります。
ConfigMapとして登録する
DeschedulerPolicyをConfigMapとしてクラスタに登録します。
apiVersion: v1
kind: ConfigMap
metadata:
name: descheduler-policy-configmap
namespace: kube-system
data:
policy.yaml: |
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
"cpu" : 20
"memory": 30
"pods": 10
targetThresholds:
"cpu" : 60
"memory": 80
"pods": 15
再スケジューリング処理を実行するためのCronJobを定義
公式のGithubにはJobを使った方法が紹介されていますが、実際に運用する場合はCronJobで実行させることになるでしょう。以下CronJob定義の一例です。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: descheduler-job
namespace: kube-system
spec:
schedule: "*/30 * * * *"
jobTemplate:
spec:
template:
metadata:
name: descheduler-pod
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ""
spec:
containers:
- name: descheduler
image: gcr.io/project/descheduler:v1 # アップロードしたimageを指定
volumeMounts:
- mountPath: /policy-dir
name: policy-volume
command:
- "/bin/descheduler"
args:
- "--policy-config-file"
- "/policy-dir/policy.yaml"
- "--v"
- "3"
restartPolicy: "Never"
serviceAccountName: descheduler-sa
volumes:
- name: policy-volume
configMap:
name: descheduler-policy-configmap
たくさんのPodが同時に再スケジューリングされるのを防止する
deschedulerのJobを実行すると、対象となるPodが複数ある場合に多くのPodが同時に再スケジューリングされてしまう可能性があります。そこで、PodDisruptionBudget
を定義して同時に再スケジューリングされるPodを制限します。以下の例だと、対象のdeploymentに属するPodは同時に2台までしか再スケジューリングされなくなります。
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: sample-pdb
spec:
maxUnavailable: 2
selector:
matchLabels:
app: sample # deployment等のlabelを指定
まとめ
クラスタオートスケーラーでの利用を想定したdeschedulerの運用方法や、設定項目の詳細に関して紹介させてもらいました。
設定項目に関しては、ちょっと分かりづらい上に、公式のドキュメントにも詳細な挙動が説明されていないので自前で色々検証しました。
個人的な不満点としては、deschedulerの対象とするリソースを選択できない点でしょうか。例えば、特定のdeploymentだけdeschedulerによる再スケジューリングの対象にしたい、kube-systemに属するPodは対象にしたくないといった用途に対応できないですね。一応、Critical pods
やDeamonSetは対象外になるなど、ある程度制限はされているみたいですが、こちら側で柔軟な選別は現状出来なさそうです。
絶賛開発中みたいなので、今後にも期待したいです。