이 글은 “쿠버네티스가 실제로 어떻게 돌아가는지”를 컴포넌트 흐름(데이터/제어) 관점으로 정리한 Chapter 1입니다.
1) Docker 이야기부터 시작하는 이유: “Docker = 런타임”이 아니라 “Docker 안에 런타임이 포함”이었기 때문
강의에서 “쿠버네티스가 Docker 지원을 중단했다”라는 말이 초보자를 혼란스럽게 만드는 이유는, Docker가 한 덩어리로 보이기 때문입니다.
Docker는 여러 구성요소의 묶음(bundle)이다
- Docker CLI / Docker API: 개발자가 쓰는 도구
- 이미지 빌드(빌더), 레지스트리 연동, 인증, 볼륨 등 부가 기능
- 그리고 내부에 컨테이너 런타임(runc 등) 과 이를 관리하는 데몬
쿠버네티스 입장에서는 “도커라는 브랜드”가 필요한 게 아니라, 컨테이너를 실제로 실행하는 런타임이 필요합니다.
CRI(Container Runtime Interface): 쿠버네티스가 런타임과 대화하는 표준 인터페이스
- 쿠버네티스는 CRI를 통해 런타임에 “이미지 풀”, “컨테이너 생성/시작/중지” 같은 작업을 요청합니다.
- containerd 같은 런타임은 CRI 호환이라 쿠버네티스와 직접 붙을 수 있습니다.
- 그래서 “Docker 전체”를 쿠버네티스 런타임으로 쓰지 않아도 되었고, 결과적으로 “Docker 지원 중단” 같은 표현이 생겼습니다.
“도커 설치 안 돼도 이미지 pull 가능한가?”
가능합니다. 노드에 Docker가 아니라 containerd 같은 CRI 런타임이 있으면 kubelet이 그 런타임을 통해 이미지를 pull하고 컨테이너를 실행합니다.
“containerd는 pull은 되는데 build는 안 되나?”
정확히는:
- containerd는 “컨테이너 실행/이미지 관리” 쪽에 초점이 있고,
- “이미지 빌드(build)”는 보통 Docker build, BuildKit, kaniko, buildah 같은 별도 빌더 계열 도구를 씁니다.
즉, 빌드는 런타임의 주업무가 아닙니다.
ctr vs nerdctl vs crictl
- ctr: containerd 기본 제공 / containerd 전용 / 디버깅용 / 사용자 친화적 아님
- nerdctl: containerd 커뮤니티 / containerd 전용 / 일반 사용(Docker 대체) / Docker와 유사
- crictl: Kubernetes 커뮤니티 / CRI 전체 지원 / 디버깅용 / kubelet 관점과 맞춰 사용
- 일반적으로 kubectl에 문제가 생겨서 안될 경우 대부분 crictl로 처리가 되며, crictl도 안될 경우 nerdctl 사용하면 됨
2) 쿠버네티스 아키텍처: 요청 한 번이 클러스터를 바꾸는 실제 흐름
쿠버네티스를 “오브젝트 시스템”으로 이해하면 핵심이 빨리 잡힙니다.
오브젝트는 결국 etcd에 저장되고, 그 상태를 맞추려고 컴포넌트들이 움직입니다.
(1) etcd: 클러스터의 “진실의 원천(Source of Truth)”
- etcd는 분산 Key-Value 저장소입니다.
- 쿠버네티스에서 저장되는 것: 노드, 파드, 설정(ConfigMap), 시크릿, 계정/권한(Role/Binding) 등 “클러스터 상태” 전부.
kubectl get ...로 보이는 대부분의 정보는 결국 etcd에 저장된 오브젝트를 기반으로 합니다.
(2) kube-apiserver: 클러스터의 “관문”이자 “유일한 etcd 직접 접근자”
강의에서 강조된 문장 그대로가 핵심입니다.
kubectl은 사실상 API 서버를 호출하는 클라이언트입니다.- API 서버는 요청을
- 인증(Authentication)
- 인가(Authorization)
- 유효성 검증(Validation)
- etcd 읽기/쓰기
순으로 처리합니다.
- etcd와 직접 상호작용하는 유일한 컴포넌트가 kube-apiserver입니다.
- 스케줄러, 컨트롤러 매니저, kubelet 등은 모두 “API 서버를 통해” 클러스터를 바꿉니다.
(3) “Pod 생성 요청이 오면 실제로 뭐가 생기는가?”
강의 흐름을 그대로 정리하면:
- 사용자가 Pod 생성 요청(예:
kubectl apply -f pod.yaml) - API 서버가 Pod 오브젝트를 생성하고 etcd에 저장
- 이 단계에서 Pod는 “오브젝트로서 존재”합니다.
- 스케줄러가 “nodeName이 없는 Pod”를 감지하고 노드 선택
- API 서버가 스케줄링 결과(바인딩)를 반영(역시 etcd 업데이트)
- 해당 노드의 kubelet이 “내 노드에 배정된 Pod”를 보고 컨테이너 런타임에 실행 요청
- kubelet이 상태를 API 서버에 보고 → etcd 업데이트
“노드가 할당되지 않은 새 Pod”는 Pod가 없다는 뜻인가?
아닙니다.
- “오브젝트로서의 Pod”는 이미 API 서버/etcd에 존재합니다.
- 다만 아직 어느 노드에서 실행할지 결정되지 않았고, 실제 컨테이너도 안 떠서 “Pending”일 수 있습니다.
즉 “오브젝트는 생겼고, 실행은 아직” 상태입니다.
3) 컨트롤러는 왜 “쿠버네티스의 두뇌”인가: 원하는 상태(Desired State)를 맞추는 자동화 프로세스
컨트롤러의 정체
- 컨트롤러는 “헬스체크를 직접 때리는 녀석”이라기보다,
- API 서버를 통해 오브젝트 상태를 계속 관찰하고,
“원하는 상태와 다르면 필요한 조치(새로 만들기/지우기 등)를 수행”하는 프로세스입니다.
“레플리케이션 컨트롤러가 Pod 상태를 어떻게 아나?”
핵심은 이 구조입니다.
- kubelet이 노드에서 Pod/컨테이너 상태를 관찰
- 그 상태를 API 서버에 보고
- API 서버는 etcd를 업데이트
- 컨트롤러는 API 서버를 통해 “현재 상태”를 보고 판단
즉, 보통 컨트롤러가 Pod에 직접 헬스체크를 쏘는 구조로 이해하기보다, 클러스터 상태 데이터(오브젝트 상태)를 보고 조치한다고 이해하는 게 맞습니다.
4) 스케줄러 vs 컨트롤러 매니저: 둘 다 “감시”하지만 역할이 다르다
둘 다 API 서버(정확히는 그 뒤의 상태 변화)를 “감시”하는 것처럼 보이지만, 책임이 다릅니다.
kube-scheduler
- 책임: Pod를 어느 노드에 배치할지 결정
- 실제로 노드에 Pod를 “만드는 작업”은 하지 않음 (그건 kubelet)
스케줄러는 보통 다음과 같은 로직을 수행합니다.
- 필터링: 조건에 안 맞는 노드 제거(리소스 부족 등)
- 스코어링: 남은 노드를 점수화해 최적 노드 선택
kube-controller-manager
- 책임: 노드, 레플리카, 엔드포인트 등 다양한 컨트롤러를 묶어서 실행
- 예: 노드 컨트롤러는 노드 상태를 보고 장애 시 조치, 레플리카 관련 컨트롤러는 “개수 유지” 조치 등
5) kubelet: “노드의 선장”이 실제 실행을 담당한다
kubelet은 워커 노드에서 다음을 수행합니다.
- 노드를 클러스터에 등록
- API 서버와 통신하는 워커 노드의 대표 에이전트
- “내 노드에 배정된 Pod”를 보고 컨테이너 런타임에 실행 요청
- 상태를 주기적으로 보고
중요 포인트:
- kubeadm으로 클러스터를 올려도 kubelet은 “워커 노드에 설치/구동”이 전제인 경우가 많고(환경에 따라 자동화 정도는 다름),
- 실행 주체는 kubelet입니다. 스케줄러는 결정만 합니다.
- kubelet도 전반적인 운영을 담당하고 pod를 띄어라 등은 cri호출을 통해 containerd에게 명령
6) kube-proxy와 Service: “서비스는 가상 오브젝트지만, 트래픽은 실제로 흘러야 한다”
서비스는 “실체가 없는 가상 컴포넌트”
- Pod처럼 컨테이너가 떠서 listen 하는 프로세스가 아닙니다.
- “클러스터 메모리/오브젝트로 존재하는 추상”에 가깝습니다.
그런데 왜 서비스 IP로 접속이 되나?
여기서 kube-proxy가 등장합니다.
- kube-proxy는 각 노드에서 실행되는 프로세스
- 새 서비스가 생기면 iptables 같은 규칙을 만들어
- 서비스 IP로 들어오는 트래픽을
- 실제 백엔드 Pod IP로 전달하게 합니다.
즉, 서비스는 추상이고, kube-proxy가 노드 단의 네트워크 규칙으로 “추상을 현실로 만드는 역할”을 합니다.
7) Pod
“2개 이상 컨테이너가 절대 없다고 가정”하면 Pod의 장점이 줄어드는 건 맞습니다.
하지만 그럼에도 쿠버네티스가 Pod를 최소 단위로 삼은 이유는 다음 “샌드박스 단위” 때문입니다.
Pod = 컨테이너를 담는 “실행 샌드박스”
- Pod는 애플리케이션의 단일 인스턴스다.
- Pod는 Kubernetes에서 생성할 수 있는 가장 작은 배포 단위(오브젝트) 다.
스케일링은 “컨테이너 추가”가 아니라 “Pod 추가”로 한다
- 보통 컨테이너와 Pod는 1:1 관계
- 스케일 업: Pod를 새로 만든다
- 스케일 다운: 기존 Pod를 삭제한다
- 애플리케이션 확장을 위해 기존 Pod에 컨테이너를 추가하지 않는다
Pod에 여러 컨테이너?
Pod는 일반적으로 컨테이너와 1:1로 쓰이지만, 한 Pod 안에 여러 컨테이너를 넣을 수도 있다.
단, 중요한 전제가 있다.
- “같은 종류의 컨테이너를 여러 개 넣어서 스케일링”하려는 목적이라면 → 잘못된 접근
스케일링은 여전히 Pod를 늘리는 방식이다. - 멀티 컨테이너 Pod는 보통 서로 다른 역할의 컨테이너를 ‘붙여서’ 배치하는 경우에 쓴다.
특징
- 두 컨테이너는 함께 생성되고 함께 종료된다(“같은 운명”)
- 같은 네트워크 공간을 공유하므로 서로를 localhost로 호출하며 통신 가능
- 같은 스토리지(볼륨)를 쉽게 공유할 수 있음
멀티 컨테이너 Pod를 Docker만으로 직접 운영하면 생기는 문제
- “어떤 앱 컨테이너 ↔ 어떤 헬퍼 컨테이너”가 짝인지 맵을 유지
- 링크/커스텀 네트워크로 컨테이너 간 통신을 직접 구성
- 컨테이너 간 공유 볼륨을 만들고, 그 관계도 따로 관리
- 앱 컨테이너가 죽으면 헬퍼도 필요 없어지니 헬퍼 컨테이너를 수동으로 종료
- 새 앱 인스턴스를 띄울 때마다 헬퍼도 함께 띄우는 운영 작업이 추가됨
8) YAML로 Pod 만들기: 쿠버네티스 정의 파일의 공통 골격
쿠버네티스 오브젝트 YAML은 대부분 공통 구조를 가집니다.
최상위 4대 필드
apiVersionkindmetadataspec
apiVersion은 v1만 있나?
리소스마다 다릅니다.
- Pod/Service 같은 핵심 리소스는 흔히
v1 - ReplicaSet/Deployment 같은 건 보통
apps/v1
즉 “v2/v3가 있냐”는 질문은 “리소스 그룹/버전에 따라 다르다”가 정확합니다.
kind는 왜 대문자로 쓰나?
- Kubernetes API가 정의한 “리소스 타입 이름”이기 때문입니다. (
Pod,Service,Deployment등) - YAML 파서는 대소문자에 관대하지 않습니다. 정확히 써야 합니다.
metadata의 name과 labels는 무엇인가?
metadata.name: 오브젝트의 고유 이름- 같은 네임스페이스 내에서 동일 kind는 보통 이름이 유니크해야 합니다.
kubectl get pods nginx처럼 참조하는 기준이 됩니다.
metadata.labels: 검색/그룹핑/선택을 위한 태그(Key-Value)- “이 Pod는 어떤 역할인가?”를 기계가 찾기 쉽게 붙이는 표식입니다.
- Service/ReplicaSet/Deployment가 Pod를 “선택”할 때 핵심이 됩니다.
중요한 뉘앙스:
- “Pod들을 그룹화한다”는 말은
- 레이블 자체가 태그를 붙이는 행위이고,
- Service/ReplicaSet은 selector로 그 레이블을 기준 삼아 ‘논리적 그룹’을 만든다
로 이해하면 정확합니다.
즉, 그룹의 기준 데이터는 레이블이고, “그 레이블 집합을 서비스/레플리카셋이 채택해서 엔드포인트/대상 집합을 구성한다”가 전체 그림입니다.
9) Pod YAML 실습 예시 (이론 + CLI 생성 템플릿)
(1) 가장 기본 Pod YAML
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
tier: frontend
spec:
containers:
- name: nginx
image: nginx
(2) CLI로 YAML 템플릿 뽑기 (시험/실습에서 시간 절약)
--dry-run=client: 실제 생성하지 않고 “가능 여부만” 확인-o yaml: YAML로 출력
kubectl run nginx --image=nginx --dry-run=client -o yaml
-o의 의미
--output 옵션의 축약입니다. 즉 -o yaml은 “출력 형식을 yaml로 해라” 입니다.
10) ReplicaController vs ReplicaSet vs Deployment: “상태 유지”의 계층 구조
10.1 ReplicationController(구형)와 ReplicaSet(신형)
둘 다 목적은 같습니다.
- “지정한 개수만큼 Pod가 항상 살아있게 유지”
차이를 한 문장으로 요약하면:
- ReplicaSet은 selector가 필수이고, 더 유연한 매칭을 지원합니다.
- ReplicationController는 구형이고, 현대 쿠버네티스에서는 ReplicaSet을 권장합니다.
“차이가 단순 selector뿐인가?”
학습 관점에서 가장 큰 차이를 그렇게 잡아도 됩니다.
특히 강의 맥락에서도 “ReplicaSet은 selector를 반드시 정의해야 한다”가 핵심 포인트였습니다.
10.2 selector에 걸리는 Pod가 이미 존재하면?
ReplicaSet은 “선택된 Pod 수”를 보고 desired replicas를 맞춥니다.
- replicas: 3
- selector로 잡히는 기존 Pod: 3개
→ 새로 만들지 않습니다. 이미 충족했기 때문입니다. - replicas: 3
- selector로 잡히는 기존 Pod: 4개
→ 줄여야(desired=3) 하므로 보통 하나를 종료시키는 방향으로 수렴합니다.
(어떤 Pod가 정리될지는 상황/컨트롤러 판단에 따라 달라질 수 있지만 “개수 맞추기”가 목표입니다.)
10.3 Deployment: 운영에서 필요한 “롤링 업데이트/롤백/일시정지”의 주체
Deployment는 ReplicaSet의 상위 오브젝트입니다.
- Deployment를 만들면 내부적으로 ReplicaSet이 생성되고
- ReplicaSet이 Pod를 생성합니다.
Deployment가 제공하는 운영 기능:
- 롤링 업데이트(한 번에 다 바꾸지 않고 순차 교체)
- 롤백(업데이트 취소)
- 일시정지/재개(변경을 모아 한 번에 롤아웃)
11) Service 3종: NodePort / ClusterIP / LoadBalancer
11.1 NodePort: 외부에서 노드 IP:포트로 접근
- 노드의 특정 포트(기본 범위 30000~32767)를 열고
- 그 트래픽을 Pod로 전달
핵심 포트 3개:
targetPort: Pod(컨테이너)가 실제로 여는 포트 (예: 80)port: 서비스가 내부적으로 노출하는 포트 (보통 80)nodePort: 노드에서 외부 접근을 받는 포트 (예: 30080)
11.2 ClusterIP: 클러스터 내부 통신의 표준
- 서비스에 클러스터 내부 가상 IP + DNS 이름이 생김
- 프론트엔드 → 백엔드, 백엔드 → DB/Redis 같은 내부 계층 통신에 사용
11.3 LoadBalancer: 클라우드에서 “진짜 외부 LB”를 붙여줌
- AWS/GCP/Azure 같은 지원 클라우드에서 동작
- 로컬/미지원 환경에서는 NodePort처럼만 보일 수 있음
11.4 생성 명령어
- kubectl expose deployment web --type=NodePort --port=80 --target-port=8080
- 이런식으로 service를 생성할 수 있는데 이는 web이라는 이름을 갖는 Deployment를 찾아서 그것의 label을 가져와 selector로 맵핑시킴
12) kubectl expose vs create service: “같은 Service인데 왜 명령이 다르지?”
쿠버네티스에서 서비스를 만드는 CLI 경로가 여러 개라서 그렇습니다.
kubectl expose
- “기존 리소스(Pod/Deployment 등)를 노출(expose)한다”는 관점
- 장점: 대상 리소스의 labels를 자동으로 selector로 사용하는 경우가 많음
- 단점: NodePort 값 같은 것을 CLI에서 원하는 형태로 딱 지정 못하는 케이스가 생김(상황에 따라)
예:
kubectl expose pod redis --port=6379 --name=redis-service --dry-run=client -o yaml
이 명령은 파일을 자동 생성하지 않습니다.
표준 출력으로 YAML을 “찍어주는 것”이라서, 파일이 필요하면 리다이렉션이 필요합니다.
kubectl expose pod redis --port=6379 --name=redis-service --dry-run=client -o yaml > redis-service.yaml
kubectl create service ...
- “서비스 오브젝트를 직접 생성한다”는 관점
- 단점: 셀렉터가 자동으로 대상 Pod 레이블을 정확히 따라가지 않는 경우가 있어, 생성된 YAML을 수정해야 할 때가 있음
결론(시험/실전 팁):
- 빠르게 만들 땐
expose가 편한 경우가 많고, - 옵션 한계가 있으면
--dry-run -o yaml로 뽑아서 YAML 수정 후apply/create가 안정적입니다.
13) 네임스페이스: “이름/정책/리소스”의 경계
네임스페이스는 한 클러스터 안에서 리소스를 분리하는 가장 기본 단위입니다.
기본적으로 존재하는 것들:
default: 우리가 아무 생각 없이 작업하면 들어가는 곳kube-system: 쿠버네티스 내부 컴포넌트용 (사용자 실수로 건드리면 위험)kube-public: 모든 사용자가 볼 수 있어야 하는 일부 리소스용
왜 쓰나?
- dev/prod 분리 (같은 클러스터를 쓰되 실수 방지)
- 정책(RBAC) 분리
- 리소스 할당량(ResourceQuota)로 과다 사용 제한
네임스페이스 간 서비스 접근 DNS 형태
- 같은 네임스페이스면
db-service같은 이름만으로 접근 - 다른 네임스페이스면 보통:
서비스이름.네임스페이스.svc.cluster.local
14) kubectl config set-context --current --namespace=dev 상세 해부
kubectl config set-context --current --namespace=dev
kubectl config: kubeconfig(클러스터 접속 설정) 조작set-context: 컨텍스트(클러스터/유저/네임스페이스 묶음) 설정 변경--current: 현재 사용 중인 컨텍스트를 대상으로--namespace=dev: 기본 네임스페이스를 dev로 바꾸기
효과:
- 이후
kubectl get pods는 기본으로 dev 네임스페이스를 조회 - 다른 네임스페이스 보려면
-n default같은 옵션을 명시해야 함
15) Imperative vs Declarative: 시험/실무에서 둘 다 써야 하는 이유
Imperative(명령형)
- “이걸 만들어라 / 이걸 바꿔라”를 명령으로 즉시 수행
- 빠르지만, 실행 자체가 기록/공유에 취약
- 복잡한 요구사항은 커맨드가 길고 실수 확률이 늘어남
예:
kubectl run ...kubectl create deployment ...kubectl expose ...kubectl scale ...kubectl edit ...
Declarative(선언형)
- YAML로 “원하는 상태”를 선언하고
kubectl apply로 클러스터가 그 상태로 수렴하게 함- GitOps/리뷰/재현성에 강함
16) apply의 내부 동작: Local vs Live vs Last-Applied
kubectl apply는 “그냥 덮어쓰기”가 아닙니다.
강의의 핵심은 세 가지를 비교한다는 점입니다.
- Local configuration (내 YAML)
- Live object configuration (클러스터에 실제 존재하는 오브젝트)
- Last-applied configuration (마지막 apply 때 저장한 스냅샷)
Last-applied는 어디에 저장되나?
- 오브젝트의 annotation(주석)으로 저장됩니다.
- 그래서 apply로 만든 리소스는 “마지막 apply 값”을 기억할 수 있습니다.
- create/replace는 이 정보를 남기지 않는 흐름이므로 혼용에 주의가 필요합니다.
“local vs live만 비교하면 되지 않나?”가 헷갈리는 이유
헷갈리는 포인트를 정리하면 이렇습니다.
- Live는 현재 상태라서, 다른 시스템/사람이 imperative로 바꾼 값이 섞일 수 있습니다.
- Local에는 “내가 의도한 값”이 들어있고,
- Last-applied는 “내가 이전에 apply로 의도했던 값”이 남아있습니다.
apply가 Last-applied를 쓰는 결정적 이유:
- 필드 삭제 판단 때문입니다.
- Local에서 어떤 필드를 지웠을 때,
- 그게 “원래 없던 것”인지 “예전에 내가 넣었다가 지금 지운 것”인지
- live만 보고는 판단이 애매해질 수 있습니다.
- Last-applied를 보면 “이 필드는 과거에 내가 apply로 넣었는데, 지금 local에서 사라졌네 → 삭제 의도”라고 판단할 수 있습니다.
imperative로 live만 변경한 뒤 apply하면?
- apply는 3-way merge로 “내가 관리하는 영역(Last-applied 기준)”을 중심으로 반영합니다.
- 즉, “내가 apply로 관리하던 필드”는 local로 되돌릴 수 있고,
- “내가 건드린 적 없는 필드(Last-applied에도 없고 local에도 없는 필드)”는 보존될 가능성이 큽니다.
이게 단순 덮어쓰기와 다른 점입니다.
헷갈리는 점 정리
- Live object configuration과 Last-applied configuration는 사실상 같은 것 아닌가?
- Live는 현재 오브젝트에 실제로 적용된 애용
- Last-applied는 내가 마지막으로 apply -f 적용했던 내용
- Live는 Imperative 혹은 다른 시스템에서 건드리는 내용이 추가적으로 들어갈 수 있음
- apply시 그냥 그러면 local로 덮으면 되는 것 아닌가?
- Imperative 같은 것은 덮으면 되지만 다른 시스템에서 건드리던 내용은 건드리면 안됨
- 다른 시스템에서 건드린 내용은 Last-applied와 Local 둘 다에도 없는 것으로 판별 가능
17) kubectl 생산성 도구: api-resources / explain
kubectl api-resources
- “리소스 이름, 약칭, API 버전, 네임스페이스 범위” 등을 한 번에 확인
- 시험에서 리소스 약칭/대소문자/버전이 기억 안 날 때 매우 유용
kubectl explain
- 터미널에서 바로 스키마/필드 확인
kubectl explain podkubectl explain pod.speckubectl explain pod.spec --recursive(전체 필드 덤프)
이 흐름이 중요한 이유:
- 문서 검색 없이도 “이 리소스에 어떤 필드가 있지?”를 확인 가능
- YAML을 처음부터 만들기보다 필드를 빠르게 맞출 수 있음
18) 자주 나오는 CLI 실수 케이스 (이번 세션에서 나온 질문 기반)
1) “yml 자동 생성 아닌가?” → 출력만 하고 파일은 리다이렉션 필요
--dry-run=client -o yaml은 화면 출력입니다.- 파일로 저장하려면
>가 필요합니다.
2) kubectl create redis --image=redis -n finance가 왜 틀리나?
create는 리소스 타입을 정확히 요구합니다.kubectl create deployment ...kubectl create service ...kubectl create namespace ...
- 단순히 “redis라는 이름으로 이미지 실행”이면 보통
kubectl run을 씁니다.
3) kubectl run redis --image=redis:alpine --tier=db가 왜 틀리나?
kubectl run은 임의의--tier같은 플래그를 “레이블”로 해석하지 않습니다.- 레이블을 주려면 보통
--labels형태를 사용합니다(버전/명령 체계에 따라 차이가 있어, 시험에서는--dry-run -o yaml로 확인 후 확정하는 방식이 안전합니다).
4) kubectl expose pods httpd --port=80 --dry-run=client -o yaml 결과에 status:가 왜 있나?
kubectl이 출력하는 YAML 템플릿에 따라status가 포함되어 보일 수 있습니다.- 실제로 매니페스트로 적용할 때는 보통
status는 쿠버네티스가 관리하는 영역이라, 필요 시 제거하는 것이 일반적입니다. - 가장 안전한 루틴은:
- 템플릿 생성
- 필요한 필드만 남기고 정리
- apply/create
19) Chapter 1 정리: “쿠버네티스는 결국 상태(etcd) + 관문(API 서버) + 수렴(컨트롤러/스케줄러/kubelet) + 연결(Service/kube-proxy)”
이 챕터의 핵심 문장들을 한 번에 요약하면 아래와 같습니다.
- 클러스터의 상태는 etcd에 저장된다.
- etcd와 직접 대화하는 건 kube-apiserver뿐이다.
- 스케줄러는 “어디에 둘지” 결정하고, kubelet이 “실제로 실행”한다.
- 컨트롤러는 “원하는 상태로 유지”되도록 계속 수렴시킨다.
- 서비스는 가상 오브젝트지만 kube-proxy 규칙으로 트래픽이 흐르게 된다.
- 네임스페이스는 이름/정책/리소스를 나누는 경계다.
- 시험/실무 모두에서
--dry-run=client -o yaml,explain,api-resources,apply를 활용하면 속도와 정확도가 올라간다.
19) 헷갈리는 내용 간단한 추가 정리
- NodePort Service도 내부 통신은 ClusterIP 방식으로 동작한다. NodePort는 “노드IP:nodePort”라는 추가 진입점만 붙는 것이고, 클러스터 내부에서는 보통 서비스 DNS/ClusterIP로 접근한다.
- Service 생성에 도움되는 명령
- 생성: kubectl expose deploy/<name> ..., kubectl create service ..., --dry-run=client -o yaml로 YAML 뽑기
- 확인/디버깅: kubectl get svc -o wide, kubectl describe svc, kubectl get endpoints, kubectl get endpointslices, kubectl get pods -l ...
- 왜 deployment를 쓰나?
Service는 라우팅(주소/로드밸런싱)만 하고, Pod를 유지/복구/스케일/롤링업데이트/롤백하는 건 Deployment(ReplicaSet)가 담당한다. Pod만 띄우고 Service 붙이는 건 가능하지만 운영엔 취약하다. - kubectl expose web --port=...는 보통 안 됨
expose는 대상 리소스 타입이 필요해서 보통 kubectl expose deploy/web ... 또는 kubectl expose pod/webpod ...처럼 타입을 명시하는 게 안전하다. - kubectl expose pod webpod ...가 selector를 만드는 방식
Pod “이름”을 selector로 쓰는 게 아니라, Pod의 labels를 읽어서 Service spec.selector로 복사한다. 그래서 같은 라벨을 가진 Pod가 여러 개면 전부 선택된다. - Service 이름과 라벨은 별개
Service 이름은 기본적으로 expose한 리소스 이름을 따라간다(원하면 --name으로 변경 가능). 라벨은 selector(대상 선택) 용도다. - Service DNS 접근 규칙(같은 클러스터)
- 같은 네임스페이스: http://svc-name
- 다른 네임스페이스: http://svc-name.namespace (또는 svc-name.namespace.svc)
- 완전한 FQDN: svc-name.namespace.svc.cluster.local (도메인은 클러스터 설정에 따라 다를 수 있음)
- 다른 클러스터는 FQDN을 길게 쓴다고 접근되지 않음
클러스터 간 접근은 별도 연결/노출(LoadBalancer/Ingress/Gateway, VPC 연결+DNS, 멀티클러스터/서비스메시 등)이 필요하다. - 노드마다 /etc/resolv.conf는 다를 수 있지만, 중요한 건 Pod의 /etc/resolv.conf
일반 Pod는 dnsPolicy: ClusterFirst로 kubelet이 클러스터 DNS(CoreDNS)와 search domain을 Pod에 주입하므로, 어느 노드에 떠도 Pod 기준으로 일관되게 서비스 이름 해석이 된다(예외: dnsPolicy: Default, hostNetwork 조합 등).
'CKA' 카테고리의 다른 글
| Scheduling- Label & Selector (0) | 2025.12.29 |
|---|---|
| Scheduling- 수동 스케줄링 (0) | 2025.12.29 |
| Core Concepts - apply 커맨드 실행시 발생하는 비교 로직 (0) | 2025.12.28 |
| Core Concepts - 명령어 정리 (0) | 2025.12.28 |
| Core Concepts - Imperative vs Declarative (0) | 2025.12.28 |