これは、このセクションの複数ページの印刷可能なビューです。 印刷するには、ここをクリックしてください.
ワークロードリソース
1 - Deployment
Deployment はPodとReplicaSetの宣言的なアップデート機能を提供します。
Deploymentにおいて 理想的な状態 を記述すると、Deploymentコントローラーは指定された頻度で現在の状態を理想的な状態に変更します。Deploymentを定義することによって、新しいReplicaSetを作成したり、既存のDeploymentを削除して新しいDeploymentで全てのリソースを適用できます。
備考:
Deploymentによって作成されたReplicaSetを管理しないでください。ご自身のユースケースが以下の項目に含まれない場合、メインのKubernetesリポジトリーにIssueを作成することを検討してください。ユースケース
以下の項目はDeploymentの典型的なユースケースです。
- ReplicaSetをロールアウトするためにDeploymentの作成を行う: ReplicaSetはバックグラウンドでPodを作成します。Podの作成が完了したかどうかは、ロールアウトのステータスを確認してください。
- DeploymentのPodTemplateSpecを更新することによりPodの新しい状態を宣言する: 新しいReplicaSetが作成され、Deploymentは指定された頻度で古いReplicaSetから新しいReplicaSetへのPodの移行を管理します。新しいReplicaSetはDeploymentのリビジョンを更新します。
- Deploymentの現在の状態が不安定な場合、Deploymentのロールバックをする: ロールバックによる各更新作業は、Deploymentのリビジョンを更新します。
- より多くの負荷をさばけるように、Deploymentをスケールアップする。
- PodTemplateSpecに対する複数の修正を適用するためにDeploymentを停止(Pause)し、それを再開して新しいロールアウトを開始します。
- Deploymentのステータス をロールアウトが失敗したサインとして利用する。
- 今後必要としない古いReplicaSetのクリーンアップ
Deploymentの作成
以下はDeploymentの例です。これはnginx
Podのレプリカを3つ持つReplicaSetを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
この例では、
.metadata.name
フィールドで指定されたnginx-deployment
という名前のDeploymentが作成されます。このDeploymentは
.spec.replicas
フィールドで指定された通り、3つのレプリカPodを作成します。.spec.selector
フィールドは、Deploymentが管理するPodのラベルを定義します。ここでは、Podテンプレートにて定義されたラベル(app: nginx
)を選択しています。しかし、PodTemplate自体がそのルールを満たす限り、さらに洗練された方法でセレクターを指定することができます。備考:
`.spec.selector.matchLabels`フィールドはキーバリューペアのマップです。 `matchLabels`マップにおいて、{key, value}というペアは、keyというフィールドの値が"key"で、その演算子が"In"で、値の配列が"value"のみ含むような`matchExpressions`の要素と等しくなります。 `matchLabels`と`matchExpressions`の両方が設定された場合、条件に一致するには両方とも満たす必要があります。
template
フィールドは、以下のサブフィールドを持ちます。:- Podは
.metadata.labels
フィールドによって指定されたapp: nginx
というラベルがつけられます。 - PodTemplate、または
.template.spec
フィールドは、Podがnginx
という名前でDocker Hubにあるnginx
のバージョン1.14.2が動くコンテナを1つ動かすことを示します。 - 1つのコンテナを作成し、
.spec.template.spec.containers[0].name
フィールドを使ってnginx
という名前をつけます。
- Podは
作成を始める前に、Kubernetesクラスターが稼働していることを確認してください。 上記のDeploymentを作成するためには以下のステップにしたがってください:
- 以下のコマンドを実行してDeploymentを作成してください。
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
- Deploymentが作成されたことを確認するために、
kubectl get deployments
を実行してください。
Deploymentがまだ作成中の場合、コマンドの実行結果は以下のとおりです。
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 0/3 0 0 1s
クラスターにてDeploymentを調査するとき、以下のフィールドが出力されます。
NAME
は、クラスター内にあるDeploymentの名前一覧です。READY
は、ユーザーが使用できるアプリケーションのレプリカの数です。使用可能な数/理想的な数の形式で表示されます。UP-TO-DATE
は、理想的な状態を満たすためにアップデートが完了したレプリカの数です。AVAILABLE
は、ユーザーが利用可能なレプリカの数です。AGE
は、アプリケーションが稼働してからの時間です。
.spec.replicas
フィールドの値によると、理想的なレプリカ数は3であることがわかります。
- Deploymentのロールアウトステータスを確認するために、
kubectl rollout status deployment.v1.apps/nginx-deployment
を実行してください。
コマンドの実行結果は以下のとおりです。
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out
- 数秒後、再度
kubectl get deployments
を実行してください。 コマンドの実行結果は以下のとおりです。
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 18s
Deploymentが3つ全てのレプリカを作成して、全てのレプリカが最新(Podが最新のPodテンプレートを含んでいる)になり、利用可能となっていることを確認してください。
- Deploymentによって作成されたReplicaSet(
rs
)を確認するにはkubectl get rs
を実行してください。コマンドの実行結果は以下のとおりです:
NAME DESIRED CURRENT READY AGE
nginx-deployment-75675f5897 3 3 3 18s
ReplicaSetの出力には次のフィールドが表示されます:
NAME
は、名前空間内にあるReplicaSetの名前の一覧です。DESIRED
は、アプリケーションの理想的な レプリカ の値です。これはDeploymentを作成したときに定義したもので、これが 理想的な状態 と呼ばれるものです。CURRENT
は現在実行されているレプリカの数です。READY
は、ユーザーが使用できるアプリケーションのレプリカの数です。AGE
は、アプリケーションが稼働してからの時間です。
ReplicaSetの名前は[Deployment名]-[ランダム文字列]
という形式になることに注意してください。ランダム文字列はランダムに生成され、pod-template-hashをシードとして使用します。
- 各Podにラベルが自動的に付けられるのを確認するには
kubectl get pods --show-labels
を実行してください。 コマンドの実行結果は以下のとおりです:
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-75675f5897-7ci7o 1/1 Running 0 18s app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-kzszj 1/1 Running 0 18s app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-qqcnn 1/1 Running 0 18s app=nginx,pod-template-hash=75675f5897
作成されたReplicaSetはnginx
Podを3つ作成することを保証します。
備考:
Deploymentに対して適切なセレクターとPodテンプレートのラベルを設定する必要があります(このケースではapp: nginx
)。
ラベルやセレクターを他のコントローラーと重複させないでください(他のDeploymentやStatefulSetを含む)。Kubernetesはユーザーがラベルを重複させることを阻止しないため、複数のコントローラーでセレクターの重複が発生すると、コントローラー間で衝突し予期せぬふるまいをすることになります。
pod-template-hashラベル
注意:
このラベルを変更しないでください。pod-template-hash
ラベルはDeploymentコントローラーによってDeploymentが作成し適用した各ReplicaSetに対して追加されます。
このラベルはDeploymentが管理するReplicaSetが重複しないことを保証します。このラベルはReplicaSetのPodTemplate
をハッシュ化することにより生成され、生成されたハッシュ値はラベル値としてReplicaSetセレクター、Podテンプレートラベル、ReplicaSetが作成した全てのPodに対して追加されます。
Deploymentの更新
備考:
Deploymentのロールアウトは、DeploymentのPodテンプレート(この場合.spec.template
)が変更された場合にのみトリガーされます。例えばテンプレートのラベルもしくはコンテナイメージが更新された場合です。Deploymentのスケールのような更新では、ロールアウトはトリガーされません。Deploymentを更新するには以下のステップに従ってください。
nginxのPodで、
nginx:1.14.2
イメージの代わりにnginx:1.16.1
を使うように更新します。kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
または単に次のコマンドを使用します。
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
実行結果は以下のとおりです。
deployment.apps/nginx-deployment image updated
また、Deploymentを
編集
して、.spec.template.spec.containers[0].image
をnginx:1.14.2
からnginx:1.16.1
に変更することができます。kubectl edit deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
deployment.apps/nginx-deployment edited
ロールアウトのステータスを確認するには、以下のコマンドを実行してください。
kubectl rollout status deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
もしくは
deployment "nginx-deployment" successfully rolled out
更新されたDeploymentのさらなる情報を取得するには、以下を確認してください。
ロールアウトが成功したあと、
kubectl get deployments
を実行してDeploymentを確認できます。 実行結果は以下のとおりです。NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 36s
Deploymentが新しいReplicaSetを作成してPodを更新させたり、新しいReplicaSetのレプリカを3にスケールアップさせたり、古いReplicaSetのレプリカを0にスケールダウンさせるのを確認するには
kubectl get rs
を実行してください。kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-deployment-1564180365 3 3 3 6s nginx-deployment-2035384211 0 0 0 36s
get pods
を実行させると、新しいPodのみ確認できます。kubectl get pods
実行結果は以下のとおりです。
NAME READY STATUS RESTARTS AGE nginx-deployment-1564180365-khku8 1/1 Running 0 14s nginx-deployment-1564180365-nacti 1/1 Running 0 14s nginx-deployment-1564180365-z9gth 1/1 Running 0 14s
次にPodを更新させたいときは、DeploymentのPodテンプレートを再度更新するだけです。
Deploymentは、Podが更新されている間に特定の数のPodのみ停止状態になることを保証します。デフォルトでは、目標とするPod数の少なくとも75%が稼働状態であることを保証します(25% max unavailable)。
また、DeploymentはPodが更新されている間に、目標とするPod数を特定の数まで超えてPodを稼働させることを保証します。デフォルトでは、目標とするPod数に対して最大でも125%を超えてPodを稼働させることを保証します(25% max surge)。
例えば、上記で説明したDeploymentの状態を注意深く見ると、最初に新しいPodが作成され、次に古いPodが削除されるのを確認できます。十分な数の新しいPodが稼働するまでは、Deploymentは古いPodを削除しません。また十分な数の古いPodが削除しない限り新しいPodは作成されません。少なくとも2つのPodが利用可能で、最大でもトータルで4つのPodが利用可能になっていることを保証します。
Deploymentの詳細情報を取得します。
kubectl describe deployments
実行結果は以下のとおりです。
Name: nginx-deployment Namespace: default CreationTimestamp: Thu, 30 Nov 2017 10:56:25 +0000 Labels: app=nginx Annotations: deployment.kubernetes.io/revision=2 Selector: app=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.16.1 Port: 80/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: nginx-deployment-1564180365 (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 2m deployment-controller Scaled up replica set nginx-deployment-2035384211 to 3 Normal ScalingReplicaSet 24s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 1 Normal ScalingReplicaSet 22s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 2 Normal ScalingReplicaSet 22s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 2 Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 1 Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 3 Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 0
最初にDeploymentを作成した時、ReplicaSet(nginx-deployment-2035384211)を作成してすぐにレプリカ数を3にスケールするのを確認できます。Deploymentを更新すると新しいReplicaSet(nginx-deployment-1564180365)を作成してレプリカ数を1にスケールアップし、古いReplicaSeetを2にスケールダウンさせます。これは常に最低でも2つのPodが利用可能で、かつ最大4つのPodが作成されている状態にするためです。Deploymentは同じローリングアップ戦略に従って新しいReplicaSetのスケールアップと古いReplicaSetのスケールダウンを続けます。最終的に新しいReplicaSetを3にスケールアップさせ、古いReplicaSetを0にスケールダウンさせます。
ロールオーバー (リアルタイムでの複数のPodの更新)
Deploymentコントローラーにより、新しいDeploymentが観測される度にReplicaSetが作成され、理想とするレプリカ数のPodを作成します。Deploymentが更新されると、既存のReplicaSetが管理するPodのラベルが.spec.selector
にマッチするが、テンプレートが.spec.template
にマッチしない場合はスケールダウンされます。最終的に、新しいReplicaSetは.spec.replicas
の値にスケールアップされ、古いReplicaSetは0にスケールダウンされます。
Deploymentのロールアウトが進行中にDeploymentを更新すると、Deploymentは更新する毎に新しいReplicaSetを作成してスケールアップさせ、以前にスケールアップしたReplicaSetのロールオーバーを行います。Deploymentは更新前のReplicaSetを古いReplicaSetのリストに追加し、スケールダウンを開始します。
例えば、5つのレプリカを持つnginx:1.14.2
のDeploymentを作成し、nginx:1.14.2
の3つのレプリカが作成されているときに5つのレプリカを持つnginx:1.16.1
に更新します。このケースではDeploymentは作成済みのnginx:1.14.2
の3つのPodをすぐに削除し、nginx:1.16.1
のPodの作成を開始します。nginx:1.14.2
の5つのレプリカを全て作成するのを待つことはありません。
ラベルセレクターの更新
通常、ラベルセレクターを更新することは推奨されません。事前にラベルセレクターの使い方を計画しておきましょう。いかなる場合であっても更新が必要なときは十分に注意を払い、変更時の影響範囲を把握しておきましょう。
備考:
apps/v1
API バージョンにおいて、Deploymentのラベルセレクターは作成後に不変となります。- セレクターの追加は、Deployment Specのテンプレートラベルも新しいラベルで更新する必要があります。そうでない場合はバリデーションエラーが返されます。この変更は重複がない更新となります。これは新しいセレクターは古いセレクターを持つReplicaSetとPodを選択せず、結果として古い全てのReplicaSetがみなし子状態になり、新しいReplicaSetを作成することを意味します。
- セレクターの更新により、セレクターキー内の既存の値が変更されます。これにより、セレクターの追加と同じふるまいをします。
- セレクターの削除により、Deploymentのセレクターから存在している値を削除します。これはPodテンプレートのラベルに関する変更を要求しません。既存のReplicaSetはみなし子状態にならず、新しいReplicaSetは作成されませんが、削除されたラベルは既存のPodとReplicaSetでは残り続けます。
Deploymentのロールバック
例えば、クラッシュループ状態などのようにDeploymentが不安定な場合においては、Deploymentをロールバックしたくなることがあります。Deploymentの全てのロールアウト履歴は、いつでもロールバックできるようにデフォルトでシステムに保持されています(リビジョン履歴の上限は設定することで変更可能です)。
備考:
Deploymentのリビジョンは、Deploymentのロールアウトがトリガーされた時に作成されます。これはDeploymentのPodテンプレート(.spec.template
)が変更されたときのみ新しいリビジョンが作成されることを意味します。Deploymentのスケーリングなど、他の種類の更新においてはDeploymentのリビジョンは作成されません。これは手動もしくはオートスケーリングを同時に行うことができるようにするためです。これは過去のリビジョンにロールバックするとき、DeploymentのPodテンプレートの箇所のみロールバックされることを意味します。nginx:1.16.1
の代わりにnginx:1.161
というイメージに更新して、Deploymentの更新中にタイプミスをしたと仮定します。kubectl set image deployment/nginx-deployment nginx=nginx:1.161
実行結果は以下のとおりです。
deployment.apps/nginx-deployment image updated
このロールアウトはうまくいきません。ロールアウトのステータスを見るとそれを確認できます。
kubectl rollout status deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
ロールアウトのステータスの確認は、Ctrl-Cを押すことで停止できます。ロールアウトがうまく行かないときは、Deploymentのステータスを読んでさらなる情報を得てください。
古いレプリカ数(
nginx-deployment-1564180365
andnginx-deployment-2035384211
)が2になっていることを確認でき、新しいレプリカ数(nginx-deployment-3066724191)は1になっています。kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-deployment-1564180365 3 3 3 25s nginx-deployment-2035384211 0 0 0 36s nginx-deployment-3066724191 1 1 0 6s
作成されたPodを確認していると、新しいReplicaSetによって作成された1つのPodはコンテナイメージのpullに失敗し続けているのがわかります。
kubectl get pods
実行結果は以下のとおりです。
NAME READY STATUS RESTARTS AGE nginx-deployment-1564180365-70iae 1/1 Running 0 25s nginx-deployment-1564180365-jbqqo 1/1 Running 0 25s nginx-deployment-1564180365-hysrc 1/1 Running 0 25s nginx-deployment-3066724191-08mng 0/1 ImagePullBackOff 0 6s
備考:
Deploymentコントローラーは、この悪い状態のロールアウトを自動的に停止し、新しいReplicaSetのスケールアップを止めます。これはユーザーが指定したローリングアップデートに関するパラメーター(特に`maxUnavailable`)に依存します。デフォルトではKubernetesがこの値を25%に設定します。
Deploymentの詳細情報を取得します。
kubectl describe deployment
実行結果は以下のとおりです。
Name: nginx-deployment Namespace: default CreationTimestamp: Tue, 15 Mar 2016 14:48:04 -0700 Labels: app=nginx Selector: app=nginx Replicas: 3 desired | 1 updated | 4 total | 3 available | 1 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.161 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True ReplicaSetUpdated OldReplicaSets: nginx-deployment-1564180365 (3/3 replicas created) NewReplicaSet: nginx-deployment-3066724191 (1/1 replicas created) Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-2035384211 to 3 22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 1 22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 2 22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 2 21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 1 21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 3 13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 0 13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3066724191 to 1
これを修正するために、Deploymentを安定した状態の過去のリビジョンに更新する必要があります。
Deploymentのロールアウト履歴の確認
ロールアウトの履歴を確認するには、以下の手順に従って下さい。
最初に、Deploymentのリビジョンを確認します。
kubectl rollout history deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
deployments "nginx-deployment" REVISION CHANGE-CAUSE 1 kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml 2 kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 3 kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.161
CHANGE-CAUSE
はリビジョンの作成時にDeploymentのkubernetes.io/change-cause
アノテーションからリビジョンにコピーされます。以下の方法によりCHANGE-CAUSE
メッセージを指定できます。kubectl annotate deployment.v1.apps/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1"
の実行によりアノテーションを追加します。- リソースのマニフェストを手動で編集します。
各リビジョンの詳細を確認するためには以下のコマンドを実行してください。
kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
実行結果は以下のとおりです。
deployments "nginx-deployment" revision 2 Labels: app=nginx pod-template-hash=1159050644 Annotations: kubernetes.io/change-cause=kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 Containers: nginx: Image: nginx:1.16.1 Port: 80/TCP QoS Tier: cpu: BestEffort memory: BestEffort Environment Variables: <none> No volumes.
過去のリビジョンにロールバックする
現在のリビジョンから過去のリビジョン(リビジョン番号2)にロールバックさせるには、以下の手順に従ってください。
現在のリビジョンから過去のリビジョンにロールバックします。
kubectl rollout undo deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
deployment.apps/nginx-deployment rolled back
その他に、
--to-revision
を指定することにより特定のリビジョンにロールバックできます。kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
実行結果は以下のとおりです。
deployment.apps/nginx-deployment rolled back
ロールアウトに関連したコマンドのさらなる情報は
kubectl rollout
を参照してください。Deploymentが過去の安定したリビジョンにロールバックされました。Deploymentコントローラーによって、リビジョン番号2にロールバックする
DeploymentRollback
イベントが作成されたのを確認できます。ロールバックが成功し、Deploymentが正常に稼働していることを確認するために、以下のコマンドを実行してください。
kubectl get deployment nginx-deployment
実行結果は以下のとおりです。
NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 30m
Deploymentの詳細情報を取得します。
kubectl describe deployment nginx-deployment
実行結果は以下のとおりです。
Name: nginx-deployment Namespace: default CreationTimestamp: Sun, 02 Sep 2018 18:17:55 -0500 Labels: app=nginx Annotations: deployment.kubernetes.io/revision=4 kubernetes.io/change-cause=kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 Selector: app=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.16.1 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: nginx-deployment-c4747d96c (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deployment-75675f5897 to 3 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-c4747d96c to 1 Normal ScalingReplicaSet 11m deployment-controller Scaled down replica set nginx-deployment-75675f5897 to 2 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-c4747d96c to 2 Normal ScalingReplicaSet 11m deployment-controller Scaled down replica set nginx-deployment-75675f5897 to 1 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-c4747d96c to 3 Normal ScalingReplicaSet 11m deployment-controller Scaled down replica set nginx-deployment-75675f5897 to 0 Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set nginx-deployment-595696685f to 1 Normal DeploymentRollback 15s deployment-controller Rolled back deployment "nginx-deployment" to revision 2 Normal ScalingReplicaSet 15s deployment-controller Scaled down replica set nginx-deployment-595696685f to 0
Deploymentのスケーリング
以下のコマンドを実行させてDeploymentをスケールできます。
kubectl scale deployment.v1.apps/nginx-deployment --replicas=10
実行結果は以下のとおりです。
deployment.apps/nginx-deployment scaled
クラスター内で水平Podオートスケーラーが有効になっていると仮定します。ここでDeploymentのオートスケーラーを設定し、稼働しているPodのCPU使用量に基づいて、稼働させたいPodのレプリカ数の最小値と最大値を設定できます。
kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80
実行結果は以下のとおりです。
deployment.apps/nginx-deployment scaled
比例スケーリング
Deploymentのローリングアップデートは、同時に複数のバージョンのアプリケーションの稼働をサポートします。ユーザーやオートスケーラーがローリングアップデートをロールアウト中(更新中もしくは一時停止中)のDeploymentに対して行うと、Deploymentコントローラーはリスクを削減するために既存のアクティブなReplicaSetのレプリカのバランシングを行います。これを比例スケーリング と呼びます。
レプリカ数が10、maxSurge=3、maxUnavailable=2であるDeploymentが稼働している例です。
Deployment内で10のレプリカが稼働していることを確認します。
kubectl get deploy
実行結果は以下のとおりです。
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx-deployment 10 10 10 10 50s
クラスター内で、解決できない新しいイメージに更新します。
kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:sometag
実行結果は以下のとおりです。
deployment.apps/nginx-deployment image updated
イメージの更新は新しいReplicaSet nginx-deployment-1989198191へのロールアウトを開始させます。しかしロールアウトは、上述した
maxUnavailable
の要求によりブロックされます。ここでロールアウトのステータスを確認します。kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-deployment-1989198191 5 5 0 9s nginx-deployment-618515232 8 8 8 1m
次にDeploymentのスケーリングをするための新しい要求が発生します。オートスケーラーはDeploymentのレプリカ数を15に増やします。Deploymentコントローラーは新しい5つのレプリカをどこに追加するか決める必要がでてきます。比例スケーリングを使用していない場合、5つのレプリカは全て新しいReplicaSetに追加されます。比例スケーリングでは、追加されるレプリカは全てのReplicaSetに分散されます。比例割合が大きいものはレプリカ数の大きいReplicaSetとなり、比例割合が低いときはレプリカ数の小さいReplicaSetとなります。残っているレプリカはもっとも大きいレプリカ数を持つReplicaSetに追加されます。レプリカ数が0のReplicaSetはスケールアップされません。
上記の例では、3つのレプリカが古いReplicaSetに追加され、2つのレプリカが新しいReplicaSetに追加されました。ロールアウトの処理では、新しいレプリカ数のPodが正常になったと仮定すると、最終的に新しいReplicaSetに全てのレプリカを移動させます。これを確認するためには以下のコマンドを実行して下さい。
kubectl get deploy
実行結果は以下のとおりです。
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 15 18 7 8 7m
ロールアウトのステータスでレプリカがどのように各ReplicaSetに追加されるか確認できます。
kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE
nginx-deployment-1989198191 7 7 0 7m
nginx-deployment-618515232 11 11 11 7m
Deployment更新の一時停止と再開
ユーザーは1つ以上の更新処理をトリガーする前に更新の一時停止と再開ができます。これにより、不必要なロールアウトを実行することなく一時停止と再開を行う間に複数の修正を反映できます。
例えば、作成直後のDeploymentを考えます。 Deploymentの詳細情報を確認します。
kubectl get deploy
実行結果は以下のとおりです。
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 3 3 3 3 1m
ロールアウトのステータスを確認します。
kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-2142116321 3 3 3 1m
以下のコマンドを実行して更新処理の一時停止を行います。
kubectl rollout pause deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
deployment.apps/nginx-deployment paused
次にDeploymentのイメージを更新します。
kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
実行結果は以下のとおりです。
deployment.apps/nginx-deployment image updated
新しいロールアウトが開始されていないことを確認します。
kubectl rollout history deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
deployments "nginx" REVISION CHANGE-CAUSE 1 <none>
Deploymentの更新に成功したことを確認するためにロールアウトのステータスを確認します。
kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-2142116321 3 3 3 2m
更新は何度でも実行できます。例えば、Deploymentが使用するリソースを更新します。
kubectl set resources deployment.v1.apps/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
実行結果は以下のとおりです。
deployment.apps/nginx-deployment resource requirements updated
一時停止する前の初期状態では更新処理は機能しますが、Deploymentが一時停止されている間は新しい更新処理は反映されません。
最後に、Deploymentの稼働を再開させ、新しいReplicaSetが更新内容を全て反映させているのを確認します。
kubectl rollout resume deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
deployment.apps/nginx-deployment resumed
更新処理が完了するまでロールアウトのステータスを確認します。
kubectl get rs -w
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-2142116321 2 2 2 2m nginx-3926361531 2 2 0 6s nginx-3926361531 2 2 1 18s nginx-2142116321 1 2 2 2m nginx-2142116321 1 2 2 2m nginx-3926361531 3 2 1 18s nginx-3926361531 3 2 1 18s nginx-2142116321 1 1 1 2m nginx-3926361531 3 3 1 18s nginx-3926361531 3 3 2 19s nginx-2142116321 0 1 1 2m nginx-2142116321 0 1 1 2m nginx-2142116321 0 0 0 2m nginx-3926361531 3 3 3 20s
最新のロールアウトのステータスを確認します。
kubectl get rs
実行結果は以下のとおりです。
NAME DESIRED CURRENT READY AGE nginx-2142116321 0 0 0 2m nginx-3926361531 3 3 3 28s
備考:
Deploymentの稼働を再開させない限り、一時停止したDeploymentをロールバックすることはできません。Deploymentのステータス
Deploymentは、そのライフサイクルの間に様々な状態に遷移します。新しいReplicaSetへのロールアウト中は進行中になり、その後は完了し、また失敗にもなります。
Deploymentの更新処理
以下のタスクが実行中のとき、KubernetesはDeploymentの状態を 進行中 にします。
- Deploymentが新しいReplicaSetを作成します。
- Deploymentが新しいReplicaSetをスケールアップさせています。
- Deploymentが古いReplicaSetをスケールダウンさせています。
- 新しいPodが準備中もしくは利用可能な状態になります(少なくともMinReadySecondsの間は準備中になります)。
kubectl rollout status
を実行すると、Deploymentの進行状態を確認できます。
Deploymentの更新処理の完了
Deploymentが以下の状態になったとき、KubernetesはDeploymentのステータスを 完了 にします。
- Deploymentの全てのレプリカが、指定された最新のバージョンに更新されます。これは指定した更新処理が完了したことを意味します。
- Deploymentの全てのレプリカが利用可能になります。
- Deploymentの古いレプリカが1つも稼働していません。
kubectl rollout status
を実行して、Deploymentの更新が完了したことを確認できます。ロールアウトが正常に完了するとkubectl rollout status
の終了コードが0で返されます。
kubectl rollout status deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out
そしてkubectl rollout
の終了ステータスが0となります(成功です):
echo $?
0
Deploymentの更新処理の失敗
新しいReplicaSetのデプロイが完了せず、更新処理が止まる場合があります。これは主に以下の要因によるものです。
- 不十分なリソースの割り当て
- ReadinessProbeの失敗
- コンテナイメージの取得ができない
- 不十分なパーミッション
- リソースリミットのレンジ
- アプリケーションランタイムの設定の不備
このような状況を検知する1つの方法として、Deploymentのリソース定義でデッドラインのパラメーターを指定します(.spec.progressDeadlineSeconds
)。.spec.progressDeadlineSeconds
はDeploymentの更新が停止したことを示す前にDeploymentコントローラーが待つ秒数を示します。
以下のkubectl
コマンドでリソース定義にprogressDeadlineSeconds
を設定します。これはDeploymentの更新が止まってから10分後に、コントローラーが失敗を通知させるためです。
kubectl patch deployment.v1.apps/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
実行結果は以下のとおりです。
deployment.apps/nginx-deployment patched
一度デッドラインを超過すると、DeploymentコントローラーはDeploymentの.status.conditions
に以下のDeploymentConditionを追加します。
- Type=Progressing
- Status=False
- Reason=ProgressDeadlineExceeded
ステータスの状態に関するさらなる情報はKubernetes APIの規則を参照してください。
備考:
Kubernetesは停止状態のDeploymentに対して、ステータス状態を報告する以外のアクションを実行しません。高レベルのオーケストレーターはこれを利用して、状態に応じて行動できます。例えば、前のバージョンへのDeploymentのロールバックが挙げられます。備考:
Deploymentを停止すると、Kubernetesは指定したデッドラインを超えたかどうかチェックしません。 ロールアウトの途中でもDeploymentを安全に一時停止でき、デッドラインを超えたイベントをトリガーすることなく再開できます。設定したタイムアウトの秒数が小さかったり、一時的なエラーとして扱える他の種類のエラーが原因となり、Deploymentで一時的なエラーが出る場合があります。例えば、リソースの割り当てが不十分な場合を考えます。Deploymentの詳細情報を確認すると、以下のセクションが表示されます。
kubectl describe deployment nginx-deployment
実行結果は以下のとおりです。
<...>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True ReplicaSetUpdated
ReplicaFailure True FailedCreate
<...>
kubectl get deployment nginx-deployment -o yaml
を実行すると、Deploymentのステータスは以下のようになります。
status:
availableReplicas: 2
conditions:
- lastTransitionTime: 2016-10-04T12:25:39Z
lastUpdateTime: 2016-10-04T12:25:39Z
message: Replica set "nginx-deployment-4262182780" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
- lastTransitionTime: 2016-10-04T12:25:42Z
lastUpdateTime: 2016-10-04T12:25:42Z
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: 2016-10-04T12:25:39Z
lastUpdateTime: 2016-10-04T12:25:39Z
message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
object-counts, requested: pods=1, used: pods=3, limited: pods=2'
reason: FailedCreate
status: "True"
type: ReplicaFailure
observedGeneration: 3
replicas: 2
unavailableReplicas: 2
最後に、一度Deploymentの更新処理のデッドラインを越えると、KubernetesはDeploymentのステータスと進行中の状態を更新します。
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceeded
ReplicaFailure True FailedCreate
Deploymentか他のリソースコントローラーのスケールダウンを行うか、使用している名前空間内でリソースの割り当てを増やすことで、リソースの割り当て不足の問題に対処できます。割り当て条件を満たすと、DeploymentコントローラーはDeploymentのロールアウトを完了させ、Deploymentのステータスが成功状態になるのを確認できます(Status=True
とReason=NewReplicaSetAvailable
)。
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
Status=True
のType=Available
は、Deploymentが最小可用性の状態であることを意味します。最小可用性は、Deploymentの更新戦略において指定されているパラメーターにより決定されます。Status=True
のType=Progressing
は、Deploymentのロールアウトの途中で、更新処理が進行中であるか、更新処理が完了し、必要な最小数のレプリカが利用可能であることを意味します(各TypeのReason項目を確認してください。このケースでは、Reason=NewReplicaSetAvailable
はDeploymentの更新が完了したことを意味します)。
kubectl rollout status
を実行してDeploymentが更新に失敗したかどうかを確認できます。kubectl rollout status
はDeploymentが更新処理のデッドラインを超えたときに0以外の終了コードを返します。
kubectl rollout status deployment.v1.apps/nginx-deployment
実行結果は以下のとおりです。
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline
そしてkubectl rollout
の終了ステータスが1となります(エラーを示しています):
echo $?
1
失敗したDeploymentの操作
更新完了したDeploymentに適用した全てのアクションは、更新失敗したDeploymentに対しても適用されます。スケールアップ、スケールダウンができ、前のリビジョンへのロールバックや、Deploymentのテンプレートに複数の更新を適用させる必要があるときは一時停止もできます。
古いリビジョンのクリーンアップポリシー
Deploymentが管理する古いReplicaSetをいくつ保持するかを指定するために、.spec.revisionHistoryLimit
フィールドを設定できます。この値を超えた古いReplicaSetはバックグラウンドでガーベージコレクションの対象となって削除されます。デフォルトではこの値は10です。
備考:
このフィールドを明示的に0に設定すると、Deploymentの全ての履歴を削除します。従って、Deploymentはロールバックできません。カナリアパターンによるデプロイ
Deploymentを使って一部のユーザーやサーバーに対してリリースのロールアウトをしたい場合、リソースの管理に記載されているカナリアパターンに従って、リリース毎に1つずつ、複数のDeploymentを作成できます。
Deployment Specの記述
他の全てのKubernetesの設定と同様に、Deploymentは.apiVersion
、.kind
や.metadata
フィールドを必要とします。
設定ファイルの利用に関する情報はアプリケーションのデプロイを参照してください。コンテナの設定に関してはリソースを管理するためのkubectlの使用を参照してください。
Deploymentオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。
Deploymentは.spec
セクションも必要とします。
Podテンプレート
.spec.template
と.spec.selector
は.spec
における必須のフィールドです。
.spec.template
はPodテンプレートです。これは.spec内でネストされていないことと、apiVersion
やkind
を持たないことを除いてはPodと同じスキーマとなります。
Podの必須フィールドに加えて、Deployment内のPodテンプレートでは適切なラベルと再起動ポリシーを設定しなくてはなりません。ラベルは他のコントローラーと重複しないようにしてください。ラベルについては、セレクターを参照してください。
.spec.template.spec.restartPolicy
がAlways
に等しいときのみ許可されます。これはテンプレートで指定されていない場合のデフォルト値です。
レプリカ数
.spec.replias
は理想的なPodの数を指定するオプションのフィールドです。デフォルトは1です。
セレクター
.spec.selector
は必須フィールドで、Deploymentによって対象とされるPodのラベルセレクターを指定します。
.spec.selector
は.spec.template.metadata.labels
と一致している必要があり、一致しない場合はAPIによって拒否されます。
apps/v1
バージョンにおいて、.spec.selector
と.metadata.labels
が指定されていない場合、.spec.template.metadata.labels
の値に初期化されません。そのため.spec.selector
と.metadata.labels
を明示的に指定する必要があります。またapps/v1
のDeploymentにおいて.spec.selector
は作成後に不変になります。
Deploymentのテンプレートが.spec.template
と異なる場合や、.spec.replicas
の値を超えてPodが稼働している場合、Deploymentはセレクターに一致するラベルを持つPodを削除します。Podの数が理想状態より少ない場合Deploymentは.spec.template
をもとに新しいPodを作成します。
備考:
Deploymentのセレクターに一致するラベルを持つPodを直接作成したり、他のDeploymentやReplicaSetやReplicationControllerによって作成するべきではありません。作成してしまうと、最初のDeploymentがラベルに一致する新しいPodを作成したとみなされます。こうなったとしても、Kubernetesは処理を止めません。セレクターが重複する複数のコントローラーを持つとき、そのコントローラーは互いに競合状態となり、正しくふるまいません。
更新戦略
.spec.strategy
は古いPodから新しいPodに置き換える際の更新戦略を指定します。.spec.strategy.type
は"Recreate"もしくは"RollingUpdate"を指定できます。デフォルトは"RollingUpdate"です。
Deploymentの再作成
.spec.strategy.type==Recreate
と指定されているとき、既存の全てのPodは新しいPodが作成される前に削除されます。
備考:
これは更新のための作成の前にPodを停止する事を保証するだけです。Deploymentを更新する場合、古いリビジョンのPodは全てすぐに停止されます。削除に成功するまでは、新しいリビジョンのPodは作成されません。手動でPodを削除すると、ライフサイクルがReplicaSetに制御されているのですぐに置き換えが実施されます(たとえ古いPodがまだ停止中のステータスでも)。Podに"高々この程度の"保証を求めるならばStatefulSetの使用を検討してください。Deploymentのローリングアップデート
.spec.strategy.type==RollingUpdate
と指定されているとき、DeploymentはローリングアップデートによりPodを更新します。ローリングアップデートの処理をコントロールするためにmaxUnavailable
とmaxSurge
を指定できます。
Max Unavailable
.spec.strategy.rollingUpdate.maxUnavailable
はオプションのフィールドで、更新処理において利用不可となる最大のPod数を指定します。値は絶対値(例: 5)を指定するか、理想状態のPodのパーセンテージを指定します(例: 10%)。パーセンテージを指定した場合、絶対値は小数切り捨てされて計算されます。.spec.strategy.rollingUpdate.maxSurge
が0に指定されている場合、この値を0にできません。デフォルトでは25%です。
例えば、この値が30%と指定されているとき、ローリングアップデートが開始すると古いReplicaSetはすぐに理想状態の70%にスケールダウンされます。一度新しいPodが稼働できる状態になると、古いReplicaSetはさらにスケールダウンされ、続いて新しいReplicaSetがスケールアップされます。この間、利用可能なPodの総数は理想状態のPodの少なくとも70%以上になるように保証されます。
Max Surge
.spec.strategy.rollingUpdate.maxSurge
はオプションのフィールドで、理想状態のPod数を超えて作成できる最大のPod数を指定します。値は絶対値(例: 5)を指定するか、理想状態のPodのパーセンテージを指定します(例: 10%)。パーセンテージを指定した場合、絶対値は小数切り上げで計算されます。MaxUnavailable
が0に指定されている場合、この値を0にできません。デフォルトでは25%です。
例えば、この値が30%と指定されているとき、ローリングアップデートが開始すると新しいReplicaSetはすぐに更新されます。このとき古いPodと新しいPodの総数は理想状態の130%を超えないように更新されます。一度古いPodが削除されると、新しいReplicaSetはさらにスケールアップされます。この間、利用可能なPodの総数は理想状態のPodに対して最大130%になるように保証されます。
Progress Deadline Seconds
.spec.progressDeadlineSeconds
はオプションのフィールドで、システムがDeploymentの更新に失敗したと判断するまでに待つ秒数を指定します。更新に失敗したと判断されたとき、リソースのステータスはType=Progressing
、Status=False
かつReason=ProgressDeadlineExceeded
となるのを確認できます。DeploymentコントローラーはDeploymentの更新のリトライし続けます。デフォルト値は600です。今後、自動的なロールバックが実装されたとき、更新失敗状態になるとすぐにDeploymentコントローラーがロールバックを行うようになります。
この値が指定されているとき、.spec.minReadySeconds
より大きい値を指定する必要があります。
Min Ready Seconds
.spec.minReadySeconds
はオプションのフィールドで、新しく作成されたPodが利用可能となるために、最低どれくらいの秒数コンテナがクラッシュすることなく稼働し続ければよいかを指定するものです。デフォルトでは0です(Podは作成されるとすぐに利用可能と判断されます)。Podが利用可能と判断された場合についてさらに学ぶためにContainer Probesを参照してください。
リビジョン履歴の保持上限
Deploymentのリビジョン履歴は、Deploymentが管理するReplicaSetに保持されています。
.spec.revisionHistoryLimit
はオプションのフィールドで、ロールバック可能な古いReplicaSetの数を指定します。この古いReplicaSetはetcd
内のリソースを消費し、kubectl get rs
の出力結果を見にくくします。Deploymentの各リビジョンの設定はReplicaSetに保持されます。このため一度古いReplicaSetが削除されると、そのリビジョンのDeploymentにロールバックすることができなくなります。デフォルトでは10もの古いReplicaSetが保持されます。しかし、この値の最適値は新しいDeploymentの更新頻度と安定性に依存します。
さらに詳しく言うと、この値を0にすると、0のレプリカを持つ古い全てのReplicaSetが削除されます。このケースでは、リビジョン履歴が完全に削除されているため新しいDeploymentのロールアウトを元に戻すことができません。
paused
.spec.paused
はオプションのboolean値で、Deploymentの一時停止と再開のための値です。一時停止されているものと、そうでないものとの違いは、一時停止されているDeploymentはPodTemplateSpecのいかなる変更があってもロールアウトがトリガーされないことです。デフォルトではDeploymentは一時停止していない状態で作成されます。
2 - ReplicaSet
ReplicaSetの目的は、どのような時でも安定したレプリカPodのセットを維持することです。これは、理想的なレプリカ数のPodが利用可能であることを保証するものとして使用されます。
ReplicaSetがどのように動くか
ReplicaSetは、ReplicaSetが対象とするPodをどう特定するかを示すためのセレクターや、稼働させたいPodのレプリカ数、Podテンプレート(理想のレプリカ数の条件を満たすために作成される新しいPodのデータを指定するために用意されるもの)といったフィールドとともに定義されます。ReplicaSetは、指定された理想のレプリカ数にするためにPodの作成と削除を行うことにより、その目的を達成します。ReplicaSetが新しいPodを作成するとき、ReplicaSetはそのPodテンプレートを使用します。
ReplicaSetがそのPod群と連携するためのリンクは、Podのmetadata.ownerReferencesというフィールド(現在のオブジェクトが所有されているリソースを指定する)を介して作成されます。ReplicaSetによって所持された全てのPodは、それらのownerReferences
フィールドにReplicaSetを特定する情報を保持します。このリンクを通じて、ReplicaSetは管理しているPodの状態を把握したり、その後の実行計画を立てます。
ReplicaSetは、そのセレクターを使用することにより、所有するための新しいPodを特定します。もしownerReference
フィールドの値を持たないPodか、ownerReference
フィールドの値が コントローラーでないPodで、そのPodがReplicaSetのセレクターとマッチした場合に、そのPodは即座にそのReplicaSetによって所有されます。
ReplicaSetを使うとき
ReplicaSetはどんな時でも指定された数のPodのレプリカが稼働することを保証します。しかし、DeploymentはReplicaSetを管理する、より上位レベルの概念で、Deploymentはその他の多くの有益な機能と共に、宣言的なPodのアップデート機能を提供します。それゆえ、我々はユーザーが独自のアップデートオーケストレーションを必要としたり、アップデートを全く必要としないような場合を除いて、ReplicaSetを直接使うよりも代わりにDeploymentを使うことを推奨します。
これは、ユーザーがReplicaSetのオブジェクトを操作する必要が全く無いことを意味します。
代わりにDeploymentを使用して、spec
セクションにユーザーのアプリケーションを定義してください。
ReplicaSetの使用例
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# ケースに応じてレプリカを修正する
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
上記のマニフェストをfrontend.yaml
ファイルに保存しKubernetesクラスターに適用すると、マニフェストに定義されたReplicaSetとそれが管理するPod群を作成します。
kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml
ユーザーはデプロイされた現在のReplicaSetの情報も取得できます。
kubectl get rs
そして、ユーザーが作成したfrontendリソースについての情報も取得できます。
NAME DESIRED CURRENT READY AGE
frontend 3 3 3 6s
ユーザーはまたReplicaSetの状態も確認できます。
kubectl describe rs/frontend
その結果は以下のようになります。
Name: frontend
Namespace: default
Selector: tier=frontend
Labels: app=guestbook
tier=frontend
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: tier=frontend
Containers:
php-redis:
Image: gcr.io/google_samples/gb-frontend:v3
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 117s replicaset-controller Created pod: frontend-wtsmm
Normal SuccessfulCreate 116s replicaset-controller Created pod: frontend-b2zdv
Normal SuccessfulCreate 116s replicaset-controller Created pod: frontend-vcmts
そして最後に、ユーザーはReplicaSetによって作成されたPodもチェックできます。
kubectl get pods
表示されるPodに関する情報は以下のようになります。
NAME READY STATUS RESTARTS AGE
frontend-b2zdv 1/1 Running 0 6m36s
frontend-vcmts 1/1 Running 0 6m36s
frontend-wtsmm 1/1 Running 0 6m36s
ユーザーはまた、それらのPodのownerReferences
がfrontend
ReplicaSetに設定されていることも確認できます。
これを確認するためには、稼働しているPodの中のどれかのyamlファイルを取得します。
kubectl get pods frontend-b2zdv -o yaml
その表示結果は、以下のようになります。そのfrontend
ReplicaSetの情報がmetadata
のownerReferences
フィールドにセットされています。
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-02-12T07:06:16Z"
generateName: frontend-
labels:
tier: frontend
name: frontend-b2zdv
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: frontend
uid: f391f6db-bb9b-4c09-ae74-6a1f77f3d5cf
...
テンプレートなしのPodの所有
ユーザーが問題なくベアPod(Bare Pod: ここではPodテンプレート無しのPodのこと)を作成しているとき、そのベアPodがユーザーのReplicaSetの中のいずれのセレクターともマッチしないことを確認することを強く推奨します。 この理由として、ReplicaSetは、所有対象のPodがReplicaSetのテンプレートによって指定されたPodのみに限定されていないからです(ReplicaSetは前のセクションで説明した方法によって他のPodも所有できます)。
前のセクションで取り上げたfrontend
ReplicaSetと、下記のマニフェストのPodをみてみます。
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: gcr.io/google-samples/hello-app:2.0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
tier: frontend
spec:
containers:
- name: hello2
image: gcr.io/google-samples/hello-app:1.0
これらのPodはownerReferences
に何のコントローラー(もしくはオブジェクト)も指定されておらず、そしてfrontend
ReplicaSetにマッチするセレクターをもっており、これらのPodは即座にfrontend
ReplicaSetによって所有されます。
このfrontend
ReplicaSetがデプロイされ、初期のPodレプリカがレプリカ数の要求を満たすためにセットアップされた後で、ユーザーがそのPodを作成することを考えます。
kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml
新しいPodはそのReplicaSetによって所有され、そのReplicaSetのレプリカ数が、設定された理想のレプリカ数を超えた場合すぐにそれらのPodは削除されます。
下記のコマンドでPodを取得できます。
kubectl get pods
その表示結果で、新しいPodがすでに削除済みか、削除中のステータスになっているのを確認できます。
NAME READY STATUS RESTARTS AGE
frontend-b2zdv 1/1 Running 0 10m
frontend-vcmts 1/1 Running 0 10m
frontend-wtsmm 1/1 Running 0 10m
pod1 0/1 Terminating 0 1s
pod2 0/1 Terminating 0 1s
もしユーザーがそのPodを最初に作成する場合
kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml
そしてその後にfrontend
ReplicaSetを作成すると、
kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml
ユーザーはそのReplicaSetが作成したPodを所有し、さらにもともと存在していたPodと今回新たに作成されたPodの数が、理想のレプリカ数になるまでPodを作成するのを確認できます。 ここでまたPodの状態を取得します。
kubectl get pods
取得結果は下記のようになります。
NAME READY STATUS RESTARTS AGE
frontend-hmmj2 1/1 Running 0 9s
pod1 1/1 Running 0 36s
pod2 1/1 Running 0 36s
この方法で、ReplicaSetはテンプレートで指定されたもの以外のPodを所有することができます。
ReplicaSetのマニフェストを記述する。
他の全てのKubernetes APIオブジェクトのように、ReplicaSetはapiVersion
、kind
とmetadata
フィールドを必要とします。
ReplicaSetでは、kind
フィールドの値はReplicaSet
です。
ReplicaSetオブジェクトの名前は、有効な DNSサブドメイン名である必要があります。
また、ReplicaSetは.spec
セクションも必須です。
Pod テンプレート
.spec.template
はラベルを持つことが必要なPodテンプレート です。先ほど作成したfrontend.yaml
の例では、tier: frontend
というラベルを1つ持っています。
他のコントローラーがこのPodを所有しようとしないためにも、他のコントローラーのセレクターでラベルを上書きしないように注意してください。
テンプレートの再起動ポリシーのためのフィールドである.spec.template.spec.restartPolicy
はAlways
のみ許可されていて、そしてそれがデフォルト値です。
Pod セレクター
.spec.selector
フィールドはラベルセレクターです。
先ほど議論したように、ReplicaSetが所有するPodを指定するためにそのラベルが使用されます。
先ほどのfrontend.yaml
の例では、そのセレクターは下記のようになっていました
matchLabels:
tier: frontend
そのReplicaSetにおいて、.spec.template.metadata.labels
フィールドの値はspec.selector
と一致しなくてはならず、一致しない場合はAPIによって拒否されます。
備考:
2つのReplicaSetが同じ.spec.selector
の値を設定しているが、それぞれ異なる.spec.template.metadata.labels
と.spec.template.spec
フィールドの値を持っていたとき、それぞれのReplicaSetはもう一方のReplicaSetによって作成されたPodを無視します。レプリカ数について
ユーザーは.spec.replicas
フィールドの値を設定することにより、いくつのPodを同時に稼働させるか指定できます。そのときReplicaSetはレプリカ数がこの値に達するまでPodを作成、または削除します。
もしユーザーが.spec.replicas
を指定しない場合、デフォルト値として1がセットされます。
ReplicaSetを利用する
ReplicaSetとPodの削除
ReplicaSetとそれが所有する全てのPod削除したいときは、kubectl delete
コマンドを使ってください。
ガベージコレクターがデフォルトで自動的に全ての依存するPodを削除します。
REST APIもしくはclient-go
ライブラリーを使用するとき、ユーザーは-d
オプションでpropagationPolicy
をBackground
かForeground
と指定しなくてはなりません。例えば下記のように実行します。
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"
ReplicaSetのみを削除する
ユーザーはkubectl delete
コマンドで--cascade=false
オプションを付けることにより、所有するPodに影響を与えることなくReplicaSetを削除できます。
REST APIもしくはclient-go
ライブラリーを使用するとき、ユーザーは-d
オプションでpropagationPolicy
をOrphan
と指定しなくてはなりません。
例えば下記のように実行します:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"
一度元のReplicaSetが削除されると、ユーザーは新しいものに置き換えるため新しいReplicaSetを作ることができます。新旧のReplicaSetの.spec.selector
の値が同じである間、新しいReplicaSetは古いReplicaSetで稼働していたPodを取り入れます。
しかし、存在するPodが新しく異なるPodテンプレートとマッチさせようとするとき、この仕組みは機能しません。
ReplicaSetはローリングアップデートを直接サポートしないため、ユーザーのコントロール下においてPodを新しいspecにアップデートしたい場合は、Deploymentを使用してください。
PodをReplicaSetから分離させる
ユーザーはPodのラベルを変更することにより、ReplicaSetからそのPodを削除できます。この手法はデバッグや、データ修復などのためにサービスからPodを削除したいときに使用できます。 この方法で削除されたPodは自動的に新しいものに置き換えられます。(レプリカ数は変更されないものと仮定します。)
ReplicaSetのスケーリング
ReplicaSetは、ただ.spec.replicas
フィールドを更新することによって簡単にスケールアップまたはスケールダウンできます。ReplicaSetコントローラーは、ラベルセレクターにマッチするような指定した数のPodが利用可能であり、操作可能であることを保証します。
スケールダウンする場合、ReplicaSetコントローラーは以下の一般的なアルゴリズムに基づき、利用可能なPodをソートし、スケールダウンするPodの優先順位を付け、削除するPodを選択します:
- 保留している(またはスケジュール不可な)Podが先にスケールダウンされます。
controller.kubernetes.io/pod-deletion-cost
アノテーションが設定されている場合、値の小さいPodが優先されます。- レプリカ数の多いノード上のPodが、レプリカ数の少ないノード上のPodより優先されます。
- Podの作成時間が異なる場合、より新しく作成されたPodが古いPodより優先されます(
LogarithmicScaleDown
フィーチャーゲートが有効の場合、作成時間は整数対数スケールでバケット化されます)。
上記条件のすべてに該当する場合は、ランダム選択となります。
Pod削除コスト
Kubernetes v1.22 [beta]
controller.kubernetes.io/pod-deletion-cost
アノテーションを使用すると、ReplicaSetをスケールダウンする際に、どのPodを最初に削除するかについて、ユーザーが優先順位を設定することができます。
アノテーションはPodに設定する必要があり、範囲は[-2147483648, 2147483647]になります。同じReplicaSetに属する他のPodと比較して、Podを削除する際のコストを表しています。削除コストの低いPodは、削除コストの高いPodより優先的に削除されます。
このアノテーションを設定しないPodは暗黙的に0と設定され、負の値は許容されます。 無効な値はAPIサーバーによって拒否されます。
この機能はbeta版で、デフォルトで有効になっています。kube-apiserverとkube-controller-managerでフィーチャーゲートPodDeletionCost
を設定することで無効にすることができます。
備考:
- これはベストエフォートで実行されているもので、Pod削除の順番を保証するものではありません。
- ユーザーは、メトリック値に基づいてアノテーションを更新するなど、頻繁に更新することは避けるべきです。APIサーバー上で大量のPodの更新操作を発生させることになるためです。
使用事例
アプリケーションの異なるPodは、異なる使用レベルになる可能性があります。スケールダウンする場合、アプリケーションは使用率の低いPodを削除することを優先しています。Podを頻繁に更新することを避けるため、アプリケーションはスケールダウンする前に一度controller.kubernetes.io/pod-deletion-cost
を更新する必要があります(アノテーションをPod使用レベルに比例する値に設定します)。Spark DeploymentのドライバーPodのように、アプリケーション自体がスケールダウンを制御する場合も機能します。
HorizontalPodAutoscaler(HPA)のターゲットとしてのReplicaSet
ReplicaSetはまた、Horizontal Pod Autoscalers (HPA)のターゲットにもなることができます。 これはつまりReplicaSetがHPAによってオートスケールされうることを意味します。 ここではHPAが、前の例で作成したReplicaSetをターゲットにする例を示します。
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
このマニフェストをhpa-rs.yaml
に保存し、Kubernetesクラスターに適用すると、レプリケートされたPodのCPU使用量にもとづいてターゲットのReplicaSetをオートスケールするHPAを作成します。
kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml
同様のことを行うための代替案として、kubectl autoscale
コマンドも使用できます。(こちらの方がより簡単です。)
kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50
ReplicaSetの代替案
Deployment (推奨)
Deployment
はReplicaSetを所有することのできるオブジェクトで、宣言的なサーバサイドのローリングアップデートを介してReplicaSetとPodをアップデートできます。
ReplicaSetは単独で使用可能ですが、現在では、ReplicaSetは主にPodの作成、削除とアップデートを司るためのメカニズムとしてDeploymentによって使用されています。ユーザーがDeploymentを使用するとき、Deploymentによって作成されるReplicaSetの管理について心配する必要はありません。DeploymentはReplicaSetを所有し、管理します。
このため、もしユーザーがReplicaSetを必要とするとき、Deploymentの使用を推奨します。
ベアPod(Bare Pods)
ユーザーがPodを直接作成するケースとは異なり、ReplicaSetはNodeの故障やカーネルのアップグレードといった破壊的なNodeのメンテナンスなど、どのような理由に限らず削除または停止されたPodを置き換えます。 このため、我々はもしユーザーのアプリケーションが単一のPodのみ必要とする場合でもReplicaSetを使用することを推奨します。プロセスのスーパーバイザーについても同様に考えると、それは単一Node上での独立したプロセスの代わりに複数のNodeにまたがった複数のPodを監視します。 ReplicaSetは、KubeletのようなNode上のいくつかのエージェントに対して、ローカルのコンテナ再起動を移譲します。
Job
PodをPodそれ自身で停止させたいような場合(例えば、バッチ用のジョブなど)は、ReplicaSetの代わりにJob
を使用してください。
DaemonSet
マシンの監視やロギングなど、マシンレベルの機能を提供したい場合は、ReplicaSetの代わりにDaemonSet
を使用してください。
これらのPodはマシン自体のライフタイムに紐づいています: そのPodは他のPodが起動する前に、そのマシン上で稼働される必要があり、マシンが再起動またはシャットダウンされるときには、安全に停止されます。
ReplicationController
ReplicaSetはReplicationControllersの後継となるものです。
この2つは、ReplicationControllerがラベルについてのユーザーガイドに書かれているように、集合ベース(set-based)のセレクター要求をサポートしていないことを除いては、同じ目的を果たし、同じようにふるまいます。
このように、ReplicaSetはReplicationControllerよりも好まれます。
次の項目
- Podについて学ぶ。
- Deploymentについて学ぶ。
- ReplicaSetsに依存したDeploymentを使用してステートレスアプリケーションを実行する。
ReplicaSet
はKubernetes REST APIのトップレベルのリソースです。 ReplicaSetオブジェクトの定義を読み、レプリカセットのAPIを理解する。- PodDisruptionBudgetについて、またPodDisruptionBudgetで障害発生時のアプリケーションの可用性を管理する方法について読む。
3 - StatefulSet
StatefulSetはステートフルなアプリケーションを管理するためのワークロードAPIです。
StatefulSetはPodのデプロイとスケーリングを管理し、それらのPodの順序と一意性を保証します。
Deploymentのように、StatefulSetは指定したコンテナのspecに基づいてPodを管理します。Deploymentとは異なり、StatefulSetは各Podにおいて管理が大変な同一性を維持します。これらのPodは同一のspecから作成されますが、それらは交換可能ではなく、リスケジュール処理をまたいで維持される永続的な識別子を持ちます。
ワークロードに永続性を持たせるためにストレージボリュームを使いたい場合は、解決策の1つとしてStatefulSetが利用できます。StatefulSet内の個々のPodは障害の影響を受けやすいですが、永続化したPodの識別子は既存のボリュームと障害によって置換された新しいPodの紐付けを簡単にします。
StatefulSetの使用
StatefulSetは下記の1つ以上の項目を要求するアプリケーションにおいて最適です。
- 安定した一意のネットワーク識別子
- 安定した永続ストレージ
- 規則的で安全なデプロイとスケーリング
- 規則的で自動化されたローリングアップデート
上記において安定とは、Podのスケジュール(または再スケジュール)をまたいでも永続的であることと同義です。 もしアプリケーションが安定したネットワーク識別子と規則的なデプロイや削除、スケーリングを全く要求しない場合、ユーザーはステートレスなレプリカのセットを提供するワークロードを使ってアプリケーションをデプロイするべきです。 DeploymentやReplicaSetのようなコントローラーはこのようなステートレスな要求に対して最適です。
制限事項
- 提供されたPodのストレージは、要求された
storage class
にもとづいてPersistentVolume Provisionerによってプロビジョンされるか、管理者によって事前にプロビジョンされなくてはなりません。 - StatefulSetの削除もしくはスケールダウンをすることにより、StatefulSetに関連したボリュームは削除されません 。 これはデータ安全性のためで、関連するStatefulSetのリソース全てを自動的に削除するよりもたいてい有効です。
- StatefulSetは現在、Podのネットワークアイデンティティーに責務をもつためにHeadless Serviceを要求します。ユーザーはこのServiceを作成する責任があります。
- StatefulSetは、StatefulSetが削除されたときにPodの停止を行うことを保証していません。StatefulSetにおいて、規則的で安全なPodの停止を行う場合、削除のために事前にそのStatefulSetの数を0にスケールダウンさせることが可能です。
- デフォルト設定のPod管理ポリシー (
OrderedReady
)によってローリングアップデートを行う場合、修復のための手動介入を要求するようなブロークンな状態に遷移させることが可能です。
コンポーネント
下記の例は、StatefulSetのコンポーネントのデモンストレーションとなります。
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # .spec.template.metadata.labelsの値と一致する必要があります
serviceName: "nginx"
replicas: 3 # by default is 1
template:
metadata:
labels:
app: nginx # .spec.selector.matchLabelsの値と一致する必要があります
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
上記の例では、
- nginxという名前のHeadlessServiceは、ネットワークドメインをコントロールするために使われます。
- webという名前のStatefulSetは、specで3つのnginxコンテナのレプリカを持ち、そのコンテナはそれぞれ別のPodで稼働するように設定されています。
- volumeClaimTemplatesは、PersistentVolumeプロビジョナーによってプロビジョンされたPersistentVolumeを使って安定したストレージを提供します。
StatefulSetの名前は有効な名前である必要があります。
Podセレクター
ユーザーは、StatefulSetの.spec.template.metadata.labels
のラベルと一致させるため、StatefulSetの.spec.selector
フィールドをセットしなくてはなりません。Kubernetes1.8以前では、.spec.selector
フィールドは省略された場合デフォルト値になります。Kubernetes1.8とそれ以降のバージョンでは、ラベルに一致するPodセレクターの指定がない場合はStatefulSetの作成時にバリデーションエラーになります。
Podアイデンティティー
StatefulSetのPodは、順番を示す番号、安定したネットワークアイデンティティー、安定したストレージからなる一意なアイデンティティーを持ちます。 そのアイデンティティーはどのNode上にスケジュール(もしくは再スケジュール)されるかに関わらず、そのPodに紐付きます。
順序インデックス
N個のレプリカをもったStatefulSetにおいて、StatefulSet内の各Podは、0からはじまりN-1までの整数値を順番に割り当てられ、そのStatefulSetにおいては一意となります。
安定したネットワークID
StatefulSet内の各Podは、そのStatefulSet名とPodの順序番号から派生してホストネームが割り当てられます。
作成されたホストネームの形式は$(StatefulSet名)-$(順序番号)
となります。先ほどの上記の例では、web-0,web-1,web-2
という3つのPodが作成されます。
StatefulSetは、PodのドメインをコントロールするためにHeadless Serviceを使うことができます。
このHeadless Serviceによって管理されたドメインは$(Service名).$(ネームスペース).svc.cluster.local
形式となり、"cluster.local"というのはそのクラスターのドメインとなります。
各Podが作成されると、Podは$(Pod名).$(管理するServiceドメイン名)
に一致するDNSサブドメインを取得し、管理するServiceはStatefulSetのserviceName
で定義されます。
クラスターでのDNSの設定方法によっては、新たに起動されたPodのDNS名をすぐに検索できない場合があります。 この動作は、クラスター内の他のクライアントが、Podが作成される前にそのPodのホスト名に対するクエリーをすでに送信していた場合に発生する可能性があります。 (DNSでは通常)ネガティブキャッシュは、Podの起動後でも、少なくとも数秒間、以前に失敗したルックアップの結果が記憶され、再利用されることを意味します。
Podが作成された後、速やかにPodを検出する必要がある場合は、いくつかのオプションがあります。
- DNSルックアップに依存するのではなく、Kubernetes APIに直接(例えばwatchを使って)問い合わせる。
- Kubernetes DNS プロバイダーのキャッシュ時間を短縮する(これは現在30秒キャッシュされるようになっているCoreDNSのConfigMapを編集することを意味しています。)。
制限事項セクションで言及したように、ユーザーはPodのネットワークアイデンティティーのためにHeadless Serviceを作成する責任があります。
ここで、クラスタードメイン、Service名、StatefulSet名の選択と、それらがStatefulSetのPodのDNS名にどう影響するかの例をあげます。
Cluster Domain | Service (ns/name) | StatefulSet (ns/name) | StatefulSet Domain | Pod DNS | Pod Hostname |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
安定したストレージ
StatefulSetで定義された各VolumeClaimTemplateに対して、各Podは1つのPersistentVolumeClaimを受け取ります。上記のnginxの例において、各Podはmy-storage-class
というStorageClassをもち、1GiBのストレージ容量を持った単一のPersistentVolumeを受け取ります。もしStorageClassが指定されていない場合、デフォルトのStorageClassが使用されます。PodがNode上にスケジュール(もしくは再スケジュール)されたとき、そのvolumeMounts
はPersistentVolume Claimに関連したPersistentVolumeをマウントします。
注意点として、PodのPersistentVolume Claimと関連したPersistentVolumeは、PodやStatefulSetが削除されたときに削除されません。
削除する場合は手動で行わなければなりません。
Podのネームラベル
StatefulSet コントローラー がPodを作成したとき、Podの名前として、statefulset.kubernetes.io/pod-name
にラベルを追加します。このラベルによってユーザーはServiceにStatefulSet内の指定したPodを割り当てることができます。
デプロイとスケーリングの保証
- N個のレプリカをもつStatefulSetにおいて、Podがデプロイされるとき、それらのPodは{0..N-1}の番号で順番に作成されます。
- Podが削除されるとき、それらのPodは{N-1..0}の番号で降順に削除されます。
- Podに対してスケーリングオプションが適用される前に、そのPodの前の順番の全てのPodがRunningかつReady状態になっていなくてはなりません。
- Podが停止される前に、そのPodの番号より大きい番号を持つの全てのPodは完全にシャットダウンされていなくてはなりません。
StatefulSetはpod.Spec.TerminationGracePeriodSeconds
を0に指定すべきではありません。これは不安全で、やらないことを強く推奨します。さらなる説明としては、StatefulSetのPodの強制削除を参照してください。
上記の例のnginxが作成されたとき、3つのPodはweb-0
、web-1
、web-2
の順番でデプロイされます。web-1
はweb-0
がRunningかつReady状態になるまでは決してデプロイされないのと、同様にweb-2
はweb-1
がRunningかつReady状態にならないとデプロイされません。もしweb-0
がweb-1
がRunningかつReady状態になった後だが、web-2
が起動する前に失敗した場合、web-2
はweb-0
の再起動が成功し、RunningかつReady状態にならないと再起動されません。
もしユーザーがreplicas=1
といったようにStatefulSetにパッチをあてることにより、デプロイされたものをスケールすることになった場合、web-2
は最初に停止されます。web-1
はweb-2
が完全にシャットダウンされ削除されるまでは、停止されません。もしweb-0
が、web-2
が完全に停止され削除された後だが、web-1
の停止の前に失敗した場合、web-1
はweb-0
がRunningかつReady状態になるまでは停止されません。
Podの管理ポリシー
Kubernetes1.7とそれ以降のバージョンでは、StatefulSetは.spec.podManagementPolicy
フィールドを介して、Podの一意性とアイデンティティーを保証します。
OrderedReadyなPod管理
OrderedReady
なPod管理はStatefulSetにおいてデフォルトです。これはデプロイとスケーリングの保証に記載されている項目の振る舞いを実装します。
並行なPod管理
Parallel
なPod管理は、StatefulSetコントローラーに対して、他のPodが起動や停止される前にそのPodが完全に起動し準備完了になるか停止するのを待つことなく、Podが並行に起動もしくは停止するように指示します。
アップデートストラテジー
Kubernetes1.7とそれ以降のバージョンにおいて、StatefulSetの.spec.updateStrategy
フィールドで、コンテナの自動のローリングアップデートの設定やラベル、リソースのリクエストとリミットや、StatefulSet内のPodのアノテーションを指定できます。
OnDelete
OnDelete
というアップデートストラテジーは、レガシーな(Kubernetes1.6以前)振る舞いとなります。StatefulSetの.spec.updateStrategy.type
がOnDelete
にセットされていたとき、そのStatefulSetコントローラーはStatefulSet内でPodを自動的に更新しません。StatefulSetの.spec.template
項目の修正を反映した新しいPodの作成をコントローラーに支持するためには、ユーザーは手動でPodを削除しなければなりません。
RollingUpdate
RollingUpdate
というアップデートストラテジーは、StatefulSet内のPodに対する自動化されたローリングアップデートの機能を実装します。これは.spec.updateStrategy
フィールドが未指定の場合のデフォルトのストラテジーです。StatefulSetの.spec.updateStrategy.type
がRollingUpdate
にセットされたとき、そのStatefulSetコントローラーは、StatefulSet内のPodを削除し、再作成します。これはPodの停止(Podの番号の降順)と同じ順番で、一度に1つのPodを更新します。コントローラーは、その前のPodの状態がRunningかつReady状態になるまで次のPodの更新を待ちます。
パーティション
RollingUpdate
というアップデートストラテジーは、.spec.updateStrategy.rollingUpdate.partition
を指定することにより、パーティションに分けることができます。もしパーティションが指定されていたとき、そのパーティションの値と等しいか、大きい番号を持つPodが更新されます。パーティションの値より小さい番号を持つPodは更新されず、たとえそれらのPodが削除されたとしても、それらのPodは以前のバージョンで再作成されます。もしStatefulSetの.spec.updateStrategy.rollingUpdate.partition
が、.spec.replicas
より大きい場合、.spec.template
への更新はPodに反映されません。
多くのケースの場合、ユーザーはパーティションを使う必要はありませんが、もし一部の更新を行う場合や、カナリー版のバージョンをロールアウトする場合や、段階的ロールアウトを行う場合に最適です。
強制ロールバック
デフォルトのPod管理ポリシー(OrderedReady
)によるローリングアップデートを行う際、修復のために手作業が必要な状態にすることが可能です。
もしユーザーが、決してRunningかつReady状態にならないような設定になるようにPodテンプレートを更新した場合(例えば、不正なバイナリや、アプリケーションレベルの設定エラーなど)、StatefulSetはロールアウトを停止し、待機します。
この状態では、Podテンプレートを正常な状態に戻すだけでは不十分です。既知の問題によって、StatefulSetは元の正常な状態へ戻す前に、壊れたPodがReady状態(決して起こりえない)に戻るのを待ち続けます。
そのテンプレートを戻したあと、ユーザーはまたStatefulSetが異常状態で稼働しようとしていたPodをすべて削除する必要があります。StatefulSetはその戻されたテンプレートを使ってPodの再作成を始めます。
次の項目
- ステートフルなアプリケーションのデプロイの例を参考にしてください。
- StatefulSetを使ったCassandraのデプロイの例を参考にしてください。
- レプリカを持つステートフルアプリケーションを実行するの例を参考にしてください。
4 - DaemonSet
DaemonSet は全て(またはいくつか)のNodeが単一のPodのコピーを稼働させることを保証します。Nodeがクラスターに追加されるとき、PodがNode上に追加されます。Nodeがクラスターから削除されたとき、それらのPodはガーベージコレクターにより除去されます。DaemonSetの削除により、DaemonSetが作成したPodもクリーンアップします。
DaemonSetのいくつかの典型的な使用例は以下の通りです。
- クラスターのストレージデーモンを全てのNode上で稼働させる。
- ログ集計デーモンを全てのNode上で稼働させる。
- Nodeのモニタリングデーモンを全てのNode上で稼働させる。
シンプルなケースとして、各タイプのデーモンにおいて、全てのNodeをカバーする1つのDaemonSetが使用されるケースがあります。さらに複雑な設定では、単一のタイプのデーモン用ですが、異なるフラグや、異なるハードウェアタイプに対するメモリー、CPUリクエストを要求する複数のDaemonSetを使用するケースもあります。
DaemonSet Specの記述
DaemonSetの作成
ユーザーはYAMLファイル内でDaemonSetの設定を記述することができます。例えば、下記のdaemonset.yaml
ファイルではfluentd-elasticsearch
というDockerイメージを稼働させるDaemonSetの設定を記述します。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
YAMLファイルに基づいてDaemonSetを作成します。
kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml
必須のフィールド
他の全てのKubernetesの設定と同様に、DaemonSetはapiVersion
、kind
とmetadata
フィールドが必須となります。設定ファイルの活用法に関する一般的な情報は、ステートレスアプリケーションの稼働、kubectlを用いたオブジェクトの管理といったドキュメントを参照ください。
DaemonSetオブジェクトの名前は、有効な DNSサブドメイン名である必要があります。
また、DaemonSetにおいて.spec
セクションも必須となります。
Podテンプレート
.spec.template
は.spec
内での必須のフィールドの1つです。
.spec.template
はPodテンプレートとなります。これはフィールドがネストされていて、apiVersion
やkind
をもたないことを除いては、Podのテンプレートと同じスキーマとなります。
Podに対する必須のフィールドに加えて、DaemonSet内のPodテンプレートは適切なラベルを指定しなくてはなりません(Podセレクターの項目を参照ください)。
DaemonSet内のPodテンプレートでは、RestartPolicy
フィールドを指定せずにデフォルトのAlways
を使用するか、明示的にAlways
を設定するかのどちらかである必要があります。
Podセレクター
.spec.selector
フィールドはPodセレクターとなります。これはJobの.spec.selector
と同じものです。
ユーザーは.spec.template
のラベルにマッチするPodセレクターを指定しなくてはいけません。
また、一度DaemonSetが作成されると、その.spec.selector
は変更不可能になります。Podセレクターの変更は、意図しないPodの孤立を引き起こし、ユーザーにとってやっかいなものとなります。
.spec.selector
は2つのフィールドからなるオブジェクトです。
matchLabels
- ReplicationControllerの.spec.selector
と同じように機能します。matchExpressions
- キーと、値のリストとさらにはそれらのキーとバリューに関連したオペレーターを指定することにより、より洗練された形式のセレクターを構成できます。
上記の2つが指定された場合は、2つの条件をANDでどちらも満たすものを結果として返します。
spec.selector
は.spec.template.metadata.labels
とマッチしなければなりません。この2つの値がマッチしない設定をした場合、APIによってリジェクトされます。
選択したNode上でPodを稼働させる
もしユーザーが.spec.template.spec.nodeSelector
を指定したとき、DaemonSetコントローラーは、そのnode selectorにマッチするNode上にPodを作成します。同様に、もし.spec.template.spec.affinity
を指定したとき、DaemonSetコントローラーはnode affinityにマッチするNode上にPodを作成します。
もしユーザーがどちらも指定しないとき、DaemonSetコントローラーは全てのNode上にPodを作成します。
Daemon Podがどのようにスケジューリングされるか
DaemonSetは、全ての利用可能なNodeがPodのコピーを稼働させることを保証します。DaemonSetコントローラーは対象となる各Nodeに対してPodを作成し、ターゲットホストに一致するようにPodのspec.affinity.nodeAffinity
フィールドを追加します。Podが作成されると、通常はデフォルトのスケジューラーが引き継ぎ、.spec.nodeName
を設定することでPodをターゲットホストにバインドします。新しいNodeに適合できない場合、デフォルトスケジューラーは新しいPodの優先度に基づいて、既存Podのいくつかを先取り(退避)させることがあります。
ユーザーは、DaemonSetの.spec.template.spec.schedulerName
フィールドを設定することにより、DaemonSetのPodに対して異なるスケジューラーを指定することができます。
.spec.template.spec.affinity.nodeAffinity
フィールド(指定された場合)で指定された元のNodeアフィニティは、DaemonSetコントローラーが対象Nodeを評価する際に考慮されますが、作成されたPod上では対象Nodeの名前と一致するNodeアフィニティに置き換わります。
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name
TaintとToleration
DaemonSetコントローラーはDaemonSet Podに一連のTolerationを自動的に追加します:
Toleration key | Effect | Details |
---|---|---|
node.kubernetes.io/not-ready | NoExecute | 健康でないNodeや、Podを受け入れる準備ができていないNodeにDaemonSet Podをスケジュールできるように設定します。そのようなNode上で動作しているDaemonSet Podは退避されることがありません。 |
node.kubernetes.io/unreachable | NoExecute | Nodeコントローラーから到達できないNodeにDaemonSet Podをスケジュールできるように設定します。このようなNode上で動作しているDaemonSet Podは、退避されません。 |
node.kubernetes.io/disk-pressure | NoSchedule | ディスク不足問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。 |
node.kubernetes.io/memory-pressure | NoSchedule | メモリー不足問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。 |
node.kubernetes.io/pid-pressure | NoSchedule | 処理負荷に問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。 |
node.kubernetes.io/unschedulable | NoSchedule | スケジューリング不可能なNodeにDaemonSet Podをスケジュールできるように設定します。 |
node.kubernetes.io/network-unavailable | NoSchedule | ホストネットワークを要求するDaemonSet Podにのみ追加できます、つまりspec.hostNetwork: true と設定されているPodです。このようなDaemonSet Podは、ネットワークが利用できないNodeにスケジュールできるように設定します。 |
DaemonSetのPodテンプレートで定義すれば、DaemonSetのPodに独自のTolerationを追加することも可能です。
DaemonSetコントローラーはnode.kubernetes.io/unschedulable:NoSchedule
のTolerationを自動的に設定するため、Kubernetesは スケジューリング不可能 としてマークされているNodeでDaemonSet Podを実行することが可能です。
クラスターのネットワークのような重要なNodeレベルの機能をDaemonSetで提供する場合、KubernetesがDaemonSet PodをNodeが準備完了になる前に配置することは有用です。 例えば、その特別なTolerationがなければ、ネットワークプラグインがそこで実行されていないためにNodeが準備完了としてマークされず、同時にNodeがまだ準備完了でないためにそのNode上でネットワークプラグインが実行されていないというデッドロック状態に陥ってしまう可能性があるのです。
Daemon Podとのコミュニケーション
DaemonSet内のPodとのコミュニケーションをする際に考えられるパターンは以下の通りです:
- Push: DaemonSet内のPodは統計データベースなどの他のサービスに対して更新情報を送信するように設定されます。クライアントは持っていません。
- NodeIPとKnown Port: PodがNodeIPを介して疎通できるようにするため、DaemonSet内のPodは
hostPort
を使用できます。慣例により、クライアントはNodeIPのリストとポートを知っています。 - DNS: 同じPodセレクターを持つHeadlessServiceを作成し、
endpoints
リソースを使ってDaemonSetを探すか、DNSから複数のAレコードを取得します。 - Service: 同じPodセレクターを持つServiceを作成し、複数のうちのいずれかのNode上のDaemonに疎通させるためにそのServiceを使います。(特定のNodeにアクセスする方法はありません。)
DaemonSetの更新
もしNodeラベルが変更されたとき、そのDaemonSetは直ちに新しくマッチしたNodeにPodを追加し、マッチしなくなったNodeからPodを削除します。
ユーザーはDaemonSetが作成したPodを修正可能です。しかし、Podは全てのフィールドの更新を許可していません。また、DaemonSetコントローラーは次のNode(同じ名前でも)が作成されたときにオリジナルのテンプレートを使ってPodを作成します。
ユーザーはDaemonSetを削除可能です。kubectl
コマンドで--cascade=orphan
を指定するとDaemonSetのPodはNode上に残り続けます。その後、同じセレクターで新しいDaemonSetを作成すると、新しいDaemonSetは既存のPodを再利用します。PodでDaemonSetを置き換える必要がある場合は、updateStrategy
に従ってそれらを置き換えます。
ユーザーはDaemonSet上でローリングアップデートの実施が可能です。
DaemonSetの代替案
Initスクリプト
Node上で直接起動することにより(例: init
、upstartd
、systemd
を使用する)、デーモンプロセスを稼働することが可能です。この方法は非常に良いですが、このようなプロセスをDaemonSetを介して起動することはいくつかの利点があります。
- アプリケーションと同じ方法でデーモンの監視とログの管理ができる。
- デーモンとアプリケーションで同じ設定用の言語とツール(例: Podテンプレート、
kubectl
)を使える。 - リソースリミットを使ったコンテナ内でデーモンを稼働させることにより、デーモンとアプリケーションコンテナの分離性が高まります。ただし、これはPod内ではなく、コンテナ内でデーモンを稼働させることでも可能です。
ベアPod
特定のNode上で稼働するように指定したPodを直接作成することは可能です。しかし、DaemonSetはNodeの故障やNodeの破壊的なメンテナンスやカーネルのアップグレードなど、どのような理由に限らず、削除されたもしくは停止されたPodを置き換えます。このような理由で、ユーザーはPod単体を作成するよりもむしろDaemonSetを使うべきです。
静的Pod
Kubeletによって監視されているディレクトリに対してファイルを書き込むことによって、Podを作成することが可能です。これは静的Podと呼ばれます。DaemonSetと違い、静的Podはkubectlや他のKubernetes APIクライアントで管理できません。静的PodはApiServerに依存しておらず、クラスターの自立起動時に最適です。また、静的Podは将来的には廃止される予定です。
Deployment
DaemonSetは、Podの作成し、そのPodが停止されることのないプロセスを持つことにおいてDeploymentと同様です(例: webサーバー、ストレージサーバー)。
フロントエンドのようなServiceのように、どのホスト上にPodが稼働するか制御するよりも、レプリカ数をスケールアップまたはスケールダウンしたりローリングアップデートする方が重要であるような、状態をもたないServiceに対してDeploymentを使ってください。 DaemonSetがNodeレベルの機能を提供し、他のPodがその特定のNodeで正しく動作するようにする場合、Podのコピーが全てまたは特定のホスト上で常に稼働していることが重要な場合にDaemonSetを使ってください。
例えば、ネットワークプラグインには、DaemonSetとして動作するコンポーネントが含まれていることがよくあります。DaemonSetコンポーネントは、それが動作しているNodeでクラスターネットワークが動作していることを確認します。
次の項目
- Podについて学ぶ。
- Kubernetesのコントロールプレーンコンポーネントを実行するのに便利な静的Podについて学ぶ。
- DaemonSetの使用方法を確認する
- DaemonSetでローリングアップデートを実施する
- DaemonSetでロールバックを実行する (例えば、ロールアウトが期待通りに動作しなかった場合)。
- Node上へのPodのスケジューリングの仕組みを理解する
- よくDaemonSetとして実行されるデバイスプラグインとアドオンについて学ぶ。
DaemonSet
は、Kubernetes REST APIのトップレベルのリソースです。デーモンセットのAPIを理解するため DaemonSetオブジェクトの定義を読む。
5 - Job
Jobは一つ以上のPodを作成し、指定された数のPodが正常に終了するまで、Podの実行を再試行し続けます。Podが正常に終了すると、Jobは成功したPodの数を追跡します。指定された完了数に達すると、そのタスク(つまりJob)は完了したとみなされます。Jobを削除すると、作成されたPodも一緒に削除されます。Jobを一時停止すると、再開されるまで、稼働しているPodは全部削除されます。
単純なケースを言うと、確実に一つのPodが正常に完了するまで実行されるよう、一つのJobオブジェクトを作成します。 一つ目のPodに障害が発生したり、(例えばノードのハードウェア障害またノードの再起動が原因で)削除されたりすると、Jobオブジェクトは新しいPodを作成します。
Jobで複数のPodを並列で実行することもできます。
スケジュールに沿ってJob(単一のタスクか複数タスク並列のいずれか)を実行したい場合は CronJobを参照してください。
実行例
下記にJobの定義例を記載しています。πを2000桁まで計算して出力するJobで、完了するまで約10秒かかります。
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
このコマンドで実行できます:
kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml
実行結果はこのようになります:
job.batch/pi created
kubectl
でJobの状態を確認できます:
Name: pi
Namespace: default
Selector: batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels: batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
batch.kubernetes.io/job-name=pi
...
Annotations: batch.kubernetes.io/job-tracking: ""
Parallelism: 1
Completions: 1
Start Time: Mon, 02 Dec 2019 15:20:11 +0200
Completed At: Mon, 02 Dec 2019 15:21:16 +0200
Duration: 65s
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
Pod Template:
Labels: batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
batch.kubernetes.io/job-name=pi
Containers:
pi:
Image: perl:5.34.0
Port: <none>
Host Port: <none>
Command:
perl
-Mbignum=bpi
-wle
print bpi(2000)
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 21s job-controller Created pod: pi-xf9p4
Normal Completed 18s job-controller Job completed
apiVersion: batch/v1
kind: Job
metadata:
annotations: batch.kubernetes.io/job-tracking: ""
...
creationTimestamp: "2022-11-10T17:53:53Z"
generation: 1
labels:
batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
batch.kubernetes.io/job-name: pi
name: pi
namespace: default
resourceVersion: "4751"
uid: 204fb678-040b-497f-9266-35ffa8716d14
spec:
backoffLimit: 4
completionMode: NonIndexed
completions: 1
parallelism: 1
selector:
matchLabels:
batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
suspend: false
template:
metadata:
creationTimestamp: null
labels:
batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
batch.kubernetes.io/job-name: pi
spec:
containers:
- command:
- perl
- -Mbignum=bpi
- -wle
- print bpi(2000)
image: perl:5.34.0
imagePullPolicy: IfNotPresent
name: pi
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Never
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
active: 1
ready: 0
startTime: "2022-11-10T17:53:57Z"
uncountedTerminatedPods: {}
Jobの完了したPodを確認するには、kubectl get pods
を使います。
Jobに属するPodの一覧を機械可読形式で出力するには、下記のコマンドを使います:
pods=$(kubectl get pods --selector=batch.kubernetes.io/job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods
出力結果はこのようになります:
pi-5rwd7
ここのセレクターはJobのセレクターと同じです。--output=jsonpath
オプションは、返されたリストからPodのnameフィールドを指定するための表現です。
その中の一つのPodの標準出力を確認するには:
kubectl logs $pods
Jobの標準出力を確認するもう一つの方法は:
kubectl logs jobs/pi
出力結果はこのようになります:
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901
Job spec(仕様)の書き方
他のKubernetesオブジェクト設定ファイルと同様に、JobにもapiVersion
、kind
またはmetadata
フィールドが必要です。
コントロールプレーンがJobのために新しいPodを作成するとき、Jobの.metadata.name
はそれらのPodに名前をつけるための基礎の一部になります。Jobの名前は有効なDNSサブドメイン名である必要がありますが、これはPodのホスト名に予期しない結果をもたらす可能性があります。最高の互換性を得るためには、名前はDNSラベルのより限定的な規則に従うべきです。名前がDNSサブドメインの場合でも、名前は63文字以下でなければなりません。
Jobには.spec
セクションも必要です。
Jobラベル
Jobラベルのjob-name
とcontroller-uid
の接頭辞はbatch.kubernetes.io/
となります。
Podテンプレート
.spec.template
は.spec
の唯一の必須フィールドです。
.spec.template
はpodテンプレートです。ネストされていることとapiVersion
やkind
フィールドが不要になったことを除いて、仕様の定義がPodと全く同じです。
Podの必須フィールドに加えて、Job定義ファイルにあるPodテンプレートでは、適切なラベル(podセレクターを参照)と適切な再起動ポリシーを指定する必要があります。
RestartPolicy
はNever
かOnFailure
のみ設定可能です。
Podセレクター
.spec.selector
フィールドはオプションです。ほとんどの場合はむしろ指定しないほうがよいです。
独自のPodセレクターを指定セクションを参照してください。
Jobの並列実行
Jobで実行するのに適したタスクは主に3種類あります:
- 非並列Job
- 通常、Podに障害が発生しない限り、一つのPodのみが起動されます。
- Podが正常に終了すると、Jobはすぐに完了します。
- 固定の完了数を持つ並列Job:
.spec.completions
に0以外の正の値を指定します。- Jobは全体的なタスクを表し、
.spec.completions
個のPodが成功すると、Jobの完了となります。 .spec.completionMode="Indexed"
を利用する場合、各Podは0から.spec.completions-1
までの範囲内のインデックスがアサインされます。
- ワークキューを利用した並列Job:
.spec.completions
の指定をしない場合、デフォルトは.spec.parallelism
となります。- Pod間で調整する、または外部サービスを使う方法で、それぞれ何のタスクに着手するかを決めます。例えば、一つのPodはワークキューから最大N個のタスクを一括で取得できます。
- 各Podは他のPodがすべて終了したかどうか、つまりJobが完了したかどうかを単独で判断できます。
- Jobに属する 任意 のPodが正常に終了すると、新しいPodは作成されません。
- 一つ以上のPodが正常に終了し、すべてのPodが終了すると、Jobは正常に完了します。
- 一つのPodが正常に終了すると、他のPodは同じタスクの作業を行ったり、出力を書き込んだりすることはできません。すべてのPodが終了プロセスに進む必要があります。
非並列 Jobの場合、.spec.completions
と.spec.parallelism
の両方を未設定のままにしておくことも可能です。未設定の場合、両方がデフォルトで1になります。
完了数固定 Jobの場合、.spec.completions
を必要完了数に設定する必要があります。
.spec.parallelism
を設定してもいいですし、未設定の場合、デフォルトで1になります。
ワークキュー 並列Jobの場合、.spec.completions
を未設定のままにし、.spec.parallelism
を非負の整数に設定する必要があります。
各種類のJobの使用方法の詳細については、Jobパターンセクションを参照してください。
並列処理の制御
必要並列数(.spec.parallelism
)は任意の非負の値に設定できます。
未設定の場合は、デフォルトで1になります。
0に設定した際には、増加するまでJobは一時停止されます。
実際の並列数(任意の瞬間に実行されているPod数)は、さまざまな理由により、必要並列数と異なる可能性があります:
- 完了数固定 Jobの場合、実際に並列して実行されるPodの数は、残りの完了数を超えることはありません。
.spec.parallelism
の値が高い場合は無視されます。 - ワークキュー Jobの場合、任意のPodが成功すると、新しいPodは作成されません。ただし、残りのPodは終了まで実行し続けられます。
- Jobコントローラーの応答する時間がなかった場合。
- Jobコントローラーが何らかの理由で(
ResourceQuota
の不足、権限の不足など)、Podを作成できない場合、 実際の並列数は必要並列数より少なくなる可能性があります。 - 同じJobで過去に発生した過度のPod障害が原因で、Jobコントローラーは新しいPodの作成を抑制することがあります。
- Podがグレースフルシャットダウンされた場合、停止するのに時間がかかります。
完了モード
Kubernetes v1.24 [stable]
完了数固定 Job、つまり.spec.completions
の値がnullではないJobは.spec.completionMode
で完了モードを指定できます:
NonIndexed
(デフォルト):.spec.completions
個のPodが成功した場合、Jobの完了となります。言い換えれば、各Podの完了状態は同質です。ここで要注意なのは、.spec.completions
の値がnullの場合、暗黙的にNonIndexed
として指定されることです。Indexed
: Jobに属するPodはそれぞれ、0から.spec.completions-1
の範囲内の完了インデックスを取得できます。インデックスは下記の三つの方法で取得できます。- Podアノテーション
batch.kubernetes.io/job-completion-index
。 - Podホスト名の一部として、
$(job-name)-$(index)
の形式になっています。 インデックス付きJob(Indexed Job)とServiceを一緒に使用すると、Jobに属するPodはお互いにDNSを介して確定的ホスト名で通信できます。この設定方法の詳細はPod間通信を使用したJobを参照してください。 - コンテナ化されたタスクの環境変数
JOB_COMPLETION_INDEX
。
各インデックスに1つずつ正常に完了したPodがあると、Jobは完了したとみなされます。このモードの使い方については、静的な処理の割り当てを使用した並列処理のためのインデックス付きJobを参照してください。
- Podアノテーション
備考:
めったに発生しませんが、同じインデックスに対して複数のPodが起動することがあります。(Nodeの障害、kubeletの再起動、Podの立ち退きなど)。この場合、正常に完了した最初のPodだけ完了数にカウントされ、Jobのステータスが更新されます。同じインデックスに対して実行中または完了した他のPodは、検出されるとJobコントローラーによって削除されます。Podとコンテナの障害対策
Pod内のコンテナは、その中のプロセスが0以外の終了コードで終了した、またはメモリ制限を超えたためにコンテナが強制終了されたなど、様々な理由で失敗することがあります。この場合、もし.spec.template.spec.restartPolicy = "OnFailure"
と設定すると、Podはノード上に残りますが、コンテナは再実行されます。そのため、プログラムがローカルで再起動した場合の処理を行うか、.spec.template.spec.restartPolicy = "Never"
と指定する必要があります。
restartPolicy
の詳細についてはPodのライフサイクルを参照してください。
Podがノードからキックされた(ノードがアップグレード、再起動、削除されたなど)、または.spec.template.spec.restartPolicy = "Never"
と設定されたときにPodに属するコンテナが失敗したなど、様々な理由でPod全体が故障することもあります。Podに障害が発生すると、Jobコントローラーは新しいPodを起動します。つまりアプリケーションは新しいPodで再起動された場合の処理を行う必要があります。特に、過去に実行した際に生じた一時ファイル、ロック、不完全な出力などを処理する必要があります。
デフォルトでは、それぞれのPodの失敗は.spec.backoffLimit
にカウントされます。詳しくはPod失敗のバックオフポリシーをご覧ください。しかし、JobのPod失敗ポリシーを設定することで、Pod失敗の処理をカスタマイズすることができます。
.spec.parallelism = 1
、.spec.completions = 1
と.spec.template.spec.restartPolicy = "Never"
を指定しても、同じプログラムが2回起動されることもありますので注意してください。
.spec.parallelism
と.spec.completions
を両方とも2以上指定した場合、複数のPodが同時に実行される可能性があります。そのため、Podは並行処理を行えるようにする必要があります。
フィーチャーゲートのPodDisruptionConditions
とJobPodFailurePolicy
の両方が有効で、.spec.podFailurePolicy
フィールドが設定されている場合、Jobコントローラーは終了するPod(.metadata.deletionTimestamp
フィールドが設定されているPod)を、そのPodが終了する(.status.phase
がFailed
またはSucceeded
になる)までは失敗とはみなしません。ただし、Jobコントローラーは、終了が明らかになるとすみやかに代わりのPodを作成します。Podが終了すると、Jobコントローラーはこの終了したPodを考慮に入れて、該当のJobの.backoffLimit
と.podFailurePolicy
を評価します。
これらの要件のいずれかが満たされていない場合、Jobコントローラーは、そのPodが後にphase: "Succeeded"
で終了する場合でも、終了するPodを即時に失敗として数えます。
Pod失敗のバックオフポリシー
設定の論理エラーなどにより、Jobが数回再試行した後に失敗状態にしたい場合があります。.spec.backoffLimit
を設定すると、失敗したと判断するまでの再試行回数を指定できます。バックオフ制限はデフォルトで6に設定されています。Jobに属していて失敗したPodはJobコントローラーにより再作成され、バックオフ遅延は指数関数的に増加し(10秒、20秒、40秒…)、最大6分まで増加します。
再実行回数の算出方法は以下の2通りです:
.status.phase = "Failed"
で設定されたPod数を計算します。restartPolicy = "OnFailure"
と設定された場合、.status.phase
がPending
またはRunning
であるPodに属するすべてのコンテナで再試行する回数を計算します。
どちらかの計算が.spec.backoffLimit
に達した場合、Jobは失敗とみなされます。
JobTrackingWithFinalizers
機能が無効な場合、
失敗したPodの数は、API内にまだ存在するPodのみに基づいています。
備考:
restartPolicy = "OnFailure"
が設定されたJobはバックオフ制限に達すると、属するPodは全部終了されるので注意してください。これにより、Jobの実行ファイルのデバッグ作業が難しくなる可能性があります。失敗したJobからの出力が不用意に失われないように、Jobのデバッグ作業をする際はrestartPolicy = "Never"
を設定するか、ロギングシステムを使用することをお勧めします。Pod失敗ポリシー
Kubernetes v1.26 [beta]
備考:
クラスターでJobPodFailurePolicy
フィーチャーゲートが有効になっている場合のみ、Jobに対してPod失敗ポリシーを設定することができます。さらにPod失敗ポリシーでPodの中断条件を検知して処理できるように、PodDisruptionConditions
フィーチャーゲートを有効にすることが推奨されます。(Podの中断条件を参照してください)。どちらのフィーチャーゲートもKubernetes 1.27で利用可能です。.spec.podFailurePolicy
フィールドで定義されるPod失敗ポリシーを使用すると、コンテナの終了コードとPodの条件に基づいてクラスターがPodの失敗を処理できるようになります。
状況によっては、Podの失敗を処理するときに、Jobの.spec.backoffLimit
に基づいたPod失敗のバックオフポリシーが提供する制御よりも、Podの失敗処理に対してより良い制御を求めるかもしれません。これらはいくつかの使用例です:
- 不要なPodの再起動を回避してワークロードの実行コストを最適化するために、Podの1つがソフトウェアバグを示す終了コードで失敗するとすぐにJobを終了させることができます。
- 中断が発生してもJobが完了するように、中断によって発生したPodの失敗(preemption、APIを起点とした退避、taintを起点とした立ち退き)を無視し、
.spec.backoffLimit
のリトライ回数にカウントしないようにすることができます。
上記のユースケースを満たすために、.spec.podFailurePolicy
フィールドでPod失敗ポリシーを設定できます。このポリシーは、コンテナの終了コードとPodの条件に基づいてPodの失敗を処理できます。
以下は、podFailurePolicy
を定義するJobのマニフェストです:
apiVersion: batch/v1
kind: Job
metadata:
name: job-pod-failure-policy-example
spec:
completions: 12
parallelism: 3
template:
spec:
restartPolicy: Never
containers:
- name: main
image: docker.io/library/bash:5
command: ["bash"] # example command simulating a bug which triggers the FailJob action
args:
- -c
- echo "Hello world!" && sleep 5 && exit 42
backoffLimit: 6
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
containerName: main # optional
operator: In # one of: In, NotIn
values: [42]
- action: Ignore # one of: Ignore, FailJob, Count
onPodConditions:
- type: DisruptionTarget # indicates Pod disruption
上記の例では、Pod失敗ポリシーの最初のルールは、main
コンテナが42の終了コードで失敗した場合、そのJobを失敗とマークすることを指定しています。以下は特に main
コンテナに関するルールです:
- 終了コード0はコンテナが成功したことを意味します。
- 終了コード42はJob全体が失敗したことを意味します。
- それ以外の終了コードは、コンテナが失敗したこと、つまりPod全体が失敗したことを示します。再起動の合計回数が
backoffLimit
未満であれば、Podは再作成されます。backoffLimit
に達した場合、Job全体が失敗したことになります。
備考:
PodテンプレートはrestartPolicy.Never
を指定しているため、kubeletはその特定のPodのmain
コンテナを再起動しません。Pod失敗ポリシーの2つ目のルールでは、DisruptionTarget
という条件で失敗したPodに対してIgnoreアクションを指定することで、Podの中断が.spec.backoffLimit
によるリトライの制限にカウントされないようにします。
備考:
Pod失敗ポリシーまたはPod失敗のバックオフポリシーのいずれかによってJobが失敗し、そのJobが複数のPodを実行している場合、KubernetesはそのJob内の保留中または実行中のすべてのPodを終了します。これらはAPIの要件と機能です:
.spec.podFailurePolicy
フィールドをJobに使いたい場合は、.spec.restartPolicy
をNever
に設定してそのJobのPodテンプレートも定義する必要があります。spec.podFailurePolicy.rules
で指定したPod失敗ポリシーのルールが順番に評価されます。あるPodの失敗がルールに一致すると、残りのルールは無視されます。Pod失敗に一致するルールがない場合は、デフォルトの処理が適用されます。spec.podFailurePolicy.rules[*].onExitCodes.containerName
を指定することで、ルールを特定のコンテナに制限することができます。指定しない場合、ルールはすべてのコンテナに適用されます。指定する場合は、Pod テンプレート内のコンテナ名またはinitContainer
名のいずれかに一致する必要があります。- Pod失敗ポリシーが
spec.podFailurePolicy.rules[*].action
にマッチしたときに実行されるアクションを指定できます。指定可能な値は以下のとおりです。FailJob
: PodのJobをFailed
としてマークし、実行中の Pod をすべて終了させる必要があることを示します。Ignore
:.spec.backoffLimit
のカウンターは加算されず、代替のPodが作成すべきであることを示します。Count
: Podがデフォルトの方法で処理されるべきであることを示します。.spec.backoffLimit
のカウンターが加算されます。
備考:
PodFailurePolicy
を使用すると、JobコントローラーはFailed
フェーズのPodのみにマッチします。削除タイムスタンプを持つPodで、終了フェーズ(Failed
またはSucceeded
)にないものは、まだ終了中と見なされます。これは、終了中Podは終了フェーズに達するまで追跡ファイナライザーを保持することを意味します。Kubernetes 1.27以降、Kubeletは削除されたPodを終了フェーズに遷移させます(参照:Podのフェーズ)。これにより、削除されたPodはJobコントローラーによってファイナライザーが削除されます。Jobの終了とクリーンアップ
Jobが完了すると、それ以上Podは作成されませんが、通常Podが削除されることもありません。
これらを残しておくと、完了したPodのログを確認でき、エラーや警告などの診断出力を確認できます。
またJobオブジェクトはJob完了後も残っているため、状態を確認することができます。古いJobの状態を把握した上で、削除するかどうかはユーザー次第です。Jobを削除するにはkubectl
(例:kubectl delete jobs/pi
またはkubectl delete -f ./job.yaml
)を使います。kubectl
でJobを削除する場合、Jobが作成したPodも全部削除されます。
デフォルトでは、Podが失敗しない(restartPolicy=Never
)またはコンテナがエラーで終了しない(restartPolicy=OnFailure
)限り、Jobは中断されることなく実行されます。.spec.backoffLimit
に達するとそのJobは失敗と見なされ、実行中のPodはすべて終了します。
Jobを終了させるもう一つの方法は、活動期間を設定することです。
Jobの.spec.activeDeadlineSeconds
フィールドに秒数を設定することで、活動期間を設定できます。
Podがいくつ作成されても、activeDeadlineSeconds
はJobの存続する時間に適用されます。
JobがactiveDeadlineSeconds
に達すると、実行中のすべてのPodは終了され、Jobの状態はtype: Failed
になり、理由はreason: DeadlineExceeded
になります。
ここで要注意なのは、Jobの.spec.activeDeadlineSeconds
は.spec.backoffLimit
よりも優先されます。したがって、失敗して再試行しているPodが一つ以上持っているJobは、backoffLimit
に達していなくても、activeDeadlineSeconds
で指定された設定時間に達すると、追加のPodをデプロイしなくなります。
例えば:
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-timeout
spec:
backoffLimit: 5
activeDeadlineSeconds: 100
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
Job仕様と、Jobに属するPodテンプレートの仕様は両方ともactiveDeadlineSeconds
フィールドを持っているので注意してください。適切なレベルで設定していることを確認してください。
またrestartPolicy
はJob自体ではなく、Podに適用されることも注意してください: Jobの状態はtype: Failed
になると、自動的に再起動されることはありません。
つまり、.spec.activeDeadlineSeconds
と.spec.backoffLimit
によって引き起こされるJob終了メカニズムは、永久的なJob失敗につながり、手動で介入して解決する必要があります。
終了したJobの自動クリーンアップ
終了したJobは通常システムに残す必要はありません。残ったままにしておくとAPIサーバーに負担をかけることになります。Jobが上位コントローラーにより直接管理されている場合、例えばCronJobsの場合、Jobは指定された容量ベースのクリーンアップポリシーに基づき、CronJobによりクリーンアップされます。
終了したJobのTTLメカニズム
Kubernetes v1.23 [stable]
終了したJob(状態がComplete
かFailed
になったJob)を自動的にクリーンアップするもう一つの方法は
TTLコントローラーより提供されたTTLメカニズムです。.spec.ttlSecondsAfterFinished
フィールドを指定することで、終了したリソースをクリーンアップすることができます。
TTLコントローラーでJobをクリーンアップする場合、Jobはカスケード的に削除されます。つまりJobを削除する際に、Jobに属しているオブジェクト、例えばPodなども一緒に削除されます。Jobが削除される場合、Finalizerなどの、Jobのライフサイクル保証は守られることに注意してください。
例えば:
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-ttl
spec:
ttlSecondsAfterFinished: 100
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
Job pi-with-ttl
は終了してからの100
秒後に自動的に削除されるようになっています。
このフィールドに0
を設定すると、Jobは終了後すぐに自動削除の対象になります。このフィールドに何も設定しないと、Jobが終了してもTTLコントローラーによるクリーンアップはされません。
備考:
ttlSecondsAfterFinished
フィールドを設定することが推奨されます。管理されていないJob(CronJobなどの、他のワークロードAPIを経由せずに、直接作成したJob)はorphanDependents
というデフォルトの削除ポリシーがあるため、Jobが完全に削除されても、属しているPodが残ってしまうからです。
コントロールプレーンは最終的に、失敗または完了して削除されたJobに属するPodをガベージコレクションしますが、Podが残っていると、クラスターのパフォーマンスが低下することがあり、最悪の場合、この低下によりクラスターがオフラインになることがあります。
LimitRangesとリソースクォータで、指定する名前空間が消費できるリソースの量に上限を設定することができます。
Jobパターン
Jobオブジェクトは、Podの確実な並列実行をサポートするために使用されます。科学技術計算でよく見られるような、密接に通信を行う並列処理をサポートするようには設計されていません。独立だが関連性のある一連の作業項目の並列処理をサポートします。例えば送信すべき電子メール、レンダリングすべきフレーム、トランスコードすべきファイル、スキャンすべきNoSQLデータベースのキーの範囲、などです。
複雑なシステムでは、異なる作業項目のセットが複数存在する場合があります。ここでは、ユーザーが一斉に管理したい作業項目のセットが一つだけの場合 — つまりバッチJobだけを考えます。
並列計算にはいくつかのパターンがあり、それぞれに長所と短所があります。 トレードオフの関係にあるのは:
- 各作業項目に1つのJobオブジェクト vs. すべての作業項目に1つのJobオブジェクト。
後者は大量の作業項目を処理する場合に適しています。
前者は大量のJobオブジェクトを管理するため、ユーザーとシステムにオーバーヘッドをかけることになります。 - 作成されるPod数が作業項目数と等しい、 vs. 各Podが複数の作業項目を処理する。 前者は通常、既存のコードやコンテナへの変更が少なくて済みます。 後者は上記と同じ理由で、大量の作業項目を処理する場合に適しています。
- ワークキューを利用するアプローチもいくつかあります。それを使うためには、キューサービスを実行し、既存のプログラムやコンテナにワークキューを利用させるための改造を行う必要があります。 他のアプローチは既存のコンテナ型アプリケーションに適用しやすいです。
ここでは、上記のトレードオフをまとめてあり、それぞれ2~4列目に対応しています。 またパターン名のところは、例やより詳しい説明が書いてあるページへのリンクになっています。
パターン | 単一Jobオブジェクト | Podが作業項目より少ない? | アプリを修正せずに使用できる? |
---|---|---|---|
作業項目ごとにPodを持つキュー | ✓ | 時々 | |
Pod数可変のキュー | ✓ | ✓ | |
静的な処理の割り当てを使用したインデックス付きJob | ✓ | ✓ | |
Jobテンプレート拡張 | ✓ | ||
Pod間通信を使用したJob | ✓ | 時々 | 時々 |
.spec.completions
で完了数を指定する場合、Jobコントローラーより作成された各Podは同一のspec
を持ちます。これは、このタスクのすべてのPodが同じコマンドライン、同じイメージ、同じボリューム、そして(ほぼ)同じ環境変数を持つことを意味します。これらのパターンは、Podが異なる作業をするためのさまざまな配置方法になります。
この表は、各パターンで必要な.spec.parallelism
と.spec.completions
の設定を示しています。
ここで、W
は作業項目の数を表しています。
パターン | .spec.completions | .spec.parallelism |
---|---|---|
作業項目ごとにPodを持つキュー | W | 任意 |
Pod数可変のキュー | null | 任意 |
静的な処理の割り当てを使用したインデックス付きJob | W | 任意 |
Jobテンプレート拡張 | 1 | 1であるべき |
Pod間通信を使用したJob | W | W |
高度な使い方
Jobの一時停止
Kubernetes v1.24 [stable]
Jobが作成されると、JobコントローラーはJobの要件を満たすために直ちにPodの作成を開始し、Jobが完了するまで作成し続けます。しかし、Jobの実行を一時的に中断して後で再開したい場合、または一時停止状態のJobを再開し、再開時間は後でカスタムコントローラーに判断させたい場合はあると思います。
Jobを一時停止するには、Jobの.spec.suspend
フィールドをtrueに修正し、後でまた再開したい場合にはfalseに修正すればよいです。
.spec.suspend
をtrueに設定してJobを作成すると、一時停止状態のままで作成されます。
一時停止状態のJobを再開すると、.status.startTime
フィールドの値は現在時刻にリセットされます。これはつまり、Jobが一時停止して再開すると、.spec.activeDeadlineSeconds
タイマーは停止してリセットされることになります。
Jobを中断すると、状態がCompleted
ではない実行中のPodはすべてSIGTERMシグナルを受信して終了されます。Podのグレースフル終了の猶予期間がカウントダウンされ、この期間内に、Podはこのシグナルを処理しなければなりません。場合により、その後のために処理状況を保存したり、変更を元に戻したりする処理が含まれます。この方法で終了したPodはcompletions
数にカウントされません。
下記は一時停止状態のままで作成されたJobの定義例になります:
kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
suspend: true
parallelism: 1
completions: 5
template:
spec:
...
コマンドラインを使ってJobにパッチを当てることで、Jobの一時停止状態を切り替えることもできます。
活動中のJobを一時停止する:
kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'
一時停止中のJobを再開する:
kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'
Jobのstatusセクションで、Jobが停止中なのか、過去に停止したことがあるかを判断できます:
kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata and .spec omitted
status:
conditions:
- lastProbeTime: "2021-02-05T13:14:33Z"
lastTransitionTime: "2021-02-05T13:14:33Z"
status: "True"
type: Suspended
startTime: "2021-02-05T13:13:48Z"
Jobのcondition.typeが"Suspended"で、statusが"True"になった場合、Jobは一時停止中になります。lastTransitionTime
フィールドで、どのぐらい中断されたかを判断できます。statusが"False"になった場合、Jobは一時停止状態でしたが、今は実行されていることになります。conditionが書いていない場合、Jobは一度も停止していないことになります。
Jobが一時停止して再開した場合、Eventsも作成されます:
kubectl describe jobs/myjob
Name: myjob
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 12m job-controller Created pod: myjob-hlrpl
Normal SuccessfulDelete 11m job-controller Deleted pod: myjob-hlrpl
Normal Suspended 11m job-controller Job suspended
Normal SuccessfulCreate 3s job-controller Created pod: myjob-jvb44
Normal Resumed 3s job-controller Job resumed
最後の4つのイベント、特に"Suspended"と"Resumed"のイベントは、.spec.suspend
フィールドの値を切り替えた直接の結果です。この2つのイベントの間に、Podは作成されていないことがわかりますが、Jobが再開されるとすぐにPodの作成も再開されました。
可変スケジューリング命令
Kubernetes v1.27 [stable]
ほとんどの場合、並列Jobは、すべてのPodが同じゾーン、またはすべてのGPUモデルxかyのいずれかであるが、両方の混在ではない、などの制約付きで実行することが望ましいです。
suspendフィールドは、これらの機能を実現するための第一歩です。Suspendは、カスタムキューコントローラーがJobをいつ開始すべきかを決定することができます。しかし、Jobの一時停止が解除されると、カスタムキューコントローラーは、Job内のPodの実際の配置場所には影響を与えません。
この機能により、Jobが開始する前にスケジューリング命令を更新でき、カスタムキューコントローラーがPodの配置に影響を与えることができるようになります。同時に実際のPodからNodeへの割り当てをkube-schedulerにオフロードする能力を提供します。これは一時停止されたJobの中で、一度も一時停止解除されたことのないJobに対してのみ許可されます。
JobのPodテンプレートで更新可能なフィールドはnodeAffinity、nodeSelector、tolerations、labelsとannotations、スケジューリングゲートです。
独自のPodセレクターを指定
Jobオブジェクトを作成する際には通常、.spec.selector
を指定しません。Jobが作成された際に、システムのデフォルトロジックは、他のJobと重ならないようなセレクターの値を選択し、このフィールドに追加します。
しかし、場合によっては、この自動設定されたセレクターをオーバーライドする必要があります。そのためには、Jobの.spec.selector
を指定します。
その際には十分な注意が必要です。そのJobの他のPodと重なったラベルセレクターを指定し、無関係のPodにマッチした場合、無関係のJobのPodが削除されたり、無関係のPodが完了されてもこのJobの完了数とカウントしたり、片方または両方のJobがPodの作成または完了までの実行を拒否する可能性があります。
一意でないセレクターを選択した場合、他のコントローラー(例えばReplicationController)や属しているPodが予測できない挙動をする可能性があります。Kubernetesは.spec.selector
を間違って設定しても止めることはしません。
下記はこの機能の使用例を紹介しています。
old
と名付けたJobがすでに実行されていると仮定します。既存のPodをそのまま実行し続けてほしい一方で、作成する残りのPodには別のテンプレートを使用し、そのJobには新しい名前を付けたいとしましょう。これらのフィールドは更新できないため、Jobを直接更新できません。そのため、kubectl delete jobs/old --cascade=orphan
で、属しているPodが実行されたまま、old
Jobを削除します。削除する前に、どのセレクターを使用しているかをメモしておきます:
kubectl get job old -o yaml
出力結果はこのようになります:
kind: Job
metadata:
name: old
...
spec:
selector:
matchLabels:
batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
...
次に、new
という名前で新しくJobを作成し、同じセレクターを明示的に指定します。既存のPodもbatch.kubernetes.io/controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002
ラベルが付いているので、同じくnew
Jobによってコントロールされます。
通常システムが自動的に生成するセレクターを使用しないため、新しいJobで manualSelector: true
を指定する必要があります。
kind: Job
metadata:
name: new
...
spec:
manualSelector: true
selector:
matchLabels:
batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
...
新しいJobはa8f3d00d-c6d2-11e5-9f87-42010af00002
ではなく、別のuidを持つことになります。manualSelector: true
を設定することで、自分は何をしているかを知っていて、またこのミスマッチを許容することをシステムに伝えます。
FinalizerによるJob追跡
Kubernetes v1.26 [stable]
備考:
JobTrackingWithFinalizers
機能が無効になっている時に作成されたJobについては、コントロールプレーンを1.26にアップグレードしても、ファイナライザーを使用してJobを追跡しません。コントロールプレーンは任意のJobに属するPodを追跡し、そのPodがAPIサーバーから削除されたかどうか認識します。そのためJobコントローラーはファイナライザーbatch.kubernetes.io/job-tracking
を持つPodを作成します。コントローラーがファイナライザーを削除するのは、PodがJobステータスに反映された後なので、他のコントローラーやユーザがPodを削除することができます。
Kubernetes 1.26にアップグレードする前、またはフィーチャーゲートJobTrackingWithFinalizers
が有効になる前に作成されたJobは、Podファイナライザーを使用せずに追跡されます。Jobコントローラーは、クラスターに存在するPodのみに基づいて、succeeded
Podとfailed
Podのステータスカウンタを更新します。クラスターからPodが削除されると、コントロールプレーンはJobの進捗を見失う可能性があります。
Jobがbatch.kubernetes.io/job-tracking
というアノテーションを持っているかどうかをチェックすることで、コントロールプレーンがPodファイナライザーを使ってJobを追跡しているかどうかを判断できます。Jobからこのアノテーションを手動で追加したり削除したりしてはいけません。代わりに、JobがPodファイナライザーを使用して追跡されていることを確認するために、Jobを再作成することができます。
静的なインデックス付きJob
Kubernetes v1.27 [beta]
.spec.parallelism
と.spec.compleitions
の両方を、.spec.parallelism
== .spec.compleitions
となるように変更することで、インデックス付きJobを増減させることができます。APIサーバのElasticIndexedJob
フィーチャーゲートが無効になっている場合、.spec.compleitions
は不変です。
静的なインデックス付きJobの使用例としては、MPI、Horovod、Ray、PyTorchトレーニングジョブなど、インデックス付きJobのスケーリングを必要とするバッチワークロードがあります。
代替案
単なるPod
Podが動作しているノードが再起動または故障した場合、Podは終了し、再起動されません。しかし、終了したPodを置き換えるため、Jobが新しいPodを作成します。このため、たとえアプリケーションが1つのPodしか必要としない場合でも、単なるPodではなくJobを使用することをお勧めします。
Replication Controller
JobはReplication Controllersを補完するものです。 Replication Controllerは、終了することが想定されていないPod(Webサーバーなど)を管理し、Jobは終了することが想定されているPod(バッチタスクなど)を管理します。
Podのライフサイクルで説明したように、Job
はRestartPolicy
がOnFailure
かNever
と設定されているPodにのみ適用されます。(注意:RestartPolicy
が設定されていない場合、デフォルト値はAlways
になります)
シングルJobによるコントローラーPodの起動
もう一つのパターンは、一つのJobが一つPodを作り、そのPodがカスタムコントローラーのような役割を果たし、他のPodを作ります。これは最も柔軟性がありますが、使い始めるにはやや複雑で、Kubernetesとの統合もあまりできません。
このパターンの一例としては、Sparkマスターコントローラーを起動し、sparkドライバーを実行してクリーンアップするスクリプトを実行するPodをJobで起動する(sparkの例を参照)が挙げられます。
この方法のメリットは、全処理過程でJobオブジェクトが完了する保証がありながらも、どのPodを作成し、どのように作業を割り当てるかを完全に制御できることです。
次の項目
- Podsについて学ぶ。
- Jobのさまざまな実行方法について学ぶ:
- ワークキューを用いた粒度の粗い並列処理
- ワークキューを用いた粒度の細かい並列処理
- 静的な処理の割り当てを使用した並列処理のためのインデックス付きJob を使う
- テンプレートを元に複数のJobを作成: 拡張機能を用いた並列処理
- 終了したJobの自動クリーンアップのリンクから、クラスターが完了または失敗したJobをどのようにクリーンアップするかをご確認ください。
Job
はKubernetes REST APIの一部です。JobのAPIを理解するために、 Jobオブジェクトの定義をお読みください。- UNIXツールの
cron
と同様に、スケジュールに基づいて実行される一連のJobを定義するために使用できるCronJob
についてお読みください。 - 段階的な例に基づいて、
PodFailurePolicy
を使用して、回復可能なPod失敗と回復不可能なPod失敗の処理を構成する方法を練習します。
6 - 終了したリソースのためのTTLコントローラー(TTL Controller for Finished Resources)
Kubernetes v1.12 [alpha]
TTLコントローラーは実行を終えたリソースオブジェクトのライフタイムを制御するためのTTL (time to live) メカニズムを提供します。
TTLコントローラーは現在Jobのみ扱っていて、将来的にPodやカスタムリソースなど、他のリソースの実行終了を扱えるように拡張される予定です。
α版の免責事項: この機能は現在α版の機能で、kube-apiserverとkube-controller-managerのFeature GateのTTLAfterFinished
を有効にすることで使用可能です。
TTLコントローラー
TTLコントローラーは現在Jobに対してのみサポートされています。クラスターオペレーターはこの例のように、Jobの.spec.ttlSecondsAfterFinished
フィールドを指定することにより、終了したJob(完了した
もしくは失敗した
)を自動的に削除するためにこの機能を使うことができます。
TTLコントローラーは、そのリソースが終了したあと指定したTTLの秒数後に削除できるか推定します。言い換えると、そのTTLが期限切れになると、TTLコントローラーがリソースをクリーンアップするときに、そのリソースに紐づく従属オブジェクトも一緒に連続で削除します。注意点として、リソースが削除されるとき、ファイナライザーのようなライフサイクルに関する保証は尊重されます。
TTL秒はいつでもセット可能です。下記はJobの.spec.ttlSecondsAfterFinished
フィールドのセットに関するいくつかの例です。
- Jobがその終了後にいくつか時間がたった後に自動的にクリーンアップできるように、そのリソースマニフェストにこの値を指定します。
- この新しい機能を適用させるために、存在していてすでに終了したリソースに対してこのフィールドをセットします。
- リソース作成時に、このフィールドを動的にセットするために、管理webhookの変更をさせます。クラスター管理者は、終了したリソースに対して、このTTLポリシーを強制するために使うことができます。
- リソースが終了した後に、このフィールドを動的にセットしたり、リソースステータスやラベルなどの値に基づいて異なるTTL値を選択するために、管理webhookの変更をさせます。
注意
TTL秒の更新
注意点として、Jobの.spec.ttlSecondsAfterFinished
フィールドといったTTL期間はリソースが作成された後、もしくは終了した後に変更できます。しかし、一度Jobが削除可能(TTLの期限が切れたとき)になると、それがたとえTTLを伸ばすような更新に対してAPIのレスポンスで成功したと返されたとしても、そのシステムはJobが稼働し続けることをもはや保証しません。
タイムスキュー(Time Skew)
TTLコントローラーが、TTL値が期限切れかそうでないかを決定するためにKubernetesリソース内に保存されたタイムスタンプを使うため、この機能はクラスター内のタイムスキュー(時刻のずれ)に対してセンシティブとなります。タイムスキューは、誤った時間にTTLコントローラーに対してリソースオブジェクトのクリーンアップしてしまうことを引き起こすものです。
Kubernetesにおいてタイムスキューを避けるために、全てのNode上でNTPの稼働を必須とします(#6159を参照してください)。クロックは常に正しいものではありませんが、Node間におけるその差はとても小さいものとなります。TTLに0でない値をセットするときにこのリスクに対して注意してください。
次の項目
7 - CronJob
Kubernetes v1.8 [beta]
CronJob は繰り返しのスケジュールによってJobを作成します。
CronJob オブジェクトとは crontab (cron table)ファイルでみられる一行のようなものです。 Cron形式で記述された指定のスケジュールの基づき、定期的にジョブが実行されます。
注意:
すべてのCronJobスケジュール
: 時刻はジョブが開始されたkube-controller-managerのタイムゾーンに基づいています。
コントロールプレーンがkube-controller-managerをPodもしくは素のコンテナで実行している場合、CronJobコントローラーのタイムゾーンとして、kube-controller-managerコンテナに設定されたタイムゾーンを使用します。
CronJobリソースのためのマニフェストを作成する場合、その名前が有効なDNSサブドメイン名か確認してください。 名前は52文字を超えることはできません。これはCronJobコントローラーが自動的に、与えられたジョブ名に11文字を追加し、ジョブ名の長さは最大で63文字以内という制約があるためです。
CronJob
CronJobは、バックアップの実行やメール送信のような定期的であったり頻発するタスクの作成に役立ちます。 CronJobは、クラスターがアイドル状態になりそうなときにJobをスケジューリングするなど、特定の時間に個々のタスクをスケジュールすることもできます。
例
このCronJobマニフェスト例は、毎分ごとに現在の時刻とhelloメッセージを表示します。
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
(Running Automated Tasks with a CronJobではこの例をより詳しく説明しています。).
CronJobの制限
cronジョブは一度のスケジュール実行につき、 おおよそ 1つのジョブオブジェクトを作成します。ここで おおよそ と言っているのは、ある状況下では2つのジョブが作成される、もしくは1つも作成されない場合があるためです。通常、このようなことが起こらないようになっていますが、完全に防ぐことはできません。したがって、ジョブは 冪等 であるべきです。
startingDeadlineSeconds
が大きな値、もしくは設定されていない(デフォルト)、そして、concurrencyPolicy
をAllow
に設定している場合には、少なくとも一度、ジョブが実行されることを保証します。
最後にスケジュールされた時刻から現在までの間に、CronJobコントローラーはどれだけスケジュールが間に合わなかったのかをCronJobごとにチェックします。もし、100回以上スケジュールが失敗していると、ジョブは開始されずに、ログにエラーが記録されます。
Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.
startingDeadlineSeconds
フィールドが設定されると(nil
ではない)、最後に実行された時刻から現在までではなく、startingDeadlineSeconds
の値から現在までで、どれだけジョブを逃したのかをコントローラーが数えます。 startingDeadlineSeconds
が200
の場合、過去200秒間にジョブが失敗した回数を記録します。
スケジュールされた時間にCronJobが作成できないと、失敗したとみなされます。たとえば、concurrencyPolicy
がForbid
に設定されている場合、前回のスケジュールがまだ実行中にCronJobをスケジュールしようとすると、CronJobは作成されません。
例として、CronJobが08:30:00
を開始時刻として1分ごとに新しいJobをスケジュールするように設定され、startingDeadlineSeconds
フィールドが設定されていない場合を想定します。CronJobコントローラーが08:29:00
から10:21:00
の間にダウンしていた場合、スケジューリングを逃したジョブの数が100を超えているため、ジョブは開始されません。
このコンセプトをさらに掘り下げるために、CronJobが08:30:00
から1分ごとに新しいJobを作成し、startingDeadlineSeconds
が200秒に設定されている場合を想定します。CronJobコントローラーが前回の例と同じ期間(08:29:00
から10:21:00
まで)にダウンしている場合でも、10:22:00時点でJobはまだ動作しています。このようなことは、過去200秒間(言い換えると、3回の失敗)に何回スケジュールが間に合わなかったをコントローラーが確認するときに発生します。これは最後にスケジュールされた時間から今までのものではありません。
CronJobはスケジュールに一致するJobの作成にのみ関与するのに対して、JobはJobが示すPod管理を担います。
次の項目
Cron表現形式では、CronJobのschedule
フィールドのフォーマットを説明しています。
cronジョブの作成や動作の説明、CronJobマニフェストの例については、Running automated tasks with cron jobsを見てください。