このページでは、Google Distributed Cloud の Kubernetes スケジューラ(kube-scheduler
)の問題を解決する方法について説明します。
Kubernetes が常に Pod を同じノードセットにスケジュールする
このエラーは、次のようないくつかの方法で観測される可能性があります。
クラスタ使用率が不均衡。各ノードのクラスタ使用率は、
kubectl top nodes
コマンドを使用して検査できます。次の出力例は、特定のノードで顕著な使用率を示しています。NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% XXX.gke.internal 222m 101% 3237Mi 61% YYY.gke.internal 91m 0% 2217Mi 0% ZZZ.gke.internal 512m 0% 8214Mi 0%
リクエスト数が多すぎる。一度に多数の Pod を同じノードにスケジュールし、それらの Pod が HTTP リクエストを行うと、そのノードはレート制限される可能性があります。このシナリオでサーバーが返す一般的なエラーは
429 Too Many Requests
です。サービス利用不可。たとえば、高負荷のノードでホストされているウェブサーバーは、負荷が小さくなるまですべてのリクエストに
503 Service Unavailable
エラーで応答する場合があります。
常に同じノードにスケジュールされている Pod があるかどうかを確認するには、次の操作を行います。
次の
kubectl
コマンドを実行して、Pod のステータスを表示します。kubectl get pods -o wide -n default
ノード全体の Pod の分布を確認するには、出力の
NODE
列を確認します。次の出力例では、すべての Pod が同じノードにスケジュールされています。NAME READY STATUS RESTARTS AGE IP NODE nginx-deployment-84c6674589-cxp55 1/1 Running 0 55s 10.20.152.138 10.128.224.44 nginx-deployment-84c6674589-hzmnn 1/1 Running 0 55s 10.20.155.70 10.128.226.44 nginx-deployment-84c6674589-vq4l2 1/1 Running 0 55s 10.20.225.7 10.128.226.44
Pod には、スケジューリング動作を微調整できる機能がいくつかあります。こうした機能には、トポロジの分散の制約とアンチ アフィニティ ルールが含まれます。こうした機能は単独で使用することも、組み合わせて使用することもできます。定義した要件は、kube-scheduler
で AND 結合されます。
デフォルトのロギングの詳細レベルでは、スケジューラのログはキャプチャされません。トラブルシューティングにスケジューラ ログが必要な場合は、次の手順でスケジューラ ログをキャプチャします。
ロギングの詳細レベルを上げます。
kube-scheduler
Deployment を編集します。kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \ -n USER_CLUSTER_NAMESPACE
spec.containers.command
セクションの下にフラグ--v=5
を追加します。containers: - command: - kube-scheduler - --profiling=false - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --v=5
トラブルシューティングが完了したら、詳細レベルをデフォルト レベルに戻します。
kube-scheduler
Deployment を編集します。kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \ -n USER_CLUSTER_NAMESPACE
詳細レベルをデフォルト値に戻します。
containers: - command: - kube-scheduler - --profiling=false - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true
トポロジの分散の制約
トポロジの分散の制約を使用すると、zones
、regions
、node
などのカスタム定義トポロジに従ってノードにわたって Pod を均等に分散できます。
次のマニフェストの例では、トポロジの分散の制約を使用して、すべてのスケジュール可能なノードにわたってレプリカを均等に分散する Deployment を示します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: topology-spread-deployment
labels:
app: myapp
spec:
replicas: 30
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
topologySpreadConstraints:
- maxSkew: 1 # Default. Spreads evenly. Maximum difference in scheduled Pods per Node.
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule # Default. Alternatively can be ScheduleAnyway
labelSelector:
matchLabels:
app: myapp
matchLabelKeys: # beta in 1.27
- pod-template-hash
containers:
# pause is a lightweight container that simply sleeps
- name: pause
image: registry.k8s.io/pause:3.2
トポロジの分散の制約を使用する場合は、次の考慮事項が適用されます。
- Pod の
labels.app: myapp
は、制約のlabelSelector
と一致します。 topologyKey
はkubernetes.io/hostname
を指定します。このラベルはすべてのノードに自動的に添付され、ノードのホスト名が入力されます。matchLabelKeys
は、Pod をスケジュールする場所を計算するときに、新しい Deployment のロールアウトが古いリビジョンの Pod を考慮しないようにします。pod-template-hash
ラベルは、Deployment によって自動的に入力されます。
Pod のアンチアフィニティ
Pod のアンチアフィニティを使用すると、同じノードに Pod を配置するための制約を定義できます。
次のマニフェストの例は、アンチアフィニティを使用してレプリカをノードあたり 1 つの Pod に制限する Deployment を示しています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-deployment
labels:
app: myapp
spec:
replicas: 30
selector:
matchLabels:
app: myapp
template:
metadata:
name: with-pod-affinity
labels:
app: myapp
spec:
affinity:
podAntiAffinity:
# requiredDuringSchedulingIgnoredDuringExecution
# prevents Pod from being scheduled on a Node if it
# does not meet criteria.
# Alternatively can use 'preferred' with a weight
# rather than 'required'.
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
# Your nodes might be configured with other keys
# to use as `topologyKey`. `kubernetes.io/region`
# and `kubernetes.io/zone` are common.
topologyKey: kubernetes.io/hostname
containers:
# pause is a lightweight container that simply sleeps
- name: pause
image: registry.k8s.io/pause:3.2
この Deployment の例では、30
レプリカを指定していますが、クラスタ内で使用可能なノード数しか展開されません。
Pod のアンチアフィニティを使用する場合は、次の考慮事項が適用されます。
- Pod の
labels.app: myapp
は、制約のlabelSelector
と一致します。 topologyKey
はkubernetes.io/hostname
を指定します。このラベルはすべてのノードに自動的に添付され、ノードのホスト名が入力されます。region
やzone
など、クラスタがサポートしている場合は、他のラベルを使用することもできます。
コンテナ イメージを事前に pull する
他の制約がない場合は、デフォルトでは、kube-scheduler
はコンテナ イメージがすでにダウンロードされているノードに Pod をスケジュール設定します。この動作は、すべてのノードでイメージをダウンロードでき、他のスケジューリング構成を持たない小規模なクラスタで役立つ場合があります。ただし、この考え方に頼るのは最後の手段と考えてください。より良い解決策は、nodeSelector
、トポロジの分散の制約、またはアフィニティ / アンチアフィニティを使用することです。詳細は、ノードに Pod を割り当てるをご覧ください。
コンテナ イメージがすべてのノードに事前に pull されるようにするには、次の例のように DaemonSet
を使用します。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: prepulled-images
spec:
selector:
matchLabels:
name: prepulled-images
template:
metadata:
labels:
name: prepulled-images
spec:
initContainers:
- name: prepulled-image
image: IMAGE
# Use a command the terminates immediately
command: ["sh", "-c", "'true'"]
containers:
# pause is a lightweight container that simply sleeps
- name: pause
image: registry.k8s.io/pause:3.2
すべてのノードで Pod が Running
になったら、Pod を再度デプロイして、コンテナがノード間で均等に分配されるかどうかを確認します。