Amazon EKS のリファレンス環境を、VPC と EKS クラスタ別々に CLI から作成するための手順と実践

背景

EKS の勉強をしようと思うと、いまだと鉄板は eksctl を使うことだと思うんですが、その他にも AWS が提供しているリファレンス環境を使う方法もあるかと思います(個人差があります)1

こちらからは、既存の VPC にデプロイするか、それとも新規に VPC を作成してそこにデプロイするか、のふたつから選択できます。
当然勉強用なので、既存の環境とは別に新しい VPC を作りたくなります。ですが勉強用なので EKS クラスタは何度も消したり作ったりしたいのですが、そのたびに VPC から作り直すのはなんだか無駄です。また個人的な趣味として、リージョンはオレゴン(us-east-2)ではなく東京(ap-northeast-1)を使いたいものですし。

実はリファレンス環境を作成する CloudFormation(CFn)テンプレートは、VPC 作成部分を別の CFn テンプレート(aws-vpc.template)に切り出して、必要であればそれを呼び出す(Nest する)という構成になっています。
ということはこの aws-vpc.template を手動で呼び出せば、リファレンスに則ったかたちで VPC を作れると言うことになります。

さっそくやってみました!
リファレンスのテンプレートは随時変更される可能性があり、以下の内容そのままが必ずしも将来にわたって有効である保証はないので、その旨ご了承ください。

なお eksctl について、ならびに EKS について本気で勉強したいというひとについては、弊社ハマコーが書いたこちら記事を読んで頂ければと思います!

とはいえ VPC を作る部分については、eksctl を使う場合においても流用が可能なので無駄ではないと思ってます 2

構成

このクイックスタートで構築される環境は、こちらの図の通りです。

quickstart-amazon-eks/README.md at master · aws-quickstart/quickstart-amazon-eks より

詳細は、こちらのリポジトリをご覧になるのがはやいと思います。CFnテンプレートもこちらにあります。

用意

まずは aws-vpc.template がどのように呼び出されているかを確認するため、リファレンスである CFn テンプレートを入手しましょう。
上記ページは現在、「新規の VPC にデプロイする」という文字列のリンク先が下記の URL になっています。

こちらは短縮 URL なので、HTTP レスポンスヘッダの location: からリダイレクト先を得て、そこから CFn テンプレートの URL を抽出します。
(下は適宜改行をいれています)

1
2
3
4
5
% curl -sI https://fwd.aws/6dEQ7 | grep -i '^location:'
location: https://console.aws.amazon.com/cloudformation/home?\
    region=us-east-2#/stacks/new?\
    stackName=Amazon-EKS&\
    templateURL=https://s3.amazonaws.com/aws-quickstart/quickstart-amazon-eks/templates/amazon-eks-master.template.yaml

これで、実際に呼び出される CFn テンプレートの入手先(templateURL)が分かりました。
実際にダウンロードして内容を確認すると、どうやら下記の部分が該当しているようです。

1
2
3
4
5
6
:
Resources:
  VPCStack:
 :
      TemplateURL: !Sub 'https://${QSS3BucketName}.s3.amazonaws.com/${QSS3KeyPrefix} submodules/quickstart-aws-vpc/templates/aws-vpc.template'
 :

このうち変数 ${QSS3BucketName}${QSS3KeyPrefix} は、クイックスタートの URL からするとデフォルト値が使われることになっているので、最終的には下記の URL からテンプレート (JSON) がダウンロードされることになります。

これらのテンプレートのParametersフィールドを比較し、指定すべきパラメータを特定します。
呼び出し元(amazon-eks-master.template.yaml)が決め打ちで値を指定しているものはそれを踏襲し、aws-vpc.template のデフォルト値を利用できるものはそのまま利用するとした場合、そもそも CFn 起動のために必要なものとあわせ、起動時に指定すべきパラメータはこれらになります。

  • 指定が必要なもの
    • 起動するリージョン
    • AZ 名のリスト (AvailabilityZones) 3 つ
  • 決め打ちして指定
    • Stack 名 : Amazon-EKS-VPCStack
    • ノード数 (NumberOfAZs) : 3
    • PrivateSubnetATag2 : "kubernetes.io/role/internal-elb="
    • PublicSubnetTag2 : "kubernetes.io/role/elb="

起動するリージョンを東京(ap-northeast-1)とすると、ぼくの環境では 1a, 1c, 1d の 3AZ が使用可能なので、それら全てを指定することにします。

起動 - VPC 作成

すると、AWS CLI としては以下のコマンドで構築が可能ということになりました。

1
2
3
4
5
6
7
8
9
10
11
12
13
(
    Region='ap-northeast-1'
    AvailabilityZones='ap-northeast-1a,ap-northeast-1c,ap-northeast-1d'
    aws cloudformation create-stack \
        --stack-name Amazon-EKS-VPCStack \
        --region ${Region} \
        --parameters \
            ParameterKey=AvailabilityZones,ParameterValue='"'${AvailabilityZones}'"' \
            ParameterKey=NumberOfAZs,ParameterValue=3 \
            ParameterKey=PrivateSubnetATag2,ParameterValue="kubernetes.io/role/internal-elb=" \
            ParameterKey=PublicSubnetTag2,ParameterValue="kubernetes.io/role/elb=" \
        --template-url https://aws-quickstart.s3.amazonaws.com/quickstart-amazon-eks/submodules/quickstart-aws-vpc/templates/aws-vpc.template
)

8 行目の環境変数 ${AvailabilityZones} のところは、カンマ区切りの内容を文字列のまま CloudFormation API に渡す必要があったのでこう(エスケープ)しています。

実際に動かしてみると、構築に 5 分程度でしょうか。
削除する場合は、作成した CFn スタック Amazon-EKS-VPCStack を削除すればよいです。問題なければ数分できれいになるでしょう。

1
2
aws cloudformation delete-stack \
    --stack-name Amazon-EKS-VPCStack

EKS を起動する

それでは、作った VPC の上に EKS のクイックスタート環境を起動してみます。
この場合は「既存の VPC にデプロイする」リンクからでいいかな、と思ったんですが、以下の問題がありました。

  • VPC ID や Subnet ID を指定しなくてはならない
  • 接続試験や SSH 接続のために、アクセスを許可する IP アドレスを指定する必要がある
  • 踏み台 (bastion) ホストの EC2 インスタンスタイプを選択できない

他にも EKS ノードの EC2 インスタンスタイプなど、毎回指定するのがだるいですよね。
これらはある程度値を決め打ちしたり、自動的に取得したりして起動できると楽です。ちょっとやってみましょう。

先ほどの aws-vpc.template と同じく CFn テンプレートを内容確認します。すると、クイックスタートから呼び出される CFn テンプレート、ならびに EKS クラスタを作成している CFn テンプレートは以下であることがわかりました。

というわけで、このふたつのテンプレートの「Parameters:」セクションを見比べます。デフォルト値などを勘案すると、以下のパラメータを用意すればいいことが分かりました。

パラメータ名 説明 デフォルト値
KeyPairName ノードおよび踏み台ホストに設定するキーペア名
NodeInstanceType ノード EC2 のインスタンスタイプ t3.medium
BastionInstanceType 踏み台 EC2 のインスタンスタイプ t2.micro
RemoteAccessCIDR 踏み台への SSH 接続を許可する IP アドレス

また以下のパラメータは、作成したスタックから describe-stacks すれば入手できます。

  • VPC ID VPCID
  • サブネット ID
    • PublicSubnet1ID, PublicSubnet2ID, PublicSubnet3ID
    • PrivateSubnet1ID, PrivateSubnet2ID, PrivateSubnet3ID

というわけで、下記のようなシェルスクリプトを作成しました。JSON の加工のためにjqコマンドを使っていますので、まだの方はこの機会にインストールしてみてください。
また、どうせならということでRemoteAccessCIDRに「今現在つかっているグローバル IP アドレス」を自動的にセットするために、curlコマンドで下記 URL へアクセスしています。あわせてご確認下さい。

  • https://checkip.amazonaws.com/

#!/bin/bash
# usage: $0
# $0 DELETE
# 環境変数の設定
Region="ap-northeast-1"
StackName="Amazon-EKS-EKSStack"
TemplateUrl=https://aws-quickstart.s3.amazonaws.com/quickstart-amazon-eks/templates/amazon-eks.template.yaml
VPCStackName="Amazon-EKS-VPCStack"
KeyPairName="id_rsa_aws-common"
NodeInstanceType="t3.small"
BastionInstanceType="t3.micro"
# 第一引数に DELETE と指定されたらスタックを削除する
if [ "$1" = "DELETE" ]; then
read -p "${StackName} を削除しますか? (y/N): " yn
case $yn in
y|Y)
echo "DELETING stack ${StackName}..."
aws cloudformation delete-stack \
--stack-name ${StackName} && echo "done."
exit
;;
*)
echo "abort."
exit
;;
esac
fi
# VPC情報をスタックから取得、環境変数に格納
printf "VPC情報取得 (${Amazon-EKS-VPCStack})..."
eval $(
aws cloudformation describe-stacks --stack-name ${VPCStackName} | \
jq -r '.Stacks[].Outputs[] |
"VPCID=" + select(.OutputKey == "VPCID").OutputValue,
"PublicSubnet1ID=" + select(.OutputKey == "PublicSubnet1ID").OutputValue,
"PublicSubnet2ID=" + select(.OutputKey == "PublicSubnet2ID").OutputValue,
"PublicSubnet3ID=" + select(.OutputKey == "PublicSubnet3ID").OutputValue,
"PrivateSubnet1ID=" + select(.OutputKey == "PrivateSubnet1AID").OutputValue,
"PrivateSubnet2ID=" + select(.OutputKey == "PrivateSubnet2AID").OutputValue,
"PrivateSubnet3ID=" + select(.OutputKey == "PrivateSubnet3AID").OutputValue
'
) && echo " done."
# アクセス許可IPアドレスを、使用中の端末のグローバルIPアドレスにする
printf "グローバルIPアドレス取得..."
RemoteAccessCIDR="$(curl -s https://checkip.amazonaws.com/)/32" && echo " done."
# CFn起動
echo "CFnスタック ${StackName} 作成中..."
aws cloudformation create-stack \
--stack-name ${StackName} \
--region ${Region} \
--parameters \
ParameterKey=KeyPairName,ParameterValue="${KeyPairName}" \
ParameterKey=NodeInstanceType,ParameterValue="${NodeInstanceType}" \
ParameterKey=BastionInstanceType,ParameterValue="${BastionInstanceType}" \
ParameterKey=VPCID,ParameterValue="${VPCID}" \
ParameterKey=PublicSubnet1ID,ParameterValue="${PublicSubnet1ID}" \
ParameterKey=PublicSubnet2ID,ParameterValue="${PublicSubnet2ID}" \
ParameterKey=PublicSubnet3ID,ParameterValue="${PublicSubnet3ID}" \
ParameterKey=PrivateSubnet1ID,ParameterValue="${PrivateSubnet1ID}" \
ParameterKey=PrivateSubnet2ID,ParameterValue="${PrivateSubnet2ID}" \
ParameterKey=PrivateSubnet3ID,ParameterValue="${PrivateSubnet3ID}" \
ParameterKey=RemoteAccessCIDR,ParameterValue="${RemoteAccessCIDR}" \
--capabilities CAPABILITY_IAM \
--template-url ${TemplateUrl} && echo "done."
printf "作成完了 (CREATE_COMPLETE) まで待機 (およそ20分+)..."
aws cloudformation wait stack-create-complete \
--stack-name ${StackName} && echo " done."

実際にクイックスタートを動かされた方だとお分かりと思いますが、ここからさらにネストして複数の CFn スタックが起動され、さらには作成された Lambda や AutoScalingGroup から別の CFn スタックや EC2 が起動したりします。これらが完全に立ち上がるまで 20 分〜25 分前後かかりますので、気長に待ってみて下さい。
実行時には、下記のようなログが表示されるかと思います。

1
2
3
4
5
6
7
8
9
10
$ ./launch-amazon-eks-master-existing-vpc.sh
 
VPC 情報取得 (EKS-VPCStack)... done.
グローバル IP アドレス取得... done.
CFn スタック Amazon-EKS-EKSStack 作成中...
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1: ...
}
done.
作成完了 (CREATE_COMPLETE) まで待機 (およそ 20 分+)... done.

なお、第 1 引数に DELETE と指定した場合は、既存のスタック Amazon-EKS-EKSStack を削除します。
EKS クラスタを作り直したりするときにお使い下さい。

1
2
3
$ ./launch-amazon-eks-master-existing-vpc.sh DELETE
Amazon-EKS-EKSStack を削除しますか? (y/N): y
DELETING stack Amazon-EKS-EKSStack... done.

起動した EKS クラスタで何か動かしてみる

取りあえず何か動かしてみたいと思います。踏み台サーバ (LinuxBastion) に SSH ログインして、kubectl コマンドを叩いてみます。

1
2
3
4
5
$ kubectl get nodes
NAME                                             STATUS   ROLES    AGE   VERSION
ip-10-0-31-98.ap-northeast-1.compute.internal    Ready    <none>   22m   v1.14.8-eks-b8860f
ip-10-0-34-245.ap-northeast-1.compute.internal   Ready    <none>   22m   v1.14.8-eks-b8860f
ip-10-0-90-254.ap-northeast-1.compute.internal   Ready    <none>   22m   v1.14.8-eks-b8860f

3AZ ぶん Node も見えているし大丈夫そうですね。
試しに Sock Shop マイクロサービスデモアプリケーションを動かしてみることにします。

Sock Shop を EKS で動作させる手順については、@Mr-K 氏の公開されている Qiita の記事を参考にさせて頂きました。
こちらの記事が書かれた当時と違い、いまのクイックスタートで起動する踏み台ホストには最初から Git が入っているようです。

1
2
3
4
5
6
$ git clone https://github.com/microservices-demo/microservices-demo
$ cd microservices-demo/deploy/kubernetes/
$ sed -i 's/NodePort/LoadBalancer/g' complete-demo.yaml
 
$ kubectl create namespace sock-shop
$ kubectl apply -f complete-demo.yaml

確認がとれたら削除しましょう。EKSクラスタごと消してもいいですが、Sock Shopのみ削除する場合は下記のように実行します。

1
$ kubectl delete -f complete-demo.yaml

まとめ

EKS利用を前提としたVPCの構築と、その上で動くEKSクラスタの構築について、AWSが公開しているクイックスタートをベースに少しだけカスタマイズできる状態で進めてみました。

明日から eksctl を使ってみたいと思います。

脚注


  1. 白状すると、この記事を 9 割方書き上げるまで eksctl の存在をきれいに忘れていたというのが本当のところです 
  2. 負け惜しみです