Joe Beda의 초기 쿠버네티스 선택—선언적 API, 컨트롤 루프, Pods, Services, 레이블—이 어떻게 현대 애플리케이션 플랫폼을 형성했는지 명확히 설명합니다.

Joe Beda는 쿠버네티스 초기 설계의 핵심 인물 중 하나였고, 다른 창립자들과 함께 구글 내부 시스템에서 얻은 교훈을 오픈 플랫폼으로 옮겼습니다. 그의 영향력은 유행하는 기능을 쫓는 데 있지 않았습니다. 대신 실제 프로덕션의 혼란을 견디고 일반 팀도 이해할 수 있는 간단한 원시(primitive)를 고르는 데 있었습니다.
그 초기 결정들이 쿠버네티스를 단순한 “컨테이너 도구” 이상의 것으로 만들었습니다. 현대 애플리케이션 플랫폼의 재사용 가능한 커널이 된 이유입니다.
“컨테이너 오케스트레이션”은 기계가 고장나거나 트래픽이 급증하거나 새 버전을 배포할 때 앱을 계속 실행시키는 규칙과 자동화의 집합입니다. 사람이 서버를 돌보는 대신 시스템이 컨테이너를 컴퓨터에 스케줄하고, 크래시하면 재시작하고, 내구성을 위해 분산시키고, 사용자가 접근할 수 있도록 네트워킹을 연결합니다.
쿠버네티스가 보편화되기 전에는 팀들이 기본 질문들에 답하기 위해 스크립트와 맞춤 도구를 이어 붙였습니다:
이 글은 초기 쿠버네티스 설계 선택들(쿠버네티스의 “형태”)과 그것들이 왜 현대 플랫폼에 여전히 영향을 주는지 설명합니다: 선언적 모델, 컨트롤러, Pods, 레이블, Services, 강력한 API, 일관된 클러스터 상태, 플러그형 스케줄링, 확장성. 직접 쿠버네티스를 운영하지 않더라도 이러한 아이디어 위에 구축된 플랫폼을 사용하거나 같은 문제로 고생하고 있을 가능성이 큽니다.
쿠버네티스 이전에 “컨테이너 실행”은 보통 몇 개의 컨테이너를 구동하는 것을 의미했습니다. 팀들은 배포를 위해 bash 스크립트, cron, 골든 이미지와 몇 가지 즉석 도구를 이어 붙였습니다. 무언가 고장나면 고치는 방법은 종종 누군가의 머릿속이나 아무도 신뢰하지 않는 README에 있었습니다. 운영은 재시작, 로드밸런서 재지정, 디스크 정리, 어느 머신을 건드려야 할지 추측하는 일련의 일회성 개입이었습니다.
컨테이너는 패키징을 쉽게 만들었지만 프로덕션의 까다로운 부분을 제거하지는 못했습니다. 큰 규모에서는 시스템이 더 자주, 더 다양한 방식으로 실패합니다: 노드가 사라지고, 네트워크가 분할되며, 이미지는 불일치하게 롤아웃되고, 워크로드는 당신이 "실행 중"이라고 생각하는 것과 달라집니다. "단순한" 배포가 카스케이드로 바뀔 수 있습니다—어떤 인스턴스는 업데이트되고 어떤 것은 그렇지 않거나, 일부는 멈추고 일부는 건강하지만 접근 불가 상태가 됩니다.
진짜 문제는 컨테이너 시작 그 자체가 아니라, 계속 변하는 환경 속에서도 올바른 컨테이너를 올바른 형태로 유지하는 것이었습니다.
팀들은 온프레미스 하드웨어, 가상머신, 초기 클라우드 제공자, 다양한 네트워크와 스토리지 설정 등 서로 다른 환경을 동시에 다루고 있었습니다. 각 플랫폼은 고유한 어휘와 실패 패턴을 가졌습니다. 공유된 모델이 없으면 마이그레이션마다 운영 도구를 다시 쓰고 사람들을 재교육해야 했습니다.
쿠버네티스는 머신이 어디 있든 관계없이 애플리케이션과 그 운영 요구를 설명하는 단일 일관된 방식을 제공하려 했습니다.
개발자들은 셀프 서비스(티켓 없이 배포, 용량 요청 없이 스케일, 드라마 없는 롤백)를 원했습니다. 운영팀은 예측 가능성을 원했습니다: 표준화된 헬스체크, 반복 가능한 배포, 무엇이 실행되어야 하는지에 대한 명확한 진실 소스.
쿠버네티스는 화려한 스케줄러가 되려 한 것이 아니라 신뢰할 수 있는 애플리케이션 플랫폼의 기반이 되려 했습니다—복잡한 현실을 추론 가능한 시스템으로 바꾸는 것.
초기 결정 중 가장 영향력 있었던 것 중 하나는 쿠버네티스를 선언적으로 만든 것입니다: 원하는 것을 기술하면 시스템이 현실을 그 기술과 일치시키려고 동작합니다.
온도 조절기는 유용한 일상적 예입니다. 몇 분마다 난방을 켜고 끄지 않습니다. 원하는 온도(예: 21°C)를 설정하면 온도계가 방을 계속 확인하고 난방을 조절해 그 목표에 가깝게 유지합니다.
쿠버네티스도 마찬가지입니다. 클러스터에 "이 컨테이너를 저 머신에서 시작하고 실패하면 재시작하라"고 단계별로 지시하는 대신, 결과를 선언합니다: "이 앱의 복제본 3개를 원한다." 쿠버네티스는 실제로 무엇이 실행 중인지 계속 점검하고 드리프트를 수정합니다.
선언적 구성은 종종 누군가의 머릿속이나 반쯤 업데이트된 런북에 존재하는 숨은 ‘운영 체크리스트’를 줄입니다. 설정을 적용하면 쿠버네티스가 배치, 재시작, 변경 조정 등의 기계적 작업을 처리합니다.
변경도 diff로 보이기 때문에 검토가 쉬워집니다. 즉 변경은 일련의 즉석 명령이 아니라 구성의 차이로 나타납니다.
원하는 상태가 문서화되어 있으므로 개발, 스테이징, 프로덕션에서 같은 접근을 재사용할 수 있습니다. 환경은 다를 수 있지만 의도는 일관되므로 배포가 더 예측 가능하고 감사하기 쉬워집니다.
선언적 시스템은 학습 곡선이 있습니다: "다음에 무엇을 할까"가 아니라 "무엇이 참이어야 할까"를 생각해야 합니다. 또한 좋은 기본값과 명확한 관례에 크게 의존합니다—그렇지 않으면 기술적으로 동작하지만 이해하고 유지하기 어려운 구성이 생길 수 있습니다.
쿠버네티스가 성공한 이유는 단지 컨테이너를 한 번 실행할 수 있어서가 아니라 시간이 지남에 따라 올바르게 유지할 수 있었기 때문입니다. 큰 설계 결정은 시스템의 핵심 엔진으로 "컨트롤 루프"(컨트롤러)를 만든 것이었습니다.
컨트롤러는 단순한 루프입니다:
이는 일회성 작업이 아니라 자동 조종장치와 같습니다. 워크로드를 ‘돌보는’ 대신, 원하는 것을 선언하면 컨트롤러가 클러스터를 그 결과로 계속 이끕니다.
이 패턴 덕분에 현실의 문제가 일어날 때 쿠버네티스는 회복력을 가집니다:
실패를 특수한 사례로 처리하는 대신, 컨트롤러는 이를 일상적 ‘상태 불일치’로 보고 동일한 방식으로 고칩니다.
전통적 자동화 스크립트는 종종 안정된 환경을 가정합니다: A를 수행하고 B를 수행하고 C를 수행하라. 분산 시스템에서는 그런 가정이 끊임없이 깨집니다. 컨트롤러는 **멱등성(idempotent)**을 갖고 반복 실행해도 안전하며 **결국 일관성(eventually consistent)**을 보장해 목표가 달성될 때까지 시도하기 때문에 더 잘 확장됩니다.
Deployment를 사용해봤다면 컨트롤 루프를 이미 의존하고 있는 셈입니다. 내부적으로 쿠버네티스는 요청된 수의 Pod가 존재하는지 보장하기 위해 ReplicaSet 컨트롤러를 사용하고, Deployment 컨트롤러는 예측 가능한 롤링 업데이트와 롤백을 관리합니다.
쿠버네티스는 “그냥 컨테이너”만 스케줄할 수도 있었지만, Joe Beda의 팀은 머신에 배치되는 최소 배포 단위를 나타내는 Pod를 도입했습니다. 핵심 아이디어: 많은 실제 애플리케이션은 단일 프로세스가 아닙니다. 작은 그룹의 긴밀히 결합된 프로세스들이 함께 존재해야 합니다.
Pod는 동일한 운명을 공유하는 하나 이상의 컨테이너를 감싸는 단위입니다: 함께 시작하고 같은 노드에서 실행되며 함께 스케일됩니다. 이로써 사이드카 같은 패턴이 자연스러워졌습니다—로그 수집기, 프록시, 설정 리로더, 보안 에이전트 등이 메인 앱과 항상 같이 있어야 할 때 유용합니다.
별도의 컨테이너로 헬퍼를 포장하면서도 하나의 단위처럼 동작하게 할 수 있습니다.
Pod는 두 가지 가정들을 실용적으로 만들었습니다:
localhost로 접근할 수 있어 간단하고 빠릅니다.이 선택은 맞춤형 글루 코드를 줄이면서 프로세스 수준의 격리는 유지합니다.
신규 사용자는 종종 “1 컨테이너 = 1 앱”을 기대하다가 재시작, IP, 스케일링 같은 Pod 수준 개념에 걸립니다. 많은 플랫폼은 이것을 완화하기 위해 의견이 강한 템플릿(예: “웹 서비스”, “워커”, “잡”)을 제공해 내부적으로 Pod를 생성하도록 해서 팀들이 매일 Pod 메커니즘을 직접 신경 쓰지 않아도 이점을 누리도록 합니다.
쿠버네티스에서 조용히 강력했던 초기 선택은 레이블을 일급 메타데이터로 취급하고 셀렉터를 항목을 찾는 주요 방식으로 삼은 것입니다. 특정 머신 목록을 하드코딩하기보다(예: “이 세 대의 머신이 내 앱을 실행한다”) 레이블로 그룹을 묘사하도록 권장했습니다.
레이블은 자원(Pod, Deployment, Node, Namespace 등)에 붙이는 간단한 키/값쌍입니다. 쿼리 가능한 일관된 태그처럼 동작합니다:
app=checkoutenv=prodtier=frontend레이블은 가볍고 사용자 정의 가능하기 때문에 팀, 비용 센터, 규정 준수 존, 릴리스 채널 등 실제 조직 현실을 모델링할 수 있습니다.
셀렉터는 레이블에 대한 질의입니다(예: app=checkout 그리고 env=prod인 모든 Pod). 이는 고정된 호스트 목록을 뛰어넘는 이점이 있습니다. Pod가 재스케줄되거나 스케일되거나 교체될 때 시스템이 적응할 수 있기 때문입니다. 기본 구성은 변하지 않으면서도 실제 인스턴스는 계속 바뀔 수 있습니다.
이 설계는 운영적으로 확장 가능합니다: 수천 개의 인스턴스 ID를 관리하는 대신 몇 가지 의미 있는 레이블 집합을 관리하면 됩니다. 이것이 느슨한 결합의 핵심입니다: 구성 요소는 변경 가능한 그룹에 연결되고 구성원 변경이 안전합니다.
레이블이 존재하면 플랫폼 전반의 공용 어휘가 됩니다. 트래픽 라우팅(Services), 정책 경계(NetworkPolicy), 관찰성 필터(메트릭/로그), 비용 추적 등 다양한 자동화가 레이블을 활용합니다. 간단한 아이디어—일관되게 태깅하기—가 많은 자동화 가능성을 열어줍니다.
쿠버네티스는 Pod의 IP와 머신이 수시로 바뀌는 상황에서도 네트워킹을 예측 가능하게 만들어야 했습니다. Pod는 교체되고 재스케줄되며 스케일링되기 때문에 IP나 실행되는 특정 머신이 바뀝니다. Service의 핵심 아이디어는 바뀌는 Pod 집합에 안정적인 “현관”을 제공하는 것입니다.
Service는 일관된 가상 IP와 DNS 이름(예: payments)을 제공합니다. 그 이름 뒤에서 쿠버네티스는 Service의 셀렉터와 일치하는 Pod를 지속적으로 추적하고 트래픽을 라우팅합니다. Pod가 죽고 새 Pod가 나타나도 Service는 올바른 곳을 가리키며 애플리케이션 설정을 건드릴 필요가 없습니다.
이 접근은 많은 수작업 연결을 제거했습니다. IP를 설정 파일에 하드코딩하는 대신 앱은 이름에 의존할 수 있습니다. 앱을 배포하고 Service를 배포하면 다른 구성 요소가 DNS를 통해 찾을 수 있습니다—별도의 레지스트리나 하드코딩된 엔드포인트 불필요.
Service는 건강한 엔드포인트들 간의 기본적인 로드 분산 동작도 도입했습니다. 이는 각 내부 마이크로서비스마다 자체 로드밸런서를 구축할 필요를 줄였습니다. 트래픽을 분산하면 단일 Pod 실패의 영향 범위를 줄이고 롤링 업데이트의 위험을 낮춥니다.
Service는 L4(TCP/UDP) 트래픽에 적합하지만 HTTP 라우팅 규칙, TLS 종료, 엣지 정책을 모델링하지는 않습니다. 그럴 때 Ingress나 점점 자리잡는 Gateway API가 Services 위에 쌓여 호스트 이름, 경로, 외부 진입점을 더 깔끔하게 처리합니다.
초기에는 쿠버네티스를 클릭해서 쓰는 단일 제품이 아니라 구축하고 확장할 수 있는 API로 대우한 것이 조용히 혁신적이었습니다. API 우선 접근은 쿠버네티스를 확장 가능하고 스크립트화할 수 있게 했습니다.
API가 표면적 계약이 되자 플랫폼 팀은 어떤 UI나 파이프라인, 내부 포털이 위에 얹히든 애플리케이션을 기술하고 관리하는 방법을 표준화할 수 있었습니다. “앱을 배포한다”는 것은 “API 객체(Deployment, Service, ConfigMap 등)를 제출하고 갱신한다”로 바뀌었습니다. 이는 앱 팀과 플랫폼 사이의 훨씬 깔끔한 계약입니다.
모든 것이 동일한 API를 통해 동작하기 때문에 새로운 툴은 특권 백도어가 필요 없습니다. 대시보드, GitOps 컨트롤러, 정책 엔진, CI/CD 시스템은 모두 범위가 제한된 권한으로 일반적인 API 클라이언트처럼 동작할 수 있습니다.
이 대칭성은 중요합니다: 요청이 사람, 스크립트, 내부 포털 중 어디서 왔든 동일한 규칙, 인증, 감사, 승인이 적용됩니다.
API 버전 관리는 쿠버네티스를 진화시키면서 모든 클러스터나 툴을 하루아침에 깨뜨리지 않도록 해주었습니다. 사용 중단은 단계적으로 진행할 수 있고, 호환성 테스트 및 업그레이드 계획이 가능합니다. 수년간 클러스터를 운영하는 조직에겐 “업그레이드할 수 있다”와 “갇혀 있다”의 차이입니다.
kubectl은 쿠버네티스 그 자체가 아니라 하나의 클라이언트입니다. 이 모델은 팀들이 API 워크플로우 관점에서 생각하게 만듭니다: kubectl을 자동화나 웹 UI, 커스텀 포털로 교체해도 계약이 API이기 때문에 시스템은 일관성을 유지합니다.
쿠버네티스는 현재 클러스터가 어떤 모습이어야 하는지—어떤 Pod가 존재하고 어떤 노드가 건강한지, 어떤 Service가 어떤 대상을 가리키는지—에 대한 단일 "진실의 근원"이 필요했습니다. 그것이 etcd입니다.
etcd는 제어 플레인의 데이터베이스입니다. Deployment를 만들거나 ReplicaSet을 스케일하거나 Service를 업데이트하면 원하는 구성은 etcd에 기록됩니다. 컨트롤러와 다른 제어면 컴포넌트는 그 저장된 상태를 보고 현실을 일치시키려 합니다.
쿠버네티스 클러스터는 스케줄러, 컨트롤러, kubelet, 오토스케일러, 어드미션 체크 등 수많은 움직이는 부품으로 가득합니다. 만약 이들이 서로 다른 버전의 “진실”을 읽는다면 경쟁 상태가 발생합니다—예: 두 컴포넌트가 같은 Pod에 대해 상충되는 결정을 내리는 경우.
etcd의 강한 일관성은 제어면이 “이것이 현재 상태다”라고 말할 때 모두가 정렬되도록 보장합니다. 그 정렬이 컨트롤 루프를 무질서가 아닌 예측 가능한 것으로 만듭니다.
etcd가 클러스터의 구성과 변경 이력을 보유하기 때문에 다음 상황에서 보호 대상이 됩니다:
제어 플레인 상태를 중요한 데이터로 다루세요. 정기적으로 etcd 스냅샷을 찍고, 복원을 테스트하며 백업을 클러스터 외부에 보관하세요. 관리형 쿠버네티스를 운영 중이라면 제공자가 무엇을 백업하는지, 여전히 직접 백업해야 할 항목(예: 영구 볼륨과 앱 수준 데이터)이 무엇인지 숙지하세요.
쿠버네티스는 “워크로드가 어디서 실행되는가”를 사소하게 취급하지 않았습니다. 초기부터 스케줄러는 별도의 컴포넌트로서 명확한 역할을 가졌습니다: Pod의 요구사항과 클러스터의 현재 상태를 사용해 실제로 해당 Pod를 실행할 수 있는 노드를 매칭하는 것입니다.
개념적으로 스케줄링은 두 단계 결정입니다:
이 구조 덕분에 스케줄링을 전면 재작성하지 않고도 발전시킬 수 있었습니다.
주요 설계 선택은 책임을 명확히 분리한 것입니다:
이러한 분리는 한 영역의 개선(예: 새로운 CNI 플러그인)이 전체 스케줄링 모델을 강제로 바꾸지 않도록 합니다.
자원 인식은 requests와 limits에서 시작해 스케줄러에 의미 있는 신호를 제공했습니다. 이후에는 node affinity/anti-affinity, pod affinity, priorities and preemption, taints and tolerations, 토폴로지 인식 분산 등 더 풍부한 제어가 같은 기반 위에 추가되었습니다.
이 접근법은 오늘날의 공유 클러스터를 가능하게 합니다: 우선순위와 taint로 중요한 서비스를 격리하고, 모두가 더 높은 활용도를 누리면서도 신뢰성을 유지할 수 있습니다. 더 나은 빈 패킹과 토폴로지 제어로 워크로드를 비용 효율적으로 배치할 수 있습니다.
쿠버네티스는 빌드팩, 앱 라우팅 규칙, 백그라운드 잡, 설정 관례 등과 같은 완전한 의견형 PaaS로 출발할 수도 있었습니다. 대신 Joe Beda와 초기 팀은 핵심을 작게 유지하고: 워크로드를 안정적으로 실행하고 치유하며 노출하고 자동화할 수 있는 일관된 API를 제공하겠다는 약속에 집중했습니다.
“완전한 PaaS”는 모든 사람에게 하나의 워크플로우와 하나의 트레이드오프를 강제했을 것입니다. 쿠버네티스는 헤로쿠(Heroku) 같은 개발자 단순성, 엔터프라이즈 거버넌스, 배치+ML 파이프라인, 혹은 단순 인프라 제어 등 다양한 플랫폼 스타일을 지원할 수 있는 더 넓은 기반을 목표로 했습니다.
쿠버네티스의 확장 메커니즘은 기능을 제어된 방식으로 성장시킬 수 있게 했습니다:
Certificate, Database)이는 벤더가 쿠버네티스를 포크하지 않고도 차별화된 제품을 제공하도록 하고, 내부 플랫폼 팀이 조직 요구에 맞춘 “쿠버네티스 위의 플랫폼”을 만들 수 있게 합니다.
벤더에게는 차별화된 제품을 가능하게 하고, 내부 팀에게는 조직 맞춤형 플랫폼을 허용합니다. 반면 생태계 스프로일(도구 과다)은 위험입니다: 너무 많은 CRD, 겹치는 도구, 일관성 없는 관례가 생길 수 있습니다. 거버넌스(표준, 소유권, 버전 관리, 사용중단 규칙)가 플랫폼 작업의 일부가 됩니다.
쿠버네티스의 초기 선택은 단순한 컨테이너 스케줄러를 만든 것이 아니라 재사용 가능한 플랫폼 커널을 만들었습니다. 그래서 많은 현대 내부 개발자 플랫폼(IDP)은 핵심적으로 “쿠버네티스 + 의견이 강한 워크플로우”입니다. 선언적 모델, 컨트롤러, 일관된 API는 더 높은 수준의 제품을 구축할 수 있게 했습니다—배포, 조정, 서비스 발견을 매번 재발명하지 않고도요.
API가 제품 표면이므로 벤더와 플랫폼 팀은 하나의 제어 플레인에 표준화하고 그 위에 다양한 경험을 빌드할 수 있습니다: GitOps, 멀티클러스터 관리, 정책, 서비스 카탈로그, 배포 자동화 등. 이것이 쿠버네티스가 클라우드 네이티브 플랫폼의 공통 분모가 된 큰 이유입니다: 통합은 특정 UI가 아니라 API를 대상으로 삼습니다.
깔끔한 추상화가 있어도 가장 힘든 일은 운영입니다:
운영 성숙도를 드러내는 질문을 하세요:
좋은 플랫폼은 기본 제어 플레인을 숨기지 않으면서인지 부하를 줄여줘야 합니다. 회피 경로(escape hatches)를 쓰기 어렵게 만들어선 안 됩니다.
실용적 관점: 플랫폼이 팀들이 “아이디어 → 실행 서비스”로 이동하게 돕되, 모두가 첫날부터 쿠버네티스 전문가가 되도록 강요하지 않는가를 보세요. “vibe-coding” 범주의 도구들—예: Koder.ai—는 채팅에서 실제 애플리케이션(React 웹, Go 백엔드와 PostgreSQL, Flutter 모바일)을 생성하고 계획 모드, 스냅샷, 롤백 같은 기능으로 빠르게 반복할 수 있게 하며 빌드 측면을 가속화합니다. 어떤 도구를 채택하든 자체 포털을 만들든 목표는 같습니다: 쿠버네티스의 강력한 원시를 유지하면서 그 주위의 워크플로우 부담을 줄이는 것.
쿠버네티스는 복잡하게 느껴질 수 있지만, 그 ‘이상함’의 대부분은 의도적입니다: 여러 플랫폼으로 조합되도록 설계된 작은 원시들의 집합입니다.
첫째: "쿠버네티스는 단지 Docker 오케스트레이션이다." 쿠버네티스는 컨테이너를 시작하는 도구가 주목적이 아닙니다. 실패, 롤아웃, 수요 변화 속에서 원하는 상태(당신이 실행되길 원하는 것)와 실제 상태(실제로 돌아가는 것)를 지속적으로 조정하는 것입니다.
둘째: "쿠버네티스를 쓰면 모든 것이 마이크로서비스가 된다." 쿠버네티스는 마이크로서비스를 지원하지만 모놀리식, 배치 잡, 내부 플랫폼도 모두 지원합니다. 단위들(Pod, Service, 레이블, 컨트롤러, API)은 중립적입니다; 아키텍처 선택은 도구에 의해 강제되지 않습니다.
어려운 부분은 보통 YAML이나 Pod 자체가 아니라 네트워킹, 보안, 다팀 사용입니다: 아이덴티티와 접근, 시크릿 관리, 정책, 인그레스, 관측성, 공급망 제어, 팀들이 서로 문제를 일으키지 않고 안전하게 배포하도록 가드레일을 만드는 일입니다.
계획할 때 초기 설계 선택을 기준으로 생각하세요:
요구사항을 쿠버네티스 원시와 플랫폼 계층에 매핑하세요:
워크로드 → Pods/Deployments/Jobs
연결성 → Services/Ingress
운영 → 컨트롤러, 정책, 관측성
평가하거나 표준화하고 있다면 이 매핑을 문서화해 이해관계자와 검토하세요—그리고 트렌드가 아니라 격차(gaps)를 중심으로 점진적으로 플랫폼을 구축하세요.
빌드 측면(단지 실행이 아니라 빌드)을 가속화하려면 의도(intent)를 배포 가능한 서비스로 바꾸는 전달 워크플로우를 고려하세요. 일부 팀은 큐레이션된 템플릿 세트를 사용하고, 다른 팀은 Koder.ai 같은 AI보조 워크플로우로 초기 동작 서비스를 빠르게 생성한 뒤 소스 코드를 내보내 깊이 커스터마이즈합니다. 어떤 접근을 택하든, 플랫폼은 여전히 쿠버네티스의 핵심 설계 결정을 기반으로 이득을 얻습니다.
컨테이너 오케스트레이션은 기계가 고장나거나 트래픽이 변하거나 배포가 일어날 때 앱을 계속 실행되게 하는 자동화입니다. 실제로는 다음을 처리합니다:
쿠버네티스는 이러한 작업을 서로 다른 인프라 환경에서 일관되게 수행하는 모델을 대중화했습니다.
핵심 문제는 컨테이너를 시작하는 것이 아니라, 끊임없는 변화 속에서도 올바른 컨테이너를 올바른 형태로 유지하는 것이었습니다. 대규모에서는 다음 같은 일상적 실패와 드리프트가 발생합니다:
쿠버네티스는 표준 컨트롤 플레인과 공통 어휘를 제공해 운영을 반복 가능하고 예측 가능하게 만들고자 했습니다.
선언적(declarative) 시스템에서는 ‘어떤 결과를 원하는지’(예: “레플리카 3개 실행”)를 기술하면 시스템이 그 상태가 되도록 지속적으로 동작합니다.
실제 워크플로우:
kubectl apply 또는 GitOps)이 방식은 숨겨진 운영 절차를 줄이고, 변경을 ad-hoc 명령이 아니라 설정의 diff로 검토할 수 있게 합니다.
컨트롤러는 다음을 반복하는 제어 루프입니다:
이 설계는 일반적인 실패를 특수 사례가 아니라 루틴한 상태 불일치로 다룹니다. 예를 들어 Pod가 크래시하거나 노드가 사라지면 관련 컨트롤러는 단순히 “원하는 복제 수보다 적다”를 감지하고 대체 Pod를 만듭니다.
쿠버네티스는 개별 컨테이너가 아니라 Pod를 스케줄합니다. 많은 실제 워크로드는 보조 프로세스와 긴밀히 결합된 작은 그룹이기 때문입니다.
Pod는 다음 패턴을 가능하게 합니다:
localhost로 통신)경험 법칙: 라이프사이클, 네트워크 ID, 또는 로컬 데이터를 공유해야 하는 컨테이너들만 하나의 Pod로 묶으세요.
레이블은 가벼운 키/값 태그(예: app=checkout, env=prod)이고 셀렉터는 그런 레이블을 질의해 동적 그룹을 만듭니다.
이것이 중요한 이유는 인스턴스가 일시적이기 때문입니다: Pod는 롤아웃 중에 수시로 바뀝니다. 레이블/셀렉터를 사용하면 구성은 ‘이 레이블을 가진 모든 Pod’처럼 안정적으로 유지되며 구성원이 바뀌어도 관계가 유지됩니다.
운영 팁: 소규모 레이블 분류(app, team, env, tier)를 표준화하고 정책으로 강제하면 이후 혼란을 줄일 수 있습니다.
Service는 선택자와 일치하는 변하는 Pod 집합으로 트래픽을 라우팅하는 안정적인 가상 IP와 DNS 이름을 제공합니다.
Service를 사용해야 할 때:
HTTP 라우팅, TLS 종료, 엣지 규칙은 일반적으로 Service 위에 Ingress나 Gateway API를 추가해 처리합니다.
쿠버네티스는 API를 주요 제품 표면으로 취급합니다: 모든 것이 API 객체(Deployment, Service, ConfigMap 등)로 표현됩니다. kubectl은 그중 하나의 클라이언트일 뿐입니다.
실용적 이점:
내부 플랫폼을 설계할 때는 특정 UI가 아니라 API 계약을 중심으로 워크플로우를 설계하세요.
etcd는 제어 플레인의 데이터베이스이자 클러스터의 단일 진실 소스입니다. Deployment를 만들거나 Service를 업데이트하면 그 원하는 구성은 etcd에 기록됩니다. 컨트롤러와 제어면 컴포넌트는 그 저장된 상태를 보고 현실을 일치시키려 합니다.
실용 지침:
관리형 쿠버네티스를 사용하는 경우, 제공자가 무엇을 백업해주고 무엇을 직접 백업해야 하는지(예: PV와 애플리케이션 데이터)를 확인하세요.
쿠버네티스는 코어를 작게 유지하고 확장 메커니즘을 통해 기능을 추가하게 했습니다:
Certificate, Database)이 접근은 ‘쿠버네티스 위의 플랫폼’을 가능하게 하지만 도구 스프로일이 생길 수 있습니다. 평가할 때 물어볼 질문: