すべてのプロダクト
Search
ドキュメントセンター

Container Service for Kubernetes:カスタムイメージによるエラスティックノードプールの構築

最終更新日:Mar 27, 2026

ノードソフトウェアパッケージをあらかじめカスタムイメージにプリインストールすることで、新規 ECS インスタンスが手動設定なしで登録済みクラスターのノードプールに参加可能となり、ノードが Ready 状態に到達するまでの時間を短縮できます。

本ガイドでは、バイナリファイル経由で接続された Kubernetes 1.28.3 を搭載した CentOS 7.9 を使用します。すでにカスタムイメージをお持ちの場合は、「ステップ 3:カスタムイメージをノードプールに適用」へスキップしてください。

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

制限事項

  • ステップ 1 で作成されるノードは、初期状態で 失敗 となります。これは正常な挙動です。ノードにはステップ 2 で手動設定を行うまでソフトウェアパッケージがインストールされていません。

  • ステップ 2 の手動設定には、ノードへの SSH アクセスが必要です。

  • デフォルトイメージからカスタムイメージへ切り替える場合、ノードがクラスターに参加できるようになる前に、イメージ内の残存 kubelet 証明書を削除する必要があります。この処理はステップ 4 で実行されます。

概要

image

本ワークフローは以下の 5 つのステップで構成されます。

  1. クラウドノードプールの作成 — 構成対象の生の ECS インスタンスを 1 台追加します。

  2. ノードの構成およびカスタムイメージのエクスポート — containerd、kubelet、kube-proxy をインストールし、カスタム ECS イメージを作成します。

  3. カスタムイメージをノードプールに適用 — ノードプールを更新してカスタムイメージを使用するように設定します。

  4. 初期化スクリプトの更新 — Alibaba Cloud ノードパラメーターを動的に注入するようスクリプトを再書き込みします。

  5. ノードプールのスケーリング — ノードを追加し、エラスティックノードプールが正しく動作することを検証します。

ステップ 1:クラウドノードプールの作成およびノードの追加

このステップでは、登録済みクラスターに未初期化のノードを 1 台追加します。このノードは、ステップ 2 で構築するカスタムイメージのテンプレートとして使用されます。

  1. OSS バケット内に、以下の内容で join-ecs-node.sh というファイルを作成し、アップロードします。このスクリプトは、起動時に注入される Alibaba Cloud ノードパラメーターをログ出力し、イメージ構築前の値の検証を可能にします。

    echo "The node providerid is $ALIBABA_CLOUD_PROVIDER_ID"
    echo "The node name is $ALIBABA_CLOUD_NODE_NAME"
    echo "The node labels are $ALIBABA_CLOUD_LABELS"
    echo "The node taints are $ALIBABA_CLOUD_TAINTS"
  2. join-ecs-node.sh の URL(署名付き URL でも可)を取得し、クラスター内のカスタム構成スクリプトのリファレンスを更新します。

    1. ack-agent-config ConfigMap を編集します。

      kubectl edit cm ack-agent-config -n kube-system

    2. addNodeScriptPath をスクリプトの URL に設定します。

      apiVersion: v1
      data:
        addNodeScriptPath: https://kubelet-****.oss-cn-hangzhou-internal.aliyuncs.com/join-ecs-nodes.sh
      kind: ConfigMap
      metadata:
        name: ack-agent-config
        namespace: kube-system

  3. 予期されるノード数1 に設定して、cloud-test という名前のクラウドノードプールを作成します。詳細については、「ノードプールの作成およびスケーリング」をご参照ください。

    重要

    新しいノードは 失敗 状態で表示されます。これは正常な挙動です。ノードにはまだ必要なソフトウェアパッケージが初期化されていないためです。続行する前に、SSH でノードにログインできることを確認してください。

    image

ステップ 2:ノードの構成およびカスタムイメージのエクスポート

失敗状態のノードにログインし、すべての必須コンポーネントをインストールします。これらのコンポーネントはカスタムイメージに組み込まれるため、今後のノードは事前構成済みの状態で起動します。

2.1 ノードパラメーターの検証

  1. ノードに SSH 経由でログインし、初期化ログを確認します。

    cat /var/log/acs/init.log

    期待される出力:

    ノードのプロバイダー ID は cn-zhangjiakou.i-xxxxx
    ノード名は cn-zhangjiakou.192.168.66.xx
    ノードラベルは alibabacloud.com/nodepool-id=npf9fbxxxxxx,ack.aliyun.com=c22b1a2e122ff4fde85117de4xxxxxx,alibabacloud.com/instance-id=i-8vb7m7nt3dxxxxxxx,alibabacloud.com/external=true
    ノードの Taint は

    ALIBABA_CLOUD_PROVIDER_IDALIBABA_CLOUD_NODE_NAMEALIBABA_CLOUD_LABELS、およびALIBABA_CLOUD_TAINTS の値を記録します。これらは、ステップ 2.3 の kubelet の起動パラメーターで必要になります。

2.2 基盤環境の構成

以下のコマンドを実行して基盤環境を構成します。この操作では、以下の処理が実行されます。

  • 必須システムパッケージのインストール

  • ファイアウォール、SELinux、スワップ領域の無効化(Kubernetes で必須)

  • ネットワーク管理および時刻同期の構成

  • システムファイルディスクリプタ制限の設定

# ツールパッケージをインストールします。
yum update -y && yum -y install  wget psmisc vim net-tools nfs-utils telnet yum-utils device-mapper-persistent-data lvm2 git tar curl

# ファイアウォールを無効化します。
systemctl disable --now firewalld

# SELinux を無効化します。
setenforce 0
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config

# スワップ領域を無効化します。
sed -ri 's/.*swap.*/#&/' /etc/fstab
swapoff -a && sysctl -w vm.swappiness=0

# ネットワーク構成を行います。
systemctl disable --now NetworkManager
systemctl start network && systemctl enable network

# 時刻同期を行います。
ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
yum install ntpdate -y
ntpdate ntp.aliyun.com

# ulimit を構成します。
ulimit -SHn 65535
cat >> /etc/security/limits.conf <<EOF
* soft nofile 655360
* hard nofile 131072
* soft nproc 655350
* hard nproc 655350
* seft memlock unlimited
* hard memlock unlimitedd
EOF
環境構成の完了後、カーネルをバージョン 4.18 以降にアップグレードし、ipvsadm をインストールしてください。

2.3 containerd のインストール

このセクションのすべてのコマンドは、コンテナランタイムとして containerd を構成するものであり、カーネルモジュールおよび CNI ネットワーキングを含みます。

  1. CNI プラグインと containerd パッケージをダウンロードします:

    wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
    mkdir -p /etc/cni/net.d /opt/cni/bin
    # CNI バイナリパッケージを展開します
    tar xf cni-plugins-linux-amd64-v*.tgz -C /opt/cni/bin/
    wget https://github.com/containerd/containerd/releases/download/v1.7.8/containerd-1.7.8-linux-amd64.tar.gz
    tar -xzf cri-containerd-cni-*-linux-amd64.tar.gz -C /
  2. containerd systemd サービスファイルを作成します:

    cat > /etc/systemd/system/containerd.service <<EOF
    [Unit]
    Description=containerd コンテナランタイム
    Documentation=https://containerd.io
    After=network.target local-fs.target
    
    [Service]
    ExecStartPre=-/sbin/modprobe overlay
    ExecStart=/usr/local/bin/containerd
    Type=notify
    Delegate=yes
    KillMode=process
    Restart=always
    RestartSec=5
    LimitNPROC=infinity
    LimitCORE=infinity
    LimitNOFILE=infinity
    TasksMax=infinity
    OOMScoreAdjust=-999
    
    [Install]
    WantedBy=multi-user.target
    EOF
  3. containerd に必要なカーネルモジュールを設定します:

    cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
    overlay
    br_netfilter
    EOF
    systemctl restart systemd-modules-load.service
  4. containerd で必要なカーネルパラメータを設定します:

    cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
    net.bridge.bridge-nf-call-iptables  = 1
    net.ipv4.ip_forward                 = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    EOF
    
    # カーネルを読み込む
    sysctl --system
  5. containerd 構成ファイルを生成して変更します。

    mkdir -p /etc/containerd
    containerd config default | tee /etc/containerd/config.toml
    
    # containerd の構成ファイルを変更します
    sed -i "s#SystemdCgroup\ \=\ false#SystemdCgroup\ \=\ true#g" /etc/containerd/config.toml
    cat /etc/containerd/config.toml | grep SystemdCgroup
    sed -i "s#registry.k8s.io#m.daocloud.io/registry.k8s.io#g" /etc/containerd/config.toml
    cat /etc/containerd/config.toml | grep sandbox_image
    sed -i "s#config_path\ \=\ \"\"#config_path\ \=\ \"/etc/containerd/certs.d\"#g" /etc/containerd/config.toml
    cat /etc/containerd/config.toml | grep certs.d
    
    # アクセラレータを設定します
    mkdir /etc/containerd/certs.d/docker.io -pv
    cat > /etc/containerd/certs.d/docker.io/hosts.toml << EOF
    server = "https://docker.io"
    [host."https://hub-mirror.c.163.com"]
      capabilities = ["pull", "resolve"]
    EOF
  6. containerd を有効化して起動します:

    # サービスファイルを追加した後、systemdユニットファイルを再読み込みします
    systemctl daemon-reload
    
    systemctl enable --now containerd.service
    systemctl start containerd.service
    systemctl status containerd.service
  7. crictl の設定:

    wget https://mirrors.chenby.cn/https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.28.0/crictl-v1.28.0-linux-amd64.tar.gz
    tar xf crictl-v*-linux-amd64.tar.gz -C /usr/bin/
    # 設定ファイルを生成します
    cat > /etc/crictl.yaml <<EOF
    runtime-endpoint: unix:///run/containerd/containerd.sock
    image-endpoint: unix:///run/containerd/containerd.sock
    timeout: 10
    debug: false
    EOF
    
    # テスト
    systemctl restart  containerd
    crictl info

2.4 kubelet および kube-proxy のインストール

このセクションのすべてのコマンドは、マスターノードからコピーしたバイナリファイルを用いて kubelet および kube-proxy をインストールし、両コンポーネントを起動時に自動実行するよう構成するものです。

  1. マスターノード から kubelet と kube-proxy のバイナリをコピーします:

    scp /usr/local/bin/kube{let,-proxy} $NODEIP:/usr/local/bin/
  2. 証明書ディレクトリを作成し、マスターノードから証明書をコピーします:

    mkdir -p /etc/kubernetes/pki
    for FILE in pki/ca.pem pki/ca-key.pem pki/front-proxy-ca.pem bootstrap-kubelet.kubeconfig kube-proxy.kubeconfig;
      do scp /etc/kubernetes/$FILE $NODE:/etc/kubernetes/${FILE}; done
  3. kubelet サービスを設定します。ステップ 2.1 で記録したノードプールのパラメーターで、環境変数の値を置き換えます。

    mkdir -p /var/lib/kubelet /var/log/kubernetes /etc/systemd/system/kubelet.service.d /etc/kubernetes/manifests/
    
    # すべての Kubernetes ノードで kubelet サービスを設定します
    cat > /usr/lib/systemd/system/kubelet.service << EOF
    
    [Unit]
    Description=Kubernetes Kubelet
    Documentation=https://github.com/kubernetes/kubernetes
    After=network-online.target firewalld.service containerd.service
    Wants=network-online.target
    Requires=containerd.service
    
    [Service]
    ExecStart=/usr/local/bin/kubelet \\
        --node-ip=${ALIBABA_CLOUD_NODE_NAME} \\
        --hostname-override=${ALIBABA_CLOUD_NODE_NAME} \\
        --node-labels=${ALIBABA_CLOUD_LABELS} \\
        --provider-id=${ALIBABA_CLOUD_PROVIDER_ID} \\
        --register-with-taints=${ALIBABA_CLOUD_TAINTS} \\
        --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig  \\
        --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
        --config=/etc/kubernetes/kubelet-conf.yml \\
        --container-runtime-endpoint=unix:///run/containerd/containerd.sock
    
    [Install]
    WantedBy=multi-user.target
    EOF
  4. kubelet 構成ファイルを作成します:

    cat > /etc/kubernetes/kubelet-conf.yml <<EOF
    apiVersion: kubelet.config.k8s.io/v1beta1
    kind: KubeletConfiguration
    address: 0.0.0.0
    port: 10250
    readOnlyPort: 10255
    authentication:
      anonymous:
        enabled: false
      webhook:
        cacheTTL: 2m0s
        enabled: true
      x509:
        clientCAFile: /etc/kubernetes/pki/ca.pem
    authorization:
      mode: Webhook
      webhook:
        cacheAuthorizedTTL: 5m0s
        cacheUnauthorizedTTL: 30s
    cgroupDriver: systemd
    cgroupsPerQOS: true
    clusterDNS:
    - 10.96.0.10
    clusterDomain: cluster.local
    containerLogMaxFiles: 5
    containerLogMaxSize: 10Mi
    contentType: application/vnd.kubernetes.protobuf
    cpuCFSQuota: true
    cpuManagerPolicy: none
    cpuManagerReconcilePeriod: 10s
    enableControllerAttachDetach: true
    enableDebuggingHandlers: true
    enforceNodeAllocatable:
    - pods
    eventBurst: 10
    eventRecordQPS: 5
    evictionHard:
      imagefs.available: 15%
      memory.available: 100Mi
      nodefs.available: 10%
      nodefs.inodesFree: 5%
    evictionPressureTransitionPeriod: 5m0s
    failSwapOn: true
    fileCheckFrequency: 20s
    hairpinMode: promiscuous-bridge
    healthzBindAddress: 127.0.0.1
    healthzPort: 10248
    httpCheckFrequency: 20s
    imageGCHighThresholdPercent: 85
    imageGCLowThresholdPercent: 80
    imageMinimumGCAge: 2m0s
    iptablesDropBit: 15
    iptablesMasqueradeBit: 14
    kubeAPIBurst: 10
    kubeAPIQPS: 5
    makeIPTablesUtilChains: true
    maxOpenFiles: 1000000
    maxPods: 110
    nodeStatusUpdateFrequency: 10s
    oomScoreAdj: -999
    podPidsLimit: -1
    registryBurst: 10
    registryPullQPS: 5
    resolvConf: /etc/resolv.conf
    rotateCertificates: true
    runtimeRequestTimeout: 2m0s
    serializeImagePulls: true
    staticPodPath: /etc/kubernetes/manifests
    streamingConnectionIdleTimeout: 4h0m0s
    syncFrequency: 1m0s
    volumeStatsAggPeriod: 1m0s
    EOF
  5. kubelet の有効化と起動:

    # サービスファイルを追加した後に、systemdユニットファイルを再読み込みします
    systemctl daemon-reload
    
    systemctl enable --now kubelet.service
    systemctl start kubelet.service
    systemctl status kubelet.service
  6. ノードがクラスターに参加したことを確認します:

    kubectl get node
  7. マスターノードから kube-proxy kubeconfig をコピーします:

    scp /etc/kubernetes/kube-proxy.kubeconfig $NODE:/etc/kubernetes/kube-proxy.kubeconfig
  8. kube-proxy サービスファイルを作成します。

    cat >  /usr/lib/systemd/system/kube-proxy.service << EOF
    [Unit]
    Description=Kubernetes Kube Proxy
    Documentation=https://github.com/kubernetes/kubernetes
    After=network.target
    
    [Service]
    ExecStart=/usr/local/bin/kube-proxy \\
      --config=/etc/kubernetes/kube-proxy.yaml \\
      --v=2
    Restart=always
    RestartSec=10s
    
    [Install]
    WantedBy=multi-user.target
    
    EOF
  9. kube-proxy 構成ファイルを作成します:

    cat > /etc/kubernetes/kube-proxy.yaml << EOF
    apiVersion: kubeproxy.config.k8s.io/v1alpha1
    bindAddress: 0.0.0.0
    clientConnection:
      acceptContentTypes: ""
      burst: 10
      contentType: application/vnd.kubernetes.protobuf
      kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
      qps: 5
    clusterCIDR: 172.16.0.0/12,fc00:2222::/112
    configSyncPeriod: 15m0s
    conntrack:
      max: null
      maxPerCore: 32768
      min: 131072
      tcpCloseWaitTimeout: 1h0m0s
      tcpEstablishedTimeout: 24h0m0s
    enableProfiling: false
    healthzBindAddress: 0.0.0.0:10256
    hostnameOverride: ""
    iptables:
      masqueradeAll: false
      masqueradeBit: 14
      minSyncPeriod: 0s
      syncPeriod: 30s
    ipvs:
      masqueradeAll: true
      minSyncPeriod: 5s
      scheduler: "rr"
      syncPeriod: 30s
    kind: KubeProxyConfiguration
    metricsBindAddress: 127.0.0.1:10249
    mode: "ipvs"
    nodePortAddresses: null
    oomScoreAdj: -999
    portRange: ""
    udpIdleTimeout: 250ms
    EOF
  10. kube-proxy を有効化して起動します:

     # サービスファイルを追加した後、systemd ユニットファイルを再読み込みします
     systemctl daemon-reload
    
     systemctl enable --now kube-proxy.service
     systemctl restart kube-proxy.service
     systemctl status kube-proxy.service

2.5 ノードプールのステータス同期およびイメージのエクスポート

  1. ACK コンソールで、クラスター に移動します。クラスター名をクリックし、ノード > ノードプール に進みます。

  2. ノードプールの同期 をクリックし、同期が完了するまで待ちます。すべてのノードが失敗状態でないことを確認します。

    image

  3. ECS コンソールで、インスタンスとイメージ > インスタンス に移動します。

  4. インスタンス ID をクリックし、インスタンスの詳細 タブに移動して、カスタムイメージの作成 をクリックします。

    image

  5. インスタンスとイメージ > イメージ に移動し、カスタムイメージが ステータス として 利用可能 で表示されることを確認します。

ステップ 3:カスタムイメージをノードプールに適用

すでにカスタムイメージを所有しており、手順 1 および手順 2 をスキップした場合は、まずそのカスタムイメージを使用して新しいノードプールを作成します。詳細については、「ノードプールの作成と管理」をご参照ください。
  1. ACK コンソールで、クラスター に移動し、クラスター名をクリックして、ノード > ノードプール に進みます。

  2. 該当のノードプールを見つけ、編集 をクリックし、操作 列の 詳細オプション を展開して、カスタムイメージ の横にあるドロップダウンメニューからご自身のカスタムイメージを選択します。

    image

  3. ノードプール ページの オペレーティングシステム 列に カスタムイメージ が表示されていることを確認します。

    image

ステップ 4:初期化スクリプトの更新

カスタムイメージには既にすべての必須ソフトウェアパッケージが含まれています。初期化スクリプトを更新することで、カスタムイメージから起動した新規ノードは、Alibaba Cloud ノードパラメーターを受信・適用するだけで済み、再インストールは不要になります。

重要
  • カスタムイメージ内の残存 kubelet 証明書を削除します(下記スクリプトの 7 行目)。これを実行しないと、新規ノードは新規証明書を生成できず、クラスターへの参加に失敗します。

  • 既存のカスタムノードプールをお使いの場合は、ステップ 1 で示した通り、ack-agent-config 内の addNodeScriptPath の URL を更新してください。

  1. join-ecs-node.sh の内容を次の内容に置き換えます。

    echo "The node providerid is $ALIBABA_CLOUD_PROVIDER_ID"
    echo "The node name is $ALIBABA_CLOUD_NODE_NAME"
    echo "The node labels are $ALIBABA_CLOUD_LABELS"
    echo "The node taints are $ALIBABA_CLOUD_TAINTS"
    systemctl stop kubelet.service
    echo "Delete old kubelet pki" # 古いノード証明書を削除
    rm -rf /var/lib/kubelet/pki/*
    echo "Add kubelet service config"
    # kubelet サービスを設定
    cat > /usr/lib/systemd/system/kubelet.service << EOF
    
    [Unit]
    Description=Kubernetes Kubelet
    Documentation=https://github.com/kubernetes/kubernetes
    After=network-online.target firewalld.service containerd.service
    Wants=network-online.target
    Requires=containerd.service
    
    [Service]
    ExecStart=/usr/local/bin/kubelet \\
        --node-ip=${ALIBABA_CLOUD_NODE_NAME} \\
        --hostname-override=${ALIBABA_CLOUD_NODE_NAME} \\
        --node-labels=${ALIBABA_CLOUD_LABELS} \\
        --provider-id=${ALIBABA_CLOUD_PROVIDER_ID} \\
        --register-with-taints=${ALIBABA_CLOUD_TAINTS} \\
        --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig  \\
        --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
        --config=/etc/kubernetes/kubelet-conf.yml \\
        --container-runtime-endpoint=unix:///run/containerd/containerd.sock
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    # Kubelet サービスを開始
    systemctl start kubelet.service
  2. 更新された join-ecs-node.sh を OSS にアップロードし、以前のバージョンを上書きします。

ステップ 5:ノードプールのスケーリング

  1. ACK コンソールで、クラスター に移動し、クラスター名をクリックして、ノード > ノードプール に進みます。

  2. 該当のノードプールを見つけ、その他 > スケーリング をクリックし、操作 列から新規ノードを追加します。

    image

  3. 両方のノードが Ready 状態であることを確認します:

    kubectl get nodes

    両方のノードが Ready 状態である場合、エラスティックノードプールが正常に作成されたことを確認できます。

次のステップ

  • ノードプールに自動スケーリングポリシーを構成します。詳細については、「自動スケーリングの構成」をご参照ください。