쿠버네티스는 워커 노드 중 하나를 선택해 파드를 할당하는데, 특정 노드에서만 데이터를 보관하고 있으면 다른 노드의 파드에서 해당 데이터에 의존하는 비즈니스를 수행할 수 없다.
따라서 퍼시스턴트 볼륨(Persistent Volume)을 사용한다. 퍼시스턴트 볼륨은 워커 노드들이 네트워크 상에서 스토리지를 마운트하여 영속적으로 데이터를 저장할 수 있는 볼륨이다. 따라서 어떤 노드의 파드에 장애가 생기더라도 다른 노드에서 네트워크를 통해 볼륨에 연결하여 특정 데이터에 의존적인 비즈니스를 수행할 수 있다.
NFS(Network File System), AWS EBS(Elastic Block Store), Ceph, GlusterFS 등을 퍼시스턴트 볼륨으로 사용할 수 있다.
NFS 를 네트워크 볼륨으로 사용하기
NFS는 다른 호스트에 있는 파일 시스템의 일부를 자신의 디렉토리인 것처럼 사용하게 해주고 여러 개의 클라이언트가 동시에 마운트할 수 있다.
NFS를 사용하려면 NFS 서버와 클라이언트가 필요하다. 클라이언트 노드에서는 따로 셋업할 것은 없고 NFS 서버만 있으면 된다. (해당 실습은 PV 사용해보기 위해서 최소한으로 필요한 NFS 셋업만 할 예정.)
아래 코드는 nfs 역할을 하는 파드를 1개 생성한다.
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-server spec: selector: matchLabels: role: nfs-server template: metadata: labels: role: nfs-server spec: containers: - name: nfs-server image: gcr.io/google_containers/volume-nfs:0.8 ports: - name: nfs containerPort: 2049 - name: mountd containerPort: 20048 - name: rpcbind containerPort: 111 securityContext: privileged: true
위에서 만드는 nfs 를 다른 노드들에서 접근하기 위해 clusterIP 타입의 서비스를 생성한다.
apiVersion: v1 kind: Service metadata: name: nfs-service spec: ports: - name: nfs port: 2049 - name: mountd port: 20048 - name: rpcbind port: 111 selector: role: nfs-server
그리고 nfs 의 디렉토리를 컨테이너 내부에 마운트하는 파드를 생성한다.
apiVersion: v1 kind: Pod metadata: name: nfs-mount-pod spec: volumes: - name: nfs-volume nfs: path: / server: ${NFS_SERVICE_IP} containers: - name: nfs-mount-container image: busybox args: ["tail", "-f", "/dev/null"] volumeMounts: - name: nfs-volume mountPath: /mnt # NFS 서버의 / 디렉토리를 컨테이너 내부의 /mnt 디렉토리에 마운트시킨다.
NFS_SERVICE_IP
변수는 nfs-service
의 클러스터IP 와 동일하다. nfs-service
의 클러스터IP 를 얻으려면 아래와 같이 서비스의 리소스 정보를 파싱해오면 된다.$ $ ku get svc nfs-service -o jsonpath='{.spec.clusterIP}' 10.103.254.210
trouble shooting
nfs-mount-pod 가 ContainerCreating 상태에서 멈췄을 때
$ ku describe pods nfs-mount-pod
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 70s default-scheduler Successfully assigned default/nfs-mount-pod to ip-10-220-6-77 Warning FailedMount 6s (x8 over 70s) kubelet MountVolume.SetUp failed for volume "nfs-volume" : mount failed: exit status 32 Mounting command: mount Mounting arguments: -t nfs ${NFS_SERVICE_IP}:/ /var/lib/kubelet/pods/7887d473-5d6e-4c90-9bee-260c63d3e004/volumes/kubernetes.io~nfs/nfs-volume Output: mount: /var/lib/kubelet/pods/7887d473-5d6e-4c90-9bee-260c63d3e004/volumes/kubernetes.io~nfs/nfs-volume: bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper program.
PV(Persistent Volume), PVC(Persistent Volume Claim)
아래 코드의 문제점은 파드를 생성하려고 하면 항상 NFS 를 사용해야 한다는 것이다.
apiVersion: v1 kind: Pod metadata: name: nfs-mount-pod spec: volumes: - name: nfs-volume nfs: path: / server: ${NFS_SERVICE_IP} containers: - name: nfs-mount-container image: busybox args: ["tail", "-f", "/dev/null"] volumeMounts: - name: nfs-volume mountPath: /mnt # NFS 서버의 / 디렉토리를 컨테이너 내부의 /mnt 디렉토리에 마운트시킨다.
PV, PVC는 파드를 생성하는 YAML이 파드에 마운트 하려고 하는 네트워크 볼륨이 NFS 인지, AWS EBS 인지 몰라도 볼륨을 사용할 수 있도록 볼륨을 추상화한다.
쿠버네티스 클러스터를 관리하는 어드민과 애플리케이션을 배포하려는 유저가 있다고 하자.
PV, PVC 를 통해 파드에 네트워크 볼륨이 마운트 되기까지 과정은 아래와 같다.
중요한 것은 사용자가 YAML 코드를 가지고 디플로이먼트를 만들 때 볼륨의 상세한 스펙을 정의하지 않아도 된다는 것이다.
YAML 파일에서는 ‘이 디플로이먼트는 볼륨을 마운트 할 수 있어야 한다' 는 의미의 PVC만 명시할 뿐이며, 실세로 마운트 되는 볼륨이 뭔지 몰라도 된다.
AWS EBS 를 PV로 사용하기
- 어드민 - AWS EBS 생성. (콘솔이나 aws-cli 사용) 제약사항은 여기 참고.
- 어드민 - 퍼시스턴트 볼륨 생성 (access mode 종류는 여기 참고.)
EBS_VOLUME_ID
는 콘솔이나 cli 통해서 EBS를 생성할 때 받는 ID 와 동일하다.apiVersion: v1 kind: PersistentVolume metadata: name: ebs-pv spec: capacity: storage: 4Gi accessModes: # 하나의 노드에서 해당 볼륨이 읽기-쓰기로 마운트 될 수 있다. # ReadWriteOnce 접근 모드에서도 파드가 동일 노드에서 구동되는 경우에는 복수의 파드에서 볼륨에 접근할 수 있다. - ReadWriteOnce awsElasticBlockStore: fsType: ext4 volumeID: ${EBS_VOLUME_ID}
PV가 생성된 것을 확인한다. (PV는 네임스페이스에 속하지 않는 클러스터 단위의 오브젝트.)
- 사용자 - PVC 생성