Docker 이미지와 Layer 개념과의 연관성
- Dockerfile은 각 명령마다 **Layer(층)**를 쌓아 이미지 생성
- 각 Layer는 **해시(SHA256)**로 식별됨 → 변경 감지
- → 캐시 최적화, 중복 방지, 무결성 보장 가능

-> Layer와 Volume은 다르지만 공통으로 "불변성과 재사용" 개념을 가짐
Volume은 Layer에 저장하지 못하는, 변경되는 데이터를 위한 공간
구조 요약
| Image Layer | 읽기 전용 (read-only) |
| Container Layer | 쓰기 가능하지만 컨테이너 종료 시 삭제됨 |
| ✅ Volume (외부) | 컨테이너 외부 저장소. 종료되어도 유지됨 |
도커 이미지의 Layer의 해시 값의 의미?(매우 중요!!!)
→ Docker 레이어의 해시는 “해당 명령어 결과물의 고유한 식별자”
→ 레이어 변경 사항을 변경된 해시 값으로 파악
해시 덕분에:
- Docker는 캐시 최적화를 할 수 있고,
- 중복 레이어 업로드 없이 효율적으로 이미지를 전송할 수 있으며,
- 내용 추적 및 무결성 보장이 가능
Layer가 겹칠 경우?
→ 무조건 위에 있는(나중에 들어온) 파일이 원래 있던 파일을 덮어씀
Layer를 수정한다?
→ Layer를 수정되는 것이 아니라 똑같은 형식의 Layer가 다시 쌓이는 것
→ 수정된 Layer는 사라지지 않고 감춰진 상태로 존재함
Volume

volume: 컨테이너가 종료되어도 데이터가 유지되도록 하기 위한 디스크 영역
- 컨테이너 안의 데이터는 기본적으로 휘발성(Stateless)
→ 종료되면 사라짐 - 이 한계를 극복하기 위해 외부 디스크(볼륨)를 컨테이너에 마운트!!!!!
- 종류로는 Empty Volume, HostPath 존재
Empty Volume
- 대규모 파일 기반 Sorting 작업
- Pod내의 컨테이너 간 파일 교환
- Empty Volume은 “메모리”에 생성할 수 있기 때문에 성능 면에서 아주 탁월!!!

HostPath
- Pod 가 종료 되어도 상태 유지
- 다만, Pod가 항상 다른 노드로 이동 가능성이 존재
Persistent Volume Access Mode
- ReadWriteOnce (RWO): 하나의 노드에서 읽기/쓰기로 마운트
→ RWO 모드는 하나의 노드에서 여러개의 POD가 실행중이라면 볼륨에 접근이 가능
→ Node:Disk 비율이 1:1(혼자 읽음)
- ReadWriteOncePod (RWOP): 유일하게 파드를 대상으로 디스크를 마운트
- ReadOnlyMany (ROX): 여러 노드에서 읽기 전용으로 마운트된 볼륨
→ 읽기가 많을 때 사용
- ReadWriteMany (RWX): 여러 노드에서 읽기-쓰기로 마운트된 볼륨
→ 분산 데이터베이스에서 많이 사용
PV with PVC

disk : 포맷이 되지 않은 상태
volume : 특정 상태로 포맷된 상태
디스크의 종류
- 블록 디스크(EBS): 포맷이 안되어있음 → 직접 지정해야 함, 동시에 읽기는 가능하나 쓰기는 불가능
- Stateful App에 적합 => StatefulSet(sfs)
- 네트워크 파일 시스템(=디스크)(NFS)(EFS): 여러 사용자가 한 디스크를 동시에 읽기, 쓰기 가능
- Stateless App에 적합 + 공유 세션 필요 시 적합 => Deployment
- 오브젝트 디스크(S3): 대규모 파일 작업 / append-only, read, delete만 가능 update(파일 변경)가 불가
→ → → 왜 오브젝트 디스크는 append only인가? 이유: 분산 저장과 불변성(Immutable Storage) 때문!
- 분산 작업에 유리
→ S3 같은 오브젝트 스토리지는 파일을 여러 조각(청크)으로 나눠서 여러 노드에 저장
- 수정 불가한 구조 (Append-Only)→ 수정하려면 전체 오브젝트를 새로 작성해야 함
→ 기존 파일의 중간 내용 변경(=update)은 불가능
- Docker 이미지 Layer 저장과 비슷한 방식→ 새로운 변경 사항은 새 레이어로 쌓임
→ 이와 유사하게 오브젝트 스토리지도 변경사항을 덧붙이거나 새로 덮어씀
→ 변경된 명령어 결과만 새로운 불변 Layer 단위로 저장, 기존 Layer는 캐시로 재사용되며, 변하지 않음 (불변성)
- 버전 관리 / 감사 로그 등 히스토리 추적 용이
→ 기존 객체를 수정하지 않고, 새로 덮어쓰기 때문에, 시간 순으로 버전을 기록해 감사 및 롤백(traceability) 가능
→ 변경을 기록하고 추적할 수 있는 버저닝 구조를 갖고 있음
예시: S3의 저장 구조
- 큰 파일을 저장할 때, S3는 내부적으로 Multipart Upload 방식 사용
-> S3와 같은 시스템은 데이터를 **조각(청크)**으로 나눠 여러 노드에 분산 저장
- PUT 명령을 통해 전체 파일을 업로드
- 파일 내용 일부 수정은 불가능(기존 객체 수정 불가) → 파일 전체를 새로 업로드해야 함
- 그래서 오브젝트 스토리지는 기본적으로 "append-only"처럼 동작함
PV (Persistent Volume) - 운영자
"관리자(Admin)가 미리 만들어 두는 실제 볼륨"
" 쿠버네티스에서 클러스터 외부의 실제 저장소(Volume)를 Pod에 제공하기 위한 리소스"즉, 실제 저장소(Resource)
| 개념 | 역할 | 예 |
| PV (Persistent Volume) | 쿠버네티스 리소스 객체 Pod에 외부 저장소를 붙이기 위한 "추상화된 인터페이스" |
apiVersion: v1 kind: PersistentVolume |
| EBS, EFS, S3 등 | 실제 외부 저장소 (백엔드) | AWS EBS Volume, AWS EFS 공유 디스크, AWS S3 버킷 |
- 관리자(Admin) 가 외부 저장소(EBS 등)를 준비해두고
- 그걸 PV로 등록해두면
- Pod는 PVC(PersistentVolumeClaim) 를 통해 그 PV에 접근
# pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodb-pv
spec:
capacity:
storage: 1Gi # 요청하는 디스크 용량
csi:
# CSI (Container Storage Interface)는 K8s가 외부 스토리지
#(예: AWS EBS)를 사용하게 해주는 표준 인터페이스
# AWS에서 제공하는 EBS CSI 드라이버를 지정
driver: ebs.csi.aws.com
# 드라이버 타입 지정 -> ex4
fsType: ext4
# 실제 사용할 AWS EBS 볼륨의 ID
# 이 값은 AWS CLI에서 조회한 VolumeId를 그대로 넣어야 함
# 즉, 생성한 AWS EBS 볼륨을 쿠버네티스에 직접 PV로 등록하는 것!!!
volumeHandle: vol-0ada484317d8bb919
accessModes:
- ReadWriteOnce
- ReadOnlyMany
# Retain(Pod 죽으면 유지), Delete(죽으면 삭제), "Format"(죽으면 포맷)
persistentVolumeReclaimPolicy: Retain
# 어느 노드(ZONE)에 붙일지 지정
# 해당 EBS 볼륨이 위치한 zone의 노드에서만 사용 가능
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2a
PVC (Persistent Volume Claim) - 개발자
"사용자(User)가 볼륨을 쓰기 위해 요청하는 명세서"
즉, "볼륨을 주세요!"라는 청구서
# pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-pvc
spec:
resources:
requests:
storage: 1Gi
accessModes:
- ReadWriteOnce
- PVC 명세서의 storageClassName 인자가 비어있는 것이 매우 중요함!!
- 쿠버네티스의 StorageClass(SC,GP2) 없이 직접 생성된 PV에 바인딩하겠다는 뜻
- 이 필드가 비어 있으면, StorageClass 기반 자동 프로비저닝을 사용하지 않고,
- 사용자가 미리 만들어둔 PersistentVolume(PV)와 수동 매칭 시도
- 즉, 정적 프로비저닝(static provisioning) 방식으로 동작하는 것임.
- 만약 이것을 비워두지 않으면 쿠버네티스 SC인 GP2와 동적 프로비저닝 될 것
- 인자가 존재하지 않을 경우 우선순위가 쿠버네티스의 스토리지 클래스(SC, GP2)이기 때문에
- 반드시 아래 인자를 명시해야 함
-> storageClassName: ""
Storage Class(sc)
StorageClass: 쿠버네티스에서 **볼륨(스토리지)**을 자동으로 생성할 수 있게 해주는 리소스
- 쉽게 말하면 "이런 스펙의 디스크를 자동으로 만들어줘" 라고 선언해놓는 템플릿!!!
StorageClass 구성 필드

| NAME | StorageClass의 이름 (PVC에서 참조할 때 사용됨) |
| PROVISIONER | 어떤 스토리지 드라이버(CSI)를 통해 디스크를 만들 것인지 정의 |
| RECLAIMPOLICY | PVC가 삭제되었을 때, 실제 EBS 디스크를 어떻게 할지 (Delete or Retain) |
| VOLUMEBINDINGMODE | 볼륨을 언제 바인딩할지 (즉, 디스크를 언제 만들지) |
| ALLOWVOLUMEEXPANSION | PVC 사이즈 늘리는 것 허용 여부 (true/false) |
Delete : PVC가 삭제되면, PV도 삭제되고 EBS 디스크도 같이 제거
Retain: PVC가 삭제되어도, PV와 실제 디스크는 그대로 유지됨
[ Pod (StatefulSet or Deployment) ]
↓ 요청
[ PVC (PersistentVolumeClaim) ]
↓ 연결
[ PV (PersistentVolume) ]
↓ 실제 스토리지 연결
[ EBS or EFS (AWS 기반 실제 디스크) ]
↑
[ StorageClass (SC) ]
- PVC는 SC를 통해 PV를 만들고 → PV는 실제 EBS/EFS를 연결
- SC 없으면 자동 생성 불가. 직접 만든 EBS는 수동 PV로 연결해야 함
ConfigMap
ConfigMap: 쿠버네티스에서 애플리케이션이 사용할 설정 값을 환경 변수, 커맨드라인 인자, 파일로 주입할 수 있도록 하는 리소스
- ConfigMap의 예 —> Spring boot의 applicaion.yaml 같은 파일
- 즉, ConfigMap이란 환경변수!!
ConfiMap 사용 방법
1. Yml 파일을 통한 매개변수 전달
- Docker 이미지에 매개변수로 시간 간격 (INTERVAL) 을 전달해 실행 주기를 제어
- .yml로 부터 .sh 파일에서 $1으로 받아 사용
# docimg/yml/Dockerfile
FROM ubuntu:latest
RUN apt-get update; apt-get -y install fortune
ADD fortuneloop.sh /bin/fortuneloop.sh
ENTRYPOINT ["/bin/fortuneloop.sh"]
CMD ["10"] # args가 없으면 10초
- fortuneLoop.sh: 일정 시간마다 fortune 메시지를 생성해서 index.html에 기록
- CMD ["10"]: 실행 인자 (interval)로 10초를 기본값으로 설정
- ENTRYPOINT는 이 스크립트를 실행함
# docimg/fortuneloop.sh (도커 이미지)
#!/bin/bash
trap "exit" SIGINT
INTERVAL=$1
#파라메터로 첫번째 매개 변수를 INTERVAL 로 저장
#cmd로 넘어온 변수가 $1에 대입됨!!
echo "Configured to generate neew fortune every " $INTERVAL " seconds"
mkdir /var/htdocs # 출력 파일 경로 생성
while :
do
echso $(date) Writing fortune to /var/htdocs/index.html
/usr/games/fortune > /var/htdocs/index.html
sleep $INTERVAL # INTERVAL초마다 갱신
done
- fortune 명령으로 명언 생성
- /var/htdocs/index.html에 주기적으로 덮어씀
- → 이 경로는 nginx 컨테이너와 공유된 Volume
# docker-args-fortune.yml
apiVersion: v1
kind: Pod
metadata:
name: fortune5s
spec:
containers:
- image: dangtong/fortune:args
args: ["5"] # Dockerfile의 CMD 대체, INTERVAL이 5초가 됨
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}
- 2개 컨테이너가 하나의 Pod 내에 존재
- html-generator는 명언 생성
- web-server는 /usr/share/nginx/html에서 index.html 서빙
- emptyDir Volume은 Pod 내에서만 공유되는 임시 디스크 (→ 둘이 같은 파일 공유 가능)
2. 매개변수 전달 Pod 작성
- args 대신 환경변수로 INTERVAL 값을 설정
- 실무에서는 환경변수 사용이 일반적
# fortune-yml-args.yml
apiVersion: v1
kind: Pod
metadata:
name: fortune30s
spec:
containers:
- image: dangtong/fortune:env
# 아까는 CMD로 환경변수를 넣었다면,
# 이곳에 설정된 환경변수()는 fortuneloop.sh의 INTERVAL로 바로 감
env:
- name: INTERVAL
value: "30" # 환경변수로 주입
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}
- INTERVAL=30이라는 환경변수를 설정함 (쉘 스크립트에서 사용될 변수)
- mountPath : fortuneLoop.sh는 /var/htdocs/index.html에 출력하고, nginx는 /usr/share/nginx/html/index.html을 읽음 — 즉 같은 파일을 두 컨테이너가 공유
# docimg/yml/fortuneloop.sh
#!/bin/bash
trap "exit" SIGINT
# 매개변수를 받지 않고 환경변수에서 바로 참조
echo "Configured to generate neew fortune every " $INTERVAL " seconds"
mkdir /var/htdocs # 출력 파일 경로 생성
while :
do
echo $(date) Writing fortune to /var/htdocs/index.html
/usr/games/fortune > /var/htdocs/index.html
sleep $INTERVAL
done
- 위 YAML에서 지정한 환경변수(INTERVAL=30) 사용
3. ConfigMap을 통한 설정
- 코드 수정 없이 환경 설정을 외부에서 주입
- ConfigMap 리소스 생성 후, Pod가 이를 읽어 사용
kubectl create configmap 'fortune-config'(name) --from-literal='sleep-interval'(key)=7
- ConfigMap 생성
- ConfigMap name: fortune-config
- 특정 키 값: sleep-interval
ConfigMap 환경변수로 전달(특정)
# fortune-cm-env-key.yml
apiVersion: v1
kind: Pod
metadata:
name: fortune7s-key
spec:
containers:
- image: dangtong/fortune:env
env:
- name: INTERVAL
valueFrom:
# 해당 name의 key값에 해당하는 값에서 환경변수 값을 INTERVAL로 보내라
configMapKeyRef:
name: fortune-config
key: sleep-interval # 해당 key의 값만 읽음
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}
- fortune-config라는 ConfigMap에 저장된 특정 키값(sleep-interval) 을 가져와서 INTERVAL 환경변수로 설정
- 가장 정확하고 명시적으로 전달하는 방식
- 사용자가 원하는 키만 선택해서 환경변수로 주입 가능
ConfigMap 환경변수로 전달(전체)
# fortune-cm-env-all.yml
apiVersion: v1
kind: Pod
metadata:
name: fortune7s-all
spec:
containers:
- image: dangtong/fortune:env
envFrom:
# 특정 값을 지정하지 않고 모두 가져옴
# 지정되어 있는 sleep-interval을 가져옴.
# 이는 create configmap으로 생성한 key값.
# 바로 이 key값(sleep-interval)이 바로 환경변수가 되는 것!!
- configMapRef:
name: fortune-config # 모든 키를 환경변수로 사용하겠다는 소리임
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {}
- fortune-config의 모든 키-값 쌍을 환경변수로 자동 주입
- 예: sleep-interval: "5" → Pod 안에서 SLEEP_INTERVAL=5처럼 사용 가능
- ConfigMap 내부 키 이름이 곧 환경변수 이름이 됨
- 여러 키를 한번에 주입할 수 있지만, 키 이름이 충돌하지 않도록 주의
4. ConfigMap 마운트
4-1. 디렉토리 기반 방식
- nginx 설정 파일을 ConfigMap으로 분리해 Pod 내부 /etc/nginx/conf.d에 마운트해서 사용할 수 있게 함
- 디렉토리 경로 (/etc/nginx/conf.d/)
# kubetemp/config-dir/custom-nginx-config.conf
server {
listen 8080;
server_name www.acron.com;
gzip on;
gzip_types text/plain application/xml;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
- /etc/nginx/conf.d/ 디렉토리에 ConfigMap 생성(custom-nginx-config.conf 파일)
- 서버 설정 정보 포함
kubectl create configmap nginx-config --from-file=config-dir
- ConfigMap 생성
- config-dir/ 디렉토리에 있는 모든 파일이 ConfigMap에 저장됨
- key: 파일 이름 (예: custom-nginx-config.conf)
- value: 해당 파일 내용
# configmap-volume-dir-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-configvol
spec:
containers:
- image: nginx:1.7.9
name: web-server
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: config
configMap:
name: nginx-config
- Pod에 ConfigMap을 볼륨 형태로 마운트
- mountPath: /etc/nginx/conf.d/
- ConfigMap의 여러 key들이 해당 경로에 파일 단위로 자동 생성
- volumeMounts.mountPath에 디렉토리 전체를 마운트
- /etc/nginx/conf.d/ 안에 custom-nginx-config.conf 자동 생성됨
- ConfigMap key가 파일명, value가 내용
4-2. 파일 기반 방식
- 이건 디렉토리 전체가 아니라, 특정 key(=파일 하나)만 마운트하는 방식
- 특정 파일 경로 (/etc/nginx/conf.d/파일)
# cm-volume-file-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx-configvol
spec:
containers:
- image: nginx
name: web-server
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx-config.conf # configMap 키 이름
readOnly: true
ports:
- containerPort: 8080
protocol: TCP
volumes:
- name: config
configMap:
name: nginx-config
defaultMode: 0660
- subPath를 사용하면 key값과 파일명이 정확히 매칭되어야 함
- 실제 파일명은 mountPath로, 내용은 ConfigMap의 key 내용으로 채워짐
- 기존 파일을 덮는 게 아니라 위에 씌우는 개념임 (overlay, 기존은 가려짐)
Secret
Secret: 쿠버네티스에서 민감한 정보를 저장하고 Pod에 전달하기 위한 리소스
- Secret의 예 → DB 패스워드
- Secret은 패스워드 및 인증서와 같은 중요정보 보관용으로 사용(base64 인코딩 -> 인코딩은 암호화가 아님!!)
- 환경 변수, 파일 등으로 Pod에 안전하게 주입
- 민감 정보(예: DB 비밀번호, API 키, TLS 인증서 등)를 YAML에 직접 하드코딩하지 않아도 됨
- ConfigMap과 비슷하지만, base64 인코딩된 민감한 데이터를 다룸
명령어로 Secret 생성


파일로 Secret 생성

apiVersion: v1
kind: Secret
metadata:
name: my-secret2
type: Opaque
data:
username: YWRtaW4= # admin (base64 인코딩)
password: MTIzNA== # 1234 (base64 인코딩)
Secret 사용 방법
1. 환경 변수로 주입
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: nginx
env:
- name: USERNAME
valueFrom:
secretKeyRef: # ConfigMap에서 ConfigKeyRef가 secretKeyRef로 바뀐 것을 확인!!!
name: my-secret2 # 생성한 Secret의 이름과 일치해야 함!!!
key: username # secret key
- name: PASSWORD
valueFrom:
secretKeyRef:
name: my-secret2
key: password
2. 환경 변수로 주입
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: mycontainer
image: nginx
volumeMounts:
- name: secret-volume
mountPath: "/etc/secret"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-secret2'Cloudwave > 쿠버네티스 강의' 카테고리의 다른 글
| Kubernetes 5일차 개념 정리 (0) | 2025.07.20 |
|---|---|
| Kubernetes 4일차 개념 정리 (1) | 2025.07.20 |
| Kubernetes 2일차 개념 정리 (0) | 2025.07.20 |
| Kubernetes 1일차 개념 정리 (2) | 2025.07.19 |