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 |
---|---|
| Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value. |
| Across all pods in a non-terminal state, the sum of memory limits cannot exceed this value. |
| Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value. |
| Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value. |
| Across all pods in a non-terminal state, the number of huge page requests of the specified size cannot exceed this value. |
| Same as |
| Same as |
쿼터 오브젝트를 생성하여 특정 우선 순위의 파드와 일치 시키며, "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 06-volume-emptydir.yaml 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 06-volume-emptydir.yaml 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 07-volume-emptydir-multicon.yaml 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 07-volume-emptydir-multicon.yaml 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 08-volume-hostpath.yaml pod "nginx-vol-3" deleted
3.3 NFS
NFS를 volume으로 사용할 수 있습니다.
NFS를 이용하면 여러 pod에 동일한 volume을 마운트 할 수 있고, 여러 노드가 읽고 쓸 수 있으므로 파일 공유에 많은 이점이 있습니다.
(1) 실습을 위하여 w2-k8s번 노드에 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] 실행합니다. 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 09-volume-nfs.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 09-volume-nfs.yaml 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를 생성하는 단계로 프로비저닝 방법에는 두 가지가 있습니다.
| |
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
반환 정책은 리소스를 수동으로 반환할 수 있게 한다. 퍼시스턴트 볼륨 클레임이 삭제되면 퍼시스턴트볼륨은 여전히 존재하며 볼륨은 "릴리스 된" 것으로 간주된다. 그러나 이전 요청자의 데이터가 여전히 볼륨에 남아 있기 때문에 다른 요청에 대해서는 아직 사용할 수 없다. 관리자는 다음 단계에 따라 볼륨을 수동으로 반환할 수 있다.
퍼시스턴트볼륨을 삭제한다. PV가 삭제된 후에도 외부 인프라(예: AWS EBS, GCE PD, Azure Disk 또는 Cinder 볼륨)의 관련 스토리지 자산이 존재한다.
관련 스토리지 자산의 데이터를 수동으로 삭제한다.
연결된 스토리지 자산을 수동으로 삭제하거나 동일한 스토리지 자산을 재사용하려는 경우 스토리지 자산 정의로 새 퍼시스턴트볼륨을 생성한다
실습은 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