Kubernetes 처음 접하면 YAML 파일 50 개가 어디서 어디로 흐르는지 그림이 안 그려진다. Helm chart 의 render 결과를 받아도 막막. 이 가이드는 Kubernetes 매니페스트의 공통 구조 4 가지 (apiVersion / kind / metadata / spec), 실무에서 자주 만나는 리소스 종류, Service 가 Pod 를 어떻게 찾는지, 그리고 입문자가 자주 빠지는 함정을 정리한다.
모든 매니페스트의 공통 구조
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: production
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27네 가지 최상위 필드:
- apiVersion — 리소스 정의를 누가 책임지는가 (API group/version). 예:
v1(core),apps/v1(Deployment 등),networking.k8s.io/v1(Ingress). - kind — 리소스 타입. 예:
Deployment,Service,ConfigMap. - metadata — 식별 정보.
name,namespace,labels,annotations. - spec — desired state. 이 안에 진짜 설정이 들어감. kind 별로 구조가 완전히 다름.
status 라는 5 번째 필드도 있지만 클러스터가 채우는 것이라 매니페스트엔 안 박는다.
왜 YAML 인가
Kubernetes API 자체는 JSON 으로 통신. kubectl apply -f 가 YAML 을 JSON 으로 변환해 서버로 보낸다. YAML 을 쓰는 이유:
- 주석 가능 — 운영 노하우를 매니페스트 옆에 박을 수 있음.
- 긴 문자열 친화 — YAML 의 multi-line literal (
|,>) 이 cert 나 script 박기 편함. - 따옴표 적음 — JSON 보다 시각적 노이즈 적음.
대신 indent 민감, 탭/공백 혼용 사고, anchor (&name /*name) 의도치 않은 사용 등 함정이 있다. YAML ↔ JSON 변환 으로 양방향 변환해 보면 YAML 이 실제 어떤 JSON 구조로 풀리는지 직관적.
핵심 리소스 — 14 종 분류
워크로드 (Workload) — 컨테이너를 실행
- Deployment — stateless 서비스. 가장 흔함. replica 수 + pod template + 롤링 업데이트.
- StatefulSet — stateful (DB, 큐). 안정된 pod 이름 (
web-0,web-1) + PVC 영속. - DaemonSet — 모든 노드에 1 개씩 (로깅 agent, network plugin).
- Job — 1 회 실행 후 종료 (마이그레이션, batch).
- CronJob — 스케줄링된 Job. Linux cron 과 같은 표현식.
- Pod — 최소 단위. 컨테이너 1+. 직접 만드는 일은 거의 없음 — 위 워크로드가 자동 생성.
네트워크
- Service — pod 집합에 안정된 virtual IP + DNS. 세 가지 타입:
ClusterIP(내부),NodePort(노드 포트),LoadBalancer(cloud LB). - Ingress — HTTP/HTTPS 라우팅 layer. 도메인 + path → Service 매핑.
- NetworkPolicy — pod 간 통신 방화벽 규칙.
설정·시크릿·스토리지
- ConfigMap — key-value 설정 (env 변수, 파일).
- Secret — 시크릿 (base64 인코딩, 암호화 X 가 기본 — 별도 etcd encryption 필요).
- PersistentVolumeClaim (PVC) — 영속 디스크 요청. StorageClass 가 실제 PV provision.
스케일링·접근 제어
- HorizontalPodAutoscaler (HPA) — CPU/메모리/커스텀 메트릭으로 replica 자동 조정.
- ServiceAccount — pod 가 API 호출할 때의 identity.
- Role / RoleBinding — RBAC. ServiceAccount 의 권한 정의.
- Namespace — 논리적 분리 (dev/staging/prod, 팀별).
이 모든 종류를 한눈에 보려면 매니페스트를 Kubernetes YAML 시각화 에 붙여넣으면 리소스 + 관계 그래프가 즉시 나온다.
관계의 핵심 — 어떻게 연결되나
K8s 의 가장 헷갈리는 부분이 "이 Service 는 어떤 Pod 와 연결?", "이 Deployment 는 어떤 ConfigMap 을 사용?" 같은 관계. 이름으로 직접 가리키 는 게 아니라 label selector 라는 간접 방식이 핵심.
Service → Pod: label selector
# Pod template 의 labels
apiVersion: apps/v1
kind: Deployment
metadata: {name: web}
spec:
selector:
matchLabels: {app: web, tier: frontend}
template:
metadata:
labels: {app: web, tier: frontend} # ← 이 labels
spec: {containers: [...]}
---
# Service 가 selector 로 위 labels 매칭
apiVersion: v1
kind: Service
metadata: {name: web}
spec:
selector: {app: web, tier: frontend} # ← 모든 key=value 일치하는 Pod
ports: [{port: 80, targetPort: 8080}]주의: Service 의 selector 와 Deployment 의 spec.selector.matchLabels 는 다른 역할.
- Deployment.spec.selector.matchLabels — Deployment 가 어떤 pod 을 자기 것으로 인식할지.
- Deployment.spec.template.metadata.labels — 실제 생성되는 pod 의 labels.
- Service.spec.selector — Service 가 어떤 pod 으로 트래픽 보낼지.
Deployment 의 두 selector 는 일치해야 함 (안 그러면 deploy 실패). Service 의 selector 는 위 labels 의 부분집합이면 매칭.
Ingress → Service: 직접 이름 참조
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: {name: web}
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web # ← Service 이름 직접 참조
port: {number: 80}Deployment → ConfigMap / Secret: 3 가지 방법
Pod 가 ConfigMap / Secret 을 사용하는 방식:
spec:
containers:
- name: web
# 방법 1: envFrom — 모든 key 를 env 변수로
envFrom:
- configMapRef: {name: web-config}
- secretRef: {name: web-secrets}
# 방법 2: env.valueFrom — 특정 key 만 골라서
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: web-secrets
key: database-url
# 방법 3: volumeMounts — 파일로 마운트
volumeMounts:
- name: config
mountPath: /etc/web
volumes:
- name: config
configMap: {name: web-config}Deployment → PVC: volumes
spec:
containers:
- name: db
volumeMounts:
- {name: data, mountPath: /var/lib/db}
volumes:
- name: data
persistentVolumeClaim: {claimName: db-data}HPA → Deployment: scaleTargetRef
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: {name: web-hpa}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 3
maxReplicas: 10multi-doc YAML — --- 의 의미
한 파일에 여러 리소스를 박을 때 --- 로 구분. Kubernetes 의 관례.
apiVersion: apps/v1
kind: Deployment
metadata: {name: web}
spec: {...}
---
apiVersion: v1
kind: Service
metadata: {name: web}
spec: {...}
---
apiVersion: v1
kind: ConfigMap
metadata: {name: web-config}
data: {...}kubectl apply -f all.yaml 가 위 3 개를 모두 적용. Helm / Kustomize render 결과도 보통 multi-doc YAML.
namespace 의 의미
metadata.namespace 가 같은 리소스끼리만 자동 연결. 다른 namespace 의 Service 를 부르려면 FQDN: web.production.svc.cluster.local.
namespace 명시 안 하면 default. 운영 환경은 명확히 분리 권장 — production, staging, 팀별 등.
흔히 빠지는 함정
1. selector 와 labels 불일치
Service 의 selector 와 pod 의 labels 한 글자 차이로 트래픽 0. "왜 503 만 나오지?" 의 가장 흔한 원인. Kubernetes YAML 시각화 는 selector ↔ labels 매칭 검증해 시각화 — 연결 안 되면 화살표 안 나옴.
2. Deployment 의 두 selector 불일치
spec.selector.matchLabels 와 spec.template.metadata.labels 가 일치 안 하면 kubectl apply 가 거부. 처음엔 헷갈림.
3. ConfigMap / Secret 변경 후 pod 재시작 안 됨
ConfigMap / Secret 만 update 해도 pod 가 자동 재시작 X (envFrom 케이스). kubectl rollout restart 또는 매니페스트의 annotation 에 checksum 박는 패턴 사용.
4. Secret 의 base64 = 암호화 아님
data: 값이 base64 인코딩이라 "이거 암호화구나" 오해 흔함. 실제로는 단순 인코딩. 시크릿 보호는 별도 (etcd encryption at rest, external secrets, Sealed Secrets 등).
5. namespace 누락
prod 매니페스트인데 namespace 안 박아서 default 에 배포. metadata 에 명시 또는 kubectl apply -n prod -f 강제.
6. apiVersion 잘못
extensions/v1beta1 같은 deprecated 버전 — K8s 업그레이드 시 사라진다. networking.k8s.io/v1 같은 stable 버전 사용.
7. 동일 리소스 중복 정의
같은 kind + namespace + name 이 여러 번 — 마지막 것이 이긴다. Kustomize patch 의도가 아니면 사고.
8. resource limits 누락
resources.limits 없으면 한 pod 가 노드 전체 메모리 소비 가능. requests + limits 박는 게 표준.
실전 워크플로우
- 매니페스트 파일 작성 또는 Helm chart render (
helm template). - Kubernetes YAML 시각화 에 붙여넣어 관계 시각 확인 — 의도한 대로 연결되는지.
kubectl apply --dry-run=server -f로 schema 검증.kubectl apply -f적용.kubectl get all -n <ns>로 적용 결과 확인.
Helm vs Kustomize — 큰 그림
- Helm — chart (템플릿 + values) → render → 매니페스트. 버저닝·릴리스 관리. 가장 흔한 배포 도구.
- Kustomize — base 매니페스트 + overlay (patch). 환경 별 (dev/staging/prod) 차이 명시.
kubectl apply -k가 내장. - raw YAML — 작은 프로젝트, 학습용. 환경 차이 관리 어려움.
어느 방식이든 결국 매니페스트 YAML 로 풀린다. 그 결과를 이해하는 게 핵심.
요약
- 모든 매니페스트 = apiVersion + kind + metadata + spec.
- 핵심 관계는 label selector — Service 가 selector 로 pod labels 매칭. 이름 직접 참조는 Ingress→Service, HPA→Workload, volume claim 등.
- ConfigMap / Secret 은 envFrom · env.valueFrom · volumes 3 가지 방식 으로 pod 에 주입.
- multi-doc (
---) 로 여러 리소스 한 파일에. namespace 가 격리 단위. - Secret 의 base64 ≠ 암호화. 별도 etcd encryption 또는 external secrets.
- 매니페스트 받았을 때 Kubernetes YAML 시각화 에 붙여넣어 관계 시각 확인 → 5 분 안에 구조 파악.