이 글에서 얻는 것
- Kubernetes(K8s)가 무엇이고, 왜 필요한지 이해합니다.
- Pod, Deployment, Service 같은 핵심 리소스를 설명할 수 있습니다.
- kubectl 명령어로 애플리케이션을 배포하고 관리할 수 있습니다.
- YAML 파일로 Kubernetes 리소스를 정의할 수 있습니다.
0) Kubernetes는 “컨테이너를 자동으로 관리"한다
Kubernetes란?
Kubernetes (K8s) = 컨테이너 오케스트레이션 플랫폼
목적:
- 컨테이너화된 애플리케이션 자동 배포
- 스케일링 (자동 확장/축소)
- 로드 밸런싱
- 자가 치유 (Self-healing)
- 롤링 업데이트
Docker vs Kubernetes
Docker:
- 단일 호스트에서 컨테이너 실행
- 수동 관리 필요
- 단일 장애점
Kubernetes:
- 여러 서버(클러스터)에서 컨테이너 실행
- 자동 관리 (선언적)
- 고가용성
시나리오:
Docker만 사용:
- 컨테이너 3개 실행 중
- 하나 죽음 → 수동으로 재시작 필요
Kubernetes 사용:
- "항상 3개 유지"라고 선언
- 하나 죽음 → 자동으로 새 컨테이너 시작
1) Kubernetes 아키텍처
1-1) 클러스터 구조
┌─────────────────────────────────────────┐
│ Kubernetes Cluster │
├─────────────────────────────────────────┤
│ Control Plane (Master Node) │
│ ┌──────────────────────────────────┐ │
│ │ API Server │ │ ← kubectl 명령 처리
│ │ Scheduler │ │ ← Pod 배치 결정
│ │ Controller Manager │ │ ← 상태 관리
│ │ etcd (Key-Value Store) │ │ ← 클러스터 상태 저장
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Worker Nodes (실제 컨테이너 실행) │
├─────────────────────────────────────────┤
│ Node 1 Node 2 Node 3 │
│ ┌────────┐ ┌────────┐ ┌─────┐ │
│ │ Pod A │ │ Pod B │ │Pod C│ │
│ │ Pod D │ │ Pod E │ └─────┘ │
│ └────────┘ └────────┘ │
│ kubelet kubelet kubelet │ ← Node 관리
│ kube-proxy kube-proxy kube-pr. │ ← 네트워킹
└─────────────────────────────────────────┘
2) 핵심 개념
2-1) Pod: 가장 작은 배포 단위
Pod = 하나 이상의 컨테이너를 포함하는 그룹
특징:
- 같은 Pod의 컨테이너는 네트워크/스토리지 공유
- 함께 스케줄링됨
- 같은 Node에 배치
- IP 주소 공유
일반적으로:
- 1 Pod = 1 컨테이너 (권장)
- 밀접하게 연관된 컨테이너만 같은 Pod에
Pod YAML:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: myapp:1.0
ports:
- containerPort: 8080
생성:
kubectl apply -f pod.yaml
# Pod 목록
kubectl get pods
# Pod 상세 정보
kubectl describe pod myapp-pod
# Pod 로그
kubectl logs myapp-pod
# Pod 삭제
kubectl delete pod myapp-pod
2-2) Deployment: Pod 관리
Deployment = Pod의 생명주기 관리
기능:
- 원하는 Pod 개수 유지 (ReplicaSet)
- 롤링 업데이트
- 롤백
- 스케일링
Deployment YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3 # Pod 3개 유지
selector:
matchLabels:
app: myapp
template: # Pod 템플릿
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
명령:
# Deployment 생성
kubectl apply -f deployment.yaml
# Deployment 목록
kubectl get deployments
# Pod 목록 (Deployment가 생성한 Pod)
kubectl get pods
# 스케일링
kubectl scale deployment myapp-deployment --replicas=5
# 이미지 업데이트 (롤링 업데이트)
kubectl set image deployment/myapp-deployment myapp=myapp:2.0
# 롤백
kubectl rollout undo deployment/myapp-deployment
# 업데이트 상태 확인
kubectl rollout status deployment/myapp-deployment
# Deployment 삭제
kubectl delete deployment myapp-deployment
2-3) Service: 네트워크 노출
Service = Pod에 대한 안정적인 네트워크 엔드포인트
필요한 이유:
- Pod IP는 재시작 시 변경됨
- 여러 Pod에 로드 밸런싱 필요
Service 종류:
1. ClusterIP (기본): 클러스터 내부에서만 접근
2. NodePort: 각 Node의 포트로 노출
3. LoadBalancer: 클라우드 로드밸런서 생성
Service YAML:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: ClusterIP
selector:
app: myapp # 이 레이블을 가진 Pod으로 트래픽 전달
ports:
- protocol: TCP
port: 80 # Service 포트
targetPort: 8080 # Pod 포트
명령:
# Service 생성
kubectl apply -f service.yaml
# Service 목록
kubectl get services
# Service 상세 정보
kubectl describe service myapp-service
# Service 삭제
kubectl delete service myapp-service
2-4) Service 종류별 예시
1. ClusterIP (내부 통신)
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
type: ClusterIP # 기본값
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
2. NodePort (외부 노출)
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 각 Node의 30080 포트로 접근 가능
3. LoadBalancer (클라우드)
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
# AWS/GCP/Azure에서 자동으로 로드밸런서 생성
3) kubectl 기본 명령어
3-1) 리소스 조회
# 모든 Pod
kubectl get pods
# 모든 Deployment
kubectl get deployments
# 모든 Service
kubectl get services
# 모든 리소스
kubectl get all
# 다른 네임스페이스
kubectl get pods -n kube-system
# 상세 정보
kubectl describe pod myapp-pod
# YAML 형식으로 출력
kubectl get pod myapp-pod -o yaml
# JSON 형식
kubectl get pod myapp-pod -o json
3-2) 리소스 생성/수정/삭제
# 파일로 생성
kubectl apply -f deployment.yaml
# 여러 파일
kubectl apply -f deployment.yaml -f service.yaml
# 디렉토리
kubectl apply -f ./k8s/
# 삭제
kubectl delete -f deployment.yaml
# 리소스 직접 삭제
kubectl delete pod myapp-pod
kubectl delete deployment myapp-deployment
kubectl delete service myapp-service
3-3) 디버깅
# 로그 확인
kubectl logs myapp-pod
# 실시간 로그
kubectl logs -f myapp-pod
# 여러 컨테이너 중 특정 컨테이너
kubectl logs myapp-pod -c container-name
# Pod 내부 접속
kubectl exec -it myapp-pod -- bash
# 단일 명령 실행
kubectl exec myapp-pod -- ls /app
# 포트 포워딩 (로컬 → Pod)
kubectl port-forward pod/myapp-pod 8080:8080
# http://localhost:8080으로 접근 가능
# 이벤트 확인
kubectl get events
# 리소스 사용량
kubectl top nodes
kubectl top pods
4) 실전 예제: Spring Boot 배포
4-1) Deployment + Service
# myapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: SPRING_DATASOURCE_URL
value: "jdbc:mysql://mysql-service:3306/mydb"
livenessProbe: # 컨테이너 살아있는지 체크
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe: # 트래픽 받을 준비됐는지 체크
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
배포:
kubectl apply -f myapp-deployment.yaml
# 상태 확인
kubectl get pods
kubectl get services
# 로그 확인
kubectl logs -l app=myapp --tail=100
# 스케일링
kubectl scale deployment myapp --replicas=5
4-2) ConfigMap + Secret
ConfigMap (설정):
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
application.properties: |
spring.application.name=myapp
logging.level.root=INFO
Secret (민감 정보):
apiVersion: v1
kind: Secret
metadata:
name: myapp-secret
type: Opaque
data:
# base64 인코딩 (echo -n 'secret' | base64)
db-password: c2VjcmV0
Deployment에서 사용:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: myapp:1.0
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secret
key: db-password
volumeMounts:
- name: config
mountPath: /app/config
volumes:
- name: config
configMap:
name: myapp-config
4-3) 전체 스택 배포
# mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: MYSQL_DATABASE
value: mydb
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
type: ClusterIP
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
5) 네임스페이스: 리소스 격리
# 네임스페이스 생성
kubectl create namespace dev
kubectl create namespace prod
# 네임스페이스별 배포
kubectl apply -f deployment.yaml -n dev
kubectl apply -f deployment.yaml -n prod
# 네임스페이스 확인
kubectl get namespaces
# 특정 네임스페이스의 Pod
kubectl get pods -n dev
# 기본 네임스페이스 변경
kubectl config set-context --current --namespace=dev
네임스페이스 사용 예:
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: dev # dev 네임스페이스에 배포
spec:
# ...
6) 베스트 프랙티스
✅ 1. 리소스 요청/제한 설정
resources:
requests: # 최소 보장
memory: "512Mi"
cpu: "500m"
limits: # 최대 사용
memory: "1Gi"
cpu: "1000m"
✅ 2. Health Check 설정
livenessProbe: # 죽었는지 확인 (재시작)
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe: # 준비됐는지 확인 (트래픽 전달)
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
✅ 3. 레이블 활용
metadata:
labels:
app: myapp
version: v1.0
environment: prod
✅ 4. 네임스페이스 분리
dev: 개발 환경
staging: 스테이징 환경
prod: 운영 환경
7) 자주 하는 실수
❌ 실수 1: 리소스 제한 없이 배포
# ❌ 리소스 제한 없음 → 메모리/CPU 과다 사용 가능
# ✅ 반드시 requests/limits 설정
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
❌ 실수 2: Health Check 미설정
# ✅ 항상 Health Check 설정
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
❌ 실수 3: Secret을 ConfigMap에 저장
# ❌ 나쁜 예
kind: ConfigMap
data:
password: "mypassword" # 평문!
# ✅ 좋은 예
kind: Secret
data:
password: "bXlwYXNzd29yZA==" # base64 인코딩
연습 (추천)
Minikube 설치
- 로컬 Kubernetes 클러스터 실행
- Spring Boot 앱 배포
Deployment 배포
- Pod 3개로 배포
- 스케일링, 롤링 업데이트 실습
Service 노출
- ClusterIP, NodePort, LoadBalancer 비교
- 로드 밸런싱 확인
요약: 스스로 점검할 것
- Kubernetes의 필요성을 설명할 수 있다
- Pod, Deployment, Service의 역할을 이해한다
- kubectl 명령어로 리소스를 관리할 수 있다
- YAML로 Kubernetes 리소스를 정의할 수 있다
- Health Check와 리소스 제한을 설정할 수 있다
다음 단계
- Helm 차트:
/learning/deep-dive/deep-dive-helm-basics/ - Ingress 컨트롤러:
/learning/deep-dive/deep-dive-kubernetes-ingress/ - 모니터링 (Prometheus):
/learning/deep-dive/deep-dive-prometheus-grafana/
💬 댓글