教程:适用于 pods 的安全组
适用于 pods 的安全组将 Amazon EC2 安全组与 Kubernetes pods 集成在一起。您可以使用 Amazon EC2 安全组定义允许流向和来自于您部署到运行在许多 Amazon EC2 实例类型和 Fargate 上节点的 pods 的入站和出站网络流量的规则。有关此功能的详细说明,请参阅 pods 的安全组介绍
注意事项
在部署适用于 pods 的安全组之前,请考虑以下限制和条件:
-
适用于 pods 的安全组不能与 Windows 节点一起使用。
-
pods 的安全组不能与为包含 Amazon EC2 节点的
IPv6
系列配置的集群一起使用。但是,您可以将 pods 的安全组与为仅包含 Fargate 节点的IPv6
系列配置的集群一起使用。有关更多信息,请参阅教程:将 IPv6 地址分配给 pods 和 services。 -
大多数基于 Nitro 的 Amazon EC2 实例系列都支持 pods 的安全组,但并非所有实例代的系列都支持安全组。例如,支持
m5
、c5
、r5
、p3
、m6g
、c6g
与r6g
实例系列和实例代。但不支持t
系列中的实例类型。有关支持的实例类型的完整列表,请参阅 GitHub 上的limits.go
文件。您的节点必须是在该文件中拥有 IsTrunkingCompatible: true
的所列出的实例类型之一。有关支持的实例类型的更多信息,请参阅 GitHub 上的 supported EC2 instance types by amazon-vpc-resource-controller-k8s(amazon-vpc-resource-controller-k8s 支持的 EC2 实例类型)。 -
如果您还使用 pod 安全策略来限制对 pod 更改的访问权限,则必须在 Kubernetes
ClusterRoleBinding
中为分配给您psp
的role
指定eks:vpc-resource-controller
Kubernetes 用户。如果您使用的是默认 Amazon EKS psp、role 和 ClusterRoleBinding,则此为eks:podsecuritypolicy:authenticated
ClusterRoleBinding
。例如,您将用户添加到subjects:
部分,如以下示例所示:... subjects: - kind: Group apiGroup: rbac.authorization.k8s.io name: system:authenticated - apiGroup: rbac.authorization.k8s.io kind: User name: eks:vpc-resource-controller - kind: ServiceAccount name: eks-vpc-resource-controller
-
如果您将自定义联网和适用于 pods 的安全组一起使用,则请使用适用于 pods 的安全组所指定的安全组,不要使用
ENIconfig
中指定的安全组。 -
如果您使用的是版本
1.10.2
或更早版本的 Amazon VPC CNI 插件,并且将terminationGracePeriodSeconds
设置包括在您的 pod 规范中,则该设置的值不能为零。 -
如果您使用的是版本
1.10
或更早版本的 Amazon VPC CNI 插件或POD_SECURITY_GROUP_ENFORCING_MODE
=strict
的版本1.11
(默认设置),则您向其分配安全组的 pods 将不支持使用实例目标(externalTrafficPolicy
设置为Local
)的NodePort
和LoadBalancer
型 Kubernetes 服务。有关将负载均衡器与实例目标一起使用的更多信息,请参阅 Amazon EKS 上的网络负载均衡。如果您使用的是POD_SECURITY_GROUP_ENFORCING_MODE
=standard
的版本1.11
或者更高版本的插件,则支持externalTrafficPolicy
被设置为Local
的实例类型。 -
如果您使用的是版本
1.10
或更早版本的 Amazon VPC CNI 插件或POD_SECURITY_GROUP_ENFORCING_MODE
=strict
的版本1.11
(默认设置),对于来自分配有安全组的 pods 的出站流量,禁用源 NAT,以便应用出站安全组规则。要访问 Internet,必须在配置了 NAT 网关或实例的私有子网中部署的节点上启动具有已分配安全组的 pods。将分配的安全组部署到公有子网的 Pods 无法访问 Internet。如果您使用的是
POD_SECURITY_GROUP_ENFORCING_MODE
=standard
的版本1.11
或者更高版本的插件,则发往 VPC 之外的 pod 流量将转换为实例主网络接口的 IP 地址。对于此流量,将使用主网络接口的安全组中的规则,而不是 pod's 安全组中的规则。 -
要将 Calico 网络策略用于具有关联安全组的 pods,您必须使用版本
1.11.0
或更高版本的 Amazon VPC CNI 插件并设置POD_SECURITY_GROUP_ENFORCING_MODE
=standard
。否则,流向和来自具有关联安全组的 pods 的流量不受 Calico 网络策略执行限制,并且仅受限于 Amazon EC2 安全组执行。要更新 Amazon VPC CNI 版本,请参阅 使用 Amazon VPC CNI plugin for Kubernetes Amazon EKS 附加组件。 -
在使用集群中的安全组的 Amazon EC2 节点上运行且使用 Nodelocal DNSCache
的 Pods 仅支持版本 1.11.0
或更高版本 Amazon VPC CNI 插件和POD_SECURITY_GROUP_ENFORCING_MODE
=standard
。要更新 Amazon VPC CNI 插件版本,请参阅 使用 Amazon VPC CNI plugin for Kubernetes Amazon EKS 附加组件。 -
对于具有高流失率的 pods 而言,适用于容器组 (pod) 的安全组可能会导致更高的 pod 启动延迟。这是由于资源控制器中的速率限制造成的。
为适用于 pods 的安全组配置 Amazon VPC CNI plugin for Kubernetes
要部署适用于 pods 的安全组
如果您使用的是仅适用于 Fargate pods 的安全组,而且集群上没有任何 Amazon EC2 节点,请跳至 部署示例应用程序。
-
使用以下命令查看您当前的 Amazon VPC CNI plugin for Kubernetes 版本:
kubectl describe daemonset aws-node --namespace kube-system | grep amazon-k8s-cni: | cut -d : -f 3
输出示例如下。
v1.7.6
如果您的 Amazon VPC CNI plugin for Kubernetes 版本低于
1.7.7
,请将该插件更新到版本1.7.7
或更高版本。有关更多信息,请参阅使用 Amazon VPC CNI plugin for Kubernetes Amazon EKS 附加组件。 -
将
AmazonEKSVPCResourceController
托管 IAM policy 添加到与您的 Amazon EKS 集群关联的集群角色。策略允许角色管理网络接口、网络接口的私有 IP 地址以及与网络实例之间的连接和分离。 -
检索您的集群 IAM 角色的名称,然后将其存储在一个变量中。将
替换为您的集群名称。my-cluster
cluster_role=$(aws eks describe-cluster --name
my-cluster
--query cluster.roleArn --output text | cut -d / -f 2) -
将 策略附加到该角色。
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKSVPCResourceController --role-name $cluster_role
-
-
通过在
aws-node
DaemonSet
中将ENABLE_POD_ENI
变量设置为true
,启用 Amazon VPC CNI 附加组件来管理 pods 的网络接口。此设置一旦设置为true
后,附加组件会为集群中的每个节点添加一个带有值vpc.amazonaws.com/has-trunk-attached=true
的标注。VPC 资源控制器会创建并附加一个带有aws-k8s-trunk-eni
描述且名为中继网络接口的特殊网络接口。kubectl set env daemonset aws-node -n kube-system ENABLE_POD_ENI=true
注意
中继网络接口包含在实例类型支持的最大网络接口数中。有关每种实例类型支持的最大网络接口数的列表,请参阅《适用于 Linux 实例的 Amazon EC2 用户指南》中的每种实例类型的每个网络接口的 IP 地址数。如果您的节点已经附加了最大数量的标准网络接口,则 VPC 资源控制器将预订一个空间。您将必须缩减您正在运行的 pods,以便控制器足以分离和删除标准网络接口、创建中继网络接口并将其附加到实例。
您可以使用以下命令查看哪些节点的
aws-k8s-trunk-eni
设置为true
。如果返回No resources found
,则等几秒钟后重试。上一步需要重新启动 Amazon VPC CNI plugin for Kubernetes pods,这需要几秒钟的时间。kubectl get nodes -o wide -l vpc.amazonaws.com/has-trunk-attached=true
创建中继网络接口后,可以从中继或标准网络接口为 pods 分配辅助 IP 地址。如果节点被删除,中继接口将自动删除。
当您在后面的步骤中部署适用于 pod 的安全组时,VPC 资源控制器会创建一个具有
aws-k8s-branch-eni
描述的名为分支网络接口的特殊网络接口,并将安全组与其关联。除了附加到节点的标准网络接口和中继网络接口之外,还会创建分支网络接口。如果您使用的是存活探测或就绪探测器,则您还需要禁用 TCP 早期解复用器,以便kubelet
可以使用 TCP 连接到分支网络接口上的 pods。要禁用 TCP 早期解复用器,请运行以下命令:kubectl patch daemonset aws-node -n kube-system \ -p '{"spec": {"template": {"spec": {"initContainers": [{"env":[{"name":"DISABLE_TCP_EARLY_DEMUX","value":"true"}],"name":"aws-vpc-cni-init"}]}}}}'
注意
如果您使用的是
1.11.0
或更高版本的 Amazon VPC CNI plugin for Kubernetes 附加组件并设置POD_SECURITY_GROUP_ENFORCING_MODE
=standard
,如下一步所述,则您就不需要运行上一个命令。 -
如果您的集群使用
NodeLocal DNSCache
,或者您想将 Calico 网络策略与拥有自己的安全组的 pods 配合使用,或者您有NodePort
和LoadBalancer
类型的 Kubernetes 服务,将externalTrafficPolicy
设置为Local
的实例目标用于您要将安全组分配到的 pods,则您必须使用版本1.11.0
或更高版本的 Amazon VPC CNI plugin for Kubernetes 附加组件,并且必须启用以下设置:kubectl set env daemonset aws-node -n kube-system POD_SECURITY_GROUP_ENFORCING_MODE=standard
重要
-
Pod 安全组规则不适用于位于相同节点上的 pods 之间或介于 pods 和services之间之外的流量,例如
kubelet
或nodeLocalDNS
。在同一节点上使用不同安全组的容器组(pod)无法通信,因为它们配置在不同的子网中,且这些子网之间的路由被禁用。 -
从 pods 到 VPC 以外的地址的出站流量是转换为实例的主网络接口的 IP 地址的网络地址(除非您还设置了
AWS_VPC_K8S_CNI_EXTERNALSNAT=true
)。对于此流量,将使用主网络接口的安全组中的规则,而不是 pod's 安全组中的规则。 -
要将此设置应用于现有 pods,必须重新启动 pods 在其上运行的 pods 或节点。
-
部署示例应用程序
要将安全组用于 pods,您必须拥有现有安全组并且部署 Amazon EKS SecurityGroupPolicy 到集群中,如以下程序所述。以下步骤介绍如何将安全组策略用于 pod。除非另有说明,请从同一个终端完成所有步骤,因为以下步骤中使用的变量不会在终端之间持续存在。
要使用安全组部署示例 pod
-
创建要将资源部署到该处的 Kubernetes 命名空间。您可以将
my-namespace
替换为您要使用的命名空间名称。kubectl create namespace
my-namespace
-
将 Amazon EKS
SecurityGroupPolicy
部署到您的集群。-
将以下内容复制到您的设备。如果您宁愿根据服务账户标签选择 pods,则可以将
podSelector
替换为serviceAccountSelector
。您必须指定一个或其他选择器。空的podSelector
(示例:podSelector: {}
)会选择命名空间中的所有 pods。您可以将my-role
更改为您的角色名称。空的serviceAccountSelector
会选择命名空间中的所有服务账户。您可以将my-security-group-policy
替换为SecurityGroupPolicy
的名称,并将my-namespace
替换为要在其中创建SecurityGroupPolicy
的命名空间。您必须将
my_pod_security_group_id
替换为现有安全组的 ID。如果您没有现有安全组,则必须创建一个安全组。有关更多信息,请参阅《适用于 Linux 实例Amazon EC2 用户指南》https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/ 中的适用于 Linux 实例的 Amazon EC2 安全组。您可以指定 1-5 个安全组 ID。如果指定了多个 ID,则所有安全组中的所有规则的组合都会对选定的 pods 生效。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 的 pods(或 pods 在其上运行的节点)的安全组进行出站通信。适用于您的 CoreDNS pods 的安全组必须允许来自您指定的安全组的入站TCP
和UDP
端口 53 流量。 -
它们必须具备必要的入站和出站规则才能与它们需要与其通信的其他 pods 进行通信。
-
如果您将安全组与 Fargate 一起使用,它们必须具有允许 pods 与 Kubernetes 控制面板通信的规则。执行此操作的最简单方法是将集群安全组指定为安全组之一。
安全组策略仅适用于新调度的 pods。不影响正在运行的 pods。
-
-
部署策略。
kubectl apply -f
my-security-group-policy.yaml
-
-
部署其标签与上一步中指定的
的podSelector
值相匹配的示例应用程序。my-role
-
将以下内容复制到您的设备。将
示例值
替换为您自己的值,然后运行修改后的命令。如果您替换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 -
使用以下命令部署应用程序。当您部署应用程序时,Amazon VPC CNI plugin for Kubernetes 插件将匹配
role
标注,并且您在上一步中指定的安全组将应用到 pod。kubectl apply -f
sample-application.yaml
-
-
查看使用示例应用程序部署的 pods。对于本主题的其余部分,此终端称为
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 7m51s192.168.53.48
ip-192-168-33-28
.region-code
.compute.internal <none> <none> my-deployment-5df6f7687b
-j9fl4
1/1 Running 0 7m51s192.168.70.145
ip-192-168-92-33
.region-code
.compute.internal <none> <none> my-deployment-5df6f7687b
-rjxcz
1/1 Running 0 7m51s192.168.73.207
ip-192-168-92-33
.region-code
.compute.internal <none> <none> my-deployment-5df6f7687b
-zmb42
1/1 Running 0 7m51s192.168.63.27
ip-192-168-33-28
.region-code
.compute.internal <none> <none>注意
-
如果任何 pods 卡在
Waiting
状态,则运行kubectl describe pod
。如果您看到了
-nmy-deployment-xxxxxxxxxx-xxxxx
my-namespace
Insufficient permissions: Unable to create Elastic Network Interface.
,请确认您已在上一步中将 IAM policy 添加到 IAM 集群角色。 -
如果任何容器组(pod)卡在
Pending
状态,请确认您的节点实例类型已在limits.go
中列出,并且尚未达到实例类型支持的最大分支网络接口数与您的节点组中节点数的乘积。例如, m5.large
实例支持 9 个分支网络接口。如果节点组有 5 个节点,则最多可以为节点组创建 45 个分支网络接口。在删除另一个具有关联安全组的 pod 前,您尝试部署的第 46 个 pod 将会处于Pending
状态。
如果您运行
kubectl describe pod
并看到类似于以下消息内容的消息,则可以安全地忽略该消息。当 Amazon VPC CNI plugin for Kubernetes 尝试在创建网络接口时设置主机联网并失败时,可能会出现此消息。该插件会记录此事件,直到创建了网络接口为止。my-deployment-xxxxxxxxxx-xxxxx
-nmy-namespace
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您不能超过可在实例类型上运行的 pods 的最大数量。有关可在每种实例类型上运行的 pods 的最大数量的列表,请参阅 GitHub 上的 eni-max-pods.txt
。当您删除具有关联安全组的 pod 或删除运行该 pod 的节点时,VPC 资源控制器会删除分支网络接口。如果您使用适用于安全组的 pods 删除带有 pods 的集群,则该控制器不会删除分支网络接口,因此您需要自行删除它们。有关如何删除网络接口的信息,请参阅《适用于 Linux 实例的 Amazon EC2 用户指南》中的删除网络接口。 -
-
在单独的终端中,shell 进入其中一个 pods。对于本主题的其余部分,此终端称为
TerminalB
。将
替换为您在上一步输出中返回的其中一个 pods 的 ID。5df6f7687b
-4fbjm
kubectl exec -it -n my-namespace my-deployment-
-- /bin/bash5df6f7687b
-4fbjm
-
从
TerminalB
的 shell 中,确认示例应用程序是否有效。curl
my-app
输出示例如下。
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
您收到了输出,因为运行该应用程序的所有 pods 与您创建的安全组关联。该组包含一条规则,允许安全组所关联的所有 pods 之间的所有流量。允许 DNS 流量从该安全组出站到与您的节点关联的集群安全组。节点正在运行 CoreDNS pods,您的 pods 对该 DNS 进行了名称查找。
-
从
TerminalA
中,删除允许从安全组与集群安全组进行 DNS 通信的安全组规则。如果您在上一步中没有将 DNS 规则添加到集群安全组中,请将
替换为您在其中创建规则的安全组的 ID。$my_cluster_security_group_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 -
从
TerminalB
中,尝试再次访问该应用程序。curl
my-app
输出示例如下。
curl: (6) Could not resolve host: my-app
由于 pod 不再能够访问具有与其关联的集群安全组的 CoreDNS pods,尝试失败。集群安全组不再具有安全组规则,该规则允许从与 pod 关联的安全组进行 DNS 通信。
如果您尝试使用上一步中的其中一个 pods 返回的 IP 地址访问应用程序,您仍然会收到响应,因为在与安全组相关联的所有 pods 之间允许所有端口,并且不需要名称查找。
-
完成实验后,您可以移除您创建的示例安全组策略、应用程序和安全组。从
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