全部產品
Search
文件中心

Container Service for Kubernetes:開啟雲端硬碟並行掛載提升Pod啟動速度

更新時間:Apr 25, 2025

在高密度部署有狀態業務(例如資料庫)或大量短生命週期的容器(例如持續整合、批處理)等情境中,每個Pod都需要大量的雲端硬碟來儲存資料,Pod的數量非常多,如果這些Pod同時被調度到同一個節點上,預設使用的串列掛載會導致Pod啟動時間顯著增加,您可以開啟雲端硬碟並行掛載功能來解決此類問題。

前提條件

注意事項

  • 僅支援擁有磁碟序號的雲端硬碟開啟並行掛載功能。關於如何查看磁碟序號,請詳見查看Block Storage序號

    2020年06月10日之前建立的雲端硬碟沒有可識別的序號資訊,無法開啟,因為該類雲端硬碟會導致無法正常掛載。

  • 多個雲端硬碟從同一個節點上卸載時仍然是串列的。

  • 開啟並行掛載後,ECS DescribeDisks等OpenAPI返回的Device欄位,以及控制台上顯示的掛載點可能會不可靠,請避免在業務中使用該掛載路徑,您可以通過雲端硬碟的序號確認實際路徑。

開啟步驟

您可以通過自動化指令碼或手工配置開啟雲端硬碟並行掛載功能。

自動化指令碼配置

  1. 將以下指令碼儲存為enable_parallel_attach.sh檔案。

    展開查看配置指令碼

    #!/bin/bash
    
    set -e
    set -o pipefail
    
    readonly REQUIRED_VERSION="v1.30.4"
    CLUSTER_ID=$1
    
    if [ -z "$CLUSTER_ID" ]; then
        echo "Usage: enable_parallel_attach.sh <cluster-id>"
        exit 1
    fi
    
    check_version() {
        local ADDONS VERSION
        ADDONS=$(aliyun cs GET "/clusters/${CLUSTER_ID}/addon_instances")
    
        VERSION=$(echo "$ADDONS" | jq -r '.addons[] | select(.name=="csi-plugin") | .version')
        if ! printf "%s\n" "$REQUIRED_VERSION" "$VERSION" | sort -V -C; then
            echo "csi-plugin version $VERSION is not supported, please upgrade to $REQUIRED_VERSION or later"
            exit 1
        fi
    
        PROVISIONER=managed-csiprovisioner
        VERSION=$(echo "$ADDONS" | jq -r '.addons[] | select(.name=="managed-csiprovisioner") | .version')
        if [ -z "$VERSION" ]; then
            PROVISIONER=csi-provisioner
            VERSION=$(echo "$ADDONS" | jq -r '.addons[] | select(.name=="csi-provisioner") | .version')
        fi
        if ! printf "%s\n" "$REQUIRED_VERSION" "$VERSION" | sort -V -C; then
            echo "$PROVISIONER version $VERSION is not supported, please upgrade to $REQUIRED_VERSION or later"
            exit 1
        fi
    }
    
    update_node_pool() {
        local NODE_POOL_DOC
        NODE_POOL_DOC=$(aliyun cs GET "/clusters/${CLUSTER_ID}/nodepools/$1")
    
        if [ -n "$(echo "$NODE_POOL_DOC" | jq -r '(.scaling_group.tags // [])[] | select(.key=="supportConcurrencyAttach")')" ]; then
            echo "node pool already has supportConcurrencyAttach tag"
            return
        fi
    
        aliyun cs PUT "/clusters/${CLUSTER_ID}/nodepools/$1" --header "Content-Type=application/json" \
            --body "$(echo "$NODE_POOL_DOC" | jq -c '{
        "scaling_group": {
            "tags": ((.scaling_group.tags // []) + [{
                "key": "supportConcurrencyAttach",
                "value": "true"
            }])
        }
    }')"
    }
    
    # 存量節點配置
    update_nodes() {
        local PAGE=1
        local IDX TOTAL NODES_DOC ARGS
        while :; do
            echo "tagging nodes, page $PAGE"
            NODES_DOC=$(aliyun cs GET "/clusters/${CLUSTER_ID}/nodes" --pageSize 50 --pageNumber $PAGE)
            TOTAL=$(echo "$NODES_DOC" | jq -r '.page.total_count')
    
            ARGS=()
            IDX=0
            for node in $(echo "$NODES_DOC" | jq -r '.nodes[] | select(.is_aliyun_node) | .instance_id'); do
                IDX=$((IDX+1))
                ARGS+=("--ResourceId.$IDX" "$node")
            done
            if [ "$IDX" != "0" ]; then
                aliyun ecs TagResources --region "$ALIBABA_CLOUD_REGION_ID" --ResourceType Instance "${ARGS[@]}" \
                    --Tag.1.Key supportConcurrencyAttach --Tag.1.Value true
                echo "finished nodes $(( (PAGE-1)*50+IDX ))/$TOTAL"
            fi
    
            if [[ $(( PAGE*50 )) -ge $TOTAL ]]; then
                break
            fi
            PAGE=$((PAGE+1))
        done
    }
    
    update_addon() {
        local ADDON=$1
        shift
        local CONFIG STATE
        CONFIG=$(aliyun cs GET "/clusters/${CLUSTER_ID}/addon_instances/${ADDON}" | \
            jq -c '.config | fromjson | (.FeatureGate // "" | split(",")) as $fg | .FeatureGate = ($fg + $ARGS.positional | unique | join(",")) | {config: . | tojson}' --args "$@")
    
        aliyun cs POST "/clusters/${CLUSTER_ID}/components/${ADDON}/config" --header "Content-Type=application/json" --body "$CONFIG"
    
        echo "Waiting for $ADDON config to complete"
        while true; do
            STATE=$(aliyun --secure cs GET "/clusters/${CLUSTER_ID}/addon_instances/${ADDON}" | jq -r '.state')
            echo "state: $STATE"
            if [ "$STATE" != "updating" ]; then
                break
            fi
            sleep 5
        done
        if [ "$STATE" != "active" ]; then
            echo "Failed to update $ADDON config"
            return 1
        fi
    }
    
    check_version
    
    aliyun cs GET "/clusters/${CLUSTER_ID}/nodepools" | jq -r '.nodepools[]|.nodepool_info|"\(.nodepool_id)\t\(.name)"' | \
    while read -r NODE_POOL_ID NODE_POOL_NAME; do
        echo "Updating tags for node pool $NODE_POOL_NAME ($NODE_POOL_ID)"
        update_node_pool "$NODE_POOL_ID"
    done
    
    ALIBABA_CLOUD_REGION_ID=$(aliyun cs GET "/clusters/${CLUSTER_ID}" | jq -r .region_id)
    
    update_nodes
    
    update_addon $PROVISIONER DiskADController=true DiskParallelAttach=true
    update_addon csi-plugin DiskADController=true
    
    echo "All done! Now the disks can be attached concurrently to the same node."
  2. 執行指令碼並行掛載雲端硬碟。

    bash enable_parallel_attach.sh <叢集ID>

手工配置

  1. 新增叢集節點池配置中的ECS 標籤,使其鍵為supportConcurrencyAttach,值為true,確保新建立的ECS執行個體有該標籤。

    1. 登入Container Service管理主控台,在左側導覽列選擇叢集列表

    2. 叢集列表頁面,單擊目的地組群名稱,然後在左側導覽列,選擇節點管理 > 節點池

    3. 在節點池列表頁面,單擊目標節點池所在行操作列的編輯

    4. 在編輯節點池頁面最下方的進階選項地區,增加ECS 標籤,其鍵為supportConcurrencyAttach,值為true

  2. 為叢集中所有存量節點的ECS執行個體添加標籤,使其鍵為supportConcurrencyAttach,值為true。具體操作,請參見建立並綁定自訂標籤

  3. 在左側導覽列選擇營運管理 > 組件管理,單擊儲存頁簽,定位csi-provisioner組件,單擊組件右下方的配置,將FeatureGate設定為DiskADController=true,DiskParallelAttach=true

    說明

    DiskADController=true設定後,雲端硬碟相關attachdetach的操作交由csi-provisioner;DiskParallelAttach=true設定後,將開啟雲端硬碟並行掛載功能。

  4. 待csi-provisioner配置完成後,將csi-plugin組件的FeatureGate設定為DiskADController=true

驗證雲端硬碟並行掛載效能

本樣本將在同一節點上建立大量掛載雲端硬碟的Pod,以驗證開啟並行掛載對Pod啟動速度的提升。

重要

本文中提供的測試資料僅為理論值(參考值),實際資料以您的作業環境為準。

  1. 在ACK叢集中,添加一個支援多個雲端硬碟掛載的節點。例如,ecs.g7se.16xlarge類型的執行個體最多可掛載56塊雲端硬碟。

  2. 使用以下內容,建立測試應用attach-stress.yaml檔案,並將<YOUR-HOSTNAME>替換為實際的節點名稱。

    展開查看attach-stress.yaml檔案

    ---
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: alibabacloud-disk
    provisioner: diskplugin.csi.alibabacloud.com
    parameters:
      type: cloud_auto
    volumeBindingMode: WaitForFirstConsumer
    reclaimPolicy: Delete
    allowVolumeExpansion: true
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: attach-stress
    spec:
      selector:
        matchLabels:
          app: attach-stress
      serviceName: attach-stress
      replicas: 1
      podManagementPolicy: Parallel
      persistentVolumeClaimRetentionPolicy:
        whenScaled: Retain
        whenDeleted: Delete
      template:
        metadata:
          labels:
            app: attach-stress
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: kubernetes.io/hostname
                    operator: In
                    values:
                    - <YOUR-HOSTNAME>  # 替換為實際節點的名稱。
          hostNetwork: true
          containers:
          - name: attach-stress
            image: registry-cn-hangzhou.ack.aliyuncs.com/acs/busybox
            command: ["/bin/sh", "-c", "trap exit TERM; while true; do date > /mnt/0/data; sleep 1; done"]
            volumeMounts:
            - name: volume-0
              mountPath: /mnt/0
            - name: volume-1
              mountPath: /mnt/1
      volumeClaimTemplates:
      - metadata:
          name: volume-0
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: alibabacloud-disk
          resources:
            requests:
              storage: 1Gi
      - metadata:
          name: volume-1
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: alibabacloud-disk
          resources:
            requests:
              storage: 1Gi
  3. 執行以下命令,確認應用正常啟動,然後將副本數量縮容到0,以準備後續的批量掛載測試。

    kubectl apply -f attach-stress.yaml
    kubectl rollout status sts attach-stress
    kubectl scale sts attach-stress --replicas 0

    預期輸出:

    storageclass.storage.k8s.io/alibabacloud-disk created
    statefulset.apps/attach-stress created
    partitioned roll out complete: 1 new pods have been updated...
    statefulset.apps/attach-stress scaled
  4. 執行以下命令,開始批量掛載測試,並統計Pod啟動所需的時間。

    說明

    此時該叢集尚未開啟並行掛載功能,請根據您的節點最大支援的雲端硬碟數量調整測試的副本數。

    date && \
      kubectl scale sts attach-stress --replicas 28 && \
      kubectl rollout status sts attach-stress && \
      date

    預期輸出:

    2024年10月15日 星期二 19時21分36秒 CST
    statefulset.apps/attach-stress scaled
    Waiting for 28 pods to be ready...
    Waiting for 27 pods to be ready...
    <省略……>
    Waiting for 3 pods to be ready...
    Waiting for 2 pods to be ready...
    Waiting for 1 pods to be ready...
    partitioned roll out complete: 28 new pods have been updated...
    2024年10月15日 星期二 19時24分55秒 CST

    輸出表明,在未開啟並行掛載時,28個Pod全部啟動耗時超過3分鐘。

  5. 參考上文開啟步驟開啟該叢集的並行掛載功能。

  6. 執行以下命令,清理之前建立的Pod,準備下一輪測試。

    說明

    清理時,觀察叢集中相關的volumeattachments資源,等待它們被刪除後即可完成雲端硬碟的卸載,過程大約需要幾分鐘。

    kubectl scale sts attach-stress --replicas 0
  7. 再次執行以下測試命令,統計開啟並行掛載後Pod啟動所需的時間,預期僅需要約40秒,較未開啟時的3分鐘,有明顯提升。

    date && \
      kubectl scale sts attach-stress --replicas 28 && \
      kubectl rollout status sts attach-stress && \
      date

    預期輸出:

    2024年10月15日 星期二 20時02分54秒 CST
    statefulset.apps/attach-stress scaled
    Waiting for 28 pods to be ready...
    Waiting for 27 pods to be ready...
    <省略……>
    Waiting for 3 pods to be ready...
    Waiting for 2 pods to be ready...
    Waiting for 1 pods to be ready...
    partitioned roll out complete: 28 new pods have been updated...
    2024年10月15日 星期二 20時03分31秒 CST
  1. 執行以下命令,清理叢集中的測試應用。

    kubectl delete -f attach-stress.yaml