これは、このセクションの複数ページの印刷可能なビューです。 印刷するには、ここをクリックしてください.

このページの通常のビューに戻る.

ドキュメント

1 - Kubernetesドキュメント

Kubernetesは、コンテナ化されたアプリケーションの展開、スケーリング、また管理を自動化するためのオープンソースコンテナプラットフォームです。このオープンソースプロジェクトは、Cloud Native Computing Foundationによってホストされています。

1.1 - 利用可能なドキュメントバージョン

本ウェブサイトには、現行版とその直前4バージョンのKubernetesドキュメントがあります。

Kubernetesバージョンのドキュメントの入手性は、そのリリースが現在サポートされているかどうかで分かれます。 どのKubernetesバージョンが公式にどのくらいの期間サポートされるかについて知るには、サポート期間を参照してください。

2 - はじめに

このセクションではKubernetesをセットアップして動かすための複数のやり方について説明します。 Kubernetesをインストールする際には、メンテナンスの容易さ、セキュリティ、制御、利用可能なリソース、クラスターの運用および管理に必要な専門知識に基づいてインストレーションタイプを選んでください。

Kuerbetesクラスターをローカルマシン、クラウド、データセンターにデプロイするために、Kubernetesをダウンロードできます。

kube-apiserverkube-proxyのようないくつかのKubernetesのコンポーネントも、コンテナイメージとしてクラスター内にデプロイできます。

可能であればコンテナイメージとしてKubernetesのコンポーネントを実行し、それらのコンポーネントをKubernetesで管理するようにすることを推奨します。 コンテナを実行するコンポーネント(特にkubelet)は、このカテゴリーには含まれません。

Kubernetesクラスターを自分で管理するのを望まないなら、認定プラットフォームをはじめとする、マネージドのサービスを選択することもできます。 複数のクラウドやベアメタル環境にまたがった、その他の標準あるいはカスタムのソリューションもあります。

環境について学ぶ

Kubernetesについて学んでいる場合、Kubernetesコミュニティにサポートされているツールや、Kubernetesクラスターをローカルマシンにセットアップするエコシステム内のツールを使いましょう。 ツールのインストールを参照してください。

プロダクション環境

プロダクション環境用のソリューションを評価する際には、Kubernetesクラスター(または抽象概念)の運用においてどの部分を自分で管理し、どの部分をプロバイダーに任せるのかを考慮してください。

自分で管理するクラスターであれば、Kubernetesをデプロイするための公式にサポートされているツールはkubeadmです。

次の項目

Kubernetesは、そのコントロールプレーンがLinux上で実行されるよう設計されています。 クラスター内では、Linux上でも、Windowsを含めた別のオペレーティングシステム上でも、アプリケーションを実行できます。

2.1 - 学習環境

2.1.1 - Minikubeを使用してローカル環境でKubernetesを動かす

Minikubeはローカル環境でKubernetesを簡単に実行するためのツールです。Kubernetesを試したり日々の開発への使用を検討するユーザー向けに、PC上のVM内でシングルノードのKubernetesクラスターを実行することができます。

Minikubeの機能

MinikubeのサポートするKubernetesの機能:

  • DNS
  • NodePort
  • ConfigMapとSecret
  • ダッシュボード
  • コンテナランタイム: Docker、CRI-Oおよびcontainerd
  • CNI (Container Network Interface) の有効化
  • Ingress

インストール

ツールのインストールについて知りたい場合は、公式のGet Started!のガイドに従ってください。

クイックスタート

これはMinikubeの起動、使用、削除をローカルで実施する簡単なデモです。下記の手順に従って、Minikubeを起動し試してください。

  1. Minikubeを起動し、クラスターを作成します:

    minikube start
    

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

    Starting local Kubernetes cluster...
    Running pre-create checks...
    Creating machine...
    Starting local Kubernetes cluster...
    

    特定のKubernetesのバージョン、VM、コンテナランタイム上でクラスターを起動するための詳細は、クラスターの起動を参照してください。

  2. kubectlを使用してクラスターと対話できるようになります。詳細はクラスターに触れてみようを参照してください。

    単純なHTTPサーバーであるechoserverという既存のイメージを使用して、Kubernetes Deploymentを作りましょう。そして--portを使用して8080番ポートで公開しましょう。

    kubectl create deployment hello-minikube --image=registry.k8s.io/echoserver:1.10
    

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

    deployment.apps/hello-minikube created
    
  3. hello-minikubeDeploymentに接続するために、Serviceとして公開します:

    kubectl expose deployment hello-minikube --type=NodePort --port=8080
    

    --type=NodePortオプションで、Serviceのタイプを指定します。

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

    service/hello-minikube exposed
    
  4. hello-minikubePodが起動開始されましたが、公開したService経由で接続する前にPodが起動完了になるまで待つ必要があります。

    Podが稼働しているか確認します:

    kubectl get pod
    

    STATUSContainerCreatingと表示されている場合、Podはまだ作成中です:

    NAME                              READY     STATUS              RESTARTS   AGE
    hello-minikube-3383150820-vctvh   0/1       ContainerCreating   0          3s
    

    STATUSRunningと表示されている場合、Podは稼働中です:

    NAME                              READY     STATUS    RESTARTS   AGE
    hello-minikube-3383150820-vctvh   1/1       Running   0          13s
    
  5. Serviceの詳細を確認するため、公開したServiceのURLを取得します:

    minikube service hello-minikube --url
    
  6. ローカル環境のクラスターについて詳細を確認するには、出力から得たURLをブラウザー上でコピーアンドペーストしてください。

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

    Hostname: hello-minikube-7c77b68cff-8wdzq
    
    Pod Information:
     -no pod information available-
    
    Server values:
     server_version=nginx: 1.13.3 - lua: 10008
    
    Request Information:
     client_address=172.17.0.1
     method=GET
     real path=/
     query=
     request_version=1.1
     request_scheme=http
     request_uri=http://192.168.99.100:8080/
    
    Request Headers:
    	accept=*/*
    	host=192.168.99.100:30674
    	user-agent=curl/7.47.0
    
    Request Body:
    	-no body in request-
    

    Serviceやクラスターをこれ以上稼働させない場合、削除する事ができます。

  7. hello-minikubeServiceを削除します:

    kubectl delete services hello-minikube
    

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

    service "hello-minikube" deleted
    
  8. hello-minikubeDeploymentを削除します:

    kubectl delete deployment hello-minikube
    

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

    deployment.extensions "hello-minikube" deleted
    
  9. ローカル環境のMinikubeクラスターを停止します:

    minikube stop
    

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

    Stopping "minikube"...
    "minikube" stopped.
    

    詳細はクラスターの停止を参照ください。

  10. ローカルのMinikubeクラスターを削除します:

    minikube delete
    

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

    Deleting "minikube" ...
    The "minikube" cluster has been deleted.
    

    詳細はクラスターの削除を参照ください。

クラスターの管理

クラスターの起動

minikube startコマンドを使用してクラスターを起動することができます。 このコマンドはシングルノードのKubernetesクラスターを実行する仮想マシンを作成・設定します。 また、このクラスターと通信するkubectlのインストールも設定します。

Kubernetesバージョンの指定

minikube startコマンドに--kubernetes-version文字列を追加することで、 MinikubeにKubernetesの特定のバージョンを指定することができます。 例えば、1.30.0のバージョンを実行するには以下を実行します:

minikube start --kubernetes-version 1.30.0

VMドライバーの指定

もしVMドライバーを変更したい場合は、--driver=<enter_driver_name>フラグをminikube startに設定してください。例えば、コマンドは以下のようになります。

minikube start --driver=<driver_name>

Minikubeは以下のドライバーをサポートしています:

コンテナランタイムの代替

下記のコンテナランタイム上でMinikubeを起動できます。

containerd をコンテナランタイムとして使用するには以下を実行してください:

minikube start \
    --network-plugin=cni \
    --enable-default-cni \
    --container-runtime=containerd \
    --bootstrapper=kubeadm

もしくは拡張バージョンを使用することもできます:

minikube start \
    --network-plugin=cni \
    --enable-default-cni \
    --extra-config=kubelet.container-runtime=remote \
    --extra-config=kubelet.container-runtime-endpoint=unix:///run/containerd/containerd.sock \
    --extra-config=kubelet.image-service-endpoint=unix:///run/containerd/containerd.sock \
    --bootstrapper=kubeadm

CRI-Oをコンテナランタイムとして使用するには以下を実行してください:

minikube start \
    --network-plugin=cni \
    --enable-default-cni \
    --container-runtime=cri-o \
    --bootstrapper=kubeadm

もしくは拡張バージョンを使用することもできます:

minikube start \
    --network-plugin=cni \
    --enable-default-cni \
    --extra-config=kubelet.container-runtime=remote \
    --extra-config=kubelet.container-runtime-endpoint=/var/run/crio.sock \
    --extra-config=kubelet.image-service-endpoint=/var/run/crio.sock \
    --bootstrapper=kubeadm

Dockerデーモンの再利用によるローカルイメージの使用

Kubernetesの単一のVMを使用する場合、Minikube組み込みのDockerデーモンの再利用がおすすめです。ホストマシン上にDockerレジストリを構築してイメージをプッシュする必要がなく、ローカルでの実験を加速させるMinikubeと同じDockerデーモンの中に構築することができます。

Mac/LinuxのホストでDockerデーモンを操作できるようにするには、minikube docker-envを実行します。

これにより、MinikubeのVM内のDockerデーモンと通信しているホストのMac/LinuxマシンのコマンドラインでDockerを使用できるようになります:

docker ps

Kubernetesの設定

Minikubeにはユーザーが任意の値でKubernetesコンポーネントを設定することを可能にする "configurator" 機能があります。 この機能を使うには、minikube start コマンドに --extra-config フラグを使うことができます。

このフラグは繰り返されるので、複数のオプションを設定するためにいくつかの異なる値を使って何度も渡すことができます。

このフラグは component.key=value 形式の文字列を取ります。component は下記のリストの文字列の1つです。 keyは設定構造体上の値で、 value は設定する値です。

各コンポーネントのKubernetes componentconfigs のドキュメントを調べることで有効なキーを見つけることができます。 サポートされている各設定のドキュメントは次のとおりです:

Kubeletの MaxPods 設定を5に変更するには、このフラグを渡します: --extra-config=kubelet.MaxPods=5

この機能はネストした構造体もサポートします。スケジューラーの LeaderElection.LeaderElecttrue に設定するには、このフラグを渡します: --extra-config=scheduler.LeaderElection.LeaderElect=true

apiserverAuthorizationModeRABC に設定するには、このフラグを使います: --extra-config=apiserver.authorization-mode=RBAC.

クラスターの停止

minikube stop コマンドを使ってクラスターを停止することができます。 このコマンドはMinikube仮想マシンをシャットダウンしますが、すべてのクラスターの状態とデータを保存します。 クラスターを再起動すると、以前の状態に復元されます。

クラスターの削除

minikube delete コマンドを使ってクラスターを削除することができます。 このコマンドはMinikube仮想マシンをシャットダウンして削除します。データや状態は保存されません。

minikubeのアップグレード

macOSを使用しBrew Package Managerがインストールされている場合、以下を実行します:

brew update
brew upgrade minikube

クラスターに触れてみよう

Kubectl

minikube start コマンドは "minikube" というkubectl contextを作成します。 このコンテキストはMinikubeクラスターと通信するための設定が含まれています。

Minikubeはこのコンテキストを自動的にデフォルトに設定しますが、将来的に設定を切り戻す場合には次のコマンドを実行してください:

kubectl config use-context minikube

もしくは各コマンドにコンテキストを次のように渡します:

kubectl get pods --context=minikube

ダッシュボード

Kubernetes Dashboardにアクセスするには、Minikubeを起動してアドレスを取得した後、シェルでこのコマンドを実行してください:

minikube dashboard

サービス

ノードポート経由で公開されているサービスにアクセスするには、Minikubeを起動してアドレスを取得した後、シェルでこのコマンドを実行してください:

minikube service [-n NAMESPACE] [--url] NAME

ネットワーク

MinikubeのVMは minikube ipコマンドで取得できるホストオンリーIPアドレスを介してホストシステムに公開されます。 NodePort上では、 NodePort タイプのどのサービスもそのIPアドレスを介してアクセスできます。

サービスのNodePortを決定するには、kubectl コマンドを次のように使用します:

kubectl get service $SERVICE --output='jsonpath="{.spec.ports[0].nodePort}"'

永続ボリューム

Minikubeは hostPath タイプのPersistentVolumesをサポートします。 このPersistentVolumesはMinikubeのVM内のディレクトリーにマッピングされます。

MinikubeのVMはtmpfsで起動するため、ほとんどのディレクトリーは再起動しても持続しません (minikube stop)。 しかし、Minikubeは以下のホストディレクトリーに保存されているファイルを保持するように設定されています:

  • /data
  • /var/lib/minikube
  • /var/lib/docker

以下は /data ディレクトリのデータを永続化するPersistentVolumeの設定例です:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0001
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 5Gi
  hostPath:
    path: /data/pv0001/

ホストフォルダーのマウント

一部のドライバーはVM内にホストフォルダーをマウントするため、VMとホストの間でファイルを簡単に共有できます。これらは現時点では設定可能ではなく、使用しているドライバーとOSによって異なります。

DriverOSHostFolderVM
VirtualBoxLinux/home/hosthome
VirtualBoxmacOS/Users/Users
VirtualBoxWindowsC://Users/c/Users
VMware FusionmacOS/Users/mnt/hgfs/Users
XhyvemacOS/Users/Users

プライベートコンテナレジストリ

プライベートコンテナレジストリにアクセスするには、このページの手順に従ってください。

ImagePullSecrets を使用することをおすすめしますが、MinikubeのVM内でアクセス設定したい場合には、/home/docker ディレクトリに .dockercfg を置くか、または /home/docker/.docker ディレクトリに config.json を置いてください。

アドオン

カスタムアドオンを正しく起動または再起動させるには、 Minikubeで起動したいアドオンを ~/.minikube/addons ディレクトリに置きます。 このフォルダ内のアドオンはMinikubeのVMに移動され、Minikubeが起動または再起動されるたびにアドオンが起動されます。

HTTPプロキシ経由のMinikube利用

MinikubeはKubernetesとDockerデーモンを含む仮想マシンを作成します。 KubernetesがDockerを使用してコンテナをスケジュールしようとする際、Dockerデーモンはコンテナをプルするために外部ネットワークを必要とする場合があります。

HTTPプロキシを通している場合には、プロキシ設定をDockerに提供する必要があります。 これを行うには、minikube start に必要な環境変数をフラグとして渡します。

例:

minikube start --docker-env http_proxy=http://$YOURPROXY:PORT \
               --docker-env https_proxy=https://$YOURPROXY:PORT

仮想マシンのアドレスが192.168.99.100の場合、プロキシの設定により kubectl が直接アクセスできない可能性があります。 このIPアドレスのプロキシ設定を迂回するには、以下のようにno_proxy設定を変更する必要があります。

export no_proxy=$no_proxy,$(minikube ip)

既知の問題

複数ノードを必要とする機能はMinikubeでは動作しません。

設計

MinikubeはVMのプロビジョニングにlibmachineを使用し、kubeadmをKubernetesクラスターのプロビジョニングに使用します。

Minikubeの詳細については、proposalを参照してください。

追加リンク集

  • 目標と非目標: Minikubeプロジェクトの目標と非目標については、ロードマップを参照してください。
  • 開発ガイド: プルリクエストを送る方法の概要については、コントリビュートするを参照してください。
  • Minikubeのビルド: Minikubeをソースからビルド/テストする方法については、ビルドガイドを参照してください。
  • 新しい依存性の追加: Minikubeに新しい依存性を追加する方法については、依存性追加ガイドを参照してください。
  • 新しいアドオンの追加: Minikubeに新しいアドオンを追加する方法については、アドオン追加ガイドを参照してください。
  • MicroK8s: 仮想マシンを実行したくないLinuxユーザーは代わりにMicroK8sを検討してみてください。

コミュニティ

コントリビューションや質問、コメントは歓迎・奨励されています! Minikubeの開発者はSlack#minikubeチャンネルにいます(Slackへの招待状はこちら)。dev@kubernetes Google Groupsメーリングリストもあります。メーリングリストに投稿する際は件名の最初に "minikube: " をつけてください。

2.1.2 - Kindを使用してKubernetesをインストールする

Kindは、Dockerコンテナをノードとして使用して、ローカルのKubernetesクラスターを実行するためのツールです。

インストール

Kindをインストールするを参照してください。

2.2 - プロダクション環境

2.2.1 - コンテナランタイム

クラスター内の各ノードがPodを実行できるようにするため、コンテナランタイムをインストールする必要があります。 このページでは、ノードをセットアップするための概要と関連する作業について説明します。

Kubernetes 1.30においては、Container Runtime Interface (CRI)に準拠したランタイムを使用する必要があります。

詳しくはサポートするCRIのバージョンをご覧ください。

このページではいくつかの一般的なコンテナランタイムをKubernetesで使用する方法の概要を説明します。

インストールと設定の必須要件

以下の手順では、全コンテナランタイムに共通の設定をLinux上のKubernetesノードに適用します。

特定の設定が不要であることが分かっている場合、手順をスキップして頂いて構いません。

詳細については、Network Plugin Requirementsまたは、特定のコンテナランタイムのドキュメントを参照してください。

IPv4フォワーディングを有効化し、iptablesからブリッジされたトラフィックを見えるようにする

以下のコマンドを実行します。

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# この構成に必要なカーネルパラメーター、再起動しても値は永続します
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# 再起動せずにカーネルパラメーターを適用
sudo sysctl --system

以下のコマンドを実行してbr_netfilteroverlayモジュールが読み込まれていることを確認してください。

lsmod | grep br_netfilter
lsmod | grep overlay

以下のコマンドを実行して、net.bridge.bridge-nf-call-iptablesnet.bridge.bridge-nf-call-ip6tablesnet.ipv4.ip_forwardカーネルパラメーターが1に設定されていることを確認します。

sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

cgroupドライバー

Linuxでは、プロセスに割り当てられるリソースを制約するためにcgroupが使用されます。

kubeletと基盤となるコンテナランタイムは、コンテナのリソース管理を実施し、CPU/メモリーの要求や制限などのリソースを設定するため、cgroupとインターフェースする必要があります。 cgroupとインターフェースするために、kubeletおよびコンテナランタイムはcgroupドライバーを使用する必要があります。 この際、kubeletとコンテナランタイムが同一のcgroupドライバーを使用し、同一の設定を適用することが不可欠となります。

利用可能なcgroupドライバーは以下の2つです。

cgroupfsドライバー

cgroupfsドライバーは、kubeletのデフォルトのcgroupドライバーです。 cgroupfsドライバーを使用すると、kubeletとコンテナランタイムはcgroupファイルシステムと直接インターフェースし、cgroupを設定します。

systemdがinitシステムである場合、cgroupfsドライバーは推奨されません。 なぜなら、systemdはシステム上のcgroupマネージャーが単一であると想定しているからです。 また、cgroup v2を使用している場合は、cgroupfsの代わりにsystemd cgroupドライバーを使用してください。

systemd cgroupドライバー

Linuxディストリビューションのinitシステムにsystemdが選択されている場合、 initプロセスはルートcgroupを生成・消費し、cgroupマネージャーとして動作します。

systemdはcgroupと密接に連携しており、systemdユニットごとにcgroupを割り当てます。 その結果、initシステムにsystemdを使用した状態でcgroupfsドライバーを使用すると、 システムには2つの異なるcgroupマネージャーが存在することになります。

2つのcgroupマネージャーが存在することで、システムで利用可能なリソースおよび使用中のリソースに、2つの異なる見え方が与えられることになります。 特定の場合において、kubeletとコンテナランタイムにcgroupfsを、残りのプロセスにsystemdを使用するように設定されたノードが高負荷時に不安定になることがあります。

このような不安定性を緩和するためのアプローチは、systemdがinitシステムに採用されている場合にkubeletとコンテナランタイムのcgroupドライバーとしてsystemdを使用することです。

cgroupドライバーにsystemdを設定するには、以下のようにKubeletConfigurationcgroupDriverオプションを編集してsystemdを設定します。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
cgroupDriver: systemd

kubelet用のcgroupドライバーとしてsystemdを設定する場合、コンテナランタイムのcgroupドライバーにもsystemdを設定する必要があります。 具体的な手順については、以下のリンクなどの、お使いのコンテナランタイムのドキュメントを参照してください。

kubeadmで管理されたクラスターでのsystemdドライバーへの移行

既存のkubeadm管理クラスターでsystemd cgroupドライバーに移行したい場合は、cgroupドライバーの設定に従ってください。

サポートするCRIのバージョン

コンテナランタイムは、Container Runtime Interfaceのv1alpha2以上をサポートする必要があります。

Kubernetes 1.30は、デフォルトでCRI APIのv1を使用します。 コンテナランタイムがv1 APIをサポートしていない場合、kubeletは代わりに(非推奨の)v1alpha2 APIにフォールバックします。

コンテナランタイム

containerd

このセクションでは、CRIランタイムとしてcontainerdを使用するために必要な手順の概要を説明します。

以下のコマンドを使用して、システムにcontainerdをインストールします:

まずはcontainerdの使用を開始するの指示に従ってください。有効なconfig.toml設定ファイルを作成したら、このステップに戻ります。

このファイルはパス/etc/containerd/config.tomlにあります。

このファイルはC:\Program Files\containerd\config.tomlにあります。

Linuxでは、containerd用のデフォルトのCRIソケットは/run/containerd/containerd.sockです。 Windowsでは、デフォルトのCRIエンドポイントはnpipe://./pipe/containerd-containerdです。

systemd cgroupドライバーを構成する

/etc/containerd/config.toml内でruncsystemd cgroupドライバーを使うようにするには、次のように設定します。

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  ...
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

cgroup v2を使用する場合はsystemd cgroupドライバーの利用を推奨します。

この変更を適用した場合、必ずcontainerdを再起動してください。

sudo systemctl restart containerd

kubeadmを使用している場合、手動でkubelet cgroupドライバーの設定を行ってください。

サンドボックス(pause)イメージの上書き

containerdの設定で以下の設定をすることで、サンドボックスのイメージを上書きすることができます。

[plugins."io.containerd.grpc.v1.cri"]
  sandbox_image = "registry.k8s.io/pause:3.2"

この場合も、設定ファイルの更新後にsystemctl restart containerdを実行してcontainerdも再起動する必要があるでしょう。

CRI-O

本セクションでは、コンテナランタイムとしてCRI-Oをインストールするために必要な手順を説明します。

CRI-Oをインストールするには、CRI-Oのインストール手順に従ってください。

cgroupドライバー

CRI-Oはデフォルトでsystemd cgroupドライバーを使用し、おそらく問題なく動作します。 cgroupfs cgroupドライバーに切り替えるには、/etc/crio/crio.conf を編集するか、 /etc/crio/crio.conf.d/02-cgroup-manager.confにドロップイン設定ファイルを置いて、以下のような設定を記述してください。

[crio.runtime]
conmon_cgroup = "pod"
cgroup_manager = "cgroupfs"

上記でconmon_cgroupも変更されていることに注意してください。 CRI-Oでcgroupfsを使用する場合、ここにはpodという値を設定する必要があります。 一般に、kubeletのcgroupドライバーの設定(通常はkubeadmによって行われます)とCRI-Oの設定は一致させる必要があります。

CRI-Oの場合、CRIソケットはデフォルトで/var/run/crio/crio.sockとなります。

サンドボックス(pause)イメージの上書き

CRI-Oの設定において、以下の値を設定することができます。

[crio.image]
pause_image="registry.k8s.io/pause:3.6"

このオプションはライブ設定リロードによる変更の適用に対応しています。 systemctl reload crioまたはcrioプロセスにSIGHUPを送信することで変更を適用できます。

Docker Engine

  1. 各ノードに、使用しているLinuxディストリビューション用のDockerをDocker Engineのインストールに従ってインストールします。

  2. cri-dockerdをリポジトリ内の指示に従ってインストールします。

cri-dockerdの場合、CRIソケットはデフォルトで/run/cri-dockerd.sockになります。

Mirantis Container Runtime

Mirantis Container Runtime(MCR)は、 以前はDocker Enterprise Editionとして知られていた、商業的に利用可能なコンテナランタイムです。

MCRに含まれるオープンソースのcri-dockerdコンポーネントを使用することで、 Mirantis Container RuntimeをKubernetesで使用することができます。

Mirantis Container Runtimeのインストール方法について知るには、MCRデプロイガイドを参照してください。

CRIソケットのパスを見つけるには、systemdのcri-docker.socketという名前のユニットを確認してください。

サンドボックス(pause)イメージを上書きする

cri-dockerdアダプターは、Podインフラコンテナ("pause image")として使用するコンテナイメージを指定するためのコマンドライン引数を受け付けます。 使用するコマンドライン引数は --pod-infra-container-imageです。

次の項目

コンテナランタイムに加えて、クラスターには動作するネットワークプラグインが必要です。

2.2.2 - Kubernetesをデプロイツールでインストールする

2.2.2.1 - kubeadmを使ってクラスターを構築する

2.2.2.1.1 - kubeadmのインストール

このページではkubeadmコマンドをインストールする方法を示します。このインストール処理実行後にkubeadmを使用してクラスターを作成する方法については、kubeadmを使用したシングルマスタークラスターの作成を参照してください。

始める前に

  • 次のいずれかが動作しているマシンが必要です
    • Ubuntu 16.04+
    • Debian 9+
    • CentOS 7
    • Red Hat Enterprise Linux (RHEL) 7
    • Fedora 25+
    • HypriotOS v1.0.1+
    • Container Linux (tested with 1800.6.0)
  • 1台あたり2GB以上のメモリ(2GBの場合、アプリ用のスペースはほとんどありません)
  • 2コア以上のCPU
  • クラスター内のすべてのマシン間で通信可能なネットワーク(パブリックネットワークでもプライベートネットワークでも構いません)
  • ユニークなhostname、MACアドレス、とproduct_uuidが各ノードに必要です。詳細はここを参照してください。
  • マシン内の特定のポートが開いていること。詳細はここを参照してください。
  • Swapがオフであること。kubeletが正常に動作するためにはswapは必ずオフでなければなりません。

MACアドレスとproduct_uuidが全てのノードでユニークであることの検証

  • ネットワークインターフェースのMACアドレスはip linkもしくはifconfig -aコマンドで取得できます。
  • product_uuidはsudo cat /sys/class/dmi/id/product_uuidコマンドで確認できます。

ハードウェアデバイスではユニークなアドレスが割り当てられる可能性が非常に高いですが、VMでは同じになることがあります。Kubernetesはこれらの値を使用して、クラスター内のノードを一意に識別します。これらの値が各ノードに固有ではない場合、インストール処理が失敗することもあります。

ネットワークアダプタの確認

複数のネットワークアダプターがあり、Kubernetesコンポーネントにデフォルトで到達できない場合、IPルートを追加して、Kubernetesクラスターのアドレスが適切なアダプターを経由するように設定することをお勧めします。

必須ポートの確認

Kubernetesのコンポーネントが互いに通信するためには、これらの必要なポートが開いている必要があります。 netcatなどのツールを使用することで、下記のようにポートが開いているかどうかを確認することが可能です。

nc 127.0.0.1 6443 -v

使用するPodネットワークプラグインによっては、特定のポートを開く必要がある場合もあります。 これらは各Podネットワークプラグインによって異なるため、どのようなポートが必要かについては、プラグインのドキュメントを参照してください。

ランタイムのインストール

Podのコンテナを実行するために、Kubernetesはコンテナランタイムを使用します。

デフォルトでは、Kubernetesは選択されたコンテナランタイムと通信するためにContainer Runtime Interface (CRI)を使用します。

ランタイムを指定しない場合、kubeadmはよく知られたUnixドメインソケットのリストをスキャンすることで、インストールされたコンテナランタイムの検出を試みます。 次の表がコンテナランタイムと関連するソケットのパスリストです。

コンテナランタイムとソケットパス
ランタイムUnixドメインソケットのパス
Docker/var/run/docker.sock
containerd/run/containerd/containerd.sock
CRI-O/var/run/crio/crio.sock

Dockerとcontainerdの両方が同時に検出された場合、Dockerが優先されます。Docker 18.09にはcontainerdが同梱されており、両方が検出可能であるため、この仕様が必要です。他の2つ以上のランタイムが検出された場合、kubeadmは適切なエラーメッセージで終了します。

kubeletは、組み込まれたdockershimCRIを通してDockerと連携します。

詳細は、コンテナランタイムを参照してください。

デフォルトでは、kubeadmはDockerをコンテナランタイムとして使用します。 kubeletは、組み込まれたdockershimCRIを通してDockerと連携します。

詳細は、コンテナランタイムを参照してください。

kubeadm、kubelet、kubectlのインストール

以下のパッケージをマシン上にインストールしてください

  • kubeadm: クラスターを起動するコマンドです。

  • kubelet: クラスター内のすべてのマシンで実行されるコンポーネントです。 Podやコンテナの起動などを行います。

  • kubectl: クラスターにアクセスするためのコマンドラインツールです。

kubeadmはkubeletkubectlをインストールまたは管理しないため、kubeadmにインストールするKubernetesコントロールプレーンのバージョンと一致させる必要があります。そうしないと、予期しないバグのある動作につながる可能性のあるバージョン差異(version skew)が発生するリスクがあります。ただし、kubeletとコントロールプレーン間のマイナーバージョン差異(minor version skew)は_1つ_サポートされていますが、kubeletバージョンがAPIサーバーのバージョンを超えることはできません。たとえば、1.7.0を実行するkubeletは1.8.0 APIサーバーと完全に互換性がありますが、その逆はできません。

kubectlのインストールに関する詳細情報は、kubectlのインストールおよびセットアップを参照してください。

バージョン差異(version skew)に関しては下記を参照してください。

  1. aptのパッケージ一覧を更新し、Kubernetesのaptリポジトリーを利用するのに必要なパッケージをインストールします:

    sudo apt-get update
    # apt-transport-httpsはダミーパッケージの可能性があります。その場合、そのパッケージはスキップできます
    sudo apt-get install -y apt-transport-https ca-certificates curl gpg
    
  2. Kubernetesパッケージリポジトリーの公開署名キーをダウンロードします。すべてのリポジトリーに同じ署名キーが使用されるため、URL内のバージョンは無視できます:

    # `/etc/apt/keyrings`フォルダーが存在しない場合は、curlコマンドの前に作成する必要があります。下記の備考を参照してください。
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    
  1. 適切なKubernetes aptリポジトリーを追加します。このリポジトリーには、Kubernetes 1.30用のパッケージのみがあることに注意してください。他のKubernetesマイナーバージョンの場合は、目的のマイナーバージョンに一致するようにURL内のKubernetesマイナーバージョンを変更する必要があります(インストールする予定のKubernetesバージョンのドキュメントも読んでください):

    # これにより、/etc/apt/sources.list.d/kubernetes.listにある既存の設定が上書きされます
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  2. aptのパッケージ一覧を更新し、kubelet、kubeadm、kubectlをインストールします。そしてバージョンを固定します:

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl
    sudo apt-mark hold kubelet kubeadm kubectl
    

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

# SELinuxをpermissiveモードに設定する(効果的に無効化する)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

systemctl enable --now kubelet

Note:

  • setenforce 0およびsed ...を実行することによりSELinuxをpermissiveモードに設定し、効果的に無効化できます。 これはコンテナがホストのファイルシステムにアクセスするために必要です。例えば、Podのネットワークに必要とされます。 kubeletにおけるSELinuxのサポートが改善されるまでは、これを実行しなければなりません。

CNIプラグインをインストールする(ほとんどのPodのネットワークに必要です):

CNI_VERSION="v0.8.2"
ARCH="amd64"
mkdir -p /opt/cni/bin
curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz" | tar -C /opt/cni/bin -xz

crictlをインストールする (kubeadm / Kubelet Container Runtime Interface (CRI)に必要です)

CRICTL_VERSION="v1.22.0"
ARCH="amd64"
curl -L "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${ARCH}.tar.gz" | sudo tar -C $DOWNLOAD_DIR -xz

kubeadmkubeletkubectlをインストールしkubeletをsystemd serviceに登録します:

RELEASE="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
ARCH="amd64"
mkdir -p /opt/bin
cd /opt/bin
curl -L --remote-name-all https://dl.k8s.io/release/${RELEASE}/bin/linux/${ARCH}/{kubeadm,kubelet,kubectl}
chmod +x {kubeadm,kubelet,kubectl}

curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/kubelet.service" | sed "s:/usr/bin:/opt/bin:g" > /etc/systemd/system/kubelet.service
mkdir -p /etc/systemd/system/kubelet.service.d
curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/10-kubeadm.conf" | sed "s:/usr/bin:/opt/bin:g" > /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

kubeletを有効化し起動します:

systemctl enable --now kubelet

kubeadmが何をすべきか指示するまで、kubeletはクラッシュループで数秒ごとに再起動します。

コントロールプレーンノードのkubeletによって使用されるcgroupドライバーの設定

Dockerを使用した場合、kubeadmは自動的にkubelet向けのcgroupドライバーを検出し、それを実行時に/var/lib/kubelet/kubeadm-flags.envファイルに設定します。

もしあなたが異なるCRIを使用している場合、/etc/default/kubelet(CentOS、RHEL、Fedoraでは/etc/sysconfig/kubelet)ファイル内のcgroup-driverの値を以下のように変更する必要があります。

KUBELET_EXTRA_ARGS=--cgroup-driver=<value>

このファイルは、kubeletの追加のユーザー定義引数を取得するために、kubeadm initおよびkubeadm joinによって使用されます。

CRIのcgroupドライバーがcgroupfsでない場合にのみそれを行う必要があることに注意してください。なぜなら、これはすでにkubeletのデフォルト値であるためです。

kubeletをリスタートする方法:

systemctl daemon-reload
systemctl restart kubelet

CRI-Oやcontainerdといった他のコンテナランタイムのcgroup driverは実行中に自動的に検出されます。

トラブルシュート

kubeadmで問題が発生した場合は、トラブルシューティングを参照してください。

次の項目

2.2.2.1.2 - kubeadmのトラブルシューティング

どのプログラムでもそうですが、kubeadmのインストールや実行でエラーが発生することがあります。このページでは、一般的な失敗例をいくつか挙げ、問題を理解して解決するための手順を示しています。

本ページに問題が記載されていない場合は、以下の手順を行ってください:

  • 問題がkubeadmのバグによるものと思った場合:

  • kubeadmがどのように動作するかわからない場合は、Slackの#kubeadmチャンネルで質問するか、StackOverflowで質問をあげてください。その際は、他の方が助けを出しやすいように#kubernetes#kubeadmといったタグをつけてください。

RBACがないため、v1.18ノードをv1.17クラスターに結合できない

v1.18では、同名のノードが既に存在する場合にクラスター内のノードに参加しないようにする機能を追加しました。これには、ブートストラップトークンユーザがNodeオブジェクトをGETできるようにRBACを追加する必要がありました。

しかし、これによりv1.18のkubeadm joinがkubeadm v1.17で作成したクラスターに参加できないという問題が発生します。

この問題を回避するには、次の2つの方法があります。

  • kubeadm v1.18を用いて、コントロールプレーンノード上でkubeadm init phase bootstrap-tokenを実行します。 これには、ブートストラップトークンの残りのパーミッションも同様に有効にすることに注意してください。

  • kubectl apply -f ...を使って以下のRBACを手動で適用します。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kubeadm:get-nodes
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubeadm:get-nodes
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubeadm:get-nodes
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:kubeadm:default-node-token

インストール中にebtablesもしくは他の似たような実行プログラムが見つからない

kubeadm initの実行中に以下のような警告が表示された場合は、以降に記載するやり方を行ってください。

[preflight] WARNING: ebtables not found in system path
[preflight] WARNING: ethtool not found in system path

このような場合、ノード上にebtables, ethtoolなどの実行ファイルがない可能性があります。これらをインストールするには、以下のコマンドを実行します。

  • Ubuntu/Debianユーザーは、apt install ebtables ethtoolを実行してください。
  • CentOS/Fedoraユーザーは、yum install ebtables ethtoolを実行してください。

インストール中にkubeadmがコントロールプレーンを待ち続けて止まる

以下のを出力した後にkubeadm initが止まる場合は、kubeadm initを実行してください:

[apiclient] Created API client, waiting for the control plane to become ready

これはいくつかの問題が原因となっている可能性があります。最も一般的なのは:

  • ネットワーク接続の問題が挙げられます。続行する前に、お使いのマシンがネットワークに完全に接続されていることを確認してください。

  • kubeletのデフォルトのcgroupドライバの設定がDockerで使用されているものとは異なっている場合も考えられます。 システムログファイル(例: /var/log/message)をチェックするか、journalctl -u kubeletの出力を調べてください:

    error: failed to run Kubelet: failed to create kubelet:
    misconfiguration: kubelet cgroup driver: "systemd" is different from docker cgroup driver: "cgroupfs"
    

    以上のようなエラーが現れていた場合、cgroupドライバの問題を解決するには、以下の2つの方法があります:

  1. ここの指示に従ってDockerを再度インストールします。

  2. Dockerのcgroupドライバに合わせてkubeletの設定を手動で変更します。その際は、マスターノード上でkubeletが使用するcgroupドライバを設定するを参照してください。

  • control plane Dockerコンテナがクラッシュループしたり、ハングしたりしています。これはdocker psを実行し、docker logsを実行して各コンテナを調査することで確認できます。

管理コンテナを削除する時にkubeadmが止まる

Dockerが停止して、Kubernetesで管理されているコンテナを削除しないと、以下のようなことが起こる可能性があります:

sudo kubeadm reset
[preflight] Running pre-flight checks
[reset] Stopping the kubelet service
[reset] Unmounting mounted directories in "/var/lib/kubelet"
[reset] Removing kubernetes-managed containers
(block)

考えられる解決策は、Dockerサービスを再起動してからkubeadm resetを再実行することです:

sudo systemctl restart docker.service
sudo kubeadm reset

dockerのログを調べるのも有効な場合があります:

journalctl -u docker

Podの状態がRunContainerErrorCrashLoopBackOff、またはErrorとなる

kubeadm initの直後には、これらの状態ではPodは存在しないはずです。

  • kubeadm init直後 にこれらの状態のいずれかにPodがある場合は、kubeadmのリポジトリにIssueを立ててください。ネットワークソリューションをデプロイするまではcoredns(またはkube-dns)はPending状態でなければなりません。
  • ネットワークソリューションをデプロイしてもcoredns(またはkube-dns)に何も起こらない場合にRunContainerErrorCrashLoopBackOffError`の状態でPodが表示された場合は、インストールしたPodネットワークソリューションが壊れている可能性が高いです。より多くのRBACの特権を付与するか、新しいバージョンを使用する必要があるかもしれません。PodネットワークプロバイダのイシュートラッカーにIssueを出して、そこで問題をトリアージしてください。
  • 1.12.1よりも古いバージョンのDockerをインストールした場合は、systemddockerdを起動する際にMountFlags=slaveオプションを削除してdockerを再起動してください。マウントフラグは/usr/lib/systemd/system/docker.serviceで確認できます。MountFlagsはKubernetesがマウントしたボリュームに干渉し、PodsをCrashLoopBackOff状態にすることがあります。このエラーは、Kubernetesがvar/run/secrets/kubernetes.io/serviceaccountファイルを見つけられない場合に発生します。

coredns(もしくはkube-dns)がPending状態でスタックする

kubeadmはネットワークプロバイダに依存しないため、管理者は選択したPodネットワークソリューションをインストールをする必要があります。CoreDNSを完全にデプロイする前にPodネットワークをインストールする必要があります。したがって、ネットワークがセットアップされる前の Pending状態になります。

HostPortサービスが動かない

HostPortHostIPの機能は、ご使用のPodネットワークプロバイダによって利用可能です。Podネットワークソリューションの作者に連絡して、HostPortHostIP機能が利用可能かどうかを確認してください。

Calico、Canal、FlannelのCNIプロバイダは、HostPortをサポートしていることが確認されています。

詳細については、[CNI portmap documentation] (https://github.com/containernetworking/plugins/blob/master/plugins/meta/portmap/README.md) を参照してください。

ネットワークプロバイダが portmap CNI プラグインをサポートしていない場合は、NodePortサービスを使用するか、HostNetwork=trueを使用してください。

サービスIP経由でPodにアクセスすることができない

  • 多くのネットワークアドオンは、PodがサービスIPを介して自分自身にアクセスできるようにするヘアピンモードを有効にしていません。これはCNIに関連する問題です。ヘアピンモードのサポート状況については、ネットワークアドオンプロバイダにお問い合わせください。

  • VirtualBoxを使用している場合(直接またはVagrant経由)は、hostname -iがルーティング可能なIPアドレスを返すことを確認する必要があります。デフォルトでは、最初のインターフェースはルーティング可能でないホスト専用のネットワークに接続されています。これを回避するには/etc/hostsを修正する必要があります。例としてはこのVagrantfileを参照してください。

TLS証明書のエラー

以下のエラーは、証明書の不一致の可能性を示しています。

# kubectl get pods
Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
  • HOME/.kube/configファイルに有効な証明書が含まれていることを確認し、必要に応じて証明書を再生成します。kubeconfigファイル内の証明書はbase64でエンコードされています。証明書をデコードするにはbase64 --decodeコマンドを、証明書情報を表示するにはopenssl x509 -text -nooutコマンドを用いてください。

  • 環境変数KUBECONFIGの設定を解除するには以下のコマンドを実行するか:

    unset KUBECONFIG
    

    設定をデフォルトのKUBECONFIGの場所に設定します:

    export KUBECONFIG=/etc/kubernetes/admin.conf
    
  • もう一つの回避策は、既存のkubeconfigを"admin"ユーザに上書きすることです:

    mv  $HOME/.kube $HOME/.kube.bak
    mkdir $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    

Vagrant内でPodネットワークとしてflannelを使用する時のデフォルトNIC

以下のエラーは、Podネットワークに何か問題があったことを示している可能性を示しています:

Error from server (NotFound): the server could not find the requested resource
  • Vagrant内のPodネットワークとしてflannelを使用している場合は、flannelのデフォルトのインターフェース名を指定する必要があります。

    Vagrantは通常、2つのインターフェースを全てのVMに割り当てます。1つ目は全てのホストにIPアドレス10.0.2.15が割り当てられており、NATされる外部トラフィックのためのものです。

    これは、ホストの最初のインターフェースをデフォルトにしているflannelの問題につながるかもしれません。これは、すべてのホストが同じパブリックIPアドレスを持っていると考えます。これを防ぐには、2番目のインターフェースが選択されるように --iface eth1フラグをflannelに渡してください。

公開されていないIPがコンテナに使われている

状況によっては、kubectl logskubectl runコマンドが以下のようなエラーを返すことがあります:

Error from server: Get https://10.19.0.41:10250/containerLogs/default/mysql-ddc65b868-glc5m/mysql: dial tcp 10.19.0.41:10250: getsockopt: no route to host
  • これには、おそらくマシンプロバイダのポリシーによって、一見同じサブネット上の他のIPと通信できないIPをKubernetesが使用している可能性があります。

  • DigitalOceanはパブリックIPとプライベートIPをeth0に割り当てていますが、kubeletはパブリックIPではなく、ノードのInternalIPとして後者を選択します。

    ifconfigではエイリアスIPアドレスが表示されないため、ifconfigの代わりにip addr showを使用してこのシナリオをチェックしてください。あるいは、DigitalOcean専用のAPIエンドポイントを使用して、ドロップレットからアンカーIPを取得することもできます:

    curl http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address
    

    回避策としては、--node-ipを使ってどのIPを使うかをkubeletに伝えることです。DigitalOceanを使用する場合、オプションのプライベートネットワークを使用したい場合は、パブリックIP(eth0に割り当てられている)かプライベートIP(eth1に割り当てられている)のどちらかを指定します。これにはkubeadm NodeRegistrationOptions構造体の KubeletExtraArgsセクション が利用できます。

    kubeletを再起動してください:

    systemctl daemon-reload
    systemctl restart kubelet
    

corednsのPodがCrashLoopBackOffもしくはError状態になる

SELinuxを実行しているノードで古いバージョンのDockerを使用している場合、coredns Podが起動しないということが起きるかもしれません。この問題を解決するには、以下のオプションのいずれかを試してみてください:

kubectl -n kube-system get deployment coredns -o yaml | \
  sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | \
  kubectl apply -f -

CoreDNSにCrashLoopBackOffが発生する別の原因は、KubernetesにデプロイされたCoreDNS Podがループを検出したときに発生します。CoreDNSがループを検出して終了するたびに、KubernetesがCoreDNS Podを再起動しようとするのを避けるために、いくつかの回避策が用意されています。

etcdのpodが継続的に再起動する

以下のエラーが発生した場合は:

rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:247: starting container process caused "process_linux.go:110: decoding init error from pipe caused \"read parent: connection reset by peer\""

この問題は、CentOS 7をDocker 1.13.1.84で実行した場合に表示されます。このバージョンのDockerでは、kubeletがetcdコンテナに実行されないようにすることができます。

この問題を回避するには、以下のいずれかのオプションを選択します:

  • 1.13.1-75のような以前のバージョンのDockerにロールバックする
yum downgrade docker-1.13.1-75.git8633870.el7.centos.x86_64 docker-client-1.13.1-75.git8633870.el7.centos.x86_64 docker-common-1.13.1-75.git8633870.el7.centos.x86_64
  • 18.06のような最新の推奨バージョンをインストールする:
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce-18.06.1.ce-3.el7.x86_64

コンマで区切られた値のリストを--component-extra-argsフラグ内の引数に渡すことができない

-component-extra-argsのようなkubeadm initフラグを使うと、kube-apiserverのようなコントロールプレーンコンポーネントにカスタム引数を渡すことができます。しかし、このメカニズムは値の解析に使われる基本的な型 (mapStringString) のために制限されています。

もし、--apiserver-extra-args "enable-admission plugins=LimitRanger,NamespaceExists"のようにカンマで区切られた複数の値をサポートする引数を渡した場合、このフラグはflag: malformed pair, expect string=stringで失敗します。これは--apiserver-extra-argsの引数リストがkey=valueのペアを期待しており、この場合NamespacesExistsは値を欠いたキーとみなされるためです。

別の方法として、key=valueのペアを以下のように分離してみることもできます: --apiserver-extra-args "enable-admission-plugins=LimitRanger,enable-admission-plugins=NamespaceExists"しかし、この場合は、キーenable-admission-pluginsNamespaceExistsの値しか持ちません。既知の回避策としては、kubeadm設定ファイルを使用することが挙げられます。

cloud-controller-managerによってノードが初期化される前にkube-proxyがスケジューリングされる

クラウドプロバイダのシナリオでは、クラウドコントローラーマネージャがノードアドレスを初期化する前に、kube-proxyが新しいワーカーノードでスケジューリングされてしまうことがあります。これにより、kube-proxyがノードのIPアドレスを正しく拾えず、ロードバランサを管理するプロキシ機能に悪影響を及ぼします。

kube-proxy Podsでは以下のようなエラーが発生します:

server.go:610] Failed to retrieve node IP: host IP unknown; known addresses: []
proxier.go:340] invalid nodeIP, initializing kube-proxy with 127.0.0.1 as nodeIP

既知の解決策は、初期のガード条件が緩和されるまで他のノードから離しておき、条件に関係なくコントロールプレーンノード上でスケジューリングできるように、キューブプロキシDaemonSetにパッチを当てることです:

kubectl -n kube-system patch ds kube-proxy -p='{ "spec": { "template": { "spec": { "tolerations": [ { "key": "CriticalAddonsOnly", "operator": "Exists" }, { "effect": "NoSchedule", "key": "node-role.kubernetes.io/master" } ] } } } }'

Tこの問題のトラッキング問題はこちら

kubeadmの設定をマーシャリングする際、NodeRegistration.Taintsフィールドが省略される

注意: このIssueは、kubeadmタイプをマーシャルするツール(YAML設定ファイルなど)にのみ適用されます。これはkubeadm API v1beta2で修正される予定です。

デフォルトでは、kubeadmはコントロールプレーンノードにnode-role.kubernetes.io/master:NoScheduleのテイントを適用します。kubeadmがコントロールプレーンノードに影響を与えないようにし、InitConfiguration.NodeRegistration.Taintsを空のスライスに設定すると、マーシャリング時にこのフィールドは省略されます。フィールドが省略された場合、kubeadmはデフォルトのテイントを適用します。

少なくとも2つの回避策があります:

  1. 空のスライスの代わりにnode-role.kubernetes.io/master:PreferNoScheduleテイントを使用します。他のノードに容量がない限り、Podsはマスター上でスケジュールされます

  2. kubeadm init終了後のテイントの除去:

kubectl taint nodes NODE_NAME node-role.kubernetes.io/master:NoSchedule-

ノード{#usr-mounted-read-only}に/usrが読み取り専用でマウントされる

Fedora CoreOSなどのLinuxディストリビューションでは、ディレクトリ/usrが読み取り専用のファイルシステムとしてマウントされます。 flex-volumeサポートでは、kubeletやkube-controller-managerのようなKubernetesコンポーネントはデフォルトで/usr/libexec/kubernetes/kubelet-plugins/volume/exec/のパスを使用していますが、この機能を動作させるためにはflex-volumeディレクトリは 書き込み可能 な状態でなければなりません。

この問題を回避するには、kubeadm設定ファイルを使用してflex-volumeディレクトリを設定します。

プライマリコントロールプレーンノード(kubeadm initで作成されたもの)上で、--configで以下のファイルを渡します:

apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
nodeRegistration:
  kubeletExtraArgs:
    volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
controllerManager:
  extraArgs:
    flex-volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

ノードをジョインするには:

apiVersion: kubeadm.k8s.io/v1beta2
kind: JoinConfiguration
nodeRegistration:
  kubeletExtraArgs:
    volume-plugin-dir: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

あるいは、/usrマウントを書き込み可能にするために /etc/fstabを変更することもできますが、これはLinuxディストリビューションの設計原理を変更していることに注意してください。

kubeadm upgrade plancontext deadline exceededエラーメッセージを表示する

このエラーメッセージは、外部etcdを実行している場合にkubeadmでKubernetesクラスターをアップグレードする際に表示されます。これは致命的なバグではなく、古いバージョンのkubeadmが外部etcdクラスターのバージョンチェックを行うために発生します。kubeadm upgrade apply ...で進めることができます。

この問題はバージョン1.19で修正されます。

2.2.2.1.3 - kubeadmを使用したクラスターの作成

ベストプラクティスに準拠した実用最小限のKubernetesクラスターを作成します。実際、kubeadmを使用すれば、Kubernetes Conformance testsに通るクラスターをセットアップすることができます。kubeadmは、ブートストラップトークンやクラスターのアップグレードなどのその他のクラスターのライフサイクルの機能もサポートします。

kubeadmツールは、次のようなときに適しています。

  • 新しいユーザーが初めてKubernetesを試すためのシンプルな方法が必要なとき。
  • 既存のユーザーがクラスターのセットアップを自動化し、アプリケーションをテストする方法が必要なとき。
  • より大きなスコープで、他のエコシステムやインストーラーツールのビルディングブロックが必要なとき。

kubeadmは、ラップトップ、クラウドのサーバー群、Raspberry Piなどの様々なマシンにインストールして使えます。クラウドとオンプレミスのどちらにデプロイする場合でも、kubeadmはAnsibleやTerraformなどのプロビジョニングシステムに統合できます。

始める前に

このガイドを進めるには、以下の環境が必要です。

  • UbuntuやCentOSなど、deb/rpmパッケージと互換性のあるLinux OSが動作している1台以上のマシンがあること。
  • マシンごとに2GiB以上のRAMが搭載されていること。それ以下の場合、アプリ実行用のメモリーがほとんど残りません。
  • コントロールプレーンノードとして使用するマシンには、最低でも2CPU以上あること。
  • クラスター内の全マシン間に完全なネットワーク接続があること。パブリックネットワークとプライベートネットワークのいずれでも使えます。

また、新しいクラスターで使いたいKubernetesのバージョンをデプロイできるバージョンのkubeadmを使用する必要もあります。

Kubernetesのバージョンとバージョンスキューポリシーは、kubeadmにもKubernetes全体と同じように当てはまります。Kubernetesとkubeadmがサポートするバージョンを理解するには、上記のポリシーを確認してください。このページは、Kubernetes v1.30向けに書かれています。

kubeadmツールの全体の機能の状態は、一般利用可能(GA)です。一部のサブ機能はまだ活発に開発が行われています。クラスター作成の実装は、ツールの進化に伴ってわずかに変わるかもしれませんが、全体の実装は非常に安定しているはずです。

目的

  • シングルコントロールプレーンのKubernetesクラスターをインストールする
  • クラスター上にPodネットワークをインストールして、Podがお互いに通信できるようにする

手順

ホストへのkubeadmのインストール

kubeadmのインストール」を読んでください。

コントロールプレーンノードの初期化

コントロールプレーンノードとは、etcd(クラスターのデータベース)やAPIサーバー(kubectlコマンドラインツールが通信する相手)などのコントロールプレーンのコンポーネントが実行されるマシンです。

  1. (推奨)シングルコントロールプレーンのkubeadmクラスターを高可用性クラスターにアップグレードする予定がある場合、--control-plane-endpointを指定して、すべてのコントロールプレーンノードとエンドポイントを共有する必要があります。エンドポイントにはDNSネームやロードバランサーのIPアドレスが使用できます。
  2. Podネットワークアドオンを選んで、kubeadm initに引数を渡す必要があるかどうか確認してください。選んだサードパーティーのプロバイダーによっては、--pod-network-cidrをプロバイダー固有の値に設定する必要がある場合があります。詳しくは、Podネットワークアドオンのインストールを参照してください。
  3. (オプション)バージョン1.14から、kubeadmはよく知られたドメインソケットのパスリストを用いて、Linux上のコンテナランタイムの検出を試みます。異なるコンテナランタイムを使用する場合やプロビジョニングするノードに2つ以上のランタイムがインストールされている場合、kubeadm init--cri-socket引数を指定してください。詳しくは、ランタイムのインストールを読んでください。
  4. (オプション)明示的に指定しない限り、kubeadmはデフォルトゲートウェイに関連付けられたネットワークインターフェースを使用して、この特定のコントロールプレーンノードのAPIサーバーのadvertise addressを設定します。異なるネットワークインターフェースを使用するには、kubeadm init--apiserver-advertise-address=<ip-address>引数を指定してください。IPv6アドレスを使用するIPv6 Kubernetesクラスターをデプロイするには、たとえば--apiserver-advertise-address=fd00::101のように、IPv6アドレスを指定する必要があります。
  5. (オプション)kubeadm initを実行する前にkubeadm config images pullを実行して、gcr.ioコンテナイメージレジストリに接続できるかどうかを確認します。

コントロールプレーンノードを初期化するには、次のコマンドを実行します。

kubeadm init <args>

apiserver-advertise-addressとControlPlaneEndpointに関する検討

--apiserver-advertise-addressは、この特定のコントロールプレーンノードのAPIサーバーへのadvertise addressを設定するために使えますが、--control-plane-endpointは、すべてのコントロールプレーンノード共有のエンドポイントを設定するために使えます。

--control-plane-endpointはIPアドレスと、IPアドレスへマッピングできるDNS名を使用できます。利用可能なソリューションをそうしたマッピングの観点から評価するには、ネットワーク管理者に相談してください。

以下にマッピングの例を示します。

192.168.0.102 cluster-endpoint

ここでは、192.168.0.102がこのノードのIPアドレスであり、cluster-endpointがこのIPアドレスへとマッピングされるカスタムDNSネームです。このように設定することで、--control-plane-endpoint=cluster-endpointkubeadm initに渡せるようになり、kubeadm joinにも同じDNSネームを渡せます。後でcluster-endpointを修正して、高可用性が必要なシナリオでロードバランサーのアドレスを指すようにすることができます。

kubeadmでは、--control-plane-endpointを渡さずに構築したシングルコントロールプレーンのクラスターを高可用性クラスターに切り替えることはサポートされていません。

詳細な情報

kubeadm initの引数のより詳細な情報は、kubeadmリファレンスガイドを参照してください。

設定オプションの全リストは、設定ファイルのドキュメントで確認できます。

コントロールプレーンコンポーネントやetcdサーバーのliveness probeへのオプションのIPv6の割り当てなど、コントロールプレーンのコンポーネントをカスタマイズしたい場合は、カスタムの引数に示されている方法で各コンポーネントに追加の引数を与えてください。

kubeadm initを再び実行する場合は、初めにクラスターの破壊を行う必要があります。

もし異なるアーキテクチャのノードをクラスターにjoinさせたい場合は、デプロイしたDaemonSetがそのアーキテクチャ向けのコンテナイメージをサポートしているか確認してください。

初めにkubeadm initは、マシンがKubernetesを実行する準備ができているかを確認する、一連の事前チェックを行います。これらの事前チェックはエラー発生時には警告を表示して終了します。次に、kubeadm initはクラスターのコントロールプレーンのコンポーネントをダウンロードしてインストールします。これには数分掛かるかもしれません。出力は次のようになります。

[init] Using Kubernetes version: vX.Y.Z
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [kubeadm-cp localhost] and IPs [10.138.0.4 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [kubeadm-cp localhost] and IPs [10.138.0.4 127.0.0.1 ::1]
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubeadm-cp kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.138.0.4]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 31.501735 seconds
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-X.Y" in namespace kube-system with the configuration for the kubelets in the cluster
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "kubeadm-cp" as an annotation
[mark-control-plane] Marking the node kubeadm-cp as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node kubeadm-cp as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: <token>
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstraptoken] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a Pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  /ja/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join <control-plane-host>:<control-plane-port> --token <token> --discovery-token-ca-cert-hash sha256:<hash>

kubectlをroot以外のユーザーでも実行できるようにするには、次のコマンドを実行します。これらのコマンドは、kubectl initの出力の中にも書かれています。

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

あなたがrootユーザーである場合は、代わりに次のコマンドを実行します。

export KUBECONFIG=/etc/kubernetes/admin.conf

kubeadm initが出力したkubeadm joinコマンドをメモしておいてください。クラスターにノードを追加するために、このコマンドが必要になります。

トークンは、コントロールプレーンノードと追加ノードの間の相互認証に使用します。ここに含まれるトークンには秘密の情報が含まれます。このトークンを知っていれば、誰でもクラスターに認証済みノードを追加できてしまうため、取り扱いには注意してください。kubeadm tokenコマンドを使用すると、これらのトークンの一覧、作成、削除ができます。詳しくはkubeadmリファレンスガイドを読んでください。

Podネットワークアドオンのインストール

CNIを使用するKubernetes Podネットワークを提供する外部のプロジェクトがいくつかあります。一部のプロジェクトでは、ネットワークポリシーもサポートしています。

Kubernetesのネットワークモデルを実装したアドオンの一覧も確認してください。

Podネットワークアドオンをインストールするには、コントロールプレーンノード上またはkubeconfigクレデンシャルを持っているノード上で、次のコマンドを実行します。

kubectl apply -f <add-on.yaml>

インストールできるPodネットワークは、クラスターごとに1つだけです。

Podネットワークがインストールされたら、kubectl get pods --all-namespacesの出力結果でCoreDNS PodがRunning状態であることをチェックすることで、ネットワークが動作していることを確認できます。そして、一度CoreDNS Podが動作すれば、続けてノードを追加できます。

もしネットワークやCoreDNSがRunning状態にならない場合は、kubeadmトラブルシューティングガイドをチェックしてください。

コントロールプレーンノードの隔離

デフォルトでは、セキュリティ上の理由により、クラスターはコントロールプレーンノードにPodをスケジューリングしません。たとえば、開発用のKubernetesシングルマシンのクラスターなどで、Podをコントロールプレーンノードにスケジューリングしたい場合は、次のコマンドを実行します。

kubectl taint nodes --all node-role.kubernetes.io/master-

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

node "test-01" untainted
taint "node-role.kubernetes.io/master:" not found
taint "node-role.kubernetes.io/master:" not found

このコマンドは、コントロールプレーンノードを含むすべてのノードからnode-role.kubernetes.io/mastertaintを削除します。その結果、スケジューラーはどこにでもPodをスケジューリングできるようになります。

ノードの追加

ノードは、ワークロード(コンテナやPodなど)が実行される場所です。新しいノードをクラスターに追加するためには、各マシンに対して、以下の手順を実行してください。

  • マシンへSSHする
  • rootになる(例: sudo su -)
  • kubeadm init実行時に出力されたコマンドを実行する。たとえば、次のようなコマンドです。
kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

トークンがわからない場合は、コントロールプレーンノードで次のコマンドを実行すると取得できます。

kubeadm token list

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

TOKEN                    TTL  EXPIRES              USAGES           DESCRIPTION            EXTRA GROUPS
8ewj1p.9r9hcjoqgajrj4gi  23h  2018-06-12T02:51:28Z authentication,  The default bootstrap  system:
                                                   signing          token generated by     bootstrappers:
                                                                    'kubeadm init'.        kubeadm:
                                                                                           default-node-token

デフォルトでは、トークンは24時間後に有効期限が切れます。もし現在のトークンの有効期限が切れた後にクラスターにノードを参加させたい場合は、コントロールプレーンノードで次のコマンドを実行することで、新しいトークンを生成できます。

kubeadm token create

このコマンドの出力は次のようになります。

5didvk.d09sbcov8ph2amjw

もし--discovery-token-ca-cert-hashの値がわからない場合は、コントロールプレーンノード上で次のコマンドチェーンを実行することで取得できます。

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
   openssl dgst -sha256 -hex | sed 's/^.* //'

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

8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78

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

[preflight] Running pre-flight checks

... (joinワークフローのログ出力) ...

Node join complete:
* Certificate signing request sent to control-plane and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on control-plane to see this machine join.

数秒後、コントロールプレーンノード上でkubectl get nodesを実行すると、出力内にこのノードが表示されるはずです。

(オプション)コントロールプレーンノード以外のマシンからのクラスター操作

他のコンピューター(例: ラップトップ)上のkubectlがクラスターと通信できるようにするためには、次のようにして、administratorのkubeconfigファイルをコントロールプレーンノードからそのコンピューター上にコピーする必要があります。

scp root@<control-plane-host>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf get nodes

(オプション)APIサーバーをlocalhostへプロキシする

クラスターの外部からAPIサーバーに接続したいときは、次のようにkubectl proxyコマンドが使えます。

scp root@<control-plane-host>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf proxy

これで、ローカルのhttp://localhost:8001/api/v1からAPIサーバーにアクセスできるようになります。

クリーンアップ

テストのためにクラスターに破棄可能なサーバーを使用した場合、サーバーのスイッチをオフにすれば、以降のクリーンアップの作業は必要ありません。クラスターのローカルの設定を削除するには、kubectl config delete-clusterを実行します。

しかし、もしよりきれいにクラスターのプロビジョンをもとに戻したい場合は、初めにノードのdrainを行い、ノードが空になっていることを確認した後、ノードの設定を削除する必要があります。

ノードの削除

適切なクレデンシャルを使用してコントロールプレーンノードに削除することを伝えます。次のコマンドを実行してください。

kubectl drain <node name> --delete-local-data --force --ignore-daemonsets

ノードが削除される前に、kubeadmによってインストールされた状態をリセットします。

kubeadm reset

リセットプロセスでは、iptablesのルールやIPVS tablesのリセットやクリーンアップは行われません。iptablesをリセットしたい場合は、次のように手動でコマンドを実行する必要があります。

iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X

IPVS tablesをリセットしたい場合は、次のコマンドを実行する必要があります。

ipvsadm -C

ノードを削除します。

kubectl delete node <node name>

クラスターのセットアップを最初から始めたいときは、kubeadm initkubeadm joinを適切な引数を付けて実行すればいいだけです。

コントロールプレーンのクリーンアップ

コントロールホスト上でkubeadm resetを実行すると、ベストエフォートでのクリーンアップが実行できます。

このサブコマンドとオプションに関するより詳しい情報は、kubeadm resetリファレンスドキュメントを読んでください。

次の手順

フィードバック

バージョン互換ポリシー

バージョンv1.30のkubeadmツールは、バージョンv1.30またはv1.29のコントロールプレーンを持つクラスターをデプロイできます。また、バージョンv1.30のkubeadmは、バージョンv1.29のkubeadmで構築されたクラスターをアップグレートできます。

未来を見ることはできないため、kubeadm CLI v1.30はv1.31をデプロイできないかもしれません。

例: kubeadm v1.8は、v1.7とv1.8のクラスターをデプロイでき、v1.7のkubeadmで構築されたクラスターをv1.8にアップグレートできます。

kubeletとコントロールプレーンの間や、他のKubernetesコンポーネント間のバージョンの差異に関する詳しい情報は、以下の資料を確認してください。

制限事項

クラスターのレジリエンス

ここで作られたクラスターは、1つのコントロールプレーンノードと、その上で動作する1つのetcdデータベースしか持ちません。つまり、コントロールプレーンノードが故障した場合、クラスターのデータは失われ、クラスターを最初から作り直す必要があるかもしれないということです。

対処方法:

プラットフォームの互換性

kubeadmのdeb/rpmパッケージおよびバイナリは、multi-platform proposalに従い、amd64、arm(32ビット)、arm64、ppc64le、およびs390x向けにビルドされています。

マルチプラットフォームのコントロールプレーンおよびアドオン用のコンテナイメージも、v1.12からサポートされています。

すべてのプラットフォーム向けのソリューションを提供しているネットワークプロバイダーは一部のみです。それぞれのプロバイダーが選択したプラットフォームをサポートしているかどうかを確認するには、前述のネットワークプロバイダーのリストを参照してください。

トラブルシューティング

kubeadmに関する問題が起きたときは、トラブルシューティングドキュメントを確認してください。

2.2.2.1.4 - kubeadmを使ったコントロールプレーンの設定のカスタマイズ

FEATURE STATE: Kubernetes 1.12 [stable]

kubeadmのClusterConfigurationオブジェクトはAPIServer、ControllerManager、およびSchedulerのようなコントロールプレーンの構成要素に渡されたデフォルトのフラグを上書きすることができる extraArgsの項目があります。 その構成要素は次の項目で定義されています。

  • apiServer
  • controllerManager
  • scheduler

extraArgs の項目は キー: 値 のペアです。コントロールプレーンの構成要素のフラグを上書きするには:

  1. 設定内容に適切な項目を追加
  2. フラグを追加して項目を上書き
  3. --config <任意の設定YAMLファイル>kubeadm initを実行

各設定項目のより詳細な情報はAPIリファレンスのページを参照してください。

APIServerフラグ

詳細はkube-apiserverのリファレンスドキュメントを参照してください。

使用例:

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
apiServer:
  extraArgs:
    advertise-address: 192.168.0.103
    anonymous-auth: "false"
    enable-admission-plugins: AlwaysPullImages,DefaultStorageClass
    audit-log-path: /home/johndoe/audit.log

ControllerManagerフラグ

詳細はkube-controller-managerのリファレンスドキュメントを参照してください。

使用例:

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
controllerManager:
  extraArgs:
    cluster-signing-key-file: /home/johndoe/keys/ca.key
    bind-address: 0.0.0.0
    deployment-controller-sync-period: "50"

Schedulerフラグ

詳細はkube-schedulerのリファレンスドキュメントを参照してください。

使用例:

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
scheduler:
  extraArgs:
    bind-address: 0.0.0.0
    config: /home/johndoe/schedconfig.yaml
    kubeconfig: /home/johndoe/kubeconfig.yaml

2.2.2.1.5 - 高可用性トポロジーのためのオプション

このページでは、高可用性(HA)Kubernetesクラスターのトポロジーを設定するための2つのオプションについて説明します。

HAクラスターは次の方法で設定できます。

  • 積層コントロールプレーンノードを使用する方法。こちらの場合、etcdノードはコントロールプレーンノードと同じ場所で動作します。
  • 外部のetcdノードを使用する方法。こちらの場合、etcdがコントロールプレーンとは分離されたノードで動作します。

HAクラスターをセットアップする前に、各トポロジーの利点と欠点について注意深く考慮する必要があります。

積層etcdトポロジー

積層HAクラスターは、コントロールプレーンのコンポーネントを実行する、kubeadmで管理されたノードで構成されるクラスターの上に、etcdにより提供される分散データストレージクラスターがあるようなトポロジーです。

各コントロールプレーンノードは、kube-apiserverkube-scheduler、およびkube-controller-managerを実行します。kube-apiserver はロードバランサーを用いてワーカーノードに公開されます。

各コントロールプレーンノードはローカルのetcdメンバーを作り、このetcdメンバーはそのノードのkube-apiserverとだけ通信します。ローカルのkube-controller-managerkube-schedulerのインスタンスも同様です。

このトポロジーは、同じノード上のコントロールプレーンとetcdのメンバーを結合します。外部のetcdノードを使用するクラスターよりはセットアップがシンプルで、レプリケーションの管理もシンプルです。

しかし、積層クラスターには、結合による故障のリスクがあります。1つのノードがダウンすると、etcdメンバーとコントロールプレーンのインスタンスの両方が失われ、冗長性が損なわれます。より多くのコントロールプレーンノードを追加することで、このリスクは緩和できます。

そのため、HAクラスターのためには、最低でも3台の積層コントロールプレーンノードを実行しなければなりません。

これがkubeadmのデフォルトのトポロジーです。kubeadm initkubeadm join --control-placeを実行すると、ローカルのetcdメンバーがコントロールプレーンノード上に自動的に作成されます。

積層etcdトポロジー

外部のetcdトポロジー

外部のetcdを持つHAクラスターは、コントロールプレーンコンポーネントを実行するノードで構成されるクラスターの外部に、etcdにより提供される分散データストレージクラスターがあるようなトポロジーです。

積層etcdトポロジーと同様に、外部のetcdトポロジーにおける各コントロールプレーンノードは、kube-apiserverkube-scheduler、およびkube-controller-managerのインスタンスを実行します。そして、kube-apiserverは、ロードバランサーを使用してワーカーノードに公開されます。しかし、etcdメンバーは異なるホスト上で動作しており、各etcdホストは各コントロールプレーンノードのkube-api-serverと通信します。

このトポロジーは、コントロールプレーンとetcdメンバーを疎結合にします。そのため、コントロールプレーンインスタンスまたはetcdメンバーを失うことによる影響は少なく、積層HAトポロジーほどクラスターの冗長性に影響しないHAセットアップが実現します。

しかし、このトポロジーでは積層HAトポロジーの2倍の数のホストを必要とします。このトポロジーのHAクラスターのためには、最低でもコントロールプレーンのために3台のホストが、etcdノードのために3台のホストがそれぞれ必要です。

外部のetcdトポロジー

次の項目

2.2.2.1.6 - kubeadmを使用した高可用性クラスターの作成

このページでは、kubeadmを使用して、高可用性クラスターを作成する、2つの異なるアプローチを説明します:

  • 積層コントロールプレーンノードを使う方法。こちらのアプローチは、必要なインフラストラクチャーが少ないです。etcdのメンバーと、コントロールプレーンノードは同じ場所に置かれます。
  • 外部のetcdクラスターを使う方法。こちらのアプローチには、より多くのインフラストラクチャーが必要です。コントロールプレーンノードと、etcdのメンバーは分離されます。

先へ進む前に、どちらのアプローチがアプリケーションの要件と、環境に適合するか、慎重に検討してください。こちらの比較が、それぞれの利点/欠点について概説しています。

高可用性クラスターの作成で問題が発生した場合は、kueadmのissue trackerでフィードバックを提供してください。

高可用性クラスターのアップグレードも参照してください。

始める前に

どちらの方法でも、以下のインフラストラクチャーが必要です:

  • master用に、kubeadmの最小要件を満たす3台のマシン
  • worker用に、kubeadmの最小要件を満たす3台のマシン
  • クラスター内のすべてのマシン間がフルにネットワーク接続可能であること(パブリック、もしくはプライベートネットワーク)
  • すべてのマシンにおいて、sudo権限
  • あるデバイスから、システム内のすべてのノードに対しSSH接続できること
  • kubeadmkubeletがすべてのマシンにインストールされていること。 kubectlは任意です。

外部etcdクラスターには、以下も必要です:

  • etcdメンバー用に、追加で3台のマシン

両手順における最初のステップ

kube-apiserver用にロードバランサーを作成

  1. DNSで解決される名前で、kube-apiserver用ロードバランサーを作成する。

    • クラウド環境では、コントロールプレーンノードをTCPフォワーディングロードバランサーの後ろに置かなければなりません。このロードバランサーはターゲットリストに含まれる、すべての健全なコントロールプレーンノードにトラフィックを分配します。apiserverへのヘルスチェックはkube-apiserverがリッスンするポート(デフォルト値: :6443)に対する、TCPチェックです。

    • クラウド環境では、IPアドレスを直接使うことは推奨されません。

    • ロードバランサーは、apiserverポートで、全てのコントロールプレーンノードと通信できなければなりません。また、リスニングポートに対する流入トラフィックも許可されていなければなりません。

    • ロードバランサーのアドレスは、常にkubeadmのControlPlaneEndpointのアドレスと一致することを確認してください。

    • 詳細はOptions for Software Load Balancingをご覧ください。

  2. ロードバランサーに、最初のコントロールプレーンノードを追加し、接続をテストする:

    nc -v LOAD_BALANCER_IP PORT
    
    • apiserverはまだ動いていないので、接続の拒否は想定通りです。しかし、タイムアウトしたのであれば、ロードバランサーはコントロールプレーンノードと通信できなかったことを意味します。もし、タイムアウトが起きたら、コントロールプレーンノードと通信できるように、ロードバランサーを再設定してください。
  3. 残りのコントロールプレーンノードを、ロードバランサーのターゲットグループに追加します。

積層コントロールプレーンとetcdノード

最初のコントロールプレーンノードの手順

  1. 最初のコントロールプレーンノードを初期化します:

    sudo kubeadm init --control-plane-endpoint "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT" --upload-certs
    
    • --kubernetes-versionフラグで使用するKubernetesのバージョンを設定できます。kubeadm、kubelet、kubectl、Kubernetesのバージョンを一致させることが推奨されます。
    • --control-plane-endpointフラグは、ロードバランサーのIPアドレスまたはDNS名と、ポートが設定される必要があります。
    • --upload-certsフラグは全てのコントロールプレーンノードで共有する必要がある証明書をクラスターにアップロードするために使用されます。代わりに、コントロールプレーンノード間で手動あるいは自動化ツールを使用して証明書をコピーしたい場合は、このフラグを削除し、以下の証明書の手動配布のセクションを参照してください。
    • このような出力がされます:
    ...
    You can now join any number of control-plane node by running the following command on each as a root:
        kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
    
    Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
    As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use kubeadm init phase upload-certs to reload certs afterward.
    
    Then you can join any number of worker nodes by running the following on each as root:
        kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866
    
    • この出力をテキストファイルにコピーします。あとで、他のコントロールプレーンノードとワーカーノードをクラスターに参加させる際に必要です。

    • --upload-certsフラグをkubeadm initで使用すると、プライマリコントロールプレーンの証明書が暗号化されて、kubeadm-certs Secretにアップロードされます。

    • 証明書を再アップロードして新しい復号キーを生成するには、すでにクラスターに参加しているコントロールプレーンノードで次のコマンドを使用します:

    sudo kubeadm init phase upload-certs --upload-certs
    
    • また、後でjoinで使用できるように、init中にカスタムした--certificate-keyを指定することもできます。このようなキーを生成するには、次のコマンドを使用します:
    kubeadm alpha certs certificate-key
    
  2. 使用するCNIプラグインを適用します:
    こちらの手順に従いCNIプロバイダーをインストールします。該当する場合は、kubeadmの設定で指定されたPodのCIDRに対応していることを確認してください。

    Weave Netを使用する場合の例:

    kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
    
  3. 以下のコマンドを入力し、コンポーネントのPodが起動するのを確認します:

    kubectl get pod -n kube-system -w
    

残りのコントロールプレーンノードの手順

追加のコントロールプレーンノード毎に、以下の手順を行います。

  1. kubeadm initを最初のノードで実行した際に取得したjoinコマンドを使って、新しく追加するコントロールプレーンノードでkubeadm joinを開始します。このようなコマンドになるはずです:

    sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
    
    • --control-planeフラグによって、kubeadm joinの実行は新しいコントロールプレーンを作成します。
    • -certificate-key ...を指定したキーを使って、クラスターのkubeadm-certs Secretからダウンロードされたコントロールプレーンの証明書が復号されます。

外部のetcdノード

外部のetcdノードを使ったクラスターの設定は、積層etcdの場合と似ていますが、最初にetcdを設定し、kubeadmの設定ファイルにetcdの情報を渡す必要があります。

etcdクラスターの構築

  1. こちらの手順にしたがって、etcdクラスターを構築してください。

  2. こちらの手順にしたがって、SSHを構築してください。

  3. 以下のファイルをクラスター内の任意のetcdノードから最初のコントロールプレーンノードにコピーしてください:

    export CONTROL_PLANE="ubuntu@10.0.0.7"
    scp /etc/kubernetes/pki/etcd/ca.crt "${CONTROL_PLANE}":
    scp /etc/kubernetes/pki/apiserver-etcd-client.crt "${CONTROL_PLANE}":
    scp /etc/kubernetes/pki/apiserver-etcd-client.key "${CONTROL_PLANE}":
    
    • CONTROL_PLANEの値を、最初のコントロールプレーンノードのuser@hostで置き換えます。

最初のコントロールプレーンノードの構築

  1. 以下の内容で、kubeadm-config.yamlという名前の設定ファイルを作成します:

    apiVersion: kubeadm.k8s.io/v1beta2
    kind: ClusterConfiguration
    kubernetesVersion: stable
    controlPlaneEndpoint: "LOAD_BALANCER_DNS:LOAD_BALANCER_PORT"
    etcd:
        external:
            endpoints:
            - https://ETCD_0_IP:2379
            - https://ETCD_1_IP:2379
            - https://ETCD_2_IP:2379
            caFile: /etc/kubernetes/pki/etcd/ca.crt
            certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
            keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
    
    • テンプレート内の以下の変数を、クラスターに合わせて適切な値に置き換えます:

      • LOAD_BALANCER_DNS
      • LOAD_BALANCER_PORT
      • ETCD_0_IP
      • ETCD_1_IP
      • ETCD_2_IP

以下の手順は、積層etcdの構築と同様です。

  1. sudo kubeadm init --config kubeadm-config.yaml --upload-certsをこのノードで実行します。

  2. 表示されたjoinコマンドを、あとで使うためにテキストファイルに書き込みます。

  3. 使用するCNIプラグインを適用します。以下はWeave CNIの場合です:

    kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
    

残りのコントロールプレーンノードの手順

手順は、積層etcd構築の場合と同じです:

  • 最初のコントロールプレーンノードが完全に初期化されているのを確認します。
  • テキストファイルに保存したjoinコマンドを使って、それぞれのコントロールプレーンノードをクラスターへ参加させます。コントロールプレーンノードは1台ずつクラスターへ参加させるのを推奨します。
  • --certificate-keyで指定する復号キーは、デフォルトで2時間で期限切れになることを忘れないでください。

コントロールプレーン起動後の共通タスク

workerのインストール

kubeadm initコマンドから返されたコマンドを利用して、workerノードをクラスターに参加させることが可能です。

sudo kubeadm join 192.168.0.200:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

証明書の手動配布

--upload-certsフラグを指定してkubeadm initを実行しない場合、プライマリコントロールプレーンノードから他のコントロールプレーンノードへ証明書を手動でコピーする必要があります。

コピーを行うには多くの方法があります。次の例ではsshscpを使用しています。

1台のマシンから全てのノードをコントロールしたいのであれば、SSHが必要です。

  1. システム内の全ての他のノードにアクセスできるメインデバイスで、ssh-agentを有効にします

    eval $(ssh-agent)
    
  2. SSHの秘密鍵を、セッションに追加します:

    ssh-add ~/.ssh/path_to_private_key
    
  3. 正常に接続できることを確認するために、ノード間でSSHします。

    • ノードにSSHする際は、必ず-Aフラグをつけます:

      ssh -A 10.0.0.7
      
    • ノードでsudoするときは、SSHフォワーディングが動くように、環境変数を引き継ぎます:

      sudo -E -s
      
  4. 全てのノードでSSHを設定したら、kubeadm initを実行した後、最初のコントロールノードプレーンノードで次のスクリプトを実行します。このスクリプトは、最初のコントロールプレーンノードから残りのコントロールプレーンノードへ証明書ファイルをコピーします:

    次の例の、CONTROL_PLANE_IPSを他のコントロールプレーンノードのIPアドレスに置き換えます。

    USER=ubuntu # 環境に合わせる
    CONTROL_PLANE_IPS="10.0.0.7 10.0.0.8"
    for host in ${CONTROL_PLANE_IPS}; do
        scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:
        scp /etc/kubernetes/pki/ca.key "${USER}"@$host:
        scp /etc/kubernetes/pki/sa.key "${USER}"@$host:
        scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:
        scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:
        scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:
        scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt
        # 外部のetcdノード使用時はこちらのコマンドを実行
        scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key
    done
    
  5. 次に、クラスターに参加させる残りの各コントロールプレーンノードでkubeadm joinを実行する前に次のスクリプトを実行する必要があります。このスクリプトは、前の手順でコピーした証明書をホームディレクトリから/etc/kubernetes/pkiへ移動します:

    USER=ubuntu # 環境に合わせる
    mkdir -p /etc/kubernetes/pki/etcd
    mv /home/${USER}/ca.crt /etc/kubernetes/pki/
    mv /home/${USER}/ca.key /etc/kubernetes/pki/
    mv /home/${USER}/sa.pub /etc/kubernetes/pki/
    mv /home/${USER}/sa.key /etc/kubernetes/pki/
    mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
    mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
    mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
    # 外部のetcdノード使用時はこちらのコマンドを実行
    mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key
    

2.2.2.1.7 - kubeadmを使用した高可用性etcdクラスターの作成

Kubeadm defaults to running a single member etcd cluster in a static pod managed by the kubelet on the control plane node. This is not a high availability setup as the etcd cluster contains only one member and cannot sustain any members becoming unavailable. This task walks through the process of creating a high availability etcd cluster of three members that can be used as an external etcd when using kubeadm to set up a kubernetes cluster.

始める前に

  • Three hosts that can talk to each other over ports 2379 and 2380. This document assumes these default ports. However, they are configurable through the kubeadm config file.
  • Each host must have docker, kubelet, and kubeadm installed.
  • Each host should have access to the Kubernetes container image registry (registry.k8s.io) or list/pull the required etcd image using kubeadm config images list/pull. This guide will setup etcd instances as static pods managed by a kubelet.
  • Some infrastructure to copy files between hosts. For example ssh and scp can satisfy this requirement.

クラスターの構築

The general approach is to generate all certs on one node and only distribute the necessary files to the other nodes.

  1. Configure the kubelet to be a service manager for etcd.

    Since etcd was created first, you must override the service priority by creating a new unit file that has higher precedence than the kubeadm-provided kubelet unit file.

    cat << EOF > /etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf
    [Service]
    ExecStart=
    #  Replace "systemd" with the cgroup driver of your container runtime. The default value in the kubelet is "cgroupfs".
    ExecStart=/usr/bin/kubelet --address=127.0.0.1 --pod-manifest-path=/etc/kubernetes/manifests --cgroup-driver=systemd
    Restart=always
    EOF
    
    systemctl daemon-reload
    systemctl restart kubelet
    
  2. Create configuration files for kubeadm.

    Generate one kubeadm configuration file for each host that will have an etcd member running on it using the following script.

    # Update HOST0, HOST1, and HOST2 with the IPs or resolvable names of your hosts
    export HOST0=10.0.0.6
    export HOST1=10.0.0.7
    export HOST2=10.0.0.8
    
    # Create temp directories to store files that will end up on other hosts.
    mkdir -p /tmp/${HOST0}/ /tmp/${HOST1}/ /tmp/${HOST2}/
    
    ETCDHOSTS=(${HOST0} ${HOST1} ${HOST2})
    NAMES=("infra0" "infra1" "infra2")
    
    for i in "${!ETCDHOSTS[@]}"; do
    HOST=${ETCDHOSTS[$i]}
    NAME=${NAMES[$i]}
    cat << EOF > /tmp/${HOST}/kubeadmcfg.yaml
    apiVersion: "kubeadm.k8s.io/v1beta2"
    kind: ClusterConfiguration
    etcd:
        local:
            serverCertSANs:
            - "${HOST}"
            peerCertSANs:
            - "${HOST}"
            extraArgs:
                initial-cluster: ${NAMES[0]}=https://${ETCDHOSTS[0]}:2380,${NAMES[1]}=https://${ETCDHOSTS[1]}:2380,${NAMES[2]}=https://${ETCDHOSTS[2]}:2380
                initial-cluster-state: new
                name: ${NAME}
                listen-peer-urls: https://${HOST}:2380
                listen-client-urls: https://${HOST}:2379
                advertise-client-urls: https://${HOST}:2379
                initial-advertise-peer-urls: https://${HOST}:2380
    EOF
    done
    
  3. Generate the certificate authority

    If you already have a CA then the only action that is copying the CA's crt and key file to /etc/kubernetes/pki/etcd/ca.crt and /etc/kubernetes/pki/etcd/ca.key. After those files have been copied, proceed to the next step, "Create certificates for each member".

    If you do not already have a CA then run this command on $HOST0 (where you generated the configuration files for kubeadm).

    kubeadm init phase certs etcd-ca
    

    This creates two files

    • /etc/kubernetes/pki/etcd/ca.crt
    • /etc/kubernetes/pki/etcd/ca.key
  4. Create certificates for each member

    kubeadm init phase certs etcd-server --config=/tmp/${HOST2}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-peer --config=/tmp/${HOST2}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
    kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST2}/kubeadmcfg.yaml
    cp -R /etc/kubernetes/pki /tmp/${HOST2}/
    # cleanup non-reusable certificates
    find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete
    
    kubeadm init phase certs etcd-server --config=/tmp/${HOST1}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-peer --config=/tmp/${HOST1}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
    kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST1}/kubeadmcfg.yaml
    cp -R /etc/kubernetes/pki /tmp/${HOST1}/
    find /etc/kubernetes/pki -not -name ca.crt -not -name ca.key -type f -delete
    
    kubeadm init phase certs etcd-server --config=/tmp/${HOST0}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-peer --config=/tmp/${HOST0}/kubeadmcfg.yaml
    kubeadm init phase certs etcd-healthcheck-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
    kubeadm init phase certs apiserver-etcd-client --config=/tmp/${HOST0}/kubeadmcfg.yaml
    # No need to move the certs because they are for HOST0
    
    # clean up certs that should not be copied off this host
    find /tmp/${HOST2} -name ca.key -type f -delete
    find /tmp/${HOST1} -name ca.key -type f -delete
    
  5. Copy certificates and kubeadm configs

    The certificates have been generated and now they must be moved to their respective hosts.

    USER=ubuntu
    HOST=${HOST1}
    scp -r /tmp/${HOST}/* ${USER}@${HOST}:
    ssh ${USER}@${HOST}
    USER@HOST $ sudo -Es
    root@HOST $ chown -R root:root pki
    root@HOST $ mv pki /etc/kubernetes/
    
  6. Ensure all expected files exist

    The complete list of required files on $HOST0 is:

    /tmp/${HOST0}
    └── kubeadmcfg.yaml
    ---
    /etc/kubernetes/pki
    ├── apiserver-etcd-client.crt
    ├── apiserver-etcd-client.key
    └── etcd
        ├── ca.crt
        ├── ca.key
        ├── healthcheck-client.crt
        ├── healthcheck-client.key
        ├── peer.crt
        ├── peer.key
        ├── server.crt
        └── server.key
    

    On $HOST1:

    $HOME
    └── kubeadmcfg.yaml
    ---
    /etc/kubernetes/pki
    ├── apiserver-etcd-client.crt
    ├── apiserver-etcd-client.key
    └── etcd
        ├── ca.crt
        ├── healthcheck-client.crt
        ├── healthcheck-client.key
        ├── peer.crt
        ├── peer.key
        ├── server.crt
        └── server.key
    

    On $HOST2

    $HOME
    └── kubeadmcfg.yaml
    ---
    /etc/kubernetes/pki
    ├── apiserver-etcd-client.crt
    ├── apiserver-etcd-client.key
    └── etcd
        ├── ca.crt
        ├── healthcheck-client.crt
        ├── healthcheck-client.key
        ├── peer.crt
        ├── peer.key
        ├── server.crt
        └── server.key
    
  7. Create the static pod manifests

    Now that the certificates and configs are in place it's time to create the manifests. On each host run the kubeadm command to generate a static manifest for etcd.

    root@HOST0 $ kubeadm init phase etcd local --config=/tmp/${HOST0}/kubeadmcfg.yaml
    root@HOST1 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
    root@HOST2 $ kubeadm init phase etcd local --config=$HOME/kubeadmcfg.yaml
    
  8. Optional: Check the cluster health

    docker run --rm -it \
    --net host \
    -v /etc/kubernetes:/etc/kubernetes registry.k8s.io/etcd:${ETCD_TAG} etcdctl \
    --cert /etc/kubernetes/pki/etcd/peer.crt \
    --key /etc/kubernetes/pki/etcd/peer.key \
    --cacert /etc/kubernetes/pki/etcd/ca.crt \
    --endpoints https://${HOST0}:2379 endpoint health --cluster
    ...
    https://[HOST0 IP]:2379 is healthy: successfully committed proposal: took = 16.283339ms
    https://[HOST1 IP]:2379 is healthy: successfully committed proposal: took = 19.44402ms
    https://[HOST2 IP]:2379 is healthy: successfully committed proposal: took = 35.926451ms
    
    • Set ${ETCD_TAG} to the version tag of your etcd image. For example 3.4.3-0. To see the etcd image and tag that kubeadm uses execute kubeadm config images list --kubernetes-version ${K8S_VERSION}, where ${K8S_VERSION} is for example v1.17.0
    • Set ${HOST0}to the IP address of the host you are testing.

次の項目

Once you have a working 3 member etcd cluster, you can continue setting up a highly available control plane using the external etcd method with kubeadm.

2.2.2.1.8 - kubeadmを使用したクラスター内の各kubeletの設定

FEATURE STATE: Kubernetes 1.11 [stable]

kubeadm CLIツールのライフサイクルは、Kubernetesクラスター内の各ノード上で稼働するデーモンであるkubeletから分離しています。kubeadm CLIツールはKubernetesを初期化またはアップグレードする際にユーザーによって実行されます。一方で、kubeletは常にバックグラウンドで稼働しています。

kubeletはデーモンのため、何らかのinitシステムやサービスマネージャーで管理する必要があります。DEBパッケージやRPMパッケージからkubeletをインストールすると、systemdはkubeletを管理するように設定されます。代わりに別のサービスマネージャーを使用することもできますが、手動で設定する必要があります。

いくつかのkubeletの設定は、クラスターに含まれる全てのkubeletで同一である必要があります。一方で、特定のマシンの異なる特性(OS、ストレージ、ネットワークなど)に対応するために、kubeletごとに設定が必要なものもあります。手動で設定を管理することも可能ですが、kubeadmは一元的な設定管理のためのKubeletConfigurationAPIを提供しています。

Kubeletの設定パターン

以下のセクションでは、kubeadmを使用したkubeletの設定パターンについて説明します。これは手動で各Nodeの設定を管理するよりも簡易に行うことができます。

各kubeletにクラスターレベルの設定を配布

kubeadm initおよびkubeadm joinコマンドを使用すると、kubeletにデフォルト値を設定することができます。興味深い例として、異なるCRIランタイムを使用したり、Serviceが使用するデフォルトのサブネットを設定したりすることができます。

Serviceが使用するデフォルトのサブネットとして10.96.0.0/12を設定する必要がある場合は、--service-cidrパラメーターを渡します。

kubeadm init --service-cidr 10.96.0.0/12

これによってServiceの仮想IPはこのサブネットから割り当てられるようになりました。また、--cluster-dnsフラグを使用し、kubeletが用いるDNSアドレスを設定する必要もあります。この設定はクラスター内の全てのマネージャーとNode上で同一である必要があります。kubeletは、kubeletのComponentConfigと呼ばれる、バージョン管理と構造化されたAPIオブジェクトを提供します。これはkubelet内のほとんどのパラメーターを設定し、その設定をクラスター内で稼働中の各kubeletへ適用することを可能にします。以下の例のように、キャメルケースのキーに値のリストとしてクラスターDNS IPアドレスなどのフラグを指定することができます。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
clusterDNS:
- 10.96.0.10

ComponentConfigの詳細については、このセクションをご覧ください

インスタンス固有の設定内容を適用

いくつかのホストでは、ハードウェア、オペレーティングシステム、ネットワーク、その他ホスト固有のパラメーターの違いのため、特定のkubeletの設定を必要とします。以下にいくつかの例を示します。

  • DNS解決ファイルへのパスは--resolv-confフラグで指定することができますが、オペレーティングシステムやsystemd-resolvedを使用するかどうかによって異なる場合があります。このパスに誤りがある場合、そのNode上でのDNS解決は失敗します。
  • クラウドプロバイダーを使用していない場合、Node APIオブジェクト.metadata.nameはデフォルトでマシンのホスト名に設定されます。異なるNode名を指定する必要がある場合には、--hostname-overrideフラグによってこの挙動を書き換えることができます。
  • 現在のところ、kubletはCRIランタイムが使用するcgroupドライバを自動で検知することができませんが、kubeletの稼働を保証するためには、--cgroup-driverの値はCRIランタイムが使用するcgroupドライバに一致していなければなりません。
  • クラスターが使用するCRIランタイムによっては、異なるフラグを指定する必要があるかもしれません。例えば、Dockerを使用している場合には、--network-plugin=cniのようなフラグを指定する必要があります。外部のランタイムを使用している場合には、--container-runtime=remoteと指定し、--container-runtime-endpoint=<path>のようにCRIエンドポイントを指定する必要があります。

これらのフラグは、systemdなどのサービスマネージャー内のkubeletの設定によって指定することができます。

kubeadmを使用したkubeletの設定

kubeadm ... --config some-config-file.yamlのように、カスタムのKubeletConfigurationAPIオブジェクトを設定ファイルを介して渡すことで、kubeadmによって起動されるkubeletに設定を反映することができます。

kubeadm config print init-defaults --component-configs KubeletConfigurationを実行することによって、この構造体の全てのデフォルト値を確認することができます。

また、各フィールドの詳細については、kubelet ComponentConfigに関するAPIリファレンスを参照してください。

kubeadm init実行時の流れ

kubeadm initを実行した場合、kubeletの設定は/var/lib/kubelet/config.yamlに格納され、クラスターのConfigMapにもアップロードされます。ConfigMapはkubelet-config-1.Xという名前で、Xは初期化するKubernetesのマイナーバージョンを表します。またこの設定ファイルは、クラスター内の全てのkubeletのために、クラスター全体設定の基準と共に/etc/kubernetes/kubelet.confにも書き込まれます。この設定ファイルは、kubeletがAPIサーバと通信するためのクライアント証明書を指し示します。これは、各kubeletにクラスターレベルの設定を配布することの必要性を示しています。

二つ目のパターンである、インスタンス固有の設定内容を適用するために、kubeadmは環境ファイルを/var/lib/kubelet/kubeadm-flags.envへ書き出します。このファイルは以下のように、kubelet起動時に渡されるフラグのリストを含んでいます。

KUBELET_KUBEADM_ARGS="--flag1=value1 --flag2=value2 ..."

kubelet起動時に渡されるフラグに加えて、このファイルはcgroupドライバーや異なるCRIランタイムソケットを使用するかどうか(--cri-socket)といった動的なパラメーターも含みます。

これら二つのファイルがディスク上に格納されると、systemdを使用している場合、kubeadmは以下の二つのコマンドを実行します。

systemctl daemon-reload && systemctl restart kubelet

リロードと再起動に成功すると、通常のkubeadm initのワークフローが続きます。

kubeadm join実行時の流れ

kubeadm joinを実行した場合、kubeadmはBootstrap Token証明書を使用してTLS bootstrapを行い、ConfigMapkubelet-config-1.Xをダウンロードするために必要なクレデンシャルを取得し、/var/lib/kubelet/config.yamlへ書き込みます。動的な環境ファイルは、kubeadm initの場合と全く同様の方法で生成されます。

次に、kubeadmは、kubeletに新たな設定を読み込むために、以下の二つのコマンドを実行します。

systemctl daemon-reload && systemctl restart kubelet

kubeletが新たな設定を読み込むと、kubeadmは、KubeConfigファイル/etc/kubernetes/bootstrap-kubelet.confを書き込みます。これは、CA証明書とBootstrap Tokenを含みます。これらはkubeletがTLS Bootstrapを行い/etc/kubernetes/kubelet.confに格納されるユニークなクレデンシャルを取得するために使用されます。ファイルが書き込まれると、kubeletはTLS Bootstrapを終了します。

kubelet用のsystemdファイル

kubeadmには、systemdがどのようにkubeletを実行するかを指定した設定ファイルが同梱されています。 kubeadm CLIコマンドは決してこのsystemdファイルには触れないことに注意してください。

kubeadmのDEBパッケージまたはRPMパッケージによってインストールされたこの設定ファイルは、/etc/systemd/system/kubelet.service.d/10-kubeadm.confに書き込まれ、systemdで使用されます。基本的なkubelet.service(RPM用または、 DEB用)を拡張します。

[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf
--kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generate at runtime, populating
the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably,
# the user should use the .NodeRegistration.KubeletExtraArgs object in the configuration files instead.
# KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

このファイルは、kubeadmがkubelet用に管理する全ファイルが置かれるデフォルトの場所を指定します。

  • TLS Bootstrapに使用するKubeConfigファイルは/etc/kubernetes/bootstrap-kubelet.confですが、/etc/kubernetes/kubelet.confが存在しない場合にのみ使用します。
  • ユニークなkublet識別子を含むKubeConfigファイルは/etc/kubernetes/kubelet.confです。
  • kubeletのComponentConfigを含むファイルは/var/lib/kubelet/config.yamlです。
  • KUBELET_KUBEADM_ARGSを含む動的な環境ファイルは/var/lib/kubelet/kubeadm-flags.envから取得します。
  • KUBELET_EXTRA_ARGSによるユーザー定義のフラグの上書きを格納できるファイルは/etc/default/kubelet(DEBの場合)、または/etc/sysconfig/kubelet(RPMの場合)から取得します。KUBELET_EXTRA_ARGSはフラグの連なりの最後に位置し、優先度が最も高いです。

Kubernetesバイナリとパッケージの内容

Kubernetesに同梱されるDEB、RPMのパッケージは以下の通りです。

パッケージ名説明
kubeadm/usr/bin/kubeadmCLIツールと、kubelet用のsystemdファイルをインストールします。
kubeletkubeletバイナリを/usr/binに、CNIバイナリを/opt/cni/binにインストールします。
kubectl/usr/bin/kubectlバイナリをインストールします。
cri-tools/usr/bin/crictlバイナリをcri-tools gitリポジトリからインストールします。

2.2.2.1.9 - コントロールプレーンをセルフホストするようにkubernetesクラスターを構成する

コントロールプレーンのセルフホスティング

kubeadmを使用すると、セルフホスト型のKubernetesコントロールプレーンを実験的に作成できます。これはAPIサーバー、コントローラーマネージャー、スケジューラーなどの主要コンポーネントは、静的ファイルを介してkubeletで構成されたstatic podsではなく、Kubernetes APIを介して構成されたDaemonSet podsとして実行されることを意味します。

セルフホスト型クラスターを作成する場合はkubeadm alpha selfhosting pivotを参照してください。

警告

  1. 1.8以降のセルフホスティングには、いくつかの重要な制限があります。特に、セルフホスト型クラスターは、手動の介入なしにコントロールプレーンのNode再起動から回復することはできません。

  2. デフォルトでは、セルフホスト型のコントロールプレーンのPodは、hostPathボリュームからロードされた資格情報に依存しています。最初の作成を除いて、これらの資格情報はkubeadmによって管理されません。

  3. コントロールプレーンのセルフホストされた部分にはetcdが含まれていませんが、etcdは静的Podとして実行されます。

プロセス

セルフホスティングのブートストラッププロセスは、kubeadm design documentに記載されています。

要約すると、kubeadm alpha selfhostingは次のように機能します。

  1. 静的コントロールプレーンのブートストラップが起動し、正常になるのを待ちます。これはkubeadm initのセルフホスティングを使用しないプロセスと同じです。

  2. 静的コントロールプレーンのPodのマニフェストを使用して、セルフホスト型コントロールプレーンを実行する一連のDaemonSetのマニフェストを構築します。また、必要に応じてこれらのマニフェストを変更します。たとえば、シークレット用の新しいボリュームを追加します。

  3. kube-systemのネームスペースにDaemonSetを作成し、Podの結果が起動されるのを待ちます。

  4. セルフホスト型のPodが操作可能になると、関連する静的Podが削除され、kubeadmは次のコンポーネントのインストールに進みます。これによりkubeletがトリガーされて静的Podが停止します。

  5. 元の静的なコントロールプレーンが停止すると、新しいセルフホスト型コントロールプレーンはリスニングポートにバインドしてアクティブになります。

2.2.2.2 - kopsを使ったAWS上でのKubernetesのインストール

This quickstart shows you how to easily install a Kubernetes cluster on AWS. It uses a tool called kops.

kops is an automated provisioning system:

  • Fully automated installation
  • Uses DNS to identify clusters
  • Self-healing: everything runs in Auto-Scaling Groups
  • Multiple OS support (Debian, Ubuntu 16.04 supported, CentOS & RHEL, Amazon Linux and CoreOS) - see the images.md
  • High-Availability support - see the high_availability.md
  • Can directly provision, or generate terraform manifests - see the terraform.md

始める前に

クラスターの作成

(1/5) kopsのインストール

インストール

Download kops from the releases page (it is also easy to build from source):

Download the latest release with the command:

curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-darwin-amd64

To download a specific version, replace the following portion of the command with the specific kops version.

$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)

For example, to download kops version v1.20.0 type:

curl -LO https://github.com/kubernetes/kops/releases/download/v1.20.0/kops-darwin-amd64

Make the kops binary executable.

chmod +x kops-darwin-amd64

Move the kops binary in to your PATH.

sudo mv kops-darwin-amd64 /usr/local/bin/kops

You can also install kops using Homebrew.

brew update && brew install kops

Download the latest release with the command:

curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64

To download a specific version of kops, replace the following portion of the command with the specific kops version.

$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)

For example, to download kops version v1.20.0 type:

curl -LO https://github.com/kubernetes/kops/releases/download/v1.20.0/kops-linux-amd64

Make the kops binary executable

chmod +x kops-linux-amd64

Move the kops binary in to your PATH.

sudo mv kops-linux-amd64 /usr/local/bin/kops

You can also install kops using Homebrew.

brew update && brew install kops

(2/5) クラスター用のroute53ドメインの作成

kops uses DNS for discovery, both inside the cluster and outside, so that you can reach the kubernetes API server from clients.

kops has a strong opinion on the cluster name: it should be a valid DNS name. By doing so you will no longer get your clusters confused, you can share clusters with your colleagues unambiguously, and you can reach them without relying on remembering an IP address.

You can, and probably should, use subdomains to divide your clusters. As our example we will use useast1.dev.example.com. The API server endpoint will then be api.useast1.dev.example.com.

A Route53 hosted zone can serve subdomains. Your hosted zone could be useast1.dev.example.com, but also dev.example.com or even example.com. kops works with any of these, so typically you choose for organization reasons (e.g. you are allowed to create records under dev.example.com, but not under example.com).

Let's assume you're using dev.example.com as your hosted zone. You create that hosted zone using the normal process, or with a command such as aws route53 create-hosted-zone --name dev.example.com --caller-reference 1.

You must then set up your NS records in the parent domain, so that records in the domain will resolve. Here, you would create NS records in example.com for dev. If it is a root domain name you would configure the NS records at your domain registrar (e.g. example.com would need to be configured where you bought example.com).

This step is easy to mess up (it is the #1 cause of problems!) You can double-check that your cluster is configured correctly if you have the dig tool by running:

dig NS dev.example.com

You should see the 4 NS records that Route53 assigned your hosted zone.

(3/5) クラスターの状態を保存するS3バケットの作成

kops lets you manage your clusters even after installation. To do this, it must keep track of the clusters that you have created, along with their configuration, the keys they are using etc. This information is stored in an S3 bucket. S3 permissions are used to control access to the bucket.

Multiple clusters can use the same S3 bucket, and you can share an S3 bucket between your colleagues that administer the same clusters - this is much easier than passing around kubecfg files. But anyone with access to the S3 bucket will have administrative access to all your clusters, so you don't want to share it beyond the operations team.

So typically you have one S3 bucket for each ops team (and often the name will correspond to the name of the hosted zone above!)

In our example, we chose dev.example.com as our hosted zone, so let's pick clusters.dev.example.com as the S3 bucket name.

  • Export AWS_PROFILE (if you need to select a profile for the AWS CLI to work)

  • Create the S3 bucket using aws s3 mb s3://clusters.dev.example.com

  • You can export KOPS_STATE_STORE=s3://clusters.dev.example.com and then kops will use this location by default. We suggest putting this in your bash profile or similar.

(4/5) クラスター設定の構築

Run kops create cluster to create your cluster configuration:

kops create cluster --zones=us-east-1c useast1.dev.example.com

kops will create the configuration for your cluster. Note that it only creates the configuration, it does not actually create the cloud resources - you'll do that in the next step with a kops update cluster. This give you an opportunity to review the configuration or change it.

It prints commands you can use to explore further:

  • List your clusters with: kops get cluster
  • Edit this cluster with: kops edit cluster useast1.dev.example.com
  • Edit your node instance group: kops edit ig --name=useast1.dev.example.com nodes
  • Edit your master instance group: kops edit ig --name=useast1.dev.example.com master-us-east-1c

If this is your first time using kops, do spend a few minutes to try those out! An instance group is a set of instances, which will be registered as kubernetes nodes. On AWS this is implemented via auto-scaling-groups. You can have several instance groups, for example if you wanted nodes that are a mix of spot and on-demand instances, or GPU and non-GPU instances.

(5/5) AWSにクラスターを作成

Run "kops update cluster" to create your cluster in AWS:

kops update cluster useast1.dev.example.com --yes

That takes a few seconds to run, but then your cluster will likely take a few minutes to actually be ready. kops update cluster will be the tool you'll use whenever you change the configuration of your cluster; it applies the changes you have made to the configuration to your cluster - reconfiguring AWS or kubernetes as needed.

For example, after you kops edit ig nodes, then kops update cluster --yes to apply your configuration, and sometimes you will also have to kops rolling-update cluster to roll out the configuration immediately.

Without --yes, kops update cluster will show you a preview of what it is going to do. This is handy for production clusters!

他のアドオンの参照

See the list of add-ons to explore other add-ons, including tools for logging, monitoring, network policy, visualization, and control of your Kubernetes cluster.

クリーンアップ

  • To delete your cluster: kops delete cluster useast1.dev.example.com --yes

次の項目

2.2.2.3 - kubesprayを使ったKubernetesのインストール

このクイックスタートは、Kubesprayを使用して、GCE、Azure、OpenStack、AWS、vSphere、Equinix Metal(以前のPacket)、Oracle Cloud Infrastructure(実験的)またはベアメタルにホストされたKubernetesクラスターをインストールするためのものです。

Kubesprayは、汎用的なOSやKubernetesクラスターの構成管理タスクのためのAnsibleプレイブック、インベントリー、プロビジョニングツール、ドメインナレッジをまとめたものです。

Kubesprayは次を提供します:

  • 高可用性クラスター。
  • 構成可能(例えばネットワークプラグインの選択)。
  • 最もポピュラーなLinuxディストリビューションのサポート:
    • Flatcar Container Linux by Kinvolk
    • Debian Bullseye, Buster, Jessie, Stretch
    • Ubuntu 16.04, 18.04, 20.04, 22.04
    • CentOS/RHEL 7, 8, 9
    • Fedora 35, 36
    • Fedora CoreOS
    • openSUSE Leap 15.x/Tumbleweed
    • Oracle Linux 7, 8, 9
    • Alma Linux 8, 9
    • Rocky Linux 8, 9
    • Kylin Linux Advanced Server V10
    • Amazon Linux 2
  • 継続的インテグレーションテスト。

あなたのユースケースに最適なツールの選択には、kubeadmkops比較したドキュメントを参照してください。

クラスターの作成

(1/5) 下地の要件の確認

次の要件に従ってサーバーをプロビジョニングします:

  • Kubernetesの最低必要バージョンはv1.22
  • Ansibleのコマンドを実行するマシン上にAnsible v2.11+、Jinja 2.11+とpython-netaddrがインストールされていること
  • ターゲットサーバーはdockerイメージをpullするためにインターネットにアクセスできる必要があります。そうでは無い場合は追加の構成が必要です(オフライン環境を参照)
  • ターゲットのサーバーはIPv4フォワーディングができるように構成されていること。
  • PodとServiceにIPv6を使用している場合は、ターゲットサーバーはIPv6フォワーディングができるように構成されていること。
  • ファイアウォールは管理されないため、従来のように独自のルールを実装しなければなりません。デプロイ中の問題を避けるためには、ファイアウォールを無効にすべきです
  • root以外のユーザーアカウントでkubesprayを実行する場合は、ターゲットサーバー上で特権昇格の方法を正しく構成されている必要があります。そして、ansible_becomeフラグ、またはコマンドパラメーター--become-bを指定する必要があります

Kubesprayは環境のプロビジョニングを支援するために次のユーティリティを提供します:

(2/5) インベントリーファイルの用意

サーバーをプロビジョニングした後、Ansibleのインベントリーファイルを作成します。これは手動またはダイナミックインベントリースクリプトによって行うことができます。詳細については、"独自のインベントリーを構築する"を参照してください。

(3/5) クラスター作成の計画

Kubesprayは多くの点でデプロイメントをカスタマイズする機能を提供します:

  • デプロイメントモードの選択: kubeadmまたはnon-kubeadm
  • CNI(ネットワーク)プラグイン
  • DNS設定
  • コントロールプレーンの選択: ネイティブ/バイナリまたはコンテナ化
  • コンポーネントバージョン
  • Calicoルートリフレクター
  • コンポーネントランタイムオプション
  • 証明書の生成方法

Kubesprayはvariableファイルによってカスタマイズできます。Kubesprayを使い始めたばかりであれば、Kubesprayのデフォルト設定を使用してクラスターをデプロイし、Kubernetesを探索することを検討してください。

(4/5) クラスターのデプロイ

次にクラスターをデプロイします:

クラスターのデプロイメントにはansible-playbookを使用します。

ansible-playbook -i your/inventory/inventory.ini cluster.yml -b -v \
  --private-key=~/.ssh/private_key

大規模なデプロイメント(100以上のノード)では、最適な結果を得るために個別の調整が必要な場合があります。

(5/5) デプロイの確認

Kubesprayは、NetcheckerによるPod間の接続とDNSの解決の検証を行う機能を提供します。Netcheckerは、netchecker-agents Podがdefault名前空間内でDNSリクエストを解決し、互いにpingを送信できることを確かめます。これらのPodは他のワークロードと同様の動作を再現し、クラスターの健全性を示す指標として機能します。

クラスターの操作

Kubesprayはクラスターを管理する追加のプレイブックを提供します: scaleupgrade

クラスターのスケール

scaleプレイブックを実行することで、クラスターにワーカーノードを追加することができます。詳細については、"ノードの追加"を参照してください。 remove-nodeプレイブックを実行することで、クラスターからワーカーノードを削除することができます。詳細については、"ノードの削除"を参照してください。

クラスターのアップグレード

upgrade-clusterプレイブックを実行することで、クラスターのアップグレードができます。詳細については、"アップグレード"を参照してください。

クリーンアップ

resetプレイブックを使用して、ノードをリセットし、Kubesprayでインストールした全てのコンポーネントを消すことができます。

フィードバック

次の項目

2.2.3 - ターンキークラウドソリューション

このページは、Kubernetes認定ソリューションプロバイダーのリストを提供します。 各プロバイダーのページから、本番環境でも利用可能なクラスターのインストール方法やセットアップ方法を学ぶことができます。

2.2.4 - Windows in Kubernetes

2.2.4.1 - KubernetesのWindowsサポート概要

Windowsアプリケーションは、多くの組織で実行されているサービスやアプリケーションの大部分を占めています。Windowsコンテナは、プロセスとパッケージの依存関係を一つにまとめる最新の方法を提供し、DevOpsプラクティスの使用とWindowsアプリケーションのクラウドネイティブパターンの追求を容易にします。Kubernetesは事実上、標準的なコンテナオーケストレータになりました。Kubernetes 1.14のリリースでは、Kubernetesクラスター内のWindowsノードでWindowsコンテナをスケジューリングする本番環境サポートが含まれたので、Windowsアプリケーションの広大なエコシステムにおいて、Kubernetesを有効的に活用できます。WindowsベースのアプリケーションとLinuxベースのアプリケーションに投資している組織は、ワークロードを管理する個別のオーケストレーターが不要となるため、オペレーティングシステムに関係なくアプリケーション全体の運用効率が向上します。

KubernetesのWindowsコンテナ

KubernetesでWindowsコンテナのオーケストレーションを有効にする方法は、既存のLinuxクラスターにWindowsノードを含めるだけです。KubernetesのPodでWindowsコンテナをスケジュールすることは、Linuxベースのコンテナをスケジュールするのと同じくらいシンプルで簡単です。

Windowsコンテナを実行するには、Kubernetesクラスターに複数のオペレーティングシステムを含める必要があります。コントロールプレーンノードはLinux、ワーカーノードはワークロードのニーズに応じてWindowsまたはLinuxで実行します。Windows Server 2019は、サポートされている唯一のWindowsオペレーティングシステムであり、Windows (kubelet、コンテナランタイム、kube-proxyを含む)でKubernetesノードを有効にします。Windowsディストリビューションチャンネルの詳細については、Microsoftのドキュメントを参照してください。

サポートされている機能と制限

サポートされている機能

コンピュート

APIとkubectlの観点から見ると、WindowsコンテナはLinuxベースのコンテナとほとんど同じように動作します。ただし、制限セクションで概説されている主要な機能には、いくつかの顕著な違いがあります。

オペレーティングシステムのバージョンから始めましょう。KubernetesのWindowsオペレーティングシステムのサポートについては、次の表を参照してください。単一の混成Kubernetesクラスターは、WindowsとLinuxの両方のワーカーノードを持つことができます。WindowsコンテナはWindowsノードで、LinuxコンテナはLinuxノードでスケジュールする必要があります。

Kubernetes バージョンホストOS バージョン (Kubernetes ノード)
Windows Server 1709Windows Server 1803Windows Server 1809/Windows Server 2019
Kubernetes v1.14サポートされていませんサポートされていませんWindows Server containers Builds 17763.* と Docker EE-basic 18.09 がサポートされています

Kubernetesの主要な要素は、WindowsでもLinuxと同じように機能します。このセクションでは、主要なワークロードイネーブラーのいくつかと、それらがWindowsにどのようにマップされるかについて説明します。

  • Pods

    Podは、Kubernetesにおける最も基本的な構成要素です。人間が作成またはデプロイするKubernetesオブジェクトモデルの中で最小かつ最もシンプルな単位です。WindowsとLinuxのコンテナを同じPodにデプロイすることはできません。Pod内のすべてのコンテナは、各ノードが特定のプラットフォームとアーキテクチャを表す単一のノードにスケジュールされます。次のPod機能、プロパティ、およびイベントがWindowsコンテナでサポートされています。:

    • プロセス分離とボリューム共有を備えたPodごとの単一または複数のコンテナ
    • Podステータスフィールド
    • ReadinessとLiveness Probe
    • postStartとpreStopコンテナのライフサイクルイベント
    • 環境変数またはボリュームとしてのConfigMap、 Secrets
    • EmptyDir
    • 名前付きパイプホストマウント
    • リソース制限
  • Controllers

    Kubernetesコントローラーは、Podの望ましい状態を処理します。次のワークロードコントローラーは、Windowsコンテナでサポートされています。:

    • ReplicaSet
    • ReplicationController
    • Deployments
    • StatefulSets
    • DaemonSet
    • Job
    • CronJob
  • Services

    Kubernetes Serviceは、Podの論理セットとPodにアクセスするためのポリシーを定義する抽象概念です。マイクロサービスと呼ばれることもあります。オペレーティングシステム間の接続にServiceを使用できます。WindowsでのServiceは、次のタイプ、プロパティと機能を利用できます。:

    • サービス環境変数
    • NodePort
    • ClusterIP
    • LoadBalancer
    • ExternalName
    • Headless services

Pod、Controller、Serviceは、KubernetesでWindowsワークロードを管理するための重要な要素です。ただし、それだけでは、動的なクラウドネイティブ環境でWindowsワークロードの適切なライフサイクル管理を可能にするのに十分ではありません。次の機能のサポートを追加しました:

  • Podとコンテナのメトリクス
  • Horizontal Pod Autoscalerサポート
  • kubectl Exec
  • リソースクォータ
  • Schedulerのプリエンプション

コンテナランタイム

Docker EE
FEATURE STATE: Kubernetes v1.14 [stable]

Docker EE-basic 18.09+は、Kubernetesを実行しているWindows Server 2019 / 1809ノードに推奨されるコンテナランタイムです。kubeletに含まれるdockershimコードで動作します。

CRI-ContainerD
FEATURE STATE: Kubernetes v1.18 [alpha]

ContainerDはLinux上のKubernetesで動作するOCI準拠のランタイムです。Kubernetes v1.18では、Windows上でのContainerDのサポートが追加されています。Windows上でのContainerDの進捗状況はenhancements#1001で確認できます。

永続ストレージ

Kubernetesボリュームを使用すると、データの永続性とPodボリュームの共有要件を備えた複雑なアプリケーションをKubernetesにデプロイできます。特定のストレージバックエンドまたはプロトコルに関連付けられた永続ボリュームの管理には、ボリュームのプロビジョニング/プロビジョニング解除/サイズ変更、Kubernetesノードへのボリュームのアタッチ/デタッチ、およびデータを永続化する必要があるPod内の個別のコンテナへのボリュームのマウント/マウント解除などのアクションが含まれます。特定のストレージバックエンドまたはプロトコルに対してこれらのボリューム管理アクションを実装するコードは、Kubernetesボリュームプラグインの形式で出荷されます。次の幅広いクラスのKubernetesボリュームプラグインがWindowsでサポートされています。:

In-treeボリュームプラグイン

In-treeボリュームプラグインに関連付けられたコードは、コアKubernetesコードベースの一部として提供されます。In-treeボリュームプラグインのデプロイでは、追加のスクリプトをインストールしたり、個別のコンテナ化されたプラグインコンポーネントをデプロイしたりする必要はありません。これらのプラグインは、ストレージバックエンドでのボリュームのプロビジョニング/プロビジョニング解除とサイズ変更、Kubernetesノードへのボリュームのアタッチ/アタッチ解除、Pod内の個々のコンテナへのボリュームのマウント/マウント解除を処理できます。次のIn-treeプラグインは、Windowsノードをサポートしています。:

FlexVolume Plugins

FlexVolumeプラグインに関連付けられたコードは、ホストに直接デプロイする必要があるout-of-treeのスクリプトまたはバイナリとして出荷されます。FlexVolumeプラグインは、Kubernetesノードとの間のボリュームのアタッチ/デタッチ、およびPod内の個々のコンテナとの間のボリュームのマウント/マウント解除を処理します。FlexVolumeプラグインに関連付けられた永続ボリュームのプロビジョニング/プロビジョニング解除は、通常FlexVolumeプラグインとは別の外部プロビジョニング担当者を通じて処理できます。次のFlexVolumeプラグインは、Powershellスクリプトとしてホストにデプロイされ、Windowsノードをサポートします:

CSIプラグイン
FEATURE STATE: Kubernetes v1.16 [alpha]

CSIプラグインに関連付けられたコードは、通常、コンテナイメージとして配布され、DaemonSetやStatefulSetなどの標準のKubernetesコンポーネントを使用してデプロイされるout-of-treeのスクリプトおよびバイナリとして出荷されます。CSIプラグインは、ボリュームのプロビジョニング/プロビジョニング解除/サイズ変更、Kubernetesノードへのボリュームのアタッチ/ボリュームからのデタッチ、Pod内の個々のコンテナへのボリュームのマウント/マウント解除、バックアップ/スナップショットとクローニングを使用した永続データのバックアップ/リストアといった、Kubernetesの幅広いボリューム管理アクションを処理します。CSIプラグインは通常、ノードプラグイン(各ノードでDaemonSetとして実行される)とコントローラープラグインで構成されます。

CSIノードプラグイン(特に、ブロックデバイスまたは共有ファイルシステムとして公開された永続ボリュームに関連付けられているプラ​​グイン)は、ディスクデバイスのスキャン、ファイルシステムのマウントなど、さまざまな特権操作を実行する必要があります。これらの操作は、ホストオペレーティングシステムごとに異なります。Linuxワーカーノードの場合、コンテナ化されたCSIノードプラグインは通常、特権コンテナとしてデプロイされます。Windowsワーカーノードの場合、コンテナ化されたCSIノードプラグインの特権操作は、csi-proxyを使用してサポートされます。各Windowsノードにプリインストールされている。詳細については、展開するCSIプラグインの展開ガイドを参照してください。

ネットワーキング

Windowsコンテナのネットワークは、CNIプラグインを通じて公開されます。Windowsコンテナは、ネットワークに関して仮想マシンと同様に機能します。各コンテナには、Hyper-V仮想スイッチ(vSwitch)に接続されている仮想ネットワークアダプター(vNIC)があります。Host Network Service(HNS)とHost Compute Service(HCS)は連携してコンテナを作成し、コンテナvNICをネットワークに接続します。HCSはコンテナの管理を担当するのに対し、HNSは次のようなネットワークリソースの管理を担当します。:

  • 仮想ネットワーク(vSwitchの作成を含む)
  • エンドポイント/vNIC
  • 名前空間
  • ポリシー(パケットのカプセル化、負荷分散ルール、ACL、NATルールなど)

次のServiceタイプがサポートされています。:

  • NodePort
  • ClusterIP
  • LoadBalancer
  • ExternalName

Windowsは、L2bridge、L2tunnel、Overlay、Transparent、NATの5つの異なるネットワークドライバー/モードをサポートしています。WindowsとLinuxのワーカーノードを持つ異種クラスターでは、WindowsとLinuxの両方で互換性のあるネットワークソリューションを選択する必要があります。以下のツリー外プラグインがWindowsでサポートされており、各CNIをいつ使用するかに関する推奨事項があります。:

ネットワークドライバー説明コンテナパケットの変更ネットワークプラグインネットワークプラグインの特性
L2bridgeコンテナは外部のvSwitchに接続されます。コンテナはアンダーレイネットワークに接続されますが、物理ネットワークはコンテナのMACを上り/下りで書き換えるため、MACを学習する必要はありません。コンテナ間トラフィックは、コンテナホスト内でブリッジされます。MACはホストのMACに書き換えられ、IPは変わりません。win-bridgeAzure-CNI、Flannelホストゲートウェイは、win-bridgeを使用します。win-bridgeはL2bridgeネットワークモードを使用して、コンテナをホストのアンダーレイに接続して、最高のパフォーマンスを提供します。ノード間接続にはユーザー定義ルート(UDR)が必要です。
L2Tunnelこれはl2bridgeの特殊なケースですが、Azureでのみ使用されます。すべてのパケットは、SDNポリシーが適用されている仮想化ホストに送信されます。MACが書き換えられ、IPがアンダーレイネットワークで表示されます。Azure-CNIAzure-CNIを使用すると、コンテナをAzure vNETと統合し、Azure Virtual Networkが提供する一連の機能を活用できます。たとえば、Azureサービスに安全に接続するか、Azure NSGを使用します。azure-cniのいくつかの例を参照してください。
オーバーレイ(KubernetesのWindows用のオーバーレイネットワークは アルファ 段階です)コンテナには、外部のvSwitchに接続されたvNICが付与されます。各オーバーレイネットワークは、カスタムIPプレフィックスで定義された独自のIPサブネットを取得します。オーバーレイネットワークドライバーは、VXLANを使用してカプセル化します。外部ヘッダーでカプセル化されます。Win-overlay、Flannel VXLAN (win-overlayを使用)win-overlayは、仮想コンテナネットワークをホストのアンダーレイから分離する必要がある場合に使用する必要があります(セキュリティ上の理由など)。データセンター内のIPが制限されている場合に、(異なるVNIDタグを持つ)異なるオーバーレイネットワークでIPを再利用できるようにします。このオプションには、Windows Server 2019でKB4489899が必要です。
透過的(ovn-kubernetesの特別な使用例)外部のvSwitchが必要です。コンテナは外部のvSwitchに接続され、論理ネットワーク(論理スイッチおよびルーター)を介したPod内通信を可能にします。パケットは、GENEVEまたはSTTトンネリングを介してカプセル化され、同じホスト上にないポッドに到達します。パケットは、ovnネットワークコントローラーによって提供されるトンネルメタデータ情報を介して転送またはドロップされます。NATは南北通信のために行われます。ovn-kubernetesansible経由でデプロイします。分散ACLは、Kubernetesポリシーを介して適用できます。 IPAMをサポートします。負荷分散は、kube-proxyなしで実現できます。 NATは、ip​​tables/netshを使用せずに行われます。
NAT(Kubernetesでは使用されません)コンテナには、内部のvSwitchに接続されたvNICが付与されます。DNS/DHCPは、WinNATと呼ばれる内部コンポーネントを使用して提供されます。MACおよびIPはホストMAC/IPに書き換えられます。nat完全を期すためにここに含まれています。

上で概説したように、Flannel CNIメタプラグインは、VXLANネットワークバックエンド(アルファサポート、win-overlayへのデリゲート)およびホストゲートウェイネットワークバックエンド(安定したサポート、win-bridgeへのデリゲート)を介してWindowsでもサポートされます。このプラグインは、参照CNIプラグイン(win-overlay、win-bridge)の1つへの委任をサポートし、WindowsのFlannelデーモン(Flanneld)と連携して、ノードのサブネットリースの自動割り当てとHNSネットワークの作成を行います。このプラグインは、独自の構成ファイル(cni.conf)を読み取り、FlannelDで生成されたsubnet.envファイルからの環境変数と統合します。次に、ネットワークプラミング用の参照CNIプラグインの1つに委任し、ノード割り当てサブネットを含む正しい構成をIPAMプラグイン(ホストローカルなど)に送信します。

Node、Pod、およびServiceオブジェクトの場合、TCP/UDPトラフィックに対して次のネットワークフローがサポートされます。:

  • Pod -> Pod (IP)
  • Pod -> Pod (Name)
  • Pod -> Service (Cluster IP)
  • Pod -> Service (PQDN、ただし、「.」がない場合のみ)
  • Pod -> Service (FQDN)
  • Pod -> External (IP)
  • Pod -> External (DNS)
  • Node -> Pod
  • Pod -> Node

Windowsでは、次のIPAMオプションがサポートされています。

  • ホストローカル
  • HNS IPAM (受信トレイプラットフォームIPAM、これはIPAMが設定されていない場合のフォールバック)
  • Azure-vnet-ipam(azure-cniのみ)

制限

コントロールプレーン

Windowsは、Kubernetesアーキテクチャとコンポーネントマトリックスのワーカーノードとしてのみサポートされています。つまり、Kubernetesクラスターには常にLinuxマスターノード、0以上のLinuxワーカーノード、0以上のWindowsワーカーノードが含まれている必要があります。

コンピュート

リソース管理とプロセス分離

Linux cgroupsは、Linuxのリソースを制御するPodの境界として使用されます。コンテナは、ネットワーク、プロセス、およびファイルシステムを分離するのために、その境界内に作成されます。cgroups APIを使用して、cpu/io/memoryの統計を収集できます。対照的に、Windowsはシステムネームスペースフィルターを備えたコンテナごとのジョブオブジェクトを使用して、コンテナ内のすべてのプロセスを格納し、ホストからの論理的な分離を提供します。ネームスペースフィルタリングを行わずにWindowsコンテナを実行する方法はありません。これは、ホストの環境ではシステム特権を主張できないため、Windowsでは特権コンテナを使用できないことを意味します。セキュリティアカウントマネージャー(SAM)が独立しているため、コンテナはホストからIDを引き受けることができません。

オペレーティングシステムの制限

Windowsには厳密な互換性ルールがあり、ホストOSのバージョンとコンテナのベースイメージOSのバージョンは、一致する必要があります。Windows Server 2019のコンテナオペレーティングシステムを備えたWindowsコンテナのみがサポートされます。Hyper-V分離のコンテナは、Windowsコンテナのイメージバージョンに下位互換性を持たせることは、将来のリリースで計画されています。

機能制限
  • TerminationGracePeriod:実装されていません
  • 単一ファイルのマッピング:CRI-ContainerDで実装されます
  • 終了メッセージ:CRI-ContainerDで実装されます
  • 特権コンテナ:現在Windowsコンテナではサポートされていません
  • HugePages:現在Windowsコンテナではサポートされていません
  • 既存のノード問題を検出する機能はLinux専用であり、特権コンテナが必要です。一般的に、特権コンテナはサポートされていないため、これがWindowsで使用されることは想定していません。
  • ネームスペース共有については、すべての機能がサポートされているわけではありません(詳細については、APIセクションを参照してください)
メモリ予約と処理

Windowsには、Linuxのようなメモリ不足のプロセスキラーはありません。Windowsは常に全ユーザーモードのメモリ割り当てを仮想として扱い、ページファイルは必須です。正味の効果は、WindowsはLinuxのようなメモリ不足の状態にはならず、メモリ不足(OOM)終了の影響を受ける代わりにページをディスクに処理します。メモリが過剰にプロビジョニングされ、物理メモリのすべてが使い果たされると、ページングによってパフォーマンスが低下する可能性があります。

2ステップのプロセスで、メモリ使用量を妥当な範囲内に保つことが可能です。まず、kubeletパラメーター--kubelet-reserve--system-reserveを使用して、ノード(コンテナ外)でのメモリ使用量を明確にします。これにより、NodeAllocatable)が削減されます。ワークロードをデプロイするときは、コンテナにリソース制限をかけます(制限のみを設定するか、制限が要求と等しくなければなりません)。これにより、NodeAllocatableも差し引かれ、ノードのリソースがフルな状態になるとSchedulerがPodを追加できなくなります。

過剰なプロビジョニングを回避するためのベストプラクティスは、Windows、Docker、およびKubernetesのプロセスに対応するために、最低2GBのメモリを予約したシステムでkubeletを構成することです。

フラグの振舞いについては、次のような異なる動作をします。:

  • --kubelet-reserve--system-reserve、および--eviction-hardフラグはノードの割り当て可能数を更新します
  • --enforce-node-allocableを使用した排除は実装されていません
  • --eviction-hardおよび--eviction-softを使用した排除は実装されていません
  • MemoryPressureの制約は実装されていません
  • kubeletによって実行されるOOMを排除することはありません
  • Windowsノードで実行されているKubeletにはメモリ制限がありません。--kubelet-reserve--system-reserveは、ホストで実行されているkubeletまたはプロセスに制限を設定しません。これは、ホスト上のkubeletまたはプロセスが、NodeAllocatableとSchedulerの外でメモリリソース不足を引き起こす可能性があることを意味します。

ストレージ

Windowsには、コンテナレイヤーをマウントして、NTFSに基づいて複製されたファイルシステムを作るためのレイヤー構造のファイルシステムドライバーがあります。コンテナ内のすべてのファイルパスは、そのコンテナの環境内だけで決められます。

  • ボリュームマウントは、コンテナ内のディレクトリのみを対象にすることができ、個別のファイルは対象にできません
  • ボリュームマウントは、ファイルまたはディレクトリをホストファイルシステムに投影することはできません
  • WindowsレジストリとSAMデータベースには常に書き込みアクセスが必要であるため、読み取り専用ファイルシステムはサポートされていません。ただし、読み取り専用ボリュームはサポートされています
  • ボリュームのユーザーマスクと権限は使用できません。SAMはホストとコンテナ間で共有されないため、それらの間のマッピングはありません。すべての権限はコンテナの環境内で決められます

その結果、次のストレージ機能はWindowsノードではサポートされません。

  • ボリュームサブパスのマウント。Windowsコンテナにマウントできるのはボリューム全体だけです。
  • シークレットのサブパスボリュームのマウント
  • ホストマウントプロジェクション
  • DefaultMode(UID/GID依存関係による)
  • 読み取り専用のルートファイルシステム。マップされたボリュームは引き続き読み取り専用をサポートします
  • ブロックデバイスマッピング
  • 記憶媒体としてのメモリ
  • uui/guid、ユーザーごとのLinuxファイルシステム権限などのファイルシステム機能
  • NFSベースのストレージ/ボリュームのサポート
  • マウントされたボリュームの拡張(resizefs)

ネットワーキング

Windowsコンテナネットワーキングは、Linuxネットワーキングとはいくつかの重要な実装方法の違いがあります。Microsoft documentation for Windows Container Networkingには、追加の詳細と背景があります。

Windowsホストネットワーキングサービスと仮想スイッチはネームスペースを実装して、Podまたはコンテナの必要に応じて仮想NICを作成できます。ただし、DNS、ルート、メトリックなどの多くの構成は、Linuxのような/etc/...ファイルではなく、Windowsレジストリデータベースに保存されます。コンテナのWindowsレジストリはホストのレジストリとは別であるため、ホストからコンテナへの/etc/resolv.confのマッピングなどの概念は、Linuxの場合と同じ効果をもたらしません。これらは、そのコンテナの環境で実行されるWindows APIを使用して構成する必要があります。したがって、CNIの実装は、ファイルマッピングに依存する代わりにHNSを呼び出して、ネットワークの詳細をPodまたはコンテナに渡す必要があります。

次のネットワーク機能はWindowsノードではサポートされていません

  • ホストネットワーキングモードはWindows Podでは使用できません
  • ノード自体からのローカルNodePortアクセスは失敗します(他のノードまたは外部クライアントで機能)
  • ノードからのService VIPへのアクセスは、Windows Serverの将来のリリースで利用可能になる予定です
  • kube-proxyのオーバーレイネットワーキングサポートはアルファリリースです。さらに、KB4482887がWindows Server 2019にインストールされている必要があります
  • ローカルトラフィックポリシーとDSRモード
  • l2bridge、l2tunnel、またはオーバーレイネットワークに接続されたWindowsコンテナは、IPv6スタックを介した通信をサポートしていません。これらのネットワークドライバーがIPv6アドレスを使用できるようにするために必要な機能として、優れたWindowsプラットフォームの機能があり、それに続いて、kubelet、kube-proxy、およびCNIプラグインといったKubernetesの機能があります。
  • win-overlay、win-bridge、およびAzure-CNIプラグインを介したICMPプロトコルを使用したアウトバウンド通信。具体的には、Windowsデータプレーン(VFP)は、ICMPパケットの置き換えをサポートしていません。これの意味は:
    • 同じネットワーク内の宛先に向けられたICMPパケット(pingを介したPod間通信など)は期待どおりに機能し、制限はありません
    • TCP/UDPパケットは期待どおりに機能し、制限はありません
    • リモートネットワーク(Podからping経由の外部インターネット通信など)を通過するように指示されたICMPパケットは置き換えできないため、ソースにルーティングされません。
    • TCP/UDPパケットは引き続き置き換えできるため、ping <destination>curl <destination>に置き換えることで、外部への接続をデバッグできます。

これらの機能はKubernetes v1.15で追加されました。

  • kubectl port-forward
CNIプラグイン
  • Windowsリファレンスネットワークプラグインのwin-bridgeとwin-overlayは、CNI仕様v0.4.0において「CHECK」実装がないため、今のところ実装されていません。
  • Flannel VXLAN CNIについては、Windowsで次の制限があります。:
  1. Node-podの直接間接続は設計上不可能です。FlannelPR 1096を使用するローカルPodでのみ可能です
  2. VNI 4096とUDPポート4789の使用に制限されています。VNIの制限は現在取り組んでおり、将来のリリースで解決される予定です(オープンソースのflannelの変更)。これらのパラメーターの詳細については、公式のFlannel VXLANバックエンドのドキュメントをご覧ください。
DNS
  • ClusterFirstWithHostNetは、DNSでサポートされていません。Windowsでは、FQDNとしてすべての名前を「.」で扱い、PQDNでの名前解決はスキップします。
  • Linuxでは、PQDNで名前解決しようとするときに使用するDNSサフィックスリストがあります。Windowsでは、1つのDNSサフィックスしかありません。これは、そのPodのNamespaceに関連付けられているDNSサフィックスです(たとえば、mydns.svc.cluster.local)。Windowsでは、そのサフィックスだけで名前解決可能なFQDNおよびServiceまたはNameでの名前解決ができます。たとえば、defaultのNamespaceで生成されたPodには、DNSサフィックスdefault.svc.cluster.localが付けられます。WindowsのPodでは、kubernetes.default.svc.cluster.localkubernetesの両方を名前解決できますが、kubernetes.defaultkubernetes.default.svcのような中間での名前解決はできません。
  • Windowsでは、複数のDNSリゾルバーを使用できます。これらには少し異なる動作が付属しているため、ネームクエリの解決にはResolve-DNSNameユーティリティを使用することをお勧めします。
セキュリティ

Secretはノードのボリュームに平文テキストで書き込まれます(Linuxのtmpfs/in-memoryの比較として)。これはカスタマーが2つのことを行う必要があります

  1. ファイルACLを使用してSecretファイルの場所を保護する
  2. BitLockerを使って、ボリュームレベルの暗号化を使用する

RunAsUserは、現在Windowsではサポートされていません。回避策は、コンテナをパッケージ化する前にローカルアカウントを作成することです。RunAsUsername機能は、将来のリリースで追加される可能性があります。

SELinux、AppArmor、Seccomp、特性(POSIX機能)のような、Linux固有のPodセキュリティ環境の権限はサポートされていません。

さらに、既に述べたように特権付きコンテナは、Windowsにおいてサポートされていません。

API

ほとんどのKubernetes APIがWindowsでも機能することに違いはありません。そのわずかな違いはOSとコンテナランタイムの違いによるものです。特定の状況では、PodやコンテナなどのワークロードAPIの一部のプロパティが、Linuxで実装されているが、Windowsでは実行できないことを前提に設計されています。

高いレベルで、これらOSのコンセプトに違いがります。:

  • ID - Linuxでは、Integer型として表されるuserID(UID)とgroupID(GID)を使用します。ユーザー名とグループ名は正規ではありません - それらは、UID+GIDの背後にある/etc/groupsまたは/etc/passwdの単なるエイリアスです。Windowsは、Windows Security Access Manager(SAM)データベースに格納されているより大きなバイナリセキュリティ識別子(SID)を使用します。このデータベースは、ホストとコンテナ間、またはコンテナ間で共有されません。
  • ファイル権限 - Windowsは、権限とUID+GIDのビットマスクではなく、SIDに基づくアクセス制御リストを使用します
  • ファイルパス - Windowsの規則では、/ではなく\を使用します。Go IOライブラリは通常両方を受け入れ、それを機能させるだけですが、コンテナ内で解釈されるパスまたはコマンドラインを設定する場合、\が必要になる場合があります。
  • シグナル - Windowsのインタラクティブなアプリは終了を異なる方法で処理し、次の1つ以上を実装できます。:
    • UIスレッドは、WM_CLOSEを含む明確に定義されたメッセージを処理します
    • コンソールアプリは、コントロールハンドラーを使用してctrl-cまたはctrl-breakを処理します
    • サービスは、SERVICE_CONTROL_STOP制御コードを受け入れることができるサービスコントロールハンドラー関数を登録します。

終了コードは、0が成功、0以外が失敗の場合と同じ規則に従います。特定のエラーコードは、WindowsとLinuxで異なる場合があります。ただし、Kubernetesのコンポーネント(kubelet、kube-proxy)から渡される終了コードは変更されていません。

V1.Container
  • V1.Container.ResourceRequirements.limits.cpuおよびV1.Container.ResourceRequirements.limits.memory - Windowsは、CPU割り当てにハード制限を使用しません。代わりに、共有システムが使用されます。ミリコアに基づく既存のフィールドは、Windowsスケジューラーによって追従される相対共有にスケーリングされます。参照: kuberuntime/helpers_windows.go参照: resource controls in Microsoft docs
    • Huge Pagesは、Windowsコンテナランタイムには実装されてないので、使用できません。コンテナに対して設定できないユーザー特権を主張する必要があります。
  • V1.Container.ResourceRequirements.requests.cpuおよびV1.Container.ResourceRequirements.requests.memory - リクエストはノードの利用可能なリソースから差し引かれるので、ノードのオーバープロビジョニングを回避するために使用できます。ただし、過剰にプロビジョニングされたノードのリソースを保証するために使用することはできません。オペレーターが完全にプロビジョニングし過ぎないようにする場合は、ベストプラクティスとしてこれらをすべてのコンテナに適用する必要があります。
  • V1.Container.SecurityContext.allowPrivilegeEscalation - Windowsでは使用できません、接続されている機能はありません
  • V1.Container.SecurityContext.Capabilities - POSIX機能はWindowsでは実装されていません
  • V1.Container.SecurityContext.privileged - Windowsでは特権コンテナをサポートしていません
  • V1.Container.SecurityContext.procMount - Windowsでは/procファイルシステムがありません
  • V1.Container.SecurityContext.readOnlyRootFilesystem - Windowsでは使用できません、レジストリおよびシステムプロセスがコンテナ内で実行するには、書き込みアクセスが必要です
  • V1.Container.SecurityContext.runAsGroup - Windowsでは使用できません、GIDのサポートもありません
  • V1.Container.SecurityContext.runAsNonRoot - Windowsではrootユーザーが存在しません。最も近いものは、ノードに存在しないIDであるContainerAdministratorです。
  • V1.Container.SecurityContext.runAsUser - Windowsでは使用できません。intとしてのUIDはサポートされていません。
  • V1.Container.SecurityContext.seLinuxOptions - Windowsでは使用できません、SELinuxがありません
  • V1.Container.terminationMessagePath - これは、Windowsが単一ファイルのマッピングをサポートしないという点でいくつかの制限があります。デフォルト値は/dev/termination-logであり、デフォルトではWindowsに存在しないため動作します。
V1.Pod
  • V1.Pod.hostIPC、v1.pod.hostpid - Windowsではホストのネームスペースを共有することはできません
  • V1.Pod.hostNetwork - ホストのネットワークを共有するためのWindows OSサポートはありません
  • V1.Pod.dnsPolicy - ClusterFirstWithHostNet - Windowsではホストネットワーキングがサポートされていないため、サポートされていません。
  • V1.Pod.podSecurityContext - 以下のV1.PodSecurityContextを参照
  • V1.Pod.shareProcessNamespace - これはベータ版の機能であり、Windowsに実装されていないLinuxのNamespace機能に依存しています。Windowsでは、プロセスのネームスペースまたはコンテナのルートファイルシステムを共有できません。共有できるのはネットワークだけです。
  • V1.Pod.terminationGracePeriodSeconds - これはWindowsのDockerに完全には実装されていません。リファレンスを参照してください。今日の動作では、ENTRYPOINTプロセスにCTRL_SHUTDOWN_EVENTが送信され、Windowsではデフォルトで5秒待機し、最後に通常のWindowsシャットダウン動作を使用してすべてのプロセスをシャットダウンします。5秒のデフォルトは、実際にはWindowsレジストリーコンテナ内にあるため、コンテナ作成時にオーバーライドできます。
  • V1.Pod.volumeDevices - これはベータ機能であり、Windowsには実装されていません。Windowsでは、rawブロックデバイスをPodに接続できません。
  • V1.Pod.volumes-EmptyDir、Secret、ConfigMap、HostPath - すべて動作し、TestGridにテストがあります
    • V1.emptyDirVolumeSource - ノードのデフォルトのメディアはWindowsのディスクです。Windowsでは、RAMディスクが組み込まれていないため、メモリはサポートされていません。
  • V1.VolumeMount.mountPropagation - mount propagationは、Windowsではサポートされていません。
V1.PodSecurityContext

Windowsでは、PodSecurityContextフィールドはどれも機能しません。これらは参照用にここにリストされています。

  • V1.PodSecurityContext.SELinuxOptions - SELinuxは、Windowsでは使用できません
  • V1.PodSecurityContext.RunAsUser - UIDを提供しますが、Windowsでは使用できません
  • V1.PodSecurityContext.RunAsGroup - GIDを提供しますが、Windowsでは使用できません
  • V1.PodSecurityContext.RunAsNonRoot - Windowsにはrootユーザーがありません。最も近いものは、ノードに存在しないIDであるContainerAdministratorです。
  • V1.PodSecurityContext.SupplementalGroups - GIDを提供しますが、Windowsでは使用できません
  • V1.PodSecurityContext.Sysctls - これらはLinuxのsysctlインターフェースの一部です。Windowsには同等のものはありません。

ヘルプとトラブルシューティングを学ぶ

Kubernetesクラスターのトラブルシューティングの主なヘルプソースは、トラブルシューティングページから始める必要があります。このセクションには、いくつか追加的な、Windows固有のトラブルシューティングヘルプが含まれています。ログは、Kubernetesにおけるトラブルシューティング問題の重要な要素です。他のコントリビューターからトラブルシューティングの支援を求めるときは、必ずそれらを含めてください。SIG-Windowsログ収集に関するコントリビュートガイドの指示に従ってください。

  1. start.ps1が正常に完了したことをどのように確認できますか?

    ノード上でkubelet、kube-proxy、および(ネットワーキングソリューションとしてFlannelを選択した場合)flanneldホストエージェントプロセスが実行され、実行ログが個別のPowerShellウィンドウに表示されます。これに加えて、WindowsノードがKubernetesクラスターで「Ready」として表示されているはずです。

  2. Kubernetesノードのプロセスをサービスとしてバックグラウンドで実行するように構成できますか?

    Kubeletとkube-proxyは、ネイティブのWindowsサービスとして実行するように既に構成されています、障害(例えば、プロセスのクラッシュ)が発生した場合にサービスを自動的に再起動することにより、復元性を提供します。これらのノードコンポーネントをサービスとして構成するには、2つのオプションがあります。

    1. ネイティブWindowsサービスとして

      Kubeletとkube-proxyは、sc.exeを使用してネイティブのWindowsサービスとして実行できます。

      # 2つの個別のコマンドでkubeletおよびkube-proxyのサービスを作成する
      sc.exe create <component_name> binPath= "<path_to_binary> --service <other_args>"
      
      # 引数にスペースが含まれている場合は、エスケープする必要があることに注意してください。
      sc.exe create kubelet binPath= "C:\kubelet.exe --service --hostname-override 'minion' <other_args>"
      
      # サービスを開始する
      Start-Service kubelet
      Start-Service kube-proxy
      
      # サービスを停止する
      Stop-Service kubelet (-Force)
      Stop-Service kube-proxy (-Force)
      
      # サービスの状態を問い合わせる
      Get-Service kubelet
      Get-Service kube-proxy
      
    2. nssm.exeの使用

      また、nssm.exeなどの代替サービスマネージャーを使用して、これらのプロセス(flanneld、kubelet、kube-proxy)をバックグラウンドで実行することもできます。このサンプルスクリプトを使用すると、nssm.exeを利用してkubelet、kube-proxy、flanneld.exeを登録し、Windowsサービスとしてバックグラウンドで実行できます。

      register-svc.ps1 -NetworkMode <Network mode> -ManagementIP <Windows Node IP> -ClusterCIDR <Cluster subnet> -KubeDnsServiceIP <Kube-dns Service IP> -LogDir <Directory to place logs>
      
      # NetworkMode      = ネットワークソリューションとして選択されたネットワークモードl2bridge(flannel host-gw、これもデフォルト値)またはoverlay(flannel vxlan)
      # ManagementIP     = Windowsノードに割り当てられたIPアドレス。 ipconfigを使用してこれを見つけることができます
      # ClusterCIDR      = クラスターのサブネット範囲。(デフォルト値 10.244.0.0/16)
      # KubeDnsServiceIP = Kubernetes DNSサービスIP(デフォルト値 10.96.0.10)
      # LogDir           = kubeletおよびkube-proxyログがそれぞれの出力ファイルにリダイレクトされるディレクトリ(デフォルト値 C:\k)
      

      上記のスクリプトが適切でない場合は、次の例を使用してnssm.exeを手動で構成できます。

      # flanneld.exeを登録する
      nssm install flanneld C:\flannel\flanneld.exe
      nssm set flanneld AppParameters --kubeconfig-file=c:\k\config --iface=<ManagementIP> --ip-masq=1 --kube-subnet-mgr=1
      nssm set flanneld AppEnvironmentExtra NODE_NAME=<hostname>
      nssm set flanneld AppDirectory C:\flannel
      nssm start flanneld
      
      # kubelet.exeを登録
      # マイクロソフトは、mcr.microsoft.com/k8s/core/pause:1.2.0としてポーズインフラストラクチャコンテナをリリース
      nssm install kubelet C:\k\kubelet.exe
      nssm set kubelet AppParameters --hostname-override=<hostname> --v=6 --pod-infra-container-image=mcr.microsoft.com/k8s/core/pause:1.2.0 --resolv-conf="" --allow-privileged=true --enable-debugging-handlers --cluster-dns=<DNS-service-IP> --cluster-domain=cluster.local --kubeconfig=c:\k\config --hairpin-mode=promiscuous-bridge --image-pull-progress-deadline=20m --cgroups-per-qos=false  --log-dir=<log directory> --logtostderr=false --enforce-node-allocatable="" --network-plugin=cni --cni-bin-dir=c:\k\cni --cni-conf-dir=c:\k\cni\config
      nssm set kubelet AppDirectory C:\k
      nssm start kubelet
      
      # kube-proxy.exeを登録する (l2bridge / host-gw)
      nssm install kube-proxy C:\k\kube-proxy.exe
      nssm set kube-proxy AppDirectory c:\k
      nssm set kube-proxy AppParameters --v=4 --proxy-mode=kernelspace --hostname-override=<hostname>--kubeconfig=c:\k\config --enable-dsr=false --log-dir=<log directory> --logtostderr=false
      nssm.exe set kube-proxy AppEnvironmentExtra KUBE_NETWORK=cbr0
      nssm set kube-proxy DependOnService kubelet
      nssm start kube-proxy
      
      # kube-proxy.exeを登録する (overlay / vxlan)
      nssm install kube-proxy C:\k\kube-proxy.exe
      nssm set kube-proxy AppDirectory c:\k
      nssm set kube-proxy AppParameters --v=4 --proxy-mode=kernelspace --feature-gates="WinOverlay=true" --hostname-override=<hostname> --kubeconfig=c:\k\config --network-name=vxlan0 --source-vip=<source-vip> --enable-dsr=false --log-dir=<log directory> --logtostderr=false
      nssm set kube-proxy DependOnService kubelet
      nssm start kube-proxy
      

      最初のトラブルシューティングでは、nssm.exeで次のフラグを使用して、stdoutおよびstderrを出力ファイルにリダイレクトできます。:

      nssm set <Service Name> AppStdout C:\k\mysvc.log
      nssm set <Service Name> AppStderr C:\k\mysvc.log
      

      詳細については、公式のnssmの使用法のドキュメントを参照してください。

  3. Windows Podにネットワーク接続がありません

    仮想マシンを使用している場合は、すべてのVMネットワークアダプターでMACスプーフィングが有効になっていることを確認してください。

  4. Windows Podが外部リソースにpingできません

    現在、Windows Podには、ICMPプロトコル用にプログラムされた送信ルールはありません。ただし、TCP/UDPはサポートされています。クラスター外のリソースへの接続を実証する場合は、ping <IP>に対応するcurl <IP>コマンドに置き換えてください。

    それでも問題が解決しない場合は、cni.confのネットワーク構成に値する可能性があるので、いくつかの特別な注意が必要です。この静的ファイルはいつでも編集できます。構成の更新は、新しく作成されたすべてのKubernetesリソースに適用されます。

    Kubernetesのネットワーキング要件の1つ(参照Kubernetesモデル)は、内部でNATを使用せずにクラスター通信を行うためのものです。この要件を遵守するために、すべての通信にExceptionListがあり、アウトバウンドNATが発生しないようにします。ただし、これは、クエリしようとしている外部IPをExceptionListから除外する必要があることも意味します。そうして初めて、Windows PodからのトラフィックがSNAT処理され、外部からの応答を受信できるようになります。この点で、cni.confのExceptionListは次のようになります。:

    "ExceptionList": [
                    "10.244.0.0/16",  # クラスターのサブネット
                    "10.96.0.0/12",   # Serviceのサブネット
                    "10.127.130.0/24" # 管理 (ホスト) のサブネット
                ]
    
  5. WindowsノードがNodePort Serviceにアクセスできません

    ノード自体からのローカルNodePortアクセスは失敗します。これは既知の制限です。NodePortアクセスは、他のノードまたは外部クライアントから行えます。

  6. コンテナのvNICとHNSエンドポイントが削除されています

    この問題は、hostname-overrideパラメーターがkube-proxyに渡されない場合に発生する可能性があります。これを解決するには、ユーザーは次のようにホスト名をkube-proxyに渡す必要があります。:

    C:\k\kube-proxy.exe --hostname-override=$(hostname)
    
  7. flannelを使用すると、クラスターに再参加した後、ノードに問題が発生します

    以前に削除されたノードがクラスターに再参加するときはいつも、flannelDは新しいPodサブネットをノードに割り当てようとします。ユーザーは、次のパスにある古いPodサブネット構成ファイルを削除する必要があります。:

    Remove-Item C:\k\SourceVip.json
    Remove-Item C:\k\SourceVipRequest.json
    
  8. start.ps1を起動した後、flanneldが「ネットワークが作成されるのを待っています」と表示されたままになります

    この調査中の問題に関する多数の報告があります。最も可能性が高いのは、flannelネットワークの管理IPが設定されるタイミングの問題です。回避策は、単純にstart.ps1を再起動するか、次のように手動で再起動することです。:

    PS C:> [Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows_Worker_Hostname>")
    PS C:> C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows_Worker_Node_IP> --ip-masq=1 --kube-subnet-mgr=1
    
  9. /run/flannel/subnet.envがないため、Windows Podを起動できません

    これは、Flannelが正しく起動しなかったことを示しています。 flanneld.exeの再起動を試みるか、Kubernetesマスターの/run/flannel/subnet.envからWindowsワーカーノードのC:\run\flannel\subnet.envに手動でファイルをコピーすることができます。「FLANNEL_SUBNET」行を別の番号に変更します。たとえば、ノードサブネット10.244.4.1/24が必要な場合は以下となります。:

    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.4.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    
  10. WindowsノードがService IPを使用してServiceにアクセスできない

    これは、Windows上の現在のネットワークスタックの既知の制限です。ただし、Windows PodはService IPにアクセスできます。

  11. kubeletの起動時にネットワークアダプターが見つかりません

    WindowsネットワーキングスタックがKubernetesネットワーキングを動かすには、仮想アダプターが必要です。次のコマンドを実行しても結果が返されない場合(管理シェルで)、仮想ネットワークの作成(Kubeletが機能するために必要な前提条件)に失敗したことになります。:

    Get-HnsNetwork | ? Name -ieq "cbr0"
    Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
    

    ホストのネットワークアダプターが「イーサネット」ではない場合、多くの場合、start.ps1スクリプトのInterfaceNameパラメーターを修正する価値があります。そうでない場合はstart-kubelet.ps1スクリプトの出力結果を調べて、仮想ネットワークの作成中にエラーがないか確認します。

  12. Podが「Container Creating」と表示されたまま動かなくなったり、何度も再起動を繰り返します

    PauseイメージがOSバージョンと互換性があることを確認してください。説明では、OSとコンテナの両方がバージョン1803であると想定しています。それ以降のバージョンのWindowsを使用している場合は、Insiderビルドなどでは、それに応じてイメージを調整する必要があります。イメージについては、MicrosoftのDockerレジストリを参照してください。いずれにしても、PauseイメージのDockerfileとサンプルサービスの両方で、イメージに:latestのタグが付けられていると想定しています。

    Kubernetes v1.14以降、MicrosoftはPauseインフラストラクチャコンテナをmcr.microsoft.com/k8s/core/pause:1.2.0でリリースしています。

  13. DNS名前解決が正しく機能していない

    このセクションでDNSの制限を確認してください。

  14. kubectl port-forwardが「ポート転送を実行できません:wincatが見つかりません」で失敗します

    これはKubernetes 1.15、およびPauseインフラストラクチャコンテナmcr.microsoft.com/k8s/core/pause:1.2.0で実装されました。必ずこれらのバージョン以降を使用してください。 独自のPauseインフラストラクチャコンテナを構築する場合は、必ずwincatを含めてください。

  15. Windows Serverノードがプロキシの背後にあるため、Kubernetesのインストールが失敗します

    プロキシの背後にある場合は、次のPowerShell環境変数を定義する必要があります。:

    [Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
    [Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
    
  16. pauseコンテナとは何ですか

    Kubernetes Podでは、インフラストラクチャまたは「pause」コンテナが最初に作成され、コンテナエンドポイントをホストします。インフラストラクチャやワーカーコンテナなど、同じPodに属するコンテナは、共通のネットワークネームスペースとエンドポイント(同じIPとポートスペース)を共有します。Pauseコンテナは、ネットワーク構成を失うことなくクラッシュまたは再起動するワーカーコンテナに対応するために必要です。

    「pause」(インフラストラクチャ)イメージは、Microsoft Container Registry(MCR)でホストされています。docker pull mcr.microsoft.com/k8s/core/pause:1.2.0を使用してアクセスできます。詳細については、DOCKERFILEをご覧ください。

さらなる調査

これらの手順で問題が解決しない場合は、次の方法で、KubernetesのWindowsノードでWindowsコンテナを実行する際のヘルプを利用できます。:

IssueとFeatureリクエストの報告

バグのようなものがある場合、またはFeatureリクエストを行う場合は、GitHubのIssueシステムを使用してください。GitHubでIssueを開いて、SIG-Windowsに割り当てることができます。以前に報告された場合は、まずIssueリストを検索し、Issueについての経験をコメントして、追加のログを加える必要があります。SIG-Windows Slackは、チケットを作成する前に、初期サポートとトラブルシューティングのアイデアを得るための素晴らしい手段でもあります。

バグを報告する場合は、問題の再現方法に関する次のような詳細情報を含めてください。:

  • Kubernetesのバージョン: kubectlのバージョン
  • 環境の詳細: クラウドプロバイダー、OSのディストリビューション、選択したネットワーキングと構成、およびDockerのバージョン
  • 問題を再現するための詳細な手順
  • 関連するログ
  • /sig windowsでIssueにコメントして、Issueにsig/windowsのタグを付けて、SIG-Windowsメンバーが気付くようにします

次の項目

ロードマップには多くの機能があります。高レベルの簡略リストを以下に示しますが、ロードマッププロジェクトを見て、貢献することによってWindowsサポートを改善することをお勧めします。

Hyper-V分離

Hyper-V分離はKubernetesで以下のWindowsコンテナのユースケースを実現するために必要です。

  • Pod間のハイパーバイザーベースの分離により、セキュリティを強化
  • 下位互換性により、コンテナの再構築を必要とせずにノードで新しいWindows Serverバージョンを実行
  • Podの特定のCPU/NUMA設定
  • メモリの分離と予約

既存のHyper-V分離サポートは、v1.10の試験的な機能であり、上記のCRI-ContainerD機能とRuntimeClass機能を優先して将来廃止される予定です。現在の機能を使用してHyper-V分離コンテナを作成するには、kubeletのフィーチャーゲートをHyperVContainer=trueで開始し、Podにアノテーションexperimental.windows.kubernetes.io/isolation-type=hypervを含める必要があります。実験的リリースでは、この機能はPodごとに1つのコンテナに制限されています。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: iis
spec:
  selector:
    matchLabels:
      app: iis
  replicas: 3
  template:
    metadata:
      labels:
        app: iis
      annotations:
        experimental.windows.kubernetes.io/isolation-type: hyperv
    spec:
      containers:
      - name: iis
        image: microsoft/iis
        ports:
        - containerPort: 80

kubeadmとクラスターAPIを使用したデプロイ

Kubeadmは、ユーザーがKubernetesクラスターをデプロイするための事実上の標準になりつつあります。kubeadmのWindowsノードのサポートは進行中ですが、ガイドはすでにここで利用可能です。Windowsノードが適切にプロビジョニングされるように、クラスターAPIにも投資しています。

その他の主な機能

  • グループ管理サービスアカウントのベータサポート
  • その他のCNI
  • その他のストレージプラグイン

2.2.4.2 - KubernetesでWindowsコンテナをスケジュールするためのガイド

Windowsアプリケーションは、多くの組織で実行されるサービスとアプリケーションの大部分を占めます。このガイドでは、KubernetesでWindowsコンテナを構成してデプロイする手順について説明します。

目的

  • WindowsノードでWindowsコンテナを実行するサンプルのDeploymentを構成します
  • (オプション)Group Managed Service Accounts(GMSA)を使用してPodのActive Directory IDを構成します

始める前に

  • Windows Serverを実行するマスターノードとワーカーノードを含むKubernetesクラスターを作成します
  • Kubernetes上にServiceとワークロードを作成してデプロイすることは、LinuxコンテナとWindowsコンテナ共に、ほぼ同じように動作することに注意してください。クラスターとのインターフェースとなるKubectlコマンドも同じです。Windowsコンテナをすぐに体験できる例を以下セクションに用意しています。

はじめに:Windowsコンテナのデプロイ

WindowsコンテナをKubernetesにデプロイするには、最初にサンプルアプリケーションを作成する必要があります。以下のYAMLファイルの例では、簡単なウェブサーバーアプリケーションを作成しています。以下の内容でwin-webserver.yamlという名前のサービススペックを作成します。:

apiVersion: v1
kind: Service
metadata:
  name: win-webserver
  labels:
    app: win-webserver
spec:
  ports:
    # このサービスが提供するポート
    - port: 80
      targetPort: 80
  selector:
    app: win-webserver
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: win-webserver
  name: win-webserver
spec:
  replicas: 2
  selector:
    matchLabels:
      app: win-webserver
  template:
    metadata:
      labels:
        app: win-webserver
      name: win-webserver
    spec:
      containers:
        - name: windowswebserver
          image: mcr.microsoft.com/windows/servercore:ltsc2019
          command:
            - powershell.exe
            - -command
            - "<#code used from https://gist.github.com/19WAS85/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ;  ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count = $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$ip=(Get-NetAdapter | Get-NetIpAddress); $$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString='<p>IP {0} callerCount {1} ' -f $$ip[1].IPAddress,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus)  } ; "
      nodeSelector:
        kubernetes.io/os: windows
  1. すべてのノードが正常であることを確認します。:

    kubectl get nodes
    
  2. Serviceをデプロイして、Podの更新を確認します。:

    kubectl apply -f win-webserver.yaml
    kubectl get pods -o wide -w
    

    Serviceが正しくデプロイされると、両方のPodがReadyとして表示されます。watch状態のコマンドを終了するには、Ctrl + Cを押します。

  3. デプロイが成功したことを確認します。検証するために行うこと:

    • WindowsノードのPodごとの2つのコンテナにdocker psします
    • Linuxマスターからリストされた2つのPodにkubectl get podsします
    • ネットワークを介したノードとPod間通信、LinuxマスターからのPod IPのポート80に向けてcurlして、ウェブサーバーの応答をチェックします
    • docker execまたはkubectl execを使用したPod間通信、Pod間(および複数のWindowsノードがある場合はホスト間)へのpingします
    • ServiceからPodへの通信、Linuxマスターおよび個々のPodからの仮想Service IP(kubectl get servicesで表示される)にcurlします
    • サービスディスカバリ、Kubernetesのdefault DNS suffixと共にService名にcurlします
    • Inbound connectivity, curl the NodePort from the Linux master or machines outside of the cluster
    • インバウンド接続、Linuxマスターまたはクラスター外のマシンからNodePortにcurlします
    • アウトバウンド接続、kubectl execを使用したPod内からの外部IPにcurlします

可観測性

ワークロードからのログキャプチャ

ログは可観測性の重要な要素です。これにより、ユーザーはワークロードの運用面に関する洞察を得ることができ、問題のトラブルシューティングの主要な要素になります。WindowsコンテナとWindowsコンテナ内のワークロードの動作はLinuxコンテナとは異なるため、ユーザーはログの収集に苦労し、運用の可視性が制限されていました。たとえば、Windowsワークロードは通常、ETW(Windowsのイベントトレース)にログを記録するか、アプリケーションイベントログにエントリをプッシュするように構成されます。MicrosoftのオープンソースツールであるLogMonitorは、Windowsコンテナ内の構成されたログソースを監視するための推奨方法です。LogMonitorは、イベントログ、ETWプロバイダー、カスタムアプリケーションログのモニタリングをサポートしており、それらをSTDOUTにパイプして、kubectl logs <pod>で使用できます。

LogMonitor GitHubページの指示に従って、バイナリと構成ファイルをすべてのコンテナにコピーして、LogMonitorがログをSTDOUTにプッシュするために必要なエントリーポイントを追加します。

構成可能なコンテナのユーザー名の使用

Kubernetes v1.16以降、Windowsコンテナは、イメージのデフォルトとは異なるユーザー名でエントリーポイントとプロセスを実行するように構成できます。これが達成される方法は、Linuxコンテナで行われる方法とは少し異なります。詳しくはこちら.

Group Managed Service AccountsによるワークロードIDの管理

Kubernetes v1.14以降、Windowsコンテナワークロードは、Group Managed Service Accounts(GMSA)を使用するように構成できます。Group Managed Service Accountsは、自動パスワード管理、簡略化されたサービスプリンシパル名(SPN)管理、および複数のサーバー間で他の管理者に管理を委任する機能を提供する特定の種類のActive Directoryアカウントです。GMSAで構成されたコンテナは、GMSAで構成されたIDを保持しながら、外部Active Directoryドメインリソースにアクセスできます。Windowsコンテナ用のGMSAの構成と使用の詳細はこちら

TaintsとTolerations

今日のユーザーは、LinuxとWindowsのワークロードをそれぞれのOS固有のノードで維持するために、Taintsとノードセレクターのいくつかの組み合わせを使用する必要があります。これはおそらくWindowsユーザーにのみ負担をかけます。推奨されるアプローチの概要を以下に示します。主な目標の1つは、このアプローチによって既存のLinuxワークロードの互換性が損なわれないようにすることです。

OS固有のワークロードが適切なコンテナホストに確実に到達するようにする

ユーザーは、TaintsとTolerationsを使用して、Windowsコンテナを適切なホストでスケジュールできるようにすることができます。現在、すべてのKubernetesノードには次のデフォルトラベルがあります。:

  • kubernetes.io/os = [windows|linux]
  • kubernetes.io/arch = [amd64|arm64|...]

Podの仕様で"kubernetes.io/os": windowsのようなnodeSelectorが指定されていない場合、PodをWindowsまたはLinuxの任意のホストでスケジュールすることができます。WindowsコンテナはWindowsでのみ実行でき、LinuxコンテナはLinuxでのみ実行できるため、これは問題になる可能性があります。ベストプラクティスは、nodeSelectorを使用することです。

ただし、多くの場合、ユーザーには既存の多数のLinuxコンテナのdeployment、およびコミュニティHelmチャートのような既成構成のエコシステムやOperatorのようなプログラム的にPodを生成するケースがあることを理解しています。このような状況では、nodeSelectorsを追加するための構成変更をためらう可能性があります。代替策は、Taintsを使用することです。kubeletは登録中にTaintsを設定できるため、Windowsだけで実行する時に自動的にTaintを追加するように簡単に変更できます。

例:--register-with-taints='os=windows:NoSchedule'

すべてのWindowsノードにTaintを追加することにより、それらには何もスケジュールされません(既存のLinuxPodを含む)。Windows PodがWindowsノードでスケジュールされるためには、nodeSelectorがWindowsを選択することと、適切にマッチするTolerationが必要です。

nodeSelector:
    kubernetes.io/os: windows
    node.kubernetes.io/windows-build: '10.0.17763'
tolerations:
    - key: "os"
      operator: "Equal"
      value: "windows"
      effect: "NoSchedule"

同じクラスター内の複数Windowsバージョンの管理

各Podで使用されるWindows Serverのバージョンは、ノードのバージョンと一致している必要があります。 同じクラスター内で複数のWindows Serverバージョンを使用したい場合は、追加のノードラベルとnodeSelectorsを設定する必要があります。

Kubernetes 1.17では、これを簡単するために新しいラベルnode.kubernetes.io/windows-buildが自動的に追加されます。古いバージョンを実行している場合は、このラベルをWindowsノードに手動で追加することをお勧めします。

このラベルは、互換性のために一致する必要があるWindowsのメジャー、マイナー、およびビルド番号を反映しています。以下は、Windows Serverの各バージョンで現在使用されている値です。

製品番号   ビルド番号
Windows Server 201910.0.17763
Windows Server version 180910.0.17763
Windows Server version 190310.0.18362

RuntimeClassによる簡素化

RuntimeClassは、TaintsとTolerationsを使用するプロセスを簡略化するために使用できます。クラスター管理者は、これらのTaintsとTolerationsをカプセル化するために使用するRuntimeClassオブジェクトを作成できます。

  1. このファイルをruntimeClasses.ymlに保存します。これには、Windows OS、アーキテクチャ、およびバージョンに適切なnodeSelectorが含まれています。
apiVersion: node.k8s.io/v1beta1
kind: RuntimeClass
metadata:
  name: windows-2019
handler: 'docker'
scheduling:
  nodeSelector:
    kubernetes.io/os: 'windows'
    kubernetes.io/arch: 'amd64'
    node.kubernetes.io/windows-build: '10.0.17763'
  tolerations:
  - effect: NoSchedule
    key: os
    operator: Equal
    value: "windows"
  1. クラスター管理者として使用するkubectl create -f runtimeClasses.ymlを実行します
  2. Podの仕様に応じてruntimeClassName: windows-2019を追加します

例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: iis-2019
  labels:
    app: iis-2019
spec:
  replicas: 1
  template:
    metadata:
      name: iis-2019
      labels:
        app: iis-2019
    spec:
      runtimeClassName: windows-2019
      containers:
      - name: iis
        image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        resources:
          limits:
            cpu: 1
            memory: 800Mi
          requests:
            cpu: .1
            memory: 300Mi
        ports:
          - containerPort: 80
 selector:
    matchLabels:
      app: iis-2019
---
apiVersion: v1
kind: Service
metadata:
  name: iis
spec:
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
  selector:
    app: iis-2019

2.3 - ベストプラクティス

2.3.1 - 大規模クラスターの構築

クラスターはKubernetesのエージェントが動作する(物理もしくは仮想の)ノードの集合で、コントロールプレーンによって管理されます。 Kubernetes v1.30 では、最大5000ノードから構成されるクラスターをサポートします。 具体的には、Kubernetesは次の基準を 全て 満たす構成に対して適用できるように設計されています。

  • 1ノードにつきPodが110個以上存在しない
  • 5000ノード以上存在しない
  • Podの総数が150000個以上存在しない
  • コンテナの総数が300000個以上存在しない

ノードを追加したり削除したりすることによって、クラスターをスケールできます。 これを行う方法は、クラスターがどのようにデプロイされたかに依存します。

クラウドプロバイダーのリソースクォータ

クラウドプロバイダーのクォータの問題に遭遇することを避けるため、多数のノードを使ったクラスターを作成するときには次のようなことを考慮してください。

  • 次のようなクラウドリソースの増加をリクエストする
    • コンピューターインスタンス
    • CPU
    • ストレージボリューム
    • 使用中のIPアドレス
    • パケットフィルタリングのルールセット
    • ロードバランサーの数
    • ネットワークサブネット
    • ログストリーム
  • クラウドプロバイダーによる新しいインスタンスの作成に対するレート制限のため、バッチで新しいノードを立ち上げるようなクラスターのスケーリング操作を通すためには、バッチ間ですこし休止を入れます。

コントロールプレーンのコンポーネント

大きなクラスターでは、十分な計算とその他のリソースを持ったコントロールプレーンが必要になります。

特に故障ゾーンあたり1つまたは2つのコントロールプレーンインスタンスを動かす場合、最初に垂直方向にインスタンスをスケールし、垂直方向のスケーリングの効果が低下するポイントに達したら水平方向にスケールします。

フォールトトレランスを備えるために、1つの故障ゾーンに対して最低1インスタンスを動かすべきです。 Kubernetesノードは、同一故障ゾーン内のコントロールプレーンエンドポイントに対して自動的にトラフィックが向かないようにします。 しかし、クラウドプロバイダーはこれを実現するための独自の機構を持っているかもしれません。

例えばマネージドなロードバランサーを使うと、故障ゾーン A にあるkubeletやPodから発生したトラフィックを、同じく故障ゾーン A にあるコントロールプレーンホストに対してのみ送るように設定します。もし1つのコントロールプレーンホストまたは故障ゾーン A のエンドポイントがオフラインになった場合、ゾーン A にあるノードについてすべてのコントロールプレーンのトラフィックはゾーンを跨いで送信されます。それぞれのゾーンで複数のコントロールプレーンホストを動作させることは、結果としてほとんどありません。

etcdストレージ

大きなクラスターの性能を向上させるために、他の専用のetcdインスタンスにイベントオブジェクトを保存できます。

クラスターを作るときに、(カスタムツールを使って)以下のようなことができます。

  • 追加のetcdインスタンスを起動または設定する
  • イベントを保存するためにAPIサーバを設定する

大きなクラスターのためにetcdを設定・管理する詳細については、Operating etcd clusters for Kubernetesまたはkubeadmを使用した高可用性etcdクラスターの作成を見てください。

アドオンのリソース

Kubernetesのリソース制限は、メモリリークの影響やPodやコンテナが他のコンポーネントに与える他の影響を最小化することに役立ちます。 これらのリソース制限は、アプリケーションのワークロードに適用するのと同様に、アドオンのリソースにも適用されます。

例えば、ロギングコンポーネントに対してCPUやメモリ制限を設定できます。

  ...
  containers:
  - name: fluentd-cloud-logging
    image: fluent/fluentd-kubernetes-daemonset:v1
    resources:
      limits:
        cpu: 100m
        memory: 200Mi

アドオンのデフォルト制限は、アドオンを小~中規模のKubernetesクラスターで動作させたときの経験から得られたデータに基づきます。 大規模のクラスターで動作させる場合は、アドオンはデフォルト制限よりも多くのリソースを消費することが多いです。 これらの値を調整せずに大規模のクラスターをデプロイした場合、メモリー制限に達し続けるため、アドオンが継続的に停止されるかもしれません。 あるいは、CPUのタイムスライス制限により性能がでない状態で動作するかもしれません。

クラスターのアドオンのリソース制限に遭遇しないために、多くのノードで構成されるクラスターを構築する場合は次のことを考慮します。

  • いくつかのアドオンは垂直方向にスケールします - クラスターに1つのレプリカ、もしくは故障ゾーン全体にサービングされるものがあります。このようなアドオンでは、クラスターをスケールアウトしたときにリクエストと制限を増やす必要があります。
  • 数多くのアドオンは、水平方向にスケールします - より多くのPod数を動作させることで性能を向上できます - ただし、とても大きなクラスターではCPUやメモリの制限も少し引き上げる必要があるかもしれません。VerticalPodAutoscalerは、提案されたリクエストや制限の数値を提供する _recommender_ モードで動作可能です。
  • いくつかのアドオンはDaemonSetによって制御され、1ノードに1つ複製される形で動作します: 例えばノードレベルのログアグリゲーターです。水平方向にスケールするアドオンの場合と同様に、CPUやメモリ制限を少し引き上げる必要があるかもしれません。

次の項目

VerticalPodAutoscaler は、リソースのリクエストやPodの制限についての管理を手助けするためにクラスターへデプロイ可能なカスタムリソースです。 VerticalPodAutoscaler やクラスターで致命的なアドオンを含むクラスターコンポーネントをスケールする方法についてさらに知りたい場合はVertical Pod Autoscalerをご覧ください。

cluster autoscalerは、クラスターで要求されるリソース水準を満たす正確なノード数で動作できるよう、いくつかのクラウドプロバイダーと統合されています。

addon resizerは、クラスターのスケールが変化したときにアドオンの自動的なリサイズをお手伝いします。

2.3.2 - 複数のゾーンで動かす

This page describes how to run a cluster in multiple zones.

始めに

Kubernetes 1.2より、複数のゾーンにおいて単一のクラスターを運用するサポートが追加されました(GCEでは単純に"ゾーン",AWSは"アベイラビリティゾーン"と呼びますが、ここでは"ゾーン"とします)。 これは、より範囲の広いCluster Federationの軽量バージョンです(以前は"Ubernetes"の愛称で言及されていました)。 完全なCluster Federationでは、異なるリージョンやクラウドプロバイダー(あるいはオンプレミスデータセンター)内の独立したKubernetesクラスターをまとめることが可能になります。しかしながら、多くのユーザーは単に1つのクラウドプロバイダーの複数のゾーンでより可用性の高いKubernetesクラスターを運用したいと考えており、バージョン1.2におけるマルチゾーンサポート(以前は"Ubernetes Lite"の愛称で使用されていました)ではこれが可能になります。

マルチゾーンサポートは故意に限定されています: 1つのKubernetesクラスターは複数のゾーンで運用することができますが、同じリージョン(あるいはクラウドプロバイダー)のみです。現在はGCEとAWSのみが自動的にサポートされています(他のクラウドプロバイダーやベアメタル環境においても、単にノードやボリュームに追加する適切なラベルを用意して同様のサポートを追加することは容易ではありますが)。

機能性

ノードが開始された時、kubeletは自動的にそれらにゾーン情報を付したラベルを追加します。

Kubernetesはレプリケーションコントローラーやサービス内のPodをシングルゾーンクラスターにおけるノードにデプロイします(障害の影響を減らすため)。マルチゾーンクラスターでは、このデプロイの挙動はゾーンを跨いで拡張されます(障害の影響を減らすため)(これはSelectorSpreadPriorityによって可能になります)。これはベストエフォートな配置であり、つまりもしクラスターのゾーンが異種である(例:異なる数のノード,異なるタイプのノードや異なるPodのリソース要件)場合、これはゾーンを跨いだPodのデプロイを完璧に防ぐことができます。必要であれば、同種のゾーン(同一の数及びタイプのノード)を利用して不平等なデプロイの可能性を減らすことができます。

永続ボリュームが作成されると、PersistentVolumeLabelアドミッションコントローラーがそれらにゾーンラベルを付与します。スケジューラーはVolumeZonePredicateを通じて与えられたボリュームを請求するPodがそのボリュームと同じゾーンにのみ配置されることを保証します、これはボリュームはゾーンを跨いでアタッチすることができないためです。

制限

マルチゾーンサポートにはいくつか重要な制限があります:

  • 異なるゾーンはネットワーク内においてお互いに近接して位置していることが想定されているため、いかなるzone-aware routingも行われません。特に、トラフィックはゾーンを跨いだサービスを通じて行き来するため(サービスをサポートするいくつかのPodがクライアントと同じゾーンに存在していても)、これは追加のレイテンシやコストを生むかもしれません。

  • Volume zone-affinityはPersistentVolumeと共に動作し、例えばPodのスペックにおいてEBSボリュームを直接指定しても動作しません。

  • クラスターはクラウドやリージョンを跨げません(この機能はフルフェデレーションサポートが必要です)。

*ノードは複数のゾーンに存在しますが、kube-upは現在デフォルトではシングルマスターノードでビルドします。サービスは高可用性でありゾーンの障害に耐えることができますが、コントロールプレーンは単一のゾーンに配置されます。高可用性コントロールプレーンを必要とするユーザーは高可用性の説明を参照してください。

ボリュームの制限

以下の制限はtopology-aware volume bindingに記載されています。

  • 動的なプロビジョニングを使用する際のStatefulSetボリュームゾーンのデプロイは、現在Podのアフィニティあるいはアンチアフィニティと互換性がありません。

  • StatefulSetの名前がダッシュ("-")を含む場合、ボリュームゾーンのデプロイはゾーンを跨いだストレージの均一な分配を提供しない可能性があります。

  • DeploymentやPodのスペックにおいて複数のPVCを指定すると、StorageClassは特定の1つのゾーンに割り当てる必要があります、あるいはPVは特定のゾーンに静的にプロビジョンされる必要があります。もう一つの解決方法として、StatefulSetを使用すると、レプリカに対する全てのボリュームが同じゾーンにプロビジョンされます。

全体の流れ

GCEとAWSの両方にマルチゾーンのクラスターをセットアップし使用する手順について説明します。そのために、フルクラスターを用意し(MULTIZONE=trueと指定する)、kube-upを再び実行して追加のゾーンにノードを追加します(KUBE_USE_EXISTING_MASTER=trueと指定する)。

クラスターの立ち上げ

通常と同様にクラスターを作成します、しかし複数のゾーンを管理するためにMULTIZONEをクラスターに設定します。ノードをus-central1-aに作成します。

GCE:

curl -sS https://get.k8s.io | MULTIZONE=true KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-a NUM_NODES=3 bash

AWS:

curl -sS https://get.k8s.io | MULTIZONE=true KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2a NUM_NODES=3 bash

このステップは通常と同様にクラスターを立ち上げ、1つのゾーンで動作しています(しかし、MULTIZONE=trueによりマルチゾーン能力は有効になっています)。

ノードはラベルが付与される

ノードを見てください。それらがゾーン情報と共にラベルされているのが分かります。 それら全ては今のところus-central1-a (GCE)あるいはus-west-2a (AWS)にあります。ラベルはtopology.kubernetes.io/regionがリージョンに、topology.kubernetes.io/zoneはゾーンに付けられています:

kubectl get nodes --show-labels

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

NAME                     STATUS                     ROLES    AGE   VERSION          LABELS
kubernetes-master        Ready,SchedulingDisabled   <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-1,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-master
kubernetes-minion-87j9   Ready                      <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-87j9
kubernetes-minion-9vlv   Ready                      <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-a12q   Ready                      <none>   6m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-a12q

2つ目のゾーンにさらにノードを追加

それでは、現存のマスターを再利用し、現存のクラスターの異なるゾーン(us-central1-bかus-west-2b)にもう1つのノードのセットを追加しましょう。 kube-upを再び実行します.しかしKUBE_USE_EXISTING_MASTER=trueを指定することでkube-upは新しいマスターを作成せず、代わりに以前作成したものを再利用します。

GCE:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-b NUM_NODES=3 kubernetes/cluster/kube-up.sh

AWSではマスターの内部IPアドレスに加えて追加のサブネット用のネットワークCIDRを指定する必要があります:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2b NUM_NODES=3 KUBE_SUBNET_CIDR=172.20.1.0/24 MASTER_INTERNAL_IP=172.20.0.9 kubernetes/cluster/kube-up.sh

ノードをもう1度見てください。更なる3つのノードがus-central1-bに起動し、タグ付けられているはずです:

kubectl get nodes --show-labels

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

NAME                     STATUS                     ROLES    AGE   VERSION           LABELS
kubernetes-master        Ready,SchedulingDisabled   <none>   16m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-1,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-master
kubernetes-minion-281d   Ready                      <none>   2m    v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-281d
kubernetes-minion-87j9   Ready                      <none>   16m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-87j9
kubernetes-minion-9vlv   Ready                      <none>   16m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-a12q   Ready                      <none>   17m   v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-a12q
kubernetes-minion-pp2f   Ready                      <none>   2m    v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-pp2f
kubernetes-minion-wf8i   Ready                      <none>   2m    v1.13.0           beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-wf8i

ボリュームのアフィニティ

動的ボリュームを使用してボリュームを作成します(PersistentVolumeのみがゾーンアフィニティに対してサポートされています):

kubectl apply -f - <<EOF
{
  "apiVersion": "v1",
  "kind": "PersistentVolumeClaim",
  "metadata": {
    "name": "claim1",
    "annotations": {
        "volume.alpha.kubernetes.io/storage-class": "foo"
    }
  },
  "spec": {
    "accessModes": [
      "ReadWriteOnce"
    ],
    "resources": {
      "requests": {
        "storage": "5Gi"
      }
    }
  }
}
EOF

それでは、KubernetesがPVが作成されたゾーン及びリージョンを自動的にラベルしているか確認しましょう。

kubectl get pv --show-labels

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

NAME           CAPACITY   ACCESSMODES   RECLAIM POLICY   STATUS    CLAIM            STORAGECLASS    REASON    AGE       LABELS
pv-gce-mj4gm   5Gi        RWO           Retain           Bound     default/claim1   manual                    46s       topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a

では永続ボリュームクレームを使用するPodを作成します。 GCE PD / AWS EBSボリュームはゾーンを跨いでアタッチできないため、これはこのPodがボリュームと同じゾーンにのみ作成されることを意味します:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: claim1
EOF

一般的にゾーンを跨いだアタッチはクラウドプロバイダーによって許可されていないため、Podは自動的にボリュームと同じゾーンに作成されることに注意してください:

kubectl describe pod mypod | grep Node
Node:        kubernetes-minion-9vlv/10.240.0.5

ノードのラベルをチェックします:

kubectl get node kubernetes-minion-9vlv --show-labels
NAME                     STATUS    AGE    VERSION          LABELS
kubernetes-minion-9vlv   Ready     22m    v1.6.0+fff5156   beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv

Podがゾーンをまたがって配置される

レプリケーションコントローラーやサービス内のPodは自動的にゾーンに跨いでデプロイされます。まず、3つ目のゾーンに更なるノードを立ち上げましょう:

GCE:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-f NUM_NODES=3 kubernetes/cluster/kube-up.sh

AWS:

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2c NUM_NODES=3 KUBE_SUBNET_CIDR=172.20.2.0/24 MASTER_INTERNAL_IP=172.20.0.9 kubernetes/cluster/kube-up.sh

3つのゾーンにノードがあることを確認します:

kubectl get nodes --show-labels

シンプルなWebアプリケーションを動作する、3つのRCを持つguestbook-goの例を作成します:

find kubernetes/examples/guestbook-go/ -name '*.json' | xargs -I {} kubectl apply -f {}

Podは3つの全てのゾーンにデプロイされているはずです:

kubectl describe pod -l app=guestbook | grep Node
Node:        kubernetes-minion-9vlv/10.240.0.5
Node:        kubernetes-minion-281d/10.240.0.8
Node:        kubernetes-minion-olsh/10.240.0.11
kubectl get node kubernetes-minion-9vlv kubernetes-minion-281d kubernetes-minion-olsh --show-labels
NAME                     STATUS    ROLES    AGE    VERSION          LABELS
kubernetes-minion-9vlv   Ready     <none>   34m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-a,kubernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-281d   Ready     <none>   20m    v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-b,kubernetes.io/hostname=kubernetes-minion-281d
kubernetes-minion-olsh   Ready     <none>   3m     v1.13.0          beta.kubernetes.io/instance-type=n1-standard-2,topology.kubernetes.io/region=us-central1,topology.kubernetes.io/zone=us-central1-f,kubernetes.io/hostname=kubernetes-minion-olsh

ロードバランサーはクラスター内の全てのゾーンにデプロイされています; guestbook-goの例は負荷分散サービスのサンプルを含みます:

kubectl describe service guestbook | grep LoadBalancer.Ingress

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

LoadBalancer Ingress:   130.211.126.21

IPの上に設定します:

export IP=130.211.126.21

IPをcurlを通じて探索します:

curl -s http://${IP}:3000/env | grep HOSTNAME

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

  "HOSTNAME": "guestbook-44sep",

再び、複数回探索します:

(for i in `seq 20`; do curl -s http://${IP}:3000/env | grep HOSTNAME; done)  | sort | uniq

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

  "HOSTNAME": "guestbook-44sep",
  "HOSTNAME": "guestbook-hum5n",
  "HOSTNAME": "guestbook-ppm40",

ロードバランサーは、たとえPodが複数のゾーンに存在していても、全てのPodをターゲットします。

クラスターの停止

終了したら、クリーンアップします:

GCE:

KUBERNETES_PROVIDER=gce KUBE_USE_EXISTING_MASTER=true KUBE_GCE_ZONE=us-central1-f kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=gce KUBE_USE_EXISTING_MASTER=true KUBE_GCE_ZONE=us-central1-b kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-a kubernetes/cluster/kube-down.sh

AWS:

KUBERNETES_PROVIDER=aws KUBE_USE_EXISTING_MASTER=true KUBE_AWS_ZONE=us-west-2c kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=aws KUBE_USE_EXISTING_MASTER=true KUBE_AWS_ZONE=us-west-2b kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2a kubernetes/cluster/kube-down.sh

2.3.3 - ノードのセットアップの検証

ノード適合テスト

ノード適合テスト は、システムの検証とノードに対する機能テストを提供するコンテナ型のテストフレームワークです。このテストは、ノードがKubernetesの最小要件を満たしているかどうかを検証するもので、テストに合格したノードはKubernetesクラスターに参加する資格があることになります。

ノードの前提条件

適合テストを実行するにはノードは通常のKubernetesノードと同じ前提条件を満たしている必要があります。 最低でもノードに以下のデーモンがインストールされている必要があります:

  • コンテナランタイム (Docker)
  • Kubelet

ノード適合テストの実行

ノード適合テストを実行するには、以下の手順に従います:

  1. kubeletの--kubeconfigオプションの値を調べます。例:--kubeconfig=/var/lib/kubelet/config.yaml。 このテストフレームワークはKubeletのテスト用にローカルコントロールプレーンを起動するため、APIサーバーのURLとしてhttp://localhost:8080を使用します。 他にも使用できるkubeletコマンドラインパラメーターがいくつかあります:

    • --cloud-provider: --cloud-provider=gceを指定している場合は、テストを実行する前にこのフラグを取り除いてください。
  2. 以下のコマンドでノード適合テストを実行します:

# $CONFIG_DIRはKubeletのPodのマニフェストパスです。
# $LOG_DIRはテスト出力のパスです。
sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  registry.k8s.io/node-test:0.2

他アーキテクチャ向けのノード適合テストの実行

Kubernetesは他のアーキテクチャ用のノード適合テストのdockerイメージを提供しています:

ArchImage
amd64node-test-amd64
armnode-test-arm
arm64node-test-arm64

選択したテストの実行

特定のテストを実行するには、環境変数FOCUSを実行したいテストの正規表現で上書きします。

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e FOCUS=MirrorPod \ # MirrorPodテストのみを実行します
  registry.k8s.io/node-test:0.2

特定のテストをスキップするには、環境変数SKIPをスキップしたいテストの正規表現で上書きします。

sudo docker run -it --rm --privileged --net=host \
  -v /:/rootfs:ro -v $CONFIG_DIR:$CONFIG_DIR -v $LOG_DIR:/var/result \
  -e SKIP=MirrorPod \ # MirrorPodテスト以外のすべてのノード適合テストを実行します
  registry.k8s.io/node-test:0.2

ノード適合テストは、node e2e testのコンテナ化されたバージョンです。 デフォルトでは、すべての適合テストが実行されます。

理論的には、コンテナを構成し必要なボリュームを適切にマウントすれば、どのノードのe2eテストも実行できます。しかし、不適合テストを実行するためにはより複雑な設定が必要となるため、適合テストのみを実行することを強く推奨します

注意事項

  • このテストでは、ノード適合テストイメージや機能テストで使用されるコンテナのイメージなど、いくつかのdockerイメージがノード上に残ります。
  • このテストでは、ノード上にデッドコンテナが残ります。これらのコンテナは機能テスト中に作成されます。

2.3.4 - Podセキュリティ標準の強制

このページでは、Podセキュリティの標準を強制する際のベストプラクティスの概要を説明します。

ビルトインPodセキュリティアドミッションコントローラーの使用

FEATURE STATE: Kubernetes v1.25 [stable]

Podセキュリティアドミッションコントローラーは、非推奨のPodSecurityPolicyを置き換えます。

すべてのNamespaceに設定する

設定が全く無いNamespaceは、クラスターのセキュリティモデルにおいて重大な弱点とみなすべきです。 各Namespaceで発生するワークロードのタイプを時間をかけて分析し、Podセキュリティ標準を参照しながら、それぞれに適切なレベルを決定することを推奨します。 また、ラベルのないNamespaceは、まだ評価されていないことだけを示すべきです。

すべてのNamespaceのワークロードが同じセキュリティ要件を持つというシナリオでは、PodSecurityラベルを一括適用する方法をで説明しています。

最小特権の原則を採用する

理想的な世界では、すべてのNamespaceのPodがrestrictedポリシーの要件を満たすでしょう。 しかし、ワークロードの中には正当な理由で昇格した特権を必要とするものもあるため、それは不可能であり、現実的でもありません。

  • privilegedワークロードを許可するNamespaceは、適切なアクセス制御を確立し、実施すべきである。
  • 最小権限のNamespaceで実行されるワークロードについては、そのワークロードのセキュリティ要件に関するドキュメントを整備する。可能であれば、それらの要件がどのように制約される可能性があるのかを考慮する。

マルチモード戦略の採用

Podセキュリティアドミッションコントローラーのauditwarnモードを使用すると、既存のワークロードを破壊することなく、Podに関する重要なセキュリティインサイトを簡単に収集できます。

すべてのNamespaceでこれらのモードを有効にし、最終的にenforceしたいレベルやバージョンに設定するのがよい方法です。 このフェーズで生成される警告と監査注釈は、その状態への指針となります。 ワークロード作成者が希望のレベルに収まるように変更することを期待している場合は、warnモードを有効にしてください。 監査ログを使用して、希望のレベルに収まるように変更を監視/推進することを期待している場合は、auditモードを有効にしてください。

enforceモードが希望通りの値に設定されている場合でも、これらのモードはいくつかの異なる方法で役立ちます。

  • warnenforceと同じレベルに設定すると、バリデーションを通過しないPod(またはPodテンプレートを持つリソース)を作成しようとしたときに、クライアントが警告を受け取るようになります。これにより、対象のリソースを更新して準拠させることができます。
  • enforceを特定の最新バージョンではないに固定するNamespaceでは、auditwarnモードをenforceと同じレベルに設定するが、最新バージョンに対して設定することで、以前のバージョンでは許可されていたが、現在のベストプラクティスでは許可されていない設定を可視化することができます。

サードパーティによる代替案

Kubernetesエコシステムでは、セキュリティプロファイルを強制するための他の選択肢も開発されています。

ビルトインソリューション(PodSecurityアドミッションコントローラーなど)とサードパーティツールのどちらを選ぶかは、あなたの状況次第です。 どのようなソリューションを評価する場合でも、サプライチェーンの信頼が非常に重要です。最終的には、前述のアプローチのどれを使っても、何もしないよりはましでしょう。

2.3.5 - PKI証明書とその要件

Kubernetesでは、TLS認証のためにPKI証明書が必要です。 kubeadmでKubernetesをインストールする場合、必要な証明書は自動で生成されます。 自身で証明書を作成することも可能です。例えば、秘密鍵をAPIサーバーに保持しないことで、管理をよりセキュアにする場合が挙げられます。 本ページでは、クラスターに必要な証明書について説明します。

クラスターではどのように証明書が使われているのか

Kubernetesは下記の用途でPKIを必要とします:

  • kubeletがAPIサーバーの認証をするためのクライアント証明書
  • APIサーバーのエンドポイント用サーバー証明書
  • クラスターの管理者がAPIサーバーの認証を行うためのクライアント証明書
  • APIサーバーがkubeletと通信するためのクライアント証明書
  • APIサーバーがetcdと通信するためのクライアント証明書
  • controller managerがAPIサーバーと通信するためのクライアント証明書およびkubeconfig
  • スケジューラーがAPIサーバーと通信するためのクライアント証明書およびkubeconfig
  • front-proxy用のクライアント証明書およびサーバー証明書

さらに、etcdはクライアントおよびピア間の認証に相互TLS通信を実装しています。

証明書の保存場所

kubeadmを使用してKubernetesをインストールする場合、証明書は/etc/kubernetes/pkiに保存されます。このドキュメントの全てのパスは、そのディレクトリの相対パスを表します。

手動で証明書を設定する

kubeadmで証明書を生成したくない場合は、下記の方法のいずれかで手動で生成可能です。

単一ルート認証局

管理者によりコントロールされた、単一ルート認証局の作成が可能です。このルート認証局は複数の中間認証局を作る事が可能で、作成はKubernetes自身に委ねます。

必要な認証局:

パスデフォルトCN説明
ca.crt,keykubernetes-caKubernetes全体の認証局   
etcd/ca.crt,keyetcd-caetcd用              
front-proxy-ca.crt,keykubernetes-front-proxy-cafront-end proxy用   

上記の認証局に加えて、サービスアカウント管理用に公開鍵/秘密鍵のペア(sa.keysa.pub)を取得する事が必要です。

全ての証明書

CAの秘密鍵をクラスターにコピーしたくない場合、自身で全ての証明書を作成できます。

必要な証明書:

デフォルトCN親認証局組織       種類ホスト名 (SAN)
kube-etcdetcd-caserver, client<hostname>, <Host_IP>, localhost, 127.0.0.1
kube-etcd-peeretcd-caserver, client<hostname>, <Host_IP>, localhost, 127.0.0.1
kube-etcd-healthcheck-clientetcd-caclient
kube-apiserver-etcd-clientetcd-caclient
kube-apiserverkubernetes-caserver<hostname>, <Host_IP>, <advertise_IP>, [1]
kube-apiserver-kubelet-clientkubernetes-casystem:mastersclient
front-proxy-clientkubernetes-front-proxy-caclient

[1]: クラスターに接続するIPおよびDNS名( kubeadmを使用する場合と同様、ロードバランサーのIPおよびDNS名、kuberneteskubernetes.defaultkubernetes.default.svckubernetes.default.svc.clusterkubernetes.default.svc.cluster.local)

kindは下記のx509の鍵用途のタイプにマッピングされます:

種類鍵の用途    
serverdigital signature, key encipherment, server auth
clientdigital signature, key encipherment, client auth

証明書のパス

証明書は推奨パスに配置するべきです(kubeadmを使用する場合と同様)。 パスは場所に関係なく与えられた引数で特定されます。

デフォルトCN鍵の推奨パス       証明書の推奨パス      コマンド鍵を指定する引数証明書を指定する引数
etcd-caetcd/ca.keyetcd/ca.crtkube-apiserver--etcd-cafile
kube-apiserver-etcd-clientapiserver-etcd-client.keyapiserver-etcd-client.crtkube-apiserver--etcd-keyfile--etcd-certfile
kubernetes-caca.keyca.crtkube-apiserver--client-ca-file
kubernetes-caca.keyca.crtkube-controller-manager--cluster-signing-key-file--client-ca-file, --root-ca-file, --cluster-signing-cert-file
kube-apiserverapiserver.keyapiserver.crtkube-apiserver--tls-private-key-file--tls-cert-file
kube-apiserver-kubelet-clientapiserver-kubelet-client.keyapiserver-kubelet-client.crtkube-apiserver--kubelet-client-key--kubelet-client-certificate
front-proxy-cafront-proxy-ca.keyfront-proxy-ca.crtkube-apiserver--requestheader-client-ca-file
front-proxy-cafront-proxy-ca.keyfront-proxy-ca.crtkube-controller-manager--requestheader-client-ca-file
front-proxy-clientfront-proxy-client.keyfront-proxy-client.crtkube-apiserver--proxy-client-key-file--proxy-client-cert-file
etcd-caetcd/ca.keyetcd/ca.crtetcd--trusted-ca-file, --peer-trusted-ca-file
kube-etcdetcd/server.keyetcd/server.crtetcd--key-file--cert-file
kube-etcd-peeretcd/peer.keyetcd/peer.crtetcd--peer-key-file--peer-cert-file
etcd-caetcd/ca.crtetcdctl--cacert
kube-etcd-healthcheck-clientetcd/healthcheck-client.keyetcd/healthcheck-client.crtetcdctl--key--cert

サービスアカウント用の鍵ペアについても同様です。

秘密鍵のパス      公開鍵のパス    コマンド引数
sa.keykube-controller-managerservice-account-private
sa.pubkube-apiserverservice-account-key

ユーザアカウント用に証明書を設定する

管理者アカウントおよびサービスアカウントは手動で設定しなければなりません。

ファイル名クレデンシャル名デフォルトCN組織      
admin.confdefault-adminkubernetes-adminsystem:masters
kubelet.confdefault-authsystem:node:<nodeName> (see note)system:nodes
controller-manager.confdefault-controller-managersystem:kube-controller-manager
scheduler.confdefault-schedulersystem:kube-scheduler
  1. 各コンフィグ毎に、CN名と組織を指定してx509証明書と鍵ペアを生成してください。

  2. 以下のように、各コンフィグでkubectlを実行してください。

KUBECONFIG=<filename> kubectl config set-cluster default-cluster --server=https://<host ip>:6443 --certificate-authority <path-to-kubernetes-ca> --embed-certs
KUBECONFIG=<filename> kubectl config set-credentials <credential-name> --client-key <path-to-key>.pem --client-certificate <path-to-cert>.pem --embed-certs
KUBECONFIG=<filename> kubectl config set-context default-system --cluster default-cluster --user <credential-name>
KUBECONFIG=<filename> kubectl config use-context default-system

これらのファイルは以下のように利用されます:

ファイル名コマンドコメント
admin.confkubectlクラスターの管理者設定用
kubelet.confkubeletクラスターの各ノードに1つ必要です。
controller-manager.confkube-controller-managermanifests/kube-controller-manager.yamlのマニフェストファイルに追記する必要があります。
scheduler.confkube-schedulermanifests/kube-scheduler.yamlのマニフェストファイルに追記する必要があります。

3 - コンセプト

本セクションは、Kubernetesシステムの各パートと、クラスターを表現するためにKubernetesが使用する抽象概念について学習し、Kubernetesの仕組みをより深く理解するのに役立ちます。

概要

Kubernetesを機能させるには、Kubernetes API オブジェクト を使用して、実行したいアプリケーションやその他のワークロード、使用するコンテナイメージ、レプリカ(複製)の数、どんなネットワークやディスクリソースを利用可能にするかなど、クラスターの desired state (望ましい状態)を記述します。desired state (望ましい状態)をセットするには、Kubernetes APIを使用してオブジェクトを作成します。通常はコマンドラインインターフェース kubectl を用いてKubernetes APIを操作しますが、Kubernetes APIを直接使用してクラスターと対話し、desired state (望ましい状態)を設定、または変更することもできます。

一旦desired state (望ましい状態)を設定すると、Pod Lifecycle Event Generator(PLEG)を使用したKubernetes コントロールプレーンが機能し、クラスターの現在の状態をdesired state (望ましい状態)に一致させます。そのためにKubernetesはさまざまなタスク(たとえば、コンテナの起動または再起動、特定アプリケーションのレプリカ数のスケーリング等)を自動的に実行します。Kubernetesコントロールプレーンは、クラスターで実行されている以下のプロセスで構成されています。

  • Kubernetes Master: kube-apiserverkube-controller-managerkube-scheduler の3プロセスの集合です。これらのプロセスはクラスター内の一つのノード上で実行されます。実行ノードはマスターノードとして指定します。
  • クラスター内の個々の非マスターノードは、それぞれ2つのプロセスを実行します。
    • kubelet: Kubernetes Masterと通信します。
    • kube-proxy: 各ノードのKubernetesネットワークサービスを反映するネットワークプロキシです。

Kubernetesオブジェクト

Kubernetesには、デプロイ済みのコンテナ化されたアプリケーションやワークロード、関連するネットワークとディスクリソース、クラスターが何をしているかに関するその他の情報といった、システムの状態を表現する抽象が含まれています。これらの抽象は、Kubernetes APIのオブジェクトによって表現されます。詳細については、Kubernetesオブジェクトについて知るをご覧ください。

基本的なKubernetesのオブジェクトは次のとおりです。

Kubernetesには、コントローラーに依存して基本オブジェクトを構築し、追加の機能と便利な機能を提供する高レベルの抽象化も含まれています。これらには以下のものを含みます:

Kubernetesコントロールプレーン

Kubernetesマスターや kubeletプロセスといったKubernetesコントロールプレーンのさまざまなパーツは、Kubernetesがクラスターとどのように通信するかを統制します。コントロールプレーンはシステム内のすべてのKubernetesオブジェクトの記録を保持し、それらのオブジェクトの状態を管理するために継続的制御ループを実行します。コントロールプレーンの制御ループは常にクラスターの変更に反応し、システム内のすべてのオブジェクトの実際の状態が、指定した状態に一致するように動作します。

たとえば、Kubernetes APIを使用してDeploymentを作成する場合、システムには新しいdesired state (望ましい状態)が提供されます。Kubernetesコントロールプレーンは、そのオブジェクトの作成を記録します。そして、要求されたアプリケーションの開始、およびクラスターノードへのスケジューリングにより指示を完遂します。このようにしてクラスターの実際の状態を望ましい状態に一致させます。

Kubernetesマスター

Kubernetesのマスターは、クラスターの望ましい状態を維持する責務を持ちます。kubectl コマンドラインインターフェースを使用するなどしてKubernetesとやり取りするとき、ユーザーは実際にはクラスターにあるKubernetesのマスターと通信しています。

「マスター」とは、クラスター状態を管理するプロセスの集合を指します。通常これらのプロセスは、すべてクラスター内の単一ノードで実行されます。このノードはマスターとも呼ばれます。マスターは、可用性と冗長性のために複製することもできます。

Kubernetesノード

クラスターのノードは、アプリケーションとクラウドワークフローを実行するマシン(VM、物理サーバーなど)です。Kubernetesのマスターは各ノードを制御します。運用者自身がノードと直接対話することはほとんどありません。

次の項目

コンセプトページを追加したい場合は、 ページテンプレートの使用 のコンセプトページタイプとコンセプトテンプレートに関する情報を確認してください。

3.1 - 概要

Kubernetesは、宣言的な構成管理と自動化を促進し、コンテナ化されたワークロードやサービスを管理するための、ポータブルで拡張性のあるオープンソースのプラットフォームです。Kubernetesは巨大で急速に成長しているエコシステムを備えており、それらのサービス、サポート、ツールは幅広い形で利用可能です。

このページでは、Kubernetesの概要について説明します。

Kubernetesは、宣言的な構成管理と自動化を促進し、コンテナ化されたワークロードやサービスを管理するための、ポータブルで拡張性のあるオープンソースのプラットフォームです。Kubernetesは巨大で急速に成長しているエコシステムを備えており、それらのサービス、サポート、ツールは幅広い形で利用可能です。

Kubernetesの名称は、ギリシャ語に由来し、操舵手やパイロットを意味しています。 "K"と"s"の間にある8つの文字を数えることから、K8sが略語として使われています。 Googleは2014年にKubernetesプロジェクトをオープンソース化しました。 Kubernetesは、本番環境で大規模なワークロードを稼働させたGoogleの15年以上の経験と、コミュニティからの最高のアイディアや実践を組み合わせています。

過去を振り返ってみると

過去を振り返って、Kubernetesがなぜこんなに便利なのかを見てみましょう。

Deployment evolution

仮想化ができる前の時代におけるデプロイ (Traditional deployment): 初期の頃は、組織は物理サーバー上にアプリケーションを実行させていました。物理サーバー上でアプリケーションのリソース制限を設定する方法がなかったため、リソースの割当問題が発生していました。例えば、複数のアプリケーションを実行させた場合、ひとつのアプリケーションがリソースの大半を消費してしまうと、他のアプリケーションのパフォーマンスが低下してしまうことがありました。この解決方法は、それぞれのアプリケーションを別々の物理サーバーで動かすことでした。しかし、リソースが十分に活用できなかったため、拡大しませんでした。また組織にとって多くの物理サーバーを維持することは費用がかかりました。

仮想化を使ったデプロイ (Virtualized deployment): ひとつの解決方法として、仮想化が導入されました。1台の物理サーバーのCPU上で、複数の仮想マシン(VM)を実行させることができるようになりました。仮想化によりアプリケーションをVM毎に隔離する事ができ、ひとつのアプリケーションの情報が他のアプリケーションから自由にアクセスさせないといったセキュリティレベルを提供することができます。

仮想化により、物理サーバー内のリソース使用率が向上し、アプリケーションの追加や更新が容易になり、ハードウェアコストの削減などスケーラビリティが向上します。仮想化を利用すると、物理リソースのセットを使い捨て可能な仮想マシンのクラスターとして提示することができます。

各VMは、仮想ハードウェア上で各自のOSを含んだ全コンポーネントを実行する完全なマシンです。

コンテナを使ったデプロイ (Container deployment): コンテナはVMと似ていますが、アプリケーション間でオペレーティング・システム(OS)を共有できる緩和された分離特性を持っています。そのため、コンテナは軽量だといわれます。VMと同じように、コンテナは各自のファイルシステム、CPUの共有、メモリー、プロセス空間等を持っています。基盤のインフラストラクチャから分離されているため、クラウドやOSディストリビューションを越えて移動することが可能です。

コンテナは、その他にも次のようなメリットを提供するため、人気が高まっています。

  • アジャイルアプリケーションの作成とデプロイ: VMイメージの利用時と比較して、コンテナイメージ作成の容易さと効率性が向上します。
  • 継続的な開発、インテグレーションとデプロイ: 信頼できる頻繁なコンテナイメージのビルドと、素早く簡単にロールバックすることが可能なデプロイを提供します。(イメージが不変であれば)
  • 開発者と運用者の関心を分離: アプリケーションコンテナイメージの作成は、デプロイ時ではなく、ビルド/リリース時に行います。それによって、インフラストラクチャとアプリケーションを分離します。
  • 可観測性: OSレベルの情報とメトリクスだけではなく、アプリケーションの稼働状態やその他の警告も表示します。
  • 開発、テスト、本番環境を越えた環境の一貫性: クラウドで実行させるのと同じようにノートPCでも実行させる事ができます。
  • クラウドとOSディストリビューションの可搬性: Ubuntu、RHEL、CoreOS上でも、オンプレミスも、主要なパブリッククラウドでも、それ以外のどんな環境でも、実行できます。
  • アプリケーション中心の管理: 仮想マシン上でOSを実行するから、論理リソースを使用してOS上でアプリケーションを実行するへと抽象度のレベルを向上させます。
  • 疎結合、分散化、拡張性、柔軟性のあるマイクロサービス: アプリケーションを小さく、同時にデプロイと管理が可能な独立した部品に分割されます。1台の大きな単一目的のマシン上に実行するモノリシックなスタックではありません。
  • リソースの分割: アプリケーションのパフォーマンスが予測可能です。
  • リソースの効率的な利用: 高い効率性と集約性が可能です。

Kubernetesが必要な理由と提供する機能

コンテナは、アプリケーションを集約して実行する良い方法です。本番環境では、アプリケーションを実行しダウンタイムが発生しないように、コンテナを管理する必要があります。例えば、コンテナがダウンした場合、他のコンテナを起動する必要があります。このような動作がシステムに組込まれていると、管理が簡単になるのではないでしょうか?

そこを助けてくれるのがKubernetesです! Kubernetesは分散システムを弾力的に実行するフレームワークを提供してくれます。あなたのアプリケーションのためにスケーリングとフェイルオーバーの面倒を見てくれて、デプロイのパターンなどを提供します。例えば、Kubernetesはシステムにカナリアデプロイを簡単に管理することができます。

Kubernetesは以下を提供します。

  • サービスディスカバリーと負荷分散 Kubernetesは、DNS名または独自のIPアドレスを使ってコンテナを公開することができます。コンテナへのトラフィックが多い場合は、Kubernetesは負荷分散し、ネットワークトラフィックを振り分けることができるため、デプロイが安定します。
  • ストレージ オーケストレーション Kubernetesは、ローカルストレージやパブリッククラウドプロバイダーなど、選択したストレージシステムを自動でマウントすることができます。
  • 自動化されたロールアウトとロールバック Kubernetesを使うとデプロイしたコンテナのあるべき状態を記述することができ、制御されたスピードで実際の状態をあるべき状態に変更することができます。例えば、アプリケーションのデプロイのために、新しいコンテナの作成や既存コンテナの削除、新しいコンテナにあらゆるリソースを適用する作業を、Kubernetesで自動化できます。
  • 自動ビンパッキング コンテナ化されたタスクを実行するノードのクラスターをKubernetesへ提供します。各コンテナがどれくらいCPUやメモリー(RAM)を必要とするのかをKubernetesに宣言することができます。Kubernetesはコンテナをノードにあわせて調整することができ、リソースを最大限に活用してくれます。
  • 自己修復 Kubernetesは、処理が失敗したコンテナを再起動し、コンテナを入れ替え、定義したヘルスチェックに応答しないコンテナを強制終了します。処理の準備ができるまでは、クライアントに通知しません。
  • 機密情報と構成管理 Kubernetesは、パスワードやOAuthトークン、SSHキーなどの機密の情報を保持し、管理することができます。機密情報をデプロイし、コンテナイメージを再作成することなくアプリケーションの構成情報を更新することができます。スタック構成の中で機密情報を晒してしまうこともありません。

Kubernetesにないもの

Kubernetesは、従来型の全部入りなPaaS(Platform as a Service)のシステムではありません。Kubernetesはハードウェアレベルではなく、コンテナレベルで動作するため、デプロイ、スケーリング、負荷分散といったPaaSが提供するのと共通の機能をいくつか提供し、またユーザーはロギングやモニタリング及びアラートを行うソリューションを統合できます。また一方、Kubernetesはモノリシックでなく、標準のソリューションは選択が自由で、追加と削除が容易な構成になっています。Kubernetesは開発プラットフォーム構築のためにビルディングブロックを提供しますが、重要な部分はユーザーの選択と柔軟性を維持しています。

Kubernetesは...

  • サポートするアプリケーションの種類を制限しません。Kubernetesは、ステートレス、ステートフルやデータ処理のワークロードなど、非常に多様なワークロードをサポートすることを目的としています。アプリケーションがコンテナで実行できるのであれば、Kubernetes上で問題なく実行できるはずです。
  • ソースコードのデプロイやアプリケーションのビルドは行いません。継続的なインテグレーション、デリバリー、デプロイ(CI/CD)のワークフローは、技術的な要件だけでなく組織の文化や好みで決められます。
  • ミドルウェア(例:メッセージバス)、データ処理フレームワーク(例:Spark)、データベース(例:MySQL)、キャッシュ、クラスターストレージシステム(例:Ceph)といったアプリケーションレベルの機能を組み込んで提供しません。それらのコンポーネントは、Kubernetes上で実行することもできますし、Open Service Brokerのようなポータブルメカニズムを経由してKubernetes上で実行されるアプリケーションからアクセスすることも可能です。
  • ロギング、モニタリングやアラートを行うソリューションは指定しません。PoCとしていくつかのインテグレーションとメトリクスを収集し出力するメカニズムを提供します。
  • 構成言語/システム(例:Jsonnet)の提供も指示もしません。任意の形式の宣言型仕様の対象となる可能性のある宣言型APIを提供します。
  • 統合的なマシンの構成、メンテナンス、管理、または自己修復を行うシステムは提供も採用も行いません。
  • さらに、Kubernetesは単なるオーケストレーションシステムではありません。実際には、オーケストレーションの必要性はありません。オーケストレーションの技術的な定義は、「最初にAを実行し、次にB、その次にCを実行」のような定義されたワークフローの実行です。対照的にKubernetesは、現在の状態から提示されたあるべき状態にあわせて継続的に維持するといった、独立していて構成可能な制御プロセスのセットを提供します。AからCへどのように移行するかは問題ではありません。集中管理も必要ありません。これにより、使いやすく、より強力で、堅牢で、弾力性と拡張性があるシステムが実現します。

次の項目

3.1.1 - Kubernetesのコンポーネント

Kubernetesクラスターはコントロールプレーンのコンポーネントとノードと呼ばれるマシン群で構成されています。

Kubernetesをデプロイすると、クラスターが展開されます。

Kubernetesクラスターは、 コンテナ化されたアプリケーションを実行する、ノードと呼ばれるワーカーマシンの集合です。すべてのクラスターには少なくとも1つのワーカーノードがあります。

ワーカーノードは、アプリケーションのコンポーネントであるPodをホストします。マスターノードは、クラスター内のワーカーノードとPodを管理します。複数のマスターノードを使用して、クラスターにフェイルオーバーと高可用性を提供します。 ワーカーノードは、アプリケーションワークロードのコンポーネントであるPodをホストします。コントロールプレーンは、クラスター内のワーカーノードとPodを管理します。本番環境では、コントロールプレーンは複数のコンピューターを使用し、クラスターは複数のノードを使用し、耐障害性や高可用性を提供します。

このドキュメントでは、Kubernetesクラスターが機能するために必要となるさまざまなコンポーネントの概要を説明します。

Kubernetesのコンポーネント

Kubernetesクラスターを構成するコンポーネント

コントロールプレーンコンポーネント

コントロールプレーンコンポーネントは、クラスターに関する全体的な決定(スケジューリングなど)を行います。また、クラスターイベントの検出および応答を行います(たとえば、deploymentのreplicasフィールドが満たされていない場合に、新しい Pod を起動する等)。

コントロールプレーンコンポーネントはクラスター内のどのマシンでも実行できますが、シンプルにするため、セットアップスクリプトは通常、すべてのコントロールプレーンコンポーネントを同じマシンで起動し、そのマシンではユーザーコンテナを実行しません。 複数のマシンにまたがって実行されるコントロールプレーンのセットアップ例については、kubeadmを使用した高可用性クラスターの構築 を参照してください。

kube-apiserver

APIサーバーは、Kubernetes APIを外部に提供するKubernetesコントロールプレーンのコンポーネントです。 APIサーバーはKubernetesコントロールプレーンのフロントエンドになります。

Kubernetes APIサーバーの主な実装はkube-apiserverです。 kube-apiserverは水平方向にスケールするように設計されています—つまり、インスタンスを追加することでスケールが可能です。 複数のkube-apiserverインスタンスを実行することで、インスタンス間でトラフィックを分散させることが可能です。

etcd

一貫性、高可用性を持ったキーバリューストアで、Kubernetesの全てのクラスター情報の保存場所として利用されています。

etcdをKubernetesのデータストアとして使用する場合、必ずデータのバックアッププランを作成して下さい。

公式ドキュメントでetcdに関する詳細な情報を見つけることができます。

kube-scheduler

コントロールプレーン上で動作するコンポーネントで、新しく作られたPodノードが割り当てられているか監視し、割り当てられていなかった場合にそのPodを実行するノードを選択します。

スケジューリングの決定は、PodあるいはPod群のリソース要求量、ハードウェア/ソフトウェア/ポリシーによる制約、アフィニティおよびアンチアフィニティの指定、データの局所性、ワークロード間の干渉、有効期限などを考慮して行われます。

kube-controller-manager

コントロールプレーン上で動作するコンポーネントで、複数のコントローラープロセスを実行します。

論理的には、各コントローラーは個別のプロセスですが、複雑さを減らすために一つの実行ファイルにまとめてコンパイルされ、単一のプロセスとして動きます。

コントローラーには以下が含まれます。

  • ノードコントローラー:ノードがダウンした場合の通知と対応を担当します。
  • Jobコントローラー:単発タスクを表すJobオブジェクトを監視し、そのタスクを実行して完了させるためのPodを作成します。
  • EndpointSliceコントローラー:EndpointSliceオブジェクトを作成します(つまり、ServiceとPodを紐付けます)。
  • ServiceAccountコントローラー:新規の名前空間に対して、デフォルトのServiceAccountを作成します。

cloud-controller-manager

クラウド特有の制御ロジックを組み込むKubernetesのcontrol planeコンポーネントです。クラウドコントロールマネージャーは、クラスターをクラウドプロバイダーAPIをリンクし、クラスターのみで相互作用するコンポーネントからクラウドプラットフォームで相互作用するコンポーネントを分離します。

cloud-controller-managerは、クラウドプロバイダー固有のコントローラーのみを実行します。 Kubernetesをオンプレミスあるいは個人のPC内での学習環境で動かす際には、クラスターにcloud controller managerはありません。

kube-controller-managerと同様に、cloud-controller-managerは複数の論理的に独立したコントロールループをシングルバイナリにまとめ、一つのプロセスとして動作します。パフォーマンスを向上させるあるいは障害に耐えるために水平方向にスケールする(一つ以上のコピーを動かす)ことができます。

次の各コントローラーは、それぞれ以下に示すような目的のためにクラウドプロバイダーへの依存関係を持つことができるようになっています。

  • Nodeコントローラー: ノードが応答を停止した後、クラウドで当該ノードが削除されたかどうかを判断するため
  • Route コントローラー: 基盤となるクラウドインフラのルートを設定するため
  • Service コントローラー: クラウドプロバイダーのロードバランサーの作成、更新、削除を行うため

ノードコンポーネント

ノードコンポーネントはすべてのノードで実行され、稼働中のPodの管理やKubernetesの実行環境を提供します。

kubelet

クラスター内の各ノードで実行されるエージェントです。各コンテナPodで実行されていることを保証します。

kubeletは、さまざまなメカニズムを通じて提供されるPodSpecのセットを取得し、それらのPodSpecに記述されているコンテナが正常に実行されている状態を保証します。kubeletは、Kubernetesが作成したものではないコンテナは管理しません。

kube-proxy

kube-proxyはクラスター内の各nodeで動作しているネットワークプロキシで、KubernetesのServiceコンセプトの一部を実装しています。

kube-proxyは、Nodeのネットワークルールをメンテナンスします。これらのネットワークルールにより、クラスターの内部または外部のネットワークセッションからPodへのネットワーク通信が可能になります。

kube-proxyは、オペレーティングシステムにパケットフィルタリング層があり、かつ使用可能な場合、パケットフィルタリング層を使用します。それ以外の場合は自身でトラフィックを転送します。

コンテナランタイム

コンテナランタイムは、コンテナの実行を担当するソフトウェアです。

Kubernetesは次の複数のコンテナランタイムをサポートします。 DockercontainerdCRI-O、 および全ての Kubernetes CRI (Container Runtime Interface) 実装です。

アドオン

アドオンはクラスター機能を実装するためにKubernetesリソース(DaemonSetDeploymentなど)を使用します。 アドオンはクラスターレベルの機能を提供しているため、アドオンのリソースで名前空間が必要なものはkube-system名前空間に属します。

いくつかのアドオンについて以下で説明します。より多くの利用可能なアドオンのリストは、アドオン をご覧ください。

DNS

クラスターDNS以外のアドオンは必須ではありませんが、すべてのKubernetesクラスターはクラスターDNSを持つべきです。多くの使用例がクラスターDNSを前提としています。

クラスターDNSは、環境内の他のDNSサーバーに加えて、KubernetesサービスのDNSレコードを提供するDNSサーバーです。

Kubernetesによって開始されたコンテナは、DNS検索にこのDNSサーバーを自動的に含めます。

Web UI (ダッシュボード)

ダッシュボードは、Kubernetesクラスター用の汎用WebベースUIです。これによりユーザーはクラスターおよびクラスター内で実行されているアプリケーションについて、管理およびトラブルシューティングを行うことができます。

コンテナリソース監視

コンテナリソース監視は、コンテナに関する一般的な時系列メトリックを中央データベースに記録します。また、そのデータを閲覧するためのUIを提供します。

クラスターレベルのロギング

クラスターレベルのロギングメカニズムは、コンテナのログを、検索/参照インターフェースを備えた中央ログストアに保存します。

次の項目

3.1.2 - Kubernetes API

Kubernetes APIを使用すると、Kubernetes内のオブジェクトの状態をクエリで操作できます。 Kubernetesのコントロールプレーンの中核は、APIサーバーとそれが公開するHTTP APIです。ユーザー、クラスターのさまざまな部分、および外部コンポーネントはすべて、APIサーバーを介して互いに通信します。

Kubernetesの中核である control planeAPI server です。 APIサーバーは、エンドユーザー、クラスターのさまざまな部分、および外部コンポーネントが相互に通信できるようにするHTTP APIを公開します。

Kubernetes APIを使用すると、Kubernetes API内のオブジェクトの状態をクエリで操作できます(例:Pod、Namespace、ConfigMap、Events)。

ほとんどの操作は、APIを使用しているkubectlコマンドラインインターフェースもしくはkubeadmのような別のコマンドラインツールを通して実行できます。 RESTコールを利用して直接APIにアクセスすることも可能です。

Kubernetes APIを利用してアプリケーションを書いているのであれば、client librariesの利用を考えてみてください。

OpenAPI 仕様

完全なAPIの詳細は、OpenAPIを使用して文書化されています。

OpenAPI V2

Kubernetes APIサーバーは、/openapi/v2エンドポイントを介してOpenAPI v2仕様を提供します。 次のように要求ヘッダーを使用して、応答フォーマットを要求できます。

OpenAPI v2クエリの有効なリクエストヘッダー値
ヘッダー取りうる値備考
Accept-Encodinggzipこのヘッダーを使わないことも可能
Acceptapplication/com.github.proto-openapi.spec.v2@v1.0+protobuf主にクラスター内での使用
application/jsonデフォルト
*application/jsonを提供

Kubernetesは、他の手段として主にクラスター間の連携用途向けのAPIに、Protocol buffersをベースにしたシリアライズフォーマットを実装しています。このフォーマットに関しては、Kubernetes Protobuf serializationデザイン提案を参照してください。また、各スキーマのInterface Definition Language(IDL)ファイルは、APIオブジェクトを定義しているGoパッケージ内に配置されています。

OpenAPI V3

FEATURE STATE: Kubernetes v1.24 [beta]

Kubernetes v1.30では、OpenAPI v3によるAPI仕様をベータサポートとして提供しています。これは、デフォルトで有効化されているベータ機能です。kube-apiserverのOpenAPIV3というfeature gateを切ることにより、このベータ機能を無効化することができます。

/openapi/v3が、全ての利用可能なグループやバージョンの一覧を閲覧するためのディスカバリーエンドポイントとして提供されています。このエンドポイントは、JSONのみを返却します。利用可能なグループやバージョンは、次のような形式で提供されます。

{
    "paths": {
        ...,
        "api/v1": {
            "serverRelativeURL": "/openapi/v3/api/v1?hash=CC0E9BFD992D8C59AEC98A1E2336F899E8318D3CF4C68944C3DEC640AF5AB52D864AC50DAA8D145B3494F75FA3CFF939FCBDDA431DAD3CA79738B297795818CF"
        },
        "apis/admissionregistration.k8s.io/v1": {
            "serverRelativeURL": "/openapi/v3/apis/admissionregistration.k8s.io/v1?hash=E19CC93A116982CE5422FC42B590A8AFAD92CDE9AE4D59B5CAAD568F083AD07946E6CB5817531680BCE6E215C16973CD39003B0425F3477CFD854E89A9DB6597"
        },
        ....
    }
}

クライアントサイドのキャッシングを改善するために、相対URLはイミュータブルな(不変の)OpenAPI記述を指しています。 また、APIサーバーも、同様の目的で適切なHTTPキャッシュヘッダー(Expiresには1年先の日付、Cache-Controlにはimmutable)をセットします。廃止されたURLが使用された場合、APIサーバーは最新のURLへのリダイレクトを返します。

Kubernetes APIサーバーは、/openapi/v3/apis/<group>/<version>?hash=<hash>のエンドポイントにて、KubernetesのグループバージョンごとにOpenAPI v3仕様を公開しています。

受理されるリクエストヘッダーについては、以下の表の通りです。

OpenAPI v3において有効なリクエストヘッダー
ヘッダー取りうる値備考
Accept-Encodinggzipこのヘッダーを使わないことも可能
Acceptapplication/com.github.proto-openapi.spec.v3@v1.0+protobuf主にクラスター内での使用
application/jsonデフォルト
*application/jsonを提供

永続性

KubernetesはAPIリソースの観点からシリアル化された状態をetcdに書き込むことで保存します。

APIグループとバージョニング

フィールドの削除やリソース表現の再構成を簡単に行えるようにするため、Kubernetesは複数のAPIバージョンをサポートしており、/api/v1/apis/rbac.authorization.k8s.io/v1alpha1のように、それぞれ異なるAPIのパスが割り当てられています。

APIが、システムリソースと動作について明確かつ一貫したビューを提供し、サポート終了、実験的なAPIへのアクセス制御を有効にするために、リソースまたはフィールドレベルではなく、APIレベルでバージョンが行われます。

APIの発展や拡張を簡易に行えるようにするため、Kubernetesは有効もしくは無効を行えるAPIグループを実装しました。

APIリソースは、APIグループ、リソースタイプ、ネームスペース(namespacedリソースのための)、名前によって区別されます。APIサーバーは、APIバージョン間の変換を透過的に処理します。すべてのバージョンの違いは、実際のところ同じ永続データとして表現されます。APIサーバーは、同じ基本的なデータを複数のAPIバージョンで提供することができます。

例えば、同じリソースでv1v1beta1の2つのバージョンが有ることを考えてみます。 v1beta1バージョンのAPIを利用しオブジェクトを最初に作成したとして、v1beta1バージョンが非推奨となり削除されるまで、v1beta1もしくはv1どちらのAPIバージョンを利用してもオブジェクトのread、update、deleteができます。 その時点では、v1 APIを使用してオブジェクトの修正やアクセスを継続することが可能です。

APIの変更

成功を収めているシステムはすべて、新しいユースケースの出現や既存の変化に応じて成長し、変化する必要があります。 したがって、Kubernetesには、Kubernetes APIを継続的に変更および拡張できる設計機能があります。 Kubernetesプロジェクトは、既存のクライアントとの互換性を破壊 しないこと 、およびその互換性を一定期間維持して、他のプロジェクトが適応する機会を提供することを目的としています。

基本的に、新しいAPIリソースと新しいリソースフィールドは追加することができます。 リソースまたはフィールドを削除するには、API非推奨ポリシーに従ってください。

Kubernetesは、通常はAPIバージョンv1として、公式のKubernetes APIが一度一般提供(GA)に達した場合、互換性を維持することを強く確約します。 さらに、Kubernetesは、公式Kubernetes APIの beta APIバージョン経由で永続化されたデータとの互換性を維持します。 そして、機能が安定したときにGA APIバージョン経由でデータを変換してアクセスできることを保証します。

beta APIを採用した場合、APIが卒業(Graduate)したら、後続のbetaまたはstable APIに移行する必要があります。 これを行うのに最適な時期は、オブジェクトが両方のAPIバージョンから同時にアクセスできるbeta APIの非推奨期間中です。 beta APIが非推奨期間を終えて提供されなくなったら、代替APIバージョンを使用する必要があります。

APIバージョンレベルの定義に関する詳細はAPIバージョンのリファレンスを参照してください。

APIの拡張

Kubernetes APIは2つの方法で拡張できます。

  1. カスタムリソースは、APIサーバーが選択したリソースAPIをどのように提供するかを宣言的に定義します。
  2. アグリゲーションレイヤーを実装することでKubernetes APIを拡張することもできます。

次の項目

  • 自分自身でカスタムリソース定義を追加してKubernetes APIを拡張する方法について学んでください。
  • Kubernetes APIのアクセス制御では、クラスターがAPIアクセスの認証と承認を管理する方法を説明しています。
  • APIリファレンスを読んで、APIエンドポイント、リソースタイプやサンプルについて学んでください。
  • APIの変更から、互換性のある変更とは何か, どのようにAPIを変更するかについて学んでください。

3.1.3 - Kubernetesオブジェクトを利用する

Kubernetesオブジェクトは、Kubernetes上で永続的なエンティティです。Kubernetesはこれらのエンティティを使い、クラスターの状態を表現します。 Kubernetesオブジェクトモデルと、これらのオブジェクトの利用方法について学びます。

3.1.3.1 - Kubernetesオブジェクトを理解する

このページでは、KubernetesオブジェクトがKubernetes APIでどのように表現されているか、またそれらを.yamlフォーマットでどのように表現するかを説明します。

Kubernetesオブジェクトを理解する

Kubernetesオブジェクト は、Kubernetes上で永続的なエンティティです。Kubernetesはこれらのエンティティを使い、クラスターの状態を表現します。具体的に言うと、下記のような内容が表現できます:

  • どのようなコンテナ化されたアプリケーションが稼働しているか(またそれらはどのノード上で動いているか)
  • それらのアプリケーションから利用可能なリソース
  • アプリケーションがどのように振る舞うかのポリシー、例えば再起動、アップグレード、耐障害性ポリシーなど

Kubernetesオブジェクトは「意図の記録」です。一度オブジェクトを作成すると、Kubernetesは常にそのオブジェクトが存在し続けるように動きます。オブジェクトを作成することで、Kubernetesに対し効果的にあなたのクラスターのワークロードがこのようになっていて欲しいと伝えているのです。これが、あなたのクラスターの望ましい状態です。

Kubernetesオブジェクトを操作するには、作成、変更、または削除に関わらずKubernetes APIを使う必要があるでしょう。例えばkubectlコマンドラインインターフェースを使った場合、このCLIが処理に必要なKubernetes API命令を、あなたに代わり発行します。あなたのプログラムからクライアントライブラリを利用し、直接Kubernetes APIを利用することも可能です。

オブジェクトのspec(仕様)とstatus(状態)

ほとんどのKubernetesオブジェクトは、オブジェクトの設定を管理する2つの入れ子になったオブジェクトのフィールドを持っています。それはオブジェクト spec とオブジェクト status です。specを持っているオブジェクトに関しては、オブジェクト作成時にspecを設定する必要があり、望ましい状態としてオブジェクトに持たせたい特徴を記述する必要があります。

status オブジェクトはオブジェクトの 現在の状態 を示し、その情報はKubernetesシステムとそのコンポーネントにより提供、更新されます。Kubernetesコントロールプレーンは、あなたから指定された望ましい状態と現在の状態が一致するよう常にかつ積極的に管理をします。

例えば、KubernetesのDeploymentはクラスター上で稼働するアプリケーションを表現するオブジェクトです。Deploymentを作成するとき、アプリケーションの複製を3つ稼働させるようDeploymentのspecで指定するかもしれません。KubernetesはDeploymentのspecを読み取り、指定されたアプリケーションを3つ起動し、現在の状態がspecに一致するようにします。もしこれらのインスタンスでどれかが落ちた場合(statusが変わる)、Kubernetesはspecと、statusの違いに反応し、修正しようとします。この場合は、落ちたインスタンスの代わりのインスタンスを立ち上げます。

spec、status、metadataに関するさらなる情報は、Kubernetes API Conventionsをご確認ください。

Kubernetesオブジェクトを記述する

Kubernetesでオブジェクトを作成する場合、オブジェクトの基本的な情報(例えば名前)と共に、望ましい状態を記述したオブジェクトのspecを渡さなければいけません。KubernetesAPIを利用しオブジェクトを作成する場合(直接APIを呼ぶか、kubectlを利用するかに関わらず)、APIリクエストはそれらの情報をJSON形式でリクエストのBody部に含んでいなければなりません。

ここで、KubernetesのDeploymentに必要なフィールドとオブジェクトのspecを記載した.yamlファイルの例を示します:

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

上に示した.yamlファイルを利用してDeploymentを作成するには、kubectlコマンドラインインターフェースに含まれているkubectl applyコマンドに.yamlファイルを引数に指定し、実行します。ここで例を示します:

kubectl apply -f https://k8s.io/examples/application/deployment.yaml --record

出力結果は、下記に似た形になります:

deployment.apps/nginx-deployment created

必須フィールド

Kubernetesオブジェクトを.yamlファイルに記載して作成する場合、下記に示すフィールドに値をセットしておく必要があります:

  • apiVersion - どのバージョンのKubernetesAPIを利用してオブジェクトを作成するか
  • kind - どの種類のオブジェクトを作成するか
  • metadata - オブジェクトを一意に特定するための情報、文字列のnameUID、また任意のnamespaceが該当する
  • spec - オブジェクトの望ましい状態

specの正確なフォーマットは、Kubernetesオブジェクトごとに異なり、オブジェクトごとに特有な入れ子のフィールドを持っています。Kubernetes API リファレンスが、Kubernetesで作成できる全てのオブジェクトに関するspecのフォーマットを探すのに役立ちます。 例えば、Podオブジェクトに関するspecのフォーマットはPodSpec v1 coreを、またDeploymentオブジェクトに関するspecのフォーマットはDeploymentSpec v1 appsをご確認ください。

次の項目

  • 最も重要、かつ基本的なKubernetesオブジェクト群を学びましょう、例えば、Podです。
  • Kubernetesのコントローラーを学びましょう。
  • Using the Kubernetes APIはこのページでは取り上げていない他のAPIについて説明します。

3.1.3.2 - Kubernetesオブジェクト管理

kubectlコマンドラインツールは、Kubernetesオブジェクトを作成、管理するためにいくつかの異なる方法をサポートしています。 このドキュメントでは、それらの異なるアプローチごとの概要を提供します。 Kubectlを使ったオブジェクト管理の詳細は、Kubectl bookを参照してください。

管理手法

管理手法何を対象にするか推奨環境サポートライター学習曲線
命令型コマンド現行のオブジェクト開発用プロジェクト1+緩やか
命令型オブジェクト設定個々のファイル本番用プロジェクト1中程度
宣言型オブジェクト設定ファイルのディレクトリ本番用プロジェクト1+

命令型コマンド

命令型コマンドを使う場合、ユーザーはクラスター内の現行のオブジェクトに対して処理を行います。 ユーザーはkubectlコマンドに処理内容を引数、もしくはフラグで指定します。

これはKubernetesの使い始め、またはクラスターに対して一度限りのタスクを行う際の最も簡単な手法です。 なぜなら、この手法は現行のオブジェクトに対して直接操作ができ、以前の設定履歴は提供されないからです。

Deploymentオブジェクトを作成し、nginxコンテナの単一インスタンスを起動します:

kubectl run nginx --image nginx

同じことを異なる構文で行います:

kubectl create deployment nginx --image nginx

トレードオフ

オブジェクト設定手法に対する長所:

  • コマンドは簡潔、簡単に学ぶことができ、そして覚えやすいです
  • コマンドではクラスターの設定を変えるのに、わずか1ステップしか必要としません

オブジェクト設定手法に対する短所:

  • コマンドは変更レビュープロセスと連携しません
  • コマンドは変更に伴う監査証跡を提供しません
  • コマンドは現行がどうなっているかという情報を除き、レコードのソースを提供しません
  • コマンドはオブジェクトを作成するためのテンプレートを提供しません

命令型オブジェクト設定

命令型オブジェクト設定では、kubectlコマンドに処理内容(create、replaceなど)、任意のフラグ、そして最低1つのファイル名を指定します。 指定されたファイルは、YAMLまたはJSON形式でオブジェクトの全ての定義情報を含んでいなければいけません。

オブジェクト定義の詳細は、APIリファレンスを参照してください。

設定ファイルに定義されたオブジェクトを作成します:

kubectl create -f nginx.yaml

設定ファイルに定義されたオブジェクトを削除します:

kubectl delete -f nginx.yaml -f redis.yaml

設定ファイルに定義された情報で、現行の設定を上書き更新します:

kubectl replace -f nginx.yaml

トレードオフ

命令型コマンド手法に対する長所:

  • オブジェクト設定をGitのような、ソースコード管理システムに格納することができます
  • オブジェクト設定の変更内容をプッシュする前にレビュー、監査証跡を残すようなプロセスと連携することができます
  • オブジェクト設定は新しいオブジェクトを作る際のテンプレートを提供します

命令型コマンド手法に対する短所:

  • オブジェクト設定ではオブジェクトスキーマの基礎的な理解が必要です
  • オブジェクト設定ではYAMLファイルを書くという、追加のステップが必要です

宣言型オブジェクト設定手法に対する長所:

  • 命令型オブジェクト設定の振る舞いは、よりシンプルで簡単に理解ができます
  • Kubernetesバージョン1.5においては、命令型オブジェクト設定の方がより成熟しています

宣言型オブジェクト設定手法に対する短所:

  • 命令型オブジェクト設定は各ファイルごとに設定を書くには最も適していますが、ディレクトリには適していません
  • 現行オブジェクトの更新は設定ファイルに対して反映しなければなりません。反映されない場合、次の置き換え時に更新内容が失われてしまいます

宣言型オブジェクト設定

宣言型オブジェクト設定を利用する場合、ユーザーはローカルに置かれている設定ファイルを操作します。 しかし、ユーザーはファイルに対する操作内容を指定しません。作成、更新、そして削除といった操作はオブジェクトごとにkubectlが検出します。 この仕組みが、異なるオブジェクトごとに異なる操作をディレクトリに対して行うことを可能にしています。

configディレクトリ配下にある全てのオブジェクト設定ファイルを処理し、作成、または現行オブジェクトへのパッチを行います。 まず、diffでどのような変更が行われるかを確認した後に適用します:

kubectl diff -f configs/
kubectl apply -f configs/

再帰的にディレクトリを処理します:

kubectl diff -R -f configs/
kubectl apply -R -f configs/

トレードオフ

命令型オブジェクト設定手法に対する長所:

  • 現行オブジェクトに直接行われた変更が、それらが設定ファイルに反映されていなかったとしても、保持されます
  • 宣言型オブジェクト設定は、ディレクトリごとの処理をより良くサポートしており、自動的にオブジェクトごとに操作のタイプ(作成、パッチ、削除)を検出します

命令型オブジェクト設定手法に対する短所:

  • 宣言型オブジェクト設定は、デバッグ、そして想定外の結果が出たときに理解するのが困難です
  • 差分を利用した一部のみの更新は、複雑なマージ、パッチの操作が必要です

次の項目

3.1.3.3 - オブジェクトの名前とID

クラスター内の各オブジェクトには、そのタイプのリソースに固有の名前があります。すべてのKubernetesオブジェクトには、クラスター全体で一意のUIDもあります。

たとえば、同じ名前空間内にmyapp-1234という名前のPodは1つしか含められませんが、myapp-1234という名前の1つのPodと1つのDeploymentを含めることができます。

ユーザーが一意ではない属性を付与するために、Kubernetesはラベルアノテーションを提供しています。

名前

クライアントから提供され、リソースURL内のオブジェクトを参照する文字列です。例えば/api/v1/pods/何らかの名前のようになります。

同じ種類のオブジェクトは、同じ名前を同時に持つことはできません。しかし、オブジェクトを削除することで、旧オブジェクトと同じ名前で新しいオブジェクトを作成できます。

次の3つの命名規則がよく使われます。

DNSサブドメイン名

ほとんどのリソースタイプには、RFC 1123で定義されているDNSサブドメイン名として使用できる名前が必要です。 つまり、名前は次のとおりでなければなりません:

  • 253文字以内
  • 英小文字、数字、「-」または「.」のみを含む
  • 英数字で始まる
  • 英数字で終わる

DNSラベル名

一部のリソースタイプでは、RFC 1123で定義されているDNSラベル標準に従う名前が必要です。 つまり、名前は次のとおりでなければなりません:

  • 63文字以内
  • 英小文字、数字または「-」のみを含む
  • 英数字で始まる
  • 英数字で終わる

パスセグメント名

一部のリソースタイプでは、名前をパスセグメントとして安全にエンコードできるようにする必要があります。 つまり、名前を「.」や「..」にすることはできず、名前に「/」または「%」を含めることはできません。

以下は、nginx-demoという名前のPodのマニフェストの例です。

apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

UID

オブジェクトを一意に識別するためのKubernetesが生成する文字列です。

Kubernetesクラスターの生存期間中にわたって生成された全てのオブジェクトは、異なるUIDを持っています。これは類似のエンティティの、同一時間軸での存在を区別するのが目的です。

Kubernetes UIDは、UUIDのことを指します。 UUIDは、ISO/IEC 9834-8およびITU-T X.667として標準化されています。

次の項目

3.1.3.4 - ラベル(Labels)とセレクター(Selectors)

ラベル(Labels) はPodなどのオブジェクトに割り当てられたキーとバリューのペアです。
ラベルはユーザーに関連した意味のあるオブジェクトの属性を指定するために使われることを目的としています。しかしKubernetesのコアシステムに対して直接的にその意味を暗示するものではありません。
ラベルはオブジェクトのサブセットを選択し、グルーピングするために使うことができます。また、ラベルはオブジェクトの作成時に割り当てられ、その後いつでも追加、修正ができます。
各オブジェクトはキーとバリューのラベルのセットを定義できます。各キーは、単一のオブジェクトに対してはユニークである必要があります。

"metadata": {
  "labels": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

ラベルは効率的な検索・閲覧を可能にし、UIやCLI上での利用に最適です。 識別用途でない情報は、アノテーションを用いて記録されるべきです。

ラベルを使う動機

ラベルは、クライアントにそのマッピング情報を保存することを要求することなく、ユーザー独自の組織構造をシステムオブジェクト上で疎結合にマッピングできます。

サービスデプロイメントとバッチ処理のパイプラインは多くの場合、多次元のエンティティとなります(例: 複数のパーティション、Deployment、リリーストラック、ティアー、ティアー毎のマイクロサービスなど)
管理は分野横断的な操作が必要になることが多く、それによって厳密な階層表現、特にユーザーによるものでなく、インフラストラクチャーによって定義された厳格な階層のカプセル化が破られます。

ラベルの例:

  • "release" : "stable", "release" : "canary"
  • "environment" : "dev", "environment" : "qa", "environment" : "production"
  • "tier" : "frontend", "tier" : "backend", "tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"

これらは単によく使われるラベルの例です。ユーザーは自由に規約を決めることができます。 ラベルのキーは、ある1つのオブジェクトに対してユニークである必要があることは覚えておかなくてはなりません。

構文と文字セット

ラベルは、キーとバリューのベアです。正しいラベルキーは2つのセグメントを持ちます。
それは/によって分割されたオプショナルなプレフィックスと名前です。
名前セグメントは必須で、63文字以下である必要があり、文字列の最初と最後は英数字([a-z0-9A-Z])で、文字列の間ではこれに加えてダッシュ(-)、アンダースコア(_)、ドット(.)を使うことができます。
プレフィックスはオプションです。もしプレフィックスが指定されていた場合、プレフィックスはDNSサブドメイン形式である必要があり、それはドット(.)で区切られたDNSラベルのセットで、253文字以下である必要があり、最後にスラッシュ(/)が続きます。

もしプレフィックスが省略された場合、ラベルキーはそのユーザーに対してプライベートであると推定されます。
エンドユーザーのオブジェクトにラベルを追加するような自動化されたシステムコンポーネント(例: kube-scheduler kube-controller-manager kube-apiserver kubectlやその他のサードパーティツール)は、プレフィックスを指定しなくてはなりません。

kubernetes.io/k8s.io/プレフィックスは、Kubernetesコアコンポーネントのために予約されています。

正しいラベル値は63文字以下の長さで、空文字か、もしくは開始と終了が英数字([a-z0-9A-Z])で、文字列の間がダッシュ(-)、アンダースコア(_)、ドット(.)と英数字である文字列を使うことができます。

例えば、environment: productionapp: nginxの2つのラベルを持つPodの設定ファイルは下記のようになります。


apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  labels:
    environment: production
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

ラベルセレクター

名前とUIDとは異なり、ラベルはユニーク性を提供しません。通常、多くのオブジェクトが同じラベルを保持することを想定します。

ラベルセレクター を介して、クライアントとユーザーはオブジェクトのセットを指定できます。ラベルセレクターはKubernetesにおいてコアなグルーピング機能となります。

Kubernetes APIは現在2タイプのセレクターをサポートしています。
それは等価ベース(equality-based)集合ベース(set-based) です。
単一のラベルセレクターは、コンマ区切りの複数の要件(requirements) で構成されています。
複数の要件がある場合、コンマセパレーターは論理積 AND(&&)オペレーターと同様にふるまい、全ての要件を満たす必要があります。

空文字の場合や、指定なしのセレクターに関するセマンティクスは、コンテキストに依存します。 そしてセレクターを使うAPIタイプは、それらのセレクターの妥当性とそれらが示す意味をドキュメントに記載するべきです。

等価ベース(Equality-based) の要件(requirement)

等価ベース(Equality-based) もしくは不等ベース(Inequality-based) の要件は、ラベルキーとラベル値によるフィルタリングを可能にします。
要件に一致したオブジェクトは、指定されたラベルの全てを満たさなくてはいけませんが、それらのオブジェクトはさらに追加のラベルも持つことができます。
そして等価ベースの要件においては、3つの種類のオペレーターの利用が許可されています。===!=となります。
最初の2つのオペレーター(===)は等価(Equality) を表現し(この2つは単なる同義語)、最後の1つ(!=)は不等(Inequality) を意味します。
例えば

environment = production
tier != frontend

最初の例は、キーがenvironmentで、値がproductionである全てのリソースを対象にします。
次の例は、キーがtierで、値がfrontendとは異なるリソースと、tierという名前のキーを持たない全てのリソースを対象にします。
コンマセパレーター,を使って、productionの中から、frontendのものを除外するようにフィルターすることもできます。
environment=production,tier!=frontend

等価ベースのラベル要件の1つの使用シナリオとして、PodにおけるNodeの選択要件を指定するケースがあります。
例えば、下記のサンプルPodは、ラベルaccelerator=nvidia-tesla-p100をもったNodeを選択します。

apiVersion: v1
kind: Pod
metadata:
  name: cuda-test
spec:
  containers:
    - name: cuda-test
      image: "registry.k8s.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100

集合ベース(Set-based) の要件(requirement)

集合ベース(Set-based) のラベルの要件は値のセットによってキーをフィルタリングします。
innotinexistsの3つのオペレーターをサポートしています(キーを特定するのみ)。

例えば:

environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
  • 最初の例では、キーがenvironmentで、値がproductionqaに等しいリソースを全て選択します。
  • 第2の例では、キーがtierで、値がfrontendbackend以外のもの、そしてtierキーを持たないリソースを全て選択します。
  • 第3の例では、partitionというキーをもつラベルを全て選択し、値はチェックしません。
  • 第4の例では、partitionというキーを持たないラベルを全て選択し、値はチェックしません。

同様に、コンマセパレーターは、AND オペレーターと同様にふるまいます。そのため、partitionenvironmentキーの値がともにqaでないラベルを選択するには、partition,environment notin (qa)と記述することで可能です。
集合ベース のラベルセレクターは、environment=productionという記述がenvironment in (production)と等しいため、一般的な等価形式となります。 !=notinも同様に等価となります。

集合ベース の要件は、等価ベース の要件と混在できます。
例えば:
partition in (customerA, customerB),environment!=qa.

API

LISTとWATCHによるフィルタリング

LISTとWATCHオペレーションは、単一のクエリパラメーターを使うことによって返されるオブジェクトのセットをフィルターするためのラベルセレクターを指定できます。
集合ベース等価ベース のどちらの要件も許可されています(ここでは、URLクエリストリング内で出現します)。

  • 等価ベース での要件: ?labelSelector=environment%3Dproduction,tier%3Dfrontend
  • 集合ベース での要件: ?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29

上記の2つの形式のラベルセレクターはRESTクライアントを介してリストにしたり、もしくは確認するために使われます。
例えば、kubectlによってapiserverをターゲットにし、等価ベース の要件でフィルターすると以下のように書けます。

kubectl get pods -l environment=production,tier=frontend

もしくは、集合ベース の要件を指定すると以下のようになります。

kubectl get pods -l 'environment in (production),tier in (frontend)'

すでに言及したように、集合ベース の要件は、等価ベース の要件より表現力があります。
例えば、値に対する OR オペレーターを実装して以下のように書けます。

kubectl get pods -l 'environment in (production, qa)'

もしくは、notin オペレーターを介して、否定マッチングによる制限もできます。

kubectl get pods -l 'environment,environment notin (frontend)'

APIオブジェクトに参照を設定する

ServiceReplicationControllerのような、いくつかのKubernetesオブジェクトでは、ラベルセレクターをPodのような他のリソースのセットを指定するのにも使われます。

ServiceとReplicationController

Serviceが対象とするPodの集合は、ラベルセレクターによって定義されます。
同様に、ReplicationControllerが管理するべきPod数についてもラベルセレクターを使って定義されます。

それぞれのオブジェクトに対するラベルセレクターはマップを使ってjsonもしくはyaml形式のファイルで定義され、等価ベース のセレクターのみサポートされています。

"selector": {
    "component" : "redis",
}

もしくは

selector:
    component: redis

このセレクター(それぞれjsonまたはyaml形式)は、component=redisまたはcomponent in (redis)と等価です。

集合ベース の要件指定をサポートするリソース

JobDeploymentReplicaSetDaemonSetなどの比較的新しいリソースは、集合ベース での要件指定もサポートしています。

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - {key: tier, operator: In, values: [cache]}
    - {key: environment, operator: NotIn, values: [dev]}

matchLabelsは、{key,value}ペアのマップです。matchLabels内の単一の{key,value}は、matchExpressionsの要素と等しく、それは、keyフィールドがキー名で、operatorが"In"で、values配列は単に"値"を保持します。
matchExpressionsはPodセレクター要件のリストです。対応しているオペレーターはInNotInExistsDoesNotExistです。valuesのセットは、InNotInオペレーターにおいては空文字を許容しません。
matchLabelsmatchExpressionsの両方によって指定された全ての要件指定はANDで判定されます。つまり要件にマッチするには指定された全ての要件を満たす必要があります。

Nodeのセットを選択する

ラベルを選択するための1つのユースケースはPodがスケジュールできるNodeのセットを制限することです。
さらなる情報に関しては、Node選定 のドキュメントを参照してください。

3.1.3.5 - Namespace(名前空間)

Kubernetesは、同一の物理クラスター上で複数の仮想クラスターの動作をサポートします。 この仮想クラスターをNamespaceと呼びます。

複数のNamespaceを使う時

Namespaceは、複数のチーム・プロジェクトにまたがる多くのユーザーがいる環境での使用を目的としています。 数人から数十人しかユーザーのいないクラスターに対して、あなたはNamespaceを作成したり、考える必要は全くありません。 Kubernetesが提供するNamespaceの機能が必要となった時に、Namespaceの使用を始めてください。

Namespaceは名前空間のスコープを提供します。リソース名は単一のNamespace内ではユニークである必要がありますが、Namespace全体ではその必要はありません。Namespaceは相互にネストすることはできず、各Kubernetesリソースは1つのNamespaceにのみ存在できます。

Namespaceは、複数のユーザーの間でクラスターリソースを分割する方法です。(これはリソースクォータを介して分割します。)

同じアプリケーションの異なるバージョンなど、少し違うリソースをただ分割するだけに、複数のNamespaceを使う必要はありません。 同一のNamespace内でリソースを区別するためにはラベルを使用してください。

Namespaceを利用する

Namespaceの作成と削除方法はNamespaceの管理ガイドドキュメントに記載されています。

Namespaceの表示

ユーザーは、以下の方法で単一クラスター内の現在のNamespaceの一覧を表示できます。

kubectl get namespace
NAME              STATUS   AGE
default           Active   1d
kube-node-lease   Active   1d
kube-system       Active   1d
kube-public       Active   1d

Kubernetesの起動時には4つの初期Namespaceが作成されています。

  • default 他にNamespaceを持っていないオブジェクトのためのデフォルトNamespace
  • kube-system Kubernetesシステムによって作成されたオブジェクトのためのNamespace
  • kube-public このNamespaceは自動的に作成され、全てのユーザーから読み取り可能です。(認証されていないユーザーも含みます。) このNamespaceは、リソースをクラスター全体を通じてパブリックに表示・読み取り可能にするため、ほとんどクラスターによって使用される用途で予約されます。 このNamespaceのパブリックな側面は単なる慣例であり、要件ではありません。
  • kube-node-lease クラスターのスケールに応じたノードハートビートのパフォーマンスを向上させる各ノードに関連したLeaseオブジェクトのためのNamespace。

Namespaceの設定

現在のリクエストのNamespaceを設定するには、--namespaceフラグを使用します。

例:

kubectl run nginx --image=nginx --namespace=<insert-namespace-name-here>
kubectl get pods --namespace=<insert-namespace-name-here>

Namespace設定の永続化

ユーザーはあるコンテキストのその後のコマンドで使うために、コンテキスト内で永続的にNamespaceを保存できます。

kubectl config set-context --current --namespace=<insert-namespace-name-here>
# Validate it
kubectl config view --minify | grep namespace:

NamespaceとDNS

ユーザーがServiceを作成するとき、Serviceは対応するDNSエントリを作成します。 このエントリは<service-name>.<namespace-name>.svc.cluster.localという形式になり、これはもしあるコンテナがただ<service-name>を指定していた場合、Namespace内のローカルのServiceに対して名前解決されます。 これはデベロップメント、ステージング、プロダクションといった複数のNamespaceをまたいで同じ設定を使う時に効果的です。 もしユーザーがNamespaceをまたいでアクセスしたい時、 完全修飾ドメイン名(FQDN)を指定する必要があります。

すべてのオブジェクトはNamespaceに属しているとは限らない

ほとんどのKubernetesリソース(例えば、Pod、Service、ReplicationControllerなど)はいくつかのNamespaceにあります。 しかしNamespaceのリソースそれ自体は単一のNamespace内にありません。 そしてNodeやPersistentVolumeのような低レベルのリソースはどのNamespaceにも属していません。

どのKubernetesリソースがNamespaceに属しているか、属していないかを見るためには、以下のコマンドで確認できます。

# Namespaceに属しているもの
kubectl api-resources --namespaced=true

# Namespaceに属していないもの
kubectl api-resources --namespaced=false

次の項目

3.1.3.6 - アノテーション(Annotations)

ユーザーは、識別用途でない任意のメタデータをオブジェクトに割り当てるためにアノテーションを使用できます。ツールやライブラリなどのクライアントは、このメタデータを取得できます。

オブジェクトにメタデータを割り当てる

ユーザーは、Kubernetesオブジェクトに対してラベルやアノテーションの両方またはどちらか一方を割り当てることができます。 ラベルはオブジェクトの選択や、特定の条件を満たしたオブジェクトの集合を探すことに使うことができます。 それと対照的に、アノテーションはオブジェクトを識別、または選択するために使用されません。 アノテーション内のメタデータは大小様々で、構造化されているものや、そうでないものも設定でき、ラベルでは許可されていない文字も含むことができます。

アノテーションは、ラベルと同様に、キーとバリューのマップとなります。

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

下記は、アノテーション内で記録できる情報の例です。

  • 宣言的設定レイヤーによって管理されているフィールド。これらのフィールドをアノテーションとして割り当てることで、クライアントもしくはサーバによってセットされたデフォルト値、オートサイジングやオートスケーリングシステムによってセットされたフィールドや、自動生成のフィールドなどと区別することができます。

  • ビルド、リリースやタイムスタンプのようなイメージの情報、リリースID、gitのブランチ、PR番号、イメージハッシュ、レジストリアドレスなど

  • ロギング、監視、分析用のポインタ、もしくは監査用リポジトリ

  • デバッグ目的で使用されるためのクライアントライブラリやツールの情報。例えば、名前、バージョン、ビルド情報など。

  • 他のエコシステムのコンポーネントからの関連オブジェクトのURLなど、ユーザーやツール、システムの出所情報。

  • 軽量ロールアウトツールのメタデータ。 例えば設定やチェックポイントなど。

  • 情報をどこで確認できるかを示すためのもの。例えばチームのウェブサイト、責任者の電話番号や、ページャー番号やディレクトリエンティティなど。

  • システムのふるまいの変更や、標準ではない機能を利用可能にするために、エンドユーザーがシステムに対して指定する値

アノテーションを使用するかわりに、ユーザーはこのようなタイプの情報を外部のデータベースやディレクトリに保存することもできます。しかし、それによりデプロイ、管理、イントロスペクションを行うためのクライアンライブラリやツールの生成が非常に難しくなります。

構文と文字セット

アノテーション はキーとバリューのペアです。有効なアノテーションのキーの形式は2つのセグメントがあります。 プレフィックス(オプション)と名前で、それらはスラッシュ/で区切られます。 名前セグメントは必須で、63文字以下である必要があり、文字列の最初と最後は英数字([a-z0-9A-Z])と、文字列の間にダッシュ(-)、アンダースコア(_)、ドット(.)を使うことができます。 プレフィックスはオプションです。もしプレフィックスが指定されていた場合、プレフィックスはDNSサブドメイン形式である必要があり、それはドット(.)で区切られたDNSラベルのセットで、253文字以下である必要があり、最後にスラッシュ(/)が続きます。

もしプレフィックスが除外された場合、アノテーションキーはそのユーザーに対してプライベートであると推定されます。 エンドユーザーのオブジェクトにアノテーションを追加するような自動化されたシステムコンポーネント(例: kube-scheduler kube-controller-manager kube-apiserver kubectlやその他のサードパーティツール)は、プレフィックスを指定しなくてはなりません。

kubernetes.io/k8s.io/プレフィックスは、Kubernetesコアコンポーネントのために予約されています。

たとえば、imageregistry: https://hub.docker.com/というアノテーションが付いたPodの構成ファイルは次のとおりです:

apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

次の項目

ラベルとセレクターについて学習してください。

3.1.3.7 - フィールドセレクター(Field Selectors)

フィールドセレクター(Field Selectors) は、1つかそれ以上のリソースフィールドの値を元にKubernetesリソースを選択するためのものです。
フィールドセレクタークエリの例は以下の通りです。

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

下記のkubectlコマンドは、status.phaseフィールドの値がRunningである全てのPodを選択します。

kubectl get pods --field-selector status.phase=Running

サポートされているフィールド

サポートされているフィールドセレクターはKubernetesリソースタイプによって異なります。全てのリソースタイプはmetadata.namemetadata.namespaceフィールドをサポートしています。サポートされていないフィールドセレクターの使用をするとエラーとなります。
例えば以下の通りです。

kubectl get ingress --field-selector foo.bar=baz
Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"

サポートされているオペレーター

ユーザーは、===!=といったオペレーターをフィールドセレクターと組み合わせて使用できます。(===は同義)
例として、下記のkubectlコマンドはdefaultネームスペースに属していない全てのKubernetes Serviceを選択します。

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

連結されたセレクター

ラベルや他のセレクターと同様に、フィールドセレクターはコンマ区切りのリストとして連結することができます。
下記のkubectlコマンドは、status.phaseRunnningでなく、かつspec.restartPolicyフィールドがAlwaysに等しいような全てのPodを選択します。

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

複数のリソースタイプ

ユーザーは複数のリソースタイプにまたがったフィールドセレクターを利用できます。
下記のkubectlコマンドは、defaultネームスペースに属していない全てのStatefulSetとServiceを選択します。

kubectl get statefulsets,services --field-selector metadata.namespace!=default

3.1.3.8 - ファイナライザー(Finalizers)

ファイナライザーは、削除対象としてマークされたリソースを完全に削除する前に、特定の条件が満たされるまでKubernetesを待機させるための名前空間付きのキーです。 ファイナライザーは、削除されたオブジェクトが所有していたリソースをクリーンアップするようにコントローラーに警告します。

Kubernetesにファイナライザーが指定されたオブジェクトを削除するように指示すると、Kubernetes APIはそのオブジェクトに.metadata.deletionTimestampを追加し削除対象としてマークして、ステータスコード202(HTTP "Accepted")を返します。 コントロールプレーンやその他のコンポーネントがファイナライザーによって定義されたアクションを実行している間、対象のオブジェクトは終了中の状態のまま残っています。 それらのアクションが完了したら、そのコントローラーは関係しているファイナライザーを対象のオブジェクトから削除します。 metadata.finalizersフィールドが空になったら、Kubernetesは削除が完了したと判断しオブジェクトを削除します。

ファイナライザーはリソースのガベージコレクションを管理するために使うことができます。 例えば、コントローラーが対象のリソースを削除する前に関連するリソースやインフラをクリーンアップするためにファイナライザーを定義することができます。

ファイナライザーを利用すると、対象のリソースを削除する前に特定のクリーンアップを行うようにコントローラーに警告することで、ガベージコレクションを管理することができます。

大抵の場合ファイナライザーは実行されるコードを指定することはありません。 その代わり、一般的にはアノテーションのように特定のリソースに関するキーのリストになります。 Kubernetesはいくつかのファイナライザーを自動的に追加しますが、自分で追加することもできます。

ファイナライザーはどのように動作するか

マニフェストファイルを使ってリソースを作るとき、metadata.finalizersフィールドの中でファイナライザーを指定することができます。 リソースを削除しようとするとき、削除リクエストを扱うAPIサーバーはfinalizersフィールドの値を確認し、以下のように扱います。

  • 削除を開始した時間をオブジェクトのmetadata.deletionTimestampフィールドに設定します。
  • metadata.finalizersフィールドが空になるまでオブジェクトが削除されるのを阻止します。
  • ステータスコード202(HTTP "Accepted")を返します。

ファイナライザーを管理しているコントローラーは、オブジェクトの削除がリクエストされたことを示すmetadata.deletionTimestampがオブジェクトに設定されたことを検知します。 するとコントローラーはリソースに指定されたファイナライザーの要求を満たそうとします。 ファイナライザーの条件が満たされるたびに、そのコントローラーはリソースのfinalizersフィールドの対象のキーを削除します。 finalizersフィールドが空になったとき、deletionTimestampフィールドが設定されたオブジェクトは自動的に削除されます。管理外のリソース削除を防ぐためにファイナライザーを利用することもできます。

ファイナライザーの一般的な例はkubernetes.io/pv-protectionで、これは PersistentVolumeオブジェクトが誤って削除されるのを防ぐためのものです。 PersistentVolumeオブジェクトをPodが利用中の場合、Kubernetesはpv-protectionファイナライザーを追加します。 PersistentVolumeを削除しようとするとTerminatingステータスになりますが、ファイナライザーが存在しているためコントローラーはボリュームを削除することができません。 PodがPersistentVolumeの利用を停止するとKubernetesはpv-protectionファイナライザーを削除し、コントローラーがボリュームを削除します。

オーナーリファレンス、ラベル、ファイナライザー

ラベルのように、 オーナーリファレンスはKubernetesのオブジェクト間の関係性を説明しますが、利用される目的が異なります。 コントローラー がPodのようなオブジェクトを管理するとき、関連するオブジェクトのグループの変更を追跡するためにラベルを利用します。 例えば、JobがいくつかのPodを作成するとき、JobコントローラーはそれらのPodにラベルを付け、クラスター内の同じラベルを持つPodの変更を追跡します。

Jobコントローラーは、Podを作成したJobを指すオーナーリファレンスもそれらのPodに追加します。 Podが実行されているときにJobを削除すると、Kubernetesはオーナーリファレンス(ラベルではない)を使って、クリーンアップする必要のあるPodをクラスター内から探し出します。

また、Kubernetesは削除対象のリソースのオーナーリファレンスを認識して、ファイナライザーを処理します。

状況によっては、ファイナライザーが依存オブジェクトの削除をブロックしてしまい、対象のオーナーオブジェクトが完全に削除されず予想以上に長時間残ってしまうことがあります。 このような状況では、対象のオーナーと依存オブジェクトの、ファイナライザーとオーナーリファレンスを確認して問題を解決する必要があります。

次の項目

3.1.3.9 - オーナーと従属

Kubernetesでは、いくつかのオブジェクトは他のオブジェクトのオーナーになっています。 例えば、ReplicaSetはPodの集合のオーナーです。 これらの所有されているオブジェクトはオーナーに従属しています。

オーナーシップはいくつかのリソースでも使われているラベルとセレクターとは仕組みが異なります。 例として、EndpointSliceオブジェクトを作成するServiceオブジェクトを考えてみます。 Serviceはラベルを使ってどのEndpointSliceがどのServiceに利用されているかをコントロールプレーンに判断させています。 ラベルに加えて、Serviceの代わりに管理される各EndpointSliceはオーナーリファレンスを持ちます。 オーナーリファレンスは、Kubernetesの様々な箇所で管理外のオブジェクトに干渉してしまうのを避けるのに役立ちます。

オブジェクト仕様におけるオーナーリファレンス

従属オブジェクトはオーナーオブジェクトを参照するためのmetadata.ownerReferencesフィールドを持っています。 有効なオーナーリファレンスは従属オブジェクトと同じ名前空間に存在するオブジェクトの名前とUIDで構成されます。 KubernetesはReplicaSet、DaemonSet、Deployment、Job、CronJob、ReplicationControllerのようなオブジェクトの従属オブジェクトに、自動的に値を設定します。 このフィールドの値を手動で変更することで、これらの関係性を自分で設定することもできます。 ただし、通常はその必要はなく、Kubernetesが自動で管理するようにすることができます。

従属オブジェクトは、オーナーオブジェクトが削除されたときにガベージコレクションをブロックするかどうかを管理する真偽値を取るownerReferences.blockOwnerDeletionフィールドも持っています。 Kubernetesは、コントローラー (例:Deploymentコントローラー)がmetadata.ownerReferencesフィールドに値を設定している場合、自動的にこのフィールドをtrueに設定します。 blockOwnerDeletionフィールドに手動で値を設定することで、どの従属オブジェクトがガベージコレクションをブロックするかを設定することもできます。

Kubernetesのアドミッションコントローラーはオーナーの削除権限に基づいて、ユーザーが従属リソースのこのフィールドを変更できるかを管理しています。 これにより、認証されていないユーザーがオーナーオブジェクトの削除を遅らせることを防ぎます。

オーナーシップとファイナライザー

Kubernetesでリソースを削除するとき、APIサーバーはリソースを管理するコントローラーにファイナライザールールを処理させることができます。 ファイナライザーはクラスターが正しく機能するために必要なリソースを誤って削除してしまうことを防ぎます。 例えば、まだPodが使用中のPersistentVolumeを削除しようとするとき、PersistentVolumeが持っているkubernetes.io/pv-protectionファイナライザーにより、削除は即座には行われません。 その代わり、Kubernetesがファイナライザーを削除するまでボリュームはTerminatingステータスのまま残り、PersistentVolumeがPodにバインドされなくなった後で削除が行われます。

またKubernetesはフォアグラウンド、孤立したオブジェクトのカスケード削除を行ったとき、オーナーリソースにファイナライザーを追加します。 フォアグラウンド削除では、foregroundファイナライザーを追加し、オーナーを削除する前にコントローラーがownerReferences.blockOwnerDeletion=trueを持っている従属リソースを削除するようにします。 孤立したオブジェクトの削除を行う場合、Kubernetesはorphanファイナライザーを追加し、オーナーオブジェクトを削除した後にコントローラーが従属リソースを無視するようにします。

次の項目

3.1.3.10 - 推奨ラベル(Recommended Labels)

ユーザーはkubectlやダッシュボード以外に、多くのツールでKubernetesオブジェクトの管理と可視化ができます。共通のラベルセットにより、全てのツールにおいて解釈可能な共通のマナーに沿ってオブジェクトを表現することで、ツールの相互運用を可能にします。

ツール化に対するサポートに加えて、推奨ラベルはクエリ可能な方法でアプリケーションを表現します。

メタデータは、アプリケーション のコンセプトを中心に構成されています。KubernetesはPaaS(Platform as a Service)でなく、アプリケーションの公式な概念を持たず、またそれを強制することはありません。 そのかわり、アプリケーションは、非公式で、メタデータによって表現されています。単一のアプリケーションが有する項目に対する定義は厳密に決められていません。

共有されたラベルとアノテーションは、app.kubernetes.ioという共通のプレフィックスを持ちます。プレフィックスの無いラベルはユーザーに対してプライベートなものになります。その共有されたプレフィックスは、共有ラベルがユーザーのカスタムラベルに干渉しないことを保証します。

ラベル

これらの推奨ラベルの利点を最大限得るためには、全てのリソースオブジェクトに対して推奨ラベルが適用されるべきです。

キー説明
app.kubernetes.io/nameアプリケーション名mysql文字列
app.kubernetes.io/instanceアプリケーションのインスタンスを特定するための固有名mysql-abcxzy文字列
app.kubernetes.io/versionアプリケーションの現在のバージョン (例: セマンティックバージョン、リビジョンのハッシュなど)5.7.21文字列
app.kubernetes.io/componentアーキテクチャ内のコンポーネントdatabase文字列
app.kubernetes.io/part-ofこのアプリケーションによって構成される上位レベルのアプリケーションwordpress文字列
app.kubernetes.io/managed-byこのアプリケーションの操作を管理するために使われているツールhelm文字列

これらのラベルが実際にどう使われているかを表すために、下記のStatefulSetのオブジェクトを考えましょう。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
    app.kubernetes.io/managed-by: helm

アプリケーションとアプリケーションのインスタンス

単一のアプリケーションは、Kubernetesクラスター内で、いくつかのケースでは同一の名前空間に対して1回または複数回インストールされることがあります。 例えば、WordPressは複数のウェブサイトがあれば、それぞれ別のWordPressが複数回インストールされることがあります。

アプリケーション名と、アプリケーションのインスタンス名はそれぞれ別に記録されます。 例えば、WordPressはapp.kubernetes.io/namewordpressと記述され、インスタンス名に関してはapp.kubernetes.io/instancewordpress-abcxzyと記述されます。この仕組みはアプリケーションと、アプリケーションのインスタンスを特定可能にします。全てのアプリケーションインスタンスは固有の名前を持たなければなりません。

ラベルの使用例

ここでは、ラベルの異なる使用例を示します。これらの例はそれぞれシステムの複雑さが異なります。

シンプルなステートレスサービス

DeploymentServiceオブジェクトを使って、シンプルなステートレスサービスをデプロイするケースを考えます。下記の2つのスニペットはラベルが最もシンプルな形式においてどのように使われるかをあらわします。

下記のDeploymentは、アプリケーションを稼働させるポッドを管理するのに使われます。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxzy
...

下記のServiceは、アプリケーションを公開するために使われます。

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxzy
...

データベースを使ったウェブアプリケーション

次にもう少し複雑なアプリケーションについて考えます。データベース(MySQL)を使ったウェブアプリケーション(WordPress)で、Helmを使ってインストールします。 下記のスニペットは、このアプリケーションをデプロイするために使うオブジェクト設定の出だし部分です。

はじめに下記のDeploymentは、WordPressのために使われます。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxzy
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

下記のServiceは、WordPressを公開するために使われます。

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxzy
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

MySQLはStatefulSetとして公開され、MySQL自身と、MySQLが属する親アプリケーションのメタデータを持ちます。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

このServiceはMySQLをWordPressアプリケーションの一部として公開します。

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

MySQLのStatefulSetServiceにより、MySQLとWordPressに関するより広範な情報が含まれていることに気づくでしょう。

3.2 - クラスターのアーキテクチャ

Kubernetesの背後にあるアーキテクチャのコンセプト。
Components of Kubernetes

Kubernetesクラスターのアーキテクチャ

3.2.1 - ノード

Kubernetesはコンテナを Node 上で実行されるPodに配置することで、ワークロードを実行します。 ノードはクラスターによりますが、1つのVMまたは物理的なマシンです。 各ノードはPodやそれを制御するコントロールプレーンを実行するのに必要なサービスを含んでいます。

通常、1つのクラスターで複数のノードを持ちます。学習用途やリソースの制限がある環境では、1ノードかもしれません。

1つのノード上のコンポーネントには、kubeletコンテナランタイムkube-proxyが含まれます。

管理

ノードをAPIサーバーに加えるには2つの方法があります:

  1. ノード上のkubeletが、コントロールプレーンに自己登録する。
  2. あなた、もしくは他のユーザーが手動でNodeオブジェクトを追加する。

Nodeオブジェクトの作成、もしくはノード上のkubeletによる自己登録の後、コントロールプレーンはNodeオブジェクトが有効かチェックします。例えば、下記のjsonマニフェストでノードを作成してみましょう:

{
  "kind": "Node",
  "apiVersion": "v1",
  "metadata": {
    "name": "10.240.79.157",
    "labels": {
      "name": "my-first-k8s-node"
    }
  }
}

Kubernetesは内部的にNodeオブジェクトを作成します。 APIサーバーに登録したkubeletがノードのmetadata.nameフィールドが一致しているか検証します。ノードが有効な場合、つまり必要なサービスがすべて実行されている場合は、Podを実行する資格があります。それ以外の場合、該当ノードが有効になるまではいかなるクラスターの活動に対しても無視されます。

Nodeオブジェクトの名前は有効なDNSサブドメイン名である必要があります。

ノードの自己登録

kubeletのフラグ --register-nodeがtrue(デフォルト)のとき、kubeletは自分自身をAPIサーバーに登録しようとします。これはほとんどのディストリビューションで使用されている推奨パターンです。

自己登録については、kubeletは以下のオプションを伴って起動されます:

  • --kubeconfig - 自分自身をAPIサーバーに対して認証するための資格情報へのパス
  • --cloud-provider - 自身に関するメタデータを読むためにクラウドプロバイダーと会話する方法
  • --register-node - 自身をAPIサーバーに自動的に登録
  • --register-with-taints - 与えられたtaintのリストでノードを登録します (カンマ区切りの <key>=<value>:<effect>)。

register-nodeがfalseの場合、このオプションは機能しません

  • --node-ip - ノードのIPアドレス
  • --node-labels - ノードをクラスターに登録するときに追加するLabelNodeRestriction許可プラグインによって適用されるラベルの制限を参照)
  • --node-status-update-frequency - kubeletがノードのステータスをマスターにPOSTする頻度の指定

ノード認証モードおよびNodeRestriction許可プラグインが有効になっている場合、kubeletは自分自身のノードリソースを作成/変更することのみ許可されています。

手動によるノード管理

クラスター管理者はkubectlを使用してNodeオブジェクトを作成および変更できます。

管理者が手動でNodeオブジェクトを作成したい場合は、kubeletフラグ --register-node = falseを設定してください。

管理者は--register-nodeの設定に関係なくNodeオブジェクトを変更することができます。 例えば、ノードにラベルを設定し、それをunschedulableとしてマークすることが含まれます。

ノード上のラベルは、スケジューリングを制御するためにPod上のノードセレクターと組み合わせて使用できます。 例えば、Podをノードのサブセットでのみ実行する資格があるように制限します。

ノードをunschedulableとしてマークすると、新しいPodがそのノードにスケジュールされるのを防ぎますが、ノード上の既存のPodには影響しません。 これは、ノードの再起動などの前の準備ステップとして役立ちます。

ノードにスケジュール不可能のマークを付けるには、次のコマンドを実行します:

kubectl cordon $ノード名

ノードのステータス

ノードのステータスは以下の情報を含みます:

kubectlを使用し、ノードのステータスや詳細を確認できます:

kubectl describe node <ノード名をここに挿入>

出力情報の各箇所について、以下で説明します。

Addresses

これらのフィールドの使い方は、お使いのクラウドプロバイダーやベアメタルの設定内容によって異なります。

  • HostName: ノードのカーネルによって伝えられたホスト名です。kubeletの--hostname-overrideパラメーターによって上書きすることができます。
  • ExternalIP: 通常は、外部にルーティング可能(クラスターの外からアクセス可能)なノードのIPアドレスです。
  • InternalIP: 通常は、クラスター内でのみルーティング可能なノードのIPアドレスです。

Conditions

conditionsフィールドは全てのRunningなノードのステータスを表します。例として、以下のような状態を含みます:

ノードのConditionと、各condition適用時の概要
ノードのCondition概要
Readyノードの状態が有効でPodを配置可能な場合にTrueになります。ノードの状態に問題があり、Podが配置できない場合にFalseになります。ノードコントローラーが、node-monitor-grace-periodで設定された時間内(デフォルトでは40秒)に該当ノードと疎通できない場合、Unknownになります。
DiskPressureノードのディスク容量が圧迫されているときにTrueになります。圧迫とは、ディスクの空き容量が少ないことを指します。それ以外のときはFalseです。
MemoryPressureノードのメモリが圧迫されているときにTrueになります。圧迫とは、メモリの空き容量が少ないことを指します。それ以外のときはFalseです。
PIDPressureプロセスが圧迫されているときにTrueになります。圧迫とは、プロセス数が多すぎることを指します。それ以外のときはFalseです。
NetworkUnavailableノードのネットワークが適切に設定されていない場合にTrueになります。それ以外のときはFalseです。

Nodeの状態は、Nodeリソースの.statusの一部として表現されます。例えば、正常なノードの場合は以下のようなjson構造が表示されます。

"conditions": [
  {
    "type": "Ready",
    "status": "True",
    "reason": "KubeletReady",
    "message": "kubelet is posting ready status",
    "lastHeartbeatTime": "2019-06-05T18:38:35Z",
    "lastTransitionTime": "2019-06-05T11:41:27Z"
  }
]

Ready conditionがpod-eviction-timeout(kube-controller-managerに渡された引数)に設定された時間を超えてもUnknownFalseのままになっている場合、該当ノード上にあるPodはノードコントローラーによって削除がスケジュールされます。デフォルトの退役のタイムアウトの時間は5分です。ノードが到達不能ないくつかの場合においては、APIサーバーが該当ノードのkubeletと疎通できない状態になっています。その場合、APIサーバーがkubeletと再び通信を確立するまでの間、Podの削除を行うことはできません。削除がスケジュールされるまでの間、削除対象のPodは切り離されたノードの上で稼働を続けることになります。

ノードコントローラーはクラスター内でPodが停止するのを確認するまでは強制的に削除しないようになりました。到達不能なノード上で動いているPodはTerminatingまたはUnknownのステータスになります。Kubernetesが基盤となるインフラストラクチャーを推定できない場合、クラスター管理者は手動でNodeオブジェクトを削除する必要があります。KubernetesからNodeオブジェクトを削除すると、そのノードで実行されているすべてのPodオブジェクトがAPIサーバーから削除され、それらの名前が解放されます。

ノードのライフサイクルコントローラーがconditionを表したtaintを自動的に生成します。 スケジューラーがPodをノードに割り当てる際、ノードのtaintを考慮します。Podが許容するtaintは例外です。

詳細は条件によるtaintの付与を参照してください。

CapacityとAllocatable

ノードで利用可能なリソース(CPU、メモリ、およびノードでスケジュールできる最大Pod数)について説明します。

capacityブロック内のフィールドは、ノードが持っているリソースの合計量を示します。 allocatableブロックは、通常のPodによって消費されるノード上のリソースの量を示します。

CapacityとAllocatableについて深く知りたい場合は、ノード上でどのようにコンピュートリソースが予約されるかを読みながら学ぶことができます。

Info

カーネルのバージョン、Kubernetesのバージョン(kubeletおよびkube-proxyのバージョン)、(使用されている場合)Dockerのバージョン、OS名など、ノードに関する一般的な情報です。 この情報はノードからkubeletを通じて取得され、Kubernetes APIに公開されます。

ハートビート

ハートビートは、Kubernetesノードから送信され、ノードが利用可能か判断するのに役立ちます。 以下の2つのハートビートがあります:

  • Nodeの.statusの更新
  • Lease objectです。 各ノードはkube-node-leaseというnamespaceに関連したLeaseオブジェクトを持ちます。 Leaseは軽量なリソースで、クラスターのスケールに応じてノードのハートビートにおけるパフォーマンスを改善します。

kubeletがNodeStatusとLeaseオブジェクトの作成および更新を担当します。

  • kubeletは、ステータスに変化があったり、設定した間隔の間に更新がない時にNodeStatusを更新します。NodeStatus更新のデフォルト間隔は5分です。(到達不能の場合のデフォルトタイムアウトである40秒よりもはるかに長いです)
  • kubeletは10秒間隔(デフォルトの更新間隔)でLeaseオブジェクトの生成と更新を実施します。Leaseの更新はNodeStatusの更新とは独立されて行われます。Leaseの更新が失敗した場合、kubeletは200ミリ秒から始まり7秒を上限とした指数バックオフでリトライします。

ノードコントローラー

ノードコントローラーは、ノードのさまざまな側面を管理するKubernetesのコントロールプレーンコンポーネントです。

ノードコントローラーは、ノードの存続期間中に複数の役割を果たします。1つ目は、ノードが登録されたときにCIDRブロックをノードに割り当てることです(CIDR割り当てがオンになっている場合)。

2つ目は、ノードコントローラーの内部ノードリストをクラウドの利用可能なマシンのリストと一致させることです。 クラウド環境で実行している場合、ノードに異常があると、ノードコントローラーはクラウドプロバイダーにそのNodeのVMがまだ使用可能かどうかを問い合わせます。 使用可能でない場合、ノードコントローラーはノードのリストから該当ノードを削除します。

3つ目は、ノードの状態を監視することです。 ノードが到達不能(例えば、ノードがダウンしているなどので理由で、ノードコントローラーがハートビートの受信を停止した場合)になると、ノードコントローラーは、NodeStatusのNodeReady conditionをConditionUnknownに変更する役割があります。その後も該当ノードが到達不能のままであった場合、Graceful Terminationを使って全てのPodを退役させます。デフォルトのタイムアウトは、ConditionUnknownの報告を開始するまで40秒、その後Podの追い出しを開始するまで5分に設定されています。 ノードコントローラーは、--node-monitor-periodに設定された秒数ごとに各ノードの状態をチェックします。

信頼性

ほとんどの場合、排除の速度は1秒あたり--node-eviction-rateに設定された数値(デフォルトは秒間0.1)です。つまり、10秒間に1つ以上のPodをノードから追い出すことはありません。

特定のアベイラビリティーゾーン内のノードのステータスが異常になると、ノード排除の挙動が変わります。ノードコントローラーは、ゾーン内のノードの何%が異常(NodeReady条件がConditionUnknownまたはConditionFalseである)であるかを同時に確認します。 異常なノードの割合が少なくとも --healthy-zone-thresholdに設定した値を下回る場合(デフォルトは0.55)であれば、退役率は低下します。クラスターが小さい場合(すなわち、 --large-cluster-size-thresholdの設定値よりもノード数が少ない場合。デフォルトは50)、退役は停止し、そうでない場合、退役率は秒間で--secondary-node-eviction-rateの設定値(デフォルトは0.01)に減少します。 これらのポリシーがアベイラビリティーゾーンごとに実装されているのは、1つのアベイラビリティーゾーンがマスターから分割される一方、他のアベイラビリティーゾーンは接続されたままになる可能性があるためです。 クラスターが複数のクラウドプロバイダーのアベイラビリティーゾーンにまたがっていない場合、アベイラビリティーゾーンは1つだけです(クラスター全体)。

ノードを複数のアベイラビリティゾーンに分散させる主な理由は、1つのゾーン全体が停止したときにワークロードを正常なゾーンに移動できることです。 したがって、ゾーン内のすべてのノードが異常である場合、ノードコントローラーは通常のレート --node-eviction-rateで退役します。 コーナーケースは、すべてのゾーンが完全にUnhealthyである(すなわち、クラスター内にHealthyなノードがない)場合です。 このような場合、ノードコントローラーはマスター接続に問題があると見なし、接続が回復するまですべての退役を停止します。

ノードコントローラーは、Podがtaintを許容しない場合、 NoExecuteのtaintを持つノード上で実行されているPodを排除する責務もあります。 さらに、ノードコントローラーはノードに到達できない、または準備ができていないなどのノードの問題に対応するtaintを追加する責務があります。これはスケジューラーが、問題のあるノードにPodを配置しない事を意味しています。

ノードのキャパシティ

Nodeオブジェクトはノードのリソースキャパシティ(CPUの数とメモリの量)を監視します。 自己登録したノードは、Nodeオブジェクトを作成するときにキャパシティを報告します。 手動によるノード管理を実行している場合は、ノードを追加するときにキャパシティを設定する必要があります。

Kubernetesスケジューラーは、ノード上のすべてのPodに十分なリソースがあることを確認します。スケジューラーは、ノード上のコンテナが要求するリソースの合計がノードキャパシティ以下であることを確認します。 これは、kubeletによって管理されたすべてのコンテナを含みますが、コンテナランタイムによって直接開始されたコンテナやkubeletの制御外で実行されているプロセスは含みません。

ノードのトポロジー

FEATURE STATE: Kubernetes v1.16 [alpha]
TopologyManagerフィーチャーゲートを有効にすると、 kubeletはリソースの割当を決定する際にトポロジーのヒントを利用できます。 詳細は、ノードのトポロジー管理ポリシーを制御するを参照してください。

ノードの正常終了

FEATURE STATE: Kubernetes v1.21 [beta]

kubeletは、ノードのシステムシャットダウンを検出すると、ノード上で動作しているPodを終了させます。

Kubelet は、ノードのシャットダウン時に、ポッドが通常の通常のポッド終了プロセスに従うようにします。

Graceful Node Shutdownはsystemdに依存しているため、systemd inhibitor locksを 利用してノードのシャットダウンを一定時間遅らせることができます。

Graceful Node Shutdownは、v1.21でデフォルトで有効になっているGracefulNodeShutdown フィーチャーゲートで制御されます。

なお、デフォルトでは、後述の設定オプションShutdownGracePeriodおよびShutdownGracePeriodCriticalPodsの両方がゼロに設定されているため、Graceful node shutdownは有効になりません。この機能を有効にするには、この2つのkubeletの設定を適切に設定し、ゼロ以外の値を設定する必要があります。

Graceful shutdownでは、kubeletは以下の2段階でPodを終了させます。

  1. そのノード上で動作している通常のPodを終了させます。
  2. そのノード上で動作しているcritical podsを終了させます。

Graceful Node Shutdownには、2つのKubeletConfigurationオプションを設定します。:

  • ShutdownGracePeriod:
    • ノードがシャットダウンを遅らせるべき合計期間を指定します。これは、通常のPodとcritical podsの両方のPod終了の合計猶予期間です。
  • ShutdownGracePeriodCriticalPods:
    • ノードのシャットダウン時にcritical podsを終了させるために使用する期間を指定します。この値は、ShutdownGracePeriodよりも小さくする必要があります。

例えば、ShutdownGracePeriod=30sShutdownGracePeriodCriticalPods=10sとすると、 kubeletはノードのシャットダウンを30秒遅らせます。シャットダウンの間、最初の20(30-10)秒は通常のポッドを優雅に終了させるために確保され、 残りの10秒は重要なポッドを終了させるために確保されることになります。

ノードの非正常終了

FEATURE STATE: Kubernetes v1.26 [beta]

コマンドがkubeletのinhibitor locksメカニズムをトリガーしない場合や、ShutdownGracePeriodやShutdownGracePeriodCriticalPodsが適切に設定されていないといったユーザーによるミス等が原因で、ノードがシャットダウンしたことをkubeletのNode Shutdownマネージャーが検知できないことがあります。詳細は上記セクションノードの正常終了を参照ください。

ノードのシャットダウンがkubeletのNode Shutdownマネージャーに検知されない場合、StatefulSetを構成するPodはシャットダウン状態のノード上でterminating状態のままになってしまい、他の実行中のノードに移動することができなくなってしまいます。これは、ノードがシャットダウンしているため、その上のkubeletがPodを削除できず、それにより、StatefulSetが新しいPodを同じ名前で作成できなくなってしまうためです。Podがボリュームを使用している場合、VolumeAttachmentsはシャットダウン状態のノードによって削除されないため、Podが使用しているボリュームは他の実行中のノードにアタッチすることができなくなってしまいます。その結果として、StatefulSet上で実行中のアプリケーションは適切に機能しなくなってしまいます。シャットダウンしていたノードが復旧した場合、そのノード上のPodはkubeletに削除され、他の実行中のノード上に作成されます。また、シャットダウン状態のノードが復旧できなかった場合は、そのノード上のPodは永久にterminating状態のままとなります。

上記の状況を脱却するには、ユーザーが手動でNoExecuteまたはNoSchedule effectを設定してnode.kubernetes.io/out-of-service taintをノードに付与することで、故障中の状態に設定することができます。kube-controller-manager において NodeOutOfServiceVolumeDetachフィーチャーゲートが有効になっており、かつノードがtaintによって故障中としてマークされている場合は、ノードに一致するtolerationがないPodは強制的に削除され、ノード上のterminating状態のPodに対するボリュームデタッチ操作が直ちに実行されます。これにより、故障中のノード上のPodを異なるノード上にすばやく復旧させることが可能になります。

non-graceful shutdownの間に、Podは以下の2段階で終了します:

  1. 一致するout-of-service tolerationを持たないPodを強制的に削除する。
  2. 上記のPodに対して即座にボリュームデタッチ操作を行う。

スワップメモリの管理

FEATURE STATE: Kubernetes v1.22 [alpha]

Kubernetes 1.22以前では、ノードはスワップメモリの使用をサポートしておらず、ノード上でスワップが検出された場合、 kubeletはデフォルトで起動に失敗していました。1.22以降では、スワップメモリのサポートをノードごとに有効にすることができます。

ノードでスワップを有効にするには、kubeletの NodeSwap フィーチャーゲートを有効にし、 --fail-swap-onコマンドラインフラグまたはfailSwapOnKubeletConfigurationを false に設定する必要があります。

ユーザーはオプションで、ノードがスワップメモリをどのように使用するかを指定するために、memorySwap.swapBehaviorを設定することもできます。ノードがスワップメモリをどのように使用するかを指定します。例えば、以下のようになります。

memorySwap:
  swapBehavior: LimitedSwap

swapBehaviorで使用できる設定オプションは以下の通りです。:

  • LimitedSwap: Kubernetesのワークロードが、使用できるスワップ量に制限を設けます。Kubernetesが管理していないノード上のワークロードは、依然としてスワップを使用できます。
  • UnlimitedSwap: Kubernetesのワークロードが使用できるスワップ量に制限を設けません。システムの限界まで、要求されただけのスワップメモリを使用することができます。

memorySwapの設定が指定されておらず、フィーチャーゲートが有効な場合、デフォルトのkubeletはLimitedSwapの設定と同じ動作を適用します。

LimitedSwap設定の動作は、ノードがコントロールグループ(「cgroups」とも呼ばれる)のv1とv2のどちらで動作しているかによって異なります。

Kubernetesのワークロードでは、メモリとスワップを組み合わせて使用することができ、ポッドのメモリ制限が設定されている場合はその制限まで使用できます。

  • cgroupsv1: Kubernetesのワークロードは、メモリとスワップを組み合わせて使用することができ、ポッドのメモリ制限が設定されている場合はその制限まで使用できます。
  • cgroupsv2: Kubernetesのワークロードは、スワップメモリを使用できません。

詳しくは、KEP-2400design proposalをご覧いただき、テストにご協力、ご意見をお聞かせください。

次の項目

3.2.2 - ノードとコントロールプレーン間の通信

本ドキュメントは、APIサーバーとKubernetesクラスター間の通信経路をまとめたものです。 その目的は、信頼できないネットワーク上(またはクラウドプロバイダー上の完全なパブリックIP)でクラスターが実行できるよう、ユーザーがインストールをカスタマイズしてネットワーク構成を強固にできるようにすることです。

ノードからコントロールプレーンへの通信

Kubernetesには「ハブアンドスポーク」というAPIパターンがあります。ノード(またはノードが実行するPod)からのすべてのAPIの使用は、APIサーバーで終了します。他のコントロールプレーンコンポーネントは、どれもリモートサービスを公開するようには設計されていません。APIサーバーは、1つ以上の形式のクライアント認証が有効になっている状態で、セキュアなHTTPSポート(通常は443)でリモート接続をリッスンするように設定されています。 特に匿名リクエストサービスアカウントトークンが許可されている場合は、1つ以上の認可形式を有効にする必要があります。

ノードは、有効なクライアント認証情報とともに、APIサーバーに安全に接続できるように、クラスターのパブリックルート証明書でプロビジョニングされる必要があります。適切なやり方は、kubeletに提供されるクライアント認証情報が、クライアント証明書の形式であることです。kubeletクライアント証明書の自動プロビジョニングについては、kubelet TLSブートストラップを参照してください。

APIサーバーに接続したいPodは、サービスアカウントを利用することで、安全に接続することができます。これにより、Podのインスタンス化時に、Kubernetesはパブリックルート証明書と有効なBearerトークンを自動的にPodに挿入します。 kubernetesサービス(デフォルトの名前空間)は、APIサーバー上のHTTPSエンドポイントに(kube-proxy経由で)リダイレクトされる仮想IPアドレスで構成されます。

また、コントロールプレーンのコンポーネントは、セキュアなポートを介してAPIサーバーとも通信します。

その結果、ノードやノード上で動作するPodからコントロールプレーンへの接続は、デフォルトでセキュアであり、信頼されていないネットワークやパブリックネットワークを介して実行することができます。

コントロールプレーンからノードへの通信

コントロールプレーン(APIサーバー)からノードへの主要な通信経路は2つあります。 1つ目は、APIサーバーからクラスター内の各ノードで実行されるkubeletプロセスへの通信経路です。 2つ目は、APIサーバーの プロキシ 機能を介した、APIサーバーから任意のノード、Pod、またはサービスへの通信経路です。

APIサーバーからkubeletへの通信

APIサーバーからkubeletへの接続は、以下の目的で使用されます:

  • Podのログの取得。
  • 実行中のPodへのアタッチ(通常はkubectlを使用)。
  • kubeletのポート転送機能の提供。

これらの接続は、kubeletのHTTPSエンドポイントで終了します。デフォルトでは、APIサーバーはkubeletのサービング証明書を検証しないため、接続は中間者攻撃の対象となり、信頼されていないネットワークやパブリックネットワークを介して実行するのは安全ではありません

この接続を検証するには、--kubelet-certificate-authorityフラグを使用して、kubeletのサービング証明書を検証するために使用するルート証明書バンドルを、APIサーバーに提供します。

それができない場合は、信頼できないネットワークやパブリックネットワークを介した接続を回避するため、必要に応じてAPIサーバーとkubeletの間でSSHトンネルを使用します。

最後に、kubelet APIを保護するために、Kubelet認証/認可を有効にする必要があります。

APIサーバーからノード、Pod、サービスへの通信

APIサーバーからノード、Pod、またはサービスへの接続は、デフォルトで平文のHTTP接続になるため、認証も暗号化もされません。API URL内のノード、Pod、サービス名にhttps:を付けることで、セキュアなHTTPS接続を介して実行できますが、HTTPSエンドポイントから提供された証明書を検証したり、クライアント認証情報を提供したりすることはありません。そのため、接続の暗号化はされますが、完全性の保証はありません。これらの接続を、信頼されていないネットワークやパブリックネットワークを介して実行するのは、現在のところ安全ではありません

SSHトンネル

Kubernetesは、コントロールプレーンからノードへの通信経路を保護するために、SSHトンネルをサポートしています。この構成では、APIサーバーがクラスター内の各ノードへのSSHトンネルを開始(ポート22でリッスンしているSSHサーバーに接続)し、kubelet、ノード、Pod、またはサービス宛てのすべてのトラフィックをトンネル経由で渡します。 このトンネルにより、ノードが稼働するネットワークの外部にトラフィックが公開されないようになります。

Konnectivityサービス

FEATURE STATE: Kubernetes v1.18 [beta]

SSHトンネルの代替として、Konnectivityサービスは、コントロールプレーンからクラスターへの通信に、TCPレベルのプロキシを提供します。Konnectivityサービスは、コントロールプレーンネットワークのKonnectivityサーバーと、ノードネットワークのKonnectivityエージェントの、2つの部分で構成されています。 Konnectivityエージェントは、Konnectivityサーバーへの接続を開始し、ネットワーク接続を維持します。 Konnectivityサービスを有効にすると、コントロールプレーンからノードへのトラフィックは、すべてこの接続を経由するようになります。

Konnectivityサービスのセットアップに従って、クラスターにKonnectivityサービスをセットアップしてください。

次の項目

3.2.3 - リース

しばしば分散システムでは共有リソースをロックしたりノード間の活動を調整する機構として"リース"が必要になります。 Kubernetesでは、"リース"のコンセプトはcoordination.k8s.ioAPIグループのLeaseオブジェクトに表されていて、ノードのハートビートやコンポーネントレベルのリーダー選出といったシステムにとって重要な機能に利用されています。

ノードハートビート

Kubernetesでは、kubeletのノードハートビートをKubernetes APIサーバーに送信するのにLease APIが使われています。 各Nodeごとに、kube-node-lease名前空間にノードとマッチする名前のLeaseオブジェクトが存在します。 内部的には、kubeletのハートビートはこのLeaseオブジェクトに対するUPDATEリクエストであり、Leaseのspec.renewTimeフィールドを更新しています。 Kubernetesのコントロールプレーンはこのフィールドのタイムスタンプを見て、Nodeが利用可能かを判断しています。

詳しくはNode Leaseオブジェクトをご覧ください。

リーダー選出

Kubernetesでは、あるコンポーネントのインスタンスが常に一つだけ実行されていることを保証するためにもリースが利用されています。 これはkube-controller-managerkube-schedulerといったコントロールプレーンのコンポーネントで、一つのインスタンスのみがアクティブに実行され、その他のインスタンスをスタンバイ状態とする必要があるような冗長構成を組むために利用されています。

APIサーバーのアイデンティティー

FEATURE STATE: Kubernetes v1.26 [beta]

Kubernetes v1.26から、各kube-apiserverはLease APIを利用して自身のアイデンティティーをその他のシステムに向けて公開するようになりました。 それ自体は特に有用ではありませんが、何台のkube-apiserverがKubernetesコントロールプレーンを稼働させているのかをクライアントが知るためのメカニズムを提供します。 kube-apiserverリースが存在することで、各kube-apiserver間の調整が必要となる可能性がある将来の機能が使えるようになります。

kube-system名前空間のkube-apiserver-<sha256-hash>という名前のリースオブジェクトを確認することで、各kube-apiserverが所有しているLeaseを確認することができます。 また、k8s.io/component=kube-apiserverラベルセレクターを利用することもできます。

$ kubectl -n kube-system get lease -l k8s.io/component=kube-apiserver
NAME                                        HOLDER                                                                           AGE
kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a   kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4   5m33s
kube-apiserver-dz2dqprdpsgnm756t5rnov7yka   kube-apiserver-dz2dqprdpsgnm756t5rnov7yka_84f2a85d-37c1-4b14-b6b9-603e62e4896f   4m23s
kube-apiserver-fyloo45sdenffw2ugwaz3likua   kube-apiserver-fyloo45sdenffw2ugwaz3likua_c5ffa286-8a9a-45d4-91e7-61118ed58d2e   4m43s

リースの名前に使われているSHA256ハッシュはkube-apiserverのOSホスト名に基づいています。各kube-apiserverはクラスター内で一意なホスト名を使用するように構成する必要があります。 同じホスト名を利用する新しいkube-apiserverインスタンスは、新しいリースオブジェクトを作成せずに、既存のLeaseを新しいインスタンスのアイデンティティーを利用して引き継ぎます。

kube-apiserverが使用するホスト名はkubernetes.io/hostnameラベルの値で確認できます。

$ kubectl -n kube-system get lease kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2022-11-30T15:37:15Z"
  labels:
    k8s.io/component: kube-apiserver
    kubernetes.io/hostname: kind-control-plane
  name: kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a
  namespace: kube-system
  resourceVersion: "18171"
  uid: d6c68901-4ec5-4385-b1ef-2d783738da6c
spec:
  holderIdentity: kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4
  leaseDurationSeconds: 3600
  renewTime: "2022-11-30T18:04:27.912073Z"

すでに存在しなくなったkube-apiserverの期限切れリースは、1時間後に新しいkube-apiserverによってガベージコレクションされます。

3.2.4 - コントローラー

ロボット工学やオートメーションの分野において、 制御ループ とは、あるシステムの状態を制御する終了状態のないループのことです。

ここでは、制御ループの一例として、部屋の中にあるサーモスタットを挙げます。

あなたが温度を設定すると、それはサーモスタットに 目的の状態(desired state) を伝えることになります。実際の部屋の温度は 現在の状態 です。サーモスタットは、装置をオンまたはオフにすることによって、現在の状態を目的の状態に近づけるように動作します。

Kubernetesにおいて、コントローラーはクラスターの状態を監視し、必要に応じて変更を加えたり要求したりする制御ループです。それぞれのコントローラーは現在のクラスターの状態を望ましい状態に近づけるように動作します。

コントローラーパターン

コントローラーは少なくとも1種類のKubernetesのリソースを監視します。これらのオブジェクトには目的の状態を表すspecフィールドがあります。リソースのコントローラーは、現在の状態を目的の状態に近づける責務を持ちます。

コントローラーは自分自身でアクションを実行する場合もありますが、KubernetesではコントローラーがAPIサーバーに意味のある副作用を持つメッセージを送信することが一般的です。以下では、このような例を見ていきます。

APIサーバー経由でコントロールする

JobコントローラーはKubernetesのビルトインのコントローラーの一例です。ビルトインのコントローラーは、クラスターのAPIサーバーとやりとりをして状態を管理します。

Jobは、1つ以上のPodを起動して、タスクを実行した後に停止する、Kubernetesのリソースです。

(1度スケジュールされると、Podオブジェクトはkubeletに対する目的の状態の一部になります。)

Jobコントローラーが新しいタスクを見つけると、その処理が完了するように、クラスター上のどこかで、一連のNode上のkubeletが正しい数のPodを実行することを保証します。ただし、Jobコントローラーは、自分自身でPodやコンテナを実行することはありません。代わりに、APIサーバーに対してPodの作成や削除を依頼します。コントロールプレーン上の他のコンポーネントが(スケジュールして実行するべき新しいPodが存在するという)新しい情報を基に動作することによって、最終的に目的の処理が完了します。

新しいJobが作成されたとき、目的の状態は、そのJobが完了することです。JobコントローラーはそのJobに対する現在の状態を目的の状態に近づけるようにします。つまり、そのJobが行ってほしい処理を実行するPodを作成し、Jobが完了に近づくようにします。

コントローラーは、コントローラーを設定するオブジェクトも更新します。たとえば、あるJobが完了した場合、Jobコントローラーは、JobオブジェクトにFinishedというマークを付けます。

(これは、部屋が設定温度になったことを示すために、サーモスタットがランプを消灯するのに少し似ています。)

直接的なコントロール

Jobとは対照的に、クラスターの外部に変更を加える必要があるコントローラーもあります。

たとえば、クラスターに十分な数のNodeが存在することを保証する制御ループの場合、そのコントローラーは、必要に応じて新しいNodeをセットアップするために、現在のクラスターの外部とやりとりをする必要があります。

外部の状態とやりとりをするコントローラーは、目的の状態をAPIサーバーから取得した後、外部のシステムと直接通信し、現在の状態を目的の状態に近づけます。

(クラスター内のノードを水平にスケールさせるコントローラーが実際に存在します。)

ここで重要な点は、コントローラーが目的の状態を実現するために変更を加えてから、現在の状態をクラスターのAPIサーバーに報告することです。他の制御ループは、その報告されたデータを監視し、独自のアクションを実行できます。

サーモスタットの例では、部屋が非常に寒い場合、別のコントローラーが霜防止ヒーターをオンにすることもあります。Kubernetesクラスターを使用すると、コントロールプレーンは、Kubernetesを拡張して実装することにより、IPアドレス管理ツールやストレージサービス、クラウドプロバイダーAPI、およびその他のサービスと間接的に連携します。

目的の状態 vs 現在の状態

Kubernetesはシステムに対してクラウドネイティブな見方をするため、常に変化し続けるような状態を扱えるように設計されています。

処理を実行したり、制御ループが故障を自動的に修正したりしているどの時点でも、クラスターは変化中である可能性があります。つまり、クラスターは決して安定した状態にならない可能性があるということです。

コントローラーがクラスターのために実行されていて、有用な変更が行われるのであれば、全体的な状態が安定しているかどうかは問題にはなりません。

設計

設計理念として、Kubernetesは多数のコントローラーを使用しており、各コントローラーはクラスターの状態の特定の側面をそれぞれ管理しています。最もよくあるパターンは、特定の制御ループ(コントローラー)が目的の状態として1種類のリソースを使用し、目的の状態を実現することを管理するために別の種類のリソースを用意するというものです。たとえば、Jobのコントローラーは、Jobオブジェクト(新しい処理を見つけるため)およびPodオブジェクト(Jobを実行し、処理が完了したか確認するため)を監視します。この場合、なにか別のものがJobを作成し、JobコントローラーはPodを作成します。

相互にリンクされた単一のモノリシックな制御ループよりは、複数の単純なコントローラーが存在する方が役に立ちます。コントローラーは故障することがあるため、Kubernetesは故障を許容するように設計されています。

コントローラーを実行する方法

Kubernetesには、kube-controller-manager内部で動作する一組のビルトインのコントローラーが用意されています。これらビルトインのコントローラーは、コアとなる重要な振る舞いを提供します。

DeploymentコントローラーとJobコントローラーは、Kubernetes自体の一部として同梱されているコントローラーの例です(それゆえ「ビルトイン」のコントローラーと呼ばれます)。Kubernetesは回復性のあるコントロールプレーンを実行できるようにしているため、ビルトインのコントローラーの一部が故障しても、コントロールプレーンの別の部分が作業を引き継いでくれます。

Kubernetesを拡張するためにコントロールプレーンの外で動作するコントローラーもあります。もし望むなら、新しいコントローラーを自分で書くこともできます。自作のコントローラーをPodセットとして動作させたり、Kubernetesの外部で動作させることもできます。どのような動作方法が最も適しているかは、そのコントローラーがどのようなことを行うのかに依存します。

次の項目

3.2.5 - クラウドコントローラーマネージャー

FEATURE STATE: Kubernetes v1.11 [beta]

クラウドインフラストラクチャー技術により、パブリック、プライベート、ハイブリッドクラウド上でKubernetesを動かすことができます。Kubernetesは、コンポーネント間の密なつながりが不要な自動化されたAPI駆動インフラストラクチャーを信条としています。

cloud-controller-managerは クラウド特有の制御ロジックを組み込むKubernetesのcontrol planeコンポーネントです。クラウドコントロールマネージャーは、クラスターをクラウドプロバイダーAPIをリンクし、クラスターのみで相互作用するコンポーネントからクラウドプラットフォームで相互作用するコンポーネントを分離します。

Kubernetesと下のクラウドインフラストラクチャー間の相互運用ロジックを分離することで、cloud-controller-managerコンポーネントはクラウドプロバイダを主なKubernetesプロジェクトと比較し異なるペースで機能をリリース可能にします。

cloud-controller-managerは、プラグイン機構を用い、異なるクラウドプロバイダーに対してそれぞれのプラットフォームとKubernetesの結合を可能にする構成になっています。

設計

Kubernetesのコンポーネント

クラウドコントローラーマネージャーは、複製されたプロセスの集合としてコントロールプレーンで実行されます。(通常、Pod内のコンテナとなります)各cloud-controller-managerは、シングルプロセスで複数のcontrollersを実装します。

クラウドコントローラーマネージャーの機能

クラウドコントローラーマネージャーのコントローラーは以下を含んでいます。

ノードコントローラー

ノードコントローラーは、クラウドインフラストラクチャーで新しいサーバーが作成された際に、Nodeオブジェクトを作成する責務を持ちます。ノードコントローラーは、クラウドプロバイダーのテナント内で動作しているホストの情報を取得します。ノードコントローラーは下記に示す機能を実行します:

  1. Nodeオブジェクトを、コントローラーがクラウドプロバイダーAPIを通じて見つけた各サーバーで初期化する
  2. Nodeオブジェクトに、ノードがデプロイされているリージョンや利用可能なリソース(CPU、メモリなど)のようなクラウド特有な情報を注釈付けやラベル付けをする
  3. ノードのホスト名とネットワークアドレスを取得する
  4. ノードの正常性を検証する。ノードが応答しなくなった場合、クラウドプロバイダーのAPIを利用しサーバーがdeactivated / deleted / terminatedであるかを確認する。クラウドからノードが削除されていた場合、KubernetesクラスターからNodeオブジェクトを削除する

いくつかのクラウドプロバイダーは、これをノードコントローラーと個別のノードライフサイクルコントローラーに分けて実装しています。

ルートコントローラー

ルートコントローラーは、クラスター内の異なるノード上で稼働しているコンテナが相互に通信できるように、クラウド内のルートを適切に設定する責務を持ちます。

クラウドプロバイダーによっては、ルートコントローラーはPodネットワークのIPアドレスのブロックを割り当てることもあります。

サービスコントローラー

Servicesは、マネージドロードバランサー、IPアドレスネットワークパケットフィルタや対象のヘルスチェックのようなクラウドインフラストラクチャーコンポーネントのインテグレーションを行います。サービスコントローラーは、ロードバランサーや他のインフラストラクチャーコンポーネントを必要とするServiceリソースを宣言する際にそれらのコンポーネントを設定するため、クラウドプロバイダーのAPIと対話します。

認可

このセクションでは、クラウドコントローラーマネージャーが操作を行うために様々なAPIオブジェクトに必要な権限を分類します。

ノードコントローラー

ノードコントローラーはNodeオブジェクトのみに対して働きます。Nodeオブジェクトに対して、readとmodifyの全権限が必要です。

v1/Node:

  • Get
  • List
  • Create
  • Update
  • Patch
  • Watch
  • Delete

ルートコントローラー

ルートコントローラーは、Nodeオブジェクトの作成を待ち受け、ルートを適切に設定します。Nodeオブジェクトについて、get権限が必要です。

v1/Node:

  • Get

サービスコントローラー

サービスコントローラーは、Serviceオブジェクトの作成、更新、削除イベントを待ち受け、その後、サービスのEndpointを適切に設定します。

サービスにアクセスするため、list、watchの権限が必要です。サービスを更新するため、patch、updateの権限が必要です。

サービスのEndpointリソースを設定するため、create、list、get、watchそしてupdateの権限が必要です。

v1/Service:

  • List
  • Get
  • Watch
  • Patch
  • Update

その他

クラウドコントローラーマネージャーのコア機能の実装は、Eventオブジェクトのcreate権限と、セキュアな処理を保証するため、ServiceAccountのcreate権限が必要です。

v1/Event:

  • Create
  • Patch
  • Update

v1/ServiceAccount:

  • Create

クラウドコントローラーマネージャーのRBAC ClusterRoleはこのようになります:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cloud-controller-manager
rules:
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - '*'
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - list
  - patch
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - serviceaccounts
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - endpoints
  verbs:
  - create
  - get
  - list
  - watch
  - update

次の項目

Cloud Controller Manager Administration はクラウドコントラーマネージャーの実行と管理を説明しています。

どのようにあなた自身のクラウドコントローラーマネージャーが実装されるのか、もしくは既存プロジェクトの拡張について知りたいですか?

クラウドコントローラーマネージャーは、いかなるクラウドからもプラグインとしての実装を許可するためにGoインターフェースを使います。具体的には、kubernetes/cloud-providercloud.goで定義されているCloudProviderを使います。

本ドキュメントでハイライトした共有コントローラー(Node、Route、Service)の実装と共有クラウドプロバイダーインターフェースに沿ったいくつかの足場は、Kubernetesコアの一部です。クラウドプロバイダに特化した実装は、Kubernetesのコアの外部として、またCloudProviderインターフェースを実装します。

プラグイン開発ついての詳細な情報は、Developing Cloud Controller Managerを見てください。

3.2.6 - cgroup v2について

Linuxでは、コントロールグループがプロセスに割り当てられるリソースを制限しています。

コンテナ化されたワークロードの、CPU/メモリーの要求と制限を含むPodとコンテナのリソース管理を強制するために、 kubeletと基盤となるコンテナランタイムはcgroupをインターフェースとして接続する必要があります。

Linuxではcgroup v1とcgroup v2の2つのバージョンのcgroupがあります。 cgroup v2は新世代のcgroup APIです。

cgroup v2とは何か?

FEATURE STATE: Kubernetes v1.25 [stable]

cgroup v2はLinuxのcgroup APIの次のバージョンです。 cgroup v2はリソース管理機能を強化した統合制御システムを提供しています。

以下のように、cgroup v2はcgroup v1からいくつかの点を改善しています。

  • 統合された単一階層設計のAPI
  • より安全なコンテナへのサブツリーの移譲
  • Pressure Stall Informationなどの新機能
  • 強化されたリソース割り当て管理と複数リソース間の隔離
    • 異なるタイプのメモリー割り当ての統一(ネットワークメモリー、カーネルメモリーなど)
    • ページキャッシュの書き戻しといった、非即時のリソース変更

Kubernetesのいくつかの機能では、強化されたリソース管理と隔離のためにcgroup v2のみを使用しています。 例えば、MemoryQoS機能はメモリーQoSを改善し、cgroup v2の基本的な機能に依存しています。

cgroup v2を使う

cgroup v2を使うおすすめの方法は、デフォルトでcgroup v2が有効で使うことができるLinuxディストリビューションを使うことです。

あなたのディストリビューションがcgroup v2を使っているかどうかを確認するためには、Linux Nodeのcgroupバージョンを特定するを参照してください。

必要要件

cgroup v2を使うには以下のような必要要件があります。

  • OSディストリビューションでcgroup v2が有効であること
  • Linuxカーネルバージョンが5.8以上であること
  • コンテナランタイムがcgroup v2をサポートしていること。例えば、
  • kubeletとコンテナランタイムがsystemd cgroupドライバーを使うように設定されていること

Linuxディストリビューションのcgroup v2サポート

cgroup v2を使っているLinuxディストリビューションの一覧はcgroup v2ドキュメントをご覧ください。

  • Container-Optimized OS (M97以降)
  • Ubuntu (21.10以降, 22.04以降推奨)
  • Debian GNU/Linux (Debian 11 bullseye以降)
  • Fedora (31以降)
  • Arch Linux (April 2021以降)
  • RHEL and RHEL-like distributions (9以降)

あなたのディストリビューションがcgroup v2を使っているかどうかを確認するためには、あなたのディストリビューションのドキュメントを参照するか、Linux Nodeのcgroupバージョンを特定するの説明に従ってください。

カーネルのcmdlineの起動時引数を修正することで、手動であなたのLinuxディストリビューションのcgroup v2を有効にすることもできます。 あなたのディストリビューションがGRUBを使っている場合は、 /etc/default/grubの中のGRUB_CMDLINE_LINUXsystemd.unified_cgroup_hierarchy=1を追加し、sudo update-grubを実行してください。 ただし、おすすめの方法はデフォルトですでにcgroup v2が有効になっているディストリビューションを使うことです。

cgroup v2への移行

cgroup v2に移行するには、必要要件を満たすことを確認し、 cgroup v2がデフォルトで有効であるカーネルバージョンにアップグレードします。

kubeletはOSがcgroup v2で動作していることを自動的に検出し、それに応じて処理を行うため、追加設定は必要ありません。

ノード上やコンテナ内からユーザーが直接cgroupファイルシステムにアクセスしない限り、cgroup v2に切り替えたときのユーザー体験に目立った違いはないはずです。

cgroup v2はcgroup v1とは違うAPIを利用しているため、cgroupファイルシステムに直接アクセスしているアプリケーションはcgroup v2をサポートしている新しいバージョンに更新する必要があります。例えば、

  • サードパーティーの監視またはセキュリティエージェントはcgroupファイルシステムに依存していることがあります。 エージェントをcgroup v2をサポートしているバージョンに更新してください。
  • Podやコンテナを監視するためにcAdvisorをスタンドアローンのDaemonSetとして起動している場合、v0.43.0以上に更新してください。
  • Javaアプリケーションをデプロイする場合は、完全にcgroup v2をサポートしているバージョンを利用してください:
  • uber-go/automaxprocsパッケージを利用している場合は、利用するバージョンがv1.5.1以上であることを確認してください。

Linux Nodeのcgroupバージョンを特定する

cgroupバージョンは利用されているLinuxディストリビューションと、OSで設定されているデフォルトのcgroupバージョンに依存します。 あなたのディストリビューションがどちらのcgroupバージョンを利用しているのかを確認するには、stat -fc %T /sys/fs/cgroup/コマンドをノード上で実行してください。

stat -fc %T /sys/fs/cgroup/

cgroup v2では、cgroup2fsと出力されます。

cgroup v1では、tmpfsと出力されます。

次の項目

3.2.7 - コンテナランタイムインターフェース(CRI)

CRIは、クラスターコンポーネントを再コンパイルすることなく、kubeletがさまざまなコンテナランタイムを使用できるようにするプラグインインターフェースです。

kubeletPodとそのコンテナを起動できるように、クラスター内の各ノードで動作するcontainer runtimeが必要です。

kubeletとContainerRuntime間の通信のメインプロトコルです。

Kubernetes Container Runtime Interface(CRI)は、クラスターコンポーネントkubeletcontainer runtime間の通信用のメインgRPCプロトコルを定義します。

API

FEATURE STATE: Kubernetes v1.23 [stable]

kubeletは、gRPCを介してコンテナランタイムに接続するときにクライアントとして機能します。ランタイムおよびイメージサービスエンドポイントは、コンテナランタイムで使用可能である必要があります。コンテナランタイムは、--image-service-endpointコマンドラインフラグを使用して、kubelet内で個別に設定できます。

Kubernetes v1.30の場合、kubeletはCRI v1の使用を優先します。 コンテナランタイムがCRIのv1をサポートしていない場合、kubeletはサポートされている古いバージョンのネゴシエーションを試みます。 kubelet v1.30はCRI v1alpha2をネゴシエートすることもできますが、このバージョンは非推奨と見なされます。 kubeletがサポートされているCRIバージョンをネゴシエートできない場合、kubeletはあきらめて、ノードとして登録されません。

アップグレード

Kubernetesをアップグレードする場合、kubeletはコンポーネントの再起動時に最新のCRIバージョンを自動的に選択しようとします。 それが失敗した場合、フォールバックは上記のように行われます。 コンテナランタイムがアップグレードされたためにgRPCリダイヤルが必要な場合は、コンテナランタイムも最初に選択されたバージョンをサポートする必要があります。 そうでない場合、リダイヤルは失敗することが予想されます。これには、kubeletの再起動が必要です。

次の項目

3.2.8 - ガベージコレクション

ガベージコレクションは、Kubernetesがクラスターリソースをクリーンアップするために使用するさまざまなメカニズムの総称です。これにより、次のようなリソースのクリーンアップが可能になります:

オーナーの依存関係

Kubernetesの多くのオブジェクトは、owner referenceを介して相互にリンクしています。 owner referenceは、どのオブジェクトが他のオブジェクトに依存しているかをコントロールプレーンに通知します。 Kubernetesは、owner referenceを使用して、コントロールプレーンやその他のAPIクライアントに、オブジェクトを削除する前に関連するリソースをクリーンアップする機会を提供します。 ほとんどの場合、Kubernetesはowner referenceを自動的に管理します。

Ownershipは、一部のリソースでも使用されるラベルおよびセレクターメカニズムとは異なります。 たとえば、EndpointSliceオブジェクトを作成するServiceを考えます。 Serviceはラベルを使用して、コントロールプレーンがServiceに使用されているEndpointSliceオブジェクトを判別できるようにします。 ラベルに加えて、Serviceに代わって管理される各EndpointSliceには、owner referenceがあります。 owner referenceは、Kubernetesのさまざまな部分が制御していないオブジェクトへの干渉を回避するのに役立ちます。

カスケード削除

Kubernetesは、ReplicaSetを削除したときに残されたPodなど、owner referenceがなくなったオブジェクトをチェックして削除します。 オブジェクトを削除する場合、カスケード削除と呼ばれるプロセスで、Kubernetesがオブジェクトの依存関係を自動的に削除するかどうかを制御できます。 カスケード削除には、次の2つのタイプがあります。

  • フォアグラウンドカスケード削除
  • バックグラウンドカスケード削除

また、Kubernetes finalizerを使用して、ガベージコレクションがowner referenceを持つリソースを削除する方法とタイミングを制御することもできます。

フォアグラウンドカスケード削除

フォアグラウンドカスケード削除では、削除するオーナーオブジェクトは最初に削除進行中の状態になります。 この状態では、オーナーオブジェクトに次のことが起こります。

  • Kubernetes APIサーバーは、オブジェクトのmetadata.deletionTimestampフィールドを、オブジェクトに削除のマークが付けられた時刻に設定します。
  • Kubernetes APIサーバーは、metadata.finalizersフィールドをforegroundDeletionに設定します。
  • オブジェクトは、削除プロセスが完了するまで、KubernetesAPIを介して表示されたままになります。

オーナーオブジェクトが削除進行中の状態に入ると、コントローラーは依存関係を削除します。 すべての依存関係オブジェクトを削除した後、コントローラーはオーナーオブジェクトを削除します。 この時点で、オブジェクトはKubernetesAPIに表示されなくなります。

フォアグラウンドカスケード削除中に、オーナーの削除をブロックする依存関係は、ownerReference.blockOwnerDeletion=trueフィールドを持つ依存関係のみです。 詳細については、フォアグラウンドカスケード削除の使用を参照してください。

バックグラウンドカスケード削除

バックグラウンドカスケード削除では、Kubernetes APIサーバーがオーナーオブジェクトをすぐに削除し、コントローラーがバックグラウンドで依存オブジェクトをクリーンアップします。 デフォルトでは、フォアグラウンド削除を手動で使用するか、依存オブジェクトを孤立させることを選択しない限り、Kubernetesはバックグラウンドカスケード削除を使用します。

詳細については、バックグラウンドカスケード削除の使用を参照してください。

孤立した依存関係

Kubernetesがオーナーオブジェクトを削除すると、残された依存関係はorphanオブジェクトと呼ばれます。 デフォルトでは、Kubernetesは依存関係オブジェクトを削除します。この動作をオーバーライドする方法については、オーナーオブジェクトの削除と従属オブジェクトの孤立を参照してください。

未使用のコンテナとイメージのガベージコレクション

kubeletは未使用のイメージに対して5分ごとに、未使用のコンテナに対して1分ごとにガベージコレクションを実行します。 外部のガベージコレクションツールは、kubeletの動作を壊し、存在するはずのコンテナを削除する可能性があるため、使用しないでください。

未使用のコンテナとイメージのガベージコレクションのオプションを設定するには、設定ファイルを使用してkubeletを調整し、KubeletConfigurationリソースタイプを使用してガベージコレクションに関連するパラメーターを変更します。

コンテナイメージのライフサイクル

Kubernetesは、kubeletの一部であるイメージマネージャーを通じて、cadvisorの協力を得て、すべてのイメージのライフサイクルを管理します。kubeletは、ガベージコレクションを決定する際に、次のディスク使用制限を考慮します。

  • HighThresholdPercent
  • LowThresholdPercent

設定されたHighThresholdPercent値を超えるディスク使用量はガベージコレクションをトリガーします。 ガベージコレクションは、最後に使用された時間に基づいて、最も古いものから順にイメージを削除します。 kubeletは、ディスク使用量がLowThresholdPercent値に達するまでイメージを削除します。

コンテナのガベージコレクション

kubeletは、次の変数に基づいて未使用のコンテナをガベージコレクションします。

  • MinAge: kubeletがガベージコレクションできるコンテナの最低期間。0を設定すると無効化されます。
  • MaxPerPodContainer: 各Podが持つことができるデッドコンテナの最大数。0未満に設定すると無効化されます。
  • MaxContainers: クラスターが持つことができるデッドコンテナの最大数。0未満に設定すると無効化されます。

これらの変数に加えて、kubeletは、通常、最も古いものから順に、定義されていない削除されたコンテナをガベージコレクションします。

MaxPerPodContainerMaxContainersは、Podごとのコンテナの最大数(MaxPerPodContainer)を保持すると、グローバルなデッドコンテナの許容合計(MaxContainers)を超える状況で、互いに競合する可能性があります。 この状況では、kubeletはMaxPerPodContainerを調整して競合に対処します。最悪のシナリオは、MaxPerPodContainerを1にダウングレードし、最も古いコンテナを削除することです。 さらに、削除されたPodが所有するコンテナは、MinAgeより古くなると削除されます。

ガベージコレクションの設定

これらのリソースを管理するコントローラーに固有のオプションを設定することにより、リソースのガベージコレクションを調整できます。次のページは、ガベージコレクションを設定する方法を示しています。

次の項目

3.3 - コンテナ

アプリケーションとランタイムの依存関係を一緒にパッケージ化するための技術

実行するそれぞれのコンテナは繰り返し使用可能です。依存関係を含めて標準化されており、どこで実行しても同じ動作が得られることを意味しています。

コンテナは基盤となるホストインフラからアプリケーションを切り離します。これにより、さまざまなクラウドやOS環境下でのデプロイが容易になります。

コンテナイメージ

コンテナイメージはすぐに実行可能なソフトウェアパッケージで、アプリケーションの実行に必要なものをすべて含んています。コードと必要なランタイム、アプリケーションとシステムのライブラリ、そして必須な設定項目のデフォルト値を含みます。

設計上、コンテナは不変で、既に実行中のコンテナのコードを変更することはできません。コンテナ化されたアプリケーションがあり変更したい場合は、変更を含んだ新しいイメージをビルドし、コンテナを再作成して、更新されたイメージから起動する必要があります。

コンテナランタイム

コンテナランタイムは、コンテナの実行を担当するソフトウェアです。

Kubernetesは次の複数のコンテナランタイムをサポートします。 DockercontainerdCRI-O、 および全ての Kubernetes CRI (Container Runtime Interface) 実装です。

次の項目

3.3.1 - イメージ

コンテナイメージはアプリケーションと依存関係のあるすべてソフトウェアをカプセル化したバイナリデータを表します。コンテナイメージはスタンドアロンで実行可能なソフトウェアをひとつにまとめ、ランタイム環境に関する想定を明確に定義しています。

アプリケーションのコンテナイメージを作成し、一般的にはPodで参照する前にレジストリへPushします。

このページではコンテナイメージの概要を説明します。

イメージの名称

コンテナイメージは、pauseexample/mycontainer、またはkube-apiserverのような名前が通常つけられます。 イメージにはレジストリのホスト名も含めることができ(例:fictional.registry.example/imagename)、さらにポート番号も含めることが可能です(例:fictional.registry.example:10443/imagename)。

レジストリのホスト名を指定しない場合は、KubernetesはDockerパブリックレジストリを意味していると見なします。

イメージ名の後に、タグ を追加することができます(dockerpodmanのようなコマンドを利用した場合と同様)。 タグによって同じイメージの異なるバージョンを識別できます。

イメージタグは大文字と小文字、数値、アンダースコア(_)、ピリオド(.)とマイナス(-)で構成されます。 イメージタグでは区切り記号(_-.)を指定できる追加ルールがあります。 タグを指定しない場合は、Kubernetesはlatestタグを指定したと見なします。

イメージの更新

DeploymentStatefulSet、Pod、またはPodテンプレートを含むその他のオブジェクトを最初に作成するとき、デフォルトでは、Pod内のすべてのコンテナのPullポリシーは、明示的に指定されていない場合、IfNotPresentに設定されます。

イメージがすでに存在する場合、このポリシーはkubeletにイメージのPullをスキップさせます。

イメージPullポリシー

コンテナのimagePullPolicyとイメージのタグは、kubeletが指定されたイメージをPull(ダウンロード)しようとする時に影響します。

以下は、imagePullPolicyに設定できる値とその効果の一覧です。

IfNotPresent
イメージがローカルにまだ存在しない場合のみ、イメージがPullされます。
Always
kubeletがコンテナを起動するときは常にコンテナイメージレジストリに照会して、イメージ名をイメージダイジェストに解決します。 ローカルにキャッシュされた同一ダイジェストのコンテナイメージがあった場合、kubeletはキャッシュされたイメージを使用します。 そうでない場合、kubeletは解決されたダイジェストのイメージをPullし、そのイメージを使ってコンテナを起動します。
Never
kubeletは、イメージを取得しようとしません。ローカルにイメージがすでに存在する場合、kubeletはコンテナを起動しようとします。それ以外の場合、起動に失敗します。 詳細は、事前にPullしたイメージを参照してください。

レジストリに確実にアクセスできるのであれば、基盤となるイメージプロバイダーのキャッシュセマンティクスによりimagePullPolicy: Alwaysでも効率的です。 コンテナランタイムは、イメージレイヤーが既にノード上に存在することを認識できるので、再度ダウンロードする必要がありません。

Podがいつも同じバージョンのコンテナイメージを使用するために、イメージのダイジェストを指定することができます。<image-name>:<tag><image-name>@<digest>に置き換えてください(例えば、image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2)。

イメージタグを使用する場合、イメージレジストリがそのイメージのタグが表すコードを変更すると、新旧のコードを実行するPodが混在することになるかもしれません。 イメージダイジェストは特定のバージョンのイメージを一意に識別するため、Kubernetesは特定のイメージ名とダイジェストが指定されたコンテナを起動するたびに同じコードを実行します。 イメージをダイジェストで指定することは、レジストリの変更でそのようなバージョンの混在を起こさないように、実行するコードを固定します。

Pod(およびPodテンプレート)を作成する時に、実行中のワークロードがタグではなくイメージダイジェストに基づき定義されるように変化させるサードパーティーのアドミッションコントローラーがあります。 レジストリでどのようなタグの変更があっても、すべてのワークロードが必ず同じコードを実行するようにしたい場合に役立ちます。

デフォルトのイメージPullポリシー

新しいPodがAPIサーバに送信されると、クラスターは特定の条件が満たされたときにimagePullPolicyフィールドを設定します。

  • imagePullPolicyフィールドを省略し、コンテナイメージのタグに:latestを指定した場合、imagePullPolicyには自動的にAlwaysが設定される
  • imagePullPolicyフィールドを省略し、コンテナイメージのタグを指定しなかった場合、imagePullPolicyには自動的にAlwaysが設定される
  • imagePullPolicyフィールドを省略し、コンテナイメージのタグに:latest以外を指定した場合、imagePullPolicyには自動的にIfNotPresentが設定される

必要なイメージをPullする

常に強制的にPullしたい場合は、以下のいずれかを行ってください。

  • コンテナのimagePullPolicyAlwaysを設定する。
  • imagePullPolicyを省略し、使用するイメージに:latestタグ使用する。Pod生成時に、KubernetesがポリシーにAlwaysを設定する。
  • imagePullPolicyと使用するイメージのタグを省略する。Pod生成時に、KubernetesがポリシーにAlwaysを設定する。
  • AlwaysPullImagesアドミッションコントローラーを有効にする。

ImagePullBackOff

kubeletがコンテナランタイムを使ってPodのコンテナの生成を開始するとき、ImagePullBackOffのためにコンテナがWaiting状態になる可能性があります。

ImagePullBackOffステータスは、KubernetesがコンテナイメージをPullできないために、コンテナを開始できないことを意味します(イメージ名が無効である、imagePullSecretなしでプライベートレジストリからPullしたなどの理由のため)。BackOffは、バックオフの遅延を増加させながらKubernetesがイメージをPullしようとし続けることを示します。

Kubernetesは、組み込まれた制限である300秒(5分)に達するまで、試行するごとに遅延を増加させます。

イメージインデックスを使ったマルチアーキテクチャイメージ

コンテナレジストリはバイナリイメージの提供だけでなく、コンテナイメージインデックスも提供する事ができます。イメージインデックスはコンテナのアーキテクチャ固有バージョンに関する複数のイメージマニフェストを指すことができます。イメージインデックスの目的はイメージの名前(例:pauseexample/mycontainerkube-apiserver)をもたせ、様々なシステムが使用しているマシンアーキテクチャにあう適切なバイナリイメージを取得できることです。

Kubernetes自身は、通常コンテナイメージに-$(ARCH)のサフィックスを持つ名前をつけます。下位互換の為にサフィックス付きの古い仕様のイメージを生成してください。その目的は、pauseのようなすべてのアーキテクチャのマニフェストを持つイメージと、サフィックスのあるイメージをハードコードしていた可能性のある古い仕様の設定やYAMLファイルと下位互換があるpause-amd64のようなイメージを生成することです。

プライベートレジストリを使用する方法

プライベートレジストリではイメージを読み込む為にキーが必要になる場合があります。
認証情報はいくつかの方法で提供できます。

  • プライベートレジストリへの認証をNodeに設定する
    • すべてのPodがプライベートレジストリを読み取ることができる
    • クラスター管理者によるNodeの設定が必要
  • 事前にPullされたイメージ
    • すべてのPodがNode上にキャッシュされたイメージを利用できる
    • セットアップするためにはすべてのNodeに対するrootアクセスが必要
  • PodでImagePullSecretsを指定する
    • キーを提供したPodのみがプライベートレジストリへアクセスできる
  • ベンダー固有またはローカルエクステンション
    • カスタムNode構成を使っている場合、あなた(または、あなたのクラウドプロバイダー)はコンテナレジストリへの認証の仕組みを組み込むことができる

これらのオプションについて、以下で詳しく説明します。

プライベートレジストリへの認証をNodeに設定する

認証情報を設定するための具体的な手順は、使用するコンテナランタイムとレジストリに依存します。最も正確な情報として、ソリューションのドキュメントを参照する必要があります。

プライベートなコンテナイメージレジストリを設定する例として、プライベートレジストリからイメージをPullするタスクを参照してください。その例では、Docker Hubのプライベートレジストリを使用しています。

config.jsonの解釈

config.jsonの解釈は、Dockerのオリジナルの実装とKubernetesの解釈で異なります。 Dockerでは、authsキーはルートURLしか指定できませんが、Kubernetesではプレフィックスのマッチしたパスだけでなく、グロブパターンのURLも指定できます。 以下のようなconfig.jsonが有効であるということです。

{
    "auths": {
        "*my-registry.io/images": {
            "auth": "…"
        }
    }
}

ルートURL(*my-registry.io)は、以下の構文でマッチングされます:

pattern:
    { term }

term:
    '*'         セパレーター以外の任意の文字列にマッチする
    '?'         セパレーター以外の任意の一文字にマッチする
    '[' [ '^' ] { character-range } ']'
                文字クラス (空であってはならない)
    c           文字 c とマッチする (c != '*', '?', '\\', '[')
    '\\' c      文字 c とマッチする

character-range:
    c           文字 c とマッチする (c != '\\', '-', ']')
    '\\' c      文字 c とマッチする
    lo '-' hi   lo <= c <= hi の文字 c とマッチする

イメージのPull操作では、有効なパターンごとに認証情報をCRIコンテナランタイムに渡すようになりました。例えば、以下のようなコンテナイメージ名は正常にマッチングされます。

  • my-registry.io/images
  • my-registry.io/images/my-image
  • my-registry.io/images/another-image
  • sub.my-registry.io/images/my-image
  • a.sub.my-registry.io/images/my-image

kubeletは、見つかったすべての認証情報に対してイメージのPullを順次実行します。これは、次のようにconfig.jsonに複数のエントリーを書くことも可能であることを意味します。

{
    "auths": {
        "my-registry.io/images": {
            "auth": "…"
        },
        "my-registry.io/images/subpath": {
            "auth": "…"
        }
    }
}

コンテナがmy-registry.io/images/subpath/my-imageをPullするイメージとして指定した場合、kubeletが認証ソースの片方からダウンロードに失敗すると、両方の認証ソースからダウンロードを試みます。

事前にPullしたイメージ

デフォルトでは、kubeletは指定されたレジストリからそれぞれのイメージをPullしようとします。 また一方では、コンテナのimagePullPolicyプロパティにIfNotPresentNeverが設定されている場合、ローカルのイメージが使用されます。(それぞれに対して、優先的またはか排他的に)

レジストリ認証の代替として事前にPullしたイメージを利用したい場合、クラスターのすべてのNodeが同じ事前にPullしたイメージを持っていることを確認する必要があります。

特定のイメージをあらかじめロードしておくことは高速化やプライベートレジストリへの認証の代替として利用することができます。

すべてのPodは事前にPullしたイメージへの読み取りアクセス権をもちます。

PodでimagePullSecretsを指定する

KubernetesはPodでのコンテナイメージレジストリキーの指定をサポートしています。

Dockerの設定を利用してSecretを作成する。

レジストリへの認証のためにユーザー名、レジストリのパスワード、クライアントのメールアドレス、およびそのホスト名を知っている必要があります。

適切な大文字の値を置き換えて、次のコマンドを実行します。

kubectl create secret docker-registry <name> --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL

既にDocker認証情報ファイルを持っている場合は、上記のコマンドの代わりに、認証情報ファイルをKubernetes Secretsとしてインポートすることができます。 既存のDocker認証情報に基づいてSecretを作成する で、この設定方法を説明します.

これは複数のプライベートコンテナレジストリを使用している場合に特に有効です。kubectl create secret docker-registryはひとつのプライベートレジストリにのみ機能するSecretを作成するからです。

PodのimagePullSecretsを参照する方法

これで、imagePullSecretsセクションをPod定義へ追加することでSecretを参照するPodを作成できます。

例:

cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey
EOF

cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF

これは、プライベートレジストリを使用する各Podで行う必要があります。

ただし、この項目の設定はServiceAccountリソースの中でimagePullSecretsを指定することで自動化することができます。

詳細の手順は、ImagePullSecretsをService Accountに追加するをクリックしてください。

これを各Nodeの.docker/config.jsonに組み合わせて利用できます。認証情報はマージされます。

ユースケース

プライベートレジストリを設定するためのソリューションはいくつかあります。ここでは、いくつかの一般的なユースケースと推奨される解決方法を示します。

  1. クラスターに独自仕様でない(例えば、オープンソース)イメージだけを実行する。イメージを非公開にする必要がない
    • パブリックレジストリのパブリックイメージを利用する
      • 設定は必要ない
      • クラウドプロバイダーによっては、可用性の向上とイメージをPullする時間を短くする為に、自動的にキャッシュやミラーされたパプリックイメージが提供される
  2. 社外には非公開の必要があるが、すべてのクラスター利用者には見せてよい独自仕様のイメージをクラスターで実行している
    • ホストされたプライペートレジストリを使用
      • プライベートレジストリにアクセスする必要があるノードには、手動設定が必要となる場合がある
    • または、オープンな読み取りアクセスを許可したファイヤーウォールの背後で内部向けプライベートレジストリを実行する
      • Kubernetesの設定は必要ない
    • イメージへのアクセスを制御できるホストされたコンテナイメージレジストリサービスを利用する
      • Nodeを手動設定するよりもクラスターのオートスケーリングのほうがうまく機能する
    • また、Node設定変更を自由にできないクラスターではimagePullSecretsを使用する
  3. 独自仕様のイメージを含むクラスターで、いくつかは厳格なアクセス制御が必要である
    • AlwaysPullImagesアドミッションコントローラーが有効化かを確認する必要がある。さもないと、全部のPodがすべてのイメージへのアクセスができてしまう可能性がある
    • 機密データはイメージに含めてしまうのではなく、"Secret"リソースに移行する
  4. それぞれのテナントが独自のプライベートレジストリを必要とするマルチテナントのクラスターである
    • AlwaysPullImagesアドミッションコントローラーが有効化を確認する必要がある。さもないと、すべてのテナントの全Podが全部のイメージにアクセスできてしまう可能性がある
    • 認証が必要なプライベートレジストリを実行する
    • それぞれのテナントでレジストリ認証を生成し、Secretへ設定し、各テナントのNamespaceに追加する
    • テナントは、Secretを各NamespaceのimagePullSecretsへ追加する

複数のレジストリへのアクセスが必要な場合、それぞれのレジストリ毎にひとつのSecretを作成する事ができます。

次の項目

3.3.2 - コンテナ環境

このページでは、コンテナ環境で利用可能なリソースについて説明します。

コンテナ環境

Kubernetesはコンテナにいくつかの重要なリソースを提供します。

  • イメージと1つ以上のボリュームの組み合わせのファイルシステム
  • コンテナ自体に関する情報
  • クラスター内の他のオブジェクトに関する情報

コンテナ情報

コンテナの ホスト名 は、コンテナが実行されているPodの名前です。 ホスト名はhostnameコマンドまたはlibcのgethostname関数呼び出しにより利用可能です。

Podの名前と名前空間はdownward APIを通じて環境変数として利用可能です。

Pod定義からのユーザー定義の環境変数もコンテナで利用できます。 コンテナイメージで静的に指定されている環境変数も同様です。

クラスター情報

コンテナの作成時に実行されていたすべてのサービスのリストは、環境変数として使用できます。 このリストは、新しいコンテナのPodおよびKubernetesコントロールプレーンサービスと同じ名前空間のサービスに制限されます。

bar という名前のコンテナに対応する foo という名前のサービスの場合、以下の変数が定義されています。

FOO_SERVICE_HOST=<サービスが実行されているホスト>
FOO_SERVICE_PORT=<サービスが実行されているポート>

サービスは専用のIPアドレスを持ち、DNSアドオンが有効の場合、DNSを介してコンテナで利用可能です。

次の項目

3.3.3 - ランタイムクラス(Runtime Class)

FEATURE STATE: Kubernetes v1.20 [stable]

このページではRuntimeClassリソースと、runtimeセクションのメカニズムについて説明します。

RuntimeClassはコンテナランタイムの設定を選択するための機能です。そのコンテナランタイム設定はPodのコンテナを稼働させるために使われます。

RuntimeClassを使う動機

異なるPodに異なるRuntimeClassを設定することで、パフォーマンスとセキュリティのバランスをとることができます。例えば、ワークロードの一部に高レベルの情報セキュリティ保証が必要な場合、ハードウェア仮想化を使用するコンテナランタイムで実行されるようにそれらのPodをスケジュールすることを選択できます。その後、追加のオーバーヘッドを犠牲にして、代替ランタイムをさらに分離することでメリットが得られます。

RuntimeClassを使用して、コンテナランタイムは同じで設定が異なるPodを実行することもできます。

セットアップ

  1. ノード上でCRI実装を設定する。(ランタイムに依存)
  2. 対応するRuntimeClassリソースを作成する。

1. ノード上でCRI実装を設定する

RuntimeClassを通じて利用可能な設定はContainer Runtime Interface (CRI)の実装依存となります。 ユーザーの環境のCRI実装の設定方法は、対応するドキュメント(下記)を参照ください。

RuntimeClassの設定は、RuntimeClassによって参照されるハンドラー名を持ちます。そのハンドラーは有効なDNSラベル名でなくてはなりません。

2. 対応するRuntimeClassリソースを作成する

ステップ1にて設定する各項目は、関連するハンドラー 名を持ちます。それはどの設定かを指定するものです。各ハンドラーにおいて、対応するRuntimeClassオブジェクトが作成されます。

そのRuntimeClassリソースは現時点で2つの重要なフィールドを持ちます。それはRuntimeClassの名前(metadata.name)とハンドラー(handler)です。そのオブジェクトの定義は下記のようになります。

# RuntimeClassはnode.k8s.ioというAPIグループで定義されます。
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  # RuntimeClass名
  # RuntimeClassはネームスペースなしのリソースです。
  name: myclass
# 対応するCRI設定
handler: myconfiguration

RuntimeClassオブジェクトの名前はDNSサブドメイン名に従う必要があります。

使用例

RuntimeClassがクラスターに対して設定されると、PodSpecでruntimeClassNameを指定して使用できます。 例えば

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

これは、kubeletに対してPodを稼働させるためのRuntimeClassを使うように指示します。もし設定されたRuntimeClassが存在しない場合や、CRIが対応するハンドラーを実行できない場合、そのPodはFailedというフェーズになります。 エラーメッセージに関しては対応するイベントを参照して下さい。

もしruntimeClassNameが指定されていない場合、デフォルトのRuntimeHandlerが使用され、これはRuntimeClassの機能が無効であるときのふるまいと同じものとなります。

CRIの設定

CRIランタイムのセットアップに関するさらなる詳細は、コンテナランタイムを参照してください。

containerd

ランタイムハンドラーは、/etc/containerd/config.tomlにあるcontainerdの設定ファイルにより設定されます。 正しいハンドラーは、そのruntimeセクションで設定されます。

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]

詳細はcontainerdの設定に関するドキュメントを参照してください。

CRI-O

ランタイムハンドラーは、/etc/crio/crio.confにあるCRI-Oの設定ファイルにより設定されます。 正しいハンドラーはcrio.runtime tableで設定されます。

[crio.runtime.runtimes.${HANDLER_NAME}]
  runtime_path = "${PATH_TO_BINARY}"

詳細はCRI-Oの設定に関するドキュメントを参照してください。

スケジューリング

FEATURE STATE: Kubernetes v1.16 [beta]

RuntimeClassのschedulingフィールドを指定することで、設定されたRuntimeClassをサポートするノードにPodがスケジューリングされるように制限することができます。 schedulingが設定されていない場合、このRuntimeClassはすべてのノードでサポートされていると仮定されます。

特定のRuntimeClassをサポートしているノードへPodが配置されることを保証するために、各ノードはruntimeclass.scheduling.nodeSelectorフィールドによって選択される共通のラベルを持つべきです。 RuntimeClassのnodeSelectorはアドミッション機能によりPodのnodeSelectorに統合され、効率よくノードを選択します。 もし設定が衝突した場合は、Pod作成は拒否されるでしょう。

もしサポートされているノードが他のRuntimeClassのPodが稼働しないようにtaint付与されていた場合、RuntimeClassに対してtolerationsを付与することができます。 nodeSelectorと同様に、tolerationsはPodのtolerationsにアドミッション機能によって統合され、効率よく許容されたノードを選択します。

ノードの選択とtolerationsについての詳細はノード上へのPodのスケジューリングを参照してください。

Podオーバーヘッド

FEATURE STATE: Kubernetes v1.24 [stable]

Podが稼働する時に関連する オーバーヘッド リソースを指定できます。オーバーヘッドを宣言すると、クラスター(スケジューラーを含む)がPodとリソースに関する決定を行うときにオーバーヘッドを考慮することができます。

PodのオーバーヘッドはRuntimeClass内のoverheadフィールドによって定義されます。 このフィールドを使用することで、RuntimeClassを使用して稼働するPodのオーバーヘッドを指定することができ、Kubernetes内部で使用されるオーバーヘッドを確保することができます。

次の項目

3.3.4 - コンテナライフサイクルフック

このページでは、kubeletにより管理されるコンテナがコンテナライフサイクルフックフレームワークを使用して、管理ライフサイクル中にイベントによって引き起こされたコードを実行する方法について説明します。

概要

Angularなどのコンポーネントライフサイクルフックを持つ多くのプログラミング言語フレームワークと同様に、Kubernetesはコンテナにライフサイクルフックを提供します。 フックにより、コンテナは管理ライフサイクル内のイベントを認識し、対応するライフサイクルフックが実行されたときにハンドラーに実装されたコードを実行できます。

コンテナフック

コンテナに公開されている2つのフックがあります。

PostStart

このフックはコンテナが作成された直後に実行されます。 しかし、フックがコンテナのENTRYPOINTの前に実行されるという保証はありません。 ハンドラーにパラメーターは渡されません。

PreStop

このフックは、APIからの要求、またはliveness/startup probeの失敗、プリエンプション、リソース競合などの管理イベントが原因でコンテナが終了する直前に呼び出されます。コンテナがすでに終了状態または完了状態にある場合にはPreStopフックの呼び出しは失敗し、コンテナを停止するTERMシグナルが送信される前にフックは完了する必要があります。PreStopフックが実行される前にPodの終了猶予期間のカウントダウンが開始されるので、ハンドラーの結果に関わらず、コンテナはPodの終了猶予期間内に最終的に終了します。 ハンドラーにパラメーターは渡されません。

終了動作の詳細な説明は、Termination of Podsにあります。

フックハンドラーの実装

コンテナは、フックのハンドラーを実装して登録することでそのフックにアクセスできます。 コンテナに実装できるフックハンドラーは2種類あります。

  • Exec - コンテナのcgroupsと名前空間の中で、 pre-stop.shのような特定のコマンドを実行します。 コマンドによって消費されたリソースはコンテナに対してカウントされます。
  • HTTP - コンテナ上の特定のエンドポイントに対してHTTP要求を実行します。

フックハンドラーの実行

コンテナライフサイクル管理フックが呼び出されると、Kubernetes管理システムはフックアクションにしたがってハンドラーを実行します。 httpGettcpSocketはkubeletプロセスによって実行され、execはコンテナの中で実行されます。

フックハンドラーの呼び出しは、コンテナを含むPodのコンテキスト内で同期しています。 これは、PostStartフックの場合、コンテナのENTRYPOINTとフックは非同期に起動することを意味します。 しかし、フックの実行に時間がかかりすぎたりハングしたりすると、コンテナはrunning状態になることができません。

PreStopフックはコンテナを停止するシグナルから非同期で実行されるのではなく、TERMシグナルが送られる前に実行を完了する必要があります。 もしPreStopフックが実行中にハングした場合、PodはTerminating状態になり、 terminationGracePeriodSecondsの時間切れで強制終了されるまで続きます。 この猶予時間は、PreStopフックが実行され正常にコンテナを停止できるまでの合計時間に適用されます。 例えばterminationGracePeriodSecondsが60で、フックの終了に55秒かかり、シグナルを受信した後にコンテナを正常に停止させるのに10秒かかる場合、コンテナは正常に停止する前に終了されてしまいます。terminationGracePeriodSecondsが、これら2つの実行にかかる合計時間(55+10)よりも短いからです。

PostStartまたはPreStopフックが失敗した場合、コンテナは強制終了します。

ユーザーはフックハンドラーをできるだけ軽量にするべきです。 ただし、コンテナを停止する前に状態を保存するなどの場合は、長時間のコマンド実行が必要なケースもあります。

フック配信保証

フックの配信は 少なくとも1回 を意図しています。これはフックがPostStartPreStopのような任意のイベントに対して複数回呼ばれることがあることを意味します。 これを正しく処理するのはフックの実装次第です。

通常、1回の配信のみが行われます。 たとえば、HTTPフックレシーバーがダウンしていてトラフィックを受け取れない場合、再送信は試みられません。 ただし、まれに二重配信が発生することがあります。 たとえば、フックの送信中にkubeletが再起動した場合、kubeletが起動した後にフックが再送信される可能性があります。

フックハンドラーのデバッグ

フックハンドラーのログは、Podのイベントには表示されません。 ハンドラーが何らかの理由で失敗した場合は、イベントをブロードキャストします。 PostStartの場合、これはFailedPostStartHookイベントで、PreStopの場合、これはFailedPreStopHookイベントです。 失敗のFailedPreStopHookイベントを自分自身で生成する場合には、lifecycle-events.yamlファイルに対してpostStartのコマンドを"badcommand"に変更し、適用してください。 kubectl describe pod lifecycle-demoを実行した結果のイベントの出力例を以下に示します。

Events:
  Type     Reason               Age              From               Message
  ----     ------               ----             ----               -------
  Normal   Scheduled            7s               default-scheduler  Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
  Normal   Pulled               6s               kubelet            Successfully pulled image "nginx" in 229.604315ms
  Normal   Pulling              4s (x2 over 6s)  kubelet            Pulling image "nginx"
  Normal   Created              4s (x2 over 5s)  kubelet            Created container lifecycle-demo-container
  Normal   Started              4s (x2 over 5s)  kubelet            Started container lifecycle-demo-container
  Warning  FailedPostStartHook  4s (x2 over 5s)  kubelet            Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
  Normal   Killing              4s (x2 over 5s)  kubelet            FailedPostStartHook
  Normal   Pulled               4s               kubelet            Successfully pulled image "nginx" in 215.66395ms
  Warning  BackOff              2s (x2 over 3s)  kubelet            Back-off restarting failed container

次の項目

3.4 - ワークロード

Kubernetesにおけるデプロイ可能な最小のオブジェクトであるPodと、高レベルな抽象化がPodの実行を助けることを理解します。

ワークロードとは、Kubernetes上で実行中のアプリケーションです。 ワークロードが1つのコンポーネントからなる場合でも、複数のコンポーネントが協調して動作する場合でも、KubernetesではそれらはPodの集合として実行されます。Kubernetesでは、Podはクラスター上で実行中のコンテナの集合として表されます。

Podには定義されたライフサイクルがあります。たとえば、一度Podがクラスター上で実行中になると、そのPodが実行中のノード上で深刻な障害が起こったとき、そのノード上のすべてのPodは停止してしまうことになります。Kubernetesではそのようなレベルの障害を最終的なものとして扱うため、たとえノードが後で復元したとしても、ユーザーは新しいPodを作成し直す必要があります。

しかし、生活をかなり楽にするためには、それぞれのPodを直接管理する必要はありません。ワークロードリソース を利用すれば、あなたの代わりにPodの集合の管理を行ってもらえます。これらのリソースはあなたが指定した状態に一致するようにコントローラーを設定し、正しい種類のPodが正しい数だけ実行中になることを保証してくれます。

ワークロードリソースには、次のような種類があります。

  • DeploymentReplicaSet(レガシーなリソースReplicationControllerを置き換えるものです)
  • StatefulSet
  • DaemonSet(ストレージドライバやネットワークプラグインなど、ノードローカルな機能を提供するためのPodを実行するために使われます)
  • JobCronJob(実行後に完了するようなタスクのために使われます)

多少関連のある2種類の補助的な概念もあります。

次の項目

各リソースについて読む以外にも、以下のページでそれぞれのワークロードに関連する特定のタスクについて学ぶことができます。

アプリケーションが実行できるようになったら、インターネット上で公開したくなるかもしれません。その場合には、Serviceとして公開したり、ウェブアプリケーションだけの場合、Ingressを使用することができます。

コードを設定から分離するKubernetesのしくみについて学ぶには、設定を読んでください。

3.4.1 - Pod

Podは、Kubernetes内で作成・管理できるコンピューティングの最小のデプロイ可能なユニットです。

Pod(Podという名前は、たとえばクジラの群れ(pod of whales)やえんどう豆のさや(pea pod)などの表現と同じような意味です)は、1つまたは複数のコンテナのグループであり、ストレージやネットワークの共有リソースを持ち、コンテナの実行方法に関する仕様を持っています。同じPodに含まれるリソースは、常に同じ場所で同時にスケジューリングされ、共有されたコンテキストの中で実行されます。Podはアプリケーションに特化した「論理的なホスト」をモデル化します。つまり、1つのPod内には、1つまたは複数の比較的密に結合されたアプリケーションコンテナが含まれます。クラウド外の文脈で説明すると、アプリケーションが同じ物理ホストや同じバーチャルマシンで実行されることが、クラウドアプリケーションの場合には同じ論理ホスト上で実行されることに相当します。

アプリケーションコンテナと同様に、Podでも、Podのスタートアップ時に実行されるinitコンテナを含めることができます。また、クラスターで利用できる場合には、エフェメラルコンテナを注入してデバッグすることもできます。

Podとは何か?

Podの共有コンテキストは、Dockerコンテナを隔離するのに使われているのと同じ、Linuxのnamespaces、cgroups、場合によっては他の隔離技術の集合を用いて作られます。Podのコンテキスト内では、各アプリケーションが追加の準隔離技術を適用することもあります。

Dockerの概念を使って説明すると、Podは共有の名前空間と共有ファイルシステムのボリュームを持つDockerコンテナのグループに似ています。

Podを使用する

以下は、nginx:1.14.2イメージが実行されるコンテナからなるPodの例を記載しています。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

上記のようなPodを作成するには、以下のコマンドを実行します:

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

Podは通常、直接作成されず、ワークロードリソースで作成されます。ワークロードリソースでPodを作成する方法の詳細については、Podを利用するを参照してください。

Podを管理するためのワークロードリソース

通常、たとえ単一のコンテナしか持たないシングルトンのPodだとしても、自分でPodを直接作成する必要はありません。その代わりに、DeploymentJobなどのワークロードリソースを使用してPodを作成します。もしPodが状態を保持する必要がある場合は、StatefulSetリソースを使用することを検討してください。

Kubernetesクラスター内のPodは、主に次の2種類の方法で使われます。

  • 単一のコンテナを稼働させるPod。「1Pod1コンテナ」構成のモデルは、Kubernetesでは最も一般的なユースケースです。このケースでは、ユーザーはPodを単一のコンテナのラッパーとして考えることができます。Kubernetesはコンテナを直接管理するのではなく、Podを管理します。

  • 協調して稼働させる必要がある複数のコンテナを稼働させるPod。単一のPodは、密に結合してリソースを共有する必要があるような、同じ場所で稼働する複数のコンテナからなるアプリケーションをカプセル化することもできます。これらの同じ場所で稼働するコンテナ群は、単一のまとまりのあるサービスのユニットを構成します。たとえば、1つのコンテナが共有ボリュームからファイルをパブリックに配信し、別のサイドカーコンテナがそれらのファイルを更新するという構成が考えられます。Podはこれらの複数のコンテナ、ストレージリソース、一時的なネットワークIDなどを、単一のユニットとしてまとめます。

各Podは、与えられたアプリケーションの単一のインスタンスを稼働するためのものです。もしユーザーのアプリケーションを水平にスケールさせたい場合(例: 複数インスタンスを稼働させる)、複数のPodを使うべきです。1つのPodは各インスタンスに対応しています。Kubernetesでは、これは一般的にレプリケーションと呼ばれます。レプリケーションされたPodは、通常ワークロードリソースと、それに対応するコントローラーによって、作成・管理されます。

Kubernetesがワークロードリソースとそのコントローラーを活用して、スケーラブルで自動回復するアプリケーションを実装する方法については、詳しくはPodとコントローラーを参照してください。

Podが複数のコンテナを管理する方法

Podは、まとまりの強いサービスのユニットを構成する、複数の協調する(コンテナとして実行される)プロセスをサポートするために設計されました。単一のPod内の複数のコンテナは、クラスター内の同じ物理または仮想マシン上で、自動的に同じ場所に配置・スケジューリングされます。コンテナ間では、リソースや依存関係を共有したり、お互いに通信したり、停止するときにはタイミングや方法を協調して実行できます。

たとえば、あるコンテナが共有ボリューム内のファイルを配信するウェブサーバーとして動作し、別の「サイドカー」コンテナがリモートのリソースからファイルをアップデートするような構成が考えられます。この構成を以下のダイアグラムに示します。

Pod作成ダイアグラム

Podによっては、appコンテナに加えてinitコンテナを持っている場合があります。initコンテナはappコンテナが起動する前に実行・完了するコンテナです。

Podは、Podを構成する複数のコンテナに対して、ネットワークストレージの2種類の共有リソースを提供します。

Podを利用する

通常Kubernetesでは、たとえ単一のコンテナしか持たないシングルトンのPodだとしても、個別のPodを直接作成することはめったにありません。その理由は、Podがある程度一時的で使い捨てできる存在として設計されているためです。Podが作成されると(あなたが直接作成した場合でも、コントローラーが間接的に作成した場合でも)、新しいPodはクラスター内のノード上で実行されるようにスケジューリングされます。Podは、実行が完了するか、Podオブジェクトが削除されるか、リソース不足によって強制退去されるか、ノードが停止するまで、そのノード上にとどまります。

Podオブジェクトのためのマニフェストを作成したときは、指定したPodの名前が有効なDNSサブドメイン名であることを確認してください。

Pod OS

FEATURE STATE: Kubernetes v1.25 [stable]

.spec.os.nameフィールドでwindowslinuxのいずれかを設定し、Podを実行させたいOSを指定する必要があります。Kubernetesは今のところ、この2つのOSだけサポートしています。将来的には増える可能性があります。

Kubernetes v1.30では、このフィールドに設定した値はPodのスケジューリングに影響を与えません。.spec.os.nameを設定することで、Pod OSに権限を認証することができ、バリデーションにも使用されます。kubeletが実行されているノードのOSが、指定されたPod OSと異なる場合、kubeletはPodの実行を拒否します。 Podセキュリティの標準もこのフィールドを使用し、指定したOSと関係ないポリシーの適用を回避しています。

Podとコンテナコントローラー

ワークロードリソースは、複数のPodを作成・管理するために利用できます。リソースに対応するコントローラーが、複製やロールアウトを扱い、Podの障害時には自動回復を行います。たとえば、あるノードに障害が発生した場合、コントローラーはそのノードの動作が停止したことを検知し、代わりのPodを作成します。そして、スケジューラーが代わりのPodを健全なノード上に配置します。

以下に、1つ以上のPodを管理するワークロードリソースの一例をあげます。

Podテンプレート

workloadリソース向けのコントローラーは、PodをPodテンプレートを元に作成し、あなたの代わりにPodを管理してくれます。

PodTemplateはPodを作成するための仕様で、DeploymentJobDaemonSetなどのワークロードリソースの中に含まれています。

ワークロードリソースに対応する各コントローラーは、ワークロードオブジェクト内にあるPodTemplateを使用して実際のPodを作成します。PodTemplateは、アプリを実行するために使われるワークロードリソースがどんな種類のものであれ、その目的の状態の一部を構成するものです。

以下は、単純なJobのマニフェストの一例で、1つのコンテナを実行するtemplateがあります。Pod内のコンテナはメッセージを出力した後、一時停止します。

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # これがPodテンプレートです
    spec:
      containers:
      - name: hello
        image: busybox:1.28
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # Podテンプレートはここまでです

Podテンプレートを修正するか新しいPodに切り替えたとしても、すでに存在するPodには直接の影響はありません。ワークロードリソース内のPodテンプレートを変更すると、そのリソースは更新されたテンプレートを使用して代わりとなるPodを作成する必要があります。

たとえば、StatefulSetコントローラーは、各StatefulSetごとに、実行中のPodが現在のPodテンプレートに一致することを保証します。Podテンプレートを変更するためにStatefulSetを編集すると、StatefulSetは更新されたテンプレートを元にした新しいPodを作成するようになります。最終的に、すべての古いPodが新しいPodで置き換えられ、更新は完了します。

各ワークロードリソースは、Podテンプレートへの変更を処理するための独自のルールを実装しています。特にStatefulSetについて更に詳しく知りたい場合は、StatefulSetの基本チュートリアル内のアップデート戦略を読んでください。

ノード上では、kubeletはPodテンプレートに関する詳細について監視や管理を直接行うわけではありません。こうした詳細は抽象化されています。こうした抽象化や関心の分離のおかげでシステムのセマンティクスが単純化され、既存のコードを変更せずにクラスターの動作を容易に拡張できるようになっているのです。

Podの更新と取替

前のセクションで述べたように、ワークロードリソースのPodテンプレートが変更されると、コントローラーは既存のPodを更新したりパッチを適用したりするのではなく、更新されたテンプレートに基づいて新しいPodを作成します。

KubernetesはPodを直接管理することを妨げません。実行中のPodの一部のフィールドをその場で更新することが可能です。しかし、patchreplaceといった、Podのアップデート操作にはいくつかの制限があります:

  • Podのメタデータのほとんどは固定されたものです。たとえばnamespacenameuidまたはcreationTimestampフィールドは変更できません。generationフィールドは特別で、現在の値を増加させる更新のみを受け付けます。

  • metadata.deletionTimestampが設定されている場合、metadata.finalizersリストに新しい項目を追加することはできません。

  • Podの更新ではspec.containers[*].imagespec.initContainers[*].imagespec.activeDeadlineSecondsまたはspec.tolerations以外のフィールドを変更してはなりません。 spec.tolerationsについては新しい項目のみを追加することができます。

  • spec.activeDeadlineSecondsフィールドを更新する場合、2種類の更新が可能です:

    1. 未割り当てのフィールドに正の数を設定する
    2. 現在の値から負の数でない、より小さい数に更新する

リソースの共有と通信

Podは、データの共有と構成するコンテナ間での通信を可能にします。

Pod内のストレージ

Podでは、共有ストレージであるボリュームの集合を指定できます。Pod内のすべてのコンテナは共有ボリュームにアクセスできるため、それら複数のコンテナでデータを共有できるようになります。また、ボリュームを利用すれば、Pod内のコンテナの1つに再起動が必要になった場合にも、Pod内の永続化データを保持し続けられるようにできます。Kubernetesの共有ストレージの実装方法とPodで利用できるようにする方法に関するさらに詳しい情報は、ストレージを読んでください。

Podネットワーク

各Podには、各アドレスファミリーごとにユニークなIPアドレスが割り当てられます。Pod内のすべてのコンテナは、IPアドレスとネットワークポートを含むネットワーク名前空間を共有します。Podの中では(かつその場合にのみ)、そのPod内のコンテナはlocalhostを使用して他のコンテナと通信できます。Podの内部にあるコンテナがPodの外部にあるエンティティと通信する場合、(ポートなどの)共有ネットワークリソースの使い方をコンテナ間で調整しなければなりません。Pod内では、コンテナはIPアドレスとポートの空間を共有するため、localhostで他のコンテナにアクセスできます。また、Pod内のコンテナは、SystemVのセマフォやPOSIXの共有メモリなど、標準のプロセス間通信を使って他のコンテナと通信することもできます。異なるPod内のコンテナは異なるIPアドレスを持つため、特別な設定をしない限り、OSレベルIPCで通信することはできません。異なるPod上で実行中のコンテナ間でやり取りをしたい場合は、IPネットワークを使用して通信できます。

Pod内のコンテナは、システムのhostnameがPodに設定したnameと同一であると考えます。ネットワークについての詳しい情報は、ネットワークで説明しています。

コンテナの特権モード

Linuxでは、Pod内のどんなコンテナも、privilegedフラグをコンテナのspecのsecurity contextに設定することで、特権モード(privileged mode)を有効にできます。これは、ネットワークスタックの操作やハードウェアデバイスへのアクセスなど、オペレーティングシステムの管理者の権限が必要なコンテナの場合に役に立ちます。

WindowsHostProcessContainers機能を有効にしたクラスターの場合、Pod仕様のsecurityContextにwindowsOptions.hostProcessフラグを設定することで、Windows HostProcess Podを作成することが可能です。これらのPod内のすべてのコンテナは、Windows HostProcessコンテナとして実行する必要があります。HostProcess Podはホスト上で直接実行され、Linuxの特権コンテナで行われるような管理作業を行うのにも使用できます。

static Pod

static Podは、APIサーバーには管理されない、特定のノード上でkubeletデーモンによって直接管理されるPodのことです。大部分のPodはコントロープレーン(たとえばDeployment)によって管理されますが、static Podの場合はkubeletが各static Podを直接管理します(障害時には再起動します)。

static Podは常に特定のノード上の1つのKubeletに紐付けられます。static Podの主な用途は、セルフホストのコントロールプレーンを実行すること、言い換えると、kubeletを使用して個別のコントロールプレーンコンポーネントを管理することです。

kubeletは自動的にKubernetes APIサーバー上に各static Podに対応するミラーPodの作成を試みます。つまり、ノード上で実行中のPodはAPIサーバー上でも見えるようになるけれども、APIサーバー上から制御はできないということです。

コンテナのProbe

Probe はkubeletがコンテナに対して行う定期診断です。診断を実行するために、kubeletはさまざまなアクションを実行できます:

  • ExecAction (コンテナランタイムの助けを借りて実行)
  • TCPSocketAction (kubeletにより直接チェック)
  • HTTPGetAction (kubeletにより直接チェック)

更に詳しく知りたい場合は、PodのライフサイクルドキュメントにあるProbeを読んでください。

次の項目

Kubernetesが共通のPod APIを他のリソース内(たとえばStatefulSetDeploymentなど)にラッピングしている理由の文脈を理解するためには、Kubernetes以前から存在する以下のような既存技術について読むのが助けになります。

3.4.1.1 - Podのライフサイクル

このページではPodのライフサイクルについて説明します。Podは定義されたライフサイクルに従い Pendingフェーズから始まり、少なくとも1つのプライマリーコンテナが正常に開始した場合はRunningを経由し、次に失敗により終了したコンテナの有無に応じて、SucceededまたはFailedフェーズを経由します。

Podの実行中、kubeletはコンテナを再起動して、ある種の障害を処理できます。Pod内で、Kubernetesはさまざまなコンテナのステータスを追跡して、回復させるためのアクションを決定します。

Kubernetes APIでは、Podには仕様と実際のステータスの両方があります。Podオブジェクトのステータスは、PodのConditionのセットで構成されます。カスタムのReadiness情報をPodのConditionデータに挿入することもできます。

Podはその生存期間に1回だけスケジューリングされます。PodがNodeにスケジュール(割り当て)されると、Podは停止または終了するまでそのNode上で実行されます。

Podのライフタイム

個々のアプリケーションコンテナと同様に、Podは(永続的ではなく)比較的短期間の存在と捉えられます。Podが作成されると、一意のID(UID)が割り当てられ、(再起動ポリシーに従って)終了または削除されるまでNodeで実行されるようにスケジュールされます。
ノードが停止した場合、そのNodeにスケジュールされたPodは、タイムアウト時間の経過後に削除されます。

Pod自体は、自己修復しません。Podがnodeにスケジュールされ、その後に失敗した場合、Podは削除されます。同様に、リソースの不足またはNodeのメンテナンスによりPodはNodeから立ち退きます。Kubernetesは、比較的使い捨てのPodインスタンスの管理作業を処理する、controllerと呼ばれる上位レベルの抽象化を使用します。

特定のPod(UIDで定義)は新しいNodeに"再スケジュール"されません。代わりに、必要に応じて同じ名前で、新しいUIDを持つ同一のPodに置き換えることができます。

volumeなど、Podと同じ存続期間を持つものがあると言われる場合、それは(そのUIDを持つ)Podが存在する限り存在することを意味します。そのPodが何らかの理由で削除された場合、たとえ同じ代替物が作成されたとしても、関連するもの(例えばボリューム)も同様に破壊されて再作成されます。

Podの図

file puller(ファイル取得コンテナ)とWebサーバーを含むマルチコンテナのPod。コンテナ間の共有ストレージとして永続ボリュームを使用しています。

Podのフェーズ

Podのstatus項目はPodStatusオブジェクトで、それはphaseのフィールドがあります。

Podのフェーズは、そのPodがライフサイクルのどの状態にあるかを、簡単かつ高レベルにまとめたものです。このフェーズはコンテナやPodの状態を包括的にまとめることを目的としたものではなく、また包括的なステートマシンでもありません。

Podの各フェーズの値と意味は厳重に守られています。ここに記載されているもの以外にphaseの値は存在しないと思ってください。

これらがphaseの取りうる値です。

概要
PendingPodがKubernetesクラスターによって承認されましたが、1つ以上のコンテナがセットアップされて稼働する準備ができていません。これには、スケジュールされるまでの時間と、ネットワーク経由でイメージをダウンロードするための時間などが含まれます。
RunningPodがNodeにバインドされ、すべてのコンテナが作成されました。少なくとも1つのコンテナがまだ実行されているか、開始または再起動中です。
SucceededPod内のすべてのコンテナが正常に終了し、再起動されません。
FailedPod内のすべてのコンテナが終了し、少なくとも1つのコンテナが異常終了しました。つまり、コンテナはゼロ以外のステータスで終了したか、システムによって終了されました。
Unknown何らかの理由によりPodの状態を取得できませんでした。このフェーズは通常はPodのホストとの通信エラーにより発生します。

Nodeが停止するか、クラスターの残りの部分から切断された場合、Kubernetesは失われたNode上のすべてのPodのPhaseをFailedに設定するためのポリシーを適用します。

コンテナのステータス

Pod全体のフェーズと同様に、KubernetesはPod内の各コンテナの状態を追跡します。container lifecycle hooksを使用して、コンテナのライフサイクルの特定のポイントで実行するイベントをトリガーできます。

PodがschedulerによってNodeに割り当てられると、kubeletはcontainer runtimeを使用してコンテナの作成を開始します。コンテナの状態はWaitingRunningまたはTerminatedの3ついずれかです。

Podのコンテナの状態を確認するにはkubectl describe pod [POD_NAME]のコマンドを使用します。Pod内のコンテナごとにStateの項目として表示されます。

各状態の意味は次のとおりです。

Waiting

コンテナがRunningまたはTerminatedのいずれの状態でもない場合コンテナはWaitingの状態になります。Waiting状態のコンテナは引き続きコンテナイメージレジストリからイメージを取得したりSecretを適用したりするなど必要な操作を実行します。Waiting状態のコンテナを持つPodに対してkubectlコマンドを使用すると、そのコンテナがWaitingの状態である理由の要約が表示されます。

Running

Running状態はコンテナが問題なく実行されていることを示します。postStartフックが構成されていた場合、それはすでに実行が完了しています。Running状態のコンテナを持つPodに対してkubectlコマンドを使用すると、そのコンテナがRunning状態になった時刻が表示されます。

Terminated

Terminated状態のコンテナは実行されて、完了したときまたは何らかの理由で失敗したことを示します。Terminated状態のコンテナを持つPodに対してkubectlコマンドを使用すると、いずれにせよ理由と終了コード、コンテナの開始時刻と終了時刻が表示されます。

コンテナがTerminatedに入る前にpreStopフックがあれば実行されます。

コンテナの再起動ポリシー

Podのspecには、Always、OnFailure、またはNeverのいずれかの値を持つrestartPolicyフィールドがあります。デフォルト値はAlwaysです。

restartPolicyは、Pod内のすべてのコンテナに適用されます。restartPolicyは、同じNode上のkubeletによるコンテナの再起動のみを参照します。Pod内のコンテナが終了した後、kubeletは5分を上限とする指数バックオフ遅延(10秒、20秒、40秒...)でコンテナを再起動します。コンテナが10分間実行されると、kubeletはコンテナの再起動バックオフタイマーをリセットします。

PodのCondition

PodにはPodStatusがあります。それにはPodが成功したかどうかの情報を持つPodConditionの配列が含まれています。kubeletは、下記のPodConditionを管理します:

  • PodScheduled: PodがNodeにスケジュールされました。
  • PodHasNetwork: (アルファ版機能; 明示的に有効にしなければならない) Podサンドボックスが正常に作成され、ネットワークの設定が完了しました。
  • ContainersReady: Pod内のすべてのコンテナが準備できた状態です。
  • Initialized: すべてのInitコンテナが正常に終了しました。
  • Ready: Podはリクエストを処理でき、一致するすべてのサービスの負荷分散プールに追加されます。
フィールド名内容
typeこのPodの状態の名前です。
statusその状態が適用可能かどうか示します。可能な値は"True"、"False"、"Unknown"のうちのいずれかです。
lastProbeTimePod Conditionが最後に確認されたときのタイムスタンプが表示されます。
lastTransitionTime最後にPodのステータスの遷移があった際のタイムスタンプが表示されます。
reason最後の状態遷移の理由を示す、機械可読のアッパーキャメルケースのテキストです。
messageステータスの遷移に関する詳細を示す人間向けのメッセージです。

PodのReadiness

FEATURE STATE: Kubernetes v1.14 [stable]

追加のフィードバックやシグナルをPodStatus:Pod readinessに注入できるようにします。これを使用するには、PodのspecreadinessGatesを設定して、kubeletがPodのReadinessを評価する追加の状態のリストを指定します。

ReadinessゲートはPodのstatus.conditionsフィールドの現在の状態によって決まります。KubernetesがPodのstatus.conditionsフィールドでそのような状態を発見できない場合、ステータスはデフォルトでFalseになります。

以下はその例です。

Kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready  # これはビルトインのPodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"   # 追加のPodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

PodのConditionは、Kubernetesのlabel key formatに準拠している必要があります。

PodのReadinessの状態

kubectl patchコマンドはオブジェクトステータスのパッチ適用をまだサポートしていません。Podにこれらのstatus.conditionsを設定するには、アプリケーションとoperatorsPATCHアクションを使用する必要があります。Kubernetes client libraryを使用して、PodのReadinessのためにカスタムのPodのConditionを設定するコードを記述できます。

カスタムのPodのConditionが導入されるとPodは次の両方の条件に当てはまる場合のみ準備できていると評価されます:

  • Pod内のすべてのコンテナが準備完了している。
  • ReadinessGatesで指定された条件が全てTrueである。

Podのコンテナは準備完了ですが、少なくとも1つのカスタムのConditionが欠落しているか「False」の場合、kubeletはPodのConditionContainersReadyに設定します。

PodのネットワークのReadiness

FEATURE STATE: Kubernetes v1.25 [alpha]

Podがノードにスケジュールされた後、kubeletによって承認され、任意のボリュームがマウントされる必要があります。これらのフェーズが完了すると、kubeletはコンテナランタイム(Container runtime interface (CRI)を使用)と連携して、ランタイムサンドボックスのセットアップとPodのネットワークを構成します。もしPodHasNetworkConditionフィーチャーゲートが有効になっている場合、kubeletは、Podがこの初期化の節目に到達したかどうかをPodのstatus.conditionsフィールドにあるPodHasNetwork状態を使用して報告します。

ネットワークが設定されたランタイムサンドボックスがPodにないことを検出すると、PodHasNetwork状態は、kubelet によってFalseに設定されます。これは、以下のシナリオで発生します:

  • Podのライフサイクルの初期で、kubeletがコンテナランタイムを使用してPodのサンドボックスのセットアップをまだ開始していないとき
  • Podのライフサイクルの後期で、Podのサンドボックスが以下のどちらかの原因で破壊された場合:
    • Podを退去させず、ノードが再起動する
    • コンテナランタイムの隔離に仮想マシンを使用している場合、Podサンドボックスの仮想マシンが再起動し、新しいサンドボックスと新しいコンテナネットワーク設定を作成する必要があります

ランタイムプラグインによるサンドボックスの作成とPodのネットワーク設定が正常に完了すると、kubeletによってPodHasNetwork状態がTrueに設定されます。PodHasNetwork状態がTrueに設定された後、kubeletはコンテナイメージの取得とコンテナの作成を開始することができます。

initコンテナを持つPodの場合、initコンテナが正常に完了すると(ランタイムプラグインによるサンドボックスの作成とネットワーク設定が正常に行われた後に発生)、kubeletはInitialized状態をTrueに設定します。initコンテナがないPodの場合、サンドボックスの作成およびネットワーク設定が開始する前にkubeletはInitialized状態をTrueに設定します。

コンテナのProbe

Probekubelet により定期的に実行されるコンテナの診断です。診断を行うために、kubeletはコンテナ内でコードを実行するか、ネットワークリクエストします。

チェックのメカニズム

probeを使ってコンテナをチェックする4つの異なる方法があります。 各probeは、この4つの仕組みのうち1つを正確に定義する必要があります:

exec
コンテナ内で特定のコマンドを実行します。コマンドがステータス0で終了した場合に診断を成功と見なします。
grpc
gRPCを使ってリモートプロシージャコールを実行します。 ターゲットは、gRPC health checksを実装する必要があります。 レスポンスのstatusSERVINGの場合に診断を成功と見なします。 gRPCはアルファ版の機能のため、GRPCContainerProbeフィーチャーゲートが 有効の場合のみ利用可能です。
httpGet
PodのIPアドレスに対して、指定されたポートとパスでHTTP GETのリクエストを送信します。 レスポンスのステータスコードが200以上400未満の際に診断を成功とみなします。
tcpSocket
PodのIPアドレスに対して、指定されたポートでTCPチェックを行います。 そのポートが空いていれば診断を成功とみなします。 オープンしてすぐにリモートシステム(コンテナ)が接続を切断した場合、健全な状態としてカウントします。

Probeの結果

各Probe 次の3つのうちの一つの結果を持ちます:

Success
コンテナの診断が成功しました。
Failure
コンテナの診断が失敗しました。
Unknown
コンテナの診断自体が失敗しました(何も実行する必要はなく、kubeletはさらにチェックを行います)。

Probeの種類

kubeletは3種類のProbeを実行中のコンテナで行い、また反応することができます:

livenessProbe
コンテナが動いているかを示します。 livenessProbeに失敗すると、kubeletはコンテナを殺します、そしてコンテナはrestart policyに従います。 コンテナにlivenessProbeが設定されていない場合、デフォルトの状態はSuccessです。
readinessProbe
コンテナがリクエスト応答する準備ができているかを示します。 readinessProbeに失敗すると、エンドポイントコントローラーにより、ServiceからそのPodのIPアドレスが削除されます。 initial delay前のデフォルトのreadinessProbeの初期値はFailureです。 コンテナにreadinessProbeが設定されていない場合、デフォルトの状態はSuccessです。
startupProbe
コンテナ内のアプリケーションが起動したかどうかを示します。 startupProbeが設定された場合、完了するまでその他のすべてのProbeは無効になります。 startupProbeに失敗すると、kubeletはコンテナを殺します、そしてコンテナはrestart policyに従います。 コンテナにstartupProbeが設定されていない場合、デフォルトの状態はSuccessです。

livenessProbe、readinessProbeまたはstartupProbeを設定する方法の詳細については、Liveness Probe、Readiness ProbeおよびStartup Probeを使用するを参照してください。

livenessProbeをいつ使うべきか?

FEATURE STATE: Kubernetes v1.0 [stable]

コンテナ自体に問題が発生した場合や状態が悪くなった際にクラッシュすることができればlivenessProbeは不要です。 この場合kubeletが自動でPodのrestartPolicyに基づいたアクションを実行します。

Probeに失敗したときにコンテナを殺したり再起動させたりするには、livenessProbeを設定しrestartPolicyをAlwaysまたはOnFailureにします。

readinessProbeをいつ使うべきか?

FEATURE STATE: Kubernetes v1.0 [stable]

Probeが成功したときにのみPodにトラフィックを送信したい場合は、readinessProbeを指定します。 この場合readinessProbeはlivenessProbeと同じになる可能性がありますが、readinessProbeが存在するということは、Podがトラフィックを受けずに開始され、Probe成功が開始した後でトラフィックを受け始めることになります。

コンテナがメンテナンスのために停止できるようにするには、livenessProbeとは異なる、特定のエンドポイントを確認するreadinessProbeを指定することができます。

アプリがバックエンドサービスと厳密な依存関係にある場合、livenessProbeとreadinessProbeの両方を実装することができます。アプリ自体が健全であればlivenessProbeはパスしますが、readinessProbeはさらに、必要なバックエンドサービスが利用可能であるかどうかをチェックします。これにより、エラーメッセージでしか応答できないPodへのトラフィックの転送を避けることができます。

コンテナの起動中に大きなデータ、構成ファイル、またはマイグレーションを読み込む必要がある場合は、startupProbeを使用できます。ただし、失敗したアプリと起動データを処理中のアプリの違いを検出したい場合は、readinessProbeを使用した方が良いかもしれません。

startupProbeをいつ使うべきか?

FEATURE STATE: Kubernetes v1.20 [stable]

startupProbeは、サービスの開始に時間がかかるコンテナを持つPodに役立ちます。livenessProbeの間隔を長く設定するのではなく、コンテナの起動時に別のProbeを構成して、livenessProbeの間隔よりも長い時間を許可できます。 コンテナの起動時間が、initialDelaySeconds + failureThreshold x periodSecondsよりも長い場合は、livenessProbeと同じエンドポイントをチェックするためにstartupProbeを指定します。periodSecondsのデフォルトは10秒です。次に、failureThresholdをlivenessProbeのデフォルト値を変更せずにコンテナが起動できるように、十分に高い値を設定します。これによりデッドロックを防ぐことができます。

Podの終了

Podは、クラスター内のNodeで実行中のプロセスを表すため、不要になったときにそれらのプロセスを正常に終了できるようにすることが重要です(対照的なケースは、KILLシグナルで強制終了され、クリーンアップする機会がない場合)。

ユーザーは削除を要求可能であるべきで、プロセスがいつ終了するかを知ることができなければなりませんが、削除が最終的に完了することも保証できるべきです。ユーザーがPodの削除を要求すると、システムはPodが強制終了される前に意図された猶予期間を記録および追跡します。強制削除までの猶予期間がある場合、kubelet正常な終了を試みます。

通常、コンテナランタイムは各コンテナのメインプロセスにTERMシグナルを送信します。多くのコンテナランタイムは、コンテナイメージで定義されたSTOPSIGNAL値を尊重し、TERMシグナルの代わりにこれを送信します。猶予期間が終了すると、プロセスにKILLシグナルが送信され、PodはAPI serverから削除されます。プロセスの終了を待っている間にkubeletかコンテナランタイムの管理サービスが再起動されると、クラスターは元の猶予期間を含めて、最初からリトライされます。

フローの例は下のようになります。

  1. ユーザーがデフォルトの猶予期間(30秒)でPodを削除するためにkubectlコマンドを送信する。
  2. API server内のPodは、猶予期間を越えるとPodが「死んでいる」と見なされるように更新される。
    削除中のPodに対してkubectl describeコマンドを使用すると、Podは「終了中」と表示される。
    Podが実行されているNode上で、Podが終了しているとマークされている(正常な終了期間が設定されている)とkubeletが認識するとすぐに、kubeletはローカルでPodの終了プロセスを開始します。
    1. Pod内のコンテナの1つがpreStopフックを定義している場合は、コンテナの内側で呼び出される。猶予期間が終了した後もpreStopフックがまだ実行されている場合は、一度だけ猶予期間を延長される(2秒)。
    2. kubeletはコンテナランタイムをトリガーして、コンテナ内のプロセス番号1にTERMシグナルを送信する。
  3. kubeletが正常な終了を開始すると同時に、コントロールプレーンは、終了中のPodをEndpointSlice(およびEndpoints)オブジェクトから削除します。これらのオブジェクトは、selectorが設定されたServiceを表します。ReplicaSetsとその他のワークロードリソースは、終了中のPodを有効なサービス中のReplicaSetとして扱いません。ゆっくりと終了するPodは、(サービスプロキシのような)ロードバランサーが終了猶予期間が始まるとエンドポイントからそれらのPodを削除するので、トラフィックを継続して処理できません。
  4. 猶予期間が終了すると、kubeletは強制削除を開始する。コンテナランタイムは、Pod内でまだ実行中のプロセスにSIGKILLを送信する。kubeletは、コンテナランタイムが非表示のpauseコンテナを使用している場合、そのコンテナをクリーンアップします。
  5. kubeletは猶予期間を0(即時削除)に設定することでAPI server上のPodの削除を終了する。
  6. API serverはPodのAPIオブジェクトを削除し、クライアントからは見えなくなります。

Podの強制削除

デフォルトでは、すべての削除は30秒以内に正常に行われます。kubectl delete コマンドは、ユーザーがデフォルト値を上書きして独自の値を指定できるようにする --grace-period=<seconds> オプションをサポートします。

--grace-period0に設定した場合、PodはAPI serverから即座に強制的に削除されます。PodがNode上でまだ実行されている場合、その強制削除によりkubeletがトリガーされ、すぐにクリーンアップが開始されます。

強制削除が実行されると、API serverは、Podが実行されていたNode上でPodが停止されたというkubeletからの確認を待ちません。API内のPodは直ちに削除されるため、新しいPodを同じ名前で作成できるようになります。Node上では、すぐに終了するように設定されるPodは、強制終了される前にわずかな猶予期間が与えられます。

StatefulSetのPodについては、StatefulSetからPodを削除するためのタスクのドキュメントを参照してください。

終了したPodのガベージコレクション

失敗したPodは人間またはcontrollerが明示的に削除するまで存在します。

コントロールプレーンは終了状態のPod(SucceededまたはFailedのphaseを持つ)の数が設定された閾値(kube-controller-manager内のterminated-pod-gc-thresholdによって定義される)を超えたとき、それらのPodを削除します。これはPodが作成されて時間とともに終了するため、リソースリークを避けます。

次の項目

3.4.1.2 - Initコンテナ

このページでは、Initコンテナについて概観します。Initコンテナとは、Pod内でアプリケーションコンテナの前に実行される特別なコンテナです。 Initコンテナにはアプリケーションコンテナのイメージに存在しないセットアップスクリプトやユーティリティーを含めることができます。

Initコンテナは、Podの仕様のうちcontainersという配列(これがアプリケーションコンテナを示します)と並べて指定します。

Initコンテナを理解する

単一のPodは、Pod内にアプリケーションを実行している複数のコンテナを持つことができますが、同様に、アプリケーションコンテナが起動する前に実行されるInitコンテナも1つ以上持つことができます。

Initコンテナは下記の項目をのぞいて、通常のコンテナと全く同じものとなります。

  • Initコンテナは常に完了するまで稼働します。
  • 各Initコンテナは、次のInitコンテナが稼働する前に正常に完了しなくてはなりません。

もしあるPodの単一のInitコンテナが失敗した場合、Kubeletは成功するまで何度もそのInitコンテナを再起動します。しかし、もしそのPodのrestartPolicyがNeverで、そのPodの起動時にInitコンテナが失敗した場合、KubernetesはそのPod全体を失敗として扱います。

PodにInitコンテナを指定するためには、Podの仕様initContainersフィールドをcontainerアイテムの配列として追加してください(アプリケーションのcontainersフィールドとそのコンテンツに似ています)。 詳細については、APIリファレンスのContainerを参照してください。

Initコンテナのステータスは、.status.initContainerStatusesフィールドにコンテナのステータスの配列として返されます(.status.containerStatusesと同様)。

通常のコンテナとの違い

Initコンテナは、リソースリミット、ボリューム、セキュリティ設定などのアプリケーションコンテナの全てのフィールドと機能をサポートしています。しかし、Initコンテナに対するリソースリクエストやリソースリミットの扱いは異なります。リソースにて説明します。

また、InitコンテナはそのPodの準備ができる前に完了しなくてはならないため、lifecyclelivenessProbereadinessProbeおよびstartupProbeをサポートしていません。

複数のInitコンテナを単一のPodに対して指定した場合、KubeletはそれらのInitコンテナを1つずつ順番に実行します。各Initコンテナは、次のInitコンテナが稼働する前に正常終了しなくてはなりません。全てのInitコンテナの実行が完了すると、KubeletはPodのアプリケーションコンテナを初期化し、通常通り実行します。

Initコンテナを使用する

Initコンテナはアプリケーションコンテナのイメージとは分離されているため、コンテナの起動に関連したコードにおいていくつかの利点があります。

  • Initコンテナはアプリケーションのイメージに存在しないセットアップ用のユーティリティーやカスタムコードを含むことができます。例えば、セットアップ中にsedawkpythonや、digのようなツールを使うためだけに、別のイメージを元にしてアプリケーションイメージを作る必要がなくなります。
  • アプリケーションイメージをビルドする役割とデプロイする役割は、共同で単一のアプリケーションイメージをビルドする必要がないため、それぞれ独立して実施することができます。
  • Initコンテナは同一Pod内のアプリケーションコンテナと別のファイルシステムビューで稼働することができます。その結果、アプリケーションコンテナがアクセスできないSecretに対するアクセス権限を得ることができます。
  • Initコンテナはアプリケーションコンテナが開始する前に完了するまで実行されるため、Initコンテナを使用することで、特定の前提条件が満たされるまでアプリケーションコンテナの起動をブロックしたり遅らせることができます。前提条件が満たされると、Pod内の全てのアプリケーションコンテナを並行して起動することができます。
  • Initコンテナはアプリケーションコンテナイメージの安全性を低下させるようなユーティリティーやカスタムコードを安全に実行することができます。不必要なツールを分離しておくことで、アプリケーションコンテナイメージのアタックサーフィスを制限することができます。

Initコンテナを活用する方法について、いくつかのアイデアを次に示します。

  • シェルコマンドを使って単一のServiceが作成されるのを待機する。

    for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
    
  • 以下のようなコマンドを使って下位のAPIからPodの情報をリモートサーバに登録する。

    curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
    
  • 以下のようなコマンドを使ってアプリケーションコンテナの起動を待機する。

    sleep 60
    
  • gitリポジトリをVolumeにクローンする。

  • いくつかの値を設定ファイルに配置し、メインのアプリケーションコンテナのための設定ファイルを動的に生成するためのテンプレートツールを実行する。例えば、そのPodのPOD_IPの値を設定ファイルに配置し、Jinjaを使ってメインのアプリケーションコンテナの設定ファイルを生成する。

Initコンテナの具体的な使用方法

下記の例は2つのInitコンテナを含むシンプルなPodを定義しています。 1つ目のInitコンテナはmyserviesの起動を、2つ目のInitコンテナはmydbの起動をそれぞれ待ちます。両方のInitコンテナの実行が完了すると、Podはspecセクションにあるアプリケーションコンテナを実行します。

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

次のコマンドを実行して、このPodを開始できます。

kubectl apply -f myapp.yaml

実行結果は下記のようになります。

pod/myapp-pod created

そして次のコマンドでステータスを確認します。

kubectl get -f myapp.yaml

実行結果は下記のようになります。

NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

より詳細な情報は次のコマンドで確認します。

kubectl describe -f myapp.yaml

実行結果は下記のようになります。

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app.kubernetes.io/name=MyApp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Events:
  FirstSeen    LastSeen    Count    From                      SubObjectPath                           Type          Reason        Message
  ---------    --------    -----    ----                      -------------                           --------      ------        -------
  16s          16s         1        {default-scheduler }                                              Normal        Scheduled     Successfully assigned myapp-pod to 172.17.4.201
  16s          16s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulling       pulling image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulled        Successfully pulled image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Created       Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container with docker id 5ced34a04634

このPod内のInitコンテナのログを確認するためには、次のコマンドを実行します。

kubectl logs myapp-pod -c init-myservice # 1つ目のInitコンテナを調査する
kubectl logs myapp-pod -c init-mydb      # 2つ目のInitコンテナを調査する

この時点で、これらのInitコンテナはmydbmyserviceという名前のServiceの検出を待機しています。

これらのServiceを検出させるための構成は以下の通りです。

---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

mydbおよびmyserviceというServiceを作成するために、以下のコマンドを実行します。

kubectl apply -f services.yaml

実行結果は下記のようになります。

service/myservice created
service/mydb created

Initコンテナが完了し、myapp-podというPodがRunning状態に移行したことが確認できます。

kubectl get -f myapp.yaml

実行結果は下記のようになります。

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

このシンプルな例を独自のInitコンテナを作成する際の参考にしてください。次の項目にさらに詳細な使用例に関するリンクがあります。

Initコンテナのふるまいに関する詳細

Podの起動時に、kubeletはネットワークおよびストレージの準備が整うまで、Initコンテナを実行可能な状態にしません。また、kubeletはPodのspecに定義された順番に従ってPodのInitコンテナを起動します。

各Initコンテナは次のInitコンテナが起動する前に正常に終了しなくてはなりません。もしあるInitコンテナがランタイムにより起動失敗した場合、もしくはエラーで終了した場合、そのPodのrestartPolicyの値に従ってリトライされます。しかし、もしPodのrestartPolicyAlwaysに設定されていた場合、InitコンテナのrestartPolicyOnFailureが適用されます。

Podは全てのInitコンテナが完了するまでReady状態となりません。Initコンテナ上のポートはServiceによって集約されません。初期化中のPodのステータスはPendingとなりますが、Initializedという値はtrueとなります。

もしそのPodを再起動するとき、または再起動されたとき、全てのInitコンテナは必ず再度実行されます。

Initコンテナの仕様の変更は、コンテナイメージのフィールドのみに制限されています。 Initコンテナのイメージフィールド値を変更すると、そのPodは再起動されます。

Initコンテナは何度も再起動、リトライおよび再実行可能なため、べき等(Idempotent)である必要があります。特に、EmptyDirsにファイルを書き込むコードは、書き込み先のファイルがすでに存在している可能性を考慮に入れる必要があります。

Initコンテナはアプリケーションコンテナの全てのフィールドを持っています。しかしKubernetesは、Initコンテナが完了と異なる状態を定義できないためreadinessProbeが使用されることを禁止しています。これはバリデーションの際に適用されます。

Initコンテナがずっと失敗し続けたままの状態を防ぐために、PodにactiveDeadlineSecondsを設定してください。activeDeadlineSecondsの設定はInitコンテナが実行中の時間にも適用されます。しかしactiveDeadlineSecondsはInitコンテナが終了した後でも効果があるため、チームがアプリケーションをJobとしてデプロイする場合にのみ使用することが推奨されています。 すでに正しく動作しているPodはactiveDeadlineSecondsを設定すると強制終了されます。

Pod内の各アプリケーションコンテナとInitコンテナの名前はユニークである必要があります。他のコンテナと同じ名前を共有していた場合、バリデーションエラーが返されます。

リソース

Initコンテナの順序と実行を考えるとき、リソースの使用に関して下記のルールが適用されます。

  • 全てのInitコンテナの中で定義された最も高いリソースリクエストとリソースリミットが、有効なinitリクエスト/リミット になります。いずれかのリソースでリミットが設定されていない場合、これが最上級のリミットとみなされます。
  • Podのリソースの有効なリクエスト/リミット は、下記の2つの中のどちらか高い方となります。
    • リソースに対する全てのアプリケーションコンテナのリクエスト/リミットの合計
    • リソースに対する有効なinitリクエスト/リミット
  • スケジューリングは有効なリクエスト/リミットに基づいて実行されます。つまり、InitコンテナはPodの生存中には使用されない初期化用のリソースを確保することができます。
  • Podの有効なQoS(quality of service)ティアー は、Initコンテナとアプリケーションコンテナで同様です。

クォータとリミットは有効なPodリクエストとリミットに基づいて適用されます。

Podレベルのコントロールグループ(cgroups)は、スケジューラーと同様に、有効なPodリクエストとリミットに基づいています。

Podの再起動の理由

以下の理由によりPodは再起動し、Initコンテナの再実行も引き起こす可能性があります。

  • そのPodのインフラストラクチャーコンテナが再起動された場合。これはあまり起きるものでなく、Nodeに対するルート権限を持ったユーザーにより行われることがあります。
  • restartPolicyAlwaysと設定されているPod内の全てのコンテナが停止され、強制的に再起動が行われたことで、ガベージコレクションによりInitコンテナの完了記録が失われた場合。

Kubernetes v1.20以降では、initコンテナのイメージが変更されたり、ガベージコレクションによってinitコンテナの完了記録が失われたりした場合でも、Podは再起動されません。以前のバージョンを使用している場合は、対応バージョンのドキュメントを参照してください。

次の項目

3.4.1.3 - サイドカーコンテナ

FEATURE STATE: Kubernetes v1.29 [beta]

サイドカーコンテナは、メインのアプリケーションコンテナと同じPod内で実行されるセカンダリーコンテナです。 これらのコンテナは、主要なアプリケーションコードを直接変更することなく、ロギング、モニタリング、セキュリティ、データの同期などの追加サービスや機能を提供することにより、アプリケーションコンテナの機能を強化または拡張するために使用されます。

サイドカーコンテナの有効化

Kubernetes 1.29でデフォルトで有効化されたSidecarContainersという名前の フィーチャーゲートにより、 PodのinitContainersフィールドに記載されているコンテナのrestartPolicyを指定することができます。 これらの再起動可能な サイドカー コンテナは、同じポッド内の他のinitコンテナやメインのアプリケーションコンテナとは独立しています。 これらは、メインアプリケーションコンテナや他のinitコンテナに影響を与えることなく、開始、停止、または再起動することができます。

サイドカーコンテナとPodのライフサイクル

もしinitコンテナがrestartPolicyAlwaysに設定して作成された場合、それはPodのライフサイクル全体にわたって起動し続けます。 これは、メインアプリケーションコンテナから分離されたサポートサービスを実行するのに役立ちます。

このinitコンテナにreadinessProbeが指定されている場合、その結果はPodのready状態を決定するために使用されます。

これらのコンテナはinitコンテナとして定義されているため、他のinitコンテナと同様に順序に関する保証を受けることができ、複雑なPodの初期化フローに他のinitコンテナと混在させることができます。

通常のinitコンテナと比較して、initContainers内で定義されたサイドカーは、開始した後も実行を続けます。 これは、.spec.initContainersにPod用の複数のエントリーがある場合に重要です。 サイドカースタイルのinitコンテナが実行中になった後(kubeletがそのinitコンテナのstartedステータスをtrueに設定した後)、kubeletは順序付けられた.spec.initContainersリストから次のinitコンテナを開始します。 そのステータスは、コンテナ内でプロセスが実行されておりStartup Probeが定義されていない場合、あるいはそのstartupProbeが成功するとtrueになります。

以下は、サイドカーを含む2つのコンテナを持つDeploymentの例です:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: alpine:latest
          command: ['sh', '-c', 'while true; do echo "logging" >> /opt/logs.txt; sleep 1; done']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      volumes:
        - name: data
          emptyDir: {}

この機能は、サイドカーコンテナがメインコンテナが終了した後もジョブが完了するのを妨げないため、サイドカーを持つジョブを実行するのにも役立ちます。

以下は、サイドカーを含む2つのコンテナを持つJobの例です:

apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    spec:
      containers:
        - name: myjob
          image: alpine:latest
          command: ['sh', '-c', 'echo "logging" > /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      restartPolicy: Never
      volumes:
        - name: data
          emptyDir: {}
          

通常のコンテナとの違い

サイドカーコンテナは、同じPod内の通常のコンテナと並行して実行されます。 しかし、主要なアプリケーションロジックを実行するわけではなく、メインのアプリケーションにサポート機能を提供します。

サイドカーコンテナは独自の独立したライフサイクルを持っています。 通常のコンテナとは独立して開始、停止、再起動することができます。 これは、メインアプリケーションに影響を与えることなく、サイドカーコンテナを更新、スケール、メンテナンスできることを意味します。

サイドカーコンテナは、メインのコンテナと同じネットワークおよびストレージの名前空間を共有します。 このような配置により、密接に相互作用し、リソースを共有することができます。

initコンテナとの違い

サイドカーコンテナは、メインのコンテナと並行して動作し、その機能を拡張し、追加サービスを提供します。

サイドカーコンテナは、メインアプリケーションコンテナと並行して実行されます。 Podのライフサイクル全体を通じてアクティブであり、メインコンテナとは独立して開始および停止することができます。 Initコンテナとは異なり、サイドカーコンテナはライフサイクルを制御するためのProbeをサポートしています。

これらのコンテナは、メインアプリケーションコンテナと直接相互作用することができ、同じネットワーク名前空間、ファイルシステム、環境変数を共有します。追加の機能を提供するために緊密に連携して動作します。

コンテナ内のリソース共有

Initコンテナ、サイドカーコンテナ、アプリケーションコンテナの順序と実行を考えるとき、リソースの使用に関して下記のルールが適用されます。

  • 全てのInitコンテナの中で定義された最も高いリソースリクエストとリソースリミットが、有効なinitリクエスト/リミット になります。いずれかのリソースでリミットが設定されていない場合、これが最上級のリミットとみなされます。
  • Podのリソースの有効なリクエスト/リミット は、Podのオーバーヘッドと次のうち大きい方の合計になります。
    • リソースに対する全てのアプリケーションコンテナとサイドカーコンテナのリクエスト/リミットの合計
    • リソースに対する有効なinitリクエスト/リミット
  • スケジューリングは有効なリクエスト/リミットに基づいて実行されます。つまり、InitコンテナはPodの生存中には使用されない初期化用のリソースを確保することができます。
  • Podの有効なQoS(quality of service)ティアー は、Initコンテナ、サイドカーコンテナ、アプリケーションコンテナで同様です。

クォータとリミットは有効なPodリクエストとリミットに基づいて適用されます。

Podレベルのコントロールグループ(cgroups)は、スケジューラーと同様に、有効なPodリクエストとリミットに基づいています。

次の項目

3.4.1.4 - Disruption

このガイドは、高可用性アプリケーションを構築したいと考えており、そのために、Podに対してどのような種類のDisruptionが発生する可能性があるか理解する必要がある、アプリケーション所有者を対象としたものです。

また、クラスターのアップグレードやオートスケーリングなどのクラスターの操作を自動化したいクラスター管理者も対象にしています。

自発的なDisruptionと非自発的なDisruption

Podは誰か(人やコントローラー)が破壊するか、避けることができないハードウェアまたはシステムソフトウェアエラーが発生するまで、消えることはありません。

これらの不可避なケースをアプリケーションに対する非自発的なDisruptionと呼びます。 例えば:

  • ノードのバックエンドの物理マシンのハードウェア障害
  • クラスター管理者が誤ってVM(インスタンス)を削除した
  • クラウドプロバイダーまたはハイパーバイザーの障害によってVMが消えた
  • カーネルパニック
  • クラスターネットワークパーティションが原因でクラスターからノードが消えた
  • ノードのリソース不足によるPodの退避

リソース不足を除いて、これら条件は全て、大半のユーザーにとって馴染みのあるものでしょう。 これらはKubernetesに固有のものではありません。

それ以外のケースのことを自発的なDisruptionと呼びます。 これらはアプリケーションの所有者によって起こされたアクションと、クラスター管理者によって起こされたアクションの両方を含みます。 典型的なアプリケーションの所有者によるアクションには次のものがあります:

  • Deploymentやその他のPodを管理するコントローラーの削除
  • 再起動を伴うDeployment内のPodのテンプレートの更新
  • Podの直接削除(例:アクシデントによって)

クラスター管理者のアクションには、次のようなものが含まれます:

  • 修復やアップグレードのためのノードのドレイン
  • クラスターのスケールダウンのためにクラスターからノードをドレインする(クラスター自動スケーリングについて学ぶ)。
  • そのノードに別のものを割り当てることができるように、ノードからPodを削除する。

これらのアクションはクラスター管理者によって直接実行されるか、クラスター管理者やクラスターをホスティングしているプロバイダーによって自動的に実行される可能性があります。

クラスターに対して自発的なDisruptionの要因となるものが有効になっているかどうかについては、クラスター管理者に聞くか、クラウドプロバイダーに相談または配布文書を参照してください。 有効になっているものが何もなければ、Pod Disruption Budgetの作成はスキップすることができます。

Disruptionへの対応

非自発的なDisruptionを軽減する方法をいくつか紹介します:

  • Podは必要なリソースを要求するようにする。
  • 高可用性が必要な場合はアプリケーションをレプリケートする。(レプリケートされたステートレスおよびステートフルアプリケーションの実行について学ぶ。)
  • レプリケートされたアプリケーションを実行する際にさらに高い可用性を得るには、(アンチアフィニティを使って)ラックを横断して、または(マルチゾーンクラスターを使用している場合には)ゾーンを横断してアプリケーションを分散させる。

自発的なDisruptionの頻度は様々です。 基本的なKubernetesクラスターでは、自動で発生する自発的なDisruptionはありません(ユーザーによってトリガーされたものだけです)。 しかし、クラスター管理者やホスティングプロバイダーが何か追加のサービスを実行して自発的なDisruptionが発生する可能性があります。 例えば、ノード上のソフトウェアアップデートのロールアウトは自発的なDisruptionの原因となります。 また、クラスター(ノード)自動スケーリングの実装の中には、ノードのデフラグとコンパクト化のために自発的なDisruptionを伴うものがあります。 クラスタ管理者やホスティングプロバイダーは、自発的なDisruptionがある場合、どの程度のDisruptionが予想されるかを文書化しているはずです。 Podのspecの中でPriorityClassesを使用している場合など、特定の設定オプションによっても自発的(および非自発的)なDisruptionを引き起こす可能性があります。

Pod Disruption Budget

FEATURE STATE: Kubernetes v1.21 [stable]

Kubernetesは、自発的なDisruptionが頻繁に発生する場合でも、可用性の高いアプリケーションの運用を支援する機能を提供しています。

アプリケーションの所有者として、各アプリケーションに対してPodDisruptionBudget (PDB)を作成することができます。 PDBは、レプリカを持っているアプリケーションのうち、自発的なDisruptionによって同時にダウンするPodの数を制限します。 例えば、クォーラムベースのアプリケーションでは、実行中のレプリカの数がクォーラムに必要な数を下回らないようにする必要があります。 Webフロントエンドは、負荷に対応するレプリカの数が、全体に対して一定の割合を下回らないようにしたいかもしれません。

クラスター管理者やホスティングプロバイダーは、直接PodやDeploymentを削除するのではなく、Eviction APIを呼び出す、PodDisruptionBudgetsに配慮したツールを使用すべきです。

例えば、kubectl drainサブコマンドはノードを休止中とマークします。 kubectl drainを実行すると、ツールは休止中としたノード上の全てのPodを退避しようとします。 kubectlがあなたの代わりに送信する退避要求は一時的に拒否される可能性があるため、ツールは対象のノード上の全てのPodが終了するか、設定可能なタイムアウト時間に達するまで、全ての失敗した要求を定期的に再試行します。

PDBはアプリケーションの意図したレプリカ数に対して、許容できるレプリカの数を指定します。 例えば.spec.replicas: 5を持つDeploymentは常に5つのPodを持つことが想定されます。 PDBが同時に4つまでを許容する場合、Eviction APIは1度に(2つではなく)1つのPodの自発的なDisruptionを許可します。

アプリケーションを構成するPodのグループは、アプリケーションのコントローラー(Deployment、StatefulSetなど)が使用するものと同じラベルセレクターを使用して指定されます。

"意図した"Podの数は、これらのPodを管理するワークロードリソースの.spec.replicasから計算されます。 コントロールプレーンはPodの.metadata.ownerReferencesを調べることで、所有しているワークロードリソースを見つけます。

非自発的なDisruptionはPDBによって防ぐことができません; しかし、予算にはカウントされます。

アプリケーションのローリングアップデートによって削除または利用できなくなったPodはDisruptionの予算にカウントされますが、ローリングアップグレードを実行している時は(DeploymentやStatefulSetなどの)ワークロードリソースはPDBによって制限されません。 代わりに、アプリケーションのアップデート中の障害のハンドリングは、個々のワークロードリソースに対するspecで設定されます。

ノードのドレイン中に動作がおかしくなったアプリケーションの退避をサポートするために、Unhealthy Pod Eviction PolicyAlwaysAllowを設定することを推奨します。 既定の動作は、ドレインを継続する前にアプリケーションPodがhealthyな状態になるまで待機します。

Eviction APIを使用してPodを退避した場合、PodSpecで設定したterminationGracePeriodSecondsに従って正常に終了します。

PodDisruptionBudgetの例

node-1からnode-3まで3つのノードがあるクラスターを考えます。 クラスターにはいくつかのアプリケーションが動いています。 それらのうちの1つは3つのレプリカを持ち、最初はpod-apod-bそしてpod-cと名前が付いています。 もう一つ、これとは独立したPDBなしのpod-xと呼ばれるものもあります。 初期状態ではPodは次のようにレイアウトされています:

node-1node-2node-3
pod-a availablepod-b availablepod-c available
pod-x available

3つのPodはすべてDeploymentの一部で、これらはまとめて1つのPDBを持ち、3つのPodのうちの少なくとも2つが常に存在していることを要求します。

例えばクラスター管理者がカーネルのバグを修正するために、再起動して新しいカーネルバージョンにしたいとします。 クラスター管理者はまず、kubectl drainコマンドを使ってnode-1をドレインしようとします。 ツールはpod-apod-xを退避しようとします。 これはすぐに成功します。 2つのPodは同時にterminating状態になります。 これにより、クラスターは次のような状態になります:

node-1 drainingnode-2node-3
pod-a terminatingpod-b availablepod-c available
pod-x terminating

DeploymentはPodの1つが終了中であることに気づき、pod-dという代わりのPodを作成します。 node-1はcordonされたため、別のノードに展開されます。 また、pod-xの代わりとしてpod-yも作られました。

(備考: StatefulSetの場合、pod-apod-0のように呼ばれ、代わりのPodが作成される前に完全に終了する必要があります。 この代わりのPodは、UIDは異なりますが、同じpod-0という名前になります。 それを除けば、本例はStatefulSetにも当てはまります。)

現在、クラスターは次のような状態になっています:

node-1 drainingnode-2node-3
pod-a terminatingpod-b availablepod-c available
pod-x terminatingpod-d startingpod-y

ある時点でPodは終了し、クラスターはこのようになります:

node-1 drainednode-2node-3
pod-b availablepod-c available
pod-d startingpod-y

この時点で、せっかちなクラスター管理者がnode-2node-3をドレインしようとすると、Deploymentの利用可能なPodは2つしかなく、また、PDBによって最低2つのPodが要求されているため、drainコマンドはブロックされます。 しばらくすると、pod-dが使用可能になります。

クラスターの状態はこのようになります:

node-1 drainednode-2node-3
pod-b availablepod-c available
pod-d availablepod-y

ここでクラスター管理者がnode-2をドレインしようとします。 drainコマンドは2つのPodをなんらかの順番で退避しようとします。 例えば最初にpod-b、次にpod-dとします。 pod-bについては退避に成功します。 しかしpod-dを退避しようとすると、Deploymentに対して利用可能なPodは1つしか残らないため、退避は拒否されます。

Deploymentはpod-bの代わりとしてpod-eを作成します。 クラスターにはpod-eをスケジューリングする十分なリソースがないため、ドレインは再びブロックされます。 クラスターは次のような状態になります:

node-1 drainednode-2node-3no node
pod-b terminatingpod-c availablepod-e pending
pod-d availablepod-y

この時点で、クラスター管理者はアップグレードを継続するためにクラスターにノードを追加する必要があります。

KubernetesがどのようにDisruptionの発生率を変化させているかについては、次のようなものから知ることができます:

  • いくつのレプリカをアプリケーションが必要としているか
  • インスタンスのグレースフルシャットダウンにどれくらいの時間がかかるか
  • 新しいインスタンスのスタートアップにどれくらいの時間がかかるか
  • コントローラーの種類
  • クラスターリソースのキャパシティ

Pod Disruption Condition

FEATURE STATE: Kubernetes v1.26 [beta]

有効にすると、専用のPod DisruptionTarget Conditionが追加されます。 これはPodがDisruptionによって削除されようとしていることを示すものです。 Conditionのreasonフィールドにて、追加で以下のいずれかをPodの終了の理由として示します:

PreemptionByScheduler
Podはより高い優先度を持つ新しいPodを収容するために、スケジューラーによってプリエンプトされる予定です。 詳細についてはPodの優先度とプリエンプションを参照してください。
DeletionByTaintManager
Podが許容しないNoExecute taintによって、Podは(kube-controller-managerの中のノードライフサイクルコントローラーである)Taintマネージャーによって削除される予定です。 taintベースの退避を参照してください。
EvictionByEvictionAPI
PodはKubernetes APIを使用して退避するようにマークされました。
DeletionByPodGC
すでに存在しないノードに紐づいているPodのため、Podのガベージコレクションによって削除される予定です。
TerminationByKubelet
node-pressureによる退避またはGraceful Node Shutdownのため、Podはkubeletによって終了させられました。

フィーチャーゲートPodDisruptionConditionsを有効にすると、Podのクリーンアップと共に、Podガベージコレクタ(PodGC)が非終了フェーズにあるPodを失敗とマークします。 (Podガベージコレクションも参照してください)。

Job(またはCronJob)を使用している場合、JobのPod失敗ポリシーの一部としてこれらのPod Disruption Conditionを使用したいと思うかもしれません。

クラスターオーナーとアプリケーションオーナーロールの分離

多くの場合、クラスター管理者とアプリケーションオーナーは、互いの情報を一部しか持たない別の役割であると考えるのが便利です。 このような責任の分離は、次のようなシナリオで意味を持つことがあります:

  • 多くのアプリケーションチームでKubernetesクラスターを共有していて、役割の専門化が自然に行われている場合
  • クラスター管理を自動化するためにサードパーティのツールやサービスを使用している場合

Pod Disruption Budgetはロール間のインターフェースを提供することによって、この役割の分離をサポートします。

もしあなたの組織でこのような責任の分担がなされていない場合は、Pod Disruption Budgetを使用する必要はないかもしれません。

クラスターで破壊的なアクションを実行する方法

あなたがクラスターの管理者で、ノードやシステムソフトウェアのアップグレードなど、クラスター内のすべてのノードに対して破壊的なアクションを実行する必要がある場合、次のような選択肢があります:

  • アップグレードの間のダウンタイムを許容する。
  • もう一つの完全なレプリカクラスターにフェールオーバーする。
    • ダウンタイムはありませんが、重複するノードと、切り替えを調整する人的労力の両方のコストがかかる可能性があります。
  • Disruptionに耐性のあるアプリケーションを書き、PDBを使用する。
    • ダウンタイムはありません。
    • リソースの重複は最小限です。
    • クラスター管理をより自動化できます。
    • Disruptionに耐えうるアプリケーションを書くことは大変ですが、自発的なDisruptionに耐えうるようにするための作業は、非自発的なDisruptionに耐えうるために必要な作業とほぼ重複しています。

次の項目

3.4.1.5 - エフェメラルコンテナ

FEATURE STATE: Kubernetes v1.16 [alpha]

このページでは、特別な種類のコンテナであるエフェメラルコンテナの概要を説明します。エフェメラルコンテナは、トラブルシューティングなどのユーザーが開始するアクションを実行するために、すでに存在するPod内で一時的に実行するコンテナです。エフェメラルコンテナは、アプリケーションの構築ではなく、serviceの調査のために利用します。

エフェメラルコンテナを理解する

Podは、Kubernetesのアプリケーションの基本的なビルディングブロックです。Podは破棄可能かつ置き換え可能であることが想定されているため、一度Podが作成されると新しいコンテナを追加することはできません。その代わりに、通常はDeploymentを使用してPodを削除して置き換えます。

たとえば、再現困難なバグのトラブルシューティングなどのために、すでに存在するPodの状態を調査する必要が出てくることがあります。このような場合、既存のPod内でエフェメラルコンテナを実行することで、Podの状態を調査したり、任意のコマンドを実行したりできます。

エフェメラルコンテナとは何か?

エフェメラルコンテナは、他のコンテナと異なり、リソースや実行が保証されず、自動的に再起動されることも決してないため、アプリケーションを構築する目的には適しません。エフェメラルコンテナは、通常のコンテナと同じContainerSpecで記述されますが、多くのフィールドに互換性がなかったり、使用できなくなっています。

  • エフェメラルコンテナはポートを持つことができないため、portslivenessProbereadinessProbeなどは使えなくなっています。
  • Podリソースの割り当てはイミュータブルであるため、resourcesの設定が禁止されています。
  • 利用が許可されているフィールドの一覧については、EphemeralContainerのリファレンスドキュメントを参照してください。

エフェメラルコンテナは、直接pod.specに追加するのではなく、API内の特別なephemeralcontainersハンドラを使用して作成します。そのため、エフェメラルコンテナをkubectl editを使用して追加することはできません。

エフェメラルコンテナをPodに追加した後は、通常のコンテナのようにエフェメラルコンテナを変更または削除することはできません。

エフェメラルコンテナの用途

エフェメラルコンテナは、コンテナがクラッシュしてしまったり、コンテナイメージにデバッグ用ユーティリティが同梱されていない場合など、kubectl execでは不十分なときにインタラクティブなトラブルシューティングを行うために役立ちます。

特に、distrolessイメージを利用すると、攻撃対象領域を減らし、バグや脆弱性を露出する可能性を減らせる最小のコンテナイメージをデプロイできるようになります。distrolessイメージにはシェルもデバッグ用のユーティリティも含まれないため、kubectl execのみを使用してdistrolessイメージのトラブルシューティングを行うのは困難です。

エフェメラルコンテナを利用する場合には、他のコンテナ内のプロセスにアクセスできるように、プロセス名前空間の共有を有効にすると便利です。

エフェメラルコンテナを利用してトラブルシューティングを行う例については、デバッグ用のエフェメラルコンテナを使用してデバッグするを参照してください。

Ephemeral containers API

このセクションの例では、API内でエフェメラルコンテナを表示する方法を示します。通常は、APIを直接呼び出すのではなく、kubectl alpha debugやその他のkubectlプラグインを使用して、これらのステップを自動化します。

エフェメラルコンテナは、Podのephemeralcontainersサブリソースを使用して作成されます。このサブリソースは、kubectl --rawを使用して確認できます。まずはじめに、以下にEphemeralContainersリストとして追加するためのエフェメラルコンテナを示します。

{
    "apiVersion": "v1",
    "kind": "EphemeralContainers",
    "metadata": {
        "name": "example-pod"
    },
    "ephemeralContainers": [{
        "command": [
            "sh"
        ],
        "image": "busybox",
        "imagePullPolicy": "IfNotPresent",
        "name": "debugger",
        "stdin": true,
        "tty": true,
        "terminationMessagePolicy": "File"
    }]
}

すでに実行中のexample-podのエフェメラルコンテナを更新するには、次のコマンドを実行します。

kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers -f ec.json

このコマンドを実行すると、新しいエフェメラルコンテナのリストが返されます。

{
   "kind":"EphemeralContainers",
   "apiVersion":"v1",
   "metadata":{
      "name":"example-pod",
      "namespace":"default",
      "selfLink":"/api/v1/namespaces/default/pods/example-pod/ephemeralcontainers",
      "uid":"a14a6d9b-62f2-4119-9d8e-e2ed6bc3a47c",
      "resourceVersion":"15886",
      "creationTimestamp":"2019-08-29T06:41:42Z"
   },
   "ephemeralContainers":[
      {
         "name":"debugger",
         "image":"busybox",
         "command":[
            "sh"
         ],
         "resources":{

         },
         "terminationMessagePolicy":"File",
         "imagePullPolicy":"IfNotPresent",
         "stdin":true,
         "tty":true
      }
   ]
}

新しく作成されたエフェメラルコンテナの状態を確認するには、kubectl describeを使用します。

kubectl describe pod example-pod
...
Ephemeral Containers:
  debugger:
    Container ID:  docker://cf81908f149e7e9213d3c3644eda55c72efaff67652a2685c1146f0ce151e80f
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:9f1003c480699be56815db0f8146ad2e22efea85129b5b5983d0e0fb52d9ab70
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
    State:          Running
      Started:      Thu, 29 Aug 2019 06:42:21 +0000
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

新しいエフェメラルコンテナとやりとりをするには、他のコンテナと同じように、kubectl attachkubectl execkubectl logsなどのコマンドが利用できます。例えば、次のようなコマンドが実行できます。

kubectl attach -it example-pod -c debugger

3.4.2 - ワークロードリソース

3.4.2.1 - Deployment

DeploymentPodReplicaSetの宣言的なアップデート機能を提供します。

Deploymentにおいて 理想的な状態 を記述すると、Deploymentコントローラーは指定された頻度で現在の状態を理想的な状態に変更します。Deploymentを定義することによって、新しいReplicaSetを作成したり、既存のDeploymentを削除して新しいDeploymentで全てのリソースを適用できます。

ユースケース

以下の項目はDeploymentの典型的なユースケースです。

  • ReplicaSetをロールアウトするためにDeploymentの作成を行う: ReplicaSetはバックグラウンドでPodを作成します。Podの作成が完了したかどうかは、ロールアウトのステータスを確認してください。
  • DeploymentのPodTemplateSpecを更新することによりPodの新しい状態を宣言する: 新しいReplicaSetが作成され、Deploymentは指定された頻度で古いReplicaSetから新しいReplicaSetへのPodの移行を管理します。新しいReplicaSetはDeploymentのリビジョンを更新します。
  • Deploymentの現在の状態が不安定な場合、Deploymentのロールバックをする: ロールバックによる各更新作業は、Deploymentのリビジョンを更新します。
  • より多くの負荷をさばけるように、Deploymentをスケールアップする。
  • PodTemplateSpecに対する複数の修正を適用するためにDeploymentを停止(Pause)し、それを再開して新しいロールアウトを開始します。
  • Deploymentのステータス をロールアウトが失敗したサインとして利用する。
  • 今後必要としない古いReplicaSetのクリーンアップ

Deploymentの作成

以下はDeploymentの例です。これはnginxPodのレプリカを3つ持つReplicaSetを作成します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

この例では、

  • .metadata.nameフィールドで指定されたnginx-deploymentという名前のDeploymentが作成されます。

  • このDeploymentは.spec.replicasフィールドで指定された通り、3つのレプリカPodを作成します。

  • .spec.selectorフィールドは、Deploymentが管理するPodのラベルを定義します。ここでは、Podテンプレートにて定義されたラベル(app: nginx)を選択しています。しかし、PodTemplate自体がそのルールを満たす限り、さらに洗練された方法でセレクターを指定することができます。

  • templateフィールドは、以下のサブフィールドを持ちます。:

    • Podは.metadata.labelsフィールドによって指定されたapp: nginxというラベルがつけられます。
    • PodTemplate、または.template.specフィールドは、Podがnginxという名前でDocker Hubにあるnginxのバージョン1.14.2が動くコンテナを1つ動かすことを示します。
    • 1つのコンテナを作成し、.spec.template.spec.containers[0].nameフィールドを使ってnginxという名前をつけます。

作成を始める前に、Kubernetesクラスターが稼働していることを確認してください。 上記のDeploymentを作成するためには以下のステップにしたがってください:

  1. 以下のコマンドを実行してDeploymentを作成してください。
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
  1. Deploymentが作成されたことを確認するために、kubectl get deploymentsを実行してください。

Deploymentがまだ作成中の場合、コマンドの実行結果は以下のとおりです。

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     0            0           1s

クラスターにてDeploymentを調査するとき、以下のフィールドが出力されます。

  • NAMEは、クラスター内にあるDeploymentの名前一覧です。
  • READYは、ユーザーが使用できるアプリケーションのレプリカの数です。使用可能な数/理想的な数の形式で表示されます。
  • UP-TO-DATEは、理想的な状態を満たすためにアップデートが完了したレプリカの数です。
  • AVAILABLEは、ユーザーが利用可能なレプリカの数です。
  • AGEは、アプリケーションが稼働してからの時間です。

.spec.replicasフィールドの値によると、理想的なレプリカ数は3であることがわかります。

  1. Deploymentのロールアウトステータスを確認するために、kubectl rollout status deployment.v1.apps/nginx-deploymentを実行してください。

コマンドの実行結果は以下のとおりです。

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out
  1. 数秒後、再度kubectl get deploymentsを実行してください。 コマンドの実行結果は以下のとおりです。
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18s

Deploymentが3つ全てのレプリカを作成して、全てのレプリカが最新(Podが最新のPodテンプレートを含んでいる)になり、利用可能となっていることを確認してください。

  1. Deploymentによって作成されたReplicaSet(rs)を確認するにはkubectl get rsを実行してください。コマンドの実行結果は以下のとおりです:
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-75675f5897   3         3         3       18s

ReplicaSetの出力には次のフィールドが表示されます:

  • NAMEは、名前空間内にあるReplicaSetの名前の一覧です。
  • DESIREDは、アプリケーションの理想的な レプリカ の値です。これはDeploymentを作成したときに定義したもので、これが 理想的な状態 と呼ばれるものです。
  • CURRENTは現在実行されているレプリカの数です。
  • READYは、ユーザーが使用できるアプリケーションのレプリカの数です。
  • AGEは、アプリケーションが稼働してからの時間です。

ReplicaSetの名前は[Deployment名]-[ランダム文字列]という形式になることに注意してください。ランダム文字列はランダムに生成され、pod-template-hashをシードとして使用します。

  1. 各Podにラベルが自動的に付けられるのを確認するにはkubectl get pods --show-labelsを実行してください。 コマンドの実行結果は以下のとおりです:
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897

作成されたReplicaSetはnginxPodを3つ作成することを保証します。

pod-template-hashラベル

pod-template-hashラベルはDeploymentコントローラーによってDeploymentが作成し適用した各ReplicaSetに対して追加されます。

このラベルはDeploymentが管理するReplicaSetが重複しないことを保証します。このラベルはReplicaSetのPodTemplateをハッシュ化することにより生成され、生成されたハッシュ値はラベル値としてReplicaSetセレクター、Podテンプレートラベル、ReplicaSetが作成した全てのPodに対して追加されます。

Deploymentの更新

Deploymentを更新するには以下のステップに従ってください。

  1. nginxのPodで、nginx:1.14.2イメージの代わりにnginx:1.16.1を使うように更新します。

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    

    または単に次のコマンドを使用します。

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment image updated
    

    また、Deploymentを編集して、.spec.template.spec.containers[0].imagenginx:1.14.2からnginx:1.16.1に変更することができます。

    kubectl edit deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment edited
    
  2. ロールアウトのステータスを確認するには、以下のコマンドを実行してください。

    kubectl rollout status deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    

    もしくは

    deployment "nginx-deployment" successfully rolled out
    

更新されたDeploymentのさらなる情報を取得するには、以下を確認してください。

  • ロールアウトが成功したあと、kubectl get deploymentsを実行してDeploymentを確認できます。 実行結果は以下のとおりです。

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           36s
    
  • Deploymentが新しいReplicaSetを作成してPodを更新させたり、新しいReplicaSetのレプリカを3にスケールアップさせたり、古いReplicaSetのレプリカを0にスケールダウンさせるのを確認するにはkubectl get rsを実行してください。

    kubectl get rs
    

    実行結果は以下のとおりです。

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       6s
    nginx-deployment-2035384211   0         0         0       36s
    
  • get podsを実行させると、新しいPodのみ確認できます。

    kubectl get pods
    

    実行結果は以下のとおりです。

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1564180365-khku8   1/1       Running   0          14s
    nginx-deployment-1564180365-nacti   1/1       Running   0          14s
    nginx-deployment-1564180365-z9gth   1/1       Running   0          14s
    

    次にPodを更新させたいときは、DeploymentのPodテンプレートを再度更新するだけです。

    Deploymentは、Podが更新されている間に特定の数のPodのみ停止状態になることを保証します。デフォルトでは、目標とするPod数の少なくとも75%が稼働状態であることを保証します(25% max unavailable)。

    また、DeploymentはPodが更新されている間に、目標とするPod数を特定の数まで超えてPodを稼働させることを保証します。デフォルトでは、目標とするPod数に対して最大でも125%を超えてPodを稼働させることを保証します(25% max surge)。

    例えば、上記で説明したDeploymentの状態を注意深く見ると、最初に新しいPodが作成され、次に古いPodが削除されるのを確認できます。十分な数の新しいPodが稼働するまでは、Deploymentは古いPodを削除しません。また十分な数の古いPodが削除しない限り新しいPodは作成されません。少なくとも2つのPodが利用可能で、最大でもトータルで4つのPodが利用可能になっていることを保証します。

  • Deploymentの詳細情報を取得します。

    kubectl describe deployments
    

    実行結果は以下のとおりです。

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Thu, 30 Nov 2017 10:56:25 +0000
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=2
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
       Containers:
        nginx:
          Image:        nginx:1.16.1
          Port:         80/TCP
          Environment:  <none>
          Mounts:       <none>
        Volumes:        <none>
      Conditions:
        Type           Status  Reason
        ----           ------  ------
        Available      True    MinimumReplicasAvailable
        Progressing    True    NewReplicaSetAvailable
      OldReplicaSets:  <none>
      NewReplicaSet:   nginx-deployment-1564180365 (3/3 replicas created)
      Events:
        Type    Reason             Age   From                   Message
        ----    ------             ----  ----                   -------
        Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
        Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
        Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0
    

    最初にDeploymentを作成した時、ReplicaSet(nginx-deployment-2035384211)を作成してすぐにレプリカ数を3にスケールするのを確認できます。Deploymentを更新すると新しいReplicaSet(nginx-deployment-1564180365)を作成してレプリカ数を1にスケールアップし、古いReplicaSeetを2にスケールダウンさせます。これは常に最低でも2つのPodが利用可能で、かつ最大4つのPodが作成されている状態にするためです。Deploymentは同じローリングアップ戦略に従って新しいReplicaSetのスケールアップと古いReplicaSetのスケールダウンを続けます。最終的に新しいReplicaSetを3にスケールアップさせ、古いReplicaSetを0にスケールダウンさせます。

ロールオーバー (リアルタイムでの複数のPodの更新)

Deploymentコントローラーにより、新しいDeploymentが観測される度にReplicaSetが作成され、理想とするレプリカ数のPodを作成します。Deploymentが更新されると、既存のReplicaSetが管理するPodのラベルが.spec.selectorにマッチするが、テンプレートが.spec.templateにマッチしない場合はスケールダウンされます。最終的に、新しいReplicaSetは.spec.replicasの値にスケールアップされ、古いReplicaSetは0にスケールダウンされます。

Deploymentのロールアウトが進行中にDeploymentを更新すると、Deploymentは更新する毎に新しいReplicaSetを作成してスケールアップさせ、以前にスケールアップしたReplicaSetのロールオーバーを行います。Deploymentは更新前のReplicaSetを古いReplicaSetのリストに追加し、スケールダウンを開始します。

例えば、5つのレプリカを持つnginx:1.14.2のDeploymentを作成し、nginx:1.14.2の3つのレプリカが作成されているときに5つのレプリカを持つnginx:1.16.1に更新します。このケースではDeploymentは作成済みのnginx:1.14.2の3つのPodをすぐに削除し、nginx:1.16.1のPodの作成を開始します。nginx:1.14.2の5つのレプリカを全て作成するのを待つことはありません。

ラベルセレクターの更新

通常、ラベルセレクターを更新することは推奨されません。事前にラベルセレクターの使い方を計画しておきましょう。いかなる場合であっても更新が必要なときは十分に注意を払い、変更時の影響範囲を把握しておきましょう。

  • セレクターの追加は、Deployment Specのテンプレートラベルも新しいラベルで更新する必要があります。そうでない場合はバリデーションエラーが返されます。この変更は重複がない更新となります。これは新しいセレクターは古いセレクターを持つReplicaSetとPodを選択せず、結果として古い全てのReplicaSetがみなし子状態になり、新しいReplicaSetを作成することを意味します。
  • セレクターの更新により、セレクターキー内の既存の値が変更されます。これにより、セレクターの追加と同じふるまいをします。
  • セレクターの削除により、Deploymentのセレクターから存在している値を削除します。これはPodテンプレートのラベルに関する変更を要求しません。既存のReplicaSetはみなし子状態にならず、新しいReplicaSetは作成されませんが、削除されたラベルは既存のPodとReplicaSetでは残り続けます。

Deploymentのロールバック

例えば、クラッシュループ状態などのようにDeploymentが不安定な場合においては、Deploymentをロールバックしたくなることがあります。Deploymentの全てのロールアウト履歴は、いつでもロールバックできるようにデフォルトでシステムに保持されています(リビジョン履歴の上限は設定することで変更可能です)。

  • nginx:1.16.1の代わりにnginx:1.161というイメージに更新して、Deploymentの更新中にタイプミスをしたと仮定します。

    kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment image updated
    
  • このロールアウトはうまくいきません。ロールアウトのステータスを見るとそれを確認できます。

    kubectl rollout status deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
    
  • ロールアウトのステータスの確認は、Ctrl-Cを押すことで停止できます。ロールアウトがうまく行かないときは、Deploymentのステータスを読んでさらなる情報を得てください。

  • 古いレプリカ数(nginx-deployment-1564180365 and nginx-deployment-2035384211)が2になっていることを確認でき、新しいレプリカ数(nginx-deployment-3066724191)は1になっています。

    kubectl get rs
    

    実行結果は以下のとおりです。

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       25s
    nginx-deployment-2035384211   0         0         0       36s
    nginx-deployment-3066724191   1         1         0       6s
    
  • 作成されたPodを確認していると、新しいReplicaSetによって作成された1つのPodはコンテナイメージのpullに失敗し続けているのがわかります。

    kubectl get pods
    

    実行結果は以下のとおりです。

    NAME                                READY     STATUS             RESTARTS   AGE
    nginx-deployment-1564180365-70iae   1/1       Running            0          25s
    nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
    nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
    nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s
    
  • Deploymentの詳細情報を取得します。

    kubectl describe deployment
    

    実行結果は以下のとおりです。

    Name:           nginx-deployment
    Namespace:      default
    CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
    Labels:         app=nginx
    Selector:       app=nginx
    Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
    StrategyType:       RollingUpdate
    MinReadySeconds:    0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.161
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:     nginx-deployment-1564180365 (3/3 replicas created)
    NewReplicaSet:      nginx-deployment-3066724191 (1/1 replicas created)
    Events:
      FirstSeen LastSeen    Count   From                    SubObjectPath   Type        Reason              Message
      --------- --------    -----   ----                    -------------   --------    ------              -------
      1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 1
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
    

    これを修正するために、Deploymentを安定した状態の過去のリビジョンに更新する必要があります。

Deploymentのロールアウト履歴の確認

ロールアウトの履歴を確認するには、以下の手順に従って下さい。

  1. 最初に、Deploymentのリビジョンを確認します。

    kubectl rollout history deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml
    2           kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    3           kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.161
    

    CHANGE-CAUSEはリビジョンの作成時にDeploymentのkubernetes.io/change-causeアノテーションからリビジョンにコピーされます。以下の方法によりCHANGE-CAUSEメッセージを指定できます。

    • kubectl annotate deployment.v1.apps/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1"の実行によりアノテーションを追加します。
    • リソースのマニフェストを手動で編集します。
  2. 各リビジョンの詳細を確認するためには以下のコマンドを実行してください。

    kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
    

    実行結果は以下のとおりです。

    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Annotations:  kubernetes.io/change-cause=kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
      Containers:
       nginx:
        Image:      nginx:1.16.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

過去のリビジョンにロールバックする

現在のリビジョンから過去のリビジョン(リビジョン番号2)にロールバックさせるには、以下の手順に従ってください。

  1. 現在のリビジョンから過去のリビジョンにロールバックします。

    kubectl rollout undo deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment rolled back
    

    その他に、--to-revisionを指定することにより特定のリビジョンにロールバックできます。

    kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment rolled back
    

    ロールアウトに関連したコマンドのさらなる情報はkubectl rolloutを参照してください。

    Deploymentが過去の安定したリビジョンにロールバックされました。Deploymentコントローラーによって、リビジョン番号2にロールバックするDeploymentRollbackイベントが作成されたのを確認できます。

  2. ロールバックが成功し、Deploymentが正常に稼働していることを確認するために、以下のコマンドを実行してください。

    kubectl get deployment nginx-deployment
    

    実行結果は以下のとおりです。

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           30m
    
  3. Deploymentの詳細情報を取得します。

    kubectl describe deployment nginx-deployment
    

    実行結果は以下のとおりです。

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Sun, 02 Sep 2018 18:17:55 -0500
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=4
                            kubernetes.io/change-cause=kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.16.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
    Events:
      Type    Reason              Age   From                   Message
      ----    ------              ----  ----                   -------
      Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
      Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
      Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0
    

Deploymentのスケーリング

以下のコマンドを実行させてDeploymentをスケールできます。

kubectl scale deployment.v1.apps/nginx-deployment --replicas=10

実行結果は以下のとおりです。

deployment.apps/nginx-deployment scaled

クラスター内で水平Podオートスケーラーが有効になっていると仮定します。ここでDeploymentのオートスケーラーを設定し、稼働しているPodのCPU使用量に基づいて、稼働させたいPodのレプリカ数の最小値と最大値を設定できます。

kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80

実行結果は以下のとおりです。

deployment.apps/nginx-deployment scaled

比例スケーリング

Deploymentのローリングアップデートは、同時に複数のバージョンのアプリケーションの稼働をサポートします。ユーザーやオートスケーラーがローリングアップデートをロールアウト中(更新中もしくは一時停止中)のDeploymentに対して行うと、Deploymentコントローラーはリスクを削減するために既存のアクティブなReplicaSetのレプリカのバランシングを行います。これを比例スケーリング と呼びます。

レプリカ数が10、maxSurge=3、maxUnavailable=2であるDeploymentが稼働している例です。

  • Deployment内で10のレプリカが稼働していることを確認します。

    kubectl get deploy
    

    実行結果は以下のとおりです。

    NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment     10        10        10           10          50s
    
  • クラスター内で、解決できない新しいイメージに更新します。

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:sometag
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment image updated
    
  • イメージの更新は新しいReplicaSet nginx-deployment-1989198191へのロールアウトを開始させます。しかしロールアウトは、上述したmaxUnavailableの要求によりブロックされます。ここでロールアウトのステータスを確認します。

    kubectl get rs
    

    実行結果は以下のとおりです。

    NAME                          DESIRED   CURRENT   READY     AGE
    nginx-deployment-1989198191   5         5         0         9s
    nginx-deployment-618515232    8         8         8         1m
    
  • 次にDeploymentのスケーリングをするための新しい要求が発生します。オートスケーラーはDeploymentのレプリカ数を15に増やします。Deploymentコントローラーは新しい5つのレプリカをどこに追加するか決める必要がでてきます。比例スケーリングを使用していない場合、5つのレプリカは全て新しいReplicaSetに追加されます。比例スケーリングでは、追加されるレプリカは全てのReplicaSetに分散されます。比例割合が大きいものはレプリカ数の大きいReplicaSetとなり、比例割合が低いときはレプリカ数の小さいReplicaSetとなります。残っているレプリカはもっとも大きいレプリカ数を持つReplicaSetに追加されます。レプリカ数が0のReplicaSetはスケールアップされません。

上記の例では、3つのレプリカが古いReplicaSetに追加され、2つのレプリカが新しいReplicaSetに追加されました。ロールアウトの処理では、新しいレプリカ数のPodが正常になったと仮定すると、最終的に新しいReplicaSetに全てのレプリカを移動させます。これを確認するためには以下のコマンドを実行して下さい。

kubectl get deploy

実行結果は以下のとおりです。

NAME                 DESIRED   CURRENT   UP-TO-DATE  AVAILABLE   AGE
nginx-deployment     15        18        7           8           7m

ロールアウトのステータスでレプリカがどのように各ReplicaSetに追加されるか確認できます。

kubectl get rs

実行結果は以下のとおりです。

NAME                          DESIRED   CURRENT  READY     AGE
nginx-deployment-1989198191   7         7        0         7m
nginx-deployment-618515232    11        11       11        7m

Deployment更新の一時停止と再開

ユーザーは1つ以上の更新処理をトリガーする前に更新の一時停止と再開ができます。これにより、不必要なロールアウトを実行することなく一時停止と再開を行う間に複数の修正を反映できます。

  • 例えば、作成直後のDeploymentを考えます。 Deploymentの詳細情報を確認します。

    kubectl get deploy
    

    実行結果は以下のとおりです。

    NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx     3         3         3            3           1m
    

    ロールアウトのステータスを確認します。

    kubectl get rs
    

    実行結果は以下のとおりです。

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         1m
    
  • 以下のコマンドを実行して更新処理の一時停止を行います。

    kubectl rollout pause deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment paused
    
  • 次にDeploymentのイメージを更新します。

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment image updated
    
  • 新しいロールアウトが開始されていないことを確認します。

    kubectl rollout history deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    deployments "nginx"
    REVISION  CHANGE-CAUSE
    1   <none>
    
  • Deploymentの更新に成功したことを確認するためにロールアウトのステータスを確認します。

    kubectl get rs
    

    実行結果は以下のとおりです。

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         2m
    
  • 更新は何度でも実行できます。例えば、Deploymentが使用するリソースを更新します。

    kubectl set resources deployment.v1.apps/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment resource requirements updated
    

    一時停止する前の初期状態では更新処理は機能しますが、Deploymentが一時停止されている間は新しい更新処理は反映されません。

  • 最後に、Deploymentの稼働を再開させ、新しいReplicaSetが更新内容を全て反映させているのを確認します。

    kubectl rollout resume deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment resumed
    
  • 更新処理が完了するまでロールアウトのステータスを確認します。

    kubectl get rs -w
    

    実行結果は以下のとおりです。

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   2         2         2         2m
    nginx-3926361531   2         2         0         6s
    nginx-3926361531   2         2         1         18s
    nginx-2142116321   1         2         2         2m
    nginx-2142116321   1         2         2         2m
    nginx-3926361531   3         2         1         18s
    nginx-3926361531   3         2         1         18s
    nginx-2142116321   1         1         1         2m
    nginx-3926361531   3         3         1         18s
    nginx-3926361531   3         3         2         19s
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         20s
    
  • 最新のロールアウトのステータスを確認します。

    kubectl get rs
    

    実行結果は以下のとおりです。

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         28s
    

Deploymentのステータス

Deploymentは、そのライフサイクルの間に様々な状態に遷移します。新しいReplicaSetへのロールアウト中は進行中になり、その後は完了し、また失敗にもなります。

Deploymentの更新処理

以下のタスクが実行中のとき、KubernetesはDeploymentの状態を 進行中 にします。

  • Deploymentが新しいReplicaSetを作成します。
  • Deploymentが新しいReplicaSetをスケールアップさせています。
  • Deploymentが古いReplicaSetをスケールダウンさせています。
  • 新しいPodが準備中もしくは利用可能な状態になります(少なくともMinReadySecondsの間は準備中になります)。

kubectl rollout statusを実行すると、Deploymentの進行状態を確認できます。

Deploymentの更新処理の完了

Deploymentが以下の状態になったとき、KubernetesはDeploymentのステータスを 完了 にします。

  • Deploymentの全てのレプリカが、指定された最新のバージョンに更新されます。これは指定した更新処理が完了したことを意味します。
  • Deploymentの全てのレプリカが利用可能になります。
  • Deploymentの古いレプリカが1つも稼働していません。

kubectl rollout statusを実行して、Deploymentの更新が完了したことを確認できます。ロールアウトが正常に完了するとkubectl rollout statusの終了コードが0で返されます。

kubectl rollout status deployment.v1.apps/nginx-deployment

実行結果は以下のとおりです。

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out

そしてkubectl rolloutの終了ステータスが0となります(成功です):

echo $?
0

Deploymentの更新処理の失敗

新しいReplicaSetのデプロイが完了せず、更新処理が止まる場合があります。これは主に以下の要因によるものです。

  • 不十分なリソースの割り当て
  • ReadinessProbeの失敗
  • コンテナイメージの取得ができない
  • 不十分なパーミッション
  • リソースリミットのレンジ
  • アプリケーションランタイムの設定の不備

このような状況を検知する1つの方法として、Deploymentのリソース定義でデッドラインのパラメーターを指定します(.spec.progressDeadlineSeconds)。.spec.progressDeadlineSecondsはDeploymentの更新が停止したことを示す前にDeploymentコントローラーが待つ秒数を示します。

以下のkubectlコマンドでリソース定義にprogressDeadlineSecondsを設定します。これはDeploymentの更新が止まってから10分後に、コントローラーが失敗を通知させるためです。

kubectl patch deployment.v1.apps/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

実行結果は以下のとおりです。

deployment.apps/nginx-deployment patched

一度デッドラインを超過すると、DeploymentコントローラーはDeploymentの.status.conditionsに以下のDeploymentConditionを追加します。

  • Type=Progressing
  • Status=False
  • Reason=ProgressDeadlineExceeded

ステータスの状態に関するさらなる情報はKubernetes APIの規則を参照してください。

設定したタイムアウトの秒数が小さかったり、一時的なエラーとして扱える他の種類のエラーが原因となり、Deploymentで一時的なエラーが出る場合があります。例えば、リソースの割り当てが不十分な場合を考えます。Deploymentの詳細情報を確認すると、以下のセクションが表示されます。

kubectl describe deployment nginx-deployment

実行結果は以下のとおりです。

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

kubectl get deployment nginx-deployment -o yamlを実行すると、Deploymentのステータスは以下のようになります。

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

最後に、一度Deploymentの更新処理のデッドラインを越えると、KubernetesはDeploymentのステータスと進行中の状態を更新します。

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

Deploymentか他のリソースコントローラーのスケールダウンを行うか、使用している名前空間内でリソースの割り当てを増やすことで、リソースの割り当て不足の問題に対処できます。割り当て条件を満たすと、DeploymentコントローラーはDeploymentのロールアウトを完了させ、Deploymentのステータスが成功状態になるのを確認できます(Status=TrueReason=NewReplicaSetAvailable)。

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

Status=TrueType=Availableは、Deploymentが最小可用性の状態であることを意味します。最小可用性は、Deploymentの更新戦略において指定されているパラメーターにより決定されます。Status=TrueType=Progressingは、Deploymentのロールアウトの途中で、更新処理が進行中であるか、更新処理が完了し、必要な最小数のレプリカが利用可能であることを意味します(各TypeのReason項目を確認してください。このケースでは、Reason=NewReplicaSetAvailableはDeploymentの更新が完了したことを意味します)。

kubectl rollout statusを実行してDeploymentが更新に失敗したかどうかを確認できます。kubectl rollout statusはDeploymentが更新処理のデッドラインを超えたときに0以外の終了コードを返します。

kubectl rollout status deployment.v1.apps/nginx-deployment

実行結果は以下のとおりです。

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

そしてkubectl rolloutの終了ステータスが1となります(エラーを示しています):

echo $?
1

失敗したDeploymentの操作

更新完了したDeploymentに適用した全てのアクションは、更新失敗したDeploymentに対しても適用されます。スケールアップ、スケールダウンができ、前のリビジョンへのロールバックや、Deploymentのテンプレートに複数の更新を適用させる必要があるときは一時停止もできます。

古いリビジョンのクリーンアップポリシー

Deploymentが管理する古いReplicaSetをいくつ保持するかを指定するために、.spec.revisionHistoryLimitフィールドを設定できます。この値を超えた古いReplicaSetはバックグラウンドでガーベージコレクションの対象となって削除されます。デフォルトではこの値は10です。

カナリアパターンによるデプロイ

Deploymentを使って一部のユーザーやサーバーに対してリリースのロールアウトをしたい場合、リソースの管理に記載されているカナリアパターンに従って、リリース毎に1つずつ、複数のDeploymentを作成できます。

Deployment Specの記述

他の全てのKubernetesの設定と同様に、Deploymentは.apiVersion.kind.metadataフィールドを必要とします。 設定ファイルの利用に関する情報はアプリケーションのデプロイを参照してください。コンテナの設定に関してはリソースを管理するためのkubectlの使用を参照してください。 Deploymentオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。 Deploymentは.specセクションも必要とします。

Podテンプレート

.spec.template.spec.selector.specにおける必須のフィールドです。

.spec.templatePodテンプレートです。これは.spec内でネストされていないことと、apiVersionkindを持たないことを除いてはPodと同じスキーマとなります。

Podの必須フィールドに加えて、Deployment内のPodテンプレートでは適切なラベルと再起動ポリシーを設定しなくてはなりません。ラベルは他のコントローラーと重複しないようにしてください。ラベルについては、セレクターを参照してください。

.spec.template.spec.restartPolicyAlwaysに等しいときのみ許可されます。これはテンプレートで指定されていない場合のデフォルト値です。

レプリカ数

.spec.repliasは理想的なPodの数を指定するオプションのフィールドです。デフォルトは1です。

セレクター

.spec.selectorは必須フィールドで、Deploymentによって対象とされるPodのラベルセレクターを指定します。

.spec.selector.spec.template.metadata.labelsと一致している必要があり、一致しない場合はAPIによって拒否されます。

apps/v1バージョンにおいて、.spec.selector.metadata.labelsが指定されていない場合、.spec.template.metadata.labelsの値に初期化されません。そのため.spec.selector.metadata.labelsを明示的に指定する必要があります。またapps/v1のDeploymentにおいて.spec.selectorは作成後に不変になります。

Deploymentのテンプレートが.spec.templateと異なる場合や、.spec.replicasの値を超えてPodが稼働している場合、Deploymentはセレクターに一致するラベルを持つPodを削除します。Podの数が理想状態より少ない場合Deploymentは.spec.templateをもとに新しいPodを作成します。

セレクターが重複する複数のコントローラーを持つとき、そのコントローラーは互いに競合状態となり、正しくふるまいません。

更新戦略

.spec.strategyは古いPodから新しいPodに置き換える際の更新戦略を指定します。.spec.strategy.typeは"Recreate"もしくは"RollingUpdate"を指定できます。デフォルトは"RollingUpdate"です。

Deploymentの再作成

.spec.strategy.type==Recreateと指定されているとき、既存の全てのPodは新しいPodが作成される前に削除されます。

Deploymentのローリングアップデート

.spec.strategy.type==RollingUpdateと指定されているとき、DeploymentはローリングアップデートによりPodを更新します。ローリングアップデートの処理をコントロールするためにmaxUnavailablemaxSurgeを指定できます。

Max Unavailable

.spec.strategy.rollingUpdate.maxUnavailableはオプションのフィールドで、更新処理において利用不可となる最大のPod数を指定します。値は絶対値(例: 5)を指定するか、理想状態のPodのパーセンテージを指定します(例: 10%)。パーセンテージを指定した場合、絶対値は小数切り捨てされて計算されます。.spec.strategy.rollingUpdate.maxSurgeが0に指定されている場合、この値を0にできません。デフォルトでは25%です。

例えば、この値が30%と指定されているとき、ローリングアップデートが開始すると古いReplicaSetはすぐに理想状態の70%にスケールダウンされます。一度新しいPodが稼働できる状態になると、古いReplicaSetはさらにスケールダウンされ、続いて新しいReplicaSetがスケールアップされます。この間、利用可能なPodの総数は理想状態のPodの少なくとも70%以上になるように保証されます。

Max Surge

.spec.strategy.rollingUpdate.maxSurgeはオプションのフィールドで、理想状態のPod数を超えて作成できる最大のPod数を指定します。値は絶対値(例: 5)を指定するか、理想状態のPodのパーセンテージを指定します(例: 10%)。パーセンテージを指定した場合、絶対値は小数切り上げで計算されます。MaxUnavailableが0に指定されている場合、この値を0にできません。デフォルトでは25%です。

例えば、この値が30%と指定されているとき、ローリングアップデートが開始すると新しいReplicaSetはすぐに更新されます。このとき古いPodと新しいPodの総数は理想状態の130%を超えないように更新されます。一度古いPodが削除されると、新しいReplicaSetはさらにスケールアップされます。この間、利用可能なPodの総数は理想状態のPodに対して最大130%になるように保証されます。

Progress Deadline Seconds

.spec.progressDeadlineSecondsはオプションのフィールドで、システムがDeploymentの更新に失敗したと判断するまでに待つ秒数を指定します。更新に失敗したと判断されたとき、リソースのステータスはType=ProgressingStatus=FalseかつReason=ProgressDeadlineExceededとなるのを確認できます。DeploymentコントローラーはDeploymentの更新のリトライし続けます。デフォルト値は600です。今後、自動的なロールバックが実装されたとき、更新失敗状態になるとすぐにDeploymentコントローラーがロールバックを行うようになります。

この値が指定されているとき、.spec.minReadySecondsより大きい値を指定する必要があります。

Min Ready Seconds

.spec.minReadySecondsはオプションのフィールドで、新しく作成されたPodが利用可能となるために、最低どれくらいの秒数コンテナがクラッシュすることなく稼働し続ければよいかを指定するものです。デフォルトでは0です(Podは作成されるとすぐに利用可能と判断されます)。Podが利用可能と判断された場合についてさらに学ぶためにContainer Probesを参照してください。

リビジョン履歴の保持上限

Deploymentのリビジョン履歴は、Deploymentが管理するReplicaSetに保持されています。

.spec.revisionHistoryLimitはオプションのフィールドで、ロールバック可能な古いReplicaSetの数を指定します。この古いReplicaSetはetcd内のリソースを消費し、kubectl get rsの出力結果を見にくくします。Deploymentの各リビジョンの設定はReplicaSetに保持されます。このため一度古いReplicaSetが削除されると、そのリビジョンのDeploymentにロールバックすることができなくなります。デフォルトでは10もの古いReplicaSetが保持されます。しかし、この値の最適値は新しいDeploymentの更新頻度と安定性に依存します。

さらに詳しく言うと、この値を0にすると、0のレプリカを持つ古い全てのReplicaSetが削除されます。このケースでは、リビジョン履歴が完全に削除されているため新しいDeploymentのロールアウトを元に戻すことができません。

paused

.spec.pausedはオプションのboolean値で、Deploymentの一時停止と再開のための値です。一時停止されているものと、そうでないものとの違いは、一時停止されているDeploymentはPodTemplateSpecのいかなる変更があってもロールアウトがトリガーされないことです。デフォルトではDeploymentは一時停止していない状態で作成されます。

3.4.2.2 - ReplicaSet

ReplicaSetの目的は、どのような時でも安定したレプリカPodのセットを維持することです。これは、理想的なレプリカ数のPodが利用可能であることを保証するものとして使用されます。

ReplicaSetがどのように動くか

ReplicaSetは、ReplicaSetが対象とするPodをどう特定するかを示すためのセレクターや、稼働させたいPodのレプリカ数、Podテンプレート(理想のレプリカ数の条件を満たすために作成される新しいPodのデータを指定するために用意されるもの)といったフィールドとともに定義されます。ReplicaSetは、指定された理想のレプリカ数にするためにPodの作成と削除を行うことにより、その目的を達成します。ReplicaSetが新しいPodを作成するとき、ReplicaSetはそのPodテンプレートを使用します。

ReplicaSetがそのPod群と連携するためのリンクは、Podのmetadata.ownerReferencesというフィールド(現在のオブジェクトが所有されているリソースを指定する)を介して作成されます。ReplicaSetによって所持された全てのPodは、それらのownerReferencesフィールドにReplicaSetを特定する情報を保持します。このリンクを通じて、ReplicaSetは管理しているPodの状態を把握したり、その後の実行計画を立てます。

ReplicaSetは、そのセレクターを使用することにより、所有するための新しいPodを特定します。もしownerReferenceフィールドの値を持たないPodか、ownerReferenceフィールドの値が コントローラーでないPodで、そのPodがReplicaSetのセレクターとマッチした場合に、そのPodは即座にそのReplicaSetによって所有されます。

ReplicaSetを使うとき

ReplicaSetはどんな時でも指定された数のPodのレプリカが稼働することを保証します。しかし、DeploymentはReplicaSetを管理する、より上位レベルの概念で、Deploymentはその他の多くの有益な機能と共に、宣言的なPodのアップデート機能を提供します。それゆえ、我々はユーザーが独自のアップデートオーケストレーションを必要としたり、アップデートを全く必要としないような場合を除いて、ReplicaSetを直接使うよりも代わりにDeploymentを使うことを推奨します。

これは、ユーザーがReplicaSetのオブジェクトを操作する必要が全く無いことを意味します。 代わりにDeploymentを使用して、specセクションにユーザーのアプリケーションを定義してください。

ReplicaSetの使用例

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # ケースに応じてレプリカを修正する
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

上記のマニフェストをfrontend.yamlファイルに保存しKubernetesクラスターに適用すると、マニフェストに定義されたReplicaSetとそれが管理するPod群を作成します。

kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml

ユーザーはデプロイされた現在のReplicaSetの情報も取得できます。

kubectl get rs

そして、ユーザーが作成したfrontendリソースについての情報も取得できます。

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

ユーザーはまたReplicaSetの状態も確認できます。

kubectl describe rs/frontend

その結果は以下のようになります。

Name:		frontend
Namespace:	default
Selector:	tier=frontend
Labels:		app=guestbook
		tier=frontend
Annotations:	kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas:	3 current / 3 desired
Pods Status:	3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  tier=frontend
  Containers:
   php-redis:
    Image:        gcr.io/google_samples/gb-frontend:v3
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  117s  replicaset-controller  Created pod: frontend-wtsmm
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-b2zdv
  Normal  SuccessfulCreate  116s  replicaset-controller  Created pod: frontend-vcmts

そして最後に、ユーザーはReplicaSetによって作成されたPodもチェックできます。

kubectl get pods

表示されるPodに関する情報は以下のようになります。

NAME             READY   STATUS    RESTARTS   AGE
frontend-b2zdv   1/1     Running   0          6m36s
frontend-vcmts   1/1     Running   0          6m36s
frontend-wtsmm   1/1     Running   0          6m36s

ユーザーはまた、それらのPodのownerReferencesfrontendReplicaSetに設定されていることも確認できます。 これを確認するためには、稼働しているPodの中のどれかのyamlファイルを取得します。

kubectl get pods frontend-b2zdv -o yaml

その表示結果は、以下のようになります。そのfrontendReplicaSetの情報がmetadataownerReferencesフィールドにセットされています。

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-02-12T07:06:16Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-b2zdv
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: f391f6db-bb9b-4c09-ae74-6a1f77f3d5cf
...

テンプレートなしのPodの所有

ユーザーが問題なくベアPod(Bare Pod: ここではPodテンプレート無しのPodのこと)を作成しているとき、そのベアPodがユーザーのReplicaSetの中のいずれのセレクターともマッチしないことを確認することを強く推奨します。 この理由として、ReplicaSetは、所有対象のPodがReplicaSetのテンプレートによって指定されたPodのみに限定されていないからです(ReplicaSetは前のセクションで説明した方法によって他のPodも所有できます)。

前のセクションで取り上げたfrontendReplicaSetと、下記のマニフェストのPodをみてみます。

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    tier: frontend
spec:
  containers:
  - name: hello1
    image: gcr.io/google-samples/hello-app:2.0

---

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    tier: frontend
spec:
  containers:
  - name: hello2
    image: gcr.io/google-samples/hello-app:1.0

これらのPodはownerReferencesに何のコントローラー(もしくはオブジェクト)も指定されておらず、そしてfrontendReplicaSetにマッチするセレクターをもっており、これらのPodは即座にfrontendReplicaSetによって所有されます。

このfrontendReplicaSetがデプロイされ、初期のPodレプリカがレプリカ数の要求を満たすためにセットアップされた後で、ユーザーがそのPodを作成することを考えます。

kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml

新しいPodはそのReplicaSetによって所有され、そのReplicaSetのレプリカ数が、設定された理想のレプリカ数を超えた場合すぐにそれらのPodは削除されます。

下記のコマンドでPodを取得できます。

kubectl get pods

その表示結果で、新しいPodがすでに削除済みか、削除中のステータスになっているのを確認できます。

NAME             READY   STATUS        RESTARTS   AGE
frontend-b2zdv   1/1     Running       0          10m
frontend-vcmts   1/1     Running       0          10m
frontend-wtsmm   1/1     Running       0          10m
pod1             0/1     Terminating   0          1s
pod2             0/1     Terminating   0          1s

もしユーザーがそのPodを最初に作成する場合

kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml

そしてその後にfrontendReplicaSetを作成すると、

kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml

ユーザーはそのReplicaSetが作成したPodを所有し、さらにもともと存在していたPodと今回新たに作成されたPodの数が、理想のレプリカ数になるまでPodを作成するのを確認できます。 ここでまたPodの状態を取得します。

kubectl get pods

取得結果は下記のようになります。

NAME             READY   STATUS    RESTARTS   AGE
frontend-hmmj2   1/1     Running   0          9s
pod1             1/1     Running   0          36s
pod2             1/1     Running   0          36s

この方法で、ReplicaSetはテンプレートで指定されたもの以外のPodを所有することができます。

ReplicaSetのマニフェストを記述する。

他の全てのKubernetes APIオブジェクトのように、ReplicaSetはapiVersionkindmetadataフィールドを必要とします。 ReplicaSetでは、kindフィールドの値はReplicaSetです。

ReplicaSetオブジェクトの名前は、有効な DNSサブドメイン名である必要があります。

また、ReplicaSetは.spec セクションも必須です。

Pod テンプレート

.spec.templateはラベルを持つことが必要なPodテンプレート です。先ほど作成したfrontend.yamlの例では、tier: frontendというラベルを1つ持っています。 他のコントローラーがこのPodを所有しようとしないためにも、他のコントローラーのセレクターでラベルを上書きしないように注意してください。

テンプレートの再起動ポリシーのためのフィールドである.spec.template.spec.restartPolicyAlwaysのみ許可されていて、そしてそれがデフォルト値です。

Pod セレクター

.spec.selectorフィールドはラベルセレクターです。 先ほど議論したように、ReplicaSetが所有するPodを指定するためにそのラベルが使用されます。 先ほどのfrontend.yamlの例では、そのセレクターは下記のようになっていました

matchLabels:
  tier: frontend

そのReplicaSetにおいて、.spec.template.metadata.labelsフィールドの値はspec.selectorと一致しなくてはならず、一致しない場合はAPIによって拒否されます。

レプリカ数について

ユーザーは.spec.replicasフィールドの値を設定することにより、いくつのPodを同時に稼働させるか指定できます。そのときReplicaSetはレプリカ数がこの値に達するまでPodを作成、または削除します。

もしユーザーが.spec.replicasを指定しない場合、デフォルト値として1がセットされます。

ReplicaSetを利用する

ReplicaSetとPodの削除

ReplicaSetとそれが所有する全てのPod削除したいときは、kubectl deleteコマンドを使ってください。
ガベージコレクターがデフォルトで自動的に全ての依存するPodを削除します。

REST APIもしくはclient-goライブラリーを使用するとき、ユーザーは-dオプションでpropagationPolicyBackgroundForegroundと指定しなくてはなりません。例えば下記のように実行します。

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"

ReplicaSetのみを削除する

ユーザーはkubectl deleteコマンドで--cascade=falseオプションを付けることにより、所有するPodに影響を与えることなくReplicaSetを削除できます。 REST APIもしくはclient-goライブラリーを使用するとき、ユーザーは-dオプションでpropagationPolicyOrphanと指定しなくてはなりません。 例えば下記のように実行します:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"

一度元のReplicaSetが削除されると、ユーザーは新しいものに置き換えるため新しいReplicaSetを作ることができます。新旧のReplicaSetの.spec.selectorの値が同じである間、新しいReplicaSetは古いReplicaSetで稼働していたPodを取り入れます。 しかし、存在するPodが新しく異なるPodテンプレートとマッチさせようとするとき、この仕組みは機能しません。 ReplicaSetはローリングアップデートを直接サポートしないため、ユーザーのコントロール下においてPodを新しいspecにアップデートしたい場合は、Deploymentを使用してください。

PodをReplicaSetから分離させる

ユーザーはPodのラベルを変更することにより、ReplicaSetからそのPodを削除できます。この手法はデバッグや、データ修復などのためにサービスからPodを削除したいときに使用できます。 この方法で削除されたPodは自動的に新しいものに置き換えられます。(レプリカ数は変更されないものと仮定します。)

ReplicaSetのスケーリング

ReplicaSetは、ただ.spec.replicasフィールドを更新することによって簡単にスケールアップまたはスケールダウンできます。ReplicaSetコントローラーは、ラベルセレクターにマッチするような指定した数のPodが利用可能であり、操作可能であることを保証します。

スケールダウンする場合、ReplicaSetコントローラーは以下の一般的なアルゴリズムに基づき、利用可能なPodをソートし、スケールダウンするPodの優先順位を付け、削除するPodを選択します:

  1. 保留している(またはスケジュール不可な)Podが先にスケールダウンされます。
  2. controller.kubernetes.io/pod-deletion-costアノテーションが設定されている場合、値の小さいPodが優先されます。
  3. レプリカ数の多いノード上のPodが、レプリカ数の少ないノード上のPodより優先されます。
  4. Podの作成時間が異なる場合、より新しく作成されたPodが古いPodより優先されます(LogarithmicScaleDownフィーチャーゲートが有効の場合、作成時間は整数対数スケールでバケット化されます)。

上記条件のすべてに該当する場合は、ランダム選択となります。

Pod削除コスト

FEATURE STATE: Kubernetes v1.22 [beta]

controller.kubernetes.io/pod-deletion-costアノテーションを使用すると、ReplicaSetをスケールダウンする際に、どのPodを最初に削除するかについて、ユーザーが優先順位を設定することができます。

アノテーションはPodに設定する必要があり、範囲は[-2147483648, 2147483647]になります。同じReplicaSetに属する他のPodと比較して、Podを削除する際のコストを表しています。削除コストの低いPodは、削除コストの高いPodより優先的に削除されます。

このアノテーションを設定しないPodは暗黙的に0と設定され、負の値は許容されます。 無効な値はAPIサーバーによって拒否されます。

この機能はbeta版で、デフォルトで有効になっています。kube-apiserverとkube-controller-managerでフィーチャーゲートPodDeletionCostを設定することで無効にすることができます。

使用事例

アプリケーションの異なるPodは、異なる使用レベルになる可能性があります。スケールダウンする場合、アプリケーションは使用率の低いPodを削除することを優先しています。Podを頻繁に更新することを避けるため、アプリケーションはスケールダウンする前に一度controller.kubernetes.io/pod-deletion-costを更新する必要があります(アノテーションをPod使用レベルに比例する値に設定します)。Spark DeploymentのドライバーPodのように、アプリケーション自体がスケールダウンを制御する場合も機能します。

HorizontalPodAutoscaler(HPA)のターゲットとしてのReplicaSet

ReplicaSetはまた、Horizontal Pod Autoscalers (HPA)のターゲットにもなることができます。 これはつまりReplicaSetがHPAによってオートスケールされうることを意味します。 ここではHPAが、前の例で作成したReplicaSetをターゲットにする例を示します。

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

このマニフェストをhpa-rs.yamlに保存し、Kubernetesクラスターに適用すると、レプリケートされたPodのCPU使用量にもとづいてターゲットのReplicaSetをオートスケールするHPAを作成します。

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

同様のことを行うための代替案として、kubectl autoscaleコマンドも使用できます。(こちらの方がより簡単です。)

kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50

ReplicaSetの代替案

Deployment (推奨)

DeploymentはReplicaSetを所有することのできるオブジェクトで、宣言的なサーバサイドのローリングアップデートを介してReplicaSetとPodをアップデートできます。 ReplicaSetは単独で使用可能ですが、現在では、ReplicaSetは主にPodの作成、削除とアップデートを司るためのメカニズムとしてDeploymentによって使用されています。ユーザーがDeploymentを使用するとき、Deploymentによって作成されるReplicaSetの管理について心配する必要はありません。DeploymentはReplicaSetを所有し、管理します。 このため、もしユーザーがReplicaSetを必要とするとき、Deploymentの使用を推奨します。

ベアPod(Bare Pods)

ユーザーがPodを直接作成するケースとは異なり、ReplicaSetはNodeの故障やカーネルのアップグレードといった破壊的なNodeのメンテナンスなど、どのような理由に限らず削除または停止されたPodを置き換えます。 このため、我々はもしユーザーのアプリケーションが単一のPodのみ必要とする場合でもReplicaSetを使用することを推奨します。プロセスのスーパーバイザーについても同様に考えると、それは単一Node上での独立したプロセスの代わりに複数のNodeにまたがった複数のPodを監視します。 ReplicaSetは、KubeletのようなNode上のいくつかのエージェントに対して、ローカルのコンテナ再起動を移譲します。

Job

PodをPodそれ自身で停止させたいような場合(例えば、バッチ用のジョブなど)は、ReplicaSetの代わりにJobを使用してください。

DaemonSet

マシンの監視やロギングなど、マシンレベルの機能を提供したい場合は、ReplicaSetの代わりにDaemonSetを使用してください。 これらのPodはマシン自体のライフタイムに紐づいています: そのPodは他のPodが起動する前に、そのマシン上で稼働される必要があり、マシンが再起動またはシャットダウンされるときには、安全に停止されます。

ReplicationController

ReplicaSetはReplicationControllersの後継となるものです。 この2つは、ReplicationControllerがラベルについてのユーザーガイドに書かれているように、集合ベース(set-based)のセレクター要求をサポートしていないことを除いては、同じ目的を果たし、同じようにふるまいます。
このように、ReplicaSetはReplicationControllerよりも好まれます。

次の項目

3.4.2.3 - StatefulSet

StatefulSetはステートフルなアプリケーションを管理するためのワークロードAPIです。

StatefulSetはDeploymentとPodのセットのスケーリングを管理し、それらのPodの順序と一意性を保証 します。

Deploymentのように、StatefulSetは指定したコンテナのspecに基づいてPodを管理します。Deploymentとは異なり、StatefulSetは各Podにおいて管理が大変な同一性を維持します。これらのPodは同一のspecから作成されますが、それらは交換可能ではなく、リスケジュール処理をまたいで維持される永続的な識別子を持ちます。

ワークロードに永続性を持たせるためにストレージボリュームを使いたい場合は、解決策の1つとしてStatefulSetが利用できます。StatefulSet内の個々のPodは障害の影響を受けやすいですが、永続化したPodの識別子は既存のボリュームと障害によって置換された新しいPodの紐付けを簡単にします。

StatefulSetの使用

StatefulSetは下記の1つ以上の項目を要求するアプリケーションにおいて最適です。

  • 安定した一意のネットワーク識別子
  • 安定した永続ストレージ
  • 規則的で安全なデプロイとスケーリング
  • 規則的で自動化されたローリングアップデート

上記において安定とは、Podのスケジュール(または再スケジュール)をまたいでも永続的であることと同義です。 もしアプリケーションが安定したネットワーク識別子と規則的なデプロイや削除、スケーリングを全く要求しない場合、ユーザーはステートレスなレプリカのセットを提供するワークロードを使ってアプリケーションをデプロイするべきです。 DeploymentReplicaSetのようなコントローラーはこのようなステートレスな要求に対して最適です。

制限事項

  • 提供されたPodのストレージは、要求されたstorage classにもとづいてPersistentVolume Provisionerによってプロビジョンされるか、管理者によって事前にプロビジョンされなくてはなりません。
  • StatefulSetの削除もしくはスケールダウンをすることにより、StatefulSetに関連したボリュームは削除されません 。 これはデータ安全性のためで、関連するStatefulSetのリソース全てを自動的に削除するよりもたいてい有効です。
  • StatefulSetは現在、Podのネットワークアイデンティティーに責務をもつためにHeadless Serviceを要求します。ユーザーはこのServiceを作成する責任があります。
  • StatefulSetは、StatefulSetが削除されたときにPodの停止を行うことを保証していません。StatefulSetにおいて、規則的で安全なPodの停止を行う場合、削除のために事前にそのStatefulSetの数を0にスケールダウンさせることが可能です。
  • デフォルト設定のPod管理ポリシー (OrderedReady)によってローリングアップデートを行う場合、修復のための手動介入を要求するようなブロークンな状態に遷移させることが可能です。

コンポーネント

下記の例は、StatefulSetのコンポーネントのデモンストレーションとなります。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # .spec.template.metadata.labelsの値と一致する必要があります
  serviceName: "nginx"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # .spec.selector.matchLabelsの値と一致する必要があります
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

上記の例では、

  • nginxという名前のHeadlessServiceは、ネットワークドメインをコントロールするために使われます。
  • webという名前のStatefulSetは、specで3つのnginxコンテナのレプリカを持ち、そのコンテナはそれぞれ別のPodで稼働するように設定されています。
  • volumeClaimTemplatesは、PersistentVolumeプロビジョナーによってプロビジョンされたPersistentVolumeを使って安定したストレージを提供します。

StatefulSetの名前は有効な名前である必要があります。

Podセレクター

ユーザーは、StatefulSetの.spec.template.metadata.labelsのラベルと一致させるため、StatefulSetの.spec.selectorフィールドをセットしなくてはなりません。Kubernetes1.8以前では、.spec.selectorフィールドは省略された場合デフォルト値になります。Kubernetes1.8とそれ以降のバージョンでは、ラベルに一致するPodセレクターの指定がない場合はStatefulSetの作成時にバリデーションエラーになります。

Podアイデンティティー

StatefulSetのPodは、順番を示す番号、安定したネットワークアイデンティティー、安定したストレージからなる一意なアイデンティティーを持ちます。 そのアイデンティティーはどのNode上にスケジュール(もしくは再スケジュール)されるかに関わらず、そのPodに紐付きます。

順序インデックス

N個のレプリカをもったStatefulSetにおいて、StatefulSet内の各Podは、0からはじまりN-1までの整数値を順番に割り当てられ、そのStatefulSetにおいては一意となります。

安定したネットワークID

StatefulSet内の各Podは、そのStatefulSet名とPodの順序番号から派生してホストネームが割り当てられます。 作成されたホストネームの形式は$(StatefulSet名)-$(順序番号)となります。先ほどの上記の例では、web-0,web-1,web-2という3つのPodが作成されます。 StatefulSetは、PodのドメインをコントロールするためにHeadless Serviceを使うことができます。 このHeadless Serviceによって管理されたドメインは$(Service名).$(ネームスペース).svc.cluster.local形式となり、"cluster.local"というのはそのクラスターのドメインとなります。 各Podが作成されると、Podは$(Pod名).$(管理するServiceドメイン名)に一致するDNSサブドメインを取得し、管理するServiceはStatefulSetのserviceNameで定義されます。

クラスターでのDNSの設定方法によっては、新たに起動されたPodのDNS名をすぐに検索できない場合があります。 この動作は、クラスター内の他のクライアントが、Podが作成される前にそのPodのホスト名に対するクエリーをすでに送信していた場合に発生する可能性があります。 (DNSでは通常)ネガティブキャッシュは、Podの起動後でも、少なくとも数秒間、以前に失敗したルックアップの結果が記憶され、再利用されることを意味します。

Podが作成された後、速やかにPodを検出する必要がある場合は、いくつかのオプションがあります。

  • DNSルックアップに依存するのではなく、Kubernetes APIに直接(例えばwatchを使って)問い合わせる。
  • Kubernetes DNS プロバイダーのキャッシュ時間を短縮する(これは現在30秒キャッシュされるようになっているCoreDNSのConfigMapを編集することを意味しています。)。

制限事項セクションで言及したように、ユーザーはPodのネットワークアイデンティティーのためにHeadless Serviceを作成する責任があります。

ここで、クラスタードメイン、Service名、StatefulSet名の選択と、それらがStatefulSetのPodのDNS名にどう影響するかの例をあげます。

Cluster DomainService (ns/name)StatefulSet (ns/name)StatefulSet DomainPod DNSPod Hostname
cluster.localdefault/nginxdefault/webnginx.default.svc.cluster.localweb-{0..N-1}.nginx.default.svc.cluster.localweb-{0..N-1}
cluster.localfoo/nginxfoo/webnginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}
kube.localfoo/nginxfoo/webnginx.foo.svc.kube.localweb-{0..N-1}.nginx.foo.svc.kube.localweb-{0..N-1}

安定したストレージ

Kubernetesは各VolumeClaimTemplateに対して、1つのPersistentVolumeを作成します。上記のnginxの例において、各Podはmy-storage-classというStorageClassをもち、1GiBのストレージ容量を持った単一のPersistentVolumeを受け取ります。もしStorageClassが指定されていない場合、デフォルトのStorageClassが使用されます。PodがNode上にスケジュール(もしくは再スケジュール)されたとき、そのvolumeMountsはPersistentVolume Claimに関連したPersistentVolumeをマウントします。 注意点として、PodのPersistentVolume Claimと関連したPersistentVolumeは、PodやStatefulSetが削除されたときに削除されません。 削除する場合は手動で行わなければなりません。

Podのネームラベル

StatefulSet コントローラー がPodを作成したとき、Podの名前として、statefulset.kubernetes.io/pod-nameにラベルを追加します。このラベルによってユーザーはServiceにStatefulSet内の指定したPodを割り当てることができます。

デプロイとスケーリングの保証

  • N個のレプリカをもつStatefulSetにおいて、Podがデプロイされるとき、それらのPodは{0..N-1}の番号で順番に作成されます。
  • Podが削除されるとき、それらのPodは{N-1..0}の番号で降順に削除されます。
  • Podに対してスケーリングオプションが適用される前に、そのPodの前の順番の全てのPodがRunningかつReady状態になっていなくてはなりません。
  • Podが停止される前に、そのPodの番号より大きい番号を持つの全てのPodは完全にシャットダウンされていなくてはなりません。

StatefulSetはpod.Spec.TerminationGracePeriodSecondsを0に指定すべきではありません。これは不安全で、やらないことを強く推奨します。さらなる説明としては、StatefulSetのPodの強制削除を参照してください。

上記の例のnginxが作成されたとき、3つのPodはweb-0web-1web-2の順番でデプロイされます。web-1web-0RunningかつReady状態になるまでは決してデプロイされないのと、同様にweb-2web-1がRunningかつReady状態にならないとデプロイされません。もしweb-0web-1がRunningかつReady状態になった後だが、web-2が起動する前に失敗した場合、web-2web-0の再起動が成功し、RunningかつReady状態にならないと再起動されません。

もしユーザーがreplicas=1といったようにStatefulSetにパッチをあてることにより、デプロイされたものをスケールすることになった場合、web-2は最初に停止されます。web-1web-2が完全にシャットダウンされ削除されるまでは、停止されません。もしweb-0が、web-2が完全に停止され削除された後だが、web-1の停止の前に失敗した場合、web-1web-0がRunningかつReady状態になるまでは停止されません。

Podの管理ポリシー

Kubernetes1.7とそれ以降のバージョンでは、StatefulSetは.spec.podManagementPolicyフィールドを介して、Podの一意性とアイデンティティーを保証します。

OrderedReadyなPod管理

OrderedReadyなPod管理はStatefulSetにおいてデフォルトです。これはデプロイとスケーリングの保証に記載されている項目の振る舞いを実装します。

並行なPod管理

ParallelなPod管理は、StatefulSetコントローラーに対して、他のPodが起動や停止される前にそのPodが完全に起動し準備完了になるか停止するのを待つことなく、Podが並行に起動もしくは停止するように指示します。

アップデートストラテジー

Kubernetes1.7とそれ以降のバージョンにおいて、StatefulSetの.spec.updateStrategyフィールドで、コンテナの自動のローリングアップデートの設定やラベル、リソースのリクエストとリミットや、StatefulSet内のPodのアノテーションを指定できます。

OnDelete

OnDeleteというアップデートストラテジーは、レガシーな(Kubernetes1.6以前)振る舞いとなります。StatefulSetの.spec.updateStrategy.typeOnDeleteにセットされていたとき、そのStatefulSetコントローラーはStatefulSet内でPodを自動的に更新しません。StatefulSetの.spec.template項目の修正を反映した新しいPodの作成をコントローラーに支持するためには、ユーザーは手動でPodを削除しなければなりません。

RollingUpdate

RollingUpdateというアップデートストラテジーは、StatefulSet内のPodに対する自動化されたローリングアップデートの機能を実装します。これは.spec.updateStrategyフィールドが未指定の場合のデフォルトのストラテジーです。StatefulSetの.spec.updateStrategy.typeRollingUpdateにセットされたとき、そのStatefulSetコントローラーは、StatefulSet内のPodを削除し、再作成します。これはPodの停止(Podの番号の降順)と同じ順番で、一度に1つのPodを更新します。コントローラーは、その前のPodの状態がRunningかつReady状態になるまで次のPodの更新を待ちます。

パーティション

RollingUpdateというアップデートストラテジーは、.spec.updateStrategy.rollingUpdate.partitionを指定することにより、パーティションに分けることができます。もしパーティションが指定されていたとき、そのパーティションの値と等しいか、大きい番号を持つPodが更新されます。パーティションの値より小さい番号を持つPodは更新されず、たとえそれらのPodが削除されたとしても、それらのPodは以前のバージョンで再作成されます。もしStatefulSetの.spec.updateStrategy.rollingUpdate.partitionが、.spec.replicasより大きい場合、.spec.templateへの更新はPodに反映されません。 多くのケースの場合、ユーザーはパーティションを使う必要はありませんが、もし一部の更新を行う場合や、カナリー版のバージョンをロールアウトする場合や、段階的ロールアウトを行う場合に最適です。

強制ロールバック

デフォルトのPod管理ポリシー(OrderedReady)によるローリングアップデートを行う際、修復のために手作業が必要な状態にすることが可能です。

もしユーザーが、決してRunningかつReady状態にならないような設定になるようにPodテンプレートを更新した場合(例えば、不正なバイナリや、アプリケーションレベルの設定エラーなど)、StatefulSetはロールアウトを停止し、待機します。

この状態では、Podテンプレートを正常な状態に戻すだけでは不十分です。既知の問題によって、StatefulSetは元の正常な状態へ戻す前に、壊れたPodがReady状態(決して起こりえない)に戻るのを待ち続けます。

そのテンプレートを戻したあと、ユーザーはまたStatefulSetが異常状態で稼働しようとしていたPodをすべて削除する必要があります。StatefulSetはその戻されたテンプレートを使ってPodの再作成を始めます。

次の項目

3.4.2.4 - DaemonSet

DaemonSet は全て(またはいくつか)のNodeが単一のPodのコピーを稼働させることを保証します。Nodeがクラスターに追加されるとき、PodがNode上に追加されます。Nodeがクラスターから削除されたとき、それらのPodはガーベージコレクターにより除去されます。DaemonSetの削除により、DaemonSetが作成したPodもクリーンアップします。

DaemonSetのいくつかの典型的な使用例は以下の通りです。

  • クラスターのストレージデーモンを全てのNode上で稼働させる。
  • ログ集計デーモンを全てのNode上で稼働させる。
  • Nodeのモニタリングデーモンを全てのNode上で稼働させる。

シンプルなケースとして、各タイプのデーモンにおいて、全てのNodeをカバーする1つのDaemonSetが使用されるケースがあります。さらに複雑な設定では、単一のタイプのデーモン用ですが、異なるフラグや、異なるハードウェアタイプに対するメモリー、CPUリクエストを要求する複数のDaemonSetを使用するケースもあります。

DaemonSet Specの記述

DaemonSetの作成

ユーザーはYAMLファイル内でDaemonSetの設定を記述することができます。例えば、下記のdaemonset.yamlファイルではfluentd-elasticsearchというDockerイメージを稼働させるDaemonSetの設定を記述します。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

YAMLファイルに基づいてDaemonSetを作成します。

kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml

必須のフィールド

他の全てのKubernetesの設定と同様に、DaemonSetはapiVersionkindmetadataフィールドが必須となります。設定ファイルの活用法に関する一般的な情報は、ステートレスアプリケーションの稼働kubectlを用いたオブジェクトの管理といったドキュメントを参照ください。

DaemonSetオブジェクトの名前は、有効な DNSサブドメイン名である必要があります。

また、DaemonSetにおいて.specセクションも必須となります。

Podテンプレート

.spec.template.spec内での必須のフィールドの1つです。

.spec.templatePodテンプレートとなります。これはフィールドがネストされていて、apiVersionkindをもたないことを除いては、Podのテンプレートと同じスキーマとなります。

Podに対する必須のフィールドに加えて、DaemonSet内のPodテンプレートは適切なラベルを指定しなくてはなりません(Podセレクターの項目を参照ください)。

DaemonSet内のPodテンプレートでは、RestartPolicyフィールドを指定せずにデフォルトのAlwaysを使用するか、明示的にAlwaysを設定するかのどちらかである必要があります。

Podセレクター

.spec.selectorフィールドはPodセレクターとなります。これはJob.spec.selectorと同じものです。

ユーザーは.spec.templateのラベルにマッチするPodセレクターを指定しなくてはいけません。 また、一度DaemonSetが作成されると、その.spec.selectorは変更不可能になります。Podセレクターの変更は、意図しないPodの孤立を引き起こし、ユーザーにとってやっかいなものとなります。

.spec.selectorは2つのフィールドからなるオブジェクトです。

  • matchLabels - ReplicationController.spec.selectorと同じように機能します。
  • matchExpressions - キーと、値のリストとさらにはそれらのキーとバリューに関連したオペレーターを指定することにより、より洗練された形式のセレクターを構成できます。

上記の2つが指定された場合は、2つの条件をANDでどちらも満たすものを結果として返します。

spec.selector.spec.template.metadata.labelsとマッチしなければなりません。この2つの値がマッチしない設定をした場合、APIによってリジェクトされます。

選択したNode上でPodを稼働させる

もしユーザーが.spec.template.spec.nodeSelectorを指定したとき、DaemonSetコントローラーは、そのnode selectorにマッチするNode上にPodを作成します。同様に、もし.spec.template.spec.affinityを指定したとき、DaemonSetコントローラーはnode affinityにマッチするNode上にPodを作成します。 もしユーザーがどちらも指定しないとき、DaemonSetコントローラーは全てのNode上にPodを作成します。

Daemon Podがどのようにスケジューリングされるか

DaemonSetは、全ての利用可能なNodeがPodのコピーを稼働させることを保証します。DaemonSetコントローラーは対象となる各Nodeに対してPodを作成し、ターゲットホストに一致するようにPodのspec.affinity.nodeAffinityフィールドを追加します。Podが作成されると、通常はデフォルトのスケジューラーが引き継ぎ、.spec.nodeNameを設定することでPodをターゲットホストにバインドします。新しいNodeに適合できない場合、デフォルトスケジューラーは新しいPodの優先度に基づいて、既存Podのいくつかを先取り(退避)させることがあります。

ユーザーは、DaemonSetの.spec.template.spec.schedulerNameフィールドを設定することにより、DaemonSetのPodに対して異なるスケジューラーを指定することができます。

.spec.template.spec.affinity.nodeAffinityフィールド(指定された場合)で指定された元のNodeアフィニティは、DaemonSetコントローラーが対象Nodeを評価する際に考慮されますが、作成されたPod上では対象Nodeの名前と一致するNodeアフィニティに置き換わります。

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - target-host-name

TaintとToleration

DaemonSetコントローラーはDaemonSet Podに一連のTolerationを自動的に追加します:

Tolerations for DaemonSet pods
Toleration keyEffectDetails
node.kubernetes.io/not-readyNoExecute健康でないNodeや、Podを受け入れる準備ができていないNodeにDaemonSet Podをスケジュールできるように設定します。そのようなNode上で動作しているDaemonSet Podは退避されることがありません。
node.kubernetes.io/unreachableNoExecuteNodeコントローラーから到達できないNodeにDaemonSet Podをスケジュールできるように設定します。このようなNode上で動作しているDaemonSet Podは、退避されません。
node.kubernetes.io/disk-pressureNoScheduleディスク不足問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/memory-pressureNoScheduleメモリー不足問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/pid-pressureNoSchedule処理負荷に問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/unschedulableNoScheduleスケジューリング不可能なNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/network-unavailableNoScheduleホストネットワークを要求するDaemonSet Podにのみ追加できます、つまりspec.hostNetwork: trueと設定されているPodです。このようなDaemonSet Podは、ネットワークが利用できないNodeにスケジュールできるように設定します。

DaemonSetのPodテンプレートで定義すれば、DaemonSetのPodに独自のTolerationを追加することも可能です。

DaemonSetコントローラーはnode.kubernetes.io/unschedulable:NoScheduleのTolerationを自動的に設定するため、Kubernetesは スケジューリング不可能 としてマークされているNodeでDaemonSet Podを実行することが可能です。

クラスターのネットワークのような重要なNodeレベルの機能をDaemonSetで提供する場合、KubernetesがDaemonSet PodをNodeが準備完了になる前に配置することは有用です。 例えば、その特別なTolerationがなければ、ネットワークプラグインがそこで実行されていないためにNodeが準備完了としてマークされず、同時にNodeがまだ準備完了でないためにそのNode上でネットワークプラグインが実行されていないというデッドロック状態に陥ってしまう可能性があるのです。

Daemon Podとのコミュニケーション

DaemonSet内のPodとのコミュニケーションをする際に考えられるパターンは以下の通りです:

  • Push: DaemonSet内のPodは統計データベースなどの他のサービスに対して更新情報を送信するように設定されます。クライアントは持っていません。
  • NodeIPとKnown Port: PodがNodeIPを介して疎通できるようにするため、DaemonSet内のPodはhostPortを使用できます。慣例により、クライアントはNodeIPのリストとポートを知っています。
  • DNS: 同じPodセレクターを持つHeadlessServiceを作成し、endpointsリソースを使ってDaemonSetを探すか、DNSから複数のAレコードを取得します。
  • Service: 同じPodセレクターを持つServiceを作成し、複数のうちのいずれかのNode上のDaemonに疎通させるためにそのServiceを使います。(特定のNodeにアクセスする方法はありません。)

DaemonSetの更新

もしNodeラベルが変更されたとき、そのDaemonSetは直ちに新しくマッチしたNodeにPodを追加し、マッチしなくなったNodeからPodを削除します。

ユーザーはDaemonSetが作成したPodを修正可能です。しかし、Podは全てのフィールドの更新を許可していません。また、DaemonSetコントローラーは次のNode(同じ名前でも)が作成されたときにオリジナルのテンプレートを使ってPodを作成します。

ユーザーはDaemonSetを削除可能です。kubectlコマンドで--cascade=orphanを指定するとDaemonSetのPodはNode上に残り続けます。その後、同じセレクターで新しいDaemonSetを作成すると、新しいDaemonSetは既存のPodを再利用します。PodでDaemonSetを置き換える必要がある場合は、updateStrategyに従ってそれらを置き換えます。

ユーザーはDaemonSet上でローリングアップデートの実施が可能です。

DaemonSetの代替案

Initスクリプト

Node上で直接起動することにより(例: initupstartdsystemdを使用する)、デーモンプロセスを稼働することが可能です。この方法は非常に良いですが、このようなプロセスをDaemonSetを介して起動することはいくつかの利点があります。

  • アプリケーションと同じ方法でデーモンの監視とログの管理ができる。
  • デーモンとアプリケーションで同じ設定用の言語とツール(例: Podテンプレート、kubectl)を使える。
  • リソースリミットを使ったコンテナ内でデーモンを稼働させることにより、デーモンとアプリケーションコンテナの分離性が高まります。ただし、これはPod内ではなく、コンテナ内でデーモンを稼働させることでも可能です。

ベアPod

特定のNode上で稼働するように指定したPodを直接作成することは可能です。しかし、DaemonSetはNodeの故障やNodeの破壊的なメンテナンスやカーネルのアップグレードなど、どのような理由に限らず、削除されたもしくは停止されたPodを置き換えます。このような理由で、ユーザーはPod単体を作成するよりもむしろDaemonSetを使うべきです。

静的Pod

Kubeletによって監視されているディレクトリに対してファイルを書き込むことによって、Podを作成することが可能です。これは静的Podと呼ばれます。DaemonSetと違い、静的Podはkubectlや他のKubernetes APIクライアントで管理できません。静的PodはApiServerに依存しておらず、クラスターの自立起動時に最適です。また、静的Podは将来的には廃止される予定です。

Deployment

DaemonSetは、Podの作成し、そのPodが停止されることのないプロセスを持つことにおいてDeploymentと同様です(例: webサーバー、ストレージサーバー)。

フロントエンドのようなServiceのように、どのホスト上にPodが稼働するか制御するよりも、レプリカ数をスケールアップまたはスケールダウンしたりローリングアップデートする方が重要であるような、状態をもたないServiceに対してDeploymentを使ってください。 DaemonSetがNodeレベルの機能を提供し、他のPodがその特定のNodeで正しく動作するようにする場合、Podのコピーが全てまたは特定のホスト上で常に稼働していることが重要な場合にDaemonSetを使ってください。

例えば、ネットワークプラグインには、DaemonSetとして動作するコンポーネントが含まれていることがよくあります。DaemonSetコンポーネントは、それが動作しているNodeでクラスターネットワークが動作していることを確認します。

次の項目

3.4.2.5 - Jobs

Jobは一つ以上のPodを作成し、指定された数のPodが正常に終了するまで、Podの実行を再試行し続けます。Podが正常に終了すると、Jobは成功したPodの数を追跡します。指定された完了数に達すると、そのタスク(つまりJob)は完了したとみなされます。Jobを削除すると、作成されたPodも一緒に削除されます。Jobを一時停止すると、再開されるまで、稼働しているPodは全部削除されます。

単純なケースを言うと、確実に一つのPodが正常に完了するまで実行されるよう、一つのJobオブジェクトを作成します。 一つ目のPodに障害が発生したり、(例えばノードのハードウェア障害またノードの再起動が原因で)削除されたりすると、Jobオブジェクトは新しいPodを作成します。

Jobで複数のPodを並列で実行することもできます。

スケジュールに沿ってJob(単一のタスクか複数タスク並列のいずれか)を実行したい場合は CronJobを参照してください。

実行例

下記にJobの定義例を記載しています。πを2000桁まで計算して出力するJobで、完了するまで約10秒かかります。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

このコマンドで実行できます:

kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml

実行結果はこのようになります:

job.batch/pi created

kubectlでJobの状態を確認できます:


Name:           pi
Namespace:      default
Selector:       batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                batch.kubernetes.io/job-name=pi
                ...
Annotations:    batch.kubernetes.io/job-tracking: ""
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           batch.kubernetes.io/job-name=pi
  Containers:
   pi:
    Image:      perl:5.34.0
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  21s   job-controller  Created pod: pi-xf9p4
  Normal  Completed         18s   job-controller  Job completed


apiVersion: batch/v1
kind: Job
metadata:
  annotations: batch.kubernetes.io/job-tracking: ""
             ...
  creationTimestamp: "2022-11-10T17:53:53Z"
  generation: 1
  labels:
    batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
    batch.kubernetes.io/job-name: pi
  name: pi
  namespace: default
  resourceVersion: "4751"
  uid: 204fb678-040b-497f-9266-35ffa8716d14
spec:
  backoffLimit: 4
  completionMode: NonIndexed
  completions: 1
  parallelism: 1
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
  suspend: false
  template:
    metadata:
      creationTimestamp: null
      labels:
        batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
        batch.kubernetes.io/job-name: pi
    spec:
      containers:
      - command:
        - perl
        - -Mbignum=bpi
        - -wle
        - print bpi(2000)
        image: perl:5.34.0
        imagePullPolicy: IfNotPresent
        name: pi
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Never
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status:
  active: 1
  ready: 0
  startTime: "2022-11-10T17:53:57Z"
  uncountedTerminatedPods: {}

Jobの完了したPodを確認するには、kubectl get podsを使います。

Jobに属するPodの一覧を機械可読形式で出力するには、下記のコマンドを使います:

pods=$(kubectl get pods --selector=batch.kubernetes.io/job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

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

pi-5rwd7

ここのセレクターはJobのセレクターと同じです。--output=jsonpathオプションは、返されたリストからPodのnameフィールドを指定するための表現です。

その中の一つのPodの標準出力を確認するには:

kubectl logs $pods

Jobの標準出力を確認するもう一つの方法は:

kubectl logs jobs/pi

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

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

Job spec(仕様)の書き方

他のKubernetesオブジェクト設定ファイルと同様に、JobにもapiVersionkindまたはmetadataフィールドが必要です。

コントロールプレーンがJobのために新しいPodを作成するとき、Jobの.metadata.nameはそれらのPodに名前をつけるための基礎の一部になります。Jobの名前は有効なDNSサブドメイン名である必要がありますが、これはPodのホスト名に予期しない結果をもたらす可能性があります。最高の互換性を得るためには、名前はDNSラベルのより限定的な規則に従うべきです。名前がDNSサブドメインの場合でも、名前は63文字以下でなければなりません。

Jobには.specセクションも必要です。

Jobラベル

Jobラベルのjob-namecontroller-uidの接頭辞はbatch.kubernetes.io/となります。

Podテンプレート

.spec.template.specの唯一の必須フィールドです。

.spec.templatepodテンプレートです。ネストされていることとapiVersionkindフィールドが不要になったことを除いて、仕様の定義がPodと全く同じです。

Podの必須フィールドに加えて、Job定義ファイルにあるPodテンプレートでは、適切なラベル(podセレクターを参照)と適切な再起動ポリシーを指定する必要があります。

RestartPolicyNeverOnFailureのみ設定可能です。

Podセレクター

.spec.selectorフィールドはオプションです。ほとんどの場合はむしろ指定しないほうがよいです。 独自のPodセレクターを指定セクションを参照してください。

Jobの並列実行

Jobで実行するのに適したタスクは主に3種類あります:

  1. 非並列Job
    • 通常、Podに障害が発生しない限り、一つのPodのみが起動されます。
    • Podが正常に終了すると、Jobはすぐに完了します。
  2. 固定の完了数を持つ並列Job:
    • .spec.completionsに0以外の正の値を指定します。
    • Jobは全体的なタスクを表し、.spec.completions個のPodが成功すると、Jobの完了となります。
    • .spec.completionMode="Indexed"を利用する場合、各Podは0から.spec.completions-1までの範囲内のインデックスがアサインされます。
  3. ワークキューを利用した並列Job:
    • .spec.completionsの指定をしない場合、デフォルトは.spec.parallelismとなります。
    • Pod間で調整する、または外部サービスを使う方法で、それぞれ何のタスクに着手するかを決めます。例えば、一つのPodはワークキューから最大N個のタスクを一括で取得できます。
    • 各Podは他のPodがすべて終了したかどうか、つまりJobが完了したかどうかを単独で判断できます。
    • Jobに属する 任意 のPodが正常に終了すると、新しいPodは作成されません。
    • 一つ以上のPodが正常に終了し、すべてのPodが終了すると、Jobは正常に完了します。
    • 一つのPodが正常に終了すると、他のPodは同じタスクの作業を行ったり、出力を書き込んだりすることはできません。すべてのPodが終了プロセスに進む必要があります。

非並列 Jobの場合、.spec.completions.spec.parallelismの両方を未設定のままにしておくことも可能です。未設定の場合、両方がデフォルトで1になります。

完了数固定 Jobの場合、.spec.completionsを必要完了数に設定する必要があります。 .spec.parallelismを設定してもいいですし、未設定の場合、デフォルトで1になります。

ワークキュー 並列Jobの場合、.spec.completionsを未設定のままにし、.spec.parallelismを非負の整数に設定する必要があります。

各種類のJobの使用方法の詳細については、Jobパターンセクションを参照してください。

並列処理の制御

必要並列数(.spec.parallelism)は任意の非負の値に設定できます。 未設定の場合は、デフォルトで1になります。 0に設定した際には、増加するまでJobは一時停止されます。

実際の並列数(任意の瞬間に実行されているPod数)は、さまざまな理由により、必要並列数と異なる可能性があります:

  • 完了数固定 Jobの場合、実際に並列して実行されるPodの数は、残りの完了数を超えることはありません。 .spec.parallelismの値が高い場合は無視されます。
  • ワークキュー Jobの場合、任意のPodが成功すると、新しいPodは作成されません。ただし、残りのPodは終了まで実行し続けられます。
  • Jobコントローラーの応答する時間がなかった場合。
  • Jobコントローラーが何らかの理由で(ResourceQuotaの不足、権限の不足など)、Podを作成できない場合、 実際の並列数は必要並列数より少なくなる可能性があります。
  • 同じJobで過去に発生した過度のPod障害が原因で、Jobコントローラーは新しいPodの作成を抑制することがあります。
  • Podがグレースフルシャットダウンされた場合、停止するのに時間がかかります。

完了モード

FEATURE STATE: Kubernetes v1.24 [stable]

完了数固定 Job、つまり.spec.completionsの値がnullではないJobは.spec.completionModeで完了モードを指定できます:

  • NonIndexed(デフォルト): .spec.completions個のPodが成功した場合、Jobの完了となります。言い換えれば、各Podの完了状態は同質です。ここで要注意なのは、.spec.completionsの値がnullの場合、暗黙的にNonIndexedとして指定されることです。

  • Indexed: Jobに属するPodはそれぞれ、0から.spec.completions-1の範囲内の完了インデックスを取得できます。インデックスは下記の三つの方法で取得できます。

    • Podアノテーションbatch.kubernetes.io/job-completion-index
    • Podホスト名の一部として、$(job-name)-$(index)の形式になっています。 インデックス付きJob(Indexed Job)とServiceを一緒に使用すると、Jobに属するPodはお互いにDNSを介して確定的ホスト名で通信できます。この設定方法の詳細はPod間通信を使用したJobを参照してください。 
    • コンテナ化されたタスクの環境変数JOB_COMPLETION_INDEX

    各インデックスに1つずつ正常に完了したPodがあると、Jobは完了したとみなされます。このモードの使い方については、静的な処理の割り当てを使用した並列処理のためのインデックス付きJobを参照してください。

Podとコンテナの障害対策

Pod内のコンテナは、その中のプロセスが0以外の終了コードで終了した、またはメモリ制限を超えたためにコンテナが強制終了されたなど、様々な理由で失敗することがあります。この場合、もし.spec.template.spec.restartPolicy = "OnFailure"と設定すると、Podはノード上に残りますが、コンテナは再実行されます。そのため、プログラムがローカルで再起動した場合の処理を行うか、.spec.template.spec.restartPolicy = "Never"と指定する必要があります。 restartPolicyの詳細についてはPodのライフサイクルを参照してください。

Podがノードからキックされた(ノードがアップグレード、再起動、削除されたなど)、または.spec.template.spec.restartPolicy = "Never"と設定されたときにPodに属するコンテナが失敗したなど、様々な理由でPod全体が故障することもあります。Podに障害が発生すると、Jobコントローラーは新しいPodを起動します。つまりアプリケーションは新しいPodで再起動された場合の処理を行う必要があります。特に、過去に実行した際に生じた一時ファイル、ロック、不完全な出力などを処理する必要があります。

デフォルトでは、それぞれのPodの失敗は.spec.backoffLimitにカウントされます。詳しくはPod失敗のバックオフポリシーをご覧ください。しかし、JobのPod失敗ポリシーを設定することで、Pod失敗の処理をカスタマイズすることができます。

.spec.parallelism = 1.spec.completions = 1.spec.template.spec.restartPolicy = "Never"を指定しても、同じプログラムが2回起動されることもありますので注意してください。

.spec.parallelism.spec.completionsを両方とも2以上指定した場合、複数のPodが同時に実行される可能性があります。そのため、Podは並行処理を行えるようにする必要があります。

フィーチャーゲートPodDisruptionConditionsJobPodFailurePolicyの両方が有効で、.spec.podFailurePolicyフィールドが設定されている場合、Jobコントローラーは終了するPod(.metadata.deletionTimestampフィールドが設定されているPod)を、そのPodが終了する(.status.phaseFailedまたはSucceededになる)までは失敗とはみなしません。ただし、Jobコントローラーは、終了が明らかになるとすみやかに代わりのPodを作成します。Podが終了すると、Jobコントローラーはこの終了したPodを考慮に入れて、該当のJobの.backoffLimit.podFailurePolicyを評価します。

これらの要件のいずれかが満たされていない場合、Jobコントローラーは、そのPodが後にphase: "Succeeded"で終了する場合でも、終了するPodを即時に失敗として数えます。

Pod失敗のバックオフポリシー

設定の論理エラーなどにより、Jobが数回再試行した後に失敗状態にしたい場合があります。.spec.backoffLimitを設定すると、失敗したと判断するまでの再試行回数を指定できます。バックオフ制限はデフォルトで6に設定されています。Jobに属していて失敗したPodはJobコントローラーにより再作成され、バックオフ遅延は指数関数的に増加し(10秒、20秒、40秒…)、最大6分まで増加します。

再実行回数の算出方法は以下の2通りです:

  • .status.phase = "Failed"で設定されたPod数を計算します。
  • restartPolicy = "OnFailure"と設定された場合、.status.phasePendingまたはRunningであるPodに属するすべてのコンテナで再試行する回数を計算します。

どちらかの計算が.spec.backoffLimitに達した場合、Jobは失敗とみなされます。

JobTrackingWithFinalizers機能が無効な場合、 失敗したPodの数は、API内にまだ存在するPodのみに基づいています。

Pod失敗ポリシー

FEATURE STATE: Kubernetes v1.26 [beta]

.spec.podFailurePolicyフィールドで定義されるPod失敗ポリシーを使用すると、コンテナの終了コードとPodの条件に基づいてクラスターがPodの失敗を処理できるようになります。

状況によっては、Podの失敗を処理するときに、Jobの.spec.backoffLimitに基づいたPod失敗のバックオフポリシーが提供する制御よりも、Podの失敗処理に対してより良い制御を求めるかもしれません。これらはいくつかの使用例です:

  • 不要なPodの再起動を回避してワークロードの実行コストを最適化するために、Podの1つがソフトウェアバグを示す終了コードで失敗するとすぐにJobを終了させることができます。
  • 中断が発生してもJobが完了するように、中断によって発生したPodの失敗(preemptionAPIを起点とした退避taintを起点とした立ち退き)を無視し、.spec.backoffLimitのリトライ回数にカウントしないようにすることができます。

上記のユースケースを満たすために、.spec.podFailurePolicyフィールドでPod失敗ポリシーを設定できます。このポリシーは、コンテナの終了コードとPodの条件に基づいてPodの失敗を処理できます。

以下は、podFailurePolicyを定義するJobのマニフェストです:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-example
spec:
  completions: 12
  parallelism: 3
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]        # example command simulating a bug which triggers the FailJob action
        args:
        - -c
        - echo "Hello world!" && sleep 5 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main      # optional
        operator: In             # one of: In, NotIn
        values: [42]
    - action: Ignore             # one of: Ignore, FailJob, Count
      onPodConditions:
      - type: DisruptionTarget   # indicates Pod disruption

上記の例では、Pod失敗ポリシーの最初のルールは、mainコンテナが42の終了コードで失敗した場合、そのJobを失敗とマークすることを指定しています。以下は特に mainコンテナに関するルールです:

  • 終了コード0はコンテナが成功したことを意味します。
  • 終了コード42はJob全体が失敗したことを意味します。
  • それ以外の終了コードは、コンテナが失敗したこと、つまりPod全体が失敗したことを示します。再起動の合計回数がbackoffLimit未満であれば、Podは再作成されます。backoffLimitに達した場合、Job全体が失敗したことになります。

Pod失敗ポリシーの2つ目のルールでは、DisruptionTargetという条件で失敗したPodに対してIgnoreアクションを指定することで、Podの中断が.spec.backoffLimitによるリトライの制限にカウントされないようにします。

これらはAPIの要件と機能です:

  • .spec.podFailurePolicyフィールドをJobに使いたい場合は、.spec.restartPolicyNeverに設定してそのJobのPodテンプレートも定義する必要があります。
  • spec.podFailurePolicy.rulesで指定したPod失敗ポリシーのルールが順番に評価されます。あるPodの失敗がルールに一致すると、残りのルールは無視されます。Pod失敗に一致するルールがない場合は、デフォルトの処理が適用されます。
  • spec.podFailurePolicy.rules[*].onExitCodes.containerNameを指定することで、ルールを特定のコンテナに制限することができます。指定しない場合、ルールはすべてのコンテナに適用されます。指定する場合は、Pod テンプレート内のコンテナ名またはinitContainer名のいずれかに一致する必要があります。
  • Pod失敗ポリシーがspec.podFailurePolicy.rules[*].actionにマッチしたときに実行されるアクションを指定できます。指定可能な値は以下のとおりです。
    • FailJob: PodのJobをFailedとしてマークし、実行中の Pod をすべて終了させる必要があることを示します。
    • Ignore: .spec.backoffLimitのカウンターは加算されず、代替のPodが作成すべきであることを示します。
    • Count: Podがデフォルトの方法で処理されるべきであることを示します。.spec.backoffLimitのカウンターが加算されます。

Jobの終了とクリーンアップ

Jobが完了すると、それ以上Podは作成されませんが、通常Podが削除されることもありません。 これらを残しておくと、完了したPodのログを確認でき、エラーや警告などの診断出力を確認できます。 またJobオブジェクトはJob完了後も残っているため、状態を確認することができます。古いJobの状態を把握した上で、削除するかどうかはユーザー次第です。Jobを削除するにはkubectl (例:kubectl delete jobs/piまたはkubectl delete -f ./job.yaml)を使います。kubectlでJobを削除する場合、Jobが作成したPodも全部削除されます。

デフォルトでは、Podが失敗しない(restartPolicy=Never)またはコンテナがエラーで終了しない(restartPolicy=OnFailure)限り、Jobは中断されることなく実行されます。.spec.backoffLimitに達するとそのJobは失敗と見なされ、実行中のPodはすべて終了します。

Jobを終了させるもう一つの方法は、活動期間を設定することです。 Jobの.spec.activeDeadlineSecondsフィールドに秒数を設定することで、活動期間を設定できます。 Podがいくつ作成されても、activeDeadlineSecondsはJobの存続する時間に適用されます。 JobがactiveDeadlineSecondsに達すると、実行中のすべてのPodは終了され、Jobの状態はtype: Failedになり、理由はreason: DeadlineExceededになります。

ここで要注意なのは、Jobの.spec.activeDeadlineSeconds.spec.backoffLimitよりも優先されます。したがって、失敗して再試行しているPodが一つ以上持っているJobは、backoffLimitに達していなくても、activeDeadlineSecondsで指定された設定時間に達すると、追加のPodをデプロイしなくなります。

例えば:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job仕様と、Jobに属するPodテンプレートの仕様は両方ともactiveDeadlineSecondsフィールドを持っているので注意してください。適切なレベルで設定していることを確認してください。

またrestartPolicyはJob自体ではなく、Podに適用されることも注意してください: Jobの状態はtype: Failedになると、自動的に再起動されることはありません。 つまり、.spec.activeDeadlineSeconds.spec.backoffLimitによって引き起こされるJob終了メカニズムは、永久的なJob失敗につながり、手動で介入して解決する必要があります。

終了したJobの自動クリーンアップ

終了したJobは通常システムに残す必要はありません。残ったままにしておくとAPIサーバーに負担をかけることになります。Jobが上位コントローラーにより直接管理されている場合、例えばCronJobsの場合、Jobは指定された容量ベースのクリーンアップポリシーに基づき、CronJobによりクリーンアップされます。

終了したJobのTTLメカニズム

FEATURE STATE: Kubernetes v1.23 [stable]

終了したJob(状態がCompleteFailedになったJob)を自動的にクリーンアップするもう一つの方法は TTLコントローラーより提供されたTTLメカニズムです。.spec.ttlSecondsAfterFinishedフィールドを指定することで、終了したリソースをクリーンアップすることができます。

TTLコントローラーでJobをクリーンアップする場合、Jobはカスケード的に削除されます。つまりJobを削除する際に、Jobに属しているオブジェクト、例えばPodなども一緒に削除されます。Jobが削除される場合、Finalizerなどの、Jobのライフサイクル保証は守られることに注意してください。

例えば:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job pi-with-ttlは終了してからの100秒後に自動的に削除されるようになっています。

このフィールドに0を設定すると、Jobは終了後すぐに自動削除の対象になります。このフィールドに何も設定しないと、Jobが終了してもTTLコントローラーによるクリーンアップはされません。

Jobパターン

Jobオブジェクトは、Podの確実な並列実行をサポートするために使用されます。科学技術計算でよく見られるような、密接に通信を行う並列処理をサポートするようには設計されていません。独立だが関連性のある一連の作業項目の並列処理をサポートします。例えば送信すべき電子メール、レンダリングすべきフレーム、トランスコードすべきファイル、スキャンすべきNoSQLデータベースのキーの範囲、などです。

複雑なシステムでは、異なる作業項目のセットが複数存在する場合があります。ここでは、ユーザーが一斉に管理したい作業項目のセットが一つだけの場合 — つまりバッチJobだけを考えます。

並列計算にはいくつかのパターンがあり、それぞれに長所と短所があります。 トレードオフの関係にあるのは:

  • 各作業項目に1つのJobオブジェクト vs. すべての作業項目に1つのJobオブジェクト。
     後者は大量の作業項目を処理する場合に適しています。
     前者は大量のJobオブジェクトを管理するため、ユーザーとシステムにオーバーヘッドをかけることになります。
  • 作成されるPod数が作業項目数と等しい、 vs. 各Podが複数の作業項目を処理する。  前者は通常、既存のコードやコンテナへの変更が少なくて済みます。 後者は上記と同じ理由で、大量の作業項目を処理する場合に適しています。
  • ワークキューを利用するアプローチもいくつかあります。それを使うためには、キューサービスを実行し、既存のプログラムやコンテナにワークキューを利用させるための改造を行う必要があります。 他のアプローチは既存のコンテナ型アプリケーションに適用しやすいです。

ここでは、上記のトレードオフをまとめてあり、それぞれ2~4列目に対応しています。 またパターン名のところは、例やより詳しい説明が書いてあるページへのリンクになっています。

パターン単一JobオブジェクトPodが作業項目より少ない?アプリを修正せずに使用できる?
作業項目ごとにPodを持つキュー時々
Pod数可変のキュー
静的な処理の割り当てを使用したインデックス付きJob
Jobテンプレート拡張
Pod間通信を使用したJob時々時々

.spec.completionsで完了数を指定する場合、Jobコントローラーより作成された各Podは同一のspecを持ちます。これは、このタスクのすべてのPodが同じコマンドライン、同じイメージ、同じボリューム、そして(ほぼ)同じ環境変数を持つことを意味します。これらのパターンは、Podが異なる作業をするためのさまざまな配置方法になります。

この表は、各パターンで必要な.spec.parallelism.spec.completionsの設定を示しています。 ここで、Wは作業項目の数を表しています。

パターン.spec.completions.spec.parallelism
作業項目ごとにPodを持つキューW任意
Pod数可変のキューnull任意
静的な処理の割り当てを使用したインデックス付きJobW任意
Jobテンプレート拡張11であるべき
Pod間通信を使用したJobWW

高度な使い方

Jobの一時停止

FEATURE STATE: Kubernetes v1.24 [stable]

Jobが作成されると、JobコントローラーはJobの要件を満たすために直ちにPodの作成を開始し、Jobが完了するまで作成し続けます。しかし、Jobの実行を一時的に中断して後で再開したい場合、または一時停止状態のJobを再開し、再開時間は後でカスタムコントローラーに判断させたい場合はあると思います。

Jobを一時停止するには、Jobの.spec.suspendフィールドをtrueに修正し、後でまた再開したい場合にはfalseに修正すればよいです。 .spec.suspendをtrueに設定してJobを作成すると、一時停止状態のままで作成されます。

一時停止状態のJobを再開すると、.status.startTimeフィールドの値は現在時刻にリセットされます。これはつまり、Jobが一時停止して再開すると、.spec.activeDeadlineSecondsタイマーは停止してリセットされることになります。

Jobを中断すると、状態がCompletedではない実行中のPodはすべてSIGTERMシグナルを受信して終了されます。Podのグレースフル終了の猶予期間がカウントダウンされ、この期間内に、Podはこのシグナルを処理しなければなりません。場合により、その後のために処理状況を保存したり、変更を元に戻したりする処理が含まれます。この方法で終了したPodはcompletions数にカウントされません。

下記は一時停止状態のままで作成されたJobの定義例になります:

kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  suspend: true
  parallelism: 1
  completions: 5
  template:
    spec:
      ...

コマンドラインを使ってJobにパッチを当てることで、Jobの一時停止状態を切り替えることもできます。

活動中のJobを一時停止する:

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'

一時停止中のJobを再開する:

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'

Jobのstatusセクションで、Jobが停止中なのか、過去に停止したことがあるかを判断できます:

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata and .spec omitted
status:
  conditions:
  - lastProbeTime: "2021-02-05T13:14:33Z"
    lastTransitionTime: "2021-02-05T13:14:33Z"
    status: "True"
    type: Suspended
  startTime: "2021-02-05T13:13:48Z"

Jobのcondition.typeが"Suspended"で、statusが"True"になった場合、Jobは一時停止中になります。lastTransitionTimeフィールドで、どのぐらい中断されたかを判断できます。statusが"False"になった場合、Jobは一時停止状態でしたが、今は実行されていることになります。conditionが書いていない場合、Jobは一度も停止していないことになります。

Jobが一時停止して再開した場合、Eventsも作成されます:

kubectl describe jobs/myjob
Name:           myjob
...
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  12m   job-controller  Created pod: myjob-hlrpl
  Normal  SuccessfulDelete  11m   job-controller  Deleted pod: myjob-hlrpl
  Normal  Suspended         11m   job-controller  Job suspended
  Normal  SuccessfulCreate  3s    job-controller  Created pod: myjob-jvb44
  Normal  Resumed           3s    job-controller  Job resumed

最後の4つのイベント、特に"Suspended"と"Resumed"のイベントは、.spec.suspendフィールドの値を切り替えた直接の結果です。この2つのイベントの間に、Podは作成されていないことがわかりますが、Jobが再開されるとすぐにPodの作成も再開されました。

可変スケジューリング命令

FEATURE STATE: Kubernetes v1.27 [stable]

ほとんどの場合、並列Jobは、すべてのPodが同じゾーン、またはすべてのGPUモデルxかyのいずれかであるが、両方の混在ではない、などの制約付きで実行することが望ましいです。

suspendフィールドは、これらの機能を実現するための第一歩です。Suspendは、カスタムキューコントローラーがJobをいつ開始すべきかを決定することができます。しかし、Jobの一時停止が解除されると、カスタムキューコントローラーは、Job内のPodの実際の配置場所には影響を与えません。

この機能により、Jobが開始する前にスケジューリング命令を更新でき、カスタムキューコントローラーがPodの配置に影響を与えることができるようになります。同時に実際のPodからNodeへの割り当てをkube-schedulerにオフロードする能力を提供します。これは一時停止されたJobの中で、一度も一時停止解除されたことのないJobに対してのみ許可されます。

JobのPodテンプレートで更新可能なフィールドはnodeAffinity、nodeSelector、tolerations、labelsとannotations、スケジューリングゲートです。

独自のPodセレクターを指定

Jobオブジェクトを作成する際には通常、.spec.selectorを指定しません。Jobが作成された際に、システムのデフォルトロジックは、他のJobと重ならないようなセレクターの値を選択し、このフィールドに追加します。

しかし、場合によっては、この自動設定されたセレクターをオーバーライドする必要があります。そのためには、Jobの.spec.selectorを指定します。

その際には十分な注意が必要です。そのJobの他のPodと重なったラベルセレクターを指定し、無関係のPodにマッチした場合、無関係のJobのPodが削除されたり、無関係のPodが完了されてもこのJobの完了数とカウントしたり、片方または両方のJobがPodの作成または完了までの実行を拒否する可能性があります。 一意でないセレクターを選択した場合、他のコントローラー(例えばReplicationController)や属しているPodが予測できない挙動をする可能性があります。Kubernetesは.spec.selectorを間違って設定しても止めることはしません。

下記はこの機能の使用例を紹介しています。

oldと名付けたJobがすでに実行されていると仮定します。既存のPodをそのまま実行し続けてほしい一方で、作成する残りのPodには別のテンプレートを使用し、そのJobには新しい名前を付けたいとしましょう。これらのフィールドは更新できないため、Jobを直接更新できません。そのため、kubectl delete jobs/old --cascade=orphanで、属しているPodが実行されたままoldJobを削除します。削除する前に、どのセレクターを使用しているかをメモしておきます:

kubectl get job old -o yaml

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

kind: Job
metadata:
  name: old
  ...
spec:
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

次に、newという名前で新しくJobを作成し、同じセレクターを明示的に指定します。既存のPodもbatch.kubernetes.io/controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002ラベルが付いているので、同じくnewJobによってコントロールされます。

通常システムが自動的に生成するセレクターを使用しないため、新しいJobで manualSelector: trueを指定する必要があります。

kind: Job
metadata:
  name: new
  ...
spec:
  manualSelector: true
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

新しいJobはa8f3d00d-c6d2-11e5-9f87-42010af00002ではなく、別のuidを持つことになります。manualSelector: trueを設定することで、自分は何をしているかを知っていて、またこのミスマッチを許容することをシステムに伝えます。

FinalizerによるJob追跡

FEATURE STATE: Kubernetes v1.26 [stable]

コントロールプレーンは任意のJobに属するPodを追跡し、そのPodがAPIサーバーから削除されたかどうか認識します。そのためJobコントローラーはファイナライザーbatch.kubernetes.io/job-trackingを持つPodを作成します。コントローラーがファイナライザーを削除するのは、PodがJobステータスに反映された後なので、他のコントローラーやユーザがPodを削除することができます。

Kubernetes 1.26にアップグレードする前、またはフィーチャーゲートJobTrackingWithFinalizersが有効になる前に作成されたJobは、Podファイナライザーを使用せずに追跡されます。Jobコントローラーは、クラスターに存在するPodのみに基づいて、succeededPodとfailedPodのステータスカウンタを更新します。クラスターからPodが削除されると、コントロールプレーンはJobの進捗を見失う可能性があります。

Jobがbatch.kubernetes.io/job-trackingというアノテーションを持っているかどうかをチェックすることで、コントロールプレーンがPodファイナライザーを使ってJobを追跡しているかどうかを判断できます。Jobからこのアノテーションを手動で追加したり削除したりしてはいけません。代わりに、JobがPodファイナライザーを使用して追跡されていることを確認するために、Jobを再作成することができます。

静的なインデックス付きJob

FEATURE STATE: Kubernetes v1.27 [beta]

.spec.parallelism.spec.compleitionsの両方を、.spec.parallelism == .spec.compleitionsとなるように変更することで、インデックス付きJobを増減させることができます。APIサーバElasticIndexedJobフィーチャーゲートが無効になっている場合、.spec.compleitionsは不変です。

静的なインデックス付きJobの使用例としては、MPI、Horovord、Ray、PyTorchトレーニングジョブなど、インデックス付きJobのスケーリングを必要とするバッチワークロードがあります。

代替案

単なるPod

Podが動作しているノードが再起動または故障した場合、Podは終了し、再起動されません。しかし、終了したPodを置き換えるため、Jobが新しいPodを作成します。このため、たとえアプリケーションが1つのPodしか必要としない場合でも、単なるPodではなくJobを使用することをお勧めします。

Replication Controller

JobはReplication Controllersを補完するものです。 Replication Controllerは、終了することが想定されていないPod(Webサーバーなど)を管理し、Jobは終了することが想定されているPod(バッチタスクなど)を管理します。

Podのライフサイクルで説明したように、JobRestartPolicyOnFailureNeverと設定されているPodにのみ適用されます。(注意:RestartPolicyが設定されていない場合、デフォルト値はAlwaysになります)

シングルJobによるコントローラーPodの起動

もう一つのパターンは、一つのJobが一つPodを作り、そのPodがカスタムコントローラーのような役割を果たし、他のPodを作ります。これは最も柔軟性がありますが、使い始めるにはやや複雑で、Kubernetesとの統合もあまりできません。

このパターンの一例としては、Sparkマスターコントローラーを起動し、sparkドライバーを実行してクリーンアップするスクリプトを実行するPodをJobで起動する(sparkの例を参照)が挙げられます。

この方法のメリットは、全処理過程でJobオブジェクトが完了する保証がありながらも、どのPodを作成し、どのように作業を割り当てるかを完全に制御できることです。

次の項目

3.4.2.6 - 終了したリソースのためのTTLコントローラー(TTL Controller for Finished Resources)

FEATURE STATE: Kubernetes v1.12 [alpha]

TTLコントローラーは実行を終えたリソースオブジェクトのライフタイムを制御するためのTTL (time to live) メカニズムを提供します。
TTLコントローラーは現在Jobのみ扱っていて、将来的にPodやカスタムリソースなど、他のリソースの実行終了を扱えるように拡張される予定です。

α版の免責事項: この機能は現在α版の機能で、kube-apiserverとkube-controller-managerのFeature GateTTLAfterFinishedを有効にすることで使用可能です。

TTLコントローラー

TTLコントローラーは現在Jobに対してのみサポートされています。クラスターオペレーターはこののように、Jobの.spec.ttlSecondsAfterFinishedフィールドを指定することにより、終了したJob(完了したもしくは失敗した)を自動的に削除するためにこの機能を使うことができます。
TTLコントローラーは、そのリソースが終了したあと指定したTTLの秒数後に削除できるか推定します。言い換えると、そのTTLが期限切れになると、TTLコントローラーがリソースをクリーンアップするときに、そのリソースに紐づく従属オブジェクトも一緒に連続で削除します。注意点として、リソースが削除されるとき、ファイナライザーのようなライフサイクルに関する保証は尊重されます。

TTL秒はいつでもセット可能です。下記はJobの.spec.ttlSecondsAfterFinishedフィールドのセットに関するいくつかの例です。

  • Jobがその終了後にいくつか時間がたった後に自動的にクリーンアップできるように、そのリソースマニフェストにこの値を指定します。
  • この新しい機能を適用させるために、存在していてすでに終了したリソースに対してこのフィールドをセットします。
  • リソース作成時に、このフィールドを動的にセットするために、管理webhookの変更をさせます。クラスター管理者は、終了したリソースに対して、このTTLポリシーを強制するために使うことができます。
  • リソースが終了した後に、このフィールドを動的にセットしたり、リソースステータスやラベルなどの値に基づいて異なるTTL値を選択するために、管理webhookの変更をさせます。

注意

TTL秒の更新

注意点として、Jobの.spec.ttlSecondsAfterFinishedフィールドといったTTL期間はリソースが作成された後、もしくは終了した後に変更できます。しかし、一度Jobが削除可能(TTLの期限が切れたとき)になると、それがたとえTTLを伸ばすような更新に対してAPIのレスポンスで成功したと返されたとしても、そのシステムはJobが稼働し続けることをもはや保証しません。

タイムスキュー(Time Skew)

TTLコントローラーが、TTL値が期限切れかそうでないかを決定するためにKubernetesリソース内に保存されたタイムスタンプを使うため、この機能はクラスター内のタイムスキュー(時刻のずれ)に対してセンシティブとなります。タイムスキューは、誤った時間にTTLコントローラーに対してリソースオブジェクトのクリーンアップしてしまうことを引き起こすものです。

Kubernetesにおいてタイムスキューを避けるために、全てのNode上でNTPの稼働を必須とします(#6159を参照してください)。クロックは常に正しいものではありませんが、Node間におけるその差はとても小さいものとなります。TTLに0でない値をセットするときにこのリスクに対して注意してください。

次の項目

3.4.2.7 - CronJob

FEATURE STATE: Kubernetes v1.8 [beta]

CronJob は繰り返しのスケジュールによってJobを作成します。

CronJob オブジェクトとは crontab (cron table)ファイルでみられる一行のようなものです。 Cron形式で記述された指定のスケジュールの基づき、定期的にジョブが実行されます。

CronJobリソースのためのマニフェストを作成する場合、その名前が有効なDNSサブドメイン名か確認してください。 名前は52文字を超えることはできません。これはCronJobコントローラーが自動的に、与えられたジョブ名に11文字を追加し、ジョブ名の長さは最大で63文字以内という制約があるためです。

CronJob

CronJobは、バックアップの実行やメール送信のような定期的であったり頻発するタスクの作成に役立ちます。 CronJobは、クラスターがアイドル状態になりそうなときにJobをスケジューリングするなど、特定の時間に個々のタスクをスケジュールすることもできます。

このCronJobマニフェスト例は、毎分ごとに現在の時刻とhelloメッセージを表示します。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

(Running Automated Tasks with a CronJobではこの例をより詳しく説明しています。).

CronJobの制限

cronジョブは一度のスケジュール実行につき、 おおよそ 1つのジョブオブジェクトを作成します。ここで おおよそ と言っているのは、ある状況下では2つのジョブが作成される、もしくは1つも作成されない場合があるためです。通常、このようなことが起こらないようになっていますが、完全に防ぐことはできません。したがって、ジョブは 冪等 であるべきです。

startingDeadlineSecondsが大きな値、もしくは設定されていない(デフォルト)、そして、concurrencyPolicyAllowに設定している場合には、少なくとも一度、ジョブが実行されることを保証します。

最後にスケジュールされた時刻から現在までの間に、CronJobコントローラーはどれだけスケジュールが間に合わなかったのかをCronJobごとにチェックします。もし、100回以上スケジュールが失敗していると、ジョブは開始されずに、ログにエラーが記録されます。

Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.

startingDeadlineSecondsフィールドが設定されると(nilではない)、最後に実行された時刻から現在までではなく、startingDeadlineSecondsの値から現在までで、どれだけジョブを逃したのかをコントローラーが数えます。 startingDeadlineSeconds200の場合、過去200秒間にジョブが失敗した回数を記録します。

スケジュールされた時間にCronJobが作成できないと、失敗したとみなされます。たとえば、concurrencyPolicyForbidに設定されている場合、前回のスケジュールがまだ実行中にCronJobをスケジュールしようとすると、CronJobは作成されません。

例として、CronJobが08:30:00を開始時刻として1分ごとに新しいJobをスケジュールするように設定され、startingDeadlineSecondsフィールドが設定されていない場合を想定します。CronJobコントローラーが08:29:00 から10:21:00の間にダウンしていた場合、スケジューリングを逃したジョブの数が100を超えているため、ジョブは開始されません。

このコンセプトをさらに掘り下げるために、CronJobが08:30:00から1分ごとに新しいJobを作成し、startingDeadlineSecondsが200秒に設定されている場合を想定します。CronJobコントローラーが前回の例と同じ期間(08:29:00 から10:21:00まで)にダウンしている場合でも、10:22:00時点でJobはまだ動作しています。このようなことは、過去200秒間(言い換えると、3回の失敗)に何回スケジュールが間に合わなかったをコントローラーが確認するときに発生します。これは最後にスケジュールされた時間から今までのものではありません。

CronJobはスケジュールに一致するJobの作成にのみ関与するのに対して、JobはJobが示すPod管理を担います。

次の項目

Cron表現形式では、CronJobのscheduleフィールドのフォーマットを説明しています。

cronジョブの作成や動作の説明、CronJobマニフェストの例については、Running automated tasks with cron jobsを見てください。

3.5 - Service、負荷分散とネットワーキング

Kubernetesにおけるネットワーキングの概念とリソース。

Kubernetesのネットワーキングは4つの懸念事項に対処します。

  • Pod内のコンテナは、ネットワーキングを利用してループバック経由の通信を行います。
  • クラスターネットワーキングは、異なるPod間の通信を提供します。
  • Serviceリソースは、Pod内で動作しているアプリケーションへクラスターの外部から到達可能なように露出を許可します。
  • Serviceを利用して、クラスター内部のみで使用するServiceの公開も可能です。

3.5.1 - Service

KubernetesにおけるServiceとは、 クラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開する方法です。

Kubernetesでは、なじみのないサービスディスカバリーのメカニズムを使用するためにユーザーがアプリケーションの修正をする必要はありません。 KubernetesはPodにそれぞれのIPアドレス割り振りや、Podのセットに対する単一のDNS名を提供したり、それらのPodのセットに対する負荷分散が可能です。

Serviceを利用する動機

Kubernetes Podはクラスターの状態に合わせて作成され削除されます。Podは揮発的なリソースです。 Deploymentをアプリケーションを稼働させるために使用すると、Podを動的に作成・削除してくれます。

各Podはそれ自身のIPアドレスを持ちます。しかしDeploymentでは、ある時点において同時に稼働しているPodのセットは、その後のある時点において稼働しているPodのセットとは異なる場合があります。

この仕組みはある問題を引き起こします。もし、あるPodのセット(ここでは"バックエンド"と呼びます)がクラスター内で他のPodのセット(ここでは"フロントエンド"と呼びます)に対して機能を提供する場合、フロントエンドのPodがワークロードにおけるバックエンドを使用するために、バックエンドのPodのIPアドレスを探し出したり、記録し続けるためにはどうすればよいでしょうか?

ここで Service について説明します。

Serviceリソース

Kubernetesにおいて、ServiceはPodの論理的なセットや、そのPodのセットにアクセスするためのポリシーを定義します(このパターンはよくマイクロサービスと呼ばることがあります)。 ServiceによってターゲットとされたPodのセットは、たいてい セレクターによって定義されます。 その他の方法について知りたい場合はセレクターなしのServiceを参照してください。

例えば、3つのレプリカが稼働しているステートレスな画像処理用のバックエンドを考えます。これらのレプリカは代替可能です。— フロントエンドはバックエンドが何であろうと気にしません。バックエンドのセットを構成する実際のPodのセットが変更された際、フロントエンドクライアントはその変更を気にしたり、バックエンドのPodのセットの情報を記録しておく必要はありません。

Serviceによる抽象化は、クライアントからバックエンドのPodの管理する責務を分離することを可能にします。

クラウドネイティブのサービスディスカバリー

アプリケーション内でサービスディスカバリーのためにKubernetes APIが使える場合、ユーザーはエンドポイントをAPI Serverに問い合わせることができ、またService内のPodのセットが変更された時はいつでも更新されたエンドポイントの情報を取得できます。

非ネイティブなアプリケーションのために、KubernetesはアプリケーションとバックエンドPodの間で、ネットワークポートやロードバランサーを配置する方法を提供します。

Serviceの定義

KubernetesのServiceはPodと同様にRESTのオブジェクトです。他のRESTオブジェクトと同様に、ユーザーはServiceの新しいインスタンスを作成するためにAPIサーバーに対してServiceの定義をPOSTできます。Serviceオブジェクトの名前は、有効なDNSラベル名である必要があります。

例えば、TCPで9376番ポートで待ち受けていて、app=MyappというラベルをもつPodのセットがあるとします。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

この定義では、"my-service"という名前のついた新しいServiceオブジェクトを作成します。これはapp=Myappラベルのついた各Pod上でTCPの9376番ポートをターゲットとします。

Kubernetesは、このServiceに対してIPアドレス("clusterIP"とも呼ばれます)を割り当てます。これはServiceのプロキシによって使用されます(下記の仮想IPとServiceプロキシを参照ください)。

Serviceセレクターのコントローラーはセレクターに一致するPodを継続的にスキャンし、“my-service”という名前のEndpointsオブジェクトに対して変更をPOSTします。

Pod内のポートの定義は名前を設定でき、ServiceのtargetPort属性にてその名前を参照できます。これは単一の設定名をもつService内で、複数の種類のPodが混合していたとしても有効で、異なるポート番号を介することによって利用可能な、同一のネットワークプロトコルを利用します。 この仕組みはServiceをデプロイしたり、設定を追加する場合に多くの点でフレキシブルです。例えば、バックエンドソフトウェアにおいて、次のバージョンでPodが公開するポート番号を変更するときに、クライアントの変更なしに行えます。

ServiceのデフォルトプロトコルはTCPです。また、他のサポートされているプロトコルも利用可能です。

多くのServiceが、1つ以上のポートを公開する必要があるように、Kubernetesは1つのServiceオブジェクトに対して複数のポートの定義をサポートしています。 各ポート定義は同一のprotocolまたは異なる値を設定できます。

セレクターなしのService

Serviceは多くの場合、KubernetesのPodに対するアクセスを抽象化しますが、他の種類のバックエンドも抽象化できます。 例えば:

  • プロダクション環境で外部のデータベースクラスターを利用したいが、テスト環境では、自身のクラスターが持つデータベースを利用したい場合
  • Serviceを、異なるNamespaceのServiceや他のクラスターのServiceに向ける場合
  • ワークロードをKubernetesに移行するとき、アプリケーションに対する処理をしながら、バックエンドの一部をKubernetesで実行する場合

このような場合において、ユーザーはPodセレクターなしでServiceを定義できます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

このServiceはセレクターがないため、対応するEndpointsオブジェクトは自動的に作成されません。 ユーザーはEndpointsオブジェクトを手動で追加することにより、向き先のネットワークアドレスとポートを手動でマッピングできます。

apiVersion: v1
kind: Endpoints
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 192.0.2.42
    ports:
      - port: 9376

Endpointsオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。

セレクターなしのServiceへのアクセスは、セレクターをもっているServiceと同じようにふるまいます。上記の例では、トラフィックはYAMLファイル内で192.0.2.42:9376 (TCP)で定義された単一のエンドポイントにルーティングされます。

ExternalName Serviceはセレクターの代わりにDNS名を使用する特殊なケースのServiceです。さらなる情報は、このドキュメントの後で紹介するExternalNameを参照ください。

エンドポイントスライス

FEATURE STATE: Kubernetes v1.17 [beta]

エンドポイントスライスは、Endpointsに対してよりスケーラブルな代替手段を提供できるAPIリソースです。概念的にはEndpointsに非常に似ていますが、エンドポイントスライスを使用すると、ネットワークエンドポイントを複数のリソースに分割できます。デフォルトでは、エンドポイントスライスは、100個のエンドポイントに到達すると「いっぱいである」と見なされ、その時点で追加のエンドポイントスライスが作成され、追加のエンドポイントが保存されます。

エンドポイントスライスは、エンドポイントスライスのドキュメントにて詳しく説明されている追加の属性と機能を提供します。

アプリケーションプロトコル

FEATURE STATE: Kubernetes v1.19 [beta]

AppProtocolフィールドによってServiceの各ポートに対して特定のアプリケーションプロトコルを指定することができます。 この値は、対応するEndpointsオブジェクトとEndpointSliceオブジェクトに反映されます。

仮想IPとサービスプロキシ

Kubernetesクラスターの各Nodeはkube-proxyを稼働させています。kube-proxyExternalNameタイプ以外のService用に仮想IPを実装する責務があります。

なぜ、DNSラウンドロビンを使わないのでしょうか。

ここで湧き上がる質問として、なぜKubernetesは内部のトラフィックをバックエンドへ転送するためにプロキシに頼るのでしょうか。 他のアプローチはどうなのでしょうか。例えば、複数のAバリュー(もしくはIPv6用にAAAAバリューなど)をもつDNSレコードを設定し、ラウンドロビン方式で名前を解決することは可能でしょうか。

Serviceにおいてプロキシを使う理由はいくつかあります。

  • DNSの実装がレコードのTTLをうまく扱わず、期限が切れた後も名前解決の結果をキャッシュするという長い歴史がある。
  • いくつかのアプリケーションではDNSルックアップを1度だけ行い、その結果を無期限にキャッシュする。
  • アプリケーションとライブラリーが適切なDNS名の再解決を行ったとしても、DNSレコード上の0もしくは低い値のTTLがDNSに負荷をかけることがあり、管理が難しい。

user-spaceプロキシモード

このモードでは、kube-proxyはServiceやEndpointsオブジェクトの追加・削除をチェックするために、Kubernetes Masterを監視します。 各Serviceは、ローカルのNode上でポート(ランダムに選ばれたもの)を公開します。この"プロキシポート"に対するどのようなリクエストも、そのServiceのバックエンドPodのどれか1つにプロキシされます(Endpointsを介して通知されたPodに対して)。 kube-proxyは、どのバックエンドPodを使うかを決める際にServiceのSessionAffinity項目の設定を考慮に入れます。

最後に、user-spaceプロキシはServiceのclusterIP(仮想IP)とportに対するトラフィックをキャプチャするiptablesルールをインストールします。 そのルールは、トラフィックをバックエンドPodにプロキシするためのプロキシポートにリダイレクトします。

デフォルトでは、user-spaceモードにおけるkube-proxyはラウンドロビンアルゴリズムによってバックエンドPodを選択します。

user-spaceプロキシのService概要ダイアグラム

iptablesプロキシモード

このモードでは、kube-proxyはServiceやEndpointsオブジェクトの追加・削除のチェックのためにKubernetesコントロールプレーンを監視します。 各Serviceでは、そのServiceのclusterIPportに対するトラフィックをキャプチャするiptablesルールをインストールし、そのトラフィックをServiceのあるバックエンドのセットに対してリダイレクトします。 各Endpointsオブジェクトは、バックエンドのPodを選択するiptablesルールをインストールします。

デフォルトでは、iptablesモードにおけるkube-proxyはバックエンドPodをランダムで選択します。

トラフィックのハンドリングのためにiptablesを使用すると、システムのオーバーヘッドが少なくなります。これは、トラフィックがLinuxのnetfilterによってuser-spaceとkernel-spaceを切り替える必要がないためです。 このアプローチは、オーバーヘッドが少ないことに加えて、より信頼できる方法でもあります。

kube-proxyがiptablesモードで稼働し、最初に選択されたPodが応答しない場合、そのコネクションは失敗します。 これはuser-spaceモードでの挙動と異なります: user-spaceモードにおいては、kube-proxyは最初のPodに対するコネクションが失敗したら、自動的に他のバックエンドPodに対して再接続を試みます。

iptablesモードのkube-proxyが正常なバックエンドPodのみをリダイレクト対象とするために、PodのReadinessProbeを使用してバックエンドPodが正常に動作しているか確認できます。これは、ユーザーがkube-proxyを介して、コネクションに失敗したPodに対してトラフィックをリダイレクトするのを除外することを意味します。

iptablesプロキシのService概要ダイアグラム

IPVSプロキシモード

FEATURE STATE: Kubernetes v1.11 [stable]

ipvsモードにおいて、kube-proxyはServiceとEndpointsオブジェクトを監視し、IPVSルールを作成するためにnetlinkインターフェースを呼び出し、定期的にKubernetesのServiceとEndpointsとIPVSルールを同期させます。 このコントロールループはIPVSのステータスが理想的な状態になることを保証します。 Serviceにアクセスするとき、IPVSはトラフィックをバックエンドのPodに向けます。

IPVSプロキシモードはiptablesモードと同様に、netfilterのフック関数に基づいています。ただし、基礎となるデータ構造としてハッシュテーブルを使っているのと、kernel-spaceで動作します。 これは、IPVSモードにおけるkube-proxyはiptablesモードに比べてより低いレイテンシーでトラフィックをリダイレクトし、プロキシのルールを同期する際にはよりパフォーマンスがよいことを意味します。 他のプロキシモードと比較して、IPVSモードはより高いネットワークトラフィックのスループットをサポートしています。

IPVSはバックエンドPodに対するトラフィックのバランシングのために多くのオプションを下記のとおりに提供します。

  • rr: ラウンドロビン
  • lc: 最低コネクション数(オープンされているコネクション数がもっとも小さいもの)
  • dh: 送信先IPによって割り当てられたハッシュ値をもとに割り当てる(Destination Hashing)
  • sh: 送信元IPによって割り当てられたハッシュ値をもとに割り当てる(Source Hashing)
  • sed: 見込み遅延が最小なもの
  • nq: キューなしスケジューリング

IPVSプロキシのService概要ダイアグラム

このダイアグラムのプロキシモデルにおいて、ServiceのIP:Portに対するトラフィックは、クライアントがKubernetesのServiceやPodについて何も知ることなく適切にバックエンドにプロキシされています。

特定のクライアントからのコネクションが、毎回同一のPodにリダイレクトされるようにするためには、service.spec.sessionAffinityに"ClientIP"を設定することにより、クライアントのIPアドレスに基づいたSessionAffinityを選択することができます(デフォルトは"None")。 また、service.spec.sessionAffinityConfig.clientIP.timeoutSecondsを適切に設定することにより、セッションのタイムアウト時間を設定できます(デフォルトではこの値は18,000で、3時間となります)。

複数のポートを公開するService

いくつかのServiceにおいて、ユーザーは1つ以上のポートを公開する必要があります。Kubernetesは、Serviceオブジェクト上で複数のポートを定義するように設定できます。 Serviceで複数のポートを使用するとき、どのポートかを明確にするために、複数のポート全てに対して名前をつける必要があります。 例えば:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  - name: https
    protocol: TCP
    port: 443
    targetPort: 9377

ユーザー所有のIPアドレスを選択する

Serviceを作成するリクエストの一部として、ユーザー所有のclusterIPアドレスを指定することができます。 これを行うためには.spec.clusterIPフィールドにセットします。 使用例として、もしすでに再利用したいDNSエントリーが存在していた場合や、特定のIPアドレスを設定されたレガシーなシステムや、IPの再設定が難しい場合です。

ユーザーが指定したIPアドレスは、そのAPIサーバーのために設定されているservice-cluster-ip-rangeというCIDRレンジ内の有効なIPv4またはIPv6アドレスである必要があります。 もし無効なclusterIPアドレスの値を設定してServiceを作成した場合、問題があることを示すためにAPIサーバーはHTTPステータスコード422を返します。

サービスディスカバリー

Kubernetesは、Serviceオブジェクトを見つけ出すために2つの主要なモードをサポートしています。 - それは環境変数とDNSです。

環境変数

PodがNode上で稼働するとき、kubeletはアクティブな各Serviceに対して、環境変数のセットを追加します。 これはDocker links互換性のある変数( makeLinkVariables関数を確認してください)や、より簡単な{SVCNAME}_SERVICE_HOSTや、{SVCNAME}_SERVICE_PORT変数をサポートします。この変数名で使われるService名は大文字に変換され、-_に変換されます。

例えば、TCPポート6379番を公開していて、さらにclusterIPが10.0.0.11に割り当てられているredis-masterというServiceは、下記のような環境変数を生成します。

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

DNS

ユーザーはアドオンを使ってKubernetesクラスターにDNS Serviceをセットアップできます(常にセットアップすべきです)。

CoreDNSなどのクラスター対応のDNSサーバーは新しいServiceや、各Service用のDNSレコードのセットのためにKubernetes APIを常に監視します。 もしクラスターを通してDNSが有効になっている場合、全てのPodはDNS名によって自動的にServiceに対する名前解決をするようにできるはずです。

例えば、Kubernetesのmy-nsというNamespace内でmy-serviceというServiceがある場合、KubernetesコントロールプレーンとDNS Serviceが協調して動作し、my-service.my-nsというDNSレコードを作成します。 my-nsというNamespace内のPodはmy-serviceという名前で簡単に名前解決できるはずです(my-service.my-nsでも動作します)。

他のNamespace内でのPodはmy-service.my-nsといった形で指定しなくてはなりません。これらのDNS名は、そのServiceのclusterIPに名前解決されます。

Kubernetesは名前付きのポートに対するDNS SRV(Service)レコードもサポートしています。もしmy-service.my-nsというServiceがhttpという名前のTCPポートを持っていた場合、IPアドレスと同様に、httpのポート番号を探すために_http._tcp.my-service.my-nsというDNS SRVクエリを実行できます。

KubernetesのDNSサーバーはExternalName Serviceにアクセスする唯一の方法です。 DNS Pods と ServiceにてExternalNameによる名前解決に関するさらなる情報を確認できます。

Headless Service

場合によっては、負荷分散と単一のService IPは不要です。このケースにおいて、clusterIP(.spec.clusterIP)の値を"None"に設定することにより、"Headless"とよばれるServiceを作成できます。

ユーザーは、Kubernetesの実装と紐づくことなく、他のサービスディスカバリーのメカニズムと連携するためにHeadless Serviceを使用できます。 例えば、ユーザーはこのAPI上でカスタムオペレーターを実装することができます。

このServiceにおいては、clusterIPは割り当てられず、kube-proxyはこのServiceをハンドリングしないのと、プラットフォームによって行われるはずの ロードバランシングやプロキシとしての処理は行われません。DNSがどのように自動で設定されるかは、定義されたServiceが定義されたラベルセレクターを持っているかどうかに依存します。

ラベルセレクターの利用

ラベルセレクターを定義したHeadless Serviceにおいて、EndpointsコントローラーはAPIにおいてEndpointsレコードを作成し、ServiceのバックエンドにあるPodへのIPを直接指し示すためにDNS設定を修正します。

ラベルセレクターなしの場合

ラベルセレクターを定義しないHeadless Serviceにおいては、EndpointsコントローラーはEndpointsレコードを作成しません。 しかしDNSのシステムは下記の2つ両方を探索し、設定します。

  • ExternalNameタイプのServiceに対するCNAMEレコード
  • 他の全てのServiceタイプを含む、Service名を共有している全てのEndpointsレコード

Serviceの公開 (Serviceのタイプ)

ユーザーのアプリケーションのいくつかの部分において(例えば、frontendsなど)、ユーザーのクラスターの外部にあるIPアドレス上でServiceを公開したい場合があります。

KubernetesのServiceTypesによって、ユーザーがどのような種類のServiceを使いたいかを指定することが可能です。 デフォルトではClusterIPとなります。

Type項目の値と、そのふるまいは以下のようになります。

  • ClusterIP: クラスター内部のIPでServiceを公開する。このタイプではServiceはクラスター内部からのみ疎通性があります。このタイプはデフォルトのServiceTypeです。
  • NodePort: 各NodeのIPにて、静的なポート(NodePort)上でServiceを公開します。そのNodePort のServiceが転送する先のClusterIP Serviceが自動的に作成されます。<NodeIP>:<NodePort>にアクセスすることによってNodePort Serviceにアクセスできるようになります。
  • LoadBalancer: クラウドプロバイダーのロードバランサーを使用して、Serviceを外部に公開します。クラスター外部にあるロードバランサーが転送する先のNodePortClusterIP Serviceは自動的に作成されます。
  • ExternalName: CNAMEレコードを返すことにより、externalNameフィールドに指定したコンテンツ(例: foo.bar.example.com)とServiceを紐づけます。しかし、いかなる種類のプロキシも設定されません。

また、Serviceを公開するためにIngressも利用可能です。IngressはServiceのタイプではありませんが、クラスターに対するエントリーポイントとして動作します。 Ingressは同一のIPアドレスにおいて、複数のServiceを公開するように、ユーザーの設定した転送ルールを1つのリソースにまとめることができます。

NodePort タイプ

もしtypeフィールドの値をNodePortに設定すると、Kubernetesコントロールプレーンは--service-node-port-rangeフラグによって指定されたレンジのポート(デフォルト: 30000-32767)を割り当てます。 各Nodeはそのポート(各Nodeで同じポート番号)への通信をServiceに転送します。 作成したServiceは、.spec.ports[*].nodePortフィールド内に割り当てられたポートを記述します。

もしポートへの通信を転送する特定のIPを指定したい場合、特定のIPブロックをkube-proxyの--nodeport-addressフラグで指定できます。これはKubernetes v1.10からサポートされています。 このフラグは、コンマ区切りのIPブロックのリスト(例: 10.0.0./8, 192.0.2.0/25)を使用し、kube-proxyがこのNodeに対してローカルとみなすべきIPアドレスの範囲を指定します。

例えば、--nodeport-addresses=127.0.0.0/8というフラグによってkube-proxyを起動した時、kube-proxyはNodePort Serviceのためにループバックインターフェースのみ選択します。--nodeport-addressesのデフォルト値は空のリストになります。これはkube-proxyがNodePort Serviceに対して全てのネットワークインターフェースを利用可能とするべきということを意味します(これは以前のKubernetesのバージョンとの互換性があります)。

もしポート番号を指定したい場合、nodePortフィールドに値を指定できます。コントロールプレーンは指定したポートを割り当てるか、APIトランザクションが失敗したことを知らせるかのどちらかになります。 これは、ユーザーが自分自身で、ポート番号の衝突に関して気をつける必要があることを意味します。 また、ユーザーは有効なポート番号を指定する必要があり、NodePortの使用において、設定された範囲のポートを指定する必要があります。

NodePortの使用は、Kubernetesによって完全にサポートされていないようなユーザー独自の負荷分散を設定をするための有効な方法や、1つ以上のNodeのIPを直接公開するための方法となりえます。

注意点として、このServiceは<NodeIP>:spec.ports[*].nodePortと、.spec.clusterIP:spec.ports[*].portとして疎通可能です。 (もしkube-proxyにおいて--nodeport-addresssesが設定された場合、はフィルターされたNodeIPとなります。)

例えば:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
      # デフォルトでは利便性のため、 `targetPort` は `port` と同じ値にセットされます。
    - port: 80
      targetPort: 80
      # 省略可能なフィールド
      # デフォルトでは利便性のため、Kubernetesコントロールプレーンはある範囲から1つポートを割り当てます(デフォルト値の範囲:30000-32767)
      nodePort: 30007

LoadBalancer タイプ

外部のロードバランサーをサポートするクラウドプロバイダー上で、typeフィールドにLoadBalancerを設定すると、Service用にロードバランサーがプロビジョニングされます。 実際のロードバランサーの作成は非同期で行われ、プロビジョンされたバランサーの情報は、Serviceの.status.loadBalancerフィールドに記述されます。 例えば:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

外部のロードバランサーからのトラフィックはバックエンドのPodに直接転送されます。クラウドプロバイダーはどのようにそのリクエストをバランシングするかを決めます。

LoadBalancerタイプのサービスで複数のポートが定義されている場合、すべてのポートが同じプロトコルである必要があり、さらにそのプロトコルはTCPUDPSCTPのいずれかである必要があります。

いくつかのクラウドプロバイダーにおいて、loadBalancerIPの設定をすることができます。このようなケースでは、そのロードバランサーはユーザーが指定したloadBalancerIPに対してロードバランサーを作成します。 もしloadBalancerIPフィールドの値が指定されていない場合、そのロードバランサーはエフェメラルなIPアドレスに対して作成されます。もしユーザーがloadBalancerIPを指定したが、使っているクラウドプロバイダーがその機能をサポートしていない場合、そのloadBalancerIPフィールドに設定された値は無視されます。

内部のロードバランサー

複雑な環境において、同一の(仮想)ネットワークアドレスブロック内のServiceからのトラフィックを転送する必要がでてきます。

Split-HorizonなDNS環境において、ユーザーは2つのServiceを外部と内部の両方からのトラフィックをエンドポイントに転送させる必要がでてきます。

ユーザーは、Serviceに対して下記のアノテーションを1つ追加することでこれを実現できます。 追加するアノテーションは、ユーザーが使っているクラウドプロバイダーに依存しています。

タブを選択してください。

[...]
metadata:
    name: my-service
    annotations:
        networking.gke.io/load-balancer-type: "Internal"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/azure-load-balancer-internal: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
[...]

[...]
metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
[...]

AWSにおけるTLSのサポート

AWS上で稼働しているクラスターにおいて、部分的なTLS/SSLのサポートをするには、LoadBalancer Serviceに対して3つのアノテーションを追加できます。

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012

1つ目は、使用する証明書のARNです。これはIAMにアップロードされたサードパーティーが発行した証明書か、AWS Certificate Managerで作成された証明書になります。

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)

2つ目のアノテーションはPodが利用するプロトコルを指定するものです。HTTPSとSSLの場合、ELBはそのPodが証明書を使って暗号化されたコネクションを介して自分自身のPodを認証すると推測します。

HTTPとHTTPSでは、レイヤー7でのプロキシを選択します。ELBはユーザーとのコネクションを切断し、リクエストを転送するときにリクエストヘッダーをパースして、X-Forwarded-ForヘッダーにユーザーのIPを追加します(Podは接続相手のELBのIPアドレスのみ確認可能です)。

TCPとSSLでは、レイヤー4でのプロキシを選択します。ELBはヘッダーの値を変更せずにトラフィックを転送します。

いくつかのポートがセキュアに保護され、他のポートではセキュアでないような混合した環境において、下記のようにアノテーションを使うことができます。

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
        service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443,8443"

上記の例では、もしServiceが804438443と3つのポートを含んでいる場合、4438443はSSL証明書を使いますが、80では単純にHTTPでのプロキシとなります。

Kubernetes v1.9以降のバージョンからは、Serviceのリスナー用にHTTPSやSSLと事前定義されたAWS SSLポリシーを使用できます。 どのポリシーが使用できるかを確認するために、awsコマンドラインツールを使用できます。

aws elb describe-load-balancer-policies --query 'PolicyDescriptions[].PolicyName'

ユーザーは"service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy"というアノテーションを使用することにより、複数のポリシーの中からどれか1つを指定できます。 例えば:

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS-1-2-2017-01"

AWS上でのPROXYプロトコルのサポート

AWS上で稼働するクラスターでPROXY protocolのサポートを有効にするために、下記のServiceのアノテーションを使用できます。

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"

Kubernetesバージョン1.3.0からは、このアノテーションを使用するとELBによってプロキシされた全てのポートが対象になり、そしてそれ以外の場合は構成されません。

AWS上でのELBのアクセスログ

AWS上でのELB Service用のアクセスログを管理するためにはいくつかのアノテーションが使用できます。

service.beta.kubernetes.io/aws-load-balancer-access-log-enabledというアノテーションはアクセスログを有効にするかを設定できます。

service.beta.kubernetes.io/aws-load-balancer-access-log-emit-intervalというアノテーションはアクセスログをパブリッシュするためのインターバル(分)を設定できます。 ユーザーはそのインターバルで5分もしくは60分で設定できます。

service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-nameというアノテーションはロードバランサーのアクセスログが保存されるAmazon S3のバケット名を設定できます。

service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefixというアノテーションはユーザーが作成したAmazon S3バケットの論理的な階層を指定します。

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
        # ロードバランサーのアクセスログが有効かどうか。
        service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
        # アクセスログをパブリッシュするためのインターバル(分)。ユーザーはそのインターバルで5分もしくは60分で設定できます。
        service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
        # ロードバランサーのアクセスログが保存されるAmazon S3のバケット名。
        service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-bucket-prefix/prod"
        # ユーザーが作成したAmazon S3バケットの論理的な階層。例えば: `my-bucket-prefix/prod`

AWSでの接続の中断

古いタイプのELBでの接続の中断は、service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabledというアノテーションを"true"に設定することで管理できます。 service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeoutというアノテーションで、インスタンスを登録解除するまえに既存の接続をオープンにし続けるための最大時間(秒)を指定できます。

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"

他のELBアノテーション

古いタイプのELBを管理するためのアノテーションは他にもあり、下記で紹介します。

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"
        # ロードバランサーによってクローズされる前にアイドル状態(コネクションでデータは送信されない)になれる秒数

        service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
        # ゾーンを跨いだロードバランシングが有効かどうか

        service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "environment=prod,owner=devops"
        # ELBにおいて追加タグとして保存されるキー・バリューのペアのコンマ区切りのリスト

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: ""
        # バックエンドへのトラフィックが正常になったと判断するために必要なヘルスチェックの連続成功数
        # デフォルトでは2 この値は2から10の間で設定可能

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"
        # バックエンドへのトラフィックが異常になったと判断するために必要なヘルスチェックの連続失敗数
        # デフォルトでは6 この値は2から10の間で設定可能

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "20"
        # 各インスタンスのヘルスチェックのおよそのインターバル(秒)
        # デフォルトでは10 この値は5から300の間で設定可能

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "5"
        # ヘルスチェックが失敗したと判断されるレスポンスタイムのリミット(秒)
        # この値はservice.beta.kubernetes.io/aws-load-balancer-healthcheck-intervalの値以下である必要があります。
        # デフォルトでは5 この値は2から60の間で設定可能

        service.beta.kubernetes.io/aws-load-balancer-security-groups: "sg-53fae93f"
        # ELBが作成される際に追加されるセキュリティグループのリスト
        # service.beta.kubernetes.io/aws-load-balancer-extra-security-groupsアノテーションと異なり
        # 元々ELBに付与されていたセキュリティグループを置き換えることになります。

        service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-53fae93f,sg-42efd82e"
        # ELBに追加される予定のセキュリティーグループのリスト

        service.beta.kubernetes.io/aws-load-balancer-target-node-labels: "ingress-gw,gw-name=public-api"
        # ロードバランサーがターゲットノードを指定する際に利用するキーバリューのペアのコンマ区切りリストです。

AWSでのNetwork Load Balancerのサポート

FEATURE STATE: Kubernetes v1.15 [beta]

AWSでNetwork Load Balancerを使用するには、値をnlbに設定してアノテーションservice.beta.kubernetes.io/aws-load-balancer-typeを付与します。

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

古いタイプのElastic Load Balancersとは異なり、Network Load Balancers (NLBs)はクライアントのIPアドレスをNodeに転送します。 もしServiceの.spec.externalTrafficPolicyの値がClusterに設定されていた場合、クライアントのIPアドレスは末端のPodに伝播しません。

.spec.externalTrafficPolicyLocalに設定することにより、クライアントIPアドレスは末端のPodに伝播します。しかし、これにより、トラフィックの分配が不均等になります。 特定のLoadBalancer Serviceに紐づいたPodがないNodeでは、自動的に割り当てられた.spec.healthCheckNodePortに対するNLBのターゲットグループのヘルスチェックが失敗し、トラフィックを全く受信しません。

均等なトラフィックの分配を実現するために、DaemonSetの使用や、同一のNodeに配備しないようにPodのanti-affinityを設定します。

また、内部のロードバランサーのアノテーションとNLB Serviceを使用できます。

NLBの背後にあるインスタンスに対してクライアントのトラフィックを転送するために、Nodeのセキュリティーグループは下記のようなIPルールに従って変更されます。

RuleProtocolPort(s)IpRange(s)IpRange Description
ヘルスチェックTCPNodePort(s) (.spec.healthCheckNodePort for .spec.externalTrafficPolicy = Local)VPC CIDRkubernetes.io/rule/nlb/health=<loadBalancerName>
クライアントのトラフィックTCPNodePort(s).spec.loadBalancerSourceRanges (デフォルト: 0.0.0.0/0)kubernetes.io/rule/nlb/client=<loadBalancerName>
MTUによるサービスディスカバリーICMP3,4.spec.loadBalancerSourceRanges (デフォルト: 0.0.0.0/0)kubernetes.io/rule/nlb/mtu=<loadBalancerName>

どのクライアントIPがNLBにアクセス可能かを制限するためには、loadBalancerSourceRangesを指定してください。

spec:
  loadBalancerSourceRanges:
  - "143.231.0.0/16"

Tencent Kubernetes Engine(TKE)におけるその他のCLBアノテーション

以下に示すように、TKEでCloud Load Balancerを管理するためのその他のアノテーションがあります。

    metadata:
      name: my-service
      annotations:
        # 指定したノードでロードバランサーをバインドします
        service.kubernetes.io/qcloud-loadbalancer-backends-label: key in (value1, value2)

        # 既存のロードバランサーのID
        service.kubernetes.io/tke-existed-lbid: lb-6swtxxxx

        # ロードバランサー(LB)のカスタムパラメーターは、LBタイプの変更をまだサポートしていません
        service.kubernetes.io/service.extensiveParameters: ""

        # LBリスナーのカスタムパラメーター
        service.kubernetes.io/service.listenerParameters: ""

        # ロードバランサーのタイプを指定します
        # 有効な値: classic(Classic Cloud Load Balancer)またはapplication(Application Cloud Load Balancer)
        service.kubernetes.io/loadbalance-type: xxxxx

        # パブリックネットワーク帯域幅の課金方法を指定します
        # 有効な値: TRAFFIC_POSTPAID_BY_HOUR(bill-by-traffic)およびBANDWIDTH_POSTPAID_BY_HOUR(bill-by-bandwidth)
        service.kubernetes.io/qcloud-loadbalancer-internet-charge-type: xxxxxx

        # 帯域幅の値を指定します(値の範囲:[1-2000] Mbps)。
        service.kubernetes.io/qcloud-loadbalancer-internet-max-bandwidth-out: "10"

        # この注釈が設定されている場合、ロードバランサーはポッドが実行されているノードのみを登録します
        # そうでない場合、すべてのノードが登録されます
        service.kubernetes.io/local-svc-only-bind-node-with-pod: true

ExternalName タイプ

ExternalNameタイプのServiceは、ServiceをDNS名とマッピングし、my-servicecassandraというような従来のラベルセレクターとはマッピングしません。 ユーザーはこれらのServiceにおいてspec.externalNameフィールドの値を指定します。

このServiceの定義では、例えばprodというNamespace内のmy-serviceというServiceをmy.database.example.comにマッピングします。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

my-service.prod.svc.cluster.localというホストをルックアップするとき、クラスターのDNS Serviceはmy.database.example.comという値をもつCNAMEレコードを返します。 my-serviceへのアクセスは、他のServiceと同じ方法ですが、再接続する際はプロキシや転送を介して行うよりも、DNSレベルで行われることが決定的に異なる点となります。 後にユーザーが使用しているデータベースをクラスター内に移行することになった場合は、Podを起動させ、適切なラベルセレクターやEndpointsを追加し、Serviceのtypeを変更します。

External IPs

もし1つ以上のクラスターNodeに転送するexternalIPが複数ある場合、Kubernetes ServiceはexternalIPsに指定したIPで公開されます。 そのexternalIP(到達先のIPとして扱われます)のServiceのポートからトラフィックがクラスターに入って来る場合、ServiceのEndpointsのどれか1つに対して転送されます。 externalIPsはKubernetesによって管理されず、それを管理する責任はクラスターの管理者にあります。

Serviceのspecにおいて、externalIPsは他のどのServiceTypesと併用して設定できます。 下記の例では、"my-service"は"198.51.100.32:80" (externalIP:port)のクライアントからアクセス可能です。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  externalIPs:
  - 80.11.12.10

Serviceの欠点

仮想IP用にuserspaceモードのプロキシを使用すると、小規模もしくは中規模のスケールでうまく稼働できますが、1000以上のServiceがあるようなとても大きなクラスターではうまくスケールしません。 これについては、Serviceのデザインプロポーザルにてさらなる詳細を確認できます。

userspaceモードのプロキシの使用は、Serviceにアクセスするパケットの送信元IPアドレスが不明瞭になります。 これは、いくつかの種類のネットワークフィルタリング(ファイアウォールによるフィルタリング)を不可能にします。 iptablesプロキシモードはクラスター内の送信元IPを不明瞭にはしませんが、依然としてロードバランサーやNodePortへ疎通するクライアントに影響があります。

Typeフィールドはネストされた機能としてデザインされています。 - 各レベルの値は前のレベルに対して追加します。 これは全てのクラウドプロバイダーにおいて厳密に要求されていません(例: Google Compute EngineはLoadBalancerを動作させるためにNodePortを割り当てる必要はありませんが、AWSではその必要があります)が、現在のAPIでは要求しています。

仮想IPの実装について

これより前の情報は、ただServiceを使いたいという多くのユーザーにとっては有益かもしれません。しかし、その裏側では多くのことが行われており、理解する価値があります。

衝突の回避

Kubernetesの主要な哲学のうちの一つは、ユーザーは、ユーザー自身のアクションによるミスでないものによって、ユーザーのアクションが失敗するような状況に晒されるべきでないことです。 Serviceリソースの設計において、これはユーザーの指定したポートが衝突する可能性がある場合はそのポートのServiceを作らないことを意味します。これは障害を分離することとなります。

Serviceのポート番号を選択できるようにするために、我々はどの2つのServiceでもポートが衝突しないことを保証します。 Kubernetesは各Serviceに、それ自身のIPアドレスを割り当てることで実現しています。

各Serviceが固有のIPを割り当てられるのを保証するために、内部のアロケーターは、Serviceを作成する前に、etcd内のグローバルの割り当てマップをアトミックに更新します。 そのマップオブジェクトはServiceのIPアドレスの割り当てのためにレジストリー内に存在しなくてはならず、そうでない場合は、Serviceの作成時にIPアドレスが割り当てられなかったことを示すエラーメッセージが表示されます。

コントロールプレーンにおいて、バックグラウンドのコントローラーはそのマップを作成する責務があります(インメモリーのロックが使われていた古いバージョンのKubernetesからのマイグレーションをサポートすることも必要です)。 また、Kubernetesは(例えば、管理者の介入によって)無効な割り当てがされているかをチェックすることと、現時点でどのServiceにも使用されていない割り当て済みIPアドレスのクリーンアップのためにコントローラーを使用します。

ServiceのIPアドレス

実際に固定された向き先であるPodのIPアドレスとは異なり、ServiceのIPは実際には単一のホストによって応答されません。 その代わり、kube-proxyは必要な時に透過的にリダイレクトされる仮想IPアドレスを定義するため、iptables(Linuxのパケット処理ロジック)を使用します。 クライアントがVIPに接続する時、そのトラフィックは自動的に適切なEndpointsに転送されます。 Service用の環境変数とDNSは、Serviceの仮想IPアドレス(とポート)の面において、自動的に生成されます。

kube-proxyは3つの微妙に異なった動作をするプロキシモード— userspace、iptablesとIPVS — をサポートしています。

Userspace

例として、上記で記述されている画像処理のアプリケーションを考えます。 バックエンドのServiceが作成されたとき、KubernetesのMasterは仮想IPを割り当てます。例えば10.0.0.1などです。 そのServiceのポートが1234で、そのServiceはクラスター内の全てのkube-proxyインスタンスによって監視されていると仮定します。 kube-proxyが新しいServiceを見つけた時、kube-proxyは新しいランダムポートをオープンし、その仮想IPアドレスの新しいポートにリダイレクトするようにiptablesを更新し、そのポート上で新しい接続を待ち受けを開始します。

クライアントがServiceの仮想IPアドレスに接続したとき、iptablesルールが有効になり、そのパケットをプロキシ自身のポートにリダイレクトします。 その"Service プロキシ"はバックエンドPodの対象を選択し、クライアントのトラフィックをバックエンドPodに転送します。

これはServiceのオーナーは、衝突のリスクなしに、求めるどのようなポートも選択できることを意味します。 クライアントは単純にそのIPとポートに対して接続すればよく、実際にどのPodにアクセスしているかを意識しません。

iptables

また画像処理のアプリケーションについて考えます。バックエンドServiceが作成された時、そのKubernetesコントロールプレーンは仮想IPアドレスを割り当てます。例えば10.0.0.1などです。 Serviceのポートが1234で、そのServiceがクラスター内のすべてのkube-proxyインスタンスによって監視されていると仮定します。 kube-proxyが新しいServiceを見つけた時、kube-proxyは仮想IPから各Serviceのルールにリダイレクトされるような、iptablesルールのセットをインストールします。 Service毎のルールは、トラフィックをバックエンドにリダイレクト(Destination NATを使用)しているEndpoints毎のルールに対してリンクしています。

クライアントがServiceの仮想IPアドレスに対して接続しているとき、そのiptablesルールが有効になります。 バックエンドのPodが選択され(SessionAffinityに基づくか、もしくはランダムで選択される)、パケットはバックエンドにリダイレクトされます。 userspaceモードのプロキシとは異なり、パケットは決してuserspaceにコピーされず、kube-proxyは仮想IPのために稼働される必要はなく、またNodeでは変更されていないクライアントIPからトラフィックがきます。

このように同じ基本的なフローは、NodePortまたはLoadBalancerを介してトラフィックがきた場合に、実行され、ただクライアントIPは変更されます。

IPVS

iptablesの処理は、大規模なクラスターの場合劇的に遅くなります。例としてはServiceが10,000ほどある場合です。 IPVSは負荷分散のために設計され、カーネル内のハッシュテーブルに基づいています。そのためIPVSベースのkube-proxyによって、多数のServiceがある場合でも一貫して高パフォーマンスを実現できます。 次第に、IPVSベースのkube-proxyは負荷分散のアルゴリズムはさらに洗練されています(最小接続数、位置ベース、重み付け、永続性など)。

APIオブジェクト

ServiceはKubernetesのREST APIにおいてトップレベルのリソースです。ユーザーはそのAPIオブジェクトに関して、Service API objectでさらなる情報を確認できます。

サポートされているプロトコル

TCP

ユーザーはどの種類のServiceにおいてもTCPを利用できます。これはデフォルトのネットワークプロトコルです。

UDP

ユーザーは多くのServiceにおいてUDPを利用できます。 type=LoadBalancerのServiceにおいては、UDPのサポートはこの機能を提供しているクラウドプロバイダーに依存しています。

HTTP

もしクラウドプロバイダーがサポートしている場合、ServiceのEndpointsに転送される外部のHTTP/HTTPSでのリバースプロキシをセットアップするために、LoadBalancerモードでServiceを作成可能です。

PROXY プロトコル

もしクラウドプロバイダーがサポートしている場合、Kubernetesクラスターの外部のロードバランサーを設定するためにLoadBalancerモードでServiceを利用できます。これはPROXY protocolがついた接続を転送します。

ロードバランサーは、最初の一連のオクテットを送信します。 下記のような例となります。

PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n

クライアントからのデータのあとに追加されます。

SCTP

FEATURE STATE: Kubernetes v1.19 [beta]

KubernetesはService、Endpoints、EndpointSlice、NetworkPolicyとPodの定義においてprotocolフィールドの値でSCTPをサポートしています。ベータ版の機能のため、この機能はデフォルトで有効になっています。SCTPをクラスターレベルで無効にするには、クラスター管理者はAPI ServerにおいてSCTPSupport フィーチャーゲート--feature-gates=SCTPSupport=false,…と設定して無効にする必要があります。

そのフィーチャーゲートが有効になった時、ユーザーはService、Endpoints、EndpointSlice、NetworkPolicy、またはPodのprotocolフィールドにSCTPを設定できます。 Kubernetesは、TCP接続と同様に、SCTPアソシエーションに応じてネットワークをセットアップします。

警告

マルチホームSCTPアソシエーションのサポート
type=LoadBalancer Service について
Windows
Userspace kube-proxy

次の項目

3.5.2 - Ingress

FEATURE STATE: Kubernetes v1.19 [stable]

クラスター内のServiceに対する外部からのアクセス(主にHTTP)を管理するAPIオブジェクトです。

Ingressは負荷分散、SSL終端、名前ベースの仮想ホスティングの機能を提供します。

用語

簡単のために、このガイドでは次の用語を定義します。

  • ノード: Kubernetes内のワーカーマシンで、クラスターの一部です。
  • クラスター: Kubernetesによって管理されているコンテナ化されたアプリケーションを実行させるノードの集合です。この例や、多くのKubernetesによるデプロイでは、クラスター内のノードはインターネットに公開されていません。
  • エッジルーター: クラスターでファイアウォールのポリシーを強制するルーターです。クラウドプロバイダーが管理するゲートウェイや、物理的なハードウェアの一部である場合もあります。
  • クラスターネットワーク: 物理的または論理的な繋がりの集合で、Kubernetesのネットワークモデルによって、クラスター内でのコミュニケーションを司るものです。
  • Service: ラベルセレクターを使ったPodの集合を特定するKubernetes Serviceです。特に指定がない限り、Serviceはクラスターネットワーク内でのみ疎通可能な仮想IPを持つものとして扱われます。

Ingressとは何か

Ingressはクラスター外からクラスター内ServiceへのHTTPとHTTPSのルートを公開します。トラフィックのルーティングはIngressリソース上で定義されるルールによって制御されます。

全てのトラフィックを単一のServiceに送る単純なIngressの例を示します。

ingress-diagram

図. Ingress

IngressはServiceに対して、外部疎通できるURL、負荷分散トラフィック、SSL/TLS終端の機能や、名前ベースの仮想ホスティングを提供するように設定できます。Ingressコントローラーは通常はロードバランサーを使用してIngressの機能を実現しますが、エッジルーターや、追加のフロントエンドを構成してトラフィックの処理を支援することもできます。

Ingressは任意のポートやプロトコルを公開しません。HTTPやHTTPS以外のServiceをインターネットに公開する場合、Service.Type=NodePortService.Type=LoadBalancerのServiceタイプを一般的には使用します。

Ingressを使用する上での前提条件

Ingressを提供するためにはIngressコントローラーが必要です。Ingressリソースを作成するのみでは何の効果もありません。

ingress-nginxのようなIngressコントローラーのデプロイが必要な場合があります。いくつかのIngressコントローラーの中から選択してください。

理想的には、全てのIngressコントローラーはリファレンスの仕様を満たすはずです。しかし実際には、各Ingressコントローラーは微妙に異なる動作をします。

Ingressリソース

Ingressリソースの最小構成の例は以下のとおりです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

IngressにはapiVersionkindmetadataspecフィールドが必要です。Ingressオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。設定ファイルに関する一般的な情報は、アプリケーションのデプロイコンテナの設定リソースの管理を参照してください。Ingressでは、Ingressコントローラーに依存しているいくつかのオプションの設定をするためにアノテーションを一般的に使用します。例としては、rewrite-targetアノテーションなどがあります。Ingressコントローラーの種類が異なれば、サポートするアノテーションも異なります。サポートされているアノテーションについて学ぶためには、使用するIngressコントローラーのドキュメントを確認してください。

Ingress Specは、ロードバランサーやプロキシサーバーを設定するために必要な全ての情報を持っています。最も重要なものとして、外部からくる全てのリクエストに対して一致したルールのリストを含みます。IngressリソースはHTTP(S)トラフィックに対してのルールのみサポートしています。

ingressClassNameが省略された場合、デフォルトのIngressClassを定義する必要があります。

デフォルトのIngressClassを定義しなくても動作するIngressコントローラーがいくつかあります。例えば、Ingress-NGINXコントローラーはフラグ --watch-ingress-without-classで設定できます。ただし、下記のようにデフォルトのIngressClassを指定することを推奨します

Ingressのルール

各HTTPルールは以下の情報を含みます。

  • オプションで設定可能なホスト名。上記のリソースの例では、ホスト名が指定されていないので、そのルールは指定されたIPアドレスを経由する全てのインバウンドHTTPトラフィックに適用されます。ホスト名が指定されていると(例: foo.bar.com)、そのルールは指定されたホストに対して適用されます。
  • パスのリスト(例: /testpath)。各パスにはservice.nameservice.port.nameまたはservice.port.numberで定義されるバックエンドが関連づけられます。ロードバランサーがトラフィックを関連づけられたServiceに転送するために、外部からくるリクエストのホスト名とパスが条件と一致させる必要があります。
  • バックエンドはServiceドキュメントに書かれているようなService名とポート名の組み合わせ、またはCRDによるカスタムリソースバックエンドです。Ingressで設定されたホスト名とパスのルールに一致するHTTP(とHTTPS)のリクエストは、リスト内のバックエンドに対して送信されます。

Ingressコントローラーでは、defaultBackendが設定されていることがあります。これはSpec内で指定されているパスに一致しないようなリクエストのためのバックエンドです。

デフォルトのバックエンド

ルールが設定されていないIngressは、全てのトラフィックを単一のデフォルトのバックエンドに転送します。.spec.defaultBackendはその場合にリクエストを処理するバックエンドになります。defaultBackendは、Ingressコントローラーのオプション設定であり、Ingressリソースでは指定されていません。.spec.rulesを設定しない場合、.spec.defaultBackendの設定は必須です。defaultBackendが設定されていない場合、どのルールにもマッチしないリクエストの処理は、Ingressコントローラーに任されます(このケースをどう処理するかは、お使いのIngressコントローラーのドキュメントを参照してください)。

HTTPリクエストがIngressオブジェクトのホスト名とパスの条件に1つも一致しない時、そのトラフィックはデフォルトのバックエンドに転送されます。

リソースバックエンド

ResourceバックエンドはIngressオブジェクトと同じnamespaceにある他のKubernetesリソースを指すObjectRefです。 ResourceはServiceの設定とは排他であるため、両方を指定するとバリデーションに失敗します。 Resourceバックエンドの一般的な用途は、静的なアセットが入ったオブジェクトストレージバックエンドにデータを導入することです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

上記のIngressを作成した後に、次のコマンドで参照することができます。

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Namespace:        default
Address:
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  <none>
Events:       <none>

パスのタイプ

Ingressのそれぞれのパスは対応するパスのタイプを持ちます。pathTypeが明示的に指定されていないパスはバリデーションに通りません。サポートされているパスのタイプは3種類あります。

  • ImplementationSpecific(実装に特有): このパスタイプでは、パスとの一致はIngressClassに依存します。Ingressの実装はこれを独立したpathTypeと扱うことも、PrefixExactと同一のパスタイプと扱うこともできます。

  • Exact: 大文字小文字を区別して完全に一致するURLパスと一致します。

  • Prefix: /で分割されたURLと前方一致で一致します。大文字小文字は区別され、パスの要素対要素で比較されます。パス要素は/で分割されたパスの中のラベルのリストを参照します。リクエストがパス p に一致するのは、Ingressのパス p がリクエストパス p と要素単位で前方一致する場合です。

タイプパスリクエストパス一致するか
Prefix/(全てのパス)はい
Exact/foo/fooはい
Exact/foo/barいいえ
Exact/foo/foo/いいえ
Exact/foo//fooいいえ
Prefix/foo/foo, /foo/はい
Prefix/foo//foo, /foo/はい
Prefix/aaa/bb/aaa/bbbいいえ
Prefix/aaa/bbb/aaa/bbbはい
Prefix/aaa/bbb//aaa/bbbはい、末尾のスラッシュは無視
Prefix/aaa/bbb/aaa/bbb/はい、末尾のスラッシュと一致
Prefix/aaa/bbb/aaa/bbb/cccはい、パスの一部と一致
Prefix/aaa/bbb/aaa/bbbxyzいいえ、接頭辞と一致しない
Prefix/, /aaa/aaa/cccはい、接頭辞/aaaと一致
Prefix/, /aaa, /aaa/bbb/aaa/bbbはい、接頭辞/aaa/bbbと一致
Prefix/, /aaa, /aaa/bbb/cccはい、接頭辞/と一致
Prefix/aaa/cccいいえ、デフォルトバックエンドを使用
Mixed/foo (Prefix), /foo (Exact)/fooはい、Exactが優先

複数のパスとの一致

リクエストがIngressの複数のパスと一致することがあります。そのような場合は、最も長くパスが一致したものが優先されます。2つのパスが同等に一致した場合は、完全一致が前方一致よりも優先されます。

ホスト名のワイルドカード

ホストは正確に一致する(例えばfoo.bar.com)かワイルドカード(例えば*.foo.com)とすることができます。 正確な一致ではHTTPヘッダーのhosthostフィールドと一致することが必要です。 ワイルドカードによる一致では、HTTPヘッダーのhostがワイルドカードルールに沿って後方一致することが必要です。

HostHostヘッダー一致するか
*.foo.combar.foo.com共通の接尾辞により一致
*.foo.combaz.bar.foo.com一致しない。ワイルドカードは単一のDNSラベルのみを対象とする
*.foo.comfoo.com一致しない。ワイルドカードは単一のDNSラベルのみを対象とする
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

Ingress Class

Ingressは異なったコントローラーで実装されうるため、しばしば異なった設定を必要とします。 各Ingressはクラス、つまりIngressClassリソースへの参照を指定する必要があります。IngressClassリソースには、このクラスを実装するコントローラーの名前などの追加設定が含まれています。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

IngressClassの.spec.parametersフィールドを使って、そのIngressClassに関連する設定を持っている別のリソースを参照することができます。

使用するパラメーターの種類は、IngressClassの.spec.controllerフィールドで指定したIngressコントローラーに依存します。

IngressClassスコープ

Ingressコントローラーによっては、クラスター全体で設定したパラメーターを使用できる場合もあれば、1つのNamespaceに対してのみ設定したパラメーターを使用できる場合もあります。

IngressClassパラメーターのデフォルトのスコープは、クラスター全体です。

.spec.parametersフィールドを設定して.spec.parameters.scopeフィールドを設定しなかった場合、または.spec.parameters.scopeClusterに設定した場合、IngressClassはクラスタースコープのリソースを参照します。 パラーメーターのkind(およびapiGroup)はクラスタースコープのAPI(カスタムリソースの場合もあり)を指し、パラメーターのnameはそのAPIの特定のクラスタースコープのリソースを特定します。

例えば:

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-1
spec:
  controller: example.com/ingress-controller
  parameters:
    # このIngressClassのパラメーターは「external-config-1」という名前の
    # ClusterIngressParameter(APIグループk8s.example.net)で指定されています。この定義は、Kubernetesに
    # クラスタースコープのパラメーターリソースを探すように指示しています。
    scope: Cluster
    apiGroup: k8s.example.net
    kind: ClusterIngressParameter
    name: external-config-1

FEATURE STATE: Kubernetes v1.23 [stable]

.spec.parametersフィールドを設定して.spec.parameters.scopeフィールドをNamespaceに設定した場合、IngressClassはNamespaceスコープのリソースを参照します。また.spec.parameters内のnamespaceフィールドには、使用するパラメーターが含まれているNamespaceを設定する必要があります。

パラメーターのkind(およびapiGroup)はNamespaceスコープのAPI(例えば:ConfigMap)を指し、パラメーターのnamenamespaceで指定したNamespace内の特定のリソースを特定します。

Namespaceスコープのパラメーターはクラスターオペレーターがワークロードに使用される設定(例えば:ロードバランサー設定、APIゲートウェイ定義)に対する制御を委譲するのに役立ちます。クラスタースコープパラメーターを使用した場合は以下のいずれかになります:

  • クラスターオペレーターチームは、新しい設定変更が適用されるたびに、別のチームの変更内容を承認する必要があります。
  • クラスターオペレーターは、アプリケーションチームがクラスタースコープのパラメーターリソースに変更を加えることができるように、RBACのRoleやRoleBindingといった、特定のアクセス制御を定義する必要があります。

IngressClass API自体は常にクラスタースコープです。

以下はNamespaceスコープのパラメーターを参照しているIngressClassの例です:

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-2
spec:
  controller: example.com/ingress-controller
  parameters:
    # このIngressClassのパラメーターは「external-config」という名前の
    # IngressParameter(APIグループk8s.example.com)で指定されています。
    # このリソースは「external-configuration」というNamespaceにあります。
    scope: Namespace
    apiGroup: k8s.example.com
    kind: IngressParameter
    namespace: external-configuration
    name: external-config

非推奨のアノテーション

Kubernetes 1.18でIngressClassリソースとingressClassNameフィールドが追加される前は、Ingressの種別はIngressのkubernetes.io/ingress.classアノテーションにより指定されていました。 このアノテーションは正式に定義されたことはありませんが、Ingressコントローラーに広くサポートされています。

Ingressの新しいingressClassNameフィールドはこのアノテーションを置き換えるものですが、完全に等価ではありません。 アノテーションは一般にIngressを実装すべきIngressのコントローラーの名称を示していましたが、フィールドはIngressClassリソースへの参照であり、Ingressのコントローラーの名称を含む追加のIngressの設定情報を含んでいます。

デフォルトのIngressClass

特定のIngressClassをクラスターのデフォルトとしてマークすることができます。 IngressClassリソースのingressclass.kubernetes.io/is-default-classアノテーションをtrueに設定すると、ingressClassNameフィールドが指定されないIngressにはこのデフォルトIngressClassが割り当てられるようになります。

Ingressコントローラーの中には、デフォルトのIngressClassを定義しなくても動作するものがあります。 例えば、Ingress-NGINXコントローラーはフラグ --watch-ingress-without-classで設定することができます。ただし、デフォルトIngressClassを指定することを推奨します:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Ingressのタイプ

単一ServiceのIngress

Kubernetesには、単一のServiceを公開できるようにする既存の概念があります(Ingressの代替案を参照してください)。ルールなしでデフォルトのバックエンド を指定することにより、Ingressでこれを実現することもできます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

kubectl apply -fを実行してIngressを作成すると、その作成したIngressの状態を確認することができます。

kubectl get ingress test-ingress
NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

203.0.113.123はIngressコントローラーによって割り当てられたIPで、作成したIngressを利用するためのものです。

リクエストのシンプルなルーティング

ファンアウト設定では単一のIPアドレスのトラフィックを、リクエストされたHTTP URIに基づいて1つ以上のServiceに転送します。Ingressによってロードバランサーの数を少なくすることができます。例えば、以下のように設定します。

ingress-fanout-diagram

図. Ingressファンアウト

Ingressを以下のように設定します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

Ingressをkubectl apply -fによって作成したとき:

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

IngressコントローラーはService(service1service2)が存在する限り、Ingressの条件を満たす実装固有のロードバランサーを構築します。 構築が完了すると、ADDRESSフィールドでロードバランサーのアドレスを確認できます。

名前ベースのバーチャルホスティング

名前ベースのバーチャルホストは、HTTPトラフィックを同一のIPアドレスの複数のホスト名に転送することをサポートしています。

ingress-namebase-diagram

図. Ingress名前ベースのバーチャルホスティング

以下のIngress設定は、ロードバランサーに対して、Hostヘッダーに基づいてリクエストを転送するように指示するものです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

rules項目でのホストの設定がないIngressを作成すると、IngressコントローラーのIPアドレスに対するwebトラフィックは、要求されている名前ベースのバーチャルホストなしにマッチさせることができます。

例えば、以下のIngressはfirst.bar.comに対するトラフィックをservice1へ、second.foo.comに対するトラフィックをservice2へ、リクエストにおいてホスト名が指定されていない(リクエストヘッダーがないことを意味します)トラフィックはservice3へ転送します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: second.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service3
            port:
              number: 80

TLS

TLSの秘密鍵と証明書を含んだSecretを指定することにより、Ingressをセキュアにできます。Ingressは単一のTLSポートである443番ポートのみサポートし、IngressでTLS終端を行うことを想定しています。IngressからServiceやPodへのトラフィックは平文です。IngressのTLS設定のセクションで異なるホストを指定すると、それらのホストはSNI TLSエクステンション(IngressコントローラーがSNIをサポートしている場合)を介して指定されたホスト名に対し、同じポート上で多重化されます。TLSのSecretはtls.crttls.keyというキーを含む必要があり、TLSを使用するための証明書と秘密鍵を含む値となります。以下がその例です。

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls

IngressでこのSecretを参照すると、クライアントとロードバランサー間の通信にTLSを使用するようIngressコントローラーに指示することになります。作成したTLS Secretは、https-example.foo.comの完全修飾ドメイン名(FQDN)とも呼ばれる共通名(CN)を含む証明書から作成したものであることを確認する必要があります。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

負荷分散

Ingressコントローラーは、負荷分散アルゴリズムやバックエンドの重みスキームなど、すべてのIngressに適用されるいくつかの負荷分散ポリシーの設定とともにブートストラップされます。発展した負荷分散のコンセプト(例: セッションの永続化、動的重み付けなど)はIngressによってサポートされていません。代わりに、それらの機能はService用のロードバランサーを介して利用できます。

ヘルスチェックの機能はIngressによって直接には公開されていませんが、Kubernetesにおいて、同等の機能を提供するReadiness Probeのようなコンセプトが存在することは注目に値します。コントローラーがどのようにヘルスチェックを行うかについては、コントローラーのドキュメントを参照してください(例えばnginx、またはGCE)。

Ingressの更新

リソースを編集することで、既存のIngressに対して新しいホストを追加することができます。

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

このコマンドを実行すると既存の設定をYAMLフォーマットで編集するエディターが表示されます。新しいホストを追加するには、リソースを修正してください。

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: service1
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: bar.baz.com
    http:
      paths:
      - backend:
          service:
            name: service2
            port:
              number: 80
        path: /foo
        pathType: Prefix
..

変更を保存した後、kubectlはAPIサーバー内のリソースを更新し、Ingressコントローラーに対してロードバランサーの再設定を指示します。

変更内容を確認してください。

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   service2:80 (10.8.0.91:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

修正されたIngressのYAMLファイルに対してkubectl replace -fを実行することで、同様の結果を得られます。

アベイラビリティーゾーンをまたいだ障害について

障害のあるドメインをまたいでトラフィックを分散する手法は、クラウドプロバイダーによって異なります。詳細に関して、Ingress コントローラーのドキュメントを参照してください。

Ingressの代替案

Ingressリソースを直接含まずにサービスを公開する方法は複数あります。

次の項目

3.5.3 - サービスとアプリケーションの接続

コンテナを接続するためのKubernetesモデル

継続的に実行され、複製されたアプリケーションの準備ができたので、ネットワーク上で公開することが可能になります。 Kubernetesのネットワークのアプローチについて説明する前に、Dockerの「通常の」ネットワーク手法と比較することが重要です。

デフォルトでは、Dockerはホストプライベートネットワーキングを使用するため、コンテナは同じマシン上にある場合にのみ他のコンテナと通信できます。 Dockerコンテナがノード間で通信するには、マシンのIPアドレスにポートを割り当ててから、コンテナに転送またはプロキシする必要があります。 これは明らかに、コンテナが使用するポートを非常に慎重に調整するか、ポートを動的に割り当てる必要があることを意味します。

コンテナを提供する複数の開発者やチーム間でポートの割り当てを調整することは、規模的に大変困難であり、ユーザが制御できないクラスターレベルの問題にさらされます。 Kubernetesでは、どのホストで稼働するかに関わらず、Podが他のPodと通信できると想定しています。 すべてのPodに独自のクラスタープライベートIPアドレスを付与するため、Pod間のリンクを明示的に作成したり、コンテナポートをホストポートにマップしたりする必要はありません。 これは、Pod内のコンテナがすべてlocalhostの相互のポートに到達でき、クラスター内のすべてのPodがNATなしで相互に認識できることを意味します。 このドキュメントの残りの部分では、このようなネットワークモデルで信頼できるサービスを実行する方法について詳しく説明します。

このガイドでは、シンプルなnginxサーバーを使用して概念実証を示します。

Podをクラスターに公開する

前の例でネットワークモデルを紹介しましたが、再度ネットワークの観点に焦点を当てましょう。 nginx Podを作成し、コンテナポートの仕様を指定していることに注意してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

これにより、クラスター内のどのノードからでもアクセスできるようになります。 Podが実行されているノードを確認します:

kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd

PodのIPを確認します:

kubectl get pods -l run=my-nginx -o yaml | grep podIP
    podIP: 10.244.3.4
    podIP: 10.244.2.5

クラスター内の任意のノードにSSH接続し、両方のIPにcurl接続できるはずです。 コンテナはノードでポート80を使用していないことに注意してください。 また、Podにトラフィックをルーティングする特別なNATルールもありません。 つまり、同じcontainerPortを使用して同じノードで複数のnginx Podを実行し、IPを使用してクラスター内の他のPodやノードからそれらにアクセスできます。 Dockerと同様に、ポートは引き続きホストノードのインターフェースに公開できますが、ネットワークモデルにより、この必要性は根本的に減少します。

興味があれば、これをどのように達成するかについて詳しく読むことができます。

Serviceを作成する

そのため、フラットでクラスター全体のアドレス空間でnginxを実行するPodがあります。 理論的には、これらのPodと直接通信することができますが、ノードが停止するとどうなりますか? Podはそれで死に、Deploymentは異なるIPを持つ新しいものを作成します。 これは、Serviceが解決する問題です。

Kubernetes Serviceは、クラスター内のどこかで実行されるPodの論理セットを定義する抽象化であり、すべて同じ機能を提供します。 作成されると、各Serviceには一意のIPアドレス(clusterIPとも呼ばれます)が割り当てられます。 このアドレスはServiceの有効期間に関連付けられており、Serviceが動作している間は変更されません。 Podは、Serviceと通信するように構成でき、Serviceへの通信は、ServiceのメンバーであるPodに自動的に負荷分散されることを認識できます。

2つのnginxレプリカのサービスをkubectl exposeで作成できます:

kubectl expose deployment/my-nginx
service/my-nginx exposed

これは次のyamlをkubectl apply -fすることと同等です:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

この仕様は、run:my-nginxラベルを持つ任意のPodのTCPポート80をターゲットとするサービスを作成し、抽象化されたサービスポートでPodを公開します(targetPort:はコンテナがトラフィックを受信するポート、port:は抽象化されたServiceのポートであり、他のPodがServiceへのアクセスに使用する任意のポートにすることができます)。 サービス定義でサポートされているフィールドのリストはService APIオブジェクトを参照してください。

Serviceを確認します:

kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

前述のように、ServiceはPodのグループによってサポートされています。 これらのPodはエンドポイントを通じて公開されます。 Serviceのセレクターは継続的に評価され、結果はmy-nginxという名前のEndpointsオブジェクトにPOSTされます。 Podが終了すると、エンドポイントから自動的に削除され、Serviceのセレクターに一致する新しいPodが自動的にエンドポイントに追加されます。 エンドポイントを確認し、IPが最初のステップで作成されたPodと同じであることを確認します:

kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP Family Policy:    SingleStack
IP Families:         IPv4
IP:                  10.0.162.149
IPs:                 10.0.162.149
Port:                <unset> 80/TCP
TargetPort:          80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get ep my-nginx
NAME       ENDPOINTS                     AGE
my-nginx   10.244.2.5:80,10.244.3.4:80   1m

クラスター内の任意のノードから、<CLUSTER-IP>:<PORT>でnginx Serviceにcurl接続できるようになりました。 Service IPは完全に仮想的なもので、ホスト側のネットワークには接続できないことに注意してください。 この仕組みに興味がある場合は、サービスプロキシの詳細をお読みください。

Serviceにアクセスする

Kubernetesは、環境変数とDNSの2つの主要なService検索モードをサポートしています。 前者はそのまま使用でき、後者はCoreDNSクラスターアドオンを必要とします。

環境変数

ノードでPodが実行されると、kubeletはアクティブな各サービスの環境変数のセットを追加します。 これにより、順序付けの問題が発生します。 理由を確認するには、実行中のnginx Podの環境を調べます(Pod名は環境によって異なります):

kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

サービスに言及がないことに注意してください。これは、サービスの前にレプリカを作成したためです。 これのもう1つの欠点は、スケジューラーが両方のPodを同じマシンに配置し、サービスが停止した場合にサービス全体がダウンする可能性があることです。 2つのPodを強制終了し、Deploymentがそれらを再作成するのを待つことで、これを正しい方法で実行できます。 今回は、サービスはレプリカの「前」に存在します。 これにより、スケジューラーレベルのサービスがPodに広がり(すべてのノードの容量が等しい場合)、適切な環境変数が提供されます:

kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;

kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m

Podは強制終了されて再作成されるため、異なる名前が付いていることに気付くでしょう。

kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

DNS

Kubernetesは、DNS名を他のServiceに自動的に割り当てるDNSクラスターアドオンサービスを提供します。 クラスターで実行されているかどうかを確認できます:

kubectl get services kube-dns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m

このセクションの残りの部分は、寿命の長いIP(my-nginx)を持つServiceと、そのIPに名前を割り当てたDNSサーバーがあることを前提にしています。ここではCoreDNSクラスターアドオン(アプリケーション名: kube-dns)を使用しているため、標準的なメソッド(gethostbyname()など) を使用してクラスター内の任意のPodからServiceに通信できます。CoreDNSが起動していない場合、CoreDNS READMEまたはInstalling CoreDNSを参照し、有効にする事ができます。curlアプリケーションを実行して、これをテストしてみましょう。

kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt

次に、Enterキーを押してnslookup my-nginxを実行します:

[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      my-nginx
Address 1: 10.0.162.149

Serviceを安全にする

これまでは、クラスター内からnginxサーバーにアクセスしただけでした。 サービスをインターネットに公開する前に、通信チャネルが安全であることを確認する必要があります。 これには、次のものが必要です:

  • https用の自己署名証明書(既にID証明書を持っている場合を除く)
  • 証明書を使用するように構成されたnginxサーバー
  • Podが証明書にアクセスできるようにするSecret

これらはすべてnginx httpsの例から取得できます。 これにはツールをインストールする必要があります。 これらをインストールしたくない場合は、後で手動の手順に従ってください。つまり:

make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           kubernetes.io/tls                     2         1m

configmapも作成します:

kubectl create configmap nginxconfigmap --from-file=default.conf
configmap/nginxconfigmap created
kubectl get configmaps
NAME             DATA   AGE
nginxconfigmap   1      114s

以下は、(Windows上など)makeの実行で問題が発生した場合に実行する手動の手順です:

# 公開秘密鍵ペアを作成します
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
# キーをbase64エンコードに変換します
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64

前のコマンドの出力を使用して、次のようにyamlファイルを作成します。 base64でエンコードされた値はすべて1行である必要があります。

apiVersion: "v1"
kind: "Secret"
metadata:
  name: "nginxsecret"
  namespace: "default"
  type: kubernetes.io/tls
data:
  nginx.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
  nginx.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

ファイルを使用してSecretを作成します:

kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           kubernetes.io/tls                     2         1m

次に、nginxレプリカを変更して、シークレットの証明書とServiceを使用してhttpsサーバーを起動し、両方のポート(80と443)を公開します:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        - containerPort: 443
        - containerPort: 80
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume

nginx-secure-appマニフェストに関する注目すべき点:

  • 同じファイルにDeploymentとServiceの両方が含まれています。
  • nginxサーバーはポート80のHTTPトラフィックと443のHTTPSトラフィックを処理し、nginx Serviceは両方のポートを公開します。
  • 各コンテナは/etc/nginx/sslにマウントされたボリュームを介してキーにアクセスできます。 これは、nginxサーバーが起動する前にセットアップされます。
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml

この時点で、任意のノードからnginxサーバーに到達できます。

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

最後の手順でcurlに-kパラメーターを指定したことに注意してください。 これは、証明書の生成時にnginxを実行しているPodについて何も知らないためです。 CNameの不一致を無視するようcurlに指示する必要があります。 Serviceを作成することにより、証明書で使用されるCNameを、Service検索中にPodで使用される実際のDNS名にリンクしました。 これをPodからテストしましょう(簡単にするために同じシークレットを再利用しています。PodはServiceにアクセスするためにnginx.crtのみを必要とします):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-deployment
spec:
  selector:
    matchLabels:
      app: curlpod
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: curlpod
        command:
        - sh
        - -c
        - while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
...
<title>Welcome to nginx!</title>
...

Serviceを公開する

アプリケーションの一部では、Serviceを外部IPアドレスに公開したい場合があります。 Kubernetesは、NodePortとLoadBalancerの2つの方法をサポートしています。 前のセクションで作成したServiceはすでにNodePortを使用しているため、ノードにパブリックIPがあれば、nginx HTTPSレプリカはインターネット上のトラフィックを処理する準備ができています。

kubectl get svc my-nginx -o yaml | grep nodePort -C 5
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 31704
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: https
    nodePort: 32453
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>

クラウドロードバランサーを使用するようにサービスを再作成しましょう。 my-nginxサービスのTypeNodePortからLoadBalancerに変更するだけです:

kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   LoadBalancer   10.0.162.149     xx.xxx.xxx.xxx     8080:30163/TCP        21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>

EXTERNAL-IP列のIPアドレスは、パブリックインターネットで利用可能なものです。 CLUSTER-IPは、クラスター/プライベートクラウドネットワーク内でのみ使用できます。

AWSでは、type LoadBalancerはIPではなく(長い)ホスト名を使用するELBが作成されます。 実際、標準のkubectl get svcの出力に収まるには長すぎるので、それを確認するにはkubectl describe service my-nginxを実行する必要があります。 次のようなものが表示されます:

kubectl describe service my-nginx
...
LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...

次の項目

3.5.4 - Ingressコントローラー

クラスターでIngressを動作させるためには、ingress controller が動作している必要があります。 少なくとも1つのIngressコントローラーを選択し、クラスター内にセットアップされていることを確認する必要があります。 このページはデプロイ可能な一般的なIngressコントローラーをリストアップします。

Ingressリソースが動作するためには、クラスターでIngressコントローラーが実行されている必要があります。

kube-controller-managerバイナリの一部として実行される他のタイプのコントローラーとは異なり、Ingressコントローラーはクラスターで自動的に起動されません。このページを使用して、クラスターに最適なIngressコントローラーの実装を選択してください。

プロジェクトとしてのKubernetesは現在、AWSGCE、およびnginxのIngressコントローラーをサポート・保守しています。

追加のコントローラー

複数のIngressコントローラーの使用

Ingress Classを使用して、複数のIngressコントローラーをクラスターにデプロイすることができます。 Ingress Classリソースの.metadata.nameに注目してください。 Ingressを作成する際には、IngressオブジェクトでingressClassNameフィールドを指定するために、その名前が必要になります(IngressSpec v1 referenceを参照)。 ingressClassNameは古いannotation methodの代替品です。

Ingressに対してIngressClassを指定せず、クラスターにはデフォルトとして設定されたIngressClassが1つだけある場合、KubernetesはIngressにクラスターのデフォルトIngressClassを適用します。 IngressClassのingressclass.kubernetes.io/is-default-classアノテーションを文字列"true"に設定することで、デフォルトとしてIngressClassを設定します。

理想的には、すべてのIngressコントローラーはこの仕様を満たすべきですが、いくつかのIngressコントローラーはわずかに異なる動作をします。

次の項目

3.5.5 - EndpointSlice

FEATURE STATE: Kubernetes v1.17 [beta]

EndpointSliceは、Kubernetesクラスター内にあるネットワークエンドポイントを追跡するための単純な手段を提供します。EndpointSliceは、よりスケーラブルでより拡張可能な、Endpointの代わりとなるものです。

動機

Endpoint APIはKubernetes内のネットワークエンドポイントを追跡する単純で直観的な手段を提供してきました。 残念ながら、KubernetesクラスターやServiceが大規模になり、より多くのトラフィックを処理し、より多くのバックエンドPodに送信するようになるにしたがって、Endpoint APIの限界が明らかになってきました。 最も顕著な問題の1つに、ネットワークエンドポイントの数が大きくなったときのスケーリングの問題があります。

Serviceのすべてのネットワークエンドポイントが単一のEndpointリソースに格納されていたため、リソースのサイズが非常に大きくなる場合がありました。これがKubernetesのコンポーネント(特に、マスターコントロールプレーン)の性能に悪影響を与え、結果として、Endpointに変更があるたびに、大量のネットワークトラフィックと処理が発生するようになってしまいました。EndpointSliceは、この問題を緩和するとともに、トポロジカルルーティングなどの追加機能のための拡張可能なプラットフォームを提供します。

EndpointSliceリソース

Kubernetes内ではEndpointSliceにはネットワークエンドポイントの集合へのリファレンスが含まれます。 コントロールプレーンは、セレクターが指定されているKubernetes ServiceのEndpointSliceを自動的に作成します。 これらのEndpointSliceには、Serviceセレクターに一致するすべてのPodへのリファレンスが含まれています。 EndpointSliceは、プロトコル、ポート番号、およびサービス名の一意の組み合わせによってネットワークエンドポイントをグループ化します。 EndpointSliceオブジェクトの名前は有効なDNSサブドメイン名である必要があります。

一例として、以下にexampleというKubernetes Serviceに対するサンプルのEndpointSliceリソースを示します。

apiVersion: discovery.k8s.io/v1beta1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    topology:
      kubernetes.io/hostname: node-1
      topology.kubernetes.io/zone: us-west2-a

デフォルトでは、コントロールプレーンはEndpointSliceを作成・管理し、それぞれのエンドポイント数が100以下になるようにします。--max-endpoints-per-slicekube-controller-managerフラグを設定することで、最大1000個まで設定可能です。

EndpointSliceは内部トラフィックのルーティング方法に関して、kube-proxyに対する唯一のソース(source of truth)として振る舞うことができます。EndpointSliceを有効にすれば、非常に多数のエンドポイントを持つServiceに対して性能向上が得られるはずです。

アドレスの種類

EndpointSliceは次の3種類のアドレスをサポートします。

  • IPv4
  • IPv6
  • FQDN (Fully Qualified Domain Name、完全修飾ドメイン名)

トポロジー

EndpointSliceに属する各エンドポイントは、関連するトポロジーの情報を持つことができます。この情報は、エンドポイントの場所を示すために使われ、対応するNode、ゾーン、リージョンに関する情報が含まれます。 値が利用できる場合には、コントロールプレーンはEndpointSliceコントローラーに次のようなTopologyラベルを設定します。

  • kubernetes.io/hostname - このエンドポイントが存在するNodeの名前。
  • topology.kubernetes.io/zone - このエンドポイントが存在するゾーン。
  • topology.kubernetes.io/region - このエンドポイントが存在するリージョン。

これらのラベルの値はスライス内の各エンドポイントと関連するリソースから継承したものです。hostnameラベルは対応するPod上のNodeNameフィールドの値を表します。zoneとregionラベルは対応するNode上の同じ名前のラベルの値を表します。

管理

ほとんどの場合、コントロールプレーン(具体的には、EndpointSlice コントローラー)は、EndpointSliceオブジェクトを作成および管理します。EndpointSliceには、サービスメッシュの実装など、他のさまざまなユースケースがあり、他のエンティティまたはコントローラーがEndpointSliceの追加セットを管理する可能性があります。

複数のエンティティが互いに干渉することなくEndpointSliceを管理できるようにするために、KubernetesはEndpointSliceを管理するエンティティを示すendpointslice.kubernetes.io/managed-byというラベルを定義します。 EndpointSliceを管理するその他のエンティティも同様に、このラベルにユニークな値を設定する必要があります。

所有権

ほとんどのユースケースでは、EndpointSliceはエンドポイントスライスオブジェクトがエンドポイントを追跡するServiceによって所有されます。 これは、各EndpointSlice上のownerリファレンスとkubernetes.io/service-nameラベルによって示されます。これにより、Serviceに属するすべてのEndpointSliceを簡単に検索できるようになっています。

EndpointSliceのミラーリング

場合によっては、アプリケーションはカスタムEndpointリソースを作成します。これらのアプリケーションがEndpointリソースとEndpointSliceリソースの両方に同時に書き込む必要がないようにするために、クラスターのコントロールプレーンは、ほとんどのEndpointリソースを対応するEndpointSliceにミラーリングします。

コントロールプレーンは、次の場合を除いて、Endpointリソースをミラーリングします。

  • Endpointリソースのendpointslice.kubernetes.io/skip-mirrorラベルがtrueに設定されています。
  • Endpointリソースがcontrol-plane.alpha.kubernetes.io/leaderアノテーションを持っています。
  • 対応するServiceリソースが存在しません。
  • 対応するServiceリソースには、nil以外のセレクターがあります。

個々のEndpointリソースは、複数のEndpointSliceに変換される場合があります。これは、Endpointリソースに複数のサブセットがある場合、または複数のIPファミリ(IPv4およびIPv6)を持つエンドポイントが含まれている場合に発生します。サブセットごとに最大1000個のアドレスがEndpointSliceにミラーリングされます。

EndpointSliceの分散

それぞれのEndpointSliceにはポートの集合があり、リソース内のすべてのエンドポイントに適用されます。サービスが名前付きポートを使用した場合、Podが同じ名前のポートに対して、結果的に異なるターゲットポート番号が使用されて、異なるEndpointSliceが必要になる場合があります。これはサービスの部分集合がEndpointにグループ化される場合と同様です。

コントロールプレーンはEndpointSliceをできる限り充填しようとしますが、積極的にリバランスを行うことはありません。コントローラーのロジックは極めて単純で、以下のようになっています。

  1. 既存のEndpointSliceをイテレートし、もう必要のないエンドポイントを削除し、変更があったエンドポイントを更新する。
  2. 前のステップで変更されたEndpointSliceをイテレートし、追加する必要がある新しいエンドポイントで充填する。
  3. まだ追加するべき新しいエンドポイントが残っていた場合、これまで変更されなかったスライスに追加を試み、その後、新しいスライスを作成する。

ここで重要なのは、3番目のステップでEndpointSliceを完全に分散させることよりも、EndpointSliceの更新を制限することを優先していることです。たとえば、もし新しい追加するべきエンドポイントが10個あり、2つのEndpointSliceにそれぞれ5個の空きがあった場合、このアプローチでは2つの既存のEndpointSliceを充填する代わりに、新しいEndpointSliceが作られます。言い換えれば、1つのEndpointSliceを作成する方が複数のEndpointSliceを更新するよりも好ましいということです。

各Node上で実行されているkube-proxyはEndpointSliceを監視しており、EndpointSliceに加えられた変更はクラスター内のすべてのNodeに送信されるため、比較的コストの高い処理になります。先ほどのアプローチは、たとえ複数のEndpointSliceが充填されない結果となるとしても、すべてのNodeへ送信しなければならない変更の数を抑制することを目的としています。

現実的には、こうしたあまり理想的ではない分散が発生することは稀です。EndpointSliceコントローラーによって処理されるほとんどの変更は、既存のEndpointSliceに収まるほど十分小さくなるためです。そうでなかったとしても、すぐに新しいEndpointSliceが必要になる可能性が高いです。また、Deploymentのローリングアップデートが行われれば、自然な再充填が行われます。Podとそれに対応するエンドポイントがすべて置換されるためです。

エンドポイントの重複

EndpointSliceの変更の性質上、エンドポイントは同時に複数のEndpointSliceで表される場合があります。 これは、さまざまなEndpointSliceオブジェクトへの変更が、さまざまな時間にKubernetesクライアントのウォッチ/キャッシュに到達する可能性があるために自然に発生します。 EndpointSliceを使用する実装では、エンドポイントを複数のスライスに表示できる必要があります。 エンドポイント重複排除を実行する方法のリファレンス実装は、kube-proxyEndpointSliceCache実装にあります。

次の項目

3.5.6 - ネットワークポリシー

IPアドレスまたはポートのレベル(OSI参照モデルのレイヤー3または4)でトラフィックフローを制御したい場合、クラスター内の特定のアプリケーションにKubernetesのネットワークポリシーを使用することを検討してください。ネットワークポリシーはアプリケーション中心の構造であり、Podがネットワークを介して多様な「エンティティ」(「Endpoint」や「Service」のようなKubernetesに含まれる特定の意味を持つ共通の用語との重複を避けるため、ここではエンティティという単語を使用します。)と通信する方法を指定できます。

Podが通信できるエンティティは以下の3つの識別子の組み合わせによって識別されます。

  1. 許可されている他のPod(例外: Podはそれ自体へのアクセスをブロックできません)
  2. 許可されている名前空間
  3. IPブロック(例外: PodまたはノードのIPアドレスに関係なく、Podが実行されているノードとの間のトラフィックは常に許可されます。)

Podベースもしくは名前空間ベースのネットワークポリシーを定義する場合、セレクターを使用してセレクターに一致するPodとの間で許可されるトラフィックを指定します。

一方でIPベースのネットワークポリシーが作成されると、IPブロック(CIDRの範囲)に基づいてポリシーが定義されます。

前提条件

ネットワークポリシーは、ネットワークプラグインにより実装されます。ネットワークポリシーを使用するには、NetworkPolicyをサポートするネットワークソリューションを使用しなければなりません。ネットワークポリシーを実装したコントローラーを使用せずにNetworkPolicyリソースを作成した場合は、何も効果はありません。

分離されたPodと分離されていないPod

デフォルトでは、Podは分離されていない状態(non-isolated)となるため、すべてのソースからのトラフィックを受信します。

Podを選択するNetworkPolicyが存在すると、Podは分離されるようになります。名前空間内に特定のPodを選択するNetworkPolicyが1つでも存在すると、そのPodはいずれかのNetworkPolicyで許可されていないすべての接続を拒否するようになります。(同じ名前空間内のPodでも、どのNetworkPolicyにも選択されなかった他のPodは、引き続きすべてのトラフィックを許可します。)

ネットワークポリシーは追加式であるため、競合することはありません。複数のポリシーがPodを選択する場合、そのPodに許可されるトラフィックは、それらのポリシーのingress/egressルールの和集合で制限されます。したがって、評価の順序はポリシーの結果には影響がありません。

NetworkPolicyリソース

リソースの完全な定義については、リファレンスのNetworkPolicyのセクションを参照してください。

以下は、NetworkPolicyの一例です。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

必須フィールド: 他のKubernetesの設定と同様に、NetworkPolicyにもapiVersionkindmetadataフィールドが必須です。設定ファイルの扱い方に関する一般的な情報については、ConfigMapを使用してコンテナを構成するオブジェクト管理を参照してください。

spec: NetworkPolicyのspecを見ると、指定した名前空間内で特定のネットワークポリシーを定義するのに必要なすべての情報が確認できます。

podSelector: 各NetworkPolicyには、ポリシーを適用するPodのグループを選択するpodSelectorが含まれます。ポリシーの例では、ラベル"role=db"を持つPodを選択しています。podSelectorを空にすると、名前空間内のすべてのPodが選択されます。

policyTypes: 各NetworkPolicyには、policyTypesとして、IngressEgress、またはその両方からなるリストが含まれます。policyTypesフィールドでは、指定したポリシーがどの種類のトラフィックに適用されるかを定めます。トラフィックの種類としては、選択したPodへの内向きのトラフィック(Ingress)、選択したPodからの外向きのトラフィック(Egress)、またはその両方を指定します。policyTypesを指定しなかった場合、デフォルトで常に Ingressが指定され、NetworkPolicyにegressルールが1つでもあればEgressも設定されます。

ingress: 各NetworkPolicyには、許可するingressルールのリストを指定できます。各ルールは、fromおよびportsセクションの両方に一致するトラフィックを許可します。ポリシーの例には1つのルールが含まれ、このルールは、3つのソースのいずれかから送信された1つのポート上のトラフィックに一致します。1つ目のソースはipBlockで、2つ目のソースはnamespaceSelectorで、3つ目のソースはpodSelectorでそれぞれ定められます。

egress: 各NetworkPolicyには、許可するegressルールのリストを指定できます。各ルールは、toおよびportsセクションの両方に一致するトラフィックを許可します。ポリシーの例には1つのルールが含まれ、このルールは、1つのポート上で10.0.0.0/24の範囲内の任意の送信先へ送られるトラフィックに一致します。

したがって、上のNetworkPolicyの例では、次のようにネットワークポリシーを適用します。

  1. "default"名前空間内にある"role=db"のPodを、内向きと外向きのトラフィックに対して分離する(まだ分離されていない場合)
  2. (Ingressルール) "default"名前空間内の"role=db"ラベルが付いたすべてのPodのTCPの6379番ポートへの接続のうち、次の送信元からのものを許可する
    • "default"名前空間内のラベル"role=frontend"が付いたすべてのPod
    • ラベル"project=myproject"が付いた名前空間内のすべてのPod
    • 172.17.0.0–172.17.0.255および172.17.2.0–172.17.255.255(言い換えれば、172.17.1.0/24の範囲を除く172.17.0.0/16)の範囲内のすべてのIPアドレス
  3. (Egressルール) "role=db"というラベルが付いた"default"名前空間内のすべてのPodからの、TCPの5978番ポート上でのCIDR 10.0.0.0/24への接続を許可する

追加の例については、ネットワークポリシーを宣言するの説明を参照してください。

tofromのセレクターの振る舞い

ingressfromセクションまたはegresstoセクションに指定できるセレクターは4種類あります。

podSelector: NetworkPolicyと同じ名前空間内の特定のPodを選択して、ingressの送信元またはegressの送信先を許可します。

namespaceSelector: 特定の名前空間を選択して、その名前空間内のすべてのPodについて、ingressの送信元またはegressの送信先を許可します。

namespaceSelector および podSelector: 1つのtoまたはfromエントリーでnamespaceSelectorpodSelectorの両方を指定して、特定の名前空間内の特定のPodを選択します。正しいYAMLの構文を使うように気をつけてください。このポリシーの例を以下に示します。

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

このポリシーには、1つのfrom要素があり、ラベルuser=aliceの付いた名前空間内にある、ラベルrole=clientの付いたPodからの接続を許可します。しかし、以下のポリシーには注意が必要です。

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

このポリシーには、from配列の中に2つの要素があります。そのため、ラベルrole=clientの付いた名前空間内にあるすべてのPodからの接続、または、任意の名前空間内にあるラベルuser=aliceの付いたすべてのPodからの接続を許可します。

正しいルールになっているか自信がないときは、kubectl describeを使用すると、Kubernetesがどのようにポリシーを解釈したのかを確認できます。

ipBlock: 特定のIPのCIDRの範囲を選択して、ingressの送信元またはegressの送信先を許可します。PodのIPは一時的なもので予測できないため、ここにはクラスター外のIPを指定するべきです。

クラスターのingressとegressの仕組みはパケットの送信元IPや送信先IPの書き換えを必要とすることがよくあります。その場合、NetworkPolicyの処理がIPの書き換えの前後どちらで行われるのかは定義されていません。そのため、ネットワークプラグイン、クラウドプロバイダー、Serviceの実装などの組み合わせによっては、動作が異なる可能性があります。

内向きのトラフィックの場合は、実際のオリジナルの送信元IPに基づいてパケットをフィルタリングできる可能性もあれば、NetworkPolicyが対象とする「送信元IP」がLoadBalancerやPodのノードなどのIPになってしまっている可能性もあることになります。

外向きのトラフィックの場合は、クラスター外のIPに書き換えられたPodからServiceのIPへの接続は、ipBlockベースのポリシーの対象になる場合とならない場合があることになります。

デフォルトのポリシー

デフォルトでは、名前空間にポリシーが存在しない場合、その名前空間内のPodの内向きと外向きのトラフィックはすべて許可されます。以下の例を利用すると、その名前空間内でのデフォルトの振る舞いを変更できます。

デフォルトですべての内向きのトラフィックを拒否する

すべてのPodを選択して、そのPodへのすべての内向きのトラフィックを許可しないNetworkPolicyを作成すると、その名前空間に対する「デフォルト」の分離ポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

このポリシーを利用すれば、他のいかなるNetworkPolicyにも選択されなかったPodでも分離されることを保証できます。このポリシーは、デフォルトの外向きの分離の振る舞いを変更しません。

デフォルトで内向きのすべてのトラフィックを許可する

(たとえPodを「分離されたもの」として扱うポリシーが追加された場合でも)名前空間内のすべてのPodへのすべてのトラフィックを許可したい場合には、その名前空間内のすべてのトラフィックを明示的に許可するポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

デフォルトで外向きのすべてのトラフィックを拒否する

すべてのPodを選択して、そのPodからのすべての外向きのトラフィックを許可しないNetworkPolicyを作成すると、その名前空間に対する「デフォルト」の外向きの分離ポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

このポリシーを利用すれば、他のいかなるNetworkPolicyにも選択されなかったPodでも、外向きのトラフィックが許可されないことを保証できます。このポリシーは、デフォルトの内向きの分離の振る舞いを変更しません。

デフォルトで外向きのすべてのトラフィックを許可する

(たとえPodを「分離されたもの」として扱うポリシーが追加された場合でも)名前空間内のすべてのPodからのすべてのトラフィックを許可したい場合には、その名前空間内のすべての外向きのトラフィックを明示的に許可するポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

デフォルトで内向きと外向きのすべてのトラフィックを拒否する

名前空間内に以下のNetworkPolicyを作成すると、その名前空間で内向きと外向きのすべてのトラフィックを拒否する「デフォルト」のポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

このポリシーを利用すれば、他のいかなるNetworkPolicyにも選択されなかったPodでも、内向きと外向きのトラフィックが許可されないことを保証できます。

SCTPのサポート

FEATURE STATE: Kubernetes v1.19 [beta]

ベータ版の機能として、これはデフォルトで有効化されます。 クラスターレベルでSCTPを無効化するために、クラスター管理者はAPIサーバーで--feature-gates=SCTPSupport=false,…と指定して、SCTPSupportフィーチャーゲートを無効にする必要があります。

ネットワークポリシーでできないこと(少なくともまだ)

Kubernetes1.20現在、ネットワークポリシーAPIに以下の機能は存在しません。 しかし、オペレーティングシステムのコンポーネント(SELinux、OpenVSwitch、IPTablesなど)、レイヤー7の技術(Ingressコントローラー、サービスメッシュ実装)、もしくはアドミッションコントローラーを使用して回避策を実装できる場合があります。 Kubernetesのネットワークセキュリティを初めて使用する場合は、ネットワークポリシーAPIを使用して以下のユーザーストーリーを(まだ)実装できないことに注意してください。これらのユーザーストーリーの一部(全てではありません)は、ネットワークポリシーAPIの将来のリリースで活発に議論されています。

  • クラスター内トラフィックを強制的に共通ゲートウェイを通過させる(これは、サービスメッシュもしくは他のプロキシで提供するのが最適な場合があります)。
  • TLS関連のもの(これにはサービスメッシュまたはIngressコントローラーを使用します)。
  • ノードの固有のポリシー(これらにはCIDR表記を使用できますが、Kubernetesのアイデンティティでノードを指定することはできません)。
  • 名前空間またはサービスを名前で指定する(ただし、Podまたは名前空間をラベルで指定することができます。これは多くの場合で実行可能な回避策です)。
  • サードパーティによって実行される「ポリシー要求」の作成または管理
  • 全ての名前空間もしくはPodに適用されるデフォルトのポリシー(これを実現できるサードパーティのKubernetesディストリビューションとプロジェクトがいくつか存在します)。
  • 高度なポリシークエリと到達可能性ツール
  • 単一のポリシー宣言でポートの範囲を指定する機能
  • ネットワークセキュリティイベント(例えばブロックされた接続や受け入れられた接続)をログに記録する機能
  • ポリシーを明示的に拒否する機能(現在、ネットワークポリシーのモデルはデフォルトで拒否されており、許可ルールを追加する機能のみが存在します)。
  • ループバックまたは内向きのホストトラフィックを拒否する機能(Podは現在localhostのアクセスやそれらが配置されているノードからのアクセスをブロックすることはできません)。

次の項目

3.5.7 - ServiceとPodに対するDNS

このページではKubernetesによるDNSサポートについて概観します。

イントロダクション

KubernetesのDNSはクラスター上でDNS PodとServiceをスケジュールし、DNSの名前解決をするために各コンテナに対してDNS ServiceのIPを使うようにKubeletを設定します。

何がDNS名を取得するか

クラスター内(DNSサーバーそれ自体も含む)で定義された全てのServiceはDNS名を割り当てられます。デフォルトでは、クライアントPodのDNSサーチリストはPod自身のネームスペースと、クラスターのデフォルトドメインを含みます。
下記の例でこの仕組みを説明します。

Kubernetesのbarというネームスペース内でfooという名前のServiceがあると仮定します。barネームスペース内で稼働しているPodは、fooに対してDNSクエリを実行するだけでこのServiceを探すことができます。barとは別のquuxネームスペース内で稼働しているPodは、foo.barに対してDNSクエリを実行するだけでこのServiceを探すことができます。

下記のセクションでは、サポートされているレコードタイプとレイアウトについて詳しくまとめています。 うまく機能する他のレイアウト、名前、またはクエリーは、実装の詳細を考慮し、警告なしに変更されることがあります。
最新の仕様に関する詳細は、KubernetesにおけるDNSベースのServiceディスカバリを参照ください。

Service

A/AAAAレコード

"通常の"(Headlessでない)Serviceは、my-svc.my-namespace.svc.cluster.localという形式のDNS A(AAAA)レコードを、ServiceのIPバージョンに応じて割り当てられます。このAレコードはそのServiceのClusterIPへと名前解決されます。

"Headless"(ClusterIPなしの)Serviceもまたmy-svc.my-namespace.svc.cluster.localという形式のDNS A(AAAA)レコードを、ServiceのIPバージョンに応じて割り当てられます。通常のServiceとは異なり、このレコードはServiceによって選択されたPodのIPの一覧へと名前解決されます。クライアントはこの一覧のIPを使うか、その一覧から標準のラウンドロビン方式によって選択されたIPを使います。

SRVレコード

SRVレコードは、通常のServiceもしくはHeadless Servicesの一部である名前付きポート向けに作成されます。それぞれの名前付きポートに対して、そのSRVレコードは_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.localという形式となります。
通常のServiceに対しては、このSRVレコードはmy-svc.my-namespace.svc.cluster.localという形式のドメイン名とポート番号へ名前解決します。
Headless Serviceに対しては、このSRVレコードは複数の結果を返します。それはServiceの背後にある各Podの1つを返すのと、auto-generated-name.my-svc.my-namespace.svc.cluster.localという形式のPodのドメイン名とポート番号を含んだ結果を返します。

Pod

A/AAAAレコード

一般的にPodは下記のDNS解決となります。

pod-ip-address.my-namespace.pod.cluster-domain.example

例えば、defaultネームスペースのpodのIPアドレスが172.17.0.3で、クラスターのドメイン名がcluster.localの場合、PodのDNS名は以下になります。

172-17-0-3.default.pod.cluster.local

DeploymentかDaemonSetに作成され、Serviceに公開されるどのPodも以下のDNS解決が利用できます。

pod-ip-address.deployment-name.my-namespace.svc.cluster-domain.example

Podのhostnameとsubdomainフィールド

現在、Podが作成されたとき、そのPodのホスト名はPodのmetadata.nameフィールドの値となります。

Pod Specは、オプションであるhostnameフィールドを持ち、Podのホスト名を指定するために使うことができます。hostnameが指定されたとき、hostnameはそのPodの名前よりも優先されます。例えば、hostnameフィールドが"my-host"にセットされたPodを考えると、Podはそのhostnameが"my-host"に設定されます。

Pod Specはまた、オプションであるsubdomainフィールドも持ち、Podのサブドメイン名を指定するために使うことができます。例えば、"my-namespace"というネームスペース内でhostnamefooとセットされていて、subdomainbarとセットされているPodの場合、そのPodは"foo.bar.my-namespace.svc.cluster.local"という名前の完全修飾ドメイン名(FQDN)を持つことになります。

例:

apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # 実際は、portは必要ありません。
    port: 1234
    targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

もしそのPodと同じネームスペース内で、同じサブドメインを持ったHeadless Serviceが存在していた場合、クラスターのDNSサーバーもまた、そのPodの完全修飾ドメイン名(FQDN)に対するA(AAAA)レコードを返します。 例えば、"busybox-1"というホスト名で、"default-subdomain"というサブドメインを持ったPodと、そのPodと同じネームスペース内にある"default-subdomain"という名前のHeadless Serviceがあると考えると、そのPodは自身の完全修飾ドメイン名(FQDN)を"busybox-1.default-subdomain.my-namespace.svc.cluster.local"として扱います。DNSはそのPodのIPを指し示すA(AAAA)レコードを返します。"busybox1"と"busybox2"の両方のPodはそれぞれ独立したA(AAAA)レコードを持ちます。

そのエンドポイントオブジェクトはそのIPに加えてhostnameを任意のエンドポイントアドレスに対して指定できます。

PodのsetHostnameAsFQDNフィールド

FEATURE STATE: Kubernetes v1.19 [alpha]

前提条件: API Serverに対してSetHostnameAsFQDNフィーチャーゲートを有効にする必要があります。

Podが完全修飾ドメイン名(FQDN)を持つように構成されている場合、そのホスト名は短いホスト名です。 例えば、FQDNがbusybox-1.default-subdomain.my-namespace.svc.cluster-domain.exampleのPodがある場合、 デフォルトではそのPod内のhostnameコマンドはbusybox-1を返し、hostname --fqdnコマンドはFQDNを返します。

PodのspecでsetHostnameAsFQDN: trueを設定した場合、そのPodの名前空間に対してkubeletはPodのFQDNをホスト名に書き込みます。 この場合、hostnamehostname --fqdnの両方がPodのFQDNを返します。

PodのDNSポリシー

DNSポリシーはPod毎に設定できます。現在のKubernetesでは次のようなPod固有のDNSポリシーをサポートしています。これらのポリシーはPod SpecのdnsPolicyフィールドで指定されます。

  • "Default": そのPodはPodが稼働しているNodeから名前解決の設定を継承します。詳細に関しては、関連する議論を参照してください。
  • "ClusterFirst": "www.kubernetes.io"のようなクラスタードメインのサフィックスにマッチしないようなDNSクエリーは、Nodeから継承された上流のネームサーバーにフォワーディングされます。クラスター管理者は、追加のstubドメインと上流のDNSサーバーを設定できます。このような場合におけるDNSクエリー処理の詳細に関しては、関連する議論を参照してください。
  • "ClusterFirstWithHostNet": hostNetworkによって稼働しているPodに対しては、ユーザーは明示的にDNSポリシーを"ClusterFirstWithHostNet"とセットするべきです。
  • "None": この設定では、Kubernetesの環境からDNS設定を無視することができます。全てのDNS設定は、Pod Spec内のdnsConfigフィールドを指定して提供することになっています。下記のセクションのPod's DNS configを参照ください。

下記の例では、hostNetworkフィールドがtrueにセットされているため、dnsPolicyが"ClusterFirstWithHostNet"とセットされているPodを示します。

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

PodのDNS設定

PodのDNS設定は、ユーザーがPodに対してそのDNS設定上でさらに制御するための手段を提供します。

dnsConfigフィールドはオプションで、どのような設定のdnsPolicyでも共に機能することができます。しかし、PodのdnsPolicyが"None"にセットされていたとき、dnsConfigフィールドは必ず指定されなくてはなりません。

下記の項目は、ユーザーがdnsConfigフィールドに指定可能なプロパティーとなります。

  • nameservers: そのPodに対するDNSサーバーとして使われるIPアドレスのリストです。これは最大で3つのIPアドレスを指定することができます。PodのdnsPolicyが"None"に指定されていたとき、そのリストは最低1つのIPアドレスを指定しなければならず、もし指定されていなければ、それ以外のdnsPolicyの値の場合は、このプロパティーはオプションとなります。
  • searches: Pod内のホスト名のルックアップのためのDNSサーチドメインのリストです。このプロパティーはオプションです。指定されていたとき、このリストは選択されたDNSポリシーから生成されたサーチドメイン名のベースとなるリストにマージされます。重複されているドメイン名は削除されます。Kubernetesでは最大6つのサーチドメインの設定を許可しています。
  • options: nameプロパティー(必須)とvalueプロパティー(オプション)を持つような各オプジェクトのリストで、これはオプションです。このプロパティー内の内容は指定されたDNSポリシーから生成されたオプションにマージされます。重複されたエントリーは削除されます。

下記のファイルはカスタムDNS設定を持ったPodの例です。

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

上記のPodが作成されたとき、testコンテナは、コンテナ内の/etc/resolv.confファイル内にある下記の内容を取得します。

nameserver 1.2.3.4
search ns1.svc.cluster.local my.dns.search.suffix
options ndots:2 edns0

IPv6用のセットアップのためには、サーチパスとname serverは下記のようにセットアップするべきです。

$ kubectl exec -it dns-example -- cat /etc/resolv.conf
nameserver 2001:db8:30::a
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

DNS機能を利用可用なバージョン

PodのDNS設定と"None"というDNSポリシーの利用可能なバージョンに関しては下記の通りです。

k8s versionFeature support
1.14ステーブル
1.10β版 (デフォルトで有効)
1.9α版

次の項目

DNS設定の管理方法に関しては、DNS Serviceの設定 を確認してください。

3.5.8 - IPv4/IPv6デュアルスタック

FEATURE STATE: Kubernetes v1.16 [alpha]

IPv4/IPv6デュアルスタックを利用すると、IPv4とIPv6のアドレスの両方をPodおよびServiceに指定できるようになります。

KubernetesクラスターでIPv4/IPv6デュアルスタックのネットワークを有効にすれば、クラスターはIPv4とIPv6のアドレスの両方を同時に割り当てることをサポートするようになります。

サポートされている機能

KubernetesクラスターでIPv4/IPv6デュアルスタックを有効にすると、以下の機能が提供されます。

  • デュアルスタックのPodネットワーク(PodごとにIPv4とIPv6のアドレスが1つずつ割り当てられます)
  • IPv4およびIPv6が有効化されたService(各Serviceは1つのアドレスファミリーでなければなりません)
  • IPv4およびIPv6インターフェースを経由したPodのクラスター外向きの(たとえば、インターネットへの)ルーティング

前提条件

IPv4/IPv6デュアルスタックのKubernetesクラスターを利用するには、以下の前提条件を満たす必要があります。

  • Kubernetesのバージョンが1.16以降である
  • プロバイダーがデュアルスタックのネットワークをサポートしている(クラウドプロバイダーなどが、ルーティング可能なIPv4/IPv6ネットワークインターフェースが搭載されたKubernetesを提供可能である)
  • ネットワークプラグインがデュアルスタックに対応している(KubenetやCalicoなど)

IPv4/IPv6デュアルスタックを有効にする

IPv4/IPv6デュアルスタックを有効にするには、クラスターの関連コンポーネントでIPv6DualStackフィーチャーゲートを有効にして、デュアルスタックのクラスターネットワークの割り当てを以下のように設定します。

  • kube-apiserver:
    • --feature-gates="IPv6DualStack=true"
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
  • kube-controller-manager:
    • --feature-gates="IPv6DualStack=true"
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 デフォルトのサイズは、IPv4では/24、IPv6では/64です
  • kubelet:
    • --feature-gates="IPv6DualStack=true"
  • kube-proxy:
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --feature-gates="IPv6DualStack=true"

Service

クラスターでIPv4/IPv6デュアルスタックのネットワークを有効にした場合、IPv4またはIPv6のいずれかのアドレスを持つServiceを作成できます。Serviceのcluster IPのアドレスファミリーは、Service上に.spec.ipFamilyフィールドを設定することで選択できます。このフィールドを設定できるのは、新しいServiceの作成時のみです。.spec.ipFamilyフィールドの指定はオプションであり、ServiceIngressでIPv4とIPv6を有効にする予定がある場合にのみ使用するべきです。このフィールドの設定は、外向きのトラフィックに対する要件には含まれません。

.spec.ipFamilyは、次のいずれかに設定できます。

  • IPv4: APIサーバーはipv4service-cluster-ip-rangeの範囲からIPアドレスを割り当てます
  • IPv6: APIサーバーはipv6service-cluster-ip-rangeの範囲からIPアドレスを割り当てます

次のServiceのspecにはipFamilyフィールドが含まれていません。Kubernetesは、最初に設定したservice-cluster-ip-rangeの範囲からこのServiceにIPアドレス(別名「cluster IP」)を割り当てます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

次のServiceのspecにはipFamilyフィールドが含まれています。Kubernetesは、最初に設定したservice-cluster-ip-rangeの範囲からこのServiceにIPv6のアドレス(別名「cluster IP」)を割り当てます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv6
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

比較として次のServiceのspecを見ると、このServiceには最初に設定したservice-cluster-ip-rangeの範囲からIPv4のアドレス(別名「cluster IP」)が割り当てられます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv4
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Type LoadBalancer

IPv6が有効になった外部ロードバランサーをサポートしているクラウドプロバイダーでは、typeフィールドにLoadBalancerを指定し、ipFamilyフィールドにIPv6を指定することにより、クラウドロードバランサーをService向けにプロビジョニングできます。

外向きのトラフィック

パブリックおよび非パブリックでのルーティングが可能なIPv6アドレスのブロックを利用するためには、クラスターがベースにしているCNIプロバイダーがIPv6の転送を実装している必要があります。もし非パブリックでのルーティングが可能なIPv6アドレスを使用するPodがあり、そのPodをクラスター外の送信先(例:パブリックインターネット)に到達させたい場合、外向きのトラフィックと応答の受信のためにIPマスカレードを設定する必要があります。ip-masq-agentはデュアルスタックに対応しているため、デュアルスタックのクラスター上でのIPマスカレードにはip-masq-agentが利用できます。

既知の問題

  • Kubenetは、IPv4,IPv6の順番にIPを報告することを強制します(--cluster-cidr)

次の項目

3.5.9 - トポロジーを意識したルーティング

Topology Aware Routingは、ネットワークトラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。クラスター内のPod間で同じゾーンのトラフィックを優先することで、信頼性、パフォーマンス(ネットワークの待ち時間やスループット)の向上、またはコストの削減に役立ちます。
FEATURE STATE: Kubernetes v1.23 [beta]

Topology Aware Routingは、トラフィックを発信元のゾーンに維持するようにルーティング動作を調整します。場合によっては、コストを削減したり、ネットワークパフォーマンスを向上させたりすることができます。

動機

Kubernetesクラスターは、マルチゾーン環境で展開されることが多くなっています。 Topology Aware Routingは、トラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。EndpointSliceコントローラーはServiceのendpointを計算する際に、各endpointのトポロジー(リージョンとゾーン)を考慮し、ゾーンに割り当てるためのヒントフィールドに値を入力します。kube-proxyのようなクラスターコンポーネントは、次にこれらのヒントを消費し、それらを使用してトラフィックがルーティングされる方法に影響を与えることが可能です(トポロジー的に近いendpointを優先します)。

Topology Aware Routingを有効にする

service.kubernetes.io/topology-modeアノテーションをautoに設定すると、サービスに対してTopology Aware Routingを有効にすることができます。各ゾーンに十分なendpointがある場合、個々のendpointを特定のゾーンに割り当てるために、トポロジーヒントがEndpointSliceに入力され、その結果、トラフィックは発信元の近くにルーティングされます。

最も効果的なとき

この機能は、次の場合に最も効果的に動作します。

1. 受信トラフィックが均等に分散されている

トラフィックの大部分が単一のゾーンから発信されている場合、トラフィックはそのゾーンに割り当てられたendpointのサブセットに過負荷を与える可能性があります。受信トラフィックが単一のゾーンから発信されることが予想される場合、この機能は推奨されません。

2. 1つのゾーンに3つ以上のendpointを持つサービス

3つのゾーンからなるクラスターでは、これは9つ以上のendpointがあることを意味します。ゾーン毎のendpointが3つ未満の場合、EndpointSliceコントローラーはendpointを均等に割り当てることができず、代わりにデフォルトのクラスター全体のルーティングアプローチに戻る可能性が高く(約50%)なります。

使い方

「Auto」ヒューリスティックは、各ゾーンに多数のendpointを比例的に割り当てようとします。このヒューリスティックは、非常に多くのendpointを持つサービスに最適です。

EndpointSliceコントローラー

このヒューリスティックが有効な場合、EndpointSliceコントローラーはEndpointSliceにヒントを設定する役割を担います。コントローラーは、各ゾーンに比例した量のendpointを割り当てます。この割合は、そのゾーンで実行されているノードの割り当て可能なCPUコアを基に決定されます。

たとえば、あるゾーンに2つのCPUコアがあり、別のゾーンに1つのCPUコアしかない場合、コントローラーは2つのCPUコアを持つゾーンに2倍のendpointを割り当てます。

次の例は、ヒントが入力されたときのEndpointSliceの様子を示しています。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-hints
  labels:
    kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    zone: zone-a
    hints:
      forZones:
        - name: "zone-a"

kube-proxy

kube-proxyは、EndpointSliceコントローラーによって設定されたヒントに基づいて、ルーティング先のendpointをフィルター処理します。ほとんどの場合、これはkube-proxyが同じゾーン内のendpointにトラフィックをルーティングできることを意味します。コントローラーが別のゾーンからendpointを割り当てて、ゾーン間でendpointがより均等に分散されるようにする場合があります。これにより、一部のトラフィックが他のゾーンにルーティングされます。

セーフガード

各ノードのKubernetesコントロールプレーンとkube-proxyは、Topology Aware Hintを使用する前に、いくつかのセーフガードルールを適用します。これらがチェックアウトされない場合、kube-proxyは、ゾーンに関係なく、クラスター内のどこからでもendpointを選択します。

  1. endpointの数が不十分です: クラスター内のゾーンよりもendpointが少ない場合、コントローラーはヒントを割り当てません。

  2. バランスの取れた割り当てを実現できません: 場合によっては、ゾーン間でendpointのバランスの取れた割り当てを実現できないことがあります。たとえば、ゾーンaがゾーンbの2倍の大きさであるが、endpointが2つしかない場合、ゾーンaに割り当てられたendpointはゾーンbの2倍のトラフィックを受信する可能性があります。この「予想される過負荷」値が各ゾーンの許容しきい値を下回ることができない場合、コントローラーはヒントを割り当てません。重要なことに、これはリアルタイムのフィードバックに基づいていません。それでも、個々のendpointが過負荷になる可能性があります。

  3. 1つ以上のノードの情報が不十分です: ノードにtopology.kubernetes.io/zoneラベルがないか、割り当て可能なCPUの値を報告していない場合、コントロールプレーンはtopology-aware endpoint hintsを設定しないため、kube-proxyはendpointをゾーンでフィルタリングしません。

  4. 1つ以上のendpointにゾーンヒントが存在しません: これが発生すると、kube-proxyはTopology Aware Hintから、またはTopology Aware Hintへの移行が進行中であると見なします。この状態のサービスに対してendpointをフィルタリングすることは危険であるため、kube-proxyはすべてのendpointを使用するようにフォールバックします。

  5. ゾーンはヒントで表されません: kube-proxyが、実行中のゾーンをターゲットとするヒントを持つendpointを1つも見つけることができない場合、すべてのゾーンのendpointを使用することになります。これは既存のクラスターに新しいゾーンを追加するときに発生する可能性が最も高くなります。

制約事項

  • ServiceでexternalTrafficPolicyまたはinternalTrafficPolicyLocalに設定されている場合、Topology Aware Hintは使用されません。同じServiceではなく、異なるServiceの同じクラスターで両方の機能を使用することができます。

  • このアプローチは、ゾーンのサブセットから発信されるトラフィックの割合が高いサービスではうまく機能しません。代わりに、これは着信トラフィックが各ゾーンのノードの容量にほぼ比例することを前提としています。

  • EndpointSliceコントローラーは、各ゾーンの比率を計算するときに、準備ができていないノードを無視します。ノードの大部分の準備ができていない場合、これは意図しない結果をもたらす可能性があります。

  • EndpointSliceコントローラーは、node-role.kubernetes.io/control-planeまたはnode-role.kubernetes.io/masterラベルが設定されたノードを無視します。それらのノードでワークロードが実行されている場合、これは問題になる可能性があります。

  • EndpointSliceコントローラーは、各ゾーンの比率を計算するデプロイ時にtolerationを考慮しません。サービスをバックアップするPodがクラスター内のノードのサブセットに制限されている場合、これは考慮されません。

  • これはオートスケーリングと相性が悪いかもしれません。例えば、多くのトラフィックが1つのゾーンから発信されている場合、そのゾーンに割り当てられたendpointのみがそのトラフィックを処理することになります。その結果、Horizontal Pod Autoscalerがこのイベントを拾えなくなったり、新しく追加されたPodが別のゾーンで開始されたりする可能性があります。

カスタムヒューリスティック

Kubernetesは様々な方法でデブロイされ、endpointをゾーンに割り当てるための単独のヒューリスティックは、すべてのユースケースに通用するわけではありません。 この機能の主な目的は、内蔵のヒューリスティックがユースケースに合わない場合に、カスタムヒューリスティックを開発できるようにすることです。カスタムヒューリスティックを有効にするための最初のステップは、1.27リリースに含まれています。これは限定的な実装であり、関連する妥当と思われる状況をまだカバーしていない可能性があります。

次の項目

3.5.10 - サービス内部トラフィックポリシー

FEATURE STATE: Kubernetes v1.21 [alpha]

サービス内部トラフィックポリシーを使用すると、内部トラフィック制限により、トラフィックが発信されたノード内のエンドポイントにのみ内部トラフィックをルーティングできます。 ここでの「内部」トラフィックとは、現在のクラスターのPodから発信されたトラフィックを指します。これは、コストを削減し、パフォーマンスを向上させるのに役立ちます。

ServiceInternalTrafficPolicyの使用

ServiceInternalTrafficPolicy フィーチャーゲートを有効にすると、.spec.internalTrafficPolicyLocalに設定して、Service内部のみのトラフィックポリシーを有効にすることができます。 これにより、kube-proxyは、クラスター内部トラフィックにノードローカルエンドポイントのみを使用するようになります。

次の例は、.spec.internalTrafficPolicyLocalに設定した場合のServiceの様子を示しています:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  internalTrafficPolicy: Local

使い方

kube-proxyは、spec.internalTrafficPolicyの設定に基づいて、ルーティング先のエンドポイントをフィルタリングします。 spec.internalTrafficPolicyLocalであれば、ノードのローカルエンドポイントにのみルーティングできるようにします。Clusterまたは未設定であればすべてのエンドポイントにルーティングできるようにします。 ServiceInternalTrafficPolicyフィーチャーゲートが有効な場合、spec.internalTrafficPolicyのデフォルトはClusterです。

制約

  • ServiceでexternalTrafficPolicyLocalに設定されている場合、サービス内部トラフィックポリシーは使用されません。同じServiceだけではなく、同じクラスター内の異なるServiceで両方の機能を使用することができます。

次の項目

3.5.11 - Serviceトポロジー

FEATURE STATE: Kubernetes v1.21 [deprecated]

Serviceトポロジーを利用すると、Serviceのトラフィックをクラスターのノードトポロジーに基づいてルーティングできるようになります。たとえば、あるServiceのトラフィックに対して、できるだけ同じノードや同じアベイラビリティゾーン上にあるエンドポイントを優先してルーティングするように指定できます。

はじめに

デフォルトでは、ClusterIPNodePortServiceに送信されたトラフィックは、Serviceに対応する任意のバックエンドのアドレスにルーティングされる可能性があります。しかし、Kubernetes 1.7以降では、「外部の」トラフィックをそのトラフィックを受信したノード上のPodにルーティングすることが可能になりました。しかし、この機能はClusterIPServiceでは対応しておらず、ゾーン内ルーティングなどのより複雑なトポロジーは実現不可能でした。Serviceトポロジーの機能を利用すれば、Serviceの作者が送信元ノードと送信先ノードのNodeのラベルに基づいてトラフィックをルーティングするためのポリシーを定義できるようになるため、この問題を解決できます。

送信元と送信先の間のNodeラベルのマッチングを使用することにより、オペレーターは、そのオペレーターの要件に適したメトリクスを使用して、お互いに「より近い」または「より遠い」ノードのグループを指定できます。たとえば、パブリッククラウド上のさまざまなオペレーターでは、Serviceのトラフィックを同一ゾーン内に留めようとする傾向があります。パブリッククラウドでは、ゾーンをまたぐトラフィックでは関連するコストがかかる一方、ゾーン内のトラフィックにはコストがかからない場合があるからです。その他のニーズとしては、DaemonSetが管理するローカルのPodにトラフィックをルーティングできるようにしたり、レイテンシーを低く抑えるために同じラック上のスイッチに接続されたノードにトラフィックを限定したいというものがあります。

Serviceトポロジーを利用する

クラスターのServiceトポロジーが有効になっていれば、ServiceのspecにtopologyKeysフィールドを指定することで、Serviceのトラフィックのルーティングを制御できます。このフィールドは、Nodeラベルの優先順位リストで、このServiceにアクセスするときにエンドポイントをソートするために使われます。トラフィックは、最初のラベルの値が送信元Nodeのものと一致するNodeに送信されます。一致したノード上にServiceに対応するバックエンドが存在しなかった場合は、2つ目のラベルについて検討が行われ、同様に、残っているラベルが順番に検討されまます。

一致するキーが1つも見つからなかった場合、トラフィックは、Serviceに対応するバックエンドが存在しなかったかのように拒否されます。言い換えると、エンドポイントは、利用可能なバックエンドが存在する最初のトポロジーキーに基づいて選択されます。このフィールドが指定され、すべてのエントリーでクライアントのトポロジーに一致するバックエンドが存在しない場合、そのクライアントに対するバックエンドが存在しないものとしてコネクションが失敗します。「任意のトポロジー」を意味する特別な値"*"を指定することもできます。任意の値にマッチするこの値に意味があるのは、リストの最後の値として使った場合だけです。

topologyKeysが未指定または空の場合、トポロジーの制約は適用されません。

ホスト名、ゾーン名、リージョン名のラベルが付いたNodeを持つクラスターについて考えてみましょう。このとき、ServiceのtopologyKeysの値を設定することで、トラフィックの向きを以下のように制御できます。

  • トラフィックを同じノード上のエンドポイントのみに向け、同じノード上にエンドポイントが1つも存在しない場合には失敗するようにする: ["kubernetes.io/hostname"]
  • 同一ノード上のエンドポイントを優先し、失敗した場合には同一ゾーン上のエンドポイント、同一リージョンゾーンのエンドポイントへとフォールバックし、それ以外の場合には失敗する: ["kubernetes.io/hostname", "topology.kubernetes.io/zone", "topology.kubernetes.io/region"]。これは、たとえばデータのローカリティが非常に重要である場合などに役に立ちます。
  • 同一ゾーンを優先しますが、ゾーン内に利用可能なノードが存在しない場合は、利用可能な任意のエンドポイントにフォールバックする: ["topology.kubernetes.io/zone", "*"]

制約

  • ServiceトポロジーはexternalTrafficPolicy=Localと互換性がないため、Serviceは2つの機能を同時に利用できません。2つの機能を同じクラスター上の異なるServiceでそれぞれ利用することは可能ですが、同一のService上では利用できません。

  • 有効なトポロジーキーは、現在はkubernetes.io/hostnametopology.kubernetes.io/zone、およびtopology.kubernetes.io/regionに限定されています。しかし、将来は一般化され、他のノードラベルも使用できるようになる予定です。

  • トポロジーキーは有効なラベルのキーでなければならず、最大で16個のキーまで指定できます。

  • すべての値をキャッチする"*"を使用する場合は、トポロジーキーの最後の値として指定しなければなりません。

以下では、Serviceトポロジーの機能を利用したよくある例を紹介します。

ノードローカルのエンドポイントだけを使用する

ノードローカルのエンドポイントのみにルーティングするServiceの例です。もし同一ノード上にエンドポイントが存在しない場合、トラフィックは損失します。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"

ノードローカルのエンドポイントを優先して使用する

ノードローカルのエンドポイントを優先して使用しますが、ノードローカルのエンドポイントが存在しない場合にはクラスター全体のエンドポイントにフォールバックするServiceの例です。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"
    - "*"

同一ゾーンや同一リージョンのエンドポイントだけを使用する

同一リージョンのエンドポイントより同一ゾーンのエンドポイントを優先するServiceの例です。もしいずれのエンドポイントも存在しない場合、トラフィックは損失します。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"

ノードローカル、同一ゾーン、同一リーションのエンドポイントを優先して使用する

ノードローカル、同一ゾーン、同一リージョンのエンドポイントを順番に優先し、クラスター全体のエンドポイントにフォールバックするServiceの例です。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"
    - "*"

次の項目

3.6 - ストレージ

3.6.1 - ボリューム

コンテナ内のディスク上のファイルは一時的なものであり、コンテナ内で実行する場合、重要なアプリケーションでいくつかの問題が発生します。1つの問題は、コンテナがクラッシュしたときにファイルが失われることです。kubeletはコンテナを再起動しますが、クリーンな状態です。 2番目の問題は、Podで一緒に実行されているコンテナ間でファイルを共有するときに発生します。 Kubernetesボリュームの抽象化は、これらの問題の両方を解決します。 Podに精通していることをお勧めします。

背景

Dockerにはボリュームの概念がありますが、多少緩く、管理も不十分です。Dockerボリュームは、ディスク上または別のコンテナ内のディレクトリです。Dockerはボリュームドライバーを提供しますが、機能は多少制限されています。

Kubernetesは多くの種類のボリュームをサポートしています。 Podは任意の数のボリュームタイプを同時に使用できます。 エフェメラルボリュームタイプにはPodの存続期間がありますが、永続ボリュームはPodの存続期間を超えて存在します。 Podが存在しなくなると、Kubernetesはエフェメラルボリュームを破棄します。ただしKubernetesは永続ボリュームを破棄しません。 特定のPod内のあらゆる種類のボリュームについて、データはコンテナの再起動後も保持されます。

コアとなるボリュームはディレクトリであり、Pod内のコンテナからアクセスできるデータが含まれている可能性があります。 ディレクトリがどのように作成されるか、それをバックアップするメディア、およびそのコンテンツは、使用する特定のボリュームタイプによって決まります。

ボリュームを使用するには、.spec.volumesでPodに提供するボリュームを指定し、.spec.containers[*].volumeMountsでそれらのボリュームをコンテナにマウントする場所を宣言します。 コンテナ内のプロセスはコンテナイメージの初期コンテンツと、コンテナ内にマウントされたボリューム(定義されている場合)で構成されるファイルシステムビューを確認します。 プロセスは、コンテナイメージのコンテンツと最初に一致するルートファイルシステムを確認します。 そのファイルシステム階層内への書き込みは、もし許可されている場合、後続のファイルシステムアクセスを実行するときにそのプロセスが表示する内容に影響します。 ボリュームはイメージ内の指定されたパスへマウントされます。 Pod内で定義されたコンテナごとに、コンテナが使用する各ボリュームをマウントする場所を個別に指定する必要があります。

ボリュームは他のボリューム内にマウントできません(ただし、関連するメカニズムについては、subPathの使用を参照してください)。 またボリュームには、別のボリューム内の何かへのハードリンクを含めることはできません。

ボリュームの種類

Kubernetesはいくつかのタイプのボリュームをサポートしています。

awsElasticBlockStore

awsElasticBlockStoreボリュームは、Amazon Web Services(AWS)EBSボリュームをPodにマウントします。 Podを削除すると消去されるemptyDirとは異なり、EBSボリュームのコンテンツは保持されたままボリュームはアンマウントされます。 これは、EBSボリュームにデータを事前入力でき、データをPod間で共有できることを意味します。

awsElasticBlockStoreボリュームを使用する場合、いくつかの制限があります。

  • Podが実行されているノードはAWS EC2インスタンスである必要があります
  • これらのインスタンスは、EBSボリュームと同じリージョンおよびアベイラビリティーゾーンにある必要があります
  • EBSは、ボリュームをマウントする単一のEC2インスタンスのみをサポートします

AWS EBSボリュームの作成

PodでEBSボリュームを使用する前に作成する必要があります。

aws ec2 create-volume --availability-zone=eu-west-1a --size=10 --volume-type=gp2

ゾーンがクラスターを立ち上げたゾーンと一致していることを確認してください。サイズとEBSボリュームタイプが使用に適していることを確認してください。

AWS EBS設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-ebs
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-ebs
      name: test-volume
  volumes:
  - name: test-volume
    # This AWS EBS volume must already exist.
    awsElasticBlockStore:
      volumeID: "<volume id>"
      fsType: ext4

EBSボリュームがパーティション化されている場合は、オプションのフィールドpartition: "<partition number>"を指定して、マウントするパーティションを指定できます。

AWS EBS CSIの移行

FEATURE STATE: Kubernetes v1.17 [beta]

awsElasticBlockStoreCSIMigration機能を有効にすると、すべてのプラグイン操作が既存のツリー内プラグインからebs.csi.aws.comContainer Storage Interface(CSI)ドライバーにリダイレクトされます。 この機能を使用するには、AWS EBS CSIドライバーがクラスターにインストールされ、CSIMigrationCSIMigrationAWSのbeta機能が有効になっている必要があります。

AWS EBS CSIの移行の完了

FEATURE STATE: Kubernetes v1.17 [alpha]

awsElasticBlockStoreストレージプラグインがコントローラーマネージャーとkubeletによって読み込まれないようにするには、InTreePluginAWSUnregisterフラグをtrueに設定します。

azureDisk

azureDiskボリュームタイプは、MicrosoftAzureデータディスクをPodにマウントします。

詳細については、azureDiskボリュームプラグインを参照してください。

azureDisk CSIの移行

FEATURE STATE: Kubernetes v1.19 [beta]

azureDiskCSIMigration機能を有効にすると、すべてのプラグイン操作が既存のツリー内プラグインからdisk.csi.azure.comContainer Storage Interface(CSI)ドライバーにリダイレクトされます。 この機能を利用するには、クラスターにAzure Disk CSI Driverをインストールし、CSIMigrationおよびCSIMigrationAzureDisk機能を有効化する必要があります。

azureFile

azureFileボリュームタイプは、Microsoft Azureファイルボリューム(SMB 2.1および3.0)をPodにマウントします。

詳細についてはazureFile volume pluginを参照してください。

azureFile CSIの移行

FEATURE STATE: Kubernetes v1.21 [beta]

zureFileCSIMigration機能を有効にすると、既存のツリー内プラグインからfile.csi.azure.comContainer Storage Interface(CSI)Driverへすべてのプラグイン操作がリダイレクトされます。 この機能を利用するには、クラスターにAzure File CSI Driverをインストールし、CSIMigrationおよびCSIMigrationAzureFileフィーチャーゲートを有効化する必要があります。

Azure File CSIドライバーは、異なるfsgroupで同じボリュームを使用することをサポートしていません。AzurefileCSIの移行が有効になっている場合、異なるfsgroupで同じボリュームを使用することはまったくサポートされません。

cephfs

cephfsボリュームを使用すると、既存のCephFSボリュームをPodにマウントすることができます。 Podを取り外すと消去されるemptyDirとは異なり、cephfsボリュームは内容を保持したまま単にアンマウントされるだけです。 つまりcephfsボリュームにあらかじめデータを入れておき、そのデータをPod間で共有することができます。 cephfsボリュームは複数の書き込み元によって同時にマウントすることができます。

詳細についてはCephFSの例を参照してください。

cinder

cinderボリュームタイプは、PodにOpenStackのCinderのボリュームをマウントするために使用されます。

Cinderボリュームの設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-cinder
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-cinder-container
    volumeMounts:
    - mountPath: /test-cinder
      name: test-volume
  volumes:
  - name: test-volume
    # This OpenStack volume must already exist.
    cinder:
      volumeID: "<volume id>"
      fsType: ext4

OpenStack CSIの移行

FEATURE STATE: Kubernetes v1.21 [beta]

CinderのCSIMigration機能は、Kubernetes1.21ではデフォルトで有効になっています。 既存のツリー内プラグインからのすべてのプラグイン操作をcinder.csi.openstack.orgContainer Storage Interface(CSI) Driverへリダイレクトします。 OpenStack Cinder CSIドライバーをクラスターにインストールする必要があります。 CSIMigrationOpenStackフィーチャーゲートfalseに設定すると、クラスターのCinder CSIマイグレーションを無効化することができます。 CSIMigrationOpenStack機能を無効にすると、ツリー内のCinderボリュームプラグインがCinderボリュームのストレージ管理のすべての側面に責任を持つようになります。

configMap

ConfigMapは構成データをPodに挿入する方法を提供します。 ConfigMapに格納されたデータは、タイプconfigMapのボリュームで参照され、Podで実行されているコンテナ化されたアプリケーションによって使用されます。

ConfigMapを参照するときは、ボリューム内のConfigMapの名前を指定します。 ConfigMapの特定のエントリに使用するパスをカスタマイズできます。 次の設定は、log-config ConfigMapをconfigmap-podというPodにマウントする方法を示しています。

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
    - name: test
      image: busybox
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: log-config
        items:
          - key: log_level
            path: log_level

log-configConfigMapはボリュームとしてマウントされ、そのlog_levelエントリに格納されているすべてのコンテンツは、パス/etc/config/log_levelのPodにマウントされます。 このパスはボリュームのmountPathlog_levelをキーとするpathから派生することに注意してください。

downwardAPI

downwardAPIボリュームは、アプリケーションへのdownward APIデータを利用できるようになります。ディレクトリをマウントし、要求されたデータをプレーンテキストファイルに書き込みます。

詳細についてはdownward API exampleを参照してください。

emptyDir

emptyDirボリュームはPodがノードに割り当てられたときに最初に作成され、そのPodがそのノードで実行されている限り存在します。 名前が示すようにemptyDirボリュームは最初は空です。 Pod内のすべてのコンテナはemptyDirボリューム内の同じファイルを読み書きできますが、そのボリュームは各コンテナで同じパスまたは異なるパスにマウントされることがあります。 何らかの理由でPodがノードから削除されると、emptyDir内のデータは永久に削除されます。

emptyDirのいくつかの用途は次の通りです。

  • ディスクベースのマージソートなどのスクラッチスペース
  • クラッシュからの回復のための長い計算のチェックポイント
  • Webサーバーコンテナがデータを提供している間にコンテンツマネージャコンテナがフェッチするファイルを保持する

環境に応じて、emptyDirボリュームは、ディスクやSSD、ネットワークストレージなど、ノードをバックアップするあらゆる媒体に保存されます。 ただし、emptyDir.mediumフィールドを"Memory"に設定すると、Kubernetesは代わりにtmpfs(RAMベースのファイルシステム)をマウントします。 tmpfsは非常に高速ですが、ディスクと違ってノードのリブート時にクリアされ、書き込んだファイルはコンテナのメモリー制限にカウントされることに注意してください。

emptyDirの設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

fc (fibre channel)

fcボリュームタイプを使用すると、既存のファイバーチャネルブロックストレージボリュームをPodにマウントできます。 targetWWNsボリューム構成のパラメーターを使用して、単一または複数のターゲットWorld Wide Name(WWN)を指定できます。 複数のWWNが指定されている場合、targetWWNは、それらのWWNがマルチパス接続からのものであると想定します。

詳細についてはfibre channelの例を参照してください。

flocker (非推奨)

Flockerはオープンソースのクラスター化されたコンテナデータボリュームマネージャーです。 Flockerは、さまざまなストレージバックエンドに支えられたデータボリュームの管理とオーケストレーションを提供します。

flockerボリュームを使用すると、FlockerデータセットをPodにマウントできます。 もしデータセットがまだFlockerに存在しない場合は、まずFlocker CLIかFlocker APIを使ってデータセットを作成する必要があります。 データセットがすでに存在する場合は、FlockerによってPodがスケジュールされているノードに再アタッチされます。 これは、必要に応じてPod間でデータを共有できることを意味します。

詳細についてはFlocker exampleを参照してください。

gcePersistentDisk

gcePersistentDiskボリュームは、Google Compute Engine (GCE)の永続ディスク(PD)をPodにマウントします。 Podを取り外すと消去されるemptyDirとは異なり、PDの内容は保持されボリュームは単にアンマウントされるだけです。これはPDにあらかじめデータを入れておくことができ、そのデータをPod間で共有できることを意味します。

gcePersistentDiskを使用する場合、いくつかの制限があります。

  • Podが実行されているノードはGCE VMである必要があります
  • これらのVMは、永続ディスクと同じGCEプロジェクトおよびゾーンに存在する必要があります

GCE永続ディスクの機能の1つは、永続ディスクへの同時読み取り専用アクセスです。gcePersistentDiskボリュームを使用すると、複数のコンシューマーが永続ディスクを読み取り専用として同時にマウントできます。 これはPDにデータセットを事前入力してから、必要な数のPodから並行して提供できることを意味します。 残念ながらPDは読み取り/書き込みモードで1つのコンシューマーのみがマウントできます。同時書き込みは許可されていません。

PDが読み取り専用であるか、レプリカ数が0または1でない限り、ReplicaSetによって制御されるPodでGCE永続ディスクを使用すると失敗します。

GCE永続ディスクの作成

PodでGCE永続ディスクを使用する前に、それを作成する必要があります。

gcloud compute disks create --size=500GB --zone=us-central1-a my-data-disk

GCE永続ディスクの設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    # This GCE PD must already exist.
    gcePersistentDisk:
      pdName: my-data-disk
      fsType: ext4

リージョン永続ディスク

リージョン永続ディスク機能を使用すると、同じリージョン内の2つのゾーンで使用できる永続ディスクを作成できます。 この機能を使用するには、ボリュームをPersistentVolumeとしてプロビジョニングする必要があります。Podから直接ボリュームを参照することはサポートされていません。

リージョンPD PersistentVolumeを手動でプロビジョニングする

GCE PDのStorageClassを使用して動的プロビジョニングが可能です。 SPDPersistentVolumeを作成する前に、永続ディスクを作成する必要があります。

gcloud compute disks create --size=500GB my-data-disk
  --region us-central1
  --replica-zones us-central1-a,us-central1-b

リージョン永続ディスクの設定例

apiVersion: v1
kind: PersistentVolume
metadata:
  name: test-volume
spec:
  capacity:
    storage: 400Gi
  accessModes:
  - ReadWriteOnce
  gcePersistentDisk:
    pdName: my-data-disk
    fsType: ext4
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        # failure-domain.beta.kubernetes.io/zone should be used prior to 1.21
        - key: topology.kubernetes.io/zone
          operator: In
          values:
          - us-central1-a
          - us-central1-b

GCE CSIの移行

FEATURE STATE: Kubernetes v1.17 [beta]

GCE PDのCSIMigration機能を有効にすると、すべてのプラグイン操作が既存のツリー内プラグインからpd.csi.storage.gke.ioContainer Storage Interface (CSI) Driverにリダイレクトされるようになります。 この機能を使用するには、クラスターにGCE PD CSI Driverがインストールされ、CSIMigrationCSIMigrationGCEのbeta機能が有効になっている必要があります。

GCE CSIの移行の完了

FEATURE STATE: Kubernetes v1.21 [alpha]

gcePersistentDiskストレージプラグインがコントローラーマネージャーとkubeletによって読み込まれないようにするには、InTreePluginGCEUnregisterフラグをtrueに設定します。

gitRepo(非推奨)

gitRepoボリュームは、ボリュームプラグインの一例です。このプラグインは空のディレクトリをマウントし、そのディレクトリにgitリポジトリをクローンしてPodで使えるようにします。

gitRepoボリュームの例を次に示します。

apiVersion: v1
kind: Pod
metadata:
  name: server
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /mypath
      name: git-volume
  volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

glusterfs

glusterfsボリュームはGlusterfs(オープンソースのネットワークファイルシステム)ボリュームをPodにマウントできるようにするものです。 Podを取り外すと消去されるemptyDirとは異なり、glusterfsボリュームの内容は保持され、単にアンマウントされるだけです。 これは、glusterfsボリュームにデータを事前に入力でき、データをPod間で共有できることを意味します。 GlusterFSは複数のライターが同時にマウントすることができます。

詳細についてはGlusterFSの例を参照してください。

hostPath

hostPathボリュームは、ファイルまたはディレクトリをホストノードのファイルシステムからPodにマウントします。 これはほとんどのPodに必要なものではありませんが、一部のアプリケーションには強力なエスケープハッチを提供します。

たとえばhostPathのいくつかの使用法は次のとおりです。

  • Dockerの内部にアクセスする必要があるコンテナを実行する場合:hostPath/var/lib/dockerを使用します。
  • コンテナ内でcAdvisorを実行する場合:hostPath/sysを指定します。
  • Podが実行される前に、与えられたhostPathが存在すべきかどうか、作成すべきかどうか、そして何として存在すべきかを指定できるようにします。

必須のpathプロパティに加えて、オプションでhostPathボリュームにtypeを指定することができます。

フィールドtypeでサポートされている値は次のとおりです。

ふるまい
空の文字列(デフォルト)は下位互換性のためです。つまり、hostPathボリュームをマウントする前にチェックは実行されません。
DirectoryOrCreate指定されたパスに何も存在しない場合、必要に応じて、権限を0755に設定し、Kubeletと同じグループと所有権を持つ空のディレクトリが作成されます。
Directory指定されたパスにディレクトリが存在する必要があります。
FileOrCreate指定されたパスに何も存在しない場合、必要に応じて、権限を0644に設定し、Kubeletと同じグループと所有権を持つ空のファイルが作成されます。
File指定されたパスにファイルが存在する必要があります。
SocketUNIXソケットは、指定されたパスに存在する必要があります。
CharDeviceキャラクターデバイスは、指定されたパスに存在する必要があります。
BlockDeviceブロックデバイスは、指定されたパスに存在する必要があります。

このタイプのボリュームを使用するときは、以下の理由のため注意してください。

  • HostPath は、特権的なシステム認証情報(Kubeletなど)や特権的なAPI(コンテナランタイムソケットなど)を公開する可能性があり、コンテナのエスケープやクラスターの他の部分への攻撃に利用される可能性があります。
  • 同一構成のPod(PodTemplateから作成されたものなど)は、ノード上のファイルが異なるため、ノードごとに動作が異なる場合があります。
  • ホスト上に作成されたファイルやディレクトリは、rootでしか書き込みができません。特権コンテナ内でrootとしてプロセスを実行するか、ホスト上のファイルのパーミッションを変更してhostPathボリュームに書き込みができるようにする必要があります。

hostPathの設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

hostPath FileOrCreateの設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-webserver
spec:
  containers:
  - name: test-webserver
    image: registry.k8s.io/test-webserver:latest
    volumeMounts:
    - mountPath: /var/local/aaa
      name: mydir
    - mountPath: /var/local/aaa/1.txt
      name: myfile
  volumes:
  - name: mydir
    hostPath:
      # Ensure the file directory is created.
      path: /var/local/aaa
      type: DirectoryOrCreate
  - name: myfile
    hostPath:
      path: /var/local/aaa/1.txt
      type: FileOrCreate

iscsi

iscsiボリュームは、既存のiSCSI(SCSI over IP)ボリュームをPodにマウントすることができます。 Podを取り外すと消去されるemptyDirとは異なり、iscsiボリュームの内容は保持され、単にアンマウントされるだけです。 つまり、iscsiボリュームにはあらかじめデータを入れておくことができ、そのデータをPod間で共有することができるのです。

iSCSIの特徴として、複数のコンシューマーから同時に読み取り専用としてマウントできることが挙げられます。 つまり、ボリュームにあらかじめデータセットを入れておき、必要な数のPodから並行してデータを提供することができます。 残念ながら、iSCSIボリュームは1つのコンシューマによってのみ読み書きモードでマウントすることができます。 同時に書き込みを行うことはできません。

詳細についてはiSCSIの例を参照してください。

local

localボリュームは、ディスク、パーティション、ディレクトリなど、マウントされたローカルストレージデバイスを表します。

ローカルボリュームは静的に作成されたPersistentVolumeとしてのみ使用できます。動的プロビジョニングはサポートされていません。

hostPathボリュームと比較して、localボリュームは手動でノードにPodをスケジューリングすることなく、耐久性と移植性に優れた方法で使用することができます。 システムはPersistentVolume上のノードアフィニティーを見ることで、ボリュームのノード制約を認識します。

ただし、localボリュームは、基盤となるノードの可用性に左右されるため、すべてのアプリケーションに適しているわけではありません。 ノードが異常になると、Podはlocalボリュームにアクセスできなくなります。 このボリュームを使用しているPodは実行できません。localボリュームを使用するアプリケーションは、基盤となるディスクの耐久性の特性に応じて、この可用性の低下と潜在的なデータ損失に耐えられる必要があります。

次の例では、localボリュームとnodeAffinityを使用したPersistentVolumeを示しています。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node

ローカルボリュームを使用する場合は、PersistentVolume nodeAffinityを設定する必要があります。 KubernetesのスケジューラはPersistentVolume nodeAffinityを使用して、これらのPodを正しいノードにスケジューリングします。

PersistentVolume volumeModeを(デフォルト値の「Filesystem」ではなく)「Block」に設定して、ローカルボリュームをrawブロックデバイスとして公開できます。

ローカルボリュームを使用する場合、volumeBindingModeWaitForFirstConsumerに設定したStorageClassを作成することをお勧めします。 詳細については、local StorageClassの例を参照してください。 ボリュームバインディングを遅延させると、PersistentVolumeClaimバインディングの決定が、ノードリソース要件、ノードセレクター、Podアフィニティ、Podアンチアフィニティなど、Podが持つ可能性のある他のノード制約も含めて評価されるようになります。

ローカルボリュームのライフサイクルの管理を改善するために、外部の静的プロビジョナーを個別に実行できます。 このプロビジョナーはまだ動的プロビジョニングをサポートしていないことに注意してください。 外部ローカルプロビジョナーの実行方法の例については、ローカルボリュームプロビジョナーユーザーガイドを参照してください。

nfs

nfsボリュームは、既存のNFS(Network File System)共有をPodにマウントすることを可能にします。Podを取り外すと消去されるemptyDirとは異なり、nfsボリュームのコンテンツは保存され、単にアンマウントされるだけです。 つまり、NFSボリュームにはあらかじめデータを入れておくことができ、そのデータをPod間で共有することができます。 NFSは複数のライターによって同時にマウントすることができます。

詳細についてはNFSの例を参照してください。

persistentVolumeClaim

PersistentVolumeClaimボリュームはPersistentVolumeをPodにマウントするために使用されます。 PersistentVolumeClaimは、ユーザが特定のクラウド環境の詳細を知らなくても、耐久性のあるストレージ(GCE永続ディスクやiSCSIボリュームなど)を「要求」するための方法です。

詳細についてはPersistentVolumeを参照してください。

portworxVolume

portworxVolumeは、Kubernetesとハイパーコンバージドで動作するエラスティックブロックストレージレイヤーです。 Portworxは、サーバー内のストレージをフィンガープリントを作成し、機能に応じて階層化し、複数のサーバーにまたがって容量を集約します。 Portworxは、仮想マシンまたはベアメタルのLinuxノードでゲスト内動作します。

portworxVolumeはKubernetesを通して動的に作成することができますが、事前にプロビジョニングしてPodの中で参照することもできます。 以下は、事前にプロビジョニングされたPortworxボリュームを参照するPodの例です。

apiVersion: v1
kind: Pod
metadata:
  name: test-portworx-volume-pod
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: pxvol
  volumes:
  - name: pxvol
    # This Portworx volume must already exist.
    portworxVolume:
      volumeID: "pxvol"
      fsType: "<fs-type>"

詳細についてはPortworxボリュームの例を参照してください。

投影

投影ボリュームは、複数の既存のボリュームソースを同じディレクトリにマッピングします。 詳細については投影ボリュームを参照してください。

quobyte(非推奨)

quobyteボリュームは、既存のQuobyteボリュームをPodにマウントすることができます。

CSIは、Kubernetes内部でQuobyteボリュームを使用するための推奨プラグインです。 QuobyteのGitHubプロジェクトには、CSIを使用してQuobyteをデプロイするための手順と例があります

rbd

rbdボリュームはRados Block Device(RBD)ボリュームをPodにマウントすることを可能にします。 Podを取り外すと消去されるemptyDirとは異なり、rbdボリュームの内容は保存され、ボリュームはアンマウントされます。つまり、RBDボリュームにはあらかじめデータを入れておくことができ、そのデータをPod間で共有することができるのです。

RBDの特徴として、複数のコンシューマーから同時に読み取り専用としてマウントできることが挙げられます。 つまり、ボリュームにあらかじめデータセットを入れておき、必要な数のPodから並行して提供することができるのです。 残念ながら、RBDボリュームは1つのコンシューマーによってのみ読み書きモードでマウントすることができます。 同時に書き込みを行うことはできません。

詳細についてはRBDの例を参照してください。

RBD CSIの移行

FEATURE STATE: Kubernetes v1.23 [alpha]

RBDCSIMigration機能を有効にすると、既存のツリー内プラグインからrbd.csi.ceph.comCSIドライバーにすべてのプラグイン操作がリダイレクトされます。 この機能を使用するには、クラスターにCeph CSIドライバーをインストールし、CSIMigrationおよびcsiMigrationRBDフィーチャーゲートを有効にしておく必要があります。

secret

secretボリュームは、パスワードなどの機密情報をPodに渡すために使用します。 Kubernetes APIにsecretを格納し、Kubernetesに直接結合することなくPodが使用するファイルとしてマウントすることができます。 secretボリュームはtmpfs(RAM-backed filesystem)によってバックアップされるため、不揮発性ストレージに書き込まれることはありません。

詳細についてはSecretの設定を参照してください。

storageOS(非推奨)

storageosボリュームを使用すると、既存のStorageOSボリュームをPodにマウントできます。

StorageOSは、Kubernetes環境内でコンテナとして実行され、Kubernetesクラスター内の任意のノードからローカルストレージまたは接続されたストレージにアクセスできるようにします。 データを複製してノードの障害から保護することができます。シンプロビジョニングと圧縮により使用率を向上させ、コストを削減できます。

根本的にStorageOSは、コンテナにブロックストレージを提供しファイルシステムからアクセスできるようにします。

StorageOS Containerは64ビットLinuxを必要とし、追加の依存関係はありません。 無償の開発者ライセンスが利用可能です。

次の例は、StorageOSを使用したPodの設定です。

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: redis
    role: master
  name: test-storageos-redis
spec:
  containers:
    - name: master
      image: kubernetes/redis:v1
      env:
        - name: MASTER
          value: "true"
      ports:
        - containerPort: 6379
      volumeMounts:
        - mountPath: /redis-master-data
          name: redis-data
  volumes:
    - name: redis-data
      storageos:
        # The `redis-vol01` volume must already exist within StorageOS in the `default` namespace.
        volumeName: redis-vol01
        fsType: ext4

StorageOS、動的プロビジョニング、およびPersistentVolumeClaimの詳細については、StorageOSの例を参照してください。

vsphereVolume

vsphereVolumeは、vSphereVMDKボリュームをPodにマウントするために使用されます。 ボリュームの内容は、マウント解除されたときに保持されます。VMFSとVSANの両方のデータストアをサポートします。

Creating a VMDK volume

次のいずれかの方法を選択して、VMDKを作成します。

最初にESXにSSHで接続し、次に以下のコマンドを使用してVMDKを作成します。

vmkfstools -c 2G /vmfs/volumes/DatastoreName/volumes/myDisk.vmdk

次のコマンドを使用してVMDKを作成します。

vmware-vdiskmanager -c -t 0 -s 40GB -a lsilogic myDisk.vmdk

vSphere VMDKの設定例

apiVersion: v1
kind: Pod
metadata:
  name: test-vmdk
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-vmdk
      name: test-volume
  volumes:
  - name: test-volume
    # This VMDK volume must already exist.
    vsphereVolume:
      volumePath: "[DatastoreName] volumes/myDisk"
      fsType: ext4

詳細についてはvSphereボリュームの例を参照してください。

vSphere CSIの移行

FEATURE STATE: Kubernetes v1.19 [beta]

vsphereVolumeCSIMigration機能を有効にすると、既存のツリー内プラグインからcsi.vsphere.vmware.comCSIドライバーにすべてのプラグイン操作がリダイレクトされます。 この機能を使用するには、クラスターにvSphere CSIドライバーがインストールされ、CSIMigrationおよびCSIMigrationvSphereフィーチャーゲートが有効になっていなければなりません。

また、vSphere vCenter/ESXiのバージョンが7.0u1以上、HWのバージョンがVM version 15以上であることが条件です。

vSphere CSIの移行の完了

FEATURE STATE: Kubernetes v1.19 [beta]

vsphereVolumeプラグインがコントローラーマネージャーとkubeletによって読み込まれないようにするには、InTreePluginvSphereUnregister機能フラグをtrueに設定する必要があります。すべてのワーカーノードにcsi.vsphere.vmware.comCSIドライバーをインストールする必要があります。

Portworx CSIの移行

FEATURE STATE: Kubernetes v1.23 [alpha]

PortworxのCSIMigration機能が追加されましたが、Kubernetes 1.23ではAlpha状態であるため、デフォルトで無効になっています。 すべてのプラグイン操作を既存のツリー内プラグインからpxd.portworx.comContainer Storage Interface(CSI)ドライバーにリダイレクトします。 Portworx CSIドライバーをクラスターにインストールする必要があります。 この機能を有効にするには、kube-controller-managerとkubeletでCSIMigrationPortworx=trueを設定します。

subPathの使用

1つのPodで複数の用途に使用するために1つのボリュームを共有すると便利な場合があります。 volumeMounts[*].subPathプロパティは、ルートではなく、参照されるボリューム内のサブパスを指定します。

次の例は、単一の共有ボリュームを使用してLAMPスタック(Linux Apache MySQL PHP)でPodを構成する方法を示しています。 このサンプルのsubPath構成は、プロダクションでの使用にはお勧めしません。

PHPアプリケーションのコードとアセットはボリュームのhtmlフォルダーにマップされ、MySQLデータベースはボリュームのmysqlフォルダーに保存されます。例えば:

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd"
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

拡張された環境変数でのsubPathの使用

FEATURE STATE: Kubernetes v1.17 [stable]

subPathExprフィールドを使用して、downwart API環境変数からsubPathディレクトリ名を作成します。 subPathプロパティとsubPathExprプロパティは相互に排他的です。

この例では、PodsubPathExprを使用して、hostPathボリューム/var/log/pods内にpod1というディレクトリを作成します。 hostPathボリュームはdownwardAPIからPod名を受け取ります。 ホストディレクトリ/var/log/pods/pod1は、コンテナ内の/logsにマウントされます。

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    image: busybox
    command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
    volumeMounts:
    - name: workdir1
      mountPath: /logs
      # The variable expansion uses round brackets (not curly brackets).
      subPathExpr: $(POD_NAME)
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      path: /var/log/pods

リソース

emptyDirボリュームの記憶媒体(DiskやSSDなど)は、kubeletのルートディレクトリ(通常は/var/lib/kubelet)を保持するファイルシステムの媒体によって決定されます。 emptyDirまたはhostPathボリュームが消費する容量に制限はなく、コンテナ間またはPod間で隔離されることもありません。

リソース仕様を使用したスペースの要求については、リソースの管理方法を参照してください。

ツリー外のボリュームプラグイン

ツリー外ボリュームプラグインにはContainer Storage Interface(CSI)、およびFlexVolume(非推奨)があります。 これらのプラグインによりストレージベンダーは、プラグインのソースコードをKubernetesリポジトリに追加することなく、カスタムストレージプラグインを作成することができます。

以前は、すべてのボリュームプラグインが「ツリー内」にありました。 「ツリー内」のプラグインは、Kubernetesのコアバイナリとともにビルド、リンク、コンパイルされ、出荷されていました。 つまり、Kubernetesに新しいストレージシステム(ボリュームプラグイン)を追加するには、Kubernetesのコアコードリポジトリにコードをチェックインする必要があったのです。

CSIとFlexVolumeはどちらも、ボリュームプラグインをKubernetesコードベースとは独立して開発し、拡張機能としてKubernetesクラスターにデプロイ(インストール)することを可能にします。

ツリー外のボリュームプラグインの作成を検討しているストレージベンダーについては、ボリュームプラグインのFAQを参照してください。

csi

Container Storage Interface(CSI)は、コンテナオーケストレーションシステム(Kubernetesなど)の標準インターフェースを定義して、任意のストレージシステムをコンテナワークロードに公開します。

詳細についてはCSI design proposalを参照してください。

CSI互換のボリュームドライバーがKubernetesクラスター上に展開されると、ユーザーはcsiボリュームタイプを使用して、CSIドライバーによって公開されたボリュームをアタッチまたはマウントすることができます。

csiボリュームはPodで3つの異なる方法によって使用することができます。

ストレージ管理者は、CSI永続ボリュームを構成するために次のフィールドを使用できます。

  • driver: 使用するボリュームドライバーの名前を指定する文字列。 この値はCSI specで定義されたCSIドライバーがGetPluginInfoResponseで返す値に対応していなければなりません。 これはKubernetesが呼び出すCSIドライバーを識別するために使用され、CSIドライバーコンポーネントがCSIドライバーに属するPVオブジェクトを識別するために使用されます。
  • volumeHandle: ボリュームを一意に識別する文字列。この値は、CSI specで定義されたCSIドライバーがCreateVolumeResponsevolume.idフィールドに返す値に対応していなければなりません。この値はCSIボリュームドライバーのすべての呼び出しで、ボリュームを参照する際にvolume_idとして渡されます。
  • readOnly: ボリュームを読み取り専用として「ControllerPublished」(添付)するかどうかを示すオプションのブール値。デフォルトはfalseです。この値は、ControllerPublishVolumeRequestreadonlyフィールドを介してCSIドライバーに渡されます。
  • fsType: PVのVolumeModeFilesystemの場合、このフィールドを使用して、ボリュームのマウントに使用する必要のあるファイルシステムを指定できます。ボリュームがフォーマットされておらず、フォーマットがサポートされている場合、この値はボリュームのフォーマットに使用されます。この値は、ControllerPublishVolumeRequestNodeStageVolumeRequest、およびNodePublishVolumeRequestVolumeCapabilityフィールドを介してCSIドライバーに渡されます。
  • volumeAttributes: ボリュームの静的プロパティを指定する、文字列から文字列へのマップ。このマップは、CSI specで定義されているように、CSIドライバーがCreateVolumeResponsevolume.attributesフィールドで返すマップと一致しなければなりません。このマップはControllerPublishVolumeRequest,NodeStageVolumeRequest,NodePublishVolumeRequestvolume_contextフィールドを介してCSIドライバーに渡されます。
  • controllerPublishSecretRef: CSIControllerPublishVolumeおよびControllerUnpublishVolume呼び出しを完了するためにCSIドライバーに渡す機密情報を含むsecretオブジェクトへの参照。このフィールドはオプションで、secretが必要ない場合は空にすることができます。secretに複数のsecretが含まれている場合は、すべてのsecretが渡されます。
  • nodeStageSecretRef: CSINodeStageVolume呼び出しを完了するために、CSIドライバーに渡す機密情報を含むsecretオブジェクトへの参照。このフィールドはオプションで、secretが必要ない場合は空にすることができます。secretに複数のsecretが含まれている場合、すべてのsecretが渡されます。
  • nodePublishSecretRef: CSINodePublishVolume呼び出しを完了するために、CSIドライバーに渡す機密情報を含むsecretオブジェクトへの参照。このフィールドはオプションで、secretが必要ない場合は空にすることができます。secretオブジェクトが複数のsecretを含んでいる場合、すべてのsecretが渡されます。

CSI rawブロックボリュームのサポート

FEATURE STATE: Kubernetes v1.18 [stable]

外部のCSIドライバーを使用するベンダーは、Kubernetesワークロードでrawブロックボリュームサポートを実装できます。

CSI固有の変更を行うことなく、通常どおり、rawブロックボリュームをサポートするPersistentVolume/PersistentVolumeClaimを設定できます。

CSIエフェメラルボリューム

FEATURE STATE: Kubernetes v1.16 [beta]

Pod仕様内でCSIボリュームを直接構成できます。この方法で指定されたボリュームは一時的なものであり、Podを再起動しても持続しません。詳細についてはエフェメラルボリュームを参照してください。

CSIドライバーの開発方法の詳細についてはkubernetes-csiドキュメントを参照してください。

ツリー内プラグインからCSIドライバーへの移行

FEATURE STATE: Kubernetes v1.17 [beta]

CSIMigration機能を有効にすると、既存のツリー内プラグインに対する操作が、対応するCSIプラグイン(インストールおよび構成されていることが期待されます)に転送されます。 その結果、オペレーターは、ツリー内プラグインに取って代わるCSIドライバーに移行するときに、既存のストレージクラス、PersistentVolume、またはPersistentVolumeClaim(ツリー内プラグインを参照)の構成を変更する必要がありません。

サポートされている操作と機能には、プロビジョニング/削除、アタッチ/デタッチ、マウント/アンマウント、およびボリュームのサイズ変更が含まれます。

CSIMigrationをサポートし、対応するCSIドライバーが実装されているツリー内プラグインは、ボリュームのタイプにリストされています。

flexVolume

FEATURE STATE: Kubernetes v1.23 [deprecated]

FlexVolumeは、ストレージドライバーとのインターフェースにexecベースのモデルを使用するツリー外プラグインインターフェースです。FlexVolumeドライバーのバイナリは、各ノード、場合によってはコントロールプレーンノードにも、あらかじめ定義されたボリュームプラグインパスにインストールする必要があります。

PodはflexVolumeツリー内ボリュームプラグインを通してFlexVolumeドライバーと対話します。

詳細についてはFlexVolumeのREADMEを参照してください。

マウントの伝播

マウントの伝播により、コンテナによってマウントされたボリュームを、同じPod内の他のコンテナ、または同じノード上の他のPodに共有できます。

ボリュームのマウント伝播は、containers[*].volumeMountsmountPropagationフィールドによって制御されます。その値は次のとおりです。

  • None - このボリュームマウントは、ホストによってこのボリュームまたはそのサブディレクトリにマウントされる後続のマウントを受け取りません。同様に、コンテナによって作成されたマウントはホストに表示されません。これがデフォルトのモードです。

    このモードはLinuxカーネルドキュメントで説明されているprivateマウント伝播と同じです。

  • HostToContainer - このボリュームマウントは、このボリュームまたはそのサブディレクトリのいずれかにマウントされる後続のすべてのマウントを受け取ります。

    つまりホストがボリュームマウント内に何かをマウントすると、コンテナはそこにマウントされていることを確認します。

    同様に同じボリュームに対してBidirectionalマウント伝搬を持つPodが何かをマウントすると、HostToContainerマウント伝搬を持つコンテナはそれを見ることができます。

    このモードはLinuxカーネルドキュメントで説明されているrslaveマウント伝播と同じです。

  • Bidirectional - このボリュームマウントは、HostToContainerマウントと同じように動作します。さらに、コンテナによって作成されたすべてのボリュームマウントは、ホストと、同じボリュームを使用するすべてのPodのすべてのコンテナに伝播されます。

    このモードの一般的な使用例は、FlexVolumeまたはCSIドライバーを備えたPod、またはhostPathボリュームを使用してホストに何かをマウントする必要があるPodです。

    このモードはLinuxカーネルドキュメントで説明されているrsharedマウント伝播と同じです。

構成

一部のデプロイメント(CoreOS、RedHat/Centos、Ubuntu)でマウント伝播が正しく機能する前に、以下に示すように、Dockerでマウント共有を正しく構成する必要があります。

Dockerのsystemdサービスファイルを編集します。以下のようにMountFlagsを設定します。

MountFlags=shared

または、MountFlags=slaveがあれば削除してください。その後、Dockerデーモンを再起動します。

sudo systemctl daemon-reload
sudo systemctl restart docker

次の項目

永続ボリュームを使用してWordPressとMySQLをデプロイする例に従ってください。

3.6.2 - 永続ボリューム

このドキュメントではKubernetesの PersistentVolume について説明します。ボリュームを一読することをおすすめします。

概要

ストレージを管理することはインスタンスを管理することとは全くの別物です。PersistentVolumeサブシステムは、ストレージが何から提供されているか、どのように消費されているかをユーザーと管理者から抽象化するAPIを提供します。これを実現するためのPersistentVolumeとPersistentVolumeClaimという2つの新しいAPIリソースを紹介します。

PersistentVolume (PV)はストレージクラスを使って管理者もしくは動的にプロビジョニングされるクラスターのストレージの一部です。これはNodeと同じようにクラスターリソースの一部です。PVはVolumeのようなボリュームプラグインですが、PVを使う個別のPodとは独立したライフサイクルを持っています。このAPIオブジェクトはNFS、iSCSIやクラウドプロバイダー固有のストレージシステムの実装の詳細を捕捉します。

PersistentVolumeClaim (PVC)はユーザーによって要求されるストレージです。これはPodと似ています。PodはNodeリソースを消費し、PVCはPVリソースを消費します。Podは特定レベルのCPUとメモリーリソースを要求することができます。クレームは特定のサイズやアクセスモード(例えば、ReadWriteOnce、ReadOnlyMany、ReadWriteManyにマウントできます。詳しくはアクセスモードを参照してください)を要求することができます。

PersistentVolumeClaimはユーザーに抽象化されたストレージリソースの消費を許可する一方、ユーザーは色々な問題に対処するためにパフォーマンスといった様々なプロパティを持ったPersistentVolumeを必要とすることは一般的なことです。クラスター管理者はユーザーに様々なボリュームがどのように実装されているかを表に出すことなく、サイズやアクセスモードだけではない色々な点で異なった、様々なPersistentVolumeを提供できる必要があります。これらのニーズに応えるために StorageClass リソースがあります。

実例を含む詳細なチュートリアルを参照して下さい。

ボリュームと要求のライフサイクル

PVはクラスター内のリソースです。PVCはこれらのリソースの要求でありまた、クレームのチェックとしても機能します。PVとPVCの相互作用はこのライフサイクルに従います。

プロビジョニング

PVは静的か動的どちらかでプロビジョニングされます。

静的

クラスター管理者は多数のPVを作成します。それらはクラスターのユーザーが使うことのできる実際のストレージの詳細を保持します。それらはKubernetes APIに存在し、利用できます。

動的

ユーザーのPersistentVolumeClaimが管理者の作成したいずれの静的PVにも一致しない場合、クラスターはPVC用にボリュームを動的にプロビジョニングしようとする場合があります。 このプロビジョニングはStorageClassに基づいています。PVCはストレージクラスの要求が必要であり、管理者は動的プロビジョニングを行うためにストレージクラスの作成・設定が必要です。ストレージクラスを""にしたストレージ要求は、自身の動的プロビジョニングを事実上無効にします。

ストレージクラスに基づいたストレージの動的プロビジョニングを有効化するには、クラスター管理者がDefaultStorageClassアドミッションコントローラーをAPIサーバーで有効化する必要があります。 これは例えば、DefaultStorageClassがAPIサーバーコンポーネントの--enable-admission-pluginsフラグのコンマ区切りの順序付きリストの中に含まれているかで確認できます。 APIサーバーのコマンドラインフラグの詳細についてはkube-apiserverのドキュメントを参照してください。

バインディング

ユーザは、特定のサイズのストレージとアクセスモードを指定した上でPersistentVolumeClaimを作成します(動的プロビジョニングの場合は、すでに作られています)。マスター内のコントロールループは、新しく作られるPVCをウォッチして、それにマッチするPVが見つかったときに、それらを紐付けます。PVが新しいPVC用に動的プロビジョニングされた場合、コントロールループは常にPVをそのPVCに紐付けます。そうでない場合、ユーザーは常に少なくとも要求したサイズ以上のボリュームを取得しますが、ボリュームは要求されたサイズを超えている可能性があります。一度紐付けされると、どのように紐付けられたかに関係なくPersistentVolumeClaimの紐付けは排他的(決められた特定のPVとしか結びつかない状態)になります。PVCからPVへの紐付けは、PersistentVolumeとPersistentVolumeClaim間の双方向の紐付けであるClaimRefを使用した1対1のマッピングになっています。

一致するボリュームが存在しない場合、クレームはいつまでも紐付けされないままになります。一致するボリュームが利用可能になると、クレームがバインドされます。たとえば、50GiのPVがいくつもプロビジョニングされているクラスターだとしても、100Giを要求するPVCとは一致しません。100GiのPVがクラスターに追加されると、PVCを紐付けできます。

使用

Podは要求をボリュームとして使用します。クラスターは、要求を検査して紐付けられたボリュームを見つけそのボリュームをPodにマウントします。複数のアクセスモードをサポートするボリュームの場合、ユーザーはPodのボリュームとしてクレームを使う時にどのモードを希望するかを指定します。

ユーザーがクレームを取得し、そのクレームがバインドされると、バインドされたPVは必要な限りそのユーザーに属します。ユーザーはPodをスケジュールし、PodのvolumesブロックにpersistentVolumeClaimを含めることで、バインドされたクレームのPVにアクセスします。 書式の詳細はこちらを確認して下さい。

使用中のストレージオブジェクトの保護

使用中のストレージオブジェクト保護機能の目的はデータ損失を防ぐために、Podによって実際に使用されている永続ボリュームクレーム(PVC)と、PVCにバインドされている永続ボリューム(PV)がシステムから削除されないようにすることです。

ユーザーがPodによって実際に使用されているPVCを削除しても、そのPVCはすぐには削除されません。PVCの削除は、PVCがPodで使用されなくなるまで延期されます。また、管理者がPVCに紐付けられているPVを削除しても、PVはすぐには削除されません。PVがPVCに紐付けられなくなるまで、PVの削除は延期されます。

PVCの削除が保護されているかは、PVCのステータスがTerminatingになっていて、そしてFinalizersのリストにkubernetes.io/pvc-protectionが含まれているかで確認できます。

kubectl describe pvc hostpath
Name:          hostpath
Namespace:     default
StorageClass:  example-hostpath
Status:        Terminating
Volume:
Labels:        <none>
Annotations:   volume.beta.kubernetes.io/storage-class=example-hostpath
               volume.beta.kubernetes.io/storage-provisioner=example.com/hostpath
Finalizers:    [kubernetes.io/pvc-protection]

同様にPVの削除が保護されているかは、PVのステータスがTerminatingになっていて、そしてFinalizersのリストにkubernetes.io/pv-protectionが含まれているかで確認できます。

kubectl describe pv task-pv-volume
Name:            task-pv-volume
Labels:          type=local
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    standard
Status:          Terminating
Claim:
Reclaim Policy:  Delete
Access Modes:    RWO
Capacity:        1Gi
Message:
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /tmp/data
    HostPathType:
Events:            <none>

再クレーム

ユーザーは、ボリュームの使用が完了したら、リソースの再クレームを許可するAPIからPVCオブジェクトを削除できます。PersistentVolumeの再クレームポリシーはそのクレームが解放された後のボリュームの処理をクラスターに指示します。現在、ボリュームは保持、リサイクル、または削除できます。

保持

Retainという再クレームポリシーはリソースを手動で再クレームすることができます。PersistentVolumeClaimが削除される時、PersistentVolumeは依然として存在はしますが、ボリュームは解放済みです。ただし、以前のクレームデータはボリューム上に残っているため、別のクレームにはまだ使用できません。管理者は次の手順でボリュームを手動で再クレームできます。

  1. PersistentVolumeを削除します。PVが削除された後も、外部インフラストラクチャー(AWS EBS、GCE PD、Azure Disk、Cinderボリュームなど)に関連付けられたストレージアセットは依然として残ります。
  2. ストレージアセットに関連するのデータを手動で適切にクリーンアップします。
  3. 関連するストレージアセットを手動で削除するか、同じストレージアセットを再利用したい場合、新しいストレージアセット定義と共にPersistentVolumeを作成します。

削除

Delete再クレームポリシーをサポートするボリュームプラグインの場合、削除するとPersistentVolumeオブジェクトがKubernetesから削除されるだけでなく、AWS EBS、GCE PD、Azure Disk、Cinderボリュームなどの外部インフラストラクチャーの関連ストレージアセットも削除されます。動的にプロビジョニングされたボリュームは、StorageClassの再クレームポリシーを継承します。これはデフォルトで削除です。管理者は、ユーザーの需要に応じてStorageClassを構成する必要があります。そうでない場合、PVは作成後に編集またはパッチを適用する必要があります。PersistentVolumeの再クレームポリシーの変更を参照してください。

リサイクル

基盤となるボリュームプラグインでサポートされている場合、Recycle再クレームポリシーはボリュームに対して基本的な削除(rm -rf /thevolume/*)を実行し、新しいクレームに対して再び利用できるようにします。

管理者はreferenceで説明するように、Kubernetesコントローラーマネージャーのコマンドライン引数を使用して、カスタムリサイクラーPodテンプレートを構成できます。カスタムリサイクラーPodテンプレートには、次の例に示すように、volumes仕様が含まれている必要があります。

apiVersion: v1
kind: Pod
metadata:
  name: pv-recycler
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /any/path/it/will/be/replaced
  containers:
  - name: pv-recycler
    image: "registry.k8s.io/busybox"
    command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/*  && test -z \"$(ls -A /scrub)\" || exit 1"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

ただし、カスタムリサイクラーPodテンプレートのvolumesパート内で指定された特定のパスは、リサイクルされるボリュームの特定のパスに置き換えられます。

永続ボリュームの予約

コントロールプレーンは、永続ボリュームクレームをクラスター内の一致する永続ボリュームにバインドできます。 ただし、永続ボリュームクレームを特定の永続ボリュームにバインドする場合、それらを事前にバインドする必要があります。

永続ボリュームクレームで永続ボリュームを指定することにより、その特定の永続ボリュームと永続ボリュームクレームの間のバインディングを宣言します。 永続ボリュームが存在し、そのclaimRefフィールドで永続ボリュームクレームを予約していない場合に永続ボリュームと永続ボリュームクレームがバインドされます。

バインディングは、ノードアフィニティを含むいくつかのボリュームの一致基準に関係なく発生します。 コントロールプレーンは、依然としてストレージクラス、アクセスモード、および要求されたストレージサイズが有効であることをチェックします。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
  namespace: foo
spec:
  storageClassName: "" # 空の文字列を明示的に指定する必要があります。そうしないとデフォルトのストレージクラスが設定されてしまいます。
  volumeName: foo-pv
  ...

この方法は、永続ボリュームへのバインド特権を保証するものではありません。 他の永続ボリュームクレームが指定した永続ボリュームを使用できる場合、最初にそのストレージボリュームを予約する必要があります。 永続ボリュームのclaimRefフィールドに関連する永続ボリュームクレームを指定して、他の永続ボリュームクレームがその永続ボリュームにバインドできないようにしてください。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: foo-pv
spec:
  storageClassName: ""
  claimRef:
    name: foo-pvc
    namespace: foo
  ...

これは、既存の永続ボリュームを再利用する場合など、claimPolicyRetainに設定されている永続ボリュームを使用する場合に役立ちます。

永続ボリュームクレームの拡大

FEATURE STATE: Kubernetes v1.24 [stable]

PersistentVolumeClaim(PVC)の拡大はデフォルトで有効です。次のボリュームの種類で拡大できます。

  • gcePersistentDisk
  • awsElasticBlockStore
  • Cinder
  • glusterfs
  • rbd
  • Azure File
  • Azure Disk
  • Portworx
  • FlexVolumes
  • CSI

そのストレージクラスのallowVolumeExpansionフィールドがtrueとなっている場合のみ、PVCを拡大できます。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gluster-vol-default
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://192.168.10.100:8080"
  restuser: ""
  secretNamespace: ""
  secretName: ""
allowVolumeExpansion: true

PVCに対してさらに大きなボリュームを要求するには、PVCオブジェクトを編集してより大きなサイズを指定します。これによりPersistentVolumeを受け持つ基盤にボリュームの拡大がトリガーされます。クレームを満たすため新しくPersistentVolumeが作成されることはありません。代わりに既存のボリュームがリサイズされます。

CSIボリュームの拡張

FEATURE STATE: Kubernetes v1.24 [stable]

CSIボリュームの拡張のサポートはデフォルトで有効になっていますが、ボリューム拡張をサポートするにはボリューム拡張を利用できるCSIドライバーも必要です。詳細については、それぞれのCSIドライバーのドキュメントを参照してください。

ファイルシステムを含むボリュームのリサイズ

ファイルシステムがXFS、Ext3、またはExt4の場合にのみ、ファイルシステムを含むボリュームのサイズを変更できます。

ボリュームにファイルシステムが含まれる場合、新しいPodがPersistentVolumeClaimでReadWriteモードを使用している場合にのみ、ファイルシステムのサイズが変更されます。ファイルシステムの拡張は、Podの起動時、もしくはPodの実行時で基盤となるファイルシステムがオンラインの拡張をサポートする場合に行われます。

FlexVolumesでは、ドライバのRequiresFSResize機能がtrueに設定されている場合、サイズを変更できます。 FlexVolumeは、Podの再起動時にサイズ変更できます。

使用中の永続ボリュームクレームのリサイズ

FEATURE STATE: Kubernetes v1.15 [beta]

この場合、既存のPVCを使用しているPodまたはDeploymentを削除して再作成する必要はありません。使用中のPVCは、ファイルシステムが拡張されるとすぐにPodで自動的に使用可能になります。この機能は、PodまたはDeploymentで使用されていないPVCには影響しません。拡張を完了する前に、PVCを使用するPodを作成する必要があります。

他のボリュームタイプと同様、FlexVolumeボリュームは、Podによって使用されている最中でも拡張できます。

ボリューム拡張時の障害からの復旧

基盤ストレージの拡張に失敗した際には、クラスターの管理者はPersistent Volume Claim (PVC)の状態を手動で復旧し、リサイズ要求をキャンセルします。それをしない限り、リサイズ要求は管理者の介入なしにコントローラーによって継続的に再試行されます。

  1. PersistentVolumeClaim(PVC)にバインドされているPersistentVolume(PV)をRetain再クレームポリシーとしてマークします。
  2. PVCを削除します。PVはRetain再クレームポリシーを持っているため、PVCを再び作成したときにいかなるデータも失うことはありません。
  3. claimRefエントリーをPVスペックから削除して、新しいPVCがそれにバインドできるようにします。これによりPVはAvailableになります。
  4. PVより小さいサイズでPVCを再作成し、PVCのvolumeNameフィールドをPVの名前に設定します。これにより新しいPVCを既存のPVにバインドします。
  5. PVを再クレームポリシーを復旧することを忘れずに行ってください。

永続ボリュームの種類

PersistentVolumeの種類はプラグインとして実装されます。Kubernetesは現在次のプラグインに対応しています。

  • awsElasticBlockStore - AWS Elastic Block Store (EBS)
  • azureDisk - Azure Disk
  • azureFile - Azure File
  • cephfs - CephFS volume
  • cinder - Cinder (OpenStack block storage) (非推奨)
  • csi - Container Storage Interface (CSI)
  • fc - Fibre Channel (FC) storage
  • flexVolume - FlexVolume
  • flocker - Flocker storage
  • gcePersistentDisk - GCE Persistent Disk
  • glusterfs - Glusterfs volume
  • hostPath - HostPath volume (テスト用の単一ノードのみ。マルチノードクラスターでは動作しません。代わりにlocalボリュームを利用することを検討してください。)
  • iscsi - iSCSI (SCSI over IP) storage
  • local - ノードにマウントされたローカルストレージデバイス
  • nfs - Network File System (NFS) storage
  • photonPersistentDisk - Photon controller persistent disk (対応するクラウドプロバイダーが削除されたため、このボリュームタイプは機能しなくなりました。)
  • portworxVolume - Portworx volume
  • quobyte - Quobyte volume
  • rbd - Rados Block Device (RBD) volume
  • scaleIO - ScaleIO volume (非推奨)
  • storageos - StorageOS volume
  • vsphereVolume - vSphere VMDK volume

永続ボリューム

各PVには、仕様とボリュームのステータスが含まれているspecとstatusが含まれています。 PersistentVolumeオブジェクトの名前は、有効な DNSサブドメイン名である必要があります。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

容量

通常、PVには特定のストレージ容量があります。これはPVのcapacity属性を使用して設定されます。容量によって期待される単位を理解するためには、Kubernetesのリソースモデルを参照してください。

現在、設定または要求できるのはストレージサイズのみです。将来の属性には、IOPS、スループットなどが含まれます。

ボリュームモード

FEATURE STATE: Kubernetes v1.18 [stable]

KubernetesはPersistentVolumesの2つのvolumeModesをサポートしています: FilesystemBlockです。
volumeModeは任意のAPIパラメーターです。
FilesystemvolumeModeパラメーターが省略されたときに使用されるデフォルトのモードです。

volumeMode: FilesystemであるボリュームはPodにマウントされてディレクトリになります。 ボリュームがブロックデバイスでデバイスが空の時、Kubernetesは初めてそれにマウントされる前にデバイスのファイルシステムを作成します。

volumeModeの値をBlockに設定してボリュームをRAWブロックデバイスとして使用します。
このようなボリュームは、ファイルシステムを持たないブロックデバイスとしてPodに提示されます。
このモードは、Podとボリュームの間のファイルシステムレイヤーなしにボリュームにアクセスする可能な限り最速の方法をPodに提供するのに便利です。一方で、Pod上で実行しているアプリケーションはRAWブロックデバイスの扱い方を知っていなければなりません。
Pod内でvolumeMode: Blockとともにボリュームを使用する例としては、Raw Block Volume Supportを参照してください。

アクセスモード

PersistentVolumeは、リソースプロバイダーがサポートする方法でホストにマウントできます。次の表に示すように、プロバイダーにはさまざまな機能があり、各PVのアクセスモードは、その特定のボリュームでサポートされる特定のモードに設定されます。たとえば、NFSは複数の読み取り/書き込みクライアントをサポートできますが、特定のNFSのPVはサーバー上で読み取り専用としてエクスポートされる場合があります。各PVは、その特定のPVの機能を記述する独自のアクセスモードのセットを取得します。

アクセスモードは次の通りです。

ReadWriteOnce
ボリュームは単一のNodeで読み取り/書き込みとしてマウントできます
ReadOnlyMany
ボリュームは多数のNodeで読み取り専用としてマウントできます
ReadWriteMany
ボリュームは多数のNodeで読み取り/書き込みとしてマウントできます
ReadWriteOncePod
ボリュームは、単一のPodで読み取り/書き込みとしてマウントできます。クラスター全体で1つのPodのみがそのPVCの読み取りまたは書き込みを行えるようにする場合は、ReadWriteOncePodアクセスモードを使用します。これは、CSIボリュームとKubernetesバージョン1.22以降でのみサポートされます。

これについてはブログIntroducing Single Pod Access Mode for PersistentVolumesに詳細が記載されています。

CLIではアクセスモードは次のように略されます。

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany

Important! ボリュームは、多数のモードをサポートしていても、一度に1つのアクセスモードでしかマウントできません。たとえば、GCEPersistentDiskは、単一NodeではReadWriteOnceとして、または多数のNodeではReadOnlyManyとしてマウントできますが、同時にマウントすることはできません。

ボリュームプラグインReadWriteOnceReadOnlyManyReadWriteMany
AWSElasticBlockStore--
AzureFile
AzureDisk--
CephFS
Cinder--
CSIドライバーに依存ドライバーに依存ドライバーに依存
FC-
FlexVolumeドライバーに依存
Flocker--
GCEPersistentDisk-
Glusterfs
HostPath--
iSCSI-
Quobyte
NFS
RBD-
VsphereVolume-- (Podが連結されている場合のみ)
PortworxVolume-
ScaleIO-
StorageOS--

Class

PVはクラスを持つことができます。これはstorageClassName属性をストレージクラスの名前に設定することで指定されます。特定のクラスのPVは、そのクラスを要求するPVCにのみバインドできます。storageClassNameにクラスがないPVは、特定のクラスを要求しないPVCにのみバインドできます。

以前volume.beta.kubernetes.io/storage-classアノテーションは、storageClassName属性の代わりに使用されていました。このアノテーションはまだ機能しています。ただし、将来のKubernetesリリースでは完全に非推奨です。

再クレームポリシー

現在の再クレームポリシーは次のとおりです。

  • 保持 -- 手動再クレーム
  • リサイクル -- 基本的な削除 (rm -rf /thevolume/*)
  • 削除 -- AWS EBS、GCE PD、Azure Disk、もしくはOpenStack Cinderボリュームに関連するストレージアセットを削除

現在、NFSとHostPathのみがリサイクルをサポートしています。AWS EBS、GCE PD、Azure Disk、およびCinder volumeは削除をサポートしています。

マウントオプション

Kubernetes管理者は永続ボリュームがNodeにマウントされるときの追加マウントオプションを指定できます。

次のボリュームタイプがマウントオプションをサポートしています。

  • AWSElasticBlockStore
  • AzureDisk
  • AzureFile
  • CephFS
  • Cinder (OpenStackブロックストレージ)
  • GCEPersistentDisk
  • Glusterfs
  • NFS
  • Quobyte Volumes
  • RBD (Ceph Block Device)
  • StorageOS
  • VsphereVolume
  • iSCSI

マウントオプションは検証されないため、不正だった場合マウントは失敗します。

以前volume.beta.kubernetes.io/mount-optionsアノテーションがmountOptions属性の代わりに使われていました。このアノテーションはまだ機能しています。ただし、将来のKubernetesリリースでは完全に非推奨です。

ノードアフィニティ

PVはノードアフィニティを指定して、このボリュームにアクセスできるNodeを制限する制約を定義できます。PVを使用するPodは、ノードアフィニティによって選択されたNodeにのみスケジュールされます。

フェーズ

ボリュームは次のフェーズのいずれかです。

  • 利用可能 -- まだクレームに紐付いていない自由なリソース
  • バウンド -- クレームに紐付いている
  • リリース済み -- クレームが削除されたが、クラスターにまだクレームされている
  • 失敗 -- 自動再クレームに失敗

CLIにはPVに紐付いているPVCの名前が表示されます。

永続ボリューム要求

各PVCにはspecとステータスが含まれます。これは、仕様とクレームのステータスです。

PersistentVolumeClaimオブジェクトの名前は、有効な DNSサブドメイン名である必要があります。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

アクセスモード

クレームは、特定のアクセスモードでストレージを要求するときにボリュームと同じ規則を使用します。

ボリュームモード

クレームは、ボリュームと同じ規則を使用して、ファイルシステムまたはブロックデバイスとしてのボリュームの消費を示します。

リソース

Podと同様に、クレームは特定の量のリソースを要求できます。この場合、要求はストレージ用です。同じリソースモデルがボリュームとクレームの両方に適用されます。

セレクター

クレームでは、ラベルセレクターを指定して、ボリュームセットをさらにフィルター処理できます。ラベルがセレクターに一致するボリュームのみがクレームにバインドできます。セレクターは2つのフィールドで構成できます。

  • matchLabels - ボリュームはこの値のラベルが必要です
  • matchExpressions - キー、値のリスト、およびキーと値を関連付ける演算子を指定することによって作成された要件のリスト。有効な演算子は、In、NotIn、ExistsおよびDoesNotExistです。

matchLabelsmatchExpressionsの両方からのすべての要件はANDで結合されます。一致するには、すべてが一致する必要があります。

クラス

クレームは、storageClassName属性を使用してストレージクラスの名前を指定することにより、特定のクラスを要求できます。PVCにバインドできるのは、PVCと同じstorageClassNameを持つ、要求されたクラスのPVのみです。

PVCは必ずしもクラスをリクエストする必要はありません。storageClassName""に設定されているPVCは、クラスのないPVを要求していると常に解釈されるため、クラスのないPVにのみバインドできます(アノテーションがないか、""に等しい1つのセット)。storageClassNameのないPVCはまったく同じではなく、DefaultStorageClassアドミッションプラグインがオンになっているかどうかによって、クラスターによって異なる方法で処理されます。

  • アドミッションプラグインがオンになっている場合、管理者はデフォルトのStorageClassを指定できます。storageClassNameを持たないすべてのPVCは、そのデフォルトのPVにのみバインドできます。デフォルトのStorageClassの指定は、StorageClassオブジェクトでstorageclass.kubernetes.io/is-default-classアノテーションをtrueに設定することにより行われます。管理者がデフォルトを指定しない場合、クラスターは、アドミッションプラグインがオフになっているかのようにPVC作成をレスポンスします。複数のデフォルトが指定されている場合、アドミッションプラグインはすべてのPVCの作成を禁止します。
  • アドミッションプラグインがオフになっている場合、デフォルトのStorageClassの概念はありません。storageClassNameを持たないすべてのPVCは、クラスを持たないPVにのみバインドできます。この場合、storageClassNameを持たないPVCは、storageClassName""に設定されているPVCと同じように扱われます。

インストール方法によっては、インストール時にアドオンマネージャーによってデフォルトのストレージクラスがKubernetesクラスターにデプロイされる場合があります。

PVCがselectorを要求することに加えてStorageClassを指定する場合、要件はANDで一緒に結合されます。要求されたクラスのPVと要求されたラベルのみがPVCにバインドされます。

以前は、storageClassName属性の代わりにvolume.beta.kubernetes.io/storage-classアノテーションが使用されていました。このアノテーションはまだ機能しています。ただし、今後のKubernetesリリースではサポートされません。

ボリュームとしてのクレーム

Podは、クレームをボリュームとして使用してストレージにアクセスします。クレームは、そのクレームを使用するPodと同じ名前空間に存在する必要があります。クラスターは、Podの名前空間でクレームを見つけ、それを使用してクレームを支援しているPersistentVolumeを取得します。次に、ボリュームがホストとPodにマウントされます。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

名前空間に関する注意

PersistentVolumeバインドは排他的であり、PersistentVolumeClaimは名前空間オブジェクトであるため、"多"モード(ROXRWX)でクレームをマウントすることは1つの名前空間内でのみ可能です。

Rawブロックボリュームのサポート

FEATURE STATE: Kubernetes v1.18 [stable]

次のボリュームプラグインは、必要に応じて動的プロビジョニングを含むrawブロックボリュームをサポートします。

  • AWSElasticBlockStore
  • AzureDisk
  • CSI
  • FC (Fibre Channel)
  • GCEPersistentDisk
  • iSCSI
  • Local volume
  • OpenStack Cinder
  • RBD (Ceph Block Device)
  • VsphereVolume

Rawブロックボリュームを使用した永続ボリューム

apiVersion: v1
kind: PersistentVolume
metadata:
  name: block-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  persistentVolumeReclaimPolicy: Retain
  fc:
    targetWWNs: ["50060e801049cfd1"]
    lun: 0
    readOnly: false

Rawブロックボリュームを要求する永続ボリュームクレーム

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 10Gi

コンテナにRawブロックデバイスパスを追加するPod仕様

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-block-volume
spec:
  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: [ "tail -f /dev/null" ]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: block-pvc

ブロックボリュームのバインド

ユーザーがPersistentVolumeClaim specのvolumeModeフィールドを使用してrawブロックボリュームの要求を示す場合、バインディングルールは、このモードをspecの一部として考慮しなかった以前のリリースとわずかに異なります。表にリストされているのは、ユーザーと管理者がrawブロックデバイスを要求するために指定可能な組み合わせの表です。この表は、ボリュームがバインドされるか、組み合わせが与えられないかを示します。静的にプロビジョニングされたボリュームのボリュームバインディングマトリクスはこちらです。

PVボリュームモードPVCボリュームモード結果
未定義未定義バインド
未定義ブロックバインドなし
未定義ファイルシステムバインド
ブロック未定義バインドなし
ブロックブロックバインド
ブロックファイルシステムバインドなし
ファイルシステムファイルシステムバインド
ファイルシステムブロックバインドなし
ファイルシステム未定義バインド

ボリュームのスナップショットとスナップショットからのボリュームの復元のサポート

FEATURE STATE: Kubernetes v1.17 [beta]

ボリュームスナップショット機能は、CSIボリュームプラグインのみをサポートするために追加されました。詳細については、ボリュームのスナップショットを参照してください。

ボリュームスナップショットのデータソースからボリュームを復元する機能を有効にするには、apiserverおよびcontroller-managerでVolumeSnapshotDataSourceフィーチャーゲートを有効にします。

ボリュームスナップショットから永続ボリュームクレームを作成する

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: restore-pvc
spec:
  storageClassName: csi-hostpath-sc
  dataSource:
    name: new-snapshot-test
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

ボリュームの複製

ボリュームの複製はCSIボリュームプラグインにのみ利用可能です。

PVCデータソースからのボリューム複製機能を有効にするには、apiserverおよびcontroller-managerでVolumeSnapshotDataSourceフィーチャーゲートを有効にします。

既存のPVCからの永続ボリュームクレーム作成

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cloned-pvc
spec:
  storageClassName: my-csi-plugin
  dataSource:
    name: existing-src-pvc-name
    kind: PersistentVolumeClaim
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

可搬性の高い設定の作成

もし幅広いクラスターで実行され、永続ボリュームが必要となる構成テンプレートやサンプルを作成している場合は、次のパターンを使用することをお勧めします。

  • 構成にPersistentVolumeClaimオブジェクトを含める(DeploymentやConfigMapと共に)

  • ユーザーが設定をインスタンス化する際にPersistentVolumeを作成する権限がない場合があるため、設定にPersistentVolumeオブジェクトを含めない。

  • テンプレートをインスタンス化する時にストレージクラス名を指定する選択肢をユーザーに与える

    • ユーザーがストレージクラス名を指定する場合、persistentVolumeClaim.storageClassName フィールドにその値を入力する。これにより、クラスターが管理者によって有効にされたストレージクラスを持っている場合、PVCは正しいストレージクラスと一致する。
    • ユーザーがストレージクラス名を指定しない場合、persistentVolumeClaim.storageClassNameフィールドはnilのままにする。これにより、PVはユーザーにクラスターのデフォルトストレージクラスで自動的にプロビジョニングされる。多くのクラスター環境ではデフォルトのストレージクラスがインストールされているが、管理者は独自のデフォルトストレージクラスを作成することができる。
  • ツールがPVCを監視し、しばらくしてもバインドされないことをユーザーに表示する。これはクラスターが動的ストレージをサポートしない(この場合ユーザーは対応するPVを作成するべき)、もしくはクラスターがストレージシステムを持っていない(この場合ユーザーはPVCを必要とする設定をデプロイできない)可能性があることを示す。

    次の項目

リファレンス

3.6.3 - 投影ボリューム

このドキュメントでは、Kubernetesの投影ボリュームについて説明します。ボリュームに精通していることをお勧めします。

概要

ボリュームは、いくつかの既存の投影ボリュームソースを同じディレクトリにマップします。

現在、次のタイプのボリュームソースを投影できます。

すべてのソースは、Podと同じnamespaceにある必要があります。詳細はall-in-one volumeデザインドキュメントを参照してください。

secret、downwardAPI、およびconfigMapを使用した構成例

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
            - key: config
              path: my-group/my-config

構成例:デフォルト以外のアクセス許可モードが設定されたsecret

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - secret:
          name: mysecret2
          items:
            - key: password
              path: my-group/my-password
              mode: 511

各投影ボリュームソースは、specのsourcesにリストされています。パラメーターは、2つの例外を除いてほぼ同じです。

  • secretについて、ConfigMapの命名と一致するようにsecretNameフィールドがnameに変更されました。
  • defaultModeはprojectedレベルでのみ指定でき、各ボリュームソースには指定できません。ただし上に示したように、個々の投影ごとにmodeを明示的に設定できます。

TokenRequestProjection機能が有効になっている場合、現在のサービスアカウントトークンを指定されたパスのPodに挿入できます。例えば:

apiVersion: v1
kind: Pod
metadata:
  name: sa-token-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: token-vol
      mountPath: "/service-account"
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: token-vol
    projected:
      sources:
      - serviceAccountToken:
          audience: api
          expirationSeconds: 3600
          path: token

この例のPodには、挿入されたサービスアカウントトークンを含む投影ボリュームがあります。このトークンはPodのコンテナがKubernetes APIサーバーにアクセスするために使用できます。このaudienceフィールドにはトークンの受信対象者が含まれています。トークンの受信者は、トークンのaudienceフィールドで指定された識別子で自分自身であるかを識別します。そうでない場合はトークンを拒否します。このフィールドはオプションで、デフォルトではAPIサーバーの識別子が指定されます。

expirationSecondsはサービスアカウントトークンが有効であると予想される期間です。 デフォルトは1時間で、最低でも10分(600秒)でなければなりません。 管理者は、APIサーバーに--service-account-max-token-expirationオプションを指定することで、その最大値を制限することも可能です。 pathフィールドは、投影ボリュームのマウントポイントへの相対パスを指定します。

SecurityContextの相互作用

サービスアカウントの投影ボリューム拡張でのファイル権限処理の提案により、正しい所有者権限が設定された投影ファイルが導入されました。

Linux

投影ボリュームがあり、PodのSecurityContextRunAsUserが設定されているLinux Podでは、投影されたファイルには、コンテナユーザーの所有権を含む正しい所有権が設定されます。

Windows

投影ボリュームを持ち、PodのSecurityContextRunAsUsernameを設定したWindows Podでは、Windowsのユーザーアカウント管理方法により所有権が強制されません。 Windowsは、ローカルユーザーとグループアカウントをセキュリティアカウントマネージャー(SAM)と呼ばれるデータベースファイルに保存し、管理します。 各コンテナはSAMデータベースの独自のインスタンスを維持し、コンテナの実行中はホストはそのインスタンスを見ることができません。 Windowsコンテナは、OSのユーザーモード部分をホストから分離して実行するように設計されており、そのため仮想SAMデータベースを維持することになります。 そのため、ホスト上で動作するkubeletには、仮想化されたコンテナアカウントのホストファイル所有権を動的に設定する機能がありません。 ホストマシン上のファイルをコンテナと共有する場合は、C:\以外の独自のボリュームマウントに配置することをお勧めします。

デフォルトでは、投影ボリュームファイルの例に示されているように、投影されたファイルには次の所有権があります。

PS C:\> Get-Acl C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt | Format-List

Path   : Microsoft.PowerShell.Core\FileSystem::C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt
Owner  : BUILTIN\Administrators
Group  : NT AUTHORITY\SYSTEM
Access : NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
Audit  :
Sddl   : O:BAG:SYD:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;0x1200a9;;;BU)

これは、ContainerAdministratorのようなすべての管理者ユーザーが読み取り、書き込み、および実行アクセス権を持ち、非管理者ユーザーが読み取りおよび実行アクセス権を持つことを意味します。

3.6.4 - エフェメラルボリューム

このドキュメントでは、Kubernetesのエフェメラルボリュームについて説明します。ボリューム、特にPersistentVolumeClaimとPersistentVolumeに精通していることをお勧めします。

一部のアプリケーションでは追加のストレージが必要ですが、そのデータが再起動後も永続的に保存されるかどうかは気にしません。 たとえば、キャッシュサービスは多くの場合メモリサイズによって制限されており、使用頻度の低いデータを、全体的なパフォーマンスにほとんど影響を与えずに、メモリよりも低速なストレージに移動できます。

他のアプリケーションは、構成データや秘密鍵など、読み取り専用の入力データがファイルに存在することを想定しています。

エフェメラルボリュームは、これらのユースケース向けに設計されています。 ボリュームはPodの存続期間に従い、Podとともに作成および削除されるため、Podは、永続ボリュームが利用可能な場所に制限されることなく停止および再起動できます。

エフェメラルボリュームはPod仕様でインラインで指定されているため、アプリケーションの展開と管理が簡素化されます。

エフェメラルボリュームのタイプ

Kubernetesは、さまざまな目的のためにいくつかの異なる種類のエフェメラルボリュームをサポートしています。

emptyDirconfigMapdownwardAPIsecretローカルエフェメラルストレージとして提供されます。 これらは、各ノードのkubeletによって管理されます。

CSIエフェメラルボリュームは、サードパーティーのCSIストレージドライバーによって提供される必要があります

汎用エフェメラルボリュームは、サードパーティーのCSIストレージドライバーによって提供される可能性がありますが、動的プロビジョニングをサポートする他のストレージドライバーによって提供されることもあります。一部のCSIドライバーは、CSIエフェメラルボリューム用に特別に作成されており、動的プロビジョニングをサポートしていません。これらは汎用エフェメラルボリュームには使用できません。

サードパーティー製ドライバーを使用する利点は、Kubernetes自体がサポートしていない機能を提供できることです。たとえば、kubeletによって管理されるディスクとは異なるパフォーマンス特性を持つストレージや、異なるデータの挿入などです。

CSIエフェメラルボリューム

FEATURE STATE: Kubernetes v1.25 [stable]

概念的には、CSIエフェメラルボリュームはconfigMapdownwardAPI、およびsecretボリュームタイプに似ています。 ストレージは各ノードでローカルに管理され、Podがノードにスケジュールされた後に他のローカルリソースと一緒に作成されます。Kubernetesには、この段階でPodを再スケジュールするという概念はもうありません。 ボリュームの作成は、失敗する可能性が低くなければなりません。さもないと、Podの起動が停止します。 特に、ストレージ容量を考慮したPodスケジューリングは、これらのボリュームではサポートされていません。 これらは現在、Podのストレージリソースの使用制限の対象外です。これは、kubeletが管理するストレージに対してのみ強制できるものであるためです。

CSIエフェメラルストレージを使用するPodのマニフェストの例を次に示します。

kind: Pod
apiVersion: v1
metadata:
  name: my-csi-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/data"
        name: my-csi-inline-vol
      command: [ "sleep", "1000000" ]
  volumes:
    - name: my-csi-inline-vol
      csi:
        driver: inline.storage.kubernetes.io
        volumeAttributes:
          foo: bar

volumeAttributesは、ドライバーによって準備されるボリュームを決定します。これらの属性は各ドライバーに固有のものであり、標準化されていません。詳細な手順については、各CSIドライバーのドキュメントを参照してください。

CSIドライバーの制限事項

CSIエフェメラルボリュームを使用すると、ユーザーはPod仕様の一部としてvolumeAttributesをCSIドライバーに直接提供できます。 通常は管理者に制限されているvolumeAttributesを許可するCSIドライバーは、インラインエフェメラルボリュームでの使用には適していません。 たとえば、通常StorageClassで定義されるパラメーターは、インラインエフェメラルボリュームを使用してユーザーに公開しないでください。

Pod仕様内でインラインボリュームとして使用できるCSIドライバーを制限する必要があるクラスター管理者は、次の方法で行うことができます。

  • CSIドライバー仕様のvolumeLifecycleModesからEphemeralを削除します。これにより、ドライバーをインラインエフェメラルボリュームとして使用できなくなります。
  • admission webhookを使用して、このドライバーの使用方法を制限します。

汎用エフェメラルボリューム

FEATURE STATE: Kubernetes v1.23 [stable]

汎用エフェメラルボリュームは、プロビジョニング後に通常は空であるスクラッチデータ用のPodごとのディレクトリを提供するという意味で、emptyDirボリュームに似ています。ただし、追加の機能がある場合もあります。

  • ストレージは、ローカルまたはネットワークに接続できます。
  • ボリュームは、Podが超えることができない固定サイズを持つことができます。
  • ボリュームには、ドライバーとパラメーターによっては、いくつかの初期データがある場合があります。
  • スナップショットクローン作成サイズ変更ストレージ容量の追跡などボリュームに対する一般的な操作は、ドライバーがそれらをサポートしていることを前提としてサポートされています。

例:

kind: Pod
apiVersion: v1
metadata:
  name: my-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/scratch"
        name: scratch-volume
      command: [ "sleep", "1000000" ]
  volumes:
    - name: scratch-volume
      ephemeral:
        volumeClaimTemplate:
          metadata:
            labels:
              type: my-frontend-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "scratch-storage-class"
            resources:
              requests:
                storage: 1Gi

LifecycleとPersistentVolumeClaim

設計上の重要なアイデアは、ボリュームクレームのパラメーターがPodのボリュームソース内で許可されることです。 PersistentVolumeClaimのラベル、アノテーション、および一連のフィールド全体がサポートされています。 そのようなPodが作成されると、エフェメラルボリュームコントローラーは、Podと同じ名前空間に実際のPersistentVolumeClaimオブジェクトを作成し、Podが削除されたときにPersistentVolumeClaimが確実に削除されるようにします。

これにより、ボリュームバインディングおよび/またはプロビジョニングがトリガーされます。 これは、StorageClassが即時ボリュームバインディングを使用する場合、またはPodが一時的にノードにスケジュールされている場合(WaitForFirstConsumerボリュームバインディングモード)のいずれかです。 後者は、スケジューラーがPodに適したノードを自由に選択できるため、一般的なエフェメラルボリュームに推奨されます。即時バインディングでは、ボリュームが利用可能になった時点で、ボリュームにアクセスできるノードをスケジューラーが選択する必要があります。

リソースの所有権に関して、一般的なエフェメラルストレージを持つPodは、そのエフェメラルストレージを提供するPersistentVolumeClaimの所有者です。Podが削除されると、KubernetesガベージコレクターがPVCを削除します。これにより、通常、ボリュームの削除がトリガーされます。これは、ストレージクラスのデフォルトの再利用ポリシーがボリュームを削除することであるためです。retainの再利用ポリシーを持つStorageClassを使用して、準エフェメラルなローカルストレージを作成できます。ストレージはPodよりも長く存続します。この場合、ボリュームのクリーンアップが個別に行われるようにする必要があります。

これらのPVCは存在しますが、他のPVCと同様に使用できます。特に、ボリュームのクローン作成またはスナップショットでデータソースとして参照できます。PVCオブジェクトは、ボリュームの現在のステータスも保持します。

PersistentVolumeClaimの命名

自動的に作成されたPVCの命名は決定論的です。名前はPod名とボリューム名を組み合わせたもので、途中にハイフン(-)があります。上記の例では、PVC名はmy-app-scratch-volumeになります。この決定論的な命名により、Pod名とボリューム名が分かればPVCを検索する必要がないため、PVCとの対話が容易になります。

また、決定論的な命名では、異なるPod間、およびPodと手動で作成されたPVCの間で競合が発生する可能性があります(ボリュームが"scratch"のPod"pod-a"と、名前が"pod"でボリュームが"a-scratch"の別のPodは、どちらも同じPVC名"pod-a-scratch")。

次のような競合が検出されます。Pod用に作成された場合、PVCはエフェメラルボリュームにのみ使用されます。このチェックは、所有関係に基づいています。既存のPVCは上書きまたは変更されません。ただし、適切なPVCがないとPodを起動できないため、これでは競合が解決されません。

セキュリティ

GenericEphemeralVolume機能を有効にすると、ユーザーは、PVCを直接作成する権限がなくても、Podを作成できる場合、間接的にPVCを作成できます。クラスター管理者はこれを認識している必要があります。これがセキュリティモデルに適合しない場合は、一般的なエフェメラルボリュームを持つPodなどのオブジェクトを拒否するadmission webhookを使用する必要があります。

通常のPVCの名前空間割り当ては引き続き適用されるため、ユーザーがこの新しいメカニズムの使用を許可されたとしても、他のポリシーを回避するために使用することはできません。

次の項目

kubeletによって管理されるエフェメラルボリューム

ローカルエフェメラルボリュームを参照してください。

CSIエフェメラルボリューム

汎用エフェメラルボリューム

3.6.5 - ストレージクラス

このドキュメントでは、KubernetesにおけるStorageClassの概念について説明します。ボリューム永続ボリュームに精通していることをお勧めします。

概要

StorageClassは、管理者が提供するストレージの「クラス」を記述する方法を提供します。さまざまなクラスが、サービス品質レベル、バックアップポリシー、またはクラスター管理者によって決定された任意のポリシーにマップされる場合があります。Kubernetes自体は、クラスが何を表すかについて意見を持っていません。この概念は、他のストレージシステムでは「プロファイル」と呼ばれることがあります。

StorageClassリソース

各StorageClassには、クラスに属するPersistentVolumeを動的にプロビジョニングする必要がある場合に使用されるフィールドprovisionerparameters、およびreclaimPolicyが含まれています。

StorageClassオブジェクトの名前は重要であり、ユーザーが特定のクラスを要求する方法です。管理者は、最初にStorageClassオブジェクトを作成するときにクラスの名前とその他のパラメーターを設定します。オブジェクトは、作成後に更新することはできません。

管理者は、バインドする特定のクラスを要求しないPVCに対してのみ、デフォルトのStorageClassを指定できます。詳細については、PersistentVolumeClaimセクションを参照してください。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate

プロビジョナー

各StorageClassには、PVのプロビジョニングに使用するボリュームプラグインを決定するプロビジョナーがあります。このフィールドを指定する必要があります。

Volume PluginInternal ProvisionerConfig Example
AWSElasticBlockStoreAWS EBS
AzureFileAzure File
AzureDiskAzure Disk
CephFS--
CinderOpenStack Cinder
FC--
FlexVolume--
GCEPersistentDiskGCE PD
GlusterfsGlusterfs
iSCSI--
NFS-NFS
RBDCeph RBD
VsphereVolumevSphere
PortworxVolumePortworx Volume
Local-Local

ここにリストされている「内部」プロビジョナー(名前には「kubernetes.io」というプレフィックスが付いており、Kubernetesと共に出荷されます)を指定することに制限はありません。Kubernetesによって定義された仕様に従う独立したプログラムである外部プロビジョナーを実行して指定することもできます。外部プロビジョナーの作成者は、コードの保存場所、プロビジョナーの出荷方法、実行方法、使用するボリュームプラグイン(Flexを含む)などについて完全な裁量権を持っています。リポジトリkubernetes-sigs/sig-storage-lib-external-provisionerには、仕様の大部分を実装する外部プロビジョナーを作成するためのライブラリが含まれています。一部の外部プロビジョナーは、リポジトリkubernetes-sigs/sig-storage-lib-external-provisionerの下にリストされています。

たとえば、NFSは内部プロビジョナーを提供しませんが、外部プロビジョナーを使用できます。サードパーティのストレージベンダーが独自の外部プロビジョナーを提供する場合もあります。

再利用ポリシー

StorageClassによって動的に作成されるPersistentVolumeには、クラスのreclaimPolicyフィールドで指定された再利用ポリシーがあり、DeleteまたはRetainのいずれかになります。StorageClassオブジェクトの作成時にreclaimPolicyが指定されていない場合、デフォルトでDeleteになります。

手動で作成され、StorageClassを介して管理されるPersistentVolumeには、作成時に割り当てられた再利用ポリシーが適用されます。

ボリューム拡張の許可

FEATURE STATE: Kubernetes v1.11 [beta]

PersistentVolumeは、拡張可能になるように構成できます。この機能をtrueに設定すると、ユーザーは対応するPVCオブジェクトを編集してボリュームのサイズを変更できます。

次のタイプのボリュームは、基になるStorageClassのフィールドallowVolumeExpansionがtrueに設定されている場合に、ボリュームの拡張をサポートします。

Table of Volume types and the version of Kubernetes they require
Volume typeRequired Kubernetes version
gcePersistentDisk1.11
awsElasticBlockStore1.11
Cinder1.11
glusterfs1.11
rbd1.11
Azure File1.11
Azure Disk1.11
Portworx1.11
FlexVolume1.13
CSI1.14 (alpha), 1.16 (beta)

マウントオプション

StorageClassによって動的に作成されるPersistentVolumeには、クラスのmountOptionsフィールドで指定されたマウントオプションがあります。

ボリュームプラグインがマウントオプションをサポートしていないにもかかわらず、マウントオプションが指定されている場合、プロビジョニングは失敗します。マウントオプションは、クラスまたはPVのいずれでも検証されません。マウントオプションが無効な場合、PVマウントは失敗します。

ボリュームバインディングモード

volumeBindingModeフィールドは、ボリュームバインディングと動的プロビジョニングが発生するタイミングを制御します。設定を解除すると、デフォルトで"Immediate"モードが使用されます。

Immediateモードは、PersistentVolumeClaimが作成されると、ボリュームバインディングと動的プロビジョニングが発生することを示します。トポロジに制約があり、クラスター内のすべてのノードからグローバルにアクセスできないストレージバックエンドの場合、PersistentVolumeはPodのスケジューリング要件を知らなくてもバインドまたはプロビジョニングされます。これにより、Podがスケジュール不能になる可能性があります。

クラスター管理者は、PersistentVolumeClaimを使用するPodが作成されるまでPersistentVolumeのバインドとプロビジョニングを遅らせるWaitForFirstConsumerモードを指定することで、この問題に対処できます。 PersistentVolumeは、Podのスケジュール制約によって指定されたトポロジに準拠して選択またはプロビジョニングされます。これらには、リソース要件ノードセレクターポッドアフィニティとアンチアフィニティ、およびtaints and tolerationsが含まれますが、これらに限定されません。

次のプラグインは、動的プロビジョニングでWaitForFirstConsumerをサポートしています。

次のプラグインは、事前に作成されたPersistentVolumeバインディングでWaitForFirstConsumerをサポートします。

FEATURE STATE: Kubernetes v1.17 [stable]
CSIボリュームも動的プロビジョニングと事前作成されたPVでサポートされていますが、サポートされているトポロジーキーと例を確認するには、特定のCSIドライバーのドキュメントを参照する必要があります。

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  nodeSelector:
    kubernetes.io/hostname: kube-01
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

許可されたトポロジー

クラスターオペレーターがWaitForFirstConsumerボリュームバインディングモードを指定すると、ほとんどの状況でプロビジョニングを特定のトポロジに制限する必要がなくなります。ただし、それでも必要な場合は、allowedTopologiesを指定できます。

この例は、プロビジョニングされたボリュームのトポロジを特定のゾーンに制限する方法を示しており、サポートされているプラグインのzoneおよびzonesパラメーターの代わりとして使用する必要があります。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
  - key: failure-domain.beta.kubernetes.io/zone
    values:
    - us-central-1a
    - us-central-1b

パラメーター

ストレージクラスには、ストレージクラスに属するボリュームを記述するパラメーターがあります。プロビジョナーに応じて、異なるパラメーターが受け入れられる場合があります。たとえば、パラメーターtypeの値io1とパラメーターiopsPerGBはEBSに固有です。パラメーターを省略すると、デフォルトが使用されます。

StorageClassに定義できるパラメーターは最大512個です。 キーと値を含むパラメーターオブジェクトの合計の長さは、256KiBを超えることはできません。

AWS EBS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4
  • type:io1gp2sc1st1。詳細については、AWSドキュメントを参照してください。デフォルト:gp2
  • zone(非推奨):AWS zone。zonezonesも指定されていない場合、ボリュームは通常、Kubernetesクラスターにノードがあるすべてのアクティブなゾーンにわたってラウンドロビン方式で処理されます。zoneパラメーターとzonesパラメーターを同時に使用することはできません。
  • zones(非推奨):AWS zoneのコンマ区切りリスト。zonezonesも指定されていない場合、ボリュームは通常、Kubernetesクラスターにノードがあるすべてのアクティブなゾーンにわたってラウンドロビン方式で処理されます。zoneパラメーターとzonesパラメーターを同時に使用することはできません。
  • iopsPerGB:io1ボリュームのみ。GiBごとの1秒あたりのI/O操作。AWSボリュームプラグインは、これを要求されたボリュームのサイズで乗算して、ボリュームのIOPSを計算し、上限を20,000IOPSに設定します(AWSでサポートされる最大値については、AWSドキュメントを参照してください)。ここでは文字列が必要です。つまり、10ではなく"10"です。
  • fsType:kubernetesでサポートされているfsType。デフォルト:"ext4"
  • encrypted:EBSボリュームを暗号化するかどうかを示します。有効な値は"true"または"false"です。ここでは文字列が必要です。つまり、trueではなく"true"です。
  • kmsKeyId:オプション。ボリュームを暗号化するときに使用するキーの完全なAmazonリソースネーム。何も指定されていなくてもencryptedがtrueの場合、AWSによってキーが生成されます。有効なARN値については、AWSドキュメントを参照してください。

GCE PD

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  fstype: ext4
  replication-type: none
  • type:pd-standardまたはpd-ssd。デフォルト:pd-standard
  • zone(非推奨):GCE zone。zonezonesも指定されていない場合、ボリュームは通常、Kubernetesクラスターにノードがあるすべてのアクティブなゾーンにわたってラウンドロビン方式で処理されます。zoneパラメーターとzonesパラメーターを同時に使用することはできません。
  • zones(非推奨):GCE zoneのコンマ区切りリスト。zonezonesも指定されていない場合、ボリュームは通常、Kubernetesクラスターにノードがあるすべてのアクティブなゾーンにわたってラウンドロビン方式で処理されます。zoneパラメーターとzonesパラメーターを同時に使用することはできません。
  • fstype:ext4またはxfs。デフォルト:ext4。定義されたファイルシステムタイプは、ホストオペレーティングシステムでサポートされている必要があります。
  • replication-type:noneまたはregional-pd。デフォルト:none

replication-typenoneに設定されている場合、通常の(ゾーン)PDがプロビジョニングされます。

replication-typeregional-pdに設定されている場合、Regional Persistent Diskがプロビジョニングされます。volumeBindingMode: WaitForFirstConsumerを設定することを強くお勧めします。この場合、このStorageClassを使用するPersistentVolumeClaimを使用するPodを作成すると、Regional Persistent Diskが2つのゾーンでプロビジョニングされます。1つのゾーンは、Podがスケジュールされているゾーンと同じです。もう1つのゾーンは、クラスターで使用可能なゾーンからランダムに選択されます。ディスクゾーンは、allowedTopologiesを使用してさらに制限できます。

Glusterfs(非推奨)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://127.0.0.1:8081"
  clusterid: "630372ccdc720a92c681fb928f27b53f"
  restauthenabled: "true"
  restuser: "admin"
  secretNamespace: "default"
  secretName: "heketi-secret"
  gidMin: "40000"
  gidMax: "50000"
  volumetype: "replicate:3"
  • resturl:glusterボリュームをオンデマンドでプロビジョニングするGluster RESTサービス/HeketiサービスのURL。一般的な形式はIPaddress:Portである必要があり、これはGlusterFS動的プロビジョナーの必須パラメーターです。Heketiサービスがopenshift/kubernetesセットアップでルーティング可能なサービスとして公開されている場合、これはhttp://heketi-storage-project.cloudapps.mystorage.comのような形式になる可能性があります。ここで、fqdnは解決可能なHeketiサービスURLです。

  • restauthenabled:RESTサーバーへの認証を有効にするGluster RESTサービス認証ブール値。この値が"true"の場合、restuserrestuserkeyまたはsecretNamespace+secretNameを入力する必要があります。このオプションは非推奨です。restuserrestuserkeysecretName、またはsecretNamespaceのいずれかが指定されている場合、認証が有効になります。

  • restuser:Gluster Trusted Poolでボリュームを作成するためのアクセス権を持つGluster RESTサービス/Heketiユーザー。

  • restuserkey:RESTサーバーへの認証に使用されるGluster RESTサービス/Heketiユーザーのパスワード。このパラメーターは、secretNamespace+secretNameを優先されて廃止されました。

  • secretNamespacesecretName:Gluster RESTサービスと通信するときに使用するユーザーパスワードを含むSecretインスタンスの識別。これらのパラメーターはオプションです。secretNamespacesecretNameの両方が省略された場合、空のパスワードが使用されます。提供されたシークレットには、タイプkubernetes.io/glusterfsが必要です。たとえば、次のように作成されます。

    kubectl create secret generic heketi-secret \
      --type="kubernetes.io/glusterfs" --from-literal=key='opensesame' \
      --namespace=default
    

    シークレットの例はglusterfs-provisioning-secret.yamlにあります。

  • clusterid:630372ccdc720a92c681fb928f27b53fは、ボリュームのプロビジョニング時にHeketiによって使用されるクラスターのIDです。また、クラスターIDのリストにすることもできます。これはオプションのパラメーターです。

  • gidMingidMax:StorageClassのGID範囲の最小値と最大値。この範囲内の一意の値(GID)(gidMin-gidMax)が、動的にプロビジョニングされたボリュームに使用されます。これらはオプションの値です。指定しない場合、ボリュームは、それぞれgidMinとgidMaxのデフォルトである2000から2147483647の間の値でプロビジョニングされます。

  • volumetype:ボリュームタイプとそのパラメーターは、このオプションの値で構成できます。ボリュームタイプが記載されていない場合、プロビジョニング担当者がボリュームタイプを決定します。 例えば、

    • レプリカボリューム:volumetype: replica:3ここで、'3'はレプリカ数です。
    • Disperse/ECボリューム:volumetype: disperse:4:2ここで、'4'はデータ、'2'は冗長数です。
    • ボリュームの分配:volumetype: none

    利用可能なボリュームタイプと管理オプションについては、管理ガイドを参照してください。

    詳細な参考情報については、Heketiの設定方法を参照してください。

    永続ボリュームが動的にプロビジョニングされると、Glusterプラグインはエンドポイントとヘッドレスサービスをgluster-dynamic-<claimname>という名前で自動的に作成します。永続ボリューム要求が削除されると、動的エンドポイントとサービスは自動的に削除されます。

NFS

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: example-nfs
provisioner: example.com/external-nfs
parameters:
  server: nfs-server.example.com
  path: /share
  readOnly: "false"
  • server:サーバーは、NFSサーバーのホスト名またはIPアドレスです。
  • path:NFSサーバーによってエクスポートされるパス。
  • readOnly:ストレージが読み取り専用としてマウントされるかどうかを示すフラグ(デフォルトはfalse)。

Kubernetesには、内部NFSプロビジョナーは含まれていません。NFS用のStorageClassを作成するには、外部プロビジョナーを使用する必要があります。 ここではいくつかの例を示します。

OpenStack Cinder

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gold
provisioner: kubernetes.io/cinder
parameters:
  availability: nova
  • availability:アベイラビリティゾーン。指定されていない場合、ボリュームは通常、Kubernetesクラスターにノードがあるすべてのアクティブなゾーンにわたってラウンドロビン方式で処理されます。

vSphere

vSphereストレージクラスのプロビジョナーには2つのタイプがあります。

インツリープロビジョナーは非推奨です。CSIプロビジョナーの詳細については、Kubernetes vSphere CSIドライバーおよびvSphereVolume CSI移行を参照してください。

CSIプロビジョナー

vSphere CSI StorageClassプロビジョナーは、Tanzu Kubernetesクラスターと連携します。例については、vSphere CSIリポジトリを参照してください。

vCPプロビジョナー

次の例では、VMware Cloud Provider(vCP) StorageClassプロビジョナーを使用しています。

  1. ユーザー指定のディスク形式でStorageClassを作成します。

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
      diskformat: zeroedthick
    

    diskformat:thinzeroedthickeagerzeroedthick。デフォルト:"thin".

  2. ユーザー指定のデータストアにディスクフォーマットのStorageClassを作成します。

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
        diskformat: zeroedthick
        datastore: VSANDatastore
    

    datastore:ユーザーはStorageClassでデータストアを指定することもできます。 ボリュームは、StorageClassで指定されたデータストア(この場合はVSANDatastore)に作成されます。このフィールドはオプションです。データストアが指定されていない場合、vSphere Cloud Providerの初期化に使用されるvSphere構成ファイルで指定されたデータストアにボリュームが作成されます。

  3. kubernetes内のストレージポリシー管理

    • 既存のvCenter SPBMポリシーを使用

      vSphere for Storage Managementの最も重要な機能の1つは、ポリシーベースの管理です。Storage Policy Based Management(SPBM)は、幅広いデータサービスとストレージソリューションにわたって単一の統合コントロールプレーンを提供するストレージポリシーフレームワークです。SPBMにより、vSphere管理者は、キャパシティプランニング、差別化されたサービスレベル、キャパシティヘッドルームの管理など、事前のストレージプロビジョニングの課題を克服できます。SPBMポリシーは、storagePolicyNameパラメーターを使用してStorageClassで指定できます。

    • Kubernetes内でのVirtual SANポリシーのサポート

      Vsphere Infrastructure(VI)管理者は、動的ボリュームプロビジョニング中にカスタムVirtual SANストレージ機能を指定できます。動的なボリュームプロビジョニング時に、パフォーマンスや可用性などのストレージ要件をストレージ機能の形で定義できるようになりました。ストレージ機能の要件はVirtual SANポリシーに変換され、永続ボリューム(仮想ディスク)の作成時にVirtual SANレイヤーにプッシュダウンされます。仮想ディスクは、要件を満たすためにVirtual SANデータストア全体に分散されます。

      永続的なボリューム管理にストレージポリシーを使用する方法の詳細については、ボリュームの動的プロビジョニングのためのストレージポリシーベースの管理を参照してください。

vSphereの例 では、Kubernetes for vSphere内で永続的なボリューム管理を試すことができます。

Ceph RBD

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/rbd
parameters:
  monitors: 10.16.153.105:6789
  adminId: kube
  adminSecretName: ceph-secret
  adminSecretNamespace: kube-system
  pool: kube
  userId: kube
  userSecretName: ceph-secret-user
  userSecretNamespace: default
  fsType: ext4
  imageFormat: "2"
  imageFeatures: "layering"
  • monitors:カンマ区切りのCephモニター。このパラメーターは必須です。

  • adminId:プールにイメージを作成できるCephクライアントID。デフォルトは"admin"です。

  • adminSecretName:adminIdのシークレット名。このパラメーターは必須です。指定されたシークレットのタイプは"kubernetes.io/rbd"である必要があります。

  • adminSecretNamespace:adminSecretNameの名前空間。デフォルトは"default"です。

  • pool:Ceph RBDプール。デフォルトは"rbd"です。

  • userId:RBDイメージのマッピングに使用されるCephクライアントID。デフォルトはadminIdと同じです。

  • userSecretName:RBDイメージをマップするためのuserIdのCephシークレットの名前。PVCと同じ名前空間に存在する必要があります。このパラメーターは必須です。提供されたシークレットのタイプは"kubernetes.io/rbd"である必要があります。たとえば、次のように作成されます。

    kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" \
      --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' \
      --namespace=kube-system
    
  • userSecretNamespace:userSecretNameの名前空間。

  • fsType:kubernetesでサポートされているfsType。デフォルト:"ext4"

  • imageFormat:Ceph RBDイメージ形式、"1"または"2"。デフォルトは"2"です。

  • imageFeatures:このパラメーターはオプションであり、imageFormatを"2"に設定した場合にのみ使用する必要があります。現在サポートされている機能はlayeringのみです。デフォルトは""で、オンになっている機能はありません。

Azure Disk

Azure Unmanaged Disk storage class

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name
  • skuName:AzureストレージアカウントのSku層。デフォルトは空です。
  • location:Azureストレージアカウントの場所。デフォルトは空です。
  • storageAccount:Azureストレージアカウント名。ストレージアカウントを指定する場合、それはクラスターと同じリソースグループに存在する必要があり、locationは無視されます。ストレージアカウントが指定されていない場合、クラスターと同じリソースグループに新しいストレージアカウントが作成されます。

Azure Disk storage class (starting from v1.7.2)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: managed
  • storageaccounttype:AzureストレージアカウントのSku層。デフォルトは空です。
  • kind:可能な値は、shareddedicated、およびmanaged(デフォルト)です。kindsharedの場合、すべてのアンマネージドディスクは、クラスターと同じリソースグループ内のいくつかの共有ストレージアカウントに作成されます。kinddedicatedの場合、新しい専用ストレージアカウントが、クラスターと同じリソースグループ内の新しいアンマネージドディスク用に作成されます。kindmanagedの場合、すべてのマネージドディスクはクラスターと同じリソースグループに作成されます。
  • resourceGroup:Azureディスクが作成されるリソースグループを指定します。これは、既存のリソースグループ名である必要があります。指定しない場合、ディスクは現在のKubernetesクラスターと同じリソースグループに配置されます。
  • Premium VMはStandard_LRSディスクとPremium_LRSディスクの両方を接続できますが、Standard VMはStandard_LRSディスクのみを接続できます。
  • マネージドVMはマネージドディスクのみをアタッチでき、アンマネージドVMはアンマネージドディスクのみをアタッチできます。

Azure File

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azurefile
provisioner: kubernetes.io/azure-file
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name
  • skuName:AzureストレージアカウントのSku層。デフォルトは空です。
  • location:Azureストレージアカウントの場所。デフォルトは空です。
  • storageAccount:Azureストレージアカウント名。デフォルトは空です。ストレージアカウントが指定されていない場合は、リソースグループに関連付けられているすべてのストレージアカウントが検索され、skuNamelocationに一致するものが見つかります。ストレージアカウントを指定する場合は、クラスターと同じリソースグループに存在する必要があり、skuNamelocationは無視されます。
  • secretNamespace:Azureストレージアカウント名とキーを含むシークレットの名前空間。デフォルトはPodと同じです。
  • secretName:Azureストレージアカウント名とキーを含むシークレットの名前。デフォルトはazure-storage-account-<accountName>-secretです
  • readOnly:ストレージが読み取り専用としてマウントされるかどうかを示すフラグ。デフォルトはfalseで、読み取り/書き込みマウントを意味します。この設定は、VolumeMountsのReadOnly設定にも影響します。

ストレージのプロビジョニング中に、secretNameという名前のシークレットがマウント資格証明用に作成されます。クラスターでRBACController Rolesの両方が有効になっている場合は、追加します。clusterrolesystem:controller:persistent-volume-binderに対するリソースsecretcreateパーミッション。

マルチテナンシーコンテキストでは、secretNamespaceの値を明示的に設定することを強くお勧めします。そうしないと、ストレージアカウントの資格情報が他のユーザーに読み取られる可能性があります。

Portworx Volume

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
  repl: "1"
  snap_interval:   "70"
  priority_io:  "high"
  • fs:配置するファイルシステム:none/xfs/ext4(デフォルト:ext4)。
  • block_size:キロバイト単位のブロックサイズ(デフォルト:32)。
  • repl:レプリケーション係数1..3の形式で提供される同期レプリカの数(デフォルト:1)。ここでは文字列が期待されます。つまり、1ではなく"1"です。
  • priority_io:ボリュームがパフォーマンスの高いストレージから作成されるか、優先度の低いストレージhigh/medium/low(デフォルト:low)から作成されるかを決定します。
  • snap_interval:スナップショットをトリガーするクロック/時間間隔(分単位)。スナップショットは、前のスナップショットとの差分に基づいて増分されます。0はスナップを無効にします(デフォルト:0)。ここでは文字列が必要です。つまり、70ではなく"70"です。
  • aggregation_level:ボリュームが分散されるチャンクの数を指定します。0は非集約ボリュームを示します(デフォルト:0)。ここには文字列が必要です。つまり、0ではなく"0"です。
  • ephemeral:アンマウント後にボリュームをクリーンアップするか、永続化するかを指定します。emptyDirユースケースではこの値をtrueに設定でき、Cassandraなどのデータベースのようなpersistent volumesユースケースではfalse、true/false(デフォルトはfalse)に設定する必要があります。ここでは文字列が必要です。つまり、trueではなく"true"です。

Local

FEATURE STATE: Kubernetes v1.14 [stable]
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

ローカルボリュームは現在、動的プロビジョニングをサポートしていませんが、Podのスケジューリングまでボリュームバインドを遅らせるには、引き続きStorageClassを作成する必要があります。これは、WaitForFirstConsumerボリュームバインディングモードによって指定されます。

ボリュームバインディングを遅延させると、PersistentVolumeClaimに適切なPersistentVolumeを選択するときに、スケジューラはPodのスケジューリング制約をすべて考慮することができます。

3.6.6 - ボリュームの動的プロビジョニング(Dynamic Volume Provisioning)

ボリュームの動的プロビジョニングにより、ストレージ用のボリュームをオンデマンドに作成することができます。 動的プロビジョニングなしでは、クラスター管理者はクラウドプロバイダーまたはストレージプロバイダーに対して新規のストレージ用のボリュームとPersistentVolumeオブジェクトを作成するように手動で指示しなければなりません。動的プロビジョニングの機能によって、クラスター管理者がストレージを事前にプロビジョンする必要がなくなります。その代わりに、ユーザーによってリクエストされたときに自動でストレージをプロビジョンします。

バックグラウンド

ボリュームの動的プロビジョニングの実装はstorage.k8s.ioというAPIグループ内のStorageClassというAPIオブジェクトに基づいています。クラスター管理者はStorageClassオブジェクトを必要に応じていくつでも定義でき、各オブジェクトはボリュームをプロビジョンするVolumeプラグイン (別名プロビジョナー)と、プロビジョンされるときにプロビジョナーに渡されるパラメーターを指定します。 クラスター管理者はクラスター内で複数の種類のストレージ(同一または異なるストレージシステム)を定義し、さらには公開でき、それらのストレージはパラメーターのカスタムセットを持ちます。この仕組みにおいて、エンドユーザーはストレージがどのようにプロビジョンされるか心配する必要がなく、それでいて複数のストレージオプションから選択できることを保証します。

StorageClassに関するさらなる情報はStorage Classを参照ください。

動的プロビジョニングを有効にする

動的プロビジョニングを有効にするために、クラスター管理者はユーザーのために1つまたはそれ以上のStorageClassを事前に作成する必要があります。StorageClassオブジェクトは、動的プロビジョニングが実行されるときに、どのプロビジョナーが使用されるべきか、またどのようなパラメーターをプロビジョナーに渡すべきか定義します。StorageClassオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。

下記のマニフェストでは標準的な永続化ディスクをプロビジョンする"slow"というStorageClassを作成します。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard

下記のマニフェストではSSDを使った永続化ディスクをプロビジョンする"fast"というStorageClassを作成します。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

動的プロビジョニングの使用

ユーザーはPersistentVolumeClaimリソース内でStorageClassを含むことで、動的にプロビジョンされたStorageをリクエストできます。Kubernetes v1.6以前では、この機能はvolume.beta.kubernetes.io/storage-classアノテーションを介して使うことができました。しかしこのアノテーションはv1.9から非推奨になりました。その代わりユーザーは現在ではPersistentVolumeClaimオブジェクトのstorageClassNameを使う必要があります。このフィールドの値は、管理者によって設定されたStorageClassの名前と一致しなければなりません(下記のセクションも参照ください)。

"fast"というStorageClassを選択するために、例としてユーザーは下記のPersistentVolumeClaimを作成します。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast
  resources:
    requests:
      storage: 30Gi

このリソースによってSSDのような永続化ディスクが自動的にプロビジョンされます。このリソースが削除された時、そのボリュームは削除されます。

デフォルトの挙動

動的プロビジョニングは、もしStorageClassが1つも指定されていないときに全てのPersistentVolumeClaimが動的にプロビジョンされるようにクラスター上で有効にできます。クラスター管理者は、下記を行うことによりこのふるまいを有効にできます。

管理者はStorageClassに対してstorageclass.kubernetes.io/is-default-classアノテーションをつけることで、デフォルトのStorageClassとしてマーキングできます。 デフォルトのStorageClassがクラスター内で存在し、かつユーザーがPersistentVolumeClaimリソースでstorageClassNameを指定しなかった場合、DefaultStorageClassという管理コントローラーはstorageClassNameフィールドの値をデフォルトのStorageClassを指し示すように自動で追加します。

注意点として、クラスター上では最大1つしかデフォルト のStorageClassが指定できず、storageClassNameを明示的に指定しないPersistentVolumeClaimは作成することもできません。

トポロジーに関する注意

マルチゾーンクラスター内では、Podは単一のリージョン内のゾーンをまたいでしか稼働できません。シングルゾーンのStorageバックエンドはPodがスケジュールされるゾーン内でプロビジョンされる必要があります。これはVolume割り当てモードを設定することにより可能となります。

3.6.7 - ボリュームのスナップショット

Kubernetesでは、VolumeSnapshotはストレージシステム上のボリュームのスナップショットを表します。このドキュメントは、Kubernetes永続ボリュームに既に精通していることを前提としています。

概要

APIリソースPersistentVolumePersistentVolumeClaimを使用してユーザーと管理者にボリュームをプロビジョニングする方法と同様に、VolumeSnapshotContentVolumeSnapshotAPIリソースは、ユーザーと管理者のボリュームスナップショットを作成するために提供されます。

VolumeSnapshotContentは、管理者によってプロビジョニングされたクラスター内のボリュームから取得されたスナップショットです。PersistentVolumeがクラスターリソースであるように、これはクラスターのリソースです。

VolumeSnapshotは、ユーザーによるボリュームのスナップショットの要求です。PersistentVolumeClaimに似ています。

VolumeSnapshotClassを使用すると、VolumeSnapshotに属するさまざまな属性を指定できます。これらの属性は、ストレージシステム上の同じボリュームから取得されたスナップショット間で異なる場合があるため、PersistentVolumeClaimの同じStorageClassを使用して表現することはできません。

ボリュームスナップショットは、完全に新しいボリュームを作成することなく、特定の時点でボリュームの内容をコピーするための標準化された方法をKubernetesユーザーに提供します。この機能により、たとえばデータベース管理者は、編集または削除の変更を実行する前にデータベースをバックアップできます。

この機能を使用する場合、ユーザーは次のことに注意する必要があります。

  • APIオブジェクトVolumeSnapshotVolumeSnapshotContent、およびVolumeSnapshotClassCRDであり、コアAPIの一部ではありません。
  • VolumeSnapshotのサポートは、CSIドライバーでのみ利用できます。
  • VolumeSnapshotの展開プロセスの一環として、Kubernetesチームは、コントロールプレーンに展開されるスナップショットコントローラーと、CSIドライバーと共に展開されるcsi-snapshotterと呼ばれるサイドカーヘルパーコンテナを提供します。スナップショットコントローラーは、VolumeSnapshotおよびVolumeSnapshotContentオブジェクトを管理し、VolumeSnapshotContentオブジェクトの作成と削除を担当します。サイドカーcsi-snapshotterは、VolumeSnapshotContentオブジェクトを監視し、CSIエンドポイントに対してCreateSnapshotおよびDeleteSnapshot操作をトリガーします。
  • スナップショットオブジェクトの厳密な検証を提供するvalidation Webhookサーバーもあります。これは、CSIドライバーではなく、スナップショットコントローラーおよびCRDと共にKubernetesディストリビューションによってインストールする必要があります。スナップショット機能が有効になっているすべてのKubernetesクラスターにインストールする必要があります。
  • CSIドライバーは、ボリュームスナップショット機能を実装している場合と実装していない場合があります。ボリュームスナップショットのサポートを提供するCSIドライバーは、csi-snapshotterを使用する可能性があります。詳細については、CSIドライバーのドキュメントを参照してください。
  • CRDとスナップショットコントローラーのインストールは、Kubernetesディストリビューションの責任です。

ボリュームスナップショットとボリュームスナップショットのコンテンツのライフサイクル

VolumeSnapshotContentsはクラスター内のリソースです。VolumeSnapshotsは、これらのリソースに対するリクエストです。VolumeSnapshotContentsVolumeSnapshotsの間の相互作用は、次のライフサイクルに従います。

プロビジョニングボリュームのスナップショット

スナップショットをプロビジョニングするには、事前プロビジョニングと動的プロビジョニングの2つの方法があります。

事前プロビジョニング

クラスター管理者は、多数のVolumeSnapshotContentsを作成します。それらは、クラスターユーザーが使用できるストレージシステム上の実際のボリュームスナップショットの詳細を保持します。それらはKubernetesAPIに存在し、消費することができます。

動的プロビジョニング

既存のスナップショットを使用する代わりに、スナップショットをPersistentVolumeClaimから動的に取得するように要求できます。VolumeSnapshotClassは、スナップショットを作成するときに使用するストレージプロバイダー固有のパラメーターを指定します。

バインディング

スナップショットコントローラーは、事前プロビジョニングされたシナリオと動的にプロビジョニングされたシナリオの両方で、適切なVolumeSnapshotContentオブジェクトを使用したVolumeSnapshotオブジェクトのバインディングを処理します。バインディングは1対1のマッピングです。

事前プロビジョニングされたバインディングの場合、要求されたVolumeSnapshotContentオブジェクトが作成されるまで、VolumeSnapshotはバインドされないままになります。

スナップショットソース保護としてのPersistentVolumeClaim

この保護の目的は、スナップショットがシステムから取得されている間、使用中のPersistentVolumeClaimAPIオブジェクトがシステムから削除されないようにすることです(これにより、データが失われる可能性があります)。

PersistentVolumeClaimのスナップショットが作成されている間、そのPersistentVolumeClaimは使用中です。スナップショットソースとしてアクティブに使用されているPersistentVolumeClaim APIオブジェクトを削除しても、PersistentVolumeClaimオブジェクトはすぐには削除されません。代わりに、PersistentVolumeClaimオブジェクトの削除は、スナップショットがReadyToUseになるか中止されるまで延期されます。

削除

削除はVolumeSnapshotオブジェクトの削除によってトリガーされ、DeletionPolicyに従います。DeletionPolicyDeleteの場合、基になるストレージスナップショットはVolumeSnapshotContentオブジェクトとともに削除されます。DeletionPolicyRetainの場合、基になるスナップショットとVolumeSnapshotContentの両方が残ります。

ボリュームスナップショット

各VolumeSnapshotには、仕様とステータスが含まれています。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: new-snapshot-test
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass
  source:
    persistentVolumeClaimName: pvc-test

persistentVolumeClaimNameは、スナップショットのPersistentVolumeClaimデータソースの名前です。このフィールドは、スナップショットを動的にプロビジョニングするために必要です。

ボリュームスナップショットは、属性volumeSnapshotClassNameを使用してVolumeSnapshotClassの名前を指定することにより、特定のクラスを要求できます。何も設定されていない場合、利用可能な場合はデフォルトのクラスが使用されます。

事前プロビジョニングされたスナップショットの場合、次の例に示すように、スナップショットのソースとしてvolumeSnapshotContentNameを指定する必要があります。事前プロビジョニングされたスナップショットには、volumeSnapshotContentNameソースフィールドが必要です。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: test-snapshot
spec:
  source:
    volumeSnapshotContentName: test-content

ボリュームスナップショットコンテンツ

各VolumeSnapshotContentには、仕様とステータスが含まれています。動的プロビジョニングでは、スナップショット共通コントローラーがVolumeSnapshotContentオブジェクトを作成します。以下に例を示します。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: snapcontent-72d9a349-aacd-42d2-a240-d775650d2455
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    volumeHandle: ee0cfb94-f8d4-11e9-b2d8-0242ac110002
  sourceVolumeMode: Filesystem
  volumeSnapshotClassName: csi-hostpath-snapclass
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default
    uid: 72d9a349-aacd-42d2-a240-d775650d2455

volumeHandleは、ストレージバックエンドで作成され、ボリュームの作成中にCSIドライバーによって返されるボリュームの一意の識別子です。このフィールドは、スナップショットを動的にプロビジョニングするために必要です。これは、スナップショットのボリュームソースを指定します。 事前プロビジョニングされたスナップショットの場合、(クラスター管理者として)次のようにVolumeSnapshotContentオブジェクトを作成する必要があります。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: new-snapshot-content-test
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
  sourceVolumeMode: Filesystem
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default

snapshotHandleは、ストレージバックエンドで作成されたボリュームスナップショットの一意の識別子です。このフィールドは、事前プロビジョニングされたスナップショットに必要です。このVolumeSnapshotContentが表すストレージシステムのCSIスナップショットIDを指定します。

sourceVolumeModeは、スナップショットが作成されるボリュームのモードです。sourceVolumeModeフィールドの値は、FilesystemまたはBlockのいずれかです。ソースボリュームモードが指定されていない場合、Kubernetesはスナップショットをソースボリュームのモードが不明であるかのように扱います。

volumeSnapshotRefは、対応するVolumeSnapshotの参照です。VolumeSnapshotContentが事前プロビジョニングされたスナップショットとして作成されている場合、volumeSnapshotRefで参照されるVolumeSnapshotがまだ存在しない可能性があることに注意してください。

スナップショットのボリュームモードの変換

クラスターにインストールされているVolumeSnapshotsAPIがsourceVolumeModeフィールドをサポートしている場合、APIには、権限のないユーザーがボリュームのモードを変換するのを防ぐ機能があります。

クラスターにこの機能の機能があるかどうかを確認するには、次のコマンドを実行します。

$ kubectl get crd volumesnapshotcontent -o yaml

ユーザーが既存のVolumeSnapshotからPersistentVolumeClaimを作成できるようにしたいが、ソースとは異なるボリュームモードを使用する場合は、VolumeSnapshotに対応するVolumeSnapshotContentにアノテーションsnapshot.storage.kubernetes.io/allow-volume-mode-change: "true"を追加する必要があります。

事前プロビジョニングされたスナップショットの場合、クラスター管理者がspec.sourceVolumeModeを入力する必要があります。

この機能を有効にしたVolumeSnapshotContentリソースの例は次のようになります。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: new-snapshot-content-test
  annotations:
    - snapshot.storage.kubernetes.io/allow-volume-mode-change: "true"
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
  sourceVolumeMode: Filesystem
  volumeSnapshotRef:
    name: new-snapshot-test
    namespace: default

スナップショットからのボリュームのプロビジョニング

PersistentVolumeClaimオブジェクトのdataSourceフィールドを使用して、スナップショットからのデータが事前に取り込まれた新しいボリュームをプロビジョニングできます。

詳細については、ボリュームのスナップショットとスナップショットからのボリュームの復元を参照してください。

3.6.8 - VolumeSnapshotClass

このドキュメントでは、KubernetesにおけるVolumeSnapshotClassのコンセプトについて説明します。
関連する項目として、Volumeのスナップショットストレージクラスも参照してください。

イントロダクション

StorageClassはVolumeをプロビジョンするときに、ストレージの"クラス"に関する情報を記述する方法を提供します。それと同様に、VolumeSnapshotClassではVolumeSnapshotをプロビジョンするときに、ストレージの"クラス"に関する情報を記述する方法を提供します。

VolumeSnapshotClass リソース

VolumeSnapshotClassdriverdeletionPolicyparametersフィールドを含み、それらは、そのクラスに属するVolumeSnapshotが動的にプロビジョンされるときに使われます。

VolumeSnapshotClassオブジェクトの名前は重要であり、それはユーザーがどのように特定のクラスをリクエストできるかを示したものです。管理者は初めてVolumeSnapshotClassオブジェクトを作成するときに、その名前と他のパラメーターをセットし、そのオブジェクトは一度作成されるとそのあと更新することができません。

管理者は、バインド対象のクラスを1つもリクエストしないようなVolumeSnapshotのために、デフォルトのVolumeSnapshotClassを指定することができます。

apiVersion: snapshot.storage.k8s.io/v1beta1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

Driver

VolumeSnapshotClassは、VolumeSnapshotをプロビジョンするときに何のCSIボリュームプラグインを使うか決定するためのdriverフィールドを持っています。このフィールドは必須となります。

DeletionPolicy

VolumeSnapshotClassにはdeletionPolicyがあります。これにより、バインドされている VolumeSnapshotオブジェクトが削除されるときに、VolumeSnapshotContentがどうなるかを設定することができます。VolumeSnapshotのdeletionPolicyは、RetainまたはDeleteのいずれかです。このフィールドは指定しなければなりません。

deletionPolicyがDeleteの場合、元となるストレージスナップショットは VolumeSnapshotContentオブジェクトとともに削除されます。deletionPolicyがRetainの場合、元となるスナップショットとVolumeSnapshotContentの両方が残ります。

Parameters

VolumeSnapshotClassは、そのクラスに属するVolumeSnapshotを指定するパラメーターを持っています。 driverに応じて様々なパラメーターを使用できます。

3.6.9 - CSI Volume Cloning

このドキュメントではKubernetesで既存のCSIボリュームの複製についてのコンセプトを説明します。このページを読む前にあらかじめボリュームについてよく理解していることが望ましいです。

イントロダクション

CSIのボリューム複製機能は、ユーザーがボリュームの複製を作成することを示すdataSourceフィールドで既存のPVCを指定するためのサポートを追加します。

複製は既存のKubernetesボリュームの複製として定義され、標準のボリュームと同じように使用できます。唯一の違いは、プロビジョニング時に「新しい」空のボリュームを作成するのではなく、バックエンドデバイスが指定されたボリュームの正確な複製を作成することです。

複製の実装は、Kubernetes APIの観点からは新しいPVCの作成時に既存のPVCをdataSourceとして指定する機能を追加するだけです。ソースPVCはバインドされており、使用可能でなければなりません(使用中ではありません)。

この機能を使用する場合、ユーザーは次のことに注意する必要があります:

  • 複製のサポート(VolumePVCDataSource)はCSIドライバーのみです。
  • 複製のサポートは動的プロビジョニングのみです。
  • CSIドライバーはボリューム複製機能を実装している場合としていない場合があります。
  • PVCは複製先のPVCと同じ名前空間に存在する場合にのみ複製できます(複製元と複製先は同じ名前空間になければなりません)。
  • 複製は同じストレージクラス内でのみサポートされます。
    • 宛先ボリュームは、ソースと同じストレージクラスである必要があります。
    • デフォルトのストレージクラスを使用でき、仕様ではstorageClassNameを省略できます。
  • 複製は同じVolumeMode設定を使用する2つのボリューム間でのみ実行できます(ブロックモードのボリュームを要求する場合、ソースもブロックモードである必要があります)。

プロビジョニング

複製は同じ名前空間内の既存のPVCを参照するdataSourceを追加すること以外は他のPVCと同様にプロビジョニングされます。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: clone-of-pvc-1
    namespace: myns
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: cloning
  resources:
    requests:
      storage: 5Gi
  dataSource:
    kind: PersistentVolumeClaim
    name: pvc-1

このyamlの作成結果は指定された複製元であるpvc-1と全く同じデータを持つclone-of-pvc-1という名前の新しいPVCです。

使い方

新しいPVCが使用可能になると、複製されたPVCは他のPVCと同じように利用されます。またこの時点で新しく作成されたPVCは独立したオブジェクトであることが期待されます。元のdataSource PVCを考慮せず個別に利用、複製、スナップショット、削除できます。これはまた複製元が新しく作成された複製にリンクされておらず、新しく作成された複製に影響を与えずに変更または削除できることを意味します。

3.6.10 - ストレージ容量

ストレージ容量は、Podが実行されるノードごとに制限があったり、大きさが異なる可能性があります。たとえば、NASがすべてのノードからはアクセスできなかったり、初めからストレージがノードローカルでしか利用できない可能性があります。

FEATURE STATE: Kubernetes v1.19 [alpha]

このページでは、Kubernetesがストレージ容量を追跡し続ける方法と、スケジューラーがその情報を利用して、残りの未作成のボリュームのために十分なストレージ容量へアクセスできるノード上にどのようにPodをスケジューリングするかについて説明します。もしストレージ容量の追跡がなければ、スケジューラーは、ボリュームをプロビジョニングするために十分な容量のないノードを選択してしまい、スケジューリングの再試行が複数回行われてしまう恐れがあります。

ストレージ容量の追跡は、Container Storage Interface(CSI)向けにサポートされており、CSIドライバーのインストール時に有効にする必要があります

API

この機能には、以下の2つのAPI拡張があります。

  • CSIStorageCapacityオブジェクト: このオブジェクトは、CSIドライバーがインストールされた名前空間に生成されます。各オブジェクトには1つのストレージクラスに対する容量の情報が含まれ、そのストレージに対してどのノードがアクセス権を持つかが定められています。

  • CSIDriverSpec.StorageCapacityフィールド: trueに設定すると、Kubernetesのスケジューラーが、CSIドライバーを使用するボリュームに対してストレージ容量を考慮するようになります。

スケジューリング

ストレージ容量の情報がKubernetesのスケジューラーで利用されるのは、以下のすべての条件を満たす場合です。

  • CSIStorageCapacityフィーチャーゲートがtrueである
  • Podがまだ作成されていないボリュームを使用する時
  • そのボリュームが、CSIドライバーを参照し、volume binding modeWaitForFirstConsumerを使うStorageClassを使用している
  • ドライバーに対するCSIDriverオブジェクトのStorageCapacityがtrueに設定されている

その場合、スケジューラーはPodに対して、十分なストレージ容量が利用できるノードだけを考慮するようになります。このチェックは非常に単純で、ボリュームのサイズと、CSIStorageCapacityオブジェクトに一覧された容量を、ノードを含むトポロジーで比較するだけです。

volume binding modeがImmediateのボリュームの場合、ストレージドライバーはボリュームを使用するPodとは関係なく、ボリュームを作成する場所を決定します。次に、スケジューラーはボリュームが作成された後、Podをボリュームが利用できるノードにスケジューリングします。

CSI ephemeral volumesの場合、スケジューリングは常にストレージ容量を考慮せずに行われます。このような動作になっているのは、このボリュームタイプはノードローカルな特別なCSIドライバーでのみ使用され、そこでは特に大きなリソースが必要になることはない、という想定に基づいています。

再スケジューリング

WaitForFirstConsumerボリュームがあるPodに対してノードが選択された場合は、その決定はまだ一時的なものです。次のステップで、CSIストレージドライバーに対して、選択されたノード上でボリュームが利用可能になることが予定されているというヒントを使用してボリュームの作成を要求します。

Kubernetesは古い容量の情報をもとにノードを選択する場合があるため、実際にはボリュームが作成できないという可能性が存在します。その場合、ノードの選択がリセットされ、KubernetesスケジューラーはPodに割り当てるノードを再び探します。

制限

ストレージ容量を追跡することで、1回目の試行でスケジューリングが成功する可能性が高くなります。しかし、スケジューラーは潜在的に古い情報に基づいて決定を行う可能性があるため、成功を保証することはできません。通常、ストレージ容量の情報が存在しないスケジューリングと同様のリトライの仕組みによって、スケジューリングの失敗に対処します。

スケジューリングが永続的に失敗する状況の1つは、Podが複数のボリュームを使用する場合で、あるトポロジーのセグメントで1つのボリュームがすでに作成された後、もう1つのボリュームのために十分な容量が残っていないような場合です。この状況から回復するには、たとえば、容量を増加させたり、すでに作成されたボリュームを削除するなどの手動での対応が必要です。この問題に自動的に対処するためには、まだ追加の作業が必要となっています。

ストレージ容量の追跡を有効にする

ストレージ容量の追跡はアルファ機能であり、CSIStorageCapacityフィーチャーゲートstorage.k8s.io/v1alpha1 API groupを有効にした場合にのみ、有効化されます。詳細については、--feature-gatesおよび--runtime-config kube-apiserverパラメーターを参照してください。

Kubernetesクラスターがこの機能をサポートしているか簡単に確認するには、以下のコマンドを実行して、CSIStorageCapacityオブジェクトを一覧表示します。

kubectl get csistoragecapacities --all-namespaces

クラスターがCSIStorageCapacityをサポートしていれば、CSIStorageCapacityのリストが表示されるか、次のメッセージが表示されます。

No resources found

もしサポートされていなければ、代わりに次のエラーが表示されます。

error: the server doesn't have a resource type "csistoragecapacities"

クラスター内で機能を有効化することに加えて、CSIドライバーもこの機能をサポートしている必要があります。詳細については、各ドライバーのドキュメントを参照してください。

次の項目

3.6.11 - ノード固有のボリューム制限

このページでは、さまざまなクラウドプロバイダーのノードに接続できるボリュームの最大数について説明します。

通常、Google、Amazon、Microsoftなどのクラウドプロバイダーには、ノードに接続できるボリュームの数に制限があります。Kubernetesがこれらの制限を尊重することが重要です。 そうしないと、ノードでスケジュールされたPodが、ボリュームが接続されるのを待ってスタックする可能性があります。

Kubernetesのデフォルトの制限

Kubernetesスケジューラーには、ノードに接続できるボリュームの数にデフォルトの制限があります。

クラウドサービスノード当たりの最大ボリューム
Amazon Elastic Block Store (EBS)39
Google Persistent Disk16
Microsoft Azure Disk Storage16

カスタム制限

これらの制限を変更するには、KUBE_MAX_PD_VOLS環境変数の値を設定し、スケジューラーを開始します。CSIドライバーの手順は異なる場合があります。制限をカスタマイズする方法については、CSIドライバーのドキュメントを参照してください。

デフォルトの制限よりも高い制限を設定する場合は注意してください。クラウドプロバイダーのドキュメントを参照して、設定した制限をノードが実際にサポートしていることを確認してください。

制限はクラスター全体に適用されるため、すべてのノードに影響します。

動的ボリューム制限

FEATURE STATE: Kubernetes v1.17 [stable]

動的ボリューム制限は、次のボリュームタイプでサポートされています。

  • Amazon EBS
  • Google Persistent Disk
  • Azure Disk
  • CSI

ツリー内のボリュームプラグインによって管理されるボリュームの場合、Kubernetesはノードタイプを自動的に決定し、ノードに適切なボリュームの最大数を適用します。例えば:

  • Google Compute Engine上ではノードタイプに応じて、最大127個のボリュームをノードに接続できます。

  • M5、C5、R5、T3、およびZ1DインスタンスタイプのAmazon EBSディスクの場合、Kubernetesは25ボリュームのみをノードにアタッチできます。Amazon Elastic Compute Cloud (EC2)の他のインスタンスタイプの場合、Kubernetesでは39個のボリュームをノードに接続できます。

  • Azureでは、ノードの種類に応じて、最大64個のディスクをノードに接続できます。詳細については、Azureの仮想マシンのサイズを参照してください。

  • CSIストレージドライバーが(NodeGetInfoを使用して)ノードの最大ボリューム数をアドバタイズする場合、kube-schedulerはその制限を尊重します。詳細については、CSIの仕様を参照してください。

  • CSIドライバーに移行されたツリー内プラグインによって管理されるボリュームの場合、ボリュームの最大数はCSIドライバーによって報告される数になります。

3.6.12 - ボリュームヘルスモニタリング

FEATURE STATE: Kubernetes v1.21 [alpha]

CSIボリュームヘルスモニタリングにより、CSIドライバーは、基盤となるストレージシステムから異常なボリューム状態を検出し、それらをPVCまたはPodのイベントとして報告できます。

ボリュームヘルスモニタリング

Kubernetes volume health monitoring は、KubernetesがContainerStorageInterface(CSI)を実装する方法の一部です。ボリュームヘルスモニタリング機能は、外部のヘルスモニターコントローラーとkubeletの2つのコンポーネントで実装されます。

CSIドライバーがコントローラー側からのボリュームヘルスモニタリング機能をサポートしている場合、CSIボリュームで異常なボリューム状態が検出されると、関連するPersistentVolumeClaim(PVC)でイベントが報告されます。

外部ヘルスモニターコントローラーも、ノード障害イベントを監視します。enable-node-watcherフラグをtrueに設定することで、ノード障害の監視を有効にできます。外部ヘルスモニターがノード障害イベントを検出すると、コントローラーは、このPVCを使用するポッドが障害ノード上にあることを示すために、PVCでイベントが報告されることを報告します。

CSIドライバーがノード側からのボリュームヘルスモニタリング機能をサポートしている場合、CSIボリュームで異常なボリューム状態が検出されると、PVCを使用するすべてのPodでイベントが報告されます。さらに、ボリュームヘルス情報はKubelet VolumeStatsメトリクスとして公開されます。新しいメトリックkubelet_volume_stats_health_status_abnormalが追加されました。このメトリックにはnamespacepersistentvolumeclaimの2つのラベルが含まれます。カウントは1または0です。1はボリュームが異常であることを示し、0はボリュームが正常であることを示します。詳細については、KEPを確認してください。

次の項目

CSIドライバーのドキュメントを参照して、この機能を実装しているCSIドライバーを確認してください。

3.7 - 設定

3.7.1 - 設定のベストプラクティス

このドキュメントでは、ユーザーガイド、入門マニュアル、および例を通して紹介されている設定のベストプラクティスを中心に説明します。

このドキュメントは生ものです。このリストには載っていないが他の人に役立つかもしれない何かについて考えている場合、IssueまたはPRを遠慮なく作成してください。

一般的な設定のTips

  • 構成を定義する際には、最新の安定したAPIバージョンを指定してください。

  • 設定ファイルは、クラスターに反映される前にバージョン管理システムに保存されるべきです。これによって、必要に応じて設定変更を迅速にロールバックできます。また、クラスターの再作成や復元時にも役立ちます。

  • JSONではなくYAMLを使って設定ファイルを書いてください。これらのフォーマットはほとんどすべてのシナリオで互換的に使用できますが、YAMLはよりユーザーフレンドリーになる傾向があります。

  • 意味がある場合は常に、関連オブジェクトを単一ファイルにグループ化します。多くの場合、1つのファイルの方が管理が簡単です。例としてguestbook-all-in-one.yamlファイルを参照してください。

  • 多くのkubectlコマンドがディレクトリに対しても呼び出せることも覚えておきましょう。たとえば、設定ファイルのディレクトリで kubectl applyを呼び出すことができます。

  • 不必要にデフォルト値を指定しないでください。シンプルかつ最小限の設定のほうがエラーが発生しにくくなります。

  • よりよいイントロスペクションのために、オブジェクトの説明をアノテーションに入れましょう。

"真っ裸"のPod に対する ReplicaSet、Deployment、およびJob

  • 可能な限り、"真っ裸"のPod(ReplicaSetDeploymentにバインドされていないPod)は使わないでください。Nodeに障害が発生した場合、これらのPodは再スケジュールされません。

    明示的にrestartPolicy: Neverを使いたいシーンを除いて、DeploymentはPodを直接作成するよりもほとんど常に望ましい方法です。Deploymentには、希望する数のPodが常に使用可能であることを確認するためにReplicaSetを作成したり、Podを置き換えるための戦略(RollingUpdateなど)を指定したりできます。Jobのほうが適切な場合もあるかもしれません。

Service

  • 対応するバックエンドワークロード(DeploymentまたはReplicaSet)の前、およびそれにアクセスする必要があるワークロードの前にServiceを作成します。Kubernetesがコンテナを起動すると、コンテナ起動時に実行されていたすべてのServiceを指す環境変数が提供されます。たとえば、fooという名前のServiceが存在する場合、すべてのコンテナは初期環境で次の変数を取得します。

    FOO_SERVICE_HOST=<the host the Service is running on>
    FOO_SERVICE_PORT=<the port the Service is running on>
    

    これは順序付けの必要性を意味します - PodがアクセスしたいServicePod自身の前に作らなければならず、そうしないと環境変数は注入されません。DNSにはこの制限はありません。

  • (強くお勧めしますが)クラスターアドオンの1つの選択肢はDNSサーバーです。DNSサーバーは、新しいServiceについてKubernetes APIを監視し、それぞれに対して一連のDNSレコードを作成します。クラスター全体でDNSが有効になっている場合は、すべてのPodが自動的にServicesの名前解決を行えるはずです。

  • どうしても必要な場合以外は、PodにhostPortを指定しないでください。PodをhostPortにバインドすると、Podがスケジュールできる場所の数を制限します、それぞれの<hostIPhostPortprotocol>の組み合わせはユニークでなければならないからです。hostIPprotocolを明示的に指定しないと、KubernetesはデフォルトのhostIPとして0.0.0.0を、デフォルトの protocolとしてTCPを使います。

    デバッグ目的でのみポートにアクセスする必要がある場合は、apiserver proxyまたはkubectl port-forwardを使用できます。

    ノード上でPodのポートを明示的に公開する必要がある場合は、hostPortに頼る前にNodePortの使用を検討してください。

  • hostPortの理由と同じくして、hostNetworkの使用はできるだけ避けてください。

  • kube-proxyのロードバランシングが不要な場合は、headless ServiceClusterIPNone)を使用してServiceを簡単に検出できます。

ラベルの使用

  • { app: myapp, tier: frontend, phase: test, deployment: v3 }のように、アプリケーションまたはデプロイメントの セマンティック属性 を識別するラベルを定義して使いましょう。これらのラベルを使用して、他のリソースに適切なPodを選択できます。例えば、すべてのtier:frontendを持つPodを選択するServiceや、app:myappに属するすべてのphase:testコンポーネント、などです。このアプローチの例を知るには、ゲストブックアプリも合わせてご覧ください。

セレクターからリリース固有のラベルを省略することで、Serviceを複数のDeploymentにまたがるように作成できます。 Deploymentにより、ダウンタイムなしで実行中のサービスを簡単に更新できます。

オブジェクトの望ましい状態はDeploymentによって記述され、その仕様への変更が 適用 されると、Deploymentコントローラーは制御された速度で実際の状態を望ましい状態に変更します。

  • デバッグ用にラベルを操作できます。Kubernetesコントローラー(ReplicaSetなど)とServiceはセレクターラベルを使用してPodとマッチするため、Podから関連ラベルを削除すると、コントローラーによって考慮されたり、Serviceによってトラフィックを処理されたりすることがなくなります。既存のPodのラベルを削除すると、そのコントローラーはその代わりに新しいPodを作成します。これは、「隔離」環境で以前の「ライブ」Podをデバッグするのに便利な方法です。対話的にラベルを削除または追加するには、kubectl labelを使います。

コンテナイメージ

imagePullPolicyとイメージのタグは、kubeletが特定のイメージをpullしようとしたときに作用します。

  • imagePullPolicy: IfNotPresent: ローカルでイメージが見つからない場合にのみイメージをpullします。

  • imagePullPolicy: Always: kubeletがコンテナを起動する度に、kubeletはコンテナイメージレジストリに問い合わせて、イメージのダイジェストの名前解決を行います。もし、kubeletが同じダイジェストのコンテナイメージをローカルにキャッシュしていたら、kubeletはそのキャッシュされたイメージを利用します。そうでなければ、kubeletは解決されたダイジェストのイメージをダウンロードし、そのイメージを利用してコンテナを起動します。

  • imagePullPolicy のタグが省略されていて、利用してるイメージのタグが:latestの場合や省略されている場合、Alwaysが適用されます。

  • imagePullPolicy のタグが省略されていて、利用してるイメージのタグはあるが:latestでない場合、IfNotPresentが適用されます。

  • imagePullPolicy: Never: 常にローカルでイメージを探そうとします。ない場合にもイメージはpullしません。

kubectlの使い方

3.7.2 - ConfigMap

ConfigMapは、 機密性のないデータをキーと値のペアで保存するために使用されるAPIオブジェクトです。Podは、環境変数、コマンドライン引数、またはボリューム内の設定ファイルとしてConfigMapを使用できます。

ConfigMapを使用すると、環境固有の設定をコンテナイメージから分離できるため、アプリケーションを簡単に移植できるようになります。

動機

アプリケーションのコードとは別に設定データを設定するには、ConfigMapを使用します。

たとえば、アプリケーションを開発していて、(開発用時には)自分のコンピューター上と、(実際のトラフィックをハンドルするときは)クラウド上とで実行することを想像してみてください。あなたは、DATABASE_HOSTという名前の環境変数を使用するコードを書きます。ローカルでは、この変数をlocalhostに設定します。クラウド上では、データベースコンポーネントをクラスター内に公開するKubernetesのServiceを指すように設定します。

こうすることで、必要であればクラウド上で実行しているコンテナイメージを取得することで、ローカルでも完全に同じコードを使ってデバッグができるようになります。

ConfigMapは、大量のデータを保持するようには設計されていません。ConfigMapに保存されるデータは1MiBを超えることはできません。この制限を超える設定を保存する必要がある場合は、ボリュームのマウントを検討するか、別のデータベースまたはファイルサービスを使用することを検討してください。

ConfigMapオブジェクト

ConfigMapは、他のオブジェクトが使うための設定を保存できるAPIオブジェクトです。ほとんどのKubernetesオブジェクトにspecセクションがあるのとは違い、ConfigMapにはdataおよびbinaryDataフィールドがあります。これらのフィールドは、キーとバリューのペアを値として受け入れます。dataフィールドとbinaryDataフィールドはどちらもオプションです。dataフィールドはUTF-8バイトシーケンスを含むように設計されていますが、binaryDataフィールドはバイナリデータを含むように設計されています。

ConfigMapの名前は、有効なDNSのサブドメイン名でなければなりません。

dataまたはbinaryDataフィールドの各キーは、英数字、-_、または.で構成されている必要があります。dataに格納されているキーは、binaryDataフィールドのキーと重複することはできません。

v1.19以降、ConfigMapの定義にimmutableフィールドを追加して、イミュータブルなConfigMapを作成できます。

ConfigMapとPod

ConfigMapを参照して、ConfigMap内のデータを元にしてPod内のコンテナの設定をするPodのspecを書くことができます。このとき、PodとConfigMapは同じ名前空間内に存在する必要があります。

以下に、ConfigMapの例を示します。単一の値を持つキーと、Configuration形式のデータ片のような値を持つキーがあります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # プロパティーに似たキー。各キーは単純な値にマッピングされている
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # ファイルに似たキー
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true    

ConfigMapを利用してPod内のコンテナを設定する方法には、次の4種類があります。

  1. コンテナ内のコマンドと引数
  2. 環境変数をコンテナに渡す
  3. 読み取り専用のボリューム内にファイルを追加し、アプリケーションがそのファイルを読み取る
  4. Kubernetes APIを使用してConfigMapを読み込むコードを書き、そのコードをPod内で実行する

これらのさまざまな方法は、利用するデータをモデル化するのに役立ちます。最初の3つの方法では、kubeletがPodのコンテナを起動する時にConfigMapのデータを使用します。

4番目の方法では、ConfigMapとそのデータを読み込むためのコードを自分自身で書く必要があります。しかし、Kubernetes APIを直接使用するため、アプリケーションはConfigMapがいつ変更されても更新イベントを受信でき、変更が発生したときにすぐに反応できます。この手法では、Kubernetes APIに直接アクセスすることで、別の名前空間にあるConfigMapにもアクセスできます。

以下に、Podを設定するためにgame-demoから値を使用するPodの例を示します。

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # 環境変数を定義します。
        - name: PLAYER_INITIAL_LIVES # ここではConfigMap内のキーの名前とは違い
                                     # 大文字が使われていることに着目してください。
          valueFrom:
            configMapKeyRef:
              name: game-demo           # この値を取得するConfigMap。
              key: player_initial_lives # 取得するキー。
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
    # Podレベルでボリュームを設定し、Pod内のコンテナにマウントします。
    - name: config
      configMap:
        # マウントしたいConfigMapの名前を指定します。
        name: game-demo
        # ファイルとして作成するConfigMapのキーの配列
        items:
        - key: "game.properties"
          path: "game.properties"
        - key: "user-interface.properties"
          path: "user-interface.properties"

ConfigMapは1行のプロパティの値と複数行のファイルに似た形式の値を区別しません。問題となるのは、Podや他のオブジェクトによる値の使用方法です。

この例では、ボリュームを定義して、demoコンテナの内部で/configにマウントしています。これにより、ConfigMap内には4つのキーがあるにもかかわらず、2つのファイル/config/game.propertiesおよび/config/user-interface.propertiesだけが作成されます。

これは、Podの定義がvolumesセクションでitemsという配列を指定しているためです。もしitemsの配列を完全に省略すれば、ConfigMap内の各キーがキーと同じ名前のファイルになり、4つのファイルが作成されます。

ConfigMapを使う

ConfigMapは、データボリュームとしてマウントできます。ConfigMapは、Podへ直接公開せずにシステムの他の部品として使うこともできます。たとえば、ConfigMapには、システムの他の一部が設定のために使用するデータを保存できます。

ConfigMapの最も一般的な使い方では、同じ名前空間にあるPod内で実行されているコンテナに設定を構成します。ConfigMapを独立して使用することもできます。

たとえば、ConfigMapに基づいて動作を調整するアドオンオペレーターを見かけることがあるかもしれません。

ConfigMapをPodからファイルとして使う

ConfigMapをPod内のボリュームで使用するには、次のようにします。

  1. ConfigMapを作成するか、既存のConfigMapを使用します。複数のPodから同じConfigMapを参照することもできます。
  2. Podの定義を修正して、.spec.volumes[]以下にボリュームを追加します。ボリュームに任意の名前を付け、.spec.volumes[].configMap.nameフィールドにConfigMapオブジェクトへの参照を設定します。
  3. ConfigMapが必要な各コンテナに.spec.containers[].volumeMounts[]を追加します。.spec.containers[].volumeMounts[].readOnly = trueを指定して、.spec.containers[].volumeMounts[].mountPathには、ConfigMapのデータを表示したい未使用のディレクトリ名を指定します。
  4. イメージまたはコマンドラインを修正して、プログラムがそのディレクトリ内のファイルを読み込むように設定します。ConfigMapのdataマップ内の各キーが、mountPath以下のファイル名になります。

以下は、ボリューム内にConfigMapをマウントするPodの例です。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    configMap:
      name: myconfigmap

使用したいそれぞれのConfigMapごとに、.spec.volumes内で参照する必要があります。

Pod内に複数のコンテナが存在する場合、各コンテナにそれぞれ別のvolumeMountsのブロックが必要ですが、.spec.volumesはConfigMapごとに1つしか必要ありません。

マウントしたConfigMapの自動的な更新

ボリューム内で現在使用中のConfigMapが更新されると、射影されたキーも最終的に(eventually)更新されます。kubeletは定期的な同期のたびにマウントされたConfigMapが新しいかどうか確認します。しかし、kubeletが現在のConfigMapの値を取得するときにはローカルキャッシュを使用します。キャッシュの種類は、KubeletConfiguration構造体の中のConfigMapAndSecretChangeDetectionStrategyフィールドで設定可能です。ConfigMapは、監視(デフォルト)、ttlベース、またはすべてのリクエストを直接APIサーバーへ単純にリダイレクトする方法のいずれかによって伝搬されます。その結果、ConfigMapが更新された瞬間から、新しいキーがPodに射影されるまでの遅延の合計は、最長でkubeletの同期期間+キャッシュの伝搬遅延になります。ここで、キャッシュの伝搬遅延は選択したキャッシュの種類に依存します(監視の伝搬遅延、キャッシュのttl、または0に等しくなります)。

環境変数として使用されるConfigMapは自動的に更新されないため、ポッドを再起動する必要があります。

イミュータブルなConfigMap

FEATURE STATE: Kubernetes v1.19 [beta]

Kubernetesのベータ版の機能である イミュータブルなSecretおよびConfigMap は、個別のSecretやConfigMapをイミュータブルに設定するオプションを提供します。ConfigMapを広範に使用している(少なくとも数万のConfigMapがPodにマウントされている)クラスターでは、データの変更を防ぐことにより、以下のような利点が得られます。

  • アプリケーションの停止を引き起こす可能性のある予想外の(または望まない)変更を防ぐことができる
  • ConfigMapをイミュータブルにマークして監視を停止することにより、kube-apiserverへの負荷を大幅に削減し、クラスターの性能が向上する

この機能は、ImmutableEmphemeralVolumesフィーチャーゲートによって管理されます。immutableフィールドをtrueに設定することで、イミュータブルなConfigMapを作成できます。次に例を示します。

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

一度ConfigMapがイミュータブルに設定されると、この変更を元に戻したり、dataまたはbinaryDataフィールドのコンテンツを変更することはできません。ConfigMapの削除と再作成のみ可能です。既存のPodは削除されたConfigMapのマウントポイントを保持するため、こうしたPodは再作成することをおすすめします。

次の項目

3.7.3 - Secret

Secretとは、パスワードやトークン、キーなどの少量の機密データを含むオブジェクトのことです。 このような情報は、Secretを用いないとPodの定義やコンテナイメージに直接記載することになってしまうかもしれません。 Secretを使用すれば、アプリケーションコードに機密データを含める必要がなくなります。

なぜなら、Secretは、それを使用するPodとは独立して作成することができ、 Podの作成、閲覧、編集といったワークフローの中でSecret(およびそのデータ)が漏洩する危険性が低くなるためです。 また、Kubernetesやクラスター内で動作するアプリケーションは、不揮発性ストレージに機密データを書き込まないようにするなど、Secretで追加の予防措置を取ることができます。

Secretsは、ConfigMapsに似ていますが、機密データを保持するために用います。

Secretの概要

Secretを使うには、PodはSecretを参照することが必要です。 PodがSecretを使う方法は3種類あります。

KubernetesのコントロールプレーンでもSecretsは使われています。例えば、bootstrap token Secretsは、ノード登録を自動化するための仕組みです。

Secretオブジェクトの名称は正当なDNSサブドメイン名である必要があります。 シークレットの構成ファイルを作成するときに、dataおよび/またはstringDataフィールドを指定できます。dataフィールドとstringDataフィールドはオプションです。 dataフィールドのすべてのキーの値は、base64でエンコードされた文字列である必要があります。 base64文字列への変換が望ましくない場合は、代わりにstringDataフィールドを指定することを選択できます。これは任意の文字列を値として受け入れます。

datastringDataのキーは、英数字、-_、または.で構成されている必要があります。 stringDataフィールドのすべてのキーと値のペアは、内部でdataフィールドにマージされます。 キーがdataフィールドとstringDataフィールドの両方に表示される場合、stringDataフィールドで指定された値が優先されます。

Secretの種類

Secretを作成するときは、Secrettypeフィールド、または特定の同等のkubectlコマンドラインフラグ(使用可能な場合)を使用して、その型を指定できます。 Secret型は、Secret dataのプログラムによる処理を容易にするために使用されます。

Kubernetesは、いくつかの一般的な使用シナリオに対応するいくつかの組み込み型を提供します。 これらの型は、実行される検証とKubernetesが課す制約の点で異なります。

Builtin TypeUsage
Opaquearbitrary user-defined data
kubernetes.io/service-account-tokenservice account token
kubernetes.io/dockercfgserialized ~/.dockercfg file
kubernetes.io/dockerconfigjsonserialized ~/.docker/config.json file
kubernetes.io/basic-authcredentials for basic authentication
kubernetes.io/ssh-authcredentials for SSH authentication
kubernetes.io/tlsdata for a TLS client or server
bootstrap.kubernetes.io/tokenbootstrap token data

Secretオブジェクトのtype値として空でない文字列を割り当てることにより、独自のSecret型を定義して使用できます。空の文字列はOpaque型として扱われます。Kubernetesは型名に制約を課しません。ただし、組み込み型の1つを使用している場合は、その型に定義されているすべての要件を満たす必要があります。

Opaque secrets

Opaqueは、Secret構成ファイルから省略された場合のデフォルトのSecret型です。 kubectlを使用してSecretを作成する場合、genericサブコマンドを使用してOpaqueSecret型を示します。 たとえば、次のコマンドは、Opaque型の空のSecretを作成します。

kubectl create secret generic empty-secret
kubectl get secret empty-secret

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

NAME           TYPE     DATA   AGE
empty-secret   Opaque   0      2m6s

DATA列には、Secretに保存されているデータ項目の数が表示されます。 この場合、0は空のSecretを作成したことを意味します。

Service account token Secrets

kubernetes.io/service-account-token型のSecretは、サービスアカウントを識別するトークンを格納するために使用されます。 このSecret型を使用する場合は、kubernetes.io/service-account.nameアノテーションが既存のサービスアカウント名に設定されていることを確認する必要があります。Kubernetesコントローラーは、kubernetes.io/service-account.uidアノテーションや実際のトークンコンテンツに設定されたdataフィールドのtokenキーなど、他のいくつかのフィールドに入力します。

apiVersion: v1
kind: Secret
metadata:
  name: secret-sa-sample
  annotations:
    kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
  # You can include additional key value pairs as you do with Opaque Secrets
  extra: YmFyCg==

Podを作成すると、Kubernetesはservice account Secretを自動的に作成し、このSecretを使用するようにPodを自動的に変更します。service account token Secretには、APIにアクセスするための資格情報が含まれています。

API証明の自動作成と使用は、必要に応じて無効にするか、上書きすることができます。 ただし、API Serverに安全にアクセスするだけの場合は、これが推奨されるワークフローです。

ServiceAccountの動作の詳細については、ServiceAccountのドキュメントを参照してください。 PodからServiceAccountを参照する方法については、PodautomountServiceAccountTokenフィールドとserviceAccountNameフィールドを確認することもできます。

Docker config Secrets

次のtype値のいずれかを使用して、イメージのDockerレジストリにアクセスするための資格情報を格納するSecretを作成できます。

  • kubernetes.io/dockercfg
  • kubernetes.io/dockerconfigjson

kubernetes.io/dockercfg型は、Dockerコマンドラインを構成するためのレガシー形式であるシリアル化された~/.dockercfgを保存するために予約されています。 このSecret型を使用する場合は、Secretのdataフィールドに.dockercfgキーが含まれていることを確認する必要があります。このキーの値は、base64形式でエンコードされた~/.dockercfgファイルの内容です。

kubernetes.io/dockerconfigjson型は、~/.dockercfgの新しいフォーマットである~/.docker/config.jsonファイルと同じフォーマットルールに従うシリアル化されたJSONを保存するために設計されています。 このSecret型を使用する場合、Secretオブジェクトのdataフィールドには.dockerconfigjsonキーが含まれている必要があります。このキーでは、~/.docker/config.jsonファイルのコンテンツがbase64でエンコードされた文字列として提供されます。

以下は、kubernetes.io/dockercfg型のSecretの例です。

apiVersion: v1
kind: Secret
  name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
  .dockercfg: |
    "<base64 encoded ~/.dockercfg file>"    

マニフェストを使用してこれらの型のSecretを作成すると、APIserverは期待されるキーがdataフィールドに存在するかどうかを確認し、提供された値を有効なJSONとして解析できるかどうかを確認します。APIサーバーは、JSONが実際にDocker configファイルであるかどうかを検証しません。

Docker configファイルがない場合、またはkubectlを使用してDockerレジストリSecretを作成する場合は、次の操作を実行できます。

kubectl create secret docker-registry secret-tiger-docker \
  --docker-username=tiger \
  --docker-password=pass113 \
  --docker-email=tiger@acme.com \
  --docker-server=my-registry.example:5000

このコマンドは、kubernetes.io/dockerconfigjson型のSecretを作成します。 dataフィールドから.dockerconfigjsonコンテンツをダンプすると、その場で作成された有効なDocker configである次のJSONコンテンツを取得します。

{
    "apiVersion": "v1",
    "data": {
        ".dockerconfigjson": "eyJhdXRocyI6eyJteS1yZWdpc3RyeTo1MDAwIjp7InVzZXJuYW1lIjoidGlnZXIiLCJwYXNzd29yZCI6InBhc3MxMTMiLCJlbWFpbCI6InRpZ2VyQGFjbWUuY29tIiwiYXV0aCI6ImRHbG5aWEk2Y0dGemN6RXhNdz09In19fQ=="
    },
    "kind": "Secret",
    "metadata": {
        "creationTimestamp": "2021-07-01T07:30:59Z",
        "name": "secret-tiger-docker",
        "namespace": "default",
        "resourceVersion": "566718",
        "uid": "e15c1d7b-9071-4100-8681-f3a7a2ce89ca"
    },
    "type": "kubernetes.io/dockerconfigjson"
}

Basic authentication Secret

kubernetes.io/basic-auth型は、Basic認証に必要な認証を保存するために提供されています。このSecret型を使用する場合、Secretのdataフィールドには次の2つのキーが含まれている必要があります。

  • username: 認証のためのユーザー名
  • password: 認証のためのパスワードかトークン

上記の2つのキーの両方の値は、base64でエンコードされた文字列です。もちろん、Secretの作成にstringDataを使用してクリアテキストコンテンツを提供することもできます。

次のYAMLは、Basic authentication Secretの設定例です。

apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: t0p-Secret

Basic認証Secret型は、ユーザーの便宜のためにのみ提供されています。Basic認証に使用される資格情報のOpaqueを作成できます。 ただし、組み込みのSecret型を使用すると、認証の形式を統一するのに役立ち、APIserverは必要なキーがSecret configurationで提供されているかどうかを確認します。

SSH authentication secrets

組み込みのタイプkubernetes.io/ssh-authは、SSH認証で使用されるデータを保存するために提供されています。このSecret型を使用する場合、使用するSSH認証としてdata(またはstringData)フィールドにssh-privatekeyキーと値のペアを指定する必要があります。

次のYAMLはSSH authentication Secretの設定例です:

apiVersion: v1
kind: Secret
metadata:
  name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
  # the data is abbreviated in this example
  ssh-privatekey: |
     MIIEpQIBAAKCAQEAulqb/Y ...     

SSH authentication Secret型は、ユーザーの便宜のためにのみ提供されています。 SSH認証に使用される資格情報のOpaqueを作成できます。 ただし、組み込みのSecret型を使用すると、認証の形式を統一するのに役立ち、APIserverは必要なキーがSecret configurationで提供されているかどうかを確認します。

TLS secrets

Kubernetesは、TLSに通常使用される証明書とそれに関連付けられたキーを保存するための組み込みのSecret型kubernetes.io/tlsを提供します。このデータは、主にIngressリソースのTLS terminationで使用されますが、他のリソースで使用されることも、ワークロードによって直接使用されることもあります。 このSecret型を使用する場合、APIサーバーは各キーの値を実際には検証しませんが、tls.keyおよびtls.crtキーをSecret configurationのdata(またはstringData)フィールドに指定する必要があります。

次のYAMLはTLS Secretの設定例です:

apiVersion: v1
kind: Secret
metadata:
  name: secret-tls
type: kubernetes.io/tls
data:
  # the data is abbreviated in this example
  tls.crt: |
    MIIC2DCCAcCgAwIBAgIBATANBgkqh ...    
  tls.key: |
    MIIEpgIBAAKCAQEA7yn3bRHQ5FHMQ ...    

TLS Secret型は、ユーザーの便宜のために提供されています。 TLSサーバーやクライアントに使用される資格情報のOpaqueを作成できます。ただし、組み込みのSecret型を使用すると、プロジェクトでSecret形式の一貫性を確保できます。APIserverは、必要なキーがSecret configurationで提供されているかどうかを確認します。

kubectlを使用してTLS Secretを作成する場合、次の例に示すようにtlsサブコマンドを使用できます。

kubectl create secret tls my-tls-secret \
  --cert=path/to/cert/file \
  --key=path/to/key/file

公開鍵と秘密鍵のペアは、事前に存在している必要があります。--certの公開鍵証明書は.PEMエンコード(Base64エンコードDER形式)であり、--keyの指定された秘密鍵と一致する必要があります。 秘密鍵は、一般にPEM秘密鍵形式と呼ばれる暗号化されていない形式である必要があります。どちらの場合も、PEMの最初と最後の行(たとえば、-------- BEGIN CERTIFICATE ------------ END CERTIFICATE ----)は含まれていません

Bootstrap token Secrets

Bootstrap token Secretは、Secretのtypebootstrap.kubernetes.io/tokenに明示的に指定することで作成できます。このタイプのSecretは、ノードのブートストラッププロセス中に使用されるトークン用に設計されています。よく知られているConfigMapに署名するために使用されるトークンを格納します。

Bootstrap token Secretは通常、kube-systemnamespaceで作成されbootstrap-token-<token-id>の形式で名前が付けられます。ここで<token-id>はトークンIDの6文字の文字列です。

Kubernetesマニフェストとして、Bootstrap token Secretは次のようになります。

apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-5emitj
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
  expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
  token-id: NWVtaXRq
  token-secret: a3E0Z2lodnN6emduMXAwcg==
  usage-bootstrap-authentication: dHJ1ZQ==
  usage-bootstrap-signing: dHJ1ZQ==

Bootstrap type Secretには、dataで指定された次のキーがあります。

  • token_id:トークン識別子としてのランダムな6文字の文字列。必須。
  • token-secret:実際のtoken secretとしてのランダムな16文字の文字列。必須。
  • description:トークンの使用目的を説明する人間が読める文字列。オプション。
  • expiration:トークンの有効期限を指定するRFC3339を使用した絶対UTC時間。オプション。
  • usage-bootstrap-<usage>:Bootstrap tokenの追加の使用法を示すブールフラグ。
  • auth-extra-groupssystem:bootstrappersグループに加えて認証されるグループ名のコンマ区切りのリスト。

上記のYAMLは、値がすべてbase64でエンコードされた文字列であるため、分かりづらく見えるかもしれません。実際には、次のYAMLを使用して同一のSecretを作成できます。

apiVersion: v1
kind: Secret
metadata:
  # Note how the Secret is named
  name: bootstrap-token-5emitj
  # A bootstrap token Secret usually resides in the kube-system namespace
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
  expiration: "2020-09-13T04:39:10Z"
  # This token ID is used in the name
  token-id: "5emitj"
  token-secret: "kq4gihvszzgn1p0r"
  # This token can be used for authentication
  usage-bootstrap-authentication: "true"
  # and it can be used for signing
  usage-bootstrap-signing: "true"

Secretの作成

Secretを作成するには、いくつかのオプションがあります。

Secretの編集

既存のSecretは次のコマンドで編集することができます。

kubectl edit secrets mysecret

デフォルトに設定されたエディターが開かれ、dataフィールドのBase64でエンコードされたSecretの値を編集することができます。

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: { ... }
  creationTimestamp: 2016-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque

Secretの使用

Podの中のコンテナがSecretを使うために、データボリュームとしてマウントしたり、環境変数として値を参照できるようにできます。 Secretは直接Podが参照できるようにはされず、システムの別の部分に使われることもあります。 例えば、Secretはあなたに代わってシステムの他の部分が外部のシステムとやりとりするために使う機密情報を保持することもあります。

SecretをファイルとしてPodから利用する

PodのボリュームとしてSecretを使うには、

  1. Secretを作成するか既存のものを使用します。複数のPodが同一のSecretを参照することができます。
  2. ボリュームを追加するため、Podの定義の.spec.volumes[]以下を書き換えます。ボリュームに命名し、.spec.volumes[].secret.secretNameフィールドはSecretオブジェクトの名称と同一にします。
  3. Secretを必要とするそれぞれのコンテナに.spec.containers[].volumeMounts[]を追加します。.spec.containers[].volumeMounts[].readOnly = trueを指定して.spec.containers[].volumeMounts[].mountPathをSecretをマウントする未使用のディレクトリ名にします。
  4. イメージやコマンドラインを変更し、プログラムがそのディレクトリを参照するようにします。連想配列dataのキーはmountPath以下のファイル名になります。

これはSecretをボリュームとしてマウントするPodの例です。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

使用したいSecretはそれぞれ.spec.volumesの中で参照されている必要があります。

Podに複数のコンテナがある場合、それぞれのコンテナがvolumeMountsブロックを必要としますが、.spec.volumesはSecret1つあたり1つで十分です。

多くのファイルを一つのSecretにまとめることも、多くのSecretを使うことも、便利な方を採ることができます。

Secretのキーの特定のパスへの割り当て

Secretのキーが割り当てられるパスを制御することができます。 それぞれのキーがターゲットとするパスは.spec.volumes[].secret.itemsフィールドによって指定できます。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

次のような挙動をします。

  • username/etc/foo/usernameの代わりに/etc/foo/my-group/my-usernameの元に格納されます。
  • passwordは現れません。

.spec.volumes[].secret.itemsが使われるときは、itemsの中で指定されたキーのみが現れます。 Secretの中の全てのキーを使用したい場合は、itemsフィールドに全て列挙する必要があります。 列挙されたキーは対応するSecretに存在する必要があり、そうでなければボリュームは生成されません。

Secretファイルのパーミッション

単一のSecretキーに対して、ファイルアクセスパーミッションビットを指定することができます。 パーミッションを指定しない場合、デフォルトで0644が使われます。 Secretボリューム全体のデフォルトモードを指定し、必要に応じてキー単位で上書きすることもできます。

例えば、次のようにしてデフォルトモードを指定できます。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 0400

Secretは/etc/fooにマウントされ、Secretボリュームが生成する全てのファイルはパーミッション0400に設定されます。

JSONの仕様は8進数の記述に対応していないため、パーミッション0400を示す値として256を使用することに注意が必要です。 Podの定義にJSONではなくYAMLを使う場合は、パーミッションを指定するためにより自然な8進表記を使うことができます。

kubectl execを使ってPodに入るときは、期待したファイルモードを知るためにシンボリックリンクを辿る必要があることに注意してください。

例として、PodのSecretのファイルモードを確認します。

kubectl exec mypod -it sh

cd /etc/foo
ls -l

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

total 0
lrwxrwxrwx 1 root root 15 May 18 00:18 password -> ..data/password
lrwxrwxrwx 1 root root 15 May 18 00:18 username -> ..data/username

正しいファイルモードを知るためにシンボリックリンクを辿ります。

cd /etc/foo/..data
ls -l

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

total 8
-r-------- 1 root root 12 May 18 00:18 password
-r-------- 1 root root  5 May 18 00:18 username

前の例のようにマッピングを使い、ファイルごとに異なるパーミッションを指定することができます。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
        mode: 0777

この例では、ファイル/etc/foo/my-group/my-usernameのパーミッションは0777になります。 JSONを使う場合は、JSONの制約により10進表記の511と記述する必要があります。

後で参照する場合、このパーミッションの値は10進表記で表示されることがあることに注意してください。

Secretの値のボリュームによる利用

Secretのボリュームがマウントされたコンテナからは、Secretのキーはファイル名として、Secretの値はBase64デコードされ、それらのファイルに格納されます。 上記の例のコンテナの中でコマンドを実行した結果を示します。

ls /etc/foo/

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

username
password
cat /etc/foo/username

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

admin
cat /etc/foo/password

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

1f2d1e2e67df

コンテナ内のプログラムはファイルからSecretの内容を読み取る責務を持ちます。

マウントされたSecretの自動更新

ボリュームとして使用されているSecretが更新されると、やがて割り当てられたキーも同様に更新されます。 kubeletは定期的な同期のたびにマウントされたSecretが新しいかどうかを確認します。 しかしながら、kubeletはSecretの現在の値の取得にローカルキャッシュを使用します。 このキャッシュはKubeletConfiguration struct内のConfigMapAndSecretChangeDetectionStrategyフィールドによって設定可能です。 Secretはwatch(デフォルト)、TTLベース、単に全てのリクエストをAPIサーバーへリダイレクトすることのいずれかによって伝搬します。 結果として、Secretが更新された時点からPodに新しいキーが反映されるまでの遅延時間の合計は、kubeletの同期間隔 + キャッシュの伝搬遅延となります。 キャッシュの遅延は、キャッシュの種別により、それぞれwatchの伝搬遅延、キャッシュのTTL、0になります。

Secretを環境変数として使用する

SecretをPodの環境変数として使用するには、

  1. Secretを作成するか既存のものを使います。複数のPodが同一のSecretを参照することができます。
  2. Podの定義を変更し、Secretを使用したいコンテナごとにSecretのキーと割り当てたい環境変数を指定します。Secretキーを利用する環境変数はenv[].valueFrom.secretKeyRefにSecretの名前とキーを指定すべきです。
  3. イメージまたはコマンドライン(もしくはその両方)を変更し、プログラムが指定した環境変数を参照するようにします。

Secretを環境変数で参照するPodの例を示します。

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

環境変数からのSecretの値の利用

Secretを環境変数として利用するコンテナの内部では、Secretのキーは一般の環境変数名として現れ、値はBase64デコードされた状態で保持されます。

上記の例のコンテナの内部でコマンドを実行した結果の例を示します。

echo $SECRET_USERNAME

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

admin
echo $SECRET_PASSWORD

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

1f2d1e2e67df

Immutable Secrets

FEATURE STATE: Kubernetes v1.19 [beta]

Kubernetesベータ機能ImmutableSecrets and ConfigMapsは、個々のSecretsとConfigMapsをimutableとして設定するオプションを提供します。Secret(少なくとも数万の、SecretからPodへの一意のマウント)を広範囲に使用するクラスターの場合、データの変更を防ぐことには次の利点があります。

  • アプリケーションの停止を引き起こす可能性のある偶発的な(または不要な)更新からユーザーを保護します
  • imutableとしてマークされたSecretのウォッチを閉じることで、kube-apiserverの負荷を大幅に削減することができ、クラスターのパフォーマンスを向上させます。

この機能は、ImmutableEphemeralVolumesfeature gateによって制御されます。これは、v1.19以降デフォルトで有効になっています。immutableフィールドをtrueに設定することで、imutableのSecretを作成できます。例えば、

apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
immutable: true

imagePullSecretsを使用する

imagePullSecretsフィールドは同一のネームスペース内のSecretの参照のリストです。 kubeletにDockerやその他のイメージレジストリのパスワードを渡すために、imagePullSecretsにそれを含むSecretを指定することができます。 kubeletはこの情報をPodのためにプライベートイメージをpullするために使います。 imagePullSecretsの詳細はPodSpec APIを参照してください。

imagePullSecretを手動で指定する

ImagePullSecretsの指定の方法はコンテナイメージのドキュメントに記載されています。

imagePullSecretsが自動的にアタッチされるようにする

imagePullSecretsを手動で作成し、サービスアカウントから参照することができます。 サービスアカウントが指定されるまたはデフォルトでサービスアカウントが設定されたPodは、サービスアカウントが持つimagePullSecretsフィールドを得ます。 詳細な手順の説明はサービスアカウントへのImagePullSecretsの追加を参照してください。

手動で作成されたSecretの自動的なマウント

手動で作成されたSecret(例えばGitHubアカウントへのアクセスに使うトークンを含む)はサービスアカウントを基に自動的にアタッチすることができます。 詳細な説明はPodPresetを使ったPodへの情報の注入を参照してください。

詳細

制限事項

Secretボリュームは指定されたオブジェクト参照が実際に存在するSecretオブジェクトを指していることを保証するため検証されます。 そのため、Secretはそれを必要とするPodよりも先に作成する必要があります。

Secretリソースはnamespaceに属します。 Secretは同一のnamespaceに属するPodからのみ参照することができます。

各Secretは1MiBの容量制限があります。 これはAPIサーバーやkubeletのメモリーを枯渇するような非常に大きなSecretを作成することを避けるためです。 しかしながら、小さなSecretを多数作成することも同様にメモリーを枯渇させます。 Secretに起因するメモリー使用量をより網羅的に制限することは、将来計画されています。

kubeletがPodに対してSecretを使用するとき、APIサーバーから取得されたSecretのみをサポートします。 これにはkubectlを利用して、またはレプリケーションコントローラーによって間接的に作成されたPodが含まれます。 kubeletの--manifest-urlフラグ、--configフラグ、またはREST APIにより生成されたPodは含まれません (これらはPodを生成するための一般的な方法ではありません)。

環境変数として使われるSecretは任意と指定されていない限り、それを使用するPodよりも先に作成される必要があります。 存在しないSecretへの参照はPodの起動を妨げます。

Secretに存在しないキーへの参照(secretKeyRefフィールド)はPodの起動を妨げます。

SecretをenvFromフィールドによって環境変数へ設定する場合、環境変数の名称として不適切なキーは飛ばされます。 Podは起動することを認められます。 このとき、reasonがInvalidVariableNamesであるイベントが発生し、メッセージに飛ばされたキーのリストが含まれます。 この例では、Podは2つの不適切なキー1badkey2alsobadを含むdefault/mysecretを参照しています。

kubectl get events

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

LASTSEEN   FIRSTSEEN   COUNT     NAME            KIND      SUBOBJECT                         TYPE      REASON
0s         0s          1         dapi-test-pod   Pod                                         Warning   InvalidEnvironmentVariableNames   kubelet, 127.0.0.1      Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.

SecretとPodの相互作用

Kubernetes APIがコールされてPodが生成されるとき、参照するSecretの存在は確認されません。 Podがスケジューリングされると、kubeletはSecretの値を取得しようとします。 Secretが存在しない、または一時的にAPIサーバーへの接続が途絶えたことにより取得できない場合、kubeletは定期的にリトライします。 kubeletはPodがまだ起動できない理由に関するイベントを報告します。 Secretが取得されると、kubeletはそのボリュームを作成しマウントします。 Podのボリュームが全てマウントされるまでは、Podのコンテナは起動することはありません。

ユースケース

ユースケース: コンテナの環境変数として

Secretの作成

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  USER_NAME: YWRtaW4=
  PASSWORD: MWYyZDFlMmU2N2Rm
kubectl apply -f mysecret.yaml

envFromを使ってSecretの全てのデータをコンテナの環境変数として定義します。 SecretのキーはPod内の環境変数の名称になります。

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      envFrom:
      - secretRef:
          name: mysecret
  restartPolicy: Never

ユースケース: SSH鍵を持つPod

SSH鍵を含むSecretを作成します。

kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub

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

secret "ssh-key-secret" created

SSH鍵を含むsecretGeneratorフィールドを持つkustomization.yamlを作成することもできます。

SSH鍵のSecretを参照し、ボリュームとして使用するPodを作成することができます。

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

コンテナのコマンドを実行するときは、下記のパスにて鍵が利用可能です。

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

コンテナはSecretのデータをSSH接続を確立するために使用することができます。

ユースケース: 本番、テスト用の認証情報を持つPod

あるPodは本番の認証情報のSecretを使用し、別のPodはテスト環境の認証情報のSecretを使用する例を示します。

secretGeneratorフィールドを持つkustomization.yamlを作成するか、kubectl create secretを実行します。

kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11

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

secret "prod-db-secret" created
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests

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

secret "test-db-secret" created

Podを作成します。

cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
EOF

同じkustomization.yamlにPodを追記します。

cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF

下記のコマンドを実行して、APIサーバーにこれらのオブジェクト群を適用します。

kubectl apply -k .

両方のコンテナはそれぞれのファイルシステムに下記に示すファイルを持ちます。ファイルの値はそれぞれのコンテナの環境ごとに異なります。

/etc/secret-volume/username
/etc/secret-volume/password

2つのPodの仕様の差分は1つのフィールドのみである点に留意してください。 これは共通のPodテンプレートから異なる能力を持つPodを作成することを容易にします。

2つのサービスアカウントを使用すると、ベースのPod仕様をさらに単純にすることができます。

  1. prod-userprod-db-secret
  2. test-usertest-db-secret

簡略化されたPod仕様は次のようになります。

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

ユースケース: Secretボリューム内のdotfile

キーをドットから始めることで、データを「隠す」ことができます。 このキーはdotfileまたは「隠し」ファイルを示します。例えば、次のSecretはsecret-volumeボリュームにマウントされます。

apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: dotfile-secret
  containers:
  - name: dotfile-test-container
    image: registry.k8s.io/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

このボリュームは.secret-fileという単一のファイルを含み、dotfile-test-containerはこのファイルを/etc/secret-volume/.secret-fileのパスに持ちます。

ユースケース: Podの中の単一コンテナのみが参照できるSecret

HTTPリクエストを扱い、複雑なビジネスロジックを処理し、メッセージにHMACによる認証コードを付与する必要のあるプログラムを考えます。 複雑なアプリケーションロジックを持つため、サーバーにリモートのファイルを読み出せる未知の脆弱性がある可能性があり、この脆弱性は攻撃者に秘密鍵を晒してしまいます。

このプログラムは2つのコンテナに含まれる2つのプロセスへと分割することができます。 フロントエンドのコンテナはユーザーとのやりとりやビジネスロジックを扱い、秘密鍵を参照することはできません。 署名コンテナは秘密鍵を参照することができて、単にフロントエンドからの署名リクエストに応答します。例えば、localhostの通信によって行います。

この分割する手法によって、攻撃者はアプリケーションサーバーを騙して任意の処理を実行させる必要があるため、ファイルの内容を読み出すより困難になります。

ベストプラクティス

Secret APIを使用するクライアント

Secret APIとやりとりするアプリケーションをデプロイするときには、RBACのような認可ポリシーを使用して、アクセスを制限すべきです。 Secretは様々な種類の重要な値を保持することが多く、サービスアカウントのトークンのようにKubernetes内部や、外部のシステムで昇格できるものも多くあります。個々のアプリケーションが、Secretの能力について推論することができたとしても、同じネームスペースの別のアプリケーションがその推定を覆すこともあります。

これらの理由により、ネームスペース内のSecretに対するwatchlistリクエストはかなり強力な能力であり、避けるべきです。Secretのリストを取得することはクライアントにネームスペース内の全てのSecretの値を調べさせることを認めるからです。クラスター内の全てのSecretに対するwatchlist権限は最も特権的な、システムレベルのコンポーネントに限って認めるべきです。

Secret APIへのアクセスが必要なアプリケーションは、必要なSecretに対するgetリクエストを発行すべきです。管理者は全てのSecretに対するアクセスは制限しつつ、アプリケーションが必要とする個々のインスタンスに対するアクセス許可を与えることができます。

getリクエストの繰り返しに対するパフォーマンスを向上するために、クライアントはSecretを参照するリソースを設計し、それをwatchして、参照が変更されたときにSecretを再度リクエストすることができます。加えて、個々のリソースをwatchすることのできる"bulk watch" APIが提案されており、将来のKubernetesリリースにて利用可能になる可能性があります。

セキュリティ特性

保護

Secretはそれを使用するPodとは独立に作成されるので、Podを作ったり、参照したり、編集したりするワークフローにおいてSecretが晒されるリスクは軽減されています。 システムは、可能であればSecretの内容をディスクに書き込まないような、Secretについて追加の考慮も行っています。

Secretはノード上のPodが必要とした場合のみ送られます。 kubeletはSecretがディスクストレージに書き込まれないよう、tmpfsに保存します。 Secretを必要とするPodが削除されると、kubeletはSecretのローカルコピーも同様に削除します。

同一のノードにいくつかのPodに対する複数のSecretが存在することもあります。 しかし、コンテナから参照できるのはPodが要求したSecretのみです。 そのため、あるPodが他のPodのためのSecretにアクセスすることはできません。

Podに複数のコンテナが含まれることもあります。しかし、Podの各コンテナはコンテナ内からSecretを参照するためにvolumeMountsによってSecretボリュームを要求する必要があります。 これはPodレベルでのセキュリティ分離を実装するのに便利です。

ほとんどのKubernetesディストリビューションにおいては、ユーザーとAPIサーバー間やAPIサーバーからkubelet間の通信はSSL/TLSで保護されています。 そのような経路で伝送される場合、Secretは保護されています。

FEATURE STATE: Kubernetes v1.13 [beta]

保存データの暗号化を有効にして、Secretがetcdに平文で保存されないようにすることができます。

リスク

  • APIサーバーでは、機密情報はetcdに保存されます。 そのため、
    • 管理者はクラスターデータの保存データの暗号化を有効にすべきです(v1.13以降が必要)。
    • 管理者はetcdへのアクセスを管理ユーザに限定すべきです。
    • 管理者はetcdで使用していたディスクを使用しなくなったときにはそれをワイプするか完全消去したくなるでしょう。
    • クラスターの中でetcdが動いている場合、管理者はetcdのピアツーピア通信がSSL/TLSを利用していることを確認すべきです。
  • Secretをマニフェストファイル(JSONまたはYAML)を介して設定する場合、それはBase64エンコードされた機密情報を含んでいるので、ファイルを共有したりソースリポジトリに入れることは秘密が侵害されることを意味します。Base64エンコーディングは暗号化手段では なく 、平文と同様であると判断すべきです。
  • アプリケーションはボリュームからSecretの値を読み取った後も、その値を保護する必要があります。例えば意図せずログに出力する、信用できない相手に送信するようなことがないようにです。
  • Secretを利用するPodを作成できるユーザーはSecretの値を見ることができます。たとえAPIサーバーのポリシーがユーザーにSecretの読み取りを許可していなくても、ユーザーはSecretを晒すPodを実行することができます。
  • 現在、任意のノードでルート権限を持つ人は誰でも、kubeletに偽装することで 任意の SecretをAPIサーバーから読み取ることができます。 単一のノードのルート権限を不正に取得された場合の影響を抑えるため、実際に必要としているノードに対してのみSecretを送る機能が計画されています。

次の項目

3.7.4 - コンテナのリソース管理

Podを指定する際に、コンテナが必要とする各リソースの量をオプションで指定することができます。 指定する最も一般的なリソースはCPUとメモリ(RAM)ですが、他にもあります。

Pod内のコンテナのリソース要求を指定すると、スケジューラはこの情報を使用して、どのNodeにPodを配置するかを決定します。コンテナに制限ソースを指定すると、kubeletはその制限を適用し、実行中のコンテナが設定した制限を超えてリソースを使用することができないようにします。また、kubeletは、少なくともそのシステムリソースのうち、要求の量を、そのコンテナが使用するために特別に確保します。

要求と制限

Podが動作しているNodeに利用可能なリソースが十分にある場合、そのリソースの要求が指定するよりも多くのリソースをコンテナが使用することが許可されます ただし、コンテナはそのリソースの制限を超えて使用することはできません。

たとえば、コンテナに256MiBのメモリー要求を設定し、そのコンテナが8GiBのメモリーを持つNodeにスケジュールされたPod内に存在し、他のPodが存在しない場合、コンテナはより多くのRAMを使用しようとする可能性があります。

そのコンテナに4GiBのメモリー制限を設定すると、kubelet(およびコンテナランタイム) が制限を適用します。ランタイムは、コンテナが設定済みのリソース制限を超えて使用するのを防ぎます。例えば、コンテナ内のプロセスが、許容量を超えるメモリを消費しようとすると、システムカーネルは、メモリ不足(OOM)エラーで、割り当てを試みたプロセスを終了します。

制限は、違反が検出されるとシステムが介入するように事後的に、またはコンテナが制限を超えないようにシステムが防ぐように強制的に、実装できます。 異なるランタイムは、同じ制限を実装するために異なる方法をとることができます。

リソースタイプ

CPUメモリーはいずれもリソースタイプです。リソースタイプには基本単位があります。 CPUは計算処理を表し、Kubernetes CPUsの単位で指定されます。 メモリはバイト単位で指定されます。 Kubernetes v1.14以降を使用している場合は、huge pageリソースを指定することができます。 Huge PageはLinux固有の機能であり、Nodeのカーネルはデフォルトのページサイズよりもはるかに大きいメモリブロックを割り当てます。

たとえば、デフォルトのページサイズが4KiBのシステムでは、hugepages-2Mi: 80Miという制限を指定できます。 コンテナが40を超える2MiBの巨大ページ(合計80 MiB)を割り当てようとすると、その割り当ては失敗します。

CPUとメモリーは、まとめてコンピュートリソースまたは単にリソースと呼ばれます。 コンピューティングリソースは、要求され、割り当てられ、消費され得る測定可能な量です。 それらはAPI resourcesとは異なります。 PodやServicesなどのAPIリソースは、Kubernetes APIサーバーを介して読み取りおよび変更できるオブジェクトです。

Podとコンテナのリソース要求と制限

Podの各コンテナは、次の1つ以上を指定できます。

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.limits.hugepages-<size>
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory
  • spec.containers[].resources.requests.hugepages-<size>

要求と制限はそれぞれのコンテナでのみ指定できますが、このPodリソースの要求と制限の関係性について理解すると便利です。 特定のリソースタイプのPodリソース要求/制限は、Pod内の各コンテナに対するそのタイプのリソース要求/制限の合計です。

Kubernetesにおけるリソースの単位

CPUの意味

CPUリソースの制限と要求は、cpu単位で測定されます。 Kuberenetesにおける1つのCPUは、クラウドプロバイダーの1 vCPU/コアおよびベアメタルのインテルプロセッサーの1 ハイパースレッドに相当します。

要求を少数で指定することもできます。 spec.containers[].resources.requests.cpu0.5のコンテナは、1CPUを要求するコンテナの半分のCPUが保証されます。 0.1という表現は100mという表現と同等であり、100ミリCPUと読み替えることができます。 100ミリコアという表現も、同じことを意味しています。 0.1のような小数点のある要求はAPIによって100mに変換され、1mより細かい精度は許可されません。 このため、100mの形式が推奨されます。

CPUは常に相対量としてではなく、絶対量として要求されます。 0.1は、シングルコア、デュアルコア、あるいは48コアマシンのどのCPUに対してでも、同一の量を要求します。

メモリーの意味

メモリーの制限と要求はバイト単位で測定されます。 E、P、T、G、M、Kのいずれかのサフィックスを使用して、メモリーを整数または固定小数点数として表すことができます。 また、Ei、Pi、Ti、Gi、Mi、Kiのような2の累乗の値を使用することもできます。 たとえば、以下はほぼ同じ値を表しています。

128974848, 129e6, 129M, 123Mi

例を見てみましょう。 次のPodには2つのコンテナがあります。 各コンテナには、0.25cpuおよび64MiB(226バイト)のメモリー要求と、0.5cpuおよび128MiBのメモリー制限があります Podには0.5cpuと128MiBのメモリー要求があり、1cpuと256MiBのメモリ制限があると言えます。

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

リソース要求を含むPodがどのようにスケジュールされるか

Podを作成すると、KubernetesスケジューラーはPodを実行するNodeを選択します。 各Nodeには、リソースタイプごとに最大容量があります。それは、Podに提供できるCPUとメモリの量です。 スケジューラーは、リソースタイプごとに、スケジュールされたコンテナのリソース要求の合計がNodeの容量より少ないことを確認します。 Node上の実際のメモリーまたはCPUリソースの使用率は非常に低いですが、容量チェックが失敗した場合、スケジューラーはNodeにPodを配置しないことに注意してください。 これにより、例えば日々のリソース要求のピーク時など、リソース利用が増加したときに、Nodeのリソース不足から保護されます。

リソース制限のあるPodがどのように実行されるか

kubeletがPodのコンテナを開始すると、CPUとメモリーの制限がコンテナランタイムに渡されます。

Dockerを使用する場合:

  • spec.containers[].resources.requests.cpuは、潜在的に小数であるコア値に変換され、1024倍されます。 docker runコマンドの--cpu-sharesフラグの値は、この数値と2のいずれか大きい方が用いられます。

  • spec.containers[].resources.limits.cpuはミリコアの値に変換され、100倍されます。 結果の値は、コンテナが100ミリ秒ごとに使用できるCPU時間の合計です。 コンテナは、この間隔の間、CPU時間の占有率を超えて使用することはできません。

  • spec.containers[].resources.limits.memoryは整数に変換され、docker runコマンドの--memoryフラグの値として使用されます。

コンテナがメモリー制限を超過すると、終了する場合があります。 コンテナが再起動可能である場合、kubeletは他のタイプのランタイム障害と同様にコンテナを再起動します。

コンテナがメモリー要求を超過すると、Nodeのメモリーが不足するたびにそのPodが排出される可能性があります。

コンテナは、長時間にわたってCPU制限を超えることが許可される場合と許可されない場合があります。 ただし、CPUの使用量が多すぎるために、コンテナが強制終了されることはありません。

コンテナをスケジュールできないか、リソース制限が原因で強制終了されているかどうかを確認するには、トラブルシューティングのセクションを参照してください。

コンピュートリソースとメモリーリソースの使用量を監視する

Podのリソース使用量は、Podのステータスの一部として報告されます。

オプションの監視ツールがクラスターにおいて利用可能な場合、Podのリソース使用量はメトリクスAPIから直接、もしくは監視ツールから取得できます。

ローカルのエフェメラルストレージ

FEATURE STATE: Kubernetes v1.10 [beta]

Nodeには、ローカルに接続された書き込み可能なデバイス、または場合によってはRAMによってサポートされるローカルのエフェメラルストレージがあります。 "エフェメラル"とは、耐久性について長期的な保証がないことを意味します。

Podは、スクラッチ領域、キャッシュ、ログ用にエフェメラルなローカルストレージを使用しています。 kubeletは、ローカルのエフェメラルストレージを使用して、Podにスクラッチ領域を提供し、emptyDir ボリュームをコンテナにマウントできます。

また、kubeletはこの種類のストレージを使用して、Nodeレベルのコンテナログ、コンテナイメージ、実行中のコンテナの書き込み可能なレイヤーを保持します。

ベータ版の機能として、Kubernetesでは、Podが消費するローカルのエフェメラルストレージの量を追跡、予約、制限することができます。

ローカルエフェメラルストレージの設定

Kubernetesは、Node上のローカルエフェメラルストレージを構成する2つの方法をサポートしています。

この構成では、さまざまな種類のローカルのエフェメラルデータ(emptyDirボリュームや、書き込み可能なレイヤー、コンテナイメージ、ログなど)をすべて1つのファイルシステムに配置します。 kubeletを構成する最も効果的な方法は、このファイルシステムをKubernetes(kubelet)データ専用にすることです。

kubeletはNodeレベルのコンテナログも書き込み、これらをエフェメラルなローカルストレージと同様に扱います。

kubeletは、設定されたログディレクトリ(デフォルトでは/var/log)内のファイルにログを書き出し、ローカルに保存された他のデータのベースディレクトリ(デフォルトでは/var/lib/kubelet)を持ちます。

通常、/var/lib/kubelet/var/logはどちらもシステムルートファイルシステムにあり、kubeletはそのレイアウトを考慮して設計されています。

Nodeには、Kubernetesに使用されていない他のファイルシステムを好きなだけ持つことができます。

Node上にファイルシステムがありますが、このファイルシステムは、ログやemptyDirボリュームなど、実行中のPodの一時的なデータに使用されます。 このファイルシステムは、例えばKubernetesに関連しないシステムログなどの他のデータに使用することができ、ルートファイルシステムとすることさえ可能です。

また、kubeletはノードレベルのコンテナログを最初のファイルシステムに書き込み、これらをエフェメラルなローカルストレージと同様に扱います。

また、別の論理ストレージデバイスでバックアップされた別のファイルシステムを使用することもできます。 この設定では、コンテナイメージレイヤーと書き込み可能なレイヤーを配置するようにkubeletに指示するディレクトリは、この2番目のファイルシステム上にあります。

最初のファイルシステムは、コンテナイメージレイヤーや書き込み可能なレイヤーを保持していません。

Nodeには、Kubernetesに使用されていない他のファイルシステムを好きなだけ持つことができます。

kubeletは、ローカルストレージの使用量を測定できます。 これは、以下の条件で提供されます。

  • LocalStorageCapacityIsolationフィーチャーゲートが有効になっています。(デフォルトでオンになっています。)
  • そして、ローカルのエフェメラルストレージ用にサポートされている構成の1つを使用してNodeをセットアップします。

別の構成を使用している場合、kubeletはローカルのエフェメラルストレージにリソース制限を適用しません。

ローカルのエフェメラルストレージの要求と制限設定

ローカルのエフェメラルストレージを管理するためには ephemeral-storage パラメーターを利用することができます。 Podの各コンテナは、次の1つ以上を指定できます。

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

ephemeral-storageの制限と要求はバイト単位で記します。 ストレージは、次のいずれかの接尾辞を使用して、通常の整数または固定小数点数として表すことができます。 E、P、T、G、M、K。Ei、Pi、Ti、Gi、Mi、Kiの2のべき乗を使用することもできます。 たとえば、以下はほぼ同じ値を表しています。

128974848, 129e6, 129M, 123Mi

次の例では、Podに2つのコンテナがあります。 各コンテナには、2GiBのローカルのエフェメラルストレージ要求があります。 各コンテナには、4GiBのローカルのエフェメラルストレージ制限があります。 したがって、Podには4GiBのローカルのエフェメラルストレージの要求と、8GiBのローカルのエフェメラルストレージ制限があります。

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
      - name: ephemeral
        mountPath: "/tmp"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
      - name: ephemeral
        mountPath: "/tmp"
  volumes:
    - name: ephemeral
      emptyDir: {}

エフェメラルストレージを要求するPodのスケジュール方法

Podを作成すると、KubernetesスケジューラーはPodを実行するNodeを選択します。 各Nodeには、Podに提供できるローカルのエフェメラルストレージの上限があります。 詳細については、Node割り当て可能を参照してください。

スケジューラーは、スケジュールされたコンテナのリソース要求の合計がNodeの容量より少なくなるようにします。

エフェメラルストレージの消費管理

kubeletがローカルのエフェメラルストレージをリソースとして管理している場合、kubeletはストレージの使用量を測定します

  • tmpfsemptyDirボリュームを除くemptyDirボリューム
  • Nodeレベルのログを保持するディレクトリ
  • 書き込み可能なコンテナレイヤー

Podが許可するよりも多くのエフェメラルストレージを使用している場合、kubeletはPodの排出をトリガーするシグナルを設定します。

コンテナレベルの分離の場合、コンテナの書き込み可能なレイヤーとログ使用量がストレージの制限を超えると、kubeletはPodに排出のマークを付けます。

Podレベルの分離の場合、kubeletはPod内のコンテナの制限を合計し、Podの全体的なストレージ制限を計算します。 このケースでは、すべてのコンテナからのローカルのエフェメラルストレージの使用量とPodのemptyDirボリュームの合計がPod全体のストレージ制限を超過する場合、 kubeletはPodをまた排出対象としてマークします。

kubeletはPodストレージの使用状況を測定するさまざまな方法をサポートしています

kubeletは、emptyDirボリューム、コンテナログディレクトリ、書き込み可能なコンテナレイヤーをスキャンする定期的なスケジュールチェックを実行します。

スキャンは、使用されているスペースの量を測定します。

FEATURE STATE: Kubernetes v1.15 [alpha]

プロジェクトクォータは、ファイルシステム上のストレージ使用量を管理するためのオペレーティングシステムレベルの機能です。 Kubernetesでは、プロジェクトクォータを有効にしてストレージの使用状況を監視することができます。 ノード上のemptyDirボリュームをバックアップしているファイルシステムがプロジェクトクォータをサポートしていることを確認してください。 例えば、XFSやext4fsはプロジェクトクォータを提供しています。

Kubernetesでは、1048576から始まるプロジェクトIDを使用します。 使用するプロジェクトIDは/etc/projects/etc/projidに登録されます。 この範囲のプロジェクトIDをシステム上で別の目的で使用する場合は、それらのプロジェクトIDを/etc/projects/etc/projidに登録し、 Kubernetesが使用しないようにする必要があります。

クォータはディレクトリスキャンよりも高速で正確です。 ディレクトリがプロジェクトに割り当てられると、ディレクトリ配下に作成されたファイルはすべてそのプロジェクト内に作成され、カーネルはそのプロジェクト内のファイルによって使用されているブロックの数を追跡するだけです。 ファイルが作成されて削除されても、開いているファイルディスクリプタがあれば、スペースを消費し続けます。 クォータトラッキングはそのスペースを正確に記録しますが、ディレクトリスキャンは削除されたファイルが使用するストレージを見落としてしまいます。

プロジェクトクォータを使用する場合は、次のことを行う必要があります。

  • kubelet設定で、LocalocalStorpactionCapactionIsolationFSQuotaMonitoring=trueフィーチャーゲートを有効にします。

  • ルートファイルシステム(またはオプションのランタイムファイルシステム))がプロジェクトクォータを有効にしていることを確認してください。 すべてのXFSファイルシステムはプロジェクトクォータをサポートしています。 ext4ファイルシステムでは、ファイルシステムがマウントされていない間は、プロジェクトクォータ追跡機能を有効にする必要があります。

    # ext4の場合、/dev/block-deviceがマウントされていません
    sudo tune2fs -O project -Q prjquota /dev/block-device
    
  • ルートファイルシステム(またはオプションのランタイムファイルシステム)がプロジェクトクォータを有効にしてマウントされていることを確認してください。 XFSとext4fsの両方で、マウントオプションはprjquotaという名前になっています。

拡張リソース

拡張リソースはkubernetes.ioドメインの外で完全に修飾されたリソース名です。 これにより、クラスターオペレーターはKubernetesに組み込まれていないリソースをアドバタイズし、ユーザはそれを利用することができるようになります。

拡張リソースを使用するためには、2つのステップが必要です。 第一に、クラスターオペレーターは拡張リソースをアドバタイズする必要があります。 第二に、ユーザーはPodで拡張リソースを要求する必要があります。

拡張リソースの管理

Nodeレベルの拡張リソース

Nodeレベルの拡張リソースはNodeに関連付けられています。

デバイスプラグイン管理のリソース

各Nodeにデバイスプラグインで管理されているリソースをアドバタイズする方法については、デバイスプラグインを参照してください。

その他のリソース

新しいNodeレベルの拡張リソースをアドバタイズするには、クラスターオペレーターはAPIサーバにPATCHHTTPリクエストを送信し、クラスター内のNodeのstatus.capacityに利用可能な量を指定します。 この操作の後、ノードのstatus.capacityには新しいリソースが含まれます。 status.allocatableフィールドは、kubeletによって非同期的に新しいリソースで自動的に更新されます。 スケジューラはPodの適合性を評価する際にNodeのstatus.allocatable値を使用するため、Nodeの容量に新しいリソースを追加してから、そのNodeでリソースのスケジューリングを要求する最初のPodが現れるまでには、短い遅延が生じる可能性があることに注意してください。

例:

以下は、curlを使用して、Masterがk8s-masterであるNodek8s-node-1で5つのexample.com/fooリソースを示すHTTPリクエストを作成する方法を示す例です。

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status

クラスターレベルの拡張リソース

クラスターレベルの拡張リソースはノードに関連付けられていません。 これらは通常、リソース消費とリソースクォータを処理するスケジューラー拡張機能によって管理されます。

スケジューラーポリシー構成では。スケジューラー拡張機能によって扱われる拡張リソースを指定できます。

例:

次のスケジューラーポリシーの構成は、クラスターレベルの拡張リソース"example.com/foo"がスケジューラー拡張機能によって処理されることを示しています。

  • スケジューラーは、Podが"example.com/foo"を要求した場合にのみ、Podをスケジューラー拡張機能に送信します。
  • ignoredBySchedulerフィールドは、スケジューラがそのPodFitsResources述語で"example.com/foo"リソースをチェックしないことを指定します。
{
  "kind": "Policy",
  "apiVersion": "v1",
  "extenders": [
    {
      "urlPrefix":"<extender-endpoint>",
      "bindVerb": "bind",
      "managedResources": [
        {
          "name": "example.com/foo",
          "ignoredByScheduler": true
        }
      ]
    }
  ]
}

拡張リソースの消費

ユーザーは、CPUやメモリのようにPodのスペックで拡張されたリソースを消費できます。 利用可能な量以上のリソースが同時にPodに割り当てられないように、スケジューラーがリソースアカウンティングを行います。

APIサーバーは、拡張リソースの量を整数の値で制限します。 有効な数量の例は、33000m3Kiです。 無効な数量の例は、0.51500mです。

Podで拡張リソースを消費するには、コンテナ名のspec.containers[].resources.limitsマップにキーとしてリソース名を含めます。

Podは、CPU、メモリ、拡張リソースを含むすべてのリソース要求が満たされた場合にのみスケジュールされます。 リソース要求が満たされない限り、PodはPENDING状態のままです。

例:

下のPodはCPUを2つ、"example.com/foo"(拡張リソース)を1つ要求しています。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: myimage
    resources:
      requests:
        cpu: 2
        example.com/foo: 1
      limits:
        example.com/foo: 1

トラブルシューティング

failedSchedulingイベントメッセージが表示され、Podが保留中になる

スケジューラーがPodが収容されるNodeを見つけられない場合、場所が見つかるまでPodはスケジュールされないままになります。 スケジューラーがPodの場所を見つけられないたびに、次のようなイベントが生成されます。

kubectl describe pod frontend | grep -A 3 Events
Events:
  FirstSeen LastSeen   Count  From          Subobject   PathReason      Message
  36s   5s     6      {scheduler }              FailedScheduling  Failed for reason PodExceedsFreeCPU and possibly others

前述の例では、"frontend"という名前のPodは、Node上のCPUリソースが不足しているためにスケジューリングに失敗しています。 同様のエラーメッセージは、メモリー不足による失敗を示唆することもあります(PodExceedsFreeMemory)。 一般的に、このタイプのメッセージでPodが保留されている場合は、いくつか試すべきことがあります。

  • クラスターにNodeを追加します。
  • 不要なポッドを終了して、保留中のPodのためのスペースを空けます。
  • PodがすべてのNodeよりも大きくないことを確認してください。 例えば、すべてのNodeがcpu: 1の容量を持っている場合、cpu: 1.1を要求するPodは決してスケジューリングされません。

Nodeの容量や割り当て量はkubectl describe nodesコマンドで調べることができる。 例えば、以下のようになる。

kubectl describe nodes e2e-test-node-pool-4lw4
Name:            e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... lines removed for clarity ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  kube-dns-3297075139-61lj3             260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (12%)        1070Mi (14%)

前述の出力では、Podが1120m以上のCPUや6.23Gi以上のメモリーを要求した場合、そのPodはNodeに収まらないことがわかります。

Podsセクションを見れば、どのPodがNode上でスペースを占有しているかがわかります。

システムデーモンが利用可能なリソースの一部を使用しているため、Podに利用可能なリソースの量はNodeの容量よりも少なくなっています。 allocatableフィールドNodeStatusは、Podに利用可能なリソースの量を与えます。 詳細については、ノード割り当て可能なリソースを参照してください。

リソースクォータ機能は、消費できるリソースの総量を制限するように設定することができます。 名前空間と組み合わせて使用すると、1つのチームがすべてのリソースを占有するのを防ぐことができます。

コンテナが終了した

コンテナはリソース不足のため、終了する可能性があります。 コンテナがリソース制限に達したために強制終了されているかどうかを確認するには、対象のPodでkubectl describe podを呼び出します。

kubectl describe pod simmemleak-hra99
Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Replication Controllers:        simmemleak (1/1 replicas created)
Containers:
  simmemleak:
    Image:  saadali/simmemleak
    Limits:
      cpu:                      100m
      memory:                   50Mi
    State:                      Running
      Started:                  Tue, 07 Jul 2015 12:54:41 -0700
    Last Termination State:     Terminated
      Exit Code:                1
      Started:                  Fri, 07 Jul 2015 12:54:30 -0700
      Finished:                 Fri, 07 Jul 2015 12:54:33 -0700
    Ready:                      False
    Restart Count:              5
Conditions:
  Type      Status
  Ready     False
Events:
  FirstSeen                         LastSeen                         Count  From                              SubobjectPath                       Reason      Message
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {scheduler }                                                          scheduled   Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   pulled      Pod container image "registry.k8s.io/pause:0.8.0" already present on machine
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   created     Created with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   started     Started with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    spec.containers{simmemleak}         created     Created with docker id 87348f12526a

上記の例では、Restart Count:5はPodのsimmemleakコンテナが終了して、5回再起動したことを示しています。

-o go-template=...オプションを指定して、kubectl get podを呼び出し、以前に終了したコンテナのステータスを取得できます。

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]

reason:OOM Killedが原因でコンテナが終了したことがわかります。OOMはメモリー不足を表します。

次の項目

3.7.5 - kubeconfigファイルを使用してクラスターアクセスを組織する

kubeconfigを使用すると、クラスターに、ユーザー、名前空間、認証の仕組みに関する情報を組織できます。kubectlコマンドラインツールはkubeconfigファイルを使用してクラスターを選択するために必要な情報を見つけ、クラスターのAPIサーバーと通信します。

デフォルトでは、kubectl$HOME/.kubeディレクトリ内にあるconfigという名前のファイルを探します。KUBECONFIG環境変数を設定するか、--kubeconfigフラグで指定することで、別のkubeconfigファイルを指定することもできます。

kubeconfigファイルの作成と指定に関するステップバイステップの手順を知りたいときは、複数のクラスターへのアクセスを設定するを参照してください。

複数のクラスター、ユーザ、認証の仕組みのサポート

複数のクラスターを持っていて、ユーザーやコンポーネントがさまざまな方法で認証を行う次のような状況を考えてみます。

  • 実行中のkubeletが証明書を使用して認証を行う可能性がある。
  • ユーザーがトークンを使用して認証を行う可能性がある。
  • 管理者が個別のユーザに提供する複数の証明書を持っている可能性がある。

kubeconfigファイルを使用すると、クラスター、ユーザー、名前空間を組織化することができます。また、contextを定義することで、複数のクラスターや名前空間を素早く簡単に切り替えられます。

Context

kubeconfigファイルのcontext要素は、アクセスパラメーターを使いやすい名前でグループ化するために使われます。各contextは3つのパラメーター、cluster、namespace、userを持ちます。デフォルトでは、kubectlコマンドラインツールはクラスターとの通信にcurrent contextのパラメーターを使用します。

current contextを選択するには、以下のコマンドを使用します。

kubectl config use-context

KUBECONFIG環境変数

KUBECONFIG環境変数には、kubeconfigファイルのリストを指定できます。LinuxとMacでは、リストはコロン区切りです。Windowsでは、セミコロン区切りです。KUBECONFIG環境変数は必須ではありません。KUBECONFIG環境変数が存在しない場合は、kubectlはデフォルトのkubeconfigファイルである$HOME/.kube/configを使用します。

KUBECONFIG環境変数が存在する場合は、kubectlKUBECONFIG環境変数にリストされているファイルをマージした結果を有効な設定として使用します。

kubeconfigファイルのマージ

設定ファイルを確認するには、以下のコマンドを実行します。

kubectl config view

上で説明したように、出力は1つのkubeconfigファイルから作られる場合も、複数のkubeconfigファイルをマージした結果となる場合もあります。

kubectlがkubeconfigファイルをマージするときに使用するルールを以下に示します。

  1. もし--kubeconfigフラグが設定されていた場合、指定したファイルだけが使用されます。マージは行いません。このフラグに指定できるのは1つのファイルだけです。

    そうでない場合、KUBECONFIG環境変数が設定されていた場合には、それをマージするべきファイルのリストとして使用します。KUBECONFIG環境変数にリストされたファイルのマージは、次のようなルールに従って行われます。

    • 空のファイルを無視する。
    • デシリアライズできない内容のファイルに対してエラーを出す。
    • 特定の値やmapのキーを設定する最初のファイルが勝つ。
    • 値やmapのキーは決して変更しない。 例: 最初のファイルが指定したcurrent-contextを保持する。 例: 2つのファイルがred-userを指定した場合、1つ目のファイルのred-userだけを使用する。もし2つ目のファイルのred-user以下に競合しないエントリーがあったとしても、それらは破棄する。

    KUBECONFIG環境変数を設定する例については、KUBECONFIG環境変数を設定するを参照してください。

    それ以外の場合は、デフォルトのkubeconfigファイル$HOME/.kube/configをマージせずに使用します。

  2. 以下のチェーンで最初に見つかったものをもとにして、使用するcontextを決定する。

    1. --contextコマンドラインフラグが存在すれば、それを使用する。
    2. マージしたkubeconrfigファイルからcurrent-contextを使用する。

    この時点では、空のcontextも許容されます。

  3. クラスターとユーザーを決定する。この時点では、contextである場合もそうでない場合もあります。以下のチェーンで最初に見つかったものをもとにして、クラスターとユーザーを決定します。この手順はユーザーとクラスターについてそれぞれ1回ずつ、合わせて2回実行されます。

    1. もし存在すれば、コマンドラインフラグ--userまたは--clusterを使用する。
    2. もしcontextが空でなければ、contextからユーザーまたはクラスターを取得する。

    この時点では、ユーザーとクラスターは空である可能性があります。

  4. 使用する実際のクラスター情報を決定する。この時点では、クラスター情報は存在しない可能性があります。以下のチェーンで最初に見つかったものをもとにして、クラスター情報の各パーツをそれぞれを構築します。

    1. もし存在すれば、--server--certificate-authority--insecure-skip-tls-verifyコマンドラインフラグを使用する。
    2. もしマージしたkubeconfigファイルにクラスター情報の属性が存在すれば、それを使用する。
    3. もしサーバーの場所が存在しなければ、マージは失敗する。
  5. 使用する実際のユーザー情報を決定する。クラスター情報の場合と同じルールを使用して、ユーザー情報を構築します。ただし、ユーザーごとに許可される認証方法は1つだけです。

    1. もし存在すれば、--client-certificate--client-key--username--password--tokenコマンドラインフラグを使用する。
    2. マージしたkubeconfigファイルのuserフィールドを使用する。
    3. もし2つの競合する方法が存在する場合、マージは失敗する。
  6. もし何らかの情報がまだ不足していれば、デフォルトの値を使用し、認証情報については場合によってはプロンプトを表示する。

ファイルリファレンス

kubeconfigファイル内のファイルとパスのリファレンスは、kubeconfigファイルの位置からの相対パスで指定します。コマンドライン上のファイルのリファレンスは、現在のワーキングディレクトリからの相対パスです。$HOME/.kube/config内では、相対パスは相対のまま、絶対パスは絶対のまま保存されます。

プロキシ

kubeconfigファイルでproxy-urlを使用すると、以下のようにクラスターごとにプロキシを使用するようにkubectlを設定することができます。

apiVersion: v1
kind: Config

clusters:
- cluster:
    proxy-url: http://proxy.example.org:3128
    server: https://k8s.example.org/k8s/clusters/c-xxyyzz
  name: development

users:
- name: developer

contexts:
- context:
  name: development

次の項目

3.8 - セキュリティ

クラウドネイティブなワークロードをセキュアに維持するための概念

3.8.1 - クラウドネイティブセキュリティの概要

この概要では、クラウドネイティブセキュリティにおけるKubernetesのセキュリティを考えるためのモデルを定義します。

クラウドネイティブセキュリティの4C

セキュリティは階層で考えることができます。クラウドネイティブの4Cは、クラウド、クラスター、コンテナ、そしてコードです。

クラウドネイティブセキュリティの4C

クラウドネイティブセキュリティモデルの各レイヤーは次の最も外側のレイヤー上に構築します。コードレイヤーは、強固な基盤(クラウド、クラスター、コンテナ)セキュリティレイヤーから恩恵を受けます。コードレベルのセキュリティに対応しても基盤レイヤーが低い水準のセキュリティでは守ることができません。

クラウド

いろいろな意味でも、クラウド(または同じ場所に設置されたサーバー、企業のデータセンター)はKubernetesクラスターのトラステッド・コンピューティング・ベースです。クラウドレイヤーが脆弱な(または脆弱な方法で構成されている)場合、この基盤の上に構築されたコンポーネントが安全であるという保証はありません。各クラウドプロバイダーは、それぞれの環境でワークロードを安全に実行させるためのセキュリティの推奨事項を作成しています。

クラウドプロバイダーのセキュリティ

Kubernetesクラスターを所有しているハードウェアや様々なクラウドプロバイダー上で実行している場合、セキュリティのベストプラクティスに関するドキュメントを参考にしてください。ここでは人気のあるクラウドプロバイダーのセキュリティドキュメントの一部のリンクを紹介します。

Cloud provider security
IaaSプロバイダーリンク
Alibaba Cloudhttps://www.alibabacloud.com/trust-center
Amazon Web Serviceshttps://aws.amazon.com/security/
Google Cloud Platformhttps://cloud.google.com/security/
Huawei Cloudhttps://www.huaweicloud.com/intl/ja-jp/securecenter/overallsafety.html
IBM Cloudhttps://www.ibm.com/cloud/security
Microsoft Azurehttps://docs.microsoft.com/en-us/azure/security/azure-security
Oracle Cloud Infrastructurehttps://www.oracle.com/security/
VMWare VSpherehttps://www.vmware.com/security/hardening-guides.html

インフラのセキュリティ

Kubernetesクラスターのインフラを保護するための提案です。

Infrastructure security
Kubernetesインフラに関する懸念事項推奨事項
API Server(コントロールプレーン)へのネットワークアクセスKubernetesコントロールプレーンへのすべてのアクセスは、インターネット上での一般公開は許されず、クラスター管理に必要なIPアドレスに制限するネットワークアクセス制御リストによって制御されます。
NodeへのネットワークアクセスNodeはコントロールプレーンの特定ポート のみ 接続(ネットワークアクセス制御リストを介して)を受け入れるよう設定し、NodePortとLoadBalancerタイプのKubernetesのServiceに関する接続を受け入れるよう設定する必要があります。可能であれば、それらのNodeはパブリックなインターネットに完全公開しないでください。
KubernetesからのクラウドプロバイダーAPIへのアクセス各クラウドプロバイダーはKubernetesコントロールプレーンとNodeに異なる権限を与える必要があります。最小権限の原則に従い、管理に必要なリソースに対してクラウドプロバイダーへのアクセスをクラスターに提供するのが最善です。KopsドキュメントにはIAMのポリシーとロールについての情報が記載されています。
etcdへのアクセスetcd(Kubernetesのデータストア)へのアクセスはコントロールプレーンのみに制限すべきです。設定によっては、TLS経由でetcdを利用する必要があります。詳細な情報はetcdドキュメントを参照してください。
etcdの暗号化可能な限り、保存時に全ドライブを暗号化することは良いプラクティスですが、etcdはクラスター全体(Secretを含む)の状態を保持しているため、そのディスクは特に暗号化する必要があります。

クラスター

Kubernetesを保護する為には2つの懸念事項があります。

  • 設定可能なクラスターコンポーネントの保護
  • クラスターで実行されるアプリケーションの保護

クラスターのコンポーネント

想定外または悪意のあるアクセスからクラスターを保護して適切なプラクティスを採用したい場合、クラスターの保護に関するアドバイスを読み従ってください。

クラスター内のコンポーネント(アプリケーション)

アプリケーションを対象にした攻撃に応じて、セキュリティの特定側面に焦点をあてたい場合があります。例:他のリソースとの連携で重要なサービス(サービスA)と、リソース枯渇攻撃に対して脆弱な別のワークロード(サービスB)が実行されている場合、サービスBのリソースを制限していないとサービスAが危険にさらされるリスクが高くなります。次の表はセキュリティの懸念事項とKubernetesで実行されるワークロードを保護するための推奨事項を示しています。

ワークロードセキュリティに関する懸念事項推奨事項
RBAC認可(Kubernetes APIへのアクセス)https://kubernetes.io/ja/docs/reference/access-authn-authz/rbac/
認証https://kubernetes.io/docs/concepts/security/controlling-access/
アプリケーションのSecret管理(およびetcdへの保存時に暗号化)https://kubernetes.io/ja/docs/concepts/configuration/secret/
https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
PodSecurityPolicyhttps://kubernetes.io/docs/concepts/policy/pod-security-policy/
Quality of Service (およびクラスターリソース管理)https://kubernetes.io/ja/docs/tasks/configure-pod-container/quality-service-pod/
NetworkPolicyhttps://kubernetes.io/ja/docs/concepts/services-networking/network-policies/
Kubernetes IngressのTLShttps://kubernetes.io/ja/docs/concepts/services-networking/ingress/#tls

コンテナ

コンテナセキュリティは本ガイドの範囲外になります。このトピックを検索するために一般的な推奨事項とリンクを以下に示します。

コンテナに関する懸念事項推奨事項
コンテナの脆弱性スキャンとOS依存のセキュリティイメージをビルドする手順の一部として、既知の脆弱性がないかコンテナをスキャンする必要があります。
イメージの署名と実施コンテナイメージを署名し、コンテナの中身に関する信頼性を維持します。
特権ユーザーを許可しないコンテナの構成時に、コンテナの目的を実行するために必要最低限なOS特権を持ったユーザーをコンテナ内部に作成する方法のドキュメントを参考にしてください。

コード

アプリケーションコードは、あなたが最も制御できる主要な攻撃対象のひとつです。アプリケーションコードを保護することはKubernetesのセキュリティトピックの範囲外ですが、アプリケーションコードを保護するための推奨事項を以下に示します。

コードセキュリティ

Code security
コードに関する懸念事項推奨事項
TLS経由のアクセスのみコードがTCP通信を必要とする場合は、事前にクライアントとのTLSハンドシェイクを実行してください。 いくつかの例外を除いて、全ての通信を暗号化してください。さらに一歩すすめて、サービス間のネットワークトラフィックを暗号化することはよい考えです。これは、サービスを特定した2つの証明書で通信の両端を検証する相互認証、またはmTLSして知られているプロセスを通じて実行できます。
通信ポートの範囲制限この推奨事項は一目瞭然かもしれませんが、可能なかぎり、通信とメトリクス収集に必要不可欠なサービスのポートのみを公開します。
サードパティに依存するセキュリティ既知の脆弱性についてアプリケーションのサードパーティ製ライブラリーを定期的にスキャンすることを推奨します。それぞれの言語は自動でこのチェックを実行するツールを持っています。
静的コード解析ほとんどの言語ではコードのスニペットを解析して、安全でない可能性のあるコーディングを分析する方法が提供しています。可能な限り、コードベースでスキャンして、よく起こるセキュリティエラーを検出できる自動ツールを使用してチェックを実行すべきです。一部のツールはここで紹介されています。 https://owasp.org/www-community/Source_Code_Analysis_Tools
動的プロービング攻撃よく知られているいくつかのサービス攻撃をサービスに対して試すことができる自動ツールがいくつかあります。これにはSQLインジェクション、CSRF、そしてXSSが含まれます。よく知られている動的解析ツールはOWASP Zed Attack proxytoolです。

次の項目

関連するKubernetesセキュリティについて学びます。

3.8.2 - Podセキュリティの標準

Podに対するセキュリティの設定は通常Security Contextを使用して適用されます。Security ContextはPod単位での特権やアクセスコントロールの定義を実現します。

クラスターにおけるSecurity Contextの強制やポリシーベースの定義はPod Security Policyによって実現されてきました。 Pod Security Policy はクラスターレベルのリソースで、Pod定義のセキュリティに関する設定を制御します。

しかし、PodSecurityPolicyを拡張したり代替する、ポリシーを強制するための多くの方法が生まれてきました。 このページの意図は、推奨されるPodのセキュリティプロファイルを特定の実装から切り離して詳しく説明することです。

ポリシーの種別

まず、幅広いセキュリティの範囲をカバーできる、基礎となるポリシーの定義が必要です。 それらは強く制限をかけるものから自由度の高いものまでをカバーすべきです。

  • 特権 - 制限のかかっていないポリシーで、可能な限り幅広い権限を提供します。このポリシーは既知の特権昇格を認めます。
  • ベースライン、デフォルト - 制限は最小限にされたポリシーですが、既知の特権昇格を防止します。デフォルト(最小の指定)のPod設定を許容します。
  • 制限 - 厳しく制限されたポリシーで、Podを強化するための現在のベストプラクティスに沿っています。

ポリシー

特権

特権ポリシーは意図的に開放されていて、完全に制限がかけられていません。この種のポリシーは通常、特権ユーザーまたは信頼されたユーザーが管理する、システムまたはインフラレベルのワークロードに対して適用されることを意図しています。

特権ポリシーは制限がないことと定義されます。gatekeeperのようにデフォルトで許可される仕組みでは、特権プロファイルはポリシーを設定せず、何も制限を適用しないことにあたります。 一方で、Pod Security Policyのようにデフォルトで拒否される仕組みでは、特権ポリシーでは全ての制限を無効化してコントロールできるようにする必要があります。

ベースライン、デフォルト

ベースライン、デフォルトのプロファイルは一般的なコンテナ化されたランタイムに適用しやすく、かつ既知の特権昇格を防ぐことを意図しています。 このポリシーはクリティカルではないアプリケーションの運用者または開発者を対象にしています。 次の項目は強制、または無効化すべきです。

ベースラインポリシーの定義
項目ポリシー
ホストのプロセス

Windows Podは、Windowsノードへの特権的なアクセスを可能にするHostProcessコンテナを実行する機能を提供します。ベースラインポリシーでは、ホストへの特権的なアクセスは禁止されています。HostProcess Podは、Kubernetes v1.22時点ではアルファ版の機能です。 ホストのネームスペースの共有は無効化すべきです。

制限されるフィールド

  • spec.securityContext.windowsOptions.hostProcess
  • spec.containers[*].securityContext.windowsOptions.hostProcess
  • spec.initContainers[*].securityContext.windowsOptions.hostProcess
  • spec.ephemeralContainers[*].securityContext.windowsOptions.hostProcess

認められる値

  • Undefined/nil
  • false
ホストのネームスペースホストのネームスペースの共有は無効化すべきです。

制限されるフィールド:
spec.hostNetwork
spec.hostPID
spec.hostIPC

認められる値: false, Undefined/nil
特権コンテナ特権を持つPodはほとんどのセキュリティ機構を無効化できるので、禁止すべきです。

制限されるフィールド:
spec.containers[*].securityContext.privileged
spec.initContainers[*].securityContext.privileged
spec.ephemeralContainers[*].securityContext.privileged

認められる値: false, undefined/nil
ケーパビリティーデフォルトよりも多くのケーパビリティーを与えることは禁止すべきです。

制限されるフィールド:
spec.containers[*].securityContext.capabilities.add
spec.initContainers[*].securityContext.capabilities.add
spec.ephemeralContainers[*].securityContext.capabilities.add

認められる値: Undefined/nil
AUDIT_WRITE
CHOWN
DAC_OVERRIDE
FOWNER
FSETID
KILL
MKNOD
NET_BIND_SERVICE
SETFCAP
SETGID
SETPCAP
SETUID
SYS_CHROOT
HostPathボリュームHostPathボリュームは禁止すべきです。

制限されるフィールド:
spec.volumes[*].hostPath

認められる値: undefined/nil
ホストのポートHostPortは禁止するか、最小限の既知のリストに限定すべきです。

制限されるフィールド:
spec.containers[*].ports[*].hostPort
spec.initContainers[*].ports[*].hostPort
spec.ephemeralContainers[*].ports[*].hostPort

認められる値: 0, undefined (または既知のリストに限定)
AppArmor(任意)サポートされるホストでは、AppArmorの'runtime/default'プロファイルがデフォルトで適用されます。デフォルトのポリシーはポリシーの上書きや無効化を防ぎ、許可されたポリシーのセットを上書きできないよう制限すべきです。

制限されるフィールド:
metadata.annotations['container.apparmor.security.beta.kubernetes.io/*']

認められる値: 'runtime/default', undefined, localhost/*
SELinux (任意)SELinuxのオプションをカスタムで設定することは禁止すべきです。

制限されるフィールド:
spec.securityContext.seLinuxOptions
spec.containers[*].securityContext.seLinuxOptions
spec.initContainers[*].securityContext.seLinuxOptions
spec.ephemeralContainers[*].securityContext.seLinuxOptions.type

認められる値:undefined/nil
Undefined/""
container_t
container_init_t
container_kvm_t


制限されるフィールド:
spec.securityContext.seLinuxOptions.user
spec.containers[*].securityContext.seLinuxOptions.user
spec.initContainers[*].securityContext.seLinuxOptions.user
spec.ephemeralContainers[*].securityContext.seLinuxOptions.user
spec.securityContext.seLinuxOptions.role
spec.containers[*].securityContext.seLinuxOptions.role
spec.initContainers[*].securityContext.seLinuxOptions.role
spec.ephemeralContainers[*].securityContext.seLinuxOptions.role

認められる値:undefined/nil
Undefined/""
/procマウントタイプ攻撃対象を縮小するため/procのマスクを設定し、必須とすべきです。

制限されるフィールド:
spec.containers[*].securityContext.procMount
spec.initContainers[*].securityContext.procMount
spec.ephemeralContainers[*].securityContext.procMount

認められる値:undefined/nil, 'Default'
Seccomp

Seccompプロファイルを明示的にUnconfinedに設定することはできません。

Restricted Fields

  • spec.securityContext.seccompProfile.type
  • spec.containers[*].securityContext.seccompProfile.type
  • spec.initContainers[*].securityContext.seccompProfile.type
  • spec.ephemeralContainers[*].securityContext.seccompProfile.type

Allowed Values

  • Undefined/nil
  • RuntimeDefault
  • Localhost
SysctlSysctlはセキュリティ機構を無効化したり、ホストの全てのコンテナに影響を与えたりすることが可能なので、「安全」なサブネットを除いては禁止すべきです。 コンテナまたはPodの中にsysctlがありネームスペースが分離されていて、同じノードの別のPodやプロセスから分離されている場合はsysctlは安全だと考えられます。

制限されるフィールド:
spec.securityContext.sysctls

認められる値:
kernel.shm_rmid_forced
net.ipv4.ip_local_port_range
net.ipv4.tcp_syncookies
net.ipv4.ping_group_range
undefined/空文字列

制限

制限ポリシーはいくらかの互換性を犠牲にして、Podを強化するためのベストプラクティスを強制することを意図しています。 セキュリティ上クリティカルなアプリケーションの運用者や開発者、また信頼度の低いユーザーも対象にしています。 下記の項目を強制、無効化すべきです。

制限ポリシーの定義
項目ポリシー
デフォルトプロファイルにある項目全て
VolumeタイプHostPathボリュームの制限に加え、制限プロファイルではコアでない種類のボリュームの利用をPersistentVolumeにより定義されたものに限定します。

制限されるフィールド:
spec.volumes[*].hostPath
spec.volumes[*].gcePersistentDisk
spec.volumes[*].awsElasticBlockStore
spec.volumes[*].gitRepo
spec.volumes[*].nfs
spec.volumes[*].iscsi
spec.volumes[*].glusterfs
spec.volumes[*].rbd
spec.volumes[*].flexVolume
spec.volumes[*].cinder
spec.volumes[*].cephfs
spec.volumes[*].flocker
spec.volumes[*].fc
spec.volumes[*].azureFile
spec.volumes[*].vsphereVolume
spec.volumes[*].quobyte
spec.volumes[*].azureDisk
spec.volumes[*].portworxVolume
spec.volumes[*].scaleIO
spec.volumes[*].storageos
spec.volumes[*].photonPersistentDisk

認められる値: undefined/nil
特権昇格特権昇格(ファイルモードのset-user-IDまたはset-group-IDのような方法による)は禁止すべきです。

制限されるフィールド:
spec.containers[*].securityContext.allowPrivilegeEscalation
spec.initContainers[*].securityContext.allowPrivilegeEscalation
spec.ephemeralContainers[*].securityContext.allowPrivilegeEscalation

認められる値: false
root以外での実行コンテナはroot以外のユーザーで実行する必要があります。

制限されるフィールド:
spec.securityContext.runAsNonRoot
spec.containers[*].securityContext.runAsNonRoot
spec.initContainers[*].securityContext.runAsNonRoot
spec.ephemeralContainers[*].securityContext.runAsNonRoot

認められる値: true
root以外のグループ (任意)コンテナをrootのプライマリまたは補助GIDで実行することを禁止すべきです。

制限されるフィールド:
spec.securityContext.runAsGroup
spec.securityContext.supplementalGroups[*]
spec.securityContext.fsGroup
spec.containers[*].securityContext.runAsGroup
spec.initContainers[*].securityContext.runAsGroup

認められる値:
0以外
undefined / nil (`*.runAsGroup`を除く)
SeccompSeccompのRuntimeDefaultを必須とする、または特定の追加プロファイルを許可することが必要です。

制限されるフィールド:
spec.securityContext.seccompProfile.type
spec.containers[*].securityContext.seccompProfile
spec.initContainers[*].securityContext.seccompProfile

認められる値:
'runtime/default'
undefined / nil
Capabilities (v1.22+)

コンテナはすべてのケイパビリティを削除する必要があり、NET_BIND_SERVICEケイパビリティを追加することだけが許可されています。

Restricted Fields

  • spec.containers[*].securityContext.capabilities.drop
  • spec.initContainers[*].securityContext.capabilities.drop
  • spec.ephemeralContainers[*].securityContext.capabilities.drop

Allowed Values

  • Any list of capabilities that includes ALL

Restricted Fields

  • spec.containers[*].securityContext.capabilities.add
  • spec.initContainers[*].securityContext.capabilities.add
  • spec.ephemeralContainers[*].securityContext.capabilities.add

Allowed Values

  • Undefined/nil
  • NET_BIND_SERVICE

ポリシーの実例

ポリシーの定義とポリシーの実装を切り離すことによって、ポリシーを強制する機構とは独立して、汎用的な理解や複数のクラスターにわたる共通言語とすることができます。

機構が成熟してきたら、ポリシーごとに下記に定義されます。それぞれのポリシーを強制する方法についてはここでは定義しません。

PodSecurityPolicy

FAQ

特権とデフォルトの間のプロファイルがないのはどうしてですか?

ここで定義されている3つのプロファイルは最も安全(制限)から最も安全ではない(特権)まで、直線的に段階が設定されており、幅広いワークロードをカバーしています。 ベースラインを超える特権が必要な場合、その多くはアプリケーションに特化しているため、その限られた要求に対して標準的なプロファイルを提供することはできません。 これは、このような場合に必ず特権プロファイルを使用すべきだという意味ではなく、場合に応じてポリシーを定義する必要があります。

将来、他のプロファイルの必要性が明らかになった場合、SIG Authはこの方針について再考する可能性があります。

セキュリティポリシーとセキュリティコンテキストの違いは何ですか?

Security Contextは実行時のコンテナやPodを設定するものです。 Security ContextはPodのマニフェストの中でPodやコンテナの仕様の一部として定義され、コンテナランタイムへ渡されるパラメーターを示します。

セキュリティポリシーはコントロールプレーンの機構で、Security Contextとそれ以外も含め、特定の設定を強制するものです。 2020年2月時点では、ネイティブにサポートされているポリシー強制の機構はPod Security Policyです。これはクラスター全体にわたってセキュリティポリシーを中央集権的に強制するものです。 セキュリティポリシーを強制する他の手段もKubernetesのエコシステムでは開発が進められています。例えばOPA Gatekeeperがあります。

WindowsのPodにはどのプロファイルを適用すればよいですか?

Kubernetesでは、Linuxベースのワークロードと比べてWindowsの使用は制限や差異があります。 特に、PodのSecurityContextフィールドはWindows環境では効果がありません。 したがって、現段階では標準化されたセキュリティポリシーは存在しません。

Windows Podに制限付きプロファイルを適用すると、実行時にPodに影響が出る場合があります。 制限付きプロファイルでは、Linux固有の制限(seccompプロファイルや特権昇格の不許可など)を適用する必要があります。 kubeletおよび/またはそのコンテナランタイムがこれらのLinux固有の値を無視した場合、Windows Podは制限付きプロファイル内で正常に動作します。 ただし、強制力がないため、Windows コンテナを使用するPodについては、ベースラインプロファイルと比較して追加の制限はありません。

HostProcess Podを作成するためのHostProcessフラグの使用は、特権的なポリシーに沿ってのみ行われるべきです。 Windows HostProcess Podの作成は、ベースラインおよび制限されたポリシーの下でブロックされているため、いかなるHostProcess Podも特権的であるとみなされるべきです。

サンドボックス化されたPodはどのように扱えばよいでしょうか?

現在のところ、Podがサンドボックス化されていると見なされるかどうかを制御できるAPI標準はありません。 サンドボックス化されたPodはサンドボックス化されたランタイム(例えばgVisorやKata Containers)の使用により特定することは可能ですが、サンドボックス化されたランタイムの標準的な定義は存在しません。

サンドボックス化されたランタイムに対して必要な保護は、それ以外に対するものとは異なります。 例えば、ワークロードがその基になるカーネルと分離されている場合、特権を制限する必要性は小さくなります。 これにより、強い権限を必要とするワークロードが隔離された状態を維持できます。

加えて、サンドボックス化されたワークロードの保護はサンドボックス化の実装に強く依存します。 したがって、全てのサンドボックス化されたワークロードに推奨される単一のポリシーは存在しません。

3.8.3 - Podのセキュリティアドミッション

FEATURE STATE: Kubernetes v1.23 [beta]

KubernetesのPodセキュリティの標準はPodに対して異なる分離レベルを定義します。 これらの標準によって、Podの動作をどのように制限したいかを、明確かつ一貫した方法で定義することができます。

ベータ版機能として、KubernetesはPodSecurityPolicyの後継である組み込みの Pod Security アドミッションコントローラーを提供しています。 Podセキュリティの制限は、Pod作成時に名前空間レベルで適用されます。

PodSecurityアドミッションプラグインの有効化

v1.23において、PodSecurityフィーチャーゲートはベータ版の機能で、デフォルトで有効化されています。 v1.22において、PodSecurityフィーチャーゲートはアルファ版の機能で、組み込みのアドミッションプラグインを使用するには、kube-apiserverで有効にする必要があります。

--feature-gates="...,PodSecurity=true"

代替案:PodSecurityアドミッションwebhookのインストール

クラスターがv1.22より古い、あるいはPodSecurity機能を有効にできないなどの理由で、ビルトインのPodSecurityアドミッションプラグインが使えない環境では、PodSecurityはアドミッションロジックはベータ版のvalidating admission webhookとしても提供されています。

ビルド前のコンテナイメージ、証明書生成スクリプト、マニフェストの例は、https://git.k8s.io/pod-security-admission/webhookで入手可能です。

インストール方法:

git clone git@github.com:kubernetes/pod-security-admission.git
cd pod-security-admission/webhook
make certs
kubectl apply -k .

Podのセキュリティレベル

Podのセキュリティアドミッションは、PodのSecurity Contextとその他の関連フィールドに、Podセキュリティの標準で定義された3つのレベル、privilegedbaselinerestrictedに従って要件を設定するものです。 これらの要件の詳細については、Podセキュリティの標準のページを参照してください。

Podの名前空間に対するセキュリティアドミッションラベル

この機能を有効にするか、Webhookをインストールすると、名前空間を設定して、各名前空間でPodセキュリティに使用したいadmission controlモードを定義できます。 Kubernetesは、名前空間に使用したい定義済みのPodセキュリティの標準レベルのいずれかを適用するために設定できるラベルのセットを用意しています。 選択したラベルは、以下のように違反の可能性が検出された場合にコントロールプレーンが取るアクションを定義します。

Podのセキュリティアドミッションのモード
モード説明
enforceポリシーに違反した場合、Podは拒否されます。
auditポリシー違反は、監査ログに記録されるイベントに監査アノテーションを追加するトリガーとなりますが、それ以外は許可されます。
warnポリシーに違反した場合は、ユーザーへの警告がトリガーされますが、それ以外は許可されます。

名前空間は、任意のまたはすべてのモードを設定することができ、異なるモードに対して異なるレベルを設定することもできます。

各モードには、使用するポリシーを決定する2つのラベルがあります。

# モードごとのレベルラベルは、そのモードに適用するポリシーレベルを示す。
#
# MODEは`enforce`、`audit`、`warn`のいずれかでなければならない。
# LEVELは`privileged`、`baseline`、`restricted`のいずれかでなければならない。
pod-security.kubernetes.io/<MODE>: <LEVEL>

# オプション: モードごとのバージョンラベルは、Kubernetesのマイナーバージョンに同梱される
# バージョンにポリシーを固定するために使用できる(例えばv1.30など)。
#
# MODEは`enforce`、`audit`、`warn`のいずれかでなければならない。
# VERSIONは有効なKubernetesのマイナーバージョンか`latest`でなければならない。
pod-security.kubernetes.io/<MODE>-version: <VERSION>

名前空間ラベルでのPodセキュリティの標準の適用で使用例を確認できます。

WorkloadのリソースとPodテンプレート

Podは、DeploymentJobのようなワークロードオブジェクトを作成することによって、しばしば間接的に生成されます。 ワークロードオブジェクトは_Pod template_を定義し、ワークロードリソースのコントローラーはそのテンプレートに基づきPodを作成します。 違反の早期発見を支援するために、auditモードとwarningモードは、ワークロードリソースに適用されます。 ただし、enforceモードはワークロードリソースには適用されず、結果としてのPodオブジェクトにのみ適用されます。

適用除外(Exemption)

Podセキュリティの施行から exemptions を定義することで、特定の名前空間に関連するポリシーのために禁止されていたPodの作成を許可することができます。 Exemptionはアドミッションコントローラーの設定で静的に設定することができます。

Exemptionは明示的に列挙する必要があります。 Exemptionを満たしたリクエストは、アドミッションコントローラーによって 無視 されます(enforceauditwarnのすべての動作がスキップされます)。Exemptionの次元は以下の通りです。

  • Usernames: 認証されていない(あるいは偽装された)ユーザー名を持つユーザーからの要求は無視されます。
  • RuntimeClassNames: Podとワークロードリソースで指定された除外ランタイムクラス名は、無視されます。
  • Namespaces: 除外された名前空間のPodとワークロードリソースは、無視されます。

以下のPodフィールドに対する更新は、ポリシーチェックの対象外となります。つまり、Podの更新要求がこれらのフィールドを変更するだけであれば、Podが現在のポリシーレベルに違反していても拒否されることはありません。

  • すべてのメタデータの更新(seccompまたはAppArmorアノテーションへの変更を除く)
    • seccomp.security.alpha.kubernetes.io/pod(非推奨)
    • container.seccomp.security.alpha.kubernetes.io/*(非推奨)
    • container.apparmor.security.beta.kubernetes.io/*
  • .spec.activeDeadlineSecondsに対する有効な更新
  • .spec.tolerationsに対する有効な更新

次の項目

3.8.4 - Kubernetes APIへのアクセスコントロール

このページではKubernetes APIへのアクセスコントロールの概要を説明します。

Kubernetes APIにはkubectlやクライアントライブラリ、あるいはRESTリクエストを用いてアクセスします。 APIアクセスには、人間のユーザーとKubernetesサービスアカウントの両方が認証可能です。 リクエストがAPIに到達すると、次の図のようにいくつかの段階を経ます。

Kubernetes APIリクエストの処理手順図

トランスポート層のセキュリティ

一般的なKubernetesクラスターでは、APIはTLSで保護された443番ポートで提供されます。 APIサーバーは証明書を提示します。 この証明書は、プライベート認証局(CA)を用いて署名することも、一般に認知されているCAと連携した公開鍵基盤に基づき署名することも可能です。

クラスターがプライベート認証局を使用している場合、接続を信頼し、傍受されていないと確信できるように、クライアント上の~/.kube/configに設定されたそのCA証明書のコピーが必要です。

クライアントは、この段階でTLSクライアント証明書を提示することができます。

認証

TLSが確立されると、HTTPリクエストは認証のステップに移行します。 これは図中のステップ1に該当します。 クラスター作成スクリプトまたはクラスター管理者は、1つまたは複数のAuthenticatorモジュールを実行するようにAPIサーバーを設定します。 Authenticatorについては、認証で詳しく説明されています。

認証ステップへの入力はHTTPリクエスト全体ですが、通常はヘッダとクライアント証明書の両方、またはどちらかを調べます。

認証モジュールには、クライアント証明書、パスワード、プレーントークン、ブートストラップトークン、JSON Web Tokens(サービスアカウントに使用)などがあります。

複数の認証モジュールを指定することができ、その場合、1つの認証モジュールが成功するまで、それぞれを順番に試行します。

認証できない場合、HTTPステータスコード401で拒否されます。 そうでなければ、ユーザーは特定のusernameとして認証され、そのユーザー名は後続のステップでの判断に使用できるようになります。 また、ユーザーのグループメンバーシップを提供する認証機関と、提供しない認証機関があります。

Kubernetesはアクセスコントロールの決定やリクエストログにユーザー名を使用しますが、Userオブジェクトを持たず、ユーザー名やその他のユーザーに関する情報をAPIはに保存しません。

認可

リクエストが特定のユーザーからのものであると認証された後、そのリクエストは認可される必要があります。 これは図のステップ2に該当します。

リクエストには、リクエスト者のユーザー名、リクエストされたアクション、そのアクションによって影響を受けるオブジェクトを含める必要があります。 既存のポリシーで、ユーザーが要求されたアクションを完了するための権限を持っていると宣言されている場合、リクエストは承認されます。

例えば、Bobが以下のようなポリシーを持っている場合、彼は名前空間projectCaribou内のPodのみを読むことができます。

{
    "apiVersion": "abac.authorization.kubernetes.io/v1beta1",
    "kind": "Policy",
    "spec": {
        "user": "bob",
        "namespace": "projectCaribou",
        "resource": "pods",
        "readonly": true
    }
}

Bobが次のようなリクエストをした場合、Bobは名前空間projectCaribouのオブジェクトを読むことが許可されているので、このリクエストは認可されます。

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "projectCaribou",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    }
  }
}

Bobが名前空間projectCaribouのオブジェクトに書き込み(createまたはupdate)のリクエストをした場合、承認は拒否されます。 また、もしBobがprojectFishのような別の名前空間にあるオブジェクトを読み込む(get)リクエストをした場合も、承認は拒否されます。

Kubernetesの認可では、組織全体またはクラウドプロバイダー全体の既存のアクセスコントロールシステムと対話するために、共通のREST属性を使用する必要があります。 これらのコントロールシステムは、Kubernetes API以外のAPIとやり取りする可能性があるため、REST形式を使用することが重要です。

Kubernetesは、ABACモード、RBACモード、Webhookモードなど、複数の認可モジュールをサポートしています。 管理者はクラスターを作成する際に、APIサーバーで使用する認証モジュールを設定します。 複数の認可モジュールが設定されている場合、Kubernetesは各モジュールをチェックし、いずれかのモジュールがリクエストを認可した場合、リクエストを続行することができます。 すべてのモジュールがリクエストを拒否した場合、リクエストは拒否されます(HTTPステータスコード403)。

サポートされている認可モジュールを使用したポリシー作成の詳細を含む、Kubernetesの認可については、認可を参照してください。

アドミッションコントロール

アドミッションコントロールモジュールは、リクエストを変更したり拒否したりすることができるソフトウェアモジュールです。 認可モジュールが利用できる属性に加えて、アドミッションコントロールモジュールは、作成または修正されるオブジェクトのコンテンツにアクセスすることができます。

アドミッションコントローラーは、オブジェクトの作成、変更、削除、または接続(プロキシ)を行うリクエストに対して動作します。 アドミッションコントローラーは、単にオブジェクトを読み取るだけのリクエストには動作しません。 複数のアドミッションコントローラーが設定されている場合は、順番に呼び出されます。

これは図中のステップ3に該当します。

認証・認可モジュールとは異なり、いずれかのアドミッションコントローラーモジュールが拒否した場合、リクエストは即座に拒否されます。

オブジェクトを拒否するだけでなく、アドミッションコントローラーは、フィールドに複雑なデフォルトを設定することもできます。

利用可能なアドミッションコントロールモジュールは、アドミッションコントローラーに記載されています。

リクエストがすべてのアドミッションコントローラーを通過すると、対応するAPIオブジェクトの検証ルーチンを使って検証され、オブジェクトストアに書き込まれます(図のステップ4に該当します)。

監査

Kubernetesの監査は、クラスター内の一連のアクションを文書化した、セキュリティに関連する時系列の記録を提供します。 クラスターは、ユーザー、Kubernetes APIを使用するアプリケーション、およびコントロールプレーン自身によって生成されるアクティビティを監査します。

詳しくは監査をご覧ください。

APIサーバーのIPとポート

これまでの説明は、APIサーバーのセキュアポートに送信されるリクエストに適用されます(典型的なケース)。 APIサーバーは、実際には2つのポートでサービスを提供することができます。

デフォルトでは、Kubernetes APIサーバーは2つのポートでHTTPを提供します。

  1. localhostポート:

    • テストとブートストラップ用で、マスターノードの他のコンポーネント(スケジューラー、コントローラーマネージャー)がAPIと通信するためのものです。
    • TLSは使用しません。
    • デフォルトポートは8080です。
    • デフォルトのIPはlocalhostですが、--insecure-bind-addressフラグで変更することができます。
    • リクエストは認証と認可のモジュールをバイパスします。
    • リクエストは、アドミッションコントロールモジュールによって処理されます。
    • ホストにアクセスする必要があるため、保護されています。
  2. “セキュアポート”:

    • 可能な限りこちらを使用してください。
    • TLSを使用します。証明書は--tls-cert-fileフラグで、鍵は--tls-private-key-fileフラグで設定します。
    • デフォルトポートは6443です。--secure-portフラグで変更することができます。
    • デフォルトのIPは、最初の非localhostのネットワークインターフェースです。--bind-addressフラグで変更することができます。
    • リクエストは、認証・認可モジュールによって処理されます。
    • リクエストは、アドミッションコントロールモジュールによって処理されます。
    • 認証・認可モジュールが実行されます。

次の項目

認証、認可、APIアクセスコントロールに関する詳しいドキュメントはこちらをご覧ください。

以下についても知ることができます。

  • PodがAPIクレデンシャルを取得するためにSecretsを使用する方法について。

3.8.5 - Kubernetes Secretの適切な使用方法

クラスター管理者とアプリケーション開発者向けの適切なSecret管理の原則と実践方法。

Kubernetesでは、Secretは次のようなオブジェクトです。 パスワードやOAuthトークン、SSHキーのような機密の情報を保持します。

Secretは、機密情報の使用方法をより管理しやすくし、偶発的な漏洩のリスクを減らすことができます。Secretの値はbase64文字列としてエンコードされ、デフォルトでは暗号化されずに保存されますが、保存時に暗号化するように設定することもできます。

Podは、ボリュームマウントや環境変数など、さまざまな方法でSecretを参照できます。Secretは機密データ用に設計されており、ConfigMapは非機密データ用に設計されています。

以下の適切な使用方法は、クラスター管理者とアプリケーション開発者の両方を対象としています。 これらのガイドラインに従って、Secretオブジェクト内の機密情報のセキュリティを向上させ、Secretの効果的な管理を行ってください。

クラスター管理者

このセクションでは、クラスター管理者がクラスター内の機密情報のセキュリティを強化するために使用できる適切な方法を提供します。

データ保存時の暗号化を構成する

デフォルトでは、Secretオブジェクトはetcd内で暗号化されていない状態で保存されます。 etcd内のSecretデータを暗号化するように構成する必要があります。 手順については、機密データ保存時の暗号化を参照してください。

Secretへの最小特権アクセスを構成する

Kubernetesのロールベースアクセス制御 (RBAC)などのアクセス制御メカニズムを計画する際、Secretオブジェクトへのアクセスに関する以下のガイドラインを考慮してください。 また、RBACの適切な使用方法の他のガイドラインにも従ってください。

  • コンポーネント: watchまたはlistアクセスを、最上位の特権を持つシステムレベルのコンポーネントのみに制限してください。コンポーネントの通常の動作が必要とする場合にのみ、Secretへのgetアクセスを許可してください。
  • ユーザー: Secretへのgetwatchlistアクセスを制限してください。etcdへのアクセスはクラスター管理者にのみ許可し、読み取り専用アクセスも許可してください。特定の注釈を持つSecretへのアクセスを制限するなど、より複雑なアクセス制御については、サードパーティの認証メカニズムを検討してください。

Secretを使用するPodを作成できるユーザーは、そのSecretの値も見ることができます。 クラスターのポリシーがユーザーにSecretを直接読むことを許可しない場合でも、同じユーザーがSecretを公開するPodを実行するアクセスを持つかもしれません。 このようなアクセスを持つユーザーによるSecretデータの意図的または偶発的な公開の影響を検出または制限することができます。 いくつかの推奨事項には以下があります:

  • 短寿命のSecretを使用する
  • 特定のイベントに対してアラートを出す監査ルールを実装する(例:単一ユーザーによる複数のSecretの同時読み取り)

etcdの管理ポリシーを改善する

使用しなくなった場合には、etcdが使用する永続ストレージを削除するかシュレッダーで処理してください。

複数のetcdインスタンスがある場合、インスタンス間の通信を暗号化されたSSL/TLS通信に設定して、転送中のSecretデータを保護してください。

外部Secretへのアクセスを構成する

外部のSecretストアプロバイダーを使用して機密データをクラスターの外部に保存し、その情報にアクセスするようにPodを構成できます。 Kubernetes Secrets Store CSI Driverは、kubeletが外部ストアからSecretを取得し、データにアクセスすることを許可された特定のPodにSecretをボリュームとしてマウントするDaemonSetです。

サポートされているプロバイダーの一覧については、Secret Store CSI Driverのプロバイダーを参照してください。

開発者

このセクションでは、Kubernetesリソースの作成と展開時に機密データのセキュリティを向上させるための開発者向けの適切な使用方法を提供します。

特定のコンテナへのSecretアクセスを制限する

Pod内で複数のコンテナを定義し、そのうち1つのコンテナだけがSecretへのアクセスを必要とする場合、他のコンテナがそのSecretにアクセスできないようにボリュームマウントや環境変数の設定を行ってください。

読み取り後にSecretデータを保護する

アプリケーションは、環境変数やボリュームから機密情報を読み取った後も、その値を保護する必要があります。 例えば、アプリケーションは機密情報を平文でログに記録したり、信頼できない第三者に送信したりしないようにする必要があります。

Secretマニフェストの共有を避ける

Secretをマニフェストを介して設定し、秘密データをBase64でエンコードしている場合、このファイルを共有したりソースリポジトリにチェックインしたりすると、その秘密はマニフェストを読むことのできる全員に公開されます。

3.9 - ポリシー

3.9.1 - Limit Range

デフォルトでは、コンテナは、Kubernetesクラスター上の計算リソースの消費を制限されずに実行されます。リソースクォータを利用すれば、クラスター管理者はリソースの消費と作成を名前空間ベースで制限することができます。名前空間内では、Podやコンテナは名前空間のリソースクォータで定義された範囲内でできるだけ多くのCPUとメモリーを消費できてしまうため、1つのPodまたはコンテナが利用可能なすべてのリソースを専有してしまう恐れがあります。LimitRangeを利用すれば、このような名前空間内での(Podやコンテナへの)リソースの割り当てを制限するポリシーを定めることができます。

LimitRangeを利用すると、次のような制約を課せるようになります。

  • 名前空間内のPodまたはコンテナごとに、計算リソースの使用量の最小値と最大値を強制する。
  • 名前空間内のPersistentVolumeClaimごとに、ストレージリクエストの最小値と最大値を強制する。
  • 名前空間内で、リソースのrequestとlimitの割合を強制する。
  • 名前空間内の計算リソースのデフォルトのrequest/limitの値を設定して、実行時にコンテナに自動的に注入する。

LimitRangeを有効にする

Kubernetes 1.10以降では、LimitRangeのサポートはデフォルトで有効になりました。

LimitRangeが特定の名前空間内で強制されるのは、その名前空間内にLimitRangeオブジェクトが存在する場合です。

LimitRangeオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。

Limit Rangeの概要

  • 管理者は、1つの名前空間に1つのLimitRangeを作成します。
  • ユーザーは、Pod、コンテナ、PersistentVolumeClaimのようなリソースを名前空間内に作成します。
  • LimitRangerアドミッションコントローラーは、計算リソース要求が設定されていないすべてのPodとコンテナに対して、デフォルト値と制限値を強制します。そして、リソースの使用量を追跡し、名前空間内に存在するすべてのLimitRangeで定義された最小値、最大値、割合を外れないことを保証します。
  • LimitRangeの制約を破るようなリソース(Pod、コンテナ、PersistentVolumeClaim)の作成や更新を行うと、APIサーバーへのリクエストがHTTPステータスコード403 FORBIDDENで失敗し、破られた制約を説明するメッセージが返されます。
  • 名前空間内でLimitRangeがcpumemoryなどの計算リソースに対して有効になっている場合、ユーザーはrequestsやlimitsに値を指定しなければなりません。指定しなかった場合、システムはPodの作成を拒否する可能性があります。
  • LimitRangeの検証は、Podのアドミッションステージでのみ発生し、実行中のPodでは発生しません。

以下は、LimitRangeを使用して作成できるポリシーの例です。

  • 8GiBのRAMと16コアのCPUの容量がある2ノードのクラスター上で、名前空間内のPodに対して、CPUには100mのrequestと最大500mのlimitの制約を課し、メモリーには200Miのrequestと600Miのlimitの制約を課す。
  • Spec内のrequestsにcpuやmemoryを指定せずに起動したコンテナに対して、CPUにはデフォルトで150mのlimitとrequestを、メモリーにはデフォルトで300Miのrequestをそれぞれ定義する。

名前空間のlimitの合計が、Podやコンテナのlimitの合計よりも小さくなる場合、リソースの競合が起こる可能性があります。その場合、コンテナやPodは作成されません。

LimitRangeに対する競合や変更は、すでに作成済みのリソースに対しては影響しません。

次の項目

より詳しい情報は、LimitRangerの設計ドキュメントを参照してください。

制限の使用例については、以下のページを読んでください。

3.9.2 - リソースクォータ

複数のユーザーやチームが決められた数のノードを持つクラスターを共有しているとき、1つのチームが公平に使えるリソース量を超えて使用するといった問題が出てきます。

リソースクォータはこの問題に対処するための管理者向けツールです。

ResourceQuotaオブジェクトによって定義されるリソースクォータは、名前空間ごとの総リソース消費を制限するための制約を提供します。リソースクォータは同じ名前空間のクラスター内でタイプごとに作成できるオブジェクト数や、名前空間内のリソースによって消費されるコンピュートリソースの総量を制限できます。

リソースクォータは下記のように働きます。

  • 異なる名前空間で異なるチームが存在するとき。現時点ではこれは自主的なものですが、将来的にはACLsを介してリソースクォータの設定を強制するように計画されています。
  • 管理者は各名前空間で1つのResourceQuotaを作成します。
  • ユーザーが名前空間内でリソース(Pod、Serviceなど)を作成し、クォータシステムがResourceQuotaによって定義されたハードリソースリミットを超えないことを保証するために、リソースの使用量をトラッキングします。
  • リソースの作成や更新がクォータの制約に違反しているとき、そのリクエストはHTTPステータスコード403 FORBIDDENで失敗し、違反した制約を説明するメッセージが表示されます。
  • cpumemoryといったコンピューターリソースに対するクォータが名前空間内で有効になっているとき、ユーザーはそれらの値に対するrequestslimitsを設定する必要があります。設定しないとクォータシステムがPodの作成を拒否します。 ヒント: コンピュートリソースの要求を設定しないPodに対してデフォルト値を強制するために、LimitRangerアドミッションコントローラーを使用してください。この問題を解決する例はwalkthroughで参照できます。

ResourceQuotaのオブジェクト名は、有効なDNSサブドメイン名である必要があります.

名前空間とクォータを使用して作成できるポリシーの例は以下の通りです。

  • 32GiB RAM、16コアのキャパシティーを持つクラスターで、Aチームに20GiB、10コアを割り当て、Bチームに10GiB、4コアを割り当て、将来の割り当てのために2GiB、2コアを予約しておく。
  • "testing"という名前空間に対して1コア、1GiB RAMの使用制限をかけ、"production"という名前空間には制限をかけない。

クラスターの総キャパシティーが、その名前空間のクォータの合計より少ない場合、リソースの競合が発生する場合があります。このとき、リソースの先着順で処理されます。

リソースの競合もクォータの変更も、作成済みのリソースには影響しません。

リソースクォータを有効にする

多くのKubernetesディストリビューションにおいてリソースクォータはデフォルトで有効になっています。APIサーバーで--enable-admission-plugins=の値にResourceQuotaが含まれるときに有効になります。

特定の名前空間にResourceQuotaがあるとき、そのリソースクォータはその名前空間に適用されます。

コンピュートリソースクォータ

特定の名前空間において、コンピュートリソースの合計に上限を設定できます。

下記のリソースタイプがサポートされています。

リソース名説明
limits.cpu停止していない状態の全てのPodで、CPUリミットの合計がこの値を超えることができません。
limits.memory停止していない状態の全てのPodで、メモリーの合計がこの値を超えることができません。
requests.cpu停止していない状態の全てのPodで、CPUリクエストの合計がこの値を超えることができません。
requests.memory停止していない状態の全てのPodで、メモリーリクエストの合計がこの値を超えることができません。
hugepages-<size>停止していない状態の全てのPodで, 指定されたサイズのHuge Pageリクエスト数がこの値を超えることができません。
cpurequests.cpuと同じ。
memoryrequests.memoryと同じ。

拡張リソースのためのリソースクォータ

上記で取り上げたリソースに加えて、Kubernetes v1.10において、拡張リソースのためのリソースクォータのサポートが追加されました。

拡張リソースに対するオーバーコミットが禁止されているのと同様に、リソースクォータで拡張リソース用にrequestslimitsの両方を指定しても意味がありません。現在、拡張リソースに対してはrequests.というプレフィックスのついたクォータアイテムのみ設定できます。

GPUリソースを例にすると、もしリソース名がnvidia.com/gpuで、ユーザーが名前空間内でリクエストされるGPUの上限を4に指定するとき、下記のようにリソースクォータを定義します。

  • requests.nvidia.com/gpu: 4

さらなる詳細はクォータの確認と設定を参照してください。

ストレージのリソースクォータ

特定の名前空間においてストレージリソースの総数に上限をかけることができます。

さらに、関連するストレージクラスに基づいて、ストレージリソースの消費量に上限をかけることもできます。

リソース名説明
requests.storage全てのPersistentVolumeClaimにおいて、ストレージのリクエストの合計がこの値を超えないようにします。
persistentvolumeclaims特定の名前空間内で作成可能なPersistentVolumeClaimの総数。
<storage-class-name>.storageclass.storage.k8s.io/requests.storageストレージクラス名<storage-class-name>に関連する全てのPersistentVolumeClaimにおいて、ストレージリクエストの合計がこの値を超えないようにします。
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaimsストレージクラス名<storage-class-name>に関連する全てのPersistentVolumeClaimにおいて、特定の名前空間内で作成可能なPersistentVolumeClaimの総数。

例えば、もし管理者がgoldストレージクラスをbronzeストレージクラスと分けてリソースクォータを設定するとき、管理者はリソースクォータを下記のように指定できます。

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

Kubernetes v1.8において、ローカルのエフェメラルストレージに対するリソースクォータのサポートがα版の機能として追加されました。

リソース名説明
requests.ephemeral-storage名前空間内の全てのPodで、ローカルのエフェメラルストレージのリクエストの合計がこの値を超えないようにします。
limits.ephemeral-storage名前空間内の全てのPodで、ローカルのエフェメラルストレージのリミットの合計がこの値を超えないようにします。
ephemeral-storagerequests.ephemeral-storageと同じ。

オブジェクト数に対するクォータ

下記のシンタックスを使用して、名前空間に紐づいた全ての標準であるリソースタイプの中の特定のリソースの総数に対するリソースクォータを設定できます。

  • count/<resource>.<group> コアでないグループのリソース用
  • count/<resource> コアグループのリソース用

オブジェクト数に対するクォータでユーザーが設定するリソースの例は下記の通りです。

  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/replicationcontrollers
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch

カスタムリソースに対して同じシンタックスを使用できます。例えば、example.comというAPIグループ内のwidgetsというカスタムリソースのリソースクォータを設定するにはcount/widgets.example.comと記述します。

count/*リソースクォータの使用において、オブジェクトがサーバーストレージに存在するときオブジェクトはクォータの計算対象となります。このようなタイプのリソースクォータはストレージリソース浪費の防止に有効です。例えば、もしSecretが大量に存在するとき、そのSecretリソースの総数に対してリソースクォータの制限をかけたい場合です。クラスター内でSecretが大量にあると、サーバーとコントローラーの起動を妨げることになります。適切に設定されていないCronJobから保護するためにジョブのクォータを設定できます。名前空間内で大量のJobを作成するCronJobは、サービスを利用不可能にする可能性があります。

また、限定されたリソースのセットにおいて汎用オブジェクトカウントのリソースクォータを実行可能です。

下記のタイプのリソースがサポートされています。

リソース名説明
configmaps名前空間内で存在可能なConfigMapの総数。
persistentvolumeclaims名前空間内で存在可能なPersistentVolumeClaimの総数。
pods名前空間内で存在可能な停止していないPodの総数。.status.phase in (Failed, Succeeded)がtrueのとき、Podは停止状態にあります。
replicationcontrollers名前空間内で存在可能なReplicationControlerの総数。
resourcequotas名前空間内で存在可能なResourceQuotaの総数。
services名前空間内で存在可能なServiceの総数。
services.loadbalancers名前空間内で存在可能なtype:LoadBalancerであるServiceの総数。
services.nodeports名前空間内で存在可能なtype:NodePortであるServiceの総数。
secrets名前空間内で存在可能なSecretの総数。

例えば、podsのリソースクォータはPodの総数をカウントし、特定の名前空間内で作成されたPodの総数の最大数を設定します。またユーザーが多くのPodを作成し、クラスターのPodのIPが枯渇する状況を避けるためにpodsのリソースクォータを名前空間に設定したい場合があります。

クォータのスコープについて

各リソースクォータには関連するscopeのセットを関連づけることができます。クォータは、列挙されたscopeの共通部分と一致する場合にのみリソースの使用量を計測します。

スコープがクォータに追加されると、サポートするリソースの数がスコープに関連するリソースに制限されます。許可されたセット以外のクォータ上でリソースを指定するとバリデーションエラーになります。

スコープ説明
Terminating.spec.activeDeadlineSeconds >= 0であるPodに一致します。
NotTerminating.spec.activeDeadlineSecondsがnilであるPodに一致します。
BestEffortベストエフォート型のサービス品質のPodに一致します。
NotBestEffortベストエフォート型のサービス品質でないPodに一致します。
PriorityClass指定された優先度クラスと関連付いているPodに一致します。

BestEffortスコープはリソースクォータを次のリソースに対するトラッキングのみに制限します:

  • pods

TerminatingNotTerminatingNotBestEffortPriorityClassスコープは、リソースクォータを次のリソースに対するトラッキングのみに制限します:

  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

同じクォータでTerminatingNotTerminatingの両方のスコープを指定することはできず、また同じクォータでBestEffortNotBestEffortの両方のスコープを指定することもできないことに注意してください。

scopeSelectoroperator フィールドにおいて下記の値をサポートしています。:

  • In
  • NotIn
  • Exists
  • DoesNotExist

scopeSelectorの定義においてscopeNameに下記のいずれかの値を使用する場合、operatorExistsを指定してください。

  • Terminating
  • NotTerminating
  • BestEffort
  • NotBestEffort

operatorInまたはNotInの場合、valuesフィールドには少なくとも1つの値が必要です。例えば以下のように記述します:

  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

operatorExistsまたはDoesNotExistの場合、valuesフィールドは指定しないでください

PriorityClass毎のリソースクォータ

FEATURE STATE: Kubernetes v1.17 [stable]

Podは特定の優先度で作成されます。リソースクォータのSpec内にあるscopeSelectorフィールドを使用して、Podの優先度に基づいてPodのシステムリソースの消費をコントロールできます。

リソースクォータのSpec内のscopeSelectorによってPodが選択されたときのみ、そのリソースクォータが一致し、消費されます。

リソースクォータがscopeSelectorフィールドを使用して優先度クラスに対してスコープされる場合、リソースクォータのオプジェクトは、次のリソースのみトラッキングするように制限されます:

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

この例ではリソースクォータのオブジェクトを作成し、特定の優先度を持つPodに一致させます。この例は下記のように動作します。

  • クラスター内のPodは"low"、"medium"、"high"の3つの優先度クラスのうち1つをもちます。
  • 1つのリソースクォータのオブジェクトは優先度毎に作成されます。

下記のYAMLをquota.ymlというファイルに保存します。

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "1000"
      memory: 200Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "10"
      memory: 20Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["low"]

kubectl createを実行してYAMLの内容を適用します。

kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

kubectl describe quotaを実行してUsedクォータが0であることを確認します。

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

プライオリティーが"high"であるPodを作成します。下記の内容をhigh-priority-pod.ymlというファイルに保存します。

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

kubectl createでマニフェストを適用します。

kubectl create -f ./high-priority-pod.yml

pods-highという名前のプライオリティーが"high"のクォータにおける"Used"項目の値が変更され、それ以外の2つの値は変更されていないことを確認してください。

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

リクエスト vs リミット

コンピュートリソースを分配する際に、各コンテナはCPUとメモリーそれぞれのリクエストとリミット値を指定します。クォータはそれぞれの値を設定できます。

クォータにrequests.cpurequests.memoryの値が指定されている場合は、コンテナはそれらのリソースに対する明示的な要求を行います。同様に、クォータにlimits.cpulimits.memoryの値が指定されている場合は、コンテナはそれらのリソースに対する明示的な制限を行います。

クォータの確認と設定

kubectlでは、クォータの作成、更新、確認をサポートしています。

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

また、kubectlはcount/<resource>.<group>というシンタックスを用いることにより、名前空間に依存した全ての主要なリソースに対するオブジェクト数のクォータをサポートしています。

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

クォータとクラスター容量

ResourceQuotaはクラスター容量に依存しません。またユニット数の絶対値で表されます。そのためクラスターにノードを追加したことにより、各名前空間が自動的により多くのリソースを消費するような機能が提供されるわけではありません

下記のようなより複雑なポリシーが必要な状況があります。

  • 複数チーム間でクラスターリソースの総量を分けあう。
  • 各テナントが必要な時にリソース使用量を増やせるようにするが、偶発的なリソースの枯渇を防ぐために上限を設定する。
  • 1つの名前空間に対してリソース消費の需要を検出し、ノードを追加し、クォータを増加させる。

このようなポリシーは、クォータの使用量の監視と、他のシグナルにしたがってクォータのハードの制限を調整する"コントローラー"を記述することにより、ResourceQuotaをビルディングブロックのように使用して実装できます。

リソースクォータは集約されたクラスターリソースを分割しますが、ノードに対しては何の制限も行わないことに注意して下さい。例: 複数の名前空間のPodは同一のノード上で稼働する可能性があります。

デフォルトで優先度クラスの消費を制限する

例えば"cluster-services"のように、条件に一致するクォータオブジェクトが存在する場合に限り、特定の優先度のPodを名前空間で許可することが望ましい場合があります。

このメカニズムにより、オペレーターは特定の高優先度クラスの使用を限られた数の名前空間に制限することができ、全ての名前空間でこれらの優先度クラスをデフォルトで使用することはできなくなります。

これを実施するには、kube-apiserver--admission-control-config-fileというフラグを使い、下記の設定ファイルに対してパスを渡す必要がります。

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

なお、"cluster-services"Podは、条件に一致するscopeSelectorを持つクォータオブジェクトが存在する名前空間でのみ許可されます。

    scopeSelector:
      matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

次の項目

3.10 - スケジューリングと退避

Kubernetesにおいてスケジューリングとは、稼働させたいPodをNodeにマッチさせ、kubeletが実行できるようにすることを指します。 退避とは、リソース不足のNodeで1つ以上のPodを積極的に停止させるプロセスです。

3.10.1 - Kubernetesのスケジューラー

Kubernetesにおいて、スケジューリング とは、KubeletPodを稼働させるためにNodeに割り当てることを意味します。

スケジューリングの概要

スケジューラーは新規に作成されたPodで、Nodeに割り当てられていないものを監視します。スケジューラーは発見した各Podのために、稼働させるべき最適なNodeを見つけ出す責務を担っています。そのスケジューラーは下記で説明するスケジューリングの原理を考慮に入れて、NodeへのPodの割り当てを行います。

Podが特定のNodeに割り当てられる理由を理解したい場合や、カスタムスケジューラーを自身で作ろうと考えている場合、このページはスケジューリングに関して学ぶのに役立ちます。

kube-scheduler

kube-schedulerはKubernetesにおけるデフォルトのスケジューラーで、コントロールプレーンの一部分として稼働します。 kube-schedulerは、もし希望するのであれば自分自身でスケジューリングのコンポーネントを実装でき、それを代わりに使用できるように設計されています。

kube-schedulerは、新規に作成された各Podや他のスケジューリングされていないPodを稼働させるために最適なNodeを選択します。 しかし、Pod内の各コンテナにはそれぞれ異なるリソースの要件があり、各Pod自体にもそれぞれ異なる要件があります。そのため、既存のNodeは特定のスケジューリング要求によってフィルターされる必要があります。

クラスター内でPodに対する割り当て要求を満たしたNodeは 割り当て可能 なNodeと呼ばれます。 もし適切なNodeが一つもない場合、スケジューラーがNodeを割り当てることができるまで、そのPodはスケジュールされずに残ります。

スケジューラーはPodに対する割り当て可能なNodeをみつけ、それらの割り当て可能なNodeにスコアをつけます。その中から最も高いスコアのNodeを選択し、Podに割り当てるためのいくつかの関数を実行します。 スケジューラーは binding と呼ばれる処理中において、APIサーバーに対して割り当てが決まったNodeの情報を通知します。

スケジューリングを決定する上で考慮が必要な要素としては、個別または複数のリソース要求や、ハードウェア/ソフトウェアのポリシー制約、affinityやanti-affinityの設定、データの局所性や、ワークロード間での干渉などが挙げられます。

kube-schedulerによるスケジューリング

kube-schedulerは2ステップの操作によってPodに割り当てるNodeを選択します。

  1. フィルタリング

  2. スコアリング

フィルタリング ステップでは、Podに割り当て可能なNodeのセットを探します。例えばPodFitsResourcesフィルターは、Podのリソース要求を満たすのに十分なリソースをもつNodeがどれかをチェックします。このステップの後、候補のNodeのリストは、要求を満たすNodeを含みます。 たいてい、リストの要素は複数となります。もしこのリストが空の場合、そのPodはスケジュール可能な状態とはなりません。

スコアリング ステップでは、Podを割り当てるのに最も適したNodeを選択するために、スケジューラーはリストの中のNodeをランク付けします。 スケジューラーは、フィルタリングによって選ばれた各Nodeに対してスコアを付けます。このスコアはアクティブなスコア付けのルールに基づいています。

最後に、kube-schedulerは最も高いランクのNodeに対してPodを割り当てます。もし同一のスコアのNodeが複数ある場合は、kube-schedulerがランダムに1つ選択します。

スケジューラーのフィルタリングとスコアリングの動作に関する設定には2つのサポートされた手法があります。

  1. スケジューリングポリシー は、フィルタリングのための Predicates とスコアリングのための Priorities の設定することができます。
  2. スケジューリングプロファイルは、QueueSortFilterScoreBindReservePermitやその他を含む異なるスケジューリングの段階を実装するプラグインを設定することができます。kube-schedulerを異なるプロファイルを実行するように設定することもできます。

次の項目

3.10.2 - Node上へのPodのスケジューリング

Podを特定のNodeで実行するように 制限 したり、特定のNodeで実行することを 優先 させたりといった制約をかけることができます。 これを実現するためにはいくつかの方法がありますが、推奨されている方法は、すべてラベルセレクターを使用して選択を容易にすることです。 多くの場合、このような制約を設定する必要はなく、スケジューラーが自動的に妥当な配置を行います(例えば、Podを複数のNodeに分散させ、空きリソースが十分でないNodeにPodを配置しないようにすることができます)。 しかし、例えばSSDが接続されているNodeにPodが配置されるようにしたり、多くの通信を行う2つの異なるサービスのPodを同じアベイラビリティーゾーンに配置したりする等、どのNodeに配置するかを制御したい状況もあります。

Kubernetesが特定のPodの配置場所を選択するために、以下の方法があります:

Nodeラベル

他の多くのKubernetesオブジェクトと同様に、Nodeにもラベルがあります。手動でラベルを付けることができます。 また、Kubernetesはクラスター内のすべてのNodeに対し、いくつかの標準ラベルを付けます。Nodeラベルの一覧についてはよく使われるラベル、アノテーションとtaintを参照してください。

Nodeの分離/制限

Nodeにラベルを追加することで、Podを特定のNodeまたはNodeグループ上でのスケジューリングの対象にすることができます。この機能を使用すると、特定のPodが一定の独立性、安全性、または規制といった属性を持ったNode上でのみ実行されるようにすることができます。

Node分離するのにラベルを使用する場合、kubeletが修正できないラベルキーを選択してください。 これにより、侵害されたNodeが自身でそれらのラベルを設定することで、スケジューラーがそのNodeにワークロードをスケジュールしてしまうのを防ぐことができます。

NodeRestrictionアドミッションプラグインは、kubeletがnode-restriction.kubernetes.io/というプレフィックスを持つラベルを設定または変更するのを防ぎます。

ラベルプレフィックスをNode分離に利用するには:

  1. Node認可を使用していることと、NodeRestriction アドミッションプラグインが 有効 になっていることを確認します。
  2. node-restriction.kubernetes.io/プレフィックスを持つラベルをNodeに追加し、 nodeSelectorでそれらのラベルを使用します。 例えば、example.com.node-restriction.kubernetes.io/fips=trueexample.com.node-restriction.kubernetes.io/pci-dss=trueなどです。

nodeSelector

nodeSelectorは、Node選択制約の中で最もシンプルな推奨形式です。 Podのspec(仕様)にnodeSelectorフィールドを追加することで、ターゲットNodeが持つべきNodeラベルを指定できます。 Kubernetesは指定された各ラベルを持つNodeにのみ、Podをスケジューリングします。

詳しい情報についてはPodをNodeに割り当てるを参照してください。

アフィニティとアンチアフィニティ

nodeSelectorはPodを特定のラベルが付与されたNodeに制限する最も簡単な方法です。 アフィニティとアンチアフィニティでは、定義できる制約の種類が拡張されています。 アフィニティとアンチアフィニティのメリットは以下の通りです。

  • アフィニティとアンチアフィニティで使われる言語は、より表現力が豊かです。nodeSelectorは指定されたラベルを全て持つNodeを選択するだけです。アフィニティとアンチアフィニティは選択ロジックをより細かく制御することができます。
  • ルールが柔軟であったり優先での指定ができたりするため、一致するNodeが見つからない場合でも、スケジューラーはPodをスケジュールします。
  • Node自体のラベルではなく、Node(または他のトポロジカルドメイン)上で稼働している他のPodのラベルを使ってPodを制約することができます。これにより、Node上にどのPodを共存させるかのルールを定義することができます。

アフィニティ機能は、2種類のアフィニティで構成されています:

  • NodeアフィニティnodeSelectorフィールドと同様に機能しますが、より表現力が豊かで、より柔軟にルールを指定することができます。
  • Pod間アフィニティとアンチアフィニティは、他のPodのラベルを元に、Podを制約することができます。

Nodeアフィニティ

Nodeアフィニティは概念的には、NodeのラベルによってPodがどのNodeにスケジュールされるかを制限するnodeSelectorと同様です。

Nodeアフィニティには2種類あります:

  • requiredDuringSchedulingIgnoredDuringExecution: スケジューラーは、ルールが満たされない限り、Podをスケジュールすることができません。これはnodeSelectorと同じように機能しますが、より表現力豊かな構文になっています。
  • preferredDuringSchedulingIgnoredDuringExecution: スケジューラーは、対応するルールを満たすNodeを探そうとします。 一致するNodeが見つからなくても、スケジューラーはPodをスケジュールします。

Podのspec(仕様)にある.spec.affinity.nodeAffinityフィールドを使用して、Nodeアフィニティを指定することができます。

例えば、次のようなPodのspec(仕様)を考えてみましょう:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - antarctica-east1
            - antarctica-west1
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

この例では、以下のルールが適用されます:

  • Nodeにはtopology.kubernetes.io/zoneをキーとするラベルが必要で、そのラベルの値はantarctica-east1またはantarctica-west1のいずれかでなければなりません。
  • Nodeにはキー名がanother-node-label-keyで、値がanother-node-label-valueのラベルを持つことが望ましいです。

operatorフィールドを使用して、Kubernetesがルールを解釈する際に使用できる論理演算子を指定することができます。InNotInExistsDoesNotExistGtLtが使用できます。

NotInDoesNotExistを使って、Nodeのアンチアフィニティ動作を定義することができます。また、NodeのTaintを使用して、特定のNodeからPodをはじくこともできます。

詳細についてはNodeアフィニティを利用してPodをNodeに割り当てるを参照してください。

Nodeアフィニティの重み

preferredDuringSchedulingIgnoredDuringExecutionアフィニティタイプの各インスタンスに、1から100の範囲のweightを指定できます。 Podの他のスケジューリング要件をすべて満たすNodeを見つけると、スケジューラーはそのNodeが満たすすべての優先ルールを繰り返し実行し、対応する式のweight値を合計に加算します。

最終的な合計は、そのNodeの他の優先度関数のスコアに加算されます。合計スコアが最も高いNodeが、スケジューラーがPodのスケジューリングを決定する際に優先されます。

例えば、次のようなPodのspec(仕様)を考えてみましょう:

apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-anti-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:2.0

preferredDuringSchedulingIgnoredDuringExecutionルールにマッチするNodeとして、一つはlabel-1:key-1ラベル、もう一つはlabel-2:key-2ラベルの2つの候補がある場合、スケジューラーは各Nodeのweightを考慮し、その重みとNodeの他のスコアを加え、最終スコアが最も高いNodeにPodをスケジューリングします。

スケジューリングプロファイルごとのNodeアフィニティ

FEATURE STATE: Kubernetes v1.20 [beta]

複数のスケジューリングプロファイルを設定する場合、プロファイルにNodeアフィニティを関連付けることができます。これは、プロファイルが特定のNode群にのみ適用される場合に便利です。スケジューラーの設定にあるNodeAffinityプラグインargsフィールドにaddedAffinityを追加すると実現できます。例えば:

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

addedAffinityは、Podの仕様(spec)で指定されたNodeアフィニティに加え、.spec.schedulerNamefoo-schedulerに設定したすべてのPodに適用されます。つまり、Podにマッチするためには、NodeはaddedAffinityとPodの.spec.NodeAffinityを満たす必要があるのです。

addedAffinityはエンドユーザーには見えないので、その動作はエンドユーザーにとって予期しないものになる可能性があります。スケジューラープロファイル名と明確な相関関係のあるNodeラベルを使用すべきです。

Pod間のアフィニティとアンチアフィニティ

Pod間のアフィニティとアンチアフィニティは、Nodeのラベルではなく、すでにNode上で稼働しているPodのラベルに従って、PodがどのNodeにスケジュールされるかを制限できます。

XはNodeや、ラック、クラウドプロバイダーのゾーンやリージョン等を表すトポロジードメインで、YはKubernetesが満たそうとするルールである場合、Pod間のアフィニティとアンチアフィニティのルールは、"XにてルールYを満たすPodがすでに稼働している場合、このPodもXで実行すべき(アンチアフィニティの場合はすべきではない)"という形式です。

これらのルール(Y)は、オプションの関連する名前空間のリストを持つラベルセレクターで表現されます。PodはKubernetesの名前空間オブジェクトであるため、Podラベルも暗黙的に名前空間を持ちます。Kubernetesが指定された名前空間でラベルを探すため、Podラベルのラベルセレクターは、名前空間を指定する必要があります。

トポロジードメイン(X)はtopologyKeyで表現され、システムがドメインを示すために使用するNodeラベルのキーになります。具体例はよく知られたラベル、アノテーションとTaintを参照してください。

Pod間のアフィニティとアンチアフィニティの種類

Nodeアフィニティと同様に、Podアフィニティとアンチアフィニティにも下記の2種類があります:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

例えば、requiredDuringSchedulingIgnoredDuringExecutionアフィニティを使用して、2つのサービスのPodはお互いのやり取りが多いため、同じクラウドプロバイダーゾーンに併置するようにスケジューラーに指示することができます。 同様に、preferredDuringSchedulingIgnoredDuringExecutionアンチアフィニティを使用して、あるサービスのPodを複数のクラウドプロバイダーゾーンに分散させることができます。

Pod間アフィニティを使用するには、Pod仕様(spec)のaffinity.podAffinityフィールドで指定します。Pod間アンチアフィニティを使用するには、Pod仕様(spec)のaffinity.podAntiAffinityフィールドで指定します。

Podアフィニティ使用例

次のようなPod仕様(spec)を考えてみましょう:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:2.0

この例では、PodアフィニティルールとPodアンチアフィニティルールを1つずつ定義しています。 Podアフィニティルールは"ハード"なrequiredDuringSchedulingIgnoredDuringExecutionを使用し、アンチアフィニティルールは"ソフト"なpreferredDuringSchedulingIgnoredDuringExecutionを使用しています。

アフィニティルールは、スケジューラーがNodeにPodをスケジュールできるのは、そのNodeが、security=S1ラベルを持つ1つ以上の既存のPodと同じゾーンにある場合のみであることを示しています。より正確には、現在Podラベルsecurity=S1を持つPodが1つ以上あるNodeが、そのゾーン内に少なくとも1つ存在する限り、スケジューラーはtopology.kubernetes.io/zone=Vラベルを持つNodeにPodを配置しなければなりません。

アンチアフィニティルールは、security=S2ラベルを持つ1つ以上のPodと同じゾーンにあるNodeには、スケジューラーがPodをスケジュールしないようにすることを示しています。より正確には、PodラベルSecurity=S2を持つPodが稼働している他のNodeが、同じゾーン内に存在する場合、スケジューラーはtopology.kubernetes.io/zone=Rラベルを持つNodeにはPodを配置しないようにしなければなりません。

Podアフィニティとアンチアフィニティの使用例についてもっと知りたい方はデザイン案を参照してください。

Podアフィニティとアンチアフィニティのoperatorフィールドで使用できるのは、InNotInExistsDoesNotExistです。

原則として、topologyKeyには任意のラベルキーが指定できますが、パフォーマンスやセキュリティの観点から、以下の例外があります:

  • Podアフィニティとアンチアフィニティでは、requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution内のどちらも、topologyKeyフィールドが空であることは許可されていません。
  • PodアンチアフィニティルールのrequiredDuringSchedulingIgnoredDuringExecutionでは、アドミッションコントローラーLimitPodHardAntiAffinityTopologytopologyKeykubernetes.io/hostnameに制限しています。アドミッションコントローラーを修正または無効化すると、トポロジーのカスタマイズができるようになります。

labelSelectortopologyKeyに加え、labelSelectortopologyKeyと同じレベルのnamespacesフィールドを使用して、labelSelectorが合致すべき名前空間のリストを任意に指定することができます。省略または空の場合、namespacesがデフォルトで、アフィニティとアンチアフィニティが定義されたPodの名前空間に設定されます。

名前空間セレクター

FEATURE STATE: Kubernetes v1.24 [stable]

namespaceSelectorを使用し、ラベルで名前空間の集合に対して検索することによって、名前空間を選択することができます。 アフィニティ項はnamespaceSelectornamespacesフィールドによって選択された名前空間に適用されます。 要注意なのは、空のnamespaceSelector({})はすべての名前空間にマッチし、nullまたは空のnamespacesリストとnullのnamespaceSelectorは、ルールが定義されているPodの名前空間にマッチします。

実践的なユースケース

Pod間アフィニティとアンチアフィニティは、ReplicaSet、StatefulSet、Deploymentなどのより高レベルなコレクションと併せて使用するとさらに有用です。これらのルールにより、ワークロードのセットが同じ定義されたトポロジーに併置されるように設定できます。たとえば、2つの関連するPodを同じNodeに配置することが好ましい場合です。

例えば、3つのNodeで構成されるクラスターを想像してください。そのクラスターを使用してウェブアプリケーションを実行し、さらにインメモリーキャッシュ(Redisなど)を使用します。この例では、ウェブアプリケーションとメモリーキャッシュの間のレイテンシーは実用的な範囲の低さも想定しています。Pod間アフィニティやアンチアフィニティを使って、ウェブサーバーとキャッシュをなるべく同じ場所に配置することができます。

以下のRedisキャッシュのDeploymentの例では、各レプリカはラベルapp=storeが付与されています。podAntiAffinityルールは、app=storeラベルを持つ複数のレプリカを単一Nodeに配置しないよう、スケジューラーに指示します。これにより、各キャッシュが別々のNodeに作成されます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

次のウェブサーバーのDeployment例では、app=web-storeラベルが付与されたレプリカを作成します。Podアフィニティルールは、各レプリカを、app=storeラベルが付与されたPodを持つNodeに配置するようスケジューラーに指示します。Podアンチアフィニティルールは、1つのNodeに複数のapp=web-storeサーバーを配置しないようにスケジューラーに指示します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

上記2つのDeploymentが生成されると、以下のようなクラスター構成になり、各ウェブサーバーはキャッシュと同位置に、3つの別々のNodeに配置されます。

node-1node-2node-3
webserver-1webserver-2webserver-3
cache-1cache-2cache-3

全体的な効果として、各キャッシュインスタンスは、同じNode上で実行している単一のクライアントによってアクセスされる可能性が高いです。この方法は、スキュー(負荷の偏り)とレイテンシーの両方を最小化することを目的としています。

Podアンチアフィニティを使用する理由は他にもあります。 この例と同様の方法で、アンチアフィニティを用いて高可用性を実現したStatefulSetの使用例はZooKeeperチュートリアルを参照してください。

nodeName

nodeNameはアフィニティやnodeSelectorよりも直接的なNode選択形式になります。nodeNameはPod仕様(spec)内のフィールドです。nodeNameフィールドが空でない場合、スケジューラーはPodを考慮せずに、指定されたNodeにあるkubeletがそのNodeにPodを配置しようとします。nodeNameを使用すると、nodeSelectorやアフィニティおよびアンチアフィニティルールを使用するよりも優先されます。

nodeNameを使ってNodeを選択する場合の制約は以下の通りです:

  • 指定されたNodeが存在しない場合、Podは実行されず、場合によっては自動的に削除されることがあります。
  • 指定されたNodeがPodを収容するためのリソースを持っていない場合、Podの起動は失敗し、OutOfmemoryやOutOfcpuなどの理由が表示されます。
  • クラウド環境におけるNode名は、常に予測可能で安定したものではありません。

以下は、nodeNameフィールドを使用したPod仕様(spec)の例になります:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

上記のPodはkube-01というNodeでのみ実行されます。

Podトポロジー分散制約

トポロジー分散制約 を使って、リージョン、ゾーン、Nodeなどの障害ドメイン間、または定義したその他のトポロジードメイン間で、クラスター全体にどのようにPodを分散させるかを制御することができます。これにより、パフォーマンス、予想される可用性、または全体的な使用率を向上させることができます。

詳しい仕組みについては、トポロジー分散制約を参照してください。

次の項目

3.10.3 - Podのオーバーヘッド

FEATURE STATE: Kubernetes v1.24 [stable]

PodをNode上で実行する時に、Pod自身は大量のシステムリソースを消費します。これらのリソースは、Pod内のコンテナ(群)を実行するために必要なリソースとして追加されます。Podのオーバーヘッドは、コンテナの要求と制限に加えて、Podのインフラストラクチャで消費されるリソースを計算するための機能です。

Kubernetesでは、PodのRuntimeClassに関連するオーバーヘッドに応じて、アドミッション時にPodのオーバーヘッドが設定されます。

Podのオーバーヘッドを有効にした場合、Podのスケジューリング時にコンテナのリソース要求の合計に加えて、オーバーヘッドも考慮されます。同様に、Kubeletは、Podのcgroupのサイズ決定時およびPodの退役の順位付け時に、Podのオーバーヘッドを含めます。

Podのオーバーヘッドの有効化 

クラスター全体でPodOverheadフィーチャーゲートが有効になっていること(1.18時点ではデフォルトでオンになっています)と、overheadフィールドを定義するRuntimeClassが利用されていることを確認する必要があります。

使用例

Podのオーバーヘッド機能を使用するためには、overheadフィールドが定義されたRuntimeClassが必要です。例として、仮想マシンとゲストOSにPodあたり約120MiBを使用する仮想化コンテナランタイムで、次のようなRuntimeClassを定義できます。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata-fc
handler: kata-fc
overhead:
  podFixed:
    memory: "120Mi"
    cpu: "250m"

kata-fcRuntimeClassハンドラーを指定して作成されたワークロードは、リソースクォータの計算や、Nodeのスケジューリング、およびPodのcgroupのサイズ決定にメモリーとCPUのオーバーヘッドが考慮されます。

次のtest-podのワークロードの例を実行するとします。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  runtimeClassName: kata-fc
  containers:
  - name: busybox-ctr
    image: busybox:1.28
    stdin: true
    tty: true
    resources:
      limits:
        cpu: 500m
        memory: 100Mi
  - name: nginx-ctr
    image: nginx
    resources:
      limits:
        cpu: 1500m
        memory: 100Mi

アドミッション時、RuntimeClassアドミッションコントローラーは、RuntimeClass内に記述されたオーバーヘッドを含むようにワークロードのPodSpecを更新します。もし既にPodSpec内にこのフィールドが定義済みの場合、そのPodは拒否されます。この例では、RuntimeClassの名前しか指定されていないため、アドミッションコントローラーはオーバーヘッドを含むようにPodを変更します。

RuntimeClassのアドミッションコントローラーの後、更新されたPodSpecを確認できます。

kubectl get pod test-pod -o jsonpath='{.spec.overhead}'

出力は次の通りです:

map[cpu:250m memory:120Mi]

ResourceQuotaが定義されている場合、コンテナ要求の合計とオーバーヘッドフィールドがカウントされます。

kube-schedulerが新しいPodを実行すべきNodeを決定する際、スケジューラーはそのPodのオーバーヘッドと、そのPodに対するコンテナ要求の合計を考慮します。この例だと、スケジューラーは、要求とオーバーヘッドを追加し、2.25CPUと320MiBのメモリを持つNodeを探します。

PodがNodeにスケジュールされると、そのNodeのkubeletはPodのために新しいcgroupを生成します。基盤となるコンテナランタイムがコンテナを作成するのは、このPod内です。

リソースにコンテナごとの制限が定義されている場合(制限が定義されているGuaranteed QoSまたはBustrable QoS)、kubeletはそのリソース(CPUはcpu.cfs_quota_us、メモリはmemory.limit_in_bytes)に関連するPodのcgroupの上限を設定します。この上限は、コンテナの制限とPodSpecで定義されたオーバーヘッドの合計に基づきます。

CPUについては、PodがGuaranteedまたはBurstable QoSの場合、kubeletはコンテナの要求の合計とPodSpecに定義されたオーバーヘッドに基づいてcpu.shareを設定します。

次の例より、ワークロードに対するコンテナの要求を確認できます。

kubectl get pod test-pod -o jsonpath='{.spec.containers[*].resources.limits}'

コンテナの要求の合計は、CPUは2000m、メモリーは200MiBです。

map[cpu: 500m memory:100Mi] map[cpu:1500m memory:100Mi]

Nodeで観測される値と比較してみましょう。

kubectl describe node | grep test-pod -B2

出力では、2250mのCPUと320MiBのメモリーが要求されており、Podのオーバーヘッドが含まれていることが分かります。

  Namespace    Name       CPU Requests  CPU Limits   Memory Requests  Memory Limits  AGE
  ---------    ----       ------------  ----------   ---------------  -------------  ---
  default      test-pod   2250m (56%)   2250m (56%)  320Mi (1%)       320Mi (1%)     36m

Podのcgroupの制限を確認

ワークロードで実行中のNode上にある、Podのメモリーのcgroupを確認します。次に示す例では、CRI互換のコンテナランタイムのCLIを提供するNodeでcrictlを使用しています。これはPodのオーバーヘッドの動作を示すための高度な例であり、ユーザーがNode上で直接cgroupsを確認する必要はありません。

まず、特定のNodeで、Podの識別子を決定します。

# PodがスケジュールされているNodeで実行
POD_ID="$(sudo crictl pods --name test-pod -q)"

ここから、Podのcgroupのパスが決定します。

# PodがスケジュールされているNodeで実行
sudo crictl inspectp -o=json $POD_ID | grep cgroupsPath

結果のcgroupパスにはPodのポーズ中コンテナも含まれます。Podレベルのcgroupは1つ上のディレクトリです。

  "cgroupsPath": "/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/7ccf55aee35dd16aca4189c952d83487297f3cd760f1bbf09620e206e7d0c27a"

今回のケースでは、Podのcgroupパスは、kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2となります。メモリーのPodレベルのcgroupの設定を確認しましょう。

# PodがスケジュールされているNodeで実行
# また、Podに割り当てられたcgroupと同じ名前に変更
 cat /sys/fs/cgroup/memory/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/memory.limit_in_bytes

予想通り320MiBです。

335544320

Observability

Podのオーバヘッドが利用されているタイミングを特定し、定義されたオーバーヘッドで実行されているワークロードの安定性を観察するため、kube-state-metricsにはkube_pod_overheadというメトリクスが用意されています。この機能はv1.9のkube-state-metricsでは利用できませんが、次のリリースで期待されています。それまでは、kube-state-metricsをソースからビルドする必要があります。

次の項目

3.10.4 - TaintとToleration

NodeアフィニティPodの属性であり、あるNode群を引きつけます(優先条件または必須条件)。反対に taint はNodeがある種のPodを排除できるようにします。

toleration はPodに適用され、一致するtaintが付与されたNodeへPodがスケジューリングされることを認めるものです。ただしそのNodeへ必ずスケジューリングされるとは限りません。

taintとtolerationは組になって機能し、Podが不適切なNodeへスケジューリングされないことを保証します。taintはNodeに一つまたは複数個付与することができます。これはそのNodeがtaintを許容しないPodを受け入れるべきではないことを示します。

コンセプト

Nodeにtaintを付与するにはkubectl taintコマンドを使用します。 例えば、次のコマンドは

kubectl taint nodes node1 key1=value1:NoSchedule

node1にtaintを設定します。このtaintのキーはkey1、値はvalue1、taintの効果はNoScheduleです。 これはnode1にはPodに合致するtolerationがなければスケジューリングされないことを意味します。

上記のコマンドで付与したtaintを外すには、下記のコマンドを使います。

kubectl taint nodes node1 key1=value1:NoSchedule-

PodのtolerationはPodSpecの中に指定します。下記のtolerationはどちらも、上記のkubectl taintコマンドで追加したtaintと合致するため、どちらのtolerationが設定されたPodもnode1へスケジューリングされることができます。

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

tolerationを設定したPodの例を示します。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

operatorのデフォルトはEqualです。

tolerationがtaintと合致するのは、keyeffectが同一であり、さらに下記の条件のいずれかを満たす場合です。

  • operatorExistsvalueを指定すべきでない場合)
  • operatorEqualであり、かつvalueが同一である場合

上記の例ではeffectNoScheduleを指定しました。代わりに、effectPreferNoScheduleを指定することができます。 これはNoScheduleの「ソフトな」バージョンであり、システムはtaintに対応するtolerationが設定されていないPodがNodeへ配置されることを避けようとしますが、必須の条件とはしません。3つ目のeffectの値としてNoExecuteがありますが、これについては後述します。

同一のNodeに複数のtaintを付与することや、同一のPodに複数のtolerationを設定することができます。 複数のtaintやtolerationが設定されている場合、Kubernetesはフィルタのように扱います。最初はNodeの全てのtaintがある状態から始め、Podが対応するtolerationを持っているtaintは無視され外されていきます。無視されずに残ったtaintが効果を及ぼします。 具体的には、

  • effect NoScheduleのtaintが無視されず残った場合、KubernetesはそのPodをNodeへスケジューリングしません。
  • effect NoScheduleのtaintは残らず、effect PreferNoScheduleのtaintは残った場合、KubernetesはそのNodeへのスケジューリングをしないように試みます。
  • effect NoExecuteのtaintが残った場合、既に稼働中のPodはそのNodeから排除され、まだ稼働していないPodはスケジューリングされないようになります。

例として、下記のようなtaintが付与されたNodeを考えます。

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

Podには2つのtolerationが設定されています。

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

この例では、3つ目のtaintと合致するtolerationがないため、PodはNodeへはスケジューリングされません。 しかし、これらのtaintが追加された時点で、そのNodeでPodが稼働していれば続けて稼働することが可能です。 これは、Podのtolerationと合致しないtaintは3つあるtaintのうちの3つ目のtaintのみであり、それがNoScheduleであるためです。

一般に、effect NoExecuteのtaintがNodeに追加されると、合致するtolerationが設定されていないPodは即時にNodeから排除され、合致するtolerationが設定されたPodが排除されることは決してありません。 しかし、effectNoExecuteに対するtolerationはtolerationSecondsフィールドを任意で指定することができ、これはtaintが追加された後にそのNodeにPodが残る時間を示します。例えば、

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

この例のPodが稼働中で、対応するtaintがNodeへ追加された場合、PodはそのNodeに3600秒残り、その後排除されます。仮にtaintがそれよりも前に外された場合、Podは排除されません。

ユースケースの例

taintとtolerationは、実行されるべきではないNodeからPodを遠ざけたり、排除したりするための柔軟な方法です。いくつかのユースケースを示します。

  • 専有Node: あるNode群を特定のユーザーに専有させたい場合、そのNode群へtaintを追加し(kubectl taint nodes nodename dedicated=groupName:NoSchedule) 対応するtolerationをPodへ追加します(これを実現する最も容易な方法はカスタム アドミッションコントローラーを書くことです)。 tolerationが設定されたPodはtaintの設定された(専有の)Nodeと、クラスターにあるその他のNodeの使用が認められます。もしPodが必ず専有Nodeのみを使うようにしたい場合は、taintと同様のラベルをそのNode群に設定し(例: dedicated=groupName)、アドミッションコントローラーはNodeアフィニティを使ってPodがdedicated=groupNameのラベルの付いたNodeへスケジューリングすることが必要であるということも設定する必要があります。

  • 特殊なハードウェアを備えるNode: クラスターの中の少数のNodeが特殊なハードウェア(例えばGPU)を備える場合、そのハードウェアを必要としないPodがスケジューリングされないようにして、後でハードウェアを必要とするPodができたときの余裕を確保したいことがあります。 これは特殊なハードウェアを持つNodeにtaintを追加(例えば kubectl taint nodes nodename special=true:NoSchedule または kubectl taint nodes nodename special=true:PreferNoSchedule)して、ハードウェアを使用するPodに対応するtolerationを追加することで可能です。 専有Nodeのユースケースと同様に、tolerationを容易に適用する方法はカスタム アドミッションコントローラーを使うことです。 例えば、特殊なハードウェアを表すために拡張リソース を使い、ハードウェアを備えるNodeに拡張リソースの名称のtaintを追加して、 拡張リソースtoleration アドミッションコントローラーを実行することが推奨されます。Nodeにはtaintが付与されているため、tolerationのないPodはスケジューリングされません。しかし拡張リソースを要求するPodを作成しようとすると、拡張リソースtoleration アドミッションコントローラーはPodに自動的に適切なtolerationを設定し、Podはハードウェアを備えるNodeへスケジューリングされます。 これは特殊なハードウェアを備えたNodeではそれを必要とするPodのみが稼働し、Podに対して手作業でtolerationを追加しなくて済むようにします。

  • taintを基にした排除: Nodeに問題が起きたときにPodごとに排除する設定を行うことができます。次のセクションにて説明します。

taintを基にした排除

FEATURE STATE: Kubernetes v1.18 [stable]

上述したように、effect NoExecuteのtaintはNodeで実行中のPodに次のような影響を与えます。

  • 対応するtolerationのないPodは即座に除外される
  • 対応するtolerationがあり、それにtolerationSecondsが指定されていないPodは残り続ける
  • 対応するtolerationがあり、それにtolerationSecondsが指定されているPodは指定された間、残される

Nodeコントローラーは特定の条件を満たす場合に自動的にtaintを追加します。 組み込まれているtaintは下記の通りです。

  • node.kubernetes.io/not-ready: Nodeの準備ができていない場合。これはNodeCondition ReadyFalseである場合に対応します。
  • node.kubernetes.io/unreachable: NodeがNodeコントローラーから到達できない場合。これはNodeConditionReadyUnknownの場合に対応します。
  • node.kubernetes.io/out-of-disk: Nodeのディスクの空きがない場合。
  • node.kubernetes.io/memory-pressure: Nodeのメモリーが不足している場合。
  • node.kubernetes.io/disk-pressure: Nodeのディスクが不足している場合。
  • node.kubernetes.io/network-unavailable: Nodeのネットワークが利用できない場合。
  • node.kubernetes.io/unschedulable: Nodeがスケジューリングできない場合。
  • node.cloudprovider.kubernetes.io/uninitialized: kubeletが外部のクラウド事業者により起動されたときに設定されるtaintで、このNodeは利用不可能であることを示します。cloud-controller-managerによるコントローラーがこのNodeを初期化した後にkubeletはこのtaintを外します。

Nodeから追い出すときには、Nodeコントローラーまたはkubeletは関連するtaintをNoExecute効果の状態で追加します。 不具合のある状態から通常の状態へ復帰した場合は、kubeletまたはNodeコントローラーは関連するtaintを外すことができます。

PodにtolerationSecondsを指定することで不具合があるか応答のないNodeに残る時間を指定することができます。

例えば、ローカルの状態を多数持つアプリケーションとネットワークが分断された場合を考えます。ネットワークが復旧して、Podを排除しなくて済むことを見込んで、長時間Nodeから排除されないようにしたいこともあるでしょう。 この場合Podに設定するtolerationは次のようになります。

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

DaemonSetのPodは次のtaintに対してNoExecuteのtolerationがtolerationSecondsを指定せずに設定されます。

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

これはDaemonSetのPodはこれらの問題によって排除されないことを保証します。

条件によるtaintの付与

NodeのライフサイクルコントローラーはNodeの状態に応じてNoSchedule効果のtaintを付与します。 スケジューラーはNodeの状態ではなく、taintを確認します。 Nodeに何がスケジューリングされるかは、そのNodeの状態に影響されないことを保証します。ユーザーは適切なtolerationをPodに付与することで、どの種類のNodeの問題を無視するかを選ぶことができます。

DaemonSetのコントローラーは、DaemonSetが中断されるのを防ぐために自動的に次のNoScheduletolerationを全てのDaemonSetに付与します。

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/out-of-disk (重要なPodのみ)
  • node.kubernetes.io/unschedulable (1.10またはそれ以降)
  • node.kubernetes.io/network-unavailable (ホストネットワークのみ)

これらのtolerationを追加することは後方互換性を保証します。DaemonSetに任意のtolerationを加えることもできます。

次の項目

3.10.5 - スケジューリングフレームワーク

FEATURE STATE: Kubernetes v1.19 [stable]

スケジューリングフレームワークはKubernetesのスケジューラーに対してプラグイン可能なアーキテクチャです。 このアーキテクチャは、既存のスケジューラーに新たに「プラグイン」としてAPI群を追加するもので、プラグインはスケジューラー内部にコンパイルされます。このAPI群により、スケジューリングの「コア」の軽量かつ保守しやすい状態に保ちながら、ほとんどのスケジューリングの機能をプラグインとして実装することができます。このフレームワークの設計に関する技術的な情報についてはこちらのスケジューリングフレームワークの設計提案をご覧ください。

フレームワークのワークフロー

スケジューリングフレームワークは、いくつかの拡張点を定義しています。スケジューラープラグインは、1つ以上の拡張点で呼び出されるように登録します。これらのプラグインの中には、スケジューリングの決定を変更できるものから、単に情報提供のみを行うだけのものなどがあります。

この1つのPodをスケジュールしようとする各動作はScheduling CycleBinding Cycleの2つのフェーズに分けられます。

Scheduling Cycle & Binding Cycle

Scheduling CycleではPodが稼働するNodeを決定し、Binding Cycleではそれをクラスターに適用します。この2つのサイクルを合わせて「スケジューリングコンテキスト」と呼びます。

Scheduling CycleではPodに対して1つ1つが順番に実行され、Binding Cyclesでは並列に実行されます。

Podがスケジューリング不能と判断された場合や、内部エラーが発生した場合、Scheduling CycleまたはBinding Cycleを中断することができます。その際、Podはキューに戻され再試行されます。

拡張点

次の図はPodに対するスケジューリングコンテキストとスケジューリングフレームワークが公開する拡張点を示しています。この図では「Filter」がフィルタリングのための「Predicate」、「Scoring」がスコアリングのための「Priorities」機能に相当します。

1つのプラグインを複数の拡張点に登録することで、より複雑なタスクやステートフルなタスクを実行することができます。

scheduling framework extension points

QueueSort

これらのプラグインはスケジューリングキュー内のPodをソートするために使用されます。このプラグインは、基本的にLess(Pod1, Pod2)という関数を提供します。また、このプラグインは、1つだけ有効化できます。

PreFilter

これらのプラグインは、Podに関する情報を前処理したり、クラスターやPodが満たすべき特定の条件をチェックするために使用されます。もし、PreFilterプラグインのいずれかがエラーを返した場合、Scheduling Cycleは中断されます。

Filter

FilterプラグインはPodを実行できないNodeを候補から除外します。各Nodeに対して、スケジューラーは設定された順番でFilterプラグインを呼び出します。もし、いずれかのFilterプラグインが途中でそのNodeを実行不可能とした場合、残りのプラグインではそのNodeは呼び出されません。Nodeは同時に評価されることがあります。

PostFilter

これらのプラグインはFilterフェーズで、Podに対して実行可能なNodeが見つからなかった場合にのみ呼び出されます。このプラグインは設定された順番で呼び出されます。もしいずれかのPostFilterプラグインが、あるNodeを「スケジュール可能(Schedulable)」と目星をつけた場合、残りのプラグインは呼び出されません。典型的なPostFilterの実装はプリエンプション方式で、他のPodを先取りして、Podをスケジューリングできるようにしようとします。

PreScore

これらのプラグインは、Scoreプラグインが使用する共有可能な状態を生成する「スコアリングの事前」作業を行うために使用されます。このプラグインがエラーを返した場合、Scheduling Cycleは中断されます。

Score

これらのプラグインはフィルタリングのフェーズを通過したNodeをランク付けするために使用されます。スケジューラーはそれぞれのNodeに対して、それぞれのscoringプラグインを呼び出します。スコアの最小値と最大値の範囲が明確に定義されます。NormalizeScoreフェーズの後、スケジューラーは設定されたプラグインの重みに従って、全てのプラグインからNodeのスコアを足し合わせます。

NormalizeScore

これらのプラグインはスケジューラーが最終的なNodeの順位を計算する前にスコアを修正するために使用されます。この拡張点に登録されたプラグインは、同じプラグインのScoreの結果を使用して呼び出されます。各プラグインはScheduling Cycle毎に、1回呼び出されます。

例えば、BlinkingLightScorerというプラグインが、点滅する光の数に基づいてランク付けをするとします。

func ScoreNode(_ *v1.pod, n *v1.Node) (int, error) {
    return getBlinkingLightCount(n)
}

ただし、NodeScoreMaxに比べ、点滅をカウントした最大値の方が小さい場合があります。これを解決するために、BlinkingLightScorerも拡張点に登録する必要があります。

func NormalizeScores(scores map[string]int) {
    highest := 0
    for _, score := range scores {
        highest = max(highest, score)
    }
    for node, score := range scores {
        scores[node] = score*NodeScoreMax/highest
    }
}

NormalizeScoreプラグインが途中でエラーを返した場合、Scheduling Cycleは中断されます。

Reserve

Reserve拡張を実装したプラグインには、ReserveとUnreserve という2つのメソッドがあり、それぞれReserveUnreserveと呼ばれる2つの情報スケジューリングフェーズを返します。 実行状態を保持するプラグイン(別名「ステートフルプラグイン」)は、これらのフェーズを使用して、Podに対してNodeのリソースが予約されたり予約解除された場合に、スケジューラーから通知を受け取ります。

Reserveフェーズは、スケジューラーが実際にPodを指定されたNodeにバインドする前に発生します。このフェーズはスケジューラーがバインドが成功するのを待つ間にレースコンディションの発生を防ぐためにあります。 各ReserveプラグインのReserveメソッドは成功することも失敗することもあります。もしどこかのReserveメソッドの呼び出しが失敗すると、後続のプラグインは実行されず、Reserveフェーズは失敗したものとみなされます。全てのプラグインのReserveメソッドが成功した場合、Reserveフェーズは成功とみなされ、残りのScheduling CycleとBinding Cycleが実行されます。

Unreserveフェーズは、Reserveフェーズまたは後続のフェーズが失敗した場合に、呼び出されます。この時、全てのReserveプラグインのUnreserveメソッドが、Reserveメソッドの呼び出された逆の順序で実行されます。このフェーズは予約されたPodに関連する状態をクリーンアップするためにあります。

Permit

Permit プラグインは、各PodのScheduling Cycleの終了時に呼び出され、候補Nodeへのバインドを阻止もしくは遅延させるために使用されます。permitプラグインは次の3つのうちどれかを実行できます。

  1. 承認(approve)
    全てのPermitプラグインから承認(approve)されたPodは、バインド処理へ送られます。

  2. 拒否(deny)
    もしどれか1つのPermitプラグインがPodを拒否(deny)した場合、そのPodはスケジューリングキューに戻されます。 これはReserveプラグイン内のUnreserveフェーズで呼び出されます。

  3. 待機(wait) (タイムアウトあり)
    もしPermitプラグインが「待機(wait)」を返した場合、そのPodは内部の「待機中」Podリストに保持され、このPodに対するBinding Cycleは開始されるものの、承認(approve)されるまで直接ブロックされます。もしタイムアウトが発生した場合、この待機(wait)denyへ変わり、対象のPodはスケジューリングキューに戻されると共に、ReserveプラグインのUnreserveフェーズが呼び出されます。

PreBind

これらのプラグインは、Podがバインドされる前に必要な作業を行うために使用されます。例えば、Podの実行を許可する前に、ネットワークボリュームをプロビジョニングし、Podを実行予定のNodeにマウントすることができます。

もし、いずれかのPreBindプラグインがエラーを返した場合、Podは拒否され、スケジューリングキューに戻されます。

Bind

これらのプラグインはPodをNodeにバインドするために使用されます。このプラグインは全てのPreBindプラグインの処理が完了するまで呼ばれません。それぞれのBindプラグインは設定された順序で呼び出されます。このプラグインは、与えられたPodを処理するかどうかを選択することができます。もしPodを処理することを選択した場合、残りのBindプラグインは全てスキップされます。

PostBind

これは単に情報提供のための拡張点です。Post-bindプラグインはPodのバインドが成功した後に呼び出されます。これはBinding Cycleの最後であり、関連するリソースのクリーンアップに使用されます。

プラグインAPI

プラグインAPIには2つの段階があります。まず、プラグインを登録し設定することです。そして、拡張点インターフェースを使用することです。このインターフェースは次のような形式をとります。

type Plugin interface {
    Name() string
}

type QueueSortPlugin interface {
    Plugin
    Less(*v1.pod, *v1.pod) bool
}

type PreFilterPlugin interface {
    Plugin
    PreFilter(context.Context, *framework.CycleState, *v1.pod) error
}

// ...

プラグインの設定

スケジューラーの設定でプラグインを有効化・無効化することができます。Kubernetes v1.18以降を使用しているなら、ほとんどのスケジューリングプラグインは使用されており、デフォルトで有効になっています。

デフォルトのプラグインに加えて、独自のスケジューリングプラグインを実装し、デフォルトのプラグインと一緒に使用することも可能です。詳しくはスケジューラープラグインをご覧下さい。

Kubernetes v1.18以降を使用しているなら、プラグインのセットをスケジューラープロファイルとして設定し、様々な種類のワークロードに適合するように複数のプロファイルを定義することが可能です。詳しくは複数のプロファイルをご覧下さい。

3.10.6 - スケジューラーのパフォーマンスチューニング

FEATURE STATE: Kubernetes 1.14 [beta]

kube-schedulerはKubernetesのデフォルトのスケジューラーです。クラスター内のノード上にPodを割り当てる責務があります。

クラスター内に存在するノードで、Podのスケジューリング要求を満たすものはPodに対して割り当て可能なノードと呼ばれます。スケジューラーはPodに対する割り当て可能なノードをみつけ、それらの割り当て可能なノードにスコアをつけます。その中から最も高いスコアのノードを選択し、Podに割り当てるためのいくつかの関数を実行します。スケジューラーはBindingと呼ばれる処理中において、APIサーバーに対して割り当てが決まったノードの情報を通知します。

このページでは、大規模のKubernetesクラスターにおけるパフォーマンス最適化のためのチューニングについて説明します。

大規模クラスターでは、レイテンシー(新規Podをすばやく配置)と精度(スケジューラーが不適切な配置を行うことはめったにありません)の間でスケジューリング結果を調整するスケジューラーの動作をチューニングできます。

このチューニング設定は、kube-scheduler設定のpercentageOfNodesToScoreで設定できます。KubeSchedulerConfiguration設定は、クラスター内のノードにスケジュールするための閾値を決定します。

閾値の設定

percentageOfNodesToScoreオプションは、0から100までの数値を受け入れます。0は、kube-schedulerがコンパイル済みのデフォルトを使用することを示す特別な値です。 percentageOfNodesToScoreに100より大きな値を設定した場合、kube-schedulerの挙動は100を設定した場合と同様となります。

この値を変更するためには、kube-schedulerの設定ファイル(これは/etc/kubernetes/config/kube-scheduler.yamlの可能性が高い)を編集し、スケジューラーを再起動します。

この変更をした後、

kubectl get pods -n kube-system | grep kube-scheduler

を実行して、kube-schedulerコンポーネントが正常であることを確認できます。

ノードへのスコア付けの閾値

スケジューリング性能を改善するため、kube-schedulerは割り当て可能なノードが十分に見つかるとノードの検索を停止できます。大規模クラスターでは、すべてのノードを考慮する単純なアプローチと比較して時間を節約できます。

クラスター内のすべてのノードに対する十分なノード数を整数パーセンテージで指定します。kube-schedulerは、これをノード数に変換します。スケジューリング中に、kube-schedulerが設定されたパーセンテージを超える十分な割り当て可能なノードを見つけた場合、kube-schedulerはこれ以上割り当て可能なノードを探すのを止め、スコアリングフェーズに進みます。

スケジューラーはどのようにノードを探索するかで処理を詳しく説明しています。

デフォルトの閾値

閾値を指定しない場合、Kubernetesは100ノードのクラスターでは50%、5000ノードのクラスターでは10%になる線形方程式を使用して数値を計算します。自動計算の下限は5%です。

つまり、明示的にpercentageOfNodesToScoreを5未満の値を設定しない限り、クラスターの規模に関係なく、kube-schedulerは常に少なくともクラスターの5%のノードに対してスコア付けをします。

スケジューラーにクラスター内のすべてのノードに対してスコア付けをさせる場合は、percentageOfNodesToScoreの値に100を設定します。

percentageOfNodesToScoreの値を50%に設定する例は下記のとおりです。

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider

...

percentageOfNodesToScore: 50

percentageOfNodesToScoreのチューニング

percentageOfNodesToScoreは1から100の間の範囲である必要があり、デフォルト値はクラスターのサイズに基づいて計算されます。また、クラスターのサイズの最小値は50ノードとハードコードされています。

この値を設定する際に考慮するべき重要な注意事項として、割り当て可能ノードのチェック対象のノードが少ないと、一部のノードはPodの割り当てのためにスコアリングされなくなります。結果として、高いスコアをつけられる可能性のあるノードがスコアリングフェーズに渡されることがありません。これにより、Podの配置が理想的なものでなくなります。

kube-schedulerが頻繁に不適切なPodの配置を行わないよう、percentageOfNodesToScoreをかなり低い値を設定することは避けるべきです。スケジューラのスループットがアプリケーションにとって致命的で、ノードのスコアリングが重要でない場合を除いて、10%未満に設定することは避けてください。言いかえると、割り当て可能な限り、Podは任意のノード上で稼働させるのが好ましいです。

スケジューラーはどのようにノードを探索するか

このセクションでは、この機能の内部の詳細を理解したい人向けになります。

クラスター内の全てのノードに対して平等にPodの割り当ての可能性を持たせるため、スケジューラーはラウンドロビン方式でノードを探索します。複数のノードの配列になっているイメージです。スケジューラーはその配列の先頭から探索を開始し、percentageOfNodesToScoreによって指定された数のノードを検出するまで、割り当て可能かどうかをチェックしていきます。次のPodでは、スケジューラーは前のPodの割り当て処理でチェックしたところから探索を再開します。

ノードが複数のゾーンに存在するとき、スケジューラーは様々なゾーンのノードを探索して、異なるゾーンのノードが割り当て可能かどうかのチェック対象になるようにします。例えば2つのゾーンに6つのノードがある場合を考えます。

Zone 1: Node 1, Node 2, Node 3, Node 4
Zone 2: Node 5, Node 6

スケジューラーは、下記の順番でノードの割り当て可能性を評価します。

Node 1, Node 5, Node 2, Node 6, Node 3, Node 4

全てのノードのチェックを終えたら、1番目のノードに戻ってチェックをします。

3.10.7 - 拡張リソースのリソースビンパッキング

FEATURE STATE: Kubernetes v1.16 [alpha]

kube-schedulerでは、優先度関数RequestedToCapacityRatioResourceAllocationを使用した、 拡張リソースを含むリソースのビンパッキングを有効化できます。優先度関数はそれぞれのニーズに応じて、kube-schedulerを微調整するために使用できます。

RequestedToCapacityRatioResourceAllocationを使用したビンパッキングの有効化

Kubernetesでは、キャパシティー比率への要求に基づいたNodeのスコアリングをするために、各リソースの重みと共にリソースを指定することができます。これにより、ユーザーは適切なパラメーターを使用することで拡張リソースをビンパックすることができ、大規模クラスターにおける希少なリソースを有効活用できるようになります。優先度関数RequestedToCapacityRatioResourceAllocationの動作はRequestedToCapacityRatioArgsと呼ばれる設定オプションによって変わります。この引数はshaperesourcesパラメーターによって構成されます。shapeパラメーターはutilizationscoreの値に基づいて、最も要求が多い場合か最も要求が少ない場合の関数をチューニングできます。resourcesパラメーターは、スコアリングの際に考慮されるリソース名のnameと、各リソースの重みを指定するweightで構成されます。

以下は、拡張リソースintel.com/foointel.com/barのビンパッキングにrequestedToCapacityRatioArgumentsを設定する例になります。

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
profiles:
# ...
  pluginConfig:
  - name: RequestedToCapacityRatio
    args:
      shape:
      - utilization: 0
        score: 10
      - utilization: 100
        score: 0
      resources:
      - name: intel.com/foo
        weight: 3
      - name: intel.com/bar
        weight: 5

スケジューラーには、kube-schedulerフラグ--config=/path/to/config/fileを使用してKubeSchedulerConfigurationのファイルを指定することで渡すことができます。

この機能はデフォルトで無効化されています

優先度関数のチューニング

shapeRequestedToCapacityRatioPriority関数の動作を指定するために使用されます。

shape:
 - utilization: 0
   score: 0
 - utilization: 100
   score: 10

上記の引数は、utilizationが0%の場合は0、utilizationが100%の場合は10というscoreをNodeに与え、ビンパッキングの動作を有効にしています。最小要求を有効にするには、次のようにスコアを反転させる必要があります。

shape:
  - utilization: 0
    score: 10
  - utilization: 100
    score: 0

resourcesはオプションパラメーターで、デフォルトでは以下の通りです。

resources:
  - name: cpu
    weight: 1
  - name: memory
    weight: 1

以下のように拡張リソースの追加に利用できます。

resources:
  - name: intel.com/foo
    weight: 5
  - name: cpu
    weight: 3
  - name: memory
    weight: 1

weightはオプションパラメーターで、指定されてない場合1が設定されます。また、マイナスの値は設定できません。

キャパシティ割り当てのためのNodeスコアリング

このセクションは、本機能の内部詳細について理解したい方を対象としています。以下は、与えられた値に対してNodeのスコアがどのように計算されるかの例です。

要求されたリソース:

intel.com/foo : 2
memory: 256MB
cpu: 2

リソースの重み:

intel.com/foo : 5
memory: 1
cpu: 3

shapeの値 {{0, 0}, {100, 10}}

Node1のスペック:

Available:
  intel.com/foo: 4
  memory: 1 GB
  cpu: 8

Used:
  intel.com/foo: 1
  memory: 256MB
  cpu: 1

Nodeのスコア:

intel.com/foo  = resourceScoringFunction((2+1),4)
               = (100 - ((4-3)*100/4)
               = (100 - 25)
               = 75                       # requested + used = 75% * available
               = rawScoringFunction(75)
               = 7                        # floor(75/10)

memory         = resourceScoringFunction((256+256),1024)
               = (100 -((1024-512)*100/1024))
               = 50                       # requested + used = 50% * available
               = rawScoringFunction(50)
               = 5                        # floor(50/10)

cpu            = resourceScoringFunction((2+1),8)
               = (100 -((8-3)*100/8))
               = 37.5                     # requested + used = 37.5% * available
               = rawScoringFunction(37.5)
               = 3                        # floor(37.5/10)

NodeScore   =  ((7 * 5) + (5 * 1) + (3 * 3)) / (5 + 1 + 3)
            =  5

Node2のスペック:

Available:
  intel.com/foo: 8
  memory: 1GB
  cpu: 8
Used:
  intel.com/foo: 2
  memory: 512MB
  cpu: 6

Nodeのスコア:

intel.com/foo  = resourceScoringFunction((2+2),8)
               =  (100 - ((8-4)*100/8)
               =  (100 - 50)
               =  50
               =  rawScoringFunction(50)
               = 5

memory         = resourceScoringFunction((256+512),1024)
               = (100 -((1024-768)*100/1024))
               = 75
               = rawScoringFunction(75)
               = 7

cpu            = resourceScoringFunction((2+6),8)
               = (100 -((8-8)*100/8))
               = 100
               = rawScoringFunction(100)
               = 10

NodeScore   =  ((5 * 5) + (7 * 1) + (10 * 3)) / (5 + 1 + 3)
            =  7

次の項目

3.10.8 - Podの優先度とプリエンプション

FEATURE STATE: Kubernetes v1.14 [stable]

Podpriority(優先度)を持つことができます。 優先度は他のPodに対する相対的なPodの重要度を示します。 もしPodをスケジューリングできないときには、スケジューラーはそのPodをスケジューリングできるようにするため、優先度の低いPodをプリエンプトする(追い出す)ことを試みます。

優先度とプリエンプションを使う方法

優先度とプリエンプションを使うには、

  1. 1つまたは複数のPriorityClassを追加します

  2. 追加したPriorityClassをpriorityClassNameに設定したPodを作成します。 もちろんPodを直接作る必要はありません。 一般的にはpriorityClassNameをDeploymentのようなコレクションオブジェクトのPodテンプレートに追加します。

これらの手順のより詳しい情報については、この先を読み進めてください。

PriorityClass

PriorityClassはnamespaceによらないオブジェクトで、優先度クラスの名称から優先度を表す整数値への対応を定義します。 PriorityClassオブジェクトのメタデータのnameフィールドにて名称を指定します。 値はvalueフィールドで指定し、必須です。 値が大きいほど、高い優先度を示します。 PriorityClassオブジェクトの名称はDNSサブドメイン名として適切であり、かつsystem-から始まってはいけません。

PriorityClassオブジェクトは10億以下の任意の32ビットの整数値を持つことができます。これは、PriorityClassオブジェクトの値の範囲が-2147483648から1000000000までであることを意味します。 それよりも大きな値は通常はプリエンプトや追い出すべきではない重要なシステム用のPodのために予約されています。 クラスターの管理者は割り当てたい優先度に対して、PriorityClassオブジェクトを1つずつ作成すべきです。

PriorityClassは任意でフィールドglobalDefaultdescriptionを設定可能です。 globalDefaultフィールドはpriorityClassNameが指定されないPodはこのPriorityClassを使うべきであることを示します。globalDefaultがtrueに設定されたPriorityClassはシステムで一つのみ存在可能です。globalDefaultが設定されたPriorityClassが存在しない場合は、priorityClassNameが設定されていないPodの優先度は0に設定されます。

descriptionフィールドは任意の文字列です。クラスターの利用者に対して、PriorityClassをどのような時に使うべきか示すことを意図しています。

PodPriorityと既存のクラスターに関する注意

  • もし既存のクラスターをこの機能がない状態でアップグレードすると、既存のPodの優先度は実質的に0になります。

  • globalDefaulttrueに設定されたPriorityClassを追加しても、既存のPodの優先度は変わりません。PriorityClassのそのような値は、PriorityClassが追加された以後に作成されたPodのみに適用されます。

  • PriorityClassを削除した場合、削除されたPriorityClassの名前を使用する既存のPodは変更されませんが、削除されたPriorityClassの名前を使うPodをそれ以上作成することはできなくなります。

PriorityClassの例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "この優先度クラスはXYZサービスのPodに対してのみ使用すべきです。"

非プリエンプトのPriorityClass

FEATURE STATE: Kubernetes v1.24 [stable]

preemptionPolicy: Neverと設定されたPodは、スケジューリングのキューにおいて他の優先度の低いPodよりも優先されますが、他のPodをプリエンプトすることはありません。 スケジューリングされるのを待つ非プリエンプトのPodは、リソースが十分に利用可能になるまでスケジューリングキューに残ります。 非プリエンプトのPodは、他のPodと同様に、スケジューラーのバックオフの対象になります。これは、スケジューラーがPodをスケジューリングしようと試みたものの失敗した場合、低い頻度で再試行するようにして、より優先度の低いPodが先にスケジューリングされることを許します。

非プリエンプトのPodは、他の優先度の高いPodにプリエンプトされる可能性はあります。

preemptionPolicyはデフォルトではPreemptLowerPriorityに設定されており、これが設定されているPodは優先度の低いPodをプリエンプトすることを許容します。これは既存のデフォルトの挙動です。 preemptionPolicyNeverに設定すると、これが設定されたPodはプリエンプトを行わないようになります。

ユースケースの例として、データサイエンスの処理を挙げます。 ユーザーは他の処理よりも優先度を高くしたいジョブを追加できますが、そのとき既存の実行中のPodの処理結果をプリエンプトによって破棄させたくはありません。 preemptionPolicy: Neverが設定された優先度の高いジョブは、他の既にキューイングされたPodよりも先に、クラスターのリソースが「自然に」開放されたときにスケジューリングされます。

非プリエンプトのPriorityClassの例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "この優先度クラスは他のPodをプリエンプトさせません。"

Podの優先度

一つ以上のPriorityClassがあれば、仕様にPriorityClassを指定したPodを作成することができるようになります。優先度のアドミッションコントローラーはpriorityClassNameフィールドを使用し、優先度の整数値を設定します。PriorityClassが見つからない場合、そのPodの作成は拒否されます。

下記のYAMLは上記の例で作成したPriorityClassを使用するPodの設定の例を示します。優先度のアドミッションコントローラーは仕様を確認し、このPodの優先度は1000000であると設定します。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

スケジューリング順序におけるPodの優先度の効果

Podの優先度が有効な場合、スケジューラーは待機状態のPodをそれらの優先度順に並べ、スケジューリングキューにおいてより優先度の低いPodよりも前に来るようにします。その結果、その条件を満たしたときには優先度の高いPodは優先度の低いPodより早くスケジューリングされます。優先度の高いPodがスケジューリングできない場合は、スケジューラーは他の優先度の低いPodのスケジューリングも試みます。

プリエンプション

Podが作成されると、スケジューリング待ちのキューに入り待機状態になります。スケジューラーはキューからPodを取り出し、ノードへのスケジューリングを試みます。Podに指定された条件を全て満たすノードが見つからない場合は、待機状態のPodのためにプリエンプションロジックが発動します。待機状態のPodをPと呼ぶことにしましょう。プリエンプションロジックはPよりも優先度の低いPodを一つ以上追い出せばPをスケジューリングできるようになるノードを探します。そのようなノードがあれば、優先度の低いPodはノードから追い出されます。Podが追い出された後に、Pはノードへスケジューリング可能になります。

ユーザーへ開示される情報

Pod PがノードNのPodをプリエンプトした場合、ノードNの名称がPのステータスのnominatedNodeNameフィールドに設定されます。このフィールドはスケジューラーがPod Pのために予約しているリソースの追跡を助け、ユーザーにクラスターにおけるプリエンプトに関する情報を与えます。

Pod Pは必ずしも「指名したノード」へスケジューリングされないことに注意してください。スケジューラーは、他のノードに対して処理を繰り返す前に、常に「指定したノード」に対して試行します。Podがプリエンプトされると、そのPodは終了までの猶予期間を得ます。スケジューラーがPodの終了を待つ間に他のノードが利用可能になると、スケジューラーは他のノードをPod Pのスケジューリング先にすることがあります。この結果、PodのnominatedNodeNamenodeNameは必ずしも一致しません。また、スケジューラーがノードNのPodをプリエンプトさせた後に、Pod Pよりも優先度の高いPodが来た場合、スケジューラーはノードNをその新しい優先度の高いPodへ与えることもあります。このような場合は、スケジューラーはPod PのnominatedNodeNameを消去します。これによって、スケジューラーはPod Pが他のノードのPodをプリエンプトさせられるようにします。

プリエンプトの制限

プリエンプトされるPodの正常終了

Podがプリエンプトされると、猶予期間が与えられます。 Podは作業を完了し、終了するために十分な時間が与えられます。仮にそうでない場合、強制終了されます。この猶予期間によって、スケジューラーがPodをプリエンプトした時刻と、待機状態のPod Pがノード Nにスケジュール可能になるまでの時刻の間に間が開きます。この間、スケジューラーは他の待機状態のPodをスケジュールしようと試みます。プリエンプトされたPodが終了したら、スケジューラーは待ち行列にあるPodをスケジューリングしようと試みます。そのため、Podがプリエンプトされる時刻と、Pがスケジュールされた時刻には間が開くことが一般的です。この間を最小にするには、優先度の低いPodの猶予期間を0または小さい値にする方法があります。

PodDisruptionBudgetは対応するが、保証されない

PodDisruptionBudget (PDB)は、アプリケーションのオーナーが冗長化されたアプリケーションのPodが意図的に中断される数の上限を設定できるようにするものです。KubernetesはPodをプリエンプトする際にPDBに対応しますが、PDBはベストエフォートで考慮します。スケジューラーはプリエンプトさせたとしてもPDBに違反しないPodを探します。そのようなPodが見つからない場合でもプリエンプションは実行され、PDBに反しますが優先度の低いPodが追い出されます。

優先度の低いPodにおけるPod間のアフィニティ

次の条件が真の場合のみ、ノードはプリエンプションの候補に入ります。 「待機状態のPodよりも優先度の低いPodをノードから全て追い出したら、待機状態のPodをノードへスケジュールできるか」

待機状態のPodが、優先度の低いPodとの間でPod間のアフィニティを持つ場合、Pod間のアフィニティはそれらの優先度の低いPodがなければ満たされません。この場合、スケジューラーはノードのどのPodもプリエンプトしようとはせず、代わりに他のノードを探します。スケジューラーは適切なノードを探せる場合と探せない場合があります。この場合、待機状態のPodがスケジューリングされる保証はありません。

この問題に対して推奨される解決策は、優先度が同一または高いPodに対してのみPod間のアフィニティを作成することです。

複数ノードに対するプリエンプション

Pod PがノードNにスケジューリングできるよう、ノードNがプリエンプションの対象となったとします。 他のノードのPodがプリエンプトされた場合のみPが実行可能になることもあります。下記に例を示します。

  • Pod PをノードNに配置することを検討します。
  • Pod QはノードNと同じゾーンにある別のノードで実行中です。
  • Pod Pはゾーンに対するQへのアンチアフィニティを持ちます (topologyKey: topology.kubernetes.io/zone)。
  • Pod Pと、ゾーン内の他のPodに対しては他のアンチアフィニティはない状態です。
  • Pod PをノードNへスケジューリングするには、Pod Qをプリエンプトすることが考えられますが、スケジューラーは複数ノードにわたるプリエンプションは行いません。そのため、Pod PはノードNへはスケジューリングできないとみなされます。

Pod Qがそのノードから追い出されると、Podアンチアフィニティに違反しなくなるので、Pod PはノードNへスケジューリング可能になります。

複数ノードに対するプリエンプションに関しては、十分な需要があり、合理的な性能を持つアルゴリズムを見つけられた場合に、将来的に機能追加を検討する可能性があります。

トラブルシューティング

Podの優先度とプリエンプションは望まない副作用をもたらす可能性があります。 いくつかの起こりうる問題と、その対策について示します。

Podが不必要にプリエンプトされる

プリエンプションは、リソースが不足している場合に優先度の高い待機状態のPodのためにクラスターの既存のPodを追い出します。 誤って高い優先度をPodに割り当てると、意図しない高い優先度のPodはクラスター内でプリエンプションを引き起こす可能性があります。Podの優先度はPodの仕様のpriorityClassNameフィールドにて指定されます。優先度を示す整数値へと変換された後、podSpecpriorityへ設定されます。

この問題に対処するには、PodのpriorityClassNameをより低い優先度に変更するか、このフィールドを未設定にすることができます。priorityClassNameが未設定の場合、デフォルトでは優先度は0とされます。

Podがプリエンプトされたとき、プリエンプトされたPodのイベントが記録されます。 プリエンプションはPodに必要なリソースがクラスターにない場合のみ起こるべきです。 このような場合、プリエンプションはプリエンプトされるPodよりも待機状態のPodの優先度が高い場合のみ発生します。 プリエンプションは待機状態のPodがない場合や待機状態のPodがプリエンプト対象のPod以下の優先度を持つ場合には決して発生しません。そのような状況でプリエンプションが発生した場合、問題を報告してください。

Podはプリエンプトされたが、プリエンプトさせたPodがスケジューリングされない

Podがプリエンプトされると、それらのPodが要求した猶予期間が与えられます。そのデフォルトは30秒です。 Podがその期間内に終了しない場合、強制終了されます。プリエンプトされたPodがなくなれば、プリエンプトさせたPodはスケジューリング可能です。

プリエンプトさせたPodがプリエンプトされたPodの終了を待っている間に、より優先度の高いPodが同じノードに対して作成されることもあります。この場合、スケジューラーはプリエンプトさせたPodの代わりに優先度の高いPodをスケジューリングします。

これは予期された挙動です。優先度の高いPodは優先度の低いPodに取って代わります。

優先度の高いPodが優先度の低いPodより先にプリエンプトされる

スケジューラーは待機状態のPodが実行可能なノードを探します。ノードが見つからない場合、スケジューラーは任意のノードから優先度の低いPodを追い出し、待機状態のPodのためのリソースを確保しようとします。 仮に優先度の低いPodが動いているノードが待機状態のPodを動かすために適切ではない場合、スケジューラーは他のノードで動いているPodと比べると、優先度の高いPodが動いているノードをプリエンプションの対象に選ぶことがあります。この場合もプリエンプトされるPodはプリエンプトを起こしたPodよりも優先度が低い必要があります。

複数のノードがプリエンプションの対象にできる場合、スケジューラーは優先度が最も低いPodのあるノードを選ぼうとします。しかし、そのようなPodがPodDisruptionBudgetを持っており、プリエンプトするとPDBに反する場合はスケジューラーは優先度の高いPodのあるノードを選ぶこともあります。

複数のノードがプリエンプションの対象として利用可能で、上記の状況に当てはまらない場合、スケジューラーは優先度の最も低いノードを選択します。

Podの優先度とQoSの相互作用

Podの優先度とQoSクラスは直交する機能で、わずかに相互作用がありますが、デフォルトではQoSクラスによる優先度の設定の制約はありません。スケジューラーのプリエンプションのロジックはプリエンプションの対象を決めるときにQoSクラスは考慮しません。 プリエンプションはPodの優先度を考慮し、優先度が最も低いものを候補とします。より優先度の高いPodは優先度の低いPodを追い出すだけではプリエンプトを起こしたPodのスケジューリングに不十分な場合と、PodDisruptionBudgetにより優先度の低いPodが保護されている場合のみ対象になります。

kubeletはnode-pressureによる退避を行うPodの順番を決めるために、優先度を利用します。QoSクラスを使用して、最も退避される可能性の高いPodの順番を推定することができます。 kubeletは追い出すPodの順位付けを次の順で行います。

  1. 枯渇したリソースを要求以上に使用しているか
  2. Podの優先度
  3. 要求に対するリソースの使用量

詳細はkubeletによるPodの退避を参照してください。

kubeletによるリソース不足時のPodの追い出しでは、リソースの消費が要求を超えないPodは追い出されません。優先度の低いPodのリソースの利用量がその要求を超えていなければ、追い出されることはありません。より優先度が高く、要求を超えてリソースを使用しているPodが追い出されます。

次の項目

3.10.9 - APIを起点とした退避

APIを起点とした退避は、Eviction APIを使用して退避オブジェクトを作成し、Podの正常終了を起動させるプロセスです。

Eviction APIを直接呼び出すか、kubectl drainコマンドのようにAPIサーバーのクライアントを使って退避を要求することが可能です。これにより、Evictionオブジェクトを作成し、APIサーバーにPodを終了させます。

APIを起点とした退避はPodDisruptionBudgetsterminationGracePeriodSecondsの設定を優先します。

APIを使用してPodのEvictionオブジェクトを作成することは、Podに対してポリシー制御されたDELETE操作を実行することに似ています。

Eviction APIの実行

Kubernetes APIへアクセスしてEvictionオブジェクトを作るためにKubernetesのプログラミング言語のクライアントを使用できます。 そのためには、次の例のようなデータをPOSTすることで操作を試みることができます。

{
  "apiVersion": "policy/v1",
  "kind": "Eviction",
  "metadata": {
    "name": "quux",
    "namespace": "default"
  }
}

{
  "apiVersion": "policy/v1beta1",
  "kind": "Eviction",
  "metadata": {
    "name": "quux",
    "namespace": "default"
  }
}

また、以下の例のようにcurlwgetを使ってAPIにアクセスすることで、操作を試みることもできます。

curl -v -H 'Content-type: application/json' https://your-cluster-api-endpoint.example/api/v1/namespaces/default/pods/quux/eviction -d @eviction.json

APIを起点とした退避の仕組み

APIを使用して退去を要求した場合、APIサーバーはアドミッションチェックを行い、以下のいずれかを返します。

  • 200 OK:この場合、退去が許可されるとEvictionサブリソースが作成され、PodのURLにDELETEリクエストを送るのと同じように、Podが削除されます。
  • 429 Too Many Requests:PodDisruptionBudgetの設定により、現在退去が許可されていないことを示します。しばらく時間を空けてみてください。また、APIのレート制限のため、このようなレスポンスが表示されることもあります。
  • 500 Internal Server Error:複数のPodDisruptionBudgetが同じPodを参照している場合など、設定に誤りがあり退去が許可されないことを示します。

退去させたいPodがPodDisruptionBudgetを持つワークロードの一部でない場合、APIサーバーは常に200 OKを返して退去を許可します。

APIサーバーが退去を許可した場合、以下の流れでPodが削除されます。

  1. APIサーバーのPodリソースの削除タイムスタンプが更新され、APIサーバーはPodリソースが終了したと見なします。またPodリソースは、設定された猶予期間が設けられます。
  2. ローカルのPodが動作しているNodeのkubeletは、Podリソースが終了するようにマークされていることに気付き、Podの適切なシャットダウンを開始します。
  3. kubeletがPodをシャットダウンしている間、コントロールプレーンはEndpointオブジェクトからPodを削除します。その結果、コントローラーはPodを有効なオブジェクトと見なさないようになります。
  4. Podの猶予期間が終了すると、kubeletはローカルPodを強制的に終了します。
  5. kubeletはAPIサーバーにPodリソースを削除するように指示します。
  6. APIサーバーはPodリソースを削除します。

トラブルシューティング

場合によっては、アプリケーションが壊れた状態になり、対処しない限りEviction APIが429または500レスポンスを返すだけとなることがあります。例えば、ReplicaSetがアプリケーション用のPodを作成しても、新しいPodがReady状態にならない場合などです。また、最後に退去したPodの終了猶予期間が長い場合にも、この事象が見られます。

退去が進まない場合は、以下の解決策を試してみてください。

  • 問題を引き起こしている自動化された操作を中止または一時停止し、操作を再開する前に、スタックしているアプリケーションを調査を行ってください。
  • しばらく待ってから、Eviction APIを使用する代わりに、クラスターのコントロールプレーンから直接Podを削除してください。

次の項目

3.11 - クラスターの管理

3.11.1 - クラスター管理の概要

このページはKubernetesクラスターの作成や管理者向けの内容です。Kubernetesのコアコンセプトについてある程度精通していることを前提とします。

クラスターのプランニング

Kubernetesクラスターの計画、セットアップ、設定の例を知るには設定のガイドを参照してください。この記事で列挙されているソリューションはディストリビューション と呼ばれます。

ガイドを選択する前に、いくつかの考慮事項を挙げます。

  • ユーザーのコンピューター上でKubernetesを試したいでしょうか、それとも高可用性のあるマルチノードクラスターを構築したいでしょうか?あなたのニーズにあったディストリビューションを選択してください。
  • もしあなたが高可用性を求める場合複数ゾーンにまたがるクラスターの設定について学んでください。
  • Google Kubernetes EngineのようなホストされているKubernetesクラスターを使用するのか、それとも自分自身でクラスターをホストするのでしょうか
  • 使用するクラスターはオンプレミスなのか、それともクラウド(IaaS) でしょうか?Kubernetesはハイブリッドクラスターを直接サポートしていません。その代わりユーザーは複数のクラスターをセットアップできます。
  • Kubernetesを 「ベアメタル」なハードウェア上で稼働させますか?それとも仮想マシン(VMs) 上で稼働させますか?
  • もしオンプレミスでKubernetesを構築する場合、どのネットワークモデルが最適か検討してください。
  • ただクラスターを稼働させたいだけでしょうか、それともKubernetesプロジェクトのコードの開発を行いたいでしょうか?もし後者の場合、開発が進行中のディストリビューションを選択してください。いくつかのディストリビューションはバイナリリリースのみ使用していますが、多くの選択肢があります。
  • クラスターを稼働させるのに必要なコンポーネントについてよく理解してください。

注意: 全てのディストリビューションがアクティブにメンテナンスされている訳ではありません。最新バージョンのKubernetesでテストされたディストリビューションを選択してください。

クラスターの管理

クラスターをセキュアにする

  • Certificatesでは、異なるツールチェインを使用して証明書を作成する方法を説明します。

  • Kubernetes コンテナの環境では、Kubernetesノード上でのKubeletが管理するコンテナの環境について説明します。

  • Kubernetes APIへのアクセス制御では、Kubernetesで自身のAPIに対するアクセスコントロールがどのように実装されているかを説明します。

  • 認証では、様々な認証オプションを含むKubernetesでの認証について説明します。

  • 認可では、認証とは別に、HTTPリクエストの処理方法を制御します。

  • アドミッションコントローラーの使用では、認証と認可の後にKubernetes APIに対するリクエストをインターセプトするプラグインについて説明します。

  • Kubernetesクラスターでのsysctlの使用では、管理者向けにカーネルパラメーターを設定するためsysctlコマンドラインツールの使用方法について解説します。

  • クラスターの監査では、Kubernetesの監査ログの扱い方について解説します。

kubeletをセキュアにする

オプションのクラスターサービス

3.11.2 - 証明書

クライアント証明書認証を使用する場合、easyrsaopensslcfsslを用いて、手動で証明書を生成できます。

easyrsa

easyrsaを用いると、クラスターの証明書を手動で生成できます。

  1. パッチを当てたバージョンのeasyrsa3をダウンロードして解凍し、初期化します。

    curl -LO https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
    tar xzf easy-rsa.tar.gz
    cd easy-rsa-master/easyrsa3
    ./easyrsa init-pki
    
  2. 新しい認証局(CA)を生成します。--batchは自動モードを設定し、--req-cnはCAの新しいルート証明書の共通名(CN)を指定します。

    ./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
    
  3. サーバー証明書と鍵を生成します。 引数--subject-alt-nameは、APIサーバーへのアクセスに使用できるIPおよびDNS名を設定します。 MASTER_CLUSTER_IPは通常、APIサーバーとコントローラーマネージャーコンポーネントの両方で引数--service-cluster-ip-rangeとして指定されるサービスCIDRの最初のIPです。 引数--daysは、証明書の有効期限が切れるまでの日数を設定するために使われます。 以下の例は、デフォルトのDNSドメイン名としてcluster.localを使用していることを前提とします。

    ./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
    "IP:${MASTER_CLUSTER_IP},"\
    "DNS:kubernetes,"\
    "DNS:kubernetes.default,"\
    "DNS:kubernetes.default.svc,"\
    "DNS:kubernetes.default.svc.cluster,"\
    "DNS:kubernetes.default.svc.cluster.local" \
    --days=10000 \
    build-server-full server nopass
    
  4. pki/ca.crtpki/issued/server.crtpki/private/server.keyをディレクトリーにコピーします。

  5. 以下のパラメーターを、APIサーバーの開始パラメーターとして追加します。

    --client-ca-file=/yourdirectory/ca.crt
    --tls-cert-file=/yourdirectory/server.crt
    --tls-private-key-file=/yourdirectory/server.key
    

openssl

opensslはクラスターの証明書を手動で生成できます。

  1. 2048ビットのca.keyを生成します。

    openssl genrsa -out ca.key 2048
    
  2. ca.keyに応じて、ca.crtを生成します。証明書の有効期間を設定するには、-daysを使用します。

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
    
  3. 2048ビットのserver.keyを生成します。

    openssl genrsa -out server.key 2048
    
  4. 証明書署名要求(CSR)を生成するための設定ファイルを生成します。 ファイル(例: csr.conf)に保存する前に、かぎ括弧で囲まれた値(例: <MASTER_IP>)を必ず実際の値に置き換えてください。 MASTER_CLUSTER_IPの値は、前節で説明したAPIサーバーのサービスクラスターIPであることに注意してください。 以下の例は、デフォルトのDNSドメイン名としてcluster.localを使用していることを前提とします。

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    
    [ dn ]
    C = <country>
    ST = <state>
    L = <city>
    O = <organization>
    OU = <organization unit>
    CN = <MASTER_IP>
    
    [ req_ext ]
    subjectAltName = @alt_names
    
    [ alt_names ]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster
    DNS.5 = kubernetes.default.svc.cluster.local
    IP.1 = <MASTER_IP>
    IP.2 = <MASTER_CLUSTER_IP>
    
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    subjectAltName=@alt_names
    
  5. 設定ファイルに基づいて、証明書署名要求を生成します。

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  6. ca.key、ca.crt、server.csrを使用してサーバー証明書を生成します。

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out server.crt -days 10000 \
    -extensions v3_ext -extfile csr.conf -sha256
    
  7. 証明書を表示します。

    openssl x509  -noout -text -in ./server.crt
    

最後にAPIサーバーの起動パラメーターに、同様のパラメーターを追加します。

cfssl

cfsslも証明書を生成するためのツールです。

  1. 以下のように、ダウンロードして解凍し、コマンドラインツールを用意します。 使用しているハードウェアアーキテクチャやcfsslのバージョンに応じて、サンプルコマンドの調整が必要な場合があります。

    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
    chmod +x cfssl
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
    chmod +x cfssljson
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
    chmod +x cfssl-certinfo
    
  2. アーティファクトを保持するディレクトリーを生成し、cfsslを初期化します。

    mkdir cert
    cd cert
    ../cfssl print-defaults config > config.json
    ../cfssl print-defaults csr > csr.json
    
  3. CAファイルを生成するためのJSON設定ファイル(例: ca-config.json)を生成します。

    {
      "signing": {
        "default": {
          "expiry": "8760h"
        },
        "profiles": {
          "kubernetes": {
            "usages": [
              "signing",
              "key encipherment",
              "server auth",
              "client auth"
            ],
            "expiry": "8760h"
          }
        }
      }
    }
    
  4. CA証明書署名要求(CSR)用のJSON設定ファイル(例: ca-csr.json)を生成します。 かぎ括弧で囲まれた値は、必ず使用したい実際の値に置き換えてください。

    {
      "CN": "kubernetes",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names":[{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  5. CA鍵(ca-key.pem)と証明書(ca.pem)を生成します。

    ../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
    
  6. APIサーバーの鍵と証明書を生成するためのJSON設定ファイル(例: server-csr.json)を生成します。 かぎ括弧で囲まれた値は、必ず使用したい実際の値に置き換えてください。 MASTER_CLUSTER_IPの値は、前節で説明したAPIサーバーのサービスクラスターIPです。 以下の例は、デフォルトのDNSドメイン名としてcluster.localを使用していることを前提とします。

    {
      "CN": "kubernetes",
      "hosts": [
        "127.0.0.1",
        "<MASTER_IP>",
        "<MASTER_CLUSTER_IP>",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
      ],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  7. APIサーバーの鍵と証明書を生成します。デフォルトでは、それぞれserver-key.pemserver.pemというファイルに保存されます。

    ../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
    --config=ca-config.json -profile=kubernetes \
    server-csr.json | ../cfssljson -bare server
    

自己署名CA証明書の配布

クライアントノードは、自己署名CA証明書を有効だと認識しないことがあります。 プロダクション用でない場合や、会社のファイアウォールの背後で実行する場合は、自己署名CA証明書をすべてのクライアントに配布し、有効な証明書のローカルリストを更新できます。

各クライアントで、以下の操作を実行します。

sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.

証明書API

certificates.k8s.ioAPIを用いることで、こちらのドキュメントにあるように、認証に使用するx509証明書をプロビジョニングすることができます。

3.11.3 - リソースの管理

アプリケーションをデプロイし、Serviceを介して外部に公開できました。さて、どうしますか?Kubernetesは、スケーリングや更新など、アプリケーションのデプロイを管理するための多くのツールを提供します。 我々が取り上げる機能についての詳細は設定ファイルラベルについて詳細に説明します。

リソースの設定を管理する

多くのアプリケーションではDeploymentやServiceなど複数のリソースの作成を要求します。複数のリソースの管理は、同一のファイルにひとまとめにしてグループ化すると簡単になります(YAMLファイル内で---で区切る)。 例えば:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

複数のリソースは単一のリソースと同様の方法で作成できます。

kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml
service/my-nginx-svc created
deployment.apps/my-nginx created

リソースは、ファイル内に記述されている順番通りに作成されます。そのため、Serviceを最初に指定するのが理想です。スケジューラーがServiceに関連するPodを、Deploymentなどのコントローラーによって作成されるときに確実に拡散できるようにするためです。

kubectl applyもまた、複数の-fによる引数指定を許可しています。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-svc.yaml -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml

個別のファイルに加えて、-fの引数としてディレクトリ名も指定できます:

kubectl apply -f https://k8s.io/examples/application/nginx/

kubectl.yaml.yml.jsonといったサフィックスの付くファイルを読み込みます。

同じマイクロサービス、アプリケーションティアーのリソースは同一のファイルにまとめ、アプリケーションに関するファイルをグループ化するために、それらのファイルを同一のディレクトリに配備するのを推奨します。アプリケーションのティアーがDNSを通じて互いにバインドされると、アプリケーションスタックの全てのコンポーネントをひとまとめにして簡単にデプロイできます。

リソースの設定ソースとして、URLも指定できます。githubから取得した設定ファイルから直接手軽にデプロイができます:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

kubectlによる一括操作

kubectlが一括して実行できる操作はリソースの作成のみではありません。作成済みのリソースの削除などの他の操作を実行するために、設定ファイルからリソース名を取得することができます。

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

2つのリソースだけを削除する場合には、コマンドラインでリソース/名前というシンタックスを使うことで簡単に指定できます。

kubectl delete deployments/my-nginx services/my-nginx-svc

さらに多くのリソースに対する操作では、リソースをラベルでフィルターするために-l--selectorを使ってセレクター(ラベルクエリ)を指定するのが簡単です:

kubectl delete deployment,services -l app=nginx
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

kubectlは同様のシンタックスでリソース名を出力するので、$()xargsを使ってパイプで操作するのが容易です:

kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service)
NAME           TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)      AGE
my-nginx-svc   LoadBalancer   10.0.0.208   <pending>     80/TCP       0s

上記のコマンドで、最初にexamples/application/nginx/配下でリソースを作成し、-o nameという出力フォーマットにより、作成されたリソースの名前を表示します(各リソースをresource/nameという形式で表示)。そして"service"のみgrepし、kubectl getを使って表示させます。

あるディレクトリ内の複数のサブディレクトリをまたいでリソースを管理するような場合、--filename,-fフラグと合わせて--recursive-Rを指定することでサブディレクトリに対しても再帰的に操作が可能です。

例えば、開発環境用に必要な全てのマニフェストをリソースタイプによって整理しているproject/k8s/developmentというディレクトリがあると仮定します。

project/k8s/development
├── configmap
│   └── my-configmap.yaml
├── deployment
│   └── my-deployment.yaml
└── pvc
    └── my-pvc.yaml

デフォルトでは、project/k8s/developmentにおける一括操作は、どのサブディレクトリも処理せず、ディレクトリの第1階層で処理が止まります。下記のコマンドによってこのディレクトリ配下でリソースを作成しようとすると、エラーが発生します。

kubectl apply -f project/k8s/development
error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin)

代わりに、下記のように--filename,-fフラグと合わせて--recursive-Rを指定してください:

kubectl apply -f project/k8s/development --recursive
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

--recursiveフラグはkubectl {create,get,delete,describe,rollout}などのような--filename,-fフラグを扱うどの操作でも有効です。

また、--recursiveフラグは複数の-fフラグの引数を指定しても有効です。

kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive
namespace/development created
namespace/staging created
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

kubectlについてさらに知りたい場合は、コマンドラインツール(kubectl)を参照してください。

ラベルを有効に使う

これまで取り上げた例では、リソースに対して最大1つのラベルを適用してきました。リソースのセットを他のセットと区別するために、複数のラベルが必要な状況があります。

例えば、異なるアプリケーション間では、異なるappラベルを使用したり、ゲストブックの例のようなマルチティアーのアプリケーションでは、各ティアーを区別する必要があります。frontendというティアーでは下記のラベルを持ちます。:

     labels:
        app: guestbook
        tier: frontend

Redisマスターやスレーブでは異なるtierラベルを持ち、加えてroleラベルも持つことでしょう。:

     labels:
        app: guestbook
        tier: backend
        role: master

そして

     labels:
        app: guestbook
        tier: backend
        role: slave

ラベルを使用すると、ラベルで指定された任意の次元に沿ってリソースを分割できます。

kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME                           READY     STATUS    RESTARTS   AGE       APP         TIER       ROLE
guestbook-fe-4nlpb             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-ght6d             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-jpy62             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-redis-master-5pg3b   1/1       Running   0          1m        guestbook   backend    master
guestbook-redis-slave-2q2yf    1/1       Running   0          1m        guestbook   backend    slave
guestbook-redis-slave-qgazl    1/1       Running   0          1m        guestbook   backend    slave
my-nginx-divi2                 1/1       Running   0          29m       nginx       <none>     <none>
my-nginx-o0ef1                 1/1       Running   0          29m       nginx       <none>     <none>
kubectl get pods -lapp=guestbook,role=slave
NAME                          READY     STATUS    RESTARTS   AGE
guestbook-redis-slave-2q2yf   1/1       Running   0          3m
guestbook-redis-slave-qgazl   1/1       Running   0          3m

Canary deployments カナリアデプロイ

複数のラベルが必要な他の状況として、異なるリリース間でのDeploymentや、同一コンポーネントの設定を区別することが挙げられます。よく知られたプラクティスとして、本番環境の実際のトラフィックを受け付けるようにするために、新しいリリースを完全にロールアウトする前に、新しいカナリア版のアプリケーションを過去のリリースと合わせてデプロイする方法があります。

例えば、異なるリリースバージョンを分けるためにtrackラベルを使用できます。

主要な安定板のリリースではtrackラベルにstableという値をつけることがあるでしょう。:

     name: frontend
     replicas: 3
     ...
     labels:
        app: guestbook
        tier: frontend
        track: stable
     ...
     image: gb-frontend:v3

そして2つの異なるPodのセットを上書きしないようにするため、trackラベルに異なる値を持つ(例: canary)ようなguestbookフロントエンドの新しいリリースを作成できます。

     name: frontend-canary
     replicas: 1
     ...
     labels:
        app: guestbook
        tier: frontend
        track: canary
     ...
     image: gb-frontend:v4

frontend Serviceは、トラフィックを両方のアプリケーションにリダイレクトさせるために、両方のアプリケーションに共通したラベルのサブセットを選択して両方のレプリカを扱えるようにします。:

  selector:
     app: guestbook
     tier: frontend

安定版とカナリア版リリースで本番環境の実際のトラフィックを転送する割合を決めるため、双方のレプリカ数を変更できます(このケースでは3対1)。 最新版のリリースをしても大丈夫な場合、安定版のトラックを新しいアプリケーションにして、カナリア版を削除します。

さらに具体的な例については、tutorial of deploying Ghostを参照してください。

ラベルの更新

新しいリソースを作成する前に、既存のPodと他のリソースのラベルの変更が必要な状況があります。これはkubectl labelで実行できます。 例えば、全てのnginx Podを frontendティアーとしてラベル付けするには、下記のコマンドを実行するのみです。

kubectl label pods -l app=nginx tier=fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled

これは最初に"app=nginx"というラベルのついたPodをフィルターし、そのPodに対して"tier=fe"というラベルを追加します。 ラベル付けしたPodを確認するには、下記のコマンドを実行してください。

kubectl get pods -l app=nginx -L tier
NAME                        READY     STATUS    RESTARTS   AGE       TIER
my-nginx-2035384211-j5fhi   1/1       Running   0          23m       fe
my-nginx-2035384211-u2c7e   1/1       Running   0          23m       fe
my-nginx-2035384211-u3t6x   1/1       Running   0          23m       fe

このコマンドでは"app=nginx"というラベルのついた全てのPodを出力し、Podのtierという項目も表示します(-Lまたは--label-columnsで指定)。

さらなる情報は、ラベルkubectl labelを参照してください。

アノテーションの更新

リソースに対してアノテーションを割り当てたい状況があります。アノテーションは、ツール、ライブラリなどのAPIクライアントによって取得するための任意の非識別メタデータです。アノテーションの割り当てはkubectl annotateで可能です。例:

kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx'
kubectl get pods my-nginx-v4-9gw19 -o yaml
apiVersion: v1
kind: pod
metadata:
  annotations:
    description: my frontend running nginx
...

さらなる情報は、アノテーション や、kubectl annotateを参照してください。

アプリケーションのスケール

アプリケーションの負荷が増減するとき、kubectlを使って簡単にスケールできます。例えば、nginxのレプリカを3から1に減らす場合、下記を実行します:

kubectl scale deployment/my-nginx --replicas=1
deployment.apps/my-nginx scaled

実行すると、Deploymentによって管理されるPod数が1となります。

kubectl get pods -l app=nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

システムに対してnginxのレプリカ数を自動で選択させるには、下記のように1から3の範囲で指定します。:

kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

実行すると、nginxのレプリカは必要に応じて自動でスケールアップ、スケールダウンします。

さらなる情報は、kubectl scalekubectl autoscale and horizontal pod autoscalerを参照してください。

リソースの直接的アップデート

場合によっては、作成したリソースに対して処理を中断させずに更新を行う必要があります。

kubectl apply

開発者が設定するリソースをコードとして管理しバージョニングも行えるように、設定ファイルのセットをソースによって管理する方法が推奨されています。 この場合、クラスターに対して設定の変更をプッシュするためにkubectl applyを使用できます。

このコマンドは、リソース設定の過去のバージョンと、今適用した変更を比較し、差分に現れないプロパティーに対して上書き変更することなくクラスターに適用させます。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

注意として、前回の変更適用時からの設定の変更内容を決めるため、kubectl applyはリソースに対してアノテーションを割り当てます。変更が実施されるとkubectl applyは、1つ前の設定内容と、今回変更しようとする入力内容と、現在のリソースの設定との3つの間で変更内容の差分をとります。

現在、リソースはこのアノテーションなしで作成されました。そのため、最初のkubectl paplyの実行においては、与えられた入力と、現在のリソースの設定の2つの間の差分が取られ、フォールバックします。この最初の実行の間、リソースが作成された時にプロパティーセットの削除を検知できません。この理由により、プロパティーの削除はされません。

kubectl applyの実行後の全ての呼び出しや、kubectl replacekubectl editなどの設定を変更する他のコマンドではアノテーションを更新します。kubectl applyした後の全ての呼び出しにおいて3-wayの差分取得によってプロパティの検知と削除を実施します。

kubectl edit

その他に、kubectl editによってリソースの更新もできます。:

kubectl edit deployment/my-nginx

このコマンドは、最初にリソースをgetし、テキストエディタでリソースを編集し、更新されたバージョンでリソースをapplyします。:

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml
vi /tmp/nginx.yaml
# yamlファイルを編集し、ファイルを保存します。

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

このコマンドによってより重大な変更を簡単に行えます。注意として、あなたのEDITORKUBE_EDITORといった環境変数も指定できます。

さらなる情報は、kubectl editを参照してください。

kubectl patch

APIオブジェクトの更新にはkubectl patchを使うことができます。このコマンドはJSON patch、JSON merge patch、戦略的merge patchをサポートしています。 kubectl patchを使ったAPIオブジェクトの更新kubectl patchを参照してください。

破壊的なアップデート

一度初期化された後、更新できないようなリソースフィールドの更新が必要な場合や、Deploymentによって作成され、壊れている状態のPodを修正するなど、再帰的な変更を即座に行いたい場合があります。このようなフィールドを変更するため、リソースの削除と再作成を行うreplace --forceを使用してください。このケースでは、シンプルに元の設定ファイルを修正するのみです。:

kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force
deployment.apps/my-nginx deleted
deployment.apps/my-nginx replaced

サービス停止なしでアプリケーションを更新する

ある時点で、前述したカナリアデプロイのシナリオにおいて、新しいイメージやイメージタグを指定することによって、デプロイされたアプリケーションを更新が必要な場合があります。kubectlではいくつかの更新操作をサポートしており、それぞれの操作が異なるシナリオに対して適用可能です。

ここでは、Deploymentを使ってアプリケーションの作成と更新についてガイドします。

まずnginxのバージョン1.14.2を稼働させていると仮定します。:

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

レプリカ数を3にします(新旧のリビジョンは混在します)。:

kubectl scale deployment my-nginx --current-replicas=1 --replicas=3
deployment.apps/my-nginx scaled

バージョン1.16.1に更新するには、上述したkubectlコマンドを使って.spec.template.spec.containers[0].imageの値をnginx:1.14.2からnginx:1.16.1に変更するだけでできます。

kubectl edit deployment/my-nginx

できました!Deploymentはデプロイされたnginxのアプリケーションを宣言的にプログレッシブに更新します。更新途中では、決まった数の古いレプリカのみダウンし、一定数の新しいレプリカが希望するPod数以上作成されても良いことを保証します。詳細について学ぶにはDeployment pageを参照してください。

次の項目

3.11.4 - クラスターのネットワーク

ネットワークはKubernetesにおける中心的な部分ですが、どのように動作するかを正確に理解することは難解な場合もあります。 Kubernetesには、4つの異なる対応すべきネットワークの問題があります:

  1. 高度に結合されたコンテナ間の通信: これは、Podおよびlocalhost通信によって解決されます。
  2. Pod間の通信: 本ドキュメントの主な焦点です。
  3. Podからサービスへの通信: これはServiceでカバーされています。
  4. 外部からサービスへの通信: これはServiceでカバーされています。

Kubernetesは、言ってしまえばアプリケーション間でマシンを共有するためのものです。通常、マシンを共有するには、2つのアプリケーションが同じポートを使用しないようにする必要があります。 複数の開発者間でのポートの調整は、大規模に行うことが非常に難しく、ユーザーが制御できないクラスターレベルの問題に直面することになります。

動的ポート割り当てはシステムに多くの複雑さをもたらします。すべてのアプリケーションはポートをフラグとして受け取らなければならない、APIサーバーは設定ブロックに動的ポート番号を挿入する方法を知っていなければならない、各サービスは互いを見つける方法を知らなければならない、などです。Kubernetesはこれに対処するのではなく、別のアプローチを取ります。

Kubernetesネットワークモデルについては、こちらを参照してください。

Kubernetesネットワークモデルの実装方法

ネットワークモデルは、各ノード上のコンテナランタイムによって実装されます。最も一般的なコンテナランタイムは、Container Network Interface (CNI)プラグインを使用して、ネットワークとセキュリティ機能を管理します。CNIプラグインは、さまざまなベンダーから多数提供されています。これらの中には、ネットワークインターフェースの追加と削除という基本的な機能のみを提供するものもあれば、他のコンテナオーケストレーションシステムとの統合、複数のCNIプラグインの実行、高度なIPAM機能など、より洗練されたソリューションを提供するものもあります。

Kubernetesがサポートするネットワークアドオンの非網羅的なリストについては、このページを参照してください。

次の項目

ネットワークモデルの初期設計とその根拠、および将来の計画については、ネットワーク設計ドキュメントで詳細に説明されています。

3.11.5 - ロギングのアーキテクチャ

アプリケーションログは、アプリケーション内で何が起こっているかを理解するのに役立ちます。ログは、問題のデバッグとクラスターアクティビティの監視に特に役立ちます。最近のほとんどのアプリケーションには、何らかのロギングメカニズムがあります。同様に、コンテナエンジンはロギングをサポートするように設計されています。コンテナ化されたアプリケーションで、最も簡単で最も採用されているロギング方法は、標準出力と標準エラーストリームへの書き込みです。

ただし、コンテナエンジンまたはランタイムによって提供されるネイティブ機能は、たいていの場合、完全なロギングソリューションには十分ではありません。

たとえば、コンテナがクラッシュした場合やPodが削除された場合、またはノードが停止した場合に、アプリケーションのログにアクセスしたい場合があります。

クラスターでは、ノードやPod、またはコンテナに関係なく、ノードに個別のストレージとライフサイクルが必要です。この概念は、クラスターレベルロギング と呼ばれます。

クラスターレベルロギングのアーキテクチャでは、ログを保存、分析、およびクエリするための個別のバックエンドが必要です。Kubernetesは、ログデータ用のネイティブストレージソリューションを提供していません。代わりに、Kubernetesに統合される多くのロギングソリューションがあります。次のセクションでは、ノードでログを処理および保存する方法について説明します。

Kubernetesでの基本的なロギング

この例では、1秒に1回標準出力ストリームにテキストを書き込むコンテナを利用する、Pod specificationを使います。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

このPodを実行するには、次のコマンドを使用します:

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

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

pod/counter created

ログを取得するには、以下のようにkubectl logsコマンドを使用します:

kubectl logs counter

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

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...

コンテナの以前のインスタンスからログを取得するために、kubectl logs --previousを使用できます。Podに複数のコンテナがある場合は、次のように-cフラグでコマンドにコンテナ名を追加することで、アクセスするコンテナのログを指定します。

kubectl logs counter -c count

詳細については、kubectl logsドキュメントを参照してください。

ノードレベルでのロギング

Node level logging

コンテナエンジンは、生成された出力を処理して、コンテナ化されたアプリケーションのstdoutstderrストリームにリダイレクトします。たとえば、Dockerコンテナエンジンは、これら2つのストリームをロギングドライバーにリダイレクトします。ロギングドライバーは、JSON形式でファイルに書き込むようにKubernetesで設定されています。

デフォルトでは、コンテナが再起動すると、kubeletは1つの終了したコンテナをログとともに保持します。Podがノードから削除されると、対応する全てのコンテナが、ログとともに削除されます。

ノードレベルロギングでの重要な考慮事項は、ノードで使用可能な全てのストレージをログが消費しないように、ログローテーションを実装することです。Kubernetesはログのローテーションを担当しませんが、デプロイツールでそれに対処するソリューションを構築する必要があります。たとえば、kube-up.shスクリプトによってデプロイされたKubernetesクラスターには、1時間ごとに実行するように構成されたlogrotateツールがあります。アプリケーションのログを自動的にローテーションするようにコンテナランタイムを構築することもできます。

例として、configure-helper scriptに対応するスクリプトであるkube-up.shが、どのようにGCPでCOSイメージのロギングを構築しているかについて、詳細な情報を見つけることができます。

CRIコンテナランタイムを使用する場合、kubeletはログのローテーションとログディレクトリ構造の管理を担当します。kubeletはこの情報をCRIコンテナランタイムに送信し、ランタイムはコンテナログを指定された場所に書き込みます。2つのkubeletパラメーター、container-log-max-sizecontainer-log-max-fileskubelet設定ファイルで使うことで、各ログファイルの最大サイズと各コンテナで許可されるファイルの最大数をそれぞれ設定できます。

基本的なロギングの例のように、kubectl logsを実行すると、ノード上のkubeletがリクエストを処理し、ログファイルから直接読み取ります。kubeletはログファイルの内容を返します。

システムコンポーネントログ

システムコンポーネントには、コンテナ内で実行されるものとコンテナ内で実行されないものの2種類があります。例えば以下のとおりです。

  • Kubernetesスケジューラーとkube-proxyはコンテナ内で実行されます。
  • kubeletとコンテナランタイムはコンテナ内で実行されません。

systemdを搭載したマシンでは、kubeletとコンテナランタイムがjournaldに書き込みます。systemdが存在しない場合、kubeletとコンテナランタイムはvar/logディレクトリ内の.logファイルに書き込みます。コンテナ内のシステムコンポーネントは、デフォルトのロギングメカニズムを迂回して、常に/var/logディレクトリに書き込みます。それらはklogというロギングライブラリを使用します。これらのコンポーネントのロギングの重大性に関する規則は、development docs on loggingに記載されています。

コンテナログと同様に、/var/logディレクトリ内のシステムコンポーネントログはローテーションする必要があります。kube-up.shスクリプトによって生成されたKubernetesクラスターでは、これらのログは、logrotateツールによって毎日、またはサイズが100MBを超えた時にローテーションされるように設定されています。

クラスターレベルロギングのアーキテクチャ

Kubernetesはクラスターレベルロギングのネイティブソリューションを提供していませんが、検討可能な一般的なアプローチがいくつかあります。ここにいくつかのオプションを示します:

  • 全てのノードで実行されるノードレベルのロギングエージェントを使用します。
  • アプリケーションのPodにログインするための専用のサイドカーコンテナを含めます。
  • アプリケーション内からバックエンドに直接ログを送信します。

ノードロギングエージェントの使用

Using a node level logging agent

各ノードに ノードレベルのロギングエージェント を含めることで、クラスターレベルロギングを実装できます。ロギングエージェントは、ログを公開したり、ログをバックエンドに送信したりする専用のツールです。通常、ロギングエージェントは、そのノード上の全てのアプリケーションコンテナからのログファイルを含むディレクトリにアクセスできるコンテナです。

ロギングエージェントは全てのノードで実行する必要があるため、エージェントをDaemonSetとして実行することをおすすめします。

ノードレベルのロギングは、ノードごとに1つのエージェントのみを作成し、ノードで実行されているアプリケーションに変更を加える必要はありません。

コンテナはstdoutとstderrに書き込みますが、合意された形式はありません。ノードレベルのエージェントはこれらのログを収集し、集約のために転送します。

ロギングエージェントでサイドカーコンテナを使用する

サイドカーコンテナは、次のいずれかの方法で使用できます:

  • サイドカーコンテナは、アプリケーションログを自身のstdoutにストリーミングします。
  • サイドカーコンテナは、アプリケーションコンテナからログを取得するように設定されたロギングエージェントを実行します。

ストリーミングサイドカーコンテナ

Sidecar container with a streaming container

サイドカーコンテナに自身のstdoutstderrストリームへの書き込みを行わせることで、各ノードですでに実行されているkubeletとロギングエージェントを利用できます。サイドカーコンテナは、ファイル、ソケット、またはjournaldからログを読み取ります。各サイドカーコンテナは、ログを自身のstdoutまたはstderrストリームに出力します。

このアプローチにより、stdoutまたはstderrへの書き込みのサポートが不足している場合も含め、アプリケーションのさまざまな部分からいくつかのログストリームを分離できます。ログのリダイレクトの背後にあるロジックは最小限であるため、大きなオーバーヘッドにはなりません。さらに、stdoutstderrはkubeletによって処理されるため、kubectl logsのような組み込みツールを使用できます。

たとえば、Podは単一のコンテナを実行し、コンテナは2つの異なる形式を使用して2つの異なるログファイルに書き込みます。Podの構成ファイルは次のとおりです:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

両方のコンポーネントをコンテナのstdoutストリームにリダイレクトできたとしても、異なる形式のログエントリを同じログストリームに書き込むことはおすすめしません。代わりに、2つのサイドカーコンテナを作成できます。各サイドカーコンテナは、共有ボリュームから特定のログファイルを追跡し、ログを自身のstdoutストリームにリダイレクトできます。

2つのサイドカーコンテナを持つPodの構成ファイルは次のとおりです:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-1
    image: busybox
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

これで、このPodを実行するときに、次のコマンドを実行して、各ログストリームに個別にアクセスできます:

kubectl logs counter count-log-1

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

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...
kubectl logs counter count-log-2

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

Mon Jan  1 00:00:00 UTC 2001 INFO 0
Mon Jan  1 00:00:01 UTC 2001 INFO 1
Mon Jan  1 00:00:02 UTC 2001 INFO 2
...

クラスターにインストールされているノードレベルのエージェントは、それ以上の設定を行わなくても、これらのログストリームを自動的に取得します。必要があれば、ソースコンテナに応じてログをパースするようにエージェントを構成できます。

CPUとメモリーの使用量が少ない(CPUの場合は数ミリコアのオーダー、メモリーの場合は数メガバイトのオーダー)にも関わらず、ログをファイルに書き込んでからstdoutにストリーミングすると、ディスクの使用量が2倍になる可能性があることに注意してください。単一のファイルに書き込むアプリケーションがある場合は、ストリーミングサイドカーコンテナアプローチを実装するのではなく、/dev/stdoutを宛先として設定することをおすすめします。

サイドカーコンテナを使用して、アプリケーション自体ではローテーションできないログファイルをローテーションすることもできます。このアプローチの例は、logrotateを定期的に実行する小さなコンテナです。しかし、stdoutstderrを直接使い、ローテーションと保持のポリシーをkubeletに任せることをおすすめします。

ロギングエージェントを使用したサイドカーコンテナ

Sidecar container with a logging agent

ノードレベルロギングのエージェントが、あなたの状況に必要なだけの柔軟性を備えていない場合は、アプリケーションで実行するように特別に構成した別のロギングエージェントを使用してサイドカーコンテナを作成できます。

ロギングエージェントを使用したサイドカーコンテナを実装するために使用できる、2つの構成ファイルを次に示します。最初のファイルには、fluentdを設定するためのConfigMapが含まれています。

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>    

2番目のファイルは、fluentdを実行しているサイドカーコンテナを持つPodを示しています。Podは、fluentdが構成データを取得できるボリュームをマウントします。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-agent
    image: registry.k8s.io/fluentd-gcp:1.30
    env:
    - name: FLUENTD_ARGS
      value: -c /etc/fluentd-config/fluentd.conf
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: config-volume
      mountPath: /etc/fluentd-config
  volumes:
  - name: varlog
    emptyDir: {}
  - name: config-volume
    configMap:
      name: fluentd-config

サンプル構成では、fluentdを任意のロギングエージェントに置き換えて、アプリケーションコンテナ内の任意のソースから読み取ることができます。

アプリケーションから直接ログを公開する

Exposing logs directly from the application

すべてのアプリケーションから直接ログを公開または送信するクラスターロギングは、Kubernetesのスコープ外です。

3.11.6 - システムログ

システムコンポーネントのログは、クラスター内で起こったイベントを記録します。このログはデバッグのために非常に役立ちます。ログのverbosityを設定すると、ログをどの程度詳細に見るのかを変更できます。ログはコンポーネント内のエラーを表示する程度の荒い粒度にすることも、イベントのステップバイステップのトレース(HTTPのアクセスログ、Podの状態の変更、コントローラーの動作、スケジューラーの決定など)を表示するような細かい粒度に設定することもできます。

klog

klogは、Kubernetesのログライブラリです。klogは、Kubernetesのシステムコンポーネント向けのログメッセージを生成します。

klogの設定に関する詳しい情報については、コマンドラインツールのリファレンスを参照してください。

klogネイティブ形式の例:

I1025 00:15:15.525108       1 httplog.go:79] GET /api/v1/namespaces/kube-system/pods/metrics-server-v0.3.1-57c75779f-9p8wg: (1.512ms) 200 [pod_nanny/v0.0.0 (linux/amd64) kubernetes/$Format 10.56.1.19:51756]

構造化ログ

FEATURE STATE: Kubernetes v1.19 [alpha]

構造化ログは、ログメッセージに単一の構造を導入し、プログラムで情報の抽出ができるようにするものです。構造化ログは、僅かな労力とコストで保存・処理できます。新しいメッセージ形式は後方互換性があり、デフォルトで有効化されます。

構造化ログの形式:

<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...

例:

I1025 00:15:15.525108       1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"

JSONログ形式

FEATURE STATE: Kubernetes v1.19 [alpha]

--logging-format=jsonフラグは、ログの形式をネイティブ形式klogからJSON形式に変更します。以下は、JSONログ形式の例(pretty printしたもの)です。

{
   "ts": 1580306777.04728,
   "v": 4,
   "msg": "Pod status updated",
   "pod":{
      "name": "nginx-1",
      "namespace": "default"
   },
   "status": "ready"
}

特別な意味を持つキー:

  • ts - Unix時間のタイムスタンプ(必須、float)
  • v - verbosity (必須、int、デフォルトは0)
  • err - エラー文字列 (オプション、string)
  • msg - メッセージ (必須、string)

現在サポートされているJSONフォーマットの一覧:

ログのサニタイズ

FEATURE STATE: Kubernetes v1.20 [alpha]

--experimental-logging-sanitizationフラグはklogのサニタイズフィルタを有効にします。有効にすると、すべてのログの引数が機密データ(パスワード、キー、トークンなど)としてタグ付けされたフィールドについて検査され、これらのフィールドのログの記録は防止されます。

現在ログのサニタイズをサポートしているコンポーネント一覧:

  • kube-controller-manager
  • kube-apiserver
  • kube-scheduler
  • kubelet

ログのverbosityレベル

-vフラグはログのverbosityを制御します。値を増やすとログに記録されるイベントの数が増えます。値を減らすとログに記録されるイベントの数が減ります。verbosityの設定を増やすと、ますます多くの深刻度の低いイベントをログに記録するようになります。verbosityの設定を0にすると、クリティカルなイベントだけをログに記録します。

ログの場所

システムコンポーネントには2種類あります。コンテナ内で実行されるコンポーネントと、コンテナ内で実行されないコンポーネントです。たとえば、次のようなコンポーネントがあります。

  • Kubernetesのスケジューラーやkube-proxyはコンテナ内で実行されます。
  • kubeletやDockerのようなコンテナランタイムはコンテナ内で実行されません。

systemdを使用しているマシンでは、kubeletとコンテナランタイムはjournaldに書き込みを行います。それ以外のマシンでは、/var/logディレクトリ内の.logファイルに書き込みます。コンテナ内部のシステムコンポーネントは、デフォルトのログ機構をバイパスするため、常に/var/logディレクトリ内の.logファイルに書き込みます。コンテナのログと同様に、/var/logディレクトリ内のシステムコンポーネントのログはローテートする必要があります。kube-up.shスクリプトによって作成されたKubernetesクラスターでは、ログローテーションはlogrotateツールで設定されます。logrotateツールはログを1日ごとまたはログのサイズが100MBを超えたときにローテートします。

次の項目

3.11.7 - Kubernetesのプロキシ

このページではKubernetesと併用されるプロキシについて説明します。

プロキシ

Kubernetesを使用する際に、いくつかのプロキシを使用する場面があります。

  1. kubectlのプロキシ:

    • ユーザーのデスクトップ上かPod内で稼働します
    • ローカルホストのアドレスからKubernetes apiserverへのプロキシを行います
    • クライアントからプロキシ間ではHTTPを使用します
    • プロキシからapiserverへはHTTPSを使用します
    • apiserverの場所を示します
    • 認証用のヘッダーを追加します
  2. apiserverのプロキシ:

    • apiserver内で動作する踏み台となります
    • これがなければ到達不可能であるクラスターIPへ、クラスターの外部からのユーザーを接続します
    • apiserverのプロセス内で稼働します
    • クライアントからプロキシ間ではHTTPSを使用します(apiserverの設定により、HTTPを使用します)
    • プロキシからターゲット間では利用可能な情報を使用して、プロキシによって選択されたHTTPかHTTPSのいずれかを使用します
    • Node、Pod、Serviceへ到達するのに使えます
    • Serviceへ到達するときは負荷分散を行います
  3. kube proxy:

    • 各ノード上で稼働します
    • UDP、TCP、SCTPをプロキシします
    • HTTPを解釈しません
    • 負荷分散機能を提供します
    • Serviceへ到達させるためのみに使用されます
  4. apiserverの前段にあるプロキシ/ロードバランサー:

    • 実際に存在するかどうかと実装はクラスターごとに異なります(例: nginx)
    • 全てのクライアントと、1つ以上のapiserverの間に位置します
    • 複数のapiserverがあるときロードバランサーとして稼働します
  5. 外部サービス上で稼働するクラウドロードバランサー:

    • いくつかのクラウドプロバイダーによって提供されます(例: AWS ELB、Google Cloud Load Balancer)
    • LoadBalancerというtypeのKubernetes Serviceが作成されたときに自動で作成されます
    • たいていのクラウドロードバランサーはUDP/TCPのみサポートしています
    • SCTPのサポートはクラウドプロバイダーのロードバランサーの実装によって異なります
    • ロードバランサーの実装はクラウドプロバイダーによって異なります

Kubernetesユーザーのほとんどは、最初の2つのタイプ以外に心配する必要はありません。クラスター管理者はそれ以外のタイプのロードバランサーを正しくセットアップすることを保証します。

リダイレクトの要求

プロキシはリダイレクトの機能を置き換えました。リダイレクトの使用は非推奨となります。

3.11.8 - アドオンのインストール

アドオンはKubernetesの機能を拡張するものです。

このページでは、利用可能なアドオンの一部の一覧と、それぞれのアドオンのインストール方法へのリンクを提供します。この一覧は、すべてを網羅するものではありません。

ネットワークとネットワークポリシー

  • ACIは、統合されたコンテナネットワークとネットワークセキュリティをCisco ACIを使用して提供します。
  • Antreaは、L3またはL4で動作して、Open vSwitchをネットワークデータプレーンとして活用する、Kubernetes向けのネットワークとセキュリティサービスを提供します。
  • Calicoはネットワークとネットワークポリシーのプロバイダーです。Calicoは、BGPを使用または未使用の非オーバーレイおよびオーバーレイネットワークを含む、フレキシブルなさまざまなネットワークオプションをサポートします。Calicoはホスト、Pod、そして(IstioとEnvoyを使用している場合には)サービスメッシュ上のアプリケーションに対してネットワークポリシーを強制するために、同一のエンジンを使用します。
  • CanalはFlannelとCalicoをあわせたもので、ネットワークとネットワークポリシーを提供します。
  • Ciliumは、L3のネットワークとネットワークポリシーのプラグインで、HTTP/API/L7のポリシーを透過的に強制できます。ルーティングとoverlay/encapsulationモードの両方をサポートしており、他のCNIプラグイン上で機能できます。
  • CNI-Genieは、KubernetesをCalico、Canal、Flannel、Weaveなど選択したCNIプラグインをシームレスに接続できるようにするプラグインです。
  • Contivは、さまざまなユースケースと豊富なポリシーフレームワーク向けに設定可能なネットワーク(BGPを使用したネイティブのL3、vxlanを使用したオーバーレイ、古典的なL2、Cisco-SDN/ACI)を提供します。Contivプロジェクトは完全にオープンソースです。インストーラーはkubeadmとkubeadm以外の両方をベースとしたインストールオプションがあります。
  • Contrailは、Tungsten Fabricをベースにしている、オープンソースでマルチクラウドに対応したネットワーク仮想化およびポリシー管理プラットフォームです。ContrailおよびTungsten Fabricは、Kubernetes、OpenShift、OpenStack、Mesosなどのオーケストレーションシステムと統合されており、仮想マシン、コンテナ/Pod、ベアメタルのワークロードに隔離モードを提供します。
  • Flannelは、Kubernetesで使用できるオーバーレイネットワークプロバイダーです。
  • Knitterは、1つのKubernetes Podで複数のネットワークインターフェースをサポートするためのプラグインです。
  • Multusは、すべてのCNIプラグイン(たとえば、Calico、Cilium、Contiv、Flannel)に加えて、SRIOV、DPDK、OVS-DPDK、VPPをベースとするKubernetes上のワークロードをサポートする、複数のネットワークサポートのためのマルチプラグインです。
  • OVN-Kubernetesは、Open vSwitch(OVS)プロジェクトから生まれた仮想ネットワーク実装であるOVN(Open Virtual Network)をベースとする、Kubernetesのためのネットワークプロバイダーです。OVN-Kubernetesは、OVSベースのロードバランサーおよびネットワークポリシーの実装を含む、Kubernetes向けのオーバーレイベースのネットワーク実装を提供します。
  • Nodusは、OVNベースのCNIコントローラープラグインで、クラウドネイティブベースのService function chaining(SFC)を提供します。
  • NSX-T Container Plug-in(NCP)は、VMware NSX-TとKubernetesなどのコンテナオーケストレーター間のインテグレーションを提供します。また、NSX-Tと、Pivotal Container Service(PKS)とOpenShiftなどのコンテナベースのCaaS/PaaSプラットフォームとのインテグレーションも提供します。
  • Nuageは、Kubernetes Podと非Kubernetes環境間で可視化とセキュリティモニタリングを使用してポリシーベースのネットワークを提供するSDNプラットフォームです。
  • Romanaは、NetworkPolicy APIもサポートするPodネットワーク向けのL3のネットワークソリューションです。
  • Weave Netは、ネットワークパーティションの両面で機能し、外部データベースを必要とせずに、ネットワークとネットワークポリシーを提供します。

サービスディスカバリー

  • CoreDNSは、フレキシブルで拡張可能なDNSサーバーです。Pod向けのクラスター内DNSとしてインストールできます。

可視化と制御

  • DashboardはKubernetes向けのダッシュボードを提供するウェブインターフェースです。
  • Weave Scopeは、コンテナ、Pod、Serviceなどをグラフィカルに可視化するツールです。Weave Cloud accountと組み合わせて使うか、UIを自分でホストして使います。

インフラストラクチャ

  • KubeVirtは仮想マシンをKubernetes上で実行するためのアドオンです。通常、ベアメタルのクラスターで実行します。
  • node problem detectorはLinuxノード上で動作し、システムの問題をEventまたはノードのConditionとして報告します。

レガシーなアドオン

いくつかのアドオンは、廃止されたcluster/addonsディレクトリに掲載されています。

よくメンテナンスされたアドオンはここにリンクしてください。PRを歓迎しています。

3.12 - Kubernetesを拡張する

Kubernetesクラスターの挙動を変えるいろいろな方法

Kubernetesは柔軟な設定が可能で、高い拡張性を持っています。 結果として、Kubernetesのプロジェクトソースコードをフォークしたり、パッチを当てて利用することは滅多にありません。 このガイドは、Kubernetesクラスターをカスタマイズするための選択肢を記載します。 管理しているKubernetesクラスターを、動作環境の要件にどのように適合させるべきかを理解したいクラスター管理者を対象にしています。 将来の プラットフォーム開発者 、またはKubernetesプロジェクトのコントリビューターにとっても、どのような拡張のポイントやパターンが存在するのか、また、それぞれのトレードオフや制限事項を学ぶための導入として役立つでしょう。

概要

カスタマイズのアプローチには大きく分けて、フラグ、ローカル設定ファイル、またはAPIリソースの変更のみを含んだ 設定 と、稼働しているプログラムまたはサービスも含んだ 拡張 があります。このドキュメントでは、主に拡張について説明します。

設定

設定ファイルフラグ はオンラインドキュメントのリファレンスセクションの中の、各項目に記載されています:

ホスティングされたKubernetesサービスやマネージドなKubernetesでは、フラグと設定ファイルが常に変更できるとは限りません。変更可能な場合でも、通常はクラスターの管理者のみが変更できます。また、それらは将来のKubernetesバージョンで変更される可能性があり、設定変更にはプロセスの再起動が必要になるかもしれません。これらの理由により、この方法は他の選択肢が無いときにのみ利用するべきです。

ResourceQuotaPodSecurityPolicyNetworkPolicy、そしてロールベースアクセス制御(RBAC)といった ビルトインポリシーAPI は、ビルトインのKubernetes APIです。APIは通常、ホスティングされたKubernetesサービスやマネージドなKubernetesで利用されます。これらは宣言的で、Podのような他のKubernetesリソースと同じ慣例に従っています。そのため、新しいクラスターの設定は繰り返し再利用することができ、アプリケーションと同じように管理することが可能です。さらに、安定版(stable)を利用している場合、他のKubernetes APIのような定義済みのサポートポリシーを利用することができます。これらの理由により、この方法は、適切な用途の場合、 設定ファイルフラグ よりも好まれます。

拡張

拡張はKubernetesを拡張し、深く統合されたソフトウェアの構成要素です。 これは新しいタイプと、新しい種類のハードウェアをサポートするために利用されます。

ほとんどのクラスター管理者は、ホスティングされている、またはディストリビューションとしてのKubernetesを使っているでしょう。 結果として、ほとんどのKubernetesユーザーは既存の拡張を使えばよいため、新しい拡張を書く必要は無いと言えます。

拡張パターン

Kubernetesは、クライアントのプログラムを書くことで自動化ができるようにデザインされています。 Kubernetes APIに読み書きをするどのようなプログラムも、役に立つ自動化機能を提供することができます。 自動化機能 はクラスター上、またはクラスター外で実行できます。 このドキュメントに後述のガイダンスに従うことで、高い可用性を持つ頑強な自動化機能を書くことができます。 自動化機能は通常、ホスティングされているクラスター、マネージドな環境など、どのKubernetesクラスター上でも動きます。

Kubernetes上でうまく動くクライアントプログラムを書くために、コントローラー パターンという明確なパターンがあります。 コントローラーは通常、オブジェクトの .spec を読み取り、何らかの処理をして、オブジェクトの .status を更新します。

コントローラーはKubernetesのクライアントです。Kubernetesがクライアントとして動き、外部のサービスを呼び出す場合、それは Webhook と呼ばれます。 呼び出されるサービスは Webhookバックエンド と呼ばれます。コントローラーのように、Webhookも障害点を追加します。

Webhookのモデルでは、Kubernetesは外部のサービスを呼び出します。 バイナリプラグイン モデルでは、Kubernetesはバイナリ(プログラム)を実行します。 バイナリプラグインはkubelet(例、FlexVolumeプラグインネットワークプラグイン)、またkubectlで利用されています。

下図は、それぞれの拡張ポイントが、Kubernetesのコントロールプレーンとどのように関わっているかを示しています。

拡張ポイント

この図は、Kubernetesにおける拡張ポイントを示しています。

  1. ユーザーは頻繁にkubectlを使って、Kubernetes APIとやり取りをします。Kubectlプラグインは、kubectlのバイナリを拡張します。これは個別ユーザーのローカル環境のみに影響を及ぼすため、サイト全体にポリシーを強制することはできません。
  2. APIサーバーは全てのリクエストを処理します。APIサーバーのいくつかの拡張ポイントは、リクエストを認可する、コンテキストに基づいてブロックする、リクエストを編集する、そして削除を処理することを可能にします。これらはAPIアクセス拡張セクションに記載されています。
  3. APIサーバーは様々な種類の リソース を扱います。Podのような ビルトインリソース はKubernetesプロジェクトにより定義され、変更できません。ユーザーも、自身もしくは、他のプロジェクトで定義されたリソースを追加することができます。それは カスタムリソース と呼ばれ、カスタムリソースセクションに記載されています。カスタムリソースは度々、APIアクセス拡張と一緒に使われます。
  4. KubernetesのスケジューラーはPodをどのノードに配置するかを決定します。スケジューリングを拡張するには、いくつかの方法があります。それらはスケジューラー拡張セクションに記載されています。
  5. Kubernetesにおける多くの振る舞いは、APIサーバーのクライアントであるコントローラーと呼ばれるプログラムに実装されています。コントローラーは度々、カスタムリソースと共に使われます。
  6. kubeletはサーバー上で実行され、Podが仮想サーバーのようにクラスターネットワーク上にIPを持った状態で起動することをサポートします。ネットワークプラグインがPodのネットワーキングにおける異なる実装を適用することを可能にします。
  7. kubeletはまた、コンテナのためにボリュームをマウント、アンマウントします。新しい種類のストレージはストレージプラグインを通じてサポートされます。

もしあなたがどこから始めるべきかわからない場合、このフローチャートが役立つでしょう。一部のソリューションは、いくつかの種類の拡張を含んでいることを留意してください。

API拡張

ユーザー定義タイプ

新しいコントローラー、アプリケーションの設定に関するオブジェクト、また宣言型APIを定義し、それらをkubectlのようなKubernetesのツールから管理したい場合、Kubernetesにカスタムリソースを追加することを検討して下さい。

カスタムリソースはアプリケーション、ユーザー、監視データのデータストレージとしては使わないで下さい。

カスタムリソースに関するさらなる情報は、カスタムリソースコンセプトガイドを参照して下さい。

新しいAPIと自動化機能の連携

カスタムリソースAPIと制御ループの組み合わせはオペレーターパターンと呼ばれています。オペレーターパターンは、通常ステートフルな特定のアプリケーションを管理するために利用されます。これらのカスタムAPIと制御ループは、ストレージ、またはポリシーのような他のリソースを管理するためにも利用されます。

ビルトインリソースの変更

カスタムリソースを追加し、KubernetesAPIを拡張する場合、新たに追加されたリソースは常に新しいAPIグループに分類されます。既存のAPIグループを置き換えたり、変更することはできません。APIを追加することは直接、既存のAPI(例、Pod)の振る舞いに影響を与えることは無いですが、APIアクセス拡張の場合、その可能性があります。

APIアクセス拡張

リクエストがKubernetes APIサーバーに到達すると、まず最初に認証が行われ、次に認可、その後、様々なAdmission Controlの対象になります。このフローの詳細はKubernetes APIへのアクセスをコントロールするを参照して下さい。

これらの各ステップごとに拡張ポイントが用意されています。

Kubdernetesはいくつかのビルトイン認証方式をサポートしています。それは認証プロキシの後ろに配置することも可能で、認可ヘッダーを通じて(Webhookの)検証のために外部サービスにトークンを送ることもできます。全てのこれらの方法は認証ドキュメントでカバーされています。

認証

認証は、全てのリクエストのヘッダーまたは証明書情報を、リクエストを投げたクライアントのユーザー名にマッピングします。

Kubernetesはいくつかのビルトイン認証方式と、それらが要件に合わない場合、認証Webhookを提供します。

認可

認可は特定のユーザーがAPIリソースに対して、読み込み、書き込み、そしてその他の操作が可能かどうかを決定します。それはオブジェクト全体のレベルで機能し、任意のオブジェクトフィールドに基づいての区別は行いません。もしビルトインの認可メカニズムが要件に合わない場合、認可Webhookが、ユーザー提供のコードを呼び出し認可の決定を行うことを可能にします。

動的Admission Control

リクエストが認可された後、もしそれが書き込み操作だった場合、リクエストはAdmission Controlのステップを通ります。ビルトインのステップに加え、いくつかの拡張が存在します:

  • イメージポリシーWebhookは、コンテナでどのイメージを実行することができるかを制限する
  • 任意のAdmission Controlの決定を行うには、一般的なAdmission webhookが利用できる。Admission Webhookは作成、更新を拒絶できる

インフラストラクチャ拡張

ストレージプラグイン

Flex Volumesは、Kubeletがバイナリプラグインを呼び出してボリュームをマウントすることにより、ユーザーはビルトインのサポートなしでボリュームタイプをマウントすることを可能にします。

デバイスプラグイン

デバイスプラグインを通じて、ノードが新たなノードのリソース(CPU、メモリなどのビルトインのものに加え)を見つけることを可能にします。

ネットワークプラグイン

他のネットワークファブリックがネットワークプラグインを通じてサポートされます。

スケジューラー拡張

スケジューラーは特別な種類のコントローラーで、Podを監視し、Podをノードに割り当てます。デフォルトのコントローラーを完全に置き換えることもできますが、他のKubernetesのコンポーネントの利用を継続する、または複数のスケジューラーを同時に動かすこともできます。

これはかなりの大きな作業で、ほとんど全てのKubernetesユーザーはスケジューラーを変更する必要はありません。

スケジューラはWebhookもサポートしており、Webhookバックエンド(スケジューラー拡張)を通じてPodを配置するために選択されたノードをフィルタリング、優先度付けすることが可能です。

次の項目

3.12.1 - Kubernetesクラスターの拡張

Kubernetesは柔軟な設定が可能で、高い拡張性を持っています。 結果として、Kubernetesのプロジェクトソースコードをフォークしたり、パッチを当てて利用することは滅多にありません。

このガイドは、Kubernetesクラスターをカスタマイズするための選択肢を記載します。 管理しているKubernetesクラスターを、動作環境の要件にどのように適合させるべきかを理解したいクラスター管理者を対象にしています。 将来の プラットフォーム開発者 、またはKubernetesプロジェクトのコントリビューターにとっても、どのような拡張のポイントやパターンが存在するのか、また、それぞれのトレードオフや制限事項を学ぶための導入として役立つでしょう。

概要

カスタマイズのアプローチには大きく分けて、フラグ、ローカル設定ファイル、またはAPIリソースの変更のみを含んだ コンフィグレーション と、稼働しているプログラムまたはサービスも含んだ エクステンション があります。このドキュメントでは、主にエクステンションについて説明します。

コンフィグレーション

設定ファイルフラグ はオンラインドキュメントのリファレンスセクションの中の、各項目に記載されています:

ホスティングされたKubernetesサービスやマネージドなKubernetesでは、フラグと設定ファイルが常に変更できるとは限りません。変更可能な場合でも、通常はクラスターの管理者のみが変更できます。また、それらは将来のKubernetesバージョンで変更される可能性があり、設定変更にはプロセスの再起動が必要になるかもしれません。これらの理由により、この方法は他の選択肢が無いときにのみ利用するべきです。

ResourceQuotaPodSecurityPolicyNetworkPolicy、そしてロールベースアクセス制御(RBAC)といった ビルトインポリシーAPI は、ビルトインのKubernetes APIです。APIは通常、ホスティングされたKubernetesサービスやマネージドなKubernetesで利用されます。これらは宣言的で、Podのような他のKubernetesリソースと同じ慣例に従っています。そのため、新しいクラスターの設定は繰り返し再利用することができ、アプリケーションと同じように管理することが可能です。さらに、安定版(stable)を利用している場合、他のKubernetes APIのような定義済みのサポートポリシーを利用することができます。これらの理由により、この方法は、適切な用途の場合、 設定ファイルフラグ よりも好まれます。

エクステンション

エクステンションはKubernetesを拡張し、深く統合されたソフトウェアの構成要素です。 これは新しいタイプと、新しい種類のハードウェアをサポートするために利用されます。

ほとんどのクラスター管理者は、ホスティングされている、またはディストリビューションとしてのKubernetesを使っているでしょう。 結果として、ほとんどのKubernetesユーザーは既存のエクステンションを使えばよいため、新しいエクステンションを書く必要は無いと言えます。

エクステンションパターン

Kubernetesは、クライアントのプログラムを書くことで自動化ができるようにデザインされています。 Kubernetes APIに読み書きをするどのようなプログラムも、役に立つ自動化機能を提供することができます。 自動化機能 はクラスター上、またはクラスター外で実行できます。 このドキュメントに後述のガイダンスに従うことで、高い可用性を持つ頑強な自動化機能を書くことができます。 自動化機能は通常、ホスティングされているクラスター、マネージドな環境など、どのKubernetesクラスター上でも動きます。

Kubernetes上でうまく動くクライアントプログラムを書くために、コントローラー パターンという明確なパターンがあります。 コントローラーは通常、オブジェクトの .spec を読み取り、何らかの処理をして、オブジェクトの .status を更新します。

コントローラーはKubernetesのクライアントです。Kubernetesがクライアントとして動き、外部のサービスを呼び出す場合、それは Webhook と呼ばれます。 呼び出されるサービスは Webhookバックエンド と呼ばれます。コントローラーのように、Webhookも障害点を追加します。

Webhookのモデルでは、Kubernetesは外部のサービスを呼び出します。 バイナリプラグイン モデルでは、Kubernetesはバイナリ(プログラム)を実行します。 バイナリプラグインはkubelet(例、FlexVolumeプラグインネットワークプラグイン)、またkubectlで利用されています。

下図は、それぞれの拡張ポイントが、Kubernetesのコントロールプレーンとどのように関わっているかを示しています。

拡張ポイント

この図は、Kubernetesにおける拡張ポイントを示しています。

  1. ユーザーは頻繁にkubectlを使って、Kubernetes APIとやり取りをします。Kubectlプラグインは、kubectlのバイナリを拡張します。これは個別ユーザーのローカル環境のみに影響を及ぼすため、サイト全体にポリシーを強制することはできません。
  2. APIサーバーは全てのリクエストを処理します。APIサーバーのいくつかの拡張ポイントは、リクエストを認可する、コンテキストに基づいてブロックする、リクエストを編集する、そして削除を処理することを可能にします。これらはAPIアクセスエクステンションセクションに記載されています。
  3. APIサーバーは様々な種類の リソース を扱います。Podのような ビルトインリソース はKubernetesプロジェクトにより定義され、変更できません。ユーザーも、自身もしくは、他のプロジェクトで定義されたリソースを追加することができます。それは カスタムリソース と呼ばれ、カスタムリソースセクションに記載されています。カスタムリソースは度々、APIアクセスエクステンションと一緒に使われます。
  4. KubernetesのスケジューラーはPodをどのノードに配置するかを決定します。スケジューリングを拡張するには、いくつかの方法があります。それらはスケジューラーエクステンションセクションに記載されています。
  5. Kubernetesにおける多くの振る舞いは、APIサーバーのクライアントであるコントローラーと呼ばれるプログラムに実装されています。コントローラーは度々、カスタムリソースと共に使われます。
  6. kubeletはサーバー上で実行され、Podが仮想サーバーのようにクラスターネットワーク上にIPを持った状態で起動することをサポートします。ネットワークプラグインがPodのネットワーキングにおける異なる実装を適用することを可能にします。
  7. kubeletはまた、コンテナのためにボリュームをマウント、アンマウントします。新しい種類のストレージはストレージプラグインを通じてサポートされます。

もしあなたがどこから始めるべきかわからない場合、このフローチャートが役立つでしょう。一部のソリューションは、いくつかの種類のエクステンションを含んでいることを留意してください。

APIエクステンション

ユーザー定義タイプ

新しいコントローラー、アプリケーションの設定に関するオブジェクト、また宣言型APIを定義し、それらをkubectlのようなKubernetesのツールから管理したい場合、Kubernetesにカスタムリソースを追加することを検討して下さい。

カスタムリソースはアプリケーション、ユーザー、監視データのデータストレージとしては使わないで下さい。

カスタムリソースに関するさらなる情報は、カスタムリソースコンセプトガイドを参照して下さい。

新しいAPIと自動化機能の連携

カスタムリソースAPIと制御ループの組み合わせはオペレーターパターンと呼ばれています。オペレーターパターンは、通常ステートフルな特定のアプリケーションを管理するために利用されます。これらのカスタムAPIと制御ループは、ストレージ、またはポリシーのような他のリソースを管理するためにも利用されます。

ビルトインリソースの変更

カスタムリソースを追加し、KubernetesAPIを拡張する場合、新たに追加されたリソースは常に新しいAPIグループに分類されます。既存のAPIグループを置き換えたり、変更することはできません。APIを追加することは直接、既存のAPI(例、Pod)の振る舞いに影響を与えることは無いですが、APIアクセスエクステンションの場合、その可能性があります。

APIアクセスエクステンション

リクエストがKubernetes APIサーバーに到達すると、まず最初に認証が行われ、次に認可、その後、様々なAdmission Controlの対象になります。このフローの詳細はKubernetes APIへのアクセスをコントロールするを参照して下さい。

これらの各ステップごとに拡張ポイントが用意されています。

Kubdernetesはいくつかのビルトイン認証方式をサポートしています。それは認証プロキシの後ろに配置することも可能で、認可ヘッダーを通じて(Webhookの)検証のために外部サービスにトークンを送ることもできます。全てのこれらの方法は認証ドキュメントでカバーされています。

認証

認証は、全てのリクエストのヘッダーまたは証明書情報を、リクエストを投げたクライアントのユーザー名にマッピングします。

Kubernetesはいくつかのビルトイン認証方式と、それらが要件に合わない場合、認証Webhookを提供します。

認可

認可は特定のユーザーがAPIリソースに対して、読み込み、書き込み、そしてその他の操作が可能かどうかを決定します。それはオブジェクト全体のレベルで機能し、任意のオブジェクトフィールドに基づいての区別は行いません。もしビルトインの認可メカニズムが要件に合わない場合、認可Webhookが、ユーザー提供のコードを呼び出し認可の決定を行うことを可能にします。

動的Admission Control

リクエストが認可された後、もしそれが書き込み操作だった場合、リクエストはAdmission Controlのステップを通ります。ビルトインのステップに加え、いくつかのエクステンションが存在します:

  • イメージポリシーWebhookは、コンテナでどのイメージを実行することができるかを制限する
  • 任意のAdmission Controlの決定を行うには、一般的なAdmission webhookが利用できる。Admission Webhookは作成、更新を拒絶できる

インフラストラクチャエクステンション

ストレージプラグイン

Flex Volumesは、Kubeletがバイナリプラグインを呼び出してボリュームをマウントすることにより、ユーザーはビルトインのサポートなしでボリュームタイプをマウントすることを可能にします。

デバイスプラグイン

デバイスプラグインを通じて、ノードが新たなノードのリソース(CPU、メモリなどのビルトインのものに加え)を見つけることを可能にします。

ネットワークプラグイン

他のネットワークファブリックがネットワークプラグインを通じてサポートされます。

スケジューラーエクステンション

スケジューラーは特別な種類のコントローラーで、Podを監視し、Podをノードに割り当てます。デフォルトのコントローラーを完全に置き換えることもできますが、他のKubernetesのコンポーネントの利用を継続する、または複数のスケジューラーを同時に動かすこともできます。

これはかなりの大きな作業で、ほとんど全てのKubernetesユーザーはスケジューラーを変更する必要はありません。

スケジューラはWebhookもサポートしており、Webhookバックエンド(スケジューラーエクステンション)を通じてPodを配置するために選択されたノードをフィルタリング、優先度付けすることが可能です。

次の項目

3.12.2 - Kubernetes APIの拡張

3.12.2.1 - カスタムリソース

カスタムリソース はKubernetes APIの拡張です。このページでは、いつKubernetesのクラスターにカスタムリソースを追加するべきなのか、そしていつスタンドアローンのサービスを利用するべきなのかを議論します。カスタムリソースを追加する2つの方法と、それらの選択方法について説明します。

カスタムリソース

リソース は、Kubernetes APIのエンドポイントで、特定のAPIオブジェクトのコレクションを保持します。例えば、ビルトインの Pods リソースは、Podオブジェクトのコレクションを包含しています。

カスタムリソース は、Kubernetes APIの拡張で、デフォルトのKubernetesインストールでは、必ずしも利用できるとは限りません。つまりそれは、特定のKubernetesインストールのカスタマイズを表します。しかし、今現在、多数のKubernetesのコア機能は、カスタムリソースを用いて作られており、Kubernetesをモジュール化しています。

カスタムリソースは、稼働しているクラスターに動的に登録され、現れたり、消えたりし、クラスター管理者はクラスター自体とは無関係にカスタムリソースを更新できます。一度、カスタムリソースがインストールされると、ユーザーはkubectlを使い、ビルトインのリソースである Pods と同じように、オブジェクトを作成、アクセスすることが可能です。

カスタムコントローラー

カスタムリソースそれ自身は、単純に構造化データを格納、取り出す機能を提供します。カスタムリソースを カスタムコントローラー と組み合わせることで、カスタムリソースは真の 宣言的API を提供します。

宣言的APIは、リソースのあるべき状態を 宣言 または指定することを可能にし、Kubernetesオブジェクトの現在の状態を、あるべき状態に同期し続けるように動きます。 コントローラーは、構造化データをユーザーが指定したあるべき状態と解釈し、その状態を管理し続けます。

稼働しているクラスターのライフサイクルとは無関係に、カスタムコントローラーをデプロイ、更新することが可能です。カスタムコントローラーはあらゆるリソースと連携できますが、カスタムリソースと組み合わせると特に効果を発揮します。オペレーターパターンは、カスタムリソースとカスタムコントローラーの組み合わせです。カスタムコントローラーにより、特定アプリケーションのドメイン知識を、Kubernetes APIの拡張に変換することができます。

カスタムリソースをクラスターに追加するべきか?

新しいAPIを作る場合、APIをKubernetesクラスターAPIにアグリゲート(集約)するか、もしくはAPIをスタンドアローンで動かすかを検討します。

APIアグリゲーションを使う場合:スタンドアローンAPIを使う場合:
APIが宣言的APIが宣言的モデルに適さない
新しいリソースをkubectlを使い読み込み、書き込みしたいkubectlのサポートは必要ない
新しいリソースをダッシュボードのような、Kubernetes UIで他のビルトインリソースと同じように管理したいKubernetes UIのサポートは必要ない
新しいAPIを開発しているAPIを提供し、適切に機能するプログラムが既に存在している
APIグループ、名前空間というような、RESTリソースパスに割り当てられた、Kubernetesのフォーマット仕様の制限を許容できる(API概要を参照)既に定義済みのREST APIと互換性を持っていなければならない
リソースはクラスターごとか、クラスター内の名前空間に自然に分けることができるクラスター、または名前空間による分割がリソース管理に適さない。特定のリソースパスに基づいて管理したい
Kubernetes APIサポート機能を再利用したいこれらの機能は必要ない

宣言的API

宣言的APIは、通常、下記に該当します:

  • APIは、比較的少数の、比較的小さなオブジェクト(リソース)で構成されている
  • オブジェクトは、アプリケーションの設定、インフラストラクチャーを定義する
  • オブジェクトは、比較的更新頻度が低い
  • 人は、オブジェクトの情報をよく読み書きする
  • オブジェクトに対する主要な手続きは、CRUD(作成、読み込み、更新、削除)になる
  • 複数オブジェクトをまたいだトランザクションは必要ない: APIは今現在の状態ではなく、あるべき状態を表現する

命令的APIは、宣言的ではありません。 APIが宣言的ではない兆候として、次のものがあります:

  • クライアントから"これを実行"と命令がきて、完了の返答を同期的に受け取る
  • クライアントから"これを実行"と命令がきて、処理IDを取得する。そして処理が完了したかどうかを、処理IDを利用して別途問い合わせる
  • リモートプロシージャコール(RPC)という言葉が飛び交っている
  • 直接、大量のデータを格納している(例、1オブジェクトあたり数kBより大きい、または数千オブジェクトより多い)
  • 高帯域アクセス(持続的に毎秒数十リクエスト)が必要
  • エンドユーザーのデータ(画像、PII、その他)を格納している、またはアプリケーションが処理する大量のデータを格納している
  • オブジェクトに対する処理が、CRUDではない
  • APIをオブジェクトとして簡単に表現できない
  • 停止している処理を処理ID、もしくは処理オブジェクトで表現することを選択している

ConfigMapとカスタムリソースのどちらを使うべきか?

下記のいずれかに該当する場合は、ConfigMapを使ってください:

  • mysql.cnfpom.xmlのような、十分に文書化された設定ファイルフォーマットが既に存在している
  • 単一キーのConfigMapに、設定ファイルの内容の全てを格納している
  • 設定ファイルの主な用途は、クラスター上のPodで実行されているプログラムがファイルを読み込み、それ自体を構成することである
  • ファイルの利用者は、Kubernetes APIよりも、Pod内のファイルまたはPod内の環境変数を介して利用することを好む
  • ファイルが更新されたときに、Deploymentなどを介してローリングアップデートを行いたい

下記のほとんどに該当する場合、カスタムリソース(CRD、またはアグリゲートAPI)を使ってください:

  • 新しいリソースを作成、更新するために、Kubernetesのクライアントライブラリー、CLIを使いたい
  • kubectlのトップレベルサポートが欲しい(例、kubectl get my-object object-name)
  • 新しい自動化の仕組みを作り、新しいオブジェクトの更新をウォッチしたい、その更新を契機に他のオブジェクトのCRUDを実行したい、またはその逆を行いたい
  • オブジェクトの更新を取り扱う、自動化の仕組みを書きたい
  • .spec.status.metadataというような、Kubernetes APIの慣習を使いたい
  • オブジェクトは、制御されたリソースコレクションの抽象化、または他のリソースのサマリーとしたい

カスタムリソースを追加する

Kubernetesは、クラスターへカスタムリソースを追加する2つの方法を提供しています:

  • CRDはシンプルで、プログラミングなしに作成可能
  • APIアグリゲーションは、プログラミングが必要だが、データがどのように格納され、APIバージョン間でどのように変換されるかというような、より詳細なAPIの振る舞いを制御できる

Kubernetesは、さまざまなユーザーのニーズを満たすためにこれら2つのオプションを提供しており、使いやすさや柔軟性が損なわれることはありません。

アグリゲートAPIは、プロキシとして機能するプライマリAPIサーバーの背後にある、下位のAPIServerです。このような配置はAPIアグリゲーション(AA)と呼ばれています。ユーザーにとっては、単にAPIサーバーが拡張されているように見えます。

CRDでは、APIサーバーの追加なしに、ユーザーが新しい種類のリソースを作成できます。CRDを使うには、APIアグリゲーションを理解する必要はありません。

どのようにインストールされたかに関わらず、新しいリソースはカスタムリソースとして参照され、ビルトインのKubernetesリソース(Podなど)とは区別されます。

CustomResourceDefinition

CustomResourceDefinitionAPIリソースは、カスタムリソースを定義します。CRDオブジェクトを定義することで、指定した名前、スキーマで新しいカスタムリソースが作成されます。Kubernetes APIは、作成したカスタムリソースのストレージを提供、および処理します。 CRDオブジェクトの名前はDNSサブドメイン名に従わなければなりません。

これはカスタムリソースを処理するために、独自のAPIサーバーを書くことから解放してくれますが、一般的な性質としてAPIサーバーアグリゲーションと比べると、柔軟性に欠けます。

新しいカスタムリソースをどのように登録するか、新しいリソースタイプとの連携、そしてコントローラーを使いイベントを処理する方法例について、カスタムコントローラー例を参照してください。

APIサーバーアグリゲーション

通常、Kubernetes APIの各リソースは、RESTリクエストとオブジェクトの永続的なストレージを管理するためのコードが必要です。メインのKubernetes APIサーバーは PodService のようなビルトインのリソースを処理し、またカスタムリソースもCRDを通じて同じように管理することができます。

アグリゲーションレイヤーは、独自のAPIサーバーを書き、デプロイすることで、カスタムリソースに特化した実装の提供を可能にします。メインのAPIサーバーが、処理したいカスタムリソースへのリクエストを独自のAPIサーバーに委譲することで、他のクライアントからも利用できるようにします。

カスタムリソースの追加方法を選択する

CRDは簡単に使えます。アグリゲートAPIはより柔軟です。ニーズに最も合う方法を選択してください。

通常、CRDは下記の場合に適しています:

  • 少数のフィールドしか必要ない
  • そのリソースは社内のみで利用している、または小さいオープンソースプロジェクトの一部で利用している(商用プロダクトではない)

使いやすさの比較

CRDは、アグリゲートAPIと比べ、簡単に作れます。

CRDアグリゲートAPI
プログラミングが不要で、ユーザーはCRDコントローラーとしてどの言語でも選択可能Go言語でプログラミングし、バイナリとイメージの作成が必要
追加のサービスは不要。CRDはAPIサーバーで処理される追加のサービス作成が必要で、障害が発生する可能性がある
CRDが作成されると、継続的なサポートは無い。バグ修正は通常のKubernetesマスターのアップグレードで行われる定期的にアップストリームからバグ修正の取り込み、リビルド、そしてアグリゲートAPIサーバーの更新が必要かもしれない
複数バージョンのAPI管理は不要。例えば、あるリソースを操作するクライアントを管理していた場合、APIのアップグレードと一緒に更新される複数バージョンのAPIを管理しなければならない。例えば、世界中に共有されている拡張機能を開発している場合

高度な機能、柔軟性

アグリゲートAPIは、例えばストレージレイヤーのカスタマイズのような、より高度なAPI機能と他の機能のカスタマイズを可能にします。

機能詳細CRDアグリゲートAPI
バリデーションエラーを予防し、クライアントと無関係にAPIを発達させることができるようになる。これらの機能は多数のクライアントがおり、同時に全てを更新できないときに最も効果を発揮するはい、ほとんどのバリデーションはOpenAPI v3.0 validationで、CRDに指定できる。その他のバリデーションはWebhookのバリデーションによりサポートされているはい、任意のバリデーションが可能
デフォルト設定上記を参照はい、OpenAPI v3.0 validationdefaultキーワード(1.17でGA)、またはMutating Webhookを通じて可能 (ただし、この方法は古いオブジェクトをetcdから読み込む場合には動きません)はい
複数バージョニング同じオブジェクトを、違うAPIバージョンで利用可能にする。フィールドの名前を変更するなどのAPIの変更を簡単に行うのに役立つ。クライアントのバージョンを管理する場合、重要性は下がるはいはい
カスタムストレージ異なる性能のストレージが必要な場合(例えば、キーバリューストアの代わりに時系列データベース)または、セキュリティの分離(例えば、機密情報の暗号化、その他)いいえはい
カスタムビジネスロジックオブジェクトが作成、読み込み、更新、また削除されるときに任意のチェック、アクションを実行するはい、Webhooksを利用はい
サブリソースのスケールHorizontalPodAutoscalerやPodDisruptionBudgetなどのシステムが、新しいリソースと連携できるようにするはいはい
サブリソースの状態ユーザーがspecセクションに書き込み、コントローラーがstatusセクションに書き込む際に、より詳細なアクセスコントロールができるようにする。カスタムリソースのデータ変換時にオブジェクトの世代を上げられるようにする(リソース内のspecとstatusでセクションが分離している必要がある)はいはい
その他のサブリソース"logs"や"exec"のような、CRUD以外の処理の追加いいえはい
strategic-merge-patchContent-Type: application/strategic-merge-patch+jsonで、PATCHをサポートする新しいエンドポイント。ローカル、サーバー、どちらでも更新されうるオブジェクトに有用。さらなる情報は"APIオブジェクトをkubectl patchで決まった場所で更新"を参照いいえはい
プロトコルバッファプロトコルバッファを使用するクライアントをサポートする新しいリソースいいえはい
OpenAPIスキーマサーバーから動的に取得できる型のOpenAPI(Swagger)スキーマはあるか、許可されたフィールドのみが設定されるようにすることで、ユーザーはフィールド名のスペルミスから保護されているか、型は強制されているか(言い換えると、「文字列」フィールドに「int」を入れさせない)はい、OpenAPI v3.0 validation スキーマがベース(1.16でGA)はい

一般的な機能

CRD、またはアグリゲートAPI、どちらを使ってカスタムリソースを作った場合でも、Kubernetesプラットフォーム外でAPIを実装するのに比べ、多数の機能が提供されます:

機能何を実現するか
CRUD新しいエンドポイントが、HTTP、kubectlを通じて、基本的なCRUD処理をサポート
Watch新しいエンドポイントが、HTTPを通じて、KubernetesのWatch処理をサポート
Discoverykubectlやダッシュボードのようなクライアントが、自動的にリソースの一覧表示、個別表示、フィールドの編集処理を提供
json-patch新しいエンドポイントがContent-Type: application/json-patch+jsonを用いたPATCHをサポート
merge-patch新しいエンドポイントがContent-Type: application/merge-patch+jsonを用いたPATCHをサポート
HTTPS新しいエンドポイントがHTTPSを利用
ビルトイン認証拡張機能へのアクセスに認証のため、コアAPIサーバー(アグリゲーションレイヤー)を利用
ビルトイン認可拡張機能へのアクセスにコアAPIサーバーで使われている認可メカニズムを再利用(例、RBAC)
ファイナライザー外部リソースの削除が終わるまで、拡張リソースの削除をブロック
Admission Webhooks拡張リソースの作成/更新/削除処理時に、デフォルト値の設定、バリデーションを実施
UI/CLI 表示kubectl、ダッシュボードで拡張リソースを表示
未設定 対 空設定クライアントは、フィールドの未設定とゼロ値を区別することができる
クライアントライブラリーの生成Kubernetesは、一般的なクライアントライブラリーと、タイプ固有のクライアントライブラリーを生成するツールを提供
ラベルとアノテーションツールがコアリソースとカスタムリソースの編集方法を知っているオブジェクト間で、共通のメタデータを提供

カスタムリソースのインストール準備

クラスターにカスタムリソースを追加する前に、いくつか認識しておくべき事項があります。

サードパーティのコードと新しい障害点

CRDを作成しても、勝手に新しい障害点が追加されてしまうことはありませんが(たとえば、サードパーティのコードをAPIサーバーで実行することによって)、パッケージ(たとえば、Chart)またはその他のインストールバンドルには、多くの場合、CRDと新しいカスタムリソースのビジネスロジックを実装するサードパーティコードが入ったDeploymentが含まれます。

アグリゲートAPIサーバーのインストールすると、常に新しいDeploymentが付いてきます。

ストレージ

カスタムリソースは、ConfigMapと同じ方法でストレージの容量を消費します。多数のカスタムリソースを作成すると、APIサーバーのストレージ容量を超えてしまうかもしれません。

アグリゲートAPIサーバーも、メインのAPIサーバーと同じストレージを利用するかもしれません。その場合、同じ問題が発生しえます。

認証、認可、そして監査

CRDでは、APIサーバーのビルトインリソースと同じ認証、認可、そして監査ロギングの仕組みを利用します。

もしRBACを使っている場合、ほとんどのRBACのロールは新しいリソースへのアクセスを許可しません。(クラスター管理者ロール、もしくはワイルドカードで作成されたロールを除く)新しいリソースには、明示的にアクセスを許可する必要があります。多くの場合、CRDおよびアグリゲートAPIには、追加するタイプの新しいロール定義がバンドルされています。

アグリゲートAPIサーバーでは、APIサーバーのビルトインリソースと同じ認証、認可、そして監査の仕組みを使う場合と使わない場合があります。

カスタムリソースへのアクセス

Kubernetesのクライアントライブラリーを使い、カスタムリソースにアクセスすることが可能です。全てのクライアントライブラリーがカスタムリソースをサポートしているわけでは無いですが、GoPython のライブラリーはサポートしています。

カスタムリソースは、下記のような方法で操作できます:

  • kubectl
  • kubernetesの動的クライアント
  • 自作のRESTクライアント
  • Kubernetesクライアント生成ツールを使い生成したクライアント(生成は高度な作業ですが、一部のプロジェクトは、CRDまたはAAとともにクライアントを提供する場合があります)

次の項目

3.12.2.2 - アグリゲーションレイヤーを使ったKubernetes APIの拡張

アグリゲーションレイヤーを使用すると、KubernetesのコアAPIで提供されている機能を超えて、追加のAPIでKubernetesを拡張できます。追加のAPIは、service-catalogのような既製のソリューション、または自分で開発したAPIのいずれかです。

アグリゲーションレイヤーは、カスタムリソースとは異なり、kube-apiserverに新しい種類のオブジェクトを認識させる方法です。

アグリゲーションレイヤー

アグリゲーションレイヤーは、kube-apiserverのプロセス内で動きます。拡張リソースが登録されるまでは、アグリゲーションレイヤーは何もしません。APIを登録するには、ユーザーはKubernetes APIで使われるURLのパスを"要求"した、APIService オブジェクトを追加します。それを追加すると、アグリゲーションレイヤーはAPIパス(例、/apis/myextension.mycompany.io/v1/…)への全てのアクセスを、登録されたAPIServiceにプロキシします。

APIServiceを実装する最も一般的な方法は、クラスター内で実行されるPodで拡張APIサーバー を実行することです。クラスター内のリソース管理に拡張APIサーバーを使用している場合、拡張APIサーバー("extension-apiserver"とも呼ばれます)は通常、1つ以上のコントローラーとペアになっています。apiserver-builderライブラリは、拡張APIサーバーと関連するコントローラーの両方にスケルトンを提供します。

応答遅延

拡張APIサーバーは、kube-apiserverとの間の低遅延ネットワーキングが必要です。 kube-apiserverとの間を5秒以内に往復するためには、ディスカバリーリクエストが必要です。

拡張APIサーバーがそのレイテンシ要件を達成できない場合は、その要件を満たすように変更することを検討してください。また、kube-apiserverでEnableAggregatedDiscoveryTimeout=false フィーチャーゲートを設定することで、タイムアウト制限を無効にすることができます。この非推奨のフィーチャーゲートは将来のリリースで削除される予定です。

次の項目

3.12.3 - オペレーターパターン

オペレーターはカスタムリソースを使用するKubernetesへのソフトウェア拡張です。 オペレーターは、特に制御ループのようなKubernetesが持つ仕組みに準拠しています。

モチベーション

オペレーターパターンはサービス、またはサービス群を管理している運用担当者の主な目的をキャプチャすることが目標です。 特定のアプリケーション、サービスの面倒を見ている運用担当者は、システムがどのように振る舞うべきか、どのようにデプロイをするか、何らかの問題があったときにどのように対応するかについて深い知識を持っています。

Kubernetes上でワークロードを稼働させている人は、しばしば繰り返し可能なタスクを自動化することを好みます。 オペレーターパターンは、Kubernetes自身が提供している機能を超えて、あなたがタスクを自動化するために、どのようにコードを書くかをキャプチャします。

Kubernetesにおけるオペレーター

Kubernetesは自動化のために設計されています。追加の作業、設定無しに、Kubernetesのコア機能によって多数のビルトインされた自動化機能が提供されます。 ワークロードのデプロイおよび稼働を自動化するためにKubernetesを使うことができます。 さらに Kubernetesがそれをどのように行うかの自動化も可能です。

Kubernetesのオペレーターパターンコンセプトは、Kubernetesのソースコードを修正すること無く、一つ以上のカスタムリソースにカスタムコントローラーをリンクすることで、クラスターの振る舞いを拡張することを可能にします。 オペレーターはKubernetes APIのクライアントで、Custom Resourceにとっての、コントローラーのように振る舞います。

オペレーターの例

オペレーターを使い自動化できるいくつかのことは、下記のようなものがあります:

  • 必要に応じてアプリケーションをデプロイする
  • アプリケーションの状態のバックアップを取得、リストアする
  • アプリケーションコードの更新と同時に、例えばデータベーススキーマ、追加の設定修正など必要な変更の対応を行う
  • Kubernetes APIをサポートしていないアプリケーションに、サービスを公開してそれらを発見する
  • クラスターの回復力をテストするために、全て、または一部分の障害をシミュレートする
  • 内部のリーダー選出プロセス無しに、分散アプリケーションのリーダーを選択する

オペレーターをもっと詳しく見るとどのように見えるでしょうか?より詳細な例を示します:

  1. クラスターに設定可能なSampleDBという名前のカスタムリソース
  2. オペレーターの、コントローラー部分を含むPodが実行されていることを保証するDeployment
  3. オペレーターのコードを含んだコンテナイメージ
  4. 設定されているSampleDBのリソースを見つけるために、コントロールプレーンに問い合わせるコントローラーのコード
  5. オペレーターのコアは、現実を、設定されているリソースにどのように合わせるかをAPIサーバーに伝えるコードです。
    • もし新しいSampleDBを追加した場合、オペレーターは永続化データベースストレージを提供するためにPersistentVolumeClaimsをセットアップし、StatefulSetがSampleDBの起動と、初期設定を担うJobを走らせます
    • もしそれを削除した場合、オペレーターはスナップショットを取り、StatefulSetとVolumeも合わせて削除されたことを確認します
  6. オペレーターは定期的なデータベースのバックアップも管理します。それぞれのSampleDBリソースについて、オペレーターはデータベースに接続可能な、バックアップを取得するPodをいつ作成するかを決定します。これらのPodはデータベース接続の詳細情報、クレデンシャルを保持するConfigMapとSecret、もしくはどちらかに依存するでしょう。
  7. オペレーターは、管理下のリソースの堅牢な自動化を提供することを目的としているため、補助的な追加コードが必要になるかもしれません。この例では、データベースが古いバージョンで動いているかどうかを確認するコードで、その場合、アップグレードを行うJobをあなたに代わり作成します。

オペレーターのデプロイ

オペレーターをデプロイする最も一般的な方法は、Custom Resource Definitionとそれに関連するコントローラーをクラスターに追加することです。 このコントローラーは通常、あなたがコンテナアプリケーションを動かすのと同じように、コントロールプレーン外で動作します。

例えば、コントローラーをDeploymentとしてクラスター内で動かすことができます。

オペレーターを利用する

一度オペレーターをデプロイすると、そのオペレーターを使って、それ自身が使うリソースの種類を追加、変更、または削除できます。 上記の利用例に従ってオペレーターそのもののためのDeploymentをセットアップし、以下のようなコマンドを実行します:

kubectl get SampleDB                   # 設定したデータベースを発見します

kubectl edit SampleDB/example-database # 手動でいくつかの設定を変更します

これだけです!オペレーターが変更の適用だけでなく既存のサービスがうまく稼働し続けるように面倒を見てくれます。

自分でオペレーターを書く

必要な振る舞いを実装したオペレーターがエコシステム内に無い場合、自分で作成することができます。 次の項目で、自分でクラウドネイティブオペレーターを作るときに利用できるライブラリやツールのリンクを見つけることができます。

オペレーター(すなわち、コントローラー)はどの言語/ランタイムでも実装でき、Kubernetes APIのクライアントとして機能させることができます。

次の項目

3.12.4 - コンピュート、ストレージ、ネットワーキングの拡張機能

このセクションでは、Kubernetes自体の一部としては提供されていない、クラスターへの拡張について説明します。 これらの拡張を使用して、クラスター内のノードを強化したり、Pod同士をつなぐネットワークファブリックを提供したりすることができます。

  • CSIおよびFlexVolumeストレージプラグイン

    Container Storage Interface (CSI)プラグインは、新しい種類のボリュームをサポートするためのKubernetesの拡張方法を提供します。 これらのボリュームは、永続性のある外部ストレージにバックアップすることができます。また、一時的なストレージを提供することも、ファイルシステムのパラダイムを使用して、情報への読み取り専用のインターフェースを提供することもできます。

    Kubernetesには、Kubernetes v1.23から非推奨とされている(CSIに置き換えられる)FlexVolumeプラグインへのサポートも含まれています。

    FlexVolumeプラグインは、Kubernetesがネイティブにサポートしていないボリュームタイプをマウントすることをユーザーに可能にします。 FlexVolumeストレージに依存するPodを実行すると、kubeletはバイナリプラグインを呼び出してボリュームをマウントします。 アーカイブされたFlexVolumeデザインの提案には、このアプローチの詳細が記載されています。

    Kubernetes Volume Plugin FAQ for Storage Vendorsには、ストレージプラグインに関する一般的な情報が含まれています。

  • デバイスプラグイン

    デバイスプラグインは、ノードが(cpumemoryなどの組み込みノードリソースに加えて)新しいNode設備を発見し、これらのカスタムなノードローカル設備を要求するPodに提供することを可能にします。

  • ネットワークプラグイン

    ネットワークプラグインにより、Kubernetesはさまざまなネットワーキングのトポロジーや技術を扱うことができます。 動作するPodネットワークを持ち、Kubernetesネットワークモデルの他の側面をサポートするためには、Kubernetesクラスターに ネットワークプラグイン が必要です。

    Kubernetes 1.30は、CNIネットワークプラグインと互換性があります。

4 - タスク

Kubernetesドキュメントのこのセクションには、個々のタスクの実行方法を示すページが含まれています。タスクページは、通常、短い手順を実行することにより、1つのことを行う方法を示します。

タスクページを作成したい場合は、ドキュメントのPull Requestの作成を参照してください。

4.1 - ツールのインストール

Kubernetesのツールをローカルのコンピューター上にセットアップします。

kubectl

Kubernetesのコマンドラインツールkubectlを使用すると、Kubernetesクラスターに対してコマンドを実行できるようになります。kubectlは、アプリケーションのデプロイ、クラスターリソースの調査と管理、ログの表示などに使用できます。

kubectlのダウンロードとインストールを行い、クラスターへのアクセスをセットアップする方法については、kubectlのインストールおよびセットアップを参照してください。

また、kubectlリファレンスドキュメントも参照できます。

minikube

minikubeは、Kubernetesをローカルで実行するツールです。minikubeはシングルノードのKubernetesクラスターをパーソナルコンピューター上(Windows、macOS、Linux PCを含む)で実行することで、Kubernetesを試したり、日常的な開発作業のために利用できます。

ツールのインストールについて知りたい場合は、公式のGet Started!のガイドに従ってください。

minikubeが起動したら、サンプルアプリケーションの実行を試すことができます。

kind

minikubeと同じように、kindもローカルコンピューター上でKubernetesを実行するツールです。minikubeとは違い、kindは1種類のコンテナランタイム上でしか動作しません。実行にはDockerのインストールと設定が必要です。

Quick Startに、kindの起動に必要な手順が説明されています。

4.1.1 - Install and Set Up kubectl on Linux

始める前に

You must use a kubectl version that is within one minor version difference of your cluster. For example, a v1.30 client can communicate with v1.29, v1.30, and v1.31 control planes. Using the latest compatible version of kubectl helps avoid unforeseen issues.

Install kubectl on Linux

The following methods exist for installing kubectl on Linux:

Install kubectl binary with curl on Linux

  1. Download the latest release with the command:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"
       
  2. Validate the binary (optional)

    Download the kubectl checksum file:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl.sha256"
       

    Validate the kubectl binary against the checksum file:

    echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
    

    If valid, the output is:

    kubectl: OK
    

    If the check fails, sha256 exits with nonzero status and prints output similar to:

    kubectl: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  3. Install kubectl

    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
    
  4. Test to ensure the version you installed is up-to-date:

    kubectl version --client
    

    Or use this for detailed view of version:

    kubectl version --client --output=yaml
    

Install using native package management

  1. Update the apt package index and install packages needed to use the Kubernetes apt repository:

    sudo apt-get update
    # apt-transport-https may be a dummy package; if so, you can skip that package
    sudo apt-get install -y apt-transport-https ca-certificates curl
    
  2. Download the public signing key for the Kubernetes package repositories. The same signing key is used for all repositories so you can disregard the version in the URL:

    # If the folder `/etc/apt/keyrings` does not exist, it should be created before the curl command, read the note below.
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    
  1. Add the appropriate Kubernetes apt repository. If you want to use Kubernetes version different than v1.30, replace v1.30 with the desired minor version in the command below:

    # This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  1. Update apt package index, then install kubectl:

    sudo apt-get update
    sudo apt-get install -y kubectl
    

  1. Add the Kubernetes yum repository. If you want to use Kubernetes version different than v1.30, replace v1.30 with the desired minor version in the command below.

    # This overwrites any existing configuration in /etc/yum.repos.d/kubernetes.repo
    cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
    EOF
    
  1. Install kubectl using yum:

    sudo yum install -y kubectl
    

  1. Add the Kubernetes zypper repository. If you want to use Kubernetes version different than v1.30, replace v1.30 with the desired minor version in the command below.

    # This overwrites any existing configuration in /etc/zypp/repos.d/kubernetes.repo
    cat <<EOF | sudo tee /etc/zypp/repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
    EOF
    
  1. Install kubectl using zypper:

    sudo zypper install -y kubectl
    

Install using other package management

If you are on Ubuntu or another Linux distribution that supports the snap package manager, kubectl is available as a snap application.

snap install kubectl --classic
kubectl version --client

If you are on Linux and using Homebrew package manager, kubectl is available for installation.

brew install kubectl
kubectl version --client

Verify kubectl configuration

kubectlがKubernetesクラスターを探索し接続するために、kubeconfigファイルが必要です。 これは、kube-up.shによりクラスターを作成した際や、Minikubeクラスターを正常にデプロイした際に自動生成されます。 デフォルトでは、kubectlの設定は~/.kube/configに格納されています。

クラスターの状態を取得し、kubectlが適切に設定されていることを確認してください:

kubectl cluster-info

URLのレスポンスが表示されている場合は、kubectlはクラスターに接続するよう正しく設定されています。

以下のようなメッセージが表示されている場合は、kubectlは正しく設定されていないか、Kubernetesクラスターに接続できていません。

The connection to the server <server-name:port> was refused - did you specify the right host or port?

たとえば、ラップトップ上(ローカル環境)でKubernetesクラスターを起動するような場合、Minikubeなどのツールを最初にインストールしてから、上記のコマンドを再実行する必要があります。

kubectl cluster-infoがURLレスポンスを返したにもかかわらずクラスターにアクセスできない場合は、次のコマンドで設定が正しいことを確認してください:

kubectl cluster-info dump

エラーメッセージ'No Auth Provider Found'のトラブルシューティング

Kubernetes 1.26にて、kubectlは以下のクラウドプロバイダーが提供するマネージドKubernetesのビルトイン認証を削除しました。 これらのプロバイダーは、クラウド固有の認証を提供するkubectlプラグインをリリースしています。 手順については以下のプロバイダーのドキュメントを参照してください:

(この変更とは関係なく、他の理由で同じエラーメッセージが表示される可能性もあります。)

Optional kubectl configurations and plugins

Enable shell autocompletion

kubectl provides autocompletion support for Bash, Zsh, Fish, and PowerShell, which can save you a lot of typing.

Below are the procedures to set up autocompletion for Bash, Fish, and Zsh.

Introduction

The kubectl completion script for Bash can be generated with the command kubectl completion bash. Sourcing the completion script in your shell enables kubectl autocompletion.

However, the completion script depends on bash-completion, which means that you have to install this software first (you can test if you have bash-completion already installed by running type _init_completion).

Install bash-completion

bash-completion is provided by many package managers (see here). You can install it with apt-get install bash-completion or yum install bash-completion, etc.

The above commands create /usr/share/bash-completion/bash_completion, which is the main script of bash-completion. Depending on your package manager, you have to manually source this file in your ~/.bashrc file.

To find out, reload your shell and run type _init_completion. If the command succeeds, you're already set, otherwise add the following to your ~/.bashrc file:

source /usr/share/bash-completion/bash_completion

Reload your shell and verify that bash-completion is correctly installed by typing type _init_completion.

Enable kubectl autocompletion

Bash

You now need to ensure that the kubectl completion script gets sourced in all your shell sessions. There are two ways in which you can do this:


echo 'source <(kubectl completion bash)' >>~/.bashrc


kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
sudo chmod a+r /etc/bash_completion.d/kubectl

If you have an alias for kubectl, you can extend shell completion to work with that alias:

echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc

Both approaches are equivalent. After reloading your shell, kubectl autocompletion should be working. To enable bash autocompletion in current session of shell, source the ~/.bashrc file:

source ~/.bashrc

Fishにおけるkubectlの補完スクリプトはkubectl completion fishコマンドで生成できます。 シェル内で補完スクリプトをsourceすることでkubectlの自動補完が有効になります。

すべてのシェルセッションで使用するには、~/.config/fish/config.fishに以下を追記してください:

kubectl completion fish | source

シェルをリロードしたあとに、kubectlの自動補完が機能するはずです。

Zshにおけるkubectlの補完スクリプトはkubectl completion zshコマンドで生成できます。 シェル内で補完スクリプトをsourceすることでkubectlの自動補完が有効になります。

すべてのシェルセッションで使用するには、~/.zshrcに以下を追記してください:

source <(kubectl completion zsh)

kubectlにエイリアスを張っている場合でも、kubectlの自動補完は自動的に機能します。

シェルをリロードしたあとに、kubectlの自動補完が機能するはずです。

2: command not found: compdefのようなエラーが出力された場合は、以下を~/.zshrcの先頭に追記してください:

autoload -Uz compinit
compinit

Install kubectl convert plugin

異なるAPIバージョン間でマニフェストを変換できる、Kubernetesコマンドラインツールkubectlのプラグインです。 これは特に、新しいKubernetesのリリースで、非推奨ではないAPIバージョンにマニフェストを移行する場合に役に立ちます。 詳細については非推奨ではないAPIへの移行を参照してください。

  1. Download the latest release with the command:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl-convert"
       
  2. Validate the binary (optional)

    Download the kubectl-convert checksum file:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl-convert.sha256"
       

    Validate the kubectl-convert binary against the checksum file:

    echo "$(cat kubectl-convert.sha256) kubectl-convert" | sha256sum --check
    

    If valid, the output is:

    kubectl-convert: OK
    

    If the check fails, sha256 exits with nonzero status and prints output similar to:

    kubectl-convert: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  3. Install kubectl-convert

    sudo install -o root -g root -m 0755 kubectl-convert /usr/local/bin/kubectl-convert
    
  4. Verify plugin is successfully installed

    kubectl convert --help
    

    If you do not see an error, it means the plugin is successfully installed.

  5. After installing the plugin, clean up the installation files:

    rm kubectl-convert kubectl-convert.sha256
    

次の項目

4.1.2 - macOS上でのkubectlのインストールおよびセットアップ

始める前に

kubectlのバージョンは、クラスターのマイナーバージョンとの差分が1つ以内でなければなりません。 たとえば、クライアントがv1.30であれば、v1.29、v1.30、v1.31のコントロールプレーンと通信できます。 最新の互換性のあるバージョンのkubectlを使うことで、不測の事態を避けることができるでしょう。

macOSへkubectlをインストールする

macOSへkubectlをインストールするには、次の方法があります:

curlを使用してmacOSへkubectlのバイナリをインストールする

  1. 最新リリースをダウンロードしてください:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
       
  2. バイナリを検証してください(オプション)

    kubectlのチェックサムファイルをダウンロードします:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl.sha256"
       

    チェックサムファイルに対してkubectlバイナリを検証します:

    echo "$(cat kubectl.sha256)  kubectl" | shasum -a 256 --check
    

    正しければ、出力は次のようになります:

    kubectl: OK
    

    チェックに失敗すると、shasumは0以外のステータスで終了し、次のような出力を表示します:

    kubectl: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  3. kubectlバイナリを実行可能にしてください。

    chmod +x ./kubectl
    
  4. kubectlバイナリをPATHの中に移動させてください。

    sudo mv ./kubectl /usr/local/bin/kubectl
    sudo chown root: /usr/local/bin/kubectl
    
  5. インストールしたバージョンが最新であることを確認してください:

    kubectl version --client
    

    または、バージョンの詳細を表示するために次を使用します:

    kubectl version --client --output=yaml
    
  6. kubectlをインストールし、検証した後は、チェックサムファイルを削除してください:

    rm kubectl.sha256
    

Homebrewを使用してmacOSへインストールする

macOSでHomebrewパッケージマネージャーを使用していれば、Homebrewでkubectlをインストールできます。

  1. インストールコマンドを実行してください:

    brew install kubectl
    

    または

    brew install kubernetes-cli
    
  2. インストールしたバージョンが最新であることを確認してください:

    kubectl version --client
    

MacPortsを使用してmacOSへインストールする

macOSでMacPortsパッケージマネージャーを使用していれば、MacPortsでkubectlをインストールできます。

  1. インストールコマンドを実行してください:

    sudo port selfupdate
    sudo port install kubectl
    
  2. インストールしたバージョンが最新であることを確認してください:

    kubectl version --client
    

kubectlの設定を検証する

kubectlがKubernetesクラスターを探索し接続するために、kubeconfigファイルが必要です。 これは、kube-up.shによりクラスターを作成した際や、Minikubeクラスターを正常にデプロイした際に自動生成されます。 デフォルトでは、kubectlの設定は~/.kube/configに格納されています。

クラスターの状態を取得し、kubectlが適切に設定されていることを確認してください:

kubectl cluster-info

URLのレスポンスが表示されている場合は、kubectlはクラスターに接続するよう正しく設定されています。

以下のようなメッセージが表示されている場合は、kubectlは正しく設定されていないか、Kubernetesクラスターに接続できていません。

The connection to the server <server-name:port> was refused - did you specify the right host or port?

たとえば、ラップトップ上(ローカル環境)でKubernetesクラスターを起動するような場合、Minikubeなどのツールを最初にインストールしてから、上記のコマンドを再実行する必要があります。

kubectl cluster-infoがURLレスポンスを返したにもかかわらずクラスターにアクセスできない場合は、次のコマンドで設定が正しいことを確認してください:

kubectl cluster-info dump

エラーメッセージ'No Auth Provider Found'のトラブルシューティング

Kubernetes 1.26にて、kubectlは以下のクラウドプロバイダーが提供するマネージドKubernetesのビルトイン認証を削除しました。 これらのプロバイダーは、クラウド固有の認証を提供するkubectlプラグインをリリースしています。 手順については以下のプロバイダーのドキュメントを参照してください:

(この変更とは関係なく、他の理由で同じエラーメッセージが表示される可能性もあります。)

オプションのkubectlの設定とプラグイン

シェルの自動補完を有効にする

kubectlはBash、Zsh、Fish、PowerShellの自動補完を提供しています。 これにより、入力を大幅に削減することができます。

以下にBash、Fish、Zshの自動補完の設定手順を示します。

はじめに

Bashにおけるkubectlの補完スクリプトはkubectl completion bashコマンドで生成できます。 シェル内で補完スクリプトをsourceすることでkubectlの自動補完が有効になります。

ただし、補完スクリプトはbash-completionに依存しているため、事前にインストールしておく必要があります。

Bashのアップグレード

ここではBash 4.1以降の使用を前提としています。 Bashのバージョンは下記のコマンドで調べることができます:

echo $BASH_VERSION

バージョンが古い場合、Homebrewを使用してインストールもしくはアップグレードできます:

brew install bash

シェルをリロードし、希望するバージョンを使用していることを確認してください:

echo $BASH_VERSION $SHELL

Homebrewは通常、/usr/local/bin/bashにインストールします。

bash-completionをインストールする

type _init_completionを実行することで、bash-completionがすでにインストールされていることを確認できます。 ない場合は、Homebrewを使用してインストールすることができます:

brew install bash-completion@2

このコマンドの出力で示されたように、~/.bash_profileに以下を追記してください:

brew_etc="$(brew --prefix)/etc" && [[ -r "${brew_etc}/profile.d/bash_completion.sh" ]] && . "${brew_etc}/profile.d/bash_completion.sh"

シェルをリロードし、type _init_completionを実行してbash-completion v2が正しくインストールされていることを検証してください。

kubectlの自動補完を有効にする

すべてのシェルセッションにてkubectlの補完スクリプトをsourceできるようにしなければなりません。 これを行うには複数の方法があります:

  • 補完スクリプトを~/.bash_profile内でsourceする:

    echo 'source <(kubectl completion bash)' >>~/.bash_profile
    
  • 補完スクリプトを/usr/local/etc/bash_completion.dディレクトリに追加する:

    kubectl completion bash >/usr/local/etc/bash_completion.d/kubectl
    
  • kubectlにエイリアスを張っている場合は、エイリアスでも動作するようにシェルの補完を拡張することができます:

    echo 'alias k=kubectl' >>~/.bash_profile
    echo 'complete -o default -F __start_kubectl k' >>~/.bash_profile
    
  • kubectlをHomebrewでインストールした場合(前述の通り)、kubectlの補完スクリプトはすでに/usr/local/etc/bash_completion.d/kubectlに格納されているでしょうか。 この場合、なにも操作する必要はありません。

どの場合でも、シェルをリロードしたあとに、kubectlの自動補完が機能するはずです。

Fishにおけるkubectlの補完スクリプトはkubectl completion fishコマンドで生成できます。 シェル内で補完スクリプトをsourceすることでkubectlの自動補完が有効になります。

すべてのシェルセッションで使用するには、~/.config/fish/config.fishに以下を追記してください:

kubectl completion fish | source

シェルをリロードしたあとに、kubectlの自動補完が機能するはずです。

Zshにおけるkubectlの補完スクリプトはkubectl completion zshコマンドで生成できます。 シェル内で補完スクリプトをsourceすることでkubectlの自動補完が有効になります。

すべてのシェルセッションで使用するには、~/.zshrcに以下を追記してください:

source <(kubectl completion zsh)

kubectlにエイリアスを張っている場合でも、kubectlの自動補完は自動的に機能します。

シェルをリロードしたあとに、kubectlの自動補完が機能するはずです。

2: command not found: compdefのようなエラーが出力された場合は、以下を~/.zshrcの先頭に追記してください:

autoload -Uz compinit
compinit

kubectl convertプラグインをインストールする

異なるAPIバージョン間でマニフェストを変換できる、Kubernetesコマンドラインツールkubectlのプラグインです。 これは特に、新しいKubernetesのリリースで、非推奨ではないAPIバージョンにマニフェストを移行する場合に役に立ちます。 詳細については非推奨ではないAPIへの移行を参照してください。

  1. 次のコマンドを使用して最新リリースをダウンロードしてください:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert"
       
  2. バイナリを検証してください(オプション)

    kubectl-convertのチェックサムファイルをダウンロードします:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert.sha256"
       

    チェックサムファイルに対してkubectl-convertバイナリを検証します:

    echo "$(cat kubectl-convert.sha256)  kubectl-convert" | shasum -a 256 --check
    

    正しければ、出力は次のようになります:

    kubectl-convert: OK
    

    チェックに失敗すると、shasumは0以外のステータスで終了し、次のような出力を表示します:

    kubectl-convert: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  3. kubectl-convertバイナリを実行可能にしてください。

    chmod +x ./kubectl-convert
    
  4. kubectl-convertバイナリをPATHの中に移動してください。

    sudo mv ./kubectl-convert /usr/local/bin/kubectl-convert
    sudo chown root: /usr/local/bin/kubectl-convert
    
  5. インストールしたバージョンが最新であることを確認してください

    kubectl convert --help
    

    何もエラーが表示されない場合は、プラグインが正常にインストールされたことを示しています。

  6. プラグインのインストール後、インストールファイルを削除してください:

    rm kubectl-convert kubectl-convert.sha256
    

macOS上のkubectlをアンインストールする

kubectlのインストール方法に応じて、次の方法を使用してください。

コマンドラインを使用してkubectlをアンインストールする

  1. システム上のkubectlバイナリの場所を特定してください:

    which kubectl
    
  2. kubectlバイナリを削除してください:

    sudo rm <path>
    

    <path>を前のステップのkubectlバイナリのパスに置き換えてください。 例えばsudo rm /usr/local/bin/kubectl

Homebrewを使用してkubectlをアンインストールする

Homebrewを使用してkubectlをインストールした場合は、次のコマンドを実行してください:

brew remove kubectl

次の項目

4.1.3 - Windows上でのkubectlのインストールおよびセットアップ

始める前に

kubectlのバージョンは、クラスターのマイナーバージョンとの差分が1つ以内でなければなりません。 たとえば、クライアントがv1.30であれば、v1.29、v1.30、v1.31のコントロールプレーンと通信できます。 最新の互換性のあるバージョンのkubectlを使うことで、不測の事態を避けることができるでしょう。

Windowsへkubectlをインストールする

Windowsへkubectlをインストールするには、次の方法があります:

curlを使用してWindowsへkubectlのバイナリをインストールする

  1. 最新の1.30のパッチリリースをダウンロードしてください: kubectl 1.30.0

    または、curlがインストールされていれば、次のコマンドも使用できます:

    curl.exe -LO "https://dl.k8s.io/release/v1.30.0/bin/windows/amd64/kubectl.exe"
    
  2. バイナリを検証してください(オプション)

    kubectlのチェックサムファイルをダウンロードします:

    curl.exe -LO "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubectl.exe.sha256"
    

    チェックサムファイルに対してkubectlバイナリを検証します:

    • コマンドプロンプトを使用して、CertUtilの出力とダウンロードしたチェックサムファイルを手動で比較します:

      CertUtil -hashfile kubectl.exe SHA256
      type kubectl.exe.sha256
      
    • PowerShellにて-eqオペレーターを使用して自動で検証を行い、TrueまたはFalseで結果を取得します:

       $(Get-FileHash -Algorithm SHA256 .\kubectl.exe).Hash -eq $(Get-Content .\kubectl.exe.sha256)
      
  3. kubectlバイナリのフォルダーをPATH環境変数に追加します。

  4. kubectlのバージョンがダウンロードしたものと同じであることを確認してください:

    kubectl version --client
    

    または、バージョンの詳細を表示するために次を使用します:

    kubectl version --client --output=yaml
    

Chocolatey、Scoopまたはwingetを使用してWindowsへインストールする

  1. Windowsへkubectlをインストールするために、ChocolateyパッケージマネージャーやScoopコマンドラインインストーラー、wingetパッケージマネージャーを使用することもできます。

    choco install kubernetes-cli
    

    scoop install kubectl
    

    winget install -e --id Kubernetes.kubectl
    
  2. インストールしたバージョンが最新であることを確認してください:

    kubectl version --client
    
  3. ホームディレクトリへ移動してください:

    # cmd.exeを使用している場合はcd %USERPROFILE%を実行してください。
    cd ~
    
  4. .kubeディレクトリを作成してください:

    mkdir .kube
    
  5. 作成した.kubeディレクトリへ移動してください:

    cd .kube
    
  6. リモートのKubernetesクラスターを使うために、kubectlを設定してください:

    New-Item config -type file
    

kubectlの設定を検証する

kubectlがKubernetesクラスターを探索し接続するために、kubeconfigファイルが必要です。 これは、kube-up.shによりクラスターを作成した際や、Minikubeクラスターを正常にデプロイした際に自動生成されます。 デフォルトでは、kubectlの設定は~/.kube/configに格納されています。

クラスターの状態を取得し、kubectlが適切に設定されていることを確認してください:

kubectl cluster-info

URLのレスポンスが表示されている場合は、kubectlはクラスターに接続するよう正しく設定されています。

以下のようなメッセージが表示されている場合は、kubectlは正しく設定されていないか、Kubernetesクラスターに接続できていません。

The connection to the server <server-name:port> was refused - did you specify the right host or port?

たとえば、ラップトップ上(ローカル環境)でKubernetesクラスターを起動するような場合、Minikubeなどのツールを最初にインストールしてから、上記のコマンドを再実行する必要があります。

kubectl cluster-infoがURLレスポンスを返したにもかかわらずクラスターにアクセスできない場合は、次のコマンドで設定が正しいことを確認してください:

kubectl cluster-info dump

エラーメッセージ'No Auth Provider Found'のトラブルシューティング

Kubernetes 1.26にて、kubectlは以下のクラウドプロバイダーが提供するマネージドKubernetesのビルトイン認証を削除しました。 これらのプロバイダーは、クラウド固有の認証を提供するkubectlプラグインをリリースしています。 手順については以下のプロバイダーのドキュメントを参照してください:

(この変更とは関係なく、他の理由で同じエラーメッセージが表示される可能性もあります。)

オプションのkubectlの設定とプラグイン

シェルの自動補完を有効にする

kubectlはBash、Zsh、Fish、PowerShellの自動補完を提供しています。 これにより、入力を大幅に削減することができます。

以下にPowerShellの自動補完の設定手順を示します。

PowerShellにおけるkubectlの補完スクリプトはkubectl completion powershellコマンドで生成できます。

すべてのシェルセッションでこれを行うには、次の行を$PROFILEファイルに追加します。

kubectl completion powershell | Out-String | Invoke-Expression

このコマンドは、PowerShellを起動する度に自動補完のスクリプトを再生成します。 生成されたスクリプトを直接$PROFILEファイルに追加することもできます。

生成されたスクリプトを$PROFILEファイルに追加するためには、PowerShellのプロンプトで次の行を実行します:

kubectl completion powershell >> $PROFILE

シェルをリロードした後、kubectlの自動補完が機能します。

kubectl convertプラグインをインストールする

異なるAPIバージョン間でマニフェストを変換できる、Kubernetesコマンドラインツールkubectlのプラグインです。 これは特に、新しいKubernetesのリリースで、非推奨ではないAPIバージョンにマニフェストを移行する場合に役に立ちます。 詳細については非推奨ではないAPIへの移行を参照してください。

  1. 次のコマンドを使用して最新リリースをダウンロードしてください:

    curl.exe -LO "https://dl.k8s.io/release/v1.30.0/bin/windows/amd64/kubectl-convert.exe"
    
  2. バイナリを検証してください(オプション)。

    kubectl-convertのチェックサムファイルをダウンロードします:

    curl.exe -LO "https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubectl-convert.exe.sha256"
    

    チェックサムファイルに対してkubectl-convertバイナリを検証します:

    • コマンドプロンプトを使用して、CertUtilの出力とダウンロードしたチェックサムファイルを手動で比較します:

      CertUtil -hashfile kubectl-convert.exe SHA256
      type kubectl-convert.exe.sha256
      
    • PowerShellにて-eqオペレーターを使用して自動で検証を行い、TrueまたはFalseで結果を取得します:

      $($(CertUtil -hashfile .\kubectl-convert.exe SHA256)[1] -replace " ", "") -eq $(type .\kubectl-convert.exe.sha256)
      
  3. kubectl-convertバイナリのフォルダーをPATH環境変数に追加します。

  4. プラグインが正常にインストールされたことを確認してください。

    kubectl convert --help
    

    何もエラーが表示されない場合は、プラグインが正常にインストールされたことを示しています。

  5. プラグインのインストール後、インストールファイルを削除してください:

    del kubectl-convert.exe
    del kubectl-convert.exe.sha256
    

次の項目

4.2 - クラスターの管理

クラスターの管理のための一般的なタスクについて学びます。

4.2.1 - kubeadmによる管理

4.2.1.1 - kubeadmによる証明書管理

FEATURE STATE: Kubernetes v1.15 [stable]

kubeadmで生成されたクライアント証明書は1年で失効します。 このページでは、kubeadmで証明書の更新を管理する方法について説明します。

始める前に

KubernetesにおけるPKI証明書と要件を熟知している必要があります。

カスタム証明書の使用

デフォルトでは、kubeadmはクラスターの実行に必要なすべての証明書を生成します。 独自の証明書を提供することで、この動作をオーバーライドできます。

そのためには、--cert-dirフラグまたはkubeadmのClusterConfigurationcertificatesDirフィールドで指定された任意のディレクトリに配置する必要があります。 デフォルトは/etc/kubernetes/pkiです。

kubeadm init を実行する前に与えられた証明書と秘密鍵のペアが存在する場合、kubeadmはそれらを上書きしません。 つまり、例えば既存のCAを/etc/kubernetes/pki/ca.crt/etc/kubernetes/pki/ca.keyにコピーすれば、kubeadmは残りの証明書に署名する際、このCAを使用できます。

外部CAモード

また、ca.crtファイルのみを提供し、ca.keyファイルを提供しないことも可能です(これはルートCAファイルのみに有効で、他の証明書ペアには有効ではありません)。 他の証明書とkubeconfigファイルがすべて揃っている場合、kubeadmはこの状態を認識し、外部CAモードを有効にします。 kubeadmはディスク上のCAキーがなくても処理を進めます。

代わりに、Controller-managerをスタンドアロンで、--controllers=csrsignerと実行し、CA証明書と鍵を指し示します。

PKI certificates and requirementsには、外部CAを使用するためのクラスターのセットアップに関するガイダンスが含まれています。

証明書の有効期限の確認

check-expirationサブコマンドを使うと、証明書の有効期限を確認することができます。

kubeadm certs check-expiration

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

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Dec 30, 2020 23:36 UTC   364d                                    no
apiserver                  Dec 30, 2020 23:36 UTC   364d            ca                      no
apiserver-etcd-client      Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
apiserver-kubelet-client   Dec 30, 2020 23:36 UTC   364d            ca                      no
controller-manager.conf    Dec 30, 2020 23:36 UTC   364d                                    no
etcd-healthcheck-client    Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-peer                  Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-server                Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
front-proxy-client         Dec 30, 2020 23:36 UTC   364d            front-proxy-ca          no
scheduler.conf             Dec 30, 2020 23:36 UTC   364d                                    no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 28, 2029 23:36 UTC   9y              no
etcd-ca                 Dec 28, 2029 23:36 UTC   9y              no
front-proxy-ca          Dec 28, 2029 23:36 UTC   9y              no

このコマンドは、/etc/kubernetes/pkiフォルダ内のクライアント証明書と、kubeadmが使用するKUBECONFIGファイル(admin.conf,controller-manager.conf,scheduler.conf)に埋め込まれたクライアント証明書の有効期限/残余時間を表示します。

また、証明書が外部管理されている場合、kubeadmはユーザーに通知します。この場合、ユーザーは証明書の更新を手動または他のツールを使用して管理する必要があります。

証明書の自動更新

kubeadmはコントロールプレーンのアップグレード時にすべての証明書を更新します。

この機能は、最もシンプルなユースケースに対応するために設計されています。 証明書の更新に特別な要件がなく、Kubernetesのバージョンアップを定期的に行う場合(各アップグレードの間隔が1年未満)、kubeadmがクラスターを最新かつ適度に安全に保つための処理を行います。

証明書の更新に関してより複雑な要求がある場合は、--certificate-renewal=falsekubeadm upgrade applykubeadm upgrade nodeに渡して、デフォルトの動作から外れるようにすることができます。

手動による証明書更新

kubeadm certs renew コマンドを使えば、いつでも証明書を手動で更新することができます。

このコマンドは/etc/kubernetes/pkiに格納されているCA(またはfront-proxy-CA)の証明書と鍵を使って更新を行います。

コマンド実行後、コントロールプレーンのPodを再起動する必要があります。 これは、現在すべてのコンポーネントと証明書について動的な証明書のリロードがサポートされていないため、必要な作業です。 スタティックPodはローカルkubeletによって管理され、API Serverによって管理されないため、kubectlで削除および再起動することはできません。

スタティックPodを再起動するには、一時的に/etc/kubernetes/manifests/からマニフェストファイルを削除して20秒間待ちます(KubeletConfiguration structfileCheckFrequency値を参照してください)。 マニフェストディレクトリにPodが無くなると、kubeletはPodを終了します。 その後ファイルを戻して、さらにfileCheckFrequency期間後に、kubeletはPodを再作成し、コンポーネントの証明書更新を完了することができます。

kubeadm certs renew は以下のオプションを提供します:

Kubernetesの証明書は通常1年後に有効期限を迎えます。

  • --csr-onlyを使用すると、証明書署名要求を生成して外部CAとの証明書を更新することができます(実際にはその場で証明書を更新しません)。詳しくは次の段落を参照してください。

  • また、すべての証明書を更新するのではなく、1つの証明書を更新することも可能です。

Kubernetes certificates APIによる証明書の更新

ここでは、Kubernetes certificates APIを使用して手動で証明書更新を実行する方法について詳しく説明します。

署名者の設定

Kubernetesの認証局は、そのままでは機能しません。 cert-managerなどの外部署名者を設定するか、組み込みの署名者を使用することができます。

ビルトインサイナーはkube-controller-managerに含まれるものです。

ビルトインサイナーを有効にするには、--cluster-signing-cert-file--cluster-signing-key-fileフラグを渡す必要があります。

新しいクラスターを作成する場合は、kubeadm設定ファイルを使用します。

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
controllerManager:
  extraArgs:
    cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt
    cluster-signing-key-file: /etc/kubernetes/pki/ca.key

証明書署名要求の作成 (CSR)

Kubernetes APIでのCSR作成については、Create CertificateSigningRequestを参照ください。

外部CAによる証明書の更新

ここでは、外部認証局を利用して手動で証明書更新を行う方法について詳しく説明します。

外部CAとの連携を強化するために、kubeadmは証明書署名要求(CSR)を生成することもできます。 CSRとは、クライアント用の署名付き証明書をCAに要求することを表します。 kubeadmの用語では、通常ディスク上のCAによって署名される証明書をCSRとして生成することができます。しかし、CAはCSRとして生成することはできません。

証明書署名要求の作成 (CSR)

kubeadm certs renew --csr-onlyで証明書署名要求を作成することができます。

CSRとそれに付随する秘密鍵の両方が出力されます。 ディレクトリを--csr-dirで渡すと、指定した場所にCSRを出力することができます。 csr-dirを指定しない場合は、デフォルトの証明書ディレクトリ(/etc/kubernetes/pki)が使用されます。

証明書はkubeadm certs renew --csr-onlyで更新することができます。 kubeadm initと同様に、--csr-dirフラグで出力先ディレクトリを指定することができます。

CSRには、証明書の名前、ドメイン、IPが含まれますが、用途は指定されません。 証明書を発行する際に、正しい証明書の使用法を指定するのはCAの責任です。

お好みの方法で証明書に署名した後、証明書と秘密鍵をPKIディレクトリ(デフォルトでは/etc/kubernetes/pki)にコピーする必要があります。

認証局(CA)のローテーション

Kubeadmは、CA証明書のローテーションや交換を最初からサポートしているわけではありません。

CAの手動ローテーションや交換についての詳細は、manual rotation of CA certificatesを参照してください。

署名付きkubeletサービング証明書の有効化

デフォルトでは、kubeadmによって展開されるkubeletサービング証明書は自己署名されています。 これは、metrics-serverのような外部サービスからキューブレットへの接続がTLSで保護されないことを意味します。 新しいkubeadmクラスター内のkubeletが適切に署名されたサービング証明書を取得するように設定するには、kubeadm initに以下の最小限の設定を渡す必要があります。

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true

すでにクラスターを作成している場合は、以下の手順で適応させる必要があります。

  • kube-systemネームスペースにあるkubelet-config-1.30 ConfigMapを見つけて編集します。

そのConfigMapのkubeletキーの値としてKubeletConfigurationドキュメントを指定します。KubeletConfigurationドキュメントを編集し、serverTLSBootstrap: trueを設定します。

  • 各ノードで、/var/lib/kubelet/config.yamlserverTLSBootstrap: trueフィールドを追加し、systemctl restart kubeletでkubeletを再起動します。

serverTLSBootstrap: trueフィールドは、kubeletサービングのブートストラップを有効にします。 証明書をcertificates.k8s.ioAPIにリクエストすることで、証明書を発行することができます。

既知の制限事項として、これらの証明書のCSR(Certificate Signing Requests)はkube-controller-managerのデフォルトサイナーによって自動的に承認されないことがあります。 kubernetes.io/kubelet-serving を参照してください。

これには、ユーザーまたはサードパーティーのコントローラーからのアクションが必要です。

これらのCSRは、以下を使用して表示できます:

kubectl get csr
NAME        AGE     SIGNERNAME                        REQUESTOR                      CONDITION
csr-9wvgt   112s    kubernetes.io/kubelet-serving     system:node:worker-1           Pending
csr-lz97v   1m58s   kubernetes.io/kubelet-serving     system:node:control-plane-1    Pending

承認するためには、次のようにします:

kubectl certificate approve <CSR-name>

デフォルトでは、これらのサービング証明書は1年後に失効します。

KubeadmはKubeletConfigurationフィールドrotateCertificatestrueに設定します。これは有効期限が切れる間際に、サービング証明書のための新しいCSRセットを作成し、ローテーションを完了するために承認する必要があることを意味します。

詳しくはCertificate Rotationをご覧ください。

これらのCSRを自動的に承認するためのソリューションをお探しの場合は、以下をお勧めします。 クラウドプロバイダーに連絡し、ノードの識別をアウトオブバンドのメカニズムで行うCSRの署名者がいるかどうか尋ねてください。

サードパーティーのカスタムコントローラーを使用することができます。

このようなコントローラーは、CSRのCommonNameを検証するだけでなく、要求されたIPやドメイン名も検証しなければ、安全なメカニズムとは言えません。これにより、kubeletクライアント証明書にアクセスできる悪意のあるアクターが、任意のIPやドメイン名に対してサービング証明書を要求するCSRを作成することを防ぐことができます。

4.2.1.2 - cgroupドライバーの設定

このページでは、kubeadmクラスターのコンテナランタイムcgroupドライバーに合わせて、kubelet cgroupドライバーを設定する方法について説明します。

始める前に

Kubernetesのコンテナランタイムの要件を熟知している必要があります。

コンテナランタイムのcgroupドライバーの設定

コンテナランタイムページでは、kubeadmベースのセットアップではcgroupfsドライバーではなく、systemdドライバーが推奨されると説明されています。

このページでは、デフォルトのsystemdドライバーを使用して多くの異なるコンテナランタイムをセットアップする方法についての詳細も説明されています。

kubelet cgroupドライバーの設定

kubeadmでは、kubeadm initの際にKubeletConfiguration構造体を渡すことができます。

このKubeletConfigurationには、kubeletのcgroupドライバーを制御するcgroupDriverフィールドを含めることができます。

フィールドを明示的に設定する最小限の例です:

# kubeadm-config.yaml
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
kubernetesVersion: v1.21.0
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd

このような設定ファイルは、kubeadmコマンドに渡すことができます:

kubeadm init --config kubeadm-config.yaml

cgroupfsドライバーの使用

このガイドで説明するように、cgroupfsドライバーをkubeadmと一緒に使用することは推奨されません。 cgroupfsを使い続け、kubeadm upgradeが既存のセットアップでKubeletConfiguration cgroupドライバーを変更しないようにするには、その値を明示的に指定する必要があります。 これは、将来のバージョンのkubeadmにsystemdドライバーをデフォルトで適用させたくない場合に適用されます。 値を明示する方法については、後述の「kubelet ConfigMapの修正」の項を参照してください。 cgroupfsドライバーを使用するようにコンテナランタイムを設定したい場合は、選択したコンテナランタイムのドキュメントを参照する必要があります。

systemdドライバーへの移行

既存のkubeadmクラスターのcgroupドライバーをsystemdにインプレースで変更する場合は、kubeletのアップグレードと同様の手順が必要です。 これには、以下に示す両方の手順を含める必要があります。

kubelet ConfigMapの修正

  • kubectl get cm -n kube-system | grep kubelet-configで、kubelet ConfigMapの名前を探します。
  • kubectl edit cm kubelet-config-x.yy -n kube-systemを呼び出します(x.yyはKubernetesのバージョンに置き換えてください)。
  • 既存のcgroupDriverの値を修正するか、以下のような新しいフィールドを追加します。
  cgroupDriver: systemd

このフィールドは、ConfigMapのkubelet:セクションの下に存在する必要があります。

全ノードでcgroupドライバーを更新

クラスター内の各ノードについて:

  • Drain the nodekubectl drain <node-name> --ignore-daemonsetsを使ってドレーンします。
  • systemctl stop kubeletを使用して、kubeletを停止します。
  • コンテナランタイムの停止。
  • コンテナランタイムのcgroupドライバーをsystemdに変更します。
  • var/lib/kubelet/config.yamlcgroupDriver: systemdを設定します。
  • コンテナランタイムの開始。
  • systemctl start kubeletでkubeletを起動します。
  • Drain the nodekubectl uncordon <node-name>を使って行います。

ワークロードが異なるノードでスケジュールするための十分な時間を確保するために、これらのステップを1つずつノード上で実行します。 プロセスが完了したら、すべてのノードとワークロードが健全であることを確認します。

4.2.1.3 - Windowsノードの追加

FEATURE STATE: Kubernetes v1.18 [beta]

Kubernetesを使用してLinuxノードとWindowsノードを混在させて実行できるため、Linuxで実行するPodとWindowsで実行するPodを混在させることができます。このページでは、Windowsノードをクラスターに登録する方法を示します。

始める前に

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

目標

  • Windowsノードをクラスターに登録する
  • LinuxとWindowsのPodとServiceが相互に通信できるようにネットワークを構成する

はじめに: クラスターへのWindowsノードの追加

ネットワーク構成

LinuxベースのKubernetesコントロールプレーンノードを取得したら、ネットワーキングソリューションを選択できます。このガイドでは、簡単にするためにVXLANモードでのFlannelの使用について説明します。

Flannel構成

  1. FlannelのためにKubernetesコントロールプレーンを準備する

    クラスター内のKubernetesコントロールプレーンでは、多少の準備が推奨されます。Flannelを使用する場合は、iptablesチェーンへのブリッジIPv4トラフィックを有効にすることをお勧めします。すべてのLinuxノードで次のコマンドを実行する必要があります:

    sudo sysctl net.bridge.bridge-nf-call-iptables=1
    
  2. Linux用のFlannelをダウンロードして構成する

    最新のFlannelマニフェストをダウンロード:

    wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    

    VNIを4096、ポートを4789に設定するために、flannelマニフェストのnet-conf.jsonセクションを変更します。次のようになります:

    net-conf.json: |
        {
          "Network": "10.244.0.0/16",
          "Backend": {
            "Type": "vxlan",
            "VNI" : 4096,
            "Port": 4789
          }
        }
    
  3. Flannelマニフェストを適用して検証する

    Flannelの構成を適用しましょう:

    kubectl apply -f kube-flannel.yml
    

    数分後、Flannel Podネットワークがデプロイされていれば、すべてのPodが実行されていることがわかります。

    kubectl get pods -n kube-system
    

    出力結果には、実行中のLinux flannel DaemonSetが含まれているはずです:

    NAMESPACE     NAME                                      READY        STATUS    RESTARTS   AGE
    ...
    kube-system   kube-flannel-ds-54954                     1/1          Running   0          1m
    
  4. Windows Flannelとkube-proxy DaemonSetを追加する

    これで、Windows互換バージョンのFlannelおよびkube-proxyを追加できます。 互換性のあるバージョンのkube-proxyを確実に入手するには、イメージのタグを置換する必要があります。 次の例は、Kubernetes 1.30.0の使用方法を示していますが、 独自のデプロイに合わせてバージョンを調整する必要があります。

    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/kube-proxy.yml | sed 's/VERSION/v1.30.0/g' | kubectl apply -f -
    kubectl apply -f https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/flannel-overlay.yml
    

Windowsワーカーノードの参加

  1. wins、kubelet、kubeadmをインストールします。

    curl.exe -LO https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/kubeadm/scripts/PrepareNode.ps1
    .\PrepareNode.ps1 -KubernetesVersion v1.30.0
    
  2. kubeadmを実行してノードに参加します

    コントロールプレーンホストでkubeadm initを実行したときに提供されたコマンドを使用します。 このコマンドがなくなった場合、またはトークンの有効期限が切れている場合は、kubeadm token create --print-join-command (コントロールプレーンホスト上で)を実行して新しいトークンを生成します。

インストールの確認

次のコマンドを実行して、クラスター内のWindowsノードを表示できるようになります:

kubectl get nodes -o wide

新しいノードがNotReady状態の場合は、flannelイメージがまだダウンロード中の可能性があります。 kube-system名前空間のflannel Podを確認することで、以前と同様に進行状況を確認できます:

kubectl -n kube-system get pods -l app=flannel

flannel Podが実行されると、ノードはReady状態になり、ワークロードを処理できるようになります。

次の項目

4.2.1.4 - Windowsノードのアップグレード

FEATURE STATE: Kubernetes v1.18 [beta]

このページでは、kubeadmで作られたWindowsノードをアップグレードする方法について説明します。

始める前に

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

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

ワーカーノードをアップグレード

kubeadmをアップグレード

  1. Windowsノードから、kubeadmをアップグレードします。:

    # 1.30.0を目的のバージョンに置き換えます
    curl.exe -Lo C:\k\kubeadm.exe https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubeadm.exe
    

ノードをドレインする

  1. Kubernetes APIにアクセスできるマシンから、 ノードをスケジュール不可としてマークして、ワークロードを削除することでノードのメンテナンスを準備します:

    # <node-to-drain>をドレインするノードの名前に置き換えます
    kubectl drain <node-to-drain> --ignore-daemonsets
    

    このような出力結果が表示されるはずです:

    node/ip-172-31-85-18 cordoned
    node/ip-172-31-85-18 drained
    

kubeletの構成をアップグレード

  1. Windowsノードから、次のコマンドを呼び出して新しいkubelet構成を同期します:

    kubeadm upgrade node
    

kubeletをアップグレード

  1. Windowsノードから、kubeletをアップグレードして再起動します:

    stop-service kubelet
    curl.exe -Lo C:\k\kubelet.exe https://dl.k8s.io/v1.30.0/bin/windows/amd64/kubelet.exe
    restart-service kubelet
    

ノードをオンライン状態に

  1. Kubernetes APIにアクセスできるマシンから、 スケジュール可能としてマークして、ノードをオンラインに戻します:

    # <node-to-drain>をノードの名前に置き換えます
    kubectl uncordon <node-to-drain>
    

kube-proxyをアップグレード

  1. Kubernetes APIにアクセスできるマシンから、次を実行します、 もう一度1.30.0を目的のバージョンに置き換えます:

    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/kube-proxy.yml | sed 's/VERSION/v1.30.0/g' | kubectl apply -f -
    

4.2.2 - dockershimからの移行

dockershimから他のコンテナランタイムに移行する際に知っておくべき情報を紹介します。

Kubernetes 1.20でdockershim deprecationが発表されてから、様々なワークロードやKubernetesインストールにどう影響するのかという質問が寄せられています。

この問題をよりよく理解するために、dockershimの削除に関するFAQブログが役に立つでしょう。

dockershimから代替のコンテナランタイムに移行することが推奨されます。 コンテナランタイムのセクションをチェックして、どのような選択肢があるかを確認してください。 問題が発生した場合は、必ず問題の報告をしてください。 そうすれば、問題が適時に修正され、クラスターがdockershimの削除に対応できるようになります。

4.2.2.1 - ノードで使用されているコンテナランタイムの確認

このページでは、クラスター内のノードが使用しているコンテナランタイムを確認する手順を概説しています。

クラスターの実行方法によっては、ノード用のコンテナランタイムが事前に設定されている場合と、設定する必要がある場合があります。 マネージドKubernetesサービスを使用している場合、ノードに設定されているコンテナランタイムを確認するためのベンダー固有の方法があるかもしれません。 このページで説明する方法は、kubectlの実行が許可されていればいつでも動作するはずです。

始める前に

kubectlをインストールし、設定します。詳細はツールのインストールの項を参照してください。

ノードで使用されているコンテナランタイムの確認

ノードの情報を取得して表示するにはkubectlを使用します:

kubectl get nodes -o wide

出力は以下のようなものです。列CONTAINER-RUNTIMEには、ランタイムとそのバージョンが出力されます。

# For dockershim
NAME         STATUS   VERSION    CONTAINER-RUNTIME
node-1       Ready    v1.16.15   docker://19.3.1
node-2       Ready    v1.16.15   docker://19.3.1
node-3       Ready    v1.16.15   docker://19.3.1
# For containerd
NAME         STATUS   VERSION   CONTAINER-RUNTIME
node-1       Ready    v1.19.6   containerd://1.4.1
node-2       Ready    v1.19.6   containerd://1.4.1
node-3       Ready    v1.19.6   containerd://1.4.1

コンテナランタイムについては、コンテナランタイムのページで詳細を確認することができます。

4.2.2.2 - dockershim削除の影響範囲を確認する

Kubernetesのdockershimコンポーネントは、DockerをKubernetesのコンテナランタイムとして使用することを可能にします。

Kubernetesの組み込みコンポーネントであるdockershimはリリースv1.24で削除されました。

このページでは、あなたのクラスターがどのようにDockerをコンテナランタイムとして使用しているか、使用中のdockershimが果たす役割について詳しく説明し、dockershimの削除によって影響を受けるワークロードがあるかどうかをチェックするためのステップを示します。

自分のアプリがDockerに依存しているかどうかの確認

アプリケーションコンテナの構築にDockerを使用している場合でも、これらのコンテナを任意のコンテナランタイム上で実行することができます。このようなDockerの使用は、コンテナランタイムとしてのDockerへの依存とはみなされません。

代替のコンテナランタイムが使用されている場合、Dockerコマンドを実行しても動作しないか、予期せぬ出力が得られる可能性があります。

このように、Dockerへの依存があるかどうかを調べることができます:

  1. 特権を持つPodがDockerコマンド(docker psなど)を実行したり、Dockerサービスを再起動したり(systemctl restart docker.serviceなどのコマンド)、Docker固有のファイル(/etc/docker/daemon.jsonなど)を変更しないことを確認すること。
  2. Dockerの設定ファイル(/etc/docker/daemon.json など)にプライベートレジストリやイメージミラーの設定がないか確認します。これらは通常、別のコンテナランタイムのために再設定する必要があります。
  3. Kubernetesインフラストラクチャーの外側のノードで実行される以下のようなスクリプトやアプリがDockerコマンドを実行しないことを確認します。
    • トラブルシューティングのために人間がノードにSSHで接続
    • ノードのスタートアップスクリプト
    • ノードに直接インストールされた監視エージェントやセキュリティエージェント
  4. 上記のような特権的な操作を行うサードパーティツール。詳しくはMigrating telemetry and security agents from dockershim を参照してください。
  5. dockershimの動作に間接的な依存性がないことを確認します。 これはエッジケースであり、あなたのアプリケーションに影響を与える可能性は低いです。ツールによっては、Docker固有の動作に反応するように設定されている場合があります。例えば、特定のメトリクスでアラートを上げたり、トラブルシューティングの指示の一部として特定のログメッセージを検索したりします。そのようなツールを設定している場合、移行前にテストクラスターで動作をテストしてください。

Dockerへの依存について解説

コンテナランタイムとは、Kubernetes Podを構成するコンテナを実行できるソフトウェアです。

KubernetesはPodのオーケストレーションとスケジューリングを担当し、各ノードではkubeletがコンテナランタイムインターフェースを抽象化して使用するので、互換性があればどのコンテナランタイムでも使用することができます。 初期のリリースでは、Kubernetesは1つのコンテナランタイムと互換性を提供していました: Dockerです。 その後、Kubernetesプロジェクトの歴史の中で、クラスター運用者は追加のコンテナランタイムを採用することを希望しました。 CRIはこのような柔軟性を可能にするために設計され、kubeletはCRIのサポートを開始しました。 しかし、DockerはCRI仕様が考案される前から存在していたため、Kubernetesプロジェクトはアダプタコンポーネント「dockershim」を作成しました。

dockershimアダプターは、DockerがCRI互換ランタイムであるかのように、kubeletがDockerと対話することを可能にします。 Kubernetes Containerd integration goes GAブログ記事で紹介されています。

Dockershim vs. CRI with Containerd

コンテナランタイムとしてContainerdに切り替えることで、中間マージンを排除することができます。 これまでと同じように、Containerdのようなコンテナランタイムですべてのコンテナを実行できます。 しかし今は、コンテナはコンテナランタイムで直接スケジュールするので、Dockerからは見えません。 そのため、これらのコンテナをチェックするために以前使っていたかもしれないDockerツールや派手なUIは、もはや利用できません。 docker psdocker inspectを使用してコンテナ情報を取得することはできません。 コンテナを一覧表示できないので、ログを取得したり、コンテナを停止したり、docker execを使用してコンテナ内で何かを実行したりすることもできません。

この場合でも、イメージを取得したり、docker buildコマンドを使用してビルドすることは可能です。 しかし、Dockerによってビルドまたはプルされたイメージは、コンテナランタイムとKubernetesからは見えません。 Kubernetesで使用できるようにするには、何らかのレジストリにプッシュする必要がありました。

4.2.2.3 - dockershimからテレメトリーやセキュリティエージェントを移行する

Kubernetes 1.20でdockershimは非推奨になりました。

dockershimの削除に関するFAQから、ほとんどのアプリがコンテナをホストするランタイムに直接依存しないことは既にご存知かもしれません。 しかし、コンテナのメタデータやログ、メトリクスを収集するためにDockerに依存しているテレメトリーやセキュリティエージェントはまだ多く存在します。 この文書では、これらの依存関係を検出する方法と、これらのエージェントを汎用ツールまたは代替ランタイムに移行する方法に関するリンクを集約しています。

テレメトリーとセキュリティエージェント

Kubernetesクラスター上でエージェントを実行するには、いくつかの方法があります。エージェントはノード上で直接、またはDaemonSetとして実行することができます。

テレメトリーエージェントがDockerに依存する理由とは?

歴史的には、KubernetesはDockerの上に構築されていました。 Kubernetesはネットワークとスケジューリングを管理し、Dockerはコンテナをノードに配置して操作していました。 そのため、KubernetesからはPod名などのスケジューリング関連のメタデータを、Dockerからはコンテナの状態情報を取得することができます。 時が経つにつれ、コンテナを管理するためのランタイムも増えてきました。 また、多くのランタイムにまたがるコンテナ状態情報の抽出を一般化するプロジェクトやKubernetesの機能もあります。

いくつかのエージェントはDockerツールに関連しています。 エージェントはdocker psdocker topといったコマンドを実行し、コンテナやプロセスの一覧を表示します。 またはdocker logsを使えば、dockerログを購読することができます。

Dockerがコンテナランタイムとして非推奨になったため、これらのコマンドはもう使えません。

Dockerに依存するDaemonSetの特定

Podがノード上で動作しているdockerdを呼び出したい場合、Podは以下のいずれかを行う必要があります。

  • Dockerデーモンの特権ソケットがあるファイルシステムをvolumeのようにマウントする。

  • Dockerデーモンの特権ソケットの特定のパスを直接ボリュームとしてマウントします。

例: COSイメージでは、DockerはそのUnixドメインソケットを/var/run/docker.sockに公開します。 つまり、Pod仕様には/var/run/docker.sockhostPathボリュームマウントが含まれることになります。

以下は、Dockerソケットを直接マッピングしたマウントを持つPodを探すためのシェルスクリプトのサンプルです。

このスクリプトは、Podの名前空間と名前を出力します。

grep '/var/run/docker.sock'を削除して、他のマウントを確認することもできます。

kubectl get pods --all-namespaces \
-o=jsonpath='{range .items[*]}{"\n"}{.metadata.namespace}{":\t"}{.metadata.name}{":\t"}{range .spec.volumes[*]}{.hostPath.path}{", "}{end}{end}' \
| sort \
| grep '/var/run/docker.sock'

ノードエージェントからDockerの依存性を検出する

クラスターノードをカスタマイズし、セキュリティやテレメトリーのエージェントをノードに追加インストールする場合、エージェントのベンダーにDockerへの依存性があるかどうかを必ず確認してください。

テレメトリーとセキュリティエージェントのベンダー

様々なテレメトリーおよびセキュリティエージェントベンダーのための移行指示の作業中バージョンをGoogle docに保管しています。 dockershimからの移行に関する最新の手順については、各ベンダーにお問い合わせください。

4.2.3 - 証明書を手動で生成する

クライアント証明書認証を使用する場合、easyrsaopensslまたはcfsslを使って手動で証明書を生成することができます。

easyrsa

easyrsaはクラスターの証明書を手動で生成することができます。

  1. パッチが適用されたバージョンのeasyrsa3をダウンロードして解凍し、初期化します。

    curl -LO https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
    tar xzf easy-rsa.tar.gz
    cd easy-rsa-master/easyrsa3
    ./easyrsa init-pki
    
  2. 新しい認証局(CA)を生成します。 --batchで自動モードに設定します。--req-cnはCAの新しいルート証明書のコモンネーム(CN)を指定します。

    ./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
    
  3. サーバー証明書と鍵を生成します。

    引数--subject-alt-nameは、APIサーバーがアクセス可能なIPとDNS名を設定します。 MASTER_CLUSTER_IPは通常、APIサーバーとコントローラーマネージャーコンポーネントの両方で--service-cluster-ip-range引数に指定したサービスCIDRの最初のIPとなります。 引数--daysは、証明書の有効期限が切れるまでの日数を設定するために使用します。 また、以下のサンプルでは、デフォルトのDNSドメイン名としてcluster.localを使用することを想定しています。

    ./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
    "IP:${MASTER_CLUSTER_IP},"\
    "DNS:kubernetes,"\
    "DNS:kubernetes.default,"\
    "DNS:kubernetes.default.svc,"\
    "DNS:kubernetes.default.svc.cluster,"\
    "DNS:kubernetes.default.svc.cluster.local" \
    --days=10000 \
    build-server-full server nopass
    
  4. pki/ca.crtpki/issued/server.crtpki/private/server.keyを自分のディレクトリにコピーします。

  5. APIサーバーのスタートパラメーターに以下のパラメーターを記入し、追加します。

    --client-ca-file=/yourdirectory/ca.crt
    --tls-cert-file=/yourdirectory/server.crt
    --tls-private-key-file=/yourdirectory/server.key
    

openssl

opensslは、クラスター用の証明書を手動で生成することができます。

  1. 2048bitのca.keyを生成します:

    openssl genrsa -out ca.key 2048
    
  2. ca.keyに従ってca.crtを生成します(-daysで証明書の有効期限を設定します):

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
    
  3. 2048bitでserver.keyを生成します:

    openssl genrsa -out server.key 2048
    
  4. 証明書署名要求(CSR)を生成するための設定ファイルを作成します。

    山括弧で囲まれた値(例:<MASTER_IP>)は必ず実際の値に置き換えてから、ファイル(例:csr.conf)に保存してください。MASTER_CLUSTER_IPの値は、前のサブセクションで説明したように、APIサーバーのサービスクラスターのIPであることに注意してください。また、以下のサンプルでは、デフォルトのDNSドメイン名としてcluster.localを使用することを想定しています。

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    
    [ dn ]
    C = <country>
    ST = <state>
    L = <city>
    O = <organization>
    OU = <organization unit>
    CN = <MASTER_IP>
    
    [ req_ext ]
    subjectAltName = @alt_names
    
    [ alt_names ]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster
    DNS.5 = kubernetes.default.svc.cluster.local
    IP.1 = <MASTER_IP>
    IP.2 = <MASTER_CLUSTER_IP>
    
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    subjectAltName=@alt_names
    
  5. 設定ファイルに基づき、証明書署名要求を生成します:

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  6. ca.key、ca.crt、server.csrを使用して、サーバー証明書を生成します:

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
        -CAcreateserial -out server.crt -days 10000 \
        -extensions v3_ext -extfile csr.conf -sha256
    
  7. 証明書署名要求を表示します:

    openssl req  -noout -text -in ./server.csr
    
  8. 証明書を表示します:

    openssl x509  -noout -text -in ./server.crt
    

最後に、同じパラメーターをAPIサーバーのスタートパラメーターに追加します。

cfssl

cfsslも証明書を生成するためのツールです。

  1. 以下のように、コマンドラインツールをダウンロードし、解凍して準備してください。

    なお、サンプルのコマンドは、お使いのハードウェア・アーキテクチャやCFSSLのバージョンに合わせる必要があるかもしれません。

    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
    chmod +x cfssl
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
    chmod +x cfssljson
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
    chmod +x cfssl-certinfo
    
  2. 成果物を格納するディレクトリを作成し、cfsslを初期化します:

    mkdir cert
    cd cert
    ../cfssl print-defaults config > config.json
    ../cfssl print-defaults csr > csr.json
    
  3. CAファイルを生成するためのJSON設定ファイル、例えばca-config.jsonを作成します:

    {
      "signing": {
        "default": {
          "expiry": "8760h"
        },
        "profiles": {
          "kubernetes": {
            "usages": [
              "signing",
              "key encipherment",
              "server auth",
              "client auth"
            ],
            "expiry": "8760h"
          }
        }
      }
    }
    
  4. CA証明書署名要求(CSR)用のJSON設定ファイル(例:ca-csr.json)を作成します。 山括弧で囲まれた値は、必ず使用したい実際の値に置き換えてください。

    {
      "CN": "kubernetes",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names":[{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  5. CAキー(ca-key.pem)と証明書(ca.pem)を生成します:

    ../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
    
  6. APIサーバーの鍵と証明書を生成するためのJSON設定ファイル、例えばserver-csr.jsonを作成します。 山括弧内の値は、必ず使用したい実際の値に置き換えてください。 MASTER_CLUSTER_IPは、前のサブセクションで説明したように、APIサーバーのサービスクラスターのIPです。 また、以下のサンプルでは、デフォルトのDNSドメイン名としてcluster.localを使用することを想定しています。

    {
      "CN": "kubernetes",
      "hosts": [
        "127.0.0.1",
        "<MASTER_IP>",
        "<MASTER_CLUSTER_IP>",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
      ],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  7. APIサーバーの鍵と証明書を生成します。 デフォルトでは、それぞれserver-key.pemserver.pemというファイルに保存されます:

    ../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
         --config=ca-config.json -profile=kubernetes \
         server-csr.json | ../cfssljson -bare server
    

自己署名入りCA証明書を配布する

クライアントノードが自己署名入りCA証明書を有効なものとして認識できない場合があります。

非プロダクション環境、または会社のファイアウォールの内側での開発環境であれば、自己署名入りCA証明書をすべてのクライアントに配布し、有効な証明書のローカルリストを更新することができます。

各クライアントで、次の操作を実行します:

sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.

証明書API

認証に使用するx509証明書のプロビジョニングにはcertificates.k8s.ioAPIを使用することができます。クラスターでのTLSの管理に記述されています。

4.2.4 - メモリー、CPU、APIリソースの管理

4.2.4.1 - ネームスペースのデフォルトのメモリー要求と制限を設定する

ネームスペースのデフォルトのメモリーリソース制限を定義して、そのネームスペース内のすべての新しいPodにメモリーリソース制限が設定されるようにします。

このページでは、ネームスペースのデフォルトのメモリー要求と制限を設定する方法を説明します。

Kubernetesクラスターはネームスペースに分割することができます。デフォルトのメモリー制限を持つネームスペースがあり、独自のメモリー制限を指定しないコンテナでPodを作成しようとすると、コントロールプレーンはそのコンテナにデフォルトのメモリー制限を割り当てます。

Kubernetesは、このトピックで後ほど説明する特定の条件下で、デフォルトのメモリー要求を割り当てます。

始める前に

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

クラスターにネームスペースを作成するには、アクセス権が必要です。

クラスターの各ノードには、最低でも2GiBのメモリーが必要です。

ネームスペースの作成

この演習で作成したリソースがクラスターの他の部分から分離されるように、ネームスペースを作成します。

kubectl create namespace default-mem-example

LimitRangeとPodの作成

以下は、LimitRangeのマニフェストの例です。このマニフェストでは、デフォルトのメモリー要求とデフォルトのメモリー制限を指定しています。

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

default-mem-exampleネームスペースにLimitRangeを作成します:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --namespace=default-mem-example

default-mem-exampleネームスペースでPodを作成し、そのPod内のコンテナがメモリー要求とメモリー制限の値を独自に指定しない場合、コントロールプレーンはデフォルト値のメモリー要求256MiBとメモリー制限512MiBを適用します。

以下は、コンテナを1つ持つPodのマニフェストの例です。コンテナは、メモリー要求とメモリー制限を指定していません。

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

Podを作成します:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod.yaml --namespace=default-mem-example

Podの詳細情報を表示します:

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

この出力は、Podのコンテナのメモリー要求が256MiBで、メモリー制限が512MiBであることを示しています。 これらはLimitRangeで指定されたデフォルト値です。

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

Podを削除します:

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

コンテナの制限を指定し、要求を指定しない場合

以下は1つのコンテナを持つPodのマニフェストです。コンテナはメモリー制限を指定しますが、メモリー要求は指定しません。

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-2
spec:
  containers:
  - name: default-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1Gi"

Podを作成します:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-2.yaml --namespace=default-mem-example

Podの詳細情報を表示します:

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

この出力は、コンテナのメモリー要求がそのメモリー制限に一致するように設定されていることを示しています。 コンテナにはデフォルトのメモリー要求値である256Miが割り当てられていないことに注意してください。

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

コンテナの要求を指定し、制限を指定しない場合

1つのコンテナを持つPodのマニフェストです。コンテナはメモリー要求を指定しますが、メモリー制限は指定しません。

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-3
spec:
  containers:
  - name: default-mem-demo-3-ctr
    image: nginx
    resources:
      requests:
        memory: "128Mi"

Podを作成します:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-3.yaml --namespace=default-mem-example

Podの詳細情報を表示します:

kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example

この出力は、コンテナのメモリー要求が、コンテナのマニフェストで指定された値に設定されていることを示しています。 コンテナは512MiB以下のメモリーを使用するように制限されていて、これはネームスペースのデフォルトのメモリー制限と一致します。

resources:
  limits:
    memory: 512Mi
  requests:
    memory: 128Mi

デフォルトのメモリー制限と要求の動機

ネームスペースにメモリーリソースクォータが設定されている場合、メモリー制限のデフォルト値を設定しておくと便利です。

以下はリソースクォータがネームスペースに課す制限のうちの2つです。

  • ネームスペースで実行されるすべてのPodについて、Podとその各コンテナにメモリー制限を設ける必要があります(Pod内のすべてのコンテナに対してメモリー制限を指定すると、Kubernetesはそのコンテナの制限を合計することでPodレベルのメモリー制限を推測することができます)。
  • メモリー制限は、当該Podがスケジュールされているノードのリソース予約を適用します。ネームスペース内のすべてのPodに対して予約されるメモリーの総量は、指定された制限を超えてはなりません。
  • また、ネームスペース内のすべてのPodが実際に使用するメモリーの総量も、指定された制限を超えてはなりません。

LimitRangeの追加時:

コンテナを含む、そのネームスペース内のいずれかのPodが独自のメモリー制限を指定していない場合、コントロールプレーンはそのコンテナにデフォルトのメモリー制限を適用し、メモリーのResourceQuotaによって制限されているネームスペース内でPodを実行できるようにします。

クリーンアップ

ネームスペースを削除します:

kubectl delete namespace default-mem-example

次の項目

クラスター管理者向け

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

4.2.4.2 - Namespaceに対する最小および最大メモリー制約の構成

このページでは、Namespaceで実行されるコンテナが使用するメモリーの最小値と最大値を設定する方法を説明します。 LimitRange で最小値と最大値のメモリー値を指定します。 PodがLimitRangeによって課される制約を満たさない場合、そのNamespaceではPodを作成できません。

始める前に

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

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

クラスター内の各ノードには、少なくとも1GiBのメモリーが必要です。

Namespaceの作成

この演習で作成したリソースがクラスターの他の部分から分離されるように、Namespaceを作成します。

kubectl create namespace constraints-mem-example

LimitRangeとPodを作成

LimitRangeの設定ファイルです。

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

LimitRangeを作成します。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example

LimitRangeの詳細情報を表示します。

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

出力されるのは、予想通りメモリー制約の最小値と最大値を示しています。 しかし、LimitRangeの設定ファイルでデフォルト値を指定していないにもかかわらず、 自動的に作成されていることに気づきます。

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

constraints-mem-exampleNamespaceにコンテナが作成されるたびに、 Kubernetesは以下の手順を実行するようになっています。

  • コンテナが独自のメモリー要求と制限を指定しない場合は、デフォルトのメモリー要求と制限をコンテナに割り当てます。

  • コンテナに500MiB以上のメモリー要求があることを確認します。

  • コンテナのメモリー制限が1GiB以下であることを確認します。

以下は、1つのコンテナを持つPodの設定ファイルです。設定ファイルのコンテナ(containers)では、600MiBのメモリー要求と800MiBのメモリー制限が指定されています。これらはLimitRangeによって課される最小と最大のメモリー制約を満たしています。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

Podの作成

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example

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

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

Podの詳細情報を見ます

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

出力は、コンテナが600MiBのメモリ要求と800MiBのメモリー制限になっていることを示しています。これらはLimitRangeによって課される制約を満たしています。

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

Podを消します。

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

最大メモリ制約を超えるPodの作成の試み

これは、1つのコンテナを持つPodの設定ファイルです。コンテナは800MiBのメモリー要求と1.5GiBのメモリー制限を指定しています。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

Podを作成してみます。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

出力は、コンテナが大きすぎるメモリー制限を指定しているため、Podが作成されないことを示しています。

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

最低限のメモリ要求を満たさないPodの作成の試み

これは、1つのコンテナを持つPodの設定ファイルです。コンテナは100MiBのメモリー要求と800MiBのメモリー制限を指定しています。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

Podを作成してみます。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

出力は、コンテナが小さすぎるメモリー要求を指定しているため、Podが作成されないことを示しています。

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

メモリ要求や制限を指定しないPodの作成

これは、1つのコンテナを持つPodの設定ファイルです。コンテナはメモリー要求を指定しておらず、メモリー制限も指定していません。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

Podを作成します。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

Podの詳細情報を見ます

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

出力を見ると、Podのコンテナのメモリ要求は1GiB、メモリー制限は1GiBであることがわかります。 コンテナはどのようにしてこれらの値を取得したのでしょうか?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

コンテナが独自のメモリー要求と制限を指定していなかったため、LimitRangeから与えられのです。 コンテナが独自のメモリー要求と制限を指定していなかったため、LimitRangeからデフォルトのメモリー要求と制限が与えられたのです。

この時点で、コンテナは起動しているかもしれませんし、起動していないかもしれません。このタスクの前提条件は、ノードが少なくとも1GiBのメモリーを持っていることであることを思い出してください。それぞれのノードが1GiBのメモリーしか持っていない場合、どのノードにも1GiBのメモリー要求に対応するのに十分な割り当て可能なメモリーがありません。たまたま2GiBのメモリーを持つノードを使用しているのであれば、おそらく1GiBのメモリーリクエストに対応するのに十分なスペースを持っていることになります。

Podを削除します。

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

最小および最大メモリー制約の強制

LimitRangeによってNamespaceに課される最大および最小のメモリー制約は、Podが作成または更新されたときにのみ適用されます。LimitRangeを変更しても、以前に作成されたPodには影響しません。

最小・最大メモリー制約の動機

クラスター管理者としては、Podが使用できるメモリー量に制限を課したいと思うかもしれません。

例:

  • クラスター内の各ノードは2GBのメモリーを持っています。クラスター内のどのノードもその要求をサポートできないため、2GB以上のメモリーを要求するPodは受け入れたくありません。

  • クラスターは運用部門と開発部門で共有されています。 本番用のワークロードでは最大8GBのメモリーを消費しますが、開発用のワークロードでは512MBに制限したいとします。本番用と開発用に別々のNamespaceを作成し、それぞれのNamespaceにメモリー制限を適用します。

クリーンアップ

Namespaceを削除します。

kubectl delete namespace constraints-mem-example

次の項目

クラスター管理者向け

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

4.2.5 - 拡張リソースをNodeにアドバタイズする

このページでは、Nodeに対して拡張リソースを指定する方法を説明します。拡張リソースを利用すると、Kubernetesにとって未知のノードレベルのリソースをクラスター管理者がアドバタイズできるようになります。

始める前に

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

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

Nodeの名前を取得する

kubectl get nodes

この練習で使いたいNodeを1つ選んでください。

Nodeの1つで新しい拡張リソースをアドバタイズする

Node上の新しい拡張リソースをアドバタイズするには、HTTPのPATCHリクエストをKubernetes APIサーバーに送ります。たとえば、Nodeの1つに4つのドングルが接続されているとします。以下に、4つのドングルリソースをNodeにアドバタイズするPATCHリクエストの例を示します。

PATCH /api/v1/nodes/<選択したNodeの名前>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "add",
    "path": "/status/capacity/example.com~1dongle",
    "value": "4"
  }
]

Kubernetesは、ドングルとは何かも、ドングルが何に利用できるのかを知る必要もないことに注意してください。上のPATCHリクエストは、ただNodeが4つのドングルと呼ばれるものを持っているとKubernetesに教えているだけです。

Kubernetes APIサーバーに簡単にリクエストを送れるように、プロキシを実行します。

kubectl proxy

もう1つのコマンドウィンドウを開き、HTTPのPATCHリクエストを送ります。<選択したNodeの名前>の部分は、選択したNodeの名前に置き換えてください。

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
http://localhost:8001/api/v1/nodes/<選択したNodeの名前>/status

出力には、Nodeがキャパシティー4のdongleを持っていることが示されます。

"capacity": {
  "cpu": "2",
  "memory": "2049008Ki",
  "example.com/dongle": "4",

Nodeの説明を確認します。

kubectl describe node <選択したNodeの名前>

出力には、再びdongleリソースが表示されます。

Capacity:
 cpu:  2
 memory:  2049008Ki
 example.com/dongle:  4

これで、アプリケーション開発者は特定の数のdongleをリクエストするPodを作成できるようになりました。詳しくは、拡張リソースをコンテナに割り当てるを読んでください。

議論

拡張リソースは、メモリやCPUリソースと同様のものです。たとえば、Nodeが持っている特定の量のメモリやCPUがNode上で動作している他のすべてのコンポーネントと共有されるのと同様に、Nodeが搭載している特定の数のdongleが他のすべてのコンポーネントと共有されます。そして、アプリケーション開発者が特定の量のメモリとCPUをリクエストするPodを作成できるのと同様に、Nodeが搭載している特定の数のdongleをリクエストするPodが作成できます。

拡張リソースはKubernetesには詳細を意図的に公開しないため、Kubernetesは拡張リソースの実体をまったく知りません。Kubernetesが知っているのは、Nodeが特定の数の拡張リソースを持っているということだけです。拡張リソースは整数値でアドバタイズしなければなりません。たとえば、Nodeは4つのdongleをアドバタイズできますが、4.5のdongleというのはアドバタイズできません。

Storageの例

Nodeに800GiBの特殊なディスクストレージがあるとします。この特殊なストレージの名前、たとえばexample.com/special-storageという名前の拡張リソースが作れます。そして、そのなかの一定のサイズ、たとえば100GiBのチャンクをアドバタイズできます。この場合、Nodeはexample.com/special-storageという種類のキャパシティ8のリソースを持っているとアドバタイズします。

Capacity:
 ...
 example.com/special-storage: 8

特殊なストレージに任意のサイズのリクエストを許可したい場合、特殊なストレージを1バイトのサイズのチャンクでアドバタイズできます。その場合、example.com/special-storageという種類の800Giのリソースとしてアドバタイズします。

Capacity:
 ...
 example.com/special-storage:  800Gi

すると、コンテナは好きなバイト数の特殊なストレージを最大800Giまでリクエストできるようになります。

クリーンアップ

以下に、dongleのアドバタイズをNodeから削除するPATCHリクエストを示します。

PATCH /api/v1/nodes/<選択したNodeの名前>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "remove",
    "path": "/status/capacity/example.com~1dongle",
  }
]

Kubernetes APIサーバーに簡単にリクエストを送れるように、プロキシを実行します。

kubectl proxy

もう1つのコマンドウィンドウで、HTTPのPATCHリクエストを送ります。<選択したNodeの名前>の部分は、選択したNodeの名前に置き換えてください。

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
http://localhost:8001/api/v1/nodes/<選択したNodeの名前>/status

dongleのアドバタイズが削除されたことを検証します。

kubectl describe node <選択したNodeの名前> | grep dongle

(出力には何も表示されないはずです)

次の項目

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

クラスター管理者向け

4.2.6 - クラウドコントローラーマネージャーの運用管理

FEATURE STATE: Kubernetes v1.11 [beta]

クラウドプロバイダーはKubernetesプロジェクトとは異なるペースで開発およびリリースされるため、プロバイダー固有のコードを`cloud-controller-manager`バイナリに抽象化することでクラウドベンダーはKubernetesのコアのコードとは独立して開発が可能となりました。

cloud-controller-managerは、cloudprovider.Interfaceを満たす任意のクラウドプロバイダーと接続できます。下位互換性のためにKubernetesのコアプロジェクトで提供されるcloud-controller-managerkube-controller-managerと同じクラウドライブラリを使用します。Kubernetesのコアリポジトリですでにサポートされているクラウドプロバイダーは、Kubernetesリポジトリにあるcloud-controller-managerを使用してKubernetesのコアから移行することが期待されています。

運用

要件

すべてのクラウドには動作させるためにそれぞれのクラウドプロバイダーの統合を行う独自の要件があり、kube-controller-managerを実行する場合の要件とそれほど違わないようにする必要があります。一般的な経験則として、以下のものが必要です。

  • クラウドの認証/認可: クラウドではAPIへのアクセスを許可するためにトークンまたはIAMルールが必要になる場合があります
  • kubernetesの認証/認可: cloud-controller-managerは、kubernetes apiserverと通信するためにRBACルールの設定を必要とする場合があります
  • 高可用性: kube-controller-managerのように、リーダー選出を使用したクラウドコントローラーマネージャーの高可用性のセットアップが必要になる場合があります(デフォルトでオンになっています)。

cloud-controller-managerを動かす

cloud-controller-managerを正常に実行するにはクラスター構成にいくつかの変更が必要です。

  • kube-apiserverkube-controller-managerは**--cloud-providerフラグを指定してはいけません**。これによりクラウドコントローラーマネージャーによって実行されるクラウド固有のループが実行されなくなります。将来このフラグは非推奨になり削除される予定です。
  • kubelet--cloud-provider=externalで実行する必要があります。これは作業をスケジュールする前にクラウドコントローラーマネージャーによって初期化する必要があることをkubeletが認識できるようにするためです。

クラウドコントローラーマネージャーを使用するようにクラスターを設定するとクラスターの動作がいくつか変わることに注意してください。

  • --cloud-provider=externalを指定したkubeletは、初期化時にNoSchedulenode.cloudprovider.kubernetes.io/uninitialized汚染を追加します。これによりノードは作業をスケジュールする前に外部のコントローラーからの2回目の初期化が必要であるとマークされます。クラウドコントローラーマネージャーが使用できない場合クラスター内の新しいノードはスケジュールできないままになることに注意してください。スケジューラーはリージョンやタイプ(高CPU、GPU、高メモリ、スポットインスタンスなど)などのノードに関するクラウド固有の情報を必要とする場合があるためこの汚染は重要です。
  • クラスター内のノードに関するクラウド情報はローカルメタデータを使用して取得されなくなりましたが、代わりにノード情報を取得するためのすべてのAPI呼び出しはクラウドコントローラーマネージャーを経由して行われるようになります。これはセキュリティを向上させるためにkubeletでクラウドAPIへのアクセスを制限できることを意味します。大規模なクラスターではクラスター内からクラウドのほとんどすべてのAPI呼び出しを行うため、クラウドコントローラーマネージャーがレートリミットに達するかどうかを検討する必要があります。

クラウドコントローラーマネージャーは以下を実装できます。

  • ノードコントローラー - クラウドAPIを使用してkubernetesノードを更新し、クラウドで削除されたkubernetesノードを削除します。
  • サービスコントローラー - タイプLoadBalancerのサービスに対応してクラウド上のロードバランサーを操作します。
  • ルートコントローラー - クラウド上でネットワークルートを設定します。
  • Kubernetesリポジトリの外部にあるプロバイダーを実行している場合はその他の機能の実装。

現在Kubernetesのコアでサポートされているクラウドを使用していて、クラウドコントローラーマネージャーを利用する場合は、kubernetesのコアのクラウドコントローラーマネージャーを参照してください。

Kubernetesのコアリポジトリにないクラウドコントローラーマネージャーの場合、クラウドベンダーまたはsigリードが管理するリポジトリでプロジェクトを見つけることができます。

すでにKubernetesのコアリポジトリにあるプロバイダーの場合、クラスター内でデーモンセットとしてKubernetesリポジトリ内部のクラウドコントローラーマネージャーを実行できます。以下をガイドラインとして使用してください。

# This is an example of how to set up cloud-controller-manager as a Daemonset in your cluster.
# It assumes that your masters can run pods and has the role node-role.kubernetes.io/master
# Note that this Daemonset will not work straight out of the box for your cloud, this is
# meant to be a guideline.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:cloud-controller-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    k8s-app: cloud-controller-manager
  name: cloud-controller-manager
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: cloud-controller-manager
  template:
    metadata:
      labels:
        k8s-app: cloud-controller-manager
    spec:
      serviceAccountName: cloud-controller-manager
      containers:
      - name: cloud-controller-manager
        # for in-tree providers we use registry.k8s.io/cloud-controller-manager
        # this can be replaced with any other image for out-of-tree providers
        image: registry.k8s.io/cloud-controller-manager:v1.8.0
        command:
        - /usr/local/bin/cloud-controller-manager
        - --cloud-provider=[YOUR_CLOUD_PROVIDER]  # Add your own cloud provider here!
        - --leader-elect=true
        - --use-service-account-credentials
        # these flags will vary for every cloud provider
        - --allocate-node-cidrs=true
        - --configure-cloud-routes=true
        - --cluster-cidr=172.17.0.0/16
      tolerations:
      # this is required so CCM can bootstrap itself
      - key: node.cloudprovider.kubernetes.io/uninitialized
        value: "true"
        effect: NoSchedule
      # these tolerations are to have the daemonset runnable on control plane nodes
      # remove them if your control plane nodes should not run pods
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      # this is to restrict CCM to only run on master nodes
      # the node selector may vary depending on your cluster setup
      nodeSelector:
        node-role.kubernetes.io/master: ""

制限

クラウドコントローラーマネージャーの実行にはいくつかの制限があります。これらの制限は今後のリリースで対処されますが、本番のワークロードにおいてはこれらの制限を認識することが重要です。

ボリュームのサポート

ボリュームの統合にはkubeletとの調整も必要になるためクラウドコントローラーマネージャーはkube-controller-managerにあるボリュームコントローラーを実装しません。CSI(コンテナストレージインターフェース)が進化してFlexボリュームプラグインの強力なサポートが追加されるにつれ、クラウドがボリュームと完全に統合できるようクラウドコントローラーマネージャーに必要なサポートが追加されます。Kubernetesリポジトリの外部にあるCSIボリュームプラグインの詳細についてはこちらをご覧ください。

スケーラビリティ

cloud-controller-managerは、クラウドプロバイダーのAPIにクエリーを送信して、すべてのノードの情報を取得します。非常に大きなクラスターの場合、リソース要件やAPIレートリミットなどのボトルネックの可能性を考慮する必要があります。

鶏と卵

クラウドコントローラーマネージャープロジェクトの目標はKubernetesのコアプロジェクトからクラウドに関する機能の開発を切り離すことです。残念ながら、Kubernetesプロジェクトの多くの面でクラウドプロバイダーの機能がKubernetesプロジェクトに緊密に結びついているという前提があります。そのため、この新しいアーキテクチャを採用するとクラウドプロバイダーの情報を要求する状況が発生する可能性がありますが、クラウドコントローラーマネージャーはクラウドプロバイダーへのリクエストが完了するまでその情報を返すことができない場合があります。

これの良い例は、KubeletのTLSブートストラップ機能です。TLSブートストラップはKubeletがすべてのアドレスタイプ(プライベート、パブリックなど)をクラウドプロバイダー(またはローカルメタデータサービス)に要求する能力を持っていると仮定していますが、クラウドコントローラーマネージャーは最初に初期化されない限りノードのアドレスタイプを設定できないためapiserverと通信するためにはkubeletにTLS証明書が必要です。

このイニシアチブが成熟するに連れ、今後のリリースでこれらの問題に対処するための変更が行われます。

次の項目

独自のクラウドコントローラーマネージャーを構築および開発するにはクラウドコントローラーマネージャーの開発を参照してください。

4.2.7 - ノードのトポロジー管理ポリシーを制御する

FEATURE STATE: Kubernetes v1.18 [beta]

近年、CPUやハードウェア・アクセラレーターの組み合わせによって、レイテンシーが致命的となる実行や高いスループットを求められる並列計算をサポートするシステムが増えています。このようなシステムには、通信、科学技術計算、機械学習、金融サービス、データ分析などの分野のワークロードが含まれます。このようなハイブリッドシステムは、高い性能の環境で構成されます。

最高のパフォーマンスを引き出すために、CPUの分離やメモリーおよびデバイスの位置に関する最適化が求められます。しかしながら、Kubernetesでは、これらの最適化は分断されたコンポーネントによって処理されます。

トポロジーマネージャー はKubeletコンポーネントの1つで最適化の役割を担い、コンポーネント群を調和して機能させます。

始める前に

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

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

トポロジーマネージャーはどのように機能するか

トポロジーマネージャー導入前は、KubernetesにおいてCPUマネージャーやデバイスマネージャーはそれぞれ独立してリソースの割り当てを決定します。 これは、マルチソケットのシステムでは望ましくない割り当てとなり、パフォーマンスやレイテンシーが求められるアプリケーションは、この望ましくない割り当てに悩まされます。 この場合の望ましくない例として、CPUやデバイスが異なるNUMAノードに割り当てられ、それによりレイテンシー悪化を招くことが挙げられます。

トポロジーマネージャーはKubeletコンポーネントであり、信頼できる情報源として振舞います。それによって、他のKubeletコンポーネントはトポロジーに沿ったリソース割り当ての選択を行うことができます。

トポロジーマネージャーは Hint Providers と呼ばれるコンポーネントのインターフェースを提供し、トポロジー情報を送受信します。トポロジーマネージャーは、ノード単位のポリシー群を保持します。ポリシーについて以下で説明します。

トポロジーマネージャーは Hint Providers からトポロジー情報を受け取ります。トポロジー情報は、利用可能なNUMAノードと優先割り当て表示を示すビットマスクです。トポロジーマネージャーのポリシーは、提供されたヒントに対して一連の操作を行い、ポリシーに沿ってヒントをまとめて最適な結果を得ます。もし、望ましくないヒントが保存された場合、ヒントの優先フィールドがfalseに設定されます。現在のポリシーでは、最も狭い優先マスクが優先されます。

選択されたヒントはトポロジーマネージャーの一部として保存されます。設定されたポリシーにしたがい、選択されたヒントに基づいてノードがPodを許可したり、拒否することができます。 トポロジーマネージャーに保存されたヒントは、Hint Providers が使用しリソース割り当てを決定します。

トポロジーマネージャーの機能を有効にする

トポロジーマネージャーをサポートするには、TopologyManager フィーチャーゲートを有効にする必要があります。Kubernetes 1.18ではデフォルトで有効です。

トポロジーマネージャーのスコープとポリシー

トポロジーマネージャは現在:

  • 全てのQoAクラスのPodを調整する
  • Hint Providerによって提供されたトポロジーヒントから、要求されたリソースを調整する

これらの条件が合致した場合、トポロジーマネージャーは要求されたリソースを調整します。

この調整をどのように実行するかカスタマイズするために、トポロジーマネージャーは2つのノブを提供します: スコープポリシーです。

スコープはリソースの配置を行う粒度を定義します(例:podcontainer)。そして、ポリシーは調整を実行するための実戦略を定義します(best-effort, restricted, single-numa-node等)。

現在利用可能なスコープポリシーの値について詳細は以下の通りです。

トポロジーマネージャーのスコープ

トポロジーマネージャーは、以下の複数の異なるスコープでリソースの調整を行う事が可能です:

  • container (デフォルト)
  • pod

いずれのオプションも、--topology-manager-scopeフラグによって、kubelet起動時に選択できます。

containerスコープ

containerスコープはデフォルトで使用されます。

このスコープでは、トポロジーマネージャーは連続した複数のリソース調整を実行します。つまり、Pod内の各コンテナは、分離された配置計算がされます。言い換えると、このスコープでは、コンテナを特定のNUMAノードのセットにグループ化するという概念はありません。実際には、トポロジーマネージャーは各コンテナのNUMAノードへの配置を任意に実行します。

コンテナをグループ化するという概念は、以下のスコープで設定・実行されます。例えば、podスコープが挙げられます。

podスコープ

podスコープを選択するには、コマンドラインで--topology-manager-scope=podオプションを指定してkubeletを起動します。

このスコープでは、Pod内全てのコンテナを共通のNUMAノードのセットにグループ化することができます。トポロジーマネージャーはPodをまとめて1つとして扱い、ポッド全体(全てのコンテナ)を単一のNUMAノードまたはNUMAノードの共通セットのいずれかに割り当てようとします。以下の例は、さまざまな場面でトポロジーマネージャーが実行する調整を示します:

  • 全てのコンテナは、単一のNUMAノードに割り当てられます。
  • 全てのコンテナは、共有されたNUMAノードのセットに割り当てられます。

Pod全体に要求される特定のリソースの総量は有効なリクエスト/リミットの式に従って計算されるため、この総量の値は以下の最大値となります。

  • 全てのアプリケーションコンテナのリクエストの合計。
  • リソースに対するinitコンテナのリクエストの最大値。

podスコープとsingle-numa-nodeトポロジーマネージャーポリシーを併用することは、レイテンシーが重要なワークロードやIPCを行う高スループットのアプリケーションに対して特に有効です。両方のオプションを組み合わせることで、Pod内の全てのコンテナを単一のNUMAノードに配置できます。そのため、PodのNUMA間通信によるオーバーヘッドを排除することができます。

single-numa-nodeポリシーの場合、可能な割り当ての中に適切なNUMAノードのセットが存在する場合にのみ、Podが許可されます。上の例をもう一度考えてみましょう:

  • 1つのNUMAノードのみを含むセット - Podが許可されます。
  • 2つ以上のNUMAノードを含むセット - Podが拒否されます(1つのNUMAノードの代わりに、割り当てを満たすために2つ以上のNUMAノードが必要となるため)。

要約すると、トポロジーマネージャーはまずNUMAノードのセットを計算し、それをトポロジーマネージャーのポリシーと照合し、Podの拒否または許可を検証します。

トポロジーマネージャーのポリシー

トポロジーマネージャーは4つの調整ポリシーをサポートします。--topology-manager-policyというKubeletフラグを通してポリシーを設定できます。 4つのサポートされるポリシーがあります:

  • none (デフォルト)
  • best-effort
  • restricted
  • single-numa-node

none ポリシー

これはデフォルトのポリシーで、トポロジーの調整を実行しません。

best-effort ポリシー

Pod内の各コンテナに対して、best-effort トポロジー管理ポリシーが設定されたkubeletは、各Hint Providerを呼び出してそれらのリソースの可用性を検出します。 トポロジーマネージャーはこの情報を使用し、そのコンテナの推奨されるNUMAノードのアフィニティーを保存します。アフィニティーが優先されない場合、トポロジーマネージャーはこれを保存し、Podをノードに許可します。

Hint Providers はこの情報を使ってリソースの割り当てを決定します。

restricted ポリシー

Pod内の各コンテナに対して、restricted トポロジー管理ポリシーが設定されたkubeletは各Hint Providerを呼び出してそれらのリソースの可用性を検出します。 トポロジーマネージャーはこの情報を使用し、そのコンテナの推奨されるNUMAノードのアフィニティーを保存します。アフィニティーが優先されない場合、トポロジーマネージャーはPodをそのノードに割り当てることを拒否します。この結果、PodはPodの受付失敗となりTerminated 状態になります。

Podが一度Terminated状態になると、KubernetesスケジューラーはPodの再スケジューリングを試み ません 。Podの再デプロイをするためには、ReplicasetかDeploymenを使用してください。Topology Affinityエラーとなったpodを再デプロイするために、外部のコントロールループを実行することも可能です。

Podが許可されれば、 Hint Providers はこの情報を使ってリソースの割り当てを決定します。

single-numa-node ポリシー

Pod内の各コンテナに対して、single-numa-nodeトポロジー管理ポリシーが設定されたkubeletは各Hint Prociderを呼び出してそれらのリソースの可用性を検出します。 トポロジーマネージャーはこの情報を使用し、単一のNUMAノードアフィニティが可能かどうか決定します。 可能な場合、トポロジーマネージャーは、この情報を保存し、Hint Providers はこの情報を使ってリソースの割り当てを決定します。 不可能な場合、トポロジーマネージャーは、Podをそのノードに割り当てることを拒否します。この結果、Pod は Pod の受付失敗となりTerminated状態になります。

Podが一度Terminated状態になると、KubernetesスケジューラーはPodの再スケジューリングを試みません。Podの再デプロイをするためには、ReplicasetかDeploymentを使用してください。Topology Affinityエラーとなったpodを再デプロイするために、外部のコントロールループを実行することも可能です。

Podとトポロジー管理ポリシーの関係

以下のようなpodのSpecで定義されるコンテナを考えます:

spec:
  containers:
  - name: nginx
    image: nginx

requestslimitsも定義されていないため、このPodはBestEffortQoSクラスで実行します。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

requestsがlimitsより小さい値のため、このPodはBurstableQoSクラスで実行します。

選択されたポリシーがnone以外の場合、トポロジーマネージャーは、これらのPodのSpecを考慮します。トポロジーマネージャーは、Hint Providersからトポロジーヒントを取得します。CPUマネージャーポリシーがstaticの場合、デフォルトのトポロジーヒントを返却します。これらのPodは明示的にCPUリソースを要求していないからです。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

整数値でCPUリクエストを指定されたこのPodは、requestslimitsが同じ値のため、GuaranteedQoSクラスで実行します。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

CPUの一部をリクエストで指定されたこのPodは、requestslimitsが同じ値のため、GuaranteedQoSクラスで実行します。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        example.com/deviceA: "1"
        example.com/deviceB: "1"
      requests:
        example.com/deviceA: "1"
        example.com/deviceB: "1"

CPUもメモリもリクエスト値がないため、このPodは BestEffort QoSクラスで実行します。

トポロジーマネージャーは、上記Podを考慮します。トポロジーマネージャーは、Hint ProvidersとなるCPUマネージャーとデバイスマネージャーに問い合わせ、トポロジーヒントを取得します。

整数値でCPU要求を指定されたGuaranteedQoSクラスのPodの場合、staticが設定されたCPUマネージャーポリシーは、排他的なCPUに関するトポロジーヒントを返却し、デバイスマネージャーは要求されたデバイスのヒントを返します。

CPUの一部を要求を指定されたGuaranteedQoSクラスのPodの場合、排他的ではないCPU要求のためstaticが設定されたCPUマネージャーポリシーはデフォルトのトポロジーヒントを返却します。デバイスマネージャーは要求されたデバイスのヒントを返します。

上記のGuaranteedQoSクラスのPodに関する2ケースでは、noneで設定されたCPUマネージャーポリシーは、デフォルトのトポロジーヒントを返却します。

BestEffortQoSクラスのPodの場合、staticが設定されたCPUマネージャーポリシーは、CPUの要求がないためデフォルトのトポロジーヒントを返却します。デバイスマネージャーは要求されたデバイスごとのヒントを返します。

トポロジーマネージャーはこの情報を使用してPodに最適なヒントを計算し保存します。保存されたヒントは Hint Providersが使用しリソースを割り当てます。

既知の制限

  1. トポロジーマネージャーが許容するNUMAノードの最大値は8です。8より多いNUMAノードでは、可能なNUMAアフィニティを列挙しヒントを生成する際に、生成する状態数が爆発的に増加します。

  2. スケジューラーはトポロジーを意識しません。そのため、ノードにスケジュールされた後に実行に失敗する可能性があります。

4.2.8 - ネットワークポリシーを宣言する

このドキュメントでは、Pod同士の通信を制御するネットワークポリシーを定義するための、KubernetesのNetworkPolicy APIを使い始める手助けをします。

始める前に

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

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

ネットワークポリシーをサポートしているネットワークプロバイダーが設定済みであることを確認してください。さまざまなネットワークプロバイダーがNetworkPolicyをサポートしています。次に挙げるのは一例です。

nginx Deploymentを作成してService経由で公開する

Kubernetesのネットワークポリシーの仕組みを理解するために、まずはnginx Deploymentを作成することから始めましょう。

kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

nginxという名前のService経由でDeploymentを公開します。

kubectl expose deployment nginx --port=80
service/nginx exposed

上記のコマンドを実行すると、nginx Podを持つDeploymentが作成され、そのDeploymentがnginxという名前のService経由で公開されます。nginxのPodおよびDeploymentはdefault名前空間の中にあります。

kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/kubernetes          10.100.0.1    <none>        443/TCP    46m
service/nginx               10.100.0.16   <none>        80/TCP     33s

NAME                        READY         STATUS        RESTARTS   AGE
pod/nginx-701339712-e0qfq   1/1           Running       0          35s

もう1つのPodからアクセスしてServiceを検証する

これで、新しいnginxサービスに他のPodからアクセスできるようになったはずです。default名前空間内の他のPodからnginx Serviceにアクセスするために、busyboxコンテナを起動します。

kubectl run busybox --rm -ti --image=busybox -- /bin/sh

シェルの中で、次のコマンドを実行します。

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

nginx Serviceへのアクセスを制限する

nginx Serviceへのアクセスを制限するために、access: trueというラベルが付いたPodだけがクエリできるようにします。次の内容でNetworkPolicyオブジェクトを作成してください。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

NetworkPolicyオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。

Serviceにポリシーを割り当てる

kubectlを使って、上記のnginx-policy.yamlファイルからNetworkPolicyを作成します。

kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created

accessラベルが定義されていない状態でServiceへのアクセスをテストする

nginx Serviceに正しいラベルが付いていないPodからアクセスを試してみると、リクエストがタイムアウトします。

kubectl run busybox --rm -ti --image=busybox -- /bin/sh

シェルの中で、次のコマンドを実行します。

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out

accessラベルを定義して再テストする

正しいラベルが付いたPodを作成すると、リクエストが許可されるようになるのがわかります。

kubectl run busybox --rm -ti --labels="access=true" --image=busybox -- /bin/sh

シェルの中で、次のコマンドを実行します。

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

4.2.9 - クラウドコントローラーマネージャーの開発

cloud-controller-managerは クラウド特有の制御ロジックを組み込むKubernetesのcontrol planeコンポーネントです。クラウドコントロールマネージャーは、クラスターをクラウドプロバイダーAPIをリンクし、クラスターのみで相互作用するコンポーネントからクラウドプラットフォームで相互作用するコンポーネントを分離します。

Kubernetesと下のクラウドインフラストラクチャー間の相互運用ロジックを分離することで、cloud-controller-managerコンポーネントはクラウドプロバイダを主なKubernetesプロジェクトと比較し異なるペースで機能をリリース可能にします。

背景

クラウドプロバイダーはKubernetesプロジェクトとは異なる速度で開発しリリースすることから、プロバイダー特有なコードをcloud-controller-managerバイナリから抽象化することで、クラウドベンダーはコアKubernetesコードから独立して発展することができます。

Kubernetesプロジェクトは、(クラウドプロバイダーの)独自実装を組み込めるGoインターフェースを備えたcloud-controller-managerのスケルトンコードを提供しています。これは、クラウドプロバイダーがKubernetesコアからパッケージをインポートすることでcloud-controller-managerを実装できることを意味します。各クラウドプロバイダーは利用可能なクラウドプロバイダーのグローバル変数を更新するためにcloudprovider.RegisterCloudProviderを呼び出し、独自のコードを登録します。

開発

Kubernetesには登録されていない独自クラウドプロバイダー

Kubernetesには登録されていない独自のクラウドプロバイダーのクラウドコントローラーマネージャーを構築するには、

  1. cloudprovider.Interfaceを満たす go パッケージを実装します。
  2. Kubernetesのコアにあるcloud-controller-managerのmain.goをあなたのmain.goのテンプレートとして利用します。上で述べたように、唯一の違いはインポートされるクラウドパッケージのみです。
  3. クラウドパッケージを main.go にインポートし、パッケージに cloudprovider.RegisterCloudProvider を実行するための init ブロックがあることを確認します。

多くのクラウドプロバイダーはオープンソースとしてコントローラーマネージャーのコードを公開しています。新たにcloud-controller-managerをスクラッチから開発する際には、既存のKubernetesには登録されていない独自クラウドプロバイダーのコントローラーマネージャーを開始地点とすることができます。

Kubernetesに登録されているクラウドプロバイダー

Kubernetesに登録されているクラウドプロバイダーであれば、DaemonSetを使ってあなたのクラスターで動かすことができます。詳細についてはKubernetesクラウドコントローラーマネージャーを参照してください。

4.2.10 - Kubernetes向けetcdクラスターの運用

etcdは 一貫性、高可用性を持ったキーバリューストアで、Kubernetesの全てのクラスター情報の保存場所として利用されています。

etcdをKubernetesのデータストアとして使用する場合、必ずデータのバックアッププランを作成して下さい。

公式ドキュメントでetcdに関する詳細な情報を見つけることができます。

始める前に

Kubernetesクラスターが必要で、kubectlコマンドラインツールがクラスターと通信できるように設定されている必要があります。 コントロールプレーンノード以外に少なくとも2つのノードを持つクラスターで、このガイドに従うことを推奨します。 まだクラスターを用意していない場合は、minikubeを使用して作成することができます。

前提条件

  • etcdは、奇数のメンバーを持つクラスターとして実行します。

  • etcdはリーダーベースの分散システムです。リーダーが定期的に全てのフォロワーにハートビートを送信し、クラスターの安定性を維持するようにします。

  • リソース不足が発生しないようにします。

    クラスターのパフォーマンスと安定性は、ネットワークとディスクのI/Oに敏感です。 リソース不足はハートビートのタイムアウトを引き起こし、クラスターの不安定化につながる可能性があります。 不安定なetcdは、リーダーが選出されていないことを意味します。 そのような状況では、クラスターは現在の状態に変更を加えることができません。 これは、新しいPodがスケジュールされないことを意味します。

  • Kubernetesクラスターの安定性には、etcdクラスターの安定性が不可欠です。 したがって、etcdクラスターは専用のマシンまたは保証されたリソース要件を持つ隔離された環境で実行してください。

  • 本番環境で実行するために推奨される最低限のetcdバージョンは、3.4.22以降あるいは3.5.6以降です。

リソース要件

限られたリソースでetcdを運用するのはテスト目的にのみ適しています。 本番環境へのデプロイには、高度なハードウェア構成が必要です。 本番環境へetcdをデプロイする前に、リソース要件を確認してください。

etcdクラスターの起動

このセクションでは、単一ノードおよびマルチノードetcdクラスターの起動について説明します。

単一ノードetcdクラスター

単一ノードetcdクラスターは、テスト目的でのみ使用してください。

  1. 以下を実行します:

    etcd --listen-client-urls=http://$PRIVATE_IP:2379 \
       --advertise-client-urls=http://$PRIVATE_IP:2379
    
  2. Kubernetes APIサーバーをフラグ--etcd-servers=$PRIVATE_IP:2379で起動します。

    PRIVATE_IPがetcdクライアントIPに設定されていることを確認してください。

マルチノードetcdクラスター

耐久性と高可用性のために、本番環境ではマルチノードクラスターとしてetcdを実行し、定期的にバックアップを取ります。 本番環境では5つのメンバーによるクラスターが推奨されます。 詳細はFAQドキュメントを参照してください。

etcdクラスターは、静的なメンバー情報、または動的な検出によって構成されます。 クラスタリングに関する詳細は、etcdクラスタリングドキュメントを参照してください。

例として、次のクライアントURLで実行される5つのメンバーによるetcdクラスターを考えてみます。 5つのURLは、http://$IP1:2379http://$IP2:2379http://$IP3:2379http://$IP4:2379、およびhttp://$IP5:2379です。Kubernetes APIサーバーを起動するには、

  1. 以下を実行します:

    etcd --listen-client-urls=http://$IP1:2379,http://$IP2:2379,http://$IP3:2379,http://$IP4:2379,http://$IP5:2379 --advertise-client-urls=http://$IP1:2379,http://$IP2:2379,http://$IP3:2379,http://$IP4:2379,http://$IP5:2379
    
  2. フラグ--etcd-servers=$IP1:2379,$IP2:2379,$IP3:2379,$IP4:2379,$IP5:2379を使ってKubernetes APIサーバーを起動します。

    IP<n>変数がクライアントのIPアドレスに設定されていることを確認してください。

ロードバランサーを使用したマルチノードetcdクラスター

ロードバランシングされたetcdクラスターを実行するには、次の手順に従います。

  1. etcdクラスターを設定します。
  2. etcdクラスターの前にロードバランサーを設定します。 例えば、ロードバランサーのアドレスを$LBとします。
  3. フラグ--etcd-servers=$LB:2379を使ってKubernetes APIサーバーを起動します。

etcdクラスターのセキュリティ確保

etcdへのアクセスはクラスター内でのルート権限に相当するため、理想的にはAPIサーバーのみがアクセスできるようにするべきです。 データの機密性を考慮して、etcdクラスターへのアクセス権を必要とするノードのみに付与することが推奨されます。

etcdをセキュアにするためには、ファイアウォールのルールを設定するか、etcdによって提供されるセキュリティ機能を使用します。 etcdのセキュリティ機能はx509公開鍵基盤(PKI)に依存します。 この機能を使用するには、キーと証明書のペアを生成して、セキュアな通信チャンネルを確立します。 例えば、etcdメンバー間の通信をセキュアにするためにpeer.keypeer.certのキーペアを使用し、etcdとそのクライアント間の通信をセキュアにするためにclient.keyclient.certを使用します。 クライアント認証用のキーペアとCAファイルを生成するには、etcdプロジェクトによって提供されているサンプルスクリプトを参照してください。

通信のセキュリティ確保

セキュアなピア通信を持つetcdを構成するためには、--peer-key-file=peer.keyおよび--peer-cert-file=peer.certフラグを指定し、URLスキーマとしてHTTPSを使用します。

同様に、セキュアなクライアント通信を持つetcdを構成するためには、--key-file=k8sclient.keyおよび--cert-file=k8sclient.certフラグを指定し、URLスキーマとしてHTTPSを使用します。 セキュアな通信を使用するクライアントコマンドの例は以下の通りです:

ETCDCTL_API=3 etcdctl --endpoints 10.2.0.9:2379 \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  member list

etcdクラスターへのアクセス制限

セキュアな通信を構成した後、TLS認証を使用して、etcdクラスターへのアクセスをKubernetes APIサーバーのみに制限します。

例えば、CA etcd.caによって信頼されるキーペアk8sclient.keyk8sclient.certを考えてみます。 --client-cert-authとTLSを使用してetcdが構成されている場合、etcdは--trusted-ca-fileフラグで渡されたCAまたはシステムのCAを使用してクライアントからの証明書を検証します。 --client-cert-auth=trueおよび--trusted-ca-file=etcd.caフラグを指定することで、証明書k8sclient.certを持つクライアントのみにアクセスを制限します。

etcdが正しく構成されると、有効な証明書を持つクライアントのみがアクセスできます。 Kubernetes APIサーバーにアクセス権を与えるためには、--etcd-certfile=k8sclient.cert--etcd-keyfile=k8sclient.keyおよび--etcd-cafile=ca.certフラグで構成します。

障害が発生したetcdメンバーの交換

etcdクラスターは、一部のメンバーの障害を許容することで高可用性を実現します。 しかし、クラスターの全体的な状態を改善するためには、障害が発生したメンバーを直ちに交換することが重要です。 複数のメンバーに障害が発生した場合は、1つずつ交換します。 障害が発生したメンバーを交換するには、メンバーを削除し、新しいメンバーを追加するという2つのステップがあります。

etcdは内部でユニークなメンバーIDを保持していますが、人的なミスを避けるためにも各メンバーにはユニークな名前を使用することが推奨されます。 例えば、3つのメンバーのetcdクラスターを考えてみましょう。 URLがmember1=http://10.0.0.1member2=http://10.0.0.2、そしてmember3=http://10.0.0.3だとします。 member1に障害が発生した場合、member4=http://10.0.0.4で交換します。

  1. 障害が発生したmember1のメンバーIDを取得します:

    etcdctl --endpoints=http://10.0.0.2,http://10.0.0.3 member list
    

    次のメッセージが表示されます:

    8211f1d0f64f3269, started, member1, http://10.0.0.1:2380, http://10.0.0.1:2379
    91bc3c398fb3c146, started, member2, http://10.0.0.2:2380, http://10.0.0.2:2379
    fd422379fda50e48, started, member3, http://10.0.0.3:2380, http://10.0.0.3:2379
    
  2. 以下のいずれかを行います:

    1. 各Kubernetes APIサーバーが全てのetcdメンバーと通信するように構成されている場合、--etcd-serversフラグから障害が発生したメンバーを削除し、各Kubernetes APIサーバーを再起動します。
    2. 各Kubernetes APIサーバーが単一のetcdメンバーと通信している場合、障害が発生したetcdと通信しているKubernetes APIサーバーを停止します。
  3. 壊れたノード上のetcdサーバーを停止します。 Kubernetes APIサーバー以外のクライアントからetcdにトラフィックが流れている可能性があり、データディレクトリへの書き込みを防ぐためにすべてのトラフィックを停止することが望ましいです。

  4. メンバーを削除します:

    etcdctl member remove 8211f1d0f64f3269
    

    次のメッセージが表示されます:

    Removed member 8211f1d0f64f3269 from cluster
    
  5. 新しいメンバーを追加します:

    etcdctl member add member4 --peer-urls=http://10.0.0.4:2380
    

    次のメッセージが表示されます:

    Member 2be1eb8f84b7f63e added to cluster ef37ad9dc622a7c4
    
  6. IP 10.0.0.4のマシン上で新たに追加されたメンバーを起動します:

    export ETCD_NAME="member4"
    export ETCD_INITIAL_CLUSTER="member2=http://10.0.0.2:2380,member3=http://10.0.0.3:2380,member4=http://10.0.0.4:2380"
    export ETCD_INITIAL_CLUSTER_STATE=existing
    etcd [flags]
    
  7. 以下のいずれかを行います:

    1. 各Kubernetes APIサーバーが全てのetcdメンバーと通信するように構成されている場合、--etcd-serversフラグに新たに追加されたメンバーを加え、各Kubernetes APIサーバーを再起動します。
    2. 各Kubernetes APIサーバーが単一のetcdメンバーと通信している場合、ステップ2で停止したKubernetes APIサーバーを起動します。 その後、Kubernetes APIサーバークライアントを再度構成して、停止されたKubernetes APIサーバーへのリクエストをルーティングします。 これは多くの場合、ロードバランサーを構成することで行われます。

クラスターの再構成に関する詳細については、etcd再構成ドキュメントを参照してください。

etcdクラスターのバックアップ

すべてのKubernetesオブジェクトはetcdに保存されています。 定期的にetcdクラスターのデータをバックアップすることは、すべてのコントロールプレーンノードを失うなどの災害シナリオでKubernetesクラスターを復旧するために重要です。 スナップショットファイルには、すべてのKubernetesの状態と重要な情報が含まれています。 機密性の高いKubernetesデータを安全に保つために、スナップショットファイルを暗号化してください。

etcdクラスターのバックアップは、etcdのビルトインスナップショットとボリュームスナップショットの2つの方法で実現できます。

ビルトインスナップショット

etcdはビルトインスナップショットをサポートしています。 スナップショットは、etcdctl snapshot saveコマンドを使用してライブメンバーから、あるいはetcdプロセスによって現在使用されていないデータディレクトリからmember/snap/dbファイルをコピーして作成できます。 スナップショットを作成しても、メンバーのパフォーマンスに影響はありません。

以下は、$ENDPOINTによって提供されるキースペースのスナップショットをsnapshot.dbファイルに作成する例です:

ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db

スナップショットを確認します:

ETCDCTL_API=3 etcdctl --write-out=table snapshot status snapshot.db
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| fe01cf57 |       10 |          7 | 2.1 MB     |
+----------+----------+------------+------------+

ボリュームスナップショット

etcdがAmazon Elastic Block Storeのようなバックアップをサポートするストレージボリューム上で実行されている場合、ストレージボリュームのスナップショットを作成することによってetcdデータをバックアップします。

etcdctlオプションを使用したスナップショット

etcdctlによって提供されるさまざまなオプションを使用してスナップショットを作成することもできます。例えば

ETCDCTL_API=3 etcdctl -h

はetcdctlから利用可能なさまざまなオプションを一覧表示します。 例えば、以下のようにエンドポイント、証明書、キーを指定してスナップショットを作成することができます:

ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> \
  snapshot save <backup-file-location>

ここで、trusted-ca-filecert-filekey-fileはetcd Podの説明から取得できます。

etcdクラスターのスケールアウト

etcdクラスターのスケールアウトは、パフォーマンスとのトレードオフで可用性を高めます。 スケーリングはクラスターのパフォーマンスや能力を高めるものではありません。 一般的なルールとして、etcdクラスターをスケールアウトまたはスケールインすることはありません。 etcdクラスターに自動スケーリンググループを設定しないでください。 公式にサポートされるどんなスケールの本番環境のKubernetesクラスターにおいても、常に静的な5つのメンバーのetcdクラスターを運用することを強く推奨します。

合理的なスケーリングは、より高い信頼性が求められる場合に、3つのメンバーで構成されるクラスターを5つのメンバーにアップグレードすることです。 既存のクラスターにメンバーを追加する方法については、etcdの再構成ドキュメントを参照してください。

etcdクラスターの復元

etcdは、major.minorバージョンのetcdプロセスから取得されたスナップショットからの復元をサポートしています。 異なるパッチバージョンのetcdからのバージョン復元もサポートされています。 復元操作は、障害が発生したクラスターのデータを回復するために用いられます。

復元操作を開始する前に、スナップショットファイルが存在している必要があります。 これは、以前のバックアップ操作からのスナップショットファイルでも、残っているデータディレクトリからのスナップショットファイルでも構いません。

クラスターを復元する場合は、--data-dirオプションを使用して、クラスターをどのフォルダーに復元するかを指定します:

ETCDCTL_API=3 etcdctl --data-dir <data-dir-location> snapshot restore snapshot.db

ここで、<data-dir-location>は復元プロセス中に作成されるディレクトリです。

もう一つの例としては、まずETCDCTL_API環境変数をエクスポートします:

export ETCDCTL_API=3
etcdctl --data-dir <data-dir-location> snapshot restore snapshot.db

<data-dir-location>が以前と同じフォルダーである場合は、クラスターを復元する前にそれを削除してetcdプロセスを停止します。そうでない場合は、etcdの構成を変更し、復元後にetcdプロセスを再起動して新しいデータディレクトリを使用するようにします。

スナップショットファイルからクラスターを復元する方法と例についての詳細は、etcd災害復旧ドキュメントを参照してください。

復元されたクラスターのアクセスURLが前のクラスターと異なる場合、Kubernetes APIサーバーをそれに応じて再設定する必要があります。 この場合、--etcd-servers=$OLD_ETCD_CLUSTERのフラグの代わりに、--etcd-servers=$NEW_ETCD_CLUSTERのフラグでKubernetes APIサーバーを再起動します。 $NEW_ETCD_CLUSTER$OLD_ETCD_CLUSTERをそれぞれのIPアドレスに置き換えてください。 etcdクラスターの前にロードバランサーを使用している場合、代わりにロードバランサーを更新する必要があるかもしれません。

etcdメンバーの過半数に永続的な障害が発生した場合、etcdクラスターは故障したと見なされます。 このシナリオでは、Kubernetesは現在の状態に対して変更を加えることができません。 スケジュールされたPodは引き続き実行されるかもしれませんが、新しいPodはスケジュールできません。 このような場合、etcdクラスターを復旧し、必要に応じてKubernetes APIサーバーを再設定して問題を修正します。

etcdクラスターのアップグレード

etcdのアップグレードに関する詳細は、etcdアップグレードのドキュメントを参照してください。

etcdクラスターのメンテナンス

etcdのメンテナンスに関する詳細は、etcdメンテナンスのドキュメントを参照してください。

4.2.11 - クラスターのセキュリティ

このドキュメントでは、偶発的または悪意のあるアクセスからクラスターを保護するためのトピックについて説明します。 また、全体的なセキュリティに関する推奨事項を提供します。

始める前に

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

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

Kubernetes APIへのアクセスの制御

Kubernetesは完全にAPI駆動であるため、誰がクラスターにアクセスできるか、どのようなアクションを実行できるかを制御・制限することが第一の防御策となります。

すべてのAPIトラフィックにTLS(Transport Layer Security)を使用する

Kubernetesは、クラスター内のすべてのAPI通信がデフォルトでTLSにより暗号化されていることを期待しており、大半のインストール方法では、必要な証明書を作成してクラスターコンポーネントに配布することができます。

コンポーネントやインストール方法によっては、HTTP上のローカルポートを有効にする場合があることに注意してください。管理者は、潜在的に保護されていないトラフィックを特定するために、各コンポーネントの設定に精通している必要があります。

APIの認証

クラスターのインストール時に、共通のアクセスパターンに合わせて、APIサーバーが使用する認証メカニズムを選択します。 例えば、シングルユーザーの小規模なクラスターでは、シンプルな証明書や静的なBearerトークンを使用することができます。 大規模なクラスターでは、ユーザーをグループに細分化できる既存のOIDCまたはLDAPサーバーを統合することができます。

ノード、プロキシ、スケジューラー、ボリュームプラグインなど、インフラの一部であるものも含めて、すべてのAPIクライアントを認証する必要があります。 これらのクライアントは通常、service accountsであるか、またはx509クライアント証明書を使用しており、クラスター起動時に自動的に作成されるか、クラスターインストールの一部として設定されます。

詳細については、認証を参照してください。

APIの認可

認証されると、すべてのAPIコールは認可チェックを通過することになります。

Kubernetesには、統合されたRBACコンポーネントが搭載されており、入力されたユーザーやグループを、ロールにまとめられたパーミッションのセットにマッチさせます。 これらのパーミッションは、動詞(get, create, delete)とリソース(pods, services, nodes)を組み合わせたもので、ネームスペース・スコープまたはクラスター・スコープに対応しています。 すぐに使えるロールのセットが提供されており、クライアントが実行したいアクションに応じて、デフォルトで適切な責任の分離を提供します。

NodeRBACの承認者は、NodeRestrictionのアドミッションプラグインと組み合わせて使用することをお勧めします。

認証の場合と同様に、小規模なクラスターにはシンプルで幅広い役割が適切かもしれません。 しかし、より多くのユーザーがクラスターに関わるようになるとチームを別の名前空間に分け、より限定的な役割を持たせることが必要になるかもしれません。 認可においては、あるオブジェクトの更新が、他の場所でどのようなアクションを起こすかを理解することが重要です。

たとえば、ユーザーは直接Podを作成することはできませんが、ユーザーに代わってPodを作成するDeploymentの作成を許可することで、間接的にそれらのPodを作成することができます。 同様に、APIからノードを削除すると、そのノードにスケジューリングされていたPodが終了し、他のノードに再作成されます。 すぐに使えるロールは、柔軟性と一般的なユースケースのバランスを表していますが、より限定的なロールは、偶発的なエスカレーションを防ぐために慎重に検討する必要があります。 すぐに使えるロールがニーズを満たさない場合は、ユースケースに合わせてロールを作成することができます。

詳しくはauthorization reference sectionに参照してください。

Kubeletへのアクセスの制御

Kubeletsは、ノードやコンテナの強力な制御を可能にするHTTPSエンドポイントを公開しています。 デフォルトでは、KubeletsはこのAPIへの認証されていないアクセスを許可しています。

本番環境のクラスターでは、Kubeletの認証と認可を有効にする必要があります。

詳細は、Kubelet 認証/認可に参照してください。

ワークロードやユーザーのキャパシティーを実行時に制御

Kubernetesにおける権限付与は、意図的にハイレベルであり、リソースに対する粗いアクションに焦点を当てています。

より強力なコントロールはpoliciesとして存在し、それらのオブジェクトがクラスターや自身、その他のリソースにどのように作用するかをユースケースによって制限します。

クラスターのリソース使用量の制限

リソースクォータは、ネームスペースに付与されるリソースの数や容量を制限するものです。

これは、ネームスペースが割り当てることのできるCPU、メモリー、永続的なディスクの量を制限するためによく使われますが、各ネームスペースに存在するPod、サービス、ボリュームの数を制御することもできます。

Limit rangesは、上記のリソースの一部の最大または最小サイズを制限することで、ユーザーがメモリーなどの一般的に予約されたリソースに対して不当に高いまたは低い値を要求するのを防いだり、何も指定されていない場合にデフォルトの制限を提供したりします。

コンテナが利用する特権の制御

Podの定義には、security contextが含まれており、ノード上の特定の Linux ユーザー(rootなど)として実行するためのアクセス、特権的に実行するためのアクセス、ホストネットワークにアクセスするためのアクセス、その他の制御を要求することができます。 Pod security policiesは、危険なセキュリティコンテキスト設定を提供できるユーザーやサービスアカウントを制限することができます。

たとえば、Podのセキュリティポリシーでは、ボリュームマウント、特にhostPathを制限することができ、これはPodの制御すべき側面です。 一般に、ほとんどのアプリケーションワークロードでは、ホストリソースへのアクセスを制限する必要があります。 ホスト情報にアクセスすることなく、ルートプロセス(uid 0)として正常に実行できます。 ただし、ルートユーザーに関連する権限を考慮して、非ルートユーザーとして実行するようにアプリケーションコンテナを記述する必要があります。

コンテナが不要なカーネルモジュールをロードしないようにします

Linuxカーネルは、ハードウェアが接続されたときやファイルシステムがマウントされたときなど、特定の状況下で必要となるカーネルモジュールをディスクから自動的にロードします。 特にKubernetesでは、非特権プロセスであっても、適切なタイプのソケットを作成するだけで、特定のネットワークプロトコル関連のカーネルモジュールをロードさせることができます。これにより、管理者が使用されていないと思い込んでいるカーネルモジュールのセキュリティホールを攻撃者が利用できる可能性があります。 特定のモジュールが自動的にロードされないようにするには、そのモジュールをノードからアンインストールしたり、ルールを追加してブロックしたりします。

ほとんどのLinuxディストリビューションでは、/etc/modprobe.d/kubernetes-blacklist.confのような内容のファイルを作成することで実現できます。

# DCCPは必要性が低く、複数の深刻な脆弱性があり、保守も十分ではありません。
blacklist dccp

# SCTPはほとんどのKubernetesクラスターでは使用されておらず、また過去には脆弱性がありました。
blacklist sctp

モジュールのロードをより一般的にブロックするには、SELinuxなどのLinuxセキュリティモジュールを使って、コンテナに対する module_request権限を完全に拒否し、いかなる状況下でもカーネルがコンテナ用のモジュールをロードできないようにすることができます。 (Podは、手動でロードされたモジュールや、より高い権限を持つプロセスに代わってカーネルがロードしたモジュールを使用することはできます)。

ネットワークアクセスの制限

名前空間のネットワークポリシーにより、アプリケーション作成者は、他の名前空間のPodが自分の名前空間内のPodやポートにアクセスすることを制限することができます。

サポートされているKubernetes networking providersの多くは、ネットワークポリシーを尊重するようになりました。 クォータやリミットの範囲は、ユーザーがノードポートや負荷分散サービスを要求するかどうかを制御するためにも使用でき、多くのクラスターでは、ユーザーのアプリケーションがクラスターの外で見えるかどうかを制御できます。 ノードごとのファイアウォール、クロストークを防ぐための物理的なクラスターノードの分離、高度なネットワークポリシーなど、プラグインや環境ごとにネットワークルールを制御する追加の保護機能が利用できる場合もあります。

クラウドメタデータのAPIアクセスを制限

クラウドプラットフォーム(AWS、Azure、GCEなど)では、しばしばメタデータサービスをインスタンスローカルに公開しています。 デフォルトでは、これらのAPIはインスタンス上で実行されているPodからアクセスでき、そのノードのクラウド認証情報や、kubelet認証情報などのプロビジョニングデータを含むことができます。 これらの認証情報は、クラスター内でのエスカレーションや、同じアカウントの他のクラウドサービスへのエスカレーションに使用できます。

クラウドプラットフォーム上でKubernetesを実行する場合は、インスタンスの認証情報に与えられるパーミッションを制限し、ネットワークポリシーを使用してメタデータAPIへのPodのアクセスを制限し、プロビジョニングデータを使用してシークレットを配信することは避けてください。

Podのアクセス可能ノードを制御

デフォルトでは、どのノードがPodを実行できるかについての制限はありません。 Kubernetesは、エンドユーザーが利用できるNode上へのPodのスケジューリングTaintとTolerationを提供します。 多くのクラスターでは、ワークロードを分離するためにこれらのポリシーを使用することは、作者が採用したり、ツールを使って強制したりする慣習になっています。

管理者としては、ベータ版のアドミッションプラグイン「PodNodeSelector」を使用して、ネームスペース内のPodをデフォルトまたは特定のノードセレクタを必要とするように強制することができます。 エンドユーザーがネームスペースを変更できない場合は、特定のワークロード内のすべてのPodの配置を強く制限することができます。

クラスターのコンポーネントの保護

このセクションでは、クラスターを危険から守るための一般的なパターンを説明します。

etcdへのアクセスの制限

API用のetcdバックエンドへの書き込みアクセスは、クラスター全体のrootを取得するのと同等であり、読み取りアクセスはかなり迅速にエスカレートするために使用できます。 管理者は、TLSクライアント証明書による相互認証など、APIサーバーからetcdサーバーへの強力な認証情報を常に使用すべきであり、API サーバーのみがアクセスできるファイアウォールの後ろにetcdサーバーを隔離することがしばしば推奨されます。

監査ログの有効

audit loggerはベータ版の機能で、APIによって行われたアクションを記録し、侵害があった場合に後から分析できるようにするものです。

監査ログを有効にして、ログファイルを安全なサーバーにアーカイブすることをお勧めします。

アルファまたはベータ機能へのアクセスの制限

アルファ版およびベータ版のKubernetesの機能は活発に開発が行われており、セキュリティ上の脆弱性をもたらす制限やバグがある可能性があります。 常に、アルファ版またはベータ版の機能が提供する価値と、セキュリティ体制に起こりうるリスクを比較して評価してください。 疑問がある場合は、使用しない機能を無効にしてください。

インフラの認証情報を頻繁に交換

秘密やクレデンシャルの有効期間が短いほど、攻撃者がそのクレデンシャルを利用することは難しくなります。 証明書の有効期間を短く設定し、そのローテーションを自動化します。 発行されたトークンの利用可能期間を制御できる認証プロバイダーを使用し、可能な限り短いライフタイムを使用します。 外部統合でサービス・アカウント・トークンを使用する場合、これらのトークンを頻繁にローテーションすることを計画します。 例えば、ブートストラップ・フェーズが完了したら、ノードのセットアップに使用したブートストラップ・トークンを失効させるか、その認証を解除する必要があります。

サードパーティの統合を有効にする前に確認

Kubernetesへの多くのサードパーティの統合は、クラスターのセキュリティプロファイルを変更する可能性があります。 統合を有効にする際には、アクセスを許可する前に、拡張機能が要求するパーミッションを常に確認してください。

例えば、多くのセキュリティ統合は、事実上そのコンポーネントをクラスター管理者にしているクラスター上のすべての秘密を見るためのアクセスを要求するかもしれません。 疑問がある場合は、可能な限り単一の名前空間で機能するように統合を制限してください。 Podを作成するコンポーネントも、kube-system名前空間のような名前空間内で行うことができれば、予想外に強力になる可能性があります。これは、サービスアカウントのシークレットにアクセスしたり、サービスアカウントに寛容なpod security policiesへのアクセスが許可されている場合に、昇格したパーミッションでPodが実行される可能性があるからです。

etcdにあるSecretを暗号化

一般的に、etcdデータベースにはKubernetes APIを介してアクセス可能なあらゆる情報が含まれており、クラスターの状態に対する大きな可視性を攻撃者へ与える可能性があります。 よく吟味されたバックアップおよび暗号化ソリューションを使用して、常にバックアップを暗号化し、可能な場合はフルディスク暗号化の使用を検討してください。

Kubernetesは1.7で導入された機能であるencryption at restをサポートしており、これは1.13からはベータ版となっています。 これは、etcdのSecretリソースを暗号化し、etcdのバックアップにアクセスした人が、それらのシークレットの内容を見ることを防ぎます。 この機能は現在ベータ版ですが、バックアップが暗号化されていない場合や、攻撃者がetcdへの読み取りアクセスを得た場合に、追加の防御レベルを提供します。

セキュリティアップデートのアラートの受信と脆弱性の報告

kubernetes-announceに参加してください。 グループに参加すると、セキュリティアナウンスに関するメールを受け取ることができます。 脆弱性の報告方法については、security reportingページを参照してください。

4.2.12 - クラスターでカスケード削除を使用する

このページでは、ガベージコレクション中にクラスターで使用するカスケード削除のタイプを指定する方法を示します。

始める前に

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

また、さまざまな種類のカスケード削除を試すために、サンプルのDeploymentを作成する必要があります。 タイプごとにDeploymentを再作成する必要があります。

Podのオーナーリファレンスを確認する

PodにownerReferencesフィールドが存在することを確認します:

kubectl get pods -l app=nginx --output=yaml

出力には、次のようにownerReferencesフィールドがあります。

apiVersion: v1
    ...
    ownerReferences:
    - apiVersion: apps/v1
      blockOwnerDeletion: true
      controller: true
      kind: ReplicaSet
      name: nginx-deployment-6b474476c4
      uid: 4fdcd81c-bd5d-41f7-97af-3a3b759af9a7
    ...

フォアグラウンドカスケード削除を使用する

デフォルトでは、Kubernetesはバックグラウンドカスケード削除を使用して、オブジェクトの依存関係を削除します。 クラスターが動作しているKubernetesのバージョンに応じて、kubectlまたはKubernetes APIのいずれかを使用して、フォアグラウンドカスケード削除に切り替えることができます。 バージョンを確認するには次のコマンドを実行してください: kubectl version.

kubectlまたはKubernetes APIを使用して、フォアグラウンドカスケード削除を使用してオブジェクトを削除することができます。

kubectlを使用する

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

kubectl delete deployment nginx-deployment --cascade=foreground

Kubernetes APIを使用する

  1. ローカルプロキシセッションを開始します:

    kubectl proxy --port=8080
    
  2. 削除のトリガーとしてcurlを使用します:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
        -H "Content-Type: application/json"
    

    出力には、次のようにforegroundDeletionファイナライザーが含まれています。

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "nginx-deployment",
        "namespace": "default",
        "uid": "d1ce1b02-cae8-4288-8a53-30e84d8fa505",
        "resourceVersion": "1363097",
        "creationTimestamp": "2021-07-08T20:24:37Z",
        "deletionTimestamp": "2021-07-08T20:27:39Z",
        "finalizers": [
          "foregroundDeletion"
        ]
        ...
    

バッググラウンドカスケード削除を使用する

  1. サンプルのDeploymentを作成する
  2. クラスターが動作しているKubernetesのバージョンに応じて、kubectlまたはKubernetes APIのいずれかを使用してDeploymentを削除します。 バージョンを確認するには次のコマンドを実行してください: kubectl version.

kubectlまたはKubernetes APIを使用して、バックグラウンドカスケード削除を使用してオブジェクトを削除できます。

Kubernetesはデフォルトでバックグラウンドカスケード削除を使用し、--cascadeフラグまたはpropagationPolicy引数なしで以下のコマンドを実行した場合も同様です。

kubectlを使用する

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

kubectl delete deployment nginx-deployment --cascade=background

Kubernetes APIを使用する

  1. ローカルプロキシセッションを開始します:

    kubectl proxy --port=8080
    
  2. 削除のトリガーとしてcurlを使用します:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
        -H "Content-Type: application/json"
    

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

    "kind": "Status",
    "apiVersion": "v1",
    ...
    "status": "Success",
    "details": {
        "name": "nginx-deployment",
        "group": "apps",
        "kind": "deployments",
        "uid": "cc9eefb9-2d49-4445-b1c1-d261c9396456"
    }
    

オーナーオブジェクトの削除と従属オブジェクトの孤立

デフォルトでは、Kubernetesにオブジェクトの削除を指示すると、コントローラーは従属オブジェクトも削除します。クラスターが動作しているKubernetesのバージョンに応じて、kubectlまたはKubernetes APIを使用して、これらの従属オブジェクトをKubernetesでorphanにすることができます。 バージョンを確認するには次のコマンドを実行してください: kubectl version.

kubectlを使用する

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

kubectl delete deployment nginx-deployment --cascade=orphan

Kubernetes APIを使用する

  1. ローカルプロキシセッションを開始します:

    kubectl proxy --port=8080
    
  2. 削除のトリガーとしてcurlを使用します:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
        -H "Content-Type: application/json"
    

    出力には、次のようにfinalizersフィールドにorphanが含まれます。

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "namespace": "default",
    "uid": "6f577034-42a0-479d-be21-78018c466f1f",
    "creationTimestamp": "2021-07-09T16:46:37Z",
    "deletionTimestamp": "2021-07-09T16:47:08Z",
    "deletionGracePeriodSeconds": 0,
    "finalizers": [
      "orphan"
    ],
    ...
    

Deploymentによって管理されているPodがまだ実行中であることを確認できます。

kubectl get pods -l app=nginx

次の項目

4.2.13 - サービスディスカバリーにCoreDNSを使用する

このページでは、CoreDNSのアップグレードプロセスと、kube-dnsの代わりにCoreDNSをインストールする方法を説明します。

始める前に

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

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

CoreDNSについて

CoreDNSは、KubernetesクラスターDNSとして稼働させることができる柔軟で拡張可能なDNSサーバーです。Kubernetesと同様に、CoreDNSプロジェクトはCNCFによってホストされています。

既存のデプロイでkube-dnsを置き換えるか、クラスターのデプロイとアップグレードを代行してくれるkubeadmのようなツールを使用することで、クラスターでkube-dnsの代わりにCoreDNSを使用することができます。

CoreDNSのインストール

kube-dnsの手動デプロイや置き換えについては、CoreDNS websiteのドキュメントを参照してください。

CoreDNSへの移行

kubeadmを使用した既存のクラスターのアップグレード

Kubernetesバージョン1.21でkubeadmはDNSアプリケーションとしてのkube-dnsに対するサポートを削除しました。 kubeadm v1.30に対してサポートされるクラスターDNSアプリケーションはCoreDNSのみです。

kube-dnsを使用しているクラスターをkubeadmを使用してアップグレードするときに、CoreDNSに移行することができます。 この場合、kubeadmは、kube-dns ConfigMapをベースにしてCoreDNS設定("Corefile")を生成し、スタブドメインおよび上流のネームサーバーの設定を保持します。

CoreDNSのアップグレード

KubernetesのバージョンごとにkubeadmがインストールするCoreDNSのバージョンは、KubernetesにおけるCoreDNSのバージョンのページで確認することができます。

CoreDNSのみをアップグレードしたい場合や、独自のカスタムイメージを使用したい場合は、CoreDNSを手動でアップグレードすることができます。 スムーズなアップグレードのために役立つガイドラインとウォークスルーが用意されています。 クラスターをアップグレードする際には、既存のCoreDNS設定("Corefile")が保持されていることを確認してください。

kubeadmツールを使用してクラスターをアップグレードしている場合、kubeadmは既存のCoreDNSの設定を自動的に保持する処理を行うことができます。

CoreDNSのチューニング

リソース使用率が問題になる場合は、CoreDNSの設定を調整すると役立つ場合があります。 詳細は、CoreDNSのスケーリングに関するドキュメントを参照してください。

次の項目

CoreDNSは、設定("Corefile")を変更することで、kube-dnsよりも多くのユースケースをサポートする構成にすることができます。 詳細はKubernetes CoreDNSプラグインのドキュメントを参照するか、CoreDNSブログを参照してください。

4.2.14 - KubernetesクラスターでNodeLocal DNSキャッシュを使用する

FEATURE STATE: Kubernetes v1.18 [stable]
このページでは、KubernetesのNodeLocal DNSキャッシュの機能の概要について説明します。

始める前に

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

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

イントロダクション

NodeLocal DNSキャッシュは、クラスターノード上でDNSキャッシュエージェントをDaemonSetで稼働させることで、クラスターのDNSパフォーマンスを向上させます。現在のアーキテクチャにおいて、ClusterFirstのDNSモードでのPodは、DNSクエリー用にkube-dnsのService IPに疎通します。これにより、kube-proxyによって追加されたiptablesを介してkube-dns/CoreDNSのエンドポイントへ変換されます。この新しいアーキテクチャによって、Podは同じノード上で稼働するDNSキャッシュエージェントに対して疎通し、それによってiptablesのDNATルールとコネクショントラッキングを回避します。ローカルのキャッシュエージェントはクラスターのホスト名(デフォルトではcluster.localというサフィックス)に対するキャッシュミスがあるときはkube-dnsサービスへ問い合わせます。

動機

  • 現在のDNSアーキテクチャでは、ローカルのkube-dns/CoreDNSがないとき、DNSへの秒間クエリー数が最も高いPodは他のノードへ疎通する可能性があります。ローカルでキャッシュを持つことにより、この状況におけるレイテンシーの改善に役立ちます。

  • iptables DNATとコネクショントラッキングをスキップすることはconntrackの競合を減らし、UDPでのDNSエントリーがconntrackテーブルを満杯にすることを避けるのに役立ちます。

  • ローカルのキャッシュエージェントからkube-dnsサービスへの接続がTCPにアップグレードされます。タイムアウトをしなくてはならないUDPエントリーと比べ、TCPのconntrackエントリーはコネクションクローズ時に削除されます(デフォルトの nf_conntrack_udp_timeout は30秒です)。

  • DNSクエリーをUDPからTCPにアップグレードすることで、UDPパケットの欠損や、通常30秒(10秒のタイムアウトで3回再試行する)であるDNSのタイムアウトによるテイルレイテンシーを減少させます。NodeLocalキャッシュはUDPのDNSクエリーを待ち受けるため、アプリケーションを変更する必要はありません。

  • DNSクエリーに対するノードレベルのメトリクスと可視性を得られます。

  • DNSの不在応答のキャッシュも再度有効にされ、それによりkube-dnsサービスに対するクエリー数を減らします。

アーキテクチャ図

この図はNodeLocal DNSキャッシュが有効にされた後にDNSクエリーがあったときの流れとなります。

NodeLocal DNSCache flow

Nodelocal DNSCacheのフロー

この図は、NodeLocal DNSキャッシュがDNSクエリーをどう扱うかを表したものです。

設定

この機能は、下記の手順により有効化できます。

  • nodelocaldns.yamlと同様のマニフェストを用意し、nodelocaldns.yamlという名前で保存してください。

  • マニフェスト内の変数を正しい値に置き換えてください。

    • kubedns=kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}

    • domain=<cluster-domain>

    • localdns=<node-local-address>

    <cluster-domain>はデフォルトで"cluster.local"です。<node-local-address> はNodeLocal DNSキャッシュ用に確保されたローカルの待ち受けIPアドレスです。

    • kube-proxyがIPTABLESモードで稼働中のとき:

      sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
      

      __PILLAR__CLUSTER__DNS____PILLAR__UPSTREAM__SERVERS__はnode-local-dnsというPodによって生成されます。 このモードでは、node-local-dns Podは<node-local-address>とkube-dnsのサービスIPの両方で待ち受けるため、PodはIPアドレスでもDNSレコードのルップアップができます。

    • kube-proxyがIPVSモードで稼働中のとき:

       sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
      

      このモードでは、node-local-dns Podは<node-local-address>上のみで待ち受けます。node-local-dnsのインターフェースはkube-dnsのクラスターIPをバインドしません。なぜならばIPVSロードバランシング用に使われているインターフェースは既にこのアドレスを使用しているためです。 __PILLAR__UPSTREAM__SERVERS__ はnode-local-dns Podにより生成されます。

  • kubectl create -f nodelocaldns.yamlを実行してください。

  • kube-proxyをIPVSモードで使用しているとき、NodeLocal DNSキャッシュが待ち受けている<node-local-address>を使用するため、kubeletに対する--cluster-dnsフラグを修正する必要があります。IPVSモード以外のとき、--cluster-dnsフラグの値を修正する必要はありません。なぜならNodeLocal DNSキャッシュはkube-dnsのサービスIPと<node-local-address>の両方で待ち受けているためです。

一度有効にすると、クラスターの各Node上で、kube-systemという名前空間でnode-local-dns Podが、稼働します。このPodはCoreDNSをキャッシュモードで稼働させるため、異なるプラグインによって公開された全てのCoreDNSのメトリクスがNode単位で利用可能となります。

kubectl delete -f <manifest>を実行してDaemonSetを削除することによって、この機能を無効にできます。また、kubeletの設定に対して行った全ての変更をリバートすべきです。

4.2.15 - EndpointSliceの有効化

このページはKubernetesのEndpointSliceの有効化の概要を説明します。

始める前に

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

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

概要

EndpointSliceは、KubernetesのEndpointsに対してスケーラブルで拡張可能な代替手段を提供します。Endpointsが提供する機能のベースの上に構築し、スケーラブルな方法で拡張します。Serviceが多数(100以上)のネットワークエンドポイントを持つ場合、それらは単一の大きなEndpointsリソースではなく、複数の小さなEndpointSliceに分割されます。

EndpointSliceの有効化

FEATURE STATE: Kubernetes v1.17 [beta]

EndpoitSliceはベータ版の機能です。APIとEndpointSliceコントローラーはデフォルトで有効です。kube-proxyはデフォルトでEndpointSliceではなくEndpointsを使用します。

スケーラビリティと性能向上のため、kube-proxy上でEndpointSliceProxyingフィーチャーゲートを有効にできます。この変更はデータソースをEndpointSliceに移します、これはkube-proxyとKubernetes API間のトラフィックの量を削減します。

EndpointSliceの使用

クラスター内でEndpointSliceを完全に有効にすると、各Endpointsリソースに対応するEndpointSliceリソースが表示されます。既存のEndpointsの機能をサポートすることに加えて、EndpointSliceはトポロジーなどの新しい情報を含みます。これらにより、クラスター内のネットワークエンドポイントのスケーラビリティと拡張性が大きく向上します。

次の項目

4.3 - Podとコンテナの設定

Podとコンテナの一般的な設定のタスクを行います。

4.3.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

次の項目

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

クラスター管理者向け

4.3.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

次の項目

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

クラスター管理者向け

4.3.3 - 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

次の項目

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

クラスター管理者向け

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

FEATURE STATE: Kubernetes v1.30 [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

次の項目

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

クラスター管理者向け

4.3.5 - ストレージにボリュームを使用する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など、さまざまなネットワーク接続ストレージソリューションをサポートします。これらは、重要なデータに好ましく、ノード上のデバイスのマウントやアンマウントなどの詳細を処理します。詳細はボリュームを参照してください。

4.3.6 - ストレージに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

次の項目

4.3.7 - 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
  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になります。

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

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

shellから抜けましょう。

exit

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

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

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

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

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

FEATURE STATE: Kubernetes v1.25 [alpha]

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

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

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

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

議論

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

次の項目

4.3.8 - 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/livenessイメージを使用したコンテナを起動するPodを作成します。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/liveness
    args:
    - /server
    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。
  • 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リファレンスも参考にしてください:

4.3.9 - 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を作成します。

次の項目

4.3.10 - 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についてさらに学ぶ。

4.3.11 - 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>
  ...

次の項目

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

このページでは、コンテナのライフサイクルイベントにハンドラーを紐付けする方法を説明します。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のライフサイクルを参照してください。

次の項目

参照

4.3.13 - 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に使用できません。

次の項目

4.3.14 - 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 attach -it nginx -c shell
    

    コマンドプロンプトが表示されない場合は、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内の他のコンテナに表示されます。 これによりデバッグが容易になりますが、ファイルシステム内の秘密情報はファイルシステムのアクセス許可によってのみ保護されることも意味します。

4.3.15 - 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

4.4 - 監視、ログ、デバッグ

クラスターのトラブルシューティングや、コンテナ化したアプリケーションのデバッグのために、監視とログをセットアップします。

時には物事がうまくいかないこともあります。このガイドは、それらを正すことを目的としています。

2つのセクションから構成されています:

また、使用しているリリースの既知の問題を確認する必要があります。

ヘルプを受けます

もしあなたの問題が上記のどのガイドでも解決されない場合は、Kubernetesコミュニティから助けを得るための様々な方法があります。

ご質問

本サイトのドキュメントは、様々な疑問に対する答えを提供するために構成されています。

コンセプトでは、Kubernetesのアーキテクチャと各コンポーネントの動作について説明し、セットアップでは、使い始めるための実用的な手順を提供しています。 タスク は、よく使われるタスクの実行方法を示し、 チュートリアルは、実世界の業界特有、またはエンドツーエンドの開発シナリオ、より包括的なウォークスルーとなります。 リファレンスセクションでは、Kubernetes APIkubectlなどのコマンドラインインターフェース(CLI)に関する詳しいドキュメントが提供されています。

ヘルプ!私の質問はカバーされていません!今すぐ助けてください!

Stack Overflow

コミュニティの誰かがすでに同じような質問をしている可能性があり、あなたの問題を解決できるかもしれません。 KubernetesチームもKubernetesタグが付けられた投稿を監視しています。 もし役立つ既存の質問がない場合は、新しく質問する前に、あなたの質問がStack Overflowのトピックに沿ったものであることを確認し新しく質問する方法のガイダンスに目を通してください!

Slack

Kubernetesコミュニティの多くの人々は、Kubernetes Slackの#kubernetes-usersチャンネルに集まっています。 Slackは登録が必要です。招待をリクエストすることができ、登録は誰でも可能です。 お気軽にお越しいただき、何でも質問してください。 登録が完了したら、WebブラウザまたはSlackの専用アプリからKubernetes organization in Slackにアクセスします。

登録が完了したら、増え続けるチャンネルリストを見て、興味のある様々なテーマについて調べてみましょう。 たとえば、Kubernetesの初心者は、#kubernetes-noviceに参加してみるのもよいでしょう。 別の例として、開発者は#kubernetes-contributorsチャンネルに参加するとよいでしょう。

また、多くの国別/言語別チャンネルがあります。これらのチャンネルに参加すれば、地域特有のサポートや情報を得ることができます。

Country / language specific Slack channels
CountryChannels
中国#cn-users, #cn-events
フィンランド#fi-users
フランス#fr-users, #fr-events
ドイツ#de-users, #de-events
インド#in-users, #in-events
イタリア#it-users, #it-events
日本#jp-users, #jp-events
韓国#kr-users
オランダ#nl-users
ノルウェー#norw-users
ポーランド#pl-users
ロシア#ru-users
スペイン#es-users
スウェーデン#se-users
トルコ#tr-users, #tr-events

フォーラム

Kubernetesの公式フォーラムへの参加は大歓迎ですdiscuss.kubernetes.io

バグと機能の要望

バグらしきものを発見した場合、または機能要望を出したい場合、GitHub課題追跡システムをご利用ください。 課題を提出する前に、既存の課題を検索して、あなたの課題が解決されているかどうかを確認してください。

バグを報告する場合は、そのバグを再現するための詳細な情報を含めてください。

  • Kubernetes のバージョン: kubectl version
  • クラウドプロバイダー、OSディストリビューション、ネットワーク構成、コンテナランタイムバージョン
  • 問題を再現するための手順

4.4.1 - アプリケーションのトラブルシューティング

コンテナ化されたアプリケーションの一般的な問題をデバッグします。

このドキュメントには、コンテナ化されたアプリケーションの問題を解決するための、一連のリソースが記載されています。Kubernetesリソース(Pod、Service、StatefulSetなど)に関する一般的な問題や、コンテナ終了メッセージを理解するためのアドバイス、実行中のコンテナをデバッグする方法などが網羅されています。

4.4.1.1 - Podのデバッグ

このガイドは、Kubernetesにデプロイされ、正しく動作しないアプリケーションをユーザーがデバッグするためのものです。 これは、自分のクラスターをデバッグしたい人のためのガイドでは ありません。 そのためには、debug-clusterを確認する必要があります。

問題の診断

トラブルシューティングの最初のステップは切り分けです。何が問題なのでしょうか? Podなのか、レプリケーションコントローラーなのか、それともサービスなのか?

Podのデバッグ

デバッグの第一歩は、Podを見てみることです。 以下のコマンドで、Podの現在の状態や最近のイベントを確認します。

kubectl describe pods ${POD_NAME}

Pod内のコンテナの状態を見てください。 すべてRunningですか? 最近、再起動がありましたか? Podの状態に応じてデバッグを続けます。

PodがPendingのまま

PodがPendingで止まっている場合、それはノードにスケジュールできないことを意味します。 一般に、これはある種のリソースが不十分で、スケジューリングできないことが原因です。 上のkubectl describe ...コマンドの出力を見てください。

なぜあなたのPodをスケジュールできないのか、スケジューラーからのメッセージがあるはずです。 理由は以下の通りです。

  • リソースが不足しています。 クラスターのCPUまたはメモリーを使い果たしている可能性があります。Podを削除するか、リソースの要求値を調整するか、クラスターに新しいノードを追加する必要があります。詳しくはCompute Resources documentを参照してください。

  • あなたが使用しているのはhostPortです。 PodをhostPortにバインドすると、そのPodがスケジュールできる場所が限定されます。ほとんどの場合、hostPortは不要なので、Serviceオブジェクトを使ってPodを公開するようにしてください。もしhostPort が必要な場合は、Kubernetesクラスターのノード数だけPodをスケジュールすることができます。

Podがwaitingのまま

PodがWaiting状態で止まっている場合、ワーカーノードにスケジュールされていますが、そのノード上で実行することができません。この場合も、kubectl describe ...の情報が参考になるはずです。Waiting状態のPodの最も一般的な原因は、コンテナイメージのプルに失敗することです。

確認すべきことは3つあります。

  • イメージの名前が正しいかどうか確認してください。
  • イメージをレジストリにプッシュしましたか?
  • あなたのマシンで手動でdocker pull <image>を実行し、イメージをプルできるかどうか確認してください。

Podがクラッシュするなどの不健全な状態

Podがスケジュールされると、実行中のPodのデバッグで説明されている方法がデバッグに利用できるようになります。

Podが期待する通りに動きません

Podが期待した動作をしない場合、ポッドの記述(ローカルマシンの mypod.yaml ファイルなど)に誤りがあり、Pod作成時にその誤りが黙って無視された可能性があります。Pod記述のセクションのネストが正しくないか、キー名が間違って入力されていることがよくあり、そのようなとき、そのキーは無視されます。たとえば、commandのスペルをcommndと間違えた場合、Podは作成されますが、あなたが意図したコマンドラインは使用されません。

まずPodを削除して、--validate オプションを付けて再度作成してみてください。 例えば、kubectl apply --validate -f mypod.yamlと実行します。 commandのスペルをcommndに間違えると、以下のようなエラーになります。

I0805 10:43:25.129850   46757 schema.go:126] unknown field: commnd
I0805 10:43:25.129973   46757 schema.go:129] this may be a false alarm, see https://github.com/kubernetes/kubernetes/issues/6842
pods/mypod

次に確認することは、apiserver上のPodが、作成しようとしたPod(例えば、ローカルマシンのyamlファイル)と一致しているかどうかです。 例えば、kubectl get pods/mypod -o yaml > mypod-on-apiserver.yaml を実行して、元のポッドの説明であるmypod.yamlとapiserverから戻ってきたmypod-on-apiserver.yamlを手動で比較してみてください。 通常、"apiserver" バージョンには、元のバージョンにはない行がいくつかあります。これは予想されることです。 しかし、もし元のバージョンにある行がapiserverバージョンにない場合、これはあなたのPod specに問題があることを示している可能性があります。

レプリケーションコントローラーのデバッグ

レプリケーションコントローラーはかなり単純なものです。 彼らはPodを作ることができるか、できないか、どちらかです。 もしPodを作成できないのであれば、上記の説明を参照して、Podをデバッグしてください。 また、kubectl describe rc ${CONTROLLER_NAME}を使用すると、レプリケーションコントローラーに関連するイベントを確認することができます。

Serviceのデバッグ

Serviceは、Podの集合全体でロードバランシングを提供します。 Serviceが正しく動作しない原因には、いくつかの一般的な問題があります。

以下の手順は、Serviceの問題をデバッグするのに役立つはずです。

まず、Serviceに対応するEndpointが存在することを確認します。 全てのServiceオブジェクトに対して、apiserverは endpoints リソースを利用できるようにします。 このリソースは次のようにして見ることができます。

kubectl get endpoints ${SERVICE_NAME}

EndpointがServiceのメンバーとして想定されるPod数と一致していることを確認してください。 例えば、3つのレプリカを持つnginxコンテナ用のServiceであれば、ServiceのEndpointには3つの異なるIPアドレスが表示されるはずです。

Serviceに対応するEndpointがありません

Endpointが見つからない場合は、Serviceが使用しているラベルを使用してPodをリストアップしてみてください。 ラベルがあるところにServiceがあると想像してください。

...
spec:
  - selector:
     name: nginx
     type: frontend

セレクタに一致するPodを一覧表示するには、次のコマンドを使用します。

kubectl get pods --selector=name=nginx,type=frontend

リストがServiceを提供する予定のPodと一致することを確認します。 PodのcontainerPortがServiceのtargetPortと一致することを確認します。

ネットワークトラフィックが転送されません

詳しくはServiceのデバッグを参照してください。

次の項目

上記のいずれの方法でも問題が解決しない場合は、以下の手順に従ってください。 Serviceのデバッグに関するドキュメントで、Serviceが実行されていること、Endpointsがあること、Podsが実際にサービスを提供していること、DNSが機能していること、IPtablesルールがインストールされていること、kube-proxyが誤作動を起こしていないようなことを確認してください。

トラブルシューティングドキュメントに詳細が記載されています。

4.4.1.2 - Serviceのデバッグ

新規にKubernetesをインストールした環境でかなり頻繁に発生する問題は、Serviceが適切に機能しないというものです。Deployment(または他のワークロードコントローラー)を通じてPodを実行し、サービスを作成したにもかかわらず、アクセスしようとしても応答がありません。何が問題になっているのかを理解するのに、このドキュメントがきっと役立つでしょう。

Pod内でコマンドを実行する

ここでの多くのステップでは、クラスターで実行されているPodが見ているものを確認する必要があります。これを行う最も簡単な方法は、インタラクティブなalpineのPodを実行することです。

kubectl run -it --rm --restart=Never alpine --image=alpine sh

使用したい実行中のPodがすでにある場合は、以下のようにしてそのPod内でコマンドを実行できます。

kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>

セットアップ

このドキュメントのウォークスルーのために、いくつかのPodを実行しましょう。おそらくあなた自身のServiceをデバッグしているため、あなた自身の詳細に置き換えることもできますし、これに沿って2番目のデータポイントを取得することもできます。

kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created

kubectlコマンドは作成、変更されたリソースのタイプと名前を出力するため、この後のコマンドで使用することもできます。

Deploymentを3つのレプリカにスケールさせてみましょう。

kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled

これは、次のYAMLでDeploymentを開始した場合と同じです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostnames
  labels:
    app: hostnames
spec:
  selector:
    matchLabels:
      app: hostnames
  replicas: 3
  template:
    metadata:
      labels:
        app: hostnames
    spec:
      containers:
      - name: hostnames
        image: registry.k8s.io/serve_hostname

"app"ラベルはkubectl create deploymentによって、Deploymentの名前に自動的にセットされます。

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

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          2m
hostnames-632524106-ly40y   1/1       Running   0          2m
hostnames-632524106-tlaok   1/1       Running   0          2m

Podが機能していることも確認できます。Pod IP アドレスリストを取得し、直接テストできます。

kubectl get pods -l app=hostnames \
    -o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7

このウォークスルーに使用されるサンプルコンテナは、ポート9376でHTTPを介して独自のホスト名を提供するだけですが、独自のアプリをデバッグする場合は、Podがリッスンしているポート番号を使用する必要があります。

Pod内から実行します。

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

次のように表示されます。

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

この時点で期待通りの応答が得られない場合、Podが正常でないか、想定しているポートでリッスンしていない可能性があります。なにが起きているかを確認するためにkubectl logsが役立ちます。Podに直接に入りデバッグする場合はkubectl execが必要になります。

これまでにすべての計画が完了していると想定すると、Serviceが機能しない理由を調査することができます。

Serviceは存在するか?

賢明な読者は、Serviceをまだ実際に作成していないことにお気付きかと思いますが、これは意図的です。これは時々忘れられるステップであり、最初に確認すべきことです。

存在しないServiceにアクセスしようとするとどうなるでしょうか?このServiceを名前で利用する別のPodがあると仮定すると、次のような結果が得られます。

wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'

最初に確認するのは、そのServiceが実際に存在するかどうかです。

kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found

Serviceを作成しましょう。前と同様に、これはウォークスルー用です。ご自身のServiceの詳細を使用することもできます。

kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed

そして、念のため内容を確認します。

kubectl get svc hostnames
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
hostnames   ClusterIP   10.0.1.175   <none>        80/TCP    5s

これで、Serviceが存在することがわかりました。

前と同様に、これは次のようなYAMLでServiceを開始した場合と同じです。

apiVersion: v1
kind: Service
metadata:
  name: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - name: default
    protocol: TCP
    port: 80
    targetPort: 9376

構成の全範囲をハイライトするため、ここで作成したServiceはPodとは異なるポート番号を使用します。多くの実際のServiceでは、これらのポートは同じになる場合があります。

サービスはDNS名によって機能しているか?

クライアントがサービスを使用する最も一般的な方法の1つは、DNS名を使用することです。同じNamespaceのPodから次のコマンドを実行してください。

nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

これが失敗した場合、おそらくPodとServiceが異なるNamespaceにあるため、ネームスペースで修飾された名前を試してください。(Podの中からもう一度)

nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

これが機能する場合、クロスネームスペース名を使用するようにアプリケーションを調整するか、同じNamespaceでアプリとServiceを実行する必要があります。これでも失敗する場合は、完全修飾名を試してください。

nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

ここでのサフィックス"default.svc.cluster.local"に注意してください。"default"は、操作しているNamespaceです。"svc"は、これがServiceであることを示します。"cluster.local"はクラスタードメインであり、あなたのクラスターでは異なる場合があります。

クラスター内のノードからも試すこともできます。

nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server:         10.0.0.10
Address:        10.0.0.10#53

Name:   hostnames.default.svc.cluster.local
Address: 10.0.1.175

完全修飾名では検索できるのに、相対名ではできない場合、Podの/etc/resolv.confファイルが正しいことを確認する必要があります。Pod内から実行します。

cat /etc/resolv.conf

次のように表示されます。

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5

nameserver行はクラスターのDNS Serviceを示さなければなりません。これは、--cluster-dnsフラグでkubeletに渡されます。

search行には、Service名を見つけるための適切なサフィックスを含める必要があります。この場合、ローカルのNamespaceServiceを見つけるためのサフィックス(default.svc.cluster.local)、すべてのNamespacesServiceを見つけるためのサフィックス(svc.cluster.local)、およびクラスターのサフィックス(cluster.local)です。インストール方法によっては、その後に追加のレコードがある場合があります(合計6つまで)。クラスターのサフィックスは、--cluster-domainフラグを使用してkubeletに渡されます。このドキュメントではそれが"cluster.local"であると仮定していますが、あなたのクラスターでは異なる場合があります。その場合は、上記のすべてのコマンドでクラスターのサフィックスを変更する必要があります。

options行では、DNSクライアントライブラリーが検索パスをまったく考慮しないようにndotsを十分に高く設定する必要があります。Kubernetesはデフォルトでこれを5に設定します。これは、生成されるすべてのDNS名をカバーするのに十分な大きさです。

DNS名で機能するServiceはあるか?

上記がまだ失敗する場合、DNSルックアップがServiceに対して機能していません。一歩離れて、他の何が機能していないかを確認しましょう。KubernetesマスターのServiceは常に機能するはずです。Pod内から実行します。

nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local

これが失敗する場合は、このドキュメントのkube-proxyセクションを参照するか、このドキュメントの先頭に戻って最初からやり直してください。ただし、あなた自身のServiceをデバッグするのではなく、DNSサービスをデバッグします。

ServiceはIPでは機能するか?

DNSサービスが正しく動作できると仮定すると、次にテストするのはIPによってServiceが動作しているかどうかです。上述のkubectl getで確認できるIPに、クラスター内のPodからアクセスします。

for i in $(seq 1 3); do
    wget -qO- 10.0.1.175:80
done

次のように表示されます。

hostnames-0uton
hostnames-bvc05
hostnames-yp2kp

Serviceが機能している場合は、正しい応答が得られるはずです。そうでない場合、おかしい可能性のあるものがいくつかあるため、続けましょう。

Serviceは正しく定義されているか?

馬鹿げているように聞こえるかもしれませんが、Serviceが正しく定義されPodのポートとマッチすることを二度、三度と確認すべきです。Serviceを読み返して確認しましょう。

kubectl get service hostnames -o json
{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "hostnames",
        "namespace": "default",
        "uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
        "resourceVersion": "347189",
        "creationTimestamp": "2015-07-07T15:24:29Z",
        "labels": {
            "app": "hostnames"
        }
    },
    "spec": {
        "ports": [
            {
                "name": "default",
                "protocol": "TCP",
                "port": 80,
                "targetPort": 9376,
                "nodePort": 0
            }
        ],
        "selector": {
            "app": "hostnames"
        },
        "clusterIP": "10.0.1.175",
        "type": "ClusterIP",
        "sessionAffinity": "None"
    },
    "status": {
        "loadBalancer": {}
    }
}
  • アクセスしようとしているServiceポートはspec.ports[]のリストのなかに定義されていますか?
  • targetPortはPodに対して適切ですか(いくつかのPodはServiceとは異なるポートを使用します)?
  • targetPortを数値で定義しようとしている場合、それは数値(9376)、文字列"9376"のどちらですか?
  • targetPortを名前で定義しようとしている場合、Podは同じ名前でポートを公開していますか?
  • ポートのprotocolはPodに適切ですか?

ServiceにEndpointsがあるか?

ここまで来たということは、Serviceは正しく定義され、DNSによって名前解決できることが確認できているでしょう。ここでは、実行したPodがServiceによって実際に選択されていることを確認しましょう。

以前に、Podが実行されていることを確認しました。再確認しましょう。

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          1h
hostnames-632524106-ly40y   1/1       Running   0          1h
hostnames-632524106-tlaok   1/1       Running   0          1h

-l app=hostnames引数はラベルセレクターで、ちょうど私たちのServiceに定義されているものと同じです。

"AGE"列は、これらのPodが約1時間前のものであることを示しており、それらが正常に実行され、クラッシュしていないことを意味します。

"RESTARTS"列は、これらのポッドが頻繁にクラッシュしたり、再起動されていないことを示しています。頻繁に再起動すると、断続的な接続性の問題が発生する可能性があります。再起動回数が多い場合は、ポッドをデバッグするを参照してください。

Kubernetesシステム内には、すべてのServiceのセレクターを評価し、結果をEndpointsオブジェクトに保存するコントロールループがあります。

kubectl get endpoints hostnames

NAME        ENDPOINTS
hostnames   10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376

これにより、EndpointsコントローラーがServiceの正しいPodを見つけていることを確認できます。ENDPOINTS列が<none>の場合、Serviceのspec.selectorフィールドが実際にPodのmetadata.labels値を選択していることを確認する必要があります。よくある間違いは、タイプミスやその他のエラー、たとえばDeployment作成にもkubectl runが使われた1.18以前のバージョンのように、Serviceがapp=hostnamesを選択しているのにDeploymentがrun=hostnamesを指定していることです。

Podは機能しているか?

この時点で、Serviceが存在し、Podを選択していることがわかります。このウォークスルーの最初に、Pod自体を確認しました。Podが実際に機能していることを確認しましょう。Serviceメカニズムをバイパスして、上記EndpointsにリストされているPodに直接アクセスすることができます。

Pod内から実行します。

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

次のように表示されます。

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

Endpointsリスト内の各Podは、それぞれの自身のホスト名を返すはずです。そうならない(または、あなた自身のPodの正しい振る舞いにならない)場合は、そこで何が起こっているのかを調査する必要があります。

kube-proxyは機能しているか?

ここに到達したのなら、Serviceは実行され、Endpointsがあり、Podが実際にサービスを提供しています。この時点で、Serviceのプロキシメカニズム全体が疑わしいです。ひとつひとつ確認しましょう。

Serviceのデフォルト実装、およびほとんどのクラスターで使用されるものは、kube-proxyです。kube-proxyはそれぞれのノードで実行され、Serviceの抽象化を提供するための小さなメカニズムセットの1つを構成するプログラムです。クラスターがkube-proxyを使用しない場合、以下のセクションは適用されず、使用しているServiceの実装を調査する必要があります。

kube-proxyは実行されているか?

kube-proxyがノード上で実行されていることを確認しましょう。ノードで実行されていれば、以下のような結果が得られるはずです。

ps auxw | grep kube-proxy
 root  4194  0.4  0.1 101864 17696 ?    Sl Jul04  25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2

次に、マスターとの接続など、明らかな失敗をしていないことを確認します。これを行うには、ログを確認する必要があります。ログへのアクセス方法は、ノードのOSに依存します。一部のOSでは/var/log/kube-proxy.logのようなファイルですが、他のOSではjournalctlを使用してログにアクセスします。次のように表示されます。

I1027 22:14:53.995134    5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163    5063 server.go:247] Using iptables Proxier.
I1027 22:14:53.999055    5063 server.go:255] Tearing down userspace rules. Errors here are acceptable.
I1027 22:14:54.038140    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209    5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238    5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048    5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP

マスターに接続できないことに関するエラーメッセージが表示された場合、ノードの設定とインストール手順をダブルチェックする必要があります。

kube-proxyが正しく実行できない理由の可能性の1つは、必須のconntrackバイナリが見つからないことです。これは、例えばKubernetesをスクラッチからインストールするなど、クラスターのインストール方法に依存して、一部のLinuxシステムで発生する場合があります。これが該当する場合は、conntrackパッケージを手動でインストール(例: Ubuntuではsudo apt install conntrack)する必要があり、その後に再試行する必要があります。

kube-proxyは、いくつかのモードのいずれかで実行できます。上記のログのUsing iptables Proxierという行は、kube-proxyが「iptables」モードで実行されていることを示しています。最も一般的な他のモードは「ipvs」です。古い「ユーザースペース」モードは、主にこれらに置き換えられました。

Iptables mode

「iptables」モードでは、ノードに次のようなものが表示されます。

iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR

各サービスのポートごとに、KUBE-SERVICESに1つのルールと1つのKUBE-SVC- <hash>チェーンが必要です。Podエンドポイントごとに、そのKUBE-SVC- <hash>に少数のルールがあり、少数のルールが含まれる1つのKUBE-SEP- <hash>チェーンがあるはずです。正確なルールは、正確な構成(NodePortとLoadBalancerを含む)に基づいて異なります。

IPVS mode

「ipvs」モードでは、ノードに次のようなものが表示されます。

ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
...
TCP  10.0.1.175:80 rr
  -> 10.244.0.5:9376               Masq    1      0          0
  -> 10.244.0.6:9376               Masq    1      0          0
  -> 10.244.0.7:9376               Masq    1      0          0
...

各Serviceの各ポートに加えて、NodePort、External IP、およびLoad Balancer IPに対して、kube-proxyは仮想サーバーを作成します。Pod endpointごとに、対応する実サーバーが作成されます。この例では、サービスhostnames(10.0.1.175:80)は3つのendpoints(10.244.0.5:937610.244.0.6:937610.244.0.7:9376)を持っています。

IPVSプロキシは、各Serviceアドレス(Cluster IP、External IP、NodePort IP、Load Balancer IPなど)毎の仮想サーバーと、Serviceのエンドポイントが存在する場合に対応する実サーバーを作成します。この例では、hostnames Service(10.0.1.175:80)は3つのエンドポイント(10.244.0.5:937610.244.0.6:937610.244.0.7:9376)を持ち、上と似た結果が得られるはずです。

Userspace mode

まれに、「userspace」モードを使用している場合があります。

ノードから実行します。

iptables-save | grep hostnames
-A KUBE-PORTALS-CONTAINER -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j REDIRECT --to-ports 48577
-A KUBE-PORTALS-HOST -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j DNAT --to-destination 10.240.115.247:48577

サービスの各ポートには2つのルールが必要です(この例では1つだけ)-「KUBE-PORTALS-CONTAINER」と「KUBE-PORTALS-HOST」です。

「userspace」モードを使用する必要はほとんどないので、ここでこれ以上時間を費やすことはありません。

kube-proxyはプロキシしているか?

上記のいずれかが発生したと想定して、いずれかのノードからIPでサービスにアクセスをしています。

curl 10.0.1.175:80
hostnames-632524106-bbpiw

もしこれが失敗し、あなたがuserspaceプロキシを使用している場合、プロキシへの直接アクセスを試してみてください。もしiptablesプロキシを使用している場合、このセクションはスキップしてください。

上記のiptables-saveの出力を振り返り、kube-proxyがServiceに使用しているポート番号を抽出します。上記の例では"48577"です。このポートに接続してください。

curl localhost:48577
hostnames-632524106-tlaok

もしまだ失敗する場合は、kube-proxyログで次のような特定の行を探してください。

Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]

これらが表示されない場合は、-vフラグを4に設定してkube-proxyを再起動してから、再度ログを確認してください。

エッジケース: PodがService IP経由で自身に到達できない

これはありそうに聞こえないかもしれませんが、実際には起こり、動作するはずです。これはネットワークが"hairpin"トラフィック用に適切に設定されていない場合、通常はkube-proxyiptablesモードで実行され、Podがブリッジネットワークに接続されている場合に発生します。Kubelethairpin-modeフラグを公開します。これにより、Serviceのエンドポイントが自身のServiceのVIPにアクセスしようとした場合に、自身への負荷分散を可能にします。hairpin-modeフラグはhairpin-vethまたはpromiscuous-bridgeに設定する必要があります。

この問題をトラブルシューティングする一般的な手順は次のとおりです。

  • hairpin-modehairpin-vethまたはpromiscuous-bridgeに設定されていることを確認します。次のような表示がされるはずです。この例では、hairpin-modepromiscuous-bridgeに設定されています。
ps auxw | grep kubelet
root      3392  1.1  0.8 186804 65208 ?        Sl   00:51  11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
  • 実際に使われているhairpin-modeを確認します。これを行うには、kubeletログを確認する必要があります。ログへのアクセス方法は、ノードのOSによって異なります。一部のOSでは/var/log/kubelet.logなどのファイルですが、他のOSではjournalctlを使用してログにアクセスします。互換性のために、実際に使われているhairpin-mode--hairpin-modeフラグと一致しない場合があることに注意してください。kubelet.logにキーワードhairpinを含むログ行があるかどうかを確認してください。実際に使われているhairpin-modeを示す以下のようなログ行があるはずです。
I0629 00:51:43.648698    3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
  • 実際に使われているhairpin-modehairpin-vethの場合、Kubeletにノードの/sysで操作する権限があることを確認します。すべてが正常に機能している場合、次のようなものが表示されます。
for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1

実際に使われているhairpin-modepromiscuous-bridgeの場合、Kubeletにノード上のLinuxブリッジを操作する権限があることを確認してください。cbr0ブリッジが使用され適切に構成されている場合、以下が表示されます。

ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1460  Metric:1
  • 上記のいずれも解決しない場合、助けを求めてください。

助けを求める

ここまでたどり着いたということは、とてもおかしなことが起こっています。Serviceは実行中で、Endpointsがあり、Podは実際にサービスを提供しています。DNSは動作していて、kube-proxyも誤動作していないようです。それでも、あなたのServiceは機能していません。おそらく私たちにお知らせ頂いた方がよいでしょう。調査をお手伝いします!

SlackForumまたはGitHubでお問い合わせください。

次の項目

詳細については、トラブルシューティングドキュメントをご覧ください。

4.4.1.3 - Pod障害の原因を特定する

このページでは、コンテナ終了メッセージの読み書き方法を説明します。

終了メッセージは、致命的なイベントに関する情報を、ダッシュボードや監視ソフトウェアなどのツールで簡単に取得して表示できる場所にコンテナが書き込むための手段を提供します。 ほとんどの場合、終了メッセージに入力した情報も一般的なKubernetesログに書き込まれるはずです。

始める前に

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

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

終了メッセージの書き込みと読み取り

この課題では、1つのコンテナを実行するPodを作成します。 設定ファイルには、コンテナの開始時に実行されるコマンドを指定します。

apiVersion: v1
kind: Pod
metadata:
  name: termination-demo
spec:
  containers:
  - name: termination-demo-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
  1. YAML設定ファイルに基づいてPodを作成します:

     kubectl apply -f https://k8s.io/examples/debug/termination.yaml
    

    YAMLファイルのcommandフィールドとargsフィールドで、コンテナが10秒間スリープしてから/dev/termination-logファイルに「Sleep expired」と書いているのがわかります。コンテナが「Sleep expired」メッセージを書き込んだ後、コンテナは終了します。

  2. Podに関する情報を表示します:

     kubectl get pod termination-demo
    

    Podが実行されなくなるまで、上記のコマンドを繰り返します。

  3. Podに関する詳細情報を表示します:

     kubectl get pod termination-demo --output=yaml
    

    出力には「Sleep expired」メッセージが含まれています:

     apiVersion: v1
     kind: Pod
     ...
         lastState:
           terminated:
             containerID: ...
             exitCode: 0
             finishedAt: ...
             message: |
               Sleep expired
             ...
    
  4. Goテンプレートを使用して、終了メッセージのみが含まれるように出力をフィルタリングします:

     kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
    

終了メッセージのカスタマイズ

Kubernetesは、コンテナのterminationMessagePathフィールドで指定されている終了メッセージファイルから終了メッセージを取得します。デフォルト値は/dev/termination-logです。このフィールドをカスタマイズすることで、Kubernetesに別のファイルを使うように指示できます。Kubernetesは指定されたファイルの内容を使用して、成功と失敗の両方についてコンテナのステータスメッセージを入力します。

終了メッセージはアサーションエラーメッセージのように、最終状態を簡潔に示します。kubeletは4096バイトより長いメッセージは切り詰めます。全コンテナの合計メッセージの長さの上限は12キビバイトです。デフォルトの終了メッセージのパスは/dev/termination-logです。Pod起動後に終了メッセージのパスを設定することはできません。

次の例では、コンテナはKubernetesが取得するために終了メッセージを/tmp/my-logに書き込みます:

apiVersion: v1
kind: Pod
metadata:
  name: msg-path-demo
spec:
  containers:
  - name: msg-path-demo-container
    image: debian
    terminationMessagePath: "/tmp/my-log"

さらに、ユーザーは追加のカスタマイズをするためにContainerのterminationMessagePolicyフィールドを設定できます。このフィールドのデフォルト値はFileです。これは、終了メッセージが終了メッセージファイルからのみ取得されることを意味します。terminationMessagePolicyFallbackToLogsOnErrorに設定することで、終了メッセージファイルが空でコンテナがエラーで終了した場合に、コンテナログ出力の最後のチャンクを使用するようにKubernetesに指示できます。ログ出力は、2048バイトまたは80行のどちらか小さい方に制限されています。

次の項目

4.4.1.4 - StatefulSetのデバッグ

このタスクでは、StatefulSetをデバッグする方法を説明します。

始める前に

  • Kubernetesクラスターが必要です。また、kubectlコマンドラインツールがクラスターと通信するように設定されている必要があります。
  • 調べたいStatefulSetを実行しておきましょう。

StatefulSetのデバッグ

StatefulSetに属し、ラベルapp=myappが設定されているすべてのPodを一覧表示するには、以下のコマンドを利用できます。

kubectl get pods -l app=myapp

Podが長期間UnknownまたはTerminatingの状態になっていることがわかった場合は、それらを処理する方法についてStatefulSetの削除タスクを参照してください。 Podのデバッグガイドを使用して、StatefulSet内の個々のPodをデバッグできます。

次の項目

Initコンテナのデバッグの詳細

4.4.1.5 - Initコンテナのデバッグ

このページでは、Initコンテナの実行に関連する問題を調査する方法を説明します。以下のコマンドラインの例では、Podを<pod-name>、Initコンテナを<init-container-1>および<init-container-2>として参照しています。

始める前に

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

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

Initコンテナのステータスを確認する

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

kubectl get pod <pod-name>

たとえば、Init:1/2というステータスは、2つのInitコンテナのうちの1つが正常に完了したことを示します。

NAME         READY     STATUS     RESTARTS   AGE
<pod-name>   0/1       Init:1/2   0          7s

ステータス値とその意味の例については、Podのステータスを理解するを参照してください。

Initコンテナの詳細を取得する

Initコンテナの実行に関する詳細情報を表示します:

kubectl describe pod <pod-name>

たとえば、2つのInitコンテナを持つPodでは、次のように表示されます:

Init Containers:
  <init-container-1>:
    Container ID:    ...
    ...
    State:           Terminated
      Reason:        Completed
      Exit Code:     0
      Started:       ...
      Finished:      ...
    Ready:           True
    Restart Count:   0
    ...
  <init-container-2>:
    Container ID:    ...
    ...
    State:           Waiting
      Reason:        CrashLoopBackOff
    Last State:      Terminated
      Reason:        Error
      Exit Code:     1
      Started:       ...
      Finished:      ...
    Ready:           False
    Restart Count:   3
    ...

また、Pod Specのstatus.initContainerStatusesフィールドを読むことでプログラムでInitコンテナのステータスにアクセスすることもできます。:

kubectl get pod nginx --template '{{.status.initContainerStatuses}}'

このコマンドは生のJSONで上記と同じ情報を返します。

Initコンテナのログにアクセスする

ログにアクセスするには、Initコンテナ名とPod名を渡します。

kubectl logs <pod-name> -c <init-container-2>

シェルスクリプトを実行するInitコンテナは、実行時にコマンドを出力します。たとえば、スクリプトの始めにset -xを実行することでBashで同じことができます。

Podのステータスを理解する

Init:で始まるPodステータスはInitコンテナの実行ステータスを要約します。以下の表は、Initコンテナのデバッグ中に表示される可能性のあるステータス値の例をいくつか示しています。

ステータス意味
Init:N/MPodはM個のInitコンテナを持ち、これまでにN個完了しました。
Init:ErrorInitコンテナが実行に失敗しました。
Init:CrashLoopBackOffInitコンテナが繰り返し失敗しました。
PendingPodはまだInitコンテナの実行を開始していません。
PodInitializing or RunningPodはすでにInitコンテナの実行を終了しています。

4.4.1.6 - 実行中のPodのデバッグ

このページでは、ノード上で動作している(またはクラッシュしている)Podをデバッグする方法について説明します。

始める前に

  • あなたのPodは既にスケジュールされ、実行されているはずです。Podがまだ実行されていない場合は、アプリケーションのトラブルシューティングから始めてください。

  • いくつかの高度なデバッグ手順では、Podがどのノードで動作しているかを知り、そのノードでコマンドを実行するためのシェルアクセス権を持っていることが必要です。kubectlを使用する標準的なデバッグ手順の実行には、そのようなアクセスは必要ではありません。

kubectl describe podを使ってpodの詳細を取得

この例では、先ほどの例と同様に、Deploymentを使用して2つのpodを作成します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80

以下のコマンドを実行して、Deploymentを作成します:

kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created

以下のコマンドでPodの状態を確認します:

kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1006230814-6winp   1/1       Running   0          11s
nginx-deployment-1006230814-fmgu3   1/1       Running   0          11s

kubectl describe podを使うと、これらのPodについてより多くの情報を得ることができます。 例えば:

kubectl describe pod nginx-deployment-1006230814-6winp
Name:		nginx-deployment-1006230814-6winp
Namespace:	default
Node:		kubernetes-node-wul5/10.240.0.9
Start Time:	Thu, 24 Mar 2016 01:39:49 +0000
Labels:		app=nginx,pod-template-hash=1006230814
Annotations:    kubernetes.io/created-by={"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicaSet","namespace":"default","name":"nginx-deployment-1956810328","uid":"14e607e7-8ba1-11e7-b5cb-fa16" ...
Status:		Running
IP:		10.244.0.6
Controllers:	ReplicaSet/nginx-deployment-1006230814
Containers:
  nginx:
    Container ID:	docker://90315cc9f513c724e9957a4788d3e625a078de84750f244a40f97ae355eb1149
    Image:		nginx
    Image ID:		docker://6f62f48c4e55d700cf3eb1b5e33fa051802986b77b874cc351cce539e5163707
    Port:		80/TCP
    QoS Tier:
      cpu:	Guaranteed
      memory:	Guaranteed
    Limits:
      cpu:	500m
      memory:	128Mi
    Requests:
      memory:		128Mi
      cpu:		500m
    State:		Running
      Started:		Thu, 24 Mar 2016 01:39:51 +0000
    Ready:		True
    Restart Count:	0
    Environment:        <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-5kdvl (ro)
Conditions:
  Type          Status
  Initialized   True
  Ready         True
  PodScheduled  True
Volumes:
  default-token-4bcbi:
    Type:	Secret (a volume populated by a Secret)
    SecretName:	default-token-4bcbi
    Optional:   false
QoS Class:      Guaranteed
Node-Selectors: <none>
Tolerations:    <none>
Events:
  FirstSeen	LastSeen	Count	From					SubobjectPath		Type		Reason		Message
  ---------	--------	-----	----					-------------		--------	------		-------
  54s		54s		1	{default-scheduler }						Normal		Scheduled	Successfully assigned nginx-deployment-1006230814-6winp to kubernetes-node-wul5
  54s		54s		1	{kubelet kubernetes-node-wul5}	spec.containers{nginx}	Normal		Pulling		pulling image "nginx"
  53s		53s		1	{kubelet kubernetes-node-wul5}	spec.containers{nginx}	Normal		Pulled		Successfully pulled image "nginx"
  53s		53s		1	{kubelet kubernetes-node-wul5}	spec.containers{nginx}	Normal		Created		Created container with docker id 90315cc9f513
  53s		53s		1	{kubelet kubernetes-node-wul5}	spec.containers{nginx}	Normal		Started		Started container with docker id 90315cc9f513

ここでは、コンテナ(複数可)とPodに関する構成情報(ラベル、リソース要件など)や、コンテナ(複数可)とPodに関するステータス情報(状態、準備状況、再起動回数、イベントなど)を確認できます。

コンテナの状態は、Waiting(待機中)、Running(実行中)、Terminated(終了)のいずれかです。状態に応じて、追加の情報が提供されます。ここでは、Running状態のコンテナについて、コンテナがいつ開始されたかが表示されています。

Readyは、コンテナが最後のReadiness Probeに合格したかどうかを示す。(この場合、コンテナにはReadiness Probeが設定されていません。Readiness Probeが設定されていない場合、コンテナは準備が完了した状態であるとみなされます)。

Restart Countは、コンテナが何回再起動されたかを示します。この情報は、再起動ポリシーが「always」に設定されているコンテナのクラッシュループを検出するのに役立ちます。

現在、Podに関連する条件は、二値のReady条件のみです。これは、Podがリクエストに対応可能であり、マッチングするすべてのサービスのロードバランシングプールに追加されるべきであることを示します。

最後に、Podに関連する最近のイベントのログが表示されます。このシステムでは、複数の同一イベントを圧縮して、最初に見られた時刻と最後に見られた時刻、そして見られた回数を示します。"From"はイベントを記録しているコンポーネントを示し、"SubobjectPath"はどのオブジェクト(例: Pod内のコンテナ)が参照されているかを示し、"Reason"と "Message"は何が起こったかを示しています。

例: Pending Podsのデバッグ

イベントを使って検出できる一般的なシナリオは、どのノードにも収まらないPodを作成した場合です。例えば、Podがどのノードでも空いている以上のリソースを要求したり、どのノードにもマッチしないラベルセレクターを指定したりする場合です。例えば、各(仮想)マシンが1つのCPUを持つ4ノードのクラスター上で、(2つではなく)5つのレプリカを持ち、500ではなく600ミリコアを要求する前のDeploymentを作成したとします。この場合、Podの1つがスケジュールできなくなります。(なお、各ノードではfluentdやskydnsなどのクラスターアドオンPodが動作しているため、もし1000ミリコアを要求した場合、どのPodもスケジュールできなくなります)

kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1006230814-6winp   1/1       Running   0          7m
nginx-deployment-1006230814-fmgu3   1/1       Running   0          7m
nginx-deployment-1370807587-6ekbw   1/1       Running   0          1m
nginx-deployment-1370807587-fg172   0/1       Pending   0          1m
nginx-deployment-1370807587-fz9sd   0/1       Pending   0          1m

nginx-deployment-1370807587-fz9sdのPodが実行されていない理由を調べるには、保留中のPodに対してkubectl describe podを使用し、そのイベントを見てみましょう

kubectl describe pod nginx-deployment-1370807587-fz9sd
  Name:		nginx-deployment-1370807587-fz9sd
  Namespace:	default
  Node:		/
  Labels:		app=nginx,pod-template-hash=1370807587
  Status:		Pending
  IP:
  Controllers:	ReplicaSet/nginx-deployment-1370807587
  Containers:
    nginx:
      Image:	nginx
      Port:	80/TCP
      QoS Tier:
        memory:	Guaranteed
        cpu:	Guaranteed
      Limits:
        cpu:	1
        memory:	128Mi
      Requests:
        cpu:	1
        memory:	128Mi
      Environment Variables:
  Volumes:
    default-token-4bcbi:
      Type:	Secret (a volume populated by a Secret)
      SecretName:	default-token-4bcbi
  Events:
    FirstSeen	LastSeen	Count	From			        SubobjectPath	Type		Reason			    Message
    ---------	--------	-----	----			        -------------	--------	------			    -------
    1m		    48s		    7	    {default-scheduler }			        Warning		FailedScheduling	pod (nginx-deployment-1370807587-fz9sd) failed to fit in any node
  fit failure on node (kubernetes-node-6ta5): Node didn't have enough resource: CPU, requested: 1000, used: 1420, capacity: 2000
  fit failure on node (kubernetes-node-wul5): Node didn't have enough resource: CPU, requested: 1000, used: 1100, capacity: 2000

ここでは、理由 FailedScheduling (およびその他の理由)でPodのスケジュールに失敗したという、スケジューラーによって生成されたイベントを見ることができます。このメッセージは、どのノードでもPodに十分なリソースがなかったことを示しています。

この状況を修正するには、kubectl scaleを使用して、4つ以下のレプリカを指定するようにDeploymentを更新します。(あるいは、1つのPodを保留にしたままにしておいても害はありません。)

kubectl describe podの最後に出てきたようなイベントは、etcdに永続化され、クラスターで何が起こっているかについての高レベルの情報を提供します。

すべてのイベントをリストアップするには、次のようにします:

kubectl get events

しかし、イベントは名前空間に所属することを忘れてはいけません。つまり、名前空間で管理されているオブジェクトのイベントに興味がある場合(例: 名前空間 my-namespaceのPods で何が起こったか)、コマンドに名前空間を明示的に指定する必要があります。

kubectl get events --namespace=my-namespace

すべての名前空間からのイベントを見るには、--all-namespaces 引数を使用できます。

kubectl describe podに加えて、(kubectl get pod で提供される以上の)Podに関する追加情報を得るためのもう一つの方法は、-o yaml出力形式フラグを kubectl get podに渡すことです。これにより、kubectl describe podよりもさらに多くの情報、つまりシステムが持っているPodに関するすべての情報をYAML形式で得ることができます。ここでは、アノテーション(Kubernetesのシステムコンポーネントが内部的に使用している、ラベル制限のないキーバリューのメタデータ)、再起動ポリシー、ポート、ボリュームなどが表示されます。

kubectl get pod nginx-deployment-1006230814-6winp -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/created-by: |
      {"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicaSet","namespace":"default","name":"nginx-deployment-1006230814","uid":"4c84c175-f161-11e5-9a78-42010af00005","apiVersion":"extensions","resourceVersion":"133434"}}      
  creationTimestamp: 2016-03-24T01:39:50Z
  generateName: nginx-deployment-1006230814-
  labels:
    app: nginx
    pod-template-hash: "1006230814"
  name: nginx-deployment-1006230814-6winp
  namespace: default
  resourceVersion: "133447"
  uid: 4c879808-f161-11e5-9a78-42010af00005
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: nginx
    ports:
    - containerPort: 80
      protocol: TCP
    resources:
      limits:
        cpu: 500m
        memory: 128Mi
      requests:
        cpu: 500m
        memory: 128Mi
    terminationMessagePath: /dev/termination-log
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-4bcbi
      readOnly: true
  dnsPolicy: ClusterFirst
  nodeName: kubernetes-node-wul5
  restartPolicy: Always
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  volumes:
  - name: default-token-4bcbi
    secret:
      secretName: default-token-4bcbi
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: 2016-03-24T01:39:51Z
    status: "True"
    type: Ready
  containerStatuses:
  - containerID: docker://90315cc9f513c724e9957a4788d3e625a078de84750f244a40f97ae355eb1149
    image: nginx
    imageID: docker://6f62f48c4e55d700cf3eb1b5e33fa051802986b77b874cc351cce539e5163707
    lastState: {}
    name: nginx
    ready: true
    restartCount: 0
    state:
      running:
        startedAt: 2016-03-24T01:39:51Z
  hostIP: 10.240.0.9
  phase: Running
  podIP: 10.244.0.6
  startTime: 2016-03-24T01:39:49Z

例: ダウン/到達不可能なノードのデバッグ

例えば、ノード上で動作しているPodのおかしな挙動に気付いたり、Podがノード上でスケジュールされない原因を探ったりと、デバッグ時にノードのステータスを見ることが有用な場合があります。Podと同様に、kubectl describe nodekubectl get node -o yamlを使ってノードの詳細情報を取得することができます。例えば、ノードがダウンした場合(ネットワークから切断された、またはkubeletが死んで再起動しないなど)に表示される内容は以下の通りです。ノードがNotReadyであることを示すイベントに注目してください。また、Podが実行されなくなっていることにも注目してください(NotReady状態が5分続くと、Podは退避されます)。

kubectl get nodes
NAME                     STATUS       ROLES     AGE     VERSION
kubernetes-node-861h     NotReady     <none>    1h      v1.13.0
kubernetes-node-bols     Ready        <none>    1h      v1.13.0
kubernetes-node-st6x     Ready        <none>    1h      v1.13.0
kubernetes-node-unaj     Ready        <none>    1h      v1.13.0
kubectl describe node kubernetes-node-861h
Name:			kubernetes-node-861h
Role
Labels:		 kubernetes.io/arch=amd64
           kubernetes.io/os=linux
           kubernetes.io/hostname=kubernetes-node-861h
Annotations:        node.alpha.kubernetes.io/ttl=0
                    volumes.kubernetes.io/controller-managed-attach-detach=true
Taints:             <none>
CreationTimestamp:	Mon, 04 Sep 2017 17:13:23 +0800
Phase:
Conditions:
  Type		Status		LastHeartbeatTime			LastTransitionTime			Reason					Message
  ----    ------    -----------------     ------------------      ------          -------
  OutOfDisk             Unknown         Fri, 08 Sep 2017 16:04:28 +0800         Fri, 08 Sep 2017 16:20:58 +0800         NodeStatusUnknown       Kubelet stopped posting node status.
  MemoryPressure        Unknown         Fri, 08 Sep 2017 16:04:28 +0800         Fri, 08 Sep 2017 16:20:58 +0800         NodeStatusUnknown       Kubelet stopped posting node status.
  DiskPressure          Unknown         Fri, 08 Sep 2017 16:04:28 +0800         Fri, 08 Sep 2017 16:20:58 +0800         NodeStatusUnknown       Kubelet stopped posting node status.
  Ready                 Unknown         Fri, 08 Sep 2017 16:04:28 +0800         Fri, 08 Sep 2017 16:20:58 +0800         NodeStatusUnknown       Kubelet stopped posting node status.
Addresses:	10.240.115.55,104.197.0.26
Capacity:
 cpu:           2
 hugePages:     0
 memory:        4046788Ki
 pods:          110
Allocatable:
 cpu:           1500m
 hugePages:     0
 memory:        1479263Ki
 pods:          110
System Info:
 Machine ID:                    8e025a21a4254e11b028584d9d8b12c4
 System UUID:                   349075D1-D169-4F25-9F2A-E886850C47E3
 Boot ID:                       5cd18b37-c5bd-4658-94e0-e436d3f110e0
 Kernel Version:                4.4.0-31-generic
 OS Image:                      Debian GNU/Linux 8 (jessie)
 Operating System:              linux
 Architecture:                  amd64
 Container Runtime Version:     docker://1.12.5
 Kubelet Version:               v1.6.9+a3d1dfa6f4335
 Kube-Proxy Version:            v1.6.9+a3d1dfa6f4335
ExternalID:                     15233045891481496305
Non-terminated Pods:            (9 in total)
  Namespace                     Name                                            CPU Requests    CPU Limits      Memory Requests Memory Limits
  ---------                     ----                                            ------------    ----------      --------------- -------------
......
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests  CPU Limits      Memory Requests         Memory Limits
  ------------  ----------      ---------------         -------------
  900m (60%)    2200m (146%)    1009286400 (66%)        5681286400 (375%)
Events:         <none>
kubectl get node kubernetes-node-861h -o yaml
apiVersion: v1
kind: Node
metadata:
  creationTimestamp: 2015-07-10T21:32:29Z
  labels:
    kubernetes.io/hostname: kubernetes-node-861h
  name: kubernetes-node-861h
  resourceVersion: "757"
  uid: 2a69374e-274b-11e5-a234-42010af0d969
spec:
  externalID: "15233045891481496305"
  podCIDR: 10.244.0.0/24
  providerID: gce://striped-torus-760/us-central1-b/kubernetes-node-861h
status:
  addresses:
  - address: 10.240.115.55
    type: InternalIP
  - address: 104.197.0.26
    type: ExternalIP
  capacity:
    cpu: "1"
    memory: 3800808Ki
    pods: "100"
  conditions:
  - lastHeartbeatTime: 2015-07-10T21:34:32Z
    lastTransitionTime: 2015-07-10T21:35:15Z
    reason: Kubelet stopped posting node status.
    status: Unknown
    type: Ready
  nodeInfo:
    bootID: 4e316776-b40d-4f78-a4ea-ab0d73390897
    containerRuntimeVersion: docker://Unknown
    kernelVersion: 3.16.0-0.bpo.4-amd64
    kubeProxyVersion: v0.21.1-185-gffc5a86098dc01
    kubeletVersion: v0.21.1-185-gffc5a86098dc01
    machineID: ""
    osImage: Debian GNU/Linux 7 (wheezy)
    systemUUID: ABE5F6B4-D44B-108B-C46A-24CCE16C8B6E

Podログを調べます

まず、影響を受けるコンテナのログを見ます。

kubectl logs ${POD_NAME} ${CONTAINER_NAME}

コンテナが以前にクラッシュしたことがある場合、次のコマンドで以前のコンテナのクラッシュログにアクセスすることができます:

kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}

container execによるデバッグ

もしcontainer imageがデバッグユーティリティを含んでいれば、LinuxやWindows OSのベースイメージからビルドしたイメージのように、kubectl execで特定のコンテナ内でコマンドを実行することが可能です:

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

例として、実行中のCassandra Podからログを見るには、次のように実行します。

kubectl exec cassandra -- cat /var/log/cassandra/system.log

例えばkubectl exec-i-t引数を使って、端末に接続されたシェルを実行することができます:

kubectl exec -it cassandra -- sh

詳しくは、実行中のコンテナへのシェルを取得するを参照してください。

エフェメラルコンテナによるデバッグ

FEATURE STATE: Kubernetes v1.25 [stable]

エフェメラルコンテナは、コンテナがクラッシュしたり、コンテナイメージにデバッグユーティリティが含まれていないなどの理由でkubectl execが不十分な場合に、対話的にトラブルシューティングを行うのに便利です(ディストロレスイメージの場合など)。

エフェメラルコンテナを使用したデバッグ例

実行中のPodにエフェメラルコンテナを追加するには、kubectl debugコマンドを使用することができます。 まず、サンプル用のPodを作成します:

kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never

このセクションの例では、デバッグユーティリティが含まれていないpauseコンテナイメージを使用していますが、この方法はすべてのコンテナイメージで動作します。

もし、kubectl execを使用してシェルを作成しようとすると、このコンテナイメージにはシェルが存在しないため、エラーが表示されます。

kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown

代わりに、kubectl debugを使ってデバッグ用のコンテナを追加することができます。 引数に-i/--interactiveを指定すると、kubectlは自動的にエフェメラルコンテナのコンソールにアタッチされます。

kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #

このコマンドは新しいbusyboxコンテナを追加し、それにアタッチします。--targetパラメーターは、他のコンテナのプロセス名前空間をターゲットにします。これはkubectl runが作成するPodでプロセス名前空間の共有を有効にしないため、指定する必要があります。

新しく作成されたエフェメラルコンテナの状態はkubectl describeを使って見ることができます:

kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
  debugger-8xzrl:
    Container ID:   docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
    Image:          busybox
    Image ID:       docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 12 Feb 2020 14:25:42 +0100
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

終了したらkubectl deleteを使ってPodを削除してください:

kubectl delete pod ephemeral-demo

Podのコピーを使ったデバッグ

Podの設定オプションによって、特定の状況でのトラブルシューティングが困難になることがあります。 例えば、コンテナイメージにシェルが含まれていない場合、またはアプリケーションが起動時にクラッシュした場合は、kubectl execを実行してトラブルシューティングを行うことができません。 このような状況では、kubectl debugを使用してデバッグを支援するために設定値を変更したPodのコピーを作ることができます。

新しいコンテナを追加しながらPodをコピーします

新しいコンテナを追加することは、アプリケーションは動作しているが期待通りの動作をせず、トラブルシューティングユーティリティをPodに追加したい場合に便利です。 例えば、アプリケーションのコンテナイメージはbusybox上にビルドされているが、busyboxに含まれていないデバッグユーティリティが必要な場合があります。このシナリオはkubectl runを使ってシミュレーションすることができます。

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

このコマンドを実行すると、myappのコピーにmyapp-debugという名前が付き、デバッグ用の新しいUbuntuコンテナが追加されます。

kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#

デバッグが終わったら、Podの後始末をするのを忘れないでください。

kubectl delete pod myapp myapp-debug

Podのコマンドを変更しながらコピーします

例えば、デバッグフラグを追加する場合や、アプリケーションがクラッシュしている場合などでは、コンテナのコマンドを変更すると便利なことがあります。 アプリケーションのクラッシュをシミュレートするには、kubectl runを使用して、すぐに終了するコンテナを作成します:

kubectl run --image=busybox:1.28 myapp -- false

kubectl describe pod myappを使用すると、このコンテナがクラッシュしていることがわかります:

Containers:
  myapp:
    Image:         busybox
    ...
    Args:
      false
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1

kubectl debugを使うと、コマンドをインタラクティブシェルに変更したこのPodのコピーを作成することができます。

kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #

これで、ファイルシステムのパスのチェックやコンテナコマンドの手動実行などのタスクを実行するために使用できる対話型シェルが完成しました。

デバッグが終わったら、Podの後始末をするのを忘れないでください:

kubectl delete pod myapp myapp-debug

コンテナイメージを変更してPodをコピーします

状況によっては、動作不良のPodを通常のプロダクション用のコンテナイメージから、デバッグビルドや追加ユーティリティを含むイメージに変更したい場合があります。

例として、kubectl runを使用してPodを作成します:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

ここで、kubectl debugを使用してコピーを作成し、そのコンテナイメージをubuntuに変更します:

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

--set-imageの構文は、kubectl set imageと同じcontainer_name=imageの構文を使用します。*=ubuntuは、全てのコンテナのイメージをubuntuに変更することを意味します。

デバッグが終わったら、Podの後始末をするのを忘れないでください:

kubectl delete pod myapp myapp-debug

ノード上のシェルによるデバッグ

いずれの方法でもうまくいかない場合は、Podが動作しているノードを探し出し、ホストの名前空間で動作する特権Podを作成します。 ノード上でkubectl debugを使って対話型のシェルを作成するには、以下を実行します:

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#

ノードでデバッグセッションを作成する場合、以下の点に注意してください:

  • kubectl debugはノードの名前に基づいて新しいPodの名前を自動的に生成します。
  • コンテナはホストのIPC、Network、PIDネームスペースで実行されます。
  • ノードのルートファイルシステムは/hostにマウントされます。

デバッグが終わったら、Podの後始末をするのを忘れないでください:

kubectl delete pod node-debugger-mynode-pdx84

4.4.1.7 - 実行中のコンテナへのシェルを取得する

このページはkubectl execを使用して実行中のコンテナへのシェルを取得する方法を説明します。

始める前に

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

コンテナへのシェルの取得

このエクササイズでは、1つのコンテナを持つPodを作成します。 コンテナはnginxのイメージを実行します。以下がそのPodの設定ファイルです:

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

Podを作成します:

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

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

kubectl get pod shell-demo

実行中のコンテナへのシェルを取得します:

kubectl exec --stdin --tty shell-demo -- /bin/bash

シェル内で、ルートディレクトリーのファイル一覧を表示します:

# このコマンドをコンテナ内で実行します
ls /

シェル内で、他のコマンドを試しましょう。以下がいくつかの例です:

# これらのサンプルコマンドをコンテナ内で実行することができます
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx

nginxのルートページへの書き込み

Podの設定ファイルを再度確認します。PodはemptyDirボリュームを持ち、 コンテナは/usr/share/nginx/htmlボリュームをマウントします。

シェル内で、/usr/share/nginx/htmlディレクトリにindex.htmlを作成します。

# このコマンドをコンテナ内で実行します
echo 'Hello shell demo' > /usr/share/nginx/html/index.html

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

# これらのコマンドをコンテナ内のシェルで実行します
apt-get update
apt-get install curl
curl http://localhost/

出力にindex.htmlファイルに書き込んだ文字列が表示されます:

Hello shell demo

シェルを終了する場合、exitを入力します。

exit # コンテナ内のシェルを終了する

コンテナ内での各コマンドの実行

シェルではない通常のコマンドウインドウ内で、実行中のコンテナの環境変数の一覧を表示します:

kubectl exec shell-demo -- env

他のコマンドを試します。以下がいくつかの例です:

kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts

Podが1つ以上のコンテナを持つ場合にシェルを開く

Podが1つ以上のコンテナを持つ場合、--container-cを使用して、kubectl execコマンド内でコンテナを指定します。 例えば、my-podという名前のPodがあり、そのPodが main-apphelper-app という2つのコンテナを持つとします。 以下のコマンドは main-app のコンテナへのシェルを開きます。

kubectl exec -i -t my-pod --container main-app -- /bin/bash

次の項目

4.4.2 - クラスターのトラブルシューティング

一般的なクラスターの問題をデバッグします。

このドキュメントはクラスターのトラブルシューティングに関するもので、あなたが経験している問題の根本原因として、アプリケーションをすでに除外していることを前提としています。 アプリケーションのデバッグのコツは、アプリケーションのトラブルシューティングガイドをご覧ください。 また、トラブルシューティングドキュメントにも詳しい情報があります。

kubectlのトラブルシューティングについては、kubectlのトラブルシューティングを参照してください。

クラスターのリストアップ

クラスターで最初にデバッグするのは、ノードがすべて正しく登録されているかどうかです。

以下を実行します。

kubectl get nodes

そして、期待するノードがすべて存在し、それらがすべて Ready 状態であることを確認します。

クラスター全体の健全性に関する詳細な情報を得るには、以下を実行します。

kubectl cluster-info dump

例: ダウンあるいは到達不能なノードのデバッグ

デバッグを行う際、ノードの状態を見ることが有用なことがあります。 たとえば、そのノード上で動作しているPodが奇妙な挙動を示している場合や、なぜPodがそのノードにスケジュールされないのかを知りたい場合などです。 Podと同様に、kubectl describe nodekubectl get node -o yamlを使用してノードに関する詳細情報を取得できます。 例えば、ノードがダウンしている(ネットワークから切断されている、またはkubeletが停止して再起動しないなど)場合に見られる状況は以下の通りです。 ノードがNotReadyであることを示すイベントに注意し、また、Podが動作していないことにも注意してください(NotReady状態が5分間続くとPodは追い出されます)。

kubectl get nodes
NAME                     STATUS       ROLES     AGE     VERSION
kube-worker-1            NotReady     <none>    1h      v1.23.3
kubernetes-node-bols     Ready        <none>    1h      v1.23.3
kubernetes-node-st6x     Ready        <none>    1h      v1.23.3
kubernetes-node-unaj     Ready        <none>    1h      v1.23.3
kubectl describe node kube-worker-1
Name:               kube-worker-1
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kube-worker-1
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Thu, 17 Feb 2022 16:46:30 -0500
Taints:             node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  kube-worker-1
  AcquireTime:     <unset>
  RenewTime:       Thu, 17 Feb 2022 17:13:09 -0500
Conditions:
  Type                 Status    LastHeartbeatTime                 LastTransitionTime                Reason              Message
  ----                 ------    -----------------                 ------------------                ------              -------
  NetworkUnavailable   False     Thu, 17 Feb 2022 17:09:13 -0500   Thu, 17 Feb 2022 17:09:13 -0500   WeaveIsUp           Weave pod has set this
  MemoryPressure       Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  DiskPressure         Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  PIDPressure          Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  Ready                Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
Addresses:
  InternalIP:  192.168.0.113
  Hostname:    kube-worker-1
Capacity:
  cpu:                2
  ephemeral-storage:  15372232Ki
  hugepages-2Mi:      0
  memory:             2025188Ki
  pods:               110
Allocatable:
  cpu:                2
  ephemeral-storage:  14167048988
  hugepages-2Mi:      0
  memory:             1922788Ki
  pods:               110
System Info:
  Machine ID:                 9384e2927f544209b5d7b67474bbf92b
  System UUID:                aa829ca9-73d7-064d-9019-df07404ad448
  Boot ID:                    5a295a03-aaca-4340-af20-1327fa5dab5c
  Kernel Version:             5.13.0-28-generic
  OS Image:                   Ubuntu 21.10
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.5.9
  Kubelet Version:            v1.23.3
  Kube-Proxy Version:         v1.23.3
Non-terminated Pods:          (4 in total)
  Namespace                   Name                                 CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                 ------------  ----------  ---------------  -------------  ---
  default                     nginx-deployment-67d4bdd6f5-cx2nz    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  default                     nginx-deployment-67d4bdd6f5-w6kd7    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  kube-system                 kube-proxy-dnxbz                     0 (0%)        0 (0%)      0 (0%)           0 (0%)         28m
  kube-system                 weave-net-gjxxp                      100m (5%)     0 (0%)      200Mi (10%)      0 (0%)         28m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1100m (55%)  1 (50%)
  memory             456Mi (24%)  256Mi (13%)
  ephemeral-storage  0 (0%)       0 (0%)
  hugepages-2Mi      0 (0%)       0 (0%)
Events:
...
kubectl get node kube-worker-1 -o yaml
apiVersion: v1
kind: Node
metadata:
  annotations:
    kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
    node.alpha.kubernetes.io/ttl: "0"
    volumes.kubernetes.io/controller-managed-attach-detach: "true"
  creationTimestamp: "2022-02-17T21:46:30Z"
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: kube-worker-1
    kubernetes.io/os: linux
  name: kube-worker-1
  resourceVersion: "4026"
  uid: 98efe7cb-2978-4a0b-842a-1a7bf12c05f8
spec: {}
status:
  addresses:
  - address: 192.168.0.113
    type: InternalIP
  - address: kube-worker-1
    type: Hostname
  allocatable:
    cpu: "2"
    ephemeral-storage: "14167048988"
    hugepages-2Mi: "0"
    memory: 1922788Ki
    pods: "110"
  capacity:
    cpu: "2"
    ephemeral-storage: 15372232Ki
    hugepages-2Mi: "0"
    memory: 2025188Ki
    pods: "110"
  conditions:
  - lastHeartbeatTime: "2022-02-17T22:20:32Z"
    lastTransitionTime: "2022-02-17T22:20:32Z"
    message: Weave pod has set this
    reason: WeaveIsUp
    status: "False"
    type: NetworkUnavailable
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient memory available
    reason: KubeletHasSufficientMemory
    status: "False"
    type: MemoryPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has no disk pressure
    reason: KubeletHasNoDiskPressure
    status: "False"
    type: DiskPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient PID available
    reason: KubeletHasSufficientPID
    status: "False"
    type: PIDPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:15:15Z"
    message: kubelet is posting ready status. AppArmor enabled
    reason: KubeletReady
    status: "True"
    type: Ready
  daemonEndpoints:
    kubeletEndpoint:
      Port: 10250
  nodeInfo:
    architecture: amd64
    bootID: 22333234-7a6b-44d4-9ce1-67e31dc7e369
    containerRuntimeVersion: containerd://1.5.9
    kernelVersion: 5.13.0-28-generic
    kubeProxyVersion: v1.23.3
    kubeletVersion: v1.23.3
    machineID: 9384e2927f544209b5d7b67474bbf92b
    operatingSystem: linux
    osImage: Ubuntu 21.10
    systemUUID: aa829ca9-73d7-064d-9019-df07404ad448

ログの確認

今のところ、クラスターをより深く掘り下げるには、関連するマシンにログインする必要があります。 以下は、関連するログファイルの場所です。 (systemdベースのシステムでは、代わりにjournalctlを使う必要があるかもしれないことに注意してください)

コントロールプレーンノード

  • /var/log/kube-apiserver.log - APIの提供を担当するAPIサーバーのログ
  • /var/log/kube-scheduler.log - スケジューリング決定責任者であるスケジューラーのログ
  • /var/log/kube-controller-manager.log - スケジューリングを除く、ほとんどのKubernetes組み込みのコントローラーを実行するコンポーネントのログ(スケジューリングはkube-schedulerが担当します)

ワーカーノード

  • /var/log/kubelet.log - ノード上でコンテナの実行を担当するKubeletのログ
  • /var/log/kube-proxy.log - サービスのロードバランシングを担うKube Proxyのログ

クラスター障害モードの一般的な概要

これは、問題が発生する可能性のある事柄と、問題を軽減するためにクラスターのセットアップを調整する方法の不完全なリストです。

根本的な原因

  • VMのシャットダウン
  • クラスター内、またはクラスターとユーザー間のネットワークパーティション
  • Kubernetesソフトウェアのクラッシュ
  • データの損失や永続的ストレージ(GCE PDやAWS EBSボリュームなど)の使用不能
  • Kubernetesソフトウェアやアプリケーションソフトウェアの設定ミスなど、オペレーターのミス

具体的なシナリオ

  • apiserver VMのシャットダウンまたはapiserverのクラッシュ
    • その結果
      • 新しいPod、サービス、レプリケーションコントローラーの停止、更新、起動ができない
      • Kubernetes APIに依存していない限り、既存のPodやサービスは正常に動作し続けるはずです
  • apiserverのバックアップストレージが失われた
    • その結果
      • apiserverが立ち上がらない
      • kubeletは到達できなくなりますが、同じPodを実行し、同じサービスのプロキシを提供し続けます
      • apiserverを再起動する前に、手動でapiserverの状態を回復または再現する必要がある
  • サポートサービス(ノードコントローラー、レプリケーションコントローラーマネージャー、スケジューラーなど)VMのシャットダウンまたはクラッシュ
    • 現在、これらはapiserverとコロケーションしており、使用できない場合はapiserverと同様の影響があります
    • 将来的には、これらも複製されるようになり、同じ場所に配置されない可能性があります
    • 独自の永続的な状態を持っていない
  • 個別ノード(VMまたは物理マシン)のシャットダウン
    • その結果
      • そのノード上のPodの実行を停止
  • ネットワークパーティション
    • その結果
      • パーティションAはパーティションBのノードがダウンしていると考え、パーティションBはapiserverがダウンしていると考えています。(マスターVMがパーティションAで終了したと仮定)
  • Kubeletソフトウェア障害
    • その結果
      • クラッシュしたkubeletがノード上で新しいPodを起動できない
      • kubeletがPodを削除するかどうか
      • ノードが不健全と判定される
      • レプリケーションコントローラーが別の場所で新しいPodを起動する
  • クラスターオペレーターエラー
    • その結果
      • PodやServiceなどの損失
      • apiserverのバックエンドストレージの紛失
      • ユーザーがAPIを読めなくなる
      • その他

軽減策

  • 対処法: IaaSプロバイダーの自動VM再起動機能をIaaS VMに使用する

    • 異常: Apiserver VMのシャットダウンまたはApiserverのクラッシュ
    • 異常: サポートサービスのVMシャットダウンまたはクラッシュ
  • 対処法: IaaSプロバイダーの信頼できるストレージ(GCE PDやAWS EBSボリュームなど)をapiserver+etcdを使用するVMに使用する

    • 異常: Apiserverのバックエンドストレージが失われる
  • 対処法: 高可用性構成を使用します

    • 異常: コントロールプレーンノードのシャットダウンまたはコントロールプレーンコンポーネント(スケジューラー、APIサーバー、コントローラーマネージャー)のクラッシュ
      • 1つ以上のノードまたはコンポーネントの同時故障に耐えることができる
    • 異常: APIサーバーのバックアップストレージ(etcdのデータディレクトリーなど)が消失
      • HA(高可用性)etcdの構成を想定しています
  • 対処法: apiserver PDs/EBS-volumesを定期的にスナップショットする

    • 異常: Apiserverのバックエンドストレージが失われる
    • 異常: 操作ミスが発生する場合がある
    • 異常: Kubernetesのソフトウェアに障害が発生する場合がある
  • 対処法:レプリケーションコントローラーとServiceをPodの前に使用する

    • 異常: ノードのシャットダウン
    • 異常: Kubeletソフトウェア障害
  • 対処法: 予期せぬ再起動に耐えられるように設計されたアプリケーション(コンテナ)

    • 異常: ノードのシャットダウン
    • 異常: Kubeletソフトウェア障害

次の項目

4.4.2.1 - リソースメトリクスパイプライン

Kubernetesでは、コンテナのCPU使用率やメモリ使用率といったリソース使用量のメトリクスが、メトリクスAPIを通じて提供されています。これらのメトリクスは、ユーザーがkubectl topコマンドで直接アクセスするか、クラスター内のコントローラー(例えばHorizontal Pod Autoscaler)が判断するためにアクセスすることができます。

メトリクスAPI

メトリクスAPIを使用すると、指定したノードやPodが現在使用しているリソース量を取得することができます。 このAPIはメトリックの値を保存しないので、例えば10分前に指定されたノードが使用したリソース量を取得することはできません。

メトリクスAPIは他のAPIと何ら変わりはありません。

  • 他のKubernetes APIと同じエンドポイントを経由して、/apis/metrics.k8s.io/パスの下で発見できます。
  • 同じセキュリティ、スケーラビリティ、信頼性の保証を提供します。

メトリクスAPIはk8s.io/metricsリポジトリで定義されています。 メトリクスAPIについての詳しい情報はそちらをご覧ください。

リソース使用量の測定

CPU

CPUは、一定期間の平均使用量をCPU coresという単位で報告されます。 この値は、カーネルが提供する累積CPUカウンターの比率を取得することで得られます(LinuxとWindowsの両カーネルで)。 kubeletは、比率計算のためのウィンドウを選択します。

メモリ

メモリは、測定値が収集された時点のワーキングセットとして、バイト単位で報告されます。 理想的な世界では、「ワーキングセット」は、メモリ不足で解放できない使用中のメモリ量です。 しかし、ワーキングセットの計算はホストOSによって異なり、一般に推定値を生成するために経験則を多用しています。 Kubernetesはスワップをサポートしていないため、すべての匿名(非ファイルバックアップ)メモリが含まれます。 ホストOSは常にそのようなページを再請求することができないため、メトリックには通常、一部のキャッシュされた(ファイルバックされた)メモリも含まれます。

メトリクスサーバー

メトリクスサーバーは、クラスター全体のリソース使用量データのアグリゲーターです。 デフォルトでは、kube-up.shスクリプトで作成されたクラスターにDeploymentオブジェクトとしてデプロイされます。 別のKubernetesセットアップ機構を使用する場合は、提供されるdeployment components.yamlファイルを使用してデプロイすることができます。 メトリクスサーバーは、Summary APIからメトリクスを収集します。 各ノードのKubeletからKubernetes aggregator経由でメインAPIサーバーに登録されるようになっています。

メトリクスサーバーについては、Design proposalsで詳しく解説しています。

Summary APIソース

Kubeletは、ノード、ボリューム、Pod、コンテナレベルの統計情報を収集し、Summary APIで省略して消費者が読めるようにするものです。

1.23以前は、これらのリソースは主にcAdvisorから収集されていました。しかし、1.23ではPodAndContainerStatsFromCRIフィーチャーゲートの導入により、コンテナとPodレベルの統計情報をCRI実装で収集することができます。

注意: これはCRI実装によるサポートも必要です(containerd >= 1.6.0, CRI-O >= 1.23.0)。

4.4.2.2 - リソース監視のためのツール

アプリケーションを拡張し、信頼性の高いサービスを提供するために、デプロイ時にアプリケーションがどのように動作するかを理解する必要があります。 コンテナ、PodService、クラスター全体の特性を調べることにより、Kubernetesクラスターのアプリケーションパフォーマンスを調査することができます。 Kubernetesは、これらの各レベルでアプリケーションのリソース使用に関する詳細な情報を提供します。 この情報により、アプリケーションのパフォーマンスを評価し、ボトルネックを取り除くことで全体のパフォーマンスを向上させることができます。

Kubernetesでは、アプリケーションの監視は1つの監視ソリューションに依存することはありません。 新しいクラスターでは、リソースメトリクスまたはフルメトリクスパイプラインを使用してモニタリング統計を収集することができます。

リソースメトリクスパイプライン

リソースメトリックパイプラインは、Horizontal Pod Autoscalerコントローラーなどのクラスターコンポーネントや、kubectl topユーティリティに関連する限定的なメトリックセットを提供します。

これらのメトリクスは軽量、短期、インメモリーのmetrics-serverによって収集され、metrics.k8s.io APIを通じて公開されます。

metrics-serverはクラスター上のすべてのノードを検出し 各ノードのkubeletにCPUとメモリーの使用量を問い合わせます。

kubeletはKubernetesマスターとノードの橋渡し役として、マシン上で動作するPodやコンテナを管理する。 kubeletは各Podを構成するコンテナに変換し、コンテナランタイムインターフェースを介してコンテナランタイムから個々のコンテナ使用統計情報を取得します。この情報は、レガシーDocker統合のための統合cAdvisorから取得されます。

そして、集約されたPodリソース使用統計情報を、metrics-server Resource Metrics APIを通じて公開します。 このAPIは、kubeletの認証済みおよび読み取り専用ポート上の /metrics/resource/v1beta1 で提供されます。

フルメトリクスパイプライン

フルメトリクスパイプラインは、より豊富なメトリクスにアクセスすることができます。 Kubernetesは、Horizontal Pod Autoscalerなどのメカニズムを使用して、現在の状態に基づいてクラスターを自動的にスケールまたは適応させることによって、これらのメトリクスに対応することができます。 モニタリングパイプラインは、kubeletからメトリクスを取得し、custom.metrics.k8s.io または external.metrics.k8s.io APIを実装してアダプタ経由でKubernetesにそれらを公開します。 CNCFプロジェクトのPrometheusは、Kubernetes、ノード、Prometheus自身をネイティブに監視することができます。 CNCFに属さない完全なメトリクスパイプラインのプロジェクトは、Kubernetesのドキュメントの範囲外です。

次の項目

以下のような追加のデバッグツールについて学びます:

4.4.2.3 - ノードの健全性を監視します

Node Problem Detectorは、ノードの健全性を監視し、報告するためのデーモンです。 Node Problem DetectorDaemonSetとして、あるいはスタンドアロンデーモンとして実行することができます。

Node Problem Detectorは様々なデーモンからノードの問題に関する情報を収集し、これらの状態をNodeConditionおよびEventとしてAPIサーバーにレポートします。 Node Problem Detectorのインストール方法と使用方法については、Node Problem Detectorプロジェクトドキュメントを参照してください。

始める前に

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

制限事項

  • Node Problem Detectorは、ファイルベースのカーネルログのみをサポートします。 journaldのようなログツールはサポートされていません。

  • Node Problem Detectorは、カーネルの問題を報告するためにカーネルログフォーマットを使用します。 カーネルログフォーマットを拡張する方法については、Add support for another log format を参照してください。

ノード問題検出の有効化

クラウドプロバイダーによっては、Node Problem DetectorAddonとして有効にしている場合があります。 また、kubectlを使ってNode Problem Detectorを有効にするか、Addon podを作成することで有効にできます。

kubectlを使用してNode Problem Detectorを有効化します

kubectlNode Problem Detectorを最も柔軟に管理することができます。 デフォルトの設定を上書きして自分の環境に合わせたり、カスタマイズしたノードの問題を検出したりすることができます。 例えば:

  1. node-problem-detector.yamlのようなNode Problem Detectorの設定を作成します:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-problem-detector-v0.1
      namespace: kube-system
      labels:
        k8s-app: node-problem-detector
        version: v0.1
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        matchLabels:
          k8s-app: node-problem-detector  
          version: v0.1
          kubernetes.io/cluster-service: "true"
      template:
        metadata:
          labels:
            k8s-app: node-problem-detector
            version: v0.1
            kubernetes.io/cluster-service: "true"
        spec:
          hostNetwork: true
          containers:
          - name: node-problem-detector
            image: registry.k8s.io/node-problem-detector:v0.1
            securityContext:
              privileged: true
            resources:
              limits:
                cpu: "200m"
                memory: "100Mi"
              requests:
                cpu: "20m"
                memory: "20Mi"
            volumeMounts:
            - name: log
              mountPath: /log
              readOnly: true
          volumes:
          - name: log
            hostPath:
              path: /var/log/
  2. Node Problem Detectorkubectlで起動します。

    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector.yaml
    

Addon podを使用してNode Problem Detectorを有効化します

カスタムのクラスターブートストラップソリューションを使用していて、デフォルトの設定を上書きする必要がない場合は、Addon Podを利用してデプロイをさらに自動化できます。 node-problem-detector.yamlを作成し、制御プレーンノードのAddon Podのディレクトリ/etc/kubernetes/addons/node-problem-detectorに設定を保存します。

コンフィギュレーションを上書きします

Node Problem Detectorの Dockerイメージをビルドする際に、default configurationが埋め込まれます。

ConfigMap を使用することで設定を上書きすることができます。

  1. config/ にある設定ファイルを変更します

  2. ConfigMap node-problem-detector-configを作成します。

    kubectl create configmap node-problem-detector-config --from-file=config/
    
  3. node-problem-detector.yamlを変更して、ConfigMapを使用するようにします。

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-problem-detector-v0.1
      namespace: kube-system
      labels:
        k8s-app: node-problem-detector
        version: v0.1
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        matchLabels:
          k8s-app: node-problem-detector  
          version: v0.1
          kubernetes.io/cluster-service: "true"
      template:
        metadata:
          labels:
            k8s-app: node-problem-detector
            version: v0.1
            kubernetes.io/cluster-service: "true"
        spec:
          hostNetwork: true
          containers:
          - name: node-problem-detector
            image: registry.k8s.io/node-problem-detector:v0.1
            securityContext:
              privileged: true
            resources:
              limits:
                cpu: "200m"
                memory: "100Mi"
              requests:
                cpu: "20m"
                memory: "20Mi"
            volumeMounts:
            - name: log
              mountPath: /log
              readOnly: true
            - name: config # Overwrite the config/ directory with ConfigMap volume
              mountPath: /config
              readOnly: true
          volumes:
          - name: log
            hostPath:
              path: /var/log/
          - name: config # Define ConfigMap volume
            configMap:
              name: node-problem-detector-config
  4. 新しい設定ファイルでNode Problem Detectorを再作成します。

    # If you have a node-problem-detector running, delete before recreating
    kubectl delete -f https://k8s.io/examples/debug/node-problem-detector.yaml
    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector-configmap.yaml
    

ノード問題検出装置がクラスターアドオンとして実行されている場合、設定の上書きはサポートされていません。 Addon Managerは、ConfigMapをサポートしていません。

Kernel Monitor

Kernel MonitorNode Problem Detectorでサポートされるシステムログ監視デーモンです。 Kernel Monitorはカーネルログを監視し、事前に定義されたルールに従って既知のカーネル問題を検出します。 Kernel Monitorconfig/kernel-monitor.jsonにある一連の定義済みルールリストに従ってカーネルの問題を照合します。 ルールリストは拡張可能です。設定を上書きすることで、ルールリストを拡張することができます。

新しいNodeConditionsの追加

新しいNodeConditionをサポートするには、例えばconfig/kernel-monitor.jsonconditionsフィールド内に条件定義を作成します。

{
  "type": "NodeConditionType",
  "reason": "CamelCaseDefaultNodeConditionReason",
  "message": "arbitrary default node condition message"
}

新たな問題の発見

新しい問題を検出するために、config/kernel-monitor.jsonrulesフィールドを新しいルール定義で拡張することができます。

{
  "type": "temporary/permanent",
  "condition": "NodeConditionOfPermanentIssue",
  "reason": "CamelCaseShortReason",
  "message": "regexp matching the issue in the kernel log"
}

カーネルログデバイスのパスの設定

ご使用のオペレーティングシステム(OS)ディストリビューションのカーネルログパスをご確認ください。 Linuxカーネルのログデバイスは通常/dev/kmsgとして表示されます。 しかし、OSのディストリビューションによって、ログパスの位置は異なります。 config/kernel-monitor.jsonlogフィールドは、コンテナ内のログパスを表します。 logフィールドは、Node Problem Detectorで見たデバイスパスと一致するように設定することができます。

別のログ形式をサポートします

Kernel monitorはTranslatorプラグインを使用して、カーネルログの内部データ構造を変換します。 新しいログフォーマット用に新しいトランスレータを実装することができます。

推奨・制限事項

ノードの健全性を監視するために、クラスターでNode Problem Detectorを実行することが推奨されます。 Node Problem Detectorを実行する場合、各ノードで余分なリソースのオーバーヘッドが発生することが予想されます。

通常これは問題ありません。

  • カーネルログは比較的ゆっくりと成長します。
  • Node Problem Detector にはリソース制限が設定されています。
  • 高負荷時であっても、リソースの使用は許容範囲内です。

詳細はNode Problem Detectorベンチマーク結果を参照してください。

4.4.2.4 - crictlによるKubernetesノードのデバッグ

FEATURE STATE: Kubernetes v1.11 [stable]

crictlはCRI互換のコンテナランタイム用のコマンドラインインターフェースです。

これを使って、Kubernetesノード上のコンテナランタイムやアプリケーションの検査やデバッグを行うことができます。 crictlとそのソースコードはcri-toolsリポジトリにホストされています。

始める前に

crictlにはCRIランタイムを搭載したLinuxが必要です。

crictlのインストール

cri-toolsのリリースページから、いくつかの異なるアーキテクチャ用の圧縮アーカイブcrictlをダウンロードできます。

お使いのKubernetesのバージョンに対応するバージョンをダウンロードしてください。 それを解凍してシステムパス上の/usr/local/bin/などの場所に移動します。

一般的な使い方

crictlコマンドにはいくつかのサブコマンドとランタイムフラグがあります。 詳細はcrictl helpまたはcrictl <subcommand> helpを参照してください。 crictlはデフォルトではunix:///var/run/dockershim.sockに接続します。

他のランタイムの場合は、複数の異なる方法でエンドポイントを設定することができます:

  • フラグ--runtime-endpoint--image-endpointの設定により
  • 環境変数CONTAINER_RUNTIME_ENDPOINTIMAGE_SERVICE_ENDPOINTの設定により
  • 設定ファイル--config=/etc/crictl.yamlでエンドポイントの設定により

また、サーバーに接続する際のタイムアウト値を指定したり、デバッグを有効/無効にしたりすることもできます。 これには、設定ファイルでtimeoutdebugを指定するか、--timeout--debugのコマンドラインフラグを使用します。

現在の設定を表示または編集するには、/etc/crictl.yamlの内容を表示または編集します。

cat /etc/crictl.yaml
runtime-endpoint: unix:///var/run/dockershim.sock
image-endpoint: unix:///var/run/dockershim.sock
timeout: 10
debug: true

crictlコマンドの例

以下の例では、いくつかのcrictlコマンドとその出力例を示しています。

podsの一覧

すべてのポッドをリストアップ:

crictl pods

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

POD ID              CREATED              STATE               NAME                         NAMESPACE           ATTEMPT
926f1b5a1d33a       About a minute ago   Ready               sh-84d7dcf559-4r2gq          default             0
4dccb216c4adb       About a minute ago   Ready               nginx-65899c769f-wv2gp       default             0
a86316e96fa89       17 hours ago         Ready               kube-proxy-gblk4             kube-system         0
919630b8f81f1       17 hours ago         Ready               nvidia-device-plugin-zgbbv   kube-system         0

Podを名前でリストアップします:

crictl pods --name nginx-65899c769f-wv2gp

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

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

Podをラベルでリストアップします:

crictl pods --label run=nginx

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

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

イメージの一覧

すべてのイメージをリストアップします:

crictl images

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

IMAGE                                     TAG                 IMAGE ID            SIZE
busybox                                   latest              8c811b4aec35f       1.15MB
k8s-gcrio.azureedge.net/hyperkube-amd64   v1.10.3             e179bbfe5d238       665MB
k8s-gcrio.azureedge.net/pause-amd64       3.1                 da86e6ba6ca19       742kB
nginx                                     latest              cd5239a0906a6       109MB

イメージをリポジトリでリストアップします:

crictl images nginx

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

IMAGE               TAG                 IMAGE ID            SIZE
nginx               latest              cd5239a0906a6       109MB

イメージのIDのみをリストアップします:

crictl images -q

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

sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
sha256:e179bbfe5d238de6069f3b03fccbecc3fb4f2019af741bfff1233c4d7b2970c5
sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e
sha256:cd5239a0906a6ccf0562354852fae04bc5b52d72a2aff9a871ddb6bd57553569

List containers

すべてのコンテナをリストアップします:

crictl ps -a

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

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   7 minutes ago       Running             sh                         1
9c5951df22c78       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   8 minutes ago       Exited              sh                         0
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     8 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   18 hours ago        Running             kube-proxy                 0

ランニングコンテナをリストアップします:

crictl ps

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

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   6 minutes ago       Running             sh                         1
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     7 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   17 hours ago        Running             kube-proxy                 0

実行中のコンテナでコマンドの実行

crictl exec -i -t 1f73f2d81bf98 ls

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

bin   dev   etc   home  proc  root  sys   tmp   usr   var

コンテナログの取得

すべてのコンテナログを取得します:

crictl logs 87d3992f84f74

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

10.240.0.96 - - [06/Jun/2018:02:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

最新のN行のログのみを取得します:

crictl logs --tail=1 87d3992f84f74

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

10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

Podサンドボックスの実行

crictlを使ってPodサンドボックスを実行することは、コンテナのランタイムをデバッグするのに便利です。 稼働中のKubernetesクラスターでは、サンドボックスは最終的にKubeletによって停止され、削除されます。

  1. 以下のようなJSONファイルを作成します:

    {
        "metadata": {
            "name": "nginx-sandbox",
            "namespace": "default",
            "attempt": 1,
            "uid": "hdishd83djaidwnduwk28bcsb"
        },
        "logDirectory": "/tmp",
        "linux": {
        }
    }
    
  2. JSONを適用してサンドボックスを実行するには、crictl runpコマンドを使用します:

    crictl runp pod-config.json
    

    サンドボックスのIDが返されます。

コンテナの作成

コンテナの作成にcrictlを使うと、コンテナのランタイムをデバッグするのに便利です。 稼働中のKubernetesクラスターでは、サンドボックスは最終的にKubeletによって停止され、削除されます。

  1. busyboxイメージをプルします:

    crictl pull busybox
    Image is up to date for busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
    
  2. Podとコンテナのコンフィグを作成します:

    Pod config:

    {
        "metadata": {
            "name": "nginx-sandbox",
            "namespace": "default",
            "attempt": 1,
            "uid": "hdishd83djaidwnduwk28bcsb"
        },
        "log_directory": "/tmp",
        "linux": {
        }
    }
    

    Container config:

    {
      "metadata": {
          "name": "busybox"
      },
      "image":{
          "image": "busybox"
      },
      "command": [
          "top"
      ],
      "log_path":"busybox.log",
      "linux": {
      }
    }
    
  3. 先に作成されたPodのID、コンテナの設定ファイル、Podの設定ファイルを渡して、コンテナを作成します。コンテナのIDが返されます。

    crictl create f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f container-config.json pod-config.json
    
  4. すべてのコンテナをリストアップし、新しく作成されたコンテナの状態がCreatedに設定されていることを確認します:

    crictl ps -a
    

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

    CONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT
    3e025dd50a72d       busybox             32 seconds ago      Created             busybox             0
    

コンテナの起動

コンテナを起動するには、そのコンテナのIDをcrictl startに渡します:

crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

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

3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

コンテナの状態が「Running」に設定されていることを確認します:

crictl ps

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

CONTAINER ID        IMAGE               CREATED              STATE               NAME                ATTEMPT
3e025dd50a72d       busybox             About a minute ago   Running             busybox             0

詳しくはkubernetes-sigs/cri-toolsをご覧ください。

docker cliからcrictlへのマッピング

以下のマッピング表の正確なバージョンは、docker cli v1.40crictl v1.19.0のものです。 この一覧はすべてを網羅しているわけではないことに注意してください。 たとえば、docker cliの実験的なコマンドは含まれていません。

デバッグ情報の取得

mapping from docker cli to crictl - retrieve debugging information
docker clicrictl説明サポートされていない機能
attachattach実行中のコンテナにアタッチ--detach-keys, --sig-proxy
execexec実行中のコンテナでコマンドの実行--privileged, --user, --detach-keys
imagesimagesイメージのリストアップ 
infoinfoシステム全体の情報の表示 
inspectinspect, inspectiコンテナ、イメージ、タスクの低レベルの情報を返します 
logslogsコンテナのログを取得します--details
pspsコンテナのリストアップ 
statsstatsコンテナのリソース使用状況をライブで表示Column: NET/BLOCK I/O, PIDs
versionversionランタイム(Docker、ContainerD、その他)のバージョン情報を表示します 

変更を行います

mapping from docker cli to crictl - perform changes
docker clicrictl説明サポートされていない機能
createcreate新しいコンテナを作成します 
killstop (timeout = 0)1つ以上の実行中のコンテナを停止します--signal
pullpullレジストリーからイメージやリポジトリをプルします--all-tags, --disable-content-trust
rmrm1つまたは複数のコンテナを削除します 
rmirmi1つまたは複数のイメージを削除します 
runrun新しいコンテナでコマンドを実行 
startstart停止した1つまたは複数のコンテナを起動--detach-keys
stopstop実行中の1つまたは複数のコンテナの停止 
updateupdate1つまたは複数のコンテナの構成を更新--restart--blkio-weightとその他

crictlでのみ対応

mapping from docker cli to crictl - supported only in crictl
crictl説明
imagefsinfoイメージファイルシステムの情報を返します
inspectp1つまたは複数のPodの状態を表示します
port-forwardローカルポートをPodに転送します
runp新しいPodを実行します
rmp1つまたは複数のPodを削除します
stopp稼働中の1つまたは複数のPodを停止します

4.4.2.5 - Kubectlを用いたKubernetesノードのデバッグ

このページでは、Kubernetesクラスター上で動作しているノードkubectl debugコマンドを使用してデバッグする方法について説明します。

始める前に

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

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

Podを作成し、それらの新しいPodを任意のノードに割り当てる権限が必要です。 また、ホストのファイルシステムにアクセスするPodを作成する権限も必要です。

kubectl debug nodeを用いてノードをデバッグする

kubectl debug nodeコマンドを使用して、トラブルシューティングしたいノードにPodをデプロイします。 このコマンドは、SSH接続を使用してノードにアクセスできないシナリオで役に立ちます。 Podが作成されると、そのPodはノード上で対話型シェルを開きます。 mynodeという名前のノード上で対話型シェルを作成するには、次のように実行します。

kubectl debug node/mynode -it --image=ubuntu
ノードmynode上に、コンテナデバッガーを持つデバッグ用Pod、node-debugger-mynode-pdx84を作成します。
コマンドプロンプトが表示されない場合は、エンターキーを押してみてください。
root@mynode:/#

デバッグコマンドは、情報を収集し、問題をトラブルシューティングするのに役立ちます。 使用する可能性のあるコマンドには、ipifconfigncpingpsなどがあります。 また、mtrtcpdumpcurlなどの他のツールもそれぞれのパッケージマネージャーからインストールすることができます。

デバッグ用のPodは、Pod内の/hostにマウントされたノードのルートファイルシステムにアクセスできます。 kubeletをファイルシステムのネームスペースで実行している場合、デバッグ用のPodはそのネームスペースのルートを見ることになり、ノード全体のルートではありません。 典型的なLinuxノードの場合、関連するログを見つけるために以下のパスを確認できます。

/host/var/log/kubelet.log
ノード上でコンテナを実行する責任があるkubeletからのログです。
/host/var/log/kube-proxy.log
サービスのエンドポイントへのトラフィックを指示する責任があるkube-proxyからのログです。
/host/var/log/containerd.log
ノード上で実行されているcontainerdプロセスからのログです。
/host/var/log/syslog
システムに関する一般的なメッセージや情報です。
/host/var/log/kern.log
カーネルログです。

ノード上でデバッグセッションを作成する際には、以下の点を考慮してください。

  • kubectl debugは、新しいPodの名前をノードの名前に基づいて自動的に生成します。
  • ノードのルートファイルシステムは/hostにマウントされます。
  • コンテナはホストのIPC、ネットワーク、PID Namespaceで実行されますが、Podは特権を持っていません。 これは、一部のプロセス情報の読み取りが失敗する可能性があることを意味します。 その情報へのアクセスはスーパーユーザーに制限されているためです。 例えば、chroot /hostは失敗します。 特権Podが必要な場合は、手動で作成してください。

クリーンアップ

デバッグ用のPodの使用が終了したら、それを削除してください。

kubectl get pods
NAME                          READY   STATUS       RESTARTS   AGE
node-debugger-mynode-pdx84    0/1     Completed    0          8m1s
# Podの名前は適宜変更してください
kubectl delete pod node-debugger-mynode-pdx84 --now
pod "node-debugger-mynode-pdx84" deleted

4.4.2.6 - WindowsデバッグTips

ノードレベルのトラブルシューティング

  1. Podが"Container Creating"と表示されたまま動かなくなったり、何度も再起動を繰り返します

    pauseイメージがWindows OSのバージョンと互換性があることを確認してください。 最新/推奨のpauseイメージや詳細情報については、Pauseコンテナを参照してください。

  2. PodがErrImgPullまたはImagePullBackOffのステータスを表示します

    Podが互換性のあるWindowsノードにスケジュールされていることを確認してください。

    Podに対して互換性のあるノードを指定する方法の詳細については、このガイドを参照してください。

ネットワークのトラブルシューティング

  1. Windows Podがネットワークに接続できません

    仮想マシンを使用している場合は、すべてのVMのネットワークアダプターでMACスプーフィングが有効になっていることを確認してください。

  2. Windows Podから外部リソースにpingできません

    Windows Podには、ICMPプロトコル用にプログラムされたアウトバウンドルールはありません。ただし、TCP/UDPはサポートされています。 クラスター外のリソースへの接続を実証する場合は、ping <IP>を対応するcurl <IP>コマンドに置き換えてください。

    それでも問題が解決しない場合は、cni.confのネットワーク設定に問題がある可能性が高いです。 この静的ファイルはいつでも編集できます。 設定の更新は、新しく作成されたすべてのKubernetesリソースに適用されます。

    Kubernetesのネットワーク要件の1つ(Kubernetesモデルを参照)は、内部でNATせずにクラスター通信が行われることです。 この要件を満たすために、アウトバウンドのNATを発生させたくないすべての通信のためのExceptionListがあります。 ただしこれは、クエリしようとしている外部IPをExceptionListから除外する必要があることも意味します。 そうして初めて、Windows Podからのトラフィックが正しくSNATされ、外部からの応答を受信できるようになります。 この点について、cni.confExceptionListは次のようになります:

    "ExceptionList": [
                    "10.244.0.0/16",  # クラスターのサブネット
                    "10.96.0.0/12",   # Serviceのサブネット
                    "10.127.130.0/24" # 管理(ホスト)のサブネット
                ]
    
  3. WindowsノードがNodePortタイプのServiceにアクセスできません

    ノード自身からのローカルNodePortへのアクセスは失敗します。 これは既知の制限です。 NodePortへのアクセスは、他のノードや外部のクライアントからは動作します。

  4. コンテナのvNICとHNSエンドポイントが削除されています

    この問題はhostname-overrideパラメーターがkube-proxy に渡されていない場合に発生する可能性があります。 これを解決するためには、ユーザーは次のようにkube-proxyにホスト名を渡す必要があります:

    C:\k\kube-proxy.exe --hostname-override=$(hostname)
    
  5. WindowsノードがService IPを使用してサービスにアクセスできません

    これはWindows上のネットワークスタックの既知の制限です。 ただし、Windows PodはService IPにアクセスできます。

  6. kubeletの起動時にネットワークアダプターが見つかりません

    Windowsのネットワーキングスタックでは、Kubernetesネットワーキングが動作するために仮想アダプターが必要です。 (管理者シェルで)次のコマンドを実行しても結果が返されない場合、kubeletが動作するために必要な前提条件である仮想ネットワークの作成に失敗しています。

    Get-HnsNetwork | ? Name -ieq "cbr0"
    Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
    

    ホストのネットワークアダプターが"Ethernet"ではない場合、start.ps1スクリプトのInterfaceNameパラメーターを修正することが有益です。 それ以外の場合は、start-kubelet.ps1スクリプトの出力結果を参照して、仮想ネットワークの作成中にエラーが発生していないか確認します。

  7. DNS名前解決が正しく動作しません

    このセクションのWindowsにおけるDNSの制限について確認してください。

  8. kubectl port-forwardが"unable to do port forwarding: wincat not found"で失敗します

    これは、pauseインフラコンテナmcr.microsoft.com/oss/kubernetes/pause:3.6wincat.exeを含める形で、Kubernetes 1.15にて実装されました。 必ずサポートされたKubernetesのバージョンを使用してください。 独自のpauseインフラコンテナをビルドしたい場合は、必ずwincatを含めるようにしてください。

  9. Windows Serverノードがプロキシの背後にあるため、Kubernetesのインストールに失敗しています

    プロキシの背後にある場合は、次のPowerShell環境変数が定義されている必要があります:

    [Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
    [Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
    

Flannelのトラブルシューティング

  1. Flannelを使用すると、クラスターに再参加した後にノードに問題が発生します

    以前に削除したノードがクラスターに再参加すると、flanneldはノードに新しいPodサブネットを割り当てようとします。 ユーザーは、次のパスにある古いPodサブネットの設定ファイルを削除する必要があります:

    Remove-Item C:\k\SourceVip.json
    Remove-Item C:\k\SourceVipRequest.json
    
  2. Flanneldが"Waiting for the Network to be created"と表示されたままになります

    このIssueに関する多数の報告があります; 最も可能性が高いのは、flannelネットワークの管理IPが設定されるタイミングの問題です。 回避策は、start.ps1を再度実行するか、次のように手動で再起動することです:

    [Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows_Worker_Hostname>")
    C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows_Worker_Node_IP> --ip-masq=1 --kube-subnet-mgr=1
    
  3. /run/flannel/subnet.envが見つからないためにWindows Podが起動しません

    これはFlannelが正常に起動できなかったことを示しています。 flanneld.exeを再起動するか、Kubernetesマスター上の/run/flannel/subnet.envをWindowsワーカーノード上のC:\run\flannel\subnet.envに手動でコピーして、FLANNEL_SUBNET行を異なる数値に変更します。 例えば、ノードのサブネットを10.244.4.1/24としたい場合は次のようにします:

    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.4.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    

さらなる調査

これらの手順で問題が解決しない場合は、下記からKubernetesのWindowsノード上でWindowsコンテナを実行するためのヘルプを得ることができます:

4.4.2.7 - テレプレゼンスを使用したローカルでのサービス開発・デバッグ

Kubernetesアプリケーションは通常、複数の独立したサービスから構成され、それぞれが独自のコンテナで動作しています。これらのサービスをリモートのKubernetesクラスター上で開発・デバッグするには、実行中のコンテナへのシェルを取得してリモートシェル内でツールを実行しなければならず面倒な場合があります。

telepresenceは、リモートKubernetesクラスターにサービスをプロキシしながら、ローカルでサービスを開発・デバッグするプロセスを容易にするためのツールです。 telepresence を使用すると、デバッガーやIDEなどのカスタムツールをローカルサービスで使用でき、ConfigMapやsecret、リモートクラスター上で動作しているサービスへのフルアクセスをサービスに提供します。

このドキュメントでは、リモートクラスター上で動作しているサービスをローカルで開発・デバッグするためにtelepresenceを使用する方法を説明します。

始める前に

  • Kubernetesクラスターがインストールされていること
  • クラスターと通信するために kubectl が設定されていること
  • telepresenceがインストールされていること

リモートクラスター上でシェルの取得

ターミナルを開いて、引数なしでtelepresenceを実行すると、telepresenceシェルが表示されます。 このシェルはローカルで動作し、ローカルのファイルシステムに完全にアクセスすることができます。

このtelepresenceシェルは様々な方法で使用することができます。 例えば、ラップトップでシェルスクリプトを書いて、それをシェルから直接リアルタイムで実行することができます。これはリモートシェルでもできますが、好みのコードエディターが使えないかもしれませんし、コンテナが終了するとスクリプトは削除されます。

終了してシェルを閉じるにはexitと入力してください。

既存サービスの開発・デバッグ

Kubernetes上でアプリケーションを開発する場合、通常は1つのサービスをプログラミングまたはデバッグすることになります。 そのサービスは、テストやデバッグのために他のサービスへのアクセスを必要とする場合があります。 継続的なデプロイメントパイプラインを使用することも一つの選択肢ですが、最速のデプロイメントパイプラインでさえ、プログラムやデバッグサイクルに遅延が発生します。

既存のデプロイメントとtelepresenceプロキシを交換するには、--swap-deployment オプションを使用します。 スワップすることで、ローカルでサービスを実行し、リモートのKubernetesクラスターに接続することができます。 リモートクラスター内のサービスは、ローカルで実行されているインスタンスにアクセスできるようになりました。

telepresenceを「--swap-deployment」で実行するには、次のように入力します。

telepresence --swap-deployment $DEPLOYMENT_NAME

ここで、$DEPLOYMENT_NAMEは既存のDeploymentの名前です。

このコマンドを実行すると、シェルが起動します。そのシェルで、サービスを起動します。 そして、ローカルでソースコードの編集を行い、保存すると、すぐに変更が反映されるのを確認できます。 また、デバッガーやその他のローカルな開発ツールでサービスを実行することもできます。

次の項目

もしハンズオンのチュートリアルに興味があるなら、Google Kubernetes Engine上でGuestbookアプリケーションをローカルに開発する手順を説明したこちらのチュートリアルをチェックしてみてください。

telepresenceには、状況に応じてnumerous proxying optionsがあります。

さらに詳しい情報は、telepresence websiteをご覧ください。

4.4.2.8 - 監査

Kubernetesの監査はクラスター内の一連の行動を記録するセキュリティに関連した時系列の記録を提供します。 クラスターはユーザー、Kubernetes APIを使用するアプリケーション、 およびコントロールプレーン自体によって生成されたアクティビティなどを監査します。

監査により、クラスター管理者は以下の質問に答えることができます:

  • 何が起きたのか?
  • いつ起こったのか?
  • 誰がそれを始めたのか?
  • 何のために起こったのか?
  • それはどこで観察されたのか?
  • それはどこから始まったのか?
  • それはどこへ向かっていたのか?

監査記録のライフサイクルはkube-apiserverコンポーネントの中で始まります。 各リクエストの実行の各段階で、監査イベントが生成されます。 ポリシーに従って前処理され、バックエンドに書き込まれます。 ポリシーが何を記録するかを決定し、 バックエンドがその記録を永続化します。現在のバックエンドの実装はログファイルやWebhookなどがあります。

各リクエストは関連する stage で記録されます。 定義されたステージは以下の通りです:

  • RequestReceived - 監査ハンドラーがリクエストを受信すると同時に生成されるイベントのステージ。 つまり、ハンドラーチェーンに委譲される前に生成されるイベントのステージです。
  • ResponseStarted - レスポンスヘッダーが送信された後、レスポンスボディが送信される前のステージです。 このステージは長時間実行されるリクエスト(watchなど)でのみ発生します。
  • ResponseComplete - レスポンスボディの送信が完了して、それ以上のバイトは送信されません。
  • Panic - パニックが起きたときに発生するイベント。

監査ログ機能は、リクエストごとに監査に必要なコンテキストが保存されるため、APIサーバーのメモリー消費量が増加します。 メモリーの消費量は、監査ログ機能の設定によって異なります。

監査ポリシー

監査ポリシーはどのようなイベントを記録し、どのようなデータを含むべきかについてのルールを定義します。 監査ポリシーのオブジェクト構造は、audit.k8s.io API groupで定義されています。

イベントが処理されると、そのイベントは順番にルールのリストと比較されます。 最初のマッチングルールは、イベントの監査レベルを設定します。

定義されている監査レベルは:

  • None - ルールに一致するイベントを記録しません。
  • Metadata - リクエストのメタデータ(リクエストしたユーザー、タイムスタンプ、リソース、動作など)を記録しますが、リクエストやレスポンスのボディは記録しません。
  • Request - ログイベントのメタデータとリクエストボディは表示されますが、レスポンスボディは表示されません。 これは非リソースリクエストには適用されません。
  • RequestResponse - イベントのメタデータ、リクエストとレスポンスのボディを記録しますが、 非リソースリクエストには適用されません。

audit-policy-fileフラグを使って、ポリシーを記述したファイルを kube-apiserverに渡すことができます。 このフラグが省略された場合イベントは記録されません。 監査ポリシーファイルでは、rulesフィールドが必ず指定されることに注意してください。 ルールがない(0)ポリシーは不当なものとして扱われます。

以下は監査ポリシーファイルの例です:

apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
  - "RequestReceived"
rules:
  # Log pod changes at RequestResponse level
  - level: RequestResponse
    resources:
    - group: ""
      # Resource "pods" doesn't match requests to any subresource of pods,
      # which is consistent with the RBAC policy.
      resources: ["pods"]
  # Log "pods/log", "pods/status" at Metadata level
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # Don't log requests to a configmap called "controller-leader"
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

  # Don't log watch requests by the "system:kube-proxy" on endpoints or services
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: "" # core API group
      resources: ["endpoints", "services"]

  # Don't log authenticated requests to certain non-resource URL paths.
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*" # Wildcard matching.
    - "/version"

  # Log the request body of configmap changes in kube-system.
  - level: Request
    resources:
    - group: "" # core API group
      resources: ["configmaps"]
    # This rule only applies to resources in the "kube-system" namespace.
    # The empty string "" can be used to select non-namespaced resources.
    namespaces: ["kube-system"]

  # Log configmap and secret changes in all other namespaces at the Metadata level.
  - level: Metadata
    resources:
    - group: "" # core API group
      resources: ["secrets", "configmaps"]

  # Log all other resources in core and extensions at the Request level.
  - level: Request
    resources:
    - group: "" # core API group
    - group: "extensions" # Version of group should NOT be included.

  # A catch-all rule to log all other requests at the Metadata level.
  - level: Metadata
    # Long-running requests like watches that fall under this rule will not
    # generate an audit event in RequestReceived.
    omitStages:
      - "RequestReceived"

最小限の監査ポリシーファイルを使用して、すべてのリクエストを Metadataレベルで記録することができます。

# Log all requests at the Metadata level.
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

独自の監査プロファイルを作成する場合は、Google Container-Optimized OSの監査プロファイルを出発点として使用できます。 監査ポリシーファイルを生成するconfigure-helper.shスクリプトを確認することができます。 スクリプトを直接見ることで、監査ポリシーファイルのほとんどを見ることができます。

また、定義されているフィールドの詳細については、Policy` configuration referenceを参照できます。

監査バックエンド

監査バックエンドは監査イベントを外部ストレージに永続化します。 kube-apiserverには2つのバックエンドが用意されています。

  • イベントをファイルシステムに書き込むログバックエンド
  • 外部のHTTP APIにイベントを送信するWebhookバックエンド

いずれの場合も、監査イベントはKubernetes APIaudit.k8s.io API groupで定義されている構造に従います。

ログバックエンド

ログバックエンドは監査イベントをJSONlines形式のファイルに書き込みます。 以下の kube-apiserver フラグを使ってログ監査バックエンドを設定できます。

  • --audit-log-path は、ログバックエンドが監査イベントを書き込む際に使用するログファイルのパスを指定します。 このフラグを指定しないと、ログバックエンドは無効になります。- は標準出力を意味します。
  • --audit-log-maxage は、古い監査ログファイルを保持する最大日数を定義します。
  • audit-log-maxbackupは、保持する監査ログファイルの最大数を定義します。
  • --audit-log-maxsize は、監査ログファイルがローテーションされるまでの最大サイズをメガバイト単位で定義します。

クラスターのコントロールプレーンでkube-apiserverをPodとして動作させている場合は、監査記録が永久化されるように、ポリシーファイルとログファイルの場所にhostPathをマウントすることを忘れないでください。 例えば:

  - --audit-policy-file=/etc/kubernetes/audit-policy.yaml
  - --audit-log-path=/var/log/kubernetes/audit/audit.log

それからボリュームをマウントします:

...
volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy.yaml
    name: audit
    readOnly: true
  - mountPath: /var/log/audit.log
    name: audit-log
    readOnly: false

最後にhostPathを設定します:

...
volumes:
- name: audit
  hostPath:
    path: /etc/kubernetes/audit-policy.yaml
    type: File

- name: audit-log
  hostPath:
    path: /var/log/audit.log
    type: FileOrCreate

Webhookバックエンド

Webhook監査バックエンドは、監査イベントをリモートのWeb APIに送信しますが、 これは認証手段を含むKubernetes APIの形式であると想定されます。

Webhook監査バックエンドを設定するには、以下のkube-apiserverフラグを使用します。

  • --audit-webhook-config-file は、Webhookの設定ファイルのパスを指定します。 webhookの設定は、事実上特化したkubeconfigです。
  • --audit-webhook-initial-backoff は、最初に失敗したリクエストの後、再試行するまでに待つ時間を指定します。 それ以降のリクエストは、指数関数的なバックオフで再試行されます。

Webhookの設定ファイルは、kubeconfig形式でサービスのリモートアドレスと接続に使用する認証情報を指定します。

イベントバッチ

ログバックエンドとwebhookバックエンドの両方がバッチ処理をサポートしています。 webhookを例に、利用可能なフラグの一覧を示します。 ログバックエンドで同じフラグを取得するには、フラグ名のwebhooklogに置き換えてください。 デフォルトでは、バッチングはwebhookでは有効で、logでは無効です。 同様に、デフォルトではスロットリングは webhook で有効で、logでは無効です。

  • --audit-webhook-mode は、バッファリング戦略を定義します。以下のいずれかとなります。
    • batch - イベントをバッファリングして、非同期にバッチ処理します。これがデフォルトです。
    • blocking - 個々のイベントを処理する際に、APIサーバーの応答をブロックします。
    • blocking-strict - blockingと同じですが、RequestReceivedステージでの監査ログに失敗した場合は RequestReceivedステージで監査ログに失敗すると、kube-apiserverへのリクエスト全体が失敗します。

以下のフラグは batch モードでのみ使用されます:

  • --audit-webhook-batch-buffer-sizeは、バッチ処理を行う前にバッファリングするイベントの数を定義します。 入力イベントの割合がバッファをオーバーフローすると、イベントはドロップされます。
  • --audit-webhook-batch-max-sizeは、1つのバッチに入れるイベントの最大数を定義します。
  • --audit-webhook-batch-max-waitは、キュー内のイベントを無条件にバッチ処理するまでの最大待機時間を定義します。
  • --audit-webhook-batch-throttle-qpsは、1秒あたりに生成されるバッチの最大平均数を定義します。
  • --audit-webhook-batch-throttle-burstは、許可された QPS が低い場合に、同じ瞬間に生成されるバッチの最大数を定義します。

パラメーターチューニング

パラメーターは、APIサーバーの負荷に合わせて設定してください。

例えば、kube-apiserverが毎秒100件のリクエストを受け取り、それぞれのリクエストがResponseStartedResponseCompleteの段階でのみ監査されるとします。毎秒≅200の監査イベントが発生すると考えてください。 1つのバッチに最大100個のイベントがあるの場合、スロットリングレベルを少なくとも2クエリ/秒に設定する必要があります。 バックエンドがイベントを書き込むのに最大で5秒かかる場合、5秒分のイベントを保持するようにバッファーサイズを設定する必要があります。

10バッチ、または1000イベントとなります。

しかし、ほとんどの場合デフォルトのパラメーターで十分であり、手動で設定する必要はありません。 kube-apiserverが公開している以下のPrometheusメトリクスや、ログを見て監査サブシステムの状態を監視することができます。

  • apiserver_audit_event_totalメトリックには、エクスポートされた監査イベントの合計数が含まれます。
  • apiserver_audit_error_totalメトリックには、エクスポート中にエラーが発生してドロップされたイベントの総数が含まれます。

ログエントリー・トランケーション

logバックエンドとwebhookバックエンドは、ログに記録されるイベントのサイズを制限することをサポートしています。

例として、logバックエンドで利用可能なフラグの一覧を以下に示します

  • audit-log-truncate-enabledイベントとバッチの切り捨てを有効にするかどうかです。
  • audit-log-truncate-max-batch-sizeバックエンドに送信されるバッチのバイト単位の最大サイズ。
  • audit-log-truncate-max-event-sizeバックエンドに送信される監査イベントのバイト単位の最大サイズです。

デフォルトでは、webhooklogの両方で切り捨ては無効になっていますが、クラスター管理者は audit-log-truncate-enabledまたはaudit-webhook-truncate-enabledを設定して、この機能を有効にする必要があります。

次の項目

4.5 - Kubernetesオブジェクトの管理

Kubernetes APIと対話するための宣言型および命令型のパラダイム。

4.6 - Secretの管理

Secretを使用した機密設定データの管理

4.6.1 - kubectlを使用してSecretを管理する

kubectlコマンドラインを使用してSecretを作成する

始める前に

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

Secretを作成する

SecretはデータベースにアクセスするためにPodが必要とするユーザー資格情報を含めることができます。 たとえば、データベース接続文字列はユーザー名とパスワードで構成されます。 ユーザー名はローカルマシンの./username.txtに、パスワードは./password.txtに保存します。

echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt

上記の2つのコマンドの-nフラグは、生成されたファイルにテキスト末尾の余分な改行文字が含まれないようにします。 kubectlがファイルを読み取り、内容をbase64文字列にエンコードすると、余分な改行文字もエンコードされるため、これは重要です。

kubectl create secretコマンドはこれらのファイルをSecretにパッケージ化し、APIサーバー上にオブジェクトを作成します。

kubectl create secret generic db-user-pass \
  --from-file=./username.txt \
  --from-file=./password.txt

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

secret/db-user-pass created

ファイル名がデフォルトのキー名になります。オプションで--from-file=[key=]sourceを使用してキー名を設定できます。たとえば:

kubectl create secret generic db-user-pass \
  --from-file=username=./username.txt \
  --from-file=password=./password.txt

--from-fileに指定したファイルに含まれるパスワードの特殊文字をエスケープする必要はありません。

また、--from-literal=<key>=<value>タグを使用してSecretデータを提供することもできます。 このタグは、複数のキーと値のペアを提供するために複数回指定することができます。 $\*=!などの特殊文字はシェルによって解釈されるため、エスケープを必要とすることに注意してください。 ほとんどのシェルでは、パスワードをエスケープする最も簡単な方法は、シングルクォート(')で囲むことです。 たとえば、実際のパスワードがS!B\*d$zDsb=の場合、次のようにコマンドを実行します:

kubectl create secret generic db-user-pass \
  --from-literal=username=admin \
  --from-literal=password='S!B\*d$zDsb='

Secretを検証する

Secretが作成されたことを確認できます:

kubectl get secrets

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

NAME                  TYPE                                  DATA      AGE
db-user-pass          Opaque                                2         51s

Secretの説明を参照できます:

kubectl describe secrets/db-user-pass

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

Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password:    12 bytes
username:    5 bytes

kubectl getkubectl describeコマンドはデフォルトではSecretの内容を表示しません。 これは、Secretが不用意に他人にさらされたり、ターミナルログに保存されたりしないようにするためです。

Secretをデコードする

先ほど作成したSecretの内容を見るには、以下のコマンドを実行します:

kubectl get secret db-user-pass -o jsonpath='{.data}'

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

{"password":"MWYyZDFlMmU2N2Rm","username":"YWRtaW4="}

passwordのデータをデコードします:

echo 'MWYyZDFlMmU2N2Rm' | base64 --decode

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

1f2d1e2e67df

クリーンアップ

作成したSecretを削除するには次のコマンドを実行します:

kubectl delete secret db-user-pass

次の項目

4.6.2 - 設定ファイルを使用してSecretを管理する

リソース設定ファイルを使用してSecretを作成する

始める前に

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

設定ファイルを作成する

あらかじめYAMLまたはJSON形式でSecretのマニフェストを作成したうえで、オブジェクトを作成することができます。 Secretリソースには、datastringDataの2つのマップが含まれています。 dataフィールドは任意のデータを格納するのに使用され、base64でエンコードされます。 stringDataフィールドは利便性のために用意されており、Secretデータをエンコードされていない文字列として提供することができます。 datastringDataのキーは、英数字、-_.で構成されている必要があります。

たとえば、dataフィールドを使用して2つの文字列をSecretに格納するには、次のように文字列をbase64に変換します:

echo -n 'admin' | base64

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

YWRtaW4=
echo -n '1f2d1e2e67df' | base64

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

MWYyZDFlMmU2N2Rm

以下のようなSecret設定ファイルを記述します:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

なお、Secretオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。

特定のシナリオでは、代わりにstringDataフィールドを使用できます。 このフィールドでは、base64エンコードされていない文字列を直接Secretに入れることができ、Secretの作成時や更新時には、その文字列がエンコードされます。

たとえば、設定ファイルを保存するためにSecretを使用しているアプリケーションをデプロイする際に、デプロイプロセス中に設定ファイルの一部を入力したい場合などが考えられます。

たとえば、次のような設定ファイルを使用しているアプリケーションの場合:

apiUrl: "https://my.api.com/api/v1"
username: "<user>"
password: "<password>"

次のような定義でSecretに格納できます:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |
    apiUrl: "https://my.api.com/api/v1"
    username: <user>
    password: <password>    

Secretを作成する

kubectl applyでSecretを作成します:

kubectl apply -f ./secret.yaml

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

secret/mysecret created

Secretを確認する

stringDataフィールドは、書き込み専用の便利なフィールドです。Secretを取得する際には決して出力されません。たとえば、次のようなコマンドを実行した場合:

kubectl get secret mysecret -o yaml

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

apiVersion: v1
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque

kubectl getkubectl describeコマンドはデフォルトではSecretの内容を表示しません。 これは、Secretが不用意に他人にさらされたり、ターミナルログに保存されたりしないようにするためです。 エンコードされたデータの実際の内容を確認するには、Secretのデコードを参照してください。

usernameなどのフィールドがdatastringDataの両方に指定されている場合は、stringDataの値が使われます。 たとえば、以下のようなSecretの定義の場合:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

結果は以下の通りです:

apiVersion: v1
data:
  username: YWRtaW5pc3RyYXRvcg==
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:46:46Z
  name: mysecret
  namespace: default
  resourceVersion: "7579"
  uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque

YWRtaW5pc3RyYXRvcg==をデコードするとadministratorとなります。

クリーンアップ

作成したSecretを削除するには次のコマンドを実行します:

kubectl delete secret mysecret

次の項目

4.6.3 - Kustomizeを使用してSecretを管理する

kustomization.yamlを使用してSecretを作成する

Kubernetes v1.14以降、kubectlKustomizeを使ったオブジェクト管理をサポートしています。 KustomizeはSecretやConfigMapを作成するためのリソースジェネレーターを提供します。 Kustomizeジェネレーターは、ディレクトリ内のkustomization.yamlファイルで指定します。 Secretを生成したら、kubectl applyでAPIサーバー上にSecretを作成します。

始める前に

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

Kustomizationファイルを作成する

kustomization.yamlファイルの中でsecretGeneratorを定義し、他の既存のファイルを参照することで、Secretを生成することができます。 たとえば、以下のkustomizationファイルは./username.txt./password.txtを参照しています。

secretGenerator:
- name: db-user-pass
  files:
  - username.txt
  - password.txt

また、kustomization.yamlファイルの中でリテラルを指定してsecretGeneratorを定義することもできます。 たとえば、以下のkustomization.yamlファイルにはusernamepasswordの2つのリテラルが含まれています。

secretGenerator:
- name: db-user-pass
  literals:
  - username=admin
  - password=1f2d1e2e67df

また、kustomization.yamlファイルに.envファイルを用意してsecretGeneratorを定義することもできます。 たとえば、以下のkustomization.yamlファイルは、.env.secretファイルからデータを取り込みます。

secretGenerator:
- name: db-user-pass
  envs:
  - .env.secret

なお、いずれの場合も、値をbase64エンコードする必要はありません。

Secretを作成する

kustomization.yamlを含むディレクトリを適用して、Secretを作成します。

kubectl apply -k .

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

secret/db-user-pass-96mffmfh4k created

なお、Secretを生成する際には、データをハッシュ化し、そのハッシュ値を付加することでSecret名を生成します。 これにより、データが変更されるたびに、新しいSecretが生成されます。

作成したSecretを確認する

Secretが作成されたことを確認できます:

kubectl get secrets

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

NAME                             TYPE                                  DATA      AGE
db-user-pass-96mffmfh4k          Opaque                                2         51s

Secretの説明を参照できます:

kubectl describe secrets/db-user-pass-96mffmfh4k

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

Name:            db-user-pass-96mffmfh4k
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password.txt:    12 bytes
username.txt:    5 bytes

kubectl getkubectl describeコマンドはデフォルトではSecretの内容を表示しません。 これは、Secretが不用意に他人にさらされたり、ターミナルログに保存されたりしないようにするためです。 エンコードされたデータの実際の内容を確認するには、Secretのデコードを参照してください。

クリーンアップ

作成したSecretを削除するには次のコマンドを実行します:

kubectl delete secret db-user-pass-96mffmfh4k

次の項目

4.7 - アプリケーションへのデータ注入

ワークロードを実行するPodの構成とその他のデータを指定します。

4.7.1 - コンテナの環境変数の定義

このページでは、Kubernetes Podでコンテナの環境変数を定義する方法を説明します。

始める前に

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

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

コンテナの環境変数を定義する

Podを作成するとき、そのPodで実行するコンテナに環境変数を設定することができます。環境変数を設定するには、設定ファイルに env または envFrom フィールドを含めます。

この演習では、1つのコンテナを実行するPodを作成します。Podの設定ファイルには、名前 DEMO_GREETING、値 "Hello from the environment"を持つ環境変数が定義されています。Podの設定マニフェストを以下に示します:

apiVersion: v1
kind: Pod
metadata:
  name: envar-demo
  labels:
    purpose: demonstrate-envars
spec:
  containers:
  - name: envar-demo-container
    image: gcr.io/google-samples/node-hello:1.0
    env:
    - name: DEMO_GREETING
      value: "Hello from the environment"
    - name: DEMO_FAREWELL
      value: "Such a sweet sorrow"
  1. マニフェストに基づいてPodを作成します:

    kubectl apply -f https://k8s.io/examples/pods/inject/envars.yaml
    
  2. 実行中のPodを一覧表示します:

    kubectl get pods -l purpose=demonstrate-envars
    

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

    NAME            READY     STATUS    RESTARTS   AGE
    envar-demo      1/1       Running   0          9s
    
  3. Podで実行しているコンテナのシェルを取得します:

    kubectl exec -it envar-demo -- /bin/bash
    
  4. シェルでprintenvコマンドを実行すると、環境変数の一覧が表示されます。

    # コンテナ内のシェルで以下のコマンドを実行します
    printenv
    

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

    NODE_VERSION=4.4.2
    EXAMPLE_SERVICE_PORT_8080_TCP_ADDR=10.3.245.237
    HOSTNAME=envar-demo
    ...
    DEMO_GREETING=Hello from the environment
    DEMO_FAREWELL=Such a sweet sorrow
    
  5. シェルを終了するには、exitと入力します。

設定の中で環境変数を使用する

Podの設定で定義した環境変数は、Podのコンテナに設定したコマンドや引数など、設定の他の場所で使用することができます。以下の設定例では、環境変数GREETINGHONORORIFICNAMEにそれぞれ Warm greetings toThe Most HonorableKubernetesを設定しています。これらの環境変数は、env-print-demoコンテナに渡されるCLI引数で使われます。

apiVersion: v1
kind: Pod
metadata:
  name: print-greeting
spec:
  containers:
  - name: env-print-demo
    image: bash
    env:
    - name: GREETING
      value: "Warm greetings to"
    - name: HONORIFIC
      value: "The Most Honorable"
    - name: NAME
      value: "Kubernetes"
    command: ["echo"]
    args: ["$(GREETING) $(HONORIFIC) $(NAME)"]

作成されると、コンテナ上でecho Warm greetings to The Most Honorable Kubernetesというコマンドが実行されます。

次の項目

4.7.2 - 環境変数によりコンテナにPod情報を共有する

このページでは、Podが内部で実行しているコンテナに自身の情報を共有する方法を説明します。環境変数ではPodのフィールドとコンテナのフィールドを共有することができます。

始める前に

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

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

Downward API

Podとコンテナのフィールドを実行中のコンテナに共有する方法は2つあります:

これら2つの方法を合わせて、Podとコンテナフィールドを共有する方法をDownward APIと呼びます。

Podフィールドを環境変数の値として使用する

この演習では、1つのコンテナを持つPodを作成します。Podの設定ファイルは次のとおりです:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-fieldref
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
          printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
          sleep 10;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
  restartPolicy: Never

設定ファイルには、5つの環境変数があります。envフィールドはEnvVarsの配列です。配列の最初の要素では、環境変数MY_NODE_NAMEの値をPodのspec.nodeNameフィールドから取得することを指定します。同様に、他の環境変数もPodのフィールドから名前を取得します。

Podを作成します:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-pod.yaml

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

kubectl get pods

コンテナのログを表示します:

kubectl logs dapi-envars-fieldref

出力には、選択した環境変数の値が表示されます:

minikube
dapi-envars-fieldref
default
172.17.0.4
default

これらの値がログにある理由を確認するには、設定ファイルのcommandおよびargsフィールドを確認してください。コンテナが起動すると、5つの環境変数の値が標準出力に書き込まれます。これを10秒ごとに繰り返します。

次に、Podで実行しているコンテナへのシェルを取得します:

kubectl exec -it dapi-envars-fieldref -- sh

シェルで環境変数を表示します:

/# printenv

出力は、特定の環境変数にPodフィールドの値が割り当てられていることを示しています:

MY_POD_SERVICE_ACCOUNT=default
...
MY_POD_NAMESPACE=default
MY_POD_IP=172.17.0.4
...
MY_NODE_NAME=minikube
...
MY_POD_NAME=dapi-envars-fieldref

コンテナフィールドを環境変数の値として使用する

前の演習では、環境変数の値としてPodフィールドを使用しました。次の演習では、環境変数の値としてコンテナフィールドを使用します。これは、1つのコンテナを持つPodの設定ファイルです:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-resourcefieldref
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox:1.24
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 10;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.memory
  restartPolicy: Never

設定ファイルには、4つの環境変数があります。envフィールドはEnvVarsの配列です。配列の最初の要素では、環境変数MY_CPU_REQUESTの値をtest-containerという名前のコンテナのrequests.cpuフィールドから取得することを指定します。同様に、他の環境変数もコンテナのフィールドから値を取得します。

Podを作成します:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-container.yaml

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

kubectl get pods

コンテナのログを表示します:

kubectl logs dapi-envars-resourcefieldref

出力には、選択した環境変数の値が表示されます:

1
1
33554432
67108864

次の項目

4.7.3 - Secretsで安全にクレデンシャルを配布する

このページでは、パスワードや暗号化キーなどの機密データをPodに安全に注入する方法を紹介します。

始める前に

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

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

機密データをbase64でエンコードする

ユーザー名my-appとパスワード39528$vdg7Jbの2つの機密データが必要だとします。 まず、base64エンコーディングツールを使って、ユーザ名とパスワードをbase64表現に変換します。 ここでは、手軽に入手できるbase64プログラムを使った例を紹介します:

echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64

出力結果によると、ユーザ名のbase64表現はbXktYXBwで、パスワードのbase64表現はMzk1MjgkdmRnN0piです。

Secretを作成する

以下はユーザー名とパスワードを保持するSecretを作成するために使用できる設定ファイルです:

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
data:
  username: bXktYXBw
  password: Mzk1MjgkdmRnN0pi
  1. Secret を作成する

    kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
    
  2. Secretの情報を取得する

    kubectl get secret test-secret
    

    出力:

    NAME          TYPE      DATA      AGE
    test-secret   Opaque    2         1m
    
  3. Secretの詳細な情報を取得する:

    kubectl describe secret test-secret
    

    出力:

    Name:       test-secret
    Namespace:  default
    Labels:     <none>
    Annotations:    <none>
    
    Type:   Opaque
    
    Data
    ====
    password:   13 bytes
    username:   7 bytes
    

kubectlでSecretを作成する

base64エンコードの手順を省略したい場合は、kubectl create secretコマンドで同じSecretを作成することができます。

例えば:

kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'

先ほどの詳細なアプローチでは 各ステップを明示的に実行し、何が起こっているかを示していますが、kubectl create secretの方が便利です。

Volumeにある機密情報をアクセスするPodを作成する

これはPodの作成に使用できる設定ファイルです。

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
          # name must match the volume name below
          - name: secret-volume
            mountPath: /etc/secret-volume
  # The secret data is exposed to Containers in the Pod through a Volume.
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret
  1. Podを作成する:

    kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
    
  2. PodのSTATUSRunningであるのを確認する:

    kubectl get pod secret-test-pod
    

    出力:

    NAME              READY     STATUS    RESTARTS   AGE
    secret-test-pod   1/1       Running   0          42m
    
  3. Podの中にあるコンテナにシェルを実行する

    kubectl exec -i -t secret-test-pod -- /bin/bash
    
  4. 機密データは /etc/secret-volume にマウントされたボリュームを介してコンテナに公開されます。

    ディレクトリ /etc/secret-volume 中のファイルの一覧を確認する:

    # Run this in the shell inside the container
    ls /etc/secret-volume
    

    passwordusername 2つのファイル名が出力される:

    password username
    
  5. usernamepassword ファイルの中身を表示する:

# Run this in the shell inside the container
echo "$( cat /etc/secret-volume/username )"
echo "$( cat /etc/secret-volume/password )"

出力:

my-app
39528$vdg7Jb

Secretでコンテナの環境変数を定義する

単一のSecretでコンテナの環境変数を定義する

  • Secretの中でkey-valueペアで環境変数を定義する:

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    
  • Secretで定義されたbackend-usernameの値をPodの環境変数SECRET_USERNAMEに割り当てます。

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-single-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
    
  • Podを作成する:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
    
  • コンテナの環境変数SECRET_USERNAMEの中身を表示する:

    kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
    

    出力:

    backend-admin
    

複数のSecretからコンテナの環境変数を定義する

  • 前述の例と同様に、まずSecretを作成します:

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    kubectl create secret generic db-user --from-literal=db-username='db-admin'
    
  • Podの中で環境変数を定義する:

    apiVersion: v1
    kind: Pod
    metadata:
      name: envvars-multiple-secrets
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: BACKEND_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-user
              key: db-username
    
  • Podを作成する:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
    
  • コンテナの環境変数を表示する:

    kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
    

    出力:

    DB_USERNAME=db-admin
    BACKEND_USERNAME=backend-admin
    

Secretのすべてのkey-valueペアを環境変数として設定する

  • 複数のkey-valueペアを含むSecretを作成する

    kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
    
  • envFromを使用してSecretのすべてのデータをコンテナの環境変数として定義します。SecretのキーがPodの環境変数名になります。

    apiVersion: v1
     kind: Pod
     metadata:
       name: envfrom-secret
     spec:
       containers:
       - name: envars-test-container
         image: nginx
         envFrom:
         - secretRef:
             name: test-secret
     
  • Podを作成する:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
    
  • usernamepasswordコンテナの環境変数を表示する

    kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'
    

    出力:

    username: my-app
    password: 39528$vdg7Jb
    

参考文献

次の項目

4.8 - アプリケーションの実行

ステートレスアプリケーションとステートフルアプリケーションの両方を実行および管理します。

4.8.1 - Deploymentを使用してステートレスアプリケーションを実行する

このページでは、Kubernetes Deploymentオブジェクトを使用してアプリケーションを実行する方法を説明します。

目標

  • nginx deploymentを作成します。
  • kubectlを使ってdeploymentに関する情報を一覧表示します。
  • deploymentを更新します。

始める前に

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

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

nginx deploymentの作成と探検

Kubernetes Deploymentオブジェクトを作成することでアプリケーションを実行できます。また、YAMLファイルでDeploymentを記述できます。例えば、このYAMLファイルはnginx:1.14.2 Dockerイメージを実行するデプロイメントを記述しています:

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. YAMLファイルに基づいてDeploymentを作成します:

     kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    
  2. Deploymentに関する情報を表示します:

     kubectl describe deployment nginx-deployment
    

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

     Name:     nginx-deployment
     Namespace:    default
     CreationTimestamp:  Tue, 30 Aug 2016 18:11:37 -0700
     Labels:     app=nginx
     Annotations:    deployment.kubernetes.io/revision=1
     Selector:   app=nginx
     Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
     StrategyType:   RollingUpdate
     MinReadySeconds:  0
     RollingUpdateStrategy:  1 max unavailable, 1 max surge
     Pod Template:
       Labels:       app=nginx
       Containers:
        nginx:
         Image:              nginx:1.14.2
         Port:               80/TCP
         Environment:        <none>
         Mounts:             <none>
       Volumes:              <none>
     Conditions:
       Type          Status  Reason
       ----          ------  ------
       Available     True    MinimumReplicasAvailable
       Progressing   True    NewReplicaSetAvailable
     OldReplicaSets:   <none>
     NewReplicaSet:    nginx-deployment-1771418926 (2/2 replicas created)
     No events.
    
  3. Deploymentによって作成されたPodを一覧表示します:

     kubectl get pods -l app=nginx
    

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

     NAME                                READY     STATUS    RESTARTS   AGE
     nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
     nginx-deployment-1771418926-r18az   1/1       Running   0          16h
    
  4. Podに関する情報を表示します:

     kubectl describe pod <pod-name>
    

    ここで<pod-name>はPodの1つの名前を指定します。

Deploymentの更新

新しいYAMLファイルを適用してDeploymentを更新できます。このYAMLファイルは、Deploymentを更新してnginx 1.16.1を使用するように指定しています。

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # Update the version of nginx from 1.14.2 to 1.16.1
        ports:
        - containerPort: 80
  1. 新しいYAMLファイルを適用します:

      kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
    
  2. Deploymentが新しい名前でPodを作成し、古いPodを削除するのを監視します:

      kubectl get pods -l app=nginx
    

レプリカ数を増やすことによるアプリケーションのスケール

新しいYAMLファイルを適用することで、Deployment内のPodの数を増やすことができます。このYAMLファイルはreplicasを4に設定します。これはDeploymentが4つのPodを持つべきであることを指定します:

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # Update the replicas from 2 to 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80
  1. 新しいYAMLファイルを適用します:

     kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
    
  2. Deploymentに4つのPodがあることを確認します:

     kubectl get pods -l app=nginx
    

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

     NAME                               READY     STATUS    RESTARTS   AGE
     nginx-deployment-148880595-4zdqq   1/1       Running   0          25s
     nginx-deployment-148880595-6zgi1   1/1       Running   0          25s
     nginx-deployment-148880595-fxcez   1/1       Running   0          2m
     nginx-deployment-148880595-rwovn   1/1       Running   0          2m
    

Deploymentの削除

Deploymentを名前を指定して削除します:

kubectl delete deployment nginx-deployment

ReplicationControllers -- 昔のやり方

複製アプリケーションを作成するための好ましい方法はDeploymentを使用することです。そして、DeploymentはReplicaSetを使用します。 DeploymentとReplicaSetがKubernetesに追加される前は、ReplicationControllerを使用して複製アプリケーションを構成していました。

次の項目

4.8.2 - 単一レプリカのステートフルアプリケーションを実行する

このページでは、PersistentVolumeとDeploymentを使用して、Kubernetesで単一レプリカのステートフルアプリケーションを実行する方法を説明します。アプリケーションはMySQLです。

目標

  • 自身の環境のディスクを参照するPersistentVolumeを作成します。
  • MySQLのDeploymentを作成します。
  • MySQLをDNS名でクラスター内の他のPodに公開します。

始める前に

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

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

  • ここで使用されているPersistentVolumeClaimsの要件を満たすには、デフォルトのStorageClassを使用して動的PersistentVolumeプロビジョナーを作成するか、PersistentVolumesを静的にプロビジョニングする必要があります。

MySQLをデプロイする

Kubernetes Deploymentを作成し、PersistentVolumeClaimを使用して既存のPersistentVolumeに接続することで、ステートフルアプリケーションを実行できます。 たとえば、以下のYAMLファイルはMySQLを実行し、PersistentVolumeClaimを参照するDeploymentを記述しています。 このファイルは/var/lib/mysqlのボリュームマウントを定義してから、20Gのボリュームを要求するPersistentVolumeClaimを作成します。 この要求は、要件を満たす既存のボリューム、または動的プロビジョナーによって満たされます。

注:パスワードはYAMLファイル内に定義されており、これは安全ではありません。安全な解決策についてはKubernetes Secretを参照してください 。

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  1. YAMLファイルに記述されたPVとPVCをデプロイします。

     kubectl create -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
    
  2. YAMLファイルの内容をデプロイします。

     kubectl create -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
    
  3. 作成したDeploymentの情報を表示します。

     kubectl describe deployment mysql
    
     Name:                 mysql
     Namespace:            default
     CreationTimestamp:    Tue, 01 Nov 2016 11:18:45 -0700
     Labels:               app=mysql
     Annotations:          deployment.kubernetes.io/revision=1
     Selector:             app=mysql
     Replicas:             1 desired | 1 updated | 1 total | 0 available | 1 unavailable
     StrategyType:         Recreate
     MinReadySeconds:      0
     Pod Template:
       Labels:       app=mysql
       Containers:
        mysql:
         Image:      mysql:5.6
         Port:       3306/TCP
         Environment:
           MYSQL_ROOT_PASSWORD:      password
         Mounts:
           /var/lib/mysql from mysql-persistent-storage (rw)
       Volumes:
        mysql-persistent-storage:
         Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
         ClaimName:  mysql-pv-claim
         ReadOnly:   false
     Conditions:
       Type          Status  Reason
       ----          ------  ------
       Available     False   MinimumReplicasUnavailable
       Progressing   True    ReplicaSetUpdated
     OldReplicaSets:       <none>
     NewReplicaSet:        mysql-63082529 (1/1 replicas created)
     Events:
       FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
       ---------    --------    -----    ----                -------------    --------    ------            -------
       33s          33s         1        {deployment-controller }             Normal      ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
    
  4. Deploymentによって作成されたPodを一覧表示します。

     kubectl get pods -l app=mysql
    
     NAME                   READY     STATUS    RESTARTS   AGE
     mysql-63082529-2z3ki   1/1       Running   0          3m
    
  5. PersistentVolumeClaimを確認します。

     kubectl describe pvc mysql-pv-claim
    
     Name:         mysql-pv-claim
     Namespace:    default
     StorageClass:
     Status:       Bound
     Volume:       mysql-pv-volume
     Labels:       <none>
     Annotations:    pv.kubernetes.io/bind-completed=yes
                     pv.kubernetes.io/bound-by-controller=yes
     Capacity:     20Gi
     Access Modes: RWO
     Events:       <none>
    

MySQLインスタンスにアクセスする

前述のYAMLファイルは、クラスター内の他のPodがデータベースにアクセスできるようにするServiceを作成します。 ServiceのオプションでclusterIP: Noneを指定すると、ServiceのDNS名がPodのIPアドレスに直接解決されます。 このオプションは、ServiceのバックエンドのPodが1つのみであり、Podの数を増やす予定がない場合に適しています。

MySQLクライアントを実行してサーバーに接続します。

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

このコマンドは、クラスター内にMySQLクライアントを実行する新しいPodを作成し、Serviceを通じてMySQLサーバーに接続します。 接続できれば、ステートフルなMySQLデータベースが稼働していることが確認できます。

Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

mysql>

アップデート

イメージまたはDeploymentの他の部分は、kubectl applyコマンドを使用して通常どおりに更新できます。 ステートフルアプリケーションに固有のいくつかの注意事項を以下に記載します。

  • アプリケーションをスケールしないでください。このセットアップは単一レプリカのアプリケーション専用です。 下層にあるPersistentVolumeは1つのPodにしかマウントできません。 クラスター化されたステートフルアプリケーションについては、StatefulSetのドキュメントを参照してください。
  • Deploymentを定義するYAMLファイルではstrategy: type: Recreateを使用して下さい。 この設定はKubernetesにローリングアップデートを使用 しない ように指示します。 同時に複数のPodを実行することはできないため、ローリングアップデートは使用できません。 Recreate戦略は、更新された設定で新しいPodを作成する前に、最初のPodを停止します。

Deploymentの削除

名前を指定してデプロイしたオブジェクトを削除します。

kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume

PersistentVolumeを手動でプロビジョニングした場合は、PersistentVolumeを手動で削除し、また、下層にあるリソースも解放する必要があります。 動的プロビジョニング機能を使用した場合は、PersistentVolumeClaimを削除すれば、自動的にPersistentVolumeも削除されます。 一部の動的プロビジョナー(EBSやPDなど)は、PersistentVolumeを削除すると同時に下層にあるリソースも解放します。

次の項目

4.8.3 - レプリカを持つステートフルアプリケーションを実行する

このページでは、StatefulSet コントローラーを使用して、レプリカを持つステートフルアプリケーションを実行する方法を説明します。 ここでの例は、非同期レプリケーションを行う複数のスレーブを持つ、単一マスターのMySQLです。

この例は本番環境向けの構成ではないことに注意してください。 具体的には、MySQLの設定が安全ではないデフォルトのままとなっています。 これはKubernetesでステートフルアプリケーションを実行するための一般的なパターンに焦点を当てるためです。

始める前に

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

    バージョンを確認するには次のコマンドを実行してください: kubectl version.
  • ここで使用されているPersistentVolumeClaimsの要件を満たすには、デフォルトのStorageClassを使用して動的PersistentVolumeプロビジョナーを作成するか、PersistentVolumesを静的にプロビジョニングする必要があります。

  • このチュートリアルは、あなたがPersistentVolumeStatefulSet、 さらにはPodServiceConfigMapなどの 他のコアな概念に精通していることを前提としています。
  • MySQLに関する知識は記事の理解に役立ちますが、 このチュートリアルは他のシステムにも役立つ一般的なパターンを提示することを目的としています。

目標

  • StatefulSetコントローラーを使用して、レプリカを持つMySQLトポロジーをデプロイします。
  • MySQLクライアントトラフィックを送信します。
  • ダウンタイムに対する耐性を観察します。
  • StatefulSetをスケールアップおよびスケールダウンします。

MySQLをデプロイする

このMySQLのデプロイの例は、1つのConfigMap、2つのService、および1つのStatefulSetから構成されます。

ConfigMap

次のYAML設定ファイルからConfigMapを作成します。

kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
data:
  primary.cnf: |
    # Apply this config only on the primary.
    [mysqld]
    log-bin    
  replica.cnf: |
    # Apply this config only on replicas.
    [mysqld]
    super-read-only    

このConfigMapは、MySQLマスターとスレーブの設定を独立して制御するために、 それぞれのmy.cnfを上書きする内容を提供します。 この場合、マスターはスレーブにレプリケーションログを提供するようにし、 スレーブはレプリケーション以外の書き込みを拒否するようにします。

ConfigMap自体に特別なことはありませんが、ConfigMapの各部分は異なるPodに適用されます。 各Podは、StatefulSetコントローラーから提供される情報に基づいて、 初期化時にConfigMapのどの部分を見るかを決定します。

Services

以下のYAML設定ファイルからServiceを作成します。

kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml
# Headless service for stable DNS entries of StatefulSet members.
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# Client service for connecting to any MySQL instance for reads.
# For writes, you must instead connect to the master: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql

ヘッドレスサービスは、StatefulSetコントローラーが StatefulSetの一部であるPodごとに作成するDNSエントリーのベースエントリーを提供します。 この例ではヘッドレスサービスの名前はmysqlなので、同じKubernetesクラスターの 同じ名前空間内の他のPodは、<pod-name>.mysqlを名前解決することでPodにアクセスできます。

mysql-readと呼ばれるクライアントサービスは、独自のクラスターIPを持つ通常のServiceであり、 Ready状態のすべてのMySQL Podに接続を分散します。 Serviceのエンドポイントには、MySQLマスターとすべてのスレーブが含まれる可能性があります。

読み込みクエリーのみが、負荷分散されるクライアントサービスを使用できることに注意してください。 MySQLマスターは1つしかいないため、クライアントが書き込みを実行するためには、 (ヘッドレスサービス内のDNSエントリーを介して)MySQLのマスターPodに直接接続する必要があります。

StatefulSet

最後に、次のYAML設定ファイルからStatefulSetを作成します。

kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Generate mysql server-id from pod ordinal index.
          [[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files from config-map to emptyDir.
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/master.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/slave.cnf /mnt/conf.d/
          fi          
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Skip the clone if data already exists.
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # Skip the clone on master (ordinal index 0).
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # Clone data from previous peer.
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # Prepare the backup.
          xtrabackup --prepare --target-dir=/var/lib/mysql          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # Check we can execute queries over TCP (skip-networking is off).
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # Determine binlog position of cloned data, if any.
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup already generated a partial "CHANGE MASTER TO" query
            # because we're cloning from an existing slave. (Need to remove the tailing semicolon!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # Ignore xtrabackup_binlog_info in this case (it's useless).
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # We're cloning directly from master. Parse binlog position.
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm -f xtrabackup_binlog_info xtrabackup_slave_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi

          # Check if we need to complete a clone by starting replication.
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done

            echo "Initializing replication from clone position"
            mysql -h 127.0.0.1 \
                  -e "$(<change_master_to.sql.in), \
                          MASTER_HOST='mysql-0.mysql', \
                          MASTER_USER='root', \
                          MASTER_PASSWORD='', \
                          MASTER_CONNECT_RETRY=10; \
                        START SLAVE;" || exit 1
            # In case of container restart, attempt this at-most-once.
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # Start a server to send backups when requested by peers.
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

次のコマンドを実行して起動の進行状況を確認できます。

kubectl get pods -l app=mysql --watch

しばらくすると、3つのPodすべてがRunning状態になるはずです。

NAME      READY     STATUS    RESTARTS   AGE
mysql-0   2/2       Running   0          2m
mysql-1   2/2       Running   0          1m
mysql-2   2/2       Running   0          1m

Ctrl+Cを押してウォッチをキャンセルします。 起動が進行しない場合は、始める前にで説明されているように、 PersistentVolumeの動的プロビジョニング機能が有効になっていることを確認してください。

このマニフェストでは、StatefulSetの一部としてステートフルなPodを管理するためにさまざまな手法を使用しています。 次のセクションでは、これらの手法のいくつかに焦点を当て、StatefulSetがPodを作成するときに何が起こるかを説明します。

ステートフルなPodの初期化を理解する

StatefulSetコントローラーは、序数インデックスの順にPodを一度に1つずつ起動します。 各PodがReady状態を報告するまで待機してから、その次のPodの起動が開始されます。

さらに、コントローラーは各Podに <statefulset-name>-<ordinal-index>という形式の一意で不変の名前を割り当てます。 この例の場合、Podの名前はmysql-0mysql-1、そしてmysql-2となります。

上記のStatefulSetマニフェスト内のPodテンプレートは、これらのプロパティーを利用して、 MySQLレプリケーションの起動を順序正しく実行します。

構成を生成する

Podスペック内のコンテナを起動する前に、Podは最初に 初期化コンテナを定義された順序で実行します。

最初の初期化コンテナはinit-mysqlという名前で、序数インデックスに基づいて特別なMySQL設定ファイルを生成します。

スクリプトは、hostnameコマンドによって返されるPod名の末尾から抽出することによって、自身の序数インデックスを特定します。 それから、序数を(予約された値を避けるために数値オフセット付きで)MySQLのconf.dディレクトリーのserver-id.cnfというファイルに保存します。 これは、StatefulSetコントローラーによって提供される一意で不変のIDを、同じ特性を必要とするMySQLサーバーIDの領域に変換します。

さらに、init-mysqlコンテナ内のスクリプトは、master.cnfまたはslave.cnfのいずれかを、 ConfigMapから内容をconf.dにコピーすることによって適用します。 このトポロジー例は単一のMySQLマスターと任意の数のスレーブで構成されているため、 スクリプトは単に序数の0がマスターになるように、それ以外のすべてがスレーブになるように割り当てます。 StatefulSetコントローラーによる デプロイ順序の保証と組み合わせると、 スレーブが作成される前にMySQLマスターがReady状態になるため、スレーブはレプリケーションを開始できます。

既存データをクローンする

一般に、新しいPodがセットにスレーブとして参加するときは、 MySQLマスターにはすでにデータがあるかもしれないと想定する必要があります。 また、レプリケーションログが期間の先頭まで全て揃っていない場合も想定する必要があります。 これらの控えめな仮定は、実行中のStatefulSetのサイズを初期サイズに固定するのではなく、 時間の経過とともにスケールアップまたはスケールダウンできるようにするために重要です。

2番目の初期化コンテナはclone-mysqlという名前で、スレーブPodが空のPersistentVolumeで最初に起動したときに、 クローン操作を実行します。 つまり、実行中の別のPodから既存のデータをすべてコピーするので、 そのローカル状態はマスターからレプリケーションを開始するのに十分な一貫性があります。

MySQL自体はこれを行うためのメカニズムを提供していないため、この例ではPercona XtraBackupという人気のあるオープンソースツールを使用しています。 クローンの実行中は、ソースとなるMySQLサーバーのパフォーマンスが低下する可能性があります。 MySQLマスターへの影響を最小限に抑えるために、スクリプトは各Podに序数インデックスが自分より1低いPodからクローンするように指示します。 StatefulSetコントローラーは、N+1のPodを開始する前には必ずNのPodがReady状態であることを保証するので、この方法が機能します。

レプリケーションを開始する

初期化コンテナが正常に完了すると、通常のコンテナが実行されます。 MySQLのPodは実際にmysqldサーバーを実行するmysqlコンテナと、 サイドカー として機能するxtrabackupコンテナから成ります。

xtrabackupサイドカーはクローンされたデータファイルを見て、 スレーブ上でMySQLレプリケーションを初期化する必要があるかどうかを決定します。 もし必要がある場合、mysqldが準備できるのを待ってから、 XtraBackupクローンファイルから抽出されたレプリケーションパラメーターでCHANGE MASTER TOSTART SLAVEコマンドを実行します。

スレーブがレプリケーションを開始すると、スレーブはMySQLマスターを記憶し、 サーバーが再起動した場合または接続が停止した場合に、自動的に再接続します。 また、スレーブはその不変のDNS名(mysql-0.mysql)でマスターを探すため、 再スケジュールされたために新しいPod IPを取得したとしても、自動的にマスターを見つけます。

最後に、レプリケーションを開始した後、xtrabackupコンテナはデータのクローンを要求する他のPodからの接続を待ち受けます。 StatefulSetがスケールアップした場合や、次のPodがPersistentVolumeClaimを失ってクローンをやり直す必要がある場合に備えて、 このサーバーは無期限に起動したままになります。

クライアントトラフィックを送信する

テストクエリーをMySQLマスター(ホスト名 mysql-0.mysql)に送信するには、 mysql:5.7イメージを使って一時的なコンテナを実行し、mysqlクライアントバイナリを実行します。

kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF

Ready状態を報告したいずれかのサーバーにテストクエリーを送信するには、ホスト名mysql-readを使用します。

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-read -e "SELECT * FROM test.messages"

次のような出力が得られるはずです。

Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

mysql-readサービスがサーバー間で接続を分散させることを実証するために、 ループでSELECT @@server_idを実行することができます。

kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"

接続の試行ごとに異なるエンドポイントが選択される可能性があるため、 報告される@@server_idはランダムに変更されるはずです。

+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         101 | 2006-01-02 15:04:07 |
+-------------+---------------------+

ループを止めたいときはCtrl+Cを押すことができますが、別のウィンドウで実行したままにしておくことで、 次の手順の効果を確認できます。

PodとNodeのダウンタイムをシミュレーションする

単一のサーバーではなくスレーブのプールから読み取りを行うことによって可用性が高まっていることを実証するため、 Podを強制的にReadyではない状態にする間、上記のSELECT @@server_idループを実行したままにしてください。

Readiness Probeを壊す

mysqlコンテナに対する readiness probe は、mysql -h 127.0.0.1 -e 'SELECT 1'コマンドを実行することで、サーバーが起動していてクエリーが実行できることを確認します。

このreadiness probeを失敗させる1つの方法は、そのコマンドを壊すことです。

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off

ここでは、mysql-2 Podの実際のコンテナのファイルシステムにアクセスし、 mysqlコマンドの名前を変更してreadiness probeがコマンドを見つけられないようにしています。 数秒後、Podはそのコンテナの1つがReadyではないと報告するはずです。以下を実行して確認できます。

kubectl get pod mysql-2

READY列の1/2を見てください。

NAME      READY     STATUS    RESTARTS   AGE
mysql-2   1/2       Running   0          3m

この時点で、SELECT @@server_idループは実行され続け、しかしもう102が報告されないことが確認できるはずです。 init-mysqlスクリプトがserver-id100+$ordinalとして定義したことを思い出して下さい。 そのため、サーバーID102はPodのmysql-2に対応します。

それではPodを修復しましょう。すると数秒後にループ出力に再び現れるはずです。

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql

Podを削除する

StatefulSetは、Podが削除された場合にPodを再作成します。 これはReplicaSetがステートレスなPodに対して行うのと同様です。

kubectl delete pod mysql-2

StatefulSetコントローラーはmysql-2 Podがもう存在しないことに気付き、 同じ名前で同じPersistentVolumeClaimにリンクされた新しいPodを作成します。 サーバーID102がしばらくの間ループ出力から消えて、また元に戻るのが確認できるはずです。

ノードをdrainする

Kubernetesクラスターに複数のノードがある場合は、 drainを発行して ノードのダウンタイム(例えばノードのアップグレード時など)をシミュレートできます。

まず、あるMySQL Podがどのノード上にいるかを確認します。

kubectl get pod mysql-2 -o wide

ノード名が最後の列に表示されるはずです。

NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-2   2/2       Running   0          15m       10.244.5.27   kubernetes-minion-group-9l2t

その後、次のコマンドを実行してノードをdrainします。 これにより、新しいPodがそのノードにスケジュールされないようにcordonされ、そして既存のPodは強制退去されます。 <node-name>は前のステップで確認したノードの名前に置き換えてください。

この操作はノード上の他のアプリケーションに影響を与える可能性があるため、 テストクラスターでのみこの操作を実行するのが最善です。

kubectl drain <node-name> --force --delete-local-data --ignore-daemonsets

Podが別のノードに再スケジュールされる様子を確認しましょう。

kubectl get pod mysql-2 -o wide --watch

次のような出力が見られるはずです。

NAME      READY   STATUS          RESTARTS   AGE       IP            NODE
mysql-2   2/2     Terminating     0          15m       10.244.1.56   kubernetes-minion-group-9l2t
[...]
mysql-2   0/2     Pending         0          0s        <none>        kubernetes-minion-group-fjlm
mysql-2   0/2     Init:0/2        0          0s        <none>        kubernetes-minion-group-fjlm
mysql-2   0/2     Init:1/2        0          20s       10.244.5.32   kubernetes-minion-group-fjlm
mysql-2   0/2     PodInitializing 0          21s       10.244.5.32   kubernetes-minion-group-fjlm
mysql-2   1/2     Running         0          22s       10.244.5.32   kubernetes-minion-group-fjlm
mysql-2   2/2     Running         0          30s       10.244.5.32   kubernetes-minion-group-fjlm

また、サーバーID102SELECT @@server_idループの出力からしばらくの消えて、 そして戻ることが確認できるはずです。

それでは、ノードをuncordonして正常な状態に戻しましょう。

kubectl uncordon <node-name>

スレーブの数をスケーリングする

MySQLレプリケーションでは、スレーブを追加することで読み取りクエリーのキャパシティーをスケールできます。 StatefulSetを使用している場合、単一のコマンドでこれを実行できます。

kubectl scale statefulset mysql  --replicas=5

次のコマンドを実行して、新しいPodが起動してくるのを確認します。

kubectl get pods -l app=mysql --watch

新しいPodが起動すると、サーバーID103104SELECT @@server_idループの出力に現れます。

また、これらの新しいサーバーが、これらのサーバーが存在する前に追加したデータを持っていることを確認することもできます。

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

元の状態へのスケールダウンもシームレスに可能です。

kubectl scale statefulset mysql --replicas=3

ただし、スケールアップすると新しいPersistentVolumeClaimが自動的に作成されますが、 スケールダウンしてもこれらのPVCは自動的には削除されないことに注意して下さい。 このため、初期化されたPVCをそのまま置いておいくことで再スケールアップを速くしたり、 PVを削除する前にデータを抽出するといった選択が可能になります。

次のコマンドを実行してこのことを確認できます。

kubectl get pvc -l app=mysql

StatefulSetを3にスケールダウンしたにもかかわらず、5つのPVCすべてがまだ存在しています。

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
data-mysql-0   Bound     pvc-8acbf5dc-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-1   Bound     pvc-8ad39820-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-2   Bound     pvc-8ad69a6d-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-3   Bound     pvc-50043c45-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
data-mysql-4   Bound     pvc-500a9957-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m

余分なPVCを再利用するつもりがないのであれば、削除することができます。

kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4

クリーンアップ

  1. SELECT @@server_idループを実行している端末でCtrl+Cを押すか、 別の端末から次のコマンドを実行して、ループをキャンセルします。

    kubectl delete pod mysql-client-loop --now
    
  2. StatefulSetを削除します。これによってPodの終了も開始されます。

    kubectl delete statefulset mysql
    
  3. Podが消えたことを確認します。 Podが終了処理が完了するのには少し時間がかかるかもしれません。

    kubectl get pods -l app=mysql
    

    上記のコマンドから以下の出力が戻れば、Podが終了したことがわかります。

    No resources found.
    
  4. ConfigMap、Services、およびPersistentVolumeClaimを削除します。

    kubectl delete configmap,service,pvc -l app=mysql
    
  5. PersistentVolumeを手動でプロビジョニングした場合は、それらを手動で削除し、 また、下層にあるリソースも解放する必要があります。 動的プロビジョニング機能を使用した場合は、PersistentVolumeClaimを削除すれば、自動的にPersistentVolumeも削除されます。 一部の動的プロビジョナー(EBSやPDなど)は、PersistentVolumeを削除すると同時に下層にあるリソースも解放します。

次の項目

  • その他のステートフルアプリケーションの例は、Helm Charts repositoryを見てください。

4.8.4 - StatefulSetのスケール

このタスクは、StatefulSetをスケールする方法を示します。StatefulSetをスケーリングするとは、レプリカの数を増減することです。

始める前に

  • StatefulSetはKubernetesバージョン1.5以降でのみ利用可能です。 Kubernetesのバージョンを確認するには、kubectl versionを実行してください。

  • すべてのステートフルアプリケーションがうまくスケールできるわけではありません。StatefulSetがスケールするかどうかわからない場合は、StatefulSetの概念またはStatefulSetのチュートリアルを参照してください。

  • ステートフルアプリケーションクラスターが完全に健全であると確信できる場合にのみ、スケーリングを実行してください。

StatefulSetのスケール

kubectlを使用したStatefulSetのスケール

まず、スケールしたいStatefulSetを見つけます。

kubectl get statefulsets <stateful-set-name>

StatefulSetのレプリカ数を変更します:

kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>

StatefulSetのインプレースアップデート

コマンドライン上でレプリカ数を変更する代わりに、StatefulSetにインプレースアップデートが可能です。

StatefulSetが最初に kubectl applyで作成されたのなら、StatefulSetマニフェストの.spec.replicasを更新してから、kubectl applyを実行します:

kubectl apply -f <stateful-set-file-updated>

そうでなければ、kubectl editでそのフィールドを編集してください:

kubectl edit statefulsets <stateful-set-name>

あるいはkubectl patchを使ってください:

kubectl patch statefulsets <stateful-set-name> -p '{"spec":{"replicas":<new-replicas>}}'

トラブルシューティング

スケールダウンがうまくいかない

管理するステートフルPodのいずれかが異常である場合、StatefulSetをスケールダウンすることはできません。それらのステートフルPodが実行され準備ができた後にのみ、スケールダウンが行われます。

spec.replicas > 1の場合、Kubernetesは不健康なPodの理由を判断できません。それは、永続的な障害または一時的な障害の結果である可能性があります。一時的な障害は、アップグレードまたはメンテナンスに必要な再起動によって発生する可能性があります。

永続的な障害が原因でPodが正常でない場合、障害を修正せずにスケーリングすると、StatefulSetメンバーシップが正しく機能するために必要な特定の最小レプリカ数を下回る状態になる可能性があります。これにより、StatefulSetが利用できなくなる可能性があります。

一時的な障害によってPodが正常でなくなり、Podが再び使用可能になる可能性がある場合は、一時的なエラーがスケールアップまたはスケールダウン操作の妨げになる可能性があります。一部の分散データベースでは、ノードが同時に参加および脱退するときに問題があります。このような場合は、アプリケーションレベルでスケーリング操作を考えることをお勧めします。また、ステートフルアプリケーションクラスターが完全に健全であることが確実な場合にのみスケーリングを実行してください。

次の項目

4.8.5 - StatefulSetの削除

このタスクでは、StatefulSetを削除する方法を説明します。

始める前に

  • このタスクは、クラスター上で、StatefulSetで表現されるアプリケーションが実行されていることを前提としています。

StatefulSetの削除

Kubernetesで他のリソースを削除するのと同じ方法でStatefulSetを削除することができます。つまり、kubectl deleteコマンドを使い、StatefulSetをファイルまたは名前で指定します。

kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset-name>

StatefulSet自体が削除された後で、関連するヘッドレスサービスを個別に削除する必要があるかもしれません。

kubectl delete service <service-name>

kubectlを使ってStatefulSetを削除すると0にスケールダウンされ、すべてのPodが削除されます。PodではなくStatefulSetだけを削除したい場合は、--cascade=orphanを使用してください。

kubectl delete -f <file.yaml> --cascade=orphan

--cascade=orphankubectl deleteに渡すことで、StatefulSetオブジェクト自身が削除された後でも、StatefulSetによって管理されていたPodは残ります。Podにapp.kubernetes.io/name=MyAppというラベルが付いている場合は、次のようにして削除できます:

kubectl delete pods -l app.kubernetes.io/name=MyApp

永続ボリューム

StatefulSet内のPodを削除しても、関連付けられているボリュームは削除されません。これは、削除する前にボリュームからデータをコピーする機会があることを保証するためです。Podが終了した後にPVCを削除すると、ストレージクラスと再利用ポリシーによっては、背後にある永続ボリュームの削除がトリガーされることがあります。決してクレーム削除後にボリュームにアクセスできると想定しないでください。

StatefulSetの完全削除

関連付けられたPodを含むStatefulSet内のすべてのものを単純に削除するには、次のような一連のコマンドを実行します:

grace=$(kubectl get pods <stateful-set-pod> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app.kubernetes.io/name=MyApp
sleep $grace
kubectl delete pvc -l app.kubernetes.io/name=MyApp

上の例では、Podはapp.kubernetes.io/name=MyAppというラベルを持っています。必要に応じてご利用のラベルに置き換えてください。

StatefulSet Podの強制削除

StatefulSet内の一部のPodが長期間TerminatingまたはUnknown状態のままになっていることが判明した場合は、手動でapiserverからPodを強制的に削除する必要があります。これは潜在的に危険な作業です。詳細はStatefulSet Podの強制削除を参照してください。

次の項目

StatefulSet Podの強制削除の詳細。

4.8.6 - StatefulSet Podの強制削除

このページでは、StatefulSetの一部であるPodを削除する方法と、削除する際に考慮すべき事項について説明します。

始める前に

  • これはかなり高度なタスクであり、StatefulSetに固有のいくつかの特性に反する可能性があります。
  • 先に進む前に、以下に列挙されている考慮事項をよく理解してください。

StatefulSetに関する考慮事項

StatefulSetの通常の操作では、StatefulSet Podを強制的に削除する必要はまったくありません。StatefulSetコントローラーは、StatefulSetのメンバーの作成、スケール、削除を行います。それは序数0からN-1までの指定された数のPodが生きていて準備ができていることを保証しようとします。StatefulSetは、クラスター内で実行されている特定のIDを持つ最大1つのPodがいつでも存在することを保証します。これは、StatefulSetによって提供される最大1つのセマンティクスと呼ばれます。

手動による強制削除は、StatefulSetに固有の最大1つのセマンティクスに違反する可能性があるため、慎重に行う必要があります。StatefulSetを使用して、安定したネットワークIDと安定した記憶域を必要とする分散型およびクラスター型アプリケーションを実行できます。これらのアプリケーションは、固定IDを持つ固定数のメンバーのアンサンブルに依存する構成を持つことがよくあります。同じIDを持つ複数のメンバーを持つことは悲惨なことになり、データの損失につながる可能性があります(例:定足数ベースのシステムでのスプリットブレインシナリオ)。

Podの削除

次のコマンドで正常なPod削除を実行できます:

kubectl delete pods <pod>

上記がグレースフルターミネーションにつながるためには、pod.Spec.TerminationGracePeriodSecondsに0を指定してはいけませんpod.Spec.TerminationGracePeriodSecondsを0秒に設定することは安全ではなく、StatefulSet Podには強くお勧めできません。グレースフル削除は安全で、kubeletがapiserverから名前を削除する前にPodが適切にシャットダウンすることを保証します。

Kubernetes(バージョン1.5以降)は、Nodeにアクセスできないという理由だけでPodを削除しません。到達不能なNodeで実行されているPodは、タイムアウトの後にTerminatingまたはUnknown状態になります。到達不能なNode上のPodをユーザーが適切に削除しようとすると、Podはこれらの状態に入ることもあります。そのような状態のPodをapiserverから削除することができる唯一の方法は以下の通りです:

  • (ユーザーまたはNode Controllerによって)Nodeオブジェクトが削除されます。
  • 応答していないNodeのkubeletが応答を開始し、Podを終了してapiserverからエントリーを削除します。
  • ユーザーによりPodを強制削除します。

推奨されるベストプラクティスは、1番目または2番目のアプローチを使用することです。Nodeが死んでいることが確認された(例えば、ネットワークから恒久的に切断された、電源が切られたなど)場合、Nodeオブジェクトを削除します。Nodeがネットワークパーティションに苦しんでいる場合は、これを解決するか、解決するのを待ちます。パーティションが回復すると、kubeletはPodの削除を完了し、apiserverでその名前を解放します。

通常、PodがNode上で実行されなくなるか、管理者によってそのNodeが削除されると、システムは削除を完了します。あなたはPodを強制的に削除することでこれを無効にすることができます。

強制削除

強制削除はPodが終了したことをkubeletから確認するまで待ちません。強制削除がPodの削除に成功したかどうかに関係なく、apiserverから名前をすぐに解放します。これにより、StatefulSetコントローラーは、同じIDを持つ交換Podを作成できます。これは、まだ実行中のPodの複製につながる可能性があり、そのPodがまだStatefulSetの他のメンバーと通信できる場合、StatefulSetが保証するように設計されている最大1つのセマンティクスに違反します。

StatefulSetのPodを強制的に削除するということは、問題のPodがStatefulSet内の他のPodと再び接触することはなく、代わりのPodを作成するために名前が安全に解放されることを意味します。

バージョン1.5以上のkubectlを使用してPodを強制的に削除する場合は、次の手順を実行します:

kubectl delete pods <pod> --grace-period=0 --force

バージョン1.4以下のkubectlを使用している場合、--forceオプションを省略する必要があります:

kubectl delete pods <pod> --grace-period=0

これらのコマンドを実行した後でもPodがUnknown状態のままになっている場合は、次のコマンドを使用してPodをクラスターから削除します:

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

StatefulSet Podの強制削除は、常に慎重に、関連するリスクを完全に把握して実行してください。

次の項目

StatefulSetのデバッグの詳細

4.8.7 - 水平Pod自動スケーリング

Kubernetesでは、 HorizontalPodAutoscaler は自動的にワークロードリソース(DeploymentStatefulSetなど)を更新し、ワークロードを自動的にスケーリングして需要に合わせることを目指します。

水平スケーリングとは、負荷の増加に対応するために、より多くのPodをデプロイすることを意味します。これは、Kubernetesの場合、既に稼働しているワークロードのPodに対して、より多くのリソース(例:メモリーやCPU)を割り当てることを意味する 垂直 スケーリングとは異なります。

負荷が減少し、Podの数が設定された最小値より多い場合、HorizontalPodAutoscalerはワークロードリソース(Deployment、StatefulSet、または他の類似のリソース)に対してスケールダウンするよう指示します。

水平Pod自動スケーリングは、スケーリングできないオブジェクト(例:DaemonSet)には適用されません。

HorizontalPodAutoscalerは、Kubernetes APIリソースとコントローラーとして実装されています。リソースはコントローラーの動作を決定します。Kubernetesコントロールプレーン内で稼働している水平Pod自動スケーリングコントローラーは、平均CPU利用率、平均メモリー利用率、または指定した任意のカスタムメトリクスなどの観測メトリクスに合わせて、ターゲット(例:Deployment)の理想的なスケールを定期的に調整します。

水平Pod自動スケーリングの使用例のウォークスルーがあります。

HorizontalPodAutoscalerの仕組みは?

graph BT hpa[Horizontal Pod Autoscaler] --> scale[Scale] subgraph rc[RC / Deployment] scale end scale -.-> pod1[Pod 1] scale -.-> pod2[Pod 2] scale -.-> pod3[Pod N] classDef hpa fill:#D5A6BD,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef rc fill:#F9CB9C,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef scale fill:#B6D7A8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef pod fill:#9FC5E8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; class hpa hpa; class rc rc; class scale scale; class pod1,pod2,pod3 pod

図1. HorizontalPodAutoscalerはDeploymentとそのReplicaSetのスケールを制御します。

Kubernetesは水平Pod自動スケーリングを断続的に動作する制御ループとして実装しています(これは連続的なプロセスではありません)。その間隔はkube-controller-manager--horizontal-pod-autoscaler-sync-periodパラメーターで設定します(デフォルトの間隔は15秒です)。

各期間中に1回、コントローラーマネージャーはHorizontalPodAutoscalerの定義のそれぞれに指定されたメトリクスに対するリソース使用率を照会します。コントローラーマネージャーはscaleTargetRefによって定義されたターゲットリソースを見つけ、ターゲットリソースの.spec.selectorラベルに基づいてPodを選択し、リソースメトリクスAPI(Podごとのリソースメトリクスの場合)またはカスタムメトリクスAPI(他のすべてのメトリクスの場合)からメトリクスを取得します。

  • Podごとのリソースメトリクス(CPUなど)の場合、コントローラーはHorizontalPodAutoscalerによってターゲットとされた各PodのリソースメトリクスAPIからメトリクスを取得します。その後、使用率の目標値が設定されている場合、コントローラーは各Pod内のコンテナの同等のリソース要求に対する割合として使用率を算出します。生の値の目標値が設定されている場合、生のメトリクス値が直接使用されます。次に、コントローラーはすべてのターゲットとなるPod間で使用率または生の値(指定されたターゲットのタイプによります)の平均を取り、理想のレプリカ数でスケールするために使用される比率を生成します。

    Podのコンテナの一部に関連するリソース要求が設定されていない場合、PodのCPU利用率は定義されず、オートスケーラーはそのメトリクスに対して何も行動を起こしません。オートスケーリングアルゴリズムの動作についての詳細は、以下のアルゴリズムの詳細をご覧ください。

  • Podごとのカスタムメトリクスについては、コントローラーはPodごとのリソースメトリクスと同様に機能しますが、使用率の値ではなく生の値で動作します。

  • オブジェクトメトリクスと外部メトリクスについては、問題となるオブジェクトを表す単一のメトリクスが取得されます。このメトリクスは目標値と比較され、上記のような比率を生成します。autoscaling/v2 APIバージョンでは、比較を行う前にこの値をPodの数で割ることもできます。

HorizontalPodAutoscalerを使用する一般的な目的は、集約API(metrics.k8s.iocustom.metrics.k8s.io、またはexternal.metrics.k8s.io)からメトリクスを取得するように設定することです。metrics.k8s.io APIは通常、別途起動する必要があるMetrics Serverというアドオンによって提供されます。リソースメトリクスについての詳細は、Metrics Serverをご覧ください。

メトリクスAPIのサポートは、これらの異なるAPIの安定性の保証とサポート状況を説明します。

HorizontalPodAutoscalerコントローラーは、スケーリングをサポートするワークロードリソース(DeploymentやStatefulSetなど)にアクセスします。これらのリソースはそれぞれscaleというサブリソースを持っており、これはレプリカの数を動的に設定し、各々の現在の状態を調べることができるインターフェースを提供します。Kubernetes APIのサブリソースに関する一般的な情報については、Kubernetes API Conceptsをご覧ください。

アルゴリズムの詳細

最も基本的な観点から言えば、HorizontalPodAutoscalerコントローラーは、理想のメトリクス値と現在のメトリクス値との間の比率で動作します:

desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

たとえば、現在のメトリクス値が200mで、理想の値が100mの場合、レプリカの数は倍増します。なぜなら、200.0 / 100.0 == 2.0だからです。現在の値が50mの場合、レプリカの数は半分になります。なぜなら、50.0 / 100.0 == 0.5だからです。コントロールプレーンは、比率が十分に1.0に近い場合(全体的に設定可能な許容範囲内、デフォルトでは0.1)には、任意のスケーリング操作をスキップします。

targetAverageValueまたはtargetAverageUtilizationが指定されている場合、currentMetricValueは、HorizontalPodAutoscalerのスケールターゲット内のすべてのPodで指定されたメトリクスの平均を取ることで計算されます。

許容範囲を確認し、最終的な値を決定する前に、コントロールプレーンは、メトリクスが欠けていないか、また何個のPodがReady状態であるかを考慮します。削除タイムスタンプが設定されているすべてのPod(削除タイムスタンプがあるオブジェクトはシャットダウンまたは削除の途中です)は無視され、失敗したPodはすべて破棄されます。

特定のPodがメトリクスを欠いている場合、それは後で検討するために取っておかれます。メトリクスが欠けているPodは、最終的なスケーリング量の調整に使用されます。

CPUに基づいてスケーリングする場合、任意のPodがまだReadyになっていない(まだ初期化中か、おそらくunhealthy)、またはPodがReadyになる前の最新のメトリクスポイントがある場合、そのPodも取り置かれます。

技術的な制約により、HorizontalPodAutoscalerコントローラーは特定のCPUメトリクスを取り置くかどうかを判断する際に、Podが初めてReadyになる時間を正確に決定することができません。その代わり、Podが起動してから設定可能な短い時間内にReadyに遷移した場合、それを「まだReadyになっていない」とみなします。この値は、--horizontal-pod-autoscaler-initial-readiness-delayフラグで設定し、デフォルトは30秒です。Podが一度Readyになると、起動してから設定可能な長い時間内に発生した場合、それが最初のReadyへの遷移だとみなします。この値は、--horizontal-pod-autoscaler-cpu-initialization-periodフラグで設定し、デフォルトは5分です。

次に、上記で取り置かれたり破棄されたりしていない残りのPodを使用して、currentMetricValue / desiredMetricValueの基本スケール比率が計算されます。

メトリクスが欠けていた場合、コントロールプレーンは平均値をより保守的に再計算し、スケールダウンの場合はそのPodが理想の値の100%を消費していたと仮定し、スケールアップの場合は0%を消費していたと仮定します。これにより、潜在的なスケールの大きさが抑制されます。

さらに、まだReadyになっていないPodが存在し、欠けているメトリクスやまだReadyになっていないPodを考慮せずにワークロードがスケールアップした場合、コントローラーは保守的にまだReadyになっていないPodが理想のメトリクスの0%を消費していると仮定し、スケールアップの大きさをさらに抑制します。

まだReadyになっていないPodと欠けているメトリクスを考慮に入れた後、コントローラーは使用率の比率を再計算します。新しい比率がスケールの方向を逆転させるか、許容範囲内である場合、コントローラーはスケーリング操作を行いません。その他の場合、新しい比率がPodの数の変更を決定するために使用されます。

新しい使用率の比率が使用されたときであっても、平均使用率の元の値は、まだReadyになっていないPodや欠けているメトリクスを考慮せずに、HorizontalPodAutoscalerのステータスを通じて報告されることに注意してください。

HorizontalPodAutoscalerに複数のメトリクスが指定されている場合、この計算は各メトリクスに対して行われ、その後、理想のレプリカ数の最大値が選択されます。これらのメトリクスのいずれかを理想のレプリカ数に変換できない場合(例えば、メトリクスAPIからのメトリクスの取得エラーが原因)、そして取得可能なメトリクスがスケールダウンを提案する場合、スケーリングはスキップされます。これは、1つ以上のメトリクスが現在の値よりも大きなdesiredReplicasを示す場合でも、HPAはまだスケーリングアップ可能であることを意味します。

最後に、HPAがターゲットを減らす直前に、減らす台数の推奨値が記録されます。コントローラーは、設定可能な時間内のすべての推奨値を考慮し、その時間内で最も高い推奨値を選択します。この値は、--horizontal-pod-autoscaler-downscale-stabilizationフラグを使用して設定でき、デフォルトは5分です。これは、スケールダウンが徐々に行われ、急速に変動するメトリクス値の影響を滑らかにすることを意味します。

APIオブジェクト

Horizontal Pod Autoscalerは、Kubernetesのautoscaling APIグループのAPIリソースです。現行の安定バージョンは、メモリーおよびカスタムメトリクスに対するスケーリングのサポートを含むautoscaling/v2 APIバージョンに見つけることができます。autoscaling/v2で導入された新たなフィールドは、autoscaling/v1で作業する際にアノテーションとして保持されます。

HorizontalPodAutoscaler APIオブジェクトを作成するときは、指定された名前が有効なDNSサブドメイン名であることを確認してください。APIオブジェクトについての詳細は、HorizontalPodAutoscaler Objectで見つけることができます。

ワークロードスケールの安定性

HorizontalPodAutoscalerを使用してレプリカ群のスケールを管理する際、評価されるメトリクスの動的な性質により、レプリカの数が頻繁に変動する可能性があります。これは、スラッシング または フラッピング と呼ばれることがあります。これは、サイバネティクス における ヒステリシス の概念に似ています。

ローリングアップデート中の自動スケーリング

Kubernetesでは、Deploymentに対してローリングアップデートを行うことができます。その場合、Deploymentが基礎となるReplicaSetを管理します。Deploymentに自動スケーリングを設定すると、HorizontalPodAutoscalerを単一のDeploymentに結びつけます。HorizontalPodAutoscalerはDeploymentのreplicasフィールドを管理します。Deploymentコントローラーは、ロールアウト時およびその後も適切な数になるように、基礎となるReplicaSetのreplicasを設定する責任があります。

自動スケールされたレプリカ数を持つStatefulSetのローリングアップデートを実行する場合、StatefulSetは直接そのPodのセットを管理します(ReplicaSetのような中間リソースは存在しません)。

リソースメトリクスのサポート

HPAの任意のターゲットは、スケーリングターゲット内のPodのリソース使用状況に基づいてスケールすることができます。Podの仕様を定義する際には、cpumemoryなどのリソース要求を指定する必要があります。これはリソースの使用状況を決定するために使用され、HPAコントローラーがターゲットをスケールアップまたはスケールダウンするために使用されます。リソース使用状況に基づくスケーリングを使用するには、以下のようなメトリクスソースを指定します:

type: Resource
resource:
  name: cpu
  target:
    type: Utilization
    averageUtilization: 60

このメトリクスを使用すると、HPAコントローラーはスケーリングターゲット内のPodの平均使用率を60%に保ちます。使用率は、Podの要求したリソースに対する現在のリソース使用量の比率です。使用率がどのように計算され、平均化されるかの詳細については、アルゴリズムを参照してください。

コンテナリソースメトリクス

FEATURE STATE: Kubernetes v1.27 [beta]

HorizontalPodAutoscaler APIは、コンテナメトリクスソースもサポートしています。これは、ターゲットリソースをスケールするために、HPAが一連のPod内の個々のコンテナのリソース使用状況を追跡できるようにするものです。これにより、特定のPodで最も重要なコンテナのスケーリング閾値を設定することができます。例えば、Webアプリケーションとロギングサイドカーがある場合、サイドカーのコンテナとそのリソース使用を無視して、Webアプリケーションのリソース使用に基づいてスケーリングすることができます。

ターゲットリソースを新しいPodの仕様に修正し、異なるコンテナのセットを持つようにした場合、新たに追加されたコンテナもスケーリングに使用されるべきであれば、HPAの仕様も修正すべきです。メトリクスソースで指定されたコンテナが存在しないか、または一部のPodのみに存在する場合、それらのPodは無視され、推奨が再計算されます。計算に関する詳細は、アルゴリズムを参照してください。コンテナリソースを自動スケーリングに使用するためには、以下のようにメトリクスソースを定義します:

type: ContainerResource
containerResource:
  name: cpu
  container: application
  target:
    type: Utilization
    averageUtilization: 60

上記の例では、HPAコントローラーはターゲットをスケールし、すべてのPodのapplicationコンテナ内のCPUの平均使用率が60%になるようにします。

カスタムメトリクスでのスケーリング

FEATURE STATE: Kubernetes v1.23 [stable]

(以前のautoscaling/v2beta2 APIバージョンでは、これをベータ機能として提供していました)

autoscaling/v2 APIバージョンを使用することで、HorizontalPodAutoscalerをカスタムメトリクス(KubernetesまたはKubernetesのコンポーネントに組み込まれていない)に基づいてスケールするように設定することができます。その後、HorizontalPodAutoscalerコントローラーはこれらのカスタムメトリクスをKubernetes APIからクエリします。

要件については、メトリクスAPIのサポートを参照してください。

複数メトリクスでのスケーリング

FEATURE STATE: Kubernetes v1.23 [stable]

(以前のautoscaling/v2beta2 APIバージョンでは、これをベータ機能として提供していました)

autoscaling/v2 APIバージョンを使用することで、HorizontalPodAutoscalerがスケールするための複数のメトリクスを指定することができます。その後、HorizontalPodAutoscalerコントローラーは各メトリクスを評価し、そのメトリクスに基づいた新しいスケールを提案します。HorizontalPodAutoscalerは、各メトリクスで推奨される最大のスケールを取得し、そのサイズにワークロードを設定します(ただし、これが設定した全体の最大値を超えていないことが前提です)。

メトリクスAPIのサポート

デフォルトでは、HorizontalPodAutoscalerコントローラーは一連のAPIからメトリクスを取得します。これらのAPIにアクセスするためには、クラスター管理者が以下を確認する必要があります:

  • API集約レイヤーが有効になっていること。

  • 対応するAPIが登録されていること:

    • リソースメトリクスの場合、これは一般的にmetrics-serverによって提供されるmetrics.k8s.io APIです。クラスターの追加機能として起動することができます。

    • カスタムメトリクスの場合、これはcustom.metrics.k8s.io APIです。これはメトリクスソリューションベンダーが提供する「アダプター」APIサーバーによって提供されます。利用可能なKubernetesメトリクスアダプターがあるかどうかは、メトリクスパイプラインで確認してください。

    • 外部メトリクスの場合、これはexternal.metrics.k8s.io APIです。これは上記のカスタムメトリクスアダプターによって提供される可能性があります。

これらの異なるメトリクスパスとその違いについての詳細は、HPA V2custom.metrics.k8s.io、およびexternal.metrics.k8s.ioの関連デザイン提案をご覧ください。

これらの使用方法の例については、カスタムメトリクスの使用方法外部メトリクスの使用方法をご覧ください。

設定可能なスケーリング動作

FEATURE STATE: Kubernetes v1.23 [stable]

(以前のautoscaling/v2beta2 APIバージョンでは、これをベータ機能として提供していました)

v2 HorizontalPodAutoscaler APIを使用する場合、behaviorフィールド(APIリファレンスを参照)を使用して、スケールアップとスケールダウンの振る舞いを個別に設定することができます。これらの振る舞いは、behaviorフィールドの下でscaleUpおよび/またはscaleDownを設定することにより指定します。

スケーリングターゲットのレプリカ数のフラッピングを防ぐための 安定化ウィンドウ を指定することができます。また、スケーリングポリシーにより、スケーリング中のレプリカの変化率を制御することもできます。

スケーリングポリシー

1つ以上のスケーリングポリシーをspecのbehaviorセクションで指定することができます。複数のポリシーが指定された場合、デフォルトで最も多くの変更を許可するポリシーが選択されます。次の例は、スケールダウンする際のこの振る舞いを示しています:

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSecondsは、ポリシーが真でなければならない過去の時間を示します。最初のポリシー(Pods)では、1分間で最大4つのレプリカをスケールダウンできます。2つ目のポリシー(Percent)では、1分間で現在のレプリカの最大10%をスケールダウンできます。

デフォルトでは、最も多くの変更を許可するポリシーが選択されるため、2つ目のポリシーはPodのレプリカの数が40を超える場合にのみ使用されます。40レプリカ以下の場合、最初のポリシーが適用されます。例えば、レプリカが80あり、ターゲットを10レプリカにスケールダウンしなければならない場合、最初のステップでは8レプリカが減少します。次のイテレーションでは、レプリカの数が72で、ポッドの10%は7.2ですが、数値は8に切り上げられます。オートスケーラーコントローラーの各ループで、変更するべきPodの数は現在のレプリカの数に基づいて再計算されます。レプリカの数が40以下になると、最初のポリシー(Pods)が適用され、一度に4つのレプリカが減少します。

ポリシーの選択は、スケーリング方向のselectPolicyフィールドを指定することで変更できます。この値をMinに設定すると、レプリカ数の最小変化を許可するポリシーが選択されます。この値をDisabledに設定すると、その方向へのスケーリングが完全に無効になります。

安定化ウィンドウ

安定化ウィンドウは、スケーリングに使用されるメトリクスが常に変動する場合のレプリカ数のフラッピングを制限するために使用されます。自動スケーリングアルゴリズムは、このウィンドウを使用して以前の望ましい状態を推測し、ワークロードスケールへの望ましくない変更を避けます。

例えば、次の例のスニペットでは、scaleDownに対して安定化ウィンドウが指定されています。

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300

メトリクスがターゲットをスケールダウンすべきであることを示すと、アルゴリズムは以前に計算された望ましい状態を探し、指定された間隔から最高値を使用します。上記の例では、過去5分間のすべての望ましい状態が考慮されます。

これは移動最大値を近似し、スケーリングアルゴリズムが頻繁にPodを削除して、わずかな時間後に同等のPodの再作成をトリガーするのを防ぎます。

デフォルトの動作

カスタムスケーリングを使用するためには、全てのフィールドを指定する必要はありません。カスタマイズが必要な値のみを指定することができます。これらのカスタム値はデフォルト値とマージされます。デフォルト値はHPAアルゴリズムの既存の動作と一致します。

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

スケールダウンの場合、安定化ウィンドウは300秒(--horizontal-pod-autoscaler-downscale-stabilizationフラグが指定されている場合はその値)です。スケールダウンのための単一のポリシーがあり、現在稼働しているレプリカの100%を削除することが許可されています。これは、スケーリングターゲットが最小許容レプリカ数まで縮小されることを意味します。スケールアップの場合、安定化ウィンドウはありません。メトリクスがターゲットをスケールアップするべきであることを示すと、ターゲットはすぐにスケールアップされます。2つのポリシーがあり、HPAが安定状態に達するまで、最大で15秒ごとに4つのポッドまたは現在稼働しているレプリカの100%が追加されます。

例: ダウンスケール安定化ウィンドウの変更

1分間のカスタムダウンスケール安定化ウィンドウを提供するには、HPAに以下の動作を追加します:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

例: スケールダウン率の制限

HPAによるPodの除去率を毎分10%に制限するには、HPAに以下の動作を追加します:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

1分あたりに削除されるPodが5つを超えないようにするために、固定サイズ5の2番目のスケールダウンポリシーを追加し、selectPolicyを最小に設定することができます。selectPolicyMinに設定すると、オートスケーラーは最少数のPodに影響を与えるポリシーを選択します:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

例: スケールダウンの無効化

selectPolicyの値がDisabledの場合、指定された方向のスケーリングをオフにします。したがって、スケールダウンを防ぐには、次のようなポリシーが使われます:

behavior:
  scaleDown:
    selectPolicy: Disabled

kubectlにおけるHorizontalPodAutoscalerのサポート

HorizontalPodAutoscalerは、他のすべてのAPIリソースと同様にkubectlによって標準的にサポートされています。kubectl createコマンドを使用して新しいオートスケーラーを作成することができます。kubectl get hpaを使用してオートスケーラーを一覧表示したり、kubectl describe hpaを使用して詳細な説明を取得したりできます。最後に、kubectl delete hpaを使用してオートスケーラーを削除することができます。

さらに、HorizontalPodAutoscalerオブジェクトを作成するための特別なkubectl autoscaleコマンドがあります。例えば、kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80を実行すると、ReplicaSet fooのオートスケーラーが作成され、ターゲットのCPU使用率が80%に設定され、レプリカ数は2から5の間になります。

暗黙のメンテナンスモードの非活性化

HPAの設定自体を変更することなく、ターゲットのHPAを暗黙的に非活性化することができます。ターゲットの理想のレプリカ数が0に設定され、HPAの最小レプリカ数が0より大きい場合、HPAはターゲットの調整を停止します(そして、自身のScalingActive条件をfalseに設定します)。これは、ターゲットの理想のレプリカ数またはHPAの最小レプリカ数を手動で調整して再活性化するまで続きます。

DeploymentとStatefulSetを水平自動スケーリングへ移行する

HPAが有効になっている場合、Deploymentおよび/またはStatefulSetのspec.replicasの値をそのマニフェストから削除することが推奨されます。これを行わない場合、たとえばkubectl apply -f deployment.yamlを介してそのオブジェクトに変更が適用されるたびに、これはKubernetesに現在のPodの数をspec.replicasキーの値にスケールするよう指示します。これは望ましくない場合があり、HPAがアクティブなときに問題になる可能性があります。

spec.replicasの削除は、このキーのデフォルト値が1であるため(参照: Deploymentのレプリカ数)、一度だけPod数が低下する可能性があることに注意してください。更新時に、1つを除くすべてのPodが終了手順を開始します。その後の任意のDeploymentアプリケーションは通常どおり動作し、望む通りのローリングアップデート設定を尊重します。Deploymentをどのように変更しているかによって、以下の2つの方法から1つを選択することでこの低下を回避することができます:

  1. kubectl apply edit-last-applied deployment/<deployment_name>
  2. エディターでspec.replicasを削除します。保存してエディターを終了すると、kubectlが更新を適用します。このステップではPod数に変更はありません。
  3. これでマニフェストからspec.replicasを削除できます。ソースコード管理を使用している場合は、変更をコミットするか、更新の追跡方法に適したソースコードの改訂に関するその他の手順を行います。
  4. ここからはkubectl apply -f deployment.yamlを実行できます。

サーバーサイド適用を使用する場合は、この具体的なユースケースをカバーしている所有権の移行ガイドラインに従うことができます。

次の項目

クラスターでオートスケーリングを設定する場合、Cluster Autoscalerのようなクラスターレベルのオートスケーラーを実行することも検討してみてください。

HorizontalPodAutoscalerに関する詳細情報:

4.8.8 - Horizontal Pod Autoscalerウォークスルー

Horizontal Pod Autoscalerは、Deployment、ReplicaSetまたはStatefulSetといったレプリケーションコントローラー内のPodの数を、観測されたCPU使用率(もしくはベータサポートの、アプリケーションによって提供されるその他のメトリクス)に基づいて自動的にスケールさせます。

このドキュメントはphp-apacheサーバーに対しHorizontal Pod Autoscalerを有効化するという例に沿ってウォークスルーで説明していきます。Horizontal Pod Autoscalerの動作についてのより詳細な情報を知りたい場合は、Horizontal Pod Autoscalerユーザーガイドをご覧ください。

始める前に

この例ではバージョン1.2以上の動作するKubernetesクラスターおよびkubectlが必要です。 Metrics APIを介してメトリクスを提供するために、Metrics serverによるモニタリングがクラスター内にデプロイされている必要があります。 Horizontal Pod Autoscalerはメトリクスを収集するためにこのAPIを利用します。metrics-serverをデプロイする方法を知りたい場合はmetrics-server ドキュメントをご覧ください。

Horizontal Pod Autoscalerで複数のリソースメトリクスを利用するためには、バージョン1.6以上のKubernetesクラスターおよびkubectlが必要です。カスタムメトリクスを使えるようにするためには、あなたのクラスターがカスタムメトリクスAPIを提供するAPIサーバーと通信できる必要があります。 最後に、Kubernetesオブジェクトと関係のないメトリクスを使うにはバージョン1.10以上のKubernetesクラスターおよびkubectlが必要で、さらにあなたのクラスターが外部メトリクスAPIを提供するAPIサーバーと通信できる必要があります。 詳細についてはHorizontal Pod Autoscaler user guideをご覧ください。

php-apacheの起動と公開

Horizontal Pod Autoscalerのデモンストレーションのために、php-apacheイメージをもとにしたカスタムのDockerイメージを使います。 このDockerfileは下記のようになっています。

FROM php:5-apache
COPY index.php /var/www/html/index.php
RUN chmod a+rx index.php

これはCPU負荷の高い演算を行うindex.phpを定義しています。

<?php
  $x = 0.0001;
  for ($i = 0; $i <= 1000000; $i++) {
    $x += sqrt($x);
  }
  echo "OK!";
?>

まず最初に、イメージを動かすDeploymentを起動し、Serviceとして公開しましょう。 下記の設定を使います。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

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

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache created
service/php-apache created

Horizontal Pod Autoscalerを作成する

サーバーが起動したら、kubectl autoscaleを使ってautoscalerを作成しましょう。以下のコマンドで、最初のステップで作成したphp-apache deploymentによって制御されるPodレプリカ数を1から10の間に維持するHorizontal Pod Autoscalerを作成します。 簡単に言うと、HPAは(Deploymentを通じて)レプリカ数を増減させ、すべてのPodにおける平均CPU使用率を50%(それぞれのPodはkubectl runで200 milli-coresを要求しているため、平均CPU使用率100 milli-coresを意味します)に保とうとします。 このアルゴリズムについての詳細はこちらをご覧ください。

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled

以下を実行して現在のAutoscalerの状況を確認できます。

kubectl get hpa
NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

現在はサーバーにリクエストを送っていないため、CPU使用率が0%になっていることに注意してください(TARGETカラムは対応するDeploymentによって制御される全てのPodの平均値を示しています。)。

負荷の増加

Autoscalerがどのように負荷の増加に反応するか見てみましょう。 コンテナを作成し、クエリの無限ループをphp-apacheサーバーに送ってみます(これは別のターミナルで実行してください)。

kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

数分以内に、下記を実行することでCPU負荷が高まっていることを確認できます。

kubectl get hpa
NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

ここでは、CPU使用率はrequestの305%にまで高まっています。 結果として、Deploymentはレプリカ数7にリサイズされました。

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

負荷の停止

ユーザー負荷を止めてこの例を終わらせましょう。

私たちがbusyboxイメージを使って作成したコンテナ内のターミナルで、<Ctrl> + Cを入力して負荷生成を終了させます。

そして結果の状態を確認します(数分後)。

kubectl get hpa
NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m
kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

ここでCPU使用率は0に下がり、HPAによってオートスケールされたレプリカ数は1に戻ります。

複数のメトリクスやカスタムメトリクスを基にオートスケーリングする

autoscaling/v2beta2 APIバージョンと使うと、php-apache Deploymentをオートスケーリングする際に使う追加のメトリクスを導入することが出来ます。

まず、autoscaling/v2beta2内のHorizontalPodAutoscalerのYAMLファイルを入手します。

kubectl get hpa.v2beta2.autoscaling -o yaml > /tmp/hpa-v2.yaml

/tmp/hpa-v2.yamlファイルをエディタで開くと、以下のようなYAMLファイルが見えるはずです。

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
      current:
        averageUtilization: 0
        averageValue: 0

targetCPUUtilizationPercentageフィールドはmetricsと呼ばれる配列に置換されています。 CPU使用率メトリクスは、Podコンテナで定められたリソースの割合として表されるため、リソースメトリクスです。CPU以外のリソースメトリクスを指定することもできます。デフォルトでは、他にメモリだけがリソースメトリクスとしてサポートされています。これらのリソースはクラスター間で名前が変わることはなく、そしてmetrics.k8s.io APIが利用可能である限り常に利用可能です。

さらにtarget.typeにおいてUtilizationの代わりにAverageValueを使い、target.averageUtilizationフィールドの代わりに対応するtarget.averageValueフィールドを設定することで、リソースメトリクスをrequest値に対する割合に代わり、直接的な値に設定することも可能です。

PodメトリクスとObjectメトリクスという2つの異なる種類のメトリクスが存在し、どちらもカスタムメトリクスとみなされます。これらのメトリクスはクラスター特有の名前を持ち、利用するにはより発展的なクラスター監視設定が必要となります。

これらの代替メトリクスタイプのうち、最初のものがPodメトリクスです。これらのメトリクスはPodを説明し、Podを渡って平均され、レプリカ数を決定するためにターゲット値と比較されます。 これらはほとんどリソースメトリクス同様に機能しますが、targetの種類としてはAverageValueのみをサポートしている点が異なります。

Podメトリクスはmetricブロックを使って以下のように指定されます。

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

2つ目のメトリクスタイプはObjectメトリクスです。これらのメトリクスはPodを説明するかわりに、同一Namespace内の異なったオブジェクトを説明します。このメトリクスはオブジェクトから取得される必要はありません。単に説明するだけです。Objectメトリクスはtargetの種類としてValueAverageValueをサポートします。Valueでは、ターゲットはAPIから返ってきたメトリクスと直接比較されます。AverageValueでは、カスタムメトリクスAPIから返ってきた値はターゲットと比較される前にPodの数で除算されます。以下の例はrequests-per-secondメトリクスのYAML表現です。

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

もしこのようなmetricブロックを複数提供した場合、HorizontalPodAutoscalerはこれらのメトリクスを順番に処理します。 HorizontalPodAutoscalerはそれぞれのメトリクスについて推奨レプリカ数を算出し、その中で最も多いレプリカ数を採用します。

例えば、もしあなたがネットワークトラフィックについてのメトリクスを収集する監視システムを持っているなら、kubectl editを使って指定を次のように更新することができます。

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      current:
        value: 10k

この時、HorizontalPodAutoscalerはそれぞれのPodがCPU requestの50%を使い、1秒当たり1000パケットを送信し、そしてmain-route Ingressの裏にあるすべてのPodが合計で1秒当たり10000パケットを送信する状態を保持しようとします。

より詳細なメトリクスをもとにオートスケーリングする

多くのメトリクスパイプラインは、名前もしくは labels と呼ばれる追加の記述子の組み合わせによって説明することができます。全てのリソースメトリクス以外のメトリクスタイプ(Pod、Object、そして下で説明されている外部メトリクス)において、メトリクスパイプラインに渡す追加のラベルセレクターを指定することができます。例えば、もしあなたがhttp_requestsメトリクスをverbラベルとともに収集しているなら、下記のmetricブロックを指定してGETリクエストにのみ基づいてスケールさせることができます。

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

このセレクターは完全なKubernetesラベルセレクターと同じ文法を利用します。もし名前とセレクターが複数の系列に一致した場合、この監視パイプラインはどのようにして複数の系列を一つの値にまとめるかを決定します。このセレクターは付加的なもので、ターゲットオブジェクト(Podsタイプの場合は対象Pod、Objectタイプの場合は説明されるオブジェクト)ではないオブジェクトを説明するメトリクスを選択することは出来ません。

Kubernetesオブジェクトと関係ないメトリクスに基づいたオートスケーリング

Kubernetes上で動いているアプリケーションを、Kubernetes Namespaceと直接的な関係がないサービスを説明するメトリクスのような、Kubernetesクラスター内のオブジェクトと明確な関係が無いメトリクスを基にオートスケールする必要があるかもしれません。Kubernetes 1.10以降では、このようなユースケースを外部メトリクスによって解決できます。

外部メトリクスを使うにはあなたの監視システムについての知識が必要となります。この設定はカスタムメトリクスを使うときのものに似ています。外部メトリクスを使うとあなたの監視システムのあらゆる利用可能なメトリクスに基づいてクラスターをオートスケールできるようになります。上記のようにmetricブロックでnameselectorを設定し、ObjectのかわりにExternalメトリクスタイプを使います。 もし複数の時系列がmetricSelectorにより一致した場合は、それらの値の合計がHorizontalPodAutoscalerに使われます。 外部メトリクスはValueAverageValueの両方のターゲットタイプをサポートしています。これらの機能はObjectタイプを利用するときとまったく同じです。

例えばもしあなたのアプリケーションがホストされたキューサービスからのタスクを処理している場合、あなたは下記のセクションをHorizontalPodAutoscalerマニフェストに追記し、未処理のタスク30個あたり1つのワーカーを必要とすることを指定します。

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector: "queue=worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

可能なら、クラスター管理者がカスタムメトリクスAPIを保護することを簡単にするため、外部メトリクスのかわりにカスタムメトリクスを用いることが望ましいです。外部メトリクスAPIは潜在的に全てのメトリクスへのアクセスを許可するため、クラスター管理者はこれを公開する際には注意が必要です。

付録: Horizontal Pod Autoscaler status conditions

autoscaling/v2beta2形式のHorizontalPodAutoscalerを使っている場合は、KubernetesによるHorizontalPodAutoscaler上のstatus conditionsセットを見ることができます。status conditionsはHorizontalPodAutoscalerがスケール可能かどうか、そして現時点でそれが何らかの方法で制限されているかどうかを示しています。

このconditionsはstatus.conditionsフィールドに現れます。HorizontalPodAutoscalerに影響しているconditionsを確認するために、kubectl describe hpaを利用できます。

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

このHorizontalPodAutoscalerにおいて、いくつかの正常な状態のconditionsを見ることができます。まず最初に、AbleToScaleは、HPAがスケール状況を取得し、更新させることが出来るかどうかだけでなく、何らかのbackoffに関連した状況がスケーリングを妨げていないかを示しています。2番目に、ScalingActiveは、HPAが有効化されているかどうか(例えば、レプリカ数のターゲットがゼロでないこと)や、望ましいスケールを算出できるかどうかを示します。もしこれがFalseの場合、大体はメトリクスの取得において問題があることを示しています。最後に、一番最後の状況であるScalingLimitedは、HorizontalPodAutoscalerの最大値や最小値によって望ましいスケールがキャップされていることを示しています。この指標を見てHorizontalPodAutoscaler上の最大・最小レプリカ数制限を増やす、もしくは減らす検討ができます。

付録: 数量

全てのHorizontalPodAutoscalerおよびメトリクスAPIにおけるメトリクスはquantityとして知られる特殊な整数表記によって指定されます。例えば、10500mという数量は10進数表記で10.5と書くことができます。メトリクスAPIは可能であれば接尾辞を用いない整数を返し、そうでない場合は基本的にミリ単位での数量を返します。これはメトリクス値が11500mの間で、もしくは10進法表記で書かれた場合は11.5の間で変動するということを意味します。

付録: その他の起きうるシナリオ

Autoscalerを宣言的に作成する

kubectl autoscaleコマンドを使って命令的にHorizontalPodAutoscalerを作るかわりに、下記のファイルを使って宣言的に作成することができます。

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

下記のコマンドを実行してAutoscalerを作成します。

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created

4.9 - Jobの実行

並列処理を使用してJobを実行します。

4.9.1 - CronJobを使用して自動化タスクを実行する

CronJobは、Kubernetes v1.21で一般利用(GA)に昇格しました。古いバージョンのKubernetesを使用している場合、正確な情報を参照できるように、使用しているバージョンのKubernetesのドキュメントを参照してください。古いKubernetesのバージョンでは、batch/v1 CronJob APIはサポートされていません。

CronJobを使用すると、Jobを時間ベースのスケジュールで実行できるようになります。この自動化されたJobは、LinuxまたはUNIXシステム上のCronのように実行されます。

CronJobは、バックアップやメールの送信など、定期的なタスクや繰り返しのタスクを作成する時に便利です。CronJobはそれぞれのタスクを、たとえばアクティビティが少ない期間など、特定の時間にスケジューリングすることもできます。

CronJobには制限と特性があります。たとえば、特定の状況下では、1つのCronJobが複数のJobを作成する可能性があるため、Jobは冪等性を持つようにしなければいけません。

制限に関する詳しい情報については、CronJobを参照してください。

始める前に

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

CronJobを作成する

CronJobには設定ファイルが必要です。次の例のCronJobの.specは、現在の時刻とhelloというメッセージを1分ごとに表示します。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

次のコマンドで例のCronJobを実行します。

kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml

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

cronjob.batch/hello created

CronJobを作成したら、次のコマンドで状態を取得します。

kubectl get cronjob hello

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

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          10s

コマンドの結果からわかるように、CronJobはまだスケジュールされておらず、まだ何のJobも実行していません。約1分以内にJobが作成されるのを見てみましょう。

kubectl get jobs --watch

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

NAME               COMPLETIONS   DURATION   AGE
hello-4111706356   0/1                      0s
hello-4111706356   0/1           0s         0s
hello-4111706356   1/1           5s         5s

"hello"CronJobによってスケジュールされたJobが1つ実行中になっていることがわかります。Jobを見るのをやめて、再度CronJobを表示して、Jobがスケジュールされたことを確認してみます。

kubectl get cronjob hello

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

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        50s             75s

CronJobhelloが、LAST SCHEDULEで指定された時間にJobを正しくスケジュールしたことが確認できるはずです。現在、activeなJobの数は0です。つまり、Jobは完了または失敗したことがわかります。

それでは、最後にスケジュールされたJobの作成と、Podの1つの標準出力を表示してみましょう。

# "hello-4111706356" の部分は、あなたのシステム上のJobの名前に置き換えてください。
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items[*].metadata.name})

Podのログを表示します。

kubectl logs $pods

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

Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster

CronJobの削除

CronJobが必要なくなったときは、kubectl delete cronjob <cronjob name>で削除します。

kubectl delete cronjob hello

CronJobを削除すると、すべてのJobと、そのJobが作成したPodが削除され、追加のJobの作成が停止されます。Jobの削除について詳しく知りたい場合は、ガベージコレクションを読んでください。

CronJobのspecを書く

すべてのKubernetesの設定と同じように、CronJobにもapiVersionkindmetadataのフィールドが必要です。設定ファイルの扱い方についての一般的な情報については、アプリケーションのデプロイkubectlを使用してリソースを管理するを読んでください。

CronJobの設定には、.specセクションも必要です。

Schedule

.spec.scheduleは、.specには必須のフィールドです。0 * * * *@hourlyなどのCron形式の文字列を取り、Jobの作成と実行のスケジュール時間を指定します。

フォーマットにはVixie cronのステップ値(step value)も指定できます。FreeBSDのマニュアルでは次のように説明されています。

ステップ値は範囲指定と組み合わせて使用できます。範囲の後ろに/<number>を付けると、範囲全体で指定したnumberの値ごとにスキップすることを意味します。たとえば、0-23/2をhoursフィールドに指定すると、2時間毎にコマンド実行を指定することになります(V7標準では代わりに0,2,4,6,8,10,12,14,16,18,20,22と指定する必要があります)。ステップはアスタリスクの後ろにつけることもできます。そのため、「2時間毎に実行」したい場合は、単純に*/2と指定できます。

Job Template

.spec.jobTemplateはJobのテンプレートであり、必須です。Jobと完全に同一のスキーマを持ちますが、フィールドがネストされている点と、apiVersionkindが存在しない点だけが異なります。Jobの.specを書くための情報については、JobのSpecを書くを参照してください。

Starting Deadline

.spec.startingDeadlineSecondsフィールドはオプションです。何かの理由でスケジュールに間に合わなかった場合に適用される、Jobの開始のデッドライン(締め切り)を秒数で指定します。デッドラインを過ぎると、CronJobはJobを開始しません。この場合にデッドラインに間に合わなかったJobは、失敗したJobとしてカウントされます。もしこのフィールドが指定されなかった場合、Jobはデッドラインを持ちません。

.spec.startingDeadlineSecondsフィールドがnull以外に設定された場合、CronJobコントローラーはJobの作成が期待される時間と現在時刻との間の時間を計測します。もしその差が制限よりも大きかった場合、その実行はスキップされます。

たとえば、この値が200に設定された場合、実際のスケジュールの最大200秒後までに作成されるJobだけが許可されます。

Concurrency Policy

.spec.concurrencyPolicyフィールドもオプションです。このフィールドは、このCronJobで作成されたJobの並列実行をどのように扱うかを指定します。specには以下のconcurrency policyのいずれかを指定します。

  • Allow (デフォルト): CronJobがJobを並列に実行することを許可します。
  • Forbid: CronJobの並列実行を禁止します。もし新しいJobの実行時に過去のJobがまだ完了していなかった場合、CronJobは新しいJobの実行をスキップします。
  • Replace: もし新しいJobの実行の時間になっても過去のJobの実行が完了していなかった場合、CronJobは現在の実行中のJobを新しいJobで置換します。

concurrency policyは、同じCronJobが作成したJobにのみ適用されます。もし複数のCronJobがある場合、それぞれのJobの並列実行は常に許可されます。

Suspend

.spec.suspendフィールドもオプションです。このフィールドをtrueに設定すると、すべての後続の実行がサスペンド(一時停止)されます。この設定はすでに実行開始したJobには適用されません。デフォルトはfalseです。

Job History Limit

.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimitフィールドはオプションです。これらのフィールドには、完了したJobと失敗したJobをいくつ保持するかを指定します。デフォルトでは、それぞれ3と1に設定されます。リミットを0に設定すると、対応する種類のJobを実行完了後に何も保持しなくなります。

4.9.2 - 静的な処理の割り当てを使用した並列処理のためのインデックス付きJob

FEATURE STATE: Kubernetes v1.21 [alpha]

この例では、複数の並列ワーカープロセスを使用するKubernetesのJobを実行します。各ワーカーは、それぞれが自分のPod内で実行される異なるコンテナです。Podはコントロールプレーンが自動的に設定するインデックス値を持ち、この値を利用することで、各Podは処理するタスク全体のどの部分を処理するのかを特定できます。

Podのインデックスは、アノテーション内のbatch.kubernetes.io/job-completion-indexを整数値の文字列表現として利用できます。コンテナ化されたタスクプロセスがこのインデックスを取得できるようにするために、このアノテーションの値はdownward APIの仕組みを利用することで公開できます。利便性のために、コントロールプレーンは自動的にdownward APIを設定して、JOB_COMPLETION_INDEX環境変数にインデックスを公開します。

以下に、この例で実行するステップの概要を示します。

  1. completionのインデックスを使用してJobのマニフェストを定義する。downward APIはPodのインデックスのアノテーションを環境変数またはファイルとしてコンテナに渡してくれます。
  2. そのマニフェストに基づいてインデックス付き(Indexed)のJobを開始する

始める前に

あらかじめ基本的な非並列のJobの使用に慣れている必要があります。

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

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

インデックス付きJobを作成できるようにするには、APIサーバーコントローラーマネージャー上でIndexedJobフィーチャーゲートを有効にしていることを確認してください。

アプローチを選択する

ワーカープログラムから処理アイテムにアクセスするには、いくつかの選択肢があります。

  1. JOB_COMPLETION_INDEX環境変数を読み込む。Jobコントローラーは、この変数をcompletion indexを含むアノテーションに自動的にリンクします。
  2. completion indexを含むファイルを読み込む。
  3. プログラムを修正できない場合、プログラムをスクリプトでラップし、上のいずれかの方法でインデックスを読み取り、プログラムが入力として使用できるものに変換する。

この例では、3番目のオプションを選択肢して、revユーティリティを実行したいと考えているとしましょう。このプログラムはファイルを引数として受け取り、内容を逆さまに表示します。

rev data.txt

revツールはbusyboxコンテナイメージから利用できます。

これは単なる例であるため、各Podはごく簡単な処理(短い文字列を逆にする)をするだけです。現実のワークロードでは、たとえば、シーンデータをもとに60秒の動画を生成するというようなタスクを記述したJobを作成するかもしれません。ビデオレンダリングJobの各処理アイテムは、ビデオクリップの特定のフレームのレンダリングを行うものになるでしょう。その場合、インデックス付きの完了が意味するのは、クリップの最初からフレームをカウントすることで、Job内の各Podがレンダリングと公開をするのがどのフレームであるかがわかるということです。

インデックス付きJobを定義する

以下は、completion modeとしてIndexedを使用するJobのマニフェストの例です。

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      initContainers:
      - name: 'input'
        image: 'docker.io/library/bash'
        command:
        - "bash"
        - "-c"
        - |
          items=(foo bar baz qux xyz)
          echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt          
        volumeMounts:
        - mountPath: /input
          name: input
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        emptyDir: {}

上記の例では、Jobコントローラーがすべてのコンテナに設定する組み込みのJOB_COMPLETION_INDEX環境変数を使っています。initコンテナがインデックスを静的な値にマッピングし、その値をファイルに書き込み、ファイルをemptyDir volumeを介してワーカーを実行しているコンテナと共有します。オプションとして、インデックスとコンテナに公開するためにdownward APIを使用して独自の環境変数を定義することもできます。環境変数やファイルとして設定したConfigMapから値のリストを読み込むという選択肢もあります。

他には、以下の例のように、直接downward APIを使用してアノテーションの値をボリュームファイルとして渡すこともできます。

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        downwardAPI:
          items:
          - path: "data.txt"
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']

Jobを実行する

次のコマンドでJobを実行します。

# このコマンドでは1番目のアプローチを使っています ($JOB_COMPLETION_INDEX に依存しています)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml

このJobを作成したら、コントロールプレーンは指定した各インデックスごとに一連のPodを作成します。.spec.parallelismの値が同時に実行できるPodの数を決定し、.spec.completionsの値がJobが作成するPodの合計数を決定します。

.spec.parallelism.spec.completionsより小さいため、コントロールプレーンは別のPodを開始する前に最初のPodの一部が完了するまで待機します。

Jobを作成したら、少し待ってから進行状況を確認します。

kubectl describe jobs/indexed-job

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

Name:              indexed-job
Namespace:         default
Selector:          controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels:            controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
                   job-name=indexed-job
Annotations:       <none>
Parallelism:       3
Completions:       5
Start Time:        Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses:     2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
  Labels:  controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
           job-name=indexed-job
  Init Containers:
   input:
    Image:      docker.io/library/bash
    Port:       <none>
    Host Port:  <none>
    Command:
      bash
      -c
      items=(foo bar baz qux xyz)
      echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt

    Environment:  <none>
    Mounts:
      /input from input (rw)
  Containers:
   worker:
    Image:      docker.io/library/busybox
    Port:       <none>
    Host Port:  <none>
    Command:
      rev
      /input/data.txt
    Environment:  <none>
    Mounts:
      /input from input (rw)
  Volumes:
   input:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-njkjj
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-9kd4h
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-qjwsz
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-fdhq5
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-ncslj

この例では、各インデックスごとにカスタムの値を使用してJobを実行します。次のコマンドでPodの1つの出力を確認できます。

kubectl logs indexed-job-fdhq5 # これを対象のJobのPodの名前に一致するように変更してください。

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

xuq

4.10 - クラスター内アプリケーションへのアクセス

クラスター内アプリケーションへアクセスできるようにするために、ロードバランシングやポートフォワーディングの設定、ファイアウォールやDNS設定のセットアップを行います。

4.10.1 - Web UI (Dashboard)

ダッシュボードは、WebベースのKubernetesユーザーインターフェースです。 ダッシュボードを使用して、コンテナ化されたアプリケーションをKubernetesクラスターにデプロイしたり、 コンテナ化されたアプリケーションをトラブルシューティングしたり、クラスターリソースを管理したりすることができます。 ダッシュボードを使用して、クラスター上で実行されているアプリケーションの概要を把握したり、 個々のKubernetesリソース(Deployments、Jobs、DaemonSetsなど)を作成または修正したりすることができます。 たとえば、Deploymentのスケール、ローリングアップデートの開始、Podの再起動、 デプロイウィザードを使用した新しいアプリケーションのデプロイなどが可能です。

ダッシュボードでは、クラスター内のKubernetesリソースの状態や、発生した可能性のあるエラーに関する情報も提供されます。

Kubernetes Dashboard UI

ダッシュボードUIのデプロイ

ダッシュボードUIはデフォルトではデプロイされていません。デプロイするには、以下のコマンドを実行します:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

ダッシュボードUIへのアクセス

クラスターデータを保護するために、ダッシュボードはデフォルトで最小限のRBAC構成でデプロイします。 現在、ダッシュボードはBearer Tokenによるログインのみをサポートしています。 このデモ用のトークンを作成するには、 サンプルユーザーの作成ガイドに従ってください。

コマンドラインプロキシ

以下のコマンドを実行することで、kubectlコマンドラインツールを使ってダッシュボードにアクセスすることができます:

kubectl proxy

kubectlは、ダッシュボードを http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ で利用できるようにします。

UIはコマンドを実行しているマシンから のみ アクセスできます。オプションについてはkubectl proxy --helpを参照してください。

ウェルカムビュー

空のクラスターでダッシュボードにアクセスすると、ウェルカムページが表示されます。 このページには、このドキュメントへのリンクと、最初のアプリケーションをデプロイするためのボタンが含まれています。 さらに、クラスターのkube-system名前空間でデフォルトで実行されているシステムアプリケーション、たとえばダッシュボード自体を見ることができます。

Kubernetes Dashboard welcome page

コンテナ化されたアプリケーションのデプロイ

ダッシュボードを使用すると、簡単なウィザードでコンテナ化されたアプリケーションをDeploymentとオプションのServiceとして作成してデプロイすることができます。 アプリケーションの詳細を手動で指定するか、アプリケーションの設定を含むYAMLまたはJSONファイルをアップロードすることができます。

任意のページの右上にあるCREATEボタンをクリックして開始します。

アプリケーションの詳細の指定

デプロイウィザードでは、以下の情報を入力する必要があります:

  • App name (必須): アプリケーションの名前です。 その名前のlabelは、デプロイされるDeploymentとServiceに追加されます。

    アプリケーション名は、選択したKubernetes名前空間内で一意である必要があります。 小文字で始まり、小文字または数字で終わり、小文字、数字、ダッシュ(-)のみを含む必要があります。文字数は24文字に制限されています。先頭と末尾のスペースは無視されます。

  • Container image (必須): 任意のレジストリ上の公開Dockerコンテナイメージ、またはプライベートイメージ(一般的にはGoogle Container RegistryやDocker Hub上でホストされている)のURLです。 コンテナイメージの指定はコロンで終わらせる必要があります。

    クラスター全体で必要な数のPodを維持するために、Deploymentが作成されます。

  • Service (任意): アプリケーションのいくつかの部分(たとえばフロントエンド)では、 Serviceをクラスター外の外部、おそらくパブリックIPアドレス(外部サービス)に公開したいと思うかもしれません。

    クラスター内部からしか見えないその他のサービスは、内部サービスと呼ばれます。

    サービスの種類にかかわらず、サービスを作成し、コンテナがポート(受信)をリッスンする場合は、 2つのポートを指定する必要があります。 サービスは、ポート(受信)をコンテナから見たターゲットポートにマッピングして作成されます。 このサービスは、デプロイされたPodにルーティングされます。サポートされるプロトコルはTCPとUDPです。 このサービスの内部DNS名は、上記のアプリケーション名として指定した値になります。

必要に応じて、高度なオプションセクションを展開して、より多くの設定を指定することができます:

  • Description: ここで入力したテキストは、 アノテーションとしてDeploymentに追加され、アプリケーションの詳細に表示されます。

  • Labels: アプリケーションに使用するデフォルトのラベルは、アプリケーション名とバージョンです。 リリース、環境、ティア、パーティション、リリーストラックなど、Deployment、Service(存在する場合)、Podに適用する追加のラベルを指定できます。

    例:

    release=1.0
    tier=frontend
    environment=pod
    track=stable
    
  • Namespace: Kubernetesは、同じ物理クラスターを基盤とする複数の仮想クラスターをサポートしています。 これらの仮想クラスターは名前空間 と呼ばれます。 これにより、リソースを論理的に名前のついたグループに分割することができます。

    ダッシュボードでは、利用可能なすべての名前空間がドロップダウンリストに表示され、新しい名前空間を作成することができます。 名前空間名には、最大63文字の英数字とダッシュ(-)を含めることができますが、大文字を含めることはできません。 名前空間名は数字だけで構成されるべきではありません。 名前が10などの数値として設定されている場合、Podはデフォルトの名前空間に配置されます。

    名前空間の作成に成功した場合は、デフォルトで選択されます。 作成に失敗した場合は、最初の名前空間が選択されます。

  • Image Pull Secret: 指定されたDockerコンテナイメージが非公開の場合、 pull secretの認証情報が必要になる場合があります。

    ダッシュボードでは、利用可能なすべてのSecretがドロップダウンリストに表示され、新しいSecretを作成できます。 Secret名は DNSドメイン名の構文に従う必要があります。たとえば、new.image-pull.secretです。 Secretの内容はbase64エンコードされ、.dockercfgファイルで指定されている必要があります。 Secret名は最大253文字で構成されます。

    イメージプルシークレットの作成に成功した場合は、デフォルトで選択されています。作成に失敗した場合は、シークレットは適用されません。

  • CPU requirement (cores)Memory requirement (MiB): コンテナの最小リソース制限を指定することができます。デフォルトでは、PodはCPUとメモリの制限がない状態で実行されます。

  • Run commandRun command arguments: デフォルトでは、コンテナは指定されたDockerイメージのデフォルトのentrypointコマンドを実行します。 コマンドのオプションと引数を使ってデフォルトを上書きすることができます。

  • Run as privileged: この設定は、特権コンテナ内のプロセスが、ホスト上でrootとして実行されているプロセスと同等であるかどうかを決定します。特権コンテナは、 ネットワークスタックの操作やデバイスへのアクセスなどの機能を利用できます。

  • Environment variables: Kubernetesは環境変数を介してServiceを公開しています。 環境変数を作成したり、環境変数の値を使ってコマンドに引数を渡したりすることができます。 環境変数の値はServiceを見つけるためにアプリケーションで利用できます。 値は$(VAR_NAME)構文を使用して他の変数を参照できます。

YAMLまたはJSONファイルのアップロード

Kubernetesは宣言的な設定をサポートしています。 このスタイルでは、すべての設定は Kubernetes APIリソーススキーマを使用してYAMLまたは JSON設定ファイルに格納されます。

デプロイウィザードでアプリケーションの詳細を指定する代わりに、 YAMLまたはJSONファイルでアプリケーションを定義し、ダッシュボードを使用してファイルをアップロードできます。

ダッシュボードの使用

以下のセクションでは、Kubernetes Dashboard UIのビュー、それらが提供するものとその使用方法について説明します。

ナビゲーション

クラスターにKubernetesオブジェクトが定義されている場合、ダッシュボードではそれらのオブジェクトが初期表示されます。 デフォルトでは default 名前空間のオブジェクトのみが表示されますが、これはナビゲーションメニューにある名前空間セレクターで変更できます。

ダッシュボードにはほとんどのKubernetesオブジェクトの種類が表示され、いくつかのメニューカテゴリーにグループ化されています。

管理者の概要

クラスターと名前空間の管理者向けに、ダッシュボードにはノード、名前空間、永続ボリュームが一覧表示され、それらの詳細ビューが用意されています。 ノードリストビューには、すべてのノードにわたって集計されたCPUとメモリーのメトリクスが表示されます。 詳細ビューには、ノードのメトリクス、仕様、ステータス、割り当てられたリソース、イベント、ノード上で実行されているPodが表示されます。

ワークロード

選択した名前空間で実行されているすべてのアプリケーションを表示します。 このビューでは、アプリケーションがワークロードの種類(例:Deployment、ReplicaSet、StatefulSetなど)ごとに一覧表示され、各ワークロードの種類を個別に表示することができます。 リストには、ReplicaSetの準備ができたPodの数やPodの現在のメモリ使用量など、ワークロードに関する実用的な情報がまとめられています。

ワークロードの詳細ビューには、ステータスや仕様情報、オブジェクト間の表面関係が表示されます。 たとえば、ReplicaSetが制御しているPodや、新しいReplicaSet、DeploymentのためのHorizontal Pod Autoscalerなどです。

Service

外部の世界にサービスを公開し、クラスター内でサービスを発見できるようにするKubernetesリソースを表示します。 そのため、ServiceとIngressのビューには、それらが対象とするPod、クラスター接続の内部エンドポイント、外部ユーザーの外部エンドポイントが表示されます。

ストレージ

ストレージビューには、アプリケーションがデータを保存するために使用するPersistentVolumeClaimリソースが表示されます。

ConfigMapとSecret

クラスターで実行されているアプリケーションのライブ設定に使用されているすべてのKubernetesリソースを表示します。 このビューでは、設定オブジェクトの編集と管理が可能で、デフォルトで非表示になっているSecretを表示します。

ログビューアー

Podのリストと詳細ページは、ダッシュボードに組み込まれたログビューアーにリンクしています。 このビューアーでは、単一のPodに属するコンテナからログをドリルダウンすることができます。

Logs viewer

次の項目

詳細についてはKubernetes Dashboardプロジェクトページをご覧ください。

4.10.2 - 複数のクラスターへのアクセスを設定する

ここでは、設定ファイルを使って複数のクラスターにアクセスする方法を紹介します。クラスター、ユーザー、コンテキストの情報を一つ以上の設定ファイルにまとめることで、kubectl config use-contextのコマンドを使ってクラスターを素早く切り替えることができます。

始める前に

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

kubectlがインストールされているか確認するため、kubectl version --clientを実行してください。kubectlのバージョンは、クラスターのAPIサーバーの1つのマイナーバージョン内である必要があります。

クラスター、ユーザー、コンテキストを設定する

例として、開発用のクラスターが一つ、実験用のクラスターが一つ、計二つのクラスターが存在する場合を考えます。developmentと呼ばれる開発用のクラスター内では、フロントエンドの開発者はfrontendというnamespace内で、ストレージの開発者はstorageというnamespace内で作業をします。scratchと呼ばれる実験用のクラスター内では、開発者はデフォルトのnamespaceで作業をするか、状況に応じて追加のnamespaceを作成します。開発用のクラスターは証明書を通しての認証を必要とします。実験用のクラスターはユーザーネームとパスワードを通しての認証を必要とします。

config-exerciseというディレクトリを作成してください。config-exerciseディレクトリ内に、以下を含むconfig-demoというファイルを作成してください:

apiVersion: v1
kind: Config
preferences: {}

clusters:
- cluster:
  name: development
- cluster:
  name: scratch

users:
- name: developer
- name: experimenter

contexts:
- context:
  name: dev-frontend
- context:
  name: dev-storage
- context:
  name: exp-scratch

設定ファイルには、クラスター、ユーザー、コンテキストの情報が含まれています。上記のconfig-demo設定ファイルには、二つのクラスター、二人のユーザー、三つのコンテキストの情報が含まれています。

config-exerciseディレクトリに移動してください。クラスター情報を設定ファイルに追加するために、以下のコマンドを実行してください:

kubectl config --kubeconfig=config-demo set-cluster development --server=https://1.2.3.4 --certificate-authority=fake-ca-file
kubectl config --kubeconfig=config-demo set-cluster scratch --server=https://5.6.7.8 --insecure-skip-tls-verify

ユーザー情報を設定ファイルに追加してください:

kubectl config --kubeconfig=config-demo set-credentials developer --client-certificate=fake-cert-file --client-key=fake-key-seefile
kubectl config --kubeconfig=config-demo set-credentials experimenter --username=exp --password=some-password

コンテキスト情報を設定ファイルに追加してください:

kubectl config --kubeconfig=config-demo set-context dev-frontend --cluster=development --namespace=frontend --user=developer
kubectl config --kubeconfig=config-demo set-context dev-storage --cluster=development --namespace=storage --user=developer
kubectl config --kubeconfig=config-demo set-context exp-scratch --cluster=scratch --namespace=default --user=experimenter

追加した情報を確認するために、config-demoファイルを開いてください。config-demoファイルを開く代わりに、config viewのコマンドを使うこともできます。

kubectl config --kubeconfig=config-demo view

出力には、二つのクラスター、二人のユーザー、三つのコンテキストが表示されます:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
- cluster:
    insecure-skip-tls-verify: true
    server: https://5.6.7.8
  name: scratch
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: scratch
    namespace: default
    user: experimenter
  name: exp-scratch
current-context: ""
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file
- name: experimenter
  user:
    password: some-password
    username: exp

上記のfake-ca-filefake-cert-filefake-key-fileは、証明書ファイルの実際のパスのプレースホルダーです。環境内にある証明書ファイルの実際のパスに変更してください。

証明書ファイルのパスの代わりにbase64にエンコードされたデータを使用したい場合は、キーに-dataの接尾辞を加えてください。例えば、certificate-authority-dataclient-certificate-dataclient-key-dataとできます。

それぞれのコンテキストは、クラスター、ユーザー、namespaceの三つ組からなっています。例えば、dev-frontendは、developerユーザーの認証情報を使ってdevelopmentクラスターのfrontendnamespaceへのアクセスを意味しています。

現在のコンテキストを設定してください:

kubectl config --kubeconfig=config-demo use-context dev-frontend

これ以降実行されるkubectlコマンドは、dev-frontendに設定されたクラスターとnamespaceに適用されます。また、dev-frontendに設定されたユーザーの認証情報を使用します。

現在のコンテキストの設定情報のみを確認するには、--minifyフラグを使用してください。

kubectl config --kubeconfig=config-demo view --minify

出力には、dev-frontendの設定情報が表示されます:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file

今度は、実験用のクラスター内でしばらく作業する場合を考えます。

現在のコンテキストをexp-scratchに切り替えてください:

kubectl config --kubeconfig=config-demo use-context exp-scratch

これ以降実行されるkubectlコマンドは、scratchクラスター内のデフォルトnamespaceに適用されます。また、exp-scratchに設定されたユーザーの認証情報を使用します。

新しく切り替えたexp-scratchの設定を確認してください。

kubectl config --kubeconfig=config-demo view --minify

最後に、developmentクラスター内のstoragenamespaceでしばらく作業する場合を考えます。

現在のコンテキストをdev-storageに切り替えてください:

kubectl config --kubeconfig=config-demo use-context dev-storage

新しく切り替えたdev-storageの設定を確認してください。

kubectl config --kubeconfig=config-demo view --minify

二つ目の設定ファイルを作成する

config-exerciseディレクトリ内に、以下を含むconfig-demo-2というファイルを作成してください:

apiVersion: v1
kind: Config
preferences: {}

contexts:
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up

上記の設定ファイルは、dev-ramp-upというコンテキストを表します。

KUBECONFIG環境変数を設定する

KUBECONFIGという環境変数が存在するかを確認してください。もし存在する場合は、後で復元できるようにバックアップしてください。例えば:

Linux

export KUBECONFIG_SAVED=$KUBECONFIG

Windows PowerShell

$Env:KUBECONFIG_SAVED=$ENV:KUBECONFIG

KUBECONFIG環境変数は、設定ファイルのパスのリストです。リスト内のパスはLinuxとMacではコロンで区切られ、Windowsではセミコロンで区切られます。KUBECONFIG環境変数が存在する場合は、リスト内の設定ファイルの内容を確認してください。

一時的にKUBECONFIG環境変数に以下の二つのパスを追加してください。例えば:

Linux

export KUBECONFIG=$KUBECONFIG:config-demo:config-demo-2

Windows PowerShell

$Env:KUBECONFIG=("config-demo;config-demo-2")

config-exerciseディレクトリ内から、以下のコマンドを実行してください:

kubectl config view

出力には、KUBECONFIG環境変数に含まれる全てのファイルの情報がまとめて表示されます。config-demo-2ファイルに設定されたdev-ramp-upの情報と、config-demoに設定された三つのコンテキストの情報がまとめてあることに注目してください:

contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: scratch
    namespace: default
    user: experimenter
  name: exp-scratch

kubeconfigファイルに関するさらなる情報を参照するには、kubeconfigファイルを使ってクラスターへのアクセスを管理するを参照してください。

$HOME/.kubeディレクトリの内容を確認する

既にクラスターを所持していて、kubectlを使ってクラスターを操作できる場合は、$HOME/.kubeディレクトリ内にconfigというファイルが存在する可能性が高いです。

$HOME/.kubeに移動して、そこに存在するファイルを確認してください。configという設定ファイルが存在するはずです。他の設定ファイルも存在する可能性があります。全てのファイルの中身を確認してください。

$HOME/.kube/configをKUBECONFIG環境変数に追加する

もし$HOME/.kube/configファイルが存在していて、既にKUBECONFIG環境変数に追加されていない場合は、KUBECONFIG環境変数に追加してください。例えば:

Linux

export KUBECONFIG=$KUBECONFIG:$HOME/.kube/config

Windows Powershell

$Env:KUBECONFIG="$Env:KUBECONFIG;$HOME/.kube/config"

KUBECONFIG環境変数内のファイルからまとめられた設定情報を確認してください。config-exerciseディレクトリ内から、以下のコマンドを実行してください:

kubectl config view

クリーンアップ

KUBECONFIG環境変数を元に戻してください。例えば:

Linux:

export KUBECONFIG=$KUBECONFIG_SAVED

Windows PowerShell

$Env:KUBECONFIG=$ENV:KUBECONFIG_SAVED

次の項目

4.10.3 - Serviceを利用したクラスター内のアプリケーションへのアクセス

ここでは、クラスター内で稼働しているアプリケーションに外部からアクセスするために、KubernetesのServiceオブジェクトを作成する方法を紹介します。 例として、2つのインスタンスから成るアプリケーションへのロードバランシングを扱います。

始める前に

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

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

目標

  • 2つのHello Worldアプリケーションを稼働させる。
  • Nodeのポートを公開するServiceオブジェクトを作成する。
  • 稼働しているアプリケーションにアクセスするためにServiceオブジェクトを使用する。

2つのPodから成るアプリケーションのServiceを作成

アプリケーションDeploymentの設定ファイルは以下の通りです:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  selector:
    matchLabels:
      run: load-balancer-example
  replicas: 2
  template:
    metadata:
      labels:
        run: load-balancer-example
    spec:
      containers:
        - name: hello-world
          image: gcr.io/google-samples/node-hello:1.0
          ports:
            - containerPort: 8080
              protocol: TCP
  1. クラスターでHello Worldアプリケーションを稼働させます: 上記のファイルを使用し、アプリケーションのDeploymentを作成します:

    kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml
    

    このコマンドはDeploymentオブジェクトとそれに紐付くReplicaSetオブジェクトを作成します。ReplicaSetは、Hello Worldアプリケーションが稼働している2つのPodから構成されます。

  2. Deploymentの情報を表示します:

    kubectl get deployments hello-world
    kubectl describe deployments hello-world
    
  3. ReplicaSetオブジェクトの情報を表示します:

    kubectl get replicasets
    kubectl describe replicasets
    
  4. Deploymentを公開するServiceオブジェクトを作成します:

    kubectl expose deployment hello-world --type=NodePort --name=example-service
    
  5. Serviceに関する情報を表示します:

    kubectl describe services example-service
    

    出力例は以下の通りです:

    Name:                   example-service
    Namespace:              default
    Labels:                 run=load-balancer-example
    Annotations:            <none>
    Selector:               run=load-balancer-example
    Type:                   NodePort
    IP:                     10.32.0.16
    Port:                   <unset> 8080/TCP
    TargetPort:             8080/TCP
    NodePort:               <unset> 31496/TCP
    Endpoints:              10.200.1.4:8080,10.200.2.5:8080
    Session Affinity:       None
    Events:                 <none>
    

    NodePortの値を記録しておきます。上記の例では、31496です。

  6. Hello Worldアプリーションが稼働しているPodを表示します:

    kubectl get pods --selector="run=load-balancer-example" --output=wide
    

    出力例は以下の通りです:

    NAME                           READY   STATUS    ...  IP           NODE
    hello-world-2895499144-bsbk5   1/1     Running   ...  10.200.1.4   worker1
    hello-world-2895499144-m1pwt   1/1     Running   ...  10.200.2.5   worker2
    
  7. Hello World podが稼働するNodeのうち、いずれか1つのパブリックIPアドレスを確認します。 確認方法は、使用している環境により異なります。 例として、Minikubeの場合はkubectl cluster-info、Google Compute Engineの場合はgcloud compute instances listによって確認できます。

  8. 選択したノード上で、NodePortの値でのTCP通信を許可するファイヤーウォールを作成します。 NodePortの値が31568の場合、31568番のポートを利用したTCP通信を許可するファイヤーウォールを作成します。 クラウドプロバイダーによって設定方法が異なります。

  9. Hello World applicationにアクセスするために、Nodeのアドレスとポート番号を使用します:

    curl http://<public-node-ip>:<node-port>
    

    ここで <public-node-ip> はNodeのパブリックIPアドレス、 <node-port> はNodePort Serviceのポート番号の値を表しています。 リクエストが成功すると、下記のメッセージが表示されます:

    Hello Kubernetes!
    

service configuration fileの利用

kubectl exposeコマンドの代わりに、 service configuration file を使用してServiceを作成することもできます。

クリーンアップ

Serviceを削除するには、以下のコマンドを実行します:

kubectl delete services example-service

Hello Worldアプリケーションが稼働しているDeployment、ReplicaSet、Podを削除するには、以下のコマンドを実行します:

kubectl delete deployment hello-world

次の項目

詳細は serviceを利用してアプリケーションと接続する を確認してください。

4.10.4 - Serviceを使用してフロントエンドをバックエンドに接続する

このタスクでは、フロントエンドとバックエンドのマイクロサービスを作成する方法を示します。 バックエンドのマイクロサービスは挨拶です。 フロントエンドとバックエンドは、Kubernetes Serviceオブジェクトを使用して接続されます。

目標

  • Deploymentオブジェクトを使用してマイクロサービスを作成および実行します。
  • フロントエンドを経由してトラフィックをバックエンドにルーティングします。
  • Serviceオブジェクトを使用して、フロントエンドアプリケーションをバックエンドアプリケーションに接続します。

始める前に

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

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

このタスクではServiceで外部ロードバランサーを使用しますが、外部ロードバランサーの使用がサポートされている環境である必要があります。 ご使用の環境がこれをサポートしていない場合は、代わりにタイプNodePortのServiceを使用できます。

Deploymentを使用したバックエンドの作成

バックエンドは、単純な挨拶マイクロサービスです。 バックエンドのDeploymentの構成ファイルは次のとおりです:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
      tier: backend
      track: stable
  replicas: 7
  template:
    metadata:
      labels:
        app: hello
        tier: backend
        track: stable
    spec:
      containers:
        - name: hello
          image: "gcr.io/google-samples/hello-go-gke:1.0"
          ports:
            - name: http
              containerPort: 80

バックエンドのDeploymentを作成します:

kubectl apply -f https://k8s.io/examples/service/access/hello.yaml

バックエンドのDeploymentに関する情報を表示します:

kubectl describe deployment hello

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

Name:                           hello
Namespace:                      default
CreationTimestamp:              Mon, 24 Oct 2016 14:21:02 -0700
Labels:                         app=hello
                                tier=backend
                                track=stable
Annotations:                    deployment.kubernetes.io/revision=1
Selector:                       app=hello,tier=backend,track=stable
Replicas:                       7 desired | 7 updated | 7 total | 7 available | 0 unavailable
StrategyType:                   RollingUpdate
MinReadySeconds:                0
RollingUpdateStrategy:          1 max unavailable, 1 max surge
Pod Template:
  Labels:       app=hello
                tier=backend
                track=stable
  Containers:
   hello:
    Image:              "gcr.io/google-samples/hello-go-gke:1.0"
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable
OldReplicaSets:                 <none>
NewReplicaSet:                  hello-3621623197 (7/7 replicas created)
Events:
...

バックエンドServiceオブジェクトの作成

フロントエンドをバックエンドに接続する鍵は、バックエンドServiceです。 Serviceは、バックエンドマイクロサービスに常に到達できるように、永続的なIPアドレスとDNS名のエントリを作成します。 Serviceはセレクターを使用して、トラフィックをルーティングするPodを見つけます。

まず、Service構成ファイルを調べます:

kind: Service
apiVersion: v1
metadata:
  name: hello
spec:
  selector:
    app: hello
    tier: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: http

設定ファイルで、Serviceがapp:helloおよびtier:backendというラベルを持つPodにトラフィックをルーティングしていることがわかります。

hello Serviceを作成します:

kubectl apply -f https://k8s.io/examples/service/access/hello-service.yaml

この時点で、バックエンドのDeploymentが実行され、そちらにトラフィックをルーティングできるServiceがあります。

フロントエンドの作成

バックエンドができたので、バックエンドに接続するフロントエンドを作成できます。 フロントエンドは、バックエンドServiceに指定されたDNS名を使用して、バックエンドワーカーPodに接続します。 DNS名はhelloです。これは、前のサービス設定ファイルのnameフィールドの値です。

フロントエンドDeploymentのPodは、helloバックエンドServiceを見つけるように構成されたnginxイメージを実行します。 これはnginx設定ファイルです:

upstream hello {
    server hello;
}

server { listen 80;

location / {
    proxy_pass http://hello;
}

}

バックエンドと同様に、フロントエンドにはDeploymentとServiceがあります。 Serviceの設定にはtype:LoadBalancerがあります。これは、Serviceがクラウドプロバイダーのデフォルトのロードバランサーを使用することを意味します。

apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: hello
    tier: frontend
  ports:
  - protocol: "TCP"
    port: 80
    targetPort: 80
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: hello
      tier: frontend
      track: stable
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        tier: frontend
        track: stable
    spec:
      containers:
      - name: nginx
        image: "gcr.io/google-samples/hello-frontend:1.0"
        lifecycle:
          preStop:
            exec:
              command: ["/usr/sbin/nginx","-s","quit"]

フロントエンドのDeploymentとServiceを作成します:

kubectl apply -f https://k8s.io/examples/service/access/frontend.yaml

出力結果から両方のリソースが作成されたことを確認します:

deployment.apps/frontend created
service/frontend created

フロントエンドServiceと対話

LoadBalancerタイプのServiceを作成したら、このコマンドを使用して外部IPを見つけることができます:

kubectl get service frontend --watch

これによりfrontend Serviceの設定が表示され、変更が監視されます。 最初、外部IPは<pending>としてリストされます:

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   <pending>     80/TCP   10s

ただし、外部IPがプロビジョニングされるとすぐに、EXTERNAL-IPという見出しの下に新しいIPが含まれるように構成が更新されます:

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP        PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   XXX.XXX.XXX.XXX    80/TCP   1m

このIPを使用して、クラスターの外部からfrontend Serviceとやり取りできるようになりました。

フロントエンドを介するトラフィック送信

フロントエンドとバックエンドが接続されました。 フロントエンドServiceの外部IPに対してcurlコマンドを使用して、エンドポイントにアクセスできます。

curl http://${EXTERNAL_IP} # これを前に見たEXTERNAL-IPに置き換えます

出力には、バックエンドによって生成されたメッセージが表示されます:

{"message":"Hello"}

クリーンアップ

Serviceを削除するには、このコマンドを入力してください:

kubectl delete services frontend hello

バックエンドとフロントエンドアプリケーションを実行しているDeploymentとReplicaSetとPodを削除するために、このコマンドを入力してください:

kubectl delete deployment frontend hello

次の項目

4.10.5 - クラスターで実行されているすべてのコンテナイメージを一覧表示する

このページでは、kubectlを使用して、クラスターで実行されているPodのすべてのコンテナイメージを一覧表示する方法を説明します。

始める前に

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

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

この演習では、kubectlを使用してクラスターで実行されているすべてのPodを取得し、出力をフォーマットしてそれぞれのコンテナの一覧を取得します。

すべての名前空間のコンテナイメージを一覧表示する

  • kubectl get pods --all-namespacesを使用して、すべての名前空間のPodを取得します
  • -o jsonpath={.. image}を使用して、コンテナイメージ名のリストのみが含まれるように出力をフォーマットします。これは、返されたjsonのimageフィールドを再帰的に解析します。
  • trsortuniqなどの標準ツールを使用して出力をフォーマットします。
    • trを使用してスペースを改行に置換します。
    • sortを使用して結果を並べ替えます。
    • uniqを使用してイメージ数を集計します。
kubectl get pods --all-namespaces -o jsonpath="{..image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c

上記のコマンドは、返されるすべてのアイテムについて、imageという名前のすべてのフィールドを再帰的に返します。

別の方法として、Pod内のimageフィールドへの絶対パスを使用することができます。これにより、フィールド名が繰り返されている場合でも正しいフィールドが取得されます。多くのフィールドは与えられたアイテム内でnameと呼ばれます:

kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}"

jsonpathは次のように解釈されます:

  • .items[*]: 各戻り値
  • .spec: 仕様の取得
  • .containers[*]: 各コンテナ
  • .image: イメージの取得

Podごとにコンテナイメージを一覧表示する

rangeを使用して要素を個別に繰り返し処理することにより、フォーマットをさらに制御できます。

kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |\
sort

Podのラベルを使用してコンテナイメージ一覧をフィルタリングする

特定のラベルに一致するPodのみを対象とするには、-lフラグを使用します。以下は、app=nginxに一致するラベルを持つPodのみに一致します。

kubectl get pods --all-namespaces -o jsonpath="{..image}" -l app=nginx

Podの名前空間でコンテナイメージ一覧をフィルタリングする

特定の名前空間のPodのみを対象とするには、namespaceフラグを使用します。以下はkube-system名前空間のPodのみに一致します。

kubectl get pods --namespace kube-system -o jsonpath="{..image}"

jsonpathの代わりにgo-templateを使用してコンテナイメージを一覧表示する

jsonpathの代わりに、kubectlはgo-templatesを使用した出力のフォーマットをサポートしています:

kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"

次の項目

参照

4.10.6 - Minikube上でNGINX Ingressコントローラーを使用してIngressをセットアップする

Ingressとは、クラスター内のServiceに外部からのアクセスを許可するルールを定義するAPIオブジェクトです。IngressコントローラーはIngress内に設定されたルールを満たすように動作します。

このページでは、簡単なIngressをセットアップして、HTTPのURIに応じてwebまたはweb2というServiceにリクエストをルーティングする方法を説明します。

始める前に

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

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

Minikubeクラスターを作成する

  1. Launch Terminalをクリックします。

  2. (オプション) Minikubeをローカル環境にインストールした場合は、次のコマンドを実行します。

    minikube start
    

Ingressコントローラーを有効化する

  1. NGINX Ingressコントローラーを有効にするために、次のコマンドを実行します。

    minikube addons enable ingress
    
  2. NGINX Ingressコントローラーが起動したことを確認します。

    kubectl get pods -n kube-system
    

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

    NAME                                        READY     STATUS    RESTARTS   AGE
    default-http-backend-59868b7dd6-xb8tq       1/1       Running   0          1m
    kube-addon-manager-minikube                 1/1       Running   0          3m
    kube-dns-6dcb57bcc8-n4xd4                   3/3       Running   0          2m
    kubernetes-dashboard-5498ccf677-b8p5h       1/1       Running   0          2m
    nginx-ingress-controller-5984b97644-rnkrg   1/1       Running   0          1m
    storage-provisioner                         1/1       Running   0          2m
    

Hello Worldアプリをデプロイする

  1. 次のコマンドを実行して、Deploymentを作成します。

    kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
    

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

    deployment.apps/web created
    
  2. Deploymentを公開します。

    kubectl expose deployment web --type=NodePort --port=8080
    

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

    service/web exposed
    
  3. Serviceが作成され、NodePort上で利用できるようになったことを確認します。

    kubectl get service web
    

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

    NAME      TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
    web       NodePort   10.104.133.249   <none>        8080:31637/TCP   12m
    
  4. NodePort経由でServiceを訪問します。

    minikube service web --url
    

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

    http://172.17.0.15:31637
    

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

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

    これで、MinikubeのIPアドレスとNodePort経由で、サンプルアプリにアクセスできるようになりました。次のステップでは、Ingressリソースを使用してアプリにアクセスできるように設定します。

Ingressリソースを作成する

以下に示すファイルは、hello-world.info経由で送られたトラフィックをServiceに送信するIngressリソースです。

  1. 以下の内容でexample-ingress.yamlを作成します。

    apiVersion: networking.k8s.io/v1
     kind: Ingress
     metadata:
       name: example-ingress
       annotations:
         nginx.ingress.kubernetes.io/rewrite-target: /$1
     spec:
       rules:
         - host: hello-world.info
           http:
             paths:
               - path: /
                 pathType: Prefix
                 backend:
                   service:
                     name: web
                     port:
                       number: 8080
  2. 次のコマンドを実行して、Ingressリソースを作成します。

    kubectl apply -f https://kubernetes.io/examples/service/networking/example-ingress.yaml
    

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

    ingress.networking.k8s.io/example-ingress created
    
  3. 次のコマンドで、IPアドレスが設定されていることを確認します。

    kubectl get ingress
    
    NAME              CLASS    HOSTS              ADDRESS        PORTS   AGE
    example-ingress   <none>   hello-world.info   172.17.0.15    80      38s
    
  4. 次の行を/etc/hostsファイルの最後に書きます。

    172.17.0.15 hello-world.info
    

    この設定により、リクエストがhello-world.infoからMinikubeに送信されるようになります。

  5. Ingressコントローラーがトラフィックを制御していることを確認します。

    curl hello-world.info
    

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

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

2番目のDeploymentを作成する

  1. 次のコマンドを実行して、v2のDeploymentを作成します。

    kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
    

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

    deployment.apps/web2 created
    
  2. Deploymentを公開します。

    kubectl expose deployment web2 --port=8080 --type=NodePort
    

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

    service/web2 exposed
    

Ingressを編集する

  1. 既存のexample-ingress.yamlを編集して、以下の行を追加します。

          - path: /v2
            pathType: Prefix
            backend:
              service:
                name: web2
                port:
                  number: 8080
    
  2. 次のコマンドで変更を適用します。

    kubectl apply -f example-ingress.yaml
    

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

    ingress.networking/example-ingress configured
    

Ingressを試す

  1. Hello Worldアプリの1番目のバージョンにアクセスします。

    curl hello-world.info
    

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

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    
  2. Hello Worldアプリの2番目のバージョンにアクセスします。

    curl hello-world.info/v2
    

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

    Hello, world!
    Version: 2.0.0
    Hostname: web2-75cd47646f-t8cjk
    

次の項目

4.10.7 - 共有ボリュームを使用して同じPod内のコンテナ間で通信する

このページでは、ボリュームを使用して、同じPodで実行されている2つのコンテナ間で通信する方法を示します。 コンテナ間でプロセス名前空間を共有することにより、プロセスが通信できるようにする方法も参照してください。

始める前に

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

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

2つのコンテナを実行するPodの作成

この演習では、2つのコンテナを実行するPodを作成します。 2つのコンテナは、通信に使用できるボリュームを共有します。 これがPodの設定ファイルです:

apiVersion: v1
kind: Pod
metadata:
  name: two-containers
spec:

  restartPolicy: Never

  volumes:
  - name: shared-data
    emptyDir: {}

  containers:

  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

設定ファイルで、Podにshared-dataという名前のボリュームがあることがわかります。

設定ファイルにリストされている最初のコンテナは、nginxサーバーを実行します。 共有ボリュームのマウントパスは/usr/share/nginx/htmlです。 2番目のコンテナはdebianイメージをベースとしており、/pod-dataのマウントパスを持っています。 2番目のコンテナは次のコマンドを実行してから終了します。

echo Hello from the debian container > /pod-data/index.html

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

Podと2つのコンテナを作成します:

kubectl apply -f https://k8s.io/examples/pods/two-container-pod.yaml

Podとコンテナに関する情報を表示します:

kubectl get pod two-containers --output=yaml

こちらは出力の一部です:

apiVersion: v1
kind: Pod
metadata:
  ...
  name: two-containers
  namespace: default
  ...
spec:
  ...
  containerStatuses:

  - containerID: docker://c1d8abd1 ...
    image: debian
    ...
    lastState:
      terminated:
        ...
    name: debian-container
    ...

  - containerID: docker://96c1ff2c5bb ...
    image: nginx
    ...
    name: nginx-container
    ...
    state:
      running:
    ...

debianコンテナが終了し、nginxコンテナがまだ実行されていることがわかります。

nginxコンテナへのシェルを取得します:

kubectl exec -it two-containers -c nginx-container -- /bin/bash

シェルで、nginxが実行されていることを確認します:

root@two-containers:/# apt-get update
root@two-containers:/# apt-get install curl procps
root@two-containers:/# ps aux

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

USER       PID  ...  STAT START   TIME COMMAND
root         1  ...  Ss   21:12   0:00 nginx: master process nginx -g daemon off;

debianコンテナがnginxルートディレクトリにindex.htmlファイルを作成したことを思い出してください。 curlを使用して、GETリクエストをnginxサーバーに送信します:

root@two-containers:/# curl localhost

出力は、nginxがdebianコンテナによって書かれたWebページを提供することを示しています:

Hello from the debian container

議論

Podが複数のコンテナを持つことができる主な理由は、プライマリアプリケーションを支援するヘルパーアプリケーションをサポートするためです。 ヘルパーアプリケーションの典型的な例は、データプラー、データプッシャー、およびプロキシです。 多くの場合、ヘルパーアプリケーションとプライマリアプリケーションは互いに通信する必要があります。 通常、これは、この演習に示すように共有ファイルシステムを介して、またはループバックネットワークインターフェースであるlocalhostを介して行われます。 このパターンの例は、新しい更新のためにGitリポジトリをポーリングするヘルパープログラムを伴うWebサーバーです。

この演習のボリュームは、コンテナがポッドの寿命中に通信する方法を提供します。 Podを削除して再作成すると、共有ボリュームに保存されているデータはすべて失われます。

次の項目

4.11 - TLS

Transport Layer Security(TLS)を使用して、クラスター内のトラフィックを保護する方法について理解します。

4.11.1 - Kubeletの証明書のローテーションを設定する

このページでは、kubeletの証明書のローテーションを設定する方法を説明します。

FEATURE STATE: Kubernetes v1.8 [beta]

始める前に

  • Kubernetesはバージョン1.8.0以降である必要があります。

概要

kubeletは、Kubernetes APIへの認証のために証明書を使用します。デフォルトでは、証明書は1年間の有効期限付きで発行されるため、頻繁に更新する必要はありません。

Kubernetes 1.8にはベータ機能のkubelet certificate rotationが含まれているため、現在の証明書の有効期限が近づいたときに自動的に新しい鍵を生成して、Kubernetes APIに新しい証明書をリクエストできます。新しい証明書が利用できるようになると、Kubernetes APIへの接続の認証に利用されます。

クライアント証明書のローテーションを有効にする

kubeletプロセスは--rotate-certificatesという引数を受け付けます。この引数によって、現在使用している証明書の有効期限が近づいたときに、kubeletが自動的に新しい証明書をリクエストするかどうかを制御できます。証明書のローテーションはベータ機能であるため、--feature-gates=RotateKubeletClientCertificate=trueを使用してフィーチャーフラグを有効にする必要もあります。

kube-controller-managerプロセスは、--experimental-cluster-signing-durationという引数を受け付け、この引数で証明書が発行される期間を制御できます。

証明書のローテーションの設定を理解する

kubeletが起動すると、ブートストラップが設定されている場合(--bootstrap-kubeconfigフラグを使用した場合)、初期証明書を使用してKubernetes APIに接続して、証明書署名リクエスト(certificate signing request、CSR)を発行します。証明書署名リクエストのステータスは、次のコマンドで表示できます。

kubectl get csr

ノード上のkubeletから発行された証明書署名リクエストは、初めはPending状態です。証明書署名リクエストが特定の条件を満たすと、コントローラーマネージャーに自動的に承認され、Approved状態になります。次に、コントローラーマネージャーは--experimental-cluster-signing-durationパラメーターで指定された有効期限で発行された証明書に署名を行い、署名された証明書が証明書署名リクエストに添付されます。

kubeletは署名された証明書をKubernetes APIから取得し、ディスク上の--cert-dirで指定された場所に書き込みます。その後、kubeletは新しい証明書を使用してKubernetes APIに接続するようになります。

署名された証明書の有効期限が近づくと、kubeletはKubernetes APIを使用して新しい証明書署名リクエストを自動的に発行します。再び、コントローラーマネージャーは証明書のリクエストを自動的に承認し、署名された証明書を証明書署名リクエストに添付します。kubeletは新しい署名された証明書をKubernetes APIから取得してディスクに書き込みます。その後、kubeletは既存のコネクションを更新して、新しい証明書でKubernetes APIに再接続します。

4.12 - ネットワーク

クラスターのネットワークの設定方法を学びます。

4.12.1 - HostAliasesを使用してPodの/etc/hostsにエントリーを追加する

Podの/etc/hostsファイルにエントリーを追加すると、DNSやその他の選択肢を利用できない場合に、Podレベルでホスト名の名前解決を上書きできるようになります。このようなカスタムエントリーは、PodSpecのHostAliasesフィールドに追加できます。

HostAliasesを使用せずにファイルを修正することはおすすめできません。このファイルはkubeletが管理しており、Podの作成や再起動時に上書きされる可能性があるためです。

デフォルトのhostsファイルの内容

Nginx Podを実行すると、Pod IPが割り当てられます。

kubectl run nginx --image nginx
pod/nginx created

Pod IPを確認します。

kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

hostsファイルの内容は次のようになります。

kubectl exec nginx -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	nginx

デフォルトでは、hostsファイルには、localhostやPod自身のホスト名などのIPv4とIPv6のボイラープレートだけが含まれています。

追加エントリーをhostAliasesに追加する

デフォルトのボイラープレートに加えて、hostsファイルに追加エントリーを追加できます。たとえば、foo.localbar.local127.0.0.1に、foo.remotebar.remote10.1.2.3にそれぞれ解決するためには、PodのHostAliasesを.spec.hostAliases以下に設定します。

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox
    command:
    - cat
    args:
    - "/etc/hosts"

この設定を使用したPodを開始するには、次のコマンドを実行します。

kubectl apply -f https://k8s.io/examples/service/networking/hostaliases-pod.yaml
pod/hostaliases-pod created

Podの詳細情報を表示して、IPv4アドレスと状態を確認します。

kubectl get pod --output=wide
NAME                           READY     STATUS      RESTARTS   AGE       IP              NODE
hostaliases-pod                0/1       Completed   0          6s        10.200.0.5      worker0

hostsファイルの内容は次のようになります。

kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.5	hostaliases-pod

# Entries added by HostAliases.
127.0.0.1	foo.local	bar.local
10.1.2.3	foo.remote	bar.remote

ファイルの最後に追加エントリーが指定されています。

kubeletがhostsファイルを管理するのはなぜですか?

kubeletがPodの各コンテナのhostsファイルを管理するのは、コンテナ起動後にDockerがファイルを編集するのを防ぐためです。

4.12.2 - IPv4/IPv6デュアルスタックの検証

このドキュメントでは、IPv4/IPv6デュアルスタックが有効化されたKubernetesクラスターを検証する方法について共有します。

始める前に

  • プロバイダーがデュアルスタックのネットワークをサポートしていること (クラウドプロバイダーか、ルーティングできるIPv4/IPv6ネットワークインターフェースを持つKubernetesノードが提供できること)
  • (KubenetやCalicoなど)デュアルスタックをサポートするネットワークプラグイン
  • デュアルスタックを有効化したクラスター
作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.20. バージョンを確認するには次のコマンドを実行してください: kubectl version.

アドレスの検証

ノードアドレスの検証

各デュアルスタックのノードは、1つのIPv4ブロックと1つのIPv6ブロックを割り当てる必要があります。IPv4/IPv6のPodアドレスの範囲が設定されていることを検証するには、次のコマンドを実行します。例の中のノード名は、自分のクラスターの有効なデュアルスタックのノードの名前に置換してください。この例では、ノードの名前はk8s-linuxpool1-34450317-0になっています。

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .spec.podCIDRs}}{{printf "%s\n" .}}{{end}}'
10.244.1.0/24
2001:db8::/64

IPv4ブロックとIPv6ブロックがそれぞれ1つずつ割り当てられているはずです。

ノードが検出されたIPv4とIPv6のインターフェースを持っていることを検証します。ノード名は自分のクラスター内の有効なノード名に置換してください。この例では、ノード名はk8s-linuxpool1-34450317-0になっています。

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .status.addresses}}{{printf "%s: %s\n" .type .address}}{{end}}'
Hostname: k8s-linuxpool1-34450317-0
InternalIP: 10.0.0.5
InternalIP: 2001:db8:10::5

Podアドレスの検証

PodにIPv4とIPv6のアドレスが割り当てられていることを検証します。Podの名前は自分のクラスター内の有効なPodの名前と置換してください。この例では、Podの名前はpod01になっています。

kubectl get pods pod01 -o go-template --template='{{range .status.podIPs}}{{printf "%s\n" .ip}}{{end}}'
10.244.1.4
2001:db8::4

Downward APIを使用して、status.podIPsのfieldPath経由でPod IPを検証することもできます。次のスニペットは、Pod IPをMY_POD_IPSという名前の環境変数経由でコンテナ内に公開する方法を示しています。

        env:
        - name: MY_POD_IPS
          valueFrom:
            fieldRef:
              fieldPath: status.podIPs

次のコマンドを実行すると、MY_POD_IPS環境変数の値をコンテナ内から表示できます。値はカンマ区切りのリストであり、PodのIPv4とIPv6のアドレスに対応しています。

kubectl exec -it pod01 -- set | grep MY_POD_IPS
MY_POD_IPS=10.244.1.4,2001:db8::4

PodのIPアドレスは、コンテナ内の/etc/hostsにも書き込まれます。次のコマンドは、デュアルスタックのPod上で/etc/hostsに対してcatコマンドを実行します。出力を見ると、Pod用のIPv4およびIPv6のIPアドレスの両方が確認できます。

kubectl exec -it pod01 -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
fe00::0    ip6-mcastprefix
fe00::1    ip6-allnodes
fe00::2    ip6-allrouters
10.244.1.4    pod01
2001:db8::4    pod01

Serviceの検証

.spec.isFamilyPolicyを明示的に定義していない、以下のようなServiceを作成してみます。Kubernetesは最初に設定したservice-cluster-ip-rangeの範囲からServiceにcluster IPを割り当てて、.spec.ipFamilyPolicySingleStackに設定します。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectlを使ってServiceのYAMLを表示します。

kubectl get svc my-service -o yaml

Serviceの.spec.ipFamilyPolicySingleStackに設定され、.spec.clusterIPにはkube-controller-manager上の--service-cluster-ip-rangeフラグで最初に設定した範囲から1つのIPv4アドレスが設定されているのがわかります。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: default
spec:
  clusterIP: 10.0.217.164
  clusterIPs:
  - 10.0.217.164
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
  selector:
    app: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

.spec.ipFamilies内の配列の1番目の要素にIPv6を明示的に指定した、次のようなServiceを作成してみます。Kubernetesはservice-cluster-ip-rangeで設定したIPv6の範囲からcluster IPを割り当てて、.spec.ipFamilyPolicySingleStackに設定します。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamilies:
  - IPv6
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectlを使ってServiceのYAMLを表示します。

kubectl get svc my-service -o yaml

Serviceの.spec.ipFamilyPolicySingleStackに設定され、.spec.clusterIPには、kube-controller-manager上の--service-cluster-ip-rangeフラグで指定された最初の設定範囲から1つのIPv6アドレスが設定されているのがわかります。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: MyApp
  name: my-service
spec:
  clusterIP: 2001:db8:fd00::5118
  clusterIPs:
  -  2001:db8:fd00::5118
  ipFamilies:
  - IPv6
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

.spec.ipFamiliePolicyPreferDualStackを明示的に指定した、次のようなServiceを作成してみます。Kubernetesは(クラスターでデュアルスタックを有効化しているため)IPv4およびIPv6のアドレスの両方を割り当て、.spec.ClusterIPsのリストから、.spec.ipFamilies配列の最初の要素のアドレスファミリーに基づいた.spec.ClusterIPを設定します。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

kubectl describeを使用して、ServiceがIPv4およびIPv6アドレスのブロックからcluster IPを割り当てられていることを検証します。その後、ServiceにIPアドレスとポートを使用してアクセスできることを検証することもできます。

kubectl describe svc -l app=MyApp
Name:              my-service
Namespace:         default
Labels:            app=MyApp
Annotations:       <none>
Selector:          app=MyApp
Type:              ClusterIP
IP Family Policy:  PreferDualStack
IP Families:       IPv4,IPv6
IP:                10.0.216.242
10.0.216.242,2001:db8:fd00::af55
Port:              <unset>  80/TCP
TargetPort:        9376/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

デュアルスタックのLoadBalancer Serviceを作成する

クラウドプロバイダーがIPv6を有効化した外部ロードバランサーのプロビジョニングをサポートする場合、.spec.ipFamilyPolicyPreferDualStackを指定し、.spec.ipFamiliesの最初の要素をIPv6にして、typeフィールドにLoadBalancerを指定したServiceを作成できます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
  - IPv6
  type: LoadBalancer
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

Serviceを確認します。

kubectl get svc -l app=MyApp

ServiceがIPv6アドレスブロックからCLUSTER-IPのアドレスとEXTERNAL-IPを割り当てられていることを検証します。その後、IPとポートを用いたServiceへのアクセスを検証することもできます。

NAME         TYPE           CLUSTER-IP            EXTERNAL-IP        PORT(S)        AGE
my-service   LoadBalancer   2001:db8:fd00::7ebc   2603:1030:805::5   80:30790/TCP   35s

4.13 - GPUのスケジューリング

クラスター内のノードのリソースとしてGPUを設定してスケジューリングします
FEATURE STATE: Kubernetes v1.10 [beta]

Kubernetesには、複数ノードに搭載されたAMDおよびNVIDIAのGPU(graphical processing unit)を管理するための実験的なサポートが含まれています。

このページでは、異なるバージョンのKubernetesを横断してGPUを使用する方法と、現時点での制限について説明します。

デバイスプラグインを使用する

Kubernetesでは、GPUなどの特別なハードウェアの機能にPodがアクセスできるようにするために、デバイスプラグインが実装されています。

管理者として、ノード上に対応するハードウェアベンダーのGPUドライバーをインストールして、以下のような対応するGPUベンダーのデバイスプラグインを実行する必要があります。

上記の条件を満たしていれば、Kubernetesはamd.com/gpuまたはnvidia.com/gpuをスケジュール可能なリソースとして公開します。

これらのGPUをコンテナから使用するには、cpumemoryをリクエストするのと同じように<vendor>.com/gpuというリソースをリクエストするだけです。ただし、GPUを使用するときにはリソースのリクエストの指定方法にいくつか制限があります。

  • GPUはlimitsセクションでのみ指定されることが想定されている。この制限は、次のことを意味します。
    • Kubernetesはデフォルトでlimitの値をrequestの値として使用するため、GPUのrequestsを省略してlimitsを指定できる。
    • GPUをlimitsrequestsの両方で指定できるが、これら2つの値は等しくなければならない。
    • GPUのlimitsを省略してrequestsだけを指定することはできない。
  • コンテナ(およびPod)はGPUを共有しない。GPUのオーバーコミットは起こらない。
  • 各コンテナは1つ以上のGPUをリクエストできる。1つのGPUの一部だけをリクエストすることはできない。

以下に例を示します。

apiVersion: v1
kind: Pod
metadata:
  name: cuda-vector-add
spec:
  restartPolicy: OnFailure
  containers:
    - name: cuda-vector-add
      # https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
      image: "registry.k8s.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1 # 1 GPUをリクエストしています

AMDのGPUデバイスプラグインをデプロイする

AMD公式のGPUデバイスプラグインには以下の要件があります。

  • Kubernetesのノードに、AMDのGPUのLinuxドライバーがあらかじめインストール済みでなければならない。

クラスターが起動して上記の要件が満たされれば、以下のコマンドを実行することでAMDのデバイスプラグインをデプロイできます。

kubectl create -f https://raw.githubusercontent.com/RadeonOpenCompute/k8s-device-plugin/v1.10/k8s-ds-amdgpu-dp.yaml

このサードパーティーのデバイスプラグインに関する問題は、RadeonOpenCompute/k8s-device-pluginで報告できます。

NVIDIAのGPUデバイスプラグインをデプロイする

現在、NVIDIAのGPU向けのデバイスプラグインの実装は2種類あります。

NVIDIA公式のGPUデバイスプラグイン

NVIDIA公式のGPUデバイスプラグインには以下の要件があります。

  • Kubernetesのノードに、NVIDIAのドライバーがあらかじめインストール済みでなければならない。
  • Kubernetesのノードに、nvidia-docker 2.0があらかじめインストール済みでなければならない。
  • KubeletはコンテナランタイムにDockerを使用しなければならない。
  • runcの代わりにDockerのデフォルトランタイムとして、nvidia-container-runtimeを設定しなければならない。
  • NVIDIAのドライバーのバージョンが次の条件を満たさなければならない ~= 384.81。

クラスターが起動して上記の要件が満たされれば、以下のコマンドを実行することでNVIDIAのデバイスプラグインがデプロイできます。

kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/1.0.0-beta4/nvidia-device-plugin.yml

このサードパーティーのデバイスプラグインに関する問題は、NVIDIA/k8s-device-pluginで報告できます。

GCEで使用されるNVIDIAのGPUデバイスプラグイン

GCEで使用されるNVIDIAのGPUデバイスプラグインは、nvidia-dockerを必要としないため、KubernetesのContainer Runtime Interface(CRI)と互換性のある任意のコンテナランタイムで動作するはずです。このデバイスプラグインはContainer-Optimized OSでテストされていて、1.9以降ではUbuntu向けの実験的なコードも含まれています。

以下のコマンドを実行すると、NVIDIAのドライバーとデバイスプラグインをインストールできます。

# NVIDIAドライバーをContainer-Optimized OSにインストールする
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/stable/daemonset.yaml

# NVIDIAドライバーをUbuntuにインストールする(実験的)
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/stable/nvidia-driver-installer/ubuntu/daemonset.yaml

# デバイスプラグインをインストールする
kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.14/cluster/addons/device-plugins/nvidia-gpu/daemonset.yaml

このサードパーティーのデバイスプラグインの使用やデプロイに関する問題は、GoogleCloudPlatform/container-engine-acceleratorsで報告できます。

Googleは、GKE上でNVIDIAのGPUを使用するための手順も公開しています。

異なる種類のGPUを搭載するクラスター

クラスター上の別のノードに異なる種類のGPUが搭載されている場合、NodeラベルとNodeセレクターを使用することで、Podを適切なノードにスケジューリングできます。

以下に例を示します。

# アクセラレーターを搭載したノードにラベルを付けます。
kubectl label nodes <node-with-k80> accelerator=nvidia-tesla-k80
kubectl label nodes <node-with-p100> accelerator=nvidia-tesla-p100

自動的なNodeラベルの付加

AMDのGPUデバイスを使用している場合、Node Labellerをデプロイできます。Node Labellerはコントローラーの1種で、GPUデバイスのプロパティを持つノードに自動的にラベルを付けてくれます。

現在は、このコントローラーは以下のプロパティに基づいてラベルを追加できます。

  • デバイスID(-device-id)
  • VRAMのサイズ(-vram)
  • SIMDの数(-simd-count)
  • Compute Unitの数(-cu-count)
  • ファームウェアとフィーチャーのバージョン(-firmware)
  • 2文字の頭字語で表されたGPUファミリー(-family)
    • SI - Southern Islands
    • CI - Sea Islands
    • KV - Kaveri
    • VI - Volcanic Islands
    • CZ - Carrizo
    • AI - Arctic Islands
    • RV - Raven
kubectl describe node cluster-node-23
    Name:               cluster-node-23
    Roles:              <none>
    Labels:             beta.amd.com/gpu.cu-count.64=1
                        beta.amd.com/gpu.device-id.6860=1
                        beta.amd.com/gpu.family.AI=1
                        beta.amd.com/gpu.simd-count.256=1
                        beta.amd.com/gpu.vram.16G=1
                        beta.kubernetes.io/arch=amd64
                        beta.kubernetes.io/os=linux
                        kubernetes.io/hostname=cluster-node-23
    Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
                        node.alpha.kubernetes.io/ttl: 0
    …

Node Labellerを使用すると、GPUの種類をPodのspec内で指定できます。

apiVersion: v1
kind: Pod
metadata:
  name: cuda-vector-add
spec:
  restartPolicy: OnFailure
  containers:
    - name: cuda-vector-add
      # https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
      image: "registry.k8s.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100 # または nvidia-tesla-k80 など

これにより、指定した種類のGPUを搭載したノードにPodがスケジューリングされることを保証できます。

4.14 - huge pageを管理する

クラスター内のスケジュール可能なリソースとしてhuge pageの設定と管理を行います。
FEATURE STATE: Kubernetes v1.30 [stable]

Kubernetesでは、事前割り当てされたhuge pageをPod内のアプリケーションに割り当てたり利用したりすることをサポートしています。このページでは、ユーザーがhuge pageを利用できるようにする方法について説明します。

始める前に

  1. Kubernetesのノードがhuge pageのキャパシティを報告するためには、ノード上でhuge pageを事前割り当てしておく必要があります。1つのノードでは複数のサイズのhuge pageが事前割り当てできます。

ノードは、すべてのhuge pageリソースを、スケジュール可能なリソースとして自動的に探索・報告してくれます。

API

huge pageはコンテナレベルのリソース要求でhugepages-<size>という名前のリソースを指定することで利用できます。ここで、<size>は、特定のノード上でサポートされている整数値を使った最も小さなバイナリ表記です。たとえば、ノードが2048KiBと1048576KiBのページサイズをサポートしている場合、ノードはスケジュール可能なリソースとして、hugepages-2Mihugepages-1Giの2つのリソースを公開します。CPUやメモリとは違い、huge pageはオーバーコミットをサポートしません。huge pageリソースをリクエストするときには、メモリやCPUリソースを同時にリクエストしなければならないことに注意してください。

1つのPodのspec内に書くことで、Podから複数のサイズのhuge pageを利用することもできます。その場合、すべてのボリュームマウントでmedium: HugePages-<hugepagesize>という表記を使う必要があります。

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages-2Mi
      name: hugepage-2mi
    - mountPath: /hugepages-1Gi
      name: hugepage-1gi
    resources:
      limits:
        hugepages-2Mi: 100Mi
        hugepages-1Gi: 2Gi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage-2mi
    emptyDir:
      medium: HugePages-2Mi
  - name: hugepage-1gi
    emptyDir:
      medium: HugePages-1Gi

Podで1種類のサイズのhuge pageをリクエストするときだけは、medium: HugePagesという表記を使うこともできます。

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages
      name: hugepage
    resources:
      limits:
        hugepages-2Mi: 100Mi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage
    emptyDir:
      medium: HugePages
  • huge pageのrequestsはlimitsと等しくなければなりません。limitsを指定した場合にはこれがデフォルトですが、requestsを指定しなかった場合にはデフォルトではありません。
  • huge pageはコンテナのスコープで隔離されるため、各コンテナにはそれぞれのcgroupサンドボックスの中でcontainer specでリクエストされた通りのlimitが設定されます。
  • huge pageベースのEmptyDirボリュームは、Podがリクエストしたよりも大きなサイズのページメモリーを使用できません。
  • shmget()SHM_HUGETLBを指定して取得したhuge pageを使用するアプリケーションは、/proc/sys/vm/hugetlb_shm_groupに一致する補助グループ(supplemental group)を使用して実行する必要があります。
  • namespace内のhuge pageの使用量は、ResourceQuotaに対してcpumemoryのような他の計算リソースと同じようにhugepages-<size>というトークンを使用することで制御できます。
  • 複数のサイズのhuge pageのサポートはフィーチャーゲートによる設定が必要です。kubeletkube-apiserver上で、HugePageStorageMediumSizeフィーチャーゲートを使用すると有効にできます(--feature-gates=HugePageStorageMediumSize=true)。

5 - チュートリアル

本セクションにはチュートリアルが含まれています。チュートリアルでは、単一のタスクよりも大きな目標を達成する方法を示します。通常、チュートリアルにはいくつかのセクションがあり、各セクションには一連のステップがあります。各チュートリアルを進める前に、後で参照できるように標準化された用語集ページをブックマークしておくことをお勧めします。

基本

設定

ステートレスアプリケーション

ステートフルアプリケーション

サービス

セキュリティ

次の項目

チュートリアルのページタイプについての情報は、Content Page Typesを参照してください。

5.1 - Hello Minikube

このチュートリアルでは、minikubeとKatacodaを使用して、Kubernetes上でサンプルアプリケーションを動かす方法を紹介します。Katacodaはブラウザで無償のKubernetes環境を提供します。

目標

  • minikubeへのサンプルアプリケーションのデプロイ
  • アプリケーションの実行
  • アプリケーションログの確認

始める前に

このチュートリアルはNGINXを利用してすべての要求をエコーバックするコンテナイメージを提供します。

minikubeクラスターの作成

  1. Launch Terminal をクリックしてください

  1. ブラウザーでKubernetesダッシュボードを開いてください:

    minikube dashboard
    
  2. Katacoda環境のみ:ターミナルペーン上部の+ボタンをクリックしてから Select port to view on Host 1 をクリックしてください。

  3. Katacoda環境のみ:30000を入力し、Display Portをクリックしてください。

Deploymentの作成

KubernetesのPod は、コンテナの管理やネットワーキングの目的でまとめられた、1つ以上のコンテナのグループです。このチュートリアルのPodがもつコンテナは1つのみです。Kubernetesの Deployment はPodの状態を確認し、Podのコンテナが停止した場合には再起動します。DeploymentはPodの作成やスケールを管理するために推奨される方法(手段)です。

  1. kubectl create コマンドを使用してPodを管理するDeploymentを作成してください。Podは提供されたDockerイメージを元にコンテナを実行します。

    kubectl create deployment hello-node --image=registry.k8s.io/echoserver:1.4
    
  2. Deploymentを確認します:

    kubectl get deployments
    

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

    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    hello-node   1/1     1            1           1m
    
  3. Podを確認します:

    kubectl get pods
    

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

    NAME                          READY     STATUS    RESTARTS   AGE
    hello-node-5f76cf6ccf-br9b5   1/1       Running   0          1m
    
  4. クラスターイベントを確認します:

    kubectl get events
    
  5. kubectl で設定を確認します:

    kubectl config view
    

Serviceの作成

通常、PodはKubernetesクラスター内部のIPアドレスからのみアクセスすることができます。hello-nodeコンテナをKubernetesの仮想ネットワークの外部からアクセスするためには、KubernetesのServiceとしてPodを公開する必要があります。

  1. kubectl expose コマンドを使用してPodをインターネットに公開します:

    kubectl expose deployment hello-node --type=LoadBalancer --port=8080
    

    --type=LoadBalancerフラグはServiceをクラスター外部に公開したいことを示しています。

  2. 作成したServiceを確認します:

    kubectl get services
    

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

    NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    hello-node   LoadBalancer   10.108.144.78   <pending>     8080:30369/TCP   21s
    kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          23m
    

    ロードバランサーをサポートするクラウドプロバイダーでは、Serviceにアクセスするための外部IPアドレスが提供されます。 minikube では、LoadBalancerタイプはminikube serviceコマンドを使用した接続可能なServiceを作成します。

  3. 次のコマンドを実行します:

    minikube service hello-node
    
  4. Katacoda環境のみ:ターミナル画面上部の+ボタンをクリックして Select port to view on Host 1 をクリックしてください。

  5. Katacoda環境のみ:8080の反対側のService出力に、5桁のポート番号が表示されます。このポート番号はランダムに生成されるため、ここで使用するポート番号と異なる場合があります。ポート番号テキストボックスに番号を入力し、ポートの表示をクリックしてください。前の例の場合は、30369と入力します。

    アプリケーションとその応答が表示されるブラウザーウィンドウが開きます。

アドオンの有効化

minikubeはビルトインのアドオンがあり、有効化、無効化、あるいはローカルのKubernetes環境に公開することができます。

  1. サポートされているアドオンをリストアップします:

    minikube addons list
    

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

    addon-manager: enabled
    dashboard: enabled
    default-storageclass: enabled
    efk: disabled
    freshpod: disabled
    gvisor: disabled
    helm-tiller: disabled
    ingress: disabled
    ingress-dns: disabled
    logviewer: disabled
    metrics-server: disabled
    nvidia-driver-installer: disabled
    nvidia-gpu-device-plugin: disabled
    registry: disabled
    registry-creds: disabled
    storage-provisioner: enabled
    storage-provisioner-gluster: disabled
    
  2. ここでは例としてmetrics-serverのアドオンを有効化します:

    minikube addons enable metrics-server
    

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

    metrics-server was successfully enabled
    
  3. 作成されたPodとサービスを確認します:

    kubectl get pod,svc -n kube-system
    

    出力:

    NAME                                        READY     STATUS    RESTARTS   AGE
    pod/coredns-5644d7b6d9-mh9ll                1/1       Running   0          34m
    pod/coredns-5644d7b6d9-pqd2t                1/1       Running   0          34m
    pod/metrics-server-67fb648c5                1/1       Running   0          26s
    pod/etcd-minikube                           1/1       Running   0          34m
    pod/influxdb-grafana-b29w8                  2/2       Running   0          26s
    pod/kube-addon-manager-minikube             1/1       Running   0          34m
    pod/kube-apiserver-minikube                 1/1       Running   0          34m
    pod/kube-controller-manager-minikube        1/1       Running   0          34m
    pod/kube-proxy-rnlps                        1/1       Running   0          34m
    pod/kube-scheduler-minikube                 1/1       Running   0          34m
    pod/storage-provisioner                     1/1       Running   0          34m
    
    NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    service/metrics-server         ClusterIP   10.96.241.45    <none>        80/TCP              26s
    service/kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP       34m
    service/monitoring-grafana     NodePort    10.99.24.54     <none>        80:30002/TCP        26s
    service/monitoring-influxdb    ClusterIP   10.111.169.94   <none>        8083/TCP,8086/TCP   26s
    
  4. metrics-serverを無効化します:

    minikube addons disable metrics-server
    

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

    metrics-server was successfully disabled
    

クリーンアップ

クラスターに作成したリソースをクリーンアップします:

kubectl delete service hello-node
kubectl delete deployment hello-node

(オプション)minikubeの仮想マシン(VM)を停止します:

minikube stop

(オプション)minikubeのVMを削除します:

minikube delete

次の項目

5.2 - Kubernetesの基本を学ぶ

Kubernetesの基本

このチュートリアルでは、Kubernetesクラスターオーケストレーションシステムの基本について学びます。各モジュールには、Kubernetesの主な機能と概念に関する背景情報と、インタラクティブなオンラインチュートリアルが含まれています。これらの対話型チュートリアルでは、簡単なクラスターとそのコンテナ化されたアプリケーションを自分で管理できます。

この対話型のチュートリアルでは、以下のことを学ぶことができます:

  • コンテナ化されたアプリケーションをクラスターにデプロイ
  • Deploymentのスケーリング
  • 新しいソフトウェアのバージョンでコンテナ化されたアプリケーションをアップデート
  • コンテナ化されたアプリケーションのデバッグ

このチュートリアルでは、Katacodaを使用して、Webブラウザ上の仮想ターミナルでMinikubeを実行します。Minikubeは、どこでも実行できるKubernetesの小規模なローカル環境です。ソフトウェアをインストールしたり、何かを設定したりする必要はありません。各対話型チュートリアルは、Webブラウザ自体の上で直接実行されます


Kubernetesはどんなことができるの?

モダンなWebサービスでは、ユーザはアプリケーションが24時間365日利用可能であることを期待しており、開発者はそれらのアプリケーションの新しいバージョンを1日に数回デプロイすることを期待しています。コンテナ化は、パッケージソフトウェアがこれらの目標を達成するのを助け、アプリケーションをダウンタイムなしで簡単かつ迅速にリリース、アップデートできるようにします。Kubernetesを使用すると、コンテナ化されたアプリケーションをいつでもどこでも好きなときに実行できるようになり、それらが機能するために必要なリソースとツールを見つけやすくなります。Kubernetesは、コンテナオーケストレーションにおけるGoogleのこれまでの経験と、コミュニティから得られた最善のアイデアを組み合わせて設計された、プロダクションレディなオープンソースプラットフォームです。

5.2.1 - クラスターの作成

5.2.1.1 - Minikubeを使ったクラスターの作成

目標

  • Kubernetesクラスターとは何かを学ぶ
  • Minikubeとは何かを学ぶ
  • Kubernetesクラスターを、オンラインのターミナルを使って動かす

Kubernetesクラスター

Kubernetesは、単一のユニットとして機能するように接続された、可用性の高いコンピューターのクラスターをまとめあげます。Kubernetesの抽象化により、コンテナ化されたアプリケーションを個々のマシンに特に結び付けることなくクラスターにデプロイできます。この新しいデプロイモデルを利用するには、アプリケーションを個々のホストから切り離す方法でアプリケーションをパッケージ化(つまり、コンテナ化)する必要があります。コンテナ化されたアプリケーションは、アプリケーションがホストに深く統合されたパッケージとして特定のマシンに直接インストールされていた従来のデプロイモデルよりも柔軟で、より迅速に利用可能です。Kubernetesはより効率的な方法で、クラスター全体のアプリケーションコンテナの配布とスケジューリングを自動化します。Kubernetesはオープンソースのプラットフォームであり、プロダクションレディです。

Kubernetesクラスターは以下の2種類のリソースで構成されています:

  • マスターがクラスターを管理する
  • ノードがアプリケーションを動かすワーカーとなる

まとめ:

  • Kubernetesクラスター
  • Minikube

Kubernetesは、コンピュータークラスター内およびコンピュータークラスター間でのアプリケーションコンテナの配置(スケジューリング)および実行を調整する、プロダクショングレードのオープンソースプラットフォームです。


クラスターダイアグラム


マスターはクラスターの管理を担当します。マスターは、アプリケーションのスケジューリング、望ましい状態の維持、アプリケーションのスケーリング、新しい更新のロールアウトなど、クラスター内のすべての動作をまとめあげます。

ノードは、Kubernetesクラスターのワーカーマシンとして機能するVMまたは物理マシンです。各ノードにはKubeletがあり、これはノードを管理し、Kubernetesマスターと通信するためのエージェントです。ノードにはDockerやrktなどのコンテナ操作を処理するためのツールもあるはずです。プロダクションのトラフィックを処理するKubernetesクラスターには、最低3つのノードが必要です。

マスターは実行中のアプリケーションをホストするために使用されるノードとクラスターを管理します。

Kubernetesにアプリケーションをデプロイするときは、マスターにアプリケーションコンテナを起動するように指示します。マスターはコンテナがクラスターのノードで実行されるようにスケジュールします。ノードは、マスターが公開しているKubernetes APIを使用してマスターと通信します。エンドユーザーは、Kubernetes APIを直接使用して対話することもできます。

Kubernetesクラスターは、物理マシンまたは仮想マシンのどちらにも配置できます。Kubernetes開発を始めるためにMinikubeを使うことができます。Minikubeは、ローカルマシン上にVMを作成し、1つのノードのみを含む単純なクラスターをデプロイする軽量なKubernetes実装です。Minikubeは、Linux、macOS、およびWindowsシステムで利用可能です。Minikube CLIは、起動、停止、ステータス、削除など、クラスターを操作するための基本的なブートストラップ操作を提供します。ただし、このチュートリアルでは、Minikubeがプリインストールされた状態で提供されているオンラインのターミナルを使用します。

Kubernetesが何であるかがわかったので、オンラインチュートリアルに行き、最初のクラスターを動かしましょう!


5.2.1.2 - 対話型チュートリアル - クラスターの作成

ターミナルを使うにはスクリーンが狭い場合は、PCまたはタブレットをお使いください

5.2.2 - アプリケーションのデプロイ

5.2.2.1 - kubectlを使ったDeploymentの作成

目標

  • アプリケーションのデプロイについて学ぶ
  • kubectlを使って、Kubernetes上にはじめてのアプリケーションをデプロイする

Kubernetes Deployments

実行中のKubernetesクラスターを入手すると、その上にコンテナ化アプリケーションをデプロイすることができます。そのためには、KubernetesのDeployment の設定を作成します。DeploymentはKubernetesにあなたのアプリケーションのインスタンスを作成し、更新する方法を指示します。Deploymentを作成すると、KubernetesマスターはDeployment内に含まれるアプリケーションインスタンスをクラスター内の個々のノードで実行するようにスケジュールします。

アプリケーションインスタンスが作成されると、Kubernetes Deploymentコントローラーは、それらのインスタンスを継続的に監視します。インスタンスをホストしているノードが停止、削除された場合、Deploymentコントローラーはそのインスタンスをクラスター内の別のノード上のインスタンスと置き換えます。これは、マシンの故障やメンテナンスに対処するためのセルフヒーリングの仕組みを提供しています。

オーケストレーションが入る前の世界では、インストールスクリプトを使用してアプリケーションを起動することはよくありましたが、マシン障害が発生した場合に復旧する事はできませんでした。アプリケーションのインスタンスを作成し、それらをノード間で実行し続けることで、Kubernetes Deploymentsはアプリケーションの管理に根本的に異なるアプローチを提供します。

まとめ:

  • Deployments
  • kubectl

Deploymentは、アプリケーションのインスタンスを作成および更新する責務があります。


Kubernetes上にはじめてのアプリケーションをデプロイする


Kubernetesのコマンドラインインターフェースであるkubectlを使用して、Deploymentを作成、管理できます。kubectlはKubernetes APIを使用してクラスターと対話します。このモジュールでは、Kubernetesクラスターでアプリケーションを実行するDeploymentを作成するために必要な、最も一般的なkubectlコマンドについて学びます。

Deploymentを作成するときは、アプリケーションのコンテナイメージと実行するレプリカの数を指定する必要があります。Deploymentを更新することで、あとでその情報を変更できます。チュートリアルのモジュール56では、Deploymentをどのようにスケール、更新できるかについて説明します。

Kubernetesにデプロイするには、アプリケーションをサポートされているコンテナ形式のいずれかにパッケージ化する必要があります。

最初のDeploymentには、DockerコンテナにパッケージされたNode.jsアプリケーションを使用します。(まだNode.jsアプリケーションを作成してデプロイしていない場合、Hello Minikubeチュートリアルの通りにやってみましょう。)

Deploymentが何であるかがわかったので、オンラインチュートリアルに行き、最初のアプリケーションをデプロイしましょう!


5.2.2.2 - 対話型チュートリアル - アプリケーションのデプロイ

Podは、Kubernetesアプリケーションの基本的な実行単位です。各Podは、クラスターで実行されているワークロードの一部を表します。Podの詳細はこちらです


ターミナルを使うには、PCまたはタブレットをお使いください

5.2.3 - アプリケーションの探索

5.2.3.1 - Podとノードについて

目標

  • KubernetesのPodについて学ぶ
  • Kubernetesのノードについて学ぶ
  • デプロイされたアプリケーションのトラブルシューティング

Kubernetes Pod

モジュール2でDeploymentを作成したときに、KubernetesはアプリケーションインスタンスをホストするためのPodを作成しました。Podは、1つ以上のアプリケーションコンテナ(Dockerなど)のグループとそれらのコンテナの共有リソースを表すKubernetesの抽象概念です。 Podには以下のものが含まれます:

  • 共有ストレージ(ボリューム)
  • ネットワーキング(クラスターに固有のIPアドレス)
  • コンテナのイメージバージョンや使用するポートなどの、各コンテナをどう動かすかに関する情報

Podは、アプリケーション固有の「論理ホスト」をモデル化し、比較的密接に結合されたさまざまなアプリケーションコンテナを含むことができます。 たとえば、Podには、Node.jsアプリケーションを含むコンテナと、Node.js Webサーバによって公開されるデータを供給する別のコンテナの両方を含めることができます。Pod内のコンテナはIPアドレスとポートスペースを共有し、常に同じ場所に配置され、同じスケジュールに入れられ、同じノード上の共有コンテキストで実行されます。

Podは、Kubernetesプラットフォームの原子単位です。 Kubernetes上にDeploymentを作成すると、そのDeploymentはその中にコンテナを持つPodを作成します(コンテナを直接作成するのではなく)。 各Podは、スケジュールされているノードに関連付けられており、終了(再起動ポリシーに従って)または削除されるまでそこに残ります。 ノードに障害が発生した場合、同じPodがクラスター内の他の使用可能なノードにスケジュールされます。

まとめ:

  • Pod
  • ノード
  • kubectlの主要なコマンド

Podは1つ以上のアプリケーションコンテナ(Dockerなど)のグループであり、共有ストレージ(ボリューム)、IPアドレス、それらの実行方法に関する情報が含まれています。


Podの概要


ノード

Podは常にノード上で動作します。ノードはKubernetesではワーカーマシンであり、クラスターによって仮想、物理マシンのどちらであってもかまいません。各ノードはマスターによって管理されます。ノードは複数のPodを持つことができ、Kubernetesマスターはクラスター内のノード間でPodのスケジュールを自動的に処理します。マスターの自動スケジューリングは各ノードで利用可能なリソースを考慮に入れます。

すべてのKubernetesノードでは少なくとも以下のものが動作します。

  • Kubelet: Kubernetesマスターとノード間の通信を担当するプロセス。マシン上で実行されているPodとコンテナを管理します。
  • レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行することを担当する、Dockerのようなコンテナランタイム。

コンテナ同士が密接に結合され、ディスクなどのリソースを共有する必要がある場合は、コンテナを1つのPodにまとめてスケジュールする必要があります。


ノードの概要


kubectlを使ったトラブルシューティング

モジュール2では、kubectlのコマンドラインインターフェースを使用しました。モジュール3でもこれを使用して、デプロイされたアプリケーションとその環境に関する情報を入手します。最も一般的な操作は、次のkubectlコマンドで実行できます。

  • kubectl get - リソースの一覧を表示
  • kubectl describe - 単一リソースに関する詳細情報を表示
  • kubectl logs - 単一Pod上の単一コンテナ内のログを表示
  • kubectl exec - 単一Pod上の単一コンテナ内でコマンドを実行

これらのコマンドを使用して、アプリケーションがいつデプロイされたか、それらの現在の状況、実行中の場所、および構成を確認することができます。

クラスターのコンポーネントとコマンドラインの詳細についてわかったので、次にデプロイしたアプリケーションを探索してみましょう。

ノードはKubernetesではワーカーマシンであり、クラスターに応じてVMまたは物理マシンになります。 複数のPodを1つのノードで実行できます。


5.2.3.2 - 対話型チュートリアル - デプロイしたアプリケーションの探索


ターミナルを使うには、PCまたはタブレットをお使いください

5.2.4 - アプリケーションの公開

5.2.4.1 - Serviceを使ったアプリケーションの公開

目標

  • KubernetesにおけるServiceについて理解する
  • ラベルとLabelSelectorオブジェクトがServiceにどう関係しているかを理解する
  • Serviceを使って、Kubernetesクラスターの外にアプリケーションを公開する

Kubernetes Serviceの概要

Kubernetes Podの寿命は永続的ではありません。実際、Podにはライフサイクルがあります。ワーカーのノードが停止すると、そのノードで実行されているPodも失われます。そうなると、ReplicaSetは、新しいPodを作成してアプリケーションを実行し続けるために、クラスターを動的に目的の状態に戻すことができます。別の例として、3つのレプリカを持つ画像処理バックエンドを考えます。それらのレプリカは交換可能です。フロントエンドシステムはバックエンドのレプリカを気にしたり、Podが失われて再作成されたとしても配慮すべきではありません。ただし、Kubernetesクラスター内の各Podは、同じノード上のPodであっても一意のIPアドレスを持っているため、アプリケーションが機能し続けるように、Pod間の変更を自動的に調整する方法が必要です。

KubernetesのServiceは、Podの論理セットと、それらにアクセスするためのポリシーを定義する抽象概念です。Serviceによって、依存Pod間の疎結合が可能になります。Serviceは、すべてのKubernetesオブジェクトのように、YAML(推奨)またはJSONを使って定義されます。Serviceが対象とするPodのセットは通常、LabelSelectorによって決定されます(なぜ仕様にセレクタを含めずにServiceが必要になるのかについては下記を参照してください)。

各Podには固有のIPアドレスがありますが、それらのIPは、Serviceなしではクラスターの外部に公開されません。Serviceによって、アプリケーションはトラフィックを受信できるようになります。ServiceSpecでtypeを指定することで、Serviceをさまざまな方法で公開することができます。

  • ClusterIP (既定値) - クラスター内の内部IPでServiceを公開します。この型では、Serviceはクラスター内からのみ到達可能になります。
  • NodePort - NATを使用して、クラスター内の選択された各ノードの同じポートにServiceを公開します。<NodeIP>:<NodePort>を使用してクラスターの外部からServiceにアクセスできるようにします。これはClusterIPのスーパーセットです。
  • LoadBalancer - 現在のクラウドに外部ロードバランサを作成し(サポートされている場合)、Serviceに固定の外部IPを割り当てます。これはNodePortのスーパーセットです。
  • ExternalName - 仕様のexternalNameで指定した名前のCNAMEレコードを返すことによって、任意の名前を使ってServiceを公開します。プロキシは使用されません。このタイプはv1.7以上のkube-dnsを必要とします。

さまざまな種類のServiceに関する詳細情報はUsing Source IP tutorialにあります。アプリケーションとServiceの接続も参照してください。

加えて、Serviceには、仕様にselectorを定義しないというユースケースがいくつかあります。selectorを指定せずに作成したServiceについて、対応するEndpointsオブジェクトは作成されません。これによって、ユーザーは手動でServiceを特定のエンドポイントにマッピングできます。セレクタがない可能性があるもう1つの可能性は、type:ExternalNameを厳密に使用していることです。

まとめ

  • Podを外部トラフィックに公開する
  • 複数のPod間でトラフィックを負荷分散する
  • ラベルを使う

Kubernetes Serviceは、Podの論理セットを定義し、それらのPodに対する外部トラフィックの公開、負荷分散、およびサービス検出を可能にする抽象化層です。


Serviceとラベル

Serviceは、一連のPodにトラフィックをルーティングします。Serviceは、アプリケーションに影響を与えることなく、KubernetesでPodが死んだり複製したりすることを可能にする抽象概念です。(アプリケーションのフロントエンドおよびバックエンドコンポーネントなどの)依存Pod間の検出とルーティングは、Kubernetes Serviceによって処理されます。

Serviceは、ラベルとセレクタを使用して一連のPodを照合します。これは、Kubernetes内のオブジェクトに対する論理操作を可能にするグループ化のプリミティブです。ラベルはオブジェクトに付けられたkey/valueのペアであり、さまざまな方法で使用できます。

  • 開発、テスト、および本番用のオブジェクトを指定する
  • バージョンタグを埋め込む
  • タグを使用してオブジェクトを分類する


ラベルは、作成時またはそれ以降にオブジェクトにアタッチでき、いつでも変更可能です。Serviceを使用してアプリケーションを公開し、いくつかのラベルを適用してみましょう。


5.2.4.2 - 対話型チュートリアル - アプリケーションの公開

ターミナルを使うには、PCまたはタブレットをお使いください

5.2.5 - アプリケーションのスケーリング

5.2.5.1 - アプリケーションの複数インスタンスを実行

目標

  • kubectlを使用してアプリケーションをスケールする

アプリケーションのスケーリング

前回のモジュールでは、Deploymentを作成し、それをService経由で公開しました。該当のDeploymentでは、アプリケーションを実行するためのPodを1つだけ作成しました。トラフィックが増加した場合、ユーザーの需要に対応するためにアプリケーションをスケールする必要があります。

スケーリングは、Deploymentのレプリカの数を変更することによって実現可能です。

まとめ

  • Deploymentのスケーリング

kubectl create deploymentコマンドの--replicasパラメーターを使用することで、最初から複数のインスタンスを含むDeploymentを作成できます。


スケーリングの概要


Deploymentをスケールアウトすると、新しいPodが作成され、使用可能なリソースを持つノードにスケジュールされます。スケールすると、Podの数が増えて新たな望ましい状態になります。KubernetesはPodのオートスケーリングもサポートしていますが、このチュートリアルでは範囲外です。スケーリングを0に設定することも可能で、指定された配置のすべてのPodを終了させます。

アプリケーションの複数インスタンスを実行するには、それらすべてにトラフィックを分散する方法が必要になります。Serviceには、公開されたDeploymentのすべてのPodにネットワークトラフィックを分散する統合ロードバランサがあります。Serviceは、エンドポイントを使用して実行中のPodを継続的に監視し、トラフィックが使用可能なPodにのみ送信されるようにします。

スケーリングは、Deploymentのレプリカの数を変更することによって実現可能です。


アプリケーションの複数のインスタンスを実行すると、ダウンタイムなしでローリングアップデートを実行できます。それについては、次のモジュールで学習します。それでは、オンラインのターミナルを使って、アプリケーションをデプロイしてみましょう。


5.2.5.2 - 対話型チュートリアル - アプリケーションのスケーリング

ターミナルを使うには、PCまたはタブレットをお使いください

5.2.6 - アプリケーションのアップデート

5.2.6.1 - ローリングアップデートの実行

目標

  • kubectlを使ってローリングアップデートを実行する

アプリケーションのアップデート

ユーザーはアプリケーションが常に利用可能であることを期待し、開発者はそれらの新しいバージョンを1日に数回デプロイすることが期待されます。Kubernetesでは、アプリケーションのアップデートをローリングアップデートで行います。ローリングアップデートでは、Podインスタンスを新しいインスタンスで段階的にアップデートすることで、ダウンタイムなしでDeploymentをアップデートできます。新しいPodは、利用可能なリソースを持つノードにスケジュールされます。

前回のモジュールでは、複数のインスタンスを実行するようにアプリケーションをデプロイしました。これは、アプリケーションの可用性に影響を与えずにアップデートを行うための要件です。デフォルトでは、アップデート中に使用できなくなる可能性があるPodの最大数と作成できる新しいPodの最大数は1です。どちらのオプションも、Podの数または全体数に対する割合(%)のいずれかに設定できます。Kubernetesでは、アップデートはバージョン管理されており、Deploymentにおけるアップデートは以前の(stable)バージョンに戻すことができます。

まとめ

  • アプリケーションのアップデート

ローリングアップデートでは、Podを新しいインスタンスで段階的にアップデートすることで、ダウンタイムなしDeploymentをアップデートできます。


ローリングアップデートの概要


アプリケーションのスケーリングと同様に、Deploymentがパブリックに公開されている場合、Serviceはアップデート中に利用可能なPodのみにトラフィックを負荷分散します。 利用可能なPodは、アプリケーションのユーザーが利用できるインスタンスです。

ローリングアップデートでは、次の操作が可能です。

  • コンテナイメージのアップデートを介した、ある環境から別の環境へのアプリケーションの昇格
  • 以前のバージョンへのロールバック
  • ダウンタイムなしでのアプリケーションのCI/CD

Deploymentがパブリックに公開されている場合、Serviceはアップデート中に利用可能なPodにのみトラフィックを負荷分散します。


次の対話型チュートリアルでは、アプリケーションを新しいバージョンにアップデートし、ロールバックも実行します。


5.2.6.2 - 対話型チュートリアル - アプリケーションのアップデート

ターミナルを使うには、PCまたはタブレットをお使いください

5.3 - 設定

5.3.1 - ConfigMapを使ったRedisの設定

本ページでは、ConfigMapを使ったコンテナの設定に基づき、ConfigMapを使ってRedisの設定を行う実践的な例を提供します。

目標

  • 以下の要素を含むkustomization.yamlファイルを作成する:
    • ConfigMapGenerator
    • ConfigMapを使ったPodリソースの設定
  • kubectl apply -k ./コマンドにてディレクトリ全体を適用する
  • 設定が正しく反映されていることを確認する

始める前に

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

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

実践例: ConfigMapを使ったRedisの設定

以下の手順に従って、ConfigMapに保存されているデータを使用してRedisキャッシュを設定できます。

最初に、redis-configファイルからConfigMapを含むkustomization.yamlを作成します:

maxmemory 2mb
maxmemory-policy allkeys-lru
curl -OL https://k8s.io/examples/pods/config/redis-config

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-redis-config
  files:
  - redis-config
EOF

Podリソースの設定をkustomization.yamlに入れます:

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: kubernetes/redis:v1
    env:
    - name: MASTER
      value: "true"
    ports:
    - containerPort: 6379
    resources:
      limits:
        cpu: "0.1"
    volumeMounts:
    - mountPath: /redis-master-data
      name: data
    - mountPath: /redis-master
      name: config
  volumes:
    - name: data
      emptyDir: {}
    - name: config
      configMap:
        name: example-redis-config
        items:
        - key: redis-config
          path: redis.conf
curl -OL https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/pods/config/redis-pod.yaml

cat <<EOF >>./kustomization.yaml
resources:
- redis-pod.yaml
EOF

kustomizationディレクトリを反映して、ConfigMapオブジェクトとPodオブジェクトの両方を作成します:

kubectl apply -k .

作成されたオブジェクトを確認します

> kubectl get -k .
NAME                                        DATA   AGE
configmap/example-redis-config-dgh9dg555m   1      52s

NAME        READY   STATUS    RESTARTS   AGE
pod/redis   1/1     Running   0          52s

この例では、設定ファイルのボリュームは/redis-masterにマウントされています。 pathを使ってredis-configキーをredis.confという名前のファイルに追加します。 したがって、redisコンフィグのファイルパスは/redis-master/redis.confです。 ここが、コンテナイメージがredisマスターの設定ファイルを探す場所です。

kubectl execを使ってPodに入り、redis-cliツールを実行して設定が正しく適用されたことを確認してください:

kubectl exec -it redis -- redis-cli
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "2097152"
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "allkeys-lru"

作成したPodを削除します:

kubectl delete pod redis

次の項目

5.4 - ステートレスアプリケーション

5.4.1 - クラスター内のアプリケーションにアクセスするために外部IPアドレスを公開する

このページでは、外部IPアドレスを公開するKubernetesのServiceオブジェクトを作成する方法を示します。

始める前に

  • kubectlをインストールしてください。

  • Kubernetesクラスターを作成する際に、Google Kubernetes EngineやAmazon Web Servicesのようなクラウドプロバイダーを使用します。このチュートリアルでは、クラウドプロバイダーを必要とする外部ロードバランサーを作成します。

  • Kubernetes APIサーバーと通信するために、kubectlを設定してください。手順については、各クラウドプロバイダーのドキュメントを参照してください。

目標

  • 5つのインスタンスで実際のアプリケーションを起動します。
  • 外部IPアドレスを公開するServiceオブジェクトを作成します。
  • 起動中のアプリケーションにアクセスするためにServiceオブジェクトを使用します。

5つのPodで起動しているアプリケーションへのServiceの作成

  1. クラスターにてHello Worldアプリケーションを実行してください。
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: load-balancer-example
  name: hello-world
spec:
  replicas: 5
  selector:
    matchLabels:
      app.kubernetes.io/name: load-balancer-example
  template:
    metadata:
      labels:
        app.kubernetes.io/name: load-balancer-example
    spec:
      containers:
      - image: gcr.io/google-samples/node-hello:1.0
        name: hello-world
        ports:
        - containerPort: 8080
kubectl apply -f https://k8s.io/examples/service/load-balancer-example.yaml

上記のコマンドにより、 Deploymentを作成し、ReplicaSetを関連づけます。ReplicaSetには5つのPodがあり、それぞれHello Worldアプリケーションが起動しています。

  1. Deploymentに関する情報を表示します:

     kubectl get deployments hello-world
     kubectl describe deployments hello-world
    
  2. ReplicaSetオブジェクトに関する情報を表示します:

     kubectl get replicasets
     kubectl describe replicasets
    
  3. Deploymentを公開するServiceオブジェクトを作成します。

     kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
    
  4. Serviceに関する情報を表示します:

     kubectl get services my-service
    

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

     NAME         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
     my-service   LoadBalancer   10.3.245.137   104.198.205.71   8080/TCP   54s
    
  5. Serviceに関する詳細な情報を表示します:

     kubectl describe services my-service
    

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

     Name:           my-service
     Namespace:      default
     Labels:         app.kubernetes.io/name=load-balancer-example
     Annotations:    <none>
     Selector:       app.kubernetes.io/name=load-balancer-example
     Type:           LoadBalancer
     IP:             10.3.245.137
     LoadBalancer Ingress:   104.198.205.71
     Port:           <unset> 8080/TCP
     NodePort:       <unset> 32377/TCP
     Endpoints:      10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080 + 2 more...
     Session Affinity:   None
     Events:         <none>
    

    Serviceによって公開された外部IPアドレス(LoadBalancer Ingress)を記録しておいてください。 この例では、外部IPアドレスは104.198.205.71です。 また、PortおよびNodePortの値も控えてください。 この例では、Portは8080、NodePortは32377です。

  6. 先ほどの出力にて、Serviceにはいくつかのエンドポイントがあることを確認できます: 10.0.0.6:8080、 10.0.1.6:8080、10.0.1.7:8080、その他2つです。 これらはHello Worldアプリケーションが動作しているPodの内部IPアドレスです。 これらのPodのアドレスを確認するには、次のコマンドを実行します:

     kubectl get pods --output=wide
    

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

     NAME                         ...  IP         NODE
     hello-world-2895499144-1jaz9 ...  10.0.1.6   gke-cluster-1-default-pool-e0b8d269-1afc
     hello-world-2895499144-2e5uh ...  10.0.1.8   gke-cluster-1-default-pool-e0b8d269-1afc
     hello-world-2895499144-9m4h1 ...  10.0.0.6   gke-cluster-1-default-pool-e0b8d269-5v7a
     hello-world-2895499144-o4z13 ...  10.0.1.7   gke-cluster-1-default-pool-e0b8d269-1afc
     hello-world-2895499144-segjf ...  10.0.2.5   gke-cluster-1-default-pool-e0b8d269-cpuc
    
  7. Hello Worldアプリケーションにアクセスするために、外部IPアドレス(LoadBalancer Ingress)を使用します:

     curl http://<external-ip>:<port>
    

    ここで、<external-ip>はServiceの外部IPアドレス(LoadBalancer Ingress)で、 <port>はServiceの詳細出力におけるPortです。minikubeを使用している場合、minikube service my-serviceを実行することでHello Worldアプリケーションをブラウザで自動的に 開かれます。

    正常なリクエストに対するレスポンスは、helloメッセージです:

     Hello Kubernetes!
    

クリーンアップ

Serviceを削除する場合、次のコマンドを実行します:

kubectl delete services my-service

Deployment、ReplicaSet、およびHello Worldアプリケーションが動作しているPodを削除する場合、次のコマンドを実行します:

kubectl delete deployment hello-world

次の項目

connecting applications with servicesにて詳細を学ぶことができます。

5.4.2 - 例: Redisを使用したPHPのゲストブックアプリケーションのデプロイ

このチュートリアルでは、KubernetesとDockerを使用した、シンプルなマルチティアのウェブアプリケーションのビルドとデプロイの方法を紹介します。この例は、以下のコンポーネントから構成されています。

  • ゲストブックのエントリーを保存するための、シングルインスタンスのRedisマスター
  • 読み込みデータ配信用の、複数のレプリケーションされたRedisインスタンス
  • 複数のウェブフロントエンドのインスタンス

目標

  • Redisのマスターを起動する。
  • Redisのスレーブを起動する。
  • ゲストブックのフロントエンドを起動する。
  • フロントエンドのServiceを公開して表示を確認する。
  • クリーンアップする。

始める前に

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

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

Redisのマスターを起動する

ゲストブックアプリケーションでは、データを保存するためにRedisを使用します。ゲストブックはRedisのマスターインスタンスにデータを書き込み、複数のRedisのスレーブインスタンスからデータを読み込みます。

RedisのマスターのDeploymentを作成する

以下のマニフェストファイルは、シングルレプリカのRedisのマスターPodを実行するDeploymentコントローラーを指定しています。

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: redis-master
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: master
      tier: backend
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master
        image: registry.k8s.io/redis:e2e  # or just image: redis
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
  1. マニフェストファイルをダウンロードしたディレクトリ内で、ターミナルウィンドウを起動します。

  2. redis-master-deployment.yamlファイルから、RedisのマスターのDeploymentを適用します。

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-master-deployment.yaml
    
  3. Podのリストを問い合わせて、RedisのマスターのPodが実行中になっていることを確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    NAME                            READY     STATUS    RESTARTS   AGE
    redis-master-1068406935-3lswp   1/1       Running   0          28s
    
  4. 次のコマンドを実行して、RedisのマスターのPodからログを表示します。

    kubectl logs -f POD-NAME
    

RedisのマスターのServiceを作成する

ゲストブックアプリケーションは、データを書き込むためにRedisのマスターと通信する必要があります。そのためには、Serviceを適用して、トラフィックをRedisのマスターのPodへプロキシしなければなりません。Serviceは、Podにアクセスするためのポリシーを指定します。

apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
    role: master
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: master
    tier: backend
  1. 次のredis-master-service.yamlから、RedisのマスターのServiceを適用します。

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-master-service.yaml
    
  2. Serviceのリストを問い合わせて、RedisのマスターのServiceが実行中になっていることを確認します。

    kubectl get service
    

    The response should be similar to this:

    NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    1m
    redis-master   ClusterIP   10.0.0.151   <none>        6379/TCP   8s
    

Redisのスレーブを起動する

Redisのマスターは1つのPodですが、レプリカのRedisのスレーブを追加することで、トラフィックの需要を満たすための高い可用性を持たせることができます。

RedisのスレーブのDeploymentを作成する

Deploymentはマニフェストファイル内に書かれた設定に基づいてスケールします。ここでは、Deploymentオブジェクトは2つのレプリカを指定しています。

もし1つもレプリカが実行されていなければ、このDeploymentは2つのレプリカをコンテナクラスター上で起動します。逆に、もしすでに2つ以上のレプリカが実行されていれば、実行中のレプリカが2つになるようにスケールダウンします。

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: redis-slave
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: slave
      tier: backend
  replicas: 2
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: gcr.io/google_samples/gb-redisslave:v3
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # Using `GET_HOSTS_FROM=dns` requires your cluster to
          # provide a dns service. As of Kubernetes 1.3, DNS is a built-in
          # service launched automatically. However, if the cluster you are using
          # does not have a built-in DNS service, you can instead
          # access an environment variable to find the master
          # service's host. To do so, comment out the 'value: dns' line above, and
          # uncomment the line below:
          # value: env
        ports:
        - containerPort: 6379
  1. redis-slave-deployment.yamlファイルから、RedisのスレーブのDeploymentを適用します。

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-slave-deployment.yaml
    
  2. Podのリストを問い合わせて、RedisのスレーブのPodが実行中になっていることを確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    NAME                            READY     STATUS              RESTARTS   AGE
    redis-master-1068406935-3lswp   1/1       Running             0          1m
    redis-slave-2005841000-fpvqc    0/1       ContainerCreating   0          6s
    redis-slave-2005841000-phfv9    0/1       ContainerCreating   0          6s
    

RedisのスレーブのServiceを作成する

ゲストブックアプリケーションは、データを読み込むためにRedisのスレーブと通信する必要があります。Redisのスレーブが発見できるようにするためには、Serviceをセットアップする必要があります。Serviceは一連のPodに対する透過的なロードバランシングを提供します。

apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    app: redis
    role: slave
    tier: backend
spec:
  ports:
  - port: 6379
  selector:
    app: redis
    role: slave
    tier: backend
  1. 次のredis-slave-service.yamlファイルから、RedisのスレーブのServiceを適用します。

    kubectl apply -f https://k8s.io/examples/application/guestbook/redis-slave-service.yaml
    
  2. Serviceのリストを問い合わせて、RedisのスレーブのServiceが実行中になっていることを確認します。

    kubectl get services
    

    結果は次のようになるはずです。

    NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    2m
    redis-master   ClusterIP   10.0.0.151   <none>        6379/TCP   1m
    redis-slave    ClusterIP   10.0.0.223   <none>        6379/TCP   6s
    

ゲストブックのフロントエンドをセットアップして公開する

ゲストブックアプリケーションには、HTTPリクエストをサーブするPHPで書かれたウェブフロントエンドがあります。このアプリケーションは、書き込みリクエストに対してはredis-master Serviceに、読み込みリクエストに対してはredis-slave Serviceに接続するように設定されています。

ゲストブックのフロントエンドのDeploymentを作成する

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: frontend
  labels:
    app: guestbook
spec:
  selector:
    matchLabels:
      app: guestbook
      tier: frontend
  replicas: 3
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google-samples/gb-frontend:v4
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # Using `GET_HOSTS_FROM=dns` requires your cluster to
          # provide a dns service. As of Kubernetes 1.3, DNS is a built-in
          # service launched automatically. However, if the cluster you are using
          # does not have a built-in DNS service, you can instead
          # access an environment variable to find the master
          # service's host. To do so, comment out the 'value: dns' line above, and
          # uncomment the line below:
          # value: env
        ports:
        - containerPort: 80
  1. frontend-deployment.yamlファイルから、フロントエンドのDeploymentを適用します。

    kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
    
  2. Podのリストを問い合わせて、3つのフロントエンドのレプリカが実行中になっていることを確認します。

    kubectl get pods -l app=guestbook -l tier=frontend
    

    結果は次のようになるはずです。

    NAME                        READY     STATUS    RESTARTS   AGE
    frontend-3823415956-dsvc5   1/1       Running   0          54s
    frontend-3823415956-k22zn   1/1       Running   0          54s
    frontend-3823415956-w9gbt   1/1       Running   0          54s
    

フロントエンドのServiceを作成する

適用したredis-slaveおよびredis-master Serviceは、コンテナクラスター内部からのみアクセス可能です。これは、デフォルトのServiceのtypeがClusterIPであるためです。ClusterIPは、Serviceが指している一連のPodに対して1つのIPアドレスを提供します。このIPアドレスはクラスター内部からのみアクセスできます。

もしゲストの人にゲストブックにアクセスしてほしいのなら、フロントエンドServiceを外部から見えるように設定しなければなりません。そうすれば、クライアントはコンテナクラスターの外部からServiceにリクエストを送れるようになります。Minikubeでは、ServiceをNodePortでのみ公開できます。

apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # comment or delete the following line if you want to use a LoadBalancer
  type: NodePort 
  # if your cluster supports it, uncomment the following to automatically create
  # an external load-balanced IP for the frontend service.
  # type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: guestbook
    tier: frontend
  1. frontend-service.yamlファイルから、フロントエンドのServiceを提供します。

    kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
    
  2. Serviceのリストを問い合わせて、フロントエンドのServiceが実行中であることを確認します。

    kubectl get services
    

    結果は次のようになるはずです。

    NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
    frontend       NodePort    10.0.0.112   <none>       80:31323/TCP   6s
    kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP        4m
    redis-master   ClusterIP   10.0.0.151   <none>        6379/TCP       2m
    redis-slave    ClusterIP   10.0.0.223   <none>        6379/TCP       1m
    

フロントエンドのServiceをNodePort経由で表示する

このアプリケーションをMinikubeやローカルのクラスターにデプロイした場合、ゲストブックを表示するためのIPアドレスを見つける必要があります。

  1. 次のコマンドを実行すると、フロントエンドServiceに対するIPアドレスを取得できます。

    minikube service frontend --url
    

    結果は次のようになるはずです。

    http://192.168.99.100:31323
    
  2. IPアドレスをコピーして、ブラウザー上でページを読み込み、ゲストブックを表示しましょう。

フロントエンドのServiceをLoadBalancer経由で表示する

もしfrontend-service.yamlマニフェストをtype: LoadBalancerでデプロイした場合、ゲストブックを表示するためのIPアドレスを見つける必要があります。

  1. 次のコマンドを実行すると、フロントエンドServiceに対するIPアドレスを取得できます。

    kubectl get service frontend
    

    結果は次のようになるはずです。

    NAME       TYPE        CLUSTER-IP      EXTERNAL-IP        PORT(S)        AGE
    frontend   ClusterIP   10.51.242.136   109.197.92.229     80:32372/TCP   1m
    
  2. 外部IPアドレス(EXTERNAL-IP)をコピーして、ブラウザー上でページを読み込み、ゲストブックを表示しましょう。

ウェブフロントエンドをスケールする

サーバーがDeploymentコントローラーを使用するServiceとして定義されているため、スケールアップやスケールダウンは簡単です。

  1. 次のコマンドを実行すると、フロントエンドのPodの数をスケールアップできます。

    kubectl scale deployment frontend --replicas=5
    
  2. Podのリストを問い合わせて、実行中のフロントエンドのPodの数を確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    NAME                            READY     STATUS    RESTARTS   AGE
    frontend-3823415956-70qj5       1/1       Running   0          5s
    frontend-3823415956-dsvc5       1/1       Running   0          54m
    frontend-3823415956-k22zn       1/1       Running   0          54m
    frontend-3823415956-w9gbt       1/1       Running   0          54m
    frontend-3823415956-x2pld       1/1       Running   0          5s
    redis-master-1068406935-3lswp   1/1       Running   0          56m
    redis-slave-2005841000-fpvqc    1/1       Running   0          55m
    redis-slave-2005841000-phfv9    1/1       Running   0          55m
    
  3. 次のコマンドを実行すると、フロントエンドのPodの数をスケールダウンできます。

    kubectl scale deployment frontend --replicas=2
    
  4. Podのリストを問い合わせて、実行中のフロントエンドのPodの数を確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    NAME                            READY     STATUS    RESTARTS   AGE
    frontend-3823415956-k22zn       1/1       Running   0          1h
    frontend-3823415956-w9gbt       1/1       Running   0          1h
    redis-master-1068406935-3lswp   1/1       Running   0          1h
    redis-slave-2005841000-fpvqc    1/1       Running   0          1h
    redis-slave-2005841000-phfv9    1/1       Running   0          1h
    

クリーンアップ

DeploymentとServiceを削除すると、実行中のPodも削除されます。ラベルを使用すると、複数のリソースを1つのコマンドで削除できます。

  1. 次のコマンドを実行すると、すべてのPod、Deployment、Serviceが削除されます。

    kubectl delete deployment -l app=redis
    kubectl delete service -l app=redis
    kubectl delete deployment -l app=guestbook
    kubectl delete service -l app=guestbook
    

    結果は次のようになるはずです。

    deployment.apps "redis-master" deleted
    deployment.apps "redis-slave" deleted
    service "redis-master" deleted
    service "redis-slave" deleted
    deployment.apps "frontend" deleted
    service "frontend" deleted
    
  2. Podのリストを問い合わせて、実行中のPodが存在しないことを確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    No resources found.
    

次の項目

5.4.3 - 例: PHP / Redisを使用したゲストブックの例にロギングとメトリクスを追加する

このチュートリアルは、Redisを使用したPHPのゲストブックのチュートリアルを前提に作られています。Elasticが開発したログ、メトリクス、ネットワークデータを転送するオープンソースの軽量データシッパーであるBeatsを、ゲストブックと同じKubernetesクラスターにデプロイします。BeatsはElasticsearchに対してデータの収集、分析、インデックス作成を行うため、結果の運用情報をKibana上で表示・分析できるようになります。この例は、以下のコンポーネントから構成されます。

目標

  • Redisを使用したPHPのゲストブックを起動する。
  • kube-state-metricsをインストールする。
  • KubernetesのSecretを作成する。
  • Beatsをデプロイする。
  • ログとメトリクスのダッシュボードを表示する。

始める前に

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

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

追加で以下の作業が必要です。

Redisを使用したPHPのゲストブックを起動する

このチュートリアルは、Redisを使用したPHPのゲストブックのチュートリアルを前提に作られています。もしゲストブックアプリケーションが実行中なら、そのアプリケーションを監視できます。もしまだ実行中のアプリケーションがなければ、ゲストブックのデプロイの手順を行い、クリーンアップのステップは実行しないでください。ゲストブックが起動したら、このページに戻ってきてください。

Cluster role bindingを追加する

クラスターレベルのrole bindingを作成して、kube-state-metricsとBeatsをクラスターレベルで(kube-system内に)デプロイできるようにします。

kubectl create clusterrolebinding cluster-admin-binding \
 --clusterrole=cluster-admin --user=<k8sのプロバイダーアカウントと紐付いたあなたのメールアドレス>

kube-state-metricsをインストールする

Kubernetesのkube-state-metricsは、Kubernetes APIサーバーをlistenして、オブジェクトの状態に関するメトリクスを生成する単純なサービスです。Metricbeatはこれらのメトリクスを報告します。kube-state-metricsをゲストブックが実行されているKubernetesクラスターに追加しましょう。

kube-state-metricsが起動しているか確認する

kubectl get pods --namespace=kube-system | grep kube-state

必要に応じてkube-state-metricsをインストールする

git clone https://github.com/kubernetes/kube-state-metrics.git kube-state-metrics
kubectl apply -f kube-state-metrics/examples/standard
kubectl get pods --namespace=kube-system | grep kube-state-metrics

kube-state-metricsがRunningかつreadyの状態になっていることを確認します。

kubectl get pods -n kube-system -l app.kubernetes.io/name=kube-state-metrics

結果は次のようになります。

NAME                                 READY   STATUS    RESTARTS   AGE
kube-state-metrics-89d656bf8-vdthm   1/1     Running     0          21s

GitHubリポジトリのElasticの例をクローンする

git clone https://github.com/elastic/examples.git

これ以降のコマンドはexamples/beats-k8s-send-anywhereディレクトリ内のファイルを参照するため、カレントディレクトリを変更します。

cd examples/beats-k8s-send-anywhere

KubernetesのSecretを作成する

KubernetesのSecretとは、パスワード、トークン、または鍵などの小さなサイズの機密データを含んだオブジェクトのことです。このような機密情報はPodのspecやイメージの中に置くことも不可能ではありませんが、Secretオブジェクトの中に置くことで、情報の使用方法を適切に制御したり、誤って公開してしまうリスクを減らすことができます。

セルフマネージド

Elastic Cloud上のElasticsearch Serviceに接続する場合は、マネージドサービスタブに切り替えてください。

クレデンシャルを設定する

セルフマネージドのElasticsearchとKibanaへ接続する場合、KubernetesのSecretを作成するために編集するべきファイルは4つあります(セルフマネージドとは、事実上Elastic Cloud以外で実行されているElasticsearch Serviceを指します)。ファイルは次の4つです。

  1. ELASTICSEARCH_HOSTS
  2. ELASTICSEARCH_PASSWORD
  3. ELASTICSEARCH_USERNAME
  4. KIBANA_HOST

これらのファイルにElasticsearchクラスターとKibanaホストの情報を設定してください。ここでは例をいくつか示します(こちらの設定も参照してください)。

ELASTICSEARCH_HOSTS

  1. Elastic Elasticsearch Helm Chartで作成したnodeGroupの場合。

    ["http://elasticsearch-master.default.svc.cluster.local:9200"]
    
  2. Mac上で単一のElasticsearchノードが実行されており、BeatsがDocker for Macで実行中の場合。

    ["http://host.docker.internal:9200"]
    
  3. 2ノードのElasticsearchがVM上または物理ハードウェア上で実行中の場合。

    ["http://host1.example.com:9200", "http://host2.example.com:9200"]
    

ELASTICSEARCH_HOSTSを編集します。

vi ELASTICSEARCH_HOSTS

ELASTICSEARCH_PASSWORD

パスワードだけを書きます。空白、クォート、<>などの文字は書かないでください。

<yoursecretpassword>

ELASTICSEARCH_PASSWORDを編集します。

vi ELASTICSEARCH_PASSWORD

ELASTICSEARCH_USERNAME

ユーザー名だけを書きます。空白、クォート、<>などの文字は書かないでください。

<Elasticsearchに追加するユーザー名>

ELASTICSEARCH_USERNAMEを編集します。

vi ELASTICSEARCH_USERNAME

KIBANA_HOST

  1. Elastic Kibana Helm Chartで作成したKibanaインスタンスが実行中の場合。defaultというサブドメインは、default Namespaceを指します。もしHelm Chartを別のNamespaceにデプロイした場合、サブドメインは異なります。

    "kibana-kibana.default.svc.cluster.local:5601"
    
  2. Mac上でKibanaインスタンスが実行中で、BeatsがDocker for Macで実行中の場合。

    "host.docker.internal:5601"
    
  3. 2つのElasticsearchノードが、VMまたは物理ハードウェア上で実行中の場合。

    "host1.example.com:5601"
    

KIBANA_HOSTを編集します。

vi KIBANA_HOST

KubernetesのSecretを作成する

次のコマンドを実行すると、KubernetesのシステムレベルのNamespace(kube-system)に、たった今編集したファイルを元にSecretが作成されます。

kubectl create secret generic dynamic-logging \
  --from-file=./ELASTICSEARCH_HOSTS \
  --from-file=./ELASTICSEARCH_PASSWORD \
  --from-file=./ELASTICSEARCH_USERNAME \
  --from-file=./KIBANA_HOST \
  --namespace=kube-system

マネージドサービス

このタブは、Elastic Cloud上のElasticsearch Serviceの場合のみ必要です。もしセルフマネージドのElasticsearchとKibanaのDeployment向けにSecretをすでに作成した場合、Beatsをデプロイするに進んでください。

クレデンシャルを設定する

Elastic Cloud上のマネージドElasticsearch Serviceに接続する場合、KubernetesのSecretを作成するために編集する必要があるのは、次の2つのファイルです。

  1. ELASTIC_CLOUD_AUTH
  2. ELASTIC_CLOUD_ID

Deploymentを作成するときに、Elasticsearch Serviceのコンソールから提供された情報を設定してください。以下に例を示します。

ELASTIC_CLOUD_ID

devk8s:ABC123def456ghi789jkl123mno456pqr789stu123vwx456yza789bcd012efg345hijj678klm901nop345zEwOTJjMTc5YWQ0YzQ5OThlN2U5MjAwYTg4NTIzZQ==

ELASTIC_CLOUD_AUTH

ユーザー名、コロン(:)、パスワードだけを書きます。空白やクォートは書かないでください。

elastic:VFxJJf9Tjwer90wnfTghsn8w

必要なファイルを編集する

vi ELASTIC_CLOUD_ID
vi ELASTIC_CLOUD_AUTH

KubernetesのSecretを作成する

次のコマンドを実行すると、KubernetesのシステムレベルのNamespace(kube-system)に、たった今編集したファイルを元にSecretが作成されます。

kubectl create secret generic dynamic-logging \
  --from-file=./ELASTIC_CLOUD_ID \
  --from-file=./ELASTIC_CLOUD_AUTH \
  --namespace=kube-system

Beatsをデプロイする

マニフェストファイルはBeatごとに提供されます。これらのマニフェストファイルは、上で作成したSecretを使用して、BeatsをElasticsearchおよびKibanaサーバーに接続するように設定します。

Filebeatについて

Filebeatは、Kubernetesのノードと、ノード上で実行している各Pod内のコンテナから、ログを収集します。FilebeatはDaemonSetとしてデプロイされます。FilebeatはKubernetesクラスター上で実行されているアプリケーションを自動検出することもできます。起動時にFilebeatは既存のコンテナをスキャンし、それらに対して適切な設定を立ち上げ、その後、新しいstart/stopイベントを監視します。

Filebeatが、ゲストブックアプリケーションでデプロイしたRedisコンテナからRedisのログを特定・解析できるように自動検出を設定する例を示します。この設定はfilebeat-kubernetes.yamlファイル内にあります。

- condition.contains:
    kubernetes.labels.app: redis
  config:
    - module: redis
      log:
        input:
          type: docker
          containers.ids:
            - ${data.kubernetes.container.id}
      slowlog:
        enabled: true
        var.hosts: ["${data.host}:${data.port}"]

この設定により、Filebeatは、appラベルにredisという文字列が含まれるコンテナを検出したときにredis Filebeatモジュールを適用するようになります。redisモジュールには、input typeとしてdockerを使用することで(このRedisコンテナの標準出力のストリームと関連付けられた、Kubernetesノード上のファイルを読み取ることで)コンテナからlogストリームを収集する機能があります。さらに、このモジュールには、コンテナのメタデータとして提供された適切なPodのホストとポートと接続することにより、Redisのslowlogエントリーを収集する機能もあります。

Filebeatをデプロイする

kubectl create -f filebeat-kubernetes.yaml

検証する

kubectl get pods -n kube-system -l k8s-app=filebeat-dynamic

Metricbeatについて

Metricbeatの自動検出はFilebeatと同じ方法で設定します。以下にMetricbeatにおけるRedisコンテナの自動検出の設定を示します。この設定はmetricbeat-kubernetes.yamlファイル内にあります。

- condition.equals:
    kubernetes.labels.tier: backend
  config:
    - module: redis
      metricsets: ["info", "keyspace"]
      period: 10s

      # Redis hosts
      hosts: ["${data.host}:${data.port}"]

この設定により、Metricbeatは、tierラベルにbackendという文字列が含まれるコンテナを検出したときにredis Metricbeatモジュールを適用するようになります。redisモジュールには、コンテナのメタデータとして提供された適切なPodのホストとポートと接続することにより、コンテナからinfoおよびkeyspaceメトリクスを収集する機能があります。

Metricbeatをデプロイする

kubectl create -f metricbeat-kubernetes.yaml

検証する

kubectl get pods -n kube-system -l k8s-app=metricbeat

Packetbeatについて

Packetbeatの設定は、FilebeatやMetricbeatとは異なります。コンテナのラベルに対するパターンマッチを指定する代わりに、関連するプロトコルとポート番号に基づいた設定を書きます。以下に示すのは、ポート番号のサブセットです。

packetbeat.interfaces.device: any

packetbeat.protocols:
- type: dns
  ports: [53]
  include_authorities: true
  include_additionals: true

- type: http
  ports: [80, 8000, 8080, 9200]

- type: mysql
  ports: [3306]

- type: redis
  ports: [6379]

packetbeat.flows:
  timeout: 30s
  period: 10s

Packetbeatをデプロイする

kubectl create -f packetbeat-kubernetes.yaml

検証する

kubectl get pods -n kube-system -l k8s-app=packetbeat-dynamic

Kibanaで表示する

ブラウザでKibanaを開き、Dashboardアプリケーションを開きます。検索バーでKubernetesと入力して、KubernetesのためのMetricbeatダッシュボードを開きます。このダッシュボードでは、NodeやDeploymentなどの状態のレポートが表示されます。

DashboardページでPacketbeatと検索し、Packetbeat overviewを表示します。

同様に、ApacheおよびRedisのためのDashboardを表示します。それぞれに対してログとメトリクスのDashboardが表示されます。Apache Metricbeat dashboardには何も表示されていないはずです。Apache Filebeat dashboardを表示して、ページの最下部までスクロールしてApacheのエラーログを確認します。ログを読むと、Apacheのメトリクスが表示されない理由が分かります。

Metricbeatを有効にしてApacheのメトリクスを取得するには、mod-status設定ファイルを含んだConfigMapを追加してゲストブックを再デプロイすることで、server-statusを有効にします。

Deploymentをスケールして新しいPodが監視されるのを確認する

存在するDeploymentを一覧します。

kubectl get deployments

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

NAME            READY   UP-TO-DATE   AVAILABLE   AGE
frontend        3/3     3            3           3h27m
redis-master    1/1     1            1           3h27m
redis-slave     2/2     2            2           3h27m

frontendのPodを2つにスケールダウンします。

kubectl scale --replicas=2 deployment/frontend

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

deployment.extensions/frontend scaled

frontendのPodを再び3つにスケールアップします。

kubectl scale --replicas=3 deployment/frontend

Kibana上で変更を表示する

スクリーンショットを確認し、指定されたフィルターを追加して、ビューにカラムを追加します。赤い枠の右下を見ると、ScalingReplicaSetというエントリーが確認できます。そこからリストを上に見てゆくと、イメージのpull、ボリュームのマウント、Podのスタートなどのイベントが確認できます。

Kibana Discover

クリーンアップ

DeploymentとServiceを削除すると、実行中のすべてのPodも削除されます。ラベルを使って複数のリソースを1つのコマンドで削除します。

  1. 次のコマンドを実行して、すべてのPod、Deployment、Serviceを削除します。

    kubectl delete deployment -l app=redis
    kubectl delete service -l app=redis
    kubectl delete deployment -l app=guestbook
    kubectl delete service -l app=guestbook
    kubectl delete -f filebeat-kubernetes.yaml
    kubectl delete -f metricbeat-kubernetes.yaml
    kubectl delete -f packetbeat-kubernetes.yaml
    kubectl delete secret dynamic-logging -n kube-system
    
  2. Podの一覧を問い合わせて、実行中のPodがなくなったことを確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    No resources found.
    

次の項目

5.5 - セキュリティ

5.5.1 - クラスターレベルでのPodセキュリティの標準の適用

Podセキュリティアドミッション(PSA)は、ベータへ進み、v1.23以降でデフォルトで有効になっています。 Podセキュリティアドミッションは、Podが作成される際に、Podセキュリティの標準の適用の認可を制御するものです。 このチュートリアルでは、クラスター内の全ての名前空間に標準設定を適用することで、クラスターレベルでbaseline Podセキュリティの標準を強制する方法を示します。

Podセキュリティの標準を特定の名前空間に適用するには、名前空間レベルでのPodセキュリティの標準の適用を参照してください。

v1.30以外のKubernetesバージョンを実行している場合は、そのバージョンのドキュメントを確認してください。

始める前に

ワークステーションに以下をインストールしてください:

このチュートリアルでは、完全な制御下にあるKubernetesクラスターの何を設定できるかをデモンストレーションします。 コントロールプレーンを設定できない管理されたクラスターのPodセキュリティアドミッションに対しての設定方法を知りたいのであれば、名前空間レベルでのPodセキュリティの標準の適用を参照してください。

適用する正しいPodセキュリティの標準の選択

Podのセキュリティアドミッションは、以下のモードでビルトインのPodセキュリティの標準の適用を促します: enforceauditwarn。 設定に最適なPodセキュリティの標準を選択するにあたって助けになる情報を収集するために、以下を行ってください:

  1. Podセキュリティの標準を適用していないクラスターを作成します:

    kind create cluster --name psa-wo-cluster-pss
    

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

    Creating cluster "psa-wo-cluster-pss" ...
    ✓ Ensuring node image (kindest/node:v1.30.0) 🖼
    ✓ Preparing nodes 📦
    ✓ Writing configuration 📜
    ✓ Starting control-plane 🕹️
    ✓ Installing CNI 🔌
    ✓ Installing StorageClass 💾
    Set kubectl context to "kind-psa-wo-cluster-pss"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-wo-cluster-pss
    
    Thanks for using kind! 😊
    
  2. kubectl contextを新しいクラスターにセットします:

    kubectl cluster-info --context kind-psa-wo-cluster-pss
    

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

    Kubernetes control plane is running at https://127.0.0.1:61350
    
    CoreDNS is running at https://127.0.0.1:61350/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    
  3. クラスター内の名前空間の一覧を取得します:

    kubectl get ns
    

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

    NAME                 STATUS   AGE
    default              Active   9m30s
    kube-node-lease      Active   9m32s
    kube-public          Active   9m32s
    kube-system          Active   9m32s
    local-path-storage   Active   9m26s
    
  4. 異なるPodセキュリティの標準が適用されたときに何が起きるかを理解するために、-dry-run=serverを使います:

    1. privileged

      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=privileged
      

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

      namespace/default labeled
      namespace/kube-node-lease labeled
      namespace/kube-public labeled
      namespace/kube-system labeled
      namespace/local-path-storage labeled
      
    2. baseline

      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=baseline
      

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

      namespace/default labeled
      namespace/kube-node-lease labeled
      namespace/kube-public labeled
      Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "baseline:latest"
      Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes
      Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes
      Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged
      namespace/kube-system labeled
      namespace/local-path-storage labeled
      
    3. restricted

      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=restricted
      

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

      namespace/default labeled
      namespace/kube-node-lease labeled
      namespace/kube-public labeled
      Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "restricted:latest"
      Warning: coredns-7bb9c7b568-hsptc (and 1 other pod): unrestricted capabilities, runAsNonRoot != true, seccompProfile
      Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true
      Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
      Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
      namespace/kube-system labeled
      Warning: existing pods in namespace "local-path-storage" violate the new PodSecurity enforce level "restricted:latest"
      Warning: local-path-provisioner-d6d9f7ffc-lw9lh: allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfile
      namespace/local-path-storage labeled
      

この出力から、privileged Podセキュリティの標準を適用すると、名前空間のどれにも警告が示されないことに気付くでしょう。 これに対し、baselinerestrictの標準ではどちらも、とりわけkube-system名前空間に対して警告が示されています。

モード、バージョン、標準のセット

このセクションでは、latestバージョンに以下のPodセキュリティの標準を適用します:

  • enforceモードでbaseline標準。
  • warnおよびauditモードでrestricted標準。

baseline Podセキュリティの標準は、免除リストを短く保てて、かつ既知の特権昇格を防ぐような、利便性のある中庸を提供します。

加えて、kube-system内の失敗からPodを守るために、適用されるPodセキュリティの標準の対象から名前空間を免除します。

環境にPodセキュリティアドミッションを実装する際には、以下の点を考慮してください:

  1. クラスターに適用されるリスク状況に基づくと、restrictedのようにより厳格なPodセキュリティの標準のほうが、より良い選択肢かもしれません。

  2. kube-system名前空間の免除は、Podがその名前空間でprivilegedとして実行するのを許容することになります。 実世界で使うにあたっては、以下の最小権限の原則に従ってkube-systemへのアクセスを制限する厳格なRBACポリシーを適用することを、Kubernetesプロジェクトは強く推奨します。 上記の標準を実装するには、次のようにします:

  3. 目的のPodセキュリティの標準を実装するために、Podセキュリティアドミッションコントローラーで利用可能な設定ファイルを作成します:

    mkdir -p /tmp/pss
    cat <<EOF > /tmp/pss/cluster-level-pss.yaml
    apiVersion: apiserver.config.k8s.io/v1
    kind: AdmissionConfiguration
    plugins:
    - name: PodSecurity
      configuration:
        apiVersion: pod-security.admission.config.k8s.io/v1
        kind: PodSecurityConfiguration
        defaults:
          enforce: "baseline"
          enforce-version: "latest"
          audit: "restricted"
          audit-version: "latest"
          warn: "restricted"
          warn-version: "latest"
        exemptions:
          usernames: []
          runtimeClasses: []
          namespaces: [kube-system]
    EOF
    
  4. クラスターの作成中にこのファイルを取り込むAPIサーバーを設定します:

    cat <<EOF > /tmp/pss/cluster-config.yaml
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      kubeadmConfigPatches:
      - |
        kind: ClusterConfiguration
        apiServer:
            extraArgs:
              admission-control-config-file: /etc/config/cluster-level-pss.yaml
            extraVolumes:
              - name: accf
                hostPath: /etc/config
                mountPath: /etc/config
                readOnly: false
                pathType: "DirectoryOrCreate"
      extraMounts:
      - hostPath: /tmp/pss
        containerPath: /etc/config
        # optional: if set, the mount is read-only.
        # default false
        readOnly: false
        # optional: if set, the mount needs SELinux relabeling.
        # default false
        selinuxRelabel: false
        # optional: set propagation mode (None, HostToContainer or Bidirectional)
        # see https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation
        # default None
        propagation: None
    EOF
    
  5. 目的のPodセキュリティの標準を適用するために、Podセキュリティアドミッションを使うクラスターを作成します:

    kind create cluster --name psa-with-cluster-pss --config /tmp/pss/cluster-config.yaml
    

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

    Creating cluster "psa-with-cluster-pss" ...
     ✓ Ensuring node image (kindest/node:v1.30.0) 🖼
     ✓ Preparing nodes 📦
     ✓ Writing configuration 📜
     ✓ Starting control-plane 🕹️
     ✓ Installing CNI 🔌
     ✓ Installing StorageClass 💾
    Set kubectl context to "kind-psa-with-cluster-pss"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-with-cluster-pss
    
    Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
    
  6. kubectlをこのクラスターに向けます:

    kubectl cluster-info --context kind-psa-with-cluster-pss
    

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

    Kubernetes control plane is running at https://127.0.0.1:63855
    CoreDNS is running at https://127.0.0.1:63855/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    
  7. デフォルトの名前空間にPodを作成します:

    kubectl apply -f https://k8s.io/examples/security/example-baseline-pod.yaml
    

    Podは正常に開始されますが、出力には警告が含まれます:

    Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
    pod/nginx created
    

後片付け

では、上記で作成したクラスターを、以下のコマンドを実行して削除します:

kind delete cluster --name psa-with-cluster-pss
kind delete cluster --name psa-wo-cluster-pss

次の項目

5.5.2 - 名前空間レベルでのPodセキュリティの標準の適用

Podセキュリティアドミッション(PSA)は、ベータへ進み、v1.23以降でデフォルトで有効になっています。 Podセキュリティアドミッションは、Podが作成される際に、Podセキュリティの標準の適用の認可を制御するものです。 このチュートリアルでは、一度に1つの名前空間でbaseline Podセキュリティ標準を強制します。

Podセキュリティの標準を複数の名前空間に一度にクラスターレベルで適用することもできます。やり方についてはクラスターレベルでのPodセキュリティの標準の適用を参照してください。

始める前に

ワークステーションに以下をインストールしてください:

クラスターの作成

  1. 以下のようにKinDクラスターを作成します。

    kind create cluster --name psa-ns-level
    

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

    Creating cluster "psa-ns-level" ...
     ✓ Ensuring node image (kindest/node:v1.30.0) 🖼 
     ✓ Preparing nodes 📦  
     ✓ Writing configuration 📜 
     ✓ Starting control-plane 🕹️ 
     ✓ Installing CNI 🔌 
     ✓ Installing StorageClass 💾 
    Set kubectl context to "kind-psa-ns-level"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-ns-level
    
    Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/
    
  2. kubectl のコンテキストを新しいクラスターにセットします:

    kubectl cluster-info --context kind-psa-ns-level
    

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

    Kubernetes control plane is running at https://127.0.0.1:50996
    CoreDNS is running at https://127.0.0.1:50996/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    

名前空間の作成

exampleと呼ぶ新しい名前空間を作成します:

kubectl create ns example

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

namespace/example created

名前空間へのPodセキュリティの標準チェックの有効化

  1. ビルトインのPod Security Admissionでサポートされているラベルを使って、この名前空間のPodセキュリティの標準を有効にします。 このステップでは、baseline Podセキュリティの標準の最新バージョンに合わないPodについて警告するチェックを設定します。

    kubectl label --overwrite ns example \
       pod-security.kubernetes.io/warn=baseline \
       pod-security.kubernetes.io/warn-version=latest
    
  2. ラベルを使って、任意の名前空間に対して複数のPodセキュリティの標準チェックを設定できます。 以下のコマンドは、baseline Podセキュリティの標準をenforce(強制)としますが、restricted Podセキュリティの標準には最新バージョンに準じてwarn(警告)およびaudit(監査)とします(デフォルト値)。

    kubectl label --overwrite ns example \
      pod-security.kubernetes.io/enforce=baseline \
      pod-security.kubernetes.io/enforce-version=latest \
      pod-security.kubernetes.io/warn=restricted \
      pod-security.kubernetes.io/warn-version=latest \
      pod-security.kubernetes.io/audit=restricted \
      pod-security.kubernetes.io/audit-version=latest
    

Podセキュリティの標準の強制の実証

  1. example名前空間内にbaseline Podを作成します:

    kubectl apply -n example -f https://k8s.io/examples/security/example-baseline-pod.yaml
    

    Podは正常に起動しますが、出力には警告が含まれています。例えば:

    Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
    pod/nginx created
    
  2. default名前空間内にbaseline Podを作成します:

    kubectl apply -n default -f https://k8s.io/examples/security/example-baseline-pod.yaml
    

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

    pod/nginx created
    

example名前空間にだけ、Podセキュリティの標準のenforceと警告の設定が適用されました。 default名前空間内では、警告なしに同じPodを作成できました。

後片付け

では、上記で作成したクラスターを、以下のコマンドを実行して削除します:

kind delete cluster --name psa-ns-level

次の項目

5.6 - ステートフルアプリケーション

5.6.1 - StatefulSetの基本

このチュートリアルでは、StatefulSetを使用したアプリケーションを管理するための基本を説明します。StatefulSetのPodを作成、削除、スケール、そして更新する方法について紹介します。

始める前に

このチュートリアルを始める前に、以下のKubernetesの概念について理解しておく必要があります。

目標

StatefulSetはステートフルアプリケーションや分散システムで使用するために存在します。しかし、Kubernetes上のステートフルアプリケーションや分散システムは、広範で複雑なトピックです。StatefulSetの基本的な機能を示すという目的のため、また、ステートフルアプリケーションを分散システムと混同しないようにするために、ここでは、Statefulsetを使用する単純なウェブアプリケーションのデプロイを行います。

このチュートリアルを終えると、以下のことが理解できるようになります。

  • StatefulSetの作成方法
  • StatefulSetがどのようにPodを管理するのか
  • StatefulSetの削除方法
  • StatefulSetのスケール方法
  • StatefulSetが管理するPodの更新方法

StatefulSetを作成する

はじめに、以下の例を使ってStatefulSetを作成しましょう。これは、コンセプトのStatefulSetのページで使ったものと同じような例です。nginxというheadless Serviceを作成し、webというStatefulSet内のPodのIPアドレスを公開します。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

上の例をダウンロードして、web.yamlという名前で保存します。

ここでは、ターミナルウィンドウを2つ使う必要があります。1つ目のターミナルでは、kubectl getを使って、StatefulSetのPodの作成を監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルでは、kubectl applyを使って、web.yamlに定義されたheadless ServiceとStatefulSetを作成します。

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

上のコマンドを実行すると、2つのPodが作成され、それぞれのPodでNGINXウェブサーバーが実行されます。nginxServiceを取得してみましょう。

kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s

そして、webStatefulSetを取得して、2つのリソースの作成が成功したことも確認します。

kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         20s

順序付きPodの作成

n 個のレプリカを持つStatefulSetは、Podをデプロイするとき、1つずつ順番に作成し、 {0..n-1} という順序付けを行います。1つ目のターミナルでkubectl getコマンドの出力を確認しましょう。最終的に、以下の例のような出力が表示されるはずです。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

web-0Podが Running (Pod Phaseを参照)かつ Ready (Pod Conditionstypeを参照)の状態になるまでは、web-1Podが起動していないことに注目してください。

StatefulSet内のPod

StatefulSet内のPodは、ユニークな順序インデックスと安定したネットワーク識別子を持ちます。

Podの順序インデックスを確かめる

StatefulSetのPodを取得します。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

StatefulSetのコンセプトで説明したように、StatefulSet内のPodは安定したユニークな識別子を持ちます。この識別子は、StatefulSetコントローラーによって各Podに割り当てられる、ユニークな順序インデックスに基づいて付けられます。Podの名前は、<statefulsetの名前>-<順序インデックス>という形式です。webStatefulSetは2つのレプリカを持つため、web-0web-1という2つのPodを作成します。

安定したネットワーク識別子の使用

各Podは、順序インデックスに基づいた安定したホスト名を持ちます。kubectl execを使用して、各Pod内でhostnameコマンドを実行してみましょう。

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

kubectl runを使用して、dnsutilsパッケージのnslookupコマンドを提供するコンテナを実行します。Podのホスト名に対してnslookupを実行すると、クラスター内のDNSアドレスが確認できます。

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

これにより、新しいシェルが起動します。新しいシェルで、次のコマンドを実行します。

# このコマンドは、dns-testコンテナのシェルで実行してください
nslookup web-0.nginx

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

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

(コンテナのシェルを終了するために、exitコマンドを実行してください。)

headless serviceのCNAMEは、SRVレコードを指しています(1つのレコードがRunningかつReadyのPodに対応します)。SRVレコードは、PodのIPアドレスを含むAレコードを指します。

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pod -w -l app=nginx

2つ目のターミナルで、kubectl deleteを使用して、StatefulSetのすべてのPodを削除します。

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

StatefulSetがPodを再起動して、2つのPodがRunningかつReadyの状態に移行するのを待ちます。

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

kubectl execkubectl runコマンドを使用して、Podのホスト名とクラスター内DNSエントリーを確認します。まず、Podのホスト名を見てみましょう。

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

その後、次のコマンドを実行します。

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

これにより、新しいシェルが起動します。新しいシェルで、次のコマンドを実行します。

# このコマンドは、dns-testコンテナのシェルで実行してください
nslookup web-0.nginx

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

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

(コンテナのシェルを終了するために、exitコマンドを実行してください。)

Podの順序インデックス、ホスト名、SRVレコード、そしてAレコード名は変化していませんが、Podに紐付けられたIPアドレスは変化する可能性があります。このチュートリアルで使用しているクラスターでは、IPアドレスは変わりました。このようなことがあるため、他のアプリケーションがStatefulSet内のPodに接続するときには、IPアドレスで指定しないことが重要です。

StatefulSetの有効なメンバーを探して接続する必要がある場合は、headless ServiceのCNAME(nginx.default.svc.cluster.local)をクエリしなければなりません。CNAMEに紐付けられたSRVレコードには、StatefulSet内のRunnningかつReadyなPodだけが含まれます。

アプリケーションがlivenessとreadinessをテストするコネクションのロジックをすでに実装している場合、PodのSRVレコード(web-0.nginx.default.svc.cluster.localweb-1.nginx.default.svc.cluster.local)をPodが安定しているものとして使用できます。PodがRunning and Readyな状態に移行すれば、アプリケーションはPodのアドレスを発見できるようになります。

安定したストレージへの書き込み

web-0およびweb-1のためのPersistentVolumeClaimを取得しましょう。

kubectl get pvc -l app=nginx

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

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

StatefulSetコントローラーは、2つのPersistentVolumeにバインドされた2つのPersistentVolumeClaimを作成しています。

このチュートリアルで使用しているクラスターでは、PersistentVolumeの動的なプロビジョニングが設定されているため、PersistentVolumeが自動的に作成されてバインドされています。

デフォルトでは、NGINXウェブサーバーは/usr/share/nginx/html/index.htmlに置かれたindexファイルを配信します。StatefulSetのspec内のvolumeMountsフィールドによって、/usr/share/nginx/htmlディレクトリがPersistentVolume上にあることが保証されます。

Podのホスト名をindex.htmlファイルに書き込むことで、NGINXウェブサーバーがホスト名を配信することを検証しましょう。

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pod -w -l app=nginx

2つ目のターミナルで、StatefulSetのすべてのPodを削除します。

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

1つ目のターミナルでkubectl getコマンドの出力を確認して、すべてのPodがRunningかつReadyの状態に変わるまで待ちます。

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

ウェブサーバーがホスト名を配信し続けていることを確認します。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

もしweb-0およびweb-1が再スケジュールされたとしても、Podは同じホスト名を配信し続けます。これは、PodのPersistentVolumeClaimに紐付けられたPersistentVolumeが、PodのvolumeMountsに再マウントされるためです。web-0web-1がどんなノードにスケジュールされたとしても、PodのPersistentVolumeは適切なマウントポイントにマウントされます。

StatefulSetをスケールする

StatefulSetのスケールとは、レプリカ数を増減することを意味します。これは、replicasフィールドを更新することによって実現できます。StatefulSetのスケールには、kubectl scalekubectl patchのどちらも使用できます。

スケールアップ

1つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、kubectl scaleを使って、レプリカ数を5にスケールします。

kubectl scale sts web --replicas=5
statefulset.apps/web scaled

1つ目のターミナルのkubectl getコマンドの出力を確認して、3つの追加のPodがRunningかつReadyの状態に変わるまで待ちます。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

StatefulSetコントローラーはレプリカ数をスケールします。 StatefulSetを作成するで説明したように、StatefulSetコントローラーは各Podを順序インデックスに従って1つずつ作成し、次のPodを起動する前に、1つ前のPodがRunningかつReadyの状態になるまで待ちます。

スケールダウン

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、kubectl patchコマンドを使用して、StatefulSetを3つのレプリカにスケールダウンします。

kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

web-4およびweb-3がTerminatingの状態になるまで待ちます。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

順序付きPodを削除する

コントローラーは、順序インデックスの逆順に1度に1つのPodを削除し、次のPodを削除する前には、各Podが完全にシャットダウンするまで待機しています。

StatefulSetのPersistentVolumeClaimを取得しましょう。

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

まだ、5つのPersistentVolumeClaimと5つのPersistentVolumeが残っています。安定したストレージへの書き込みを読むと、StatefulSetのPodが削除されても、StatefulSetのPodにマウントされたPersistentVolumeは削除されないと書かれています。このことは、StatefulSetのスケールダウンによってPodが削除された場合にも当てはまります。

StatefulSetsを更新する

Kubernetes 1.7以降では、StatefulSetコントローラーは自動アップデートをサポートしています。使われる戦略は、StatefulSet APIオブジェクトのspec.updateStrategyフィールドによって決まります。この機能はコンテナイメージのアップグレード、リソースのrequestsやlimits、ラベル、StatefulSet内のPodのアノテーションの更新時に利用できます。有効なアップデートの戦略は、RollingUpdateOnDeleteの2種類です。

RollingUpdateは、StatefulSetのデフォルトのアップデート戦略です。

RollingUpdate

RollingUpdateアップデート戦略は、StatefulSetの保証を尊重しながら、順序インデックスの逆順にStatefulSet内のすべてのPodをアップデートします。

webStatefulSetにpatchを当てて、RollingUpdateアップデート戦略を適用しましょう。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
statefulset.apps/web patched

1つ目のターミナルで、webStatefulSetに再度patchを当てて、コンテナイメージを変更します。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.8"}]'
statefulset.apps/web patched

2つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pod -l app=nginx -w

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

NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

StatefulSet内のPodは、順序インデックスの逆順に更新されました。StatefulSetコントローラーは各Podを終了させ、次のPodを更新する前に、新しいPodがRunningかつReadyの状態に変わるまで待機します。ここで、StatefulSetコントローラーは順序インデックスの前のPodがRunningかつReadyの状態になるまで次のPodの更新を始めず、現在の状態へのアップデートに失敗したPodがあった場合、そのPodをリストアすることに注意してください。

すでにアップデートを受け取ったPodは、アップデートされたバージョンにリストアされます。まだアップデートを受け取っていないPodは、前のバージョンにリストアされます。このような方法により、もし途中で失敗が起こっても、コントローラーはアプリケーションが健全な状態を保ち続けられるようにし、更新が一貫したものになるようにします。

Podを取得して、コンテナイメージを確認してみましょう。

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.8
registry.k8s.io/nginx-slim:0.8
registry.k8s.io/nginx-slim:0.8

現在、StatefulSet内のすべてのPodは、前のコンテナイメージを実行しています。

ステージングアップデート

RollingUpdateアップデート戦略にpartitionパラメーターを使用すると、StatefulSetへのアップデートをステージングすることができます。ステージングアップデートを利用すれば、StatefulSet内のすべてのPodを現在のバージョンにしたまま、StatefulSetの.spec.templateを変更することが可能になります。

webStatefulSetにpatchを当てて、updateStrategyフィールドにpartitionを追加しましょう。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched

StatefulSetに再度patchを当てて、コンテナイメージを変更します。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.7"}]'
statefulset.apps/web patched

StatefulSet内のPodを削除します。

kubectl delete pod web-2
pod "web-2" deleted

PodがRunningかつReadyになるまで待ちます。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

Podのコンテナイメージを取得します。

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.8

アップデート戦略がRollingUpdateであっても、StatefulSetが元のコンテナを持つPodをリストアしたことがわかります。これは、Podの順序インデックスがupdateStrategyで指定したpartitionより小さいためです。

カナリア版をロールアウトする

ステージングアップデートのときに指定したpartitionを小さくすることで、変更をテストするためのカナリア版をロールアウトできます。

StatefulSetにpatchを当てて、partitionを小さくします。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched

web-2がRunningかつReadyの状態になるまで待ちます。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

Podのコンテナを取得します。

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.7

partitionを変更すると、StatefulSetコントローラーはPodを自動的に更新します。Podの順序インデックスがpartition以上の値であるためです。

web-1Podを削除します。

kubectl delete pod web-1
pod "web-1" deleted

web-1PodがRunningかつReadyになるまで待ちます。

kubectl get pod -l app=nginx -w

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

NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

web-1Podのコンテナイメージを取得します。

kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.8

Podの順序インデックスがpartitionよりも小さいため、web-1は元の設定のコンテナイメージにリストアされました。partitionを指定すると、StatefulSetの.spec.templateが更新されたときに、順序インデックスがそれ以上の値を持つすべてのPodがアップデートされます。partitionよりも小さな順序インデックスを持つPodが削除されたり終了されたりすると、元の設定のPodにリストアされます。

フェーズロールアウト

カナリア版をロールアウトするのと同じような方法でパーティションされたローリングアップデートを使用すると、フェーズロールアウト(例: 線形、幾何級数的、指数関数的ロールアウト)を実行できます。フェーズロールアウトを実行するには、コントローラーがアップデートを途中で止めてほしい順序インデックスをpartitionに設定します。

現在、partitionは2に設定されています。partitionを0に設定します。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched

StatefulSet内のすべてのPodがRunningかつReadyの状態になるまで待ちます。

kubectl get pod -l app=nginx -w

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

NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

StatefulSet内のPodのコンテナイメージの詳細を取得します。

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.7
registry.k8s.io/nginx-slim:0.7
registry.k8s.io/nginx-slim:0.7

partition0に移動することで、StatefulSetがアップデート処理を続けられるようにできます。

OnDelete

OnDeleteアップデート戦略は、(1.6以前の)レガシーな動作を実装しています。このアップデート戦略を選択すると、StatefulSetの.spec.templateフィールドへ変更を加えても、StatefulSetコントローラーが自動的にPodを更新しなくなります。この戦略を選択するには、.spec.template.updateStrategy.typeOnDeleteを設定します。

StatefulSetを削除する

StatefulSetは、非カスケードな削除とカスケードな削除の両方をサポートしています。非カスケードな削除では、StatefulSetが削除されても、StatefulSet内のPodは削除されません。カスケードな削除では、StatefulSetとPodが一緒に削除されます。

非カスケードな削除

1つ目のターミナルで、StatefulSet内のPodを監視します

kubectl get pods -w -l app=nginx

kubectl deleteを使用して、StatefulSetを削除します。このとき、--cascade=orphanパラメーターをコマンドに与えてください。このパラメーターは、Kubernetesに対して、StatefulSetだけを削除して配下のPodは削除しないように指示します。

kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted

Podを取得して、ステータスを確認します。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

webが削除されても、すべてのPodはまだRunningかつReadyの状態のままです。web-0を削除します。

kubectl delete pod web-0
pod "web-0" deleted

StatefulSetのPodを取得します。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

webStatefulSetはすでに削除されているため、web-0は再起動しません。

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、StatefulSetを再作成します。もしnginxServiceを削除しなかった場合(この場合は削除するべきではありませんでした)、Serviceがすでに存在することを示すエラーが表示されます。

kubectl apply -f web.yaml
statefulset.apps/web created
service/nginx unchanged

このエラーは無視してください。このメッセージは、すでに存在する nginx というheadless Serviceを作成しようと試みたということを示しているだけです。

1つ目のターミナルで、kubectl getコマンドの出力を確認します。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

webStatefulSetが再作成されると、最初にweb-0を再実行します。web-1はすでにRunningかつReadyの状態であるため、web-0がRunningかつReadyの状態に移行すると、StatefulSetは単純にこのPodを選びます。StatefulSetをreplicasを2にして再作成したため、一度web-0が再作成されて、web-1がすでにRunningかつReadyの状態であることが判明したら、web-2は停止されます。

Podのウェブサーバーが配信しているindex.htmlファイルのコンテンツをもう一度見てみましょう。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

たとえStatefulSetとweb-0Podの両方が削除されても、Podは最初にindex.htmlファイルに書き込んだホスト名をまだ配信しています。これは、StatefulSetがPodに紐付けられたPersistentVolumeを削除しないためです。StatefulSetを再作成してweb-0を再実行すると、元のPersistentVolumeが再マウントされます。

カスケードな削除

1つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、StatefulSetをもう一度削除します。今回は、--cascade=orphanパラメーターを省略します。

kubectl delete statefulset web
statefulset.apps "web" deleted

1つ目のターミナルで実行しているkubectl getコマンドの出力を確認し、すべてのPodがTerminatingの状態に変わるまで待ちます。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

スケールダウンのセクションで見たように、順序インデックスの逆順に従って、Podは一度に1つずつ終了します。StatefulSetコントローラーは、次のPodを終了する前に、前のPodが完全に終了するまで待ちます。

kubectl delete service nginx
service "nginx" deleted

さらにもう一度、StatefulSetとheadless Serviceを再作成します。

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

StatefulSet上のすべてのPodがRunningかつReadyの状態に変わったら、Pod上のindex.htmlファイルのコンテンツを取得します。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

StatefulSetを完全に削除して、すべてのPodが削除されたとしても、PersistentVolumeがマウントされたPodが再生成されて、web-0web-1はホスト名の配信を続けます。

最後に、webStatefulSetを削除します。

kubectl delete service nginx
service "nginx" deleted

そして、nginxServiceも削除します。

kubectl delete statefulset web
statefulset "web" deleted

Pod管理ポリシー

分散システムによっては、StatefulSetの順序の保証が不必要であったり望ましくない場合もあります。こうしたシステムでは、一意性と同一性だけが求められます。この問題に対処するために、Kubernetes 1.7でStatefulSet APIオブジェクトに.spec.podManagementPolicyが導入されました。

OrderedReadyのPod管理

OrderedReadyのPod管理はStatefulSetのデフォルトの設定です。StatefulSetコントローラーに対して、これまでに紹介したような順序の保証を尊重するように指示します。

ParallelのPod管理

ParallelのPod管理では、StatefulSetコントローラーに対して、PodがRunningかつReadyの状態や完全に停止するまで待たないように指示し、すべてのPodを並列に起動または停止させるようにします。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

上の例をダウンロードして、web-parallel.yamlという名前でファイルに保存してください。

このマニフェストは、.spec.podManagementPolicyParallelに設定されている以外は、前にダウンロードしたwebStatefulSetと同一です。

1つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pod -l app=nginx -w

2つ目のターミナルで、マニフェスト内のStatefulSetとServiceを作成します。

kubectl apply -f web-parallel.yaml
service/nginx created
statefulset.apps/web created

1つ目のターミナルで実行したkubectl getコマンドの出力を確認します。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s

StatefulSetコントローラーはweb-0web-1を同時に起動しています。

2つ目のターミナルで、StatefulSetをスケールしてみます。

kubectl scale statefulset/web --replicas=4
statefulset.apps/web scaled

kubectl getコマンドを実行しているターミナルの出力を確認します。

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     1/1       Running   0         10s
web-3     1/1       Running   0         26s

StatefulSetが2つのPodを実行し、1つ目のPodがRunningかつReadyの状態になるのを待たずに2つ目のPodを実行しているのがわかります。

クリーンアップ

2つのターミナルが開かれているはずなので、クリーンアップの一部としてkubectlコマンドを実行する準備ができています。

kubectl delete sts web
# stsは、statefulsetの略です。

kubectl getを監視すると、Podが削除されていく様子を確認できます。

kubectl get pod -l app=nginx -w
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

削除の間、StatefulSetはすべてのPodを並列に削除し、順序インデックスが1つ前のPodが停止するのを待つことはありません。

kubectl getコマンドを実行しているターミナルを閉じて、nginxServiceを削除します。

kubectl delete svc nginx

5.6.2 - 例: Persistent Volumeを使用したWordpressとMySQLをデプロイする

このチュートリアルでは、WordPressのサイトとMySQLデータベースをMinikubeを使ってデプロイする方法を紹介します。2つのアプリケーションとも、データを保存するためにPersistentVolumeとPersistentVolumeClaimを使用します。

PersistentVolume(PV)とは、管理者が手動でプロビジョニングを行うか、StorageClassを使ってKubernetesによって動的にプロビジョニングされた、クラスター内のストレージの一部です。PersistentVolumeClaim(PVC)は、PVによって満たすことができる、ユーザーによるストレージへのリクエストのことです。PersistentVolumeとPersistentVolumeClaimは、Podのライフサイクルからは独立していて、Podの再起動、Podの再スケジューリング、さらにはPodの削除が行われたとしても、その中のデータは削除されずに残ります。

目標

  • PersistentVolumeClaimとPersistentVolumeを作成する
  • 以下を含むkustomization.yamlを作成する
    • Secret generator
    • MySQLリソースの設定
    • WordPressリソースの設定
  • kustomizationディレクトリをkubectl apply -k ./で適用する
  • クリーンアップする

始める前に

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

バージョンを確認するには次のコマンドを実行してください: kubectl version. このページで示された例は、kubectl 1.14以降で動作します。

以下の設定ファイルをダウンロードします。

  1. mysql-deployment.yaml

  2. wordpress-deployment.yaml

PersistentVolumeClaimとPersistentVolumeを作成する

MySQLとWordpressはそれぞれ、データを保存するためのPersistentVolumeを必要とします。各PersistentVolumeClaimはデプロイの段階で作成されます。

多くのクラスター環境では、デフォルトのStorageClassがインストールされています。StorageClassがPersistentVolumeClaim中で指定されていなかった場合、クラスターのデフォルトのStorageClassが代わりに使われます。

PersistentVolumeClaimが作成されるとき、StorageClassの設定に基づいてPersistentVolumeが動的にプロビジョニングされます。

kustomization.yamlを作成する

Secret generatorを追加する

Secretとは、パスワードやキーのような機密性の高いデータ片を保存するためのオブジェクトです。バージョン1.14からは、kubectlがkustomizationファイルを使用したKubernetesオブジェクトの管理をサポートしています。kustomization.yaml内のgeneratorによってSecretを作成することができます。

以下のコマンドを実行して、kustomization.yamlの中にSecret generatorを追加します。YOUR_PASSWORDの部分を使いたいパスワードに置換してください。

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
  literals:
  - password=YOUR_PASSWORD
EOF

MySQLとWordPressのためのリソースの設定を追加する

以下のマニフェストには、シングルインスタンスのMySQLのDeploymentが書かれています。MySQLコンテナはPersistentVolumeを/var/lib/mysqlにマウントします。MYSQL_ROOT_PASSWORD環境変数には、Secretから得られたデータベースのパスワードが設定されます。

apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
  clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

以下のマニフェストには、シングルインスタンスのWordPressのDeploymentが書かれています。WordPressコンテナはPersistentVolumeをウェブサイトのデータファイルのために/var/www/htmlにマウントします。WORDPRESS_DB_HOST環境変数に上で定義したMySQLのServiceの名前を設定すると、WordPressはServiceによってデータベースにアクセスします。WORDPRESS_DB_PASSWORD環境変数には、kustomizeが生成したSecretから得たデータベースのパスワードが設定されます。

apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: frontend
  type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
      - image: wordpress:4.8-apache
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wp-pv-claim
  1. MySQLのDeploymentの設定ファイルをダウンロードします。

    curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
    
  2. WordPressの設定ファイルをダウンロードします。

    curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
    
  3. これらをkustomization.yamlファイルに追加します。

cat <<EOF >>./kustomization.yaml
resources:
  - mysql-deployment.yaml
  - wordpress-deployment.yaml
EOF

適用と確認

kustomization.yamlには、WordPressのサイトとMySQLデータベースのためのすべてのリソースが含まれています。次のコマンドでこのディレクトリを適用できます。

kubectl apply -k ./

これで、すべてのオブジェクトが存在していることを確認できます。

  1. 次のコマンドを実行して、Secretが存在していることを確認します。

    kubectl get secrets
    

    結果は次のようになるはずです。

    NAME                    TYPE                                  DATA   AGE
    mysql-pass-c57bb4t7mf   Opaque                                1      9s
    
  2. 次のコマンドを実行して、PersistentVolumeが動的にプロビジョニングされていることを確認します。

    kubectl get pvc
    

    結果は次のようになるはずです。

    NAME             STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
    mysql-pv-claim   Bound     pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    wp-pv-claim      Bound     pvc-8cd0df54-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    
  3. 次のコマンドを実行して、Podが実行中であることを確認します。

    kubectl get pods
    

    結果は次のようになるはずです。

    NAME                               READY     STATUS    RESTARTS   AGE
    wordpress-mysql-1894417608-x5dzt   1/1       Running   0          40s
    
  4. 次のコマンドを実行して、Serviceが実行中であることを確認します。

    kubectl get services wordpress
    

    結果は次のようになるはずです。

    NAME        TYPE            CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
    wordpress   LoadBalancer    10.0.0.89    <pending>     80:32406/TCP   4m
    
  5. 次のコマンドを実行して、WordPress ServiceのIPアドレスを取得します。

    minikube service wordpress --url
    

    結果は次のようになるはずです。

    http://1.2.3.4:32406
    
  6. IPアドレスをコピーして、ブラウザーで読み込み、サイトを表示しましょう。

    WordPressによりセットアップされた次のスクリーンショットのようなページが表示されるはずです。

    wordpress-init

クリーンアップ

  1. 次のコマンドを実行して、Secret、Deployment、Service、およびPersistentVolumeClaimを削除します。

    kubectl delete -k ./
    

次の項目

5.6.3 - 例: StatefulSetを使用したCassandraのデプロイ

このチュートリアルでは、Apache CassandraをKubernetes上で実行する方法を紹介します。 データベースの一種であるCassandraには、データの耐久性(アプリケーションの 状態)を提供するために永続ストレージが必要です。 この例では、カスタムのCassandraのseed providerにより、Cassandraクラスターに参加した新しいCassandraインスタンスを検出できるようにします。

StatefulSetを利用すると、ステートフルなアプリケーションをKubernetesクラスターにデプロイするのが簡単になります。 このチュートリアルで使われている機能のより詳しい情報は、StatefulSetを参照してください。

目標

  • Cassandraのheadless Serviceを作成して検証する。
  • StatefulSetを使用してCassandra ringを作成する。
  • StatefulSetを検証する。
  • StatefulSetを編集する。
  • StatefulSetとPodを削除する。

始める前に

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

このチュートリアルを完了するには、PodServiceStatefulSetの基本についてすでに知っている必要があります。

Minikubeのセットアップに関する追加の設定手順

Cassandraのheadless Serviceを作成する

Kubernetesでは、Serviceは同じタスクを実行するPodの集合を表します。

以下のServiceは、Cassandra Podとクラスター内のクライアント間のDNSルックアップに使われます:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandra
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: cassandra

cassandra-service.yamlファイルから、Cassandra StatefulSetのすべてのメンバーをトラッキングするServiceを作成します。

kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml

検証 (オプション)

Cassandra Serviceを取得します。

kubectl get svc cassandra

結果は次のようになります。

NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
cassandra   ClusterIP   None         <none>        9042/TCP   45s

cassandraという名前のServiceが表示されない場合、作成に失敗しています。よくある問題のトラブルシューティングについては、Serviceのデバッグを読んでください。

StatefulSetを使ってCassandra ringを作成する

以下に示すStatefulSetマニフェストは、3つのPodからなるCassandra ringを作成します。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  selector:
    matchLabels:
      app: cassandra
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v13
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
            cpu: "500m"
            memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: 
              - /bin/sh
              - -c
              - nodetool drain
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: "K8Demo"
          - name: CASSANDRA_DC
            value: "DC1-K8Demo"
          - name: CASSANDRA_RACK
            value: "Rack1-K8Demo"
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        # These volume mounts are persistent. They are like inline claims,
        # but not exactly because the names need to match exactly one of
        # the stateful pod volumes.
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  # These are converted to volume claims by the controller
  # and mounted at the paths mentioned above.
  # do not use these in production until ssd GCEPersistentDisk or other ssd pd
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: fast
      resources:
        requests:
          storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

cassandra-statefulset.yamlファイルから、CassandraのStatefulSetを作成します:

# cassandra-statefulset.yaml を編集せずにapplyできる場合は、このコマンドを使用してください
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml

クラスターに合わせてcassandra-statefulset.yamlを編集する必要がある場合、 https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml をダウンロードして、修正したバージョンを保存したフォルダからマニフェストを適用してください。

# cassandra-statefulset.yaml をローカルで編集する必要がある場合、このコマンドを使用してください
kubectl apply -f cassandra-statefulset.yaml

CassandraのStatefulSetを検証する

  1. CassandraのStatefulSetを取得します

    kubectl get statefulset cassandra
    

    結果は次のようになるはずです:

    NAME        DESIRED   CURRENT   AGE
    cassandra   3         0         13s
    

    StatefulSetリソースがPodを順番にデプロイします。

  2. Podを取得して順序付きの作成ステータスを確認します

    kubectl get pods -l="app=cassandra"
    

    結果は次のようになるはずです:

    NAME          READY     STATUS              RESTARTS   AGE
    cassandra-0   1/1       Running             0          1m
    cassandra-1   0/1       ContainerCreating   0          8s
    

    3つすべてのPodのデプロイには数分かかる場合があります。デプロイが完了すると、同じコマンドは次のような結果を返します:

    NAME          READY     STATUS    RESTARTS   AGE
    cassandra-0   1/1       Running   0          10m
    cassandra-1   1/1       Running   0          9m
    cassandra-2   1/1       Running   0          8m
    
  3. 1番目のPodの中でCassandraのnodetoolを実行して、ringのステータスを表示します。

    kubectl exec -it cassandra-0 -- nodetool status
    

    結果は次のようになるはずです:

    Datacenter: DC1-K8Demo
    ======================
    Status=Up/Down
    |/ State=Normal/Leaving/Joining/Moving
    --  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
    UN  172.17.0.5  83.57 KiB  32           74.0%             e2dd09e6-d9d3-477e-96c5-45094c08db0f  Rack1-K8Demo
    UN  172.17.0.4  101.04 KiB  32           58.8%             f89d6835-3a42-4419-92b3-0e62cae1479c  Rack1-K8Demo
    UN  172.17.0.6  84.74 KiB  32           67.1%             a6a1e8c2-3dc5-4417-b1a0-26507af2aaad  Rack1-K8Demo
    

CassandraのStatefulSetを変更する

kubectl editを使うと、CassandraのStatefulSetのサイズを変更できます。

  1. 次のコマンドを実行します。

    kubectl edit statefulset cassandra
    

    このコマンドを実行すると、ターミナルでエディタが起動します。変更が必要な行はreplicasフィールドです。 以下の例は、StatefulSetファイルの抜粋です:

    # Please edit the object below. Lines beginning with a '#' will be ignored,
    # and an empty file will abort the edit. If an error occurs while saving this file will be
    # reopened with the relevant failures.
    #
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      creationTimestamp: 2016-08-13T18:40:58Z
      generation: 1
      labels:
      app: cassandra
      name: cassandra
      namespace: default
      resourceVersion: "323"
      uid: 7a219483-6185-11e6-a910-42010a8a0fc0
    spec:
      replicas: 3
    
  2. レプリカ数を4に変更し、マニフェストを保存します。

    これで、StatefulSetが4つのPodを実行するようにスケールされました。

  3. CassandraのStatefulSetを取得して、変更を確かめます:

    kubectl get statefulset cassandra
    

    結果は次のようになるはずです:

    NAME        DESIRED   CURRENT   AGE
    cassandra   4         4         36m
    

クリーンアップ

StatefulSetを削除したりスケールダウンしても、StatefulSetに関係するボリュームは削除されません。 StatefulSetに関連するすべてのリソースを自動的に破棄するよりも、データの方がより貴重であるため、安全のためにこのような設定になっています。

  1. 次のコマンドを実行して(単一のコマンドにまとめています)、CassandraのStatefulSetに含まれるすべてのリソースを削除します:

    grace=$(kubectl get pod cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
      && kubectl delete statefulset -l app=cassandra \
      && echo "Sleeping ${grace} seconds" 1>&2 \
      && sleep $grace \
      && kubectl delete persistentvolumeclaim -l app=cassandra
    
  2. 次のコマンドを実行して、CassandraをセットアップしたServiceを削除します:

    kubectl delete service -l app=cassandra
    

Cassandraコンテナの環境変数

このチュートリアルのPodでは、Googleのコンテナレジストリgcr.io/google-samples/cassandra:v13イメージを使用しました。このDockerイメージはdebian-baseをベースにしており、OpenJDK 8が含まれています。

このイメージには、Apache Debianリポジトリの標準のCassandraインストールが含まれます。 環境変数を利用すると、cassandra.yamlに挿入された値を変更できます。

環境変数デフォルト値
CASSANDRA_CLUSTER_NAME'Test Cluster'
CASSANDRA_NUM_TOKENS32
CASSANDRA_RPC_ADDRESS0.0.0.0

次の項目

5.6.4 - 分散システムコーディネーターZooKeeperの実行

このチュートリアルでは、StatefulSetPodDisruptionBudgetsPodアンチアフィニティを使って、Kubernetes上でのApache Zookeeperの実行をデモンストレーションします。

始める前に

このチュートリアルを始める前に、以下のKubernetesの概念について理解しておく必要があります。

少なくとも4つのノードのクラスターが必要で、各ノードは少なくとも2つのCPUと4GiBのメモリが必須です。このチュートリアルでは、クラスターのノードをcordonおよびdrainします。 つまり、クラスターがそのノードの全てのPodを終了して退去させ、ノードが一時的にスケジュールできなくなる、ということです。 このチュートリアル専用のクラスターを使うか、起こした破壊がほかのテナントに干渉しない確証を得ることをお勧めします。

このチュートリアルでは、クラスターがPersistentVolumeの動的なプロビジョニングが行われるように設定されていることを前提としています。 クラスターがそのように設定されていない場合、チュートリアルを始める前に20GiBのボリュームを3つ、手動でプロビジョニングする必要があります。

目標

このチュートリアルを終えると、以下の知識を得られます。

  • StatefulSetを使ってZooKeeperアンサンブルをデプロイする方法。
  • アンサンブルを一貫して設定する方法。
  • ZooKeeperサーバーのデプロイをアンサンブルに広げる方法。
  • 計画されたメンテナンス中もサービスが利用可能であることを保証するためにPodDisruptionBudgetsを使う方法。

ZooKeeper

Apache ZooKeeperは、分散アプリケーションのための、分散型オープンソースコーディネーションサービスです。 ZooKeeperでは、データの読み書き、および更新の監視ができます。 データは階層化されてファイルシステム内に編成され、アンサンブル(ZooKeeperサーバーのセット)内の全てのZooKeeperサーバーに複製されます。 データへの全ての操作はアトミックかつ逐次的に一貫性を持ちます。 ZooKeeperは、アンサンブル内の全てのサーバー間でステートマシンを複製するためにZab合意プロトコルを使ってこれを保証します。

アンサンブルはリーダーを選出するのにZabプロトコルを使い、選出が完了するまでデータを書き出しません。 完了すると、アンサンブルは複製するのにZabを使い、書き込みが承認されてクライアントに可視化されるより前に、全ての書き込みをクォーラムに複製することを保証します。 重み付けされたクォーラムでなければ、クォーラムは現在のリーダーを含むアンサンブルの過半数を占めるコンポーネントです。 例えばアンサンブルが3つのサーバーを持つ時、リーダーとそれ以外のもう1つのサーバーを含むコンポーネントが、クォーラムを構成します。 アンサンブルがクォーラムに達しない場合、アンサンブルはデータを書き出せません。

ZooKeeperサーバー群はそれらの全てのステートマシンをメモリに保持し、それぞれの変化をストレージメディア上の永続的なWAL(Write Ahead Log)に書き出します。 サーバーがクラッシュした時には、WALをリプレーすることで以前のステートに回復できます。 WALを際限のない増加から防ぐために、ZooKeeperサーバーは、メモリステートにあるものをストレージメディアに定期的にスナップショットします。 これらのスナップショットはメモリに直接読み込むことができ、スナップショットより前の全てのWALエントリは破棄され得ます。

ZooKeeperアンサンブルの作成

以下のマニフェストはHeadless ServiceServicePodDisruptionBudgetStatefulSetを含んでいます。

apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  labels:
    app: zk
spec:
  ports:
  - port: 2888
    name: server
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  labels:
    app: zk
spec:
  ports:
  - port: 2181
    name: client
  selector:
    app: zk
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  selector:
    matchLabels:
      app: zk
  maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zk
spec:
  selector:
    matchLabels:
      app: zk
  serviceName: zk-hs
  replicas: 3
  updateStrategy:
    type: RollingUpdate
  podManagementPolicy: OrderedReady
  template:
    metadata:
      labels:
        app: zk
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: kubernetes-zookeeper
        imagePullPolicy: Always
        image: "registry.k8s.io/kubernetes-zookeeper:1.0-3.4.10"
        resources:
          requests:
            memory: "1Gi"
            cpu: "0.5"
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: server
        - containerPort: 3888
          name: leader-election
        command:
        - sh
        - -c
        - "start-zookeeper \
          --servers=3 \
          --data_dir=/var/lib/zookeeper/data \
          --data_log_dir=/var/lib/zookeeper/data/log \
          --conf_dir=/opt/zookeeper/conf \
          --client_port=2181 \
          --election_port=3888 \
          --server_port=2888 \
          --tick_time=2000 \
          --init_limit=10 \
          --sync_limit=5 \
          --heap=512M \
          --max_client_cnxns=60 \
          --snap_retain_count=3 \
          --purge_interval=12 \
          --max_session_timeout=40000 \
          --min_session_timeout=4000 \
          --log_level=INFO"
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "zookeeper-ready 2181"
          initialDelaySeconds: 10
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: datadir
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi

ターミナルを開き、マニフェストを作成するために kubectl applyコマンドを使います。

kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml

これはzk-hs Headless Service、zk-cs Service、zk-pdb PodDisruptionBudget、 zk StatefulSetを作成します。

service/zk-hs created
service/zk-cs created
poddisruptionbudget.policy/zk-pdb created
statefulset.apps/zk created

StatefulSetのPodを作成するStatefulSetコントローラーを監視するため、kubectl getを使います。

kubectl get pods -w -l app=zk

zk-2 PodがRunningおよびReadyになったら、CTRL-Cでkubectlを終了してください。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

StatefulSetコントローラーが3つのPodを作成し、各PodはZooKeeperサーバー付きのコンテナを持ちます。

リーダーの選出のファシリテート

匿名のネットワークにおいてリーダー選出を終了するアルゴリズムがないので、Zabはリーダー選出のための明示的なメンバーシップ設定を要します。 アンサンブルの各サーバーはユニーク識別子を持つ必要があり、全てのサーバーは識別子のグローバルセットを知っている必要があり、各識別子はネットワークアドレスと関連付けられている必要があります。

zk StatefulSetのPodのホスト名を取得するためにkubectl execを使います。

for i in 0 1 2; do kubectl exec zk-$i -- hostname; done

StatefulSetコントローラーは各Podに、その順序インデックスに基づくユニークなホスト名を提供します。 ホスト名は<statefulset名>-<順序インデックス>という形をとります。 zk StatefulSetのreplicasフィールドが3にセットされているので、このセットのコントローラーは、ホスト名にそれぞれzk-0zk-1zk-2が付いた3つのPodを作成します。

zk-0
zk-1
zk-2

ZooKeeperアンサンブルのサーバーは、ユニーク識別子として自然数を使い、それぞれのサーバーの識別子をサーバーのデータディレクトリ内のmyidというファイルに格納します。

各サーバーのmyidファイルの内容を調べるには、以下のコマンドを使います。

for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done

識別子が自然数で順序インデックスは正の整数なので、順序に1を加算することで識別子を生成できます。

myid zk-0
1
myid zk-1
2
myid zk-2
3

zk StatefulSet内の各Podの完全修飾ドメイン名(FQDN)を取得するには、以下のコマンドを使います。

for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done

zk-hs Serviceは、全Podのためのドメインzk-hs.default.svc.cluster.localを作成します。

zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local

Kubernetes DNSのAレコードは、FQDNをPodのIPアドレスに解決します。 KubernetesがPodを再スケジュールした場合、AレコードはPodの新しいIPアドレスに更新されますが、Aレコードの名前は変更されません。

ZooKeeperはそのアプリケーション設定をzoo.cfgという名前のファイルに格納します。 zk-0 Pod内のzoo.cfgファイルの内容を見るには、kubectl execを使います。

kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg

ファイル末尾にあるserver.1server.2server.3のプロパティの、123はZooKeeperサーバーのmyidファイル内の識別子に対応します。 これらはzk StatefulSet内のPodのFQDNにセットされます。

clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888

合意形成

合意(consensus)プロトコルは、各参加者の識別子がユニークであることを要件としています。 Zabプロトコル内で同じユニーク識別子を主張する2つの参加者はないものとします。 これは、システム内のプロセスが、どのプロセスがどのデータをコミットしたかを同意できるようにするために必須です。 2つのPodが同じ順序値で起動されたなら、2つのZooKeeperサーバーはどちらもそれら自身を同じサーバーとして認識してしまうでしょう。

kubectl get pods -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

各PodのAレコードは、PodがReadyになった時に記入されます。そのため、ZooKeeperサーバー群のFQDNはある1つのエンドポイント、すなわちmyidファイルで設定された識別子を主張するユニークなZooKeeperサーバーに解決されます。

zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local

これは、ZooKeeperのzoo.cfgファイル内のserversプロパティが正しく設定されたアンサンブルを表していることを保証します。

server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888

サーバーが値のコミットを試みるためにZabプロトコルを使う時、(リーダー選出が成功していて、少なくともPodのうちの2つがRunningおよびReadyならば)それぞれのサーバーは双方の合意をとって値をコミット、あるいは、(もし双方の状態が合わなければ)それを行うことに失敗します。 あるサーバーが別のサーバーを代行して書き込みを承認する状態は発生しません。

アンサンブルの健全性テスト

最も基本的な健全性テストは、データを1つのZooKeeperサーバーに書き込み、そのデータを別のサーバーで読み取ることです。

以下のコマンドは、worldをアンサンブル内のzk-0 Podのパス/helloに書き込むのに、zkCli.shスクリプトを実行します。

kubectl exec zk-0 -- zkCli.sh create /hello world
WATCHER::

WatchedEvent state:SyncConnected type:None path:null
Created /hello

zk-1 Podからデータを取得するには、以下のコマンドを使います。

kubectl exec zk-1 -- zkCli.sh get /hello

zk-0に作成したデータは、アンサンブル内の全てのサーバーで利用できます。

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

永続的なストレージの提供

ZooKeeperの概要のセクションで言及したように、 ZooKeeperは全てのエントリを永続的なWALにコミットし、定期的にメモリ状態のスナップショットをストレージメディアに書き出します。 永続性を提供するためにWALを使用するのは、複製されたステートマシンを立てるために合意プロトコルを使うアプリケーションでよくあるテクニックです。

zk StatefulSetを削除するために、kubectl deleteコマンドを使います。

kubectl delete statefulset zk
statefulset.apps "zk" deleted

StatefulSet内のPodの終了を観察します。

kubectl get pods -w -l app=zk

zk-0が完全に終了したら、CTRL-Cでkubectlを終了します。

zk-2      1/1       Terminating   0         9m
zk-0      1/1       Terminating   0         11m
zk-1      1/1       Terminating   0         10m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m

zookeeper.yamlのマニフェストを再適用します。

kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml

これはzk StatefulSetオブジェクトを作成しますが、マニフェストのその他のAPIオブジェクトはすでに存在しているので変更されません。

StatefulSetコントローラーがStatefulSetのPodを再作成するのを見てみます。

kubectl get pods -w -l app=zk

zk-2 PodがRunningおよびReadyになったら、CTRL-Cでkubectlを終了します。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s

健全性テストで入力した値をzk-2 Podから取得するには、以下のコマンドを使います。

kubectl exec zk-2 zkCli.sh get /hello

zk StatefulSet内の全てのPodを終了して再作成したにもかかわらず、アンサンブルは元の値をなおも供給します。

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

zk StatefulSetのspecvolumeClaimTemplatesフィールドは、各PodにプロビジョニングされるPersistentVolumeを指定します。

volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi

StatefulSetコントローラーは、StatefulSet内の各PodのためにPersistentVolumeClaimを生成します。

StatefulSetPersistentVolumeClaimsを取得するために、以下のコマンドを使います。

kubectl get pvc -l app=zk

StatefulSetがそのPodを再作成した時、StatefulSetはPodのPersistentVolumeを再マウントします。

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
datadir-zk-0   Bound     pvc-bed742cd-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-1   Bound     pvc-bedd27d2-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-2   Bound     pvc-bee0817e-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h

StatefulSetのコンテナtemplatevolumeMountsセクションは、ZooKeeperサーバーのデータディレクトリにあるPersistentVolumeをマウントします。

volumeMounts:
- name: datadir
  mountPath: /var/lib/zookeeper

zk StatefulSet内のPodが(再)スケジュールされると、ZooKeeperサーバーのデータディレクトリにマウントされた同じPersistentVolumeを常に得ます。 Podが再スケジュールされたとしても、全ての書き込みはZooKeeperサーバーのWALおよび全スナップショットに行われ、永続性は残ります。

一貫性のある設定の保証

リーダーの選出のファシリテートおよび合意形成のセクションで記したように、ZooKeeperのアンサンブル内のサーバー群は、リーダーを選出しクォーラムを形成するための一貫性のある設定を必要とします。 また、プロトコルがネットワーク越しで正しく動作するために、Zabプロトコルの一貫性のある設定も必要です。 この例では、設定を直接マニフェストに埋め込むことで一貫性のある設定を達成します。

zk StatefulSetを取得しましょう。

kubectl get sts zk -o yaml
…
command:
      - sh
      - -c
      - "start-zookeeper \
        --servers=3 \
        --data_dir=/var/lib/zookeeper/data \
        --data_log_dir=/var/lib/zookeeper/data/log \
        --conf_dir=/opt/zookeeper/conf \
        --client_port=2181 \
        --election_port=3888 \
        --server_port=2888 \
        --tick_time=2000 \
        --init_limit=10 \
        --sync_limit=5 \
        --heap=512M \
        --max_client_cnxns=60 \
        --snap_retain_count=3 \
        --purge_interval=12 \
        --max_session_timeout=40000 \
        --min_session_timeout=4000 \
        --log_level=INFO"
…

このcommandでは、ZooKeeperサーバーを開始するためにコマンドラインパラメータで設定を渡しています。 設定をアンサンブルへ渡すのには環境変数を使うこともできます。

ログの設定

zkGenConfig.shスクリプトで生成されたファイルの1つは、ZooKeeperのログを制御します。 ZooKeeperはLog4jを使い、デフォルトではログの設定に基づいて、ログ設定に時間およびサイズベースでのローリングファイルアペンダー(ログのローテーション)を使用します。

zk StatefulSet内のPodの1つからログ設定を取得するには、以下のコマンドを使います。

kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties

以下のログ設定は、ZooKeeperにログの全てを標準出力ファイルストリームに書き出す処理をさせます。

zookeeper.root.logger=CONSOLE
zookeeper.console.threshold=INFO
log4j.rootLogger=${zookeeper.root.logger}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n

これはログコンテナ内のログを安全にとるための、最もシンプルと思われる方法です。 アプリケーションはログを標準出力に書き出し、Kubernetesがログのローテーションを処理してくれます。 Kubernetesは、標準出力と標準エラー出力に書き出されるアプリケーションのログがローカルストレージメディアを使い尽くさないことを保証する、健全維持ポリシーも実装しています。

Podの1つから末尾20行を取得するために、kubectl logsを使ってみます。

kubectl logs zk-0 --tail 20

kubectl logsを利用するか、Kubernetes Dashboardから、標準出力または標準エラーに書き出されたアプリケーションログを参照できます。

2016-12-06 19:34:16,236 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740
2016-12-06 19:34:16,237 [myid:1] - INFO  [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client)
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749
2016-12-06 19:34:26,156 [myid:1] - INFO  [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client)
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750
2016-12-06 19:34:26,226 [myid:1] - INFO  [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client)
2016-12-06 19:34:36,151 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client)
2016-12-06 19:34:36,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client)
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client)
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client)

Kubernetesは多くのログソリューションを統合しています。 クラスターおよびアプリケーションに最も適合するログソリューションを選べます。 クラスターレベルのロギングとアグリゲーションとして、ログをローテートおよび輸送するためのサイドカーコンテナをデプロイすることを検討してください。

非特権ユーザーの設定

コンテナ内で特権ユーザーとしての実行をアプリケーションに許可するベストプラクティスは、議論の的です。 アプリケーションが非特権ユーザーとして動作することを組織で必須としているなら、エントリポイントがそのユーザーとして実行できるユーザーを制御するセキュリティコンテキストを利用できます。

zk StatefulSetのPod templateは、SecurityContextを含んでいます。

securityContext:
  runAsUser: 1000
  fsGroup: 1000

Podのコンテナ内で、UID 1000はzookeeperユーザーに、GID 1000はzookeeperグループにそれぞれ相当します。

zk-0 PodからのZooKeeperプロセス情報を取得してみましょう。

kubectl exec zk-0 -- ps -elf

securityContextオブジェクトのrunAsUserフィールドが1000にセットされているとおり、ZooKeeperプロセスは、rootとして実行される代わりにzookeeperユーザーとして実行されています。

F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S zookeep+     1     0  0  80   0 -  1127 -      20:46 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
0 S zookeep+    27     1  0  80   0 - 1155556 -    20:46 ?        00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg

デフォルトでは、PodのPersistentVolumeがZooKeeperサーバーのデータディレクトリにマウントされている時、rootユーザーのみがそこにアクセス可能です。 この設定はZooKeeperのプロセスがそのWALに書き込んだりスナップショットに格納したりするのを妨げることになります。

zk-0 PodのZooKeeperデータディレクトリのファイルパーミッションを取得するには、以下のコマンドを使います。

kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data

securityContextオブジェクトのfsGroupフィールドが1000にセットされているので、PodのPersistentVolumeの所有権はzookeeperグループにセットされ、ZooKeeperのプロセスがそのデータを読み書きできるようになります。

drwxr-sr-x 3 zookeeper zookeeper 4096 Dec  5 20:45 /var/lib/zookeeper/data

ZooKeeperプロセスの管理

ZooKeeperドキュメントでは、「You will want to have a supervisory process that manages each of your ZooKeeper server processes (JVM).(各ZooKeeperサーバープロセス(JVM)を管理する監督プロセスを持ちたくなります)」と述べています。 分散型システム内で失敗したプロセスを再起動するのにwatchdog(監督プロセス)を使うのは、典型的パターンです。 アプリケーションをKubernetesにデプロイする時には、監督プロセスのような外部ユーティリティを使うよりもむしろ、アプリケーションのwatchdogとしてKubernetesを使うべきです。

アンサンブルのアップデート

zk StatefulSetRollingUpdateアップデート戦略を使うように設定されています。 サーバーに割り当てられるcpusの数を更新するのに、kubectl patchを利用できます。

kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]'
statefulset.apps/zk patched

更新の状況を見るには、kubectl rollout statusを使います。

kubectl rollout status sts/zk
waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-5db4499664...

これはPod群を終了し、逆の順番で1つずつそれらを新しい設定で再作成します。 これはクォーラムがローリングアップデート中に維持されることを保証します。

履歴や過去の設定を見るには、kubectl rollout historyコマンドを使います。

kubectl rollout history sts/zk

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

statefulsets "zk"
REVISION
1
2

変更をロールバックするには、kubectl rollout undoコマンドを使います。

kubectl rollout undo sts/zk

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

statefulset.apps/zk rolled back

プロセスの失敗の取り扱い

再起動ポリシーは、Pod内のコンテナのエントリポイントへのプロセスの失敗をKubernetesがどのように取り扱うかを制御します。 StatefulSet内のPodにおいて唯一妥当なRestartPolicyはAlwaysで、これはデフォルト値です。 ステートフルなアプリケーションでは、このデフォルトポリシーの上書きは絶対にすべきではありません

zk-0 Pod内で実行されているZooKeeperサーバーのプロセスツリーを調査するには、以下のコマンドを使います。

kubectl exec zk-0 -- ps -ef

コンテナのエントリポイントとして使われるコマンドはPID 1、エントリポイントの子であるZooKeeperプロセスはPID 27となっています。

UID        PID  PPID  C STIME TTY          TIME CMD
zookeep+     1     0  0 15:03 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
zookeep+    27     1  0 15:03 ?        00:00:03 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg

別のターミナルで、以下のコマンドを使ってzk StatefulSet内のPodを見てみます。

kubectl get pod -w -l app=zk

別のターミナルで、以下のコマンドを使ってPod zk-0内のZooKeeperプロセスを終了します。

kubectl exec zk-0 -- pkill java

ZooKeeperプロセスの終了は、その親プロセスの終了を引き起こします。 コンテナのRestartPolicyはAlwaysなので、親プロセスが再起動(restart)されます。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          21m
zk-1      1/1       Running   0          20m
zk-2      1/1       Running   0          19m
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Error     0          29m
zk-0      0/1       Running   1         29m
zk-0      1/1       Running   1         29m

アプリケーションが、そのビジネスロジックを実装するプロセスを立ち上げるのにスクリプト(zkServer.shなど)を使っている場合、スクリプトは子プロセスとともに終了する必要があります。 これは、Kubernetesがアプリケーションのコンテナを、そのビジネスロジックを実装しているプロセスが失敗した時に再起動することを保証します。

生存性(liveness)テスト

失敗したプロセスを再起動するための設定をアプリケーションに施すのは、分散型システムの健全さを保つのに十分ではありません。 システムのプロセスが生きていることもあれば無反応なこともあり、あるいはそうでなく不健全という状況もあります。 アプリケーションのプロセスが不健全で再起動すべきであることをKubernetesに通知するには、liveness probeを使うのがよいでしょう。

zk StatefulSetのPod templateでliveness probeを指定します。

  livenessProbe:
    exec:
      command:
      - sh
      - -c
      - "zookeeper-ready 2181"
    initialDelaySeconds: 15
    timeoutSeconds: 5

プローブはサーバーの健全さをテストするのに、ZooKeeperのruok 4文字コマンドを使うbashスクリプトを呼び出します。

OK=$(echo ruok | nc 127.0.0.1 $1)
if [ "$OK" == "imok" ]; then
    exit 0
else
    exit 1
fi

ターミナルウィンドウで、zk StatefulSet内のPodを見るのに以下のコマンドを使います。

kubectl get pod -w -l app=zk

別のウィンドウで、Pod zk-0のファイルシステムからzookeeper-readyスクリプトを削除するために以下のコマンドを使います。

kubectl exec zk-0 -- rm /opt/zookeeper/bin/zookeeper-ready

ZooKeeperプロセスの失敗のためにliveness probeを使う時、アンサンブル内の不健全なプロセスが再起動されることを保証するために、Kubernetesは自動的にプロセスを再起動します。

kubectl get pod -w -l app=zk
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Running   0          1h
zk-0      0/1       Running   1         1h
zk-0      1/1       Running   1         1h

準備性(readiness)テスト

準備性は生存性と同じではありません。 プロセスが生きているのであれば、スケジュールされ健全です。 プロセスの準備ができたら、入力を処理できます。 生存性はなくてはならないものですが、準備性の状態には十分ではありません。 プロセスは生きてはいるが準備はできていない時、特に初期化および終了の間がそのケースに相当します。

readiness probeを指定するとKubernetesは、準備性チェックに合格するまで、アプリケーションのプロセスがネットワークトラフィックを受け取らないことを保証します。

ZooKeeperサーバーにとって、健全性は準備性を意味します。 そのため、zookeeper.yamlマニフェストからのreadiness probeは、liveness probeと同一です。

  readinessProbe:
    exec:
      command:
      - sh
      - -c
      - "zookeeper-ready 2181"
    initialDelaySeconds: 15
    timeoutSeconds: 5

liveness probeとreadiness probeが同一だとしても、両方を指定することが重要です。 これは、ZooKeeperアンサンブル内の健全なサーバーだけがネットワークトラフィックを受け取ることを保証します。

ノードの失敗の許容

ZooKeeperはデータの変更を正しくコミットするのにサーバーのクォーラムを必要とします。 3つのサーバーのアンサンブルにおいては、書き込みの成功のために2つのサーバーは健全でなければなりません。 クォーラムベースのシステムにおいて、可用性を保証するために、メンバーは障害ドメインにデプロイされます。 個々のマシンの損失による障害を避けるためのベストプラクティスは、同じマシン上でアプリケーションの複数のインスタンスがコロケート(同じ場所に配置)されないようにすることです。

デフォルトでKubernetesは、同じノードのStatefulSetにPodをコロケートします。 3つのサーバーアンサンブルを作成していたとして、2つのサーバーが同じノードにあり、そのノードが障害を起こした場合、ZooKeeperサービスのクライアントは、少なくともPodの1つが再スケジュールされるまで障害に見舞われることになります。

クリティカルシステムのプロセスがノードの失敗イベントで再スケジュールできるよう、追加のキャパシティを常にプロビジョンしておくべきです。 そうしておけば、障害は単にKubernetesのスケジューラーがZooKeeperのサーバーの1つを再スケジュールするまでの辛抱です。 ただし、ダウンタイムなしでノードの障害への耐性をサービスに持たせたいなら、podAntiAffinityをセットすべきです。

zk StatefulSet内のPodのノードを取得するには、以下のコマンドを使います。

for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done

zk StatefulSet内の全てのPodは、別々のノードにデプロイされます。

kubernetes-node-cxpk
kubernetes-node-a5aq
kubernetes-node-2g2d

これはzk StatefulSet内のPodにPodAntiAffinityの指定があるからです。

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: "app"
              operator: In
              values:
                - zk
        topologyKey: "kubernetes.io/hostname"

requiredDuringSchedulingIgnoredDuringExecutionフィールドは、topologyKeyで定義されたドメイン内でappラベルの値がzkの2つのPodが絶対にコロケートすべきでないことを、Kubernetes Schedulerに指示します。 topologyKeykubernetes.io/hostnameは、ドメインが固有ノードであることを示しています。 異なるルール、ラベル、セレクターを使って、物理・ネットワーク・電源といった障害ドメイン全体に広がるアンサンブルにこのテクニックを広げることができます。

メンテナンス時の存続

このセクションでは、ノードをcordon(スケジュール不可化)およびdorain(解放)します。もし共有クラスターでこのチュートリアルを試しているのであれば、これがほかのテナントに有害な影響を及ぼさないことを確認してください。

前のセクションでは、計画外のノード障害に備えてどのようにPodをノード全体に広げるかを示しましたが、計画されたメンテナンスのため引き起こされる一時的なノード障害に対して計画する必要もあります。

クラスター内のノードを取得するために、以下のコマンドを使います。

kubectl get nodes

このチュートリアルでは、4つのノードのあるクラスターを仮定しています。 クラスターが4つよりも多くある場合には、4つのノード以外全てをcordonするためにkubectl cordonを使ってください。 ノードを4つに制約することで、以下のメンテナンスシミュレーションにおいてzookeeper Podをスケジュールした時に、KubernetesがアフィニティとPodDisruptionBudget制約に遭遇することを保証します。

kubectl cordon <ノード名>

zk-pdbPodDisruptionBudgetを取得するために、以下のコマンドを使います。

kubectl get pdb zk-pdb

max-unavailableフィールドは、zk StatefulSetの最大で1つのPodがいつでも利用できなくなる可能性があるということを、Kubernetesに指示します。

NAME      MIN-AVAILABLE   MAX-UNAVAILABLE   ALLOWED-DISRUPTIONS   AGE
zk-pdb    N/A             1                 1

1つ目のターミナルで、zk StatefulSet内のPodを見るのに以下のコマンドを使います。

kubectl get pods -w -l app=zk

次に別のターミナルで、Podが現在スケジュールされているノードを取得するために、以下のコマンドを使います。

for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done

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

kubernetes-node-pb41
kubernetes-node-ixsl
kubernetes-node-i4c4

zk-0 Podがスケジュールされているノードをcordonおよびdrainするには、kubectl drainを使います。

kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

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

node "kubernetes-node-pb41" cordoned

WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-pb41, kube-proxy-kubernetes-node-pb41; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-o5elz
pod "zk-0" deleted
node "kubernetes-node-pb41" drained

クラスターに4つのノードがあるので、kubectl drainは成功し、zk-0が別のノードに再スケジュールされます。

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m

最初のターミナルでStatefulSetのPodを見守り、zk-1がスケジュールされたノードをdrainします。

kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

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

"kubernetes-node-ixsl" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-ixsl, kube-proxy-kubernetes-node-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74
pod "zk-1" deleted
node "kubernetes-node-ixsl" drained

zk StatefulSetがPodのコロケーションを抑止するPodAntiAffinityルールを含んでいるので、zk-1 Podはスケジュールされず、またスケジュール可能なのは2つのノードだけなので、PodはPendingの状態のままになっています。

kubectl get pods -w -l app=zk

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

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
zk-1      1/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s

StatefulSetのPodを見続け、zk-2がスケジュールされているノードをdrainします。

kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

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

node "kubernetes-node-i4c4" cordoned

WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4
There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget.
pod/zk-2

kubectlを終了するためにCTRL-Cを押します。

zk-2を退去させるとzk-budget違反になってしまうので、3つ目のノードはdrainできません。ただし、ノードはcordonされたままとなります。

健全性テスト中に入力した値をzk-0から取得するには、zkCli.shを使います。

kubectl exec zk-0 zkCli.sh get /hello

PodDisruptionBudgetが遵守されているので、サービスはまだ利用可能です。

WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Dec 07 00:08:59 UTC 2016
mZxid = 0x200000002
mtime = Wed Dec 07 00:08:59 UTC 2016
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

最初のノードをuncordon(スケジュール可能化)するには、kubectl uncordonを使います。

kubectl uncordon kubernetes-node-pb41

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

node "kubernetes-node-pb41" uncordoned

zk-1はこのノードで再スケジュールされます。zk-1がRunningおよびReadyになるまで待ちます。

kubectl get pods -w -l app=zk

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

NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
zk-1      1/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         12m
zk-1      0/1       ContainerCreating   0         12m
zk-1      0/1       Running   0         13m
zk-1      1/1       Running   0         13m

試しにzk-2がスケジュールされているノードをdrainしてみます。

kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data

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

node "kubernetes-node-i4c4" already cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
pod "heapster-v1.2.0-2604621511-wht1r" deleted
pod "zk-2" deleted
node "kubernetes-node-i4c4" drained

今度はkubectl drainは成功しました。

zk-2の再スケジュールができるように、2つ目のノードをuncordonします。

kubectl uncordon kubernetes-node-ixsl

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

node "kubernetes-node-ixsl" uncordoned

サービスがメンテナンス中も利用可能なままであることを保証するために、PodDisruptionBudgetsとあわせてkubectl drainを利用できます。 メンテナンスでノードがオフラインになる前にノードをcordonして、Podを退去させるのにdrainが使われている場合、Disruption Budget(停止状態の予算)を表すサービスは遵守すべきバジェットを持ちます。 クリティカルサービスでは、Podをすぐに再スケジュールできるよう、追加のキャパティを常に割り当てておくべきです。

クリーンアップ

  • クラスターの全てのノードをuncordonするために、kubectl uncordonを実行してください。
  • このチュートリアルで使ったPersistentVolumeの永続的なストレージメディアを削除する必要があります。 全てのストレージが回収されたことを確実とするために、お使いの環境、ストレージ設定、プロビジョニング方法に基いて必要な手順に従ってください。

5.7 - クラスター

5.7.1 - AppArmorを使用してコンテナのリソースへのアクセスを制限する

FEATURE STATE: Kubernetes v1.4 [beta]

AppArmorは、Linux標準のユーザー・グループをベースとしたパーミッションを補完するLinuxカーネルのセキュリティモジュールであり、プログラムのアクセスを限定されたリソースセットに制限するために利用されます。AppArmorを設定することで、任意のアプリケーションの攻撃サーフェイスとなりうる面を減らしたり、より優れた多重の防御を提供できます。AppArmorは、たとえばLinuxのcapability、ネットワークアクセス、ファイルのパーミッションなど、特定のプログラムやコンテナに必要なアクセスを許可するようにチューニングされたプロファイルにより設定を行います。各プロファイルは、許可されなかったリソースへのアクセスをブロックするenforcingモードと、ルール違反を報告するだけのcomplainモードのいずれかで実行できます。

AppArmorを利用すれば、コンテナに許可することを制限したりシステムログを通してよりよい監査を提供することで、デプロイをよりセキュアにする助けになります。しかし、AppArmorは銀の弾丸ではなく、アプリケーションコードの悪用からの防御を強化できるだけであることを心に留めておくことが重要です。制限の強い優れたプロファイルを提供し、アプリケーションとクラスターを別の角度から強化することが重要です。

目標

  • プロファイルをノードに読み込む方法の例を見る
  • Pod上でプロファイルを矯正する方法を学ぶ
  • プロファイルが読み込まれたかを確認する方法を学ぶ
  • プロファイルに違反した場合に何が起こるのかを見る
  • プロファイルが読み込めなかった場合に何が起こるのかを見る

始める前に

以下のことを確認してください。

  1. Kubernetesのバージョンがv1.4以上であること。KubernetesのAppArmorのサポートはv1.4で追加されました。v1.4より古いバージョンのKubernetesのコンポーネントは、新しいAppArmorのアノテーションを認識できないため、AppArmorの設定を与えたとしても黙って無視されてしまいます。Podが期待した保護を確実に受けられるようにするためには、次のようにノードのKubeletのバージョンを確認することが重要です。

    kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {@.status.nodeInfo.kubeletVersion}\n{end}'
    
    gke-test-default-pool-239f5d02-gyn2: v1.4.0
    gke-test-default-pool-239f5d02-x1kf: v1.4.0
    gke-test-default-pool-239f5d02-xwux: v1.4.0
    
  2. AppArmorカーネルモジュールが有効であること。LinuxカーネルがAppArmorプロファイルを強制するためには、AppArmorカーネルモジュールのインストールと有効化が必須です。UbuntuやSUSEなどのディストリビューションではデフォルトで有効化されますが、他の多くのディストリビューションでのサポートはオプションです。モジュールが有効になっているかチェックするには、次のように/sys/module/apparmor/parameters/enabledファイルを確認します。

    cat /sys/module/apparmor/parameters/enabled
    Y
    

    KubeletがAppArmorをサポートしていれば(>= v1.4)、カーネルモジュールが有効になっていない場合にはAppArmorオプションが付いたPodを拒否します。

  1. コンテナランタイムがAppArmorをサポートしていること。現在、Kubernetesがサポートするすべての一般的なコンテナランタイム、DockerCRI-Ocontainerdなどは、AppArmorをサポートしています。関連するランタイムのドキュメントを参照して、クラスターがAppArmorを利用するための要求を満たしているかどうかを検証してください。

  2. プロファイルが読み込まれていること。AppArmorがPodに適用されるのは、各コンテナが実行されるべきAppArmorプロファイルを指定したときです。もし指定されたプロファイルがまだカーネルに読み込まれていなければ、Kubelet(>= v1.4)はPodを拒否します。どのプロファイルがノードに読み込まれているのかを確かめるには、次のようなコマンドを実行して/sys/kernel/security/apparmor/profilesをチェックします。

    ssh gke-test-default-pool-239f5d02-gyn2 "sudo cat /sys/kernel/security/apparmor/profiles | sort"
    
    apparmor-test-deny-write (enforce)
    apparmor-test-audit-write (enforce)
    docker-default (enforce)
    k8s-nginx (enforce)
    

    ノード上でのプロファイルの読み込みの詳細については、プロファイルを使用したノードのセットアップを参照してください。

KubeletのバージョンがAppArmorサポートに対応しているもの(>= v1.4)である限り、Kubeletは必要条件を1つでも満たさないAppArmorオプションが付けられたPodをリジェクトします。また、ノード上のAppArmorのサポートは、次のようにready conditionのメッセージで確認することもできます(ただし、この機能は将来のリリースで削除される可能性があります)。

kubectl get nodes -o=jsonpath='{range .items[*]}{@.metadata.name}: {.status.conditions[?(@.reason=="KubeletReady")].message}{"\n"}{end}'
gke-test-default-pool-239f5d02-gyn2: kubelet is posting ready status. AppArmor enabled
gke-test-default-pool-239f5d02-x1kf: kubelet is posting ready status. AppArmor enabled
gke-test-default-pool-239f5d02-xwux: kubelet is posting ready status. AppArmor enabled

Podをセキュアにする

AppArmorのプロファイルは各コンテナごとに指定します。Podのコンテナで実行するAppArmorのプロファイルを指定するには、Podのメタデータに次のようなアノテーションを追加します。

container.apparmor.security.beta.kubernetes.io/<container_name>: <profile_ref>

ここで、<container_name>はプロファイルを適用するコンテナの名前であり、<profile_ref>には適用するプロファイルを指定します。profile_refは次の値のうち1つを指定します。

  • runtime/default: ランタイムのデフォルトのプロファイルを適用する
  • localhost/<profile_name>: <profile_name>という名前でホストにロードされたプロファイルを適用する
  • unconfined: いかなるプロファイルもロードされないことを示す

アノテーションとプロファイルの名前のフォーマットの詳細については、APIリファレンスを参照してください。

KubernetesのAppArmorの強制では、まずはじめにすべての前提条件が満たされているかどうかをチェックします。その後、強制を行うためにプロファイルの選択をコンテナランタイムに委ねます。前提条件が満たされなかった場合、Podはリジェクトされ、実行されません。

プロファイルが適用されたかどうか確認するには、AppArmor securityオプションがコンテナ作成イベントに一覧されているかどうかを確認します。

kubectl get events | grep Created
22s        22s         1         hello-apparmor     Pod       spec.containers{hello}   Normal    Created     {kubelet e2e-test-stclair-node-pool-31nt}   Created container with docker id 269a53b202d3; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]

proc attrを調べることで、コンテナのルートプロセスが正しいプロファイルで実行されているかどうかを直接確認することもできます。

kubectl exec <pod_name> -- cat /proc/1/attr/current
k8s-apparmor-example-deny-write (enforce)

この例は、クラスターがすでにAppArmorのサポート付きでセットアップ済みであることを前提としています。

まず、使用したいプロファイルをノード上に読み込む必要があります。このプロファイルは、すべてのファイル書き込みを拒否します。

#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

Podがどのノードにスケジュールされるかは予測できないため、プロファイルはすべてのノードに読み込ませる必要があります。この例では、単純にSSHを使ってプロファイルをインストールしますが、プロファイルを使用したノードのセットアップでは、他のアプローチについて議論しています。

NODES=(
    # SSHでアクセス可能なノードのドメイン名
    gke-test-default-pool-239f5d02-gyn2.us-central1-a.my-k8s
    gke-test-default-pool-239f5d02-x1kf.us-central1-a.my-k8s
    gke-test-default-pool-239f5d02-xwux.us-central1-a.my-k8s)
for NODE in ${NODES[*]}; do ssh $NODE 'sudo apparmor_parser -q <<EOF
#include <tunables/global>

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}
EOF'
done

次に、deny-writeプロファイルを使用した単純な "Hello AppArmor" Podを実行します。

apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # Tell Kubernetes to apply the AppArmor profile "k8s-apparmor-example-deny-write".
    # Note that this is ignored if the Kubernetes node is not running version 1.4 or greater.
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
  containers:
  - name: hello
    image: busybox
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
kubectl create -f ./hello-apparmor.yaml

Podイベントを確認すると、PodコンテナがAppArmorプロファイル "k8s-apparmor-example-deny-write" を使用して作成されたことがわかります。

kubectl get events | grep hello-apparmor
14s        14s         1         hello-apparmor   Pod                                Normal    Scheduled   {default-scheduler }                           Successfully assigned hello-apparmor to gke-test-default-pool-239f5d02-gyn2
14s        14s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Pulling     {kubelet gke-test-default-pool-239f5d02-gyn2}   pulling image "busybox"
13s        13s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Pulled      {kubelet gke-test-default-pool-239f5d02-gyn2}   Successfully pulled image "busybox"
13s        13s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Created     {kubelet gke-test-default-pool-239f5d02-gyn2}   Created container with docker id 06b6cd1c0989; Security:[seccomp=unconfined apparmor=k8s-apparmor-example-deny-write]
13s        13s         1         hello-apparmor   Pod       spec.containers{hello}   Normal    Started     {kubelet gke-test-default-pool-239f5d02-gyn2}   Started container with docker id 06b6cd1c0989

コンテナがこのプロファイルで実際に実行されていることを確認するために、コンテナのproc attrをチェックします。

kubectl exec hello-apparmor -- cat /proc/1/attr/current
k8s-apparmor-example-deny-write (enforce)

最後に、ファイルへの書き込みを行おうとすることで、プロファイルに違反すると何が起こるか見てみましょう。

kubectl exec hello-apparmor touch -- /tmp/test
touch: /tmp/test: Permission denied
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1

まとめとして、読み込まれていないプロファイルを指定しようとするとどうなるのか見てみましょう。

kubectl create -f /dev/stdin <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor-2
  annotations:
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-allow-write
spec:
  containers:
  - name: hello
    image: busybox
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
EOF
pod/hello-apparmor-2 created
kubectl describe pod hello-apparmor-2
Name:          hello-apparmor-2
Namespace:     default
Node:          gke-test-default-pool-239f5d02-x1kf/
Start Time:    Tue, 30 Aug 2016 17:58:56 -0700
Labels:        <none>
Annotations:   container.apparmor.security.beta.kubernetes.io/hello=localhost/k8s-apparmor-example-allow-write
Status:        Pending
Reason:        AppArmor
Message:       Pod Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded
IP:
Controllers:   <none>
Containers:
  hello:
    Container ID:
    Image:     busybox
    Image ID:
    Port:
    Command:
      sh
      -c
      echo 'Hello AppArmor!' && sleep 1h
    State:              Waiting
      Reason:           Blocked
    Ready:              False
    Restart Count:      0
    Environment:        <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-dnz7v (ro)
Conditions:
  Type          Status
  Initialized   True
  Ready         False
  PodScheduled  True
Volumes:
  default-token-dnz7v:
    Type:    Secret (a volume populated by a Secret)
    SecretName:    default-token-dnz7v
    Optional:   false
QoS Class:      BestEffort
Node-Selectors: <none>
Tolerations:    <none>
Events:
  FirstSeen    LastSeen    Count    From                        SubobjectPath    Type        Reason        Message
  ---------    --------    -----    ----                        -------------    --------    ------        -------
  23s          23s         1        {default-scheduler }                         Normal      Scheduled     Successfully assigned hello-apparmor-2 to e2e-test-stclair-node-pool-t1f5
  23s          23s         1        {kubelet e2e-test-stclair-node-pool-t1f5}             Warning        AppArmor    Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded

PodのステータスはPendingとなり、Pod Cannot enforce AppArmor: profile "k8s-apparmor-example-allow-write" is not loaded(PodはAppArmorを強制できません: プロファイル "k8s-apparmor-example-allow-write" はロードされていません)という役に立つエラーメッセージが表示されています。同じメッセージのイベントも記録されています。

管理

プロファイルを使用したノードのセットアップ

現在、KubernetesはAppArmorのプロファイルをノードに読み込むネイティブの仕組みは提供していません。しかし、プロファイルをセットアップする方法は、以下のように様々な方法があります。

  • 各ノード上に正しいプロファイルがロードされていることを保証するPodを実行するDaemonSetを利用する方法。ここに実装例があります。
  • ノードの初期化時に初期化スクリプト(例: Salt、Ansibleなど)や初期化イメージを使用する。
  • で示したような方法で、プロファイルを各ノードにコピーし、SSHで読み込む。

スケジューラーはどのプロファイルがどのノードに読み込まれているのかがわからないため、すべてのプロファイルがすべてのノードに読み込まれていなければなりません。もう1つのアプローチとしては、各プロファイル(あるいはプロファイルのクラス)ごとにノードラベルを追加し、node selectorを用いてPodが必要なプロファイルを読み込んだノードで実行されるようにする方法もあります。

PodSecurityPolicyを使用したプロファイルの制限

PodSecurityPolicy extensionが有効になっている場合、クラスター全体でAppArmorn制限が適用されます。PodSecurityPolicyを有効にするには、apiserver上で次のフラグを設定する必要があります。

--enable-admission-plugins=PodSecurityPolicy[,others...]

AppArmorのオプションはPodSecurityPolicy上でアノテーションとして指定します。

apparmor.security.beta.kubernetes.io/defaultProfileName: <profile_ref>
apparmor.security.beta.kubernetes.io/allowedProfileNames: <profile_ref>[,others...]

defaultProfileNameオプションには、何も指定されなかった場合にコンテナにデフォルトで適用されるプロファイルを指定します。allowedProfileNamesオプションには、Podコンテナの実行が許可されるプロファイルのリストを指定します。両方のオプションが指定された場合、デフォルトは許可されなければいけません。プロファイルはコンテナ上で同じフォーマットで指定されます。完全な仕様については、APIリファレンスを参照してください。

AppArmorの無効化

クラスター上でAppArmorを利用可能にしたくない場合、次のコマンドラインフラグで無効化できます。

--feature-gates=AppArmor=false

無効化すると、AppArmorプロファイルを含むPodは"Forbidden"エラーで検証に失敗します。ただし、デフォルトのdockerは非特権Pod上では"docker-default"というプロファイルを常に有効化し(AppArmorカーネルモジュールが有効である場合)、フィーチャーゲートで無効化したとしても有効化し続けることに注意してください。AppArmorを無効化するオプションは、AppArmorが一般利用(GA)になったときに削除される予定です。

AppArmorを使用するKubernetes v1.4にアップグレードする

クラスターをv1.4にアップグレードするために、AppArmorに関する操作は必要ありません。ただし、既存のPodがAppArmorのアノテーションを持っている場合、検証(またはPodSecurityPolicy admission)は行われません。もしpermissiveなプロファイルがノードに読み込まれていた場合、悪意のあるユーザーがPodの権限を上述のdocker-defaultより昇格させるために、permissiveなプロファイルを再適用する恐れがあります。これが問題となる場合、apparmor.security.beta.kubernetes.ioのアノテーションを含むすべてのPodのクラスターをクリーンアップすることを推奨します。

一般利用可能(General Availability)への更新パス

AppArmorが一般利用可能(GA)になったとき、現在アノテーションで指定しているオプションはフィールドに変換されます。移行中のすべてのアップグレードとダウングレードの経路をサポートするのは非常に微妙であるため、以降が必要になったときに詳細に説明する予定です。最低2リリースの間はフィールドとアノテーションの両方がサポートされるようにする予定です。最低2リリースの後は、アノテーションは明示的に拒否されるようになります。

Profilesの作成

AppArmorのプロファイルを正しく指定するのはやっかいな作業です。幸い、その作業を補助するツールがいくつかあります。

  • aa-genprofおよびaa-logprofは、アプリケーションの動作とログを監視することによりプロファイルのルールを生成します。詳しい説明については、AppArmor documentationを参照してください。
  • baneは、Docker向けのAppArmorのプロファイル・ジェネレータです。簡略化されたプロファイル言語を使用しています。

プロファイルの生成には、アプリケーションを開発用ワークステーション上でDockerで実行することを推奨します。しかし、実際にPodが実行されるKubernetesノード上でツールを実行してはいけない理由はありません。

AppArmorに関する問題をデバッグするには、システムログをチェックして、特に何が拒否されたのかを確認できます。AppArmorのログはdmesgにverboseメッセージを送り、エラーは通常システムログまたはjournalctlで確認できます。詳しい情報は、AppArmor failuresで提供されています。

APIリファレンス

Podアノテーション

コンテナが実行するプロファイルを指定します。

  • key: container.apparmor.security.beta.kubernetes.io/<container_name> ここで、<container_name>はPod内のコンテナの名前を一致させます。Pod内の各コンテナごとに別々のプロファイルを指定できます。
  • value: 下で説明するプロファイルのリファレンス

プロファイルのリファレンス

  • runtime/default: デフォルトのランタイムプロファイルを指します。
    • (PodSecurityPolicyのデフォルトを設定せずに)プロファイルを指定しない場合と同等ですが、AppArmorを有効化する必要があります。
    • Dockerの場合、非特権コンテナではdocker-defaultプロファイルが選択され、特権コンテナではunconfined(プロファイルなし)が選択されます。
  • localhost/<profile_name>: 名前で指定されたノード(localhost)に読み込まれたプロファイルを指します。
  • unconfined: これは実質的にコンテナ上のAppArmorを無効化します。

これ以外のプロファイルリファレンスはすべて無効です。

PodSecurityPolicyアノテーション

何も指定されなかった場合にコンテナに適用するデフォルトのプロファイルは、以下のように指定します。

  • key: apparmor.security.beta.kubernetes.io/defaultProfileName
  • value: 上で説明したプロファイルのリファレンス

Podコンテナが指定することを許可するプロファイルのリストは、以下のように指定します。

  • key: apparmor.security.beta.kubernetes.io/allowedProfileNames
  • value: カンマ区切りの上述のプロファイルリファレンスのリスト
    • プロファイル名ではエスケープしたカンマは不正な文字ではありませんが、ここでは明示的に許可されません。

次の項目

追加のリソースとしては以下のものがあります。

5.8 - Service

5.8.1 - アプリケーションをServiceに接続する

コンテナに接続するためのKubernetesモデル

さて、継続的に実行され、複製されたアプリケーションができたので、これをネットワーク上に公開できます。

Kubernetesは、Podがどのホストに配置されているかにかかわらず、ほかのPodと通信できることを引き受けます。 Kubernetesは各Podにそれぞれ固有のクラスタープライベートなIPアドレスを付与するので、Pod間のリンクや、コンテナのポートとホストのポートのマップを明示的に作成する必要はありません。 これは、Pod内のコンテナは全てlocalhost上でお互いのポートに到達でき、クラスター内の全てのPodはNATなしに互いを見られるということを意味します。このドキュメントの残りの部分では、このようなネットワークモデルの上で信頼性のあるServiceを実行する方法について、詳しく述べていきます。

このチュートリアルでは概念のデモンストレーションのために、シンプルなnginx Webサーバーを例として使います。

Podをクラスターへ公開

これは前出の例でも行いましたが、もう一度やってみて、ネットワークからの観点に着目してみましょう。 nginx Podを作成し、コンテナのポート指定も記載します:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

この設定で、あなたのクラスターにはどのノードからもアクセス可能になります。Podを実行中のノードを確認してみましょう:

kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd

PodのIPアドレスを確認します:

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.4]]
    [map[ip:10.244.2.5]]

あなたのクラスター内のどのノードにもsshで入ることができて、双方のIPアドレスに対して問い合わせるためにcurlのようなツールを使えるようにしておくのがよいでしょう。 各コンテナはノード上でポート80を使っておらず、トラフィックをPodに流すための特別なNATルールもなんら存在しないことに注意してください。 つまり、全て同じcontainerPortを使った同じノード上で複数のnginx Podを実行でき、Serviceに割り当てられたIPアドレスを使って、クラスター内のほかのどのPodあるいはノードからもそれらにアクセスできます。 背後にあるPodにフォワードするためにホストNode上の特定のポートを充てたいというのであれば、それも可能です。とはいえ、ネットワークモデルではそのようなことをする必要がありません。

興味があれば、さらなる詳細について Kubernetesネットワークモデル を読んでください。

Serviceの作成

さて、フラットなクラスター全体のアドレス空間内でnginxを実行中のPodが得られました。 理論的にはこれらのPodと直接対話することは可能ですが、ノードが死んでしまった時には何が起きるでしょうか? ノードと一緒にPodは死に、Deploymentが新しいPodを異なるIPアドレスで作成します。 これがServiceが解決する問題です。

KubernetesのServiceは、全て同じ機能を提供する、クラスター内のどこかで実行するPodの論理的な集合を定義した抽象物です。 作成時に各Serviceは固有のIPアドレス(clusterIPとも呼ばれます)を割り当てられます。 このアドレスはServiceのライフスパンと結び付けられており、Serviceが生きている間は変わりません。 PodはServiceと対話できるよう設定され、Serviceのメンバーである複数のPodへ自動的に負荷分散されたServiceへ通信する方法を知っています。

kubectl exposeを使って、2つのnginxレプリカのためのServiceを作成できます:

kubectl expose deployment/my-nginx
service/my-nginx exposed

これはkubectl apply -fを以下のyamlに対して実行するのと同じです:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

この指定は、run: my-nginxラベルの付いた任意のPod上のTCPポート80を宛先とし、それを抽象化されたServiceポート(targetPortはコンテナがトラフィックを許可するポート、portは抽象化されたServiceポートで、ほかのPodがServiceにアクセスするのに使う任意のポートです)で公開するServiceを作成します。 Service定義内でサポートされているフィールドのリストを見るには、Service APIオブジェクトを参照してください。 Serviceを確認してみましょう:

kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

前述したとおり、ServiceはPodのグループに支えられています。 これらのPodはEndpointSlicesを通して公開されています。 Serviceのセレクターは継続的に評価され、その結果はServiceに接続されているEndpointSliceにlabelsを使って「投稿(POST)」されます。

Podが死ぬと、エンドポイントとして含まれていたEndpointSliceからそのPodは自動的に削除されます。 Serviceのセレクターにマッチする新しいPodが、Serviceのために自動的にEndpointSliceに追加されます。 エンドポイントを確認し、IPアドレスが最初のステップで作成したPodと同じであることに注目してください:

kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP Family Policy:    SingleStack
IP Families:         IPv4
IP:                  10.0.162.149
IPs:                 10.0.162.149
Port:                <unset> 80/TCP
TargetPort:          80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get endpointslices -l kubernetes.io/service-name=my-nginx
NAME             ADDRESSTYPE   PORTS   ENDPOINTS               AGE
my-nginx-7vzhx   IPv4          80      10.244.2.5,10.244.3.4   21s

今や、あなたのクラスター内のどのノードからもnginx Serviceに<CLUSTER-IP>:<PORT>でcurlを使用してアクセスできるはずです。Service IPは完全に仮想であり、物理的なケーブルで接続されるものではありません。どのように動作しているのか興味があれば、さらなる詳細についてサービスプロキシを読んでください。

Serviceへのアクセス

KubernetesはServiceを探す2つの主要なモードとして、環境変数とDNSをサポートしています。 前者はすぐに動かせるのに対し、後者はCoreDNSクラスターアドオンが必要です。

環境変数

PodをNode上で実行する時、kubeletはアクティブなServiceのそれぞれに環境変数のセットを追加します。 これは順序問題を生みます。なぜそうなるかの理由を見るために、実行中のnginx Podの環境を調査してみましょう(Podの名前は環境によって異なります):

kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

Serviceについて何も言及がないことに注意してください。 これは、Serviceの前にレプリカを作成したからです。 このようにすることでの不利益のもう1つは、スケジューラーが同一のマシンに両方のPodを置く可能性があることです(もしそのマシンが死ぬと全Serviceがダウンしてしまいます)。 2つのPodを殺し、Deploymentがそれらを再作成するのを待つことで、これを正しいやり方にできます。 今回は、レプリカの前にServiceが存在します。 これにより、正しい環境変数だけでなく、(全てのノードで等量のキャパシティを持つ場合)Podに広がった、スケジューラーレベルのServiceが得られます:

kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;

kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m

Podが、いったん殺されて再作成された後、異なる名前を持ったことに気付いたでしょうか。

kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

DNS

Kubernetesは、DNS名をほかのServiceに自動的に割り当てる、DNSクラスターアドオンServiceを提供しています。 クラスター上でそれを実行しているならば、確認できます:

kubectl get services kube-dns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m

本セクションの以降では、長寿命のIPアドレス(my-nginx)を持つServiceと、そのIPアドレスに名前を割り当てているDNSサーバーがあることを想定しています。 ここではCoreDNSクラスターアドオン(アプリケーション名kube-dns)を使い、標準的な手法(例えばgethostbyname())を使ってクラスター内の任意のPodからServiceと対話してみます。 CoreDNSが動作していない時には、 CoreDNS READMECoreDNSのインストールを参照して有効化してください。 テストするために、別のcurlアプリケーションを実行してみましょう:

kubectl run curl --image=radial/busyboxplus:curl -i --tty
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt

次にenterを押し、nslookup my-nginxを実行します:

[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      my-nginx
Address 1: 10.0.162.149

Serviceのセキュア化

これまではクラスター内からnginxサーバーだけにアクセスしてきました。 Serviceをインターネットに公開する前に、通信経路がセキュアかどうかを確かめたいところです。 そのためには次のようなものが必要です:

  • https用の自己署名証明書(まだ本人証明を用意していない場合)
  • 証明書を使うよう設定されたnginxサーバー
  • 証明書をPodからアクセスできるようにするSecret

これら全てはnginx https exampleから取得できます。 go環境とmakeツールのインストールが必要です (もしこれらをインストールしたくないときには、後述の手動手順に従ってください)。簡潔には:

make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
nginxsecret           kubernetes.io/tls                     2         1m

configmapも同様:

kubectl create configmap nginxconfigmap --from-file=default.conf
configmap/nginxconfigmap created
kubectl get configmaps
NAME             DATA   AGE
nginxconfigmap   1      114s

以下に示すのは、makeを実行したときに問題が発生する場合(例えばWindowsなど)の手動手順です:

# 公開鍵と秘密鍵のペアを作成する
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
# 鍵をbase64エンコーディングに変換する
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64

以下のようなyamlファイルを作成するために、前のコマンドからの出力を使います。 base64エンコードされた値は、全て1行で記述する必要があります。

apiVersion: "v1"
kind: "Secret"
metadata:
  name: "nginxsecret"
  namespace: "default"
type: kubernetes.io/tls
data:
  tls.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
  tls.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

では、このファイルを使ってSecretを作成します:

kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
nginxsecret           kubernetes.io/tls                     2         1m

Secretにある証明書を使ってhttpsサーバーを開始するために、nginxレプリカを変更します。また、Serviceは(80および443の)両方のポートを公開するようにします:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        - containerPort: 443
        - containerPort: 80
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume

nginx-secure-appマニフェストの注目すべきポイント:

  • DeploymentとServiceの指定の両方が同じファイルに含まれています。
  • nginxサーバーは、ポート80でHTTPトラフィック、ポート443でHTTPSトラフィックをサービスし、nginx Serviceは両方のポートを公開します。
  • 各コンテナは、/etc/nginx/sslにマウントされたボリューム経由で鍵にアクセスできます。 これはnginxサーバーが開始するにセットアップされます。
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml

この時点で、任意のノードからnginxサーバーに到達できます。

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

最後の手順でcurlに-kパラメーターを与えていることに注意してください。 これは、証明書の生成時点ではnginxを実行中のPodについて何もわからないので、curlにCNameのミスマッチを無視するよう指示する必要があるからです。 Serviceを作成することで、証明書で使われているCNameと、PodがServiceルックアップ時に使う実際のDNS名とがリンクされます。 Podからこれをテストしてみましょう(単純化のため同じSecretが再利用されるので、ServiceにアクセスするのにPodが必要なのはnginx.crtだけです):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-deployment
spec:
  selector:
    matchLabels:
      app: curlpod
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: curlpod
        command:
        - sh
        - -c
        - while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
...
<title>Welcome to nginx!</title>
...

Serviceの公開

アプリケーションのいくつかの部分においては、Serviceを外部IPアドレスで公開したいと思うかもしれません。 Kubernetesはこれに対して2つのやり方をサポートしています: NodePortとLoadBalancerです。 前のセクションで作成したServiceではすでにNodePortを使っていたので、ノードにパブリックIPアドレスがあれば、nginx HTTPSレプリカもトラフィックをインターネットでサービスする準備がすでに整っています。

kubectl get svc my-nginx -o yaml | grep nodePort -C 5
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 31704
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: https
    nodePort: 32453
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>

では、クラウドロードバランサーを使うために、Serviceを再作成してみましょう。 my-nginxTypeNodePortからLoadBalancerに変更してください:

kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   LoadBalancer   10.0.162.149     xx.xxx.xxx.xxx     8080:30163/TCP        21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>

EXTERNAL-IP列のIPアドレスが、パブリックインターネットで利用可能なものになっています。 CLUSTER-IPはクラスター/プライベートクラウドネットワーク内でのみ利用可能なものです。

AWSにおいては、LoadBalancerタイプは、IPアドレスではなく(長い)ホスト名を使うELBを作成することに注意してください。 これは標準のkubectl get svcの出力に合わせるには長すぎ、実際それを見るにはkubectl describe service my-nginxを使う必要があります。 これは以下のような見た目になります:

kubectl describe service my-nginx
...
LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...

次の項目

5.8.2 - 送信元IPを使用する

Kubernetesクラスター内で実行されているアプリケーションは、Serviceという抽象化を経由して、他のアプリケーションや外の世界との発見や通信を行います。このドキュメントでは、異なる種類のServiceに送られたパケットの送信元IPに何が起こるのか、そして必要に応じてこの振る舞いを切り替える方法について説明します。

始める前に

用語

このドキュメントでは、以下の用語を使用します。

NAT
ネットワークアドレス変換(network address translation)
送信元NAT
パケットの送信元のIPを置換します。このページでは、通常ノードのIPアドレスを置換することを意味します。
送信先NAT
パケットの送信先のIPを置換します。このページでは、通常PodのIPアドレスを置換することを意味します。
VIP
Kubernetes内のすべてのServiceなどに割り当てられる仮想IPアドレス(virtual IP address)です。
kube-proxy
すべてのノード上でServiceのVIPを管理するネットワークデーモンです。

前提条件

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

以下の例では、HTTPヘッダー経由で受け取ったリクエストの送信元IPをエコーバックする、小さなnginxウェブサーバーを使用します。次のコマンドでウェブサーバーを作成できます。

kubectl create deployment source-ip-app --image=registry.k8s.io/echoserver:1.4

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

deployment.apps/source-ip-app created

目標

  • 単純なアプリケーションを様々な種類のService経由で公開する
  • それぞれの種類のServiceがどのように送信元IPのNATを扱うかを理解する
  • 送信元IPを保持することに関わるトレードオフを理解する

Type=ClusterIPを使用したServiceでの送信元IP

kube-proxyがiptablesモード(デフォルト)で実行されている場合、クラスター内部からClusterIPに送られたパケットに送信元のNATが行われることは決してありません。kube-proxyが実行されているノード上でhttp://localhost:10249/proxyModeにリクエストを送って、kube-proxyのモードを問い合わせてみましょう。

kubectl get nodes

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

NAME                           STATUS     ROLES    AGE     VERSION
kubernetes-node-6jst   Ready      <none>   2h      v1.13.0
kubernetes-node-cx31   Ready      <none>   2h      v1.13.0
kubernetes-node-jj1t   Ready      <none>   2h      v1.13.0

これらのノードの1つでproxyモードを取得します(kube-proxyはポート10249をlistenしています)。

# このコマンドは、問い合わせを行いたいノード上のシェルで実行してください。
curl http://localhost:10249/proxyMode

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

iptables

source IPアプリのServiceを作成することで、送信元IPが保持されているかテストできます。

kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080

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

service/clusterip exposed
kubectl get svc clusterip

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

NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
clusterip    ClusterIP   10.0.170.92   <none>        80/TCP    51s

そして、同じクラスター上のPodからClusterIPにアクセスします。

kubectl run busybox -it --image=busybox --restart=Never --rm

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

Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

これで、Podの内部でコマンドが実行できます。

# このコマンドは、"kubectl run" のターミナルの内部で実行してください
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue
    link/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff
    inet 10.244.3.8/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::188a:84ff:feb0:26a5/64 scope link
       valid_lft forever preferred_lft forever

そして、wgetを使用してローカルのウェブサーバーに問い合わせます。

# "10.0.170.92" の部分をService名が"clusterip"のIPv4アドレスに置き換えてください
wget -qO - 10.0.170.92
CLIENT VALUES:
client_address=10.244.3.8
command=GET
...

client_addressは常にクライアントのPodのIPアドレスになります。これは、クライアントのPodとサーバーのPodが同じノード内にあっても異なるノードにあっても変わりません。

Type=NodePortを使用したServiceでの送信元IP

Type=NodePortを使用したServiceに送られたパケットは、デフォルトで送信元のNATが行われます。NodePort Serviceを作ることでテストできます。

kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort

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

service/nodeport exposed
NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)
NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }')

クラウドプロバイダーで実行する場合、上に示したnodes:nodeportに対してファイアウォールのルールを作成する必要があるかもしれません。それでは、上で割り当てたノードポート経由で、クラスターの外部からServiceにアクセスしてみましょう。

for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done

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

client_address=10.180.1.1
client_address=10.240.0.5
client_address=10.240.0.3

これらは正しいクライアントIPではなく、クラスターのinternal IPであることがわかります。ここでは、次のようなことが起こっています。

  • クライアントがパケットをnode2:nodePortに送信する
  • node2は、パケット内の送信元IPアドレスを自ノードのIPアドレスに置換する(SNAT)
  • node2は、パケット内の送信先IPアドレスをPodのIPアドレスに置換する
  • パケットはnode1にルーティングされ、endpointにルーティングされる
  • Podからの応答がnode2にルーティングされて戻ってくる
  • Podからの応答がクライアントに送り返される

図で表すと次のようになります。

graph LR; client(client)-->node2[Node 2]; node2-->client; node2-. SNAT .->node1[Node 1]; node1-. SNAT .->node2; node1-->endpoint(Endpoint); classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; class node1,node2,endpoint k8s; class client plain;

クライアントのIPが失われることを回避するために、Kubernetesにはクライアントの送信元IPを保持する機能があります。service.spec.externalTrafficPolicyの値をLocalに設定すると、kube-proxyはローカルに存在するエンドポイントへのプロキシリクエストだけをプロキシし、他のノードへはトラフィックを転送しなくなります。このアプローチでは、オリジナルの送信元IPアドレスが保持されます。ローカルにエンドポイントが存在しない場合には、そのノードに送信されたパケットは損失します。そのため、エンドポイントに到達するパケットに適用する可能性のあるパケット処理ルールでは、送信元IPが正しいことを信頼できます。

次のようにしてservice.spec.externalTrafficPolicyフィールドを設定します。

kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'

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

service/nodeport patched

そして、再度テストしてみます。

for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done

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

client_address=198.51.100.79

今度は、正しいクライアントIPが含まれる応答が1つだけ得られました。これは、エンドポイントのPodが実行されているノードから来たものです。

ここでは、次のようなことが起こっています。

  • クライアントがパケットをエンドポイントが存在しないnode2:nodePortに送信する
  • パケットが損失する
  • クライアントがパケットをエンドポイントが存在するnode1:nodePortに送信する
  • node1は、正しい送信元IPを持つパケットをエンドポイントにルーティングする

図で表すと次のようになります。

graph TD; client --> node1[Node 1]; client(client) --x node2[Node 2]; node1 --> endpoint(endpoint); endpoint --> node1; classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; class node1,node2,endpoint k8s; class client plain;

Type=LoadBalancerを使用したServiceでの送信元IP

Type=LoadBalancerを使用したServiceに送られたパケットは、デフォルトで送信元のNATが行われます。Ready状態にあるすべてのスケジュール可能なKubernetesのNodeは、ロードバランサーからのトラフィックを受付可能であるためです。そのため、エンドポイントが存在しないノードにパケットが到達した場合、システムはエンドポイントが存在するノードにパケットをプロシキーします。このとき、(前のセクションで説明したように)パケットの送信元IPがノードのIPに置換されます。

ロードバランサー経由でsource-ip-appを公開することで、これをテストできます。

kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer

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

service/loadbalancer exposed

ServiceのIPアドレスを表示します。

kubectl get svc loadbalancer

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

NAME           TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE
loadbalancer   LoadBalancer   10.0.65.118   203.0.113.140     80/TCP    5m

次に、Serviceのexternal-ipにリクエストを送信します。

curl 203.0.113.140

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

CLIENT VALUES:
client_address=10.240.0.5
...

しかし、Google Kubernetes EngineやGCE上で実行している場合、同じservice.spec.externalTrafficPolicyフィールドをLocalに設定すると、ロードバランサーからのトラフィックを受け付け可能なノードのリストから、Serviceエンドポイントが存在しないノードが強制的に削除されます。この動作は、ヘルスチェックを意図的に失敗させることによって実現されています。

図で表すと次のようになります。

Source IP with externalTrafficPolicy

アノテーションを設定することで動作をテストできます。

kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'

Kubernetesにより割り当てられたservice.spec.healthCheckNodePortフィールドをすぐに確認します。

kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort

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

  healthCheckNodePort: 32122

service.spec.healthCheckNodePortフィールドは、/healthzでhealth checkを配信しているすべてのノード上のポートを指しています。次のコマンドでテストできます。

kubectl get pod -o wide -l run=source-ip-app

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

NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE
source-ip-app-826191075-qehz4   1/1       Running   0          20h       10.180.1.136   kubernetes-node-6jst

curlを使用して、さまざまなノード上の/healthzエンドポイントからデータを取得します。

# このコマンドは選んだノードのローカル上で実行してください
curl localhost:32122/healthz
1 Service Endpoints found

ノードが異なると、得られる結果も異なる可能性があります。

# このコマンドは、選んだノード上でローカルに実行してください
curl localhost:32122/healthz
No Service Endpoints Found

コントロールプレーン上で実行中のコントローラーは、クラウドのロードバランサーを割り当てる責任があります。同じコントローラーは、各ノード上のポートやパスを指すHTTPのヘルスチェックも割り当てます。エンドポイントが存在しない2つのノードがヘルスチェックに失敗するまで約10秒待った後、curlを使用してロードバランサーのIPv4アドレスに問い合わせます。

curl 203.0.113.140

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

CLIENT VALUES:
client_address=198.51.100.79
...

クロスプラットフォームのサポート

Type=LoadBalancerを使用したServiceで送信元IPを保持する機能を提供しているのは一部のクラウドプロバイダだけです。実行しているクラウドプロバイダによっては、以下のように異なる方法でリクエストを満たす場合があります。

  1. クライアントとのコネクションをプロキシが終端し、ノードやエンドポイントとの接続には新しいコネクションが開かれる。このような場合、送信元IPは常にクラウドのロードバランサーのものになり、クライアントのIPにはなりません。

  2. クライアントからロードバランサーのVIPに送信されたリクエストが、中間のプロキシではなく、クライアントの送信元IPとともにノードまで到達するようなパケット転送が使用される。

1つめのカテゴリーのロードバランサーの場合、真のクライアントIPと通信するために、 HTTPのForwardedヘッダーやX-FORWARDED-FORヘッダー、proxy protocolなどの、ロードバランサーとバックエンドの間で合意されたプロトコルを使用する必要があります。2つ目のカテゴリーのロードバランサーの場合、Serviceのservice.spec.healthCheckNodePortフィールドに保存されたポートを指すHTTPのヘルスチェックを作成することで、上記の機能を活用できます。

クリーンアップ

Serviceを削除します。

kubectl delete svc -l app=source-ip-app

Deployment、ReplicaSet、Podを削除します。

kubectl delete deployment source-ip-app

次の項目

5.8.3 - Podとそのエンドポイントの終了動作を探る

アプリケーションをServiceに接続するで概略を示したステップに従ってアプリケーションをServiceに接続すると、ネットワーク上で公開され、継続的に実行されて、複製されたアプリケーションが得られます。 このチュートリアルでは、Podを終了する流れを見て、gracefulな(猶予のある)接続ドレインを実装する手法を模索するための手助けをします。

Podの終了手続きとそのエンドポイント

アップグレードやスケールダウンのために、Podを終了しなければならない場面はままあります。 アプリケーションの可用性を高めるために、適切なアクティブ接続ドレインを実装することは重要でしょう。

このチュートリアルでは概念のデモンストレーションのために、シンプルなnginx Webサーバーを例として、対応するエンドポイントの状態に関連したPodの終了および削除の流れを説明します。

エンドポイント終了の流れの例

以下は、Podの終了ドキュメントに記載されている流れの例です。

1つのnginxレプリカを含むDeployment(純粋にデモンストレーション目的です)とServiceがあるとします:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 120 # 非常に長い猶予期間
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        lifecycle:
          preStop:
            exec:
              # 実際の活動終了はterminationGracePeriodSecondsまでかかる可能性がある。
              # この例においては、少なくともterminationGracePeriodSecondsの間は待機し、
              # 120秒経過すると、コンテナは強制的に終了される。
              # この間ずっとnginxはリクエストを処理し続けていることに注意。
              command: [
                "/bin/sh", "-c", "sleep 180"
              ]
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 120 # 非常に長い猶予期間
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        lifecycle:
          preStop:
            exec:
              # 実際の活動終了はterminationGracePeriodSecondsまでかかる可能性がある。
              # この例においては、少なくともterminationGracePeriodSecondsの間は待機し、
              # 120秒経過すると、コンテナは強制終了される。
              # この間ずっとnginxはリクエストを処理し続けていることに注意。
              command: [
                "/bin/sh", "-c", "sleep 180"
              ]

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

PodとServiceが実行中になったら、関連付けられたEndpointSliceの名前を得られます:

kubectl get endpointslice

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

NAME                  ADDRESSTYPE   PORTS   ENDPOINTS                 AGE
nginx-service-6tjbr   IPv4          80      10.12.1.199,10.12.1.201   22m

状態からわかるように、1つのエンドポイントが登録されていることが確認できます:

kubectl get endpointslices -o json -l kubernetes.io/service-name=nginx-service

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

{
    "addressType": "IPv4",
    "apiVersion": "discovery.k8s.io/v1",
    "endpoints": [
        {
            "addresses": [
                "10.12.1.201"
            ],
            "conditions": {
                "ready": true,
                "serving": true,
                "terminating": false

では、Podを終了し、そのPodがgracefulな終了期間設定を守って終了されていることを確認してみましょう:

kubectl delete pod nginx-deployment-7768647bf9-b4b9s

全Podについて調べます:

kubectl get pods

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

NAME                                READY   STATUS        RESTARTS      AGE
nginx-deployment-7768647bf9-b4b9s   1/1     Terminating   0             4m1s
nginx-deployment-7768647bf9-rkxlw   1/1     Running       0             8s

新しいPodがスケジュールされたことを見てとれます。

新しいPodのために新しいエンドポイントが作成される間、古いエンドポイントは終了中の状態のまま残っています:

kubectl get endpointslice -o json nginx-service-6tjbr

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

{
    "addressType": "IPv4",
    "apiVersion": "discovery.k8s.io/v1",
    "endpoints": [
        {
            "addresses": [
                "10.12.1.201"
            ],
            "conditions": {
                "ready": false,
                "serving": true,
                "terminating": true
            },
            "nodeName": "gke-main-default-pool-dca1511c-d17b",
            "targetRef": {
                "kind": "Pod",
                "name": "nginx-deployment-7768647bf9-b4b9s",
                "namespace": "default",
                "uid": "66fa831c-7eb2-407f-bd2c-f96dfe841478"
            },
            "zone": "us-central1-c"
        },
        {
            "addresses": [
                "10.12.1.202"
            ],
            "conditions": {
                "ready": true,
                "serving": true,
                "terminating": false
            },
            "nodeName": "gke-main-default-pool-dca1511c-d17b",
            "targetRef": {
                "kind": "Pod",
                "name": "nginx-deployment-7768647bf9-rkxlw",
                "namespace": "default",
                "uid": "722b1cbe-dcd7-4ed4-8928-4a4d0e2bbe35"
            },
            "zone": "us-central1-c"

これを使うと、終了中のアプリケーションがその状態について、接続ドレイン機能の実装目的でクライアント(ロードバランサーなど)と通信する、ということが可能です。 これらのクライアントではエンドポイントの終了を検出し、そのための特別なロジックを実装できます。

Kubernetesでは、終了中のエンドポイントのready状態は全てfalseにセットされます。 これは後方互換性のために必要な措置で、既存のロードバランサーは通常のトラフィックにはそれを使用しません。 Podの終了時にトラフィックのドレインが必要な場合、実際に準備できているかはserving状態として調べられます。

Podが削除される時には、古いエンドポイントも削除されます。

次の項目

6 - リファレンス

本セクションには、Kubernetesのドキュメントのリファレンスが含まれています。

APIリファレンス

公式にサポートされているクライアントライブラリー

プログラミング言語からKubernetesのAPIを呼ぶためには、クライアントライブラリーを使うことができます。公式にサポートしているクライアントライブラリー:

CLIリファレンス

  • kubectl - コマンドの実行やKubernetesクラスターの管理に使う主要なCLIツールです。
  • kubeadm - セキュアなKubernetesクラスターを簡単にプロビジョニングするためのCLIツールです。

コンポーネントリファレンス

  • kubelet - 各ノード上で動作する最も重要なノードエージェントです。kubeletは一通りのPodSpecを受け取り、コンテナが実行中で正常であることを確認します。

  • kube-apiserver - Pod、Service、Replication Controller等、APIオブジェクトのデータを検証・設定するREST APIサーバーです。

  • kube-controller-manager - Kubernetesに同梱された、コアのコントロールループを埋め込むデーモンです。

  • kube-proxy - 単純なTCP/UDPストリームのフォワーディングや、一連のバックエンド間でTCP/UDPのラウンドロビンでのフォワーディングを実行できます。

  • kube-scheduler - 可用性、パフォーマンス、およびキャパシティを管理するスケジューラーです。

  • コントロールプレーンとワーカーノードで開いておくべきポートとプロトコルの一覧

設定APIリファレンス

このセクションでは、Kubernetesのコンポーネントやツールを設定するのに使われている「未公開」のAPIのドキュメントをまとめています。 クラスターを使ったり管理したりするユーザーやオペレーターにとって必要不可欠ではありますが、これらのAPIの大半はRESTful方式のAPIサーバーでは提供されません。

kubeadmの設定APIリファレンス

設計のドキュメント

Kubernetesの機能に関する設計ドキュメントのアーカイブです。KubernetesアーキテクチャKubernetesデザイン概要から読み始めると良いでしょう。

6.1 - 標準化用語集

6.2 - KubeletチェックポイントAPI

FEATURE STATE: Kubernetes v1.25 [alpha]

コンテナのチェックポイントは実行中のコンテナのステートフルコピーを作成するための機能です。 コンテナのステートフルコピーがあると、デバックや類似の目的のために別のコンピューターに移動させることができます。

チェックポイントコンテナデータを復元可能なコンピューターに移動させる場合、その復元したコンテナは、チェックポイントが作成された正確に同じ地点で実行が再開されます。 保存したデータを検査することも可能です。 ただし、検査を行うための適したツールを保持している必要があります。

コンテナのチェックポイントを作成することで、セキュリティ影響が発生する場合があります。 通常、チェックポイントはチェックポイントされたコンテナ内のすべてのプロセスのすべてのメモリーページを含んでいます。 メモリー内で使用された全てがローカルディスク上で利用できるようになることを意味しています。 これはすべてのプライベートデータを含んでおり、もしかしたら暗号化に使用した鍵も含まれているかもしれません。 基礎となるCRI実装(そのノード上のコンテナランタイム)は、rootユーザーのみがアクセス可能なチェックポイントアーカイブを作成するべきです。 チェックポイントアーカイブが他のシステムに転送された場合、全てのメモリーページがチェックポイントアーカイブのオーナーによって読み取れるようになることを覚えておくことが重要です。

操作方法

post 指定したコンテナのチェックポイント

指定したPodから指定したコンテナのチェックポイントを作成するようにkubeletに指示します。

kubeletチェックポイントインターフェースへのアクセスの制御方法についての詳細な情報は、Kubelet authentication/authorization referenceを参照してください。

kubeletは基礎となるCRI実装にチェックポイントをリクエストします。 チェックポイントリクエストでは、kubeletがcheckpoint-<podFullName>-<containerName>-<timestamp>.tarのようなチェックポイントアーカイブの名前を指定します。 併せて、(--root-dirで定義される)rootディレクトリ配下のcheckpointsディレクトリに、チェックポイントアーカイブを保存することをリクエストします。 デフォルトは/var/lib/kubelet/checkpointsです。

チェックポイントアーカイブは tar フォーマットであり、tarの実装を使用して一覧表示できます。 アーカイブの内容は、基礎となるCRI実装(ノード上のコンテナランタイム)に依存します。

HTTPリクエスト

POST /checkpoint/{namespace}/{pod}/{container}

パラメーター

  • namespace (パス内): string, 必須項目

    Namespace
  • pod (パス内): string, 必須項目

    Pod
  • container (パス内): string, 必須項目

    コンテナ
  • timeout (クエリ内): integer

    チェックポイントの作成が終了するまで待機する秒単位のタイムアウト。 ゼロまたはタイムアウトが指定されていない場合、デフォルトはCRIタイムアウトの値が使用されます。 チェックポイント作成時間はコンテナの使用メモリーに直接依存します。 コンテナの使用メモリーが多いほど、対応するチェックポイントを作成するために必要な時間が長くなります。

レスポンス

200: OK

401: Unauthorized

404: Not Found (ContainerCheckpointフィーチャーゲートが無効の場合)

404: Not Found (指定したnamespacepodcontainerが見つからない場合)

500: Internal Server Error (CRI実装でチェックポイント中にエラーが発生した場合(詳細はエラーメッセージを参照))

500: Internal Server Error (CRI実装がチェックポイントCRI APIを実装していない場合(詳細はエラーメッセージを参照))

6.3 - 認証

このページでは、認証の概要について説明します。

Kubernetesにおけるユーザー

すべてのKubernetesクラスターには、2種類のユーザーがあります。Kubernetesによって管理されるサービスアカウントと、通常のユーザーです。

クラスターから独立したサービスは通常のユーザーを以下の方法で管理することを想定されています。

  • 秘密鍵を配布する管理者
  • KeystoneやGoogle Accountsのようなユーザーストア
  • ユーザー名とパスワードのリストを持つファイル

これを考慮すると、 Kubernetesは通常のユーザーアカウントを表すオブジェクトを持ちません。 APIコールを介して、通常のユーザーをクラスターに追加することはできません。

APIコールを介して通常のユーザーを追加できませんが、クラスターの認証局(CA)に署名された有効な証明書で表すユーザーは認証済みと判断されます。この構成では、Kubernetesは証明書の‘subject’内にある一般的な名前フィールド(例えば、“/CN=bob”)からユーザー名を特定します。そこから、ロールベースアクセス制御(RBAC)サブシステムは、ユーザーがあるリソースにおける特定の操作を実行するために認証済みかどうか特定します。詳細は、 証明書要求内の通常のユーザーの題目を参照してください。

対照的に、サービスアカウントはKubernetes APIによって管理されるユーザーです。サービスアカウントは特定の名前空間にバインドされており、APIサーバーによって自動的に作成されるか、APIコールによって手動で作成されます。サービスアカウントは、Secretsとして保存された資格情報の集合に紐付けられています。これをPodにマウントすることで、クラスター内のプロセスがKubernetes APIと通信できるようにします。

APIリクエストは、通常のユーザーかサービスアカウントに紐付けられているか、匿名リクエストとして扱われます。つまり、ワークステーションでkubectlを入力する人間のユーザーから、ノード上のkubeletsやコントロールプレーンのメンバーまで、クラスター内外の全てのプロセスは、APIサーバーへのリクエストを行う際に認証を行うか匿名ユーザーとして扱われる必要があります。

認証戦略

Kubernetesは、クライアント証明書、Bearerトークン、認証プロキシ、HTTP Basic認証を使い、認証プラグインを通してAPIリクエストを認証します。APIサーバーにHTTPリクエストが送信されると、プラグインは以下の属性をリクエストに関連付けようとします。

  • ユーザー名: エンドユーザーを識別する文字列です。一般的にな値は、kube-adminjane@example.comです。
  • UID: エンドユーザーを識別する文字列であり、ユーザー名よりも一貫性と一意性を持たせようとするものです。
  • グループ: 各要素がユーザーの役割を示すような意味を持つ文字列の集合です。system:mastersdevops-teamといった値が一般的です。
  • 追加フィールド: 認証者が有用と思われる追加情報を保持する文字列のリストに対する、文字列のマップです。

すべての値は認証システムに対して非透過であり、認可機能が解釈した場合にのみ意味を持ちます。

一度に複数の認証方法を有効にすることができます。通常は、以下のように少なくとも2つの方法を使用するべきです。

  • サービスアカウント用のサービスアカウントトークン
  • ユーザー認証のための、少なくとも1つの他の方法

複数の認証モジュールが有効化されている場合、リクエストの認証に成功した最初のモジュールが、評価が簡略化します。APIサーバーは、認証の実行順序を保証しません。

system:authenticatedグループには、すべての認証済みユーザーのグループのリストが含まれます。

他の認証プロトコル(LDAP、SAML、Kerberos、X509スキームなど)との統合は、認証プロキシ認証Webhookを使用して実施できます。

X509クライアント証明書

クライアント証明書認証は、APIサーバーに--client-ca-file=SOMEFILEオプションを渡すことで有効になります。参照されるファイルには、APIサーバーに提示されたクライアント証明書を検証するために使用する1つ以上の認証局が含まれている必要があります。クライアント証明書が提示され、検証された場合、サブジェクトのCommon Nameがリクエストのユーザー名として使用されます。Kubernetes1.4時点では、クライアント証明書は、証明書のOrganizationフィールドを使用して、ユーザーのグループメンバーシップを示すこともできます。あるユーザーに対して複数のグループメンバーシップを含めるには、証明書に複数のOrganizationフィールドを含めます。

例えば、証明書署名要求を生成するために、opensslコマンドラインツールを使用します。

openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

これにより、"app1"と"app2"の2つのグループに属するユーザー名"jbeda"の証明書署名要求が作成されます。

クライアント証明書の生成方法については、証明書の管理を参照してください。

静的なトークンファイル

コマンドラインで--token-auth-file=SOMEFILEオプションを指定すると、APIサーバーはファイルからBearerトークンを読み込みます。現在のところ、トークンの有効期限は無く、APIサーバーを再起動しない限りトークンのリストを変更することはできません。

トークンファイルは、トークン、ユーザー名、ユーザーUIDの少なくとも3つの列を持つcsvファイルで、その後にオプションでグループ名が付きます。

リクエストにBearerトークンを含める

HTTPクライアントからBearerトークン認証を利用する場合、APIサーバーはBearer THETOKENという値を持つAuthorizationヘッダーを待ち受けます。Bearerトークンは、HTTPのエンコーディングとクォート機能を利用してHTTPヘッダーの値に入れることができる文字列でなければなりません。例えば、Bearerトークンが31ada4fd-adec-460c-809a-9e56ceb75269であれば、HTTPのヘッダを以下のようにします。

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

ブートストラップトークン

FEATURE STATE: Kubernetes v1.18 [stable]

新しいクラスターの効率的なブートストラップを可能にするために、Kubernetesにはブートストラップトークンと呼ばれる動的に管理されたBearerトークンタイプが含まれています。これらのトークンは、kube-system名前空間にSecretsとして格納され、動的に管理したり作成したりすることができます。コントローラーマネージャーには、TokenCleanerコントローラーが含まれており、ブートストラップトークンの有効期限が切れると削除します。

トークンの形式は[a-z0-9]{6}.[a-z0-9]{16}です。最初のコンポーネントはトークンIDであり、第2のコンポーネントはToken Secretです。以下のように、トークンをHTTPヘッダーに指定します。

Authorization: Bearer 781292.db7bc3a58fc5f07e

APIサーバーの--enable-bootstrap-token-authフラグで、Bootstrap Token Authenticatorを有効にする必要があります。TokenCleanerコントローラーを有効にするには、コントローラーマネージャーの--controllersフラグを使います。--controllers=*,tokencleanerのようにして行います。クラスターをブートストラップするためにkubeadmを使用している場合は、kubeadmがこれを代行してくれます。

認証機能はsystem:bootstrap:<Token ID>という名前で認証します。これはsystem:bootstrappersグループに含まれます。名前とグループは意図的に制限されており、ユーザーがブートストラップ後にこれらのトークンを使わないようにしています。ユーザー名とグループは、クラスターのブートストラップをサポートする適切な認可ポリシーを作成するために使用され、kubeadmによって使用されます。

ブートストラップトークンの認証機能やコントローラーについての詳細な説明、kubeadmでこれらのトークンを管理する方法については、ブートストラップトークンを参照してください。

サービスアカウントトークン

サービスアカウントは、自動的に有効化される認証機能で、署名されたBearerトークンを使ってリクエストを検証します。このプラグインは、オプションとして2つのフラグを取ります。

  • --service-account-key-file: Bearerトークンに署名するためのPEMエンコードされた鍵を含むファイルです。指定しない場合は、APIサーバーのTLS秘密鍵が使われます。
  • --service-account-lookup: 有効にすると、APIから削除されたトークンは取り消されます。

サービスアカウントは通常、APIサーバーによって自動的に作成され、ServiceAccountAdmission Controllerを介してクラスター内のPodに関連付けられます。Bearerトークンは、Podのよく知られた場所にマウントされ、これによりクラスター内のプロセスがAPIサーバー通信できるようになります。アカウントはPodSpecserviceAccountNameフィールドを使って、明示的にPodに関連付けることができます。

apiVersion: apps/v1 # このapiVersionは、Kubernetes1.9時点で適切です
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
    # ...
    spec:
      serviceAccountName: bob-the-bot
      containers:
      - name: nginx
        image: nginx:1.14.2

サービスアカウントのBearerトークンは、クラスター外で使用するために完全に有効であり、Kubernetes APIと通信したい長期的なジョブのアイデンティティを作成するために使用することができます。サービスアカウントを手動で作成するには、単にkubectl create serviceaccount (NAME)コマンドを使用します。これにより、現在の名前空間にサービスアカウントと関連するSecretが作成されます。

kubectl create serviceaccount jenkins
serviceaccount "jenkins" created

以下のように、関連するSecretを確認できます。

kubectl get serviceaccounts jenkins -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  # ...
secrets:
- name: jenkins-token-1yvwg

作成されたSecretは、APIサーバーのパブリック認証局と署名されたJSON Web Token(JWT)を保持します。

kubectl get secret jenkins-token-1yvwg -o yaml
apiVersion: v1
data:
  ca.crt: (base64でエンコードされたAPIサーバーの認証局)
  namespace: ZGVmYXVsdA==
  token: (base64でエンコードされたBearerトークン)
kind: Secret
metadata:
  # ...
type: kubernetes.io/service-account-token

署名されたJWTは、与えられたサービスアカウントとして認証するためのBearerトークンとして使用できます。トークンをリクエストに含める方法については、リクエストにBearerトークンを含めるを参照してください。通常、これらのSecretはAPIサーバーへのクラスター内アクセス用にPodにマウントされますが、クラスター外からも使用することができます。

サービスアカウントは、ユーザー名system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)で認証され、グループsystem:serviceaccountssystem:serviceaccounts:(NAMESPACE)に割り当てられます。

警告: サービスアカウントトークンはSecretに保持されているため、Secretにアクセスできるユーザーは誰でもサービスアカウントとして認証することができます。サービスアカウントに権限を付与したり、Secretの読み取り機能を付与したりする際には注意が必要です。

OpenID Connectトークン

OpenID Connectは、Azure Active Directory、Salesforce、Googleなど、いくつかのOAuth2プロバイダーでサポートされているOAuth2の一種です。 このプロトコルのOAuth2の主な拡張機能は、ID Tokenと呼ばれる、アクセストークンとアクセストークンと一緒に返される追加フィールドです。 このトークンは、ユーザーの電子メールなどのよく知られたフィールドを持つJSON Web Token(JWT)であり、サーバーによって署名されています。トークンをリクエストに含める方法については、リクエストにBearerトークンを含めるを参照してください。

Kubernetes OpenID Connect Flow

  1. IDプロバイダーにログインします
  2. IDプロバイダーは、access_tokenid_tokenrefresh_tokenを提供します
  3. kubectlを使う場合は、--tokenフラグでid_tokenを使うか、kubeconfigに直接追加してください
  4. kubectlは、id_tokenをAuthorizationと呼ばれるヘッダーでAPIサーバーに送ります
  5. APIサーバーは、設定で指定された証明書と照合することで、JWT署名が有効であることを確認します
  6. id_tokenの有効期限が切れていないことを確認します
  7. ユーザーが認可されていることを確認します
  8. 認可されると、APIサーバーはkubectlにレスポンスを返します
  9. kubectlはユーザーにフィードバックを提供します

自分が誰であるかを確認するために必要なデータはすべてid_tokenの中にあるので、KubernetesはIDプロバイダーと通信する必要がありません。すべてのリクエストがステートレスであるモデルでは、これは非常に認証のためのスケーラブルなソリューションを提供します。一方で、以下のようにいくつか課題があります。

  1. Kubernetesには、認証プロセスを起動するための"Webインターフェース"がありません。クレデンシャルを収集するためのブラウザやインターフェースがないため、まずIDプロバイダに認証を行う必要があります。
  2. id_tokenは、取り消すことができません。これは証明書のようなもので、有効期限が短い(数分のみ)必要があるので、数分ごとに新しいトークンを取得しなければならないのは非常に面倒です。
  3. Kubernetesダッシュボードへの認証において、kubectl proxyコマンドやid_tokenを注入するリバースプロキシを使う以外に、簡単な方法はありません。

APIサーバーの設定

プラグインを有効にするには、APIサーバーで以下のフラグを設定します。

パラメーター説明必須か
--oidc-issuer-urlAPIサーバーが公開署名鍵を発見できるようにするプロバイダーのURLです。 https://スキームを使用するURLのみが受け入れられます。これは通常、"https://accounts.google.com"や"https://login.salesforce.com"のようにパスを持たないプロバイダのディスカバリーURLです。このURLは、.well-known/openid-configurationの下のレベルを指す必要があります。ディスカバリーURLがhttps://accounts.google.com/.well-known/openid-configurationである場合、値はhttps://accounts.google.comとします。はい
--oidc-client-idすべてのトークンが発行されなければならないクライアントIDです。kubernetesはい
--oidc-username-claimユーザー名として使用するJWTのクレームを指定します。デフォルトではsubが使用されますが、これはエンドユーザーの一意の識別子であることが期待されます。管理者はプロバイダーに応じてemailnameなどの他のクレームを選択することができます。ただし、他のプラグインとの名前の衝突を防ぐために、email以外のクレームには、プレフィックスとして発行者のURLが付けられます。subいいえ
--oidc-username-prefix既存の名前(system:ユーザーなど)との衝突を防ぐために、ユーザー名の前にプレフィックスを付加します。例えばoidc:という値は、oidc:jane.doeのようなユーザー名を生成します。このフラグが指定されておらず、--oidc-username-claimemail以外の値である場合、プレフィックスのデフォルトは(Issuer URL)#で、(Issuer URL)--oidc-issuer-urlの値です。すべてのプレフィックスを無効にするためには、-という値を使用できます。oidc:いいえ
--oidc-groups-claimユーザーのグループとして使用するJWTのクレームです。クレームがある場合は、文字列の配列である必要があります。groupsいいえ
--oidc-groups-prefix既存の名前(system:グループなど)との衝突を防ぐために、グループ名の前にプレフィックスを付加します。例えばoidc:という値は、oidc:engineeringoidc:infraのようなグループ名を生成します。oidc:いいえ
--oidc-required-claimIDトークンの中の必須クレームを記述するkey=valueのペアです。設定されている場合、クレームが一致する値でIDトークンに存在することが検証されます。このフラグを繰り返して複数のクレームを指定します。claim=valueいいえ
--oidc-ca-fileIDプロバイダーのWeb証明書に署名した認証局の証明書へのパスです。デフォルトはホストのルート認証局が指定されます。/etc/kubernetes/ssl/kc-ca.pemいいえ

重要なのは、APIサーバーはOAuth2クライアントではなく、ある単一の発行者を信頼するようにしか設定できないことです。これにより、サードパーティーに発行されたクレデンシャルを信頼せずに、Googleのようなパブリックプロバイダーを使用することができます。複数のOAuthクライアントを利用したい管理者は、azpクレームをサポートしているプロバイダや、あるクライアントが別のクライアントに代わってトークンを発行できるような仕組みを検討する必要があります。

KubernetesはOpenID Connect IDプロバイダーを提供していません。既存のパブリックなOpenID Connect IDプロバイダー(Googleやその他など)を使用できます。もしくは、CoreOS dexKeycloak、CloudFoundryUAA、Tremolo SecurityのOpenUnisonなど、独自のIDプロバイダーを実行することもできます。

IDプロバイダーがKubernetesと連携するためには、以下のことが必要です。

  1. すべてではないが、[OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)をサポートしていること
  2. 廃れていない暗号を用いたTLSで実行されていること
  3. 認証局が署名した証明書を持っていること(認証局が商用ではない場合や、自己署名の場合も可)

上述の要件#3、認証局署名付き証明書を必要とすることについて、注意事項があります。GoogleやMicrosoftなどのクラウドプロバイダーではなく、独自のIDプロバイダーをデプロイする場合は、たとえ自己署名されていても、CAフラグがTRUEに設定されている証明書によって署名されたIDプロバイダーのWebサーバー証明書を持っていなければなりません。これは、Go言語のTLSクライアント実装が、証明書検証に関する標準に対して非常に厳格であるためです。認証局をお持ちでない場合は、CoreOSチームのこのスクリプトを使用して、シンプルな認証局と署名付きの証明書と鍵のペアを作成することができます。 または、この類似のスクリプトを使って、より寿命が長く、よりキーサイズの大きいSHA256証明書を生成できます。

特定のシステム用のセットアップ手順は、以下を参照してください。

kubectlの使用

選択肢1 - OIDC認証機能

最初の選択肢は、kubectlのoidc認証機能を利用することです。これはすべてのリクエストのBearerトークンとしてid_tokenを設定し、有効期限が切れるとトークンを更新します。プロバイダーにログインした後、kubectlを使ってid_tokenrefresh_tokenclient_idclient_secretを追加してプラグインを設定します。

リフレッシュトークンのレスポンスの一部としてid_tokenを返さないプロバイダーは、このプラグインではサポートされていないので、以下の"選択肢2"を使用してください。

kubectl config set-credentials USER_NAME \
   --auth-provider=oidc \
   --auth-provider-arg=idp-issuer-url=( issuer url ) \
   --auth-provider-arg=client-id=( your client id ) \
   --auth-provider-arg=client-secret=( your client secret ) \
   --auth-provider-arg=refresh-token=( your refresh token ) \
   --auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
   --auth-provider-arg=id-token=( your id_token )

例として、IDプロバイダーに認証した後に以下のコマンドを実行します。

kubectl config set-credentials mmosley  \
        --auth-provider=oidc  \
        --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP  \
        --auth-provider-arg=client-id=kubernetes  \
        --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5  \
        --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
        --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
        --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

これは以下のような構成になります。

users:
- name: mmosley
  user:
    auth-provider:
      config:
        client-id: kubernetes
        client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
        id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
        idp-certificate-authority: /root/ca.pem
        idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
        refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
      name: oidc

id_tokenの有効期限が切れると、kubectlrefresh_tokenclient_secretを用いてid_tokenの更新しようとします。refresh_tokenid_tokenの新しい値は、.kube/configに格納されます。

選択肢2 - --tokenオプションの使用

kubectlコマンドでは、--tokenオプションを使ってトークンを渡すことができる。以下のように、このオプションにid_tokenをコピーして貼り付けるだけです。

kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Webhookトークン認証

Webhook認証は、Bearerトークンを検証するためのフックです。

  • --authentication-token-webhook-config-file: リモートのWebhookサービスへのアクセス方法を記述した設定ファイルです
  • --authentication-token-webhook-cache-ttl: 認証をキャッシュする時間を決定します。デフォルトは2分です

設定ファイルは、kubeconfigのファイル形式を使用します。 ファイル内で、clustersはリモートサービスを、usersはAPIサーバーのWebhookを指します。例えば、以下のようになります。

# Kubernetes APIのバージョン
apiVersion: v1
# APIオブジェクトの種類
kind: Config
# clustersは、リモートサービスを指します。
clusters:
  - name: name-of-remote-authn-service
    cluster:
      certificate-authority: /path/to/ca.pem         # リモートサービスを検証するためのCA
      server: https://authn.example.com/authenticate # クエリするリモートサービスのURL。'https'を使用する必要があります。

# usersは、APIサーバーのWebhook設定を指します。
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # Webhookプラグインを使うための証明書
      client-key: /path/to/key.pem          # 証明書に合致する鍵

# kubeconfigファイルにはコンテキストが必要です。APIサーバー用のものを用意してください。
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authn-service
    user: name-of-api-sever
  name: webhook

クライアントが上記のようにBearerトークンを使用してAPIサーバーとの認証を試みた場合、認証Webhookはトークンを含むJSONでシリアライズされたauthentication.k8s.io/v1beta1 TokenReviewオブジェクトをリモートサービスにPOSTします。Kubernetesはそのようなヘッダーが不足しているリクエストを作成しようとはしません。

Webhook APIオブジェクトは、他のKubernetes APIオブジェクトと同じように、Versioning Compatibility Ruleに従うことに注意してください。実装者は、ベータオブジェクトで保証される互換性が緩いことに注意し、正しいデシリアライゼーションが使用されるようにリクエストの"apiVersion"フィールドを確認する必要があります。さらにAPIサーバーは、API拡張グループauthentication.k8s.io/v1beta1を有効にしなければなりません(--runtime config=authentication.k8s.io/v1beta1=true)。

POSTボディは、以下の形式になります。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    "token": "(Bearerトークン)"
  }
}

リモートサービスはログインの成功を示すために、リクエストのstatusフィールドを埋めることが期待されます。レスポンスボディのspecフィールドは無視され、省略することができます。Bearerトークンの検証に成功すると、以下のようにBearerトークンが返されます。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      "username": "janedoe@example.com",
      "uid": "42",
      "groups": [
        "developers",
        "qa"
      ],
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    }
  }
}

リクエストに失敗した場合は、以下のように返されます。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false
  }
}

HTTPステータスコードは、追加のエラーコンテキストを提供するために使うことができます。

認証プロキシ

APIサーバーは、X-Remote-Userのようにリクエストヘッダの値からユーザーを識別するように設定することができます。 これは、リクエストヘッダの値を設定する認証プロキシと組み合わせて使用するために設計です。

  • --requestheader-username-headers: 必須であり、大文字小文字を区別しません。ユーザーのIDをチェックするためのヘッダー名を順番に指定します。値を含む最初のヘッダーが、ユーザー名として使われます。
  • --requestheader-group-headers: バージョン1.6以降で任意であり、大文字小文字を区別しません。"X-Remote-Group"を推奨します。ユーザーのグループをチェックするためのヘッダー名を順番に指定します。指定されたヘッダーの全ての値が、グループ名として使われます。
  • --requestheader-extra-headers-prefix バージョン1.6以降で任意であり、大文字小文字を区別しません。"X-Remote-Extra-"を推奨します。ユーザーに関する追加情報を判断するために検索するヘッダーのプレフィックスです。通常、設定された認可プラグインによって使用されます。指定されたプレフィックスのいずれかで始まるヘッダーは、プレフィックスが削除されます。ヘッダー名の残りの部分は小文字化されパーセントデコーディングされて追加のキーとなり、ヘッダーの値が追加の値となります。

例えば、このような設定を行います。

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

以下のようなリクエストを考えます。

GET / HTTP/1.1
X-Remote-User: fido
X-Remote-Group: dogs
X-Remote-Group: dachshunds
X-Remote-Extra-Acme.com%2Fproject: some-project
X-Remote-Extra-Scopes: openid
X-Remote-Extra-Scopes: profile

このリクエストは、このユーザー情報を取得します。

name: fido
groups:
- dogs
- dachshunds
extra:
  acme.com/project:
  - some-project
  scopes:
  - openid
  - profile

ヘッダーのスプーフィングを防ぐため、認証プロキシはリクエストヘッダーがチェックされる前に、指定された認証局に対する検証のために有効なクライアント証明書をAPIサーバーへ提示する必要があります。

  • --requestheader-client-ca-file: 必須です。PEMエンコードされた証明書バンドルです。有効なクライアント証明書を提示し、リクエストヘッダーでユーザー名がチェックされる前に、指定されたファイル内の認証局に対して検証する必要があります。
  • --requestheader-allowed-names: 任意です。Common Name(CN)の値のリストです。設定されている場合、リクエストヘッダーでユーザー名がチェックされる前に、指定されたリストのCNを持つ有効なクライアント証明書を提示する必要があります。空の場合は、任意のCNが許可されます。

匿名リクエスト

この機能を有効にすると、他の設定された認証方法で拒否されなかったリクエストは匿名リクエストとして扱われ、 system:anonymousというユーザー名とsystem:unauthenticatedというグループが与えられます。

例えば、トークン認証が設定されており、匿名アクセスが有効になっているサーバー上で、無効なBearerトークンを提供するリクエストは401 Unauthorizedエラーを受け取ります。Bearerトークンを提供しないリクエストは匿名リクエストとして扱われます。

バージョン1.5.1から1.5.xでは、匿名アクセスはデフォルトでは無効になっており、APIサーバーに --anonymous-auth=trueオプションを渡すことで有効にすることができます。

バージョン1.6以降では、AlwaysAllow以外の認証モードが使用されている場合、匿名アクセスがデフォルトで有効であり、--anonymous-auth=falseオプションをAPIサーバーに渡すことで無効にできます。 1.6以降、ABACおよびRBAC認可機能は、system:anonymousユーザーまたはsystem:unauthenticatedグループの明示的な認証を必要とするようになったため、*ユーザーまたは*グループへのアクセスを許可する従来のポリシールールには匿名ユーザーは含まれません。

ユーザーの偽装

ユーザーは偽装ヘッダーを使って別のユーザーとして振る舞うことができます。これにより、リクエストが認証したユーザー情報を手動で上書きすることが可能です。例えば、管理者はこの機能を使って一時的に別のユーザーに偽装、リクエストが拒否されたかどうかを確認することで認可ポリシーをデバッグすることができます。

偽装リクエストは最初にリクエスト中のユーザーとして認証を行い、次に偽装ユーザー情報に切り替えます。

  • ユーザーは、認証情報と偽装ヘッダーを使ってAPIコールを行います。
  • APIサーバーはユーザーを認証します。
  • APIサーバーは、認証されたユーザーが偽装した権限を持っていることを確認します。
  • リクエストされたユーザー情報は、偽装した値に置き換えられます。
  • リクエストが評価され、認可は偽装されたユーザー情報に基づいて実行されます。

偽装リクエストを実行する際には、以下のHTTPヘッダを使用することができます。

  • Impersonate-User: ユーザー名を指定します。このユーザーとして振る舞います。
  • Impersonate-Group: グループ名を指定します。このグループとして振る舞います。複数回指定して複数のグループを設定することができます。任意であり、"Impersonate-User"が必要です。
  • Impersonate-Extra-( extra name ): 追加フィールドをユーザーに関連付けるために使用される動的なヘッダーです。任意であり、"Impersonate-User"が必要です。一貫して保存されるためには、( extra name )は小文字である必要があり、HTTPヘッダーラベルで使用可能な文字以外の文字は、UTF-8であり、パーセントエンコーディングされている必要があります.

以下が、ヘッダーの例です。

Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development

kubectlを使う場合は、--asフラグにImpersonate-Userヘッダーを、--as-groupフラグにImpersonate-Groupヘッダーを設定します。

kubectl drain mynode
Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

--asフラグと--as-groupフラグを設定します。

kubectl drain mynode --as=superman --as-group=system:masters
node/mynode cordoned
node/mynode drained

ユーザー、グループ、または追加フィールドを偽装するために、偽装ユーザーは偽装される属性の種類("user"、"group"など)に対して、"偽装した"操作を行う能力を持っている必要があります。RBAC認可プラグインが有効なクラスターの場合、以下のClusterRoleは、ユーザーとグループの偽装ヘッダーを設定するために必要なルールを網羅しています。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: impersonator
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]

追加フィールドは、"userextras"リソースのサブリソースとして評価されます。ユーザーが追加フィールド"scopes"に偽装ヘッダーを使用できるようにするには、ユーザーに以下のようなロールを付与する必要があります。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: scopes-impersonator
rules:
# "Impersonate-Extra-scopes"ヘッダーを設定できます。
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]

偽装ヘッダーの値は、リソースが取り得るresourceNamesの集合を制限することで、管理することもできます。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: limited-impersonator
rules:
# "jane.doe@example.com"というユーザーを偽装できます。
- apiGroups: [""]
  resources: ["users"]
  verbs: ["impersonate"]
  resourceNames: ["jane.doe@example.com"]

# "developers"と"admins"というグループを偽装できます。
- apiGroups: [""]
  resources: ["groups"]
  verbs: ["impersonate"]
  resourceNames: ["developers","admins"]

# "view"と"development"を値に持つ"scopes"という追加フィールドを偽装できます。
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]
  resourceNames: ["view", "development"]

client-goクレデンシャルプラグイン

FEATURE STATE: Kubernetes v1.11 [beta]

k8s.io/client-goと、それを使用するkubectlkubeletのようなツールは、外部コマンドを実行してユーザーの認証情報を受け取ることができます。

この機能はk8s.io/client-goがネイティブにサポートしていない認証プロトコル(LDAP、Kerberos、OAuth2、SAMLなど)とクライアントサイドで統合するためのものです。プラグインはプロトコル固有のロジックを実装し、使用する不透明なクレデンシャルを返します。ほとんどすべてのクレデンシャルプラグインのユースケースでは、クライアントプラグインが生成するクレデンシャルフォーマットを解釈するために、Webhookトークン認証をサポートするサーバーサイドコンポーネントが必要です。

使用例

ある組織は、LDAPクレデンシャルをユーザー固有の署名済みトークンと交換する外部サービスを実行すると仮定します。このサービスは、トークンを検証するためにWebhookトークン認証リクエストに応答することもできます。ユーザーはワークステーションにクレデンシャルプラグインをインストールする必要があります。

以下のようにして、APIに対して認証を行います。

  • ユーザーはkubectlコマンドを発行します。
  • クレデンシャルプラグインは、LDAPクレデンシャルの入力をユーザーに要求し、クレデンシャルを外部サービスとトークンと交換します。
  • クレデンシャルプラグインはトークンをclient-goに返します。これはAPIサーバーに対するBearerトークンとして使用されます。
  • APIサーバーは、Webhookトークン認証を使用して、TokenReviewを外部サービスに送信します。
  • 外部サービスはトークンの署名を検証し、ユーザーのユーザー名とグループを返します。

設定

クレデンシャルプラグインの設定は、userフィールドの一部としてkubectlの設定ファイルで行います。

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # 実行するコマンドです。必須です。
      command: "example-client-go-exec-plugin"

      # ExecCredentialsリソースをデコードする際に使用するAPIのバージョン。必須です。
      #
      # プラグインが返すAPIのバージョンは、ここに記載されているバージョンと一致しなければなりません
      #
      # 複数のバージョンをサポートするツール(client.authentication.k8s.io/v1alpha1など)と統合するには、
      # 環境変数を設定するか、execプラグインが期待するバージョンを示す引数をツールに渡します。
      apiVersion: "client.authentication.k8s.io/v1beta1"

      # プラグインを実行する際に設定する環境変数です。任意です。
      env:
      - name: "FOO"
        value: "bar"

      # プラグインを実行する際に渡す引数です。任意です。
      args:
      - "arg1"
      - "arg2"
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

相対的なコマンドパスは、設定ファイルのディレクトリーからの相対的なものとして解釈されます。KUBECONFIGが/home/jane/kubeconfigに設定されていて、execコマンドが./bin/example-client-go-exec-pluginの場合、バイナリ/home/jane/bin/example-client-go-exec-pluginが実行されます。

- name: my-user
  user:
    exec:
      # kubeconfigのディレクトリーへの相対パス
      command: "./bin/example-client-go-exec-plugin"
      apiVersion: "client.authentication.k8s.io/v1beta1"

入出力フォーマット

実行されたコマンドはExecCredentialオブジェクトをstdoutに出力します。k8s.io/client-gostatusで返された認証情報を用いて、Kubernetes APIに対して認証を行ういます。

対話的なセッションから実行する場合、stdinはプラグインに直接公開されます。プラグインはTTYチェックを使って、対話的にユーザーにプロンプトを出すことが適切かどうかを判断する必要があります。

Bearerトークンのクレデンシャルを使用するために、プラグインはExecCredentialのステータスにトークンを返します。

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

あるいは、PEMエンコードされたクライアント証明書と鍵を返して、TLSクライアント認証を使用することもできます。 プラグインが後続の呼び出しで異なる証明書と鍵を返すと、k8s.io/client-goはサーバーとの既存の接続を閉じて、新しいTLSハンドシェイクを強制します

指定された場合、clientKeyDataclientCertificateData両方が存在しなければなりません。

clientCertificateDataには、サーバーに送信するための中間証明書を含めることができます。

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

オプションで、レスポンスにはRFC3339のタイムスタンプとしてフォーマットされたクレデンシャルの有効期限を含めることができます。有効期限の有無には、以下のような影響あります。

  • 有効期限が含まれている場合、BearerトークンとTLSクレデンシャルは有効期限に達するまで、またはサーバーがHTTPステータスコード401で応答したとき、またはプロセスが終了するまでキャッシュされます。
  • 有効期限が省略された場合、BearerトークンとTLSクレデンシャルはサーバーがHTTPステータスコード401で応答したとき、またはプロセスが終了するまでキャッシュされます。
{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

6.4 - API概要

このセクションでは、Kubernetes APIのリファレンス情報を提供します。

REST APIはKubernetesの基本的な構造です。 すべての操作とコンポーネント間の通信、および外部ユーザーのコマンドは、REST API呼び出しでありAPIサーバーが処理します。

その結果、Kubernetesプラットフォーム内のすべてのものは、APIオブジェクトとして扱われ、APIに対応するエントリーがあります。

Kubernetes APIリファレンスは、Kubernetesバージョンv1.30のAPI一覧を提供します。

一般的な背景情報を知るには、The Kubernetes APIControlling Access to the Kubernetes APIを読んでください。 それらはKubernetes APIサーバーがクライアントを認証する方法とリクエストを認可する方法を説明します。

APIバージョニング

JSONとProtobufなどのシリアル化スキーマの変更については同じガイドラインに従います。 以下の説明は、両方のフォーマットをカバーしています。

APIのバージョニングとソフトウェアのバージョニングは間接的に関係しています。 API and release versioning proposalは、APIバージョニングとソフトウェアバージョニングの関係を説明しています。

APIのバージョンが異なると、安定性やサポートのレベルも異なります。 各レベルの基準については、API Changes documentationで詳しく説明しています。

各レベルの概要は以下の通りです:

  • Alpha:

    • バージョン名にalphaが含まれています(例:v1alpha1)。
    • 組み込みのalpha APIバージョンはデフォルトで無効化されており、使用するためにはkube-apiserverの設定で明示的に有効にする必要があります。
    • バグが含まれている可能性があります。 機能を有効にするとバグが露呈する可能性があります。
    • alpha APIのサポートは、予告なしにいつでも中止される可能性があります。
    • 後にリリースされるソフトウェアで、互換性のない方法で予告なく変更される可能性があります。
    • バグのリスクが高く、長期的なサポートが得られないため、短期間のテストクラスターのみでの使用を推奨します。
  • Beta:

    • バージョン名には beta が含まれています(例:v2beta3)。

    • 組み込みのbeta APIバージョンはデフォルトで無効化されており、使用するためにはkube-apiserverの設定で明示的に有効にする必要があります。 (例外としてKubernetes 1.22以前に導入されたAPIのbetaバージョンはデフォルトで有効化されています)

    • 組み込みのbeta APIバージョンは、導入から非推奨となるまでが9ヶ月または3マイナーリリース(どちらかの長い期間)、そして非推奨から削除まで9ヶ月または3マイナーリリース(どちらかの長い期間)の最大存続期間を持ちます。

    • ソフトウェアは十分にテストされています。 機能を有効にすることは安全であると考えられています。

    • 機能のサポートが打ち切られることはありませんが、詳細は変更される可能性があります。

    • オブジェクトのスキーマやセマンティクスは、その後のベータ版や安定版のAPIバージョンで互換性のない方法で変更される可能性があります。 このような場合には、移行手順が提供されます。 後続のbetaまたはstable APIバージョンに適応することはAPIオブジェクトの編集や再作成が必要になる場合があり、単純ではないかもしれません。 移行に伴い、その機能に依存しているアプリケーションのダウンタイムが必要になる場合があります。

    • 本番環境での使用は推奨しません。 後続のリリースは、互換性のない変更を導入する可能性があります。 beta APIを使用する場合、そのbeta APIが非推奨となり提供されなくなった際には、後続のbetaまたはstable APIバージョンに移行する必要があります。

  • Stable:

    • バージョン名は vX であり、X は整数である。
    • stable APIバージョンはKubernetesのメジャーバージョン内の全てのリリースで利用可能であり、stable APIを削除するようなKubernetesのメジャーバージョンの修正は、現在計画されていません。

APIグループ

API groupsで、KubernetesのAPIを簡単に拡張することができます。 APIグループは、RESTパスとシリアル化されたオブジェクトのapiVersionフィールドで指定されます。

KubernetesにはいくつかのAPIグループがあります:

  • core(legacyとも呼ばれる)グループは、RESTパス /api/v1 にあります。 コアグループは apiVersion フィールドの一部としては指定されません。 例えば、apiVersion: v1 のように。
  • 名前付きのグループは、RESTパス /apis/$GROUP_NAME/$VERSION にあり、以下のように使用します。 apiVersion: $GROUP_NAME/$VERSIONを使用します(例:apiVersion: batch/v1)。 サポートされているAPIグループの完全なリストは以下にあります。 Kubernetes API reference

APIグループの有効化と無効化

一部のリソースやAPIグループはデフォルトで有効になっています。 APIサーバー上で--runtime-configを設定することで、有効にしたり無効にしたりすることができます。 またruntime-configフラグには、APIサーバーのランタイム構成を記述したコンマ区切りの<key>[=<value>]ペアを指定します。 もし=<value>の部分が省略された場合には、=trueが指定されたものとして扱われます。

例えば:

  • batch/v1を無効するには、--runtime-config=batch/v1=falseを設定する
  • batch/v2alpha1を有効するには、--runtime-config=batch/v2alpha1を設定する

永続化

Kubernetesはシリアライズされた状態を、APIリソースとしてetcdに書き込んで保存します。

次の項目

6.5 - RBAC認可を使用する

Role Based Access Control(RBAC)は、組織内の個々のユーザーのRoleをベースに、コンピューターまたはネットワークリソースへのアクセスを制御する方法です。

RBAC認可はAPIグループ rbac.authorization.k8s.ioを使用して認可の決定を行い、Kubernetes APIを介して動的にポリシーを構成できるようにします。

RBACを有効にするには、以下の例のようにAPI server--authorization-mode フラグをコンマ区切りのRBACを含むリストでスタートします。

kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options

APIオブジェクト

RBAC APIは4種類のKubernetesオブジェクト(RoleClusterRoleRoleBinding そして ClusterRoleBinding)を宣言します。他のKubernetesオブジェクトのようにkubectlのようなツールを使って、オブジェクトを記述、または変更できます。

RoleとClusterRole

RBACの Role または ClusterRole には、一連の権限を表すルールが含まれて言います。 権限は完全な追加方式です(「deny」のルールはありません)。

Roleは常に特定のnamespaceで権限を設定します。 つまり、Roleを作成する時は、Roleが属するNamespaceを指定する必要があります。

対照的にClusterRoleは、Namespaceに属さないリソースです。Kubernetesオブジェクトは常にNamespaceに属するか、属さないかのいずれかである必要があり、リソースは異なる名前(RoleとClusterRole)を持っています。つまり、両方であることは不可能です。

ClusterRolesにはいくつかの用途があります。ClusterRoleを利用して、以下のことができます。

  1. Namespaceに属するリソースに対する権限を定義し、個々のNamespace内で付与する
  2. Namespaceに属するリソースに対する権限を定義し、すべてのNamespaceにわたって付与する
  3. クラスター単位でスコープされているリソースに対するアクセス許可を定義する

NamespaceでRoleを定義する場合は、Roleを使用します。クラスター全体でRoleを定義する場合は、ClusterRoleを使用します

Roleの例

以下はNamespace「default」にあるRoleの例で、 Podへの読み取りアクセス権の付与に使用できます。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" はコアのAPIグループを示します
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

ClusterRoleの例

ClusterRoleを使用してRoleと同じ権限を付与できます。 ClusterRolesはクラスター単位でスコープされているため、以下へのアクセスの許可もできます。

  • クラスター単位でスコープされているリソースに(nodeなど)
  • 非リソースエンドポイントに(/healthzなど)
  • すべてのNamespaceに渡ってNamespaceに属するリソースに(Podなど)。 例えば、ClusterRoleを使用して特定のユーザーにkubectl get pods --all-namespacesの実行を許可できます。

以下は特定のNamespace、またはすべてのNamespace(バインド方法によります)でsecretsへの読み取りアクセス権を付与するClusterRoleの例です。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # 「namespace」はClusterRolesがNamespaceに属していないため、省略されています
  name: secret-reader
rules:
- apiGroups: [""]
  #
  # HTTPレベルでの、Secretにアクセスするリソースの名前
  # オブジェクトは"secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

RoleまたはClusterRoleオブジェクトの名前は有効な パスセグメント名である必要があります。

RoleBindingとClusterRoleBinding

RoleBindingはRoleで定義された権限をユーザーまたはユーザのセットに付与します。 RoleBindingはsubjects (ユーザー、グループ、サービスアカウント)のリストと、付与されるRoleへの参照を保持します。 RoleBindingは特定のNamespace内の権限を付与しますが、ClusterRoleBindingはクラスター全体にアクセスする権限を付与します。

RoleBindingは、同じNamespace内の任意のRoleを参照できます。 または、RoleBindingはClusterRoleを参照し、そのClusterRoleをRoleBindingのNamespaceにバインドできます。 ClusterRoleをクラスター内のすべてのNamespaceにバインドする場合は、ClusterRoleBindingを使用します。

RoleBindingまたはClusterRoleBindingオブジェクトは有効な パスセグメント名である必要があります。

RoleBindingの例

以下はNamespace「default」内でユーザー「jane」に「pod-reader」のRoleを付与するRoleBindingの例です。 これにより、「jane」にNamespace「default」のポッドの読み取り許可されます。

apiVersion: rbac.authorization.k8s.io/v1
# このRoleBindingは「jane」にNamespace「default」のポッドの読み取りを許可する
# そのNamespaceでRole「pod-reader」を既に持っている必要があります。
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 一つ以上の「subject」を指定する必要があります
- kind: User
  name: jane # 「name」は大文字と小文字が区別されます
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # 「roleRef」はRole/ClusterRoleへのバインドを指定します
  kind: Role #RoleまたはClusterRoleである必要があります
  name: pod-reader # これはバインドしたいRole名またはClusterRole名とマッチする必要があります
  apiGroup: rbac.authorization.k8s.io

RoleBindingはClusterRoleを参照し、ClusterRoleで定義されている権限をRoleBinding内のNamespaceのリソースに権限付与もできます。この種類の参照を利用すると、クラスター全体で共通のRoleのセットを定義して、それらを複数のNamespace内での再利用できます。

例えば、以下のRoleBindingがClusterRoleを参照している場合でも、 「dave」(大文字と小文字が区別されるsubject)はRoleBindingのNamespace(メタデータ内)が「development」のため、Namespace「development」のSecretsのみの読み取りができます。

apiVersion: rbac.authorization.k8s.io/v1
# このRoleBindingは「dave」にNamespace「development」のSecretsの読み取りを許可する
# ClusterRole「secret-reader」を既に持っている必要があります。
kind: RoleBinding
metadata:
  name: read-secrets
  #
  # RoleBindingのNamespaceが、どこの権限が付与されるかを決定する。
  # これはNamespace「development」内の権限のみ付与する。
  namespace: development
subjects:
- kind: User
  name: dave # nameは大文字、小文字を区別する
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBindingの例

クラスター全体に権限を付与するには、ClusterRoleBindingを使用できます。 以下のClusterRoleBindingはグループ「manager」のすべてのユーザーに Secretsの読み取りを許可します。

apiVersion: rbac.authorization.k8s.io/v1
# このClusterRoleBindingはグループ「manager」のすべてのユーザーに任意のNamespaceのSecretsの読み取りを許可します。
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # nameは大文字、小文字を区別します
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

Bindingを作成後は、それが参照するRoleまたはClusterRoleを変更できません。 BindingのroleRefを変更しようとすると、バリデーションエラーが発生します。BindingのroleRefを変更する場合は、Bindingのオブジェクトを削除して、代わりのオブジェクトを作成する必要があります。

この制限には2つの理由があります。

  1. roleRefをイミュータブルにすることで、誰かに既存のオブジェクトに対するupdate権限を付与します。それにより、subjectsに付与されたRoleの変更ができなくても、subjectsのリストを管理できるようになります。
  2. 異なるRoleへのBindingは根本的に異なるBindingです。 roleRefを変更するためにBindingの削除/再作成を要求することによって、(すべての既存のsubjectsを確認せずに、roleRefだけを誤って変更できるようにするのとは対照的に)Binding内のsubjectsのリストのすべてが意図された新しいRoleが付与されることを担保します。

kubectl auth reconcile コマンドラインユーティリティーはRBACオブジェクトを含んだマニフェストファイルを作成または更新します。また、それらが参照しているRoleへの変更を要求されると、Bindingオブジェクトの削除と再作成を取り扱います。 詳細はcommand usage and examplesを確認してください。

リソースを参照する

KubernetesのAPIでは、ほとんどのリソースはPodであればpodsのように、オブジェクト名の文字列表現を使用して表されます。RBACは、関連するAPIエンドポイントのURLに表示されるものとまったく同じ名前を使用するリソースを参照します。 一部のKubernetes APIには、Podのログなどの subresource が含まれます。Podのログのリクエストは次のようになります。

GET /api/v1/namespaces/{namespace}/pods/{name}/log

この場合、pods はPodリソースのNamespaceに属するリソースであり、logpodsのサブリソースです。これをRBACRoleで表すには、スラッシュ(/)を使用してリソースとサブリソースを区切ります。サブジェクトへのpodsの読み込みと各Podのlogサブリソースへのアクセスを許可するには、次のように記述します。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

resourceNamesリストを通じて、特定のリクエストのリソースを名前で参照することもできます。 指定すると、リクエストをリソースの個々のインスタンスに制限できます。 以下は対象をgetまたはmy-configmapと名付けられた ConfigMapupdateのみに制限する例です。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: configmap-updater
rules:
- apiGroups: [""]
  #
  # HTTPレベルでの、ConfigMapにアクセスするリソースの名前
  # オブジェクトは"configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-configmap"]
  verbs: ["update", "get"]

集約ClusterRole

複数のClusterRoleを一つのClusterRoleに 集約 できます。 クラスターコントロールプレーンの一部として実行されるコントローラーは、aggregationRuleセットを持つClusterRoleオブジェクトを監視します。aggregationRuleはコントローラーが、このオブジェクトのrulesフィールドに結合する必要のある他のClusterRoleオブジェクトを一致させるために使用するラベルselectorを定義します。

以下に、集約されたClusterRoleの例を示します。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # コントロールプレーンは自動的にルールを入力します

既存の集約されたClusterRoleのラベルセレクターと一致する新しいClusterRoleを作成すると、その変更をトリガーに、集約されたClusterRoleに新しいルールが追加されます。 rbac.example.com/aggregate-to-monitoring: trueラベルが付けられた別のClusterRoleを作成して、ClusterRole「monitoring」にルールを追加する例を以下に示します。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# ClusterRole「monitoring-endpoints」を作成すると、
# 以下のルールがClusterRole「monitoring」に追加されます
rules:
- apiGroups: [""]
  resources: ["services", "endpoints", "pods"]
  verbs: ["get", "list", "watch"]

デフォルトのユーザー向けRoleはClusterRoleの集約を使用します。これによりクラスター管理者として、 デフォルトroleを拡張するため、CustomResourceDefinitionsまたは集約されたAPIサーバーなどによって提供されたルールをカスタムリソースに含めることができます。

例えば、次のClusterRoleでは、「admin」と「edit」のデフォルトのRoleでCronTabという名前のカスタムリソースを管理できますが、「view」のRoleではCronTabリソースに対して読み取りアクションのみを実行できます。CronTabオブジェクトは、APIサーバーから見たURLで"crontabs"と名前が付けられていると想定できます。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: aggregate-cron-tabs-edit
  labels:
    # デフォルトRoleの「admin」と「edit」にこれらの権限を追加する。
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: aggregate-cron-tabs-view
  labels:
    # デフォルトRoleの「view」にこれらの権限を追加します。
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch"]

Roleの例

次の例は、RoleオブジェクトまたはClusterRoleオブジェクトからの抜粋であり、rulesセクションのみを示しています。

"pods"の読み取りを許可する API Group

rules:
- apiGroups: [""]
  #
  # HTTPレベルでの、Podにアクセスするリソースの名前
  # オブジェクトは"pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

APIグループ" extensions "" apps " の両方で、Deploymentsへの読み取り/書き込みを許可します。 (HTTPレベルでURLのリソース部分に"deployments"を持つオブジェクトで)

rules:
- apiGroups: ["extensions", "apps"]
  #
  # HTTPレベルでの、Deploymentにアクセスするリソースの名前
  # オブジェクトは"deployments"
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

コアAPIグループのPodの読み取り、および"batch"または"extensions"APIグループのJobリソースの読み取りまたは書き込みを許可します。

rules:
- apiGroups: [""]
  #
  # HTTPレベルでの、Podにアクセスするリソースの名前
  # オブジェクトは"pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch", "extensions"]
  #
  # HTTPレベルでの、Jobにアクセスするリソースの名前
  # オブジェクトは"jobs"
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

「my-config」という名前のConfigMapの読み取りを許可します( 単一のNamespace内の単一のConfigMapに制限するRoleBinding)

rules:
- apiGroups: [""]
  #
  # HTTPレベルでの、ConfigMapにアクセスするリソースの名前
  # オブジェクトは"configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]

コアグループ内のリソース "nodes"の読み取りを許可します(Nodeはクラスタースコープであり、ClusterRoleBindingが効果的であるため、ClusterRoleにバインドされている必要があります)。

rules:
- apiGroups: [""]
  #
  # HTTPレベルでの、Nodeにアクセスするリソースの名前
  # オブジェクトは"nodes"
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]

非リソースエンドポイント / healthzおよびすべてのサブパス(ClusterRoleBindingが効果的であるため、ClusterRoleにバインドされている必要があります)のGETおよびPOSTリクエストを許可します。

rules:
- nonResourceURLs: ["/healthz", "/healthz/*"] # nonResourceURLの「*」はサフィックスグロブマッチです
  verbs: ["get", "post"]

subjectsを参照する

RoleBindingまたはClusterRoleBindingは、Roleをsubjectsにバインドします。subjectsはグループ、ユーザー、またはServiceAccountsにすることができます。

Kubernetesはユーザー名を文字列として表します。 これらは次のようにできます。「alice」などの単純な名前。「bob@example.com」のような電子メール形式の名前。または文字列として表される数値のユーザーID。 認証が必要な形式のユーザー名を生成するように認証モジュールを構成するかどうかは、クラスター管理者が決定します。

Kubernetesでは、Authenticatorモジュールがグループ情報を提供します。 ユーザーと同様に、グループは文字列として表され、その文字列には、プレフィックスsystem:が予約されていることを除いて、フォーマット要件はありません。

ServiceAccountの名前はプレフィックスsystem:serviceaccount:が付いており、名前のプレフィックスsystem:serviceaccounts:が付いているグループに属しています。

RoleBindingの例

次の例はRoleBindingsubjectsセクションのみを示す抜粋です。

alice@example.comという名前のユーザーの場合。

subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io

frontend-adminsという名前のグループの場合。

subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io

Namespace「kube-system」のデフォルトのサービスアカウントの場合。

subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

Namespace「qa」の全てのサービスアカウントの場合。

subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io

任意のNamespaceの全てのサービスアカウントの場合。

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

すべての認証済みユーザーの場合。

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io

認証されていないすべてのユーザーの場合。

subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

すべてのユーザーの場合。

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

デフォルトRoleとClusterRoleBinding

APIサーバーは、デフォルトのClusterRoleオブジェクトとClusterRoleBindingオブジェクトのセットを作成します。 これらの多くにはプレフィックスsystem:が付いています。これは、リソースがクラスターコントロールプレーンによって直接管理されることを示しています。 デフォルトのすべてのClusterRoleおよびClusterRoleBindingには、ラベルkubernetes.io/bootstrapping=rbac-defaultsが付いています。

自動調整

起動するたびに、APIサーバーはデフォルトのClusterRoleを不足している権限で更新し、 デフォルトのClusterRoleBindingを不足しているsubjectsで更新します。 これにより、誤った変更をクラスターが修復できるようになり、新しいKubernetesリリースで権限とsubjectsが変更されても、 RoleとRoleBindingを最新の状態に保つことができます。

この調整を無効化するにはrbac.authorization.kubernetes.io/autoupdateをデフォルトのClusterRoleまたはRoleBindingのアノテーションをfalseに設定します。 デフォルトの権限と subjectsがないと、クラスターが機能しなくなる可能性があることに注意してください。

RBAC authorizerが有効な場合、自動調整はデフォルトで有効になっています。

APIディスカバリーRole

デフォルトのRoleBindingでは、認証されていないユーザーと認証されたユーザーが、パブリックアクセスが安全であると見なされるAPI情報(CustomResourceDefinitionを含む)の読み取りを認可しています。匿名の非認証アクセスを無効にするには、APIサーバー構成に--anonymous-auth=false 追加します。

kubectlの実行によってこれらのRoleの構成を表示するには。

kubectl get clusterroles system:discovery -o yaml
Kubernetes RBAC APIディスカバリーRole
デフォルトのClusterRoleデフォルトのClusterRoleBinding説明
system:basic-usersystem:authenticated groupユーザーに、自身に関する基本情報への読み取り専用アクセスを許可します。v1.14より前は、このRoleはデフォルトでsystem:unauthenticatedにもバインドされていました。
system:discoverysystem:authenticated groupAPIレベルのディスカバリーとネゴシエーションに必要なAPIディスカバリーエンドポイントへの読み取り専用アクセスを許可します。v1.14より前は、このRoleはデフォルトでsystem:unauthenticatedにもバインドされていました。
system:public-info-viewersystem:authenticated and system:unauthenticated groupsクラスターに関する機密情報以外への読み取り専用アクセスを許可します。Kubernetes v1.14で導入されました。

ユーザー向けRole

一部のデフォルトClusterRolesにはプレフィックスsystem:が付いていません。これらは、ユーザー向けのroleを想定しています。それらは、スーパーユーザのRole(cluster-admin)、ClusterRoleBindingsを使用してクラスター全体に付与されることを意図しているRole、そしてRoleBindings(admin, edit, view)を使用して、特定のNamespace内に付与されることを意図しているRoleを含んでいます。

ユーザー向けのClusterRolesはClusterRoleの集約を使用して、管理者がこれらのClusterRolesにカスタムリソースのルールを含めることができるようにします。ルールをadminedit、またはview Roleに追加するには、次のラベルの一つ以上でClusterRoleを作成します。

metadata:
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-view: "true"

デフォルトのClusterRoleデフォルトのClusterRoleBinding説明
cluster-adminsystem:masters groupスーパーユーザーが任意のリソースで任意のアクションを実行できるようにします。 ClusterRoleBindingで使用すると、クラスター内およびすべてのNamespace内のすべてのリソースを完全に制御できます。 RoleBindingで使用すると、Namespace自体を含む、RoleBindingのNamespace内のすべてのリソースを完全に制御できます。
adminNoneRoleBindingを使用してNamespace内で付与することを想定した、管理者アクセスを許可します。 RoleBindingで使用した場合、Namespace内にRoleとRoleBindingを作成する機能を含め、Namespaceのほとんどのリソースへの読み取り/書き込みアクセスを許可します。 このRoleは、リソースクォータまたはNamespace自体への書き込みアクセスを許可しません。
editNoneNamespace内のほとんどのオブジェクトへの読み取り/書き込みアクセスを許可します。

このRoleは、RoleまたはRoleBindingの表示または変更を許可しません。 ただし、このRoleでは、Secretsにアクセスして、Namespace内の任意のServiceAccountとしてPodsを実行できるため、Namespace内の任意のServiceAccountのAPIアクセスレベルを取得するために使用できます。

viewNoneNamespace内のほとんどのオブジェクトを表示するための読み取り専用アクセスを許可します。 RoleまたはRoleBindingは表示できません。

Secretsの内容を読み取るとNamespaceのServiceAccountのクレデンシャルにアクセスできるため、このRoleではSecretsの表示は許可されません。これにより、Namespace内の任意のServiceAccountとしてAPIアクセスが許可されます(特権昇格の形式)。

コアコンポーネントのRole

デフォルトのClusterRoleデフォルトのClusterRoleBinding説明
system:kube-schedulersystem:kube-scheduler userschedulerコンポーネントが必要とするリソースへのアクセスを許可します。
system:volume-schedulersystem:kube-scheduler userkube-scheduleコンポーネントが必要とするリソースへのアクセスを許可します。
system:kube-controller-managersystem:kube-controller-manager usercontroller managerコンポーネントが必要とするリソースへのアクセスを許可します。 個々のコントローラーに必要な権限については、組み込みコントローラーのRoleで詳しく説明しています
system:nodeNoneすべてのsecretへの読み取りアクセス、すべてのポッドステータスオブジェクトへの書き込みアクセスなど、kubeletが必要とするリソースへのアクセスを許可します。

system:nodeRoleの代わりにNode authorizerNodeRestriction admission pluginを使用し、それらで実行するようにスケジュールされたPodに基づいてkubeletへのAPIアクセスを許可する必要があります。

system:nodeのRoleは、V1.8より前のバージョンからアップグレードしたKubernetesクラスターとの互換性のためだけに存在します。

system:node-proxiersystem:kube-proxy userkube-proxyコンポーネントが必要とするリソースへのアクセスを許可します。

他のコンポーネントのRole

デフォルトのClusterRoleデフォルトのClusterRoleBinding説明
system:auth-delegatorNone委任された認証と認可のチェックを許可します。 これは一般に、認証と認可を統合するためにアドオンAPIサーバーで使用されます。
system:heapsterNoneHeapsterコンポーネントのRole(非推奨)。
system:kube-aggregatorNonekube-aggregatorコンポーネントのRole。
system:kube-dnskube-systemNamespaceのサービスアカウントkube-dnskube-dnsコンポーネントのRole。
system:kubelet-api-adminNonekubelet APIへのフルアクセスを許可します。
system:node-bootstrapperNonekubelet TLS bootstrappingの実行に必要なリソースへのアクセスを許可します。
system:node-problem-detectorNonenode-problem-detectorコンポーネントのRole。
system:persistent-volume-provisionerNoneほとんどのdynamic volume provisionersが必要とするリソースへのアクセスを許可します。

組み込みコントローラーのRole

Kubernetes controller managerは、Kubernetesコントロールプレーンに組み込まれているcontrollersを実行します。 --use-service-account-credentialsを指定して呼び出すと、kube-controller-manager個別のサービスアカウントを使用して各コントローラーを起動します。 組み込みコントローラーごとに、プレフィックスsystem:controller:付きの対応するRoleが存在します。 コントローラーマネージャーが--use-service-account-credentialsで開始されていない場合、コントローラーマネージャーは、関連するすべてのRoleを付与する必要がある自身のクレデンシャルを使用して、すべてのコントロールループを実行します。 これらのRoleは次のとおりです。

  • system:controller:attachdetach-controller
  • system:controller:certificate-controller
  • system:controller:clusterrole-aggregation-controller
  • system:controller:cronjob-controller
  • system:controller:daemon-set-controller
  • system:controller:deployment-controller
  • system:controller:disruption-controller
  • system:controller:endpoint-controller
  • system:controller:expand-controller
  • system:controller:generic-garbage-collector
  • system:controller:horizontal-pod-autoscaler
  • system:controller:job-controller
  • system:controller:namespace-controller
  • system:controller:node-controller
  • system:controller:persistent-volume-binder
  • system:controller:pod-garbage-collector
  • system:controller:pv-protection-controller
  • system:controller:pvc-protection-controller
  • system:controller:replicaset-controller
  • system:controller:replication-controller
  • system:controller:resourcequota-controller
  • system:controller:root-ca-cert-publisher
  • system:controller:route-controller
  • system:controller:service-account-controller
  • system:controller:service-controller
  • system:controller:statefulset-controller
  • system:controller:ttl-controller

特権昇格の防止とブートストラップ

RBAC APIは、RoleまたはRoleBindingを編集することにより、ユーザーが特権を昇格するのを防ぎます。 これはAPIレベルで適用されるため、RBAC authorizerが使用されていない場合でも適用されます。

Roleの作成または更新に関する制限

次の項目1つ以上が当てはまる場合にのみ、Roleを作成/更新できます。

  1. 変更対象のオブジェクトと同じスコープで、Roleに含まれるすべての権限を既に持っている(ClusterRoleの場合はクラスター全体。Roleの場合は、同じNamespace内またはクラスター全体)。
  2. rbac.authorization.k8s.ioAPIグループの rolesまたはclusterrolesリソースで escalate verbを実行する明示的な権限が付与されている。

たとえば、 user-1にクラスター全体でSecretsを一覧表示する権限がない場合、それらにその権限を含むClusterRoleを作成できません。 ユーザーがRoleを作成/更新できるようにするには、以下のいずれかを実施します。

  1. 必要に応じて、RoleオブジェクトまたはClusterRoleオブジェクトを作成/更新できるRoleを付与する。
  2. 作成/更新するRoleに特定の権限を含む権限を付与する。
    • 暗黙的に、これらの権限を付与することにより(自分自身が付与されていない権限でRoleまたはClusterRoleを作成または変更しようとすると、APIリクエストは禁止されます)。
    • または、rbac.authorization.k8s.ioAPIグループの rolesまたは clusterrolesリソースで escalate verbを実行する権限を与えることにより、 Roleまたは ClusterRoleで権限を指定することを明示的に許可する

RoleBindingの作成または更新に関する制限

参照されるRoleに含まれるすべての権限を(RoleBindingと同じスコープで)すでに持っている場合、 または参照されたRoleでbind verbを実行する認可されている場合のみ、RoleBindingを作成/更新できます。 たとえば、 user-1にクラスター全体でSecretsを一覧表示する権限がない場合、ClusterRoleBindingを作成してもRoleにその権限を付与できません。 ユーザーがRoleBindingを作成/更新できるようにするには、以下のいずれかを実施します。

  1. 必要に応じて、RoleBindingまたはClusterRoleBindingオブジェクトを作成/更新できるようにする役割を付与する。
  2. 特定の役割をバインドするために必要なアクセス許可を付与する。
    • 暗黙的に、Roleに含まれる権限を付与することによって。
    • 明示的に、特定のRole(またはClusterRole)で bind verbを実行する許可を与えることによって。

たとえば、このClusterRoleとRoleBindingを使用すると、 user-1は他のユーザーにNamespace user-1-namespaceadmin edit、および viewRoleを付与します。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterroles"]
  verbs: ["bind"]
  resourceNames: ["admin","edit","view"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: role-grantor-binding
  namespace: user-1-namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: role-grantor
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: user-1

最初のRoleとRoleBindingをブートストラップするときは、最初のユーザーがまだ持っていない権限を付与する必要があります。 初期RoleとRoleBindingをブートストラップするには、以下のいずれかを実施します。

  • 「system:masters」グループのクレデンシャルを使用します。このグループは、デフォルトのBindingによって「cluster-admin」スーパーユーザーRoleにバインドされています。
  • APIサーバーが安全でないポート(--insecure-port)を有効にして実行されている場合、そのポートを介してのAPI呼び出しもできます。これにより、認証や認可が実行されません。

コマンドラインユーティリティー

kubectl create role

以下に、単一のNamespace内で権限を定義するRoleオブジェクトをいくつか例として作成します。

  • ユーザーがポッドで get watch、および listを実行できるように「pod-reader」という名前のRoleを作成します。

    kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
    
  • resourceNamesを指定して、「pod-reader」という名前のRoleを作成します。

    kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • apiGroupsを指定して「foo」という名前のRoleを作成します。

    kubectl create role foo --verb=get,list,watch --resource=replicasets.apps
    
  • サブリソースの権限を持つ「foo」という名前のRoleを作成します。

  • Create a Role named "foo" with subresource permissions:

    kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
    
  • 特定の名前のリソースを取得/更新する権限を持つ「my-component-lease-holder」という名前のRoleを作成します。

    kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component
    

kubectl create clusterrole

以下にClusterRoleをいくつか例として作成します。

  • ユーザーがポッドに対してget watch、および listを実行できるようにする「pod-reader」という名前のClusterRoleを作成します。

    kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
    
  • resourceNamesを指定して、「pod-reader」という名前のClusterRoleを作成します。

    kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • apiGroupsを指定して「foo」という名前のClusterRoleを作成します。

    kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps
    
  • サブリソースの権限を持つ「foo」という名前のClusterRoleを作成します。

    kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
    
  • nonResourceURLを指定して「foo」という名前のClusterRoleを作成します。

    kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
    
  • aggregationRuleを指定して、「monitoring」という名前のClusterRoleを作成します。

    kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
    

kubectl create rolebinding

以下に、特定のNamespace内でRoleまたはClusterRoleをいくつか例として付与します。

  • Namespace「acme」内で、「admin」ClusterRoleの権限を「bob」という名前のユーザーに付与します。

    kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme
    
  • Namespace「acme」内で、ClusterRole「view」へのアクセス許可を「myapp」というNamespace「acme」のサービスアカウントに付与します。

    kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme
    
  • Namespace「acme」内で、ClusterRole「view」へのアクセス許可を「myapp」というNamespace「myappnamespace」のサービスアカウントに付与します。

    kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole=view --serviceaccount=myappnamespace:myapp --namespace=acme
    

kubectl create clusterrolebinding

以下に、クラスター全体(すべてのNamespace)にClusterRoleをいくつか例として付与します。

  • クラスター全体で、ClusterRole「cluster-admin」へのアクセス許可を「root」という名前のユーザーに付与します。

    kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root
    
  • クラスター全体で、ClusterRole「system:node-proxier」へのアクセス許可を「system:kube-proxy」という名前のユーザーに付与します。

    kubectl create clusterrolebinding kube-proxy-binding --clusterrole=system:node-proxier --user=system:kube-proxy
    
  • クラスター全体で、ClusterRole「view」へのアクセス許可を、Namespace「acme」の「myapp」という名前のサービスアカウントに付与します。

    kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp
    

kubectl auth reconcile

マニフェストファイルから rbac.authorization.k8s.io/v1APIオブジェクトを作成または更新します。

欠落しているオブジェクトが作成され、必要に応じて、Namespaceに属するオブジェクト用にオブジェクトを含むNamespaceが作成されます。

既存のRoleが更新され、入力オブジェクトに権限が含まれるようになります。 --remove-extra-permissionsが指定されている場合は、余分な権限を削除します。

既存のBindingが更新され、入力オブジェクトにsubjectsが含まれるようになります。 --remove-extra-subjectsが指定されている場合は、余分な件名を削除します。

以下、例として。

  • RBACオブジェクトのマニフェストファイルをテストとして適用し、行われる変更を表示します。

    kubectl auth reconcile -f my-rbac-rules.yaml --dry-run=client
    
  • RBACオブジェクトのマニフェストファイルを適用し、(Role内の)追加のアクセス許可と(Binding内の)追加のsubjectsを保持します。

    kubectl auth reconcile -f my-rbac-rules.yaml
    
  • RBACオブジェクトのマニフェストファイルを適用し、(Role内の)余分なアクセス許可と(Binding内の)余分なsubjectsを削除します。

ServiceAccount権限

デフォルトのRBACポリシーは、コントロールプレーンコンポーネント、ノード、 およびコントローラーをスコープとして権限を付与しますが、 Namespacekube-system外のサービスアカウントにはno permissionsで付与します (すべての認証されたユーザーに与えられたディスカバリー権限に関わらず)。

これにより、必要に応じて特定のServiceAccountに特定のRoleを付与できます。 きめ細かいRoleBindingはセキュリティを強化しますが、管理にはより多くの労力が必要です。 より広範な権限は、不必要な(そして潜在的にエスカレートする)APIアクセスをServiceAccountsに与える可能性がありますが、管理が簡単です。

アプローチを最も安全なものから最も安全でないものの順に並べると、次のとおりです。

  1. アプリケーション固有のサービスアカウントにRoleを付与する(ベストプラクティス) これには、アプリケーションがpodのspec、そして作成するサービスアカウント(API、アプリケーションマニフェスト、 kubectl create serviceaccountなどを介して)でserviceAccountNameを指定する必要があります。 たとえば、「my-namespace」内の読み取り専用権限を「my-sa」サービスアカウントに付与します。

    kubectl create rolebinding my-sa-view \
      --clusterrole=view \
      --serviceaccount=my-namespace:my-sa \
      --namespace=my-namespace
    
  2. あるNamespaceのサービスアカウント「default」にRoleを付与します

    アプリケーションが serviceAccountNameを指定しない場合、サービスアカウント「default」を使用します。

    たとえば、「my-namespace」内の読み取り専用権限をサービスアカウント「default」に付与します。

    kubectl create rolebinding default-view \
      --clusterrole=view \
      --serviceaccount=my-namespace:default \
      --namespace=my-namespace
    

    多くのアドオンは、 Namespacekube-systemのサービスアカウント「default」として実行されます。 これらのアドオンをスーパーユーザーアクセスでの実行を許可するには、Namespacekube-systemのサービスアカウント「default」のcluster-admin権限を付与します。

    kubectl create clusterrolebinding add-on-cluster-admin \
      --clusterrole=cluster-admin \
      --serviceaccount=kube-system:default
    
  3. Namespace内のすべてのサービスアカウントにRoleを付与します

    Namespace内のすべてのアプリケーションにRoleを持たせたい場合は、使用するサービスアカウントに関係なく、 そのNamespaceのサービスアカウントグループにRoleを付与できます。

    たとえば、「my-namespace」内の読み取り専用アクセス許可を、そのNamespace内のすべてのサービスアカウントに付与します。

    kubectl create rolebinding serviceaccounts-view \
      --clusterrole=view \
      --group=system:serviceaccounts:my-namespace \
      --namespace=my-namespace
    
  4. クラスター全体のすべてのサービスアカウントに制限されたRoleを付与します(お勧めしません)

    Namespaceごとのアクセス許可を管理したくない場合は、すべてのサービスアカウントにクラスター全体の役割を付与できます。

    たとえば、クラスター内のすべてのサービスアカウントに、すべてのNamespaceで読み取り専用のアクセス許可を付与します。

    kubectl create clusterrolebinding serviceaccounts-view \
      --clusterrole=view \
     --group=system:serviceaccounts
    
  5. クラスター全体のすべてのサービスアカウントへのスーパーユーザーアクセスを許可します。(強くお勧めしません)

    権限の分割をまったく考慮しない場合は、すべてのサービスアカウントにスーパーユーザーアクセスを許可できます。

    kubectl create clusterrolebinding serviceaccounts-cluster-admin \
      --clusterrole=cluster-admin \
      --group=system:serviceaccounts
    

ABACからアップグレードする

以前は古いバージョンのKubernetesを実行していたクラスターは、すべてのサービスアカウントに完全なAPIアクセスを許可するなど、permissiveなABACポリシーを使用することがよくありました。

デフォルトのRBACポリシーは、コントロールプレーンコンポーネント、ノード、 およびコントローラーをスコープとして権限を付与しますが、 Namespacekube-system外のサービスアカウントにはno permissionsで付与します (すべての認証されたユーザーに与えられたディスカバリー権限に関わらず)。

これははるかに安全ですが、API権限を自動的に受け取ることを期待している既存のワークロードを混乱させる可能性があります。 この移行を管理するための2つのアプローチは次のとおりです。

並行認可

RBACとABACの両方のauthorizerを実行し、legacy ABAC policyを含むポリシーファイルを指定します。

--authorization-mode=...,RBAC,ABAC --authorization-policy-file=mypolicy.json

最初のコマンドラインオプションを詳細に説明すると、Nodeなどの以前のauthorizerが 要求を拒否すると、RBAC authorizerはAPI要求を認可しようとします。 RBACの場合 また、そのAPI要求を拒否すると、ABAC authorizerが実行されます。これにより、すべてのリクエストが RBACまたはABACポリシーのいずれかで許可されます。

RBACコンポーネントのログレベルが5以上でkube-apiserverを実行した場合(--vmodule = rbac * = 5または --v = 5)、APIサーバーログでRBACの拒否を確認できます(プレフィックスは「RBAC」)。 その情報を使用して、どのRoleをどのユーザー、グループ、またはサービスアカウントに付与する必要があるかを判断できます。

サービスアカウントに付与されたRoleを取得し、 ワークロードがサーバーログにRBACの拒否メッセージがない状態で実行されている場合は、ABAC authorizerを削除できます。

Permissive RBAC権限

RBACRoleBindingを使用して、permissive ABACポリシーを複製できます。

RBACの使用に移行後、クラスターが情報セキュリティのニーズを確実に満たすように、アクセスコントロールを調整する必要があります。

6.6 - ネットワーキングのリファレンス

このセクションでは、Kubernetesネットワーキングの詳細を提供します。

6.6.1 - ポートとプロトコル

パブリッククラウドにおける仮想ネットワークや、物理ネットワークファイアウォールを持つオンプレミスのデータセンターのような、ネットワークの境界が厳格な環境でKubernetesを実行する場合、Kubernetesのコンポーネントが使用するポートやプロトコルを認識しておくと便利です。

コントロールプレーン

プロトコル通信の向きポート範囲目的使用者
TCPInbound6443Kubernetes API server全て
TCPInbound2379-2380etcd server client APIkube-apiserver, etcd
TCPInbound10250Kubelet API自身, コントロールプレーン
TCPInbound10259kube-scheduler自身
TCPInbound10257kube-controller-manager自身

etcdポートはコントロールプレーンノードに含まれていますが、独自のetcdクラスターを外部またはカスタムポートでホストすることもできます。

ワーカーノード

プロトコル通信の向きポート範囲目的使用者
TCPInbound10250Kubelet API自身, コントロールプレーン
TCPInbound30000-32767NodePort Services†全て

NodePort Servicesのデフォルトのポート範囲。

すべてのデフォルトのポート番号が書き換え可能です。 カスタムポートを使用する場合、ここに記載されているデフォルトではなく、それらのポートを開く必要があります。

よくある例としては、API Serverのポートを443に変更することがあります。 または、デフォルトポートをそのままにし、API Serverを443でリッスンしているロードバランサーの後ろに置き、APIサーバのデフォルトポートにリクエストをルーティングする方法もあります。

6.7 - セットアップツールのリファレンス

6.7.1 - Kubeadm

kubeadmは、kubeadm initkubeadm joinなどのコマンドを提供するツールで、Kubernetesクラスターを構築する上でのベストプラクティスを反映した「近道」を提供するものとして開発されました。

kubeadmは実用最小限のクラスターをセットアップするための処理を実行します。設計上、kubeadmはブートストラップのみを行い、マシンのプロビジョニングは行いません。同様に、Kubernetesダッシュボード、モニタリングソリューション、クラウド向けのアドオンなど、あれば便利でもなくても支障のない各種アドオンのインストールも範囲外です。

その代わりに、高度な特定用途向けのツールはkubeadmをベースに構築されることが期待されています。理想的には、すべてのデプロイのベースとしてkubeadmを使用することで、適合テストに通るクラスターを簡単に作れるようになります。

インストール方法

kubeadmをインストールするには、インストールガイドを参照してください。

次の項目

  • kubeadm initを使用して、Kubernetesのコントロールプレーンノードをブートストラップする
  • kubeadm joinを使用して、Kubernetesのワーカーノードをブートストラップし、クラスターに参加させる
  • kubeadm upgradeで、Kubernetesクラスターを新しいバージョンにアップグレードする
  • kubeadm configを使用して、kubeadm v1.7.x以前で初期化されたクラスターを、kubeadm upgradeを利用できるように設定する
  • kubeadm tokenで、kubeadm joinのためのトークンを管理する
  • kubeadm resetを使用して、kubeadm initまたはkubeadm joinでホストに行われた変更を元に戻す
  • kubeadm versionで、kubeadmのバージョンを表示する
  • kubeadm alphaで、コミュニティからのフィードバックを集めるために有効にされた各種機能を試用する

6.8 - コマンドラインツール(kubectl)

Kubernetesが提供する、 kubernetes APIを使用してKubernetesクラスターのコントロールプレーンと通信するためのコマンドラインツールです。

このツールの名前は、kubectl です。

kubectlコマンドラインツールを使うと、Kubernetesクラスターを制御できます。環境設定のために、kubectlは、$HOME/.kubeディレクトリにあるconfigという名前のファイルを探します。他のkubeconfigファイルは、KUBECONFIG環境変数を設定するか、--kubeconfigフラグを設定することで指定できます。

この概要では、kubectlの構文を扱い、コマンド操作を説明し、一般的な例を示します。サポートされているすべてのフラグやサブコマンドを含め、各コマンドの詳細については、kubectlリファレンスドキュメントを参照してください。

インストール方法については、kubectlのインストールおよびセットアップをご覧ください。クイックガイドは、チートシートをご覧ください。dockerコマンドラインツールに慣れている方は、kubectl for Docker UsersでKubernetesの同等のコマンドを説明しています。

構文

ターミナルウィンドウからkubectlコマンドを実行するには、以下の構文を使用します。

kubectl [command] [TYPE] [NAME] [flags]

ここで、commandTYPENAMEflagsは、以下を表します。

  • command: 1つ以上のリソースに対して実行したい操作を指定します。例えば、creategetdescribedeleteです。

  • TYPE: リソースタイプを指定します。リソースタイプは大文字と小文字を区別せず、単数形や複数形、省略形を指定できます。例えば、以下のコマンドは同じ出力を生成します。

    kubectl get pod pod1
    kubectl get pods pod1
    kubectl get po pod1
    
  • NAME: リソースの名前を指定します。名前は大文字と小文字を区別します。kubectl get podsのように名前が省略された場合は、すべてのリソースの詳細が表示されます。

    複数のリソースに対して操作を行う場合は、各リソースをタイプと名前で指定するか、1つまたは複数のファイルを指定することができます。

    • リソースをタイプと名前で指定する場合

      • タイプがすべて同じとき、リソースをグループ化するにはTYPE1 name1 name2 name<#>とします。
        例: kubectl get pod example-pod1 example-pod2

      • 複数のリソースタイプを個別に指定するには、TYPE1/name1 TYPE1/name2 TYPE2/name3 TYPE<#>/name<#>とします。
        例: kubectl get pod/example-pod1 replicationcontroller/example-rc1

    • リソースを1つ以上のファイルで指定する場合は、-f file1 -f file2 -f file<#>とします。

  • flags: オプションのフラグを指定します。例えば、-sまたは--serverフラグを使って、Kubernetes APIサーバーのアドレスやポートを指定できます。

ヘルプが必要な場合は、ターミナルウィンドウからkubectl helpを実行してください。

クラスター内認証と名前空間のオーバーライド

デフォルトでは、kubectlは最初にPod内で動作しているか、つまりクラスター内で動作しているかどうかを判断します。まず、KUBERNETES_SERVICE_HOSTKUBERNETES_SERVICE_PORTの環境変数を確認し、サービスアカウントのトークンファイルが/var/run/secrets/kubernetes.io/serviceaccount/tokenに存在するかどうかを確認します。3つともクラスター内で見つかった場合、クラスター内認証とみなされます。

後方互換性を保つため、クラスター内認証時にPOD_NAMESPACE環境変数が設定されている場合には、サービスアカウントトークンのデフォルトの名前空間が上書きされます。名前空間のデフォルトに依存しているすべてのマニフェストやツールは、この影響を受けます。

POD_NAMESPACE環境変数

POD_NAMESPACE環境変数が設定されている場合、名前空間に属するリソースのCLI操作は、デフォルトで環境変数の値になります。例えば、変数にseattleが設定されている場合、kubectl get podsは、seattle名前空間のPodを返します。これは、Podが名前空間に属するリソースであり、コマンドで名前空間が指定されていないためです。kubectl api-resourcesの出力を見て、リソースが名前空間に属するかどうかを判断してください。

明示的に--namespace <value>を使用すると、この動作は上書きされます。

kubectlによるServiceAccountトークンの処理方法

以下の条件がすべて成立した場合、

  • /var/run/secrets/kubernetes.io/serviceaccount/tokenにマウントされたKubernetesサービスアカウントのトークンファイルがある
  • KUBERNETES_SERVICE_HOST環境変数が設定されている
  • KUBERNETES_SERVICE_PORT環境変数が設定されている
  • kubectlコマンドラインで名前空間を明示的に指定しない

kubectlはクラスター内で実行されているとみなして、そのServiceAccountの名前空間(これはPodの名前空間と同じです)を検索し、その名前空間に対して機能します。これは、クラスターの外の動作とは異なります。kubectlがクラスターの外で実行され、名前空間を指定しない場合、kubectlコマンドは、クライアント構成の現在のコンテキストに設定されている名前空間に対して動作します。kubectlのデフォルトの名前空間を変更するには、次のコマンドを使用できます。

kubectl config set-context --current --namespace=<namespace-name>

操作

以下の表に、kubectlのすべての操作の簡単な説明と一般的な構文を示します。

操作                構文説明
alphakubectl alpha SUBCOMMAND [flags]アルファ機能に該当する利用可能なコマンドを一覧表示します。これらの機能は、デフォルトではKubernetesクラスターで有効になっていません。
annotatekubectl annotate (-f FILENAME | TYPE NAME | TYPE/NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--overwrite] [--all] [--resource-version=version] [flags]1つ以上のリソースのアノテーションを、追加または更新します。
api-resourceskubectl api-resources [flags]利用可能なAPIリソースを一覧表示します。
api-versionskubectl api-versions [flags]利用可能なAPIバージョンを一覧表示します。
applykubectl apply -f FILENAME [flags]ファイルまたは標準出力から、リソースの設定変更を適用します。
attachkubectl attach POD -c CONTAINER [-i] [-t] [flags]実行中のコンテナにアタッチして、出力ストリームを表示するか、コンテナ(標準入力)と対話します。
authkubectl auth [flags] [options]認可を検査します。
autoscalekubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]ReplicationControllerで管理されているPodのセットを、自動的にスケールします。
certificatekubectl certificate SUBCOMMAND [options]証明書のリソースを変更します。
cluster-infokubectl cluster-info [flags]クラスター内のマスターとサービスに関するエンドポイント情報を表示します。
completionkubectl completion SHELL [options]指定されたシェル(bashまたはzsh)のシェル補完コードを出力します。
configkubectl config SUBCOMMAND [flags]kubeconfigファイルを変更します。詳細は、個々のサブコマンドを参照してください。
convertkubectl convert -f FILENAME [options]異なるAPIバージョン間で設定ファイルを変換します。YAMLとJSONに対応しています。
cordonkubectl cordon NODE [options]Nodeをスケジュール不可に設定します。
cpkubectl cp <file-spec-src> <file-spec-dest> [options]コンテナとの間でファイルやディレクトリをコピーします。
createkubectl create -f FILENAME [flags]ファイルまたは標準出力から、1つ以上のリソースを作成します。
deletekubectl delete (-f FILENAME | TYPE [NAME | /NAME | -l label | --all]) [flags]ファイル、標準出力、またはラベルセレクター、リソースセレクター、リソースを指定して、リソースを削除します。
describekubectl describe (-f FILENAME | TYPE [NAME_PREFIX | /NAME | -l label]) [flags]1つ以上のリソースの詳細な状態を表示します。
diffkubectl diff -f FILENAME [flags]ファイルまたは標準出力と、現在の設定との差分を表示します。
drainkubectl drain NODE [options]メンテナンスの準備のためにNodeをdrainします。
editkubectl edit (-f FILENAME | TYPE NAME | TYPE/NAME) [flags]デファルトのエディタを使い、サーバー上の1つ以上のリソースリソースの定義を編集し、更新します。
eventskubectl eventsイベントを一覧表示します。
execkubectl exec POD [-c CONTAINER] [-i] [-t] [flags] [-- COMMAND [args...]]Pod内のコンテナに対して、コマンドを実行します。
explainkubectl explain [--recursive=false] [flags]様々なリソースのドキュメントを取得します。例えば、Pod、Node、Serviceなどです。
exposekubectl expose (-f FILENAME | TYPE NAME | TYPE/NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type] [flags]ReplicationController、Service、Podを、新しいKubernetesサービスとして公開します。
getkubectl get (-f FILENAME | TYPE [NAME | /NAME | -l label]) [--watch] [--sort-by=FIELD] [[-o | --output]=OUTPUT_FORMAT] [flags]1つ以上のリソースを表示します。
kustomizekubectl kustomize <dir> [flags] [options]kustomization.yamlファイル内の指示から生成されたAPIリソースのセットを一覧表示します。引数はファイルを含むディレクトリのPath,またはリポジトリルートに対して同じ場所を示すパスサフィックス付きのgitリポジトリのURLを指定しなければなりません。
labelkubectl label (-f FILENAME | TYPE NAME | TYPE/NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--overwrite] [--all] [--resource-version=version] [flags]1つ以上のリソースのラベルを、追加または更新します。
logskubectl logs POD [-c CONTAINER] [--follow] [flags]Pod内のコンテナのログを表示します。
optionskubectl optionsすべてのコマンドに適用されるグローバルコマンドラインオプションを一覧表示します。
patchkubectl patch (-f FILENAME | TYPE NAME | TYPE/NAME) --patch PATCH [flags]Strategic Merge Patchの処理を使用して、リソースの1つ以上のフィールドを更新します。
pluginkubectl plugin [flags] [options]プラグインと対話するためのユーティリティを提供します。
port-forwardkubectl port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N] [flags]1つ以上のローカルポートを、Podに転送します。
proxykubectl proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix] [flags]Kubernetes APIサーバーへのプロキシを実行します。
replacekubectl replace -f FILENAMEファイルや標準出力から、リソースを置き換えます。
rolloutkubectl rollout SUBCOMMAND [options]リソースのロールアウトを管理します。有効なリソースには、Deployment、DaemonSetとStatefulSetが含まれます。
runkubectl run NAME --image=image [--env="key=value"] [--port=port] [--dry-run=server|client|none] [--overrides=inline-json] [flags]指定したイメージを、クラスター上で実行します。
scalekubectl scale (-f FILENAME | TYPE NAME | TYPE/NAME) --replicas=COUNT [--resource-version=version] [--current-replicas=count] [flags]指定したReplicationControllerのサイズを更新します。
setkubectl set SUBCOMMAND [options]アプリケーションリソースを設定します。
taintkubectl taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N [options]1つ以上のNodeのtaintを更新します。
topkubectl top [flags] [options]リソース(CPU/メモリー/ストレージ)の使用量を表示します。
uncordonkubectl uncordon NODE [options]Nodeをスケジュール可に設定します。
versionkubectl version [--client] [flags]クライアントとサーバーで実行中のKubernetesのバージョンを表示します。
waitkubectl wait ([-f FILENAME] | resource.group/resource.name | resource.group [(-l label | --all)]) [--for=delete|--for condition=available] [options]実験中の機能: 1つ以上のリソースが特定の状態になるまで待ちます。

コマンド操作について詳しく知りたい場合は、kubectlリファレンスドキュメントを参照してください。

リソースタイプ

以下の表に、サポートされているすべてのリソースと、省略されたエイリアスの一覧を示します。

(この出力はkubectl api-resourcesから取得でき、Kubernetes 1.25.0時点で正確でした。)

リソース名短縮名APIバージョン名前空間に属するかリソースの種類
bindingsv1trueBinding
componentstatusescsv1falseComponentStatus
configmapscmv1trueConfigMap
endpointsepv1trueEndpoints
eventsevv1trueEvent
limitrangeslimitsv1trueLimitRange
namespacesnsv1falseNamespace
nodesnov1falseNode
persistentvolumeclaimspvcv1truePersistentVolumeClaim
persistentvolumespvv1falsePersistentVolume
podspov1truePod
podtemplatesv1truePodTemplate
replicationcontrollersrcv1trueReplicationController
resourcequotasquotav1trueResourceQuota
secretsv1trueSecret
serviceaccountssav1trueServiceAccount
servicessvcv1trueService
mutatingwebhookconfigurationsadmissionregistration.k8s.io/v1falseMutatingWebhookConfiguration
validatingwebhookconfigurationsadmissionregistration.k8s.io/v1falseValidatingWebhookConfiguration
customresourcedefinitionscrd,crdsapiextensions.k8s.io/v1falseCustomResourceDefinition
apiservicesapiregistration.k8s.io/v1falseAPIService
controllerrevisionsapps/v1trueControllerRevision
daemonsetsdsapps/v1trueDaemonSet
deploymentsdeployapps/v1trueDeployment
replicasetsrsapps/v1trueReplicaSet
statefulsetsstsapps/v1trueStatefulSet
tokenreviewsauthentication.k8s.io/v1falseTokenReview
localsubjectaccessreviewsauthorization.k8s.io/v1trueLocalSubjectAccessReview
selfsubjectaccessreviewsauthorization.k8s.io/v1falseSelfSubjectAccessReview
selfsubjectrulesreviewsauthorization.k8s.io/v1falseSelfSubjectRulesReview
subjectaccessreviewsauthorization.k8s.io/v1falseSubjectAccessReview
horizontalpodautoscalershpaautoscaling/v2trueHorizontalPodAutoscaler
cronjobscjbatch/v1trueCronJob
jobsbatch/v1trueJob
certificatesigningrequestscsrcertificates.k8s.io/v1falseCertificateSigningRequest
leasescoordination.k8s.io/v1trueLease
endpointslicesdiscovery.k8s.io/v1trueEndpointSlice
eventsevevents.k8s.io/v1trueEvent
flowschemasflowcontrol.apiserver.k8s.io/v1beta2falseFlowSchema
prioritylevelconfigurationsflowcontrol.apiserver.k8s.io/v1beta2falsePriorityLevelConfiguration
ingressclassesnetworking.k8s.io/v1falseIngressClass
ingressesingnetworking.k8s.io/v1trueIngress
networkpoliciesnetpolnetworking.k8s.io/v1trueNetworkPolicy
runtimeclassesnode.k8s.io/v1falseRuntimeClass
poddisruptionbudgetspdbpolicy/v1truePodDisruptionBudget
podsecuritypoliciespsppolicy/v1beta1falsePodSecurityPolicy
clusterrolebindingsrbac.authorization.k8s.io/v1falseClusterRoleBinding
clusterrolesrbac.authorization.k8s.io/v1falseClusterRole
rolebindingsrbac.authorization.k8s.io/v1trueRoleBinding
rolesrbac.authorization.k8s.io/v1trueRole
priorityclassespcscheduling.k8s.io/v1falsePriorityClass
csidriversstorage.k8s.io/v1falseCSIDriver
csinodesstorage.k8s.io/v1falseCSINode
csistoragecapacitiesstorage.k8s.io/v1trueCSIStorageCapacity
storageclassesscstorage.k8s.io/v1falseStorageClass
volumeattachmentsstorage.k8s.io/v1falseVolumeAttachment

出力オプション

ある特定のコマンドの出力に対してフォーマットやソートを行う方法については、以下の節を参照してください。どのコマンドが様々な出力オプションをサポートしているかについては、kubectlリファレンスドキュメントをご覧ください。

出力のフォーマット

すべてのkubectlコマンドのデフォルトの出力フォーマットは、人間が読みやすいプレーンテキスト形式です。特定のフォーマットで、詳細をターミナルウィンドウに出力するには、サポートされているkubectlコマンドに-oまたは--outputフラグのいずれかを追加します。

構文

kubectl [command] [TYPE] [NAME] -o <output_format>

kubectlの操作に応じて、以下の出力フォーマットがサポートされています。

出力フォーマット説明
-o custom-columns=<spec>カスタムカラムのコンマ区切りのリストを使用して、テーブルを表示します。
-o custom-columns-file=<filename><filename>ファイル内のカスタムカラムのテンプレートを使用して、テーブルを表示します。
-o jsonJSON形式のAPIオブジェクトを出力します。
-o jsonpath=<template>jsonpath式で定義されたフィールドを表示します。
-o jsonpath-file=<filename><filename>ファイル内のjsonpath式で定義されたフィールドを表示します。
-o nameリソース名のみを表示します。
-o wide追加情報を含めて、プレーンテキスト形式で出力します。Podの場合は、Node名が含まれます。
-o yamlYAML形式のAPIオブジェクトを出力します。

この例において、以下のコマンドは1つのPodの詳細を、YAML形式のオブジェクトとして出力します。

kubectl get pod web-pod-13je7 -o yaml

各コマンドでサポートされている出力フォーマットの詳細については、kubectlリファレンスドキュメントを参照してください。

カスタムカラム

カスタムカラムを定義して、必要な詳細のみをテーブルに出力するには、custom-columnsオプションを使います。カスタムカラムをインラインで定義するか、-o custom-columns=<spec>または-o custom-columns-file=<filename>のようにテンプレートファイルを使用するかを選択できます。

インラインで定義する例は、以下の通りです。

kubectl get pods <pod-name> -o custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion

テンプレートファイルを使用して定義する例は、以下の通りです。

kubectl get pods <pod-name> -o custom-columns-file=template.txt

ここで、template.txtには以下の内容が含まれます。

NAME          RSRC
metadata.name metadata.resourceVersion

どちらのコマンドを実行した場合でも、以下の結果を得ます。

NAME           RSRC
submit-queue   610995

サーバーサイドカラム

kubectlは、サーバーからオブジェクトに関する特定のカラム情報を受け取ることをサポートしています。 つまり、与えられた任意のリソースについて、サーバーはそのリソースに関連する列や行を返し、クライアントが表示できるようにします。 これにより、サーバーが表示の詳細をカプセル化することで、同一クラスターに対して使用されているクライアント間で、一貫した人間が読みやすい出力が可能です。

この機能は、デフォルトで有効になっています。無効にするには、kubectl getコマンドに--server-print=falseフラグを追加します。

Podの状態に関する情報を表示するには、以下のようなコマンドを使用します。

kubectl get pods <pod-name> --server-print=false

以下のように出力されます。

NAME       AGE
pod-name   1m

オブジェクトリストのソート

ターミナルウィンドウで、オブジェクトをソートされたリストに出力するには、サポートされているkubectlコマンドに--sort-byフラグを追加します。--sort-byフラグで任意の数値フィールドや文字列フィールドを指定することで、オブジェクトをソートします。フィールドの指定には、jsonpath式を使用します。

構文

kubectl [command] [TYPE] [NAME] --sort-by=<jsonpath_exp>

名前でソートしたPodのリストを表示するには、以下のように実行します。

kubectl get pods --sort-by=.metadata.name

例: 一般的な操作

よく使われるkubectlの操作に慣れるために、以下の例を使用してください。

kubectl apply - ファイルや標準出力から、リソースの適用や更新を行います。

# example-service.yaml内の定義を使用して、Serviceを作成します。
kubectl apply -f example-service.yaml

# example-controller.yaml内の定義を使用して、ReplicationControllerを作成します。
kubectl apply -f example-controller.yaml

# <directory>ディレクトリ内の、任意の.yaml、.yml、.jsonファイルで定義されているオブジェクトを作成します。
kubectl apply -f <directory>

kubectl get - 1つ以上のリソースの一覧を表示します。

# すべてのPodの一覧をプレーンテキスト形式で表示します。
kubectl get pods

# すべてのPodの一覧を、ノード名などの追加情報を含めて、プレーンテキスト形式で表示します。
kubectl get pods -o wide

# 指定した名前のReplicationControllerの一覧をプレーンテキスト形式で表示します。'replicationcontroller'リソースタイプを短縮して、エイリアス'rc'で置き換えることもできます。
kubectl get replicationcontroller <rc-name>

# すべてのReplicationControllerとServiceの一覧をまとめてプレーンテキスト形式で表示します。
kubectl get rc,services

# すべてのDaemonSetの一覧をプレーンテキスト形式で表示します。
kubectl get ds

# server01ノードで実行中のPodの一覧をプレーンテキスト形式で表示します。
kubectl get pods --field-selector=spec.nodeName=server01

kubectl describe - 1つ以上のリソースの詳細な状態を、デフォルトでは初期化されないものも含めて表示します。

# Node <node-name>の詳細を表示します。
kubectl describe nodes <node-name>

# Pod <pod-name>の詳細を表示します。
kubectl describe pods/<pod-name>

# ReplicationController <rc-name>が管理しているすべてのPodの詳細を表示します。
# ReplicationControllerによって作成された任意のPodには、ReplicationControllerの名前がプレフィックスとして付与されます。
kubectl describe pods <rc-name>

# すべてのPodの詳細を表示します。
kubectl describe pods

kubectl delete - ファイル、標準出力、または指定したラベルセレクター、名前、リソースセレクター、リソースを指定して、リソースを削除します。

# pod.yamlファイルで指定されたタイプと名前を用いて、Podを削除します。
kubectl delete -f pod.yaml

# '<label-key>=<label-value>'というラベルを持つPodとServiceをすべて削除します。
kubectl delete pods,services -l <label-key>=<label-value>

# 初期化されていないPodを含む、すべてのPodを削除します。
kubectl delete pods --all

kubectl exec - Pod内のコンテナに対してコマンドを実行します。

# Pod <pod-name>から、'date'を実行している時の出力を取得します。デフォルトでは、最初のコンテナから出力されます。
kubectl exec <pod-name> -- date

# Pod <pod-name>のコンテナ <container-name>から、'date'を実行している時の出力を取得します。
kubectl exec <pod-name> -c <container-name> -- date

# インタラクティブな TTY を取得し、Pod <pod-name>から/bin/bashを実行します。デフォルトでは、最初のコンテナから出力されます。
kubectl exec -ti <pod-name> -- /bin/bash

kubectl logs - Pod内のコンテナのログを表示します。

# Pod <pod-name>のログのスナップショットを返します。
kubectl logs <pod-name>

# Pod <pod-name>から、ログのストリーミングを開始します。Linuxの'tail -f'コマンドと似ています。
kubectl logs -f <pod-name>

kubectl diff - 提案されたクラスターに対する更新の差分を表示します。

# pod.jsonに含まれるリソースの差分を表示します。
kubectl diff -f pod.json

# 標準出力から読み込んだファイルの差分を表示します。
cat service.yaml | kubectl diff -f -

例: プラグインの作成と使用

kubectlプラグインの書き方や使い方に慣れるために、以下の例を使用してください。

# 任意の言語でシンプルなプラグインを作成し、生成される実行可能なファイルに
# プレフィックス"kubectl-"で始まる名前を付けます。
cat ./kubectl-hello
#!/bin/sh

# このプラグインは、"hello world"という単語を表示します。
echo "hello world"

プラグインを書いたら、実行可能にします。

chmod a+x ./kubectl-hello

# さらに、PATH内の場所に移動させます。
sudo mv ./kubectl-hello /usr/local/bin
sudo chown root:root /usr/local/bin

# これでkubectlプラグインを作成し、"インストール"できました。
# 通常のコマンドのようにkubectlから呼び出すことで、プラグインを使用できます。
kubectl hello
hello world
# 配置したPATHのフォルダから削除することで、プラグインを"アンインストール"できます。
sudo rm /usr/local/bin/kubectl-hello

kubectlで利用可能なプラグインをすべて表示するには、kubectl plugin listサブコマンドを使用してください。

kubectl plugin list

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

The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-hello
/usr/local/bin/kubectl-foo
/usr/local/bin/kubectl-bar

kubectl plugin listコマンドは、実行不可能なプラグインや、他のプラグインの影に隠れてしまっているプラグインなどについて、警告することもできます。例えば、以下のようになります。

sudo chmod -x /usr/local/bin/kubectl-foo # 実行権限を削除します。
kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-hello
/usr/local/bin/kubectl-foo
  - warning: /usr/local/bin/kubectl-foo identified as a plugin, but it is not executable
/usr/local/bin/kubectl-bar

error: one plugin warning was found

プラグインは、既存のkubectlコマンドの上に、より複雑な機能を構築するための手段であると考えることができます。

cat ./kubectl-whoami

次の例では、下記の内容を含んだkubectl-whoamiが既に作成済であることを前提としています。

#!/bin/bash

# このプラグインは、`kubectl config`コマンドを使って
# 現在選択されているコンテキストに基づいて、現在のユーザーに関する情報を提供します。
kubectl config view --template='{{ range .contexts }}{{ if eq .name "'$(kubectl config current-context)'" }}Current user: {{ printf "%s\n" .context.user }}{{ end }}{{ end }}'

上記のコマンドを実行すると、KUBECONFIGファイル内のカレントコンテキストのユーザーを含んだ出力を得られます。

# ファイルを実行可能にします。
sudo chmod +x ./kubectl-whoami

# さらに、ファイルをPATHに移動します。
sudo mv ./kubectl-whoami /usr/local/bin

kubectl whoami
Current user: plugins-user

次の項目

6.8.1 - kubectlチートシート

このページには、一般的によく使われるkubectlコマンドとフラグのリストが含まれています。

Kubectlコマンドの補完

BASH

source <(kubectl completion bash) # 現在のbashシェルにコマンド補完を設定するには、最初にbash-completionパッケージをインストールする必要があります。
echo "source <(kubectl completion bash)" >> ~/.bashrc # bashシェルでのコマンド補完を永続化するために.bashrcに追記します。

また、エイリアスを使用している場合にもkubectlコマンドを補完できます。

alias k=kubectl
complete -F __start_kubectl k

ZSH

source <(kubectl completion zsh)  # 現在のzshシェルにコマンド補完を設定します
echo "[[ $commands[kubectl] ]] && source <(kubectl completion zsh)" >> ~/.zshrc # zshシェルでのコマンド補完を永続化するために.zshrcに追記します。

Kubectlコンテキストの設定

kubectlがどのKubernetesクラスターと通信するかを設定します。 設定ファイル詳細についてはkubeconfigを使用した複数クラスターとの認証をご覧ください。

kubectl config view # マージされたkubeconfigの設定を表示します。

# 複数のkubeconfigファイルを同時に読み込む場合はこのように記述します。
KUBECONFIG=~/.kube/config:~/.kube/kubconfig2 

kubectl config view

# e2eユーザのパスワードを取得します。
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'

kubectl config view -o jsonpath='{.users[].name}'    # 最初のユーザー名を表示します
kubectl config view -o jsonpath='{.users[*].name}'   # ユーザー名のリストを表示します
kubectl config get-contexts                          # コンテキストのリストを表示します
kubectl config current-context                       # 現在のコンテキストを表示します
kubectl config use-context my-cluster-name           # デフォルトのコンテキストをmy-cluster-nameに設定します

# basic認証をサポートする新たなユーザーをkubeconfigに追加します
kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword

# 現在のコンテキストでkubectlのサブコマンドの名前空間を永続的に変更します
kubectl config set-context --current --namespace=ggckad-s2

# 特定のユーザー名と名前空間を使用してコンテキストを設定します
kubectl config set-context gce --user=cluster-admin --namespace=foo \
  && kubectl config use-context gce
 
kubectl config unset users.foo    # ユーザーfooを削除します

Kubectl Apply

applyはKubernetesリソースを定義するファイルを通じてアプリケーションを管理します。kubectl applyを実行して、クラスター内のリソースを作成および更新します。これは、本番環境でKubernetesアプリケーションを管理する推奨方法です。 詳しくはKubectl Bookをご覧ください。

Objectの作成

Kubernetesのマニフェストファイルは、JSONまたはYAMLで定義できます。ファイル拡張子として、.yaml.yml.jsonが使えます。

kubectl apply -f ./my-manifest.yaml            # リソースを作成します
kubectl apply -f ./my1.yaml -f ./my2.yaml      # 複数のファイルからリソースを作成します
kubectl apply -f ./dir                         # dirディレクトリ内のすべてのマニフェストファイルからリソースを作成します
kubectl apply -f https://git.io/vPieo          # urlで公開されているファイルからリソースを作成します
kubectl create deployment nginx --image=nginx  # 単一のnginx Deploymentを作成します
kubectl explain pods                           # Podマニフェストのドキュメントを取得します

# 標準入力から複数のYAMLオブジェクトを作成します

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep-less
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000"
EOF

# いくつかの鍵を含むSecretを作成します

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: $(echo -n "s33msi4" | base64 -w0)
  username: $(echo -n "jane" | base64 -w0)
EOF

リソースの検索と閲覧

# Getコマンドで基本的な情報を確認します
kubectl get services                          # 現在の名前空間上にあるすべてのサービスのリストを表示します
kubectl get pods --all-namespaces             # すべての名前空間上にあるすべてのPodのリストを表示します
kubectl get pods -o wide                      # 現在の名前空間上にあるすべてのPodについてより詳細なリストを表示します
kubectl get deployment my-dep                 # 特定のDeploymentを表示します
kubectl get pods                              # 現在の名前空間上にあるすべてのPodのリストを表示します
kubectl get pod my-pod -o yaml                # PodのYAMLを表示します

# Describeコマンドで詳細な情報を確認します
kubectl describe nodes my-node
kubectl describe pods my-pod

# 名前順にソートしたServiceのリストを表示します
kubectl get services --sort-by=.metadata.name

# Restartカウント順にPodのリストを表示します
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# capacity順にソートしたPersistentVolumeのリストを表示します
kubectl get pv --sort-by=.spec.capacity.storage

# app=cassandraラベルのついたすべてのPodのversionラベルを表示します
kubectl get pods --selector=app=cassandra -o \
  jsonpath='{.items[*].metadata.labels.version}'

# 'ca.crt'のようなピリオドが含まれるキーの値を取得します
kubectl get configmap myconfig \
  -o jsonpath='{.data.ca\.crt}'

# すべてのワーカーノードを取得します(セレクターを使用して、
# 「node-role.kubernetes.io/master」という名前のラベルを持つ結果を除外します)
kubectl get node --selector='!node-role.kubernetes.io/master'

# 現在の名前空間でrunning状態のPodのリストを表示します
kubectl get pods --field-selector=status.phase=Running

# すべてのノードのExternal IPのリストを表示します
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 特定のRCに属するPodの名前のリストを表示します
# `jq`コマンドは複雑なjsonpathを変換する場合に便利であり、https://stedolan.github.io/jq/で見つけることが可能です
sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?}
echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# すべてのPod(またはラベル付けをサポートする他のKubernetesオブジェクト)のラベルのリストを表示します
kubectl get pods --show-labels

# どのノードがready状態か確認します
JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \
 && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"

# Podで現在使用中のSecretをすべて表示します
kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

# すべてのPodのInitContainerのコンテナIDのリストを表示します
# initContainerの削除を回避しながら、停止したコンテナを削除するときに役立つでしょう
kubectl get pods --all-namespaces -o jsonpath='{range .items[*].status.initContainerStatuses[*]}{.containerID}{"\n"}{end}' | cut -d/ -f3

# タイムスタンプでソートされたEventのリストを表示します
kubectl get events --sort-by=.metadata.creationTimestamp

# クラスターの現在の状態を、マニフェストが適用された場合のクラスターの状態と比較します。
kubectl diff -f ./my-manifest.yaml

# Nodeから返されるすべてのキーをピリオド区切りの階層表記で生成します。
# 複雑にネストされたJSON構造をもつキーを指定したい時に便利です
kubectl get nodes -o json | jq -c 'paths|join(".")'

# Pod等から返されるすべてのキーをピリオド区切り階層表記で生成します。
kubectl get pods -o json | jq -c 'paths|join(".")'

リソースのアップデート

kubectl set image deployment/frontend www=image:v2               # frontend Deploymentのwwwコンテナイメージをv2にローリングアップデートします
kubectl rollout history deployment/frontend                      # frontend Deploymentの改訂履歴を確認します
kubectl rollout undo deployment/frontend                         # 1つ前のDeploymentにロールバックします
kubectl rollout undo deployment/frontend --to-revision=2         # 特定のバージョンにロールバックします
kubectl rollout status -w deployment/frontend                    # frontend Deploymentのローリングアップデートを状態をwatchします
kubectl rollout restart deployment/frontend                      # frontend Deployment を再起動します


cat pod.json | kubectl replace -f -                              # 標準入力から渡されたJSONに基づいてPodを置き換えます

# リソースを強制的に削除してから再生成し、置き換えます。サービスの停止が発生します
kubectl replace --force -f ./pod.json

# ReplicaSetリソースで作られたnginxについてServiceを作成します。これは、ポート80で提供され、コンテナへはポート8000で接続します
kubectl expose rc nginx --port=80 --target-port=8000

# 単一コンテナのPodイメージのバージョン(タグ)をv4に更新します
kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

kubectl label pods my-pod new-label=awesome                      # ラベルを追加します
kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # アノテーションを追加します
kubectl autoscale deployment foo --min=2 --max=10                # "foo" Deploymentのオートスケーリングを行います

リソースへのパッチ適用

# ノードを部分的に更新します
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'

# コンテナのイメージを更新します。spec.containers[*].nameはマージキーであるため必須です
kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

# ポテンシャル配列を含むJSONパッチを使用して、コンテナのイメージを更新します
kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'

# ポテンシャル配列のJSONパッチを使用してDeploymentのlivenessProbeを無効にします
kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'

# ポテンシャル配列に新たな要素を追加します
kubectl patch sa default --type='json' -p='[{"op": "add", "path": "/secrets/1", "value": {"name": "whatever" } }]'

リソースの編集

任意のエディターでAPIリソースを編集します。

kubectl edit svc/docker-registry                      # docker-registryという名前のサービスを編集します
KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # エディターを指定します

リソースのスケーリング

kubectl scale --replicas=3 rs/foo                                 # 「foo」という名前のレプリカセットを3にスケーリングします
kubectl scale --replicas=3 -f foo.yaml                            # 「foo.yaml」で指定されたリソースを3にスケーリングします
kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # mysqlと名付けられたdeploymentの現在のサイズが2であれば、mysqlを3にスケーリングします
kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # 複数のReplication controllerをスケーリングします

リソースの削除

kubectl delete -f ./pod.json                                              # pod.jsonで指定されたタイプと名前を使用してPodを削除します
kubectl delete pod,service baz foo                                        # 「baz」と「foo」の名前を持つPodとServiceを削除します
kubectl delete pods,services -l name=myLabel                              # name=myLabelラベルを持つのPodとServiceを削除します
kubectl -n my-ns delete pod,svc --all                                     # 名前空間my-ns内のすべてのPodとServiceを削除します
# awkコマンドのpattern1またはpattern2に一致するすべてのPodを削除します。
kubectl get pods  -n mynamespace --no-headers=true | awk '/pattern1|pattern2/{print $1}' | xargs  kubectl delete -n mynamespace pod

実行中のポッドとの対話処理

kubectl logs my-pod                                 # Podのログをダンプします(標準出力)
kubectl logs -l name=myLabel                        # name=myLabelラベルの持つPodのログをダンプします(標準出力)
kubectl logs my-pod --previous                      # 以前に存在したコンテナのPodログをダンプします(標準出力)
kubectl logs my-pod -c my-container                 # 複数コンテナがあるPodで、特定のコンテナのログをダンプします(標準出力)
kubectl logs -l name=myLabel -c my-container        # name=mylabelラベルを持つPodのログをダンプします(標準出力) 
kubectl logs my-pod -c my-container --previous      # 複数コンテナがあるPodで、以前に作成した特定のコンテナのログをダンプします(標準出力)
kubectl logs -f my-pod                              # Podのログをストリームで確認します(標準出力)
kubectl logs -f my-pod -c my-container              # 複数のコンテナがあるPodで、特定のコンテナのログをストリームで確認します(標準出力)
kubectl logs -f -l name=myLabel --all-containers    # name-myLabelラベルを持つすべてのコンテナのログをストリームで確認します(標準出力)
kubectl run -i --tty busybox --image=busybox -- sh  # Podをインタラクティブシェルとして実行します
kubectl run nginx --image=nginx -n 
mynamespace                                         # 特定の名前空間でnginx Podを実行します
kubectl run nginx --image=nginx                     # nginx Podを実行し、マニフェストファイルをpod.yamlという名前で書き込みます
--dry-run=client -o yaml > pod.yaml
kubectl attach my-pod -i                            # 実行中のコンテナに接続します
kubectl port-forward my-pod 5000:6000               # ローカルマシンのポート5000を、my-podのポート6000に転送します
kubectl exec my-pod -- ls /                         # 既存のPodでコマンドを実行(単一コンテナの場合)
kubectl exec my-pod -c my-container -- ls /         # 既存のPodでコマンドを実行(複数コンテナがある場合)
kubectl top pod POD_NAME --containers               # 特定のPodとそのコンテナのメトリクスを表示します

ノードおよびクラスターとの対話処理

kubectl cordon my-node                                                # my-nodeをスケジューリング不能に設定します
kubectl drain my-node                                                 # メンテナンスの準備としてmy-nodeで動作中のPodを空にします
kubectl uncordon my-node                                              # my-nodeをスケジューリング可能に設定します
kubectl top node my-node                                              # 特定のノードのメトリクスを表示します
kubectl cluster-info                                                  # Kubernetesクラスターのマスターとサービスのアドレスを表示します
kubectl cluster-info dump                                             # 現在のクラスター状態を標準出力にダンプします
kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 現在のクラスター状態を/path/to/cluster-stateにダンプします

# special-userキーとNoScheduleエフェクトを持つTaintがすでに存在する場合、その値は指定されたとおりに置き換えられます
kubectl taint nodes foo dedicated=special-user:NoSchedule

リソースタイプ

サポートされているすべてのリソースタイプを、それらがAPI groupNamespacedKindに関わらずその短縮名をリストします。

kubectl api-resources

APIリソースを探索するためのその他の操作:

kubectl api-resources --namespaced=true      # 名前空間付きのすべてのリソースを表示します
kubectl api-resources --namespaced=false     # 名前空間のないすべてのリソースを表示します
kubectl api-resources -o name                # すべてのリソースを単純な出力(リソース名のみ)で表示します
kubectl api-resources -o wide                # すべてのリソースを拡張された形(別名 "wide")で表示します
kubectl api-resources --verbs=list,get       # "list"および"get"操作をサポートするすべてのリソースを表示します
kubectl api-resources --api-group=extensions # "extensions" APIグループのすべてのリソースを表示します

出力のフォーマット

特定の形式で端末ウィンドウに詳細を出力するには、サポートされているkubectlコマンドに-o(または--output)フラグを追加します。

出力フォーマット説明
-o=custom-columns=<spec>コンマ区切りされたカスタムカラムのリストを指定してテーブルを表示します
-o=custom-columns-file=<filename><filename>ファイル内のカスタムカラムテンプレートを使用してテーブルを表示します
-o=jsonJSON形式のAPIオブジェクトを出力します
-o=jsonpath=<template>jsonpath式で定義されたフィールドを出力します
-o=jsonpath-file=<filename><filename>ファイル内のjsonpath式で定義されたフィールドを出力します
-o=nameリソース名のみを出力し、それ以外は何も出力しません。
-o=wide追加の情報を含むプレーンテキスト形式で出力します。Podの場合、Node名が含まれます。
-o=yamlYAML形式のAPIオブジェクトを出力します

-o=custom-columnsを使用したサンプル:

# クラスター内で実行中のすべてのイメージ名を表示する
kubectl get pods -A -o=custom-columns='DATA:spec.containers[*].image'

# "registry.k8s.io/coredns:1.6.2"を除いたすべてのイメージ名を表示する
kubectl get pods -A -o=custom-columns='DATA:spec.containers[?(@.image!="registry.k8s.io/coredns:1.6.2")].image'

# 名前に関係なくmetadata以下のすべてのフィールドを表示する
kubectl get pods -A -o=custom-columns='DATA:metadata.*'

kubectlに関するより多くのサンプルはカスタムカラムのリファレンスを参照してください。

Kubectlのログレベルとデバッグ

kubectlのログレベルは、レベルを表す整数が後に続く-vまたは--vフラグで制御されます。一般的なKubernetesのログ記録規則と関連するログレベルについて、こちらで説明します。

ログレベル説明
--v=0これは、クラスターオペレーターにログレベルが0であることを"常に"見えるようにするために役立ちます
--v=1ログレベルが必要ない場合に、妥当なデフォルトのログレベルです
--v=2サービスに関する重要な定常状態情報と、システムの重要な変更に関連する可能性がある重要なログメッセージを表示します。 これは、ほとんどのシステムで推奨されるデフォルトのログレベルです。
--v=3変更に関するより詳細なログレベルを表示します
--v=4デバックにむいたログレベルで表示します
--v=6要求されたリソースを表示します
--v=7HTTPリクエストのヘッダを表示します
--v=8HTTPリクエストのコンテンツを表示します
--v=9HTTPリクエストのコンテンツをtruncationなしで表示します

次の項目

6.8.2 - JSONPathのサポート

kubectlはJSONPathのテンプレートをサポートしています。

JSONPathのテンプレートは、波括弧{}によって囲まれたJSONPathの式によって構成されています。 kubectlでは、JSONPathの式を使うことで、JSONオブジェクトの特定のフィールドをフィルターしたり、出力のフォーマットを変更することができます。 本来のJSONPathのテンプレートの構文に加え、以下の機能と構文が使えます:

  1. JSONPathの式の内部でテキストをクォートするために、ダブルクォーテーションを使用します。
  2. リストを反復するために、rangeendオペレーターを使用します。
  3. リストを末尾側から参照するために、負の数のインデックスを使用します。負の数のインデックスはリストを「周回」せず、-index + listLength >= 0が満たされる限りにおいて有効になります。

以下のようなJSONの入力が与えられたとします。

{
  "kind": "List",
  "items":[
    {
      "kind":"None",
      "metadata":{"name":"127.0.0.1"},
      "status":{
        "capacity":{"cpu":"4"},
        "addresses":[{"type": "LegacyHostIP", "address":"127.0.0.1"}]
      }
    },
    {
      "kind":"None",
      "metadata":{"name":"127.0.0.2"},
      "status":{
        "capacity":{"cpu":"8"},
        "addresses":[
          {"type": "LegacyHostIP", "address":"127.0.0.2"},
          {"type": "another", "address":"127.0.0.3"}
        ]
      }
    }
  ],
  "users":[
    {
      "name": "myself",
      "user": {}
    },
    {
      "name": "e2e",
      "user": {"username": "admin", "password": "secret"}
    }
  ]
}
機能説明結果
textプレーンテキストkind is {.kind}kind is List
@現在のオブジェクト{@}入力した値と同じ値
. or []子要素{.kind}, {['kind']} or {['name\.type']}List
..子孫要素を再帰的に探す{..name}127.0.0.1 127.0.0.2 myself e2e
*ワイルドカード。すべてのオブジェクトを取得する{.items[*].metadata.name}[127.0.0.1 127.0.0.2]
[start:end:step]添字{.users[0].name}myself
[,]和集合{.items[*]['metadata.name', 'status.capacity']}127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]
?()フィルター{.users[?(@.name=="e2e")].user.password}secret
range, endリストの反復{range .items[*]}[{.metadata.name}, {.status.capacity}] {end}[127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]]
''解釈済みの文字列をクォートする{range .items[*]}{.metadata.name}{'\t'}{end}127.0.0.1 127.0.0.2

kubectlとJSONPathの式を使った例:

kubectl get pods -o json
kubectl get pods -o=jsonpath='{@}'
kubectl get pods -o=jsonpath='{.items[0]}'
kubectl get pods -o=jsonpath='{.items[0].metadata.name}'
kubectl get pods -o=jsonpath="{.items[*]['metadata.name', 'status.capacity']}"
kubectl get pods -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.startTime}{"\n"}{end}'

6.8.3 - kubectlの使用規則

kubectlの推奨される使用規則です。

再利用可能なスクリプトでのkubectlの使用

スクリプトでの安定した出力のために:

  • -o name, -o json, -o yaml, -o go-template, -o jsonpath などの機械指向の出力形式のいずれかを必要します。
  • バージョンを完全に指定します。例えば、jobs.v1.batch/myjobのようにします。これにより、kubectlが時間とともに変化する可能性のあるデフォルトのバージョンを使用しないようにします。
  • コンテキストや設定、その他の暗黙的な状態に頼ってはいけません。

ベストプラクティス

kubectl run

kubectl runがインフラのコード化を満たすために:

  • イメージにバージョン固有のタグを付けて、そのタグを新しいバージョンに移さない。例えば、:latestではなく、:v1234v1.2.3r03062016-1-4を使用してください(詳細は、Best Practices for Configurationを参照してください)。
  • パラメーターが多用されているイメージをスクリプトでチェックします。
  • kubectl run フラグでは表現できない機能を、ソースコントロールでチェックした設定ファイルに切り替えます。

dry-run=client フラグを使用すると、実際に送信することなく、クラスターに送信されるオブジェクトを確認することができます。

Generators

kubectl create --dry-run=client -o yamlというkubectlコマンドで以下のリソースを生成することができます。

  • clusterrole: ClusterRoleを作成します。
  • clusterrolebinding: 特定のClusterRoleに対するClusterRoleBindingを作成します。
  • configmap: ローカルファイル、ディレクトリ、またはリテラル値からConfigMapを作成します。
  • cronjob: 指定された名前のCronJobを作成します。
  • deployment: 指定された名前でDeploymentを作成します。
  • job: 指定された名前でJobを作成します。
  • namespace: 指定された名前でNamespaceを作成します。
  • poddisruptionbudget: 指定された名前でPodDisruptionBudgetを作成します。
  • priorityclass: 指定された名前でPriorityClassを作成します。
  • quota: 指定された名前でQuotaを作成します。
  • role: 1つのルールでRoleを作成します。
  • rolebinding: 特定のロールやClusterRoleに対するRoleBindingを作成します。
  • secret: 指定されたサブコマンドを使用してSecretを作成します。
  • service: 指定されたサブコマンドを使用してServiceを作成します。
  • ServiceAccount: 指定された名前でServiceAccountを作成します。

kubectl apply

  • リソースの作成や更新には kubectl apply を使用できます。kubectl applyを使ったリソースの更新については、Kubectl Bookを参照してください。

6.9 - コマンドラインツールのリファレンス

6.9.1 - フィーチャーゲート

このページでは管理者がそれぞれのKubernetesコンポーネントで指定できるさまざまなフィーチャーゲートの概要について説明しています。

各機能におけるステージの説明については、機能のステージを参照してください。

概要

フィーチャーゲートはアルファ機能または実験的機能を記述するkey=valueのペアのセットです。管理者は各コンポーネントで--feature-gatesコマンドラインフラグを使用することで機能をオンまたはオフにできます。

各コンポーネントはそれぞれのコンポーネント固有のフィーチャーゲートの設定をサポートします。すべてのコンポーネントのフィーチャーゲートの全リストを表示するには-hフラグを使用します。kubeletなどのコンポーネントにフィーチャーゲートを設定するには以下のようにリストの機能ペアを--feature-gatesフラグを使用して割り当てます。

--feature-gates="...,DynamicKubeletConfig=true"

次の表は各Kubernetesコンポーネントに設定できるフィーチャーゲートの概要です。

  • 「導入開始バージョン」列は機能が導入されたとき、またはリリース段階が変更されたときのKubernetesリリースバージョンとなります。
  • 「最終利用可能バージョン」列は空ではない場合はフィーチャーゲートを使用できる最後のKubernetesリリースバージョンとなります。
  • アルファまたはベータ状態の機能はAlphaまたはBetaのフィーチャーゲートに載っています。
  • 安定している機能は、graduatedまたはdeprecatedのフィーチャーゲートに載っています。
  • graduatedまたはdeprecatedのフィーチャーゲートには、非推奨および廃止された機能もリストされています。

AlphaまたはBetaのフィーチャーゲート

AlphaまたはBetaのフィーチャーゲート
機能名デフォルト値ステージ導入開始バージョン最終利用可能バージョン
APIListChunkingfalseAlpha1.81.8
APIListChunkingtrueBeta1.9
APIPriorityAndFairnessfalseAlpha1.181.19
APIPriorityAndFairnesstrueBeta1.20
APIResponseCompressionfalseAlpha1.71.15
APIResponseCompressiontrueBeta1.16
APISelfSubjectReviewfalseAlpha1.26
APIServerIdentityfalseAlpha1.201.25
APIServerIdentitytrueBeta1.26
APIServerTracingfalseAlpha1.22
AggregatedDiscoveryEndpointfalseAlpha1.26
AnyVolumeDataSourcefalseAlpha1.181.23
AnyVolumeDataSourcetrueBeta1.24
AppArmortrueBeta1.4
CheckpointContainerfalseAlpha1.25
CPUManagerPolicyAlphaOptionsfalseAlpha1.23
CPUManagerPolicyBetaOptionstrueBeta1.23
CPUManagerPolicyOptionsfalseAlpha1.221.22
CPUManagerPolicyOptionstrueBeta1.23
CSIMigrationPortworxfalseAlpha1.231.24
CSIMigrationPortworxfalseBeta1.25
CSIMigrationRBDfalseAlpha1.23
CSINodeExpandSecretfalseAlpha1.25
CSIVolumeHealthfalseAlpha1.21
ComponentSLIsfalseAlpha1.26
ContainerCheckpointfalseAlpha1.25
ContextualLoggingfalseAlpha1.24
CronJobTimeZonefalseAlpha1.241.24
CronJobTimeZonetrueBeta1.25
CrossNamespaceVolumeDataSourcefalseAlpha1.26
CustomCPUCFSQuotaPeriodfalseAlpha1.12
CustomResourceValidationExpressionsfalseAlpha1.231.24
CustomResourceValidationExpressionstrueBeta1.25
DisableCloudProvidersfalseAlpha1.22
DisableKubeletCloudCredentialProvidersfalseAlpha1.23
DownwardAPIHugePagesfalseAlpha1.201.20
DownwardAPIHugePagesfalseBeta1.211.21
DownwardAPIHugePagestrueBeta1.22
DynamicResourceAllocationfalseAlpha1.26
EventedPLEGfalseAlpha1.26-
ExpandedDNSConfigfalseAlpha1.221.25
ExpandedDNSConfigtrueBeta1.26
ExperimentalHostUserNamespaceDefaultingfalseBeta1.5
GRPCContainerProbefalseAlpha1.231.23
GRPCContainerProbetrueBeta1.24
GracefulNodeShutdownfalseAlpha1.201.20
GracefulNodeShutdowntrueBeta1.21
GracefulNodeShutdownBasedOnPodPriorityfalseAlpha1.231.23
GracefulNodeShutdownBasedOnPodPrioritytrueBeta1.24
HPAContainerMetricsfalseAlpha1.20
HPAScaleToZerofalseAlpha1.16
HonorPVReclaimPolicyfalseAlpha1.23
IPTablesOwnershipCleanupfalseAlpha1.25
InTreePluginAWSUnregisterfalseAlpha1.21
InTreePluginAzureDiskUnregisterfalseAlpha1.21
InTreePluginAzureFileUnregisterfalseAlpha1.21
InTreePluginGCEUnregisterfalseAlpha1.21
InTreePluginOpenStackUnregisterfalseAlpha1.21
InTreePluginPortworxUnregisterfalseAlpha1.23
InTreePluginRBDUnregisterfalseAlpha1.23
InTreePluginvSphereUnregisterfalseAlpha1.21
JobMutableNodeSchedulingDirectivestrueBeta1.23
JobPodFailurePolicyfalseAlpha1.251.25
JobPodFailurePolicytrueBeta1.26
JobReadyPodsfalseAlpha1.231.23
JobReadyPodstrueBeta1.24
KMSv2falseAlpha1.25
KubeletInUserNamespacefalseAlpha1.22
KubeletPodResourcesfalseAlpha1.131.14
KubeletPodResourcestrueBeta1.15
KubeletPodResourcesGetAllocatablefalseAlpha1.211.22
KubeletPodResourcesGetAllocatabletrueBeta1.23
KubeletTracingfalseAlpha1.25
LegacyServiceAccountTokenTrackingfalseAlpha1.261.26
LegacyServiceAccountTokenTrackingtrueBeta1.27
LocalStorageCapacityIsolationFSQuotaMonitoringfalseAlpha1.15-
LogarithmicScaleDownfalseAlpha1.211.21
LogarithmicScaleDowntrueBeta1.22
LoggingAlphaOptionsfalseAlpha1.24-
LoggingBetaOptionstrueBeta1.24-
MatchLabelKeysInPodTopologySpreadfalseAlpha1.25
MaxUnavailableStatefulSetfalseAlpha1.24
MemoryManagerfalseAlpha1.211.21
MemoryManagertrueBeta1.22
MemoryQoSfalseAlpha1.22
MinDomainsInPodTopologySpreadfalseAlpha1.241.24
MinDomainsInPodTopologySpreadfalseBeta1.25
MinimizeIPTablesRestorefalseAlpha1.26-
MultiCIDRRangeAllocatorfalseAlpha1.25
NetworkPolicyStatusfalseAlpha1.24
NodeInclusionPolicyInPodTopologySpreadfalseAlpha1.251.25
NodeInclusionPolicyInPodTopologySpreadtrueBeta1.26
NodeOutOfServiceVolumeDetachfalseAlpha1.241.25
NodeOutOfServiceVolumeDetachtrueBeta1.26
NodeSwapfalseAlpha1.22
OpenAPIEnumsfalseAlpha1.231.23
OpenAPIEnumstrueBeta1.24
OpenAPIV3falseAlpha1.231.23
OpenAPIV3trueBeta1.24
PDBUnhealthyPodEvictionPolicyfalseAlpha1.26
PodAndContainerStatsFromCRIfalseAlpha1.23
PodDeletionCostfalseAlpha1.211.21
PodDeletionCosttrueBeta1.22
PodDisruptionConditionsfalseAlpha1.251.25
PodDisruptionConditionstrueBeta1.26
PodHasNetworkConditionfalseAlpha1.25
PodSchedulingReadinessfalseAlpha1.26
ProbeTerminationGracePeriodfalseAlpha1.211.21
ProbeTerminationGracePeriodfalseBeta1.221.24
ProbeTerminationGracePeriodtrueBeta1.25
ProcMountTypefalseAlpha1.12
ProxyTerminatingEndpointsfalseAlpha1.221.25
ProxyTerminatingEndpointstrueBeta1.26
QOSReservedfalseAlpha1.11
ReadWriteOncePodfalseAlpha1.22
RecoverVolumeExpansionFailurefalseAlpha1.23
RemainingItemCountfalseAlpha1.151.15
RemainingItemCounttrueBeta1.16
RetroactiveDefaultStorageClassfalseAlpha1.251.25
RetroactiveDefaultStorageClasstrueBeta1.26
RotateKubeletServerCertificatefalseAlpha1.71.11
RotateKubeletServerCertificatetrueBeta1.12
SELinuxMountReadWriteOncePodfalseAlpha1.25
SeccompDefaultfalseAlpha1.221.24
SeccompDefaulttrueBeta1.25
ServerSideFieldValidationfalseAlpha1.231.24
ServerSideFieldValidationtrueBeta1.25
SizeMemoryBackedVolumesfalseAlpha1.201.21
SizeMemoryBackedVolumestrueBeta1.22
StatefulSetAutoDeletePVCfalseAlpha1.22
StatefulSetStartOrdinalfalseAlpha1.26
StorageVersionAPIfalseAlpha1.20
StorageVersionHashfalseAlpha1.141.14
StorageVersionHashtrueBeta1.15
TopologyAwareHintsfalseAlpha1.211.22
TopologyAwareHintsfalseBeta1.231.23
TopologyAwareHintstrueBeta1.24
TopologyManagerfalseAlpha1.161.17
TopologyManagertrueBeta1.18
TopologyManagerPolicyAlphaOptionsfalseAlpha1.26
TopologyManagerPolicyBetaOptionsfalseBeta1.26
TopologyManagerPolicyOptionsfalseAlpha1.26
UserNamespacesStatelessPodsSupportfalseAlpha1.25
ValidatingAdmissionPolicyfalseAlpha1.26
VolumeCapacityPriorityfalseAlpha1.21-
WinDSRfalseAlpha1.14
WinOverlayfalseAlpha1.141.19
WinOverlaytrueBeta1.20
WindowsHostNetworkfalseAlpha1.26

GraduatedまたはDeprecatedのフィーチャーゲート

GraduatedまたはDeprecatedのフィーチャーゲート
機能名デフォルト値ステージ導入開始バージョン最終利用可能バージョン
AdvancedAuditingfalseAlpha1.71.7
AdvancedAuditingtrueBeta1.81.11
AdvancedAuditingtrueGA1.12-
CPUManagerfalseAlpha1.81.9
CPUManagertrueBeta1.101.25
CPUManagertrueGA1.26-
CSIInlineVolumefalseAlpha1.151.15
CSIInlineVolumetrueBeta1.161.24
CSIInlineVolumetrueGA1.25-
CSIMigrationfalseAlpha1.141.16
CSIMigrationtrueBeta1.171.24
CSIMigrationtrueGA1.25-
CSIMigrationAWSfalseAlpha1.141.16
CSIMigrationAWSfalseBeta1.171.22
CSIMigrationAWStrueBeta1.231.24
CSIMigrationAWStrueGA1.25-
CSIMigrationAzureDiskfalseAlpha1.151.18
CSIMigrationAzureDiskfalseBeta1.191.22
CSIMigrationAzureDisktrueBeta1.231.23
CSIMigrationAzureDisktrueGA1.24
CSIMigrationAzureFilefalseAlpha1.151.20
CSIMigrationAzureFilefalseBeta1.211.23
CSIMigrationAzureFiletrueBeta1.241.25
CSIMigrationAzureFiletrueGA1.26
CSIMigrationGCEfalseAlpha1.141.16
CSIMigrationGCEfalseBeta1.171.22
CSIMigrationGCEtrueBeta1.231.24
CSIMigrationGCEtrueGA1.25-
CSIMigrationvSpherefalseAlpha1.181.18
CSIMigrationvSpherefalseBeta1.191.24
CSIMigrationvSpheretrueBeta1.251.25
CSIMigrationvSpheretrueGA1.26-
CSIStorageCapacityfalseAlpha1.191.20
CSIStorageCapacitytrueBeta1.211.23
CSIStorageCapacitytrueGA1.24-
ConsistentHTTPGetHandlerstrueGA1.25-
ControllerManagerLeaderMigrationfalseAlpha1.211.21
ControllerManagerLeaderMigrationtrueBeta1.221.23
ControllerManagerLeaderMigrationtrueGA1.24-
DaemonSetUpdateSurgefalseAlpha1.211.21
DaemonSetUpdateSurgetrueBeta1.221.24
DaemonSetUpdateSurgetrueGA1.25-
DelegateFSGroupToCSIDriverfalseAlpha1.221.22
DelegateFSGroupToCSIDrivertrueBeta1.231.25
DelegateFSGroupToCSIDrivertrueGA1.26-
DevicePluginsfalseAlpha1.81.9
DevicePluginstrueBeta1.101.25
DevicePluginstrueGA1.26-
DisableAcceleratorUsageMetricsfalseAlpha1.191.19
DisableAcceleratorUsageMetricstrueBeta1.201.24
DisableAcceleratorUsageMetricstrueGA1.25-
DryRunfalseAlpha1.121.12
DryRuntrueBeta1.131.18
DryRuntrueGA1.19-
EfficientWatchResumptionfalseAlpha1.201.20
EfficientWatchResumptiontrueBeta1.211.23
EfficientWatchResumptiontrueGA1.24-
EndpointSliceTerminatingConditionfalseAlpha1.201.21
EndpointSliceTerminatingConditiontrueBeta1.221.25
EndpointSliceTerminatingConditiontrueGA1.26
EphemeralContainersfalseAlpha1.161.22
EphemeralContainerstrueBeta1.231.24
EphemeralContainerstrueGA1.25-
ExecProbeTimeouttrueGA1.20-
ExpandCSIVolumesfalseAlpha1.141.15
ExpandCSIVolumestrueBeta1.161.23
ExpandCSIVolumestrueGA1.24-
ExpandInUsePersistentVolumesfalseAlpha1.111.14
ExpandInUsePersistentVolumestrueBeta1.151.23
ExpandInUsePersistentVolumestrueGA1.24-
ExpandPersistentVolumesfalseAlpha1.81.10
ExpandPersistentVolumestrueBeta1.111.23
ExpandPersistentVolumestrueGA1.24-
JobTrackingWithFinalizersfalseAlpha1.221.22
JobTrackingWithFinalizersfalseBeta1.231.24
JobTrackingWithFinalizerstrueBeta1.251.25
JobTrackingWithFinalizerstrueGA1.26-
KubeletCredentialProvidersfalseAlpha1.201.23
KubeletCredentialProviderstrueBeta1.241.25
KubeletCredentialProviderstrueGA1.26-
LegacyServiceAccountTokenNoAutoGenerationtrueBeta1.241.25
LegacyServiceAccountTokenNoAutoGenerationtrueGA1.26-
LocalStorageCapacityIsolationfalseAlpha1.71.9
LocalStorageCapacityIsolationtrueBeta1.101.24
LocalStorageCapacityIsolationtrueGA1.25-
MixedProtocolLBServicefalseAlpha1.201.23
MixedProtocolLBServicetrueBeta1.241.25
MixedProtocolLBServicetrueGA1.26-
NetworkPolicyEndPortfalseAlpha1.211.21
NetworkPolicyEndPorttrueBeta1.221.24
NetworkPolicyEndPorttrueGA1.25-
PodSecurityfalseAlpha1.221.22
PodSecuritytrueBeta1.231.24
PodSecuritytrueGA1.25
RemoveSelfLinkfalseAlpha1.161.19
RemoveSelfLinktrueBeta1.201.23
RemoveSelfLinktrueGA1.24-
ServerSideApplyfalseAlpha1.141.15
ServerSideApplytrueBeta1.161.21
ServerSideApplytrueGA1.22-
ServiceIPStaticSubrangefalseAlpha1.241.24
ServiceIPStaticSubrangetrueBeta1.251.25
ServiceIPStaticSubrangetrueGA1.26-
ServiceInternalTrafficPolicyfalseAlpha1.211.21
ServiceInternalTrafficPolicytrueBeta1.221.25
ServiceInternalTrafficPolicytrueGA1.26-
StatefulSetMinReadySecondsfalseAlpha1.221.22
StatefulSetMinReadySecondstrueBeta1.231.24
StatefulSetMinReadySecondstrueGA1.25-
WatchBookmarkfalseAlpha1.151.15
WatchBookmarktrueBeta1.161.16
WatchBookmarktrueGA1.17-
WindowsHostProcessContainersfalseAlpha1.221.22
WindowsHostProcessContainerstrueBeta1.231.25
WindowsHostProcessContainerstrueGA1.26-

機能を使用する

機能のステージ

機能にはAlphaBetaGA の段階があります。Alpha 機能とは:

  • デフォルトでは無効になっています。
  • バグがあるかもしれません。機能を有効にするとバグが発生する可能性があります。
  • 機能のサポートは予告無しにいつでも削除される場合があります。
  • APIは今後のソフトウェアリリースで予告なく互換性の無い変更が行われる場合があります。
  • バグが発生するリスクが高く長期的なサポートはないため、短期間のテストクラスターでのみ使用することをお勧めします。

Beta 機能とは:

  • デフォルトで有効になっています。
  • この機能は十分にテストされていて、有効にすることは安全と考えられます。
  • 詳細は変更される可能性がありますが、機能全体のサポートは削除されません。
  • オブジェクトのスキーマやセマンティックは、その後のベータ版または安定版リリースで互換性の無い変更が行われる場合があります。互換性の無い変更が行われた場合には次のバージョンへの移行手順を提供します。これにはAPIオブジェクトの削除、編集、および再作成が必要になる場合があります。バージョンアップにはいくつかの対応が必要な場合があります。これには機能に依存するアプリケーションのダウンタイムが発生する場合があります。
  • 今後のリリースで互換性の無い変更が行われる可能性があるため、ビジネスクリティカルでない使用のみが推奨されます。個別にアップグレードできる複数のクラスターがある場合はこの制限を緩和できる場合があります。

GA 機能とは(GA 機能は安定版 機能とも呼ばれます):

  • 機能は常に有効となり、無効にすることはできません。
  • フィーチャーゲートの設定は不要になります。
  • 機能の安定版は後続バージョンでリリースされたソフトウェアで使用されます。

フィーチャーゲート

各フィーチャーゲートは特定の機能を有効/無効にするように設計されています。

  • Accelerators: DockerでのNvidia GPUのサポートを有効にします。
  • AdvancedAuditing: 高度な監査機能を有効にします。
  • AffinityInAnnotations(非推奨): Podのアフィニティまたはアンチアフィニティを有効にします。
  • AnyVolumeDataSource: PVCDataSourceとしてカスタムリソースの使用を有効にします。
  • AllowExtTrafficLocalEndpoints: サービスが外部へのリクエストをノードのローカルエンドポイントにルーティングできるようにします。
  • APIListChunking: APIクライアントがAPIサーバーからチャンク単位で(LISTGETの)リソースを取得できるようにします。 APIPriorityAndFairness: 各サーバーで優先順位付けと公平性を備えた要求の並行性を管理できるようにします(RequestManagementから名前が変更されました)。
  • APIResponseCompression:LISTGETリクエストのAPIレスポンスを圧縮します。
  • AppArmor: Dockerを使用する場合にLinuxノードでAppArmorによる強制アクセスコントロールを有効にします。詳細はAppArmorチュートリアルで確認できます。
  • ContainerCheckpoint: kubeletチェックポイントAPIを有効にします。詳細はKubeletチェックポイントAPIで確認できます。
  • AttachVolumeLimit: ボリュームプラグインを有効にすることでノードにアタッチできるボリューム数の制限を設定できます。
  • BalanceAttachedNodeVolumes: スケジューリング中にバランスのとれたリソース割り当てを考慮するノードのボリュームカウントを含めます。判断を行う際に、CPU、メモリー使用率、およびボリュームカウントが近いノードがスケジューラーによって優先されます。
  • BlockVolume: PodでRawブロックデバイスの定義と使用を有効にします。詳細はRawブロックボリュームのサポートで確認できます。
  • BoundServiceAccountTokenVolume: ServiceAccountTokenVolumeProjectionによって構成される計画ボリュームを使用するにはServiceAccountボリュームを移行します。詳細はService Account Token Volumesで確認できます。
  • ConfigurableFSGroupPolicy: Podにボリュームをマウントするときに、ユーザーがfsGroupsのボリューム権限変更ポリシーを設定できるようにします。詳細については、Podのボリューム権限と所有権変更ポリシーの設定をご覧ください。
  • CPUManager: コンテナレベルのCPUアフィニティサポートを有効します。CPUマネジメントポリシーを見てください。
  • CRIContainerLogRotation: criコンテナランタイムのコンテナログローテーションを有効にします。
  • CSIBlockVolume: 外部CSIボリュームドライバーを有効にしてブロックストレージをサポートします。詳細はcsiRawブロックボリュームのサポートで確認できます。
  • CSIDriverRegistry: csi.storage.k8s.ioのCSIDriver APIオブジェクトに関連するすべてのロジックを有効にします。
  • CSIInlineVolume: PodのCSIインラインボリュームサポートを有効にします。
  • CSIMigration: シムと変換ロジックを有効にしてボリューム操作をKubernetesリポジトリー内のプラグインから対応した事前インストール済みのCSIプラグインにルーティングします。
  • CSIMigrationAWS: シムと変換ロジックを有効にしてボリューム操作をKubernetesリポジトリー内のAWS-EBSプラグインからEBS CSIプラグインにルーティングします。ノードにEBS CSIプラグインがインストールおよび設定されていない場合、ツリー内のEBSプラグインへのフォールバックをサポートします。CSIMigration機能フラグを有効にする必要があります。
  • CSIMigrationAWSComplete: EBSツリー内プラグインのkubeletおよびボリュームコントローラーへの登録を停止し、シムと変換ロジックを有効にして、AWS-EBSツリー内プラグインからEBS CSIプラグインにボリューム操作をルーティングします。CSIMigrationおよびCSIMigrationAWS機能フラグを有効にし、クラスター内のすべてのノードにEBS CSIプラグインをインストールおよび設定する必要があります。
  • CSIMigrationAzureDisk: シムと変換ロジックを有効にしてボリューム操作をKubernetesリポジトリー内のAzure-DiskプラグインからAzure Disk CSIプラグインにルーティングします。ノードにAzureDisk CSIプラグインがインストールおよび設定されていない場合、ツリー内のAzureDiskプラグインへのフォールバックをサポートします。CSIMigration機能フラグを有効にする必要があります。
  • CSIMigrationAzureDiskComplete: Azure-Diskツリー内プラグインのkubeletおよびボリュームコントローラーへの登録を停止し、シムと変換ロジックを有効にして、Azure-Diskツリー内プラグインからAzureDisk CSIプラグインにボリューム操作をルーティングします。CSIMigrationおよびCSIMigrationAzureDisk機能フラグを有効にし、クラスター内のすべてのノードにAzureDisk CSIプラグインをインストールおよび設定する必要があります。
  • CSIMigrationAzureFile: シムと変換ロジックを有効にしてボリューム操作をKubernetesリポジトリー内のAzure-FileプラグインからAzure File CSIプラグインにルーティングします。ノードにAzureFile CSIプラグインがインストールおよび設定されていない場合、ツリー内のAzureFileプラグインへのフォールバックをサポートします。CSIMigration機能フラグを有効にする必要があります。
  • CSIMigrationAzureFileComplete: Azure-Fileツリー内プラグインのkubeletおよびボリュームコントローラーへの登録を停止し、シムと変換ロジックを有効にして、Azure-Fileツリー内プラグインからAzureFile CSIプラグインにボリューム操作をルーティングします。CSIMigrationおよびCSIMigrationAzureFile機能フラグを有効にし、クラスター内のすべてのノードにAzureFile CSIプラグインをインストールおよび設定する必要があります。
  • CSIMigrationGCE: シムと変換ロジックを有効にしてボリューム操作をKubernetesリポジトリー内のGCE-PDプラグインからPD CSIプラグインにルーティングします。ノードにPD CSIプラグインがインストールおよび設定されていない場合、ツリー内のGCEプラグインへのフォールバックをサポートします。CSIMigration機能フラグを有効にする必要があります。
  • CSIMigrationGCEComplete: GCE-PDのツリー内プラグインのkubeletおよびボリュームコントローラーへの登録を停止し、シムと変換ロジックがGCE-PDのツリー内プラグインからPD CSIプラグインにボリューム操作をルーティングできるようにします。CSIMigrationおよびCSIMigrationGCE機能フラグを有効にし、クラスター内のすべてのノードにPD CSIプラグインをインストールおよび設定する必要があります。
  • CSIMigrationOpenStack: シムと変換ロジックを有効にしてボリューム操作をKubernetesリポジトリー内のCinderプラグインからCinder CSIプラグインにルーティングします。ノードにCinder CSIプラグインがインストールおよび設定されていない場合、ツリー内のCinderプラグインへのフォールバックをサポートします。CSIMigration機能フラグを有効にする必要があります。
  • CSIMigrationOpenStackComplete: Cinderのツリー内プラグインのkubeletおよびボリュームコントローラーへの登録を停止し、シムと変換ロジックがCinderのツリー内プラグインからCinder CSIプラグインにボリューム操作をルーティングできるようにします。CSIMigrationおよびCSIMigrationOpenStack機能フラグを有効にし、クラスター内のすべてのノードにCinder CSIプラグインをインストールおよび設定する必要があります。
  • CSINodeInfo: csi.storage.k8s.ioのCSINodeInfo APIオブジェクトに関連するすべてのロジックを有効にします。
  • CSIPersistentVolume: CSI(Container Storage Interface)互換のボリュームプラグインを通してプロビジョニングされたボリュームの検出とマウントを有効にします。 詳細についてはcsiボリュームタイプドキュメントを確認してください。
  • CustomCPUCFSQuotaPeriod: ノードがCPUCFSQuotaPeriodを変更できるようにします。
  • CustomPodDNS: dnsConfigプロパティを使用したPodのDNS設定のカスタマイズを有効にします。詳細はPodのDNS構成で確認できます。
  • CustomResourceDefaulting: OpenAPI v3バリデーションスキーマにおいて、デフォルト値のCRDサポートを有効にします。
  • CustomResourcePublishOpenAPI: CRDのOpenAPI仕様での公開を有効にします。
  • CustomResourceSubresources: CustomResourceDefinitionから作成されたリソースの/statusおよび/scaleサブリソースを有効にします。
  • CustomResourceValidation: CustomResourceDefinitionから作成されたリソースのスキーマによる検証を有効にします。
  • CustomResourceWebhookConversion: CustomResourceDefinitionから作成されたリソースのWebhookベースの変換を有効にします。
  • DevicePlugins: device-pluginsによるノードでのリソースプロビジョニングを有効にします。
  • DryRun: サーバーサイドでのdry runリクエストを有効にします。
  • DynamicKubeletConfig: kubeletの動的構成を有効にします。kubeletの再設定を参照してください。
  • DynamicProvisioningScheduling: デフォルトのスケジューラーを拡張してボリュームトポロジーを認識しPVプロビジョニングを処理します。この機能は、v1.12のVolumeScheduling機能に完全に置き換えられました。
  • DynamicVolumeProvisioning(非推奨): Podへの永続ボリュームの動的プロビジョニングを有効にします。
  • EnableAggregatedDiscoveryTimeout (非推奨): 集約されたディスカバリーコールで5秒のタイムアウトを有効にします。
  • EnableEquivalenceClassCache: Podをスケジュールするときにスケジューラーがノードの同等をキャッシュできるようにします。
  • EphemeralContainers: 稼働するPodにephemeral containersを追加する機能を有効にします。
  • EvenPodsSpread: Podをトポロジードメイン全体で均等にスケジュールできるようにします。Even Pods Spreadをご覧ください。
  • ExpandInUsePersistentVolumes: 使用中のPVCのボリューム拡張を有効にします。使用中のPersistentVolumeClaimのサイズ変更を参照してください。
  • ExpandPersistentVolumes: 永続ボリュームの拡張を有効にします。永続ボリューム要求の拡張を参照してください。
  • ExperimentalCriticalPodAnnotation: スケジューリングが保証されるように特定のPodへの クリティカル の注釈を加える設定を有効にします。
  • ExperimentalHostUserNamespaceDefaultingGate: ホストするデフォルトのユーザー名前空間を有効にします。これは他のホストの名前空間やホストのマウントを使用しているコンテナ、特権を持つコンテナ、または名前空間のない特定の機能(たとえばMKNODESYS_MODULEなど)を使用しているコンテナ用です。これはDockerデーモンでユーザー名前空間の再マッピングが有効になっている場合にのみ有効にすべきです。
  • EndpointSlice: よりスケーラブルで拡張可能なネットワークエンドポイントのエンドポイントスライスを有効にします。Enabling Endpoint Slicesをご覧ください。
  • EndpointSliceProxying: このフィーチャーゲートを有効にすると、kube-proxyはエンドポイントの代わりにエンドポイントスライスをプライマリデータソースとして使用し、スケーラビリティとパフォーマンスの向上を実現します。Enabling Endpoint Slices.をご覧ください。
  • GCERegionalPersistentDisk: GCEでリージョナルPD機能を有効にします。
  • HugePages: 事前に割り当てられたhuge pagesの割り当てと消費を有効にします。
  • HugePageStorageMediumSize: 事前に割り当てられた複数のサイズのhuge pagesのサポートを有効にします。
  • HyperVContainer: WindowsコンテナのHyper-Vによる分離を有効にします。
  • HPAScaleToZero: カスタムメトリクスまたは外部メトリクスを使用するときに、HorizontalPodAutoscalerリソースのminReplicasを0に設定できるようにします。
  • ImmutableEphemeralVolumes: 安全性とパフォーマンスを向上させるために、個々のSecretとConfigMapが不変となるように指定できるようにします。
  • KubeletConfigFile: 設定ファイルを使用して指定されたファイルからのkubelet設定の読み込みを有効にします。詳細は設定ファイルによるkubeletパラメーターの設定で確認できます。
  • KubeletPluginsWatcher: 調査ベースのプラグイン監視ユーティリティを有効にしてkubeletがCSIボリュームドライバーなどのプラグインを検出できるようにします。
  • KubeletPodResources: kubeletのPodのリソースgrpcエンドポイントを有効にします。詳細はデバイスモニタリングのサポートで確認できます。
  • LegacyNodeRoleBehavior: 無効にすると、サービスロードバランサーの従来の動作とノードの中断により機能固有のラベルが優先され、node-role.kubernetes.io/masterラベルが無視されます。
  • LocalStorageCapacityIsolation: ローカルの一時ストレージの消費を有効にして、emptyDirボリュームsizeLimitプロパティも有効にします。
  • LocalStorageCapacityIsolationFSQuotaMonitoring: LocalStorageCapacityIsolationローカルの一時ストレージで有効になっていて、emptyDirボリュームのbacking filesystemがプロジェクトクォータをサポートし有効になっている場合、プロジェクトクォータを使用して、パフォーマンスと精度を向上させるために、ファイルシステムへのアクセスではなくemptyDirボリュームストレージ消費を監視します。
  • MountContainers: ホスト上のユーティリティコンテナをボリュームマウンターとして使用できるようにします。
  • MountPropagation: あるコンテナによってマウントされたボリュームを他のコンテナまたはPodに共有できるようにします。詳細はマウントの伝播で確認できます。
  • NodeDisruptionExclusion: ノードラベルnode.kubernetes.io/exclude-disruptionの使用を有効にします。これにより、ゾーン障害時にノードが退避するのを防ぎます。
  • NodeLease: 新しいLease APIを有効にしてノードヘルスシグナルとして使用できるノードのハートビートをレポートします。
  • NonPreemptingPriority: PriorityClassとPodのNonPreemptingオプションを有効にします。
  • PersistentLocalVolumes: Podでlocalボリュームタイプの使用を有効にします。localボリュームを要求する場合、Podアフィニティを指定する必要があります。
  • PodOverhead: PodOverhead機能を有効にして、Podのオーバーヘッドを考慮するようにします。
  • PodDisruptionBudget: PodDisruptionBudget機能を有効にします。
  • PodPriority: 優先度に基づいてPodの再スケジューリングとプリエンプションを有効にします。
  • PodReadinessGates: Podのreadinessの評価を拡張するためにPodReadinessGateフィールドの設定を有効にします。詳細はPod readiness gateで確認できます。
  • PodShareProcessNamespace: Podで実行されているコンテナ間で単一のプロセス名前空間を共有するには、PodでshareProcessNamespaceの設定を有効にします。詳細については、Pod内のコンテナ間でプロセス名前空間を共有するをご覧ください。
  • ProcMountType: コンテナのProcMountTypeの制御を有効にします。
  • PVCProtection: 永続ボリューム要求(PVC)がPodでまだ使用されているときに削除されないようにします。詳細はここで確認できます。
  • QOSReserved: QoSレベルでのリソース予約を許可して、低いQoSレベルのポッドが高いQoSレベルで要求されたリソースにバーストするのを防ぎます(現時点ではメモリのみ)。
  • ResourceLimitsPriorityFunction: 入力したPodのCPU制限とメモリ制限の少なくとも1つを満たすノードに対して最低スコアを1に割り当てるスケジューラー優先機能を有効にします。その目的は同じスコアを持つノード間の関係を断つことです。
  • ResourceQuotaScopeSelectors: リソース割当のスコープセレクターを有効にします。
  • RotateKubeletClientCertificate: kubeletでクライアントTLS証明書のローテーションを有効にします。詳細はkubeletの設定で確認できます。
  • RotateKubeletServerCertificate: kubeletでサーバーTLS証明書のローテーションを有効にします。詳細はkubeletの設定で確認できます。
  • RunAsGroup: コンテナの初期化プロセスで設定されたプライマリグループIDの制御を有効にします。
  • RuntimeClass: コンテナのランタイム構成を選択するにはRuntimeClass機能を有効にします。
  • ScheduleDaemonSetPods: DaemonSetのPodをDaemonSetコントローラーではなく、デフォルトのスケジューラーによってスケジュールされるようにします。
  • SCTPSupport: ServiceEndpointsNetworkPolicyPodの定義でprotocolの値としてSCTPを使用できるようにします
  • ServerSideApply: APIサーバーでサーバーサイドApply(SSA)のパスを有効にします。
  • ServiceAccountIssuerDiscovery: APIサーバーにてサービスアカウント発行者のOIDC検出エンドポイント(発行者とJWKS URL)を有効にします。詳細については、Podのサービスアカウント設定をご覧ください。
  • ServiceAppProtocol: サービスとエンドポイントでAppProtocolフィールドを有効にします。
  • ServiceLoadBalancerFinalizer: サービスロードバランサーのファイナライザー保護を有効にします。
  • ServiceNodeExclusion: クラウドプロバイダーによって作成されたロードバランサーからのノードの除外を有効にします。"alpha.service-controller.kubernetes.io/exclude-balancer"キーまたはnode.kubernetes.io/exclude-from-external-load-balancersでラベル付けされている場合ノードは除外の対象となります。
  • ServiceTopology: クラスターのノードトポロジーに基づいてトラフィックをルーティングするサービスを有効にします。詳細については、Serviceトポロジーを参照してください。
  • StartupProbe: kubeletでstartupプローブを有効にします。
  • StorageObjectInUseProtection: PersistentVolumeまたはPersistentVolumeClaimオブジェクトがまだ使用されている場合、それらの削除を延期します。
  • StorageVersionHash: apiserversがディスカバリーでストレージのバージョンハッシュを公開できるようにします。
  • StreamingProxyRedirects: ストリーミングリクエストのバックエンド(kubelet)からのリダイレクトをインターセプト(およびフォロー)するようAPIサーバーに指示します。ストリーミングリクエストの例にはexecattachport-forwardリクエストが含まれます。
  • SupportIPVSProxyMode: IPVSを使用したクラスター内サービスの負荷分散の提供を有効にします。詳細はサービスプロキシで確認できます。
  • SupportPodPidsLimit: PodのPID制限のサポートを有効にします。
  • Sysctls: 各Podに設定できる名前空間付きのカーネルパラメーター(sysctl)のサポートを有効にします。詳細はsysctlsで確認できます。
  • TaintBasedEvictions: ノードのTaintとPodのTolerationに基づいてノードからPodを排除できるようにします。。詳細はTaintとTolerationで確認できます。
  • TaintNodesByCondition: ノードの条件に基づいてノードの自動Taintを有効にします。
  • TokenRequest: サービスアカウントリソースでTokenRequestエンドポイントを有効にします。
  • TokenRequestProjection: Projectedボリュームを使用したPodへのサービスアカウントのトークンの注入を有効にします。
  • TTLAfterFinished: TTLコントローラーが実行終了後にリソースをクリーンアップできるようにします。
  • VolumePVCDataSource: 既存のPVCをデータソースとして指定するサポートを有効にします。
  • VolumeScheduling: ボリュームトポロジー対応のスケジューリングを有効にし、PersistentVolumeClaim(PVC)バインディングにスケジューリングの決定を認識させます。またPersistentLocalVolumesフィーチャーゲートと一緒に使用するとlocalボリュームタイプの使用が可能になります。
  • VolumeSnapshotDataSource: ボリュームスナップショットのデータソースサポートを有効にします。
  • VolumeSubpathEnvExpansion: 環境変数をsubPathに展開するためのsubPathExprフィールドを有効にします。
  • WatchBookmark: ブックマークイベントの監視サポートを有効にします。
  • WindowsGMSA: GMSA資格仕様をPodからコンテナランタイムに渡せるようにします。
  • WindowsRunAsUserName: デフォルト以外のユーザーでWindowsコンテナアプリケーションを実行するためのサポートを有効にします。詳細については、RunAsUserNameの設定を参照してください。
  • WinDSR: kube-proxyがWindows用のDSRロードバランサーを作成できるようにします。
  • WinOverlay: kube-proxyをWindowsのオーバーレイモードで実行できるようにします。

次の項目

  • Kubernetesの非推奨ポリシーでは、機能とコンポーネントを削除するためのプロジェクトのアプローチを説明しています。

6.9.2 - Kubelet 認証/認可

概要

kubeletのHTTPSエンドポイントは、さまざまな感度のデータへのアクセスを提供するAPIを公開し、 ノードとコンテナ内のさまざまなレベルの権限でタスクを実行できるようにします。

このドキュメントでは、kubeletのHTTPSエンドポイントへのアクセスを認証および承認する方法について説明します。

Kubelet 認証

デフォルトでは、他の構成済み認証方法によって拒否されないkubeletのHTTPSエンドポイントへのリクエストは 匿名リクエストとして扱われ、ユーザー名はsystem:anonymous、 グループはsystem:unauthenticatedになります。

匿名アクセスを無効にし、認証されていないリクエストに対して401 Unauthorized応答を送信するには:

  • --anonymous-auth=falseフラグでkubeletを開始します。

kubeletのHTTPSエンドポイントに対するX509クライアント証明書認証を有効にするには:

  • --client-ca-fileフラグでkubeletを起動し、クライアント証明書を確認するためのCAバンドルを提供します。
  • --kubelet-client-certificateおよび--kubelet-client-keyフラグを使用してapiserverを起動します。
  • 詳細については、apiserver認証ドキュメントを参照してください。

APIベアラートークン(サービスアカウントトークンを含む)を使用して、kubeletのHTTPSエンドポイントへの認証を行うには:

  • APIサーバーでauthentication.k8s.io/v1beta1グループが有効になっていることを確認します。
  • --authentication-token-webhookおよび--kubeconfigフラグを使用してkubeletを開始します。
  • kubeletは、構成済みのAPIサーバーで TokenReview APIを呼び出して、ベアラートークンからユーザー情報を判別します。

Kubelet 承認

認証に成功した要求(匿名要求を含む)はすべて許可されます。デフォルトの認可モードは、すべての要求を許可するAlwaysAllowです。

kubelet APIへのアクセスを細分化するのは、次のような多くの理由が考えられます:

  • 匿名認証は有効になっていますが、匿名ユーザーがkubeletのAPIを呼び出す機能は制限する必要があります。
  • ベアラートークン認証は有効になっていますが、kubeletのAPIを呼び出す任意のAPIユーザー(サービスアカウントなど)の機能を制限する必要があります。
  • クライアント証明書の認証は有効になっていますが、構成されたCAによって署名されたクライアント証明書の一部のみがkubeletのAPIの使用を許可されている必要があります。

kubeletのAPIへのアクセスを細分化するには、APIサーバーに承認を委任します:

  • APIサーバーでauthorization.k8s.io/v1beta1 APIグループが有効になっていることを確認します。
  • --authorization-mode=Webhook--kubeconfigフラグでkubeletを開始します。
  • kubeletは、構成されたAPIサーバーでSubjectAccessReview APIを呼び出して、各リクエストが承認されているかどうかを判断します。

kubeletは、apiserverと同じリクエスト属性アプローチを使用してAPIリクエストを承認します。

動詞は、受けとったリクエストのHTTP動詞から決定されます:

HTTP動詞要求 動詞
POSTcreate
GET, HEADget
PUTupdate
PATCHpatch
DELETEdelete

リソースとサブリソースは、受けとったリクエストのパスから決定されます:

Kubelet APIリソースサブリソース
/stats/*nodesstats
/metrics/*nodesmetrics
/logs/*nodeslog
/spec/*nodesspec
all othersnodesproxy

名前空間とAPIグループの属性は常に空の文字列であり、 リソース名は常にkubeletのNode APIオブジェクトの名前です。

このモードで実行する場合は、apiserverに渡される--kubelet-client-certificateフラグと--kubelet-client-key フラグで識別されるユーザーが次の属性に対して許可されていることを確認します:

  • verb=*, resource=nodes, subresource=proxy
  • verb=*, resource=nodes, subresource=stats
  • verb=*, resource=nodes, subresource=log
  • verb=*, resource=nodes, subresource=spec
  • verb=*, resource=nodes, subresource=metrics

6.10 - Scheduling

6.10.1 - スケジューラーの設定

FEATURE STATE: Kubernetes v1.19 [beta]

設定ファイルを作成し、そのパスをコマンドライン引数として渡すことでkube-schedulerの振る舞いをカスタマイズすることができます。

スケジューリングプロファイルは、kube-schedulerでスケジューリングの異なるステージを設定することができます。 各ステージは、拡張点に公開されています。プラグインをそれらの拡張点に1つ以上実装することで、スケジューリングの振る舞いを変更できます。

KubeSchedulerConfiguration(v1beta2v1beta3)構造体を使用して、kube-scheduler --config <filename>を実行することで、スケジューリングプロファイルを指定することができます。

最小限の設定は次の通りです。

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: /etc/srv/kubernetes/kube-scheduler/kubeconfig

プロファイル

スケジューリングプロファイルは、kube-schedulerでスケジューリングの異なるステージを設定することができます。 各ステージは拡張点に公開されています。 プラグインをそれらの拡張点に1つ以上実装することで、スケジューリングの振る舞いを変更できます。

単一のkube-schedulerインスタンスで複数のプロファイルを実行するように設定することも可能です。

拡張点

スケジューリングは一連のステージで行われ、以下の拡張点に公開されています。

  1. queueSort: これらのプラグインは、スケジューリングキューにあるpending状態のPodをソートするための順序付け関数を提供します。同時に有効化できるプラグインは1つだけです。
  2. preFilter: これらのプラグインは、フィルタリングをする前にPodやクラスターの情報のチェックや前処理のために使用されます。これらのプラグインは、設定された順序で呼び出されます。
  3. filter: これらのプラグインは、スケジューリングポリシーにおけるPredicatesに相当するもので、Podの実行不可能なNodeをフィルターするために使用されます。もし全てのNodeがフィルターされてしまった場合、Podはunschedulableとしてマークされます。
  4. postFilter:これらのプラグインは、Podの実行可能なNodeが見つからなかった場合、設定された順序で呼び出されます。もしpostFilterプラグインのいずれかが、Podを スケジュール可能 とマークした場合、残りのpostFilterプラグインは呼び出されません。
  5. preScore: これは、スコアリング前の作業を行う際に使用できる情報提供のための拡張点です。
  6. score: これらのプラグインはフィルタリングフェーズを通過してきたそれぞれのNodeに対してスコア付けを行います。その後スケジューラーは、最も高い重み付きスコアの合計を持つノードを選択します。
  7. reserve: これは、指定されたPodのためにリソースが予約された際に、プラグインに通知する、情報提供のための拡張点です。また、プラグインはReserve中に失敗した際、またはReserveの後に呼び出されるUnreserveも実装しています。
  8. permit: これらのプラグインではPodのバインディングを拒む、または遅延させることができます。
  9. preBind: これらのプラグインは、Podがバインドされる前に必要な処理を実行できます。
  10. bind: これらのプラグインはPodをNodeにバインドします。bindプラグインは順番に呼び出され、1つのプラグインがバインドを完了すると、残りのプラグインはスキップされます。bindプラグインは少なくとも1つは必要です。
  11. postBind: これは、Podがバインドされた後に呼び出される情報提供のための拡張点です。
  12. multiPoint: このフィールドは設定のみ可能で、プラグインが適用されるすべての拡張点に対して同時に有効化または無効化することができます。

次の例のように、それぞれの拡張点に対して、特定のデフォルトプラグインを無効化、または自作のプラグインを有効化することができます。

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
  - plugins:
      score:
        disabled:
        - name: PodTopologySpread
        enabled:
        - name: MyCustomPluginA
          weight: 2
        - name: MyCustomPluginB
          weight: 1

disabled配列のnameフィールドに*を使用することで、その拡張点の全てのデフォルトプラグインを無効化できます。また、必要に応じてプラグインの順序を入れ替える場合にも使用されます。

Scheduling plugins

以下のプラグインはデフォルトで有効化されており、1つ以上の拡張点に実装されています。

  • ImageLocality:Podが実行するコンテナイメージを既に持っているNodeを優先します。 拡張点:score
  • TaintToleration:TaintsとTolerationsを実行します。 実装する拡張点:filterpreScorescore
  • NodeName: PodのSpecのNode名が、現在のNodeと一致するかをチェックします。 拡張点:filter
  • NodePorts:要求されたPodのポートに対して、Nodeが空きポートを持っているかチェックします。 拡張点:preFilterfilter
  • NodeAffinity:nodeselectorsNodeアフィニティを実行します。 拡張点:filterscore
  • PodTopologySpread:Podトポロジーの分散制約を実行します。 拡張点:preFilterfilterpreScorescore
  • NodeUnschedulable:.spec.unschedulableがtrueに設定されているNodeをフィルタリングします。 拡張点:filter.
  • NodeResourcesFit:Podが要求しているすべてのリソースがNodeにあるかをチェックします。スコアは3つのストラテジのうちの1つを使用します:LeastAllocated(デフォルト)、MostAllocated、 とRequestedToCapacityRatio 拡張点:preFilterfilterscore
  • NodeResourcesBalancedAllocation:Podがスケジュールされた場合に、よりバランスの取れたリソース使用量となるNodeを優先します。 拡張点:score
  • VolumeBinding:Nodeが、要求されたボリュームを持っている、もしくはバインドしているかチェックします。 拡張点:preFilterfilterreservepreBindscore
  • VolumeRestrictions:Nodeにマウントされたボリュームが、ボリュームプロバイダ固有の制限を満たしているかを確認します。 拡張点:filter
  • VolumeZone:要求されたボリュームがゾーン要件を満たしているかどうかを確認します。 拡張点:filter
  • NodeVolumeLimits:NodeのCSIボリューム制限を満たすかどうかをチェックします。 拡張点:filter
  • EBSLimits:NodeのAWSのEBSボリューム制限を満たすかどうかをチェックします。 拡張点:filter
  • GCEPDLimits:NodeのGCP-PDボリューム制限を満たすかどうかをチェックします。 拡張点:filter
  • AzureDiskLimits:NodeのAzureディスクボリューム制限を満たすかどうかをチェックします。 拡張点:filter
  • InterPodAffinity:Pod間のaffinityとanti-affinityを実行します。 拡張点:preFilterfilterpreScorescore
  • PrioritySort:デフォルトの優先順位に基づくソートを提供します。 拡張点:queueSort.
  • DefaultBinder:デフォルトのバインディングメカニズムを提供します。 拡張点:bind
  • DefaultPreemption:デフォルトのプリエンプションメカニズムを提供します。 拡張点:postFilter

また、コンポーネント設定のAPIにより、以下のプラグインを有効にすることができます。 デフォルトでは有効になっていません。

複数のプロファイル

kube-schedulerは複数のプロファイルを実行するように設定することができます。 各プロファイルは関連するスケジューラー名を持ち、その拡張点に異なるプラグインを設定することが可能です。

以下のサンプル設定では、スケジューラーは2つのプロファイルで実行されます。1つはデフォルトプラグインで、もう1つはすべてのスコアリングプラグインを無効にしたものです。

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
  - schedulerName: no-scoring-scheduler
    plugins:
      preScore:
        disabled:
        - name: '*'
      score:
        disabled:
        - name: '*'

特定のプロファイルに従ってスケジュールさせたいPodは、その.spec.schedulerNameに、対応するスケジューラー名を含めることができます。

デフォルトでは、スケジューラー名default-schedulerとしてプロファイルが生成されます。 このプロファイルは、上記のデフォルトプラグインを含みます。複数のプロファイルを宣言する場合は、それぞれユニークなスケジューラー名にする必要があります。

もしPodがスケジューラー名を指定しない場合、kube-apiserverはdefault-schedulerを設定します。 従って、これらのPodをスケジュールするために、このスケジューラー名を持つプロファイルが存在する必要があります。

複数の拡張点に適用されるプラグイン

kubescheduler.config.k8s.io/v1beta3からは、プロファイル設定にmultiPointというフィールドが追加され、複数の拡張点でプラグインを簡単に有効・無効化できるようになりました。 multiPoint設定の目的は、カスタムプロファイルを使用する際に、ユーザーや管理者が必要とする設定を簡素化することです。

MyPluginというプラグインがあり、preScorescorepreFilterfilter拡張点を実装しているとします。 すべての利用可能な拡張点でMyPluginを有効化するためには、プロファイル設定は次のようにします。

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:
      multiPoint:
        enabled:
        - name: MyPlugin

これは以下のように、MyPluginを手動ですべての拡張ポイントに対して有効にすることと同じです。

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: non-multipoint-scheduler
    plugins:
      preScore:
        enabled:
        - name: MyPlugin
      score:
        enabled:
        - name: MyPlugin
      preFilter:
        enabled:
        - name: MyPlugin
      filter:
        enabled:
        - name: MyPlugin

multiPointを使用する利点の一つは、将来的にMyPluginが別の拡張点を実装した場合に、multiPoint設定が自動的に新しい拡張点に対しても有効化されることです。

特定の拡張点は、その拡張点のdisabledフィールドを使用して、MultiPointの展開から除外することができます。 これは、デフォルトのプラグインを無効にしたり、デフォルト以外のプラグインを無効にしたり、ワイルドカード('*')を使ってすべてのプラグインを無効にしたりする場合に有効です。 ScorePreScoreを無効にするためには、次の例のようにします。

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: non-multipoint-scheduler
    plugins:
      multiPoint:
        enabled:
        - name: 'MyPlugin'
      preScore:
        disabled:
        - name: '*'
      score:
        disabled:
        - name: '*'

v1beta3では、MultiPointを通じて、内部的に全てのデフォルトプラグインが有効化されています。 しかしながら、デフォルト値(並び順やスコアの重みなど)を柔軟に設定し直せるように、個別の拡張点は用意されています。 例えば、2つのスコアプラグインDefaultScore1DefaultScore2に、重み1が設定されているとします。 その場合、次のように重さを変更し、並べ替えることができます。

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:
      score:
        enabled:
        - name: 'DefaultScore2'
          weight: 5

この例では、MultiPointはデフォルトプラグインであるため、明示的にプラグイン名を指定する必要はありません。 そして、Scoreに指定されているプラグインはDefaultScore2のみです。 これは、特定の拡張点を通じて設定されたプラグインは、常にMultiPointプラグインよりも優先されるためです。つまり、この設定例では、結果的に2つのプラグインを両方指定することなく、並び替えが行えます。

MultiPointプラグインを設定する際の一般的な優先順位は、以下の通りです。

  1. 特定の拡張点が最初に実行され、その設定は他の場所で設定されたものよりも優先される
  2. MultiPointを使用して、手動で設定したプラグインとその設定内容
  3. デフォルトプラグインとそのデフォルト設定

上記の優先順位を示すために、次の例はこれらのプラグインをベースにします。

プラグイン拡張点
DefaultQueueSortQueueSort
CustomQueueSortQueueSort
DefaultPlugin1Score, Filter
DefaultPlugin2Score
CustomPlugin1Score, Filter
CustomPlugin2Score, Filter

これらのプラグインの有効な設定例は次の通りです。

apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:
      multiPoint:
        enabled:
        - name: 'CustomQueueSort'
        - name: 'CustomPlugin1'
          weight: 3
        - name: 'CustomPlugin2'
        disabled:
        - name: 'DefaultQueueSort'
      filter:
        disabled:
        - name: 'DefaultPlugin1'
      score:
        enabled:
        - name: 'DefaultPlugin2'

なお、特定の拡張点にMultiPointプラグインを再宣言しても、エラーにはなりません。 特定の拡張点が優先されるため、再宣言は無視されます(ログは記録されます)。

このサンプルは、ほとんどのコンフィグを一箇所にまとめるだけでなく、いくつかの工夫をしています。

  • カスタムのqueueSortプラグインを有効にし、デフォルトのプラグインを無効にする。
  • CustomPlugin1CustomPlugin2を有効にし、この拡張点のプラグイン内で、最初に実行されるようにする。
  • filter拡張点でのみ、DefaultPlugin1を無効にする。
  • score拡張点でDefaultPlugin2が最初に実行されるように並べ替える(カスタムプラグインより先に)。

v1beta3以前のバージョンで、multiPointがない場合、上記の設定例は、次のものと同等になります。

apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: multipoint-scheduler
    plugins:

      # デフォルトQueueSortプラグインを無効化
      queueSort:
        enabled:
        - name: 'CustomQueueSort'
        disabled:
        - name: 'DefaultQueueSort'

      # カスタムFilterプラグインを有効化
      filter:
        enabled:
        - name: 'CustomPlugin1'
        - name: 'CustomPlugin2'
        - name: 'DefaultPlugin2'
        disabled:
        - name: 'DefaultPlugin1'

      # カスタムScoreプラグインを有効化し、実行順を並べ替える
      score:
        enabled:
        - name: 'DefaultPlugin2'
          weight: 1
        - name: 'DefaultPlugin1'
          weight: 3

これは複雑な例ですが、MultiPoint設定の柔軟性と、拡張点を設定する既存の方法とのシームレスな統合を実証しています。

スケジューラー設定の移行

  • v1beta2のバージョンの設定では、新しいNodeResourcesFitプラグインをスコア拡張点で使用できます。 この新しい拡張機能は、NodeResourcesLeastAllocatedNodeResourcesMostAllocatedRequestedToCapacityRatioプラグインの機能を組み合わせたものです。 例えば、以前はNodeResourcesMostAllocatedプラグインを使っていたなら、代わりにNodeResourcesFitプラグインを使用し(デフォルトで有効)、pluginConfigに次のようなscoreStrategy`を追加することになるでしょう。

    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
    - pluginConfig:
      - args:
          scoringStrategy:
            resources:
            - name: cpu
              weight: 1
            type: MostAllocated
        name: NodeResourcesFit
    
  • スケジューラープラグインのNodeLabelは廃止されました。代わりにNodeAffinityプラグイン(デフォルトで有効)を使用することで同様の振る舞いを実現できます。

  • スケジューラープラグインのServiceAffinityは廃止されました。代わりにInterPodAffinityプラグイン(デフォルトで有効)を使用することで同様の振る舞いを実現できます。

  • スケジューラープラグインのNodePreferAvoidPodsは廃止されました。代わりにNode taintsを使用することで同様の振る舞いを実現できます。

  • v1beta2で有効化されたプラグインは、そのプラグインのデフォルトの設定より優先されます。

  • スケジューラーのヘルスとメトリクスのバインドアドレスに設定されているhostportが無効な場合、バリデーションに失敗します。

  • デフォルトで3つのプラグインの重みが増加しました。
    • InterPodAffinity:1から2
    • NodeAffinity:1から2
    • TaintToleration:1から3

次の項目

6.10.2 - スケジューリングポリシー

バージョンv1.23より前のKubernetesでは、スケジューリングポリシーを使用して、predicatesprioritiesの処理を指定することができました。例えば、kube-scheduler --policy-config-file <filename>またはkube-scheduler --policy-configmap <ConfigMap>を実行すると、スケジューリングポリシーを設定することが可能です。

このスケジューリングポリシーは、バージョンv1.23以降のKubernetesではサポートされていません。関連するフラグである、policy-config-filepolicy-configmappolicy-configmap-namespaceuse-legacy-policy-configも同様にサポートされていません。 代わりに、スケジューラー設定を使用してください。

次の項目

6.11 - ツール

Kubernetesには、Kubernetesシステムの操作に役立ついくつかの組み込みツールが含まれています。

Kubectl

kubectlは、Kubernetesのためのコマンドラインツールです。このコマンドはKubernetes cluster managerを操作します。

Kubeadm

kubeadmは、物理サーバやクラウドサーバ、仮想マシン上にKubernetesクラスターを容易にプロビジョニングするためのコマンドラインツールです(現在はアルファ版です)。

Minikube

minikubeは、開発やテストのためにワークステーション上でシングルノードのKubernetesクラスターをローカルで実行するツールです。

Dashboard

Dashboardは、KubernetesのWebベースのユーザインターフェースで、コンテナ化されたアプリケーションをKubernetesクラスターにデプロイしたり、トラブルシューティングしたり、クラスターとそのリソース自体を管理したりすることが出来ます。

Helm

Kubernetes Helmは、事前に設定されたKubernetesリソースのパッケージ、別名Kubernetes chartsを管理するためのツールです。

Helmを用いて以下のことを行います。

  • Kubernetes chartsとしてパッケージ化された人気のあるソフトウェアの検索と利用

  • Kubernetes chartsとして所有するアプリケーションを共有すること

  • Kubernetesアプリケーションの再現性のあるビルドの作成

  • Kubernetesマニフェストファイルを知的な方法で管理

  • Helmパッケージのリリース管理

Kompose

Komposeは、Docker ComposeユーザがKubernetesに移行する手助けをするツールです。

Komposeを用いて以下のことを行います。

  • Docker ComposeファイルのKubernetesオブジェクトへの変換

  • ローカルのDocker開発からKubernetesを経由したアプリケーション管理への移行

  • v1またはv2のDocker Compose用 yaml ファイルならびに分散されたアプリケーションバンドルの変換

7 - K8sのドキュメントに貢献する

Kubernetesは初心者でも経験者でも、全てのコントリビューターからの改善を歓迎しています!


このウェブサイトはKubernetes SIG Docsが管理しています。

Kubernetesドキュメントコントリビューターは

  • 既存のコンテンツを改善します
  • 新しいコンテンツを作成します
  • ドキュメントを翻訳します
  • Kubernetesリリースサイクルの一部としてドキュメントを管理・公開します

はじめに

どなたでも、問題を説明するissueや、ドキュメントの改善を求めるissueを作成し、kubernetes/website GitHub リポジトリに対するプルリクエスト(PR)を用いて変更に貢献することができます。 Kubernetesコミュニティで効果的に働くためには、gitGitHubを基本的に使いこなせる必要があります。

ドキュメンテーションに関わるには:

  1. CNCFのContributor License Agreementにサインしてください。
  2. ドキュメンテーションのリポジトリーと、ウェブサイトの静的サイトジェネレーターに慣れ親しんでください。
  3. プルリクエストのオープン変更レビューの基本的なプロセスを理解していることを確認してください。

flowchart TB subgraph third[プルリクエストのオープン] direction TB U[ ] -.- Q[コンテンツを改善する] --- N[コンテンツを作成する] N --- O[ドキュメントを翻訳する] O --- P[k8sリリースサイクルの
ドキュメントを管理する] end subgraph second[レビュー] direction TB T[ ] -.- D[kubernetes/website
リポジトリを確認する] --- E[静的サイトジェネレータ
Hugoを確認する] E --- F[基本的なGitHubの
コマンドを理解する] F --- G[オープンした
プルリクエストを確認し
レビュープロセスを見直す] end subgraph first[サインアップ] direction TB S[ ] -.- B[CNCFの
貢献者ライセンス
契約に署名する] --- C[Slackチャンネル
sig-docs に
参加する] C --- V[kubernetes-sig-docsの
メーリングリストに
参加する] V --- M[毎週開催している
sig-docs callsや
slack callsに
参加する] end A([fa:fa-user 新たな
貢献者]) --> first A --> second A --> third A --> H[質問をする!!!] classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,C,D,E,F,G,H,M,Q,N,O,P,V grey class S,T,U spacewhite class first,second,third white
図1. 新たな貢献者のためのスタートガイド。

図1は新たな貢献者のためのロードマップを概説しています。サインアップレビューのステップのいくつか、またはその全てに従えばよいです。これで、プルリクエストのオープンの下にリストされているいくつかの貢献目標を達成するためのプルリクエストを開く準備が整いました。また、質問はいつでも歓迎です!

一部のタスクでは、Kubernetes organizationで、より多くの信頼とアクセス権限が必要です。 役割と権限についての詳細は、SIG Docsへの参加を参照してください。

はじめての貢献

あらかじめいくつかのステップを見直すことで、最初の貢献に備えることができます。図2はそれらのステップの概説で、詳細は次のとおりです。

flowchart LR subgraph second[はじめての貢献] direction TB S[ ] -.- G[K8sメンバーからの
PRレビューを受ける] --> A[最初のPRを作成するための
良いissueを
kubernetes/websiteから探す] --> B[PRをオープンする!!] end subgraph first[推奨される準備] direction TB T[ ] -.- D[貢献者の概要を読む] -->E[K8sのコンテンツと
スタイルガイドを読む] E --> F[Hugoのページコンテンツタイプと
ショートコードについて学ぶ] end first ----> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,D,E,F,G grey class S,T spacewhite class first,second white
図2. はじめての貢献のための準備。

貢献時の支援の受け方

はじめて貢献を行うのは大変なことかもしれません。新規貢献者のためのアンバサダーは、最初の数回の貢献を行う手助けをしてくれます。Kubernetes Slackで、特に#sig-docsチャンネルを用いて連絡を取ることができます。また毎月第一火曜日に行われる新規貢献者のための歓迎会もあります。ここで新規貢献者のアンバサダーと交流し、質問に答えてもらうことができます。

次のステップ

SIG Docsに参加する

SIG DocsはKubernetesのドキュメントとウェブサイトを公開・管理するコントリビューターのグループです。SIG Docsに参加することはKubernetesコントリビューター(機能開発でもそれ以外でも)にとってKubernetesプロジェクトに大きな影響を与える素晴らしい方法の一つです。

SIG Docsは複数の方法でコミュニケーションをとっています。

その他の貢献方法

7.1 - コンテンツの改善を提案する

Kubernetesのドキュメントに何か問題を見つけたり、新しいコンテンツに関してアイデアを思い付いたときは、issueを作ってください。必要なものは、GitHubアカウントとウェブブラウザーだけです。

Kubernetesのドキュメント上の新しい作業は、ほとんどの場合、GitHubのissueから始まります。Kubernetesのコントリビューターは、必要に応じてレビュー、分類、タグ付けを行います。次に、あなたやKubernetesコミュニティの他のメンバーが、そのissueを解決するための変更を加えるpull requestを開きます。

issueを作る

既存のコンテンツに対して改善を提案したい場合や、間違いを発見した場合は、issueを作ってください。

  1. ページの右側のサイドバーにあるドキュメントのissueを作成ボタンをクリックします。GitHubのissueページにリダイレクトし、一部のヘッダーが自動的に挿入されます。
  2. 問題や改善の提案について書きます。できる限り多くの詳細情報を提供するようにしてください。
  3. Submit new issueボタンをクリックします。

送信後、定期的にissueを確認するか、GitHubの通知を設定してください。レビュアや他のコミュニティメンバーが、issueに対して作業を行うために、あなたに何か質問をするかもしれません。

新しいコンテンツの提案

新しいコンテンツに関するアイデアがあるものの、どの場所に追加すればわからないときでも、issueを作ることができます。次のいずれかを選択して行ってください。

  • コンテンツが追加されるべきだと思う既存のページを選択し、ドキュメントのissueを作成ボタンをクリックする。
  • GitHubに移動し、直接issueを作る。

よいissueを作るには

issueを作るときは、以下のことを心に留めてください。

  • 明確なissueの説明を書く。不足している点、古くなっている点、誤っている点、改善が必要な点など、どの点がそうであるか明確に書く。
  • issueがユーザーに与える具体的な影響を説明する。
  • 合理的な作業単位になるように、特定のissueのスコープを制限する。スコープの大きな問題については、小さな複数のissueに分割する。たとえば、"Fix the security docs"(セキュリティのドキュメントを修正する)というのはスコープが大きすぎますが、"Add details to the 'Restricting network access' topic"(トピック「ネットワークアクセスの制限」に詳細情報を追加する)であれば十分に作業可能な大きさです。
  • すでにあるissueを検索し、関連または同様のissueがないかどうか確認する。
  • 新しいissueがほかのissueやpull requestと関係する場合は、完全なURLを参照するか、issueやpull requestの数字の前に#の文字を付けて参照する。例えば、Introduced by #987654のように書きます。
  • 行動規範に従って、仲間のコントリビューターに敬意を払いましょう。たとえば、"The docs are terrible"(このドキュメントは最悪だ)のようなコメントは、役に立つ敬意のあるフィードバックではありません。

7.2 - 新しいコンテンツの貢献

このセクションでは、新しいコンテンツの貢献を行う前に知っておくべき情報を説明します。

flowchart LR subgraph second[始める前に] direction TB S[ ] -.- A[CNCF CLAに署名] --> B[Gitブランチを選択] B --> C[言語ごとにPR] C --> F[コントリビューターのための
ツールをチェックアウト] end subgraph first[貢献の基本] direction TB T[ ] -.- D[ドキュメントをMarkdownで書き
Hugoでサイトをビルド] --- E[GitHubにあるソース] E --- G[複数の言語のドキュメントを含む
'/content/../docs'フォルダー] G --- H[Hugoのpage content
typesやshortcodeをレビュー] end first ----> second classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,C,D,E,F,G,H grey class S,T spacewhite class first,second white

図 - 新しいコンテンツ提供の貢献方法

上記の図は新しいコンテンツを申請する前に知っておくべき情報を示しています。 詳細については以下で説明します。

貢献の基本

  • KubernetesのドキュメントはMarkdownで書き、KubernetesのウェブサイトはHugoを使ってビルドします。
  • Kubernetesのドキュメントは、MarkdownのスタイルとしてCommonMarkを使用しています。
  • ソースはGitHubにあります。Kubernetesのドキュメントは/content/en/docs/にあります。リファレンスドキュメントの一部は、update-imported-docs/ディレクトリ内のスクリプトから自動的に生成されます。
  • Page content typesにHugoによるドキュメントのコンテンツの見え方を記述しています。
  • Kubernetesのドキュメントに貢献するのにDocsy shortcodeカスタムのHugo shortcodeが使えます。
  • 標準のHugoのshortcodeに加えて、多数のカスタムのHugo shortcodeを使用してコンテンツの見え方をコントロールしています。
  • ドキュメントのソースは/content/内にある複数の言語で利用できます。各言語はそれぞれISO 639-1標準で定義された2文字のコードの名前のフォルダを持ちます。たとえば、英語のドキュメントのソースは/content/en/docs/内に置かれています。
  • 複数言語でのドキュメントへの貢献や新しい翻訳の開始に関する情報については、Kubernetesのドキュメントを翻訳するを参照してください。

始める前に

CNCF CLAに署名する

すべてのKubernetesのコントリビューターは、コントリビューターガイドを読み、Contributor License Agreement(コントリビューターライセンス契約、CLA)への署名必ず行わなければなりません

CLAへの署名が完了していないコントリビューターからのpull requestは、自動化されたテストで失敗します。名前とメールアドレスはgit configコマンドで表示されるものに一致し、gitの名前とメールアドレスはCNCF CLAで使われたものに一致しなければなりません。

どのGitブランチを使用するかを選ぶ

pull requestをオープンするときは、どのブランチをベースにして作業するかをあらかじめ知っておく必要があります。

シナリオブランチ
現在のリリースに対する既存または新しい英語のコンテンツmain
機能変更のリリースに対するコンテンツ機能変更が含まれるメジャーおよびマイナーバージョンに対応する、dev-<version>というパターンのブランチを使います。たとえば、機能変更がv1.31に含まれる場合、ドキュメントの変更はdev-1.31ブランチに追加します。
他の言語内のコンテンツ(翻訳)各翻訳対象の言語のルールに従います。詳しい情報は、翻訳のブランチ戦略を読んでください。

それでも選ぶべきブランチがわからないときは、Slack上の#sig-docsチャンネルで質問してください。

言語ごとのPR

pull requestはPRごとに1つの言語に限定してください。複数の言語に同一の変更を行う必要がある場合は、言語ごとに別々のPRを作成してください。

コントリビューターのためのツール

kubernetes/websiteリポジトリ内のdoc contributors toolsディレクトリには、コントリビューターとしての旅を楽にしてくれるツールがあります。

7.3 - 変更のレビュー

このセクションでは、コンテンツのレビュー方法について説明します。

7.3.1 - プルリクエストのレビュー

ドキュメントのプルリクエストは誰でもレビューすることができます。Kubernetesのwebsiteリポジトリでpull requestsのセクションに移動し、open状態のプルリクエストを確認してください。

ドキュメントのプルリクエストのレビューは、Kubernetesコミュニティに自分を知ってもらうためのよい方法の1つです。コードベースについて学んだり、他のコントリビューターとの信頼関係を築く助けともなるはずです。

レビューを行う前には、以下のことを理解しておくとよいでしょう。

はじめる前に

レビューを始める前に、以下のことを心に留めてください。

  • CNCFの行動規範を読み、いかなる時にも行動規範にしたがって行動するようにする。
  • 礼儀正しく、思いやりを持ち、助け合う気持ちを持つ。
  • 変更点だけでなく、PRのポジティブな側面についてもコメントする。
  • 相手の気持ちに共感して、自分のレビューが相手にどのように受け取られるのかをよく意識する。
  • 相手の善意を前提として、疑問点を明確にする質問をする。
  • 経験を積んだコントリビューターの場合、コンテンツに大幅な変更が必要な作業を行う新規のコントリビューターとペアを組んで取り組むことを考える。

レビューのプロセス

一般に、コンテンツや文体に対するプルリクエストは、英語でレビューを行います。図1は、レビュープロセスについて手順の概要を示しています。 各ステップの詳細は次のとおりです。

(訳注:SIG Docs jaでは、日本語でも対応しています。日本語の翻訳に対するレビューは、日本語でも構いません。ただし、プルリクエストの作成者や他のコントリビューターが必ずしも日本語を理解できるとは限りませんので、注意して発言してください。)

flowchart LR subgraph fourth[レビューの開始] direction TB S[ ] -.- M[コメントを追加] --> N[変更の確認] N --> O[Commentを選択] end subgraph third[PRの選択] direction TB T[ ] -.- J[説明とコメントを読む]--> K[Netlifyプレビューで
変更点を表示] end A[オープン状態の
PR一覧を確認]--> B[オープン状態のPRを
ラベルで絞り込む] B --> third --> fourth classDef grey fill:#dddddd,stroke:#ffffff,stroke-width:px,color:#000000, font-size:15px; classDef white fill:#ffffff,stroke:#000,stroke-width:px,color:#000,font-weight:bold classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class A,B,J,K,M,N,O grey class S,T spacewhite class third,fourth white

図1. レビュープロセスの手順

  1. https://github.com/kubernetes/website/pullsに移動します。Kubernetesのウェブサイトとドキュメントに対するopen状態のプルリクエスト一覧が表示されます。

  2. open状態のPRに、以下に示すラベルを1つ以上使って絞り込みます。

    • cncf-cla: yes (推奨): CLAにサインしていないコントリビューターが提出したPRはマージできません。詳しい情報は、CLAの署名を読んでください。
    • language/en (推奨): 英語のPRだけに絞り込みます。
    • size/<size>: 特定の大きさのPRだけに絞り込みます。レビューを始めたばかりの人は、小さなPRから始めてください。

    さらに、PRがwork in progressとしてマークされていないことも確認してください。work in progressラベルの付いたPRは、まだレビューの準備ができていない状態です。

  3. レビューするPRを選んだら、以下のことを行い、変更点について理解します。

    • PRの説明を読み、行われた変更について理解し、関連するissueがあればそれも読みます。
    • 他のレビュアのコメントがあれば読みます。
    • Files changedタブをクリックし、変更されたファイルと行を確認します。
    • Conversationタブの下にあるPRのbuild checkセクションまでスクロールし、Netlifyのプレビュービルドで変更点をプレビューします。これはスクリーンショットです(これは、GitHubのデスクトップサイトを見せています。タブレットやスマートフォンデバイスでレビューしている場合は、GitHubウェブのUIは少し異なります):
      GitHub pull request details including link to Netlify preview
      プレビューを開くには、チェックリストのdeploy/netlify行のDetailsリンクをクリックします。
  4. Files changedタブに移動してレビューを始めます。

    1. コメントしたい場合は行の横の+マークをクリックします。

    2. その行に関するコメントを書き、Add single comment(1つのコメントだけを残したい場合)またはStart a review(複数のコメントを行いたい場合)のいずれかをクリックします。

    3. コメントをすべて書いたら、ページ上部のReview changesをクリックします。ここでは、レビューの要約を追加できます(コントリビューターにポジティブなコメントも書きましょう!)。常に「Comment」を使用してください。

      • レビューの終了時、「Request changes」ボタンをクリックしないでください。さらに変更される前にマージされるのをブロックしたい場合、「/hold」コメントを残すことができます。Holdを設定する理由を説明し、必要に応じて、自分や他のレビューアがHoldを解除できる条件を指定してください。
      • レビューの終了時、「Approve」ボタンをクリックしないでください。大抵の場合、「/approve」コメントを残すことが推奨されます。

レビューのチェックリスト

レビューするときは、最初に以下の点を確認してみてください。

言語と文法

  • 言語や文法に明らかな間違いはないですか? もっとよい言い方はないですか?
    • 作成者が変更している箇所の用語や文法に注目してください。作成者がページ全体の変更を目的として明確にしていない限り、そのページのすべての問題を修正する義務はありません。
    • 既存のページを変更するPRである場合、変更されている箇所に注目してレビューしてください。その変更されたコンテンツは、技術的および編集の正確性についてレビューしてください。PRの作成者が対処しようとしている内容と直接関係のない間違いを見つけた場合、それは別のIssueとして扱うべきです(既存のIssueが無いことを確認してください)。
    • コンテンツを移動するPull Requestに注意してください。作成者がページの名前を変更したり、2つのページを結合したりする場合、通常、私たち(Kubernetes SIG Docs)は、その移動されたコンテンツ内で見つけられるすべての文法やスペルの間違いを修正することを作成者に要求することを避けています。
  • もっと簡単な単語に置き換えられる複雑な単語や古い単語はありませんか?
  • 使われている単語や専門用語や言い回しで差別的ではない別の言葉に置き換えられるものはありませんか?
  • 言葉選びや大文字の使い方はstyle guideに従っていますか?
  • もっと短くしたり単純な文に書き換えられる長い文はありませんか?
  • 箇条書きやテーブルでもっとわかりやすく表現できる長いパラグラフはありませんか?

コンテンツ

  • 同様のコンテンツがKubernetesのサイト上のどこかに存在しませんか?
  • コンテンツが外部サイト、特定のベンダー、オープンソースではないドキュメントなどに過剰にリンクを張っていませんか?

ウェブサイト

  • PRはページ名、slug/alias、アンカーリンクの変更や削除をしていますか? その場合、このPRの変更の結果、リンク切れは発生しませんか? ページ名を変更してslugはそのままにするなど、他の選択肢はありませんか?
  • PRは新しいページを作成するものですか? その場合、次の点に注意してください。
    • ページは正しいpage content typeと関係するHugoのshortcodeを使用していますか?
    • セクションの横のナビゲーションにページは正しく表示されますか(または表示されませんか)?
    • ページはDocs Homeに一覧されますか?
  • Netlifyのプレビューで変更は確認できますか? 特にリスト、コードブロック、テーブル、備考、画像などに注意してください。

その他

  • 些細な編集に注意してください。些細な編集だと思われる変更を見つけた場合は、そのポリシーを指摘してください (それが本当に改善である場合は、変更を受け入れても問題ありません)。
  • 空白の修正を行っている作成者には、PRの最初のコミットでそれを行い、その後に他の変更を加えるよう促してください。これにより、マージとレビューの両方が容易になります。特に、大量の空白文字の整理と共に1回のコミットで発生する些細な変更に注意してください(もしそれを見つけたら、作成者に修正を促してください)。

レビュアーが誤字や不適切な空白など、PRの本質でない小さな問題を発見した場合は、コメントの先頭にnit:を付けてください。これにより、作成者はこのフィードバックが重要でないことを知ることができます。

Pull Requestの承認を検討する際、残りのすべてのフィードバックがnitとしてマークされていれば、残っていたとしてもPRをマージできます。その場合、残っているnitに関するIssueをオープンすると役立つことがよくあります。その新しいIssueをGood First Issueとしてマークするための条件を満たすことができるかどうか検討してください。それができたら、これらは良い情報源になります。

7.3.2 - approverとreviewer向けのレビュー

SIG DocsのReviewer(レビュアー)Approver(承認者)は、変更をレビューする時にいくつか追加の作業を行います。

毎週、docsのメンバーの特定のapproverのボランティアは、pull requestのトリアージとレビューを担当します。この担当者は、その週の「PR Wrangler(PRの世話人)」と呼ばれます。詳しい情報は、PR Wrangler schedulerを参照してください。PR Wranglerになるには、週次のSIG Docsミーティングに参加し、ボランティアをします。もしその週にスケジュールされていなくても、活発なレビューが行われていないpull request(PR)をレビューすることは問題ありません。

このローテーションに加えて、変更されたファイルのオーナーに基づいて、botがPRにreviewerとapproverを割り当てます。

PRをレビューする

KubernetesのドキュメントはKubernetesコードレビュープロセスに従います。

pull requestのレビューに書かれているすべてのことが適用されますが、ReviewerとApproverはそれに加えて次のことも行います。

  • 必要に応じて、/assignProwコマンドを使用して、特定のreviewerにPRを割り当てる。これは、コードのコントリビューターからの技術的なレビューが必要な場合には特に重要です。

  • PRがコンテンツおよびスタイルのガイドに従っていることを確認してください。ガイドに従っていない場合は、ガイドの関連する部分にリンクを作者に示してください。

  • PRの作者に変更を提案できるときは、GitHubのRequest Changes(変更をリクエスト)オプションを利用してください。

  • 提案したことが反映されたら、/approve/lgtmコマンドを使用して、GitHubのレビューステータスを変更してください。

他の作者のPRにコミットを追加する

PRにコメントを残すのは助けになりますが、まれに他の作者のPRに代わりにコミットを追加する必要がある場合があります。

あなたが明示的に作者から頼まれたり、長い間放置されたPRを蘇らせるような場合でない限り、他の作者のPRを「乗っ取る」ようなことはしないでください。短期的に見ればそのほうが短時間で終わるかもしれませんが、そのようなことをするとその人が貢献するチャンスを奪ってしまうことになります。

あなたが取る方法は、編集する必要のあるファイルがすでにPRのスコープに入っているか、あるいはPRがまだ触れていないファイルであるかによって変わります。

以下のいずれかが当てはまる場合、他の作者のPRにあなたがコミットを追加することはできません。

  • PRの作者が自分のブランチを直接https://github.com/kubernetes/website/リポジトリにpushした場合。この場合、pushアクセス権限を持つreviewerしか他のユーザーのPRにコミットを追加することはできません。

  • PRの作者が明示的にapproverからの編集を禁止している場合。

レビュー向けのProwコマンド

Prowは、pull request(PR)に対してジョブを実行するKubernetesベースのCI/CDシステムです。Prowは、Kubernetes organization全体でchatbotスタイルのコマンドを利用してGitHub actionsを扱えるようにします。たとえば、ラベルの追加と削除、issueのclose、approverの割り当てなどが行なえます。Prowコマンドは、GitHubのコメントに/<command-name>という形式で入力します。

reviewerとapproverが最もよく使うprowコマンドには、以下のようなものがあります。

Prow commands for reviewing
ProwコマンドRoleの制限説明
/lgtmOrganizationメンバーPRのレビューが完了し、変更に納得したことを知らせる。
/approveApproverPRをマージすることを承認する。
/assign誰でもPRのレビューまたは承認するひとを割り当てる。
/closeOrganizationメンバーissueまたはPRをcloseする。
/hold誰でもdo-not-merge/holdラベルを追加して、自動的にマージできないPRであることを示す。
/hold cancel誰でもdo-not-merge/holdラベルを削除する。

PRで利用できるすべてのコマンドを確認するには、Prowコマンドリファレンスを参照してください。

issueのトリアージとカテゴリー分類

一般に、SIG DocsはKubernetes issue triageのプロセスに従い、同じラベルを使用しています。

このGitHub issueのフィルターは、トリアージが必要な可能性があるissueを表示します。

issueをトリアージする

  1. issueを検証する
  • issueがドキュメントのウェブサイトに関係するものであることを確かめる。質問に答えたりリソースの場所を報告者に教えることですぐに閉じられるissueもあります。詳しくは、サポートリクエストまたはコードのバグレポートのセクションを読んでください。
  • issueにメリットがあるかどうか評価する。
  • issueに行動を取るのに十分な詳細情報がない場合や、テンプレートが十分埋められていない場合は、triage/needs-informationラベルを追加する。
  • lifecycle/staletriage/needs-informationの両方のラベルがあるときは、issueをcloseする。
  1. 優先度(priority)ラベルを追加する(issueトリアージガイドラインは、priorityラベルについて詳しく定義しています。)
issueのラベル
ラベル説明
priority/critical-urgent今すぐに作業する。
priority/important-soon3ヶ月以内に取り組む。
priority/important-longterm6ヶ月以内に取り組む。
priority/backlog無期限に延期可能。リソースに余裕がある時に取り組む。
priority/awaiting-more-evidenceよいissueの可能性があるissueを見失わないようにするためのプレースホルダー。
helpまたはgood first issueKubernetesまたはSIG Docsでほとんど経験がない人に適したissue。より詳しい情報は、Help WantedとGood First Issueラベルを読んでください。

あなたの裁量で、issueのオーナーシップを取り、issueに対するPRを提出してください(簡単なissueや、自分がすでに行った作業に関連するissueである場合は特に)。

issueのトリアージについて質問があるときは、Slackの#sig-docskubernetes-sig-docs mailing listで質問してください。

issueラベルの追加と削除

ラベルを追加するには、以下のいずれかの形式でコメントします。

  • /<label-to-add>(たとえば、/good-first-issue)
  • /<label-category> <label-to-add>(たとえば、/triage needs-information/language ja)

ラベルを削除するには、以下のいずれかの形式でコメントします。

  • /remove-<label-to-remove>(たとえば、/remove-help)
  • /remove-<label-category> <label-to-remove>(たとえば、/remove-triage needs-information)

いずれの場合でも、ラベルは既存のものでなければなりません。存在しないラベルを追加しようとした場合、コマンドは無視されます。

すべてのラベル一覧は、websiteリポジトリーのラベルセクションで確認できます。SIG Docsですべてのラベルが使われているわけではありません。

issueのライフサイクルに関するラベル

issueは一般にopen後に短期間でcloseされます。しかし、issueがopenされた後にアクティブでなくなったり、issueが90日以上openのままである場合もあります。

issueのライブラリに関するラベル
ラベル説明
lifecycle/stale90日間活動がない場合、issueは自動的にstaleとラベル付けされます。/remove-lifecycle staleコマンドを使って手動でlifecycleをリバートしない限り、issueは自動的にcloseされます。
lifecycle/frozenこのラベルが付けられたissueは、90日間活動がなくてもstaleになりません。priority/important-longtermラベルを付けたissueなど、90日以上openにしておく必要があるissueには、このラベルを手動で追加します。

特別な種類のissueに対処する

SIG Docsでは、対処方法をドキュメントに書いても良いくらい頻繁に、以下のような種類のissueに出会います。

重服したissue

1つの問題に対して1つ以上のissueがopenしている場合、1つのissueに統合します。あなたはどちらのissueをopenにしておくか(あるいは新しいissueを作成するか)を決断して、すべての関連する情報を移動し、関連するすべてのissueにリンクしなければなりません。最後に、同じ問題について書かれたすべての他のissueにtriage/duplicateラベルを付けて、それらをcloseします。作業対象のissueを1つだけにすることで、混乱を減らし、同じ問題に対して作業が重複することを避けられます。

リンク切れに関するissue

リンク切れのissueがAPIまたはkubectlのドキュメントにあるものは、問題が完全に理解されるまでは/priority critical-urgentを割り当ててください。その他のすべてのリンク切れに関するissueには、手動で修正が必要であるため、/priority important-longtermを付けます。

Blogに関するissue

Kubernetes Blogのエントリーは時間が経つと情報が古くなるものだと考えています。そのため、ブログのエントリーは1年以内のものだけをメンテナンスします。1年以上前のブログエントリーに関するissueは修正せずにcloseします。

サポートリクエストまたはコードのバグレポート

一部のドキュメントのissueは、実際には元になっているコードの問題や、何か(たとえば、チュートリアル)がうまく動かないときにサポートをリクエストするものです。ドキュメントに関係のない問題は、kind/supportラベルを付け、サポートチャンネル(SlackやStack Overflowなど)へ報告者を導くコメントをして、もし関連があれば機能のバグに対するissueを報告するリポジトリ(kubernetes/kubernetesは始めるのに最適な場所です)を教えて、closeします。

サポートリクエストに対する返答の例を示します。(リクエストを行う際は英語で行うことが想定されるため、英文とその日本語訳を記載しています)

This issue sounds more like a request for support and less
like an issue specifically for docs. I encourage you to bring
your question to the `#kubernetes-users` channel in
[Kubernetes slack](https://slack.k8s.io/). You can also search
resources like
[Stack Overflow](https://stackoverflow.com/questions/tagged/kubernetes)
for answers to similar questions.

You can also open issues for Kubernetes functionality in
https://github.com/kubernetes/kubernetes.

If this is a documentation issue, please re-open this issue.
このissueは特定のドキュメントに関するissueではなく、サポートリクエストのようです。
Kubernetesに関する質問については、[Kubernetes slack](https://slack.k8s.io/)の
`#kubernetes-users`チャンネルに投稿することをおすすめします。同様の質問に対する回答を
[Stack Overflow](https://stackoverflow.com/questions/tagged/kubernetes)などの
リソースで検索することもできます。

Kubernetesの機能に関するissueについては、https://github.com/kubernetes/kubernetes
でissueを作成できます。

もしこれがドキュメントに関するissueの場合、このissueを再びopenしてください。

コードのバグに対する返答の例を示します。

This sounds more like an issue with the code than an issue with
the documentation. Please open an issue at
https://github.com/kubernetes/kubernetes/issues.

If this is a documentation issue, please re-open this issue.
こちらのissueは、ドキュメントではなくコードに関係するissueのようです。
https://github.com/kubernetes/kubernetes/issues でissueを作成してください。

もしこれがドキュメントに関するissueの場合、このissueを再びopenしてください。

7.4 - Kubernetesのドキュメントを翻訳する

このページでは、Kubernetesドキュメントにおける日本語翻訳の方針について説明します。

ドキュメントを日本語に翻訳するまでの流れ

翻訳を行うための基本的な流れについて説明します。不明点がある場合はKubernetes公式Slack#kubernetes-docs-jaチャンネルにてお気軽にご質問ください。

前提知識

翻訳作業は全てGitHubのIssueによって管理されています。翻訳作業を行いたい場合は、Issueの一覧をまず最初にご確認ください。

また、Kubernetes傘下のリポジトリではCLAと呼ばれる同意書に署名しないと、Pull Requestをマージすることができません。詳しくは英語のドキュメントや、Qiitaに有志の方が書いてくださった日本語のまとめをご覧ください。

翻訳を始めるまで

翻訳を希望するページのIssueが存在しない場合

  1. こちらのサンプルに従う形でIssueを作成する
  2. 自分自身を翻訳作業に割り当てたい場合は、Issueのメッセージまたはコメントに/assignと書く
  3. 新規ページを翻訳する場合のステップに進む

不明点がある場合はKubernetes公式Slack#kubernetes-docs-jaチャンネルにてお気軽にご質問ください。

翻訳を希望するページのIssueが存在する場合

  1. 自分自身を翻訳作業に割り当てるために、Issueのコメントに/assignと書く
  2. 新規ページを翻訳する場合のステップに進む

Pull Requestを送るまで

新規ページを翻訳する場合の手順

  1. kubernetes/websiteリポジトリをフォークする
  2. mainから任意の名前でブランチを作成する
  3. content/enのディレクトリから必要なファイルをcontent/jaにコピーし、翻訳する
  4. mainブランチに向けてPull Requestを作成する

既存のページの誤字脱字や古い記述を修正する場合の手順

  1. kubernetes/websiteリポジトリをフォークする
  2. mainから任意の名前でブランチを作成する
  3. content/jaのディレクトリから必要なファイルを編集する
  4. mainブランチに向けてPull Requestを作成する

翻訳スタイルガイド

基本方針

  • 本文を、敬体(ですます調)で統一
    • 特に、「〜になります」「〜となります」という表現は「〜です」の方が適切な場合が多いため注意
  • 句読点は「、」と「。」を使用
  • 漢字、ひらがな、カタカナは全角で表記
  • 数字とアルファベットは半角で表記
  • 記号類は感嘆符「!」と疑問符「?」のみ全角、それ以外は半角で表記
  • 英単語と日本語の間に半角スペースは不要
  • 日本語文では、文章の途中で改行を行わない。句点「。」で改行する
  • メタデータのreviewerの項目は削除する
  • すでに日本語訳が存在するページにリンクを張る場合は、/ja/を含めたURLを使用する
    • 例: /path/to/page/ではなく、/ja/path/to/page/を使用する

用語の表記

Kubernetesのリソース名や技術用語などは、原則としてそのままの表記を使用します。 例えば、PodやService、Deploymentなどは翻訳せずにそのまま表記してください。

ただし、ノード(Node)に関しては明確にKubernetesとしてのNodeリソース(例: kind: Nodekubectl get nodes)を指していないのであれば、「ノード」と表記してください。

またこれらの単語は、複数形ではなく単数形を用います。 例えば、原文に"pods"と表記されている場合でも、日本語訳では"Pod"と表記してください。

頻出表記(日本語)

よくある表記あるべき形
〜ので、〜から、〜だから〜のため 、〜ため
(あいうえお。)(あいうえお)。
〇,〇,〇〇、〇、〇(※列挙はすべて読点で統一)

長音の有無

カタカナ語に長音を付与するかどうかは、以下の原則に従ってください。

  • -er、-or、-ar、-cy、-gyで終わる単語は長音を付与する
    • 例: 「クラスター」「セレクター」「サイドカー」「ポリシー」「トポロジー」
  • -ear、-eer、-re、-ty、-dy、-ryで終わる単語は長音を付与しない
    • 例: 「クリア」「エンジニア」「アーキテクチャ」「セキュリティ」「スタディ」「ディレクトリ」

ただし、「コンテナ」は例外的に長音を付与しないこととします。

この原則を作成するにあたって、mozilla-japan/translation Editorial Guideline#カタカナ語の表記を参考にしました。

その他の表記

その他の表記については、以下の表を参考にしてください。

英語日本語
interfaceインターフェース
proxyプロキシ
quotaクォータ
stacked積層

cron jobの訳し方に関して

混同を避けるため、cron jobはcronジョブと訳し、CronJobはリソース名としてそのまま表記します。 cron「の」ジョブは、「の」が続く事による解釈の難から基本的にはつけないものとします。

その他基本方針など

  • 意訳と直訳で迷った場合は「直訳」で訳す
  • 訳で難しい・わからないと感じたらSlackの#kubernetes-docs-jaで相談する
  • できることを挙手制で、できないときは早めに報告

アップストリームのコントリビューター

SIG Docsでは、英語のソースに対するアップストリームへのコントリビュートや誤りの訂正を歓迎しています。

7.5 - SIG Docsへの参加

SIG Docsは、Kubernetesプロジェクト内の special interest groupsの1つであり、 Kubernetes全体のドキュメントの作成、更新、および保守に重点を置いています。 SIGの詳細については、SIG DocsのGithubリポジトリを参照してください。

SIG Docsは、すべての寄稿者からのコンテンツとレビューを歓迎します。 誰でもPull Request(PR)を開くことができ、コンテンツに関するissueを提出したり、進行中のPull Requestにコメントしたりできます。

あなたは、memberや、 reviewerapproverになることもできます。 これらの役割にはより多くのアクセスが必要であり、変更を承認およびコミットするための特定の責任が伴います。 Kubernetesコミュニティ内でメンバーシップがどのように機能するかについての詳細は、 community-membership をご覧ください。

このドキュメントの残りの部分では、kubernetesの中で最も広く公開されている Kubernetesのウェブサイトとドキュメントの管理を担当しているSIG Docsの中で、これらの役割がどのように機能するのかを概説します。

SIG Docs chairperson

SIG Docsを含む各SIGは、議長として機能する1人以上のSIGメンバーを選択します。 これらは、SIGDocsとKubernetes organizationの他の部分との連絡先です。 それらには、Kubernetesプロジェクト全体の構造と、SIG Docsがその中でどのように機能するかについての広範な知識が必要です。 現在のchairpersonのリストについては、 Leadership を参照してください。

SIG Docs teamsと自動化

SIG Docsの自動化は、GitHub teamsとOWNERSファイルの2つの異なるメカニズムに依存しています。

GitHub teams

GitHubには、二つのSIG Docs teams カテゴリがあります:

  • @sig-docs-{language}-ownersは承認者かつリードです。
  • @sig-docs-{language}-reviews はレビュアーです。

それぞれをGitHubコメントの@nameで参照して、そのグループの全員とコミュニケーションできます。

ProwチームとGitHub teamsが完全に一致せずに重複する場合があります。 問題の割り当て、Pull Request、およびPR承認のサポートのために、自動化ではOWNERSファイルからの情報を使用します。

OWNERSファイルとfront-matter

Kubernetesプロジェクトは、GitHubのissueとPull Requestに関連する自動化のためにprowと呼ばれる自動化ツールを使用します。 Kubernetes Webサイトリポジトリ は、2つのprowプラグインを使用します:

  • blunderbuss
  • approve

これらの2つのプラグインはkubernetes.websiteのGithubリポジトリのトップレベルにある OWNERSファイルと、 OWNERS_ALIASESファイルを使用して、 リポジトリ内でのprowの動作を制御します。

OWNERSファイルには、SIG Docsのレビュー担当者および承認者であるユーザーのリストが含まれています。 OWNERSファイルはサブディレクトリに存在することもでき、そのサブディレクトリとその子孫のファイルのレビュー担当者または承認者として機能できるユーザーを上書きできます。 一般的なOWNERSファイルの詳細については、 OWNERSを参照してください。

さらに、個々のMarkdownファイルは、個々のGitHubユーザー名またはGitHubグループを一覧表示することにより、そのfront-matterでレビュー担当者と承認者を一覧表示できます。

OWNERSファイルとMarkdownファイルのfront-matterの組み合わせにより、PRの技術的および編集上のレビューを誰に依頼するかについてPRの所有者が自動化システムから得るアドバイスが決まります。

マージの仕組み

Pull Requestがコンテンツの公開に使用されるブランチにマージされると、そのコンテンツは http://kubernetes.io に公開されます。 公開されたコンテンツの品質を高くするために、Pull RequestのマージはSIG Docsの承認者に限定しています。仕組みは次のとおりです。

  • Pull Requestにlgtmラベルとapproveラベルの両方があり、holdラベルがなく、すべてのテストに合格すると、Pull Requestは自動的にマージされます。
  • Kubernetes organizationのメンバーとSIG Docsの承認者はコメントを追加して、特定のPull Requestが自動的にマージされないようにすることができます(/holdコメントを追加するか、/lgtmコメントを保留します)。
  • Kubernetesメンバーは誰でも、/lgtmコメントを追加することでlgtmラベルを追加できます。
  • /approveコメントを追加してPull Requestをマージできるのは、SIG Docsの承認者だけです。一部の承認者は、PR WranglerSIG Docsのchairpersonなど、追加の特定の役割も実行します。

次の項目

Kubernetesドキュメントへの貢献の詳細については、以下を参照してください:

7.6 - ドキュメントスタイルの概要

このセクション内のトピックでは、文章のスタイル、コンテンツの形式や構成、特にKubernetesのドキュメント特有のHugoカスタマイズの使用方法に関するガイダンスを提供します。

7.6.1 - ドキュメントコンテンツガイド

このページでは、Kubernetesのドキュメント上のコンテンツのガイドラインを説明します。

許可されるコンテンツに関して疑問がある場合は、Kubernetes Slackの#sig-docsチャンネルに参加して質問してください!

Kubernetes Slackには、https://slack.k8s.io/ から参加登録ができます。

Kubernetesドキュメントの新しいコンテンツの作成に関する情報については、スタイルガイドに従ってください。

概要

ドキュメントを含むKubernetesのウェブサイトのソースは、kubernetes/websiteリポジトリに置かれています。

Kubernetesの主要なドキュメントはkubernetes/website/content/<language_code>/docsフォルダに置かれており、これらはKubernetesプロジェクトを対象としています。

許可されるコンテンツ

Kubernetesのドキュメントにサードパーティーのコンテンツを掲載することが許されるのは、次の場合のみです。

  • コンテンツがKubernetesプロジェクト内のソフトウェアのドキュメントとなる場合
  • コンテンツがプロジェクト外のソフトウェアのドキュメントとなるが、Kubernetesを機能させるために必要である場合
  • コンテンツがkubernetes.ioの正規のコンテンツであるか、他の場所の正規のコンテンツへのリンクである場合

サードパーティーのコンテンツ

Kubernetesのドキュメントには、Kubernetesプロジェクト(kubernetesおよびkubernetes-sigs GitHub organizationsに存在するプロジェクト)の適用例が含まれています。

Kubernetesプロジェクト内のアクティブなコンテンツへのリンクは常に許可されます。

Kubernetesを機能させるためには、一部サードパーティーのコンテンツが必要です。たとえば、コンテナランタイム(containerd、CRI-O、Docker)、ネットワークポリシー(CNI plugin)、Ingressコントローラーロギングなどです。

ドキュメント内で、Kubernetesプロジェクト外のサードパーティーのオープンソースソフトウェア(OSS)にリンクすることができるのは、Kubernetesを機能させるために必要な場合のみです。

情報源が重複するコンテンツ

可能な限り、Kubernetesのドキュメントは正規の情報源にリンクするようにし、情報源が重複してしまうようなホスティングは行いません。

情報源が重複したコンテンツは、メンテナンスするために2倍の労力(あるいはそれ以上!)が必要になり、より早く情報が古くなってしまいます。

その他の情報

許可されるコンテンツに関して疑問がある場合は、Kubernetes Slackの#sig-docsチャンネルに参加して質問してください!

次の項目

7.6.2 - コンテンツの構造化

このサイトではHugoを使用しています。Hugoでは、コンテンツの構造化がコアコンセプトとなっています。

ページの一覧

ページの順序

ドキュメントのサイドメニューやページブラウザーなどでは、Hugoのデフォルトのソート順序を使用して一覧を作成しています。デフォルトでは、weight(1から開始)、日付(最新のものが1番目)、最後にリンクのタイトルの順でソートされます。

そのため、特定のページやセッションを上に移動したい場合には、ページのフロントマター内のweightを設定します。

title: My Page
weight: 10

ドキュメントのメインメニュー

ドキュメントのメインメニューは、docs/以下に置かれたセクションのコンテンツファイル_index.mdのフロントマター内にmain_menuフラグが設定されたものから生成されます。

main_menu: true

リンクのタイトルは、ページのlinkTitleから取得されることに注意してください。そのため、ページのタイトルとは異なるリンクテキストにしたい場合、コンテンツファイル内の値を以下のように設定します。

main_menu: true
title: ページタイトル
linkTitle: リンク内で使われるタイトル

ドキュメントのサイドメニュー

ドキュメントのサイドバーメニューは、docs/以下の現在のセクションツリーから生成されます。

セクションと、そのセクション内のページがすべて表示されます。

特定のセクションやページをリストに表示したくない場合、フロントマター内のtoc_hideフラグをtrueに設定してください。

toc_hide: true

コンテンツが存在するセクションに移動すると、特定のセクションまたはページ(例:index.md)が表示されます。それ以外の場合、そのセクションの最初のページが表示されます。

ドキュメントのブラウザー

ドキュメントのホームページのページブラウザーは、docsセクション直下のすべてのセクションとページを使用して生成されています。

特定のセクションやページを表示したくない場合、フロントマターのtoc_hideフラグをtrueに設定してください。

toc_hide: true

メインメニュー

右上のメニュー(およびフッター)にあるサイトリンクは、page-lookupの機能を使用して実装されています。これにより、ページが実際に存在することを保証しています。そのため、たとえばcase-studiesのセクションが特定の言語のサイトに存在しない場合、メニューにはケーススタディのリンクが表示されません。

Page Bundle

スタンドアローンのコンテンツページ(Markdownファイル)に加えて、Hugoでは、Page Bundlesがサポートされています。

Page Bundleの1つの例は、カスタムのHugo Shortcodeです。これは、leaf bundleであると見做されます。ディレクトリ内のすべてのファイルは、index.mdを含めてバンドルの一部となります。これには、ページからの相対リンク、処理可能な画像なども含まれます。

en/docs/home/contribute/includes
├── example1.md
├── example2.md
├── index.md
└── podtemplate.json

もう1つのPage Bundleがよく使われる例は、includesバンドルです。フロントマターにheadless: trueを設定すると、自分自身のURLを持たなくなり、他のページ内でのみ使用されるようになります。

en/includes
├── default-storage-class-prereqs.md
├── index.md
├── partner-script.js
├── partner-style.css
├── task-tutorial-prereqs.md
├── user-guide-content-moved.md
└── user-guide-migration-notice.md

バンドル内のファイルに関して、いくつか重要な注意点があります。

  • 翻訳されたバンドルに対しては、コンテンツ以外の見つからなかったファイルは上位の言語から継承されます。これにより重複が回避できます。
  • バンドル内のすべてのファイルは、HugoがResourcesと呼ぶファイルになり、フロントマター(YAMLファイルなど)をサポートしていない場合であっても、言語ごとにパラメーターやタイトルなどのメタデータを提供できます。詳しくは、Page Resourcesメタデータを参照してください。
  • Resource.RelPermalinkから取得した値は、ページからの相対的なものとなっています。詳しくは、Permalinksを参照してください。

スタイル

このサイトのスタイルシートのSASSのソースは、assets/sassに置かれていて、Hugoによって自動的にビルドされます。

次の項目

7.6.3 - カスタムHugoショートコード

このページではKubernetesのマークダウンドキュメント内で使用できるHugoショートコードについて説明します。

ショートコードについての詳細はHugoのドキュメントを読んでください。

機能の状態

このサイトのマークダウンページ(.mdファイル)内では、説明されている機能のバージョンや状態を表示するためにショートコードを使用することができます。

機能の状態のデモ

最新のKubernetesバージョンで機能をstableとして表示するためのデモスニペットを次に示します。

{{< feature-state state="stable" >}}

これは次の様に表示されます:

FEATURE STATE: Kubernetes v1.30 [stable]

stateの値として妥当な値は次のいずれかです:

  • alpha
  • beta
  • deprecated
  • stable

機能の状態コード

表示されるKubernetesのバージョンのデフォルトはそのページのデフォルトまたはサイトのデフォルトです。 for_k8s_versionパラメーターを渡すことにより、機能の状態バージョンを変更することができます。 例えば:

{{< feature-state for_k8s_version="v1.10" state="beta" >}}

これは次の様に表示されます:

FEATURE STATE: Kubernetes v1.10 [beta]

用語集

用語集に関連するショートコードとして、glossary_tooltipglossary_definitionの二つがあります。

コンテンツを自動的に更新し、用語集へのリンクを付与する挿入を使用して、用語を参照することができます。 用語がマウスオーバーされると、用語集の内容がツールチップとして表示されます。 また、用語はリンクとして表示されます。

ツールチップの挿入と同様に、用語集の定義も再利用することができます。

用語集の用語データはglossaryディレクトリに、それぞれの用語のファイルとして保存されています。

用語集のデモ

例えば、マークダウン内でツールチップ付きのclusterを表示するには、次の挿入を使用します:

{{< glossary_tooltip text="cluster" term_id="cluster" >}}

用語集の定義はこのようにします:

{{< glossary_definition prepend="A cluster is" term_id="cluster" length="short" >}}

これは次の様に表示されます:

A cluster is コンテナ化されたアプリケーションを実行する、ノードと呼ばれるワーカーマシンの集合です。すべてのクラスターには少なくとも1つのワーカーノードがあります。

完全な用語定義を挿入することもできます:

{{< glossary_definition term_id="cluster" length="all" >}}

これは次の様に表示されます:

コンテナ化されたアプリケーションを実行する、ノードと呼ばれるワーカーマシンの集合です。すべてのクラスターには少なくとも1つのワーカーノードがあります。

ワーカーノードは、アプリケーションのコンポーネントであるPodをホストします。マスターノードは、クラスター内のワーカーノードとPodを管理します。複数のマスターノードを使用して、クラスターにフェイルオーバーと高可用性を提供します。 ワーカーノードは、アプリケーションワークロードのコンポーネントであるPodをホストします。コントロールプレーンは、クラスター内のワーカーノードとPodを管理します。本番環境では、コントロールプレーンは複数のコンピューターを使用し、クラスターは複数のノードを使用し、耐障害性や高可用性を提供します。

APIリファレンスへのリンク

api-referenceショートコードを使用することで、Kubernetes APIリファレンスへのリンクを作成することができます。 例えば、 Podへの参照方法は次の通りです:

{{< api-reference page="workload-resources/pod-v1" >}}

pageパラメーターの値はAPIリファレンスページのURLの末尾です。

anchorパラメーターを指定することでページ内の特定の場所へリンクすることもできます。 例えば、 PodSpecenvironment-variablesへのリンクは次の様に書きます:

{{< api-reference page="workload-resources/pod-v1" anchor="PodSpec" >}}
{{< api-reference page="workload-resources/pod-v1" anchor="environment-variables" >}}

textパラメーターを指定することでリンクテキストを変更することもできます。 例えば、 Environment Variablesへのリンクは次の様に書きます:

{{< api-reference page="workload-resources/pod-v1" anchor="environment-variables" text="Environment Variable" >}}

テーブルキャプション

テーブルキャプションを追加することで、表をスクリーンリーダーにとってよりアクセスしやすいものにする事ができます。 表へキャプションを追加するには、表をtableショートコードで囲い、captionパラメーターにキャプションを指定します。

例えば、次の様に書きます:

{{< table caption="Configuration parameters" >}}
Parameter | Description | Default
:---------|:------------|:-------
`timeout` | The timeout for requests | `30s`
`logLevel` | The log level for log output | `INFO`
{{< /table >}}

これは次の様に表示されます:

Configuration parameters
ParameterDescriptionDefault
timeoutThe timeout for requests30s
logLevelThe log level for log outputINFO

この表に対するHTMLを検査すると、次の要素が<table>要素のすぐ次にあるのを見ることができるでしょう:

<caption style="display: none;">Configuration parameters</caption>

タブ

このサイトのマークダウンページ(.mdファイル)内では、あるソリューションに対する複数のフレーバーを表示するためのタブセットを追加することができます。

tabsショートコードはこれらのパラメーターを受けとります:

  • name: タブに表示される名前
  • codelang: 内側のtabショートコードにこれを指定した場合、Hugoはハイライトに使用するコード言語を知ることができます。
  • include: タブ内で挿入するファイル。Hugo leaf bundle内にタブがある場合そのファイル(HugoがサポートしているどのMIMEタイプでも良い)はそのbundle自身によって探されます。 もしそうでない場合、そのコンテントページは現在のページから相対的に探されます。 includeを使う場合、ショートコードの内部コンテンツはなく、自己終了構文を使用する必要があることに注意してください。 例えば、{{< tab name="Content File #1" include="example1" />}}の様にします。 codelangを指定するか、ファイル名から言語が特定される必要があります。 非コンテンツファイルはデフォルトでコードが強調表示されます。
  • もし内部コンテンツがマークダウンの場合、タブの周りに%デリミターを使用する必要があります。 例えば、{{% tab name="Tab 1" %}}This is **markdown**{{% /tab %}}の様にします。
  • タブセット内で、上記で説明したバリエーションを組み合わせることができます。

タブショートコードの例を次に示します。

タブのデモ: コードハイライト

{{< tabs name="tab_with_code" >}}
{{{< tab name="Tab 1" codelang="bash" >}}
echo "これはタブ1です。"
{{< /tab >}}
{{< tab name="Tab 2" codelang="go" >}}
println "これはタブ2です。"
{{< /tab >}}}
{{< /tabs >}}

これは次の様に表示されます:


echo "これはタブ1です。"


println "これはタブ2です。"

タブのデモ: インラインマークダウンとHTML

{{< tabs name="tab_with_md" >}}
{{% tab name="Markdown" %}}
これは**なにがしかのマークダウン**です。
{{< note >}}
ショートコードを含むこともできます。
{{< /note >}}
{{% /tab %}}
{{< tab name="HTML" >}}
<div>
	<h3>プレーンHTML</h3>
	<p>これはなにがしかの<i>プレーン</i>HTMLです。</p>
</div>
{{< /tab >}}
{{< /tabs >}}

これは次の様に表示されます。

これはなにがしかのマークダウンです。

プレーンHTML

これはなにがしかのプレーンHTMLです。

タブのデモ: ファイルの読み込み

{{< tabs name="tab_with_file_include" >}}
{{< tab name="Content File #1" include="example1" />}}
{{< tab name="Content File #2" include="example2" />}}
{{< tab name="JSON File" include="podtemplate" />}}
{{< /tabs >}}

これは次の様に表示されます:

これは挿入leaf bundle内のコンテンツファイルのです。

これは挿入leaf bundle内のコンテンツファイルのもう一つのです

  {
    "apiVersion": "v1",
    "kind": "PodTemplate",
    "metadata": {
      "name": "nginx"
    },
    "template": {
      "metadata": {
        "labels": {
          "name": "nginx"
        },
        "generateName": "nginx-"
      },
      "spec": {
         "containers": [{
           "name": "nginx",
           "image": "dockerfile/nginx",
           "ports": [{"containerPort": 80}]
         }]
      }
    }
  }

サードパーティーコンテンツマーカー

Kubernetesの実行にはサードパーティーのソフトウェアが必要です。 例えば、名前解決を行うためにはクラスターにDNSサーバーを追加する必要があります。

私たちがサードパーティーソフトウェアにリンクするときや言及するときは、コンテンツガイドに従い、サードパーティーのものに印をつけます。

これらのショートコードを使用すると、それらを使用しているドキュメントページに免責事項が追加されます。

リスト

サードパーティーのリストには、

{{% thirdparty-content %}}

をすべてのアイテムを含むセクションのヘッダーのすぐ下に追加します。

アイテム

ほとんどのアイテムがプロジェクト内ソフトウェア(例えばKubernetes自体やDeschedulerコンポーネント)を参照している場合、違う形を使用することができます。

次のショートコードをアイテムの前か、特定のアイテムのヘッダーのすぐ下に追加します:

{{% thirdparty-content single="true" %}}

バージョン文字列

ドキュメント内でバージョン文字列を生成して挿入するために、いくつかのバージョンショートコードから選んで使用することができます。 それぞれのバージョンショートコードはサイトの設定ファイル(hugo.toml)から取得したバージョンパラメーターの値を使用してバージョン文字列を表示します。 最もよく使われる二つのバージョンパラメーターはlatestversionです。

{{< param "version" >}}

{{< param "version" >}}ショートコードはサイトのversionパラメーターに設定されたKubernetesドキュメントの現在のバージョンを生成します。 paramショートコードはサイトパラメーターの名前の一つを受けとり、この場合はversionを渡しています。

これは次の様に表示されます:

v1.30

{{< latest-version >}}

{{< latest-version >}}ショートコードはサイトのlatestパラメーターの値を返します。 サイトのlatestパラメーターは新しいドキュメントのバージョンがリリースされた時に更新されます。 このパラメーターは必ずしもversionの値と一致しません。

これは次の様に表示されます:

v1.30

{{< latest-semver >}}

{{< latest-semver >}}ショートコードはlatestから"v"接頭辞を取り除いた値を生成します。

これは次の様に表示されます。

1.30

{{< version-check >}}

{{< version-check >}}ショートコードはページにmin-kubernetes-server-versionパラメーターがあるかどうか確認し、versionと比較するために使用します。

これは次の様に表示されます:

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

{{< latest-release-notes >}}

{{< latest-release-notes >}}ショートコードはlatestからバージョン文字列を生成し、"v"接頭辞を取り除きます。 このショートコードはバージョン文字列に対応したリリースノートCHANGELOGページのURLを表示します。

これは次の様に表示されます:

https://git.k8s.io/kubernetes/CHANGELOG/CHANGELOG-1.30.md

次の項目