알아두면 쓸모 있는 쿠버네티스 오브젝트

노하람·2021년 9월 7일
0

컨테이너를 다루는 표준 아키텍처, 쿠버네티스

  • mac SSH Client가 무료로 1주일밖에 사용하지 못하는군요. 다른 클라이언트를 찾아서 설명드립니다.
    - mac App Store에서 제공하는 Terminus - SSH client를 설치해보았습니다.
    - 계정등록없이 basic edition만으로 ssh, telnet, port forwarding 을 제공하기 때문에 사용에 무리가 없습니다.
    - 계정 등록시 무료 Premium Trial이 14일간 주어지며, 기간 종료 후에 자동으로 결제되지 않습니다. 전 계정 등록 후 맛만 보겠습니다.
    - github에서 학생정보 연동시 Premium 구독을 무료로 제공한다는데, 얼마전에 졸업을 해버려서 확인해보지 못했습니다 :)
    • 앞으로의 실습에서 Terminus를 사용하여 진행할텐데, 각자 취향에 맞는 ssh client를 사용해주시면 될 것 같습니다. 물론 mac 자체의 터미널을 이용해서 ssh 접속을 하셔도 무방합니다. 저는 터미널 창을 n개 이상 켜고 관리하기가 불편하여 이렇게 제품을 사용하고 있습니다.

Terminus에 vagrant ssh 정보 등록하고 접속하기

  1. 그룹과 각 노드(Host)를 등록해줍니다.

    • +NEW GROUP 클릭 후 레이블(k8s), username(root), password(vagrant), SSH(ON) 설정 후 SAVE를 클릭합니다.
    • +NEW HOST 클릭 후 각각의 레이블(m-k8s, w1-k8s, w2-k8s, w3-k8s)을 입력하고(총 NEW HOST를 4번 하는 겁니다!) Address는 127.0.0.1로 통일, Group에서 앞서 만든 k8s를 등록해줍니다. Tags는 필수요소는 아니며 하단의 SSH/Mosh 설정에서 SSH를 ON으로 바꾸고 Port는 각각(60010, 60101, 60102, 60103)으로, username과 password는 group에서 받아와 자동 생성됩니다. 그 아래 SSH Agent Forwarding까지 ON으로 바꿔주신 후 SAVE하면 됩니다.
  2. 각 노드를 더블클릭해서 SSH 접속합니다.

  3. 노드들에 접속 한 후 kubectl get nodes로 정상적으로 접속되었는지 확인합니다.

    이제 SSH 연결 문제가 해결되었으니 다시 오브젝트 얘기로 돌아가봅시다!

알아두면 쓸모 있는 쿠버네티스 오브젝트

  • 쿠버네티스의 모든 포스팅은 포스팅 순서대로 차례대로 진행해주셔서 막힘없이 실습할 수 있습니다. 저번 포스팅에서 설치, 세팅해둔 설정 등이 이어서 쓰입니다.

  • 하루만에 모든 실습을 진행하기 힘들기 때문에, 중간에 실습을 중단하고 나중에 다시 쿠버네티스를 실행한다면 VirtualBox에서 각 노드들을 헤드리스로 시작해주시고, SSH 클라이언트(Terminus 등)에서 다시 터미널에 접속해주시면 됩니다 :)

지금까지 파드를 안정적으로 사용하는 방법을 배우며 파드를 관리하는 여러 기능이 포함된 '디플로이먼트 오브젝트'를 사용해 봤습니다!(앞선 포스팅을 참고하세요)
디플로이먼트 외에도 용도에 따라 사용할 수 있는 다양한 오브젝트가 이미 정의돼 있습니다.
예를 들면 데몬셋, 컨피그맵, PV/PVC, 스테이트풀셋 등이 있습니다.
쿠버네티스의 발전에 따라 사용되는 오브젝트는 계속 변하겠지만, 현존하는 다양한 오브젝트를 알아둔다면 쿠버네티스를 활용하고 추가로 개발되는 오브젝트에도 쉽게 적응할 수 있을겁니다!

데몬셋

  • 데몬셋
    - 디플로이먼트의 replicas가 노드 수만큼 정해져 있는 형태입니다.
    • 즉, 노드 하나당 파드 한 개만을 생성합니다.
    • 이미 Calico 네트워크 플러그인과 kube-proxy를 생성할 때와 MetalLB 스피커에서도 사용했었습니다. 이들의 공통점은 노드의 단일 접속 지점으로 노드 외부와 통신하는 것입니다.
    • 따라서 파드가 1개 이상 필요하지 않습니다.
    • 결국, 노드를 관리하는 파드라면 데몬셋으로 만드는 게 가장 효율적입니다.
  1. kubectl get pods -n metallb-system -o wide를 실행해 현재 MetalLB의 스피커가 각 노드에 분포돼 있는 상태를 확인합니다.

  2. 워커 노드를 1개 늘립니다. 호스트 컴퓨터의 깃 클론or압축해제한 디텍토리 경로/_Book_k8sInfra-main/ch3/3.1.3 경로로 이동해 Vagrantfile의 5번쨰 줄에 있는 N 인자의 값을 3에서 4로 수정하면 됩니다.

  3. 호스트 컴퓨터의 명령 창에서 깃 클론or압축해제한 디텍토리 경로/_Book_k8sInfra-main/ch3/3.1.3으로 이동해서 vagrant up w4-k8s를 실행합니다. 새로운 워커 노드를 추가하는 명령입니다.

    • vagrant up w4-k8s
    • 포트는 60104로 자동 할당됩니다.
    • w4-k8s의 터미널은 사용하지 않을 예정이기 때문에 굳이 ssh client(Terminus)에 Host를 추가하지 않으셔도 됩니다. 다만 VirtualBox에 노드가 새로 추가되었는지는 확인해보면 좋을 것 같습니다!

  4. w4-k8s 노드 추가가 완료되면 m-k8s(마스터 노드)에서 변화를 확인합니다.

    • kubectl get pods -n metallb-system -o wide -w
      - -w 옵션은 watch의 약자로 오브젝트 상태에 변화가 감지되면 해당 변화를 출력합니다. 리눅스에서 tail -f와 비슷한 역할입니다.
      • 변화를 모두 확인했다면 Ctrl+C로 명령을 중지하고 나옵니다. (데몬셋인 speaker-f4kfs가 2m35s 전에 새로 생성되었으며 NODE w4-k8s에 할당된 것을 볼 수 있습니다.)
  5. 자동으로 추가된 노드에 설치된 speaker가 데몬셋이 맞는지 확인합니다.

    • kubectl get pods speaker-f4kfs -o yaml -n metallb-system
    • 스피커 이름은 각자 생성된 이름으로 넣어주세요.
    • yaml 파일을 읽다보면 metadata - ownerReferences - kind에서 확인할 수 있습니다!

추가된 워커 노드에서 데몬셋이 정삭적으로 설치되고 작동하는 것을 확인해봤습니다 :)


컨피그맵(ConfigMap)

ConfigMap은 이름 그대로 설정(config)를 목적으로 사용하는 오브젝트입니다. MetalLB를 구성할 때 이미 컨피그맵을 사용해봤죠?
그런데 인그레스에서는 설정을 위해 오브젝트를 인그레스로 선언했는데, 왜 MetalLB에서는 컨피그맵을 사용했을까요?
명확하게 규정하기는 어렵지만, 인그레스는 오브젝트가 인그레스로 지정돼 있지만, MetalLB는 프로젝트 타입으로 정해진 오브젝트가 없어서 범용 설정으로 사용되는 컨피그맵을 지정했습니다.

컨피그맵으로 작성된 MetalLB의 IP 설정을 변경해봅시다!

  1. 테스트용 디플로이먼트를 cfgmap이라는 이름으로 생성합니다.

    • 슬슬 쿠버네티스 명령어에 익숙해져가시죠? 이번엔 명령어 예시를 보지 않고 직접 연습해보세요!
    • kubectl create deployment cfgmap --image=sysnet4admin/echo-hname
  2. cfgmap을 로드밸런서(MetalLB)를 통해 노출하고 이름은 cfgmap-svc로 지정합니다.

    • kubectl expose deployment cfgmap --type=LoadBalancer --name=cfgmap-svc --port=80
  3. 생성된 서비스의 EXTERNAL-IP를 확인합니다.

    • kubectl get services
    • 192.168.1.11 이군요!
  4. 사전에 구성돼 있는 컨피그맵의 기존 IP(192.168.1.11~192.168.1.13)를 sed 명령을 사용해 192.168.1.21~192.168.1.23으로 변경해봅시다.

    • cat ~/_Book_k8sInfra/ch3/3.4.2/metallb-l2config.yaml | grep 192.
    • sed -i 's/11/21/;s/13/23/' ~/_Book_k8sInfra/ch3/3.4.2/metallb-l2config.yaml
    • cat ~/_Book_k8sInfra/ch3/3.4.2/metallb-l2config.yaml | grep 192.
  5. 컨피그맵의 설정 파일(metallb-l2config.yaml)에 apply를 실행해 변경된 설정을 적용합니다!

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.2/metallb-l2config.yaml
  6. MetalLB와 관련된 모든 파드를 삭제합니다. 삭제하고 나면 kubelet에서 해당 파드를 자동으로 모두 다시 생성합니다. --all은 파드를 모두 삭제하는 옵션입니다.

    • kubectl delete pods --all -n metallb-system
  7. 새로 생성된 MetalLB의 파드들을 확인합니다.

    • kubectl get pods -n metallb-system
  8. 기존에 노출한 MetalLB 서비스(cfgmap-svc)를 삭제(delete)하고 동일한 이름으로 다시 생성해 새로운 컨피그맵을 적용한 서비스가 올라오게 합니다.

    • kubectl delete service cfgmap-svc
    • kubectl expose deployment cfgmap --type=LoadBalancer --name=cfgmap-svc --port=80
  9. 변경한 설정이 적용돼 새로운 MetalLB 서비스의 IP와 port가 바뀌었는지 확인합니다.

    • kubectl get services
    • EXTERNAL-IP는 11->21로 변경되었으며 PORT도 80:32264->80:30523으로 바뀌었습니다.
  10. 호스트 브라우저에서 192.168.1.21로 접속해 파드의 이름이 화면에 표시되는지 확인합니다.

  11. 파드 이름이 표시되므로 설정 변경이 성공한 것으로 확인됩니다. 다음 테스트를 위해 생성한 디플로이먼트와 서비스를 삭제합니다.

    • kubectl delete deployment cfgmap
    • kubectl delete service cfgmap-svc

PV와 PVC

이제 파드가 언제라도 생성되고 지워진다는 것을 충분히 경험해봤습니다.
그런데 때때로 파드에서 생성한 내용을 기록하고 보관하거나 모든 파드가 동일한 설정 값을 유지하고 관리하기 위해, 공유된 볼륨으로부터 공통된 설정을 가지고 올 수 있도록 설계해야 할 때도 있습니다.

쿠버네티스는 이런 경우를 위해 다음과 같은 목적으로 다양한 형태의 볼륨을 제공합니다!

  • 임시 : emptyDir
  • 로컬 : host Path, local
  • 원격 : persistentVolumeClaim, cephfs, cinder, csi, fc(fibre channel), flexVolume, flocker, glusterfs, iscsi, nfs, portwroxVolume, quobyte, rbd, scaleIO, storageos, vsphereVolume
  • 특수 목적 : downwardAPI, confinMap, secret, azureFile, projected
  • 클라우드 : awsElasticBlockStore, azureDisk, gcePersistentDist

다양한 쿠버네티스 볼륨 스토리지 중에서 PV와 PVC를 알아봅시다.
PV와 PVC의 관계를 이해한다면 다른 볼륨 스토리지도 쉽게 이해할 수 있습니다!

쿠버네티스는 필요할 때 PVC(PersistentVolumeClaim, 지속적으로 사용 가능한 볼륨 요청)를 요청해 사용합니다.
PVC를 사용하려면 PV(PersistentVolume, 지속적으로 사용 가능한 볼륨)로 볼륨을 선언해야 합니다.
간단하게 PV는 볼륨을 사용할 수 있게 준비하는 단계이고, PVC는 준비된 볼륨에서 일정 공간을 할당받는 것입니다.
비유하면 PV는 관리자가 피자를 굽는 것이고, PVC는 사용자가 원하는 만큼 피자를 접시에 담아 가져오는 것입니다.

  • PV로 생성 가능한 볼륨 타입
    - GCEPersistentDist, AWS EBS, Azure File, Azure Disk, Cinder(OpenStack block storage, RBD(Ceph Block Device), FlexVolume, StorageOS, Glusterfs, CSI(Container Storage Interface), Portworx Volumes, Flocker, Vsphere Volume, Quobyte Volumes, ScaleIO Volumes, CephFS, HostPath, Fibre Channel, NFS, iSCSI

그러면 가장 구현하기 쉬운 NFS 볼륨 타입으로 PV와 PVC를 생성하고 파드에 마운트해 보면서 실제로 어떻게 작동하는지 확인해봅시다!

  • 마스터 노드에 NFS 볼륨을 마운트하고(/nfs_shared), 워커 노드에서 NFS를 연결합니다(/audit).
  • 블로그에도 자세한 내용이 나와있으니 참고하시면 좋을 것 같습니다.

NFS 볼륨에 PV/PVC를 만들고 파드에 연결하기

  1. PV로 선언할 볼륨을 만들기 위해 NFS 서버를 마스터 노드에 구성합니다.

    • 공유되는 디렉토리는 /nfs_shared로 생성하고, 해당 디렉토리를 NFS로 받아들일 IP 영역은 192.168.1.0/24로 정합니다.
    • 옵션을 적용해 /etc/exports에 기록합니다. 옵션에서 rw는 읽기/쓰기, sync는 쓰기작업 동기화, no_root_squash는 root 계정 사용을 의미합니다.
    • 이때 nfs-utils.x86_64는 현재 CentOS에 설치돼 있으므로 설치하지 않아도 됩니다.
    • mkdir /nfs_shared
    • echo '/nfs_shared 192.168.1.0/24(rw,sync,no_root_squash)' >> /etc/exports
  2. 해당 내용을 시스템에 적용해 NFS 서버를 활성화하고 다음에 시작할 때도 자동으로 적용되도록 합니다.

    • systemctl enable --now nfs
  3. 다음 경로에 있는 오브젝트 스펙을 실행해 PV를 생성합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pv.yaml
    • 오브젝트 스펙은 다음과 같습니다. 이제 파일구조에 익숙할 테니 새로 나온 몇 가지만 주석을 달아놓겠습니다.
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: nfs-pv
      spec:
        # storage는 실제로 사용하는 용량을 제한하는 것이 아니라 쓸 수 있는 양을 레이블로 붙이는 것과 같습니다. 이는 현재 스토리지가 단순히 NFS로 설정돼서 그렇습니다. 용량을 제한하는 방법은 아래서 자세히 설명합니다.
        capacity:
          storage: 100Mi
        # PV를 어떤 방식으로 사용할지 정의하는 부분입니다. ReadWriteMany는 여러 개의 노드가 읽고 쓸 수 있도록 마운트하는 옵션입니다. 이외에도 ReadWriteOnce(하나의 노드에서만 볼륨을 읽고 쓸 수 있게 마운트)와 ReadOnlyMany(여러 개의 노드가 읽기만 하도록 마운트) 옵션이 있습니다.
        accessModes:
          - ReadWriteMany
        # PV가 제거됐을 때 작동하는 방법을 정의하는 것으로, 여기서는 유지는 Retain을 사용합니다. 이 외에도 Delete(삭제), Recycle(재활용, Deprecated) 옵션이 있습니다.
        persistentVolumeReclaimPolicy: Retain
        # NFS 서버의 연결 위치에 대한 설정입니다.
        nfs:
          server: 192.168.1.10
          path: /nfs_shared
  4. 실행된 PV의 상태가 Avaliable(사용 가능)임을 확인합니다.

    • kubectl get pv
  5. 다음 경로에서 오브젝트 스펙을 실행해 PVC를 생성합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc.yaml
    • 해당 오브젝트 스펙은 다음과 같습니다.
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: nfs-pvc
      spec:
        accessModes:
          - ReadWriteMany
        resources:
          requests:
            storage: 10Mi
    • PV와 PVC는 구성이 거의 동일합니다.
      - 하지만 PV는 사용자가 요청할 볼륨 공간을 관리자가 만들고, PVC는 사용자(개발자)간 볼륨을 요청하는데 사용한다는 점이 차이입니다.
      • 여기서 요청하는 storage: 10Mi는 동적 볼륨이 아닌 경우에는 레이블 정도의 의미를 가집니다.
      • 모든 설정이 완료되고 파드에 마운트된 볼륨에서 용량을 확인하면 그 의미를 확실하게 알 수 있습니다.
  6. 생성된 PVC를 kubectl get pvc로 확인합니다. 여기서 두 가지를 살펴봐야 합니다.

    • 첫 번째는 상태가 Bound(묶여짐)로 변경됐다는 것입니다. 이는 PV와 PVC가 연결됐음을 의미합니다.
    • 두 번째는 용량이 10Mi가 아닌 100Mi라는 것입니다. 사실 용량은 동적으로 PVC를 따로 요청해 생성하는 경우가 아니면 큰 의미가 없습니다. 따라서 Bound만 확인하면 됩니다.
  7. PV의 상태도 Bound로 바뀌었음을 확인합니다.

    • kubectl get pv
  8. 생성한 PVC를 볼륨으로 사용하는 디플로이먼트 오브젝트 스펙을 배포합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc-deploy.yaml
    • 배포한 오브젝트 스펙은 다음과 같습니다. 확인해야 할 부분은 주석처리 해놓겠습니다.
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nfs-pvc-deploy
      spec:
        replicas: 4
        selector:
          matchLabels:
            app: nfs-pvc-deploy
        template:
          metadata:
            labels:
              app: nfs-pvc-deploy
          spec:
            # audit-trail 이미지를 가지고 옵니다. 해당 컨테이너 이미지는 요청을 처리할 때마다 접속 정보를 로그로 기록합니다.
            containers:
            - name: audit-trail
              image: sysnet4admin/audit-trail
              # 볼륨이 마운트될 위치(/audit)을 지정합니다.
              volumeMounts:
              - name: nfs-vol
                mountPath: /audit
            # PVC로 생성된 볼륨을 마운트하기 위해서 nfs-pvc라는 이름을 사용합니다.
            volumes:
            - name: nfs-vol
              persistentVolumeClaim:
                claimName: nfs-pvc
  9. 생성된 파드를 확인합니다.

    • kubectl get pods
  10. 생성한 파드 중 하나에 exec로 접속합니다.

    • kubectl exec -it nfs-pvc-deploy-5fd9876c46-cxblc -- /bin/bash
  11. df -h를 실행해 PVC의 마운트 상태를 확인합니다.

    • 용량이 100Mi가 아닌 NFS 서버의 용량이 37G임을 확인합니다. PVC 요청에 맞게 용량을 사용하는 방법은 아래서 다시 살펴보겠습니다.
  12. m-k8s 세션을 더블클릭하여 한번 더 connect하고(m-k8s(1)이 만들어집니다) audit-trail 컨테이너의 기능을 테스트합니다. 외부에서 파드(nfs-pv-deploy)에 접속할 수 있도록 expose로 로드밸런서 서비스를 생성합니다.

    • kubectl expose deployment nfs-pvc-deploy --type=LoadBalancer --name=nfs-pvc-deploy-svc --port=80
  13. 생성한 로드밸런서 서비스의 IP를 확인합니다.

    • kubectl get services
  14. 호스트 브라우저를 엽니다. 192.168.1.21에 접속해 파드 이름과 IP가 표시되는지 확인합니다.

  15. exec를 통해 접속한 파드에서 ls /audit 명령을 실행해 접속 기록 파일이 남았는지 확인합니다. cat으로 해당 파일의 내용도 함께 확인합니다.

    • Terminus는 자동완성 기능을 제공합니다(Tap). 또한 설정에서 복사/붙여넣기가 가능하도록 설정할 수 있습니다. /audit 디렉토리 내부의 복잡한 아래의 파일명같은 명령을 작성해야할 경우 자동완성을 활용합시다!
    • ls /audit
    • cat /audit/audit_nfs-pvc-deploy-5fd9876c46-nhrnm.log
  16. 마스터 노드(m-k8s(1))에서 scale 명령으로 파드를 4개에서 8개로 증가시킵니다.

    • kubectl scale deployment nfs-pvc-deploy --replicas=8
  17. 생성된 파드를 확인합니다.

    • kubectl get pods
  18. 최근에 증가한 4개의 파드 중 1개를 선택해 exec로 접속하고 기록된 audit 로그가 동일한지 확인합니다.(동일합니다)

    • kubectl exec -it nfs-pvc-deploy-5fd9876c46-bf6pb -- /bin/bash
    • audit_nfs-pvc-deploy-5fd9876c46-nhrnm.log : 동일한 로그가 들어가 있습니다.
    • cat /audit/audit_nfs-pvc-deploy-5fd9876c46-nhrnm.log
  19. 아까와 다른 브라우저창에서 192.168.1.21로 접속해 다른 파드 이름과 IP가 표시되는지 확인합니다.

    • 구글 아이디를 달리하여 새 브라우저에 접속하든지, 다른 브라우저 프로그램을 사용하든지 해주세요! 동일한 아이디에서 새 창을 여는게 아닙니다!
  20. exec로 접속한 파드(최근 접속 파드)에서 ls /audit을 실행해 새로 추가된 audit 로그를 확인합니다. 그리고 cat으로 기록된 내용도 함께 확인합니다.

    • ls /audit
    • 로그가 새로 생겼습니다!
    • cat /audit/audit_nfs-pvc-deploy-5fd9876c46-bf6pb.log
  21. 기존에 접속한 파드에서도 동일한 로그가 audit에 기록돼 있는지 확인합니다.

    • ls /audit
    • root@nfs-pvc-deploy-5fd9876c46-bf6pb -> root@nfs-pvc-deploy-5fd9876c46-cxblc

NFS 볼륨을 파드에 직접 마운트하기

  1. 사용자가 관리자와 동일한 단일 시스템이라면 PV와 PVC를 사용할 필요가 없습니다. 따라서 단순히 볼륨을 마운트하는지 확인하고 넘어가겠습니다.

    • 이전 실습의 세션이 아닌, 마스터 노드에 새 세션으로 접속해주었습니다.
    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-ip.yaml
    • 오브젝트 스펙을 살펴봅시다. PV와 PVC를 거치지 않고 바로 NFS 서버에 접속하는 것을 확인할 수 있습니다.
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nfs-ip
      spec:
        replicas: 4
        selector:
          matchLabels:
            app: nfs-ip
        template:
          metadata:
            labels:
              app: nfs-ip
          spec:
            containers:
            - name: audit-trail
              image: sysnet4admin/audit-trail
              volumeMounts:
              - name: nfs-vol
                mountPath: /audit
            # PV/PVC를 거치지않고 바로 NFS 서버로 접속하기
            volumes:
            - name: nfs-vol
              nfs:
                server: 192.168.1.10
                path: /nfs_shared
  2. 새로 배포된 파드를 확인하고 그중 하나에 exec로 접속합니다.

    • kubectl get pods
    • kubectl exec -it nfs-ip-7789f445b7-4wzvc -- /bin/bash
  3. 접속한 파드에서 ls /audit을 실행해 동일한 NFS 볼륨을 바라보고 있음을 확인합니다.

    • ls /audit
  4. 다음 진행을 위해 설치한 PV와 PVC를 제외한 나머지인 파드와 서비스를 삭제합니다.

    • exit
    • kubectl delete deployment nfs-pvc-deploy
    • kubectl delete deployment nfs-ip
    • kubectl delete service nfs-pvc-deploy-svc

실제로 PV와 PVC를 구성해서 PV와 PVC를 구성하는 주체가 관리자와 사용자로 나뉜다는 것을 확인했습니다.
또한 관리자/사용자가 나뉘어 있지 않다면 굳이 PV와 PVC를 통하지 않고 바로 파드에 공유가 가능한 NFS 볼륨을 마운트할 수 있음을 확인했습니다!

볼륨 용량을 제한하는 방법

볼륨 용량을 제한하는 방법은 크게 두 가지가 있습니다.
첫 번째는 PVC로 PV에 요청되는 용량을 제한하는 것이고,
두 번째는 스토리지 리소스에 대한 사용량을 제한하는 것입니다.

먼저 PVC로 PV에 요청되는 용량을 제한하는 방법을 살펴봅시다!

  1. PVC로 PV를 요청할 때 용량을 제한하는 오브젝트 스펙을 가져와 적용합니다.
    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/limits-pvc.yaml
    • PVC에서 PV로 요청할 때 최소 1Mi에서 최대 5Mi로 용량을 제한합니다.
      apiVersion: v1
      kind: LimitRange
      metadata:
        name: storagelimits
      spec:
        # 용량 제한하기
        limits:
        - type: PersistentVolumeClaim
          max:
            storage: 5Mi
          min:
            storage: 1Mi
  2. 이전 실습에서 생성한 PV와 PVC를 삭제합니다.
    • 먼저 삭제를 하지 않으면 이후에 PV와 PVC를 재생성할 때 아무것도 변화하지 않았다는 unchange 알림이 나옵니다.
    • kubectl delete pv nfs-pv
      - 시간이 꽤 걸리네요.
    • kubectl delete pvc nfs-pvc
  1. PV와 PVC를 새로 생성하고 PVC가 최대 용량 제한(5Mi)에 걸려 수행되지 못하는지 확인합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pv.yaml
    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc.yaml
  2. 용량 제한 설정을 삭제합니다

    • kubectl delete limitranges storagelimits

이제 두 번째 방법인 스토리지 리소스에 대한 총 용량을 제한하는 방법도 살펴보겠습니다.

  1. 총 누적 사용량을 제한하기 위해 다음 오브젝트 스펙을 적용합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/quota-pvc.yaml
    • 오브젝트 스펙은 PVC를 5개, 용량은 25Mi가 넘지 않도록 제한합니다.
      apiVersion: v1
      kind: ResourceQuota
      metadata:
        name: storagequota
      spec:
        hard:
          persistentvolumeclaims: "5"
          requests.storage: "25Mi"
  2. 지금까지 배운 내용을 활용해 PV를 3개(100Mi)의 상태를 만들고 오프젝트 스펙을 적용합니다(과제 : 스스로 작성해보세요).

    • kubectl delete pv nfs-pv
    • 제가 한 방법이 맞는 방법이라고 할 순 없을 것 같습니다만, 스테이트풀을 배우기 전에 동적바인딩이 어떻게 진행되는지 모르겠습니다.
      - 저는 nfs-pv.yaml의 복사본 2개를 더 생성하여 각 오브젝트 파일의 metadata name을 nfs-pv1, nfs-pv2로 설정해준 다음 각각 apply 해주었습니다.
    • kubectl get pv
  3. 그런 다음 PVC 3개(10Mi)를 요청해 25Mi 제한으로 더 이상 PVC가 수행되지 못하는지 확인합니다.

    • 위와 마찬가지로 PVC 오브젝트 파일을 총 3개 만듭니다. metadata name을 변경해줍니다.
    • cp ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc.yaml ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc1.yaml
    • cp ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc.yaml ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc2.yaml
    • vim ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc1.yaml
    • vim ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc2.yaml
    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc.yaml
    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc1.yaml
    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.3/nfs-pvc2.yaml
    • 25Mi 제한이 걸려 nfs-pvc2를 생성하지 못한 것을 확인할 수 있습니다!
  1. PVC를 생성하기 위해 설정한 리소스 제한을 삭제합니다.
    kubectl delete resourcequotas storagequota

  2. 과도하게 생성한 PV(nfs-pv1, nfs-pv2)와 PVC(nfs-pvc1)을 삭제합니다. 이때 Bound(PV와 PVC가 연결된 대상)의 상대를 잘 보고 삭제해야 합니다. Bound는 기본적으로 생성된 시간에 맞춰 연결됩니다.

    • 우리는 원래의 nfs-pv와 nfs-pvc만 남겨두려고 합니다.
    • kubectl get pvc 명령을 실행하면 Bound된 볼륨이 nfs-pv1, nfs-pv2로 잡혀있는게 보이시죠? 우리가 원하는 바가 아닙니다. 그래서 두 pvc를 모두 삭제해야겠습니다.
      - kubectl delete pvc nfs-pvc
      - kubectl delete pvc nfs-pvc1
    • 이제 과도하게 생성된 PV를 확인하고 제거합니다.
      - kubectl get pv
      • kubectl delete pv nfs-pv1
      • kubectl delete pv nfs-pv2
    • 이제 다시 PVC를 생성하면 PV가 nfs-pv밖에 남지 않았으니 자연스럽게 nfs-pv에 Bound되게 됩니다!

스테이트풀셋(StatefulSet)

지금까지는 파드가 replicas에 선언된 만큼 무작위로 생성될 뿐이었습니다.
그런데 파드가 만들어지는 이름가 순서를 예측해야 할 때가 있습니다.
이전 실습에서 직접 오브젝트 스펙을 작성해보신 분이라면 PV 오브젝트 스펙 파일에 단순히 replicas를 넣는다고 작동하지 않는다는 점을 깨달으셨을 겁니다.
스테이트풀셋은 주로 레디스(Redis), 주키퍼(Zookeeper), 카산드라(Cassandra), 몽고DB(MongoDB) 등의 마스터-슬레이브 구조 시스템에서 필요합니다.
PV와 PVC도 1:1도 마스터-슬레이브 구조를 갖습니다.(아직 제 개념이 확실하지 않아 추후에 자세한 내용을 알게되거나, 해당 내용이 틀리면 수정하도록 하겠습니다.)

스테이트풀셋은 volumeClaimTemplates 기능을 사용해 PVC를 자동으로 생성할 수 있고, 각 파드가 순서대로 생성되기 때문에 고정된 이름, 볼륨, 설정 등을 가질 수 있습니다.
그래서 StatefulSet(이전 상태를 기억하는 세트)이라는 이름을 사용합니다.
다만, 효율성 면에서 좋은 구조가 아니므로 요구 사항에 맞게 적절히 사용하는 것이 좋습니다!

스테이트풀셋을 직접 만들어 보면서 생성 과정을 살펴보고 어떤 형태의 고정 값을 가지는지 알아보겠습니다.
참고로 스테이트풀셋은 디플로이먼트와 형제나 다름없는 구조라 디플로이먼트에서 오브젝트 종류를 변경하면 바로 실습할 수 있습니다.

NFS에서 스테이트풀셋 서비스 생성하기

  1. PV와 PVC는 앞에서 이미 생성했으므로 바로 스테이프풀셋의 오브젝트 스펙을 적용합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.4/nfs-pvc-sts.yaml
    • 오브젝트 스펙은 다음과 같습니다. 7번째 줄에 serviceName이 추가된 것 이외에는 앞의 nfs-pvc-deployment.yaml 코드와 동일합니다.
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: nfs-pvc-sts
      spec:
        replicas: 4
        # statefulset을 추가합니다
        serviceName: sts-svc-domain #statefulset need it
        selector:
          matchLabels:
            app: nfs-pvc-sts
        template:
          metadata:
            labels:
              app: nfs-pvc-sts
          spec:
            containers:
            - name: audit-trail
              image: sysnet4admin/audit-trail
              volumeMounts:
              - name: nfs-vol # same name of volumes's name 
                mountPath: /audit
            volumes:
            - name: nfs-vol
              persistentVolumeClaim:
                claimName: nfs-pvc
  2. 파드가 생성되는지 확인합니다. 다음과 같이 순서대로 하나씩 생성하는 것을 볼 수 있습니다.

    • kubectl get pods -w
    • 저는 이전 실습에서 문제가 있었는지 파드 생성이 Pending 상태에서 변화하지 않아 PV, PVC, pod를 모두 삭제하고 재진행하였습니다.

  3. 생성한 스테이트풀셋에 expose를 실행합니다. 그런데 에러가 발생합니다. 이는 expose 명령이 스테이트풀셋을 지원하지 않기 때문입니다. 해결하려면 파일로 로드밸런서 서비스를 작성, 실행해야 합니다.

    • expose 명령으로 서비스를 생성할 수 있는 오브젝트는 디플로이먼트, 파드, 레플리카셋, 레플리케이션 컨트롤러입니다.
    • kubectl expose statefulset nfs-pvc-sys --type=LoadBalancer --name=nfs-pvc-sts-svc --port=80
  4. 다음의 오브젝트 스펙을 적용해 스테이트풀셋을 노출하기 위한 서비스를 생성하고, 생성한 로드밸런서 서비스를 확인합니다.

    • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.4/nfs-pvc-sts-svc.yaml
    • kubectl get services
    • 스테이트풀셋을 노출하기 위한 서비스 오브젝트 스펙은 다음과 같습니다.
      apiVersion: v1
      kind: Service
      metadata:
        name: nfs-pvc-sts-svc
      spec:
        selector:
          app: nfs-pvc-sts
        ports:
          - port: 80
        type: LoadBalancer
  5. 호스트 브라우저를 열고 192.168.1.21에 접속해 파드 이름과 IP가 표시되는지 확인합니다.

  6. exec로 파드에 접속한 후에 ls -l /audit로 새로 접속한 파드의 정보가 추가됐는지 확인합니다. 정보를 확인하고 나면 exit로 빠져나옵니다.

    • kubectl exec -it nfs-pvc-sts-0 -- /bin/bash
    • ls -l /audit
  • 스테이트풀셋은 헤드리스(Headless) 서비스로 노출한다고 하던데요?
    • 일반적으로 맞습니다. 헤드리스 서비스는 IP를 가지지 않는 서비스 타입으로 중요한 자원인 IP를 절약할 수 있을 뿐만 아니라, 스테이트풀셋과 같은 상태를 가지고 있는 오브젝트를 모두 노출하지 않고 상태 값을 외부에 알리고 싶은 것만 선택적으로 노출하게 할 수 있습니다.
    • 따라서 일반적으로 스테이트풀셋은 헤드리스 서비스로 노출하나, 고정된 이름을 사용하면서 외부에 모든 스테이트풀셋을 노출하고자 하는 경우에는 노드포트나 로드밸런서 서비스로 노출할 수 있습니다.
    • 현재의 구성에서 헤드리스 서비스로 노출하고자 하는 경우에는 다음 코드를 사용해서 노출할 수 있습니다.
      apiVersion: v1
      kind: Service
      metadata:
        name: sts-svc-domain
      spec:
        selector:
          app: nfs-pvc-sts
        ports:
          - port: 80
        # 아까의 type: LoadBalancer에서 clusterIP: None으로 바뀜
        clusterIP: None
    • 이를 실행하면 IP가 할당되지 않는 형태로 노출된 것을 확인할 수 있습니다.
      • kubectl apply -f ~/_Book_k8sInfra/ch3/3.4.4/sts-svc-domain.yaml
      • kubectl get services
    • 노출된 IP는 없지만 내부적으로 각 파드의 이름과 노출된 서비스 이름 등을 조합한 도메인 이름으로 아래와 같이 쿠버네티스 클러스터 내에서 통신할 수 있는 상태가 됩니다. 이를 가능하게 해주는 CoreDNS는 나중에 포스팅하겠습니다. 할당된 도메인 이름과 IP가 확인되었다면, 다음 실습을 위해 헤드리스 서비스는 삭제합니다.
      • kubectl run net --image=sysnet4admin/net-tools --restart=Never --rm -it -- nslookup nfs-pvc-sts-0.sts-svc-domain
      • kubectl delete services sts-svc-domain
  1. 스테이트풀셋의 파드를 삭제합니다. 파드는 생성된 순서의 역순으로 삭제되는데, 삭제되는 과정도 확인해봅시다.
    • kubectl delete statefulset nfs-pvc-sts
    • kubectl get pods -w
    • 일반적으로 스테이트풀셋은 volumeClaimTemplates를 이용해 자동으로 각 파드에 독립적인 스토리지를 할당해 구성할 수 있습니다. 그러나 이 실습의 NFS 환경에서는 동적으로 할당받을 수 없기 때문에 나중에 클라우드 스토리지를 경험하게 된다면 꼭 실습을 해보시기 바랍니다.
      - 동적으로 저장 공간을 할당할 수 있는 스토리지 타입 : GCEPersistentDisk, AWS EBS, Azure File, Azure Disk, Cinder, RBD, FlexVolume, StorageOS, Glusterfs, CSI, Portworx Volumes, Flocker, Vsphere Volume, Quobyte Volumes, ScaleIO Volumes
      • Kind: StorageClass, provisioner: kubernetes.io/<스토리지_타입>

클라우드 스토리지에서 PV와 PVC 동적 할당

  • 현재 시스템에서 클라우드 스토리지를 실습하기 어려우므로 작동하는 방식만 나열합니다. 나중에 참고하여 구현해주세요!

클라우드 스토리지와 오브젝트 형태의 스토리지는 동적으로 PVC 요청을 받아서 처리할 수 있도록 구현돼 있습니다.
이때 오브젝트는 kind: StorageClass를 사용하고 PV와 PVC가 오브젝트를 호출하는 구조입니다.
메타데이터로 지정한 standard로 호출이 들어오면 동적으로 스토리지를 제공합니다.

apiVersion: storage.k8s.io/v1
# 이렇게 선언합니다.
kind: StorageClass
# standard로 호출이 들어오면 처리합니다.
metadata:
  name: standard 
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  replication-type: none
  1. dynamic-pvc.yaml을 적용해 PVC를 100GI만큼 요청합니다. 동적으로 설정된 PV는 PVC의 요청에 따라 생성됩니다.(동적으로!!)

    • kubectl apply -f https://raw.githubusercontent.com/sysnet4admin/_Book_k8sInfra/main/ch3/3.4.4/dynamic-pvc.yaml

    • 코드는 다음과 같이 거의 비슷하지만, 제공자에 따라 storageClassName을 작성해야 합니다. 여기서는 번거로움을 피하기 위해 공란으로 둡니다. 공란으로 두면 기본값(standard)를 사용합니다.

    • 오브젝트 스펙은 다음과 같습니다.

        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: dynamic-pvc
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 100Gi
        # storageClassName: <default 사용>
    • 클라우드 업체별로 storageClassName 기본값은 다음과 같습니다.

      제공 업체기본 StorageClass 이름기본 프로비저너
      Amazon Web Servicesgp2aws-ebs
      Microsoft Azurestandardazure-disk
      Google Cloud Platformstandardgce-pd
      OpenStackstandardcinder
      VMware vSpherethinvsphere-volume
  2. 생성된 PVC와 PVC 요청에 따라 생성된 PV를 함께 확인합니다.

    • kubectl get pvc
    • kubectl get pv
  3. 해당 PVC를 사용할 파드들을 생성합니다.

    • kubectl apply -f https://raw.githubusercontent.com/sysnet4admin/_Book_k8sInfra/main/ch3/3.4.4/dynamic-pvc-deploy.yaml
    • dynamic-pvc를 사용하도록 이름을 변경한 것 외에는 dynamic-pvc.yaml과 모두 동일합니다.
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: dynamic-pvc-deploy
      spec:
        replicas: 3
        selector:
          matchLabels:
            app: dynamic-pvc-deploy
        template:
          metadata:
            labels:
              app: dynamic-pvc-deploy
          spec:
            containers:
            - name: audit-trail
              image: sysnet4admin/audit-trail
              volumeMounts:
              - name: dynamic-vol # same name of volumes's name 
                mountPath: /audit
            volumes:
            - name: dynamic-vol  
              persistentVolumeClaim:
                claimName: dynamic-pvc
  4. 배포된 파드를 확인합니다.

    • kubectl get pods
  5. 배포된 파드 중 1개에 exec로 접속해 df -h로 마운트된 볼륨을 확인합니다. 그런 다음 /audit에 설정한 것과 같이 100Gi(98G) 용량이 마운트 된 것을 확인할 수 있습니다.


지금까지 쿠버네티스의 전반적인 개념을 배우고 직접 실습하며 어떻게 사용되는지 확인해봤습니다.
한번에 알아야 할 것들이 매우 많은 기술이기 때문에 전체를 여러 번 반복해서 실습하길 권장합니다.
조금씩 응용을 곁들이면 더 오랫동안 기억하기 수월합니다.

다음 포스팅부터는 쿠버네티스에서 내려받아 이용만 했단 컨테이너 도커를 알아보고, 직접 애플리케이션을 빌드하여 이를 도커 이미지로 만들어 보겠습니다.
그리고 만들어진 도커 이미지를 사설 컨테이너 저장소인 레지스트리에 넣고 쿠버네티스에서 저장된 이미지를 직접 불러오는 방법을 실습하며 쿠버네티스와의 관계를 확실히 정립해보겠습니다!


본 게시물은 "컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커 - 조훈,심근우,문성주 지음(2021)" 기반으로 작성되었습니다.

profile
MLOps, MLE 직무로 일하고 있습니다😍

0개의 댓글