ローカルでEKSクラスターを立てる

# はじめに

OpenStandiaアドベントカレンダー、22日目になります。今年も去年に引き続き、LocalStackに焦点を当てています。最近、Kubernetesに触れる機会が増え、LocalStackを使用してEKSを構築できないか考えていました。今回はその手順をご紹介します。


:::note 
過去のLocalStackに関する記事はこちらです。
[AWS CDK + LocalStackでAWSサーバーレス環境をローカルに構築してみた](https://qiita.com/yasomaru/items/0ffeba00feb495ee38dd)
[LocalStackを使ってローカル環境でS3を立ち上げ、Lambdaでファイルをアップロードしてみた
](https://qiita.com/yasomaru/items/fa708a1f21a79e637868)

:::


# LocalStack

LocalStackはAWSのクラウド環境を模倣したローカル環境を提供し、開発およびテストを行う際にAWSのサービスを利用できるようにします。
pipやdockerを用いて簡単に環境構築が可能です。

https://www.localstack.cloud/


:::note info
LocalStackの環境構築については、[公式ドキュメント](https://docs.localstack.cloud/getting-started/installation/#installation)を参照してください。
:::

## LocalStack起動

今回はEKSを起動するので、有料版のLocalStackが必要になります。
無料と有料版で使用できるサービスの違いは以下になります。

|  無料  |  有料  |
| ---- | ---- |
|<top> ACM <br> API Gateway <br> CloudFormation <br> CloudWatch <br>  CloudWatchLogs <br>  DynamoDB <br> DynamoDB Streams <br> EC2 <br> Elasticsearch Service <br> EventBridge <br>  Firehose <br> IAM  <br>  Kinesis <br> KMS <br> Lambda <br> Redshift <br> Route53 <br> S3 <br> SecretsManager <br> SES <br> StepFunctions  |  Amplify <br> AppConfig <br> Application AutoScaling  <br>  AppSync <br> Athena <br> Backup <br> Batch <br> CloudFront<br> CloudTrail <br> CodeCommit <br> Cognito Identity <br> ECR <br> ECS <br> EKS <br> ElastiCache <br> EFS <br> EMR  <br> Managed Streaming for Kafka (MSK) <br> Neptune DB <br> QLDB <br> RDS / Aurora Serverless <br>


詳細や料金については以下のページを参照してください。

https://docs.localstack.cloud/user-guide/aws/feature-coverage/


https://www.localstack.cloud/pricing



有料版のLocalStackを起動するためには事前にAPI Keyを発行しておきます。
API KEYを環境変数に格納した後、`localstack start`コマンドでLocal Stackを起動します。


```sh

% export LOCALSTACK_API_KEY=<発行したAPI KEY>    
% localstack start

     __                     _______ __             __
    / /   ____  _________ _/ / ___// /_____ ______/ /__
   / /   / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
  / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
 /_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|

 💻 LocalStack CLI 2.2.0

[21:51:29] starting LocalStack in Docker mode 🐳                       localstack.py:409
──────────────────── LocalStack Runtime Log (press CTRL-C to quit) ─────────────────────

LocalStack version: 2.2.1.dev20230817092311
LocalStack Docker container id: 04db20caff3b
                .
                .
                .
                
```


# EKSの起動

さっそく、[公式ドキュメント](https://docs.localstack.cloud/user-guide/aws/elastic-kubernetes-service/)に沿って、EKSを起動します。
CreateCluster APIを使用して新しいクラスタを作成するために以下のコマンドを実行します。

```sh
awslocal eks create-cluster \
  --name cluster1 \
  --role-arn "arn:aws:iam::000000000000:role/eks-role" \
  --resources-vpc-config "{}"

  {
    "cluster": {
        "name": "cluster1",
{
    "cluster": {
        "name": "cluster1",
        "name": "cluster1",
        "arn": "arn:aws:eks:us-east-1:000000000000:cluster/cluster1",
        "createdAt": "2023-11-14T21:52:17.767000+09:00",
        "version": "1.22",
        "roleArn": "arn:aws:iam::000000000000:role/eks-role",
        "resourcesVpcConfig": {
            "securityGroupIds": [],
            "endpointPublicAccess": true,
            "endpointPrivateAccess": false,
            "publicAccessCidrs": [
                "0.0.0.0/0"
            ]
        },
        "identity": {
            "oidc": {
                "issuer": "https://localhost.localstack.cloud/eks-oidc"
            }
        },
        "status": "CREATING",
        "platformVersion": "eks.5",
        "tags": {}
    }
}
...skipping...
{
    "cluster": {
        "name": "cluster1",
        "arn": "arn:aws:eks:us-east-1:000000000000:cluster/cluster1",
        "createdAt": "2023-11-14T21:52:17.767000+09:00",
        "version": "1.22",
        "roleArn": "arn:aws:iam::000000000000:role/eks-role",
        "resourcesVp
```

docker CLI のコマンドでコンテナが作成されたことを確認できます。

```sh
% docker ps
CONTAINER ID   IMAGE                            COMMAND                   CREATED          STATUS                PORTS                                                                                     NAMES
00a92a10d55b   ghcr.io/k3d-io/k3d-tools:5.4.9   "/app/k3d-tools noop"     10 seconds ago   Up 9 seconds                                                                                                        k3d-cluster1-tools
909c1c06a7e9   rancher/k3s:v1.22.6-k3s1         "/bin/k3d-entrypoint…"   10 seconds ago   Up 7 seconds   
```

クラスターの作成後、以下のコマンドで作成したクラスターの詳細を確認することができます。

```sh
% awslocal eks describe-cluster --name cluster1
{
    "cluster": {
        "name": "cluster1",
        "arn": "arn:aws:eks:us-east-1:000000000000:cluster/cluster1",
        "createdAt": "2023-11-16T22:03:12.140000+09:00",
        "version": "1.22",
        "endpoint": "https://localhost.localstack.cloud:4510",
        "roleArn": "arn:aws:iam::000000000000:role/eks-role",
        "resourcesVpcConfig": {
            "securityGroupIds": [],
            "endpointPublicAccess": true,
            "endpointPrivateAccess": false,
            "publicAccessCidrs": [
                "0.0.0.0/0"
            ]
        },
        "identity": {
            "oidc": {
                "issuer": "https://localhost.localstack.cloud/eks-oidc"
            }
        },
        "status": "ACTIVE",
        "certificateAuthority": {

```

:::note warn

## kubectlのuse-contextの変更

UpdateKubeconfig API を使用して kubectl をLocalStackのEKSクラスターに設定できます。以下のコマンドを実行してください。

```sh
awslocal eks update-kubeconfig --name cluster1 && \
    kubectl config use-context arn:aws:eks:us-east-1:000000000000:cluster/cluster1
Added new context arn:aws:eks:us-east-1:000000000000:cluster/cluster1 to /Users/syasoda/.kube/config
Switched to context "arn:aws:eks:us-east-1:000000000000:cluster/cluster1".
```

また、以下のコマンドで現在のcontextがどれかを確認することができます。

```sh
kubectl config get-contexts
CURRENT   NAME                                                  CLUSTER                                               AUTHINFO                                              NAMESPACE
          arn:aws:eks:us-east-1:000000000000:cluster/cluster1   arn:aws:eks:us-east-1:000000000000:cluster/cluster1   arn:aws:eks:us-east-1:000000000000:cluster/cluster1
*         docker-desktop                                        docker-desktop                                        docker-desktop
```

:::


## アプリケーションのデプロイ

試しに、[kubernetesの公式](https://kubernetes.io/docs/tutorials/stateless-application/guestbook/)が提供しているサンプルのアプリケーションをデプロイします。


### RedisマスターのDeploymentの作成

GuestBookアプリケーションはRedisサーバとwebサーバに分かれています。
まずはRedisサーバからデプロイします。

```shell
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-deployment.yaml
```
以下のコマンドで作成できているか確認します。

```sh
kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
redis-leader-55b556899d-7f742           1/1     Running   0          6s
```

### Redisマスターの Serviceの作成

次にRedisポッドへのトラフィックをプロキシするサービスを適用します。

```sh
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-service.yaml
```

以下のコマンドで作成できているか確認します。
```sh
kubectl get service
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
redis-leader          ClusterIP   10.108.131.61    <none>        6379/TCP   5s
```

### RedisスレーブのDeploymentの作成

ここまでのRedisleaderは単一のPodですが、数個のRedisレプリカを追加することで、可用性を高めてトラフィック需要に対応することができます。


```sh
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-deployment.yaml
```

以下のコマンドで作成できているか確認します。

```sh
kubectl get pods
NAME                                    READY   STATUS              RESTARTS   AGE
redis-follower-86546888fd-kc8bb         0/1     ContainerCreating   0          6s
redis-follower-86546888fd-mt7gb         0/1     ContainerCreating   0          6s
```

### RedisスレーブのServiceの作成

アプリケーションがデータを読み込むためにRedisフォロワーと通信する必要があるため、
Redis followerのServiceを作成します。

```sh
kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-service.yaml

```

以下のコマンドで作成できているか確認します。

```sh
kubectl get service
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
redis-follower        ClusterIP   10.109.131.130   <none>        6379/TCP   8s
redis-leader          ClusterIP   10.108.131.61    <none>        6379/TCP   4m40s
```

### フロントエンド用のDeploymentの作成

次にGuestBookアプリケーションのWebサーバーを起動します。


フロントエンド用のDeploymentを作成します。

```sh
kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
```

以下のコマンドで3つのフロントエンドのレプリカが実行中になっていることを確認します。

```sh
kubectl get pods -l app=guestbook -l tier=frontend
NAME                       READY   STATUS    RESTARTS   AGE
frontend-d6778b77f-hl7r4   1/1     Running   0          18s
frontend-d6778b77f-k4f2r   1/1     Running   0          18s
frontend-d6778b77f-kz8kw   1/1     Running   0          18s
```


### フロントエンド用のServiceの作成

次にフロントエンド用のServiceを作成します。

```sh
kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
```

以下のコマンドで作成できているか確認します。

```sh
kubectl get service
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
frontend              ClusterIP   10.111.170.145   <none>        80/TCP     7s
redis-follower        ClusterIP   10.109.131.130   <none>        6379/TCP   20m
redis-leader          ClusterIP   10.108.131.61    <none>        6379/TCP   25m
```

アプリケーションにアクセスできるようにするために、`kubectl port-forwardを使用してサービスにアクセスできます。
以下のコマンドを実行して、ローカルマシンのポート8080をサービスのポート80に転送します。

```sh
kubectl port-forward svc/frontend 8080:80
```

`http://localhost:8080/`にアクセスすると、GuestBookアプリケーションが表示されます。

![スクリーンショット 2023-12-13 15.36.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2004450/6bad7282-1f1a-acdc-dad0-f954b69c394c.png)


## 構築環境の削除

削除は以下のコマンドで実行します。

```sh
kubectl delete deployment -l app=redis
kubectl delete service -l app=redis
kubectl delete deployment frontend
kubectl delete service frontend
```


# 最後に

今回、初めて有料版のLocalStackを使用しました。
LocalStack上でEKSクラスタを構築することで、実際のAWS環境を利用することなく、Kubernetesの開発およびテストを行うことができるかなと思いました。
有料版になるとEKS以外のサービスも使えるようになるので、色々他のサービスも検証したいなと思います。