チュートリアル 
    
      
本セクションにはチュートリアルが含まれています。チュートリアルでは、単一のタスク よりも大きな目標を達成する方法を示します。通常、チュートリアルにはいくつかのセクションがあり、各セクションには一連のステップがあります。各チュートリアルを進める前に、後で参照できるように標準化された用語集 ページをブックマークしておくことをお勧めします。
基本 
設定 
ステートレスアプリケーション 
ステートフルアプリケーション 
サービス 
セキュリティ 
次の項目 
チュートリアルのページタイプについての情報は、Content Page Types を参照してください。
 
 
  
  
  
  
  
  
  
    
    
	
    
    
	1 - Hello Minikube 
    
	
このチュートリアルでは、minikubeを使用して、Kubernetes上でサンプルアプリケーションを動かす方法を紹介します。
このチュートリアルはNGINXを利用してすべての要求をエコーバックするコンテナイメージを提供します。
目標 
minikubeへのサンプルアプリケーションのデプロイ 
アプリケーションの実行 
アプリケーションログの確認 
 
始める前に 
このチュートリアルは、minikubeがセットアップ済みであることを前提としています。
インストール手順はminikube start の Step 1  を参照してください。
備考: Step 1, Installation  の手順のみ実行してください。それ以降の手順はこのページで説明します。
また、kubectlをインストールする必要があります。
インストール手順はツールのインストール を参照してください。
minikubeクラスターの作成 
ダッシュボードを開く 
Kubernetesダッシュボードを開きます。これには二通りの方法があります:
新しい ターミナルを開き、次のコマンドを実行します:
# 新しいターミナルを起動し、以下を実行したままにします 
minikube dashboard
 minikube startを実行したターミナルに戻ります。
備考: dashboardコマンドは、ダッシュボードアドオンを有効にし、デフォルトのWebブラウザーでプロキシを開きます。
ダッシュボード上で、DeploymentやServiceなどのKubernetesリソースを作成できます。
ターミナルから直接ブラウザーを起動させずに、WebダッシュボードのURLを取得する方法については、「URLをコピー&ペースト」タブを参照してください。
デフォルトでは、ダッシュボードはKubernetesの仮想ネットワーク内部からのみアクセス可能です。
dashboardコマンドは、Kubernetes仮想ネットワークの外部からダッシュボードにアクセス可能にするための一時的なプロキシを作成します。
プロキシを停止するには、Ctrl+Cを実行してプロセスを終了します。
dashboardコマンドが終了した後も、ダッシュボードはKubernetesクラスター内で実行を続けます。
再度dashboardコマンドを実行すれば、新しい別のプロキシを作成してダッシュボードにアクセスできます。
 
  
minikubeが自動的にWebブラウザーを開くことを望まない場合、dashboardサブコマンドを--urlフラグと共に実行します。
minikubeは、お好みのブラウザーで開くことができるURLを出力します。
新しい ターミナルを開き、次のコマンドを実行します:
# 新しいターミナルを起動し、以下を実行したままにします 
minikube dashboard --url
 URLをコピー&ペーストし、ブラウザーで開きます。
minikube startを実行したターミナルに戻ります。
 Deploymentの作成 
KubernetesのPod Deployment 
kubectl createコマンドを使用してPodを管理するDeploymentを作成してください。Podは提供されたDockerイメージを元にコンテナを実行します。
# Webサーバーを含むテストコンテナイメージを実行する 
kubectl create deployment hello-node --image= registry.k8s.io/e2e-test-images/agnhost:2.53 -- /agnhost netexec --http-port= 8080 
  
Deploymentを確認します:
出力は下記のようになります:
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
hello-node   1/1     1            1           1m
(Podが利用可能になるまで時間がかかる場合があります。"0/1"と表示された場合は、数秒後にもう一度確認してみてください。)
 
Podを確認します:
出力は下記のようになります:
NAME                          READY     STATUS    RESTARTS   AGE
hello-node-5f76cf6ccf-br9b5   1/1       Running   0          1m
 
クラスターイベントを確認します:
 
kubectlで設定を確認します:
 
Podで実行されているコンテナのアプリケーションログを確認します(Podの名前はkubectl get podsで取得したものに置き換えてください)。
備考: kubectl logsコマンドの引数hello-node-5f76cf6ccf-br9b5は、kubectl get podsコマンドで取得したPodの名前に置き換えてください。
kubectl logs hello-node-5f76cf6ccf-br9b5
 出力は下記のようになります:
I0911 09:19:26.677397       1 log.go:195] Started HTTP server on port 8080
I0911 09:19:26.677586       1 log.go:195] Started UDP server on port  8081
 
 
Serviceの作成 
通常、PodはKubernetesクラスター内部のIPアドレスからのみアクセスすることができます。hello-nodeコンテナをKubernetesの仮想ネットワークの外部からアクセスするためには、KubernetesのService 
警告: agnhostコンテナには/shellエンドポイントがあり、デバッグには便利ですが、インターネットに公開するのは危険です。
インターネットに接続されたクラスターや、プロダクション環境のクラスターで実行しないでください。
kubectl exposeコマンドを使用してPodをインターネットに公開します:
kubectl expose deployment hello-node --type= LoadBalancer --port= 8080 
 --type=LoadBalancerフラグはServiceをクラスター外部に公開したいことを示しています。
テストイメージ内のアプリケーションコードはTCPの8080番ポートのみを待ち受けます。
kubectl exposeで8080番ポート以外を公開した場合、クライアントはそのポートに接続できません。
 
作成したServiceを確認します:
出力は下記のようになります:
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-node   LoadBalancer   10.108.144.78   <pending>     8080:30369/TCP   21s
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          23m
ロードバランサーをサポートするクラウドプロバイダーでは、Serviceにアクセスするための外部IPアドレスが提供されます。
minikubeでは、LoadBalancerタイプはminikube serviceコマンドを使用した接続可能なServiceを作成します。
 
次のコマンドを実行します:
minikube service hello-node
 アプリケーションとその応答が表示されるブラウザーウィンドウが開きます。
 
 
アドオンの有効化 
minikubeはビルトインのアドオン があり、有効化、無効化、あるいはローカルのKubernetes環境に公開することができます。
サポートされているアドオンをリストアップします:
出力は下記のようになります:
addon-manager: enabled
dashboard: enabled
default-storageclass: enabled
efk: disabled
freshpod: disabled
gvisor: disabled
helm-tiller: disabled
ingress: disabled
ingress-dns: disabled
logviewer: disabled
metrics-server: disabled
nvidia-driver-installer: disabled
nvidia-gpu-device-plugin: disabled
registry: disabled
registry-creds: disabled
storage-provisioner: enabled
storage-provisioner-gluster: disabled
 
ここでは例としてmetrics-serverのアドオンを有効化します:
minikube addons enable  metrics-server
 出力は下記のようになります:
The 'metrics-server' addon is enabled
 
作成されたPodとサービスを確認します:
kubectl get pod,svc -n kube-system
 出力:
NAME                                        READY     STATUS    RESTARTS   AGE
pod/coredns-5644d7b6d9-mh9ll                1/1       Running   0          34m
pod/coredns-5644d7b6d9-pqd2t                1/1       Running   0          34m
pod/metrics-server-67fb648c5                1/1       Running   0          26s
pod/etcd-minikube                           1/1       Running   0          34m
pod/influxdb-grafana-b29w8                  2/2       Running   0          26s
pod/kube-addon-manager-minikube             1/1       Running   0          34m
pod/kube-apiserver-minikube                 1/1       Running   0          34m
pod/kube-controller-manager-minikube        1/1       Running   0          34m
pod/kube-proxy-rnlps                        1/1       Running   0          34m
pod/kube-scheduler-minikube                 1/1       Running   0          34m
pod/storage-provisioner                     1/1       Running   0          34m
NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
service/metrics-server         ClusterIP   10.96.241.45    <none>        80/TCP              26s
service/kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP       34m
service/monitoring-grafana     NodePort    10.99.24.54     <none>        80:30002/TCP        26s
service/monitoring-influxdb    ClusterIP   10.111.169.94   <none>        8083/TCP,8086/TCP   26s
 
metrics-serverの出力を確認します:
出力は下記のようになります:
NAME                         CPU(cores)   MEMORY(bytes)
hello-node-ccf4b9788-4jn97   1m           6Mi
次のメッセージが表示された場合は、しばらく待ってから再度実行してください:
error: Metrics API not available
 
metrics-serverを無効化します:
minikube addons disable metrics-server
 出力は下記のようになります:
metrics-server was successfully disabled
 
 
クリーンアップ 
クラスターに作成したリソースをクリーンアップします:
kubectl delete service hello-node
 kubectl delete deployment hello-node
 minikubeクラスターを停止します
(オプション)minikubeのVMを削除します:
Kubernetesの学習で再度minikubeを使用したい場合、minikubeのVMを削除する必要はありません。
まとめ 
このページでは、minikubeクラスターを立ち上げて実行するための基本的な部分を説明しました。
これでアプリケーションをデプロイする準備が整いました。
次の項目 
 
    
	
  
    
    
	
    
    
	2 - Kubernetesの基本を学ぶ 
    
	
  
    
      
        Kubernetesの基本 
        このチュートリアルでは、Kubernetesクラスターオーケストレーションシステムの基本について学びます。各モジュールには、Kubernetesの主な機能と概念に関する背景情報と、一緒に進めるためのチュートリアルが含まれています。
        このチュートリアルでは、以下のことを学ぶことができます:
        
        コンテナ化されたアプリケーションをクラスターにデプロイ 
        Deploymentのスケーリング 
        新しいソフトウェアのバージョンでコンテナ化されたアプリケーションをアップデート 
        コンテナ化されたアプリケーションのデバッグ 
         
       
     
    
      
        Kubernetesはどんなことができるの? 
        モダンなWebサービスでは、ユーザはアプリケーションが24時間365日利用可能であることを期待しており、開発者はそれらのアプリケーションの新しいバージョンを1日に数回デプロイすることを期待しています。コンテナ化は、パッケージソフトウェアがこれらの目標を達成するのを助け、アプリケーションをダウンタイムなしでリリース、アップデートできるようにします。Kubernetesを使用すると、コンテナ化されたアプリケーションをいつでもどこでも好きなときに実行できるようになり、それらが機能するために必要なリソースとツールを見つけやすくなります。Kubernetesは、コンテナオーケストレーションにおけるGoogleのこれまでの経験と、コミュニティから得られた最善のアイデアを組み合わせて設計された、プロダクションレディなオープンソースプラットフォームです。
       
     
     
 
 
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	
2.1 - クラスターの作成 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	2.1.1 - Minikubeを使ったクラスターの作成 
    Kubernetesクラスターとは何かを学ぶ。
Minikubeとは何かを学ぶ。
Kubernetesクラスターを起動する。
	
    
        
      
          目標 
                
                    Kubernetesクラスターとは何かを学ぶ 
                    Minikubeとは何かを学ぶ 
                    Kubernetesクラスターをローカルで動かす 
                 
             
            
                Kubernetesクラスター 
                
                Kubernetesは、単一のユニットとして機能するように接続された、可用性の高いコンピューターのクラスターをまとめあげます。 Kubernetesの抽象化により、コンテナ化されたアプリケーションを個々のマシンに特に結び付けることなくクラスターにデプロイできます。この新しいデプロイモデルを利用するには、アプリケーションを個々のホストから切り離す方法でアプリケーションをパッケージ化(つまり、コンテナ化)する必要があります。コンテナ化されたアプリケーションは、アプリケーションがホストに深く統合されたパッケージとして特定のマシンに直接インストールされていた従来のデプロイモデルよりも柔軟で、より迅速に利用可能です。Kubernetesはより効率的な方法で、クラスター全体のアプリケーションコンテナの配布とスケジューリングを自動化します。 Kubernetesはオープンソースのプラットフォームであり、プロダクションレディです。
                
                Kubernetesクラスターは以下の2種類のリソースで構成されています:
                    
                        コントロールプレーン がクラスターを管理するノード がアプリケーションを動かすワーカーとなる 
                
             
            
                
                
                    
                        Kubernetesは、コンピュータークラスター内およびコンピュータークラスター間でのアプリケーションコンテナの配置(スケジューリング)および実行を調整する、プロダクショングレードのオープンソースプラットフォームです。
                     
                 
             
         
        
            
                コントロールプレーンはクラスターの管理を担当します。 コントロールプレーンは、アプリケーションのスケジューリング、望ましい状態の維持、アプリケーションのスケーリング、新しい更新のロールアウトなど、クラスター内のすべての動作をまとめあげます。
                ノードは、Kubernetesクラスターのワーカーマシンとして機能するVMまたは物理マシンです。 各ノードにはKubeletがあり、これはノードを管理し、Kubernetesコントロールプレーンと通信するためのエージェントです。ノードにはcontainerd  や CRI-O  などのコンテナ操作を処理するためのツールもあるはずです。プロダクションのトラフィックを処理するKubernetesクラスターには、最低3つのノードが必要です。これは、1ノードがダウンすると、etcd  メンバーとコントロールプレーンのインスタンスの両方を同時に失うリスクがあり、冗長性が損なわれてしまうからです。このリスクを軽減するには、コントロールプレーンノードを複数構成することで対応できます。
             
            
                
                    コントロールプレーンは実行中のアプリケーションをホストするために使用されるノードとクラスターを管理します。 
                 
             
         
        
            
                Kubernetesにアプリケーションをデプロイするときは、コントロールプレーンにアプリケーションコンテナを起動するように指示します。コントロールプレーンはコンテナがクラスターのノードで実行されるようにスケジュールします。ノードは、コントロールプレーンが公開しているKubernetes API を使用してコントロールプレーンと通信します。 エンドユーザーは、Kubernetes APIを直接使用して対話することもできます。
                Kubernetesクラスターは、物理マシンまたは仮想マシンのどちらにも配置できます。Kubernetes開発を始めるためにMinikubeを使うことができます。Minikubeは、ローカルマシン上にVMを作成し、1つのノードのみを含む単純なクラスターをデプロイする軽量なKubernetes実装です。Minikubeは、Linux、macOS、およびWindowsシステムで利用可能です。Minikube CLIは、起動、停止、ステータス、削除など、クラスターを操作するための基本的なブートストラップ操作を提供します。
                Kubernetesが何であるかがわかったので、Hello Minikube  に行き、最初のクラスターを動かしましょう!
             
         
     
 
 
    
	
  
    
	
  
    
    
	
    
    
	
2.2 - アプリケーションのデプロイ 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	2.2.1 - Deploymentを作成するためにkubectlを使う 
    
	目標 
アプリケーションのデプロイについて学ぶ。 
kubectlを使って、Kubernetes上にはじめてのアプリケーションをデプロイする。 
 
KubernetesのDeployment 
    Deploymentは、アプリケーションのインスタンスを作成および更新する責務があります。 
備考: このチュートリアルでは、AMD64アーキテクチャを必要とするコンテナを使用します。
異なるCPUアーキテクチャのコンピューターでminikubeを使用する場合は、AMD64をエミュレートできるドライバーでminikubeを使用してみてください。
例えば、Docker Desktopドライバーはこれが可能です。
実行中のKubernetesクラスター を入手すると、その上にコンテナ化アプリケーションをデプロイすることができます。
そのためには、KubernetesのDeployment の設定を作成します。
DeploymentはKubernetesにあなたのアプリケーションのインスタンスを作成し、更新する方法を指示します。
Deploymentを作成すると、KubernetesコントロールプレーンはDeployment内に含まれるアプリケーションインスタンスをクラスター内の個々のノードで実行するようにスケジュールします。
アプリケーションインスタンスが作成されると、Kubernetes Deploymentコントローラーは、それらのインスタンスを継続的に監視します。
インスタンスをホストしているノードが停止、削除された場合、Deploymentコントローラーはそのインスタンスをクラスター内の別のノード上のインスタンスと置き換えます。
これは、マシンの故障やメンテナンスに対処するためのセルフヒーリングの仕組みを提供しています。 
オーケストレーションが登場する前の世界では、インストールスクリプトを使用してアプリケーションを起動することはよくありましたが、マシン障害が発生した場合に復旧する事はできませんでした。
アプリケーションのインスタンスを作成し、それらをノード間で実行し続けることで、Kubernetes Deploymentはアプリケーションの管理に根本的に異なるアプローチを提供します。
Kubernetes上にはじめてのアプリケーションをデプロイする 
    Kubernetesにデプロイするには、アプリケーションをサポートされているコンテナ形式のいずれかにパッケージ化する必要があります。 
     
Kubernetesのコマンドラインインターフェースであるkubectl を使用して、Deploymentを作成、管理できます。
kubectlはKubernetes APIを使用してクラスターと対話します。
このモジュールでは、Kubernetesクラスターでアプリケーションを実行するDeploymentを作成するために必要な、最も一般的なkubectlコマンドについて学びます。
Deploymentを作成するときは、アプリケーションのコンテナイメージと実行するレプリカの数を指定する必要があります。
Deploymentを更新することで、あとでその情報を変更できます。
ブートキャンプのModule 5 とModule 6 では、Deploymentをどのようにスケール、更新できるかについて説明します。
最初のDeploymentには、NGINXを使用して全てのリクエストをエコーバックする、Dockerコンテナにパッケージ化されたhello-nodeアプリケーションを使用します。
(まだhello-nodeアプリケーションを作成して、コンテナを使用してデプロイしていない場合、Hello Minikube tutorial の通りにやってみましょう。)
kubectlもインストールされている必要があります。
インストールが必要な場合は、ツールのインストール からインストールしてください。
Deploymentが何であるかがわかったので、最初のアプリケーションをデプロイしましょう!
kubectlの基本 
kubectlコマンドの一般的な書式はkubectl action resourceです。
これは指定された resource (node、deploymentなど)に対して指定された action (create、describe、deleteなど)を実行します。
指定可能なパラメーターに関する追加情報を取得するために、サブコマンドの後に--helpを使うこともできます(例:kubectl get nodes --help)。
kubectl versionコマンドを実行して、kubectlがクラスターと通信できるように設定されていることを確認してください。
kubectlがインストールされていて、クライアントとサーバーの両方のバージョンを確認できることを確認してください。
クラスター内のノードを表示するには、kubectl get nodesコマンドを実行します。
利用可能なノードが表示されます。
後で、KubernetesはNodeの利用可能なリソースに基づいてアプリケーションをデプロイする場所を選択します。
アプリケーションをデプロイする 
最初のアプリケーションをkubectl create deploymentコマンドでKubernetesにデプロイしてみましょう。
デプロイ名とアプリケーションイメージの場所を指定する必要があります(Docker Hub外でホストされているイメージはリポジトリの完全なURLを含める必要があります)。
kubectl create deployment kubernetes-bootcamp --image= gcr.io/google-samples/kubernetes-bootcamp:v1
 素晴らしい!Deploymentを作成して、最初のアプリケーションをデプロイしました。
これによっていくつかのことが実行されました。
アプリケーションのインスタンスを実行可能な適切なノードを探しました(利用可能なノードは1つしかない)。 
アプリケーションをそのノードで実行するためのスケジュールを行いました。 
必要な場合にインスタンスを新しいノードで再スケジュールするような設定を行いました。 
 
Deploymentの一覧を得るにはkubectl get deploymentsコマンドを使います。
アプリケーションの単一のインスタンスを実行しているDeploymentが1つあることが分かります。
インスタンスはノード上のコンテナ内で実行されています。
アプリケーションを見る 
Kubernetes内部で動作しているPod は、プライベートに隔離されたネットワーク上で動作しています。
デフォルトでは、同じKubernetesクラスター内の他のPodやServiceからは見えますが、そのネットワークの外からは見えません。
kubectlを使用する場合、アプリケーションと通信するためにAPIエンドポイントを通じてやりとりしています。
アプリケーションをKubernetesクラスターの外部に公開するための他のオプションについては、後ほどModule 4 で説明します。
また、これは基本的なチュートリアルであるため、ここではPodとは何かについては詳しく説明しません。
kubectl proxyコマンドによって、通信をクラスター全体のプライベートネットワークに転送するプロキシを作成することができます。
プロキシはcontrol-Cキーを押すことで終了させることができ、実行中は何も出力されません。
プロキシを実行するにはもう一つターミナルウィンドウを開く必要があります。 
ホスト(端末)とKubernetesクラスター間の接続ができました。
プロキシによって端末からAPIへの直接アクセスが可能となります。
プロキシエンドポイントを通してホストされている全てのAPIを確認することができます。
例えば、curlコマンドを使って、APIを通じて直接バージョンを調べることができます。
curl http://localhost:8001/version
 
備考: ポート8001にアクセスできない場合は、上で起動したkube proxyがもう一つのターミナルで実行されていることを確認してください。
APIサーバーはPod名に基づいて各Pod用のエンドポイントを自動的に作成し、プロキシからもアクセスできるようにします。
まずPod名を取得する必要があるので、環境変数POD_NAMEに格納しておきます。
export  POD_NAME = $( kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' ) 
echo  Name of the Pod: $POD_NAME 
以下を実行することで、プロキシされたAPIを通してPodにアクセスすることができます。
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME :8080/proxy/
 プロキシを使わずに新しいDeploymentにアクセスするには、Module 4 で説明するServiceが必要です。
次の項目 
 
    
	
  
    
	
  
    
    
	
    
    
	
2.3 - アプリケーションの探索 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	2.3.1 - Podとノードについて 
    
	
    
        
     
          目標 
                
                    KubernetesのPodについて学ぶ 
                    Kubernetesのノードについて学ぶ 
                    デプロイされたアプリケーションのトラブルシューティング 
                 
             
            
                Kubernetes Pod 
                モジュール2 でDeploymentを作成したときに、KubernetesはアプリケーションインスタンスをホストするためのPodを作成しました。Podは、1つ以上のアプリケーションコンテナ(Dockerなど)のグループとそれらのコンテナの共有リソースを表すKubernetesの抽象概念です。 Podには以下のものが含まれます:
                
                    共有ストレージ(ボリューム) 
                    ネットワーキング(クラスターに固有のIPアドレス) 
                    コンテナのイメージバージョンや使用するポートなどの、各コンテナをどう動かすかに関する情報 
                 
                Podは、アプリケーション固有の「論理ホスト」をモデル化し、比較的密接に結合されたさまざまなアプリケーションコンテナを含むことができます。 たとえば、Podには、Node.jsアプリケーションを含むコンテナと、Node.js Webサーバによって公開されるデータを供給する別のコンテナの両方を含めることができます。Pod内のコンテナはIPアドレスとポートスペースを共有し、常に同じ場所に配置され、同じスケジュールに入れられ、同じノード上の共有コンテキストで実行されます。
                Podは、Kubernetesプラットフォームの原子単位です。 Kubernetes上にDeploymentを作成すると、そのDeploymentはその中にコンテナを持つPodを作成します(コンテナを直接作成するのではなく)。 各Podは、スケジュールされているノードに関連付けられており、終了(再起動ポリシーに従って)または削除されるまでそこに残ります。 ノードに障害が発生した場合、同じPodがクラスター内の他の使用可能なノードにスケジュールされます。
             
            
                
                
                        
                            Podは1つ以上のアプリケーションコンテナ(Dockerなど)のグループであり、共有ストレージ(ボリューム)、IPアドレス、それらの実行方法に関する情報が含まれています。
                         
                 
             
         
        
            
                ノード 
                Podは常にノード 上で動作します。ノードはKubernetesではワーカーマシンであり、クラスターによって仮想、物理マシンのどちらであってもかまいません。各ノードはマスターによって管理されます。ノードは複数のPodを持つことができ、Kubernetesマスターはクラスター内のノード間でPodのスケジュールを自動的に処理します。マスターの自動スケジューリングは各ノードで利用可能なリソースを考慮に入れます。
                すべてのKubernetesノードでは少なくとも以下のものが動作します。
                
                    Kubelet: Kubernetesマスターとノード間の通信を担当するプロセス。マシン上で実行されているPodとコンテナを管理します。 
                    レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行することを担当する、Dockerのようなコンテナランタイム。 
                 
             
            
                
                     コンテナ同士が密接に結合され、ディスクなどのリソースを共有する必要がある場合は、コンテナを1つのPodにまとめてスケジュールする必要があります。  
                 
             
         
        
            
                kubectlを使ったトラブルシューティング 
                モジュール2 では、kubectlのコマンドラインインターフェースを使用しました。モジュール3でもこれを使用して、デプロイされたアプリケーションとその環境に関する情報を入手します。最も一般的な操作は、次のkubectlコマンドで実行できます。
                
                    kubectl get  - リソースの一覧を表示kubectl describe  - 単一リソースに関する詳細情報を表示kubectl logs  - 単一Pod上の単一コンテナ内のログを表示kubectl exec  - 単一Pod上の単一コンテナ内でコマンドを実行 
                これらのコマンドを使用して、アプリケーションがいつデプロイされたか、それらの現在の状況、実行中の場所、および構成を確認することができます。
                クラスターのコンポーネントとコマンドラインの詳細についてわかったので、次にデプロイしたアプリケーションを探索してみましょう。
             
            
                
                     ノードはKubernetesではワーカーマシンであり、クラスターに応じてVMまたは物理マシンになります。 複数のPodを1つのノードで実行できます。  
                 
             
         
         
 
 
    
	
  
    
	
  
    
    
	
    
    
	
2.4 - アプリケーションの公開 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	2.4.1 - Serviceを使ったアプリケーションの公開 
    
	
	
		
			
    		目標 
				
					KubernetesにおけるServiceについて理解する 
					ラベルとLabelSelectorオブジェクトがServiceにどう関係しているかを理解する 
					Serviceを使って、Kubernetesクラスターの外にアプリケーションを公開する 
				 
			 
			
			Kubernetes Serviceの概要 
			Kubernetes Podの寿命は永続的ではありません。実際、Pod にはライフサイクル があります。ワーカーのノードが停止すると、そのノードで実行されているPodも失われます。そうなると、ReplicaSet は、新しいPodを作成してアプリケーションを実行し続けるために、クラスターを動的に目的の状態に戻すことができます。別の例として、3つのレプリカを持つ画像処理バックエンドを考えます。それらのレプリカは交換可能です。フロントエンドシステムはバックエンドのレプリカを気にしたり、Podが失われて再作成されたとしても配慮すべきではありません。ただし、Kubernetesクラスター内の各Podは、同じノード上のPodであっても一意のIPアドレスを持っているため、アプリケーションが機能し続けるように、Pod間の変更を自動的に調整する方法が必要です。
			KubernetesのServiceは、Podの論理セットと、それらにアクセスするためのポリシーを定義する抽象概念です。Serviceによって、依存Pod間の疎結合が可能になります。Serviceは、すべてのKubernetesオブジェクトのように、YAML(推奨) またはJSONを使って定義されます。Serviceが対象とするPodのセットは通常、LabelSelector によって決定されます(なぜ仕様にセレクタを含めずにServiceが必要になるのかについては下記を参照してください)。
			各Podには固有のIPアドレスがありますが、それらのIPは、Serviceなしではクラスターの外部に公開されません。Serviceによって、アプリケーションはトラフィックを受信できるようになります。ServiceSpecでtypeを指定することで、Serviceをさまざまな方法で公開することができます。
			
				ClusterIP  (既定値) - クラスター内の内部IPでServiceを公開します。この型では、Serviceはクラスター内からのみ到達可能になります。NodePort  - NATを使用して、クラスター内の選択された各ノードの同じポートにServiceを公開します。<NodeIP>:<NodePort>を使用してクラスターの外部からServiceにアクセスできるようにします。これはClusterIPのスーパーセットです。LoadBalancer  - 現在のクラウドに外部ロードバランサを作成し(サポートされている場合)、Serviceに固定の外部IPを割り当てます。これはNodePortのスーパーセットです。ExternalName  - 仕様のexternalNameで指定した名前のCNAMEレコードを返すことによって、任意の名前を使ってServiceを公開します。プロキシは使用されません。このタイプはv1.7以上のkube-dnsを必要とします。 
			さまざまな種類のServiceに関する詳細情報はUsing Source IP  tutorialにあります。アプリケーションとServiceの接続 も参照してください。
			加えて、Serviceには、仕様にselectorを定義しないというユースケースがいくつかあります。selectorを指定せずに作成したServiceについて、対応するEndpointsオブジェクトは作成されません。これによって、ユーザーは手動でServiceを特定のエンドポイントにマッピングできます。セレクタがない可能性があるもう1つの可能性は、type:ExternalNameを厳密に使用していることです。
			 
			
				
					まとめ 
					
						Podを外部トラフィックに公開する 
						複数のPod間でトラフィックを負荷分散する 
						ラベルを使う 
					 
				 
				
						Kubernetes Serviceは、Podの論理セットを定義し、それらのPodに対する外部トラフィックの公開、負荷分散、およびサービス検出を可能にする抽象化層です。 
				 
			 
		 
		
			
				Serviceは、一連のPodにトラフィックをルーティングします。Serviceは、アプリケーションに影響を与えることなく、KubernetesでPodが死んだり複製したりすることを可能にする抽象概念です。(アプリケーションのフロントエンドおよびバックエンドコンポーネントなどの)依存Pod間の検出とルーティングは、Kubernetes Serviceによって処理されます。
				Serviceは、ラベルとセレクタを使用して一連のPodを照合します。これは、Kubernetes内のオブジェクトに対する論理操作を可能にするグループ化のプリミティブです。ラベルはオブジェクトに付けられたkey/valueのペアであり、さまざまな方法で使用できます。
				
					開発、テスト、および本番用のオブジェクトを指定する 
					バージョンタグを埋め込む 
					タグを使用してオブジェクトを分類する 
				 
			 
		 
		
			
				ラベルは、作成時またはそれ以降にオブジェクトにアタッチでき、いつでも変更可能です。Serviceを使用してアプリケーションを公開し、いくつかのラベルを適用してみましょう。
			 
		 
		 
 
 
    
	
  
    
    
	
    
    
	2.4.2 - 対話型チュートリアル - アプリケーションの公開 
    
	
    
        
            
                ターミナルを使うには、PCまたはタブレットをお使いください
            
            
            
         
        
     
 
 
    
	
  
    
	
  
    
    
	
    
    
	
2.5 - アプリケーションのスケーリング 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	2.5.1 - アプリケーションの複数インスタンスを実行 
    
	
    
        
     
          目標 
                
                    kubectlを使用してアプリケーションをスケールする 
                 
             
            
       アプリケーションのスケーリング 
            前回のモジュールでは、Deployment を作成し、それをService 経由で公開しました。該当のDeploymentでは、アプリケーションを実行するためのPodを1つだけ作成しました。トラフィックが増加した場合、ユーザーの需要に対応するためにアプリケーションをスケールする必要があります。
            スケーリング は、Deploymentのレプリカの数を変更することによって実現可能です。
             
            
                
                
                    kubectl create deploymentコマンドの--replicasパラメーターを使用することで、最初から複数のインスタンスを含むDeploymentを作成できます。 
                 
             
         
        
            
                Deploymentをスケールアウトすると、新しいPodが作成され、使用可能なリソースを持つノードにスケジュールされます。スケールすると、Podの数が増えて新たな望ましい状態になります。KubernetesはPodのオートスケーリング もサポートしていますが、このチュートリアルでは範囲外です。スケーリングを0に設定することも可能で、指定された配置のすべてのPodを終了させます。
                アプリケーションの複数インスタンスを実行するには、それらすべてにトラフィックを分散する方法が必要になります。Serviceには、公開されたDeploymentのすべてのPodにネットワークトラフィックを分散する統合ロードバランサがあります。Serviceは、エンドポイントを使用して実行中のPodを継続的に監視し、トラフィックが使用可能なPodにのみ送信されるようにします。
             
            
                
                    スケーリングは、Deploymentのレプリカの数を変更することによって実現可能です。 
                 
             
         
        
            
                アプリケーションの複数のインスタンスを実行すると、ダウンタイムなしでローリングアップデートを実行できます。それについては、次のモジュールで学習します。それでは、オンラインのターミナルを使って、アプリケーションをデプロイしてみましょう。
             
         
         
 
 
    
	
  
    
    
	
    
    
	2.5.2 - 対話型チュートリアル - アプリケーションのスケーリング 
    
	
    
        
            
                ターミナルを使うには、PCまたはタブレットをお使いください
            
            
            
         
        
     
 
 
    
	
  
    
	
  
    
    
	
    
    
	
2.6 - アプリケーションのアップデート 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	2.6.1 - ローリングアップデートの実行 
    
	
    
        
     
          目標 
                
                    kubectlを使ってローリングアップデートを実行する 
                 
             
            
            アプリケーションのアップデート 
            ユーザーはアプリケーションが常に利用可能であることを期待し、開発者はそれらの新しいバージョンを1日に数回デプロイすることが期待されます。Kubernetesでは、アプリケーションのアップデートをローリングアップデートで行います。ローリングアップデート では、Podインスタンスを新しいインスタンスで段階的にアップデートすることで、ダウンタイムなしでDeploymentをアップデートできます。新しいPodは、利用可能なリソースを持つノードにスケジュールされます。
            前回のモジュールでは、複数のインスタンスを実行するようにアプリケーションをデプロイしました。これは、アプリケーションの可用性に影響を与えずにアップデートを行うための要件です。デフォルトでは、アップデート中に使用できなくなる可能性があるPodの最大数と作成できる新しいPodの最大数は1です。どちらのオプションも、Podの数または全体数に対する割合(%)のいずれかに設定できます。Kubernetesでは、アップデートはバージョン管理されており、Deploymentにおけるアップデートは以前の(stable)バージョンに戻すことができます。
             
            
                
                
                    ローリングアップデートでは、Podを新しいインスタンスで段階的にアップデートすることで、ダウンタイムなしDeploymentをアップデートできます。 
                 
             
         
        
            
                アプリケーションのスケーリングと同様に、Deploymentがパブリックに公開されている場合、Serviceはアップデート中に利用可能なPodのみにトラフィックを負荷分散します。 利用可能なPodは、アプリケーションのユーザーが利用できるインスタンスです。
                ローリングアップデートでは、次の操作が可能です。
                
                    コンテナイメージのアップデートを介した、ある環境から別の環境へのアプリケーションの昇格 
                    以前のバージョンへのロールバック 
                    ダウンタイムなしでのアプリケーションのCI/CD 
                 
             
            
                
                    Deploymentがパブリックに公開されている場合、Serviceはアップデート中に利用可能なPodにのみトラフィックを負荷分散します。  
                 
             
         
        
            
                次の対話型チュートリアルでは、アプリケーションを新しいバージョンにアップデートし、ロールバックも実行します。
             
         
         
 
 
    
	
  
    
    
	
    
    
	2.6.2 - 対話型チュートリアル - アプリケーションのアップデート 
    
	
    
        
            
                ターミナルを使うには、PCまたはタブレットをお使いください
            
            
            
         
        
     
 
 
    
	
  
    
	
  
    
	
  
    
    
	
    
    
	
3 - 設定 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	3.1 - ConfigMapを使ったRedisの設定 
    
	
本ページでは、ConfigMapを使ったコンテナの設定 に基づき、ConfigMapを使ってRedisの設定を行う実践的な例を提供します。
目標 
以下の要素を含むkustomization.yamlファイルを作成する:
ConfigMapGenerator 
ConfigMapを使ったPodリソースの設定 
 
 
kubectl apply -k ./コマンドにてディレクトリ全体を適用する設定が正しく反映されていることを確認する 
 
始める前に 
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。
このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。
まだクラスターがない場合、minikube を使って作成するか、
以下のいずれかのKubernetesプレイグラウンドも使用できます:
 
  バージョンを確認するには次のコマンドを実行してください:  kubectl version.
実践例: ConfigMapを使ったRedisの設定 
以下の手順に従って、ConfigMapに保存されているデータを使用してRedisキャッシュを設定できます。
最初に、redis-configファイルからConfigMapを含むkustomization.yamlを作成します:
    
    maxmemory 2mb
maxmemory-policy allkeys-lru
 
curl -OL https://k8s.io/examples/pods/config/redis-config
 
 cat <<EOF >./kustomization.yaml
  configMapGenerator:
 - name: example-redis-config
   files:
   - redis-config
 EOF 
Podリソースの設定をkustomization.yamlに入れます:
    
    apiVersion :  v1
 kind :  Pod
 metadata :
    name :  redis
 spec :
    containers :
    - name :  redis
      image :  kubernetes/redis:v1
      env :
      - name :  MASTER
        value :  "true" 
      ports :
      - containerPort :  6379 
      resources :
        limits :
          cpu :  "0.1" 
      volumeMounts :
      - mountPath :  /redis-master-data
        name :  data
      - mountPath :  /redis-master
        name :  config
    volumes :
      - name :  data
        emptyDir :  {}
      - name :  config
        configMap :
          name :  example-redis-config
          items :
          - key :  redis-config
            path :  redis.conf
  
curl -OL https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/pods/config/redis-pod.yaml
 
 cat <<EOF >>./kustomization.yaml
  resources:
 - redis-pod.yaml
 EOF 
kustomizationディレクトリを反映して、ConfigMapオブジェクトとPodオブジェクトの両方を作成します:
作成されたオブジェクトを確認します
> kubectl get -k .
 NAME                                        DATA   AGE
 configmap/example-redis-config-dgh9dg555m   1       52s
 
 NAME        READY   STATUS    RESTARTS   AGE
 pod/redis   1/1     Running   0           52s
 この例では、設定ファイルのボリュームは/redis-masterにマウントされています。
pathを使ってredis-configキーをredis.confという名前のファイルに追加します。
したがって、redisコンフィグのファイルパスは/redis-master/redis.confです。
ここが、コンテナイメージがredisマスターの設定ファイルを探す場所です。
kubectl execを使ってPodに入り、redis-cliツールを実行して設定が正しく適用されたことを確認してください:
kubectl exec  -it redis -- redis-cli
 127.0.0.1:6379> CONFIG GET maxmemory
 1)  "maxmemory" 
 2)  "2097152" 
 127.0.0.1:6379> CONFIG GET maxmemory-policy
 1)  "maxmemory-policy" 
 2)  "allkeys-lru" 
 作成したPodを削除します:
次の項目 
 
    
	
  
    
	
  
    
    
	
    
    
	
4 - ステートレスアプリケーション 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	4.1 - クラスター内のアプリケーションにアクセスするために外部IPアドレスを公開する 
    
	
このページでは、外部IPアドレスを公開するKubernetesのServiceオブジェクトを作成する方法を示します。
始める前に 
kubectl をインストールしてください。
 
Kubernetesクラスターを作成する際に、Google Kubernetes EngineやAmazon Web Servicesのようなクラウドプロバイダーを使用します。このチュートリアルでは、クラウドプロバイダーを必要とする外部ロードバランサー を作成します。
 
Kubernetes APIサーバーと通信するために、kubectlを設定してください。手順については、各クラウドプロバイダーのドキュメントを参照してください。
 
 
目標 
5つのインスタンスで実際のアプリケーションを起動します。 
外部IPアドレスを公開するServiceオブジェクトを作成します。 
起動中のアプリケーションにアクセスするためにServiceオブジェクトを使用します。 
 
5つのPodで起動しているアプリケーションへのServiceの作成 
クラスターにてHello Worldアプリケーションを実行してください。 
 
    
    apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    labels :
      app.kubernetes.io/name :  load-balancer-example
    name :  hello-world
 spec :
    replicas :  5 
    selector :
      matchLabels :
        app.kubernetes.io/name :  load-balancer-example
    template :
      metadata :
        labels :
          app.kubernetes.io/name :  load-balancer-example
      spec :
        containers :
        - image :  gcr.io/google-samples/node-hello:1.0
          name :  hello-world
          ports :
          - containerPort :  8080 
  
kubectl apply -f https://k8s.io/examples/service/load-balancer-example.yaml
 上記のコマンドにより、 Deployment を作成し、ReplicaSet を関連づけます。ReplicaSetには5つのPod があり、それぞれHello Worldアプリケーションが起動しています。
Deploymentに関する情報を表示します:
 kubectl get deployments hello-world
 kubectl describe deployments hello-world
 
ReplicaSetオブジェクトに関する情報を表示します:
 kubectl get replicasets
 kubectl describe replicasets
 
Deploymentを公開するServiceオブジェクトを作成します。
 kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
 
Serviceに関する情報を表示します:
 kubectl get services my-service
出力は次のようになります:
 NAME         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
 my-service   LoadBalancer   10.3.245.137   104.198.205.71   8080/TCP   54s
備考: `type=LoadBalancer`のServiceは外部のクラウドプロバイダーによってサポートされており、ここでは扱いません。詳細は[こちらのページ](/ja/docs/concepts/services-networking/service/#loadbalancer)を参照してください。
備考: 外部IPアドレスが\<pending\>と表示されている場合は、しばらく待ってから同じコマンドを実行してください。
 
Serviceに関する詳細な情報を表示します:
 kubectl describe services my-service
出力は次のようになります:
 Name:           my-service
 Namespace:      default
 Labels:         app.kubernetes.io/name=load-balancer-example
 Annotations:    <none>
 Selector:       app.kubernetes.io/name=load-balancer-example
 Type:           LoadBalancer
 IP:             10.3.245.137
 LoadBalancer Ingress:   104.198.205.71
 Port:           <unset> 8080/TCP
 NodePort:       <unset> 32377/TCP
 Endpoints:      10.0.0.6:8080,10.0.1.6:8080,10.0.1.7:8080 + 2 more...
 Session Affinity:   None
 Events:         <none>
Serviceによって公開された外部IPアドレス(LoadBalancer Ingress)を記録しておいてください。
この例では、外部IPアドレスは104.198.205.71です。
また、PortおよびNodePortの値も控えてください。
この例では、Portは8080、NodePortは32377です。
 
先ほどの出力にて、Serviceにはいくつかのエンドポイントがあることを確認できます: 10.0.0.6:8080、
10.0.1.6:8080、10.0.1.7:8080、その他2つです。
これらはHello Worldアプリケーションが動作しているPodの内部IPアドレスです。
これらのPodのアドレスを確認するには、次のコマンドを実行します:
 kubectl get pods --output=wide
出力は次のようになります:
 NAME                         ...  IP         NODE
 hello-world-2895499144-1jaz9 ...  10.0.1.6   gke-cluster-1-default-pool-e0b8d269-1afc
 hello-world-2895499144-2e5uh ...  10.0.1.8   gke-cluster-1-default-pool-e0b8d269-1afc
 hello-world-2895499144-9m4h1 ...  10.0.0.6   gke-cluster-1-default-pool-e0b8d269-5v7a
 hello-world-2895499144-o4z13 ...  10.0.1.7   gke-cluster-1-default-pool-e0b8d269-1afc
 hello-world-2895499144-segjf ...  10.0.2.5   gke-cluster-1-default-pool-e0b8d269-cpuc
 
Hello Worldアプリケーションにアクセスするために、外部IPアドレス(LoadBalancer Ingress)を使用します:
 curl http://<external-ip>:<port>
ここで、<external-ip>はServiceの外部IPアドレス(LoadBalancer Ingress)で、
<port>はServiceの詳細出力におけるPortです。minikubeを使用している場合、minikube service my-serviceを実行することでHello Worldアプリケーションをブラウザで自動的に
開かれます。
正常なリクエストに対するレスポンスは、helloメッセージです:
 Hello Kubernetes!
 
 
クリーンアップ 
Serviceを削除する場合、次のコマンドを実行します:
kubectl delete services my-service
Deployment、ReplicaSet、およびHello Worldアプリケーションが動作しているPodを削除する場合、次のコマンドを実行します:
kubectl delete deployment hello-world
次の項目 
connecting applications with services にて詳細を学ぶことができます。
 
    
	
  
    
    
	
    
    
	4.2 - 例: Redisを使用したPHPのゲストブックアプリケーションのデプロイ 
    
	
このチュートリアルでは、KubernetesとDocker を使用した、シンプルなマルチティアのウェブアプリケーションのビルドとデプロイの方法を紹介します。この例は、以下のコンポーネントから構成されています。
目標 
Redisのマスターを起動する。 
Redisのスレーブを起動する。 
ゲストブックのフロントエンドを起動する。 
フロントエンドのServiceを公開して表示を確認する。 
クリーンアップする。 
 
始める前に 
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。
このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。
まだクラスターがない場合、minikube を使って作成するか、
以下のいずれかのKubernetesプレイグラウンドも使用できます:
 バージョンを確認するには次のコマンドを実行してください:  kubectl version.
Redisのマスターを起動する 
ゲストブックアプリケーションでは、データを保存するためにRedisを使用します。ゲストブックはRedisのマスターインスタンスにデータを書き込み、複数のRedisのスレーブインスタンスからデータを読み込みます。
RedisのマスターのDeploymentを作成する 
以下のマニフェストファイルは、シングルレプリカのRedisのマスターPodを実行するDeploymentコントローラーを指定しています。
    
    apiVersion :  apps/v1  # for versions before 1.9.0 use apps/v1beta2 
 kind :  Deployment
 metadata :
    name :  redis-master
    labels :
      app :  redis
 spec :
    selector :
      matchLabels :
        app :  redis
        role :  master
        tier :  backend
    replicas :  1 
    template :
      metadata :
        labels :
          app :  redis
          role :  master
          tier :  backend
      spec :
        containers :
        - name :  master
          image: registry.k8s.io/redis:e2e  # or just image :  redis
          resources :
            requests :
              cpu :  100m
              memory :  100Mi
          ports :
          - containerPort :  6379 
  
マニフェストファイルをダウンロードしたディレクトリ内で、ターミナルウィンドウを起動します。
 
redis-master-deployment.yamlファイルから、RedisのマスターのDeploymentを適用します。
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-master-deployment.yaml
  
Podのリストを問い合わせて、RedisのマスターのPodが実行中になっていることを確認します。
結果は次のようになるはずです。
NAME                            READY     STATUS    RESTARTS   AGE
 redis-master-1068406935-3lswp   1/1       Running   0           28s
  
次のコマンドを実行して、RedisのマスターのPodからログを表示します。
 
 
備考: POD-NAMEの部分を実際のPodの名前に書き換えてください。
RedisのマスターのServiceを作成する 
ゲストブックアプリケーションは、データを書き込むためにRedisのマスターと通信する必要があります。そのためには、Service を適用して、トラフィックをRedisのマスターのPodへプロキシしなければなりません。Serviceは、Podにアクセスするためのポリシーを指定します。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  redis-master
    labels :
      app :  redis
      role :  master
      tier :  backend
 spec :
    ports :
    - port :  6379 
      targetPort :  6379 
    selector :
      app :  redis
      role :  master
      tier :  backend
  
次のredis-master-service.yamlから、RedisのマスターのServiceを適用します。
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-master-service.yaml
  
Serviceのリストを問い合わせて、RedisのマスターのServiceが実行中になっていることを確認します。
The response should be similar to this:
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT( S)     AGE
 kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    1m
 redis-master   ClusterIP   10.0.0.151   <none>        6379/TCP   8s
  
 
備考: このマニフェストファイルは、redis-masterという名前のServiceを、前に定義したラベルにマッチする一連のラベル付きで作成します。これにより、ServiceはネットワークトラフィックをRedisのマスターのPodへとルーティングできるようになります。
Redisのスレーブを起動する 
Redisのマスターは1つのPodですが、レプリカのRedisのスレーブを追加することで、トラフィックの需要を満たすための高い可用性を持たせることができます。
RedisのスレーブのDeploymentを作成する 
Deploymentはマニフェストファイル内に書かれた設定に基づいてスケールします。ここでは、Deploymentオブジェクトは2つのレプリカを指定しています。
もし1つもレプリカが実行されていなければ、このDeploymentは2つのレプリカをコンテナクラスター上で起動します。逆に、もしすでに2つ以上のレプリカが実行されていれば、実行中のレプリカが2つになるようにスケールダウンします。
    
    apiVersion :  apps/v1  # for versions before 1.9.0 use apps/v1beta2 
 kind :  Deployment
 metadata :
    name :  redis-slave
    labels :
      app :  redis
 spec :
    selector :
      matchLabels :
        app :  redis
        role :  slave
        tier :  backend
    replicas :  2 
    template :
      metadata :
        labels :
          app :  redis
          role :  slave
          tier :  backend
      spec :
        containers :
        - name :  slave
          image :  gcr.io/google_samples/gb-redisslave:v3
          resources :
            requests :
              cpu :  100m
              memory :  100Mi
          env :
          - name :  GET_HOSTS_FROM
            value :  dns
            # Using `GET_HOSTS_FROM=dns` requires your cluster to 
            # provide a dns service. As of Kubernetes 1.3, DNS is a built-in 
            # service launched automatically. However, if the cluster you are using 
            # does not have a built-in DNS service, you can instead 
            # access an environment variable to find the master 
            # service's host. To do so, comment out the 'value: dns' line above, and 
            # uncomment the line below: 
            # value: env 
          ports :
          - containerPort :  6379 
  
redis-slave-deployment.yamlファイルから、RedisのスレーブのDeploymentを適用します。
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-slave-deployment.yaml
  
Podのリストを問い合わせて、RedisのスレーブのPodが実行中になっていることを確認します。
結果は次のようになるはずです。
NAME                            READY     STATUS              RESTARTS   AGE
 redis-master-1068406935-3lswp   1/1       Running             0           1m
 redis-slave-2005841000-fpvqc    0/1       ContainerCreating   0           6s
 redis-slave-2005841000-phfv9    0/1       ContainerCreating   0           6s
  
 
RedisのスレーブのServiceを作成する 
ゲストブックアプリケーションは、データを読み込むためにRedisのスレーブと通信する必要があります。Redisのスレーブが発見できるようにするためには、Serviceをセットアップする必要があります。Serviceは一連のPodに対する透過的なロードバランシングを提供します。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  redis-slave
    labels :
      app :  redis
      role :  slave
      tier :  backend
 spec :
    ports :
    - port :  6379 
    selector :
      app :  redis
      role :  slave
      tier :  backend
  
次のredis-slave-service.yamlファイルから、RedisのスレーブのServiceを適用します。
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-slave-service.yaml
  
Serviceのリストを問い合わせて、RedisのスレーブのServiceが実行中になっていることを確認します。
結果は次のようになるはずです。
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    2m
redis-master   ClusterIP   10.0.0.151   <none>        6379/TCP   1m
redis-slave    ClusterIP   10.0.0.223   <none>        6379/TCP   6s
 
 
ゲストブックのフロントエンドをセットアップして公開する 
ゲストブックアプリケーションには、HTTPリクエストをサーブするPHPで書かれたウェブフロントエンドがあります。このアプリケーションは、書き込みリクエストに対してはredis-master Serviceに、読み込みリクエストに対してはredis-slave Serviceに接続するように設定されています。
ゲストブックのフロントエンドのDeploymentを作成する 
    
    apiVersion :  apps/v1  # for versions before 1.9.0 use apps/v1beta2 
 kind :  Deployment
 metadata :
    name :  frontend
    labels :
      app :  guestbook
 spec :
    selector :
      matchLabels :
        app :  guestbook
        tier :  frontend
    replicas :  3 
    template :
      metadata :
        labels :
          app :  guestbook
          tier :  frontend
      spec :
        containers :
        - name :  php-redis
          image :  gcr.io/google-samples/gb-frontend:v4
          resources :
            requests :
              cpu :  100m
              memory :  100Mi
          env :
          - name :  GET_HOSTS_FROM
            value :  dns
            # Using `GET_HOSTS_FROM=dns` requires your cluster to 
            # provide a dns service. As of Kubernetes 1.3, DNS is a built-in 
            # service launched automatically. However, if the cluster you are using 
            # does not have a built-in DNS service, you can instead 
            # access an environment variable to find the master 
            # service's host. To do so, comment out the 'value: dns' line above, and 
            # uncomment the line below: 
            # value: env 
          ports :
          - containerPort :  80 
  
frontend-deployment.yamlファイルから、フロントエンドのDeploymentを適用します。
kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
  
Podのリストを問い合わせて、3つのフロントエンドのレプリカが実行中になっていることを確認します。
kubectl get pods -l app = guestbook -l tier = frontend
 結果は次のようになるはずです。
NAME                        READY     STATUS    RESTARTS   AGE
frontend-3823415956-dsvc5   1/1       Running   0          54s
frontend-3823415956-k22zn   1/1       Running   0          54s
frontend-3823415956-w9gbt   1/1       Running   0          54s
 
 
フロントエンドのServiceを作成する 
適用したredis-slaveおよびredis-master Serviceは、コンテナクラスター内部からのみアクセス可能です。これは、デフォルトのServiceのtypeがClusterIP であるためです。ClusterIPは、Serviceが指している一連のPodに対して1つのIPアドレスを提供します。このIPアドレスはクラスター内部からのみアクセスできます。
もしゲストの人にゲストブックにアクセスしてほしいのなら、フロントエンドServiceを外部から見えるように設定しなければなりません。そうすれば、クライアントはコンテナクラスターの外部からServiceにリクエストを送れるようになります。Minikubeでは、ServiceをNodePortでのみ公開できます。
備考: 一部のクラウドプロバイダーでは、Google Compute EngineやGoogle Kubernetes Engineなど、外部のロードバランサーをサポートしているものがあります。もしクラウドプロバイダーがロードバランサーをサポートしていて、それを使用したい場合は、type: NodePortという行を単に削除またはコメントアウトして、type: LoadBalancerのコメントアウトを外せば使用できます。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  frontend
    labels :
      app :  guestbook
      tier :  frontend
 spec :
    # comment or delete the following line if you want to use a LoadBalancer 
    type :  NodePort 
    # if your cluster supports it, uncomment the following to automatically create 
    # an external load-balanced IP for the frontend service. 
    # type: LoadBalancer 
    ports :
    - port :  80 
    selector :
      app :  guestbook
      tier :  frontend
  
frontend-service.yamlファイルから、フロントエンドのServiceを提供します。
kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
  
Serviceのリストを問い合わせて、フロントエンドのServiceが実行中であることを確認します。
結果は次のようになるはずです。
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
frontend       NodePort    10.0.0.112   <none>       80:31323/TCP   6s
kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP        4m
redis-master   ClusterIP   10.0.0.151   <none>        6379/TCP       2m
redis-slave    ClusterIP   10.0.0.223   <none>        6379/TCP       1m
 
 
フロントエンドのServiceをNodePort経由で表示する 
このアプリケーションをMinikubeやローカルのクラスターにデプロイした場合、ゲストブックを表示するためのIPアドレスを見つける必要があります。
次のコマンドを実行すると、フロントエンドServiceに対するIPアドレスを取得できます。
minikube service frontend --url
 結果は次のようになるはずです。
http://192.168.99.100:31323
 
IPアドレスをコピーして、ブラウザー上でページを読み込み、ゲストブックを表示しましょう。
 
 
フロントエンドのServiceをLoadBalancer経由で表示する 
もしfrontend-service.yamlマニフェストをtype: LoadBalancerでデプロイした場合、ゲストブックを表示するためのIPアドレスを見つける必要があります。
次のコマンドを実行すると、フロントエンドServiceに対するIPアドレスを取得できます。
kubectl get service frontend
 結果は次のようになるはずです。
NAME       TYPE        CLUSTER-IP      EXTERNAL-IP        PORT(S)        AGE
frontend   ClusterIP   10.51.242.136   109.197.92.229     80:32372/TCP   1m
 
外部IPアドレス(EXTERNAL-IP)をコピーして、ブラウザー上でページを読み込み、ゲストブックを表示しましょう。
 
 
ウェブフロントエンドをスケールする 
サーバーがDeploymentコントローラーを使用するServiceとして定義されているため、スケールアップやスケールダウンは簡単です。
次のコマンドを実行すると、フロントエンドのPodの数をスケールアップできます。
kubectl scale deployment frontend --replicas= 5 
  
Podのリストを問い合わせて、実行中のフロントエンドのPodの数を確認します。
結果は次のようになるはずです。
NAME                            READY     STATUS    RESTARTS   AGE
frontend-3823415956-70qj5       1/1       Running   0          5s
frontend-3823415956-dsvc5       1/1       Running   0          54m
frontend-3823415956-k22zn       1/1       Running   0          54m
frontend-3823415956-w9gbt       1/1       Running   0          54m
frontend-3823415956-x2pld       1/1       Running   0          5s
redis-master-1068406935-3lswp   1/1       Running   0          56m
redis-slave-2005841000-fpvqc    1/1       Running   0          55m
redis-slave-2005841000-phfv9    1/1       Running   0          55m
 
次のコマンドを実行すると、フロントエンドのPodの数をスケールダウンできます。
kubectl scale deployment frontend --replicas= 2 
  
Podのリストを問い合わせて、実行中のフロントエンドのPodの数を確認します。
結果は次のようになるはずです。
NAME                            READY     STATUS    RESTARTS   AGE
frontend-3823415956-k22zn       1/1       Running   0          1h
frontend-3823415956-w9gbt       1/1       Running   0          1h
redis-master-1068406935-3lswp   1/1       Running   0          1h
redis-slave-2005841000-fpvqc    1/1       Running   0          1h
redis-slave-2005841000-phfv9    1/1       Running   0          1h
 
 
クリーンアップ 
DeploymentとServiceを削除すると、実行中のPodも削除されます。ラベルを使用すると、複数のリソースを1つのコマンドで削除できます。
次のコマンドを実行すると、すべてのPod、Deployment、Serviceが削除されます。
kubectl delete deployment -l app = redis
 kubectl delete service -l app = redis
 kubectl delete deployment -l app = guestbook
 kubectl delete service -l app = guestbook
 結果は次のようになるはずです。
deployment.apps "redis-master" deleted
deployment.apps "redis-slave" deleted
service "redis-master" deleted
service "redis-slave" deleted
deployment.apps "frontend" deleted
service "frontend" deleted
 
Podのリストを問い合わせて、実行中のPodが存在しないことを確認します。
結果は次のようになるはずです。
No resources found.
 
 
次の項目 
 
    
	
  
    
    
	
    
    
	4.3 - 例: PHP / Redisを使用したゲストブックの例にロギングとメトリクスを追加する 
    
	
このチュートリアルは、Redisを使用したPHPのゲストブック のチュートリアルを前提に作られています。Elasticが開発したログ、メトリクス、ネットワークデータを転送するオープンソースの軽量データシッパーであるBeats を、ゲストブックと同じKubernetesクラスターにデプロイします。BeatsはElasticsearchに対してデータの収集、分析、インデックス作成を行うため、結果の運用情報をKibana上で表示・分析できるようになります。この例は、以下のコンポーネントから構成されます。
目標 
Redisを使用したPHPのゲストブックを起動する。 
kube-state-metricsをインストールする。 
KubernetesのSecretを作成する。 
Beatsをデプロイする。 
ログとメトリクスのダッシュボードを表示する。 
 
始める前に 
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。
このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。
まだクラスターがない場合、minikube を使って作成するか、
以下のいずれかのKubernetesプレイグラウンドも使用できます:
 バージョンを確認するには次のコマンドを実行してください:  kubectl version.
追加で以下の作業が必要です。
Redisを使用したPHPのゲストブックを起動する 
このチュートリアルは、Redisを使用したPHPのゲストブック のチュートリアルを前提に作られています。もしゲストブックアプリケーションが実行中なら、そのアプリケーションを監視できます。もしまだ実行中のアプリケーションがなければ、ゲストブックのデプロイの手順を行い、クリーンアップ のステップは実行しないでください。ゲストブックが起動したら、このページに戻ってきてください。
Cluster role bindingを追加する 
クラスターレベルのrole binding を作成して、kube-state-metricsとBeatsをクラスターレベルで(kube-system内に)デプロイできるようにします。
kubectl create clusterrolebinding cluster-admin-binding \
  = cluster-admin --user= <k8sのプロバイダーアカウントと紐付いたあなたのメールアドレス>
kube-state-metricsをインストールする 
Kubernetesのkube-state-metrics 
kube-state-metricsが起動しているか確認する 
kubectl get pods --namespace= kube-system | grep kube-state
 必要に応じてkube-state-metricsをインストールする 
git clone https://github.com/kubernetes/kube-state-metrics.git kube-state-metrics
 kubectl apply -f kube-state-metrics/examples/standard
 kubectl get pods --namespace= kube-system | grep kube-state-metrics
 kube-state-metricsがRunningかつreadyの状態になっていることを確認します。
kubectl get pods -n kube-system -l app.kubernetes.io/name= kube-state-metrics
 結果は次のようになります。
NAME                                 READY   STATUS    RESTARTS   AGE
 kube-state-metrics-89d656bf8-vdthm   1/1     Running     0           21s
 GitHubリポジトリのElasticの例をクローンする 
git clone https://github.com/elastic/examples.git
 これ以降のコマンドはexamples/beats-k8s-send-anywhereディレクトリ内のファイルを参照するため、カレントディレクトリを変更します。
cd  examples/beats-k8s-send-anywhere
KubernetesのSecretを作成する 
KubernetesのSecret とは、パスワード、トークン、または鍵などの小さなサイズの機密データを含んだオブジェクトのことです。このような機密情報はPodのspecやイメージの中に置くことも不可能ではありませんが、Secretオブジェクトの中に置くことで、情報の使用方法を適切に制御したり、誤って公開してしまうリスクを減らすことができます。
備考: ここでは2種類の手順を紹介します。1つはセルフマネージド な(自分のサーバーで実行中またはElastic Helm Chartを使用して構築された)ElasticsearchおよびKibanaのためのもので、もう1つはマネージドサービス のElastic CloudのElasticsearch Serviceのための別の手順です。このチュートリアルで使う種類のElasticsearchおよびKibanaのシステムのためのSecretだけを作成してください。
セルフマネージド 
Elastic Cloud上のElasticsearch Serviceに接続する場合は、マネージドサービス タブに切り替えてください。
クレデンシャルを設定する 
セルフマネージドのElasticsearchとKibanaへ接続する場合、KubernetesのSecretを作成するために編集するべきファイルは4つあります(セルフマネージドとは、事実上Elastic Cloud以外で実行されているElasticsearch Serviceを指します)。ファイルは次の4つです。
ELASTICSEARCH_HOSTS 
ELASTICSEARCH_PASSWORD 
ELASTICSEARCH_USERNAME 
KIBANA_HOST 
 
これらのファイルにElasticsearchクラスターとKibanaホストの情報を設定してください。ここでは例をいくつか示します(こちらの設定 
ELASTICSEARCH_HOSTS
Elastic Elasticsearch Helm Chartで作成したnodeGroupの場合。
[ "http://elasticsearch-master.default.svc.cluster.local:9200" ] 
 
Mac上で単一のElasticsearchノードが実行されており、BeatsがDocker for Macで実行中の場合。
[ "http://host.docker.internal:9200" ] 
 
2ノードのElasticsearchがVM上または物理ハードウェア上で実行中の場合。
[ "http://host1.example.com:9200" , "http://host2.example.com:9200" ] 
 
 
ELASTICSEARCH_HOSTSを編集します。
ELASTICSEARCH_PASSWORDパスワードだけを書きます。空白、クォート、<>などの文字は書かないでください。
<yoursecretpassword>
ELASTICSEARCH_PASSWORDを編集します。
vi ELASTICSEARCH_PASSWORD
 ELASTICSEARCH_USERNAMEユーザー名だけを書きます。空白、クォート、<>などの文字は書かないでください。
<Elasticsearchに追加するユーザー名>
ELASTICSEARCH_USERNAMEを編集します。
vi ELASTICSEARCH_USERNAME
 KIBANA_HOST
Elastic Kibana Helm Chartで作成したKibanaインスタンスが実行中の場合。defaultというサブドメインは、default Namespaceを指します。もしHelm Chartを別のNamespaceにデプロイした場合、サブドメインは異なります。
"kibana-kibana.default.svc.cluster.local:5601" 
 
Mac上でKibanaインスタンスが実行中で、BeatsがDocker for Macで実行中の場合。
"host.docker.internal:5601" 
 
2つのElasticsearchノードが、VMまたは物理ハードウェア上で実行中の場合。
 
 
KIBANA_HOSTを編集します。
KubernetesのSecretを作成する 
次のコマンドを実行すると、KubernetesのシステムレベルのNamespace(kube-system)に、たった今編集したファイルを元にSecretが作成されます。
kubectl create secret generic dynamic-logging \
  --from-file=./ELASTICSEARCH_HOSTS \
  --from-file=./ELASTICSEARCH_PASSWORD \
  --from-file=./ELASTICSEARCH_USERNAME \
  --from-file=./KIBANA_HOST \
  --namespace=kube-system
 
  
マネージドサービス 
このタブは、Elastic Cloud上のElasticsearch Serviceの場合のみ必要です。もしセルフマネージドのElasticsearchとKibanaのDeployment向けにSecretをすでに作成した場合、Beatsをデプロイする に進んでください。
クレデンシャルを設定する 
Elastic Cloud上のマネージドElasticsearch Serviceに接続する場合、KubernetesのSecretを作成するために編集する必要があるのは、次の2つのファイルです。
ELASTIC_CLOUD_AUTH 
ELASTIC_CLOUD_ID 
 
Deploymentを作成するときに、Elasticsearch Serviceのコンソールから提供された情報を設定してください。以下に例を示します。
ELASTIC_CLOUD_ID 
devk8s:ABC123def456ghi789jkl123mno456pqr789stu123vwx456yza789bcd012efg345hijj678klm901nop345zEwOTJjMTc5YWQ0YzQ5OThlN2U5MjAwYTg4NTIzZQ== 
 ELASTIC_CLOUD_AUTH 
ユーザー名、コロン(:)、パスワードだけを書きます。空白やクォートは書かないでください。
elastic:VFxJJf9Tjwer90wnfTghsn8w
 必要なファイルを編集する 
vi ELASTIC_CLOUD_ID
 vi ELASTIC_CLOUD_AUTH
 KubernetesのSecretを作成する 
次のコマンドを実行すると、KubernetesのシステムレベルのNamespace(kube-system)に、たった今編集したファイルを元にSecretが作成されます。
kubectl create secret generic dynamic-logging \
  --from-file=./ELASTIC_CLOUD_ID \
  --from-file=./ELASTIC_CLOUD_AUTH \
  --namespace=kube-system
 Beatsをデプロイする 
マニフェストファイルはBeatごとに提供されます。これらのマニフェストファイルは、上で作成したSecretを使用して、BeatsをElasticsearchおよびKibanaサーバーに接続するように設定します。
Filebeatについて 
Filebeatは、Kubernetesのノードと、ノード上で実行している各Pod内のコンテナから、ログを収集します。FilebeatはDaemonSet としてデプロイされます。FilebeatはKubernetesクラスター上で実行されているアプリケーションを自動検出することもできます。起動時にFilebeatは既存のコンテナをスキャンし、それらに対して適切な設定を立ち上げ、その後、新しいstart/stopイベントを監視します。
Filebeatが、ゲストブックアプリケーションでデプロイしたRedisコンテナからRedisのログを特定・解析できるように自動検出を設定する例を示します。この設定はfilebeat-kubernetes.yamlファイル内にあります。
- condition.contains :
       kubernetes.labels.app :  redis
    config :
      - module :  redis
        log :
          input :
            type :  docker
            containers.ids :
              - ${data.kubernetes.container.id}
        slowlog :
          enabled :  true 
          var.hosts :  ["${data.host}:${data.port}" ]
 この設定により、Filebeatは、appラベルにredisという文字列が含まれるコンテナを検出したときにredis Filebeatモジュールを適用するようになります。redisモジュールには、input typeとしてdockerを使用することで(このRedisコンテナの標準出力のストリームと関連付けられた、Kubernetesノード上のファイルを読み取ることで)コンテナからlogストリームを収集する機能があります。さらに、このモジュールには、コンテナのメタデータとして提供された適切なPodのホストとポートと接続することにより、Redisのslowlogエントリーを収集する機能もあります。
Filebeatをデプロイする 
kubectl create -f filebeat-kubernetes.yaml
 検証する 
kubectl get pods -n kube-system -l k8s-app= filebeat-dynamic
 Metricbeatについて 
Metricbeatの自動検出はFilebeatと同じ方法で設定します。以下にMetricbeatにおけるRedisコンテナの自動検出の設定を示します。この設定はmetricbeat-kubernetes.yamlファイル内にあります。
- condition.equals :
       kubernetes.labels.tier :  backend
    config :
      - module :  redis
        metricsets :  ["info" ,  "keyspace" ]
        period :  10s
 
        # Redis hosts 
        hosts :  ["${data.host}:${data.port}" ]
 この設定により、Metricbeatは、tierラベルにbackendという文字列が含まれるコンテナを検出したときにredis Metricbeatモジュールを適用するようになります。redisモジュールには、コンテナのメタデータとして提供された適切なPodのホストとポートと接続することにより、コンテナからinfoおよびkeyspaceメトリクスを収集する機能があります。
Metricbeatをデプロイする 
kubectl create -f metricbeat-kubernetes.yaml
 検証する 
kubectl get pods -n kube-system -l k8s-app= metricbeat
 Packetbeatについて 
Packetbeatの設定は、FilebeatやMetricbeatとは異なります。コンテナのラベルに対するパターンマッチを指定する代わりに、関連するプロトコルとポート番号に基づいた設定を書きます。以下に示すのは、ポート番号のサブセットです。
備考: サービスを標準ポート以外で実行している場合、そのポート番号をfilebeat.yaml内の適切なtypeに追加し、PacketbeatのDaemonSetを削除・再作成してください。
packetbeat.interfaces.device :  any
 
 packetbeat.protocols :
 type :  dns
    ports :  [53 ]
    include_authorities :  true 
    include_additionals :  true 
 
 type :  http
    ports :  [80 ,  8000 ,  8080 ,  9200 ]
 
 type :  mysql
    ports :  [3306 ]
 
 type :  redis
    ports :  [6379 ]
 
 packetbeat.flows :
    timeout :  30s
    period :  10s
 Packetbeatをデプロイする 
kubectl create -f packetbeat-kubernetes.yaml
 検証する 
kubectl get pods -n kube-system -l k8s-app= packetbeat-dynamic
 Kibanaで表示する 
ブラウザでKibanaを開き、Dashboard アプリケーションを開きます。検索バーでKubernetesと入力して、KubernetesのためのMetricbeatダッシュボードを開きます。このダッシュボードでは、NodeやDeploymentなどの状態のレポートが表示されます。
DashboardページでPacketbeatと検索し、Packetbeat overviewを表示します。
同様に、ApacheおよびRedisのためのDashboardを表示します。それぞれに対してログとメトリクスのDashboardが表示されます。Apache Metricbeat dashboardには何も表示されていないはずです。Apache Filebeat dashboardを表示して、ページの最下部までスクロールしてApacheのエラーログを確認します。ログを読むと、Apacheのメトリクスが表示されない理由が分かります。
Metricbeatを有効にしてApacheのメトリクスを取得するには、mod-status設定ファイルを含んだConfigMapを追加してゲストブックを再デプロイすることで、server-statusを有効にします。
Deploymentをスケールして新しいPodが監視されるのを確認する 
存在するDeploymentを一覧します。
出力は次のようになります。
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
 frontend        3/3     3             3            3h27m
 redis-master    1/1     1             1            3h27m
 redis-slave     2/2     2             2            3h27m
 frontendのPodを2つにスケールダウンします。
kubectl scale --replicas= 2  deployment/frontend
 出力は次のようになります。
deployment.extensions/frontend scaled
 frontendのPodを再び3つにスケールアップします。
kubectl scale --replicas= 3  deployment/frontend
 Kibana上で変更を表示する 
スクリーンショットを確認し、指定されたフィルターを追加して、ビューにカラムを追加します。赤い枠の右下を見ると、ScalingReplicaSetというエントリーが確認できます。そこからリストを上に見てゆくと、イメージのpull、ボリュームのマウント、Podのスタートなどのイベントが確認できます。
クリーンアップ 
DeploymentとServiceを削除すると、実行中のすべてのPodも削除されます。ラベルを使って複数のリソースを1つのコマンドで削除します。
次のコマンドを実行して、すべてのPod、Deployment、Serviceを削除します。
kubectl delete deployment -l app = redis
 kubectl delete service -l app = redis
 kubectl delete deployment -l app = guestbook
 kubectl delete service -l app = guestbook
 kubectl delete -f filebeat-kubernetes.yaml
 kubectl delete -f metricbeat-kubernetes.yaml
 kubectl delete -f packetbeat-kubernetes.yaml
 kubectl delete secret dynamic-logging -n kube-system
  
Podの一覧を問い合わせて、実行中のPodがなくなったことを確認します。
結果は次のようになるはずです。
No resources found.
 
 
次の項目 
 
    
	
  
    
	
  
    
    
	
    
    
	
5 - セキュリティ 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	5.1 - クラスターレベルでのPodセキュリティの標準の適用 
    
	
Note 
    このチュートリアルは、新しいクラスターにのみ適用されます。
Podセキュリティアドミッション(PSA)は、ベータへ進み 、v1.23以降でデフォルトで有効になっています。
Podセキュリティアドミッションは、Podが作成される際に、Podセキュリティの標準 の適用の認可を制御するものです。
このチュートリアルでは、クラスター内の全ての名前空間に標準設定を適用することで、クラスターレベルでbaseline Podセキュリティの標準を強制する方法を示します。
Podセキュリティの標準を特定の名前空間に適用するには、名前空間レベルでのPodセキュリティの標準の適用 を参照してください。
v1.34以外のKubernetesバージョンを実行している場合は、そのバージョンのドキュメントを確認してください。
始める前に 
ワークステーションに以下をインストールしてください:
このチュートリアルでは、完全な制御下にあるKubernetesクラスターの何を設定できるかをデモンストレーションします。
コントロールプレーンを設定できない管理されたクラスターのPodセキュリティアドミッションに対しての設定方法を知りたいのであれば、名前空間レベルでのPodセキュリティの標準の適用 を参照してください。
適用する正しいPodセキュリティの標準の選択 
Podのセキュリティアドミッション は、以下のモードでビルトインのPodセキュリティの標準 の適用を促します: enforce、audit、warn。
設定に最適なPodセキュリティの標準を選択するにあたって助けになる情報を収集するために、以下を行ってください:
Podセキュリティの標準を適用していないクラスターを作成します:
kind create cluster --name psa-wo-cluster-pss
 出力は次のようになります:
Creating cluster "psa-wo-cluster-pss" ...
✓ Ensuring node image (kindest/node:v1.34.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-psa-wo-cluster-pss"
You can now use your cluster with:
kubectl cluster-info --context kind-psa-wo-cluster-pss
Thanks for using kind! 😊
 
kubectl contextを新しいクラスターにセットします:
kubectl cluster-info --context kind-psa-wo-cluster-pss
 出力は次のようになります:
Kubernetes control plane is running at https://127.0.0.1:61350
CoreDNS is running at https://127.0.0.1:61350/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
 
クラスター内の名前空間の一覧を取得します:
出力は次のようになります:
NAME                 STATUS   AGE
default              Active   9m30s
kube-node-lease      Active   9m32s
kube-public          Active   9m32s
kube-system          Active   9m32s
local-path-storage   Active   9m26s
 
異なるPodセキュリティの標準が適用されたときに何が起きるかを理解するために、-dry-run=serverを使います:
privileged
kubectl label --dry-run= server --overwrite ns --all \
  = privileged
出力は次のようになります:
namespace/default labeled
namespace/kube-node-lease labeled
namespace/kube-public labeled
namespace/kube-system labeled
namespace/local-path-storage labeled
 
baseline
kubectl label --dry-run= server --overwrite ns --all \
  = baseline
出力は次のようになります:
namespace/default labeled
namespace/kube-node-lease labeled
namespace/kube-public labeled
Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "baseline:latest"
Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes
Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes
Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged
namespace/kube-system labeled
namespace/local-path-storage labeled
 
restricted
kubectl label --dry-run= server --overwrite ns --all \
  = restricted
出力は次のようになります:
namespace/default labeled
namespace/kube-node-lease labeled
namespace/kube-public labeled
Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "restricted:latest"
Warning: coredns-7bb9c7b568-hsptc (and 1 other pod): unrestricted capabilities, runAsNonRoot != true, seccompProfile
Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true
Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
namespace/kube-system labeled
Warning: existing pods in namespace "local-path-storage" violate the new PodSecurity enforce level "restricted:latest"
Warning: local-path-provisioner-d6d9f7ffc-lw9lh: allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfile
namespace/local-path-storage labeled
 
 
 
 
この出力から、privileged Podセキュリティの標準を適用すると、名前空間のどれにも警告が示されないことに気付くでしょう。
これに対し、baselineとrestrictの標準ではどちらも、とりわけkube-system名前空間に対して警告が示されています。
モード、バージョン、標準のセット 
このセクションでは、latestバージョンに以下のPodセキュリティの標準を適用します:
enforceモードでbaseline標準。warnおよびauditモードでrestricted標準。 
baseline Podセキュリティの標準は、免除リストを短く保てて、かつ既知の特権昇格を防ぐような、利便性のある中庸を提供します。
加えて、kube-system内の失敗からPodを守るために、適用されるPodセキュリティの標準の対象から名前空間を免除します。
環境にPodセキュリティアドミッションを実装する際には、以下の点を考慮してください:
クラスターに適用されるリスク状況に基づくと、restrictedのようにより厳格なPodセキュリティの標準のほうが、より良い選択肢かもしれません。
 
kube-system名前空間の免除は、Podがその名前空間でprivilegedとして実行するのを許容することになります。
実世界で使うにあたっては、以下の最小権限の原則に従ってkube-systemへのアクセスを制限する厳格なRBACポリシーを適用することを、Kubernetesプロジェクトは強く推奨します。
上記の標準を実装するには、次のようにします:
 
目的のPodセキュリティの標準を実装するために、Podセキュリティアドミッションコントローラーで利用可能な設定ファイルを作成します:
mkdir -p /tmp/pss
cat <<EOF > /tmp/pss/cluster-level-pss.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "latest"
      audit: "restricted"
      audit-version: "latest"
      warn: "restricted"
      warn-version: "latest"
    exemptions:
      usernames: []
      runtimeClasses: []
      namespaces: [kube-system]
EOF
備考: pod-security.admission.config.k8s.io/v1設定はv1.25+での対応です。
v1.23とv1.24では
v1beta1 を使用してください。
v1.22では
v1alpha1 を使用してください。
 
クラスターの作成中にこのファイルを取り込むAPIサーバーを設定します:
cat <<EOF > /tmp/pss/cluster-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
        extraArgs:
          admission-control-config-file: /etc/config/cluster-level-pss.yaml
        extraVolumes:
          - name: accf
            hostPath: /etc/config
            mountPath: /etc/config
            readOnly: false
            pathType: "DirectoryOrCreate"
  extraMounts:
  - hostPath: /tmp/pss
    containerPath: /etc/config
    # optional: if set, the mount is read-only.
    # default false
    readOnly: false
    # optional: if set, the mount needs SELinux relabeling.
    # default false
    selinuxRelabel: false
    # optional: set propagation mode (None, HostToContainer or Bidirectional)
    # see https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation
    # default None
    propagation: None
EOF
備考: macOSでDocker Desktopとkind を利用している場合は、Preferences > Resources > File Sharing のメニュー項目からShared Directoryとして/tmpを追加できます。
 
目的のPodセキュリティの標準を適用するために、Podセキュリティアドミッションを使うクラスターを作成します:
kind create cluster --name psa-with-cluster-pss --config /tmp/pss/cluster-config.yaml
 出力は次のようになります:
Creating cluster "psa-with-cluster-pss" ...
 ✓ Ensuring node image (kindest/node:v1.34.0) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-psa-with-cluster-pss"
You can now use your cluster with:
kubectl cluster-info --context kind-psa-with-cluster-pss
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
 
kubectlをこのクラスターに向けます:
kubectl cluster-info --context kind-psa-with-cluster-pss
 出力は次のようになります:
Kubernetes control plane is running at https://127.0.0.1:63855
CoreDNS is running at https://127.0.0.1:63855/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
 
デフォルトの名前空間にPodを作成します:
kubectl apply -f https://k8s.io/examples/security/example-baseline-pod.yaml
 Podは正常に開始されますが、出力には警告が含まれます:
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/nginx created
 
 
後片付け 
では、上記で作成したクラスターを、以下のコマンドを実行して削除します:
kind delete cluster --name psa-with-cluster-pss
 kind delete cluster --name psa-wo-cluster-pss
 次の項目 
 
    
	
  
    
    
	
    
    
	5.2 - 名前空間レベルでのPodセキュリティの標準の適用 
    
	
Note 
    このチュートリアルは、新しいクラスターにのみ適用されます。
Podセキュリティアドミッション(PSA)は、ベータへ進み 、v1.23以降でデフォルトで有効になっています。
Podセキュリティアドミッションは、Podが作成される際に、Podセキュリティの標準 の適用の認可を制御するものです。
このチュートリアルでは、一度に1つの名前空間でbaseline Podセキュリティ標準を強制します。
Podセキュリティの標準を複数の名前空間に一度にクラスターレベルで適用することもできます。やり方についてはクラスターレベルでのPodセキュリティの標準の適用 を参照してください。
始める前に 
ワークステーションに以下をインストールしてください:
クラスターの作成 
以下のようにKinDクラスターを作成します。
kind create cluster --name psa-ns-level
 出力は次のようになります:
Creating cluster "psa-ns-level" ...
 ✓ Ensuring node image (kindest/node:v1.34.0) 🖼 
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-psa-ns-level"
You can now use your cluster with:
kubectl cluster-info --context kind-psa-ns-level
Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/
 
kubectl のコンテキストを新しいクラスターにセットします:
kubectl cluster-info --context kind-psa-ns-level
 出力は次のようになります:
Kubernetes control plane is running at https://127.0.0.1:50996
CoreDNS is running at https://127.0.0.1:50996/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
 
 
名前空間の作成 
exampleと呼ぶ新しい名前空間を作成します:
kubectl create ns example
 出力は次のようになります:
namespace/example created
名前空間へのPodセキュリティの標準チェックの有効化 
ビルトインのPod Security Admissionでサポートされているラベルを使って、この名前空間のPodセキュリティの標準を有効にします。
このステップでは、baseline  Podセキュリティの標準の最新バージョンに合わないPodについて警告するチェックを設定します。
kubectl label --overwrite ns example \
  = baseline \
 = latest
 
ラベルを使って、任意の名前空間に対して複数のPodセキュリティの標準チェックを設定できます。
以下のコマンドは、baseline Podセキュリティの標準をenforce(強制)としますが、restricted Podセキュリティの標準には最新バージョンに準じてwarn(警告)およびaudit(監査)とします(デフォルト値)。
kubectl label --overwrite ns example \
  = baseline \
 = latest \
 = restricted \
 = latest \
 = restricted \
 = latest
 
 
Podセキュリティの標準の強制の実証 
example名前空間内にbaseline Podを作成します:
kubectl apply -n example -f https://k8s.io/examples/security/example-baseline-pod.yaml
 Podは正常に起動しますが、出力には警告が含まれています。例えば:
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/nginx created
 
default名前空間内にbaseline Podを作成します:
kubectl apply -n default -f https://k8s.io/examples/security/example-baseline-pod.yaml
 出力は次のようになります:
pod/nginx created
 
 
example名前空間にだけ、Podセキュリティの標準のenforceと警告の設定が適用されました。
default名前空間内では、警告なしに同じPodを作成できました。
後片付け 
では、上記で作成したクラスターを、以下のコマンドを実行して削除します:
kind delete cluster --name psa-ns-level
 次の項目 
 
    
	
  
    
    
	
    
    
	5.3 - AppArmorを使用してコンテナのリソースへのアクセスを制限する 
    
	
  
              FEATURE STATE:  
              Kubernetes v1.31 [stable] (enabled by default: true)
            
このページでは、ノードでAppArmorプロファイルを読み込み、それらのプロファイルをPodに適用する方法を説明します。
AppArmorを使用してPodを制限するKubernetesの仕組みについて詳しく知りたい場合は、PodとコンテナのためのLinuxカーネルのセキュリティ制約 をご覧ください。
目標 
プロファイルをノードに読み込む方法の例を見る 
Pod上でプロファイルを強制する方法を学ぶ 
プロファイルが読み込まれたかを確認する方法を学ぶ 
プロファイルに違反した場合に何が起こるのかを見る 
プロファイルが読み込めなかった場合に何が起こるのかを見る 
 
始める前に 
AppArmorはカーネルモジュールおよびKubernetesのオプション機能です。
そのため、先に進む前にノード上でAppArmorがサポートされていることを確認してください:
AppArmorカーネルモジュールが有効であること。
LinuxカーネルがAppArmorプロファイルを強制するためには、AppArmorカーネルモジュールのインストールと有効化が必須です。
UbuntuやSUSEなどのディストリビューションではデフォルトで有効化されますが、他の多くのディストリビューションでのサポートはオプションです。
モジュールが有効になっているかどうかを確認するためには、/sys/module/apparmor/parameters/enabledファイルを確認します:
cat /sys/module/apparmor/parameters/enabled
 Y
 Kubeletは、AppArmorが明示的に設定されたPodを受け入れる前にホスト上でAppArmorが有効になっているかどうかを検証します。
 
コンテナランタイムがAppArmorをサポートしていること。
Kubernetesがサポートするcontainerd やCRI-O などのすべての一般的なコンテナランタイムは、AppArmorをサポートしています。
関連するランタイムのドキュメントを参照して、クラスターがAppArmorを利用するための要求を満たしているかどうかを検証してください。
 
プロファイルが読み込まれていること。
AppArmorがPodに適用されるのは、各コンテナが実行するAppArmorプロファイルを指定したときです。
もし指定されたプロファイルがまだカーネルに読み込まれていなければ、KubeletはPodを拒否します。
どのプロファイルがノードに読み込まれているのかを確かめるには、/sys/kernel/security/apparmor/profilesを確認します。
例えば:
ssh gke-test-default-pool-239f5d02-gyn2 "sudo cat /sys/kernel/security/apparmor/profiles | sort" 
 apparmor-test-deny-write (enforce)
apparmor-test-audit-write (enforce)
docker-default (enforce)
k8s-nginx (enforce)
ノード上でのプロファイルの読み込みの詳細については、プロファイルを使用したノードのセットアップ を参照してください。
 
 
Podをセキュアにする 
備考: Kubernetes v1.30より前では、AppArmorはアノテーションを通じて指定されていました。
この非推奨となったAPIに関するドキュメントを表示するには、ドキュメントのバージョンセレクターを使用してください。
AppArmorプロファイルは、Podレベルまたはコンテナレベルで指定することができます。
コンテナのAppArmorプロファイルは、Podのプロファイルよりも優先されます。
securityContext :
    appArmorProfile :
      type :  <profile_type>
 ここで、<profile_type>には次の値のうち1つを指定します:
RuntimeDefault: ランタイムのデフォルトのプロファイルを適用します。Localhost: ホストに読み込まれたプロファイルを適用します(下記を参照してください)。unconfined: AppArmorなしで実行します。 
AppArmorプロファイルAPIの詳細については、AppArmorによる制限の指定 を参照してください。
プロファイルが適用されたかどうかを確認するには、proc attrを調べることでコンテナのルートプロセスが正しいプロファイルで実行されているかどうかを確認します:
kubectl exec  <pod_name> -- cat /proc/1/attr/current
 出力は以下のようになるはずです:
cri-containerd.apparmor.d (enforce)
例 
この例は、クラスターがすでにAppArmorのサポート付きでセットアップ済みであることを前提としています。 
まず、使用したいプロファイルをノード上に読み込む必要があります。
このプロファイルは、すべてのファイル書き込みを拒否します:
#include <tunables/global>
profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>
  file,
  # Deny all file writes.
  deny /** w,
}
Podがどのノードにスケジュールされるかは予測できないため、プロファイルはすべてのノードに読み込ませる必要があります。
この例では、単純にSSHを使ってプロファイルをインストールしますが、プロファイルを使用したノードのセットアップ では、他のアプローチについて議論しています。
# この例では、ノード名がホスト名と一致し、SSHで到達可能であることを前提としています。 
NODES =( $(  kubectl get node -o jsonpath = '{.items[*].status.addresses[?(.type == "Hostname")].address}'  ) ) 
 for  NODE in ${ NODES [*]} ; do  ssh $NODE  'sudo apparmor_parser -q <<EOF
 #include <tunables/global>
 
 profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
   #include <abstractions/base>
 
   file,
 
   # Deny all file writes.
   deny /** w,
 }
 EOF' 
done 
次に、deny-writeプロファイルを使用した単純な"Hello AppArmor" Podを実行します。
    
    apiVersion :  v1
 kind :  Pod
 metadata :
    name :  hello-apparmor
 spec :
    securityContext :
      appArmorProfile :
        type :  Localhost
        localhostProfile :  k8s-apparmor-example-deny-write
    containers :
    - name :  hello
      image :  busybox:1.28
      command :  [  "sh" ,  "-c" ,  "echo 'Hello AppArmor!' && sleep 1h"   ]
  
kubectl create -f hello-apparmor.yaml
 /proc/1/attr/currentを確認することで、コンテナがこのプロファイルで実際に実行されていることを検証できます:
kubectl exec  hello-apparmor -- cat /proc/1/attr/current
 出力は以下のようになるはずです:
k8s-apparmor-example-deny-write (enforce)
最後に、ファイルへの書き込みを行おうとすることで、プロファイルに違反すると何が起こるか見てみましょう:
kubectl exec  hello-apparmor -- touch /tmp/test
 touch: /tmp/test: Permission denied
error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1
まとめとして、読み込まれていないプロファイルを指定しようとするとどうなるのか見てみましょう:
kubectl create -f /dev/stdin <<EOF
  apiVersion: v1
 kind: Pod
 metadata:
   name: hello-apparmor-2
 spec:
   securityContext:
     appArmorProfile:
       type: Localhost
       localhostProfile: k8s-apparmor-example-allow-write
   containers:
   - name: hello
     image: busybox:1.28
     command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
 EOF 
pod/hello-apparmor-2 created
Podは正常に作成されましたが、さらに調査すると、PodがPendingで止まっていることがわかります:
kubectl describe pod hello-apparmor-2
 Name:          hello-apparmor-2
Namespace:     default
Node:          gke-test-default-pool-239f5d02-x1kf/10.128.0.27
Start Time:    Tue, 30 Aug 2016 17:58:56 -0700
Labels:        <none>
Annotations:   container.apparmor.security.beta.kubernetes.io/hello=localhost/k8s-apparmor-example-allow-write
Status:        Pending
... 
Events:
  Type     Reason     Age              From               Message
  ----     ------     ----             ----               -------
  Normal   Scheduled  10s              default-scheduler  Successfully assigned default/hello-apparmor to gke-test-default-pool-239f5d02-x1kf
  Normal   Pulled     8s               kubelet            Successfully pulled image "busybox:1.28" in 370.157088ms (370.172701ms including waiting)
  Normal   Pulling    7s (x2 over 9s)  kubelet            Pulling image "busybox:1.28"
  Warning  Failed     7s (x2 over 8s)  kubelet            Error: failed to get container spec opts: failed to generate apparmor spec opts: apparmor profile not found k8s-apparmor-example-allow-write
  Normal   Pulled     7s               kubelet            Successfully pulled image "busybox:1.28" in 90.980331ms (91.005869ms including waiting)
イベントにはエラーメッセージとその理由が表示されます。
具体的な文言はランタイムによって異なります:
  Warning  Failed     7s (x2 over 8s)  kubelet            Error: failed to get container spec opts: failed to generate apparmor spec opts: apparmor profile not found 
管理 
プロファイルを使用したノードのセットアップ 
Kubernetes 1.34は、AppArmorプロファイルをノードに読み込むネイティブの仕組みは提供していません。
プロファイルは、カスタムインフラストラクチャーやKubernetes Security Profiles Operator などのツールを通じて読み込むことができます。
スケジューラーはどのプロファイルがどのノードに読み込まれているのかがわからないため、すべてのプロファイルがすべてのノードに読み込まれていなければなりません。
もう1つのアプローチとしては、各プロファイル(あるいはプロファイルのクラス)ごとにノードラベルを追加し、ノードセレクター を用いてPodが必要なプロファイルを読み込んだノードで実行されるようにする方法もあります。
Profilesの作成 
AppArmorプロファイルを正しく指定するのはやっかいな作業です。
幸い、その作業を補助するツールがいくつかあります:
aa-genprofおよびaa-logprofは、アプリケーションの動作とログを監視し、実行されるアクションを許可することで、プロファイルのルールを生成します。
詳しい説明については、AppArmor documentation を参照してください。bane は、Docker向けのAppArmorプロファイル・ジェネレーターです。簡略化されたプロファイル言語を使用しています。 
AppArmorに関する問題をデバッグするには、システムログを確認して、特に何が拒否されたのかを確認できます。
AppArmorのログはdmesgにverboseメッセージを送り、エラーは通常システムログまたはjournalctlで確認できます。
詳しい情報は、AppArmor failures で提供されています。
AppArmorによる制限の指定 
注意: Kubernetes v1.30より前では、AppArmorはアノテーションを通じて指定されていました。
この非推奨となったAPIに関するドキュメントを表示するには、ドキュメントのバージョンセレクターを使用してください。
セキュリティコンテキスト内のAppArmorプロファイル 
appArmorProfileは、コンテナのsecurityContextまたはPodのsecurityContextのいずれかで指定できます。
プロファイルがPodレベルで設定されている場合、Pod内のすべてのコンテナ(initコンテナ、サイドカーコンテナ、およびエフェメラルコンテナを含む)のデフォルトのプロファイルとして使用されます。
Podとコンテナの両方でAppArmorプロファイルが設定されている場合は、コンテナのプロファイルが使用されます。
AppArmorプロファイルには2つのフィールドがあります:
type (必須)  - 適用されるAppArmorプロファイルの種類を示します。
有効なオプションは以下の通りです:
Localhostノードに事前に読み込まれているプロファイル(localhostProfileで指定します)。 
RuntimeDefaultコンテナランタイムのデフォルトのプロファイル。 
UnconfinedAppArmorによる制限を適用しません。 
 
localhostProfile - ノード上に読み込まれている、使用すべきプロファイルの名前。
このプロファイルは、ノード上であらかじめ設定されている必要があります。
このオプションは、typeがLocalhostの場合にのみ指定する必要があります。
次の項目 
追加のリソースとしては以下のものがあります:
 
    
	
  
    
    
	
    
    
	5.4 - seccompを使用してコンテナのシステムコールを制限する 
    
	
  
      FEATURE STATE: 
      Kubernetes v1.19 [stable]
    
  
seccomp(SECure COMPuting mode)はLinuxカーネル2.6.12以降の機能です。
この機能を用いると、ユーザー空間からカーネルに対して発行できるシステムコールを制限することで、プロセス権限のサンドボックスを構築することができます。
Kubernetesでは、ノード 上で読み込んだseccompプロファイルをPodやコンテナに対して自動で適用することができます。
あなたのワークロードが必要とする権限を特定するのは、実際には難しい作業になるかもしれません。
このチュートリアルでは、ローカルのKubernetesクラスターでseccompプロファイルを読み込むための方法を説明し、seccompプロファイルのPodへの適用方法について学んだ上で、コンテナプロセスに対して必要な権限のみを付与するためのseccompプロファイルを作成する方法を概観していきます。
目標 
ノードでseccompプロファイルを読み込む方法を学ぶ。 
seccompプロファイルをコンテナに適用する方法を学ぶ。 
コンテナプロセスが生成するシステムコールの監査出力を確認する。 
seccompプロファイルが指定されない場合の挙動を確認する。 
seccompプロファイルの違反を確認する。 
きめ細やかなseccompプロファイルの作成方法を学ぶ。 
コンテナランタイムの標準seccompプロファイルを適用する方法を学ぶ。 
 
始める前に 
このチュートリアルのステップを完了するためには、kind とkubectl をインストールしておく必要があります。
このチュートリアルで利用するコマンドは、Docker をコンテナランタイムとして利用していることを前提としています。
(kindが作成するクラスターは内部的に異なるコンテナランタイムを利用する可能性があります)。
Podman を使うこともできますが、チュートリアルを完了するためには、所定の手順 に従う必要があります。
このチュートリアルでは、現時点(v1.25以降)でのベータ機能を利用する設定例をいくつか示しますが、その他の例についてはGAなseccomp関連機能を用いています。
利用するKubernetesバージョンを対象としたクラスターの正しい設定 がなされていることを確認してください。
チュートリアル内では、サンプルをダウンロードするためにcurlを利用します。
この手順については、ほかの好きなツールを用いて実施してもかまいません。
備考: securityContextにprivileged: trueが設定されているContainerに対しては、seccompプロファイルを適用することができません。
特権コンテナは常にUnconfinedな状態で動作します。
 
サンプルのseccompプロファイルをダウンロードする 
プロファイルの内容については後で解説しますので、まずはクラスターで読み込むためのseccompプロファイルをprofiles/ディレクトリ内にダウンロードしましょう。
    
    {
     "defaultAction" : "SCMP_ACT_LOG" 
 }  
 
  
    
    {
     "defaultAction" : "SCMP_ACT_ERRNO" 
 }  
 
  
    
    {
     "defaultAction" : "SCMP_ACT_ERRNO" ,
     "architectures" : [
         "SCMP_ARCH_X86_64" ,
         "SCMP_ARCH_X86" ,
         "SCMP_ARCH_X32" 
     ],
     "syscalls" : [
         {
             "names" : [
                 "accept4" ,
                 "epoll_wait" ,
                 "pselect6" ,
                 "futex" ,
                 "madvise" ,
                 "epoll_ctl" ,
                 "getsockname" ,
                 "setsockopt" ,
                 "vfork" ,
                 "mmap" ,
                 "read" ,
                 "write" ,
                 "close" ,
                 "arch_prctl" ,
                 "sched_getaffinity" ,
                 "munmap" ,
                 "brk" ,
                 "rt_sigaction" ,
                 "rt_sigprocmask" ,
                 "sigaltstack" ,
                 "gettid" ,
                 "clone" ,
                 "bind" ,
                 "socket" ,
                 "openat" ,
                 "readlinkat" ,
                 "exit_group" ,
                 "epoll_create1" ,
                 "listen" ,
                 "rt_sigreturn" ,
                 "sched_yield" ,
                 "clock_gettime" ,
                 "connect" ,
                 "dup2" ,
                 "epoll_pwait" ,
                 "execve" ,
                 "exit" ,
                 "fcntl" ,
                 "getpid" ,
                 "getuid" ,
                 "ioctl" ,
                 "mprotect" ,
                 "nanosleep" ,
                 "open" ,
                 "poll" ,
                 "recvfrom" ,
                 "sendto" ,
                 "set_tid_address" ,
                 "setitimer" ,
                 "writev" ,
                 "fstatfs" ,
                 "getdents64" ,
                 "pipe2" ,
                 "getrlimit" 
             ],
             "action" : "SCMP_ACT_ALLOW" 
         }
     ]
 }  
 次のコマンドを実行してください:
mkdir ./profiles
 curl -L -o profiles/audit.json https://k8s.io/examples/pods/security/seccomp/profiles/audit.json
 curl -L -o profiles/violation.json https://k8s.io/examples/pods/security/seccomp/profiles/violation.json
 curl -L -o profiles/fine-grained.json https://k8s.io/examples/pods/security/seccomp/profiles/fine-grained.json
 ls profiles
 最終的に3つのプロファイルが確認できるはずです:
audit.json  fine-grained.json  violation.json
kindでローカルKubernetesクラスターを構築する 
手軽な方法として、kind を利用することで、seccompプロファイルを読み込んだ単一ノードクラスターを構築できます。
kindはKubernetesをDocker内で稼働させるため、クラスターの各ノードはコンテナとなります。
これにより、ノード上にファイルを展開するのと同じように、各コンテナのファイルシステムに対してファイルをマウントすることが可能です。
    
    apiVersion :  kind.x-k8s.io/v1alpha4
 kind :  Cluster
 nodes :
 role :  control-plane
    extraMounts :
    - hostPath :  "./profiles" 
      containerPath :  "/var/lib/kubelet/seccomp/profiles"  
kindの設定サンプルをダウンロードして、kind.yamlの名前で保存してください:
curl -L -O https://k8s.io/examples/pods/security/seccomp/kind.yaml
 ノードのコンテナイメージを設定する際には、特定のKubernetesバージョンを指定することもできます。
この設定方法の詳細については、kindのドキュメント内の、ノード の項目を参照してください。
このチュートリアルではKubernetes v1.34を使用することを前提とします。
ベータ機能として、Unconfinedにフォールバックするのではなく、コンテナランタイム がデフォルトで推奨するプロファイルを利用するようにKubernetesを設定することもできます。
この機能を試したい場合、これ以降の手順に進む前に、全ワークロードに対する標準seccompプロファイルとしてRuntimeDefaultを使用する を参照してください。
kindの設定ファイルを設置したら、kindクラスターを作成します:
kind create cluster --config= kind.yaml
 Kubernetesクラスターが準備できたら、単一ノードクラスターが稼働しているDockerコンテナを特定してください:
kind-control-planeという名前のコンテナが稼働していることが確認できるはずです。
出力は次のようになるでしょう:
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                       NAMES
6a96207fed4b        kindest/node:v1.18.2   "/usr/local/bin/entr…"   27 seconds ago      Up 24 seconds       127.0.0.1:42223->6443/tcp   kind-control-plane
このコンテナのファイルシステムを観察すると、profiles/ディレクトリがkubeletのデフォルトのseccompパスとして正しく読み込まれていることを確認できるはずです。
Pod内でコマンドを実行するためにdocker execを使います:
# 6a96207fed4bを"docker ps"で確認したコンテナIDに変更してください。 
docker exec  -it 6a96207fed4b ls /var/lib/kubelet/seccomp/profiles
 audit.json  fine-grained.json  violation.json
kind内で稼働しているkubeletがseccompプロファイルを利用可能であることを確認しました。
コンテナランタイムの標準seccompプロファイルを利用してPodを作成する 
ほとんどのコンテナランタイムは、許可・拒否の対象とする標準的なシステムコールの集合を提供しています。
PodやContainerのセキュリティコンテキストでseccompタイプをRuntimeDefaultに設定すると、コンテナランタイムが提供するデフォルトのプロファイルをワークロードに適用することができます。
備考: seccompDefaultの
設定 を有効化している場合、他にseccompプロファイルを定義しなくても、Podは
RuntimeDefault seccompプロファイルを使用します。
seccompDefaultが無効の場合のデフォルトは
Unconfinedです。
Pod内の全てのContainerに対してRuntimeDefault seccompプロファイルを要求するマニフェストは次のようなものです:
    
    apiVersion :  v1
 kind :  Pod
 metadata :
    name :  default-pod
    labels :
      app :  default-pod
 spec :
    securityContext :
      seccompProfile :
        type :  RuntimeDefault
    containers :
    - name :  test-container
      image :  hashicorp/http-echo:1.0
      args :
      - "-text=just made some more syscalls!" 
      securityContext :
        allowPrivilegeEscalation :  false  
このPodを作成してみます:
kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/default-pod.yaml
 kubectl get pod default-pod
 Podが正常に起動できていることを確認できるはずです:
NAME        READY   STATUS    RESTARTS   AGE
default-pod 1/1     Running   0          20s
次のセクションに進む前に、Podを削除します:
kubectl delete pod default-pod --wait --now
 システムコール監査のためのseccompプロファイルを利用してPodを作成する 
最初に、新しいPodにプロセスの全システムコールを記録するためのaudit.jsonプロファイルを適用します。
このPodのためのマニフェストは次の通りです:
    
    apiVersion :  v1
 kind :  Pod
 metadata :
    name :  audit-pod
    labels :
      app :  audit-pod
 spec :
    securityContext :
      seccompProfile :
        type :  Localhost
        localhostProfile :  profiles/audit.json
    containers :
    - name :  test-container
      image :  hashicorp/http-echo:1.0
      args :
      - "-text=just made some syscalls!" 
      securityContext :
        allowPrivilegeEscalation :  false  
備考: 過去のバージョンのKubernetesでは、
アノテーション を用いてseccompの挙動を制御することができました。
Kubernetes 1.34におけるseccompの設定では
.spec.securityContextフィールドのみをサポートしており、このチュートリアルではKubernetes 1.34における手順を解説しています。
クラスター内にPodを作成します:
kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/audit-pod.yaml
 このプロファイルは、いかなるシステムコールも制限しないため、Podは正常に起動するはずです。
kubectl get pod audit-pod
 NAME        READY   STATUS    RESTARTS   AGE
audit-pod   1/1     Running   0          30s
このコンテナが公開するエンドポイントとやりとりするために、kindのコントロールプレーンコンテナの内部からこのエンドポイントにアクセスできるように、NodePort Service を作成します。
kubectl expose pod audit-pod --type NodePort --port 5678 
 どのポートがノード上のServiceに割り当てられたのかを確認しましょう。
kubectl get service audit-pod
 次のような出力が得られます:
NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
audit-pod   NodePort   10.111.36.142   <none>        5678:32373/TCP   72s
ここまで来れば、kindのコントロールプレーンコンテナの内部からエンドポイントに対して、Serviceが公開するポートを通じてcurlで接続することができます。
docker execを使って、コントロールプレーンコンテナに属するコンテナの中からcurlを実行しましょう:
# 6a96207fed4bと32373を、それぞれ"docker ps"で確認したコンテナIDとポート番号に変更してください。 
docker exec  -it 6a96207fed4b curl localhost:32373
 just made some syscalls!
プロセスが実行されていることは確認できましたが、実際にどんなシステムコールが発行されているのでしょうか?
このPodはローカルクラスターで稼働しているため、発行されたシステムコールを/var/log/syslogで確認することができるはずです。
新しいターミナルを開いて、http-echoが発行するシステムコールをtailしてみましょう:
# あなたのマシンのログは"/var/log/syslog"以外の場所にあるかもしれません。 
tail -f /var/log/syslog | grep 'http-echo' 
 すでにhttp-echoが発行したいくつかのシステムコールのログが見えているはずです。
コントロールプレーンコンテナ内から再度curlを実行すると、新たにログに追記された内容が出力されます。
例えば、次のような出力が得られるでしょう:
Jul  6 15:37:40 my-machine kernel: [369128.669452] audit: type=1326 audit(1594067860.484:14536): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=51 compat=0 ip=0x46fe1f code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669453] audit: type=1326 audit(1594067860.484:14537): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=54 compat=0 ip=0x46fdba code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669455] audit: type=1326 audit(1594067860.484:14538): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=202 compat=0 ip=0x455e53 code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669456] audit: type=1326 audit(1594067860.484:14539): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=288 compat=0 ip=0x46fdba code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669517] audit: type=1326 audit(1594067860.484:14540): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=0 compat=0 ip=0x46fd44 code=0x7ffc0000
Jul  6 15:37:40 my-machine kernel: [369128.669519] audit: type=1326 audit(1594067860.484:14541): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=270 compat=0 ip=0x4559b1 code=0x7ffc0000
Jul  6 15:38:40 my-machine kernel: [369188.671648] audit: type=1326 audit(1594067920.488:14559): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=270 compat=0 ip=0x4559b1 code=0x7ffc0000
Jul  6 15:38:40 my-machine kernel: [369188.671726] audit: type=1326 audit(1594067920.488:14560): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=29064 comm="http-echo" exe="/http-echo" sig=0 arch=c000003e syscall=202 compat=0 ip=0x455e53 code=0x7ffc0000
各行のsyscall=エントリに着目することで、http-echoプロセスが必要とするシステムコールを理解していくことができるでしょう。
このプロセスが利用する全てのシステムコールを網羅するものではないかもしれませんが、このコンテナのseccompプロファイルを作成する上での基礎とすることは可能です。
次のセクションに進む前にServiceとPodを削除します:
kubectl delete service audit-pod --wait
 kubectl delete pod audit-pod --wait --now
 違反が発生するseccompプロファイルでPodを作成する 
デモとして、いかなるシステムコールも許可しないseccompプロファイルをPodに適用してみましょう。
マニフェストは次の通りです:
    
    apiVersion :  v1
 kind :  Pod
 metadata :
    name :  violation-pod
    labels :
      app :  violation-pod
 spec :
    securityContext :
      seccompProfile :
        type :  Localhost
        localhostProfile :  profiles/violation.json
    containers :
    - name :  test-container
      image :  hashicorp/http-echo:1.0
      args :
      - "-text=just made some syscalls!" 
      securityContext :
        allowPrivilegeEscalation :  false  
クラスターにPodを作成してみます:
kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/violation-pod.yaml
 Podを作成しても問題が発生します。
Podの状態を確認すると、起動に失敗していることが確認できるはずです。
kubectl get pod violation-pod
 NAME            READY   STATUS             RESTARTS   AGE
violation-pod   0/1     CrashLoopBackOff   1          6s
直前の事例で見てきたように、http-echoプロセスは多くのシステムコールを必要とします。
ここでは"defaultAction": "SCMP_ACT_ERRNO"が設定されているため、あらゆるシステムコールに対してseccompがエラーを発生させました。
この構成はとてもセキュアですが、有意義な処理は何もできないことを意味します。
私たちが実際にやりたいのは、ワークロードが必要とする権限のみを付与することです。
次のセクションに進む前にPodを削除します。
kubectl delete pod violation-pod --wait --now
 必要なシステムコールのみを許可するseccompプロファイルを用いてPodを作成する 
fine-grained.jsonプロファイルの内容を見れば、最初の例で"defaultAction":"SCMP_ACT_LOG"を設定していた際に、ログに表示されたシステムコールが含まれていることに気づくでしょう。
今回のプロファイルでも"defaultAction": "SCMP_ACT_ERRNO"を設定しているものの、"action": "SCMP_ACT_ALLOW"ブロックで明示的に一連のシステムコールを許可しています。
理想的な状況下であれば、コンテナが正常に稼働することに加え、syslogへのメッセージ送信を見ることはなくなるでしょう。
この事例のマニフェストは次の通りです:
    
    apiVersion :  v1
 kind :  Pod
 metadata :
    name :  fine-pod
    labels :
      app :  fine-pod
 spec :
    securityContext :
      seccompProfile :
        type :  Localhost
        localhostProfile :  profiles/fine-grained.json
    containers :
    - name :  test-container
      image :  hashicorp/http-echo:1.0
      args :
      - "-text=just made some syscalls!" 
      securityContext :
        allowPrivilegeEscalation :  false  
クラスター内にPodを作成します:
kubectl apply -f https://k8s.io/examples/pods/security/seccomp/ga/fine-pod.yaml
 Podは正常に起動しているはずです:
NAME        READY   STATUS    RESTARTS   AGE
fine-pod   1/1     Running   0          30s
新しいターミナルを開いて、http-echoからのシステムコールに関するログエントリをtailで監視しましょう:
# あなたのマシンのログは"/var/log/syslog"以外の場所にあるかもしれません。 
tail -f /var/log/syslog | grep 'http-echo' 
 次のPodをNodePort Serviceで公開します:
kubectl expose pod fine-pod --type NodePort --port 5678 
 ノード上のServiceに割り当てられたポートを確認します:
kubectl get service fine-pod
 出力は次のようになるでしょう:
NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
fine-pod    NodePort   10.111.36.142   <none>        5678:32373/TCP   72s
kindのコントロールプレーンコンテナの内部から、curlを用いてエンドポイントにアクセスします:
# 6a96207fed4bと32373を、それぞれ"docker ps"で確認したコンテナIDとポート番号に変更してください。 
docker exec  -it 6a96207fed4b curl localhost:32373
 just made some syscalls!
syslogには何も出力されないはずです。
なぜなら、このプロファイルは必要なシステムコールを全て許可しており、一覧にないシステムコールが呼び出された時にのみエラーを発生させるように構成しているためです。
これはセキュリティの観点からすると理想的なシチュエーションといえますが、seccompプロファイルを作成するためのプログラムの解析には多少の労力が必要です。
多くの労力を割かなくてもこれに近いセキュリティが得られるシンプルな手法があったなら、きっと素晴らしいものになるでしょう。
次のセクションに進む前にServiceとPodを削除します:
kubectl delete service fine-pod --wait
 kubectl delete pod fine-pod --wait --now
 全ワークロードに対する標準seccompプロファイルとしてRuntimeDefaultを使用する 
  
      FEATURE STATE: 
      Kubernetes v1.27 [stable]
    
  
標準seccompプロファイルを指定するためには、この機能を利用したい全てのノードで--seccomp-defaultコマンドラインフラグ を用いてkubeletを起動する必要があります。
この機能を有効化すると、kubeletはコンテナランタイムが定義するRuntimeDefaultのseccompプロファイルをデフォルトで使用するようになり、Unconfinedモード(seccomp無効化)になることはありません。
コンテナランタイムが用意する標準プロファイルは、ワークロードの機能性を維持しつつ、強力な標準セキュリティルールの一式を用意することを目指しています。
標準のプロファイルはコンテナランタイムやリリースバージョンによって異なる可能性があります。
例えば、CRI-Oとcontainerdの標準プロファイルを比較してみるとよいでしょう。
備考: この機能を有効化しても、PodやContainerなどの
securityContext.seccompProfileフィールドは変更されず、非推奨のアノテーションがワークロードに追加されることもありません。
したがって、ユーザーはワークロードの設定を変更せずにロールバックすることが可能です。
crictl inspectのようなツールを使うことで、コンテナがどのseccompプロファイルを利用しているのかを確認できます。
いくつかのワークロードでは、他のワークロードよりもシステムコール制限を少なくすることが必要な場合があります。
つまり、RuntimeDefaultを適用する場合、こうしたワークロードの実行が失敗する可能性があります。
このような障害を緩和するために、次のような対策を講じることができます:
ワークロードを明示的にUnconfinedとして稼働させる。 
SeccompDefault機能をノードで無効化する。
また、機能を無効化したノードに対してワークロードが配置されていることを確認しておく。ワークロードを対象とするカスタムseccompプロファイルを作成する。 
 
実運用環境に近いクラスターに対してこの機能を展開する場合、クラスター全体に対して変更をロールアウトする前に、一部ノードのみを対象にしてこのフィーチャーゲートを有効化し、ワークロードが実行できることを検証しておくことをお勧めします。
クラスターに対してとりうるアップグレード・ダウングレード戦略について更に詳細な情報を知りたい場合は、関連するKubernetes Enhancement Proposal (KEP)であるEnable seccomp by default を参照してください。
Kubernetes 1.34では、specで特定のseccompプロファイルを指定していないPodに対して、デフォルトで適用するseccompプロファイルを設定することができます。
ただし、この標準seccompプロファイルを利用する場合、対象とする全ノードでこの機能を有効化しておく必要があります。
稼働中のKubernetes 1.34クラスターでこの機能を有効化する場合、kubeletに--seccomp-defaultコマンドラインフラグを付与して起動するか、Kubernetesの設定ファイル でこの機能を有効化する必要があります。
このフィーチャーゲートをkind で有効化する場合、kindが最低限必要なKubernetesバージョンを満たしていて、かつkindの設定ファイル でSeccompDefaultを有効化してください:
kind :  Cluster
 apiVersion :  kind.x-k8s.io/v1alpha4
 nodes :
    - role :  control-plane
      image :  kindest/node:v1.28.0@sha256:9f3ff58f19dcf1a0611d11e8ac989fdb30a28f40f236f59f0bea31fb956ccf5c
      kubeadmConfigPatches :
        - |
         kind: JoinConfiguration
         nodeRegistration:
           kubeletExtraArgs:
             seccomp-default: "true"         
    - role :  worker
      image :  kindest/node:v1.28.0@sha256:9f3ff58f19dcf1a0611d11e8ac989fdb30a28f40f236f59f0bea31fb956ccf5c
      kubeadmConfigPatches :
        - |
         kind: JoinConfiguration
         nodeRegistration:
           kubeletExtraArgs:
             seccomp-default: "true"         
 クラスターの準備ができたら、Podを実行します:
kubectl run --rm -it --restart= Never --image= alpine alpine -- sh
 このコマンドで標準のseccompプロファイルを紐付けられるはずです。
この結果を確認するには、docker exec経由でcrictl inspectを実行することで、kindワーカー上のコンテナを確認します:
docker exec  -it kind-worker bash -c \
  'crictl inspect $(crictl ps --name=alpine -q) | jq .info.runtimeSpec.linux.seccomp' 
{
   "defaultAction" : "SCMP_ACT_ERRNO" ,
   "architectures" : ["SCMP_ARCH_X86_64" , "SCMP_ARCH_X86" , "SCMP_ARCH_X32" ],
   "syscalls" : [
     {
       "names" : ["..." ]
     }
   ]
 }
 次の項目 
Linuxのseccompについて更に学びたい場合は、次の記事を参考にすると良いでしょう:
 
    
	
  
    
	
  
    
    
	
    
    
	
6 - ステートフルアプリケーション 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	6.1 - StatefulSetの基本 
    
	
このチュートリアルでは、StatefulSet を使用したアプリケーションを管理するための基本を説明します。StatefulSetのPodを作成、削除、スケール、そして更新する方法について紹介します。
始める前に 
このチュートリアルを始める前に、以下のKubernetesの概念について理解しておく必要があります。
備考: このチュートリアルでは、クラスターがPersistentVolumeの動的なプロビジョニングが行われるように設定されていることを前提としています。クラスターがそのように設定されていない場合、チュートリアルを始める前に1GiBのボリュームを2つ手動でプロビジョニングする必要があります。
目標 
StatefulSetはステートフルアプリケーションや分散システムで使用するために存在します。しかし、Kubernetes上のステートフルアプリケーションや分散システムは、広範で複雑なトピックです。StatefulSetの基本的な機能を示すという目的のため、また、ステートフルアプリケーションを分散システムと混同しないようにするために、ここでは、Statefulsetを使用する単純なウェブアプリケーションのデプロイを行います。
このチュートリアルを終えると、以下のことが理解できるようになります。
StatefulSetの作成方法 
StatefulSetがどのようにPodを管理するのか 
StatefulSetの削除方法 
StatefulSetのスケール方法 
StatefulSetが管理するPodの更新方法 
 
StatefulSetを作成する 
はじめに、以下の例を使ってStatefulSetを作成しましょう。これは、コンセプトのStatefulSet のページで使ったものと同じような例です。nginxというheadless Service を作成し、webというStatefulSet内のPodのIPアドレスを公開します。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  nginx
    labels :
      app :  nginx
 spec :
    ports :
    - port :  80 
      name :  web
    clusterIP :  None
    selector :
      app :  nginx
 --- 
 apiVersion :  apps/v1
 kind :  StatefulSet
 metadata :
    name :  web
 spec :
    serviceName :  "nginx" 
    replicas :  2 
    selector :
      matchLabels :
        app :  nginx
    template :
      metadata :
        labels :
          app :  nginx
      spec :
        containers :
        - name :  nginx
          image :  registry.k8s.io/nginx-slim:0.8
          ports :
          - containerPort :  80 
            name :  web
          volumeMounts :
          - name :  www
            mountPath :  /usr/share/nginx/html
    volumeClaimTemplates :
    - metadata :
        name :  www
      spec :
        accessModes :  [  "ReadWriteOnce"   ]
        resources :
          requests :
            storage :  1Gi
 
  
上の例をダウンロードして、web.yamlという名前で保存します。
ここでは、ターミナルウィンドウを2つ使う必要があります。1つ目のターミナルでは、kubectl get
kubectl get pods -w -l app = nginx
 2つ目のターミナルでは、kubectl applyweb.yamlに定義されたheadless ServiceとStatefulSetを作成します。
kubectl apply -f web.yaml
 service/nginx created
statefulset.apps/web created
上のコマンドを実行すると、2つのPodが作成され、それぞれのPodでNGINX ウェブサーバーが実行されます。nginxServiceを取得してみましょう。
kubectl get service nginx
 NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s
そして、webStatefulSetを取得して、2つのリソースの作成が成功したことも確認します。
kubectl get statefulset web
 NAME      DESIRED   CURRENT   AGE
web       2         1         20s
順序付きPodの作成 
n  個のレプリカを持つStatefulSetは、Podをデプロイするとき、1つずつ順番に作成し、 {0..n-1}  という順序付けを行います。1つ目のターミナルでkubectl getコマンドの出力を確認しましょう。最終的に、以下の例のような出力が表示されるはずです。
kubectl get pods -w -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s
web-0Podが Running  (Pod Phase を参照)かつ Ready  (Pod Conditions のtypeを参照)の状態になるまでは、web-1Podが起動していないことに注目してください。
StatefulSet内のPod 
StatefulSet内のPodは、ユニークな順序インデックスと安定したネットワーク識別子を持ちます。
Podの順序インデックスを確かめる 
StatefulSetのPodを取得します。
kubectl get pods -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m
StatefulSet のコンセプトで説明したように、StatefulSet内のPodは安定したユニークな識別子を持ちます。この識別子は、StatefulSetコントローラー によって各Podに割り当てられる、ユニークな順序インデックスに基づいて付けられます。Podの名前は、<statefulsetの名前>-<順序インデックス>という形式です。webStatefulSetは2つのレプリカを持つため、web-0とweb-1という2つのPodを作成します。
安定したネットワーク識別子の使用 
各Podは、順序インデックスに基づいた安定したホスト名を持ちます。kubectl exechostnameコマンドを実行してみましょう。
for  i in 0  1; do  kubectl exec  "web- $i "  -- sh -c 'hostname' ; done 
web-0
web-1
kubectl rundnsutilsパッケージのnslookupコマンドを提供するコンテナを実行します。Podのホスト名に対してnslookupを実行すると、クラスター内のDNSアドレスが確認できます。
kubectl run -i --tty --image busybox:1.28 dns-test --restart= Never --rm
 これにより、新しいシェルが起動します。新しいシェルで、次のコマンドを実行します。
# このコマンドは、dns-testコンテナのシェルで実行してください 
nslookup web-0.nginx
 出力は次のようになります。
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web-0.nginx
Address 1: 10.244.1.6
nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web-1.nginx
Address 1: 10.244.2.6
(コンテナのシェルを終了するために、exitコマンドを実行してください。)
headless serviceのCNAMEは、SRVレコードを指しています(1つのレコードがRunningかつReadyのPodに対応します)。SRVレコードは、PodのIPアドレスを含むAレコードを指します。
1つ目のターミナルで、StatefulSetのPodを監視します。
kubectl get pod -w -l app = nginx
 2つ目のターミナルで、kubectl delete
kubectl delete pod -l app = nginx
 pod "web-0" deleted
pod "web-1" deleted
StatefulSetがPodを再起動して、2つのPodがRunningかつReadyの状態に移行するのを待ちます。
kubectl get pod -w -l app = nginx
 NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s
kubectl execとkubectl runコマンドを使用して、Podのホスト名とクラスター内DNSエントリーを確認します。まず、Podのホスト名を見てみましょう。
for  i in 0  1; do  kubectl exec  web-$i  -- sh -c 'hostname' ; done 
web-0
web-1
その後、次のコマンドを実行します。
kubectl run -i --tty --image busybox:1.28 dns-test --restart= Never --rm
 これにより、新しいシェルが起動します。新しいシェルで、次のコマンドを実行します。
# このコマンドは、dns-testコンテナのシェルで実行してください 
nslookup web-0.nginx
 出力は次のようになります。
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web-0.nginx
Address 1: 10.244.1.7
nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web-1.nginx
Address 1: 10.244.2.8
(コンテナのシェルを終了するために、exitコマンドを実行してください。)
Podの順序インデックス、ホスト名、SRVレコード、そしてAレコード名は変化していませんが、Podに紐付けられたIPアドレスは変化する可能性があります。このチュートリアルで使用しているクラスターでは、IPアドレスは変わりました。このようなことがあるため、他のアプリケーションがStatefulSet内のPodに接続するときには、IPアドレスで指定しないことが重要です。
StatefulSetの有効なメンバーを探して接続する必要がある場合は、headless ServiceのCNAME(nginx.default.svc.cluster.local)をクエリしなければなりません。CNAMEに紐付けられたSRVレコードには、StatefulSet内のRunningかつReadyなPodだけが含まれます。
アプリケーションがlivenessとreadinessをテストするコネクションのロジックをすでに実装している場合、PodのSRVレコード(web-0.nginx.default.svc.cluster.local、web-1.nginx.default.svc.cluster.local)をPodが安定しているものとして使用できます。PodがRunning and Readyな状態に移行すれば、アプリケーションはPodのアドレスを発見できるようになります。
安定したストレージへの書き込み 
web-0およびweb-1のためのPersistentVolumeClaimを取得しましょう。
kubectl get pvc -l app = nginx
 出力は次のようになります。
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s
StatefulSetコントローラーは、2つのPersistentVolume にバインドされた2つのPersistentVolumeClaim を作成しています。
このチュートリアルで使用しているクラスターでは、PersistentVolumeの動的なプロビジョニングが設定されているため、PersistentVolumeが自動的に作成されてバインドされています。
デフォルトでは、NGINXウェブサーバーは/usr/share/nginx/html/index.htmlに置かれたindexファイルを配信します。StatefulSetのspec内のvolumeMountsフィールドによって、/usr/share/nginx/htmlディレクトリがPersistentVolume上にあることが保証されます。
Podのホスト名をindex.htmlファイルに書き込むことで、NGINXウェブサーバーがホスト名を配信することを検証しましょう。
for  i in 0  1; do  kubectl exec  "web- $i "  -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html' ; done 
 for  i in 0  1; do  kubectl exec  -i -t "web- $i "  -- curl http://localhost/; done 
web-0
web-1
備考: 上記のcurlコマンドに対して代わりに403 Forbidden というレスポンスが返ってくる場合、volumeMountsでマウントしたディレクトリのパーミッションを修正する必要があります(これは、hostPathボリュームを使用したときに起こるバグ が原因です)。この問題に対処するには、上のcurlコマンドを再実行する前に、次のコマンドを実行します。
for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
1つ目のターミナルで、StatefulSetのPodを監視します。
kubectl get pod -w -l app = nginx
 2つ目のターミナルで、StatefulSetのすべてのPodを削除します。
kubectl delete pod -l app = nginx
 pod "web-0" deleted
pod "web-1" deleted
1つ目のターミナルでkubectl getコマンドの出力を確認して、すべてのPodがRunningかつReadyの状態に変わるまで待ちます。
kubectl get pod -w -l app = nginx
 NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s
ウェブサーバーがホスト名を配信し続けていることを確認します。
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
もしweb-0およびweb-1が再スケジュールされたとしても、Podは同じホスト名を配信し続けます。これは、PodのPersistentVolumeClaimに紐付けられたPersistentVolumeが、PodのvolumeMountsに再マウントされるためです。web-0とweb-1がどんなノードにスケジュールされたとしても、PodのPersistentVolumeは適切なマウントポイントにマウントされます。
StatefulSetをスケールする 
StatefulSetのスケールとは、レプリカ数を増減することを意味します。これは、replicasフィールドを更新することによって実現できます。StatefulSetのスケールには、kubectl scalekubectl patch
スケールアップ 
1つ目のターミナルで、StatefulSet内のPodを監視します。
kubectl get pods -w -l app = nginx
 2つ目のターミナルで、kubectl scaleを使って、レプリカ数を5にスケールします。
kubectl scale sts web --replicas= 5 
 statefulset.apps/web scaled
1つ目のターミナルのkubectl getコマンドの出力を確認して、3つの追加のPodがRunningかつReadyの状態に変わるまで待ちます。
kubectl get pods -w -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s
StatefulSetコントローラーはレプリカ数をスケールします。
StatefulSetを作成する で説明したように、StatefulSetコントローラーは各Podを順序インデックスに従って1つずつ作成し、次のPodを起動する前に、1つ前のPodがRunningかつReadyの状態になるまで待ちます。
スケールダウン 
1つ目のターミナルで、StatefulSetのPodを監視します。
kubectl get pods -w -l app = nginx
 2つ目のターミナルで、kubectl patchコマンドを使用して、StatefulSetを3つのレプリカにスケールダウンします。
kubectl patch sts web -p '{"spec":{"replicas":3}}' 
 statefulset.apps/web patched
web-4およびweb-3がTerminatingの状態になるまで待ちます。
kubectl get pods -w -l app = nginx
 NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s
順序付きPodを削除する 
コントローラーは、順序インデックスの逆順に1度に1つのPodを削除し、次のPodを削除する前には、各Podが完全にシャットダウンするまで待機しています。
StatefulSetのPersistentVolumeClaimを取得しましょう。
kubectl get pvc -l app = nginx
 NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h
まだ、5つのPersistentVolumeClaimと5つのPersistentVolumeが残っています。安定したストレージへの書き込み を読むと、StatefulSetのPodが削除されても、StatefulSetのPodにマウントされたPersistentVolumeは削除されないと書かれています。このことは、StatefulSetのスケールダウンによってPodが削除された場合にも当てはまります。
StatefulSetsを更新する 
Kubernetes 1.7以降では、StatefulSetコントローラーは自動アップデートをサポートしています。使われる戦略は、StatefulSet APIオブジェクトのspec.updateStrategyフィールドによって決まります。この機能はコンテナイメージのアップグレード、リソースのrequestsやlimits、ラベル、StatefulSet内のPodのアノテーションの更新時に利用できます。有効なアップデートの戦略は、RollingUpdateとOnDeleteの2種類です。
RollingUpdateは、StatefulSetのデフォルトのアップデート戦略です。
RollingUpdate 
RollingUpdateアップデート戦略は、StatefulSetの保証を尊重しながら、順序インデックスの逆順にStatefulSet内のすべてのPodをアップデートします。
webStatefulSetにpatchを当てて、RollingUpdateアップデート戦略を適用しましょう。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}' 
 statefulset.apps/web patched
1つ目のターミナルで、webStatefulSetに再度patchを当てて、コンテナイメージを変更します。
kubectl patch statefulset web --type= 'json'  -p= '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.8"}]' 
 statefulset.apps/web patched
2つ目のターミナルで、StatefulSet内のPodを監視します。
kubectl get pod -l app = nginx -w
 出力は次のようになります。
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
StatefulSet内のPodは、順序インデックスの逆順に更新されました。StatefulSetコントローラーは各Podを終了させ、次のPodを更新する前に、新しいPodがRunningかつReadyの状態に変わるまで待機します。ここで、StatefulSetコントローラーは順序インデックスの前のPodがRunningかつReadyの状態になるまで次のPodの更新を始めず、現在の状態へのアップデートに失敗したPodがあった場合、そのPodをリストアすることに注意してください。
すでにアップデートを受け取ったPodは、アップデートされたバージョンにリストアされます。まだアップデートを受け取っていないPodは、前のバージョンにリストアされます。このような方法により、もし途中で失敗が起こっても、コントローラーはアプリケーションが健全な状態を保ち続けられるようにし、更新が一貫したものになるようにします。
Podを取得して、コンテナイメージを確認してみましょう。
for  p in 0  1  2; do  kubectl get pod "web- $p "  --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}' ; echo; done 
registry.k8s.io/nginx-slim:0.8
registry.k8s.io/nginx-slim:0.8
registry.k8s.io/nginx-slim:0.8
現在、StatefulSet内のすべてのPodは、前のコンテナイメージを実行しています。
備考: kubectl rollout status sts/<name>を使って、StatefulSetへのローリングアップデートの状態を確認することもできます。
ステージングアップデート 
RollingUpdateアップデート戦略にpartitionパラメーターを使用すると、StatefulSetへのアップデートをステージングすることができます。ステージングアップデートを利用すれば、StatefulSet内のすべてのPodを現在のバージョンにしたまま、StatefulSetの.spec.templateを変更することが可能になります。
webStatefulSetにpatchを当てて、updateStrategyフィールドにpartitionを追加しましょう。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}' 
 statefulset.apps/web patched
StatefulSetに再度patchを当てて、コンテナイメージを変更します。
kubectl patch statefulset web --type= 'json'  -p= '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.7"}]' 
 statefulset.apps/web patched
StatefulSet内のPodを削除します。
pod "web-2" deleted
PodがRunningかつReadyになるまで待ちます。
kubectl get pod -l app = nginx -w
 NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s
Podのコンテナイメージを取得します。
kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}' 
 registry.k8s.io/nginx-slim:0.8
アップデート戦略がRollingUpdateであっても、StatefulSetが元のコンテナを持つPodをリストアしたことがわかります。これは、Podの順序インデックスがupdateStrategyで指定したpartitionより小さいためです。
カナリア版をロールアウトする 
ステージングアップデート のときに指定したpartitionを小さくすることで、変更をテストするためのカナリア版をロールアウトできます。
StatefulSetにpatchを当てて、partitionを小さくします。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}' 
 statefulset.apps/web patched
web-2がRunningかつReadyの状態になるまで待ちます。
kubectl get pod -l app = nginx -w
 NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s
Podのコンテナを取得します。
kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}' 
 registry.k8s.io/nginx-slim:0.7
partitionを変更すると、StatefulSetコントローラーはPodを自動的に更新します。Podの順序インデックスがpartition以上の値であるためです。
web-1Podを削除します。
pod "web-1" deleted
web-1PodがRunningかつReadyになるまで待ちます。
kubectl get pod -l app = nginx -w
 出力は次のようになります。
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s
web-1Podのコンテナイメージを取得します。
kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}' 
 registry.k8s.io/nginx-slim:0.8
Podの順序インデックスがpartitionよりも小さいため、web-1は元の設定のコンテナイメージにリストアされました。partitionを指定すると、StatefulSetの.spec.templateが更新されたときに、順序インデックスがそれ以上の値を持つすべてのPodがアップデートされます。partitionよりも小さな順序インデックスを持つPodが削除されたり終了されたりすると、元の設定のPodにリストアされます。
フェーズロールアウト 
カナリア版 をロールアウトするのと同じような方法でパーティションされたローリングアップデートを使用すると、フェーズロールアウト(例: 線形、幾何級数的、指数関数的ロールアウト)を実行できます。フェーズロールアウトを実行するには、コントローラーがアップデートを途中で止めてほしい順序インデックスをpartitionに設定します。
現在、partitionは2に設定されています。partitionを0に設定します。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}' 
 statefulset.apps/web patched
StatefulSet内のすべてのPodがRunningかつReadyの状態になるまで待ちます。
kubectl get pod -l app = nginx -w
 出力は次のようになります。
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s
StatefulSet内のPodのコンテナイメージの詳細を取得します。
for  p in 0  1  2; do  kubectl get pod "web- $p "  --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}' ; echo; done 
registry.k8s.io/nginx-slim:0.7
registry.k8s.io/nginx-slim:0.7
registry.k8s.io/nginx-slim:0.7
partitionを0に移動することで、StatefulSetがアップデート処理を続けられるようにできます。
OnDelete 
OnDeleteアップデート戦略は、(1.6以前の)レガシーな動作を実装しています。このアップデート戦略を選択すると、StatefulSetの.spec.templateフィールドへ変更を加えても、StatefulSetコントローラーが自動的にPodを更新しなくなります。この戦略を選択するには、.spec.template.updateStrategy.typeにOnDeleteを設定します。
StatefulSetを削除する 
StatefulSetは、非カスケードな削除とカスケードな削除の両方をサポートしています。非カスケードな削除では、StatefulSetが削除されても、StatefulSet内のPodは削除されません。カスケードな削除では、StatefulSetとPodが一緒に削除されます。
非カスケードな削除 
1つ目のターミナルで、StatefulSet内のPodを監視します
kubectl get pods -w -l app=nginx
kubectl delete--cascade=orphanパラメーターをコマンドに与えてください。このパラメーターは、Kubernetesに対して、StatefulSetだけを削除して配下のPodは削除しないように指示します。
kubectl delete statefulset web --cascade= orphan
 statefulset.apps "web" deleted
Podを取得して、ステータスを確認します。
kubectl get pods -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m
webが削除されても、すべてのPodはまだRunningかつReadyの状態のままです。web-0を削除します。
pod "web-0" deleted
StatefulSetのPodを取得します。
kubectl get pods -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m
webStatefulSetはすでに削除されているため、web-0は再起動しません。
1つ目のターミナルで、StatefulSetのPodを監視します。
kubectl get pods -w -l app = nginx
 2つ目のターミナルで、StatefulSetを再作成します。もしnginxServiceを削除しなかった場合(この場合は削除するべきではありませんでした)、Serviceがすでに存在することを示すエラーが表示されます。
kubectl apply -f web.yaml
 statefulset.apps/web created
service/nginx unchanged
このエラーは無視してください。このメッセージは、すでに存在する nginx  というheadless Serviceを作成しようと試みたということを示しているだけです。
1つ目のターミナルで、kubectl getコマンドの出力を確認します。
kubectl get pods -w -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
webStatefulSetが再作成されると、最初にweb-0を再実行します。web-1はすでにRunningかつReadyの状態であるため、web-0がRunningかつReadyの状態に移行すると、StatefulSetは単純にこのPodを選びます。StatefulSetをreplicasを2にして再作成したため、一度web-0が再作成されて、web-1がすでにRunningかつReadyの状態であることが判明したら、web-2は停止されます。
Podのウェブサーバーが配信しているindex.htmlファイルのコンテンツをもう一度見てみましょう。
for  i in 0  1; do  kubectl exec  -i -t "web- $i "  -- curl http://localhost/; done 
web-0
web-1
たとえStatefulSetとweb-0Podの両方が削除されても、Podは最初にindex.htmlファイルに書き込んだホスト名をまだ配信しています。これは、StatefulSetがPodに紐付けられたPersistentVolumeを削除しないためです。StatefulSetを再作成してweb-0を再実行すると、元のPersistentVolumeが再マウントされます。
カスケードな削除 
1つ目のターミナルで、StatefulSet内のPodを監視します。
kubectl get pods -w -l app = nginx
 2つ目のターミナルで、StatefulSetをもう一度削除します。今回は、--cascade=orphanパラメーターを省略します。
kubectl delete statefulset web
 statefulset.apps "web" deleted
1つ目のターミナルで実行しているkubectl getコマンドの出力を確認し、すべてのPodがTerminatingの状態に変わるまで待ちます。
kubectl get pods -w -l app = nginx
 NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
スケールダウン のセクションで見たように、順序インデックスの逆順に従って、Podは一度に1つずつ終了します。StatefulSetコントローラーは、次のPodを終了する前に、前のPodが完全に終了するまで待ちます。
備考: カスケードな削除ではStatefulSetがPodとともに削除されますが、StatefulSetと紐付けられたheadless Serviceは削除されません。そのため、nginxServiceは手動で削除する必要があります。
kubectl delete service nginx
 service "nginx" deleted
さらにもう一度、StatefulSetとheadless Serviceを再作成します。
kubectl apply -f web.yaml
 service/nginx created
statefulset.apps/web created
StatefulSet上のすべてのPodがRunningかつReadyの状態に変わったら、Pod上のindex.htmlファイルのコンテンツを取得します。
for  i in 0  1; do  kubectl exec  -i -t "web- $i "  -- curl http://localhost/; done 
web-0
web-1
StatefulSetを完全に削除して、すべてのPodが削除されたとしても、PersistentVolumeがマウントされたPodが再生成されて、web-0とweb-1はホスト名の配信を続けます。
最後に、webStatefulSetを削除します。
kubectl delete service nginx
 service "nginx" deleted
そして、nginxServiceも削除します。
kubectl delete statefulset web
 statefulset "web" deleted
Pod管理ポリシー 
分散システムによっては、StatefulSetの順序の保証が不必要であったり望ましくない場合もあります。こうしたシステムでは、一意性と同一性だけが求められます。この問題に対処するために、Kubernetes 1.7でStatefulSet APIオブジェクトに.spec.podManagementPolicyが導入されました。
OrderedReadyのPod管理 
OrderedReadyのPod管理はStatefulSetのデフォルトの設定です。StatefulSetコントローラーに対して、これまでに紹介したような順序の保証を尊重するように指示します。
ParallelのPod管理 
ParallelのPod管理では、StatefulSetコントローラーに対して、PodがRunningかつReadyの状態や完全に停止するまで待たないように指示し、すべてのPodを並列に起動または停止させるようにします。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  nginx
    labels :
      app :  nginx
 spec :
    ports :
    - port :  80 
      name :  web
    clusterIP :  None
    selector :
      app :  nginx
 --- 
 apiVersion :  apps/v1
 kind :  StatefulSet
 metadata :
    name :  web
 spec :
    serviceName :  "nginx" 
    podManagementPolicy :  "Parallel" 
    replicas :  2 
    selector :
      matchLabels :
        app :  nginx
    template :
      metadata :
        labels :
          app :  nginx
      spec :
        containers :
        - name :  nginx
          image :  registry.k8s.io/nginx-slim:0.8
          ports :
          - containerPort :  80 
            name :  web
          volumeMounts :
          - name :  www
            mountPath :  /usr/share/nginx/html
    volumeClaimTemplates :
    - metadata :
        name :  www
      spec :
        accessModes :  [  "ReadWriteOnce"   ]
        resources :
          requests :
            storage :  1Gi
  
上の例をダウンロードして、web-parallel.yamlという名前でファイルに保存してください。
このマニフェストは、.spec.podManagementPolicyがParallelに設定されている以外は、前にダウンロードしたwebStatefulSetと同一です。
1つ目のターミナルで、StatefulSet内のPodを監視します。
kubectl get pod -l app = nginx -w
 2つ目のターミナルで、マニフェスト内のStatefulSetとServiceを作成します。
kubectl apply -f web-parallel.yaml
 service/nginx created
statefulset.apps/web created
1つ目のターミナルで実行したkubectl getコマンドの出力を確認します。
kubectl get pod -l app = nginx -w
 NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s
StatefulSetコントローラーはweb-0とweb-1を同時に起動しています。
2つ目のターミナルで、StatefulSetをスケールしてみます。
kubectl scale statefulset/web --replicas= 4 
 statefulset.apps/web scaled
kubectl getコマンドを実行しているターミナルの出力を確認します。
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     1/1       Running   0         10s
web-3     1/1       Running   0         26s
StatefulSetが2つのPodを実行し、1つ目のPodがRunningかつReadyの状態になるのを待たずに2つ目のPodを実行しているのがわかります。
クリーンアップ 
2つのターミナルが開かれているはずなので、クリーンアップの一部としてkubectlコマンドを実行する準備ができています。
kubectl delete sts web
 # stsは、statefulsetの略です。 
kubectl getを監視すると、Podが削除されていく様子を確認できます。
kubectl get pod -l app = nginx -w
 web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
削除の間、StatefulSetはすべてのPodを並列に削除し、順序インデックスが1つ前のPodが停止するのを待つことはありません。
kubectl getコマンドを実行しているターミナルを閉じて、nginxServiceを削除します。
備考: このチュートリアルで使用したPersistentVolumeのための永続ストレージも削除する必要があります。
すべてのストレージが再利用できるようにするために、環境、ストレージの設定、プロビジョニング方法に基づいて必要な手順に従ってください。
 
    
	
  
    
    
	
    
    
	6.2 - 例: Persistent Volumeを使用したWordpressとMySQLをデプロイする 
    
	
このチュートリアルでは、WordPressのサイトとMySQLデータベースをMinikubeを使ってデプロイする方法を紹介します。2つのアプリケーションとも、データを保存するためにPersistentVolumeとPersistentVolumeClaimを使用します。
PersistentVolume (PV)とは、管理者が手動でプロビジョニングを行うか、StorageClass を使ってKubernetesによって動的にプロビジョニングされた、クラスター内のストレージの一部です。PersistentVolumeClaim (PVC)は、PVによって満たすことができる、ユーザーによるストレージへのリクエストのことです。PersistentVolumeとPersistentVolumeClaimは、Podのライフサイクルからは独立していて、Podの再起動、Podの再スケジューリング、さらにはPodの削除が行われたとしても、その中のデータは削除されずに残ります。
警告: シングルインスタンスのWordPressとMySQLのPodを使用しているため、ここで行うデプロイは本番のユースケースには適しません。WordPressを本番環境にデプロイするときは、
WordPress Helm Chart を使用することを検討してください。
備考: このチュートリアルで提供されるファイルは、GAとなっているDeployment APIを使用しているため、Kubernetesバージョン1.9以降のためのものになっています。もしこのチュートリアルを古いバージョンのKubernetesで使いたい場合は、APIのバージョンを適切にアップデートするか、このチュートリアルの古いバージョンを参照してください。
目標 
PersistentVolumeClaimとPersistentVolumeを作成する 
以下を含むkustomization.yamlを作成する
Secret generator 
MySQLリソースの設定 
WordPressリソースの設定 
 
 
kustomizationディレクトリをkubectl apply -k ./で適用する 
クリーンアップする 
 
始める前に 
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。
このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。
まだクラスターがない場合、minikube を使って作成するか、
以下のいずれかのKubernetesプレイグラウンドも使用できます:
 
  バージョンを確認するには次のコマンドを実行してください:  kubectl version.
このページで示された例は、
kubectl 1.14以降で動作します。
以下の設定ファイルをダウンロードします。
mysql-deployment.yaml 
 
wordpress-deployment.yaml 
 
 
PersistentVolumeClaimとPersistentVolumeを作成する 
MySQLとWordpressはそれぞれ、データを保存するためのPersistentVolumeを必要とします。各PersistentVolumeClaimはデプロイの段階で作成されます。
多くのクラスター環境では、デフォルトのStorageClassがインストールされています。StorageClassがPersistentVolumeClaim中で指定されていなかった場合、クラスターのデフォルトのStorageClassが代わりに使われます。
PersistentVolumeClaimが作成されるとき、StorageClassの設定に基づいてPersistentVolumeが動的にプロビジョニングされます。
警告: ローカルのクラスターでは、デフォルトのStorageClassにはhostPathプロビジョナーが使われます。hostPathボリュームは開発およびテストにのみ適しています。hostPathボリュームでは、データはPodがスケジュールされたノード上の/tmp内に保存されます。そのため、もしPodが死んだり、クラスター上の他のノードにスケジュールされたり、ノードが再起動すると、データは失われます。
備考: hostPathプロビジョナーを使用する必要があるクラスターを立ち上げたい場合は、--enable-hostpath-provisionerフラグを controller-manager コンポーネントで設定する必要があります。
備考: Google Kubernetes Engine上で動作するKubernetesクラスターを使っている場合は、
このガイド に従ってください。
kustomization.yamlを作成する 
Secret generatorを追加する 
Secret とは、パスワードやキーのような機密性の高いデータ片を保存するためのオブジェクトです。バージョン1.14からは、kubectlがkustomizationファイルを使用したKubernetesオブジェクトの管理をサポートしています。kustomization.yaml内のgeneratorによってSecretを作成することができます。
以下のコマンドを実行して、kustomization.yamlの中にSecret generatorを追加します。YOUR_PASSWORDの部分を使いたいパスワードに置換してください。
cat <<EOF >./kustomization.yaml
  secretGenerator:
 - name: mysql-pass
   literals:
   - password=YOUR_PASSWORD
 EOF 
MySQLとWordPressのためのリソースの設定を追加する 
以下のマニフェストには、シングルインスタンスのMySQLのDeploymentが書かれています。MySQLコンテナはPersistentVolumeを/var/lib/mysqlにマウントします。MYSQL_ROOT_PASSWORD環境変数には、Secretから得られたデータベースのパスワードが設定されます。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  wordpress-mysql
    labels :
      app :  wordpress
 spec :
    ports :
      - port :  3306 
    selector :
      app :  wordpress
      tier :  mysql
    clusterIP :  None
 --- 
 apiVersion :  v1
 kind :  PersistentVolumeClaim
 metadata :
    name :  mysql-pv-claim
    labels :
      app :  wordpress
 spec :
    accessModes :
      - ReadWriteOnce
    resources :
      requests :
        storage :  20Gi
 --- 
 apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    name :  wordpress-mysql
    labels :
      app :  wordpress
 spec :
    selector :
      matchLabels :
        app :  wordpress
        tier :  mysql
    strategy :
      type :  Recreate
    template :
      metadata :
        labels :
          app :  wordpress
          tier :  mysql
      spec :
        containers :
        - image :  mysql:8.0
          name :  mysql
          env :
          - name :  MYSQL_ROOT_PASSWORD
            valueFrom :
              secretKeyRef :
                name :  mysql-pass
                key :  password
          - name :  MYSQL_DATABASE
            value :  wordpress
          - name :  MYSQL_USER
            value :  wordpress
          - name :  MYSQL_PASSWORD
            valueFrom :
              secretKeyRef :
                name :  mysql-pass
                key :  password
          ports :
          - containerPort :  3306 
            name :  mysql
          volumeMounts :
          - name :  mysql-persistent-storage
            mountPath :  /var/lib/mysql
        volumes :
        - name :  mysql-persistent-storage
          persistentVolumeClaim :
            claimName :  mysql-pv-claim
  
以下のマニフェストには、シングルインスタンスのWordPressのDeploymentが書かれています。WordPressコンテナはPersistentVolumeをウェブサイトのデータファイルのために/var/www/htmlにマウントします。WORDPRESS_DB_HOST環境変数に上で定義したMySQLのServiceの名前を設定すると、WordPressはServiceによってデータベースにアクセスします。WORDPRESS_DB_PASSWORD環境変数には、kustomizeが生成したSecretから得たデータベースのパスワードが設定されます。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  wordpress
    labels :
      app :  wordpress
 spec :
    ports :
      - port :  80 
    selector :
      app :  wordpress
      tier :  frontend
    type :  LoadBalancer
 --- 
 apiVersion :  v1
 kind :  PersistentVolumeClaim
 metadata :
    name :  wp-pv-claim
    labels :
      app :  wordpress
 spec :
    accessModes :
      - ReadWriteOnce
    resources :
      requests :
        storage :  20Gi
 --- 
 apiVersion :  apps/v1  # for versions before 1.9.0 use apps/v1beta2 
 kind :  Deployment
 metadata :
    name :  wordpress
    labels :
      app :  wordpress
 spec :
    selector :
      matchLabels :
        app :  wordpress
        tier :  frontend
    strategy :
      type :  Recreate
    template :
      metadata :
        labels :
          app :  wordpress
          tier :  frontend
      spec :
        containers :
        - image :  wordpress:4.8-apache
          name :  wordpress
          env :
          - name :  WORDPRESS_DB_HOST
            value :  wordpress-mysql
          - name :  WORDPRESS_DB_PASSWORD
            valueFrom :
              secretKeyRef :
                name :  mysql-pass
                key :  password
          ports :
          - containerPort :  80 
            name :  wordpress
          volumeMounts :
          - name :  wordpress-persistent-storage
            mountPath :  /var/www/html
        volumes :
        - name :  wordpress-persistent-storage
          persistentVolumeClaim :
            claimName :  wp-pv-claim
  
MySQLのDeploymentの設定ファイルをダウンロードします。
curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
  
WordPressの設定ファイルをダウンロードします。
curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
  
これらをkustomization.yamlファイルに追加します。
 
 
cat <<EOF >>./kustomization.yaml
  resources:
   - mysql-deployment.yaml
   - wordpress-deployment.yaml
 EOF 
適用と確認 
kustomization.yamlには、WordPressのサイトとMySQLデータベースのためのすべてのリソースが含まれています。次のコマンドでこのディレクトリを適用できます。
これで、すべてのオブジェクトが存在していることを確認できます。
次のコマンドを実行して、Secretが存在していることを確認します。
結果は次のようになるはずです。
NAME                    TYPE                                  DATA   AGE
 mysql-pass-c57bb4t7mf   Opaque                                1       9s
  
次のコマンドを実行して、PersistentVolumeが動的にプロビジョニングされていることを確認します。
備考:   PVがプロビジョニングされてバインドされるまでに、最大で数分かかる場合があります。
結果は次のようになるはずです。
NAME             STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
 mysql-pv-claim   Bound     pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
 wp-pv-claim      Bound     pvc-8cd0df54-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
  
次のコマンドを実行して、Podが実行中であることを確認します。
備考:   PodのStatusが`Running`の状態になる前に、最大で数分かかる場合があります。
結果は次のようになるはずです。
NAME                               READY     STATUS    RESTARTS   AGE
wordpress-mysql-1894417608-x5dzt   1/1       Running   0          40s
 
次のコマンドを実行して、Serviceが実行中であることを確認します。
kubectl get services wordpress
 結果は次のようになるはずです。
NAME        TYPE            CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
wordpress   LoadBalancer    10.0.0.89    <pending>     80:32406/TCP   4m
備考:   MinikubeではServiceを`NodePort`経由でしか公開できません。EXTERNAL-IPは常にpendingのままになります。
 
次のコマンドを実行して、WordPress ServiceのIPアドレスを取得します。
minikube service wordpress --url
 結果は次のようになるはずです。
http://1.2.3.4:32406
 
IPアドレスをコピーして、ブラウザーで読み込み、サイトを表示しましょう。
WordPressによりセットアップされた次のスクリーンショットのようなページが表示されるはずです。
 
 
警告: WordPressのインストールをこのページのまま放置してはいけません。もしほかのユーザーがこのページを見つけた場合、その人はインスタンス上にウェブサイトをセットアップして、悪意のあるコンテンツの配信に利用できてしまいます。クリーンアップ 
次のコマンドを実行して、Secret、Deployment、Service、およびPersistentVolumeClaimを削除します。
 
 
次の項目 
 
    
	
  
    
    
	
    
    
	6.3 - 例: StatefulSetを使用したCassandraのデプロイ 
    
	
このチュートリアルでは、Apache Cassandra をKubernetes上で実行する方法を紹介します。
データベースの一種であるCassandraには、データの耐久性(アプリケーションの 状態 )を提供するために永続ストレージが必要です。
この例では、カスタムのCassandraのseed providerにより、Cassandraクラスターに参加した新しいCassandraインスタンスを検出できるようにします。
StatefulSet を利用すると、ステートフルなアプリケーションをKubernetesクラスターにデプロイするのが簡単になります。
このチュートリアルで使われている機能のより詳しい情報は、StatefulSet を参照してください。
備考: CassandraとKubernetesは、ともにクラスターのメンバーを表すために ノード  という用語を使用しています。このチュートリアルでは、StatefulSetに属するPodはCassandraのノードであり、Cassandraクラスター( ring  と呼ばれます)のメンバーでもあります。これらのPodがKubernetesクラスター内で実行されるとき、Kubernetesのコントロールプレーンは、PodをKubernetesのNode 上にスケジュールします。
Cassandraノードが開始すると、 シードリスト  を使ってring上の他のノードの検出が始まります。
このチュートリアルでは、Kubernetesクラスター内に現れた新しいCassandra Podを検出するカスタムのCassandraのseed providerをデプロイします。
目標 
Cassandraのheadless Service を作成して検証する。 
StatefulSet を使用してCassandra ringを作成する。StatefulSetを検証する。 
StatefulSetを編集する。 
StatefulSetとPod を削除する。 
 
始める前に 
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。
このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。
まだクラスターがない場合、minikube を使って作成するか、
以下のいずれかのKubernetesプレイグラウンドも使用できます:
このチュートリアルを完了するには、Pod 、Service 、StatefulSet の基本についてすでに知っている必要があります。
Minikubeのセットアップに関する追加の設定手順 
注意: Minikube は、デフォルトでは1024MiBのメモリと1CPUに設定されます。
デフォルトのリソース設定で起動したMinikubeでは、このチュートリアルの実行中にリソース不足のエラーが発生してしまいます。このエラーを回避するためにはMinikubeを次の設定で起動してください:
minikube start --memory 5120  --cpus= 4 
 Cassandraのheadless Serviceを作成する 
Kubernetesでは、Service は同じタスクを実行するPod の集合を表します。
以下のServiceは、Cassandra Podとクラスター内のクライアント間のDNSルックアップに使われます:
    
    apiVersion :  v1
 kind :  Service
 metadata :
    labels :
      app :  cassandra
    name :  cassandra
 spec :
    clusterIP :  None
    ports :
    - port :  9042 
    selector :
      app :  cassandra
  
cassandra-service.yamlファイルから、Cassandra StatefulSetのすべてのメンバーをトラッキングするServiceを作成します。
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml
 検証 (オプション) 
Cassandra Serviceを取得します。
kubectl get svc cassandra
 結果は次のようになります。
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
cassandra   ClusterIP   None         <none>        9042/TCP   45s
cassandraという名前のServiceが表示されない場合、作成に失敗しています。よくある問題のトラブルシューティングについては、Serviceのデバッグ を読んでください。
StatefulSetを使ってCassandra ringを作成する 
以下に示すStatefulSetマニフェストは、3つのPodからなるCassandra ringを作成します。
備考: この例ではMinikubeのデフォルトのプロビジョナーを使用しています。
クラウドを使用している場合、StatefulSetを更新してください。
    
    apiVersion :  apps/v1
 kind :  StatefulSet
 metadata :
    name :  cassandra
    labels :
      app :  cassandra
 spec :
    serviceName :  cassandra
    replicas :  3 
    selector :
      matchLabels :
        app :  cassandra
    template :
      metadata :
        labels :
          app :  cassandra
      spec :
        terminationGracePeriodSeconds :  500 
        containers :
        - name :  cassandra
          image :  gcr.io/google-samples/cassandra:v13
          imagePullPolicy :  Always
          ports :
          - containerPort :  7000 
            name :  intra-node
          - containerPort :  7001 
            name :  tls-intra-node
          - containerPort :  7199 
            name :  jmx
          - containerPort :  9042 
            name :  cql
          resources :
            limits :
              cpu :  "500m" 
              memory :  1Gi
            requests :
              cpu :  "500m" 
              memory :  1Gi
          securityContext :
            capabilities :
              add :
                - IPC_LOCK
          lifecycle :
            preStop :
              exec :
                command :
                - /bin/sh
                - -c
                - nodetool drain
          env :
            - name :  MAX_HEAP_SIZE
              value :  512M
            - name :  HEAP_NEWSIZE
              value :  100M
            - name :  CASSANDRA_SEEDS
              value :  "cassandra-0.cassandra.default.svc.cluster.local" 
            - name :  CASSANDRA_CLUSTER_NAME
              value :  "K8Demo" 
            - name :  CASSANDRA_DC
              value :  "DC1-K8Demo" 
            - name :  CASSANDRA_RACK
              value :  "Rack1-K8Demo" 
            - name :  POD_IP
              valueFrom :
                fieldRef :
                  fieldPath :  status.podIP
          readinessProbe :
            exec :
              command :
              - /bin/bash
              - -c
              - /ready-probe.sh
            initialDelaySeconds :  15 
            timeoutSeconds :  5 
          # These volume mounts are persistent. They are like inline claims, 
          # but not exactly because the names need to match exactly one of 
          # the stateful pod volumes. 
          volumeMounts :
          - name :  cassandra-data
            mountPath :  /cassandra_data
    # These are converted to volume claims by the controller 
    # and mounted at the paths mentioned above. 
    # do not use these in production until ssd GCEPersistentDisk or other ssd pd 
    volumeClaimTemplates :
    - metadata :
        name :  cassandra-data
      spec :
        accessModes :  [  "ReadWriteOnce"   ]
        storageClassName :  fast
        resources :
          requests :
            storage :  1Gi
 --- 
 kind :  StorageClass
 apiVersion :  storage.k8s.io/v1
 metadata :
    name :  fast
 provisioner :  k8s.io/minikube-hostpath
 parameters :
    type :  pd-ssd
  
cassandra-statefulset.yamlファイルから、CassandraのStatefulSetを作成します:
# cassandra-statefulset.yaml を編集せずにapplyできる場合は、このコマンドを使用してください 
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml
 クラスターに合わせてcassandra-statefulset.yamlを編集する必要がある場合、 https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml  をダウンロードして、修正したバージョンを保存したフォルダからマニフェストを適用してください。
# cassandra-statefulset.yaml をローカルで編集する必要がある場合、このコマンドを使用してください 
kubectl apply -f cassandra-statefulset.yaml
 CassandraのStatefulSetを検証する 
CassandraのStatefulSetを取得します
kubectl get statefulset cassandra
 結果は次のようになるはずです:
NAME        DESIRED   CURRENT   AGE
cassandra   3         0         13s
StatefulSetリソースがPodを順番にデプロイします。
 
Podを取得して順序付きの作成ステータスを確認します
kubectl get pods -l= "app=cassandra" 
 結果は次のようになるはずです:
NAME          READY     STATUS              RESTARTS   AGE
 cassandra-0   1/1       Running             0           1m
 cassandra-1   0/1       ContainerCreating   0           8s
 3つすべてのPodのデプロイには数分かかる場合があります。デプロイが完了すると、同じコマンドは次のような結果を返します:
NAME          READY     STATUS    RESTARTS   AGE
cassandra-0   1/1       Running   0          10m
cassandra-1   1/1       Running   0          9m
cassandra-2   1/1       Running   0          8m
 
1番目のPodの中でCassandraのnodetool を実行して、ringのステータスを表示します。
kubectl exec  -it cassandra-0 -- nodetool status
 結果は次のようになるはずです:
Datacenter: DC1-K8Demo
======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
UN  172.17.0.5  83.57 KiB  32           74.0%             e2dd09e6-d9d3-477e-96c5-45094c08db0f  Rack1-K8Demo
UN  172.17.0.4  101.04 KiB  32           58.8%             f89d6835-3a42-4419-92b3-0e62cae1479c  Rack1-K8Demo
UN  172.17.0.6  84.74 KiB  32           67.1%             a6a1e8c2-3dc5-4417-b1a0-26507af2aaad  Rack1-K8Demo
 
 
CassandraのStatefulSetを変更する 
kubectl editを使うと、CassandraのStatefulSetのサイズを変更できます。
次のコマンドを実行します。
kubectl edit statefulset cassandra
 このコマンドを実行すると、ターミナルでエディタが起動します。変更が必要な行はreplicasフィールドです。
以下の例は、StatefulSetファイルの抜粋です:
# Please edit the object below. Lines beginning with a '#' will be ignored, 
 # and an empty file will abort the edit. If an error occurs while saving this file will be 
 # reopened with the relevant failures. 
 # 
 apiVersion :  apps/v1
 kind :  StatefulSet
 metadata :
    creationTimestamp :  2016-08-13T18:40:58Z
    generation :  1 
    labels :
    app :  cassandra
    name :  cassandra
    namespace :  default
    resourceVersion :  "323" 
    uid :  7a219483-6185-11e6-a910-42010a8a0fc0
 spec :
    replicas :  3 
  
レプリカ数を4に変更し、マニフェストを保存します。
これで、StatefulSetが4つのPodを実行するようにスケールされました。
 
CassandraのStatefulSetを取得して、変更を確かめます:
kubectl get statefulset cassandra
 結果は次のようになるはずです:
NAME        DESIRED   CURRENT   AGE
cassandra   4         4         36m
 
 
クリーンアップ 
StatefulSetを削除したりスケールダウンしても、StatefulSetに関係するボリュームは削除されません。
StatefulSetに関連するすべてのリソースを自動的に破棄するよりも、データの方がより貴重であるため、安全のためにこのような設定になっています。
警告: ストレージクラスやreclaimポリシーによっては、PersistentVolumeClaim を削除すると、関連するボリュームも削除される可能性があります。PersistentVolumeClaimの削除後にもデータにアクセスできるとは決して想定しないでください。
次のコマンドを実行して(単一のコマンドにまとめています)、CassandraのStatefulSetに含まれるすべてのリソースを削除します:
grace = $( kubectl get pod cassandra-0 -o= jsonpath = '{.spec.terminationGracePeriodSeconds}' )  \
 &&  kubectl delete statefulset -l app = cassandra \
 &&  echo  "Sleeping  ${ grace }  seconds"  1>&2  \
 &&  sleep $grace  \
 &&  kubectl delete persistentvolumeclaim -l app = cassandra
 
次のコマンドを実行して、CassandraをセットアップしたServiceを削除します:
kubectl delete service -l app = cassandra
  
 
Cassandraコンテナの環境変数 
このチュートリアルのPodでは、Googleのコンテナレジストリ のgcr.io/google-samples/cassandra:v13debian-base をベースにしており、OpenJDK 8が含まれています。
このイメージには、Apache Debianリポジトリの標準のCassandraインストールが含まれます。
環境変数を利用すると、cassandra.yamlに挿入された値を変更できます。
環境変数 
デフォルト値 
 
 
CASSANDRA_CLUSTER_NAME'Test Cluster' 
CASSANDRA_NUM_TOKENS32 
CASSANDRA_RPC_ADDRESS0.0.0.0 
 
次の項目 
 
    
	
  
    
    
	
    
    
	6.4 - 分散システムコーディネーターZooKeeperの実行 
    
	
このチュートリアルでは、StatefulSet 、PodDisruptionBudgets 、Podアンチアフィニティ を使って、Kubernetes上でのApache Zookeeper の実行をデモンストレーションします。
始める前に 
このチュートリアルを始める前に、以下のKubernetesの概念について理解しておく必要があります。
少なくとも4つのノードのクラスターが必要で、各ノードは少なくとも2つのCPUと4GiBのメモリが必須です。このチュートリアルでは、クラスターのノードをcordonおよびdrainします。
つまり、クラスターがそのノードの全てのPodを終了して退去させ、ノードが一時的にスケジュールできなくなる、ということです。 
このチュートリアル専用のクラスターを使うか、起こした破壊がほかのテナントに干渉しない確証を得ることをお勧めします。
このチュートリアルでは、クラスターがPersistentVolumeの動的なプロビジョニングが行われるように設定されていることを前提としています。
クラスターがそのように設定されていない場合、チュートリアルを始める前に20GiBのボリュームを3つ、手動でプロビジョニングする必要があります。
目標 
このチュートリアルを終えると、以下の知識を得られます。
StatefulSetを使ってZooKeeperアンサンブルをデプロイする方法。 
アンサンブルを一貫して設定する方法。 
ZooKeeperサーバーのデプロイをアンサンブルに広げる方法。 
計画されたメンテナンス中もサービスが利用可能であることを保証するためにPodDisruptionBudgetsを使う方法。 
 
ZooKeeper 
Apache ZooKeeper は、分散アプリケーションのための、分散型オープンソースコーディネーションサービスです。
ZooKeeperでは、データの読み書き、および更新の監視ができます。
データは階層化されてファイルシステム内に編成され、アンサンブル(ZooKeeperサーバーのセット)内の全てのZooKeeperサーバーに複製されます。
データへの全ての操作はアトミックかつ逐次的に一貫性を持ちます。
ZooKeeperは、アンサンブル内の全てのサーバー間でステートマシンを複製するためにZab 合意プロトコルを使ってこれを保証します。
アンサンブルはリーダーを選出するのにZabプロトコルを使い、選出が完了するまでデータを書き出しません。
完了すると、アンサンブルは複製するのにZabを使い、書き込みが承認されてクライアントに可視化されるより前に、全ての書き込みをクォーラムに複製することを保証します。
重み付けされたクォーラムでなければ、クォーラムは現在のリーダーを含むアンサンブルの過半数を占めるコンポーネントです。
例えばアンサンブルが3つのサーバーを持つ時、リーダーとそれ以外のもう1つのサーバーを含むコンポーネントが、クォーラムを構成します。
アンサンブルがクォーラムに達しない場合、アンサンブルはデータを書き出せません。
ZooKeeperサーバー群はそれらの全てのステートマシンをメモリに保持し、それぞれの変化をストレージメディア上の永続的なWAL(Write Ahead Log)に書き出します。
サーバーがクラッシュした時には、WALをリプレーすることで以前のステートに回復できます。
WALを際限のない増加から防ぐために、ZooKeeperサーバーは、メモリステートにあるものをストレージメディアに定期的にスナップショットします。
これらのスナップショットはメモリに直接読み込むことができ、スナップショットより前の全てのWALエントリは破棄され得ます。
ZooKeeperアンサンブルの作成 
以下のマニフェストはHeadless Service 、Service 、PodDisruptionBudget 、StatefulSet を含んでいます。
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  zk-hs
    labels :
      app :  zk
 spec :
    ports :
    - port :  2888 
      name :  server
    - port :  3888 
      name :  leader-election
    clusterIP :  None
    selector :
      app :  zk
 --- 
 apiVersion :  v1
 kind :  Service
 metadata :
    name :  zk-cs
    labels :
      app :  zk
 spec :
    ports :
    - port :  2181 
      name :  client
    selector :
      app :  zk
 --- 
 apiVersion :  policy/v1
 kind :  PodDisruptionBudget
 metadata :
    name :  zk-pdb
 spec :
    selector :
      matchLabels :
        app :  zk
    maxUnavailable :  1 
 --- 
 apiVersion :  apps/v1
 kind :  StatefulSet
 metadata :
    name :  zk
 spec :
    selector :
      matchLabels :
        app :  zk
    serviceName :  zk-hs
    replicas :  3 
    updateStrategy :
      type :  RollingUpdate
    podManagementPolicy :  OrderedReady
    template :
      metadata :
        labels :
          app :  zk
      spec :
        affinity :
          podAntiAffinity :
            requiredDuringSchedulingIgnoredDuringExecution :
              - labelSelector :
                  matchExpressions :
                    - key :  "app" 
                      operator :  In
                      values :
                      - zk
                topologyKey :  "kubernetes.io/hostname" 
        containers :
        - name :  kubernetes-zookeeper
          imagePullPolicy :  Always
          image :  "registry.k8s.io/kubernetes-zookeeper:1.0-3.4.10" 
          resources :
            requests :
              memory :  "1Gi" 
              cpu :  "0.5" 
          ports :
          - containerPort :  2181 
            name :  client
          - containerPort :  2888 
            name :  server
          - containerPort :  3888 
            name :  leader-election
          command :
          - sh
          - -c
          - "start-zookeeper \
           --servers=3 \
           --data_dir=/var/lib/zookeeper/data \
           --data_log_dir=/var/lib/zookeeper/data/log \
           --conf_dir=/opt/zookeeper/conf \
           --client_port=2181 \
           --election_port=3888 \
           --server_port=2888 \
           --tick_time=2000 \
           --init_limit=10 \
           --sync_limit=5 \
           --heap=512M \
           --max_client_cnxns=60 \
           --snap_retain_count=3 \
           --purge_interval=12 \
           --max_session_timeout=40000 \
           --min_session_timeout=4000 \
           --log_level=INFO" 
          readinessProbe :
            exec :
              command :
              - sh
              - -c
              - "zookeeper-ready 2181" 
            initialDelaySeconds :  10 
            timeoutSeconds :  5 
          livenessProbe :
            exec :
              command :
              - sh
              - -c
              - "zookeeper-ready 2181" 
            initialDelaySeconds :  10 
            timeoutSeconds :  5 
          volumeMounts :
          - name :  datadir
            mountPath :  /var/lib/zookeeper
        securityContext :
          runAsUser :  1000 
          fsGroup :  1000 
    volumeClaimTemplates :
    - metadata :
        name :  datadir
      spec :
        accessModes :  [  "ReadWriteOnce"   ]
        resources :
          requests :
            storage :  10Gi
  
ターミナルを開き、マニフェストを作成するために
kubectl apply
kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
 これはzk-hs Headless Service、zk-cs Service、zk-pdb PodDisruptionBudget、 zk StatefulSetを作成します。
service/zk-hs created
service/zk-cs created
poddisruptionbudget.policy/zk-pdb created
statefulset.apps/zk created
StatefulSetのPodを作成するStatefulSetコントローラーを監視するため、kubectl get
kubectl get pods -w -l app = zk
 zk-2 PodがRunningおよびReadyになったら、CTRL-Cでkubectlを終了してください。
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s
StatefulSetコントローラーが3つのPodを作成し、各PodはZooKeeper サーバー付きのコンテナを持ちます。
リーダーの選出のファシリテート 
匿名のネットワークにおいてリーダー選出を終了するアルゴリズムがないので、Zabはリーダー選出のための明示的なメンバーシップ設定を要します。
アンサンブルの各サーバーはユニーク識別子を持つ必要があり、全てのサーバーは識別子のグローバルセットを知っている必要があり、各識別子はネットワークアドレスと関連付けられている必要があります。
zk StatefulSetのPodのホスト名を取得するためにkubectl exec
for  i in 0  1  2; do  kubectl exec  zk-$i  -- hostname; done 
StatefulSetコントローラーは各Podに、その順序インデックスに基づくユニークなホスト名を提供します。
ホスト名は<statefulset名>-<順序インデックス>という形をとります。
zk StatefulSetのreplicasフィールドが3にセットされているので、このセットのコントローラーは、ホスト名にそれぞれzk-0、zk-1、zk-2が付いた3つのPodを作成します。
zk-0
zk-1
zk-2
ZooKeeperアンサンブルのサーバーは、ユニーク識別子として自然数を使い、それぞれのサーバーの識別子をサーバーのデータディレクトリ内のmyidというファイルに格納します。
各サーバーのmyidファイルの内容を調べるには、以下のコマンドを使います。
for  i in 0  1  2; do  echo  "myid zk- $i " ;kubectl exec  zk-$i  -- cat /var/lib/zookeeper/data/myid; done 
識別子が自然数で順序インデックスは正の整数なので、順序に1を加算することで識別子を生成できます。
myid zk-0
1
myid zk-1
2
myid zk-2
3
zk StatefulSet内の各Podの完全修飾ドメイン名(FQDN)を取得するには、以下のコマンドを使います。
for  i in 0  1  2; do  kubectl exec  zk-$i  -- hostname -f; done 
zk-hs Serviceは、全Podのためのドメインzk-hs.default.svc.cluster.localを作成します。
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
Kubernetes DNS のAレコードは、FQDNをPodのIPアドレスに解決します。
KubernetesがPodを再スケジュールした場合、AレコードはPodの新しいIPアドレスに更新されますが、Aレコードの名前は変更されません。
ZooKeeperはそのアプリケーション設定をzoo.cfgという名前のファイルに格納します。
zk-0 Pod内のzoo.cfgファイルの内容を見るには、kubectl execを使います。
kubectl exec  zk-0 -- cat /opt/zookeeper/conf/zoo.cfg
 ファイル末尾にあるserver.1、server.2、server.3のプロパティの、1、2、3はZooKeeperサーバーのmyidファイル内の識別子に対応します。
これらはzk StatefulSet内のPodのFQDNにセットされます。
clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888
合意形成 
合意(consensus)プロトコルは、各参加者の識別子がユニークであることを要件としています。
Zabプロトコル内で同じユニーク識別子を主張する2つの参加者はないものとします。
これは、システム内のプロセスが、どのプロセスがどのデータをコミットしたかを同意できるようにするために必須です。
2つのPodが同じ順序値で起動されたなら、2つのZooKeeperサーバーはどちらもそれら自身を同じサーバーとして認識してしまうでしょう。
kubectl get pods -w -l app = zk
 NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s
各PodのAレコードは、PodがReadyになった時に記入されます。そのため、ZooKeeperサーバー群のFQDNはある1つのエンドポイント、すなわちmyidファイルで設定された識別子を主張するユニークなZooKeeperサーバーに解決されます。
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
これは、ZooKeeperのzoo.cfgファイル内のserversプロパティが正しく設定されたアンサンブルを表していることを保証します。
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888
サーバーが値のコミットを試みるためにZabプロトコルを使う時、(リーダー選出が成功していて、少なくともPodのうちの2つがRunningおよびReadyならば)それぞれのサーバーは双方の合意をとって値をコミット、あるいは、(もし双方の状態が合わなければ)それを行うことに失敗します。
あるサーバーが別のサーバーを代行して書き込みを承認する状態は発生しません。
アンサンブルの健全性テスト 
最も基本的な健全性テストは、データを1つのZooKeeperサーバーに書き込み、そのデータを別のサーバーで読み取ることです。
以下のコマンドは、worldをアンサンブル内のzk-0 Podのパス/helloに書き込むのに、zkCli.shスクリプトを実行します。
kubectl exec  zk-0 -- zkCli.sh create /hello world
 WATCHER::
WatchedEvent state:SyncConnected type:None path:null
Created /hello
zk-1 Podからデータを取得するには、以下のコマンドを使います。
kubectl exec  zk-1 -- zkCli.sh get /hello
 zk-0に作成したデータは、アンサンブル内の全てのサーバーで利用できます。
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
永続的なストレージの提供 
ZooKeeperの概要 のセクションで言及したように、
ZooKeeperは全てのエントリを永続的なWALにコミットし、定期的にメモリ状態のスナップショットをストレージメディアに書き出します。
永続性を提供するためにWALを使用するのは、複製されたステートマシンを立てるために合意プロトコルを使うアプリケーションでよくあるテクニックです。
zk StatefulSetを削除するために、kubectl delete
kubectl delete statefulset zk
 statefulset.apps "zk" deleted
StatefulSet内のPodの終了を観察します。
kubectl get pods -w -l app = zk
 zk-0が完全に終了したら、CTRL-Cでkubectlを終了します。
zk-2      1/1       Terminating   0         9m
zk-0      1/1       Terminating   0         11m
zk-1      1/1       Terminating   0         10m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-2      0/1       Terminating   0         9m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-1      0/1       Terminating   0         10m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zk-0      0/1       Terminating   0         11m
zookeeper.yamlのマニフェストを再適用します。
kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
 これはzk StatefulSetオブジェクトを作成しますが、マニフェストのその他のAPIオブジェクトはすでに存在しているので変更されません。
StatefulSetコントローラーがStatefulSetのPodを再作成するのを見てみます。
kubectl get pods -w -l app = zk
 zk-2 PodがRunningおよびReadyになったら、CTRL-Cでkubectlを終了します。
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Pending   0          0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         19s
zk-0      1/1       Running   0         40s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       ContainerCreating   0         0s
zk-1      0/1       Running   0         18s
zk-1      1/1       Running   0         40s
zk-2      0/1       Pending   0         0s
zk-2      0/1       Pending   0         0s
zk-2      0/1       ContainerCreating   0         0s
zk-2      0/1       Running   0         19s
zk-2      1/1       Running   0         40s
健全性テスト で入力した値をzk-2 Podから取得するには、以下のコマンドを使います。
kubectl exec  zk-2 zkCli.sh get /hello
 zk StatefulSet内の全てのPodを終了して再作成したにもかかわらず、アンサンブルは元の値をなおも供給します。
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
zk StatefulSetのspecのvolumeClaimTemplatesフィールドは、各PodにプロビジョニングされるPersistentVolumeを指定します。
volumeClaimTemplates :
    - metadata :
        name :  datadir
        annotations :
          volume.alpha.kubernetes.io/storage-class :  anything
      spec :
        accessModes :  [  "ReadWriteOnce"   ]
        resources :
          requests :
            storage :  20Gi
 StatefulSetコントローラーは、StatefulSet内の各PodのためにPersistentVolumeClaimを生成します。
StatefulSetのPersistentVolumeClaimsを取得するために、以下のコマンドを使います。
kubectl get pvc -l app = zk
 StatefulSetがそのPodを再作成した時、StatefulSetはPodのPersistentVolumeを再マウントします。
NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
datadir-zk-0   Bound     pvc-bed742cd-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-1   Bound     pvc-bedd27d2-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
datadir-zk-2   Bound     pvc-bee0817e-bcb1-11e6-994f-42010a800002   20Gi       RWO           1h
StatefulSetのコンテナtemplateのvolumeMountsセクションは、ZooKeeperサーバーのデータディレクトリにあるPersistentVolumeをマウントします。
volumeMounts :
 name :  datadir
    mountPath :  /var/lib/zookeeper
 zk StatefulSet内のPodが(再)スケジュールされると、ZooKeeperサーバーのデータディレクトリにマウントされた同じPersistentVolumeを常に得ます。
Podが再スケジュールされたとしても、全ての書き込みはZooKeeperサーバーのWALおよび全スナップショットに行われ、永続性は残ります。
一貫性のある設定の保証 
リーダーの選出のファシリテート および合意形成 のセクションで記したように、ZooKeeperのアンサンブル内のサーバー群は、リーダーを選出しクォーラムを形成するための一貫性のある設定を必要とします。
また、プロトコルがネットワーク越しで正しく動作するために、Zabプロトコルの一貫性のある設定も必要です。
この例では、設定を直接マニフェストに埋め込むことで一貫性のある設定を達成します。
zk StatefulSetを取得しましょう。
kubectl get sts zk -o yaml
 …
command:
      - sh
      - -c
      - "start-zookeeper \
        --servers=3 \
        --data_dir=/var/lib/zookeeper/data \
        --data_log_dir=/var/lib/zookeeper/data/log \
        --conf_dir=/opt/zookeeper/conf \
        --client_port=2181 \
        --election_port=3888 \
        --server_port=2888 \
        --tick_time=2000 \
        --init_limit=10 \
        --sync_limit=5 \
        --heap=512M \
        --max_client_cnxns=60 \
        --snap_retain_count=3 \
        --purge_interval=12 \
        --max_session_timeout=40000 \
        --min_session_timeout=4000 \
        --log_level=INFO"
…
このcommandでは、ZooKeeperサーバーを開始するためにコマンドラインパラメータで設定を渡しています。
設定をアンサンブルへ渡すのには環境変数を使うこともできます。
ログの設定 
zkGenConfig.shスクリプトで生成されたファイルの1つは、ZooKeeperのログを制御します。
ZooKeeperはLog4j を使い、デフォルトではログの設定に基づいて、ログ設定に時間およびサイズベースでのローリングファイルアペンダー(ログのローテーション)を使用します。
zk StatefulSet内のPodの1つからログ設定を取得するには、以下のコマンドを使います。
kubectl exec  zk-0 cat /usr/etc/zookeeper/log4j.properties
 以下のログ設定は、ZooKeeperにログの全てを標準出力ファイルストリームに書き出す処理をさせます。
zookeeper.root.logger=CONSOLE
zookeeper.console.threshold=INFO
log4j.rootLogger=${zookeeper.root.logger}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
これはログコンテナ内のログを安全にとるための、最もシンプルと思われる方法です。
アプリケーションはログを標準出力に書き出し、Kubernetesがログのローテーションを処理してくれます。
Kubernetesは、標準出力と標準エラー出力に書き出されるアプリケーションのログがローカルストレージメディアを使い尽くさないことを保証する、健全維持ポリシーも実装しています。
Podの1つから末尾20行を取得するために、kubectl logs
kubectl logs zk-0 --tail 20 
 kubectl logsを利用するか、Kubernetes Dashboardから、標準出力または標準エラーに書き出されたアプリケーションログを参照できます。
2016-12-06 19:34:16,236 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740
2016-12-06 19:34:16,237 [myid:1] - INFO  [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client)
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749
2016-12-06 19:34:26,155 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749
2016-12-06 19:34:26,156 [myid:1] - INFO  [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client)
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750
2016-12-06 19:34:26,222 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750
2016-12-06 19:34:26,226 [myid:1] - INFO  [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client)
2016-12-06 19:34:36,151 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO  [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client)
2016-12-06 19:34:36,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO  [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client)
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO  [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client)
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO  [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client)
Kubernetesは多くのログソリューションを統合しています。
クラスターおよびアプリケーションに最も適合するログソリューションを選べます。
クラスターレベルのロギングとアグリゲーションとして、ログをローテートおよび輸送するためのサイドカーコンテナ をデプロイすることを検討してください。
非特権ユーザーの設定 
コンテナ内で特権ユーザーとしての実行をアプリケーションに許可するベストプラクティスは、議論の的です。
アプリケーションが非特権ユーザーとして動作することを組織で必須としているなら、エントリポイントがそのユーザーとして実行できるユーザーを制御するセキュリティコンテキスト を利用できます。
zk StatefulSetのPod templateは、SecurityContextを含んでいます。
securityContext :
    runAsUser :  1000 
    fsGroup :  1000 
 Podのコンテナ内で、UID 1000はzookeeperユーザーに、GID 1000はzookeeperグループにそれぞれ相当します。
zk-0 PodからのZooKeeperプロセス情報を取得してみましょう。
kubectl exec  zk-0 -- ps -elf
 securityContextオブジェクトのrunAsUserフィールドが1000にセットされているとおり、ZooKeeperプロセスは、rootとして実行される代わりにzookeeperユーザーとして実行されています。
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S zookeep+     1     0  0  80   0 -  1127 -      20:46 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
0 S zookeep+    27     1  0  80   0 - 1155556 -    20:46 ?        00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
デフォルトでは、PodのPersistentVolumeがZooKeeperサーバーのデータディレクトリにマウントされている時、rootユーザーのみがそこにアクセス可能です。
この設定はZooKeeperのプロセスがそのWALに書き込んだりスナップショットに格納したりするのを妨げることになります。
zk-0 PodのZooKeeperデータディレクトリのファイルパーミッションを取得するには、以下のコマンドを使います。
kubectl exec  -ti zk-0 -- ls -ld /var/lib/zookeeper/data
 securityContextオブジェクトのfsGroupフィールドが1000にセットされているので、PodのPersistentVolumeの所有権はzookeeperグループにセットされ、ZooKeeperのプロセスがそのデータを読み書きできるようになります。
drwxr-sr-x 3 zookeeper zookeeper 4096 Dec  5 20:45 /var/lib/zookeeper/data
ZooKeeperプロセスの管理 
ZooKeeperドキュメント では、「You will want to have a supervisory process that manages each of your ZooKeeper server processes (JVM).(各ZooKeeperサーバープロセス(JVM)を管理する監督プロセスを持ちたくなります)」と述べています。
分散型システム内で失敗したプロセスを再起動するのにwatchdog(監督プロセス)を使うのは、典型的パターンです。
アプリケーションをKubernetesにデプロイする時には、監督プロセスのような外部ユーティリティを使うよりもむしろ、アプリケーションのwatchdogとしてKubernetesを使うべきです。
アンサンブルのアップデート 
zk StatefulSetはRollingUpdateアップデート戦略を使うように設定されています。
サーバーに割り当てられるcpusの数を更新するのに、kubectl patchを利用できます。
kubectl patch sts zk --type= 'json'  -p= '[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]' 
 statefulset.apps/zk patched
更新の状況を見るには、kubectl rollout statusを使います。
kubectl rollout status sts/zk
 waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-5db4499664...
これはPod群を終了し、逆の順番で1つずつそれらを新しい設定で再作成します。
これはクォーラムがローリングアップデート中に維持されることを保証します。
履歴や過去の設定を見るには、kubectl rollout historyコマンドを使います。
kubectl rollout history  sts/zk
 出力は次のようになります:
statefulsets "zk"
REVISION
1
2
変更をロールバックするには、kubectl rollout undoコマンドを使います。
kubectl rollout undo sts/zk
 出力は次のようになります:
statefulset.apps/zk rolled back
プロセスの失敗の取り扱い 
再起動ポリシー は、Pod内のコンテナのエントリポイントへのプロセスの失敗をKubernetesがどのように取り扱うかを制御します。
StatefulSet内のPodにおいて唯一妥当なRestartPolicyはAlwaysで、これはデフォルト値です。
ステートフルなアプリケーションでは、このデフォルトポリシーの上書きは絶対にすべきではありません 。
zk-0 Pod内で実行されているZooKeeperサーバーのプロセスツリーを調査するには、以下のコマンドを使います。
kubectl exec  zk-0 -- ps -ef
 コンテナのエントリポイントとして使われるコマンドはPID 1、エントリポイントの子であるZooKeeperプロセスはPID 27となっています。
UID        PID  PPID  C STIME TTY          TIME CMD
zookeep+     1     0  0 15:03 ?        00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
zookeep+    27     1  0 15:03 ?        00:00:03 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
別のターミナルで、以下のコマンドを使ってzk StatefulSet内のPodを見てみます。
kubectl get pod -w -l app = zk
 別のターミナルで、以下のコマンドを使ってPod zk-0内のZooKeeperプロセスを終了します。
kubectl exec  zk-0 -- pkill java
 ZooKeeperプロセスの終了は、その親プロセスの終了を引き起こします。
コンテナのRestartPolicyはAlwaysなので、親プロセスが再起動(restart)されます。
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          21m
zk-1      1/1       Running   0          20m
zk-2      1/1       Running   0          19m
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Error     0          29m
zk-0      0/1       Running   1         29m
zk-0      1/1       Running   1         29m
アプリケーションが、そのビジネスロジックを実装するプロセスを立ち上げるのにスクリプト(zkServer.shなど)を使っている場合、スクリプトは子プロセスとともに終了する必要があります。
これは、Kubernetesがアプリケーションのコンテナを、そのビジネスロジックを実装しているプロセスが失敗した時に再起動することを保証します。
生存性(liveness)テスト 
失敗したプロセスを再起動するための設定をアプリケーションに施すのは、分散型システムの健全さを保つのに十分ではありません。
システムのプロセスが生きていることもあれば無反応なこともあり、あるいはそうでなく不健全という状況もあります。
アプリケーションのプロセスが不健全で再起動すべきであることをKubernetesに通知するには、liveness probeを使うのがよいでしょう。
zk StatefulSetのPod templateでliveness probeを指定します。
   livenessProbe :
      exec :
        command :
        - sh
        - -c
        - "zookeeper-ready 2181" 
      initialDelaySeconds :  15 
      timeoutSeconds :  5 
 プローブはサーバーの健全さをテストするのに、ZooKeeperのruok 4文字コマンドを使うbashスクリプトを呼び出します。
OK=$(echo ruok | nc 127.0.0.1 $1)
if [ "$OK" == "imok" ]; then
    exit 0
else
    exit 1
fi
ターミナルウィンドウで、zk StatefulSet内のPodを見るのに以下のコマンドを使います。
kubectl get pod -w -l app = zk
 別のウィンドウで、Pod zk-0のファイルシステムからzookeeper-readyスクリプトを削除するために以下のコマンドを使います。
kubectl exec  zk-0 -- rm /opt/zookeeper/bin/zookeeper-ready
 ZooKeeperプロセスの失敗のためにliveness probeを使う時、アンサンブル内の不健全なプロセスが再起動されることを保証するために、Kubernetesは自動的にプロセスを再起動します。
kubectl get pod -w -l app = zk
 NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   0          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS    RESTARTS   AGE
zk-0      0/1       Running   0          1h
zk-0      0/1       Running   1         1h
zk-0      1/1       Running   1         1h
準備性(readiness)テスト 
準備性は生存性と同じではありません。
プロセスが生きているのであれば、スケジュールされ健全です。
プロセスの準備ができたら、入力を処理できます。
生存性はなくてはならないものですが、準備性の状態には十分ではありません。
プロセスは生きてはいるが準備はできていない時、特に初期化および終了の間がそのケースに相当します。
readiness probeを指定するとKubernetesは、準備性チェックに合格するまで、アプリケーションのプロセスがネットワークトラフィックを受け取らないことを保証します。
ZooKeeperサーバーにとって、健全性は準備性を意味します。
そのため、zookeeper.yamlマニフェストからのreadiness probeは、liveness probeと同一です。
   readinessProbe :
      exec :
        command :
        - sh
        - -c
        - "zookeeper-ready 2181" 
      initialDelaySeconds :  15 
      timeoutSeconds :  5 
 liveness probeとreadiness probeが同一だとしても、両方を指定することが重要です。
これは、ZooKeeperアンサンブル内の健全なサーバーだけがネットワークトラフィックを受け取ることを保証します。
ノードの失敗の許容 
ZooKeeperはデータの変更を正しくコミットするのにサーバーのクォーラムを必要とします。
3つのサーバーのアンサンブルにおいては、書き込みの成功のために2つのサーバーは健全でなければなりません。
クォーラムベースのシステムにおいて、可用性を保証するために、メンバーは障害ドメインにデプロイされます。
個々のマシンの損失による障害を避けるためのベストプラクティスは、同じマシン上でアプリケーションの複数のインスタンスがコロケート(同じ場所に配置)されないようにすることです。
デフォルトでKubernetesは、同じノードのStatefulSetにPodをコロケートします。
3つのサーバーアンサンブルを作成していたとして、2つのサーバーが同じノードにあり、そのノードが障害を起こした場合、ZooKeeperサービスのクライアントは、少なくともPodの1つが再スケジュールされるまで障害に見舞われることになります。
クリティカルシステムのプロセスがノードの失敗イベントで再スケジュールできるよう、追加のキャパシティを常にプロビジョンしておくべきです。
そうしておけば、障害は単にKubernetesのスケジューラーがZooKeeperのサーバーの1つを再スケジュールするまでの辛抱です。
ただし、ダウンタイムなしでノードの障害への耐性をサービスに持たせたいなら、podAntiAffinityをセットすべきです。
zk StatefulSet内のPodのノードを取得するには、以下のコマンドを使います。
for  i in 0  1  2; do  kubectl get pod zk-$i  --template {{ .spec.nodeName}} ; echo  "" ; done 
zk StatefulSet内の全てのPodは、別々のノードにデプロイされます。
kubernetes-node-cxpk
kubernetes-node-a5aq
kubernetes-node-2g2d
これはzk StatefulSet内のPodにPodAntiAffinityの指定があるからです。
affinity :
    podAntiAffinity :
      requiredDuringSchedulingIgnoredDuringExecution :
        - labelSelector :
            matchExpressions :
              - key :  "app" 
                operator :  In
                values :
                  - zk
          topologyKey :  "kubernetes.io/hostname" 
 requiredDuringSchedulingIgnoredDuringExecutionフィールドは、topologyKeyで定義されたドメイン内でappラベルの値がzkの2つのPodが絶対にコロケートすべきでないことを、Kubernetes Schedulerに指示します。
topologyKeyのkubernetes.io/hostnameは、ドメインが固有ノードであることを示しています。
異なるルール、ラベル、セレクターを使って、物理・ネットワーク・電源といった障害ドメイン全体に広がるアンサンブルにこのテクニックを広げることができます。
メンテナンス時の存続 
このセクションでは、ノードをcordon(スケジュール不可化)およびdorain(解放)します。もし共有クラスターでこのチュートリアルを試しているのであれば、これがほかのテナントに有害な影響を及ぼさないことを確認してください。
前のセクションでは、計画外のノード障害に備えてどのようにPodをノード全体に広げるかを示しましたが、計画されたメンテナンスのため引き起こされる一時的なノード障害に対して計画する必要もあります。
クラスター内のノードを取得するために、以下のコマンドを使います。
このチュートリアルでは、4つのノードのあるクラスターを仮定しています。
クラスターが4つよりも多くある場合には、4つのノード以外全てをcordonするためにkubectl cordon
zk-pdbのPodDisruptionBudgetを取得するために、以下のコマンドを使います。
max-unavailableフィールドは、zk StatefulSetの最大で1つのPodがいつでも利用できなくなる可能性があるということを、Kubernetesに指示します。
NAME      MIN-AVAILABLE   MAX-UNAVAILABLE   ALLOWED-DISRUPTIONS   AGE
zk-pdb    N/A             1                 1
1つ目のターミナルで、zk StatefulSet内のPodを見るのに以下のコマンドを使います。
kubectl get pods -w -l app = zk
 次に別のターミナルで、Podが現在スケジュールされているノードを取得するために、以下のコマンドを使います。
for  i in 0  1  2; do  kubectl get pod zk-$i  --template {{ .spec.nodeName}} ; echo  "" ; done 
出力は次のようになります:
kubernetes-node-pb41
kubernetes-node-ixsl
kubernetes-node-i4c4
zk-0 Podがスケジュールされているノードをcordonおよびdrainするには、kubectl drain
kubectl drain $( kubectl get pod zk-0 --template {{ .spec.nodeName}} )  --ignore-daemonsets --force --delete-emptydir-data
 出力は次のようになります:
node "kubernetes-node-pb41" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-pb41, kube-proxy-kubernetes-node-pb41; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-o5elz
pod "zk-0" deleted
node "kubernetes-node-pb41" drained
クラスターに4つのノードがあるので、kubectl drainは成功し、zk-0が別のノードに再スケジュールされます。
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
最初のターミナルでStatefulSetのPodを見守り、zk-1がスケジュールされたノードをdrainします。
kubectl drain $( kubectl get pod zk-1 --template {{ .spec.nodeName}} )  --ignore-daemonsets --force --delete-emptydir-data
 出力は次のようになります:
"kubernetes-node-ixsl" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-ixsl, kube-proxy-kubernetes-node-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74
pod "zk-1" deleted
node "kubernetes-node-ixsl" drained
zk StatefulSetがPodのコロケーションを抑止するPodAntiAffinityルールを含んでいるので、zk-1 Podはスケジュールされず、またスケジュール可能なのは2つのノードだけなので、PodはPendingの状態のままになっています。
kubectl get pods -w -l app = zk
 出力は次のようになります:
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
zk-1      1/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
StatefulSetのPodを見続け、zk-2がスケジュールされているノードをdrainします。
kubectl drain $( kubectl get pod zk-2 --template {{ .spec.nodeName}} )  --ignore-daemonsets --force --delete-emptydir-data
 出力は次のようになります:
node "kubernetes-node-i4c4" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4
There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget.
pod/zk-2
kubectlを終了するためにCTRL-Cを押します。
zk-2を退去させるとzk-budget違反になってしまうので、3つ目のノードはdrainできません。ただし、ノードはcordonされたままとなります。
健全性テスト中に入力した値をzk-0から取得するには、zkCli.shを使います。
kubectl exec  zk-0 zkCli.sh get /hello
 PodDisruptionBudgetが遵守されているので、サービスはまだ利用可能です。
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Dec 07 00:08:59 UTC 2016
mZxid = 0x200000002
mtime = Wed Dec 07 00:08:59 UTC 2016
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
最初のノードをuncordon(スケジュール可能化)するには、kubectl uncordon
kubectl uncordon kubernetes-node-pb41
 出力は次のようになります:
node "kubernetes-node-pb41" uncordoned
zk-1はこのノードで再スケジュールされます。zk-1がRunningおよびReadyになるまで待ちます。
kubectl get pods -w -l app = zk
 出力は次のようになります:
NAME      READY     STATUS    RESTARTS   AGE
zk-0      1/1       Running   2          1h
zk-1      1/1       Running   0          1h
zk-2      1/1       Running   0          1h
NAME      READY     STATUS        RESTARTS   AGE
zk-0      1/1       Terminating   2          2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Terminating   2         2h
zk-0      0/1       Pending   0         0s
zk-0      0/1       Pending   0         0s
zk-0      0/1       ContainerCreating   0         0s
zk-0      0/1       Running   0         51s
zk-0      1/1       Running   0         1m
zk-1      1/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Terminating   0         2h
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         0s
zk-1      0/1       Pending   0         12m
zk-1      0/1       ContainerCreating   0         12m
zk-1      0/1       Running   0         13m
zk-1      1/1       Running   0         13m
試しにzk-2がスケジュールされているノードをdrainしてみます。
kubectl drain $( kubectl get pod zk-2 --template {{ .spec.nodeName}} )  --ignore-daemonsets --force --delete-emptydir-data
 出力は次のようになります:
node "kubernetes-node-i4c4" already cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
pod "heapster-v1.2.0-2604621511-wht1r" deleted
pod "zk-2" deleted
node "kubernetes-node-i4c4" drained
今度はkubectl drainは成功しました。
zk-2の再スケジュールができるように、2つ目のノードをuncordonします。
kubectl uncordon kubernetes-node-ixsl
 出力は次のようになります:
node "kubernetes-node-ixsl" uncordoned
サービスがメンテナンス中も利用可能なままであることを保証するために、PodDisruptionBudgetsとあわせてkubectl drainを利用できます。
メンテナンスでノードがオフラインになる前にノードをcordonして、Podを退去させるのにdrainが使われている場合、Disruption Budget(停止状態の予算)を表すサービスは遵守すべきバジェットを持ちます。
クリティカルサービスでは、Podをすぐに再スケジュールできるよう、追加のキャパティを常に割り当てておくべきです。
クリーンアップ 
クラスターの全てのノードをuncordonするために、kubectl uncordonを実行してください。 
このチュートリアルで使ったPersistentVolumeの永続的なストレージメディアを削除する必要があります。
全てのストレージが回収されたことを確実とするために、お使いの環境、ストレージ設定、プロビジョニング方法に基いて必要な手順に従ってください。 
 
 
    
	
  
    
	
  
    
    
	
    
    
	
7 - Service 
    
	
    
      
  
  
  
  
  
  
  
    
    
	
    
    
	7.1 - アプリケーションをServiceに接続する 
    
	
コンテナに接続するためのKubernetesモデル 
さて、継続的に実行され、複製されたアプリケーションができたので、これをネットワーク上に公開できます。
Kubernetesは、Podがどのホストに配置されているかにかかわらず、ほかのPodと通信できることを引き受けます。
Kubernetesは各Podにそれぞれ固有のクラスタープライベートなIPアドレスを付与するので、Pod間のリンクや、コンテナのポートとホストのポートのマップを明示的に作成する必要はありません。
これは、Pod内のコンテナは全てlocalhost上でお互いのポートに到達でき、クラスター内の全てのPodはNATなしに互いを見られるということを意味します。このドキュメントの残りの部分では、このようなネットワークモデルの上で信頼性のあるServiceを実行する方法について、詳しく述べていきます。
このチュートリアルでは概念のデモンストレーションのために、シンプルなnginx Webサーバーを例として使います。
Podをクラスターへ公開 
これは前出の例でも行いましたが、もう一度やってみて、ネットワークからの観点に着目してみましょう。
nginx Podを作成し、コンテナのポート指定も記載します:
    
    apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    name :  my-nginx
 spec :
    selector :
      matchLabels :
        run :  my-nginx
    replicas :  2 
    template :
      metadata :
        labels :
          run :  my-nginx
      spec :
        containers :
        - name :  my-nginx
          image :  nginx
          ports :
          - containerPort :  80 
 
  
この設定で、あなたのクラスターにはどのノードからもアクセス可能になります。Podを実行中のノードを確認してみましょう:
kubectl apply -f ./run-my-nginx.yaml
 kubectl get pods -l run = my-nginx -o wide
 NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd
PodのIPアドレスを確認します:
kubectl get pods -l run = my-nginx -o custom-columns= POD_IP:.status.podIPs
     POD_IP
     [ map[ ip:10.244.3.4]] 
     [ map[ ip:10.244.2.5]] 
 あなたのクラスター内のどのノードにもsshで入ることができて、双方のIPアドレスに対して問い合わせるためにcurlのようなツールを使えるようにしておくのがよいでしょう。
各コンテナはノード上でポート80を使っておらず 、トラフィックをPodに流すための特別なNATルールもなんら存在しないことに注意してください。
つまり、全て同じcontainerPortを使った同じノード上で複数のnginx Podを実行でき、Serviceに割り当てられたIPアドレスを使って、クラスター内のほかのどのPodあるいはノードからもそれらにアクセスできます。
背後にあるPodにフォワードするためにホストNode上の特定のポートを充てたいというのであれば、それも可能です。とはいえ、ネットワークモデルではそのようなことをする必要がありません。
興味があれば、さらなる詳細について
Kubernetesネットワークモデル 
を読んでください。
Serviceの作成 
さて、フラットなクラスター全体のアドレス空間内でnginxを実行中のPodが得られました。
理論的にはこれらのPodと直接対話することは可能ですが、ノードが死んでしまった時には何が起きるでしょうか?
ノードと一緒にPodは死に、Deploymentが新しいPodを異なるIPアドレスで作成します。
これがServiceが解決する問題です。
KubernetesのServiceは、全て同じ機能を提供する、クラスター内のどこかで実行するPodの論理的な集合を定義した抽象物です。
作成時に各Serviceは固有のIPアドレス(clusterIPとも呼ばれます)を割り当てられます。
このアドレスはServiceのライフスパンと結び付けられており、Serviceが生きている間は変わりません。
PodはServiceと対話できるよう設定され、Serviceのメンバーである複数のPodへ自動的に負荷分散されたServiceへ通信する方法を知っています。
kubectl exposeを使って、2つのnginxレプリカのためのServiceを作成できます:
kubectl expose deployment/my-nginx
 service/my-nginx exposed
これはkubectl apply -fを以下のyamlに対して実行するのと同じです:
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  my-nginx
    labels :
      run :  my-nginx
 spec :
    ports :
    - port :  80 
      protocol :  TCP
    selector :
      run :  my-nginx
  
この指定は、run: my-nginxラベルの付いた任意のPod上のTCPポート80を宛先とし、それを抽象化されたServiceポート(targetPortはコンテナがトラフィックを許可するポート、portは抽象化されたServiceポートで、ほかのPodがServiceにアクセスするのに使う任意のポートです)で公開するServiceを作成します。
Service定義内でサポートされているフィールドのリストを見るには、Service  APIオブジェクトを参照してください。
Serviceを確認してみましょう:
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s
前述したとおり、ServiceはPodのグループに支えられています。
これらのPodはEndpointSlices を通して公開されています。
Serviceのセレクターは継続的に評価され、その結果はServiceに接続されているEndpointSliceにlabels を使って「投稿(POST)」されます。
Podが死ぬと、エンドポイントとして含まれていたEndpointSliceからそのPodは自動的に削除されます。
Serviceのセレクターにマッチする新しいPodが、Serviceのために自動的にEndpointSliceに追加されます。
エンドポイントを確認し、IPアドレスが最初のステップで作成したPodと同じであることに注目してください:
kubectl describe svc my-nginx
 Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP Family Policy:    SingleStack
IP Families:         IPv4
IP:                  10.0.162.149
IPs:                 10.0.162.149
Port:                <unset> 80/TCP
TargetPort:          80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get endpointslices -l kubernetes.io/service-name= my-nginx
 NAME             ADDRESSTYPE   PORTS   ENDPOINTS               AGE
my-nginx-7vzhx   IPv4          80      10.244.2.5,10.244.3.4   21s
今や、あなたのクラスター内のどのノードからもnginx Serviceに<CLUSTER-IP>:<PORT>でcurlを使用してアクセスできるはずです。Service IPは完全に仮想であり、物理的なケーブルで接続されるものではありません。どのように動作しているのか興味があれば、さらなる詳細についてサービスプロキシ を読んでください。
Serviceへのアクセス 
KubernetesはServiceを探す2つの主要なモードとして、環境変数とDNSをサポートしています。
前者はすぐに動かせるのに対し、後者はCoreDNSクラスターアドオン が必要です。
備考: もしServiceの環境変数が望ましくないなら(想定しているプログラムの環境変数と競合する可能性がある、処理する変数が多すぎる、DNSだけ使いたい、など)、
pod spec で
enableServiceLinksのフラグを
falseにすることで、このモードを無効化できます。
環境変数 
PodをNode上で実行する時、kubeletはアクティブなServiceのそれぞれに環境変数のセットを追加します。
これは順序問題を生みます。なぜそうなるかの理由を見るために、実行中のnginx Podの環境を調査してみましょう(Podの名前は環境によって異なります):
kubectl exec  my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
 KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
Serviceについて何も言及がないことに注意してください。
これは、Serviceの前にレプリカを作成したからです。
このようにすることでの不利益のもう1つは、スケジューラーが同一のマシンに両方のPodを置く可能性があることです(もしそのマシンが死ぬと全Serviceがダウンしてしまいます)。
2つのPodを殺し、Deploymentがそれらを再作成するのを待つことで、これを正しいやり方にできます。
今回は、レプリカの前に Serviceが存在します。
これにより、正しい環境変数だけでなく、(全てのノードで等量のキャパシティを持つ場合)Podに広がった、スケジューラーレベルのServiceが得られます:
kubectl scale deployment my-nginx --replicas= 0; kubectl scale deployment my-nginx --replicas= 2;
 
 kubectl get pods -l run = my-nginx -o wide
 NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m
Podが、いったん殺されて再作成された後、異なる名前を持ったことに気付いたでしょうか。
kubectl exec  my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
 KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443
DNS 
Kubernetesは、DNS名をほかのServiceに自動的に割り当てる、DNSクラスターアドオンServiceを提供しています。
クラスター上でそれを実行しているならば、確認できます:
kubectl get services kube-dns --namespace= kube-system
 NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m
本セクションの以降では、長寿命のIPアドレス(my-nginx)を持つServiceと、そのIPアドレスに名前を割り当てているDNSサーバーがあることを想定しています。
ここではCoreDNSクラスターアドオン(アプリケーション名kube-dns)を使い、標準的な手法(例えばgethostbyname())を使ってクラスター内の任意のPodからServiceと対話してみます。
CoreDNSが動作していない時には、
CoreDNS README 
やCoreDNSのインストール を参照して有効化してください。
テストするために、別のcurlアプリケーションを実行してみましょう:
kubectl run curl --image= radial/busyboxplus:curl -i --tty
 Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt
次にenterを押し、nslookup my-nginxを実行します:
[  root@curl-131556218-9fnch:/ ] $ nslookup my-nginx
Server:    10.0.0.10
 Address 1: 10.0.0.10
 
 Name:      my-nginx
 Address 1: 10.0.162.149
 Serviceのセキュア化 
これまではクラスター内からnginxサーバーだけにアクセスしてきました。
Serviceをインターネットに公開する前に、通信経路がセキュアかどうかを確かめたいところです。
そのためには次のようなものが必要です:
https用の自己署名証明書(まだ本人証明を用意していない場合) 
証明書を使うよう設定されたnginxサーバー 
証明書をPodからアクセスできるようにするSecret  
 
これら全てはnginx https example から取得できます。
go環境とmakeツールのインストールが必要です
(もしこれらをインストールしたくないときには、後述の手動手順に従ってください)。簡潔には:
make keys KEY = /tmp/nginx.key CERT = /tmp/nginx.crt
 kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
 secret/nginxsecret created
NAME                  TYPE                                  DATA      AGE
nginxsecret           kubernetes.io/tls                     2         1m
configmapも同様:
kubectl create configmap nginxconfigmap --from-file= default.conf
 configmap/nginxconfigmap created
NAME             DATA   AGE
nginxconfigmap   1      114s
以下に示すのは、makeを実行したときに問題が発生する場合(例えばWindowsなど)の手動手順です:
# 公開鍵と秘密鍵のペアを作成する 
openssl req -x509 -nodes -days 365  -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx" 
 # 鍵をbase64エンコーディングに変換する 
cat /d/tmp/nginx.crt | base64
 cat /d/tmp/nginx.key | base64
 以下のようなyamlファイルを作成するために、前のコマンドからの出力を使います。
base64エンコードされた値は、全て1行で記述する必要があります。
apiVersion :  "v1" 
 kind :  "Secret" 
 metadata :
    name :  "nginxsecret" 
    namespace :  "default" 
 type :  kubernetes.io/tls
 data :
    # 注意: 以下の値はご自身で base64 エンコードした証明書と鍵に置き換えてください。 
    tls.crt :  "REPLACE_WITH_BASE64_CERT"  
    tls.key :  "REPLACE_WITH_BASE64_KEY" 
 では、このファイルを使ってSecretを作成します:
kubectl apply -f nginxsecrets.yaml
 kubectl get secrets
 NAME                  TYPE                                  DATA      AGE
nginxsecret           kubernetes.io/tls                     2         1m
Secretにある証明書を使ってhttpsサーバーを開始するために、nginxレプリカを変更します。また、Serviceは(80および443の)両方のポートを公開するようにします:
    
    apiVersion :  v1
 kind :  Service
 metadata :
    name :  my-nginx
    labels :
      run :  my-nginx
 spec :
    type :  NodePort
    ports :
    - port :  8080 
      targetPort :  80 
      protocol :  TCP
      name :  http
    - port :  443 
      protocol :  TCP
      name :  https
    selector :
      run :  my-nginx
 --- 
 apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    name :  my-nginx
 spec :
    selector :
      matchLabels :
        run :  my-nginx
    replicas :  1 
    template :
      metadata :
        labels :
          run :  my-nginx
      spec :
        volumes :
        - name :  secret-volume
          secret :
            secretName :  nginxsecret
        containers :
        - name :  nginxhttps
          image :  bprashanth/nginxhttps:1.0
          ports :
          - containerPort :  443 
          - containerPort :  80 
          volumeMounts :
          - mountPath :  /etc/nginx/ssl
            name :  secret-volume
  
nginx-secure-appマニフェストの注目すべきポイント:
DeploymentとServiceの指定の両方が同じファイルに含まれています。 
nginxサーバー は、ポート80でHTTPトラフィック、ポート443でHTTPSトラフィックをサービスし、nginx Serviceは両方のポートを公開します。各コンテナは、/etc/nginx/sslにマウントされたボリューム経由で鍵にアクセスできます。
これはnginxサーバーが開始する前 にセットアップされます。 
 
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml
 この時点で、任意のノードからnginxサーバーに到達できます。
kubectl get pods -l run = my-nginx -o custom-columns= POD_IP:.status.podIPs
     POD_IP
     [ map[ ip:10.244.3.5]] 
 node $ curl -k https://10.244.3.5
 ...
 <h1>Welcome to nginx!</h1>
 最後の手順でcurlに-kパラメーターを与えていることに注意してください。
これは、証明書の生成時点ではnginxを実行中のPodについて何もわからないので、curlにCNameのミスマッチを無視するよう指示する必要があるからです。
Serviceを作成することで、証明書で使われているCNameと、PodがServiceルックアップ時に使う実際のDNS名とがリンクされます。
Podからこれをテストしてみましょう(単純化のため同じSecretが再利用されるので、ServiceにアクセスするのにPodが必要なのはnginx.crtだけです):
    
    apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    name :  curl-deployment
 spec :
    selector :
      matchLabels :
        app :  curlpod
    replicas :  1 
    template :
      metadata :
        labels :
          app :  curlpod
      spec :
        volumes :
        - name :  secret-volume
          secret :
            secretName :  nginxsecret
        containers :
        - name :  curlpod
          command :
          - sh
          - -c
          - while true; do sleep 1; done
          image :  radial/busyboxplus:curl
          volumeMounts :
          - mountPath :  /etc/nginx/ssl
            name :  secret-volume
  
kubectl apply -f ./curlpod.yaml
 kubectl get pods -l app = curlpod
 NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec  curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
 ...
 <title>Welcome to nginx!</title>
 ...
 Serviceの公開 
アプリケーションのいくつかの部分においては、Serviceを外部IPアドレスで公開したいと思うかもしれません。
Kubernetesはこれに対して2つのやり方をサポートしています: NodePortとLoadBalancerです。
前のセクションで作成したServiceではすでにNodePortを使っていたので、ノードにパブリックIPアドレスがあれば、nginx HTTPSレプリカもトラフィックをインターネットでサービスする準備がすでに整っています。
kubectl get svc my-nginx -o yaml | grep nodePort -C 5 
   uid: 07191fb3-f61a-11e5-8ae5-42010af00002
 spec:
   clusterIP: 10.0.162.149
   ports:
   - name: http
     nodePort: 31704 
     port: 8080 
     protocol: TCP
     targetPort: 80 
   - name: https
     nodePort: 32453 
     port: 443 
     protocol: TCP
     targetPort: 443 
   selector:
     run: my-nginx
 kubectl get nodes -o yaml | grep ExternalIP -C 1 
     - address: 104.197.41.11
       type: ExternalIP
     allocatable:
 --
     - address: 23.251.152.56
       type: ExternalIP
     allocatable:
 ...
 
 $ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
 ...
 <h1>Welcome to nginx!</h1>
 では、クラウドロードバランサーを使うために、Serviceを再作成してみましょう。
my-nginxのTypeをNodePortからLoadBalancerに変更してください:
kubectl edit svc my-nginx
 kubectl get svc my-nginx
 NAME       TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   LoadBalancer   10.0.162.149     xx.xxx.xxx.xxx     8080:30163/TCP        21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>
EXTERNAL-IP列のIPアドレスが、パブリックインターネットで利用可能なものになっています。
CLUSTER-IPはクラスター/プライベートクラウドネットワーク内でのみ利用可能なものです。
AWSにおいては、LoadBalancerタイプは、IPアドレスではなく(長い)ホスト名を使うELBを作成することに注意してください。
これは標準のkubectl get svcの出力に合わせるには長すぎ、実際それを見るにはkubectl describe service my-nginxを使う必要があります。
これは以下のような見た目になります:
kubectl describe service my-nginx
 ...
 LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
 ...
 次の項目 
 
    
	
  
    
    
	
    
    
	7.2 - 送信元IPを使用する 
    
	
Kubernetesクラスター内で実行されているアプリケーションは、Serviceという抽象化を経由して、他のアプリケーションや外の世界との発見や通信を行います。このドキュメントでは、異なる種類のServiceに送られたパケットの送信元IPに何が起こるのか、そして必要に応じてこの振る舞いを切り替える方法について説明します。
始める前に 
用語 
このドキュメントでは、以下の用語を使用します。
NAT ネットワークアドレス変換(network address translation) 
送信元NAT パケットの送信元のIPを置換します。このページでは、通常ノードのIPアドレスを置換することを意味します。 
送信先NAT パケットの送信先のIPを置換します。このページでは、通常Pod のIPアドレスを置換することを意味します。 
VIP Kubernetes内のすべてのService などに割り当てられる仮想IPアドレス(virtual IP address)です。 
kube-proxy すべてのノード上でServiceのVIPを管理するネットワークデーモンです。 
 
前提条件 
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。
このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。
まだクラスターがない場合、minikube を使って作成するか、
以下のいずれかのKubernetesプレイグラウンドも使用できます:
以下の例では、HTTPヘッダー経由で受け取ったリクエストの送信元IPをエコーバックする、小さなnginxウェブサーバーを使用します。次のコマンドでウェブサーバーを作成できます。
kubectl create deployment source-ip-app --image= registry.k8s.io/echoserver:1.10
 出力は次のようになります。
deployment.apps/source-ip-app created
目標 
単純なアプリケーションを様々な種類のService経由で公開する 
それぞれの種類のServiceがどのように送信元IPのNATを扱うかを理解する 
送信元IPを保持することに関わるトレードオフを理解する 
 
Type=ClusterIPを使用したServiceでの送信元IPkube-proxyがiptablesモード (デフォルト)で実行されている場合、クラスター内部からClusterIPに送られたパケットに送信元のNATが行われることは決してありません。kube-proxyが実行されているノード上でhttp://localhost:10249/proxyModeにリクエストを送って、kube-proxyのモードを問い合わせてみましょう。
出力は次のようになります。
NAME                           STATUS     ROLES    AGE     VERSION
kubernetes-node-6jst   Ready      <none>   2h      v1.13.0
kubernetes-node-cx31   Ready      <none>   2h      v1.13.0
kubernetes-node-jj1t   Ready      <none>   2h      v1.13.0
これらのノードの1つでproxyモードを取得します(kube-proxyはポート10249をlistenしています)。
# このコマンドは、問い合わせを行いたいノード上のシェルで実行してください。 
curl http://localhost:10249/proxyMode
 出力は次のようになります。
iptables
source IPアプリのServiceを作成することで、送信元IPが保持されているかテストできます。
kubectl expose deployment source-ip-app --name= clusterip --port= 80  --target-port= 8080 
 出力は次のようになります。
service/clusterip exposed
kubectl get svc clusterip
 出力は次のようになります。
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
clusterip    ClusterIP   10.0.170.92   <none>        80/TCP    51s
そして、同じクラスター上のPodからClusterIPにアクセスします。
kubectl run busybox -it --image= busybox --restart= Never --rm
 出力は次のようになります。
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
これで、Podの内部でコマンドが実行できます。
# このコマンドは、"kubectl run" のターミナルの内部で実行してください 
ip addr
 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue
    link/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff
    inet 10.244.3.8/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::188a:84ff:feb0:26a5/64 scope link
       valid_lft forever preferred_lft forever
そして、wgetを使用してローカルのウェブサーバーに問い合わせます。
# "10.0.170.92" の部分をService名が"clusterip"のIPv4アドレスに置き換えてください 
wget -qO - 10.0.170.92
 CLIENT VALUES:
client_address=10.244.3.8
command=GET
...
client_addressは常にクライアントのPodのIPアドレスになります。これは、クライアントのPodとサーバーのPodが同じノード内にあっても異なるノードにあっても変わりません。
Type=NodePortを使用したServiceでの送信元IPType=NodePortNodePort Serviceを作ることでテストできます。
kubectl expose deployment source-ip-app --name= nodeport --port= 80  --target-port= 8080  --type= NodePort
 出力は次のようになります。
service/nodeport exposed
NODEPORT = $( kubectl get -o jsonpath = "{.spec.ports[0].nodePort}"  services nodeport) 
NODES = $( kubectl get nodes -o jsonpath = '{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }' ) 
クラウドプロバイダーで実行する場合、上に示したnodes:nodeportに対してファイアウォールのルールを作成する必要があるかもしれません。それでは、上で割り当てたノードポート経由で、クラスターの外部からServiceにアクセスしてみましょう。
for  node in $NODES ; do  curl -s $node :$NODEPORT  | grep -i client_address; done 
出力は次のようになります。
client_address=10.180.1.1
client_address=10.240.0.5
client_address=10.240.0.3
これらは正しいクライアントIPではなく、クラスターのinternal IPであることがわかります。ここでは、次のようなことが起こっています。
クライアントがパケットをnode2:nodePortに送信する 
node2は、パケット内の送信元IPアドレスを自ノードのIPアドレスに置換する(SNAT)node2は、パケット内の送信先IPアドレスをPodのIPアドレスに置換するパケットはnode1にルーティングされ、endpointにルーティングされる 
Podからの応答がnode2にルーティングされて戻ってくる 
Podからの応答がクライアントに送り返される 
 
図で表すと次のようになります。
    
graph LR;
  client(client)-->node2[Node 2];
  node2-->client;
  node2-. SNAT .->node1[Node 1];
  node1-. SNAT .->node2;
  node1-->endpoint(Endpoint);
  classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
  classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
  class node1,node2,endpoint k8s;
  class client plain;
 
  
    このコンテンツを表示するには、JavaScriptを有効に する必要があります 
   
 
クライアントのIPが失われることを回避するために、Kubernetesにはクライアントの送信元IPを保持する 機能があります。service.spec.externalTrafficPolicyの値をLocalに設定すると、kube-proxyはローカルに存在するエンドポイントへのプロキシリクエストだけをプロキシし、他のノードへはトラフィックを転送しなくなります。このアプローチでは、オリジナルの送信元IPアドレスが保持されます。ローカルにエンドポイントが存在しない場合には、そのノードに送信されたパケットは損失します。そのため、エンドポイントに到達するパケットに適用する可能性のあるパケット処理ルールでは、送信元IPが正しいことを信頼できます。
次のようにしてservice.spec.externalTrafficPolicyフィールドを設定します。
kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}' 
 出力は次のようになります。
service/nodeport patched
そして、再度テストしてみます。
for  node in $NODES ; do  curl --connect-timeout 1  -s $node :$NODEPORT  | grep -i client_address; done 
出力は次のようになります。
client_address=198.51.100.79
今度は、正しい クライアントIPが含まれる応答が1つだけ得られました。これは、エンドポイントのPodが実行されているノードから来たものです。
ここでは、次のようなことが起こっています。
クライアントがパケットをエンドポイントが存在しないnode2:nodePortに送信する 
パケットが損失する 
クライアントがパケットをエンドポイントが存在する node1:nodePortに送信する 
node1は、正しい送信元IPを持つパケットをエンドポイントにルーティングする 
 
図で表すと次のようになります。
    
graph TD;
  client --> node1[Node 1];
  client(client) --x node2[Node 2];
  node1 --> endpoint(endpoint);
  endpoint --> node1;
  classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
  classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
  class node1,node2,endpoint k8s;
  class client plain;
 
  
    このコンテンツを表示するには、JavaScriptを有効に する必要があります 
   
 
Type=LoadBalancerを使用したServiceでの送信元IPType=LoadBalancerReady状態にあるすべてのスケジュール可能なKubernetesのNodeは、ロードバランサーからのトラフィックを受付可能であるためです。そのため、エンドポイントが存在しないノードにパケットが到達した場合、システムはエンドポイントが存在する ノードにパケットをプロシキーします。このとき、(前のセクションで説明したように)パケットの送信元IPがノードのIPに置換されます。
ロードバランサー経由でsource-ip-appを公開することで、これをテストできます。
kubectl expose deployment source-ip-app --name= loadbalancer --port= 80  --target-port= 8080  --type= LoadBalancer
 出力は次のようになります。
service/loadbalancer exposed
ServiceのIPアドレスを表示します。
kubectl get svc loadbalancer
 出力は次のようになります。
NAME           TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE
loadbalancer   LoadBalancer   10.0.65.118   203.0.113.140     80/TCP    5m
次に、Serviceのexternal-ipにリクエストを送信します。
出力は次のようになります。
CLIENT VALUES:
client_address=10.240.0.5
...
しかし、Google Kubernetes EngineやGCE上で実行している場合、同じservice.spec.externalTrafficPolicyフィールドをLocalに設定すると、ロードバランサーからのトラフィックを受け付け可能なノードのリストから、Serviceエンドポイントが存在しない ノードが強制的に削除されます。この動作は、ヘルスチェックを意図的に失敗させることによって実現されています。
図で表すと次のようになります。
アノテーションを設定することで動作をテストできます。
kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}' 
 Kubernetesにより割り当てられたservice.spec.healthCheckNodePortフィールドをすぐに確認します。
kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort
 出力は次のようになります。
   healthCheckNodePort :  32122 
 service.spec.healthCheckNodePortフィールドは、/healthzでhealth checkを配信しているすべてのノード上のポートを指しています。次のコマンドでテストできます。
kubectl get pod -o wide -l run = source-ip-app
 出力は次のようになります。
NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE
source-ip-app-826191075-qehz4   1/1       Running   0          20h       10.180.1.136   kubernetes-node-6jst
curlを使用して、さまざまなノード上の/healthzエンドポイントからデータを取得します。
# このコマンドは選んだノードのローカル上で実行してください 
curl localhost:32122/healthz
 1 Service Endpoints found
ノードが異なると、得られる結果も異なる可能性があります。
# このコマンドは、選んだノード上でローカルに実行してください 
curl localhost:32122/healthz
 No Service Endpoints Found
コントロールプレーン 上で実行中のコントローラーは、クラウドのロードバランサーを割り当てる責任があります。同じコントローラーは、各ノード上のポートやパスを指すHTTPのヘルスチェックも割り当てます。エンドポイントが存在しない2つのノードがヘルスチェックに失敗するまで約10秒待った後、curlを使用してロードバランサーのIPv4アドレスに問い合わせます。
出力は次のようになります。
CLIENT VALUES:
client_address=198.51.100.79
...
クロスプラットフォームのサポート 
Type=LoadBalancerを使用したServiceで送信元IPを保持する機能を提供しているのは一部のクラウドプロバイダだけです。実行しているクラウドプロバイダによっては、以下のように異なる方法でリクエストを満たす場合があります。
クライアントとのコネクションをプロキシが終端し、ノードやエンドポイントとの接続には新しいコネクションが開かれる。このような場合、送信元IPは常にクラウドのロードバランサーのものになり、クライアントのIPにはなりません。
 
クライアントからロードバランサーのVIPに送信されたリクエストが、中間のプロキシではなく、クライアントの送信元IPとともにノードまで到達するようなパケット転送が使用される。
 
 
1つめのカテゴリーのロードバランサーの場合、真のクライアントIPと通信するために、 HTTPのForwarded ヘッダーやX-FORWARDED-FOR ヘッダー、proxy protocol などの、ロードバランサーとバックエンドの間で合意されたプロトコルを使用する必要があります。2つ目のカテゴリーのロードバランサーの場合、Serviceのservice.spec.healthCheckNodePortフィールドに保存されたポートを指すHTTPのヘルスチェックを作成することで、上記の機能を活用できます。
クリーンアップ 
Serviceを削除します。
kubectl delete svc -l app = source-ip-app
 Deployment、ReplicaSet、Podを削除します。
kubectl delete deployment source-ip-app
 次の項目 
 
    
	
  
    
    
	
    
    
	7.3 - Podとそのエンドポイントの終了動作を探る 
    
	
アプリケーションをServiceに接続する で概略を示したステップに従ってアプリケーションをServiceに接続すると、ネットワーク上で公開され、継続的に実行されて、複製されたアプリケーションが得られます。
このチュートリアルでは、Podを終了する流れを見て、gracefulな(猶予のある)接続ドレインを実装する手法を模索するための手助けをします。
Podの終了手続きとそのエンドポイント 
アップグレードやスケールダウンのために、Podを終了しなければならない場面はままあります。
アプリケーションの可用性を高めるために、適切なアクティブ接続ドレインを実装することは重要でしょう。
このチュートリアルでは概念のデモンストレーションのために、シンプルなnginx Webサーバーを例として、対応するエンドポイントの状態に関連したPodの終了および削除の流れを説明します。
エンドポイント終了の流れの例 
以下は、Podの終了 ドキュメントに記載されている流れの例です。
1つのnginxレプリカを含むDeployment(純粋にデモンストレーション目的です)とServiceがあるとします:
    
    apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    name :  nginx-deployment
    labels :
      app :  nginx
 spec :
    replicas :  1 
    selector :
      matchLabels :
        app :  nginx
    template :
      metadata :
        labels :
          app :  nginx
      spec :
        terminationGracePeriodSeconds :  120   # 非常に長い猶予期間 
        containers :
        - name :  nginx
          image :  nginx:latest
          ports :
          - containerPort :  80 
          lifecycle :
            preStop :
              exec :
                # 実際の活動終了はterminationGracePeriodSecondsまでかかる可能性がある。 
                # この例においては、少なくともterminationGracePeriodSecondsの間は待機し、 
                # 120秒経過すると、コンテナは強制的に終了される。 
                # この間ずっとnginxはリクエストを処理し続けていることに注意。 
                command :  [
                  "/bin/sh" ,  "-c" ,  "sleep 180" 
                ]
  
apiVersion :  apps/v1
 kind :  Deployment
 metadata :
    name :  nginx-deployment
    labels :
      app :  nginx
 spec :
    replicas :  1 
    selector :
      matchLabels :
        app :  nginx
    template :
      metadata :
        labels :
          app :  nginx
      spec :
        terminationGracePeriodSeconds :  120   # 非常に長い猶予期間 
        containers :
        - name :  nginx
          image :  nginx:latest
          ports :
          - containerPort :  80 
          lifecycle :
            preStop :
              exec :
                # 実際の活動終了はterminationGracePeriodSecondsまでかかる可能性がある。 
                # この例においては、少なくともterminationGracePeriodSecondsの間は待機し、 
                # 120秒経過すると、コンテナは強制終了される。 
                # この間ずっとnginxはリクエストを処理し続けていることに注意。 
                command :  [
                  "/bin/sh" ,  "-c" ,  "sleep 180" 
                ]
 
 --- 
 
 apiVersion :  v1
 kind :  Service
 metadata :
    name :  nginx-service
 spec :
    selector :
      app :  nginx
    ports :
      - protocol :  TCP
        port :  80 
        targetPort :  80 
 PodとServiceが実行中になったら、関連付けられたEndpointSliceの名前を得られます:
kubectl get endpointslice
 この出力は以下のようなものになります:
NAME                  ADDRESSTYPE   PORTS   ENDPOINTS                 AGE
nginx-service-6tjbr   IPv4          80      10.12.1.199,10.12.1.201   22m
状態からわかるように、1つのエンドポイントが登録されていることが確認できます:
kubectl get endpointslices -o json -l kubernetes.io/service-name= nginx-service
 この出力は以下のようなものになります:
{
    "addressType": "IPv4",
    "apiVersion": "discovery.k8s.io/v1",
    "endpoints": [
        {
            "addresses": [
                "10.12.1.201"
            ],
            "conditions": {
                "ready": true,
                "serving": true,
                "terminating": false
では、Podを終了し、そのPodがgracefulな終了期間設定を守って終了されていることを確認してみましょう:
kubectl delete pod nginx-deployment-7768647bf9-b4b9s
 全Podについて調べます:
この出力は以下のようなものになります:
NAME                                READY   STATUS        RESTARTS      AGE
nginx-deployment-7768647bf9-b4b9s   1/1     Terminating   0             4m1s
nginx-deployment-7768647bf9-rkxlw   1/1     Running       0             8s
新しいPodがスケジュールされたことを見てとれます。
新しいPodのために新しいエンドポイントが作成される間、古いエンドポイントは終了中の状態のまま残っています:
kubectl get endpointslice -o json nginx-service-6tjbr
 この出力は以下のようなものになります:
{
    "addressType": "IPv4",
    "apiVersion": "discovery.k8s.io/v1",
    "endpoints": [
        {
            "addresses": [
                "10.12.1.201"
            ],
            "conditions": {
                "ready": false,
                "serving": true,
                "terminating": true
            },
            "nodeName": "gke-main-default-pool-dca1511c-d17b",
            "targetRef": {
                "kind": "Pod",
                "name": "nginx-deployment-7768647bf9-b4b9s",
                "namespace": "default",
                "uid": "66fa831c-7eb2-407f-bd2c-f96dfe841478"
            },
            "zone": "us-central1-c"
        },
        {
            "addresses": [
                "10.12.1.202"
            ],
            "conditions": {
                "ready": true,
                "serving": true,
                "terminating": false
            },
            "nodeName": "gke-main-default-pool-dca1511c-d17b",
            "targetRef": {
                "kind": "Pod",
                "name": "nginx-deployment-7768647bf9-rkxlw",
                "namespace": "default",
                "uid": "722b1cbe-dcd7-4ed4-8928-4a4d0e2bbe35"
            },
            "zone": "us-central1-c"
これを使うと、終了中のアプリケーションがその状態について、接続ドレイン機能の実装目的でクライアント(ロードバランサーなど)と通信する、ということが可能です。
これらのクライアントではエンドポイントの終了を検出し、そのための特別なロジックを実装できます。
Kubernetesでは、終了中のエンドポイントのready状態は全てfalseにセットされます。
これは後方互換性のために必要な措置で、既存のロードバランサーは通常のトラフィックにはそれを使用しません。
Podの終了時にトラフィックのドレインが必要な場合、実際に準備できているかはserving状態として調べられます。
Podが削除される時には、古いエンドポイントも削除されます。
次の項目