Kubernetesをローカル環境で使用したことはありましたが、複数ホストをまたいだコンテナ管理に触れてみたいということで、Amazon EKSのチュートリアル をやってみました。ハマりポイントもあったので、勘所をまとめています。
#前提
- クライアント側の作業はmacOSで実施
- macOSにはAWS CLIとkubectlをインストールしていること
- AWS CLI:1.18.10以上
- kubectl:クラスターのマイナーバージョンとの差分が1つ以内のバージョン(kubectlのインストールおよびセットアップ 参照)
たとえば、クライアントがv1.2であれば、v1.1、v1.2、v1.3のマスターで動作するはずです。最新バージョンのkubectlを使うことで、不測の事態を避けることができるでしょう。
今回のゴール
今回構築するシステムの構成図はこちらです。
AWSリソースとKubernetesリソースを一緒に表現しているため、分かりにくいかもしれませんが、3つのAZ(サブネット)にまたがるWeb+Redisアプリケーションを構築します。EKSクラスターとは、Kubernetesの様々なリソースを管理する集合体のことを指します。
Kubernetesリソースの基本説明
リソース名 | 用途 |
---|---|
Node | コンテナを配置するためのホスト(EC2に相当) |
Pod | 複数のコンテナの集合体(単体のケースもある) |
Service | Podの集合体への通信経路を定義 |
EKSの利用方法
2通りの方法が存在しますが、今回はAWSマネジメントコンソールで実施しています。
- eksctlを使用
- AWSマネジメントコンソールを使用
作業手順
以下の作業を順番に実施していきます。
※ 詳細は [AWSマネジメントコンソールの開始方法] (https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/getting-started-console.html) を参照
EKSサービスロールを作成する
EKSクラスターが各種AWSリソースを参照・作成するために必要なIAMロールです。
下記のテンプレートを使用して、AWS CloudFormationコンソールでスタックを作成します。
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Service Role'
Resources:
eksServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- eks.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonEKSServicePolicy
- arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
Outputs:
RoleArn:
Description: The role that Amazon EKS will use to create AWS resources for Kubernetes clusters
Value: !GetAtt eksServiceRole.Arn
Export:
Name: !Sub "${AWS::StackName}-RoleArn"
EKSワーカーノードロールを作成する
EKSワーカーノードが各種AWSリソースを参照・作成するために必要なIAMロールです。
下記のS3テンプレートURLを指定して、AWS CloudFormationコンソールでスタックを作成します。
EKSクラスターVPCを作成する
下記のS3テンプレートURLを指定して、AWS CloudFormationコンソールでスタックを作成します。
今回、AZ (ap-northeast-1a
、ap-northeast-1c
、ap-northeast-1d
) にまたがる3つのパブリックサブネットを持つVPCを構築します。
スタック作成中に Template error: Fn::Select cannot select nonexistent value at index 2
エラーが発生した場合
使用しているAWSアカウントでは、default subnetにap-northeast-1dが含まれていない可能性が高いので、以下のコマンドを実行してから、再度スタックの作成をしてみてください。
$ aws ec2 create-default-subnet --availability-zone ap-northeast-1d
EKSクラスターを作成する
任意のクラスター名を入力して、事前に作成したEKSサービスロール、VPC、サブネット、セキュリティーグループを指定します。
kubeconfigファイルを作成する
$ aws eks --region ap-northeast-1 update-kubeconfig --name <cluster name>
Added new context arn:aws:eks:ap-northeast-1:************:cluster/<cluster name> to /Users/********/.kube/config
上記コマンドでkubectlのカレントの接続先にEKSクラスターが上書き設定されます。
※ kubeconfigについては、kubectlの接続設定ファイル(kubeconfig)の概要 を参照
EKSノードグループを起動する
EKSクラスター上に、ノードグループを起動します。
任意のノードグループ名を入力して、事前に作成したEKSワーカーノードロール、サブネットを指定します。
例)T3系の最大IP数
インスタンスタイプ | 最大ENI数(1) | ENIあたりIP数(2) | 最大IP数(1)×(2) |
---|---|---|---|
t3.micro | 2 | 2 | 4 |
t3.small | 3 | 4 | 12 |
t3.medium | 3 | 6 | 18 |
t3.large | 3 | 12 | 36 |
※ 詳細はElastic Network Interface を参照 |
なお、kube-systemと呼ばれるシステム用のNamespace上でもIPアドレスは利用されているため、それらを引いた数がPodで使用できるIP数となります。
$ kubectl get pod --namespace kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
aws-node-d5krq 1/1 Running 0 62m 192.168.220.97 ip-192-168-220-97.ap-northeast-1.compute.internal <none> <none>
aws-node-flh75 1/1 Running 0 62m 192.168.152.228 ip-192-168-152-228.ap-northeast-1.compute.internal <none> <none>
aws-node-mnpxl 1/1 Running 0 62m 192.168.113.251 ip-192-168-113-251.ap-northeast-1.compute.internal <none> <none>
coredns-58986cd576-kc8xv 1/1 Running 0 95m 192.168.171.225 ip-192-168-152-228.ap-northeast-1.compute.internal <none> <none>
coredns-58986cd576-rqwqs 1/1 Running 0 95m 192.168.225.139 ip-192-168-220-97.ap-northeast-1.compute.internal <none> <none>
kube-proxy-j9lk5 1/1 Running 0 62m 192.168.152.228 ip-192-168-152-228.ap-northeast-1.compute.internal <none> <none>
kube-proxy-jgmfb 1/1 Running 0 62m 192.168.220.97 ip-192-168-220-97.ap-northeast-1.compute.internal <none> <none>
kube-proxy-rfjtd 1/1 Running 0 62m 192.168.113.251 ip-192-168-113-251.ap-northeast-1.compute.internal <none> <none>
サンプルアプリケーションを起動する
ここからサンプルのゲストブックアプリケーションを作成します。
※ 詳細は [ゲストブックアプリケーションを起動する] (https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/eks-guestbook.html) を参照
Redisマスターレプリケーションコントローラーを作成 (Pod作成)
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-controller.json
Redisマスターサービスを作成 (Service作成)
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-service.json
RedisマスターPodがノードグループのうち1ノードに配置され、そのPodへの通信経路であるServiceも作成されています。
$ kubectl get pod,svc -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/redis-master-5l8nr 1/1 Running 0 9m25s 192.168.74.238 ip-192-168-113-251.ap-northeast-1.compute.internal <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 11h <none>
service/redis-master ClusterIP 10.100.59.64 <none> 6379/TCP 4m31s app=redis,role=master
PodとServiceの関係性について、もう少し触れておくと、実行した下記の定義ファイルにあるように、PodのラベルとServiceのセレクタが合致した場合、対象のPodはそのServiceのターゲットとなり、Serviceを経由してトラフィックが流れる仕組みになっています。
"template":{
"metadata":{
"labels":{
"app":"redis",
"role":"master"
}
},
"selector":{
"app":"redis",
"role":"master"
}
Redisスレーブレプリケーションコントローラーを作成 (Pod作成)
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-controller.json
Redisスレーブサービスを作成 (Service作成)
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-service.json
RedisスレーブPod(レプリカ数2)がノードグループのうち2ノードに配置され、そのPodへの通信経路であるServiceも作成されています。
$ kubectl get pod,svc -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/redis-master-5l8nr 1/1 Running 0 28m 192.168.74.238 ip-192-168-113-251.ap-northeast-1.compute.internal <none> <none>
pod/redis-slave-hqpd2 1/1 Running 0 4m48s 192.168.192.184 ip-192-168-220-97.ap-northeast-1.compute.internal <none> <none>
pod/redis-slave-nrnwk 1/1 Running 0 4m48s 192.168.133.49 ip-192-168-152-228.ap-northeast-1.compute.internal <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 11h <none>
service/redis-master ClusterIP 10.100.59.64 <none> 6379/TCP 23m app=redis,role=master
service/redis-slave ClusterIP 10.100.106.145 <none> 6379/TCP 4m34s app=redis,role=slave
ゲストブックレプリケーションコントローラーを作成 (Pod作成)
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-controller.json
ゲストブックサービスを作成 (Service作成)
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-service.json
ゲストブックPod(レプリカ数3)がノードグループのうち3ノードに均等に配置され、そのPodへの通信経路であるServiceも作成されています。
$ kubectl get pod,svc -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/guestbook-2wcm6 1/1 Running 0 4m20s 192.168.222.236 ip-192-168-220-97.ap-northeast-1.compute.internal <none> <none>
pod/guestbook-cnl46 1/1 Running 0 4m20s 192.168.124.194 ip-192-168-113-251.ap-northeast-1.compute.internal <none> <none>
pod/guestbook-smjjm 1/1 Running 0 4m20s 192.168.160.43 ip-192-168-152-228.ap-northeast-1.compute.internal <none> <none>
pod/redis-master-5l8nr 1/1 Running 0 38m 192.168.74.238 ip-192-168-113-251.ap-northeast-1.compute.internal <none> <none>
pod/redis-slave-hqpd2 1/1 Running 0 14m 192.168.192.184 ip-192-168-220-97.ap-northeast-1.compute.internal <none> <none>
pod/redis-slave-nrnwk 1/1 Running 0 14m 192.168.133.49 ip-192-168-152-228.ap-northeast-1.compute.internal <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/guestbook LoadBalancer 10.100.186.33 ac867331c722611ea8bc70e5c3799917-1903484356.ap-northeast-1.elb.amazonaws.com 3000:32317/TCP 4m13s app=guestbook
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 12h <none>
service/redis-master ClusterIP 10.100.59.64 <none> 6379/TCP 33m app=redis,role=master
service/redis-slave ClusterIP 10.100.106.145 <none> 6379/TCP 14m app=redis,role=slave
システム構成も冒頭に説明した 今回のゴール 通りに作られているようです。
ゲストブックアプリケーションにブラウザでアクセス
http://ac867331c722611ea8bc70e5c3799917-1903484356.ap-northeast-1.elb.amazonaws.com:3000
無事、ゲストブックアプリケーションが表示されました。
さいごに
今回のチュートリアルでは、作成したRedisおよびゲストブックPodが、均等に各ノードに配置されています。これにはタネも仕掛けも分からず、どこか釈然としない気持ちになってしまいましたが、Kubernetesの公式ドキュメントに以下の一節を見つけました。
Node上へのPodのスケジューリング
Podが稼働するNodeを特定のものに指定したり、優先条件を指定して制限することができます。 これを実現するためにはいくつかの方法がありますが、推奨されている方法はラベルでの選択です。 スケジューラーが最適な配置を選択するため、一般的にはこのような制限は不要です。
柔軟な指定もできるようですが、スケジューラー(Podのノードへの割り当てをつかさどるマスターコンポーネント)が頑張ってくれるようです。流石。