Kubernetes 2026.10.27 7 min read

Ansible 연결 확인에서 kubeadm과 Calico 클러스터 구성까지

관리 서버의 일반 계정에서 SSH 키를 준비하고 테스트 VM에 공개키를 배포했다. 기존 키를 덮어쓰지 않는지 확인한 뒤 직접 SSH 접속을 확인했다. Inventory에는 대상의 역할 이름, 접속 주소, 관리 사용자와 Python interpreter를 지정했다. 실제 주소와 개인 경로는 이 글에서 생략했다.

Terraform으로 VM을 준비한 뒤에는 서버 안의 상태를 맞출 차례였다. 패키지를 설치하고 서비스를 켜는 작업을 노드마다 반복하기보다, Inventory와 Playbook을 기준으로 관리하려 했다. 처음부터 Kubernetes 설치 전체를 묶지 않고 테스트 VM 한 대의 연결과 bootstrap부터 진행했다.

pong으로 확인한 SSH와 Python 실행 경로

관리 서버의 일반 계정에서 SSH 키를 준비하고 테스트 VM에 공개키를 배포했다. 기존 키를 덮어쓰지 않는지 확인한 뒤 직접 SSH 접속을 확인했다. Inventory에는 대상의 역할 이름, 접속 주소, 관리 사용자와 Python interpreter를 지정했다. 실제 주소와 개인 경로는 이 글에서 생략했다.

ansible -i inventory/hosts.ini lab -m ping

pong이 돌아오면서 첫 연결 검사를 마쳤다. 여기서 Ansible의 ping은 SSH로 연결하고 원격 모듈을 실행하는 검사였다. 네트워크의 ICMP ping과 같은 검사로 생각하면 확인한 범위를 잘못 설명하게 된다. 이후 Playbook을 실행할 경로가 준비됐다는 의미로 받아들였다.

bootstrap Playbook에서는 기본 패키지 설치, SSH 서비스 활성화와 관리 표시 파일 생성을 다뤘다. 먼저 syntax-check를 하고 실행한 뒤 같은 Playbook을 다시 돌려 불필요한 변경이 반복되는지 살펴보는 흐름이었다. 패키지와 서비스 상태를 선언하는 방식이 수동 명령을 모아 놓는 것과 어떻게 다른지 확인할 수 있었다.

확인용 command 모듈의 표시도 주의해서 읽었다. 파일 내용을 cat으로 읽었는데 CHANGED가 표시될 수 있었다. 그 한 단어를 파일이 수정됐다는 증거로 옮기지 않았다. 작업의 실제 동작과 모듈이 변경 여부를 보고하는 방식을 함께 이해해야 했다.

Kubernetes 노드를 역할별 그룹으로 나눴다

테스트 VM 연결 뒤에는 Inventory를 Control Plane과 Worker 그룹으로 나눴다. 두 그룹을 상위 Kubernetes 그룹으로 묶어 공통 작업과 역할별 작업의 실행 대상을 구분했다. 세 노드에서 Ansible 응답을 확인한 다음 사전 환경을 구성했다.

Ansible 버전과 Control Plane·Worker Inventory 구조
Ansible 버전과 Control Plane·Worker Inventory 구조.

Terraform으로 선언한 노드는 Control Plane 한 대와 Worker 두 대였다. 각 VM은 CPU 2개, 메모리 4GiB와 가상 디스크 40GiB로 준비했다. 가상 디스크 크기가 게스트 root 파일시스템 크기까지 보장하지 않는다는 점은 이후 용량 문제에서 다시 확인하게 된다.

Ansible 쪽에서는 common, containerd, Kubernetes 패키지를 별도 Role로 나눴다. common은 기본 패키지와 swap, 커널 모듈, IP forwarding과 bridge 관련 설정을 다뤘다. containerd 쪽은 CRI 활성화와 systemd cgroup 설정, 서비스 상태를 대상으로 삼았다. 패키지 Role에서는 Kubernetes 도구를 설치하고 버전을 hold하는 흐름을 준비했다.

변경 시에만 필요한 작업은 handler로 연결했다. 반면 모듈 로드처럼 변경 표시를 직접 조정한 작업도 있었다. 재실행 화면이 조용하다는 사실만으로 모든 drift를 검사했다고 말할 수는 없었다. 각 Role이 어떤 상태를 확인하고 어떤 상태를 확인하지 않는지 알아야 결과를 읽을 수 있었다.

당시 안내 코드에 들어 있던 버전 검사 문구는 그 시점의 구성 의도였다. 이후 노드 출력에서 확인한 containerd는 2.2.1이었다. 이 기록을 새 버전의 지원 조건이나 현재 공식 최소 요구사항으로 일반화하지 않았다.

kubeadm 초기화와 Worker 합류 순서를 잡았다

다음 목표는 kubeadm으로 클러스터를 구성하는 것이었다. Playbook 흐름은 전체 노드 사전 설정, Control Plane 초기화, Calico 준비, Worker 순차 합류와 Ready 확인으로 잡았다.

Control Plane에서는 admin.conf의 존재로 초기화 여부를 확인하도록 했다. API endpoint에는 내부 이름을 두고 노드가 이를 해석할 수 있도록 준비했다. 이름을 부여한 것만으로 단일 Control Plane의 가용성이 높아진 것은 아니었다. 당시 구성은 Control Plane 한 대였다.

Worker에서는 kubelet.conf의 존재를 확인하고, 아직 합류하지 않은 대상에 대해서만 join 명령을 받도록 안내했다. 토큰과 join 명령은 로그에 노출하지 않는 범위로 다뤘다. 합류는 한 번에 여러 노드를 처리하기보다 순서대로 진행하도록 구성했다.

Calico 설정과 호스트 관리 주체를 함께 봤다

CNI는 Calico를 선택했다. 당시 선택한 설정은 Iptables dataplane, VXLAN encapsulation, BGP 비활성화와 Kubernetes NodeInternalIP 기반 주소 감지였다. Pod와 Service 대역은 서로 나눴고 노드 네트워크와도 구분했다.

Calico 관련 작업을 준비하면서 NetworkManager도 확인했다. 관리 서버가 NetworkManager를 쓴다고 Kubernetes 템플릿에도 같은 서비스가 있다고 볼 수는 없었다. 결국 새로 설치하지 않고, 활성화된 경우에만 Calico 인터페이스를 관리 대상에서 제외하는 조건부 작업으로 범위를 잡았다.

이렇게 해 두면 모든 노드에 같은 변경을 강제로 넣기 전에 각 호스트의 상태를 먼저 확인할 수 있었다. 네트워크 인터페이스를 누가 관리하는지 모른 채 설정 파일을 복사하는 실수를 피하려는 선택이었다.

전체 실행 뒤 클러스터 구성과 상태 확인을 마쳤다. 여기서도 설치 명령이 끝났다는 사실에 머물지 않고 노드, 시스템 Pod와 Calico 상태를 확인 대상으로 잡았다. 이후에는 실제로 서로 다른 Worker의 Pod를 연결하고 DNS, Service와 NetworkPolicy까지 검증했다. 구성 자동화와 동작 검증을 이어 가야 서비스 배포를 시작할 수 있었다.

Kubernetes Control Plane과 Worker 두 대의 Ready 상태
Kubernetes Control Plane과 Worker 두 대의 Ready 상태.

2026년 10월 19일 재조회에서는 Ansible core 2.16.3과 Inventory의 Control Plane·Worker 그룹을 확인했다. 이때 새 Playbook이나 ping을 실행하지는 않았다. 최초·재실행 PLAY RECAP 전체가 없어 changed=0을 새 수치로 제시하지 않는다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동