

 **帮助改进此页面** 

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

# 对 Amazon EKS 容器组（pod）使用安全组策略
<a name="sg-pods-example-deployment"></a>

要使用容器组（pod）的安全组，您必须拥有现有的安全组。以下步骤展示了如何将安全组策略用于容器组（pod）。除非另有说明，请从同一个终端完成所有步骤，因为以下步骤中使用的变量不会在终端之间持续存在。

如果您具有带 Amazon EC2 实例的容器组（pod），则必须在使用此过程之前配置插件。有关更多信息，请参阅 [为 Amazon EKS 容器组（pod）的安全组配置适用于 Kubernetes 的 Amazon VPC CNI 插件](security-groups-pods-deployment.md)。

1. 创建要将资源部署到的 Kubernetes 命名空间。您可以将 {{my-namespace}} 替换为您要使用的命名空间名称。

   ```
   kubectl create namespace my-namespace
   ```

1.  将 Amazon EKS `SecurityGroupPolicy` 部署到您的集群。

   1. 将以下内容复制到您的设备。如果您宁愿根据服务账户标签选择容器组（pod），则可以将 {{podSelector}} 替换为 `serviceAccountSelector`。您必须指定一个或其他选择器。空的 `podSelector`（示例：`podSelector: {}`）会选择命名空间中的所有容器组（pod）。您可以将 {{my-role}} 更改为您的角色名称。空的 `serviceAccountSelector` 会选择命名空间中的所有服务账户。您可以将 {{my-security-group-policy}} 替换为 `SecurityGroupPolicy` 的名称，并将 {{my-namespace}} 替换为要在其中创建 `SecurityGroupPolicy` 的命名空间。

      您必须将 {{my\_pod\_security\_group\_id}} 替换为现有安全组的 ID。如果您没有现有安全组，则必须创建一个安全组。有关更多信息，请参阅《Amazon EC2 用户指南》[https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/](https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/)中的[适用于 Linux 实例的 Amazon EC2 安全组](https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/ec2-security-groups.html)。您可以指定 1-5 个安全组 ID。如果指定了多个 ID，则所有安全组中的所有规则的组合都会对选定的容器组（pod）生效。

      ```
      cat >my-security-group-policy.yaml <<EOF
      apiVersion: vpcresources.k8s.aws/v1beta1
      kind: SecurityGroupPolicy
      metadata:
        name: my-security-group-policy
        namespace: my-namespace
      spec:
        podSelector:
          matchLabels:
            role: my-role
        securityGroups:
          groupIds:
            - my_pod_security_group_id
      EOF
      ```
**重要**  
您为容器组（pod）指定的一个或多个安全组必须符合以下标准：  
它们必须存在。如果它们不存在，当您部署与选择器匹配的容器组（pod）时，该容器组（pod）会在创建过程中处于卡住状态。如果您描述容器组（pod），则会看到类似于以下内容的错误消息：`An error occurred (InvalidSecurityGroupID.NotFound) when calling the CreateNetworkInterface operation: The securityGroup ID '{{sg-05b1d815d1EXAMPLE}}' does not exist`。
这些安全组必须允许通过您为其配置探测器的任何端口，从应用于您节点的安全组（对于 `kubelet`）进行入站通信。
这些安全组必须允许通过 `TCP` 和 `UDP` 端口 53 与分配到运行 CoreDNS 的容器组（pod）（或其上会运行容器组的节点）的安全组进行出站通信。适用于您的 CoreDNS 容器组（pod）的安全组必须允许来自您指定的安全组的入站 `TCP` 和 `UDP` 端口 53 流量。
它们必须具备必要的入站和出站规则，才能与它们需要与其通信的其他容器组（pod）进行通信。
如果您将安全组与 Fargate 一起使用，它们必须具有允许容器组（pod）与 Kubernetes 控制面板通信的规则。执行此操作的最简单方法是将集群安全组指定为安全组之一。
安全组策略仅适用于新调度的容器组（pod），不会影响正在运行的容器组（pod）。

   1. 部署策略。

      ```
      kubectl apply -f my-security-group-policy.yaml
      ```

1. 部署其标签与上一步中指定的 {{podSelector}} 的 {{my-role}} 值相匹配的示例应用程序。

   1. 将以下内容复制到您的设备。将示例值替换为您自己的值，然后运行修改后的命令。如果您替换 {{my-role}}，请确保它与上一步中为选择器指定的值相同。

      ```
      cat >sample-application.yaml <<EOF
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: my-deployment
        namespace: my-namespace
        labels:
          app: my-app
      spec:
        replicas: 4
        selector:
          matchLabels:
            app: my-app
        template:
          metadata:
            labels:
              app: my-app
              role: my-role
          spec:
            terminationGracePeriodSeconds: 120
            containers:
            - name: nginx
              image: public.ecr.aws/nginx/nginx:1.23
              ports:
              - containerPort: 80
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: my-app
        namespace: my-namespace
        labels:
          app: my-app
      spec:
        selector:
          app: my-app
        ports:
          - protocol: TCP
            port: 80
            targetPort: 80
      EOF
      ```

   1. 使用以下命令部署应用程序。当您部署应用程序时，适用于 Kubernetes 的 Amazon VPC CNI 插件将匹配 `role` 标签，并且您在上一步中指定的安全组将应用到容器组（pod）。

      ```
      kubectl apply -f sample-application.yaml
      ```

1. 查看使用示例应用程序部署的容器组（pod）。对于本主题的其余部分，此终端称为 `TerminalA`。

   ```
   kubectl get pods -n my-namespace -o wide
   ```

   示例输出如下。

   ```
   NAME                             READY   STATUS    RESTARTS   AGE     IP               NODE                                            NOMINATED NODE   READINESS GATES
   my-deployment-5df6f7687b-4fbjm   1/1     Running   0          7m51s   192.168.53.48    ip-192-168-33-28.region-code.compute.internal   <none>           <none>
   my-deployment-5df6f7687b-j9fl4   1/1     Running   0          7m51s   192.168.70.145   ip-192-168-92-33.region-code.compute.internal   <none>           <none>
   my-deployment-5df6f7687b-rjxcz   1/1     Running   0          7m51s   192.168.73.207   ip-192-168-92-33.region-code.compute.internal   <none>           <none>
   my-deployment-5df6f7687b-zmb42   1/1     Running   0          7m51s   192.168.63.27    ip-192-168-33-28.region-code.compute.internal   <none>           <none>
   ```
**注意**  
如果容器组（pod）被卡住，请遵循以下提示。  
如果任何容器组（pod）卡在 `Waiting` 状态，则运行 `kubectl describe pod {{my-deployment-xxxxxxxxxx-xxxxx}} -n {{my-namespace}} `。如果您看到了 `Insufficient permissions: Unable to create Elastic Network Interface.`，请确认您已在上一步中将 IAM 策略添加到 IAM 集群角色。
如果任何容器组（pod）卡在 `Pending` 状态，请确认您的节点实例类型已在 [limits.go](https://github.com/aws/amazon-vpc-resource-controller-k8s/blob/master/pkg/aws/vpc/limits.go) 中列出，并且尚未达到实例类型支持的最大分支网络接口数与您的节点组中节点数的乘积。例如，`m5.large` 实例支持 9 个分支网络接口。如果节点组有 5 个节点，则最多可以为节点组创建 45 个分支网络接口。在删除另一个具有关联安全组的容器组（pod）前，您尝试部署的第 46 个容器组（pod）将会处于 `Pending` 状态。

   如果您运行 `kubectl describe pod {{my-deployment-xxxxxxxxxx-xxxxx}} -n {{my-namespace}} ` 并看到类似于以下消息内容的消息，则可以安全地忽略该消息。当适用于 Kubernetes 的 Amazon VPC CNI 插件尝试在创建网络接口时设置主机联网并失败时，可能会出现此消息。该插件会记录此事件，直到创建了网络接口为止。

   ```
   Failed to create Pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "e24268322e55c8185721f52df6493684f6c2c3bf4fd59c9c121fd4cdc894579f" network for Pod "my-deployment-5df6f7687b-4fbjm": networkPlugin
   cni failed to set up Pod "my-deployment-5df6f7687b-4fbjm-c89wx_my-namespace" network: add cmd: failed to assign an IP address to container
   ```

   不能超过可在实例类型上运行的容器组（pod）的最大数量。有关可在每种实例类型上运行的容器组（pod）的最大数量的列表，请参阅 GitHub 上的 [eni-max-pods.txt](https://github.com/aws/amazon-vpc-cni-k8s/blob/master/misc/eni-max-pods.txt)。当您删除具有关联安全组的容器组（pod）或删除运行该容器组的节点时，VPC 资源控制器会删除分支网络接口。如果您使用适用于安全组的容器组（pod）删除带有容器组的集群，则该控制器不会删除分支网络接口，因此您需要自行删除这些接口。有关如何删除网络接口的信息，请参阅《Amazon EC2 用户指南》中的[删除网络接口](https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/using-eni.html#delete_eni)。

1. 在单独的终端中，Shell 进入其中一个容器组（pod）。对于本主题的其余部分，此终端称为 `TerminalB`。请将 {{5df6f7687b-4fbjm}} 替换为您在上一步输出中返回的其中一个容器组（pod）的 ID。

   ```
   kubectl exec -it -n my-namespace my-deployment-5df6f7687b-4fbjm -- /bin/bash
   ```

1. 从 `TerminalB` 的 Shell 中，确认示例应用程序是否有效。

   ```
   curl my-app
   ```

   示例输出如下。

   ```
   <!DOCTYPE html>
   <html>
   <head>
   <title>Welcome to nginx!</title>
   [...]
   ```

   您收到了输出，因为运行该应用程序的所有容器组（pod）与您创建的安全组关联。该组包含一条规则，允许安全组所关联的所有容器组（pod）之间的所有流量。允许 DNS 流量从该安全组出站到与您的节点关联的集群安全组。节点正在运行 CoreDNS 容器组（pod），您的容器组对其进行了名称查找。

1. 从 `TerminalA` 中，删除允许从安全组与集群安全组进行 DNS 通信的安全组规则。如果您在上一步中没有将 DNS 规则添加到集群安全组中，请将 {{$my\_cluster\_security\_group\_id}} 替换为您在其中创建规则的安全组的 ID。

   ```
   aws ec2 revoke-security-group-ingress --group-id $my_cluster_security_group_id --security-group-rule-ids $my_tcp_rule_id
   aws ec2 revoke-security-group-ingress --group-id $my_cluster_security_group_id --security-group-rule-ids $my_udp_rule_id
   ```

1. 从 `TerminalB` 中，尝试再次访问该应用程序。

   ```
   curl my-app
   ```

   示例输出如下。

   ```
   curl: (6) Could not resolve host: my-app
   ```

   由于容器组（pod）不再能够访问具有与其关联的集群安全组的 CoreDNS 容器组，尝试失败。集群安全组不再具有安全组规则，该规则允许从与您的容器组（pod）关联的安全组进行 DNS 通信。

   如果您尝试使用上一步中的其中一个容器组（pod）返回的 IP 地址访问应用程序，您仍然会收到响应，因为在与安全组相关联的容器组之间允许所有端口，并且不需要名称查找。

1. 完成实验后，您可以移除您创建的示例安全组策略、应用程序和安全组。从 `TerminalA` 运行以下命令。

   ```
   kubectl delete namespace my-namespace
   aws ec2 revoke-security-group-ingress --group-id $my_pod_security_group_id --security-group-rule-ids $my_inbound_self_rule_id
   wait
   sleep 45s
   aws ec2 delete-security-group --group-id $my_pod_security_group_id
   ```