トラブルシューティング


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

さらにサポートが必要な場合は、Cloud カスタマーケアにお問い合わせください。

Kubernetes リソースのデバッグ

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

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

同じ Virtual Private Cloud(VPC)ネットワーク内または VPC ネットワーク ピアリングで接続された 2 つの VPC ネットワーク内にある Compute Engine VM 間の接続に問題がある場合は、内部 IP アドレスを持つ仮想マシン(VM)インスタンス間の接続のトラブルシューティングをご覧ください。

Cloud NATVPC ネイティブ クラスタ、または IP マスカレード エージェントを使用して、クラスタから外部 IP アドレスにトラフィックを送信したときにパケットロスが発生した場合は、GKE クラスタからの Cloud NAT パケットロスのトラブルシューティングをご覧ください

kubectl コマンドに関する問題のトラブルシューティング

kubectl コマンドが見つからない

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

    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 コマンドがタイムアウトしました

クラスタを作成した後、クラスタに対して kubectl コマンドを実行しようとすると、Unable to connect to the server: dial tcp IP_ADDRESS: connect: connection timed outUnable to connect to the server: dial tcp IP_ADDRESS: i/o timeout などのエラーが返されます。

これは、kubectl がクラスタ コントロール プレーンと通信できない場合に発生します。

この問題を解決するには、クラスタが設定されているコンテキストを確認します。

  1. $HOME/.kube/config に移動するか、kubectl config view コマンドを実行して、構成ファイルにクラスタ コンテキストとコントロール プレーンの外部 IP アドレスが含まれていることを確認します。

  2. クラスタの認証情報を設定します。

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location=COMPUTE_LOCATION \
        --project=PROJECT_ID
    

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

  3. 限定公開 GKE クラスタの場合は、接続元のマシンの送信 IP が既存の承認済みネットワークのリストに含まれていることを確認します。承認済みネットワークはコンソールまたは次のコマンドで確認できます。

    gcloud container clusters describe CLUSTER_NAME \
        --location=COMPUTE_LOCATION \
        --project=PROJECT_ID \
        --format "flattened(masterAuthorizedNetworksConfig.cidrBlocks[])"
    

上記のコマンドの出力で、マシンの送信 IP が承認済みネットワークのリストにない場合は、限定公開クラスタのコントロール プレーンに到達できないの説明に従ってください。Cloud Shell から接続している場合は、Cloud Shell を使用して限定公開クラスタにアクセスするの手順に沿って操作してください。

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

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

gcloud auth application-default login

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

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

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

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

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

    クラスタ内のノードの数を減らして 0 にした場合、コマンドは機能しません。

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

SSH

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

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

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

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

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

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

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

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

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

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

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

Konnectivity プロキシ

  • 次のシステム Deployment を確認して、クラスタが Konnectivity プロキシを使用しているかどうかを判断します。

    kubectl get deployments konnectivity-agent --namespace kube-system
    
  • ネットワークのファイアウォール ルールで、Konnectivity エージェントのコントロール プレーンへのアクセスが許可されていない。

    クラスタの作成時に、Konnectivity エージェント Pod がポート 8132 でコントロール プレーンへの接続を確立し、維持している。kubectl コマンドのいずれかが実行されると、API サーバーはこの接続を使用してクラスタと通信します。

    ネットワークのファイアウォール ルールに下り(外向き)拒否ルールが含まれている場合、エージェントが接続できなくなる可能性があります。ポート 8132 でクラスタ コントロール プレーンへの下り(外向き)トラフィックを許可する必要があります(API サーバーは 443 を使用します)。

  • クラスタのネットワーク ポリシーにより、kube-system Namespace から workload Namespace への上り(内向き)トラフィックがブロックされています。影響を受ける Namespace のネットワーク ポリシーを確認するには、次のコマンドを実行します。

    kubectl get networkpolicy --namespace AFFECTED_NAMESPACE
    

    この問題を解決するには、ネットワーク ポリシーの spec.ingress フィールドに次の行を追加します。

    - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: kube-system
        podSelector:
          matchLabels:
            k8s-app: konnectivity-agent
    

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

エラー 4xx のトラブルシューティング

GKE クラスタに接続する際の認証と認可のエラー

この問題は、ローカル環境から GKE クラスタで kubectl コマンドを実行しようとしたときに発生することがあります。コマンドが失敗し、エラー メッセージが表示されます。通常は HTTP ステータス コード 401(Unauthorized)が返されます。

この問題の原因は、次のいずれかである可能性があります。

  • gke-gcloud-auth-plugin 認証プラグインが正しくインストールされていないか、構成されていません。
  • クラスタ API サーバーに接続し、kubectl コマンドを実行するための権限がありません。

原因を診断するには、次の操作を行います。

curl を使用してクラスタに接続する

curl を使用すると、kubectl CLI と gke-gcloud-auth-plugin プラグインがバイパスされます。

  1. 環境変数を設定します。

    APISERVER=https://$(gcloud container clusters describe CLUSTER_NAME --location=COMPUTE_LOCATION --format "value(endpoint)")
    TOKEN=$(gcloud auth print-access-token)
    
  2. アクセス トークンが有効であることを確認します。

    curl https://oauth2.googleapis.com/tokeninfo?access_token=$TOKEN
    
  3. API サーバーのコア API エンドポイントに接続できることを確認します。

    gcloud container clusters describe CLUSTER_NAME --location=COMPUTE_LOCATION --format "value(masterAuth.clusterCaCertificate)" | base64 -d > /tmp/ca.crt
    curl -s -X GET "${APISERVER}/api/v1/namespaces" --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt
    

curl コマンドが次のような出力で失敗した場合は、クラスタにアクセスするための適切な権限があることを確認します。

{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}

curl コマンドが成功した場合は、プラグインが原因かどうかを確認します。

kubeconfig でプラグインを構成する

次の手順では、クラスタの認証時に gke-gcloud-auth-plugin バイナリを無視するようにローカル環境を構成します。バージョン 1.25 以降を実行している Kubernetes クライアントでは、gke-gcloud-auth-plugin バイナリが必須であるため、プラグインを使用せずにクラスタにアクセスする場合は、次の操作を行います。

  1. curl を使用して kubectl CLI バージョン 1.24 をインストールします。

    curl -LO https://dl.k8s.io/release/v1.24.0/bin/linux/amd64/kubectl
    

    バージョン 1.24 以前の kubectl CLI を使用できます。

  2. テキスト エディタでシェル起動スクリプト ファイル(Bash シェルの .bashrc など)を開きます。

    vi ~/.bashrc
    
  3. 次の行をファイルに追加して保存します。

    export USE_GKE_GCLOUD_AUTH_PLUGIN=False
    
  4. 起動スクリプトを実行します。

    source ~/.bashrc
    
  5. クラスタの認証情報を取得します。これにより、.kube/config ファイルが設定されます。

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location=COMPUTE_LOCATION
    

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

  6. kubectl コマンドを実行します。

    kubectl cluster-info
    

401 エラーまたは同様の認証エラーが発生した場合は、オペレーションの実行に必要な権限があることを確認してください。

エラー 400: ノードプールの再作成が必要

進行中の認証情報のローテーションを完了する場合など、コントロール プレーンとノードを再作成するアクションを実行しようとすると、次の問題が発生します。

GKE がクラスタ内の 1 つ以上のノードプールを再作成していないため、オペレーションが失敗します。バックエンドでは、ノードプールが再作成対象としてマークされていますが、実際の再作成オペレーションの開始には時間がかかることがあります。

次のようなエラー メッセージが表示されます。

ERROR: (gcloud.container.clusters.update) ResponseError: code=400, message=Node pool "test-pool-1" requires recreation.

この問題を解決するには、以下のいずれかを行います。

  • 再作成が完了するまで待ちます。既存のメンテナンスの時間枠や除外などの要因に応じて、完了までに数時間、数日、あるいは数週間かかることがあります。
  • コントロール プレーンと同じバージョンへのアップグレードを開始して、影響を受けるノードプールの再作成を手動で開始します。再作成を開始するには、次のコマンドを実行します。

    gcloud container clusters upgrade CLUSTER_NAME \
        --node-pool=POOL_NAME
    

    アップグレードが完了したら、操作をやり直してください。

エラー 403: 十分な権限がない

gcloud container clusters get-credentials を使用して GKE クラスタに接続しようとしたときに、アカウントに Kubernetes API サーバーにアクセスする権限がないと、次のエラーが発生します。

ERROR: (gcloud.container.clusters.get-credentials) ResponseError: code=403, message=Required "container.clusters.get" permission(s) for "projects/<your-project>/locations/<region>/clusters/<your-cluster>".

この問題を解決するには、次の操作を行います。

  1. アクセスの問題が発生しているアカウントを特定します。

    gcloud auth list
    
  2. Kubernetes API サーバーの認証の手順に沿って、アカウントに必要なアクセス権を付与します。

エラー 404: gcloud container コマンドを呼び出したときにリソースが見つからない

Google Cloud CLI に対する認証を再度行います。

gcloud auth login

エラー 400/403: アカウントに編集権限がない

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

Compute Engine または Kubernetes Engine API を有効にすると、Google Cloud は次のサービス アカウントとエージェントを作成します。

  • プロジェクトに対する編集権限を持つ Compute Engine のデフォルトのサービス アカウント。
  • プロジェクトに対する編集権限を持つ Google API サービス エージェント。
  • プロジェクトに対する Kubernetes Engine サービス エージェント ロールを持つ Google Kubernetes Engine サービス アカウント。

いずれかの時点で、これらの権限の編集、プロジェクトのロール バインディングの削除、サービス アカウントの完全削除、API の無効化が行われると、クラスタの作成とすべての管理機能が失敗します。

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

service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com

次のコマンドを使用して、プロジェクトに対する Kubernetes Engine サービス エージェントのロールが Google Kubernetes Engine サービス アカウントに割り当てられていることを確認できます。

gcloud projects get-iam-policy PROJECT_ID

PROJECT_ID は、実際のプロジェクト ID に置き換えます。

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

コンソール

  1. Google Cloud コンソールの [API とサービス] ページに移動します。

    [API とサービス] に移動

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

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

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

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

gcloud

gcloud CLI で次のコマンドを実行して、サービス アカウントを再度追加します。

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

GKE クラスタの作成に関する問題のトラブルシューティング

エラー CONDITION_NOT_MET: Constraintconstraint/compute.vmExternalIpAccess 違反

組織のポリシーの制約 constraints/compute.vmExternalIpAccessDeny All に構成されているか、GKE 一般公開クラスタを作成しようとしている組織、フォルダ、プロジェクト レベルの特定の VM インスタンスに外部 IP が制限されています。

GKE 一般公開クラスタを作成すると、このクラスタのワーカーノードの基盤となる Compute Engine VM に外部 IP アドレスが割り当てられます。組織のポリシーの制約 constraints/compute.vmExternalIpAccessDeny All に構成する場合、または外部 IP を特定の VM インスタンスに制限する場合は、このポリシーによって GKE ワーカーノードが外部 IP アドレスを取得できなくなるため、クラスタ作成エラーが発生します。

クラスタ作成オペレーションのログを見つけるには、ログ エクスプローラで次のような検索クエリを使用し、GKE クラスタ オペレーションの監査ログを確認します。

resource.type="gke_cluster"
logName="projects/test-last-gke-sa/logs/cloudaudit.googleapis.com%2Factivity"
protoPayload.methodName="google.container.v1beta1.ClusterManager.CreateCluster"
resource.labels.cluster_name="CLUSTER_NAME"
resource.labels.project_id="PROJECT_ID"

この問題を解決するには、GKE 一般公開クラスタを作成するプロジェクトで、制約 constraints/compute.vmExternalIpAccess の有効なポリシーが Allow All であることを確認します。この制約の使用方法については、外部 IP アドレスを特定の VM インスタンスに制限するをご覧ください。制約を Allow All に設定した後、障害が発生したクラスタを削除して新しいクラスタを作成します。障害が発生したクラスタを修復できないため、この操作が必要になります。

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

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

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 の名前に置き換えます。

コンソール

次の操作を行います。

  1. Google Cloud コンソールの [ワークロード] ページに移動します。

    [ワークロード] に移動

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

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

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

CrashLoopBackOff

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

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

Google Cloud コンソールを使用して CrashLoopBackOff エラーをトラブルシューティングできます。

  1. クラッシュループしている Pod のインタラクティブ ハンドブックに移動します。

    ハンドブックに移動

  2. [ クラスタ] に、トラブルシューティングするクラスタの名前を入力します。

  3. [ 名前空間] に、トラブルシューティングする名前空間を入力します。

  4. (省略可)今後 CrashLoopBackOff エラーが発生した場合に通知するアラートを作成します。

    1. [今後の対応のヒント] で [アラートの作成] を選択します。

ログを検査する

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

kubectl

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

kubectl get pods

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

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

kubectl logs POD_NAME

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

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

コンソール

次の操作を行います。

  1. Google Cloud コンソールの [ワークロード] ページに移動します。

    [ワークロード] に移動

  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 は、コンテナが使用するイメージをイメージ レジストリからロードできないことを示します。

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

kubectl

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

kubectl describe pod POD_NAME

コンソール

次の操作を行います。

  1. Google Cloud コンソールの [ワークロード] ページに移動します。

    [ワークロード] に移動

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

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

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

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

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

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

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

      たとえば、VM に SSH で接続するには、次のコマンドを実行します。

      gcloud compute ssh VM_NAME --zone=ZONE_NAME
      

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

    • docker-credential-gcr configure-docker を実行します。このコマンドにより、/home/[USER]/.docker/config.json に構成ファイルが生成されます。このファイルの credHelpers フィールドにイメージのレジストリが含まれていることを確認します。たとえば、次のファイルには、asia.gcr.io、eu.gcr.io、gcr.io、marketplace.gcr.io、us.gcr.io でホストされているイメージの認証情報が含まれています。

      {
        "auths": {},
        "credHelpers": {
          "asia.gcr.io": "gcr",
          "eu.gcr.io": "gcr",
          "gcr.io": "gcr",
          "marketplace.gcr.io": "gcr",
          "us.gcr.io": "gcr"
        }
      }
      
    • docker pull IMAGE_NAME を実行します。

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

権限拒否エラー

「permission denied」または「no pull access」というエラーが発生した場合は、ログインしていることと、イメージへのアクセス権があることを確認してください。イメージをホストするレジストリに応じて、次のいずれかの方法を試してください。

Artifact Registry

イメージが Artifact Registry にある場合、ノードプールのサービス アカウントには、イメージを含むリポジトリへの読み取りアクセス権が必要です。

サービス アカウントに artifactregistry.reader ロールを付与します。

gcloud artifacts repositories add-iam-policy-binding REPOSITORY_NAME \
    --location=REPOSITORY_LOCATION \
    --member=serviceAccount:SERVICE_ACCOUNT_EMAIL \
    --role="roles/artifactregistry.reader"

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

  • REPOSITORY_NAME: Artifact Registry リポジトリの名前。
  • REPOSITORY_LOCATION: Artifact Registry リポジトリのリージョン
  • SERVICE_ACCOUNT_EMAIL: ノードプールに関連付けられた IAM サービス アカウントのメールアドレス。

Container Registry

イメージが Container Registry にある場合、ノードプールのサービス アカウントには、イメージを含む Cloud Storage バケットに対する読み取りアクセス権が必要です。

サービス アカウントに roles/storage.objectViewer ロールを付与して、バケットから読み取れるようにします。

gsutil iam ch \
serviceAccount:SERVICE_ACCOUNT_EMAIL:roles/storage.objectViewer \
  gs://BUCKET_NAME

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

  • SERVICE_ACCOUNT_EMAIL: ノードプールに関連付けられたサービス アカウントのメールアドレス。gcloud iam service-accounts list を使用すると、プロジェクトのサービス アカウントを一覧表示できます。
  • BUCKET_NAME: イメージを含む Cloud Storage バケットの名前。gsutil ls を使用すると、プロジェクト内のバケットを一覧表示できます。

レジストリ管理者が、Container Registry ではなく gcr.io ドメインのイメージを保存するように Artifact Registry で gcr.io リポジトリを設定している場合は、Container Registry ではなく、Artifact Registry に対する読み取りアクセス権をユーザーに付与する必要があります。

非公開レジストリ

イメージが非公開レジストリにある場合、イメージにアクセスするための鍵が必要になることがあります。詳細については、非公開レジストリの使用をご覧ください。

401 Unauthorized: 非公開の Container Registry リポジトリからイメージを pull できない

非公開の Container Registry リポジトリからイメージを pull すると、次のようなエラーが発生する可能性があります。

gcr.io/PROJECT_ID/IMAGE:TAG: rpc error: code = Unknown desc = failed to pull and
unpack image gcr.io/PROJECT_ID/IMAGE:TAG: failed to resolve reference
gcr.io/PROJECT_ID/IMAGE]:TAG: unexpected status code [manifests 1.0]: 401 Unauthorized

Warning  Failed     3m39s (x4 over 5m12s)  kubelet            Error: ErrImagePull
Warning  Failed     3m9s (x6 over 5m12s)   kubelet            Error: ImagePullBackOff
Normal   BackOff    2s (x18 over 5m12s)    kubelet            Back-off pulling image
  1. Pod を実行しているノードを特定します。

    kubectl describe pod POD_NAME | grep "Node:"
    
  2. ノードにストレージ スコープがあることを確認します。

    gcloud compute instances describe NODE_NAME \
        --zone=COMPUTE_ZONE --format="flattened(serviceAccounts[].scopes)"
    

    ノードのアクセス スコープには、次のうち少なくとも 1 つが含まれている必要があります。

    serviceAccounts[0].scopes[0]: https://www.googleapis.com/auth/devstorage.read_only
    serviceAccounts[0].scopes[0]: https://www.googleapis.com/auth/cloud-platform
    
  3. ノードが属するノードプールを十分なスコープで再作成します。既存のノードは変更できません。正しいスコープでノードを再作成する必要があります。

    • 推奨: gke-default スコープで新しいノードプールを作成します。

      gcloud container node-pools create NODE_POOL_NAME \
          --cluster=CLUSTER_NAME \
          --zone=COMPUTE_ZONE \
          --scopes="gke-default"
      
    • ストレージ スコープのみを持つ新しいノードプールを作成します。

      gcloud container node-pools create NODE_POOL_NAME \
          --cluster=CLUSTER_NAME \
          --zone=COMPUTE_ZONE \
          --scopes="https://www.googleapis.com/auth/devstorage.read_only"
      

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

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

Kubernetes API サーバーと Kubernetes スケジューラーの指標を Cloud Monitoring に送信するように GKE クラスタを構成している場合は、スケジューラーの指標API サーバーの指標でエラーの詳細を確認できます。

Google Cloud コンソールを使用して PodUnschedulable エラーをトラブルシューティングできます。

  1. スケジュール不可の Pod のインタラクティブ ハンドブックに移動します。

    ハンドブックに移動

  2. [ クラスタ] に、トラブルシューティングするクラスタの名前を入力します。

  3. [ 名前空間] に、トラブルシューティングする名前空間を入力します。

  4. (省略可)今後 PodUnschedulable エラーが発生した場合に通知するアラートを作成します。

    1. [今後の対応のヒント] で [アラートの作成] を選択します。

リソースの不足

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

Pod のリソース リクエストが有効なノードプールの単一ノードの上限を超えた場合、GKE は Pod をスケジュールしません。また、新しいノードを追加するためのスケールアップもトリガーしません。GKE が Pod をスケジュールするには、Pod 用にリクエストするリソースを減らすか、十分なリソースを持つ新しいノードプールを作成する必要があります。

また、ノードの自動プロビジョニングを有効にして、スケジュールされていない Pod が実行可能なノードを含むノードプールを自動的に作成することもできます。

デフォルトの 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: ラベルの値。

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

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 をスケジュールできません。ノードのステータスを確認するには、Google Cloud コンソールまたは kubectl コマンドライン ツールを使用します。

kubectl

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

kubectl get nodes

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

kubectl uncordon NODE_NAME

コンソール

次の操作を行います。

  1. Google Cloud コンソールで Google Kubernetes Engine のページに移動します。

    Google Kubernetes Engine に移動

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

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

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

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

ノードあたりの Pod 数の上限に達した

クラスタ内のすべてのノードがノードあたりの最大 Pod 数の上限に達すると、Pod はスケジュール不可の状態で停止します。Pod の [イベント] タブに、Too many pods というフレーズを含むメッセージが表示されます。

  1. Google Cloud コンソールで GKE クラスタの詳細に移動し、[ノード] タブで Maximum pods per node 構成を確認します。

  2. ノードのリストを取得します。

    kubectl get nodes
    
  3. ノードごとに、そのノードで実行されている Pod の数を確認します。

    kubectl get pods -o wide | grep NODE_NAME | wc -l
    
  4. 上限に達している場合は、新しいノードプールを追加するか、既存のノードプールにノードを追加します。

クラスタ オートスケーラーが有効な状態でノードプールの最大サイズに達した

ノードプールがクラスタ オートスケーラーの構成に従って最大サイズに達した場合、GKE は、このノードプールでスケジュールされている Pod のスケールアップをトリガーしません。このノードプールで Pod をスケジュールする場合は、クラスタ オートスケーラーの構成を変更します。

クラスタ オートスケーラーが無効な状態でノードプールの最大サイズに達した

クラスタ オートスケーラーが無効になっている状態でノードプールが最大ノード数に達した場合、GKE はノードプールで Pod をスケジュールできません。ノードプールのサイズを増やすか、GKE でクラスタ オートスケーラーを有効にして、クラスタのサイズを自動的に変更します。

PersistentVolumeClaim がバインドされていない

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

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

kubectl describe pvc STATEFULSET_NAME-PVC_NAME-0

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

  • STATEFULSET_NAME: StatefulSet オブジェクトの名前。
  • PVC_NAME: PersistentVolumeClaim オブジェクトの名前。

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

割り当て不足

GKE がクラスタをスケールアップするのに十分な Compute Engine 割り当てがプロジェクトにあることを確認します。GKE が Pod をスケジュールするためにクラスタにノードを追加しようとしたときに、スケールアップがプロジェクトの使用可能な割り当てを超えると、scale.up.error.quota.exceeded エラー メッセージが表示されます。

詳細については、ScaleUp エラーをご覧ください。

非推奨 API

クラスタのマイナー バージョンと一緒に削除される非推奨の API を使用していないことを確認します。詳細については、GKE のサポートの終了をご覧ください。

接続に関する問題

ネットワークの概要で説明したように、トラブルシューティングを効果的に行うには、Pod がネットワークの名前空間からノードのルート名前空間までどのように接続されているかを理解することが重要です。以下の説明では、特に明記しない限り、クラスタは 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 Pod インターフェースの MTU がクラスタの VPC ネットワークの MTU に正しく設定されていることを確認します。

ip address show cbr0

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

GKE MTU

Pod インターフェースに選択される MTU は、クラスタノードで使用される Container Network Interface(CNI)と、基盤となる VPC MTU の設定によって異なります。詳細については、Pod をご覧ください。

Pod インターフェースの MTU 値は 1460 か、ノードのプライマリ インターフェースから継承されます。

CNI MTU GKE Standard
kubenet 1460 デフォルト
kubenet
(GKE バージョン 1.26.1 以降)
継承 デフォルト
Calico 1460

--enable-network-policy を使用して有効にします。

詳細については、ネットワーク ポリシーを使用して Pod と Service 間の通信を制御するをご覧ください。

netd 継承 次のいずれかを使用して有効にします。
GKE Dataplane V2 継承

--enable-dataplane-v2 を使用して有効にします。

詳細については、GKE Dataplane V2 の使用をご覧ください。

断続的に接続が失敗する

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

NodeLocal DNSCache を使用して接続トラッキング エントリを減らすこともできます。

コンテナが「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   registry.k8s.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

エラー: 「failed to allocate for range 0: no IP addresses in range set」

GKE バージョン 1.18.17 以降では、コンテナの起動前に Pod が削除された場合に、メモリ不足(OOM)イベントによって Pod の強制排除が誤って発生する問題を修正しました。この誤った強制排除により、孤立した Pod が、割り当てられたノード範囲の予約済み IP アドレスを保持し続ける可能性があります。時間の経過とともに孤立した Pod が増加したため、GKE で新しい Pod に割り当てる IP アドレスが不足しています。割り当てられたノード範囲に新しい Pod に割り当てる IP がなかったため、エラー メッセージ failed to allocate for range 0: no IP addresses in range set が表示されています。

この問題を解決するには、GKE バージョン 1.18.17 以降にクラスタとノードプールをアップグレードします。

GKE バージョン 1.18.17 より前のクラスタでこの問題を回避または解決するには、リソースの上限を引き上げ、今後の OOM イベントを回避してから、孤立した Pod を削除し、IP アドレスを再利用します。

GKE IP アドレスの使用率の分析情報を確認することもできます。

影響を受けるノードから孤立した Pod を削除する

孤立した Pod を削除するには、ノードをドレインするか、ノードプールをアップグレードするか、影響を受けるディレクトリを移動します。

ノードをドレインする(推奨)

  1. ノードを閉鎖して、新しい Pod がそのノードでスケジュールされないようにします。

     kubectl cordon NODE
    

    NODE は、ドレインするノードの名前に置き換えます。

  2. ノードをドレインします。GKE は、Deployment で管理されている Pod を他のノードに自動的に再スケジュールします。管理リソースを持たない孤立した Pod をドレインするには、--force フラグを使用します。

     kubectl drain NODE --force
    
  3. ノードの閉鎖を解除して、GKE が新しい Pod をスケジュールできるようにします。

     kubectl uncordon NODE
    

影響を受けるディレクトリを移動する

/var/lib/kubelet/pods で孤立した Pod ディレクトリを特定し、メイン ディレクトリから移動して、GKE が Pod を終了できるようにします。

リソースの終了に関する問題のトラブルシューティング

Namespace が Terminating 状態のままになる

Namespace は、Kubernetes のファイナライザを使用して、Namespace 内にまだリソースが残っている場合の削除を防いでいます。kubectl delete コマンドを使用して Namespace を削除すると、Kubernetes が依存リソースを削除してすべてのファイナライザをクリアするまで、Namespace は Terminating 状態になります。Namespace ライフサイクル コントローラは、まず Namespace 内で GKE が削除する必要のあるリソースのリストを作成します。GKE が依存リソースを削除できない場合か、Namespace ライフサイクル コントローラが Namespace が空の状態であることを確認できない場合、問題を解決するまで Namespace は Terminating 状態のままになります。

Namespace が Terminating 状態のままになっている問題を解決するには、削除をブロックしている異常なコンポーネントを特定して削除する必要があります。次のいずれかの解決策を試してください。

利用できない API サービスを見つけて削除する

  1. 使用できない API サービスの一覧を取得します。

    kubectl get apiservice | grep False
    
  2. 応答していないサービスのトラブルシューティングを行います。

    kubectl describe apiservice API_SERVICE
    

    API_SERVICE は、応答していないサービスの名前に置き換えます。

  3. Namespace が終了処理中かどうか確認します。

    kubectl get ns | grep Terminating
    

残りのリソースを見つけて削除する

  1. 終了処理中の Namespace に残っているリソースの一覧を取得します。

    kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get -n NAMESPACE
    

    NAMESPACE は、削除する Namespace の名前に置き換えます。

  2. 出力にあるリソースをすべて削除します。

  3. Namespace が終了処理中かどうか確認します。

    kubectl get ns | grep Terminating
    

Namespace を強制削除する

Namespace の削除をブロックしているファイナライザを削除すると、Namespace を強制終了できます。

  1. 次のマニフェストを YAML ファイルとして保存します。

    kubectl get ns NAMESPACE -o yaml > ns-terminating.yml
    
  2. テキスト エディタでマニフェストを開き、spec.finalizers フィールドのすべての値を削除します。

    vi ns-terminating.yml
    
  3. ファイナライザ フィールドが空であることを確認します。

    cat ns-terminating.yml
    

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

    apiVersion: v1
    kind: Namespace
    metadata:
      annotations:
      name: NAMESPACE
    spec:
      finalizers:
    status:
      phase: Terminating
    
  4. HTTP プロキシを開始して Kubernetes API にアクセスします。

    kubectl proxy
    
  5. curl を使用して Namespace マニフェストを置き換えます。

    curl -H "Content-Type: application/yaml" -X PUT --data-binary @ns-terminating.yml http://127.0.0.1:8001/api/v1/namespaces/NAMESPACE/finalize
    
  6. Namespace が終了処理中かどうか確認します。

    kubectl get ns | grep Terminating
    

GKE クラスタの Cloud NAT パケットロスのトラブルシューティング

VPC ネイティブの GKE 限定公開クラスタのノード VM には外部 IP アドレスがなく、単独ではインターネットに接続できません。限定公開クラスタがパブリック接続を確立できるようにするには、Cloud NAT を使用して外部 IP アドレスとポートを割り振ります。

ノード VM が Cloud NAT からの外部ポートと IP アドレスの割り当てを使い果たすと、パケットは破棄されます。これを回避するには、アウトバウンド パケットレートを下げるか、使用可能な Cloud NAT の送信元 IP アドレスとポートの割り当てを増やします。以降のセクションでは、GKE 限定公開クラスタのコンテキストで Cloud NAT からのパケットロスを診断し、トラブルシューティングする方法について説明します。

パケットロスの診断

このセクションでは、Cloud Logging を使用してドロップ パケットをロギングし、Cloud Monitoring を使用してパケット ドロップの原因を診断する方法について説明します。

ドロップされたパケットのロギング

Cloud Logging で次のクエリを使用して、ドロップされたパケットをロギングできます。

resource.type="nat_gateway"
resource.labels.region=REGION
resource.labels.gateway_name=GATEWAY_NAME
jsonPayload.allocation_status="DROPPED"
  • REGION: クラスタが存在するリージョンの名前。
  • GATEWAY_NAME: Cloud NAT ゲートウェイの名前。

このコマンドは、Cloud NAT ゲートウェイによってドロップされたすべてのパケットの一覧を返しますが、原因は特定されません。

パケットロスの原因のモニタリング

パケットがドロップされた原因を特定するには、Cloud Monitoring の指標オブザーバーにクエリを実行します。いずれかでパケットがドロップする原因としては、次の 3 つの理由が考えられます。

OUT_OF_RESOURCES または ENDPOINT_ALLOCATION_FAILED エラーコードが原因でドロップされたパケットを特定するには、次のクエリを使用します。

fetch nat_gateway
  metric 'router.googleapis.com/nat/dropped_sent_packets_count'
  filter (resource.gateway_name == NAT_NAME)
  align rate(1m)
  every 1m
  group_by [metric.reason],
    [value_dropped_sent_packets_count_aggregate:
       aggregate(value.dropped_sent_packets_count)]

NAT_ALLOCATION_FAILED エラーコードが原因でドロップされたパケットを特定するには、次のクエリを使用します。

fetch nat_gateway
  metric 'router.googleapis.com/nat/nat_allocation_failed'
  group_by 1m,
    [value_nat_allocation_failed_count_true:
       count_true(value.nat_allocation_failed)]
  every 1m

GKE IP マスカレードを使用した Cloud NAT のトラブルシューティング

上記のクエリで空の結果が返され、GKE Pod が外部 IP アドレスと通信できない場合は、構成のトラブルシューティングを行います。

構成 トラブルシューティング
サブネットのプライマリ IP アドレス範囲にのみ適用されるように構成された Cloud NAT。 Cloud NAT がサブネットのプライマリ IP アドレス範囲にのみ構成されている場合、クラスタから外部 IP アドレスに送信されるパケットに送信元ノードの IP アドレスが必要です。この Cloud NAT 構成では、次のことが発生します。
  • 外部 IP アドレスの宛先が IP マスカレードの対象である場合、Pod は外部 IP アドレスにパケットを送信できます。ip-masq-agent をデプロイするときに、nonMasqueradeCIDRs リストに宛先 IP アドレスとポートが含まれていないことを確認します。これらの宛先に送信されたパケットは、まず送信元ノードの IP アドレスに変換されてから、Cloud NAT によって処理されます。
  • この Cloud NAT 構成を使用して Pod がすべての外部 IP アドレスに接続できるようにするには、ip-masq-agent がデプロイされ、nonMasqueradeCIDRs リストにクラスタのノードと Pod IP アドレス範囲のみが含まれていることを確認します。クラスタ外の宛先に送信されたパケットは、まず送信元ノードの IP アドレスに変換されてから、Cloud NAT によって処理されます。
  • Pod が一部の外部 IP アドレスにパケットを送信しないようにするには、これらのアドレスを明示的にブロックして、マスカレードされないようにする必要があります。ip-masq-agent がデプロイされたら、ブロックする外部 IP アドレスを nonMasqueradeCIDRs リストに追加します。これらの宛先に送信されたパケットは、元の Pod IP アドレスの送信元とともにノードから送信されます。Pod の IP アドレスは、クラスタのサブネットのセカンダリ IP アドレス範囲から取得されます。この構成では、Cloud NAT はそのセカンダリ範囲で動作しません。
Pod IP に使用されるサブネットのセカンダリ IP アドレス範囲にのみ適用されるように構成された Cloud NAT。

Cloud NAT が、クラスタの Pod IP によって使用されるサブネットのセカンダリ IP アドレス範囲にのみ構成されている場合、クラスタから外部 IP アドレスに送信されるパケットに送信元 Pod IP アドレスが必要です。この Cloud NAT 構成では、次のことが発生します。

  • IP マスカレード エージェントを使用すると、パケットが Cloud NAT によって処理されるときに送信元 Pod IP アドレスが失われます。送信元 Pod の IP アドレスを保持するには、nonMasqueradeCIDRs リストに宛先 IP アドレス範囲を指定します。ip-masq-agent がデプロイされると、nonMasqueradeCIDRs リストの宛先に送信されたパケットは、Cloud NAT によって処理される前に送信元 Pod IP アドレスを保持します。
  • この Cloud NAT 構成を使用して Pod がすべての外部 IP アドレスに接続できるようにするには、ip-masq-agent がデプロイされ、nonMasqueradeCIDRs リストが可能な限り大きくなっていることを確認します(0.0.0.0/0 に、すべての IP アドレスの宛先を指定します)。すべての宛先に送信されたパケットは、Cloud NAT によって処理される前に送信元 Pod IP アドレスを保持します。

パケットロスを回避するための最適化

パケットロスは、次の方法で防ぐことができます。

アプリケーションの最適化

アプリケーションが同じ宛先 IP アドレスとポートに対して複数のアウトバウンド接続を行う場合、割り当てられた NAT 送信元アドレスと送信元ポートのタプルの数を使用して、Cloud NAT がその宛先に対して確立できるすべての接続がすぐに消費される可能性があります。このシナリオでは、アプリケーションのアウトバウンド パケットレートを下げることでパケットロスを減らします。

宛先への同時接続数の制限など、Cloud NAT が NAT の送信元アドレスと送信元ポートを使用して接続を確立する方法については、ポートと接続をご覧ください。

アプリケーションからのアウトバウンド接続のレートを下げると、パケットロスを軽減できます。これは、開いている接続を再利用することで実現できます。接続を再利用する一般的な方法としては、接続プーリング、HTTP/2 などのプロトコルを使用した接続の多重化、複数のリクエストで再利用される永続的な接続の確立、などがあります。詳細については、ポートと接続をご覧ください。

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

クラスタのコントロール プレーンで実行されている Kubernetes のバージョンと、クラスタのノードプールで実行されている Kubernetes のバージョンを確認します。クラスタのノードプールのいずれかがコントロール プレーンの 2 つ前のマイナー バージョンよりも古い場合、クラスタで問題が生じる可能性があります。

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

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

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

アップグレード プロセスにより、影響を受けるノードで実行されているワークロードが中断されることが懸念される場合は、次の手順でワークロードを新しいノードプールに移行します。

  1. 互換性のあるバージョンで新しいノードプールを作成します。
  2. 既存のノードプールのノードを閉鎖します。
  3. 必要に応じて、既存のノードプールで実行されているワークロードを更新して、ラベル cloud.google.com/gke-nodepool:NEW_NODE_POOL_NAME の nodeSelector を追加します。ここで、NEW_NODE_POOL_NAME は新しいノードプールの名前です。これにより、GKE はこれらのワークロードを新しいノードプールのノードに配置します。
  4. 既存のノードプールをドレインします。
  5. 新しいノードプールでワークロードが正常に実行されていることを確認します。問題がなければ、古いノードプールを削除できます。ワークロードの中断が発生した場合は、既存のノードプール内のノードの閉鎖を解除し、新しいノードをドレインして、既存のノードでワークロードを再スケジュールします。問題のトラブルシューティングを行ってから、もう一度お試しください。

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

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

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

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

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

    gcloud container clusters describe CLUSTER_NAME
    

    このコマンドの出力では、次のように、monitoringConfig セクションの enableComponents のリストに SYSTEM_COMPONENTS が含まれているはずです。

    monitoringConfig:
      componentConfig:
        enableComponents:
        - SYSTEM_COMPONENTS
    

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

    gcloud container clusters update CLUSTER_NAME --monitoring=SYSTEM
    
  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 のドキュメントをご覧ください。

アカウントに共有 VPC クラスタに対する権限がない

共有 VPC クラスタの場合は、サービス プロジェクトの GKE サービス アカウントに、ホスト プロジェクトの Host サービス エージェント ユーザーのロールを割り当てる必要があります。その場合は、gcloud CLI を使用します。

ロール バインディングが存在するかどうかを確認するには、ホスト プロジェクトで次のコマンドを実行します。

gcloud projects get-iam-policy PROJECT_ID \
  --flatten="bindings[].members" \
  --format='table(bindings.role)' \
  --filter="bindings.members:SERVICE_ACCOUNT_NAME

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

  • PROJECT_ID: ホスト プロジェクトの ID。
  • SERVICE_ACCOUNT_NAME: GKE サービス アカウント名。

出力で roles/container.hostServiceAgentUser ロールを探します。

ROLE
...
roles/container.hostServiceAgentUser
...

hostServiceAgentUser ロールがこのリストにない場合は、Host サービス エージェント ユーザーのロールを付与するの手順に沿って、バインディングをサービス アカウントに追加します。

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

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

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

gcloud

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

gcloud projects get-iam-policy PROJECT_ID

PROJECT_ID は、実際のプロジェクト ID に置き換えます。

コンソール

Google Cloud コンソールで [IAM と管理] ページにアクセスします。

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

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

PROJECT_NUMBER=$(gcloud projects describe "PROJECT_ID" --format 'get(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

Compute Engine のデフォルトのサービス アカウントを有効にする

ノードプールに使用されるサービス アカウント(通常は Compute Engine のデフォルトのサービス アカウント)が無効になっていると、ノードをクラスタに登録できないことがあります。

プロジェクトでサービス アカウントが無効になっているかどうかを確認するには、gcloud CLI または Google Cloud コンソールを使用します。

gcloud

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

gcloud iam service-accounts list  --filter="NAME~'compute' AND disabled=true"

コンソール

Google Cloud コンソールの [IAM と管理] ページに移動します。

コマンドまたはダッシュボードでサービス アカウントが無効になっていることが示されている場合は、次のコマンドを実行してサービス アカウントを有効にします。

gcloud iam service-accounts enable PROJECT_ID-compute@developer.gserviceaccount.com

PROJECT_ID は、実際のプロジェクト ID に置き換えます。

それでもノード登録の問題が解決しない場合は、ノード登録のトラブルシューティングでトラブルシューティングの手順をご覧ください。

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

ノードを割り当て可能にした後、Pod が保留中のままになった場合、次の点に注意してください。

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

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

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

  • クラスタのサイズの変更を検討します。手順については、クラスタのサイズ変更をご覧ください。

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

クラスタのルート認証局の有効期限が近づいている

クラスタのルート認証局の有効期限が近づいています。通常のクラスタ操作が中断されないようにするには、認証情報のローテーションを行う必要があります。

「Instance 'Foo' does not contain 'instance-template' metadata」というエラーが表示される

アップグレード、スケーリング、ノードの自動修復に失敗したノードプールのステータスとして、「Instance 'Foo' does not contain 'instance-template' metadata」というエラーが表示される場合があります。

このメッセージは、GKE によって割り当てられた VM インスタンスのメタデータが破損したことを示します。これは通常、カスタム作成の自動化またはスクリプトが新しいインスタンス メタデータ(block-project-ssh-keys など)を追加しようとしたときに、値を追加または更新するだけでなく、既存のメタデータも削除した場合に発生します。VM インスタンスのメタデータについては、カスタム メタデータの設定をご覧ください。

重要なメタデータ値(instance-templatekube-labelskubelet-configkubeconfigcluster-nameconfigure-shcluster-uid)が削除されると、これらの値は GKE のオペレーションに不可欠であるため、ノードまたはノードプール全体が不安定な状態になる可能性があります。

インスタンス メタデータが破損した場合、メタデータを復元する最良の方法は、破損した VM インスタンスを含むノードプールを再作成することです。クラスタにノードプールを追加して新しいノードプールのノード数を増やすと同時に、別のノードプールでノードの閉鎖と削除を行う必要があります。ノードプール間でワークロードを移行する手順をご覧ください。

インスタンス メタデータを編集したユーザーと日時を確認するには、Compute Engine 監査ロギング情報を確認するか、次のようなクエリを使用してログ エクスプローラでログを検索します。

resource.type="gce_instance_group_manager"
protoPayload.methodName="v1.compute.instanceGroupManagers.setInstanceTemplate"

ログには、リクエスト送信元の IP アドレスとユーザー エージェントが記録されています。

requestMetadata: {
  callerIp: "REDACTED"
  callerSuppliedUserAgent: "google-api-go-client/0.5 GoogleContainerEngine/v1"
}

Cloud KMS 鍵が無効になっている

次のエラー メッセージは、GKE のデフォルトのサービス アカウントが Cloud KMS 鍵にアクセスできない場合に発生します。

Cluster problem detected (Kubernetes Engine Service Agent account unable to use CloudKMS key configured for Application Level encryption).

この問題を解決するには、無効にした鍵を再度有効にします

GKE の Secret の詳細については、アプリケーション レイヤで Secret を暗号化するをご覧ください。