CloudFormationとCLIで作るEKS環境

CloudFormationとCLIを使ってEKS環境を作成してみました。
2018.12.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

おはようございます、加藤です。CloudFormationとCLIを使ってEKS環境を作成してみました。

やってみた

環境

  • macOS Mojave 10.14.2(18C54)
  • Homebrew 1.8.6

クライアント側

ツールのインストール

brew install awscli direnv assume-role jo jq

kubectlのダウンロード

~/binに実行ファイルを保存していますが、好みの場所に保存してOKです。 ただし、その場合は関係する箇所を適時読み替えてください。

EKSが対応しているk8sのバージョンは下記の通りです。

  • 1.10.3
  • 1.11.5

クライアント(kubectl)、マスターノード、ワーカーノードのバージョンは一致させて使用したいので、全てのバージョンのkubectlをダウンロードしておきます。 ~/binにバージョン番号を付与して保存します。

mkdir ~/bin
curl -o ~/bin/eks-kubectl-1.10.3 https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-07-26/bin/darwin/amd64/kubectl
curl -o ~/bin/eks-kubectl-1.11.5 https://amazon-eks.s3-us-west-2.amazonaws.com/1.11.5/2018-12-06/bin/darwin/amd64/kubectl
chmod +x ~/bin/eks-kubectl-1.10.3
chmod +x ~/bin/eks-kubectl-1.11.5

~/binkubectlを保存したのでPATHを通します。

echo 'export PATH=$HOME/bin:$PATH' >> ~/.bash_profile
source ~/.bash_profile

aws iam authenticatorのダウンロード

既にPATHを通しているのでダウンロードと実行フラグの設定のみ行います。

curl -o ~/bin/aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-07-26/bin/darwin/amd64/aws-iam-authenticator
chmod +x ~/bin/aws-iam-authenticator

作業ディレクトリのセットアップ

mkdir ~/workspace/k8s
cd ~/workspace/k8s

direnvを使って環境変数を設定します。 assume-roleコマンドでAWS認証情報を環境変数にセットしています。 KUBECONFIGを設定しない場合は$HOME/.kube/configに設定が保存されます。私はプロジェクト毎に分離させたい人なので作業ディレクトリ内に保存するよう設定しました。 EKSのバージョンは1.10を使用するので、aliasでkubectl="~/bin/eks-kubectl-1.10.3"としています。

.envrc

eval $(assume-role default)
export KUBECONFIG='.kube/config'
export kubectl="~/bin/eks-kubectl-1.10.3"

サーバー側

BASEの作成

EKSが使用するVPCとControl Plane(マスターノード)が使用するセキュリティグループとIAMロールを作成します。 イミュータブルな部分でここは作成したら変更することが基本的にないはずです。

template-eks-base.yaml

export BASE_STACK_NAME='eks-dev-base'

curl -O https://raw.githubusercontent.com/kmd2kmd/cfn-eks/master/template-eks-base.yaml
aws cloudformation validate-template --template-body=file://template-eks-base.yaml

aws cloudformation create-stack \
--stack-name ${BASE_STACK_NAME} \
--template-body=file://template-eks-base.yaml \
--capabilities CAPABILITY_NAMED_IAM

# スタックの完了を待つ
aws cloudformation wait stack-create-complete \
--stack-name ${BASE_STACK_NAME}

# Outputから必要なパラメータを環境変数に取得しておく。
export CONTROLLPLANE_SECURITYGROUP_IDS=$( \
aws cloudformation describe-stacks \
--stack-name ${BASE_STACK_NAME} | \
jq -r '.Stacks[].Outputs[] | select(.OutputKey == "SecurityGroups")' | \
jq -r '.OutputValue')

export VPC_ID=$( \
aws cloudformation describe-stacks \
--stack-name ${BASE_STACK_NAME} | \
jq -r '.Stacks[].Outputs[] | select(.OutputKey == "VpcId")' | \
jq -r '.OutputValue')

export SUBNET_IDS=$( \
aws cloudformation describe-stacks \
--stack-name ${BASE_STACK_NAME} | \
jq -r '.Stacks[].Outputs[] | select(.OutputKey == "SubnetIds")' | \
jq -r '.OutputValue')

export CLUSTER_ROLE_ARN=$( \
aws cloudformation describe-stacks \
--stack-name ${BASE_STACK_NAME} | \
jq -r '.Stacks[].Outputs[] | select(.OutputKey == "ClusterRoleArn")' | \
jq -r '.OutputValue')

Control Plane の作成

Control Planeを作成します。CFnでも作成が可能ですがアップデートができずCFnで作るメリットが薄いです(初回だけCFnで以降はCLIとなってしまう為)。 なのでCLIで作成します。

export K8S_CLUSTER_NAME="eks-dev-Cluster"
export K8S_VERSION='1.10'

aws eks create-cluster \
--kubernetes-version ${K8S_VERSION} \
--name ${K8S_CLUSTER_NAME} \
--role-arn ${CLUSTER_ROLE_ARN} \
--resources-vpc-config subnetIds=${SUBNET_IDS},securityGroupIds=${CONTROLLPLANE_SECURITYGROUP_IDS}

# クラスターステータスがACTIVEになるのを待つ
aws eks describe-cluster --name ${K8S_CLUSTER_NAME} --query cluster.status

Worker Nodes の作成

Worker Nodesを作成します。

template-eks-nodegroup.yaml

NODE_IMAGE_ID(AMI ID)はこちらで確認してください。 Amazon EKSに最適化されたAMI - Amazon EKS

export NODEGROUP_STACK_NAME='eks-dev-nodegroup-000'
export KEY_NAME='cm-kato.ryo'
export NODE_IMAGE_ID='ami-06f4af3742fca5998'
export NODE_VOLUME_SIZE='20'
export NODE_GROUP_NAME='eks-nodegroup-001'

curl -o https://raw.githubusercontent.com/kmd2kmd/cfn-eks/master/template-eks-nodegroup.yaml

jo -a \
$(jo ParameterKey=KeyName ParameterValue=${KEY_NAME}) \
$(jo ParameterKey=NodeImageId ParameterValue=${NODE_IMAGE_ID}) \
$(jo ParameterKey=Subnets ParameterValue=${SUBNET_IDS}) \
$(jo ParameterKey=NodeVolumeSize -s ParameterValue=${NODE_VOLUME_SIZE}) \
$(jo ParameterKey=NodeGroupName ParameterValue=${NODE_GROUP_NAME}) \
$(jo ParameterKey=ClusterControlPlaneSecurityGroup ParameterValue=${CONTROLLPLANE_SECURITYGROUP_IDS}) \
$(jo ParameterKey=VpcId ParameterValue=${VPC_ID}) \
$(jo ParameterKey=ClusterName ParameterValue=${K8S_CLUSTER_NAME}) \
> parameter-eks-nodegroup.json

aws cloudformation create-stack \
--stack-name ${NODEGROUP_STACK_NAME} \
--template-body=file://template-eks-nodegroup.yaml \
--parameters=file://parameter-eks-nodegroup.json  \
--capabilities CAPABILITY_IAM

aws cloudformation wait stack-create-complete \
--stack-name ${NODEGROUP_STACK_NAME}

export K8S_NODE_INSTANCE_ROLE=$( \
aws cloudformation describe-stacks \
--stack-name ${NODEGROUP_STACK_NAME} | \
jq -r '.Stacks[].Outputs[] | select(.OutputKey == "NodeInstanceRole")' | \
jq -r '.OutputValue')

echo "NodeInstanceRole"
echo ${K8S_NODE_INSTANCE_ROLE}

コンフィグ取得

kubectlが参照するコンフィグを取得します。 KUBECONFIG.kube/configに変更しているので、作業ディレクトリ内に作成されます。

aws eks update-kubeconfig --name ${K8S_CLUSTER_NAME}

Worker NodesをControll Planeと結合

まだ、Controll PlaneからWorker Nodesを認識できていないので結合します。

curl -O https://amazon-eks.s3-us-west-2.amazonaws.com/cloudformation/2018-08-30/aws-auth-cm.yaml
sed -i '' -e "s#<ARN of instance role (not instance profile)>#${K8S_NODE_INSTANCE_ROLE}#g" aws-auth-cm.yaml
kubectl apply -f aws-auth-cm.yaml

動作確認

サービスの確認

kubectl get svc
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)          AGE
kubernetes     ClusterIP      10.100.0.1       <none>

Worker Nodesの確認

kubectl get nodes

全てのSTATUSがReadyになるのを待ちます。

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

guestbookというサンプルアプリをデプロイします。

kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-controller.json
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-service.json
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-controller.json
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-service.json
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-controller.json
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-service.json

guestbookEXTERNAL-IPを確認して、Webブラウザを開き、httpのポート3000でアクセスします。

kubectl get services -o wide

最後にサンプルアプリを削除します。

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

あとがき

CloudFormationとCLIでEKS環境を構築してみました!! eksctlを使えば簡単にEKS環境の構築が可能ですが、バージョンアップなども考えるとCFnでやった方が良いかも?と思いやってみました。 CFnでEKSクラスターのアップデートができなかったのが少し残念です。近日中にEKSのバージョンアップ方法についてもブログにします!!

参考