はじめに
Amazon EKS (Elastic Kubernetes Service) における Security Groups for Pods 機能は、Pod レベルでセキュリティグループを適用できる機能です。この機能を使うことで、Kubernetes の Pod に対して AWS のセキュリティグループを直接割り当て、きめ細かなネットワークセキュリティ制御が可能になります。
本記事では、EKS クラスターに Security Groups for Pods を導入し、インスタンスタイプごとに設定されている Pod 数の上限に達した場合の挙動を検証します。特に、Pod が Pending 状態になる条件と、その際のエラーメッセージを確認します。
対象読者
- Amazon EKS を運用している方
- Kubernetes の Pod セキュリティについて学びたい方
- EKS の Security Groups for Pods 機能に興味がある方
- インスタンスタイプごとの Pod 数制限について知りたい方
検証環境
- AWS リージョン: ap-northeast-1
- EKS バージョン: 1.30
- インスタンスタイプ: m6a.xlarge
- VPC CNI プラグインバージョン: v1.19.0-eksbuild.1
Security Groups for Pods の概要
機能の説明
Security Groups for Pods は、Amazon EKS の機能で、Kubernetes の Pod に AWS セキュリティグループを直接適用できます。通常の Kubernetes 環境では Pod レベルでのセキュリティグループ適用はできませんが、EKS ではこの機能を使うことで、Pod ごとに異なるセキュリティポリシーを適用できます。
この機能は Amazon VPC CNI プラグインの拡張機能として提供されており、Pod ごとに Branch ENI (Elastic Network Interface) を作成することで実現しています。
仕組み
Security Groups for Pods を有効にすると、以下のような仕組みで動作します:
- クラスターの各ノードに Trunk ENI が作成される
- Pod が作成されると、その Pod 用に Branch ENI が作成される
- Branch ENI にセキュリティグループが適用される
- Pod のネットワークトラフィックは Branch ENI を通過する
参考: Amazon EKS Pod のセキュリティグループ用に Amazon VPC CNI Plugin for Kubernetes を設定する - Amazon EKS
トランクネットワークインターフェイスが作成されると、Pod にはトランクネットワークインターフェイスまたは標準ネットワークインターフェイスのセカンダリ IP アドレスが割り当てられます。ノードが削除されると、トランクインターフェイスは自動的に削除されます。
後のステップで Pod のセキュリティグループをデプロイすると、VPC リソースコントローラーはブランチネットワークインターフェイスと呼ばれる特別なインターフェイスを aws-k8s-branch-eni の説明と共に作成し、セキュリティグループを関連付けます。ノードにアタッチされた標準ネットワークインターフェイスとトランクネットワークインターフェイスに加えて、ブランチネットワークインターフェイスが作成されます。
インスタンスタイプによる制限
重要なポイントとして、インスタンスタイプによって以下の制限があります:
- 対応するインスタンスタイプが限られている(Nitro ベースのインスタンスのみ)
- インスタンスタイプごとに作成できる Branch ENI(つまり SG for Pods を適用できる Pod)の数に上限がある
対応しているインスタンスタイプおよび Branch ENI の上限は amazon-vpc-resource-controller-k8s の limits.go から確認できます。
参考:Amazon EKS Pod のセキュリティグループポリシーを使用する - Amazon EKS
Pod が Pending 状態でスタックした場合、ノードのインスタンスタイプが limits.go のリストに含まれていることを確認します。
今回検証に使用する m6a.xlarge の場合、以下の制限があります:
- 最大 18 個の Branch ENI(= 18 個の Pod に SG を適用可能)
前提条件
必要な権限とリソース
- AWS アカウントと管理者権限
-
eksctl
コマンドラインツール -
kubectl
コマンドラインツール - AWS CLI
インスタンスタイプの選択
Security Groups for Pods は全てのインスタンスタイプでサポートされているわけではありません。特に t2/t3 などの旧世代インスタンスでは使用できません。サポートされているのは主に Nitro ベースのインスタンスです。
サポートされているインスタンスは limits.go より確認できます。
limits.go より m6a.xlarge の記載を抜粋
"m6a.xlarge": {
Interface: 4,
IPv4PerInterface: 15,
IsTrunkingCompatible: true, // この値が true であることが重要
BranchInterface: 18, // 最大 18 個の Branch ENI が作成可能
DefaultNetworkCardIndex: 0,
NetworkCards: []NetworkCard{
{
MaximumNetworkInterfaces: 4,
NetworkCardIndex: 0,
},
},
Hypervisor: "nitro",
IsBareMetal: false,
},
実装手順
STEP1: EKS クラスターの作成
まずは Security Groups for Pods をサポートする EKS クラスターを作成します。
- クラスター設定ファイルを作成します:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: sg-for-pods-cluster
region: ap-northeast-1
version: '1.30'
iam:
withOIDC: true # required
cloudWatch:
clusterLogging:
enableTypes:
- audit
- api
- authenticator
- controllerManager
- scheduler
managedNodeGroups:
- name: managed-ng-1
instanceType: m6a.xlarge # Nitro ベースのインスタンスを選択
minSize: 1
maxSize: 2
desiredCapacity: 1
volumeSize: 20
- クラスターを作成します:
eksctl create cluster -f clusterconfig.yaml
クラスターの作成には約 15〜20 分かかります。
STEP2: Security Groups for Pods の有効化
参考:Amazon EKS Pod のセキュリティグループ用に Amazon VPC CNI Plugin for Kubernetes を設定する - Amazon EKS
- まず、現在の VPC CNI プラグインのバージョンを確認します:
kubectl describe daemonset aws-node --namespace kube-system | grep amazon-k8s-cni: | cut -d : -f 3
出力例:
v1.19.0-eksbuild.1
- クラスターロールに必要な IAM ポリシーを追加します:
cluster_role=$(aws eks describe-cluster --name sg-for-pods-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 に設定します:
kubectl set env daemonset aws-node -n kube-system ENABLE_POD_ENI=true
- 設定が反映されたことを確認します:
kubectl describe daemonset aws-node -n kube-system | grep ENABLE_POD_ENI
出力例:
ENABLE_POD_ENI: true
- CNINode カスタムリソースが作成されたことを確認します:
kubectl get cninode -A
出力例:
NAME FEATURES
ip-192-168-14-83.ap-northeast-1.compute.internal [{"name":"SecurityGroupsForPods"}]
- AWS コンソールで、Trunk ENI が作成されたことを確認します:
Description には「aws-k8s-trunk-eni」と表示されます。
STEP3: セキュリティグループポリシーの作成と適用
参考:Amazon EKS Pod のセキュリティグループポリシーを使用する - Amazon EKS
- Pod 用のセキュリティグループを作成します:
AWS コンソールから、クラスターと同じ VPC 内にセキュリティグループを作成します。今回は検証のため、インバウンドおよびアウトバウンドですべてのトラフィック (0.0.0.0/0) を許可する設定にします。
- Kubernetes 名前空間を作成します:
ここでは my-namespace としています。
kubectl create namespace my-namespace
- SecurityGroupPolicy リソースを作成します:
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
name: my-security-group-policy
namespace: my-namespace
spec:
podSelector:
matchLabels:
role: my-role
securityGroups:
groupIds:
- sg-xxxxxxxx # 作成したセキュリティグループの ID に置き換えてください
- SecurityGroupPolicy をクラスターに適用します:
kubectl apply -f my-security-group-policy.yaml
STEP4: サンプルアプリケーションのデプロイ
- サンプルアプリケーションの YAML ファイルを作成します:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: my-namespace
labels:
app: my-app
spec:
replicas: 4 # まずは少ない数の Pod から始める
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
role: my-role # SecurityGroupPolicy の podSelector と一致させる
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
- アプリケーションをデプロイします:
kubectl apply -f sample-application.yaml
- Pod の状態を確認します:
kubectl get pods -n my-namespace
出力例:
NAME READY STATUS RESTARTS AGE
my-deployment-asdfqwer-29l8g 1/1 Running 0 56s
my-deployment-asdfqwer-9jnjn 1/1 Running 0 56s
my-deployment-asdfqwer-frsvs 1/1 Running 0 56s
my-deployment-asdfqwer-lqjdn 1/1 Running 0 56s
STEP5: Pod の詳細情報の確認
- Pod の詳細情報を確認します:
kubectl describe pods -n my-namespace my-deployment-asdfqwer-29l8g
注目すべき点:
- Annotations セクションに
vpc.amazonaws.com/pod-eni
情報がある - Events セクションに
SecurityGroupRequested
イベントがある
Annotations: vpc.amazonaws.com/pod-eni:
[{"eniId":"eni-08ebf4f50eedbc88f","ifAddress":"0e:9b:d7:18:01:a3","privateIp":"192.168.31.236","ipv6Addr":"","vlanId":2,"subnetCidr":"192....
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 109s default-scheduler Successfully assigned my-namespace/my-deployment-asdfqwer-29l8g to ip-192-168-14-83.ap-northeast-1.compute.internal
Normal SecurityGroupRequested 109s vpc-resource-controller Pod will get the following Security Groups [sg-xxxxxxxx]
Warning FailedCreatePodSandBox 109s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "d9b9b6ad0d2ebe550dd7a3bdefb20afcf37134a568da9443b90bc663087b095e": plugin type="aws-cni" name="aws-cni" failed (add): add cmd: failed to assign an IP address to container
Normal ResourceAllocated 108s vpc-resource-controller Allocated [{"eniId":"eni-08ebf4f50eedbc88f","ifAddress":"0e:9b:d7:18:01:a3","privateIp":"192.168.31.236","ipv6Addr":"","vlanId":2,"subnetCidr":"192.168.0.0/19","subnetV6Cidr":""}] to the pod
Normal Pulling 108s kubelet Pulling image "public.ecr.aws/nginx/nginx:1.23"
Normal Pulled 104s kubelet Successfully pulled image "public.ecr.aws/nginx/nginx:1.23" in 3.959s (3.959s including waiting). Image size: 57001878 bytes.
Normal Created 104s kubelet Created container nginx
Normal Started 104s kubelet Started container nginx
注意:
FailedCreatePodSandBox
のエラーが表示されていますが、その後ResourceAllocated
イベントがあれば問題ありません。これは AWS の公式ドキュメントでも言及されている既知の挙動です。
参考:Amazon EKS Pod のセキュリティグループポリシーを使用する - Amazon EKS
kubectl describe pod my-deployment-xxxxxxxxxx-xxxxx -n my-namespace を実行したときに次のようなメッセージが表示されている場合は、無視しても問題ありません
add cmd: failed to assign an IP address to container
- AWS コンソールで、Branch ENI が作成されていることを確認します:
各 Pod に対して、「branch」タイプの ENI が作成されているはずです。
STEP6: セキュリティグループの動作確認
- Pod に接続して動作確認します:
kubectl exec -it -n my-namespace my-deployment-asdfqwer-29l8g -- /bin/bash
- Pod 内から Service への接続を確認します:
curl my-app
出力例:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
- セキュリティグループの効果を確認するため、アウトバウンドルールを削除します:
AWS コンソールから、先ほど作成したセキュリティグループのアウトバウンドルールをすべて削除します。
- 再度 Pod 内から Service への接続を試みます:
curl my-app
出力例:
curl: (6) Could not resolve host: my-app
これは CoreDNS への接続ができなくなり、名前解決ができなくなったためです。セキュリティグループが正しく機能していることを示しています。
STEP7: Pod 数の上限検証
m6a.xlarge インスタンスでは最大 18 個の Branch ENI を作成できるため、Security Groups for Pods を適用できる Pod 数の上限は 18 個です。この上限を超えた場合の挙動を検証します。
- サンプルアプリケーションの replicas を 19 に変更します:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: my-namespace
labels:
app: my-app
spec:
replicas: 19 # 上限の 18 を超える 19 に設定
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
- 変更を適用します:
kubectl apply -f sample-application.yaml
- Pod の状態を確認します:
kubectl get pods -n my-namespace
出力例:
NAME READY STATUS RESTARTS AGE
my-deployment-asdfqwer-44wgv 1/1 Running 0 14s
my-deployment-asdfqwer-5qzqq 1/1 Running 0 14s
my-deployment-asdfqwer-7whm2 1/1 Running 0 14s
my-deployment-asdfqwer-8zd2r 1/1 Running 0 14s
my-deployment-asdfqwer-bbgcx 1/1 Running 0 14s
my-deployment-asdfqwer-bppbq 1/1 Running 0 14s
my-deployment-asdfqwer-hj2sm 1/1 Running 0 14s
my-deployment-asdfqwer-hktvp 1/1 Running 0 14s
my-deployment-asdfqwer-jsf2l 1/1 Running 0 14s
my-deployment-asdfqwer-mmxb7 1/1 Running 0 14s
my-deployment-asdfqwer-n5tvp 1/1 Running 0 14s
my-deployment-asdfqwer-nlkq9 1/1 Running 0 14s
my-deployment-asdfqwer-nnzhn 1/1 Running 0 14s
my-deployment-asdfqwer-q7wld 1/1 Running 0 14s
my-deployment-asdfqwer-t4z52 1/1 Running 0 14s
my-deployment-asdfqwer-wjxch 1/1 Running 0 14s
my-deployment-asdfqwer-wtb85 1/1 Running 0 14s
my-deployment-asdfqwer-wxmfh 1/1 Running 0 14s
my-deployment-asdfqwer-zhld2 0/1 Pending 0 14s
予想通り、19 個目の Pod が Pending 状態になっています。これは m6a.xlarge インスタンスで作成できる Branch ENI の上限が 18 個であるためです。
- Pending 状態の Pod の詳細を確認します:
kubectl describe pods my-deployment-asdfqwer-zhld2 -n my-namespace
出力例:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 49s default-scheduler 0/1 nodes are available: 1 Insufficient vpc.amazonaws.com/pod-eni. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
エラーメッセージから、vpc.amazonaws.com/pod-eni
リソース(Branch ENI)が不足していることが原因で Pod がスケジュールできないことがわかります。
検証結果と考察
Security Groups for Pods の動作確認
-
正常な動作
- Security Groups for Pods を有効化すると、Trunk ENI が各ノードに作成される
- Pod が作成されると、その Pod 用に Branch ENI が作成される
- Pod に指定したセキュリティグループが適用される
- セキュリティグループのルールに従ってトラフィックが制御される
-
Pod 数の上限
- m6a.xlarge インスタンスでは最大 18 個の Branch ENI を作成可能
- 19 個目の Pod は Pending 状態になり、
Insufficient vpc.amazonaws.com/pod-eni
エラーが発生
インスタンスタイプによる制限
各インスタンスタイプによって作成できる Branch ENI の数が異なります。一部の例を示します:
インスタンスタイプ | 最大 Branch ENI 数 |
---|---|
m6a.xlarge | 18 |
m5.large | 9 |
c5.xlarge | 18 |
r5.2xlarge | 38 |
インスタンスタイプを選択する際は、必要な Pod 数に対して十分な Branch ENI をサポートしているかを確認することが重要です。
注意点
-
既知の警告メッセージ
Pod の詳細情報に
FailedCreatePodSandBox
というエラーが表示されることがありますが、その後にResourceAllocated
イベントがあれば問題ありません。AWS の公式ドキュメントでも言及されている既知の挙動です。 -
インスタンスタイプの互換性
すべてのインスタンスタイプが Security Groups for Pods をサポートしているわけではありません。特に T2/T3 などの旧世代インスタンスはサポートされていません。
-
ENI 数の制限
Security Groups for Pods を使用すると、各 Pod に Branch ENI が割り当てられるため、インスタンスの ENI 制限に達する可能性があります。大規模なデプロイメントを計画する場合は、この制限を考慮する必要があります。
応用例と考察
マルチノード環境での動作
今回は単一ノードでの検証でしたが、マルチノード環境では各ノードごとに Branch ENI の上限があります。例えば、m6a.xlarge インスタンスを 3 台使用する場合、最大 18 × 3 = 54 個の Pod に Security Groups を適用できます。
異なるセキュリティグループの適用
複数の SecurityGroupPolicy を作成し、異なるラベルを持つ Pod に異なるセキュリティグループを適用することも可能です。これにより、マイクロサービスアーキテクチャにおいて、サービスごとに異なるセキュリティポリシーを適用できます。
まとめ
Amazon EKS の Security Groups for Pods 機能は、Kubernetes の Pod レベルでセキュリティグループを適用できる機能です。この機能を使用することで、以下のメリットがあります:
- Pod ごとに異なるセキュリティグループを適用できる
- AWS のセキュリティグループの細かい制御を Kubernetes の Pod に適用できる
- マイクロサービスアーキテクチャにおいて、サービスごとに異なるセキュリティポリシーを適用できる
一方で、以下の制限や注意点があります:
- インスタンスタイプによって作成できる Branch ENI の数に上限がある
- すべてのインスタンスタイプがサポートされているわけではない
- Pod 数が増えると、ENI の数も増加する
今回の検証では、m6a.xlarge インスタンスで最大 18 個の Pod に Security Groups を適用でき、19 個目の Pod は Pending 状態になることを確認しました。この結果は AWS のドキュメントに記載されている制限と一致しています。
Security Groups for Pods を導入する際は、インスタンスタイプごとの Branch ENI の上限を考慮し、必要な Pod 数に対して適切なインスタンスタイプを選択することが重要です。
この記事が Security Groups for Pods の理解と実装の一助となれば幸いです。実際の環境に導入する際は、アプリケーションの要件に合わせて適切に設計・実装してください。