05_01 쿠버네티스 볼륨 기초 실습 가이드

📚 실습 개요

이 실습에서는 쿠버네티스의 기본 볼륨 타입인 emptyDirhostPath를 직접 사용해보면서 볼륨의 기본 개념과 활용 패턴을 학습합니다.

기본실습 : https://github.com/sysnet4admin/_Lecture_k8s_learning.kit/tree/main/ch5 추가실습 : 본문 코드 참조.


🎯 학습 목표

  1. emptyDir 볼륨의 특성과 사용 사례 이해
  2. hostPath 볼륨의 장단점과 주의사항 파악
  3. DeploymentDaemonSet에서의 볼륨 활용 차이점 학습
  4. 실제 로그 수집 시나리오 구현

📋 사전 준비

필요 환경

  • Kubernetes 클러스터 (minikube, k3s, 또는 관리형 클러스터)
  • kubectl CLI 도구
  • 기본적인 YAML 문법 지식

디렉터리 구조 확인

# 실습용 디렉터리 생성
mkdir -p ~/k8s-volume-lab
cd ~/k8s-volume-lab

🔧 실습 1: emptyDir 볼륨 기본 사용법

1-1. 기본 emptyDir 실습

pod-emptydir-basic.yaml

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-basic-pod
  labels:
    app: emptydir-test
spec:
  containers:
  - name: writer
    image: busybox:1.35
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo 'Hello from writer' >> /shared/data.txt; date >> /shared/data.txt; sleep 5; done"]
    volumeMounts:
    - name: shared-storage
      mountPath: /shared
  - name: reader
    image: busybox:1.35
    command: ["/bin/sh"]  
    args: ["-c", "while true; do echo '=== Reading shared data ==='; cat /shared/data.txt 2>/dev/null || echo 'No data yet'; echo; sleep 10; done"]
    volumeMounts:
    - name: shared-storage
      mountPath: /shared
  volumes:
  - name: shared-storage
    emptyDir: {}  # 기본 emptyDir

실행 및 확인

# Pod 생성
kubectl apply -f pod-emptydir-basic.yaml
 
# Pod 상태 확인
kubectl get pods -w
 
# writer 컨테이너 로그 확인
kubectl logs emptydir-basic-pod -c writer
 
# reader 컨테이너 로그 확인  
kubectl logs emptydir-basic-pod -c reader
 
# 볼륨 내용 직접 확인
kubectl exec emptydir-basic-pod -c reader -- cat /shared/data.txt

1-2. 메모리 기반 emptyDir 실습

pod-emptydir-memory.yaml

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-memory-pod
spec:
  containers:
  - name: cache-app
    image: nginx:1.21-alpine
    volumeMounts:
    - name: memory-cache
      mountPath: /var/cache/nginx
    - name: disk-storage  
      mountPath: /usr/share/nginx/html
    ports:
    - containerPort: 80
  volumes:
  - name: memory-cache
    emptyDir:
      medium: Memory  # 메모리 기반
      sizeLimit: 100Mi
  - name: disk-storage
    emptyDir:
      sizeLimit: 1Gi  # 디스크 기반, 크기 제한

실행 및 성능 테스트

# Pod 생성
kubectl apply -f pod-emptydir-memory.yaml
 
# Pod IP 확인
kubectl get pod emptydir-memory-pod -o wide
 
# 메모리 마운트 확인
kubectl exec emptydir-memory-pod -- df -h
 
# 메모리 볼륨 위치 확인
kubectl exec emptydir-memory-pod -- mount | grep memory-cache

🏠 실습 2: hostPath 볼륨 활용

2-1. Deployment에서 hostPath 사용

deploy-hostpath.yaml (실습 PDF 기반)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-hostpath
  labels:
    app: deploy-hostpath
spec:
  replicas: 3
  selector:
    matchLabels:
      app: deploy-hostpath
  template:
    metadata:
      labels:
        app: deploy-hostpath
    spec:
      containers:
      - name: host-mon
        image: sysnet4admin/sleepy:2.0
        volumeMounts:
        - mountPath: /host-log
          name: hostpath-directory
      volumes:
      - name: hostpath-directory
        hostPath:
          path: /var/log

2-2. DaemonSet에서 hostPath 사용

ds-hostpath.yaml (실습 PDF 기반)

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ds-hostpath
  labels:
    app: ds-hostpath
spec:
  selector:
    matchLabels:
      app: ds-hostpath
  template:
    metadata:
      labels:
        app: ds-hostpath
    spec:
      containers:
      - name: host-mon
        image: sysnet4admin/sleepy:2.0
        volumeMounts:
        - mountPath: /host-log
          name: hostpath-directory
      volumes:
      - name: hostpath-directory
        hostPath:
          path: /var/log

실행 및 비교 분석

# 두 리소스 모두 배포
kubectl apply -f deploy-hostpath.yaml
kubectl apply -f ds-hostpath.yaml
 
# Pod 분산 확인
kubectl get pods -o wide -l app=deploy-hostpath
kubectl get pods -o wide -l app=ds-hostpath
 
# 각 Pod에서 호스트 로그 확인
kubectl exec deploy-hostpath-xxx -- ls -la /host-log
kubectl exec ds-hostpath-xxx -- ls -la /host-log

🔍 실습 3: 실제 로그 수집 시나리오

3-1. Nginx 로그 수집 구현

nginx-logging-pod.yaml

apiVersion: v1
kind: Pod  
metadata:
  name: nginx-with-logging
spec:
  containers:
  # 메인 애플리케이션
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80
    volumeMounts:
    - name: nginx-logs
      mountPath: /var/log/nginx
    # 커스텀 nginx 설정으로 로그 활성화
    command: ["/bin/sh"]
    args: ["-c", "nginx && tail -f /var/log/nginx/access.log"]
    
  # 로그 수집 사이드카
  - name: log-collector
    image: busybox:1.35
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo '=== Nginx Access Logs ==='; tail -n 5 /logs/access.log 2>/dev/null || echo 'No logs yet'; echo; sleep 30; done"]
    volumeMounts:
    - name: nginx-logs
      mountPath: /logs
      
  volumes:
  - name: nginx-logs
    emptyDir: {}

3-2. 로그 rotation 시뮬레이션

log-rotation-demo.yaml

apiVersion: v1
kind: Pod
metadata:
  name: log-rotation-demo
spec:
  containers:
  - name: app
    image: busybox:1.35
    command: ["/bin/sh"]
    args: ["-c", "
      counter=0;
      while true; do 
        echo \"Log entry $counter at $(date)\" >> /logs/app.log;
        counter=$((counter + 1));
        if [ $((counter % 100)) -eq 0 ]; then
          echo \"Rotating log at $counter entries\";
          mv /logs/app.log /logs/app.log.old;
          echo \"New log file started\" >> /logs/app.log;
        fi;
        sleep 1;
      done"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
      
  - name: log-monitor
    image: busybox:1.35  
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo '=== Current log ==='; tail -n 3 /logs/app.log 2>/dev/null; echo '=== Old log ==='; tail -n 3 /logs/app.log.old 2>/dev/null; echo; sleep 10; done"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
      
  volumes:
  - name: app-logs
    emptyDir:
      sizeLimit: 50Mi

📊 실습 4: 성능 및 제한사항 테스트

4-1. 볼륨 크기 제한 테스트

volume-limit-test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: volume-limit-test
spec:
  containers:
  - name: space-filler
    image: busybox:1.35
    command: ["/bin/sh"]
    args: ["-c", "
      echo 'Testing volume size limit...';
      for i in $(seq 1 10); do
        echo \"Creating 10MB file $i\";
        dd if=/dev/zero of=/data/file$i bs=1M count=10 2>/dev/null || echo \"Failed at file $i\";
        df -h /data;
        sleep 2;
      done;
      echo 'Keeping container alive...';
      tail -f /dev/null"]
    volumeMounts:
    - name: limited-storage
      mountPath: /data
  volumes:
  - name: limited-storage
    emptyDir:
      sizeLimit: 50Mi  # 50MB 제한

4-2. hostPath 타입별 동작 테스트

hostpath-types-test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-types-test
spec:
  containers:
  - name: tester
    image: busybox:1.35
    command: ["/bin/sh"]
    args: ["-c", "
      echo 'Testing hostPath types...';
      echo 'Directory contents:'; ls -la /host-dir/ || echo 'Directory access failed';
      echo 'File contents:'; cat /host-file/hostname || echo 'File access failed';
      echo 'Creating test file:'; echo 'test data' > /host-dir/test.txt || echo 'Write failed';
      tail -f /dev/null"]
    volumeMounts:
    - name: host-directory
      mountPath: /host-dir
    - name: host-file
      mountPath: /host-file
  volumes:
  - name: host-directory
    hostPath:
      path: /tmp
      type: Directory
  - name: host-file  
    hostPath:
      path: /etc
      type: Directory

🔧 실습 5: 문제 상황 시뮬레이션

5-1. Pod 재시작 시 데이터 손실 확인

# 1. emptyDir Pod에 데이터 생성
kubectl exec emptydir-basic-pod -c writer -- echo "Important data" > /shared/important.txt
 
# 2. 데이터 확인
kubectl exec emptydir-basic-pod -c reader -- cat /shared/important.txt
 
# 3. Pod 삭제 및 재생성
kubectl delete pod emptydir-basic-pod
kubectl apply -f pod-emptydir-basic.yaml
 
# 4. 데이터 손실 확인
kubectl exec emptydir-basic-pod -c reader -- cat /shared/important.txt
# 결과: 파일이 존재하지 않음 (데이터 손실)

5-2. hostPath의 노드 종속성 확인

# 1. hostPath Pod에서 데이터 생성  
kubectl exec ds-hostpath-xxx -- echo "Node-specific data" > /host-log/nodedata.txt
 
# 2. 해당 노드에서 직접 확인
# (노드에 SSH 접근 가능한 경우)
cat /var/log/nodedata.txt
 
# 3. Pod를 다른 노드로 강제 이동
kubectl delete pod ds-hostpath-xxx
# DaemonSet이므로 다른 노드에 재생성됨
 
# 4. 새로운 노드에서 데이터 확인
kubectl exec ds-hostpath-yyy -- cat /host-log/nodedata.txt
# 결과: 파일이 존재하지 않음 (다른 노드이므로)

📈 결과 분석 및 비교

emptyDir vs hostPath 비교표

특성emptyDirhostPath
데이터 생존Pod 생명주기와 동일노드 재부팅까지 유지
공유 범위같은 Pod 내 컨테이너간같은 노드의 모든 Pod
성능높음 (로컬 스토리지)높음 (로컬 스토리지)
보안Pod 격리됨호스트 노출 위험
이식성높음낮음 (노드 종속)
사용 사례임시 공유, 캐시로그 수집, 시스템 모니터링

활용 패턴별 권장사항

✅ emptyDir 권장 상황

  • 사이드카 패턴: 로그 수집, 프록시
  • 초기화 컨테이너: 설정 파일 준비
  • 임시 작업 공간: 빌드, 압축 해제
  • 고성능 캐시: 메모리 기반 캐시

✅ hostPath 권장 상황

  • 로그 수집: DaemonSet으로 노드별 로그 수집
  • 시스템 모니터링: 노드 리소스 모니터링
  • 개발/디버깅: 호스트 파일 직접 접근
  • 레거시 통합: 기존 호스트 애플리케이션 연동

❌ 피해야 할 상황

  • 데이터베이스: 영구 데이터는 PV/PVC 사용
  • 프로덕션 데이터: hostPath는 보안 위험
  • 다중 노드 공유: NFS 등 네트워크 스토리지 필요

🧹 실습 정리

리소스 정리

# 모든 실습 Pod 삭제
kubectl delete pod emptydir-basic-pod emptydir-memory-pod nginx-with-logging log-rotation-demo volume-limit-test hostpath-types-test
 
# Deployment와 DaemonSet 삭제  
kubectl delete deployment deploy-hostpath
kubectl delete daemonset ds-hostpath
 
# 확인
kubectl get pods

학습한 명령어 정리

# 볼륨 관련 확인 명령어
kubectl describe pod <pod-name>          # 볼륨 마운트 정보 확인
kubectl exec <pod> -- df -h              # 마운트된 볼륨 크기 확인  
kubectl exec <pod> -- mount | grep <>  # 마운트 포인트 확인
kubectl logs <pod> -c <container>        # 특정 컨테이너 로그 확인

🎓 핵심 학습 포인트

1. 볼륨의 기본 개념

  • emptyDir: Pod 내 컨테이너간 데이터 공유
  • hostPath: 호스트 노드의 파일시스템 접근
  • 생명주기: 볼륨 타입에 따른 데이터 지속성 차이

2. 실무 적용 가이드라인

  • 보안: hostPath 사용 시 보안 정책 수립 필요
  • 이식성: 환경 독립적인 애플리케이션을 위해 hostPath 최소화
  • 성능: 용도에 맞는 볼륨 타입 및 옵션 선택

3. 다음 단계 준비

  • PV/PVC: 영구 볼륨과 볼륨 클레임 학습 준비
  • StorageClass: 동적 프로비저닝 이해 필요
  • StatefulSet: 상태 유지 애플리케이션 패턴 학습

📚 추가 참고 자료

공식 문서


실습 완료일: 2025-11-17
난이도: 초급 → 중급
소요 시간: 약 30분