개요
Kubernetes(K8s)는 컨테이너를 여러 서버에 자동으로 배포하고, 죽으면 다시 띄우고, 부하가 늘면 늘려주는 도구입니다.
Docker가 “컨테이너 하나를 만들고 실행하는 것"이라면, Kubernetes는 “그 컨테이너를 여러 노드에 걸쳐 운영하는 것"입니다.
이 글은 각 개념을 정의, 필요한 이유, 관련 명령어 순으로 정리합니다.
1. 클러스터 구조
정리 보기
클러스터 = 컨트롤 플레인 + 노드
Kubernetes 클러스터는 크게 두 부분으로 나뉩니다.
┌─────────────────────────────────────────────┐
│ Control Plane │
│ API Server / Scheduler / Controller / etcd │
└──────────────────────┬──────────────────────┘
│ 명령 전달
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ (워커 노드) │ │ (워커 노드) │ │ (워커 노드) │
│ Pod Pod │ │ Pod Pod │ │ Pod Pod │
└────────────┘ └────────────┘ └────────────┘Control Plane (컨트롤 플레인) — 클러스터 상태를 관리하는 컴포넌트 모음
| 구성요소 | 역할 |
|---|---|
| API Server | 모든 요청의 출입구. kubectl 명령이 여기로 감 |
| Scheduler | 새 Pod를 어느 Node에 배치할지 결정 |
| Controller Manager | 원하는 상태를 유지 (Pod 죽으면 다시 띄움) |
| etcd | 클러스터 전체 상태를 저장하는 데이터베이스 |
Node (노드) — 실제로 컨테이너가 돌아가는 서버
| 구성요소 | 역할 |
|---|---|
| kubelet | 이 노드에서 Pod을 실행/감시 |
| kube-proxy | 네트워크 트래픽을 적절한 Pod으로 전달 |
| Container Runtime | 실제 컨테이너 실행 (containerd 등) |
확인 명령어
# 클러스터 정보 확인
kubectl cluster-info
# 노드 목록과 상태
kubectl get nodes
# 노드 상세 정보 (CPU/메모리 할당 현황 등)
kubectl describe node <노드이름>2. Pod (파드)
정리 보기
Pod = 가장 작은 배포 단위
Pod는 Kubernetes에서 가장 작은 실행 단위입니다. 공식 문서는 컨테이너 하나를 실행하는 단일 컨테이너 Pod(“one-container-per-Pod”)를 가장 일반적인 Kubernetes 사용 방식으로 설명합니다.
Docker에서 docker run으로 컨테이너를 띄웠다면, Kubernetes에서는 Pod를 만듭니다.
왜 컨테이너가 아니라 Pod인가?
- 같은 Pod 안의 컨테이너끼리는 네트워크(localhost)와 볼륨을 공유
- 앱 옆에 로그 수집기를 붙이는 식의 “사이드카 패턴"이 가능
- 공식 문서에서는 단일 컨테이너 Pod를 가장 일반적인 사용 방식으로 설명하고, 여러 컨테이너를 한 Pod에 함께 두는 것은 컨테이너가 강하게 결합된 경우에만 쓰는 비교적 고급 사용 사례로 설명
Pod YAML 예시
apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80명령어
# Pod 목록
kubectl get pods
# Pod 상세 정보 (이벤트, 상태, IP 등)
kubectl describe pod <pod이름>
# Pod 로그 보기
kubectl logs <pod이름>
# Pod 안에 들어가기 (디버깅)
kubectl exec -it <pod이름> -- /bin/sh
# Pod 삭제
kubectl delete pod <pod이름>Pod의 생명주기
status.phase가 가질 수 있는 값은 Pending, Running, Succeeded, Failed, Unknown 5가지입니다.
Pending → Running → Succeeded / Failed
(노드와 통신 불가 시 Unknown)- Pending: 클러스터가 Pod를 받아들였지만 컨테이너 하나 이상이 아직 실행 준비되지 않은 상태. 스케줄링 대기 시간과 이미지 다운로드 시간이 여기 포함됨
- Running: 노드에 바인딩되고 모든 컨테이너가 생성된 상태. 최소 한 개가 실행 중이거나 시작/재시작 중
- Succeeded: 모든 컨테이너가 성공적으로 종료되고 재시작되지 않음
- Failed: 모든 컨테이너가 종료됐고 그중 하나 이상이 실패로 끝남
- Unknown: 노드와 통신이 안 돼서 상태를 가져올 수 없는 경우
CrashLoopBackOff는 phase가 아닙니다. 재시작 백오프가 걸린 컨테이너를 kubectl이 STATUS 칼럼에 표시하는 값입니다(Terminating도 같은 부류). phase는 Pod API 데이터 모델의 일부이고 STATUS는 kubectl 표시용 필드입니다.
- CrashLoopBackOff: 컨테이너가 시작에 계속 실패해서 kubelet이 재시작 백오프(10s, 20s, 40s … 기본 상한 300초)를 적용 중인 상태
3. Deployment (디플로이먼트)
정리 보기
Deployment = “이 앱을 N개 유지해줘"라는 선언
Deployment를 만들면 K8s가 알아서 Pod을 원하는 개수만큼 띄우고, 죽으면 다시 만들고, 업데이트하면 순차적으로 교체합니다.
Deployment YAML 예시
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3 # Pod 3개 유지
selector:
matchLabels:
app: web
template: # Pod 템플릿
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myapp:1.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"명령어
# Deployment 생성 (YAML 적용)
kubectl apply -f deployment.yaml
# Deployment 목록
kubectl get deployments
# Pod 수 변경 (스케일링)
kubectl scale deployment web-app --replicas=5
# 이미지 업데이트 (롤링 업데이트 자동 실행)
kubectl set image deployment/web-app web=myapp:2.0
# 업데이트 상태 확인
kubectl rollout status deployment/web-app
# 문제 시 롤백
kubectl rollout undo deployment/web-app
# 배포 히스토리
kubectl rollout history deployment/web-appDeployment → ReplicaSet → Pod 관계
Deployment (버전 관리 + 롤링 업데이트)
└── ReplicaSet (Pod 개수 유지)
└── Pod (실제 실행 단위)- Deployment가 ReplicaSet을 관리하므로, 공식 문서는 ReplicaSet을 직접 만들지 않고 Deployment를 사용하는 것을 권장 (커스텀 업데이트 제어가 필요하거나 업데이트가 아예 필요 없는 경우는 예외)
4. Service (서비스)
정리 보기
Service = Pod에 접속하는 고정 주소
Pod는 죽었다 살아나면 IP가 바뀝니다. 다른 앱이 이 Pod에 접속하려면 매번 IP를 찾아야 하는데, Service가 고정 주소를 제공합니다.
Service 종류
| 종류 | 접근 범위 | 용도 |
|---|---|---|
| ClusterIP | 클러스터 내부에서만 | 앱 간 통신 (type을 지정하지 않으면 이게 기본값) |
| NodePort | 외부에서 노드IP:포트로 | 노드 포트로 직접 외부 노출 |
| LoadBalancer | 외부 로드밸런서 연결 | 프로바이더 연동 필요 |
| ExternalName | 외부 호스트명으로 CNAME 반환 | 클러스터 밖 서비스를 이름으로 참조 |
- NodePort로 할당되는 포트는 API 서버의
--service-node-port-range플래그로 정해지고, 기본값은 30000-32767입니다.nodePort를 직접 지정할 때도 이 범위 안이어야 합니다 - LoadBalancer는 Kubernetes가 로드밸런서 자체를 제공하지 않습니다. 클라우드 프로바이더 연동이나 별도 구현이 있어야 실제로 프로비저닝됩니다
ClusterIP 예시 (기본 타입)
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web # label이 app=web인 Pod들에 연결
ports:
- port: 80 # Service 포트
targetPort: 8080 # Pod의 컨테이너 포트
type: ClusterIP이렇게 만들면 클러스터 안에서 web-service:80으로 접속하면 app=web Pod들에 자동 로드밸런싱됩니다.
DNS 자동 등록
- Service 만들면
<서비스이름>.<네임스페이스>.svc.<클러스터도메인>형태로 A/AAAA 레코드가 등록됩니다. 클러스터 도메인 기본값이cluster.local이라 보통web-service.default.svc.cluster.local로 보입니다 - 같은 네임스페이스면 그냥 서비스 이름으로 접속:
curl http://web-service. Pod의/etc/resolv.confsearch 목록에 자기 네임스페이스가 들어있어서 짧은 이름이 확장됩니다 - 다른 네임스페이스는
<서비스이름>.<네임스페이스>까지만 적어도 됩니다
명령어
# Service 목록
kubectl get services
# Service 상세 (연결된 Pod IP 목록 = Endpoints)
kubectl describe service web-service
# 외부 노출 (빠른 테스트)
kubectl expose deployment web-app --type=NodePort --port=805. Namespace (네임스페이스)
정리 보기
Namespace = 클러스터 내 리소스 그룹
하나의 클러스터를 논리적으로 나누는 방법입니다. 팀별, 환경별(dev/stg/prod)로 분리할 때 사용합니다.
# 네임스페이스 목록
kubectl get namespaces
# 특정 네임스페이스의 Pod 보기
kubectl get pods -n kube-system
# 네임스페이스 생성
kubectl create namespace dev
# 기본 네임스페이스 변경 (매번 -n 안 붙이려면)
kubectl config set-context --current --namespace=dev기본 제공 네임스페이스
| 이름 | 용도 |
|---|---|
| default | 별도 지정 안 하면 여기에 생성됨 |
| kube-system | K8s 시스템 컴포넌트 (API Server, CoreDNS 등) |
| kube-public | 누구나 읽을 수 있는 공개 리소스 |
6. ConfigMap과 Secret
정리 보기
ConfigMap = 설정 파일, Secret = 비밀 정보
앱의 설정값이나 비밀번호를 이미지에 넣지 않고 외부에서 주입합니다. Docker의 -e 환경변수나 .env 파일과 같은 역할.
ConfigMap 예시
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "db.example.com"
LOG_LEVEL: "info"Secret 예시
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM= # base64 인코딩 (echo -n 'password123' | base64)Pod에서 사용하기
spec:
containers:
- name: app
image: myapp:1.0
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret명령어
# ConfigMap 생성 (명령어)
kubectl create configmap app-config --from-literal=LOG_LEVEL=info
# Secret 생성
kubectl create secret generic app-secret --from-literal=DB_PASSWORD=password123
# ConfigMap/Secret 확인
kubectl get configmaps
kubectl get secrets
# Secret 내용 보기 (base64 디코딩 필요)
kubectl get secret app-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d- Secret의
data값은 base64 인코딩이며 암호화가 아닙니다. 저장 시 암호화는 etcd 암호화 설정으로 별도 구성합니다
7. 볼륨: PV와 PVC
정리 보기
PV = 프로비저닝된 스토리지, PVC = 스토리지 요청
Pod는 죽으면 데이터가 사라집니다. 데이터를 보존하려면 외부 저장소를 연결해야 합니다.
- PersistentVolume (PV): 실제 스토리지. 관리자가 미리 만들어두는 정적 프로비저닝과, StorageClass를 통해 PVC에 맞춰 자동 생성되는 동적 프로비저닝 두 가지가 있습니다
- PersistentVolumeClaim (PVC): 개발자가 “이만큼의 저장소가 필요합니다"라고 요청
PVC 예시
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-storage
spec:
accessModes:
- ReadWriteOnce # 단일 노드에서 읽기/쓰기로 마운트
resources:
requests:
storage: 10Gi # 10GiB 요청 (Gi는 2의 거듭제곱 단위)accessModes는 Pod 단위가 아니라 노드 단위
| 값 | 약어 | 의미 |
|---|---|---|
| ReadWriteOnce | RWO | 단일 노드에서 읽기/쓰기 마운트 |
| ReadOnlyMany | ROX | 여러 노드에서 읽기 전용 마운트 |
| ReadWriteMany | RWX | 여러 노드에서 읽기/쓰기 마운트 |
| ReadWriteOncePod | RWOP | 클러스터 전체에서 단 하나의 Pod만 읽기/쓰기 |
- ReadWriteOnce는 “Pod 하나"가 아니라 “노드 하나"가 기준입니다. 같은 노드에 있는 여러 Pod가 동시에 그 볼륨을 읽고 쓸 수 있습니다. Pod 하나로 제한하려면 ReadWriteOncePod를 씁니다
- 볼륨은 여러 accessModes를 지원해도 한 번에 하나의 모드로만 마운트됩니다
- RWO/ROX/RWX는 PV와 PVC를 매칭하는 데 쓰이는 값이고, 마운트된 뒤 쓰기 자체를 막아주지는 않습니다. RWOP만 실제로 단일 Pod 제약이 적용됩니다
Pod에서 사용
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- mountPath: /data
name: storage
volumes:
- name: storage
persistentVolumeClaim:
claimName: data-storage명령어
# PV, PVC 확인
kubectl get pv
kubectl get pvc
# PVC 상태 확인 (Bound = 정상 연결됨)
kubectl describe pvc data-storage8. 워크로드 종류
정리 보기
Deployment 외에도 상황에 맞는 워크로드 종류가 있습니다.
| 종류 | 용도 | 예시 |
|---|---|---|
| Deployment | 상태 없는 앱 N개 유지 | 웹서버, API 서버 |
| StatefulSet | 순서/고유 이름이 필요한 앱 | DB, Kafka, Redis 클러스터 |
| DaemonSet | 모든(또는 일부) 노드에 1개씩 배치 | 로그 수집기, 모니터링 에이전트 |
| Job | 한 번 실행하고 끝 | DB 마이그레이션, 배치 처리 |
| CronJob | 정해진 시간에 반복 실행 | 매일 백업, 주기적 리포트 |
DaemonSet 예시 (모든 노드에 로그 수집기 배치)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-collector
spec:
selector:
matchLabels:
app: log-collector
template:
metadata:
labels:
app: log-collector
spec:
containers:
- name: fluentd
image: fluentd:latestCronJob 예시 (매일 새벽 2시 백업)
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:1.0
restartPolicy: OnFailure# 각 워크로드 확인
kubectl get statefulsets
kubectl get daemonsets
kubectl get jobs
kubectl get cronjobs9. Ingress (인그레스)
정리 보기
Ingress = 외부 → 클러스터 안으로 들어오는 입구 규칙
Service의 LoadBalancer는 서비스 하나당 로드밸런서 하나가 필요합니다. Ingress를 쓰면 하나의 입구에서 도메인/경로 기반으로 여러 Service에 라우팅할 수 있습니다.
Ingress 예시
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80- Ingress는 규칙만 정의. 실제 동작은 Ingress Controller (nginx, traefik 등)가 담당
- TLS(HTTPS) 인증서도 Ingress에서 설정
# Ingress 목록
kubectl get ingress
# 상세 확인
kubectl describe ingress my-ingress10. kubectl 명령어 모음
정리 보기
조회 계열
# 리소스 목록 (pods, services, deployments 등)
kubectl get <리소스>
# 모든 네임스페이스에서 조회
kubectl get pods --all-namespaces
kubectl get pods -A # 위와 같음 (축약)
# 상세 정보
kubectl describe <리소스> <이름>
# YAML로 현재 상태 보기
kubectl get deployment web-app -o yaml생성/수정 계열
# YAML 파일로 생성 또는 업데이트
kubectl apply -f <파일.yaml>
# 폴더 안의 YAML 전부 적용
kubectl apply -f ./k8s/
# 빠른 생성 (간단한 테스트용)
kubectl run test-pod --image=nginx
kubectl create deployment test --image=nginx --replicas=2삭제 계열
# 리소스 삭제
kubectl delete <리소스> <이름>
# YAML로 만든 것 삭제
kubectl delete -f <파일.yaml>
# 네임스페이스 통째로 삭제 (안의 모든 리소스 포함)
kubectl delete namespace dev디버깅 계열
# 로그 보기
kubectl logs <pod이름>
# 실시간 로그 (tail -f처럼)
kubectl logs -f <pod이름>
# 이전 크래시 로그 (CrashLoopBackOff일 때)
kubectl logs <pod이름> --previous
# Pod 안에 접속
kubectl exec -it <pod이름> -- /bin/sh
# Pod 상태/이벤트 확인 (왜 안 뜨는지 원인 파악)
kubectl describe pod <pod이름>
# 리소스 사용량 (metrics-server 필요)
kubectl top pods
kubectl top nodes배포 계열
# 롤링 업데이트
kubectl set image deployment/<이름> <컨테이너>=<새이미지>
# 롤백
kubectl rollout undo deployment/<이름>
# 스케일링
kubectl scale deployment/<이름> --replicas=<수>
# 재시작 (이미지 그대로, Pod만 재생성)
kubectl rollout restart deployment/<이름>자동완성과 축약어
# 자동완성 설정 (매번 긴 이름 안 쳐도 됨)
source <(kubectl completion zsh)
# 축약어 사용
kubectl get po # pods
kubectl get svc # services
kubectl get deploy # deployments
kubectl get ns # namespaces
kubectl get no # nodes
# 라벨로 필터링
kubectl get pods -l app=web
# 여러 리소스 한번에 보기
kubectl get pods,services,deployments