Amazon EKSが一般公開されたので使ってみた

Amazon EKSが一般公開されたので使ってみた

皆さんこんにちは。先日、ついにAWSのAmazon EKSが一般公開されましたね。現時点ではまだオレゴンとバージニア北部でしか使えませんが、今回は検証の為、ユーザーズガイドに沿って実際にクラスタを作成し、ゲストブックアプリケーションをデプロイしてみたいと思います。
尚、各種作業におけるKubernetesと、AWSの従来のリソースについての詳細説明は省略していますのでご了承下さい。

準備

AWS CLI

ドキュメントではマネジメントコンソールを使っていますが、今回はCLIを使っていきたいと思います。基礎的な部分なのでインストール方法は省略します。(ドキュメントはこちら

今回使ったバージョンは次の通りです:

$ aws --version
aws-cli/1.15.42 Python/2.7.10 Darwin/17.6.0 botocore/1.10.42

また、今回はずっとオレゴン(us-west-2)で作業するので、環境変数をセットしておきます。

$ export AWS_DEFAULT_REGION=us-west-2

kubectl

今回は、Docker for Mac (18.05.0-ce-mac67, Channel: edge) に同梱されている物を使いました。バージョンは次の通りです:

$ kubectl version --short --client
Client Version: v1.10.3

Heptio Authenticator

Amazon EKSでは、クラスタとの認証にIAMを使うようですが、これにはHeptio Authenticatorと言うツールが必要なのでインストールします。

ドキュメントではいくつかのインストール方法が紹介されていますが、今回はシンプルにcurlでダウンロードし、実行権限を与えた上でPATHの効いてるディレクトリに放り込む事にしました:

$ cd /tmp
$ curl -o heptio-authenticator-aws https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-06-05/bin/darwin/amd64/heptio-authenticator-aws
$ chmod +x heptio-authenticator-aws
$ mv heptio-authenticator-aws /usr/local/bin

実際にコマンドを叩いてみます:

$ heptio-authenticator-aws

A tool to authenticate to Kubernetes using AWS IAM credentials
Usage:
  heptio-authenticator-aws [command]

Available Commands:
  help        Help about any command
  init        Pre-generate certificate, private key, and kubeconfig files for the server.
  server      Run a webhook validation server suitable that validates tokens using AWS IAM
  token       Authenticate using AWS IAM and get token for Kubernetes
  verify      Verify a token for debugging purpose

Flags:
  -i, --cluster-id ID     Specify the cluster ID, a unique-per-cluster identifier for your heptio-authenticator-aws installation.
  -c, --config filename   Load configuration from filename
  -h, --help              help for heptio-authenticator-aws

Use "heptio-authenticator-aws [command] --help" for more information about a command.

usageが表示されました。

IAM Role

EKSサービスがクラスタの操作時にAWSの他のサービスを操作できるように、予めEKSサービス用のRoleを作り、 AmazonEKSClusterPolicy とAmazonEKSServicePolicy をアタッチする必要があります。

CLIでさくっと作っていきます:

$ aws iam create-role --role-name eks-role --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"eks.amazonaws.com"}}]}'
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "eks.amazonaws.com"
                    }
                }
            ]
        },
        "RoleId": "AROBILD2LKDSSJCCIADME",
        "CreateDate": "2018-06-25T07:31:28.966Z",
        "RoleName": "eks-service-role",
        "Path": "/",
        "Arn": "arn:aws:iam::000000000000:role/eks-service-role"
    }
}

$ aws iam attach-role-policy --role-name eks-service-role --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy

$ aws iam attach-role-policy --role-name eks-service-role --policy-arn arn:aws:iam::aws:policy/AmazonEKSServicePolicy

今回は eks-service-role と言う名前を付ける事にしました。尚、RoleのARNは後ほど使うので控えておきます。

VPC

Kubernetesクラスタを展開するVPCを用意する必要がありますが、今回は面倒なのでアカウント作成時にデフォルトで作られている物を使用する事にしました。

EC2のキーペア

KubernetesのWorkerノードとして動作するEC2インスタンス用のSSHキーペアを用意する必要がありますが、今回も適当な物を使う事にしたので作成方法については割愛します。

クラスタを作る

次にクラスタを作っていきます。因みに、クラスタは1個につき、1時間あたりUS$0.2課金されます。残念ながら、GKEと違って無料じゃないようですね。

クラスタの作成にもCLIを使って作っていきます。使用するインターフェースはeks create-clusterです。
使い方をざっくり説明すると、

  • --name--role-arn--resources-vpc-config オプションは必須
  • --name にはクラスタ名を指定。ユニークである必要がある
  • --role-arn には先程作ったRole(eks-service-role)のARNを指定
  • --resources-vpc-config にはVPCのサブネットIDと、セキュリティグループのIDを指定(複数指定可)
    • サブネットは最低2個以上指定する必要がある
    • 今回はデフォルトVPCのus-west-2aと2bのサブネットと、VPCのデフォルトのセキュリティグループを使用した

その他、--kubernetes-version でKubernetesのバージョンを指定したりできますが、今回は必要最低限のオプションのみで実行しました。因みに、サブネットは us-west-2a と us-west-2b の物、セキュリティグループにはVPCのデフォルトの物を指定しました。
(当たり前ですが、現時点で利用できない東京リージョン等を指定するとエンドポイントが無いと怒られます)

実行すると、作成したクラスタの詳細が表示されます:

$ aws eks create-cluster \
  --name my-cluster \
  --role-arn arn:aws:iam::000000000000:role/eks-service-role \
  --resources-vpc-config "subnetIds=subnet-0164f46a,subnet-057c0a4c,securityGroupIds=sg-03ebe152"
{
    "cluster": {
        "status": "CREATING",
        "name": "my-cluster",
        "certificateAuthority": {},
        "roleArn": "arn:aws:iam::000000000000:role/eks-service-role",
        "resourcesVpcConfig": {
            "subnetIds": [
                "subnet-0164f46a",
                "subnet-057c0a4c"
            ],
            "vpcId": "vpc-013a2508",
            "securityGroupIds": [
                "sg-03ebe152"
            ]
        },
        "version": "1.10",
        "arn": "arn:aws:eks:us-west-2:000000000000:cluster/my-cluster",
        "createdAt": 1529826773.192
    }
}

この時点で結果のJSONを見ると、cluster.status は “CREATING” の状態だと言う事が分かります。これが “ACTIVE” になると、クラスタが利用できるようになります。(因みに結構時間かかります。現時点でのバージョンでは、通常10分未満で完了するようです)

また、上のJSONと同様の物がeks describe-clusterで取得可能ですので、下記を実行して “ACTIVE” になるまで待ちましょう(--name に取得したいクラスタ名を指定します):

$ aws eks describe-cluster --name my-cluster --query cluster.status
"ACTIVE"

ACTIVEになったら、後ほど必要になるKubernetes APIのエンドポイントURLと、認証用のCA証明書を控えておきます。これらも、describe-clusterで取得可能です:

$ aws eks describe-cluster --name my-cluster --query cluster.endpoint --output text
https://5C1485ACC29FAA5A16E7E77A4491F4E8.yl4.us-west-2.eks.amazonaws.com

$ aws eks describe-cluster --name my-cluster --query cluster.certificateAuthority.data --output text
(base64 message)...

続いて、クラスタの設定をします。ドキュメントに記載の通り、以下のYAMLの <endpoint-url> と <base64-encoded-ca-cert> の部分を先程控えておいた値に、 <cluster-name> をクラスタ名に置き換えて、 ~/.kube/config-my-eks-cluster に保存します。

apiVersion: v1
clusters:
- cluster:
    server: <endpoint-url>
    certificate-authority-data: <base64-encoded-ca-cert>
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: aws
  name: aws
current-context: aws
kind: Config
preferences: {}
users:
- name: aws
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      command: heptio-authenticator-aws
      args:
        - "token"
        - "-i"
        - "<cluster-name>"
        # - "-r"
        # - "<role-arn>"

その後、$KUBECONFIG 環境変数に上記の設定ファイルを適用しておきます:

$ export KUBECONFIG=$KUBECONFIG:~/.kube/config-my-eks-cluster

ここまでできたら、 kubectl を使ってクラスタとやり取りが行えます。試しにServiceの状態を確認してみましょう:

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   3h

無事にクラスタとのやり取りができました。

Workerノードを起動・登録する

クラスタが無事にできたので、次にWorkerノードを登録します。WorkerノードはEC2インスタンスとなりますので、インスタンスタイプに応じた課金が行われます。

ドキュメントでは、CloudFormationを使ってノードのオートスケーリンググループ等のリソースを作成していたので、今回はそれを使っていきます。
https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-06-05/amazon-eks-nodegroup.yaml をテンプレートとし、それぞれ以下のパラメータを設定します:

  • Stack name: CloudFormationのスタック名。適当な名前 (eks-my-cluster-worker-nodes) を設定
  • Cluster name: 今回作ったEKSクラスタの名前
  • ClusterControlPlaneSecurityGroup: クラスタ生成時に使ったセキュリティグループのID
  • NodeGroupName: オートスケーリンググループ関連のリソースのNameのプレフィックスとして使われる模様。これも適当な名前 (eks-my-cluster-worker) を設定
  • NodeAutoScalingGroupMinSize: オートスケーリングの最小数
  • NodeAutoScalingGroupMaxSize: オートスケーリングの最大数
  • NodeInstanceType: EC2インスタンスのインスタンスタイプ
  • NodeImageId: EC2インスタンスの起動イメージ, 今回はオレゴンなので ami-73a6e20b (バージニア北部は ami-dea4d5a1)
  • KeyName: EC2インスタンスへSSHログインする為のキーペア名。予め準備しておいた物を使う
  • VpcId: クラスタ作成時に指定したサブネットが属するVPCのID
  • Subnets: クラスタ作成時に指定したサブネットID

ここでもCLIを使います。インターフェースはcloudformation create-stackです:

$ aws cloudformation create-stack \
  --template-body https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-06-05/amazon-eks-nodegroup.yaml \
  --stack-name eks-my-cluster-worker-nodes \
  --capabilities CAPABILITY_IAM \
  --parameters \
    ParameterKey=ClusterName,ParameterValue=my-cluster \
    ParameterKey=ClusterControlPlaneSecurityGroup,ParameterValue=sg-03ebe152 \
    ParameterKey=NodeGroupName,ParameterValue=eks-my-cluster-worker \
    ParameterKey=NodeAutoScalingGroupMinSize,ParameterValue=1 \
    ParameterKey=NodeAutoScalingGroupMaxSize,ParameterValue=2 \
    ParameterKey=NodeInstanceType,ParameterValue=t2.small \
    ParameterKey=NodeImageId,ParameterValue=ami-73a6e20b \
    ParameterKey=KeyName,ParameterValue=issei \
    ParameterKey=VpcId,ParameterValue=vpc-013a2508 \
    ParameterKey=Subnets,ParameterValue=\"subnet-0164f46a,subnet-057c0a4c\"
{ "StackId": "arn:aws:cloudformation:us-west-2:000000000000:stack/eks-my-cluster-worker-nodes/c7e20620-7822-11e8-bfa6-50a68a0e32ba" } 

作成されたスタックの、 NodeInstanceRole と言う論理IDが名付けられたリソースのIAM RoleのARNが後ほど必要になるので、控えておきます。これにはcloudformation describe-stack-resourcesiam get-roleインターフェースを組み合わせると取得できます(--stack-name
スタック名、--logical-resource-id に論理リソースIDを指定):

$ aws iam get-role --role-name $(aws cloudformation describe-stack-resources --stack-name eks-my-cluster-worker-nodes --logical-resource-id NodeInstanceRole --query "StackResources[0].PhysicalResourceId" --output text) --query Role.Arn --output text
arn:aws:iam::000000000000:role/eks-my-cluster-worker-nodes-NodeInstanceRole-CTP7DO7ST83A

起動したWorkerをクラスタに追加する為には、更にAWS AuthenticatorのConfigMapを設定する必要があります。まずはテンプレートをDLします:

$ curl -O https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-06-05/aws-auth-cm.yaml

エディタでこのファイルを開き、 <ARN of instance role (not instance profile)> の部分を、先程のIAM RoleのARNで置き換えます。

# aws-auth-cm.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: <ARN of instance role (not instance profile)>
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

その後、ConfigMapをクラスタに適用します:

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

しばらくすると、WorkerノードのステータスがREADYになります:

$ kubectl get nodes
NAME                                         STATUS    ROLES     AGE       VERSION
ip-172-31-34-76.us-west-2.compute.internal   Ready     <none>    1m        v1.10.3
ip-172-31-43-90.us-west-2.compute.internal   Ready     <none>    1m        v1.10.3

ゲストブックアプリケーションを作成する

クラスタの準備が整ったので、いよいよアプリケーションをデプロイしてみます。
ここからはちょっと駆け足でいきます。

アプリケーションに必要なレプリケーションコントローラーやサービス等のオブジェクトを適用していきます。インターネット上に公開されている設定YAMLファイルを直接適用できる所がKubernetesの良い所です:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/redis-master-controller.json
replicationcontroller "redis-master" created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/redis-master-controller.json
replicationcontroller "redis-master" created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/redis-master-service.json
service "redis-master" created
 
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/redis-slave-controller.json
replicationcontroller "redis-slave" created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/redis-slave-service.json
service "redis-slave" created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/guestbook-controller.json
replicationcontroller "guestbook" created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.3/examples/guestbook-go/guestbook-service.json
service "guestbook" created

その後、guestbook サービスのEXTERNAL-IPが割り当てられるまで待ちます:

$ kubectl get services -o wide
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)          AGE       SELECTOR
guestbook      LoadBalancer   10.100.121.35    a6aa2e574778911e894530228d4e0b41-1287580158.us-west-2.elb.amazonaws.com   3000:32209/TCP   2m        app=guestbook
kubernetes     ClusterIP      10.100.0.1       <none>                                                                    443/TCP          36m       <none>
redis-master   ClusterIP      10.100.237.173   <none>                                                                    6379/TCP         2m        app=redis,role=master
redis-slave    ClusterIP      10.100.168.148   <none>

割り当てられたら、アクセスが可能です。 http://a143abb3b782711e890be021a4e4ac58-425821813.us-west-2.elb.amazonaws.com:3000/ を開いてみると、無事ゲストブックアプリケーションが開かれました。

クリーンナップ

無事にアプリケーションをデプロイできたので、作ったリソースを削除していきます。

クラスタからゲストブックアプリケーションの為にデプロイしたオブジェクトを削除:

$ kubectl delete \
  rc/redis-master \
  rc/redis-slave \
  rc/guestbook \
  svc/redis-master \
  svc/redis-slave \
  svc/guestbook

Workerノードのオートスケーリンググループを削除:

$ aws cloudformation delete-stack --stack-name eks-my-cluster-worker-nodes

EKSクラスタを削除:

$ aws eks delete-cluster --name my-cluster

感想

GKEに比べると、クラスタの構築は大変な印象がありました。また、GKEがクラスタの料金を無料としている一方で、EKSは少額なものの、課金されてしまう点も無視はできません。
そう言った意味でも、自社開発であるAmazon ECSの方が(クラスタ構築自体の)敷居は低いように思います。しかし、世の中の流れ的にKubernetesがデファクトとなっていますし、クラスタ構築後の手軽さや手堅さを考えると、今後も積極的にKubernetesを使いたいと思います。なのでAmazon EKSの今後の改善に期待。(と言うか早く東京に来てくれ)