Kubernetes는 고대 그리스어로 배의 조타수(Helmsman) 를 의미한다. 컨테이너라는 화물을 실은 배를 조종하는 역할에서 따온 이름이다.
| 정의 | 설명 |
|---|---|
| 플랫폼을 만들기 위한 플랫폼 | 원시적인 기능들만 제공하고, 사용자가 필요한 구성을 결정해 자신만의 배포 플랫폼을 제작한다 |
| 오픈소스 플랫폼 | 컨테이너화된 애플리케이션의 자동 배포, 확장 및 관리를 해주는 오픈소스 플랫폼이다 |
코드 한 줄을 바꾸거나 명령어 하나를 입력하면 몇 초 만에 똑같은 파드가 수십 개로 늘어난다. CPU 사용량이 올라가면 알아서 자동으로 늘려준다.
HPA (Horizontal Pod Autoscaler), Auto-scaling
쿠버네티스는 의심이 많고 부지런한 감시자다. 파드가 응답이 없거나 죽으면 사람이 개입하기 전에 기존 파드를 버리고 새 파드를 만들어 교체해 둔다. 엔지니어는 아침에 출근해서 복구 이력 로그만 확인하면 된다.
Self-Healing
무중단 배포 기능 덕분에 대낮에 서비스가 정상적으로 돌아가는 중에도 패치를 진행할 수 있다.
Rolling Update
기능별로 컨테이너와 파드를 쪼개 놓았기 때문에, 결제 파드가 터지더라도 로그인이나 장바구니 파드는 아무런 영향을 받지 않게 구성할 수 있다.
MSA 구조
애플리케이션과 환경을 통째로 박스(컨테이너 이미지)에 담아 코드로 정의해 두었기 때문에, 밑바닥이 AWS든 네이버 클라우드든 상관없이 K8s 클러스터만 준비되어 있다면 그 코드를 그대로 가져다 실행할 수 있다.
| 문제 | 쿠버네티스의 해결 | 기능 이름 |
|---|---|---|
| 서버 확장 관리 | 명령 한 번으로 파드 증설, CPU 사용량에 따라 자동 확장 | HPA / Auto-scaling |
| 장애 서버 재시작 | 죽은 파드를 감지해 자동으로 교체 | Self-Healing |
| 배포 시 서비스 중단 | 서비스 운영 중에도 무중단으로 패치 | Rolling Update |
| 장애의 전체 확산 | 기능별로 파드를 분리해 영향 범위를 격리 | MSA 구조 |
| 환경 이전의 어려움 | 애플리케이션과 환경을 이미지로 코드화해 어디서든 실행 | 이식성(Portability) |
AS-IS: 쿠버네티스 도입 이전
컨테이너(또는 서버)를 운영자가 직접 관리하는 방식이었다. 트래픽이 몰려도 수동으로 서버를 늘려야 했고, 프로세스가 죽으면 사람이 알아채고 재시작해야 했으며, 배포도 수동 스크립트나 순차 작업에 의존했다.
이 방식의 한계:
TO-BE: 쿠버네티스 도입 이후
쿠버네티스는 이런 반복적인 운영 작업을 선언적(Declarative)으로 자동화한다. "몇 개의 파드를 어떤 상태로 유지할지"만 정의하면, 나머지는 쿠버네티스 컨트롤 플레인이 지속적으로 감시하고 스스로 맞춰준다.
핵심 차이점 요약
| 구분 | AS-IS (쿠버네티스 이전) | TO-BE (쿠버네티스 도입 후) |
|---|---|---|
| 배포 | 수동/순차 배포, 중단 위험 존재 | Deployment로 선언적 관리, 무중단 롤링 업데이트 |
| 확장 | 트래픽 대응이 수동, 반응 속도 느림 | Auto Scaling으로 지표 기반 자동 확장/축소 |
| 장애 대응 | 사람이 감지 후 수동 복구 | Auto Healing으로 자동 감지 및 자가 복구 |
| 운영 부담 | 반복 작업에 인력 소모 | 선언적 설정으로 운영 자동화, 사람은 정책 설계에 집중 |
[ VM 방식 ] [ Container 방식 ]
┌──────┬──────┬──────┐ ┌──────┬──────┬──────┐
│ App │ App │ App │ │ App │ App │ App │
│ Bin │ Bin │ Bin │ │ Bin │ Bin │ Bin │
│ /Lib │ /Lib │ /Lib │ │ /Lib │ /Lib │ /Lib │
├──────┼──────┼──────┤ └──────┴──────┴──────┘
│Guest │Guest │Guest │ ← 컨테이너 이미지
│ OS │ OS │ OS │ ┌─────────────────────┐
├──────┴──────┴──────┤ │ Container Engine │
│ Hypervisor │ ├─────────────────────┤
├────────────────────┤ │ Host OS │
│ Host OS │ │ namespace + cgroups│
├────────────────────┤ ├─────────────────────┤
│ Host Server │ │ Host Server │
└────────────────────┘ └─────────────────────┘
커널 N개 커널 1개
계층 구성
| 계층 | 역할 |
|---|---|
| Host Server | 물리 서버. CPU, 메모리, 디스크 등 실제 하드웨어 |
| Host OS | 물리 서버에서 동작하는 운영체제 |
| Hypervisor | 가상화 담당. 물리 자원을 논리적으로 분할해 각 VM에 할당 |
| Guest OS | VM마다 독립적으로 부팅되는 완전한 운영체제 |
특징
비용
계층 구성
| 계층 | 역할 |
|---|---|
| Host Server | 물리 서버 |
| Host OS | 운영체제. 커널을 모든 컨테이너가 공유 |
| Container Engine | 컨테이너 이미지를 실행 (예: Docker) |
| Container Image | 애플리케이션(서비스) + 라이브러리 + 실행 바이너리를 패키징 |
특징
격리 방식
컨테이너는 리눅스 커널의 두 기능으로 격리와 자원 제어를 수행한다.
| 기능 | 담당 |
|---|---|
| namespace | 커널 자원의 격리 |
| cgroups | 자원 사용량의 제어 |
정의
커널이 관리하는 시스템 자원을 프로세스 그룹 단위로 분리하는 커널 기능이다. 특정 namespace에 속한 프로세스는 해당 namespace에 할당된 자원만 인식하고 접근할 수 있다.
종류
| namespace | 격리 대상 |
|---|---|
| mnt | 파일시스템 마운트 포인트 |
| pid | 프로세스 ID 공간 |
| net | 네트워크 인터페이스, IP 주소, 포트, 라우팅 테이블 |
| ipc | 프로세스 간 통신 자원 (공유 메모리, 세마포어, 메시지 큐) |
| uts | 호스트명, 도메인명 |
| user | 사용자 ID(UID)와 그룹 ID(GID) 매핑 |
namespace의 역할 — 자원 격리
컨테이너는 Host OS의 커널을 공유한다. 별도 조치가 없다면 모든 컨테이너가 동일한 파일시스템, 동일한 포트, 동일한 프로세스 공간을 사용하게 되어 서로 충돌한다.
namespace는 커널이 관리하는 자원을 프로세스 그룹별로 분리해, 각 컨테이너가 자신에게 할당된 자원만 인식하도록 만든다. 그 결과 하나의 커널 위에서 여러 컨테이너가 독립된 환경처럼 동작할 수 있다.
정의
프로세스 그룹이 사용할 수 있는 시스템 자원의 양을 할당하고 제한하는 커널 기능이다. namespace가 자원의 접근 범위를 결정한다면, cgroups는 자원의 사용량 상한을 결정한다.
제어 자원
| 자원 | 제어 내용 |
|---|---|
| CPU | CPU 사용 시간의 비율 및 상한 |
| Memory | 메모리 사용량 상한 (초과 시 프로세스 종료) |
| I/O | 블록 디바이스 읽기/쓰기 대역폭 |
| Network | 네트워크 트래픽 분류 및 우선순위 |
적용 효과
| 구분 | VM | Container |
|---|---|---|
| 가상화 대상 | 하드웨어 (Guest OS 전체를 실행) | 프로세스 (namespace + cgroups로 격리) |
| 커널 | Guest OS마다 독립된 커널 | Host OS의 커널을 공유 |
| 기동 시간 | 수십 초 ~ 수 분 | 수백 ms ~ 수 초 |
| 이미지 크기 | 수 GB | 수십 MB ~ 수백 MB |
| 자원 오버헤드 | 큼 (OS가 중복 실행됨) | 작음 (프로세스 수준) |
| 서버당 밀도 | 수십 대 | 수백 개 |
| 격리 수준 | 커널까지 완전 분리 | 커널 공유 (상대적으로 낮음) |
| OS 제약 | Host와 다른 OS 실행 가능 | Host 커널과 동일 계열만 실행 가능 |
개념
Pod 내 컨테이너의 제약
Pod 단위로 묶는 이유
애플리케이션과 그 애플리케이션에 종속된 보조 프로세스를 하나의 논리적 단위로 다루기 위해서다.
| Pod | Container | |
|---|---|---|
| 역할 | 배포·스케줄링의 단위, 네트워크와 스토리지의 경계 | 애플리케이션 프로세스의 실행 단위 |
| 관리 주체 | 쿠버네티스 스케줄러 | 컨테이너 런타임 |
| IP 주소 | Pod마다 하나씩 할당 | Pod의 IP를 공유 |
| 배치 | 하나의 노드에 배치됨 | Pod 내부에 1개 이상 존재 |
| 생명주기 | 생성과 삭제의 단위 | Pod 내부에서 개별 재시작 가능 |
┌─────────── Pod (IP: 10.244.1.7) ───────────┐
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Container A │ │ Container B │ │
│ │ │ │ │ │
│ │ 개별 소유: │ │ 개별 소유: │ │
│ │ 파일시스템 │ │ 파일시스템 │ │
│ │ 프로세스 │ │ 프로세스 │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ─────┴─────────────────────┴───── │
│ 공유: net namespace (동일 IP) │
│ ipc namespace │
│ Volume │
└─────────────────────────────────────────────┘
공유되는 자원
| 자원 | 내용 | 제약 |
|---|---|---|
| net namespace | 동일한 IP 주소를 가지며 localhost로 상호 호출 가능 | 같은 포트 번호를 동시에 사용할 수 없음 |
| ipc namespace | 공유 메모리를 통한 통신 가능 | — |
| Volume | Pod 수준에서 정의하고 각 컨테이너가 마운트 | 마운트를 선언해야 접근 가능 |
공유되지 않는 자원
| 자원 | 내용 |
|---|---|
| mnt namespace | 컨테이너별로 자신의 이미지에 포함된 파일시스템을 사용 |
| pid namespace | 컨테이너별로 프로세스 공간이 분리됨 |
컨테이너 1개로 구성된 Pod
가장 일반적인 형태로, 하나의 애플리케이션을 하나의 Pod로 배포한다.
apiVersion: v1
kind: Pod
metadata:
name: forday-api
spec:
containers:
- name: app
image: forday-api:1.2.0
ports:
- containerPort: 8080
사이드카 — 메인 컨테이너에 보조 기능을 추가
apiVersion: v1
kind: Pod
metadata:
name: forday-api
spec:
volumes:
- name: log-volume
emptyDir: {}
containers:
- name: app
image: forday-api:1.2.0
ports:
- containerPort: 8080
volumeMounts:
- name: log-volume
mountPath: /var/log/app
- name: log-collector
image: fluent-bit:2.1
volumeMounts:
- name: log-volume
mountPath: /var/log/app
Init Container — 메인 컨테이너보다 먼저 실행
spec:
initContainers:
- name: db-migration
image: forday-migration:1.2.0
containers:
- name: app
image: forday-api:1.2.0
구성 시 판단 기준
전제 — Pod는 수정하지 않고 교체한다
Pod는 생성된 이후 내용을 변경하지 않는 것을 원칙으로 한다. 새로운 버전의 배포는 다음을 의미한다.
동작 흐름 (forday-api:1.2.0 → 1.3.0)
[1] Deployment의 이미지 버전을 1.3.0으로 변경
↓
[2] 새 버전의 Pod 생성
스케줄러가 배치할 노드를 선택
→ 이미지 다운로드 → 컨테이너 실행
↓
[3] 새 Pod가 정상 상태(Ready)가 될 때까지 대기
Ready가 되어야 Service를 통해 트래픽을 전달받음
↓
[4] 기존 버전의 Pod 삭제
Service에서 먼저 제외한 뒤 종료
↓
[5] Pod 개수만큼 [2]~[4]를 반복
롤링 업데이트(Rolling Update)
Pod IP 변경과 Service
| 문제 | 해결 |
|---|---|
| Pod가 새로 생성될 때마다 IP 주소가 변경된다 | 쿠버네티스는 Service 리소스를 제공한다 |
| Pod IP를 직접 참조하면 배포 이후 통신이 끊긴다 | Service가 고정된 접근 지점을 제공하고, 현재 정상 상태인 Pod들에게 요청을 분배한다 |
따라서 애플리케이션 간 통신은 항상 Service를 통해 이루어져야 한다.
┌──────────────── Master Node (Control Plane) ────────────────┐
│ API Server 스케줄러 컨트롤러 │
│ = 명령을 받고, 배치를 결정하고, 상태를 감시 │
└─────────────────────────┬───────────────────────────────────┘
│ 지시
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Worker Node1 │ │ Worker Node2 │ │ Worker Node3 │
│ Pod Pod │ │ Pod │ │ Pod Pod │
└──────────────┘ └──────────────┘ └──────────────┘
= 실제로 Pod(컨테이너)가 실행되는 서버
| 구분 | 역할 |
|---|---|
| Master Node | 클러스터의 두뇌. 사용자 명령을 받고, Pod를 어느 노드에 배치할지 결정하고, 현재 상태가 원하는 상태와 같은지 계속 감시한다 |
| Worker Node | 실제 작업 서버. Master의 지시를 받아 Pod를 실행하고, 자신의 상태를 Master에 보고한다 |
사용자는 Worker Node를 직접 조작하지 않는다. Master에게 "이렇게 해달라"고 선언하면 Master가 Worker에 배분한다.
역할: Pod의 개수를 지정한 수만큼 유지한다
ReplicaSet (replicas: 3)
│
├── Pod A (Running)
├── Pod B (Running)
└── Pod C (Running)
Pod C 가 죽으면 ↓
├── Pod A (Running)
├── Pod B (Running)
└── Pod D (새로 생성) ← ReplicaSet이 자동 생성
Deployment → ReplicaSet → Pod → Container
배포/버전 관리 개수 유지 배포 단위 실행 단위
사용자는 보통 ReplicaSet을 직접 만들지 않고 Deployment를 만든다. Deployment가 ReplicaSet을 생성하고, ReplicaSet이 Pod를 관리한다. 새 버전을 배포하면 Deployment가 새 ReplicaSet을 만들어 Pod를 교체한다.