AWS
kubernetes
eks

AWS初心者だけど、EKSのクラスタを作って、NodePort と ELBアクセス、EBSのPVを確認したよ

自己研鑽で学んだことのメモです。

準備事項

  1. kubectlをインストールする
  2. AWS CLIをインストールする
  3. aws-iam-authenticator をインストールする
  4. キーペアを作成する
  5. サービスロールを作成する
  6. クラスタVPCを作成する

インストールするものから、先に進めていきます。EKSは、日本から一番近いオレゴンのリージョンを利用しました。

1.kubectlコマンドのインストール

筆者が試したところ、CNCFからダウンロードした kubectl コマンドでも、利用できたので、特別にビルドされたものではないようです。
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/configure-kubectl.html

ホームディレクトリに、ディレクトリ aws-eks を作成して、ダウンロードした kubectl を保存します。

2.AWS CLIをイントール

awsコマンドは、IaaS部分の操作のために必要なので、インストールしておきます。

$ aws configure
AWS Access Key ID []: ****************ADMA
AWS Secret Access Key []: ****************pXoq
Default region name []: us-west-2 
Default output format []: text 

AWS Access Key ID と Secret Access ID の取得は、https://console.aws.amazon.com/iam/home#/security_credential で、「新しいアクセスキーの作成」をクリックする。

スクリーンショット 2018-10-20 9.32.34.png

アクセスキーを表示して、コピーして、上記の asw configure コマンドのプロンプトへペーストする。
スクリーンショット 2018-10-20 9.33.46.png

3.aws-iam-authenticator をインストール

このaws-iam-authenticator kubectl コマンドの中から呼び出して利用します。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/getting-started.html Amazon EKS の 「aws-iam-authenticator をインストールするには」に従ってインストールする。

ホームディレクトリに、ディレクトリ aws-eks を作成して、ダウンロードした このファイルを保存します。

4.キーペアを作成する

後に、ワーカーノードを作成する時に、必要となるので、キーペアを作成しておきます。

コンソールのリンク
https://us-west-2.console.aws.amazon.com/ec2/v2/home?region=us-west-2#KeyPairs:sort=keyName

5.サービスロールの作成

EKSクラスタのために、サービスロールを作成します。

https://console.aws.amazon.com/iam/home#/home をクリックして、Web画面を開いてロールを選択する。そして、「ロールの作成」ボタンをクリックする。次に遷移した画面で、"EKS" のリンクをクリックする。 「次のステップ:アクセス権限」をクリック、「次のステップ:確認」をクリックする。次の画面のロール名*にロール名を入れて、「ロールの作成」ボタンをクリックする。

スクリーンショット 2018-10-20 13.30.58.png

クリックにより、ロールのリストが表示されたら、今作った「eksRole」をクリックする。そして、「ポリシーをアタッチします」をクリックする。
ロール名のリストの中から、「AmazonEC2FullAccess」を探して、アタッチする。

6.クラスタVPCを作成する

EKSクラスタのために、VPCを作成します。

CloudFormationマネジメント https://us-west-2.console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks?filter=active をクリックして開くいて、「新しいスタックの作成」をクリック

テンプレートが登録されたURLをペーストして、「次へ」をクリックする。

https://amazon-eks.s3-us-west-2.amazonaws.com/cloudformation/2018-08-30/amazon-eks-vpc-sample.yaml

以下は、S3のURLをペーストしたところ

スクリーンショット 2018-10-20 13.46.31.png

Amazon S3テンプレートによって、次の画面の表示が変わる。 スタックの名前に eks-vpc をセットして、「次へ」をクリックする。

スクリーンショット 2018-10-20 13.49.11.png

オプション画面は入力なしで、「次へ」をクリック。確認画面が表示されるので「作成」をクリックする。

スタック作成の進行中画面が表示されるので、「状況」に「CREATE_COMPLETE」が表示されたら、作成成功です。

以上で、EKSクラスタを作成するための準備が整いました。これから、EKSクラスタのマスターノードとワーカーノードを作成していきます。


EKS (Kubernetes) クラスタの構築

EKSでは、クラスタを一歩づつ構成していく必要があるので、GKE/IKSと同じような環境をつくるために、次の 1〜7 の作業をおこないます。

  1. k8sクラスタの作成
  2. KUBECONFIGの設定
  3. アクセス資格情報の取得
  4. ワーカーノードの起動
  5. ワーカーノードとクラスタ結合
  6. Calicoのデーモンセット起動
  7. ストレージクラスの設定

1.k8sクラスタの作成

EKSではマスターノードとワーカーノードは別々に作成します。 ここでは、まず、マスターノードのデプロイです。

スクリーンショット 2018-10-20 14.51.13.png

マスターノードだけの作成時間
開始 14:52 完了 15:02

2.KUBECONFIGの設定

macOSのケースですが、ホームディレクトリにaws-eksに作っておき、以下のファイルを置いておきます。 

export KUBECONFIG=/Users/maho/aws-eks/.kube/config.yml:$KUBECONFIG
export PATH=/Users/maho/aws-eks/bin:/Users/maho/Library/Python/2.7/bin:$PATH

aws-eksにディレクトリとコマンドを、専用のディレクトリに置いて、上のパス設定で利用することにしました。

$ tree aws-eks
aws-eks
├── bin
│   ├── aws-iam-authenticator
│   └── kubectl
├── env-setup

EKSを利用する前に、source して、環境変数を設定します。

$ . env-setup

3.アクセス資格情報の取得

新しく提供されたコマンド https://aws.amazon.com/jp/about-aws/whats-new/2018/09/amazon-eks-simplifies-cluster-setup-with-update-kubeconfig-cli-command/ を利用して、前述で設定した 環境変数 KUBECONFIG のパスに設定されたコンフィグファイルに、EKSクラスタの設定を追加します。

$ aws eks update-kubeconfig --region us-west-2 --name eks1
Added new context arn:aws:eks:us-west-2:102321567306:cluster/eks1 to /Users/maho/aws-eks/.kube/config.yml
imac:aws-eks maho$ kubectl config get-contexts
CURRENT   NAME                                              CLUSTER                                           AUTHINFO                                          NAMESPACE
*         arn:aws:eks:us-west-2:102321567306:cluster/eks1   arn:aws:eks:us-west-2:102321567306:cluster/eks1   arn:aws:eks:us-west-2:102321567306:cluster/eks1   

上記のコマンドが成功すると、次のように、マスターノード上で動作するサービスが表示されます。しかし、この時点でワーカーノードが無いので、これ以上のことができません。

imac:aws-eks maho$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   5m

4.ワーカーノードの起動

ワーカーノードは、EC2インスタンスとして起動して、マスターと結合します。 CloudFormation ( https://us-west-2.console.aws.amazon.com/cloudformation/) を開いて、「スタックの作成」をクリックして、作成画面を表示します。そして、次のリンクをAmazon S3 テンプレートURLの入力フィールドにコピペします。

https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-07-26/amazon-eks-nodegroup.yaml

次の画面は、テンプレートURLをペーストしたところです。

スクリーンショット 2018-10-20 20.41.50.png

これで「次へ」をクリックして、入力画面を表示します。スタックの名称をインプットします。

スクリーンショット 2018-10-20 20.45.20.png

クラスタの作成パラメータをセットしていきます。この設定が正しくないと、ワーカーノードの作成に失敗したり、ネットワーク関連の動作がうまくいかないケースが発生するので、注意が必要です。

  • スタックの名前: CloudFormation上の名前
  • ClusterName: EKSクラスタの名前で、前述で作成した名前と一致させます。
  • ClusterControlPlaneSecurityGroup: 前述のCloudFormationで作成したVPCの名前で始まる eks-pvc をセットします。
  • NodeGroupName: ノード名に付与される目印です。
  • NodeAutoScalingGroupMinSize: オートスケール利用時の最小ノード数
  • NodeAutoScalingGroupMaxSize: オートスケール利用時の最大ノード数
  • NodeInstanceType: EC2インスタンスのタイプ
  • NodeImageId: イメージID データセンターによってセットするべきIdが決まっています。
  • KeyName: 前述で作成したキーペアの名前です。
  • Vpcid: 前述で作成したVPCのIDです。
  • Subnets: サブネットはVPCのもとに作られたサブネットを全て選択します。

以下は、入力画面です。

スクリーンショット 2018-10-20 20.45.29.png

入力後に「作成」ボタンをクリックした後に、EC2インスタンスのリストを表示したところです。 インスタンスが作成されていきます。

スクリーンショット 2018-10-20 20.52.34.png

5.ワーカーノードとクラスタ結合

次のYAMLファイルを作成して、キー rolearn の値に、NodeInstanceRole の値をセットします。

aws-auth-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: (ここに CloudFormation の NodeInstanceRole の値を入れる)
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

この NodeInstanceRole は、CloudFormationの画面から参照できます。

スクリーンショット 2018-10-20 21.06.30.png

YAMLを適用して、ノードがクラスタに追加される様子です。約40秒で追加が完了しています。

$ kubectl apply -f aws-auth-cm.yaml
configmap "aws-auth" created

$ kubectl get no --watch
NAME                                            STATUS     ROLES     AGE       VERSION
ip-192-168-112-136.us-west-2.compute.internal   NotReady   <none>    3s        v1.10.3
ip-192-168-142-69.us-west-2.compute.internal    NotReady   <none>    3s        v1.10.3
ip-192-168-248-229.us-west-2.compute.internal   NotReady   <none>    5s        v1.10.3
ip-192-168-248-229.us-west-2.compute.internal   NotReady   <none>    11s       v1.10.3
ip-192-168-142-69.us-west-2.compute.internal   NotReady   <none>    11s       v1.10.3
ip-192-168-112-136.us-west-2.compute.internal   NotReady   <none>    11s       v1.10.3
ip-192-168-248-229.us-west-2.compute.internal   Ready     <none>    21s       v1.10.3
ip-192-168-142-69.us-west-2.compute.internal   Ready     <none>    21s       v1.10.3
ip-192-168-112-136.us-west-2.compute.internal   NotReady   <none>    21s       v1.10.3
ip-192-168-248-229.us-west-2.compute.internal   Ready     <none>    31s       v1.10.3
ip-192-168-142-69.us-west-2.compute.internal   Ready     <none>    31s       v1.10.3
ip-192-168-112-136.us-west-2.compute.internal   Ready     <none>    31s       v1.10.3
ip-192-168-248-229.us-west-2.compute.internal   Ready     <none>    41s       v1.10.3
ip-192-168-142-69.us-west-2.compute.internal   Ready     <none>    41s       v1.10.3

次は追加されたノードのリストです。

$ kubectl get no
NAME                                            STATUS    ROLES     AGE       VERSION
ip-192-168-112-136.us-west-2.compute.internal   Ready     <none>    5m        v1.10.3
ip-192-168-142-69.us-west-2.compute.internal    Ready     <none>    5m        v1.10.3
ip-192-168-248-229.us-west-2.compute.internal   Ready     <none>    5m        v1.10.3

6.Calicoの設定

次のネットワークのポリシー設定が有効となるように、Calicoを導入します。

$ kubectl apply -f https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/master/config/v1.2/calico.yaml
daemonset.extensions "calico-node" created
customresourcedefinition.apiextensions.k8s.io "felixconfigurations.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "bgpconfigurations.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "ippools.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "hostendpoints.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "clusterinformations.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "globalnetworkpolicies.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "globalnetworksets.crd.projectcalico.org" created
customresourcedefinition.apiextensions.k8s.io "networkpolicies.crd.projectcalico.org" created
serviceaccount "calico-node" created
clusterrole.rbac.authorization.k8s.io "calico-node" created
clusterrolebinding.rbac.authorization.k8s.io "calico-node" created
deployment.extensions "calico-typha" created
clusterrolebinding.rbac.authorization.k8s.io "typha-cpha" created
clusterrole.rbac.authorization.k8s.io "typha-cpha" created
configmap "calico-typha-horizontal-autoscaler" created
deployment.extensions "calico-typha-horizontal-autoscaler" created
role.rbac.authorization.k8s.io "typha-cpha" created
serviceaccount "typha-cpha" created
rolebinding.rbac.authorization.k8s.io "typha-cpha" created
service "calico-typha" created

calicoの導入と稼働は、デーモンセットによって行われますから、ノードを追加した場合でも、自動的にインストールされます。 calico-nodeがノードで動作することで、機能が働くようになります。

$ kubectl get daemonset calico-node --namespace=kube-system
NAME          DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
calico-node   3         3         3         3            3           <none>          1m

$ kubectl get po  --namespace=kube-system
NAME                                                  READY     STATUS    RESTARTS   AGE
aws-node-lktrj                                        1/1       Running   0          12m
aws-node-m745d                                        1/1       Running   0          12m
aws-node-x2vr2                                        1/1       Running   1          12m
calico-node-hcw6s                                     1/1       Running   0          1m
calico-node-nchg5                                     1/1       Running   0          1m
calico-node-p8d2l                                     1/1       Running   0          1m
calico-typha-75667d89cb-ftvbf                         1/1       Running   0          1m
calico-typha-horizontal-autoscaler-78f747b679-8k2jm   1/1       Running   0          1m
kube-dns-7cc87d595-zfmf5                              3/3       Running   0          6h
kube-proxy-9vc2m                                      1/1       Running   0          12m
kube-proxy-m584p                                      1/1       Running   0          12m
kube-proxy-xlp7r                                      1/1       Running   0          12m

7.ストレージクラスの設定

EKSでは、デフォルトでストレージクラスが入っていないので、AWSのブロックストレージ EBS が利用できるように、ストレージクラスを設定します。

ebs-storage-class.yml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: ebs-gp2
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
mountOptions:
  - debug

上記のストレージクラスのマニフェストを設定する様子が次の実行結果です。

$ kubectl apply -f ebs-storage-class.yml
storageclass.storage.k8s.io "ebs-gp2" created

$ kubectl get sc
NAME                PROVISIONER             AGE
ebs-gp2 (default)   kubernetes.io/aws-ebs   7s

以上で、EKSのセットアップが完了しました。


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

EKSの中でも、ネットワークとストレージの基本的な部分を以下の3つのケースで確認しました。

  1. ノードポート EC2インスタンスに対応づけたパブリックIPアドレスでアクセスします。
  2. ロードバランサー ELBを起動してパブリックIPでアクセスします。
  3. 永続ボリューム EBSをマウントします。ブロックストレージなので、1PVは1ポッド接続になります。

1.ノードポート

マニフェストを適用した結果です。

$ kubectl apply -f apl-1.yml
deployment.apps "web-apl1" created
service "web-apl1" created

$ kubectl get -f apl-1.yml
NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web-apl1   1         1         1            0           7s

NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/web-apl1   NodePort   10.100.97.17   <none>        80:31467/TCP   7s

EC2インスタンスのリストを表示したところです。 このIPアドレスに対して、curlでアクセスして、動作を確認します。

$ aws ec2 describe-instances --query 'Reservations[].Instances[].[PublicIpAddress,Tags[?Key==`Name`].Value|[0]]'
18.***.***.***  eks1-ng1-Node
35.***.***.**   eks1-ng1-Node
34.***.***.***  eks1-ng1-Node

セキュリティ・グループへのNodePortをアクセスするためのルールの追加します。

スクリーンショット 2018-10-20 21.30.56.png

次は curlのテスト結果です。 ポッドが1個なので、どのノード(EC2インスタンス)から、同じポッドにアクセスしていることがわかります。

$ curl http://18.***.***.***:31467/
Hostname: web-apl1-74cb4d5959-tk6qp

$ curl http://35.***.***.**:31467/
Hostname: web-apl1-74cb4d5959-tk6qp

$ curl http://34.***.***.***:31467/
Hostname: web-apl1-74cb4d5959-tk6qp

ここで利用したマニフェストです。

apl-1.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: web-apl1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        web: web-apl1
    spec:
      containers:
      - image: maho/webapl3
        name: web-server-c
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-apl1
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
  selector:
    web: web-apl1

2.ロードバランサー

EKSからELBを利用して、外部からアクセスするケースです。

$ kubectl apply -f apl-3.yml
deployment.apps "web-apl3" created
service "web-apl3" created

$ kubectl get -f apl-3.yml
NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web-apl3   3         3         3            0           8s

NAME               TYPE           CLUSTER-IP       EXTERNAL-IP        PORT(S)        AGE
service/web-apl3   LoadBalancer   10.100.162.235   a4538c834d465...   80:32641/TCP   8s

起動状態の確認です。

$ kubectl get svc web-apl3 -o wide
NAME       TYPE           CLUSTER-IP      EXTERNAL-IP                                                               PORT(S)        AGE       SELECTOR
web-apl3   LoadBalancer   10.100.248.65   acfbaa466d46611e8baaa02a2ba37abf-1173102834.us-west-2.elb.amazonaws.com   80:31152/TCP   4m        apl=web-apl3

ELBのDNS名でアクセスした結果です。 同様にポッドが一個なので、一つのポッドへしかアクセスしていません。

imac:yaml maho$ curl http://acfbaa466d46611e8baaa02a2ba37abf-1173102834.us-west-2.elb.amazonaws.com/
Hostname: web-apl3-dc7dcbfdf-rmkz9

imac:yaml maho$ curl http://acfbaa466d46611e8baaa02a2ba37abf-1173102834.us-west-2.elb.amazonaws.com/
Hostname: web-apl3-dc7dcbfdf-rmkz9

imac:yaml maho$ curl http://acfbaa466d46611e8baaa02a2ba37abf-1173102834.us-west-2.elb.amazonaws.com/
Hostname: web-apl3-dc7dcbfdf-rmkz9

ここで利用したマニフェストです。 この設定でELBが起動して外部公開されます。

apl-3.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-apl3
spec:
  replicas: 1
  selector:
    matchLabels:
      apl: web-apl3
  template:
    metadata:
      labels:
        apl: web-apl3
    spec:
      containers:
      - image: maho/webapl3
        name: web-server-c
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-apl3
spec:
  selector:
    apl: web-apl3
  ports:
  - name: http
    protocol: TCP
    port: 80
  type: LoadBalancer

3.永続ボリューム

EBSを利用するケースです。

最初に、PVC(永続ボリューム要求)を作成しておきます。 EBSはブロックストレージなので、1ボリュームは、1ポッドからのみアクセスできます。

pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data1
spec:
  accessModes:
  #- ReadWriteMany
  - ReadWriteOnce
  # storageClassName: standard
  resources:
    requests:
      storage: 2Gi

永続ボリュームにマウントするポッドのマニフェストです。

pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  volumes:
  - name: pvc1
    persistentVolumeClaim:
      claimName: data1
  containers:
  - name: ubuntu
    image: ubuntu:16.04
    volumeMounts:
    - name: pvc1
      mountPath: /mnt
    #command: [”tail”, “-f”, “/d
    command: [ 'tail' ]
    args:
    - -f
    - /dev/null

デプロイと結果です。

$ kubectl apply -f pvc.yml
persistentvolumeclaim "data1" created

$ kubectl get pvc
NAME      STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data1     Bound     pvc-4ce26c73-d476-11e8-baaa-02a2ba37abf8   2Gi        RWO            ebs-gp2        20s

$ kubectl apply -f pod.yml
pod "pod1" created

$ kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
pod1      1/1       Running   0          15s

デプロイしたポッドに入って、永続ボリュームをマウントしている状態を確認したものです。

$ kubectl exec -it pod1 -- bash
root@pod1:/# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay          20G  2.3G   18G  12% /
tmpfs           997M     0  997M   0% /dev
tmpfs           997M     0  997M   0% /sys/fs/cgroup
/dev/xvdbj      2.0G  6.0M  1.8G   1% /mnt
/dev/xvda1       20G  2.3G   18G  12% /etc/hosts
shm              64M     0   64M   0% /dev/shm
tmpfs           997M   12K  997M   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs           997M     0  997M   0% /sys/firmware

クリーンアップ

EKSクラスタを削除しただけでは、ワーカーノードのEC2インスタンスなどを削除しないので、注意が必要です。想定外の課金が発生しないように、それぞれ削除しなければなりません。

  • EKSクラスタの削除
  • Cloud Formation のワーカーノードのスタックを削除
  • ELBの削除 k8sから作成したPVを消去する必要がある。
  • EBSの削除 k8sから作成したLoadBalancerは削除されないケースがあった。

感想

EKSは、GKEやIKSとは違った設定方法だけど、解ってしまえば、同じように使えるということが解った。


参考資料

[1] Amazon EKS の使用開始、https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/getting-started.html