쿠버네티스 공부 1 - node와 cluster

쿠버네티스 백업 쪽으로 만들 게 생겨서 공부를 시작했어요. 컨테이너는 돌려봤고 서버 한 대에 프로그램을 올려본 적도 있는데, 개념은 모르는 상태였어요.

1편에서는 node · Pod · controller 세 단어를 정리하고, 그걸 배우다가 클러스터에서 크게 한 번 걸린 이야기를 적었어요.

컨테이너는 가벼운 가상머신이 아니었어요

이것부터 잘못 알고 있었어요. 컨테이너를 작은 VM 같은 걸로 생각했거든요.

가상머신은 운영체제가 통째로 하나 더 들어 있는 별도의 컴퓨터예요. 그래서 무겁고, 켜는 데 수십 초가 걸려요. 반면 컨테이너는 그냥 프로세스예요. 내 노트북에서 도는 다른 프로그램과 똑같은 프로세스 하나고요. 다만 커널이 그 프로세스가 볼 수 있는 범위를 제한해요. 볼 수 있는 파일 시스템, 다른 프로세스가 보이는지, 자기 IP가 무엇인지를 따로 정해 줘요. 운영체제는 하나뿐이고, 프로세스마다 보이는 범위만 다른 거예요.

docker run 하면 거의 즉시 뜨는 이유도 이거예요. 새 OS를 부팅하는 게 아니라 프로세스 하나를 실행하는 거니까요.

여기서 중요한 사실이 하나 나와요. 컨테이너는 죽으면 그냥 죽어요. 프로세스니까요. 아무도 대신 살려주지 않아요. 뒤에 나오는 내용은 전부 이 사실에서 출발해요.

서버 한 대로는 안 되는 네 순간

레슨이 단어부터 주지 않고 곤란한 상황부터 보여줘서 좋았어요. 서버 한 대에 컨테이너를 띄워 두면 이런 일이 생겨요.

  1. 새벽 3시에 프로세스가 죽어요. 아침에 출근할 때까지 서비스가 멈춰 있어요. 누가 대신 살려주죠?
  2. 서버가 통째로 죽어요. 디스크가 나갔거나 전원이 나갔거나요. 프로세스를 살리는 걸로는 해결이 안 돼요. 다른 서버로 어떻게 옮기죠?
  3. 트래픽이 늘어서 서버가 세 대 필요해져요. 어디에 몇 개 띄울지 누가 계산하죠? 한 대는 놀고 한 대는 과부하라면요?
  4. 멈추지 않고 새 버전을 올려야 해요. 하나씩 내리고, 올리고, 확인하고, 안 되면 되돌리고. 이걸 매번 손으로 해야 할까요?

넷 다 사람이 붙어 있으면 풀리는 문제예요. 쿠버네티스는 그 네 가지를 하던 사람의 일을 대신하는 프로그램이었어요.

곤란한 상황을 먼저 보니까 단어를 따로 외우지 않아도 이해가 됐어요.

그래서 나온 단어 세 개

node

Pod이 실제로 도는 컴퓨터 한 대예요. 물리 서버든 클라우드 VM이든 상관없어요. 2번 문제(서버가 통째로 죽는다)를 풀려면 애초에 여러 대가 있어야 하니까 이 단어가 필요해요.

Pod

거의 언제나 컨테이너 하나를 감싸는 단위예요. 컨테이너를 직접 다루지 않고 한 겹을 더 둔 이유는, 가끔 두 컨테이너가 반드시 같은 기계에서 같은 IP를 공유하며 함께 시작하고 함께 종료돼야 하는 경우가 있어서예요. 그런 컨테이너 묶음을 하나로 다룰 단위가 필요했던 거예요.

그리고 여기서 제 프로젝트에 바로 걸리는 게 하나 있었어요. Pod은 일회용으로 설계됐어요. 죽으면 되살리는 게 아니라 버리고 새로 만들어요. 새로 만들어진 Pod은 이름도 IP도 달라요.

그래서 "실행 중인 Pod을 그대로 떠서 그대로 되돌린다"는 발상은 처음부터 성립하지 않아요. 백업이 보존해야 하는 건 "이런 Pod이 있어야 한다"는 기술(description)이에요. 실행 중인 Pod 자체는 언제든 새로 만들어지니까요. 나중에 spec과 status 구분으로 다시 나온다는데, 아직 거기까지는 안 갔어요.

controller

아주 단순한 루프를 계속 도는 프로그램이에요.

  1. 있어야 하는 상태를 읽어요 ("nginx Pod이 3개 있어야 한다")
  2. 지금 상태를 봐요 ("2개뿐이네")
  3. 차이를 줄이는 행동을 해요 ("하나 더 만들자")
  4. 1번으로 돌아가요. 이걸 멈추지 않고 반복해요.

공식 문서는 이걸 온도조절기에 비유해요. 25도로 맞춰 두면 방이 지금 몇 도인지 보고, 차이가 나면 보일러를 켜거나 꺼요. "25도로 만들어라"는 명령을 한 번 받고 끝나는 게 아니라, 계속 지켜보면서 차이를 줄여요.

위의 네 문제가 전부 이걸로 풀려요. 죽으면 다시 만들고, 서버가 나가면 다른 node에 만들고, 개수가 모자라면 채우고, 새 버전을 올릴 땐 원하는 상태를 바꿔 주면 하나씩 교체해요. 사람이 하던 일을 이 루프가 대신하는 거였어요.

실습은 쿠버네티스 없이 했어요

첫 실습이 클러스터 만들기가 아니라 Docker만으로 컨테이너 하나를 띄우고 죽여 보기였어요. 다시 살아나지 않아요. 아무도 살려주지 않고요.

쿠버네티스가 없을 때 어떤 문제가 생기는지 먼저 겪게 하는 구성인데, controller를 정의로 배우는 것보다 이게 빨랐어요. "상태 차이를 줄이는 루프"라는 말은 잘 와닿지 않는데, "죽였는데 아무도 안 살리네"는 바로 이해가 됐어요.

여기서 크게 한 번 걸렸어요

퀴즈에서 node가 무엇을 가리키는 말이냐는 문제에 "클러스터 전체를 묶는 이름"을 골라서 틀렸어요.

틀린 것보다 왜 그렇게 골랐는지가 문제였어요. 바로 이런 질문이 나왔거든요. "노드가 서버 한 대라면, 클러스터는 어디에 설치된 프로세스임?"

이 질문 안에 오해가 다 들어 있었어요. 쿠버네티스 개념을 전부 "어딘가에 설치되는 소프트웨어"로 생각하고 있었던 거예요. 서버 한 대에 프로그램을 올려본 경험만 있으면 자연스럽게 나오는 생각인 것 같아요.

cluster라는 프로세스는 없어요

실제로 기계에 설치돼 있는 것만 적으면 이게 전부예요.

TEXT
[서버 A]  kube-apiserver · etcd · kube-scheduler · kube-controller-manager · kubelet
[서버 B]  kubelet · kube-proxy · containerd
[서버 C]  kubelet · kube-proxy · containerd

cluster라는 이름의 프로세스는 없어요. 찾아봐도 없어요. 클러스터는 "같은 API 서버에 등록된 기계들의 집합"을 가리키는 이름이었어요.

서버 B의 kubelet이 서버 A의 API 서버를 보도록 설정돼 있으면 B는 그 클러스터 소속이에요. 설정을 바꿔서 다른 API 서버를 보게 하면, 아무것도 다시 설치하지 않았는데 다른 클러스터 소속이 돼요.

여기서 그냥 이해가 됐어요. 클러스터가 어디에 존재하는 건지 궁금했는데, 등록된 기계들을 묶어서 부르는 이름이었어요.

그럼 등록은 어떻게 하나요

기계 한 대를 넣는 데 필요한 건 세 개뿐이에요.

  • API 서버 주소 — 누구에게 요청할지
  • CA 인증서 지문 — node가 API 서버를 믿는 근거
  • 자격증명(부트스트랩 토큰) — API 서버가 node를 믿는 근거

신뢰가 양방향이라 둘이 아니라 셋이에요. 아무 서버나 "내가 API 서버야"라고 하면 안 되고, 아무 기계나 들어와도 안 되니까요.

Bash
kubeadm join 10.0.0.5:6443 \
  --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:1a2b3c...

그다음이 등록이에요. kubelet이 토큰으로 임시 인증해서 자기 클라이언트 인증서를 발급받고(TLS bootstrapping), 그 인증서로 API 서버에 자기를 Node 오브젝트로 만들어요. 이후엔 "나 살아 있음, 자원 이만큼 남음"을 주기적으로 갱신하고요.

그러니까 이 기계에 설치되는 건 kubelet 하나고, 가입은 API 서버의 목록에 줄 하나가 생기는 일이었어요.

탈퇴도 같은 구조라서 신기했어요. kubectl delete node를 하면 그 줄만 지워지고 기계는 그대로 돌아가요. kubelet이 살아 있으면 잠시 뒤에 자기를 다시 등록하고요. 목록에서 지우는 것과 기계를 실제로 없애는 것은 별개의 일이에요.

master node는 따로 있는 종류인가요

하나 더 물어봤어요. 마스터 노드가 따로 있는 걸로 알고 있었거든요. 클러스터 노드라는 게 따로 있는 건가 싶었어요.

따로 있는 종류는 없었어요. control plane node도 kubelet이 돌고 위와 똑같이 등록된 그냥 node예요. 차이는 둘뿐이에요.

  • 위에서 도는 프로세스가 달라요. kube-apiserver, etcd, kube-scheduler, kube-controller-manager가 추가로 돌아요. 그런데 대개 이것들조차 Pod으로 떠 있어요.
  • 표식이 하나 붙어 있어요. node-role.kubernetes.io/control-plane:NoSchedule이라는 taint예요. "일반 앱은 여기 배치하지 마"라고 스케줄러에게 주는 규칙 문자열이고, 물리적인 제약은 아니에요.

물리적인 제약이 아니라는 건, 이 taint를 떼면 그 기계에도 앱이 뜬다는 뜻이에요. 실제로 kind로 만드는 단일 노드 클러스터는 이 taint를 떼고 쓴다고 해요. 노드가 하나뿐인데 앱을 못 올리면 아무것도 할 수 없으니까요.

master는 폐기된 이름이고 지금은 control plane node라고 불러요. 한 대일 필요도 없어서, 운영 환경에선 etcd 정족수 때문에 보통 3대 이상을 둔다는데 이건 아직 안 해봐서 잘 모르겠어요.

1편 정리

  • 컨테이너는 VM이 아니고, 보이는 범위가 제한된 프로세스예요. 죽으면 그냥 죽어요.
  • 서버 한 대로는 안 되는 네 순간이 있고, 쿠버네티스는 그때 사람이 하던 일을 대신해요.
  • node는 기계 한 대, Pod은 컨테이너를 감싸는 단위, controller는 원하는 상태와 지금 상태의 차이를 줄이는 루프예요.
  • Pod은 일회용이라 그대로 떠서 되돌릴 수 없어요. 백업은 실행 중인 Pod 대신 "이런 Pod이 있어야 한다"는 기술을 보존해야 해요.

그리고 제가 틀렸던 것들이에요.

제가 생각한 것실제
컨테이너는 가벼운 VM보이는 범위가 제한된 프로세스
클러스터는 설치된 프로그램등록된 기계들을 부르는 이름
가입은 설치가입은 등록
master는 특별한 기계master는 그 기계가 맡은 역할

아래 셋은 전부 같은 오해였어요. 이름일 뿐인 것을 실제로 설치된 무언가로 생각한 거예요. namespace나 Service를 배울 때도 같은 데서 걸릴 것 같은 느낌이에요.

아직 정리가 다 된 건 아니에요. 그래도 감은 좀 오는 것 같아요. 새 단어가 나오면 일단 이게 프로세스인지 이름인지부터 물어봐야겠어요.

다음 편은 API 서버예요.