cluster api(이하 capi)는 kubernetes에서 kuberntes 클러스터 자체도 자원으로 관리하는 컨트롤러로 클러스터와 이를 구성하는 인프라까지도 포함하여 IaC 체계로 사용할 수 있다. 클라우드 인프라별로 capi의 구현체게 제공되며 현재 지원되는 인프라는 AWS, Azure, DigitalOcean, Docker, Equinix Metal, GCP, Hetzner, IBM Cloud, Metal3, Nutanix, OCI, OpenStack, vSphere 등 현존하는 주요 클라우드와 가상화 플랫폼을 모두 포함한다. 팀에서는 aws 기반의 클러스터를 생성하는 기능을 개발하였고 aws provider를 사용하여 클러스터를 관리하고 있다.

본 페이지에서는 aws 기반으로 cluster를 운영하는데 IaC를 적용하기 위해 사용했던 도구와 개발물을 소개하고자 한다.

cluster-api-aws helm chart

간단하게 capi를 사용하기 위해서는 커뮤니티에서 제공하는 quick start 메뉴얼(https://cluster-api.sigs.k8s.io/user/quick-start.html)을 참조하면 된다. 해당 문서에는 capi를 위한 다양한 기반 소프트웨어 설치부터 이를 활용하여 클러스터를 배포하고 이를 정리(삭제)하는 내용까지 포함하고 있다. 이 문서에서는 클러스터 배포를 위한 자원 생성으로 cli에 포함된 템플릿을 기반으로 yaml을 만들고 이를 배포하는 형태로 가이드하고 있다.

export AWS_REGION=us-east-1
export AWS_SSH_KEY_NAME=default
# Select instance types
export AWS_CONTROL_PLANE_MACHINE_TYPE=t3.large
export AWS_NODE_MACHINE_TYPE=t3.large
clusterctl generate cluster capi-quickstart \\
  --kubernetes-version v1.23.3 \\
  --control-plane-machine-count=3 \\
  --worker-machine-count=3 \\
  > capi-quickstart.yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: capi-quickstart
  namespace: default
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
      - 192.168.0.0/16
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    name: capi-quickstart-control-plane
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
    kind: AWSCluster
    name: capi-quickstart
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSCluster
metadata:
  name: capi-quickstart
  namespace: default
spec:
  region: us-east-1
  sshKeyName: default
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
  name: capi-quickstart-control-plane
  namespace: default
spec:
  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 }}'
  machineTemplate:
    infrastructureRef:
      apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
      kind: AWSMachineTemplate
      name: capi-quickstart-control-plane
  replicas: 3
  version: v1.23.3
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSMachineTemplate
metadata:
  name: capi-quickstart-control-plane
  namespace: default
spec:
  template:
    spec:
      iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t3.large
      sshKeyName: default
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: capi-quickstart-md-0
  namespace: default
spec:
  clusterName: capi-quickstart
  replicas: 3
  selector:
    matchLabels: null
  template:
    spec:
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: capi-quickstart-md-0
      clusterName: capi-quickstart
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: AWSMachineTemplate
        name: capi-quickstart-md-0
      version: v1.23.3
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSMachineTemplate
metadata:
  name: capi-quickstart-md-0
  namespace: default
spec:
  template:
    spec:
      iamInstanceProfile: nodes.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t3.large
      sshKeyName: default
---
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
metadata:
  name: capi-quickstart-md-0
  namespace: default
spec:
  template:
    spec:
      joinConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
            cloud-provider: aws
          name: '{{ ds.meta_data.local_hostname }}'

위의 예시에서 보여주는 것처럼 결론적으로 만들어진 파일은 capi에서 다루는 자원들을 기술한 yaml들의 집합이다. cluster를 규정하고 control plane으로 사용할 마스터노드, 워커노드의 자원들을 기술하고 있다. 이러한 파일들을 잘 관리하고 동기화하여 IaC로 사용하는 것도 가능하지만 다량의 클러스터를 만들고 사용하는 데는 전체 파일을 대상으로 customize해야 하는 불편함과 좀더 세세한 자원들을 기술하는데 부족함이 있다. 이에 따라 좀더 확장 가능한 방법으로 구성할 필요가 있다.

helm은 kubernetes에서 여러 자원을 배포하기 위해 만들어진 패키지로 팀에서도 적극 활용하는 배포단위이다. helm chart를 구성하게되면 필요한 yaml을 배포하면서도 필요한 부분의 값을 치환하여 적용하는 것이 편리하다. 따라서 클러스터 구성을 위한 자원들을 helm chart를 구성하고 배포하는 것이 좋은 대안이었다. 따라서 helm chart를 개발하였고 이 과정에서 풀어야 했었던 것들에 대해 이야기 한다.

필요한 배포자원들

다시 앞의 자동생성 yaml을 보면 다음과 같은 자원을 기술하고 있다.

Worker node를 AZ에 고르게 배포하는 방법 (feat. MachinePool)

예제에서 제공하는 자원 중 워크노드와 관련된 MachinDeployment는 단일 AZ에서 복제수(replica)만큼의 워커노드를 생성하여 클러스터를 확장한다. AZ의 개념상 단일 장애구간으로 물리적 위치로부터 전원 네트워크등의 자원을 독립적으로 갖고 있는 공간이다. AZ가 여러개 존재하는 경우 각 워커노드는 AZ별로 고루 분산시키는 것이 필요하고 MachineDeployment에서는 지원하지 않으므로 이러한 배포가 가능한 자원을 찾은 것이 MachinePool이다.

MachinePool은 동일한 설정값과 복제본 수의 설정을 통해 machine들의 집합을 관리하는 기능이다. capi MachineSet 컨트롤러가 제어하는 MachineDeployment와 다르게 MachinePool은 클라우드 제공자마다 각자의 컨트롤러를 제공하도록 하는 기능으로 현재는 실험적(experimental)으로 제공된다.이를 사용하기 위해서 capi 배포시, ‘EXP_MACHINE_POOL=true’로 환경변수를 명시적으로 선언해야 한다. (https://cluster-api.sigs.k8s.io/tasks/experimental-features/machine-pools.html)