「aws_eks_node_groupの設定がわからず、とりあえずコピペしたが何が正しいのか理解していない」
EKSをTerraformで管理しようとすると、クラスター本体・IAMロール・node group・add-onと、設定すべきリソースが多く、どこから手を付ければいいか迷うエンジニアが多い。
この記事では、EKSクラスターをTerraformでゼロから構築する実践手順を解説します。managed node groupの最小構成から、IRSAの後継であるPod Identityの設定方法、EKS managed add-onのバージョン管理、managed node groupとFargateプロファイルの設計判断まで、設計軸で体系的に身につけられます。
実行環境:Terraform 1.8.x / AWS provider 5.x(EKS 1.30、ap-northeast-1で動作確認済み)
この記事のポイント
・aws_eks_clusterとnode groupリソースで最小構成EKSを作れる
・Pod IdentityはIRSAの後継でOIDC設定が不要になる
・EKS add-onはaws_eks_addonでバージョン固定できる
・managed node group vs Fargate profileの設計判断基準も解説
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜEKSをTerraformで管理するのか
EKSクラスターをAWSコンソールから作成すると、IAMロール・VPC設定・セキュリティグループ・node groupと、多数の設定を手作業で組み合わせることになります。設定内容が暗黙知になり、「なぜこのセキュリティグループが付いているのか」「本番と開発で何が違うのか」が後から追えなくなります。TerraformでEKSを管理する利点は次の3点です。
・構成の再現性:HCLに書かれたリソース定義がそのまま設計書になる。dev環境とprod環境でVPCサイズやnode group数だけ違う構成を、変数1つで切り替えられる
・変更の安全性:terraform planで変更差分を事前確認してからapplyする。コンソール操作では「消すつもりのないリソースを誤って変更した」という事故が起きやすい
・GitOpsへの接続:HCLをGitで管理することで、GitHub ActionsのCI/CDパイプラインにterraform planを組み込み、PRベースのインフラレビューが可能になる
実務では「コンソールで作ってみたが、terraform importで後からTerraform管理に移した」というケースも多い。これについてはterraform importで既存AWSリソースをコード化する方法で詳しく解説しています。
EKSクラスターのHCL基本構成
EKSをTerraformで構築するために最低限必要なリソースは次の4種類です。・aws_eks_cluster:EKSクラスター本体(コントロールプレーン)
・aws_iam_role(クラスター用):EKSコントロールプレーンがAWSサービスにアクセスするためのロール
・aws_eks_node_group:ワーカーノードのmanaged node group
・aws_iam_role(ノード用):ワーカーノードのEC2インスタンスに付与するロール
1. EKSクラスターリソースの定義
まずクラスター本体のIAMロールを作成し、EKSに引き受けさせます。# クラスター用IAMロール resource "aws_iam_role" "eks_cluster" { name = "${var.cluster_name}-cluster-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "eks.amazonaws.com" } Action = "sts:AssumeRole" }] }) } resource "aws_iam_role_policy_attachment" "eks_cluster_policy" { policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" role = aws_iam_role.eks_cluster.name } # EKSクラスター本体 resource "aws_eks_cluster" "main" { name = var.cluster_name version = "1.30" role_arn = aws_iam_role.eks_cluster.arn vpc_config { subnet_ids = var.private_subnet_ids endpoint_private_access = true endpoint_public_access = false } access_config { authentication_mode = "API_AND_CONFIG_MAP" } depends_on = [aws_iam_role_policy_attachment.eks_cluster_policy] }
access_config.authentication_mode = "API_AND_CONFIG_MAP" はEKS 1.23以降で推奨される設定です。以前のaws-auth ConfigMapによる認証に加え、EKS Access Entries(APIベースのアクセス制御)が使えるようになります。
2. managed node groupの設定
ノード用のIAMロールにはEC2ワーカーノードに必要な3つのマネージドポリシーを付与します。resource "aws_iam_role" "eks_node" { name = "${var.cluster_name}-node-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" }] }) } resource "aws_iam_role_policy_attachment" "eks_node_worker" { policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy" role = aws_iam_role.eks_node.name } resource "aws_iam_role_policy_attachment" "eks_node_cni" { policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" role = aws_iam_role.eks_node.name } resource "aws_iam_role_policy_attachment" "eks_node_ecr" { policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" role = aws_iam_role.eks_node.name } resource "aws_eks_node_group" "main" { cluster_name = aws_eks_cluster.main.name node_group_name = "main-ng" node_role_arn = aws_iam_role.eks_node.arn subnet_ids = var.private_subnet_ids # ami_type: AL2023_x86_64_STANDARD (x86_64) or AL2023_ARM_64_STANDARD (Graviton) ami_type = "AL2023_x86_64_STANDARD" instance_types = ["t3.medium"] scaling_config { desired_size = 2 max_size = 5 min_size = 1 } update_config { max_unavailable = 1 } depends_on = [ aws_iam_role_policy_attachment.eks_node_worker, aws_iam_role_policy_attachment.eks_node_cni, aws_iam_role_policy_attachment.eks_node_ecr, ] }
update_config.max_unavailable = 1 はローリングアップデート時に最大1台ずつノードを入れ替えることを意味します。本番でノード数が少ない場合は max_unavailable_percentage で割合指定するほうが安全です。
terraform applyを実行した際の実際の出力です。EKSコントロールプレーンの作成に約9分、その後node groupに約3分かかるのが通常の待機時間です。
aws_iam_role.eks_cluster: Creating... aws_iam_role.eks_cluster: Creation complete after 1s [id=my-eks-cluster-role] aws_iam_role_policy_attachment.eks_cluster_policy: Creation complete after 0s aws_eks_cluster.main: Creating... aws_eks_cluster.main: Still creating... [1m0s elapsed] aws_eks_cluster.main: Still creating... [9m0s elapsed] aws_eks_cluster.main: Creation complete after 9m23s [id=my-eks-cluster] aws_eks_node_group.main: Creating... aws_eks_node_group.main: Still creating... [1m0s elapsed] aws_eks_node_group.main: Creation complete after 2m47s [id=my-eks-cluster:main-ng] Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
$ aws eks update-kubeconfig --name my-eks-cluster --region ap-northeast-1 Updated context arn:aws:eks:ap-northeast-1:123456789012:cluster/my-eks-cluster in /home/ec2-user/.kube/config $ kubectl get nodes NAME STATUS ROLES AGE VERSION ip-10-0-1-45.ap-northeast-1.compute.internal Ready
87s v1.30.2-eks-1552ad0 ip-10-0-2-112.ap-northeast-1.compute.internal Ready 91s v1.30.2-eks-1552ad0
Pod IdentityでPodにIAMロールを付与する
EKSでPodにAWSリソース(S3・DynamoDB等)へのアクセス権を与えるには、IAMロールをKubernetesのサービスアカウントに紐付けます。これまでの標準手法はIRSA(IAM Roles for Service Accounts)でしたが、2023年11月にGA(一般公開)されたPod Identityを使うほうが設定が大幅にシンプルになります。1. IRSAとPod Identityの設計上の違い
| 観点 | IRSA(旧方式) | Pod Identity(新方式) |
|---|---|---|
| OIDC設定 | aws_iam_openid_connect_providerが必要 | 不要(EKS側で自動処理) |
| IAMロールのTrust Policy | OIDCエンドポイントのURL埋め込みが必要 | Service: pods.eks.amazonaws.comを指定 |
| 追加のアドオン | なし | EKS Pod Identity Agentアドオンが必要 |
| 対応EKSバージョン | 1.13以降 | 1.24以降 |
| Terraform側の複雑さ | OIDC ProviderのARN取得処理が煩雑 | aws_eks_pod_identity_associationで完結 |
2. Pod IdentityのHCL設定
Pod Identityを使うには、EKS Pod Identity AgentアドオンとIAMロール・アソシエーションの3つを定義します。# Step 1: Pod Identity Agentアドオンを有効化 resource "aws_eks_addon" "pod_identity_agent" { cluster_name = aws_eks_cluster.main.name addon_name = "eks-pod-identity-agent" depends_on = [aws_eks_node_group.main] } # Step 2: PodのIAMロール(pods.eks.amazonaws.comが引き受ける) resource "aws_iam_role" "s3_reader" { name = "${var.cluster_name}-s3-reader" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "pods.eks.amazonaws.com" } Action = ["sts:AssumeRole", "sts:TagSession"] }] }) } resource "aws_iam_role_policy_attachment" "s3_reader" { policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" role = aws_iam_role.s3_reader.name } # Step 3: サービスアカウントとIAMロールの紐付け resource "aws_eks_pod_identity_association" "s3_access" { cluster_name = aws_eks_cluster.main.name namespace = "default" service_account = "s3-reader-sa" role_arn = aws_iam_role.s3_reader.arn depends_on = [aws_eks_addon.pod_identity_agent] }
>> Terraform実践セミナーの詳細はこちら
EKS managed add-onをTerraformでバージョン管理する
EKS managed add-onはkube-proxy・CoreDNS・vpc-cniなどKubernetesの動作に必要なコンポーネントをAWSが管理してくれる機能です。Terraformで定義しておくことで、add-onのバージョンもコード管理できます。1. 基本3アドオンの定義(vpc-cni・CoreDNS・kube-proxy)
resource "aws_eks_addon" "vpc_cni" { cluster_name = aws_eks_cluster.main.name addon_name = "vpc-cni" addon_version = "v1.18.3-eksbuild.2" depends_on = [aws_eks_node_group.main] } resource "aws_eks_addon" "coredns" { cluster_name = aws_eks_cluster.main.name addon_name = "coredns" addon_version = "v1.11.3-eksbuild.1" depends_on = [aws_eks_node_group.main] } resource "aws_eks_addon" "kube_proxy" { cluster_name = aws_eks_cluster.main.name addon_name = "kube-proxy" addon_version = "v1.30.3-eksbuild.5" depends_on = [aws_eks_node_group.main] }
最新の addon_version は次のコマンドで確認できます。
$ aws eks describe-addon-versions \ --kubernetes-version 1.30 \ --addon-name vpc-cni \ --query 'addons[].addonVersions[].addonVersion' \ --output text \ --region ap-northeast-1 v1.18.3-eksbuild.2 v1.18.2-eksbuild.1 v1.18.1-eksbuild.3
2. EBS CSI Driverアドオンの追加
EBSボリューム(PersistentVolumeClaim)をPodで使うにはEBS CSI Driverが必要です。このアドオンはIAMロールが必要なため、Pod Identityで連携させます。# EBS CSI Driver用のIAMロール resource "aws_iam_role" "ebs_csi_driver" { name = "${var.cluster_name}-ebs-csi-driver" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "pods.eks.amazonaws.com" } Action = ["sts:AssumeRole", "sts:TagSession"] }] }) } resource "aws_iam_role_policy_attachment" "ebs_csi_driver" { policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy" role = aws_iam_role.ebs_csi_driver.name } resource "aws_eks_addon" "ebs_csi_driver" { cluster_name = aws_eks_cluster.main.name addon_name = "aws-ebs-csi-driver" addon_version = "v1.35.0-eksbuild.1" service_account_role_arn = aws_iam_role.ebs_csi_driver.arn depends_on = [aws_eks_node_group.main] }
managed node group vs Fargateプロファイルの設計判断
EKSのワーカーノード方式としてmanaged node groupとFargateプロファイルの2種類があります。どちらを選ぶかはワークロードの特性によって異なります。| 観点 | managed node group | Fargateプロファイル |
|---|---|---|
| コスト | EC2料金(ノードを常時起動) | Pod単位の秒課金 |
| スケーリング速度 | Cluster Autoscalerが必要(2分前後) | Podごとに即座に起動(数秒) |
| DaemonSet | 利用可能 | 利用不可 |
| EBSボリューム | 利用可能 | 利用不可(EFSのみ) |
| GPUワークロード | 利用可能(GPU対応AMI) | 利用不可 |
| 向き先 | 常時稼働サービス・ステートフルアプリ | バッチ・イベント駆動の短命なワークロード |
コスト最適化を優先するケースでは、managed node groupにSpotインスタンスを組み合わせる方法も有効です。その場合は capacity_type = "SPOT" を指定し、複数のインスタンスタイプを指定してスポット枯渇リスクを分散させます。
トラブルシュート:よくあるエラーと対処
1. 「InvalidParameterException: No subnets found」エラー
EKSクラスターのサブネットにはKubernetesがロードバランサーをプロビジョニングできるようタグが必要です。PublicサブネットにはALBを作るために kubernetes.io/role/elb = 1、PrivateサブネットにはInternal ALB用に kubernetes.io/role/internal-elb = 1 タグを付けてください。# VPCモジュールを使っている場合の例 module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "5.13.0" private_subnet_tags = { "kubernetes.io/cluster/${var.cluster_name}" = "shared" "kubernetes.io/role/internal-elb" = "1" } public_subnet_tags = { "kubernetes.io/cluster/${var.cluster_name}" = "shared" "kubernetes.io/role/elb" = "1" } }
2. terraform planで「node groupの変更」が毎回出る
ami_type を指定していてもEKSがAMIリリースを新しくした場合、Terraformがドリフトを検知してnode groupの変更差分を出し続けることがあります。これは正常な動作ですが、予期しないnode groupのローリングアップデートを防ぐには lifecycle.ignore_changes でAMI関連フィールドを無視する設定を検討してください。resource "aws_eks_node_group" "main" { # ... 省略 ... lifecycle { ignore_changes = [ # AMIが更新されても自動でnode groupを変更しない ami_type, release_version, ] } }
3. 「AccessDenied: Not authorized to perform sts:AssumeRole」
Pod Identityを使ったIAMロールの引き受けに失敗するケースのほとんどは、IAMロールのTrust PolicyのPrincipalが pods.eks.amazonaws.com ではなく ec2.amazonaws.com になっているミスです。Pod Identity用のTrust PolicyはService: pods.eks.amazonaws.comを必ず使ってください。IRSAのTrust PolicyコピーからService名を直し忘れると発生します。本記事のまとめ
| やりたいこと | Terraformリソース |
|---|---|
| EKSクラスター本体の作成 | aws_eks_cluster |
| ワーカーノードの追加 | aws_eks_node_group |
| PodへのIAMロール紐付け(新方式) | aws_eks_pod_identity_association |
| vpc-cni / CoreDNS / kube-proxy管理 | aws_eks_addon |
| kubeconfigの更新 | aws eks update-kubeconfig |
| add-onバージョン確認 | aws eks describe-addon-versions |
・TerraformでAWS VPCとEC2を構築する方法|HCL記法とterraformコマンドの実践
・Terraformのmoduleとfor_each・countで構成を再利用する設計パターン
・Terraformのtfstate管理とS3バックエンド設定|チーム運用で壊さないための基礎
・Terraformのsensitive変数とシークレット管理|SSMパラメータストア参照で秘密情報を安全に扱う設計
・Terraformのdepends_onと依存関係設計|暗黙依存とterraform graphでリソース作成順序を制御する方法
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformでCloudFrontとS3の静的サイトをコード管理する方法|OAC・HTTPSリダイレクト・キャッシュポリシーの実践設計
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます