K8s 시리즈 01: Kubernetes란? 컨테이너부터 클러스터까지

왜 LLM 엔지니어가 Kubernetes를 알아야 하는가

LLM 엔지니어에게 Kubernetes는 “인프라팀이 알아서 해주는 것” 이상의 의미를 가진다. 모델을 학습하고 나면 결국 서빙, 스케일링, 비용 최적화 문제가 남고, 이 모든 것이 Kubernetes 위에서 돌아가기 때문이다.

  • “vLLM 서빙 서버를 3대에서 10대로 늘리고 싶다” → Deployment replica, HPA
  • “A100 노드에만 학습 Pod을 배치하고 싶다” → Node Selector, Taint/Toleration
  • “모델 체크포인트를 여러 Pod에서 공유해야 한다” → EFS PVC (RWX)
  • “추론 서버가 죽으면 자동으로 살아나야 한다” → Pod 자동 복구, Health Check
  • “GPU 노드 비용이 너무 많이 나온다” → Spot Instance, Auto Mode, 스케일링 전략

모델 개발만이 아니다. 데이터와 평가 파이프라인에서도 Kubernetes가 핵심이다.

  • “Annotation 툴, 데이터 공유 플랫폼 등 여러 내부 서비스를 운영해야 한다” → Deployment + Service + Ingress
  • “매일 새로운 데이터를 전처리하고 정제해야 한다” → CronJob (주기적 배치)
  • “벤치마크를 여러 모델에 대해 병렬로 돌리고 싶다” → Job (일회성 실행, 수평 확장)

결국 LLM 엔지니어가 하는 일의 상당 부분 — 학습, 서빙, 데이터 처리, 평가 — 이 Kubernetes 위에서 돌아간다. 인프라를 “모르겠고 알아서 해줘”로 넘기면 병목이 어디인지, 비용이 왜 나오는지, 왜 Pod이 안 뜨는지 파악할 수 없다.

이 시리즈에서는 Kubernetes의 기초부터 AWS EKS 실무까지를 다룬다. 첫 번째 글에서는 “Kubernetes가 뭔데?”에 답한다.

이 글은 K8s 시리즈의 첫 번째 글이다.


1. 전통적인 배포 vs 컨테이너 배포

1.1 가상 머신(VM)의 문제

전통적으로 애플리케이션은 가상 머신(VM) 단위로 배포한다. EC2 인스턴스 하나에 OS를 설치하고, 라이브러리를 깔고, 앱을 올린다.

문제는:

  • 무겁다: Guest OS 전체를 포함하므로 이미지 크기가 수 GB~수십 GB
  • 느리다: VM 부팅에 수 분이 걸린다
  • 낭비가 크다: CPU 10%만 쓰는 앱에도 VM 1대를 할당
  • 환경 차이: “내 로컬에서는 되는데 서버에서는 안 돼요”

1.2 컨테이너가 해결하는 것

컨테이너는 앱 + 실행에 필요한 라이브러리만 패키징한다. Guest OS가 없고, Host OS의 커널을 공유한다.

VM과 컨테이너 구조 비교
항목 VM 컨테이너
크기 수 GB (OS 포함) 수십~수백 MB (앱만)
시작 시간 수 분 수 초
격리 하드웨어 수준 (강함) 프로세스 수준 (가벼움)
밀도 서버당 수 개 서버당 수십~수백 개
이식성 환경 의존적 어디서든 동일하게 실행

핵심은 이식성이다. Dockerfile로 한 번 정의하면, 로컬이든 CI 서버든 프로덕션이든 완전히 동일한 환경에서 실행된다. “내 컴퓨터에서는 되는데”가 사라진다.


2. Kubernetes가 해결하는 문제

컨테이너 하나를 실행하는 것은 docker run으로 충분하다. 하지만 프로덕션에서는:

  • 컨테이너가 수십~수백 개 동시에 돌아간다
  • 컨테이너가 죽으면 자동으로 살려야 한다
  • 트래픽이 늘면 자동으로 확장해야 한다
  • 새 버전을 중단 없이 배포해야 한다
  • 여러 서버에 컨테이너를 적절히 분배해야 한다

이 모든 것을 수동으로 하면 운영 부담이 막대하다. Kubernetes(K8s)는 이를 자동화하는 컨테이너 오케스트레이션 플랫폼이다.

기능 K8s가 해주는 것 비유
자동 복구 컨테이너가 죽으면 자동 재시작 쓰러진 병사를 즉시 교체
수평 확장 트래픽에 따라 컨테이너 수를 늘리거나 줄임 바쁜 시간에 직원 추가
무중단 배포 새 버전을 하나씩 교체하며 배포 영업 중 인테리어 공사
선언적 관리 “이 상태가 되어야 한다”고 정의하면 K8s가 알아서 맞춤 온도 설정하면 에어컨이 알아서
스케줄링 여러 서버 중 적절한 곳에 자동 배치 자리 배정 담당자

선언적(Declarative) 관리란?

Kubernetes의 핵심 철학이다. “이 명령을 실행해라”(명령형)가 아니라 “이 상태가 유지되어야 한다”(선언형)로 정의한다.

# "백엔드 Pod 3개가 항상 실행 중이어야 한다"
spec:
  replicas: 3

이렇게 정의하면 Pod이 하나 죽어도 K8s가 자동으로 새 Pod을 띄워 3개를 맞춘다. 수동으로 “죽은 Pod을 재시작해”라고 명령할 필요가 없다.


3. 클러스터 구조

Kubernetes는 클러스터 단위로 동작한다. 클러스터는 Control Plane(두뇌)Worker Node(일꾼)로 구성된다.

Kubernetes 클러스터 아키텍처: Control Plane과 Worker Node

3.1 Control Plane — 클러스터의 두뇌

클러스터 전체를 관리하는 컴포넌트다. 사용자가 직접 접근할 일은 거의 없고, kubectl이나 Rancher를 통해 API Server에 요청을 보내는 방식으로 사용한다.

구성 요소 역할 비유
API Server 모든 요청의 진입점. kubectl, Rancher, CI/CD 파이프라인이 여기에 요청 접수 창구
etcd 클러스터의 모든 상태를 저장하는 분산 Key-Value DB 기록 보관소
Scheduler 새 Pod을 어느 노드에 배치할지 결정 (CPU, 메모리, GPU 고려) 자리 배정 담당
Controller Manager Deployment, ReplicaSet 등의 “원하는 상태 = 실제 상태” 루프를 실행 자동 관리자

3.2 Worker Node — 실제 실행되는 곳

컨테이너(Pod)가 실제로 실행되는 서버다. EC2 인스턴스라고 생각하면 된다. 각 Worker Node에는 3개의 컴포넌트가 돌아간다.

kubelet — 노드의 관리자

  • Control Plane(Scheduler)이 “이 노드에서 Pod A를 실행해”라고 지시하면, kubelet이 받아서 실제로 컨테이너를 띄운다
  • Pod이 실행 중인지, 죽었는지 주기적으로 확인해서 Control Plane에 보고한다
  • Health Check(Probe)도 kubelet이 실행한다

Container Runtime — 컨테이너를 실제로 실행하는 엔진

  • kubelet이 “이 이미지로 컨테이너를 만들어”라고 요청하면, Container Runtime(containerd)이 실제로 컨테이너 프로세스를 생성한다
  • docker run을 대신 해주는 것이라고 생각하면 된다
  • 과거에는 Docker를 사용했지만, 현재 K8s는 containerd나 CRI-O를 사용한다

kube-proxy — 네트워크 연결 담당

  • Pod A가 Pod B에 요청을 보낼 때, kube-proxy가 네트워크 규칙을 관리해서 트래픽이 올바른 Pod에 도착하도록 한다
  • Service를 만들면 kube-proxy가 해당 Service로 오는 트래픽을 여러 Pod에 분배(로드밸런싱)하는 규칙을 설정한다

전체 동작 흐름: “vLLM 서버 3대 띄워줘”

  1. 사용자가 kubectl 또는 Rancher로 “Deployment를 만들어줘” 요청
  2. API Server가 요청을 받고 etcd에 저장
  3. Controller Manager가 “Deployment에 Pod 3개가 필요하네” 인식
  4. Scheduler가 GPU 메모리, CPU 여유 등을 고려해 “Node 1에 2개, Node 2에 1개 배치” 결정
  5. 각 노드의 kubelet이 Scheduler의 지시를 받아 Container Runtime에게 컨테이너 실행을 요청
  6. Container Runtime이 ECR에서 이미지를 Pull하고 컨테이너를 실행
  7. kube-proxy가 Service의 트래픽을 3개 Pod에 분배하는 네트워크 규칙 설정
  8. Pod이 죽으면 kubelet이 감지 → Controller Manager가 새 Pod 자동 생성

4. 컨테이너 이미지

Kubernetes에서 실행하는 모든 애플리케이션은 컨테이너 이미지로 패키징되어야 한다.

4.1 주요 개념

개념 설명 비유
Image 컨테이너를 만들기 위한 읽기 전용 템플릿 붕어빵 틀
Container Image로부터 만들어진 실행 중인 인스턴스 구워진 붕어빵
Dockerfile Image를 빌드하기 위한 명령어 모음 레시피
Registry Image를 저장하고 배포하는 저장소 붕어빵 틀 창고
Tag Image의 버전 (예: v1.0, latest) 버전 번호

4.2 이미지 빌드 흐름

컨테이너 이미지 빌드 및 배포 흐름

4.3 Dockerfile 예시

# 1. 베이스 이미지
FROM python:3.11-slim

# 2. 작업 디렉토리
WORKDIR /app

# 3. 의존성 먼저 복사 (캐시 활용)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 4. 소스 코드 복사
COPY . .

# 5. 포트 노출
EXPOSE 8080

# 6. 실행 명령어
CMD ["python", "main.py"]

왜 의존성을 먼저 복사하는가?

Docker는 각 단계를 레이어로 캐싱한다. requirements.txt가 바뀌지 않으면 pip install 단계가 캐시에서 재사용되어 빌드가 훨씬 빨라진다. 소스 코드만 바꿨을 때 전체 의존성을 다시 설치하지 않아도 되는 것이다.

4.4 이미지 빌드·태그·푸시

# 이미지 빌드
docker build -t my-app:v1.0 .

# Registry에 맞게 태그
docker tag my-app:v1.0 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v1.0

# Registry에 푸시
docker push 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v1.0

이렇게 Registry에 올라간 이미지를 Kubernetes가 Pull해서 Pod으로 실행한다.


5. YAML — Kubernetes의 언어

Kubernetes의 모든 리소스는 YAML 파일로 정의한다. K8s를 쓴다는 것은 YAML을 읽고 쓴다는 것이다.

5.1 기본 구조

모든 K8s YAML은 4개의 필수 필드를 가진다.

apiVersion: apps/v1 # API 버전 — 어떤 API 그룹을 사용하는가
kind: Deployment # 리소스 종류 — 무엇을 만드는가
metadata: # 메타데이터 — 이름, 라벨 등
  name: my-app
  namespace: default
  labels:
    app: my-app
spec: # 스펙 — 원하는 상태를 정의
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    spec:
      containers:
        - name: my-app
          image: my-app:v1.0
필드 역할 예시
apiVersion 사용할 API 버전 v1, apps/v1, batch/v1
kind 리소스 종류 Pod, Deployment, Service, Job
metadata 이름, 네임스페이스, 라벨 name: my-app
spec 원하는 상태 정의 (핵심) replicas: 3, containers: [...]

5.2 자주 쓰는 apiVersion

apiVersion 포함 리소스
v1 Pod, Service, ConfigMap, Secret, PersistentVolumeClaim
apps/v1 Deployment, StatefulSet, DaemonSet, ReplicaSet
batch/v1 Job, CronJob
networking.k8s.io/v1 Ingress, NetworkPolicy

처음에는 외울 필요 없다. kubectl explain <리소스>로 항상 확인할 수 있다.

5.3 kubectl 기본 명령어

YAML을 작성했으면 kubectl로 클러스터에 적용한다.

# YAML 적용 (생성 또는 업데이트)
kubectl apply -f deployment.yaml

# 리소스 목록 조회
kubectl get pods                    # Pod 목록
kubectl get deploy                  # Deployment 목록
kubectl get all                     # 모든 리소스

# 상세 정보 (문제 해결 시 필수)
kubectl describe pod <pod-name>

# 로그 확인
kubectl logs <pod-name>             # 로그 출력
kubectl logs <pod-name> -f          # 실시간 스트리밍

# Pod 쉘 접속
kubectl exec -it <pod-name> -- bash

# 리소스 삭제
kubectl delete -f deployment.yaml

Rancher를 쓰면 이 명령어를 몰라도 된다. 웹 UI에서 Deployment 생성, Pod 로그 확인, 스케일링 등 대부분의 작업을 클릭으로 할 수 있다. 하지만 자동화 스크립트나 CI/CD 파이프라인에서는 kubectl이 필수이므로, 기본 명령어 정도는 알아두는 것이 좋다.


6. 용어 정리

용어 설명
Cluster Control Plane + Worker Node로 구성된 K8s 실행 환경 전체
Node Pod이 실행되는 서버. Worker Node = EC2 인스턴스
Pod K8s에서 배포 가능한 최소 단위. 1개 이상의 컨테이너 포함
Container 앱 + 실행 환경을 하나로 패키징한 격리된 실행 단위
Image 컨테이너를 만들기 위한 읽기 전용 템플릿
Registry 이미지를 저장하는 저장소 (ECR, Docker Hub)
Deployment Pod의 개수, 업데이트, 롤백을 관리하는 컨트롤러
Service Pod 집합에 대한 고정 네트워크 엔드포인트
Namespace 클러스터 내 리소스를 논리적으로 분리하는 범위
kubectl K8s 클러스터를 CLI로 제어하는 도구
Rancher K8s 클러스터를 웹 GUI로 관리하는 플랫폼
YAML K8s 리소스를 정의하는 파일 형식
EKS AWS 관리형 K8s 서비스 (Control Plane을 AWS가 관리)
ECR AWS 관리형 컨테이너 이미지 저장소

다음 글

이번 글에서는 Kubernetes가 무엇이고, 왜 필요하며, 어떤 구조인지를 정리했다. 다음 글에서는 실제로 앱을 실행하는 핵심 리소스인 Pod, Deployment, Job, CronJob을 다룬다.

다음 글: K8s 시리즈 02: Pod, Deployment, Job, CronJob — K8s 워크로드 총정리


참고 문헌




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • K8s 시리즈 06: EKS 네트워킹·보안·비용·운영
  • K8s 시리즈 05: Amazon EKS — 아키텍처와 Worker Node
  • K8s 시리즈 04: ConfigMap, Secret, Storage — 설정과 데이터 관리
  • K8s 시리즈 03: Service, Ingress — 트래픽 라우팅과 외부 접근
  • K8s 시리즈 02: Pod, Deployment, Job, CronJob — K8s 워크로드 총정리