[{"data":1,"prerenderedAt":425},["ShallowReactive",2],{"post-document:\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-2\u002F":3},{"id":4,"title":5,"body":6,"categories":399,"date":402,"description":403,"extension":404,"image":405,"key_concepts":406,"last_modified_at":405,"legacyPath":411,"meta":412,"navigation":245,"part":413,"path":414,"published":245,"robots":405,"seo":415,"series":416,"stem":417,"strengths":405,"summary":418,"tags":419,"tradeoffs":405,"__hash__":424},"posts\u002Fposts\u002FCI-CD-Docker\u002Fkubernetes\u002F2026-09-07-kubernetes-part-2.md","2. 클러스터는 명령을 어떻게 Pod로 바꿀까",{"type":7,"value":8,"toc":389},"minimark",[9,14,22,33,36,40,46,49,101,104,108,114,120,123,126,130,136,139,145,156,164,168,174,180,183,190,194,200,270,277,280,284,290,352,355,359,375,385],[10,11,13],"h2",{"id":12},"컨트롤-플레인은-조정하고-워커-노드는-실행한다","컨트롤 플레인은 조정하고, 워커 노드는 실행한다",[15,16,17,21],"p",{},[18,19,20],"strong",{},"쿠버네티스는 하나의 프로그램이 모든 일을 처리하는 구조가 아니다. 서로 다른 컴포넌트가 API에 기록된 객체를 관찰하며 자기 역할을 수행한다."," 이름을 외우기 전에, 누가 무엇을 바꾸는지 구분해 보자.",[23,24,30],"pre",{"className":25,"code":27,"language":28,"meta":29},[26],"language-text","Cluster\n├── Control Plane\n│   ├── API Server: 요청과 상태 변경의 관문\n│   ├── etcd: API 객체 저장\n│   ├── Controller Manager: 여러 조정 루프 실행\n│   └── Scheduler: Pod의 실행 Node 선택\n└── Worker Node\n    ├── kubelet: 배정된 Pod를 실행 상태로 맞춤\n    ├── containerd 등 런타임: 이미지와 컨테이너 처리\n    └── Pod: 함께 배치되는 컨테이너 묶음\n","text","",[31,32,27],"code",{"__ignoreMap":29},[15,34,35],{},"아래 화살표는 결과가 이어지는 순서를 뜻한다. Controller가 Scheduler 함수를 직접 호출한다는 뜻은 아니다. 주요 컴포넌트는 API Server를 통해 객체 변경을 읽고 쓴다. 앱의 사용자 트래픽까지 API Server를 거쳐 흐르는 것은 아니다.",[10,37,39],{"id":38},"_1-api-server는-요청을-받지만-컨테이너를-실행하지-않는다","1. API Server는 요청을 받지만 컨테이너를 실행하지 않는다",[15,41,42,45],{},[18,43,44],{},"배포 요청이 받아들여졌다는 것은 선언이 저장됐다는 뜻이지, 앱이 준비됐다는 뜻은 아니다."," 실제 실행까지는 뒤의 단계가 남아 있다.",[15,47,48],{},"요청에는 인증, 인가, API 필드 검증과 admission 정책 같은 검사가 적용된다. 인증은 “누구인가”, 인가는 “이 작업을 해도 되는가”에 대응한다. 권한이 있어도 별도 정책이나 자원 쿼터 때문에 생성이 거절될 수 있다.",[50,51,52,65],"table",{},[53,54,55],"thead",{},[56,57,58,62],"tr",{},[59,60,61],"th",{},"관찰한 결과",[59,63,64],{},"먼저 구분할 문제",[66,67,68,77,85,93],"tbody",{},[56,69,70,74],{},[71,72,73],"td",{},"Unauthorized",[71,75,76],{},"자격증명과 인증 과정",[56,78,79,82],{},[71,80,81],{},"Forbidden",[71,83,84],{},"권한 또는 오류 메시지가 가리키는 정책",[56,86,87,90],{},[71,88,89],{},"필드 타입 검증 오류",[71,91,92],{},"매니페스트의 필드와 값",[56,94,95,98],{},[71,96,97],{},"요청 성공, Pod는 미준비",[71,99,100],{},"저장 이후의 생성, 배치, 실행 과정",[15,102,103],{},"etcd는 API 객체를 보관한다. 일반적으로 컨트롤러와 kubelet이 etcd를 직접 수정하는 것이 아니라 API Server를 통한다. 따라서 etcd와 API Server의 문제는 상태 조회와 새 변경에 큰 영향을 준다. 이미 실행 중인 컨테이너가 모두 즉시 종료된다고 단정할 수는 없다.",[10,105,107],{"id":106},"_2-deployment-replicaset-pod는-서로-다른-객체다","2. Deployment, ReplicaSet, Pod는 서로 다른 객체다",[15,109,110,113],{},[18,111,112],{},"Deployment는 Pod template과 배포를 관리하고, ReplicaSet은 그 template으로 유지할 Pod 개수를 관리한다."," Pod는 그 결과로 생기는 실행 단위다.",[23,115,118],{"className":116,"code":117,"language":28,"meta":29},[26],"Deployment demo-api: replicas=3, image=v1\n    ↓ Deployment Controller\nReplicaSet v1: desired=3\n    ↓ ReplicaSet Controller\nPod A, Pod B, Pod C\n",[31,119,117],{"__ignoreMap":29},[15,121,122],{},"Pod B만 지워졌다면 ReplicaSet이 가진 목표 3은 그대로다. 그래서 ReplicaSet Controller가 새 Pod를 만든다. 반대로 Deployment의 목표를 5로 바꾸면 Deployment Controller가 ReplicaSet의 목표를 변경하고, 이후 ReplicaSet Controller가 부족한 Pod를 만든다.",[15,124,125],{},"이미지를 바꾸는 경우에는 새 Pod template에 대응하는 ReplicaSet을 키우고 이전 것을 줄인다. 이 계층이 있어야 개수 조정과 버전 교체를 분리할 수 있다. 직접 만든 단독 Pod에는 동일한 복제본 유지 책임자가 없다는 점도 차이다.",[10,127,129],{"id":128},"_3-scheduler는-실행할-자리를-고른다","3. Scheduler는 실행할 자리를 고른다",[15,131,132,135],{},[18,133,134],{},"Pod 객체가 존재해도 Node가 정해지지 않았다면 컨테이너 실행은 시작되지 않는다."," Scheduler는 미배정 Pod가 들어갈 수 있는 노드를 먼저 찾고, 후보를 평가해 배정한다.",[15,137,138],{},"식당에 세 팀의 손님이 왔다고 해보자. 자리를 고르기 전에 각 테이블에 필요한 인원이 들어가는지 확인해야 한다. 쿠버네티스에서도 Pod의 자원 요청, 노드 조건, taint와 toleration, 배치 제약 등을 확인한다.",[23,140,143],{"className":141,"code":142,"language":28,"meta":29},[26],"Pod 생성\n  → 들어갈 수 없는 Node 제외\n  → 남은 후보 평가\n  → Node 배정 결과를 API에 기록\n",[31,144,142],{"__ignoreMap":29},[15,146,147,148,151,152,155],{},"모든 후보가 탈락하면 Pod는 배정을 기다린다. 다만 ",[31,149,150],{},"Pending","이라는 phase에는 이미지 준비 같은 다른 초기 과정도 포함될 수 있으므로, 이름만 보고 자원 부족으로 확정하지 않는다. ",[31,153,154],{},"describe pod","의 Node와 Events를 함께 봐야 한다.",[15,157,158,163],{},[159,160,162],"a",{"href":161},"\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-4\u002F","4편 모형","은 배정 시점을 보여주기 위해 Pod가 적은 Worker를 고른다. 실제 Scheduler의 자원 필터와 점수 계산 전체를 구현한 것은 아니다.",[10,165,167],{"id":166},"_4-kubelet은-런타임을-통해-컨테이너를-실행한다","4. kubelet은 런타임을 통해 컨테이너를 실행한다",[15,169,170,173],{},[18,171,172],{},"kubelet은 자기 노드에 배정된 Pod를 보고, 런타임에 필요한 컨테이너 실행을 요청한다."," containerd 같은 런타임이 이미지를 준비하고 컨테이너를 실행하며, kubelet은 상태와 프로브 결과를 보고한다.",[23,175,178],{"className":176,"code":177,"language":28,"meta":29},[26],"Scheduler가 Worker 2에 배정\n  → Worker 2의 kubelet이 Pod spec 확인\n  → CRI로 containerd에 준비와 실행 요청\n  → 컨테이너 Running\n  → kubelet의 readiness 검사\n  → Ready 조건 반영\n",[31,179,177],{"__ignoreMap":29},[15,181,182],{},"CRI는 이 둘이 대화하는 인터페이스다. Docker로 이미지를 만드는 일과 Kubernetes 노드가 런타임으로 실행하는 일은 이 지점에서 연결된다. 이미지 형식의 호환성과 런타임 연결 방식은 서로 다른 문제다.",[15,184,185,186,189],{},"네트워크를 붙이는 CNI, 볼륨 작업에 참여하는 CSI 드라이버도 실행 과정에 영향을 준다. 그래서 ",[31,187,188],{},"ContainerCreating","에서 멈추면 이미지뿐 아니라 볼륨과 네트워크 준비 Events도 살펴봐야 한다.",[10,191,193],{"id":192},"_5-이름-대신-label과-selector가-연결한다","5. 이름 대신 Label과 Selector가 연결한다",[15,195,196,199],{},[18,197,198],{},"관리 대상과 트래픽 대상을 찾을 때는 Pod의 개별 이름보다 Label과 Selector의 관계가 중요하다."," 새 Pod의 이름이 달라져도 연결이 이어질 수 있는 이유다.",[23,201,205],{"className":202,"code":203,"language":204,"meta":29,"style":29},"language-yaml shiki shiki-themes github-light github-dark","# Pod template의 이름표\nlabels:\n  app: demo-api\n\n# Service가 찾는 조건\nselector:\n  app: demo-api\n","yaml",[31,206,207,216,227,240,247,253,261],{"__ignoreMap":29},[208,209,212],"span",{"class":210,"line":211},"line",1,[208,213,215],{"class":214},"sJ8bj","# Pod template의 이름표\n",[208,217,219,223],{"class":210,"line":218},2,[208,220,222],{"class":221},"s9eBZ","labels",[208,224,226],{"class":225},"sVt8B",":\n",[208,228,230,233,236],{"class":210,"line":229},3,[208,231,232],{"class":221},"  app",[208,234,235],{"class":225},": ",[208,237,239],{"class":238},"sZZnC","demo-api\n",[208,241,243],{"class":210,"line":242},4,[208,244,246],{"emptyLinePlaceholder":245},true,"\n",[208,248,250],{"class":210,"line":249},5,[208,251,252],{"class":214},"# Service가 찾는 조건\n",[208,254,256,259],{"class":210,"line":255},6,[208,257,258],{"class":221},"selector",[208,260,226],{"class":225},[208,262,264,266,268],{"class":210,"line":263},7,[208,265,232],{"class":221},[208,267,235],{"class":225},[208,269,239],{"class":238},[15,271,272,273,276],{},"Deployment의 ",[31,274,275],{},"selector.matchLabels","와 Pod template의 Label도 맞아야 한다. 개별 Pod 이름은 진단과 삭제에 사용할 수 있지만, 계속 교체되는 Pod 집합을 연결할 때는 이 분류 조건을 사용한다.",[15,278,279],{},"Namespace는 리소스의 이름과 권한, 쿼터를 나누는 논리적 범위다. Namespace가 다르다는 사실만으로 Pod 사이 통신이 차단되지는 않는다. Node와 PV처럼 Namespace에 속하지 않는 객체도 있다.",[10,281,283],{"id":282},"_6-장애가-난-컴포넌트와-영향을-나눠-본다","6. 장애가 난 컴포넌트와 영향을 나눠 본다",[15,285,286,289],{},[18,287,288],{},"관리 기능이 멈춘 것과 현재 사용자 요청이 끊긴 것은 구분해서 확인해야 한다."," 새 배포는 실패하는데 기존 앱은 응답하는 상황도 가능하다.",[50,291,292,302],{},[53,293,294],{},[56,295,296,299],{},[59,297,298],{},"멈춘 부분",[59,300,301],{},"우선 확인할 영향",[66,303,304,312,320,328,336,344],{},[56,305,306,309],{},[71,307,308],{},"API Server 또는 etcd",[71,310,311],{},"상태 조회와 변경 요청이 가능한가",[56,313,314,317],{},[71,315,316],{},"Controller",[71,318,319],{},"Pod 개수 복구와 롤아웃이 진행되는가",[56,321,322,325],{},[71,323,324],{},"Scheduler",[71,326,327],{},"새 Pod가 Node에 배정되는가",[56,329,330,333],{},[71,331,332],{},"특정 Node의 kubelet",[71,334,335],{},"그 Node의 Pod 관리와 상태 보고가 되는가",[56,337,338,341],{},[71,339,340],{},"Service 규칙을 관리하는 구성 요소",[71,342,343],{},"바뀐 연결 대상이 노드에 반영되는가",[56,345,346,349],{},[71,347,348],{},"CoreDNS",[71,350,351],{},"서비스 이름을 주소로 해석할 수 있는가",[15,353,354],{},"기존 네트워크 규칙이나 연결이 남아 있으면 일부 트래픽은 계속 흐를 수 있다. 표는 원인을 확정하는 답안이 아니라, 증상에서 다음 확인 지점을 정하는 지도다.",[10,356,358],{"id":357},"자료와-다음-파트","자료와 다음 파트",[15,360,361,364,365,369,370,374],{},[18,362,363],{},"다음에는 이 내부 흐름이 kubectl 출력에 어떻게 보이는지 읽어 본다."," ",[159,366,368],{"href":367},"\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-3\u002F","3편, kubectl과 Pod 상태","으로 이어진다. ",[159,371,373],{"href":372},"\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-1\u002F","전체 목차","에서도 이동할 수 있다.",[15,376,377,378,384],{},"배포 계층과 컨트롤러의 동작은 ",[159,379,383],{"href":380,"rel":381},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fworkloads\u002Fcontrollers\u002Fdeployment\u002F",[382],"nofollow","Kubernetes Deployment 문서","에서 더 확인할 수 있다.",[386,387,388],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":29,"searchDepth":218,"depth":218,"links":390},[391,392,393,394,395,396,397,398],{"id":12,"depth":218,"text":13},{"id":38,"depth":218,"text":39},{"id":106,"depth":218,"text":107},{"id":128,"depth":218,"text":129},{"id":166,"depth":218,"text":167},{"id":192,"depth":218,"text":193},{"id":282,"depth":218,"text":283},{"id":357,"depth":218,"text":358},[400,401],"CI-CD-Docker","kubernetes","2026-09-07 08:01:00 +0900","API Server에서 ReplicaSet, Scheduler, kubelet과 containerd까지, Pod가 만들어지는 흐름으로 구성 요소의 역할을 이해한다.","md",null,[407,408,409,410],"Control Plane: API와 컨트롤러, 스케줄링을 담당하는 관리 영역","ReplicaSet Controller: 관리하는 Pod의 개수를 맞춘다","Scheduler: 미배정 Pod가 실행될 Node를 선택한다","CRI: kubelet과 컨테이너 런타임 사이의 인터페이스","\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-2\u002F",{},"2","\u002Fposts\u002Fci-cd-docker\u002Fkubernetes\u002F2026-09-07-kubernetes-part-2",{"title":5,"description":403},"직접 실험하는 Kubernetes","posts\u002FCI-CD-Docker\u002Fkubernetes\u002F2026-09-07-kubernetes-part-2","API Server는 요청과 상태 변경의 관문이고, 컨트롤러는 객체의 목표를 맞추며, Scheduler는 노드를 고른다. kubelet은 자기 노드에 배정된 Pod를 런타임으로 실행하고 상태를 보고한다. 이 역할의 경계가 장애를 좁히는 기준이 된다.",[420,421,324,422,423],"Kubernetes","ControlPlane","kubelet","containerd","CZNKcfQ5rbfblTNy-TVnLZbBydYISUk9frS3pcYWUKQ",1788744788141]