Ansible로 Kubernetes 클러스터를 코드 한 줄로 (홈랩 k8s 구축기 2편)

munilive
munilive
Ansible로 Kubernetes 클러스터를 코드 한 줄로 (홈랩 k8s 구축기 2편)

지난 편에서 Terraform으로 VM 3개를 만들었다. 이번에는 그 VM들 위에 Kubernetes 클러스터를 올리는 과정이다. kubeadm으로 클러스터를 직접 구성하고, 그 과정 전체를 Ansible로 자동화했다.

왜 Ansible인가

VM이 3개다. Control Plane 1개, Worker 2개. 패키지 설치, 커널 모듈 설정, swap 비활성화 같은 작업을 각 노드에 SSH로 접속해서 일일이 하는 건 귀찮은 일이기도 하지만, 나중에 다시 구성해야 할 때 무엇을 했는지 기억하기 어렵다는 게 더 큰 문제다.

Ansible은 이런 상황에 잘 맞는 도구다. 플레이북에 순서대로 작성해두면 다음에 같은 과정을 반복할 때 명령 하나로 끝난다.

플레이북 구조

역할(role) 기반으로 구성했다.

infrastructure/ansible/
├── ansible.cfg
├── inventory.ini
├── site.yml
└── roles/
    ├── common/          # VM 공통 초기화
    ├── containerd/      # 컨테이너 런타임 설치
    ├── kubernetes/      # kubeadm / kubelet / kubectl 설치
    ├── control-plane/   # kubeadm init + Flannel CNI + kube-proxy 설정
    └── worker/          # kubeadm join

site.yml을 실행하면 이 순서대로 모든 노드에 적용된다.

순서대상역할
1전체 노드common — 기본 패키지, swap 비활성화, 커널 모듈, sysctl
2전체 노드containerd — 컨테이너 런타임 설치 및 설정
3전체 노드kubernetes — kubeadm / kubelet / kubectl 설치
4control-planecontrol-plane — kubeadm init + Flannel CNI + kube-proxy 설정
5workersworker — kubeadm join

ansible-playbook site.yml 한 줄이면 끝이다.

containerd CRI 플러그인 문제

처음 실행에서 클러스터가 제대로 올라오지 않았다. kubeadm이 containerd와 통신을 못 했는데, 오류 메시지를 보면 이렇게 나온다.

failed to create new CRI runtime service: validate service connection:
validate CRI v1 runtime API for endpoint "unix:///var/run/containerd/containerd.sock":
rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService

CRI 플러그인이 비활성화된 상태라는 뜻이었다. Ubuntu 24.04에는 containerd가 기본 설치되어 있는데, 그 기본 설정이 CRI 플러그인을 꺼두거나, config.toml 파일이 이미 존재해서 내 설정이 덮어씌워지지 않은 것이 원인이었다.

Ansible task에서 creates: 조건이 문제였다.

# 수정 전 — 기존 파일이 있으면 건너뜀
- name: Generate containerd default config
  ansible.builtin.shell: containerd config default > /etc/containerd/config.toml
  args:
    creates: /etc/containerd/config.toml

# 수정 후 — 항상 재생성
- name: Generate containerd default config (force regenerate)
  ansible.builtin.shell: containerd config default > /etc/containerd/config.toml
  changed_when: true

creates: 조건을 제거하고 항상 재생성하도록 바꾸었다. 이후 SystemdCgroup = true로 설정해서 kubeadm과 cgroup 드라이버를 맞춰주는 것도 함께 진행했다.

kubeadm config 파일 방식

처음에는 kubeadm init을 명령줄 인자 방식으로 실행했다.

kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.50.110

이렇게 해도 클러스터는 올라온다. 그런데 나중에 모니터링을 설치하면서 문제가 생겼다. Prometheus가 kube-controller-manager, kube-scheduler, etcd, kube-proxy의 메트릭을 수집하지 못하는 것이었다. 원인을 파보니 kubeadm 기본 설정에서 이 컴포넌트들이 127.0.0.1에만 바인딩되어 있어서 Prometheus Pod이 접근할 수 없었던 것이다.

이걸 코드로 관리하려면 config 파일 방식이 맞다.

# infrastructure/ansible/roles/control-plane/files/kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
networking:
  podSubnet: 10.244.0.0/16
apiServer:
  advertiseAddress: 192.168.50.110
controllerManager:
  extraArgs:
    - name: bind-address
      value: '0.0.0.0'
scheduler:
  extraArgs:
    - name: bind-address
      value: '0.0.0.0'
etcd:
  local:
    extraArgs:
      - name: listen-metrics-urls
        value: 'http://0.0.0.0:2381'

kube-proxy는 Ansible task에서 ConfigMap 패치로 따로 처리한다.

- name: Patch kube-proxy metricsBindAddress
  ansible.builtin.shell: |
    kubectl get configmap kube-proxy -n kube-system -o yaml \
      | sed 's/metricsBindAddress: ""/metricsBindAddress: "0.0.0.0:10249"/' \
      | kubectl apply -f -

이렇게 해두면 다음에 클러스터를 다시 구성할 때 Prometheus 연동 문제 없이 바로 올라온다.

클러스터 확인

ansible-playbook site.yml이 완료되면 아래와 같이 3개 노드가 Ready 상태로 올라온다.

NAME                STATUS   ROLES           AGE   VERSION
k8s-control-plane   Ready    control-plane   96s   v1.32.13
k8s-worker-1        Ready    <none>          83s   v1.32.13
k8s-worker-2        Ready    <none>          83s   v1.32.13

VM 3개에 이걸 일일이 손으로 했다면 시간도 오래 걸리고 실수도 생기기 쉬웠을 것이다. Ansible에 한 번 정리해두고 나니, 클러스터를 처음부터 다시 만드는 일이 이제는 부담스러운 일이 아니었다.

다음 편에서는 이 클러스터 위에 네트워크 컴포넌트와 스토리지를 올리는 과정을 이야기 하겠다.

munilive

munilive

Backend Application Developer

Share

Comments

Related Posts

집에 Kubernetes 클러스터를 구축해 봤습니다

집에 Kubernetes 클러스터를 구축해 봤습니다

퇴근하고 집에 돌아와 미니 PC 한 대를 책상 위에 올려두고는 여기다가 Kubernetes 클러스터를 직접 구축해 보겠다고 마음먹은 게 몇 달 전이었다. 그리고 지금, 그 미니 P…

munilive munilive ·
MetalLB, ingress-nginx, Longhorn으로 네트워크와 스토리지 구성하기 (홈랩 k8s 구축기 3편)

MetalLB, ingress-nginx, Longhorn으로 네트워크와 스토리지 구성하기 (홈랩 k8s 구축기 3편)

지난 편에서 Ansible로 Kubernetes 클러스터를 구성했다. 클러스터는 올라왔지만, 이 상태로는 아무것도 할 수 없다. 외부에서 트래픽을 받는 방법도 없고, 데이터를 저장…

munilive munilive ·
Proxmox 설치부터 Terraform으로 VM 찍어내기까지 (홈랩 k8s 구축기 1편)

Proxmox 설치부터 Terraform으로 VM 찍어내기까지 (홈랩 k8s 구축기 1편)

집에 Kubernetes 클러스터를 구축해 봤습니다에서 전체 구성과 삽질의 개요를 간략하게 정리했다. 이번 편부터는 각 단계를 좀 더 구체적으로 풀어볼 생각이다. 1편의 주제는 P…

munilive munilive ·