透過 GKE Sandbox 執行工作負載

本頁面說明當 Pod 中的容器要執行未知或不受信任的程式碼,或需要與節點額外隔離時,如何使用 GKE Sandbox 保護節點上的主機核心。

啟用 GKE Sandbox

您可以在新叢集或現有叢集上啟用 GKE Sandbox。

事前準備

如要準備這項工作,請執行下列步驟:

  • 確認您已啟用 Google Kubernetes Engine API。
  • 啟用 Google Kubernetes Engine API
  • 確認您已安裝 Cloud SDK
  • 設定預設的專案 ID
    gcloud config set project [PROJECT_ID]
  • 如果您使用區域叢集,請設定預設的運算區域
    gcloud config set compute/zone [COMPUTE_ZONE]
  • 如果您使用地區叢集,請設定預設的運算地區
    gcloud config set compute/region [COMPUTE_REGION]
  • gcloud 更新到最新版本:
    gcloud components update
  • 對於叢集主要執行個體和節點,GKE Sandbox 需要使用 GKE v1.12.7-gke.17 以上版本或 v1.13.5-gke.15 以上版本。
  • 請確認 gcloud 指令為 243.0.0 以上版本。

於新叢集

如要啟用 GKE Sandbox,請設定節點集區。預設節點集區 (叢集建立時即建立的第一個節點) 無法使用 GKE Sandbox。如要在叢集建立時啟用 GKE Sandbox,請務必在建立叢集時新增第二個節點集區。

主控台

如要查看叢集,請前往 Cloud Console 中的「Google Kubernetes Engine」選單。

  1. 前往 Cloud Console 的「Google Kubernetes Engine」選單。

    前往 Google Kubernetes Engine 選單

  2. 按一下 [Create cluster] (建立叢集)。

  3. 選擇 [Standard cluster] (標準叢集) 範本,或者根據工作負載選擇適用的範本

  4. 選用 (建議使用):啟用 Stackdriver Logging 和 Stackdriver Monitoring,以記錄 gVisor 訊息。

  5. 按一下 [Add node pool] (新增節點集區)

  6. 根據您的需求設定節點集區。按一下節點集區的 [More node pool options] (更多節點集區選項)。調整以下設定:

    • 選取 v1.12.6-gke.8 以上的節點版本。
    • 對於節點映像檔,請選取 [Container-Optimized OS with Containerd (cos_containerd) (beta)] (含有 Containerd (cos_containerd) 的 Container-Optimized OS (Beta 版))
    • 啟用「Enable sandbox with gVisor (beta)」(啟用 gVisor 沙箱 (Beta 版))
    • 如果節點集區中的節點使用一個以上的 vCPU,按一下 [Add Label] (新增標籤)。將索引鍵設為 cloud.google.com/gke-smt-disabled 並將值設為 true。然後,依照操作說明停用安全性公告中的超執行緒。

    請按需求來設定其他節點集區的設定。

  7. 儲存節點集區設定並繼續設定叢集。

gcloud

GKE Sandbox 無法在預設節點集區上啟用,也無法在使用 gcloud 指令建立新叢集時建立其他節點。請改為按照平常方式建立叢集。您可以加入旗標 --enable-stackdriver-kubernetes,以此方式啟用 Stackdriver Logging 和 Stackdriver Monitoring,此為選擇性,但建議採用。系統會記錄 gVisor 訊息。

接下來使用 gcloud beta container node-pools create 指令,將 --sandbox 旗標設為 type=gvisor。將方括號內的值取代為您自己的值,並務必指定 v1.12.6-gke.8 以上的節點版本。

gcloud beta container node-pools create [NODE_POOL_NAME] \
  --cluster=[CLUSTER_NAME] \
  --node-version=[NODE_VERSION] \
  --image-type=cos_containerd \
  --sandbox type=gvisor \
  --enable-autoupgrade

gvisor RuntimeClass 會在節點建立期間進行執行個體化 (在任何工作負載排程到節點上之前)。您可以使用下列指令檢查 gvisor RuntimeClass 是否存在:

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

於現有叢集

您可以在現有叢集上啟用 GKE Sandbox,方法是新增節點集區並啟用該節點集區的功能,或是修改現有的非預設節點集區。

主控台

  1. 前往 Cloud Console 的「Google Kubernetes Engine」選單。

    前往「Google Kubernetes Engine」選單

  2. 按一下鉛筆形狀的叢集 [Edit] (編輯) 按鈕。

  3. 如有需要,可按一下 [Add node pool] (新增節點集區) 來新增其他節點集區。如要編輯現有節點集區,請按一下節點集區的「Edit」(編輯) 按鈕。請勿在預設節點集區上啟用「Sandbox with gVisor (beta)」(gVisor Sandbox (Beta 版))

  4. 啟用「Sandbox with gVisor (beta)」(gVisor Sandbox (Beta 版)),然後點擊 [Done] (完成)。

  5. 如有需要,請對叢集進行其他設定變更,然後按一下 [Save] (儲存)

gcloud

如要建立啟用 GKE Sandbox 的「新」節點集區,請使用類似如下的指令:

gcloud beta container node-pools create [NODE_POOL_NAME] \
  --cluster=[CLUSTER_NAME] \
  --image-type=cos_containerd \
  --sandbox type=gvisor \
  --enable-autoupgrade

如要在「現有」節點集區上啟用 GKE Sandbox,請使用類似如下的指令。請勿在預設節點集區中啟用 --sandbox type=gvisor

 gcloud beta container node-pools update [NODE_POOL_NAME] \
  --sandbox type=gvisor

gvisor RuntimeClass 會在節點建立期間進行執行個體化 (在任何工作負載排程到節點上之前)。您可以使用下列指令檢查 gvisor RuntimeClass 是否存在:

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

選用:啟用 Stackdriver Logging 和 Stackdriver Monitoring

請在叢集上啟用 Stackdriver Logging 和 Stackdriver Monitoring 以記錄 gVisor 訊息,此為選擇性,但建議採用。必須使用 Google Cloud Console,才能在現有叢集上啟用這些功能。

  1. 前往 Cloud Console 的「Google Kubernetes Engine」選單。

    前往「Google Kubernetes Engine」選單

  2. 按一下鉛筆形狀的叢集 [Edit] (編輯) 按鈕。

  3. 啟用 Stackdriver Logging 和 Stackdriver Monitoring。

  4. 如有需要,請對叢集進行其他設定變更,然後按一下 [Save] (儲存)

使用 GKE Sandbox

在沙箱中執行應用程式

如要在已啟用 GKE Sandbox 的節點上強制執行 Deployment,請將其 spec.template.spec.runtimeClassName 設為 gvisor,如 Deployment 資訊清單所示:

# 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 部署至節點。如要驗證這一點,請使用 kubectl get pods 指令找出部署 Pod 的節點:

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

在輸出中找出 Pod 的名稱,然後執行下列指令,以查看其在 RuntimeClass 中的值:

kubectl get pods [NAME-OF-POD] -o jsonpath='{.spec.runtimeClassName}'
gvisor

也可以列出每個 Pod 的 RuntimeClass,並尋找將其設為 gvisor 的項目:

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

這種驗證 Pod 是否在沙箱中執行的方法值得信賴,因為此方法不需依賴沙箱中的任何資料。任何來自沙箱的回報資訊都可能有所缺陷或帶有惡意,因此不可信任。

執行一般 Pod 以及採用沙箱機制的 Pod

在節點集區上啟用 GKE Sandbox 後,可以使用節點 taint 和容許條件,以此方式在不使用沙箱的情況下,在這類節點上執行可信任的應用程式。這些 Pod 稱為「一般 Pod」,以便和採用沙箱機制沙箱的 Pod 做出區別。

一般 Pod 與採用沙箱機制的 Pod 相同,無法存取其他 Google Cloud 服務或叢集中繼資料。這項防護措施是節點設定的一部分。如果一般 Pod 或採用沙箱機制的 Pod 需要存取 Google Cloud 服務,請使用 Workload Identity

GKE Sandbox 會將以下標籤和 taint 新增至可執行沙箱 Pod 的節點:

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

除了 Pod 資訊清單中的任何節點的相依性和容許條件設定之外,GKE Sandbox 也會將下列節點相依性和容許條件套用至所有將 RuntimeClass 設為gvisor 的 Pod:

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,請在 Pod 資訊清單中,手動套用上述節點相依性和容許條件。

  • 如果您的 pod 可以在啟用 GKE Sandbox 的節點上執行,請加入容許條件。
  • 如果您的 pod 必須在啟用 GKE Sandbox 的節點上執行,請同時新增節點相依性和容許條件。

例如,下列資訊清單會修改在沙箱中執行應用程式一節中使用的資訊清單,移除 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:

由於 runtimeClass 為 gvisor,沙箱中正在執行之前建立的 httpd Deployment。httpd-no-sandbox Deployment 沒有 runtimeClass 的值,因此不會在沙箱中執行。

接下來,請執行以下指令,確認非採用沙箱機制的 Deployment 是否正在使用 GKE Sandbox 的節點上執行:

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

節點集區的名稱會嵌入 nodeName 值中。確認在節點上執行的 Pod 是否在已啟用 GKE Sandbox 的節點集區內。

驗證中繼資料保護措施

如要驗證中繼資料是否確定受到保護,不會受到可執行沙箱 Pod 的節點的影響,可執行以下測試:

  1. 使用 kubectl apply -f,從下列資訊清單建立使用沙箱機制的 Deployment。其會使用包含 curl 指令的 fedora 映像檔。Pod 會執行 /bin/sleep 指令,以確保 Deployment 執行 10000 秒。

    # 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 取得 Pod 名稱,然後使用 kubectl exec 以互動方式連線至 Pod。

    kubectl exec -it [POD-NAME] /bin/sh
    

    系統會在 /bin/sh 工作階段將您連線到在 Pod 中執行的容器。

  3. 請在互動式工作階段中,嘗試存取會傳回叢集中繼資料的網址:

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

    由於系統會以無訊息的方式捨棄封包,使得指令停滯,最後逾時。

  4. 按下 Ctrl + C 鍵可終止 curl指令,再輸入 exit 即可中斷 Pod 的連線。

  5. 從 YAML 資訊清單中刪除 RuntimeClass 列,並使用 kubectl apply -f [FILENAME] 重新部署 Pod。使用沙箱機制的 Pod 會終止,並在沒有 GKE Sandbox 的節點上重新建立。

  6. 取得新的 Pod 名稱,並用 kubectl exec 與 Pod 連線,然後再次執行 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 即可與 Pod 中斷連線。

  7. 移除部署:

    kubectl delete deployment fedora
    

停用 GKE Sandbox

目前無法更新節點集區的方式來停用 GKE Sandbox。如要在現有節點集區中停用 GKE Sandbox,請執行下列其中一項操作:

  • 刪除先前使用沙箱的 Pod。否則,當您停用 GKE Sandbox 後,如果沒有已啟用 GKE Sandbox 的可用節點,這些 Pod 會以一般 Pod 的形式執行。然後刪除節點集區,該集區已啟用 GKE Sandbox
  • 將節點集區調整為零節點或是
  • 重新建立 Pod,不指定 RuntimeClassName 的值。

後續步驟