이전 글은 Cluster API 가 어떤 것이고 어떻게 동작하는지를 알아봤다면 이번에는 AWS 에 Cluster API를 이용하여 Kubernetes 를 쉽게 설치하는 방법을 설명한다. 사실 AWS를 어느 정도 알고있어야 (특히 VPC, Subnet, Route Table, Nat Gateway, Internet Gateway 같은 네트워크) 해당 내용들을 이해하기가 쉽기 때문에 Cluster API 로 구현되는 최종 Kubernetes Cluster 구성에 대해서 먼저 설명하겠다.
특정 리전 (여기서는 서울)에 3개의 가용존(AZ1~3)에 Kubernetes Master 와 Node 가 설치되게 된다. 하나의 AZ에는 public subnet 과 private subnet 이 만들어지며 public subnet은 인터넷과 통하는 Internet Gateway 와 연결되어 있다. 네트워크 통신을 위해서는 Route Table 을 통해서 default gateway (0.0.0.0/0)를 설정해야 하는데 public subnet 은 Internet Gateway 를 private subnet 은 EIP 가 할당되어 있는 NAT gateway 를 설정한다.
AWS 에서는 Internet Gateway 와 연결되어 있는데 subnet 을 public subnet 으로 명명하며 public IP 가 할당되는 자원들은 모두 public subnet 에 생성한다. 그 외의 subnet 은 private subnet 이라 명명하는데, 이 때 외부 outbound 연결을 하기 위해서는 public subnet 에 NAT Gateway 를 만들어 활용한다. 보라색 점선이 Internet Gateway 와 NAT Gateway 로 default gateway 를 설정한 내용을 보여준다.
요약하면 AWS 에서의 네트워크는 Subnet, Route table, Gateway (Internet 혹은 NAT) 로 구성된다.
모든 VM (Control plane과 Node) instance는 private subnet에 생성되어 외부에서 직접 VM 으로 접속하는 경로를 차단한다. 필요에 따라 외부의 Inbound 를 트래픽을 가능하게 하려면 Load Balancer 를 통해서 연결한다(물론 Internal LB도 가능하다). Load Balancer 가 private subent 의 VM 에 연결되기 위해서는 public subnet 이 LB에 등록되어야 한다. 다이어그램에서 이를 표현한 것이 초록색 점선이다. LB 와 Control plane 을 연결한 것은 API Server 가 LB 에 등록되어 로드밸랜싱된다는 의미이며, 고객 서비스의 연결은 Node VM 에 연결될 수 있다. 이 때 Service 리소스의 Type 을 LoadBalancer 로 지정하면 자동으로 연결된다.
이 기능들이 가능한 이유는 Kubernetes 에 Cloud provider controller 가 내장되어 있기 때문이다. 하지만 이 기능은 deprecated 될 예정으로 버전 1.23 부터는 소스가 분리될 예정이다.
또 한가지 일반적인 방법으로 설치하면 그림의 내용과 같이 모든 Node VM 이 AZ1 의 private subnet 에 몰려서 생성된다. 이는 가용성에 문제가 될 수 있으며 이를 해결하기 위해서는 아직은 experimental 버전인 MachinePool 기능을 사용해야 한다.
이제 본격적으로 Cluster API 로 Kubernetes Cluster 를 설치해 보자.
이전 글을 보면 Management cluster 에 Cluster API Controller 를 설치한 후 Custom Resource (CR) 를 생성하면 자동으로 Workload cluster 가 생성된다는 것을 설명하였다. 이를 잘 기억해 두고 Management cluster 는 존재한다는 가정하에 시작한다(인터넷에서 조회해보면 Kind 로 쉽게 Management cluster 를 구성하는 방법을 찾을 수 있다).
1. clusterctl 설치
clusterctl 은 Managed cluster 에 Cluster API controller 를 설치하고 Workload cluster 를 위한 CR 생성을 도와주는 도구이다.
$ curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.20/clusterctl-linux-amd64 -o clusterctl
$ chmod +x ./clusterctl
$ mv ./clusterctl /usr/local/bin/clusterctl
$ clusterctl version
=== output ===
clusterctl version: &version.Info{Major:"0", Minor:"3", GitVersion:"v0.3.20", GitCommit:"ea9dc4bdc2a9938325aab3817ece3e654873aaab", GitTreeState:"clean", BuildDate:"2021-06-30T22:10:51Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}
2. clusterawsadm 설치
clusterawsadm 은 aws 에 필요한 Role 과 Policy 를 자동으로 생성해 주는 도구이다.
$ curl -L https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/download/v0.6.6/clusterawsadm-linux-amd64 -o clusterawsadm
$ chmod +x clusterawsadm
$ mv ./clusterawsadm /usr/local/bin/clusterawsadm
$ clusterawsadm version
=== output ===
clusterawsadm version: &version.Info{Major:"0", Minor:"6", GitVersion:"v0.6.6-4-d4593daa95fb96-dirty", GitCommit:"d4593daa95fb961be91dc6db869f26ca4359ebc0", GitTreeState:"dirty", BuildDate:"2021-06-01T20:05:33Z", GoVersion:"go1.13.15", AwsSdkVersion:"v1.36.26", Compiler:"gc", Platform:"linux/amd64"}
3. awscli 및 jq 설치
$ curl -L https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.2.13.zip -o awscliv2.zip
$ unzip awscliv2.zip
$ ./aws/install
$ aws --version
=== output ===
aws-cli/2.2.13 Python/3.8.8 Linux/4.4.0-87-generic exe/x86_64.ubuntu.16 prompt/off
$ mkdir -p ~/.aws
$ vi ~/.aws/credentials
[default]
aws_access_key_id=<< access_key_id >>
aws_secret_access_key=<< secret_access_key >>
$ vi ~/.aws/config
[default]
region = ap-northeast-2
4. 사전 작업
clusterawsadm 을 활용하여 IAM Role 과 Policy 를 생성한다.
## 설정이 안되어 있을 때 환경 변수로 설정한다.
$ export AWS_REGION=ap-northeast-2
$ export AWS_ACCESS_KEY_ID=<< access_key_id >>
$ export AWS_SECRET_ACCESS_KEY=<< secret_access_key >>
$ clusterawsadm bootstrap iam create-cloudformation-stack
Workload cluster VM 에 접속할 key pair 를 Import 한다.
$ aws ec2 import-key-pair \
--key-name capi-seungkyu \
--public-key-material fileb://~/.ssh/id_rsa.pub
5. Management cluster 생성
clusterctl 생성에 필요한 환경 변수를 다음과 같이 설정한다.
$ clusterawsadm bootstrap credentials encode-as-profile
=== output ===
<< crendentials >>
$ vi env.sh
export AWS_REGION=ap-northeast-2
export AWS_ACCESS_KEY_ID=<< access_key_id >>
export AWS_SECRET_ACCESS_KEY=<< secret_access_key >>
export AWS_B64ENCODED_CREDENTIALS=<< crendentials >>
$ source ./env.sh
clusterctl init 명령어를 사용하여 Management cluster 를 생성한다.
$ clusterctl init --core cluster-api:v0.3.20 --infrastructure aws:v0.6.6 --bootstrap kubeadm:v0.3.20 --control-plane kubeadm:v0.3.20 -v5
=== output ===
...
Fetching providers
Installing cert-manager
Waiting for cert-manager to be available...
...
Installing Provider="cluster-api" Version="v0.3.20" TargetNamespace="capi-system"
...
Installing Provider="bootstrap-kubeadm" Version="v0.3.20" TargetNamespace="capi-kubeadm-bootstrap-system"
...
Installing Provider="control-plane-kubeadm" Version="v0.3.20" TargetNamespace="capi-kubeadm-control-plane-system"
...
Installing Provider="infrastructure-aws" Version="v0.6.6" TargetNamespace="capa-system"
Your management cluster has been initialized successfully!
You can now create your first workload cluster by running the following:
clusterctl config cluster [name] --kubernetes-version [version] | kubectl apply -f -
6. Workload cluster 생성
필요한 환경변수를 설정하고 clusterctl config 명령어를 이용하여 CR 파일을 생성한다.
$ vi env-workload.sh
export AWS_CONTROL_PLANE_MACHINE_TYPE=t3.large
export AWS_NODE_MACHINE_TYPE=t3.large
export AWS_SSH_KEY_NAME=capi-seungkyu
$ source ./env-workload.sh
$ clusterctl config cluster capi-quickstart \
-n capi-quickstart \
--kubernetes-version v1.20.5 \
--control-plane-machine-count=3 \
--worker-machine-count=3 \
> capi-quickstart.yaml
capi-quickstart.yaml 을 확인하면 아래와 같다.
$ cat capi-quickstart.yaml
---
apiVersion: cluster.x-k8s.io/v1alpha3
kind: Cluster
metadata:
name: capi-quickstart
namespace: capi-quickstart
spec:
clusterNetwork:
pods:
cidrBlocks:
- 192.168.0.0/16
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: KubeadmControlPlane
name: capi-quickstart-control-plane
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSCluster
name: capi-quickstart
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSCluster
metadata:
name: capi-quickstart
namespace: capi-quickstart
spec:
region: ap-northeast-2
sshKeyName: capi-seungkyu
---
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: KubeadmControlPlane
metadata:
name: capi-quickstart-control-plane
namespace: capi-quickstart
spec:
infrastructureTemplate:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSMachineTemplate
name: capi-quickstart-control-plane
kubeadmConfigSpec:
clusterConfiguration:
apiServer:
extraArgs:
cloud-provider: aws
controllerManager:
extraArgs:
cloud-provider: aws
initConfiguration:
nodeRegistration:
kubeletExtraArgs:
cloud-provider: aws
name: '{{ ds.meta_data.local_hostname }}'
joinConfiguration:
nodeRegistration:
kubeletExtraArgs:
cloud-provider: aws
name: '{{ ds.meta_data.local_hostname }}'
replicas: 3
version: v1.20.5
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSMachineTemplate
metadata:
name: capi-quickstart-control-plane
namespace: capi-quickstart
spec:
template:
spec:
iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
instanceType: t3.large
sshKeyName: capi-seungkyu
---
apiVersion: cluster.x-k8s.io/v1alpha3
kind: MachineDeployment
metadata:
name: capi-quickstart-md-0
namespace: capi-quickstart
spec:
clusterName: capi-quickstart
replicas: 3
selector:
matchLabels: null
template:
spec:
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
kind: KubeadmConfigTemplate
name: capi-quickstart-md-0
clusterName: capi-quickstart
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSMachineTemplate
name: capi-quickstart-md-0
version: v1.20.5
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSMachineTemplate
metadata:
name: capi-quickstart-md-0
namespace: capi-quickstart
spec:
template:
spec:
iamInstanceProfile: nodes.cluster-api-provider-aws.sigs.k8s.io
instanceType: t3.large
sshKeyName: capi-seungkyu
---
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
kind: KubeadmConfigTemplate
metadata:
name: capi-quickstart-md-0
namespace: capi-quickstart
spec:
template:
spec:
joinConfiguration:
nodeRegistration:
kubeletExtraArgs:
cloud-provider: aws
name: '{{ ds.meta_data.local_hostname }}'
해당 CR 은 아래 다이어그램 구조로 매칭된다.
Cluster API 는 공통이 Abstract Class 와 같이 기본을 정의해 놓았으며, Provider 는 실제 Cloud 에 맞는 구현을 정의하였다. AWSCluster 에는 VPC, subnet, Route table 등과 같은 인프라 생성 정보를 가지고 있고 AWSMachineTemplate 은 Kubernetes Control Plane 에 대한 정보를 AWSMachineTemplate 에는 Node 정보를 갖고 있다.
CR 을 배포하여 Workload cluster 를 생성한다.
$ kubectl create ns capi-quickstart
$ kubectl apply -f capi-quickstart.yaml
=== output ===
cluster.cluster.x-k8s.io/capi-quickstart created
awscluster.infrastructure.cluster.x-k8s.io/capi-quickstart created
kubeadmcontrolplane.controlplane.cluster.x-k8s.io/capi-quickstart-control-plane created
awsmachinetemplate.infrastructure.cluster.x-k8s.io/capi-quickstart-control-plane created
machinedeployment.cluster.x-k8s.io/capi-quickstart-md-0 created
awsmachinetemplate.infrastructure.cluster.x-k8s.io/capi-quickstart-md-0 created
kubeadmconfigtemplate.bootstrap.cluster.x-k8s.io/capi-quickstart-md-0 created
kubectl 명령어를 위해 아래와 같이 kubeconfig 를 생성한다.
$ clusterctl get kubeconfig capi-quickstart -n capi-quickstart > capi-quickstart.kubeconfig
한가지 중요한 부분은 Cluster API 는 Network 모듈이나, CSI 를 설치해 주지는 않는다. 이는 추가로 설치해야 한다.
$ kubectl --kubeconfig=./capi-quickstart.kubeconfig \
apply -f https://docs.projectcalico.org/v3.19/manifests/calico.yaml
네트워크 모듈까지 다 설치하면 정상적으로 Kubernetes Cluster 가 설치된 것을 볼 수 있다.
$ kubectl get kubeadmcontrolplane -n capi-quickstart
=== output ===
NAME INITIALIZED API SERVER AVAILABLE VERSION REPLICAS READY UPDATED UNAVAILABLE
capi-quickstart-control-plane true true v1.20.5 3 3 3
$ kubectl --kubeconfig=mycluster.kubeconfig get nodes
=== output ===
NAME STATUS ROLES AGE VERSION
ip-10-0-143-5.ap-northeast-2.compute.internal Ready control-plane,master 23h v1.20.5
ip-10-0-164-198.ap-northeast-2.compute.internal Ready <none> 23h v1.20.5
ip-10-0-222-84.ap-northeast-2.compute.internal Ready control-plane,master 23h v1.20.5
ip-10-0-255-19.ap-northeast-2.compute.internal Ready <none> 23h v1.20.5
ip-10-0-68-113.ap-northeast-2.compute.internal Ready <none> 23h v1.20.5
ip-10-0-80-79.ap-northeast-2.compute.internal Ready control-plane,master 23h v1.20.5