前回の記事では、Kubernetesプロジェクトにおいて最も重要なPodの概念について詳しく紹介しました。今回は、Podオブジェクトの詳細をさらに共有します。
Pod vs. 仮想マシン
ここまでで、Kubernetesプロジェクトにおける最小のオーケストレーションユニットはコンテナではなくPodであることを明確に理解できているはずです。この設計はAPIオブジェクトにも反映されており、コンテナはPod属性内の単なる通常のフィールドになっています。当然ながら、どの属性がPodオブジェクトに属し、どの属性がContainerに属するのかという疑問が湧きます。
Podは、従来のデプロイ環境における「仮想マシン」の役割を果たします。この設計は、従来の環境(仮想マシン環境)からKubernetes(コンテナ環境)への移行をよりスムーズにするためのものです。
Podを従来環境の「マシン」、コンテナをこの「マシン」上で実行される「ユーザプログラム」と考えると、Podオブジェクトの設計の多くの側面が理解しやすくなります。
例えば、スケジューリング、ネットワーク、ストレージ、セキュリティに関連する属性は、基本的にPodレベルにあります。これらの属性の共通点は、「マシン」全体を記述するものであり、内部で実行される「プログラム」を記述するものではないことです。
たとえば、「マシン」のネットワークカードの設定(Podのネットワーク定義)、「マシン」のディスクの設定(Podのストレージ定義)、「マシン」のファイアウォールの設定(Podのセキュリティ定義)などが該当します。また、この「マシン」がどのサーバーで実行されるか(Podのスケジューリング)も同様です。
Kubernetes YAML
Kubernetesでは、宣言型ファイルを通じてPodリソースを定義します。
NodeSelector: これはユーザーがPodをNodeにバインドできるようにするフィールドで、以下のように記述します。
apiVersion: v1
kind: Pod
...
spec:
nodeSelector:
disktype: ssd
この設定は、このPodが「disktype: ssd」ラベルを持つノードでのみ実行でき、そうでなければスケジューリングに失敗することを意味します。
NodeName: Podのこのフィールドが割り当てられると、KubernetesプロジェクトはそのPodがスケジューリングされたとみなし、スケジューリング結果として割り当てられたノード名が設定されます。そのため、このフィールドは通常スケジューラによって設定されますが、ユーザーが設定してスケジューラを「だます」ことも可能です。ただし、この方法は通常テスト時やデバッグ時にのみ使用されます。
HostAliases: Podのhostsファイル(例:/etc/hosts)の内容を定義します。以下のように記述します。
apiVersion: v1
kind: Pod
...
spec:
hostAliases:
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
...
注意点として、Kubernetesプロジェクトでhostsファイルの内容を設定する場合、必ずこの方法を用いる必要があります。そうしなければ、直接hostsファイルを変更しても、Podが削除されて再作成された後にkubeletが自動的に変更内容を上書きしてしまいます。
上記の「マシン」関連の設定に加えて、コンテナのLinux Namespaceに関連する属性もすべてPodレベルにあることにお気づきでしょう。これも理解しやすい点です。Podの設計は、内部のコンテナができるだけ多くのLinux Namespaceを共有できるようにし、必要な隔離と制限機能のみを残すことを目的としています。これにより、Podがシミュレートする効果は仮想マシン内のプログラム間の関係に非常に近くなります。
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
これはKubernetes公式ドキュメントのPod YAMLファイルです。非常にシンプルで、nginxイメージを使用するコンテナを1つ定義しているだけです。しかし、このYAMLファイルのcontainersセクションでは、コンテナにpostStartとpreStopのパラメータが設定されています。
これは何を意味するのでしょうか?
まずpostStartから説明します。これはコンテナ起動直後に実行される操作を指します。postStartで定義された操作はDockerコンテナのENTRYPOINTの後に実行されますが、厳密に順序が保証されるわけではありません。つまり、postStartが開始された時点でENTRYPOINTがまだ終了していない可能性があります。
もちろん、postStartの実行がタイムアウトしたりエラーが発生した場合、KubernetesはそのPodのEventsにコンテナの起動に失敗したというエラーメッセージを報告し、Podも失敗状態になります。
同様に、preStopのタイミングはコンテナが強制終了される前(例えばSIGKILLシグナルを受け取ったとき)に発生します。明確にしておくと、preStop操作の実行は同期です。そのため、定義された操作が完了するまで現在のコンテナ終了プロセスをブロックします。この点はpostStartとは異なります。
この例では、コンテナが正常に起動した後、/usr/share/messageに「greeting message」を書き込みます(postStartで定義された操作)。そして、このコンテナが削除される前に、まずnginxの終了コマンドを呼び出します(preStopで定義された操作)。これにより、コンテナの「優雅なシャットダウン」が実現されます。
Podの主要なフィールドとそのContainerセクションに慣れてきたところで、次にKubernetesにおけるPodオブジェクトのライフサイクルについて共有します。
Podのライフサイクルの変化は、主にPod APIオブジェクトのStatus部分に現れます。これはMetadataやSpecに次ぐ3番目に重要なフィールドです。その中で、pod.status.phaseはPodの現在の状態を表し、以下のような状態があります。
1. Pending. この状態は、PodのYAMLファイルがKubernetesに送信され、APIオブジェクトが作成されてEtcdに保存されたことを意味します。しかし、このPod内の一部のコンテナが何らかの理由でスムーズに作成できません。例えば、スケジューリングの失敗などです。
2. Running. この状態では、Podは正常にスケジューリングされ、特定のノードにバインドされています。含まれるすべてのコンテナが正常に作成され、少なくとも1つは現在実行中です。
3. Succeeded. この状態は、Pod内のすべてのコンテナが実行を完了し、終了したことを意味します。この状況は、1回限りのタスクを実行する場合に最も一般的です。
4. Failed. この状態では、Pod内の少なくとも1つのコンテナが異常終了(ゼロ以外の戻りコード)しています。この状態が現れた場合、コンテナ内のアプリケーションをどのようにデバッグするか(PodのEventsやログを確認するなど)を考える必要があります。
5. Unknown. これは異常な状態で、Podの状態をkubeletがkube-apiserverに継続的に報告できないことを示します。これはmasterとkubeletの間の通信問題が原因である可能性が高いです。
さらに、PodオブジェクトのStatusフィールドは、一連のConditionsに細分化できます。これらの詳細なステータス値には、PodScheduled、Ready、Initialized、Unschedulableが含まれます。これらは主に現在のStatusの具体的な理由を説明するために使用されます。
例えば、Podの現在のStatusがPendingで、対応するConditionがUnschedulableの場合、スケジューリングに問題があることを意味します。
その中でも、Ready サブステータスは特に注目に値します。これは、Podが正常に起動した(Running状態)だけでなく、外部にサービスを提供する準備もできていることを意味します。RunningとReadyの間には違いがあるため、よく考えてみることをお勧めします。
これらのPodのステータスメッセージは、アプリケーションの実行状態を判断する重要な基準です。特にPodが「Running」以外の状態になった場合、迅速に対応し、示された異常状況に基づいて追跡と特定を開始する必要があります。慌ててドキュメントを調べるのではなく、です。
まとめ
今日の記事では、Pod APIオブジェクトについて詳しく説明し、Podの主要な使用方法を紹介し、フィールドに関するPodとContainerの類似点と相違点を分析しました。これらの説明が、Pod YAMLの主要フィールドとその正確な意味をよりよく理解し、記憶するのに役立つことを願っています。
実際、Pod APIオブジェクトはKubernetesシステム全体で最も中核的な概念であり、後ほど説明するコントローラーでも使用されます。
