Amazon EKSをVPC内部通信のみで利用してみる

本記事は個人の意見であり、所属する組織の見解とは関係ありません。


こちらはAmazon EKS Advent Calendar 2019の18日目の記事です。現在の時刻は、12/18 190時ぐらいでしょうか


re:Inventでの EKS on Fargete 発表もあり、改めてEKSに注目されている方も多くいらっしゃると思います。re:Inventで発表された内容そのものではありませんが、今回はVPC内部の通信のみでEKSを利用できるかを手順のウォークスルーを通じて確認していきます。


# 確認結果
12月24日現在の動作として次の様な挙動をします。

* Worker Node としてEC2 インスタンスを利用している場合、 VPC 内部通信のみで利用可能
* AWWS Fargete を利用している場合、インターネット通信が必要である。

結果としては、EKS を VPC 内部通信のみで利用する事が可能です。もちろん、利用している add on 等のツールがインターネット通信を必要とするケースもあると思います。実際の利用においては、手持ちの環境で事前検証をする事をおすすめします。 Fargateを利用する場合は、現時点ではインターネット通信が必要ですが、これは実際の動作から確認したものであるという点にご留意ください。


# 検証の流れ
インターネット通信を遮断した状況でのセットアップは地味に手間がかかりますね。今回は Cloud9 + VPC Peering を利用してみました。EKS 用のVPCに作成されたリソースからは直接のインターネットアクセスは出来ません。作業用となるCloud9 インスタンス からはインターネットにアクセス出来ます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63007/e2d9307e-3bff-7a03-475f-0f32f1760ec5.png)


EKS clusterを作成する VPC には4つの Subnet があり、そのうち3つは Worker Node を作成する Subnet として利用します。残りの1つは、予備として作成しておきます。これはあとで、インターネット通信が必要になった場合に、NATGW を作成する為の Subnet として利用します。(結果的にこのsubnetを利用する事になりました。)


VPC内部通信に制限する為に、VPC endpoint 活用していきます。利用するサービス、環境に応じて、適宜アレンジしてみてください。ユースケースを妄想しながら、この辺りは使うかな？と以下の様な環境で構成してみました。

* Amazon ECR VPCエンドポイント(ecr.apiとecr.dkrの2種類)
* Amazon S3 VPCエンドポイント
* Amazon EC2 VPCエンドポイント
* Amazon Cloudwatch logs VPCエンドポイント

現時点では、EKS のAPI は VPC endpoint をサポートしていませんので、EKS 自身の管理は public な endpoint 経由で実施する必要があります。

# 環境セットアップ

Cloud9 インスタンスで必要なツール類(aws cli、kubectl、eksctl)のセットアップ、EC2 インスタンスへのIAM ロール設定を済ませたらいざセットアップの開始です。
(Cloud9を利用する場合のセットアップ内容は eks workshopの手順が参考になります。)

まず最初にEKS clusterを作成する際に利用するIAM認証情報を確認しておきます。ここが想定と異なる場合、設定を見直しましょう

```
$ aws sts get-caller-identity
{
    "Account": "123456789012",
    "UserId": "AROA5O666JYXWZ5UOGYY7:i-xxxxxxxxxxxxxxxxx", 
    "Arn": "arn:aws:sts::123456789012:assumed-role/eks-admin/i-xxxxxxxxxxxxxxxxx"
}
```

続いてeksctlで既存VPC上に EKS cluster を作成します。

```
$ cat eks-cluster.yml 
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: eks-cluster-private
  region: us-east-2
vpc:
  clusterEndpoints:
    publicAccess: false
    privateAccess: true
  id: vpc-038949360da1e5281
  cidr: 172.16.0.0/16
  subnets:
    private:
      us-east-2a: { id: subnet-0b369e89278e184bc }
      us-east-2b: { id: subnet-0566c87640b758942 }
      us-east-2c: { id: subnet-0c7c9a28ec7c595bd }

nodeGroups:
  - name: private-standard-group
    instanceType: t3.medium
    desiredCapacity: 3
    maxSize: 3
    minSize: 3
    privateNetworking: true
    
    
cloudWatch:
  clusterLogging:
    enableTypes:
      - api
      - audit
      - authenticator
      - controllerManager
      - scheduler

$ eksctl create cluster -f eks-cluster.yml [ℹ]  eksctl version 0.11.1
[ℹ]  using region us-east-2
[✖]  warning, having public access disallowed will subsequently interfere with some features of eksctl. This will require running subsequent eksctl (and Kubernetes) commands/API calls from within the VPC.  Running these in the VPC requires making updates to some AWS resources.  See: https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html#private-access for more details

```

いきなりつまづきました。eksctl で cluster を作成する場合に、`public access`を`disable`とした場合は、clusterの作成が行えない様です。aws cli での作成に切り替えます。

```
$ aws eks --region us-east-2 create-cluster --name privateEKS \
--kubernetes-version 1.14 \
--role-arn arn:aws:iam::123456789012:role/eksServiceRole \
--resources-vpc-config subnetIds=subnet-0b369e89278e184bc,\
subnet-0566c87640b758942,subnet-0c7c9a28ec7c595bd,\
securityGroupIds=sg-0b6ecda1c7a802b79,\
endpointPublicAccess=false,endpointPrivateAccess=true

```

Clusterの作成が完了するまで少し時間がかかりますので、その間に ECR repository の作成と、適当な Docker イメージを push しておきます。

```
$ aws ecr --region us-east-2 create-repository --repository-name private4eks
$ $(aws ecr get-login --no-include-email --regio us-east-2)

$ docker tag private4eks:01 123456789012.dkr.ecr.us-east-2.amazonaws.com/private4eks:01
$ docker push  123456789012.dkr.ecr.us-east-2.amazonaws.com/private4eks:01
```


# Worker Node にEC2 インスタンスを利用する場合
最初に Managed Node Group を作成して、Worker Node のセットアップを行います。 Managed Node Group は、11月にリリースされたアップデートで、こちらを使うと Worker Node をいい感じに構成できます。なお、 Worker Node で利用するIAM ロールを指定していますが、これは事前に作成しておく必要があります。

```
aws eks create-nodegroup --cluster-name privateEKS \
--nodegroup-name privateGroup \
--subnets subnet-0b369e89278e184bc,subnet-0566c87640b758942,subnet-0c7c9a28ec7c595bd \
--node-role arn:aws:iam::123456789012:role/eks-nodegroup-ng-NodeInstanceRole \
--region us-east-2
```

Worker Node が構成され、cluster に Join している事を確認します。

```
$ kubectl get node                                                   
NAME                                                  STATUS   ROLES    AGE     VERSION
ip-172-16-4-166.us-east-2.compute.internal            Ready    <none>   2m25s   v1.14.7-eks-1861c5
ip-172-16-40-206.us-east-2.compute.internal           Ready    <none>   2m41s   v1.14.7-eks-1861c5
```

これで準備が整いました。先ほど push した docker イメージを利用して pod を作成してみます。

```
$ kubectl create deployment ec2-app --image 123456789012.dkr.ecr.us-east-2.amazonaws.com/private4eks:01

deployment.apps/ec2-app created


$ kubectl get deployments
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
ec2-app   1/1     1            1           51s
```

Fargate での動作を確認する前に、kube-system で動いている CoreDNS の状況を確認しておきます。Managed Workger Group 経由で起動したEC2 インスタンス上で起動されていることがわかります。

```
$ kubectl get po,node -o wide --namespace kube-system
NAME                           READY   STATUS    RESTARTS   AGE   IP              NODE                                          NOMINATED NODE   READINESS GATES
pod/aws-node-v9w2h             1/1     Running   0          36m   172.16.4.166    ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>
pod/aws-node-x72b7             1/1     Running   0          36m   172.16.40.206   ip-172-16-40-206.us-east-2.compute.internal   <none>           <none>
pod/coredns-6ffc87dccf-6nf22   1/1     Running   0          44m   172.16.7.56     ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>
pod/coredns-6ffc87dccf-hr8hp   1/1     Running   0          44m   172.16.5.204    ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>
pod/kube-proxy-2z775           1/1     Running   0          36m   172.16.40.206   ip-172-16-40-206.us-east-2.compute.internal   <none>           <none>
pod/kube-proxy-hrd9c           1/1     Running   0          36m   172.16.4.166    ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>

NAME                                               STATUS   ROLES    AGE   VERSION              INTERNAL-IP     EXTERNAL-IP    OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
node/ip-172-16-4-166.us-east-2.compute.internal    Ready    <none>   36m   v1.14.7-eks-1861c5   172.16.4.166    52.15.125.83   Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
node/ip-172-16-40-206.us-east-2.compute.internal   Ready    <none>   36m   v1.14.7-eks-1861c5   172.16.40.206   3.14.3.148     Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
```


### Fargateを利用する場合
Fargate を利用する場合には、Worker Node 向けの設定として profile を作成します。 profile の作成はEKSの機能ですので、aws cli での作成です。 今回は、namespaceに`fargate`が指定された場合に、Pod が Fargate 上で起動します。

```
aws eks create-fargate-profile --fargate-profile-name private-fargate --cluster-name privateEKS --region us-east-2 --pod-execution-role-arn arn:aws:iam::123456789012:role/AmazonEKSFargatePodExecutionRole --subnets subnet-0b369e89278e184bc subnet-0566c87640b758942 subnet-0c7c9a28ec7c595bd --selectors namespace=fargate
```

EC2インスタンスを利用した時と同様にPodを起動します。今回はnamespaceとしてfargateを指定しています。

```
$ kubectl create namespace fargate

$ kubectl create deployment fargate-app --image 123456789012.dkr.ecr.us-east-2.amazonaws.com/private4eks:01 --namespace fargate

$ kubectl get po,node -o wide --namespace fargate
NAME                               READY   STATUS    RESTARTS   AGE     IP       NODE     NOMINATED NODE                                READINESS GATES
pod/fargate-app-7fcb75bf84-ghfql   0/1     Pending   0          5m47s   <none>   <none>   6686814949-d8df61853ca64096aabccc25785772b9   <none>

NAME                                               STATUS   ROLES    AGE   VERSION              INTERNAL-IP     EXTERNAL-IP    OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
node/ip-172-16-4-166.us-east-2.compute.internal    Ready    <none>   42m   v1.14.7-eks-1861c5   172.16.4.166    52.15.125.83   Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
node/ip-172-16-40-206.us-east-2.compute.internal   Ready    <none>   42m   v1.14.7-eks-1861c5   172.16.40.206   3.14.3.148     Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
```

残念。PodのステータスがPendingのままです。どうやらこの環境ではFargate上でPodが起動しません。ここで Fargate 向けに指定した Subnet に適用している Route Table を変更して NATGW 経由でインターネット通信が行える様にすると、以下の様になります。無事起動しました。

```
$ kubectl get po,node -o wide --namespace fargate
NAME                               READY   STATUS              RESTARTS   AGE     IP       NODE                                                  NOMINATED NODE   READINESS GATES
pod/fargate-app-7fcb75bf84-ghfql   0/1     ContainerCreating   0          8m29s   <none>   fargate-ip-172-16-35-143.us-east-2.compute.internal   <none>           <none>

NAME                                                       STATUS   ROLES    AGE   VERSION              INTERNAL-IP     EXTERNAL-IP    OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
node/fargate-ip-172-16-35-143.us-east-2.compute.internal   Ready    <none>   18s   v1.14.8-eks          172.16.35.143   <none>         Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0
node/ip-172-16-4-166.us-east-2.compute.internal            Ready    <none>   45m   v1.14.7-eks-1861c5   172.16.4.166    52.15.125.83   Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
node/ip-172-16-40-206.us-east-2.compute.internal           Ready    <none>   45m   v1.14.7-eks-1861c5   172.16.40.206   3.14.3.148     Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
```

EC2 インスタンスを利用した時と同じ様に CoreDNS が動いている場所を確認してみます。

```
$ kubectl get po,node -o wide --namespace kube-system
NAME                           READY   STATUS    RESTARTS   AGE   IP              NODE                                          NOMINATED NODE   READINESS GATES
pod/aws-node-v9w2h             1/1     Running   0          49m   172.16.4.166    ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>
pod/aws-node-x72b7             1/1     Running   0          49m   172.16.40.206   ip-172-16-40-206.us-east-2.compute.internal   <none>           <none>
pod/coredns-6ffc87dccf-6nf22   1/1     Running   0          57m   172.16.7.56     ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>
pod/coredns-6ffc87dccf-hr8hp   1/1     Running   0          57m   172.16.5.204    ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>
pod/kube-proxy-2z775           1/1     Running   0          49m   172.16.40.206   ip-172-16-40-206.us-east-2.compute.internal   <none>           <none>
pod/kube-proxy-hrd9c           1/1     Running   0          49m   172.16.4.166    ip-172-16-4-166.us-east-2.compute.internal    <none>           <none>

NAME                                                       STATUS   ROLES    AGE     VERSION              INTERNAL-IP     EXTERNAL-IP    OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
node/fargate-ip-172-16-35-143.us-east-2.compute.internal   Ready    <none>   4m52s   v1.14.8-eks          172.16.35.143   <none>         Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0
node/ip-172-16-4-166.us-east-2.compute.internal            Ready    <none>   49m     v1.14.7-eks-1861c5   172.16.4.166    52.15.125.83   Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
node/ip-172-16-40-206.us-east-2.compute.internal           Ready    <none>   49m     v1.14.7-eks-1861c5   172.16.40.206   3.14.3.148     Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
```

CoreDNS は標準では EC2 インスタンス上で起動する様になっています。こちらも Fargate 上で動かしたい場合は、追加の設定が必要になります。詳しくはドキュメントを参考にしてみてください。

```
$ aws eks create-fargate-profile --fargate-profile-name fargate \
--cluster-name privateEKS \
--region us-east-2 \
--pod-execution-role-arn arn:aws:iam::123456789012:role/AmazonEKSFargatePodExecutionRole \
--subnets subnet-0b369e89278e184bc subnet-0566c87640b758942 subnet-0c7c9a28ec7c595bd \
--selectors namespace=kube-system,labels={k8s-app=kube-dns}

$ kubectl get po,node -o wide --namespace kube-systemNAME                           READY   STATUS    RESTARTS   AGE   IP              NODE                                                  NOMINATED NODE   READINESS GATES
pod/aws-node-v9w2h             1/1     Running   0          63m   172.16.4.166    ip-172-16-4-166.us-east-2.compute.internal            <none>           <none>
pod/aws-node-x72b7             1/1     Running   0          63m   172.16.40.206   ip-172-16-40-206.us-east-2.compute.internal           <none>           <none>
pod/coredns-6d59cf6ccd-592rh   0/1     Running   0          62s   172.16.33.134   fargate-ip-172-16-33-134.us-east-2.compute.internal   <none>           <none>
pod/coredns-6d59cf6ccd-v7tjz   0/1     Running   0          62s   172.16.26.225   fargate-ip-172-16-26-225.us-east-2.compute.internal   <none>           <none>
pod/coredns-6ffc87dccf-hr8hp   1/1     Running   0          71m   172.16.5.204    ip-172-16-4-166.us-east-2.compute.internal            <none>           <none>
pod/kube-proxy-2z775           1/1     Running   0          63m   172.16.40.206   ip-172-16-40-206.us-east-2.compute.internal           <none>           <none>
pod/kube-proxy-hrd9c           1/1     Running   0          63m   172.16.4.166    ip-172-16-4-166.us-east-2.compute.internal            <none>           <none>

NAME                                                       STATUS   ROLES    AGE   VERSION              INTERNAL-IP     EXTERNAL-IP    OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
node/fargate-ip-172-16-26-225.us-east-2.compute.internal   Ready    <none>   6s    v1.14.8-eks          172.16.26.225   <none>         Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0
node/fargate-ip-172-16-33-134.us-east-2.compute.internal   Ready    <none>   10s   v1.14.8-eks          172.16.33.134   <none>         Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0
node/fargate-ip-172-16-35-143.us-east-2.compute.internal   Ready    <none>   18m   v1.14.8-eks          172.16.35.143   <none>         Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0
node/ip-172-16-4-166.us-east-2.compute.internal            Ready    <none>   63m   v1.14.7-eks-1861c5   172.16.4.166    52.15.125.83   Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
node/ip-172-16-40-206.us-east-2.compute.internal           Ready    <none>   63m   v1.14.7-eks-1861c5   172.16.40.206   3.14.3.148     Amazon Linux 2   4.14.146-119.123.amzn2.x86_64   docker://18.6.1
````

### 最後に
今回は手順上、省略していますが、aws cliを利用する場合、EKS が利用する IAM ロールを事前に作成したり、 CoreDNS に対するアップデートを個別に実施しました。このあたり、実は eksctl を利用すると 上記の様な設定を自動的にしてくれます。とても便利です。ぜひ eksctl もお試しください！