トラブルシューティング

Google Kubernetes Engine(GKE)の使用中に問題が発生した場合に役立つトラブルシューティング手順について説明します。

Kubernetes リソースのデバッグ

クラスタに関する問題が発生している場合は、Kubernetes のドキュメントでクラスタのトラブルシューティングをご覧ください。

アプリケーション、Pod、またはコントローラ オブジェクトに問題がある場合は、アプリケーションのトラブルシューティングをご覧ください。

kubectl コマンドが見つかりません

  1. 次のコマンドを実行して kubectl バイナリをインストールします。

    sudo gcloud components update kubectl
    
  2. $PATH 環境変数を変更するかどうかを確認するメッセージが表示されたら、「yes」を選択します。この変数を変更すると、ファイルの完全なパスを入力せずに kubectl コマンドを使用できます。

    または、次の行を ~/.bashrc(macOS では ~/.bash_profile、または使用しているシェルが環境変数を格納する場所)に追加します。

    export PATH=$PATH:/usr/local/share/google/google-cloud-sdk/bin/
    
  3. 次のコマンドを実行して、更新した .bashrc(または .bash_profile)ファイルを読み込みます。

    source ~/.bashrc
    

kubectl コマンドを実行すると「接続は拒否されました」というエラーが表示されます

次のコマンドを使用してクラスタのコンテキストを設定します。

gcloud container clusters get-credentials cluster-name

cluster-name に何を入力すればよいかわからない場合は、次のコマンドを使用してクラスタを一覧表示します。

gcloud container clusters list

kubectl コマンドを実行すると「API バージョンのネゴシエーションができませんでした」というエラーが表示されます

kubectl に認証情報があることを確認します。

gcloud auth application-default login

kubectl logsattachexecport-forward コマンドが応答しなくなった

これらのコマンドは、クラスタのコントロール プレーン(マスター)がクラスタ内のノードと通信できることを前提にして動作します。ただし、コントロール プレーンはクラスタのノードと同じ Compute Engine ネットワーク内にはないので、SSH トンネルを利用してセキュアな通信を実現しています。

GKE は SSH 公開鍵ファイルを Compute Engine プロジェクトのメタデータに保存します。Google 提供のイメージを使用するすべての Compute Engine VM はプロジェクトの共通メタデータとインスタンスのメタデータを定期的にチェックし、承認済みユーザーの VM のリストに追加する SSH 認証鍵がないかどうかを確認します。また、GKE はコントロール プレーンの IP アドレスからクラスタ内の各ノードへの SSH アクセスを許可するファイアウォール ルールを Compute Engine ネットワークに追加します。

上記のいずれかの kubectl コマンドが実行されない場合、ノードで API サーバーが SSH トンネルを開くことができない可能性があります。次のような問題がないかどうかを確認してください。

  1. クラスタにノードが存在しない。

    クラスタ内のノードの数を減らして 0 にした場合、SSH トンネルは動作しません。

    この問題を修正するには、クラスタのサイズを変更してノードを 1 つ以上配置します。

  2. クラスタ内の Pod が終了状態のまま復帰しないため、存在しなくなったノードをクラスタから削除できない。

    この問題は Kubernetes バージョン 1.1 にのみ影響しますが、クラスタのサイズの変更を繰り返した場合に発生する可能性があります。

    この問題を修正するには、終了状態のまま数分以上経過しているPod を削除します。これによって古いノードはコントロール プレーンから削除され、新しいノードに置き換えられます。

  3. ネットワークのファイアウォール ルールでは、コントロール プレーンへの SSH アクセスは許可されません。

    すべての Compute Engine ネットワークは default-allow-ssh というファイアウォール ルールを使用して作成されます。このルールでは有効な秘密鍵を持つすべての IP アドレスからの SSH アクセスが許可されます。また、GKE は一般公開クラスタごとに gke-cluster-name-random-characters-ssh 形式の SSH ルールも挿入します。このルールでは、クラスタのコントロール プレーンからクラスタのノードへの SSH アクセスが個別に許可されます。どちらのルールも存在しない場合、コントロール プレーンは SSH トンネルを開くことができません。

    この問題を修正するには、コントロール プレーンの IP アドレスからすべてのクラスタのノード上にあるタグの付いた VM へのアクセスを許可するファイアウォール ルールを再度追加します。

  4. プロジェクトの「ssh-keys」用の共通メタデータ エントリがいっぱいになっている。

    プロジェクトの「ssh-keys」というメタデータ エントリが最大サイズの上限に近い場合、GKE は独自の SSH 認証鍵を追加できず、SSH トンネルを開くことができません。次のコマンドを実行してプロジェクトのメタデータを表示します。

    gcloud compute project-info describe [--project=PROJECT]
    

    次に、ssh-keys リストの長さを確認します。

    この問題を修正するには、不要になった SSH 認証鍵を削除します。

  5. クラスタ内の VM でメタデータ フィールドに認証鍵「ssh-keys」を設定している。

    VM のノード エージェントではプロジェクト全体の SSH 認証鍵よりもインスタンスごとの ssh-keys が優先されるため、クラスタのノードで SSH 認証鍵を個別に設定している場合、ノードはプロジェクトのメタデータ内にあるコントロール プレーンの SSH 認証鍵を無視します。確認するには、gcloud compute instances describe <VM-name> コマンドを実行し、メタデータで「ssh-keys」フィールドを探します。

    この問題を修正するには、インスタンスのメタデータからインスタンスごとの SSH 認証鍵を削除します。

これらの機能はクラスタの正常な動作に必須ではないことに注意してください。クラスタのネットワークを外部アクセスから完全に遮断した場合、これらのような機能が動作しなくなることに注意してください。

コントロール プレーンと互換性のないバージョンのノード

クラスタのコントロール プレーンで実行されている Kubernetes のバージョンを確認し、クラスタのノードプールで実行されている Kubernetes のバージョンを確認します。クラスタのノードプールのいずれかがコントロール プレーンより 2 つ以上前のバージョンを持つ場合、これがクラスタの問題を引き起こす可能性があります。

GKE チームはユーザーに代わって定期的にクラスタ コントロール プレーンのアップグレードを行います。コントロール プレーンは、新しい安定バージョンの Kubernetes にアップグレードされます。デフォルトでは、クラスタのノードで自動アップグレードが有効になっています。無効にすることはおすすめしません。

クラスタのノードの自動アップグレードが無効になっている場合、ノードプール バージョンをコントロール プレーンと互換性のあるバージョンに手動でアップグレードしなければ、コントロール プレーンが時間の経過とともに自動的にアップグレードされるため、最終的には、ノードとの互換性がなくなります。クラスタのコントロール プレーンとノードの間に互換性がない場合、予期しない問題が発生する可能性があります。

Kubernetes バージョンとバージョン スキューのサポート ポリシーにより、コントロール プレーンは、コントロール プレーンの 2 つ前までのマイナー バージョンのノードと互換性が保証されています。たとえば、Kubernetes 1.19 コントロール プレーンは Kubernetes 1.19、1.18、1.17 のノードに対応しています。この問題を解決するには、ノードプールのバージョンをコントロール プレーンと互換性のあるバージョンに手動でアップグレードします。

アップグレード プロセスによって、影響を受けるノード上で実行されているワークロードの中断が発生していると思われる場合は、[ワークロードの移行] セクションの [異なるマシンタイプへのワークロードの移行] チュートリアルをご覧ください。それらの手順により、新しいノードプールを作成し、古いノードプールを閉鎖してドレインすることで、正常に移行できます。

クラスタからの指標が Cloud Monitoring に表示されない

プロジェクトで Cloud Monitoring APICloud Logging API が有効になっていること、プロジェクトを Cloud Monitoring で表示できることを確認します。

問題が解決しない場合は、次の原因が考えられます。

  1. クラスタでモニタリングを有効にしてあることを確認します。

    Google Cloud Console と gcloud コマンドライン ツールで作成したクラスタでは、モニタリングがデフォルトで有効になりますが、これを確認するには、次のコマンドを実行するか、Cloud Console でクラスタの詳細情報をクリックします。

    gcloud container clusters describe cluster-name
    

    このコマンドの出力に、「monitoringService」が「monitoring.googleapis.com」であることが示されるはずです。Cloud Console で Cloud Monitoring を有効にする必要があります。

    モニタリングが有効になっていない場合は、次のコマンドを実行して有効にします。

    gcloud container clusters update cluster-name --monitoring-service=monitoring.googleapis.com
    
  2. クラスタを作成してから、またはモニタリングを有効にしてからどれくらいの時間が経過していますか?

    新しいクラスタの指標が Cloud Monitoring に表示されるまでに最長で 1 時間かかります。

  3. heapster または gke-metrics-agent(OpenTelemetry Collector)がクラスタの "kube-system" 名前空間で実行されていますか?

    クラスタのリソースが不足しているため、Pod がワークロードのスケジュールに失敗している可能性があります。kubectl get pods --namespace=kube-system を呼び出し、名前に heapster または gke-metrics-agent が含まれる Pod があるか調べて、Heapster または OpenTelemetry が実行されているかどうかを確認してください。

  4. クラスタのコントロール プレーンはノードと通信できますか?

    Cloud Monitoring はこの通信に依存します。この問題が発生しているかどうかを確認するには、次のコマンドを実行します。

    kubectl logs pod-name
    

    このコマンドがエラーを返した場合は、SSH トンネルで問題が発生している可能性があります。詳しくは、このセクションをご覧ください。

Cloud Logging エージェントに関連する問題が発生している場合は、エージェントのトラブルシューティングのドキュメントをご覧ください。

詳細については、Logging のドキュメントをご覧ください。

エラー 404: gcloud container コマンドを呼び出したときにリソースが「見つかりませんでした」

gcloud コマンドライン ツールで再度認証を行います。

gcloud auth login

エラー 400/403: アカウントに編集権限がありません

Compute Engine のデフォルトのサービス アカウントまたは GKE に関連付けられているサービス アカウントが手動で削除または編集されました。

Compute Engine または Kubernetes Engine API を有効にすると、サービス アカウントが作成され、プロジェクトにおける編集権限が与えられます。いずれかの時点で権限を編集するか、「Kubernetes Engine サービス エージェント」ロールを削除するか、アカウントを完全に削除するか、あるいは API を無効にした場合、クラスタの作成とすべての管理機能が失敗します。

Google Kubernetes Engine サービス アカウントの名前は次のとおりです。ここで project-number は、プロジェクト番号です。

service-project-number@container-engine-robot.iam.gserviceaccount.com

この問題を解決するには、Google Kubernetes Engine サービス アカウントから Kubernetes Engine サービス エージェントのロールを削除している場合、もう一度追加します。ロールを削除していない場合は、Kubernetes Engine API を再度有効にする必要があります。これで、サービス アカウントと権限が正しく復元されます。これは gcloud ツールまたは Cloud Console で行うことができます。

Console

  1. Cloud Console の [API とサービス] にアクセスします。

    [API とサービス] ページ

  2. プロジェクトを選択します。

  3. [API とサービスを有効化] をクリックします。

  4. Kubernetes を検索して、検索結果から API を選択します。

  5. [有効にする] をクリックします。API をすでに有効にしている場合は、まずそれを無効にしてから再度有効にする必要があります。API と関連サービスが有効になるには、数分かかることがあります。

gcloud

gcloud ツールで次のコマンドを実行します。

gcloud services enable container.googleapis.com

1.8.x 以前の自動ファイアウォール ルールを 1.9.x 以降で複製する

クラスタで Kubernetes バージョン 1.9.x を実行している場合、自動ファイアウォール ルールが変更されたため、GKE クラスタ内のワークロードが、同じネットワーク上にあるがそのクラスタの外にある他の Compute Engine VM と通信を開始できなくなりました。

次の手順を行って、1.8.x 以前のクラスタの自動ファイアウォール ルールの動作を再現できます。

  1. クラスタのネットワークを見つけます。

    gcloud container clusters describe cluster-name --format=get"(network)"
    
  2. コンテナに使用する、クラスタの IPv4 CIDR を取得します。

    gcloud container clusters describe cluster-name --format=get"(clusterIpv4Cidr)"
    
  3. ネットワークのファイアウォール ルールを作成します。その CIDR をソース範囲とし、すべてのプロトコルを許可します。

    gcloud compute firewall-rules create "cluster-name-to-all-vms-on-network" \
      --network="network" --source-ranges="cluster-ipv4-cidr" \
      --allow=tcp,udp,icmp,esp,ah,sctp
    

GCP プロジェクトに対するデフォルトのサービス アカウントを復元する

GKE のデフォルトのサービス アカウント、container-engine-robot が、プロジェクトから誤ってバインド解除されることがあります。GKE サービス エージェントは、クラスタ リソースを管理する権限をサービス アカウントに与える Identity and Access Management(IAM)ロールです。このロール バインディングをサービス アカウントから削除すると、デフォルト サービス アカウントはプロジェクトからバインド解除され、アプリケーションのデプロイや他のクラスタ操作の実行をユーザーができなくなる可能性があります。

gcloud ツールまたは Cloud Console を使用して、サービス アカウントがプロジェクトから削除されているか確認できます。

gcloud

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

gcloud projects get-iam-policy project-id

project-id を実際のプロジェクト ID に置き換えます。

Console

Cloud Console の [IAM と管理] ページにアクセスします。

コマンドまたはダッシュボードで、サービス アカウントの中に container-engine-robot が表示されない場合、サービス アカウントのバインドは解除されています。

GKE サービス エージェントのロール バインディングを削除した場合は、次のコマンドを実行してロール バインディングを復元します。

PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format "value(projectNumber)")
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
  --member "serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.iam.gserviceaccount.com" \
  --role roles/container.serviceAgent

役割バインディングが付与されたことを確認するには、次のコマンドを実行します。

gcloud projects get-iam-policy $PROJECT_ID

サービス アカウント名と container.serviceAgent 役割が表示されている場合、役割バインディングが付与されています。例:

- members:
  - serviceAccount:service-1234567890@container-engine-robot.iam.gserviceaccount.com
  role: roles/container.serviceAgent

Cloud KMS 鍵が無効になっています。

アプリケーション レベルでのシークレットの暗号化で、GKE のデフォルトのサービス アカウントは無効になっている Cloud KMS 鍵を使用できません。

無効になっている鍵を再度有効にするには、無効な鍵バージョンを有効にするをご覧ください。

ノードを割り当て可能にした後、Pod が保留状態になります

ノードを割り当て可能にした後、保留中の Pod で問題が発生したら、次の点に注意してください。

バージョン 1.7.6 以降の GKE では、Docker とオペレーティング システムを含め、Kubernetes のオーバーヘッド用に CPU とメモリが予約されています。Pod によってスケジュールされる各マシンタイプの数については、クラスタのアーキテクチャをご覧ください。

アップグレード後に Pod が保留中になる場合は、次のことをおすすめします。

  1. Pod の CPU リクエストとメモリ リクエストがピーク時の使用量を超えていないことを確認します。オーバーヘッド用に CPU とメモリを予約している GKE では、Pod はこのリソースをリクエストできません。実際の使用量よりも多くの CPU またはメモリをリクエストする Pod では、他の Pod がこのリソースをリクエストすることができなくなり、クラスタが十分に活用されないことがあります。詳細については、リソース リクエストを含む Pod のスケジュール設定をご覧ください。

  2. クラスタのサイズを変更することをおすすめします。手順については、クラスタのサイズ変更をご覧ください。

  3. この変更を元に戻すには、クラスタをダウングレードします。手順については、クラスタまたはノードプールの手動アップグレードをご覧ください。

作成された限定公開クラスタノードがクラスタに参加しません

多くの場合、限定公開クラスタで使用されている VPC でカスタム ルーティングとサードパーティ ネットワーク アプライアンスを使用すると、デフォルトのインターネット ゲートウェイではなく、アプライアンスにデフォルト ルート(0.0.0.0/0)がリダイレクトされます。コントロール プレーンの接続だけでなく、次の宛先に到達できるようにする必要があります。

  • *.googleapis.com
  • *.gcr.io
  • gcr.io

3 つのドメインすべてに対して限定公開の Google アクセスを構成します。このベスト プラクティスでは、インターネットにバインドされたトラフィックを制限しながら、新しいノードが起動してクラスタに参加できるようになります。

デプロイされたワークロードに関する問題のトラブルシューティング

ワークロードの Pod に問題がある場合、GKE がエラーを返します。Pod のステータスを確認するには、kubectl コマンドライン ツールまたは Cloud Console を使用します。

kubectl

クラスタで実行しているすべての Pod を表示するには、次のコマンドを実行します。

kubectl get pods

出力:

NAME       READY  STATUS             RESTARTS  AGE
pod-name   0/1    CrashLoopBackOff   23        8d

特定の Pod に関する詳細情報を取得するには、次のコマンドを実行します。

kubectl describe pod pod-name

pod-name を目的の Pod の名前に置き換えます。

Console

次の手順を行います。

  1. Cloud Console で GKE の [ワークロード] ダッシュボードに移動します。

    GKE の [ワークロード] ダッシュボードに移動

  2. 目的のワークロードを選択します。[概要] タブにワークロードのステータスが表示されます。

  3. [管理対象 Pod] セクションで、エラー ステータス メッセージをクリックします。

以下のセクションでは、ワークロードによって返される一般的なエラーとその解決方法について説明します。

CrashLoopBackOff

CrashLoopBackOff は、コンテナが再起動後に繰り返しクラッシュしていることを示します。コンテナはさまざまな理由でクラッシュすることがあり、Pod のログを調べると根本原因のトラブルシューティングに役立ちます。

デフォルトでは、クラッシュしたコンテナは 5 分間までの指数的遅延で再起動します。この動作を変更するには、spec: restartPolicyrestartPolicy フィールドの Deployment の Pod 仕様を設定します。このフィールドのデフォルト値は Always です。

Pod のコンテナがクラッシュする原因を確認するには、kubectl コマンドライン ツールまたは Cloud Console を使用します。

kubectl

クラスタで実行しているすべての Pod を表示するには、次のコマンドを実行します。

kubectl get pods

CrashLoopBackOff エラーがある Pod を探します。

Pod のログを取得するには、次のコマンドを実行します。

kubectl logs pod-name

pod-name を問題のある Pod の名前に置き換えます。

また、-p フラグを使用して、Pod の以前のコンテナ インスタンス(存在する場合)のログを取得することもできます。

Console

次の手順を行います。

  1. Cloud Console で GKE の [ワークロード] ダッシュボードに移動します。

    GKE の [ワークロード] ダッシュボードに移動

  2. 目的のワークロードを選択します。[概要] タブにワークロードのステータスが表示されます。

  3. [管理対象 Pod] セクションで、問題のある Pod をクリックします。

  4. Pod のメニューで、[ログ] タブをクリックします。

クラッシュしたコンテナの終了コードを調べる

終了コードは次のタスクを実行して確認できます。

  1. 次のコマンドを実行します。

    kubectl describe pod pod-name
    

    pod-name は、Pod の名前で置き換えます。

  2. containers: container-name: last state: exit code フィールドの値を確認します。

    • 終了コードが 1 の場合、コンテナがクラッシュした理由はアプリケーションがクラッシュしたからです。
    • 終了コードが 0 の場合は、アプリの実行時間を確認してください。

    コンテナは、アプリケーションのメインプロセスが終了したときに終了します。アプリの実行が非常に速く終了する場合、コンテナは再起動を続行している可能性があります。

実行中のコンテナに接続する

Pod へのシェルを開きます。

kubectl exec -it pod-name -- /bin/bash

Pod 内にコンテナが複数ある場合は、-c container-name を追加します。

これで、そのコンテナから bash コマンドを実行できます。ネットワークのテストや、アプリケーションで使用されているファイルやデータベースにアクセスできるかどうかを調べることができます。

ImagePullBackOff と ErrImagePull

ImagePullBackOffErrImagePull は、コンテナが使用するイメージをイメージ レジストリからロードできないことを示します。

この問題は、Cloud Console または kubectl コマンドライン ツールで確認できます。

kubectl

Pod のコンテナ イメージに関する詳細情報を表示するには、次のコマンドを実行します。

kubectl describe pod pod-name

Console

次の手順を行います。

  1. Cloud Console で GKE の [ワークロード] ダッシュボードに移動します。

    GKE の [ワークロード] ダッシュボードに移動

  2. 目的のワークロードを選択します。[概要] タブにワークロードのステータスが表示されます。

  3. [管理対象 Pod] セクションで、問題のある Pod をクリックします。

  4. Pod のメニューで、[イベント] タブをクリックします。

イメージが見つからない場合

イメージが見つからない場合:

  1. イメージの名前が正しいことを確認します。
  2. イメージのタグが正しいことを確認します。(:latest またはタグなしを試して最新のイメージを pull します)。
  3. イメージにレジストリ フルパスがある場合は、使用している Docker レジストリにそのパスが存在することを確認します。イメージ名のみを指定した場合は、Docker Hub レジストリを確認してください。
  4. Docker イメージを手動で pull してみます。

    • SSH でノードに接続します。

      たとえば、SSH を使用して us-central1-a ゾーンの example-instance に接続するには、次のようにします。

      gcloud compute ssh example-instance --zone us-central1-a
      
    • docker pull image-name を実行します。

    このオプションが機能すると、Pod で ImagePullSecret の指定が必要になる場合があります。Pod がイメージ pull シークレットを参照できるのはそれぞれの名前空間内だけであるため、このプロセスを名前空間ごとに 1 回行う必要があります。

「permission denied」または「no pull access」というエラーが発生した場合は、ログインしていることと、イメージへのアクセス権があることを確認してください。

非公開レジストリを使用している場合は、イメージを読み取るためのキーが必要な場合があります。

イメージが Container Registry でホストされている場合、ノードプールに関連付けられているサービス アカウントには、そのイメージを含む Cloud Storage バケットに対する読み取りアクセス権が必要です。詳細については、Container Registry のドキュメントをご覧ください。

Pod をスケジュールできない

PodUnschedulable は、リソースが不足しているかなんらかの構成エラーのために Pod をスケジュールできないことを示します。

リソースの不足

CPU、メモリ、またはその他のリソースが不足していることを示すエラーが発生する場合があります。たとえば、「No nodes are available that match all of the predicates: Insufficient cpu (2)」は、2 つのノードで、ポッドのリクエストを満たせる十分な CPU がないことを示します。

デフォルトの CPU リクエストは 100m、または CPU の 10%(あるいは 1 コア)です。 リクエストするリソースの数を変更するには、spec: containers: resources: requests の Pod 仕様に値を指定します。

MatchNodeSelector

MatchNodeSelector は、Pod のラベルセレクタに一致するノードがないことを示します。

これを確認するには、Pod 仕様の nodeSelector フィールド(spec: nodeSelector の下)に指定されているラベルを確認します。

クラスタ内のノードがどのようにラベルされているか確認するには、次のコマンドを実行します。

kubectl get nodes --show-labels

ノードにラベルを付けるには、次のコマンドを実行します。

kubectl label nodes node-name label-key=label-value

次のように置き換えます。

  • node-name を目的のノードに置き換えます。
  • label-key をラベルのキーに置き換えます。
  • label-value をラベルの値に置き換えます。

詳細については、ノードにポッドを割り当てるを参照してください。

PodToleratesNodeTaints

PodToleratesNodeTaints は、node taint を許容しているノードが現在ないため、Pod をノードにスケジュールできないことを示します。

これが該当することを確認するには、次のコマンドを実行します。

kubectl describe nodes node-name

出力で、Taints フィールドを確認すると、Key-Value のペアとスケジューリングの結果が一覧表示されています。

リストされた結果が NoSchedule の場合、そのノードは一致する容認がなければ、Pod をスケジュールできません。

この問題を解決する方法の 1 つは、taint を削除することです。たとえば、NoSchedule taint を削除するには、次のコマンドを実行します。

kubectl taint nodes node-name key:NoSchedule-

PodFitsHostPorts

PodFitsHostPorts は、ノードが利用しようとしているポートがすでに使用中であることを示します。

この問題を解決するには、spec: containers: ports: hostPort の下にある Pod 仕様の hostPort の値を確認します。この値を別のポートに変更することが必要になる場合があります。

最低限の有用性がない

ノードに十分なリソースがあるのに Does not have minimum availability というメッセージが表示される場合は、Pod のステータスを確認します。ステータスが SchedulingDisabled または Cordoned の場合、ノードは新しい Pod をスケジュールできません。Cloud Console または kubectl コマンドライン ツールを使用して、ノードのステータスを確認できます。

kubectl

ノードのステータスを取得するには、次のコマンドを実行します。

kubectl get nodes

ノードでスケジューリングを可能にするには、次のコマンドを実行します。

kubectl uncordon node-name

Console

次の手順を行います。

  1. Cloud Console で GKE の [ワークロード] ダッシュボードに移動します。

    GKE の [クラスタ] ダッシュボードに移動

  2. 目的のクラスタを選択します。[ノード] タブに、ノードとそのステータスが表示されます。

ノードでスケジューリングを可能にするには、次の手順を行います。

  1. リストで、目的のノードをクリックします。

  2. [ノードの詳細] で、[閉鎖解除] ボタンをクリックします。

バインドされていない PersistentVolumeClaim

Unbound PersistentVolumeClaims は、Pod がバインドされていない PersistentVolumeClaim を参照していることを示します。このエラーは、PersistentVolume がプロビジョニングに失敗した場合に発生することがあります。プロビジョニングが失敗したことを確認するには、PersistentVolumeClaim のイベントを取得して失敗の有無を調べます。

イベントを取得するには、次のコマンドを実行します。

kubectl describe pvc statefulset-name-pvc-name-0

次のように置き換えます。

  • statefulset-name を StatefulSet オブジェクトの名前に置き換えます。
  • pvc-name は、PersistentVolumeClaim オブジェクトの名前に置き換えます。

PersistentVolume および PersistentVolumeClaim とのバインディングを手動で事前プロビジョニングする際に構成エラーがあった場合にも、このエラーが発生することがあります。ボリュームの事前プロビジョニングをもう一度行ってみてください。

接続に関する問題

ネットワークの概要で説明したように、トラブルシューティングを効果的に行うには、ポッドがネットワークの名前空間からノードのルート名前空間までどのように接続されているかを理解することが重要です。以下の説明では、特に明記しない限り、クラスタは Calico CNI ではなく GKE のネイティブ CNI を使用するものとします。つまり、ネットワーク ポリシーは適用されていません。

一部のノードの Pod が使用できない

一部のノードの Pod がネットワークに接続できない場合は、Linux ブリッジが起動していることを確認してください。

ip address show cbr0

Linux ブリッジが停止している場合は、次のように起動します。

sudo ip link set cbr0 up

ノードが、cbr0 に接続された Pod の MAC アドレスを学習していることを確認します。

arp -an

一部のノードの Pod の接続状態が悪い

一部のノードの Pod の接続状態が悪い場合は、まず、ツールボックス コンテナで tcpdump を実行して、パケットが失われていないか確認します。

sudo toolbox bash

まだインストールしていない場合は、ツールボックスに tcpdump をインストールします。

apt install -y tcpdump

cbr0 に tcpdump を実行します。

tcpdump -ni cbr0 host hostname and port port-number and [tcp|udp|icmp]

大きなパケットがブリッジのダウンストリームでドロップされているように見える場合(たとえば、TCP handshake は完了しているが SSL hello は受信されていない)、Linux ブリッジの MTU がクラスタの VPC ネットワークの MTU に正しく設定されていることを確認します。

ip address show cbr0

オーバーレイを使用する場合は(Weave や Flannel など)、オーバーレイのカプセル化オーバーヘッドに対応するために、この MTU をさらに小さくする必要があります。

断続的に接続が失敗する

Pod との接続は iptables によって転送されます。フローは conntrack テーブルのエントリとしてトラッキングされ、ノードあたりのワークロードが多いと、conntrack テーブルが枯渇して失敗となることがあります。これは、ノードのシリアル コンソールに、たとえば次のように記録されます。

nf_conntrack: table full, dropping packet

断続的な失敗の原因が conntrack の枯渇であると判断できる場合は、クラスタのサイズを大きくする(つまり、ノードあたりのワークロード数とフロー数を減らす)か、nf_conntrack_max を増やすことができます。

new_ct_max=$(awk '$1 == "MemTotal:" { printf "%d\n", $2/32; exit; }' /proc/meminfo)
sysctl -w net.netfilter.nf_conntrack_max="${new_ct_max:?}" \
  && echo "net.netfilter.nf_conntrack_max=${new_ct_max:?}" >> /etc/sysctl.conf

コンテナが "bind: Address already in use" と報告される

アプリケーションがバインドしようとしているポートがすでに予約されているため、Pod 内のコンテナを起動できないことが、コンテナログで示されています。コンテナはクラッシュ ループしています。たとえば、Cloud Logging に次のように記録されているとします。

resource.type="container"
textPayload:"bind: Address already in use"
resource.labels.container_name="redis"

2018-10-16 07:06:47.000 CEST 16 Oct 05:06:47.533 # Creating Server TCP listening socket *:60250: bind: Address already in use
2018-10-16 07:07:35.000 CEST 16 Oct 05:07:35.753 # Creating Server TCP listening socket *:60250: bind: Address already in use

Docker がクラッシュすると、実行中のコンテナが処理されずに最新状態でなくなることがあります。プロセスはまだ Pod に割り当てられたネットワーク名前空間で実行されており、そのポートをリッスンしています。Docker と kubelet は古いコンテナを認識せず、新しいプロセスで新しいコンテナを起動しようとします。しかし、ポートにバインドしようとすると、すでに Pod に関連付けられているネットワーク名前空間にポートが追加されるため、バインドできません。

この問題を診断するには、

  1. .metadata.uuid フィールドに Pod の UUID が必要です。

    kubectl get pod -o custom-columns="name:.metadata.name,UUID:.metadata.uid" ubuntu-6948dd5657-4gsgg
    
    name                      UUID
    ubuntu-6948dd5657-4gsgg   db9ed086-edba-11e8-bdd6-42010a800164
    
  2. ノードから次のコマンドの出力を取得します。

    docker ps -a
    ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]
    
  3. この Pod から実行されているプロセスを確認します。cgroup 名前空間の UUID に Pod の UUID が含まれるため、ps の出力で Pod の UUID を grep で検索します。引数にコンテナ ID が含まれる docker-containerd-shim プロセスも表示されるように、前の行も grep 検索に含めます。出力を見やすくするために、その他の cgroup 列は切り捨てます。

    # ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep -B 1 db9ed086-edba-11e8-bdd6-42010a800164 | sed s/'blkio:.*'/''/
    1283089     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 276e173b0846e24b704d4 12:
    1283107 1283089 Ss   sys_pause            4026532393         pause           /pause                                     12:
    1283150     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim ab4c7762f5abf40951770 12:
    1283169 1283150 Ss   do_wait              4026532393         sh              /bin/sh -c echo hello && sleep 6000000     12:
    1283185 1283169 S    hrtimer_nanosleep    4026532393           sleep           sleep 6000000                            12:
    1283244     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 44e76e50e5ef4156fd5d3 12:
    1283263 1283244 Ss   sigsuspend           4026532393         nginx           nginx: master process nginx -g daemon off; 12:
    1283282 1283263 S    ep_poll              4026532393           nginx           nginx: worker process
    
  4. このリストからコンテナ ID を確認でき、これは docker ps にも表示されます。

    この場合、次の手順に従います。

    • docker-containerd-shim 276e173b0846e24b704d4 は pause を実行中
    • docker-containerd-shim ab4c7762f5abf40951770 は sh で sleep を実行中(sleep-ctr)
    • docker-containerd-shim 44e76e50e5ef4156fd5d3 は nginx を実行中(echoserver-ctr)
  5. これらを docker ps の出力で確認します。

    # docker ps --no-trunc | egrep '276e173b0846e24b704d4|ab4c7762f5abf40951770|44e76e50e5ef4156fd5d3'
    44e76e50e5ef4156fd5d383744fa6a5f14460582d0b16855177cbed89a3cbd1f   gcr.io/google_containers/echoserver@sha256:3e7b182372b398d97b747bbe6cb7595e5ffaaae9a62506c725656966d36643cc                   "nginx -g 'daemon off;'"                                                                                                                                                                                                                                                                                                                                                                     14 hours ago        Up 14 hours                             k8s_echoserver-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    ab4c7762f5abf40951770d3e247fa2559a2d1f8c8834e5412bdcec7df37f8475   ubuntu@sha256:acd85db6e4b18aafa7fcde5480872909bd8e6d5fbd4e5e790ecc09acc06a8b78                                                "/bin/sh -c 'echo hello && sleep 6000000'"                                                                                                                                                                                                                                                                                                                                                   14 hours ago        Up 14 hours                             k8s_sleep-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    276e173b0846e24b704d41cf4fbb950bfa5d0f59c304827349f4cf5091be3327   k8s.gcr.io/pause-amd64:3.1
    

    通常は、ps で表示されるすべてのコンテナ ID が、docker ps で表示されます。表示されない ID がある場合、それは古くなったコンテナです。おそらく、使用中であると報告されている TCP ポートをリッスンしている docker-containerd-shim process の子プロセスが見つかります。

    これを確認するには、コンテナのネットワーク名前空間で netstat を実行します。Pod のコンテナ プロセスの pid を取得します(docker-containerd-shim ではありません)。

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

    • 1283107 - pause
    • 1283169 - sh
    • 1283185 - sleep
    • 1283263 - nginx master
    • 1283282 - nginx worker
    # nsenter -t 1283107 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # nsenter -t 1283169 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    

    また、ip netns を使用して netstat を実行できます。ただし、Docker がリンクを実行していないため、プロセスのネットワーク名前空間を手動でリンクする必要があります。

    # ln -s /proc/1283169/ns/net /var/run/netns/1283169
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns list
    1283169 (id: 2)
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns exec 1283169 netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # rm /var/run/netns/1283169
    

緩和策:

短期的な緩和策としては、上記の説明に従って古くなったプロセスを識別し、kill [PID] コマンドを使用してこのプロセスを終了します。

長期的な緩和策としては、Docker がクラッシュした原因を識別し、修正します。考えられる原因は次のとおりです。

  • ゾンビプロセスが蓄積して、PID 名前空間が不足した
  • Docker のバグ
  • リソース不足 / OOM