Kubernetes를 운영 관점에서 이해하기
Kubernetes의 핵심 오브젝트, 선언적 관리, 스케줄링과 네트워킹, 자동 스케일링을 운영 관점에서 풀어 설명한다. ECS에서 EKS로 넘어갈 때의 비교 지점과 프로덕션에서 자주 만나는 실패 모드까지 함께 정리한다.
Script Companion
오디오와 함께 스크립트 보기
- 01
Kubernetes를 이해할 때 첫 출발점은 컨테이너를 어디에 올리느냐가 아니라, 클러스터가 원하는 상태를 계속 맞추는 시스템이라는 점입니다. 2025 CNCF Annual Cloud Native Survey에 따르면 컨테이너를 사용하는 조직의 82%가 프로덕션에서 Kubernetes를 운영한다고 문서는 설명합니다. ECS가 AWS 전용 모델에 가깝다면, Kubernetes는 클라우드 중립적인 API 모델을 제공하기 때문에 ArgoCD, Helm, Crossplane 같은 플랫폼 엔지니어링 도구 생태계를 읽는 공통 언어가 됩니다.
- 02
Kubernetes 아키텍처에서 가장 중요한 원칙은 모든 컴포넌트가 API Server를 통해서만 통신한다는 점입니다. Scheduler가 kubelet에게 직접 명령하지 않고, Controller Manager도 etcd에 직접 쓰지 않습니다. API Server는 은행 창구 직원, etcd는 금고, Scheduler와 Controller는 내부 업무 부서에 가깝습니다. 모든 요청이 창구를 지나기 때문에 컴포넌트가 독립적으로 동작할 수 있고, 하나가 재시작되어도 전체 시스템이 곧바로 멈추지 않는 구조가 됩니다.
- 03
Pod가 어느 Node에 올라갈지는 단순히 빈 자리를 찾는 문제가 아닙니다. Scheduler는 먼저 Filtering 단계에서 배치할 수 없는 Node를 제거합니다. CPU와 메모리 요청량이 가용 자원보다 크거나, NodeSelector와 Affinity 조건을 만족하지 못하거나, Taint와 Toleration 규칙이 맞지 않으면 후보에서 빠집니다. 그다음 Scoring 단계에서는 남은 Node에 점수를 매깁니다. 리소스 분산도, 이미 같은 이미지가 있는지, 데이터 지역성, Zone 분산 같은 기준을 보고 가장 적합한 Node를 선택합니다.
- 04
핵심 오브젝트는 역할을 나누어 이해하면 덜 복잡합니다. Pod는 하나 이상의 컨테이너를 담는 실행 최소 단위이고, Deployment는 Pod의 원하는 개수를 유지하며 롤링 업데이트를 관리합니다. Service는 Pod에 안정적인 네트워크 접점을 제공하고, Ingress는 외부 HTTP 트래픽을 Service로 라우팅합니다. ConfigMap은 설정값을 코드와 분리하고, Secret은 비밀번호나 API Key 같은 민감 정보를 다룹니다. Namespace는 dev, staging, prod처럼 리소스를 논리적으로 나누는 경계입니다.
- 05
Service 네트워킹은 Pod IP가 언제든 바뀔 수 있다는 사실에서 출발합니다. Pod가 재시작되거나 스케일링되면 IP가 달라질 수 있으므로, Pod끼리 직접 IP로 연결하면 안정적이지 않습니다. Service는 대표 전화번호처럼 고정된 ClusterIP를 제공하고, CoreDNS는 전화번호부처럼 Service 이름을 IP로 바꿉니다. Pod 안에는 DNS 설정이 주입되고, Service 이름으로 요청하면 네임스페이스가 포함된 클러스터 내부 이름으로 확장됩니다. 이후 kube-proxy가 ClusterIP로 들어온 트래픽을 실제 Pod IP로 라우팅합니다.
- 06
Kubernetes의 선언적 관리는 “지금 이렇게 해라”가 아니라 “이 상태가 되어라”에 가깝습니다. Pod 3개가 필요하다고 선언하면, 하나가 죽었을 때 Controller가 현재 상태와 원하는 상태의 차이를 보고 새 Pod를 만들어 다시 3개로 맞춥니다. 이 Reconciliation Loop는 Kubernetes만의 개념이 아닙니다. Terraform은 선언한 AWS 리소스와 실제 상태를 plan과 apply 흐름으로 맞추고, GitOps의 ArgoCD는 Git 저장소의 YAML 상태로 클러스터를 동기화합니다. SQL 스키마에서는 Flyway와 Liquibase 같은 DB 마이그레이션 도구가 DDL로 선언한 구조를 맞춥니다.
- 07
Reconciliation 시스템의 공통 구조는 선언, 감지, 수렴입니다. 이 패턴을 알면 Crossplane으로 AWS 인프라를 Kubernetes 방식으로 선언하거나, AWS Config Rules로 인프라 컴플라이언스를 다루거나, Ansible의 멱등 실행을 볼 때도 같은 사고 모델로 읽을 수 있습니다. 하지만 실패 경계도 비슷합니다. 상태를 읽지 못하면 수렴할 수 없고, 서로 다른 도구가 같은 리소스를 반대로 고치면 무한 루프가 생깁니다. 컨트롤러 버그나 의존 사이클처럼 상태가 폭발하는 경우도 있기 때문에, 새 도구를 볼 때는 어디서 현재 상태를 읽고 누가 최종 상태를 조정하는지 확인해야 합니다.
- 08
자가 치유는 선언적 관리가 운영에서 드러나는 대표 기능입니다. Pod가 죽으면 Deployment Controller 쪽에서 새 Pod를 만들고, ReplicaSet Controller는 etcd를 감시하면서 현재 Pod 수와 desired 수가 다른지 계속 확인합니다. 자동 스케일링에서는 HPA가 CPU나 메모리 사용률을 보고 Pod 수를 늘리거나 줄입니다. 공식 알고리즘은 현재 Pod 수에 현재 메트릭과 목표 메트릭의 비율을 곱한 뒤 올림하는 형태입니다. 예를 들어 Pod 2개의 평균 CPU가 78%이고 목표가 50%라면 결과는 4개가 됩니다.
- 09
HPA를 쓴다고 해서 항상 올바르게 스케일링되는 것은 아닙니다. HPA가 동작하려면 metrics-server 같은 메트릭 소스가 필요하고, AWS EKS에서는 기본 설치되어 있지 않다고 문서는 짚습니다. 또 CPU 기준만 보면 조용히 틀린 판단을 할 수 있습니다. Nest.js API가 외부 결제 API 응답을 2초씩 기다리는 I/O 병목이면 CPU는 20%로 낮아도 p95 latency는 1초를 넘을 수 있습니다. 이때는 CPU 목표치만 보지 말고 애플리케이션 p95와 p99 latency, 큐 길이, connection pool, downstream timeout과 재시도 정책까지 함께 봐야 합니다.
- 10
이벤트 기반 부하에는 KEDA가 더 직접적인 답이 될 수 있습니다. HPA가 직원이 바쁠 때 사람을 더 붙이는 방식이라면, KEDA는 주문이 밀릴 때 사람을 더 붙이는 방식에 가깝습니다. SQS 큐에 메시지가 쌓이거나 Kafka 토픽 lag이 커질 때처럼 외부 이벤트를 기준으로 Pod를 늘릴 수 있고, AWS SQS, Kafka, Redis, RabbitMQ, Prometheus 메트릭 등 50개 이상의 내장 스케일러를 제공합니다. Pod가 늘어났는데 Node가 부족하면 Pending에 멈추므로, Karpenter가 Pod의 CPU, 메모리, ARM이나 x86 같은 요구사항을 보고 적합한 인스턴스를 즉시 프로비저닝하는 역할을 맡습니다.
- 11
프로덕션에서는 Probe, NetworkPolicy, RBAC 같은 기본 안전장치가 중요합니다. Readiness Probe는 이 Pod가 트래픽을 받을 준비가 됐는지 확인하고, 실패하면 Service 엔드포인트에서 제외합니다. Liveness Probe는 컨테이너가 살아 있는지 확인하고, 실패하면 재시작합니다. NetworkPolicy는 기본적으로 모든 Pod가 서로 통신할 수 있는 Kubernetes 클러스터에서 Pod 간 트래픽을 코드로 제한하는 방화벽 규칙서입니다. 다만 CNI 플러그인이 지원해야 실제로 동작하므로, YAML이 만들어졌다고 안심하지 말고 허용되지 않은 Pod에서 실제 TCP 연결이 실패하는지 확인해야 합니다. RBAC는 누가 어떤 리소스에 어떤 행동을 할 수 있는지를 제어합니다.
- 12
운영 중 자주 보는 오류는 증상과 원인을 묶어 기억하는 편이 좋습니다. CrashLoopBackOff는 Pod가 시작했다가 죽기를 반복하는 상태이고, Nest.js 앱이라면 DB 연결 실패, 환경변수 누락, 포트 충돌이 흔한 원인으로 제시됩니다. OOMKilled는 메모리 부족으로 강제 종료된 상태이며, Exit Code 137은 SIGKILL 신호와 연결됩니다. ImagePullBackOff는 private 레지스트리 인증 정보가 없거나 이미지 태그가 존재하지 않을 때 나타납니다. Pending은 요청한 CPU와 메모리를 만족하는 Node가 없거나 Node Selector와 Affinity 조건이 맞지 않을 때 발생할 수 있습니다.
- 13
더 큰 장애는 클러스터 아키텍처와 인프라 이벤트에서 옵니다. etcd는 Raft 합의 알고리즘을 사용하고, 3노드 클러스터에서 2노드가 동시에 장애를 일으키면 쿼럼을 잃습니다. 이때 API Server가 새 상태를 기록할 수 없어 kubectl 명령은 타임아웃되고, 이미 실행 중인 Pod는 계속 동작하더라도 신규 배포, 스케일링, 자가 치유는 멈춥니다. Spot 인스턴스 대량 회수도 위험합니다. AWS는 회수 2분 전에 termination notice를 보내지만, terminationGracePeriodSeconds나 preStop hook이 부실하면 처리 중인 요청이 강제 종료되어 데이터 유실과 504 에러가 늘 수 있습니다.
- 14
2025년 변화도 운영 판단에 영향을 줍니다. StatefulSet MaxUnavailable은 업데이트 때 여러 Pod를 병렬로 업데이트할 수 있게 해, 기존처럼 하나씩 순서대로 바꾸던 방식보다 DB 클러스터 업데이트 시간을 줄이는 기능으로 소개됩니다. PreferSameNode 트래픽 분산은 같은 Node의 엔드포인트를 우선 선택해 네트워크 레이턴시와 cross-zone 데이터 전송 비용을 줄이는 방향입니다. AWS re:Invent 2025에서 정식 출시된 EKS Capabilities는 ArgoCD Capability, ACK, KRO를 통해 GitOps 배포, Kubernetes 리소스 기반 AWS 서비스 관리, 복잡한 리소스 묶음의 CRD 추상화를 관리형으로 제공하는 흐름입니다.
- 15
정리하면 Kubernetes는 Pod, Deployment, Service를 중심으로 실행 단위, 복제 관리, 네트워크 접점을 나누고, YAML로 선언한 원하는 상태에 현재 상태를 계속 맞추는 시스템입니다. ECS에서 넘어올 때는 Task Definition 하나로 보이던 것이 Pod spec과 Deployment spec처럼 더 잘게 나뉘기 때문에 처음에는 오브젝트 수가 많아 보일 수 있습니다. 대신 이 구조 위에서 HPA와 KEDA는 Pod 수를 조정하고, Karpenter는 Node를 추가하며, Readiness, Liveness, NetworkPolicy, RBAC가 운영 경계를 세웁니다. 핵심은 무엇을 선언했고, 누가 감지하며, 어떤 조건에서 수렴이 멈추는지 끝까지 추적하는 것입니다.
같은 레이어