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

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

コンセプト

本セクションは、Kubernetesシステムの各パートと、クラスターを表現するためにKubernetesが使用する抽象概念について学習し、Kubernetesの仕組みをより深く理解するのに役立ちます。

1 - 概要

Kubernetesは、宣言的な構成管理と自動化を促進し、コンテナ化されたワークロードやサービスを管理するための、ポータブルで拡張性のあるオープンソースのプラットフォームです。Kubernetesは巨大で急速に成長しているエコシステムを備えており、それらのサービス、サポート、ツールは幅広い形で利用可能です。

このページでは、Kubernetesの概要について説明します。

Kubernetesの名称は、ギリシャ語に由来し、操舵手やパイロットを意味しています。 "K"と"s"の間にある8つの文字を数えることから、K8sが略語として使われています。 Googleは2014年にKubernetesプロジェクトをオープンソース化しました。 Kubernetesは、本番環境で大規模なワークロードを稼働させたGoogleの15年以上の経験と、コミュニティからの最高のアイディアや実践を組み合わせています。

Kubernetesが必要な理由と提供する機能

コンテナは、アプリケーションを集約して実行する良い方法です。本番環境では、アプリケーションを実行しダウンタイムが発生しないように、コンテナを管理する必要があります。例えば、コンテナがダウンした場合、他のコンテナを起動する必要があります。このような動作がシステムに組み込まれていると、管理が簡単になるのではないでしょうか?

そこを助けてくれるのがKubernetesです!Kubernetesは分散システムを弾力的に実行するフレームワークを提供してくれます。あなたのアプリケーションのためにスケーリングとフェイルオーバーの面倒を見てくれて、デプロイのパターンなどを提供します。例えば、Kubernetesはシステムにカナリアデプロイを簡単に管理することができます。

Kubernetesは以下を提供します。

  • サービスディスカバリと負荷分散 Kubernetesは、DNS名または独自のIPアドレスを使ってコンテナを公開することができます。コンテナへのトラフィックが多い場合は、Kubernetesは負荷分散し、ネットワークトラフィックを振り分けることができるため、デプロイが安定します。
  • ストレージオーケストレーション Kubernetesは、ローカルストレージやパブリッククラウドプロバイダーなど、選択したストレージシステムを自動でマウントすることができます。
  • 自動化されたロールアウトとロールバック Kubernetesを使うとデプロイしたコンテナのあるべき状態を記述することができ、制御されたスピードで実際の状態をあるべき状態に変更することができます。例えば、アプリケーションのデプロイのために、新しいコンテナの作成や既存コンテナの削除、新しいコンテナにあらゆるリソースを適用する作業を、Kubernetesで自動化できます。
  • 自動ビンパッキング コンテナ化されたタスクを実行するノードのクラスターをKubernetesへ提供します。各コンテナがどれくらいCPUやメモリ(RAM)を必要とするのかをKubernetesに宣言することができます。Kubernetesはコンテナをノードにあわせて調整することができ、リソースを最大限に活用してくれます。
  • 自己修復 Kubernetesは、処理が失敗したコンテナを再起動し、コンテナを入れ替え、定義したヘルスチェックに応答しないコンテナを強制終了します。処理の準備ができるまでは、クライアントに通知しません。
  • 機密情報と構成管理 Kubernetesは、パスワードやOAuthトークン、SSHキーなどの機密の情報を保持し、管理することができます。機密情報をデプロイし、コンテナイメージを再作成することなくアプリケーションの構成情報を更新することができます。スタック構成の中で機密情報を晒してしまうこともありません。
  • バッチの実行 サービスに加えて、KubernetesはバッチやCIのワークロードを管理し、必要であれば障害が発生したコンテナを置き換えることもできます。
  • 水平スケーリング 簡単なコマンド、UI、あるいはCPU使用率に基づいて自動的に、アプリケーションをスケールイン、スケールアウトできます。
  • IPv4/IPv6デュアルスタック PodとServiceに、IPv4とIPv6アドレスを割り当てます。
  • 拡張性のある設計 アップストリームのソースコードを変更することなく、Kubernetesクラスターに機能を追加できます。

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へどのように移行するかは問題ではありません。集中管理も必要ありません。これにより、使いやすく、より強力で、堅牢で、弾力性と拡張性があるシステムが実現します。

Kubernetesの歴史的背景

過去を振り返って、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台の大きな単一目的のマシン上に実行するモノリシックなスタックではありません。
  • リソースの分割: アプリケーションのパフォーマンスが予測可能です。
  • リソースの効率的な利用: 高い効率性と集約性を実現できます。

次の項目

1.1 - Kubernetesのコンポーネント

Kubernetesクラスターを構成するキーコンポーネントの概要。

このページでは、Kubernetesクラスターを構成する必須コンポーネントの概略を説明します。

Kubernetesのコンポーネント

Kubernetesクラスターのコンポーネント

コアコンポーネント

Kubernetesクラスターは、コントロールプレーンと1つ以上のワーカーノードで構成されています。 以下に主要なコンポーネントの概要を簡単に説明します。

コントロールプレーンコンポーネント

クラスター全体の状態を管理します:

kube-apiserver
Kubernetes HTTP APIを公開するコアコンポーネントサーバーです。
etcd
APIサーバーのすべてのデータを保存する、一貫性と高可用性を兼ね備えたキーバリューストアです。
kube-scheduler
まだノードにバインドされていないPodを検出し、それぞれのPodを適切なノードに割り当てます。
kube-controller-manager
コントローラーを実行して、Kubernetes APIの動作を実装します。
cloud-controller-manager(オプション)
基盤となるクラウドプロバイダーと統合します。

ノードコンポーネント

すべてのノードで実行され、実行中のPodを維持し、Kubernetesランタイム環境を提供します。

kubelet
コンテナを含むPodが稼働していることを保証します。
kube-proxy(オプション)
ノード上のネットワークルールを維持し、Serviceを実装します。
コンテナランタイム
コンテナを実行する役割を持つソフトウェアです。 詳細はコンテナランタイムを参照してください。
🛇 この項目は、Kubernetes自体の一部ではないサードパーティのプロジェクトまたは製品にリンクしています。詳細情報

クラスターによっては、各ノードに追加のソフトウェアが必要になる場合があります。 例えば、Linuxノード上でsystemdを実行してローカルコンポーネントを監督することもあります。

アドオン

アドオンはKubernetesの機能を拡張します。 いくつかの重要な例は次のとおりです:

DNS
クラスター全体のDNS解決のために使われます。
Web UI(ダッシュボード)
ウェブインターフェースを通してクラスターを管理するために使われます。
Container Resource Monitoring
コンテナのメトリクスを収集・保存するために使われます。
Cluster-level Logging
コンテナのログを中央ログストアに保存するために使われます。

アーキテクチャの柔軟性

Kubernetesは、これらのコンポーネントのデプロイと管理方法に柔軟性を持たせることができます。 Kubernetesのアーキテクチャは、小規模な開発環境から大規模な本番環境への展開まで、様々なニーズに適応できます。

各コンポーネントの詳細情報や、クラスターのアーキテクチャを設定する様々な方法については、クラスターのアーキテクチャのページをご覧ください。

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-Encoding gzip このヘッダーを使わないことも可能
Accept application/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.36では、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-Encoding gzip このヘッダーを使わないことも可能
Accept application/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バージョンを使用する必要があります。

備考:

Kubernetesは、 alpha APIバージョンについても互換性の維持に注力しますが、いくつかの事情により不可である場合もあります。 alpha APIバージョンを使っている場合、クラスターをアップグレードする時にKubernetesのリリースノートを確認してください。 APIが互換性のない方法で変更された場合は、アップグレードをする前に既存のalphaオブジェクトをすべて削除する必要があります。

APIバージョンレベルの定義に関する詳細はAPIバージョンのリファレンスを参照してください。

APIの拡張

Kubernetes APIは2つの方法で拡張できます。

  1. カスタムリソースは、APIサーバーが選択したリソースAPIをどのように提供するかを宣言的に定義します。
  2. アグリゲーションレイヤーを実装することでKubernetes APIを拡張することもできます。

次の項目

  • 自分自身でカスタムリソース定義を追加してKubernetes APIを拡張する方法について学んでください。
  • Kubernetes APIのアクセス制御では、クラスターがAPIアクセスの認証と承認を管理する方法を説明しています。
  • APIリファレンスを読んで、APIエンドポイント、リソースタイプやサンプルについて学んでください。
  • APIの変更から、互換性のある変更とは何か, どのようにAPIを変更するかについて学んでください。

1.3 - Kubernetesオブジェクトを利用する

Kubernetesオブジェクトは、Kubernetes上で永続的なエンティティです。Kubernetesはこれらのエンティティを使い、クラスターの状態を表現します。 Kubernetesオブジェクトモデルと、これらのオブジェクトの利用方法について学びます。

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について説明します。

1.3.2 - Kubernetesオブジェクト管理

kubectlコマンドラインツールは、Kubernetesオブジェクトを作成、管理するためにいくつかの異なる方法をサポートしています。 このドキュメントでは、それらの異なるアプローチごとの概要を提供します。 Kubectlを使ったオブジェクト管理の詳細は、Kubectl bookを参照してください。

管理手法

警告:

Kubernetesのオブジェクトは、いずれか一つの手法で管理してください。 同じオブジェクトに対して、複数の手法を組み合わせた場合、未定義の挙動をもたらします。
管理手法 何を対象にするか 推奨環境 サポートライター 学習曲線
命令型コマンド 現行のオブジェクト 開発用プロジェクト 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リファレンスを参照してください。

警告:

命令型のreplaceコマンドは、既存の構成情報を新しく提供された設定に置き換え、設定ファイルに無いオブジェクトの全ての変更を削除します。 このアプローチは、構成情報が設定ファイルとは無関係に更新されるリソースタイプでは使用しないでください。 例えば、タイプがLoadBalancerのServiceオブジェクトにおけるexternalIPsフィールドは、設定ファイルとは無関係に、クラスターによって更新されます。

設定ファイルに定義されたオブジェクトを作成します:

kubectl create -f nginx.yaml

設定ファイルに定義されたオブジェクトを削除します:

kubectl delete -f nginx.yaml -f redis.yaml

設定ファイルに定義された情報で、現行の設定を上書き更新します:

kubectl replace -f nginx.yaml

トレードオフ

命令型コマンド手法に対する長所:

  • オブジェクト設定をGitのような、ソースコード管理システムに格納することができます
  • オブジェクト設定の変更内容をプッシュする前にレビュー、監査証跡を残すようなプロセスと連携することができます
  • オブジェクト設定は新しいオブジェクトを作る際のテンプレートを提供します

命令型コマンド手法に対する短所:

  • オブジェクト設定ではオブジェクトスキーマの基礎的な理解が必要です
  • オブジェクト設定ではYAMLファイルを書くという、追加のステップが必要です

宣言型オブジェクト設定手法に対する長所:

  • 命令型オブジェクト設定の振る舞いは、よりシンプルで簡単に理解ができます
  • Kubernetesバージョン1.5においては、命令型オブジェクト設定の方がより成熟しています

宣言型オブジェクト設定手法に対する短所:

  • 命令型オブジェクト設定は各ファイルごとに設定を書くには最も適していますが、ディレクトリには適していません
  • 現行オブジェクトの更新は設定ファイルに対して反映しなければなりません。反映されない場合、次の置き換え時に更新内容が失われてしまいます

宣言型オブジェクト設定

宣言型オブジェクト設定を利用する場合、ユーザーはローカルに置かれている設定ファイルを操作します。 しかし、ユーザーはファイルに対する操作内容を指定しません。作成、更新、そして削除といった操作はオブジェクトごとにkubectlが検出します。 この仕組みが、異なるオブジェクトごとに異なる操作をディレクトリに対して行うことを可能にしています。

備考:

宣言型オブジェクト設定は、他の人が行った変更が設定ファイルにマージされなかったとしても、それらの変更を保持します。 これは、replaceAPI操作のように、全てのオブジェクト設定を置き換えるわけではなく、patchAPI操作による、変更箇所のみの更新が可能にしています。

configディレクトリ配下にある全てのオブジェクト設定ファイルを処理し、作成、または現行オブジェクトへのパッチを行います。 まず、diffでどのような変更が行われるかを確認した後に適用します:

kubectl diff -f configs/
kubectl apply -f configs/

再帰的にディレクトリを処理します:

kubectl diff -R -f configs/
kubectl apply -R -f configs/

トレードオフ

命令型オブジェクト設定手法に対する長所:

  • 現行オブジェクトに直接行われた変更が、それらが設定ファイルに反映されていなかったとしても、保持されます
  • 宣言型オブジェクト設定は、ディレクトリごとの処理をより良くサポートしており、自動的にオブジェクトごとに操作のタイプ(作成、パッチ、削除)を検出します

命令型オブジェクト設定手法に対する短所:

  • 宣言型オブジェクト設定は、デバッグ、そして想定外の結果が出たときに理解するのが困難です
  • 差分を利用した一部のみの更新は、複雑なマージ、パッチの操作が必要です

次の項目

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として標準化されています。

次の項目

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タイプは、それらのセレクターの妥当性とそれらが示す意味をドキュメントに記載するべきです。

備考:

ReplicaSetなど、いくつかのAPIタイプにおいて、2つのインスタンスのラベルセレクターは単一の名前空間において重複してはいけません。重複していると、コントローラーがそれらのラベルセレクターがコンフリクトした操作とみなし、どれだけの数のレプリカを稼働させるべきか決めることができなくなります。

注意:

等価ベース、集合ベースともに、論理OR (||) オペレーターは存在しません。フィルターステートメントが意図した通りになっていることを確認してください。

等価ベース(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選定 のドキュメントを参照してください。

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の管理ガイドドキュメントに記載されています。

備考:

プレフィックスkube-を持つNamespaceは、Kubernetesシステムの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

次の項目

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

次の項目

ラベルとセレクターについて学習してください。

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

備考:

フィールドセレクターは本質的にリソースの フィルター となります。デフォルトでは、セレクター/フィルターが指定されていない場合は、全てのタイプのリソースが取得されます。これは、kubectlクエリのkubectl get podskubectl get pods --field-selector ""が同じであることを意味します。

サポートされているフィールド

サポートされているフィールドセレクターは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.phaseRunningでなく、かつ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

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オブジェクトが誤って削除されるのを防ぐためのものです。 PodがPersistentVolumeオブジェクトを利用中の場合、Kubernetesはpv-protectionファイナライザーを追加します。 PersistentVolumeを削除しようとするとTerminatingステータスになりますが、ファイナライザーが存在しているためコントローラーはボリュームを削除することができません。 PodがPersistentVolumeの利用を停止するとKubernetesはpv-protectionファイナライザーを削除し、コントローラーがボリュームを削除します。

備考:

  • オブジェクトをDELETEすると、Kubernetesはそのオブジェクトに削除タイムスタンプを追加し、削除待ちとなったオブジェクトの.metadata.finalizersフィールドへの変更をただちに制限し始めます。 既存のファイナライザーを削除する(finalizersリストからエントリーを削除する)ことはできますが、新しいファイナライザーを追加することはできません。 また、いったん設定されたdeletionTimestampを変更することもできません。

  • 削除がリクエストされた後は、このオブジェクトを復活させることはできません。 唯一の方法は、削除して同様のオブジェクトを新しく作成することです。

備考:

カスタムファイナライザー名は、example.com/finalizer-nameのように公開された修飾されたファイナライザー名でなければなりません。 Kubernetesはこの形式を強制しており、いずれかのカスタムファイナライザーに対して修飾されたファイナライザー名を使用していない変更については、APIサーバーがオブジェクトへの書き込みを拒否します。

オーナーリファレンス、ラベル、ファイナライザー

ラベルのように、 オーナーリファレンスはKubernetesのオブジェクト間の関係性を説明しますが、利用される目的が異なります。 コントローラーがPodのようなオブジェクトを管理するとき、関連するオブジェクトのグループの変更を追跡するためにラベルを利用します。 例えば、JobがいくつかのPodを作成するとき、JobコントローラーはそれらのPodにラベルを付け、クラスター内の同じラベルを持つPodの変更を追跡します。

Jobコントローラーは、Podを作成したJobを指すオーナーリファレンスもそれらのPodに追加します。 Podが実行されているときにJobを削除すると、Kubernetesはオーナーリファレンス(ラベルではない)を使って、クリーンアップする必要のあるPodをクラスター内から探し出します。

また、Kubernetesは削除対象のリソースのオーナーリファレンスを認識して、ファイナライザーを処理します。

状況によっては、ファイナライザーが依存オブジェクトの削除をブロックしてしまい、対象のオーナーオブジェクトが完全に削除されず予想以上に長時間残ってしまうことがあります。 このような状況では、対象のオーナーと依存オブジェクトの、ファイナライザーとオーナーリファレンスを確認して問題を解決する必要があります。

備考:

オブジェクトが削除中の状態で詰まってしまった場合、削除を続行するために手動でファイナライザーを削除することは避けてください。 通常、ファイナライザーは理由があってリソースに追加されているものであるため、強制的に削除してしまうとクラスターで何らかの問題を引き起こすことがあります。 そのファイナライザーの目的を理解しており、かつ別の方法でそれを達成できる場合にのみ行うべきです(例えば、依存オブジェクトを手動でクリーンアップするなど)。

次の項目

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のアドミッションコントローラーはオーナーの削除権限に基づいて、ユーザーが従属リソースのこのフィールドを変更できるかを管理しています。 これにより、認証されていないユーザーがオーナーオブジェクトの削除を遅らせることを防ぎます。

備考:

名前空間をまたぐオーナーリファレンスは仕様により許可されていません。 名前空間付き従属オブジェクトには、クラスタースコープ、または名前空間付きのオーナーを指定することができます。名前空間付きオーナーは必ず従属オブジェクトと同じ名前空間に存在していなければなりません。 そうでない場合、オーナーリファレンスはないものとして扱われ、全てのオーナーがいなくなった時点で従属オブジェクトは削除対象となります。

クラスタースコープの従属オブジェクトはクラスタースコープのオーナーのみ指定できます。 v1.20以降では、クラスタースコープの従属オブジェクトが名前空間付きのオブジェクトをオーナーとした場合、 解決できないオーナーリファレンスを持っているものとして扱われ、ガベージコレクションの対象とすることができません。

v1.20以降で、ガベージコレクターが無効な名前空間またぎのownerReferenceや名前空間付きのオーナーに依存するクラスタースコープのオブジェクトなどを検知した場合、OwnerRefInvalidNamespaceを理由とした警告のEventを出し、involvedObjectで無効な従属オブジェクトを報告します。 kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespaceを実行することで、この種類のEventを確認することができます。

オーナーシップとファイナライザー

Kubernetesでリソースを削除するとき、APIサーバーはリソースを管理するコントローラーにファイナライザールールを処理させることができます。 ファイナライザーはクラスターが正しく機能するために必要なリソースを誤って削除してしまうことを防ぎます。 例えば、まだPodが使用中のPersistentVolumeを削除しようとするとき、PersistentVolumeが持っているkubernetes.io/pv-protectionファイナライザーにより、削除は即座には行われません。 その代わり、Kubernetesがファイナライザーを削除するまでボリュームはTerminatingステータスのまま残り、PersistentVolumeがPodにバインドされなくなった後で削除が行われます。

またKubernetesはフォアグラウンド、孤立したオブジェクトのカスケード削除を行ったとき、オーナーリソースにファイナライザーを追加します。 フォアグラウンド削除では、foregroundファイナライザーを追加し、オーナーを削除する前にコントローラーがownerReferences.blockOwnerDeletion=trueを持っている従属リソースを削除するようにします。 孤立したオブジェクトの削除を行う場合、Kubernetesはorphanファイナライザーを追加し、オーナーオブジェクトを削除した後にコントローラーが従属リソースを無視するようにします。

次の項目

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に関するより広範な情報が含まれていることに気づくでしょう。

1.3.11 - ストレージバージョン

Kubernetes APIサーバーは、etcd互換のバッキングストア(多くの場合、バッキングストレージはetcd自体です)にオブジェクトを保存します。 各オブジェクトは、それぞれのAPIタイプの特定のバージョンを使用してシリアライズされ、ConfigMapのv1表現などがその例です。 Kubernetesでは、クラスター内でのオブジェクトの保存方法を ストレージバージョン という用語で表現します。

Kubernetes APIは、自動的なバージョン変換にも対応しています。 たとえば、HorizontalPodAutoscalerがある場合、HorizontalPodAutoscaler APIのv1とv2のバージョンを任意に組み合わせて、そのHorizontalPodAutoscalerとやり取りできます。 Kubernetesは、実際にシリアライズされているバージョンがクライアントからは見えないようにするため、各API呼び出しを変換する役割を担っています。

クラスター管理者にとって、オブジェクトのストレージバージョンは理解しておくべき重要な概念です。 なぜなら、これは、オブジェクトのAPI表現をストレージバックエンドの実際のエンコーディングに結び付けるものであるためです。 これは、保存時の暗号化やAPIの非推奨化など、オブジェクトの基礎となるバイナリエンコーディングが問題となる場合に重要です。

同じAPIは複数のストレージバージョンを持つことができ、APIサーバーはそれらをオブジェクトスキーマに変換できます。 そのリソースの一部である単一のオブジェクトは、任意の時点で1つのストレージバージョンのみを持つ必要があります。 これは、APIサーバーがオブジェクトのバイナリエンコーディングを認識しており、保存されているすべてのバージョンをオブジェクトのAPI表現に動的に変換できることを意味します。

オブジェクトのバージョンは、ストレージバージョンとは完全に別のものです。 たとえば、同じリソースに対するv1alpha1v1beta1のAPIオブジェクトは、2つのオブジェクト間でストレージバージョンが更新されていない限り、ストレージ内では同じようにエンコードされます。

リソースへのストレージバージョンのマッピング

すべてのリソースは、任意の時点で1つのアクティブなストレージバージョンを持ちます。 つまり、オブジェクトへのあらゆる書き込みは、その特定のストレージバージョンでオブジェクトを保存します。 ただし、ストレージバージョンは更新できるため、オブジェクトは異なるバージョンで保存される可能性があります。 1つのオブジェクトは、任意の時点で1つのストレージバージョンでのみ保存されます。

APIサーバーからの読み取りは、保存されたデータをオブジェクトのAPI表現に変換します。 これにより、オブジェクトへの更新が発生しない限り、古いストレージバージョンが無期限に存在できます。 一方、書き込みは、更新時に保存されたオブジェクトを新しい表現に変換します。

カスタムリソースのストレージバージョン

カスタムリソースは動的に定義されるため、組み込みのKubernetesタイプとはストレージバージョンの扱いが異なります。 組み込みオブジェクトは通常、ストレージエンコーディングがAPIタイプとは別に定義されており、保存されたオブジェクトがハブとして機能します。 そのため、リソースの特定のバージョンは、オブジェクトスキーマ内のフィールドとして記録される以外、特に重要ではありません。

しかし、カスタムリソースの場合、リソースの特定のバージョンをストレージバージョンとして設定する必要があります。 そのカスタムリソースの特定のバージョンによって定義されたスキーマが、ストレージレイヤーでのリソースのエンコーディングとして使用されます。 APIのセットアップとバージョン管理の詳細については、CRDの高度な機能を参照してください。

たとえば、crontabs のこのCustomResourceDefinitionを参照してください:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  # このCustomResourceDefinitionでサポートされているバージョンのリスト
  versions:
  - name: v1beta1
    # 各バージョンは、Servedフラグで有効/無効にできます。
    served: true
    # 1つのバージョンのみをストレージバージョンとしてマークする必要があります。
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
          time:
            type: string
  conversion:
    strategy: None
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

v1beta1 API定義がストレージバージョンとして使用されます。 つまり、crontabsを更新または作成すると、v1beta1 APIのオブジェクトスキーマで保存されることを意味します。 この場合、実際にはv1 APIオブジェクトはtimeフィールドを保存できないことを意味します。 これは、ストレージ定義の一部ではないためです。 このスキーマは、オブジェクト自体のバイナリエンコーディングとしてストレージレイヤーで使用されます。 2つのバージョンを同時に保存バージョンとして設定しようとすることは無効と見なされます。 これは、2つのデータスキーマが同時にオブジェクトを保存する有効な方法と見なされることを意味するためです。

ストレージに使用されるバージョンを変更すると、新規作成または更新されるCRは、そのバージョンのAPIを使って保存されます。 オブジェクトの監視や取得によって、そのオブジェクトは使用中の状態になりますが、古いストレージバージョンから変換されるだけで、オブジェクト自体は変更されません。 オブジェクトを更新または作成した場合にのみ、新しく定義されたストレージバージョンが使用されます。

ストレージバージョンが保存時の暗号化に関連する理由

クラスターの保存時のストレージを暗号化するツールがあります。 特にクラスターのSecretに対して使用されます。 これにより、クラスターに実際に保存されているデータが暗号化されるため、データ流出に対する追加の保護層が提供されます。 つまり、APIサーバーは、ストレージからデータを取得する際に、実際にデータを復号しています。 APIサーバーは、オブジェクトを適切にデコードするために、そのストレージバージョンの鍵を持っている必要があります。

この場合、ストレージバージョンは、オブジェクトのバイナリエンコーディング形式だけを指すわけではありません。 保存されたデータが何らかの方法でAPIオブジェクトに変換できる限り、それをストレージバージョンとして扱えます。

異なるストレージバージョンへの移行

単一のリソースに対して複数のストレージバージョンが存在すると、クラスター管理者にとって問題が生じる可能性があります。 クラスター管理者は、すべてのオブジェクトが古いストレージバージョンを使用していないことを確認するまで、CRDのサポートされていない古いバージョンのAPIを削除できない場合があります。 多数のオブジェクトが存在し、どれが新しくどれがまだ古いストレージバージョンに基づいているかが不透明な場合、あるバージョンを安全に削除できるタイミングを判断するのは困難です。 バージョンを早まって削除すると、オブジェクトを完全に読み取れなくなる可能性があります。

もう1つの重要な問題は、上記のセクションで定義されている暗号化鍵の使用です。 リソースはストレージバージョンを更新するために実際に使用されている必要があるため、鍵のローテーションが行われると、すべてのオブジェクトが少なくとも1回書き込まれたことを管理者が確認するまで、古い暗号化鍵と新しい暗号化鍵の両方を使用し続ける必要があります。 これは、それまで鍵を完全に使用停止できないため、セキュリティリスクとユーザビリティの問題の両方をもたらします。

手動操作なしですべてのオブジェクトが新しいストレージバージョンを使用するように移行を実行する方法の例については、ストレージバージョンの移行を参照してください。

1.4 - kubectlコマンドラインツール

kubectlは、Kubernetesクラスターと通信するための主要なコマンドラインツールです。 このページでは、kubectlとKubernetesエコシステムにおけるその役割について概要を説明します。
Kubernetes APIを使用してKubernetesクラスターのコントロールプレーンと通信するためのコマンドラインツールです。

kubectlツールは、Kubernetes APIを通じてクラスターと通信します。 設定については、kubectl$HOME/.kubeディレクトリ内のconfigという名前のファイルを探します。 KUBECONFIG環境変数を設定するか、--kubeconfigフラグを設定することで、他のkubeconfigファイルを指定できます。

kubectlの役割

kubectlツールは、Kubernetesオブジェクトの作成、検査、更新、削除を行うための主要なインターフェースです。 クラスター内部で実行されるKubernetesコンポーネントと、それらのコンポーネントが実装するKubernetes APIを補完するものです。 ノートパソコンからkubectlを実行する場合でも、クラスター内のPodから実行する場合でも、APIサーバーにリクエストを送信します。 クライアントライブラリHeadlampのようなWebダッシュボードなど、他のクライアントも同じAPIを通じて通信します。

kubectlの仕組み

kubectlツールは、kubeconfigファイルで定義されたクラスター、ユーザー、およびコンテキストを使用してAPIサーバーに接続し、認証を行います。 クラスターの外部からkubectlを実行する場合、kubeconfigファイルを使用してAPIサーバーのアドレスと認証情報を見つけます。 kubectlがPod内(例えばCI/CDパイプライン内)で実行される場合、PodにマウントされたServiceAccountトークンに基づくクラスター内認証を使用できます。

コマンドを実行すると、kubectlはその意図をKubernetes APIへの1つ以上のHTTPリクエストに変換します。 APIサーバーは各リクエストを検証し、etcdに保存されたクラスターの状態に適用し、結果を返します。 これは、Deploymentの作成であってもログの読み取りであっても、すべてのkubectlのアクションが同じAPIドリブンなパスを辿ることを意味します。

kubeconfigでは複数のクラスター、ユーザー、コンテキストを定義できるため、環境を再設定することなくkubectlを使用してクラスター間を切り替えることができます。 kubectl config use-contextを実行してアクティブなコンテキストを変更してください。

kubectlでできること

kubectlツールは多くの操作をサポートしており、以下の大まかなカテゴリに分類されます:

  • リソースの管理 – Pod、Deployment、Serviceなどのオブジェクトを作成、更新、削除します。 設定ファイルによる宣言的な管理にはkubectl applyを使用します。
  • クラスターの状態の検査 – オブジェクトの一覧表示と詳細表示、イベントの確認、リソース使用量のチェックを行います。
  • デバッグ – コンテナからのログの確認、実行中のコンテナ内でのコマンドの実行、またはPodへのポートフォワードを行います。
  • クラスター操作 – メンテナンスのためにノードをドレインし、新しいワークロードを防ぐためにノードをcordonし、クラスターの設定を管理します。
  • スクリプトと自動化 – スクリプトやパイプラインで使用するために、出力をJSON、YAML、またはJSONPathを使用したカスタムカラム形式としてフォーマットします。

構文、コマンドリファレンス、および例については、kubectlリファレンスドキュメントを参照してください。

宣言的管理と命令的管理

本番ワークロードでは、バージョン管理された設定ファイルとともにkubectl applyを使用した宣言的なオブジェクト管理を推奨します。 宣言的管理は、変更の追跡、コラボレーション、GitOpsワークフローとの統合に役立ちます。 命令的なコマンド(kubectl createkubectl runなど)は開発や実験には便利ですが、再現や監査が困難です。

プラグインによるkubectlの拡張

新しいサブコマンドを追加するプラグインkubectlを拡張できます。 プラグインは、kubectl-<plugin-name>の命名規則に従うスタンドアロンのバイナリです。 Kubernetesコミュニティは多くのプラグインをメンテナンスしており、Krewプラグインマネージャーで管理できます。

バージョン互換性

kubectlツールは、クラスターのコントロールプレーンに対して前後1マイナーバージョンのバージョンスキューをサポートしています。 例えば、kubectl v1.32はv1.31、v1.32、v1.33のコントロールプレーンで動作します。 互換性のあるバージョンを使用することで、予期しない動作を回避できます。 詳細については、バージョンスキューポリシーを参照してください。

次の項目

2 - クラスターのアーキテクチャ

Kubernetesの背後にあるアーキテクチャのコンセプト。

Kubernetesクラスターは、コントロールプレーンと、コンテナ化されたアプリケーションを実行するワーカーマシン群(ノードと呼ばれます)で構成されます。 各クラスターには、Podを実行するために少なくとも1つのワーカーノードが必要です。

ワーカーノードは、アプリケーションのワークロードを構成するPodをホストします。 コントロールプレーンは、クラスター内のワーカーノードとPodを管理します。 本番環境では、コントロールプレーンは通常複数のコンピューターにまたがって実行され、クラスターは通常複数のノードを実行することで、耐障害性と高可用性を提供します。

このドキュメントでは、完全に機能するKubernetesクラスターに必要なさまざまなコンポーネントの概要を説明します。

コントロールプレーン(kube-apiserver、etcd、kube-controller-manager、kube-scheduler)と複数のノード。各ノードではkubeletとkube-proxyが実行されています。

図1. Kubernetesクラスターのコンポーネント。

このアーキテクチャについて

図1は、Kubernetesクラスターのリファレンスアーキテクチャの一例を示しています。 コンポーネントの実際の配置は、具体的なクラスターの構成や要件によって異なる場合があります。

この図では、各ノードでkube-proxyコンポーネントが実行されています。 Service APIと関連する動作をクラスターネットワーク上で利用可能にするためには、各ノードにネットワークプロキシコンポーネントが必要です。 ただし、一部のネットワークプラグインは、独自のサードパーティ製プロキシ実装を提供しています。 そのようなネットワークプラグインを使用する場合、ノードでkube-proxyを実行する必要はありません。

コントロールプレーンコンポーネント

コントロールプレーンのコンポーネントは、クラスターに関する全体的な判断(たとえばスケジューリングなど)を行うほか、クラスターのイベントの検知と対応(たとえば、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

コントロールプレーン上で動作するコンポーネントで、複数のコントローラープロセスを実行します。

論理的には、各コントローラーは個別のプロセスですが、複雑さを減らすために一つの実行ファイルにまとめてコンパイルされ、単一のプロセスとして動きます。

コントローラーにはさまざまな種類があります。 いくつか例を挙げます:

  • Nodeコントローラー: ノードがダウンした際に、それを検知して対応する役割を担います。
  • Jobコントローラー: 一度限りのタスクを表すJobオブジェクトを監視し、それらのタスクを完了まで実行するためのPodを作成します。
  • EndpointSliceコントローラー: (ServiceとPodの間のリンクを提供するために)EndpointSliceオブジェクトを作成します。
  • ServiceAccountコントローラー: 新しい名前空間に対してデフォルトのServiceAccountを作成します。

上記はすべてを網羅したリストではありません。

cloud-controller-manager

クラウド特有の制御ロジックを組み込むKubernetesのコントロールプレーンコンポーネントです。 クラウドコントロールマネージャーは、クラスターをクラウドプロバイダーAPIとリンクし、クラスターのみで相互作用するコンポーネントからクラウドプラットフォームで相互作用するコンポーネントを分離します。

cloud-controller-managerは、利用しているクラウドプロバイダーに固有のコントローラーのみを実行します。 オンプレミス環境でKubernetesを実行している場合や、自分のPC内の学習環境で実行している場合、クラスターにcloud-controller-managerは存在しません。

kube-controller-managerと同様に、cloud-controller-managerは、論理的に独立した複数の制御ループを、単一のプロセスとして実行する1つのバイナリにまとめています。 パフォーマンスの向上や障害への耐性を高めるために、水平方向にスケール(複数のコピーを実行)できます。

次のコントローラーは、クラウドプロバイダーに依存する可能性があります。

  • Nodeコントローラー: ノードが応答を停止した後、そのノードがクラウド上で削除されたかどうかを判断するためにクラウドプロバイダーを確認します。
  • Routeコントローラー: 基盤となるクラウドインフラ内でルートを設定します。
  • Serviceコントローラー: クラウドプロバイダーのロードバランサーの作成、更新、削除を行います。

Nodeコンポーネント

ノードコンポーネントはすべてのノード上で実行され、実行中のPodの維持やKubernetesの実行環境の提供を行います。

kubelet

クラスター内の各ノード上で実行されるエージェント。 コンテナPod内で実行されていることを保証します。

kubeletは、さまざまなメカニズムを通じて提供される一連のPodSpecを受け取り、そのPodSpecに記述されたコンテナが実行され、正常であることを保証します。 kubeletは、Kubernetesによって作成されていないコンテナを管理しません。

kube-proxy(オプション)

kube-proxyはクラスター内の各nodeで動作しているネットワークプロキシで、KubernetesのServiceコンセプトの一部を実装しています。

kube-proxyは、Nodeのネットワークルールをメンテナンスします。これらのネットワークルールにより、クラスターの内部または外部のネットワークセッションからPodへのネットワーク通信が可能になります。

kube-proxyは、オペレーティングシステムにパケットフィルタリング層があり、かつ使用可能な場合、パケットフィルタリング層を使用します。それ以外の場合は自身でトラフィックを転送します。

Serviceに対するパケット転送を自身で実装し、kube-proxyと同等の動作を提供するネットワークプラグインを使用している場合、クラスター内のノードでkube-proxyを実行する必要はありません。

コンテナランタイム

Kubernetesがコンテナを効果的に実行するために不可欠なコンポーネントです。 Kubernetes環境においてコンテナの実行とライフサイクルの管理を担います。

KubernetesはcontainerdCRI-Oなどのコンテナランタイムと、Kubernetes CRI (Container Runtime Interface)のすべての実装をサポートします。

アドオン

アドオンは、Kubernetesのリソース(DaemonSetDeploymentなど)を使用してクラスターの機能を実装します。 これらはクラスターレベルの機能を提供するため、アドオンの名前空間に属するリソースはkube-system名前空間に配置されます。

いくつかのアドオンについて以下で説明します。 利用可能なアドオンのより詳細な一覧については、アドオンを参照してください。

DNS

他のアドオンは厳密には必須ではありませんが、多くの例がクラスターDNSに依存しているため、すべてのKubernetesクラスターはクラスターDNSを備えるべきです。

クラスターDNSは、環境内にある他のDNSサーバーに加えて稼働するDNSサーバーであり、KubernetesのService用のDNSレコードを提供します。

Kubernetesによって起動されたコンテナは、自動的にこのDNSサーバーをDNS検索に含めます。

Web UI(Dashboard)

Dashboardは、Kubernetesクラスターのための汎用的なWebベースのUIです。 ユーザーはこれを使って、クラスター内で稼働しているアプリケーションやクラスター自体の管理やトラブルシューティングを行えます。

コンテナリソースの監視

コンテナリソースの監視は、コンテナに関する一般的な時系列メトリクスを中央のデータベースに記録し、そのデータを閲覧するためのUIを提供します。

クラスターレベルのロギング

クラスターレベルのロギングの仕組みは、コンテナのログを検索・閲覧用のインターフェースを備えた中央のログストアに保存する役割を担います。

ネットワークプラグイン

ネットワークプラグインは、Container Network Interface(CNI)仕様を実装するソフトウェアコンポーネントです。 PodへのIPアドレスの割り当てや、クラスター内でPod同士が通信できるようにする役割を担います。

アーキテクチャのバリエーション

Kubernetesの中核となるコンポーネントは一貫していますが、それらがデプロイされ管理される方法はさまざまです。 これらのバリエーションを理解することは、特定の運用上のニーズを満たすKubernetesクラスターを設計し維持するうえで非常に重要です。

コントロールプレーンのデプロイオプション

コントロールプレーンコンポーネントは、いくつかの方法でデプロイできます。

従来のデプロイ
コントロールプレーンコンポーネントは専用のマシンやVM上で直接実行され、多くの場合systemdのサービスとして管理されます。
static Pod
コントロールプレーンコンポーネントはstatic Podとしてデプロイされ、特定のノード上のkubeletによって管理されます。 これはkubeadmのようなツールで使われる一般的なアプローチです。
セルフホスト
コントロールプレーンはKubernetesクラスター自体の内部でPodとして実行され、DeploymentやStatefulSet、その他のKubernetesのプリミティブによって管理されます。
マネージドKubernetesサービス
クラウドプロバイダーは多くの場合コントロールプレーンを抽象化し、そのコンポーネントを自身のサービス提供の一部として管理します。

ワークロード配置に関する考慮事項

コントロールプレーンコンポーネントを含むワークロードの配置は、クラスターのサイズ、パフォーマンス要件、運用ポリシーによって異なる場合があります:

  • より小規模なクラスターや開発用のクラスターでは、コントロールプレーンコンポーネントとユーザーのワークロードが同じノード上で実行されることがあります。
  • より大規模な本番クラスターでは、多くの場合特定のノードをコントロールプレーンコンポーネント専用にし、ユーザーのワークロードから分離します。
  • 一部の組織では、重要なアドオンや監視ツールをコントロールプレーンのノード上で実行します。

クラスター管理ツール

kubeadm、kops、Kubesprayといったツールは、クラスターのデプロイと管理に対してそれぞれ異なるアプローチを提供しており、コンポーネントの配置や管理の方法もそれぞれ独自のものです。

カスタマイズと拡張性

Kubernetesのアーキテクチャは、大幅なカスタマイズを可能にします。

  • カスタムスケジューラーをデプロイして、デフォルトのKubernetesスケジューラーと並行して動作させたり、完全に置き換えたりできます。
  • APIサーバーは、CustomResourceDefinitionやAPIアグリゲーションによって拡張できます。
  • クラウドプロバイダーは、cloud-controller-managerを使用してKubernetesと深く統合できます。

Kubernetesのアーキテクチャの柔軟性により、組織は運用の複雑さ、パフォーマンス、管理のオーバーヘッドといった要素のバランスを取りながら、クラスターを特定のニーズに合わせて調整できます。

次の項目

以下についてさらに学べます。

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を実行する資格があります。それ以外の場合、該当ノードが有効になるまではいかなるクラスターの活動に対しても無視されます。

備考:

Kubernetesは無効なNodeのオブジェクトを保持し、それが有効になるまで検証を続けます。

ヘルスチェックを止めるためには、あなた、もしくはコントローラーが明示的にNodeを削除する必要があります。

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 $ノード名

備考:

DaemonSetによって作成されたPodはノード上のunschedulable属性を考慮しません。 これは、再起動の準備中にアプリケーションからアプリケーションが削除されている場合でも、DaemonSetがマシンに属していることを前提としているためです。

ノードのステータス

ノードのステータスは以下の情報を含みます:

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です。

備考:

コマンドラインを使用してcordonされたNodeを表示する場合、ConditionはSchedulingDisabledを含みます。 SchedulingDisabledはKubernetesのAPIにおけるConditionではありません;その代わり、cordonされたノードはUnschedulableとしてマークされます。

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の制御外で実行されているプロセスは含みません。

備考:

Pod以外のプロセス用にリソースを明示的に予約したい場合は、Systemデーモン用にリソースを予約を参照してください。

ノードのトポロジー

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秒は重要なポッドを終了させるために確保されることになります。

備考:

Graceful Node Shutdown中にPodが退避された場合、それらのPodの.statusFailedになります。 kubectl get podsを実行すると、退避させられたPodのステータスが Shutdown と表示されます。 また、kubectl describe podを実行すると、ノードのシャットダウンのためにPodが退避されたことがわかります。

Status:         Failed
Reason:         Shutdown
Message:        Node is shutting, evicting pods

失敗したポッドオブジェクトは、明示的に削除されるか、GCによってクリーンアップされるまで保存されます。 これは、ノードが突然終了した場合とは異なった振る舞いです。

ノードの非正常終了

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に対して即座にボリュームデタッチ操作を行う。

備考:

  • node.kubernetes.io/out-of-service taintを付与する前に、ノードがシャットダウンしているか電源がオフになっていることを確認してください(再起動中ではないこと)。
  • Podの別ノードへの移動後、シャットダウンしていたノードが回復した場合は、ユーザーが手動で付与したout-of-service taintをユーザー自ら手動で削除する必要があります。

スワップメモリの管理

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をご覧いただき、テストにご協力、ご意見をお聞かせください。

次の項目

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、またはサービス宛てのすべてのトラフィックをトンネル経由で渡します。 このトンネルにより、ノードが稼働するネットワークの外部にトラフィックが公開されないようになります。

備考:

SSHトンネルは現在非推奨であるため、自分が何をしているのか理解していないのであれば、使用すべきではありません。この通信経路の代替となるものとして、Konnectivityサービスがあります。

Konnectivityサービス

FEATURE STATE: Kubernetes v1.18 (Beta)

SSHトンネルの代替として、Konnectivityサービスは、コントロールプレーンからクラスターへの通信に、TCPレベルのプロキシを提供します。Konnectivityサービスは、コントロールプレーンネットワークのKonnectivityサーバーと、ノードネットワークのKonnectivityエージェントの、2つの部分で構成されています。 Konnectivityエージェントは、Konnectivityサーバーへの接続を開始し、ネットワーク接続を維持します。 Konnectivityサービスを有効にすると、コントロールプレーンからノードへのトラフィックは、すべてこの接続を経由するようになります。

Konnectivityサービスのセットアップに従って、クラスターにKonnectivityサービスをセットアップしてください。

次の項目

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名前空間の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
apiserver-c4vwjftbvpc5os2vvzle4qg27a   kube-apiserver-c4vwjftbvpc5os2vvzle4qg27a_9cbf54e5-1136-44bd-8f9a-1dcd15c346b4   5m33s
apiserver-dz2dqprdpsgnm756t5rnov7yka   kube-apiserver-dz2dqprdpsgnm756t5rnov7yka_84f2a85d-37c1-4b14-b6b9-603e62e4896f   4m23s
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によってガベージコレクションされます。

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コントローラーは、自分が制御するリソースに関連するリソースにのみ注意を払うように作られています。

たとえば、DeploymentとJobがありますが、これらは両方ともPodを作成するものです。しかし、JobコントローラーはDeploymentが作成したPodを削除することはありません。各コントローラーが2つのPodを区別できる情報(ラベル)が存在するためです。

コントローラーを実行する方法

Kubernetesには、kube-controller-manager内部で動作する一組のビルトインのコントローラーが用意されています。これらビルトインのコントローラーは、コアとなる重要な振る舞いを提供します。

DeploymentコントローラーとJobコントローラーは、Kubernetes自体の一部として同梱されているコントローラーの例です(それゆえ「ビルトイン」のコントローラーと呼ばれます)。Kubernetesは回復性のあるコントロールプレーンを実行できるようにしているため、ビルトインのコントローラーの一部が故障しても、コントロールプレーンの別の部分が作業を引き継いでくれます。

Kubernetesを拡張するためにコントロールプレーンの外で動作するコントローラーもあります。もし望むなら、新しいコントローラーを自分で書くこともできます。自作のコントローラーをPodセットとして動作させたり、Kubernetesの外部で動作させることもできます。どのような動作方法が最も適しているかは、そのコントローラーがどのようなことを行うのかに依存します。

次の項目

2.5 - クラウドコントローラーマネージャー

FEATURE STATE: Kubernetes v1.11 (Beta)

クラウドインフラストラクチャ技術により、パブリック、プライベート、ハイブリッドクラウド上でKubernetesを動かすことができます。Kubernetesは、コンポーネント間の密なつながりが不要な自動化されたAPI駆動インフラストラクチャを信条としています。

cloud-controller-managerは クラウド特有の制御ロジックを組み込むKubernetesのコントロールプレーンコンポーネントです。 クラウドコントロールマネージャーは、クラスターをクラウドプロバイダーAPIとリンクし、クラスターのみで相互作用するコンポーネントからクラウドプラットフォームで相互作用するコンポーネントを分離します。

Kubernetesと基盤となるクラウドインフラストラクチャ間の相互運用ロジックを分離することで、クラウドプロバイダーはcloud-controller-managerコンポーネントにより、メインのKubernetesプロジェクトとは異なるペースで機能をリリースできるようになります。

cloud-controller-managerは、プラグイン機構を用い、異なるクラウドプロバイダーに対してそれぞれのプラットフォームとKubernetesの結合を可能にする構成になっています。

設計

Kubernetesのコンポーネント

クラウドコントローラーマネージャーは、複製されたプロセスの集合としてコントロールプレーンで実行されます(通常、Pod内のコンテナとなります)。各cloud-controller-managerは、シングルプロセスで複数のコントローラーを実装します。

備考:

コントロールプレーンの一部ではなく、Kubernetesのアドオンとしてクラウドコントローラーマネージャーを実行することもできます。

クラウドコントローラーマネージャーの機能

クラウドコントローラーマネージャーのコントローラーは以下を含んでいます。

ノードコントローラー

ノードコントローラーは、クラウドインフラストラクチャで新しいサーバーが作成された際に、Nodeオブジェクトを作成する責務を持ちます。ノードコントローラーは、クラウドプロバイダーのテナント内で動作しているホストの情報を取得します。ノードコントローラーは下記に示す機能を実行します:

  1. Nodeオブジェクトを、コントローラーがクラウドプロバイダーAPIを通じて見つけた各サーバーで初期化する。
  2. Nodeオブジェクトに、ノードがデプロイされているリージョンや利用可能なリソース(CPU、メモリなど)のようなクラウド特有な情報を注釈付けやラベル付けをする。
  3. ノードのホスト名とネットワークアドレスを取得する。
  4. ノードの正常性を検証する。ノードが応答しなくなった場合、クラウドプロバイダーのAPIを利用しサーバーがdeactivated / deleted / terminatedであるかを確認する。クラウドからノードが削除されていた場合、KubernetesクラスターからNodeオブジェクトを削除する。

いくつかのクラウドプロバイダーは、これをノードコントローラーと個別のノードライフサイクルコントローラーに分けて実装しています。

ルートコントローラー

ルートコントローラーは、クラスター内の異なるノード上で稼働しているコンテナが相互に通信できるように、クラウド内のルートを適切に設定する責務を持ちます。

クラウドプロバイダーによっては、ルートコントローラーはPodネットワークのIPアドレスのブロックを割り当てることもあります。

サービスコントローラー

Serviceは、マネージドロードバランサー、IPアドレスネットワークパケットフィルターや対象のヘルスチェックのようなクラウドインフラストラクチャコンポーネントとの統合を行います。サービスコントローラーは、ロードバランサーや他のインフラストラクチャコンポーネントを必要とするServiceリソースを宣言する際にそれらのコンポーネントを設定するため、クラウドプロバイダーのAPIと対話します。

認可

このセクションでは、クラウドコントローラーマネージャーが操作を行うために様々なAPIオブジェクトに必要な権限を分類します。

ノードコントローラー

ノードコントローラーはNodeオブジェクトのみに対して働きます。Nodeオブジェクトに対して、readとmodifyの全権限が必要です。

v1/Node:

  • get
  • list
  • create
  • update
  • patch
  • watch
  • delete

ルートコントローラー

ルートコントローラーは、Nodeオブジェクトの作成を待ち受け、ルートを適切に設定します。Nodeオブジェクトについて、get権限が必要です。

v1/Node:

  • get

サービスコントローラー

サービスコントローラーは、Serviceオブジェクトのcreateupdatedeleteイベントを待ち受け、その後、サービスのロードバランサーを適切に設定します。

Serviceにアクセスするため、listwatchの権限が必要です。Serviceを更新するため、statusサブリソースへのpatchupdateの権限が必要です。

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
  - watch
- apiGroups:
  - ""
  resources:
  - services/status
  verbs:
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - serviceaccounts
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list
  - update
  - watch

次の項目

  • クラウドコントローラーマネージャーの運用管理はクラウドコントローラーマネージャーの実行と管理を説明しています。

  • クラウドコントローラーマネージャーを使用するためにHAコントロールプレーンをアップグレードするには、複製されたコントロールプレーンをクラウドコントローラーマネージャーを使用するために移行するを参照してください。

  • どのようにあなた自身のクラウドコントローラーマネージャーが実装されるのか、もしくは既存プロジェクトの拡張について知りたいですか?

    • クラウドコントローラーマネージャーは、いかなるクラウドからもプラグインとしての実装を許可するためにGoインターフェースを使います。具体的には、kubernetes/cloud-providercloud.goで定義されているCloudProviderを使います。

    • 本ドキュメントでハイライトした共有コントローラー(Node、Route、Service)の実装と共有クラウドプロバイダーインターフェースに沿ったいくつかの足場は、Kubernetesコアの一部です。クラウドプロバイダーに特化した実装は、Kubernetesのコアの外部として、またCloudProviderインターフェースを実装します。

    • プラグイン開発についての詳細な情報は、クラウドコントローラーマネージャーの開発を見てください。

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 (GA)

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と出力されます。

次の項目

2.7 - Kubernetesの自己修復機能

Kubernetesは、ワークロードの健全性と可用性を維持するための自己修復機能を備えています。 ノードが使用不能になった際にはワークロードを再スケジュールし、障害の発生したコンテナを自動的に置き換え、システムの望ましい状態が維持されるようにします。

自己修復機能

  • コンテナレベルの再起動: Pod内のコンテナが失敗した場合、KubernetesはrestartPolicyに基づいてコンテナを再起動します。

  • レプリカの置換: DeploymentStatefulSetに属するPodが失敗した場合、Kubernetesは指定されたレプリカ数を維持するために代替のPodを作成します。DaemonSetに属するPodが失敗した場合、コントロールプレーンが同じノード上で実行するための代替Podを作成します。

  • 永続ストレージの復旧: PersistentVolume(PV)をアタッチしたPodが実行されているノードが障害を起こした場合、Kubernetesはそのボリュームを別のノード上の新しいPodに再アタッチできます。

  • Serviceにおける負荷分散: Serviceの背後にあるPodが失敗した場合、KubernetesはそのPodをServiceのエンドポイントから自動的に除外し、正常なPodのみにトラフィックをルーティングします。

Kubernetesの自己修復を実現する主なコンポーネントには、次のようなものがあります:

  • kubelet: コンテナが実行されていることを確認し、失敗したコンテナを再起動します。

  • ReplicaSet、StatefulSet、DaemonSetコントローラー: Podの望ましいレプリカ数を維持します。

  • PersistentVolumeコントローラー: ステートフルなワークロードに対してボリュームのアタッチおよびデタッチを管理します。

考慮事項

  • ストレージの障害: 永続ボリュームが利用不能になった場合、復旧のための手順が必要になることがあります。

  • アプリケーションのエラー: Kubernetesはコンテナを再起動できますが、アプリケーション内部の問題は別途対処する必要があります。

次の項目

2.8 - コンテナランタイムインターフェース(CRI)

CRIは、クラスターコンポーネントを再コンパイルすることなく、kubeletがさまざまなコンテナランタイムを使用できるようにするプラグインインターフェースです。

kubeletPodとそのコンテナを起動できるように、クラスター内の各ノードで動作するcontainer runtimeが必要です。

kubeletとContainerRuntime間の通信のメインプロトコルです。

Kubernetes Container Runtime Interface(CRI)は、クラスターコンポーネントkubeletcontainer runtime間の通信用のメインgRPCプロトコルを定義します。

API

FEATURE STATE: Kubernetes v1.23 (GA)

kubeletは、gRPCを介してコンテナランタイムに接続するときにクライアントとして機能します。ランタイムおよびイメージサービスエンドポイントは、コンテナランタイムで使用可能である必要があります。コンテナランタイムは、--image-service-endpointコマンドラインフラグを使用して、kubelet内で個別に設定できます。

Kubernetes v1.36の場合、kubeletはCRI v1の使用を優先します。 コンテナランタイムがCRIのv1をサポートしていない場合、kubeletはサポートされている古いバージョンのネゴシエーションを試みます。 kubelet v1.36はCRI v1alpha2をネゴシエートすることもできますが、このバージョンは非推奨と見なされます。 kubeletがサポートされているCRIバージョンをネゴシエートできない場合、kubeletはあきらめて、ノードとして登録されません。

アップグレード

Kubernetesをアップグレードする場合、kubeletはコンポーネントの再起動時に最新のCRIバージョンを自動的に選択しようとします。 それが失敗した場合、フォールバックは上記のように行われます。 コンテナランタイムがアップグレードされたためにgRPCリダイヤルが必要な場合は、コンテナランタイムも最初に選択されたバージョンをサポートする必要があります。 そうでない場合、リダイヤルは失敗することが予想されます。これには、kubeletの再起動が必要です。

次の項目

2.9 - ガベージコレクション

ガベージコレクションは、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のさまざまな部分が制御していないオブジェクトへの干渉を回避するのに役立ちます。

備考:

namespace間のowner referenceは、設計上許可されていません。 namespaceの依存関係は、クラスタースコープまたはnamespaceのオーナーを指定できます。 namespaceのオーナーは、依存関係と同じnamespaceに存在する必要があります。 そうでない場合、owner referenceは不在として扱われ、すべてのオーナーが不在であることが確認されると、依存関係は削除される可能性があります。

クラスタースコープの依存関係は、クラスタースコープのオーナーのみを指定できます。 v1.20以降では、クラスタースコープの依存関係がnamespaceを持つkindをオーナーとして指定している場合、それは解決できないowner referenceを持つものとして扱われ、ガベージコレクションを行うことはできません。

V1.20以降では、ガベージコレクタは無効な名前空間間のownerReference、またはnamespaceのkindを参照するownerReferenceをもつクラスター・スコープの依存関係を検出した場合、無効な依存関係のOwnerRefInvalidNamespaceinvolvedObjectを理由とする警告イベントが報告されます。 以下のコマンドを実行すると、そのようなイベントを確認できます。 kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespace

カスケード削除

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より古くなると削除されます。

備考:

kubeletがガベージコレクションするのは、自分が管理するコンテナのみです。

ガベージコレクションの設定

これらのリソースを管理するコントローラーに固有のオプションを設定することにより、リソースのガベージコレクションを調整できます。次のページは、ガベージコレクションを設定する方法を示しています。

次の項目

2.10 - Mixed Version Proxy

FEATURE STATE: Kubernetes v1.36 (Beta; (デフォルトで有効))

Kubernetes 1.36には、APIサーバーが他の ピア APIサーバーにリソースリクエストをプロキシできるようにするアルファ機能が含まれています。 また、クライアントがディスカバリを通じてクラスター全体で提供されるリソースの全体像を取得することもできます。 これは、1つのクラスター内で異なるバージョンのKubernetesを実行する複数のAPIサーバーがある場合に役立ちます(例えば、Kubernetesの新しいリリースへの長期間にわたるロールアウト中など)。

これにより、クラスター管理者は、より安全にアップグレードできる高可用性クラスターを設定できます:

  1. 重要なタスクでリソースの包括的なリストを表示するためにディスカバリに依存するコントローラーが、常にすべてのリソースの完全なビューを取得できるようにします。 この完全なクラスター全体のディスカバリを ピア集約ディスカバリ と呼びます。
  2. (アップグレード中に行われる)リソースリクエストを正しいkube-apiserverに転送します。 このプロキシにより、ユーザーはアップグレードプロセスに起因する予期しない404 Not Foundエラーを見ることがなくなります。 このメカニズムは Mixed Version Proxy と呼ばれます。

ピア集約ディスカバリとMixed Version Proxyの有効化

APIサーバーを起動する際に、UnknownVersionInteroperabilityProxyフィーチャーゲートが有効になっていることを確認してください:

kube-apiserver \
--feature-gates=UnknownVersionInteroperabilityProxy=true \
# この機能に必要なコマンドライン引数
--peer-ca-file=<kube-apiserver CA証明書へのパス>
--proxy-client-cert-file=<アグリゲータープロキシ証明書へのパス>,
--proxy-client-key-file=<アグリゲータープロキシ鍵へのパス>,
--requestheader-client-ca-file=<アグリゲーターCA証明書へのパス>,
# requestheader-allowed-namesは、任意のCommon Nameを許可するために空白に設定できます
--requestheader-allowed-names=<プロキシクライアント証明書を検証するための有効なCommon Names>,

# この機能のオプションフラグ
--peer-advertise-ip=`ピアがリクエストをプロキシするために使用するこのkube-apiserverのIP`
--peer-advertise-port=`ピアがリクエストをプロキシするために使用するこのkube-apiserverのポート`

# …その他のフラグは通常通り

APIサーバー間のプロキシトランスポートと認証

  • ソースkube-apiserverは、既存のAPIサーバークライアント認証フラグ--proxy-client-cert-file--proxy-client-key-fileを再利用して、ピア(宛先kube-apiserver)によって検証される自身のIDを提示します。 宛先APIサーバーは、--requestheader-client-ca-fileコマンドライン引数を使用して指定した設定に基づいてピア接続を検証します。

  • 宛先サーバーのサービング証明書を認証するには、ソースAPIサーバーに--peer-ca-fileコマンドライン引数を使用して認証局バンドルを設定する必要があります。

ピアAPIサーバー接続の設定

ピアがリクエストをプロキシするために使用するkube-apiserverのネットワークロケーションを設定するには、kube-apiserverに--peer-advertise-ip--peer-advertise-portコマンドライン引数を使用するか、APIサーバー設定ファイルでこれらのフィールドを指定します。 これらのフラグが指定されていない場合、ピアはkube-apiserverの--advertise-addressまたは--bind-addressコマンドライン引数の値を使用します。 それらも設定されていない場合、ホストのデフォルトインターフェースが使用されます。

ピア集約ディスカバリ

この機能を有効にすると、ディスカバリリクエストはデフォルトで包括的なディスカバリドキュメント(クラスター内のすべてのapiserverによって提供されるすべてのリソースをリストする)を提供するように自動的に有効になります。

ピア集約されていないディスカバリドキュメントをリクエストしたい場合は、ディスカバリリクエストに次のAcceptヘッダーを追加することでそれを示すことができます:

application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList;profile=nopeer

備考:

ピア集約ディスカバリは、/apisエンドポイントへの集約ディスカバリリクエストにのみサポートされており、非集約(レガシー)ディスカバリリクエストにはサポートされていません。

Mixed version proxying

Mixed version proxyingを有効にすると、アグリゲーションレイヤーは次の処理を行う特別なフィルターをロードします:

  • APIサーバーがそのAPIを提供できない場合(APIが導入される前のバージョンであるか、そのAPIサーバーでAPIが無効になっているため)、リソースリクエストがAPIサーバーに到達すると、そのAPIサーバーはリクエストされたAPIを提供できるピアAPIサーバーにリクエストを送信しようとします。 これは、ローカルサーバーが認識しないAPIグループ/バージョン/リソースを識別し、リクエストを処理できるピアAPIサーバーにそれらのリクエストをプロキシしようとすることで実現されます。
  • ピアAPIサーバーが応答に失敗した場合、ソース APIサーバーは503(「Service Unavailable」)エラーで応答します。

内部での動作

APIサーバーがリソースリクエストを受信すると、最初にどのAPIサーバーがリクエストされたリソースを提供できるかを確認します。 この確認は、ピア集約されていないディスカバリドキュメントを使用して行われます。

  • リクエストを受信したAPIサーバーから取得したピア集約されていないディスカバリドキュメントにリソースがリストされている場合(例えば、GET /api/v1/pods/some-pod)、リクエストはローカルで処理されます。

  • リクエスト内のリソース(例えば、GET /apis/resource.k8s.io/v1beta1/resourceclaims)が、リクエストを処理しようとしているAPIサーバー(処理APIサーバー)から取得したピア集約されていないディスカバリドキュメントに見つからない場合、おそらくresource.k8s.io/v1beta1 APIがより新しいKubernetesバージョンで導入され、処理APIサーバー がそれをサポートしていない古いバージョンを実行しているためです。 その場合、処理APIサーバー はすべてのピアAPIサーバーからピア集約されていないディスカバリドキュメントを確認して、関連するAPIグループ/バージョン/リソース(この場合はresource.k8s.io/v1beta1/resourceclaims)を提供するピアAPIサーバーを取得します。 処理APIサーバー は、リクエストされたリソースを認識している一致するピアkube-apiserverの1つにリクエストをプロキシします。

  • そのAPIグループ/バージョン/リソースを認識しているピアがいない場合、処理APIサーバーは自身のハンドラーチェーンにリクエストを渡し、最終的に404(「Not Found」)レスポンスを返すはずです。

  • 処理APIサーバーがピアAPIサーバーを識別して選択したが、そのピアが応答に失敗した場合(ネットワーク接続の問題、またはリクエストの受信とコントローラーがピアの情報をコントロールプレーンに登録する間のデータ競合などの理由)、処理APIサーバーは503(「Service Unavailable」)エラーで応答します。

3 - コンテナ

アプリケーションとランタイムの依存関係を一緒にパッケージ化するための技術

実行するそれぞれのコンテナは繰り返し使用可能です。依存関係を含めて標準化されており、どこで実行しても同じ動作が得られることを意味しています。

コンテナは基盤となるホストインフラからアプリケーションを切り離します。これにより、さまざまなクラウドやOS環境下でのデプロイが容易になります。

コンテナイメージ

コンテナイメージはすぐに実行可能なソフトウェアパッケージで、アプリケーションの実行に必要なものをすべて含んています。コードと必要なランタイム、アプリケーションとシステムのライブラリ、そして必須な設定項目のデフォルト値を含みます。

設計上、コンテナは不変で、既に実行中のコンテナのコードを変更することはできません。コンテナ化されたアプリケーションがあり変更したい場合は、変更を含んだ新しいイメージをビルドし、コンテナを再作成して、更新されたイメージから起動する必要があります。

コンテナランタイム

Kubernetesがコンテナを効果的に実行するために不可欠なコンポーネントです。 Kubernetes環境においてコンテナの実行とライフサイクルの管理を担います。

KubernetesはcontainerdCRI-Oなどのコンテナランタイムと、Kubernetes CRI (Container Runtime Interface)のすべての実装をサポートします。

次の項目

3.1 - イメージ

コンテナイメージは、アプリケーションおよびそのすべてのソフトウェア依存関係をカプセル化したバイナリデータを表します。 コンテナイメージは、スタンドアロンで実行可能なソフトウェアバンドルで、動作するためのランタイム環境が明確に規定されているのが特徴です。

通常、アプリケーションのコンテナイメージを作成してレジストリに格納し、その後、Podから参照します。

このページでは、コンテナイメージの概要について説明します。

備考:

Kubernetesのリリース(たとえばv1.36のような最新のマイナーリリース)に対応するコンテナイメージを探している場合は、Kubernetesのダウンロードを参照してください。

イメージ名

コンテナイメージは通常、pauseexample/mycontainer、またはkube-apiserverのような名前がつけられます。 イメージには、レジストリのホスト名を含めることもできます。たとえば、fictional.registry.example/imagenameのようになります。また、同じようにポート番号を含めることもでき、その場合はfictional.registry.example:10443/imagenameのようになります。

レジストリのホスト名を指定しない場合、KubernetesはDockerパブリックレジストリを意味しているものと見なします。 この挙動は、コンテナランタイムの設定でデフォルトのイメージレジストリを指定することで変更できます。

イメージ名の後には、タグダイジェスト を追加できます(これはdockerpodmanなどのコマンドを使う場合と同様です)。 タグは、同じ系列のイメージの異なるバージョンを識別するために使用します。 ダイジェストは、イメージの特定のバージョンに対する一意の識別子です。 また、ダイジェストはイメージの内容に基づくハッシュで構成され、変更不可能です。 タグは異なるイメージを指すように移動が可能ですが、ダイジェストは固定です。

イメージタグには、小文字および大文字のアルファベット、数字、アンダースコア(_)、ピリオド(.)、ハイフン(-)を使用できます。 タグの長さは最大128文字で、次の正規表現パターンに従う必要があります: [a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}。 この仕様の詳細や検証用の正規表現については、OCI Distribution Specificationを参照してください。 タグを指定しない場合、Kubernetesはlatestタグを指定したものと見なします。

イメージダイジェストは、ハッシュアルゴリズム(sha256など)とハッシュ値で構成されます。 たとえば、sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07のようになります。 ダイジェスト形式の詳細について詳しく知りたい場合は、OCI Image Specificationを参照してください。

Kubernetesで使用可能なイメージ名の例は次のとおりです:

  • busybox — イメージ名のみで、タグやダイジェストは指定されていません。KubernetesはDockerパブリックレジストリとlatestタグを使用します。docker.io/library/busybox:latestと同等です。
  • busybox:1.32.0 — タグ付きのイメージ名。KubernetesはDockerパブリックレジストリを使用します。docker.io/library/busybox:1.32.0と同等です。
  • registry.k8s.io/pause:latest — カスタムレジストリとlatestタグを指定したイメージ名。
  • registry.k8s.io/pause:3.5 — カスタムレジストリと非latestタグを指定したイメージ名。
  • registry.k8s.io/pause@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 — ダイジェストを含むイメージ名。
  • registry.k8s.io/pause:3.5@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 — タグとダイジェストの両方を含むイメージ名。取得時にはダイジェストのみが使用されます。

イメージの更新

PodTemplateを含むDeploymentStatefulSet、Pod、またはその他のオブジェクトを初めて作成する際に、プルポリシーが明示的に指定されていない場合、デフォルトでそのPod内のすべてのコンテナのプルポリシーはIfNotPresentに設定されます。 このポリシーでは、対象のイメージがすでに存在する場合、kubeletはそのイメージの取得をスキップします。

イメージプルポリシー

コンテナのimagePullPolicyとイメージのタグの両方が、kubeletが指定されたイメージの取得(ダウンロード)を試みる タイミング に影響します。

以下は、imagePullPolicyに設定できる値とその効果の一覧です。

IfNotPresent
イメージがローカルにまだ存在しない場合にのみ取得されます。
Always
kubeletがコンテナを起動するたびに、コンテナイメージレジストリに問い合わせ、イメージ名をイメージダイジェストに解決します。 kubeletがそのダイジェストに完全一致するイメージをローカルにキャッシュしている場合は、そのキャッシュされたイメージを使用します。存在しない場合は、kubeletは解決されたダイジェストのイメージを取得し、そのイメージを使ってコンテナを起動します。
Never
kubeletは、イメージを取得しようとしません。何らかの理由でローカルにイメージがすでに存在する場合、kubeletはコンテナを起動しようとします。それ以外の場合、起動に失敗します。 詳細は、事前に取得されたイメージを参照してください。

レジストリに確実にアクセスできるのであれば、基盤となるイメージプロバイダーのキャッシュセマンティクスによりimagePullPolicy: Alwaysでも効率的です。 コンテナランタイムは、イメージレイヤーがすでにノード上に存在することを認識できるため、再度ダウンロードする必要がありません。

備考:

本番環境でコンテナをデプロイする場合は、:latestタグの使用を避けるべきです。 実行中のイメージのバージョンを追跡するのが難しく、正しくロールバックすることがより困難になるためです。

代わりに、v1.42.0のような意味のあるタグやダイジェストを指定してください。

Podが常に同じバージョンのコンテナイメージを使用するようにするためには、イメージのダイジェストを指定します。<image-name>:<tag><image-name>@<digest>に置き換えてください(たとえば、image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2)。

イメージタグを使用している場合、レジストリ側でそのタグが指すコードが変更されると、古いコードと新しいコードを実行するPodが混在する可能性があります。 イメージダイジェストは特定のバージョンのイメージを一意に識別するため、Kubernetesは特定のイメージ名とダイジェストが指定されたコンテナを起動するたびに同じコードを実行します。 イメージをダイジェストで指定することで、実行するコードのバージョンが固定され、レジストリ側の変更によってバージョンが混在する事態を防ぐことができます。

サードパーティ製のアドミッションコントローラーの中には、Pod(およびPodTemplate)の作成時にそれらを変更し、実行されるワークロードがタグではなくイメージのダイジェストに基づいて定義されるようにするものがあります。 レジストリでどのようなタグの変更があっても、すべてのワークロードが必ず同じコードを実行するようにしたい場合に有用かもしれません。

デフォルトのイメージプルポリシー

あなた(またはコントローラー)が新しいPodをAPIサーバーに送信すると、特定の条件が満たされた場合に、クラスターはimagePullPolicyフィールドを設定します。

  • imagePullPolicyフィールドを省略し、かつコンテナイメージにダイジェストを指定した場合、imagePullPolicyには自動的にIfNotPresentに設定される。
  • imagePullPolicyフィールドを省略し、コンテナイメージのタグに:latestを指定した場合、imagePullPolicyには自動的にAlwaysが設定される。
  • imagePullPolicyフィールドを省略し、コンテナイメージのタグを指定しなかった場合、imagePullPolicyには自動的にAlwaysが設定される。
  • imagePullPolicyフィールドを省略し、コンテナイメージのタグに:latest以外を指定した場合、imagePullPolicyには自動的にIfNotPresentが設定される。

備考:

コンテナのimagePullPolicyの値は、そのオブジェクトが最初に 作成 されたときに常に設定され、その後イメージのタグやダイジェストが変更されても更新されません。

たとえば、タグが:latest でない イメージを使ってDeploymentを作成した場合、後でDeploymentのイメージを:latestタグに変更しても、imagePullPolicyAlwaysに更新 されません。初回作成後にプルポリシーを変更したい場合は、手動で更新しなければいけません。

イメージ取得を強制する

常に強制的に取得したい場合は、以下のいずれかを実行できます:

  • コンテナのimagePullPolicyAlwaysを設定する。
  • imagePullPolicyを省略し、使用するイメージのタグに:latestを指定する(KubernetesはPodを送信する際にポリシーをAlwaysに設定します)。
  • imagePullPolicyと使用するイメージのタグを省略する(KubernetesはPodを送信する際にポリシーをAlwaysに設定します)。
  • AlwaysPullImagesアドミッションコントローラーを有効にする。

ImagePullBackOff

kubeletがコンテナランタイムを使ってPodのコンテナの作成を開始するとき、ImagePullBackOffのためにコンテナがWaiting状態になる可能性があります。

ImagePullBackOff状態は、Kubernetesがコンテナイメージを取得できず、コンテナを開始できないことを意味します(イメージ名が無効である、imagePullSecret無しでプライベートレジストリから取得したなどの理由のため)。BackOffは、バックオフの遅延を増加させながらKubernetesがイメージを取得しようとし続けることを示します。

Kubernetesは、組み込まれた制限である300秒(5分)に達するまで、試行するごとに遅延を増加させます。

ランタイムクラスごとのイメージ取得

FEATURE STATE: Kubernetes v1.29 (Alpha; (デフォルトで無効))
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the RuntimeClassInImageCriApi feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Kubernetesには、PodのRuntimeClassに基づいてイメージを取得するアルファ機能のサポートが含まれています。

RuntimeClassInImageCriApiフィーチャーゲートを有効にすると、kubeletは単にイメージ名やダイジェストではなく、イメージ名とランタイムハンドラーのタプルでコンテナイメージを参照するようになります。 コンテナランタイムは、選択されたランタイムハンドラーに基づいて動作を変更する可能性があります。 ランタイムクラスに応じたイメージ取得を行う機能は、Windows Hyper-VコンテナのようなVMベースのコンテナにおいて有用です。

直列・並列イメージ取得

デフォルトでは、kubeletはイメージを直列に取得します。 言い換えれば、kubeletはイメージサービスに対して一度に1つのイメージ取得の要求しか送信しません。 他のイメージ取得の要求は、処理中の要求が完了するまで待機する必要があります。

ノードは、イメージの取得を他のノードと独立して判断します。そのため、イメージ取得を直列で実行していても、異なるノード上では同じイメージを並列で取得することが可能です。

並列イメージ取得を有効にしたい場合は、kubeletの設定serializeImagePullsフィールドをfalseに設定します。 serializeImagePullsをfalseにすると、イメージ取得の要求は即座にイメージサービスへ送信され、複数のイメージが同時に取得されるようになります。

並列イメージ取得を有効にする場合は、使用しているコンテナランタイムのイメージサービスが並列取得に対応していることを確認してください。

なお、kubeletは1つのPodに対して複数のイメージを並列で取得することはありません。 たとえば、1つのPodがinitコンテナとアプリケーションコンテナを持つ場合、その2つのコンテナのイメージ取得は並列化されません。 しかし、異なるPodがそれぞれ異なるイメージを使用しており、並列イメージ取得機能が有効になっている場合、kubeletはその2つの異なるPodのためにイメージを並列で取得します。

最大並列イメージ取得数

FEATURE STATE: Kubernetes v1.32 (Beta)

serializeImagePullsがfalseに設定されている場合、kubeletはデフォルトで同時に取得できるイメージ数に制限を設けません。 並列で取得できるイメージ数を制限したい場合は、kubeletの設定でmaxParallelImagePullsフィールドを指定します。 maxParallelImagePullsn を設定すると、同時に取得できるイメージは最大で n 件となり、n 件を超えるイメージ取得は、少なくとも1件のイメージ取得が完了するまで待機する必要があります。

並列イメージ取得を有効にしている場合、同時取得数を制限することで、イメージの取得によるネットワーク帯域やディスクI/Oの過剰な消費を防ぐことができます。

maxParallelImagePullsには1以上の正の整数を指定できます。 maxParallelImagePullsを2以上に設定する場合は、serializeImagePullsをfalseに設定する必要があります。 無効なmaxParallelImagePullsの設定を行うと、kubeletは起動に失敗します。

イメージインデックスを用いたマルチアーキテクチャイメージ

コンテナレジストリはバイナリイメージの提供だけでなく、コンテナイメージインデックスも提供可能です。 イメージインデックスはコンテナのアーキテクチャ固有バージョンに関する複数のイメージマニフェストを指すことができます。 イメージには(たとえばpauseexample/mycontainerkube-apiserverなどの)名前を付け、各システムが自身のマシンアーキテクチャに適したバイナリイメージを取得できるようにする、という考え方です。

Kubernetesプロジェクトでは、通常、リリースごとに-$(ARCH)というサフィックスを含む名前でコンテナイメージを作成します。 後方互換性のために、従来の形式のサフィックス付きイメージも生成されます。 たとえば、pauseという名前のイメージは、サポートされているすべてのアーキテクチャ向けのマニフェストを含むマルチアーキテクチャ対応のイメージですが、pause-amd64は旧来の構成や、サフィックス付きのイメージ名をハードコードしているYAMLファイルとの互換性のために用意されたイメージです。

プライベートレジストリの使用

プライベートレジストリでは、イメージの検索や取得を行うために認証が必要となる場合があります。 認証情報は、以下のいくつかの方法で提供できます:

これらのオプションについて、以下で詳しく説明します。

PodでimagePullSecretsを指定する

備考:

この方法がプライベートレジストリのイメージに基づいてコンテナを実行するための推奨の方法です。

Kubernetesは、Podに対してコンテナイメージレジストリ用のキーを指定できます。 すべてのimagePullSecretsは、そのPodと同じNamespace内に存在するSecretでなければなりません。 これらのSecretは、型がkubernetes.io/dockercfgまたはkubernetes.io/dockerconfigjsonである必要があります。

ノードを構成してプライベートレジストリに対して認証を行う

認証情報の具体的な設定手順は、使用するコンテナランタイムやレジストリによって異なります。 最も正確な情報については、使用しているソリューションのドキュメントを参照してください。

プライベートコンテナイメージレジストリの構成例については、イメージをプライベートレジストリから取得するを参照してください。 この例では、Docker Hubのプライベートレジストリを使用しています。

kubelet credential providerによる認証付きイメージ取得

kubeletを構成して、プラグインバイナリを呼び出し、コンテナイメージのレジストリ認証情報を動的に取得させることができます。 これは、プライベートレジストリ用の認証情報を取得するための最も堅牢かつ柔軟な方法ですが、有効化するにはkubeletレベルでの設定が必要です。

この手法は、プライベートレジストリにホストされたコンテナイメージを必要とするStatic Podを実行する際に特に有用です。 サービスアカウントSecretを使ってプライベートレジストリの認証情報を提供することは、Static Podの仕様では不可能です。 Static Podは、その仕様として他のAPIリソースへの参照を含めることが できない ためです。

詳細については、kubelet image credential providerを設定するを参照してください。

config.jsonの解釈

config.jsonの解釈は、元のDockerの実装とKubernetesにおける解釈で異なります。 Dockerでは、authsキーにはルートURLしか指定できませんが、Kubernetesではワイルドカード形式のURLや、プレフィックスが一致するパスも許容されます。 唯一の制約は、ワイルドカード(*)を使用する場合、各サブドメインに対して.を含める必要があるという点です。 一致するサブドメインの数は、指定されたワイルドカードパターンの数(*.)と一致している必要があります。 例:

  • *.kubernetes.ioは、kubernetes.ioには一致 しません が、abc.kubernetes.ioには一致します。
  • *.*.kubernetes.ioは、abc.kubernetes.ioには一致 しません が、abc.def.kubernetes.ioには一致します。
  • prefix.*.ioは、prefix.kubernetes.ioに一致します。
  • *-good.kubernetes.ioは、prefix-good.kubernetes.ioに一致します。

つまり、次のようなconfig.jsonは有効です:

{
    "auths": {
        "my-registry.example/images": { "auth": "…" },
        "*.my-registry.example/images": { "auth": "…" }
    }
}

イメージ取得操作では、すべての有効なパターンに対して、認証情報がCRIコンテナランタイムに渡されます。 たとえば、次のようなコンテナイメージ名は正常にマッチします。

  • my-registry.example/images
  • my-registry.example/images/my-image
  • my-registry.example/images/another-image
  • sub.my-registry.example/images/my-image

しかし、次のようなコンテナイメージ名はマッチ しません

  • a.sub.my-registry.example/images/my-image
  • a.b.sub.my-registry.example/images/my-image

kubeletは、見つかった各認証情報に対してイメージ取得を順番に実行します。 つまり、config.json内に異なるパスに対する複数のエントリを含めることも可能です。

{
    "auths": {
        "my-registry.example/images": {
            "auth": "…"
        },
        "my-registry.example/images/subpath": {
            "auth": "…"
        }
    }
}

このとき、コンテナがmy-registry.example/images/subpath/my-imageというイメージを取得するよう指定している場合、kubeletは、いずれかの認証元で失敗した場合でも、両方の認証情報を使ってイメージのダウンロードを試みます。

事前に取得されたイメージ

備考:

この方法は、ノードの構成を自分で制御できる場合に適しています。 クラウドプロバイダーがノードを管理し、自動的に置き換えるような環境では、信頼性のある動作は期待できません。

デフォルトでは、kubeletは指定されたレジストリからそれぞれのイメージを取得しようとします。 ただし、コンテナのimagePullPolicyプロパティがIfNotPresentまたはNeverに設定されている場合は、ローカルのイメージが(それぞれ優先的に、あるいは専用で)使用されます。

レジストリ認証の代替として事前に取得されたイメージを利用したい場合、クラスターのすべてのノードが同じ事前に取得されたイメージを持っていることを確認する必要があります。

これは、特定のイメージをあらかじめロードしておくことで高速化したり、プライベートレジストリへの認証の代替手段として利用したりすることができます。

kubelet credential providerの利用と同様に、事前に取得されたイメージは、プライベートレジストリにホストされたイメージに依存するStatic Podを起動する場合にも適しています。

備考:

        <div class="feature-state-notice feature-beta" title="フィーチャーゲート: KubeletEnsureSecretPulledImages">
          <span class="feature-state-name">FEATURE STATE:</span> 
          <span class="feature-state-details">Kubernetes v1.35 
           
             (Beta; (デフォルトで有効))
           </span>
        </div>
        
        
        <div class="feature-beta">
          
          </div>

事前に取得されたイメージへのアクセスは、イメージ取得の認証情報の検証に基づいて許可される場合があります。

イメージ取得の認証情報の検証を保証する

FEATURE STATE: Kubernetes v1.35 (Beta; (デフォルトで有効))

クラスターでKubeletEnsureSecretPulledImagesフィーチャーゲートが有効になっている場合、Kubernetesは、取得に認証が必要なすべてのイメージに対して、たとえそのイメージがノード上にすでに存在していても、認証情報を検証します。 この検証により、Podが要求するイメージのうち、指定された認証情報で正常に取得されなかったものは、レジストリから再度取得する必要があることが保証されます。 さらに、以前に成功したイメージ取得と同じ認証情報を使用する場合には、再取得は不要で、レジストリにアクセスせずローカルで検証が行われます(該当のイメージがローカルに存在する場合)。 この動作は、kubeletの設定におけるimagePullCredentialsVerificationPolicyフィールドによって制御されます。

この設定は、イメージがすでにノード上に存在する場合に、イメージ取得の認証情報を検証する必要があるかどうかの挙動を制御します:

  • NeverVerify: このフィーチャーゲートが無効な場合と同じ動作を模倣します。イメージがローカルに存在する場合、イメージ取得の認証情報は検証されません。
  • NeverVerifyPreloadedImages: kubeletの外部で取得されたイメージは検証されませんが、それ以外のすべてのイメージについては認証情報が検証されます。これがデフォルトの動作です。
  • NeverVerifyAllowListedImages: kubeletの外部で取得されたイメージと、kubelet設定内で指定されたpreloadedImagesVerificationAllowlistに記載されたイメージは検証されません。
  • AlwaysVerify: すべてのイメージは、使用前に認証情報の検証が行われます。

この検証は、事前に取得されたイメージ、ノード全体のSecretを使用して取得されたイメージ、PodレベルのSecretを使用して取得されたイメージに適用されます。

備考:

認証情報がローテーションされた場合でも、以前にイメージの取得に使用された認証情報は、レジストリへアクセスすることなく検証に成功し続けます。 一方、新しい認証情報やローテーション後の認証情報を使用する場合には、イメージを再度レジストリから取得する必要があります。

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のSecretとしてインポートすることができます。 既存のDocker認証情報に基づいてSecretを作成するに、その設定方法が説明されています。

これは、複数のプライベートコンテナレジストリを使用している場合に特に有用です。 kubectl create secret docker-registryで作成されるSecretは、単一のプライベートレジストリにしか対応していないためです。

備考:

Podは自身のNamespace内にあるイメージプルシークレットしか参照できないため、この手順はNamespaceごとに1回ずつ実行する必要があります。

PodでimagePullSecretsを参照する

次に、Pod定義にimagePullSecretsセクションを追加することで、そのSecretを参照するPodを作成できます。 imagePullSecrets配列の各要素は、同じNamespace内の1つのSecretのみを参照できます。

たとえば、以下のように記述します:

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セクションを指定することで、この手順を自動化することが可能です。 詳細な手順については、ServiceAccountにImagePullSecretsを追加するを参照してください。

また、各ノードにある.docker/config.jsonと併用することもできます。 これらの認証情報はマージされて使用されます。

ユースケース

プライベートレジストリを構成するにはいくつかの手段があります。 ここでは、いくつかの一般的なユースケースと推奨される解決方法を示します。

  1. 非専有(たとえば、オープンソースの)イメージだけを実行し、イメージを非公開にする必要がないクラスターの場合

    • パブリックレジストリのパブリックイメージを利用する。
      • 設定は必要ない。
      • 一部のクラウドプロバイダーは、パブリックイメージを自動的にキャッシュまたはミラーリングすることで、可用性を向上させ、イメージの取得にかかる時間を短縮する。
  2. 社外には非公開にすべきだが、クラスターユーザー全員には見える必要がある専有イメージを実行しているクラスターの場合

    • ホスティング型のプライペートレジストリを使用する。
      • プライベートレジストリにアクセスする必要があるノードには、手動設定が必要となる場合がある。
    • または、ファイアウォールの内側で内部向けのプライベートレジストリを実行し、読み取りアクセスを開放して運用する。
      • Kubernetesの設定は必要ない。
    • イメージへのアクセスを制御できるホスティング型のコンテナイメージレジストリサービスを利用する。
      • ノードを手動で構成する場合よりも、ノードのオートスケーリングと組み合わせた方がうまく機能する。
    • あるいは、ノードの構成を変更するのが困難なクラスターでは、imagePullSecretsを使用する。
  3. 一部に、より厳格なアクセス制御を必要とする専有イメージを含むクラスターの場合

    • AlwaysPullImagesアドミッションコントローラーが有効化されていることを確認する。これが無効な場合、すべてのPodがすべてのイメージにアクセスできてしまう可能性がある。
    • 機密データはイメージ内に含める代わりに、Secretリソースに移行する。
  4. それぞれのテナントが独自のプライベートレジストリを必要とするマルチテナントのクラスターである場合

    • AlwaysPullImagesアドミッションコントローラーが有効化されていることを確認する。これが無効な場合、すべてのPodがすべてのイメージにアクセスできてしまう可能性がある。
    • 認証が必要なプライベートレジストリを運用する。
    • テナントごとにレジストリの認証情報を生成し、それをSecretに保存して、各テナントのNamespaceへ配布する。
    • テナントは、そのSecretを各NamespaceのimagePullSecretsに追加する。

複数のレジストリへのアクセスが必要な場合、レジストリごとに1つのSecretを作成することができます。

レガシーな組み込みkubelet credential provider

古いバージョンのKubernetesでは、kubeletがクラウドプロバイダーの認証情報と直接統合されていました。 これにより、イメージレジストリの認証情報を動的に取得することが可能でした。

このkubelet credential provider統合には、以下の3つの組み込み実装がありました: ACR(Azure Container Registry)、ECR(Elastic Container Registry)、GCR(Google Container Registry)です。

Kubernetesバージョン1.26以降では、このレガシーな仕組みは削除されたため、以下のいずれかの対応が必要です:

  • 各ノードにkubelet image credential providerを構成する
  • imagePullSecretsと少なくとも1つのSecretを使用して、イメージ取得の認証情報を指定する

次の項目

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 - ランタイムクラス(Runtime Class)

FEATURE STATE: Kubernetes v1.20 (GA)

このページではRuntimeClassリソースと、runtimeセクションのメカニズムについて説明します。

RuntimeClassはコンテナランタイムの設定を選択するための機能です。そのコンテナランタイム設定はPodのコンテナを稼働させるために使われます。

RuntimeClassを使う動機

異なるPodに異なるRuntimeClassを設定することで、パフォーマンスとセキュリティのバランスをとることができます。例えば、ワークロードの一部に高レベルの情報セキュリティ保証が必要な場合、ハードウェア仮想化を使用するコンテナランタイムで実行されるようにそれらのPodをスケジュールすることを選択できます。その後、追加のオーバーヘッドを犠牲にして、代替ランタイムをさらに分離することでメリットが得られます。

RuntimeClassを使用して、コンテナランタイムは同じで設定が異なるPodを実行することもできます。

セットアップ

  1. ノード上でCRI実装を設定する。(ランタイムに依存)
  2. 対応するRuntimeClassリソースを作成する。

1. ノード上でCRI実装を設定する

RuntimeClassを通じて利用可能な設定はContainer Runtime Interface (CRI)の実装依存となります。 ユーザーの環境のCRI実装の設定方法は、対応するドキュメント(下記)を参照ください。

備考:

RuntimeClassは、クラスター全体で同じ種類のノード設定であることを仮定しています。(これは全てのノードがコンテナランタイムに関して同じ方法で構成されていることを意味します)。 設定が異なるノードをサポートするには、スケジューリングを参照してください。

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の書き込み操作(create/update/patch/delete)はクラスター管理者のみに制限されることを推奨します。 これはたいていデフォルトで有効となっています。さらなる詳細に関してはAuthorization Overviewを参照してください。

使用例

RuntimeClassがクラスターに対して設定されると、PodSpecでruntimeClassNameを指定して使用できます。 例えば

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

これは、kubeletに対してPodを稼働させるためのRuntimeClassを使うように指示します。もし設定されたRuntimeClassが存在しない場合や、CRIが対応するハンドラーを実行できない場合、そのPodはFailedというフェーズになります。 エラーメッセージに関しては対応するイベントを参照して下さい。

もしruntimeClassNameが指定されていない場合、デフォルトのRuntimeHandlerが使用され、これはRuntimeClassの機能が無効であるときのふるまいと同じものとなります。

CRIの設定

CRIランタイムのセットアップに関するさらなる詳細は、コンテナランタイムを参照してください。

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 (GA)

Podが稼働する時に関連する オーバーヘッド リソースを指定できます。オーバーヘッドを宣言すると、クラスター(スケジューラーを含む)がPodとリソースに関する決定を行うときにオーバーヘッドを考慮することができます。

PodのオーバーヘッドはRuntimeClass内のoverheadフィールドによって定義されます。 このフィールドを使用することで、RuntimeClassを使用して稼働するPodのオーバーヘッドを指定することができ、Kubernetes内部で使用されるオーバーヘッドを確保することができます。

次の項目

3.4 - コンテナライフサイクルフック

このページでは、kubeletにより管理されるコンテナがコンテナライフサイクルフックフレームワークを使用して、管理ライフサイクル中にイベントによって引き起こされたコードを実行する方法について説明します。

概要

Angularなどのコンポーネントライフサイクルフックを持つ多くのプログラミング言語フレームワークと同様に、Kubernetesはコンテナにライフサイクルフックを提供します。 フックにより、コンテナは管理ライフサイクル内のイベントを認識し、対応するライフサイクルフックが実行されたときにハンドラーに実装されたコードを実行できます。

コンテナフック

コンテナに公開されている2つのフックがあります。

PostStart

このフックはコンテナが作成された直後に実行されます。 しかし、フックがコンテナのENTRYPOINTの前に実行されるという保証はありません。 ハンドラーにパラメーターは渡されません。

PreStop

このフックは、APIからの要求、またはliveness/startup probeの失敗、プリエンプション、リソース競合などの管理イベントが原因でコンテナが終了する直前に呼び出されます。コンテナがすでに終了状態または完了状態にある場合にはPreStopフックの呼び出しは失敗し、コンテナを停止するTERMシグナルが送信される前にフックは完了する必要があります。PreStopフックが実行される前にPodの終了猶予期間のカウントダウンが開始されるので、ハンドラーの結果に関わらず、コンテナはPodの終了猶予期間内に最終的に終了します。 ハンドラーにパラメーターは渡されません。

終了動作の詳細な説明は、Termination of Podsにあります。

フックハンドラーの実装

コンテナは、フックのハンドラーを実装して登録することでそのフックにアクセスできます。 コンテナに実装できるフックハンドラーは3種類あります。

  • Exec - コンテナのcgroupsと名前空間の中で、 pre-stop.shのような特定のコマンドを実行します。 コマンドによって消費されたリソースはコンテナに対してカウントされます。
  • HTTP - コンテナ上の特定のエンドポイントに対してHTTP要求を実行します。
  • Sleep - 指定された期間コンテナを一時停止します。これは、PodLifecycleSleepActionフィーチャーゲートによりデフォルトで有効になっているベータ機能です。

備考:

Sleepライフサイクルフックのスリープ時間を0秒(実質的なno-op)に設定したい場合は、PodLifecycleSleepActionAllowZeroフィーチャーゲートを有効にしてください。

フックハンドラーの実行

コンテナライフサイクル管理フックが呼び出されると、Kubernetes管理システムはフックアクションにしたがってハンドラーを実行します。 httpGettcpSocket(非推奨です)、およびsleepは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.5 - コンテナランタイムインターフェース(CRI)

CRIはプラグインインターフェースで、クラスターコンポーネントを再コンパイルすることなく、kubeletがさまざまなコンテナランタイムを使用できるようにします。

kubeletPodとそのコンテナを起動できるように、クラスター内の各ノードで動作するコンテナランタイムが必要です。

コンテナランタイムインターフェース(CRI)は kubeletとコンテナランタイム間の通信のための主要なプロトコルです。

Kubernetesコンテナランタイムインターフェース(CRI)は、ノードコンポーネントであるkubeletコンテナランタイムの間の通信のための主要なgRPCプロトコルを定義します。

API

FEATURE STATE: Kubernetes v1.23 (GA)

kubeletは、gRPCを用いてコンテナランタイムに接続するときにクライアントとして機能します。 ランタイムおよびイメージサービスエンドポイントは、コンテナランタイムで利用できる必要があります。 これらは、--container-runtime-endpointコマンドラインフラグを使用してkubelet内で個別に設定できます。

Kubernetes v1.26以降では、kubeletはコンテナランタイムがv1 CRI APIをサポートしていることを要求します。 コンテナランタイムがv1 APIをサポートしていない場合、kubeletはノードを登録しません。

アップグレード

ノード上でKubernetesのバージョンをアップグレードすると、kubeletが再起動します。 コンテナランタイムがv1 CRI APIをサポートしていない場合、kubeletは登録に失敗し、エラーを報告します。 コンテナランタイムのアップグレードによってgRPCの再接続が必要な場合、接続を成功させるには、ランタイムがv1 CRI APIをサポートしている必要があります。 これには、コンテナランタイムが正しく設定された後、kubeletの再起動が必要になる場合があります。

次の項目

4 - Windows in Kubernetes

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のドキュメントを参照してください。

備考:

マスターコンポーネントを含むKubernetesコントロールプレーンは、Linuxで実行し続けます。WindowsのみのKubernetesクラスターを導入する計画はありません。

備考:

このドキュメントでは、Windowsコンテナについて説明する場合、プロセス分離のWindowsコンテナを意味します。Hyper-V分離のWindowsコンテナは、将来リリースが計画されています。

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

サポートされている機能

コンピュート

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

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

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

備考:

すべてのWindowsユーザーがアプリのオペレーティングシステムを頻繁に更新することは望んでいません。アプリケーションのアップグレードは、クラスターに新しいノードをアップグレードまたは導入することを要求する必要があります。Kubernetesで実行されているコンテナのオペレーティングシステムをアップグレードすることを選択したユーザーには、新しいオペレーティングシステムバージョンのサポート追加時に、ガイダンスと段階的な指示を提供します。このガイダンスには、クラスターノードと共にアプリケーションをアップグレードするための推奨アップグレード手順が含まれます。Windowsノードは、現在のLinuxノードと同じように、Kubernetesバージョンスキューポリシー(ノードからコントロールプレーンのバージョン管理)に準拠しています。

備考:

Windows Serverホストオペレーティングシステムには、Windows Serverライセンスが適用されます。Windowsコンテナイメージには、Windowsコンテナの追加ライセンス条項ライセンスが提供されます。

備考:

プロセス分離のWindowsコンテナには、ホストOSのバージョンはコンテナのベースイメージのOSバージョンと一致する必要があるという厳格な互換性ルールがあります。KubernetesでHyper-V分離のWindowsコンテナをサポートする際には、制限と互換性ルールが変更されます。

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 (GA)

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 v1.18におけるWindows上でのContainerDは以下の既知の欠点があります:

  • ContainerDは公式リリースではWindowsをサポートしていません。すなわち、Kubernetesでのすべての開発はアクティブなContainerD開発ブランチに対して行われています。本番環境へのデプロイは常に、完全にテストされセキュリティ修正をサポートした公式リリースを利用するべきです。
  • ContainerDを利用した場合、Group Managed Service Accountsは実装されていません。詳細はcontainerd/cri#1276を参照してください。

永続ストレージ

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-CNI Azure-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-kubernetes ansible経由でデプロイします。分散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
  • その他のストレージプラグイン

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

備考:

ポートマッピングもサポートされていますが、この例では簡単にするために、コンテナポート80がサービスに直接公開されています。
  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コンテナホストは、ホストされているサービスのIPにアクセスできません。Service IPにアクセスできるのは、Windows Podだけです。

可観測性

ワークロードからのログキャプチャ

ログは可観測性の重要な要素です。これにより、ユーザーはワークロードの運用面に関する洞察を得ることができ、問題のトラブルシューティングの主要な要素になります。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 2019 10.0.17763
Windows Server version 1809 10.0.17763
Windows Server version 1903 10.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

5 - ワークロード

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のしくみについて学ぶには、設定を読んでください。

5.1 - Pod

Podは、Kubernetes内で作成・管理できるコンピューティングの最小のデプロイ可能なユニットです。

Pod(Podという名前は、たとえばクジラの群れ(pod of whales)やえんどう豆のさや(pea pod)などの表現と同じような意味です)は、1つまたは複数のコンテナのグループであり、ストレージやネットワークの共有リソースを持ち、コンテナの実行方法に関する仕様を持っています。同じPodに含まれるリソースは、常に同じ場所で同時にスケジューリングされ、共有されたコンテキストの中で実行されます。Podはアプリケーションに特化した「論理的なホスト」をモデル化します。つまり、1つのPod内には、1つまたは複数の比較的密に結合されたアプリケーションコンテナが含まれます。クラウド外の文脈で説明すると、アプリケーションが同じ物理ホストや同じバーチャルマシンで実行されることが、クラウドアプリケーションの場合には同じ論理ホスト上で実行されることに相当します。

アプリケーションコンテナと同様に、Podでも、Podのスタートアップ時に実行されるinitコンテナを含めることができます。また、クラスターで利用できる場合には、エフェメラルコンテナを注入してデバッグすることもできます。

Podとは何か?

備考:

KubernetesはDockerだけでなく複数のコンテナランタイムをサポートしていますが、Dockerが最も一般的に知られたランタイムであるため、Docker由来の用語を使って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は、与えられたアプリケーションの単一のインスタンスを稼働するためのものです。もしユーザーのアプリケーションを水平にスケールさせたい場合(例: 複数インスタンスを稼働させる)、複数の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の再起動を混同しないでください。Podはプロセスではなく、コンテナが実行するための環境です。Podは削除されるまでは残り続けます。

Podオブジェクトのためのマニフェストを作成したときは、指定したPodの名前が有効なDNSサブドメイン名であることを確認してください。

Pod OS

FEATURE STATE: Kubernetes v1.25 (GA)

.spec.os.nameフィールドでwindowslinuxのいずれかを設定し、Podを実行させたいOSを指定する必要があります。Kubernetesは今のところ、この2つのOSだけサポートしています。将来的には増える可能性があります。

Kubernetes v1.36では、このフィールドに設定した値は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サーバー上から制御はできないということです。

備考:

Static Podのspecは他のAPIオブジェクト (例えばサービスアカウントConfigMapSecret、など)を参照することはできません。

コンテナのProbe

Probe はkubeletがコンテナに対して行う定期診断です。診断を実行するために、kubeletはさまざまなアクションを実行できます:

  • ExecAction (コンテナランタイムの助けを借りて実行)
  • TCPSocketAction (kubeletにより直接チェック)
  • HTTPGetAction (kubeletにより直接チェック)

更に詳しく知りたい場合は、PodのライフサイクルドキュメントにあるProbeを読んでください。

次の項目

Kubernetesが共通のPod APIを他のリソース内(たとえばStatefulSetDeploymentなど)にラッピングしている理由の文脈を理解するためには、Kubernetes以前から存在する以下のような既存技術について読むのが助けになります。

5.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の取りうる値です。

概要
Pending PodがKubernetesクラスターによって承認されましたが、1つ以上のコンテナがセットアップされて稼働する準備ができていません。これには、スケジュールされるまでの時間と、ネットワーク経由でイメージをダウンロードするための時間などが含まれます。
Running PodがNodeにバインドされ、すべてのコンテナが作成されました。少なくとも1つのコンテナがまだ実行されているか、開始または再起動中です。
Succeeded Pod内のすべてのコンテナが正常に終了し、再起動されません。
Failed Pod内のすべてのコンテナが終了し、少なくとも1つのコンテナが異常終了しました。つまり、コンテナはゼロ以外のステータスで終了したか、システムによって終了されました。
Unknown 何らかの理由によりPodの状態を取得できませんでした。このフェーズは通常はPodのホストとの通信エラーにより発生します。

備考:

Podの削除中に、kubectlコマンドにはTerminatingが出力されることがあります。このTerminatingステータスは、Podのフェーズではありません。Podには、正常に終了するための期間を与えられており、デフォルトは30秒です。--forceフラグを使用して、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"のうちのいずれかです。
lastProbeTime Pod Conditionが最後に確認されたときのタイムスタンプが表示されます。
lastTransitionTime 最後にPodのステータスの遷移があった際のタイムスタンプが表示されます。
reason 最後の状態遷移の理由を示す、機械可読のアッパーキャメルケースのテキストです。
message ステータスの遷移に関する詳細を示す人間向けのメッセージです。

PodのReadiness

FEATURE STATE: Kubernetes v1.14 (GA)

追加のフィードバックやシグナルを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はコンテナランタイム(コンテナランタイムインターフェース(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 (GA)

コンテナ自体に問題が発生した場合や状態が悪くなった際にクラッシュすることができればlivenessProbeは不要です。 この場合kubeletが自動でPodのrestartPolicyに基づいたアクションを実行します。

Probeに失敗したときにコンテナを殺したり再起動させたりするには、livenessProbeを設定しrestartPolicyをAlwaysまたはOnFailureにします。

readinessProbeをいつ使うべきか?

FEATURE STATE: Kubernetes v1.0 (GA)

Probeが成功したときにのみPodにトラフィックを送信したい場合は、readinessProbeを指定します。 この場合readinessProbeはlivenessProbeと同じになる可能性がありますが、readinessProbeが存在するということは、Podがトラフィックを受けずに開始され、Probe成功が開始した後でトラフィックを受け始めることになります。

コンテナがメンテナンスのために停止できるようにするには、livenessProbeとは異なる、特定のエンドポイントを確認するreadinessProbeを指定することができます。

アプリがバックエンドサービスと厳密な依存関係にある場合、livenessProbeとreadinessProbeの両方を実装することができます。アプリ自体が健全であればlivenessProbeはパスしますが、readinessProbeはさらに、必要なバックエンドサービスが利用可能であるかどうかをチェックします。これにより、エラーメッセージでしか応答できないPodへのトラフィックの転送を避けることができます。

コンテナの起動中に大きなデータ、構成ファイル、またはマイグレーションを読み込む必要がある場合は、startupProbeを使用できます。ただし、失敗したアプリと起動データを処理中のアプリの違いを検出したい場合は、readinessProbeを使用した方が良いかもしれません。

備考:

Podが削除されたときにリクエストを来ないようにするためには必ずしもreadinessProbeが必要というわけではありません。Podの削除時にはreadinessProbeが存在するかどうかに関係なくPodは自動的に自身をunreadyにします。Pod内のコンテナが停止するのを待つ間Podはunreadyのままです。

startupProbeをいつ使うべきか?

FEATURE STATE: Kubernetes v1.20 (GA)

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秒)。

      備考:

      preStopフックが完了するまでにより長い時間が必要な場合は、terminationGracePeriodSecondsを変更する必要があります。
    2. kubeletはコンテナランタイムをトリガーして、コンテナ内のプロセス番号1にTERMシグナルを送信する。

      備考:

      Pod内のすべてのコンテナが同時にTERMシグナルを受信するわけではなく、シャットダウンの順序が問題になる場合はそれぞれにpreStopフックを使用して同期することを検討する。
  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の強制削除

注意:

強制削除は、Podによっては潜在的に危険な場合があるため、慎重に実行する必要があります。

デフォルトでは、すべての削除は30秒以内に正常に行われます。kubectl delete コマンドは、ユーザーがデフォルト値を上書きして独自の値を指定できるようにする --grace-period=<seconds> オプションをサポートします。

--grace-period0に設定した場合、PodはAPI serverから即座に強制的に削除されます。PodがNode上でまだ実行されている場合、その強制削除によりkubeletがトリガーされ、すぐにクリーンアップが開始されます。

備考:

強制削除を実行するために --grace-period=0 と共に --force というフラグを追加で指定する必要があります。

強制削除が実行されると、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が作成されて時間とともに終了するため、リソースリークを避けます。

次の項目

5.1.2 - Pod Condition

Kubernetesでは、多くのオブジェクトに Condition があります。 Conditionは、オブジェクトが表す対象の実際の状態における、ある側面を示すマーカーです。 PodにもConditionがあり、KubernetesのPodのConditionは、コントローラー(やトラブルシューティングを行う人)がPodの健全性を理解するための重要な要素です。

Podのフェーズは、Podがライフサイクル上のどこに位置するかを大まかにまとめたものですが、単一の値ですべてを表現することはできません。 例えば、PodがRunningフェーズにあっても、まだトラフィックを処理する準備が整っていない場合があります。 PodのConditionは、Podがスケジュールされたかどうか、コンテナが準備完了かどうか、リサイズが進行中かどうか、taintによってPodが中断されようとしているかなど、Podの状態の複数の側面を独立して追跡することでフェーズを補完します。

Pod Conditionの構造

Podのステータスには、Podが特定のチェックポイントを通過したかどうかを示すPodConditionの配列が含まれています。

PodCondition配列の各要素には、以下のフィールドがあります:

PodConditionのフィールド
フィールド名 説明
type このPodのConditionの名前です。
status このConditionが適用可能かどうかを示します。可能な値は"True""False""Unknown"のいずれかです。
lastProbeTime Pod Conditionが最後に確認されたときのタイムスタンプです。
lastTransitionTime Podのステータスが、あるステータスから別のステータスへ最後に遷移したときのタイムスタンプです。
reason Conditionの最後の遷移理由を示す、機械可読のアッパーキャメルケースのテキストです。
message 最後のステータス遷移に関する詳細を示す人間向けのメッセージです。
observedGeneration Conditionが記録された時点のPodの.metadata.generationです。Pod generationを参照してください。

ビルトインのPod Condition

Kubernetesは以下のPod Conditionを管理します:

ライフサイクル上のCondition: Podがライフサイクルを進む過程で、おおよそPodScheduledPodReadyToStartContainersInitializedContainersReadyReadyの順に設定されます。

その他のCondition: 特定の操作やイベントに応じて、DisruptionTargetPodResizePendingPodResizeInProgressが設定されます。

上記のビルトインのConditionに加えて、PodのReadinessゲートを使用してカスタムのConditionを定義することもできます。

ライフサイクル上のPod Condition

Podがライフサイクルを進むにつれて、kubeletは以下のConditionをおおよそ次の順序で設定します:

  1. PodScheduled: Podがノードにスケジュールされたことを示します。
  2. PodReadyToStartContainers: Podのサンドボックスが正常に作成され、ネットワークが構成されたことを示します。 サンドボックスとネットワークは、コンテナランタイムCNIプラグインによってセットアップされます。
  3. Initialized: すべてのInitコンテナが正常に完了したことを示します。 Initコンテナを持たないPodでは、サンドボックスの作成前にTrueに設定されます。
  4. ContainersReady: Pod内のすべてのコンテナが準備完了であることを示します。 コンテナのReadinessは、設定されている場合はReadiness Probeによって判定されます。
  5. Ready: Podがリクエストを処理でき、一致するすべてのServiceの負荷分散プールに追加されるべきであることを示します。 ReadyでないPodはServiceのエンドポイントから削除されます。

備考:

Ready ConditionはContainersReadyだけに依存しているわけではありません。 PodがreadinessGatesを指定している場合、PodがReadyになるためには、それらのカスタムConditionもすべてTrueである必要があります。 詳細はPodのReadinessを参照してください。

kubectlを使用してPodのConditionを確認できます:

kubectl get pod <pod-name> -o yaml

実行中のPodのstatus.conditionsは次のようになります:

status:
  conditions:
    - type: PodScheduled
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-03-29T08:52:21Z"
      observedGeneration: 1
    - type: PodReadyToStartContainers
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-04-11T06:02:16Z"
      observedGeneration: 1
    - type: Initialized
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-03-29T08:52:21Z"
      observedGeneration: 1
    - type: ContainersReady
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-04-11T06:02:45Z"
      observedGeneration: 1
    - type: Ready
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-04-11T06:02:45Z"
      observedGeneration: 1

PodReadyToStartContainers

FEATURE STATE: Kubernetes v1.29 (Beta; (デフォルトで有効))

備考:

このConditionは、開発初期にはPodHasNetworkという名前でした。

Podがノードにスケジュールされた後、kubeletに受け入れられ、必要なストレージボリュームがマウントされる必要があります。 これらのフェーズが完了すると、kubeletは(コンテナランタイムインターフェース(CRI)を使用して)コンテナランタイムと連携し、ランタイムサンドボックスをセットアップしてPodのネットワークを構成します。 PodReadyToStartContainersConditionフィーチャーゲートが有効な場合(Kubernetes 1.36ではデフォルトで有効)、PodReadyToStartContainers ConditionがPodのstatus.conditionsフィールドに追加されます。

PodReadyToStartContainers Conditionは、Podにネットワーク構成済みのランタイムサンドボックスがないことをkubeletが検出すると、Falseに設定されます。 これは以下のシナリオで発生します:

  • Podのライフサイクルの初期で、kubeletがコンテナランタイムを使用してPodのサンドボックスのセットアップをまだ開始していないとき。
  • Podのライフサイクルの後期で、Podのサンドボックスが以下のいずれかの原因で破棄されたとき:
    • Podが退避させられずに、ノードが再起動した。
    • 隔離に仮想マシンを使用するコンテナランタイムにおいて、Podサンドボックスの仮想マシンが再起動し、新しいサンドボックスと新しいコンテナネットワーク設定の作成が必要になった。

ランタイムプラグインによるPodのサンドボックスの作成とネットワーク構成が正常に完了すると、kubeletはPodReadyToStartContainers ConditionをTrueに設定します。 PodReadyToStartContainers ConditionがTrueに設定された後、kubeletはコンテナイメージの取得とコンテナの作成を開始できます。

Initコンテナを持つPodでは、kubeletはInitコンテナが正常に完了した後(これはランタイムプラグインによるサンドボックスの作成とネットワーク構成が成功した後に発生します)、Initialized ConditionをTrueに設定します。 Initコンテナを持たないPodでは、kubeletはサンドボックスの作成とネットワーク構成が開始する前に、Initialized ConditionをTrueに設定します。

その他のPod Condition

以下のConditionは、通常のPodのライフサイクルの進行には含まれません。 これらは特定の操作やイベントに応じて設定されます。

DisruptionTarget

DisruptionによりPodが削除されようとしていることを示すために、専用のPod DisruptionTarget Conditionが追加されます。 このConditionのreasonフィールドは、Podの終了理由として以下のいずれかを示します:

PreemptionByScheduler
より高い優先度を持つ新しいPodを受け入れるために、スケジューラーによってPodがプリエンプトされようとしています。 詳細はPodの優先度とプリエンプションを参照してください。
DeletionByTaintManager
Podが許容しないNoExecuteのtaintにより、Taint Manager(kube-controller-manager内のノードライフサイクルコントローラーの一部)によってPodが削除されようとしています。 taintベースの退避を参照してください。
EvictionByEvictionAPI
PodがKubernetes APIを使用した退避の対象としてマークされました。
DeletionByPodGC
既に存在しないノードにバインドされているPodが、Podのガベージコレクションによって削除されようとしています。
TerminationByKubelet
ノードの圧迫による退避ノードのグレースフルシャットダウン、またはシステムにとってクリティカルなPodのためのプリエンプションのいずれかにより、Podがkubeletによって終了されました。

Podのコンテナの制限を超過したことによる退避のようなその他すべてのDisruptionシナリオでは、Disruptionの原因がPod自体にある可能性が高く、リトライしても再発するため、PodはDisruptionTarget Conditionを受け取りません。

備考:

PodのDisruptionは中断される可能性があります。 コントロールプレーンは、同じPodのDisruptionを継続しようと再試行する場合がありますが、保証はされていません。 その結果、DisruptionTarget ConditionがPodに追加されても、そのPodが実際には削除されないことがあります。 このような場合、しばらくすると、PodのDisruptionのConditionはクリアされます。

Podのガベージコレクター(PodGC)は、Podをクリーンアップするとともに、Podが非終了フェーズにある場合はそれらをfailedとしてマークします(Podのガベージコレクションも参照してください)。

Job(またはCronJob)を使用する場合、これらのPodのDisruptionのConditionを、JobのPod失敗ポリシーの一部として活用したい場合があります。

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

PodResizePendingとPodResizeInProgress

kubeletは、リサイズリクエストの状態を示すために、PodのステータスのConditionを更新します:

  • type: PodResizePending: kubeletはリクエストをすぐには許可できません。 messageフィールドにその理由が記載されます。
    • reason: Infeasible: 要求されたリサイズは現在のノード上では実行不可能です(例えば、ノードが持つリソースより多くを要求した場合)。
    • reason: Deferred: 要求されたリサイズは現時点では不可能ですが、後で実現可能になる可能性があります(例えば、他のPodが削除された場合)。 kubeletはリサイズを再試行します。
  • type: PodResizeInProgress: kubeletはリサイズを受け入れてリソースを割り当てましたが、変更はまだ適用中です。 通常は短時間で完了しますが、リソースの種類やランタイムの挙動によってはさらに時間がかかる場合があります。 実行中に発生したエラーは、messageフィールドに(reason: Errorとともに)報告されます。

要求されたリサイズが Deferred となった場合、kubeletは、例えば他のPodが削除されたりスケールダウンされたりしたときなどに、定期的にリサイズを再試行します。

Podのリサイズの詳細については、コンテナに割り当てられたCPUおよびメモリリソースのリサイズを参照してください。

Enhanced Pod readiness

アプリケーションは、Podの.statusに追加のフィードバックやシグナルを注入できます。 これは Enhanced Pod readiness と呼ばれます。 これを使用するには、PodのspecreadinessGatesを設定し、kubeletがPodのReadinessを評価する際に確認する追加のConditionのリストを指定します。 そして、これらのカスタムConditionを管理するコントローラーを実装またはインストールすると、kubeletはそれをPodがreadyかどうかを判断するための追加情報として使用します。

ReadinessゲートはPodのstatus.conditionフィールドの現在の状態によって決まります。 Kubernetesがstatus.conditionsフィールド内に該当するConditionを見つけられない場合、そのConditionのステータスはデフォルトで"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のラベルキーのフォーマットに準拠している必要があります。

Pod Readinessのためのステータス

Podにこれらのstatus.conditionsを設定するには、アプリケーションやオペレーターは、Podのステータスサブリソースに対してPATCHアクションを使用する必要があります。 kubectl patch--subresource=statusとともに使用するか、Kubernetesのクライアントライブラリを使用して、PodのReadinessのためにカスタムのPodのConditionを設定するコードを記述できます。

カスタムConditionを使用するPodは、以下の両方が当てはまる場合のみreadyと評価されます。

  • Pod内のすべてのコンテナがreadyである。
  • readinessGatesに指定されたすべてのConditionがTrueである。

PodのコンテナがReadyでも、カスタムConditionの少なくとも1つが欠落しているかFalseの場合、kubeletはPodのReady Conditionをreason: ReadinessGatesNotReadyとともにstatus: "False"に設定します。

次の項目

5.1.3 - Initコンテナ

このページでは、Initコンテナの概要について説明します。 Initコンテナとは、Pod内でアプリケーションコンテナの前に実行される特別なコンテナです。 Initコンテナには、アプリケーションコンテナのイメージに存在しないユーティリティやセットアップスクリプトを含めることができます。

Podの仕様では、アプリケーションコンテナを記述するcontainers配列と同じ階層に並べて、Initコンテナを指定できます。

Kubernetesでは、サイドカーコンテナは、メインのアプリケーションコンテナよりも前に起動し、実行し続ける コンテナです。 このドキュメントでは、Podの初期化中に実行が完了するコンテナであるInitコンテナについて説明します。

Initコンテナを理解する

Podは、内部で実行される複数のアプリケーションコンテナを持つことができますが、アプリケーションコンテナが起動する前に実行される1つ以上のInitコンテナを持つこともできます。

Initコンテナは下記の項目をのぞいて、通常のコンテナと全く同じです:

  • Initコンテナは常に完了するまで稼働します。
  • 各Initコンテナは、次のInitコンテナが稼働する前に正常に完了しなくてはなりません。

Pod内のInitコンテナが失敗した場合、kubeletは成功するまで、Initコンテナの再起動を繰り返します。 しかし、PodのrestartPolicyがNeverに設定されていて、Podの起動時にInitコンテナが失敗した場合、KubernetesはPod全体を失敗として扱います。

PodにInitコンテナを指定するためには、Podの仕様initContainersフィールドをcontainer項目の配列として追加してください(アプリケーションのcontainersフィールドとそのコンテンツと同様です)。 詳細については、APIリファレンスのコンテナを参照してください。

Initコンテナのステータスは、.status.initContainerStatusesフィールドにコンテナのステータスの配列として返されます(.status.containerStatusesと同様です)。

通常のコンテナとの違い

Initコンテナは、リソース制限、ボリューム、セキュリティ設定などのアプリケーションコンテナの全てのフィールドと機能をサポートしています。 ただし、Initコンテナのリソース要求と制限は、コンテナ間のリソース共有に記載されているように、異なる方法で処理されます。

通常のInitコンテナ(つまり、サイドカーコンテナを除く)は、lifecyclelivenessProbereadinessProbestartupProbeフィールドをサポートしていません。 InitコンテナはPodの準備が完了する前に実行を完了する必要があります。 一方、サイドカーコンテナはPodのライフタイム中は常に実行され続け、一部のProbeを サポートしています。 サイドカーコンテナの詳細については、サイドカーコンテナを参照してください。

単一のPodに対して複数のInitコンテナを指定した場合、kubeletはそれらのInitコンテナを順次実行します。 各Initコンテナは、次のInitコンテナが実行される前に正常に終了する必要があります。 全てのInitコンテナの実行が完了すると、kubeletはPodのアプリケーションコンテナを初期化し、通常通り実行します。

サイドカーコンテナとの違い

Initコンテナは、メインのアプリケーションコンテナが起動する前にタスクを実行して完了します。 サイドカーコンテナとは異なり、Initコンテナはメインコンテナと並行して継続的に実行されることはありません。

Initコンテナは順次実行され完了します。 すべてのInitコンテナが正常に完了するまで、メインコンテナは起動しません。

InitコンテナはlifecyclelivenessProbereadinessProbestartupProbeをサポートしていませんが、サイドカーコンテナはこれらすべてのProbeをサポートしてライフサイクルを制御します。

Initコンテナは、メインのアプリケーションコンテナとリソース(CPU、メモリ、ネットワーク)を共有しますが、直接やり取りすることはありません。 ただし、共有ボリュームを使用してデータの交換を行うことは可能です。

Initコンテナを使用する

Initコンテナはアプリケーションコンテナのイメージとは分離されているため、コンテナの起動に関連したコードにおいていくつかの利点があります:

  • Initコンテナには、アプリケーションイメージに含まれないセットアップ用のユーティリティやカスタムコードを含めることができます。 たとえば、セットアップ時にsedawkpythondigなどのツールを使用するためだけに、別のイメージをFROMしてイメージを作成する必要はありません。
  • アプリケーションイメージをビルドする役割とデプロイする役割は、共同で単一のアプリケーションイメージをビルドする必要がないため、それぞれ独立して実施することができます。
  • 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
    
  • 以下のようなコマンドを使って、Downward APIを介してこのPodをリモートサーバーに登録する:

    curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
    
  • 以下のようなコマンドを使ってアプリケーションコンテナの起動を待機する:

    sleep 60
    
  • Gitリポジトリをボリュームにクローンする。

  • いくつかの値を設定ファイルに配置し、メインのアプリケーションコンテナのための設定ファイルを動的に生成するためのテンプレートツールを実行する。 例えば、そのPodのPOD_IPの値を設定ファイルに配置し、Jinjaを使ってメインのアプリケーションコンテナの設定ファイルを生成する。

Initコンテナの具体的な使用方法

下記の例は、2つのInitコンテナを含むシンプルなPodを定義しています。 1つ目のInitコンテナはmyserviceの起動を、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 init-myservice
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container init-myservice

この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が実行状態に移行したことを確認できます:

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のrestartPolicyがAlwaysに設定されていた場合は、InitコンテナのrestartPolicyはOnFailureとして適用されます。

すべてのInitコンテナが成功するまで、PodはReadyになりません。 InitコンテナのポートはService配下に集約されません。 初期化中のPodはPending状態ですが、条件Initializedはfalseに設定されているはずです。

Podを再起動するとき、またはPodが再起動されたとき、全てのInitコンテナは必ず再度実行されます。

Initコンテナのspecに対する変更は、コンテナイメージフィールドに制限されています。 Initコンテナのimageフィールドを直接変更しても、Podの再起動や再作成はトリガー されません。 ただし、Podがまだ起動していない場合、その変更はPodの起動方法に影響を与える可能性があります。

Podテンプレートの場合、通常はInitコンテナの任意のフィールドを変更できます。 その変更の影響は、Podテンプレートがどこで使用されているかによって異なります。

Initコンテナは何度も再起動、リトライおよび再実行可能であるため、べき等(Idempotent)である必要があります。 特に、emptyDirにファイルを書き込むコードは、書き込み先のファイルがすでに存在している可能性を考慮に入れなければいけません。

Initコンテナは、アプリケーションコンテナが持つすべてのフィールドを持っています。 ただし、KubernetesではreadinessProbeの使用が禁止されています。 これは、Initコンテナでは完了とは別にreadiness状態を定義することができないためです。 この制約は、バリデーション時に強制されます。

Initコンテナが永久に失敗し続けることを防ぐために、Pod上でactiveDeadlineSecondsを使用してください。 activeDeadlineSecondsの設定はInitコンテナが実行中の時間にも適用されます。 ただし、activeDeadlineSecondsはInitコンテナが完了した後にも影響が及ぶため、アプリケーションをJobとしてデプロイする場合にのみ使用することを推奨します。 すでに正しく動作しているPodは、activeDeadlineSecondsを設定すると強制終了されます。

Pod内の各アプリケーションコンテナとInitコンテナの名前はユニークである必要があります。 他のコンテナと同じ名前を共有していた場合、バリデーションエラーが返されます。

コンテナ間のリソース共有

Initコンテナ、サイドカーコンテナ、アプリケーションコンテナの実行順序を考慮すると、リソース使用に関して以下のルールが適用されます:

  • すべてのInitコンテナで定義された特定のリソースの要求または制限のうち、最も高い値が実効Init要求/制限となります。 リソース制限が指定されていない場合、これが最も高い制限であるとみなされます。
  • リソースに対するPodの実効要求/制限は、以下のうち高い方になります。
    • すべてのアプリケーションコンテナのリソース要求/制限の合計
    • リソースに対する実効Init要求/制限
  • スケジューリングは実効要求/制限に基づいて行われます。 つまり、Initコンテナは初期化のためにリソースを予約できますが、これらはPodのライフタイム中は使用されません。
  • Podの実効QoS tierは、Initコンテナとアプリケーションコンテナの両方に適用されるQoS(サービス品質) tierです。

クォータと制限は、実効的なPodの要求と制限に基づいて適用されます。

InitコンテナとLinux cgroups

Linuxでは、Podレベルのコントロールグループ(cgroups)に対するリソース割り当ては、スケジューラーと同様に、実効的なPodの要求と制限に基づいています。

Podの再起動の理由

以下の理由によりPodは再起動し、Initコンテナの再実行を引き起こす可能性があります:

  • Podインフラストラクチャコンテナが再起動された場合。 これは稀なケースであり、ノードへのルートアクセス権を持つ人が実行する必要があります。
  • restartPolicyがAlwaysに設定されている状態でPod内のすべてのコンテナが終了し、再起動が強制され、かつInitコンテナの完了記録がガベージコレクションにより失われた場合。

Initコンテナイメージが変更された場合、またはガベージコレクションによりInitコンテナの完了記録が失われた場合、Podは再起動されません。 これはKubernetes v1.20以降に適用されます。 それ以前のバージョンのKubernetesを使用している場合は、使用しているバージョンのドキュメントを参照してください。

次の項目

詳しく学ぶには、以下を参照してください:

5.1.4 - サイドカーコンテナ

FEATURE STATE: Kubernetes v1.33 (GA)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.33. It was first available in the v1.28 release.

サイドカーコンテナは、同じPod内でメインのアプリケーションコンテナと共に実行されるセカンダリコンテナです。 これらのコンテナは、ロギング、モニタリング、セキュリティ、データ同期などの追加サービスや機能を提供することで、プライマリの アプリケーションコンテナ の機能を強化または拡張するために使用されます。 メインのアプリケーションコードを直接変更する必要はありません。

通常、Pod内にはアプリケーションコンテナが1つだけ含まれます。 例えば、ローカルWebサーバーを必要とするWebアプリケーションがある場合、ローカルWebサーバーがサイドカーであり、Webアプリケーション自体がアプリケーションコンテナです。

Kubernetesにおけるサイドカーコンテナ

Kubernetesは、サイドカーコンテナをInitコンテナの特殊なケースとして実装しています。 サイドカーコンテナはPod起動後も実行され続けます。 このドキュメントでは、Pod起動時にのみ実行されるコンテナを明確に指すために、通常のInitコンテナ という用語を使用します。

クラスターでSidecarContainersフィーチャーゲートが有効になっている場合(この機能はKubernetes v1.29以降デフォルトで有効です)、PodのinitContainersフィールドにリストされているコンテナに対してrestartPolicyを指定できます。 これらの再起動可能な サイドカー コンテナは、同じPod内の他のInitコンテナやメインアプリケーションコンテナから独立しています。 メインアプリケーションコンテナや他のInitコンテナに影響を与えることなく、これらを起動、停止、または再起動することができます。

また、Initコンテナやサイドカーコンテナとして定義されていない複数のコンテナでPodを実行することもできます。 これは、Pod内のコンテナがPod全体の動作に必要であるが、どのコンテナを最初に起動または停止するかを制御する必要がない場合に適しています。 また、コンテナレベルのrestartPolicyフィールドをサポートしていない古いバージョンのKubernetesをサポートする必要がある場合にも、この方法を使用できます。

アプリケーションの例

以下は、2つのコンテナを持つDeploymentの例で、そのうちの1つがサイドカーコンテナです:

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: {}

サイドカーコンテナとPodのライフサイクル

InitコンテナがrestartPolicyAlwaysに設定して作成された場合、Podの全ライフサイクルを通じて起動し、実行され続けます。 これは、メインのアプリケーションコンテナから分離した補助的なサービスを実行する際に役立ちます。

このInitコンテナにreadinessProbeが指定されている場合、その結果はPodのready状態を判定するために使用されます。

これらのコンテナはInitコンテナとして定義されているため、通常のInitコンテナと同じ順序と順次実行の保証の恩恵を受けます。 これにより、複雑なPod初期化フローの際に、サイドカーコンテナと通常のInitコンテナを混在させることができます。

通常のInitコンテナと比較して、initContainers内で定義されたサイドカーコンテナは起動後も実行され続けます。 これは、Podの.spec.initContainers内に複数のエントリがある場合に重要です。 サイドカー形式のInitコンテナが実行状態になった後(kubeletがそのInitコンテナのstartedステータスをtrueに設定した後)、kubeletは順序付けられた.spec.initContainersリストから次のInitコンテナを起動します。 このステータスは、コンテナ内でプロセスが実行されておりStartup Probeが定義されていない場合、またはstartupProbeが成功した結果として、trueになります。

Podの終了時には、kubeletはメインのアプリケーションコンテナが完全に停止するまで、サイドカーコンテナの終了を引き延ばします。 その後、サイドカーコンテナはPodの仕様内で定義された順序と逆の順序でシャットダウンされます。 このアプローチにより、サイドカーコンテナは、そのサービスが不要になるまで、Pod内の他のコンテナをサポートし続けることが保証されます。

サイドカーコンテナを持つJob

Kubernetes形式のInitコンテナを使用してサイドカーコンテナを使用するJobを定義した場合、各Pod内のサイドカーコンテナは、メインコンテナが終了した後にJobが完了することを妨げません。

以下は、2つのコンテナを持つJobの例で、そのうちの1つがサイドカーコンテナです:

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内で アプリケーションコンテナ と並行して実行されます。 ただし、サイドカーコンテナは主要なアプリケーションロジックを実行するのではなく、メインアプリケーションに補助的な機能を提供します。

サイドカーコンテナは独自の独立したライフサイクルを持ちます。 アプリケーションコンテナとは独立して、起動、停止、再起動できます。 これは、メインアプリケーションに影響を与えることなく、サイドカーコンテナを更新、スケール、またはメンテナンスできることを意味します。

サイドカーコンテナは、プライマリコンテナと同じネットワークおよびストレージ名前空間を共有します。 このように共存することで、密接に相互作用しリソースを共有できます。

Kubernetesの観点からは、サイドカーコンテナのグレースフルな終了はそれほど重要ではありません。 他のコンテナが、割り当てられたグレースフルな終了時間をすべて消費した場合、サイドカーコンテナはグレースフルに終了する時間を持つ前に、SIGTERMシグナルに続いてSIGKILLシグナルを受信します。 そのため、サイドカーコンテナにおいては、Pod終了時の0以外の終了コード(0は正常終了を示します)は正常なものであり、一般的に外部ツールによって無視されるべきです。

Initコンテナとの違い

サイドカーコンテナはメインコンテナと並行して動作し、その機能を拡張して追加のサービスを提供します。

サイドカーコンテナは、メインのアプリケーションコンテナと同時に実行されます。 サイドカーコンテナはPodのライフサイクル全体を通じてアクティブであり、メインコンテナとは独立して起動および停止できます。 Initコンテナとは異なり、サイドカーコンテナは、ライフサイクルを制御するためのProbeをサポートしています。

サイドカーコンテナは、メインのアプリケーションコンテナと直接やり取りできます。 これは、Initコンテナと同様に常に同じネットワークを共有し、オプションでボリューム(ファイルシステム)も共有できるためです。

Initコンテナはメインコンテナが起動する前に停止するため、InitコンテナはPod内のアプリケーションコンテナとメッセージを交換できません。 データの受け渡しは一方向です(例えば、InitコンテナがemptyDirボリューム内に情報を配置することはできます)。

サイドカーコンテナのイメージを変更してもPodは再起動されませんが、コンテナの再起動はトリガーされます。

コンテナ内でのリソース共有

Initコンテナ、サイドカーコンテナ、アプリケーションコンテナの実行順序を考慮すると、リソース使用に関して以下のルールが適用されます:

  • すべてのInitコンテナで定義された特定のリソース要求または制限のうち、最も高い値が実効Init要求/制限となります。 いずれかのリソースにリソース制限が指定されていない場合、これが最も高い制限と見なされます。
  • リソースに対するPodの実効要求/制限は、Podのオーバーヘッドと以下のうち高い方の合計です:
    • すべての非Initコンテナ(アプリケーションコンテナとサイドカーコンテナ)のリソース要求/制限の合計
    • リソースに対する実効Init要求/制限
  • スケジューリングは実効要求/制限に基づいて行われます。 これは、InitコンテナがPodのライフタイム中には使用されない初期化用のリソースを予約できることを意味します。
  • Podの実効QoS tierのQoS(サービス品質) tierは、Initコンテナ、サイドカーコンテナ、アプリケーションコンテナすべてに対するQoS tierです。

クォータと制限は、実効Pod要求と制限に基づいて適用されます。

サイドカーコンテナとLinux cgroup

Linuxでは、Podレベルのコントロールグループ(cgroup)に対するリソース割り当ては、スケジューラーと同様に、実効的なPod要求/制限に基づいて行われます。

次の項目

5.1.5 - エフェメラルコンテナ

FEATURE STATE: Kubernetes v1.25 (GA)

このページでは、特別な種類のコンテナであるエフェメラルコンテナの概要を説明します。エフェメラルコンテナは、トラブルシューティングなどのユーザーが開始するアクションを実行するために、すでに存在するPod内で一時的に実行するコンテナです。エフェメラルコンテナは、アプリケーションの構築ではなく、serviceの調査のために利用します。

エフェメラルコンテナを理解する

Podは、Kubernetesのアプリケーションの基本的なビルディングブロックです。Podは破棄可能かつ置き換え可能であることが想定されているため、一度Podが作成されると新しいコンテナを追加することはできません。その代わりに、通常はDeploymentを使用してPodを削除して置き換えます。

たとえば、再現困難なバグのトラブルシューティングなどのために、すでに存在するPodの状態を調査する必要が出てくることがあります。このような場合、既存のPod内でエフェメラルコンテナを実行することで、Podの状態を調査したり、任意のコマンドを実行したりできます。

エフェメラルコンテナとは何か?

エフェメラルコンテナは、他のコンテナと異なり、リソースや実行が保証されず、自動的に再起動されることも決してないため、アプリケーションを構築する目的には適しません。エフェメラルコンテナは、通常のコンテナと同じContainerSpecで記述されますが、多くのフィールドに互換性がなかったり、使用できなくなっています。

  • エフェメラルコンテナはポートを持つことができないため、portslivenessProbereadinessProbeなどは使えなくなっています。
  • Podリソースの割り当てはイミュータブルであるため、resourcesの設定が禁止されています。
  • 利用が許可されているフィールドの一覧については、EphemeralContainerのリファレンスドキュメントを参照してください。

エフェメラルコンテナは、直接pod.specに追加するのではなく、API内の特別なephemeralcontainersハンドラを使用して作成します。そのため、エフェメラルコンテナをkubectl editを使用して追加することはできません。

エフェメラルコンテナをPodに追加した後は、通常のコンテナのようにエフェメラルコンテナを変更または削除することはできません。

備考:

エフェメラルコンテナは、static Podではサポートされていません。

エフェメラルコンテナの用途

エフェメラルコンテナは、コンテナがクラッシュしてしまったり、コンテナイメージにデバッグ用ユーティリティが同梱されていない場合など、kubectl execでは不十分なときにインタラクティブなトラブルシューティングを行うために役立ちます。

特に、distrolessイメージを利用すると、攻撃対象領域を減らし、バグや脆弱性を露出する可能性を減らせる最小のコンテナイメージをデプロイできるようになります。distrolessイメージにはシェルもデバッグ用のユーティリティも含まれないため、kubectl execのみを使用してdistrolessイメージのトラブルシューティングを行うのは困難です。

エフェメラルコンテナを利用する場合には、他のコンテナ内のプロセスにアクセスできるように、プロセス名前空間の共有を有効にすると便利です。

次の項目

5.1.6 - Disruption

このガイドは、高可用性アプリケーションを構築したいと考えており、そのために、Podに対してどのような種類のDisruptionが発生する可能性があるか理解する必要がある、アプリケーション所有者を対象としたものです。

また、クラスターのアップグレードやオートスケーリングなどのクラスターの操作を自動化したいクラスター管理者も対象にしています。

自発的なDisruptionと非自発的なDisruption

Podは誰か(人やコントローラー)が破壊するか、避けることができないハードウェアまたはシステムソフトウェアエラーが発生するまで、消えることはありません。

これらの不可避なケースをアプリケーションに対する非自発的なDisruptionと呼びます。 例えば:

  • ノードのバックエンドの物理マシンのハードウェア障害
  • クラスター管理者が誤ってVM(インスタンス)を削除した
  • クラウドプロバイダーまたはハイパーバイザーの障害によってVMが消えた
  • カーネルパニック
  • クラスターネットワークパーティションが原因でクラスターからノードが消えた
  • ノードのリソース不足によるPodの退避

リソース不足を除いて、これら条件は全て、大半のユーザーにとって馴染みのあるものでしょう。 これらはKubernetesに固有のものではありません。

それ以外のケースのことを自発的なDisruptionと呼びます。 これらはアプリケーションの所有者によって起こされたアクションと、クラスター管理者によって起こされたアクションの両方を含みます。 典型的なアプリケーションの所有者によるアクションには次のものがあります:

  • Deploymentやその他のPodを管理するコントローラーの削除
  • 再起動を伴うDeployment内のPodのテンプレートの更新
  • Podの直接削除(例:アクシデントによって)

クラスター管理者のアクションには、次のようなものが含まれます:

  • 修復やアップグレードのためのノードのドレイン
  • クラスターのスケールダウンのためにクラスターからノードをドレインする(クラスター自動スケーリングについて学ぶ)。
  • そのノードに別のものを割り当てることができるように、ノードからPodを削除する。

これらのアクションはクラスター管理者によって直接実行されるか、クラスター管理者やクラスターをホスティングしているプロバイダーによって自動的に実行される可能性があります。

クラスターに対して自発的なDisruptionの要因となるものが有効になっているかどうかについては、クラスター管理者に聞くか、クラウドプロバイダーに相談または配布文書を参照してください。 有効になっているものが何もなければ、Pod Disruption Budgetの作成はスキップすることができます。

注意:

全ての自発的なDisruptionがPod Disruption Budgetによる制約を受けるわけではありません。 例えばDeploymentやPodの削除は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 (GA)

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-1 node-2 node-3
pod-a available pod-b available pod-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 draining node-2 node-3
pod-a terminating pod-b available pod-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 draining node-2 node-3
pod-a terminating pod-b available pod-c available
pod-x terminating pod-d starting pod-y

ある時点でPodは終了し、クラスターはこのようになります:

node-1 drained node-2 node-3
pod-b available pod-c available
pod-d starting pod-y

この時点で、せっかちなクラスター管理者がnode-2node-3をドレインしようとすると、Deploymentの利用可能なPodは2つしかなく、また、PDBによって最低2つのPodが要求されているため、drainコマンドはブロックされます。 しばらくすると、pod-dが使用可能になります。

クラスターの状態はこのようになります:

node-1 drained node-2 node-3
pod-b available pod-c available
pod-d available pod-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 drained node-2 node-3 no node
pod-b terminating pod-c available pod-e pending
pod-d available pod-y

この時点で、クラスター管理者はアップグレードを継続するためにクラスターにノードを追加する必要があります。

KubernetesがどのようにDisruptionの発生率を変化させているかについては、次のようなものから知ることができます:

  • いくつのレプリカをアプリケーションが必要としているか
  • インスタンスのグレースフルシャットダウンにどれくらいの時間がかかるか
  • 新しいインスタンスのスタートアップにどれくらいの時間がかかるか
  • コントローラーの種類
  • クラスターリソースのキャパシティ

Pod Disruption Condition

FEATURE STATE: Kubernetes v1.26 (Beta)

備考:

この機能を使用するためには、クラスターでフィーチャーゲートPodDisruptionConditionsを有効にする必要があります。

有効にすると、専用の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によって終了させられました。

備考:

PodのDisruptionは一時停止する場合があります。 コントロールプレーンは同じPodに対するDisruptionを継続するために再試行するかもしれませんが、保証はされていません。 その結果、DisruptionTarget ConditionはPodに付与されるかもしれませんが、実際にはPodは削除されていない可能性があります。 そのような状況の場合、しばらくすると、Pod Disruption Conditionはクリアされます。

フィーチャーゲート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に耐えうるために必要な作業とほぼ重複しています。

次の項目

5.1.7 - Pod Quality of Serviceクラス

このページでは、Kubernetesにおける Quality of Service(QoS)クラス を紹介し、Pod内のコンテナに指定したリソース制約に応じて、KubernetesがどのようにPodにQoSクラスを割り当てるのかについて説明します。 Kubernetesは、ノード上で利用可能なリソースが不足した際に、どのPodを退避させるかを決定するために、このクラスを利用します。

Quality of Serviceクラス

Kubernetesは、実行中のPodを分類し、各Podを特定の Quality of Service(QoS)クラス に割り当てます。 Kubernetesは、このクラスを用いてそれぞれのPodの扱い方を決定します。 分類は、Pod内のコンテナリソース要求と、それらの要求とリソース制限との関連性に基づいて行われます。 これはQuality of Service(QoS)クラスと呼ばれます。 Kubernetesは、Podのコンポーネントであるコンテナのリソース要求と制限に基づいて、すべてのPodにQoSクラスを割り当てます。 QoSクラスは、ノードの圧迫が発生しているノードからどのPodを退避させるかを決定する際に使用されます。 QoSクラスにはGuaranteedBurstableBestEffortがあります。 ノードのリソースが不足すると、KubernetesはまずBestEffort Podを退避し、次にBurstable、最後にGuaranteed Podを退避させます。 リソースの圧迫による退避の場合、リソース要求を超過しているPodのみが退避の候補となります。

Guaranteed

GuaranteedのPodは最も厳しいリソース制限を持ち、退避される可能性が最も低いです。 制限を超過するか、ノードからプリエンプト可能なより低い優先度のPodが存在しない限り、強制終了されることはありません。 ただし、指定された制限を超えてリソースを取得することはできません。 これらのPodは、static CPU管理ポリシーを使って、排他的にCPUを利用することもできます。

条件

PodがGuaranteed QoSクラスとして分類されるための条件は以下の通りです:

  • Pod内のすべてのコンテナが、メモリ制限とメモリ要求を持っていること。
  • Pod内のすべてのコンテナで、メモリ制限がメモリ要求と等しいこと。
  • Pod内のすべてのコンテナが、CPU制限とCPU要求を持っていること。
  • Pod内のすべてのコンテナで、CPU制限がCPU要求と等しいこと。

もしくは、PodがPodレベルのリソースを使用する場合は以下の通りです:

FEATURE STATE: Kubernetes v1.34 (Beta; (デフォルトで有効))
  • PodがPodレベルのメモリ制限とメモリ要求を持ち、それらの値が等しいこと。
  • PodがPodレベルのCPU制限とCPU要求を持ち、それらの値が等しいこと。

Burstable

BurstableのPodは、要求に基づく下限のリソース保証を持ちますが、特定の制限は必要としません。 制限が指定されていない場合、デフォルトでノードの容量と同等の制限となり、リソースが利用可能であればPodは柔軟にリソースを増やすことができます。 ノードのリソース圧迫によるPod退避の際、これらのPodは、すべてのBestEffort Podが退避されてから退避されます。 Burstable Podには、リソース制限や要求を持たないコンテナを含めることができるため、BurstableなPodは任意の量のノードリソースを使おうとする可能性があります。

条件

以下の場合、PodはBurstable QoSクラスとして分類されます:

  • PodがGuaranteed QoSクラスの条件を満たさないこと。
  • Pod内の少なくとも1つのコンテナがメモリまたはCPUの要求または制限を持つか、PodがPodレベルのメモリまたはCPUの要求または制限を持つこと。

BestEffort

BestEffort QoSクラスのPodは、他のQoSクラスのPodに明示的に割り当てられていないノードリソースを使用できます。 たとえば、kubeletで利用可能な16個のCPUコアを持つノードがあり、Guaranteed Podに4個のCPUコアを割り当てた場合、BestEffort QoSクラスのPodは、残りの12個のCPUコアのうち任意の量を使うことができます。

kubeletは、ノードがリソース圧迫を受けた場合、BestEffort Podを優先的に退避させます。

条件

Podは、GuaranteedまたはBurstableのいずれの条件も満たさない場合、BestEffort QoSクラスになります。 つまり、Pod内のどのコンテナもメモリ制限またはメモリ要求を持たず、Pod内のどのコンテナもCPU制限またはCPU要求を持たず、PodがPodレベルのメモリまたはCPUの制限または要求を持たない場合にのみ、PodはBestEffortとなります。 Pod内のコンテナは、(CPUまたはメモリ以外の)他のリソースを要求していても、BestEffortとして分類されます。

cgroup v2を使用したメモリQoS

FEATURE STATE: Kubernetes v1.22 (Alpha; (デフォルトで無効))
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the MemoryQoS feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

メモリQoSは、cgroup v2のメモリコントローラーを使用して、Kubernetesでメモリリソースを保証します。 Pod内のコンテナのメモリ要求と制限は、メモリコントローラーが提供するmemory.minmemory.highインターフェースの設定に使用されます。 memory.minがメモリ要求に設定されると、メモリリソースは予約され、カーネルによって回収されることはありません。 これが、メモリQoSがKubernetes Podのメモリ可用性を保証する仕組みです。 また、コンテナでメモリ制限が設定されている場合、システムはコンテナのメモリ使用量を制限する必要があります。 メモリQoSは、memory.highを使用してメモリ制限に近づいているワークロードの動作を抑制し、瞬間的なメモリ割り当てによってシステムが圧迫されないようにします。

メモリQoSは、QoSクラスに基づいてどの設定を適用するか決定しますが、これらは異なるメカニズムであり、どちらもQuality of Serviceに対する制御を提供します。

QoSクラスに依存しない動作

Kubernetesによって割り当てられたQoSクラスとは無関係な動作もあります。 例えば、以下が該当します:

  • リソース制限を超過したコンテナは、そのPod内の他のコンテナに影響を与えることなく、kubeletによって強制終了され、再起動されます。

  • コンテナがリソース要求を超過し、実行しているノードがリソース圧迫に直面している場合、そのコンテナが含まれるPodは退避の候補となります。 このような場合、Pod内のすべてのコンテナが終了されます。 Kubernetesは、通常は別のノード上に、置き換えとなるPodを作成する可能性があります。

  • Podのリソース要求は、コンポーネントであるコンテナのリソース要求の合計に等しく、Podのリソース制限は、コンポーネントであるコンテナのリソース制限の合計に等しくなります。

  • kube-schedulerは、どのPodをプリエンプトするかを選択する際に、QoSクラスを考慮しません。 プリエンプションは、クラスター内に、定義したすべてのPodを実行するのに十分なリソースがない場合に発生する可能性があります。

次の項目

5.1.8 - Podのホスト名

このページでは、Podのホスト名を設定する方法、その設定後に起こり得る副作用、そして基盤となる仕組みについて説明します。

Podのデフォルトのホスト名

Podが作成されると、(Pod内部から観測される)そのホスト名は、Podのmetadata.nameの値から導き出されます。 ホスト名と、それに対応する完全修飾ドメイン名(FQDN)の両方が(Podの視点からは)metadata.nameの値に設定されます。

apiVersion: v1
kind: Pod
metadata:
  name: busybox-1
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

このmanifestで作成されたPodは、ホスト名と完全修飾ドメイン名(FQDN)がbusybox-1に設定されます。

Podのhostnameとsubdomainフィールド

Podのspecには、オプションのhostnameフィールドがあります。 この値が設定されると、Podのmetadata.nameよりも優先され、(Pod内部から観測される)ホスト名として使われます。 例えば、spec.hostnameがmy-hostに設定されているPodは、ホスト名がmy-hostです。

また、Podのspecにはオプションのsubdomainフィールドもあり、Podが自分のNamespace内のサブドメインに属していることを示します。 もしPodのspec.hostnameが"foo"、spec.subdomainが"bar"に設定され、さらにNamespaceがmy-namespaceの場合、ホスト名はfooで、完全修飾ドメイン名(FQDN)は(Podの内部から観測される)foo.bar.my-namespace.svc.cluster-domain.exampleです。

hostnameとsubdomainの両方が設定されていると、クラスターのDNSサーバーはこれらのフィールドに基づいてA/AAAAレコードを作成します。 Podのhostnameとsubdomainフィールドを参照してください。

PodのsetHostnameAsFQDNフィールド

FEATURE STATE: Kubernetes v1.22 (GA)

Podが完全修飾ドメイン名(FQDN)を持つように設定されている場合、そのホスト名は短いホスト名です。 例えば、Podの完全修飾ドメイン名がbusybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.exampleの場合、デフォルトではそのPod内でhostnameコマンドを実行するとbusybox-1が返り、hostname --fqdnコマンドを実行するとFQDNが返ります。

setHostnameAsFQDN: trueとsubdomainフィールドがPodのspecに設定されている場合、kubeletはそのPodのNamespaceに対してFQDNをホスト名として書き込みます。 この場合、hostnamehostname --fqdnの両方がPodのFQDNを返します。

PodのFQDNは前述と同じ方法で構築されます。 つまり、Podのspec.hostname(設定されている場合)またはmetadata.nameフィールド、spec.subdomainnamespace名、そしてクラスタードメインサフィックスで構成されます。

備考:

Linuxでは、kernelのhostnameフィールド(struct utsnamenodenameフィールド)は64文字に制限されています。

Podがこの機能を有効にし、そのFQDNが64文字を超える場合、起動に失敗します。 そのPodはPendingステータスのままになり(kubectlからはContainerCreatingと表示)、"Failed to construct FQDN from Pod hostname and cluster domain"などのエラーイベントが生成されます。

つまり、このフィールドを使う場合、Podのmetadata.name(またはspec.hostname)とspec.subdomainフィールドを組み合わせた長さが64文字を超えないようにする必要があります。

PodのhostnameOverride

FEATURE STATE: Kubernetes v1.35 (Beta; (デフォルトで有効))

PodのspecでhostnameOverrideに値を設定すると、kubeletは無条件にその値をPodのホスト名とFQDN両方に設定します。

hostnameOverrideフィールドには64文字の長さ制限があり、RFC 1123で定義されているDNSのサブドメイン名の基準に従う必要があります。

例:

apiVersion: v1
kind: Pod
metadata:
  name: busybox-2-busybox-example-domain
spec:
  hostnameOverride: busybox-2.busybox.example.domain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

備考:

これはPod内のホスト名にのみ影響し、クラスターのDNSサーバーにおけるPodのAレコードやAAAAレコードには影響しません。

hostnameOverridehostnamesubdomainフィールドと同時に設定されている場合:

  • Pod内のホスト名はhostnameOverrideの値に上書きされます。

  • クラスターのDNSサーバーにおけるPodのA/AAAAレコードは、hostnamesubdomainフィールドに基づいて引き続き生成されます。

備考: hostnameOverrideが設定されている場合、hostNetworksetHostnameAsFQDNフィールドを同時に設定することはできません。 APIサーバーは、この組み合わせで作成要求が行われた場合、明示的に拒否します。

hostnameOverrideが他のフィールド(hostname、subdomain、setHostnameAsFQDN、hostNetwork)と組み合わされた時の動作の詳細については、KEP-4762の設計詳細の表を参照してください。

5.1.9 - Workload参照

FEATURE STATE: Kubernetes v1.35 (Alpha; (デフォルトで無効))
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

PodをWorkloadオブジェクトに紐づけることで、そのPodがより大きなアプリケーションやグループに属していることを示すことができます。 これにより、スケジューラーは各Podを独立したエンティティとして扱うのではなく、グループとしての要件を考慮してスケジューリングを行います。

Workload参照の指定

GenericWorkloadフィーチャーゲートが有効な場合、Podマニフェストでspec.workloadRefフィールドを使用できます。 このフィールドは、同じ名前空間内のWorkloadリソースで定義された特定のPodグループへの紐づけを行います。

apiVersion: v1
kind: Pod
metadata:
  name: worker-0
  namespace: some-ns
spec:
  workloadRef:
    # 同じ名前空間内のWorkloadオブジェクトの名前
    name: training-job-workload
    # このWorkload内の特定のPodグループの名前
    podGroup: workers

Podグループのレプリカ

より複雑なシナリオでは、単一のPodグループを複数の独立したスケジューリング単位に複製できます。 これは、PodのworkloadRef内でpodGroupReplicaKeyフィールドを使用して実現します。 このキーはラベルとして機能し、論理的なサブグループを作成します。

たとえば、minCount: 2のPodグループがあり、4つのPodを作成する場合を考えます。 2つにpodGroupReplicaKey: "0"を、残り2つにpodGroupReplicaKey: "1"を設定すると、それぞれ2つのPodから構成される独立した2つのグループとして扱われます。

spec:
  workloadRef:
    name: training-job-workload
    podGroup: workers
    # レプリカキー"0"を持つすべてのworkerは、1つのグループとして一緒にスケジュールされます
    podGroupReplicaKey: "0"

動作

workloadRefを定義すると、Podは参照先のPodグループで定義されたポリシーに応じて異なる動作をします。

  • 参照先のグループがbasicポリシーを使用している場合、Workload参照は主にグループ化のためのラベルとして機能します。
  • 参照先のグループがgangポリシーを使用している場合(かつGangSchedulingフィーチャーゲートが有効な場合)、Podはgangスケジューリングのライフサイクルに入ります。 この場合、Podはノードにバインドされる前に、グループ内の他のPodが作成され、スケジュールされるのを待ちます。

参照が存在しない場合

スケジューラーは、配置を決定する前にworkloadRefを検証します。

Podが、存在しないWorkloadを参照している場合、またはそのWorkload内で定義されていないPodグループを参照している場合、Podは保留状態のままになります。 存在しないWorkloadオブジェクトを作成するか、不足しているPodGroup定義を含んだWorkloadを再作成するまで、配置の対象とはみなされません。

この動作は、最終的なポリシーがbasicgangかに関係なく、workloadRefを持つすべてのPodに適用されます。 スケジューラーはポリシーを決定するためにWorkload定義を必要とするためです。

次の項目

5.1.10 - Static Pod

Static Pod は、APIサーバーによる監視を受けることなく、特定のノード上のkubeletデーモンによって直接管理されます。 コントロールプレーンによって管理されるPod(たとえばDeployment)とは異なり、kubeletが各Static Podを監視し、障害が発生した場合には再起動します。

Static Podは、常に特定のノード上の1つのkubeletに紐付けられます。

Static Podの主な用途は、セルフホスト型のコントロールプレーンを実行することです。 言い換えると、kubeletを使用して個々のコントロールプレーンコンポーネントを管理することです。 たとえば、kubeadmはStatic Podを使用して、コントロールプレーンのノード上でkube-apiserverkube-controller-managerkube-scheduleretcdを実行します。

備考:

もしクラスターがコントロールプレーンコンポーネントをPodとして実行している場合、それらはおそらくStatic Podです。 これらのミラーPodは、kube-system名前空間内でkubernetes.io/config.mirrorアノテーションによって識別できます。

ミラーPod

kubeletは、各Static Podに対応するミラーPodを、Kubernetes APIサーバー上に自動的に作成しようとします。 これにより、ノード上で実行されているPodはAPIサーバー上で参照できるようになりますが、APIサーバーから制御することはできません。 Pod名には、先頭にハイフンを付けたノードのホスト名がサフィックスとして付加されます。

kubeletは、Static PodからラベルをミラーPodへ伝播します。 これらのラベルは、セレクターを通じて通常どおり使用できます。

kubectlを使用してAPIサーバーからミラーPodを削除しようとしても、kubeletはStatic Podを削除 しません。 kubeletはミラーPodを再作成します。

制限事項

Static Podのspecは、ServiceAccountConfigMapSecretなどの他のAPIオブジェクトを参照できません。

Static Podは、エフェメラルコンテナをサポートしていません。

Static PodとDaemonSetの比較

クラスター化されたKubernetesを実行していて、すべてのノードでPodを実行するためにStatic Podを使用している場合は、代わりにDaemonSetを使用するべきでしょう。

Static Podはコントロールプレーンによって管理されないため、Kubernetesの標準的な仕組みを使用してロールアウト、ロールバック、スケールを行うことはできません。 DaemonSetはこれらの機能を提供しており、ノードレベルのワークロードを実行するための推奨される方法です。

Static Podは、APIサーバーが利用可能になる前にkubeletによって起動されるため、コントロールプレーンコンポーネントのブートストラップに適しています。 DaemonSetは、稼働中のコントロールプレーンを必要とします。

次の項目

5.1.11 - ユーザー名前空間

FEATURE STATE: Kubernetes v1.30 (Beta)

このページでは、KubernetesのPodにおけるユーザー名前空間について説明します。 ユーザー名前空間はホストのユーザーとコンテナ内プロセスが利用するユーザーを隔離するものです。

ユーザー名前空間を使うと、コンテナ内でrootとして稼働するプロセスを、ホスト側の異なる(root以外の)ユーザーとして実行することができます。 言い換えれば、ユーザー名前空間内部のリソースの操作に特権をもつプロセスは、名前空間の外側では非特権のプロセスとなっています。

ホストや他のPodに危害を及ぼす、侵害されたコンテナによる被害を軽減するために、この機能を用いることができます。 HIGH ないしは CRITICAL にレートされたいくつかの脆弱性は、ユーザー名前空間が有効な場合には悪用できないものでした。 ユーザー名前空間は、将来の脆弱性を緩和することも期待できます。

始める前に

備考: このセクションでは、Kubernetesが必要とする機能を提供するサードパーティプロジェクトにリンクしています。これらのプロジェクトはアルファベット順に記載されていて、Kubernetesプロジェクトの作者は責任を持ちません。このリストにプロジェクトを追加するには、変更を提出する前にコンテンツガイドをお読みください。詳細はこちら。

この機能はLinux固有であり、Linuxのファイルシステムでidmapマウントがサポートされている必要があります。

  • ノード上で/var/lib/kubelet/pods/(ないしはそのカスタムディレクトリとして設定した場所)でidmapマウントがサポートされている必要があります。
  • Podのボリュームとして使われる全てのファイルシステムがidmapマウントをサポートしている必要があります。

これは、最低でもLinux 6.3以降を利用していて、かつidmapマウントをサポートするtmpfsが必要であることを意味します。 一般的に、いくつかのKubernetesの機能はtmpfsを利用しています。 (デフォルトでは、PodがサービスアカウントトークンやSecretをマウントする時にtmpfsを使っていたりします)。

Linux 6.3でidmapマウントをサポートするポピュラーなファイルシステムはbtrfs、ext4、xfs、fat、tmpfs、overlayfsです。

さらに、コンテナランタイムとその基盤であるOCIランタイムもユーザー名前空間をサポートしている必要があります。 次のOCIランタイムではサポートが提供されています。

  • crun バージョン1.9以上 (推奨バージョンは1.13以上).
  • runc バージョン1.2以上

備考:

いくつかのOCIランタイムには、LinuxのPodでユーザー名前空間を利用するのに必要なサポートが含まれていません。 マネージドKubernetesを利用している場合やOCIランタイムをパッケージとしてダウンロードしてセットアップした場合には、クラスター内のノードがユーザー名前空間をサポートしない可能性があります。

Kubernetesでユーザー名前空間を利用する際、Podでこの機能を使うためにはCRIコンテナランタイム も必要です。

  • containerd: バージョン2.0以上ではコンテナのユーザー名前空間をサポート。
  • CRI-O: バージョン1.25以上でコンテナのユーザー名前空間をサポート。

ユーザー名前空間のサポート状況については、GitHubのissueで確認できます。

導入

Linuxの機能であるユーザー名前空間を用いると、コンテナのユーザーをホスト側の異なるユーザーにマップすることができます。 さらに言えば、ユーザー名前空間においてPodに付与されたケーパビリティ(capability)は、ユーザー名前空間内においてのみ有効で、外側では無効です。

Podはpod.spec.hostUsersフィールドをfalseに設定することで、ユーザー名前空間を使えるようになります。

kubeletはPodに対応する(ホストの)UID/GIDを選択した上で、同一ノードの2つ以上のPodが同じ対応関係にならないよう保証するようにしつつ、ユーザーをマップします。

pod.specにおけるrunAsUserrunAsGroupfsGroupなどのフィールドはコンテナ内のユーザーを指すものです。

この機能が有効化された場合、UID/GIDとして正しい値の範囲は0-65535です。 これはファイルとプロセスに対して適用されます。 (runAsUserrunAsGroupなど)。

この範囲を超えるUID/GIDを利用するファイルはオーバーフローしたID(一般的には65534)に所属するように見えるでしょう。 (/proc/sys/kernel/overflowuid/proc/sys/kernel/overflowgidで設定されます)。 ただし、これらのファイルは65534のユーザー/グループで稼働するプロセスであっても、編集することはできません。

rootとして動かす必要があるアプリケーションであっても、ホストにおける他のユーザー名前空間や他のリソースに対してアクセスしないものの多くは、ユーザー名前空間を有効化しても動かせますし、アプリケーションを修正しなくても問題なく動作するでしょう。

Podにおけるユーザー名前空間を理解する

いくつかのコンテナランタイム(Docker Engineやcontainer、CRI-Oなど)は、デフォルトでユーザー名前空間を利用するように設定されています。

これらのランタイムとその他の既存技術を組み合わせて使うことも可能です。 (例えば、Kata ContainerはLinux名前空間の代わりにVMを利用します)。 このページの内容はLinux名前空間を隔離に使うコンテナランタイムに適用できるものです。

Podを作成する時、デフォルトでは、Podの隔離にいくつかの新しい名前空間が利用されます。 (コンテナネットワークの隔離のためのネットワーク名前空間、プロセスの見える範囲を隔離するためのPID名前空間など)。 ユーザー名前空間を利用する場合には、コンテナ内のユーザーをノードのユーザーと隔離します。

これは、コンテナがrootとしてプロセスを動かせる一方で、ホスト上では非rootユーザーとしてマップされていることを意味します。 コンテナ内部のプロセスはrootとして動作しているものと思っていることでしょう。 (したがって、aptyumなどは問題なく動作します)。 しかし、実際には、このプロセスにホスト上での特権はありません。 これを検証するには、例えばホスト上でps auxを実行することで、コンテナのプロセスがどのユーザーを使用しているかを確認するとよいでしょう。 psが示すユーザーは、コンテナ内でidを実行した場合に示されるユーザーとは異なっているはずです。

この分離によって、ホスト上で「起こせること」を制限できます。例えばコンテナ内プロセスのホストへのエスケープを処理する場合にこの制限が有効に働きます。 コンテナはホスト上で非特権のユーザーとして動作しているため、ホスト上でできることが制限されているのです。

さらに言えば、それぞれのPodのユーザーは、ホスト上においては異なるユーザーにマップされており、UID/GIDは重複しません。 他のPodに対してできることさえも制限されているのです。

Podに付与されたケーパビリティについても、Podのユーザー名前空間に制限されており、名前空間の外部においてはほとんどが効力を持たず、いくつかのケーパビリティは外部では完全に無効です。 2つの例を挙げます:

  • CAP_SYS_MODULE がユーザー名前空間を使うPodに付与されている場合、Podはカーネルモジュールをロードできません。
  • CAP_SYS_ADMIN はPodのユーザー名前空間の内部のみに制限され、名前空間の外部では無効です。

コンテナブレークアウトのケースについて考えてみます。 この場合、ユーザー名前空間を使用せずにコンテナをrootで稼働させていると、ノードのroot権限が取得されます。 さらに、ケーパビリティがコンテナに付与されていた場合には、そのケーパビリティはホスト上でも有効となっています。 ユーザー名前空間を利用していれば、これらはいずれも成立しません。

ユーザー名前空間を使う場合に何が変わるのかについて詳しく知りたい場合には、man 7 user_namespacesを参照してください。

ユーザー名前空間をサポートするノードを設定する

ほとんどのLinuxディストリビューションで標準的なUIDである0-65535の範囲については、kubeletはホストのファイルやプロセスがこの範囲のUIDを利用しているものとみなし、デフォルトではこれよりも上のUID/GIDの値をPodに紐付けます。 言い換えれば、0-65535の範囲のIDをPodで使うことはできません。 このアプローチにより、PodとホストのUID/GIDが重複することを防ぎます。

Podによる潜在的な任意のファイル読み出しの脆弱性であるCVE-2021-25741のような脆弱性の影響を緩和する上で、UID/GIDの重複を防ぐことは重要です。 PodとホストのUID/GIDが重複しなければ、Podができることは限定されます。 (PodのUID/GIDはホスト上のファイル所有者やグループと一致することがないのです)。

kubeletはPodに割り当てるユーザーIDとグループIDの範囲を変更することが可能です。カスタムの範囲を設定するには、ノードが次の条件を満たす必要があります。

  • ユーザーkubeletがシステム上に存在していること(他のユーザー名を使うことはできません)
  • getsubidsバイナリ(shadow-utilsの一部)がインストールされており、kubeletバイナリが参照するPATHに入っていること
  • kubeletユーザーのsubordinate UID/GID (man 5 subuid およびman 5 subgidを参照)

これはsubordinate UID/GIDの範囲に関する設定のみを示しており、kubeletを実行するユーザーは変更しません。

ユーザーkubeletに割り当てるsubordinate IDの範囲に関しては、いくつかの制約に従う必要があります。

  • subordinate UIDの起点(つまりPodのUID範囲の開始位置)が65536の倍数に設定されていて、かつ65536以上であること( 必須 )。 言い換えると、0-65535の範囲をPodのUIDとして使うことはできません。 偶発的にインセキュアな設定がなされることを防ぐために、kubeletはこの制約を強制します。

  • subordinate UID/GIDの個数が65536の倍数であること(必須)。

  • subordinate UID/GIDの個数は最低でも65536 x <最大Pod数>であること(必須)。 <最大Pod数> はノードで稼働できるPodの数の最大値を表します。

  • UIDとGIDとして同じ個数を割り当てること(必須)。 他のユーザーに対して、GIDの範囲と合致しないUID範囲を指定することは問題ありません。

  • 割り当てられたUID/GID範囲は他の割当と重複しないこと(推奨)。

  • subordinate UID/GIDの設定は単一行でなされること(必須)。 同一のユーザーに対して複数のUID/GID範囲を定義することはできません。

例えば、ユーザーkubeletのエントリについて、/etc/subuid/etc/subgidに次のように定義することができます。

# フォーマットは次の通り
#   name:firstID:count
# この例における意味
# - firstIDは65536 (とりうる最小の値)
# - countは110 (デフォルトの制限値) * 65536
kubelet:65536:7208960

Podセキュリティのアドミッション検証への統合

FEATURE STATE: Kubernetes v1.29 (Alpha)

ユーザー名前空間を有効化したLinuxのPodでは、KubernetesはPod Security Standardsで制御されるアプリケーションの制限を緩和します。 この挙動はエンドユーザーの早期オプトインを可能にするためのUserNamespacesPodSecurityStandardsフィーチャーゲートで制御することが可能です。 このフィーチャーゲートを使う場合、クラスター管理者はユーザー名前空間が全てのノードで有効化されていることを確実にする必要があります。

フィーチャーゲートを有効化した上でユーザー名前空間を使うPodを作成する場合、_Baseline_ないしは_Restricted_Podセキュリティ基準のセキュリティコンテキストが強制されていても、以下のフィールドによる制約がなされません。

  • spec.securityContext.runAsNonRoot
  • spec.containers[*].securityContext.runAsNonRoot
  • spec.initContainers[*].securityContext.runAsNonRoot
  • spec.ephemeralContainers[*].securityContext.runAsNonRoot
  • spec.securityContext.runAsUser
  • spec.containers[*].securityContext.runAsUser
  • spec.initContainers[*].securityContext.runAsUser
  • spec.ephemeralContainers[*].securityContext.runAsUser

制限

Podでユーザー名前空間を利用する際には、他のホスト名前空間を利用することはできません。 特にhostUsers: falseを設定している場合、次の値を設定することはできません。

  • hostNetwork: true
  • hostIPC: true
  • hostPID: true

次の項目

5.1.12 - Downward API

Podやコンテナのフィールドを実行中のコンテナに公開する方法には2つあります。 1つは環境変数で、もう1つは特殊なボリュームタイプによってファイルとして公開する方法です。 これら2つの方法をまとめてDownward APIと呼びます。

Kubernetesに過度に密結合することなく、コンテナが自分自身についての情報を持つことは有用な場合があります。 downward API を用いることで、コンテナはKubernetesのクライアントやAPIサーバーを利用せずに、自分自身やクラスターに関する情報を取得することができます。

例として、特定の既知の環境変数に一意な識別子が格納されていることを前提とする既存のアプリケーションがあるとします。 一つの可能性は、アプリケーションをラップすることですが、これは煩雑でエラーが起こりやすく、疎結合という目標に反します。 より良い選択肢は、Pod名を識別子として使用し、Pod名をその既知の環境変数に注入することです。

Kubernetesでは、実行中のコンテナにPodおよびコンテナフィールドを公開する方法が2つあります:

これらPodおよびコンテナフィールドを公開する2つの方法を総称して、downward API と呼びます。

利用可能なフィールド

Kubernetes APIフィールドのうち、downward APIを通じて利用可能なものは一部のみです。 このセクションでは、利用可能なフィールドを列挙します。

利用可能なPodレベルのフィールドからの情報は、fieldRefを使用して渡すことができます。 APIレベルでは、Podのspecは常に少なくとも1つのContainerを定義します。 利用可能なコンテナレベルのフィールドからの情報は、resourceFieldRefを使用して渡すことができます。

fieldRefを通じて利用可能な情報

一部のPodレベルフィールドについては、環境変数として、またはdownwardAPIボリュームを使用して、コンテナに提供することができます。 どちらのメカニズムでも利用可能なフィールドは以下の通りです:

metadata.name
Podの名前
metadata.namespace
Podのネームスペース
metadata.uid
Podの一意ID
metadata.annotations['<KEY>']
<KEY>という名前のPodのアノテーションの値(例: metadata.annotations['myannotation'])
metadata.labels['<KEY>']
<KEY>という名前のPodのラベルのテキスト値(例: metadata.labels['mylabel'])

以下の情報は環境変数を通じて利用可能ですが、downwardAPIボリュームのfieldRefとしては利用できません:

spec.serviceAccountName
Podのサービスアカウントの名前
spec.nodeName
Podが実行されているノードの名前
status.hostIP
Podが割り当てられているノードのプライマリIPアドレス
status.hostIPs
status.hostIPのデュアルスタック版のIPアドレスで、最初のIPアドレスは常にstatus.hostIPと同じです
status.podIP
PodのプライマリIPアドレス(通常、IPv4アドレス)
status.podIPs
status.podIPのデュアルスタック版のIPアドレスで、最初のIPアドレスは常にstatus.podIPと同じです

以下の情報はdownwardAPIボリュームのfieldRefを通じて利用可能ですが、環境変数としては利用できません:

metadata.labels
Podのすべてのラベルで、label-key="escaped-label-value"形式でフォーマットされ、1行に1つのラベルが記載されます
metadata.annotations
Podのすべてのアノテーションで、annotation-key="escaped-annotation-value"形式でフォーマットされ、1行に1つのアノテーションが記載されます

resourceFieldRefを通じて利用可能な情報

これらのコンテナレベルフィールドを使用すると、CPUやメモリなどのリソースの要求と制限に関する情報を提供することができます。

備考:

        <div class="feature-state-notice feature-stable" title="フィーチャーゲート: InPlacePodVerticalScaling">
          <span class="feature-state-name">FEATURE STATE:</span> 
          <span class="feature-state-details">Kubernetes v1.35 
           
             (GA)
           </span>
        </div>
        
        
        <div class="feature-stable">
          
          <details>
          <summary>More information about this feature</summary>
          <p><em>This is a stable feature in Kubernetes, and has been since version 1.35. It was first available in the v1.27 release.</em>
          
          </details>

          
          </div>

コンテナのCPUとメモリリソースは、コンテナの実行中にリサイズすることができます。 この場合、downward APIボリュームは更新されますが、環境変数はコンテナが再起動されない限り更新されません。 詳細については、コンテナに割り当てるCPUとメモリ容量を変更するを参照してください。

resource: limits.cpu
コンテナのCPU制限
resource: requests.cpu
コンテナのCPU要求
resource: limits.memory
コンテナのメモリ制限
resource: requests.memory
コンテナのメモリ要求
resource: limits.hugepages-*
コンテナのhugepages制限
resource: requests.hugepages-*
コンテナのhugepages要求
resource: limits.ephemeral-storage
コンテナの一時ストレージ制限
resource: requests.ephemeral-storage
コンテナの一時ストレージ要求

リソース制限のフォールバック情報

コンテナにCPUとメモリの制限が指定されておらず、downward APIを使用してその情報を公開しようとする場合、kubeletはノード割り当て可能量の計算に基づいて、CPUとメモリの最大割り当て可能値をデフォルトで公開します。

次の項目

downwardAPIボリュームについて詳しく読むことができます。

downward APIを使用してコンテナレベルまたはPodレベルの情報を公開することを試してみることができます:

5.1.13 - 高度なPod設定

このページでは、PriorityClassRuntimeClass、Pod内のセキュリティコンテキストを含む高度なPod設定に関するトピックを扱い、スケジューリングとの関連についても説明します。

PriorityClass

PriorityClass を使用すると、他のPodと比較したPodの重要度を設定することができます。 Podに優先度クラスを割り当てると、Kubernetesは指定したPriorityClassに基づいて、そのPodの.spec.priorityフィールドを設定します(ただし、.spec.priorityを直接設定することはできません)。 Podがスケジュールできず、その問題がリソース不足によるものである場合、kube-schedulerは、より高い優先度のPodのスケジューリングを可能にするため、より低い優先度のPodをプリエンプトしようとします。

PriorityClassは、優先度クラス名を整数の優先度値にマッピングするクラスタースコープのAPIオブジェクトです。 数値が大きいほど高い優先度であることを示します。

PriorityClassを定義する

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 10000
globalDefault: false
description: "Priority class for high-priority workloads"

PriorityClassを使用してPodの優先度を指定する

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  priorityClassName: high-priority

ビルトインのPriorityClass

Kubernetesは2つのビルトインのPriorityClassを提供します:

  • system-cluster-critical: クラスターにとって重要なシステムコンポーネント用。
  • system-node-critical: 個々のノードにとって重要なシステムコンポーネント用。 これはKubernetesでPodが持つことができる最も高い優先度です。

詳細については、Podの優先度とプリエンプションを参照してください。

RuntimeClass

RuntimeClass を使用すると、Podの低レベルなコンテナランタイムを指定できます。 これは、異なる分離レベルやランタイム機能が必要な場合など、異なる種類のPodに対して異なるコンテナランタイムを指定したい場合に役立ちます。

Pod定義の例

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  containers:
  - name: mycontainer
    image: nginx

RuntimeClassは、クラスター内の一部またはすべてのノードで利用可能なコンテナランタイムを表すクラスタースコープのオブジェクトです。

クラスター管理者は、RuntimeClassを支える具体的なランタイムをインストールおよび設定します。

管理者は、その特別なコンテナランタイム設定をすべてのノードに設定する場合もあれば、一部のノードのみに設定する場合もあります。

詳細については、RuntimeClassのドキュメントを参照してください。

Podおよびコンテナレベルのセキュリティコンテキスト設定

Pod仕様内のsecurityContextフィールドは、Podとコンテナのセキュリティ設定をきめ細かく制御できます。

Pod全体のsecurityContext

セキュリティ設定の中には、Pod全体に適用されるものがあります。 その他の設定については、コンテナレベルでオーバーライドせずにデフォルトを設定することもできます。

以下は、PodレベルでPod全体のsecurityContextを使用する例です:

Pod定義の例

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:  # この設定は、Pod全体に適用されます
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: ["sh", "-c", "sleep 1h"]

コンテナレベルのセキュリティコンテキスト

特定のコンテナに対してのみ、セキュリティコンテキストを指定できます。 以下はその例です:

Pod定義の例

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  containers:
  - name: sec-ctx-demo-2
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      allowPrivilegeEscalation: false
      runAsNonRoot: true
      runAsUser: 1000
      capabilities:
        drop:
        - ALL
      seccompProfile:
        type: RuntimeDefault

セキュリティコンテキストのオプション

  • ユーザーIDとグループID: コンテナを実行するユーザー/グループを制御します
  • ケーパビリティ: Linuxケーパビリティを追加または削除します
  • Seccompプロファイル: セキュリティコンピューティングプロファイル(seccomp)を設定します
  • SELinuxオプション: SELinuxコンテキストを設定します
  • AppArmor: 追加のアクセス制御のためにAppArmorプロファイルを設定します
  • Windowsオプション: Windows固有のセキュリティ設定を行います

注意:

PodのsecurityContextを使用して、Linuxコンテナで特権モードを許可することもできます。 特権モードは、securityContextの他の多くのセキュリティ設定を上書きします。 securityContextの他のフィールドを使用して同等の権限を付与できない場合を除き、この設定を使用することは避けてください。 PodレベルのセキュリティコンテキストでwindowsOptions.hostProcessフラグを設定することで、同様の特権モードでWindowsコンテナを実行できます。 詳細とその手順については、Windows HostProcess Podの作成を参照してください。

詳細については、Podまたはコンテナのセキュリティコンテキストの設定を参照してください。

Podのスケジューリングを制御する

Kubernetesは、Podがどのノードにスケジュールされるかを制御するためのいくつかのメカニズムを提供します。

ノードセレクター

最もシンプルな形式のノード選択制約:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    disktype: ssd

ノードアフィニティ

ノードアフィニティを使用すると、Podをスケジュールできるノードを制約するルールを指定できます。 以下は、topology.kubernetes.io/zoneラベルの値に基づいて選択し、特定の大陸(continent)にあるとラベル付けされたノードでの実行を優先するPodの例です。

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
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:3.8

Podアフィニティとアンチアフィニティ

ノードアフィニティに加えて、ノード上ですでに実行されている 他のPod のラベルに基づいて、Podがスケジュールされるノードを制約することもできます。 Podアフィニティを使用すると、他のPodとの位置関係に基づいてPodを配置するルールを指定できます。

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - database
        topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:3.8

Toleration

Toleration を使用すると、一致するTaintを持つノードにPodをスケジュールできます:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: myapp
    image: nginx
  tolerations:
  - key: "key"
    operator: "Equal"
    value: "value"
    effect: "NoSchedule"

詳細については、ノード上へのPodのスケジューリングを参照してください。

Podのオーバーヘッド

Podのオーバーヘッドを使用すると、コンテナのリソース要求とリソース制限に加えて、Podのインフラストラクチャが消費するリソースを考慮できます。

---
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kvisor-runtime
handler: kvisor-runtime
overhead:
  podFixed:
    memory: "2Gi"
    cpu: "500m"
---
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: kvisor-runtime
  containers:
  - name: myapp
    image: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

次の項目

5.2 - Workload API

FEATURE STATE: Kubernetes v1.35 (Alpha; (デフォルトで無効))
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Workload APIリソースを使用すると、複数のPodで構成されるアプリケーションについて、スケジューリング要件とPodのグループ構成を記述できます。 ワークロードコントローラーはワークロードのランタイム動作を提供しますが、Workload APIはJobなどの「真の」ワークロードに対して、スケジューリング制約を提供することを目的としています。

Workloadとは

Workload APIリソースは、scheduling.k8s.io/v1alpha1 APIグループの一部です(このAPIを利用するには、クラスターで、そのAPIグループとGenericWorkloadフィーチャーゲートの両方を有効にする必要があります)。 このリソースは、複数のPodで構成されるアプリケーションのスケジューリング要件を、構造化された機械可読な形式で定義します。 Jobのようなユーザー向けのワークロードは何を実行するかを定義します。 一方で、Workloadリソースは、Podのグループをどのようにスケジュールし、ライフサイクル全体を通じてその配置をどう管理するかを決定します。

APIの構造

Workloadを使用すると、Podのグループを定義し、それらにスケジューリングポリシーを適用できます。 これは、Podグループのリストとコントローラーへの参照という2つのセクションで構成されます。

Podグループ

podGroupsリストは、ワークロードの個別のコンポーネントを定義します。 たとえば、機械学習ジョブにはdriverグループとworkerグループがある場合があります。

podGroupsの各エントリには以下が必要です:

  1. PodのWorkload参照で使用できる一意のname
  2. スケジューリングポリシー(basicまたはgang)
apiVersion: scheduling.k8s.io/v1alpha1
kind: Workload
metadata:
  name: training-job-workload
  namespace: some-ns
spec:
  controllerRef:
    apiGroup: batch
    kind: Job
    name: training-job
  podGroups:
  - name: workers
    policy:
      gang:
        # gangは4つのPodが同時に実行できる場合にのみスケジュール可能
        minCount: 4

ワークロード管理オブジェクトの参照

controllerRefフィールドは、WorkloadをJobやカスタムCRDなど、アプリケーションを定義する上位のオブジェクトに紐づけます。 これは可観測性とツールの利用に役立ちます。 このデータは、Workloadのスケジューリングや管理には使用されません。

次の項目

5.2.1 - Podグループポリシー

FEATURE STATE: Kubernetes v1.35 (Alpha; (デフォルトで無効))
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Workloadで定義される各Podグループは、スケジューリングポリシーを宣言する必要があります。 このポリシーは、スケジューラーがPodのグループをどのように扱うかを指定します。

ポリシータイプ

現在、APIはbasicgangの2つのポリシータイプをサポートしています。 各グループに対して、いずれか1つのポリシーを指定する必要があります。

basicポリシー

basicポリシーは、グループ内のすべてのPodを独立したエンティティとして扱い、標準的なKubernetesの動作でスケジューリングするようスケジューラーに指示します。

basicポリシーを使用する主な理由は、Workload内のPodを整理して、可観測性と管理性を向上させることです。 このポリシーは、同時起動を必要としないが、論理的に同じアプリケーションに属するWorkloadのグループに使用できます。 また、将来的に「全か無か」の配置を意味しないグループ制約を追加する余地も残されています。

policy:
  basic: {}

gangポリシー

gangポリシーは、「全か無か」のスケジューリングを強制します。 これは、一部のPodだけが起動すると、デッドロックやリソースの浪費が発生する密結合したワークロードには必須です。

これは、すべてのワーカーが同時に実行されなければ処理が進まないJobや、その他のバッチ処理に使用できます。

gangポリシーにはminCountパラメーターが必要です:

policy:
  gang:
    # グループが受け入れられるために、
    # 同時にスケジュール可能である必要があるPodの数
    minCount: 4

次の項目

5.3 - ワークロード管理

Kubernetesは、ワークロードとそのワークロードのコンポーネントを宣言的に管理するための、いくつかの組み込みAPIを提供しています。

最終的に、アプリケーションはPod内のコンテナとして実行されますが、個々のPodを管理するのは多大な労力を要します。 たとえば、Podが失敗した場合、それを置き換える新しいPodを実行したくなるでしょう。 Kubernetesはそれを代わりに行うことができます。

Kubernetes APIを使用してPodよりも高い抽象度を表すワークロードオブジェクトを作成すると、定義したワークロードオブジェクトの仕様に基づいて、KubernetesのコントロールプレーンがPodオブジェクトを自動的に管理します。

ワークロードを管理するための組み込みAPIは以下のとおりです:

Deployment(および間接的にReplicaSet)は、クラスター上でアプリケーションを実行する最も一般的な方法です。 Deploymentは、Deployment内の任意のPodが交換可能で必要に応じて置き換え可能な、ステートレスなアプリケーションワークロードをクラスター上で管理するのに適しています。 (Deploymentは、レガシーのReplicationController APIを置き換えるものです)。

StatefulSetを使用すると、すべて同じアプリケーションコードを実行する1つ以上のPodを、固有のアイデンティティを持つ前提で管理できます。 これは、Podが交換可能であることを前提とするDeploymentとは異なります。 StatefulSetの最も一般的な用途は、そのPodと永続的なストレージとの紐付けを実現することです。 たとえば、各PodをPersistentVolumeに関連付けるStatefulSetを実行できます。 StatefulSet内のPodの1つが失敗した場合、Kubernetesは同じPersistentVolumeに接続された置き換えのPodを作成します。

DaemonSetは、特定のノードにローカルな機能を提供するPodを定義します。 たとえば、そのノード上のコンテナがストレージシステムにアクセスできるようにするドライバーがそれにあたります。 DaemonSetは、ドライバーやその他のノードレベルのサービスを、それが必要なノード上で動作させなければならない場合に使用します。 DaemonSet内の各Podは、古典的なUnix/POSIXサーバー上のシステムデーモンに似た役割を果たします。 DaemonSetは、ノードがクラスターネットワークにアクセスできるようにするプラグインのようにクラスターの動作に不可欠な場合もあれば、ノードの管理を支援する場合や、実行中のコンテナプラットフォームを強化するそれほど重要でない機能を提供する場合もあります。 DaemonSet(およびそのPod)は、クラスター内のすべてのノードで実行することも、サブセットでのみ実行することもできます(たとえば、GPUがインストールされているノードにのみGPUアクセラレータードライバーをインストールするなど)。

JobCronJobを使用して、完了まで実行されてから停止するタスクを定義できます。 Jobは1回限りのタスクを表し、CronJobはスケジュールに従って繰り返し実行されます。

このセクションのその他のトピック:

5.3.1 - Deployment

DeploymentPodReplicaSetの宣言的なアップデート機能を提供します。

Deploymentにおいて 理想的な状態 を記述すると、Deploymentコントローラーは指定された頻度で現在の状態を理想的な状態に変更します。Deploymentを定義することによって、新しいReplicaSetを作成したり、既存のDeploymentを削除して新しいDeploymentで全てのリソースを適用できます。

備考:

Deploymentによって作成されたReplicaSetを管理しないでください。ご自身のユースケースが以下の項目に含まれない場合、メインのKubernetesリポジトリーにIssueを作成することを検討してください。

ユースケース

以下の項目はDeploymentの典型的なユースケースです。

  • ReplicaSetをロールアウトするためにDeploymentの作成を行う: ReplicaSetはバックグラウンドでPodを作成します。Podの作成が完了したかどうかは、ロールアウトのステータスを確認してください。
  • DeploymentのPodTemplateSpecを更新することによりPodの新しい状態を宣言する: 新しいReplicaSetが作成され、Deploymentは指定された頻度で古いReplicaSetから新しいReplicaSetへのPodの移行を管理します。新しいReplicaSetはDeploymentのリビジョンを更新します。
  • Deploymentの現在の状態が不安定な場合、Deploymentのロールバックをする: ロールバックによる各更新作業は、Deploymentのリビジョンを更新します。
  • より多くの負荷をさばけるように、Deploymentをスケールアップする。
  • PodTemplateSpecに対する複数の修正を適用するためにDeploymentを停止(Pause)し、それを再開して新しいロールアウトを開始します。
  • Deploymentのステータス をロールアウトが失敗したサインとして利用する。
  • 今後必要としない古いReplicaSetのクリーンアップ

Deploymentの作成

以下はDeploymentの例です。これは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自体がそのルールを満たす限り、さらに洗練された方法でセレクターを指定することができます。

    備考:

    `.spec.selector.matchLabels`フィールドはキーバリューペアのマップです。
    `matchLabels`マップにおいて、{key, value}というペアは、keyというフィールドの値が"key"で、その演算子が"In"で、値の配列が"value"のみ含むような`matchExpressions`の要素と等しくなります。
    `matchLabels`と`matchExpressions`の両方が設定された場合、条件に一致するには両方とも満たす必要があります。
    
  • .spec.templateフィールドは、以下のサブフィールドを持ちます。:

    • Podは.metadata.labelsフィールドによって指定されたapp: nginxというラベルがつけられます。
    • PodTemplate、または.specフィールドは、Podがnginxという名前でDocker Hubにあるnginxのバージョン1.14.2が動くコンテナを1つ動かすことを示します。
    • 1つのコンテナを作成し、.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つ作成することを保証します。

備考:

Deploymentに対して適切なセレクターとPodテンプレートのラベルを設定する必要があります(このケースではapp: nginx)。

ラベルやセレクターを他のコントローラーと重複させないでください(他のDeploymentやStatefulSetを含む)。Kubernetesはユーザーがラベルを重複させることを阻止しないため、複数のコントローラーでセレクターの重複が発生すると、コントローラー間で衝突し予期せぬふるまいをすることになります。

pod-template-hashラベル

注意:

このラベルを変更しないでください。

pod-template-hashラベルはDeploymentコントローラーによってDeploymentが作成し適用した各ReplicaSetに対して追加されます。

このラベルはDeploymentが管理するReplicaSetが重複しないことを保証します。このラベルはReplicaSetのPodTemplateをハッシュ化することにより生成され、生成されたハッシュ値はラベル値としてReplicaSetセレクター、Podテンプレートラベル、ReplicaSetが作成した全てのPodに対して追加されます。

Deploymentの更新

備考:

Deploymentのロールアウトは、DeploymentのPodテンプレート(この場合.spec.template)が変更された場合にのみトリガーされます。例えばテンプレートのラベルもしくはコンテナイメージが更新された場合です。Deploymentのスケールのような更新では、ロールアウトはトリガーされません。

Deploymentを更新するには以下のステップに従ってください。

  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つのレプリカを全て作成するのを待つことはありません。

ラベルセレクターの更新

通常、ラベルセレクターを更新することは推奨されません。事前にラベルセレクターの使い方を計画しておきましょう。いかなる場合であっても更新が必要なときは十分に注意を払い、変更時の影響範囲を把握しておきましょう。

備考:

apps/v1API バージョンにおいて、Deploymentのラベルセレクターは作成後に不変となります。
  • セレクターの追加は、Deployment Specのテンプレートラベルも新しいラベルで更新する必要があります。そうでない場合はバリデーションエラーが返されます。この変更は重複がない更新となります。これは新しいセレクターは古いセレクターを持つReplicaSetとPodを選択せず、結果として古い全てのReplicaSetがみなし子状態になり、新しいReplicaSetを作成することを意味します。
  • セレクターの更新により、セレクターキー内の既存の値が変更されます。これにより、セレクターの追加と同じふるまいをします。
  • セレクターの削除により、Deploymentのセレクターから存在している値を削除します。これはPodテンプレートのラベルに関する変更を要求しません。既存のReplicaSetはみなし子状態にならず、新しいReplicaSetは作成されませんが、削除されたラベルは既存のPodとReplicaSetでは残り続けます。

Deploymentのロールバック

例えば、クラッシュループ状態などのようにDeploymentが不安定な場合においては、Deploymentをロールバックしたくなることがあります。Deploymentの全てのロールアウト履歴は、いつでもロールバックできるようにデフォルトでシステムに保持されています(リビジョン履歴の上限は設定することで変更可能です)。

備考:

Deploymentのリビジョンは、Deploymentのロールアウトがトリガーされた時に作成されます。これはDeploymentのPodテンプレート(.spec.template)が変更されたときのみ新しいリビジョンが作成されることを意味します。Deploymentのスケーリングなど、他の種類の更新においてはDeploymentのリビジョンは作成されません。これは手動もしくはオートスケーリングを同時に行うことができるようにするためです。これは過去のリビジョンにロールバックするとき、DeploymentのPodテンプレートの箇所のみロールバックされることを意味します。
  • nginx:1.16.1の代わりにnginx:1.161というイメージに更新して、Deploymentの更新中にタイプミスをしたと仮定します。

    kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    実行結果は以下のとおりです。

    deployment.apps/nginx-deployment image updated
    
  • このロールアウトはうまくいきません。ロールアウトのステータスを見るとそれを確認できます。

    kubectl rollout status deployment.v1.apps/nginx-deployment
    

    実行結果は以下のとおりです。

    Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
    
  • ロールアウトのステータスの確認は、Ctrl-Cを押すことで停止できます。ロールアウトがうまく行かないときは、Deploymentのステータスを読んでさらなる情報を得てください。

  • 古いレプリカ数(nginx-deployment-1564180365 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コントローラーは、この悪い状態のロールアウトを自動的に停止し、新しいReplicaSetのスケールアップを止めます。これはユーザーが指定したローリングアップデートに関するパラメーター(特に`maxUnavailable`)に依存します。デフォルトではKubernetesがこの値を25%に設定します。
    
  • Deploymentの詳細情報を取得します。

    kubectl describe deployment
    

    実行結果は以下のとおりです。

    Name:           nginx-deployment
    Namespace:      default
    CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
    Labels:         app=nginx
    Selector:       app=nginx
    Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
    StrategyType:       RollingUpdate
    MinReadySeconds:    0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.161
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:     nginx-deployment-1564180365 (3/3 replicas created)
    NewReplicaSet:      nginx-deployment-3066724191 (1/1 replicas created)
    Events:
      FirstSeen LastSeen    Count   From                    SubObjectPath   Type        Reason              Message
      --------- --------    -----   ----                    -------------   --------    ------              -------
      1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 1
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
    

    これを修正するために、Deploymentを安定した状態の過去のリビジョンに更新する必要があります。

Deploymentのロールアウト履歴の確認

ロールアウトの履歴を確認するには、以下の手順に従って下さい。

  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をロールバックすることはできません。

Deploymentのステータス

Deploymentは、そのライフサイクルの間に様々な状態に遷移します。新しいReplicaSetへのロールアウト中は進行中になり、その後は完了し、また失敗にもなります。

Deploymentの更新処理

以下のタスクが実行中のとき、KubernetesはDeploymentの状態を 進行中 にします。

  • Deploymentが新しいReplicaSetを作成します。
  • Deploymentが新しいReplicaSetをスケールアップさせています。
  • Deploymentが古いReplicaSetをスケールダウンさせています。
  • 新しいPodが準備中もしくは利用可能な状態になります(少なくともMinReadySecondsの間は準備中になります)。

kubectl rollout statusを実行すると、Deploymentの進行状態を確認できます。

Deploymentの更新処理の完了

Deploymentが以下の状態になったとき、KubernetesはDeploymentのステータスを 完了 にします。

  • Deploymentの全てのレプリカが、指定された最新のバージョンに更新されます。これは指定した更新処理が完了したことを意味します。
  • Deploymentの全てのレプリカが利用可能になります。
  • Deploymentの古いレプリカが1つも稼働していません。

kubectl rollout statusを実行して、Deploymentの更新が完了したことを確認できます。ロールアウトが正常に完了するとkubectl rollout statusの終了コードが0で返されます。

kubectl rollout status deployment.v1.apps/nginx-deployment

実行結果は以下のとおりです。

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out

そしてkubectl rolloutの終了ステータスが0となります(成功です):

echo $?
0

Deploymentの更新処理の失敗

新しいReplicaSetのデプロイが完了せず、更新処理が止まる場合があります。これは主に以下の要因によるものです。

  • 不十分なリソースの割り当て
  • ReadinessProbeの失敗
  • コンテナイメージの取得ができない
  • 不十分なパーミッション
  • リソースリミットのレンジ
  • アプリケーションランタイムの設定の不備

このような状況を検知する1つの方法として、Deploymentのリソース定義でデッドラインのパラメーターを指定します(.spec.progressDeadlineSeconds)。.spec.progressDeadlineSecondsはDeploymentの更新が停止したことを示す前にDeploymentコントローラーが待つ秒数を示します。

以下のkubectlコマンドでリソース定義にprogressDeadlineSecondsを設定します。これはDeploymentの更新が止まってから10分後に、コントローラーが失敗を通知させるためです。

kubectl patch deployment.v1.apps/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

実行結果は以下のとおりです。

deployment.apps/nginx-deployment patched

一度デッドラインを超過すると、DeploymentコントローラーはDeploymentの.status.conditionsに以下のDeploymentConditionを追加します。

  • Type=Progressing
  • Status=False
  • Reason=ProgressDeadlineExceeded

ステータスの状態に関するさらなる情報はKubernetes APIの規則を参照してください。

備考:

Kubernetesは停止状態のDeploymentに対して、ステータス状態を報告する以外のアクションを実行しません。高レベルのオーケストレーターはこれを利用して、状態に応じて行動できます。例えば、前のバージョンへのDeploymentのロールバックが挙げられます。

備考:

Deploymentを停止すると、Kubernetesは指定したデッドラインを超えたかどうかチェックしません。 ロールアウトの途中でもDeploymentを安全に一時停止でき、デッドラインを超えたイベントをトリガーすることなく再開できます。

設定したタイムアウトの秒数が小さかったり、一時的なエラーとして扱える他の種類のエラーが原因となり、Deploymentで一時的なエラーが出る場合があります。例えば、リソースの割り当てが不十分な場合を考えます。Deploymentの詳細情報を確認すると、以下のセクションが表示されます。

kubectl describe deployment nginx-deployment

実行結果は以下のとおりです。

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

kubectl get deployment nginx-deployment -o yamlを実行すると、Deploymentのステータスは以下のようになります。

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

最後に、一度Deploymentの更新処理のデッドラインを越えると、KubernetesはDeploymentのステータスと進行中の状態を更新します。

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

Deploymentか他のリソースコントローラーのスケールダウンを行うか、使用している名前空間内でリソースの割り当てを増やすことで、リソースの割り当て不足の問題に対処できます。割り当て条件を満たすと、DeploymentコントローラーはDeploymentのロールアウトを完了させ、Deploymentのステータスが成功状態になるのを確認できます(Status=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です。

備考:

このフィールドを明示的に0に設定すると、Deploymentの全ての履歴を削除します。従って、Deploymentはロールバックできません。

カナリアパターンによるデプロイ

Deploymentを使って一部のユーザーやサーバーに対してリリースのロールアウトをしたい場合、リソースの管理に記載されているカナリアパターンに従って、リリース毎に1つずつ、複数のDeploymentを作成できます。

Deployment Specの記述

他の全てのKubernetesの設定と同様に、Deploymentは.apiVersion.kind.metadataフィールドを必要とします。 設定ファイルの利用に関する情報はアプリケーションのデプロイを参照してください。コンテナの設定に関してはリソースを管理するためのkubectlの使用を参照してください。 Deploymentオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。 Deploymentは.specセクションも必要とします。

Podテンプレート

.spec.template.spec.selector.specにおける必須のフィールドです。

.spec.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を作成します。

備考:

Deploymentのセレクターに一致するラベルを持つPodを直接作成したり、他のDeploymentやReplicaSetやReplicationControllerによって作成するべきではありません。作成してしまうと、最初のDeploymentがラベルに一致する新しいPodを作成したとみなされます。こうなったとしても、Kubernetesは処理を止めません。

セレクターが重複する複数のコントローラーを持つとき、そのコントローラーは互いに競合状態となり、正しくふるまいません。

更新戦略

.spec.strategyは古いPodから新しいPodに置き換える際の更新戦略を指定します。.spec.strategy.typeは"Recreate"もしくは"RollingUpdate"を指定できます。デフォルトは"RollingUpdate"です。

Deploymentの再作成

.spec.strategy.type==Recreateと指定されているとき、既存の全てのPodは新しいPodが作成される前に削除されます。

備考:

これは更新のための作成の前にPodを停止する事を保証するだけです。Deploymentを更新する場合、古いリビジョンのPodは全てすぐに停止されます。削除に成功するまでは、新しいリビジョンのPodは作成されません。手動でPodを削除すると、ライフサイクルがReplicaSetに制御されているのですぐに置き換えが実施されます(たとえ古いPodがまだ停止中のステータスでも)。Podに"高々この程度の"保証を求めるならばStatefulSetの使用を検討してください。

Deploymentのローリングアップデート

.spec.strategy.type==RollingUpdateと指定されているとき、DeploymentはローリングアップデートによりPodを更新します。ローリングアップデートの処理をコントロールするために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は一時停止していない状態で作成されます。

5.3.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によって拒否されます。

備考:

2つのReplicaSetが同じ.spec.selectorの値を設定しているが、それぞれ異なる.spec.template.metadata.labels.spec.template.specフィールドの値を持っていたとき、それぞれのReplicaSetはもう一方のReplicaSetによって作成されたPodを無視します。

レプリカ数について

ユーザーは.spec.replicasフィールドの値を設定することにより、いくつのPodを同時に稼働させるか指定できます。そのときReplicaSetはレプリカ数がこの値に達するまでPodを作成、または削除します。

もしユーザーが.spec.replicasを指定しない場合、デフォルト値として1がセットされます。

ReplicaSetを利用する

ReplicaSetとPodの削除

ReplicaSetとそれが所有する全てのPod削除したいときは、kubectl deleteコマンドを使ってください。
ガベージコレクターがデフォルトで自動的に全ての依存するPodを削除します。

REST APIもしくはclient-goライブラリーを使用するとき、ユーザーは-dオプションで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削除の順番を保証するものではありません。
  • ユーザーは、メトリック値に基づいてアノテーションを更新するなど、頻繁に更新することは避けるべきです。APIサーバー上で大量のPodの更新操作を発生させることになるためです。

使用事例

アプリケーションの異なるPodは、異なる使用レベルになる可能性があります。スケールダウンする場合、アプリケーションは使用率の低いPodを削除することを優先しています。Podを頻繁に更新することを避けるため、アプリケーションはスケールダウンする前に一度controller.kubernetes.io/pod-deletion-costを更新する必要があります(アノテーションをPod使用レベルに比例する値に設定します)。Spark DeploymentのドライバーPodのように、アプリケーション自体がスケールダウンを制御する場合も機能します。

HorizontalPodAutoscaler(HPA)のターゲットとしてのReplicaSet

ReplicaSetはまた、Horizontal Pod Autoscalers (HPA)のターゲットにもなることができます。 これはつまりReplicaSetがHPAによってオートスケールされうることを意味します。 ここではHPAが、前の例で作成したReplicaSetをターゲットにする例を示します。

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

このマニフェストをhpa-rs.yamlに保存し、Kubernetesクラスターに適用すると、レプリケートされたPodのCPU使用量にもとづいてターゲットのReplicaSetをオートスケールするHPAを作成します。

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

同様のことを行うための代替案として、kubectl autoscaleコマンドも使用できます。(こちらの方がより簡単です。)

kubectl autoscale rs frontend --max=10 --min=3 --cpu=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よりも好まれます。

次の項目

5.3.3 - StatefulSet

StatefulSetはステートフルなアプリケーションを管理するためのワークロードAPIです。

StatefulSetはPodのデプロイとスケーリングを管理し、それらのPodの順序と一意性を保証します。

Deploymentのように、StatefulSetは指定したコンテナのspecに基づいてPodを管理します。Deploymentとは異なり、StatefulSetは各Podにおいて管理が大変な同一性を維持します。これらのPodは同一のspecから作成されますが、それらは交換可能ではなく、リスケジュール処理をまたいで維持される永続的な識別子を持ちます。

ワークロードに永続性を持たせるためにストレージボリュームを使いたい場合は、解決策の1つとしてStatefulSetが利用できます。StatefulSet内の個々のPodは障害の影響を受けやすいですが、永続化したPodの識別子は既存のボリュームと障害によって置換された新しいPodの紐付けを簡単にします。

StatefulSetの使用

StatefulSetは下記の1つ以上の項目を要求するアプリケーションにおいて最適です。

  • 安定した一意のネットワーク識別子
  • 安定した永続ストレージ
  • 規則的で安全なデプロイとスケーリング
  • 規則的で自動化されたローリングアップデート

上記において安定とは、Podのスケジュール(または再スケジュール)をまたいでも永続的であることと同義です。 もしアプリケーションが安定したネットワーク識別子と規則的なデプロイや削除、スケーリングを全く要求しない場合、ユーザーはステートレスなレプリカのセットを提供するワークロードを使ってアプリケーションをデプロイするべきです。 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 Domain Service (ns/name) StatefulSet (ns/name) StatefulSet Domain Pod DNS Pod Hostname
cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}
kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1}

備考:

クラスタードメインはその他の設定がされない限り、cluster.localにセットされます。

安定したストレージ

StatefulSetで定義された各VolumeClaimTemplateに対して、各Podは1つのPersistentVolumeClaimを受け取ります。上記のnginxの例において、各Podはmy-storage-classというStorageClassをもち、1GiBのストレージ容量を持った単一のPersistentVolumeを受け取ります。もしStorageClassが指定されていない場合、デフォルトのStorageClassが使用されます。PodがNode上にスケジュール(もしくは再スケジュール)されたとき、そのvolumeMountsはPersistentVolume Claimに関連したPersistentVolumeをマウントします。 注意点として、PodのPersistentVolume Claimと関連したPersistentVolumeは、PodやStatefulSetが削除されたときに削除されません。 削除する場合は手動で行わなければなりません。

Podのネームラベル

StatefulSet コントローラー がPodを作成したとき、Podの名前として、statefulset.kubernetes.io/pod-nameにラベルを追加します。このラベルによってユーザーはServiceにStatefulSet内の指定したPodを割り当てることができます。

デプロイとスケーリングの保証

  • N個のレプリカをもつStatefulSetにおいて、Podがデプロイされるとき、それらのPodは{0..N-1}の番号で順番に作成されます。
  • Podが削除されるとき、それらのPodは{N-1..0}の番号で降順に削除されます。
  • Podに対してスケーリングオプションが適用される前に、そのPodの前の順番の全てのPodがRunningかつReady状態になっていなくてはなりません。
  • Podが停止される前に、そのPodの番号より大きい番号を持つの全てのPodは完全にシャットダウンされていなくてはなりません。

StatefulSetはpod.Spec.TerminationGracePeriodSecondsを0に指定すべきではありません。これは不安全で、やらないことを強く推奨します。さらなる説明としては、StatefulSetのPodの強制削除を参照してください。

上記の例のnginxが作成されたとき、3つのPodはweb-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の再作成を始めます。

次の項目

5.3.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:v5.0.1
        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 key Effect Details
node.kubernetes.io/not-ready NoExecute 健康でないNodeや、Podを受け入れる準備ができていないNodeにDaemonSet Podをスケジュールできるように設定します。そのようなNode上で動作しているDaemonSet Podは退避されることがありません。
node.kubernetes.io/unreachable NoExecute Nodeコントローラーから到達できないNodeにDaemonSet Podをスケジュールできるように設定します。このようなNode上で動作しているDaemonSet Podは、退避されません。
node.kubernetes.io/disk-pressure NoSchedule ディスク不足問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/memory-pressure NoSchedule メモリー不足問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/pid-pressure NoSchedule 処理負荷に問題のあるNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/unschedulable NoSchedule スケジューリング不可能なNodeにDaemonSet Podをスケジュールできるように設定します。
node.kubernetes.io/network-unavailable NoSchedule ホストネットワークを要求するDaemonSet Podにのみ追加できます、つまりspec.hostNetwork: trueと設定されているPodです。このようなDaemonSet Podは、ネットワークが利用できないNodeにスケジュールできるように設定します。

DaemonSetのPodテンプレートで定義すれば、DaemonSetのPodに独自のTolerationを追加することも可能です。

DaemonSetコントローラーはnode.kubernetes.io/unschedulable:NoScheduleのTolerationを自動的に設定するため、Kubernetesは スケジューリング不可能 としてマークされているNodeでDaemonSet Podを実行することが可能です。

クラスターのネットワークのような重要なNodeレベルの機能をDaemonSetで提供する場合、KubernetesがDaemonSet PodをNodeが準備完了になる前に配置することは有用です。 例えば、その特別なTolerationがなければ、ネットワークプラグインがそこで実行されていないためにNodeが準備完了としてマークされず、同時にNodeがまだ準備完了でないためにそのNode上でネットワークプラグインが実行されていないというデッドロック状態に陥ってしまう可能性があるのです。

Daemon Podとのコミュニケーション

DaemonSet内のPodとのコミュニケーションをする際に考えられるパターンは以下の通りです:

  • Push: DaemonSet内のPodは統計データベースなどの他のサービスに対して更新情報を送信するように設定されます。クライアントは持っていません。
  • NodeIPとKnown Port: PodがNodeIPを介して疎通できるようにするため、DaemonSet内のPodはhostPortを使用できます。慣例により、クライアントはNodeIPのリストとポートを知っています。
  • DNS: 同じPodセレクターを持つHeadlessServiceを作成し、endpointsリソースを使ってDaemonSetを探すか、DNSから複数のAレコードを取得します。
  • Service: 同じPodセレクターを持つServiceを作成し、複数のうちのいずれかのNode上のDaemonに疎通させるためにそのServiceを使います。(特定のNodeにアクセスする方法はありません。)

DaemonSetの更新

もしNodeラベルが変更されたとき、そのDaemonSetは直ちに新しくマッチしたNodeにPodを追加し、マッチしなくなったNodeからPodを削除します。

ユーザーはDaemonSetが作成したPodを修正可能です。しかし、Podは全てのフィールドの更新を許可していません。また、DaemonSetコントローラーは次のNode(同じ名前でも)が作成されたときにオリジナルのテンプレートを使ってPodを作成します。

ユーザーはDaemonSetを削除可能です。kubectlコマンドで--cascade=orphanを指定するとDaemonSetのPodはNode上に残り続けます。その後、同じセレクターで新しいDaemonSetを作成すると、新しいDaemonSetは既存のPodを再利用します。PodでDaemonSetを置き換える必要がある場合は、updateStrategyに従ってそれらを置き換えます。

ユーザーはDaemonSet上でローリングアップデートの実施が可能です。

DaemonSetの代替案

Initスクリプト

Node上で直接起動することにより(例: 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でクラスターネットワークが動作していることを確認します。

次の項目

5.3.5 - Job

Jobは一つ以上のPodを作成し、指定された数のPodが正常に終了するまで、Podの実行を再試行し続けます。Podが正常に終了すると、Jobは成功したPodの数を追跡します。指定された完了数に達すると、そのタスク(つまりJob)は完了したとみなされます。Jobを削除すると、作成されたPodも一緒に削除されます。Jobを一時停止すると、再開されるまで、稼働しているPodは全部削除されます。

単純なケースを言うと、確実に一つのPodが正常に完了するまで実行されるよう、一つのJobオブジェクトを作成します。 一つ目のPodに障害が発生したり、(例えばノードのハードウェア障害またノードの再起動が原因で)削除されたりすると、Jobオブジェクトは新しいPodを作成します。

Jobで複数のPodを並列で実行することもできます。

スケジュールに沿ってJob(単一のタスクか複数タスク並列のいずれか)を実行したい場合は CronJobを参照してください。

実行例

下記にJobの定義例を記載しています。πを2000桁まで計算して出力するJobで、完了するまで約10秒かかります。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

このコマンドで実行できます:

kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml

実行結果はこのようになります:

job.batch/pi created

kubectlでJobの状態を確認できます:


Name:           pi
Namespace:      default
Selector:       batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                batch.kubernetes.io/job-name=pi
                ...
Annotations:    batch.kubernetes.io/job-tracking: ""
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           batch.kubernetes.io/job-name=pi
  Containers:
   pi:
    Image:      perl:5.34.0
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  21s   job-controller  Created pod: pi-xf9p4
  Normal  Completed         18s   job-controller  Job completed

apiVersion: batch/v1
kind: Job
metadata:
  annotations: batch.kubernetes.io/job-tracking: ""
             ...
  creationTimestamp: "2022-11-10T17:53:53Z"
  generation: 1
  labels:
    batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
    batch.kubernetes.io/job-name: pi
  name: pi
  namespace: default
  resourceVersion: "4751"
  uid: 204fb678-040b-497f-9266-35ffa8716d14
spec:
  backoffLimit: 4
  completionMode: NonIndexed
  completions: 1
  parallelism: 1
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
  suspend: false
  template:
    metadata:
      creationTimestamp: null
      labels:
        batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
        batch.kubernetes.io/job-name: pi
    spec:
      containers:
      - command:
        - perl
        - -Mbignum=bpi
        - -wle
        - print bpi(2000)
        image: perl:5.34.0
        imagePullPolicy: IfNotPresent
        name: pi
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Never
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status:
  active: 1
  ready: 0
  startTime: "2022-11-10T17:53:57Z"
  uncountedTerminatedPods: {}

Jobの完了したPodを確認するには、kubectl get podsを使います。

Jobに属するPodの一覧を機械可読形式で出力するには、下記のコマンドを使います:

pods=$(kubectl get pods --selector=batch.kubernetes.io/job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

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

pi-5rwd7

ここのセレクターはJobのセレクターと同じです。--output=jsonpathオプションは、返されたリストからPodのnameフィールドを指定するための表現です。

その中の一つのPodの標準出力を確認するには:

kubectl logs $pods

Jobの標準出力を確認するもう一つの方法は:

kubectl logs jobs/pi

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

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

Job spec(仕様)の書き方

他のKubernetesオブジェクト設定ファイルと同様に、Jobにも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 (GA)

完了数固定 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が起動することがあります。(Nodeの障害、kubeletの再起動、Podの立ち退きなど)。この場合、正常に完了した最初のPodだけ完了数にカウントされ、Jobのステータスが更新されます。同じインデックスに対して実行中または完了した他のPodは、検出されるとJobコントローラーによって削除されます。

Podとコンテナの障害対策

Pod内のコンテナは、その中のプロセスが0以外の終了コードで終了した、またはメモリ制限を超えたためにコンテナが強制終了されたなど、様々な理由で失敗することがあります。この場合、もし.spec.template.spec.restartPolicy = "OnFailure"と設定すると、Podはノード上に残りますが、コンテナは再実行されます。そのため、プログラムがローカルで再起動した場合の処理を行うか、.spec.template.spec.restartPolicy = "Never"と指定する必要があります。 restartPolicyの詳細についてはPodのライフサイクルを参照してください。

Podがノードからキックされた(ノードがアップグレード、再起動、削除されたなど)、または.spec.template.spec.restartPolicy = "Never"と設定されたときにPodに属するコンテナが失敗したなど、様々な理由でPod全体が故障することもあります。Podに障害が発生すると、Jobコントローラーは新しいPodを起動します。つまりアプリケーションは新しいPodで再起動された場合の処理を行う必要があります。特に、過去に実行した際に生じた一時ファイル、ロック、不完全な出力などを処理する必要があります。

デフォルトでは、それぞれのPodの失敗は.spec.backoffLimitにカウントされます。詳しくはPod失敗のバックオフポリシーをご覧ください。しかし、JobのPod失敗ポリシーを設定することで、Pod失敗の処理をカスタマイズすることができます。

.spec.parallelism = 1.spec.completions = 1.spec.template.spec.restartPolicy = "Never"を指定しても、同じプログラムが2回起動されることもありますので注意してください。

.spec.parallelism.spec.completionsを両方とも2以上指定した場合、複数のPodが同時に実行される可能性があります。そのため、Podは並行処理を行えるようにする必要があります。

フィーチャーゲート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のみに基づいています。

備考:

restartPolicy = "OnFailure"が設定されたJobはバックオフ制限に達すると、属するPodは全部終了されるので注意してください。これにより、Jobの実行ファイルのデバッグ作業が難しくなる可能性があります。失敗したJobからの出力が不用意に失われないように、Jobのデバッグ作業をする際はrestartPolicy = "Never"を設定するか、ロギングシステムを使用することをお勧めします。

Pod失敗ポリシー

FEATURE STATE: Kubernetes v1.26 (Beta)

備考:

クラスターでJobPodFailurePolicyフィーチャーゲートが有効になっている場合のみ、Jobに対してPod失敗ポリシーを設定することができます。さらにPod失敗ポリシーでPodの中断条件を検知して処理できるように、PodDisruptionConditionsフィーチャーゲートを有効にすることが推奨されます。(Podの中断条件を参照してください)。どちらのフィーチャーゲートもKubernetes 1.27で利用可能です。

.spec.podFailurePolicyフィールドで定義されるPod失敗ポリシーを使用すると、コンテナの終了コードとPodの条件に基づいてクラスターがPodの失敗を処理できるようになります。

状況によっては、Podの失敗を処理するときに、Jobの.spec.backoffLimitに基づいたPod失敗のバックオフポリシーが提供する制御よりも、Podの失敗処理に対してより良い制御を求めるかもしれません。これらはいくつかの使用例です:

  • 不要なPodの再起動を回避してワークロードの実行コストを最適化するために、Podの1つがソフトウェアバグを示す終了コードで失敗するとすぐにJobを終了させることができます。
  • 中断が発生してもJobが完了するように、中断によって発生したPodの失敗(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テンプレートはrestartPolicy.Neverを指定しているため、kubeletはその特定のPodのmainコンテナを再起動しません。

Pod失敗ポリシーの2つ目のルールでは、DisruptionTargetという条件で失敗したPodに対してIgnoreアクションを指定することで、Podの中断が.spec.backoffLimitによるリトライの制限にカウントされないようにします。

備考:

Pod失敗ポリシーまたはPod失敗のバックオフポリシーのいずれかによってJobが失敗し、そのJobが複数のPodを実行している場合、KubernetesはそのJob内の保留中または実行中のすべてのPodを終了します。

これらはAPIの要件と機能です:

  • .spec.podFailurePolicyフィールドをJobに使いたい場合は、.spec.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のカウンターが加算されます。

備考:

PodFailurePolicyを使用すると、JobコントローラーはFailedフェーズのPodのみにマッチします。削除タイムスタンプを持つPodで、終了フェーズ(FailedまたはSucceeded)にないものは、まだ終了中と見なされます。これは、終了中Podは終了フェーズに達するまで追跡ファイナライザーを保持することを意味します。Kubernetes 1.27以降、Kubeletは削除されたPodを終了フェーズに遷移させます(参照:Podのフェーズ)。これにより、削除されたPodはJobコントローラーによってファイナライザーが削除されます。

Jobの終了とクリーンアップ

Jobが完了すると、それ以上Podは作成されませんが、通常Podが削除されることもありません。 これらを残しておくと、完了したPodのログを確認でき、エラーや警告などの診断出力を確認できます。 またJobオブジェクトはJob完了後も残っているため、状態を確認することができます。古いJobの状態を把握した上で、削除するかどうかはユーザー次第です。Jobを削除するにはkubectl (例:kubectl delete jobs/piまたはkubectl delete -f ./job.yaml)を使います。kubectlでJobを削除する場合、Jobが作成したPodも全部削除されます。

デフォルトでは、Podが失敗しない(restartPolicy=Never)またはコンテナがエラーで終了しない(restartPolicy=OnFailure)限り、Jobは中断されることなく実行されます。.spec.backoffLimitに達するとそのJobは失敗と見なされ、実行中のPodはすべて終了します。

Jobを終了させるもう一つの方法は、活動期間を設定することです。 Jobの.spec.activeDeadlineSecondsフィールドに秒数を設定することで、活動期間を設定できます。 Podがいくつ作成されても、activeDeadlineSecondsはJobの存続する時間に適用されます。 JobがactiveDeadlineSecondsに達すると、実行中のすべてのPodは終了され、Jobの状態はtype: Failedになり、理由はreason: DeadlineExceededになります。

ここで要注意なのは、Jobの.spec.activeDeadlineSeconds.spec.backoffLimitよりも優先されます。したがって、失敗して再試行しているPodが一つ以上持っているJobは、backoffLimitに達していなくても、activeDeadlineSecondsで指定された設定時間に達すると、追加のPodをデプロイしなくなります。

例えば:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job仕様と、Jobに属するPodテンプレートの仕様は両方ともactiveDeadlineSecondsフィールドを持っているので注意してください。適切なレベルで設定していることを確認してください。

またrestartPolicyはJob自体ではなく、Podに適用されることも注意してください: Jobの状態はtype: Failedになると、自動的に再起動されることはありません。 つまり、.spec.activeDeadlineSeconds.spec.backoffLimitによって引き起こされるJob終了メカニズムは、永久的なJob失敗につながり、手動で介入して解決する必要があります。

終了したJobの自動クリーンアップ

終了したJobは通常システムに残す必要はありません。残ったままにしておくとAPIサーバーに負担をかけることになります。Jobが上位コントローラーにより直接管理されている場合、例えばCronJobsの場合、Jobは指定された容量ベースのクリーンアップポリシーに基づき、CronJobによりクリーンアップされます。

終了したJobのTTLメカニズム

FEATURE STATE: Kubernetes v1.23 (GA)

終了した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コントローラーによるクリーンアップはされません。

備考:

ttlSecondsAfterFinishedフィールドを設定することが推奨されます。管理されていないJob(CronJobなどの、他のワークロードAPIを経由せずに、直接作成したJob)はorphanDependentsというデフォルトの削除ポリシーがあるため、Jobが完全に削除されても、属しているPodが残ってしまうからです。 コントロールプレーンは最終的に、失敗または完了して削除されたJobに属するPodをガベージコレクションしますが、Podが残っていると、クラスターのパフォーマンスが低下することがあり、最悪の場合、この低下によりクラスターがオフラインになることがあります。

LimitRangesリソースクォータで、指定する名前空間が消費できるリソースの量に上限を設定することができます。

Jobパターン

Jobオブジェクトは、Podの確実な並列実行をサポートするために使用されます。科学技術計算でよく見られるような、密接に通信を行う並列処理をサポートするようには設計されていません。独立だが関連性のある一連の作業項目の並列処理をサポートします。例えば送信すべき電子メール、レンダリングすべきフレーム、トランスコードすべきファイル、スキャンすべきNoSQLデータベースのキーの範囲、などです。

複雑なシステムでは、異なる作業項目のセットが複数存在する場合があります。ここでは、ユーザーが一斉に管理したい作業項目のセットが一つだけの場合 — つまりバッチJobだけを考えます。

並列計算にはいくつかのパターンがあり、それぞれに長所と短所があります。 トレードオフの関係にあるのは:

  • 各作業項目に1つのJobオブジェクト vs. すべての作業項目に1つのJobオブジェクト。 後者は大量の作業項目を処理する場合に適しています。 前者は大量のJobオブジェクトを管理するため、ユーザーとシステムにオーバーヘッドをかけることになります。
  • 作成されるPod数が作業項目数と等しい、 vs. 各Podが複数の作業項目を処理する。 前者は通常、既存のコードやコンテナへの変更が少なくて済みます。 後者は上記と同じ理由で、大量の作業項目を処理する場合に適しています。
  • ワークキューを利用するアプローチもいくつかあります。それを使うためには、キューサービスを実行し、既存のプログラムやコンテナにワークキューを利用させるための改造を行う必要があります。 他のアプローチは既存のコンテナ型アプリケーションに適用しやすいです。

ここでは、上記のトレードオフをまとめてあり、それぞれ2~4列目に対応しています。 またパターン名のところは、例やより詳しい説明が書いてあるページへのリンクになっています。

パターン 単一Jobオブジェクト Podが作業項目より少ない? アプリを修正せずに使用できる?
作業項目ごとにPodを持つキュー 時々
Pod数可変のキュー
静的な処理の割り当てを使用したインデックス付きJob
Jobテンプレート拡張
Pod間通信を使用したJob 時々 時々

.spec.completionsで完了数を指定する場合、Jobコントローラーより作成された各Podは同一のspecを持ちます。これは、このタスクのすべてのPodが同じコマンドライン、同じイメージ、同じボリューム、そして(ほぼ)同じ環境変数を持つことを意味します。これらのパターンは、Podが異なる作業をするためのさまざまな配置方法になります。

この表は、各パターンで必要な.spec.parallelism.spec.completionsの設定を示しています。 ここで、Wは作業項目の数を表しています。

パターン .spec.completions .spec.parallelism
作業項目ごとにPodを持つキュー W 任意
Pod数可変のキュー null 任意
静的な処理の割り当てを使用したインデックス付きJob W 任意
Jobテンプレート拡張 1 1であるべき
Pod間通信を使用したJob W W

高度な使い方

Jobの一時停止

FEATURE STATE: Kubernetes v1.24 (GA)

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 (GA)

ほとんどの場合、並列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 (GA)

備考:

JobTrackingWithFinalizers機能が無効になっている時に作成されたJobについては、コントロールプレーンを1.26にアップグレードしても、ファイナライザーを使用してJobを追跡しません。

コントロールプレーンは任意のJobに属するPodを追跡し、そのPodがAPIサーバーから削除されたかどうか認識します。そのためJobコントローラーはファイナライザーbatch.kubernetes.io/job-trackingを持つPodを作成します。コントローラーがファイナライザーを削除するのは、PodがJobステータスに反映された後なので、他のコントローラーやユーザーがPodを削除することができます。

Kubernetes 1.26にアップグレードする前、またはフィーチャーゲートJobTrackingWithFinalizersが有効になる前に作成されたJobは、Podファイナライザーを使用せずに追跡されます。Jobコントローラーは、クラスターに存在するPodのみに基づいて、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、Horovod、Ray、PyTorchトレーニングジョブなど、インデックス付きJobのスケーリングを必要とするバッチワークロードがあります。

代替案

単なるPod

Podが動作しているノードが再起動または故障した場合、Podは終了し、再起動されません。しかし、終了したPodを置き換えるため、Jobが新しいPodを作成します。このため、たとえアプリケーションが1つのPodしか必要としない場合でも、単なるPodではなくJobを使用することをお勧めします。

Replication Controller

JobはReplication Controllersを補完するものです。 Replication Controllerは、終了することが想定されていないPod(Webサーバーなど)を管理し、Jobは終了することが想定されているPod(バッチタスクなど)を管理します。

Podのライフサイクルで説明したように、JobRestartPolicyOnFailureNeverと設定されているPodにのみ適用されます。(注意:RestartPolicyが設定されていない場合、デフォルト値はAlwaysになります)

シングルJobによるコントローラーPodの起動

もう一つのパターンは、一つのJobが一つPodを作り、そのPodがカスタムコントローラーのような役割を果たし、他のPodを作ります。これは最も柔軟性がありますが、使い始めるにはやや複雑で、Kubernetesとの統合もあまりできません。

この方法のメリットは、全処理過程でJobオブジェクトが完了する保証がありながらも、どのPodを作成し、どのように作業を割り当てるかを完全に制御できることです。

次の項目

5.3.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でない値をセットするときにこのリスクに対して注意してください。

次の項目

5.3.7 - CronJob

FEATURE STATE: Kubernetes v1.8 (Beta)

CronJob は繰り返しのスケジュールによってJobを作成します。

CronJob オブジェクトとは crontab (cron table)ファイルでみられる一行のようなものです。 Cron形式で記述された指定のスケジュールの基づき、定期的にジョブが実行されます。

注意:

すべてのCronJobスケジュール: 時刻はジョブが開始されたkube-controller-managerのタイムゾーンに基づいています。

コントロールプレーンがkube-controller-managerをPodもしくは素のコンテナで実行している場合、CronJobコントローラーのタイムゾーンとして、kube-controller-managerコンテナに設定されたタイムゾーンを使用します。

CronJobリソースのためのマニフェストを作成する場合、その名前が有効なDNSサブドメイン名か確認してください。 名前は52文字を超えることはできません。これはCronJobコントローラーが自動的に、与えられたジョブ名に11文字を追加し、ジョブ名の長さは最大で63文字以内という制約があるためです。

CronJob

CronJobは、バックアップの実行やメール送信のような定期的であったり頻発するタスクの作成に役立ちます。 CronJobは、クラスターがアイドル状態になりそうなときにJobをスケジューリングするなど、特定の時間に個々のタスクをスケジュールすることもできます。

このCronJobマニフェスト例は、毎分ごとに現在の時刻とhelloメッセージを表示します。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

(Running Automated Tasks with a CronJobではこの例をより詳しく説明しています。).

CronJobの制限

cronジョブは一度のスケジュール実行につき、 おおよそ 1つのジョブオブジェクトを作成します。ここで おおよそ と言っているのは、ある状況下では2つのジョブが作成される、もしくは1つも作成されない場合があるためです。通常、このようなことが起こらないようになっていますが、完全に防ぐことはできません。したがって、ジョブは 冪等 であるべきです。

startingDeadlineSecondsが大きな値、もしくは設定されていない(デフォルト)、そして、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を見てください。

5.3.8 - ReplicationController

水平スケーリング可能なワークロードを管理するためのレガシーAPI。 DeploymentおよびReplicaSet APIに置き換えられています。

備考:

現在では、レプリケーションを設定するための推奨方法は、ReplicaSetを構成するDeploymentを使用することです。

ReplicationController は、常に指定した数のPodレプリカが実行されていることを保証します。 つまり、ReplicationControllerは、単一のPodまたは同質な複数のPodが常に稼働し、利用可能であることを保証します。

ReplicationControllerの動作

Podが多すぎる場合、ReplicationControllerは余分なPodを終了します。 Podが少なすぎる場合、ReplicationControllerはさらにPodを起動します。 手動で作成したPodとは異なり、ReplicationControllerによって管理されるPodは、障害が発生したり、削除されたり、終了されたりすると自動的に置き換えられます。 たとえば、カーネルのアップグレードなどの破壊的なメンテナンスの後には、Podはノード上で再作成されます。 このため、アプリケーションが単一のPodのみを必要とする場合でも、ReplicationControllerを使用するべきです。 ReplicationControllerはプロセススーパーバイザーに似ていますが、単一ノード上の個々のプロセスを監視する代わりに、ReplicationControllerは複数のノード上の複数のPodを監視します。

ReplicationControllerは、議論の中では「rc」と略されることが多く、kubectlコマンドのショートカットとしても使われます。

単純なケースとしては、ReplicationControllerオブジェクトを1つ作成し、Podインスタンスを1つ無期限に実行し続けることができます。 より複雑なユースケースとしては、Webサーバーなどで、複数の同一レプリカを実行することができます。

ReplicationControllerの実行例

このReplicationController設定の例では、nginx Webサーバーの3つのコピーを実行します。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

サンプルファイルをダウンロードして、次のコマンドを実行することで、サンプルジョブを実行できます:

kubectl apply -f https://k8s.io/examples/controllers/replication.yaml

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

replicationcontroller/nginx created

次のコマンドを使用して、ReplicationControllerのステータスを確認します:

kubectl describe replicationcontrollers/nginx

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

Name:        nginx
Namespace:   default
Selector:    app=nginx
Labels:      app=nginx
Annotations:    <none>
Replicas:    3 current / 3 desired
Pods Status: 0 Running / 3 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       app=nginx
  Containers:
   nginx:
    Image:              nginx
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen       LastSeen     Count    From                        SubobjectPath    Type      Reason              Message
  ---------       --------     -----    ----                        -------------    ----      ------              -------
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-qrm3m
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-3ntk0
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-4ok8v

ここでは、3つのPodが作成されていますが、おそらくイメージを取得中であるため、まだ実行状態にはなっていません。 少し経ってから同じコマンドを実行すると、次のような出力が得られます:

Pods Status:    3 Running / 0 Waiting / 0 Succeeded / 0 Failed

ReplicationControllerに属するすべてのPodをプログラムで処理しやすい形式でリストするには、次のようなコマンドを使用できます:

pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods

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

nginx-3ntk0 nginx-4ok8v nginx-qrm3m

ここで、セレクターは、ReplicationControllerのセレクター(kubectl describeの出力で確認できます)と同じで、replication.yamlでは異なる形式で記述されています。 --output=jsonpathオプションは、返されたリスト内の各Podから名前を取得する式を指定します。

ReplicationControllerマニフェストの記述

他のすべてのKubernetes設定と同様に、ReplicationControllerにはapiVersionkindmetadataフィールドが必要です。

コントロールプレーンがReplicationControllerの新しいPodを作成する際、ReplicationControllerの.metadata.nameが、それらのPodを命名する際の基準の一部になります。 ReplicationControllerの名前は有効なDNSサブドメインの値である必要がありますが、これはPodのホスト名に予期しない結果をもたらす可能性があります。 互換性を最大限に保つために、名前はDNSラベルに準じた、より厳格なルールに従うことをお勧めします。

設定ファイルの操作に関する一般的な情報については、オブジェクト管理を参照してください。

また、ReplicationControllerには.specセクションも必要です。

Podテンプレート

.spec.template.specの唯一の必須フィールドです。

.spec.templatePodテンプレートです。 Podとまったく同じスキーマを持ちますが、ネストされている点や、apiVersionkindを持たない点が異なります。

Podの必須フィールドに加えて、ReplicationController内のPodテンプレートは適切なラベルと適切な再起動ポリシーを指定する必要があります。 ラベルについては、他のコントローラーと重複しないようにしてください。 詳しくは、Podセレクターを参照してください。

.spec.template.spec.restartPolicyにはAlwaysのみが許可されており、指定されていない場合、デフォルトでAlwaysになります。

ローカルコンテナの再起動については、ReplicationControllerはKubeletなどのノード上のエージェントに委任します。

ReplicationControllerのラベル

ReplicationController自体もラベル(.metadata.labels)を持つことができます。 通常、これらは.spec.template.metadata.labelsと同じに設定します。 .metadata.labelsが指定されていない場合、デフォルトで.spec.template.metadata.labelsになります。 ただし、これらは異なる値に設定することが可能で、.metadata.labelsはReplicationControllerの動作には影響しません。

Podセレクター

.spec.selectorフィールドはラベルセレクターです。 ReplicationControllerは、セレクターに一致するラベルを持つすべてのPodを管理します。 ReplicationController自身が作成したPodか、他の人やプロセスが作成したPodかを区別しません。 これにより、実行中のPodに影響を与えることなく、ReplicationControllerを置き換えることができます。

指定されている場合、.spec.template.metadata.labels.spec.selectorと等しい必要があり、そうでない場合はAPIによって拒否されます。 もし、.spec.selectorが指定されていない場合は、デフォルトで.spec.template.metadata.labelsに設定されます。

また、通常は、このセレクターに一致するラベルを持つPodを、直接、または別のReplicationControllerを通して、またはJobなどの別のコントローラーで作成しないでください。 もし作成すると、ReplicationControllerは他のPodを自身が作成したPodであるとみなしてしまいます。 Kubernetesはこのような操作を制限しません。

複数のコントローラーのセレクターが重複してしまった場合、削除を自分で管理する必要があります(下記を参照)。

複数のレプリカ

.spec.replicasを、同時に実行したいPodの数に設定することで、同時に実行すべきPodの数を指定できます。 任意の時点で実行されているPod数は、レプリカ数の増減直後や、Podの正常なシャットダウンと代替Podの早期起動が重なった場合などに、指定した数と多少異なることがあります。

.spec.replicasを指定しない場合、デフォルトは1に設定されます。

ReplicationControllerの操作

ReplicationControllerとそのPodの削除

ReplicationControllerとそのすべてのPodを削除するには、kubectl deleteを使用します。 Kubectlは、ReplicationControllerをゼロにスケールし、各Podを削除するのを待ってから、ReplicationController自体を削除します。 このkubectlコマンドが中断された場合、再起動できます。

REST APIまたはクライアントライブラリを使用する場合は、明示的に手順を実行する必要があります(レプリカを0にスケール後、Pod削除を待機し、ReplicationControllerを削除する)。

ReplicationControllerのみの削除

Podに影響を与えることなく、ReplicationControllerを削除することが可能です。

kubectlを使用して、kubectl delete--cascade=orphanオプションを指定します。

REST APIまたはクライアントライブラリを使用する場合は、ReplicationControllerオブジェクトを削除できます。

元のコントローラーが削除されたら、新しいReplicationControllerを作成して置き換えることができます。 新旧の.spec.selectorが同じである限り、新しいコントローラーは古いPodを引き継ぎます。 ただし、既存のPodを新しい異なるPodテンプレートに合わせて更新することはしません。 制御された方法でPodを新しい仕様に更新するには、ローリングアップデートを使用します。

ReplicationControllerからPodを分離する

Podのラベルを変更することで、ReplicationControllerの管理対象から外すことができます。 この手法は、デバッグやデータ復旧の目的で、Podをサービスから削除するために使用できます。 この方法で削除されたPodは、自動的に置き換えられます(レプリカ数も変更されていないと仮定)。

一般的な使用パターン

再スケジューリング

前述のとおり、実行し続けたいPodが1つでも1000でも、ReplicationControllerは、ノード障害やPodの終了(たとえば、別の制御エージェントによる操作など)が発生した場合でも、指定された数のPodが存在することを保証します。

スケーリング

ReplicationControllerは、手動またはオートスケーリング制御エージェントによって、replicasフィールドを更新することで、レプリカ数を増減させることができます。

ローリングアップデート

ReplicationControllerは、Podを1つずつ置き換えることで、サービスへのローリングアップデートを容易にするように設計されています。

#1353で説明されているように、推奨されるアプローチは、1つのレプリカで新しいReplicationControllerを作成し、新しいコントローラー(+1)と古いコントローラー(-1)を1つずつスケールし、古いコントローラーが0レプリカに達したら削除することです。 これにより、予期しない障害に左右されることなく、Podのセットが予測どおりに更新されます。

理想的には、ローリングアップデートコントローラーはアプリケーションの準備状態を考慮し、常に十分な数のPodが実際にサービスを提供できる状態にあることを保証します。

2つのReplicationControllerは、Podのプライマリコンテナのイメージタグなど、少なくとも1つのラベルで区別できるPodを作成する必要があります。 ローリングアップデートのきっかけになるのは、通常はイメージの更新が原因であるためです。

複数のリリーストラック

ローリングアップデート中にアプリケーションの複数のリリースを実行するのに加えて、複数のリリーストラックを用いて、複数のリリースを長期間、あるいは継続的に実行することも一般的です。 トラックはラベルで区別されます。

たとえば、とあるサービスがtier in (frontend), environment in (prod)の条件を満たすラベルを持つ、すべてのPodを対象にしているとします。 このティアを構成する10個のレプリケートされたPodがあるとします。 ただし、このコンポーネントの新しいバージョンを「カナリア」リリースしたいとします。 大部分のレプリカについて、replicasを9に設定し、ラベルtier=frontend, environment=prod, track=stableを持つReplicationControllerを設定し、 カナリア用にreplicasを1に設定し、ラベルtier=frontend, environment=prod, track=canaryを持つ別のReplicationControllerを設定できます。 これで、サービスはカナリアと非カナリアの両方のPodをカバーします。 一方で、ReplicationControllerを個別に操作して、テストしたり、結果を監視したりすることもできます。

ReplicationControllerとサービスの併用

複数のReplicationControllerを単一のサービスの背後に配置して、たとえば、一部のトラフィックが古いバージョンに送られ、一部が新しいバージョンに送られるようにすることができます。

ReplicationControllerは自ら終了することはありませんが、サービスほど長く存在し続けることは想定されていません。 サービスは、複数のReplicationControllerによって制御されるPodで構成される場合があり、1つのサービスの存続期間中に多くのReplicationControllerが作成・破棄されることが想定されます(たとえば、サービスを実行するPodを更新する場合など)。 サービス自体とそのクライアントは、サービスのPodを維持しているReplicationControllerについて意識する必要はありません。

レプリケーション用のプログラムの記述

ReplicationControllerによって作成されたPodは、交換可能で意味的に同一であることを意図していますが、時間の経過とともに構成が不均一になる可能性があります。 これは、レプリケートされたステートレスなサーバーに適していることは明らかですが、ReplicationControllerは、マスター選出、シャーディング、ワーカープールアプリケーションなど可用性維持のためにも使用できます。 このようなアプリケーションでは、アンチパターンと見なされる各Podの構成の静的/1回限りのカスタマイズではなく、RabbitMQワークキューなどの動的なワーク割り当てメカニズムを使用する必要があります。 実行されるPodのカスタマイズ、たとえばリソースの垂直オートサイジング(cpuやメモリなど)は、ReplicationController自体と同様に、別のオンラインコントローラープロセスによって実行される必要があります。

ReplicationControllerの責任

ReplicationControllerは、ラベルセレクターに一致するPodが目的の数だけ存在し、それらが動作していることを保証します。 現在は、終了したPodのみがカウントから除外されます。 将来的には、readinessやシステムから利用可能なその他の情報が考慮される可能性があり、置き換えポリシーに対してより多くの制御を追加する可能性があります。 また、外部クライアントが任意の洗練された置き換えポリシーやスケールダウンポリシーを実装するために使用できるイベントを発行する予定です。

ReplicationControllerは、常にこの限定された責任のみを持ちます。 ReplicationController自身が、Readiness ProbeやLiveness Probeを実行することはありません。 オートスケーリングを実行するのではなく、(#492で議論されているように)外部のオートスケーラーによって制御され、そのオートスケーラーがreplicasフィールドを変更することを想定しています。 ReplicationControllerにスケジューリングポリシー(たとえば、spreading)を追加することはありません。 また、制御しているPodが現在指定されているテンプレートと一致することを検証すべきではありません。 それは、オートサイジングやその他の自動化されたプロセスを妨げるためです。 同様に、完了期限、順序依存関係、設定の展開、その他の機能は別の場所に属します。 一括でのPod作成のメカニズムを分離することさえ計画しています(#170)。

ReplicationControllerは、組み合わせ可能なビルディングブロックのプリミティブとして意図されています。 将来的には、ユーザーの利便性のために、ReplicationControllerやその他の補完的なプリミティブの上に、より高レベルのAPIやツールが構築されることを期待しています。 現在kubectlでサポートされている「マクロ」操作(run、scale)は、これの概念実証の例です。 たとえば、ReplicationController、オートスケーラー、サービス、スケジューリングポリシー、カナリアなどを管理するAsgardのようなものが想定されます。

APIオブジェクト

ReplicationControllerは、Kubernetes REST APIのトップレベルリソースです。 APIオブジェクトの詳細については、以下を参照してください: ReplicationController APIオブジェクト

ReplicationControllerの代替

ReplicaSet

ReplicaSetは、新しい集合ベースのラベルセレクターをサポートする次世代のReplicationControllerです。 主にDeploymentによって、Pod作成、削除、更新を調整するメカニズムとして使用されます。 カスタムの更新オーケストレーションが必要な場合や、更新がまったく必要ない場合を除き、ReplicaSetを直接使用するのではなく、Deploymentを使用することをお勧めします。

Deploymentは、基盤となるReplicaSetとそのPodを更新する、より高レベルのAPIオブジェクトです。 Deploymentは宣言的で、サーバー側で動作し、追加機能を持つため、ローリングアップデート機能が必要な場合には推奨されます。

ベアPod

ユーザーが直接Podを作成した場合とは異なり、ReplicationControllerは、ノード障害やカーネルアップグレードなどの破壊的なノードメンテナンスなど、何らかの理由で削除または終了されたPodを置き換えます。 このため、アプリケーションが単一のPodのみを必要とする場合でも、ReplicationControllerを使用することを推奨します。 プロセススーパーバイザーと同様に考えてください。 ただし、単一ノード上の個々のプロセスではなく、複数のノード上の複数のPodを監視する点が異なります。 ReplicationControllerは、ローカルコンテナの再起動をkubeletなどのノード上のエージェントに委任します。

Job

自ら終了することが期待されるPod(つまり、バッチジョブ)には、ReplicationControllerの代わりにJobを使用してください。

DaemonSet

マシン監視やマシンログなど、マシンレベルの機能を提供するPodには、ReplicationControllerの代わりにDaemonSetを使用してください。 これらのPodは、マシンの稼働期間に結び付けられた存続期間を持ちます。 Podは、他のPodが起動する前にマシン上で実行されている必要があり、マシンが再起動またはシャットダウンの準備ができたときに安全に終了できます。

次の項目

  • Podについて学ぶ。
  • ReplicationControllerの代替であるDeploymentについて学ぶ。
  • ReplicationControllerはKubernetes REST APIの一部です。 レプリケーションコントローラーのAPIを理解するには、 オブジェクト定義を読んでください。

5.4 - ワークロードの管理

アプリケーションをデプロイし、Serviceを介して公開しました。次に何をすべきでしょうか? Kubernetesには、スケーリングや更新など、アプリケーションのデプロイメントを管理するためのいくつかのツールが用意されています。

リソース構成の整理

多くのアプリケーションでは、Serviceに加えてDeploymentなどの複数のリソースを作成する必要があります。複数のリソースを管理しやすくするために、同じファイル内にまとめて記述することができます(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を先に指定するのが望ましいです。これにより、DeploymentなどのコントローラーによってPodが作成される際に、スケジューラーがServiceに関連付けられたPodを適切に分散できるようになります。

また、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

同じマイクロサービスやアプリケーションの階層に関連するリソースは、同じファイルにまとめることが推奨されます。また、アプリケーションに関連するすべてのファイルを同じディレクトリに整理することで、管理しやすくなります。アプリケーションの各階層がDNSを使用して相互に接続される場合、スタックのすべてのコンポーネントをまとめてデプロイできます。

さらに、設定ソースとしてURLを指定することも可能です。これにより、ソース管理システム内のマニフェストから直接デプロイする際に便利です。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

さらに、ConfigMapを追加するなど、追加のマニフェストを定義することも可能です。

外部ツール

このセクションでは、Kubernetes上でワークロードを管理するために一般的に使用されるツールのみを紹介します。 より多くのツールの一覧については、CNCF Landscapeの アプリケーション定義とイメージビルドを参照してください。

Helm

🛇 この項目は、Kubernetes自体の一部ではないサードパーティのプロジェクトまたは製品にリンクしています。詳細情報

Helmは、あらかじめ設定されたKubernetesリソースのパッケージを管理するためのツールです。これらのパッケージは Helmチャート と呼ばれます。

Kustomize

Kustomizeは、Kubernetesのマニフェストを処理し、設定オプションを追加・削除・更新するツールです。Kustomizeは単独のバイナリとして利用できるほか、kubectlのネイティブ機能としても利用できます。

kubectlにおける一括操作

リソースの作成だけがkubectlによる一括操作の対象ではありません。設定ファイルからリソース名を抽出し、他の操作を実行することも可能です。特に、作成したリソースを削除する際に利用できます。

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

2つのリソースを対象とする場合、resource/nameの構文を使って、両方のリソースをコマンドラインで指定することができます。

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/)
kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service/ | xargs -i kubectl get '{}'

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

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx-svc LoadBalancer 10.0.0.208 <pending> 80/TCP 0s

上記のコマンドでは、まずdocs/concepts/cluster-administration/nginx/内のリソースを作成し、-o name出力形式を使用して作成されたリソースを出力します(各リソースをresource/nameの形式で出力します)。次にgrepを使ってServiceのみを抽出し、それを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に対して一括操作を実行すると、ディレクトリの最上位レベルで処理が止まり、サブディレクトリ内のリソースは処理されません。そのため、以下のコマンドを使用してこのディレクトリ内のリソースを作成しようとすると、エラーが発生します。

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引数は、--filename/-f引数を受け付けるすべての操作で使用できます。例えば、kubectl createkubectl getkubectl deletekubectl describekubectl rolloutなどに適用できます。

また、--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)を参照してください。

アプリケーションをダウンタイムなしで更新する

デプロイ済みのアプリケーションは、いずれ更新が必要になります。通常は、新しいイメージまたはイメージタグを指定することで更新を行います。kubectlには、さまざまな更新操作が用意されており、それぞれ異なるシナリオに適用できます。

アプリケーションの複数のコピーを実行し、ロールアウト を使用して新しい正常なPodへ段階的にトラフィックを移行することで、ダウンタイムなしの更新が可能です。最終的には、すべての実行中のPodが新しいソフトウェアへ更新されます。

このセクションでは、Deploymentを使用してアプリケーションを作成し、更新する方法について説明します。

例えば、nginxのバージョン1.14.2を実行しているとします。

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

1つのレプリカが存在することを確認します。

kubectl scale --replicas 1 deployments/my-nginx --subresource='scale' --type='merge' -p '{"spec":{"replicas": 1}}'
deployment.apps/my-nginx scaled

そして、ロールアウト時にKubernetesが一時的なレプリカをより多く追加できるようにするため、最大サージ を100%に設定します。

kubectl patch --type='merge' -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge": "100%" }}}}'
deployment.apps/my-nginx patched

バージョン1.16.1へ更新するには、.spec.template.spec.containers[0].imagenginx:1.14.2からnginx:1.16.1に変更します。kubectl editを使用してマニフェストを編集します。

kubectl edit deployment/my-nginx
# 新しいコンテナイメージを使用するようにマニフェストを変更し、変更を保存

以上で完了です!Deploymentは、デプロイされたnginxアプリケーションを宣言的に更新し、バックグラウンドで段階的に処理を進めます。これにより、更新中に一定数の古いレプリカのみが停止され、新しいレプリカが必要なPod数を超えて作成されることがないように制御されます。この仕組みの詳細については、Deploymentを参照してください。

ロールアウトは、DaemonSet、Deployment、StatefulSetに対して使用できます。

ロールアウトの管理

kubectl rolloutを使用すると、既存のアプリケーションの段階的な更新を管理できます。

例えば、次のように実行できます。

kubectl apply -f my-deployment.yaml

# ロールアウトの完了を待機
kubectl rollout status deployment/my-deployment --timeout 10m # 10分のタイムアウト

または、次のように実行できます。

kubectl apply -f backing-stateful-component.yaml

# ロールアウトの完了を待たず、ステータスのみを確認
kubectl rollout status statefulsets/backing-stateful-component --watch=false

さらに、ロールアウトを一時停止、再開、または取り消すことも可能です。 詳細については、kubectl rolloutを参照してください。

カナリアデプロイ

複数のラベルが必要となる別のシナリオとして、同じコンポーネントの異なるリリースや設定を区別する場合があります。 一般的な方法として、新しいアプリケーションリリース(Podテンプレート内のイメージタグで指定)を、以前のリリースと並行してカナリアデプロイすることがあります。これにより、新しいリリースが本番環境のトラフィックを受け取りつつ、完全にロールアウトする前に動作を確認できます。

例えば、trackラベルを使用して異なるリリースを区別することができます。

プライマリの安定したリリースには、trackラベルの値としてstableを設定します。

name: frontend
replicas: 3
...
labels:
   app: guestbook
   tier: frontend
   track: stable
...
image: gb-frontend:v3

その後、trackラベルの値を異なる値(例:canary)に設定した新しいguestbook frontendのリリースを作成することで、2つのPodセットが重ならないようにすることができます。

name: frontend-canary
replicas: 1
...
labels:
   app: guestbook
   tier: frontend
   track: canary
...
image: gb-frontend:v4

フロントエンドのServiceは、両方のレプリカセットにまたがるように、共通のラベルの部分集合(つまり、trackラベルを省略)を選択することで、トラフィックを両方のアプリケーションに振り分けることができます。

selector:
  app: guestbook
  tier: frontend

stableリリースとcanaryリリースのレプリカ数を調整することで、それぞれのリリースが本番トラフィックを受け取る割合(この例では、3:1)を決定できます。新しいリリースに十分な自信が持てたら、stableトラックを新しいアプリケーションリリースに更新し、canaryリリースを削除します。

アノテーションの更新

リソースにアノテーションを付与したい場合があります。 アノテーションは、ツールやライブラリなどの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=my-nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

システムに必要に応じてnginxのレプリカ数(1~3の範囲)を自動的に選択させるには、次のコマンドを実行します。

# これには、コンテナおよびPodのメトリクスの取得元が必要です
kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

これで、nginxのレプリカ数は必要に応じて自動的にスケールアップおよびスケールダウンされます。

詳しくは、kubectl scalekubectl autoscale、および水平Pod自動スケーリングのドキュメントを参照してください。

リソースのインプレース更新

作成したリソースに対して、小規模で影響の少ない更新を行う必要がある場合があります。

kubectl apply

構成ファイルのセットをソース管理下で管理すること(構成のコード化を参照)が推奨されています。これにより、構成対象のリソースのコードとともに、構成を保守・バージョン管理することができます。その後、kubectl applyを使用して、構成の変更をクラスターに反映できます。

このコマンドは、適用しようとしている設定のバージョンと、以前のバージョンを比較し、変更を適用します。指定していないプロパティに対する自動的な変更は上書きされません。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

基盤となる仕組みについて詳しく知りたい場合は、server-side applyを参照してください。

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
# 何らかの編集を行い、ファイルを保存

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

これにより、より大きな変更を簡単に行うことができます。 なお、EDITORまたはKUBE_EDITOR環境変数を指定することで、使用するエディターを設定できます。

詳しくは、kubectl editを参照してください。

kubectl patch

kubectl patchを使用すると、APIオブジェクトをインプレースで更新できます。このサブコマンドは、JSONパッチ、JSONマージパッチ、戦略的マージパッチをサポートしています。

詳細については、kubectl patchを使用したAPIオブジェクトのインプレース更新を参照してください。

破壊的な更新

場合によっては、一度初期化されると更新できないリソースのフィールドを変更する必要があることがあります。また、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

次の項目

5.5 - ワークロードの自動スケーリング

自動スケーリングによって、何らかのかたちでワークロードを自動的に更新できます。これによりクラスターはリソース要求の変化に対してより弾力的かつ効率的に対応できるようになります。

Kubernetesでは、現在のリソース要求に応じてワークロードをスケールできます。 これによりクラスターはリソース要求の変化に対してより弾力的かつ効率的に対応できるようになります。

ワークロードをスケールするとき、ワークロードによって管理されるレプリカ数を増減したり、レプリカで使用可能なリソースをインプレースで調整できます。

ひとつ目のアプローチは 水平スケーリング と呼ばれ、一方でふたつ目のアプローチは 垂直スケーリング と呼ばれます。

ユースケースに応じて、ワークロードをスケールするには手動と自動の方法があります。

ワークロードを手動でスケーリングする

Kubernetesはワークロードの 手動スケーリング をサポートします。 水平スケーリングはkubectl CLIを使用して行うことができます。 垂直スケーリングの場合、ワークロードのリソース定義を パッチ適用 する必要があります。

両方の戦略の例については以下をご覧ください。

ワークロードを自動でスケーリングする

Kubernetesはワークロードの 自動スケーリング もサポートしており、これがこのページの焦点です。

Kubernetesにおける オートスケーリング の概念は、一連のPodを管理するオブジェクト(例えばDeployment)を自動的に更新する機能を指します。

ワークロードを水平方向にスケーリングする

Kubernetesにおいて、 HorizontalPodAutoscaler (HPA)を使用してワークロードを水平方向に自動的にスケールできます。

これはKubernetes APIリソースおよびコントローラーとして実装されており、CPUやメモリ使用率のような観測されたリソース使用率と一致するようにワークロードのレプリカ数を定期的に調整します。

Deployment用のHorizontalPodAutoscalerを構成するウォークスルーチュートリアルがあります。

ワークロードを垂直方向にスケーリングする

FEATURE STATE: Kubernetes v1.25 (GA)

VerticalPodAutoscaler (VPA)を使用してワークロードを垂直方向に自動的にスケールできます。 HPAと異なり、VPAはデフォルトでKubernetesに付属していませんが、GitHubで見つかる別のプロジェクトです。

インストールすることにより、管理されたレプリカのリソースを どのように いつ スケールするのかを定義するワークロードのCustomResourceDefinitions(CRDs)を作成できるようになります。

備考:

VPAが機能するにはクラスターにMetrics Serverがインストールされている必要があります。

現時点では、VPAは4つの異なるモードで動作できます:

VPAの異なるモード
モード 説明
Auto 現在はRecreateです。これは将来インプレースアップデートに変更される可能性があります。
Recreate VPAはPod作成時にリソースリクエストを割り当てるだけでなく、要求されたリソースが新しい推奨事項と大きく異なる場合にそれらを削除することによって既存のPod上でリソースリクエストを更新します。
Initial VPAはPod作成時にリソースリクエストを割り当て、後から変更することはありません。
Off VPAはPodのリソース要件を自動的に変更しません。推奨事項は計算され、VPAオブジェクトで検査できます。

インプレースPodの水平スケーリング

FEATURE STATE: Kubernetes v1.35 (GA)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.35. It was first available in the v1.27 release.

Kubernetes 1.36の時点では、VPAはインプレースでのPodのリサイズをサポートしていませんが、この統合は現在作業中です。 インプレースでPodの手動リサイズをするには、コンテナリソースをインプレースでリサイズするを参照してください。

クラスターサイズに基づく自動スケーリング

クラスターのサイズに基づいてスケールする必要があるワークロード(例えばcluster-dnsや他のシステムコンポーネント)の場合は、Cluster Proportional Autoscalerを使用できます。 VPAと同じように、これはKubernetesのコア部分ではありませんが、独自のGitHubプロジェクトとしてホストされています。

Cluster Proportional Autoscalerはスケジュール可能なノードとコアの数を監視し、それに応じてターゲットワークロードのレプリカ数をスケールします。

レプリカ数を同じままにする必要がある場合、Cluster Proportional Vertical Autoscalerを使用してクラスターサイズに応じてワークロードを垂直方向にスケールできます。 このプロジェクトは現在ベータ版でありGitHubで見つけることができます。

Cluster Proportional Autoscalerがワークロードのレプリカ数をスケールする一方で、Cluster Proportional Vertical Autoscalerはクラスター内のノードおよび/またはコアの数に基づいてワークロード(例えばDeploymentやDaemonSet)のリソース要求を調整します。

イベント駆動型自動スケーリング

例えばKubernetes Event Driven Autoscaler (KEDA)を使用して、イベントに基づいてワークロードをスケールすることもできます。

KEDAは例えばキューのメッセージ数などの処理するべきイベント数に基づいてワークロードをスケールするCNCF Graduatedプロジェクトです。 様々なイベントソースに合わせて選択できる幅広いアダプターが存在します。

スケジュールに基づく自動スケーリング

ワークロードををスケールするためのもう一つの戦略は、例えばオフピークの時間帯にリソース消費を削減するために、スケーリング操作をスケジュールすることです。

イベント駆動型オートスケーリングと同様に、そのような動作はKEDAをCronスケーラーと組み合わせて使用することで実現できます。 Cronスケーラーによりワークロードをスケールインまたはスケールアウトするためのスケジュール(およびタイムゾーン)を定義できます。

クラスターのインフラストラクチャのスケーリング

ワークロードのスケーリングだけではニーズを満たすのに十分でない場合は、クラスターのインフラストラクチャ自体をスケールすることもできます。

クラスターのインフラストラクチャのスケーリングは通常ノードの追加または削除を意味します。 詳しくはNodeの自動スケーリングを読んでください。

次の項目

5.6 - 垂直Pod自動スケーリング

Kubernetesにおいて、VerticalPodAutoscaler は、ワークロード管理リソース(DeploymentStatefulSetなど)を自動的に更新し、インフラストラクチャリソース要求と制限を実際の使用状況に合わせて自動的に調整します。

垂直スケーリングとは、リソース需要が増加した際に、ワークロードで既に実行されているPodに、より多くのリソース(たとえば、メモリやCPUなど)を割り当てることを意味します。 これは、rightsizing や、時には autopilot とも呼ばれます。 これは、Kubernetesで負荷を分散するために追加のPodをデプロイする水平スケーリングとは異なります。

リソース使用量が減少し、Podのリソース要求が最適なレベルを上回っている場合、VerticalPodAutoscalerは、ワークロードリソース(Deployment、StatefulSet、または類似のリソース)に指示して、リソース要求を下げ、リソースの浪費を防ぎます。

VerticalPodAutoscalerは、Kubernetes APIリソースおよびコントローラーとして実装されています。 リソースがコントローラーの動作を決定します。 Kubernetesデータプレーン内で実行される垂直Pod自動スケーリングコントローラーは、過去のリソース使用率の分析、クラスターで利用可能なリソースの量、およびout-of-memory(OOM)条件などのリアルタイムイベントに基づいて、対象(Deploymentなど)のリソース要求と制限を定期的に調整します。

APIオブジェクト

VerticalPodAutoscalerは、Kubernetesでカスタムリソース定義(CRD)として定義されています。 KubernetesのコアAPIの一部であるHorizontalPodAutoscalerとは異なり、VPAはクラスターに個別にインストールする必要があります。

現在の安定版のAPIバージョンはautoscaling.k8s.io/v1です。VPAのインストール方法とAPIの詳細については、VPA GitHubリポジトリを参照してください。

VerticalPodAutoscalerはどのように動作するか?

graph BT
    metrics[Metrics Server]
    api[APIサーバー]
    admission[VPA Admission Controller]
    
    vpa_cr[VerticalPodAutoscaler CRD]
    recommender[VPA recommender]
    updater[VPA updater]

    metrics --> recommender
    recommender -->|推奨値を格納| vpa_cr

    subgraph アプリケーションワークロード
        controller[Deployment / RC / StatefulSet]
        pod[Pod / Container]
    end

    vpa_cr -->|変更を確認| updater
    updater -->|Podを退避またはインプレースで更新| controller
    controller -->|新しいPodをリクエスト| api

    api -->|新しいPodの作成| admission
    admission -->|最新の推奨値を取得| vpa_cr
    admission -->|新しいリソース値を注入| api

    api -->|Podを作成| controller
    controller -->|最適なリソースを持つ新しいPod| pod

    classDef vpa fill:#9FC5E8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
    classDef crd fill:#D5A6BD,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
    classDef metrics fill:#FFD966,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
    classDef app fill:#B6D7A8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;

    class recommender,updater,admission vpa;
    class vpa_cr crd;
    class metrics metrics;
    class controller,pod app;

図1. VerticalPodAutoscalerは、Deployment内のPodのリソース要求と制限を制御します

Kubernetesは、断続的に実行される複数の連携するコンポーネントを通じて垂直Pod自動スケーリングを実装します(継続的なプロセスではありません)。VPAは3つの主要なコンポーネントで構成されています:

  • リソース使用量を分析し、推奨値を提供する recommender
  • Podのリソース要求を、Podを退避させるか、またはその場で変更することで更新する updater
  • 新しく作成または再作成されたPodにリソース推奨値を適用する、VPA admission controller webhook

各サイクルごとに1回、Recommenderは各VerticalPodAutoscaler定義によって対象とされるPodのリソース使用率を照会します。Recommenderは、targetRefで定義された対象リソースを見つけ、対象リソースの.spec.selectorラベルに基づいてPodを選択し、リソースメトリクスAPIからメトリクスを取得して、実際のCPUとメモリ消費を分析します。

Recommenderは、VerticalPodAutoscalerによって対象とされる各Podの現在および過去のリソース使用データ(CPUとメモリ)を分析します。調査される要素には以下が含まれます:

  • トレンドを特定するための、時間経過に伴う過去の消費パターン
  • 十分な余裕を確保するための、ピーク使用量と変動
  • Out-of-memory(OOM)イベントおよびその他のリソース関連のインシデント

この分析に基づいて、Recommenderは3種類の推奨値を計算します:

  • 目標推奨値(通常の使用に最適なリソース)
  • 下限値(最小限の実行可能なリソース)
  • 上限値(最大の合理的なリソース)

これらの推奨値は、VerticalPodAutoscalerリソースの.status.recommendationフィールドに保存されます。

Updater コンポーネントは、VerticalPodAutoscalerリソースを監視し、現在のPodリソース要求を推奨値と比較します。 差異が設定された閾値を超え、更新ポリシーで許可されている場合、updaterは以下のいずれかを実行できます:

  • Podを退避し、新しいリソース要求で再作成をトリガーする(従来のアプローチ)
  • クラスターがインプレースPodリソース更新をサポートしている場合、退避せずにその場でPodリソースを更新する

選択される方法は、設定された更新モード、クラスターのケイパビリティ、および必要なリソース変更の種類に依存します。 インプレース更新が利用可能な場合、Podの中断を回避しますが、変更可能なリソースに制限がある場合があります。 updaterは、サービスへの影響を最小限に抑えるためにPodDisruptionBudgetを尊重します。

Admission controller は、Podの作成リクエストをインターセプトするmutating webhookとして動作します。 PodがVerticalPodAutoscalerの対象であるかどうかを確認し、対象である場合、Podが作成される前に推奨されるリソース要求と制限を適用します。 より具体的には、admission controllerはVerticalPodAutoscalerリソースの.status.recommendationスタンザ内のTarget recommendationを新しいリソースリクエストとして使用します。 Admission controllerは、初回デプロイ時、updaterによる退避後、またはスケーリング操作による場合のいずれであっても、新しいPodが適切なサイズのリソース割り当てで起動することを保証します。

VerticalPodAutoscalerは、クラスターにインストールされているKubernetesのメトリクスサーバーアドオンなどのメトリクスソースを必要とします。 VPAコンポーネントは、metrics.k8s.io APIからメトリクスを取得します。 メトリクスサーバーは、ほとんどのクラスターでデフォルトではデプロイされないため、個別に起動する必要があります。 リソースメトリクスの詳細については、メトリクスサーバーを参照してください。

更新モード

VerticalPodAutoscalerは複数の 更新モード をサポートしており、リソース推奨値がいつ、どのようにPodに適用されるかを制御することができます。 更新モードは、VPA specのupdatePolicy内にあるupdateModeフィールドで設定します:

---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Recreate"  # Off, Initial, Recreate, InPlaceOrRecreate

Off

Off 更新モードでは、VPA recommenderは引き続きリソース使用量を分析し、推奨値を生成しますが、これらの推奨値はPodに自動的に適用されません。 推奨値は、VPAオブジェクトの.statusフィールドにのみ保存されます。

kubectlなどのツールを使用して、.statusに含まれる推奨値を表示できます。

Initial

Initial モードでは、VPAはPodが最初に作成されたときにのみリソース要求を設定します。 推奨値が時間とともに変化しても、既に実行中のPodのリソースは更新しません。 推奨値はPod作成時にのみ適用されます。

Recreate

Recreate モードでは、VPAは、現在のリソース要求が推奨値と大きく異なる場合、Podを退避させることでPodリソースを積極的に管理します。 Podが退避されると、ワークロードコントローラー(Deployment、StatefulSetなどを管理)が代替のPodを作成し、VPA admission controllerが更新されたリソース要求を新しいPodに適用します。

InPlaceOrRecreate

InPlaceOrRecreateモードでは、VPAは可能な限りPodを再起動せずにPodリソース要求と制限を更新しようとします。ただし、特定のリソース変更に対してインプレース更新を実行できない場合、VPAはPodの退避にフォールバック(Recreateモードと同様)し、ワークロードコントローラーが更新されたリソースで代替のPodを作成できるようにします。

このモードでは、updaterはコンテナリソースのインプレースリサイズ機能を使用して、推奨値をインプレースで適用します。

Auto(非推奨)

備考:

Auto更新モードはVPAバージョン1.4.0から非推奨です。退避ベースの更新にはRecreateを、退避フォールバックを伴うインプレース更新にはInPlaceOrRecreateを使用してください。

Autoモードは現在、Recreateモードのエイリアスであり、同じように動作します。 これは、将来的に自動更新戦略を拡張できるようにするために導入されました。

リソースポリシー

リソースポリシーを使用すると、VerticalPodAutoscalerが推奨値を生成し、更新を適用する方法を細かく調整できます。 リソース推奨値の境界を設定し、管理するリソースを指定し、Pod内の個々のコンテナに対して異なるポリシーを設定できます。

リソースポリシーは、VPA specのresourcePolicyフィールドで定義します:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Recreate"
  resourcePolicy:
    containerPolicies:
    - containerName: "application"
      minAllowed:
        cpu: 100m
        memory: 128Mi
      maxAllowed:
        cpu: 2
        memory: 2Gi
      controlledResources:
      - cpu
      - memory
      controlledValues: RequestsAndLimits

minAllowedとmaxAllowed

これらのフィールドは、VPA推奨値の境界を設定します。 実際の使用データが異なる値を示唆していても、VPAはminAllowedを下回る、またはmaxAllowedを上回るリソースを推奨することはありません。

controlledResources

controlledResourcesフィールドは、VPAがPod内のコンテナに対して管理すべきリソースタイプを指定します。 指定されていない場合、VPAはデフォルトでCPUとメモリの両方を管理します。 VPAが特定のリソースのみを管理するように制限できます。 有効なリソース名には、cpumemoryが含まれます。

controlledValues

controlledValuesフィールドは、VPAがリソース要求、制限、または両方を制御するかどうかを決定します:

RequestsAndLimits
VPAは要求と制限の両方を設定します。制限は、Pod specで定義されたrequest-to-limitの比率に基づいて、要求に比例してスケールします。これはデフォルトのモードです。
RequestsOnly
VPAは要求のみを設定し、制限は変更されません。制限は維持され、使用量が制限を超えた場合、スロットリングまたはout-of-memory killをトリガーする可能性があります。

これら2つの概念の詳細については、要求と制限を参照してください。

LimitRangeリソース

Admission controllerとupdater VPAコンポーネントは、LimitRangeで定義された制約に準拠するよう推奨値を後処理します。 Kubernetesクラスター内でtypeがPodおよびContainerのLimitRangeリソースがチェックされます。

たとえば、Container LimitRangeリソースのmaxフィールドを超過した場合、両方のVPAコンポーネントは制限をmaxフィールドで定義された値まで引き下げ、Pod specにおけるrequest-to-limitの比率を維持するように、要求が比例して減少されます。

次の項目

クラスターで自動スケーリングを設定する場合、適切な数のノードを実行していることを確認するために、ノードの自動スケーリングの使用を検討することもお勧めします。 水平 Pod自動スケーリングの詳細についても参照してください。

6 - Service、負荷分散とネットワーク

Kubernetesにおけるネットワークの概念とリソース。

Kubernetesのネットワークモデル

Kubernetesのネットワークモデルは、いくつかの要素で構成されています:

  • クラスター内の各Podは、クラスター全体で一意のIPアドレスを取得します。

    • Podは独自のプライベートネットワーク名前空間を持ち、Pod内のすべてのコンテナで共有されます。 同じPod内の異なるコンテナで実行されているプロセスは、localhostで相互に通信できます。
  • Podネットワーク(クラスターネットワークとも呼ばれます)は、Pod間の通信を処理します。 これにより、以下が保証されます(意図的なネットワークセグメンテーションがない限り):

    • すべてのPodは、同じノード上にあるか、異なるノード上にあるかに関わらず、他のすべてのPodと通信できます。 Podは、プロキシやアドレス変換(NAT)を使用せずに直接通信できます。

      Windowsでは、このルールはホストネットワークPodには適用されません。

    • ノード上のエージェント(システムデーモンやkubeletなど)は、そのノード上のすべてのPodと通信できます。

  • Service APIは、複数のバックエンドPodによって実装されるサービスに対して、安定した(長期的な)IPアドレスまたはホスト名を提供します。 サービスを構成する個々のPodが時間とともに変化しても、このIPアドレスやホスト名は変わりません。

    • Kubernetesは、Serviceを構成するPodに関する情報を提供するため、EndpointSliceオブジェクトを自動的に管理します。

    • サービスプロキシ実装は、ServiceとEndpointSliceオブジェクトのセットを監視します。 そして、オペレーティングシステムまたはクラウドプロバイダーAPIを使用してパケットをインターセプトまたは書き換えることで、サービストラフィックをバックエンドにルーティングするようデータプレーンをプログラムします。

  • Gateway API(またはその前身であるIngress)を使用すると、クラスター外部のクライアントからServiceにアクセスできるようになります。

    • サポートされているクラウドプロバイダーを使用している場合、Service APIのtype: LoadBalancerを使用することで、シンプルではあるものの、設定の自由度が低いクラスターイングレスの仕組みを利用することができます。
  • NetworkPolicyは、Pod間、またはPodと外部との間のトラフィックを制御できる組み込みのKubernetes APIです。

古いコンテナシステムでは、異なるホスト上のコンテナ同士は自動的には通信できませんでした。 そのため、コンテナ間のリンクを明示的に作成したり、コンテナポートをホストポートにマッピングして他のホストから到達可能にしたりする必要がありました。 Kubernetesではこのような作業は不要です。 Kubernetesのネットワークモデルでは、ポート割り当て、名前解決、サービスディスカバリ、負荷分散、アプリケーション設定、マイグレーションといった観点から、Podは、VMや物理ホストと同じように扱えます。

Kubernetes本体で実装されているのは、このモデルの一部のみです。 その他の部分については、KubernetesがAPIを定義し、実際の機能は外部コンポーネントが提供します。これらのコンポーネントの一部はオプションです:

  • Podネットワーク名前空間のセットアップは、コンテナランタイムインターフェースを実装するシステムレベルのソフトウェアによって処理されます。

  • Podネットワーク自体は、Podネットワーク実装によって管理されます。 Linuxでは、ほとんどのコンテナランタイムがコンテナネットワークインターフェース(CNI)を使用してPodネットワーク実装と連携するため、これらの実装は CNIプラグイン と呼ばれることがよくあります。

  • Kubernetesは、kube-proxyと呼ばれるサービスプロキシのデフォルト実装を提供していますが、一部のPodネットワーク実装は、実装の他の部分とより密に統合された独自のサービスプロキシを使用します。

  • 一般的には、NetworkPolicyもPodネットワーク実装によって実装されます(一部のシンプルなPodネットワーク実装ではNetworkPolicyを実装していない場合や、管理者がNetworkPolicyサポートなしでPodネットワークを設定する場合があります。これらの場合、APIは引き続き存在しますが、効果はありません)。

  • Gateway APIの実装は多数存在し、特定のクラウド環境に特化したもの、「ベアメタル」環境に焦点を当てたもの、より汎用的なものなどがあります。

次の項目

アプリケーションをServiceに接続するチュートリアルでは、実践的な例を通じてServiceとKubernetesネットワークについて学ぶことができます。

クラスターのネットワークでは、クラスターのネットワークを設定する方法について説明し、関連する技術の概要も提供しています。

6.1 - Service

ワークロードが複数のバックエンドに分散している場合でも、クラスター内で実行されているアプリケーションを、単一の外部向けエンドポイントの背後に公開します。

Kubernetesにおいて、Serviceとは、 クラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開する方法です。

KubernetesにおけるServiceの主要な目的の一つは、既存のアプリケーションを改修することなく、サービスディスカバリの仕組みを利用できるようにすることです。 Pod内で実行するコードは、クラウドネイティブな環境向けに設計されたものでも、コンテナ化した古いアプリケーションでも構いません。 Serviceを使用することで、その複数のPodがネットワーク上でアクセス可能になり、クライアントから通信できるようになります。

Deploymentを使用してアプリケーションを実行する場合、DeploymentはPodを動的に作成、削除します。 常に、どれだけのPodが動作していて健全な状態であるかは分からず、健全なPodの名前すら分からない可能性もあります。 KubernetesのPodは、クラスターの望ましい状態に一致させるために作成、削除されます。 Podは一時的なリソースです(個々のPodに対して、信頼性が高く永続的であるとは期待すべきではありません)。

各Podは独自のIPアドレスを取得します(Kubernetesは、ネットワークプラグインがこれを保証することを期待しています)。 クラスター内の特定のDeploymentで、ある時点で実行されているPodの集合は、少し時が経過すると、異なるPodの集合になっている可能性があります。

これは、とある問題につながります。 Podの集合(「バックエンド」と呼びます)がクラスター内の他のPod(「フロントエンド」と呼びます)に機能を提供する場合、フロントエンドがワークロードのバックエンド部分を使用できるように、どのようにして接続対象のIPアドレスを発見し、追跡するのでしょうか?

ここで Service の登場です。

KubernetesにおけるService

Kubernetesの一部であるService APIは、Podのグループをネットワーク経由で公開するための抽象化です。 各Serviceオブジェクトは、エンドポイントの論理的な集合(通常、これらのエンドポイントはPodです)および、Podの公開方法についてのポリシーを定義します。

例として、3つのレプリカで実行されているステートレスな画像処理バックエンドについて考えます。 これらのレプリカは互いに代替可能です—フロントエンドはどのバックエンドを使用するかを気にしません。 バックエンドを構成する実際のPodは変化する可能性がありますが、フロントエンドのクライアントはそれを意識する必要はなく、バックエンドの集合を自身で追跡すべきでもありません。

Serviceという抽象化により、この分離が可能になります。

Serviceが対象とするPodは、通常、ユーザーが定義するセレクターによって決定されます。 Serviceのエンドポイントを定義する他の方法については、セレクター なし のServiceを参照してください。

ワークロードがHTTPを使用する場合、Ingressを使用して、Webトラフィックがワークロードに到達する方法を制御できます。 IngressはServiceタイプではありませんが、クラスターへのエントリポイントとして機能します。 Ingressを使用すると、ルーティングルールを単一のリソースに統合できるため、クラスター内で個別に実行されている複数のワークロードコンポーネントを、単一のリスナーの背後で公開できます。

KubernetesのGateway APIは、IngressやServiceを超える追加機能を提供します。 GatewayはCustomResourceDefinitionを使用して実装された拡張APIの一種であり、クラスターに追加することで、クラスター内で実行されているネットワークサービスへのアクセスを設定できます。

クラウドネイティブなサービスディスカバリ

アプリケーションでサービスディスカバリにKubernetes APIを使用できる場合、APIサーバーに問い合わせて一致するEndpointSliceを取得できます。 KubernetesはService内のPodの集合が変更されるたびに、そのServiceのEndpointSliceを更新します。

ネイティブではないアプリケーションの場合、KubernetesはアプリケーションとバックエンドのPodの間にネットワークポートまたはロードバランサーを配置する方法を提供します。

いずれの場合でも、ワークロードはこれらのサービスディスカバリの仕組みを使用して、接続先を見つけることができます。

Serviceの定義方法

Serviceはオブジェクトです(PodやConfigMapがオブジェクトであるのと同じです)。 Kubernetes APIを使用してServiceの定義を作成、表示、変更できます。 通常はkubectlのようなツールを使用して、これらのAPI呼び出しを行います。

例えば、TCPポート9376でリッスンし、app.kubernetes.io/name=MyAppというラベルが付けられたPodの集合があるとします。 そのTCPリスナーを公開するServiceを定義できます:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

このマニフェストを適用すると、デフォルトのClusterIP Serviceタイプで「my-service」という名前の新しいServiceが作成されます。 このServiceは、app.kubernetes.io/name: MyAppラベルを持つすべてのPodのTCPポート9376を対象とします。

KubernetesはこのServiceに対してIPアドレス(ClusterIP)を割り当てます。 このIPアドレスは仮想IPアドレスメカニズムによって使用されます。 このメカニズムの詳細については、仮想IPとServiceプロキシを参照してください。

このServiceのコントローラーは、セレクターに一致するPodを継続的にスキャンし、ServiceのEndpointSliceの集合に対して必要に応じて更新を行います。

Serviceオブジェクト名は、有効なRFC 1035ラベル名である必要があります。

備考:

Serviceは 任意の 受信porttargetPortにマッピングできます。 ただし、デフォルトでは、利便性のためにtargetPortportフィールドと同じ値に設定されます。

Serviceオブジェクトの緩和された命名要件

FEATURE STATE: Kubernetes v1.36 (Beta; (デフォルトで有効))

RelaxedServiceNameValidationフィーチャーゲートは、Serviceオブジェクト名が数字で始まることを許可します。 このフィーチャーゲートが有効化されている場合、Serviceオブジェクト名は有効なRFC 1123ラベル名である必要があります。

ポート定義

Pod内のポート定義には名前が含まれ、ServiceのtargetPort属性でこれらの名前を参照できます。 例えば、次のようにServiceのtargetPortをPodのポートにバインドできます:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

これは、Serviceに異なるポート番号で同じネットワークプロトコルを提供する複数のPodが混在している場合でも、単一の設定された名前を使用して機能します。 これにより、Serviceのデプロイと進化に大きな柔軟性がもたらされます。 たとえば、バックエンドソフトウェアの次のバージョンでPodが公開するポート番号を変更しても、クライアントを壊すことはありません。

ServiceのデフォルトプロトコルはTCPです。 他のサポートされているプロトコルも使用できます。

多くのServiceは複数のポートを公開する必要があるため、Kubernetesは単一のServiceに対して、複数のポート定義をサポートしています。 各ポート定義は、同じprotocolを持つことも、異なるものを持つことも可能です。

セレクター無しのService

Serviceは、セレクターを使ってKubernetes Podへのアクセスを抽象化するのが一般的です。 ただし、セレクター無しで対応するEndpointSliceオブジェクトを使用すれば、クラスター外のバックエンドなど、他の種類のバックエンドも抽象化できます。

例えば:

  • 本番環境では外部のデータベースクラスターを使用したいが、テスト環境では独自のデータベースを使用する場合。
  • Serviceを別のNamespaceまたは別のクラスター上のServiceに向けたい場合。
  • ワークロードをKubernetesに移行中の場合。 移行方法を評価している間は、バックエンドの一部のみをKubernetesで実行する場合。

これらのいずれのシナリオでも、Podに一致するセレクターを指定 せずに Serviceを定義できます。 例えば:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

このServiceにはセレクターが含まれないため、対応するEndpointSliceオブジェクトは自動的に作成されません。 EndpointSliceオブジェクトを手動で追加することで、実行されているネットワークアドレスとポートにServiceをマッピングできます。 例えば、次のように定義します:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # 慣習で、EndpointSliceの名前にプレフィックスとしてService名を使用します

  labels:
    # "kubernetes.io/service-name"ラベルを設定する必要があります。
    # その値はServiceの名前と一致させてください
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: http # 上記で定義したServiceポート名と一致させる必要があります
    appProtocol: http
    protocol: TCP
    port: 9376
endpoints:
  - addresses:
      - "10.4.5.6"
  - addresses:
      - "10.1.2.3"

カスタムEndpointSlice

Service用にEndpointSliceオブジェクトを作成する場合、EndpointSliceには任意の名前を使用できます。 ただし、名前空間内の各EndpointSliceは一意の名前である必要があります。 EndpointSliceにkubernetes.io/service-nameラベルを設定することで、EndpointSliceをServiceに関連付けられます。

備考:

エンドポイントのIPは次のものであっては いけません: ループバック(IPv4の場合は127.0.0.0/8、IPv6の場合は::1/128)、またはリンクローカル(IPv4の場合は169.254.0.0/16と224.0.0.0/24、IPv6の場合はfe80::/64)。

エンドポイントIPアドレスは、他のKubernetes ServiceのClusterIPにすることはできません。 これは、kube-proxyが宛先として仮想IPをサポートしていないためです。

自身で作成するEndpointSlice、または独自のコード内で作成するEndpointSliceの場合、endpointslice.kubernetes.io/managed-byラベルに使用する値も選択するべきです。 EndpointSliceを管理する独自のコントローラーコードを作成する場合は、"my-domain.example/name-of-controller"のような値の使用を検討してください。 サードパーティツールを使用している場合は、ツールの名前をすべて小文字で使用し、スペースやその他の句読点をダッシュ(-)に変更してください。 kubectlのようなツールを直接使用してEndpointSliceを管理する場合は、"staff""cluster-admins"のような、手動で管理していることを説明する名前を使用してください。 Kubernetes自身のコントロールプレーンによって管理されるEndpointSliceを識別する予約語である"controller"の使用は避けてください。

セレクターの無いServiceへのアクセス

セレクターの無いServiceへのアクセスは、セレクターがある場合と同じように機能します。 セレクターの無いServiceのでは、トラフィックはEndpointSliceマニフェストで定義された2つのエンドポイントのいずれかにルーティングされます: ポート9376上の10.1.2.3または10.4.5.6へのTCP接続です。

備考:

Kubernetes APIサーバーは、Podにマッピングされていないエンドポイントへのプロキシを許可しません。 kubectl port-forward service/<service-name> forwardedPort:servicePortのようなアクションは、Serviceにセレクターが無い場合、この制約により失敗します。 これにより、Kubernetes APIサーバーが、呼び出し元に未許可のエンドポイントへのプロキシとして使用されることを防ぐことができます。

ExternalName Serviceは、セレクターを持たず、代わりにDNS名を使用するServiceの特殊なケースです。 詳細については、ExternalNameセクションを参照してください。

EndpointSlice

FEATURE STATE: Kubernetes v1.21 (GA)

EndpointSliceは、Serviceの背後にあるネットワークエンドポイントのサブセット(スライス)を表すオブジェクトです。

Kubernetesクラスターは、各EndpointSliceが保持するエンドポイントの数を追跡します。 特定のServiceに紐づくエンドポイント数が多く、しきい値に達した場合、Kubernetesは新しい空のEndpointSliceを追加し、そこに新たなエンドポイント情報を保存します。 デフォルトでは、既存のすべてのEndpointSliceがそれぞれ、少なくとも100個のエンドポイントを含むと、Kubernetesは新しいEndpointSliceを作成します。 Kubernetesは、追加のエンドポイントを追加する必要性が生じるまで、新しいEndpointSliceを作成しません。

このAPIの詳細については、EndpointSliceを参照してください。

Endpoints(非推奨)

FEATURE STATE: Kubernetes v1.33 (非推奨)

EndpointSlice APIは、従来のEndpoints APIの後継です。 非推奨となったEndpoints APIには、EndpointSliceと比較していくつかの問題があります:

  • デュアルスタッククラスターをサポートしていません。
  • trafficDistributionのような新しい機能をサポートするために必要な情報が含まれていません。
  • エンドポイントのリストが単一のオブジェクトに収まらないほど長い場合、切り捨てられます。

このため、すべてのクライアントはEndpointsではなくEndpointSlice APIを使用することをお勧めします。

容量超過のEndpoints

Kubernetesでは、単一のEndpointsオブジェクトに格納できるエンドポイントの数は制限されています。 Serviceに1000個を超えるエンドポイントが存在する場合、KubernetesはEndpointsオブジェクト内のデータを切り捨てます。 Serviceは複数のEndpointSliceにリンクできるため、1000個という制限はレガシーのEndpoints APIにのみ影響します。

その場合、KubernetesはEndpointsオブジェクトに格納する最大1000個のバックエンドエンドポイントを選択し、Endpointsにendpoints.kubernetes.io/over-capacity: truncatedというアノテーションを設定します。 コントロールプレーンはまた、バックエンドのPod数が1000未満に減少した場合、このアノテーションを削除します。

トラフィックは引き続きバックエンドに送信されますが、レガシーのEndpoints APIに依存するロードバランシング機構は、利用可能なバックエンドのエンドポイントのうち最大1000個にのみトラフィックを送信します。

同様に、APIの制限によって、Endpointsを手動で更新して1000個を超えるエンドポイントを持つようにすることはできません。

アプリケーションプロトコル

FEATURE STATE: Kubernetes v1.20 (GA)

appProtocolフィールドは、各Serviceポートに対してアプリケーションプロトコルを指定する方法を提供します。 これは、実装が理解するプロトコルに対して、より高度な動作を提供するためのヒントとして使用されます。 このフィールドの値は、対応するEndpointsおよびEndpointSliceオブジェクトに反映されます。

このフィールドは、標準のKubernetesラベル構文に従います。 有効な値は次のいずれかです:

  • IANA標準サービス名

  • mycompany.com/my-custom-protocolのような実装定義のプレフィックス付きの名前。

  • Kubernetes定義のプレフィックス付きの名前:

プロトコル 説明
kubernetes.io/h2c RFC 7540で説明されている平文のHTTP/2
kubernetes.io/ws RFC 6455で説明されている平文のWebSocket
kubernetes.io/wss RFC 6455で説明されているTLS上のWebSocket

マルチポートService

一部のServiceでは、複数のポートを公開する必要があります。 Kubernetesでは、単一のServiceオブジェクトに複数のポート定義を設定できます。 Serviceに複数のポートを使用する場合、曖昧さを避けるために、すべてのポートに名前を付ける必要があります。 例えば、下記のように設定します:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

備考:

Kubernetesの名前全般と同様に、ポート名は小文字の英数字と-のみを含む必要があります。 ポート名は英数字で始まり、英数字で終わる必要もあります。

例えば、123-abcwebという名前は有効ですが、123_abc-webは無効です。

Serviceタイプ

アプリケーションの一部(例えば、フロントエンド)で、Serviceをクラスターの外部からアクセス可能な外部IPアドレスとして公開したい場合があります。

KubernetesのServiceタイプを使用すると、必要なServiceの種類を指定できます。

利用可能なtypeの値とその動作は次の通りです:

ClusterIP
クラスター内部のIPでServiceを公開します。 この値を選択すると、Serviceはクラスター内からのみ到達可能になります。 これは、Serviceに対して明示的にtypeを指定しない場合に使用されるデフォルトです。 IngressまたはGatewayを使用して、Serviceをパブリックインターネットに公開できます。
NodePort
各ノードのIPの静的ポート(NodePort)でServiceを公開します NodePortを利用可能にするために、Kubernetesは、type: ClusterIPのServiceを要求した場合と同じように、ClusterIPアドレスを設定します。
LoadBalancer
外部のロードバランサーを使用してServiceを外部に公開します。 Kubernetesはロードバランシングコンポーネントを直接提供しないため、独自に提供するか、Kubernetesクラスターをクラウドプロバイダーと統合する必要があります。
ExternalName
ServiceをexternalNameフィールドの内容(例えば、ホスト名api.foo.bar.example)にマッピングします。 このマッピングにより、クラスターのDNSサーバーがその外部ホスト名の値を持つCNAMEレコードを返すように設定されます。 いかなる種類のプロキシも設定されません。

Service APIのtypeフィールドは階層的な機能として設計されており、上位のタイプは下位のタイプの機能を含みます。 ただし、この階層的な設計には例外があります。 ロードバランサーのNodePort割り当てを無効にすることで、LoadBalancer Serviceを定義できます。

type: ClusterIP

このデフォルトのServiceタイプでは、クラスターが予約済みのIPアドレスプールからIPアドレスが割り当てられます。

他のいくつかのServiceタイプは、ClusterIPタイプを基盤として構築されています。

.spec.clusterIP"None"に設定したServiceを定義すると、KubernetesはIPアドレスを割り当てません。 詳細については、ヘッドレスServiceを参照してください。

独自のIPアドレスの選択

Service作成リクエストの一部として、独自のclusterIPアドレスを指定できます。 指定するためには、.spec.clusterIPフィールドを設定します。 例えば、再利用したい既存のDNSエントリがある場合や、特定のIPアドレス用に設定されていて再設定が困難なレガシーシステムがある場合などです。

選択するIPアドレスは、APIサーバーに設定されているservice-cluster-ip-range CIDR範囲内の有効なIPv4またはIPv6アドレスである必要があります。 無効なclusterIPアドレスの値でServiceを作成しようとすると、APIサーバーは問題が発生したことを示す422 HTTPステータスコードを返します。

Kubernetesがどのように、2つの異なるServiceが同じIPアドレスを使用しようとする際のリスクと影響を軽減するかについては、衝突の回避を参照してください。

type: NodePort

typeフィールドをNodePortに設定すると、Kubernetesコントロールプレーンは--service-node-port-rangeフラグで指定された範囲(デフォルト: 30000-32767)からポートを割り当てます。 各ノードはそのポート(すべてのノードで同じポート番号)をServiceにプロキシします。 割り当てられたポートは、Serviceの.spec.ports[*].nodePortフィールドで確認できます。

NodePortを使用すると、独自のロードバランシングソリューションのセットアップや、Kubernetesで完全にサポートされていない環境の構成、あるいはノードのIPアドレスの直接公開さえ可能になります。

NodePortタイプのServiceの場合、Kubernetesはさらにポート(Serviceのプロトコルに合わせてTCP、UDP、またはSCTP)を割り当てます。 クラスター内のすべてのノードは、割り当てられたポートをリッスンし、Serviceに関連付けられた準備完了状態のエンドポイントの1つにトラフィックを転送するように自身を構成します。 適切なプロトコル(例: TCP)と適切なポート(そのServiceに割り当てられたポート)を使用して任意のノードに接続することで、クラスター外からtype: NodePortのServiceにアクセスできます。

独自のポート選択

特定のポート番号が必要な場合は、nodePortフィールドに値を指定できます。 コントロールプレーンはそのポートを割り当てるか、APIのトランザクションが失敗したことを報告します。 つまり、ポートの衝突を自分で処理する必要があります。 また、NodePortによる使用のために設定された範囲内の有効なポート番号を使用する必要があります。

以下は、NodePort値(この例では、30007)を指定したtype: NodePortのServiceのマニフェストの例です:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      # デフォルトでは、利便性のために`targetPort`は
      # `port`フィールドと同じ値に設定されます。
      targetPort: 80
      # オプションのフィールド
      # デフォルトでは、利便性のためにKubernetesコントロールプレーンは
      # 範囲(デフォルト: 30000-32767)からポートを割り当てます
      nodePort: 30007

ポート衝突を避けるためのNodePort範囲の予約

NodePortサービスへのポート割り当てのポリシーは、自動割り当てと手動割り当ての両方のシナリオに適用されます。 ユーザーが特定のポートを使用するNodePortサービスを作成する場合、ターゲットポートはすでに割り当てられている別のポートと競合する可能性があります。

この問題を回避するため、NodePortサービスのポート範囲は2つの帯域に分割されています。 動的なポート割り当てはデフォルトで上位の帯域を使用し、上位の帯域が使い尽くされると下位帯域を使用することがあります。 ユーザーは、下位の帯域から割り当てることでポート衝突のリスクを低減することができます。

デフォルトのNodePortの範囲である30000-32767を使用する場合、各帯域は次のように分割されます:

  • 静的帯域: 30000-30085
  • 動的帯域: 30086-32767

静的帯域と動的帯域がどのように計算されるかの詳細は、NodePort Serviceへのポート割り当てにおける衝突の回避を参照してください。

type: NodePort ServiceのカスタムIPアドレス設定

NodePortサービスを提供するために特定のIPアドレスを使用するよう、クラスター内のノードを設定できます。 各ノードが複数のネットワークに接続されている場合(例: アプリケーショントラフィック用のネットワークと、ノード間およびコントロールプレーン間のトラフィック用の別のネットワーク)に、この設定が必要になることがあります。

ポートをプロキシする特定のIPアドレスを指定する場合は、kube-proxyの--nodeport-addressesフラグ、またはkube-proxy設定ファイルの同等のnodePortAddressesフィールドを特定のIPブロックに設定できます。

このフラグは、カンマ区切りのIPブロックリスト(例: 10.0.0.0/8192.0.2.0/25)を受け取り、kube-proxyがこのノードのローカルとみなすIPアドレス範囲を指定します。

例えば、--nodeport-addresses=127.0.0.0/8フラグでkube-proxyを起動すると、kube-proxyはNodePortサービスに対してループバックインターフェースのみを選択します。 --nodeport-addressesのデフォルトは空のリストです。 これは、kube-proxyがNodePortに対して利用可能なすべてのネットワークインターフェースを考慮する必要があることを意味します。(これは以前のKubernetesリリースとも互換性があります。)

備考:

このServiceは<NodeIP>:spec.ports[*].nodePortおよび.spec.clusterIP:spec.ports[*].portとして表示されます。 kube-proxyの--nodeport-addressesフラグまたはkube-proxy設定ファイルの同等のフィールドが設定されている場合、<NodeIP>はフィルタリングされたノードのIPアドレス(または複数のIPアドレスの可能性)になります。

type: LoadBalancer

外部ロードバランサーをサポートするクラウドプロバイダーでは、typeフィールドをLoadBalancerに設定すると、Service向けにロードバランサーがプロビジョニングされます。 実際のロードバランサーの作成は非同期で行われ、プロビジョニングされたバランサーに関する情報はServiceの.status.loadBalancerフィールドで公開されます。 たとえば、次のように設定します:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

外部ロードバランサーからのトラフィックはバックエンドのPodに転送されます。 負荷分散の方法は、クラウドプロバイダーが決定します。

type: LoadBalancerのServiceを実装するために、Kubernetesは通常、まずtype: NodePortのServiceをリクエストした場合と同等の変更を行います。 その後、cloud-controller-managerコンポーネントが外部ロードバランサーを設定し、割り当てられたNodePortにトラフィックを転送します。

クラウドプロバイダーの実装でサポートされている場合、LoadBalancerタイプのServiceを設定してNodePortの割り当てを省略できます。

一部のクラウドプロバイダーではloadBalancerIPを指定できます。 その場合、ユーザーが指定したloadBalancerIPでロードバランサーが作成されます。 loadBalancerIPフィールドが指定されていない場合、ロードバランサーは一時的なIPアドレスで設定されます。 loadBalancerIPを指定しても、クラウドプロバイダーがこの機能をサポートしていない場合、設定したloadbalancerIPフィールドは無視されます。

備考:

Serviceの.spec.loadBalancerIPフィールドはKubernetes v1.24で非推奨になりました。

このフィールドは仕様が不十分で、実装によって意味が異なっていました。 また、デュアルスタックネットワーキングをサポートできません。 このフィールドは将来のAPIバージョンで削除される可能性があります。

(プロバイダー固有の)アノテーションを介してServiceのロードバランサーIPアドレスの指定をサポートするプロバイダーと統合する場合は、その方法に切り替えてください。

Kubernetesとロードバランサーの統合コードを書いている場合は、このフィールドの使用を避けてください。 ServiceではなくGatewayと統合するか、同等の詳細を指定するService上の独自の(プロバイダー固有の)アノテーションを定義できます。

ノードの稼働状態がロードバランサーのトラフィックに与える影響

ロードバランサーによるヘルスチェックは現代のアプリケーションにとって重要です。 これらはロードバランサーがトラフィックをディスパッチするサーバー(仮想マシンまたはIPアドレス)を決定するために使用されます。 Kubernetes APIはKubernetesが管理するロードバランサーに対してヘルスチェックをどのように実装する必要があるかを定義していません。 代わりに、クラウドプロバイダー(および統合コードを実装する人々)によって動作が決定されます。 ロードバランサーによるヘルスチェックはServiceのexternalTrafficPolicyフィールドをサポートする文脈で広く使用されています。

混合プロトコルタイプのロードバランサー

This is a stable feature in Kubernetes, and has been since the 1.26 release. You can no longer toggle this feature (the associated feature gate has been removed).

デフォルトでは、LoadBalancerタイプのServiceで複数のポートが定義されている場合、すべてのポートは同じプロトコルを持つ必要があり、そのプロトコルはクラウドプロバイダーがサポートするものでなければなりません。

フィーチャーゲートMixedProtocolLBService(v1.24以降のkube-apiserverではデフォルトで有効)により、複数のポートが定義されている場合、LoadBalancerタイプのServiceで異なるプロトコルを使用できます。

備考:

ロードバランサーServiceで使用できるプロトコルのセットは、クラウドプロバイダーによって定義されます。 クラウドプロバイダーは、Kubernetes APIが強制する制限を超えた追加の制限を課す場合があります。

ロードバランサーによるNodePort割り当ての無効化

FEATURE STATE: Kubernetes v1.24 (GA)

spec.allocateLoadBalancerNodePortsフィールドをfalseに設定することで、type: LoadBalancerのServiceに対するNodePort割り当てをオプションで無効にできます。 これは、NodePortを使用せずに、トラフィックをPodに直接ルーティングするロードバランサー実装に対してのみ使用してください。 デフォルトでは、spec.allocateLoadBalancerNodePortstrueであり、LoadBalancerタイプのServiceは引き続きNodePortを割り当てます。 既に割り当て済みのNodePortを持つ既存のServiceに対してspec.allocateLoadBalancerNodePortsfalseに設定しても、それらのNodePortは自動的に割り当て解除されません。 NodePortの割り当てを解除するには、すべてのServiceポートのnodePortsエントリを明示的に削除する必要があります。

ロードバランサー実装のクラスの指定

FEATURE STATE: Kubernetes v1.24 (GA)

typeLoadBalancerに設定されたServiceの場合、.spec.loadBalancerClassフィールドを使用すると、クラウドプロバイダーのデフォルト以外のロードバランサー実装を使用できます。

デフォルトでは、.spec.loadBalancerClassは設定されておらず、クラスターが--cloud-providerコンポーネントフラグを使用してクラウドプロバイダーで設定されている場合、LoadBalancerタイプのServiceはクラウドプロバイダーのデフォルトロードバランサー実装を使用します。

.spec.loadBalancerClassを指定すると、指定されたクラスに一致するロードバランサー実装がServiceを監視していると想定されます。 デフォルトのロードバランサー実装(たとえば、クラウドプロバイダーが提供するもの)は、このフィールドが設定されたServiceを無視します。 spec.loadBalancerClassLoadBalancerタイプのServiceにのみ設定できます。 一度設定すると、変更できません。 spec.loadBalancerClassの値はラベル形式の識別子である必要があり、"internal-vip"や"example.com/internal-vip"などのプレフィックスをオプションとして持つことができます。 プレフィックスのない名前はエンドユーザー向けに予約されています。

ロードバランサーのIPアドレスモード

This is a stable feature in Kubernetes, and has been since the 1.32 release. You can no longer toggle this feature (the associated feature gate has been removed).

type: LoadBalancerのServiceの場合、コントローラーは.status.loadBalancer.ingress.ipModeを設定できます。 .status.loadBalancer.ingress.ipModeは、ロードバランサーIPの動作方法を指定します。 これは.status.loadBalancer.ingress.ipフィールドも指定されている場合にのみ指定できます。

.status.loadBalancer.ingress.ipModeには、「VIP」と「Proxy」の2つの値が設定できます。 デフォルト値は「VIP」で、これはトラフィックがロードバランサーのIPとポートを宛先として設定された状態でノードに配信されることを意味します。 「Proxy」に設定する場合、クラウドプロバイダーのロードバランサーがトラフィックを配信する方法に応じて、2つのケースがあります:

  • トラフィックがノードに配信されてからPodにDNATされる場合、宛先はノードのIPとNodePortに設定されます。
  • トラフィックがPodに直接配信される場合、宛先はPodのIPとポートに設定されます。

Service実装はこの情報を使用してトラフィックルーティングを調整できます。

内部ロードバランサー

混在環境では、同じ(仮想)ネットワークアドレスブロック内のServiceからトラフィックをルーティングする必要がある場合があります。

スプリットホライズンDNS環境では、エンドポイントへの外部トラフィックと内部トラフィックの両方をルーティングできるように、2つのServiceが必要です。

内部ロードバランサーを設定するには、使用しているクラウドサービスプロバイダーに応じて、次のいずれかのアノテーションをServiceに追加します:

いずれかのタブを選択してください。

metadata:
  name: my-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"
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
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-internal: true

type: ExternalName

ExternalNameタイプのServiceは、my-servicecassandraのような典型的なセレクターではなく、ServiceをDNS名にマッピングします。 これらの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

備考:

type: ExternalNameのServiceはIPv4アドレスの文字列を受け入れますが、文字列をIPアドレスとしてではなく、数字で構成されたDNS名として扱います(ただし、インターネットでは、DNSにそのような名前を設定することは許可されていません)。 IPv4アドレスに似た外部名を持つServiceはDNSサーバーによって解決されません。

ServiceをIPアドレスに直接マッピングしたい場合は、ヘッドレスServiceの使用を検討してください。

ホストmy-service.prod.svc.cluster.localを検索すると、クラスターのDNSサービスはmy.database.example.comを持つCNAMEレコードを返します。 my-serviceへのアクセスは他のServiceと同じように機能しますが、重要な違いは、リダイレクトがプロキシや転送を介してではなく、DNSレベルで発生することです。 後でデータベースをクラスター内に移動することにした場合、Podを起動し、適切なセレクターまたはエンドポイントを追加し、Serviceのtypeを変更できます。

注意:

ExternalNameを使用すると、HTTPやHTTPSを含む一部の一般的なプロトコルで問題が発生する可能性があります。 ExternalNameを使用する場合、クラスター内のクライアントが使用するホスト名は、ExternalNameが参照する名前とは異なります。

ホスト名を使用するプロトコルの場合、この違いによりエラーや予期しないレスポンスが発生する可能性があります。 HTTPリクエストには、オリジンサーバーが認識しないHost:ヘッダーが含まれることになります。 また、TLSサーバーは、クライアントが接続したホスト名と一致する証明書を提供できなくなります。

ヘッドレスService

時には、負荷分散や単一のService IPが不要な場合があります。 この場合、ClusterIPアドレス(.spec.clusterIP)に明示的に"None"を指定することで、いわゆる ヘッドレスService を作成できます。

ヘッドレスServiceを使用すると、Kubernetesの実装に縛られることなく、他のサービスディスカバリ機構と連携できます。

ヘッドレスServiceの場合、ClusterIPは割り当てられず、kube-proxyはこれらのServiceを処理せず、プラットフォームによる負荷分散やプロキシは行われません。

ヘッドレスServiceにより、クライアントは好みのPodに直接接続できます。 ヘッドレスServiceは仮想IPアドレスとプロキシを使用してルートやパケット転送を設定しません。 その代わりに、ヘッドレスServiceはクラスターのDNSサービスを通じて提供される内部DNSレコードを介して、個々のPodのエンドポイントIPアドレスを報告します。 ヘッドレスServiceを定義するには、.spec.typeをClusterIPに設定し(typeのデフォルトでもあります)、さらに.spec.clusterIPをNoneに設定します。

文字列値のNoneは特殊なケースであり、.spec.clusterIPフィールドを未設定のままにすることとは異なります。

DNSが自動的に設定される方法は、Serviceにセレクターが定義されているかどうかによって異なります:

セレクターありの場合

セレクターを定義するヘッドレスServiceの場合、エンドポイントコントローラーはKubernetes APIでEndpointSliceを作成し、ServiceのPodを直接指すAまたはAAAAレコード(IPv4またはIPv6アドレス)を返すようにDNS設定を変更します。

セレクターなしの場合

セレクターを定義しないヘッドレスServiceの場合、コントロールプレーンはEndpointSliceオブジェクトを作成しません。 ただし、DNSシステムは次のいずれかを検索して設定します:

  • type: ExternalName ServiceのDNS CNAMEレコード
  • ExternalName以外のすべてのServiceタイプについて、Serviceの準備完了エンドポイントのすべてのIPアドレスに対するDNS A/AAAAレコード
    • IPv4エンドポイントの場合、DNSシステムはAレコードを作成します。
    • IPv6エンドポイントの場合、DNSシステムはAAAAレコードを作成します。

セレクターなしでヘッドレスServiceを定義する場合、porttargetPortと一致する必要があります。

Serviceの検出

クラスター内で実行されているクライアントに対して、Kubernetesは、環境変数とDNSという2つの主要なServiceの検出方法をサポートしています。

環境変数

Podがノード上で実行されると、kubeletはアクティブな各Serviceに対して環境変数のセットを追加します。 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT変数が追加されます。 ここで、Service名は大文字に変換され、ハイフンはアンダースコアに変換されます。

たとえば、TCPポートとして6379を公開し、ClusterIPアドレスとして10.0.0.11が割り当てられたService redis-primaryは、次の環境変数を生成します:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

備考:

PodがServiceにアクセスする必要があり、環境変数を使用してポートとClusterIPをクライアントPodに公開する場合、クライアントPodが作成される前にServiceを作成する必要があります。 そうしないと、クライアントPodに環境変数が設定されません。

DNSのみを使用してServiceのClusterIPを検出する場合は、この順序の問題を気にする必要はありません。

Kubernetesは、Docker Engineの「レガシーコンテナリンク」機能と互換性のある変数もサポートし、提供しています。 これが、Kubernetesにおいて、どのように実装されているかを確認するには、makeLinkVariablesを参照してください。

DNS

アドオンを使用して、KubernetesクラスターのDNSサービスを設定できます(そしてほぼ常に設定すべきです)。

CoreDNSなどのクラスター対応のDNSサーバーは、新しいServiceについてKubernetes APIを監視し、それぞれに対してDNSレコードのセットを作成します。 クラスター全体でDNSが有効になっている場合、すべてのPodはDNS名によってServiceを自動的に解決できるはずです。

たとえば、KubernetesのNamespace my-nsmy-serviceというServiceがある場合、コントロールプレーンとDNS Serviceが連携してmy-service.my-nsのDNSレコードを作成します。 my-ns Namespace内のPodは、my-serviceの名前解決を行うことでServiceを見つけることができるはずです(my-service.my-nsも機能します)。

他のNamespace内のPodは、名前をmy-service.my-nsとして修飾する必要があります。 これらの名前は、Serviceに割り当てられたClusterIPに解決されます。

Kubernetesは、名前付きポートに対するDNS SRV(Service)レコードもサポートしています。 my-service.my-ns ServiceにプロトコルがTCPに設定されたhttpという名前のポートがある場合、_http._tcp.my-service.my-nsに対してDNS SRVクエリを実行して、httpのポート番号とIPアドレスを検出できます。

Kubernetes DNSサーバーは、ExternalName Serviceにアクセスする唯一の方法です。 ExternalNameの解決に関する詳細情報は、ServiceとPodに対するDNSを参照してください。

仮想IPアドレッシング機構

仮想IPとServiceプロキシでは、Kubernetesが仮想IPアドレスを使用してServiceを公開するために提供する機構について詳しく説明しています。

トラフィックポリシー

.spec.internalTrafficPolicyおよび.spec.externalTrafficPolicyフィールドを設定して、Kubernetesが正常な("ready")バックエンドにトラフィックをルーティングする方法を制御できます。

詳細については、トラフィックポリシーを参照してください。

トラフィック分散制御

.spec.trafficDistributionフィールドは、Kubernetes Service内のトラフィックルーティングに影響を与える別の方法を提供します。 トラフィックポリシーが厳密なセマンティック保証に焦点を当てているのに対し、トラフィック分散では 優先設定(トポロジー的に近いエンドポイントへのルーティングなど)を表現できます。 これにより、パフォーマンス、コスト、または信頼性の最適化に役立ちます。 Kubernetes 1.36では、次のフィールド値がサポートされています:

PreferSameZone
クライアントと同じゾーンにあるエンドポイントへのトラフィックルーティングの優先設定を示します。
PreferSameNode
クライアントと同じノード上にあるエンドポイントへのトラフィックルーティングの優先設定を示します。
PreferClose(非推奨)
これはPreferSameZoneの旧称にあたるエイリアスであり、この名称が示す対象が明確ではないため推奨されません。

フィールドが設定されていない場合、実装はデフォルトのルーティング戦略を適用します。

詳細については、トラフィック分散を参照してください。

スティッキーセッション

特定のクライアントからの接続が毎回同じPodに渡されるようにしたい場合、クライアントのIPアドレスに基づいてセッションアフィニティを設定できます。 詳細については、セッションアフィニティを参照してください。

外部IP

もし、1つ以上のクラスターノードに転送する外部IPが複数ある場合、Kubernetes ServiceはそれらをexternalIPsで公開できます。 外部IP(宛先IPとして)とServiceに一致するポートでネットワークトラフィックがクラスターに到達すると、Kubernetesが設定したルールとルートにより、トラフィックがそのServiceのエンドポイントの1つにルーティングされることが保証されます。

Serviceを定義する際、任意のServiceタイプに対してexternalIPsを指定できます。 以下の例では、"my-service"という名前のServiceは、"198.51.100.32:80"(.spec.externalIPs[].spec.ports[].portから計算)でTCPを使用してクライアントからアクセスできます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 49152
  externalIPs:
    - 198.51.100.32

備考:

KubernetesはexternalIPsの割り当てを管理しません。 これらはクラスター管理者の責任です。

APIオブジェクト

ServiceはKubernetes REST APIにおいてトップレベルのリソースです。 詳細については、Service APIオブジェクトを参照してください。

次の項目

Serviceと、それがKubernetesにどのように適合するかについて、さらに学習してください:

  • Connecting Applications with Servicesのチュートリアルに従ってください。
  • Ingressを参照してください。 Ingressは、クラスター外部からクラスター内のServiceへのHTTPおよびHTTPSルートを公開します。
  • Gatewayを参照してください。 GatewayはKubernetesの拡張機能で、Ingressよりも柔軟性を提供します。

さらなる背景情報については、以下を参照してください:

6.2 - Ingress

FEATURE STATE: Kubernetes v1.19 (GA)

クラスター内の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リソース

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 と要素単位で前方一致する場合です。

    備考:

    パスの最後の要素がリクエストパスの最後の要素の部分文字列である場合、これは一致しません(例えば、/foo/bar/foo/bar/bazと一致しますが、/foo/barbazとは一致しません)。

タイプ パス リクエストパス 一致するか
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がワイルドカードルールに沿って後方一致することが必要です。

Host Hostヘッダー 一致するか
*.foo.com bar.foo.com 共通の接尾辞により一致
*.foo.com baz.bar.foo.com 一致しない。ワイルドカードは単一のDNSラベルのみを対象とする
*.foo.com foo.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
<div class="feature-state-notice feature-stable">
  <span class="feature-state-name">FEATURE STATE:</span>
  <span class="feature-state-details">
   Kubernetes v1.23 (GA)
  </span>
</div>

.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が割り当てられるようになります。

注意:

複数のIngressClassをクラスターのデフォルトに設定すると、アドミッションコントローラーはingressClassNameが指定されていない新しいIngressオブジェクトを作成できないようにします。クラスターのデフォルトIngressClassを1つ以下にすることで、これを解消することができます。

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を利用するためのものです。

備考:

IngressコントローラーとロードバランサーがIPアドレス割り当てるのに1、2分ほどかかります。この間、ADDRESSの情報は<pending>となっているのを確認できます。

リクエストのシンプルなルーティング

ファンアウト設定では単一の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フィールドでロードバランサーのアドレスを確認できます。

備考:

使用するIngressコントローラーに依存しますが、default-http-backendServiceの作成が必要な場合があります。

名前ベースのバーチャルホスティング

名前ベースのバーチャルホストは、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)を含む証明書から作成したものであることを確認する必要があります。

備考:

デフォルトルールではTLSが機能しない可能性があることに注意してください。 これは取り得る全てのサブドメインに対する証明書を発行する必要があるからです。 そのため、tlsセクションのhostsrulesセクションのhostと明示的に一致する必要があります。
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

備考:

サポートされるTLSの機能はIngressコントローラーによって違いがあります。利用する環境でTLSがどのように動作するかを理解するためには、nginxや、GCE、または他のプラットフォーム固有のIngressコントローラーのドキュメントを確認してください。

負荷分散

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リソースを直接含まずにサービスを公開する方法は複数あります。

次の項目

6.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クラスターアドオンを必要とします。

備考:

サービス環境変数が望ましくない場合(予想されるプログラム変数と衝突する可能性がある、処理する変数が多すぎる、DNSのみを使用するなど)、Pod仕様enableServiceLinksフラグをfalseに設定することでこのモードを無効にできます。

環境変数

ノードで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:
  # 注意: 以下の値はご自身で base64 エンコードした証明書と鍵に置き換えてください。
  nginx.crt: "REPLACE_WITH_BASE64_CERT"
  nginx.key: "REPLACE_WITH_BASE64_KEY"

ファイルを使用して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
...

次の項目

6.4 - Ingressコントローラー

クラスターでIngressを動作させるためには、ingress controller が動作している必要があります。 少なくとも1つのIngressコントローラーを選択し、クラスター内にセットアップされていることを確認する必要があります。 このページはデプロイ可能な一般的なIngressコントローラーをリストアップします。

Ingressリソースが動作するためには、クラスターでIngressコントローラーが実行されている必要があります。

kube-controller-managerバイナリの一部として実行される他のタイプのコントローラーとは異なり、Ingressコントローラーはクラスターで自動的に起動されません。このページを使用して、クラスターに最適なIngressコントローラーの実装を選択してください。

プロジェクトとしてのKubernetesは現在、AWSGCE、およびnginxのIngressコントローラーをサポート・保守しています。

追加のコントローラー

備考: このセクションでは、Kubernetesが必要とする機能を提供するサードパーティプロジェクトにリンクしています。これらのプロジェクトはアルファベット順に記載されていて、Kubernetesプロジェクトの作者は責任を持ちません。このリストにプロジェクトを追加するには、変更を提出する前にコンテンツガイドをお読みください。詳細はこちら。

複数の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コントローラーはわずかに異なる動作をします。

備考:

Ingressコントローラーのドキュメントを確認して、選択する際の注意点を理解してください。

次の項目

6.5 - Gateway API

Gateway APIは動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類のファミリーです。

拡張可能でロール指向な、プロトコルを意識した設定メカニズムを使用して、ネットワークサービスを利用可能にします。 Gateway APIは、動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類を含むアドオンです。

デザイン原則

Gateway APIのデザインとアーキテクチャは次の原則から成ります:

  • ロール指向: Gateway APIの種類は、Kubernetesのサービスネットワークの管理に対して責任を持つ組織のロールをモデルとしています:
    • インフラストラクチャプロバイダー: 複数の独立したクラスターをクラウドプロバイダーなどの複数のテナントに提供できるよう、インフラストラクチャを管理します。
    • クラスターオペレーター: クラスターを管理し、通常はポリシー、ネットワークアクセス、アプリケーションのパーミッションなどに関わります。
    • アプリケーション開発者: クラスター上で実行されるアプリケーションを管理し、通常はアプリケーションレベルの設定やServiceの構成に関わります。
  • ポータビリティ: Gateway APIの仕様はカスタムリソースとして定義され、多くの実装によってサポートされています。
  • 豊富な機能: Gateway APIの種類は、ヘッダーベースのマッチング、トラフィックの重み付けといった、一般的なトラフィックルーティングのユースケースに対する機能をサポートしています。これは、Ingressではカスタムアノテーションを使用することでのみ実現可能でした。
  • 拡張可能: GatewayはカスタムリソースをAPIのさまざまなレイヤーでリンクさせることができます。これにより、APIの構造内の適切な場所で細かなカスタマイズが可能となります。

リソースモデル

Gateway APIには3つの安定版のAPIの種類があります:

  • GatewayClass: 共通の設定を持ち、クラスを実装するコントローラーによって管理されたゲートウェイの集合を定義します。

  • Gateway: クラウドロードバランサーなどのトラフィックを処理するインフラストラクチャのインスタンスを定義します。

  • HTTPRoute: Gatewayリスナーからバックエンドのネットワークエンドポイントへのトラフィックのマッピングに関する、HTTP固有のルールを定義します。 これらのエンドポイントは多くの場合、Serviceで表されます。

Gateway APIは、組織のロール指向の性質をサポートするために、相互に依存関係を持つ異なるAPIの種類によって構成されます。 Gatewayオブジェクトはただ一つのGatewayClassと関連づけられます。 GatewayClassは、このクラスのGatewayを管理する責任を持つGatewayコントローラーを記述します。 HTTPRouteのような1つ以上のルートの種類がGatewayに関連づけられます。 Gatewayは、そのリスナーにアタッチされる可能性のあるルートをフィルタリングすることができ、ルートとの双方向の信頼モデルを形成します。

次の図は、3つの安定版のGateway APIの種類の関係を示しています:

3つの安定版のGateway API種別の関係を示している図

GatewayClass

Gatewayは、通常異なる設定を持つ、異なるコントローラーによって実装されます。 Gatewayはクラスを実装したコントローラーの名前を含むGatewayClassを参照する必要があります。

最小のGatewayClassの例:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-class
spec:
  controllerName: example.com/gateway-controller

この例では、Gateway APIを実装したコントローラーは、example.com/gateway-controllerという名前のコントローラーを持つGatewayClassを管理するように構成されます。 このクラスのGatewayは実装のコントローラーによって管理されます。

このAPIの種類の完全な定義については、GatewayClassのリファレンスを参照してください。

Gateway

Gatewayはトラフィックを処理するインフラストラクチャのインスタンスを記述します。 これは、Serviceのようなバックエンドに対して、フィルタリング、分散、分割などのようなトラフィック処理のために使用されるネットワークエンドポイントを定義します。 例えばGatewayは、HTTPトラフィックを受け付けるために構成された、クラウドロードバランサーやクラスター内のプロキシサーバーを表す場合があります。

最小のGatewayリソースの例:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80

この例では、トラフィックを処理するインフラストラクチャのインスタンスは、80番ポートでHTTPトラフィックをリッスンするようにプログラムされています。 addressフィールドが指定されていないので、アドレスまたはホスト名はコントローラーの実装によってGatewayに割り当てられます。 このアドレスは、ルートで定義されたバックエンドのネットワークエンドポイントのトラフィックを処理するためのネットワークエンドポイントとして使用されます。

このAPIの種類の完全な定義については、Gatewayのリファレンスを参照してください。

HTTPRoute

HTTPRouteの種類は、Gatewayリスナーからバックエンドのネットワークエンドポイントに対するHTTPリクエストのルーティングの振る舞いを指定します。 Serviceバックエンドに対して、実装はバックエンドのネットワークエンドポイントをService IPまたはServiceの背後のエンドポイントとして表すことができます。 基盤となるGatewayの実装に適用される設定はHTTPRouteによって表されます。 例えば、新しいHTTPRouteを定義することにより、クラウドロードバランサーやクラスター内のプロキシサーバーの追加のトラフィックルートを構成する場合があります。

最小のHTTPRouteの例:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-httproute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /login
    backendRefs:
    - name: example-svc
      port: 8080

この例では、Host:ヘッダーにwww.example.comが設定され、リクエストパスに/loginが指定されたHTTPトラフィックが、example-gatewayという名前のGatewayから、8080番ポート上のexample-svcという名前のServiceにルーティングされます。

このAPIの種類の完全な定義については、HTTPRouteのリファレンスを参照してください。

リクエストフロー

以下は、GatewayとHTTPRouteを使用してHTTPトラフィックをServiceにルーティングする簡単な例です:

GatewayとHTTPRouteを使用してServiceにHTTPトラフィックをルーティングする例の図

この例では、リバースプロキシとして実装されたGatewayに対するリクエストフローは次のようになります:

  1. クライアントはURL http://www.example.comに対するHTTPリクエストの準備を開始します。
  2. クライアントのDNSリゾルバは宛先の名前をクエリし、Gatewayに関連づけられた1つ以上のIPアドレスとのマッピングを学習します。
  3. クライアントはGatewayのIPアドレスにリクエストを送信します。リバースプロキシはHTTPリクエストを受信し、Host:ヘッダーを使用してGatewayとそれに関連づけられたHTTPRouteから導かれた構成にマッチさせます。
  4. オプションで、リバースプロキシはHTTPRouteのマッチングルールに基づいて、リクエストヘッダーもしくはパスのマッチングを実行することができます。
  5. オプションで、リバースプロキシはリクエストを変更することができます。例えば、HTTPRouteのフィルタールールに従ってヘッダーを追加または削除します。
  6. 最後に、リバースプロキシはリクエストを1つ以上のバックエンドにフォワードします。

適合性

Gateway APIは幅広い機能をカバーし、広く実装されています。 この組み合わせは、APIがどこで使われても一貫した体験を提供することを保証するために、明確な適合性の定義とテストを必要とします。

リリースチャンネル、サポートレベル、そして適合テストの実行などの詳細を理解するためには、適合性のドキュメントを参照してください。

Ingressからの移行

Gateway APIはIngress APIの後継です。 しかし、Ingressは含まれていません。 このため、既存のIngressリソースからGateway APIリソースへの変換を1度だけ行う必要があります。

IngressリソースからGateway APIリソースへの移行の詳細に関するガイドは、Ingressの移行を参照してください。

次の項目

Gateway APIリソースをKubernetesでネイティブに実装する代わりに、幅広い実装によってサポートされたカスタムリソースとして仕様が定義されています。 Gateway API CRDをインストールするか、選んだ実装のインストール手順に従ってください。 実装をインストールした後、Getting Startedガイドを使用してGateway APIをすぐに使い始めることができます。

備考:

選択した実装のドキュメントを必ず確認し、注意点を理解するようにしてください。

すべてのGateway API種別の追加の詳細についてはAPI仕様を参照してください。

6.6 - EndpointSlice

EndpointSlice APIは、KubernetesがServiceを多数のバックエンドに対応できるようにスケールさせるための仕組みであり、クラスターが正常なバックエンドの一覧を効率的に更新できるようにするものです。
FEATURE STATE: Kubernetes v1.21 (GA)
EndpointSlicesはバックエンドのエンドポイントのIPアドレスを追跡します。 EndpointSlicesは通常Serviceに関連付けられており、バックエンドのエンドポイントは一般的にはPodを表します。

EndpointSlice API

Kubernetesでは、EndpointSliceにはネットワークエンドポイントの集合への参照が含まれます。 コントロールプレーンは、セレクターが指定されているKubernetes ServiceのEndpointSliceを自動的に作成します。 これらのEndpointSliceには、Serviceセレクターに一致するすべてのPodへの参照が含まれています。 EndpointSliceは、IPファミリー、プロトコル、ポート番号、およびService名の一意の組み合わせによってネットワークエンドポイントをグループ化します。 EndpointSliceオブジェクトの名前は有効なDNSサブドメイン名である必要があります。

一例として、以下にexampleというKubernetes Serviceによって所有されるサンプルのEndpointSliceオブジェクトを示します。

apiVersion: discovery.k8s.io/v1
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
    nodeName: node-1
    zone: us-west2-a

既定では、コントロールプレーンはEndpointSliceを作成・管理し、それぞれのエンドポイント数が100以下になるようにします。 --max-endpoints-per-slicekube-controller-managerフラグを設定することで、最大1000個まで設定可能です。

EndpointSliceは内部トラフィックのルーティング方法に関して、kube-proxyに対する信頼できる唯一の情報源(source of truth)として機能します。

アドレスの種類

EndpointSliceは次の2種類のアドレスをサポートします。

  • IPv4
  • IPv6

EndpointSliceオブジェクトは、特定のIPアドレス種別を表します。 IPv4とIPv6の両方が利用可能なServiceがある場合は、少なくとも2つのEndpointSliceオブジェクト(IPv4用とIPv6用)が存在します。

状態

EndpointSlice APIはコンシューマーの役に立つエンドポイントの状態を保持しています。 状態にはservingterminatingreadyの3種類があります。

Serving

FEATURE STATE: Kubernetes v1.26 (GA)

serving状態は、そのエンドポイントが現在レスポンスを返していることを示し、Serviceトラフィックのターゲットとして使用されるべきであることを表します。 Podをバックに持つエンドポイントでは、PodのReady状態に対応します。

Terminating

FEATURE STATE: Kubernetes v1.26 (GA)

terminating状態は、そのエンドポイントが終了処理中であることを示します。 Podをバックに持つエンドポイントでは、この状態はPodが最初に削除された時点に設定されます(つまりdeletionタイムスタンプを受け取った時点であり、通常はPodのコンテナが終了する前)。

Serviceプロキシは通常、terminating状態のエンドポイントを無視しますが、利用可能なエンドポイントがすべてterminating状態である場合は、servingterminatingの両方が設定されているエンドポイントへトラフィックをルーティングすることがあります(これは、基盤となるPodのローリングアップデート中にServiceのトラフィックが失われないようにするためです)。

Ready

ready状態は、実質的には"servingかつterminatingではない"ことを確認するためのショートカットです(ただし、spec.publishNotReadyAddressestrueに設定されているServiceでは、常にtrueになります)。

トポロジー情報

EndpointSliceに属する各エンドポイントは、関連するトポロジーの情報を持つことができます。 トポロジー情報には、エンドポイントの配置場所、および対応するノードやゾーンに関する情報が含まれます。 これらは、EndpointSliceの各エンドポイントに対する以下のフィールドで利用できます。

  • nodeName - このエンドポイントが存在するノードの名前。
  • zone - このエンドポイントが存在するゾーン。

管理

ほとんどの場合、コントロールプレーン(具体的には、EndpointSliceコントローラー)が、EndpointSliceオブジェクトを作成および管理します。 EndpointSliceには、サービスメッシュの実装など、他のさまざまなユースケースがあり、他のエンティティまたはコントローラーがEndpointSliceの追加セットを管理する可能性があります。

複数のエンティティが互いに干渉することなくEndpointSliceを管理できるようにするために、KubernetesはEndpointSliceを管理するエンティティを示すendpointslice.kubernetes.io/managed-byというラベルを定義します。 EndpointSliceコントローラーは、自身が管理するすべてのEndpointSliceに対して、このラベルの値としてendpointslice-controller.k8s.ioを設定します。 EndpointSliceを管理するその他のエンティティも同様に、このラベルに一意な値を設定する必要があります。

所有権

ほとんどのユースケースでは、EndpointSliceはEndpointSliceオブジェクトがエンドポイントを追跡するServiceによって所有されます。 これは、各EndpointSlice上のownerリファレンスとkubernetes.io/service-nameラベルによって示されます。 これにより、Serviceに属するすべてのEndpointSliceを簡単に検索できるようになっています。

EndpointSliceの分散

それぞれのEndpointSliceにはポートの集合があり、リソース内のすべてのエンドポイントに適用されます。 Serviceが名前付きポートを使用した場合、Podは同じ名前のポートに対して、結果的に異なるターゲットポート番号を持つことがあり、そのため異なるEndpointSliceが必要になる場合があります。

コントロールプレーンはEndpointSliceをできる限り埋めようとしますが、積極的にリバランスを行うことはありません。 コントローラーのロジックは極めて単純で、以下のようになっています。

  1. 既存のEndpointSliceをイテレートし、もう必要のないエンドポイントを削除し、変更があったエンドポイントを更新する。
  2. 前のステップで変更されたEndpointSliceをイテレートし、追加する必要がある新しいエンドポイントで埋める。
  3. まだ追加するべき新しいエンドポイントが残っていた場合、これまで変更されなかったスライスに追加を試みたり、新しいスライスを作成したりする。

ここで重要なのは、3番目のステップでEndpointSliceを完全に分散させることよりも、EndpointSliceの更新を制限することを優先していることです。 たとえば、もし新しい追加するべきエンドポイントが10個あり、2つのEndpointSliceにそれぞれ5個の空きがあった場合、このアプローチでは2つの既存のEndpointSliceを埋める代わりに、新しいEndpointSliceが作られます。 言い換えれば、1つのEndpointSliceを作成する方が、複数のEndpointSliceを更新するよりも好ましいということです。

各ノード上で実行されているkube-proxyはEndpointSliceを監視しており、EndpointSliceに加えられた変更はクラスター内のすべてのノードに送信されるため、比較的コストの高い処理になります。 先ほどのアプローチは、たとえ複数のEndpointSliceが埋まらない結果になるとしても、すべてのノードへ送信しなければならない変更の数を抑制することを目的としています。

現実的には、こうしたあまり理想的ではない分散が発生することは稀です。 EndpointSliceコントローラーによって処理されるほとんどの変更は、既存のEndpointSliceに収まるほど十分小さくなるためです。 そうでなかったとしても、すぐに新しいEndpointSliceが必要になる可能性が高いです。 また、Deploymentのローリングアップデートでは、すべてのPodとそれに対応するエンドポイントが置き換えられるため、EndpointSliceも自然に再配置されます。

エンドポイントの重複

EndpointSliceの変更の性質上、エンドポイントは同時に複数のEndpointSliceで表される場合があります。 これは、さまざまなEndpointSliceオブジェクトへの変更が、さまざまな時間にKubernetesクライアントのウォッチ/キャッシュに到達する可能性があるために自然に発生します。 EndpointSliceを使用する実装では、エンドポイントを複数のスライスに表示できる必要があります。

備考:

EndpointSlice APIのクライアントは、Serviceに関連づけられた既存のすべてのEndpointSliceをイテレートして、一意のネットワークエンドポイントの完全な一覧を構築する必要があります。 異なるEndpointSliceに同じエンドポイントが重複して存在する可能性がある点に注意が必要です。

このようなエンドポイントの集約および重複排除を実行する方法のリファレンス実装は、kube-proxy内のEndpointSliceCacheのコードにあります。

EndpointSliceのミラーリング

FEATURE STATE: Kubernetes v1.33 (非推奨)

EndpointSlice APIは旧来のEndpoint APIを置き換えるものです。 kube-proxyがEndpointリソースに基づいてトラフィックをルーティングすることを前提としている古いコントローラーやユーザーワークロードとの互換性を維持するために、クラスターのコントロールプレーンは、ユーザーが作成したほとんどのEndpointリソースを対応するEndpointSliceにミラーリングします。

(ただし、この機能はEndpoint APIの他の部分と同様に非推奨です。 セレクターを持たないServiceに対してEndpointを手動で指定するユーザーは、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にミラーリングされます。

次の項目

6.7 - ネットワークポリシー

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

備考:

選択したネットワークソリューションがネットワークポリシーをサポートしていない場合には、これをクラスターのAPIサーバーにPOSTしても効果はありません。

必須フィールド: 他の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フィーチャーゲートを無効にする必要があります。

備考:

SCTPプロトコルのネットワークポリシーをサポートするCNIプラグインを使用している必要があります。

ネットワークポリシーでできないこと(少なくともまだ)

Kubernetes1.20現在、ネットワークポリシーAPIに以下の機能は存在しません。 しかし、オペレーティングシステムのコンポーネント(SELinux、OpenVSwitch、IPTablesなど)、レイヤー7の技術(Ingressコントローラー、サービスメッシュ実装)、もしくはアドミッションコントローラーを使用して回避策を実装できる場合があります。 Kubernetesのネットワークセキュリティを初めて使用する場合は、ネットワークポリシーAPIを使用して以下のユーザーストーリーを(まだ)実装できないことに注意してください。これらのユーザーストーリーの一部(全てではありません)は、ネットワークポリシーAPIの将来のリリースで活発に議論されています。

  • クラスター内トラフィックを強制的に共通ゲートウェイを通過させる(これは、サービスメッシュもしくは他のプロキシで提供するのが最適な場合があります)。
  • TLS関連のもの(これにはサービスメッシュまたはIngressコントローラーを使用します)。
  • ノードの固有のポリシー(これらにはCIDR表記を使用できますが、Kubernetesのアイデンティティでノードを指定することはできません)。
  • 名前空間またはサービスを名前で指定する(ただし、Podまたは名前空間をラベルで指定することができます。これは多くの場合で実行可能な回避策です)。
  • サードパーティによって実行される「ポリシー要求」の作成または管理
  • 全ての名前空間もしくはPodに適用されるデフォルトのポリシー(これを実現できるサードパーティのKubernetesディストリビューションとプロジェクトがいくつか存在します)。
  • 高度なポリシークエリと到達可能性ツール
  • 単一のポリシー宣言でポートの範囲を指定する機能
  • ネットワークセキュリティイベント(例えばブロックされた接続や受け入れられた接続)をログに記録する機能
  • ポリシーを明示的に拒否する機能(現在、ネットワークポリシーのモデルはデフォルトで拒否されており、許可ルールを追加する機能のみが存在します)。
  • ループバックまたは内向きのホストトラフィックを拒否する機能(Podは現在localhostのアクセスやそれらが配置されているノードからのアクセスをブロックすることはできません)。

次の項目

6.8 - ServiceとPodに対するDNS

ワークロードは、DNSを用いてクラスター内のServiceを探索できます。 本ページでは、その仕組みを解説します。

KubernetesはServiceとPodのDNSレコードを作成します。 IPアドレスの代わりに安定したDNS名を用いて、Serviceに接続できます。

Kubernetesは、DNSの設定に用いられるPodとServiceの情報を公開します。 kubeletがPodのDNSを設定することで、実行中のコンテナがIPアドレスではなくDNS名でServiceの名前解決を行えます。

クラスター内で定義されたServiceにはDNS名が割り当てられます。 デフォルトでは、クライアントであるPodのDNS検索リストには、そのPod自身の名前空間とクラスターのデフォルトドメインが含まれています。

サービスの名前空間

DNSクエリの結果は、クエリを発行したPodの名前空間によって異なる場合があります。 名前空間を指定しないDNSクエリは、Pod自身の名前空間に限定して解決されます。 他の名前空間にアクセスするには、DNSクエリ内で名前空間を指定する必要があります。

例えば、testという名前空間にPodが、prodという名前空間にdataというServiceがそれぞれ存在しているとします。

Podからdataというクエリを発行しても、DNS解決はPodの名前空間testに限定されるため、結果は返りません。

data.prodというクエリを発行すれば、Serviceの属する名前空間が指定されているため、Serviceを探索することができます。

DNSクエリはPodの/etc/resolv.confに基づいて展開される場合があります。 kubeletは、それぞれのPodにこのファイルを設定します。 例えば、dataというクエリは、data.test.svc.cluster.localに展開される場合があります。 クエリの展開にはsearchオプションの値が用いられます。 DNSクエリの詳細については、resolv.confのマニュアルをご覧ください。

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

以上のように、test という名前空間に存在するPodは、data.proddata.prod.svc.cluster.localというクエリを正常に名前解決できます。

DNSレコード

DNSレコードが作成されるオブジェクトは、以下の2つです。

  1. Service
  2. Pod

以降のセクションでは、サポートされているDNSレコードの種類および構成について説明します。 その他の構成、名前、クエリは実装に依存し、予告なく変更される可能性があります。 最新の仕様については、Kubernetes DNS-Based Service Discoveryをご覧ください。

Service

A/AAAAレコード

「通常の」(ヘッドレスでない)Serviceは、my-svc.my-namespace.svc.cluster-domain.exampleという形式のDNS A(AAAA)レコードが、ServiceのIPファミリーに応じて割り当てられます。 割り当てられたAレコードは、ServiceのClusterIPへと名前解決されます。

ClusterIPのないヘッドレスServiceにもmy-svc.my-namespace.svc.cluster-domain.exampleの形式のDNS A(AAAA)レコードが割り当てられます。 通常のServiceとは異なり、Serviceによって選択されたすべてのPodのIPアドレスに解決されます。 クライアントは、得られたIPアドレスの集合を扱うか、標準のラウンドロビン方式で集合からIPアドレスを選択します。

SRVレコード

SRVレコードは、通常のServiceもしくはヘッドレスServiceの一部である名前付きポート向けに作成されます。

  • それぞれの名前付きポートに対して、SRVレコードは_port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.exampleという形式です。
  • 通常のServiceに対しては、このSRVレコードはmy-svc.my-namespace.svc.cluster-domain.exampleという形式のドメイン名とポート番号へ名前解決します。
  • ヘッドレスServiceに対しては、このSRVレコードは複数の結果を返します。 Serviceの背後にある各Podに対し1つずつレコードが返され、それぞれのレコードはPodのポート番号とhostname.my-svc.my-namespace.svc.cluster-domain.exampleという形式のドメイン名を含んでいます。

Pod

A/AAAAレコード

DNSの仕様が実装される前のバージョンのKube-DNSでは、以下のような名前解決が行われていました。

<pod-IPv4-address>.<namespace>.pod.<cluster-domain>

例えば、IPアドレスが172.17.0.3のPodがdefaultという名前空間に存在しており、クラスターのドメイン名がcluster.localの場合、PodのDNS名は以下のとおりです。

172-17-0-3.default.pod.cluster.local

CoreDNSなどの一部のクラスター実装では、以下のようなAレコードも提供されます。

<pod-ipv4-address>.<service-name>.<my-namespace>.svc.<cluster-domain.example>

例えば、IPアドレスが172.17.0.3のPodがcafeという名前空間に存在しており、PodがbaristaというServiceのエンドポイントで、クラスターのドメイン名がcluster.localの場合、Podは次のようなServiceスコープのAレコードを持ちます。

172-17-0-3.barista.cafe.svc.cluster.local

Podのhostnameとsubdomainフィールド

現在、Podが作成されたとき、Pod内部から観測されるホスト名は、Podのmetadata.nameフィールドの値です。

Podのspecでは、オプションのhostnameフィールドで別のホスト名を指定できます。 hostnameを指定すると、Podの内部から観測されるホスト名として、Podの名前よりも優先されます。 例えば、spec.hostnameフィールドが"my-host"に設定されたPodのホスト名は"my-host"です。

Podのspecはまた、オプションのsubdomainフィールドで、Podの名前空間のサブグループを指定することができます。 例えば、"my-namespace"という名前空間において、spec.hostname"foo"spec.subdomain"bar"にそれぞれ設定されているPodがあるとします。 このとき、Podのホスト名は"foo"で、Pod内部から観測される完全修飾ドメイン名(FQDN)は"foo.bar.my-namespace.svc.cluster.local"です。

Podと同じ名前空間内に、Podのサブドメインと同じ名前のヘッドレスServiceが存在する場合、クラスターのDNSサーバーは、そのPodのFQDNに対するA(AAAA)レコードを返します。

例えば、以下の設定について考えてみましょう:

apiVersion: v1
kind: Service
metadata:
  name: busybox-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # 単一ポートのServiceには、nameは必須ではありません
    port: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: busybox-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: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

上記の設定のように、"busybox-subdomain"というServiceと、spec.subdomain"busybox-subdomain"を指定したPodが存在するとします。 このとき、1つ目のPodは、自身のFQDNを"busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example"と認識します。 DNSはその名前に対して、PodのIPを指し示すA(AAAA)レコードを返します。 "busybox1"と"busybox2"の両方のPodはそれぞれ自身のA(AAAA)レコードを持ちます。

EndpointSliceでは、IPアドレスとともに、任意のエンドポイントアドレスに対するDNSホスト名を指定できます。

備考:

Podにhostnameが設定されていない場合、そのPod名に対するhostnameベースのA(AAAA)レコードは作成されません。 hostnameを持たずにsubdomainのみを持つPodの場合、ヘッドレスService(busybox-subdomain.my-namespace.svc.cluster-domain.example)に対し、PodのIPアドレスを指すA(AAAA)レコードのみが作成されます。 なお、ServiceにpublishNotReadyAddresses=Trueが設定されている場合を除き、PodがDNSレコードに含まれるためには、Podの状態がReadyである必要があります。

PodのsetHostnameAsFQDNフィールド

FEATURE STATE: Kubernetes v1.22 (GA)

PodがFQDNを持つように構成されている場合、そのホスト名は短縮されたホスト名です。 例えば、FQDNがbusybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.exampleのPodがあるとします。 この場合、デフォルトではそのPod内でhostnameコマンドを実行するとbusybox-1が返され、hostname --fqdnコマンドを実行するとFQDNが返されます。

PodのspecでsetHostnameAsFQDN: trueを設定した場合、kubeletはPodのFQDNを、そのPodの名前空間におけるホスト名として書き込みます。 この場合、hostnamehostname --fqdnの両方がPodのFQDNを返します。

備考:

Linuxでは、カーネルのホスト名のフィールド(struct utsnamenodenameフィールド)は64文字に制限されています。

Podがこの機能を有効にしていて、そのFQDNが64文字より長い場合、Podは起動に失敗します。 PodはPendingステータス(kubectlではContainerCreatingと表示)のままになり、「Failed to construct FQDN from Pod hostname and cluster domain, FQDN long-FQDN is too long (64 characters is the max, 70 characters requested)」といったエラーイベントが生成されます。 この場合のユーザー体験を向上させる1つの方法は、admission webhook controllerを作成して、ユーザーがDeploymentなどのトップレベルのオブジェクトを作成するときにFQDNのサイズを制御することです。

PodのDNSポリシー

DNSポリシーはPodごとに設定できます。 現在のKubernetesでは次のようなPod固有のDNSポリシーをサポートしています。 これらのポリシーはPodのspecのdnsPolicyフィールドで指定されます。

  • "Default": Podが実行されているノードから名前解決の設定を継承します。 詳細に関しては、関連する議論を参照してください。

  • "ClusterFirst": "www.kubernetes.io"のようなクラスターのドメインのサフィックスに一致しないDNSクエリは、DNSサーバーによって上流のネームサーバーに転送されます。 クラスター管理者が追加のスタブドメインと上流のDNSサーバーを設定している場合があります。 このような場合におけるDNSクエリ処理の詳細に関しては、関連する議論を参照してください。

  • "ClusterFirstWithHostNet": hostNetworkによって稼働しているPodに対しては、明示的にDNSポリシーを"ClusterFirstWithHostNet"に設定してください。 そうしない場合、hostNetworkによって稼働し、かつ"ClusterFirst"が設定されているPodは、"Default"ポリシーの挙動にフォールバックします。

    備考:

    本ポリシーはWindowsではサポートされていません。 詳細はWindowsノードにおけるDNSの名前解決をご確認ください。
  • "None": Kubernetes環境からDNS設定を無視することができます。 全てのDNS設定は、PodのspecのdnsConfigフィールドで指定する必要があります。 詳細は、以下のPodのDNS設定をご覧ください。

備考:

"Default"は、デフォルトのDNSポリシーではありません。 dnsPolicyが明示的に指定されていない場合、"ClusterFirst"が使用されます。

下記の例では、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設定

FEATURE STATE: Kubernetes v1.14 (GA)

PodのDNS設定では、Podに対するDNS設定をより細かく制御できます。

dnsConfigフィールドは任意で指定でき、どのdnsPolicyの設定でも併用できます。 ただし、PodのdnsPolicyが"None"の場合、dnsConfigフィールドの設定は必須です。

以下は、dnsConfigフィールドで指定可能なプロパティです。

  • nameservers: PodのDNSサーバーとして使用されるIPアドレスのリストです。 最大で3つのIPアドレスを指定できます。 PodのdnsPolicyが"None"の場合、少なくとも1つのIPアドレスを指定する必要があります。 それ以外の場合は、このプロパティは任意です。 ここで指定したサーバーは、指定されたDNSポリシーから生成されるベースのネームサーバーと結合され、重複するアドレスは削除されます。
  • searches: Pod内のホスト名の名前解決のためのDNS検索ドメインのリストです。 このプロパティは任意です。 このプロパティを指定した場合、このリストは、選択されたDNSポリシーから生成されるベースの検索ドメイン名にマージされます。 重複するドメイン名は削除されます。 最大で32個の検索ドメインを指定できます。
  • 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:
      - 192.0.2.1 # このIPアドレスは例です
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

上記のPodが作成されたとき、testコンテナの/etc/resolv.confファイルには以下の内容が設定されます。

nameserver 192.0.2.1
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

IPv6の設定では、検索パスとネームサーバーは次のように設定されます。

kubectl exec -it dns-example -- cat /etc/resolv.conf

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

nameserver 2001:db8:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

DNS検索ドメインリストの制限

FEATURE STATE: Kubernetes 1.28 (GA)

Kubernetes自体は、DNS検索リストの要素数が32を超えたり、DNS検索ドメイン名の文字数の合計が2048を超えたりしない限り、DNS設定に制限を設けません。 この制限は、ノードのリゾルバー設定ファイル、PodのDNS設定、およびそれらがマージされたDNS設定に適用されます。

備考:

古いバージョンの一部のコンテナランタイムでは、DNS検索リストの要素数に制限がある場合があります。 コンテナランタイムによっては、DNS検索ドメインの要素数が多い場合、Podの状態がPendingのままとなることがあります。

この問題は、containerd v1.5.5以前、およびCRI-O v1.21以前で発生することが確認されています。

WindowsノードにおけるDNSの名前解決

  • Windowsノード上で実行されるPodでは、DNSポリシーのClusterFirstWithHostNetはサポートされていません。 Windowsでは、.を含む名前をFQDNとして扱い、FQDN解決はスキップされます。
  • Windowsにおいては、複数のDNSリゾルバーを利用できます。 それぞれのリゾルバーの挙動がわずかに異なるため、Resolve-DNSName PowerShellコマンドレットを使用することが推奨されます。
  • Linuxには完全修飾名としての名前解決が失敗した後に使用されるDNSサフィックスリストがあります。 一方で、WindowsにはDNSサフィックスを1つしか指定できず、それはPodの名前空間に対応するDNSサフィックスです(例: mydns.svc.cluster.local)。 Windowsでは、この単一のサフィックスを用いてFQDNやService、ネットワーク名の名前解決を行えます。 例えば、defaultという名前空間で起動されたPodは、DNSサフィックスとしてdefault.svc.cluster.localが設定されます。 このPodでは、kubernetes.default.svc.cluster.localkubernetesは名前解決できますが、部分的に修飾された名前(kubernetes.defaultkubernetes.default.svc)は名前解決できません。

次の項目

DNS設定の管理方法に関しては、DNS Serviceの設定を確認してください。

6.9 - 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"

備考:

IPv4 CIDRの例: 10.244.0.0/16 (自分のクラスターのアドレス範囲を指定してください)

IPv6 CIDRの例: fdXY:IJKL:MNOP:15::/64 (これはフォーマットを示すための例であり、有効なアドレスではありません。詳しくはRFC 4193を参照してください)

Service

クラスターでIPv4/IPv6デュアルスタックのネットワークを有効にした場合、IPv4またはIPv6のいずれかのアドレスを持つServiceを作成できます。Serviceのcluster IPのアドレスファミリーは、Service上に.spec.ipFamilyフィールドを設定することで選択できます。このフィールドを設定できるのは、新しいServiceの作成時のみです。.spec.ipFamilyフィールドの指定はオプションであり、ServiceIngressでIPv4とIPv6を有効にする予定がある場合にのみ使用するべきです。このフィールドの設定は、外向きのトラフィックに対する要件には含まれません。

備考:

クラスターのデフォルトのアドレスファミリーは、kube-controller-managerに--service-cluster-ip-rangeフラグで設定した、最初のservice cluster IPの範囲のアドレスファミリーです。

.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)

次の項目

6.10 - トポロジーを意識したルーティング

Topology Aware Routingは、ネットワークトラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。クラスター内のPod間で同じゾーンのトラフィックを優先することで、信頼性、パフォーマンス(ネットワークの待ち時間やスループット)の向上、またはコストの削減に役立ちます。
FEATURE STATE: Kubernetes v1.23 (Beta)

備考:

Kubernetes 1.27より前には、この機能は、Topology Aware Hintとして知られていました。

Topology Aware Routingは、トラフィックを発信元のゾーンに維持するようにルーティング動作を調整します。場合によっては、コストを削減したり、ネットワークパフォーマンスを向上させたりすることができます。

動機

Kubernetesクラスターは、マルチゾーン環境で展開されることが多くなっています。 Topology Aware Routingは、トラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。EndpointSliceコントローラーはServiceのendpointを計算する際に、各endpointのトポロジー(リージョンとゾーン)を考慮し、ゾーンに割り当てるためのヒントフィールドに値を入力します。kube-proxyのようなクラスターコンポーネントは、次にこれらのヒントを消費し、それらを使用してトラフィックがルーティングされる方法に影響を与えることが可能です(トポロジー的に近いendpointを優先します)。

Topology Aware Routingを有効にする

備考:

Kubernetes 1.27より前には、この動作はservice.kubernetes.io/topology-aware-hintsアノテーションを使用して制御されていました。

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リリースに含まれています。これは限定的な実装であり、関連する妥当と思われる状況をまだカバーしていない可能性があります。

次の項目

6.11 - ServiceのClusterIPの割り当て

Kubernetesでは、ServiceはPodの集合上で実行しているアプリケーションを抽象的に公開する方法です。Serviceはクラスター内で仮想IPアドレス(type: ClusterIPのServiceを使用)を持つことができます。クライアントはその仮想IPアドレスを使用してServiceに接続することができます。そしてKubernetesは、そのServiceへのトラフィックを異なる背後のPod間で負荷分散します。

どのようにServiceのClusterIPが割り当てられるのか?

KubernetesがServiceに仮想IPアドレスを割り当てる必要がある場合、2つの方法の内どちらかの方法で行われます:

動的割り当て
クラスターのコントロールプレーンは自動的にtype: ClusterIPのServiceのために設定されたIP範囲の中から未割り当てのIPアドレスを選びます。
静的割り当て
Serviceのために設定されたIP範囲の中から自身でIPアドレスを選びます。

クラスター全体を通して、ServiceのClusterIPはユニークでなければいけません。割り当て済みのClusterIPを使用してServiceを作成しようとするとエラーが返ってきます。

なぜServiceのClusterIPを予約する必要があるのか?

時には、クラスター内の他のコンポーネントやユーザーが利用できるように、Serviceをよく知られたIPアドレスで実行したい場合があります。

その最たる例がクラスターのDNS Serviceです。慣習として、一部のKubernetesインストーラーはServiceのIP範囲の10番目のIPアドレスをDNS Serviceに割り当てます。ServiceのIP範囲を10.96.0.0/16とするクラスターを構成し、DNS ServiceのIPを10.96.0.10にするとします。この場合、下記のようなServiceを作成する必要があります。

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: CoreDNS
  name: kube-dns
  namespace: kube-system
spec:
  clusterIP: 10.96.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  selector:
    k8s-app: kube-dns
  type: ClusterIP

しかし、前述したように10.96.0.10のIPアドレスは予約されていません。他のServiceが動的割り当てよりも前に、または同時に作成された場合、このIPアドレスがそのServiceに割り当てられる可能性があります。その場合、競合エラーで失敗しDNS Serviceを作成することができません。

どのようにServiceのClusterIPの競合を回避するのか?

Kubernetesで実装されているServiceへのClusterIPの割り当て戦略は、衝突リスクを軽減します。

ClusterIPの範囲は、min(max(16, cidrSize / 16), 256)という式に基づいて分割されます。最小で16、最大でも256で、その範囲内で段階的に変化する ように表されます。

動的IP割り当てはデフォルトで上位の帯域を使用し、それが使い切られると下位の範囲を使用します。これにより、ユーザーは下位の帯域を使用して静的な割り当てを行うことができ、衝突のリスクを抑えることができます。

例1

この例ではServiceのIPアドレスとして、10.96.0.0/24(CIDR表記法)のIPアドレスの範囲を使用します。

範囲の大きさ: 28 - 2 = 254 帯域のオフセット(開始位置): min(max(16, 256/16), 256) = min(16, 256) = 16 静的割り当ての帯域の開始: 10.96.0.1 静的割り当ての帯域の終了: 10.96.0.16 範囲の終了: 10.96.0.254

pie showData
    title 10.96.0.0/24
    "静的割り当て" : 16
    "動的割り当て" : 238

例2

この例では、ServiceのIPアドレスとして、10.96.0.0/20(CIDR表記法)のIPアドレスの範囲を使用します。

範囲の大きさ: 212 - 2 = 4094 帯域のオフセット(開始位置): min(max(16, 4096/16), 256) = min(256, 256) = 256 静的割り当ての帯域の開始: 10.96.0.1 静的割り当ての帯域の終了: 10.96.1.0 範囲の終了: 10.96.15.254

pie showData
    title 10.96.0.0/20
    "静的割り当て" : 256
    "動的割り当て" : 3838

例3

この例ではServiceのIPアドレスとして、10.96.0.0/16(CIDR表記法)のIPアドレスの範囲を使用します。

範囲の大きさ: 216 - 2 = 65534 帯域のオフセット(開始位置): min(max(16, 65536/16), 256) = min(4096, 256) = 256 静的割り当ての帯域の開始: 10.96.0.1 静的割り当ての帯域の終了: 10.96.1.0 範囲の終了: 10.96.255.254

pie showData
    title 10.96.0.0/16
    "静的割り当て" : 256
    "動的割り当て" : 65278

次の項目

6.12 - サービス内部トラフィックポリシー

FEATURE STATE: Kubernetes v1.21 (Alpha)

サービス内部トラフィックポリシーを使用すると、内部トラフィック制限により、トラフィックが発信されたノード内のエンドポイントにのみ内部トラフィックをルーティングできます。 ここでの「内部」トラフィックとは、現在のクラスターのPodから発信されたトラフィックを指します。これは、コストを削減し、パフォーマンスを向上させるのに役立ちます。

ServiceInternalTrafficPolicyの使用

ServiceInternalTrafficPolicy フィーチャーゲートを有効にすると、.spec.internalTrafficPolicyLocalに設定して、Service内部のみのトラフィックポリシーを有効にすることができます。 これにより、kube-proxyは、クラスター内部トラフィックにノードローカルエンドポイントのみを使用するようになります。

備考:

特定のServiceのエンドポイントがないノード上のPodの場合、Serviceに他のノードのエンドポイントがある場合でも、Serviceは(このノード上のポッドの)エンドポイントがゼロであるかのように動作します。

次の例は、.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で両方の機能を使用することができます。

次の項目

7 - ストレージ

7.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間で共有できることを意味します。

備考:

使用する前に、aws ec2 create-volumeまたはAWSAPIを使用してEBSボリュームを作成する必要があります。

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ボリュームは複数の書き込み元によって同時にマウントすることができます。

備考:

事前に共有をエクスポートした状態で、自分のCephサーバーを起動しておく必要があります。

詳細についてはCephFSの例を参照してください。

cinder

備考:

KubernetesはOpenStackクラウドプロバイダーで構成する必要があります。

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.conf

log-configConfigMapはボリュームとしてマウントされ、そのlog_levelエントリに格納されているすべてのコンテンツは、パス/etc/config/log_level.confのPodにマウントされます。 このパスはボリュームのmountPathlog_levelをキーとするpathから派生することに注意してください。

備考:

  • 使用する前にConfigMapを作成する必要があります。

  • subPathボリュームマウントとしてConfigMapを使用するコンテナはConfigMapの更新を受信しません。

  • テキストデータはUTF-8文字エンコードを使用してファイルとして公開されます。その他の文字エンコードにはbinaryDataを使用します。

downwardAPI

downwardAPIボリュームは、アプリケーションへのdownward APIデータを利用できるようになります。ディレクトリをマウントし、要求されたデータをプレーンテキストファイルに書き込みます。

備考:

subPathボリュームマウントとしてdownward APIを使用するコンテナは、downward APIの更新を受け取りません。

詳細についてはdownward API exampleを参照してください。

emptyDir

emptyDirボリュームはPodがノードに割り当てられたときに最初に作成され、そのPodがそのノードで実行されている限り存在します。 名前が示すようにemptyDirボリュームは最初は空です。 Pod内のすべてのコンテナはemptyDirボリューム内の同じファイルを読み書きできますが、そのボリュームは各コンテナで同じパスまたは異なるパスにマウントされることがあります。 何らかの理由でPodがノードから削除されると、emptyDir内のデータは永久に削除されます。

備考:

コンテナがクラッシュしても、ノードからPodが削除されることはありませんemptyDirボリューム内のデータは、コンテナのクラッシュしても安全です。

emptyDirのいくつかの用途は次の通りです。

  • ディスクベースのマージソートなどのスクラッチスペース
  • クラッシュからの回復のための長い計算のチェックポイント
  • Webサーバーコンテナがデータを提供している間にコンテンツマネージャコンテナがフェッチするファイルを保持する

環境に応じて、emptyDirボリュームは、ディスクやSSD、ネットワークストレージなど、ノードをバックアップするあらゆる媒体に保存されます。 ただし、emptyDir.mediumフィールドを"Memory"に設定すると、Kubernetesは代わりにtmpfs(RAMベースのファイルシステム)をマウントします。 tmpfsは非常に高速ですが、ディスクと違ってノードのリブート時にクリアされ、書き込んだファイルはコンテナのメモリー制限にカウントされることに注意してください。

備考:

SizeMemoryBackedVolumesフィーチャーゲートが有効な場合、メモリーバックアップボリュームにサイズを指定することができます。 サイズが指定されていない場合、メモリーでバックアップされたボリュームは、Linuxホストのメモリーの50%のサイズになります。

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がマルチパス接続からのものであると想定します。

備考:

Kubernetesホストがアクセスできるように、事前にこれらのLUN(ボリューム)をターゲットWWNに割り当ててマスクするようにFCSANゾーニングを構成する必要があります。

flocker (非推奨)

Flockerはオープンソースのクラスター化されたコンテナデータボリュームマネージャーです。 Flockerは、さまざまなストレージバックエンドに支えられたデータボリュームの管理とオーケストレーションを提供します。

flockerボリュームを使用すると、FlockerデータセットをPodにマウントできます。 もしデータセットがまだFlockerに存在しない場合は、まずFlocker CLIかFlocker APIを使ってデータセットを作成する必要があります。 データセットがすでに存在する場合は、FlockerによってPodがスケジュールされているノードに再アタッチされます。 これは、必要に応じてPod間でデータを共有できることを意味します。

備考:

使用する前に、独自のFlockerインストールを実行する必要があります。

詳細についてはFlocker exampleを参照してください。

gcePersistentDisk

gcePersistentDiskボリュームは、Google Compute Engine (GCE)の永続ディスク(PD)をPodにマウントします。 Podを取り外すと消去されるemptyDirとは異なり、PDの内容は保持されボリュームは単にアンマウントされるだけです。これはPDにあらかじめデータを入れておくことができ、そのデータをPod間で共有できることを意味します。

備考:

gcloudを使用する前に、またはGCE APIまたはUIを使用してPDを作成する必要があります。

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リポジトリを使用してコンテナをプロビジョニングするには、Gitを使用してリポジトリのクローンを作成するInitContainerにEmptyDirをマウントしてから、PodのコンテナにEmptyDirをマウントします。

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を使用するためには、事前にGlusterFSのインストールを実行しておく必要があります。

詳細についてはGlusterFSの例を参照してください。

hostPath

警告:

HostPathボリュームには多くのセキュリティリスクがあり、可能な場合はHostPathの使用を避けることがベストプラクティスです。HostPathボリュームを使用する必要がある場合は、必要なファイルまたはディレクトリのみにスコープを設定し、読み取り専用としてマウントする必要があります。

AdmissionPolicyによって特定のディレクトリへのHostPathアクセスを制限する場合、ポリシーを有効にするためにvolumeMountsreadOnlyマウントを使用するように要求されなければなりません。

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 指定されたパスにファイルが存在する必要があります。
Socket UNIXソケットは、指定されたパスに存在する必要があります。
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

注意:

FileOrCreateモードでは、ファイルの親ディレクトリは作成されません。マウントされたファイルの親ディレクトリが存在しない場合、Podは起動に失敗します。 このモードが確実に機能するようにするには、FileOrCreate構成に示すように、ディレクトリとファイルを別々にマウントしてみてください。

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サーバーを起動する必要があります。

iSCSIの特徴として、複数のコンシューマーから同時に読み取り専用としてマウントできることが挙げられます。 つまり、ボリュームにあらかじめデータセットを入れておき、必要な数のPodから並行してデータを提供することができます。 残念ながら、iSCSIボリュームは1つのコンシューマによってのみ読み書きモードでマウントすることができます。 同時に書き込みを行うことはできません。

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が持つ可能性のある他のノード制約も含めて評価されるようになります。

ローカルボリュームのライフサイクルの管理を改善するために、外部の静的プロビジョナーを個別に実行できます。 このプロビジョナーはまだ動的プロビジョニングをサポートしていないことに注意してください。 外部ローカルプロビジョナーの実行方法の例については、ローカルボリュームプロビジョナーユーザーガイドを参照してください。

備考:

ボリュームのライフサイクルを管理するために外部の静的プロビジョナーが使用されていない場合、ローカルのPersistentVolumeは、ユーザーによる手動のクリーンアップと削除を必要とします。

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>"

備考:

Podで使用する前に、pxvolという名前の既存のPortworxVolumeがあることを確認してください。

投影

投影ボリュームは、複数の既存のボリュームソースを同じディレクトリにマッピングします。 詳細については投影ボリュームを参照してください。

quobyte(非推奨)

quobyteボリュームは、既存のQuobyteボリュームをPodにマウントすることができます。

備考:

使用する前にQuobyteをセットアップして、ボリュームを作成した状態で動作させる必要があります。

CSIは、Kubernetes内部でQuobyteボリュームを使用するための推奨プラグインです。 QuobyteのGitHubプロジェクトには、CSIを使用してQuobyteをデプロイするための手順と例があります

rbd

rbdボリュームはRados Block Device(RBD)ボリュームをPodにマウントすることを可能にします。 Podを取り外すと消去されるemptyDirとは異なり、rbdボリュームの内容は保存され、ボリュームはアンマウントされます。つまり、RBDボリュームにはあらかじめデータを入れておくことができ、そのデータをPod間で共有することができるのです。

備考:

RBDを使用する前に、Cephのインストールが実行されている必要があります。

RBDの特徴として、複数のコンシューマーから同時に読み取り専用としてマウントできることが挙げられます。 つまり、ボリュームにあらかじめデータセットを入れておき、必要な数のPodから並行して提供することができるのです。 残念ながら、RBDボリュームは1つのコンシューマーによってのみ読み書きモードでマウントすることができます。 同時に書き込みを行うことはできません。

詳細についてはRBDの例を参照してください。

RBD CSIの移行

FEATURE STATE: Kubernetes v1.23 (Alpha)

RBDCSIMigration機能を有効にすると、既存のツリー内プラグインからrbd.csi.ceph.comCSIドライバーにすべてのプラグイン操作がリダイレクトされます。 この機能を使用するには、クラスターにCeph CSIドライバーをインストールし、CSIMigrationおよびcsiMigrationRBDフィーチャーゲートを有効にしておく必要があります。

備考:

ストレージを管理するKubernetesクラスターオペレーターとして、RBD CSIドライバーへの移行を試みる前に完了する必要のある前提条件は次のとおりです。

  • Ceph CSIドライバー(rbd.csi.ceph.com)v3.5.0以降をKubernetesクラスターにインストールする必要があります。
  • CSIドライバーの動作に必要なパラメーターとしてclusterIDフィールドがありますが、ツリー内StorageClassにはmonitorsフィールドがあるため、Kubernetesストレージ管理者はCSI config mapでモニターハッシュ(例:#echo -n '<monitors_string>' | md5sum)に基づいたclusterIDを作成し、モニターをこのclusterID設定の下に保持しなければなりません。
  • また、ツリー内StorageclassのadminIdの値がadminと異なる場合、ツリー内Storageclassに記載されているadminSecretNameadminIdパラメーター値のbase64値をパッチしなければなりませんが、それ以外はスキップすることが可能です。

secret

secretボリュームは、パスワードなどの機密情報をPodに渡すために使用します。 Kubernetes APIにsecretを格納し、Kubernetesに直接結合することなくPodが使用するファイルとしてマウントすることができます。 secretボリュームはtmpfs(RAM-backed filesystem)によってバックアップされるため、不揮発性ストレージに書き込まれることはありません。

備考:

使用する前にKubernetes APIでSecretを作成する必要があります。

備考:

SubPathボリュームマウントとしてSecretを使用しているコンテナは、Secretの更新を受け取りません。

詳細についてはSecretの設定を参照してください。

storageOS(非推奨)

storageosボリュームを使用すると、既存のStorageOSボリュームをPodにマウントできます。

StorageOSは、Kubernetes環境内でコンテナとして実行され、Kubernetesクラスター内の任意のノードからローカルストレージまたは接続されたストレージにアクセスできるようにします。 データを複製してノードの障害から保護することができます。シンプロビジョニングと圧縮により使用率を向上させ、コストを削減できます。

根本的にStorageOSは、コンテナにブロックストレージを提供しファイルシステムからアクセスできるようにします。

StorageOS Containerは64ビットLinuxを必要とし、追加の依存関係はありません。 無償の開発者ライセンスが利用可能です。

注意:

StorageOSボリュームにアクセスする、またはプールにストレージ容量を提供する各ノードでStorageOSコンテナを実行する必要があります。 インストール手順については、StorageOSドキュメントを参照してください。

次の例は、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

備考:

KubernetesvSphereクラウドプロバイダーを設定する必要があります。クラウドプロバイダーの設定については、vSphere入門ガイドを参照してください。

vsphereVolumeは、vSphereVMDKボリュームをPodにマウントするために使用されます。 ボリュームの内容は、マウント解除されたときに保持されます。VMFSとVSANの両方のデータストアをサポートします。

備考:

Podで使用する前に、次のいずれかの方法を使用してvSphereVMDKボリュームを作成する必要があります。

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以上であることが条件です。

備考:

組み込みのvsphereVolumeプラグインの次のStorageClassパラメーターは、vSphere CSIドライバーでサポートされていません。

  • diskformat
  • hostfailurestotolerate
  • forceprovisioning
  • cachereservation
  • diskstripes
  • objectspacereservation
  • iopslimit

これらのパラメーターを使用して作成された既存のボリュームはvSphere CSIドライバーに移行されますが、vSphere CSIドライバーで作成された新しいボリュームはこれらのパラメーターに従わないことに注意してください。

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 (GA)

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仕様バージョン0.2および0.3のサポートは、Kubernetes v1.13で非推奨になり、将来のリリースで削除される予定です。

備考:

CSIドライバーは、すべてのKubernetesリリース間で互換性があるとは限りません。各Kubernetesリリースでサポートされているデプロイ手順と互換性マトリックスについては、特定のCSIドライバーのドキュメントを確認してください。

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 (GA)

外部の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 (非推奨)

FlexVolumeは、ストレージドライバーとのインターフェースにexecベースのモデルを使用するツリー外プラグインインターフェースです。FlexVolumeドライバーのバイナリは、各ノード、場合によってはコントロールプレーンノードにも、あらかじめ定義されたボリュームプラグインパスにインストールする必要があります。

PodはflexVolumeツリー内ボリュームプラグインを通してFlexVolumeドライバーと対話します。

詳細についてはFlexVolumeのREADMEを参照してください。

備考:

FlexVolumeは非推奨です。ツリー外のCSIドライバーを使用することは、外部ストレージをKubernetesと統合するための推奨される方法です。

FlexVolumeドライバーのメンテナーは、CSIドライバーを実装し、FlexVolumeドライバーのユーザーをCSIに移行するのを支援する必要があります。FlexVolumeのユーザーは、同等のCSIドライバーを使用するようにワークロードを移動する必要があります。

マウントの伝播

マウントの伝播により、コンテナによってマウントされたボリュームを、同じPod内の他のコンテナ、または同じノード上の他のPodに共有できます。

ボリュームのマウント伝播は、containers[*].volumeMountsmountPropagationフィールドによって制御されます。その値は次のとおりです。

  • None - このボリュームマウントは、ホストによってこのボリュームまたはそのサブディレクトリにマウントされる後続のマウントを受け取りません。同様に、コンテナによって作成されたマウントはホストに表示されません。これがデフォルトのモードです。

    このモードはLinuxカーネルドキュメントで説明されているprivateマウント伝播と同じです。

  • HostToContainer - このボリュームマウントは、このボリュームまたはそのサブディレクトリのいずれかにマウントされる後続のすべてのマウントを受け取ります。

    つまりホストがボリュームマウント内に何かをマウントすると、コンテナはそこにマウントされていることを確認します。

    同様に同じボリュームに対してBidirectionalマウント伝搬を持つPodが何かをマウントすると、HostToContainerマウント伝搬を持つコンテナはそれを見ることができます。

    このモードはLinuxカーネルドキュメントで説明されているrslaveマウント伝播と同じです。

  • Bidirectional - このボリュームマウントは、HostToContainerマウントと同じように動作します。さらに、コンテナによって作成されたすべてのボリュームマウントは、ホストと、同じボリュームを使用するすべてのPodのすべてのコンテナに伝播されます。

    このモードの一般的な使用例は、FlexVolumeまたはCSIドライバーを備えたPod、またはhostPathボリュームを使用してホストに何かをマウントする必要があるPodです。

    このモードはLinuxカーネルドキュメントで説明されているrsharedマウント伝播と同じです。

    警告:

    Bidirectionalマウント伝搬は危険です。ホストオペレーティングシステムにダメージを与える可能性があるため、特権的なコンテナでのみ許可されています。 Linuxカーネルの動作に精通していることが強く推奨されます。 また、Pod内のコンテナによって作成されたボリュームマウントは、終了時にコンテナによって破棄(アンマウント)される必要があります。

構成

一部のデプロイメント(CoreOS、RedHat/Centos、Ubuntu)でマウント伝播が正しく機能する前に、以下に示すように、Dockerでマウント共有を正しく構成する必要があります。

Dockerのsystemdサービスファイルを編集します。以下のようにMountFlagsを設定します。

MountFlags=shared

または、MountFlags=slaveがあれば削除してください。その後、Dockerデーモンを再起動します。

sudo systemctl daemon-reload
sudo systemctl restart docker

次の項目

永続ボリュームを使用してWordPressとMySQLをデプロイする例に従ってください。

7.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)がシステムから削除されないようにすることです。

備考:

PVCを使用しているPodオブジェクトが存在する場合、PVCはPodによって実際に使用されています。

ユーザーが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再クレームポリシーは非推奨になりました。代わりに、動的プロビジョニングを使用することをおすすめします。

基盤となるボリュームプラグインでサポートされている場合、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 (GA)

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 (GA)

CSIボリュームの拡張のサポートはデフォルトで有効になっていますが、ボリューム拡張をサポートするにはボリューム拡張を利用できるCSIドライバーも必要です。詳細については、それぞれのCSIドライバーのドキュメントを参照してください。

ファイルシステムを含むボリュームのリサイズ

ファイルシステムがXFS、Ext3、またはExt4の場合にのみ、ファイルシステムを含むボリュームのサイズを変更できます。

ボリュームにファイルシステムが含まれる場合、新しいPodがPersistentVolumeClaimでReadWriteモードを使用している場合にのみ、ファイルシステムのサイズが変更されます。ファイルシステムの拡張は、Podの起動時、もしくはPodの実行時で基盤となるファイルシステムがオンラインの拡張をサポートする場合に行われます。

FlexVolumesでは、ドライバーのRequiresFSResize機能がtrueに設定されている場合、サイズを変更できます。 FlexVolumeは、Podの再起動時にサイズ変更できます。

使用中の永続ボリュームクレームのリサイズ

FEATURE STATE: Kubernetes v1.15 (Beta)

備考:

使用中のPVCの拡張は、Kubernetes 1.15以降のベータ版と、1.11以降のアルファ版として利用可能です。ExpandInUsePersistentVolume機能を有効化する必要があります。これはベータ機能のため多くのクラスターで自動的に行われます。詳細については、フィーチャーゲートのドキュメントを参照してください。

この場合、既存のPVCを使用しているPodまたはDeploymentを削除して再作成する必要はありません。使用中のPVCは、ファイルシステムが拡張されるとすぐにPodで自動的に使用可能になります。この機能は、PodまたはDeploymentで使用されていないPVCには影響しません。拡張を完了する前に、PVCを使用するPodを作成する必要があります。

他のボリュームタイプと同様、FlexVolumeボリュームは、Podによって使用されている最中でも拡張できます。

備考:

FlexVolumeのリサイズは、基盤となるドライバーがリサイズをサポートしている場合のみ可能です。

備考:

EBSの拡張は時間がかかる操作です。また変更は、ボリュームごとに6時間に1回までというクォータもあります。

ボリューム拡張時の障害からの復旧

基盤ストレージの拡張に失敗した際には、クラスターの管理者は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

備考:

クラスター内でPersistentVolumeを使用するには、ボリュームタイプに関連するヘルパープログラムが必要な場合があります。 この例では、PersistentVolumeはNFSタイプで、NFSファイルシステムのマウントをサポートするためにヘルパープログラム/sbin/mount.nfsが必要になります。

容量

通常、PVには特定のストレージ容量があります。これはPVのcapacity属性を使用して設定されます。容量によって期待される単位を理解するためには、Kubernetesのリソースモデルを参照してください。

現在、設定または要求できるのはストレージサイズのみです。将来の属性には、IOPS、スループットなどが含まれます。

ボリュームモード

FEATURE STATE: Kubernetes v1.18 (GA)

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としてマウントできますが、同時にマウントすることはできません。

ボリュームプラグイン ReadWriteOnce ReadOnlyMany ReadWriteMany
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リリースでは完全に非推奨です。

ノードアフィニティ

備考:

ほとんどのボリュームタイプはこのフィールドを設定する必要がありません。AWS EBSGCE PD、もしくはAzure Diskボリュームブロックタイプの場合自動的に設定されます。localボリュームは明示的に設定する必要があります。

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にバインドされます。

備考:

現在、selectorが空ではないPVCは、PVを動的にプロビジョニングできません。

以前は、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 (GA)

次のボリュームプラグインは、必要に応じて動的プロビジョニングを含む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

備考:

Podにrawブロックデバイスを追加する場合は、マウントパスの代わりにコンテナでデバイスパスを指定します。

ブロックボリュームのバインド

ユーザーがPersistentVolumeClaim specのvolumeModeフィールドを使用してrawブロックボリュームの要求を示す場合、バインディングルールは、このモードをspecの一部として考慮しなかった以前のリリースとわずかに異なります。表にリストされているのは、ユーザーと管理者がrawブロックデバイスを要求するために指定可能な組み合わせの表です。この表は、ボリュームがバインドされるか、組み合わせが与えられないかを示します。静的にプロビジョニングされたボリュームのボリュームバインディングマトリクスはこちらです。

PVボリュームモード PVCボリュームモード 結果
未定義 未定義 バインド
未定義 ブロック バインドなし
未定義 ファイルシステム バインド
ブロック 未定義 バインドなし
ブロック ブロック バインド
ブロック ファイルシステム バインドなし
ファイルシステム ファイルシステム バインド
ファイルシステム ブロック バインドなし
ファイルシステム 未定義 バインド

備考:

アルファリリースでは、静的にプロビジョニングされたボリュームのみがサポートされます。管理者は、rawブロックデバイスを使用する場合、これらの値を考慮するように注意する必要があります。

ボリュームのスナップショットとスナップショットからのボリュームの復元のサポート

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を必要とする設定をデプロイできない)可能性があることを示す。

    次の項目

リファレンス

7.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フィールドは、投影ボリュームのマウントポイントへの相対パスを指定します。

備考:

投影ボリュームソースをsubPathボリュームマウントとして使用しているコンテナは、それらのボリュームソースの更新を受信しません。

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のようなすべての管理者ユーザーが読み取り、書き込み、および実行アクセス権を持ち、非管理者ユーザーが読み取りおよび実行アクセス権を持つことを意味します。

備考:

一般に、コンテナにホストへのアクセスを許可することは、潜在的なセキュリティの悪用への扉を開く可能性があるため、お勧めできません。

Windows PodのSecurityContextRunAsUserを指定して作成すると、PodはContainerCreatingで永久に固まります。したがって、Windows PodでLinux専用のRunAsUserオプションを使用しないことをお勧めします。

7.4 - エフェメラルボリューム

このドキュメントでは、Kubernetesのエフェメラルボリュームについて説明します。ボリューム、特にPersistentVolumeClaimとPersistentVolumeに精通していることをお勧めします。

一部のアプリケーションでは追加のストレージが必要ですが、そのデータが再起動後も永続的に保存されるかどうかは気にしません。 たとえば、キャッシュサービスは多くの場合メモリサイズによって制限されており、使用頻度の低いデータを、全体的なパフォーマンスにほとんど影響を与えずに、メモリよりも低速なストレージに移動できます。

他のアプリケーションは、構成データや秘密鍵など、読み取り専用の入力データがファイルに存在することを想定しています。

エフェメラルボリュームは、これらのユースケース向けに設計されています。 ボリュームはPodの存続期間に従い、Podとともに作成および削除されるため、Podは、永続ボリュームが利用可能な場所に制限されることなく停止および再起動できます。

エフェメラルボリュームはPod仕様でインラインで指定されているため、アプリケーションの展開と管理が簡素化されます。

エフェメラルボリュームのタイプ

Kubernetesは、さまざまな目的のためにいくつかの異なる種類のエフェメラルボリュームをサポートしています。

emptyDirconfigMapdownwardAPIsecretローカルエフェメラルストレージとして提供されます。 これらは、各ノードのkubeletによって管理されます。

CSIエフェメラルボリュームは、サードパーティーのCSIストレージドライバーによって提供される必要があります

汎用エフェメラルボリュームは、サードパーティーのCSIストレージドライバーによって提供される可能性がありますが、動的プロビジョニングをサポートする他のストレージドライバーによって提供されることもあります。一部のCSIドライバーは、CSIエフェメラルボリューム用に特別に作成されており、動的プロビジョニングをサポートしていません。これらは汎用エフェメラルボリュームには使用できません。

サードパーティー製ドライバーを使用する利点は、Kubernetes自体がサポートしていない機能を提供できることです。たとえば、kubeletによって管理されるディスクとは異なるパフォーマンス特性を持つストレージや、異なるデータの挿入などです。

CSIエフェメラルボリューム

FEATURE STATE: Kubernetes v1.25 (GA)

備考:

CSIエフェメラルボリュームは、CSIドライバーのサブセットによってのみサポートされます。 Kubernetes CSIドライバーリストには、エフェメラルボリュームをサポートするドライバーが表示されます。

概念的には、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 (GA)

汎用エフェメラルボリュームは、プロビジョニング後に通常は空であるスクラッチデータ用の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を起動できないため、これでは競合が解決されません。

注意:

これらの競合が発生しないように、同じ名前空間内でPodとボリュームに名前を付けるときは注意してください。

セキュリティ

GenericEphemeralVolume機能を有効にすると、ユーザーは、PVCを直接作成する権限がなくても、Podを作成できる場合、間接的にPVCを作成できます。クラスター管理者はこれを認識している必要があります。これがセキュリティモデルに適合しない場合は、一般的なエフェメラルボリュームを持つPodなどのオブジェクトを拒否するadmission webhookを使用する必要があります。

通常のPVCの名前空間割り当ては引き続き適用されるため、ユーザーがこの新しいメカニズムの使用を許可されたとしても、他のポリシーを回避するために使用することはできません。

次の項目

kubeletによって管理されるエフェメラルボリューム

ローカルエフェメラルボリュームを参照してください。

CSIエフェメラルボリューム

汎用エフェメラルボリューム

7.5 - VolumeAttributesClass

FEATURE STATE: Kubernetes v1.29 (Alpha)

このページでは、Kubernetesのストレージクラスボリュームおよび永続ボリュームについてよく理解していることを前提としています。

VolumeAttributesClassは、管理者がストレージの変更可能な「クラス」を表現する方法を提供します。 異なるクラスは異なるサービス品質レベルに対応する場合があります。 Kubernetes自体は、これらのクラスが何を表すかかについては見解を持っていません。

これはアルファ機能であり、デフォルトで無効化されています。

アルファ機能であるうちにテストしたい場合は、kube-controller-managerおよびkube-apiserverでVolumeAttributesClassフィーチャーゲートを有効化する必要があります。 コマンドライン引数の--feature-gatesを使用します:

--feature-gates="...,VolumeAttributesClass=true"

VolumeAttributesClassはContainer Storage Interfaceをバックエンドとするストレージでのみ使用することができ、 関連するCSIドライバーがModifyVolume APIを実装している場合にのみ使用することができます

VolumeAttributesClass API

各VolumeAttributesClassにはdriverNameparametersが含まれており、 クラスに属する永続ボリューム(PV)を動的にプロビジョニングまたは変更する際に利用されます。

VolumeAttributesClassのオブジェクト名は重要であり、ユーザーが特定のクラスを要求する方法です。 管理者はVolumeAttributesClassのオブジェクトを最初に作成する際に、クラス名や他のパラメーターを設定します。 PersistentVolumeClaim内のVolumeAttributesClassのオブジェクト名は変更可能ですが、 既存のクラスのパラメーターは変更できません。

apiVersion: storage.k8s.io/v1alpha1
kind: VolumeAttributesClass
metadata:
  name: silver
driverName: pd.csi.storage.gke.io
parameters:
  provisioned-iops: "3000"
  provisioned-throughput: "50" 

プロビジョナー

各VolumeAttributesClassには、PVのプロビジョニングにどのボリュームプラグインを使用するかを決定するプロビジョナが備わっています。

VolumeAttributesClassに関する機能のサポートはkubernetes-csi/external-provisionerに実装されています。

kubernetes-csi/external-provisionerに限定されることはありません。 Kubernetesによって定義された仕様にそった独立したプログラムである、外部プロビジョナを実行、指定することもできます。 外部プロビジョナの作成者は、コードの保存場所、プロビジョナの配布方法、実行方法、使用するボリュームプラグインなど、あらゆる裁量を持っています。

リサイザー

各VolumeAttributesClassには、PVの変更にどのボリュームプラグインを使用するかを決定するリサイザが備わっています。 driverNameフィールドの指定は必須です。

VolumeAttributesClassに関するボリューム変更機能のサポートはkubernetes-csi/external-resizerに実装されています。

例えば、既存のPersistentVolumeClaimがsilverという名前のVolumeAttributesClassを使用しているとします:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pv-claim
spec:
  
  volumeAttributesClassName: silver
  

新しいgoldというVolumeAttributesClassがクラスターで使用可能です:

apiVersion: storage.k8s.io/v1alpha1
kind: VolumeAttributesClass
metadata:
  name: gold
driverName: pd.csi.storage.gke.io
parameters:
  iops: "4000"
  throughput: "60"

エンドユーザーは新しいgoldというVolumeAttributesClassを使ってPVCを更新、適用できます:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pv-claim
spec:
  
  volumeAttributesClassName: gold
  

パラメーター

VolumeAttributesClassにはそれらに属するボリュームを記述するパラメーターがあります。 プロビジョナまたはリサイザによって、異なるパラメーターを受け取る場合があります。 例えば、パラメーターiopsの値4000や、パラメーターthroughputはGCE Persistent Disk固有のものです。 パラメーターを省略した場合は、デフォルト値がボリュームのプロビジョニング時に使用されます。 ユーザーがパラメーターを省略して異なるPVCを適用する場合、CSIドライバーの実装に応じてデフォルトのパラメーターが使用されることがあります。 詳細については、関連するCSIドライバーのドキュメントを参照してください。

VolumeAttributesClassには最大512個のパラメーターを定義できます。 キーと値を含むパラメーターオブジェクトの合計の長さは256KiBを超過することはできません。

7.6 - ストレージクラス

このドキュメントでは、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 Plugin Internal Provisioner Config Example
AWSElasticBlockStore AWS EBS
AzureFile Azure File
AzureDisk Azure Disk
CephFS - -
Cinder OpenStack Cinder
FC - -
FlexVolume - -
GCEPersistentDisk GCE PD
Glusterfs Glusterfs
iSCSI - -
NFS - NFS
RBD Ceph RBD
VsphereVolume vSphere
PortworxVolume Portworx 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 type Required Kubernetes version
gcePersistentDisk 1.11
awsElasticBlockStore 1.11
Cinder 1.11
glusterfs 1.11
rbd 1.11
Azure File 1.11
Azure Disk 1.11
Portworx 1.11
FlexVolume 1.13
CSI 1.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をサポートします。

  • 上記のすべて
  • Local

FEATURE STATE: Kubernetes v1.17 (GA)
CSIボリュームも動的プロビジョニングと事前作成されたPVでサポートされていますが、サポートされているトポロジーキーと例を確認するには、特定のCSIドライバーのドキュメントを参照する必要があります。

備考:

WaitForFirstConsumerの使用を選択した場合は、Pod仕様でnodeNameを使用してノードアフィニティを指定しないでください。この場合にnodeNameを使用すると、スケジューラーはバイパスされ、PVCは保留状態のままになります。

代わりに、以下に示すように、この場合はホスト名にノードセレクターを使用できます。

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ドキュメントを参照してください。

備考:

zoneおよびzonesパラメーターは廃止され、allowedTopologiesに置き換えられました。

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を使用してさらに制限できます。

備考:

zoneおよびzonesパラメーターは廃止され、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クラスターにノードがあるすべてのアクティブなゾーンにわたってラウンドロビン方式で処理されます。

備考:

<div class="feature-state-notice feature-deprecated">
  <span class="feature-state-name">FEATURE STATE:</span>
  <span class="feature-state-details">
   Kubernetes v1.11 (非推奨)
  </span>
</div>

このOpenStackの内部プロビジョナーは非推奨です。OpenStackの外部クラウドプロバイダーをご利用ください。

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 (GA)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

ローカルボリュームは現在、動的プロビジョニングをサポートしていませんが、Podのスケジューリングまでボリュームバインドを遅らせるには、引き続きStorageClassを作成する必要があります。これは、WaitForFirstConsumerボリュームバインディングモードによって指定されます。

ボリュームバインディングを遅延させると、PersistentVolumeClaimに適切なPersistentVolumeを選択するときに、スケジューラーはPodのスケジューリング制約をすべて考慮することができます。

7.7 - ボリュームの動的プロビジョニング(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割り当てモードを設定することにより可能となります。

7.8 - ボリュームのスナップショット

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フィールドを使用して、スナップショットからのデータが事前に取り込まれた新しいボリュームをプロビジョニングできます。

詳細については、ボリュームのスナップショットとスナップショットからのボリュームの復元を参照してください。

7.9 - 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に応じて様々なパラメーターを使用できます。

7.10 - 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

備考:

spec.resources.requests.storageに容量の値を指定する必要があります。指定する値は、ソースボリュームの容量と同じかそれ以上である必要があります。

このyamlの作成結果は指定された複製元であるpvc-1と全く同じデータを持つclone-of-pvc-1という名前の新しいPVCです。

使い方

新しいPVCが使用可能になると、複製されたPVCは他のPVCと同じように利用されます。またこの時点で新しく作成されたPVCは独立したオブジェクトであることが期待されます。元のdataSource PVCを考慮せず個別に利用、複製、スナップショット、削除できます。これはまた複製元が新しく作成された複製にリンクされておらず、新しく作成された複製に影響を与えずに変更または削除できることを意味します。

7.11 - ストレージ容量

ストレージ容量は、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ドライバーもこの機能をサポートしている必要があります。詳細については、各ドライバーのドキュメントを参照してください。

次の項目

7.12 - ノード固有のボリューム制限

このページでは、さまざまなクラウドプロバイダーのノードに接続できるボリュームの最大数について説明します。

通常、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 (GA)

動的ボリューム制限は、次のボリュームタイプでサポートされています。

  • 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ドライバーによって報告される数になります。

7.13 - ローカルエフェメラルストレージ

ノードは、ローカルに接続された書き込み可能なデバイス、または場合によってはRAMによって提供されるローカルエフェメラルストレージを持っています。 「エフェメラル」とは、耐久性に関する長期的な保証がないことを意味します。

Podは、スクラッチスペース、キャッシュ、およびログのためにエフェメラルローカルストレージを使用します。 kubeletは、ローカルエフェメラルストレージを使用してコンテナにemptyDir ボリュームをマウントすることで、Podにスクラッチスペースを提供できます。

kubeletは、この種のストレージをノードレベルのコンテナログ、コンテナイメージ、および実行中のコンテナの書き込み可能なレイヤーの保持にも使用します。

注意:

ノードに障害が発生すると、そのエフェメラルストレージ内のデータは失われる可能性があります。 アプリケーションは、ローカルエフェメラルストレージに対してパフォーマンスSLA(例えばディスクIOPS)を期待することはできません。

備考:

エフェメラルストレージに対してリソースクォータを機能させるには、2つのことを行う必要があります:

  • 管理者がNamespaceにephemeral-storageのリソースクォータを設定する。
  • ユーザーがPod specでephemeral-storageリソースのlimitsを指定する。

ユーザーがPod specでephemeral-storageリソースのlimitsを指定しない場合、リソースクォータはephemeral-storageに対して適用されません。

Kubernetesでは、Podが消費するエフェメラルローカルストレージの量を追跡、予約、制限できます。

ローカルエフェメラルストレージの設定

Kubernetesは、ノード上のローカルエフェメラルストレージを設定する2つの方法をサポートしています:

この設定では、すべての種類のエフェメラルローカルデータ(emptyDirボリューム、書き込み可能なレイヤー、コンテナイメージ、ログ)を1つのファイルシステムに配置します。 kubeletを設定する最も効果的な方法は、このファイルシステムをKubernetes(kubelet)のデータ専用にすることです。

kubeletはノードレベルのコンテナログもファイルに書き込み、エフェメラルローカルストレージと同様に扱います。

kubeletは、設定されたログディレクトリ(デフォルトでは/var/log)内のファイルにログを書き込みます。 また、その他のローカルに保存されるデータのためのベースディレクトリ(デフォルトでは/var/lib/kubelet)を持っています。

通常、/var/lib/kubelet/var/logはどちらもシステムのルートファイルシステム上にあり、kubeletはそのレイアウトを前提として設計されています。

ノードには、Kubernetesに使用されない他のファイルシステムをいくつでも持つことができます。

ノード上に、実行中のPodから生成されるエフェメラルデータ(ログとemptyDirボリューム)に使用するファイルシステムがあります。 このファイルシステムは他のデータ(例えばKubernetesに関連しないシステムログ)にも使用できます。ルートファイルシステムであっても構いません。

kubeletはノードレベルのコンテナログも最初のファイルシステムに書き込み、エフェメラルローカルストレージと同様に扱います。

また、異なる論理ストレージデバイスによって提供される、別のファイルシステムも使用します。 この設定では、コンテナイメージのレイヤーと書き込み可能なレイヤーを配置するようkubeletに指定するディレクトリが、この2番目のファイルシステム上にあります。

最初のファイルシステムには、イメージレイヤーや書き込み可能なレイヤーは保持されません。

ノードには、Kubernetesに使用されない他のファイルシステムをいくつでも持つことができます。

kubeletは、使用しているローカルストレージの量を測定できます。 これは、ローカルエフェメラルストレージのサポートされた設定のいずれかを使用してノードをセットアップした場合に提供されます。

異なる設定を使用している場合、kubeletはエフェメラルローカルストレージに対するリソース制限を適用しません。

備考:

kubeletは、tmpfsのemptyDirボリュームをローカルエフェメラルストレージとしてではなく、コンテナのメモリ使用量として追跡します。

備考:

kubeletは、エフェメラルストレージについてルートファイルシステムのみを追跡します。 /var/lib/kubeletまたは/var/lib/containersに別のディスクをマウントするOSレイアウトでは、エフェメラルストレージが正しく報告されません。

ローカルエフェメラルストレージのrequestsとlimitsの設定

ローカルエフェメラルストレージを管理するためにephemeral-storageを指定できます。 Podの各コンテナは、以下のいずれかまたは両方を指定できます:

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

ephemeral-storageのlimitsとrequestsはバイト単位で測定されます。 ストレージは、整数またはサフィックス(E、P、T、G、M、k)を使用した固定小数点数として表現できます。 また、2の累乗の等価表現(Ei、Pi、Ti、Gi、Mi、Ki)も使用できます。 例えば、以下の量はすべてほぼ同じ値を表します:

  • 128974848
  • 129e6
  • 129M
  • 123Mi

サフィックスの大文字小文字に注意してください。 ephemeral-storageに400mをリクエストした場合、これは0.4バイトのリクエストになります。 これを入力した人はおそらく400メビバイト(400Mi)または400メガバイト(400M)を要求するつもりだったでしょう。

以下の例では、Podに2つのコンテナがあります。 各コンテナには2GiBのローカルエフェメラルストレージのrequestがあります。 各コンテナには4GiBのローカルエフェメラルストレージのlimitがあります。 そのため、Podには4GiBのローカルエフェメラルストレージのrequestと、8GiBのローカルエフェメラルストレージのlimitがあります。 そのlimitのうち500MiはemptyDirボリュームによって消費される可能性があります。

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:
        sizeLimit: 500Mi

ephemeral-storageのrequestを持つPodがどのようにスケジュールされるか

Podを作成すると、KubernetesスケジューラーはPodを実行するノードを選択します。 各ノードには、Podに提供できるローカルエフェメラルストレージの最大量があります。 詳細については、Node Allocatableを参照してください。

スケジューラーは、スケジュールされたコンテナのリソースrequestの合計がノードの容量より少ないことを保証します。

エフェメラルストレージの消費量管理

kubeletがローカルエフェメラルストレージをリソースとして管理している場合、kubeletは以下のストレージ使用量を測定します:

  • tmpfsemptyDirボリュームを除く、emptyDirボリューム
  • ノードレベルのログを保持するディレクトリ
  • 書き込み可能なコンテナレイヤー

Podが許可された量を超えてエフェメラルストレージを使用している場合、kubeletはPodの退避をトリガーする退避シグナルを設定します。

コンテナレベルの分離では、コンテナの書き込み可能なレイヤーとログの使用量がストレージのlimitを超えた場合、kubeletはそのPodを退避対象としてマークします。

Podレベルの分離では、kubeletはそのPod内のコンテナのlimitsを合計することで、Pod全体のストレージlimitを算出します。 この場合、すべてのコンテナからのローカルエフェメラルストレージ使用量とPodのemptyDirボリュームの合計がPod全体のストレージlimitを超えた場合、kubeletはそのPodを退避対象としてマークします。

注意:

kubeletがローカルエフェメラルストレージを測定していない場合、ローカルストレージのlimitを超えたPodは、ローカルストレージリソースの制限違反によって退避されることはありません。

ただし、書き込み可能なコンテナレイヤー、ノードレベルのログ、またはemptyDirボリュームのファイルシステムの空き容量が少なくなると、ノードはローカルストレージが不足しているというtaintを自身に付与し、このtaintを明示的に許容しないすべてのPodの退避をトリガーします。

エフェメラルローカルストレージのサポートされた設定を参照してください。

kubeletは、Podのストレージ使用量を測定するための異なる方法をサポートしています:

kubeletは、各emptyDirボリューム、コンテナログディレクトリ、および書き込み可能なコンテナレイヤーをスキャンする定期的なチェックを実行します。

スキャンは、使用されているスペースの量を測定します。

備考:

このモードでは、kubeletは削除されたファイルのオープンファイルディスクリプターを追跡しません。

emptyDirボリューム内にファイルを作成(あなたまたはコンテナが)し、何かがそのファイルを開き、ファイルがまだ開いている間にそのファイルを削除した場合、削除されたファイルのinodeはそのファイルを閉じるまで残りますが、kubeletはそのスペースを使用中として分類しません。

        <div class="feature-state-notice feature-beta" title="フィーチャーゲート: LocalStorageCapacityIsolationFSQuotaMonitoring">
          <span class="feature-state-name">FEATURE STATE:</span> 
          <span class="feature-state-details">Kubernetes v1.31 
           
             (Beta; (デフォルトで無効))
           </span>
        </div>
        
        
        <div class="feature-beta">
          
          
            <details>
            <summary>More information about this feature</summary>
            <p>To use this feature, you (or a cluster administrator) will need to enable the <a href="/docs/reference/command-line-tools-reference/feature-gates/#LocalStorageCapacityIsolationFSQuotaMonitoring"><tt>LocalStorageCapacityIsolationFSQuotaMonitoring</tt></a> feature gate for all relevant components in your cluster.</p>

See Enable Or Disable Feature Gates for more information.

プロジェクトクォータは、ファイルシステム上のストレージ使用量を管理するためのオペレーティングシステムレベルの機能です。 Kubernetesでは、ストレージ使用量を監視するためにプロジェクトクォータを有効にできます。 ノード上でemptyDirボリュームを提供するファイルシステムがプロジェクトクォータをサポートしていることを確認してください。 例えば、XFSとext4fsはプロジェクトクォータを提供しています。

備考:

プロジェクトクォータはストレージ使用量を監視しますが、制限を強制するものではありません。

Kubernetesは1048576から始まるプロジェクトIDを使用します。 使用中のIDは/etc/projects/etc/projidに登録されます。 この範囲のプロジェクトIDがシステム上の他の目的で使用されている場合、Kubernetesがそれらを使用しないように、それらのプロジェクトIDを/etc/projects/etc/projidに登録する必要があります。

クォータはディレクトリスキャンよりも高速で正確です。 ディレクトリがプロジェクトに割り当てられると、そのディレクトリ配下に作成されたすべてのファイルはそのプロジェクト内に作成され、カーネルはそのプロジェクト内のファイルが使用しているブロック数を追跡するだけで済みます。 ファイルが作成されて削除されたが、オープンファイルディスクリプターを持っている場合、そのファイルはスペースを消費し続けます。 クォータの追跡はそのスペースを正確に記録しますが、ディレクトリスキャンでは削除されたファイルが使用しているストレージを見落とします。

クォータを使用してPodのリソース使用量を追跡するには、Podがユーザー名前空間内にある必要があります。 ユーザー名前空間内では、カーネルがファイルシステム上のprojectIDの変更を制限し、クォータによって計算されるストレージメトリクスの信頼性を保証します。

プロジェクトクォータを使用する場合は、以下を行う必要があります:

  • kubelet設定featureGatesフィールドを使用して、LocalStorageCapacityIsolationFSQuotaMonitoring=trueフィーチャーゲートを有効にする。

  • UserNamespacesSupportフィーチャーゲートが有効であり、カーネル、CRI実装、およびOCIランタイムがユーザー名前空間をサポートしていることを確認する。

  • ルートファイルシステム(またはオプションのランタイムファイルシステム)でプロジェクトクォータが有効になっていることを確認する。 すべてのXFSファイルシステムはプロジェクトクォータをサポートしています。 ext4ファイルシステムの場合、ファイルシステムがマウントされていない状態でプロジェクトクォータの追跡機能を有効にする必要があります。

    # ext4の場合、/dev/block-deviceがマウントされていない状態で
    sudo tune2fs -O project -Q prjquota /dev/block-device
    
  • ルートファイルシステム(またはオプションのランタイムファイルシステム)がプロジェクトクォータを有効にしてマウントされていることを確認する。 XFSとext4fsの両方で、マウントオプションはprjquotaという名前です。

プロジェクトクォータを使用しない場合は:

次の項目