1 - コンテナおよびPodへのメモリーリソースの割り当て

このページでは、メモリーの 要求制限 をコンテナに割り当てる方法について示します。コンテナは要求されたメモリーを確保することを保証しますが、その制限を超えるメモリーの使用は許可されません。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

クラスターの各ノードには、少なくとも300MiBのメモリーが必要になります。

このページのいくつかの手順では、クラスターにてmetrics-serverサービスを実行する必要があります。すでにmetrics-serverが動作している場合、これらの手順をスキップできます。

Minikubeを動作させている場合、以下のコマンドによりmetrics-serverを有効にできます:

minikube addons enable metrics-server

metrics-serverが実行されているか、もしくはリソースメトリクスAPI (metrics.k8s.io) の別のプロバイダが実行されていることを確認するには、以下のコマンドを実行してください:

kubectl get apiservices

リソースメトリクスAPIが利用可能であれば、出力には metrics.k8s.io への参照が含まれます。

NAME
v1beta1.metrics.k8s.io

namespaceの作成

この練習で作成するリソースがクラスター内で分離されるよう、namespaceを作成します。

kubectl create namespace mem-example

メモリーの要求と制限を指定する

コンテナにメモリーの要求を指定するには、コンテナのリソースマニフェストにresources:requestsフィールドを追記します。メモリーの制限を指定するには、resources:limitsを追記します。

この練習では、一つのコンテナをもつPodを作成します。コンテナに100MiBのメモリー要求と200MiBのメモリー制限を与えます。Podの設定ファイルは次のようになります:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

設定ファイルのargsセクションでは、コンテナ起動時の引数を与えます。"--vm-bytes", "150M"という引数では、コンテナに150MiBのメモリーを割り当てます。

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example

Podのコンテナが起動していることを検証してください:

kubectl get pod memory-demo --namespace=mem-example

Podの詳細な情報を確認してください:

kubectl get pod memory-demo --output=yaml --namespace=mem-example

この出力では、Pod内の一つのコンテナに100MiBのメモリー要求と200MiBのメモリー制限があることを示しています。

...
resources:
  limits:
    memory: 200Mi
  requests:
    memory: 100Mi
...

kubectl topを実行し、Podのメトリクスを取得してください:

kubectl top pod memory-demo --namespace=mem-example

この出力では、Podが約162,900,000バイト(約150MiB)のメモリーを使用していることを示しています。Podの100MiBの要求を超えていますが、200MiBの制限には収まっています。

NAME                        CPU(cores)   MEMORY(bytes)
memory-demo                 <something>  162856960

Podを削除してください:

kubectl delete pod memory-demo --namespace=mem-example

コンテナのメモリー制限を超える

ノードに利用可能なメモリーがある場合、コンテナはメモリー要求を超えることができます。しかしながら、メモリー制限を超えて使用することは許可されません。コンテナが制限を超えてメモリーを確保しようとした場合、そのコンテナは終了候補となります。コンテナが制限を超えてメモリーを消費し続ける場合、コンテナは終了されます。終了したコンテナを再起動できる場合、ほかのランタイムの失敗時と同様に、kubeletがコンテナを再起動させます。

この練習では、制限を超えてメモリーを確保しようとするPodを作成します。以下に50MiBのメモリー要求と100MiBのメモリー制限を与えたコンテナを持つ、Podの設定ファイルを示します:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

設定ファイルのargsセクションでは、コンテナに250MiBのメモリーを割り当てており、これは100MiBの制限を十分に超えています。

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example

Podの詳細な情報を確認してください:

kubectl get pod memory-demo-2 --namespace=mem-example

この時点で、コンテナは起動中か強制終了されているでしょう。コンテナが強制終了されるまで上記のコマンドをくり返し実行してください:

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

コンテナステータスの詳細な情報を取得してください:

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

この出力では、コンテナがメモリー不足 (OOM) により強制終了されたことを示しています。

lastState:
   terminated:
     containerID: docker://65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

この練習のコンテナはkubeletによって再起動されます。次のコマンドを数回くり返し実行し、コンテナが強制終了と再起動を続けていることを確認してください:

kubectl get pod memory-demo-2 --namespace=mem-example

この出力では、コンテナが強制終了され、再起動され、再度強制終了および再起動が続いていることを示しています:

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

Podの履歴について詳細な情報を確認してください:

kubectl describe pod memory-demo-2 --namespace=mem-example

この出力では、コンテナの開始とその失敗が繰り返されていることを示しています:

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

クラスターのノードの詳細な情報を確認してください:

kubectl describe nodes

この出力には、メモリー不足の状態のためコンテナが強制終了された記録が含まれます:

Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

Podを削除してください:

kubectl delete pod memory-demo-2 --namespace=mem-example

ノードよりも大きいメモリー要求を指定する

メモリー要求と制限はコンテナと関連づけられていますが、Podにメモリー要求と制限が与えられていると考えるとわかりやすいでしょう。Podのメモリー要求は、Pod内のすべてのコンテナのメモリー要求の合計となります。同様に、Podのメモリー制限は、Pod内のすべてのコンテナのメモリー制限の合計となります。

Podのスケジューリングは要求に基づいています。Podはノード上で動作するうえで、そのメモリー要求に対してノードに十分利用可能なメモリーがある場合のみスケジュールされます。

この練習では、クラスター内のノードのキャパシティを超える大きさのメモリー要求を与えたPodを作成します。以下に1000GiBのメモリー要求を与えた一つのコンテナを持つ、Podの設定ファイルを示します。これは、クラスター内のノードのキャパシティを超える可能性があります。

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "1000Gi"
      requests:
        memory: "1000Gi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example

Podの状態を確認してください:

kubectl get pod memory-demo-3 --namespace=mem-example

この出力では、Podのステータスが待機中であることを示しています。つまり、Podがどのノードに対しても実行するようスケジュールされておらず、いつまでも待機状態のままであることを表しています:

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

イベントを含むPodの詳細な情報を確認してください:

kubectl describe pod memory-demo-3 --namespace=mem-example

この出力では、ノードのメモリー不足のためコンテナがスケジュールされないことを示しています:

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

メモリーの単位

メモリーリソースはバイト単位で示されます。メモリーをE、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Kiという接尾辞とともに、整数型または固定小数点整数で表現できます。たとえば、以下はおおよそ同じ値を表します:

128974848, 129e6, 129M , 123Mi

Podを削除してください:

kubectl delete pod memory-demo-3 --namespace=mem-example

メモリー制限を指定しない場合

コンテナのメモリー制限を指定しない場合、次のいずれかの状態となります:

  • コンテナのメモリー使用量に上限がない状態となります。コンテナは実行中のノードで利用可能なすべてのメモリーを使用でき、その後OOM Killerが呼び出される可能性があります。さらに、OOM killの場合、リソース制限のないコンテナは強制終了される可能性が高くなります。

  • メモリー制限を与えられたnamespaceでコンテナを実行されると、コンテナにはデフォルトの制限値が自動的に指定されます。クラスターの管理者はLimitRangeによってメモリー制限のデフォルト値を指定できます。

メモリー要求と制限のモチベーション

クラスターで動作するコンテナにメモリー要求と制限を設定することで、クラスターのノードで利用可能なメモリーリソースを効率的に使用することができます。Podのメモリー要求を低く保つことで、Podがスケジュールされやすくなります。メモリー要求よりも大きい制限を与えることで、次の2つを実現できます:

  • Podは利用可能なメモリーを、突発的な活動(バースト)に使用することができます。
  • バースト中のPodのメモリー使用量は、適切な量に制限されます。

クリーンアップ

namespaceを削除してください。これにより、今回のタスクで作成したすべてのPodが削除されます:

kubectl delete namespace mem-example

次の項目

アプリケーション開発者向け

クラスター管理者向け

2 - コンテナおよびPodへのCPUリソースの割り当て

このページでは、CPUの requestlimit をコンテナに割り当てる方法について示します。コンテナは設定された制限を超えてCPUを使用することはできません。システムにCPUの空き時間がある場合、コンテナには要求されたCPUを割り当てられます。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

タスク例を実行するには、クラスターに少なくとも利用可能な1 CPUが必要です。

このページのいくつかの手順では、クラスターにてmetrics-serverサービスを実行する必要があります。すでにmetrics-serverが動作している場合、これらの手順をスキップできます。

Minikubeを動作させている場合、以下のコマンドによりmetrics-serverを有効にできます:

minikube addons enable metrics-server

metrics-serverが実行されているか、もしくはリソースメトリクスAPI (metrics.k8s.io) の別のプロバイダーが実行されていることを確認するには、以下のコマンドを実行してください:

kubectl get apiservices

リソースメトリクスAPIが利用可能であれば、出力には metrics.k8s.io への参照が含まれます。

NAME
v1beta1.metrics.k8s.io

namespaceの作成

この練習で作成するリソースがクラスター内で分離されるよう、Namespaceを作成します。

kubectl create namespace cpu-example

CPUの要求と制限を指定する

コンテナにCPUの要求を指定するには、コンテナのリソースマニフェストにresources:requestsフィールドを追記します。CPUの制限を指定するには、resources:limitsを追記します。

この練習では、一つのコンテナをもつPodを作成します。コンテナに0.5 CPUの要求と1 CPUの制限を与えます。Podの設定ファイルは次のようになります:

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

設定ファイルのargsセクションでは、コンテナ起動時の引数を与えます。-cpus "2"という引数では、コンテナに2 CPUを割り当てます。

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit.yaml --namespace=cpu-example

Podのコンテナが起動していることを検証してください:

kubectl get pod cpu-demo --namespace=cpu-example

Podの詳細な情報を確認してください:

kubectl get pod cpu-demo --output=yaml --namespace=cpu-example

この出力では、Pod内の一つのコンテナに500ミリCPUの要求と1 CPUの制限があることを示しています。

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 500m

kubectl topを実行し、Podのメトリクスを取得してください:

kubectl top pod cpu-demo --namespace=cpu-example

この出力では、Podが974ミリCPUを使用していることを示しています。Podの設定で指定した1 CPUの制限よりわずかに小さい値です。

NAME                        CPU(cores)   MEMORY(bytes)
cpu-demo                    974m         <something>

-cpu "2"を設定することで、コンテナが2 CPU利用しようとすることを思い出してください。しかしながら、コンテナは約1 CPUしか使用することができません。コンテナが制限よりも多くのCPUリソースを利用しようとしているため、コンテナのCPUの利用が抑制されています。

CPUの単位

CPUリソースは CPU の単位で示されます。Kubernetesにおいて1つのCPUは次に等しくなります:

  • 1 AWS vCPU
  • 1 GCPコア
  • 1 Azure vCore
  • ハイパースレッディングが有効なベアメタルIntelプロセッサーの1スレッド

小数値も利用可能です。0.5 CPUを要求するコンテナには、1 CPUを要求するコンテナの半分のCPUが与えられます。mというミリを表す接尾辞も使用できます。たとえば、100m CPU、100 milliCPU、0.1 CPUはすべて同じです。1m以上の精度は指定できません。

CPUはつねに絶対量として要求され、決して相対量としては要求されません。0.1はシングルコア、デュアルコア、48コアCPUのマシンで同じ量となります。

Podを削除してください:

kubectl delete pod cpu-demo --namespace=cpu-example

ノードよりも大きいCPU要求を指定する

CPU要求と制限はコンテナと関連づけられていますが、PodにCPU要求と制限が与えられていると考えるとわかりやすいでしょう。PodのCPU要求は、Pod内のすべてのコンテナのCPU要求の合計となります。同様に、PodのCPU制限は、Pod内のすべてのコンテナのCPU制限の合計となります。

Podのスケジューリングはリソースの要求量に基づいています。Podはノード上で動作するうえで、そのCPU要求に対してノードに十分利用可能なCPUリソースがある場合のみスケジュールされます。

この練習では、クラスター内のノードのキャパシティを超える大きさのCPU要求を与えたPodを作成します。以下に100 CPUの要求を与えた一つのコンテナを持つ、Podの設定ファイルを示します。これは、クラスター内のノードのキャパシティを超える可能性があります。

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo-2
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr-2
    image: vish/stress
    resources:
      limits:
        cpu: "100"
      requests:
        cpu: "100"
    args:
    - -cpus
    - "2"

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit-2.yaml --namespace=cpu-example

Podの状態を確認してください:

kubectl get pod cpu-demo-2 --namespace=cpu-example

この出力では、Podのステータスが待機中であることを示しています。つまり、Podがどのノードに対しても実行するようスケジュールされておらず、いつまでも待機状態のままであることを表しています:

NAME         READY     STATUS    RESTARTS   AGE
cpu-demo-2   0/1       Pending   0          7m

イベントを含むPodの詳細な情報を確認してください:

kubectl describe pod cpu-demo-2 --namespace=cpu-example

この出力では、ノードのCPU不足のためコンテナがスケジュールされないことを示しています:

Events:
  Reason                        Message
  ------                        -------
  FailedScheduling      No nodes are available that match all of the following predicates:: Insufficient cpu (3).

Podを削除してください:

kubectl delete pod cpu-demo-2 --namespace=cpu-example

CPU制限を指定しない場合

コンテナのCPU制限を指定しない場合、次のいずれかの状態となります:

  • コンテナのCPUリソースの使用量に上限がない状態となります。コンテナは実行中のノードで利用可能なすべてのCPUを使用できます。

  • CPU制限を与えられたnamespaceでコンテナを実行されると、コンテナにはデフォルトの制限値が自動的に指定されます。クラスターの管理者はLimitRangeによってCPU制限のデフォルト値を指定できます。

CPU要求と制限のモチベーション

クラスターで動作するコンテナにCPU要求と制限を設定することで、クラスターのノードで利用可能なCPUリソースを効率的に使用することができます。PodのCPU要求を低く保つことで、Podがスケジュールされやすくなります。CPU要求よりも大きい制限を与えることで、次の2つを実現できます:

  • Podは利用可能なCPUリソースを、突発的な活動(バースト)に使用することができます。
  • バースト中のPodのCPUリソース量は、適切な量に制限されます。

クリーンアップ

namespaceを削除してください:

kubectl delete namespace cpu-example

次の項目

アプリケーション開発者向け

クラスター管理者向け

3 - Windows Podとコンテナに対するGMSAの設定

FEATURE STATE: Kubernetes v1.18 [stable]

このページでは、Windowsノード上で動作するPodとコンテナに対するグループ管理サービスアカウント(GMSA)の設定方法を示します。 グループ管理サービスアカウントは特別な種類のActive Directoryアカウントで、パスワードの自動管理、簡略化されたサービスプリンシパル名(SPN)の管理、および管理を他の管理者に委任する機能を複数のサーバーに提供します。

Kubernetesでは、GMSA資格情報仕様は、Kubernetesクラスター全体をスコープとするカスタムリソースとして設定されます。 WindowsのPodおよびPod内の個々のコンテナは、他のWindowsサービスとやり取りする際に、ドメインベースの機能(例えばKerberos認証)に対してGMSAを使用するように設定できます。

始める前に

Kubernetesクラスターが必要で、kubectlコマンドラインツールがクラスターと通信できるように設定されている必要があります。 クラスターにはWindowsワーカーノードを持つことが求められます。 このセクションでは、各クラスターに対して一度だけ実施する必要がある一連の初期ステップについて説明します:

GMSACredentialSpec CRDのインストール

カスタムリソースタイプGMSACredentialSpecを定義するために、GMSA資格情報仕様リソースに対するCustomResourceDefinition(CRD)をクラスター上で設定する必要があります。 GMSA CRDのYAMLをダウンロードし、gmsa-crd.yamlとして保存します。 次に、kubectl apply -f gmsa-crd.yamlを実行してCRDをインストールします。

GMSAユーザーを検証するためのWebhookのインストール

Podまたはコンテナレベルで参照するGMSA資格情報仕様を追加および検証するために、Kubernetesクラスター上で2つのWebhookを設定する必要があります:

  1. Mutating Webhookは、(Podの仕様から名前で指定された)GMSAへの参照を、JSON形式の完全な資格情報仕様としてPodのspecの中へ展開します。

  2. Validating Webhookは、すべてのGMSAへの参照に対して、Podサービスアカウントによる利用が認可されているか確認します。

上記Webhookと関連するオブジェクトをインストールするためには、次の手順が必要です:

  1. 証明書と鍵のペアを作成します(Webhookコンテナがクラスターと通信できるようにするために使用されます)

  2. 上記の証明書を含むSecretをインストールします。

  3. コアとなるWebhookロジックのためのDeploymentを作成します。

  4. Deploymentを参照するValidating WebhookとMutating Webhookの設定を作成します。

上で述べたGMSA Webhookと関連するオブジェクトを展開、構成するためのスクリプトがあります。 スクリプトは、--dry-run=serverオプションをつけることで、クラスターに対して行われる変更内容をレビューすることができます。

Webhookと関連するオプジェクトを(適切なパラメーターを渡すことで)手動で展開するためのYAMLテンプレートもあります。

Active DirectoryにGMSAとWindowsノードを構成する

Windows GMSAのドキュメントに記載されている通り、Kubernetes内のPodがGMSAを使用するために設定できるようにする前に、Active Directory内に目的のGMSAを展開する必要があります。 Windows GMSAのドキュメントに記載されている通り、(Kubernetesクラスターの一部である)Windowsワーカーノードは、目的のGMSAに関連づけられたシークレット資格情報にアクセスできるように、Active Directory内で設定されている必要があります。

GMSA資格情報仕様リソースの作成

(前述の通り)GMSACredentialSpec CRDをインストールすると、GMSA資格情報仕様を含むカスタムリソースを設定できます。 GMSA資格情報仕様には、シークレットや機密データは含まれません。 それは、コンテナランタイムが目的のコンテナのGMSAをWindowsに対して記述するために使用できる情報です。 GMSA資格情報仕様は、PowerShellスクリプトのユーティリティを使用して、YAMLフォーマットで生成することができます。

以下は、GMSA資格情報仕様をJSON形式で手動で生成し、その後それをYAMLに変換する手順です:

  1. CredentialSpecモジュールをインポートします: ipmo CredentialSpec.psm1

  2. New-CredentialSpecを使用してJSONフォーマットの資格情報仕様を作成します。 WebApp1という名前のGMSA資格情報仕様を作成するには、New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer)を実行します

  3. Get-CredentialSpecを使用して、JSONファイルのパスを表示します。

  4. credspecファイルをJSON形式からYAML形式に変換し、Kubernetesで設定可能なGMSACredentialSpecカスタムリソースにするために、必要なヘッダーフィールドであるapiVersionkindmetadatacredspecを記述します。

次のYAML設定は、gmsa-WebApp1という名前のGMSA資格情報仕様を記述しています:

apiVersion: windows.k8s.io/v1
kind: GMSACredentialSpec
metadata:
  name: gmsa-WebApp1  # これは任意の名前で構いませんが、参照時に使用されます
credspec:
  ActiveDirectoryConfig:
    GroupManagedServiceAccounts:
    - Name: WebApp1   # GMSAアカウントのユーザー名
      Scope: CONTOSO  # NETBIOSドメイン名
    - Name: WebApp1   # GMSAアカウントのユーザー名
      Scope: contoso.com # DNSドメイン名
  CmsPlugins:
  - ActiveDirectory
  DomainJoinConfig:
    DnsName: contoso.com  # DNSドメイン名
    DnsTreeName: contoso.com # DNSルートドメイン名
    Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a  # GUID
    MachineAccountName: WebApp1 # GMSAアカウントのユーザー名
    NetBiosName: CONTOSO  # NETBIOSドメイン名
    Sid: S-1-5-21-2126449477-2524075714-3094792973 # GMSAのSID

上記の資格情報仕様リソースはgmsa-Webapp1-credspec.yamlとして保存され、次のコマンドを使用してクラスターに適用されます: kubectl apply -f gmsa-Webapp1-credspec.yml

指定されたGMSA資格情報仕様上にRBACを有効にするためのクラスターロールの設定

各GMSA資格情報仕様リソースに対して、クラスターロールを定義する必要があります。 これは特定のGMSAリソース上のuse verbを、通常はサービスアカウントであるsubjectに対して認可します。 次の例は、前述のgmsa-WebApp1資格情報仕様の利用を認可するクラスターロールを示しています。 ファイルをgmsa-webapp1-role.yamlとして保存し、kubectl apply -f gmsa-webapp1-role.yamlを使用して適用します。

# credspecを読むためのロールを作成
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: webapp1-role
rules:
- apiGroups: ["windows.k8s.io"]
  resources: ["gmsacredentialspecs"]
  verbs: ["use"]
  resourceNames: ["gmsa-WebApp1"]

指定されたGMSA credspecを使用するためのサービスアカウントへのロールの割り当て

(Podに対して設定される)サービスアカウントを、上で作成したクラスターロールに結びつける必要があります。 これによって、要求されたGMSA資格情報仕様のリソースの利用をサービスアカウントに対して認可できます。 以下は、上で作成した資格情報仕様リソースgmsa-WebApp1を使うために、既定のサービスアカウントに対してクラスターロールwebapp1-roleを割り当てる方法を示しています。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: allow-default-svc-account-read-on-gmsa-WebApp1
  namespace: default
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: webapp1-role
  apiGroup: rbac.authorization.k8s.io

Podのspec内で参照するGMSA資格情報仕様の設定

PodのspecのフィールドsecurityContext.windowsOptions.gmsaCredentialSpecNameは、要求されたGMSA資格情報仕様のカスタムリソースに対する参照を、Podのspec内で指定するために使用されます。 これは、Podのspec内の全てのコンテナに対して、指定されたGMSAを使用するように設定します。 gmsa-WebApp1を参照するために追加された注釈を持つPodのspecのサンプルです:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      securityContext:
        windowsOptions:
          gmsaCredentialSpecName: gmsa-webapp1
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
      nodeSelector:
        kubernetes.io/os: windows

Podのspec内の個々のコンテナも、コンテナ毎のsecurityContext.windowsOptions.gmsaCredentialSpecNameフィールドを使用することで、要求されたGMSA credspecを指定することができます。

設定例:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
        securityContext:
          windowsOptions:
            gmsaCredentialSpecName: gmsa-Webapp1
      nodeSelector:
        kubernetes.io/os: windows

(上記のような)GMSAフィールドが入力されたPod specがクラスターに適用されると、次の一連のイベントが発生します:

  1. Mutating WebhookがGMSA資格情報仕様リソースへの全ての参照を解決し、GMSA資格情報仕様の内容を展開します。

  2. Validating Webhookは、Podに関連付けられたサービスアカウントが、指定されたGMSA資格情報仕様上のuse verbに対して認可されていることを保証します。

  3. コンテナランタイムは、指定されたGMSA資格情報仕様で各Windowsコンテナを設定します。 それによってコンテナはGMSAのIDがActive Directory内にあることを仮定でき、そのIDを使用してドメイン内のサービスにアクセスできます。

ホスト名またはFQDNを使用してネットワーク共有に対して認証する

PodからSMB共有へのホスト名やFQDNを使用した接続で問題が発生した際に、IPv4アドレスではSMB共有にアクセスすることはできる場合には、次のレジストリキーがWindowsノード上で設定されているか確認してください。

reg add "HKLM\SYSTEM\CurrentControlSet\Services\hns\State" /v EnableCompartmentNamespace /t REG_DWORD /d 1

その後、動作の変更を反映させるために、実行中のPodを再作成する必要があります。 このレジストリキーがどのように使用されるかについてのより詳細な情報は、こちらを参照してください。

トラブルシューティング

自分の環境でGMSAがうまく動作しない時に実行できるトラブルシューティングステップがあります。

まず、credspecがPodに渡されたことを確認します。 そのためには、Podのひとつにexecで入り、nltest.exe /parentdomainコマンドの出力をチェックする必要があります。

以下の例では、Podはcredspecを正しく取得できませんでした:

kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe

nltest.exe /parentdomainの結果は次のようなエラーになります:

Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

Podが正しくcredspecを取得したら、次にドメインと正しく通信できることを確認します。 まずはPodの中から、ドメインのルートを見つけるために、手短にnslookupを実行します。

これから3つのことがわかります:

  1. PodがDCまで到達できる
  2. DCがPodに到達できる
  3. DNSが正しく動作している

DNSと通信のテストをパスしたら、次にPodがドメインとセキュアチャネル通信を構築することができるか確認する必要があります。 そのためには、再びexecを使用してPodの中に入り、nltest.exe /queryコマンドを実行します。

nltest.exe /query

結果は次のように出力されます:

I_NetLogonControl failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

これは、Podがなんらかの理由で、credspec内で指定されたアカウントを使用してドメインにログオンできなかったことを示しています。 次のコマンドを実行してセキュアチャネルを修復してみてください:

nltest /sc_reset:domain.example

コマンドが成功したら、このような出力を確認することができます:

Flags: 30 HAS_IP  HAS_TIMESERV
Trusted DC Name \\dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

もし上記によってエラーが解消された場合は、次のライフサイクルフックをPodのspecに追加することで、手順を自動化できます。 エラーが解消されなかった場合は、credspecをもう一度調べ、正しく完全であることを確認する必要があります。

        image: registry.domain.example/iis-auth:1809v1
        lifecycle:
          postStart:
            exec:
              command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]
        imagePullPolicy: IfNotPresent

Podのspecに上記のlifecycleセクションを追加すると、nltest.exe /queryコマンドがエラーとならずに終了するまでnetlogonサービスを再起動するために、Podは一連のコマンドを実行します。

4 - PodにQuality of Serviceを設定する

このページでは、特定のQuality of Service (QoS)クラスをPodに割り当てるための設定方法を示します。Kubernetesは、Podのスケジューリングおよび退役を決定するためにQoSクラスを用います。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

QoSクラス

KubernetesはPodの作成時に次のいずれかのQoSクラスをPodに割り当てます:

  • Guaranteed
  • Burstable
  • BestEffort

namespaceの作成

この演習で作成するリソースがクラスター内で分離されるよう、namespaceを作成します。

kubectl create namespace qos-example

GuaranteedのQoSクラスを割り当てたPodを作成する

PodにGuaranteedのQoSクラスを与えるには、以下が必要になります:

  • Pod内のすべてのコンテナにメモリーの制限と要求が与えられており、同じ値であること。
  • Pod内のすべてのコンテナにCPUの制限と要求が与えられており、同じ値であること。

以下に1つのコンテナをもつPodの設定ファイルを示します。コンテナには200MiBのメモリー制限とリクエストを与え、700ミリCPUの制限と要求を与えます。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod.yaml --namespace=qos-example

Podの詳細な情報を確認してください:

kubectl get pod qos-demo --namespace=qos-example --output=yaml

この出力では、KubernetesがPodにGuaranteed QoSクラスを与えたことを示しています。Podのコンテナにメモリー制限と一致するメモリー要求があり、CPU制限と一致するCPU要求があることも確認できます。

spec:
  containers:
    ...
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
  ...
status:
  qosClass: Guaranteed

Podを削除してください:

kubectl delete pod qos-demo --namespace=qos-example

BurstableのQoSクラスを割り当てたPodを作成する

次のような場合に、Burstable QoSクラスがPodに与えられます:

  • PodがGuaranteed QoSクラスの基準に満たない場合。
  • Pod内の1つ以上のコンテナがメモリーまたはCPUの要求を与えられている場合。

以下に1つのコンテナをもつPodの設定ファイルを示します。コンテナには200MiBのメモリー制限と100MiBのメモリー要求を与えます。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-2.yaml --namespace=qos-example

Podの詳細な情報を確認してください:

kubectl get pod qos-demo-2 --namespace=qos-example --output=yaml

この出力では、KubernetesがPodにBurstable QoSクラスを与えたことを示しています。

spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: qos-demo-2-ctr
    resources:
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi
  ...
status:
  qosClass: Burstable

Podを削除してください:

kubectl delete pod qos-demo-2 --namespace=qos-example

BestEffortのQoSクラスを割り当てたPodを作成する

PodにBestEffort QoSクラスを与えるには、Pod内のコンテナにはメモリーやCPUの制限や要求を指定してはなりません。

以下に1つのコンテナをもつPodの設定ファイルを示します。コンテナにはメモリーやCPUの制限や要求がありません:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-3.yaml --namespace=qos-example

Podの詳細な情報を確認してください:

kubectl get pod qos-demo-3 --namespace=qos-example --output=yaml

この出力では、KubernetesがPodにBestEffort QoSクラスを与えたことを示しています。

spec:
  containers:
    ...
    resources: {}
  ...
status:
  qosClass: BestEffort

Podを削除してください:

kubectl delete pod qos-demo-3 --namespace=qos-example

2つのコンテナを含むPodを作成する

以下に2つのコンテナをもつPodの設定ファイルを示します。一方のコンテナは200MiBのメモリー要求を指定し、もう一方のコンテナには要求や制限を指定しません。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-4
  namespace: qos-example
spec:
  containers:

  - name: qos-demo-4-ctr-1
    image: nginx
    resources:
      requests:
        memory: "200Mi"

  - name: qos-demo-4-ctr-2
    image: redis

このPodがBurstable QoSクラスの基準を満たしていることに注目してください。つまり、Guaranteed QoSクラスの基準に満たしておらず、一方のコンテナにはメモリー要求を与えられています。

Podを作成してください:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-4.yaml --namespace=qos-example

Podの詳細な情報を確認してください:

kubectl get pod qos-demo-4 --namespace=qos-example --output=yaml

この出力では、KubernetesがPodにBurstable QoSクラスを与えたことを示しています:

spec:
  containers:
    ...
    name: qos-demo-4-ctr-1
    resources:
      requests:
        memory: 200Mi
    ...
    name: qos-demo-4-ctr-2
    resources: {}
    ...
status:
  qosClass: Burstable

Podを削除してください:

kubectl delete pod qos-demo-4 --namespace=qos-example

クリーンアップ

namespaceを削除してください:

kubectl delete namespace qos-example

次の項目

アプリケーション開発者向け

クラスター管理者向け

5 - 拡張リソースをコンテナに割り当てる

FEATURE STATE: Kubernetes v1.32 [stable]

このページでは、拡張リソースをコンテナに割り当てる方法について説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

この練習を始める前に、Nodeに拡張リソースをアドバタイズするの練習を行ってください。これにより、Nodeの1つがドングルリソースをアドバタイズするように設定されます。

拡張リソースをPodに割り当てる

拡張リソースをリクエストするには、コンテナのマニフェストにresources:requestsフィールドを含めます。拡張リソースは、*.kubernetes.io/以外の任意のドメインで完全修飾されます。有効な拡張リソース名は、example.com/fooという形式になります。ここで、example.comはあなたの組織のドメインで、fooは記述的なリソース名で置き換えます。

1つのコンテナからなるPodの構成ファイルを示します。

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo
spec:
  containers:
  - name: extended-resource-demo-ctr
    image: nginx
    resources:
      requests:
        example.com/dongle: 3
      limits:
        example.com/dongle: 3

構成ファイルでは、コンテナが3つのdongleをリクエストしていることがわかります。

次のコマンドでPodを作成します。

kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod.yaml

Podが起動したことを確認します。

kubectl get pod extended-resource-demo

Podの説明を表示します。

kubectl describe pod extended-resource-demo

dongleのリクエストが表示されます。

Limits:
  example.com/dongle: 3
Requests:
  example.com/dongle: 3

2つ目のPodの作成を試みる

以下に、1つのコンテナを持つPodの構成ファイルを示します。コンテナは2つのdongleをリクエストします。

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo-2
spec:
  containers:
  - name: extended-resource-demo-2-ctr
    image: nginx
    resources:
      requests:
        example.com/dongle: 2
      limits:
        example.com/dongle: 2

Kubernetesは、2つのdongleのリクエストを満たすことができません。1つ目のPodが、利用可能な4つのdongleのうち3つを使用してしまっているためです。

Podを作成してみます。

kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod-2.yaml

Podの説明を表示します。

kubectl describe pod extended-resource-demo-2

出力にはPodがスケジュールできないことが示されます。2つのdongleが利用できるNodeが存在しないためです。

Conditions:
  Type    Status
  PodScheduled  False
...
Events:
  ...
  ... Warning   FailedScheduling  pod (extended-resource-demo-2) failed to fit in any node
fit failure summary on nodes : Insufficient example.com/dongle (1)

Podのステータスを表示します。

kubectl get pod extended-resource-demo-2

出力には、Podは作成されたものの、Nodeにスケジュールされなかったことが示されています。PodはPending状態になっています。

NAME                       READY     STATUS    RESTARTS   AGE
extended-resource-demo-2   0/1       Pending   0          6m

クリーンアップ

この練習で作成したPodを削除します。

kubectl delete pod extended-resource-demo
kubectl delete pod extended-resource-demo-2

次の項目

アプリケーション開発者向け

クラスター管理者向け

6 - ストレージにボリュームを使用するPodを構成する

このページでは、ストレージにボリュームを使用するPodを構成する方法を示します。

コンテナのファイルシステムは、コンテナが存在する間のみ存続します。 そのため、コンテナが終了して再起動すると、ファイルシステムの変更は失われます。 コンテナに依存しない、より一貫したストレージを実現するには、ボリュームを使用できます。 これは、キーバリューストア(Redisなど)やデータベースなどのステートフルアプリケーションにとって特に重要です。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

Podのボリュームを構成する

この演習では、1つのコンテナを実行するPodを作成します。 今回作成するPodには、コンテナが終了して再起動した場合でもPodの寿命が続くemptyDirタイプのボリュームがあります。 これがPodの設定ファイルです:

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
  1. Podを作成します:

    kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml
    
  2. Podのコンテナが実行されていることを確認し、Podへの変更を監視します:

    kubectl get pod redis --watch
    

    出力は次のようになります:

    NAME      READY     STATUS    RESTARTS   AGE
    redis     1/1       Running   0          13s
    
  3. 別のターミナルで、実行中のコンテナへのシェルを取得します:

    kubectl exec -it redis -- /bin/bash
    
  4. シェルで、/data/redisに移動し、ファイルを作成します:

    root@redis:/data# cd /data/redis/
    root@redis:/data/redis# echo Hello > test-file
    
  5. シェルで、実行中のプロセスを一覧表示します:

    root@redis:/data/redis# apt-get update
    root@redis:/data/redis# apt-get install procps
    root@redis:/data/redis# ps aux
    

    出力はこのようになります:

    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    redis        1  0.1  0.1  33308  3828 ?        Ssl  00:46   0:00 redis-server *:6379
    root        12  0.0  0.0  20228  3020 ?        Ss   00:47   0:00 /bin/bash
    root        15  0.0  0.0  17500  2072 ?        R+   00:48   0:00 ps aux
    
  6. シェルで、Redisプロセスを終了します:

    root@redis:/data/redis# kill <pid>
    

    ここで<pid>はRedisプロセスID(PID)です。

  7. 元の端末で、Redis Podへの変更を監視します。最終的には、このようなものが表示されます:

    NAME      READY     STATUS     RESTARTS   AGE
    redis     1/1       Running    0          13s
    redis     0/1       Completed  0         6m
    redis     1/1       Running    1         6m
    

この時点で、コンテナは終了して再起動しました。 これは、Redis PodのrestartPolicyAlwaysであるためです。

  1. 再起動されたコンテナへのシェルを取得します:

    kubectl exec -it redis -- /bin/bash
    
  2. シェルで/data/redisに移動し、test-fileがまだ存在することを確認します。

    root@redis:/data/redis# cd /data/redis/
    root@redis:/data/redis# ls
    test-file
    
  3. この演習用に作成したPodを削除します:

    kubectl delete pod redis
    

次の項目

  • Volume参照

  • Pod参照

  • emptyDirによって提供されるローカルディスクストレージに加えて、Kubernetesは、GCEのPDやEC2のEBSなど、さまざまなネットワーク接続ストレージソリューションをサポートします。これらは、重要なデータに好ましく、ノード上のデバイスのマウントやアンマウントなどの詳細を処理します。詳細はボリュームを参照してください。

7 - ストレージにProjectedボリュームを使用するようPodを設定する

このページでは、projected(投影)ボリュームを使用して、既存の複数のボリュームソースを同一ディレクトリ内にマウントする方法を説明します。 現在、secretconfigMapdownwardAPIおよびserviceAccountTokenボリュームを投影できます。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

ProjectedボリュームをPodに設定する

この課題では、ローカルファイルからユーザーネームおよびパスワードのSecretを作成します。 次に、単一のコンテナを実行するPodを作成し、projectedボリュームを使用してそれぞれのSecretを同じ共有ディレクトリにマウントします。

以下にPodの設定ファイルを示します:

apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume
spec:
  containers:
  - name: test-projected-volume
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass
  1. Secretを作成します:

    # ユーザーネームおよびパスワードを含むファイルを作成します:
    echo -n "admin" > ./username.txt
    echo -n "1f2d1e2e67df" > ./password.txt
    
    # これらのファイルからSecretを作成します:
    kubectl create secret generic user --from-file=./username.txt
    kubectl create secret generic pass --from-file=./password.txt
    
  2. Podを作成します:

    kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
    
  3. Pod内のコンテナが実行されていることを確認するため、Podの変更を監視します:

    kubectl get --watch pod test-projected-volume
    

    出力は次のようになります:

    NAME                    READY     STATUS    RESTARTS   AGE
    test-projected-volume   1/1       Running   0          14s
    
  4. 別の端末にて、実行中のコンテナへのシェルを取得します:

    kubectl exec -it test-projected-volume -- /bin/sh
    
  5. シェル内にて、投影されたソースを含むprojected-volumeディレクトリが存在することを確認します:

    ls /projected-volume/
    

クリーンアップ

PodおよびSecretを削除します:

kubectl delete pod test-projected-volume
kubectl delete secret user pass

次の項目

8 - Podとコンテナにセキュリティコンテキストを設定する

セキュリティコンテキストはPod・コンテナの特権やアクセスコントロールの設定を定義します。 セキュリティコンテキストの設定には以下のものが含まれますが、これらに限定はされません。

  • 任意アクセス制御: user ID (UID) と group ID (GID)に基づいて、ファイルなどのオブジェクトに対する許可を行います。

  • Security Enhanced Linux (SELinux): オブジェクトにセキュリティラベルを付与します。

  • 特権または非特権として実行します。

  • Linux Capabilities: rootユーザーのすべての特権ではなく、一部の特権をプロセスに与えます。

  • AppArmor: プロファイルを用いて、個々のプログラムのcapabilityを制限します。

  • Seccomp: プロセスのシステムコールを限定します。

  • allowPrivilegeEscalation: あるプロセスが親プロセスよりも多くの特権を得ることができるかを制御します。 この真偽値は、コンテナプロセスに no_new_privs フラグが設定されるかどうかを直接制御します。 コンテナが以下の場合、allowPrivilegeEscalationは常にtrueになります。

    • コンテナが特権で動いている
    • CAP_SYS_ADMINを持っている
  • readOnlyRootFilesystem: コンテナのルートファイルシステムが読み取り専用でマウントされます。

上記の項目は全てのセキュリティコンテキスト設定を記載しているわけではありません。 より広範囲なリストはSecurityContextを確認してください。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

Podにセキュリティコンテキストを設定する

Podにセキュリティ設定を行うには、Podの設定にsecurityContextフィールドを追加してください。 securityContextフィールドはPodSecurityContextオブジェクトが入ります。 Podに設定したセキュリティ設定はPod内の全てのコンテナに適用されます。こちらはsecurityContextemptyDirボリュームを持ったPodの設定ファイルです。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    supplementalGroups: [4000]
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

設定ファイルの中のrunAsUserフィールドは、Pod内のコンテナに対して全てのプロセスをユーザーID 1000で実行するように指定します。 runAsGroupフィールドはPod内のコンテナに対して全てのプロセスをプライマリーグループID 3000で実行するように指定します。このフィールドが省略されたときは、コンテナのプライマリーグループIDはroot(0)になります。runAsGroupが指定されている場合、作成されたファイルもユーザー1000とグループ3000の所有物になります。 またfsGroupも指定されているため、全てのコンテナ内のプロセスは補助グループID 2000にも含まれます。/data/demoボリュームとこのボリュームに作成されたファイルはグループID 2000になります。加えて、supplementalGroupsフィールドが指定されている場合、全てのコンテナ内のプロセスは指定されている補助グループIDにも含まれます。もしこのフィールドが指定されていない場合、空を意味します。

Podを作成してみましょう。

kubectl apply -f https://k8s.io/examples/pods/security/security-context.yaml

Podのコンテナが実行されていることを確認します。

kubectl get pod security-context-demo

実行中のコンテナでshellを取ります。

kubectl exec -it security-context-demo -- sh

shellで、実行中のプロセスの一覧を確認します。

ps

runAsUserで指定した値である、ユーザー1000でプロセスが実行されていることが確認できます。

PID   USER     TIME  COMMAND
    1 1000      0:00 sleep 1h
    6 1000      0:00 sh
...

shellで/dataに入り、ディレクトリの一覧を確認します。

cd /data
ls -l

fsGroupで指定した値であるグループID 2000で/data/demoディレクトリが作成されていることが確認できます。

drwxrwsrwx 2 root 2000 4096 Jun  6 20:08 demo

shellで/data/demoに入り、ファイルを作成します。

cd demo
echo hello > testfile

/data/demoディレクトリでファイルの一覧を確認します。

ls -l

fsGroupで指定した値であるグループID 2000でtestfileが作成されていることが確認できます。

-rw-r--r-- 1 1000 2000 6 Jun  6 20:08 testfile

以下のコマンドを実行してください。

id

出力はこのようになります。

uid=1000 gid=3000 groups=2000,3000,4000

出力からrunAsGroupフィールドと同じくgidが3000になっていることが確認できるでしょう。runAsGroupが省略された場合、gidは0(root)になり、そのプロセスはグループroot(0)とグループroot(0)に必要なグループパーミッションを持つグループが所有しているファイルを操作することができるようになります。また、groupsの出力に、gidに加えて、fsGroupssupplementalGroupsフィールドで指定したグループIDも含まれていることも確認できるでしょう。

shellから抜けましょう。

exit

コンテナイメージ内の/etc/groupから暗黙的にマージされるグループ情報

Kubernetesは、デフォルトでは、Podで定義された情報に加えて、コンテナイメージ内の/etc/groupのグループ情報をマージします。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

このPodはsecurity contextでrunAsUserrunAsGroupsupplementalGroupsフィールドが指定されています。しかし、コンテナ内のプロセスには、コンテナイメージ内の/etc/groupに定義されたグループIDが、補助グループとして付与されていることが確認できるでしょう。

Podを作成してみましょう。

kubectl apply -f https://k8s.io/examples/pods/security/security-context-5.yaml

Podのコンテナが実行されていることを確認します。

kubectl get pod security-context-demo

実行中のコンテナでshellを取ります。

kubectl exec -it security-context-demo -- sh

プロセスのユーザー、グループ情報を確認します。

$ id

出力はこのようになります。

uid=1000 gid=3000 groups=3000,4000,50000

groupsにグループID50000が含まれていることが確認できるでしょう。これは、ユーザー(uid=1000)がコンテナイメージで定義されており、コンテナイメージ内の/etc/groupでグループ(gid=50000)に所属しているためです。

コンテナイメージの/etc/groupの内容を確認してみましょう。

$ cat /etc/group

ユーザー1000がグループ50000に所属していることが確認できるでしょう。

...
user-defined-in-image:x:1000:
group-defined-in-image:x:50000:user-defined-in-image

shellから抜けましょう。

exit

Podにfine-grained(きめ細かい) SupplementalGroups controlを設定する

FEATURE STATE: Kubernetes v1.31 [alpha] (enabled by default: false)

この機能はkubeletとkube-apiseverにSupplementalGroupsPolicy フィーチャーゲートを設定し、Podの.spec.securityContext.supplementalGroupsPolicyフィールドを指定することで利用できます。

supplementalGroupsPolicyフィールドは、Pod内のコンテナプロセスに付与される補助グループを、どのように決定するかを定義します。有効な値は次の2つです。

  • Merge: /etc/groupで定義されている、コンテナのプライマリユーザーが所属するグループをマージします。指定されていない場合、このポリシーがデフォルトです。

  • Strict: fsGroupsupplementalGroupsrunAsGroupフィールドで指定されたグループのみ補助グループに指定されます。つまり、/etc/groupで定義された、コンテナのプライマリユーザーのグループ情報はマージされません。

この機能が有効な場合、.status.containerStatuses[].user.linuxフィールドで、コンテナの最初のプロセスに付与されたユーザー、グループ情報が確認出来ます。暗黙的なグループIDが付与されているかどうかを確認するのに便利でしょう。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
    supplementalGroupsPolicy: Strict
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

このPodマニフェストはsupplementalGroupsPolicy=Strictを指定しています。/etc/groupに定義されているグループ情報が、コンテナ内のプロセスの補助グループにマージされないことが確認できるでしょう。

Podを作成してみましょう。

kubectl apply -f https://k8s.io/examples/pods/security/security-context-6.yaml

Podのコンテナが実行されていることを確認します。

kubectl get pod security-context-demo

プロセスのユーザー、グループ情報を確認します。

kubectl exec -it security-context-demo -- id

出力はこのようになります。

uid=1000 gid=3000 groups=3000,4000

Podのステータスを確認します。

kubectl get pod security-context-demo -o yaml

status.containerStatuses[].user.linuxフィールドでコンテナの最初のプロセスに付与されたユーザー、グループ情報が確認出来ます。

...
status:
  containerStatuses:
  - name: sec-ctx-demo
    user:
      linux:
        gid: 3000
        supplementalGroups:
        - 3000
        - 4000
        uid: 1000
...

利用可能な実装

下記のコンテナランタイムがfine-grained(きめ細かい) SupplementalGroups controlを実装しています。

CRI実装:

ノードのステータスでこの機能が利用可能かどうか確認出来ます。

apiVersion: v1
kind: Node
...
status:
  features:
    supplementalGroupsPolicy: true

Podのボリュームパーミッションと所有権変更ポリシーを設定する

FEATURE STATE: Kubernetes v1.23 [stable]

デフォルトでは、Kubernetesはボリュームがマウントされたときに、PodのsecurityContextで指定されたfsGroupに合わせて再帰的に各ボリュームの中の所有権とパーミッションを変更します。 大きなボリュームでは所有権の確認と変更に時間がかかり、Podの起動が遅くなります。 securityContextの中のfsGroupChangePolicyフィールドを設定することで、Kubernetesがボリュームの所有権・パーミッションの確認と変更をどう行うかを管理することができます。

fsGroupChangePolicy - fsGroupChangePolicyは、ボリュームがPod内部で公開される前に所有権とパーミッションを変更するための動作を定義します。 このフィールドはfsGroupで所有権とパーミッションを制御することができるボリュームタイプにのみ適用されます。このフィールドは以下の2つの値を取ります。

  • OnRootMismatch: ルートディレクトリのパーミッションと所有権がボリュームに設定したパーミッションと一致しない場合のみ、パーミッションと所有権を変更します。ボリュームの所有権とパーミッションを変更するのにかかる時間が短くなる可能性があります。
  • Always: ボリュームがマウントされたときに必ずパーミッションと所有権を変更します。

例:

securityContext:
  runAsUser: 1000
  runAsGroup: 3000
  fsGroup: 2000
  fsGroupChangePolicy: "OnRootMismatch"

CSIドライバーにボリュームパーミッションと所有権を移譲する

FEATURE STATE: Kubernetes v1.26 [stable]

VOLUME_MOUNT_GROUP NodeServiceCapabilityをサポートしているContainer Storage Interface (CSI)ドライバーをデプロイした場合、securityContextfsGroupで指定された値に基づいてKubernetesの代わりにCSIドライバーがファイルの所有権とパーミッションの設定処理を行います。 この場合Kubernetesは所有権とパーミッションの設定を行わないためfsGroupChangePolicyは無効となり、CSIで指定されている通りドライバーはfsGroupに従ってボリュームをマウントすると考えられるため、ボリュームはfsGroupに従って読み取り・書き込み可能になります。

コンテナにセキュリティコンテキストを設定する

コンテナに対してセキュリティ設定を行うには、コンテナマニフェストにsecurityContextフィールドを含めてください。securityContextフィールドにはSecurityContextオブジェクトが入ります。 コンテナに指定したセキュリティ設定は個々のコンテナに対してのみ反映され、Podレベルの設定を上書きします。コンテナの設定はPodのボリュームに対しては影響しません。

こちらは一つのコンテナを持つPodの設定ファイルです。PodもコンテナもsecurityContextフィールドを含んでいます。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: sec-ctx-demo-2
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      runAsUser: 2000
      allowPrivilegeEscalation: false

Podを作成します。

kubectl apply -f https://k8s.io/examples/pods/security/security-context-2.yaml

Podのコンテナが実行されていることを確認します。

kubectl get pod security-context-demo-2

実行中のコンテナでshellを取ります。

kubectl exec -it security-context-demo-2 -- sh

shellの中で、実行中のプロセスの一覧を表示します。

ps aux

ユーザー2000として実行されているプロセスが表示されました。これはコンテナのrunAsUserで指定された値です。Podで指定された値である1000を上書きしています。

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
2000         1  0.0  0.0   4336   764 ?        Ss   20:36   0:00 /bin/sh -c node server.js
2000         8  0.1  0.5 772124 22604 ?        Sl   20:36   0:00 node server.js
...

shellから抜けます。

exit

コンテナにケーパビリティを設定する

Linuxケーパビリティを用いると、プロセスに対してrootユーザーの全権を渡すことなく特定の権限を与えることができます。 コンテナに対してLinuxケーパビリティを追加したり削除したりするには、コンテナマニフェストのsecurityContextセクションのcapabilitiesフィールドに追加してください。

まず、capabilitiesフィールドを含まない場合どうなるかを見てみましょう。 こちらはコンテナに対してケーパビリティを渡していない設定ファイルです。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-3
spec:
  containers:
  - name: sec-ctx-3
    image: gcr.io/google-samples/node-hello:1.0

Podを作成します。

kubectl apply -f https://k8s.io/examples/pods/security/security-context-3.yaml

Podが実行されていることを確認します。

kubectl get pod security-context-demo-3

実行中のコンテナでshellを取ります。

kubectl exec -it security-context-demo-3 -- sh

shellの中で、実行中のプロセスの一覧を表示します。

ps aux

コンテナのプロセスID(PID)が出力されます。

USER  PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root    1  0.0  0.0   4336   796 ?     Ss   18:17   0:00 /bin/sh -c node server.js
root    5  0.1  0.5 772124 22700 ?     Sl   18:17   0:00 node server.js

shellの中で、プロセス1のステータスを確認します。

cd /proc/1
cat status

プロセスのケーパビリティビットマップが表示されます。

...
CapPrm:	00000000a80425fb
CapEff:	00000000a80425fb
...

ケーパビリティビットマップのメモを取った後、shellから抜けます。

exit

次に、追加のケーパビリティを除いて上と同じ設定のコンテナを実行します。

こちらは1つのコンテナを実行するPodの設定ファイルです。 CAP_NET_ADMINCAP_SYS_TIMEケーパビリティを設定に追加しました。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-4
spec:
  containers:
  - name: sec-ctx-4
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]

Podを作成します。

kubectl apply -f https://k8s.io/examples/pods/security/security-context-4.yaml

実行中のコンテナでshellを取ります。

kubectl exec -it security-context-demo-4 -- sh

shellの中で、プロセス1のケーパビリティを確認します。

cd /proc/1
cat status

プロセスのケーパビリティビットマップが表示されます。

...
CapPrm:	00000000aa0435fb
CapEff:	00000000aa0435fb
...

2つのコンテナのケーパビリティを比較します。

00000000a80425fb
00000000aa0435fb

1つ目のコンテナのケーパビリティビットマップでは、12, 25ビット目がクリアされています。2つ目のコンテナでは12, 25ビット目がセットされています。12ビット目はCAP_NET_ADMIN、25ビット目はCAP_SYS_TIMEです。 ケーパビリティの定数の定義はcapability.hを確認してください。

コンテナにSeccompプロフィールを設定する

コンテナにSeccompプロフィールを設定するには、Pod・コンテナマニフェストのsecurityContextseccompProfileフィールドを追加してください。 seccompProfileフィールドはSeccompProfileオブジェクトで、typelocalhostProfileで構成されています。 typeではRuntimeDefaultUnconfinedLocalhostが有効です。 localhostProfiletype: Localhostのときのみ指定可能です。こちらはノード上で事前に設定されたプロファイルのパスを示していて、kubeletのSeccompプロファイルの場所(--root-dirフラグで設定したもの)からの相対パスです。

こちらはノードのコンテナランタイムのデフォルトプロフィールをSeccompプロフィールとして設定した例です。

...
securityContext:
  seccompProfile:
    type: RuntimeDefault

こちらは<kubelet-root-dir>/seccomp/my-profiles/profile-allow.jsonで事前に設定したファイルをSeccompプロフィールに設定した例です。

...
securityContext:
  seccompProfile:
    type: Localhost
    localhostProfile: my-profiles/profile-allow.json

コンテナにAppArmorプロフィールを設定する

コンテナにAppArmorプロフィールを設定するには、securityContextセクションにappAormorProfileフィールドを含めてください。 appAormerProfileフィールドには、typeフィールドとlocalhostProfileフィールドから構成されるAppArmorProfileオブジェクトが入ります。typeフィールドの有効なオプションはRuntimeDefault(デフォルト)、UnconfinedLocalhostです。 localhostProfiletypeLocalhostのときには必ず設定しなければなりません。この値はノードで事前に設定されたプロフィール名を示します。Podは事前にどのノードにスケジュールされるかわからないため、指定されたプロフィールはPodがスケジュールされ得る全てのノードにロードされている必要があります。カスタムプロフィールをセットアップする方法はSetting up nodes with profilesを参照してください。

注意: containers[*].securityContext.appArmorProfile.typeが明示的にRuntimeDefaultに設定されている場合は、もしノードでAppArmorが有効化されていなければ、Podの作成は許可されません。 しかし、containers[*].securityContext.appArmorProfile.typeが設定されていない場合、AppArmorが有効化されていれば、デフォルト(RuntimeDefault)が適用されます。もし、AppArmorが無効化されている場合は、Podの作成は許可されますが、コンテナにはRuntimeDefaultプロフィールの制限は適用されません。

これは、AppArmorプロフィールとして、ノードのコンテナランタイムのデフォルトプロフィールを設定する例です。

...
containers:
- name: container-1
  securityContext:
    appArmorProfile:
      type: RuntimeDefault

これは、AppArmorプロフィールとして、k8s-apparmor-example-deny-writeという名前で事前に設定されたプロフィールを設定する例です。

...
containers:
- name: container-1
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: k8s-apparmor-example-deny-write

より詳細な内容についてはRestrict a Container's Access to Resources with AppArmorを参照してください。

コンテナにSELinuxラベルをつける

コンテナにSELinuxラベルをつけるには、Pod・コンテナマニフェストのsecurityContextセクションにseLinuxOptionsフィールドを追加してください。 seLinuxOptionsフィールドはSELinuxOptionsオブジェクトが入ります。 こちらはSELinuxレベルを適用する例です。

...
securityContext:
  seLinuxOptions:
    level: "s0:c123,c456"

効率的なSELinuxのボリューム再ラベル付け

FEATURE STATE: Kubernetes v1.28 [beta] (enabled by default: true)

デフォルトでは、コンテナランタイムは全てのPodのボリュームの全てのファイルに再帰的にSELinuxラベルを付与します。処理速度を上げるために、Kubernetesはマウントオプションで-o context=<label>を使うことでボリュームのSELinuxラベルを即座に変更することができます。

この高速化の恩恵を受けるには、以下の全ての条件を満たす必要があります。

  • AlphaフィーチャーゲートのReadWriteOncePodSELinuxMountReadWriteOncePodを有効にすること
  • Podが適用可能なaccessModesでPersistentVolumeClaimを使うこと
    • ボリュームがaccessModes: ["ReadWriteOncePod"]を持ち、フィーチャーゲートSELinuxMountReadWriteOncePodが有効であること
    • または、ボリュームが他のアクセスモードを使用し、フィーチャーゲートSELinuxMountReadWriteOncePodSELinuxMountの両方が有効であること
  • Pod(またはPersistentVolumeClaimを使っている全てのコンテナ)にseLinuxOptionsが設定されていること
  • 対応するPersistentVolumeが以下のいずれかであること
    • レガシーのin-treeボリュームの場合、iscsrbdfcボリュームタイプであること
    • または、CSIドライバーを使用するボリュームで、そのCSIドライバーがCSIドライバーインスタンスでspec.seLinuxMount: trueを指定したときに-o contextでマウントを行うとアナウンスしていること

それ以外のボリュームタイプでは、コンテナランタイムはボリュームに含まれる全てのinode(ファイルやディレクトリ)に対してSELinuxラベルを再帰的に変更します。 ボリューム内のファイルやディレクトリが増えるほど、ラベリングにかかる時間は増加します。

/procファイルシステムへのアクセスを管理する

FEATURE STATE: Kubernetes v1.31 [beta] (enabled by default: false)

OCI runtime specificationに準拠するランタイムでは、コンテナはデフォルトで、いくつかの複数のパスはマスクされ、かつ、読み取り専用のモードで実行されます。 その結果、コンテナのマウントネームスペース内にはこれらのパスが存在し、あたかもコンテナが隔離されたホストであるかのように機能しますが、コンテナプロセスはそれらのパスに書き込むことはできません。 マスクされるパスおよび読み取り専用のパスのリストは次のとおりです。

  • マスクされるパス:

    • /proc/asound
    • /proc/acpi
    • /proc/kcore
    • /proc/keys
    • /proc/latency_stats
    • /proc/timer_list
    • /proc/timer_stats
    • /proc/sched_debug
    • /proc/scsi
    • /sys/firmware
    • /sys/devices/virtual/powercap
  • 読み取り専用のパス:

    • /proc/bus
    • /proc/fs
    • /proc/irq
    • /proc/sys
    • /proc/sysrq-trigger

一部のPodでは、デフォルトでパスがマスクされるのを回避したい場合があります。このようなケースで最も一般的なのは、Kubernetesコンテナ(Pod内のコンテナ)内でコンテナを実行しようとする場合です。

securityContextprocMountフィールドを使用すると、コンテナの/procUnmaskedにしたり、コンテナプロセスによって読み書き可能な状態でマウントすることができます。この設定は、/proc以外の/sys/firmwareにも適用されます。

...
securityContext:
  procMount: Unmasked

議論

PodのセキュリティコンテキストはPodのコンテナや、適用可能であればPodのボリュームに対しても適用されます。 特にfsGroupseLinuxOptionsは以下のようにボリュームに対して適用されます。

  • fsGroup: 所有権管理をサポートしているボリュームはfsGroupで指定されているGIDで所有権・書き込み権限が設定されます。詳しくはOwnership Management design documentを確認してください。

  • seLinuxOptions: SELinuxラベリングをサポートしているボリュームではseLinuxOptionsで指定されているラベルでアクセス可能になるように貼り直されます。通常、levelセクションのみ設定する必要があります。 これはPod内の全てのボリュームとコンテナに対しMulti-Category Security (MCS)ラベルを設定します。

クリーンアップ

Podを削除します。

kubectl delete pod security-context-demo
kubectl delete pod security-context-demo-2
kubectl delete pod security-context-demo-3
kubectl delete pod security-context-demo-4

次の項目

9 - イメージをプライベートレジストリから取得する

このページでは、Secretを用いるPodを作成するためにイメージをプライベートコンテナイメージレジストリもしくはリポジトリから取得する方法について説明します。 多くのプライベートレジストリが存在しますが、このタスクではDocker Hubをレジストリの一例として使用します。

始める前に

  • Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

  • この演習を行うためには、dockerコマンドラインツールと自身のDocker IDが必要です。

  • 別のプライベートコンテナレジストリを使用している場合は、そのレジストリのコマンドラインツールとログイン情報が必要です。

Docker Hubにログインする

ラップトップ(ローカル環境)上では、プライベートイメージを取得するためにレジストリに認証する必要があります。

dockerツールを使用してDocker Hubにログインしてください。詳細はDocker IDアカウントSign in セクションを参照してください。

docker login

プロンプトが表示されたら、Docker IDと使用したい認証情報(アクセストークンまたはDocker IDのパスワード)を入力してください。

ログインプロセスは、認証トークンを保持するconfig.jsonファイルを作成または更新します。Kubernetesがこのファイルを解釈する方法を確認してください。

以下のようにconfig.jsonファイルを確認します:

cat ~/.docker/config.json

出力には以下のような部分が含まれています:

{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "c3R...zE2"
        }
    }
}

既存の認証情報を用いるSecretを作成する

Kubernetesクラスターはプライベートイメージを取得するためのレジストリとの認証にkubernetes.io/dockerconfigjsonタイプのSecretを使用します。

既にdocker loginを実行済みの場合、その認証情報をKubernetesにコピーできます:

kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=<path/to/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson

新しいSecretをカスタマイズする必要がある場合(例えば、新しいSecretにNamespaceやLabelを設定する場合)は、保存する前にSecretをカスタマイズできます。 必ず以下を行ってください:

  • data項目の名前を.dockerconfigjsonに設定する
  • Dockerの設定ファイルをbase64エンコードし、その文字列を分割せずにdata[".dockerconfigjson"]の値として貼り付ける
  • typekubernetes.io/dockerconfigjsonに設定する

Secretをカスタマイズする場合のマニフェストの例:

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
  namespace: awesomeapps
data:
  .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson

error: no objects passed to createをエラーメッセージとして受け取った場合、base64エンコードされた文字列が無効である可能性があります。 Secret "myregistrykey" is invalid: data[.dockerconfigjson]: invalid value ...をエラーメッセージとして受け取った場合は、data内のbase64エンコードされた文字列は正常にデコードされたものの、.docker/config.jsonファイルとして読み込むことができなかったことを意味します。

コマンドライン上で認証情報を入力してSecretを作成する

このSecretをregcredという名前で作成します:

kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

ここでは:

  • <your-registry-server>は、プライベートDockerレジストリのFQDN(完全修飾ドメイン名)です。 Docker Hubを使用する場合は、https://index.docker.io/v1/を設定して下さい。
  • <your-name>は、Dockerのユーザー名です。
  • <your-pword>は、Dockerのパスワードです。
  • <your-email>は、Dockerのメールアドレスです。

Dockerレジストリの認証情報がSecretとしてregcredという名前でクラスターに正常に設定されました。

regcredSecretを確認する

作成したregcredSecretの内容を理解するために、まずYAML形式でSecretを表示します:

kubectl get secret regcred --output=yaml

出力は以下のようになります:

apiVersion: v1
kind: Secret
metadata:
  ...
  name: regcred
  ...
data:
  .dockerconfigjson: eyJodHRwczovL2luZGV4L ... J0QUl6RTIifX0=
type: kubernetes.io/dockerconfigjson

.dockerconfigjsonフィールドの値は、Docker認証情報をbase64でエンコードしたものです。

.dockerconfigjsonフィールドの内容を理解するために、認証情報のデータを読解可能な形式に変換します:

kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode

出力は以下のようになります:

{"auths":{"your.private.registry.example.com":{"username":"janedoe","password":"xxxxxxxxxxx","email":"jdoe@example.com","auth":"c3R...zE2"}}}

authフィールドの内容を理解するために、base64エンコードされたデータを読解可能な形式に変換します:

echo "c3R...zE2" | base64 --decode

出力はユーザー名とパスワードが:で連結された以下のような形式になります:

janedoe:xxxxxxxxxxx

Secretのdataには、ローカルの~/.docker/config.jsonファイルと同様の認証トークンが含まれていることが分かります。

クラスターにregcredという名前のSecretとしてDockerの認証情報が正常に設定されました。

作成したSecretを利用するPodを作成する

以下はregcredという名前のDockerの認証情報へのアクセスが必要なPodのマニフェストの例です:

apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: <your-private-image>
  imagePullSecrets:
  - name: regcred

上記のファイルをあなたのコンピューターにダウンロードします:

curl -L -o my-private-reg-pod.yaml https://k8s.io/examples/pods/private-reg-pod.yaml

my-private-reg-pod.yamlファイル内の<your-private-image>を、以下のようなプライベートレジストリのイメージのパスに置き換えてください:

your.private.registry.example.com/janedoe/jdoe-private:v1

プライベートレジストリからイメージを取得するために、Kubernetesは認証情報を必要とします。 設定ファイル内のimagePullSecretsフィールドは、Kubernetesがregcredという名前のSecretから認証情報を取得することを指定します。

以下のようにして作成したSecretを使用するPodを作成し、Podが実行されていることを確認します:

kubectl apply -f my-private-reg-pod.yaml
kubectl get pod private-reg

また、PodのステータスがImagePullBackOffになり起動に失敗した場合は、Podのイベントを確認してください:

kubectl describe pod private-reg

もしFailedToRetrieveImagePullSecretという理由を持つイベントが表示された場合は、Kubernetesが指定された名前のSecret(この例ではregcred)を見つけることができないことを示します。

指定したSecretが存在し、その名前が正しく記載されていることを確認してください。

Events:
  ...  Reason                           ...  Message
       ------                                -------
  ...  FailedToRetrieveImagePullSecret  ...  Unable to retrieve some image pull secrets (<regcred>); attempting to pull the image may not succeed.

複数のレジストリ上のイメージを利用する

1つのPodは複数のコンテナを持つことができ、各コンテナのイメージは異なるレジストリから取得できます。 1つのPodで複数のimagePullSecretsを使用でき、それぞれに複数の認証情報を含めることができます。

レジストリに一致する各認証情報を使用してイメージの取得が試行されます。 レジストリに一致する認証情報がない場合、認証なしで、またはカスタムランタイム固有の設定を使用してイメージの取得が試行されます。

次の項目

10 - Liveness Probe、Readiness ProbeおよびStartup Probeを使用する

このページでは、Liveness Probe、Readiness ProbeおよびStartup Probeの使用方法について説明します。

kubeletは、Liveness Probeを使用して、コンテナをいつ再起動するかを認識します。 例えば、アプリケーション自体は起動しているが、処理を継続することができないデッドロック状態を検知することができます。 このような状態のコンテナを再起動することで、バグがある場合でもアプリケーションの可用性を高めることができます。

kubeletは、Readiness Probeを使用して、コンテナがトラフィックを受け入れられる状態であるかを認識します。 Podが準備ができていると見なされるのは、Pod内の全てのコンテナの準備が整ったときです。 一例として、このシグナルはServiceのバックエンドとして使用されるPodを制御するときに使用されます。 Podの準備ができていない場合、そのPodはServiceのロードバランシングから切り離されます。

kubeletは、Startup Probeを使用して、コンテナアプリケーションの起動が完了したかを認識します。 Startup Probeを使用している場合、Startup Probeが成功するまでは、Liveness Probeと Readiness Probeによるチェックを無効にし、これらがアプリケーションの起動に干渉しないようにします。 例えば、これを起動が遅いコンテナの起動チェックとして使用することで、起動する前にkubeletによって 強制終了されることを防ぐことができます。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

コマンド実行によるLiveness Probeを定義する

長期間実行されているアプリケーションの多くは、再起動されるまで回復できないような異常な状態になることがあります。 Kubernetesはこのような状況を検知し、回復するためのLiveness Probeを提供します。

この演習では、registry.k8s.io/busyboxイメージのコンテナを起動するPodを作成します。 Podの構成ファイルは次の通りです。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

この構成ファイルでは、Podは一つのContainerを起動します。 periodSecondsフィールドは、kubeletがLiveness Probeを5秒おきに行うように指定しています。 initialDelaySecondsフィールドは、kubeletが最初のProbeを実行する前に5秒間待機するように指示しています。 Probeの動作としては、kubeletはcat /tmp/healthyを対象のコンテナ内で実行します。 このコマンドが成功し、リターンコード0が返ると、kubeletはコンテナが問題なく動いていると判断します。 リターンコードとして0以外の値が返ると、kubeletはコンテナを終了し、再起動を行います。

このコンテナは、起動すると次のコマンドを実行します:

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600"

コンテナが起動してから初めの30秒間は/tmp/healthyファイルがコンテナ内に存在します。 そのため初めの30秒間はcat /tmp/healthyコマンドは成功し、正常なリターンコードが返ります。 その後30秒が経過すると、cat /tmp/healthyコマンドは異常なリターンコードを返します。

このPodを起動してください:

kubectl apply -f https://k8s.io/examples/pods/probe/exec-liveness.yaml

30秒間以内に、Podのイベントを確認します。

kubectl describe pod liveness-exec

この出力結果は、Liveness Probeがまだ失敗していないことを示しています。

FirstSeen    LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
24s       24s     1   {default-scheduler }                    Normal      Scheduled   Successfully assigned liveness-exec to worker0
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulling     pulling image "registry.k8s.io/busybox"
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulled      Successfully pulled image "registry.k8s.io/busybox"
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Created     Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Started     Started container with docker id 86849c15382e

35秒後に、Podのイベントをもう一度確認します:

kubectl describe pod liveness-exec

出力結果の最後に、Liveness Probeが失敗していることを示すメッセージが表示されます。これによりコンテナは強制終了し、再作成されました。

FirstSeen LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
37s       37s     1   {default-scheduler }                    Normal      Scheduled   Successfully assigned liveness-exec to worker0
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulling     pulling image "registry.k8s.io/busybox"
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulled      Successfully pulled image "registry.k8s.io/busybox"
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Created     Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Started     Started container with docker id 86849c15382e
2s        2s      1   {kubelet worker0}   spec.containers{liveness}   Warning     Unhealthy   Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

さらに30秒後、コンテナが再起動していることを確認します:

kubectl get pod liveness-exec

出力結果から、RESTARTSがインクリメントされていることを確認します:

NAME            READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m

HTTPリクエストによるLiveness Probeを定義する

別の種類のLiveness Probeでは、HTTP GETリクエストを使用します。 次の構成ファイルは、registry.k8s.io/e2e-test-images/agnhostイメージを使用したコンテナを起動するPodを作成します。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    args:
    - liveness
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

この構成ファイルでは、Podは一つのContainerを起動します。 periodSecondsフィールドは、kubeletがLiveness Probeを3秒おきに行うように指定しています。 initialDelaySecondsフィールドは、kubeletが最初のProbeを実行する前に3秒間待機するように指示しています。 Probeの動作としては、kubeletは8080ポートをリッスンしているコンテナ内のサーバーに対してHTTP GETリクエストを送ります。 サーバー内の/healthzパスに対するハンドラーが正常なリターンコードを応答した場合、 kubeletはコンテナが問題なく動いていると判断します。 異常なリターンコードを応答すると、kubeletはコンテナを終了し、再起動を行います。

200以上400未満のコードは成功とみなされ、その他のコードは失敗とみなされます。

server.go にてサーバーのソースコードを確認することができます。

コンテナが生きている初めの10秒間は、/healthzハンドラーが200ステータスを返します。 その後、ハンドラーは500ステータスを返します。

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

kubeletは、コンテナが起動してから3秒後からヘルスチェックを行います。 そのため、初めのいくつかのヘルスチェックは成功します。しかし、10秒経過するとヘルスチェックは失敗し、kubeletはコンテナを終了し、再起動します。

HTTPリクエストのチェックによるLiveness Probeを試すには、以下のようにPodを作成します:

kubectl apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml

10秒後、Podのイベントを表示して、Liveness Probeが失敗し、コンテナが再起動されていることを確認します。

kubectl describe pod liveness-http

v1.13以前(v1.13を含む)のリリースにおいては、Podが起動しているノードに環境変数http_proxy (または HTTP_PROXY)が設定されている場合、HTTPリクエストのLiveness Probeは設定されたプロキシを使用します。 v1.13より後のリリースにおいては、ローカルHTTPプロキシ環境変数の設定はHTTPリクエストのLiveness Probeに影響しません。

TCPによるLiveness Probeを定義する

3つ目のLiveness ProbeはTCPソケットを使用するタイプです。 この構成においては、kubeletは指定したコンテナのソケットを開くことを試みます。 コネクションが確立できる場合はコンテナを正常とみなし、失敗する場合は異常とみなします。

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

見ての通り、TCPによるチェックの構成はHTTPによるチェックと非常に似ています。 この例では、Readiness ProbeとLiveness Probeを両方使用しています。 kubeletは、コンテナが起動してから5秒後に最初のReadiness Probeを開始します。 これはgoproxyコンテナの8080ポートに対して接続を試みます。 このProbeが成功すると、Podは準備ができていると通知されます。kubeletはこのチェックを10秒ごとに行います。

この構成では、Readiness Probeに加えてLiveness Probeが含まれています。 kubeletは、コンテナが起動してから15秒後に最初のLiveness Probeを実行します。 Readiness Probeと同様に、これはgoproxyコンテナの8080ポートに対して接続を試みます。 Liveness Probeが失敗した場合、コンテナは再起動されます。

TCPのチェックによるLiveness Probeを試すには、以下のようにPodを作成します:

kubectl apply -f https://k8s.io/examples/pods/probe/tcp-liveness-readiness.yaml

15秒後、Podのイベントを表示し、Liveness Probeが行われていることを確認します:

kubectl describe pod goproxy

名前付きポートを使用する

HTTPまたはTCPによるProbeにおいて、ContainerPort で定義した名前付きポートを使用することができます。

ports:
- name: liveness-port
  containerPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port

Startup Probeを使用して、起動の遅いコンテナを保護する

場合によっては、最初の初期化において追加の起動時間が必要になるようなレガシーアプリケーションを扱う必要があります。 そのような場合、デッドロックに対する迅速な反応を損なうことなくLiveness Probeのパラメーターを設定することは難しい場合があります。

これに対する解決策の一つは、Liveness Probeと同じ構成のコマンドを用いるか、HTTPまたはTCPによるチェックを使用したStartup Probeをセットアップすることです。 その際、failureThreshold * periodSecondsで計算される時間を、起動時間として想定される最も遅いケースをカバーできる十分な長さに設定します。

上記の例は、次のようになります:

ports:
- name: liveness-port
  containerPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

Startup Probeにより、アプリケーションは起動が完了するまでに最大5分間の猶予(30 * 10 = 300秒)が与えられます。 Startup Probeに一度成功すると、その後はLiveness Probeが引き継ぎ、コンテナのデッドロックに対して迅速に反応します。 Startup Probeが成功しない場合、コンテナは300秒後に終了し、その後はPodのrestartPolicyに従います。

Readiness Probeを定義する

アプリケーションは一時的にトラフィックを処理できないことが起こり得ます。 例えば、アプリケーションは起動時に大きなデータまたは構成ファイルを読み込む必要がある場合や、起動後に外部サービスに依存している場合があります。 このような場合、アプリケーション自体を終了させたくはありませんが、このアプリケーションに対してリクエストも送信したくないと思います。 Kubernetesは、これらの状況を検知して緩和するための機能としてReadiness Probeを提供します。 これにより、準備ができていないことを報告するコンテナを含むPodは、KubernetesのServiceを通してトラフィックを受信しないようになります。

Readiness ProbeはLiveness Probeと同様に構成します。 唯一の違いはreadinessProbeフィールドをlivenessProbeフィールドの代わりに利用することだけです。

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

HTTPおよびTCPによるReadiness Probeの構成もLiveness Probeと同じです。

Readiness ProbeとLiveness Probeは同じコンテナで同時に使用できます。 両方使用することで、準備できていないコンテナへのトラフィックが到達しないようにし、コンテナが失敗したときに再起動することができます。

Probeの構成

Probe には、 Liveness ProbeおよびReadiness Probeのチェック動作をより正確に制御するために使用できるフィールドがあります:

  • initialDelaySeconds: コンテナが起動してから、Liveness ProbeまたはReadiness Probeが開始されるまでの秒数。デフォルトは0秒。最小値は0。
  • periodSeconds: Probeが実行される頻度(秒数)。デフォルトは10秒。最小値は1。 コンテナが起動してから準備が整うまでの間、periodSecondsで指定した間隔とは異なるタイミングでReadiness Probeが実行される場合があります。 これは、Podをより早く準備完了の状態に移行させるためです。
  • timeoutSeconds: Probeがタイムアウトになるまでの秒数。デフォルトは1秒。最小値は1。
  • successThreshold: 一度Probeが失敗した後、次のProbeが成功したとみなされるための最小連続成功数。 デフォルトは1。Liveness ProbeおよびStartup Probeには1を設定する必要があります。最小値は1。
  • failureThreshold: Probeが失敗した場合、KubernetesはfailureThresholdに設定した回数までProbeを試行します。 Liveness Probeにおいて、試行回数に到達することはコンテナを再起動することを意味します。 Readiness Probeの場合は、Podが準備できていない状態として通知されます。デフォルトは3。最小値は1。

HTTPによるProbe には、httpGetにて設定できる追加のフィールドがあります:

  • host: 接続先ホスト名。デフォルトはPod IP。おそらくはこのフィールドの代わりにhttpHeaders内の"Host"を代わりに使用することになります。
  • scheme: ホストへの接続で使用するスキーマ(HTTP または HTTPS)。デフォルトは HTTP。
  • path: HTTPサーバーへアクセスする際のパス
  • httpHeaders: リクエスト内のカスタムヘッダー。HTTPでは重複したヘッダーが許可されています。
  • port: コンテナにアクセスする際のポートの名前または番号。ポート番号の場合、1から65535の範囲内である必要があります。

HTTPによるProbeの場合、kubeletは指定したパスとポートに対するHTTPリクエストを送ることでチェックを行います。 httpGetのオプションであるhostフィールドでアドレスが上書きされない限り、kubeletはPodのIPアドレスに対してProbeを送ります。 schemeフィールドにHTTPSがセットされている場合、kubeletは証明書の検証を行わずにHTTPSリクエストを送ります。 ほとんどのシナリオにおいては、hostフィールドを使用する必要はありません。次のシナリオは使用する場合の一例です。 仮にコンテナが127.0.0.1をリッスンしており、かつPodのhostNetworkフィールドがtrueだとします。 その場合においては、httpGetフィールド内のhostには127.0.0.1をセットする必要があります。 より一般的なケースにおいてPodが仮想ホストに依存している場合は、おそらくhostフィールドではなく、httpHeadersフィールド内のHostヘッダーを使用する必要があります。

TCPによるProbeの場合、kubeletはPodの中ではなく、ノードに対してコネクションを確立するProbeを実行します。 kubeletはServiceの名前を解決できないため、hostパラメーター内でServiceの名前を使用することはできません。

次の項目

また、次のAPIリファレンスも参考にしてください:

11 - Podをノードに割り当てる

このページでは、KubernetesのPodをKubernetesクラスター上の特定のノードに割り当てる方法を説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

ラベルをノードに追加する

  1. クラスター内のノードのリストをラベル付きで表示します。

    kubectl get nodes --show-labels
    

    出力は次のようになります。

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  2. ノードの1つを選択して、ラベルを追加します。

    kubectl label nodes <your-node-name> disktype=ssd
    

    ここで、<your-node-name>は選択したノードの名前です。

  3. 選択したノードにdisktype=ssdラベルがあることを確認します。

    kubectl get nodes --show-labels
    

    出力は次のようになります。

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    上の出力を見ると、worker0disktype=ssdというラベルがあることがわかります。

選択したノードにスケジューリングされるPodを作成する

以下のPodの構成ファイルには、nodeSelectorにdisktype: ssdを持つPodが書かれています。これにより、Podはdisktype: ssdというラベルを持っているノードにスケジューリングされるようになります。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
  1. 構成ファイルを使用して、選択したノードにスケジューリングされるPodを作成します。

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml
    
  2. Podが選択したノード上で実行されているをことを確認します。

    kubectl get pods --output=wide
    

    出力は次のようになります。

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

特定のノードにスケジューリングされるPodを作成する

nodeNameという設定を使用して、Podを特定のノードにスケジューリングすることもできます。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeName: foo-node # 特定のノードにPodをスケジューリングする
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

構成ファイルを使用して、foo-nodeにだけスケジューリングされるPodを作成します。

次の項目

12 - Node Affinityを利用してPodをノードに割り当てる

このページでは、Node Affinityを利用して、PodをKubernetesクラスター内の特定のノードに割り当てる方法を説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.10. バージョンを確認するには次のコマンドを実行してください: kubectl version.

ノードにラベルを追加する

  1. クラスター内のノードを一覧表示して、ラベルを確認します。

    kubectl get nodes --show-labels
    

    出力は次のようになります。

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  2. ノードを選択して、ラベルを追加します。

    kubectl label nodes <your-node-name> disktype=ssd
    

    ここで、<your-node-name>は選択したノードの名前で置換します。

  3. 選択したノードにdisktype=ssdラベルがあることを確認します。

    kubectl get nodes --show-labels
    

    出力は次のようになります。

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    この出力を見ると、worker0ノードにdisktype=ssdというラベルが追加されたことがわかります。

required node affinityを使用してPodをスケジューリングする

以下に示すマニフェストには、requiredDuringSchedulingIgnoredDuringExecutiondisktype: ssdというnode affinityを使用したPodが書かれています。このように書くと、Podはdisktype=ssdというラベルを持つノードにだけスケジューリングされるようになります。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. マニフェストを適用して、選択したノード上にスケジューリングされるPodを作成します。

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-required-affinity.yaml
    
  2. Podが選択したノード上で実行されていることを確認します。

    kubectl get pods --output=wide
    

    出力は次のようになります。

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

preferred node affinityを使用してPodをスケジューリングする

以下に示すマニフェストには、preferredDuringSchedulingIgnoredDuringExecutiondisktype: ssdというnode affinityを使用したPodが書かれています。このように書くと、Podはdisktype=ssdというラベルを持つノードに優先的にスケジューリングされるようになります。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. マニフェストを適用して、選択したノード上にスケジューリングされるPodを作成します。

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-preferred-affinity.yaml
    
  2. Podが選択したノード上で実行されていることを確認します。

    kubectl get pods --output=wide
    

    出力は次のようになります。

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

次の項目

Node Affinityについてさらに学ぶ。

13 - Pod初期化の設定

このページでは、アプリケーションコンテナが実行される前に、Initコンテナを使用してPodを初期化する方法を示します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

Initコンテナを持つPodを作成する

この演習では、アプリケーションコンテナが1つ、Initコンテナが1つのPodを作成します。 Initコンテナはアプリケーションコンテナが実行される前に完了します。

Podの設定ファイルは次の通りです:

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  # These containers are run during pod initialization
  initContainers:
  - name: install
    image: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://kubernetes.io
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}

設定ファイルを確認すると、PodはInitコンテナとアプリケーションコンテナが共有するボリュームを持っています。

Initコンテナは共有ボリュームを/work-dirにマウントし、アプリケーションコンテナは共有ボリュームを/usr/share/nginx/htmlにマウントします。 Initコンテナは以下のコマンドを実行してから終了します:

wget -O /work-dir/index.html http://info.cern.ch

Initコンテナは、nginxサーバーのルートディレクトリのindex.htmlファイルに書き込むことに注意してください。

Podを作成します:

kubectl apply -f https://k8s.io/examples/pods/init-containers.yaml

nginxコンテナが実行中であることを確認します:

kubectl get pod init-demo

次の出力はnginxコンテナが実行中であることを示します:

NAME        READY     STATUS    RESTARTS   AGE
init-demo   1/1       Running   0          1m

init-demo Podで実行中のnginxコンテナのシェルを取得します:

kubectl exec -it init-demo -- /bin/bash

シェルで、nginxサーバーにGETリクエストを送信します:

root@nginx:~# apt-get update
root@nginx:~# apt-get install curl
root@nginx:~# curl localhost

出力は、Initコンテナが書き込んだウェブページをnginxが提供していることを示します:

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
  ...
  <li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
  ...

次の項目

14 - コンテナライフサイクルイベントへのハンドラー紐付け

このページでは、コンテナのライフサイクルイベントにハンドラーを紐付けする方法を説明します。KubernetesはpostStartとpreStopイベントをサポートしています。Kubernetesはコンテナの起動直後にpostStartイベントを送信し、コンテナの終了直前にpreStopイベントを送信します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

postStartハンドラーとpreStopハンドラーを定義する

この課題では、1つのコンテナを持つPodを作成します。コンテナには、postStartイベントとpreStopイベントのハンドラーがあります。

これがPodの設定ファイルです:

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]

設定ファイルでは、postStartコマンドがmessageファイルをコンテナの/usr/shareディレクトリに書き込むことがわかります。preStopコマンドはnginxを適切にシャットダウンします。これは、障害のためにコンテナが終了している場合に役立ちます。

Podを作成します:

kubectl apply -f https://k8s.io/examples/pods/lifecycle-events.yaml

Pod内のコンテナが実行されていることを確認します:

kubectl get pod lifecycle-demo

Pod内で実行されているコンテナでシェルを実行します:

kubectl exec -it lifecycle-demo -- /bin/bash

シェルで、postStartハンドラーがmessageファイルを作成したことを確認します:

root@lifecycle-demo:/# cat /usr/share/message

出力は、postStartハンドラーによって書き込まれたテキストを示しています。

Hello from the postStart handler

議論

コンテナが作成された直後にKubernetesはpostStartイベントを送信します。 ただし、コンテナのエントリーポイントが呼び出される前にpostStartハンドラーが呼び出されるという保証はありません。postStartハンドラーはコンテナのコードに対して非同期的に実行されますが、postStartハンドラーが完了するまでコンテナのKubernetesによる管理はブロックされます。postStartハンドラーが完了するまで、コンテナのステータスはRUNNINGに設定されません。

Kubernetesはコンテナが終了する直前にpreStopイベントを送信します。 コンテナのKubernetesによる管理は、Podの猶予期間が終了しない限り、preStopハンドラーが完了するまでブロックされます。詳細はPodのライフサイクルを参照してください。

次の項目

参照

15 - Podを構成してConfigMapを使用する

ConfigMapを使用すると、設定をイメージのコンテンツから切り離して、コンテナ化されたアプリケーションの移植性を維持できます。このページでは、ConfigMapを作成し、ConfigMapに保存されているデータを使用してPodを構成する一連の使用例を示します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

ConfigMapを作成する

kubectl create configmapまたはkustomization.yamlのConfigMap generatorを使用すると、ConfigMapを作成できます。kubectlkustomization.yamlをサポートをしているのは1.14からである点に注意してください。

kubectl create configmapを使用してConfigMapを作成する

kubectl create configmapを使用してConfigMapをディレクトリファイル、またはリテラル値から作成します:

kubectl create configmap <map-name> <data-source>

<map-name>の部分はConfigMapに割り当てる名前で、<data-source>はデータを取得するディレクトリ、ファイル、またはリテラル値です。ConfigMapの名前は有効なDNSサブドメイン名である必要があります。

ファイルをベースにConfigMapを作成する場合、<data-source> のキーはデフォルトでファイル名になり、値はデフォルトでファイルの中身になります。

kubectl describeまたは kubectl getを使用すると、ConfigMapに関する情報を取得できます。

ディレクトリからConfigMapを作成する

kubectl create configmapを使用すると、同一ディレクトリ内にある複数のファイルから1つのConfigMapを作成できます。ディレクトリをベースにConfigMapを作成する場合、kubectlはディレクトリ内でベース名が有効なキーであるファイルを識別し、それらのファイルを新たなConfigMapにパッケージ化します。ディレクトリ内にある通常のファイルでないものは無視されます(例: サブディレクトリ、シンボリックリンク、デバイス、パイプなど)。

例えば:

# ローカルディレクトリを作成します
mkdir -p configure-pod-container/configmap/

# `configure-pod-container/configmap/`ディレクトリにサンプルファイルをダウンロードします
wget https://kubernetes.io/examples/configmap/game.properties -O configure-pod-container/configmap/game.properties
wget https://kubernetes.io/examples/configmap/ui.properties -O configure-pod-container/configmap/ui.properties

# ConfigMapを作成します
kubectl create configmap game-config --from-file=configure-pod-container/configmap/

上記のコマンドは各ファイルをパッケージ化します。この場合、configure-pod-container/configmap/ ディレクトリのgame.propertiesui.propertiesをgame-config ConfigMapにパッケージ化します。 以下のコマンドを使用すると、ConfigMapの詳細を表示できます:

kubectl describe configmaps game-config

出力結果は以下のようになります:

Name:         game-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

configure-pod-container/configmap/ ディレクトリのgame.propertiesui.properties ファイルはConfigMapのdataセクションに表示されます。

kubectl get configmaps game-config -o yaml

出力結果は以下のようになります:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T18:52:05Z
  name: game-config
  namespace: default
  resourceVersion: "516"
  uid: b4952dc3-d670-11e5-8cd0-68f728db1985
data:
  game.properties: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30    
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice    

ファイルからConfigMapを作成する

kubectl create configmapを使用して、個別のファイルまたは複数のファイルからConfigMapを作成できます。

例えば、

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties

は、以下のConfigMapを生成します:

kubectl describe configmaps game-config-2

出力結果は以下のようになります:

Name:         game-config-2
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

--from-file引数を複数回渡し、ConfigMapを複数のデータソースから作成できます。

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties --from-file=configure-pod-container/configmap/ui.properties

以下のコマンドを使用すると、ConfigMapgame-config-2の詳細を表示できます:

kubectl describe configmaps game-config-2

出力結果は以下のようになります:

Name:         game-config-2
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

--from-env-fileオプションを利用してConfigMapをenv-fileから作成します。例えば:

# Env-filesは環境編集のリストを含んでいます。
# 以下のシンタックスルールが適用されます:
#   envファイルの各行はVAR=VALの形式である必要がある。
#   #で始まる行 (例えばコメント)は無視される。
#   空の行は無視される。
#   クオーテーションマークは特別な扱いは処理をしない(例えばConfigMapの値の一部になる).

# `configure-pod-container/configmap/`ディレクトリにサンプルファイルをダウンロードします
wget https://kubernetes.io/examples/configmap/game-env-file.properties -O configure-pod-container/configmap/game-env-file.properties

# env-file `game-env-file.properties`は以下のようになります
cat configure-pod-container/configmap/game-env-file.properties
enemies=aliens
lives=3
allowed="true"

# このコメントと上記の空の行は無視されます
kubectl create configmap game-config-env-file \
       --from-env-file=configure-pod-container/configmap/game-env-file.properties

は、以下のConfigMapを生成します:

kubectl get configmap game-config-env-file -o yaml

出力結果は以下のようになります:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2017-12-27T18:36:28Z
  name: game-config-env-file
  namespace: default
  resourceVersion: "809965"
  uid: d9d1ca5b-eb34-11e7-887b-42010a8002b8
data:
  allowed: '"true"'
  enemies: aliens
  lives: "3"

--from-env-fileを複数回渡す場合の挙動は以下のように示されます:

# `configure-pod-container/configmap/`ディレクトリにサンブルファイルをダウンロードします
wget https://kubernetes.io/examples/configmap/ui-env-file.properties -O configure-pod-container/configmap/ui-env-file.properties

# ConfigMapを作成します
kubectl create configmap config-multi-env-files \
        --from-env-file=configure-pod-container/configmap/game-env-file.properties \
        --from-env-file=configure-pod-container/configmap/ui-env-file.properties

は、以下のConfigMapを生成します:

kubectl get configmap config-multi-env-files -o yaml

出力結果は以下のようになります:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2017-12-27T18:38:34Z
  name: config-multi-env-files
  namespace: default
  resourceVersion: "810136"
  uid: 252c4572-eb35-11e7-887b-42010a8002b8
data:
  color: purple
  how: fairlyNice
  textmode: "true"

ファイルからConfigMap作成する場合は使用するキーを定義する

--from-file引数を使用する場合、ConfigMapのdata セクションでキーにファイル名以外を定義できます:

kubectl create configmap game-config-3 --from-file=<my-key-name>=<path-to-file>

<my-key-name>の部分はConfigMapで使うキー、<path-to-file> はキーで表示したいデータソースファイルの場所です。

例えば:

kubectl create configmap game-config-3 --from-file=game-special-key=configure-pod-container/configmap/game.properties

は、以下のConfigMapを生成します:

kubectl get configmaps game-config-3 -o yaml

出力結果は以下のようになります:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T18:54:22Z
  name: game-config-3
  namespace: default
  resourceVersion: "530"
  uid: 05f8da22-d671-11e5-8cd0-68f728db1985
data:
  game-special-key: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30    

リテラル値からConfigMapを作成する

--from-literal引数を指定してkubectl create configmapを使用すると、コマンドラインからリテラル値を定義できます:

kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

複数のキーバリューペアを渡せます。CLIに提供された各ペアは、ConfigMapのdataセクションで別のエントリーとして表示されます。

kubectl get configmaps special-config -o yaml

出力結果は以下のようになります:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T19:14:38Z
  name: special-config
  namespace: default
  resourceVersion: "651"
  uid: dadce046-d673-11e5-8cd0-68f728db1985
data:
  special.how: very
  special.type: charm

ジェネレーターからConfigMapを作成する

kubectlkustomization.yamlを1.14からサポートしています。 ジェネレーターからConfigMapを作成して適用すると、APIサーバー上でオブジェクトを作成できます。ジェネレーターはディレクトリ内のkustomization.yamlで指定する必要があリます。

ファイルからConfigMapを生成する

例えば、ファイルconfigure-pod-container/configmap/game.propertiesからConfigMapを生成するには、

# ConfigMapGeneratorを含むkustomization.yamlファイルを作成する
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: game-config-4
  files:
  - configure-pod-container/configmap/game.properties
EOF

ConfigMapを作成するためにkustomizationディレクトリを適用します。

kubectl apply -k .
configmap/game-config-4-m9dm2f92bt created

ConfigMapが作成されたことを以下のようにチェックできます:

kubectl get configmap
NAME                       DATA   AGE
game-config-4-m9dm2f92bt   1      37s


kubectl describe configmaps/game-config-4-m9dm2f92bt
Name:         game-config-4-m9dm2f92bt
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"game.properties":"enemies=aliens\nlives=3\nenemies.cheat=true\nenemies.cheat.level=noGoodRotten\nsecret.code.p...

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
Events:  <none>

生成されたConfigMapの名前は、コンテンツをハッシュ化したサフィックスを持つことに注意してください。これにより、コンテンツが変更されるたびに新しいConfigMapが生成されます。

ファイルからConfigMapを生成する場合に使用するキーを定義する

ConfigMapジェネレーターで使用するキーはファイルの名前以外を定義できます。 例えば、ファイルconfigure-pod-container/configmap/game.propertiesからキーgame-special-keyを持つConfigMapを作成する場合

# ConfigMapGeneratorを含むkustomization.yamlファイルを作成する
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: game-config-5
  files:
  - game-special-key=configure-pod-container/configmap/game.properties
EOF

kustomizationディレクトリを適用してConfigMapを作成します。

kubectl apply -k .
configmap/game-config-5-m67dt67794 created

リテラルからConfigMapを作成する

リテラルspecial.type=charmspecial.how=veryからConfigMapを作成する場合は、 以下のようにkustomization.yamlのConfigMapジェネレーターで指定できます。

# ConfigMapGeneratorを含むkustomization.yamlファイルを作成します
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: special-config-2
  literals:
  - special.how=very
  - special.type=charm
EOF

kustomizationディレクトリを適用してConfigMapを作成します。

kubectl apply -k .
configmap/special-config-2-c92b5mmcf2 created

ConfigMapデータを使用してコンテナ環境変数を定義する

単一のConfigMapのデータを使用してコンテナ環境変数を定義する

  1. ConfigMapに環境変数をキーバリューペアとして定義します:

    kubectl create configmap special-config --from-literal=special.how=very
    
  2. ConfigMapに定義された値special.howをPod specificationの環境変数SPECIAL_LEVEL_KEYに割り当てます。

apiVersion: v1
   kind: Pod
   metadata:
     name: dapi-test-pod
   spec:
     containers:
       - name: test-container
         image: registry.k8s.io/busybox
         command: [ "/bin/sh", "-c", "env" ]
         env:
           # 環境変数を定義します
           - name: SPECIAL_LEVEL_KEY
             valueFrom:
               configMapKeyRef:
                 # SPECIAL_LEVEL_KEYに割り当てる値をConfigMapが保持します
                 name: special-config
                 # 値に紐付けるキーを指定します
                 key: special.how
     restartPolicy: Never
   

Podを作成します:

kubectl create -f https://kubernetes.io/examples/pods/pod-single-configmap-env-variable.yaml

すると、Podの出力結果に環境変数SPECIAL_LEVEL_KEY=veryが含まれています。

複数のConfigMapのデータを使用してコンテナ環境変数を定義する

  • 先ほどの例の通り、まずはConfigMapを作成します。

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: special-config
      namespace: default
    data:
      special.how: very
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: env-config
      namespace: default
    data:
      log_level: INFO
    

    ConfigMapを作成します:

kubectl create -f https://kubernetes.io/examples/configmap/configmaps.yaml
  • Pod specificationの環境変数を定義します

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          env:
            - name: SPECIAL_LEVEL_KEY
              valueFrom:
                configMapKeyRef:
                  name: special-config
                  key: special.how
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: env-config
                  key: log_level
      restartPolicy: Never
    

    Podを作成します:

kubectl create -f https://kubernetes.io/examples/pods/pod-multiple-configmap-env-variable.yaml

すると、Podの出力結果に環境変数SPECIAL_LEVEL_KEY=very and LOG_LEVEL=INFOが含まれています。

ConfigMapの全てのキーバリューペアをコンテナ環境変数として構成する

  • 複数のキーバリューペアを含むConfigMapを作成します。

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: special-config
      namespace: default
    data:
      SPECIAL_LEVEL: very
      SPECIAL_TYPE: charm
    

    ConfigMapを作成します:

kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml
  • envFromを利用して全てのConfigMapのデータをコンテナ環境変数として定義します。ConfigMapからのキーがPodの環境変数名になります。
apiVersion: v1
 kind: Pod
 metadata:
   name: dapi-test-pod
 spec:
   containers:
     - name: test-container
       image: registry.k8s.io/busybox
       command: [ "/bin/sh", "-c", "env" ]
       envFrom:
       - configMapRef:
           name: special-config
   restartPolicy: Never
 

Podを作成します:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-envFrom.yaml

すると、Podの出力結果は環境変数SPECIAL_LEVEL=verySPECIAL_TYPE=charmが含まれています。

PodのコマンドでConfigMapに定義した環境変数を使用する

ConfigMapに環境変数を定義し、Pod specificationのcommand セクションで$(VAR_NAME)Kubernetes置換構文を介して使用できます。

例えば以下のPod specificationは

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/echo", "$(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_LEVEL
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_TYPE
  restartPolicy: Never

以下コマンドの実行で作成され、

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-env-var-valueFrom.yaml

test-containerコンテナで以下の出力結果を表示します:

kubectl logs dapi-test-pod
very charm

ボリュームにConfigMapデータを追加する

ファイルからConfigMapを作成するで説明したように、--from-fileを使用してConfigMapを作成する場合は、ファイル名がConfigMapのdataセクションに保存されるキーになり、ファイルのコンテンツがキーの値になります。

このセクションの例は以下に示されているspecial-configと名付けれたConfigMapについて言及したものです。

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  SPECIAL_LEVEL: very
  SPECIAL_TYPE: charm

ConfigMapを作成します:

kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml

ConfigMapに保存されているデータをボリュームに入力する

ConfigMap名をPod specificationのvolumesセクション配下に追加します。 これによりConfigMapデータがvolumeMounts.mountPathで指定されたディレクトリに追加されます (このケースでは、/etc/configに)。commandセクションはConfigMapのキーに合致したディレクトリファイルを名前別でリスト表示します。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "ls /etc/config/" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # コンテナに追加するファイルを含むConfigMapの名前を提供する
        name: special-config
  restartPolicy: Never

Podを作成します:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume.yaml

Podが稼働していると、ls /etc/config/は以下の出力結果を表示します:

SPECIAL_LEVEL
SPECIAL_TYPE

ConfigMapデータをボリュームの特定のパスに追加する

pathフィールドを利用して特定のConfigMapのアイテム向けに希望のファイルパスを指定します。 このケースではSPECIAL_LEVELアイテムが/etc/config/keysconfig-volumeボリュームにマウントされます。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh","-c","cat /etc/config/keys" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: special-config
        items:
        - key: SPECIAL_LEVEL
          path: keys
  restartPolicy: Never

Podを作成します:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume-specific-key.yaml

Podが稼働していると、 cat /etc/config/keysは以下の出力結果を表示します:

very

キーを特定のパスとファイルアクセス許可に投影する

キーをファイル単位で特定のパスとアクセス許可に投影できます。Secretのユーザーガイドで構文が解説されています。

マウントされたConfigMapは自動的に更新される

ボリュームで使用されているConfigMapが更新されている場合、投影されているキーも同じく結果的に更新されます。kubeletは定期的な同期ごとにマウントされているConfigMapが更新されているかチェックします。しかし、これはローカルのttlを基にしたキャッシュでConfigMapの現在の値を取得しています。その結果、新しいキーがPodに投影されてからConfigMapに更新されるまでのトータルの遅延はkubeletで、kubeletの同期期間(デフォルトで1分) + ConfigMapキャッシュのttl(デフォルトで1分)の長さになる可能性があります。Podのアノテーションを1つ更新すると即時のリフレッシュをトリガーできます。

ConfigMapとPodsを理解する

ConfigMap APIリソースは構成情報をキーバリューペアとして保存します。データはPodで利用したり、コントローラーなどのシステムコンポーネントに提供できます。ConfigMapはSecretに似ていますが、機密情報を含まない文字列を含まない操作する手段を提供します。ユーザーとシステムコンポーネントはどちらも構成情報をConfigMapに保存できます。

ConfigMapのdataフィールドは構成情報を含みます。下記の例のように、シンプルに個別のプロパティーを--from-literalで定義、または複雑に構成ファイルまたはJSON blobsを--from-fileで定義できます。

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T19:14:38Z
  name: example-config
  namespace: default
data:
  # --from-literalを使用してシンプルにプロパティーを定義する例
  example.property.1: hello
  example.property.2: world
  # --from-fileを使用して複雑にプロパティーを定義する例
  example.property.file: |-
    property.1=value-1
    property.2=value-2
    property.3=value-3    

制限事項

  • ConfigMapはPod specificationを参照させる前に作成する必要があります(ConfigMapを"optional"として設定しない限り)。存在しないConfigMapを参照させた場合、Podは起動しません。同様にConfigMapに存在しないキーを参照させた場合も、Podは起動しません。

  • ConfigMapでenvFromを使用して環境変数を定義した場合、無効と判断されたキーはスキップされます。Podは起動されますが、無効な名前はイベントログに(InvalidVariableNames)と記録されます。ログメッセージはスキップされたキーごとにリスト表示されます。例えば:

    kubectl get events
    

    出力結果は以下のようになります:

    LASTSEEN FIRSTSEEN COUNT NAME          KIND  SUBOBJECT  TYPE      REASON                            SOURCE                MESSAGE
    0s       0s        1     dapi-test-pod Pod              Warning   InvalidEnvironmentVariableNames   {kubelet, 127.0.0.1}  Keys [1badkey, 2alsobad] from the EnvFrom configMap default/myconfig were skipped since they are considered invalid environment variable names.
    
  • ConfigMapは特定のNamespaceに属します。ConfigMapは同じ名前空間に属するPodからのみ参照できます。

  • static podsはKubeletがサポートしていないため、ConfigMapに使用できません。

次の項目

16 - Pod内のコンテナ間でプロセス名前空間を共有する

FEATURE STATE: Kubernetes v1.17 [stable]

このページでは、プロセス名前空間を共有するPodを構成する方法を示します。 プロセス名前空間の共有が有効になっている場合、コンテナ内のプロセスは、そのPod内の他のすべてのコンテナに表示されます。

この機能を使用して、ログハンドラーサイドカーコンテナなどの協調コンテナを構成したり、シェルなどのデバッグユーティリティを含まないコンテナイメージをトラブルシューティングしたりできます。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.10. バージョンを確認するには次のコマンドを実行してください: kubectl version.

Podを構成する

プロセス名前空間の共有は、v1.PodSpecshareProcessNamespaceフィールドを使用して有効にします。 例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    securityContext:
      capabilities:
        add:
        - SYS_PTRACE
    stdin: true
    tty: true
  1. クラスターにPod nginxを作成します:

    kubectl apply -f https://k8s.io/examples/pods/share-process-namespace.yaml
    
  2. shellコンテナにアタッチしてpsを実行します:

    kubectl exec -it nginx -c shell -- /bin/sh
    

    コマンドプロンプトが表示されない場合は、Enterキーを押してみてください。

    / # ps ax
    PID   USER     TIME  COMMAND
        1 root      0:00 /pause
        8 root      0:00 nginx: master process nginx -g daemon off;
       14 101       0:00 nginx: worker process
       15 root      0:00 sh
       21 root      0:00 ps ax
    

他のコンテナのプロセスにシグナルを送ることができます。 たとえば、ワーカープロセスを再起動するには、SIGHUPをnginxに送信します。 この操作にはSYS_PTRACE機能が必要です。

/ # kill -HUP 8
/ # ps ax
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    8 root      0:00 nginx: master process nginx -g daemon off;
   15 root      0:00 sh
   22 101       0:00 nginx: worker process
   23 root      0:00 ps ax

/proc/$pid/rootリンクを使用して別のコンテナイメージにアクセスすることもできます。

/ # head /proc/8/root/etc/nginx/nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;

プロセス名前空間の共有について理解する

Podは多くのリソースを共有するため、プロセスの名前空間も共有することになります。 ただし、一部のコンテナイメージは他のコンテナから分離されることが期待されるため、これらの違いを理解することが重要です:

  1. コンテナプロセスは PID 1ではなくなります。 一部のコンテナイメージは、PID 1なしで起動することを拒否し(たとえば、systemdを使用するコンテナ)、kill -HUP 1などのコマンドを実行してコンテナプロセスにシグナルを送信します。 共有プロセス名前空間を持つPodでは、kill -HUP 1はPodサンドボックスにシグナルを送ります。(上の例では/pause)

  2. プロセスはPod内の他のコンテナに表示されます。 これには、引数または環境変数として渡されたパスワードなど、/procに表示されるすべての情報が含まれます。 これらは、通常のUnixアクセス許可によってのみ保護されます。

  3. コンテナファイルシステムは、/proc/$pid/rootリンクを介してPod内の他のコンテナに表示されます。 これによりデバッグが容易になりますが、ファイルシステム内の秘密情報はファイルシステムのアクセス許可によってのみ保護されることも意味します。

17 - static Podを作成する

Static Podとは、APIサーバーが監視せず、特定のノード上のkubeletデーモンによって直接管理されるPodです。コントロールプレーンに管理されるPod(たとえばDeploymentなど)とは異なり、kubeletがそれぞれのstatic Podを監視(および障害時には再起動)します。

Static Podは、常に特定のノード上の1つのKubeletに紐付けられます。

kubeletは、各static Podに対して、自動的にKubernetes APIサーバー上にミラーPodの作成を試みます。つまり、ノード上で実行中のPodはAPIサーバーから検出されますが、APIサーバー自身から制御されることはないということです。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

このページの説明では、Podを実行するためにDockerを使用しており、ノード上のOSがFedoraであることを前提としています。他のディストリビューションやKubernetesのインストール方法によっては、操作が異なる場合があります。

static Podを作成する

static Podは、ファイルシステム上でホストされた設定ファイルまたはウェブ上でホストされた設定ファイルを使用して設定できます。

ファイルシステム上でホストされたstatic Podマニフェスト

マニフェストは、JSONまたはYAML形式の標準のPod定義で、特定のディレクトリに置きます。kubeletの設定ファイルの中で、staticPodPath: <ディレクトリの場所>というフィールドを使用すると、kubeletがこのディレクトリを定期的にスキャンして、YAML/JSONファイルが作成/削除されるたびに、static Podの作成/削除が行われるようになります。指定したディレクトリをスキャンする際、kubeletはドットから始まる名前のファイルを無視することに注意してください。

例として、単純なウェブサーバーをstatic Podとして実行する方法を示します。

  1. static Podを実行したいノードを選択します。この例では、my-node1です。

    ssh my-node1
    
  2. ディレクトリを選び(ここでは/etc/kubelet.dとします)、ここにウェブサーバーのPodの定義を置きます。たとえば、/etc/kubelet.d/static-web.yamlに置きます。

    # このコマンドは、kubeletが実行中のノード上で実行してください
    mkdir /etc/kubelet.d/
    cat <<EOF >/etc/kubelet.d/static-web.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    EOF
    
  3. ノード上のkubeletがこのディレクトリを使用するようにするために、--pod-manifest-path=/etc/kubelet.d/引数を付けてkubeletを実行するように設定します。Fedoraの場合、次の行が含まれるように/etc/kubernetes/kubeletを編集します。

    KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --pod-manifest-path=/etc/kubelet.d/"
    

    あるいは、kubeletの設定ファイルに、staticPodPath: <ディレクトリの場所>フィールドを追加することでも設定できます。

  4. kubeletを再起動します。Fedoraの場合、次のコマンドを実行します。

    # このコマンドは、kubeletが実行中のノード上で実行してください
    systemctl restart kubelet
    

ウェブ上でホストされたstatic Podマニフェスト

kubeletは、--manifest-url=<URL>引数で指定されたファイルを定期的にダウンロードし、Podの定義が含まれたJSON/YAMLファイルとして解釈します。kubeletは、ファイルシステム上でホストされたマニフェストでの動作方法と同じように、定期的にマニフェストを再取得します。static Podのリスト中に変更が見つかると、kubeletがその変更を適用します。

このアプローチを採用する場合、次のように設定します。

  1. YAMLファイルを作成し、kubeletにファイルのURLを渡せるようにするために、ウェブサーバー上に保存する。

    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    
  2. 選択したノード上のkubeletを--manifest-url=<manifest-url>を使用して実行することで、このウェブ上のマニフェストを使用するように設定する。Fedoraの場合、/etc/kubernetes/kubeletに次の行が含まれるように編集します。

    KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --manifest-url=<マニフェストのURL"
    
  3. kubeletを再起動する。Fedoraの場合、次のコマンドを実行します。

    # このコマンドは、kubeletが実行中のノード上で実行してください
    systemctl restart kubelet
    

static Podの動作を観察する

kubeletが起動すると、定義されたすべてのstatic Podを起動します。ここまででstatic Podを設定してkubeletを再起動したため、すでに新しいstatic Podが実行中になっているはずです。

次のコマンドを(ノード上で)実行することで、(static Podを含む)実行中のコンテナを確認できます。

# このコマンドは、kubeletが実行中のノード上で実行してください
docker ps

出力は次のようになります。

CONTAINER ID IMAGE         COMMAND  CREATED        STATUS         PORTS     NAMES
f6d05272b57e nginx:latest  "nginx"  8 minutes ago  Up 8 minutes             k8s_web.6f802af4_static-web-fk-node1_default_67e24ed9466ba55986d120c867395f3c_378e5f3c

APIサーバー上では、ミラーPodを確認できます。

kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
static-web-my-node1        1/1       Running   0          2m

static Podに付けたラベルはミラーPodに伝搬します。ミラーPodに付けたラベルは、通常のPodと同じようにセレクターなどから利用できます。

もしkubectlを使用してAPIサーバーからミラーPodを削除しようとしても、kubeletはstatic Podを削除しません

kubectl delete pod static-web-my-node1
pod "static-web-my-node1" deleted

Podはまだ実行中であることがわかります。

kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
static-web-my-node1        1/1       Running   0          12s

kubeletが実行中のノードに戻り、Dockerコンテナを手動で停止してみることができます。しばらくすると、kubeletが変化に気づき、Podを自動的に再起動することがわかります。

# このコマンドは、kubeletが実行中のノード上で実行してください
docker stop f6d05272b57e # 実際のコンテナIDと置き換えてください
sleep 20
docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED       ...
5b920cbaf8b1        nginx:latest  "nginx -g 'daemon of   2 seconds ago ...

static Podの動的な追加と削除

実行中のkubeletは設定ディレクトリ(この例では/etc/kubelet.d)の変更を定期的にスキャンし、このディレクトリ内にファイルが追加/削除されると、Podの追加/削除を行います。

# This assumes you are using filesystem-hosted static Pod configuration
# このコマンドは、kubeletが実行中のノード上で実行してください
#
mv /etc/kubernetes/manifests/static-web.yaml /tmp
sleep 20
docker ps
# You see that no nginx container is running
mv /tmp/static-web.yaml  /etc/kubernetes/manifests/
sleep 20
docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED           ...
e7a62e3427f1        nginx:latest  "nginx -g 'daemon of   27 seconds ago