Google Cloudとコンテナの継続的なセキュリティ
本文の内容は、2022年4月14日にCarlos Arillaが投稿したブログUnderstanding Kubernetes pod pending problems(https://sysdig.com/blog/kubernetes-pod-pending-problems/)を元に日本語に翻訳・再構成した内容となっております。
Kubernetes ポッドのPendingは、成熟度が異なっても、どのクラスターにも偏在しています。
Kubernetesを使用している任意のDevOpsエンジニアに、彼らの悪夢を苦しめる最も一般的なエラーを特定するように尋ねると、PendingのPodを持つデプロイはリストのトップに近いです(おそらくCrashLoopBackOffに次ぐものです)。
アップデートをプッシュしようとして、それが止まっているのを見ると、DevOpsは不安になります。解決策がかなり簡単な場合でも、PodがPendingになっている原因を見つけ、適用すべき変更を理解することは重要です(Kubernetesのトラブルシューティングが些細なことであることはほとんどありません)。
この記事では、この問題を引き起こすさまざまな状況に光を当て、DevOpsチームが迅速に解決策を見つけ、何よりも可能な限り回避できるようにすることを目指します。
KubernetesのPodには、いくつかの異なるフェーズで構成されるライフサイクルがあります。
ほとんどのPodは、PendingからRunningに移行するのに数秒しかかからず、その状態のまま人生の大半を過ごすことになります。KubernetesのPodの状態、Pending、Running、Started、Failed、Unknown
ここまでで、PodはKubernetesクラスターに受け入れられています。しかし、1つ以上のコンテナがセットアップされ、実行可能な状態にはなっていない。これには、Podがスケジュールされるのを待っている時間や、ネットワーク経由でコンテナイメージをダウンロードしている時間などが含まれます。
PodがPendingからRunningの段階に進めない場合、ライフサイクルは停止し、進行を妨げている問題が修正されるまでPodは保持されます。
kubectlでPodをリストアップすると、Kubernetes PodのPending状況を示す出力が表示されます。
$ kubectl -n troubleshooting get pods NAME READY STATUS RESTARTS AGE stress-6d6cbc8b9d-s4sbh 0/1 Pending 0 17s
Podが動かなくなり、問題を解決しない限り実行されません。
Podが実行できなくなる原因はいくつかありますが、主な3つの問題について説明します。
最初のものが最も一般的で、最後のものはほとんど見られません。それぞれのケースについて詳しく説明しましょう。
PendingからRunningへの道:スケジューリングの問題に注目
Podが作成されると、Kubernetesクラスターが最初に行うことは、いずれかのノードでPodを実行するようにスケジューリングしようとすることです。このプロセスは多くの場合、実に高速で、Podはそれを実行するのに十分なリソースを持つノードに迅速に割り当てられます。
スケジュールするために、クラスターはPodの有効なリクエストを使用します(詳しくはPod evictionに関するこちらの投稿をご覧ください)。通常、Podは要求されていないリソースをより多く持つノードに割り当てられ、SLOに準拠した要求への返信に満ちた、幸せで素晴らしい人生を歩みます。
しかし、このプロセスが毎回うまくいくなら、この記事を読んでいないでしょう。クラスターがPodを割り当てられないようにする要因はいくつかあります。
最も一般的なものを確認してみましょう。
Kubernetesはスケジューリングのリクエストを使って、Podがノードに収まるかどうかを判断しています。リソースの実際の使用量は問題ではなく、他のPodによってすでにリクエストされたリソースのみが問題です。
メモリとCPUに対するPodの有効な要求に応じるのに十分な要求可能なリソースがある場合、Podはノードにスケジューリングされます。もちろん、そのノードが実行可能なPodの最大数に達していないことが条件です。3つのkubernetesノードでは、すべてのリソースが要求されているため、Podをスケジュールすることはできず、Kubernetes Pod Pendingとして残ります。
Podからすべての要件を満たすノードが存在しない場合、いくつかのリソースが解放されるまでKubernetes Pod Pendingの状態で保持されます。
さまざまな問題(ノードの圧力)または人間の行動(ノードのcordoned)により、ノードはスケジュール不可能な状態に変わることがあります。これらのノードは、その状態が変化するまで、どのPodもスケジュールしません。3つのkubernetesノード、すべてが整合性に問題があるため、Podはスケジュールできず、Kubernetes Pod Pendingのままです。
Taintは、異なるノードに割り当てられるPodを制限することができるKubernetesの仕組みのことです。ノードにTaintがある場合、そのノードではtolerationが一致するPodのみが実行できるようになります。
この仕組みにより、ワークロードごとに異なるタイプのノード(GPU搭載ノード、CPU/メモリ比率の異なるノードなど)を用意するなど、Kubernetesの特殊な使い方ができるようになります。スケジュールできるのは1つのKubernetesノードだけですが、そのノードにはTaintがあり、Taintを満たすPodのみがスケジュール可能です。残りはKubernetes Pod Pendingとして残ります。
すべての理由を個別に説明しても、スケジューリングの問題は、これらの問題の組み合わせで発生することが多いのです。通常、あるノードが満杯で、他のノードがTaintされているためにスケジュールできない、あるいはあるノードがメモリ圧迫のためにスケジュールできない、といったところでしょうか。
スケジューリングの問題が何であるかを調べるには、スケジューラーから生成されるPodに関するイベントを確認する必要があります。イベントには、ノードを割り当てられない理由の詳細が記述されています。イベントは、例えばkubectl describeで確認することができます。
$ kubectl -n troubleshooting describe pod stress-6d6cbc8b9d-s4sbh Name: stress-6d6cbc8b9d-s4sbh Namespace: troubleshooting Priority: 0 Node: <none> Labels: app=stress pod-template-hash=6d6cbc8b9d Annotations: <none> Status: Pending IP: IPs: <none> Controlled By: ReplicaSet/stress-6d6cbc8b9d Containers: stress: Image: progrium/stress Port: <none> Host Port: <none> Args: --cpu 1 --vm 2 --vm-bytes 150M Limits: cpu: 300m memory: 120000Mi Requests: cpu: 200m memory: 100000Mi Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-snrww (ro) Conditions: Type Status PodScheduled False Volumes: kube-api-access-snrww: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: <nil> DownwardAPI: true QoS Class: Burstable Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 4m17s (x41 over 34m) default-scheduler 0/5 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 4 Insufficient memory.
出力では、メッセージにある正確な理由を見ることができます:
0/5 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 4 Insufficient memory.
この問題を解決するために、2つの選択肢があります。
現在実行中のワークロードを更新する場合、もう1つ重要な考慮すべき要素があります。アップグレードポリシーです。
このポリシーにより、Kubernetesは、更新の進行中にワークロードが通常よりも多くのPodを作成することを許可し、新しいPodを作成する間、古いPodをしばらく維持することができます。これは、ワークロードがしばらくの間、予想以上のリソースを要求できることを意味します。クラスターに十分な予備のリソースがない場合、更新はブロックされ、プロセスがブロック解除されるまで(またはロールバックタイムアウトによって更新が停止されるまで)、一部のPodがPendingのままになります。
ノードにPodが割り当てられると、kubeletはPod specのすべてのコンテナを起動しようとします。そのために、イメージをダウンロードして実行しようとします。PendingからRunnningへの道:イメージダウンロードの問題に注目
イメージのダウンロードを妨げるエラーはいくつかあります。
この問題は通常、独立した問題として扱われるため、近日中に公開予定の別の記事でさらに説明します。
PendingからRunnningへの道:依存性の問題に注目
Podが起動する前に、kubeletは他のKubernetes要素との依存関係をすべて確認しようとします。これらの依存関係のうち1つでも満たすことができない場合、依存関係が満たされるまでPodはpending状態になります。Podの依存関係の例:必要なPersisten Volumeが利用できないため、Podはpending状態のままとなります。
この場合、kubectlではこのようにPodが表示されます:
$ kubectl -n mysql get pods NAME READY STATUS RESTARTS AGE mysql-0 0/1 ContainerCreating 0 97s
そして、イベントにおいては、次のようなものが確認できます:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 3m19s default-scheduler Successfully assigned mysql/mysql-0 to ip-172-20-38-115.eu-west-1.compute.internal Warning FailedMount 76s kubelet Unable to attach or mount volumes: unmounted volumes=[config], unattached volumes=[kube-api-access-gxjf8 data config]: timed out waiting for the condition Warning FailedMount 71s (x9 over 3m19s) kubelet MountVolume.SetUp failed for volume "config" : configmap "mysql" not found
メッセージ列には、不足している要素を特定するのに十分な情報が記載されています。通常の原因は以下の通りです。
Kubernetesでワークロードを安全にデプロイし、アップデートするためには、PodがPendingフェーズに留まる理由を理解することが重要です。問題を素早く特定し、デプロイを進行させることができれば、頭痛の種を減らし、ダウンタイムを短縮することができます。
Sysdigを使用すると、この情報をすぐに利用できるため、Kubernetesのトラブルシューティングがさらに簡単になります。 今すぐ無料のSysdigMonitorトライアルに登録して、業界をリードするKubernetesモニタリング製品ですぐに詳細情報を入手してください。