Kubernetes

이번 포스팅에서는 emptyDir
과 hostPath
그리고 pvc
와 pv
라고 하는
퍼시스턴트 볼륨 클라임과 퍼시스턴트 볼륨에 관해 알아보도록 하자.
emptyDir

먼저 emptyDir
은 컨테이너들끼리 데이터를 공유하기 위해 Volume
을 사용하는 것이다.
최초로 해당 Volume
이 생성될 때는 항상 해당 Volume
안의 내용이 비어있기 때문에 emptyDir
이라는 명칭이 붙어졌다.
만약, 컨테이너1이 웹 역할을 하는 서버이고 컨테이너2가 백엔드단을 처리해주는 서버라고 했을 때,
해당 웹서버로 받은 어떤 특정 파일을 마운트가 된 Volume
에 저장을 해놓고,
백엔드단의 컨테이너 역시 해당 볼륨을 마운트를 하면 이 두 서버가 해당 Volume
을 자신의 로컬에 있는 파일처럼 사용을 한다.
따라서, 두 서버가 서로 파일을 주고 받을 필요 없이 편하게 사용이 가능하다.
중요한 점은 emptyDir
은 위의 그럼처럼 Pod
안에 생성이 되기에 Pod
에 문제가 생겨 재생성되면
데이터가 싹 없어진다는 걸 의미한다.
따라서, 해당 Volume
에 쓰이는 데이터는 꼭 일시적인 활용 목적에 의한 데이터만 넣는 것이 좋다.
apiVersion: v1
kind: Pod
metadata:
name: pod-volume-1
spec:
containers:
- name: container1
image: kubetm/init
volumeMounts:
- name: empty-dir
mountPath: /mount1
- name: container2
image: kubetm/init
volumeMounts:
- name: empty-dir
mountPath: /mount2
volumes:
- name : empty-dir
emptyDir: {}
해당 emptyDir
을 활용한 Pod
를 생성하려면 위의 YAML 파일과 같이 생성하면 된다.
YAML 파일의 내용을 살펴보면 containers에 두 컨테이너가 존재하고 둘 다 Volume
을 마운트하고 있다.
이때, 마운트하는 Path를 보면 컨테이너1은 /mount1
이라는 Path를 사용했고,
컨테이너2는 /mount2
라는 Path를 사용했다.
이 mount
Path의 의미는 이 컨테이너가 해당 Path로 Volume
을 연결하겠다는 의미이다.
컨테이너1과 컨테이너2의 Path의 이름이 틀리더라도 결국 각각의 Path가 지정되는 볼륨의 이름은
empty-dir
로 똑같은 Volume
을 지정하고 있기 때문에 결국 한 Volume
을 마운트를 하고 있는 것이다.
hostPath

hostPath
는 이름 그대로 한 호스트, 그러니까 해당 Pod
들이 올라가져 있는 노드의 Path를 Volume
으로써 사용하는 것이다.
emptyDir
과 다른 점은 해당 Path를 각각의 Pod
들이 마운트를 해서 공유하기 때문에,
Pod
들이 죽어도 노드에 있는 데이터는 사라지지 않는다.
해당 부분에서는 다소 좋아 보일 수 있지만, Pod
입장에서는 한가지 큰 문제가 존재한다.
만약, 재생성되는 순간에 스케줄러가 자원 상황을 보고 노드2에 Pod
를 만들어 줄 수도 있고,
노드1에 장애가 생겨 다른 노드에 파드가 옮겨질 수도 있다.
즉, Pod
가 다른 노드로 옮겨졌을 때 해당 Pod
는 그 전 노드에 있는 Volume
을 마운트할 수가 없게 된다.
hostPath
기 때문에 Pod
가 올라가져 있는 노드의 Volume
만을 사용할 수 있기 때문이다.
만약, 굳이 방법을 찾는다면 노드2가 추가가 될 때마다 똑같은 이름의 경로를 만들어서
직접 노드에 있는 Path끼리 마운트를 시켜주면 문제는 없어질 것이다.
하지만, 이거는 Kubernetes
가 해주는 역할은 아니고 운영자가 노드가 추가될 때마다
직접 리눅스 시스템의 별도 마운트 기술을 사용하여 연결을 해야 한다.
이거는 뭔가 자동화를 시키는데 사람의 개입이 들어가기 때문에 실수 발생 여지가 많아져 추천되는 방법은 아니라 한다.
그럼 이 hostPath
는 어떨 때 써야 될까?
각각의 노드에는 기본적으로 각 노드 자신을 위해 사용되는 파일이 있다. 시스템 파일들이나 여러 설정 파일들이 있는데,
Pod
자신이 할당되어 있는 호스트의 데이터를 읽거나 써야 될 때 사용하면 된다.
아래는 hostPath
를 사용한 Pod
의 YAML 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: pod-volume-3
spec:
nodeSelector:
kubernetes.io/hostname: k8s-node1
containers:
- name: container
image: kubetm/init
volumeMounts:
- name: host-path
mountPath: /mount1
volumes:
- name : host-path
hostPath:
path: /node-v
type: DirectoryOrCreate
Pod
를 만들 때 컨테이너에서 Volume
을 마운트를 할 건데 해당 Path는 /mount1
이고,
해당 Path에 관한 hostPath
라는 이름의 Volume
은 아래에 작성되어 있다.
그리고 이 hostPath
라는 Volume
은 위의 YAML 파일처럼 hostPath
라는 속성이 들어가며 Path는 /node-v
이다.
또한, 타입은 DirectoryOrCreate
이다.
다시 한번 설명하자면 hostPath
는 Pod
의 데이터를 저장하기 위한 용도가 아닌,
노드에 있는 데이터를 Pod
에서 쓰기 위한 용도이다.
PVC / PV

다음으로는 퍼시스턴트 볼륨 클라임과 퍼시스턴트 볼륨에 관한 설명이다.
PVC
와 PV
는 Pod
에 영속성 있는 Volume
을 제공하기 위한 개념이다.
실제 Volume
들의 형태는 매우 다양하다.
로컬 볼륨도 있고 외부에 원격으로 사용되는 형태의 Volume
들도 있다.
위의 그림처럼 아마존이나 Git에 연결을 할 수도 있으며, NFS
를 써서 다른 서버와 연결을 할 수도 있다.
그리고 Storage OS
같이 Volume
을 직접 만들고 관리할 수 있는 솔루션들도 있다고 한다.
위처럼 다양한 Volume
에 관한 퍼시스턴트 볼륨을 정리하고 연결을 한다.
근데, Pod
는 해당 PV
에 바로 연결을 하는 것이아닌 PVC
(퍼시스턴트 볼륨 클라임)을 통해 PV
와 연결이 된다.
바로 Pod
에서 PV
로 연결을 하는게 더 깔끔해 보이는데 왜 중간에 PVC
를 둘까?
Kubernetes
는 이 Volume
사용에 있어 유저 영역
과 어드민 영역
으로 나눴기 때문이다.
어드민은 Kubernetes
를 담당하는 Kubernetes
운영자일 것이며,
유저는 Pod
에 서비스를 만들고 배포를 관리하는 서비스 담당자일 것이다.
Volume
들의 종류는 많고 각각의 Volume
들을 연결하기 위한 설정도 각각 틀리기 때문에 위와 같이 구성한 것이다.
밑의 YAML 내용을 보면 PV을 정의하는데 각각의 Volume
에 따라 이걸 연결하기 위한 속성들이 다르게 들어간다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-01
spec:
nfs:
server: 192.168.0.xxx
path: /sda/data
iscsi:
targetPortal: 163.180.11
iqn: iqn.200.qnap:...
lun: 0
fsType: ex4
readOnly: no
chapAuthSession: true
gitRepo:
repository: github.com
revision: master
directory: .
위처럼 각각의 속성이 다르기 때문에 이런 걸 전문적으로 관리하는 어드민이 PV
를 만들어 놓으면,
유저는 이걸 사용을 하기 위해 PVC
를 만들어야 한다.
이때 PVC
를 만들기 위한 YAML 레이아웃이 아래와 같다고 가정해보자.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-01
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1G
storageClassName: ""
위의 YAML 파일 내용을 살펴보면 나는 읽기 쓰기 모드로 용량이 1기가인 볼륨을 할당해달라는 것을 말한다.
맨 밑의 라인을 보면 storageClassName
이 있는데 이렇게만 넣으면,
현재 만들어져 있는 PVC
들 중 선택이 된다고만 알고 우선 넘어가자.
이걸 ""
을 안 넣고 생략을 하면 또 다른 동작으로 사용될 수 있기 때문이다.
스토리지 클래스
는 다음 과정에서 더 배워볼 것이다.
apiVersion: v1
kind: Pod
metadata:
name: pod-volume-3
spec:
containers:
- name: container
image: kubetm/init
volumeMounts:
- name: pvc-pv
mountPath: /volume
volumes:
- name : pvc-pv
persistentVolumeClaim:
claimName: pvc-01
마지막으로, Pod
를 만들 때 claimName
에 앞서 만들어 놓은 PVC
이름으로 연결을 한다.
그렇게 Volume
을 만들어 놓으면 해당 Volume
을 컨테이너에서 사용을 하면 된다.
정리하자면, 최초 어드민이 PV
를 만들어 놓고 사용자가 PVC
를 만들면
Kubernetes
가 이 PVC
내용에 맞는 적절한 Volume
에 연결을 해준다.
그리고 나서 Pod
를 만들 때 이 PVC
를 사용하면 된다.
좀 더 추가적인 설명으로, 아래 형식의 로컬 PV
를 만들었다고 생각해보자.(로컬 PV는 실제로 잘 사용하지 않는다고 한다.)
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-04
labels:
pv: pv-04
spec:
capacity:
storage: 2G
accessModes:
- ReadWriteOnce
local:
path: /node-v
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-node1]}
위의 YAML 파일 내용을 살펴보면 capacity
와 accessModes
가 존재한다.
아까 PVC
가 PV
를 연결할 때 Kubernetes
가 알아서 자동으로 연결을 해준다고 했다.
자동으로 연결을 해주려면 뭔가 근거가 있어야 되는데 바로 위의 capacity
와 accessModes
같은 걸 참조해서
Kubernetes
가 연결을 해주는 것이다.
특정 스펙의 capacity
와 accessModes
가 지정이 되면 PVC
에서 요청하는 내용에 맞게 연결이 되는 것이다.
실습
여기까지 Kubernetes
에서 지원해주는 Volume
에 관해 알아보았다.
각 Volume
유형에 관한 실습과 함께 좀 더 자세히 알아보자.
emptyDir
apiVersion: v1
kind: Pod
metadata:
name: pod-volume-1
spec:
containers:
- name: container1
image: kubetm/init
volumeMounts:
- name: empty-dir
mountPath: /mount1
- name: container2
image: kubetm/init
volumeMounts:
- name: empty-dir
mountPath: /mount2
volumes:
- name : empty-dir
emptyDir: {}
우선 해당 YAML 파일을 활용하여 emptyDir
설정이 되어있는 Pod
를 하나 생성해보자.
앞서 설명한 것처럼 emptyDir
은 Pod
안에서 생성이되어 사용이되므로, Pod
에 접속하여 각 컨테이너를 확인해보자.

컨테이너1에 접속하여 확인해보면 위의 YAML 파일에 작성한 것처럼 /mount1
디렉토리가 생성이 되어있다.
이는 Pod
의 empty-dir
과 연결되어 있는 것으로 다음 명령어로 실제 마운트가 되어 있는지 확인해보자.

마운트 설정이 잘되어있는 것을 확인할 수 있다.
그럼 해당 /mount1
경로에 파일을 생성하게 되면 같은 emptyDir
을 공유하는 컨테이너2에도 적용이 되어야 한다.

위 사진과 같이 file.txt
파일을 하나 생성하고 컨테이너2에 접속하여 확인해보자.

컨테이너2에도 파일이 잘 생성된 것을 확인할 수 있다.
그런데 아까 말했던 것과 같이 emptyDir
은 Pod
안에서만 존재하기에 Pod
가 삭제가 되면 사라진다. 한번 확인해보자.

Pod
를 삭제하고 다시 /mount1
경로로 접근하면 기존의 file.txt
가 존재하지 않는 것을 확인할 수 있다.
따라서, emptyDir
은 컨테이너들이 파일을 공유를 해서 쓰되 언제 삭제돼도 상관이 없는 내용들을 담아야 한다.
hostPath
다음은 hostPath
에 관한 실습이다. 우선, 아래 YAML 파일 내용을 살펴보자.
apiVersion: v1
kind: Pod
metadata:
name: pod-volume-3
spec:
nodeSelector:
kubernetes.io/hostname: k8s-node1
containers:
- name: container
image: kubetm/init
volumeMounts:
- name: host-path
mountPath: /mount1
volumes:
- name : host-path
hostPath:
path: /node-v
type: DirectoryOrCreate
YAML 파일의 내용을 살펴보면 위 Pod
는 Node1
에다가 생성한다.
또한, 접근할 마운트 패스는 /mount1
이고 마운트 할 Volume
의 이름은 host-path
이다.
그래서, hostPath
가 정의되어 있는 아래 volumes
를 보면 hostPath
라고 정의가 되어있다.
이때, hostPath
의 경로는 /node-v
라고 정의가 되어있다.
그리고 type: DirectoryOrCreate
은 해당 디렉토리가 없으면 생성이 된다.
위의 YAML 파일을 활용하여 Pod
를 2개 생성해보자.

미리 예상을 해보면 두 Pod
는 같은 Node1
위에 만들어질 것이다.
그럼 두 Pod
는 Node1
의 /node-v
라는 디렉토리를 공유하게 될 것이다.

현재 /node-v
경로에 아무 파일이 없으므로, 새로 생성해준 뒤
같은 디렉토리를 공유하는 pod-volume-3
에 들어가서 확인해보자.

들어가보면 잘 공유되고 있는 것을 확인할 수 있다.
그럼 이제 실제로 Node
에 해당 Volume
이 잘 공유되고 있는지 확인해보자.

Node1
에 접근을 해서 확인해보면 /node-v
디렉토리가 생성되어 있는 것을 확인할 수 있고
아래와 같이 파일이 존재하는 것을 확인할 수 있다.

즉, 실제 노드에 디렉토리와 파일이 존재하고 있다.
그렇기 때문에 Pod
를 삭제하고 다시 만들더라도 /node-1
의 hostPath
는 그대로 있기 때문에 데이터가 유지된다.
하지만 이는 앞서, 설명했던 것처럼 문제가 있다.
Pod
생성시 노드를 지정하지 않고 생성해보자.

이렇게 Pod
가 아까 두 Pod
와는 달리 Node2
에 생겼다.

그 후, 접속해서 /mount1
경로로 접근해보면 디렉토리는 새로 만들어져 있지만 아까의 Node1
과는 다른 경로이기 때문에
Node1
에서 만든 파일이 보이지 않는다.
그렇기에 hostPath
를 사용할 때는 이 점을 유의해야 한다.
PVC/PV
다음으로는 퍼센트 볼륨 클레임과 퍼센트 볼륨에 관한 내용인데,
PVC
와 PV
는 좀 더 깊이있는 Storage
와 Volume
에 관한 학습이 필요하므로, 추후에 다뤄야 할 것 같다.
'Infra > Kubernetes' 카테고리의 다른 글
[Kubernetes] Namespace, ResourceQuota, LimitRange (0) | 2024.09.04 |
---|---|
[Kubernetes] ConfigMap, Secret - Env, Mount (0) | 2024.09.02 |
[Kubernetes] Service - ClusterIP, NodePort, LoadBalancer (0) | 2024.08.27 |
[Kubernetes] Pod - Container, Label, NodeSchedule (0) | 2024.08.12 |
[Kubernetes] 왜 쿠버네티스인가? (0) | 2024.08.05 |