mingming

Kubernetes - PV & PVC 본문

새싹 하이브리드 클라우드/Kubernetes

Kubernetes - PV & PVC

mingming_96 2023. 11. 23. 13:13

PV ( Persistent Volume )  

  • PV는 클러스터 내부의 물리적 또는 가상 스토리지 볼륨을 나타냅니다.
  • PV는 클러스터 관리자 또는 스토리지 관리자에 의해 생성되며, 특정 용량, 접근 모드, 스토리지 특성을 가집니다.
  • PV은 클러스터 전체에서 공유되며, 여러 PVC에 바인딩될 수 있습니다.
  • PVC가 PV에 바인딩되면 해당 PVC는 PV의 용량과 스토리지 특성을 이용하여 데이터를 저장할 수 있습니다.

PVC ( Persistent Volume Claim )

  • PVC는 파드가 사용할 퍼시스턴트 스토리지에 대한 요청을 정의하는 Kubernetes 리소스입니다.
  • PVC는 클러스터 사용자 또는 애플리케이션에서 생성하며, 특정 용량, 접근 모드 및 스토리지 클래스를 지정할 수 있습니다.
  • PVC은 특정 스토리지 클래스에 바인딩되고, 해당 스토리지 클래스의 퍼시스턴트 볼륨과 매칭될 때까지 대기합니다.

PV & PVC 

  • PVC는 PV를 요청하고 해당 PV와 바인딩되어야 합니다.
  • PVC는 PV와 일치하는 스토리지 클래스를 요청할 수 있습니다.
  • PVC는 PV와 독립적으로 생성 및 삭제될 수 있으며, 동일한 PV에 여러 PVC를 바인딩할 수 있습니다.
  • PV는 스토리지 백엔드(로컬 디스크, 네트워크 스토리지, 클라우드 스토리지 등)와 매핑되어 파드에 마운트될 수 있습니다.

PV & PVC LifeCycle 

PV와 PVC 의 생명주기에는 네가지 단계가 존재합니다. 

 

1. 프로비저닝 

프로비저닝은 PV가 만들어지는 단계입니다. PV를 제공하는 방법은 PV를 직접 생성하고 사용하는 정적 프로비저닝과 볼륨 사용 요청이 있을 때마다 자동으로 생성하는 동적 프로비저닝이 있습니다. 

 

2. 바인딩

바인딩은 PVC 리소스를 만들어 준비된 PV 리소스와 연결하는 단계입니다. PVC 리소스에는 원하는 PV 리소스나 스토리지 용량 및 접근 방법 등을 정의하는데, 적절한 PV 리소스가 없다면 요청이 실패하고, 적절한 PV 리소스가 생성되어 연결될 때까지 대기합니다. 

PV와 PVC는 반드시 1:1로만 연결됩니다. 

 

3. 사용 

PVC에서 제공한 볼륨을 파드가 마운트해서 사용하고 있는 단계입니다. 파드가 사용중인 PVC 및 PVC가 사용중인 PV는 임의로 삭제되지 않습니다. 

 

4. 회수 

사용이 끝난 PVC가 종료/삭제 되면 연결된 PV를 회수하는 단계입니다. 회수 정책은 다음과 같이 세 가지 있습니다.

 

A. 유지 (Retain) 

PV 리소스를 그대로 유지합니다 . PV 리소스를 삭제하더라도 외부 스토리지를 사용하고 있다면 외부 스토리지의 데이터는 그대로 남아있습니다. 그렇다고 해서 다른 PVC와 연결되지는 않습니다. 관리자가 수동으로 PV를 회수해야 합니다,

 

B. 삭제 (Delete)

PVC 리소스가 삭제되면 PV 리소스도 같이 삭제되고 연결되어 있는 외부 스토리지의 데이터도 삭제됩니다. 동적 프로비저닝의 기본 정책입니다. 정적 프로비저닝에서 삭제 옵션은 사용되지 않습니다. 

 

C. 재활용 (Recycle)

스토리지의 데이터를 삭제하고 다른 PVC가 PV리소스를 사용 가능하도록 만들어줍니다. 현재는 사용되지 않습니다. 

 

정적 프로비저닝

정적 볼륨은 PV 및 PVC 리소스를 직접 생성하고 사용하는 방식입니다. 

 

1. NFS 서버 구성

## nfs server 패키지 설치 
sudo apt get install -y nfs-kernel-server

## nfs 공유 디렉터리 생성 
sudo mkdir /srv/nfs-volume

## export 설정 
echo "/srv/nfs-volume *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports

## nfs 설정 확인
sudo exportfs -arv

## 웹서버에서 사용할 index.html 생성 
echo "hello nfs Server" | sudo tee /srv/nfs-volume/index.html

 

2. 방화벽 확인 

## 방화벽이 설정되어 있는 경우 
sudo iptables -A INPUT -p tcp --dport 2049 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 2049 -j ACCEPT

 

3. NFS 클라이언트 구성

## ansible로 각 노드에 nfs-client 설치
vi inventory
[node]
192.168.56.21
192.168.56.22
192.168.56.23

ansible node -i ./inventory -m apt -a 'name=nfs-common' --become

## 반복문을 통한 nfs-client 설치

for NODE in kube-node1 kube-node2 kube-node3; do 
	ssh vagrant@NODE "sudo apt install -y nfs-common"
done

## 노드에서 마운트 테스트 
ssh kube-node1

mkdir /test

mount -t nfs 192.168.56.11:/srv/nfs-volume /test

 

4. PV 생성

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: myapp-pv-nfs
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /srv/nfs-volume
    server: 192.168.56.11

 

5. PVC 생성

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myapp-pvc-nfs
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  volumeName: myapp-pv-nfs
  
  
  ## PV 상태가 Bound 상태임
vagrant@kube-control1:~/volume$ kubectl get pv,pvc
NAME                            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
persistentvolume/myapp-pv-nfs   1Gi        RWX            Retain           Bound    default/myapp-pvc-nfs                           20m

NAME                                  STATUS   VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/myapp-pvc-nfs   Bound    myapp-pv-nfs   1Gi        RWX                           16s

 

 

6. ReplicaSet 생성 

---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-rs-nfs
spec:
  replicas: 2
  selector:
    matchLabels:
      vol: nfs
  template:
    metadata:
      labels:
        vol: nfs
    spec:
      containers:
      - name: web-server
        image: nginx:alpine
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nfs-volume
          mountPath: /usr/share/nginx/html  ## 위에 만든 nfs 볼륨을 컨테이너에 마운트
      volumes:
      - name: nfs-volume
        persistentVolumeClaim:
          claimName: myapp-pvc-nfs

 

7. Service 생성

kubectl expose replicaset --type LoadBalancer --name myapp-svc --port 80 --target-port 80

kubectl get svc 

svc 주소로 접속하면 위에서 작성한 index.html 이 반환되는것을 확인할 수 있습니다.

 

 

Delete 옵션 테스트

pv에서의 Delete 옵션은 정적 프로비저닝에선 사용되지 않습니다. 예제를 통해 확인해보겠습니다. 

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: myapp-pv-nfs
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Delete
  nfs:
    path: /srv/nfs-volume
    server: 192.168.56.11
    
    
Events:
  Type     Reason              Age   From                         Message
  ----     ------              ----  ----                         -------
  Warning  VolumeFailedDelete  23s   persistentvolume-controller  error getting deleter volume plugin for volume "myapp-pv-nfs": no deletable volume plugin matched

 

Delete 옵션으로 생성을 하게되면 위에서 보이는 것과 같은 에러를 마주하게 됩니다. 

 

동적 프로비저닝

매번 PV를 생성하고 PVC를 생성해 파드에 볼륨을 제공하는 것은 매우 번거롭기 때문에 스토리지 클래스를 정의하고 PVC 를 요청하면 스토리지 클래스에 의해 PV가 동적으로 프로비저닝되어 사용할 수 있습니다. 단 동적 프로비저닝을 위해선 프로비저너를 따로 설치해주어야 합니다. 

 

1. 프로비저너 설치

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

cd nfs-subdir-external-provisioner/deploy 

vi deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER 
              value: 192.168.56.11 ## 수정할 부분 
            - name: NFS_PATH
              value: /srv/nfs-volume ## 수정할 부분 
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.56.11  ## 수정할 부분 
            path: /srv/nfs-volume  ## 수정할 부분
            
            
            
자신의 서버의 상황에 맞게 해당 부분을 수정후 배포해줍니다. 

kubectl apply -f rbac.yaml -f deployment.yaml -f class.yaml

 

 

2. PVC 생성

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myapp-pvc-nfs
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs-client

vagrant@kube-control1:~/volume$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
persistentvolume/pvc-359f4896-e2ba-4d17-8024-061a3e165e72   1Gi        RWX            Delete           Bound    default/myapp-pvc-nfs   nfs-client              9s

NAME                                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/myapp-pvc-nfs   Bound    pvc-359f4896-e2ba-4d17-8024-061a3e165e72   1Gi        RWX            nfs-client     9s


pvc를 생성하면서 스토리지 클래스에 요청을 보내 pv를 자동으로 생성해줍니다.

 

3. 연결 확인

kubectl describe pv 

vagrant@kube-control1:/srv/nfs-volume$ kubectl describe pv
Name:            pvc-359f4896-e2ba-4d17-8024-061a3e165e72
Labels:          <none>
Annotations:     pv.kubernetes.io/provisioned-by: k8s-sigs.io/nfs-subdir-external-provisioner
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    nfs-client
Status:          Bound
Claim:           default/myapp-pvc-nfs
Reclaim Policy:  Delete  ### Delete 옵션 pvc 삭제될 때 같이 삭제 
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        1Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.56.11
    Path:      /srv/nfs-volume/default-myapp-pvc-nfs-pvc-359f4896-e2ba-4d17-8024-061a3e165e72
    ReadOnly:  false
Events:        <none>

 

 

기본 스토리지 클래스 ( Default Storage Class ) 설정

PVC 리소스 정의시 PV 프로비저닝에 사용할 스토리지 클래스를 직접 정의할 수 있지만 따로 사용할 스토리지 클래스를 지정하지 않더라도 기본으로 적용될 스토리지 클래스를 구성할 수 있습니다. 

kubectl patch storageclasses.storage.k8s.io nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

 

'새싹 하이브리드 클라우드 > Kubernetes' 카테고리의 다른 글

Kubernetes - Autoscaling  (2) 2023.11.26
Kubernetes - Storage  (0) 2023.11.23