【EKS✖️Helmチャート】Next.jsアプリを公開しよう！

## 概要
Next.jsで作成したアプリを AWS EKS上にデプロイする方法を紹介します！
以前はGoで実装したAPIをk8s環境にデプロイする記事も書きました！

https://qiita.com/railgun-0402/items/cc34110f9295e2b6f852

ローカルのk8sではなく、EKSにデプロイする記事もということで
今回はNext.jsにて試します！

## 前提条件
- node.jsの開発環境が整っていること
- Docker と Kubernetes（k8s）がインストールされていること
- AWS ECR（Elastic Container Registry）へのアクセス権限があること

また今回はHelm Chartを使用して、k8sリソース作成を
簡略化します！

Helmについては以下記事で入門編として解説してます！

https://qiita.com/railgun-0402/items/b129ea98f9bb3ae80565

## 1. Next.js プロジェクトを作成

Next.jsアプリを作成し、ウェルカムページを表示します！
今回はこの状態でEKSにデプロイしようと思います。

```
npx create-next-app@latest nextjs-app
cd nextjs-app

# 動作確認
npm run dev
```

:::note warn
### 注意
今回はEKSにデプロイすることがメインなので、
Next.jsの実装はあまり触れないです！
:::

## 2. Dockerfile の作成

次に、アプリケーションを Docker コンテナにパッケージングするための Dockerfile を作成します。

```dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm install

FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app

# 本番に必要なものだけコピー
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./

EXPOSE 3000
CMD ["npm", "start"]

```

:::note info
今回は実装少ないですが、ライブラリをインストールしたりすると重くなるので
マルチステージビルド等で工夫すると尚良いかと！
:::


## 3. Docker イメージをビルド

次に、Docker イメージをビルドします。

```bash
$ docker build -t nextjs-app .
```

### 一応ローカルでも動作確認してみよう！

#### 1. Docker イメージをビルド

```bash
docker build -t nextjs-app .
```

> -t nextjs-app はタグ名の指定です（任意）
 
#### 2. コンテナを起動

```bash
docker run -p 3000:3000 nextjs-app
```
> -p 3000:3000 でローカルのポート3000にバインドします。
> 

#### 3. ブラウザでアクセス

```
http://localhost:3000
```

Next.js のトップページが表示されれば成功です ✅

## 4. AWS ECR に Docker イメージをプッシュ

AWS ECR を使用して、作成した Docker イメージをアップロードします。

### ECR にリポジトリを作成

AWSマネジメントコンソールにログインし、ECRにリポジトリを作成しましょう。
名称は任意で大丈夫です！

![スクリーンショット 2025-04-29 15.22.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/945342/e23b8a53-6ee0-4bab-b597-277c5e1c5052.png)


作成したら、画面右上に「プッシュコマンドを表示」ボタンがあります。
これを押下すると、何とECRにPushするところまで方法を案内してくれます！

![スクリーンショット 2025-04-29 15.22.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/945342/d62754b4-87b6-421f-8940-0ebd968f0f6b.png)

## 5. Kubernetes クラスタの準備
EKS向けのクラスターは、**eksctl**というコマンドで簡単に作成可能です！

### eksインストール方法

```bash
$ brew install eksctl  # macOS
```

または公式のGithubから入手も可能です！

https://github.com/eksctl-io/eksctl/releases

### 作成例：
こんな感じで作成できますが、オプションが多いので
下に説明を載せておきます。
```bash
$ eksctl create cluster \
  --name my-cluster \
  --region ap-northeast-1 \
  --nodegroup-name standard-workers \
  --node-type t3.medium \
  --nodes 2 \
  --nodes-min 1 \
  --nodes-max 3 \
  --managed
```

## eksctl create cluster コマンドオプション一覧

| オプション             | 説明                                                                 |
|------------------------|----------------------------------------------------------------------|
| `--name`               | クラスターの名前を指定します。                                      |
| `--region`             | クラスターを作成するAWSリージョン（例: `ap-northeast-1`）。        |
| `--nodegroup-name`     | ノードグループの名前を指定します。                                  |
| `--node-type`          | EC2インスタンスタイプ（例: `t3.medium`）。                          |
| `--nodes`              | 初期のノード数を指定します。                                        |
| `--nodes-min`          | オートスケーリングの最小ノード数を指定します。                      |
| `--nodes-max`          | オートスケーリングの最大ノード数を指定します。                      |
| `--managed`            | マネージドノードグループを使用する（推奨）。                       |
| `--ssh-access`         | SSHアクセスを許可するかどうか（例: `--ssh-access` で有効化）。     |
| `--ssh-public-key`     | SSHで使用する公開鍵の名前（EC2キーペア名）。                        |
| `--version`            | 使用するKubernetesのバージョン（例: `1.29`）。                      |
| `--vpc-private-subnets`| プライベートサブネットIDのカンマ区切りリスト。                     |
| `--with-oidc`          | OIDCプロバイダーを有効にする（IRSA用、推奨）。                     |
| `--tags`               | リソースにタグを付与（例: `"env=dev,team=infra"`）。               |
| `--dry-run`            | 実行せずに設定内容を表示（確認用）。                               |

---

### 例: 最小構成でのクラスター作成

とりあえず早く作成したい！という方は
下記内容で最小限の構成で作成が可能です。

```bash
eksctl create cluster \
  --name my-cluster \
  --region ap-northeast-1 \
  --nodegroup-name standard-workers \
  --node-type t3.medium \
  --nodes 2 \
  --nodes-min 1 \
  --nodes-max 3 \
  --managed
```

:::note info
### 🔒 補足：必要な前提
- AWS CLI の認証が完了している（aws configure済み）
- IAM ユーザーに eks:* / ec2:* 権限がある
:::

:::note info

EKSクラスター作成後は、kubectl get nodes などでノード確認ができます。
:::


## 6. Helm を使ってデプロイ
helm を使って、Kubernetes 上に Next.jsアプリをデプロイします。
まずは、Helm チャートのテンプレートを作成し、values.yaml を編集します。

### Helm Chartを作成する

```
$ helm create nextjs-chart
```

これで go-api/ ディレクトリができ、k8sリソースを作成するための
基本構成が入っています。

### ディレクトリ構成例

```
nextjs-chart/
  Chart.yaml          # Chartの基本情報
  values.yaml         # デフォルトの変数
  templates/          # K8sマニフェスト（Helmテンプレート）
    deployment.yaml   # デプロイメント定義
    service.yaml      # サービス定義
    ingress.yaml      # ingress
    ...
```

### values.yaml

```values.yaml
replicaCount: 1

image:
  repository: {作成したECRのURI}
  tag: {作成したECRのタグ}

service:
  name: nextjs-chart
  type: ClusterIP
  port: 3000

readinessProbe:
  httpGet:
    path: /test
    port: 3000
  initialDelaySeconds: 3
  periodSeconds: 10

ingress:
  enabled: true # falseになるとIngress リソースが作成されないので注意！
  className: "alb" # ingress.yamlのingressClassNameに該当
  annotations: {}
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls: []
```

### deployment.yaml

```deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nextjs-chart
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: nextjs-chart
  template:
    metadata:
      labels:
        app: nextjs-chart
    spec:
      containers:
        - name: nextjs-chart
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: 3000
          livenessProbe:
            httpGet:
              path: {{ .Values.livenessProbe.httpGet.path }}
              port: {{ .Values.livenessProbe.httpGet.port }}
          readinessProbe:
            httpGet:
              path: {{ .Values.readinessProbe.httpGet.path }}
              port: {{ .Values.readinessProbe.httpGet.port }}
```

### ingress.yaml

```ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nextjs-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nextjs-chart
                port:
                  number: 3000
                  
```

## 7. ingressの説明

### 改めてingressの説明！

Kubernetes の Ingress リソースは、
クラスター外部からの HTTP/HTTPS トラフィックを 
Service にルーティングするためのエントリポイントです！

:::note info
特に複数のアプリケーション（Next.js✖️go）のように、ドメインを集約し、
パスベースで振り分ける場合に非常に有効です。

- 「/」とか「/api」みたいに
- ingressを介して、ルーティングするためにALB Ingress Controllerをインストールします（8番で手順を説明します）
:::

## 8. ALB Ingress Controllerをインストール（初回のみ）

### ① [IAM OIDC provider の有効化]（初回のみ）
```
eksctl utils associate-iam-oidc-provider --region ap-northeast-1 --cluster <CLUSTER_NAME> --approve
```

### ② IAM Policy の作成
```
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json

aws iam create-policy \
  --policy-name AWSLoadBalancerControllerIAMPolicy \
  --policy-document file://iam_policy.json
```

### ③ Service Account 作成（--cluster はあなたのEKS名に）

```
eksctl create iamserviceaccount \
  --cluster <CLUSTER_NAME> \
  --namespace kube-system \
  --name aws-load-balancer-controller \
  --attach-policy-arn arn:aws:iam::<AWS_ACCOUNT_ID>:policy/AWSLoadBalancerControllerIAMPolicy \
  --approve
```
### ④ Helm を使って Controller をインストール
```
helm repo add eks https://aws.github.io/eks-charts
helm repo update

helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=<CLUSTER_NAME> \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set region=ap-northeast-1 \
  --set vpcId=<VPC_ID> \
  --set image.repository=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon/aws-load-balancer-controller
```
> vpcId は aws eks describe-cluster --name <CLUSTER_NAME> で取得できます。

### ⑤ 確認！
```
kubectl get pods -n kube-system | grep aws-load
```

:::note info
Pod が Running になればOKです！
:::

## 9. Kubernetes にデプロイ
次に、Helm を使って Kubernetes クラスタにデプロイします。

```bash
#「nextjs-chart」は任意で変えて大丈夫です
$ helm install nextjs-chart ./nextjs-chart

# Ingress確認（ALBのDNSが表示されるまで少し待ちます）
kubectl get ingress
```

これで、Next.jsアプリが Kubernetes 上でデプロイされ、サービスが公開されます。


## 10. 動作確認
デプロイ後、以下のコマンドでリソースが正常にデプロイされているか確認します。
まずはPodが起動しているかをチェック。

### 動作確認

```
$ kubectl get pods
NAME                    READY   STATUS    RESTARTS   AGE
nextjs-chart-cd454c75-jk6wv   1/1     Running   0     2s
```

```
$ kubectl get ingress
NAME             CLASS   HOSTS                 ADDRESS                                                               PORTS   AGE
app-nextjs       alb     chart-example.local                                                                         80      44s
nextjs-ingress   alb     *                     k8s-frontend-63afaf69ef-1598947954.ap-northeast-1.elb.amazonaws.com   80      44s
```


### アクセス確認

- 表示されたALBのDNSにブラウザでアクセス（`http://<ALB-DNS>/`）
- Next.jsトップページが表示されれば成功！

![スクリーンショット 2025-05-03 2.45.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/945342/039a5307-70cb-48c0-a098-ab084800831e.png)

:::note info
Next.jsのウェルカムページの表示が
確認できればOKです！
:::

## まとめ
今回は Next.jsアプリをEKSにデプロイする方法を紹介しました！
k8sを個人開発で使用するための入門として、お役に立てれば幸いです！