[{"data":1,"prerenderedAt":812},["ShallowReactive",2],{"post-document:\u002Fci-cd-docker\u002Fkubernetes\u002F2026\u002F08\u002F26\u002Fkubernetes1\u002F":3},{"id":4,"title":5,"body":6,"categories":792,"date":795,"description":796,"extension":797,"image":798,"key_concepts":798,"last_modified_at":798,"legacyPath":799,"meta":800,"navigation":802,"part":798,"path":803,"published":802,"robots":798,"seo":804,"series":798,"stem":805,"strengths":798,"summary":806,"tags":807,"tradeoffs":798,"__hash__":811},"posts\u002Fposts\u002FCI-CD-Docker\u002Fkubernetes\u002Fkubernetes1.md","쿠버네티스 입문, 원하는 상태를 유지하는 시스템",{"type":7,"value":8,"toc":771},"minimark",[9,13,31,36,41,44,70,73,95,103,106,110,115,118,121,153,156,161,164,170,173,179,182,185,189,195,200,205,209,228,232,235,239,242,248,251,255,400,403,409,413,416,422,425,428,432,594,597,616,619,643,646,649,682,686,690,693,699,703,706,712,715,719,722,725,731,735,738,744,748,751,754,758,761,767],[10,11,12],"h1",{"id":12},"쿠버네티스",[14,15,16],"blockquote",{},[17,18,19,20,25,26,30],"p",{},"‘직접 실험하는 Kubernetes’의 ",[21,22,24],"a",{"href":23},"\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-1\u002F","6개 파트 학습 목차","와 ",[21,27,29],{"href":28},"\u002Fci-cd-docker\u002Fkubernetes\u002Fpart-4\u002F","3D Kubernetes 터미널 실험실","을 추가했다. 이 글은 처음 개념을 잡는 입문 노트다.",[14,32,33],{},[17,34,35],{},"쿠버네티스는 여러 컴퓨터에서 컨테이너를 원하는 상태로 실행하고, 그 상태가 깨지면 자동으로 복구하는 컨테이너 오케스트레이션 시스템",[37,38,40],"h2",{"id":39},"_1-docker와-쿠버네티스-차이","1. Docker와 쿠버네티스 차이",[17,42,43],{},"Docker는 컨테이너 하나를 만들고 실행하는 데 집중한다.",[45,46,51],"pre",{"className":47,"code":48,"language":49,"meta":50,"style":50},"language-bash shiki shiki-themes github-light github-dark","docker run nginx\n","bash","",[52,53,54],"code",{"__ignoreMap":50},[55,56,59,63,67],"span",{"class":57,"line":58},"line",1,[55,60,62],{"class":61},"sScJk","docker",[55,64,66],{"class":65},"sZZnC"," run",[55,68,69],{"class":65}," nginx\n",[17,71,72],{},"쿠버네티스는 다음과 같은 운영 문제를 담당",[74,75,76,80,83,86,89,92],"ul",{},[77,78,79],"li",{},"컨테이너를 어느 서버에서 실행할지 결정",[77,81,82],{},"컨테이너가 죽으면 다시 실행",[77,84,85],{},"사용자가 많아지면 여러 개로 확장",[77,87,88],{},"여러 컨테이너로 트래픽 분산",[77,90,91],{},"새 버전으로 점진적 교체",[77,93,94],{},"설정과 저장 공간 관리",[45,96,101],{"className":97,"code":99,"language":100,"meta":50},[98],"language-text","Docker\n    이미지 → 컨테이너 실행\n\nKubernetes\n    이미지 → 여러 서버에 배치 → 감시 → 복구 → 확장 → 트래픽 연결\n","text",[52,102,99],{"__ignoreMap":50},[17,104,105],{},"쿠버네티스가 컨테이너 이미지를 만드는 것으 아니다. 일반적으로 Docker등으로 이미지를 만들고 저장소에 올리면, 쿠버네티스가 그 이미지를 가져와 실행한다.",[37,107,109],{"id":108},"_2-가장-중요한-개념-원하는-상태","2. 가장 중요한 개념 : 원하는 상태",[14,111,112],{},[17,113,114],{},"선언적 명령",[17,116,117],{},"쿠버네티스는 명령을 하나씩 내리기 보다는 원하는 상태를 선언",[17,119,120],{},"예를 들어서",[45,122,126],{"className":123,"code":124,"language":125,"meta":50,"style":50},"language-yaml shiki shiki-themes github-light github-dark","replicas: 3\nimage: nginx:1.27\n","yaml",[52,127,128,142],{"__ignoreMap":50},[55,129,130,134,138],{"class":57,"line":58},[55,131,133],{"class":132},"s9eBZ","replicas",[55,135,137],{"class":136},"sVt8B",": ",[55,139,141],{"class":140},"sj4cs","3\n",[55,143,145,148,150],{"class":57,"line":144},2,[55,146,147],{"class":132},"image",[55,149,137],{"class":136},[55,151,152],{"class":65},"nginx:1.27\n",[17,154,155],{},"의미는 다음과 같다.",[14,157,158],{},[17,159,160],{},"nginx 1.27 컨테이너를 항상 3개 실행",[17,162,163],{},"그래서 3개의 Nginx 컨테이너가 정상적으로 돌아가고 있다면 아무것도 하지 않는다.",[45,165,168],{"className":166,"code":167,"language":100,"meta":50},[98],"현재 3개 = 원하는 상태 3개\n    → 유지\n",[52,169,167],{"__ignoreMap":50},[17,171,172],{},"근데 만약 1개가 죽었다?",[45,174,177],{"className":175,"code":176,"language":100,"meta":50},[98],"현재 2개 ≠ 원하는 상태 3개\n    → 새 컨테이너 1개를 생성\n    → 그리고서는 다시 3개로 복구한다.\n",[52,178,176],{"__ignoreMap":50},[17,180,181],{},"이처럼 현재 상태를 원하는 상태에 맞추는 것을 Reconciliation",[17,183,184],{},"즉, 조정(Reconciliation) 이라고 한다.",[37,186,188],{"id":187},"_3-전체-구조","3. 전체 구조",[45,190,193],{"className":191,"code":192,"language":100,"meta":50},[98],"Cluster\n├── Control Plane\n│   ├── API Server\n│   ├── Scheduler\n│   ├── Controller Manager\n│   └── etcd\n│\n└── Worker Node\n    ├── kubelet\n    ├── Container Runtime\n    └── Pod\n        └── Container\n",[52,194,192],{"__ignoreMap":50},[196,197,199],"h3",{"id":198},"cluster","Cluster",[74,201,202],{},[77,203,204],{},"쿠버네티스가 관리하는 전체 시스템, 여러 서버를 하나의 묶음으로 관리",[196,206,208],{"id":207},"control-plane","Control Plane",[74,210,211],{},[77,212,213,214],{},"클러스터의 두뇌\n",[74,215,216,219,222,225],{},[77,217,218],{},"사용자의 요청을 받음",[77,220,221],{},"현재 상태를 확인",[77,223,224],{},"어느 서버에서 실행할지 결정",[77,226,227],{},"원하는 상태와 다르면 수정",[196,229,231],{"id":230},"worker-node","worker Node",[17,233,234],{},"실제로 애플리케이션 컨테이너가 실행되는 서버",[196,236,238],{"id":237},"pod","Pod",[17,240,241],{},"쿠버네티스가 배포하는 가장 작은 실행 단위",[45,243,246],{"className":244,"code":245,"language":100,"meta":50},[98],"Pod\n└── nginx 컨테이너\n",[52,247,245],{"__ignoreMap":50},[17,249,250],{},"컨테이너를 쿠버네티스에 직접 배포하기보다는, 컨테이너를 감싼 Pod를 배포한다고 생각하면 좋다",[37,252,254],{"id":253},"_4-핵심-단어","4. 핵심 단어",[256,257,258,271],"table",{},[259,260,261],"thead",{},[262,263,264,268],"tr",{},[265,266,267],"th",{},"단어",[265,269,270],{},"의미",[272,273,274,282,289,297,304,312,320,328,336,344,352,360,368,376,384,392],"tbody",{},[262,275,276,279],{},[277,278,199],"td",{},[277,280,281],{},"쿠버네티스가 관리하는 전체 서버 묶음",[262,283,284,286],{},[277,285,208],{},[277,287,288],{},"클러스터를 관리하는 두뇌",[262,290,291,294],{},[277,292,293],{},"Node",[277,295,296],{},"쿠버네티스에 참여하는 서버",[262,298,299,301],{},[277,300,238],{},[277,302,303],{},"컨테이너가 실행되는 최소 단위",[262,305,306,309],{},[277,307,308],{},"Deployment",[277,310,311],{},"Pod의 개수, 이미지, 업데이트 방식을 관리",[262,313,314,317],{},[277,315,316],{},"Replica",[277,318,319],{},"동일하게 실행할 Pod의 개수",[262,321,322,325],{},[277,323,324],{},"Service",[277,326,327],{},"변경되는 Pod들을 하나의 고정된 주소로 연결",[262,329,330,333],{},[277,331,332],{},"Ingress",[277,334,335],{},"외부 HTTP 요청을 적절한 Service로 전달",[262,337,338,341],{},[277,339,340],{},"Namespace",[277,342,343],{},"리소스를 논리적으로 구분하는 공간",[262,345,346,349],{},[277,347,348],{},"ConfigMap",[277,350,351],{},"일반 설정값을 분리해 저장",[262,353,354,357],{},[277,355,356],{},"Secret",[277,358,359],{},"비밀번호, 토큰 같은 민감한 설정 관리",[262,361,362,365],{},[277,363,364],{},"Volume",[277,366,367],{},"컨테이너가 사용할 저장 공간",[262,369,370,373],{},[277,371,372],{},"PVC",[277,374,375],{},"필요한 저장 공간을 요청하는 객체",[262,377,378,381],{},[277,379,380],{},"Label",[277,382,383],{},"Pod 등의 리소스에 붙이는 분류표",[262,385,386,389],{},[277,387,388],{},"Selector",[277,390,391],{},"특정 Label을 가진 리소스를 선택하는 조건",[262,393,394,397],{},[277,395,396],{},"Manifest",[277,398,399],{},"원하는 상태를 작성한 YAML 파일",[17,401,402],{},"일단은 이 정도만 알고 있으면 되는 것은..",[45,404,407],{"className":405,"code":406,"language":100,"meta":50},[98],"Deployment\n    ↓ Pod 생성 및 개수 유지\nPod\n    ↓ 애플리케이션 실행\nService\n    ↓ 여러 Pod를 하나의 주소로 연결\nIngress\n    ↓ 외부 사용자의 요청을 Service로 전달\n",[52,408,406],{"__ignoreMap":50},[37,410,412],{"id":411},"_5-요청이-들어오는-흐름","5. 요청이 들어오는 흐름",[17,414,415],{},"웹 애플리케이션을 배포했다고 했을 때를 예시로 들어보자",[45,417,420],{"className":418,"code":419,"language":100,"meta":50},[98],"사용자\n    ↓\nIngress\n    ↓\nService\n    ↓\nPod 1, Pod 2, Pod 3\n    ↓\n컨테이너 애플리케이션\n",[52,421,419],{"__ignoreMap":50},[17,423,424],{},"여기서 Pod는 언제든 죽거나 새로 생성될 수 있기 때문에 IP 주소가 바뀔 수 있다.",[17,426,427],{},"사용자는 Pod에 직접 연결하지 않는다. Service가 고정된 접근 지점을 제공하고, 적잘한 Pod으로 요청을 전달한다.",[37,429,431],{"id":430},"_6-배포할-때-내부에서-벌어지는-일","6. 배포할 때 내부에서 벌어지는 일",[45,433,435],{"className":123,"code":434,"language":125,"meta":50,"style":50},"apiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: nginx\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n",[52,436,437,447,457,466,477,485,495,503,511,521,529,537,545,555,563,571,584],{"__ignoreMap":50},[55,438,439,442,444],{"class":57,"line":58},[55,440,441],{"class":132},"apiVersion",[55,443,137],{"class":136},[55,445,446],{"class":65},"apps\u002Fv1\n",[55,448,449,452,454],{"class":57,"line":144},[55,450,451],{"class":132},"kind",[55,453,137],{"class":136},[55,455,456],{"class":65},"Deployment\n",[55,458,460,463],{"class":57,"line":459},3,[55,461,462],{"class":132},"metadata",[55,464,465],{"class":136},":\n",[55,467,469,472,474],{"class":57,"line":468},4,[55,470,471],{"class":132},"  name",[55,473,137],{"class":136},[55,475,476],{"class":65},"nginx\n",[55,478,480,483],{"class":57,"line":479},5,[55,481,482],{"class":132},"spec",[55,484,465],{"class":136},[55,486,488,491,493],{"class":57,"line":487},6,[55,489,490],{"class":132},"  replicas",[55,492,137],{"class":136},[55,494,141],{"class":140},[55,496,498,501],{"class":57,"line":497},7,[55,499,500],{"class":132},"  selector",[55,502,465],{"class":136},[55,504,506,509],{"class":57,"line":505},8,[55,507,508],{"class":132},"    matchLabels",[55,510,465],{"class":136},[55,512,514,517,519],{"class":57,"line":513},9,[55,515,516],{"class":132},"      app",[55,518,137],{"class":136},[55,520,476],{"class":65},[55,522,524,527],{"class":57,"line":523},10,[55,525,526],{"class":132},"  template",[55,528,465],{"class":136},[55,530,532,535],{"class":57,"line":531},11,[55,533,534],{"class":132},"    metadata",[55,536,465],{"class":136},[55,538,540,543],{"class":57,"line":539},12,[55,541,542],{"class":132},"      labels",[55,544,465],{"class":136},[55,546,548,551,553],{"class":57,"line":547},13,[55,549,550],{"class":132},"        app",[55,552,137],{"class":136},[55,554,476],{"class":65},[55,556,558,561],{"class":57,"line":557},14,[55,559,560],{"class":132},"    spec",[55,562,465],{"class":136},[55,564,566,569],{"class":57,"line":565},15,[55,567,568],{"class":132},"      containers",[55,570,465],{"class":136},[55,572,574,577,580,582],{"class":57,"line":573},16,[55,575,576],{"class":136},"        - ",[55,578,579],{"class":132},"name",[55,581,137],{"class":136},[55,583,476],{"class":65},[55,585,587,590,592],{"class":57,"line":586},17,[55,588,589],{"class":132},"          image",[55,591,137],{"class":136},[55,593,152],{"class":65},[17,595,596],{},"적용 명령",[45,598,600],{"className":47,"code":599,"language":49,"meta":50,"style":50},"kubectl apply -f deployment.yaml\n",[52,601,602],{"__ignoreMap":50},[55,603,604,607,610,613],{"class":57,"line":58},[55,605,606],{"class":61},"kubectl",[55,608,609],{"class":65}," apply",[55,611,612],{"class":140}," -f",[55,614,615],{"class":65}," deployment.yaml\n",[17,617,618],{},"내부 흐름은 다음과 같다.",[620,621,622,625,628,631,634,637,640],"ol",{},[77,623,624],{},"Kubectl이 YAML을 API Server에 전달",[77,626,627],{},"API Server가 요청을 검사하고 etcd에 저장",[77,629,630],{},"Controller가 Pod 3개가 필요하다는 것을 확인",[77,632,633],{},"Scheduler가 각 Pod를 실행할 Node를 선택",[77,635,636],{},"각 Node의 kubelet이 컨테이너 이미지 다운로드",[77,638,639],{},"Container Runtime이 컨테이너 실행",[77,641,642],{},"Controller가 계속 상태를 감시",[17,644,645],{},"kubectl은 쿠버네티스 클러스터에 명령을 전달하는 도구",[17,647,648],{},"확인 할 때는 다음 명령을 사용한다.",[45,650,652],{"className":47,"code":651,"language":49,"meta":50,"style":50},"kubectl get deployments\nkubectl get pods\nkubectl get services\n",[52,653,654,664,673],{"__ignoreMap":50},[55,655,656,658,661],{"class":57,"line":58},[55,657,606],{"class":61},[55,659,660],{"class":65}," get",[55,662,663],{"class":65}," deployments\n",[55,665,666,668,670],{"class":57,"line":144},[55,667,606],{"class":61},[55,669,660],{"class":65},[55,671,672],{"class":65}," pods\n",[55,674,675,677,679],{"class":57,"line":459},[55,676,606],{"class":61},[55,678,660],{"class":65},[55,680,681],{"class":65}," services\n",[37,683,685],{"id":684},"_7-핵심-설계-철학","7. 핵심 설계 철학",[196,687,689],{"id":688},"선언적-관리","선언적 관리",[17,691,692],{},"\"컨테이너 하나를 실행해\" 라는 절차보다 \"항상 3개가 실행되어야 한다~\" 라는 결과를 선언",[45,694,697],{"className":695,"code":696,"language":100,"meta":50},[98],"명령형: 컨테이너를 하나 실행해\n선언형: 컨테이너가 항상 3개 존재해야 해\n",[52,698,696],{"__ignoreMap":50},[196,700,702],{"id":701},"지속적인-조정","지속적인 조정",[17,704,705],{},"쿠버네티스는 한 번 실행하고 끝나는 시스템이 아님",[45,707,710],{"className":708,"code":709,"language":100,"meta":50},[98],"현재 상태 관찰\n    → 원하는 상태와 비교\n    → 차이가 있으면 수정\n    → 다시 관찰\n",[52,711,709],{"__ignoreMap":50},[17,713,714],{},"이 반복 구조를 Control Loop",[196,716,718],{"id":717},"장애를-정상적인-상황으로-간주","장애를 정상적인 상황으로 간주",[17,720,721],{},"쿠버네티스는 Pod가 서버가 언젠가 고장난다고 가정",[17,723,724],{},"그래서 개별 Pod이 죽으면, 새로운 Pod으로 교체한다.",[45,726,729],{"className":727,"code":728,"language":100,"meta":50},[98],"기존 서버 운영: 이 서버가 절대 죽지 않도록 관리\n쿠버네티스:     죽을 수 있으니 자동으로 대체\n",[52,730,728],{"__ignoreMap":50},[196,732,734],{"id":733},"개별-객체보다-집합-관리","개별 객체보다 집합 관리",[17,736,737],{},"Pod 하나를 직접 관리하기보다, Deployment로 Pod의 집합을 관리한다.",[45,739,742],{"className":740,"code":741,"language":100,"meta":50},[98],"Pod 하나 직접 관리\n    → 죽으면 끝\n\nDeployment로 관리\n    → Pod가 죽으면 새 Pod 생성\n",[52,743,741],{"__ignoreMap":50},[196,745,747],{"id":746},"느슨한-연결","느슨한 연결",[17,749,750],{},"Deployment는 실행, Service는 네트워크 연결, ConfigMap은 설정을, Volume은 저장 공간을 담당한다.",[17,752,753],{},"기능을 분리해 필요한 부분만 교체할 수 있게 설계되어 있다.",[196,755,757],{"id":756},"불변-이미지","불변 이미지",[17,759,760],{},"실행 중인 컨테이너 내부를 직접 수정하지 않고, 새 이미지를 만들고 기존 Pod를 새 Pod로 교체",[45,762,765],{"className":763,"code":764,"language":100,"meta":50},[98],"nginx:1.0 Pod\n    ↓ 점진적 교체\nnginx:2.0 Pod\n",[52,766,764],{"__ignoreMap":50},[768,769,770],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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);}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":50,"searchDepth":144,"depth":144,"links":772},[773,774,775,781,782,783,784],{"id":39,"depth":144,"text":40},{"id":108,"depth":144,"text":109},{"id":187,"depth":144,"text":188,"children":776},[777,778,779,780],{"id":198,"depth":459,"text":199},{"id":207,"depth":459,"text":208},{"id":230,"depth":459,"text":231},{"id":237,"depth":459,"text":238},{"id":253,"depth":144,"text":254},{"id":411,"depth":144,"text":412},{"id":430,"depth":144,"text":431},{"id":684,"depth":144,"text":685,"children":785},[786,787,788,789,790,791],{"id":688,"depth":459,"text":689},{"id":701,"depth":459,"text":702},{"id":717,"depth":459,"text":718},{"id":733,"depth":459,"text":734},{"id":746,"depth":459,"text":747},{"id":756,"depth":459,"text":757},[793,794],"CI-CD-Docker","kubernetes","2026-08-26 07:11:14 +0900","Docker와 Kubernetes의 역할, 원하는 상태와 조정 루프, Pod와 Deployment, Service의 관계를 먼저 살펴본다.","md",null,"\u002Fci-cd-docker\u002Fkubernetes\u002F2026\u002F08\u002F26\u002Fkubernetes1\u002F",{"layout":801},"post",true,"\u002Fposts\u002Fci-cd-docker\u002Fkubernetes\u002Fkubernetes1",{"title":5,"description":796},"posts\u002FCI-CD-Docker\u002Fkubernetes\u002Fkubernetes1","쿠버네티스는 여러 노드에서 컨테이너를 원하는 상태로 실행하고, 실제 상태가 달라지면 컨트롤러가 차이를 줄인다. 기본 구조를 이해한 뒤 파트별 글과 3D 모의 실습으로 이어갈 수 있다.",[808,809,238,308,810],"Kubernetes","Docker","Reconciliation","u7-Rwqj6X1M_1VGJmEHhlMPy9nL5RnhfkBinujg8KaA",1788744788326]