Kubernetes에서 NFS 사용하기

Pod에서 NFS를 volume으로 사용하기 위해 공식 문서예제1를 참고하는 경우가 많을 것인데, 보완 설명을 하려고 한다. AWS EFS를 NFS 서버로 사용할 때 편리하게 쓸 수 있는 efs-provisioner 도 간략하게 소개한다.

공식 문서에서 소개하는 예제는 클러스터 내부에서 NFS 서버를 운영한다. 그리고 PV(PersistentVolume)와 PVC(PersistentVolumeClaim)를 정의해서 Pod이 PVC를 volume으로 지정하는 방식을 쓰고 있다.

더 실용적이고 간단한 방법은 PV와 PVC를 만들지 않고 아래처럼 Pod의 volume으로 NFS를 직접 지정하는 것이다. NFS 서버도 클러스터 밖에서 운영하는 것이 좋다. AWS라면 EFS 서비스를 사용하면 된다.

 1apiVersion: apps/v1
 2kind: Deployment
 3metadata:
 4  name: nfs-example
 5spec:
 6  replicas: 2 
 7  selector:
 8    matchLabels:
 9      app: nfs-example
10  template:
11    metadata:
12      labels:
13        app: nfs-example
14    spec:
15      containers:
16      - image: nginx
17        name: nginx
18        volumeMounts:
19           - mountPath: /nfs
20             name: efs-vol
21      volumes:
22      - name: efs-vol
23        nfs:
24          # 예제로 EFS를 사용했지만 일반적인 NFS 서버면 된다.
25          server: fs-f9352198.efs.ap-northeast-2.amazonaws.com 
26          path: /
27          readOnly: false

nfs 타입의 volume에서 설정할 수 있는 field는 API reference를 참고했다.

공식 문서 예제 보완 설명

먼저 예제의 내용을 따라하면서 충분히 이해한 후 이 포스트를 읽으면 더 도움이 될 것이다. 예제는 관련 개념을 파악하는데 도움이 되지만 실무에서 활용하려면 유의할 점이 있다.

우선 NFS 서버를 클러스터 내부에서 운영하고 있다. 예제로는 좋은 접근이지만 그대로 활용하기에는 아래와 같은 한계가 있다.

  • NFS 서버의 위치를 DNS name 대신 IP로 하드코딩한다.
  • Google Cloud의 Persistent Disk를 사용했다. Persistent Disk는 zone에 종속된다. (AWS라면 EBS와 AZ에 해당)

그리고 PVPVC를 정의한 후 Pod이 PVC를 volume으로 지정하는데, 맥락 상 어색한 방법이다.

IP 하드코딩

예제에선 nfs-server 라는 Service를 정의한다. 그래서 PV 정의에서 DNS name인 nfs-server.default.svc.cluster.local 로 NFS 서버의 위치를 지정하고 있지만 이렇게는 접속이 안된다. 해결하기 위한 임시방편으로 Service의 clusterIP를 사람이 확인해서 코드를 고치는 방식을 안내하고 있다.

여기서 DNS name을 사용할 수 없는 이유가 있다. NFS mount는 Pod이 직접 하는 것이 아니라 node에서 해줘야 한다. 그런데 node는 클러스터 내의 DNS(coreDNS 등)에 쿼리를 하지 않기 때문이다. 그렇게 하도록 설정하는 것이 가능하겠지만 node의 DNS lookup이 Kubernetes 클러스터 내부 DNS를 의존하는 것은 좋은 구성이 아닐 수 있다.

PVC와 PV의 어색한 활용

Pod의 생애와 관계 없이 계속 유지되는 데이터가 필요하면 PV(PersistentVolume)를 반드시 써야 한다고 생각하기 쉽다. 하지만 꼭 그런 것은 아니다.

PV는 PVC(PersistentVolumeClaim)없이 단독으로 volume으로 사용할 수 없다. 그럼 PVC는 왜 필요한가? PVC는 대부분의 경우 dynamic provisioning 하기 위해 사용한다. 클라우드 환경이라면 더욱 그렇다.

예제에서는 이미 존재하는 NFS 서버와 path(/)를 mount한 것이기 때문에 PVC와 PV를 굳이 정의할 필요가 없다. 이 포스트에서 한 것처럼 Pod의 volume에서 바로 nfs를 사용하는 것이 더 간단하고 직관적이다.

그리고, 예제에서 PVC와 PV를 binding한 방식도 적절하지 않다. 이 주제에서 별로 중요한 포인트는 아니라서 각주2에서 설명했으니 관심 있는 분들은 참고하셨으면 한다.

efs-provisioner 소개

AWS에서 EFS를 사용한다면, efs-provisioner를 쓰면 편리하다.

오해하기 쉬운데, EFS를 Kubernets에서 사용하기 위해서 반드시 efs-provisioner를 써야하는 것은 아니다. efs-provisioner는 EFS에 기반한 volume을 dynamic provisioning할 때 사용하는 것이다. 이 포스트에서 주로 다룬 경우처럼 이미 NFS 서버와 mount할 path가 존재한다면 적합하지 않은 도구이다.

이것을 사용하려면 PVC를 정의해야 하는데 PVC는 namespaced resource라서 동일한 namespace 안의 Pod만 접근할 수 있는 한계가 있다.

사용 방법

PVC를 여럿 만드는 경우에도, EFS file system을 하나만 생성하고 efs-provisioner Helm chart도 하나만 설치하면 된다.

Helm chart를 설치할 때 EFS filesystem ID를 알려주면, 이것과 연계된 StorageClass를 생성한다. 여러 Pod들이 공유해야 하는 데이터가 있으면 이 StorageClass를 사용하는 PVC를 생성한 후 여러 Pod들이 동일하게 이 PVC를 volume으로 지정하면 된다. 그러면 efs-provisioner가 EFS filesystem 내에 각 PVC별로 전용 디렉토리를 만들어 제공하는 방식이다.


  1. 참조하는 코드는 2020년 10월 10일 기준 master branch가 가리키는 commit으로 고정했다. ↩︎

  2. 공식문서의 예제에선 PVC에 이미 존재하는 PV를 binding 했다. 이것을 위해 PVC의 storageClassName 설정을 “”로 했는데 PV는 storageClassName을 정의하지 않았기 때문에 binding이 된 것 같다. 이런 일을 정확히 하려면 PV에 label을 달고 PVC에 label selector를 설정해야 할 것이다. ↩︎

comments powered by Disqus