AWS EKS 〜 demo

#1.CLOUD9 環境の作成#

##1-1.CLOUD9 環境の作成##

本ハンズオンは、全て、「バージニア北部」で実行していきます。

「Cloud9」を開き、次の要件で作成していきます。

**Step 1　Name environment**

Name：任意の名前を指定。

**Step 2　Configure settings**

基本は次の通り、全てデフォルトです。

Environment type:Create a new EC2 instance for environment (direct access)
Instance type:t2.micro (1 GiB RAM + 1 vCPU)
Platform:Amazon Linux
Network (VPC):任意の VPC を指定して、Publicサブネットを指定してください。

**Step 3　Review**

「Create environment」ボタンをクリック。

##1-2.IAM ロールの作成とEC2への割り当て##

以下のIAMポリシーを付与したIAMロールを作成し、EC2にアタッチします。

`AdministratorAccess`

##1-3.一時的なクレデンシャルの無効化##

Cloud9では、IAMユーザーの一時的なクレデンシャルを自動的に設定する機能がありますが、
この一時的なクレデンシャルは IAMなどの一部のアクションに制限があるため、
この一時的なクレデンシャルは無効化して EC2インスタンスに割り当てたIAMロールが使用されるようにします。

右上の歯車の形のアイコンを開き、「AWS Settings」メニューを開き、「AWS managed temporary credentials」を無効化します。
![スクリーンショット 2020-10-26 21.46.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/e0f7647f-792b-e959-c067-d872969b85e5.png)

##1-4.AWS CLI の初期設定##

```
leomaro7:~/environment $ rm -vf ${HOME}/.aws/credentials
leomaro7:~/environment $ aws --version
aws-cli/1.18.162 Python/3.6.12 Linux/4.14.193-113.317.amzn1.x86_64 botocore/1.19.2
leomaro7:~/environment $ AWS_REGION="us-east-1"
leomaro7:~/environment $ aws configure set default.region ${AWS_REGION}
leomaro7:~/environment $ aws configure get default.region
us-east-1
leomaro7:~/environment $ aws sts get-caller-identity
{
    "UserId": "",
    "Account": "",
    "Arn": ""
}
```

#2.クラスターの作成#

##2-1.eksctlのインストール##

eksctl：Kubernetes クラスター自体の作成に使用するコマンド

```
leomaro7:~/environment $ curl -L "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
leomaro7:~/environment $ sudo mv /tmp/eksctl /usr/local/bin
leomaro7:~/environment $ eksctl version
0.30.0
```

[eksctl - The official CLI for Amazon EKS](https://eksctl.io/)

##2-2.kubectlのインストール##

kubectl:作成した Kubernetes クラスターの操作に使用するコマンド

```
leomaro7:~/environment $ sudo curl -L -o /usr/local/bin/kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.17.7/2020-07-08/bin/linux/amd64/kubectl
leomaro7:~/environment $ sudo chmod +x /usr/local/bin/kubectl
leomaro7:~/environment $ kubectl version --short --client
Client Version: v1.17.7-eks-bffbac
```

##2-3.クラスターの作成##

```
AWS_REGION=$(aws configure get default.region)
eksctl create cluster \
  --name=ekshandson \
  --version 1.17 \
  --nodes=3 --managed \
  --region ${AWS_REGION} --zones ${AWS_REGION}a,${AWS_REGION}c
```

- 設定オプションを引数で渡す方法以外にも、設定を YAML に記述し、引数でその YAML 指定することも可能。
- 既存の VPC を指定してクラスターを作成することも可能。今回は 新規に VPC が作成される。
- eksctl は CloudFormation を使って コントロールプレーンのVPC や EKS クラスター、ワーカーノードの Auto Scaling Group などの AWS リソースが作成される。（実際にCloudFormationを開き、作成されたリソースを確認してみるのがよいと思われる。）

※クラスターに失敗する場合のトラブルシュート

- Availability Zone のリソース不足
- AWS CLI のバージョンが古い
- IAMロールが割り当てられていない

##2-4.便利なツールの導入と、コマンド補完の設定##

###jq と bash-completion###

jq：json データを処理する便利なコマンド
bash-completion：bash シェル上でコマンドの補完をしてくれる

```
sudo yum -y install jq bash-completion
```

###docker、docker-compose と コマンド補完###

```
sudo curl -L -o /etc/bash_completion.d/docker https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker
sudo curl -L -o /usr/local/bin/docker-compose "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)"
sudo chmod +x /usr/local/bin/docker-compose
sudo curl -L -o /etc/bash_completion.d/docker-compose https://raw.githubusercontent.com/docker/compose/1.26.2/contrib/completion/bash/docker-compose
```

###kubectl のコマンド補完###

```
kubectl completion bash > kubectl_completion
sudo mv kubectl_completion /etc/bash_completion.d/kubectl
```

###eksctl のコマンド補完###

```
eksctl completion bash > eksctl_completion
sudo mv eksctl_completion /etc/bash_completion.d/eksctl
```

###k（エイリアス）###

```
cat <<"EOT" >> ${HOME}/.bash_profile

alias k="kubectl"
complete -o default -F __start_kubectl k
EOT
```

###kube-ps1###

kube-ps1:現在の kubectl のコンテキストや Namespace をプロンプトに表示してくれる。

```
git clone https://github.com/jonmosco/kube-ps1.git ~/.kube-ps1
cat <<"EOT" >> ~/.bash_profile

source ~/.kube-ps1/kube-ps1.sh
function get_cluster_short() {
  echo "$1" | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT
```

###kubectx / kubens###

kubectx と kubens：kubectx と kubens　kubectl のコンテキストや Namespace の切替を容易にしてくれる

```
git clone https://github.com/ahmetb/kubectx.git ~/.kubectx
sudo ln -sf ~/.kubectx/completion/kubens.bash /etc/bash_completion.d/kubens
sudo ln -sf ~/.kubectx/completion/kubectx.bash /etc/bash_completion.d/kubectx
cat <<"EOT" >> ~/.bash_profile

export PATH=~/.kubectx:$PATH
EOT
```

###stern###

stern:コンテナのログを確認する

```
sudo curl -L -o /usr/local/bin/stern https://github.com/wercker/stern/releases/download/1.11.0/stern_linux_amd64
sudo chmod +x /usr/local/bin/stern
```

~/.bash_profile に追記した設定を有効化するために、現在のターミナルのタブを閉じて新しいターミナルのタブを開く。

#3.クラスターの確認#

##3-1.クラスターの確認##

現在存在するクラスターとクラスターの基本情報を表示。

```
leomaro7:~/environment $ eksctl get cluster
NAME            REGION
ekshandson      us-east-1

leomaro7:~/environment $ kubectl cluster-info
Kubernetes master is running at https://25FF2316ECD9ED0E8D621ED7DCFD6263.gr7.us-east-1.eks.amazonaws.com
CoreDNS is running at https://25FF2316ECD9ED0E8D621ED7DCFD6263.gr7.us-east-1.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```

##3-2.ノードの確認##

クラスターに所属しているノードとノードのキャパシティや稼働しているPodを確認。

```
leomaro7:~/environment $ kubectl get node
NAME                            STATUS   ROLES    AGE   VERSION
ip-192-168-4-184.ec2.internal   Ready    <none>   13m   v1.17.11-eks-cfdc40
ip-192-168-56-48.ec2.internal   Ready    <none>   13m   v1.17.11-eks-cfdc40
ip-192-168-6-63.ec2.internal    Ready    <none>   13m   v1.17.11-eks-cfdc40

leomaro7:~/environment $ kubectl describe node　ip-192-168-4-184.ec2.internal
```

##3-3.NAMESPACEの確認##

Namespace:Pod や Service といった Kubernetes リソースをグルーピングするもの。

Namespace を確認

```
leomaro7:~/environment $ kubectl get namespace
NAME              STATUS   AGE
default           Active   24m
kube-node-lease   Active   24m
kube-public       Active   24m
kube-system       Active   24m
```

Pod:Kubernetes におけるデプロイの最小単位で、Pod の中では 1 つ以上のコンテナが稼働する。

↓はnamespace：defaultの中身をのPod（勿論まだない。）

```
leomaro7:~/environment $ kubectl get pod -n default
No resources found in default namespace.
```

kubensを利用して、デフォルトのNamespaceをkube-system に変更。

```
leomaro7:~/environment $ kubens kube-system
Context "i-0fa58438b91ce5f53@ekshandson.us-east-1.eksctl.io" modified.
Active namespace is "kube-system".
```

kube-systemでは、↓のようなPodが動作している。

```
leomaro7:~/environment $ kubectl get pod -n kube-system
NAME                       READY   STATUS    RESTARTS   AGE
aws-node-2p7s2             1/1     Running   0          26m
aws-node-cmmkc             1/1     Running   0          26m
aws-node-vbp6f             1/1     Running   0          26m
coredns-75b44cb5b4-cktx9   1/1     Running   0          31m
coredns-75b44cb5b4-lq58q   1/1     Running   0          31m
kube-proxy-c6td9           1/1     Running   0          26m
kube-proxy-jxwc6           1/1     Running   0          26m
```

全ての Namespace を対象として情報を取得する場青は -Aオプション を使う。

```
leomaro7:~/environment $ kubectl get pod -A
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   aws-node-2p7s2             1/1     Running   0          27m
kube-system   aws-node-cmmkc             1/1     Running   0          27m
kube-system   aws-node-vbp6f             1/1     Running   0          27m
kube-system   coredns-75b44cb5b4-cktx9   1/1     Running   0          32m
kube-system   coredns-75b44cb5b4-lq58q   1/1     Running   0          32m
kube-system   kube-proxy-c6td9           1/1     Running   0          27m
kube-system   kube-proxy-jxwc6           1/1     Running   0          27m
kube-system   kube-proxy-z454c           1/1     Running   0          27m
```

#4.サンプルアプリケーションのデプロイ#

##4-1.DynamoDB テーブルの作成##

```
aws dynamodb create-table --table-name 'messages' \
  --attribute-definitions '[{"AttributeName":"uuid","AttributeType": "S"}]' \
  --key-schema '[{"AttributeName":"uuid","KeyType": "HASH"}]' \
  --provisioned-throughput '{"ReadCapacityUnits": 1,"WriteCapacityUnits": 1}'
```

##4-2.Dockerイメージの作成##

ディレクトリを移動している場合は、~/environment/ ディレクトリに戻っておく。

```
cd ~/environment/
```

サンプルアプリケーションをDLし、解凍。

```
wget https://eks-for-aws-summit-online.workshop.aws/sample-app.zip
unzip sample-app.zip
```

docker-compose で ビルド。

```
cd sample-app
docker-compose build
```

ビルドされたことを確認する。

```
docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
backend             latest              412ec271d5e7        11 seconds ago      107MB
frontend            latest              fa4eba7cd29c        20 seconds ago      57.7MB
python              3-alpine            dc68588b1801        6 days ago          44.3MB
```

##4-3.ECR へのイメージの登録##

ECRリポジトリを作成。

```
aws ecr create-repository --repository-name frontend
aws ecr create-repository --repository-name backend
```

リポジトリの URL を取得して変数に格納しておく。

```
frontend_repo=$(aws ecr describe-repositories --repository-names frontend --query 'repositories[0].repositoryUri' --output text)
backend_repo=$(aws ecr describe-repositories --repository-names backend --query 'repositories[0].repositoryUri' --output text)
```

先ほどビルドしたイメージに、ECR のリポジトリの URL 名で別名をつけます。

```
docker tag frontend:latest ${frontend_repo}:latest
docker tag backend:latest ${backend_repo}:latest
```

別名をつけたイメージを確認。

```
docker images
REPOSITORY                                              TAG                 IMAGE ID            CREATED             SIZE
4.dkr.ecr.us-east-1.amazonaws.com/backend    latest              412ec271d5e7        2 minutes ago       107MB
4.dkr.ecr.us-east-1.amazonaws.com/frontend   latest              fa4eba7cd29c        2 minutes ago       57.7MB
```

ECR にログイン。

```
ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
AWS_REGION=$(aws configure get default.region)
aws ecr get-login-password | docker login --username AWS --password-stdin https://${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
```

イメージを ECR に push。

```
docker push ${frontend_repo}:latest
docker push ${backend_repo}:latest
```

##4.アプリのデプロイ##

作業用ディレクトリの作成。

```
mkdir -p ~/environment/manifests/
cd ~/environment/manifests/
```

アプリケーション1用の Namespace を作成。デフォルトの Namespace も変更。

```
kubectl create namespace frontend
kubens frontend
```

Deployment の作成

```yaml:
frontend_repo=$(aws ecr describe-repositories --repository-names frontend --query 'repositories[0].repositoryUri' --output text)
cat <<EOF > frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: frontend
  replicas: 2
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: ${frontend_repo}:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        env:
        - name: BACKEND_URL
          value: http://backend.backend:5000/messages
EOF
```

```
kubectl apply -f frontend-deployment.yaml -n frontend
```

作成されたデプロイメントを確認。

※Deployment リソースを作成したことで、Deployment リソースの定義内容にしたがって、自動的に Pod が作成される。
（正確には、Deployment リソースによって ReplicaSet リソースが作成され、ReplicaSet リソースによって Pod が作成される。）

```
kubectl get deployment -n frontend
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
frontend   2/2     2            2           13s
```

Podを確認。

```
kubectl get pod -n frontend
NAME                        READY   STATUS    RESTARTS   AGE
frontend-84ccd456fb-l6kjl   1/1     Running   0          53s
frontend-84ccd456fb-wdhwr   1/1     Running   0          53s
```

Service の作成。

Service:Deployment によって起動された Pod 群にアクセスするために、名前解決と負荷分散の機能を提供する。

```yaml:
cat <<EOF > frontend-service-lb.yaml
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 5000
EOF
```

```
kubectl apply -f frontend-service-lb.yaml -n frontend
```

作成した Service を確認。

```
kubectl get service -n frontend
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)        AGE
frontend   LoadBalancer   10.100.219.241   dd7c90ab25e44a939b065e566aa5432-1872056256.us-east-1.elb.amazonaws.com   80:30374/TCP   10s
```

EXTERNAL-IP にアクセスして表示されるか確認する。（名前解決ができるまで数分かかる）

アプリケーション2用の Namespace を作成。デフォルトの Namespace も変更。

```
kubectl create namespace backend
kubens backend
```

Deployment の作成。

```
AWS_REGION=$(aws configure get default.region)
backend_repo=$(aws ecr describe-repositories --repository-names backend --query 'repositories[0].repositoryUri' --output text)
cat <<EOF > backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  selector:
    matchLabels:
      app: backend
  replicas: 2
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: ${backend_repo}:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        env:
        - name: AWS_DEFAULT_REGION
          value: ${AWS_REGION}
        - name: DYNAMODB_TABLE_NAME
          value: messages
EOF
```

```
kubectl apply -f backend-deployment.yaml -n backend
```

Podを確認。

```
kubectl get pod -n backend
NAME                       READY   STATUS    RESTARTS   AGE
backend-7544ddcd98-7lxcx   1/1     Running   0          12s
backend-7544ddcd98-bn5jq   1/1     Running   0          12s
```

Service の作成。

```
cat <<EOF > backend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
  - protocol: TCP
    port: 5000
    targetPort: 5000
EOF
```

```
kubectl apply -f backend-service.yaml -n backend
```

作成した Service を確認。

```
kubectl get service -n backend
NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
backend   ClusterIP   10.100.60.141   <none>        5000/TCP   13s
```

EXTERNAL-IP にもう一度アクセスしてみる。

#5.IAM ROLES FOR SERVICE ACCOUNTS#

IAM Roles for Service Accounts と呼ばれる EKS の機能を使用して アプリケーション2の Pod に IAM ロールを付与し、DynamoDB へのアクセスを許可させる。

OIDC ID プロバイダーを作成し、クラスターに関連付けます。

```
eksctl utils associate-iam-oidc-provider \
    --cluster ekshandson \
    --approve
```

DynamoDB の messages テーブルへのフルアクセスを許可する IAM ポリシーを作成。

```
cat <<EOF > dynamodb-messages-fullaccess-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ListAndDescribe",
            "Effect": "Allow",
            "Action": [
                "dynamodb:List*",
                "dynamodb:DescribeReservedCapacity*",
                "dynamodb:DescribeLimits",
                "dynamodb:DescribeTimeToLive"
            ],
            "Resource": "*"
        },
        {
            "Sid": "SpecificTable",
            "Effect": "Allow",
            "Action": [
                "dynamodb:BatchGet*",
                "dynamodb:DescribeStream",
                "dynamodb:DescribeTable",
                "dynamodb:Get*",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:BatchWrite*",
                "dynamodb:CreateTable",
                "dynamodb:Delete*",
                "dynamodb:Update*",
                "dynamodb:PutItem"
            ],
            "Resource": "arn:aws:dynamodb:*:*:table/messages"
        }
    ]
}
EOF
```

```
aws iam create-policy \
    --policy-name dynamodb-messages-fullaccess \
    --policy-document file://dynamodb-messages-fullaccess-policy.json
```

アプリケーション2の実行に使用するための ServiceAccount と IAM ロールを作成し、関連付ける。

```
ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
eksctl create iamserviceaccount \
    --name dynamodb-messages-fullaccess \
    --namespace backend \
    --cluster ekshandson \
    --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/dynamodb-messages-fullaccess \
    --override-existing-serviceaccounts \
    --approve
```

作成された ServiceAccount を確認。

```
kubectl get serviceaccount -n backend
default                        1         24m
dynamodb-messages-fullaccess   1         35s
```

アプリケーション2の Deployment の定義を修正し、作成した ServiceAccount で Pod を実行させる。

Cloud9 で backend-deployment.yaml をダブルクリックで開き、serviceAccountName の指定を次のとおり追加して保存。

```
    spec:
+     serviceAccountName: dynamodb-messages-fullaccess
      containers:
```

```
kubectl apply -f backend-deployment.yaml -n backend
```

自動的に新しい Pod が起動されるので、確認。

```
kubectl get pod -n backend
NAME                       READY   STATUS    RESTARTS   AGE
backend-647595dd78-jjmml   1/1     Running   0          70s
backend-647595dd78-w7f6t   1/1     Running   0          72s
```

EXTERNAL-IP にもう一度アクセスしてみると正しく表示される、はず。