Kubernetes Practice 02

Kubernetes 설치 전, 준비와 설치의 차이 이해하기

설치 명령이 길게 이어지면 어떤 명령이 Kubernetes 자체이고 어떤 명령이 노드를 Kubernetes가 쓸 수 있게 준비하는 것인지 섞인다.

근거 · Kubernetes 교안 p24-p35, Lab 01.md

Kubernetes 설치 전, 준비와 설치의 차이 이해하기 대표 이미지

1장: 노드 준비: Kubernetes가 원하는 깨끗한 환경

솔라는 모니터에 떠 있는 터미널 창과 설치 가이드 문서를 번갈아 보며 미간을 찌푸렸다. 거실 테이블 위에는 늦은 저녁을 해결한 접시와 함께 솔라의 노트북이 놓여 있었다. 옆에서 조용히 책을 읽던 루나가 솔라의 혼잣말을 들은 것은 그때였다.

“이상하네… 분명 쿠버네티스를 설치하는 중인데, 왜 명령어에는 쿠버네티스 얘기가 하나도 없지?”

솔라는 마우스를 허공에서 방황시키며 중얼거렸다. 화면에는 sudo swapoff -a 라는 명령어가 커서 옆에서 깜빡이고 있었다.

“뭐가 이상해?”

루나가 책을 덮으며 물었다. 솔라는 기다렸다는 듯이 루나 쪽으로 노트북 화면을 돌렸다.

“언니, 이것 봐. 쿠버네티스 설치 과정이라고 해서 따라 하고 있거든. 근데 처음부터 swapoff 라니. 스왑을 끄는 거잖아. 그 다음엔 sysctl 로 커널 파라미터를 바꾸래. 이게 왜 쿠버네티스 설치인 거야? 내 생각에 ‘설치’는 apt install kubernetes 같은 느낌인데.”

솔라의 말에는 그럴듯한 논리가 있었다. 무언가를 설치한다면, 그 무언가의 이름이 명령어에 들어가는 게 자연스럽다. 솔라는 지금 swapsysctl이 쿠버네티스를 설치하는 과정의 일부라고 생각하고 있었다. 모든 명령어가 하나의 목적, 즉 ‘쿠버네티스 설치’라는 바구니에 담겨 있다고 여겼다.

루나는 솔라의 화면을 잠시 들여다보더니, 고개를 끄덕였다.

“네 말대로 그 명령어들은 쿠버네티스 자체를 설치하는 게 아니야.”

“거봐! 그럼 이 가이드는 순서가 잘못된 거 아닐까?”

솔라의 목소리가 한 톤 높아졌다. 루나는 빙그레 웃으며 솔라를 진정시켰다.

“순서가 잘못된 건 아니야. 혹시, 새로 산 멋진 책장을 조립해서 방에 놓는다고 상상해볼래?”

“갑자기 책장?”

“응. 네 방에 책장을 놓을 거야. 그런데 책장이 들어갈 자리에 원래 있던 낡은 상자들이 쌓여있고, 바닥이 울퉁불퉁해서 수평이 맞지 않아. 그럼 제일 먼저 뭘 해야 할까?”

솔라는 잠시 생각에 잠겼다. “음… 상자부터 치우고, 바닥 수평을 맞추는 발판 같은 걸 깔아야지. 안 그러면 새 책장이 기울어질 테니까.”

“바로 그거야.” 루나가 말했다. “지금 네가 하려는 swapoffsysctl 설정이 바로 그 ‘상자 치우기’와 ‘바닥 수평 맞추기’에 해당해.”

솔라의 눈이 동그래졌다.

“쿠버네티스라는 아주 정교하고 예민한 책장을 놓기 전에, 책장이 놓일 ‘노드’라는 방을 먼저 정리하고 있는 거지. 쿠버네티스가 자기가 가진 자원(메모리)을 100% 제어하고 싶어 해. 그런데 스왑(swap)이 켜져 있으면, 운영체제가 멋대로 메모리의 일부를 디스크로 옮겨버리거든. 그럼 쿠버네티스 입장에서는 계산이 복잡해지고 예측이 어려워져. 그래서 ‘나는 스왑 없는 깨끗한 환경이 좋아’라고 요구하는 거야.”

“아… 그럼 swapoff는 쿠버네티스를 위한 ‘방 청소’ 같은 거구나.”

“정확해. 한번 확인해볼까?”

루나의 제안에 솔라는 터미널에 명령어를 입력할 준비를 했다.

“먼저, 지금 네 컴퓨터의 스왑 상태를 확인해봐. free -m 이라고 입력하면 메모리 상태를 볼 수 있어.”

솔라가 명령어를 입력하자 몇 줄의 숫자가 나타났다.

$ free -m
              total        used        free      shared  buff/cache   available
Mem:           3943        1124        1929          21         889        2564
Swap:          1023           0        1023

“여기 Swap 줄에 1023이라고 숫자가 보이지? 스왑 공간이 할당되어 있다는 뜻이야. 이제 원래 하려던 명령어를 실행해봐.”

솔라는 가이드에 적힌 대로 sudo swapoff -a를 입력했다. 아무 메시지 없이 다음 프롬프트가 나타났다.

“된 건가?”

“다시 확인해봐야지.”

솔라는 다시 free -m을 입력했다.

$ free -m
              total        used        free      shared  buff/cache   available
Mem:           3943        1127        1926          21         889        2561
Swap:             0           0           0

“어! 진짜 Swap 줄이 전부 0으로 바뀌었어!”

솔라의 목소리에 놀라움과 함께 깨달음이 섞여 나왔다. swapoff 명령어는 쿠버네티스라는 프로그램을 설치한 것이 아니었다. 그저 리눅스 시스템의 스왑 기능을 껐을 뿐이다. 그 결과가 눈앞의 숫자로 명확하게 보였다.

“이제 알겠어. swapoff나 커널 파라미터를 조정하는 sysctl 같은 명령어들은 쿠버네티스를 ‘설치’하는 게 아니라, 쿠버네티스가 잘 돌아갈 수 있도록 OS 환경을 ‘준비’시키는 거였네. 책장을 놓기 전에 방을 정리하는 것처럼.”

솔라는 방금 전까지 뒤죽박죽 섞여 있던 명령어 목록을 새로운 눈으로 바라보았다. 이제는 ‘설치’라는 하나의 덩어리가 아니라, ‘준비’와 ‘설치’라는 두 개의 다른 단계로 보이기 시작했다. 어떤 명령어가 노드 자체의 상태를 바꾸는지, 어떤 명령어가 새로운 프로그램을 가져오는지 구분할 수 있는 작은 실마리를 얻은 기분이었다.

스스로 내린 결론에 만족한 솔라의 시선이 설치 가이드의 다음 줄로 향했다.

“좋아, 그럼 이건 ‘노드 준비’ 단계였고… 어라? 다음은 containerd를 설치하라고 나오네. 이건 apt install이니까 진짜 설치인데. 그럼 이것도 방 청소인가? 아니면 이건 책장 부품 중 하나인 걸까?”

솔라의 새로운 질문에, 루나는 아무 말 없이 미소만 지었다. ‘준비’와 ‘설치’를 구분하기 시작하자, 그 경계에 있는 새로운 질문이 자연스럽게 떠오른 것이다.

2장: 컨테이너 런타임: Kubernetes의 컨테이너 작동 원리

솔라는 노트북 화면 한쪽에 작은 메모장을 열어두었다. 방금 전 루나와의 대화로 얻은 깨달음을 정리하기 위해서였다. 메모장에는 두 개의 단출한 제목이 적혀 있었다.

1. 방 정리 (노드 준비) 2. 책장 조립 (K8s 설치)

솔라는 swapoffsysctl 명령어를 ‘방 정리’ 목록 아래로 옮겼다. 이제야 명령어들이 제자리를 찾은 것 같아 마음이 편해졌다. 하지만 그 편안함은 설치 가이드의 다음 줄을 마주하자마자 다시 물음표로 바뀌었다.

apt install containerd

솔라는 잠시 망설이다가, 마지못해 ‘책장 조립’ 목록 아래에 containerd 설치라고 적었다. apt install은 명백한 설치 명령이었기 때문이다. 하지만 어딘가 찜찜한 구석을 지울 수 없었다. 쿠버네티스 책장을 조립하는데, 왜 containerd라는 이름의 부품이 필요한 걸까? 진짜 쿠버네티스는 언제 등장하는 거지?

“언니, 이건 아무리 봐도 이상해.”

솔라가 결국 혼란을 참지 못하고 입을 열었다. 그녀의 손가락은 화면의 containerd를 가리키고 있었다.

“방 정리가 끝났으면 책장을 조립해야 하잖아. 그럼 apt install kubernetes 같은 게 나와야 할 것 같은데, 뜬금없이 containerd를 설치하래. 이건… 책장 조립에 쓰는 전동 드릴 같은 건가? 꼭 이 드릴만 써야 해?”

솔라의 질문은 정확했다. containerd는 여러 컨테이너 런타임 중 하나일 뿐이고, 다른 것을 써도 되지 않을까? 그녀는 containerd 설치를 쿠버네티스라는 핵심 목표를 위한 여러 선택지 중 하나, 혹은 부차적인 도구로 보고 있었다.

루나는 솔라의 메모장을 흥미롭게 바라보았다. ‘방 정리’와 ‘책장 조립’이라는 비유가 솔라의 이해를 도왔지만, 동시에 새로운 혼란의 씨앗이 된 셈이었다.

“비유를 조금 바꿔보자.” 루나가 말했다. “책장 대신 아주 큰 레스토랑의 주방이라고 생각해봐. 쿠버네티스는 총괄 셰프야.”

“총괄 셰프?”

“응. 총괄 셰프는 모든 요리를 직접 하지 않아. 대신 ‘파스타 준비해’, ‘스테이크 구워’ 하고 지시를 내리지. 그 지시를 받아 실제로 요리를 하는 건 누구지?”

“음… 보조 요리사들이나 라인 쿡?”

“바로 그거야. 여기서 containerd가 바로 그 ‘라인 쿡’ 역할을 해. 쿠버네티스라는 총괄 셰프는 컨테이너를 직접 만들고 실행하는 세세한 일은 하지 않아. 대신 ‘이런이런 컨테이너를 띄워줘’라고 명령을 내리고, 그 명령을 실행하는 전문적인 엔진이 바로 containerd 같은 컨테이너 런타임이야.”

솔라의 눈이 반짝였다. “아, 그럼 쿠버네티스는 명령만 내리는 지휘자고, containerd가 실제 행동대장이구나.”

“비슷해. 중요한 건, 총괄 셰프가 아무한테나 지시할 수는 없다는 거야. 셰프가 사용하는 ‘주문 용어’를 알아들을 수 있는 요리사하고만 일할 수 있어. 쿠버네티스는 이 주문 용어를 ‘CRI(Container Runtime Interface)‘라는 표준으로 정해뒀어. 그리고 containerd는 그 표준을 아주 잘 지원하는 런타임 중 하나지.”

이제 솔라는 왜 containerd를 설치해야 하는지 어렴풋이 이해하기 시작했다. 그냥 드릴이 아니라, 총괄 셰프의 말을 알아들을 수 있는 특별 훈련을 받은 요리사였던 것이다.

“그럼 가이드에 나오는 이 설정은… 요리사의 근무 규칙 같은 건가?”

솔라는 가이드의 다음 부분을 가리켰다. containerd의 설정 파일(/etc/containerd/config.toml)을 수정하여 SystemdCgroup = true로 바꾸라는 내용이었다.

# /etc/containerd/config.toml
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
...

루나는 고개를 끄덕였다. “정확해. 아주 중요한 근무 규칙이야.”

루나는 설명을 이었다. “주방에서 요리사들이 각자 사용하는 가스레인지나 조리대의 공간을 ‘자원’이라고 해보자. 이 자원을 누가, 어떻게 관리하고 제한할지 정하는 규칙이 필요하겠지? 그걸 리눅스에서는 ‘Cgroup(Control Group)‘이라는 기능으로 관리해.”

“자원 관리 규칙…?”

“응. 그런데 그 규칙을 관리하는 방식이 크게 두 가지가 있어. 운영체제 커널이 직접 관리하는 방식(cgroupfs)과, systemd라는 시스템 관리자가 통제하는 방식이야. 쿠버네티스(정확히는 각 노드에 있는 쿠버네티스의 대리인)는 systemd 방식을 사용해서 자원을 파악하고 할당해. 만약 라인 쿡인 containerd가 다른 방식(cgroupfs)으로 자원을 관리하면 어떻게 될까?”

솔라는 잠시 상상에 잠겼다. “총괄 셰프는 ‘가스 불 세기를 5로 해’라고 했는데, 요리사는 그걸 ‘오븐 온도를 50도로 하라’는 식으로 제멋대로 해석하는 건가? 서로 다른 측정 단위를 쓰는 것처럼?”

“바로 그거야. 둘의 자원 관리 방식이 다르면, 쿠버네티스는 컨테이너가 자원을 얼마나 쓰는지 제대로 파악하거나 제한할 수 없게 돼. 그래서 SystemdCgroup = true라는 설정으로, ‘너(containerd), 쿠버네티스가 쓰는 거랑 똑같은 자원 관리 방식을 사용해!’라고 명확하게 지정해주는 거야. 이건 선택이 아니라 필수적인 약속이지.”

overlay, br_netfilter 같은 커널 모듈을 미리 불러오는 이유도 마찬가지였다. 컨테이너가 쓸 가상 네트워크와 파일 시스템을 쿠버네티스가 제대로 들여다보고 제어할 수 있게끔, 라인 쿡이 쓸 도구들을 미리 준비시켜주는 절차였다.

솔라는 자신의 메모장을 다시 바라보았다. ‘방 정리’와 ‘책장 조립’이라는 이분법이 너무 단순했다는 것을 깨달았다. 그녀는 containerd 설치 항목을 지우고, 두 항목 사이에 새로운 단계를 추가했다.

1. 노드 준비 (방 정리) 2. 컨테이너 런타임 구성 (셰프의 지시를 따를 '엔진' 준비시키기) 3. K8s 설치 (총괄 셰프 초빙)

이제 containerd 관련 명령어들은 더 이상 뜬금없는 설치 과정이 아니었다. 쿠버네티스라는 핵심 애플리케이션이 원활하게 작동하기 위해, 그 환경의 가장 중요한 전제 조건인 ‘컨테이너 실행 엔진’을 정교하게 맞추는 과정이었다. 준비와 설치 사이에 존재하는, 명확한 목적을 가진 ‘구성’ 단계였던 것이다.

“알겠어. 그럼 이제 엔진 준비까지 끝났으니, 드디어 총괄 셰프를 모실 차례네!”

안도의 한숨을 내쉰 솔라의 시선이 가이드의 마지막 설치 섹션으로 향했다.

apt install kubelet kubeadm kubectl

“그런데… 셰프가 왜 세 명이나 오지? kubelet, kubeadm, kubectl… 이름도 비슷해서 헷갈려. 이 중에 진짜 ‘쿠버네티스’는 누구야?“

3장: Kube 도구: Kubernetes 클러스터의 핵심 구성 요소

솔라의 노트북 화면 메모장에는 지난 몇 분간의 깨달음이 간결하게 정리되어 있었다.

1. 노드 준비 (방 정리) 2. 컨테이너 런타임 구성 (셰프의 지시를 따를 '엔진' 준비시키기) 3. K8s 설치 (총괄 셰프 초빙)

이제 마지막 단계였다. 솔라는 설치 가이드의 마지막 명령어 apt install kubelet kubeadm kubectl을 3번 항목 아래에 붙여넣었다. 그녀는 ‘총괄 셰프 초빙’이라는 자신의 비유를 구체화하기 위해, 세 가지 도구를 주방의 직책에 억지로 끼워 맞추기 시작했다. 잠시 키보드를 두드리던 그녀의 화면에는 어색한 추측의 흔적이 남았다.

3. K8s 설치 (총괄 셰프 초빙)
   - 총괄 셰프: kubectl? (control이니까?)
   - 현장 셰프: kubelet? (let이니까 시키는 사람?)
   - ...매니저?: kubeadm (admin?)

하지만 금세 모순을 발견했다. 세 명 다 셰프일 수는 없었다. 그렇다고 한 명만 셰프라면, 나머지는 누구란 말인가? 그녀는 방금 만든 목록을 지우고, 대신 세 단어 옆에 커다란 물음표를 나란히 띄웠다. kubelet? kubeadm? kubectl? 하나의 ‘설치’ 명령 안에 들어있는 세 도구의 관계가 도무지 정리되지 않았다.

“주방의 직책을 정리하는 게 쉽지 않아 보이는군.”

솔라의 고민을 지켜보던 루나가 조용히 입을 열었다. 그녀의 시선은 솔라가 지웠다 다시 쓴 물음표들에 머물러 있었다.

“네. 엔진, 그러니까 라인 쿡까지는 준비가 끝났는데… 정작 운영팀이 한꺼번에 들어오니 누가 누군지 모르겠어요. 이름도 비슷해서 헷갈리고요. 이 세 가지는 그냥 하나의 묶음인가요?”

솔라의 질문에 루나는 레스토랑 비유를 조금 더 구체화하기로 마음먹었다.

“좋은 질문이야. 사실, 세 명 다 ‘총괄 셰프’는 아니야. 각자 맡은 역할이 뚜렷하게 다른 ‘운영팀’이라고 생각하면 더 정확해.”

루나는 솔라의 메모장에 있는 세 이름을 하나씩 가리켰다.

“먼저 kubelet부터 볼까? 레스토랑의 모든 조리대, 그러니까 우리 비유에서는 모든 ‘노드’에 한 명씩 파견 나가 있는 현장 관리자야. 중앙 주방(컨트롤 플레인)에서 ‘새로운 파스타 주문 들어왔으니 만들어줘’라는 지시가 내려오면, 이 kubelet이 받아서 바로 옆에 있는 라인 쿡(containerd)에게 ‘이 레시피대로 파스타 만들어!’ 하고 실제 작업을 지시하지. 그리고 요리가 잘 되고 있는지, 재료는 충분한지 계속 중앙에 보고하는 역할도 해. 모든 노드에 반드시 상주해야 하는 쿠버네티스의 가장 기본적인 일꾼이야.”

“아, 그럼 kubelet은 쿠버네티스 클러스터의 모든 노드에 설치되어서, 실제 컨테이너 생성을 지시하고 상태를 감시하는 에이전트구나.”

“맞아. 그럼 kubeadm은 뭘까?” 루나가 다음 이름으로 손가락을 옮겼다. “adm은 administrator의 약자야. 이 레스토랑 체인을 처음 열 때, 첫 번째 식당의 중앙 주방을 만들고, 전체 시스템을 가동시키고, 새로운 지점(노드)이 생겼을 때 우리 체인에 합류시키는 절차를 진행하는 ‘오픈 지원팀’ 같은 거야.”

솔라의 눈이 동그래졌다. “설마 kubeadm init이나 kubeadm join 같은 명령어랑 관련 있는 거야?”

“빙고. kubeadm은 클러스터를 ‘초기화(init)‘하고 다른 노드들을 ‘참여(join)‘시키는, 즉 클러스터의 탄생과 확장을 도와주는 부트스트랩 도구야. 일단 레스토랑이 정상적으로 문을 열고 나면, 오픈 지원팀은 매일의 운영에 직접 관여하지는 않겠지? kubeadm도 마찬가지야.”

이제 솔라는 kubeletkubeadm의 차이를 명확히 구분할 수 있었다. 하나는 상주하는 현장 관리자, 다른 하나는 개업을 돕는 임시 전문가.

“마지막으로 kubectl이 남았네.” 루나가 말했다. “이건 ‘control’의 약자야. 레스토랑 본사에서 전체 지점 현황을 보거나, 새로운 메뉴를 출시하라고 명령을 내릴 때 사용하는 ‘관제 시스템’ 또는 ‘무전기’라고 할 수 있어. 이걸로 ‘지금 운영 중인 노드 목록 보여줘’ (get nodes) 하거나 ‘새로운 애플리케이션 배포해’ (apply -f) 같은 명령을 내리는 거지. 즉, 이건 우리가, 관리자가 클러스터와 소통하기 위한 클라이언트 도구야.”

설명을 들은 솔라는 머릿속에 흩어져 있던 퍼즐 조각들이 맞춰지는 기분을 느꼈다. 그녀는 터미널을 열고, 방금 배운 것을 시험해보기로 했다.

“그럼 내가 지금 이 무전기를 한번 써볼게.”

솔라는 자신 있게 키보드를 두드렸다.

$ kubectl get nodes

이 명령어는 kubectl(클라이언트)을 사용해 클러스터의 마스터 노드(중앙 주방)에 ‘모든 노드 정보 좀 줘’라고 요청한다. 그러면 마스터 노드는 각 노드에 상주하는 kubelet(현장 관리자)들로부터 받은 최신 상태 보고를 취합하여 솔라에게 보여준다. 잠시 후, 화면에 결과가 나타났다.

NAME           STATUS   ROLES           AGE   VERSION
k8s-master     Ready    control-plane   15m   v1.28.2
k8s-worker-1   Ready    <none>          10m   v1.28.2
k8s-worker-2   Ready    <none>          10m   v1.28.2

“우와… 진짜 보이네. 내가 kubectl로 물어보니, kubelet들이 보고한 상태를 정리해서 보여주는 거구나. 그리고 이 모든 걸 처음 가능하게 만든 게 kubeadm이었던 거고.”

솔라는 더 이상 kubelet, kubeadm, kubectl을 ‘쿠버네티스’라는 하나의 덩어리로 보지 않았다. 각자의 역할과 책임이 명확한 세 개의 다른 도구였다.

그녀는 다시 자신의 메모장으로 돌아갔다. 그리고 3번 항목을 자신감 있게 수정하기 시작했다. ‘총괄 셰프 초빙’이라는 모호한 제목은 사라졌다. 그 자리에는 명확한 역할이 담긴 새로운 목록이 자리 잡았다.

3. Kubernetes 핵심 도구 설치

  • kubeadm: 클러스터를 만들고 노드를 추가하는 ‘설치 전문가’
  • kubelet: 각 노드에서 명령을 수행하고 상태를 보고하는 ‘현장 에이전트’
  • kubectl: 내가 클러스터에 명령을 내리는 ‘관리용 리모컨’

이제 솔라의 눈앞에 있는 설치 가이드는 더 이상 의문투성이의 명령어 나열이 아니었다. 명확한 의도를 가진 세 단계의 청사진이었다. 노드라는 땅을 다지고, 컨테이너 런타임이라는 엔진을 올리고, 그 위에 쿠버네티스라는 정교한 시스템을 구성하는 각기 다른 역할의 도구들을 설치하는 과정. 앞으로 어떤 쿠버네티스 설치 가이드를 보더라도, 그녀는 이제 ‘준비’와 ‘구성’ 그리고 ‘설치’를, 그 안에서도 각 도구의 역할을 명확히 구분해낼 수 있을 터였다.