kube 를 통해 띄워진 Container 에서 생성한 데이터들은
(Docker 에서 그러했듯이) 어딘가에 저장하지 않으면 Container 가 제거되거나, 확장되는 등
이벤트가 발생했을 때 데이터를 잃어버릴 수 있음
따라서 kube 에서 데이터를 유지할 수 있는 방법인 Volume 에 대해 알아봄
deployment 와 service 가 함께 이미 실행되고 있는 상황을 전제로 설명 이어감
deployment 에 의해 생성된 (pod를 포함하는) container 가 crash 충돌 등에 의해 재시작하게 되면,
pod 에 의해 저장하고 있던 데이터는 모두 날아가게 됨
이를 방지하기 위해 volume 기능을 사용하여 데이터를 기존과 다른 어느 외부에 지정
kube 는 다양한 volume 및 driver 를 지원한다고 함
kube 는 다양한 환경(Cloud provider (AWS, Google, Azure 등) , IDC 등)위에서 운영될 수 있기 때문에
이에 맞는 volume 과 driver 를 제공하는 거라고 함
volume 은 pods 의 spec 부분에 추가하여 설정 가능함
아래와 같이 pods spec (in deployment yaml) 을 설정하면 됨
emptyDir 타입의 volume 이 적용된 yaml 예제를 들어 설명함
.... spec: containers: - name: demo image: demo volumeMounts: - mountPath: /app/demo #Container 내부 경로 name: demo-volume volumes: - name: demo-volume emptyDir: {} |
여기서 사용된 emptyDir 타입의 volume 은
pod 가 시작 될 때마다 단순히 새로운 빈 dir 를 생성함
(여기서 말하는 빈 dir 의 path 는 mountPath 로 설정한 위치가 됨)
pod 가 살아있는 동안 그 dir 를 활성 상태로 유지하고, pod 의 데이터를 그 dir 에 채움
container 가 재시작되거나 심지어 제거되더라도 해당 dir 에 들어있는 데이터는 유지됨
하지만 dir 은 pod 와 연결되어있기 때문에 pod 가 제거되면 dir 도 같이 제거됨(데이터도 함께 삭제)
pod 가 제거된 이후, 다시 생성되면 volume 에 의해 새로운 빈 dir 가 생성됨(데이터 돌아오지 않음)
containers.volumeMounts.name 에 설정된 volume명(위 예에서 demo-volume) 과
volumes.name 에 설정된 volume명이 동일한 것끼리 서로 매칭(mount)됨
즉, 위 예제에서는 emptyDir 타입을 갖는 demo-volume 이 container 에 mount 됨
emptyDir 타입의 volume 은 하나의 pod 에 연결되어있음
만약 pod가 2대 이상인 경우라면 아래와 같은 상황이 연출됨
- podA, podB 가 존재
- podA 의 emptyDir volume 에 데이터가 저장됨
- podA 의 emptyDir volume 에서 데이터를 읽어보니 잘 읽힘
- podA 의 container 가 죽음
- (loadBalancer 에 의해 트래픽이 podB 로 이동) podB 의 emptyDir volume 에서
데이터를 읽어보니 실패함(데이터가 없어서)
- podA 가 다시 살아남
- podA 의 emptyDir volume 에서 데이터를 읽어보니 잘 읽힘
위와 같은 상황을 해소하기 위해선 pod 당 새로운 빈 dir 를 생성하는 것으로 전환해야 함
그리고 그것을 도와주는 것이 hostPath driver
[hostPath driver documentation]
hostPath driver 를 통해 호스트 머신(=node) 의 path 를
(각 Pod 를 실행하는 실제 머신에 연결되도록) 설정할 수 있음
설정한 hostPath 위치 내 데이터는 각각의 pod 에 연결됨
그래서 2대 이상의 pods 가 존재해도,
여러 pods 가 (pod 의 특정 경로 대신) 하나의 호스트 머신 위의 동일한 dir 를 공유할 수 있음
(물론 이는 동일한 pod 에서 모든 요청을 처리하는 경우에만 유용함)
hostPath 는 아래와 같이 deployment 의 yaml 내 pod spec 설정 부분에 추가 설정함
.... spec: containers: - name: demo image: demo volumeMounts: - mountPath: /app/demo #Container 내부 경로 name: demo-volume volumes: - name: demo-volume hostPath: path: /data #외부 경로 type: DirectoryOrCreate |
hostPath.path 에 경로를 설정
hostPath 는 항상 빈 dir 를 생성하지 않으며, 이미 존재하는 경로를 설정할수 도 있음
마치 docker 의 bind mount 하는 것과 같음 (-v /data:/app/demo)
위 예제에서는 호스트 머신의 volumes.hostPath.path 와 pods 내의 containers.volumeMounts.mountPath 가 연결됨
type: DirectoryOrCreate 은 path 로 설정한 dir 가 존재하지 않으면 새로 생성하라는 의미
type: Directory 로 설정할 수 있으나, 만약 path 로 설정한 dir 가 존재하지 않으면 실패함
hostPath driver 를 설정해두면, pod 가 죽어도 데이터는 유지됨
왜냐하면 데이터는 호스트 머신 위에 존재하기 때문
hostPath 를 통해 여러 pods 에서 하나의 동일 dir 를 바라보게 한 것까지는 좋았지만
다른 노드(즉, 다른 호스트 머신)에 존재하는 다양한 pods 는 하나의 dir 를 바라볼 수 없음
오직 하나의 노드(하나의 호스트 머신) 위에 존재하는 pods 만이 동일 dir 를 바라볼 수 있음
그래서 hostPath driver 는 여러 호스트 머신 위에 kube 가 실행되는 실제 운영 환경 등에서 적용 불가
[persistent volume documentation]
kube 는 데이터를 영구적으로 저장 가능한 Persistent Volumes 기능을 지원함
pods 및 Nodes 독립성에 대한 아이디어를 기반으로 구축되었기 때문에
Persistent Volume 은 pods 나 Nodes 로부터 완전히 독립되어있음
(pods 나 Nodes 의 life cycle 에 영향을 받지 않는다는 말)
엔지니어는 이 volume 이 구성되는 방식에 대한 완전한 권한을 갖게 됨
각 pods 와 각 deployment yaml 파일 등에 volume 설정을 여러번 할 필요가 없음
대신 한 번만 정의하고 여러 pods 에서 사용하도록 만듦
Persistent Volume(이하 PV) 는 Cluster 내에 존재하며,
Nodes 외부에 존재함 (따라서 Nodes 및 Pods 로부터 독립성을 갖게 됨)
Nodes 내부에 PV 와의 연결을 위한 PV Claim 이란 것을 생성함
PV Claim 은 (Pods 가 실행되는) Nodes 에 귀속됨(Nodes 에 의존성이 있음)
PV Claim 은 PV 에 도달 및 접근하여 데이터를 읽어올 수 있음
그래서 Pod 내의 Container 가 PV 에 접근하여 데이터를 읽을 때 중간에서 도움을 줌.
여러 PV 를 바라보는 PV Claim 을 가질 수 있으며
여러 PV Claim 이 하나의 PV 를 바라볼 수 있음
참고) [AWS Documentation]
CSI (Container Storage Interface)는 Kubernetes에서 다양한 스토리지 솔루션을 쉽게 사용할 수 있도록 설계된 추상화임
다양한 스토리지 공급업체는 CSI 표준을 구현하는 자체 드라이버를 개발하여
스토리지 솔루션이 Kubernetes와 함께 작동하도록 할 수 있음
(연결되는 스토리지 솔루션의 내부에 관계없이)
AWS는 Amazon EBS , Amazon EFS 및 Amazon FSx for Lustre 용 CSI 플러그인을 제공함
hostPath 를 사용하는 Persistent Volume 샘플을 만들어서 이해도를 높여봄
(Persistent Volume 에서 hostPath 를 사용한다는 말은,
Cluster 를 단일 노드 하나에서만 정의한다는 말이 됨)
host-pv.yaml 이라는 이름의 yaml 파일을 새로 만들어서 Persistent Volume 을 정의해 봄
< host-pv.yaml > apiVersion: v1 kind: PersistentVolume metadata: name: host-pv spec: capacity: storage: 4Gi volumeMode: Filesystem #Filesystem 과 Block 으로 나뉨 storageClassName: standard #default sc 이름이 standard 라서 standard 를 넣음 accessModes: - ReadWriteOnce hostPath: path: /data #노드의 /data 가 PV 에 연결됨 type: DirectortOrCreate |
accessModes 는 아래와 같은 mode 를 제공함
- ReadWriteOnce : 단일 노드로부터의 rw 요청을 받을 수 있도록 mount 되는 Volume
- ReadOnlyMany : 여러 노드로부터의 r 요청만 받을 수 있도록 mount 되는 Volume
- ReadWriteMany : 여러 노드로부터의 rw 요청을 받을 수 있도록 mount 되는 Volume
(위 예제에서 hostPath 는 단일 노드 환경에서만 사용 가능하므로
사용할 수 있는 mode 는 오직 ReadWriteOnce 뿐임)
Kube 는 Storage Class 라는 개념을 갖고 있음
kubectl get sc 명령으로 확인 가능하며 default sc 도 존재함
Storage Class 는 kube 에서 관리자에게
Storage 관리 방법과 Volume 구성 방법을 세부적으로 제어할 수 있게 해주는 개념임
SC 는 hostPath Storage 를 프로비저닝 해야하는 정확한 Storage 를 정의함 (?뭔말임?)
PV 는 한 번만 설정하면(심지어 다른 사람이 설정해도 됨)
여러 pods 에서 함께 접근하여 사용 가능한 저장소가 됨
이렇게 생성한 PV 를 Pods 가 접근해서 사용 가능하게 만들려면
pods 가 PV 에 접근하는 것을 도와주는 PV Claim 을 생성해야 함
두 가지를 추가 설정하면 됨
1. PV Claim 을 생성하는 yaml
2. (PV 를 사용하려는) pods 의 yaml 에서 PV Claim 사용하도록 설정
1번(PV Claim 을 위한 yaml)을 생성해보자.
이름은 host-pvc.yaml
< host-pvc.yaml > apiVersion: v1 kind: PersistentVolumeClaim metadata: name: host-pvc spec: volumeName: host-pv #위에 PV 를 정의할 때 설정한 이름을 여기 넣어서, 해당 PV 와 PVC를 연결함 accessModes: - ReadWriteOnce storageClassName: standard #default sc 이름이 standard 라서 standard 를 넣음 resources: requests: storage: 4Gi #PVC 에서 요청하는 크기는, PV 의 stoage 이하만 가능 |
2번(pods 의 yaml 에 PVC 사용 설정)을 추가 설정해보자
deployment yaml 에서 pods 의 spec 을 정의하는 부분에 volumes 를 아래와 같이 설정
.... spec: containers: - name: demo image: demo volumeMounts: - mountPath: /app/demo name: demo-volume volumes: - name: demo-volume persistentVolumeClaim: claimName: host-pvc #위에서 PVC 를 정의할 때 사용한 이름을 가져다가 설정함 |
PV 가 데이터를 저장하는 곳은 어디이기에 Nodes 와 Pods 로부터 독립적인걸까?
PVC 가 필요한 이유는 무엇일까?
왜 PV 와 Pods 사이에 중간다리 역할이 필요한거지?
PV 의 권한과 PVC 의 권한은 왜 구분되어있는걸까?
위에서 만든 PV yaml 와 PVC yaml 및 업데이트한 deployment yaml 을 kube 에 띄움
kubectl apply -f=host-pv.yaml
kubectl apply -f=host-pvc.yaml
kubectl apply -f=deployment.yaml
PV, PVC 가 잘 올라왔는지 아래 명령어로 확인
kubectl get pv
kubectl get pvc
일반 Volume 와 Persistent Volume 의 차이를 알아봄
일반 Volumes | Persistent Volumes | |
Container 의존성 | Container 재시작, 제거 되어도 데이터 유지 | Container 재시작, 제거 되어도 데이터 유지 |
Pod 의존성 | emptyDir 타입이라면, Pods 가 사라지면 데이터도 사라짐 hostPath 를 사용한다면 Pods 가 사라져도 데이터가 사라지진 않지만, Nodes 에 종속되기 때문에 (Nodes 가 내려가면 사용 불가, 단일 Node 에서만 사용 등에 이유로) 글로벌 수준에서 관리하기 어려움 |
Pod 의존성 없음 Node 의존성 없음 |
설정 위치 | Pods 를 정의하는 deployment yaml 에 설정 | PV 를 위한 yaml 에 설정 구성이 하나의 파일에 standalone 으로 존재하므로 재사용하거나 관리하기 용이함 |
프로젝트 크기 | 소규모 프로젝트에서 사용하기에 용이 | 대규모 프로젝트에서 사용하기에 용이 |
deployment yaml 에 설정된 값을 (Container 내에서 동작하는) application code 내에서 사용 가능
마치 환경 변수마냥 사용하는 것임
예를 들어 다음과 같은 app.js 코드가 존재하고
< app.js > ... const filePath = path.join(__dirname, 'story', 'text.txt'); ... |
위 코드는, 아래와 같은 deployment yaml 로 실행될 Pods 내에서 실행될꺼라고 해보자
< deployment yaml> .... spec: containers: - name: story image: story-image volumeMounts: - mountPath: /app/story name: story-volume .... |
deployment yaml 에 코드에서 사용 가능한 환경 변수를 추가해주고,
< deployment yaml> .... spec: containers: - name: story image: story-image env: - name: STORY_DIR value: 'story' volumeMounts: - mountPath: /app/story name: story-volume .... |
app.js 코드 내의 'story' 를 설정한 환경변수명으로 바꿔줌
< app.js > ... const filePath = path.join(__dirname, process.env.STORY_DIR, 'text.txt'); ... |
모든 언어에서 process.env.[name] 형식으로 환경변수를 가져오진 않을 것 같은데
이건 언어에서 환경 변수 가져오는 방법에 따라 달라질 듯
위와 같이 yaml 자체에 환경 변수값을 넣는 방법 외에,
환경 변수를 정의한 yaml 파일을 만들어서 환경변수값을 가져와 넣는 방법이 존재함
이해를 위해 env.yaml 을 만들어보자
< env.yaml > apiVersion: v1 kind: ConfigMap metadata: name: myenv data: mydir: 'story' mykey1: 'myvalue1' mykey2: 'myvalue2' |
kubectl apply -f=env.yaml
kubectl get configmap
그리고 deployment yaml 에 코드에서 env.yaml 에서 설정한 값을 가져옴
< deployment yaml> .... spec: containers: - name: story image: story-image env: - name: STORY_DIR valueFrom: configMapKeyRef: name: myenv key: mydir volumeMounts: - mountPath: /app/story name: story-volume .... |
app.js 에서 process.env.STORY_DIR 로 읽은 값은 여전히 'story' 가 될 것임
더 복잡한데..?
'Docker' 카테고리의 다른 글
[Docker] Udemy Docker & Kubernetes : 실전 가이드 필기 - Kubernetes 네트워킹 (0) | 2024.01.21 |
---|---|
[Docker] Udemy Docker & Kubernetes : 실전 가이드 필기 - Kubernetes 핵심 개념 (1) | 2023.12.17 |
[Docker] Udemy Docker & Kubernetes : 실전 가이드 필기 - Docker Compose (0) | 2023.11.15 |
[Docker] Udemy Docker & Kubernetes : 실전 가이드 필기 - Docker (1) | 2023.10.03 |
[Docker] CentOS 7 Dockerfile (0) | 2022.07.20 |