eksctl が何をやってくれるのが、何ができるのかを確認します。
eksctl create cluster
いきなり eksctl create cluster
を実行するだけでクラスタが作れるっぽいのでひとまず試してみる。
$ eksctl create cluster
[ℹ] eksctl version 0.12.0
[ℹ] using region ap-northeast-1
[ℹ] setting availability zones to [ap-northeast-1c ap-northeast-1d ap-northeast-1b]
[ℹ] subnets for ap-northeast-1c - public:192.168.0.0/19 private:192.168.96.0/19
[ℹ] subnets for ap-northeast-1d - public:192.168.32.0/19 private:192.168.128.0/19
[ℹ] subnets for ap-northeast-1b - public:192.168.64.0/19 private:192.168.160.0/19
[ℹ] nodegroup "ng-bb178f10" will use "ami-07296175bc6b826a5" [AmazonLinux2/1.14]
[ℹ] using Kubernetes version 1.14
[ℹ] creating EKS cluster "beautiful-badger-1578666058" in "ap-northeast-1" region with un-managed nodes
[ℹ] will create 2 separate CloudFormation stacks for cluster itself and the initial nodegroup
[ℹ] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-1 --cluster=beautiful-badger-1578666058'
[ℹ] CloudWatch logging will not be enabled for cluster "beautiful-badger-1578666058" in "ap-northeast-1"
[ℹ] you can enable it with 'eksctl utils update-cluster-logging --region=ap-northeast-1 --cluster=beautiful-badger-1578666058'
[ℹ] Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "beautiful-badger-1578666058" in "ap-northeast-1"
[ℹ] 2 sequential tasks: { create cluster control plane "beautiful-badger-1578666058", create nodegroup "ng-bb178f10" }
[ℹ] building cluster stack "eksctl-beautiful-badger-1578666058-cluster"
[ℹ] deploying stack "eksctl-beautiful-badger-1578666058-cluster"
VPC から一式全部作る CloudFormation の stack (ここでは eksctl-beautiful-badger-1578666058-cluster という名前) が作成されて deploy が開始されました。AWS Console で CloudFormation の stack を確認すれば状況がわかります。ありゃ?エラーが…
[✖] unexpected status "ROLLBACK_IN_PROGRESS" while waiting for CloudFormation stack "eksctl-beautiful-badger-1578666058-nodegroup-ng-bb178f10"
[ℹ] fetching stack events in attempt to troubleshoot the root cause of the failure
[!] AWS::EC2::SecurityGroupEgress/EgressInterClusterAPI: DELETE_IN_PROGRESS
[!] AWS::EC2::SecurityGroupIngress/IngressInterClusterAPI: DELETE_IN_PROGRESS
[!] AWS::EC2::SecurityGroupIngress/IngressInterClusterCP: DELETE_IN_PROGRESS
[!] AWS::EC2::SecurityGroupEgress/EgressInterCluster: DELETE_IN_PROGRESS
[!] AWS::AutoScaling::AutoScalingGroup/NodeGroup: DELETE_IN_PROGRESS
[!] AWS::EC2::SecurityGroupIngress/IngressInterCluster: DELETE_IN_PROGRESS
[✖] AWS::AutoScaling::AutoScalingGroup/NodeGroup: CREATE_FAILED – "AWS was not able to validate the provided access credentials (Service: AmazonAutoScaling; Status Code: 400; Error Code: ValidationError; Request ID: a31c5a54-33b6-11ea-9896-5308bff4139a)"
[ℹ] 1 error(s) occurred and cluster hasn't been created properly, you may wish to check CloudFormation console
[ℹ] to cleanup resources, run 'eksctl delete cluster --region=ap-northeast-1 --name=beautiful-badger-1578666058'
[✖] waiting for CloudFormation stack "eksctl-beautiful-badger-1578666058-nodegroup-ng-bb178f10": ResourceNotReady: failed waiting for successful resource state
Error: failed to create cluster "beautiful-badger-1578666058"
NodeGroup の作成でこけたらしいことは分かったが… 😣
AWS was not able to validate the provided access credentials (Service: AmazonAutoScaling; Status Code: 400; Error Code: ValidationError; Request ID: a31c5a54-33b6-11ea-9896-5308bff4139a)
再チャレンジしたら今度は別のところでコケた…
[✖] AWS::EC2::Subnet/SubnetPublicAPNORTHEAST1A: CREATE_FAILED – "Value (ap-northeast-1a) for parameter availabilityZone is invalid. Subnets can currently only be created in the following availability zones: ap-northeast-1d, ap-northeast-1c, ap-northeast-1b. (Service: AmazonEC2; Status Code: 400; Error Code: InvalidParameterValue; Request ID: 28f3bfed-2939-4efd-ad9c-7f6152d6b041)"
[✖] AWS::EC2::Subnet/SubnetPrivateAPNORTHEAST1A: CREATE_FAILED – "Value (ap-northeast-1a) for parameter availabilityZone is invalid. Subnets can currently only be created in the following availability zones: ap-northeast-1b, ap-northeast-1c, ap-northeast-1d. (Service: AmazonEC2; Status Code: 400; Error Code: InvalidParameterValue; Request ID: 0e402395-2b61-4b7f-ac5b-6e9f01625f0c)"
この後、数回試したが、全部この AZ の問題でコケた…
それでもあと1回、あと1回と思って試してたら成功した。(eksctl create cluster –zones=ap-northeast-1b,ap-northeast-1c,ap-northeast-1d と –zones を指定すれば良いのかな) しかし、AZ の問題はわかるんだけど、初回のエラーは必ず再発するのかと思ってたので意外
$ eksctl create cluster
[ℹ] eksctl version 0.12.0
[ℹ] using region ap-northeast-1
[ℹ] setting availability zones to [ap-northeast-1c ap-northeast-1d ap-northeast-1b]
[ℹ] subnets for ap-northeast-1c - public:192.168.0.0/19 private:192.168.96.0/19
[ℹ] subnets for ap-northeast-1d - public:192.168.32.0/19 private:192.168.128.0/19
[ℹ] subnets for ap-northeast-1b - public:192.168.64.0/19 private:192.168.160.0/19
[ℹ] nodegroup "ng-c384e850" will use "ami-07296175bc6b826a5" [AmazonLinux2/1.14]
[ℹ] using Kubernetes version 1.14
[ℹ] creating EKS cluster "wonderful-painting-1578669435" in "ap-northeast-1" region with un-managed nodes
[ℹ] will create 2 separate CloudFormation stacks for cluster itself and the initial nodegroup
[ℹ] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-1 --cluster=wonderful-painting-1578669435'
[ℹ] CloudWatch logging will not be enabled for cluster "wonderful-painting-1578669435" in "ap-northeast-1"
[ℹ] you can enable it with 'eksctl utils update-cluster-logging --region=ap-northeast-1 --cluster=wonderful-painting-1578669435'
[ℹ] Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "wonderful-painting-1578669435" in "ap-northeast-1"
[ℹ] 2 sequential tasks: { create cluster control plane "wonderful-painting-1578669435", create nodegroup "ng-c384e850" }
[ℹ] building cluster stack "eksctl-wonderful-painting-1578669435-cluster"
[ℹ] deploying stack "eksctl-wonderful-painting-1578669435-cluster"
[ℹ] building nodegroup stack "eksctl-wonderful-painting-1578669435-nodegroup-ng-c384e850"
[ℹ] --nodes-min=2 was set automatically for nodegroup ng-c384e850
[ℹ] --nodes-max=2 was set automatically for nodegroup ng-c384e850
[ℹ] deploying stack "eksctl-wonderful-painting-1578669435-nodegroup-ng-c384e850"
[✔] all EKS cluster resources for "wonderful-painting-1578669435" have been created
[✔] saved kubeconfig as "/Users/teraoka/.kube/config"
[ℹ] adding identity "arn:aws:iam::949160801735:role/eksctl-wonderful-painting-1578669-NodeInstanceRole-18QXALFB6Y0W2" to auth ConfigMap
[ℹ] nodegroup "ng-c384e850" has 0 node(s)
[ℹ] waiting for at least 2 node(s) to become ready in "ng-c384e850"
[ℹ] nodegroup "ng-c384e850" has 2 node(s)
[ℹ] node "ip-192-168-12-245.ap-northeast-1.compute.internal" is ready
[ℹ] node "ip-192-168-94-158.ap-northeast-1.compute.internal" is ready
[ℹ] kubectl command should work with "/Users/teraoka/.kube/config", try 'kubectl get nodes'
[✔] EKS cluster "wonderful-painting-1578669435" in "ap-northeast-1" region is ready
~/.kube/config
に保存したよって出てるから kubectl コマンドを試してみる。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-12-245.ap-northeast-1.compute.internal Ready 3m25s v1.14.8-eks-b8860f
ip-192-168-94-158.ap-northeast-1.compute.internal Ready 3m24s v1.14.8-eks-b8860f
$ kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.100.0.1 443/TCP 16m
kube-system kube-dns ClusterIP 10.100.0.10 53/UDP,53/TCP 16m
$ kubectl get ns
NAME STATUS AGE
default Active 17m
kube-node-lease Active 17m
kube-public Active 17m
kube-system Active 17m
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system aws-node-b74p7 1/1 Running 0 11m
kube-system aws-node-xx89x 1/1 Running 0 11m
kube-system coredns-58986cd576-nc45g 1/1 Running 0 17m
kube-system coredns-58986cd576-wcxcj 1/1 Running 0 17m
kube-system kube-proxy-cfqcd 1/1 Running 0 11m
kube-system kube-proxy-kzlgz 1/1 Running 0 11m
動いてる 😍
さて、どんなリソースが作られているのか。
- VPC
- VPC から新しく作られる
- Subnet
- Public と Private という2種類の Subnet がそれぞれ3つの Availability zone に作成される
- Internet Gateway
- 新しい VPC を作って Public subnet を作成するので当然 Internet Gateway も必要になる
- Role
- ServiceRole (Controle Plane 用)、NodeInstanceRole (EC2 Workder Node 用)、FargatePodExecutionRole の3つが作成される。ServiceRole は EKS のドキュメントにある AmazonEKSClusterPolicy、AmazonEKSServicePolicy と eksctl が作る PutMetrics 用 Policy と NLB 管理用の Policy が紐付けられている。NodeInstanceRole は EKS のドキュメントにある AmazonEKSWorkerNodePolicy、AmazonEC2ContainerRegistryReadOnly、AmazonEKS_CNI_Policy の3つが、FargatePodExecutionRole には AmazonEKSFargatePodExecutionRolePolicy が紐付けられている
- NAT Gateway
- Private subnet があるので NAT Gateway も作成される。デフォルトでは1つの Availability zone にしか作られないが、–vpc-nat-mode で HighlyAvailable, Single, Disable から選択して指定することが可能。デフォルトは Single
- SecurityGroup
- クラスタ内の node 間での通信用に作成される
- ControlePlane
- もちろん Kubernetes の Controle Plane が作られる
- AutoScalingGroup
- Worker node 用の AutoScalingGroup が作られる
- LaunchTemplate
- Worker node 用の AutoScalingGroup で使われる LaunchTemplate で AMI Image や InstanceType、InstanceProflie、Userdata が設定されている
Worker node として EC2 インスタンスが作成されたらログインしてみたいはず。でも eksctl create cluster
に --ssh-access
(ファイルを指定するなら --ssh-public-key
も) をつけておかないと Public Key が設定されないため worker node へのアクセスには EC2 Instance Connect で接続する必要がある。aws-instance-connect-cli を使えば mssh <instance-id>
だけで接続できる。 しかし Instance Connect は Instance Metadata Service v1 (IDMSv1) しかサポートしていないらしい。接続先インスタンスは Global IP address を持っている必要があり、SSH (22/tcp) が SecurityGroup で許可されている必要がある。Web Console から AWS のアドレスからの接続を許可しておく必要がある。また、接続する人は IAM Policy で ec2-instance-connect:SendSSHPublicKey 権限を持っている必要がある。公開鍵を送りつけて一時的にログインできるようにしてくれるんですね。
ところで、worker node にログインしてみたら Pod ごとに2つのコンテナが起動していました。複数コンテナが指定された Pod じゃないのになんでだろ?って思ったらどれも “/pause” が実行されてるものと、名前に合ったプログラムが実行されているコンテナの2つでした。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df061216904c 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/coredns "/coredns -conf /etc…" 2 hours ago Up 2 hours k8s_coredns_coredns-58986cd576-6kl57_kube-system_00aec73c-3449-11ea-a7e3-069c17bfdc00_0
379bc8e2844a 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause-amd64:3.1 "/pause" 2 hours ago Up 2 hours k8s_POD_coredns-58986cd576-6kl57_kube-system_00aec73c-3449-11ea-a7e3-069c17bfdc00_0
09ee64a82565 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni "/bin/sh -c /app/ins…" 2 hours ago Up 2 hours k8s_aws-node_aws-node-7l99l_kube-system_6f2001d2-344e-11ea-a7e3-069c17bfdc00_0
79eef819a1f4 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/kube-proxy "kube-proxy --v=2 --…" 2 hours ago Up 2 hours k8s_kube-proxy_kube-proxy-d599c_kube-system_6f202763-344e-11ea-a7e3-069c17bfdc00_0
40ae949a2e0c 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause-amd64:3.1 "/pause" 2 hours ago Up 2 hours k8s_POD_aws-node-7l99l_kube-system_6f2001d2-344e-11ea-a7e3-069c17bfdc00_0
eeed9d5bf85f 602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause-amd64:3.1 "/pause" 2 hours ago Up 2 hours k8s_POD_kube-proxy-d599c_kube-system_6f202763-344e-11ea-a7e3-069c17bfdc00_0
The Almighty Pause Container ってのを見つけた。これが Pod という複数コンテナをまとめるキモだったんですね。
Worker node は起動時に Kubernetes のクラスタに参加する必要があるが、これは AutoScalingGroup の LaunchTemplate に userdata 設定があります。テキストが gzip されて base64 エンコードされていました、デコードすると次の内容でした。
userdata
#cloud-config
packages: null
runcmd:
- /var/lib/cloud/scripts/per-instance/bootstrap.al2.sh
write_files:
- content: |
# eksctl-specific systemd drop-in unit for kubelet, for Amazon Linux 2 (AL2)
[Service]
# Local metadata parameters: REGION, AWS_DEFAULT_REGION
EnvironmentFile=/etc/eksctl/metadata.env
# Global and static parameters: CLUSTER_DNS, NODE_LABELS, NODE_TAINTS
EnvironmentFile=/etc/eksctl/kubelet.env
# Local non-static parameters: NODE_IP, INSTANCE_ID
EnvironmentFile=/etc/eksctl/kubelet.local.env
ExecStart=
ExecStart=/usr/bin/kubelet \
--node-ip=${NODE_IP} \
--node-labels=${NODE_LABELS},alpha.eksctl.io/instance-id=${INSTANCE_ID} \
--max-pods=${MAX_PODS} \
--register-node=true --register-with-taints=${NODE_TAINTS} \
--allow-privileged=true \
--cloud-provider=aws \
--container-runtime=docker \
--network-plugin=cni \
--cni-bin-dir=/opt/cni/bin \
--cni-conf-dir=/etc/cni/net.d \
--pod-infra-container-image=${AWS_EKS_ECR_ACCOUNT}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/eks/pause-amd64:3.1 \
--kubeconfig=/etc/eksctl/kubeconfig.yaml \
--config=/etc/eksctl/kubelet.yaml
owner: root:root
path: /etc/systemd/system/kubelet.service.d/10-eksclt.al2.conf
permissions: "0644"
- content: |-
NODE_LABELS=alpha.eksctl.io/cluster-name=hilarious-hideout-1578729265,alpha.eksctl.io/nodegroup-name=ng-b50c30b5
NODE_TAINTS=
owner: root:root
path: /etc/eksctl/kubelet.env
permissions: "0644"
- content: |
address: 0.0.0.0
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/eksctl/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
cgroupDriver: cgroupfs
clusterDNS:
- 10.100.0.10
clusterDomain: cluster.local
featureGates:
RotateKubeletServerCertificate: true
kind: KubeletConfiguration
serverTLSBootstrap: true
owner: root:root
path: /etc/eksctl/kubelet.yaml
permissions: "0644"
- content: |
-----BEGIN CERTIFICATE-----
(省略)
-----END CERTIFICATE-----
owner: root:root
path: /etc/eksctl/ca.crt
permissions: "0644"
- content: |
apiVersion: v1
clusters:
- cluster:
certificate-authority: /etc/eksctl/ca.crt
server: https://7950C8F5B4E86FAF920EB443279E8896.sk1.ap-northeast-1.eks.amazonaws.com
name: hilarious-hideout-1578729265.ap-northeast-1.eksctl.io
contexts:
- context:
cluster: hilarious-hideout-1578729265.ap-northeast-1.eksctl.io
user: kubelet@hilarious-hideout-1578729265.ap-northeast-1.eksctl.io
name: kubelet@hilarious-hideout-1578729265.ap-northeast-1.eksctl.io
current-context: kubelet@hilarious-hideout-1578729265.ap-northeast-1.eksctl.io
kind: Config
preferences: {}
users:
- name: kubelet@hilarious-hideout-1578729265.ap-northeast-1.eksctl.io
user:
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
args:
- token
- -i
- hilarious-hideout-1578729265
command: aws-iam-authenticator
env: null
owner: root:root
path: /etc/eksctl/kubeconfig.yaml
permissions: "0644"
- content: |
m5.24xlarge 737
g4dn.4xlarge 29
i3.xlarge 58
... (省略) ...
t2.nano 4
c3.xlarge 58
c5.large 29
owner: root:root
path: /etc/eksctl/max_pods.map
permissions: "0644"
- content: |-
AWS_DEFAULT_REGION=ap-northeast-1
AWS_EKS_CLUSTER_NAME=hilarious-hideout-1578729265
AWS_EKS_ENDPOINT=https://7950C8F5B4E86FAF920EB443279E8896.sk1.ap-northeast-1.eks.amazonaws.com
AWS_EKS_ECR_ACCOUNT=602401143452
owner: root:root
path: /etc/eksctl/metadata.env
permissions: "0644"
- content: |
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
function get_max_pods() {
while read instance_type pods; do
if [[ "${instance_type}" == "${1}" ]] && [[ "${pods}" =~ ^[0-9]+$ ]] ; then
echo ${pods}
return
fi
done < /etc/eksctl/max_pods.map
}
NODE_IP="$(curl --silent http://169.254.169.254/latest/meta-data/local-ipv4)"
INSTANCE_ID="$(curl --silent http://169.254.169.254/latest/meta-data/instance-id)"
INSTANCE_TYPE="$(curl --silent http://169.254.169.254/latest/meta-data/instance-type)"
source /etc/eksctl/kubelet.env # this can override MAX_PODS
cat > /etc/eksctl/kubelet.local.env <<EOF
NODE_IP=${NODE_IP}
INSTANCE_ID=${INSTANCE_ID}
INSTANCE_TYPE=${INSTANCE_TYPE}
MAX_PODS=${MAX_PODS:-$(get_max_pods "${INSTANCE_TYPE}")}
EOF
systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet
owner: root:root
path: /var/lib/cloud/scripts/per-instance/bootstrap.al2.sh
permissions: "0755"
でも今は Managed Node Group ってのがあるのでこれを使うのが良いのかな。eksctl create cluster に –managed をつけるだけで良さそう。また、–fargate をつければ Fargate で実行する設定が入るっぽい。この辺りを確認して terraform で EKS 環境を構築できるようにしてみることにする。