메타 데이터의 끝으로 건너뛰기
메타 데이터의 시작으로 이동

You are viewing an old version of this content. View the current version.

현재와 비교 View Version History

« 이전 버전 18 다음 »


1. Resource Policies

2.1 Request / Limit (최소요구사항/최대 사용량)

  • 쿠버네티스에서 파드를 어느 노드에 배포할지 스케쥴링 할 때, 스케쥴링이 될 노드에 해당 파드가 동작할 수 있는 충분한 자원이 확보가 되어야 합니다.

  • 쿠버네티스에서 파드(컨테이너)에 필요한 리소스 양을 명시 할 수 있도록 설정을 지원합니다.

  • cpu/memory와 같은 컴퓨트 리소스 외에도 gpu/storage 리소스에 대한 쿼터도 지정 할 수 있습니다.

  • CPU는 m(밀리코어, 1코어=1000밀리코어) 단위로 지정하며, 해당 컨테이너에 어느정도의 CPU를 할당할 것인지를 지정합니다.

  • 메모리는 Mi(Mib)를 사용합니다.

8/00-pod-resource.yaml

---
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: nginx:latest
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: rancher/log-aggregator:v0.1.8
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

metrics-server를 이용한 노드 별 리소스 모니터링

root@k8s-master01:~# kubectl top nodes | sort -n -k 2
NAME           CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
k8s-worker02   114m         1%     4030Mi          25%
k8s-worker01   176m         2%     3196Mi          20%
k8s-master03   279m         3%     4727Mi          29%
k8s-master02   347m         4%     4144Mi          26%
k8s-master01   414m         5%     4557Mi          28%

metrics-server를 이용한 파드 별 리소스 모니터링

root@k8s-master01:~# kubectl -n kube-system top pod | sort -n -k 2
NAME                                       CPU(cores)   MEMORY(bytes)
nginx-proxy-k8s-worker01                   1m           16Mi
nginx-proxy-k8s-worker02                   1m           17Mi
dns-autoscaler-59b8867c86-hjkvs            2m           7Mi
kube-controller-manager-k8s-master02       4m           19Mi
kube-controller-manager-k8s-master03       4m           19Mi
nodelocaldns-rdkbl                         4m           15Mi
coredns-588bb58b94-9qcvw                   5m           16Mi
kube-scheduler-k8s-master02                5m           20Mi
nodelocaldns-sd99v                         5m           10Mi
coredns-588bb58b94-zcnvc                   6m           14Mi
kube-proxy-h584h                           7m           22Mi
kube-scheduler-k8s-master03                7m           19Mi
nodelocaldns-9vlvr                         7m           9Mi
nodelocaldns-b8gpm                         7m           9Mi
metrics-server-6f77f96b88-v5jjs            8m           17Mi
nodelocaldns-r4sjf                         8m           16Mi
kube-scheduler-k8s-master01                9m           21Mi
kube-proxy-x8gcq                           12m          22Mi
calico-kube-controllers-75748cc9fd-gqdzw   13m          21Mi
kube-proxy-w87v4                           13m          15Mi
kube-proxy-96tsm                           20m          15Mi
kube-proxy-tj8pv                           24m          14Mi
kube-controller-manager-k8s-master01       36m          52Mi
calico-node-87g22                          42m          85Mi
calico-node-kk5vn                          51m          84Mi
calico-node-wckv7                          51m          85Mi
calico-node-8278d                          64m          91Mi
etcd-k8s-master02                          70m          89Mi
calico-node-4hzx4                          71m          109Mi
kube-apiserver-k8s-master02                77m          303Mi
etcd-k8s-master03                          81m          84Mi
kube-apiserver-k8s-master01                94m          349Mi
etcd-k8s-master01                          99m          95Mi
kube-apiserver-k8s-master03                111m         310Mi

2.2 ResourceQuota

  • 쿠버네티스 운영 시 특정 팀에서 많은 리소스를 요구할 경우 ResourceQuota를 이용하여 네임스페이스별로 사용할 수 있는 리소스의 양을 정하고, 컨테이너 마다 사용 할 수 있는 양을 지정할 수 있습니다.

Resource Name

Description

limits.cpu

Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value.

limits.memory

Across all pods in a non-terminal state, the sum of memory limits cannot exceed this value.

requests.cpu

Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value.

requests.memory

Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value.

hugepages-<size>

Across all pods in a non-terminal state, the number of huge page requests of the specified size cannot exceed this value.

cpu

Same as requests.cpu

memory

Same as requests.memory

  • 쿼터 오브젝트를 생성하여 특정 우선 순위의 파드와 일치 시키며, "low(낮음)", "medium(중간)", "high(높음)"의 세 가지 우선 순위 클래스 중 하나를 가질 수도 있습니다.

8/01-resourcequota.yaml

---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: default
spec:
  hard:
    cpu: "1000"
    memory: 200Gi
    pods: "10"
#  scopeSelector:
#    matchExpressions:
#    - operator : In
#      scopeName: PriorityClass
#      values: ["high"]

8/02-resourcequota-test.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"

root@k8s-master01:~# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-5bccc488d-bqkvz   1/1     Running   0          18h
nginx-deployment-5bccc488d-r4s5d   1/1     Running   0          18h
nginx-deployment-5bccc488d-z29wp   1/1     Running   0          18h

root@k8s-master01:~# kubectl get quota
NAME      AGE   REQUEST                                         LIMIT
default   32s   cpu: 750m/1k, memory: 192Mi/200Gi, pods: 3/10
# k delete quota/default                                                              │INGW64 ~/project/2024
resourcequota "default" deleted 

2.3 LimitRange

  • ResourceQuota를 사용하면 네임스페이스의 리소스를 제한하고, LimitRange는 네임스페이스 내 파드(컨테이너)의 리소스를 제한합니다.

  • 사용자가 컨테이너 리소스를 너무 크게 사용하면 특정 컨테이너가 많은 리소스를 점유하는 것을 방지할 수 있기 때문에 LimitRange를 사용합니다.

8/03-limitrange.yaml

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
  namespace: default
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Pod #Container

8/04-limitrange-test.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "1000m"
          limits:
            memory: "128Mi"
            cpu: "1500m"

# kubectl get event --sort-by='lastTimestamp'
LAST SEEN TYPE REASON OBJECT MESSAGE
78s Warning FailedCreate replicaset/nginx-deployment-8579d7f98c Error creating: pods "nginx-deployment-8579d7f98c-mxw8q" is forbidden: maximum cpu usage per Pod is 800m, but limit is 1500m

# k delete limitrange/cpu-min-max-demo-lr                                             │INGW64 ~/project/2024
limitrange "cpu-min-max-demo-lr" deleted    
# k delete -f 04-limitrange-test.yaml                                                 │
deployment.apps "nginx-deployment" deleted


2. Label & Selector

  • Label

    • Label은 Pod와 같은 객체에 연결된 키/값 쌍입니다.

    • 리소스를 논리적인 그룹으로 나누거나, 식별의 편의를 위해 붙이는 이름표입니다.

    • Label은 생성 시 객체에 첨부할 수 있으며 나중에 언제든지 추가 및 수정할 수 있습니다.

  • Selector

    • 특정 Label에 해당하는 객체를 식별하고 검색할 수 있습니다.

2.1 Node Label

# Node Label 확인
kubectl get nodes --show-labels

# Node Label 추가
kubectl label nodes w1-k8s svc=web

2.2 Pod Selector

8/05-pod-selector.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    svc: web
8# k get pod -o wide
NAME                                      READY   STATUS        RESTARTS       AGE     IP               NODE     NOMINATED NODE   READINESS GATES
frontend                                  2/2     Running       2 (3h3m ago)   4h50m   172.16.132.5     w3-k8s   <none>           <none>
nfs-client-provisioner-5cf87f6995-rdkfr   1/1     Running       40 (14m ago)   14h     172.16.221.136   w1-k8s   <none>           <none>
nginx                                     1/1     Running       0              5m3s    172.16.221.140   w1-k8s   <none>           <none>


3. 쿠버네티스 볼륨

지금까지 우리는 많은 실습을 통해 Pod를 배포하고 Service를 만들어 접속 할 수 있게 되었습니다.

이번 장 에서는 Pod에 사용하는 여러가지 Volume 종류에 대해 알아보고 실습 해 봅니다.

기본적으로 pod의 모든 내용은 휘발성이며 pod가 삭제되거나 재배치되면 데이터도 사라집니다.

이러한 데이터의 유지를 위하여 volume을 사용 합니다.

3.1 emptyDir

  • emptyDir 볼륨은 파드가 노드에 할당될 때 처음 생성되며, 해당 노드에서 파드가 실행되는 동안에만 존재합니다.

  • 이름에서 알 수 있듯이 emptyDir 볼륨은 처음에는 비어있습니다.

  • 파드 내 모든 컨테이너는 emptyDir 볼륨에서 동일한 파일을 읽고 쓸 수 있지만, 해당 볼륨은 각각의 컨테이너에서 동일하거나 다른 경로에 마운트 될 수 있습니다.

  • 노드에서 파드가 제거되면 emptyDir 의 데이터가 영구적으로 삭제됩니다.

  • emptyDir은 주로 Pod내부에서 실행 중인 애플리케이션 간의 휘발성 파일을 공유할 때 유용하게 사용 됩니다.

  • 공유가 필요 없는 볼륨이라면 굳이 emptyDir를 사용 할 필요는 없습니다 명시하지 않은 모든 볼륨의 데이터는 휘발성입니다 (이미지에 존재하지 않는 mountpath생성을 위한 용도로는 사용할 될 수 있습니다)

참고: 컨테이너가 크래시되는 것은 노드에서 파드를 제거하지 않습니다. emptyDir 볼륨의 데이터는 컨테이너 크래시로부터 안전합니다.

(1) emptyDir를 가지는 파드의 yaml 파일을 작성합니다.

  • nginx pod내 empytidir 을 /cache볼륨으로 마운트 하는 yaml입니다

8/06-volume-emptydir.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-vol-1
  labels:
    app: nginx-vol-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-vol-1
  template:
    metadata:
      labels:
        app: nginx-vol-1
    spec:
      containers:
      - name: nginx-vol-1
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: cache-volume
          mountPath: /cache
      volumes:
      - name: cache-volume
        emptyDir: {}

  • 해당 yaml 파일을 적용하여 파드를 생성하여 마운트 상태를 확인합니다.

[root@m-k8s vagrant]# k apply -f 012.nginx-vol-1.yml 
deployment.apps/nginx-vol-1 created
[root@m-k8s vagrant]# k get pods
NAME                           READY   STATUS    RESTARTS   AGE
nginx-vol-1-77d6454655-xscpd   1/1     Running   0          38s

 
위와같이 Running 상태가 되면 pod내 볼륨이 정상적으로 mount되었는지 확인 합니다
 
[root@m-k8s vagrant]# k exec -it nginx-vol-1-77d6454655-xscpd -- df -h
Filesystem                   Size  Used Avail Use% Mounted on
overlay                       37G  5.4G   32G  15% /
tmpfs                         64M     0   64M   0% /dev
tmpfs                        1.5G     0  1.5G   0% /sys/fs/cgroup
/dev/mapper/centos_k8s-root   37G  5.4G   32G  15% /cache
shm                           64M     0   64M   0% /dev/shm
tmpfs                        2.9G   12K  2.9G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                        1.5G     0  1.5G   0% /proc/acpi
tmpfs                        1.5G     0  1.5G   0% /proc/scsi
tmpfs                        1.5G     0  1.5G   0% /sys/firmware
 
# 마운트 확인 
/dev/mapper/centos_k8s-root   37G  5.4G   32G  15% /cache 가 정상적으로 마운트 되었습니다

/dev/mapper/centos_k8s-root은 worker node의 root disk입니다
 
[root@cp-k8s mapper]# ssh vagrant@w1-k8s
vagrant@w1-k8s's password: 
[vagrant@w1-k8s ~]$ ls -al /dev/mapper/centos_k8s-root 
lrwxrwxrwx. 1 root root 7 Sep 12 13:49 /dev/mapper/centos_k8s-root -> ../dm-0


# 파일을 쓰고 파드를 삭제하여 데이터가 존재하는지 확인 합니다
[root@m-k8s mapper]# k get pods
NAME                           READY   STATUS    RESTARTS   AGE
nginx-vol-1-77d6454655-xscpd   1/1     Running   0          4m34s
[root@m-k8s mapper]# k exec -it nginx-vol-1-77d6454655-xscpd -- bash -c "echo 'test' > /cache/test.txt "

[root@m-k8s mapper]# k exec -it nginx-vol-1-77d6454655-xscpd -- ls -al /cache
total 4
drwxrwxrwx. 2 root root 22 Sep 13 00:33 .
drwxr-xr-x. 1 root root 52 Sep 13 00:28 ..
-rw-r--r--. 1 root root  5 Sep 13 00:33 test.txt


[root@m-k8s vagrant]# k get pod
NAME                           READY   STATUS    RESTARTS   AGE
nginx-vol-1-77d6454655-xscpd   1/1     Running   0          8s
[root@m-k8s vagrant]# k delete pod/nginx-vol-1-77d6454655-xscpd 
pod "nginx-vol-1-77d6454655-xscpd" deleted


# deploy로 pod가 재배포 되길 기다립니다
[root@m-k8s vagrant]# k get pod
NAME                           READY   STATUS    RESTARTS   AGE
nginx-vol-1-77d6454655-fd9tg   1/1     Running   0          6s


# 새로운 pod가 배포 되었습니다
# 이후 해당 pod에 마운트된 /cache볼륨에 이전에 생성한 파일이 남아있는지 확인 합니다.
[root@m-k8s vagrant]# kubectl  exec -it nginx-vol-1-77d6454655-fd9tg -- ls -al /cache
total 0
drwxrwxrwx. 2 root root  6 Sep 13 02:01 .
drwxr-xr-x. 1 root root 52 Sep 13 02:01 ..
 
 
이전에 만든 test.txt가 존재하지 않습니다(Pod가 제거되면 emptydir속성의 볼륨도 삭제)
 
# 다음 실습을 위해 환경을 정리 합니다.
[root@m-k8s vagrant]# k delete -f 012.nginx-vol-1.yml
deployment.apps "nginx-vol-1" deleted
 

(2) 멀티 컨테이너를 가지는 파드에서의 emptyDir 볼륨 파일공유를 확인합니다.

8/07-volume-emptydir-multicon.yaml

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-vol-2
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - name: cache-volume
      mountPath: /cache
      readOnly: true           #cache 디렉토리를 read only로 마운트 한다
  - image: luksa/fortune
    name: fortune
    volumeMounts:
    - name: cache-volume
      mountPath: /cache
  volumes:
  - name: cache-volume

  • 생성 된 yaml을 적용하여 pod를 생성합니다.

kubectl apply -f 012.nginx-vol-2.yml
pod/nginx-vol-2 created
 
kubectl get pod
NAME          READY   STATUS    RESTARTS   AGE
nginx-vol-2   2/2     Running   0          8s
 
# 확인하면 포드내 컨테이너가 2개로 보입니다
# 생성된 컨테이너를 확인합니다
 
# 컨테이너가 2개 이상인경우 get pod 명령어를 활용해서 컨테이너를 식별하고 제어하지 못합니다.
$ kubectl describe pod/nginx-vol-2
......
Containers:
  nginx:
    Container ID:   containerd://a9eac4ae13b2a8f052fe9bf6eca53c93e95c0de7ce4352cf1e77a0c4d2e1c5bb
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:6926dd802f40e5e7257fded83e0d8030039642e4e10c4a98a6478e9c6fe06153
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 13 Sep 2023 11:05:21 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /cache from cache-volume (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pjccg (ro)
  fortune:
    Container ID:   containerd://6e343b738170e0b1d0b0fa18fe641c0cd0b1257ae3cab9d7fc75fff041f09084
    Image:          luksa/fortune
    Image ID:       docker.io/luksa/fortune@sha256:814dd72ecc4b431560ca6ede8649fc4862c401ba5b6d37520e6672e0657deea9
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 13 Sep 2023 11:05:33 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /cache from cache-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-pjccg (ro)
 
Volumes:
  cache-volume:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>

# 두번째 yml을 보면 volume type을 EmptyDir로 선언하는것을 생략하였습니다 아무런 type이 없으면 기본값으로 EmptyDir를 사용 하게 됩니다
  .....
 
# nginx와 fortune 2개의 컨테이너가 생성된것을 확인 할수 있습니다
# rw로 마운트된 fortune에서 파일을 생성하고 nginx컨테이너에서 파일을 볼수 있는지 실습 해봅시다
 
# pod내 컨테이너 선택은 -c옵션을 이용합니다
# fortune 컨테이너에서 만든 파일을 같은 pod내에 있는 nginx컨테이너테서 확인 할 수 있습니다.
[root@m-k8s vagrant]# kubectl exec nginx-vol-2 -c fortune  -- bash -c ' echo "test" > /cache/test.txt '
[root@m-k8s vagrant]# kubectl exec nginx-vol-2 -c fortune  -- ls -l /cache
total 4
-rw-r--r--. 1 root root 5 Sep 13 02:19 test.txt
[root@m-k8s vagrant]# kubectl exec nginx-vol-2 -c nginx  -- ls -l /cache
total 4
-rw-r--r--. 1 root root 5 Sep 13 02:19 test.txt
 
# 아래와 같이 nginx는 ro권한밖에 없으므로, 실제 write는 불가능합니다.
[root@m-k8s vagrant]# kubectl exec nginx-vol-2 -c nginx  -- bash -c 'echo "test" > /cache/test2.txt'
bash: line 1: /cache/test2.txt: Read-only file system
command terminated with exit code 1
 
# 실습환경을 정리합니다. 
kubectl delete -f 012.nginx-vol-2.yml
pod "nginx-vol-2" deleted
 
*기본적으로 emptydir은 worker노드에 디스크(파일시스템)에 생성되지만 상황에따라 memory나 fc disk등에 생성 할 수도 있습니다.

3.2 hostpath

  • hostPath 볼륨은 호스트 노드의 파일시스템에 있는 파일이나 디렉터리를 파드에 마운트 합니다.

  • emptydir과 달리 pod를 삭제하여도 워커노드의 파일은 삭제되지 않습니다.

  • 각 워커노드의 파일이 다를 수 있으므로 사용에 주의하도록 합니다.

    • node seletor를 이용하여 필요한 볼륨이 있는 node만 스케줄되게 하거나 모든 node가 동일한 파일을 가지거나 등의 조치를 하지 않고 배포 시 문제가 발생 할 수 있습니다.

hostpath는 상황에 따라 다르나 매우 중요한 개념입니다.

(1) hostpath volume을 위한 경로에 파일을 생성합니다.

# worker1 노드에 hostpath를 설정하고 해당 폴더를 사용하도록 설정 합니다
[root@m-k8s vagrant]# ssh vagrant@w1-k8s
vagrant@w1-k8s's password: 
Last failed login: Wed Sep 13 10:53:11 KST 2023 from 192.168.1.10 on ssh:notty
There were 3 failed login attempts since the last successful login.
[vagrant@w1-k8s ~]$ sudo -i

# 마운트 될 경로를 생성합니다.
mkdir /hostpath
 
# 노드에 마운트 될 경로에 파일/내용을 작성합니다.
cat <<EOF | tee /hostpath/index.html
hihihihi
EOF 

(2) 해당 hostpath를 사용하는 yaml을 작성합니다.

8/08-volume-hostpath.yaml

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-vol-3
spec:
  containers:
  - name: nginx-vol-3
    image: nginx
    volumeMounts:
    - name: host-path-volume
      mountPath: /usr/share/nginx/html
  nodeName: w1-k8s             #노드네임을 이용하여 woker1번에서 구동되도록 만든다. 본인 서버의 워크 노드 이름으로 변경한다. 
  volumes:
  - name: host-path-volume
    hostPath:
      path: /hostpath        # Path를 잘못 입력하여 못찾는 경우 컨테이너가 기동되지 않는다.
      type: Directory

(3) 해당 pod를 배포하고 확인 합니다.

# yaml 파일을 적용합니다.
kubectl apply -f 08-volume-hostpath.yaml
 
# 생성된 파드에서 마운트 된 경로의 파일을 확인합니다.
kubectl exec -it nginx-vol-3 -- ls -l /usr/share/nginx/html
total 4
-rw-r--r-- 1 root root 9 Apr 28 01:17 index.html

# 파일 내용을 확인합니다.
kubectl exec -it nginx-vol-3 -- cat /usr/share/nginx/html/index.html
hihihihi
 

[root@m-k8s vagrant]# kubectl get pod -o wide
NAME          READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
nginx-vol-3   1/1     Running   0          107s   172.16.221.185   w1-k8s   <none>           <none>
[root@m-k8s vagrant]# 
[root@m-k8s vagrant]# curl 172.16.221.185
hihihihi
 
 
# node에서 생성한 파일이 pod에 hostpath를 이용해 마운트 된것을 확인 할 수 있습니다. 
# hostpath의 경우 권한이 다르면 사용할수 없으므로 컨테이너 내 사용자로 권한을 바꾸거나 root사용자로 구동되도록 하여야 합니다.
 
 
# 실습을 위해 실습환경을 정리합니다.
[root@m-k8s vagrant]# kubectl delete -f 012.nginx-vol-3.yml
pod "nginx-vol-3" deleted

3.3 NFS

  • NFS를 volume으로 사용할 수 있습니다.

  • NFS를 이용하면 여러 pod에 동일한 volume을 마운트 할 수 있고, 여러 노드가 읽고 쓸 수 있으므로 파일 공유에 많은 이점이 있습니다.

(1) 실습을 위하여 worker2번 노드에 nfs서버를 임시로 구성하여 진행 합니다

# nfs-utils 패키지를 설치 합니다
apt install -y nfs-server
 
 
# nfs 공유로 사용할 폴더를 생성하고 테스트에 사용할 index.html 파일을 생성합니다
mkdir /nfs
chmod 777 /nfs 
echo "hihihi" > /nfs/index.html
 
# nfs공유 설정을 해줍니다.
cat <<EOF | tee /etc/exports
/nfs *(rw,no_root_squash)
EOF
 
 
# nfs서버 서비스를 실행하고 활성화 합니다.
systemctl enable nfs-server --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
 
# 확인
# 출력값이 없으면 [systemctl restart nfs-server] 실행합니다.
[root@w2-k8s ~]# exportfs
/nfs            w2-k8s
 
# nfs설정이 정상인지 worker1번에서 마운트 하여 테스트 합니다 
ssh w1-k8s
 
# 모든 마스터/워커노드에서 nfs 패키지를 설치합니다.
apt install nfs-common
 
mkdir /nfs
mount w2-k8s:/nfs /nfs
df -h
Filesystem      Size  Used Avail Use% Mounted on
.....
192.168.1.102:/nfs           38770304 4569856  34200448  12% /nfs
.....
 
 
[root@w1-k8s /]# ll /nfs
total 4
-rw-r--r--. 1 root root 6 Sep 13 12:47 index.html
 
#nfs서버가 정상 작동되는것을 확인 했으니 이제 실습을 진행 합니다.

#/nfs 폴더를 umount 해줍니다.
umount /nfs    
 
이제 설정이 완료된 nfs볼륨을 이용하여 pod를 생성하여 줍니다

(2) 설정이 완료된 NFS 볼륨을 이용하여 마운트하는 파드 yaml 파일을 작성합니다.

8/09-volume-nfs.yaml

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-vol-4
spec:
  nodeName: w1-k8s
  containers:
  - name: nginx-vol-4
    image: nginx
    volumeMounts:
    - name: nfs-volume
      mountPath: /usr/share/nginx/html
  volumes:
  - name: nfs-volume
    nfs:
      path: /nfs
      server: w2-k8s

(3) 생성된 yaml을 적용하여 파드를 생성 합니다

kubectl apply -f 012.nginx-vol-4.yaml
pod/nginx-vol-4 created
 
[root@m-k8s vagrant]# k get pod -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
nginx-vol-4   1/1     Running   0          53s   172.16.221.186   w1-k8s   <none>           <none>
 
[root@m-k8s vagrant]# curl 172.16.221.186
hiihi
 
kubectl exec -it nginx-vol-4 -- df -h
Filesystem      Size  Used Avail Use% Mounted on
...
w2-k8s:/nfs                   37G  4.4G   33G  12% /usr/share/nginx/html
...
 
 
# pod가 구동중인 worker1의 마운트 상태 확인
[root@w1-k8s /]# df -h
Filesystem      Size  Used Avail Use% Mounted on
 
w2-k8s:/nfs                   37G  4.4G   33G  12% /var/lib/kubelet/pods/23229ac8-acb5-4aa0-b
0f1-6d8f36236366/volumes/kubernetes.io~nfs/nfs-volume 
 
#hostpath의 경우 해당파일이 존재하는 워커노드에서만 수행되도록 설정하였으나 nfs사용시에는 어느 노드에서 구동되어도 상관이 없습니다
 
 
# 다음 실습을 위해 실습환경을 정리 합니다.
kubectl delete -f 012.nginx-vol-4.yml
pod "nginx-vol-4" deleted

3.4 Persistent Volume(PV)

  • PV(PersistentVolume): PV는 관리자가 프로비저닝했거나 스토리지 클래스를 사용해 동적으로 프로비저닝한 클러스터의 스토리지입니다. PV는 볼륨과 같은 볼륨 플러그인이지만 PV를 사용하는 개별 Pod와 독립적인 수명 주기를 가집니다.

  • PVC(PersistentVolumeClaim): PVC는 사용자가 볼륨을 사용하기 위해 PV에 하는 스토리지 요청입니다. Pod와 유사하게 Pod는 노드 리소스를 소비하고, PVC는 PV 리소스를 소비합니다.

지금까지의 볼륨은 empty dir를 제외하고는 사용자(개발자)가 볼륨의 정보를 알아야 했습니다

볼륨의 정보를 몰라도 사용할 수 있게 볼륨을 PV로 생성 해 두면 개발자는 pvc를 생성하여 해당 볼륨을 사용할 수 있게 됩니다

persistent volume(이하 PV) 를 persistent volume claim(이하PVC) 로 요청하여 할당받아 사용하는 방식 입니다

대부분의 production환경은 PV를 사용합니다. NAS도 PV로 사용하지 앞서 실습한 형태의 구성으로 운영 환경에서 사용하지 않습니다.

(1) PV Lifecycle

Provisioning

PV를 생성하는 단계로 프로비저닝 방법에는 두 가지가 있습니다.

  • 정적 프로비저닝

    • 정적 프로비저닝은 클러스터 관리자가 미리 적정 용량의 PV를 만들어 두고 사용자의 요청이 있을 시 미리 만들어 둔 PV를 할당하는 방식입니다.

    • 사용할 수 있는 스토리지 용량에 제한이 있을 때 유용합니다.

  • 동적 프로비저닝

    • 동적 프로비저닝은 사용자가 PVC를 거쳐서 PV를 요청했을 때 생성해 제공합니다.

    • 클러스터에 사용자가 원하는 만큼의 스토리지 용량이 있다면, 원하는 용량만큼을 생성해서 사용할 수 있습니다.

Binding

바인딩은 프로비저닝으로 생성된 PV를 PVC와 연결하는 단계입니다. PVC에서 원하는 스토리지 용량과 접근방법을 명시해서 용청하면 거기에 맞는 PV가 할당됩니다. PV와 PVC의 매핑은 1대1 관계입니다.

Using

PVC는 Pod에 설정되고, Pod는 PVC를 볼륨으로 인식해서 사용합니다. 클러스터는 PVC를 확인하여 바인딩된 PV를 찾고 해당 볼륨을 Pod에서 사용할 수 있도록 해줍니다. 할당된 PVC는 파드를 유지하는 동안 계속 사용되며, 시스템에서 임의로 삭제할 수 없습니다.

Reclaiming

사용이 끝난 PVC는 삭제되고, PV를 초기화(reclaim)하는 과정을 거칩니다. PV는 기존에 사용했던 PVC가 아니더라도 다른 PVC로 재활용이 가능합니다.

(2) PV는 Policy

  • Access Policy 

    • RWO-ReadWriteOnce : 단일 노드만 읽기/쓰기를 위해 볼륨을 마운트 할 수 있다. (블록 디바이스는 이 모드만 지원)

    • ROX-ReadOnlyMany : 여러 노드가 읽기 위해 볼륨을 마운트 할 수 있다.(잘 사용안함)

    • RWX-ReadWriteMany: 여러 노드가 읽기 쓰기를 위해 볼륨을 마운트 할 수 있다.(지원하는 공유파일시스템에서 사용)

      • Pod의 수가 아니라 볼륨을 동시에 사용할 수 있는 worker 노드의 수에 해당 한다. 또한 nfs, ceph등 형태에 따라서 지원 하지 않는 AccessPolicy가 있을 수 있다. 예를 들면 nfs는 rwx를 지원하지만 rbd등 block device는 rwo만 지원 합니다.

  • Reclaim Policy

    • 영구 볼륨이 삭제 요청할 때 아래 3가지 회수 정책이 있다. retain과 delete는 다시 사용 할 수 없으며, recycle은 재사용이 가능하나 이제 사용되지 않는다

delete

AWS EBS, GCE PD, 애저 디스크 또는 오픈스택 등 스토리지 자체에서 삭제 

인스턴스 삭제 시 볼륨도 같이 삭제(테스트 시 보통 많이 사용함)

recycle

내용만 삭제(rm -rf /volume/*)

recycle은 사용 안되며, dynamic provisioning을 대체 하는 것을 권고 하고 있음

retain

수동으로 회수가 필요한 볼륨

보통 retain을 기본값으로 사용함.

Retain 반환 정책은 리소스를 수동으로 반환할 수 있게 한다. 퍼시스턴트 볼륨 클레임이 삭제되면 퍼시스턴트볼륨은 여전히 존재하며 볼륨은 "릴리스 된" 것으로 간주된다. 그러나 이전 요청자의 데이터가 여전히 볼륨에 남아 있기 때문에 다른 요청에 대해서는 아직 사용할 수 없다. 관리자는 다음 단계에 따라 볼륨을 수동으로 반환할 수 있다.

  1. 퍼시스턴트볼륨을 삭제한다. PV가 삭제된 후에도 외부 인프라(예: AWS EBS, GCE PD, Azure Disk 또는 Cinder 볼륨)의 관련 스토리지 자산이 존재한다.

  2. 관련 스토리지 자산의 데이터를 수동으로 삭제한다.

  3. 연결된 스토리지 자산을 수동으로 삭제하거나 동일한 스토리지 자산을 재사용하려는 경우 스토리지 자산 정의로 새 퍼시스턴트볼륨을 생성한다

실습은 dynamic 프로비저닝을 하는 방법을 진행할 것입니다. 넷앱이나 유명한 스토리지 회사는 CSI를 지원하고 있습니다. AWS는 그 기능이 몇 개 빠진 상황입니다.

(3) 마지막 실습한 nfs를 단순 pv로 만들어 사용하는 실습을 진행합니다.

8/10-volume-pv.yaml

---
apiVersion: v1
kind: PersistentVolume              # PersistenVolume형태로 kind정의
metadata:
  name: nginx-vol-pv-5
  labels:
    release: stable
    capacity: 1Gi
spec:
  capacity:
    storage: 1Gi                  # 1G 사이즈로 생성
  accessModes:               
  - ReadWriteOnce
  - ReadOnlyMany
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /nfs
    server: w2-k8s            

  • yaml을 이용하여 pv를 생성합니다.

[root@m-k8s vagrant]# kubectl apply -f 10-volume-pv.yaml
persistentvolume/nginx-vol-pv-5 created
[root@m-k8s vagrant]# k get pv
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nginx-vol-pv-5   1Gi        RWO,ROX,RWX    Retain           Available                                   6s

  • PV가 설정한대로 생성되면, 해당 PV를 사용하는 PVC를 생성하는 yaml 파일을 작성합니다.

8/11-volume-pvc.yaml

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc-1
spec:
  storageClassName: ""
  accessModes:
    - ReadWriteOnce     # 서로 매칭되는 클래스를 사용하겠다는 의미
  resources:
    requests:
      storage: 1Gi

  • yaml 파일을 적용하여 PVC를 생성 합니다.

kubectl apply -f 11-volume-pvc.yaml
 
 
# 생성된 pvc를 확인합니다.
[root@m-k8s vagrant]# k get pvc
NAME          STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nginx-pvc-1   Bound    nginx-vol-pv-5   1Gi        RWO,ROX,RWX                   7s

# 앞서 STATUS가 Available의 값과 CLAIM이 비어있던 것이 PVC에 바인딩 된 것을 아래와 같이 볼 수 있습니다.
[root@m-k8s vagrant]# k get pv
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
nginx-vol-pv-5   1Gi        RWO,ROX,RWX    Retain           Bound    default/nginx-pvc-1                           2m44s
 
# 해당 pvc가 요청하는 조건에 맞는 pv가 이미 생성되었으므로 Bound(할당) 되었습니다. Pod는 PV를 사용하는 것이 아니라 PVC를 사용하는 것입니다.

  • 해당 PVC를 사용하는 파드를 생성하는 yaml 파일을 작성합니다.

8/12-volume-pvc-pod.yaml

--- 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-vol-6
spec:
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - mountPath: /vol
      name: nginx-volume
  volumes:
    - name:  nginx-volume
      persistentVolumeClaim:
        claimName: nginx-pvc-1 

  • yaml 파일을 적용하여 파드를 생성합니다.

# 파드를 생성합니다.
kubectl apply -f 12-volume-pvc-pod.yaml
pod/nginx-vol-6 created
 
# 컨테이너 안에서 마운트 상태를 확인합니다.
kubectl exec -it nginx-vol-6 -- df -h
 
Filesystem      Size  Used Avail Use% Mounted on
w2-k8s:/nfs                   37G  4.4G   33G  12% /vol
 
# 정상적으로 마운트 된것을 확인 할수 있습니다
## 실습 환경을 정리 합니다
# 파드 삭제
kubectl delete pod nginx-vol-6
pod "nginx-vol-6" deleted
 
# PVC 삭제 
kubectl delete pvc/nginx-pvc-1
persistentvolumeclaim "nginx-pvc-1" deleted
 
# PV 삭제 
kubectl delete pv/nginx-vol-pv-5
persistentvolume "nginx-vol-pv-5" deleted

3.5 동적 프로비저닝 + 프로덕션 환경 테스트

Dynamic 프로비저닝을 하려면 CSI를 제공하는 스토리지가 필요합니다. 보통 운영환경에서는 모두 이 방식을 사용합니다.

동적 프로비저닝의 의미는 PV를 PVC요청에 따라 동적으로 할당 한다는 것을 의미 합니다.

  • 3.4 항목에서 PV를 수동으로 생성하고 해당 PV를 사용할 PVC를 선언하고 해당 PVC를 pod에 할당하여 사용 했습니다.

  • 동적 프로비저닝은 PVC가 생성되면 해당 PVC에 알맞도록 PV를 동적으로 생성하여 주는 기능입니다.

    • 사용자가 스토리지나 볼륨에 대해 아무것도 몰라도 운영자가 볼륨을 만들지 않아도 사용자가 요청하면(PVC) 해당 요청에 맞는 PV가 생성되는 방식 입니다.

  • 실제 프로덕션 환경에서 가장 많이 사용되고 있으며 일반적으로 동적 프로비저닝을 지원하는 스토리지 객체가 있어야 합니다

  • 부가기능을위해 CSI(Container Storage Interface)를 지원하는 스토리지를 사용하는것이 좋습니다.

  • CSI를 지원하는 스토리지는 각 클라우드 공급자의 스토리지, Netapp등의 스토리지, ceph 등이 있습니다.

(1) 동적 프로비저닝으로 nfs를 가지고 실습합니다.

  • NFS 프로비저너를 설치합니다.

[root@m-k8s vagrant]# git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner.git
[root@m-k8s vagrant]# cd nfs-subdir-external-provisioner/deploy

  • pvc만 지정하여 구성하면, 자동적으로 하드웨어에 용량이 할당되는 형태의 서비스가 시작됩니다.

...         
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: w2-k8s
            - name: NFS_PATH
              value: /nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: w2-k8s
            path: /nfs
[root@m-k8s deploy]# k apply -f rbac.yaml
[root@m-k8s deploy]# k apply -f deployment.yaml 
[root@m-k8s deploy]# k apply -f class.yaml 

  • pvc만 지정하여 구성하면, 자동적으로 하드웨어에 용량이 할당되는 형태의 서비스가 시작됩니다.

8/13-volume-dynamic-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc-dynamic
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1G
  storageClassName: 'nfs-client'
[root@m-k8s deploy]# k apply -f 012.mypvc-dynamic.yaml 
[root@m-k8s deploy]# k get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
persistentvolume/pvc-45758961-7542-4f32-a959-d5ee0bdc17e8   1G         RWX            Delete           Bound    default/mypvc-dynamic   nfs-client              4s

NAME                                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mypvc-dynamic   Bound    pvc-45758961-7542-4f32-a959-d5ee0bdc17e8   1G         RWX            nfs-client     4s

[root@m-k8s deploy]# k get sc
NAME         PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-client   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           false                  49m

  • pvc조건에 부합하는 pv가 동적으로 프로비저닝 된것을 확인 할 수 있습니다.

[root@w2-k8s nfs]# ls
default-mypvc-dynamic-pvc-45758961-7542-4f32-a959-d5ee0bdc17e8 

  • 정상적으로 volume을 사용할 수 있는지 확인합니다.

8/14-volume-nfs-pvc.yaml

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs-client
[root@m-k8s vagrant]# k get pv | grep nfs
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
pvc-5ed060f5-d2a9-4966-b23b-cfcfdc06c7cf   1Gi        RWO            Delete           Bound    default/nfs-pvc         nfs-client              98s

[root@m-k8s vagrant]# k get pvc | grep nfs
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc         Bound    pvc-5ed060f5-d2a9-4966-b23b-cfcfdc06c7cf   1Gi        RWO            nfs-client     100s

  • PV, PVC 생성된 것을 확인합니다.

[root@m-k8s vagrant]# k describe pv/pvc-45758961-7542-4f32-a959-d5ee0bdc17e8
Name:            pvc-45758961-7542-4f32-a959-d5ee0bdc17e8
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/mypvc-dynamic
Reclaim Policy:  Delete
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        1G
Node Affinity:   <none>
Message:         
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    w2-k8s
    Path:      /nfs/default-mypvc-dynamic-pvc-45758961-7542-4f32-a959-d5ee0bdc17e8
    ReadOnly:  false
Events:        <none>

[root@m-k8s vagrant]# k describe pvc/nfs-pvc
Name:          nfs-pvc
Namespace:     default
StorageClass:  nfs-client
Status:        Bound
Volume:        pvc-5ed060f5-d2a9-4966-b23b-cfcfdc06c7cf
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
               volume.beta.kubernetes.io/storage-provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
               volume.kubernetes.io/storage-provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      1Gi
Access Modes:  RWO
VolumeMode:    Filesystem
Used By:       <none>
Events:
  Type    Reason                 Age    From                                                                                                                     Message
  ----    ------                 ----   ----                                                                                                                     -------
  Normal  ExternalProvisioning   5m55s  persistentvolume-controller                                                                                              waiting for a volume to be created, either by external provisioner "k8s-sigs.io/nfs-subdir-external-provisioner" or manually created by system administrator
  Normal  Provisioning           5m55s  k8s-sigs.io/nfs-subdir-external-provisioner_nfs-client-provisioner-554d86f86-jlgt6_2df91802-b760-445b-b3f7-17fb3aa51f3f  External provisioner is provisioning volume for claim "default/nfs-pvc"
  Normal  ProvisioningSucceeded  5m55s  k8s-sigs.io/nfs-subdir-external-provisioner_nfs-client-provisioner-554d86f86-jlgt6_2df91802-b760-445b-b3f7-17fb3aa51f3f  Successfully provisioned volume pvc-5ed060f5-d2a9-4966-b23b-cfcfdc06c7cf

  • 해당 pvc를 이용하여 pod를 구동해 정상적으로 volume이 attach 되는지 확인 합니다.

8/15-volume-nfs-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-nfs-pvc
spec:
  containers:
  - name: nginx-nfs-pvc
    image: nginx
    volumeMounts:
    - mountPath: /vol
      name: nginx-volume
  volumes:
    - name:  nginx-volume
      persistentVolumeClaim:
        claimName: nfs-pvc
[root@m-k8s vagrant]# k apply -f 012.nginx-nfs-pvc.yml 
pod/nginx-nfs-pvc created
[root@m-k8s vagrant]# k get pods
NAME                                     READY   STATUS              RESTARTS   AGE
gitea-66bc89d659-z577q                   1/1     Running             0          6h15m
nfs-client-provisioner-554d86f86-jlgt6   1/1     Running             0          64m
nginx-nfs-pvc                            0/1     ContainerCreating   0          3s
[root@m-k8s vagrant]# k get pods
NAME                                     READY   STATUS    RESTARTS   AGE
gitea-66bc89d659-z577q                   1/1     Running   0          6h15m
nfs-client-provisioner-554d86f86-jlgt6   1/1     Running   0          64m
nginx-nfs-pvc                            1/1     Running   0          8s
[root@m-k8s vagrant]# k exec -it nginx-nfs-pvc -- bash
root@nginx-nfs-pvc:/# df
Filesystem                                                           1K-blocks    Used Available Use% Mounted on
overlay                                                               38770180 8663136  30107044  23% /
tmpfs                                                                    65536       0     65536   0% /dev
tmpfs                                                                  1023288       0   1023288   0% /sys/fs/cgroup
w2-k8s:/nfs/default-nfs-pvc-pvc-5ed060f5-d2a9-4966-b23b-cfcfdc06c7cf  38770432 8663296  30107136  23% /vol
/dev/mapper/centos_k8s-root                                           38770180 8663136  30107044  23% /etc/hosts
shm                                                                      65536       0     65536   0% /dev/shm
tmpfs                                                                  1944180      12   1944168   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                                                                  1023288       0   1023288   0% /proc/acpi
tmpfs                                                                  1023288       0   1023288   0% /proc/scsi
tmpfs                                                                  1023288       0   1023288   0% /sys/firmware

  • RECLAIM POLICY가 delete이므로 pvc를 삭제하여 pv와 실제 볼륨이 삭제되는지 확인 합니다.

[root@m-k8s vagrant]# k delete pvc/nfs-pvc
persistentvolumeclaim "nfs-pvc" deleted
[root@m-k8s vagrant]# k get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
git-pv-volume                              2Gi        RWO            Retain           Bound    default/git-volume                              6h18m
pvc-45758961-7542-4f32-a959-d5ee0bdc17e8   1G         RWX            Delete           Bound    default/mypvc-dynamic   nfs-client  
  • 레이블 없음