このページでは、Google Distributed Cloud Virtual for Bare Metal の 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 には、スケジューリングの動作を微調整できる機能が多数あります。こうした機能には、トポロジの分散の制約と反アフィニティ ルールが含まれます。こうした機能は 1 つでも、またそれを組み合わせても使用できます。定義した要件は 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 を再度デプロイして、コンテナがノード間で均等に分配されるかどうかを確認します。