1 - コンテナの概要
コンテナは、アプリケーションの(コンパイルされた)コードと、実行時に必要な依存関係をパッケージ化するための技術です。実行する各コンテナは再現性があります。依存関係を含めることによる標準化は、どこで実行しても同じ動作が得られることを意味します。
コンテナは、基礎となるホストインフラストラクチャからアプリケーションを切り離します。これにより、さまざまなクラウド環境やOS環境でのデプロイが容易になります。
コンテナイメージ
コンテナイメージは、アプリケーションを実行するために必要なすべてのものを含んだ、すぐに実行可能なソフトウェアパッケージです。コードとそれが必要とする任意のランタイム、アプリケーションとシステムのライブラリ、および必須の設定のデフォルト値が含まれています。
設計上、コンテナは不変であるため、すでに実行中のコンテナのコードを変更することはできません。コンテナ化されたアプリケーションがあり、変更を加えたい場合は、変更を含む新しいコンテナをビルドし、コンテナを再作成して更新されたイメージから起動する必要があります。
コンテナランタイム
コンテナランタイムは、コンテナの実行を担当するソフトウェアです。
Kubernetesは次の複数のコンテナランタイムをサポートします。
Docker、containerd、CRI-O、
および全ての
Kubernetes CRI (Container Runtime Interface)
実装です。
次の項目
2 - イメージ
コンテナイメージはアプリケーションと依存関係のあるすべてソフトウェアをカプセル化したバイナリデータを表します。コンテナイメージはスタンドアロンで実行可能なソフトウェアをひとつにまとめ、ランタイム環境に関する想定を明確に定義しています。
アプリケーションのコンテナイメージを作成し、一般的にはPodで参照する前にレジストリへPushします。
このページではコンテナイメージの概要を説明します。
イメージの名称
コンテナイメージは、pause
、example/mycontainer
、またはkube-apiserver
のような名前が通常つけられます。
イメージにはレジストリのホスト名も含めることができ(例:fictional.registry.example/imagename
)、さらにポート番号も含めることが可能です(例:fictional.registry.example:10443/imagename
)。
レジストリのホスト名を指定しない場合は、KubernetesはDockerパブリックレジストリを意味していると見なします。
イメージ名の後に、タグ を追加することができます(docker
やpodman
のようなコマンドを利用した場合と同様)。
タグによって同じイメージの異なるバージョンを識別できます。
イメージタグは大文字と小文字、数値、アンダースコア(_
)、ピリオド(.
)とマイナス(-
)で構成されます。
イメージタグでは区切り記号(_
、-
、.
)を指定できる追加ルールがあります。
タグを指定しない場合は、Kubernetesはlatest
タグを指定したと見なします。
イメージの更新
Deployment、StatefulSet、Pod、またはPodテンプレートを含むその他のオブジェクトを最初に作成するとき、デフォルトでは、Pod内のすべてのコンテナのPullポリシーは、明示的に指定されていない場合、IfNotPresent
に設定されます。
イメージがすでに存在する場合、このポリシーはkubeletにイメージのPullをスキップさせます。
イメージPullポリシー
コンテナのimagePullPolicy
とイメージのタグは、kubeletが指定されたイメージをPull(ダウンロード)しようとする時に影響します。
以下は、imagePullPolicy
に設定できる値とその効果の一覧です。
IfNotPresent
- イメージがローカルにまだ存在しない場合のみ、イメージがPullされます。
Always
- kubeletがコンテナを起動するときは常にコンテナイメージレジストリに照会して、イメージ名をイメージダイジェストに解決します。
ローカルにキャッシュされた同一ダイジェストのコンテナイメージがあった場合、kubeletはキャッシュされたイメージを使用します。
そうでない場合、kubeletは解決されたダイジェストのイメージをPullし、そのイメージを使ってコンテナを起動します。
Never
- kubeletは、イメージを取得しようとしません。ローカルにイメージがすでに存在する場合、kubeletはコンテナを起動しようとします。それ以外の場合、起動に失敗します。
詳細は、事前にPullしたイメージを参照してください。
レジストリに確実にアクセスできるのであれば、基盤となるイメージプロバイダーのキャッシュセマンティクスによりimagePullPolicy: Always
でも効率的です。
コンテナランタイムは、イメージレイヤーが既にノード上に存在することを認識できるので、再度ダウンロードする必要がありません。
備考: 本番環境でコンテナをデプロイする場合は、:latest
タグの使用を避けるべきです。
実行中のイメージのバージョンを追跡するのが難しく、正しくロールバックすることがより困難になるためです。
かわりに、v1.42.0
のような特定できるタグを指定してください。
Podがいつも同じバージョンのコンテナイメージを使用するために、イメージのダイジェストを指定することができます。<image-name>:<tag>
を<image-name>@<digest>
に置き換えてください(例えば、image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
)。
イメージタグを使用する場合、イメージレジストリがそのイメージのタグが表すコードを変更すると、新旧のコードを実行するPodが混在することになるかもしれません。
イメージダイジェストは特定のバージョンのイメージを一意に識別するため、Kubernetesは特定のイメージ名とダイジェストが指定されたコンテナを起動するたびに同じコードを実行します。
イメージをダイジェストで指定することは、レジストリの変更でそのようなバージョンの混在を起こさないように、実行するコードを固定します。
Pod(およびPodテンプレート)を作成する時に、実行中のワークロードがタグではなくイメージダイジェストに基づき定義されるように変化させるサードパーティーのアドミッションコントローラーがあります。
レジストリでどのようなタグの変更があっても、すべてのワークロードが必ず同じコードを実行するようにしたい場合に役立ちます。
デフォルトのイメージPullポリシー
新しいPodがAPIサーバに送信されると、クラスターは特定の条件が満たされたときにimagePullPolicy
フィールドを設定します。
imagePullPolicy
フィールドを省略し、コンテナイメージのタグに:latest
を指定した場合、imagePullPolicy
には自動的にAlways
が設定される
imagePullPolicy
フィールドを省略し、コンテナイメージのタグを指定しなかった場合、imagePullPolicy
には自動的にAlways
が設定される
imagePullPolicy
フィールドを省略し、コンテナイメージのタグに:latest
以外を指定した場合、imagePullPolicy
には自動的にIfNotPresent
が設定される
備考: コンテナのimagePullPolicy
の値は、そのオブジェクトが最初に 作成 されたときに常に設定され、イメージのタグが後で変更された場合でも更新されません。
例えば、タグが:latest
でない イメージを使ってDeploymentを生成した場合、後でDeploymentのイメージを:latest
タグに変更しても、imagePullPolicy
はAlways
に更新されません。オブジェクトのPullポリシーは、初期作成後に手動で変更する必要があります。
必要なイメージをPullする
常に強制的にPullしたい場合は、以下のいずれかを行ってください。
- コンテナの
imagePullPolicy
にAlways
を設定する。
imagePullPolicy
を省略し、使用するイメージに:latest
タグ使用する。Pod生成時に、KubernetesがポリシーにAlways
を設定する。
imagePullPolicy
と使用するイメージのタグを省略する。Pod生成時に、KubernetesがポリシーにAlways
を設定する。
- AlwaysPullImagesアドミッションコントローラーを有効にする。
ImagePullBackOff
kubeletがコンテナランタイムを使ってPodのコンテナの生成を開始するとき、ImagePullBackOff
のためにコンテナがWaiting状態になる可能性があります。
ImagePullBackOff
ステータスは、KubernetesがコンテナイメージをPullできないために、コンテナを開始できないことを意味します(イメージ名が無効である、imagePullSecret
なしでプライベートレジストリからPullしたなどの理由のため)。BackOff
は、バックオフの遅延を増加させながらKubernetesがイメージをPullしようとし続けることを示します。
Kubernetesは、組み込まれた制限である300秒(5分)に達するまで、試行するごとに遅延を増加させます。
イメージインデックスを使ったマルチアーキテクチャイメージ
コンテナレジストリはバイナリイメージの提供だけでなく、コンテナイメージインデックスも提供する事ができます。イメージインデックスはコンテナのアーキテクチャ固有バージョンに関する複数のイメージマニフェストを指すことができます。イメージインデックスの目的はイメージの名前(例:pause
、example/mycontainer
、kube-apiserver
)をもたせ、様々なシステムが使用しているマシンアーキテクチャにあう適切なバイナリイメージを取得できることです。
Kubernetes自身は、通常コンテナイメージに-$(ARCH)
のサフィックスを持つ名前をつけます。下位互換の為にサフィックス付きの古い仕様のイメージを生成してください。その目的は、pause
のようなすべてのアーキテクチャのマニフェストを持つイメージと、サフィックスのあるイメージをハードコードしていた可能性のある古い仕様の設定やYAMLファイルと下位互換があるpause-amd64
のようなイメージを生成することです。
プライベートレジストリを使用する方法
プライベートレジストリではイメージを読み込む為にキーが必要になる場合があります。
認証情報はいくつかの方法で提供できます。
- プライベートレジストリへの認証をNodeに設定する
- すべてのPodがプライベートレジストリを読み取ることができる
- クラスター管理者によるNodeの設定が必要
- 事前にPullされたイメージ
- すべてのPodがNode上にキャッシュされたイメージを利用できる
- セットアップするためにはすべてのNodeに対するrootアクセスが必要
- PodでImagePullSecretsを指定する
- キーを提供したPodのみがプライベートレジストリへアクセスできる
- ベンダー固有またはローカルエクステンション
- カスタムNode構成を使っている場合、あなた(または、あなたのクラウドプロバイダー)はコンテナレジストリへの認証の仕組みを組み込むことができる
これらのオプションについて、以下で詳しく説明します。
プライベートレジストリへの認証をNodeに設定する
認証情報を設定するための具体的な手順は、使用するコンテナランタイムとレジストリに依存します。最も正確な情報として、ソリューションのドキュメントを参照する必要があります。
プライベートなコンテナイメージレジストリを設定する例として、プライベートレジストリからイメージをPullするタスクを参照してください。その例では、Docker Hubのプライベートレジストリを使用しています。
config.jsonの解釈
config.json
の解釈は、Dockerのオリジナルの実装とKubernetesの解釈で異なります。
Dockerでは、auths
キーはルートURLしか指定できませんが、Kubernetesではプレフィックスのマッチしたパスだけでなく、グロブパターンのURLも指定できます。
以下のようなconfig.json
が有効であるということです。
{
"auths": {
"*my-registry.io/images": {
"auth": "…"
}
}
}
ルートURL(*my-registry.io
)は、以下の構文でマッチングされます:
pattern:
{ term }
term:
'*' セパレーター以外の任意の文字列にマッチする
'?' セパレーター以外の任意の一文字にマッチする
'[' [ '^' ] { character-range } ']'
文字クラス (空であってはならない)
c 文字 c とマッチする (c != '*', '?', '\\', '[')
'\\' c 文字 c とマッチする
character-range:
c 文字 c とマッチする (c != '\\', '-', ']')
'\\' c 文字 c とマッチする
lo '-' hi lo <= c <= hi の文字 c とマッチする
イメージのPull操作では、有効なパターンごとに認証情報をCRIコンテナランタイムに渡すようになりました。例えば、以下のようなコンテナイメージ名は正常にマッチングされます。
my-registry.io/images
my-registry.io/images/my-image
my-registry.io/images/another-image
sub.my-registry.io/images/my-image
a.sub.my-registry.io/images/my-image
kubeletは、見つかったすべての認証情報に対してイメージのPullを順次実行します。これは、次のようにconfig.json
に複数のエントリーを書くことも可能であることを意味します。
{
"auths": {
"my-registry.io/images": {
"auth": "…"
},
"my-registry.io/images/subpath": {
"auth": "…"
}
}
}
コンテナがmy-registry.io/images/subpath/my-image
をPullするイメージとして指定した場合、kubeletが認証ソースの片方からダウンロードに失敗すると、両方の認証ソースからダウンロードを試みます。
事前にPullしたイメージ
備考: Node構成を制御できる場合、この方法が適しています。
クラウドプロバイダーがNodeを管理し自動的に設定を置き換える場合は、確実に機能できません。
デフォルトでは、kubeletは指定されたレジストリからそれぞれのイメージをPullしようとします。
また一方では、コンテナのimagePullPolicy
プロパティにIfNotPresent
やNever
が設定されている場合、ローカルのイメージが使用されます。(それぞれに対して、優先的またはか排他的に)
レジストリ認証の代替として事前にPullしたイメージを利用したい場合、クラスターのすべてのNodeが同じ事前にPullしたイメージを持っていることを確認する必要があります。
特定のイメージをあらかじめロードしておくことは高速化やプライベートレジストリへの認証の代替として利用することができます。
すべてのPodは事前にPullしたイメージへの読み取りアクセス権をもちます。
PodでimagePullSecretsを指定する
備考: この方法がプライベートレジストリのイメージに基づいてコンテナを実行するための推奨の方法です。
KubernetesはPodでのコンテナイメージレジストリキーの指定をサポートしています。
Dockerの設定を利用してSecretを作成する。
レジストリへの認証のためにユーザー名、レジストリのパスワード、クライアントのメールアドレス、およびそのホスト名を知っている必要があります。
適切な大文字の値を置き換えて、次のコマンドを実行します。
kubectl create secret docker-registry <name> --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
既にDocker認証情報ファイルを持っている場合は、上記のコマンドの代わりに、認証情報ファイルをKubernetes Secretsとしてインポートすることができます。
既存のDocker認証情報に基づいてSecretを作成する で、この設定方法を説明します.
これは複数のプライベートコンテナレジストリを使用している場合に特に有効です。kubectl create secret docker-registry
はひとつのプライベートレジストリにのみ機能するSecretを作成するからです。
備考: Podは自分自身のNamespace内にあるimage pull secretsのみが参照可能であるため、この作業はNemespace毎に1回行う必要があります。
PodのimagePullSecretsを参照する方法
これで、imagePullSecrets
セクションをPod定義へ追加することでSecretを参照するPodを作成できます。
例:
cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
EOF
cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF
これは、プライベートレジストリを使用する各Podで行う必要があります。
ただし、この項目の設定はServiceAccountリソースの中でimagePullSecretsを指定することで自動化することができます。
詳細の手順は、ImagePullSecretsをService Accountに追加するをクリックしてください。
これを各Nodeの.docker/config.json
に組み合わせて利用できます。認証情報はマージされます。
ユースケース
プライベートレジストリを設定するためのソリューションはいくつかあります。ここでは、いくつかの一般的なユースケースと推奨される解決方法を示します。
- クラスターに独自仕様でない(例えば、オープンソース)イメージだけを実行する。イメージを非公開にする必要がない
- パブリックレジストリのパブリックイメージを利用する
- 設定は必要ない
- クラウドプロバイダーによっては、可用性の向上とイメージをPullする時間を短くする為に、自動的にキャッシュやミラーされたパプリックイメージが提供される
- 社外には非公開の必要があるが、すべてのクラスター利用者には見せてよい独自仕様のイメージをクラスターで実行している
- ホストされたプライペートレジストリを使用
- プライベートレジストリにアクセスする必要があるノードには、手動設定が必要となる場合がある
- または、オープンな読み取りアクセスを許可したファイヤーウォールの背後で内部向けプライベートレジストリを実行する
- イメージへのアクセスを制御できるホストされたコンテナイメージレジストリサービスを利用する
- Nodeを手動設定するよりもクラスターのオートスケーリングのほうがうまく機能する
- また、Node設定変更を自由にできないクラスターでは
imagePullSecrets
を使用する
- 独自仕様のイメージを含むクラスターで、いくつかは厳格なアクセス制御が必要である
- それぞれのテナントが独自のプライベートレジストリを必要とするマルチテナントのクラスターである
- AlwaysPullImagesアドミッションコントローラーが有効化を確認する必要がある。さもないと、すべてのテナントの全Podが全部のイメージにアクセスできてしまう可能性がある
- 認証が必要なプライベートレジストリを実行する
- それぞれのテナントでレジストリ認証を生成し、Secretへ設定し、各テナントのNamespaceに追加する
- テナントは、Secretを各NamespaceのimagePullSecretsへ追加する
複数のレジストリへのアクセスが必要な場合、それぞれのレジストリ毎にひとつのSecretを作成する事ができます。
次の項目
3 - コンテナ環境
このページでは、コンテナ環境で利用可能なリソースについて説明します。
コンテナ環境
Kubernetesはコンテナにいくつかの重要なリソースを提供します。
- イメージと1つ以上のボリュームの組み合わせのファイルシステム
- コンテナ自体に関する情報
- クラスター内の他のオブジェクトに関する情報
コンテナ情報
コンテナの ホスト名 は、コンテナが実行されているPodの名前です。
ホスト名はhostname
コマンドまたはlibcのgethostname
関数呼び出しにより利用可能です。
Podの名前と名前空間はdownward APIを通じて環境変数として利用可能です。
Pod定義からのユーザー定義の環境変数もコンテナで利用できます。
コンテナイメージで静的に指定されている環境変数も同様です。
クラスター情報
コンテナの作成時に実行されていたすべてのサービスのリストは、環境変数として使用できます。
このリストは、新しいコンテナのPodおよびKubernetesコントロールプレーンサービスと同じ名前空間のサービスに制限されます。
bar という名前のコンテナに対応する foo という名前のサービスの場合、以下の変数が定義されています。
FOO_SERVICE_HOST=<サービスが実行されているホスト>
FOO_SERVICE_PORT=<サービスが実行されているポート>
サービスは専用のIPアドレスを持ち、DNSアドオンが有効の場合、DNSを介してコンテナで利用可能です。
次の項目
4 - ランタイムクラス(Runtime Class)
FEATURE STATE: Kubernetes v1.14 [beta]
このページではRuntimeClassリソースと、runtimeセクションのメカニズムについて説明します。
RuntimeClassはコンテナランタイムの設定を選択するための機能です。そのコンテナランタイム設定はPodのコンテナを稼働させるために使われます。
RuntimeClassを使う動機
異なるPodに異なるRuntimeClassを設定することで、パフォーマンスとセキュリティのバランスをとることができます。例えば、ワークロードの一部に高レベルの情報セキュリティ保証が必要な場合、ハードウェア仮想化を使用するコンテナランタイムで実行されるようにそれらのPodをスケジュールすることを選択できます。その後、追加のオーバーヘッドを犠牲にして、代替ランタイムをさらに分離することでメリットが得られます。
RuntimeClassを使用して、コンテナランタイムは同じで設定が異なるPodを実行することもできます。
セットアップ
RuntimeClass機能のフィーチャーゲートが有効になっていることを確認してください(デフォルトで有効です)。フィーチャーゲートを有効にする方法については、フィーチャーゲートを参照してください。
そのRuntimeClass
のフィーチャーゲートはApiServerとkubeletのどちらも有効になっていなければなりません。
- ノード上でCRI実装を設定する。(ランタイムに依存)
- 対応するRuntimeClassリソースを作成する。
1. ノード上でCRI実装を設定する
RuntimeClassを通じて利用可能な設定はContainer Runtime Interface (CRI)の実装依存となります。
ユーザーの環境のCRI実装の設定方法は、対応するドキュメント(下記)を参照ください。
備考: RuntimeClassは、クラスター全体で同じ種類のノード設定であることを仮定しています。(これは全てのノードがコンテナランタイムに関して同じ方法で構成されていることを意味します)。
設定が異なるノードをサポートするには、
スケジューリングを参照してください。
RuntimeClassの設定は、RuntimeClassによって参照されるハンドラー
名を持ちます。そのハンドラーは正式なDNS-1123に準拠する形式のラベルでなくてはなりません(英数字 + -
の文字で構成されます)。
2. 対応するRuntimeClassリソースを作成する
ステップ1にて設定する各項目は、関連するハンドラー
名を持ちます。それはどの設定かを指定するものです。各ハンドラーにおいて、対応するRuntimeClassオブジェクトが作成されます。
そのRuntimeClassリソースは現時点で2つの重要なフィールドを持ちます。それはRuntimeClassの名前(metadata.name
)とハンドラー(handler
)です。そのオブジェクトの定義は下記のようになります。
apiVersion: node.k8s.io/v1beta1 # RuntimeClassはnode.k8s.ioというAPIグループで定義されます。
kind: RuntimeClass
metadata:
name: myclass # RuntimeClass名
# RuntimeClassはネームスペースなしのリソースです。
handler: myconfiguration # 対応するCRI設定
RuntimeClassオブジェクトの名前はDNSサブドメイン名に従う必要があります。
備考: RuntimeClassの書き込み操作(create/update/patch/delete)はクラスター管理者のみに制限されることを推奨します。
これはたいていデフォルトで有効となっています。さらなる詳細に関しては
Authorization
Overviewを参照してください。
使用例
一度RuntimeClassがクラスターに対して設定されると、それを使用するのは非常に簡単です。PodSpecのruntimeClassName
を指定してください。
例えば
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
runtimeClassName: myclass
# ...
これは、Kubeletに対してPodを稼働させるためのRuntimeClassを使うように指示します。もし設定されたRuntimeClassが存在しない場合や、CRIが対応するハンドラーを実行できない場合、そのPodはFailed
というフェーズになります。
エラーメッセージに関しては対応するイベントを参照して下さい。
もしruntimeClassName
が指定されていない場合、デフォルトのRuntimeHandlerが使用され、これはRuntimeClassの機能が無効であるときのふるまいと同じものとなります。
CRIの設定
CRIランタイムのセットアップに関するさらなる詳細は、CRIのインストールを参照してください。
dockershim
Kubernetesのビルトインのdockershim CRIは、ランタイムハンドラーをサポートしていません。
ランタイムハンドラーは、/etc/containerd/config.toml
にあるcontainerdの設定ファイルにより設定されます。
正しいハンドラーは、そのruntime
セクションで設定されます。
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]
containerdの設定に関する詳細なドキュメントは下記を参照してください。
https://github.com/containerd/containerd/blob/main/docs/cri/config.md
ランタイムハンドラーは、/etc/crio/crio.conf
にあるCRI-Oの設定ファイルにより設定されます。
正しいハンドラーはcrio.runtime
tableで設定されます。
[crio.runtime.runtimes.${HANDLER_NAME}]
runtime_path = "${PATH_TO_BINARY}"
詳細はCRI-Oの設定に関するドキュメントを参照してください。
スケジューリング
FEATURE STATE: Kubernetes v1.16 [beta]
Kubernetes 1.16では、RuntimeClassはscheduling
フィールドを使ったクラスター内での異なる設定をサポートしています。
このフィールドによって、設定されたRuntimeClassをサポートするノードに対してPodがスケジュールされることを保証できます。
スケジューリングをサポートするためにはRuntimeClass アドミッションコントローラーを有効にしなければなりません。(1.16ではデフォルトです)
特定のRuntimeClassをサポートしているノードへPodが配置されることを保証するために、各ノードはruntimeclass.scheduling.nodeSelector
フィールドによって選択される共通のラベルを持つべきです。
RuntimeClassのnodeSelectorはアドミッション機能によりPodのnodeSelectorに統合され、効率よくノードを選択します。
もし設定が衝突した場合は、Pod作成は拒否されるでしょう。
もしサポートされているノードが他のRuntimeClassのPodが稼働しないようにtaint付与されていた場合、RuntimeClassに対してtolerations
を付与することができます。
nodeSelector
と同様に、tolerationsはPodのtolerationsにアドミッション機能によって統合され、効率よく許容されたノードを選択します。
ノードの選択とtolerationsについての詳細はノード上へのPodのスケジューリングを参照してください。
Podオーバーヘッド
FEATURE STATE: Kubernetes v1.18 [beta]
Podが稼働する時に関連する オーバーヘッド リソースを指定できます。オーバーヘッドを宣言すると、クラスター(スケジューラーを含む)がPodとリソースに関する決定を行うときにオーバーヘッドを考慮することができます。
Podオーバーヘッドを使うためには、PodOverheadフィーチャーゲートを有効にしなければなりません。(デフォルトではonです)
PodのオーバーヘッドはRuntimeClass内のoverhead
フィールドによって定義されます。
このフィールドを使用することで、RuntimeClassを使用して稼働するPodのオーバーヘッドを指定することができ、Kubernetes内部で使用されるオーバーヘッドを確保することができます。
次の項目
5 - コンテナライフサイクルフック
このページでは、kubeletにより管理されるコンテナがコンテナライフサイクルフックフレームワークを使用して、管理ライフサイクル中にイベントによって引き起こされたコードを実行する方法について説明します。
概要
Angularなどのコンポーネントライフサイクルフックを持つ多くのプログラミング言語フレームワークと同様に、Kubernetesはコンテナにライフサイクルフックを提供します。
フックにより、コンテナは管理ライフサイクル内のイベントを認識し、対応するライフサイクルフックが実行されたときにハンドラーに実装されたコードを実行できます。
コンテナフック
コンテナに公開されている2つのフックがあります。
PostStart
このフックはコンテナが作成された直後に実行されます。
しかし、フックがコンテナのENTRYPOINTの前に実行されるという保証はありません。
ハンドラーにパラメーターは渡されません。
PreStop
このフックは、APIからの要求、またはliveness/startup probeの失敗、プリエンプション、リソース競合などの管理イベントが原因でコンテナが終了する直前に呼び出されます。コンテナがすでに終了状態または完了状態にある場合にはPreStop
フックの呼び出しは失敗し、コンテナを停止するTERMシグナルが送信される前にフックは完了する必要があります。PreStop
フックが実行される前にPodの終了猶予期間のカウントダウンが開始されるので、ハンドラーの結果に関わらず、コンテナはPodの終了猶予期間内に最終的に終了します。
ハンドラーにパラメーターは渡されません。
終了動作の詳細な説明は、Termination of Podsにあります。
フックハンドラーの実装
コンテナは、フックのハンドラーを実装して登録することでそのフックにアクセスできます。
コンテナに実装できるフックハンドラーは2種類あります。
- Exec - コンテナのcgroupsと名前空間の中で、
pre-stop.sh
のような特定のコマンドを実行します。
コマンドによって消費されたリソースはコンテナに対してカウントされます。
- HTTP - コンテナ上の特定のエンドポイントに対してHTTP要求を実行します。
フックハンドラーの実行
コンテナライフサイクル管理フックが呼び出されると、Kubernetes管理システムはフックアクションにしたがってハンドラーを実行します。
httpGet
とtcpSocket
はkubeletプロセスによって実行され、exec
はコンテナの中で実行されます。
フックハンドラーの呼び出しは、コンテナを含むPodのコンテキスト内で同期しています。
これは、PostStart
フックの場合、コンテナのENTRYPOINTとフックは非同期に起動することを意味します。
しかし、フックの実行に時間がかかりすぎたりハングしたりすると、コンテナはrunning
状態になることができません。
PreStop
フックはコンテナを停止するシグナルから非同期で実行されるのではなく、TERMシグナルが送られる前に実行を完了する必要があります。
もしPreStop
フックが実行中にハングした場合、PodはTerminating
状態になり、
terminationGracePeriodSeconds
の時間切れで強制終了されるまで続きます。
この猶予時間は、PreStop
フックが実行され正常にコンテナを停止できるまでの合計時間に適用されます。
例えばterminationGracePeriodSeconds
が60で、フックの終了に55秒かかり、シグナルを受信した後にコンテナを正常に停止させるのに10秒かかる場合、コンテナは正常に停止する前に終了されてしまいます。terminationGracePeriodSeconds
が、これら2つの実行にかかる合計時間(55+10)よりも短いからです。
PostStart
またはPreStop
フックが失敗した場合、コンテナは強制終了します。
ユーザーはフックハンドラーをできるだけ軽量にするべきです。
ただし、コンテナを停止する前に状態を保存するなどの場合は、長時間のコマンド実行が必要なケースもあります。
フック配信保証
フックの配信は 少なくとも1回 を意図しています。これはフックがPostStart
やPreStop
のような任意のイベントに対して複数回呼ばれることがあることを意味します。
これを正しく処理するのはフックの実装次第です。
通常、1回の配信のみが行われます。
たとえば、HTTPフックレシーバーがダウンしていてトラフィックを受け取れない場合、再送信は試みられません。
ただし、まれに二重配信が発生することがあります。
たとえば、フックの送信中にkubeletが再起動した場合、kubeletが起動した後にフックが再送信される可能性があります。
フックハンドラーのデバッグ
フックハンドラーのログは、Podのイベントには表示されません。
ハンドラーが何らかの理由で失敗した場合は、イベントをブロードキャストします。
PostStart
の場合、これはFailedPostStartHook
イベントで、PreStop
の場合、これはFailedPreStopHook
イベントです。
失敗のFailedPreStopHook
イベントを自分自身で生成する場合には、lifecycle-events.yamlファイルに対してpostStartのコマンドを"badcommand"に変更し、適用してください。
kubectl describe pod lifecycle-demo
を実行した結果のイベントの出力例を以下に示します。
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 7s default-scheduler Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
Normal Pulled 6s kubelet Successfully pulled image "nginx" in 229.604315ms
Normal Pulling 4s (x2 over 6s) kubelet Pulling image "nginx"
Normal Created 4s (x2 over 5s) kubelet Created container lifecycle-demo-container
Normal Started 4s (x2 over 5s) kubelet Started container lifecycle-demo-container
Warning FailedPostStartHook 4s (x2 over 5s) kubelet Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
Normal Killing 4s (x2 over 5s) kubelet FailedPostStartHook
Normal Pulled 4s kubelet Successfully pulled image "nginx" in 215.66395ms
Warning BackOff 2s (x2 over 3s) kubelet Back-off restarting failed container
次の項目