본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 29. · 15 Views
Kubernetes 아키텍처 완벽 가이드
Kubernetes가 어떻게 컨테이너를 관리하는지, 그 내부 구조를 초급 개발자도 이해할 수 있도록 쉽게 설명합니다. Control Plane부터 Worker Node까지 핵심 구성 요소를 실무 예제와 함께 살펴봅니다.
목차
- Control_Plane_구성_요소
- API_Server_역할과_동작
- etcd_분산_저장소
- Scheduler와_Controller_Manager
- Worker_Node_구성_요소
- Kubelet과_Kube_proxy
- Container_Runtime_이해
1. Control Plane 구성 요소
김개발 씨는 회사에서 처음으로 Kubernetes 클러스터를 관리하게 되었습니다. 처음 접하는 용어들 사이에서 "Control Plane이 뭐지?"라는 의문이 떠올랐습니다.
마치 복잡한 기계를 처음 마주한 것처럼, 어디서부터 시작해야 할지 막막했습니다.
Control Plane은 Kubernetes 클러스터 전체를 관리하는 두뇌와 같습니다. 마치 공항의 관제탑이 모든 비행기의 이착륙을 조율하듯이, Control Plane은 모든 컨테이너의 배포와 상태를 관리합니다.
이것을 이해하면 Kubernetes가 어떻게 수천 개의 컨테이너를 안정적으로 운영하는지 알 수 있습니다.
다음 코드를 살펴봅시다.
# Control Plane 구성 요소 확인
kubectl get componentstatuses
# Control Plane이 실행 중인 노드 확인
kubectl get nodes -l node-role.kubernetes.io/control-plane
# Control Plane의 핵심 Pod 조회
kubectl get pods -n kube-system
# 각 구성 요소의 상태 확인
kubectl describe pod kube-apiserver-master -n kube-system
kubectl describe pod etcd-master -n kube-system
kubectl describe pod kube-scheduler-master -n kube-system
kubectl describe pod kube-controller-manager-master -n kube-system
김개발 씨는 입사 6개월 차 주니어 DevOps 엔지니어입니다. 어느 날 팀장님이 다가와 말했습니다.
"개발 씨, 이번 주부터 우리 Kubernetes 클러스터 관리를 맡아볼래요?" 김개발 씨는 Docker는 어느 정도 알고 있었지만, Kubernetes는 처음이었습니다. 검색해보니 Control Plane, Node, Pod, Service...
낯선 용어들이 쏟아졌습니다. "대체 이게 다 뭐지?" 옆자리 박시니어 씨가 김개발 씨의 당황한 표정을 보고 다가왔습니다.
"처음엔 다 그래요. 하나씩 알아가면 돼요.
먼저 Control Plane부터 이해해봐요." Control Plane이란 정확히 무엇일까요? 쉽게 비유하자면, Control Plane은 마치 공항의 관제탑과 같습니다.
관제탑에서는 모든 비행기의 이착륙 시간을 조율하고, 활주로를 배정하며, 비상 상황에 대응합니다. 비행기 조종사들은 관제탑의 지시에 따라 안전하게 운항합니다.
Kubernetes에서 Control Plane은 바로 이 관제탑 역할을 합니다. 어떤 컨테이너가 어디서 실행되어야 하는지, 현재 상태는 어떤지, 문제가 생기면 어떻게 대응해야 하는지를 모두 결정합니다.
Control Plane 없이 수백 개의 컨테이너를 관리한다고 상상해봅시다. 개발자가 직접 "이 컨테이너는 서버 A에서 실행하고, 저 컨테이너는 서버 B에서 실행해야지"라고 일일이 지정해야 합니다.
서버 하나가 죽으면? 수동으로 다른 서버에 컨테이너를 다시 배포해야 합니다.
트래픽이 늘어나면? 역시 수동으로 컨테이너를 추가해야 합니다.
이런 작업을 24시간 내내 할 수는 없습니다. 바로 이 문제를 해결하기 위해 Control Plane이 존재합니다.
Control Plane은 크게 네 가지 핵심 구성 요소로 이루어져 있습니다. API Server, etcd, Scheduler, 그리고 Controller Manager입니다.
API Server는 Control Plane의 현관문입니다. 모든 요청은 여기를 통해 들어옵니다.
etcd는 클러스터의 모든 상태를 저장하는 데이터베이스입니다. Scheduler는 새로운 컨테이너를 어디에 배치할지 결정합니다.
Controller Manager는 클러스터가 원하는 상태를 유지하도록 지속적으로 감시하고 조정합니다. 위의 명령어들을 실행해보면 Control Plane의 구성 요소들이 kube-system 네임스페이스에서 Pod 형태로 실행되고 있음을 확인할 수 있습니다.
이들은 서로 긴밀하게 협력하며 클러스터를 운영합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 쇼핑몰 서비스를 운영한다고 가정해봅시다. 블랙프라이데이에 트래픽이 10배로 늘어났습니다.
Control Plane은 자동으로 이를 감지하고, 더 많은 컨테이너를 생성하여 부하를 분산시킵니다. 개발자가 새벽에 일어나 수동으로 서버를 추가할 필요가 없습니다.
하지만 주의할 점도 있습니다. Control Plane이 죽으면 클러스터 전체를 관리할 수 없게 됩니다.
따라서 실제 운영 환경에서는 고가용성(High Availability) 구성을 통해 Control Plane을 여러 대로 분산시킵니다. 마치 공항에 관제탑이 하나만 있으면 위험하듯이, 백업 시스템이 필요합니다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, Control Plane이 관제탑이라면, 각각의 구성 요소는 관제탑 안의 담당자들인 거네요!" Control Plane의 전체 구조를 이해하면 나머지 구성 요소들도 자연스럽게 이해됩니다.
이제 각 구성 요소를 하나씩 자세히 살펴보겠습니다.
실전 팁
💡 - Control Plane은 운영 환경에서 반드시 고가용성 구성을 해야 합니다
- kubectl get componentstatuses 명령으로 Control Plane 상태를 주기적으로 확인하세요
- Control Plane 노드에는 일반 워크로드를 배치하지 않는 것이 좋습니다
2. API Server 역할과 동작
김개발 씨가 kubectl apply 명령을 실행하자, 신기하게도 컨테이너가 생성되었습니다. "어떻게 명령 하나로 이런 일이 일어나는 거지?" 궁금증이 생겼습니다.
박시니어 씨가 웃으며 말했습니다. "그건 API Server가 다 처리해주거든요."
API Server는 Kubernetes의 중앙 통신 허브입니다. 마치 회사의 안내 데스크처럼 모든 요청을 받아서 적절한 곳으로 전달합니다.
사용자의 kubectl 명령, 다른 구성 요소들의 요청, 외부 시스템의 호출까지 모든 것이 API Server를 통해 처리됩니다.
다음 코드를 살펴봅시다.
# API Server 상태 확인
kubectl cluster-info
# API Server에 직접 요청 보내기 (REST API)
kubectl get --raw /api/v1/namespaces/default/pods
# API Server 버전 확인
kubectl api-versions
# API 리소스 목록 조회
kubectl api-resources
# API Server 로그 확인
kubectl logs kube-apiserver-master -n kube-system --tail=50
# 특정 리소스에 대한 API 경로 확인
kubectl get pods -v=6
김개발 씨는 매일 kubectl 명령을 사용하면서도 그 뒤에서 무슨 일이 일어나는지 몰랐습니다. 어느 날 호기심이 생겨 박시니어 씨에게 물었습니다.
"선배, kubectl apply 하면 정확히 어떤 일이 일어나는 거예요?" 박시니어 씨가 화이트보드 앞으로 걸어갔습니다. "좋은 질문이에요.
핵심은 API Server예요." API Server란 정확히 무엇일까요? 쉽게 비유하자면, API Server는 마치 회사의 안내 데스크와 같습니다.
손님이 회사에 방문하면 먼저 안내 데스크에서 신원을 확인합니다. 그다음 방문 목적을 파악하고, 적절한 담당자에게 연결해줍니다.
허가되지 않은 방문객은 건물 안으로 들어갈 수 없습니다. API Server도 마찬가지입니다.
모든 요청은 먼저 API Server에 도착합니다. API Server는 요청자의 신원을 확인하고(인증), 해당 작업을 할 권한이 있는지 검사하고(인가), 요청 내용이 올바른지 확인합니다(유효성 검사).
API Server 없이 Kubernetes를 운영한다고 상상해봅시다. Scheduler가 직접 etcd에 접근하고, Controller Manager도 직접 etcd를 수정하고, 사용자도 etcd에 직접 쓰기를 시도합니다.
데이터 일관성은 깨지고, 보안은 엉망이 됩니다. 누가 언제 무엇을 했는지 추적도 불가능합니다.
바로 이런 혼란을 방지하기 위해 API Server가 단일 진입점 역할을 합니다. API Server의 동작 과정을 단계별로 살펴보겠습니다.
김개발 씨가 kubectl apply -f deployment.yaml 명령을 실행했다고 가정합니다. 첫 번째로, kubectl은 YAML 파일을 읽어 JSON 형태로 변환합니다.
그다음 API Server의 REST 엔드포인트로 HTTP POST 요청을 보냅니다. 두 번째로, API Server는 요청을 받아 **인증(Authentication)**을 수행합니다.
"이 요청을 보낸 사람이 누구인가?" kubeconfig 파일에 있는 인증서나 토큰을 확인합니다. 세 번째로, **인가(Authorization)**를 수행합니다.
"이 사람이 이 작업을 할 권한이 있는가?" RBAC 규칙을 확인합니다. 네 번째로, Admission Control을 거칩니다.
"이 요청이 우리 정책에 맞는가?" 리소스 제한, 네임스페이스 규칙 등을 검사합니다. 다섯 번째로, 모든 검사를 통과하면 API Server는 요청 내용을 etcd에 저장합니다.
이때 비로소 "원하는 상태(Desired State)"가 기록됩니다. 마지막으로, API Server는 다른 구성 요소들에게 변경 사항을 알립니다.
Scheduler는 새 Pod가 생겼음을 감지하고, 적절한 노드를 찾아 배치합니다. 위의 코드에서 kubectl get --raw 명령을 사용하면 API Server에 직접 REST 요청을 보낼 수 있습니다.
kubectl은 결국 이 REST API를 편리하게 사용하도록 감싸준 CLI 도구일 뿐입니다. 실제 현업에서는 kubectl 외에도 다양한 방식으로 API Server와 통신합니다.
CI/CD 파이프라인에서 API를 직접 호출하기도 하고, Helm이나 ArgoCD 같은 도구들도 모두 API Server를 통해 작업합니다. 하지만 주의할 점이 있습니다.
API Server가 병목이 되면 클러스터 전체가 느려집니다. 따라서 대규모 클러스터에서는 API Server를 여러 대로 확장하고, 요청 제한(Rate Limiting)을 설정해야 합니다.
김개발 씨가 감탄했습니다. "그러니까 kubectl은 그냥 API Server에 HTTP 요청을 보내는 거네요?" 박시니어 씨가 고개를 끄덕였습니다.
"맞아요. 이걸 알면 디버깅할 때 훨씬 수월해져요." API Server를 이해하면 Kubernetes의 모든 동작이 명확해집니다.
다음으로 이 모든 상태를 저장하는 etcd를 살펴보겠습니다.
실전 팁
💡 - kubectl에 -v=6 옵션을 주면 API Server와 주고받는 요청을 볼 수 있습니다
- API Server 접근 권한은 RBAC으로 세밀하게 관리하세요
- API Server 앞에 로드밸런서를 두어 고가용성을 확보하세요
3. etcd 분산 저장소
어느 날 김개발 씨가 실수로 Deployment를 삭제했습니다. 다행히 복구했지만, 궁금증이 생겼습니다.
"Kubernetes는 모든 설정을 어디에 저장하는 거지?" 박시니어 씨가 답했습니다. "etcd라는 특별한 데이터베이스가 있어요."
etcd는 Kubernetes의 두뇌가 기억을 저장하는 곳입니다. 마치 은행의 금고처럼 클러스터의 모든 중요한 정보를 안전하게 보관합니다.
분산 키-값 저장소로서 높은 가용성과 일관성을 제공하며, 클러스터의 "진실의 원천(Source of Truth)"입니다.
다음 코드를 살펴봅시다.
# etcd Pod 상태 확인
kubectl get pods -n kube-system -l component=etcd
# etcd 클러스터 상태 확인 (etcdctl 사용)
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list
# etcd에 저장된 키 조회 (주의: 운영 환경에서 조심)
ETCDCTL_API=3 etcdctl get /registry/pods --prefix --keys-only
# etcd 스냅샷 백업
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot.db
# 백업 상태 확인
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot.db
김개발 씨는 kubectl get deployment 명령을 실행할 때마다 신기했습니다. "이 정보들은 대체 어디에 저장되어 있는 거지?
서버를 재시작해도 그대로 있으니까 파일로 저장되는 건가?" 박시니어 씨가 설명을 시작했습니다. "Kubernetes의 모든 데이터는 etcd라는 데이터베이스에 저장돼요.
이게 없으면 Kubernetes는 아무것도 기억하지 못해요." etcd란 정확히 무엇일까요? 쉽게 비유하자면, etcd는 마치 은행의 금고와 같습니다.
은행은 고객의 모든 자산 정보를 금고에 안전하게 보관합니다. 금고가 털리면 은행 전체가 마비됩니다.
그래서 은행은 금고를 여러 곳에 분산해두고, 정기적으로 백업합니다. etcd도 마찬가지입니다.
클러스터의 모든 설정, Pod 정보, Service 정보, ConfigMap, Secret 등 모든 것이 여기에 저장됩니다. etcd가 망가지면 클러스터는 자신이 무엇을 하고 있었는지 완전히 잊어버립니다.
etcd는 단순한 데이터베이스가 아닙니다. 분산 시스템입니다.
일반적인 데이터베이스는 한 대의 서버에서 실행됩니다. 그 서버가 죽으면 끝입니다.
하지만 etcd는 여러 대의 서버에 데이터를 복제합니다. 한 대가 죽어도 다른 서버들이 계속 서비스를 제공합니다.
이를 위해 etcd는 Raft 합의 알고리즘을 사용합니다. 쉽게 말해, 여러 etcd 서버가 "이 데이터가 맞아요"라고 합의를 본 후에야 데이터를 저장합니다.
덕분에 어떤 서버에 접근해도 항상 같은 데이터를 볼 수 있습니다. etcd에 저장되는 데이터의 구조를 살펴볼까요?
etcd는 키-값(Key-Value) 저장소입니다. 모든 데이터는 키와 값의 쌍으로 저장됩니다.
예를 들어 default 네임스페이스의 nginx-pod라는 Pod는 /registry/pods/default/nginx-pod라는 키에 저장됩니다. 위의 코드에서 etcdctl get 명령을 사용하면 실제로 etcd에 저장된 키들을 볼 수 있습니다.
물론 운영 환경에서는 직접 etcd를 조작하면 안 됩니다. 반드시 API Server를 통해 접근해야 합니다.
API Server가 etcd의 유일한 클라이언트라는 점이 중요합니다. Scheduler도, Controller Manager도, kubectl도 직접 etcd에 접근하지 않습니다.
모두 API Server를 거칩니다. 이를 통해 데이터 일관성과 보안을 유지합니다.
실제 현업에서 가장 중요한 것은 백업입니다. etcd 데이터가 날아가면 클러스터 전체를 처음부터 다시 구축해야 합니다.
모든 Deployment, Service, ConfigMap, Secret이 사라집니다. 따라서 정기적인 스냅샷 백업은 선택이 아니라 필수입니다.
위의 코드에서 etcdctl snapshot save 명령을 사용하면 etcd의 스냅샷을 저장할 수 있습니다. 이 백업 파일만 있으면 클러스터를 복구할 수 있습니다.
주의할 점이 있습니다. etcd는 홀수 개의 멤버로 구성해야 합니다.
3대, 5대, 7대처럼요. 이유는 Raft 합의 알고리즘 때문입니다.
과반수가 동의해야 데이터를 쓸 수 있는데, 짝수면 동점이 발생할 수 있습니다. 또한 etcd는 디스크 I/O에 민감합니다.
SSD를 사용하고, 다른 워크로드와 분리된 전용 디스크를 사용하는 것이 좋습니다. 김개발 씨가 이해했다는 듯 고개를 끄덕였습니다.
"그러니까 etcd는 Kubernetes의 기억 장치네요. 백업 안 하면 기억 상실증에 걸리는 거고요." 박시니어 씨가 웃었습니다.
"비유가 적절해요!" etcd를 이해했으니, 이제 새로운 Pod를 어디에 배치할지 결정하는 Scheduler를 살펴보겠습니다.
실전 팁
💡 - etcd 백업은 매일, 최소한 매주 수행하세요
- etcd 멤버는 반드시 홀수 개(3, 5, 7)로 구성하세요
- etcd 전용 SSD 디스크를 사용하면 성능이 크게 향상됩니다
4. Scheduler와 Controller Manager
김개발 씨가 새로운 Pod를 배포했는데, 어느 노드에서 실행될지는 지정하지 않았습니다. 그런데 Pod는 알아서 적절한 노드에 배치되었습니다.
"누가 이걸 결정하는 거지?" 바로 Scheduler와 Controller Manager의 역할입니다.
Scheduler는 새로운 Pod를 어떤 노드에 배치할지 결정하는 배치 담당자입니다. Controller Manager는 클러스터가 원하는 상태를 유지하도록 지속적으로 감시하고 조정하는 관리자입니다.
마치 호텔의 프런트 데스크가 손님을 방에 배정하고, 객실 관리자가 모든 방을 점검하는 것과 같습니다.
다음 코드를 살펴봅시다.
# Scheduler 상태 확인
kubectl get pods -n kube-system -l component=kube-scheduler
# Controller Manager 상태 확인
kubectl get pods -n kube-system -l component=kube-controller-manager
# Pod의 스케줄링 이벤트 확인
kubectl describe pod <pod-name> | grep -A 5 Events
# 특정 노드에 Pod 스케줄링 (nodeSelector 예시)
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector:
disk: ssd
containers:
- name: nginx
image: nginx:latest
김개발 씨가 kubectl apply로 Deployment를 생성했습니다. replicas: 3이라고 설정했더니 Pod 3개가 서로 다른 노드에 분산되어 실행되고 있었습니다.
"내가 노드를 지정한 적이 없는데, 누가 이렇게 배치한 거지?" 박시니어 씨가 설명했습니다. "그건 Scheduler가 한 일이에요.
그리고 Pod가 3개 유지되는 건 Controller Manager 덕분이고요." 먼저 Scheduler를 살펴볼까요? Scheduler는 마치 호텔의 프런트 데스크 직원과 같습니다.
손님이 체크인하면 프런트 직원은 빈 방 중에서 적절한 방을 배정합니다. 가족 손님에게는 넓은 방을, 비즈니스 손님에게는 조용한 층을 배정하죠.
Scheduler도 마찬가지입니다. 새로운 Pod가 생성되면 Scheduler는 클러스터의 모든 노드를 살펴봅니다.
각 노드의 CPU, 메모리 여유 공간을 확인하고, Pod의 요구 사항과 비교하여 가장 적합한 노드를 선택합니다. Scheduler의 스케줄링 과정은 두 단계로 이루어집니다.
첫 번째는 필터링(Filtering) 단계입니다. Pod를 실행할 수 없는 노드를 제외합니다.
예를 들어 메모리가 부족하거나, 특정 레이블이 없거나, 테인트(Taint)가 설정된 노드는 후보에서 제외됩니다. 두 번째는 스코어링(Scoring) 단계입니다.
남은 노드들에 점수를 매깁니다. 리소스가 더 여유 있는 노드, Pod가 적게 실행 중인 노드에 더 높은 점수를 줍니다.
가장 높은 점수를 받은 노드에 Pod가 배치됩니다. 위의 코드에서 nodeSelector를 사용하면 특정 레이블이 있는 노드에만 Pod를 배치할 수 있습니다.
disk: ssd 레이블이 있는 노드만 후보가 되는 것이죠. 이제 Controller Manager를 살펴볼까요?
Controller Manager는 마치 호텔의 객실 관리자와 같습니다. 객실 관리자는 지속적으로 모든 방을 순회하며 상태를 점검합니다.
청소가 필요한 방은 청소팀에 알리고, 고장 난 시설은 수리팀에 알립니다. 항상 모든 방이 "완벽한 상태"를 유지하도록 관리합니다.
Controller Manager는 Kubernetes가 **원하는 상태(Desired State)**와 **현재 상태(Current State)**를 계속 비교합니다. 만약 차이가 있으면 현재 상태를 원하는 상태로 맞추기 위해 조치를 취합니다.
예를 들어 replicas: 3으로 설정했는데 현재 Pod가 2개만 실행 중이라면? Controller Manager는 이를 감지하고 새로운 Pod를 생성합니다.
노드가 죽어서 Pod가 사라져도 자동으로 다른 노드에 새 Pod를 생성합니다. Controller Manager 안에는 여러 종류의 Controller가 있습니다.
Replication Controller는 지정된 수의 Pod가 항상 실행되도록 합니다. Node Controller는 노드의 상태를 모니터링하고 응답이 없으면 대응합니다.
Endpoint Controller는 Service와 Pod를 연결합니다. Service Account Controller는 새 네임스페이스에 기본 Service Account를 생성합니다.
이들이 모두 조화롭게 동작하며 클러스터를 자가 치유(Self-healing) 상태로 유지합니다. 실제 현업에서는 이 자동화가 엄청난 가치를 발휘합니다.
새벽에 서버 하나가 죽어도 개발자가 잠에서 깨어날 필요가 없습니다. Controller Manager가 자동으로 Pod를 다른 노드로 옮겨주니까요.
주의할 점도 있습니다. 너무 많은 Pod를 한 번에 생성하면 Scheduler가 과부하될 수 있습니다.
또한 Controller가 무한 루프에 빠지지 않도록 적절한 백오프 정책이 설정되어 있어야 합니다. 김개발 씨가 감탄했습니다.
"그러니까 내가 '이렇게 되었으면 좋겠다'고 선언만 하면, Scheduler가 어디에 배치할지 결정하고, Controller Manager가 그 상태를 계속 유지해주는 거네요!" 박시니어 씨가 미소 지었습니다. "맞아요.
그게 바로 Kubernetes의 선언적(Declarative) 철학이에요."
실전 팁
💡 - Pod가 Pending 상태면 kubectl describe로 스케줄링 실패 원인을 확인하세요
- 노드에 적절한 레이블을 붙여 스케줄링을 제어할 수 있습니다
- Controller Manager 로그를 모니터링하면 클러스터 문제를 조기에 발견할 수 있습니다
5. Worker Node 구성 요소
지금까지 Control Plane의 구성 요소들을 살펴봤습니다. 이제 김개발 씨는 궁금해졌습니다.
"그래서 실제로 컨테이너는 어디서 실행되는 거예요?" 박시니어 씨가 답했습니다. "그건 Worker Node의 일이에요."
Worker Node는 실제로 컨테이너가 실행되는 서버입니다. 마치 공장의 생산 라인처럼 Control Plane의 지시에 따라 실제 작업을 수행합니다.
각 Worker Node에는 Kubelet, Kube-proxy, Container Runtime이라는 핵심 구성 요소가 있습니다.
다음 코드를 살펴봅시다.
# 모든 노드 조회
kubectl get nodes
# 노드 상세 정보 확인
kubectl describe node <node-name>
# 노드의 리소스 사용량 확인
kubectl top node
# 특정 노드에서 실행 중인 Pod 확인
kubectl get pods --all-namespaces -o wide --field-selector spec.nodeName=<node-name>
# 노드 레이블 확인 및 추가
kubectl get nodes --show-labels
kubectl label nodes <node-name> disk=ssd
# 노드 상태 조건 확인
kubectl get nodes -o jsonpath='{.items[*].status.conditions}'
김개발 씨는 Control Plane이 클러스터의 두뇌라는 것을 이해했습니다. 하지만 두뇌만으로는 일을 할 수 없습니다.
실제로 손과 발이 움직여야 합니다. Kubernetes에서 그 역할을 하는 것이 바로 Worker Node입니다.
박시니어 씨가 비유를 들었습니다. "Control Plane이 본사 사무실이라면, Worker Node는 공장이에요." Worker Node란 정확히 무엇일까요?
본사에서는 전략을 세우고, 생산 계획을 수립하고, 품질을 관리합니다. 하지만 실제로 제품을 만드는 것은 공장입니다.
공장에는 생산 라인, 작업자, 각종 장비가 있습니다. Worker Node도 마찬가지입니다.
Control Plane이 "nginx 컨테이너 3개를 실행해라"고 결정하면, 실제로 컨테이너를 띄우는 것은 Worker Node입니다. Worker Node는 실제 컴퓨팅 리소스(CPU, 메모리, 디스크, 네트워크)를 제공하는 서버입니다.
Worker Node의 세 가지 핵심 구성 요소를 알아볼까요? 첫 번째는 Kubelet입니다.
Kubelet은 Worker Node에서 실행되는 에이전트입니다. Control Plane의 지시를 받아 컨테이너를 실행하고, 상태를 보고합니다.
마치 공장의 현장 감독과 같습니다. 두 번째는 Kube-proxy입니다.
Kube-proxy는 네트워크 규칙을 관리합니다. Pod 간의 통신, 외부에서 Pod로의 접근을 가능하게 합니다.
마치 공장의 물류 담당자와 같습니다. 세 번째는 Container Runtime입니다.
Container Runtime은 실제로 컨테이너를 실행하는 소프트웨어입니다. Docker, containerd, CRI-O 등이 있습니다.
마치 공장의 생산 장비와 같습니다. 위의 코드에서 kubectl get nodes 명령을 실행하면 클러스터의 모든 노드를 볼 수 있습니다.
Ready 상태인 노드만 Pod를 받을 수 있습니다. NotReady 상태면 해당 노드에 문제가 있는 것입니다.
kubectl describe node 명령으로 노드의 상세 정보를 확인할 수 있습니다. 여기서 Capacity와 Allocatable 섹션을 보면 노드의 총 리소스와 Pod에 할당 가능한 리소스를 알 수 있습니다.
시스템 데몬이 일부 리소스를 사용하므로 두 값이 다릅니다. kubectl top node 명령은 현재 리소스 사용량을 보여줍니다.
이를 통해 어떤 노드가 부하가 많은지, 스케일 아웃이 필요한지 판단할 수 있습니다. 실제 현업에서 Worker Node를 어떻게 운영할까요?
대부분의 서비스는 여러 대의 Worker Node로 구성됩니다. 한 노드가 죽어도 다른 노드에서 서비스가 계속되어야 하니까요.
또한 노드마다 특성이 다를 수 있습니다. GPU가 있는 노드, SSD가 있는 노드, 메모리가 큰 노드 등.
이를 레이블로 구분하여 적절한 Pod를 배치합니다. 주의할 점도 있습니다.
Worker Node가 많아질수록 관리가 복잡해집니다. 노드의 OS 업데이트, 보안 패치, 모니터링 등을 일일이 관리해야 합니다.
그래서 클라우드 환경에서는 관리형 Kubernetes 서비스(EKS, GKE, AKS)를 많이 사용합니다. 김개발 씨가 정리했습니다.
"그러니까 Control Plane은 결정하고, Worker Node는 실행하는 거네요." 박시니어 씨가 덧붙였습니다. "맞아요.
이제 Worker Node의 각 구성 요소를 더 자세히 알아볼까요?"
실전 팁
💡 - 노드 상태가 NotReady면 Kubelet 로그를 먼저 확인하세요
- 노드 레이블을 활용하여 워크로드를 적절히 분산시키세요
- 노드의 리소스 사용량을 정기적으로 모니터링하세요
6. Kubelet과 Kube proxy
김개발 씨는 Worker Node에서 가장 중요한 두 구성 요소인 Kubelet과 Kube-proxy가 어떻게 동작하는지 궁금해졌습니다. "Kubelet이 컨테이너를 관리한다고 했는데, 정확히 어떻게 하는 거예요?"
Kubelet은 각 Worker Node에서 실행되는 에이전트로, Pod의 생명주기를 관리합니다. Kube-proxy는 네트워크 규칙을 관리하여 Pod 간 통신과 서비스 접근을 가능하게 합니다.
마치 Kubelet은 각 사무실의 관리인이고, Kube-proxy는 건물의 우편 배달부와 같습니다.
다음 코드를 살펴봅시다.
# Kubelet 상태 확인 (노드에서)
systemctl status kubelet
# Kubelet 로그 확인
journalctl -u kubelet -f
# Kubelet 설정 확인
kubectl get configmap kubelet-config -n kube-system -o yaml
# Kube-proxy Pod 확인
kubectl get pods -n kube-system -l k8s-app=kube-proxy
# Kube-proxy 모드 확인
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# 서비스와 엔드포인트 확인
kubectl get services
kubectl get endpoints
# iptables 규칙 확인 (kube-proxy가 생성)
iptables -t nat -L KUBE-SERVICES -n
김개발 씨가 질문했습니다. "Kubelet이 컨테이너를 관리한다고 했는데, API Server하고는 어떻게 통신하는 거예요?" 박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다.
먼저 Kubelet부터 자세히 알아볼까요? Kubelet은 마치 아파트 관리인과 같습니다.
관리인은 건물주(Control Plane)의 지시를 받아 각 세대(Pod)를 관리합니다. 입주자를 안내하고, 문제가 생기면 보고하고, 정기적으로 상태를 점검합니다.
Kubelet은 각 Worker Node에서 데몬 프로세스로 실행됩니다. Control Plane의 일부가 아니라 Worker Node 자체의 일부입니다.
부팅 시 자동으로 시작되며, 시스템 서비스로 관리됩니다. Kubelet의 핵심 역할은 다음과 같습니다.
첫째, API Server와 통신합니다. Kubelet은 API Server에 지속적으로 연결하여 자신의 노드에 배정된 Pod 목록을 받아옵니다.
새로운 Pod가 할당되면 이를 감지합니다. 둘째, Pod 사양을 실행합니다.
Pod 사양에 따라 Container Runtime에 컨테이너 생성을 요청합니다. 볼륨을 마운트하고, 환경 변수를 설정하고, 네트워크를 구성합니다.
셋째, 상태를 보고합니다. 컨테이너의 상태(Running, Waiting, Terminated)를 지속적으로 모니터링하고 API Server에 보고합니다.
노드 자체의 상태(CPU, 메모리, 디스크)도 함께 보고합니다. 넷째, Liveness/Readiness Probe를 실행합니다.
Pod 사양에 정의된 헬스 체크를 수행합니다. 실패하면 컨테이너를 재시작하거나 트래픽을 차단합니다.
위의 코드에서 systemctl status kubelet 명령으로 Kubelet의 상태를 확인할 수 있습니다. 문제가 생기면 journalctl로 로그를 확인합니다.
이제 Kube-proxy를 살펴볼까요? Kube-proxy는 마치 건물의 우편 배달부와 같습니다.
누군가 "101호에 편지를 보내주세요"라고 하면, 배달부는 101호가 어디인지 알고 있어서 정확히 전달합니다. 101호 사람이 이사를 가면 배달부가 새 주소를 업데이트합니다.
Kube-proxy는 Kubernetes Service의 네트워킹을 담당합니다. Service는 여러 Pod를 하나의 고정된 주소로 묶어주는 추상화입니다.
클라이언트는 Service의 주소로 요청을 보내고, Kube-proxy가 이를 적절한 Pod로 라우팅합니다. Kube-proxy는 세 가지 모드로 동작할 수 있습니다.
iptables 모드가 가장 일반적입니다. Kube-proxy가 리눅스의 iptables 규칙을 생성하여 네트워크 패킷을 라우팅합니다.
패킷이 커널 레벨에서 처리되므로 매우 빠릅니다. IPVS 모드는 대규모 클러스터에서 사용합니다.
iptables보다 더 효율적으로 많은 수의 Service를 처리할 수 있습니다. userspace 모드는 가장 오래된 방식으로, 현재는 거의 사용하지 않습니다.
위의 코드에서 iptables 명령으로 Kube-proxy가 생성한 규칙을 확인할 수 있습니다. KUBE-SERVICES 체인에 각 Service에 대한 규칙이 있습니다.
Kubelet과 Kube-proxy가 협력하는 예를 들어볼까요? 김개발 씨가 웹 서버 Pod 3개를 배포하고 Service를 생성했습니다.
Kubelet은 각 노드에서 웹 서버 컨테이너를 실행합니다. Kube-proxy는 Service의 ClusterIP로 오는 트래픽을 3개의 Pod에 분산시킵니다.
Pod 하나가 죽으면 Kubelet이 새 Pod를 시작하고, Kube-proxy가 네트워크 규칙을 업데이트합니다. 주의할 점도 있습니다.
Kubelet이 죽으면 해당 노드의 Pod는 관리되지 않습니다. API Server는 노드가 NotReady 상태가 되었음을 감지하고, 일정 시간 후 Pod를 다른 노드로 재스케줄링합니다.
김개발 씨가 이해했습니다. "그러니까 Kubelet은 컨테이너 자체를 관리하고, Kube-proxy는 컨테이너로 가는 길을 관리하는 거네요!" 박시니어 씨가 엄지를 치켜세웠습니다.
실전 팁
💡 - Kubelet 문제는 journalctl -u kubelet로 로그를 확인하세요
- Service가 안 되면 kubectl get endpoints로 엔드포인트 연결 상태를 확인하세요
- 대규모 클러스터에서는 IPVS 모드를 고려하세요
7. Container Runtime 이해
김개발 씨가 마지막으로 궁금한 것이 있었습니다. "결국 컨테이너를 실제로 실행하는 건 뭐예요?
Docker인가요?" 박시니어 씨가 웃으며 답했습니다. "좋은 질문이에요.
Container Runtime에 대해 알아볼 시간이네요."
Container Runtime은 실제로 컨테이너를 생성하고 실행하는 소프트웨어입니다. Kubelet이 "이 컨테이너를 실행해"라고 명령하면, Container Runtime이 그 명령을 수행합니다.
Docker, containerd, CRI-O 등이 있으며, 이들은 모두 CRI(Container Runtime Interface)를 통해 Kubelet과 통신합니다.
다음 코드를 살펴봅시다.
# 노드의 Container Runtime 확인
kubectl get nodes -o wide
# containerd 상태 확인 (노드에서)
systemctl status containerd
# containerd로 컨테이너 목록 확인
crictl ps
# containerd로 이미지 목록 확인
crictl images
# Pod 내 컨테이너 상세 정보
crictl inspect <container-id>
# 컨테이너 로그 확인
crictl logs <container-id>
# CRI 소켓 설정 확인
cat /var/lib/kubelet/kubeadm-flags.env
김개발 씨는 Docker에 익숙했습니다. docker run 명령으로 수많은 컨테이너를 실행해봤습니다.
그래서 당연히 Kubernetes도 Docker를 사용할 거라고 생각했습니다. 박시니어 씨가 재미있는 사실을 알려줬습니다.
"사실 최신 Kubernetes에서는 Docker를 직접 사용하지 않아요." Container Runtime이란 정확히 무엇일까요? 쉽게 비유하자면, Container Runtime은 마치 자동차의 엔진과 같습니다.
운전자(Kubelet)가 "앞으로 가"라고 명령하면, 엔진(Container Runtime)이 실제로 바퀴를 굴립니다. 운전자는 엔진의 내부 구조를 몰라도 차를 운전할 수 있습니다.
가솔린 엔진이든 전기 모터든 "앞으로 가"라는 명령만 전달하면 됩니다. Kubernetes도 마찬가지입니다.
Kubelet은 어떤 Container Runtime을 사용하든 상관없이 동일한 방식으로 명령을 내립니다. 이를 가능하게 하는 것이 **CRI(Container Runtime Interface)**입니다.
CRI는 Kubernetes가 정의한 표준 인터페이스입니다. Container Runtime이 이 인터페이스를 구현하면 Kubernetes와 호환됩니다.
덕분에 Docker, containerd, CRI-O 등 다양한 런타임을 사용할 수 있습니다. Docker와 Kubernetes의 관계를 정리해볼까요?
초기 Kubernetes는 Docker를 직접 사용했습니다. 하지만 Docker는 너무 많은 기능을 가지고 있었습니다.
이미지 빌드, 레지스트리 관리, 네트워킹, 볼륨 등 Kubernetes가 이미 제공하는 기능들이 중복되었습니다. Kubernetes 1.24부터 dockershim이 제거되었습니다.
이제 Docker를 직접 지원하지 않습니다. 대신 containerd나 CRI-O를 사용합니다.
containerd는 원래 Docker의 일부였지만, 독립적인 프로젝트로 분리되었습니다. 더 가볍고 Kubernetes에 최적화되어 있습니다.
이게 의미하는 바는 무엇일까요? 개발자 PC에서는 여전히 Docker를 사용하여 이미지를 빌드하고 테스트합니다.
하지만 Kubernetes 클러스터 내부에서 컨테이너를 실행할 때는 containerd가 담당합니다. 이미지 형식은 동일하므로(OCI 표준) 아무 문제없이 호환됩니다.
위의 코드에서 crictl 명령은 CRI 호환 런타임과 통신하는 CLI 도구입니다. docker ps와 비슷하지만 containerd나 CRI-O에서 사용합니다.
운영 환경에서 디버깅할 때 자주 사용합니다. Container Runtime의 실제 동작을 단계별로 살펴볼까요?
첫째, Scheduler가 Pod를 특정 노드에 배정합니다. 둘째, 해당 노드의 Kubelet이 이를 감지합니다.
셋째, Kubelet이 CRI를 통해 Container Runtime에 명령을 보냅니다. "nginx:latest 이미지로 컨테이너를 생성해라." 넷째, Container Runtime은 이미지가 로컬에 없으면 레지스트리에서 다운로드합니다.
다섯째, 이미지를 기반으로 컨테이너를 생성합니다. 이때 리눅스의 cgroup과 namespace를 사용하여 격리된 환경을 만듭니다.
여섯째, 컨테이너가 실행되고, Container Runtime은 상태를 Kubelet에 보고합니다. Kubelet은 이를 다시 API Server에 보고합니다.
실제 현업에서는 containerd가 사실상 표준입니다. AWS EKS, Google GKE, Azure AKS 모두 containerd를 기본으로 사용합니다.
특별한 이유가 없다면 containerd를 선택하면 됩니다. 주의할 점도 있습니다.
Container Runtime이 죽으면 해당 노드의 모든 컨테이너가 영향을 받습니다. systemctl status containerd로 상태를 모니터링하고, 문제가 생기면 빠르게 대응해야 합니다.
김개발 씨가 마지막으로 정리했습니다. "그러니까 Docker로 이미지를 만들고, containerd로 실행하는 거네요.
형식이 같으니까 호환되고요!" 박시니어 씨가 만족스럽게 고개를 끄덕였습니다. Kubernetes 아키텍처의 모든 구성 요소를 살펴봤습니다.
Control Plane의 API Server, etcd, Scheduler, Controller Manager. Worker Node의 Kubelet, Kube-proxy, Container Runtime.
이들이 조화롭게 협력하여 컨테이너 오케스트레이션이라는 복잡한 임무를 수행합니다.
실전 팁
💡 - containerd 문제는 crictl과 systemctl로 디버깅하세요
- 이미지 풀 실패는 crictl pull로 수동 테스트해보세요
- Container Runtime은 노드 레벨에서 모니터링해야 합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Istio 보안 완벽 가이드
마이크로서비스 환경에서 필수적인 Istio 보안 기능을 실무 중심으로 설명합니다. mTLS부터 인증, 인가까지 단계별로 학습하여 안전한 서비스 메시를 구축할 수 있습니다.
Istio 트래픽 관리 완벽 가이드
Istio의 트래픽 관리 기능을 마스터하는 완벽 가이드입니다. VirtualService와 DestinationRule을 활용한 라우팅부터 트래픽 분할, 헤더 기반 라우팅까지 실무에 필요한 모든 내용을 다룹니다.
Istio 설치와 구성 완벽 가이드
Kubernetes 환경에서 Istio 서비스 메시를 설치하고 구성하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어낸 가이드입니다. istioctl 설치부터 사이드카 주입까지 단계별로 학습합니다.
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.