

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Kubernetes 資料平面
<a name="scale-data-plane"></a>

選取 EC2 執行個體類型可能是客戶面臨的最困難決策之一，因為在具有多個工作負載的叢集中。沒有適合所有解決方案one-size-fits。以下是一些秘訣，可協助您避免擴展運算的常見陷阱。

## 自動節點自動擴展
<a name="_automatic_node_autoscaling"></a>

我們建議您使用節點自動調整規模，以減少混沌，並與 Kubernetes 深度整合。建議大規模叢集使用[受管節點群組](https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html)和 [Karpenter](https://karpenter.sh/)。

受管節點群組將為您提供 Amazon EC2 Auto Scaling 群組的彈性，並新增受管升級和組態的優點。它可以使用 [Kubernetes Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler) 進行擴展，並且是具有各種運算需求的叢集的常見選項。

Karpenter 是由 AWS 建立的開放原始碼、工作負載原生節點自動擴展器。它會根據資源 （例如 GPU) 的工作負載需求，以及污點和容錯 （例如區域分散） 來擴展叢集中的節點，而無需管理節點群組。節點直接從 EC2 建立，可避免預設節點群組配額 - 每個群組 450 個節點 - 並提供更大的執行個體選擇彈性，並降低營運開銷。我們建議客戶盡可能使用 Karpenter。

## 使用許多不同的 EC2 執行個體類型
<a name="_use_many_different_ec2_instance_types"></a>

每個 AWS 區域每個執行個體類型的可用執行個體數量有限。如果您建立的叢集僅使用一種執行個體類型，並將節點數量擴展到超過該區域的容量，您會收到沒有執行個體可用的錯誤。為了避免此問題，您不應任意限制叢集中可以使用的執行個體類型。

根據預設，Karpenter 將使用廣泛的相容執行個體類型，並根據待定工作負載需求、可用性和成本在佈建時挑選執行個體。您可以擴大 [NodePools](https://karpenter.sh/docs/concepts/nodepools/#instance-types) `karpenter.k8s.aws/instance-category`金鑰中使用的執行個體類型清單。

Kubernetes Cluster Autoscaler 需要類似大小的節點群組，才能持續擴展。您應該根據 CPU 和記憶體大小建立多個群組，並獨立擴展。使用 [ec2-instance-selector](https://github.com/aws/amazon-ec2-instance-selector) 來識別節點群組大小相似的執行個體。

```
ec2-instance-selector --service eks --vcpus-min 8 --memory-min 16
a1.2xlarge
a1.4xlarge
a1.metal
c4.4xlarge
c4.8xlarge
c5.12xlarge
c5.18xlarge
c5.24xlarge
c5.2xlarge
c5.4xlarge
c5.9xlarge
c5.metal
```

## 偏好較大的節點來減少 API 伺服器負載
<a name="_prefer_larger_nodes_to_reduce_api_server_load"></a>

決定要使用的執行個體類型時，較少的大型節點會對 Kubernetes 控制平面造成較少的負載，因為會有較少的 kubelet 和執行中的 DaemonSets。不過，大型節點可能無法完全使用，就像較小的節點一樣。節點大小應根據您的工作負載可用性和擴展需求進行評估。

具有三個 u-24tb1.metal 執行個體 (24 TB 記憶體和 448 個核心） 的叢集有 3 個 kubelet，預設每個節點限制為 110 個 Pod。如果您的 Pod 各使用 4 個核心，則可能預期會發生這種情況 (4 個核心 x 110 = 440 個核心/節點）。使用 3 節點叢集時，處理執行個體事件的能力會很低，因為 1 個執行個體中斷可能會影響叢集的 1/3。您應該在工作負載中指定節點需求和 Pod，以便 Kubernetes 排程器可以正確放置工作負載。

工作負載應定義所需的資源，以及透過污點、容錯和 [PodTopologySpread](https://kubernetes.io/blog/2020/05/introducing-podtopologyspread/) 所需的可用性。他們應該偏好可充分利用的最大節點，並符合可用性目標，以降低控制平面負載、降低操作並降低成本。

如果資源可用，Kubernetes 排程器會自動嘗試將工作負載分散到可用區域和主機。如果沒有可用的容量，Kubernetes Cluster Autoscaler 會嘗試在每個可用區域中平均新增節點。除非工作負載指定其他需求，否則 Karpenter 會嘗試盡可能快速且經濟實惠地新增節點。

若要強制工作負載隨著排程器和新節點分散到可用區域，您應該使用 topologySpreadConstraints：

```
spec:
  topologySpreadConstraints:
    - maxSkew: 3
      topologyKey: "topology.kubernetes.io/zone"
      whenUnsatisfiable: ScheduleAnyway
      labelSelector:
        matchLabels:
          dev: my-deployment
    - maxSkew: 2
      topologyKey: "kubernetes.io/hostname"
      whenUnsatisfiable: ScheduleAnyway
      labelSelector:
        matchLabels:
          dev: my-deployment
```

## 使用類似的節點大小來實現一致的工作負載效能
<a name="_use_similar_node_sizes_for_consistent_workload_performance"></a>

工作負載應該定義需要執行的節點大小，以允許一致的效能和可預測的擴展。請求 500 公尺 CPU 的工作負載在具有 4 個核心的執行個體上的執行方式，與具有 16 個核心的執行個體不同。避免使用爆量 CPUs執行個體類型，例如 T 系列執行個體。

為了確保工作負載獲得一致的效能，工作負載可以使用[支援的 Karpenter 標籤](https://karpenter.sh/docs/concepts/scheduling/#labels)來鎖定特定執行個體大小。

```
kind: deployment
...
spec:
  template:
    spec:
    containers:
    nodeSelector:
      karpenter.k8s.aws/instance-size: 8xlarge
```

使用 Kubernetes Cluster Autoscaler 在叢集中排程的工作負載，應根據標籤比對將節點選擇器與節點群組進行比對。

```
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: eks.amazonaws.com/nodegroup
            operator: In
            values:
            - 8-core-node-group    # match your node group name
```

## 有效率地使用運算資源
<a name="_use_compute_resources_efficiently"></a>

運算資源包括 EC2 執行個體和可用區域。有效使用運算資源可提高您的可擴展性、可用性、效能，並降低您的總成本。在具有多個應用程式的自動擴展環境中，高效的資源使用非常困難。建立 [Karpenter](https://karpenter.sh/) 是為了根據工作負載需求隨需佈建執行個體，以最大化使用率和彈性。

Karpenter 允許工作負載宣告其所需的運算資源類型，而無需先建立節點群組或設定特定節點的標籤污點。如需詳細資訊，請參閱 [Karpenter 最佳實務](https://docs.aws.amazon.com/eks/latest/best-practices/karpenter.html)。請考慮在 Karpenter 佈建器中啟用[整合](https://docs.aws.amazon.com/eks/latest/best-practices/karpenter.html#_scheduling_pods)，以取代使用率不足的節點。

## 自動化 Amazon Machine Image (AMI) 更新
<a name="_automate_amazon_machine_image_ami_updates"></a>

將工作者節點元件保持在最新狀態，將確保您擁有最新的安全修補程式和與 Kubernetes API 相容的功能。更新 kubelet 是 Kubernetes 功能最重要的元件，但自動化作業系統、核心和本機安裝的應用程式修補程式將減少您擴展時的維護。

建議您為節點映像使用最新的 [Amazon EKS 最佳化 Amazon Linux 2](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html) 或 [Amazon EKS 最佳化 Bottlerocket AMI](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami-bottlerocket.html)。Karpenter 將自動使用[最新的可用 AMI](https://karpenter.sh/docs/concepts/nodepools/#instance-types) 來佈建叢集中的新節點。受管節點群組會在[節點群組](https://docs.aws.amazon.com/eks/latest/userguide/update-managed-node-group.html)更新期間更新 AMI，但不會在節點佈建時間更新 AMI ID。

對於受管節點群組，您需要在可用於修補程式版本時，使用新的 AMI IDs 更新 Auto Scaling 群組 (ASG) 啟動範本。AMI 次要版本 （例如 1.23.5 到 1.24.3) 可在 EKS 主控台和 API 中作為[節點群組的升級](https://docs.aws.amazon.com/eks/latest/userguide/update-managed-node-group.html)。修補程式發行版本 （例如 1.23.5 到 1.23.6) 不會顯示為節點群組的升級。如果您想要讓節點群組與 AMI 修補程式版本保持最新狀態，您需要建立新的啟動範本版本，並讓節點群組將執行個體取代為新的 AMI 版本。

您可以從[此頁面](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html)找到最新的可用 AMI，或使用 AWS CLI。

```
aws ssm get-parameter \
  --name /aws/service/eks/optimized-ami/1.24/amazon-linux-2/recommended/image_id \
  --query "Parameter.Value" \
  --output text
```

## 針對容器使用多個 EBS 磁碟區
<a name="_use_multiple_ebs_volumes_for_containers"></a>

EBS 磁碟區具有根據磁碟區類型 （例如 gp3) 和磁碟大小的輸入/輸出 (I/O) 配額。如果您的應用程式與主機共用單一 EBS 根磁碟區，這可能會耗盡整個主機的磁碟配額，並導致其他應用程式等待可用容量。如果應用程式將檔案寫入其浮水印分割區、從主機掛載本機磁碟區，以及根據所使用的記錄代理程式登入標準輸出 (STDOUT)，則會寫入磁碟。

為了避免磁碟 I/O 耗盡，您應該將第二個磁碟區掛載到容器狀態資料夾 （例如 /run/containerd)，請使用單獨的 EBS 磁碟區進行工作負載儲存，並停用不必要的本機記錄。

若要使用 [eksctl](https://eksctl.io/) 將第二個磁碟區掛載到您的 EC2 執行個體，您可以使用具有此組態的節點群組：

```
managedNodeGroups:
  - name: al2-workers
    amiFamily: AmazonLinux2
    desiredCapacity: 2
    volumeSize: 80
    additionalVolumes:
      - volumeName: '/dev/sdz'
        volumeSize: 100
    preBootstrapCommands:
    - |
      "systemctl stop containerd"
      "mkfs -t ext4 /dev/nvme1n1"
      "rm -rf /var/lib/containerd/*"
      "mount /dev/nvme1n1 /var/lib/containerd/"
      "systemctl start containerd"
```

如果您使用 terraform 來佈建節點群組，請參閱 [EKS 藍圖中 terraform ](https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/stateful/#eks-managed-nodegroup-w-multiple-volumes)的範例。如果您使用 Karpenter 佈建節點，則可以[https://karpenter.sh/docs/concepts/nodeclasses/#specblockdevicemappings](https://karpenter.sh/docs/concepts/nodeclasses/#specblockdevicemappings)搭配節點使用者資料使用 來新增其他磁碟區。

若要將 EBS 磁碟區直接掛載到您的 Pod，您應該使用 [AWS EBS CSI 驅動程式](https://github.com/kubernetes-sigs/aws-ebs-csi-driver)，並使用具有儲存類別的磁碟區。

```
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-claim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ebs-sc
  resources:
    requests:
      storage: 4Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: public.ecr.aws/docker/library/nginx
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: ebs-claim
```

## 如果工作負載使用 EBS 磁碟區，請避免具有低 EBS 連接限制的執行個體
<a name="_avoid_instances_with_low_ebs_attach_limits_if_workloads_use_ebs_volumes"></a>

EBS 是工作負載擁有持久性儲存的最簡單方法之一，但也附帶可擴展性限制。每個執行個體類型都有[可連接的 EBS 磁碟區](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/volume_limits.html)數量上限。工作負載需要宣告應該在哪些執行個體類型上執行，並限制具有 Kubernetes 污點的單一執行個體上的複本數量。

## 停用不必要的磁碟記錄
<a name="_disable_unnecessary_logging_to_disk"></a>

避免不必要的本機記錄，方法是不要在生產環境中使用偵錯記錄執行應用程式，並停用經常讀取和寫入磁碟的記錄。日誌記錄是本機日誌記錄服務，可將日誌緩衝區保留在記憶體中，並定期排清至磁碟。日誌優先於將每行立即記錄到磁碟的 syslog。停用 syslog 也會降低您需要的總儲存量，並避免需要複雜的日誌輪換規則。若要停用 syslog，您可以將下列程式碼片段新增至 cloud-init 組態：

```
runcmd:
  - [ systemctl, disable, --now, syslog.service ]
```

## 當作業系統更新速度為必要時，就有修補程式執行個體
<a name="_patch_instances_in_place_when_os_update_speed_is_a_necessity"></a>

**重要**  
只有在需要時才應完成就地修補執行個體。Amazon 建議將基礎設施視為不可變且徹底測試透過較低環境提升的更新，就像應用程式一樣。本節適用於無法這麼做的情況。

在現有的 Linux 主機上安裝套件需要幾秒鐘的時間，而不會中斷容器化工作負載。套件可以安裝和驗證，無需封鎖、耗盡或取代執行個體。

若要取代執行個體，您必須先建立、驗證和分發新的 AMIs。執行個體需要建立替換，且舊執行個體需要封鎖並耗盡。然後，需要在新執行個體上建立工作負載，並針對所有需要修補的執行個體進行驗證和重複。安全地取代執行個體需要數小時、數天或數週的時間，而不會中斷工作負載。

Amazon 建議使用從自動化宣告式系統建置、測試和提升的不可變基礎設施，但如果您需要快速修補系統，則需要修補系統，並在有新的 AMIs 可用時予以取代。由於修補和取代系統之間的時間差異很大，我們建議您在需要時使用 [AWS Systems Manager Patch Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-patch.html) 自動修補節點。

修補節點可讓您快速推出安全性更新，並在 AMI 更新後定期取代執行個體。如果您使用作業系統搭配唯讀根檔案系統，例如 [Flatcar Container Linux](https://flatcar-linux.org/) 或 [Bottlerocket OS](https://github.com/bottlerocket-os/bottlerocket)，我們建議您使用適用於這些作業系統的更新運算子。[Flatcar Linux 更新運算](https://github.com/flatcar/flatcar-linux-update-operator)子和 [Bottlerocket 更新運算子](https://github.com/bottlerocket-os/bottlerocket-update-operator)會重新啟動執行個體，讓節點自動保持最新狀態。