はじめに

5章で用意した環境にデプロイしていきます。具体的には、構成図のECRにdocker imageをpushし、EKSクラスタにmanifestを適用させます。

概念的に表すと以下のようになります。

以下の順に行なっていきます。

  1. terrform applyでAWSリソースを構築する
  2. ECRにコンテナイメージをPushする
  3. アプリケーションを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 マネジメントコンソール)でいくつか覗いてみましょう。

https://aws.amazon.com/jp/console/

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)に追加されます。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/create-kubeconfig.html

これにより、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