Kubernetes Pod와 Node의 핵심 차이 이해하기
Pod와 Node의 역할을 명확히 정의하여 Kubernetes 아키텍처의 기본을 마스터하세요. 이 가이드는 Node가 리소스를 제공하는 기본 컴퓨팅 머신이고, Pod가 애플리케이션 컨테이너를 호스팅하는 가장 작은 배포 가능 단위임을 설명합니다. Scheduler를 통한 이들 구성 요소의 상호 작용, 리소스 요청에 대한 중요한 고려 사항, 애플리케이션 안정성을 보장하기 위한 실용적인 문제 해결 단계를 알아보세요.
Kubernetes Pod와 Node의 핵심 차이 이해하기
Kubernetes를 처음 접한다면, Pod와 Node의 구분은 시스템의 나머지 부분을 이해하게 만드는 첫 번째 개념 중 하나입니다. Node는 머신입니다. Pod는 Kubernetes가 해당 머신에 배치하는 워크로드입니다. 컨테이너는 Pod 내에서 실행되고, Pod는 Node에서 실행됩니다.
간단해 보이지만, 이는 일상적인 문제 해결의 많은 부분을 설명합니다. Pod가 Pending 상태라면, Kubernetes가 적합한 Node를 찾지 못했을 수 있습니다. Node가 NotReady 상태라면, 해당 Node의 모든 Pod가 위험에 처합니다. 앱에 고가용성이 필요하다면, 더 많은 컨테이너를 실행하는 것만으로는 충분하지 않습니다. 다른 Node에 분산된 복제본이 필요합니다.
Kubernetes 클러스터 아키텍처 개요
Kubernetes 클러스터는 함께 작동하는 머신(물리적 또는 가상) 집합으로 구성됩니다. 이러한 머신은 크게 Control Plane(클러스터 상태를 관리하는 두뇌)과 Worker Node(실제 워크로드를 실행하는 근육)로 분류됩니다. Pod와 Node는 이 구조 내에서 상호 작용합니다.
- Node: CPU, 메모리, 디스크 및 네트워크를 제공하는 물리적 또는 가상 머신입니다.
- Pod: Kubernetes가 스케줄링하는 가장 작은 배포 가능 단위입니다. 하나 이상의 컨테이너를 호스팅합니다.
Node가 Pod를 호스팅하고 Pod가 컨테이너를 호스팅하는 이 계층 구조를 이해하는 것이 Kubernetes 마스터의 시작점입니다.
Kubernetes Node: 컴퓨팅 파워의 기초
Kubernetes Node(때로는 Worker Machine이라고도 함)는 애플리케이션을 실행하는 데 필요한 컴퓨팅 리소스(CPU, RAM, 네트워크)를 제공하는 머신입니다. 클러스터에는 최소한 하나의 Node가 있어야 하지만, 프로덕션 환경에서는 일반적으로 중복성과 확장성을 위해 여러 Node를 사용합니다.
Node의 주요 책임
각 Node는 Control Plane과 통신하고 애플리케이션 워크로드를 호스팅할 수 있도록 하는 필수 구성 요소를 실행합니다.
- Kubelet: 모든 Node에서 실행되는 에이전트로, Control Plane과 통신을 담당합니다. 컨테이너 런타임과 함께 작동하여 PodSpec에 설명된 컨테이너가 해당 Node에서 실행 중이고 정상 상태인지 확인합니다.
- Container Runtime: 이미지를 가져오고 컨테이너를 실행하는 소프트웨어로, 최신 클러스터에서는 일반적으로 containerd 또는 CRI-O를 사용합니다.
- Kube-proxy: Node에서 네트워크 규칙을 유지 관리하여 Pod 간 및 외부와의 통신을 가능하게 합니다.
실용적인 예: Node 표현
클러스터의 Node를 검사할 때, Kubernetes가 사용 중인 기본 인프라를 보고 있는 것입니다.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker-node-01 Ready <none> 2d1h v1.27.4
worker-node-02 Ready <none> 2d1h v1.27.4
핵심 요점: Node는 실행이 발생하는 머신 계층입니다.
Kubernetes Pod: 가장 작은 배포 가능 단위
Pod는 Kubernetes에서 배포의 원자 단위입니다. 컨테이너 자체가 아니라, 동일한 Node에 함께 배치되고 리소스를 공유하는 하나 이상의 컨테이너를 감싸는 래퍼입니다.
직접 컨테이너 대신 Pod를 사용하는 이유는?
Kubernetes는 여러 중요한 이유로 개별 컨테이너가 아닌 Pod를 관리합니다.
- 공유 컨텍스트: 단일 Pod 내의 모든 컨테이너는 동일한 네트워크 네임스페이스(IP 주소 및 포트 범위)를 공유하며
localhost를 통해 쉽게 통신할 수 있습니다. - 공유 스토리지: 동일한 Pod의 컨테이너는 동일한 마운트된 스토리지 볼륨에 액세스할 수 있습니다.
- 수명 주기 관리: Kubernetes는 Pod를 단일 엔터티로 취급합니다. Pod 내의 컨테이너가 실패하면 Kubernetes는 전체 Pod 구조의 재시작 또는 재생성을 처리합니다.
Pod의 구조
대부분의 경우 Pod는 단일 기본 애플리케이션 컨테이너를 포함합니다. 그러나 Sidecar 패턴에 자주 사용되며, 여기서 보조 컨테이너가 기본 컨테이너를 지원합니다(예: 로깅 에이전트, 서비스 메시 프록시).
Pod 정의 예시 (간소화된 YAML)
다음 YAML은 단일 Nginx 컨테이너를 감싸는 Pod를 정의합니다.
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80
핵심 요점: Pod는 애플리케이션 컨테이너의 논리적 호스트이며 Node에 스케줄링되는 단위입니다.
핵심 관계: 스케줄링 및 배치
Pod와 Node 간의 기본적인 상호 작용은 Control Plane에 있는 Kubernetes Scheduler에 의해 관리됩니다.
Pod가 Node에 배치되는 방법
- Pod 생성: 사용자가 Pod(또는 Pod를 생성하는 Deployment와 같은 상위 수준 객체)에 대한 YAML 정의를 API Server에 제출합니다.
- 스케줄링 결정: Scheduler는 리소스 요청, 제약 조건 및 사용 가능한 용량을 기반으로 해당 Pod를 실행할 최상의 Node를 식별합니다.
- 바인딩: Node가 선택되면 Pod는 해당 특정 Node에 바인딩됩니다.
- 실행: 할당된 Node의 Kubelet이 새 Pod 할당을 감지하고, 필요한 이미지를 가져와 컨테이너를 시작합니다.
중요한 점: Pod가 Node에 스케줄링되면 종료되거나, 영구적으로 충돌하거나, Node가 실패할 때까지 해당 Node에 유지됩니다. Kubernetes는 일반적으로 실행 중인 Pod를 Node 간에 마이그레이션하지 않습니다.
| 특징 | Kubernetes Node | Kubernetes Pod |
|---|---|---|
| 역할 | 물리적/가상 컴퓨팅 리소스를 제공합니다. | 하나 이상의 애플리케이션 컨테이너를 실행합니다. |
| 범위 | 클러스터 인프라 수준. | 애플리케이션 워크로드 수준. |
| 스케줄링 단위 | Scheduler로부터 Pod를 받습니다. | Node에 스케줄링되는 단위입니다. |
| 구성 요소 | Kubelet, Container Runtime, Kube-proxy. | 애플리케이션 컨테이너, 공유 볼륨, 공유 IP. |
| 수량 | 일반적으로 클러스터당 몇 개에서 많음. | 워크로드에 따라 수백 또는 수천 개가 될 수 있음. |
실제 배포 예시
다음 Deployment를 사용하는 웹 애플리케이션을 상상해 보세요.
apiVersion: apps/v1
kind: Deployment
metadata:
name: storefront
spec:
replicas: 3
selector:
matchLabels:
app: storefront
template:
metadata:
labels:
app: storefront
spec:
containers:
- name: web
image: example/storefront:v1
ports:
- containerPort: 8080
이것은 세 개의 Node를 생성하지 않습니다. 세 개의 Pod 복제본을 생성합니다. 그런 다음 스케줄러는 어떤 기존 Node가 해당 Pod를 실행할지 결정합니다. 소규모 클러스터에서는 다음과 같이 표시될 수 있습니다.
kubectl get pods -o wide
NAME READY STATUS NODE
storefront-6d8f8c7f9b-2xk9p 1/1 Running worker-node-01
storefront-6d8f8c7f9b-7hxm4 1/1 Running worker-node-02
storefront-6d8f8c7f9b-q4zpt 1/1 Running worker-node-02
세 개의 Pod가 있지만 두 개의 Node만 관련되어 있습니다. worker-node-02가 실패하면 두 개의 복제본이 동시에 사라집니다. Deployment는 이를 대체하려고 시도하지만, 이를 위해서는 정상 Node 용량이 필요합니다. 이것이 Deployment에 여러 복제본이 있더라도 Node 배치가 중요한 이유입니다.
Kubernetes가 복제본을 Node 전체에 분산하도록 하려면 토폴로지 분산 제약 조건 또는 Pod 반-어피니티를 사용하세요. 모든 소규모 내부 도구에 필요하지는 않지만, 하나의 Node 장애로 인해 대부분의 용량이 제거되어서는 안 되는 고객 대면 서비스의 경우 고려해야 합니다.
Pod는 폐기 가능하고, Node는 관리되는 용량입니다.
건강한 Kubernetes 사고 방식은 Pod가 교체 가능하다는 것입니다. Deployment, ReplicaSet, StatefulSet, Job 및 DaemonSet은 항상 Pod를 생성하고 교체합니다. 임의 접미사가 있는 Pod 이름은 영원히 의존해서는 안 됩니다.
Node도 잘 운영되는 클러스터에서는 교체 가능하지만, 용량 단위입니다. 운영 체제 패치, kubelet 상태, 충분한 디스크 공간, 작동하는 컨테이너 런타임 및 네트워크 연결이 필요합니다. Node에 문제가 발생하면 많은 Pod가 동시에 영향을 받을 수 있습니다.
이 차이는 디버깅 방식에서 나타납니다.
- 하나의 Pod가 실패하고 동일한 Node의 다른 Pod가 정상이면, Pod의 로그, 구성, 프로브, 이미지 및 리소스 제한부터 시작하세요.
- 동일한 Node에서 관련 없는 여러 Pod가 실패하면, Node부터 시작하세요: 디스크 압박, 메모리 압박, kubelet 상태, CNI 상태 및 컨테이너 런타임 로그.
- 새 Pod가 어디에서도 시작할 수 없으면, 클러스터 전체 용량, 할당량, 스케줄링 규칙 및 Control Plane 상태를 확인하세요.
일반적인 오해
첫 번째 오해는 Pod가 컨테이너와 동일하다고 생각하는 것입니다. 대부분의 Pod는 하나의 기본 컨테이너를 포함하므로, 이렇게 간단히 생각해도 무해해 보입니다. 그러나 사이드카는 이러한 구분을 중요하게 만듭니다. 서비스 메시 프록시, 로그 전달자 또는 도우미 컨테이너는 애플리케이션과 동일한 Pod에서 실행될 수 있습니다. Pod 네트워크 네임스페이스를 공유하므로, 해당 Pod 내에서 이러한 컨테이너 간의 localhost 통신이 작동합니다.
두 번째 오해는 Kubernetes가 실행 중인 Pod를 라이브 VM 마이그레이션처럼 다른 Node로 이동한다고 생각하는 것입니다. 일반적으로 그렇지 않습니다. Node가 드레인되거나 실패하면 Kubernetes는 이전 Pod를 종료하거나 손실하고 다른 곳에 대체 Pod를 생성합니다. 해당 대체 Pod는 다른 Pod ID를 가지며, 이전 Pod의 로컬 컨테이너 파일 시스템 변경 사항은 영구 스토리지에 기록되지 않는 한 사라집니다.
세 번째 오해는 Service가 Node에서 실행된다고 가정하는 것입니다. Service는 Pod 앞에 있는 안정적인 네트워킹 추상화입니다. 레이블로 Pod를 선택하고 해당 IP로 트래픽을 라우팅합니다. Node는 해당 Pod가 실행되는 장소를 제공하지만, Service 자체는 Pod가 Node에 있는 방식과 동일하게 특정 Worker Node "위에" 있지 않습니다.
이것이 일상적인 설계에 미치는 영향
복제본 수를 선택할 때, Kubernetes가 계속 실행하려고 시도해야 하는 Pod 복사본 수를 선택하는 것입니다. 존재할 Node 수를 선택하는 것이 아닙니다. 10개의 복제본이 있는 Deployment는 분산 규칙을 추가하고 클러스터에 충분한 Node 용량이 없는 한, 여전히 여러 Pod를 동일한 Node에 배치할 수 있습니다.
Node 크기를 선택할 때, 용량 풀의 형태를 선택하는 것입니다. 몇 개의 큰 Node는 효율적일 수 있지만, 하나의 Node 장애로 실행 중인 Pod의 더 큰 부분이 제거됩니다. 더 작은 Node를 많이 사용하면 장애 격리를 개선할 수 있지만, 오버헤드가 추가되고 빈 패킹 효율성이 떨어질 수 있습니다. 보편적인 답은 없습니다. 올바른 선택은 워크로드 크기, 가용성 요구 사항 및 비용에 따라 달라집니다.
상태 저장 워크로드는 또 다른 복잡성을 추가합니다. StatefulSet은 여전히 Pod를 생성하고, 해당 Pod는 여전히 Node에서 실행되지만, 각 Pod는 안정적인 ID와 영구 볼륨을 가질 수 있습니다. Node가 죽으면 Kubernetes는 다른 곳에서 Pod를 다시 생성할 수 있지만, 스토리지 계층은 새 위치에서 볼륨을 연결하거나 액세스할 수 있도록 지원해야 합니다.
모범 사례 및 문제 해결 인사이트
이 아키텍처를 이해하면 실용적인 클러스터 관리에 도움이 됩니다.
리소스 관리
- 리소스 요청/제한: Pod 사양에 항상 리소스
requests및limits를 정의하세요. 이를 통해 Scheduler가 Pod를 충분한 용량이 있는 Node에 정확하게 매칭하여 리소스 경합을 방지할 수 있습니다. - Node 압박: Node가 과부하(디스크 공간 또는 메모리 부족)되면 Kubelet이 이 상태를 보고합니다. 그러면 Kubernetes는 안정성을 유지하기 위해 해당 Node에서 Pod를 축출할 수 있습니다.
고가용성 (HA)
- 중복성: HA를 달성하려면 Deployment 또는 StatefulSet으로 관리되는 여러 복사본(복제본)의 Pod를 실행해야 합니다. Scheduler는 이러한 복제본을 다른 Node에 배치하여 하나의 Node 장애가 전체 애플리케이션을 중단시키지 않도록 합니다.
문제 해결
애플리케이션이 시작되지 않는 경우:
- Pod 상태 확인:
kubectl describe pod <pod-name>을 사용하세요. 'Events' 섹션을 보고 Pod가 스케줄링된 Node를 확인하세요. - Node 상태 확인: Pod가
Pending상태에서 멈춰 있다면, 일반적으로 스케줄링 관련 문제입니다(예: 필요한 제약 조건을 충족하는 Node가 없음). Pod가 실행 중이지만 실패한다면, 해당 Pod가 있는 특정 Node의 Kubelet 로그를 확인하세요.
유용한 명령어:
kubectl get pods -o wide
kubectl describe pod <pod-name>
kubectl get nodes
kubectl describe node <node-name>
-o wide 출력은 과소평가됩니다. 각 Pod가 어떤 Node에 있는지 보여주며, 이는 종종 애플리케이션 문제와 인프라 문제를 구분하는 단서가 됩니다.
요약
Node는 "이것이 어디서 실행될 수 있습니까?"에 답합니다. Pod는 "무엇이 함께 실행되어야 합니까?"에 답합니다. 스케줄러는 Pod를 Node에 배치하여 이 두 가지 아이디어를 연결합니다. 이 관계를 이해하면 Kubernetes 오류를 더 쉽게 읽을 수 있습니다. 보류 중인 Pod는 종종 배치 문제이고, 실패하는 Pod는 종종 워크로드 문제이며, 비정상 Node는 동시에 많은 Pod 문제를 일으킬 수 있습니다.