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

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

監視、ログ、デバッグ

クラスターのトラブルシューティングや、コンテナ化したアプリケーションのデバッグのために、監視とログをセットアップします。

1 - crictlによるKubernetesノードのデバッグ

FEATURE STATE: Kubernetes v1.11 [stable]

crictlはCRI互換のコンテナランタイム用のコマンドラインインターフェイスです。

これを使って、Kubernetesノード上のコンテナランタイムやアプリケーションの検査やデバッグを行うことができます。 crictlとそのソースコードはcri-toolsリポジトリにホストされています。

始める前に

crictlにはCRIランタイムを搭載したLinuxが必要です。

crictlのインストール

cri-toolsのリリースページから、いくつかの異なるアーキテクチャ用の圧縮アーカイブcrictlをダウンロードできます。

お使いのKubernetesのバージョンに対応するバージョンをダウンロードしてください。 それを解凍してシステムパス上の/usr/local/bin/などの場所に移動します。

一般的な使い方

crictlコマンドにはいくつかのサブコマンドとランタイムフラグがあります。 詳細はcrictl helpまたはcrictl <subcommand> helpを参照してください。 crictlはデフォルトではunix:///var/run/dockershim.sockに接続します。

他のランタイムの場合は、複数の異なる方法でエンドポイントを設定することができます:

  • フラグ--runtime-endpoint--image-endpointの設定により
  • 環境変数CONTAINER_RUNTIME_ENDPOINTIMAGE_SERVICE_ENDPOINTの設定により
  • 設定ファイル--config=/etc/crictl.yamlでエンドポイントの設定により

また、サーバーに接続する際のタイムアウト値を指定したり、デバッグを有効/無効にしたりすることもできます。 これには、設定ファイルでtimeoutdebugを指定するか、--timeout--debugのコマンドラインフラグを使用します。

現在の設定を表示または編集するには、/etc/crictl.yamlの内容を表示または編集します。

cat /etc/crictl.yaml
runtime-endpoint: unix:///var/run/dockershim.sock
image-endpoint: unix:///var/run/dockershim.sock
timeout: 10
debug: true

crictlコマンドの例

以下の例では、いくつかのcrictlコマンドとその出力例を示しています。

podsの一覧

すべてのポッドをリストアップ:

crictl pods

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

POD ID              CREATED              STATE               NAME                         NAMESPACE           ATTEMPT
926f1b5a1d33a       About a minute ago   Ready               sh-84d7dcf559-4r2gq          default             0
4dccb216c4adb       About a minute ago   Ready               nginx-65899c769f-wv2gp       default             0
a86316e96fa89       17 hours ago         Ready               kube-proxy-gblk4             kube-system         0
919630b8f81f1       17 hours ago         Ready               nvidia-device-plugin-zgbbv   kube-system         0

Podを名前でリストアップします:

crictl pods --name nginx-65899c769f-wv2gp

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

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

Podをラベルでリストアップします:

crictl pods --label run=nginx

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

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

イメージの一覧

すべてのイメージをリストアップします:

crictl images

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

IMAGE                                     TAG                 IMAGE ID            SIZE
busybox                                   latest              8c811b4aec35f       1.15MB
k8s-gcrio.azureedge.net/hyperkube-amd64   v1.10.3             e179bbfe5d238       665MB
k8s-gcrio.azureedge.net/pause-amd64       3.1                 da86e6ba6ca19       742kB
nginx                                     latest              cd5239a0906a6       109MB

イメージをリポジトリでリストアップします:

crictl images nginx

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

IMAGE               TAG                 IMAGE ID            SIZE
nginx               latest              cd5239a0906a6       109MB

イメージのIDのみをリストアップします:

crictl images -q

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

sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
sha256:e179bbfe5d238de6069f3b03fccbecc3fb4f2019af741bfff1233c4d7b2970c5
sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e
sha256:cd5239a0906a6ccf0562354852fae04bc5b52d72a2aff9a871ddb6bd57553569

List containers

すべてのコンテナをリストアップします:

crictl ps -a

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

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   7 minutes ago       Running             sh                         1
9c5951df22c78       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   8 minutes ago       Exited              sh                         0
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     8 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   18 hours ago        Running             kube-proxy                 0

ランニングコンテナをリストアップします:

crictl ps

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

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   6 minutes ago       Running             sh                         1
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     7 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   17 hours ago        Running             kube-proxy                 0

実行中のコンテナでコマンドの実行

crictl exec -i -t 1f73f2d81bf98 ls

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

bin   dev   etc   home  proc  root  sys   tmp   usr   var

コンテナログの取得

すべてのコンテナログを取得します:

crictl logs 87d3992f84f74

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

10.240.0.96 - - [06/Jun/2018:02:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

最新のN行のログのみを取得します:

crictl logs --tail=1 87d3992f84f74

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

10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

Podサンドボックスの実行

crictlを使ってPodサンドボックスを実行することは、コンテナのランタイムをデバッグするのに便利です。 稼働中のKubernetesクラスタでは、サンドボックスは最終的にKubeletによって停止され、削除されます。

  1. 以下のようなJSONファイルを作成します:

    {
        "metadata": {
            "name": "nginx-sandbox",
            "namespace": "default",
            "attempt": 1,
            "uid": "hdishd83djaidwnduwk28bcsb"
        },
        "logDirectory": "/tmp",
        "linux": {
        }
    }
    
  2. JSONを適用してサンドボックスを実行するには、crictl runpコマンドを使用します:

    crictl runp pod-config.json
    

    サンドボックスのIDが返されます。

コンテナの作成

コンテナの作成にcrictlを使うと、コンテナのランタイムをデバッグするのに便利です。 稼働中のKubernetesクラスタでは、サンドボックスは最終的にKubeletによって停止され、削除されます。

  1. busyboxイメージをプルします:

    crictl pull busybox
    Image is up to date for busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
    
  2. Podとコンテナのコンフィグを作成します:

    Pod config:

    {
        "metadata": {
            "name": "nginx-sandbox",
            "namespace": "default",
            "attempt": 1,
            "uid": "hdishd83djaidwnduwk28bcsb"
        },
        "log_directory": "/tmp",
        "linux": {
        }
    }
    

    Container config:

    {
      "metadata": {
          "name": "busybox"
      },
      "image":{
          "image": "busybox"
      },
      "command": [
          "top"
      ],
      "log_path":"busybox.log",
      "linux": {
      }
    }
    
  3. 先に作成されたPodのID、コンテナの設定ファイル、Podの設定ファイルを渡して、コンテナを作成します。コンテナのIDが返されます。

    crictl create f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f container-config.json pod-config.json
    
  4. すべてのコンテナをリストアップし、新しく作成されたコンテナの状態がCreatedに設定されていることを確認します:

    crictl ps -a
    

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

    CONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT
    3e025dd50a72d       busybox             32 seconds ago      Created             busybox             0
    

コンテナの起動

コンテナを起動するには、そのコンテナのIDをcrictl startに渡します:

crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

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

3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

コンテナの状態が「Running」に設定されていることを確認します:

crictl ps

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

CONTAINER ID        IMAGE               CREATED              STATE               NAME                ATTEMPT
3e025dd50a72d       busybox             About a minute ago   Running             busybox             0

詳しくはkubernetes-sigs/cri-toolsをご覧ください。

docker cliからcrictlへのマッピング

以下のマッピング表の正確なバージョンは、docker cli v1.40crictl v1.19.0のものです。 この一覧はすべてを網羅しているわけではないことに注意してください。 たとえば、docker cliの実験的なコマンドは含まれていません。

デバッグ情報の取得

mapping from docker cli to crictl - retrieve debugging information
docker cli crictl 説明 サポートされていない機能
attach attach 実行中のコンテナにアタッチ --detach-keys, --sig-proxy
exec exec 実行中のコンテナでコマンドの実行 --privileged, --user, --detach-keys
images images イメージのリストアップ  
info info システム全体の情報の表示  
inspect inspect, inspecti コンテナ、イメージ、タスクの低レベルの情報を返します  
logs logs コンテナのログを取得します --details
ps ps コンテナのリストアップ  
stats stats コンテナのリソース使用状況をライブで表示 Column: NET/BLOCK I/O, PIDs
version version ランタイム(Docker、ContainerD、その他)のバージョン情報を表示します  

変更を行います

mapping from docker cli to crictl - perform changes
docker cli crictl 説明 サポートされていない機能
create create 新しいコンテナを作成します  
kill stop (timeout = 0) 1つ以上の実行中のコンテナを停止します --signal
pull pull レジストリーからイメージやリポジトリをプルします --all-tags, --disable-content-trust
rm rm 1つまたは複数のコンテナを削除します  
rmi rmi 1つまたは複数のイメージを削除します  
run run 新しいコンテナでコマンドを実行  
start start 停止した1つまたは複数のコンテナを起動 --detach-keys
stop stop 実行中の1つまたは複数のコンテナの停止  
update update 1つまたは複数のコンテナの構成を更新 --restart--blkio-weightとその他

crictlでのみ対応

mapping from docker cli to crictl - supported only in crictl
crictl 説明
imagefsinfo イメージファイルシステムの情報を返します
inspectp 1つまたは複数のPodの状態を表示します
port-forward ローカルポートをPodに転送します
runp 新しいPodを実行します
rmp 1つまたは複数のPodを削除します
stopp 稼働中の1つまたは複数のPodを停止します

2 - Initコンテナのデバッグ

このページでは、Initコンテナの実行に関連する問題を調査する方法を説明します。以下のコマンドラインの例では、Podを<pod-name>、Initコンテナを<init-container-1>および<init-container-2>として参照しています。

始める前に

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

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

Initコンテナのステータスを確認する

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

kubectl get pod <pod-name>

たとえば、Init:1/2というステータスは、2つのInitコンテナのうちの1つが正常に完了したことを示します。

NAME         READY     STATUS     RESTARTS   AGE
<pod-name>   0/1       Init:1/2   0          7s

ステータス値とその意味の例については、Podのステータスを理解するを参照してください。

Initコンテナの詳細を取得する

Initコンテナの実行に関する詳細情報を表示します:

kubectl describe pod <pod-name>

たとえば、2つのInitコンテナを持つPodでは、次のように表示されます:

Init Containers:
  <init-container-1>:
    Container ID:    ...
    ...
    State:           Terminated
      Reason:        Completed
      Exit Code:     0
      Started:       ...
      Finished:      ...
    Ready:           True
    Restart Count:   0
    ...
  <init-container-2>:
    Container ID:    ...
    ...
    State:           Waiting
      Reason:        CrashLoopBackOff
    Last State:      Terminated
      Reason:        Error
      Exit Code:     1
      Started:       ...
      Finished:      ...
    Ready:           False
    Restart Count:   3
    ...

また、Pod Specのstatus.initContainerStatusesフィールドを読むことでプログラムでInitコンテナのステータスにアクセスすることもできます。:

kubectl get pod nginx --template '{{.status.initContainerStatuses}}'

このコマンドは生のJSONで上記と同じ情報を返します。

Initコンテナのログにアクセスする

ログにアクセスするには、Initコンテナ名とPod名を渡します。

kubectl logs <pod-name> -c <init-container-2>

シェルスクリプトを実行するInitコンテナは、実行時にコマンドを出力します。たとえば、スクリプトの始めにset -xを実行することでBashで同じことができます。

Podのステータスを理解する

Init:で始まるPodステータスはInitコンテナの実行ステータスを要約します。以下の表は、Initコンテナのデバッグ中に表示される可能性のあるステータス値の例をいくつか示しています。

ステータス 意味
Init:N/M PodはM個のInitコンテナを持ち、これまでにN個完了しました。
Init:Error Initコンテナが実行に失敗しました。
Init:CrashLoopBackOff Initコンテナが繰り返し失敗しました。
Pending PodはまだInitコンテナの実行を開始していません。
PodInitializing or Running PodはすでにInitコンテナの実行を終了しています。

3 - PodとReplicationControllerのデバッグ

このページでは、PodとReplicationControllerをデバッグする方法を説明します。

始める前に

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

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

Podのデバッグ

Podのデバッグの最初のステップは、Podを調べることです。 次のコマンドで、Podの現在の状態と最近のイベントを確認して下さい。

kubectl describe pods ${POD_NAME}

Pod内のコンテナの状態を確認します。 コンテナはすべてRunning状態ですか?最近再起動はしましたか?

Podの状態に応じてデバッグを続けます。

PodがPending状態にとどまっている

PodがPending状態でスタックしている場合、ノードにスケジュールできていないことを意味します。 一般的に、これは、何らかのタイプのリソースが不足しており、それによってスケジューリングを妨げられているためです。 上述のkubectl describe...コマンドの出力を確認してください。 Podをスケジュールできない理由に関するスケジューラーからのメッセージがあるはずです。 理由としては以下のようなものがあります。

リソースが不十分

クラスター内のCPUまたはメモリーの供給を使い果たした可能性があります。 この場合、いくつかのことを試すことができます。

  • クラスターにノードを追加します。

  • 不要なPodを終了して、 Pending状態のPodのための空きリソースを作ります。

  • Podがノードよりも大きくないことを確認します。 例えば、すべてのノードのキャパシティーがcpu: 1の場合、cpu: 1.1を要求するPodは決してスケジュールされません。

    kubectl get nodes -o <format>コマンドでノードのキャパシティーを確認できます。 必要な情報を抽出するコマンドラインの例を以下に示します。

    kubectl get nodes -o yaml | egrep '\sname:|cpu:|memory:'
    kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, cap: .status.capacity}'
    

    リソースクォータ機能では、 消費できるリソースの合計量を制限するように構成できます。 Namespaceと組み合わせて使用すると、1つのチームがすべてのリソースを占有することを防ぐことができます。

hostPortの使用

PodをhostPortにバインドすると、Podをスケジュールできる場所の数が制限されます。 ほとんどの場合、hostPortは不要です。Serviceオブジェクトを使用してPodを公開してください。 どうしてもhostPortが必要な場合は、コンテナクラスター内のノードと同じ数のPodのみをスケジュールできます。

PodがWaiting状態にとどまっている

PodがWaiting状態でスタックしている場合、Podはワーカーノードにスケジュールされていますが、そのマシンでは実行できない状態です。 この場合も、kubectl describe ...の情報が参考になるはずです。 PodがWaiting状態となる最も一般的な原因は、イメージをプルできないことです。 確認すべき事項が3つあります。

  • イメージの名前が正しいことを確認して下さい。
  • イメージはリポジトリーにプッシュしましたか?
  • マシンで手動でdocker pull <image>を実行し、イメージをプルできるかどうかを確認して下さい。

Podがクラッシュする、あるいはUnhealthy状態

Podがスケジュールされると、動作中のPodをデバッグするに説明されている方法がデバッグに使用可能です。

ReplicationControllerのデバッグ

ReplicationControllerはかなり明快です。Podを作成できるか、できないかのどちらかです。 Podを作成できない場合は、上述の手順を参照してPodをデバッグしてください。

kubectl describe rc ${CONTROLLER_NAME}を使用して、レプリケーションコントローラーに関連するイベントを調べることもできます。

4 - Pod障害の原因を特定する

このページでは、コンテナ終了メッセージの読み書き方法を説明します。

終了メッセージは、致命的なイベントに関する情報を、ダッシュボードや監視ソフトウェアなどのツールで簡単に取得して表示できる場所にコンテナが書き込むための手段を提供します。 ほとんどの場合、終了メッセージに入力した情報も一般的なKubernetesログに書き込まれるはずです。

始める前に

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

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

終了メッセージの書き込みと読み取り

この課題では、1つのコンテナを実行するPodを作成します。 設定ファイルには、コンテナの開始時に実行されるコマンドを指定します。

apiVersion: v1
kind: Pod
metadata:
  name: termination-demo
spec:
  containers:
  - name: termination-demo-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
  1. YAML設定ファイルに基づいてPodを作成します:

     kubectl apply -f https://k8s.io/examples/debug/termination.yaml
    

    YAMLファイルのcommandフィールドとargsフィールドで、コンテナが10秒間スリープしてから/dev/termination-logファイルに「Sleep expired」と書いているのがわかります。コンテナが「Sleep expired」メッセージを書き込んだ後、コンテナは終了します。

  2. Podに関する情報を表示します:

     kubectl get pod termination-demo
    

    Podが実行されなくなるまで、上記のコマンドを繰り返します。

  3. Podに関する詳細情報を表示します:

     kubectl get pod termination-demo --output=yaml
    

    出力には「Sleep expired」メッセージが含まれています:

     apiVersion: v1
     kind: Pod
     ...
         lastState:
           terminated:
             containerID: ...
             exitCode: 0
             finishedAt: ...
             message: |
               Sleep expired
             ...
    
  4. Goテンプレートを使用して、終了メッセージのみが含まれるように出力をフィルタリングします:

     kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
    

終了メッセージのカスタマイズ

Kubernetesは、コンテナのterminationMessagePathフィールドで指定されている終了メッセージファイルから終了メッセージを取得します。デフォルト値は/dev/termination-logです。このフィールドをカスタマイズすることで、Kubernetesに別のファイルを使うように指示できます。Kubernetesは指定されたファイルの内容を使用して、成功と失敗の両方についてコンテナのステータスメッセージを入力します。

終了メッセージはアサーションエラーメッセージのように、最終状態を簡潔に示します。kubeletは4096バイトより長いメッセージは切り詰めます。全コンテナの合計メッセージの長さの上限は12キビバイトです。デフォルトの終了メッセージのパスは/dev/termination-logです。Pod起動後に終了メッセージのパスを設定することはできません。

次の例では、コンテナはKubernetesが取得するために終了メッセージを/tmp/my-logに書き込みます:

apiVersion: v1
kind: Pod
metadata:
  name: msg-path-demo
spec:
  containers:
  - name: msg-path-demo-container
    image: debian
    terminationMessagePath: "/tmp/my-log"

さらに、ユーザーは追加のカスタマイズをするためにContainerのterminationMessagePolicyフィールドを設定できます。このフィールドのデフォルト値はFileです。これは、終了メッセージが終了メッセージファイルからのみ取得されることを意味します。terminationMessagePolicyFallbackToLogsOnErrorに設定することで、終了メッセージファイルが空でコンテナがエラーで終了した場合に、コンテナログ出力の最後のチャンクを使用するようにKubernetesに指示できます。ログ出力は、2048バイトまたは80行のどちらか小さい方に制限されています。

次の項目

5 - Serviceのデバッグ

新規にKubernetesをインストールした環境でかなり頻繁に発生する問題は、Serviceが適切に機能しないというものです。Deployment(または他のワークロードコントローラー)を通じてPodを実行し、サービスを作成したにもかかわらず、アクセスしようとしても応答がありません。何が問題になっているのかを理解するのに、このドキュメントがきっと役立つでしょう。

Pod内でコマンドを実行する

ここでの多くのステップでは、クラスターで実行されているPodが見ているものを確認する必要があります。これを行う最も簡単な方法は、インタラクティブなalpineのPodを実行することです。

kubectl run -it --rm --restart=Never alpine --image=alpine sh

使用したい実行中のPodがすでにある場合は、以下のようにしてそのPod内でコマンドを実行できます。

kubectl exec <POD-NAME> -c <CONTAINER-NAME> -- <COMMAND>

セットアップ

このドキュメントのウォークスルーのために、いくつかのPodを実行しましょう。おそらくあなた自身のServiceをデバッグしているため、あなた自身の詳細に置き換えることもできますし、これに沿って2番目のデータポイントを取得することもできます。

kubectl create deployment hostnames --image=k8s.gcr.io/serve_hostname
deployment.apps/hostnames created

kubectlコマンドは作成、変更されたリソースのタイプと名前を出力するため、この後のコマンドで使用することもできます。

Deploymentを3つのレプリカにスケールさせてみましょう。

kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled

これは、次のYAMLでDeploymentを開始した場合と同じです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostnames
  labels:
    app: hostnames
spec:
  selector:
    matchLabels:
      app: hostnames
  replicas: 3
  template:
    metadata:
      labels:
        app: hostnames
    spec:
      containers:
      - name: hostnames
        image: k8s.gcr.io/serve_hostname

"app"ラベルはkubectl create deploymentによって、Deploymentの名前に自動的にセットされます。

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

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          2m
hostnames-632524106-ly40y   1/1       Running   0          2m
hostnames-632524106-tlaok   1/1       Running   0          2m

Podが機能していることも確認できます。Pod IP アドレスリストを取得し、直接テストできます。

kubectl get pods -l app=hostnames \
    -o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7

このウォークスルーに使用されるサンプルコンテナは、ポート9376でHTTPを介して独自のホスト名を提供するだけですが、独自のアプリをデバッグする場合は、Podがリッスンしているポート番号を使用する必要があります。

Pod内から実行します。

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

次のように表示されます。

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

この時点で期待通りの応答が得られない場合、Podが正常でないか、想定しているポートでリッスンしていない可能性があります。なにが起きているかを確認するためにkubectl logsが役立ちます。Podに直接に入りデバッグする場合はkubectl execが必要になります。

これまでにすべての計画が完了していると想定すると、Serviceが機能しない理由を調査することができます。

Serviceは存在するか?

賢明な読者は、Serviceをまだ実際に作成していないことにお気付きかと思いますが、これは意図的です。これは時々忘れられるステップであり、最初に確認すべきことです。

存在しないServiceにアクセスしようとするとどうなるでしょうか?このServiceを名前で利用する別のPodがあると仮定すると、次のような結果が得られます。

wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'

最初に確認するのは、そのServiceが実際に存在するかどうかです。

kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found

Serviceを作成しましょう。前と同様に、これはウォークスルー用です。ご自身のServiceの詳細を使用することもできます。

kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed

そして、念のため内容を確認します。

kubectl get svc hostnames
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
hostnames   ClusterIP   10.0.1.175   <none>        80/TCP    5s

これで、Serviceが存在することがわかりました。

前と同様に、これは次のようなYAMLでServiceを開始した場合と同じです。

apiVersion: v1
kind: Service
metadata:
  name: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - name: default
    protocol: TCP
    port: 80
    targetPort: 9376

構成の全範囲をハイライトするため、ここで作成したServiceはPodとは異なるポート番号を使用します。多くの実際のServiceでは、これらのポートは同じになる場合があります。

サービスはDNS名によって機能しているか?

クライアントがサービスを使用する最も一般的な方法の1つは、DNS名を使用することです。同じNamespaceのPodから次のコマンドを実行してください。

nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

これが失敗した場合、おそらくPodとServiceが異なるNamespaceにあるため、ネームスペースで修飾された名前を試してください。(Podの中からもう一度)

nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

これが機能する場合、クロスネームスペース名を使用するようにアプリケーションを調整するか、同じNamespaceでアプリとServiceを実行する必要があります。これでも失敗する場合は、完全修飾名を試してください。

nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

ここでのサフィックス"default.svc.cluster.local"に注意してください。"default"は、操作しているNamespaceです。"svc"は、これがServiceであることを示します。"cluster.local"はクラスタードメインであり、あなたのクラスターでは異なる場合があります。

クラスター内のノードからも試すこともできます。

nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server:         10.0.0.10
Address:        10.0.0.10#53

Name:   hostnames.default.svc.cluster.local
Address: 10.0.1.175

完全修飾名では検索できるのに、相対名ではできない場合、Podの/etc/resolv.confファイルが正しいことを確認する必要があります。Pod内から実行します。

cat /etc/resolv.conf

次のように表示されます。

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5

nameserver行はクラスターのDNS Serviceを示さなければなりません。これは、--cluster-dnsフラグでkubeletに渡されます。

search行には、Service名を見つけるための適切なサフィックスを含める必要があります。この場合、ローカルのNamespaceServiceを見つけるためのサフィックス(default.svc.cluster.local)、すべてのNamespacesServiceを見つけるためのサフィックス(svc.cluster.local)、およびクラスターのサフィックス(cluster.local)です。インストール方法によっては、その後に追加のレコードがある場合があります(合計6つまで)。クラスターのサフィックスは、--cluster-domainフラグを使用してkubeletに渡されます。このドキュメントではそれが"cluster.local"であると仮定していますが、あなたのクラスターでは異なる場合があります。その場合は、上記のすべてのコマンドでクラスターのサフィックスを変更する必要があります。

options行では、DNSクライアントライブラリーが検索パスをまったく考慮しないようにndotsを十分に高く設定する必要があります。Kubernetesはデフォルトでこれを5に設定します。これは、生成されるすべてのDNS名をカバーするのに十分な大きさです。

DNS名で機能するServiceはあるか?

上記がまだ失敗する場合、DNSルックアップがServiceに対して機能していません。一歩離れて、他の何が機能していないかを確認しましょう。KubernetesマスターのServiceは常に機能するはずです。Pod内から実行します。

nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local

これが失敗する場合は、このドキュメントのkube-proxyセクションを参照するか、このドキュメントの先頭に戻って最初からやり直してください。ただし、あなた自身のServiceをデバッグするのではなく、DNSサービスをデバッグします。

ServiceはIPでは機能するか?

DNSサービスが正しく動作できると仮定すると、次にテストするのはIPによってServiceが動作しているかどうかです。上述のkubectl getで確認できるIPに、クラスター内のPodからアクセスします。

for i in $(seq 1 3); do
    wget -qO- 10.0.1.175:80
done

次のように表示されます。

hostnames-0uton
hostnames-bvc05
hostnames-yp2kp

Serviceが機能している場合は、正しい応答が得られるはずです。そうでない場合、おかしい可能性のあるものがいくつかあるため、続けましょう。

Serviceは正しく定義されているか?

馬鹿げているように聞こえるかもしれませんが、Serviceが正しく定義されPodのポートとマッチすることを二度、三度と確認すべきです。Serviceを読み返して確認しましょう。

kubectl get service hostnames -o json
{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "hostnames",
        "namespace": "default",
        "uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
        "resourceVersion": "347189",
        "creationTimestamp": "2015-07-07T15:24:29Z",
        "labels": {
            "app": "hostnames"
        }
    },
    "spec": {
        "ports": [
            {
                "name": "default",
                "protocol": "TCP",
                "port": 80,
                "targetPort": 9376,
                "nodePort": 0
            }
        ],
        "selector": {
            "app": "hostnames"
        },
        "clusterIP": "10.0.1.175",
        "type": "ClusterIP",
        "sessionAffinity": "None"
    },
    "status": {
        "loadBalancer": {}
    }
}
  • アクセスしようとしているServiceポートはspec.ports[]のリストのなかに定義されていますか?
  • targetPortはPodに対して適切ですか(いくつかのPodはServiceとは異なるポートを使用します)?
  • targetPortを数値で定義しようとしている場合、それは数値(9376)、文字列"9376"のどちらですか?
  • targetPortを名前で定義しようとしている場合、Podは同じ名前でポートを公開していますか?
  • ポートのprotocolはPodに適切ですか?

ServiceにEndpointsがあるか?

ここまで来たということは、Serviceは正しく定義され、DNSによって名前解決できることが確認できているでしょう。ここでは、実行したPodがServiceによって実際に選択されていることを確認しましょう。

以前に、Podが実行されていることを確認しました。再確認しましょう。

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          1h
hostnames-632524106-ly40y   1/1       Running   0          1h
hostnames-632524106-tlaok   1/1       Running   0          1h

-l app=hostnames引数はラベルセレクターで、ちょうど私たちのServiceに定義されているものと同じです。

"AGE"列は、これらのPodが約1時間前のものであることを示しており、それらが正常に実行され、クラッシュしていないことを意味します。

"RESTARTS"列は、これらのポッドが頻繁にクラッシュしたり、再起動されていないことを示しています。頻繁に再起動すると、断続的な接続性の問題が発生する可能性があります。再起動回数が多い場合は、ポッドをデバッグするを参照してください。

Kubernetesシステム内には、すべてのServiceのセレクターを評価し、結果をEndpointsオブジェクトに保存するコントロールループがあります。

kubectl get endpoints hostnames

NAME        ENDPOINTS
hostnames   10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376

これにより、EndpointsコントローラーがServiceの正しいPodを見つけていることを確認できます。ENDPOINTS列が<none>の場合、Serviceのspec.selectorフィールドが実際にPodのmetadata.labels値を選択していることを確認する必要があります。よくある間違いは、タイプミスやその他のエラー、たとえばDeployment作成にもkubectl runが使われた1.18以前のバージョンのように、Serviceがapp=hostnamesを選択しているのにDeploymentがrun=hostnamesを指定していることです。

Podは機能しているか?

この時点で、Serviceが存在し、Podを選択していることがわかります。このウォークスルーの最初に、Pod自体を確認しました。Podが実際に機能していることを確認しましょう。Serviceメカニズムをバイパスして、上記EndpointsにリストされているPodに直接アクセスすることができます。

Pod内から実行します。

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

次のように表示されます。

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

Endpointsリスト内の各Podは、それぞれの自身のホスト名を返すはずです。そうならない(または、あなた自身のPodの正しい振る舞いにならない)場合は、そこで何が起こっているのかを調査する必要があります。

kube-proxyは機能しているか?

ここに到達したのなら、Serviceは実行され、Endpointsがあり、Podが実際にサービスを提供しています。この時点で、Serviceのプロキシーメカニズム全体が疑わしいです。ひとつひとつ確認しましょう。

Serviceのデフォルト実装、およびほとんどのクラスターで使用されるものは、kube-proxyです。kube-proxyはそれぞれのノードで実行され、Serviceの抽象化を提供するための小さなメカニズムセットの1つを構成するプログラムです。クラスターがkube-proxyを使用しない場合、以下のセクションは適用されず、使用しているServiceの実装を調査する必要があります。

kube-proxyは実行されているか?

kube-proxyがノード上で実行されていることを確認しましょう。ノードで実行されていれば、以下のような結果が得られるはずです。

ps auxw | grep kube-proxy
 root  4194  0.4  0.1 101864 17696 ?    Sl Jul04  25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2

次に、マスターとの接続など、明らかな失敗をしていないことを確認します。これを行うには、ログを確認する必要があります。ログへのアクセス方法は、ノードのOSに依存します。一部のOSでは/var/log/kube-proxy.logのようなファイルですが、他のOSではjournalctlを使用してログにアクセスします。次のように表示されます。

I1027 22:14:53.995134    5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163    5063 server.go:247] Using iptables Proxier.
I1027 22:14:53.999055    5063 server.go:255] Tearing down userspace rules. Errors here are acceptable.
I1027 22:14:54.038140    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209    5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238    5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048    5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP

マスターに接続できないことに関するエラーメッセージが表示された場合、ノードの設定とインストール手順をダブルチェックする必要があります。

kube-proxyが正しく実行できない理由の可能性の1つは、必須のconntrackバイナリが見つからないことです。これは、例えばKubernetesをスクラッチからインストールするなど、クラスターのインストール方法に依存して、一部のLinuxシステムで発生する場合があります。これが該当する場合は、conntrackパッケージを手動でインストール(例: Ubuntuではsudo apt install conntrack)する必要があり、その後に再試行する必要があります。

kube-proxyは、いくつかのモードのいずれかで実行できます。上記のログのUsing iptables Proxierという行は、kube-proxyが「iptables」モードで実行されていることを示しています。最も一般的な他のモードは「ipvs」です。古い「ユーザースペース」モードは、主にこれらに置き換えられました。

Iptables mode

「iptables」モードでは、ノードに次のようなものが表示されます。

iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR

各サービスのポートごとに、KUBE-SERVICESに1つのルールと1つのKUBE-SVC- <hash>チェーンが必要です。Podエンドポイントごとに、そのKUBE-SVC- <hash>に少数のルールがあり、少数のルールが含まれる1つのKUBE-SEP- <hash>チェーンがあるはずです。正確なルールは、正確な構成(NodePortとLoadBalancerを含む)に基づいて異なります。

IPVS mode

「ipvs」モードでは、ノードに次のようなものが表示されます。

ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
...
TCP  10.0.1.175:80 rr
  -> 10.244.0.5:9376               Masq    1      0          0
  -> 10.244.0.6:9376               Masq    1      0          0
  -> 10.244.0.7:9376               Masq    1      0          0
...

各Serviceの各ポートに加えて、NodePort、External IP、およびLoad Balancer IPに対して、kube-proxyは仮想サーバーを作成します。Pod endpointごとに、対応する実サーバーが作成されます。この例では、サービスhostnames(10.0.1.175:80)は3つのendpoints(10.244.0.5:937610.244.0.6:937610.244.0.7:9376)を持っています。

IPVSプロキシーは、各Serviceアドレス(Cluster IP、External IP、NodePort IP、Load Balancer IPなど)毎の仮想サーバーと、Serviceのエンドポイントが存在する場合に対応する実サーバーを作成します。この例では、hostnames Service(10.0.1.175:80)は3つのエンドポイント(10.244.0.5:937610.244.0.6:937610.244.0.7:9376)を持ち、上と似た結果が得られるはずです。

Userspace mode

まれに、「userspace」モードを使用している場合があります。

ノードから実行します。

iptables-save | grep hostnames
-A KUBE-PORTALS-CONTAINER -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j REDIRECT --to-ports 48577
-A KUBE-PORTALS-HOST -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j DNAT --to-destination 10.240.115.247:48577

サービスの各ポートには2つのルールが必要です(この例では1つだけ)-「KUBE-PORTALS-CONTAINER」と「KUBE-PORTALS-HOST」です。

「userspace」モードを使用する必要はほとんどないので、ここでこれ以上時間を費やすことはありません。

kube-proxyはプロキシしているか?

上記のいずれかが発生したと想定して、いずれかのノードからIPでサービスにアクセスをしています。

curl 10.0.1.175:80
hostnames-632524106-bbpiw

もしこれが失敗し、あなたがuserspaceプロキシーを使用している場合、プロキシーへの直接アクセスを試してみてください。もしiptablesプロキシーを使用している場合、このセクションはスキップしてください。

上記のiptables-saveの出力を振り返り、kube-proxyがServiceに使用しているポート番号を抽出します。上記の例では"48577"です。このポートに接続してください。

curl localhost:48577
hostnames-632524106-tlaok

もしまだ失敗する場合は、kube-proxyログで次のような特定の行を探してください。

Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]

これらが表示されない場合は、-vフラグを4に設定してkube-proxyを再起動してから、再度ログを確認してください。

エッジケース: PodがService IP経由で自身に到達できない

これはありそうに聞こえないかもしれませんが、実際には起こり、動作するはずです。これはネットワークが"hairpin"トラフィック用に適切に設定されていない場合、通常はkube-proxyiptablesモードで実行され、Podがブリッジネットワークに接続されている場合に発生します。Kubelethairpin-modeフラグを公開します。これにより、Serviceのエンドポイントが自身のServiceのVIPにアクセスしようとした場合に、自身への負荷分散を可能にします。hairpin-modeフラグはhairpin-vethまたはpromiscuous-bridgeに設定する必要があります。

この問題をトラブルシューティングする一般的な手順は次のとおりです。

  • hairpin-modehairpin-vethまたはpromiscuous-bridgeに設定されていることを確認します。次のような表示がされるはずです。この例では、hairpin-modepromiscuous-bridgeに設定されています。
ps auxw | grep kubelet
root      3392  1.1  0.8 186804 65208 ?        Sl   00:51  11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
  • 実際に使われているhairpin-modeを確認します。これを行うには、kubeletログを確認する必要があります。ログへのアクセス方法は、ノードのOSによって異なります。一部のOSでは/var/log/kubelet.logなどのファイルですが、他のOSではjournalctlを使用してログにアクセスします。互換性のために、実際に使われているhairpin-mode--hairpin-modeフラグと一致しない場合があることに注意してください。kubelet.logにキーワードhairpinを含むログ行があるかどうかを確認してください。実際に使われているhairpin-modeを示す以下のようなログ行があるはずです。
I0629 00:51:43.648698    3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
  • 実際に使われているhairpin-modehairpin-vethの場合、Kubeletにノードの/sysで操作する権限があることを確認します。すべてが正常に機能している場合、次のようなものが表示されます。
for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1

実際に使われているhairpin-modepromiscuous-bridgeの場合、Kubeletにノード上のLinuxブリッジを操作する権限があることを確認してください。cbr0ブリッジが使用され適切に構成されている場合、以下が表示されます。

ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1460  Metric:1
  • 上記のいずれも解決しない場合、助けを求めてください。

助けを求める

ここまでたどり着いたということは、とてもおかしなことが起こっています。Serviceは実行中で、Endpointsがあり、Podは実際にサービスを提供しています。DNSは動作していて、kube-proxyも誤動作していないようです。それでも、あなたのServiceは機能していません。おそらく私たちにお知らせ頂いた方がよいでしょう。調査をお手伝いします!

SlackForumまたはGitHubでお問い合わせください。

次の項目

詳細については、トラブルシューティングドキュメントをご覧ください。

6 - StatefulSetのデバッグ

このタスクでは、StatefulSetをデバッグする方法を説明します。

始める前に

  • Kubernetesクラスターが必要です。また、kubectlコマンドラインツールがクラスターと通信するように設定されている必要があります。
  • 調べたいStatefulSetを実行しておきましょう。

StatefulSetのデバッグ

StatefulSetに属し、ラベルapp=myappが設定されているすべてのPodを一覧表示するには、以下のコマンドを利用できます。

kubectl get pods -l app=myapp

Podが長期間UnknownまたはTerminatingの状態になっていることがわかった場合は、それらを処理する方法についてStatefulSetの削除タスクを参照してください。 Podのデバッグガイドを使用して、StatefulSet内の個々のPodをデバッグできます。

次の項目

Initコンテナのデバッグの詳細

7 - 実行中のコンテナへのシェルを取得する

このページはkubectl execを使用して実行中のコンテナへのシェルを取得する方法を説明します。

始める前に

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

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

コンテナへのシェルの取得

このエクササイズでは、1つのコンテナを持つPodを作成します。 コンテナはnginxのイメージを実行します。以下がそのPodの設定ファイルです:

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

Podを作成します:

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

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

kubectl get pod shell-demo

実行中のコンテナへのシェルを取得します:

kubectl exec --stdin --tty shell-demo -- /bin/bash

シェル内で、ルートディレクトリーのファイル一覧を表示します:

# このコマンドをコンテナ内で実行します
ls /

シェル内で、他のコマンドを試しましょう。以下がいくつかの例です:

# これらのサンプルコマンドをコンテナ内で実行することができます
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx

nginxのルートページへの書き込み

Podの設定ファイルを再度確認します。PodはemptyDirボリュームを持ち、 コンテナは/usr/share/nginx/htmlボリュームをマウントします。

シェル内で、/usr/share/nginx/htmlディレクトリにindex.htmlを作成します。

# このコマンドをコンテナ内で実行します
echo 'Hello shell demo' > /usr/share/nginx/html/index.html

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

# これらのコマンドをコンテナ内のシェルで実行します
apt-get update
apt-get install curl
curl http://localhost/

出力にindex.htmlファイルに書き込んだ文字列が表示されます:

Hello shell demo

シェルを終了する場合、exitを入力します。

exit # コンテナ内のシェルを終了する

コンテナ内での各コマンドの実行

シェルではない通常のコマンドウインドウ内で、実行中のコンテナの環境変数の一覧を表示します:

kubectl exec shell-demo env

他のコマンドを試します。以下がいくつかの例です:

kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts

Podが1つ以上のコンテナを持つ場合にシェルを開く

Podが1つ以上のコンテナを持つ場合、--container-cを使用して、kubectl execコマンド内でコンテナを指定します。 例えば、my-podという名前のPodがあり、そのPodが main-apphelper-app という2つのコンテナを持つとします。 以下のコマンドは main-app のコンテナへのシェルを開きます。

kubectl exec -i -t my-pod --container main-app -- /bin/bash

次の項目

8 - 監査

Kubernetesの監査はクラスター内の一連の行動を記録するセキュリティに関連した時系列の記録を提供します。 クラスターはユーザー、Kubernetes APIを使用するアプリケーション、 およびコントロールプレーン自体によって生成されたアクティビティなどを監査します。

監査により、クラスター管理者は以下の質問に答えることができます:

  • 何が起きたのか?
  • いつ起こったのか?
  • 誰がそれを始めたのか?
  • 何のために起こったのか?
  • それはどこで観察されたのか?
  • それはどこから始まったのか?
  • それはどこへ向かっていたのか?

監査記録のライフサイクルはkube-apiserverコンポーネントの中で始まります。 各リクエストの実行の各段階で、監査イベントが生成されます。 ポリシーに従って前処理され、バックエンドに書き込まれます。 ポリシーが何を記録するかを決定し、 バックエンドがその記録を永続化します。現在のバックエンドの実装はログファイルやWebhookなどがあります。

各リクエストは関連する stage で記録されます。 定義されたステージは以下の通りです:

  • RequestReceived - 監査ハンドラーがリクエストを受信すると同時に生成されるイベントのステージ。 つまり、ハンドラーチェーンに委譲される前に生成されるイベントのステージです。
  • ResponseStarted - レスポンスヘッダーが送信された後、レスポンスボディが送信される前のステージです。 このステージは長時間実行されるリクエスト(watchなど)でのみ発生します。
  • ResponseComplete - レスポンスボディの送信が完了して、それ以上のバイトは送信されません。
  • Panic - パニックが起きたときに発生するイベント。

監査ログ機能は、リクエストごとに監査に必要なコンテキストが保存されるため、APIサーバーのメモリー消費量が増加します。 メモリーの消費量は、監査ログ機能の設定によって異なります。

監査ポリシー

監査ポリシーはどのようなイベントを記録し、どのようなデータを含むべきかについてのルールを定義します。 監査ポリシーのオブジェクト構造は、audit.k8s.io API groupで定義されています。

イベントが処理されると、そのイベントは順番にルールのリストと比較されます。 最初のマッチングルールは、イベントの監査レベルを設定します。

定義されている監査レベルは:

  • None - ルールに一致するイベントを記録しません。
  • Metadata - リクエストのメタデータ(リクエストしたユーザー、タイムスタンプ、リソース、動作など)を記録しますが、リクエストやレスポンスのボディは記録しません。
  • Request - ログイベントのメタデータとリクエストボディは表示されますが、レスポンスボディは表示されません。 これは非リソースリクエストには適用されません。
  • RequestResponse - イベントのメタデータ、リクエストとレスポンスのボディを記録しますが、 非リソースリクエストには適用されません。

audit-policy-fileフラグを使って、ポリシーを記述したファイルを kube-apiserverに渡すことができます。 このフラグが省略された場合イベントは記録されません。 監査ポリシーファイルでは、rulesフィールドが必ず指定されることに注意してください。 ルールがない(0)ポリシーは不当なものとして扱われます。

以下は監査ポリシーファイルの例です:

apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
  - "RequestReceived"
rules:
  # Log pod changes at RequestResponse level
  - level: RequestResponse
    resources:
    - group: ""
      # Resource "pods" doesn't match requests to any subresource of pods,
      # which is consistent with the RBAC policy.
      resources: ["pods"]
  # Log "pods/log", "pods/status" at Metadata level
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # Don't log requests to a configmap called "controller-leader"
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

  # Don't log watch requests by the "system:kube-proxy" on endpoints or services
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: "" # core API group
      resources: ["endpoints", "services"]

  # Don't log authenticated requests to certain non-resource URL paths.
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*" # Wildcard matching.
    - "/version"

  # Log the request body of configmap changes in kube-system.
  - level: Request
    resources:
    - group: "" # core API group
      resources: ["configmaps"]
    # This rule only applies to resources in the "kube-system" namespace.
    # The empty string "" can be used to select non-namespaced resources.
    namespaces: ["kube-system"]

  # Log configmap and secret changes in all other namespaces at the Metadata level.
  - level: Metadata
    resources:
    - group: "" # core API group
      resources: ["secrets", "configmaps"]

  # Log all other resources in core and extensions at the Request level.
  - level: Request
    resources:
    - group: "" # core API group
    - group: "extensions" # Version of group should NOT be included.

  # A catch-all rule to log all other requests at the Metadata level.
  - level: Metadata
    # Long-running requests like watches that fall under this rule will not
    # generate an audit event in RequestReceived.
    omitStages:
      - "RequestReceived"

最小限の監査ポリシーファイルを使用して、すべてのリクエストを Metadataレベルで記録することができます。

# Log all requests at the Metadata level.
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

独自の監査プロファイルを作成する場合は、Google Container-Optimized OSの監査プロファイルを出発点として使用できます。 監査ポリシーファイルを生成するconfigure-helper.shスクリプトを確認することができます。 スクリプトを直接見ることで、監査ポリシーファイルのほとんどを見ることができます。

また、定義されているフィールドの詳細については、Policy` configuration referenceを参照できます。

監査バックエンド

監査バックエンドは監査イベントを外部ストレージに永続化します。 kube-apiserverには2つのバックエンドが用意されています。

  • イベントをファイルシステムに書き込むログバックエンド
  • 外部のHTTP APIにイベントを送信するWebhookバックエンド

いずれの場合も、監査イベントはKubernetes APIaudit.k8s.io API groupで定義されている構造に従います。

ログバックエンド

ログバックエンドは監査イベントをJSONlines形式のファイルに書き込みます。 以下の kube-apiserver フラグを使ってログ監査バックエンドを設定できます。

  • --audit-log-path は、ログバックエンドが監査イベントを書き込む際に使用するログファイルのパスを指定します。 このフラグを指定しないと、ログバックエンドは無効になります。- は標準出力を意味します。
  • --audit-log-maxage は、古い監査ログファイルを保持する最大日数を定義します。
  • audit-log-maxbackupは、保持する監査ログファイルの最大数を定義します。
  • --audit-log-maxsize は、監査ログファイルがローテーションされるまでの最大サイズをメガバイト単位で定義します。

クラスターのコントロールプレーンでkube-apiserverをPodとして動作させている場合は、監査記録が永久化されるように、ポリシーファイルとログファイルの場所にhostPathをマウントすることを忘れないでください。 例えば:

    --audit-policy-file=/etc/kubernetes/audit-policy.yaml \
    --audit-log-path=/var/log/audit.log

それからボリュームをマウントします:

...
volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy.yaml
    name: audit
    readOnly: true
  - mountPath: /var/log/audit.log
    name: audit-log
    readOnly: false

最後にhostPathを設定します:

...
- name: audit
  hostPath:
    path: /etc/kubernetes/audit-policy.yaml
    type: File

- name: audit-log
  hostPath:
    path: /var/log/audit.log
    type: FileOrCreate

Webhookバックエンド

Webhook監査バックエンドは、監査イベントをリモートのWeb APIに送信しますが、 これは認証手段を含むKubernetes APIの形式であると想定されます。

Webhook監査バックエンドを設定するには、以下のkube-apiserverフラグを使用します。

  • --audit-webhook-config-file は、Webhookの設定ファイルのパスを指定します。 webhookの設定は、事実上特化したkubeconfigです。
  • --audit-webhook-initial-backoff は、最初に失敗したリクエストの後、再試行するまでに待つ時間を指定します。 それ以降のリクエストは、指数関数的なバックオフで再試行されます。

Webhookの設定ファイルは、kubeconfig形式でサービスのリモートアドレスと接続に使用する認証情報を指定します。

イベントバッチ

ログバックエンドとwebhookバックエンドの両方がバッチ処理をサポートしています。 webhookを例に、利用可能なフラグの一覧を示します。 ログバックエンドで同じフラグを取得するには、フラグ名のwebhooklogに置き換えてください。 デフォルトでは、バッチングはwebhookでは有効で、logでは無効です。 同様に、デフォルトではスロットリングは webhook で有効で、logでは無効です。

  • --audit-webhook-mode は、バッファリング戦略を定義します。以下のいずれかとなります。
    • batch - イベントをバッファリングして、非同期にバッチ処理します。これがデフォルトです。
    • blocking - 個々のイベントを処理する際に、APIサーバーの応答をブロックします。
    • blocking-strict - blockingと同じですが、RequestReceivedステージでの監査ログに失敗した場合は RequestReceivedステージで監査ログに失敗すると、kube-apiserverへのリクエスト全体が失敗します。

以下のフラグは batch モードでのみ使用されます:

  • --audit-webhook-batch-buffer-sizeは、バッチ処理を行う前にバッファリングするイベントの数を定義します。 入力イベントの割合がバッファをオーバーフローすると、イベントはドロップされます。
  • --audit-webhook-batch-max-sizeは、1つのバッチに入れるイベントの最大数を定義します。
  • --audit-webhook-batch-max-waitは、キュー内のイベントを無条件にバッチ処理するまでの最大待機時間を定義します。
  • --audit-webhook-batch-throttle-qpsは、1秒あたりに生成されるバッチの最大平均数を定義します。
  • --audit-webhook-batch-throttle-burstは、許可された QPS が低い場合に、同じ瞬間に生成されるバッチの最大数を定義します。

パラメーターチューニング

パラメーターは、APIサーバーの負荷に合わせて設定してください。

例えば、kube-apiserverが毎秒100件のリクエストを受け取り、それぞれのリクエストがResponseStartedResponseCompleteの段階でのみ監査されるとします。毎秒≅200の監査イベントが発生すると考えてください。 1つのバッチに最大100個のイベントがあるの場合、スロットリングレベルを少なくとも2クエリ/秒に設定する必要があります。 バックエンドがイベントを書き込むのに最大で5秒かかる場合、5秒分のイベントを保持するようにバッファーサイズを設定する必要があります。

10バッチ、または1000イベントとなります。

しかし、ほとんどの場合デフォルトのパラメーターで十分であり、手動で設定する必要はありません。 kube-apiserverが公開している以下のPrometheusメトリクスや、ログを見て監査サブシステムの状態を監視することができます。

  • apiserver_audit_event_totalメトリックには、エクスポートされた監査イベントの合計数が含まれます。
  • apiserver_audit_error_totalメトリックには、エクスポート中にエラーが発生してドロップされたイベントの総数が含まれます。

ログエントリー・トランケーション

logバックエンドとwebhookバックエンドは、ログに記録されるイベントのサイズを制限することをサポートしています。

例として、logバックエンドで利用可能なフラグの一覧を以下に示します

  • audit-log-truncate-enabledイベントとバッチの切り捨てを有効にするかどうかです。
  • audit-log-truncate-max-batch-sizeバックエンドに送信されるバッチのバイト単位の最大サイズ。
  • audit-log-truncate-max-event-sizeバックエンドに送信される監査イベントのバイト単位の最大サイズです。

デフォルトでは、webhooklogの両方で切り捨ては無効になっていますが、クラスター管理者は audit-log-truncate-enabledまたはaudit-webhook-truncate-enabledを設定して、この機能を有効にする必要があります。

次の項目