

 **帮助改进此页面** 

要帮助改进本用户指南，请选择位于每个页面右侧窗格中的**在 GitHub 上编辑此页面**链接。

# 将应用程序迁移到新的节点组
<a name="migrate-stack"></a>

本主题介绍如何创建新的节点组，将您的现有应用程序自然地迁移到新组，然后从您的集群中删除旧的节点组。您可以使用 `eksctl` 或 AWS 管理控制台 迁移到新的节点组。
+  [`eksctl`](#eksctl_migrate_apps) 
+  [AWS 管理控制台 和 AWS CLI](#console_migrate_apps) 

## `eksctl`
<a name="eksctl_migrate_apps"></a>

 **使用 `eksctl` 将您的应用程序迁移到新的节点组** 

有关使用 eksctl 进行迁移的更多信息，请参阅 `eksctl` 文档中的[非托管节点组](https://eksctl.io/usage/nodegroup-unmanaged/)。

此过程需要 `eksctl` 版本 `0.215.0` 或更高版本。可以使用以下命令来查看您的版本：

```
eksctl version
```

有关安装或升级 `eksctl` 的说明，请参阅 `eksctl` 文档中的 [Installation](https://eksctl.io/installation)。

**注意**  
此过程仅适用于使用 `eksctl` 创建的集群和节点组。

1. 检索您现有节点组的名称，同时将 *my-cluster* 替换为您的集群名称。

   ```
   eksctl get nodegroups --cluster=my-cluster
   ```

   示例输出如下。

   ```
   CLUSTER      NODEGROUP          CREATED               MIN SIZE      MAX SIZE     DESIRED CAPACITY     INSTANCE TYPE     IMAGE ID
   default      standard-nodes   2019-05-01T22:26:58Z  1             4            3                    t3.medium         ami-05a71d034119ffc12
   ```

1. 用 `eksctl` 和下面的命令启动新节点组。将命令中的所有 *example value* 替换为您自己的值。版本号不能高于控制面板的 Kubernetes 版本，也不能比控制面板的 Kubernetes 版本低两个以上的次要版本。我们建议您使用与控制面板相同的版本。

   如果满足以下条件，我们建议阻止容器组（pod）访问 IMDS：
   + 您计划将 IAM 角色分配给所有 Kubernetes 服务账户，以便容器组（pod）仅具有其所需的最低权限。
   + 集群中没有任何容器组（pod）需要出于其他原因（例如检索当前 AWS 区域）访问 Amazon EC2 实例元数据服务（IMDS）。

     有关更多信息，请参阅[限制对分配给 Worker 节点的实例配置文件的访问](https://aws.github.io/aws-eks-best-practices/security/docs/iam/#restrict-access-to-the-instance-profile-assigned-to-the-worker-node)。

     要阻止容器组（pod）对 IMDS 的访问，请将 `--disable-pod-imds` 选项添加到以下命令。
**注意**  
有关更多可用标志及其说明，请参阅 https://eksctl.io/。

   ```
   eksctl create nodegroup \
     --cluster my-cluster \
     --version 1.35 \
     --name standard-nodes-new \
     --node-type t3.medium \
     --nodes 3 \
     --nodes-min 1 \
     --nodes-max 4 \
     --managed=false
   ```

1. 当上一个命令完成时，使用以下命令验证您的所有节点是否已达到 `Ready` 状态：

   ```
   kubectl get nodes
   ```

1. 使用以下命令删除原始节点组。在命令中，将所有 *example value* 替换为集群和节点组名称：

   ```
   eksctl delete nodegroup --cluster my-cluster --name standard-nodes-old
   ```

## AWS 管理控制台 和 AWS CLI
<a name="console_migrate_apps"></a>

 **使用 AWS 管理控制台和 AWS CLI 将您的应用程序迁移到新的节点组。**

1. 执行[创建自行管理的 Amazon Linux 节点](launch-workers.md)中概述的步骤，启动新的节点组。

1. 完成创建堆栈后，在控制台中选中它，然后选择 **Outputs（输出）**。

1.  记录已创建的节点组的 **NodeInstanceRole**。您需要它来将新的 Amazon EKS 节点添加到集群。
**注意**  
如果您已将任何其他 IAM 策略附加到旧节点组 IAM 角色，则应将这些相同的策略附加到新节点组 IAM 角色以在新组上保持该功能。这适用于为 Kubernetes [Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler) 添加权限的情况。

1. 同时更新两个节点组的安全组，以便它们可以相互通信。有关更多信息，请参阅 [查看集群的 Amazon EKS 安全组要求](sec-group-reqs.md)。

   1. 记下两个节点组的安全组 ID。这在 AWS CloudFormation 堆栈输出中显示为 **NodeSecurityGroup** 值。

      您可以使用以下 AWS CLI 命令从堆栈名称中获取安全组 ID。在这些命令中，`oldNodes` 是您的较旧节点堆栈的 AWS CloudFormation 堆栈名称，`newNodes` 是要迁移到的堆栈的名称。将所有 *example value* 替换为您自己的值。

      ```
      oldNodes="old_node_CFN_stack_name"
      newNodes="new_node_CFN_stack_name"
      
      oldSecGroup=$(aws cloudformation describe-stack-resources --stack-name $oldNodes \
      --query 'StackResources[?ResourceType==`AWS::EC2::SecurityGroup`].PhysicalResourceId' \
      --output text)
      newSecGroup=$(aws cloudformation describe-stack-resources --stack-name $newNodes \
      --query 'StackResources[?ResourceType==`AWS::EC2::SecurityGroup`].PhysicalResourceId' \
      --output text)
      ```

   1. 向每个节点安全组添加入口规则，以便它们接受彼此的流量。

      以下 AWS CLI 命令向每个安全组添加入站规则，以允许来自另一个安全组的所有协议上的所有流量。通过此配置，在您将工作负载迁移到新组时，每个节点组中的容器组（pod）都可以相互通信。

      ```
      aws ec2 authorize-security-group-ingress --group-id $oldSecGroup \
      --source-group $newSecGroup --protocol -1
      aws ec2 authorize-security-group-ingress --group-id $newSecGroup \
      --source-group $oldSecGroup --protocol -1
      ```

1. 编辑 `aws-auth` configmap 以在 RBAC 中映射新的节点实例角色。

   ```
   kubectl edit configmap -n kube-system aws-auth
   ```

   为新的节点组添加新的 `mapRoles` 条目。

   ```
   apiVersion: v1
   data:
     mapRoles: |
       - rolearn: ARN of instance role (not instance profile)
         username: system:node:{{EC2PrivateDNSName}}
         groups:
           - system:bootstrappers
           - system:nodes>
       - rolearn: arn:aws:iam::111122223333:role/nodes-1-16-NodeInstanceRole-U11V27W93CX5
         username: system:node:{{EC2PrivateDNSName}}
         groups:
           - system:bootstrappers
           - system:nodes
   ```

   将 *ARN of instance role (not instance profile)* 代码段替换为在[上一步](#node-instance-role-step)中记录的 **NodeInstanceRole** 值。保存并关闭该文件以应用更新后的 configmap。

1. 查看节点的状态并等待新节点加入您的集群并达到 `Ready` 状态。

   ```
   kubectl get nodes --watch
   ```

1. （可选）如果使用 [Kubernetes Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler)，请将部署缩减到 0 个副本以避免相互冲突的扩缩操作。

   ```
   kubectl scale deployments/cluster-autoscaler --replicas=0 -n kube-system
   ```

1. 使用以下命令，对要使用 `NoSchedule` 删除的每个节点执行 Taint 操作。如此一来，就不会在要替换的节点上调用或重新调用新的容器组（pod）。有关更多信息，请参阅 Kubernetes 文档中的 [Taints and Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)。

   ```
   kubectl taint nodes node_name key=value:NoSchedule
   ```

   如果要将节点升级到新的 Kubernetes 版本，则可使用以下代码段来标识并污染特定 Kubernetes 版本（此示例中为 `1.33` 版本）的所有节点。版本号不能高于控制面板的 Kubernetes 版本，也不能比控制面板的 Kubernetes 版本低两个以上的次要版本。我们建议您使用与控制面板相同的版本。

   ```
   K8S_VERSION=1.33
   nodes=$(kubectl get nodes -o jsonpath="{.items[?(@.status.nodeInfo.kubeletVersion==\"v$K8S_VERSION\")].metadata.name}")
   for node in ${nodes[@]}
   do
       echo "Tainting $node"
       kubectl taint nodes $node key=value:NoSchedule
   done
   ```

1.  确定集群的 DNS 提供商。

   ```
   kubectl get deployments -l k8s-app=kube-dns -n kube-system
   ```

   示例输出如下。此集群使用 CoreDNS 解析 DNS，但您的集群可能会返回 `kube-dns`）：

   ```
   NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
   coredns   1         1         1            1           31m
   ```

1. 如果您的当前部署所运行的副本少于 2 个，请将部署扩展到 2 个副本。如果您的上一个命令输出返回了该项，请将 *coredns* 替换为`kubedns`。

   ```
   kubectl scale deployments/coredns --replicas=2 -n kube-system
   ```

1. 使用以下命令耗尽要从集群中删除的每个节点：

   ```
   kubectl drain node_name --ignore-daemonsets --delete-local-data
   ```

   如果您要将节点升级到新的 Kubernetes 版本，请使用以下代码段标识并耗尽特定 Kubernetes 版本（此示例中为 *1.33* 版本）的所有节点。

   ```
   K8S_VERSION=1.33
   nodes=$(kubectl get nodes -o jsonpath="{.items[?(@.status.nodeInfo.kubeletVersion==\"v$K8S_VERSION\")].metadata.name}")
   for node in ${nodes[@]}
   do
       echo "Draining $node"
       kubectl drain $node --ignore-daemonsets --delete-local-data
   done
   ```

1. 在旧节点耗尽后，请撤销您之前授权的安全组入口规则。然后删除 AWS CloudFormation 堆栈以终止实例。
**注意**  
如果您已将任何其它 IAM 策略附加到旧节点组 IAM 角色（例如，为 [Kubernetes Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler) 添加权限），请先将这些附加策略与该角色分离，然后才能删除 AWS CloudFormation 堆栈。

   1. 撤销您之前为节点安全组创建的入站规则。在这些命令中，`oldNodes` 是您的较旧节点堆栈的 AWS CloudFormation 堆栈名称，`newNodes` 是要迁移到的堆栈的名称。

      ```
      oldNodes="old_node_CFN_stack_name"
      newNodes="new_node_CFN_stack_name"
      
      oldSecGroup=$(aws cloudformation describe-stack-resources --stack-name $oldNodes \
      --query 'StackResources[?ResourceType==`AWS::EC2::SecurityGroup`].PhysicalResourceId' \
      --output text)
      newSecGroup=$(aws cloudformation describe-stack-resources --stack-name $newNodes \
      --query 'StackResources[?ResourceType==`AWS::EC2::SecurityGroup`].PhysicalResourceId' \
      --output text)
      aws ec2 revoke-security-group-ingress --group-id $oldSecGroup \
      --source-group $newSecGroup --protocol -1
      aws ec2 revoke-security-group-ingress --group-id $newSecGroup \
      --source-group $oldSecGroup --protocol -1
      ```

   1. 打开 [AWS CloudFormation 控制台](https://console.aws.amazon.com/cloudformation/)。

   1. 选择您的旧节点堆栈。

   1. 选择**删除**。

   1. 在 **Delete stack**（删除堆栈）确认对话框中，请选择 **Delete stack**（删除堆栈）。

1. 编辑 `aws-auth` configmap 以从 RBAC 中删除旧节点实例角色。

   ```
   kubectl edit configmap -n kube-system aws-auth
   ```

   删除旧节点组的 `mapRoles` 条目。

   ```
   apiVersion: v1
   data:
     mapRoles: |
       - rolearn: arn:aws:iam::111122223333:role/nodes-1-16-NodeInstanceRole-W70725MZQFF8
         username: system:node:{{EC2PrivateDNSName}}
         groups:
           - system:bootstrappers
           - system:nodes
       - rolearn: arn:aws:iam::111122223333:role/nodes-1-15-NodeInstanceRole-U11V27W93CX5
         username: system:node:{{EC2PrivateDNSName}}
         groups:
           - system:bootstrappers
           - system:nodes>
   ```

   保存并关闭该文件以应用更新后的 configmap。

1. （可选）如果您使用的是 Kubernetes [Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler)，请将部署缩减为 1 个副本。
**注意**  
您还必须适当地标记新的自动扩缩组（例如，`k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/my-cluster`）并将您的 Cluster Autoscaler 部署命令更新为指向新标记的自动扩缩组。有关更多信息，请参阅 [AWS 上的 Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/cluster-autoscaler-release-1.3/cluster-autoscaler/cloudprovider/aws)。

   ```
   kubectl scale deployments/cluster-autoscaler --replicas=1 -n kube-system
   ```

1. （可选）确认您使用的是最新版本的 [Kubernetes 的 Amazon VPC CNI 插件](https://github.com/aws/amazon-vpc-cni-k8s)。您可能需要更新 CNI 版本以使用最新的受支持实例类型。有关更多信息，请参阅 [使用 Amazon VPC CNI 将 IP 分配给容器组（pod）](managing-vpc-cni.md)。

1. 如果您的集群使用适用于 DNS 解析的 `kube-dns`（请参阅[[migrate-determine-dns-step]](#migrate-determine-dns-step)），请将 `kube-dns` 部署横向缩减为 1 个副本。

   ```
   kubectl scale deployments/kube-dns --replicas=1 -n kube-system
   ```