Elastic Beanstalk に追加された Cluster Mode で EKS へデプロイしてみた
はじめに
2026-09-17 に Elastic Beanstalk の Cluster Mode が発表されました。この記事では us-east-1 に PHP アプリをデプロイし、作成された EKS クラスターの中身を確認しました。
Elastic Beanstalk の Cluster Mode とは
アプリケーションのソースを渡すだけで、Amazon EKS へデプロイできます。発表では、イベント駆動のオートスケーリング、OpenTelemetry ベースのオブザーバビリティ、Secrets Manager 連携、HTTPS が既定で備わると説明されています。利用できるのは Elastic Beanstalk が提供されている全商用リージョンです。Cluster Mode 自体への追加料金はありません。
構築
用意するのは IAM ロール4つ
Cluster Mode で環境を作る前に、IAM ロールを4つ作っておきます。CLI / API では自動作成されないため、ロールが無いと環境作成の時点で失敗します。
| ロール名 | 信頼するサービス | アタッチする管理ポリシー |
|---|---|---|
| aws-elasticbeanstalk-eks-cluster-role | eks.amazonaws.com | AmazonEKSClusterPolicy, AmazonEKSComputePolicy, AmazonEKSNetworkingPolicy, AmazonEKSLoadBalancingPolicy, AmazonEKSBlockStoragePolicy, AWSElasticBeanstalkEKSTagging |
| aws-elasticbeanstalk-eks-node-role | ec2.amazonaws.com | AmazonEKSWorkerNodeMinimalPolicy, AmazonEC2ContainerRegistryPullOnly, AmazonSSMManagedInstanceCore |
| aws-elasticbeanstalk-eks-observability-role | pods.eks.amazonaws.com | CloudWatchAgentServerPolicy, AWSElasticBeanstalkEKSObservability |
| aws-elasticbeanstalk-eks-image-build-role | codebuild.amazonaws.com | AWSElasticBeanstalkEKSImageBuild |
cluster-role は EKS クラスター本体、node-role は EKS Auto Mode が起動するノードに使われます。observability-role はログとメトリクスを送る Pod、image-build-role はソースからのイメージビルドに使われます。ロール名はドキュメント記載のとおりに作ることが推奨されています。コンソールがロールを名前で探すためです。今回は EKS Auto Mode 側のガイドに合わせ、AmazonEKSBlockStoragePolicyV2 も cluster-role へ付けました。表の AmazonEKSBlockStoragePolicy とは別のポリシーです。
信頼ポリシーで注意が必要なのは cluster-role と observability-role です。この2つには sts:AssumeRole に加えて sts:TagSession が必要です。EKS Auto Mode 用の管理ポリシーは、権限を ${aws:PrincipalTag/eks:eks-cluster-name} 条件で絞っています。cluster-role に sts:TagSession が無いとセッションタグが付かず、ノード起動に必要な権限が有効になりません。observability-role の方は、EKS Pod Identity が sts:TagSession を要求するためです。node-role と image-build-role は sts:AssumeRole だけで足ります。
cluster-role の信頼ポリシーです。trust-eks-cluster.json として保存しました。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "eks.amazonaws.com" },
"Action": ["sts:AssumeRole", "sts:TagSession"]
}
]
}
保存した内容は次のコマンドで反映します。
aws iam update-assume-role-policy --role-name aws-elasticbeanstalk-eks-cluster-role --policy-document file://trust-eks-cluster.json
observability-role の信頼ポリシーでは、同じ2つのアクションを pods.eks.amazonaws.com に対して許可します。
アプリのビルド方式は2通り
アプリのソースを渡す方式は2つあります。Dockerfile を同梱する方式と、ソースだけを置いて Buildpacks にランタイムを判定させる方式です。方式は application version 作成時の ImageConfiguration.Build で指定します。Dockerfile 方式は Type=docker、Buildpacks 方式は Type=buildpack と Buildpack の組み合わせです。Architecture と CodeBuildServiceRole も同じ階層に置きます。
Buildpacks 側に置いたのは index.php 1ファイルだけです。
検証時点の AWS CLI 2.36.46 には --image-configuration が無かったため、boto3 1.43.97 で実行しました。
specs = [
{
"label": "php-dockerfile-v1",
"key": "app-dockerfile.zip",
"build": {"Type": "docker"},
},
{
"label": "php-buildpacks-v1",
"key": "app-buildpacks.zip",
"build": {"Type": "buildpack", "Buildpack": "paketobuildpacks/builder-jammy-full"},
},
]
for s in specs:
build = dict(s["build"])
build["Architecture"] = "amd64"
build["CodeBuildServiceRole"] = BUILD_ROLE
resp = eb.create_application_version(
ApplicationName=APP,
VersionLabel=s["label"],
SourceBundle={"S3Bucket": BUCKET, "S3Key": s["key"]},
ImageConfiguration={"Build": build},
Process=True,
)
2つの application version を同時に投入し、PROCESSED になるまでの時間を測りました。
{
"php-dockerfile-v1": {
"submitted": "2026-09-18T06:30:58.291253+00:00",
"finished": "2026-09-18T06:32:00.928767+00:00",
"status": "PROCESSED",
"seconds": 63
},
"php-buildpacks-v1": {
"submitted": "2026-09-18T06:30:59.194718+00:00",
"finished": "2026-09-18T06:33:02.124468+00:00",
"status": "PROCESSED",
"seconds": 123
}
}
今回の計測では Buildpacks 側が60秒長くかかりました(63秒に対して123秒)。どちらも環境作成の待ち時間に比べれば小さく、この差だけで方式を決める場面は少ないはずです。方式の選択で効いてくるのはランタイムのバージョンです。これは後述の curl 応答で確認できます。
環境を作るとクラスターごと用意される
create-environment に --tier Name=Cluster,Type=EKS を渡すと、EKS クラスターが無ければクラスターの作成から始まります。
2026-09-18T06:33:33.171000+00:00 INFO Creating CloudFormation stack for cluster infrastructure named: arn:aws:cloudformation:us-east-1:111122223333:stack/beanstalk-cluster-EXAMPLE/... This is a one-time operation and generally takes about 10 minutes.
2026-09-18T06:46:57.192000+00:00 INFO Application resources submitted to EKS cluster. Waiting for them to become ready.
2026-09-18T06:49:10.398000+00:00 INFO Load balancer ready: EXAMPLE-dockerfile-env
2026-09-18T06:49:14.124000+00:00 INFO Successfully launched environment: dockerfile-env
1つ目の環境と2つ目の環境の所要時間です。2つ目は1つ目が Ready になったあと、同じサブネットを指定して作りました。計測条件は us-east-1 / EKS 1.36.2 / EKS Auto Mode / レプリカ2 / デフォルト VPC のパブリックサブネット3 AZ です。
DOCKERFILE_ENV_START=2026-09-18T06:33:23Z
DOCKERFILE_ENV_DONE=2026-09-18T06:49:14Z elapsed=951s(15.9min)
BUILDPACKS_ENV_START=2026-09-18T06:51:41Z
BUILDPACKS_ENV_DONE=2026-09-18T06:54:29Z elapsed=168s(2.8min)
1つ目はクラスターの作成を含めて約16分、2つ目は約3分でした。2つ目が3分弱で完了するのは、同一アカウントで同じサブネットを使う環境が1つのクラスターを共有するためです。クラスターを選ぶ条件はサブネットの組み合わせです。別のクラスターへ分けたい場合は、別のサブネット集合を指定します。
環境のサブネットと、指定した3つのロール(cluster-role / node-role / observability-role)はあとから変更できません。アプリをどのクラスターへ寄せるかは最初に決める必要があります。クラスターの作成中に同じサブネットを指定した2つ目の環境を投入すると割り当てに失敗するため、環境は1つずつ、前の環境が Ready になってから投入します。
CPU やメモリ、レプリカ数、ALB の待ち受けは --option-settings で指定します。各項目の Namespace には注意が必要で、cluster-role と node-role は aws:elasticbeanstalk:eks、observability-role は aws:elasticbeanstalk:eks:environment と分かれています。service-port はコンテナが LISTEN するポートに合わせる必要があります。今回は Dockerfile 版が PHP + Apache の公式イメージを使っているため 80、Buildpacks 版も 80 でした。
環境作成に使った OptionSettings と実行コマンド
env-options.json として保存し、create-environment に渡しました。
[
{"Namespace":"aws:elasticbeanstalk:eks","OptionName":"cluster-role","Value":"arn:aws:iam::111122223333:role/aws-elasticbeanstalk-eks-cluster-role"},
{"Namespace":"aws:elasticbeanstalk:eks","OptionName":"node-role","Value":"arn:aws:iam::111122223333:role/aws-elasticbeanstalk-eks-node-role"},
{"Namespace":"aws:elasticbeanstalk:eks:environment","OptionName":"observability-role","Value":"arn:aws:iam::111122223333:role/aws-elasticbeanstalk-eks-observability-role"},
{"Namespace":"aws:elasticbeanstalk:eks:environment","OptionName":"subnets","Value":"subnet-EXAMPLE1,subnet-EXAMPLE2,subnet-EXAMPLE3"},
{"Namespace":"aws:elasticbeanstalk:eks:environment","OptionName":"cpu","Value":"0.25"},
{"Namespace":"aws:elasticbeanstalk:eks:environment","OptionName":"memory","Value":"256Mi"},
{"Namespace":"aws:elasticbeanstalk:eks:environment","OptionName":"memory-limit","Value":"512Mi"},
{"Namespace":"aws:elasticbeanstalk:eks:environment","OptionName":"service-port","Value":"80"},
{"Namespace":"aws:elasticbeanstalk:eks:environment:autoscaling","OptionName":"min-replica","Value":"2"},
{"Namespace":"aws:elasticbeanstalk:eks:environment:autoscaling","OptionName":"max-replica","Value":"2"},
{"Namespace":"aws:elasticbeanstalk:eks:alb","OptionName":"scheme","Value":"internet-facing"},
{"Namespace":"aws:elasticbeanstalk:eks:alb","OptionName":"listen-ports","Value":"[{\"HTTP\":80}]"},
{"Namespace":"aws:elasticbeanstalk:eks:alb","OptionName":"healthcheck-path","Value":"/"}
]
aws elasticbeanstalk create-environment \
--application-name eb-cluster-yattemita \
--environment-name dockerfile-env \
--version-label php-dockerfile-v1 \
--tier Name=Cluster,Type=EKS \
--option-settings file://env-options.json
公開は ALB + HTTPS が既定
環境に適用された設定(describe-configuration-settings の出力)には HTTPS の 443 が加わり、certificate-arn にも値が入っていました。証明書は ACM で自動的に用意されます。
certificate-arn arn:aws:acm:us-east-1:111122223333:certificate/EXAMPLE
listen-ports [{"HTTP":80},{"HTTPS":443}]
scheme internet-facing
target-type ip
ALB のリスナーは2つです。次の出力の列は、ポート / プロトコル / 既定アクション / リダイレクト先ポートです。
443 HTTPS fixed-response None
80 HTTP redirect 443
443 の既定アクションは fixed-response(404)で、優先度1のルールが /* をターゲットグループへ転送します。listen-ports に HTTP の 80 だけを指定しても、今回は HTTP のみで待ち受ける構成にはなりませんでした。動作確認で ALB の DNS 名に直接アクセスすると 443 へリダイレクトされます。リダイレクト先のホスト名は証明書と一致しないため、-k なしの curl は証明書の検証で止まりました。
HTTP/1.1 301 Moved Permanently
Server: awselb/2.0
Location: https://EXAMPLE-dockerfile-env-000000000.us-east-1.elb.amazonaws.com:443/
アクセスは環境の CNAME で行います。2つの環境それぞれの応答です。
$ curl -sk -w "\nHTTP %{http_code}\n" https://dockerfile-env.eba-EXAMPLE.us-east-1.elasticbeanstalk.com/
<h1>Hello, Beanstalk Cluster!</h1><p>Build: <strong>Dockerfile</strong></p><p>PHP: 8.3.33</p><p>Hostname: deployment-dockerfile-env-8675b6bb46-zl94n</p><p>Time: 2026-09-18 06:52:19 UTC</p>
HTTP 200
$ curl -sk -w "\nHTTP %{http_code}\n" https://buildpacks-env.eba-EXAMPLE.us-east-1.elasticbeanstalk.com/
<h1>Hello, Beanstalk Cluster!</h1><p>Build: <strong>Buildpacks</strong></p><p>PHP: 8.4.25</p><p>Hostname: deployment-buildpacks-env-75bf8d849b-4qtqb</p><p>Time: 2026-09-18 06:55:49 UTC</p>
HTTP 200
Dockerfile 版は自分で指定したベースイメージの PHP 8.3.33、ソースだけ渡した Buildpacks 版は builder が選んだ PHP 8.4.25 でした。バージョンを固定したいなら Dockerfile 方式を選ぶことになります。
なお -k は証明書の検証を省く指定で、今回は疎通確認のために付けています。このため CNAME と自動で用意された証明書のドメインが一致するかは確認していません。
クラスターの中身を見る
作成されたクラスターのエンドポイントは、既定でプライベートのみです。kubectl で接続するには、パブリックエンドポイントを有効にし(反映に約4分かかりました)、自分のプリンシパルをアクセスエントリとして登録します。publicAccessCidrs は自分の IP レンジに絞ります(値は角括弧で囲み、複数指定するときはカンマで並べます)。
aws eks update-cluster-config --name beanstalk-cluster-EXAMPLE \
--resources-vpc-config endpointPublicAccess=true,publicAccessCidrs=[203.0.113.0/24],endpointPrivateAccess=true
aws eks create-access-entry --cluster-name beanstalk-cluster-EXAMPLE \
--principal-arn arn:aws:iam::111122223333:role/<自分のロール> --type STANDARD
aws eks associate-access-policy --cluster-name beanstalk-cluster-EXAMPLE \
--principal-arn arn:aws:iam::111122223333:role/<自分のロール> --access-scope type=cluster \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy
aws eks update-kubeconfig --region us-east-1 --name beanstalk-cluster-EXAMPLE
ここで付けている AmazonEKSClusterAdminPolicy は確認目的で付けた広い権限です。確認が済んだら endpointPublicAccess=false に戻し、アクセスエントリも delete-access-entry で削除します。
ノードは2台で、OS は Bottlerocket の EKS Auto Mode 向けイメージでした。
NAME STATUS ROLES AGE VERSION OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
i-EXAMPLE1 Ready <none> 10m v1.36.2-eks-bca9cf6 Bottlerocket (EKS Auto, Standard) 2026.9.14 (aws-k8s-1.36-standard) 6.18.44 (amd64) containerd://2.2.7+bottlerocket
i-EXAMPLE2 Ready <none> 11m v1.36.2-eks-bca9cf6 Bottlerocket (EKS Auto, Standard) 2026.9.14 (aws-k8s-1.36-standard) 6.18.44 (arm64) containerd://2.2.7+bottlerocket
アーキテクチャは amd64 と arm64 が1台ずつで、Architecture: amd64 でビルドしたアプリの Pod は4つとも amd64 側のノードに載っていました。同時刻に aws ec2 describe-instances を実行してもこれらのノードは表示されませんでした(実行中インスタンス0件)。EKS Auto Mode のノードは EC2 managed instances として扱われるためです。
環境ごとに Namespace eb-<環境名> が作られ、アプリはその中の Deployment deployment-<環境名> として動きます。
クラスター内の Namespace とアプリ Pod
Elastic Beanstalk がインストールしたアドオンは Namespace ごとに分かれています。
NAME STATUS AGE
cert-manager Active 12m
eb-buildpacks-env Active 4m46s
eb-dockerfile-env Active 10m
fluent-bit Active 12m
keda Active 11m
ks-observability Active 10m
kube-state-metrics Active 12m
kube-system Active 16m
opentelemetry-operator-system Active 11m
ログ収集は fluent-bit、メトリクスは kube-state-metrics です。オートスケーリングは keda、OpenTelemetry Collector 群は ks-observability です。この一覧に Namespace が出ない metrics-server と secrets-store-csi-driver は kube-system に入っていました。アプリ側の Pod は環境ごとに2つです。
NAMESPACE NAME READY STATUS RESTARTS AGE
eb-buildpacks-env deployment-buildpacks-env-75bf8d849b-4qtqb 1/1 Running 0 4m42s
eb-buildpacks-env deployment-buildpacks-env-75bf8d849b-rlv96 1/1 Running 0 4m42s
eb-dockerfile-env deployment-dockerfile-env-8675b6bb46-267sn 1/1 Running 0 10m
eb-dockerfile-env deployment-dockerfile-env-8675b6bb46-zl94n 1/1 Running 0 10m
Pod を削除すると代替 Pod が起動する
レプリカ2の dockerfile-env で Pod を1つ削除し、代替 Pod が起動するまでの時間と、その間の応答を確認しました。以下は記録の最初と最後の抜粋です。
=== delete at 06:57:15Z
pod "deployment-dockerfile-env-8675b6bb46-zl94n" deleted
--- 06:57:36Z
NAME READY STATUS RESTARTS AGE
deployment-dockerfile-env-8675b6bb46-267sn 1/1 Running 0 10m
deployment-dockerfile-env-8675b6bb46-vhc6f 1/1 Running 0 20s
curl HTTP 200
--- 06:58:53Z
NAME READY STATUS RESTARTS AGE
deployment-dockerfile-env-8675b6bb46-267sn 1/1 Running 0 12m
deployment-dockerfile-env-8675b6bb46-vhc6f 1/1 Running 0 98s
curl HTTP 200
削除から21秒後の時点で代替 Pod はすでに Running でした。削除から98秒後の 06:58:53Z まで、約15秒間隔で計6回実行した curl はすべて HTTP 200 です。連続的な監視ではなくサンプリングなので瞬間的な失敗は捉えられませんが、レプリカ2であれば1つ落ちてもリクエストを受け続ける構成が最初から用意されています。
CloudWatch に流れてくるもの
ロググループは4つ作られていました。アプリの出力は /aws/elasticbeanstalk/application/logs に入ります。アドオン Pod のログは /aws/elasticbeanstalk/infrastructure/logs です。残る2つは /aws/elasticbeanstalk/infrastructure/metrics と、ビルド用の /aws/codebuild/elasticbeanstalk-<app>-<id> です。
アプリのログストリーム名は eb-<環境名>.<Pod名> です。メトリクスは名前空間 ElasticBeanstalk/Infrastructure に入ります。
=== ElasticBeanstalk/Infrastructure metrics
EnvironmentReplicas
container_cpu_usage_seconds_total
container_memory_working_set_bytes
=== app log sample (last 5 min)
eb-dockerfile-env.deployment-dockerfile-env-8675b6bb46-267sn x.x.x.x - - [18/Sep/2026:06:54:31 +0000] "GET / HTTP/1.1" 200 408 "-" "ELB-HealthChecker/2.0"
eb-buildpacks-env.deployment-buildpacks-env-75bf8d849b-rlv96 [Fri Sep 18 06:54:36 2026] x.x.x.x:4794 [200]: GET /
ログ転送とメトリクス収集のための設定は、自分では何も追加していません。ストリームが Pod 単位に分かれるため、どのレプリカの出力かは名前で判別できます。一方でアプリログは標準出力がそのまま流れるため、形式はアプリ側に依存します。今回は Dockerfile 版が Apache、Buildpacks 版が PHP built-in server の形式でした。ログをパースする処理を作るなら、ビルド方式ごとの形式の差を考慮する必要があります。
料金と後片付け
Cluster Mode で作られるのは通常の EKS クラスターです。標準サポートのバージョンなら、クラスター1つあたり $0.10 / 時間がかかります。ノードは EKS Auto Mode が起動するため、EC2 インスタンス料金に加えて管理手数料が加算されます。公式の例では c6a.2xlarge で、EC2 の $0.306 / 時間に対して Auto Mode の管理手数料が $0.03672 / 時間です。
さらに、ECR のイメージ保管と CloudWatch Logs のログ取り込み・保存に料金がかかります。us-east-1 では、ECR のプライベートリポジトリが $0.10 / GB / 月、CloudWatch Logs は取り込みが $0.50 / GB、保存が $0.03 / GB / 月です(2026-09-18 時点)。同一リージョン内の ECR イメージプルは無料です。アプリだけでなく、アドオンのログも取り込み対象です。
後片付けで注意が必要なのはクラスターです。環境2つの terminate-environment は約4分で Terminated になりました。しかし、EKS クラスターと beanstalk-cluster-<uuid> の CloudFormation スタックは残ったままでした。スタックの削除を明示的に実行すると、約6分で消えました。
aws elasticbeanstalk terminate-environment --environment-name dockerfile-env
aws elasticbeanstalk terminate-environment --environment-name buildpacks-env
aws elasticbeanstalk delete-application --application-name eb-cluster-yattemita --terminate-env-by-force
aws cloudformation delete-stack --stack-name beanstalk-cluster-EXAMPLE
aws s3 rm s3://<今回作成したバケット> --recursive && aws s3api delete-bucket --bucket <今回作成したバケット>
aws ecr delete-repository --repository-name elasticbeanstalk-eb-cluster-yattemita-EXAMPLE --force
aws codebuild delete-project --name elasticbeanstalk-eb-cluster-yattemita-EXAMPLE
ソースバンドル用に自分で作った S3 バケット、ECR リポジトリ、CodeBuild プロジェクトも環境の削除では消えないため、個別に削除しました。Elastic Beanstalk の既定バケットを共用している場合は、今回アップロードしたキーだけを消します。最後に、スタックとクラスター、インスタンスが残っていないことを確認しました。
=== verify at 07:11:20Z
-- cfn stack
Stack with id beanstalk-cluster-EXAMPLE does not exist
-- eks clusters
{ "clusters": [] }
-- running instances
0
まとめ
Cluster Mode では、従来の EC2 ベースの Elastic Beanstalk 環境と異なり、EKS がアプリの実行基盤になります。Kubernetes のマニフェストを管理せずに、Pod の復旧、ALB による公開、CloudWatch へのログ・メトリクス送信までを任せられる点が特徴です。クラスターや Kubernetes の詳細な運用は Elastic Beanstalk に任せたい場合の選択肢になります。
EKS を試す入口としては良さそうです。ただし、Cluster Mode で作られる EKS クラスターの構成を、Elastic Beanstalk を介さずに直接変更することは推奨されていません。Cluster Mode を採用する際は、EKS に求める機能を Elastic Beanstalk 経由でも利用できるか、事前に評価することをおすすめします。






