Initコンテナ(Init Containers)
このページでは、Initコンテナについて概観します。Initコンテナとは、アプリケーションコンテナの前に実行され、アプリケーションコンテナのイメージに存在しないセットアップスクリプトやユーティリティーを含んだ特別なコンテナです。
Initコンテナを理解する
単一のPodは、Pod内に複数のコンテナを稼働させることができますが、Initコンテナもまた、アプリケーションコンテナが稼働する前に1つまたは複数稼働できます。
Initコンテナは下記の項目をのぞいて、通常のコンテナと全く同じものとなります。
- Initコンテナは常に完了するまで稼働します。
- 各Initコンテナは、次のInitコンテナが稼働する前に正常に完了しなくてはなりません。
もしあるPodの単一のInitコンテナが失敗した場合、KubernetesはInitコンテナが成功するまで何度もそのPodを再起動します。しかし、もしそのPodのrestartPolicy
がNever
の場合、再起動されません。
単一のコンテナをInitコンテナとして指定するためには、PodSpecにそのアプリケーションのcontainers
配列と並べて、initContainers
フィールドをContainer型のオブジェクトのJSON配列として指定してください。
Initコンテナのステータスは、.status.initContainerStatuses
フィールドにコンテナのステータスの配列として返されます(.status.containerStatuses
と同様)。
通常のコンテナとの違い
Initコンテナは、リソースリミット、ボリューム、セキュリティ設定などのアプリケーションコンテナの全てのフィールドと機能をサポートしています。しかし、Initコンテナに対するリソースリクエストやリソースリミットの扱いは微妙に異なり、下記のResourcesにて説明します。また、InitコンテナはそのPodの準備ができる前に完了しなくてはならないため、readinessProbe
をサポートしていません。
もし複数のInitコンテナが単一のPodに対して指定された場合、それらのInitコンテナは1つずつ順番に実行されます。各Initコンテナは次のコンテナが完了できる前に完了しなくてはなりません。全てのInitコンテナが実行完了した時、KubernetesはPodを初期化し、通常通りアプリケーションコンテナを稼働させます。
Initコンテナは何に使用できるか?
Initコンテナはアプリケーションコンテナのイメージとは分離されているため、コンテナの起動に関連したコードにおいていくつかの利点があります。
- セキュリティの理由からアプリケーションコンテナのイメージに含めたくないユーティリティーを含んだり実行できます。
- アプリケーションのイメージに存在していないセットアップ用のユーティリティーやカスタムコードを含むことができます。例えば、セットアップ中に
sed
、awk
、python
や、dig
のようなツールを使うための他のイメージから、アプリケーションのイメージを作る必要がなくなります。 - アプリケーションイメージをひとまとめにしてビルドすることなく、アプリケーションのイメージ作成と、デプロイ処理を独立して行うことができます。
- アプリケーションコンテナと別のファイルシステムビューを持つために、Linuxの名前空間を使用します。その結果、アプリケーションコンテナがアクセスできない箇所へのシークレットなアクセス権限を得ることができます。
- Initコンテナはアプリケーションコンテナの実行の前に完了しますが、その一方で、複数のアプリケーションコンテナは並列に実行されます。そのためInitコンテナはいくつかの前提条件をセットされるまで、アプリケーションコンテナの起動をブロックしたり遅らせることができます。
使用例
ここではInitコンテナの使用例を挙げます。
シェルコマンドを使って単一のServiceが作成されるのを待機します。
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1
コマンドを使って下位のAPIからこのPodをリモートサーバに登録します。
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
sleep 60
のようなコマンドを使ってアプリケーションコンテナが起動する前に待機します。ボリュームにあるgitリポジトリをクローンします。
メインのアプリケーションコンテナのための設定ファイルを動的に生成するために、いくつかの値を設定ファイルに移してテンプレートツールを稼働させます。例えば、設定ファイルにそのPodのPOD_IPを移して、Jinjaを使ってメインのアプリケーションコンテナの設定ファイルを生成します。
さらに詳細な使用例は、StatefulSetsのドキュメントとProduction Pods guideにまとまっています。
Initコンテナの使用
下記のKubernetes1.5用のyamlファイルは、2つのInitコンテナを含む単一のシンプルなポッドの概要となります。
最初のInitコンテナの例は、myservies
、2つ目のInitコンテナはmydb
の起動をそれぞれ待ちます。2つのコンテナの実行が完了するとPodの起動が始まります。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
annotations:
pod.beta.kubernetes.io/init-containers: '[
{
"name": "init-myservice",
"image": "busybox:1.28",
"command": ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
},
{
"name": "init-mydb",
"image": "busybox:1.28",
"command": ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
}
]'
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
古いアノテーション構文がKubernetes1.6と1.7において有効ですが、1.6では新しい構文にも対応しています。Kubernetes1.8以降では新しい構文を使用する必要があります。KubernetesではInitコンテナの宣言をspec
に移行させました。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
Kubernetes1.5での構文は1.6においても稼働しますが、1.6の構文の使用を推奨します。Kubernetes1.6において、API内でInitコンテナのフィールド作成されます。ベータ版のアノテーションはKubernetes1.6と1.7において有効ですが、1.8以降ではサポートされません。
下記のyamlファイルはmydb
とmyservice
というServiceの概要です。
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
このPodは、下記のコマンドによって起動とデバッグすることが可能です。
kubectl apply -f myapp.yaml
pod/myapp-pod created
kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
kubectl describe -f myapp.yaml
Name: myapp-pod
Namespace: default
[...]
Labels: app=myapp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container with docker id 5ced34a04634
kubectl logs myapp-pod -c init-myservice # 1つ目のInitコンテナを調査する
kubectl logs myapp-pod -c init-mydb # 2つ目のInitコンテナを調査する
一度mydq
とmyservice
Serviceを起動させると、Initコンテナが完了してmyapp-pod
が作成されるのを確認できます。
kubectl apply -f services.yaml
service/myservice created
service/mydb created
kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
この例は非常にシンプルですが、ユーザー独自のInitコンテナを作成するためのインスピレーションを提供するでしょう。
Initコンテナのふるまいに関する詳細
単一のPodが起動している間、ネットワークとボリュームが初期化されたのちに、Initコンテナは順番に起動されます。各Initコンテナは次のInitコンテナが起動する前に完了しなくてはなりません。もしあるInitコンテナがランタイムもしくはエラーにより起動失敗した場合、そのPodのrestartPolicy
の値をもとにリトライされます。しかし、もしPodのrestartPolicy
がAlways
に設定されていた場合、そのInitコンテナのrestartPolicy
はOnFailure
となります。
Podは全てのInitコンテナが完了するまでReady
状態となりません。Initコンテナ上のポートはServiceによって集約されません。初期化中のPodのステータスはPending
となりますが、Initializing
という値はtrueとなります。
もしそのPodが再起動されたとき、全てのInitコンテナは再度実行されなくてはなりません。
Initコンテナのスペックに対する変更はコンテナのイメージフィールドのみに限定されます。 Initコンテナのイメージフィールド値の変更は、そのPodの再起動することと等しいです。
Initコンテナは何度も再起動、リトライ可能なため、べき等(Idempotent)である必要があります。特に、EmptyDirs
にファイルを書き込むコードは、書き込み先のファイルがすでに存在している可能性を考慮に入れるべきです。
Initコンテナはアプリケーションコンテナの全てのフィールドを持っています。しかしKubernetesは、Initコンテナが完了と異なる状態を定義できないためreadinessProbe
が使用されることを禁止しています。これはバリデーションの際に強要されます。
Initコンテナがずっと失敗し続けたままの状態を防ぐために、PodにactiveDeadlineSeconds
、コンテナにlivenessProbe
の設定をそれぞれ使用してください。activeDeadlineSeconds
の設定はInitコンテナにも適用されます。
あるPod内の各アプリケーションコンテナとInitコンテナの名前はユニークである必要があります。他のコンテナと同じ名前を共有していた場合、バリデーションエラーが返されます。
リソース
Initコンテナの順序と実行を考えるとき、リソースの使用に関して下記のルールが適用されます。
- 全てのInitコンテナの中で定義された最も高いリソースリクエストとリソースリミットが、有効なInitリクエストとリミット になります。
- Podのリソースの有効なリクエストとリミット は、下記の2つの中のどちらか高い方となります。
- そのリソースの全てのアプリケーションコンテナのリクエストとリミットの合計
- そのリソースの有効なInitリクエストとリミット
- スケジューリングは有効なリクエストとリミットに基づいて実行されます。これはInitコンテナがそのPodの生存中に使われない初期化のためのリソースを保持することができることを意味しています。
- Podの有効なQosティアー は、Initコンテナとアプリケーションコンテナで同様です。
クォータとリミットは有効なPodリクエストとリミットに基づいて適用されます。
Podレベルのcgroupsは、スケジューラーと同様に、有効なPodリクエストとリミットに基づいています。
Podの再起動の理由
単一のPodは再起動可能で、Initコンテナの再実行も引き起こします。それらは下記の理由によるものです。
- あるユーザーが、そのPodのInitコンテナのイメージを変更するようにPodSpecを更新する場合。アプリケーションコンテナのイメージの変更はそのアプリケーションコンテナの再起動のみ行われます。
- そのPodのインフラストラクチャーコンテナが再起動された場合。これはあまり起きるものでなく、Nodeに対するルート権限を持ったユーザーにより行われることがあります。
restartPolicy
がAlways
と設定されているとき、単一Pod内の全てのコンテナが停止され、再起動が行われた時と、ガーベージコレクションによりInitコンテナの完了記録が失われた場合。
サポートと互換性
ApiServerのバージョン1.6.0かそれ以上のバージョンのクラスターは、.spec.initContainers
フィールドを使ったInitコンテナの機能をサポートしています。
それ以前のバージョンでは、α版かβ版のアノテーションを使ってInitコンテナを使用できます。また、.spec.initContainers
フィールドは、Kubernetes1.3.0かそれ以上のバージョンでInitコンテナを使用できるようにするためと、ApiServerバージョン1.6において、1.5.xなどの古いバージョンにロールバックできるようにするために、α版かβ版のアノテーションにミラーされ、存在するPodのInitコンテナの機能が失われることが無いように安全にロールバックできるようにします。
ApiServerとKubeletバージョン1.8.0かそれ以上のバージョンでは、α版とβ版のアノテーションは削除されており、廃止されたアノテーションは.spec.initContainers
フィールドへの移行が必須となります。
この機能はKubernetes1.6からβ版の機能として存在しています。InitコンテナはPodSpec内で、アプリケーションのcontainers
という配列と並べて指定されます。そのベータ版のアノテーション値はまだ扱われ、PodSpecのフィールド値を上書きします。しかしながら、それらはKubernetesバージョン1.6と1.7において廃止されました。Kubernetesバージョン1.8からはそのアノテーション値はサポートされず、PodSpecフィールドの値に変換する必要があります。
次の項目
フィードバック
このページは役に立ちましたか?
Thanks for the feedback. If you have a specific, answerable question about how to use Kubernetes, ask it on Stack Overflow. Open an issue in the GitHub repo if you want to 問題を報告する or 改善を提案.