GKE Sandbox によるワークロード分離の強化


このページでは、Pod 内のコンテナが不明なコードや信頼できないコードを実行したとき、またはそのノードから特別に切り離す必要があるときに、GKE Sandbox を使用してノードのホストカーネルを保護する方法について説明します。

GKE Sandbox を有効にする

GKE Sandbox は、新しいクラスタまたは既存のクラスタで有効にできます。

始める前に

作業を始める前に、次のことを確認してください。

次のいずれかの方法で gcloud のデフォルトの設定を指定します。

  • gcloud init。デフォルトの設定全般を確認する場合に使用します。
  • gcloud config。プロジェクト ID、ゾーン、リージョンを個別に設定する場合に使用します。

gcloud init の使用

エラー One of [--zone, --region] must be supplied: Please specify location を受信した場合は、このセクションの内容を実施します。

  1. gcloud init を実行して、次の操作を行います。

    gcloud init

    リモート サーバーで SSH を使用している場合は、--console-only フラグを指定して、コマンドがブラウザを起動しないようにします。

    gcloud init --console-only
  2. 手順に従って gcloud を承認し、Google Cloud アカウントを使用します。
  3. 新しい構成を作成するか、既存の構成を選択します。
  4. Google Cloud プロジェクトを選択します。
  5. ゾーンクラスタの場合はデフォルトの Compute Engine ゾーン、リージョン クラスタまたは Autopilot クラスタの場合はデフォルトの Compute Engine リージョンを選択します。

gcloud config の使用

  • デフォルトのプロジェクト ID を設定します。
    gcloud config set project PROJECT_ID
  • ゾーンクラスタを使用する場合は、デフォルトのコンピューティング ゾーンを設定します。
    gcloud config set compute/zone COMPUTE_ZONE
  • Autopilot クラスタまたはリージョン クラスタを使用する場合は、デフォルトのコンピューティング リージョンを設定します。
    gcloud config set compute/region COMPUTE_REGION
  • gcloud を最新バージョンに更新します。
    gcloud components update
  • GKE Sandbox を使用するには、クラスタ コントロール プレーンとノードに GKE バージョン 1.13.5-gke.15 以降が必要です。
  • Cloud SDK のバージョンが 243.0.0 以降であることを確認します。

新しいクラスタの場合

GKE Sandbox を有効にするには、ノードプールを構成します。デフォルトのノードプール(クラスタの作成時に作成される最初のノードプール)は GKE Sandbox を使用できません。クラスタの作成時に GKE Sandbox を有効にするには、クラスタを作成するときに 2 つ目のノードプールを追加する必要があります。

Console

クラスタを表示するには、Cloud Console で Google Kubernetes Engine のメニューに移動します。

  1. Cloud Console で Google Kubernetes Engine ページに移動します。

    Google Kubernetes Engine に移動

  2. [ 作成] をクリックします。

  3. 省略可能、ただし推奨: ナビゲーション パネルの [クラスタ] で [機能] をクリックし、GKE 用 Cloud Operations を有効にします。これにより、gVisor メッセージがログに記録されます。

  4. [ ノードプールを追加] をクリックします。

  5. ナビゲーション パネルの [ノードプール] で新しいノードプールを展開し、[ノード] をクリックします。

  6. ノードプールに次の設定を構成します。

    1. [イメージの種類] プルダウン リストから、[containerd(cos_containerd)が含まれている Container-Optimized OS] を選択します。 これは、GKE Sandbox でサポートされている唯一のイメージタイプです。
    2. [マシンの構成] で [シリーズ] と [マシンタイプ] を選択します。

  7. ナビゲーション パネルで、構成するノードプールの名前の下にある [セキュリティ] をクリックし、[gVisor でサンドボックスを有効にする] チェックボックスをオンにします。

  8. クラスタとノードプールを必要に応じて構成します。

  9. [作成] をクリックします。

gcloud

デフォルトのノードプールで GKE Sandbox を有効にすることはできません。また、新しいクラスタを作成するときに、gcloud コマンドを実行します。代わりに、通常どおりにクラスタを作成します。必須ではありませんが、Cloud Operations for GKE を有効にして、gVisor メッセージをログに記録することをおすすめします。

次に、-- sandbox フラグを type=gvisor に設定して、gcloud container node-pools create コマンドを実行します。GKE Sandbox では、ノードイメージ タイプを cos_containerd にする必要があります。

gcloud container node-pools create NODE_POOL_NAME \
  --cluster=CLUSTER_NAME \
  --node-version=NODE_VERSION \
  --machine-type=MACHINE_TYPE \
  --image-type=cos_containerd \
  --sandbox type=gvisor

次の変数を置き換えます。

  • NODE_POOL_NAME: 新しいノードプールの名前。
  • CLUSTER_NAME: クラスタの名前。
  • NODE_VERSION: ノードプールに使用するバージョン。
  • MACHINE_TYPE: ノードに使用するマシンのタイプ。GKE Sandbox は e2-microe2-smalle2-medium マシンタイプをサポートしていません。

1.18.4-gke.1300 より前は、ノード作成中に gvisor RuntimeClass がインスタンス化されます。ノードにワークロードをスケジュールする前に、次のコマンドを使用して gvisor RuntimeClass が存在するかどうかを確認します。

kubectl get runtimeclasses

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

NAME     AGE
gvisor   19s

1.17.9-gke.1500 以前のバージョン、または 1.18.6-gke.600 以前の 1.18 バージョンを実行している場合は、gvisor.config.common-webhooks.networking.gke.io がインスタンス化されるまで待つ必要があります。次のコマンドで確認します。

kubectl get mutatingwebhookconfiguration gvisor.config.common-webhooks.networking.gke.io

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

NAME                                              CREATED AT
gvisor.config.common-webhooks.networking.gke.io   2020-04-06T17:07:17Z

既存クラスタの場合

既存のクラスタで GKE Sandbox を有効にするには、新しいノードプールを追加し、そのノードプールのこの機能を有効にします。

Console

GKE Sandbox を有効にして新しいノードプールを作成するには:

  1. Cloud Console で Google Kubernetes Engine のページに移動します。

    Google Kubernetes Engine に移動

  2. 変更するクラスタの名前をクリックします。

  3. [ ノードプールを追加] をクリックします。

  4. 必要に応じて [ノードプールの詳細] ページを構成します。

  5. ナビゲーション パネルで [ノード] をクリックし、次の設定を構成します。

    1. [イメージの種類] プルダウン リストから、[containerd(cos_containerd)が含まれている Container-Optimized OS] を選択します。 これは、GKE Sandbox でサポートされている唯一のイメージタイプです。
    2. [マシンの構成] で [シリーズ] と [マシンタイプ] を選択します。

  6. ナビゲーション パネルで [セキュリティ] をクリックし、[gVisor でサンドボックスを有効にする] チェックボックスをオンにします。

  7. [作成] をクリックします。

gcloud

GKE Sandbox を有効にする新しいノードプールを作成するには、次のようなコマンドを使用します。

gcloud container node-pools create NODE_POOL_NAME \
  --cluster=CLUSTER_NAME \
  --machine-type=MACHINE_TYPE \
  --image-type=cos_containerd \
  --sandbox type=gvisor

GKE Sandbox では、ノードイメージ タイプを cos_containerd にする必要があります。

1.18.4-gke.1300 より前は、ノード作成中に gvisor RuntimeClass がインスタンス化されます。ノードにワークロードをスケジュールする前に、次のコマンドを使用して gvisor RuntimeClass が存在するかどうかを確認します。

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

1.17.9-gke.1500 以前のバージョン、または 1.18.6-gke.600 以前の 1.18 バージョンを実行している場合は、gvisor.config.common-webhooks.networking.gke.io がインスタンス化されるまで待つ必要があります。次のコマンドで確認します。

kubectl get mutatingwebhookconfiguration gvisor.config.common-webhooks.networking.gke.io
NAME                                              CREATED AT
gvisor.config.common-webhooks.networking.gke.io   2020-04-06T17:07:17Z

省略可: GKE 用 Cloud Operations の有効化

クラスタで GKE 用 Cloud Operations を有効にして、gVisor メッセージをログに記録します。この操作は省略可能ですが、行うことをおすすめします。新しいクラスタでは、GKE 用 Cloud Operations がデフォルトで有効になります。

Google Cloud Console を使用して、既存のクラスタでこれらの機能を有効にできます。

  1. Cloud Console で Google Kubernetes Engine のページに移動します。

    Google Kubernetes Engine に移動

  2. 変更するクラスタの名前をクリックします。

  3. [機能] の [GKE 用 Cloud Opeartions] フィールドで、[ GKE 用 Cloud Opeartions を編集] をクリックします。

  4. [GKE 用 Cloud Operations を有効にする] チェックボックスをオンにします。

  5. プルダウン リストから、[システムとワークロードのロギングとモニタリング] を選択します。

  6. [変更を保存] をクリックします。

GKE Sandbox の使用

サンドボックスでのアプリケーションの実行

GKE Sandbox を有効にしたノードで Deployment を強制的に実行するには、Deployment のマニフェストに従って spec.template.spec.runtimeClassNamegvisor に設定します。

# httpd.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      runtimeClassName: gvisor
      containers:
      - name: httpd
        image: httpd

Deployment を作成するには、kubectl create コマンドを実行します。

kubectl create -f httpd.yaml

GKE Sandbox が有効になっているノードプールのノードに Pod がデプロイされます。デプロイを確認するには、次のコマンドを使用して Pod がデプロイされているノードを見つけます。

kubectl get pods

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

NAME                    READY   STATUS    RESTARTS   AGE
httpd-db5899bc9-dk7lk   1/1     Running   0          24s

出力から Pod の名前を探します。次のコマンドを実行して RuntimeClass の値を確認します。

kubectl get pods POD_NAME -o jsonpath='{.spec.runtimeClassName}'

次のように出力されます。

gvisor

または、各 Pod の RuntimeClass をリスト表示して、gvisor に設定されているものを探します。

kubectl get pods -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.runtimeClassName}\n{end}'

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

POD_NAME: gvisor

Pod がサンドボックス内で実行されていることを確認する方法は、サンドボックス内のデータに依存しないため、信頼性が高い方法となります。サンドボックス内から報告されたものは信頼できません。問題が存在していることや、悪意のある可能性があります。

通常のポッドとサンドボックス ポッドの実行

ノードプールで GKE Sandbox を有効にした後、Node Taints と許容値でサンドボックスを使用するのではなく、信頼できるアプリケーションをノードで実行できます。 これらの Pod は、「通常の Pod」といわれ、サンドボックスの Pod と区別されます。

通常の Pod は、サンドボックス化された Pod と同様に、他の Google Cloud サービスやクラスタ メタデータにアクセスできません。この設定は、ノードの構成時に有効になります。通常のポッドやサンドボックス化されたポッドが Google Cloud サービスにアクセスする必要がある場合は、ワークロード ID を使用します。

GKE Sandbox は、サンドボックス化されたポッドを実行できるノードに次のラベルと taint を追加します。

labels:
  sandbox.gke.io/runtime: gvisor
taints:
- effect: NoSchedule
  key: sandbox.gke.io/runtime
  value: gvisor

GKE Sandbox は、ポッド マニフェストのノード アフィニティと許容値に加えて、次のノード アフィニティと許容値をすべてのポッドに適用し、RuntimeClassgvisor に設定します。

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: sandbox.gke.io/runtime
          operator: In
          values:
          - gvisor
tolerations:
  - effect: NoSchedule
    key: sandbox.gke.io/runtime
    operator: Equal
    value: gvisor

GKE Sandbox が有効なノードで通常ポッドのスケジュールを設定するには、上記のノード アフィニティと許容値を Pod マニフェストに手動で適用します。

  • GKE Sandbox が有効なノードでポッドが実行可能な場合は、許容値を追加します。
  • GKE Sandbox が有効なノードで Pod を実行する必要がある場合は、上記のノード アフィニティと許容値の両方を追加します。

たとえば、次のマニフェストでは、サンドボックスでのアプリケーションの実行で使用したマニフェストを変更します。runtimeClass を削除して上記の taint と許容値の両方を追加し、サンドボックス化した Pod を含むノードで通常の Pod として実行します。

# httpd-no-sandbox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-no-sandbox
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: sandbox.gke.io/runtime
                operator: In
                values:
                - gvisor
      tolerations:
        - effect: NoSchedule
          key: sandbox.gke.io/runtime
          operator: Equal
          value: gvisor

まず、サンドボックスで Deployment が実行されていないことを確認します。

kubectl get pods -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.runtimeClassName}\n{end}'

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

httpd-db5899bc9-dk7lk: gvisor
httpd-no-sandbox-5bf87996c6-cfmmd:

前に作成した httpd Deployment はサンドボックス内で実行されます。これは、runtimeClass が gvisor のためです。httpd-no-sandbox Deployment の runtimeClass には値が設定されていないため、サンドボックスでは実行されません。

次に、GKE Sandbox を使用するノードで、サンドボックス化されていない Deployment が実行されていることを確認します。

kubectl get pod -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.nodeName}\n{end}'

ノードプールの名前は、nodeName の値に埋め込まれます。GKE Sandbox が有効なノードプール内のノードで、ポッドが実行されていることを確認します。

メタデータの保護の確認

サンドボックス ポッドを実行できるノードからメタデータが保護されていることを確認するには、次のようにテストを行います。

  1. kubectl apply -f を使用して、次のマニフェストから、サンドボックス化された Deployment を作成します。curlコマンドを含む fedora イメージを使用します。ポッドは /bin/sleep コマンドを実行して、Deployment が 10,000 秒間実行されるようにします。

    # sandbox-metadata-test.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: fedora
      labels:
        app: fedora
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: fedora
      template:
        metadata:
          labels:
            app: fedora
        spec:
          runtimeClassName: gvisor
          containers:
          - name: fedora
            image: fedora
            command: ["/bin/sleep","10000"]
    
  2. kubectl get pods を使用してポッドの名前を取得し、kubectl exec を使用して、ポッドにインタラクティブに接続します。

    kubectl exec -it POD_NAME /bin/sh
    

    /bin/sh セッションで、ポッド内のコンテナに接続されます。

  3. インタラクティブ セッションで、クラスタ メタデータを返す URL にアクセスします。

    curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env" -H "Metadata-Flavor: Google"
    

    パケットがサイレントで破棄されるため、コマンドが停止し、最終的にタイムアウトになります。

  4. Ctrl + C を押して curl コマンドを終了し、exit を入力してポッドから切断します。

  5. YAML マニフェストから RuntimeClass を削除し、kubectl apply -f FILENAME を使用してポッドを再デプロイします。サンドボックス化されたポッドが終了し、GKE Sandbox のないノードで再作成されます。

  6. 新しいポッド名を取得し、kubectl exec を使用してポッドに接続します。curl コマンドを再度実行します。今回は、結果が返されます。この出力例は省略されています。

    ALLOCATE_NODE_CIDRS: "true"
    API_SERVER_TEST_LOG_LEVEL: --v=3
    AUTOSCALER_ENV_VARS: kube_reserved=cpu=60m,memory=960Mi,ephemeral-storage=41Gi;...
    ...
    

    exit と入力して、ポッドから切断します。

  7. デプロイを削除します。

    kubectl delete deployment fedora
    

GKE Sandbox を無効にする

現在、ノードプールを更新して GKE Sandbox を無効にすることはできません。 既存のノードプールで GKE Sandbox を無効にするには、以下のいずれかを行います。

次のステップ