Kubernetes의 기본 개념을 이해하고, 첫 애플리케이션을 배포하며, kubectl 명령어를 마스터하는 실전 가이드
Docker로 컨테이너를 만들고 Docker Compose로 여러 컨테이너를 관리해봤다면, 이제 더 큰 규모의 프로덕션 환경을 위한 도구가 필요합니다. 바로 Kubernetes입니다. 이번 포스트에서는 Kubernetes의 핵심 개념을 이해하고 첫 애플리케이션을 배포해보겠습니다.
여러분이 인기 있는 온라인 쇼핑몰을 운영한다고 상상해보세요.
평상시:
블랙프라이데이:
Docker만 사용할 때의 문제:
# 수동으로 각 서버에 접속해서...
ssh server1
docker run -d -p 80:80 shopping-app
ssh server2
docker run -d -p 80:80 shopping-app
# 로드밸런서 설정도 수동으로...
# 모니터링도 직접...
# 장애 발생하면 새벽에 일어나서 복구... 😴
결과:
# Kubernetes로는 이렇게 간단히!
apiVersion: apps/v1
kind: HorizontalPodAutoscaler
metadata:
name: shopping-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: shopping-app
minReplicas: 3
maxReplicas: 100
targetCPUUtilizationPercentage: 50
결과:
Kubernetes(K8s)는 구글이 15년간의 대규모 서비스 운영 경험을 바탕으로 만든 컨테이너 오케스트레이션 플랫폼입니다.
# Docker Compose - 개발 환경
docker-compose up -d
# 단일 호스트, 수동 관리, 간단한 설정
# Kubernetes - 프로덕션 환경
kubectl apply -f deployment.yaml
# 멀티 호스트, 자동 관리, 강력한 기능
Kubernetes는 마치 잘 조직된 회사와 같습니다.
1. API Server (대표이사)
2. etcd (기록 보관소)
3. Scheduler (인사팀장)
4. Controller Manager (품질관리팀장)
1. kubelet (현장 관리자)
2. kube-proxy (네트워크 담당)
3. Container Runtime (작업 도구)
# minikube 설치 (로컬 Kubernetes 클러스터)
# macOS
brew install minikube
# Windows (관리자 권한 PowerShell)
choco install minikube
# Linux
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# 클러스터 시작
minikube start
# 상태 확인
kubectl cluster-info
먼저 빠르게 배포해보는 명령형 방식부터 시작하겠습니다.
# 1. Deployment 생성 (애플리케이션 실행)
kubectl create deployment first-app --image=nginx:latest
# 2. 배포 상태 확인
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# first-app 1/1 1 1 30s
# 3. Pod 확인 (실제 실행 중인 컨테이너)
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# first-app-6b7b9f5b9d-x7n4m 1/1 Running 0 45s
# 4. Service 생성 (외부 노출)
kubectl expose deployment first-app --port=80 --type=LoadBalancer
# 5. Service 확인
kubectl get services
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# first-app LoadBalancer 10.96.232.123 <pending> 80:31234/TCP 10s
# minikube는 실제 LoadBalancer가 없으므로 터널링 사용
minikube service first-app
# 또는 포트 포워딩
kubectl port-forward service/first-app 8080:80
# 이제 http://localhost:8080 으로 접속 가능!
실무에서는 YAML 파일을 사용한 선언형 방식을 선호합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3 # 3개의 복제본 실행
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
# 배포
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 또는 한 번에
kubectl apply -f .
# 상태 모니터링
kubectl get all
kubectl describe deployment nginx-deployment
kubectl logs -l app=nginx
# 스케일링
kubectl scale deployment nginx-deployment --replicas=5
# 업데이트 (이미지 변경)
kubectl set image deployment/nginx-deployment nginx=nginx:1.22
# 롤백
kubectl rollout undo deployment/nginx-deployment
# 기본 조회
kubectl get pods
kubectl get deployments
kubectl get services
kubectl get nodes
# 자세한 정보
kubectl get pods -o wide
kubectl get pods -o yaml
# 레이블로 필터링
kubectl get pods -l app=nginx
# 모든 네임스페이스
kubectl get pods --all-namespaces
# describe: 이벤트와 상세 정보
kubectl describe pod <pod-name>
kubectl describe deployment <deployment-name>
# logs: 컨테이너 로그
kubectl logs <pod-name>
kubectl logs <pod-name> -f # 실시간 로그
kubectl logs <pod-name> --previous # 이전 컨테이너 로그
# 생성
kubectl create -f <file.yaml>
kubectl apply -f <file.yaml> # 생성 또는 업데이트
# 수정
kubectl edit deployment <name>
kubectl patch deployment <name> -p '{"spec":{"replicas":5}}'
# 삭제
kubectl delete -f <file.yaml>
kubectl delete deployment <name>
kubectl delete pod <name> --force --grace-period=0 # 강제 삭제
# Pod 내부 접속
kubectl exec -it <pod-name> -- /bin/bash
# 포트 포워딩
kubectl port-forward pod/<pod-name> 8080:80
# 리소스 사용량
kubectl top nodes
kubectl top pods
# 이벤트 확인
kubectl get events --sort-by=.metadata.creationTimestamp
// app.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello Kubernetes!',
hostname: process.env.HOSTNAME,
version: '1.0.0'
});
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
# Dockerfile
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
# k8s-app.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-app
labels:
app: node-app
spec:
replicas: 3
selector:
matchLabels:
app: node-app
template:
metadata:
labels:
app: node-app
spec:
containers:
- name: node-app
image: your-dockerhub-username/node-app:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: node-app-service
spec:
selector:
app: node-app
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 3000
# Docker 이미지 빌드 및 푸시
docker build -t your-dockerhub-username/node-app:1.0.0 .
docker push your-dockerhub-username/node-app:1.0.0
# Kubernetes 배포
kubectl apply -f k8s-app.yaml
# 배포 확인
kubectl get all -l app=node-app
# 로그 확인
kubectl logs -l app=node-app
# 서비스 테스트
kubectl port-forward service/node-app-service 8080:80
curl http://localhost:8080
# 1. Pod 상태 확인
kubectl get pods
kubectl describe pod <pod-name>
# 2. 일반적인 문제들
# - ImagePullBackOff: 이미지를 가져올 수 없음
# → 이미지 이름/태그 확인, 레지스트리 인증 확인
# - CrashLoopBackOff: 컨테이너가 계속 재시작
# → 로그 확인: kubectl logs <pod-name>
# - Pending: 스케줄링 되지 않음
# → 리소스 부족, 노드 셀렉터 확인
# 1. Service와 Endpoints 확인
kubectl get svc
kubectl get endpoints
# 2. 레이블 셀렉터 매칭 확인
kubectl get pods --show-labels
kubectl describe svc <service-name>
# 3. 네트워크 정책 확인
kubectl get networkpolicies
항상 requests와 limits를 설정하여 리소스 사용을 제어하세요.
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
liveness와 readiness probe를 구현하여 자가 치유 기능을 활용하세요.
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
일관된 레이블 전략으로 리소스를 체계적으로 관리하세요.
labels:
app: myapp
version: v1.0.0
environment: production
team: backend
축하합니다! 이제 Kubernetes의 기본을 마스터했습니다. 다음 포스트에서는 더 깊이 있는 주제들을 다룰 예정입니다:
Kubernetes는 강력하지만 복잡한 도구입니다. 하지만 기초를 탄탄히 다지면 점진적으로 고급 기능들도 익힐 수 있습니다. 계속해서 실습하고 실험해보세요!
시리즈 네비게이션
ReplicaSet, Service 타입, ConfigMap, Secret, Volume, HPA 등 Kubernetes의 핵심 개념을 실무 예제와 함께 깊이 있게 다룹니다
Docker에서 Kubernetes로, 컨테이너 오케스트레이션의 세계로 들어가는 체계적인 학습 가이드 시리즈 소개
로컬에 개발 도구를 설치하지 않고 Docker 컨테이너로 npm, composer, artisan 등을 실행하는 유틸리티 컨테이너 패턴을 알아봅니다
Docker 기초부터 프로덕션 배포까지 단계별로 학습하는 완벽 가이드 시리즈 소개