はじめに
5章で用意した環境にデプロイしていきます。具体的には、構成図のECRにdocker imageをpushし、EKSクラスタにmanifestを適用させます。
概念的に表すと以下のようになります。
以下の順に行なっていきます。
terrform applyでAWSリソースを構築する- ECRにコンテナイメージをPushする
- アプリケーションをDeployする
本章では料金が発生します。実行は自己責任でお願いいたします。
terrform applyでAWSリソースを構築する
まずは、terraform planを実行して、実行計画を出力しましょう。./terraform/にカレントディレクトリを移してから以下を実行してください。
terraform plan
大量のdiff (全て +)が出力されたかと思います。最後に、以下のメッセージが出ています。69リソースを「追加」するというPlan結果が得られました。
Plan: 69 to add, 0 to change, 0 to destroy.
新規構築なので期待通りと言えそうです。
では、早速applyしていきましょう。
terraform apply
実行すると、再度Plan結果のdiffが表示され、実行確認を求められます。 yesで進めましょう。
Plan: 69 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
出力結果を貼っておきます。進行中ログは省いています。
module.eks.aws_cloudwatch_log_group.this[0]: Creating...
module.vpc.aws_vpc.this[0]: Creating...
aws_iam_role.eks_cluster_role: Creating...
aws_iam_role.eks_node_group_role: Creating...
aws_ecr_repository.ecr_repository: Creating...
module.eks.aws_cloudwatch_log_group.this[0]: Creation complete
aws_ecr_repository.ecr_repository: Creation complete
aws_iam_role.eks_node_group_role: Creation complete
aws_iam_role.eks_cluster_role: Creation complete
aws_iam_role_policy_attachment.eks_cluster_AmazonEKSClusterPolicy: Creating...
aws_iam_role_policy_attachment.eks_node_group_AmazonEKSWorkerNodePolicy: Creating...
aws_iam_role_policy_attachment.eks_node_group_ssm: Creating...
aws_iam_role_policy_attachment.eks_node_group_AmazonEKS_CNI_Policy: Creating...
aws_iam_role_policy_attachment.eks_node_group_AmazonEC2ContainerRegistryPullOnly: Creating...
module.vpc.aws_vpc.this[0]: Creation complete
module.vpc.aws_default_route_table.default[0]: Creation complete
module.vpc.aws_internet_gateway.this[0]: Creation complete
module.vpc.aws_subnet.public[0]: Creation complete
module.vpc.aws_subnet.public[1]: Creation complete
module.vpc.aws_subnet.private[0]: Creation complete
module.vpc.aws_subnet.private[1]: Creation complete
module.vpc.aws_route_table.public[0]: Creation complete
module.vpc.aws_route_table.private[0]: Creation complete
module.vpc.aws_route_table_association.public[0]: Creation complete
module.vpc.aws_route_table_association.public[1]: Creation complete
module.vpc.aws_route_table_association.private[0]: Creation complete
module.vpc.aws_route_table_association.private[1]: Creation complete
module.vpc.aws_eip.nat[0]: Creation complete
module.vpc.aws_nat_gateway.this[0]: Creation complete
aws_vpc_endpoint.vpc_endpoint_for_s3: Creation complete
aws_vpc_endpoint.vpc_endpoint_for_ecr_api: Creation complete
aws_vpc_endpoint.vpc_endpoint_for_ecr_dkr: Creation complete
module.eks.module.kms.aws_kms_key.this[0]: Creation complete
module.eks.module.kms.aws_kms_alias.this["cluster"]: Creation complete
module.alb.aws_lb.this[0]: Creation complete
aws_lb_listener.http: Creation complete
aws_lb_target_group.alb_tg_to_ng: Creation complete
module.eks.aws_eks_cluster.this[0]: Creation complete
module.eks.aws_eks_addon.this["vpc-cni"]: Creation complete
module.eks.aws_eks_addon.this["kube-proxy"]: Creation complete
module.eks_node_group.aws_eks_node_group.this[0]: Creation complete
aws_autoscaling_attachment.eks_node_group_attachment: Creation complete
Apply complete! Resources: 69 added, 0 changed, 0 destroyed.
Plan通り、69リソースが作成されました。
また、Stateファイルにも変化が見られるはずです。まさに、今のリモートリソースの状態を反映しています。
# terraform/terraform.tfstate
{
"version": 4,
"terraform_version": "1.13.5",
"serial": 1860,
"lineage": "ccc76b92-b59f-0be1-2928-9f8cd6cfd64f",
"outputs": {},
"resources": [
// ...
],
"check_results": null
}
マネコンを覗いてみる
1コマンドでリソースが作成されましたと言われても実感が湧かないと思うので、マネコン(AWS マネジメントコンソール)でいくつか覗いてみましょう。
VPC
検索欄でVPCとかで調べてみるとマッチするかと思います。サブネットに関しても、プライベート・パブリックのが1つずつ、2つのazに配置されているのがわかります。

EKS
Nodeとして起動させているマシンスペックもt3.microとみえていますね。

Podとしてkube-proxyが動いているのが見えたりもします。
アプリケーションをデプロイしたら後でもう一度見てみましょう。
ルートユーザーでログインした場合、EKSクラスタが存在しているところまでは見えても、以下のようにクラスタの中身を見れないと思います。これは、アクセスエントリにより権限を指定のIAMユーザーに絞れている証拠と言えます。

ALB
リスナールール / ターゲットグループの関係性もリソースマップで可視化されています。
また、ヘルスチェックに失敗しているのがわかりますね。これは、ヘルスチェックエンドポイントを持つアプリケーションをまだデプロイしていないためです。こちらも、デプロイ後再度みてみましょう。

他も色々みてみてください。
ECRにコンテナイメージをPushする
Kindクラスターの時と同じく、manifestではデプロイするアプリケーションのコンテナイメージを記述します。
# k8s/values/production/values.yaml
deployment:
name: app
image:
repository: xxx
tag: xxxx
# manifestには実際以下のように展開される
# k8s/charts/http-server/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag }}"
EKSでデプロイを行うために、kubeletがPullするためのコンテナイメージをECRに用意していきましょう。
手順としては、手元でコンテナイメージをビルドして、ECRにPushするという流れになります。
まずは、以下のコマンドでどのIAMユーザーとしてAWSにログインしているかを確認するところから始めましょう。
aws sts get-caller-identity
Push先のECRのURIを控えましょう。
ECR_REPO_URI=$(aws ecr describe-repositories --repository-names eks-sandbox-ecr-repository --query 'repositories[0].repositoryUri' --output text)
# 確認
echo $ECR_REPO_URI
846869429016.dkr.ecr.ap-northeast-1.amazonaws.com/eks-sandbox-ecr-repository
次に、対象のECRにログインします。
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ECR_REPO_URI
Login Succeededと出ればOKです。
Pushするための準備は整いました。 Kindの時と同じようにコンテナをビルドしましょう。
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
docker build -t app:$TIMESTAMP .
ビルドし終わったら、ECRへのPushのためにイメージ名をもう一つつけます。
docker tag app:$TIMESTAMP $ECR_REPO_URI:$TIMESTAMP
イメージを確認しておきます。
% docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
846869429016.dkr.ecr.ap-northeast-1.amazonaws.com/eks-sandbox-ecr-repository 20260426020650 4bc4d9dd37dc 3 minutes ago 109MB
このイメージは 846869429016.dkr.ecr.ap-northeast-1.amazonaws.com ECRのeks-sandbox-ecr-repository というリポジトリに対応する名前が付けられており、この名前を使って docker push を実行すると、そのリポジトリにイメージがpushされます。
では、これをPushしましょう。
docker push $ECR_REPO_URI:$TIMESTAMP
出力として、以下のように出ていると思います。期待通りですね。
The push refers to repository [846869429016.dkr.ecr.ap-northeast-1.amazonaws.com/eks-sandbox-ecr-repository]
マネコンからも確認しておきましょう。
また、production用のvalues.yamlにECRリポジトリ名 + イメージタグを記載しておきましょう。
# k8s/values/production/values.yaml
deployment:
name: app
image:
repository: 846869429016.dkr.ecr.ap-northeast-1.amazonaws.com/eks-sandbox-ecr-repository
tag: "20260426020650"
アプリケーションをDeployする
まずは、kubectlでEKSクラスタ(eks-sandbox)に接続できるように以下のコマンドを実行します。
aws eks update-kubeconfig --region ap-northeast-1 --name eks-sandbox --alias eks-sandbox
これにより、EKSクラスタの接続先情報(APIサーバーのエンドポイントや証明書)と、IAMを用いた認証設定がローカルのkubeconfig(~/.kube/config)に追加されます。
これにより、eks-sandboxというコンテキスト(どのユーザーでどのクラスタに接続するか)が作成されます。
% kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* eks-sandbox arn:aws:eks:ap-northeast-1:846869429016:cluster/eks-sandbox arn:aws:eks:ap-northeast-1:846869429016:cluster/eks-sandbox
kind-sandbox kind-sandbox kind-sandbox
helmfileには以下のように設定していたので、-e productionとすれば、eks-sandboxコンテキストでデプロイされるようになっています。
# k8s/helmfile.yaml.gotmpl
environments:
development:
kubeContext: kind-sandbox
production:
kubeContext: eks-sandbox
---
releases:
- name: app
namespace: "app-{{ .Environment.Name }}"
chart: ./charts/http-server
values:
- values/{{ .Environment.Name }}/values.yaml
installed: true
atomic: false # Rollback on failure is disabled for learning purposes
それでは、/k8sにカレントディレクトリを移してデプロイを実行します。
helmfile -e production apply
デプロイができているか確認する
正常にPodが作成されているのか、kubectlで確認してみます。app-http-server-85b8bbd4c7-fsw97PodがRUNNINGなので良さそうですね。
% kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
app-production app-http-server-85b8bbd4c7-fsw97 2/2 Running 0 34s
kube-system aws-node-6ttjb 2/2 Running 0 89m
kube-system kube-proxy-q427w 1/1 Running 0 89m
kube-system metrics-server-59b569559b-4lsxj 1/1 Running 0 34s
kube-systemnamespaceのpodがKindで見た時よりもPod数が少ないと思いませんか?
Podが乗っているNodeも出力されるようにすると、以下のようになりました。
% kubectl get pods -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,NODE:.spec.nodeName --sort-by='.spec.nodeName'
NAMESPACE NAME NODE
app-production app-http-server-85b8bbd4c7-fsw97 ip-10-0-3-216.ap-northeast-1.compute.internal
kube-system aws-node-6ttjb ip-10-0-3-216.ap-northeast-1.compute.internal
kube-system kube-proxy-q427w ip-10-0-3-216.ap-northeast-1.compute.internal
kube-system metrics-server-59b569559b-4lsxj ip-10-0-3-216.ap-northeast-1.compute.internal
ip-***.ap-northeast-1.compute.internalはWorker Nodeですね。Controle PlaneのPodが一切表示されていません。
これは、Controle PlaneがAWSマネージドであることによるものだと筆者は理解しています。
また、マネコンでも見てみると、ちゃんとこちらでも見えますね。イベントも追えます。
Pod内コンテナも参照できます。利用イメージも、http-serverの方は先ほどPushしたものになっていますね。
また、デプロイ前は失敗していたALBのヘルスチェックも、成功しています。
アプリケーションサーバーにアクセスしてみる
さて、最後にデプロイされているTODOアプリケーションにアクセスしてみましょう。
ALBが公開しているDNS名を取得します。
ALB_DNS=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?LoadBalancerName==`eks-sandbox-alb`].DNSName' --output text)
筆者の場合、eks-sandbox-alb-524784966.ap-northeast-1.elb.amazonaws.comのようです。
# 確認
echo $ALB_DNS
eks-sandbox-alb-524784966.ap-northeast-1.elb.amazonaws.com
ブラウザのアドレスバーに、$ALB_DNSの値をドメインとしてhttp://$ALB_DNS/todosの形式で打ち込んでみると、以下のようにTODOアプリにアクセスできました。
ログも正常に流れていることを確認しました。
% kubectl logs app-http-server-85b8bbd4c7-fsw97 -n app-production | grep todos
2026/04/25 17:58:52 [::1]:56670 GET /todos 200
2026/04/25 18:00:32 [::1]:33240 POST /todos 303
2026/04/25 18:00:32 127.0.0.1:39564 GET /todos 200
環境のお掃除
色々触って確認できたら、最後にお掃除をしましょう。これを忘れると料金が発生し続けるのでご注意ください。
まずはアプリケーションを削除します。./k8sで行ってください。
helmfile -e production destroy
そして、AWSリソース(インフラ)を削除します。./terraformで行ってください。
terraform destroy