본문으로 건너뛰기
AIDevOps
  • Learn
  • Learning Paths
  • Practice
  • Open Source
  • Books
  • Engineering

    AI DevOpsAI 서비스 개발·운영 전체 지도LLMOpsLLM 배포·평가·관측실전 프로젝트AI Agent 프로젝트 실습

    Knowledge

    Docs기술 문서 모음Blog엔지니어링 아티클Plogger개발 기록 피드

    Validate

    Certification3단계 역량 인증 · 준비 중
AI Models
LlamaMistralGemmaDeepSeekQwen
🐳 DevOps
DevOps 입문 & 로드맵LinuxDockerCI/CD|Kubernetes 기본K8s 심화/실무PrometheusGrafana
🤖 AI 실전 개발
AI 실전 입문 & 로드맵Hugging FaceLangChainLlamaIndexLLMOps|LangGraphMCPMulti-AgentAgent Evaluation
🧠 AI Core
AI 입문 & 로드맵ML FundamentalsLLM Fundamentals|Python AIC++|PyTorchTensorFlowJAX
🧠 AI Agent 개발
금융 AI AgentLLM API 서버주식 투자 AgentAIOps AI Agent교육 AI Agent코딩 AI Agent
🌱 Spring Cloud
Spring 입문 & 로드맵Spring Cloud GatewaySpring BootJava|Spring AISpring SecuritySpring BatchSpring JPA
🧱 인프라
인프라 입문 & 로드맵NginxRedis
☁️ 클라우드
클라우드 입문 & 로드맵AWSGCPAzureNCPCloudflare
🎨 Frontend
Frontend 입문 & 로드맵JavaScriptTypeScript|ReactNext.js|VueNuxt
📱 Mobile
Mobile 입문 & 로드맵KotlinAndroidFlutter
⚙️ Backend
Backend 입문 & 로드맵Python 기본FastAPIDjangoFlask|CGoGinNode.js
💾 Database
DB 입문 & 로드맵공통 SQLOracleMySQLPostgreSQL|MongoDB벡터 DB
🧪 검증
k6JMeternGrinder
AIDevOps

Engineering AI. From Code to Production.
AI와 AI Agent를 개발하고 운영하기 위한 엔지니어링 학습 플랫폼

Learn

  • 전체 가이드
  • Learning Paths
  • Practice
  • Books

Resources

  • AI DevOps
  • LLMOps
  • 실전 프로젝트
  • Docs
  • Blog
  • Plogger
  • Open Source
  • Certification (준비 중)

Start Here

  • AI Core 로드맵
  • AI 실전 개발 로드맵
  • Spring Cloud 로드맵
  • DevOps 로드맵
  • 인프라 로드맵

 

  • 클라우드 로드맵
  • Frontend 로드맵
  • Mobile 로드맵
  • Backend 로드맵
  • Database 로드맵
© 2026 AI DevOps Korea. All rights reserved.
이용약관개인정보처리방침Sitemaptestforge.kr
  1. Home
  2. Learn
  3. DevOps
  4. Kubernetes
컨테이너 오케스트레이션 완전 가이드 — 아키텍처부터 운영까지

☸️ Kubernetes 완전 가이드

Visitors

Kubernetes가 왜 필요한지부터 Control Plane·Worker Node·네트워크·스토리지·보안 구조, kubeadm 클러스터 구축, Gateway API·모니터링 구성, 배포 전체 흐름과 실무 비교·체크리스트까지 — 다이어그램과 함께 Kubernetes를 하나의 분산 플랫폼으로 이해하는 완전 가이드입니다.

  • Intermediate · 중급
  • 업데이트 2026.09.19
  • 약 79분 읽기
  • 28개 섹션
  • 예제 코드 34개
  • 웹 IDE 실습 제공
☸️

Kubernetes 웹 IDE

설치 없이 브라우저에서 코드를 실행하고 단계별 예제로 익혀보세요.

웹 IDE 열기 →
클러스터 아키텍처 이해 & 직접 구축Deployment & Service 운영Gateway API·네트워크·스토리지·보안 구조 설계모니터링 스택 구성 & 배포 흐름 정리

관련 프레임워크 & 개발환경

☸️K8s 심화/실무→🐳Docker→🐧Linux→CICI/CD→📊Prometheus→📈Grafana→

목차

0 / 30
  1. 가이드 사용법
  2. 구조 다이어그램
  3. Kubernetes란 무엇인가
  4. 전체 서비스 흐름 한눈에 보기
  5. K8s 전체 아키텍처
  6. Worker Node 상세
  7. 핵심 리소스 개념 & 관계도
  8. 구축 로드맵 & 패키지 원천
  9. 설치 준비
  10. kubeadm 클러스터 구축
  11. Kubernetes 네트워크 구조
  12. Ingress vs Gateway API
  13. CNI & 네트워크 설정
  14. Kubernetes 스토리지 구조
  15. 스토리지 설정
  16. Kubernetes 보안 구조
  17. Gateway API & TLS 설정 (Ingress 후속)
  18. Legacy 환경 — 기존 Ingress
  19. 오토스케일링과 고가용성
  20. 모니터링 스택 구성
  21. Deployment & Service
  22. ConfigMap & Secret
  23. Namespace 전략
  24. 배포 전체 흐름 — 코드에서 서비스까지
  25. 실무 관점 비교 정리
  26. 자주 하는 실수
  27. 결론 & 다음 단계
  28. Kubernetes 설계
  29. 운영 기준
  30. 검증 전략
목차 30개 섹션
  1. 가이드 사용법
  2. 구조 다이어그램
  3. Kubernetes란 무엇인가
  4. 전체 서비스 흐름 한눈에 보기
  5. K8s 전체 아키텍처
  6. Worker Node 상세
  7. 핵심 리소스 개념 & 관계도
  8. 구축 로드맵 & 패키지 원천
  9. 설치 준비
  10. kubeadm 클러스터 구축
  11. Kubernetes 네트워크 구조
  12. Ingress vs Gateway API
  13. CNI & 네트워크 설정
  14. Kubernetes 스토리지 구조
  15. 스토리지 설정
  16. Kubernetes 보안 구조
  17. Gateway API & TLS 설정 (Ingress 후속)
  18. Legacy 환경 — 기존 Ingress
  19. 오토스케일링과 고가용성
  20. 모니터링 스택 구성
  21. Deployment & Service
  22. ConfigMap & Secret
  23. Namespace 전략
  24. 배포 전체 흐름 — 코드에서 서비스까지
  25. 실무 관점 비교 정리
  26. 자주 하는 실수
  27. 결론 & 다음 단계
  28. Kubernetes 설계
  29. 운영 기준
  30. 검증 전략

가이드 사용법

읽는 방향

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)

Tip

  • 🎯 핵심 요약
  • Kubernetes는 컨테이너 "여러 개"를 "여러 서버"에 걸쳐 운영하는 문제를 푸는 오케스트레이션 플랫폼입니다.
  • 핵심은 선언형 관리 — "어떻게"가 아니라 "어떤 상태여야 하는지"만 기술하면 시스템이 그 상태를 계속 유지합니다.
  • Docker가 컨테이너 1개를 잘 실행하는 도구라면, Kubernetes는 그 컨테이너들의 배포·확장·복구·탐색을 자동화하는 도구입니다.

전체 서비스 흐름 한눈에 보기

여기서는 전체 서비스 흐름 한눈에 보기을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

세부 구성요소를 하나씩 배우기 전에, 사용자의 요청 한 건이 실제로 어떤 경로를 거쳐 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 내부에서 벌어지는 일입니다.
다이어그램 렌더링 중…

Tip

  • 🎯 핵심 요약
  • 요청은 브라우저 → LoadBalancer → Envoy(Contour) → Service → Pod 순서로 "밖에서 안으로" 한 단계씩 좁혀 들어갑니다. 각 단계는 다음 단계의 정확한 위치(IP·엔드포인트)를 모르고, 그저 "다음으로 넘겨줄 대상"만 알고 있다는 점이 이 구조의 핵심입니다.
  • Contour(컨트롤 플레인)와 Envoy(데이터 플레인)는 분리되어 있습니다 — 실제 요청은 Envoy만 거치고, Contour는 HTTPRoute/HTTPProxy를 읽어 Envoy의 설정을 실시간으로 갱신하는 역할만 합니다.
  • TLS는 Envoy 단계에서 종료됩니다 — 그 이후 클러스터 내부 트래픽(Envoy→Service→Pod)은 별도 설정이 없다면 평문 HTTP로 흐릅니다.
  • 이 다이어그램의 각 화살표는 이후 섹션(네트워크 구조, Gateway API & TLS 설정)에서 하나씩 구체적인 리소스와 설정으로 다시 다룹니다.

K8s 전체 아키텍처

여기서는 K8s 전체 아키텍처을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Kubernetes 클러스터는 클러스터 전체를 제어하는 Control Plane과 실제 워크로드를 실행하는 Worker Node로 구성됩니다. Control Plane을 사람의 "두뇌"에 비유하면 이해가 쉽습니다 — 두뇌는 판단하고 지시할 뿐 직접 팔다리를 움직이지 않듯, Control Plane도 "어디에 무엇을 배치할지"만 결정하고 실제 컨테이너 실행은 Worker Node가 담당합니다. 이렇게 역할이 분리되어 있기 때문에 Control Plane에 장애가 나도 이미 떠 있는 Pod는 계속 정상 동작합니다(단, 새 배포나 장애 복구 같은 "새로운 판단"은 멈춥니다). 반대로 Worker Node 하나가 죽으면 그 안의 Pod들은 죽지만, Control Plane이 이를 감지해 다른 정상 Node에 다시 배치합니다 — 이 자동 복구가 Kubernetes의 핵심 가치입니다.
다이어그램 렌더링 중…
다이어그램 렌더링 중…
레이어컴포넌트역할
Control Planekube-apiserver모든 kubectl/내부 통신의 진입점, REST API 제공 — 다른 모든 컴포넌트는 반드시 API Server를 거쳐 통신
Control Planeetcd클러스터 전체 상태를 저장하는 분산 Key-Value 스토어 — 사실상 클러스터의 유일한 "진실 공급원"
Control Planekube-scheduler새 Pod를 어느 Worker Node에 배치할지 결정 (자원 여유, 어피니티, 테인트 등을 기준으로)
Control Planekube-controller-managerDeployment/ReplicaSet/Node 등 각 리소스의 목표 상태를 계속 현재 상태와 비교해 맞추는 루프
Control Planecloud-controller-managerAWS/GCP/NCP 등 클라우드 API 연동 (LoadBalancer 프로비저닝, Volume 연결 등)
Worker NodekubeletAPI Server 지시에 따라 Pod 생성·삭제·헬스체크 수행 — Node에서 유일하게 API Server와 직접 통신하는 에이전트
Worker Nodekube-proxyService ClusterIP → Pod IP 네트워크 규칙 관리 (iptables/ipvs)
Worker Nodecontainerd컨테이너 런타임 — 이미지 pull, 컨테이너 실행
Worker NodeCNI PluginPod 간 네트워크 연결 (Calico/Flannel/Cilium)
Add-onCoreDNSService/Pod DNS 이름 해석
Add-onMetrics ServerHPA/VPA를 위한 CPU·메모리 메트릭 수집
Add-onGateway Controller (구 Ingress Controller)외부 트래픽을 Service로 라우팅하는 L7 프록시 — Ingress는 Legacy, 신규는 Gateway API

Tip

  • 🎯 핵심 요약
  • Control Plane(두뇌)은 "무엇을 어디에 배치할지" 판단만 하고, 실제 컨테이너 실행은 Worker Node의 kubelet이 담당합니다.
  • kubectl apply의 실체는 딱 하나 — API Server를 거쳐 etcd에 "원하는 상태"를 저장하는 것뿐입니다. 실제 배치·실행은 Scheduler와 kubelet이 그 상태를 watch하다가 뒤늦게 수행합니다.
  • Control Plane 장애 중에도 이미 떠 있는 Pod는 그대로 동작하지만, 새 배포·스케일링·장애 자동복구 같은 "새로운 결정"은 멈춥니다 — 그래서 프로덕션에서는 Control Plane도 다중화(HA)합니다.

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)아파트 건물
PodNode 위에서 함께 배치되는 컨테이너 1개 이상의 묶음. 네트워크/볼륨 공유같은 집(호실)에 사는 룸메이트들
ContainerPod 안에서 실제 프로세스가 실행되는 단위 (이미지 1개 = 컨테이너 1개)방 안의 사람 한 명

Tip

  • 🎯 핵심 요약
  • kubelet은 API Server의 지시를 받아 실제로 컨테이너를 실행·감시하는, Node 위의 유일한 "현장 담당자"입니다.
  • Pod가 최소 배포 단위인 이유는 Kubernetes의 모든 제어(스케줄링·헬스체크·재시작·네트워킹)가 Pod 단위로 이뤄지기 때문입니다.
  • Node 장애 시 컨트롤러(Deployment 등)가 관리하는 Pod는 다른 Node로 자동 재스케줄링되지만, 컨트롤러 없이 만든 Pod는 그대로 유실됩니다.

핵심 리소스 개념 & 관계도

여기서는 핵심 리소스 개념 & 관계도을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

K8s를 처음 접할 때 가장 자주 쓰는 리소스들의 역할과 관계를 정리합니다. 리소스는 서로 독립적이지 않고 Label/Selector라는 느슨한 연결 고리로 이어져 있습니다 — Service는 특정 label을 가진 Pod 집합을 찾아 트래픽을 보내고, Deployment는 자신이 만든 ReplicaSet을 label로 추적합니다. 이 "이름이 아닌 라벨로 연결한다"는 원칙이 Kubernetes 리소스 모델 전체를 관통합니다.
다이어그램 렌더링 중…
리소스설명주요 용도
Pod가장 작은 배포 단위, 1개 이상 컨테이너 묶음실제 앱 실행 단위
ReplicaSet지정한 label과 일치하는 Pod 개수를 항상 N개로 유지Deployment가 내부적으로 자동 생성·관리 — 직접 만들 일은 거의 없음
DeploymentReplicaSet을 감싸 롤링 업데이트 · 롤백 이력까지 관리Stateless 앱의 기본 컨트롤러
StatefulSetPod 순서·고정 네트워크 ID·독립 스토리지 보장DB, 메시지 브로커 등 상태 저장 워크로드
DaemonSet모든(또는 조건에 맞는) 노드에 Pod 1개씩 배포로그 수집기, 모니터링 에이전트, CNI 플러그인
Job완료(Completed)가 있는 일회성 작업 — 성공할 때까지 재시도DB 마이그레이션, 배치 처리
CronJobJob을 cron 스케줄대로 주기적으로 생성야간 리포트, 정기 백업
Namespace리소스를 논리적으로 격리하는 가상 클러스터 경계환경(dev/staging/prod)·팀별 분리
Label / Selector리소스에 붙이는 key-value 태그와 그것을 찾는 질의 조건Service·ReplicaSet 등이 대상 Pod를 찾는 유일한 방법
AnnotationLabel과 달리 질의에 쓰이지 않는 부가 메타데이터빌드 정보, 도구 설정값(예: cert-manager annotation)
ServiceLabel Selector로 찾은 Pod 집합에 안정적 엔드포인트 제공ClusterIP/NodePort/LoadBalancer/Headless
Gateway APIGatewayClass/Gateway/HTTPRoute 3계층으로 Ingress를 대체하는 K8s 공식 표준신규 클러스터의 기본 L7 라우팅 방식 (Ingress는 Legacy)
ConfigMap비민감 환경설정 데이터앱 config, 스크립트
SecretBase64로 인코딩되어 저장 (암호화 아님 — 상세는 ConfigMap & Secret 섹션 참고)비밀번호, 인증서, 토큰
ServiceAccountPod 내부 프로세스가 API Server를 호출할 때 쓰는 신원(identity)Pod ↔ RBAC를 연결하는 다리 역할
PersistentVolume / Claim스토리지 리소스 추상화 / Pod이 PV를 요청하는 명세데이터 영속성이 필요한 워크로드
HPACPU/메모리 등 지표 기반 Pod 수 자동 조절트래픽 변동 대응
RBAC (Role/RoleBinding 등)ServiceAccount·사용자별 API 접근 권한 제어팀별·워크로드별 최소 권한 분리
ResourceQuota / LimitRangeNamespace 단위 자원 총량 상한 / 컨테이너 기본·최대값멀티테넌시 환경의 자원 남용 방지

Tip

  • 🎯 핵심 요약
  • ReplicaSet은 보통 직접 만들지 않습니다 — Deployment가 롤링 업데이트를 위해 내부적으로 생성·교체하는 하위 오브젝트입니다.
  • Job은 "성공할 때까지 실행"에, CronJob은 "그 Job을 스케줄대로 반복 생성"하는 데 초점이 있습니다 — 둘은 상하 관계이지 대체 관계가 아닙니다.
  • Label은 Selector로 찾아 연결하는 데 쓰이고(Service가 Pod를 찾는 유일한 방법), Annotation은 검색에 쓰이지 않는 부가 정보라는 차이를 기억하세요.

클러스터 구축 로드맵 & 패키지 원천

클러스터 구축 로드맵 & 패키지 원천은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

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)
7단계. 영구 볼륨 (CSI)Stateless/Stateful Pod의 스토리지 영구 저장을 위한 CSI 연동Cloud Vendor CSI (AWS, GCP, NCP) 혹은 오픈소스 (NFS, Longhorn, Ceph)
8단계. 애플리케이션 운영Deployment, StatefulSet, HPA, ConfigMap/Secret 관리 및 롤링 업데이트자체 컨테이너 레지스트리 (AWS ECR, Docker Hub, Github Packages 등)
9단계. 모니터링 & 장애 조치Prometheus, Grafana를 통한 메트릭 수집 및 Day-2 롤백, 백업/복구 운영Prometheus-Community Helm 차트 및 업스트림 공식 GitHub 저장소

Tip

  • 과거에 사용되던 Google 호스팅 레거시 레포지토리(apt.kubernetes.io 및 yum.kubernetes.io)는 완전히 지원 중단(Deprecated)되었습니다. 따라서 반드시 커뮤니티 관리형인 pkgs.k8s.io 저장소를 사용해야 정상적인 패키지 설치가 가능합니다.
  • Control Plane 초기화 시 필요한 시스템 컨테이너 이미지(apiserver, controller-manager, scheduler 등)는 registry.k8s.io에서 직접 풀(Pull)해 오며, 폐쇄망 구축 시에는 `kubeadm config images list` 명령어로 이미지 목록을 추출하여 사설 레지스트리에 사전 업로드해야 합니다.
  • 운영 단계(Day-2 Operations)에서는 구성 요소의 안정적인 버전 업그레이드를 위해 apt-mark/yum versionlock 등을 활용하여 원치 않는 자동 업그레이드를 제약하는 것이 상용 클러스터 관리의 핵심입니다.

설치 준비 — 하드웨어 · OS · 네트워크

여기서는 설치 준비 — 하드웨어 · OS · 네트워크을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

kubeadm으로 온프레미스 K8s 클러스터를 구축하기 전 체크리스트입니다. 최소 1 Control Plane + 2 Worker Node 구성을 권장합니다.
00-all-nodes.shBASH
#!/bin/bash
# 모든 노드에서 실행 (Control Plane + Worker 공통)

# 1. 호스트명 설정 (각 노드마다 다르게)
hostnamectl set-hostname k8s-master-01   # Worker: k8s-worker-01, k8s-worker-02

# 2. /etc/hosts 에 모든 노드 등록
cat >> /etc/hosts << 'EOF'
192.168.1.10  k8s-master-01
192.168.1.11  k8s-worker-01
192.168.1.12  k8s-worker-02
EOF

# 3. swap 비활성화 (필수 — kubelet 요구사항)
swapoff -a
sed -i '/swap/d' /etc/fstab

# 3-1. SELinux 비활성화 (RHEL/Rocky Linux 계열 필수)
if [ -f /etc/selinux/config ]; then
  setenforce 0
  sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
fi

# 4. 커널 모듈 로드
cat > /etc/modules-load.d/k8s.conf << 'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter

# 5. sysctl 네트워크 파라미터
cat > /etc/sysctl.d/k8s.conf << 'EOF'
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sysctl --system

# 6. 방화벽 포트 개방

# [Debian/Ubuntu 계열 - ufw 사용 시]
# Control Plane:
# ufw allow 6443/tcp    # API Server
# ufw allow 2379:2380/tcp # etcd
# ufw allow 10250/tcp   # kubelet API
# ufw allow 10259/tcp   # kube-scheduler
# ufw allow 10257/tcp   # kube-controller-manager
# Worker Node:
# ufw allow 10250/tcp   # kubelet API
# ufw allow 30000:32767/tcp # NodePort range
# ufw allow from 192.168.1.0/24 # 내부망 전체 허용

# [RHEL/Rocky Linux 계열 - firewalld 사용 시]
# Control Plane:
# firewall-cmd --permanent --add-port=6443/tcp
# firewall-cmd --permanent --add-port=2379-2380/tcp
# firewall-cmd --permanent --add-port=10250/tcp
# firewall-cmd --permanent --add-port=10259/tcp
# firewall-cmd --permanent --add-port=10257/tcp
# Worker Node:
# firewall-cmd --permanent --add-port=10250/tcp
# firewall-cmd --permanent --add-port=30000-32767/tcp
# firewall-cmd --reload
01-containerd.shBASH
#!/bin/bash
# containerd 설치 (모든 노드 - Linux 배포판별 선택 실행)

# ── 1. Debian/Ubuntu 계열 (APT) ──────────────────
if command -v apt-get &> /dev/null; then
  apt-get update
  apt-get install -y ca-certificates curl gnupg
  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg |     gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  chmod a+r /etc/apt/keyrings/docker.gpg

  echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg]   https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" |     tee /etc/apt/sources.list.d/docker.list

  apt-get update
  apt-get install -y containerd.io
fi

# ── 2. RHEL/Rocky Linux 계열 (DNF) ───────────────
if command -v dnf &> /dev/null && ! command -v apt-get &> /dev/null; then
  dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  dnf install -y containerd.io
fi

# ── 3. containerd 기본 설정 생성 및 SystemdCgroup 활성화 ──
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# SystemdCgroup 활성화 (kubelet의 cgroup driver와 일치화)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

systemctl restart containerd
systemctl enable containerd
systemctl status containerd
02-kubeadm.shBASH
#!/bin/bash
# kubeadm, kubelet, kubectl 설치 (모든 노드 - Linux 배포판별 선택 실행)

# ── 1. Debian/Ubuntu 계열 (APT) ──────────────────
if command -v apt-get &> /dev/null; then
  apt-get update
  apt-get install -y apt-transport-https ca-certificates curl gpg

  curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key |     gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

  echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg]   https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /" |     tee /etc/apt/sources.list.d/kubernetes.list

  apt-get update
  apt-get install -y kubelet kubeadm kubectl
  apt-mark hold kubelet kubeadm kubectl   # 자동 업그레이드 방지
fi

# ── 2. RHEL/Rocky Linux 계열 (DNF) ───────────────
if command -v dnf &> /dev/null && ! command -v apt-get &> /dev/null; then
  cat <<EOF | tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF

  dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
fi

# ── 3. 서비스 활성화 ──────────────────────────────
systemctl enable --now kubelet

# 버전 확인
kubeadm version
kubectl version --client
항목최소 사양권장 사양 (프로덕션)
OSLinux (Debian계열: Ubuntu 22.04+ / RHEL계열: Rocky Linux 9+)Linux Enterprise (RHEL / Ubuntu FIPS)
Control Plane CPU2 vCPU4 vCPU (HA: 3노드 × 4 vCPU)
Control Plane RAM2 GB8 GB
Worker Node CPU2 vCPU8+ vCPU (워크로드 유형에 따라)
Worker Node RAM4 GB16 GB+
디스크 (Control Plane)20 GB100 GB SSD (etcd WAL 성능)
디스크 (Worker)40 GB200 GB+ SSD
네트워크1 Gbps10 Gbps (노드 간)
Pod CIDR10.244.0.0/16 (Flannel)10.244.0.0/16 or 192.168.0.0/16 (Calico)
Service CIDR10.96.0.0/1210.96.0.0/12 (변경 불가 — 설치 전 확정)

kubeadm으로 클러스터 구축

여기서는 kubeadm으로 클러스터 구축을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Control Plane 초기화 → Worker 노드 조인 → kubeconfig 설정 순서로 진행합니다. 모든 명령은 Linux (Ubuntu 22.04 / Rocky Linux 9) 및 K8s v1.36 기준입니다.
03-init-control-plane.shBASH
#!/bin/bash
# Control Plane 노드에서만 실행

# kubeadm init — Pod CIDR은 CNI 선택에 맞춰야 함
# Calico: 192.168.0.0/16 / Flannel: 10.244.0.0/16
kubeadm init \
  --control-plane-endpoint "k8s-master-01:6443" \
  --pod-network-cidr=192.168.0.0/16 \
  --service-cidr=10.96.0.0/12 \
  --kubernetes-version=v1.36.0 \
  --cri-socket unix:///run/containerd/containerd.sock \
  2>&1 | tee /root/kubeadm-init.log

# 성공 시 출력 예:
# Your Kubernetes control-plane has initialized successfully!
# kubeadm join k8s-master-01:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

# kubectl 설정 (일반 사용자)
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

# 클러스터 상태 확인 (CNI 설치 전이라 NotReady)
kubectl get nodes
kubectl get pods -n kube-system
04-worker-join.shBASH
#!/bin/bash
# Worker 노드에서 실행
# kubeadm init 출력에서 복사한 join 명령 실행

kubeadm join k8s-master-01:6443 \
  --token <your-token> \
  --discovery-token-ca-cert-hash sha256:<your-hash>

# ── 토큰 만료(24h) 후 재발급 방법 (Control Plane에서) ────────
# kubeadm token create --print-join-command
05-verify-cluster.shBASH
# 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-info
kubectl 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 없는 온프레미스 임시 노출
LoadBalancercloud-controller-manager가 클라우드 LB를 자동 프로비저닝클라우드 환경의 표준 외부 노출 방식
Headless (clusterIP: None)가상 IP를 발급하지 않고 DNS 조회 시 Pod IP 목록을 그대로 반환StatefulSet처럼 각 Pod에 개별 접근이 필요한 경우

Tip

  • 🎯 핵심 요약
  • Pod IP는 재생성될 때마다 바뀌는 임시 주소이므로, 클라이언트는 절대 Pod IP에 직접 의존하지 말고 항상 Service를 거쳐야 합니다.
  • kube-proxy는 각 Node에서 Service ClusterIP → 실제 Pod IP로 트래픽을 전달하는 규칙(iptables/ipvs)을 관리하고, CoreDNS는 그 Service 이름을 ClusterIP로 변환해줍니다.
  • Ingress는 리소스 명세일 뿐 그 자체로는 아무 일도 하지 않습니다 — 실제 라우팅은 별도로 설치하는 Ingress Controller(또는 Gateway Controller)가 수행합니다.

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애플리케이션 팀 (각자의 네임스페이스에서)자신의 서비스로 가는 경로·헤더 라우팅 규칙 정의

Tip

  • 🎯 핵심 요약
  • 입문자 입장에서는 "Ingress 한 장에 다 적던 것을, Gateway(인프라 설정)와 HTTPRoute(앱 라우팅)로 나눠 적는다"고 이해하면 충분합니다.
  • Gateway API는 벤더 종속적인 annotation 대신 Kubernetes SIG-Network 공식 표준 필드를 쓰므로, 컨트롤러를 바꿔도 HTTPRoute 대부분은 그대로 재사용됩니다.
  • 실제 설치·마이그레이션 절차(ingress2gateway 포함)는 아래 "Gateway API & TLS 설정" 섹션에서 다룹니다.

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.yaml

cat > calico-custom.yaml << 'EOF'
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    ipPools:
    - blockSize: 26
      cidr: 192.168.0.0/16
      encapsulation: VXLANCrossSubnet   # 멀티 서브넷 환경
      natOutgoing: Enabled
      nodeSelector: all()
  nodeMetricsPort: 9091
---
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
  name: default
spec: {}
EOF
kubectl 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/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}          # 모든 Pod에 적용
  policyTypes:
  - Ingress
---
# API 서버만 DB 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
  namespace: production
spec:
  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
CNIPod CIDR네트워크 정책특이사항
Calico192.168.0.0/16O (완전 지원)BGP 기반, 대규모 클러스터 적합, IPAM 내장
Flannel10.244.0.0/16X (별도 설치 필요)설정 단순, 소규모 클러스터
Cilium자유O + L7 정책eBPF 기반, 고성능, Hubble 관측성 내장
WeaveNet10.32.0.0/12O암호화 지원, 소규모 환경

Kubernetes 스토리지 구조

여기서는 Kubernetes 스토리지 구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

컨테이너는 기본적으로 상태 비저장(Stateless)입니다 — 컨테이너 내부에 쓴 파일은 컨테이너(또는 그것을 감싼 Pod)가 재생성되는 순간 함께 사라집니다. Pod는 배포·스케일링·장애 복구 과정에서 수시로 삭제되고 다시 만들어지는 존재이므로, DB나 업로드 파일처럼 반드시 남아야 하는 데이터를 컨테이너 파일시스템에만 의존해 두면 안 됩니다. 이 문제를 해결하기 위해 Kubernetes는 스토리지를 컨테이너 생명주기와 분리된 별도 오브젝트로 다룹니다.

PersistentVolume(PV)은 실제 스토리지(NFS, 클라우드 디스크, 로컬 디스크 등)를 클러스터 오브젝트로 추상화한 것이고, PersistentVolumeClaim(PVC)은 애플리케이션(Pod)이 "이만큼의 용량과 이런 접근 모드가 필요하다"고 요청하는 명세입니다. StorageClass는 PV를 미리 만들어두지 않고, PVC가 생성되는 순간 해당 백엔드에 맞는 PV를 자동으로 만들어주는 동적 프로비저닝(Dynamic Provisioning) 템플릿입니다 — 실무에서는 관리자가 PV를 일일이 손으로 만드는 정적 프로비저닝보다 이 방식을 압도적으로 많이 씁니다.
다이어그램 렌더링 중…
백엔드특징적합한 상황
Local PathNode의 로컬 디스크를 직접 사용 — Node가 바뀌면 데이터 접근 불가개발 환경, 단일 노드 테스트
NFS여러 Node에서 동시에 같은 볼륨을 공유 마운트 가능(ReadWriteMany)온프레미스, 여러 Pod가 파일을 공유해야 하는 경우
클라우드 디스크 (EBS/PD/Managed Disk 등)Node가 바뀌어도 자동으로 재연결(ReadWriteOnce)클라우드 환경의 DB, 단일 Pod 전용 볼륨

Tip

  • 🎯 핵심 요약
  • 컨테이너/Pod는 언제든 사라질 수 있는 존재이므로, 반드시 보존해야 하는 데이터는 PV/PVC를 통해 Pod 생명주기 밖에 둬야 합니다.
  • StorageClass를 통한 동적 프로비저닝이 실무 기본값입니다 — PVC만 만들면 PV가 자동으로 뒤따라옵니다.
  • StatefulSet은 각 Pod마다 고유한 PVC를 자동으로 만들어 붙여주므로, Pod가 재생성되어도 "자기 자신의" 데이터를 계속 유지합니다 — 이것이 Deployment 대신 StatefulSet을 쓰는 핵심 이유 중 하나입니다.

스토리지 설정 — 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
nfs-provisioner.shBASH
# NFS 서버 설치 (스토리지 노드에서)
apt-get install -y nfs-kernel-server
mkdir -p /data/nfs
chmod 777 /data/nfs
echo "/data/nfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
exportfs -ra
systemctl enable --now nfs-server

# NFS 클라이언트 설치 (모든 Worker 노드)
apt-get install -y nfs-common

# NFS 서브디렉터리 Provisioner (Helm)
helm repo add nfs-subdir-external-provisioner \
  https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update

helm install nfs-provisioner \
  nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --namespace kube-system \
  --set nfs.server=192.168.1.50 \
  --set nfs.path=/data/nfs \
  --set storageClass.name=nfs-client \
  --set storageClass.defaultClass=true \
  --set storageClass.reclaimPolicy=Retain

kubectl get storageclass
pv-pvc-test.yamlYAML
# PV/PVC 수동 프로비저닝 예시 (StorageClass 없을 때)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-postgres-01
spec:
  capacity:
    storage: 50Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: fast-ssd
  local:
    path: /data/postgres
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-worker-01
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: database
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 50Gi

Kubernetes 보안 구조

여기서는 Kubernetes 보안 구조을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

API Server로 들어오는 모든 요청은 세 단계를 순서대로 통과해야 합니다. 먼저 인증(Authentication) — "너는 누구냐"를 확인합니다(사용자는 클라이언트 인증서나 OIDC 토큰, Pod는 ServiceAccount 토큰). 다음은 인가(Authorization) — "너는 이 작업을 할 권한이 있느냐"를 확인합니다. Kubernetes 기본 인가 방식이 바로 RBAC입니다. 마지막으로 Admission Control이 "이 요청이 정책에 맞느냐"를 한 번 더 검증(예: privileged 컨테이너 금지, 이미지 출처 제한)한 뒤에야 etcd에 실제로 반영됩니다. 이 3단계 중 하나라도 실패하면 요청은 즉시 거부됩니다.
다이어그램 렌더링 중…
보안 구성요소역할실무 포인트
RBAC (Role/RoleBinding, ClusterRole/ClusterRoleBinding)Namespace 범위(Role) 또는 클러스터 전체(ClusterRole) 권한을 사용자·ServiceAccount에 부여최소 권한 원칙 — 상세 예제는 K8s 심화 가이드 RBAC 섹션 참고
ServiceAccountPod 내부 프로세스가 API Server를 호출할 때 쓰는 신원쓰지 않는 Pod는 automountServiceAccountToken: false로 토큰 자동 마운트 차단 권장
NetworkPolicyPod 간 트래픽을 화이트리스트 방식으로 제한 (기본은 전체 허용)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) 사용

Tip

  • 🎯 핵심 요약
  • 모든 API 요청은 인증 → 인가(RBAC) → Admission Control 3단계를 거칩니다. RBAC만으로 보안이 끝난다고 생각하면 안 됩니다.
  • Namespace는 논리적 경계일 뿐 그 자체로 강한 격리를 보장하지 않습니다 — 실질적인 멀티테넌시 격리는 Namespace + RBAC + ResourceQuota + NetworkPolicy를 함께 써야 완성됩니다.
  • 기본 상태의 Kubernetes는 Pod 간 트래픽을 전부 허용합니다 — 최소 권한 네트워크가 필요하면 NetworkPolicy를 반드시 명시적으로 적용해야 합니다.

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 channel
kubectl 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.yaml
kubectl get gatewayclass contour

# 팀별 위임(HTTPProxy)·정책 설정 등 심화 내용은 K8s 심화 가이드의
# "Contour & Gateway API" 섹션(/learn/kubernetes-advanced/#contour-gateway)에서 이어집니다.
02-gatewayclass-gateway.yamlYAML
# GatewayClass는 위 Provisioner 매니페스트 적용 시 자동 생성됨 (참고용)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: contour
spec:
  controllerName: projectcontour.io/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: myapp-gateway
  namespace: projectcontour
spec:
  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
03-httproute-app.yamlYAML
# 기존 Ingress 하나에 몰려있던 path 라우팅을, 애플리케이션 팀이 자신의 네임스페이스에서 직접 관리
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: myapp-route
  namespace: production
spec:
  parentRefs:
  - name: myapp-gateway
    namespace: projectcontour
  hostnames:
  - "api.aidevops.kr"
  rules:
  - matches:
    - path: { type: PathPrefix, value: /api/v1 }
    backendRefs:
    - name: api-service
      port: 8080
  - matches:
    - path: { type: PathPrefix, value: / }
    backendRefs:
    - name: frontend-service
      port: 3000
03b-httpproxy-app.yamlYAML
# 바로 위 HTTPRoute와 동일한 라우팅을, Contour 전용 CRD인 HTTPProxy로 표현한 버전
# Gateway API 표준을 아직 안 쓰는 클러스터거나, includes/rateLimitPolicy 같은
# Contour만의 세밀한 기능이 필요할 때 이 방식을 선택합니다.
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: myapp-proxy
  namespace: production
spec:
  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
04-clusterissuer-gateway.yamlYAML
# cert-manager는 v1.15+부터 Gateway API HTTPRoute를 challenge solver로 직접 지원
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@aidevops.kr
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        gatewayHTTPRoute:
          parentRefs:
          - name: myapp-gateway
            namespace: projectcontour
            kind: Gateway
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-aidevops-kr
  namespace: projectcontour
spec:
  secretName: api-aidevops-kr-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - api.aidevops.kr
환경진입점 리소스트래픽 컨트롤러상태
신규 (권장)GatewayClass → Gateway → HTTPRouteGateway Controller (Contour, Envoy Gateway, Cilium 등)K8s 공식 표준, 활발히 개발 중
LegacyIngressClass → Ingress기존 Ingress Controller (예: ingress-nginx)ingress-nginx는 2026-03-24 EOL — 신규 보안 패치 없음

Tip

LB 뒤에서 Envoy 로그·애플리케이션에 실제 클라이언트 IP가 찍히지 않는다면 Proxy Protocol이 필요할 수 있습니다. LB별 설정, Contour 버전별(1.28~1.33) Envoy 설정, PROXY 헤더 없는 내부 호출 대응은 <a href="/learn/kubernetes-advanced/#proxy-protocol">Kubernetes 심화/실무 — Proxy Protocol</a>에서 이어집니다.

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
legacy-nginx-ingress.shBASH
# ── Legacy 참고용 — 신규 구축에는 권장하지 않음 (ingress-nginx는 2026-03-24 EOL) ──
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.service.type=NodePort \
  --set controller.service.nodePorts.http=30080 \
  --set controller.service.nodePorts.https=30443

kubectl get pods -n ingress-nginx -w
legacy-ingress-app.yamlYAML
# Legacy Ingress 리소스 — ingress2gateway로 위와 같은 HTTPRoute로 변환 가능
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.aidevops.kr
    secretName: api-aidevops-kr-tls
  rules:
  - host: api.aidevops.kr
    http:
      paths:
      - path: /api/v1
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 3000

오토스케일링과 고가용성

오토스케일링과 고가용성은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

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으로 계속 재시작되는 문제의 해법

Tip

  • 🎯 핵심 요약
  • Self-healing은 "원하는 상태(desired state)"와 "현재 상태"를 끊임없이 비교하는 컨트롤러 루프의 결과이지, 특별한 마법이 아닙니다.
  • HPA는 Pod 개수(가로), VPA는 Pod 하나의 자원 할당량(세로)을 조정합니다 — 두 목적이 다르므로 같은 리소스에 동시에 켜면 충돌할 수 있습니다.
  • 고가용성(HA)을 위해 프로덕션에서는 Control Plane도 3대 이상 홀수로 다중화합니다 — etcd가 과반수(quorum) 합의로 동작하기 때문에, 짝수 대수는 오히려 장애 내성에 도움이 되지 않습니다.

모니터링 스택 구성 — 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 레이어에서만 보이는 장애 신호입니다.
prometheus-stack.shBASH
# kube-prometheus-stack 설치
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

cat > monitoring-values.yaml << 'EOF'
grafana:
  adminPassword: "ChangeMe!2024"
  persistence:
    enabled: true
    size: 10Gi
  ingress:
    enabled: true
    ingressClassName: nginx
    annotations:
      cert-manager.io/cluster-issuer: letsencrypt-prod
    hosts:
    - grafana.aidevops.kr
    tls:
    - secretName: grafana-tls
      hosts:
      - grafana.aidevops.kr

prometheus:
  prometheusSpec:
    retention: 30d
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: nfs-client
          resources:
            requests:
              storage: 50Gi
    serviceMonitorSelectorNilUsesHelmValues: false  # 모든 ServiceMonitor 수집

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: nfs-client
          resources:
            requests:
              storage: 5Gi

nodeExporter:
  enabled: true

kubeStateMetrics:
  enabled: true
EOF

helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  -f monitoring-values.yaml

kubectl get pods -n monitoring -w
servicemonitor.yamlYAML
# 애플리케이션 메트릭 수집 — ServiceMonitor
# Spring Boot Actuator /actuator/prometheus 엔드포인트 스크래핑
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: springboot-api
  namespace: production
  labels:
    release: kube-prometheus-stack  # Helm 릴리스 이름과 일치
spec:
  selector:
    matchLabels:
      app: springboot-api
  endpoints:
  - port: http
    path: /actuator/prometheus
    interval: 30s
  namespaceSelector:
    matchNames:
    - production
alert-rules.yamlYAML
# Prometheus Alert 규칙 예시
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: k8s-alerts
  namespace: monitoring
  labels:
    release: kube-prometheus-stack
spec:
  groups:
  - name: k8s.rules
    rules:
    - alert: NodeHighCPU
      expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "노드 CPU 사용률 85% 초과 — {{ $labels.instance }}"

    - alert: PodCrashLooping
      expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 15 > 5
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} CrashLoopBackOff"

    - alert: PVCUsageHigh
      expr: kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes * 100 > 80
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "PVC {{ $labels.persistentvolumeclaim }} 사용률 80% 초과"
grafana-dashboards.shBASH
# 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
운영 신호의미흔한 원인
PendingPod가 아직 어느 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이 앱 응답 속도보다 짧게 설정된 경우가 흔함

Tip

  • 🎯 핵심 요약
  • 메트릭·로그·트레이싱은 서로 다른 질문에 답합니다 — "얼마나(메트릭)", "무엇이(로그)", "어디서(트레이싱)" 를 구분해 도구를 고릅니다.
  • CPU/메모리 그래프만으로는 K8s 특유의 장애(Pending/CrashLoopBackOff/OOMKilled)를 놓치기 쉬우므로, kube-state-metrics 기반 Pod 상태 지표를 반드시 함께 대시보드에 올려야 합니다.

Deployment & Service

여기서는 Deployment & Service을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Deployment는 지정한 replicas 수만큼 Pod를 항상 유지하도록 관리하며, Pod가 죽으면 자동으로 새 Pod를 만들어 대체합니다. Service는 이렇게 계속 바뀌는 Pod들의 IP를 신경 쓸 필요 없이, selector로 지정한 라벨을 가진 Pod 그룹에 고정된 하나의 접속 지점을 제공합니다.
app.yamlYAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:1.0.0
        ports:
        - containerPort: 3000
        resources:
          requests: { cpu: 100m, memory: 128Mi }
          limits:   { cpu: 500m, memory: 512Mi }
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 3000

Tip

selector의 라벨이 template.metadata.labels와 정확히 일치해야 Service가 Pod를 찾을 수 있습니다 — 라벨 오타는 "Service는 떠 있는데 트래픽이 전달되지 않는" 흔한 원인입니다.

ConfigMap & Secret

여기서는 ConfigMap & Secret을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Secret은 ConfigMap과 API 구조는 거의 같지만 값이 Base64로 인코딩된다는 차이만 있습니다. Base64는 인코딩이지 암호화가 아니므로 kubectl get secret -o jsonpath 한 줄이면 누구나 원문을 복원할 수 있고, 기본 설정의 kubeadm 클러스터는 이 값을 etcd에도 평문 그대로 저장합니다. Secret이라는 이름만 보고 안전하다고 오해하지 말고, 아래 4가지 계층을 함께 갖춰야 실제로 보호됩니다.
01-basic-usage.shBASH
# ConfigMap 생성 (비민감 데이터)
kubectl create configmap myapp-config \
  --from-literal=DB_HOST=postgres \
  --from-literal=APP_ENV=production

# Secret 생성
kubectl create secret generic myapp-secret \
  --from-literal=DB_PASSWORD=s3cr3t

# 파드에서 환경변수로 사용
envFrom:
- configMapRef:
    name: myapp-config
- secretRef:
    name: myapp-secret
02-base64-is-not-encryption.shBASH
# 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/v1
kind: EncryptionConfiguration
resources:
  - 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 -
04-rbac-secret-least-privilege.yamlYAML
# Secret 접근은 리소스명 단위로 최소 권한만 부여 — 네임스페이스 전체 secrets read는 지양
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: myapp-secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["myapp-secret"]   # 이 Secret 하나만 허용 — 네임스페이스의 다른 Secret은 차단
  verbs: ["get"]                    # list/watch는 부여하지 않음 (전체 목록·변경 감시 차단)
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: myapp-secret-reader-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: myapp
  namespace: production
roleRef:
  kind: Role
  name: myapp-secret-reader
  apiGroup: rbac.authorization.k8s.io
05-external-secrets-operator.yamlYAML
# External Secrets Operator — 원문은 Vault/AWS Secrets Manager 등 외부에만 존재,
# K8s Secret은 동기화된 "사본"으로만 취급해 Git에는 원문이 전혀 올라가지 않음
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-manager
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: myapp   # IRSA 등으로 AWS IAM과 연결된 ServiceAccount
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: myapp-secret
  namespace: production
spec:
  refreshInterval: 1h              # 주기적으로 재동기화 — 회전(rotation) 자동 반영
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
  target:
    name: myapp-secret             # 이 이름으로 일반 K8s Secret이 자동 생성됨
  data:
  - secretKey: DB_PASSWORD
    remoteRef:
      key: prod/myapp/db-password
보안 계층기본 상태권장 조치
Base64 인코딩누구나 디코딩 가능 — 암호화 아님이것만으로 보호된다고 가정하지 않기
etcd 저장기본값은 평문(unencrypted) 저장EncryptionConfiguration으로 etcd Encryption at Rest 활성화
API 접근 제어RBAC 미설정 시 과도한 범위의 ServiceAccount가 조회 가능Role에 secrets get/list를 리소스명 단위로 최소 부여, watch/list는 특히 신중히
형상관리(Git)Secret YAML을 그대로 커밋하면 사실상 평문 노출Sealed Secrets / SOPS로 암호화한 상태로만 커밋, 원문은 Git에 두지 않기
비밀 관리 체계K8s Secret 자체를 진실 공급원으로 직접 운영External Secrets Operator/Vault 등 외부 저장소에서 동기화해 회전(rotation)까지 관리

Namespace 전략 & 리소스 쿼터

여기서는 Namespace 전략 & 리소스 쿼터을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Namespace로 환경(dev/staging/production), 팀, 서비스를 격리합니다. ResourceQuota와 LimitRange로 팀별 자원 상한을 설정해 단일 팀의 과도한 자원 점유를 방지합니다.
namespace-setup.shBASH
# 환경별 Namespace 생성
kubectl create namespace development
kubectl create namespace staging
kubectl create namespace production
kubectl create namespace monitoring
kubectl create namespace database
kubectl create namespace infra

# 레이블 부여 (NetworkPolicy, ResourceQuota 셀렉터)
kubectl label namespace production  env=prod  team=backend
kubectl label namespace development env=dev   team=backend
kubectl label namespace monitoring  env=infra team=ops

# Namespace 목록
kubectl get namespaces --show-labels
resource-quota.yamlYAML
# production Namespace ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    # 컴퓨팅
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    # 오브젝트 수
    count/pods: "100"
    count/services: "20"
    count/deployments.apps: "20"
    count/statefulsets.apps: "10"
    # 스토리지
    requests.storage: 500Gi
    persistentvolumeclaims: "30"
---
# development Namespace (소규모)
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: development
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    count/pods: "20"
    persistentvolumeclaims: "10"
limit-range.yamlYAML
# LimitRange — 컨테이너 기본값 & 최대값 설정
# requests 미지정 시 default 값이 자동 적용
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
  - type: Container
    default:          # requests 미지정 시 기본값
      cpu: 200m
      memory: 256Mi
    defaultRequest:
      cpu: 100m
      memory: 128Mi
    max:              # 컨테이너 최대 허용
      cpu: "4"
      memory: 4Gi
    min:
      cpu: 50m
      memory: 64Mi
  - type: PersistentVolumeClaim
    max:
      storage: 100Gi
    min:
      storage: 1Gi

배포 전체 흐름 — 코드에서 서비스까지

여기서는 배포 전체 흐름 — 코드에서 서비스까지을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

지금까지 배운 모든 구성요소를 하나의 이야기로 이어보겠습니다. ① 개발자가 코드를 작성하고 이미지를 빌드합니다. ② 그 이미지를 컨테이너 레지스트리에 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가 알림을 보냅니다. 애플리케이션 하나가 "코드"에서 "서비스"가 되기까지, 이 열 단계가 매 배포마다 반복됩니다.
다이어그램 렌더링 중…

Tip

  • 🎯 핵심 요약
  • "배포"는 한 번의 명령이 아니라, API Server·Scheduler·kubelet·Service·Gateway·ConfigMap/Secret·HPA·Prometheus가 순서대로 이어받는 하나의 파이프라인입니다.
  • 이 흐름 중 어디서 문제가 생겼는지 파악하려면 "지금 Pod가 어느 단계에 멈춰 있는가"부터 확인하는 습관이 중요합니다 — Pending이면 ④번 이전, CrashLoopBackOff면 ⑤번 직후, 502/504 에러면 ⑥~⑦번 구간을 의심합니다.

실무 관점 비교 정리

실무 관점 비교 정리은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

이 가이드에서 다룬 개념들은 이름이 비슷해 실무에서 자주 혼동됩니다. 어떤 상황에 어느 쪽을 골라야 하는지 헷갈릴 때 아래 표로 빠르게 되짚어 보세요.
비교 대상핵심 차이
Pod vs ContainerContainer는 프로세스 실행 단위, Pod는 그 Container(들)이 네트워크·볼륨을 공유하며 함께 스케줄링되는 K8s 최소 단위
Deployment vs StatefulSetDeployment는 Pod가 서로 완전히 동일하고 교체 가능(Stateless), StatefulSet은 각 Pod가 고유한 이름·네트워크 ID·전용 스토리지를 가짐(Stateful)
Service vs Ingress vs Gateway APIService는 L4 수준의 안정적 엔드포인트, Ingress/Gateway API는 그 위에서 도메인·경로 기반 L7 라우팅과 TLS 종료를 수행 (Gateway API가 Ingress의 공식 후속)
ConfigMap vs Secret둘 다 설정을 코드와 분리하는 용도지만, ConfigMap은 비민감 데이터, Secret은 Base64 인코딩된 민감 데이터 — Secret도 그 자체로 암호화는 아님
NodePort vs LoadBalancerNodePort는 모든 Node의 고정 포트를 열어 수동으로 노출, LoadBalancer는 클라우드 LB를 자동 프로비저닝 — 클라우드 환경에서는 LoadBalancer가 사실상 표준
HPA vs VPAHPA는 Pod 개수를 조정(가로 확장), VPA는 Pod 하나의 CPU/메모리 할당량을 조정(세로 확장) — 목적이 다르므로 같은 대상에 동시 적용 시 충돌 위험
Docker Compose vs KubernetesDocker Compose는 단일 호스트에서 컨테이너 여러 개를 함께 띄우는 도구, Kubernetes는 여러 호스트에 걸친 배포·확장·복구·서비스 디스커버리까지 아우르는 오케스트레이션 플랫폼
VM 운영 vs Kubernetes 운영VM 운영은 서버 단위로 사람이 배포·복구·확장을 관리, Kubernetes 운영은 "원하는 상태"만 선언하면 시스템이 지속적으로 그 상태를 맞추는 선언형·자동화 중심 운영

자주 하는 실수와 운영 팁

자주 하는 실수와 운영 팁은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

아래는 실무에서 반복적으로 관찰되는 실수들입니다. 미리 알고 있으면 대부분 피할 수 있습니다.
실수왜 문제인가올바른 접근
Pod를 직접 운영 리소스로 사용Node 장애나 삭제 시 다시 살아나지 않음 — 컨트롤러가 없으면 재스케줄링 자체가 안 됨항상 Deployment/StatefulSet/Job 등 컨트롤러를 통해 Pod를 생성
Service 없이 Pod IP에 직접 의존Pod IP는 재생성될 때마다 바뀌는 임시 주소 — 코드에 하드코딩하면 곧 끊어짐Service 이름(DNS) 또는 ClusterIP로만 통신
Secret이 Base64라서 안전하다고 오해Base64는 누구나 즉시 디코딩 가능한 인코딩일 뿐, 암호화가 아님etcd Encryption at Rest + 최소권한 RBAC + External Secrets Operator 병행
resources.requests/limits 미설정스케줄링 근거가 없어 자원 배치가 불균형해지고, 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)까지 메트릭과 통합

연계 가이드: Docker 가이드 · CI/CD 가이드 · Prometheus 가이드

Kubernetes 실무 설계

Kubernetes 실무 설계은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

Kubernetes는 YAML 작성보다 workload, service discovery, config, secret, rollout 전략 설계가 중요합니다.
결정 지점확인 질문실무 기준
경계Kubernetes 코드에서 바뀌기 쉬운 부분은 어디인가?입출력, 설정, 외부 연동, 핵심 규칙을 분리합니다.
상태상태가 어디서 생성되고 어디서 사라지는가?상태 소유자와 수명 주기를 코드로 드러냅니다.
장애실패했을 때 호출자는 무엇을 받는가?timeout, fallback, error contract를 먼저 정합니다.

Kubernetes 운영 기준

이 섹션은 Kubernetes 운영 기준을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.

requests/limits, HPA, probe, PDB, rollout history, namespace quota를 운영 기준으로 관리해야 합니다.

Tip

  • requests/limits
  • readiness probe
  • PDB
  • rollout rollback

Kubernetes 검증 전략

Kubernetes 검증 전략은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

manifest validation, policy check, canary rollout, rollback rehearsal를 포함해야 합니다.
품질 축검증 방법완료 기준
정확성정상/실패 케이스를 자동화합니다.핵심 시나리오가 재현 가능하게 통과합니다.
회귀 방지버그 수정 시 동일 케이스를 테스트로 남깁니다.같은 장애가 다시 배포되지 않습니다.
운영성로그, 메트릭, 알림을 확인합니다.문제가 생겼을 때 원인 추적 경로가 있습니다.
← 이전 가이드CI/CD다음 가이드 →Kubernetes 심화/실무