Kubernetes가 왜 필요한지부터 Control Plane·Worker Node·네트워크·스토리지·보안 구조, kubeadm 클러스터 구축, Gateway API·모니터링 구성, 배포 전체 흐름과 실무 비교·체크리스트까지 — 다이어그램과 함께 Kubernetes를 하나의 분산 플랫폼으로 이해하는 완전 가이드입니다.
Kubernetes가 왜 필요한지부터 Control Plane·Worker Node·네트워크·스토리지·보안 구조, kubeadm 클러스터 구축, Gateway API·모니터링 구성, 배포 전체 흐름과 실무 비교·체크리스트까지 — 다이어그램과 함께 Kubernetes를 하나의 분산 플랫폼으로 이해하는 완전 가이드입니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
핵심 관점
인프라 / 운영
설치 명령을 외우기보다 트래픽, 런타임, 관측, 장애 대응이 어떤 순서로 이어지는지 파악합니다.
클러스터 아키텍처 이해 & 직접 구축Deployment & Service 운영Gateway API·네트워크·스토리지·보안 구조 설계모니터링 스택 구성 & 배포 흐름 정리
구조 다이어그램
글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 Kubernetes를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.
학습 흐름
다이어그램 렌더링 중…
아키텍처 관점
다이어그램 렌더링 중…
Kubernetes란 무엇인가
Kubernetes를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.
Kubernetes(K8s)는 컨테이너로 패키징된 애플리케이션을 여러 서버(노드)에 걸쳐 자동으로 배포·확장·복구·운영하는 컨테이너 오케스트레이션 플랫폼입니다. 핵심은 "이 상태를 유지해라"고 선언만 하면 나머지는 시스템이 알아서 맞춰준다는 선언형(Declarative) 관리 모델입니다.
전통적인 서버 운영은 관리자가 서버 하나하나에 접속해 애플리케이션을 배포하고, 장애가 나면 직접 재시작하고, 트래픽이 늘면 수동으로 서버를 늘리는 방식이었습니다. VM 기반 운영은 하드웨어를 가상화해 유연성을 높였지만, VM 자체의 부팅 시간·리소스 오버헤드·이미지 크기 문제는 그대로 남아 있었습니다. Docker는 애플리케이션을 컨테이너로 패키징해 "어디서든 동일하게 실행된다"는 문제는 해결했지만, 컨테이너가 여러 대의 서버에 걸쳐 있을 때 그것들을 누가 어떻게 조율할 것인가라는 질문에는 답하지 못했습니다. Kubernetes는 바로 이 마지막 퍼즐 조각을 채웁니다 — 컨테이너 1개를 잘 실행하는 도구가 Docker라면, 컨테이너 수백~수천 개를 여러 서버에 걸쳐 안정적으로 운영하는 도구가 Kubernetes입니다.
Kubernetes가 해결하는 문제
전통적 방식의 한계
Kubernetes의 접근
배포 자동화
수동 SSH 배포, 스크립트 의존, 사람마다 절차가 다름
Deployment로 원하는 상태를 선언 → 컨트롤러가 자동으로 롤아웃
확장성
트래픽 증가 시 수동으로 서버 증설·설정 반복
HPA/Cluster Autoscaler로 지표 기반 자동 확장
장애 복구
프로세스가 죽으면 사람이 알아채고 재시작
kubelet이 지속적으로 감시하다 실패 시 자동 재시작(Self-healing)
서비스 디스커버리
IP를 하드코딩하거나 별도 레지스트리 직접 구축
Service + CoreDNS로 이름 기반 자동 탐색
운영 표준화
팀·서버마다 배포 방식이 제각각
YAML 매니페스트라는 공통 언어로 모든 워크로드를 동일하게 기술
선언형 관리
"무엇을 할지"를 명령어 순서로 기술(명령형)
"어떤 상태여야 하는지"만 선언 → 현재 상태를 계속 그 상태로 수렴(Reconcile)
전체 서비스 흐름 한눈에 보기
여기서는 전체 서비스 흐름 한눈에 보기을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
세부 구성요소를 하나씩 배우기 전에, 사용자의 요청 한 건이 실제로 어떤 경로를 거쳐 Pod까지 도달하는지부터 큰 그림으로 보고 시작하겠습니다. 사용자가 브라우저에 도메인을 입력하면 ① DNS가 그 도메인을 클라우드 LoadBalancer의 IP로 변환하고, ② 브라우저는 그 IP로 HTTPS 요청을 보냅니다. ③ LoadBalancer는 이 트래픽을 클러스터의 Gateway 계층으로 전달합니다.
이 Gateway 계층을 이 가이드에서는 Contour로 구현합니다. 실제 트래픽을 처리하며 TLS를 종료하는 것은 데이터 플레인인 Envoy Proxy이고, Contour 자신은 트래픽 경로에 직접 끼지 않는 컨트롤 플레인입니다 — Contour는 HTTPRoute(Gateway API 표준)나 HTTPProxy(Contour 전용 CRD)로 선언된 라우팅 규칙을 지켜보다가, 변경이 생기면 즉시 Envoy가 이해하는 설정(xDS)으로 변환해 전달합니다. ④ Envoy는 이렇게 전달받은 규칙으로 요청 경로에 맞는 Service를 찾아 트래픽을 전달합니다. ⑤ Service는 kube-proxy가 관리하는 규칙에 따라 실제 Pod 하나를 골라 트래픽을 라우팅하고, ⑥ 그 Pod 안의 Application Container가 요청을 처리해 응답을 만듭니다. 응답은 정확히 같은 경로를 거슬러 사용자에게 돌아갑니다. 이 왕복 하나가 여러분이 웹사이트에 접속할 때마다 K8s 내부에서 벌어지는 일입니다.
다이어그램 렌더링 중…
K8s 전체 아키텍처
여기서는 K8s 전체 아키텍처을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
Kubernetes 클러스터는 클러스터 전체를 제어하는 Control Plane과 실제 워크로드를 실행하는 Worker Node로 구성됩니다. Control Plane을 사람의 "두뇌"에 비유하면 이해가 쉽습니다 — 두뇌는 판단하고 지시할 뿐 직접 팔다리를 움직이지 않듯, Control Plane도 "어디에 무엇을 배치할지"만 결정하고 실제 컨테이너 실행은 Worker Node가 담당합니다. 이렇게 역할이 분리되어 있기 때문에 Control Plane에 장애가 나도 이미 떠 있는 Pod는 계속 정상 동작합니다(단, 새 배포나 장애 복구 같은 "새로운 판단"은 멈춥니다). 반대로 Worker Node 하나가 죽으면 그 안의 Pod들은 죽지만, Control Plane이 이를 감지해 다른 정상 Node에 다시 배치합니다 — 이 자동 복구가 Kubernetes의 핵심 가치입니다.
다이어그램 렌더링 중…
다이어그램 렌더링 중…
레이어
컴포넌트
역할
Control Plane
kube-apiserver
모든 kubectl/내부 통신의 진입점, REST API 제공 — 다른 모든 컴포넌트는 반드시 API Server를 거쳐 통신
Control Plane
etcd
클러스터 전체 상태를 저장하는 분산 Key-Value 스토어 — 사실상 클러스터의 유일한 "진실 공급원"
Control Plane
kube-scheduler
새 Pod를 어느 Worker Node에 배치할지 결정 (자원 여유, 어피니티, 테인트 등을 기준으로)
Control Plane
kube-controller-manager
Deployment/ReplicaSet/Node 등 각 리소스의 목표 상태를 계속 현재 상태와 비교해 맞추는 루프
Control Plane
cloud-controller-manager
AWS/GCP/NCP 등 클라우드 API 연동 (LoadBalancer 프로비저닝, Volume 연결 등)
Worker Node
kubelet
API Server 지시에 따라 Pod 생성·삭제·헬스체크 수행 — Node에서 유일하게 API Server와 직접 통신하는 에이전트
Worker Node
kube-proxy
Service ClusterIP → Pod IP 네트워크 규칙 관리 (iptables/ipvs)
Worker Node
containerd
컨테이너 런타임 — 이미지 pull, 컨테이너 실행
Worker Node
CNI Plugin
Pod 간 네트워크 연결 (Calico/Flannel/Cilium)
Add-on
CoreDNS
Service/Pod DNS 이름 해석
Add-on
Metrics Server
HPA/VPA를 위한 CPU·메모리 메트릭 수집
Add-on
Gateway Controller (구 Ingress Controller)
외부 트래픽을 Service로 라우팅하는 L7 프록시 — Ingress는 Legacy, 신규는 Gateway API
Worker Node 상세 — Pod는 어떻게 실제로 실행되는가
Worker Node 상세 — Pod는 어떻게 실제로 실행되는가은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
Worker Node에는 세 가지 핵심 컴포넌트가 있습니다. kubelet은 Node 위의 "현장 감독"으로, API Server가 지시한 Pod 명세를 받아 컨테이너 런타임에 실행을 요청하고, 주기적으로 컨테이너 상태를 확인해(Liveness/Readiness Probe) 죽은 컨테이너를 재시작합니다. kube-proxy는 Node의 네트워크 규칙(iptables/ipvs)을 관리해 Service의 ClusterIP로 들어온 트래픽을 실제 Pod IP로 전달합니다. 컨테이너 런타임(containerd 등)은 이미지를 실제로 pull하고 컨테이너 프로세스를 기동하는 실행 엔진입니다.
여기서 Node·Pod·Container의 관계를 명확히 하면: Node는 컨테이너가 돌아가는 물리/가상 서버 한 대, Pod는 그 Node 위에서 함께 스케줄링되는 컨테이너 1개 이상의 묶음(같은 네트워크 네임스페이스와 볼륨을 공유), Container는 Pod 안에서 실제로 프로세스가 실행되는 단위입니다. Pod가 "최소 배포 단위"인 이유는, Kubernetes가 스케줄링·헬스체크·네트워킹·재시작을 모두 Pod 단위로 다루기 때문입니다 — 컨테이너 하나만 따로 재시작하거나 다른 Node로 옮기는 개념 자체가 없고, 항상 Pod 전체가 함께 움직입니다.
Node 하나가 장애로 응답을 멈추면, Control Plane은 일정 시간(기본 40초 내외) 뒤 해당 Node를 NotReady로 표시하고, 이어서 그 위의 Pod들을 다른 정상 Node로 재스케줄링합니다. 단, 이 재스케줄링은 Deployment/StatefulSet 같은 컨트롤러가 관리하는 Pod에만 적용됩니다 — 컨트롤러 없이 직접 만든 "네이키드 Pod"는 Node가 죽으면 그대로 함께 사라지고 다시 살아나지 않습니다.
개념
정의
비유
Node
컨테이너가 실제로 실행되는 서버 (물리 서버 또는 VM)
아파트 건물
Pod
Node 위에서 함께 배치되는 컨테이너 1개 이상의 묶음. 네트워크/볼륨 공유
같은 집(호실)에 사는 룸메이트들
Container
Pod 안에서 실제 프로세스가 실행되는 단위 (이미지 1개 = 컨테이너 1개)
방 안의 사람 한 명
핵심 리소스 개념 & 관계도
여기서는 핵심 리소스 개념 & 관계도을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
K8s를 처음 접할 때 가장 자주 쓰는 리소스들의 역할과 관계를 정리합니다. 리소스는 서로 독립적이지 않고 Label/Selector라는 느슨한 연결 고리로 이어져 있습니다 — Service는 특정 label을 가진 Pod 집합을 찾아 트래픽을 보내고, Deployment는 자신이 만든 ReplicaSet을 label로 추적합니다. 이 "이름이 아닌 라벨로 연결한다"는 원칙이 Kubernetes 리소스 모델 전체를 관통합니다.
다이어그램 렌더링 중…
리소스
설명
주요 용도
Pod
가장 작은 배포 단위, 1개 이상 컨테이너 묶음
실제 앱 실행 단위
ReplicaSet
지정한 label과 일치하는 Pod 개수를 항상 N개로 유지
Deployment가 내부적으로 자동 생성·관리 — 직접 만들 일은 거의 없음
Deployment
ReplicaSet을 감싸 롤링 업데이트 · 롤백 이력까지 관리
Stateless 앱의 기본 컨트롤러
StatefulSet
Pod 순서·고정 네트워크 ID·독립 스토리지 보장
DB, 메시지 브로커 등 상태 저장 워크로드
DaemonSet
모든(또는 조건에 맞는) 노드에 Pod 1개씩 배포
로그 수집기, 모니터링 에이전트, CNI 플러그인
Job
완료(Completed)가 있는 일회성 작업 — 성공할 때까지 재시도
DB 마이그레이션, 배치 처리
CronJob
Job을 cron 스케줄대로 주기적으로 생성
야간 리포트, 정기 백업
Namespace
리소스를 논리적으로 격리하는 가상 클러스터 경계
환경(dev/staging/prod)·팀별 분리
Label / Selector
리소스에 붙이는 key-value 태그와 그것을 찾는 질의 조건
Service·ReplicaSet 등이 대상 Pod를 찾는 유일한 방법
Annotation
Label과 달리 질의에 쓰이지 않는 부가 메타데이터
빌드 정보, 도구 설정값(예: cert-manager annotation)
Service
Label Selector로 찾은 Pod 집합에 안정적 엔드포인트 제공
ClusterIP/NodePort/LoadBalancer/Headless
Gateway API
GatewayClass/Gateway/HTTPRoute 3계층으로 Ingress를 대체하는 K8s 공식 표준
클러스터 구축 로드맵 & 패키지 원천은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
Kubernetes 클러스터 구축의 전체 주기(설치부터 운영까지)와 설치 시 다운로드받는 각 패키지/소스의 원천(Origin) 및 관리 주체를 상세히 정리합니다. 프로덕션 환경의 정합성과 무결성을 지키기 위해 이들 원천지 주소 및 파이프라인의 안전성을 아는 것이 중요합니다.
구축 단계
핵심 활동 (Activity)
대상 소스/패키지 원천 및 제공처
1단계. 인프라 준비
커널 파라미터 튜닝, Swap 비활성화, 방화벽 규칙 적용, SELinux 해제
OS 업스트림 레포지토리 (Debian/Ubuntu, Rocky/RHEL 공식 미러)
2단계. 런타임 설치
컨테이너 실행 및 이미지 관리를 위한 containerd 설치 및 cgroup 설정
Docker 공식 패키지 저장소 (download.docker.com)
3단계. K8s 도구 설치
kubeadm, kubelet, kubectl 패키지 설치 및 업데이트 고정
Kubernetes 공식 패키지 저장소 (pkgs.k8s.io - 데비안/RPM)
4단계. 클러스터 초기화
kubeadm init을 통한 Control Plane 구성, kubeconfig 배포
k8s.gcr.io / registry.k8s.io (공식 OCI 이미지 레지스트리)
5단계. 네트워크 (CNI)
Pod 간의 L3 오버레이 네트워크 연결 및 네트워크 폴리시 적용
CNI 공식 프로젝트 릴리스 (github.com/projectcalico/calico, cilium/cilium 등)
6단계. 서비스 & 인그레스
외부 노출을 위한 LoadBalancer / Ingress 구성 및 TLS 종료 설정
Helm Artifact Hub 및 공식 프로젝트 저장소 (kubernetes/ingress-nginx)
# Control Plane에서 클러스터 전체 상태 확인# 노드 목록 (CNI 전: NotReady)kubectl get nodes -o wide# 시스템 Pod 상태kubectl get pods -n kube-system# etcd 헬스체크kubectl exec -n kube-system etcd-k8s-master-01 -- \ etcdctl endpoint health \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key# API Server 응답 확인 (kubectl get componentstatuses는 deprecated — /healthz?verbose 사용 권장)kubectl cluster-infokubectl get --raw='/healthz?verbose'
06-ha-control-plane.shBASH
# HA Control Plane 추가 (3-node HA 구성 시)# Control Plane #2, #3 에서 실행# kubeadm init 출력의 --control-plane 명령 사용kubeadm join k8s-master-01:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane \ --certificate-key <cert-key># certificate-key 재발급 (만료 시)# kubeadm init phase upload-certs --upload-certs# HA 완료 후 로드밸런서로 API Server 분산 필요# HAProxy / keepalived / 클라우드 LB를 VIP(192.168.1.100:6443)로 설정# --control-plane-endpoint는 VIP 또는 DNS로 지정
Kubernetes 네트워크 구조
여기서는 Kubernetes 네트워크 구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
Kubernetes 네트워킹은 하나의 원칙에서 시작합니다 — 모든 Pod는 NAT 없이 다른 모든 Pod와 직접 통신할 수 있다(Flat Network 모델). Pod가 어느 Node에 있든, Pod IP는 클러스터 전체에서 고유하고 그대로 도달 가능해야 합니다. 이 모델을 실제로 구현하는 것이 CNI(Container Network Interface) 플러그인(Calico, Flannel, Cilium 등)입니다 — CNI는 특정 제품이 아니라 "이런 규격으로 Pod 네트워크를 구현하라"는 표준 인터페이스이고, 클러스터 운영자가 그중 하나를 골라 설치합니다.
Pod IP와 Node IP는 완전히 다른 주소 공간입니다. Node IP는 서버 자체의 고정 주소(예: 10.0.1.10)이고, Pod IP는 CNI가 관리하는 별도의 가상 네트워크 대역(예: 10.244.1.5)에서 할당되며, 무엇보다 Pod가 재생성되면 IP가 바뀝니다. 배포·스케일링·장애 복구 때마다 Pod IP가 바뀐다면 클라이언트가 어떻게 이 Pod를 계속 찾아갈 수 있을까요? 이 문제를 푸는 것이 바로 Service입니다 — Service는 Label Selector로 대상 Pod 집합을 찾아 하나의 고정된 가상 IP(ClusterIP)와 이름을 부여합니다.
다이어그램 렌더링 중…
다이어그램 렌더링 중…
Service 유형
동작 방식
주요 용도
ClusterIP (기본값)
클러스터 내부에서만 접근 가능한 가상 IP 발급
내부 마이크로서비스 간 통신
NodePort
모든 Node의 동일 포트(30000-32767)를 열어 외부에서 :로 접근
간단한 테스트, 클라우드 LB 없는 온프레미스 임시 노출
LoadBalancer
cloud-controller-manager가 클라우드 LB를 자동 프로비저닝
클라우드 환경의 표준 외부 노출 방식
Headless (clusterIP: None)
가상 IP를 발급하지 않고 DNS 조회 시 Pod IP 목록을 그대로 반환
StatefulSet처럼 각 Pod에 개별 접근이 필요한 경우
Ingress vs Gateway API — 입문자를 위한 비교
Ingress vs Gateway API — 입문자를 위한 비교은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
기존 Ingress는 호스트/경로 라우팅, TLS, 컨트롤러별 세부 옵션(annotation)까지 하나의 리소스에 모두 담는 구조였습니다. 문제는 이 annotation이 컨트롤러(nginx, traefik 등)마다 제각각이라 표준이 없었고, 인프라팀(도메인·TLS 관리)과 앱팀(경로 라우팅)의 권한을 나눌 방법도 마땅치 않았다는 점입니다. Gateway API는 이 책임을 역할별로 3계층으로 쪼갭니다.
계층
누가 관리하는가
담당 역할
GatewayClass
인프라/플랫폼 팀 (클러스터 관리자)
어떤 구현체(Contour, Envoy Gateway 등)를 쓸지 정의
Gateway
인프라/플랫폼 팀
리스너 포트, 도메인, TLS 인증서 등 진입점 설정
HTTPRoute
애플리케이션 팀 (각자의 네임스페이스에서)
자신의 서비스로 가는 경로·헤더 라우팅 규칙 정의
CNI & 네트워크 플러그인 설치
여기서는 CNI & 네트워크 플러그인 설치을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
CNI(Container Network Interface) 플러그인이 없으면 노드가 NotReady 상태입니다. 대표적인 Calico와 Flannel 중 선택합니다. 네트워크 정책(NetworkPolicy)이 필요하면 Calico를 권장합니다.
calico-install.shBASH
# Calico 설치 (권장)# kubeadm init 시 --pod-network-cidr=192.168.0.0/16 지정 필요kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yamlcat > calico-custom.yaml << 'EOF'apiVersion: operator.tigera.io/v1kind: Installationmetadata: name: defaultspec: calicoNetwork: ipPools: - blockSize: 26 cidr: 192.168.0.0/16 encapsulation: VXLANCrossSubnet # 멀티 서브넷 환경 natOutgoing: Enabled nodeSelector: all() nodeMetricsPort: 9091---apiVersion: operator.tigera.io/v1kind: APIServermetadata: name: defaultspec: {}EOFkubectl create -f calico-custom.yaml# 설치 완료 확인 (모든 Pod Running)watch kubectl get pods -n calico-system# 노드 Ready 확인kubectl get nodes
network-policy.yamlBASH
# NetworkPolicy — Namespace 간 격리 예시# default namespace의 Pod는 같은 namespace 내에서만 통신 허용apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-deny-ingress namespace: productionspec: podSelector: {} # 모든 Pod에 적용 policyTypes: - Ingress---# API 서버만 DB 접근 허용apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-api-to-db namespace: productionspec: podSelector: matchLabels: app: postgres policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: role: api-server ports: - port: 5432
coredns-check.shBASH
# CoreDNS 및 DNS 해석 확인kubectl get pods -n kube-system -l k8s-app=kube-dns# DNS 동작 테스트kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- \ nslookup kubernetes.default# Service DNS 해석 형식# <service>.<namespace>.svc.cluster.local# 예: postgres-svc.database.svc.cluster.local
CNI
Pod CIDR
네트워크 정책
특이사항
Calico
192.168.0.0/16
O (완전 지원)
BGP 기반, 대규모 클러스터 적합, IPAM 내장
Flannel
10.244.0.0/16
X (별도 설치 필요)
설정 단순, 소규모 클러스터
Cilium
자유
O + L7 정책
eBPF 기반, 고성능, Hubble 관측성 내장
WeaveNet
10.32.0.0/12
O
암호화 지원, 소규모 환경
Kubernetes 스토리지 구조
여기서는 Kubernetes 스토리지 구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
컨테이너는 기본적으로 상태 비저장(Stateless)입니다 — 컨테이너 내부에 쓴 파일은 컨테이너(또는 그것을 감싼 Pod)가 재생성되는 순간 함께 사라집니다. Pod는 배포·스케일링·장애 복구 과정에서 수시로 삭제되고 다시 만들어지는 존재이므로, DB나 업로드 파일처럼 반드시 남아야 하는 데이터를 컨테이너 파일시스템에만 의존해 두면 안 됩니다. 이 문제를 해결하기 위해 Kubernetes는 스토리지를 컨테이너 생명주기와 분리된 별도 오브젝트로 다룹니다.
PersistentVolume(PV)은 실제 스토리지(NFS, 클라우드 디스크, 로컬 디스크 등)를 클러스터 오브젝트로 추상화한 것이고, PersistentVolumeClaim(PVC)은 애플리케이션(Pod)이 "이만큼의 용량과 이런 접근 모드가 필요하다"고 요청하는 명세입니다. StorageClass는 PV를 미리 만들어두지 않고, PVC가 생성되는 순간 해당 백엔드에 맞는 PV를 자동으로 만들어주는 동적 프로비저닝(Dynamic Provisioning) 템플릿입니다 — 실무에서는 관리자가 PV를 일일이 손으로 만드는 정적 프로비저닝보다 이 방식을 압도적으로 많이 씁니다.
다이어그램 렌더링 중…
백엔드
특징
적합한 상황
Local Path
Node의 로컬 디스크를 직접 사용 — Node가 바뀌면 데이터 접근 불가
개발 환경, 단일 노드 테스트
NFS
여러 Node에서 동시에 같은 볼륨을 공유 마운트 가능(ReadWriteMany)
온프레미스, 여러 Pod가 파일을 공유해야 하는 경우
클라우드 디스크 (EBS/PD/Managed Disk 등)
Node가 바뀌어도 자동으로 재연결(ReadWriteOnce)
클라우드 환경의 DB, 단일 Pod 전용 볼륨
스토리지 설정 — StorageClass & PV
여기서는 스토리지 설정 — StorageClass & PV을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
온프레미스 환경에서는 클라우드 프로비저너가 없으므로 local-path-provisioner(개발/소규모) 또는 NFS Provisioner(프로덕션)를 설치합니다.
local-path.shBASH
# local-path-provisioner 설치 (단일 노드 개발 환경)kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.28/deploy/local-path-storage.yaml# default StorageClass로 지정kubectl patch storageclass local-path \ -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'kubectl get storageclass
여기서는 Kubernetes 보안 구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
API Server로 들어오는 모든 요청은 세 단계를 순서대로 통과해야 합니다. 먼저 인증(Authentication) — "너는 누구냐"를 확인합니다(사용자는 클라이언트 인증서나 OIDC 토큰, Pod는 ServiceAccount 토큰). 다음은 인가(Authorization) — "너는 이 작업을 할 권한이 있느냐"를 확인합니다. Kubernetes 기본 인가 방식이 바로 RBAC입니다. 마지막으로 Admission Control이 "이 요청이 정책에 맞느냐"를 한 번 더 검증(예: privileged 컨테이너 금지, 이미지 출처 제한)한 뒤에야 etcd에 실제로 반영됩니다. 이 3단계 중 하나라도 실패하면 요청은 즉시 거부됩니다.
Namespace 범위(Role) 또는 클러스터 전체(ClusterRole) 권한을 사용자·ServiceAccount에 부여
최소 권한 원칙 — 상세 예제는 K8s 심화 가이드 RBAC 섹션 참고
ServiceAccount
Pod 내부 프로세스가 API Server를 호출할 때 쓰는 신원
쓰지 않는 Pod는 automountServiceAccountToken: false로 토큰 자동 마운트 차단 권장
NetworkPolicy
Pod 간 트래픽을 화이트리스트 방식으로 제한 (기본은 전체 허용)
CNI가 NetworkPolicy를 지원해야 동작 — Flannel은 미지원, Calico/Cilium은 지원
Secret
민감 데이터 저장(Base64) — 그 자체가 암호화는 아님
etcd Encryption at Rest, 최소권한 RBAC 등 상세는 ConfigMap & Secret 섹션 참고
이미지 · 레지스트리 보안
신뢰 가능한 레지스트리만 사용, 이미지 취약점 스캔(Trivy 등)
latest 태그 지양, private 레지스트리는 imagePullSecrets로 인증
Pod Security (Admission)
컨테이너의 privileged 실행, root 사용자, hostPath 마운트 등을 제한
과거 PodSecurityPolicy는 제거됨 — 현재는 Namespace 라벨 기반 Pod Security Admission(PSA) 사용
Gateway API & TLS 설정 (Ingress 후속)
여기서는 Gateway API & TLS 설정 (Ingress 후속)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
Kubernetes 커뮤니티의 Ingress-NGINX 프로젝트는 2026년 3월 24일 공식 종료(EOL)되어 이후 버그 수정이나 보안 패치가 제공되지 않습니다. Kubernetes 공식 문서도 신규 클러스터에는 Ingress 대신 Gateway API 사용을 권장합니다. Gateway API는 Ingress 한 리소스에 모든 설정을 몰아넣던 방식 대신, 인프라 담당자가 관리하는 GatewayClass/Gateway와 애플리케이션 팀이 관리하는 HTTPRoute를 역할별로 분리한 K8s SIG-Network 공식 표준(Ingress의 정식 후속 API)입니다. 이 가이드는 이제부터 Gateway API를 기본으로 안내하고, 기존 Ingress 환경은 별도 "Legacy" 섹션으로 유지합니다.
실습 예제로는 Contour를 사용합니다. Contour는 CNCF Incubating 프로젝트로, Envoy Proxy를 실제 트래픽을 처리하는 데이터 플레인으로 두고 자신은 컨트롤 플레인 역할만 담당하는 구조입니다 — 사용자가 만든 Gateway API/HTTPProxy 리소스를 지켜보다가, 변경이 생기면 Envoy가 이해하는 형식(xDS API)으로 변환해 실시간으로 전달합니다. NGINX Ingress처럼 설정 변경 시 워커 프로세스를 reload할 필요가 없어, 라우트가 많은 환경에서도 커넥션 드롭 없이 무중단으로 반영됩니다. Contour가 특히 유용한 이유는 Gateway API(HTTPRoute)와 자체 CRD인 HTTPProxy를 동시에 지원한다는 점입니다 — 표준을 따르고 싶으면 HTTPRoute를, 팀 간 라우트 위임이나 세밀한 정책(Rate limit, IP 필터 등)이 필요하면 HTTPProxy를 골라 쓸 수 있습니다. 아래 예제에서 같은 라우팅 규칙을 두 방식으로 나란히 작성해봅니다.
비교 항목
HTTPRoute (Gateway API)
HTTPProxy (Contour 전용)
표준 여부
Kubernetes SIG-Network 공식 표준
Contour 프로젝트 자체 CRD
진입점 리소스
GatewayClass + Gateway가 별도로 필요
HTTPProxy의 virtualhost 필드로 자체 완결
팀 간 위임
ReferenceGrant로 크로스 네임스페이스 허용
spec.includes로 경로/도메인별 위임을 명시적으로 표현
세밀한 정책
구현체별 확장(GEP) 필요 — 아직 표준화 진행 중
rateLimitPolicy, ipAllow/DenyFilterPolicy 등 바로 사용 가능
다른 컨트롤러로 이전
대부분 그대로 재사용 가능 (표준이므로)
Contour 전용이라 다른 구현체에서는 동작하지 않음
01-gateway-api-install.shBASH
# 1) Gateway API CRD 설치 (GatewayClass/Gateway/HTTPRoute 등록) — standard channelkubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml# 2) Gateway Controller 설치 — 여기서는 Contour 예시 (Envoy Gateway, Cilium 등으로 대체 가능)# Contour Gateway Provisioner가 GatewayClass "contour"를 자동 생성kubectl apply -f https://projectcontour.io/quickstart/contour-gateway-provisioner.yamlkubectl get gatewayclass contour# 팀별 위임(HTTPProxy)·정책 설정 등 심화 내용은 K8s 심화 가이드의# "Contour & Gateway API" 섹션(/learn/kubernetes-advanced/#contour-gateway)에서 이어집니다.
02-gatewayclass-gateway.yamlYAML
# GatewayClass는 위 Provisioner 매니페스트 적용 시 자동 생성됨 (참고용)apiVersion: gateway.networking.k8s.io/v1kind: GatewayClassmetadata: name: contourspec: controllerName: projectcontour.io/gateway-controller---apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: myapp-gateway namespace: projectcontourspec: gatewayClassName: contour listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: All - name: https protocol: HTTPS port: 443 hostname: "api.aidevops.kr" tls: mode: Terminate certificateRefs: - kind: Secret name: api-aidevops-kr-tls # cert-manager Certificate가 자동 생성 allowedRoutes: namespaces: from: All
# 바로 위 HTTPRoute와 동일한 라우팅을, Contour 전용 CRD인 HTTPProxy로 표현한 버전# Gateway API 표준을 아직 안 쓰는 클러스터거나, includes/rateLimitPolicy 같은# Contour만의 세밀한 기능이 필요할 때 이 방식을 선택합니다.apiVersion: projectcontour.io/v1kind: HTTPProxymetadata: name: myapp-proxy namespace: productionspec: virtualhost: fqdn: api.aidevops.kr tls: secretName: api-aidevops-kr-tls # cert-manager Certificate가 자동 생성 routes: - conditions: - prefix: /api/v1 services: - name: api-service port: 8080 - conditions: - prefix: / services: - name: frontend-service port: 3000
Gateway Controller (Contour, Envoy Gateway, Cilium 등)
K8s 공식 표준, 활발히 개발 중
Legacy
IngressClass → Ingress
기존 Ingress Controller (예: ingress-nginx)
ingress-nginx는 2026-03-24 EOL — 신규 보안 패치 없음
Legacy 환경 — 기존 Ingress를 아직 쓰고 있다면
여기서는 Legacy 환경 — 기존 Ingress를 아직 쓰고 있다면을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
⚠️
ingress-nginx는 2026년 3월 24일부로 프로젝트가 종료(EOL)되었습니다. 신규 취약점이 발견되어도 패치되지 않으므로, 이미 ingress-nginx를 운영 중이라면 Gateway API로의 전환 계획을 세우는 것을 권장합니다. Kubernetes SIG Network가 배포하는 ingress2gateway(2026년 3월 v1.0 출시)를 사용하면 기존 Ingress 리소스를 Gateway API 리소스(GatewayClass/Gateway/HTTPRoute)로 자동 변환할 수 있습니다.
ingress2gateway-migrate.shBASH
# ingress2gateway 설치 (Krew 플러그인 또는 바이너리 다운로드)# https://github.com/kubernetes-sigs/ingress2gateway/releases# 현재 클러스터의 ingress-nginx 기반 Ingress를 Gateway API 리소스로 변환해 출력ingress2gateway print --providers=ingress-nginx > gateway-resources.yaml# 결과 검토 후 적용 (기존 Ingress는 자동으로 삭제되지 않으므로 병행 운영하며 트래픽 전환 가능)kubectl apply -f gateway-resources.yaml# 전환 검증 후 레거시 Ingress 및 ingress-nginx 컨트롤러 제거# kubectl delete ingress myapp-ingress -n production# helm uninstall ingress-nginx -n ingress-nginx
오토스케일링과 고가용성은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
Kubernetes의 가용성 전략은 "장애가 안 나게 막는다"가 아니라 "장애가 나도 서비스는 계속되게 한다"는 전제에서 출발합니다. 가장 기본적인 방법은 Pod를 여러 개(Replica) 띄워두는 것입니다 — 하나가 죽어도 나머지가 트래픽을 받습니다. 여기에 Self-healing이 더해집니다: kubelet은 컨테이너가 죽으면 즉시 재시작하고, kube-controller-manager는 "N개를 유지하라"는 목표와 실제 살아있는 Pod 수를 계속 비교하다가 부족하면 새 Pod를 만듭니다. 사람이 개입하지 않아도 스스로 원래 상태로 돌아온다는 점에서 "Self-healing(자가 치유)"이라 부릅니다.
트래픽이 변할 때는 오토스케일러가 개입합니다. HPA(Horizontal Pod Autoscaler)는 CPU·메모리 등 지표를 보고 Pod 개수를 늘리거나 줄이고(가로 확장), VPA(Vertical Pod Autoscaler)는 컨테이너 하나의 CPU/메모리 할당량을 조정합니다(세로 확장). Pod를 늘리려 해도 Node에 여유 자원이 없어 Pending 상태가 계속되면, Cluster Autoscaler가 클라우드 Auto Scaling Group에 Node 자체를 추가해 근본적인 여유를 만들어줍니다. (HPA/VPA의 구체적인 설정 예제는 K8s 심화 가이드에서 다룹니다.)
개념
역할
헷갈리기 쉬운 점
PodDisruptionBudget(PDB)
Node 드레인·클러스터 업그레이드 같은 "자발적 중단" 시 최소 가용 Pod 수를 보장
Node 장애처럼 "비자발적 중단"은 PDB로 막을 수 없음 — Replica 수 자체를 늘려야 함
Liveness Probe
컨테이너가 살아있는지 확인 — 실패하면 kubelet이 재시작
단순 "떠 있음"만 확인 — 트래픽을 받을 준비가 됐는지는 알 수 없음
Readiness Probe
컨테이너가 트래픽을 받을 준비가 됐는지 확인 — 실패하면 Service 대상에서 즉시 제외(재시작은 안 함)
Liveness와 혼동해 재시작이 필요한 상황에 Readiness만 설정하는 실수가 흔함
Startup Probe
초기 구동이 느린 앱을 위해 최초 기동 완료까지 Liveness/Readiness 판정을 유예
느린 JVM 기동 앱에서 Liveness timeout으로 계속 재시작되는 문제의 해법
모니터링 스택 구성 — Prometheus & Grafana
여기서는 모니터링 스택 구성 — Prometheus & Grafana을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
관측 가능성(Observability)은 세 가지 신호로 나뉩니다 — 메트릭(지금 얼마나 많은 요청이 오는가, 숫자 시계열), 로그(무엇이 잘못됐는가, 이벤트 텍스트), 트레이싱(어디서 느린가, 요청 하나가 여러 서비스를 거치는 경로). Kubernetes 운영에서는 이 중 메트릭이 가장 먼저 필요합니다. kube-prometheus-stack Helm 차트는 Prometheus(수집·저장), Grafana(시각화), Alertmanager(알림), node-exporter(노드 메트릭), kube-state-metrics(K8s 오브젝트 상태 메트릭)를 한 번에 설치해줍니다.
운영 중에는 CPU/메모리 수치만 봐서는 안 됩니다. Kubernetes 특유의 신호를 함께 확인해야 합니다 — Pod가 Pending이면 스케줄링에 실패했다는 뜻(자원 부족, 노드 셀렉터 불일치 등)이고, CrashLoopBackOff는 컨테이너가 계속 죽었다 재시작되기를 반복한다는 뜻이며, OOMKilled는 메모리 limit을 초과해 커널이 프로세스를 강제 종료했다는 뜻입니다. 이 세 가지는 애플리케이션 로그만 보면 놓치기 쉬운, K8s 레이어에서만 보이는 장애 신호입니다.
# Grafana 주요 대시보드 ID (grafana.com/dashboards)# 웹 UI에서 Import → ID 입력# K8s 클러스터 전체 현황# Dashboard ID: 15661 (Kubernetes / Views / Global)# 노드 메트릭 (CPU/메모리/디스크/네트워크)# Dashboard ID: 1860 (Node Exporter Full)# Pod 메트릭 (namespace별)# Dashboard ID: 6417 (Kubernetes Pods)# Spring Boot 애플리케이션# Dashboard ID: 19004 (Spring Boot 3.x Statistics)# Gateway Controller (Envoy 기반 — Contour/Envoy Gateway 등)# Dashboard ID: 11021 (Envoy Global), 11022 (Envoy Clusters)# Legacy ingress-nginx를 아직 쓴다면: Dashboard ID 9614 (NGINX Ingress controller)# Alertmanager Slack 연동kubectl create secret generic alertmanager-slack \ --from-literal=slack_api_url=https://hooks.slack.com/services/XXX \ -n monitoring
운영 신호
의미
흔한 원인
Pending
Pod가 아직 어느 Node에도 스케줄되지 못함
클러스터 자원 부족, nodeSelector/Affinity 조건과 맞는 Node 없음, PVC 바인딩 대기
CrashLoopBackOff
컨테이너가 시작 직후 계속 죽었다 재시작되기를 반복
앱 설정 오류, 의존 서비스(DB 등) 연결 실패, Liveness Probe 오설정
OOMKilled
메모리 limit을 초과해 커널 OOM Killer가 프로세스를 강제 종료
resources.limits.memory를 너무 낮게 설정, 메모리 누수
ImagePullBackOff
이미지를 레지스트리에서 받아오지 못함
이미지 태그 오타, private 레지스트리 인증정보(imagePullSecrets) 누락
재시작 횟수(Restart Count) 증가
Pod가 정상 구간 대비 빈번히 재시작됨
Liveness Probe timeout이 앱 응답 속도보다 짧게 설정된 경우가 흔함
Deployment & Service
여기서는 Deployment & Service을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
Deployment는 지정한 replicas 수만큼 Pod를 항상 유지하도록 관리하며, Pod가 죽으면 자동으로 새 Pod를 만들어 대체합니다. Service는 이렇게 계속 바뀌는 Pod들의 IP를 신경 쓸 필요 없이, selector로 지정한 라벨을 가진 Pod 그룹에 고정된 하나의 접속 지점을 제공합니다.
여기서는 ConfigMap & Secret을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
Secret은 ConfigMap과 API 구조는 거의 같지만 값이 Base64로 인코딩된다는 차이만 있습니다. Base64는 인코딩이지 암호화가 아니므로kubectl get secret -o jsonpath 한 줄이면 누구나 원문을 복원할 수 있고, 기본 설정의 kubeadm 클러스터는 이 값을 etcd에도 평문 그대로 저장합니다. Secret이라는 이름만 보고 안전하다고 오해하지 말고, 아래 4가지 계층을 함께 갖춰야 실제로 보호됩니다.
# Base64는 누구나 즉시 복원 가능 — "인코딩"일 뿐 "암호화"가 아님을 직접 확인kubectl get secret myapp-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d# → s3cr3t (권한만 있으면 누구든 원문 확인 가능)# etcd Encryption at Rest 활성화 여부는 kube-apiserver 매니페스트로 확인# (kubeadm 클러스터는 /etc/kubernetes/manifests/kube-apiserver.yaml)grep -- "--encryption-provider-config" /etc/kubernetes/manifests/kube-apiserver.yaml# 아무 출력이 없다면 etcd에 Secret이 평문으로 저장되고 있다는 뜻
03-encryption-at-rest.yamlYAML
# kube-apiserver에 --encryption-provider-config=/etc/kubernetes/enc/enc.yaml 로 지정# (Control Plane 노드의 static pod manifest에 volume/volumeMount 추가 필요)apiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources: - resources: ["secrets"] providers: - aescbc: keys: - name: key1 secret: <base64로 인코딩한 32바이트 랜덤 키> # head -c 32 /dev/urandom | base64 - identity: {} # 기존 평문 Secret과의 호환을 위한 폴백 — 반드시 목록 마지막에 위치---# 적용 후 기존 Secret을 재작성해야 실제로 암호화됨 (rewrite 트리거)# kubectl get secrets --all-namespaces -o json | kubectl replace -f -
여기서는 배포 전체 흐름 — 코드에서 서비스까지을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
지금까지 배운 모든 구성요소를 하나의 이야기로 이어보겠습니다. ① 개발자가 코드를 작성하고 이미지를 빌드합니다. ② 그 이미지를 컨테이너 레지스트리에 Push합니다. ③ Deployment 매니페스트를 클러스터에 반영합니다(kubectl apply 또는 ArgoCD 같은 GitOps 도구로). API Server는 이 요청을 인증·인가·Admission 검증 후 etcd에 저장합니다. ④ kube-scheduler가 새로 필요한 Pod를 감지해, 자원 여유가 있는 Node를 골라 배치를 결정합니다. ⑤ 그 Node의 kubelet이 이 결정을 감지해 컨테이너 런타임에 레지스트리로부터 이미지를 pull하고 컨테이너를 실행하도록 지시합니다.
Pod가 뜨는 것만으로는 서비스가 되지 않습니다. ⑥ Service가 Label Selector로 이 Pod들을 찾아 하나의 안정적인 엔드포인트로 묶고, ⑦ Gateway(또는 Ingress)가 외부 도메인·TLS·경로 라우팅을 그 Service에 연결해야 비로소 외부 사용자가 접근할 수 있습니다. 이 과정에서 ⑧ ConfigMap과 Secret이 환경별 설정값과 민감 정보를 컨테이너에 주입합니다. 트래픽이 늘어나면 ⑨ HPA가 지표를 보고 Pod 수를 자동으로 늘리고, 이 모든 과정 내내 ⑩ Prometheus가 메트릭을 수집하고 Grafana가 그 상태를 시각화하며, 문제가 생기면 Alertmanager가 알림을 보냅니다. 애플리케이션 하나가 "코드"에서 "서비스"가 되기까지, 이 열 단계가 매 배포마다 반복됩니다.
다이어그램 렌더링 중…
실무 관점 비교 정리
실무 관점 비교 정리은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
이 가이드에서 다룬 개념들은 이름이 비슷해 실무에서 자주 혼동됩니다. 어떤 상황에 어느 쪽을 골라야 하는지 헷갈릴 때 아래 표로 빠르게 되짚어 보세요.
비교 대상
핵심 차이
Pod vs Container
Container는 프로세스 실행 단위, Pod는 그 Container(들)이 네트워크·볼륨을 공유하며 함께 스케줄링되는 K8s 최소 단위
Deployment vs StatefulSet
Deployment는 Pod가 서로 완전히 동일하고 교체 가능(Stateless), StatefulSet은 각 Pod가 고유한 이름·네트워크 ID·전용 스토리지를 가짐(Stateful)
Service vs Ingress vs Gateway API
Service는 L4 수준의 안정적 엔드포인트, Ingress/Gateway API는 그 위에서 도메인·경로 기반 L7 라우팅과 TLS 종료를 수행 (Gateway API가 Ingress의 공식 후속)
ConfigMap vs Secret
둘 다 설정을 코드와 분리하는 용도지만, ConfigMap은 비민감 데이터, Secret은 Base64 인코딩된 민감 데이터 — Secret도 그 자체로 암호화는 아님
NodePort vs LoadBalancer
NodePort는 모든 Node의 고정 포트를 열어 수동으로 노출, LoadBalancer는 클라우드 LB를 자동 프로비저닝 — 클라우드 환경에서는 LoadBalancer가 사실상 표준
HPA vs VPA
HPA는 Pod 개수를 조정(가로 확장), VPA는 Pod 하나의 CPU/메모리 할당량을 조정(세로 확장) — 목적이 다르므로 같은 대상에 동시 적용 시 충돌 위험
Docker Compose vs Kubernetes
Docker Compose는 단일 호스트에서 컨테이너 여러 개를 함께 띄우는 도구, Kubernetes는 여러 호스트에 걸친 배포·확장·복구·서비스 디스커버리까지 아우르는 오케스트레이션 플랫폼
VM 운영 vs Kubernetes 운영
VM 운영은 서버 단위로 사람이 배포·복구·확장을 관리, Kubernetes 운영은 "원하는 상태"만 선언하면 시스템이 지속적으로 그 상태를 맞추는 선언형·자동화 중심 운영
자주 하는 실수와 운영 팁
자주 하는 실수와 운영 팁은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
아래는 실무에서 반복적으로 관찰되는 실수들입니다. 미리 알고 있으면 대부분 피할 수 있습니다.
실수
왜 문제인가
올바른 접근
Pod를 직접 운영 리소스로 사용
Node 장애나 삭제 시 다시 살아나지 않음 — 컨트롤러가 없으면 재스케줄링 자체가 안 됨
스케줄링 근거가 없어 자원 배치가 불균형해지고, limit 없으면 한 Pod가 Node 메모리를 독점해 다른 Pod가 OOMKilled될 수 있음
모든 컨테이너에 requests/limits를 명시
Readiness와 Liveness Probe 혼동
Readiness 실패에 재시작을 기대하거나, Liveness만 설정해 트래픽 차단이 안 되는 경우가 흔함
Liveness=재시작 여부, Readiness=트래픽 수신 여부로 역할을 분리해서 설정
StatefulSet이 필요한데 Deployment를 사용
데이터를 가진 워크로드가 재생성될 때마다 스토리지 연결·네트워크 ID가 꼬임
DB·메시지 브로커처럼 상태를 가진 워크로드는 반드시 StatefulSet 사용
Ingress와 Ingress Controller를 같은 것으로 오해
Ingress 리소스만 apply하고 실제 트래픽이 안 온다고 당황하는 경우 — Ingress는 명세일 뿐 동작하는 실체가 아님
Ingress Controller(또는 Gateway Controller)를 반드시 별도로 설치해야 Ingress가 동작함
CNI와 Service 네트워크를 혼동
Pod 네트워크(CNI가 담당하는 Pod-to-Pod 통신)와 Service 네트워크(kube-proxy가 담당하는 가상 IP 라우팅)는 서로 다른 레이어
네트워크 문제 발생 시 "Pod 간 통신 문제인지, Service 라우팅 문제인지"부터 구분해서 접근
결론 & 다음 단계
이 섹션은 결론 & 다음 단계을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.
Kubernetes는 결국 "원하는 상태를 선언하면, 여러 컨트롤러가 협력해 그 상태를 계속 유지해주는 분산 플랫폼"입니다. Control Plane(두뇌)이 판단하고 Worker Node(팔다리)가 실행하며, Pod·Service·Gateway가 트래픽을 연결하고, PV/PVC가 데이터를 지키고, RBAC/NetworkPolicy가 경계를 지키고, HPA/Prometheus가 부하와 상태를 계속 관찰합니다. 이 그림이 머릿속에 하나로 연결됐다면, 이제 각 조각을 더 깊이 파고들 준비가 된 것입니다.
📋
실무 요약 체크리스트
☐ 모든 워크로드는 컨트롤러(Deployment/StatefulSet/Job) 경유로 생성했는가 ☐ 모든 컨테이너에 resources.requests/limits를 설정했는가 ☐ Readiness/Liveness/Startup Probe를 목적에 맞게 분리했는가 ☐ Secret은 Base64만 믿지 않고 Encryption at Rest·최소권한 RBAC를 함께 적용했는가 ☐ 신규 클러스터라면 Ingress 대신 Gateway API를 기본으로 검토했는가 ☐ NetworkPolicy로 Pod 간 트래픽을 최소 권한으로 제한했는가 ☐ PodDisruptionBudget으로 자발적 중단 시 최소 가용량을 보장했는가 ☐ Prometheus/Grafana로 CPU·메모리뿐 아니라 Pending/CrashLoopBackOff/OOMKilled까지 관측하고 있는가
🚀
기초를 마쳤다면?
HPA/VPA 오토스케일링, RBAC 보안, StatefulSet·DaemonSet·CronJob 워크로드, GPU 기반 LLM 추론 서버 배포, Helm 등 프로덕션 운영에 필요한 내용은 Kubernetes 심화/실무 가이드에서 이어집니다.
다음 단계 학습 주제: • Helm — 매니페스트를 템플릿화하고 버전 관리하는 패키지 매니저 • GitOps (ArgoCD / Flux) — Git 저장소를 원하는 상태의 단일 진실 공급원으로 삼는 배포 자동화 • Service Mesh (Istio / Linkerd) — mTLS, 세밀한 트래픽 제어, 분산 트레이싱을 애플리케이션 코드 변경 없이 추가 • Operator 패턴 — CRD + Controller로 특정 소프트웨어의 운영 지식 자체를 자동화 (Prometheus 가이드의 CRD 섹션 참고) • 관리형 Kubernetes (EKS / AKS / GKE) — Control Plane 운영 부담을 클라우드에 위임 • Observability 심화 — 로그(Loki)·트레이싱(Tempo/Jaeger)까지 메트릭과 통합