기본 콘텐츠로 건너뛰기

[Kubernetes-Storage] CentOS 8에 Dynamic NFS Client Provisioner 구성하기

[Kubernetes - Storage] How to configure a dynamic storage provisioner for Kubernetes using a network file system on CentOS 8

참고

이 문서는 Network File System을 CentOS 8에 설치하여 NFS Server로 운영하면서 Kubernetes의 PVC (Perssistent Volume Claim) - StorageClass - NFS 로 연동되는 PV (Persistent Volume)를 자동으로 구성하는 방법을 정리한 것입니다.

CentOS 8에 설치되는 NFS를 Kubernetes의 Dynamic Storage Provisioning 으로 활용해서 PV (Persistent Volume)를 구성해 본다.

Network file system 구성

NFS 서버를 구성하는 부분은 CentOS 8에 NFS 설정 및 테스트 글을 참고해서 진행하도록 한다.

NFS 서버는 물리적인 머신으로 네트워크 상에 존재하면 되며, Network을 통해서 Kubernetes Cluster에서 NFS 서버로 접근할 수 있어야 한다.

위에서 구성한 NFS 서버를 사용할 수 있도록 처리하는 NFS Provisioner Pod가 Kubernetes Cluster에 PV를 배포할 수 있도록 하기 위해서는 필요한 권한 설정이 필요하다. 따라서 PV를 배포할 수 있도록 ClusterRole, ClusterRoleBinding, Role, RoleBinding 설정을 가지는 Service Account를 생성해 줘야 한다.

Service Account 생성

서비스 계정을 생성한다. (serviceaccount.yaml)

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner

아래의 명령을 사용해서 Kubernetes에 적용한다.

$ kubectl apply -f serviceaccount.yaml

ClusterRole 생성

Cluster를 위한 역할을 생성한다. (clusterrole.yaml)

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]

아래의 명령을 사용해서 Kubernetes에 적용한다.

$ kubectl apply -f clusterrole.yaml

ClusterRoleBinding 생성

Cluster를 위한 역할 바인딩을 생성한다. (clusterbinding.yaml)

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: nfs-provisioner-otherRoles rules:

  • apiGroups: [""] resources: [“endpoints”] verbs: [“get”, “list”, “watch”, “create”, “update”, “patch”]

apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: nfs-provisioner-otherRoles subjects:

  • kind: ServiceAccount name: nfs-provisioner namespace: default # provisioner가 배포될 네임스페이스 roleRef: kind: Role name: nfs-provisioner-otherRoles apiGroup: rbac.authorization.k8s.io

아래의 명령을 사용해서 Kubernetes에 적용한다.

$ kubectl apply -f clusterbinding.yaml

NFS Client Provisioner 생성

Provisioner는 PVC와 StorageClass를 통해서 자동으로 PV를 구성해 주는 역할을 담당한다.

NFS 서버를 Dynamic Storage Provisioning으로 사용할 수 있도록 NFS Client Dynmaic Provisioner Pod를 배포하도록 구성한다. (deployment-nfs.yaml)

kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 10.0.1.20
            - name: NFS_PATH
              value: /data/NFS
      volumes:
        - name: nfs-client-root
          nfs:
            server: 10.0.1.20
            path: /data/NFS

아래의 명령을 사용해서 Kubernetes에 적용한다.

$ kubectl apply -f deployment-nfs.yaml

StorageClass 생성

NFS 연계를 위한 StorageClass를 생성한다. (storageclass-nfs.yaml)

apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: managed-nfs-storage 
provisioner: fuseim.pri/ifs

아래의 명령을 사용해서 Kubernetes에 적용한다.

$ kubectl apply -f storageclass-nfs.yaml

Statefulset 생성 및 PV 검증

NFS 연계를 검증하기 위한 Statefulset을 생성하고 PV의 생성여부를 검증한다. (statefulset-nfs.yaml)

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx1"
  replicas: 1
  selector:
    matchLabels:
      app: nginx1
  volumeClaimTemplates:
  - metadata:
      name: test 
      annotations:
        volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 2Gi 
  template:
    metadata:
     labels:
       app: nginx1
    spec:
     serviceAccount: nfs-provisioner
     containers:
     - name: nginx1
       image: nginx:1.7.9
       volumeMounts:
       - mountPath: "/mnt"
         name: test

아래의 명령을 사용해서 Kubernetes에 적용한다.

$ kubectl apply -f statefulset-nfs.yaml

Dynamic Provisioner Pod 실행 상태 확인

$ kubectl get all
NAME                                       READY   STATUS    RESTARTS   AGE
pod/nfs-pod-provisioner-5f8686f8dd-bfkgz   1/1     Running   0          8m51s

주의

만일 어느 정도의 시간이 지나도 STATUS가 ContainerCreating 또는 Pending 이라고 나오는 경우는 NFS 설정에 따라서 오류가 발생했을 수 있다.
kubectl describe 으로 확인해 보면 오류 상황을 알 수 있다.

  • connection timeout: NFS 서버의 IP에 접근이 안되는 경우이므로 실제 접근 가능한 IP인지 확인해야 한다. IP가 맞다면 NFS 서버에서 /etc/exports 설정을 다시 확인해 보고 sudo exportfs -ra를 수행하고 다시 처리한다.
  • access denied by server: NFS 서버에는 접근을 했지만 권한이 없는 경우이므로 NFS 서버에서 sudo chmod 666 를 수행하고 다시 처리한다.
  • wrong fs type, bad option...: NFS 관련 패키지가 설치되지 않았을 경우이므로 클러스터의 각 노드에 sudo dnf install -y nfs-utils를 수행하고 다시 처리한다.

그 이외의 오류가 발생했다면 관련된 메시지를 통해서 원인을 파악하고 해결해야 한다.

PVC (Persistent Volume Claim)를 통한 PV 생성 검증

StorageClass를 이용해서 간단한 테스트를 위한 PVC를 구성한다. (fns-pvc.yaml)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc-test
spec:
  storageClassName: managed-nfs-storage # StorageClass 명
  accessModes:
    - ReadWriteMany # 생성될 PV에 적용할 사용 모드
  resources:
    requests:
      storage: 10Mi  # 생성될 PV 크기

참고

사용할 수 있는 AccessMode는 다음과 같다.

  • ReadWriteOnce (RWO): 하나의 Pod에만 마운트되고 하나의 Pod에서만 읽고 쓰기 가능
  • ReadWriteMany (RWX): 여러 개의 Pod에 마운트 가능하고 여러 개의 Pod에서 읽고 쓰기 가능
  • ReadOnlyMany (ROX): 여러 개의 Pod에 마운트 가능하고 여러 개의 Pod에서 읽기는 가능하지만 쓰기는 불가능

테스트이므로 작은 크기로 설정하고 Kubernete에 적용하고 확인한다.

$ kubectl apply -f nfs-pvc.yaml
persistentvolumeclaim/nfs-pvc-test created

$ kubectl get pv,pvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-2dca7757-d8c1-4e80-8a21-f1d661d69b19 10Mi RWX Delete Bound default/nfs-pvc-test managed-nfs-storage 7s

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/nfs-pvc-test Bound pvc-2dca7757-d8c1-4e80-8a21-f1d661d69b19 10Mi RWX managed-nfs-storage 7s

NFS 서버에서 실제 생성된 정보 확인

PVC를 통해서 생성된 PV를 확인했으므로 실제 NFS 서버에도 생성이 되었는지 확인해 보면 된다.

# NFS 서버에서 공유 디렉터리 확인
$ sudo ls /data/NFS
default-nfs-pvc-test-pvc-2dca7757-d8c1-4e80-8a21-f1d661d69b19

실제 PVC와 PV 관련된 이름으로 유추할 수 있는 디렉터리가 생성된 것을 확인할 수 있다.

Deploy된 Pod의 Volume Mount 확인

실제 사용할 애플리케이션 Pod에서도 정상적으로 동작하는지를 확인하기 위해서 간단한 샘플을 구성해 보도록 한다. (nfs-test-app.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ccambo-nfs-test
  labels:
    app: nginxnfs
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginxnfs
  template:
    metadata:
      labels:
        app: nginxnfs
    spec:
      containers:
      - name: nginxnfs
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:                    
          - mountPath: /nfs
            name: nfsvolume          
      volumes:                           
      - name: nfsvolume
        persistentVolumeClaim:           
          claimName: nfs-pvc-test

위의 YAML을 Kubernetes에 적용하고 확인한다.

$ kubectl apply -f nfs-test-app.yaml
deployment.apps/ccambo-nfs-test created

Deployment 확인

$ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE ccambo-nfs-test 0/1 1 0 15s nfs-client-provisioner 1/1 1 1 75m

Pod 확인

$ kubectl get pods NAME READY STATUS RESTARTS AGE ccambo-nfs-test-9d7fc4c6c-25xr4 1/1 Running 0 39s ccambo-nfs-test-9d7fc4c6c-js5pl 1/1 Running 0 39s nfs-client-provisioner-775496b5bb-vpglp 1/1 Running 0 82m

생성된 두 개의 Pod 중에 하나의 Pod에 접속해서 마운트된 경로에서 테스트용 파일을 생성한다.

$ kubectl exec -it ccambo-nfs-test-9d7fc4c6c-25xr4 -- /bin/bash

연결된 Pod에서 마운트된 /nfs 디렉터리로 이동해서 테스트용 파일을 하나 생성한다.

cd /nfs echo “This is NFS test” > /nfs/test.log cat /nfs/test.log This is NFS test

다른 Pod에 연결해서 마운트된 /nfs 디렉터리에 파일이 존재하는지 확인한다.

$ kubectl exec -it ccambo-nfs-test-9d7fc4c6c-js5pl -- /bin/bash

연결된 Pod에서 마운트된 /nfs 디렉터리로 이동해서 테스트용 파일을 확인한다.

ls /nfs test.log

cat /nfs/test.log This is NFS test

위의 테스트와 같이 생성된 POD가 동일한 NFS를 PV로 마운트해서 사용하고 있는 것을 확인할 수 있다.

댓글

이 블로그의 인기 게시물

OData 에 대해서 알아보자.

얼마 전에 어떤 회사에 인터뷰를 하러 간 적이 있었다. 당시 그 회사는 자체 솔루션을 개발할 기술인력을 찾고 있었고 내부적으로 OData를 사용한다고 했다. 좀 창피한 이야기일 수도 있지만 나름 기술적인 부분에서는 많은 정보를 가지고 있다고 했던 것이 무색하게 OData란 단어를 그 회사 사장님에게서 처음 들었다. 작고, 단순한 사이트들만을 계속해서 작업을 하다 보니 어느덧 큰 줄기들을 잃어버린 것을 느끼기 시작했다. 명색이 개발이 좋고, 기술적인 기반을 만들려고 하는 인간이 단어조차도 모른다는 것은 있을 수 없는 것이라서 다시 새로운 단어들과 개념들을 알아보는 시간을 가지려고 한다. OData (Open Data Protocol) 란? 간단히 정리하면 웹 상에서 손쉽게 데이터를 조회하거나 수정할 수 있도록 주고 받는 웹(프로토콜)을 말한다. 서비스 제공자 입장에서는 웹으로 데이터를 제공하는 방식으로 각 포탈 사이트들이 제공하는 OPEN API 포맷을 독자적인 형식이 아니라 오픈된 공통규약으로 제공 가능하며, 개발자는 이 정보를 다양한 언어의 클라이언트 라이브러리로 어플리케이션에서 소비할 수 있도록 사용하면 된다. 공식 사이트는 www.odata.org 이며 많은 언어들을 지원하고 있다. 좀더 상세하게 정의를 해 보면 OData는 Atom Publishing Protocol  (RFC4287) 의 확장 형식이고 REST (REpresentational State Transfer) Protocol 이다. 따라서 웹 브라우저에서 OData 서비스로 노출된 데이터를 볼 수 있다. 그리고 AtomPub 의 확장이라고 했듯이 데이터의 조회만으로 한정되는 것이 아니라 CRUD 작업이 모두 가능하다. Example 웹 브라우저에서 http://services.odata.org/website/odata.svc 를 열어 보도록 하자. This XML file does not appear to have any style in...

C# 에서 Timer 사용할 때 주의할 점.

예전에 알고 지내시던 분의 질문을 받았다. Windows Forms 개발을 하는데, 주기적 (대략 1분)으로 데이터 요청을 하는 프로그램을 작성하기 위해서 Timer 를 사용하는데, 어떤 기능을 처리해야 하기 때문에 Sleep 을 같이 사용했다고 한다. 여기서 발생하는 문제는 Sleep 5초를 주었더니, Timer 까지 5초 동안 멈춘다는 것이다. Timer 라는 것은 기본적으로 시간의 흐름을 측정하는 기능이기 때문에 Sleep 을 했다고 해서 Timer 가 멈추는 일은 생겨서는 안된다. 그러나 실제 샘플을 만들어 보면 ... Timer 가 Sleep 만큼 동작이 멈추는 것을 확인할 수 있다. Windows Forms 는 UI Thread 를 사용하는 것으로 최적화 되어 있으며 여기서 Timer 를 쓰면 UI Thread 에 최적화된 System.Windows.Forms.Timer 가 사용된다. 여기서 문제의 발생이 시작되는 것이다. Sleep 을 사용하게 되면 UI Thread 가 Sleep 이 걸리기 때문에 여기에 속한 Timer 까지도 멈추는 것이다. 이런 문제를 해결하기 위해서는 System.Threading.Timer 를 사용해야 한다. 이 Timer 는 별도의 Thread 에서 동작하기 때문에 Sleep 의 영향을 받지 않는다. 언뜻 보면 쉬운 해결 방법인 것 같지만 Thread 가 분리되었기 때문에 Timer 가 돌아가는 Thread 에서 UI Thread 의 메서드나 컨트롤에 접근하기 위해서는 별도의 명령을 사용해야 하는 문제가 존재한다. 자~ 그럼 여기서 Timer 에 대해서 다시 한번 정리해 보도록 하자. .NET 에서 제공하는 Timer 들 .NET 에서는 기본적으로 3가지 Timer를 제공하고 있다. (MSDN) System.Windows.Forms.Timer - 사용자가 지정한 간격마다 이벤트를 발생시키며 Windows Forms 응용 프로그램에서 사용할 수 있도록 최적화 되어 있다. System...

[Logging] NLog 사용법 정리...

SCSF 에는 기본적으로 Enterprise Library가 사용된다. 예전에도 그랬지만 기능은 훌륭하고 많은 부분에서 최적화(?)된 것일지도 모르지만, 역시나 사용하기에는 뭔가 모르게 무겁고, 사용하지 않는 기능이 더 많다라는 느낌을 지울수가 없다. 이번 프로젝트도 SCSF를 기반으로 하고 있지만, Enterprise Library를 걷어내고 각 부분에 전문화된 오픈 소스를 사용하기로 하였다. 예전에는 Log4Net을 사용했지만, 대량 사용자 환경에서는 메모리 누수와 기타 문제점이 존재한다는 MS 컨설턴트(?)의 전해진 말을 들은 후로는 사용하지 않는다. 대안으로 사용하는 것이 NLog 이다. 조금 후에는 3.0 버전도 나온다고 홈 페이지에 기재되어 있지만, 그 때가 되면 프로젝트는 끝나기 때문에 현재 2.1.0 버전을 사용하기로 했다. [원본 출처] http://cloverink.net/most-useful-nlog-configurations-closed/ 위의 참조 자료에는 다양한 정보들이 존재하므로 꼭 링크를 통해서 관련된 정보를 확인하고 이해하는 것이 좋을 듯 하다. 여기서는 당장 필요한 부분만을 정리하도록 한다. [ Logger 찾기 ] 기본적으로 Logger가 존재하는 클래스를 기반으로 Logger 정보를 구성한다. Logger logger = LogManager.GetCurrentClassLogger(); 주로 Namespace 기반으로 Logger를 설정하는 경우에 유연하게 사용할 수 있다. 또 다른 방법으로는 지정한 문자열로 특정 Logger를 직접 선택하는 방법도 제공된다. 이를 혼용해서 Namespace와 직접 지정 방식을 같이 사용할 수도 있다. 물론 Logger 환경 설정에서 Wildcard (*)를 지정할 수도 있다. Logger logger = LogManager.GetLogger("Database.Connect"); Logger logger = LogManager.Get...