EKS Workshop(Introduction)をやってみる

# はじめに
- AWSでは、マネージドなKubernetes基盤としてAmazon Elastic Kubernetes Service(EKS)が提供されています
- ただ、機能がかなり豊富であったり、Kubernetesと密接なツール(エコシステム)が多くあり、「ちょっと試してみる」と言いつつ何から進めればよいのか困ることが多いです
- そこで提供されているのが、[EKS Workshop](https://www.eksworkshop.com/)です
- このWorkshopでは、EKSを作成するところから、AWSの監視や運用サービスに連携させる方法、また様々なエコシステムとの連携を学ぶことができます
- 本ブログでは、そのIntroductionを実行する手順を試してみます
- 後続、章立てされている内容も同様にまとめていく予定ですので、お楽しみに～

# 流れ
##  環境構築(Setup)
※ワークショップ記事がかなり丁寧な記載になっているため、困った個所や重要ポイントのみピックアップしています
1. AWS CloudFormationクイック作成リンクを用いて、us-west-2、eu-west-1、ap-southeast-1のいずれかのリージョンでIDEを作成する
    - 以下はCloudFormationテンプレートをClaudeで読み込ませた結果

    ```
    # 主要なリソース
    ## インフラストラクチャ
    - **VPC環境**: 単一のパブリックサブネット、インターネットゲートウェイ、ルートテーブルを含むVPC
    - **EC2インスタンス**: EKS Workshop用のt3.medium（30GBのボリュームサイズ）インスタンス
    - **セキュリティグループ**: CloudFrontからのHTTPアクセスを許可するセキュリティグループ
    ## アプリケーション
    - **Code Server**: ブラウザベースのVS Code環境（指定したバージョン：4.91.1）
    - **Caddy Webサーバー**: リバースプロキシとして機能
    ## アクセス管理
    - **IAMロール・ポリシー**: 以下の複数のポリシーを含むIAMロール
      - 基本ポリシー（EKS、EC2、CloudFormation、KMSなどへのアクセス）
      - EC2専用ポリシー
      - IAM管理ポリシー
      - ラボ機能用ポリシー（DynamoDB、RDS、Lambda、S3など）
      - SSMポリシー

    ## ユーザーアクセス
    - **CloudFront配布**: インスタンスへのアクセスを提供
    - **キャッシュポリシー**: CloudFrontのキャッシュ動作を定義
    - **Secrets Manager**: IDE用のパスワードを安全に保存

    ## 自動セットアップ
    - **Lambda関数**: インスタンスのブートストラップを実行
    - **SSMドキュメント**: 環境セットアップのためのコマンド
    # デプロイフロー
    1. CloudFormationスタックがデプロイされると、VPCとネットワークリソースが作成される
    2. EC2インスタンスがプロビジョニングされ、IAMロールがアタッチされる
    3. Lambdaカスタムリソースがトリガーされ、SSMドキュメントを通じてセットアップスクリプトを実行
    4. スクリプトはGitHubリポジトリ（aws-samples/eks-workshop-v2）からコードをクローンし、Code Serverをインストール
    5. CloudFrontディストリビューションがセットアップされ、ユーザーはウェブブラウザ経由でIDEにアクセス可能になる
    # 出力
    - **IdeUrl**: CloudFrontを通じてIDEにアクセスするためのURL
    - **IdePasswordSecret**: Secrets Managerに保存されたパスワードへのアクセスリンク
    - **IdePasswordSecretName**: パスワードを保存しているSecrets Managerのシークレット名
    - **IdeRole**: 作成されたIAMロールのARN
    ```

2. CloudFormationスタックのデプロイが完了後、IDEにアクセスする
    - ワークショップには5分程度でデプロイ完了と記載されていますが、実際は10分ほどかかりました
    - シークレット(IDEアクセスのパスワード)は、デプロイしたリージョンに作成されているため、シークレットマネージャーの一覧に表示されていない場合はリージョンを確認しましょう
3. eksctlもしくはTerraformを用いてEKSクラスタを作成する
    - AWS CDKは近日公開！となっていましたが、この表記になって1-2か月経っているような気がします
    - 今回は筆者好みでTerraformを使用しています
4. 環境を削除
    - サンプルアプリケーション(現時点未作成)、EKS環境、IDE環境を削除

## アプリケーションを作成(Getting started)
1. 以下[コマンド](https://www.eksworkshop.com/docs/introduction/getting-started/)でIDEの準備を実施
    - このコマンドで「EKS Workshop Git リポジトリを IDE 環境にクローンして、必要な Kubernetes マニフェスト ファイルがファイル システム上に存在するように」するようです
  
    ```shell
    prepare-environment introduction/getting-started
    ```

2. サンプルアプリケーションの構成をチェック
    - [サンプルアプリケーション](https://www.eksworkshop.com/docs/introduction/getting-started/about)
        - 画面構成イメージや、アプリケーションとして公開しているAPIが紹介されています
    - [コンポーネントのパッケージング](https://www.eksworkshop.com/docs/introduction/getting-started/packaging-application)
        - コンポーネントの詳細は解説されていませんが、コンテナイメージの公開元であるECRとDockerファイルが共有されています
    - [Kubernetes上のマイクロサービス](https://www.eksworkshop.com/docs/introduction/getting-started/microservices)
        - このアプリケーションのKubernetsとしての構成を解説しています
        - アプリケーションpodsはDeploymentとしてデプロイされており、スケーリングが可能になっています
        - そのDeploymentへはServiceで接続できるようになっています
        - また、StatefulSetとしてMySQLDBのpodが動作しています
3. まず、カタログコンポーネントを単体でデプロイします

    ```shell
    kubectl apply -k ~/environment/eks-workshop/base-application/catalog
    ```

    - そうすると、以下通りカタログ用のpodとmysql用のpodが作成されます

    ```shell
      ec2-user:~:$ kubectl get pod -n catalog
      NAME                       READY   STATUS    RESTARTS      AGE
      catalog-6d48d97dff-x6njx   1/1     Running   3 (41s ago)   64s
      catalog-mysql-0            1/1     Running   0             64s
      ec2-user:~:$ 
    ```

    - 以下のコマンドでDeploymentとStatefulSetが作成されている事も確認できます

    ```shell
      ec2-user:~:$ kubectl get Deployment -n catalog
      NAME      READY   UP-TO-DATE   AVAILABLE   AGE
      catalog   1/1     1            1           3m36s
      ec2-user:~:$ kubectl get Statefulset -n catalog
      NAME            READY   AGE
      catalog-mysql   1/1     3m47s
      ec2-user:~:$ 
    ```

4. 以下コマンドでカタログ用podを1から3へスケールしてみます
    - scaleするコマンド

    ```shell
      kubectl scale -n catalog --replicas 3 deployment/catalog
    ```

    - scaleを確認するコマンド

    ```shell
      ec2-user:~:$ kubectl get pods -n catalog
      NAME                       READY   STATUS    RESTARTS        AGE
      catalog-6d48d97dff-lsz2b   1/1     Running   0               97s
      catalog-6d48d97dff-shxtf   1/1     Running   0               97s
      catalog-6d48d97dff-x6njx   1/1     Running   3 (5m39s ago)   6m2s
      catalog-mysql-0            1/1     Running   0               6m2s
      ec2-user:~:$ kubectl get deployment -n catalog
      NAME      READY   UP-TO-DATE   AVAILABLE   AGE
      catalog   3/3     3            3           6m14s
      ec2-user:~:$ 
    ```

5. 以下コマンドでアプリケーションのカタログAPIにアクセス

    ```shell
      ec2-user:~:$ kubectl -n catalog exec -it \
      deployment/catalog -- curl catalog.catalog.svc/catalogue | jq .
      [
      {
          "id": "510a0d7e-8e83-4193-b483-e27e09ddc34d",
          "name": "Gentleman",
          "description": "Touch of class for a bargain.",
          "imageUrl": "/assets/gentleman.jpg",
          "price": 795,
          "count": 51,
          "tag": [
          "dress"
          ]
      },
      (略)
      ec2-user:~:$ 
    ```

## 残りのアプリケーションをまとめて作成(Other components)
1. Kustomizeというツールでアプリケーションをデプロイ
    - Kustomizeについては後述します
    - なかなかの量のコンポーネントが作成されていることがわかります
    - ただ、"catalog"リソースに関しては"created"ではなく、"unchanged"となっています
        - これは、前の章にて事前に作成しているため、差異がないことを示しています

    ```shell
    ec2-user:~:$ kubectl apply -k ~/environment/eks-workshop/base-application
    namespace/assets created
    namespace/carts created
    namespace/catalog unchanged
    namespace/checkout created
    namespace/orders created
    namespace/other created
    namespace/rabbitmq created
    namespace/ui created
    serviceaccount/assets created
    serviceaccount/carts created
    serviceaccount/catalog unchanged
    serviceaccount/checkout created
    serviceaccount/orders created
    serviceaccount/rabbitmq created
    serviceaccount/ui created
    role.rbac.authorization.k8s.io/rabbitmq-endpoint-reader created
    rolebinding.rbac.authorization.k8s.io/rabbitmq-endpoint-reader created
    configmap/assets created
    configmap/carts created
    configmap/catalog unchanged
    configmap/checkout created
    configmap/orders created
    configmap/dummy created
    configmap/ui created
    secret/catalog-db unchanged
    secret/orders-db created
    secret/rabbitmq created
    secret/rabbitmq-config created
    service/assets created
    service/carts created
    service/carts-dynamodb created
    service/catalog unchanged
    service/catalog-mysql unchanged
    service/checkout created
    service/checkout-redis created
    service/orders created
    service/orders-mysql created
    service/rabbitmq created
    service/rabbitmq-headless created
    service/ui created
    deployment.apps/assets created
    deployment.apps/carts created
    deployment.apps/carts-dynamodb created
    deployment.apps/catalog configured
    deployment.apps/checkout created
    deployment.apps/checkout-redis created
    deployment.apps/orders created
    deployment.apps/orders-mysql created
    deployment.apps/ui created
    statefulset.apps/catalog-mysql unchanged
    statefulset.apps/rabbitmq created
    ec2-user:~:$ 
    ```

2. 以下コマンドでリソースが作成されていることを確認します
    - namespaceやDeploymentは追加で作成されていますが、StatefulSetはcatalog用のもののみのようです

    ```shell
    ec2-user:~:$ kubectl get namespaces -l app.kubernetes.io/created-by=eks-workshop
    NAME       STATUS   AGE
    assets     Active   2m30s
    carts      Active   2m30s
    catalog    Active   13m
    checkout   Active   2m30s
    orders     Active   2m30s
    other      Active   2m30s
    rabbitmq   Active   2m30s
    ui         Active   2m30s
    ec2-user:~:$ kubectl get deployment -l app.kubernetes.io/created-by=eks-workshop -A
    NAMESPACE   NAME             READY   UP-TO-DATE   AVAILABLE   AGE
    assets      assets           1/1     1            1           2m37s
    carts       carts            1/1     1            1           2m37s
    carts       carts-dynamodb   1/1     1            1           2m37s
    catalog     catalog          1/1     1            1           13m
    checkout    checkout         1/1     1            1           2m37s
    checkout    checkout-redis   1/1     1            1           2m37s
    orders      orders           1/1     1            1           2m37s
    orders      orders-mysql     1/1     1            1           2m37s
    ui          ui               1/1     1            1           2m37s
    ec2-user:~:$ 
    kubectl get statefulset -l app.kubernetes.io/created-by=eks-workshop -A
    ec2-user:~:$ kubectl get statefulset -l app.kubernetes.io/created-by=eks-workshop -A
    NAMESPACE   NAME            READY   AGE
    catalog     catalog-mysql   1/1     14m
    ec2-user:~:$ 
    ```

3. kustomize用のyamlを編集し、pod数をスケールします
    - と言いつつ、スケールされたyamlが事前に用意されています、、

    ```yaml
    ec2-user:~:$ cat ~/environment/eks-workshop/modules/introduction/kustomize/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment　←ここがポイント
    metadata:
      name: checkout  ←ここがポイント
    spec:
      replicas: 3  ←ここがポイント
    ec2-user:~:$   
    ```

    - 以下のコマンドを実行し、スケールします
        - ほとんどが"unchanged"となっていますが、"deployment.apps/checkout"だけが"configured"となっています
        - これは前述のyamlで"ここがポイント"と記載した箇所を見るとわかるように、指定したアプリケーションだけがスケールしていることがわかります

    ```shell
    ec2-user:~:$ kubectl kustomize ~/environment/eks-workshop/modules/introduction/kustomize | kubectl apply -f -
    namespace/checkout unchanged
    serviceaccount/checkout unchanged
    configmap/checkout unchanged
    service/checkout unchanged
    service/checkout-redis unchanged
    deployment.apps/checkout configured
    deployment.apps/checkout-redis unchanged
    ec2-user:~:$ 
    ```

  - 本当にスケールしたかも見ておきましょう

    ```shell
    ec2-user:~:$ kubectl get pods -l app.kubernetes.io/created-by=eks-workshop -A
    NAMESPACE   NAME                              READY   STATUS    RESTARTS      AGE
    assets      assets-69974886bf-9dc9z           1/1     Running   0             13m
    carts       carts-7c95cd549d-9nhzt            1/1     Running   1 (12m ago)   13m
    carts       carts-dynamodb-68bd8f6f6d-mwgdw   1/1     Running   0             13m
    catalog     catalog-6d48d97dff-x6njx          1/1     Running   3 (23m ago)   24m
    catalog     catalog-mysql-0                   1/1     Running   0             24m
    checkout    checkout-7ff8988c8c-fw8cg         1/1     Running   0             4m18s
    checkout    checkout-7ff8988c8c-lrjq8         1/1     Running   0             13m
    checkout    checkout-7ff8988c8c-xbwc4         1/1     Running   0             4m18s
    checkout    checkout-redis-7fb554999b-6mbfh   1/1     Running   0             13m
    orders      orders-79bc47b54d-qd5xm           1/1     Running   2 (12m ago)   13m
    orders      orders-mysql-65bd8b5787-hkk55     1/1     Running   0             13m
    ui          ui-8564fc5cfb-bsp4q               1/1     Running   0             13m
    ec2-user:~:$ 
    ec2-user:~:$ kubectl get deployment -l app.kubernetes.io/created-by=eks-workshop -A
    NAMESPACE   NAME             READY   UP-TO-DATE   AVAILABLE   AGE
    assets      assets           1/1     1            1           12m
    carts       carts            1/1     1            1           12m
    carts       carts-dynamodb   1/1     1            1           12m
    catalog     catalog          1/1     1            1           23m
    checkout    checkout         3/3     3            3           12m
    checkout    checkout-redis   1/1     1            1           12m
    orders      orders           1/1     1            1           12m
    orders      orders-mysql     1/1     1            1           12m
    ui          ui               1/1     1            1           12m
    ec2-user:~:$ 
    ```

## アプリケーションをデプロイする別のパターンhelmを試してみる(Helm)
1. bitnamiのhelmチャートをローカルレポジトリに追加する

    ```shell
    ec2-user:~:$ helm repo add bitnami https://charts.bitnami.com/bitnami
    "bitnami" has been added to your repositories
    ec2-user:~:$ helm repo list
    NAME    URL                               
    bitnami https://charts.bitnami.com/bitnami
    ec2-user:~:$ 
    ```

2. bitnamiでインストールできるnginxの最新バージョンを確認する

    ```shell
    ec2-user:~:$ helm search repo nginx
    NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                                       
    bitnami/nginx                           20.0.0          1.28.0          NGINX Open Source is a web server that can be a...
    bitnami/nginx-ingress-controller        11.6.14         1.12.1          NGINX Ingress Controller is an Ingress controll...
    bitnami/nginx-intel                     2.1.15          0.4.9           DEPRECATED NGINX Open Source for Intel is a lig...
    ec2-user:~:$ 
    ```

3. nginxをhelmでインストールしてみる

    ```shell
    # インストールバージョンが20.0.0の場合
    ec2-user:~:$ helm install nginx bitnami/nginx \
        --version 20.0.0 \
        --namespace nginx --create-namespace --wait
    NAME: nginx
    LAST DEPLOYED: Mon Apr 28 11:57:09 2025
    NAMESPACE: nginx
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    CHART NAME: nginx
    CHART VERSION: 20.0.0
    APP VERSION: 1.28.0
    (略)
    ec2-user:~:$ 
    ```

    - 作成されたpodを確認する

    ```shell
    ec2-user:~:$ kubectl get pod -n nginx
    NAME                     READY   STATUS    RESTARTS   AGE
    nginx-7cb6b49847-6kdpp   1/1     Running   0          2m30s
    ec2-user:~:$ 
    ```

4. podの設定値を変更してみる
    - yamlで変更してみる

    ```shell
    ec2-user:~:$ cat ~/environment/eks-workshop/modules/introduction/helm/values.yaml
    podLabels:
    team: team1
    costCenter: org1

    resources:
    requests:
        cpu: 250m
        memory: 256Mi
    ec2-user:~:$ 
    ec2-user:~:$ helm upgrade --install nginx bitnami/nginx \
        --version 20.0.0 \
        --namespace nginx --create-namespace --wait \
        --values ~/environment/eks-workshop/modules/introduction/helm/values.yaml
    Release "nginx" has been upgraded. Happy Helming!
    NAME: nginx
    LAST DEPLOYED: Mon Apr 28 12:07:00 2025
    NAMESPACE: nginx
    STATUS: deployed
    REVISION: 2
    TEST SUITE: None
    NOTES:
    CHART NAME: nginx
    CHART VERSION: 20.0.0
    APP VERSION: 1.28.0
    (略)
    ec2-user:~:$ 
    ```

    - setコマンドで変更する

    ```shell
    ec2-user:~:$ helm upgrade --install nginx bitnami/nginx \
        --version 20.0.0 \
        --namespace nginx --create-namespace --wait \
        --set replicaCount=3
    Release "nginx" has been upgraded. Happy Helming!
    NAME: nginx
    LAST DEPLOYED: Mon Apr 28 12:14:34 2025
      NAMESPACE: nginx
    STATUS: deployed
    REVISION: 3
    TEST SUITE: None
    NOTES:
    CHART NAME: nginx
    CHART VERSION: 20.0.0
    APP VERSION: 1.28.0
    (略)
    ec2-user:~:$ 
    ```

5. helmの変更履歴を確認する
    - 今回helmで2回変更したため、リビジョンが3までできています
        - 1回目は初回のインストール

    ```shell
    ec2-user:~:$ helm history nginx -n nginx
    REVISION        UPDATED                         STATUS          CHART           APP VERSION     DESCRIPTION     
    1               Mon Apr 28 11:57:09 2025        superseded      nginx-20.0.0    1.28.0          Install complete
    2               Mon Apr 28 12:07:00 2025        superseded      nginx-20.0.0    1.28.0          Upgrade complete
    3               Mon Apr 28 12:14:34 2025        deployed        nginx-20.0.0    1.28.0          Upgrade complete
    ec2-user:~:$ 
    ```

6. 以下コマンドでnginxリソースを削除する

    ```shell
    ec2-user:~:$ helm uninstall nginx --namespace nginx --wait
    release "nginx" uninstalled
    ec2-user:~:$ 
    ```

## リソースを全削除
1. アプリケーションを削除

    ```shell
    delete-environment
    ```

2. EKSクラスタを削除

    ```shell
    cd ~/environment/terraform
    terraform destroy -var="cluster_name=$EKS_CLUSTER_NAME" -auto-approve
    ```

3. IDEを削除(マネージメントコンソールで削除も可能)
    ※これはIDEで実行不可、CloudShellで実行すること

    ```shell
    aws cloudformation delete-stack --stack-name eks-workshop-ide
    ```

# 解説
## EKSクラスタを作成するTerraform
- Terraformの設定ファイル(.tfファイル)は以下5つで構成されています
    - provider.tf
        - awsプロバイダーの設定と、terraformの設定がされています
    - main.tf
        - 共通となるタグを設定します
    - vpc.tf
        - VPCを作成するためのモジュールを用いて、Private Subnetを3つ、Public Subnetを3つ作成します
        - また、IGW、NatGWも作成されます
    - eks.tf
        - EKSを作成するためのモジュールを用いて、EKSクラスタを1つ作成します
        - Addonはvpc-cniのみ
        - node_groupはm5.largeで、min3台、max6台、希望3台で設定
    - variable.tf
        - 以下が設定されています
            - cluster_name
                - デフォルトeks-workshopが設定されている
            - cluster_version
                - デフォルト1.31が設定されている
            - ami_release_version
                - デフォルト1.31.3-20250103が設定されている
            - vpc_cidr
                - デフォルト10.42.0.0/16が設定されている
            - remote_network_cidr
            - remote_pod_cidr
## Kustomize
- yamlをたくさん書くのがつらい、共通化したい
- そんなときに、共通部分をまとめて記載し、差分だけ環境やリソース毎に書き換えることでまとめて記載ができるようになるツールです
- [kubernetesのkustomizeに入門する](https://qiita.com/oguogura/items/af3860ca32cd0264ca93)
## helm
- オープンソースのパッケージマネージャで、Kubernetes 用のソフトウェアのデプロイをシンプルかつ一貫性のある方法で自動化します
- また、Kubernetes 用のアプリケーションを簡単にパッケージ化して、簡単なコマンドでデプロイできます
- サードパーティであるため、Kubernetes 自体に組み込まれていませんが、Kubernetes を操作する際に利用できる非常に便利なツールの1つです
- [Kubernetes における Helm とは？概要とよく使うコマンドを解説](https://sysdig.jp/learn-cloud-native/what-is-helm-in-kubernetes/)

# おわりに
- この章はIntroductionということでそれほど学びがあるものではないかもしれません
- ただし、VPC/EKSのモジュールの使い方、Kustomize、helmを学ぶことはできました
- さて、この章を基本として、ここからEKS Workshopが開始します

# 参考
- [EKS Workshop Introduction](https://www.eksworkshop.com/docs/introduction/)
- [kubernetesのkustomizeに入門する](https://qiita.com/oguogura/items/af3860ca32cd0264ca93)
- [Kubernetes における Helm とは？概要とよく使うコマンドを解説](https://sysdig.jp/learn-cloud-native/what-is-helm-in-kubernetes/)
- [terraform-aws-modules/vpc module](https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest)
- [terraform-aws-modules/eks module](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest)