technology trend

안녕하세요. 저는 클라우드 및 애자일·협업 전문 기업 오픈소스컨설팅의 이건웅이라고 합니다. 우리 회사는 클라우드, 데브옵스(DevOps), 컨테이너 아키텍처 등 인프라 관련 최신 기술 전문성을 보유, 이를 토대로 오픈스택 및 쿠버네티스 커뮤니티 오픈소스 패키지인 ‘플레이스 클라우드(Playce Cloud)’를 개발 및 공급하고 있습니다.

우리 팀은 지속적으로 CNCF의 공개 버전을 이용하여 크고 작은 사이트들에 설계, 구축 및 유지보수 업무를 진행 하고 있는데요. 이때 생기는 알찬 노하우를 공유하고자 합니다. 많은 분들이 오픈소스를 쉽게 접할 수는 있지만 실제 환경에 적용해서 사용하기에는 많은 시행 착오와 경험이 필요한데요. 누군가가 구현 가능하고 바로 사용 가능한 아키텍처를 보여준다면 너무 좋겠죠. 그래서 준비 했습니다.

구현 가능하고 적절한 사용이 가능한 Kubernetes 구축과 운영 포인트를 저와 같이 확인해 주시면 감사하겠습니다.


목표

Kubernetes를 사용 함에 있어, 우리는 사용하게 되는 Kube-APIServer, Kube-Controller, Kube-Scheduler, etcd, kubelet, kube-proxy, CNI, CSI, nginx-ingress, service, Pod, Object 등의 상당히 많은 관리 요소들을 알고, 학습해야 할 필요가 있습니다.

Kubernetes를 일반 개발자, 혹은 사업관리자들은 Container Orchstration으로 자동 배포, 자동 수평확장, 자동 복구가 되는 시스템으로만 알고 있지만 이러한 것들은 아무런 댓가 없이 이루어 지지 않습니다.
Kubernetes를 잘 이해하는 엔지니어가 사용방법과 운영방법을 어드바이스하고 지속적인 확인과 점검을 할 필요가 있겠습니다.

그러기 위해서 엔지니어들은 Kubernetes의 요소들을 잘 숙지하고, 365일 24시간 주 7일의 안정적인 운영에 최선의 노력을 경주해야 겠지요. 그렇다면, 문제없는 관리를 위해 Kubernetes의 구성요소들을 살펴보겠습니다.

오늘은 CSI를 살펴 보겠습니다.


CSI 구조

CSI(Container Storage Interface)를 알기 위해서는 kubernetes에서 Volume을 어떻게 사용하는지 이해를 해야 합니다. Kubernetes는 수평확장과 자동복구를 위해 컨테이너화 되어 운영이 됩니다.

하지만 이런 방식에는 약간의 단점이 발생하게 되는데요.

Pod의 비정상 상황에서 Pod가 재시작 되었을때 Pod는 초기 형태로 돌아오게 됩니다. 이때 최초 생성되어 작성된 축적 데이터 혹은 수정된 Pod설정들은 사라지게 됩니다. 아래의 그림을 보겠습니다.

운영되고 있던 Pod의 /data/ 영역에는 “a”라는 파일이 있습니다. 이 파일은 기본 컨테이너 이미지에서 처음 배포 된 상태이고, Pod가 운영되면서 “b”라는 파일이 생성 되었습니다.
하지만 이 Pod는 이슈가 생겨 Kubernetes가 Pod를 재 생성하게 됩니다. 기존의 Pod는 삭제가 되기때문에 “b”라는 파일도 사라지게 되고, 기본 컨테이너 이미지가 제공하는 /data/a 만을 보유 한 상태로 Pod가 재 생성 됩니다.

이러한 문제를 해결하기 위해, Worker Node의 로컬디스크를 사용하면 되지 않을까요? 아래의 그림을 보겠습니다.

1기의 Worker Node를 사용한다면, 로컬디스크를 Pod에 마운트해서 사용 할 수 있고 Pod가 재 생성되어도 지정된 경로에 추가된 파일을 다시 이용 할 수 있습니다.
하지만, 2기 이상의 Worker Node를 이용하게 될 경우에는 또 다른 문제가 생기게 됩니다. 로컬디스크는 각 Node에 공유가 되지 않기 때문에, Pod가 Node를 이동 하게 되어 문제가 발생합니다.

이런 상황이라면, Pod를 운영하는데 심각한 문제가 있다고 볼 수 있겠죠.

Pod를 생성할때 Data부분에 관해 고려해야 할 부분이 아주 중요해 질 수 밖에 없는데요. 하지만, 물론 물리 장비를 이용할때 이러한 부분을 해결하기 위해 여러가지 방안을 이미 이용하고 있었습니다.

Kubernetes또한 이러한 부분을 중요하게 생각하고 Container간의 Storage를 이용 할 수 있는 Interface를 준비해뒀습니다.

공유된 스토리지들을 Kubernetes에서 이용 할 수 있게 해주는 CSI는 Kubernetes의 버전에 따라 지원이 되지 않는 경우는 있지만, 크게 hostpath(로컬디스크), cephfs, FC, iSCSI, NFS 가 있습니다. ( 최신 버전(현재 v1.21 – v1.28 이후에는 Public Cloud의 볼륨과 Cinder, Glusterfs 등은 지원되지 않습니다. 참고: kubernetes Volume)

NFS를 기준으로 각 노드에 사용되는 모습을 이미지로 확인해 보겠습니다.

반드시 NFS는 아니지만, 공유스토리지를 이용한다면, Node가 변경되어도 Pod가 생성한 파일을 신규 Pod가 이용 할 수 있게 되었습니다.


CSI 사용

CSI는 효율적인 볼륨사용을 위해 다양한 방법을 제공합니다. PV(Persistent Volume와 SC(Storage Classes)인데요. 사용방법은 다양하지만 쉽게 이해하기 위해 설명하자면, Pod에서 PVC(PersistentVolumeClaim)로 Node의 Volume(hostPath, local)을 바로 이용한다면, PV를 생성해 사용합니다.
Pod에서 NAS등을 이용한다면, SC를 생성한 후, PVC를 이용해 PV를 자동으로 프로비저닝 해서 사용합니다.

위의 그림을 보게 된다면, NFS를 사용하는 CSI는 컨트롤러가 각 노드에 nfs 컨테이너를 생성하고, NAS의 지정된 영역을 이용하여 SC가 구성이 됩니다. Pod는 이러한 SC를 PVC를 이요해서 Pod의 특정 위치에 mount 하게 되는 구조입니다.
이때 SC는 PVC의 요청에 따라 PV를 자동으로 프로비저닝 합니다.

PV(Host Path) 사용

Playce Kube에서 제공하는 SCI를 사용해보기 전에, Kubernetes에서 자체적으로 연결 가능한 PV를 사용해 Pod를 생성하면서 간단하게 사용방법을 알아 보겠습니다.

PV를 사용해 NFS구성도 가능하지만, 정적 프로비저닝으로 NFS를 사용 할 경우에는 SC로 구성을 하고, 특정 볼륨을 사용하거나 테스트 용으로 Hostpath를 사용한 볼륨을 이용할때 사용하시면 되겠습니다. ( 물론 환경과 운영방식에따라 다양하게 사용하시면 됩니다. )

PV를 사용하여 Pod의 구성은 Node의 디스크 경로를 이용한 PV생성, 그리고 생성된 PV를 PVC를 생성해 Pod의 특정 경로로 Mount되는 방식입니다.

사용하게 된 경로는 “/test/pv”를 Node에서 지정하여 사용하였는데요. 이때 Pod가 생성될 Worker Node에 해당 경로가 있어야 겠습니다. 스토리지 볼륨이다 보니 용량에서도 자유로울수 없는데요.

Pod별 예상되는 사용량보다 많은 용량을 할당해야 하겠습니다.

Volume을 사용한다면 우리가 제일 고려해야 할 부분은 무엇이 있을까요? 저는 용량과 속도, 그리고 권한이라고 생각합니다.

성능은 내부에서 운영되고 있는 디스크, 그리고 주로 사용하게 될 NAS의 IO는 단연 높을 것으로 예상하고, 용량 또한 사용치 X 증가치 를 계산하여 구성될 것이기 때문에 운영될 Pod의 성향별 권한들이 필요하게 되는데요.

읽기와 쓰기, 그리고 Pod의 생명주기에 따른 삭제와 보관, 그리고 재사용이 명시가 되어야 합니다.

spec.persistentVolumeReclaimPolicy 에 잘 보여주고 있는데요. 우리가 기억해야 할 부분은 데이터의 유지인가 아니면 사용후, 삭제인가 입니다.

Reclaim Policy상세비고
 RetainPod가 종료되어도 삭제 되지 않습니다. 사용 후, 직접적인 삭제가 필요합니다.
Delete사용이 종료 되면 바로 삭제 됩니다.
Reclaim Policy

사용된 볼륨은 Pod가 운영되는 중에는 재 사용되야 여러모로 편할테지만 사용이 완료된 Pod를 확인해서, 삭제를 해야 하는 부분은 불편하게 보일 수 있습니다. 사용 후, 데이터가 바로 삭제된다면 유실되는 데이터도 있겠죠. 구분을 잘 해야 겠습니다.

자 그렇다면, 이 볼륨들은 읽고 쓰기가 누구에게나 자유로워야 할까요? 한번 생성된 데이터를 변경없이 사용해야 할 경우도 있을 것입니다. 혹은 한번 생성된 데이터를 지속적으로 값을 변경해서 업데이트 해야 할 수도 있을 것입니다. 여기에 다중 접근도 포함을 해야겠죠.

이러한 요구를 해결 하기 위해 Kubernetes에서는 다양한 Acceess Mode를 제공해주고 있습니다.

accessModes상세
ReadWriteOnce1개의 Node에서 Volume으로 읽기-쓰기합니다.






복수의 Node가 아닌 동일 Node의 복수의 Pod 접근입니다.
ReadOnlyMany복수의 Node에서 읽기 전용으로 사용됩니다.
ReadWriteMany복수의 Node 복수의 Pod에서 일기-쓰기 사용 합니다.
ReadWriteOncePod단일 Pod에서 읽기-쓰기만 가능합니다. 타 Pod에서 사용 불가능 합니다. ( v1.22이상 )
Access Mode

이중 특이한 Mode가 보이는데요. 특정 파드만 사용 할 수 있는 볼륨제어가 있습니다. NFS등을 사용할때 보안에 취약한 부분을 보안하기 위해서로 보입니다.

SC(NFS) 사용

Playce Kube에서는 NFS를 이용한 SC 샘플을 제공합니다. NFS는 deploy Node에 구축 되어 있고, 이를 이용해 SC를 생성합니다.

## SC 확인 

kubectl get sc
# NAME                PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE

# nfs-csi (default)   nfs.csi.k8s.io   Delete          Immediate           false                  60d

Playce Kube가 설치된 Cluster에서 확인해 보면 SC는 nfs.csi.k8s.io 프로비저너를 사용해 구축이 되어 있습니다.
SC를 상세하게 보겠습니다.

## SC 상세 정보 

kubectl describe sc nfs-csi
# Name:                  nfs-csi
# IsDefaultClass:        Yes
# Annotations:           storageclass.kubernetes.io/is-default-class=true
# Provisioner:           nfs.csi.k8s.io
# Parameters:            server=10.1.0.11,share=/k8s
# AllowVolumeExpansion:  <unset>
# MountOptions:
#   hard
# ReclaimPolicy:      Delete
# VolumeBindingMode:  Immediate
Events:             <none>

PVC에서 사용하기 위한 정보를 확인 할 수 있네요.
이름과 NAS의 주소, 그리고 경로를 확인 할 수 있습니다.
이 정보를 이용해 PVC, PV를 생성해 보겠습니다.

## PVC 생성

echo > pvc.yaml << EOF
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-csi-pvc
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 8Gi
  storageClassName: nfs-csi
EOF

kubectl apply -f pvc.yaml 

## PVC 생성 확인

kubectl get pvc

# NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE

# nfs-csi-pvc   Bound    pvc-e470f18b-ce5c-497b-91e3-8dd4007a8b0a   8Gi        RWX            nfs-csi        6m44s

PVC가 정상적으로 생성이 되었다면, 생성된 PVC를 이용해 deployment를 생성해 보겠습니다.

## Deployment 생성

echo > deployment.yaml << EOF 
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-volume-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-container
  template:
    metadata:
      labels:
        app: nginx-container
    spec:
      containers:
      - name: nginx-container
        image: registry.local.cloud:5000/library/nginx:1.21.4
        volumeMounts:
        - mountPath: /vol
          name: nfs-volume
      volumes:
      - name:  nfs-volume
        persistentVolumeClaim:
          claimName: nfs-csi-pvc
EOF

kubectl apply -f pvc.yaml 

## Deployment 생성 확인

kubectl get deployment

# NAME                      READY   UP-TO-DATE   AVAILABLE   AGE

# nginx-volume-deployment   1/1     1            1           8m14s

Deployment와 PVC를 생성했는데요. PV가 자동으로 프로비저닝 되었는지 확인해 보겠습니다.

## PV 확인 

kubectl get pv 

# NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                   STORAGECLASS   REASON   AGE

# pvc-e470f18b-ce5c-497b-91e3-8dd4007a8b0a   8Gi        RWX            Delete           Bound    default/nfs-csi-pvc                     nfs-csi                 12m

PV도 자동으로 프로비저닝 된 것을 확인 할 수 있습니다.

그럼 이어서 Pod의 숫자를 증가 시킨 후, 정상적으로 스토리지 공유가 되는지 확인해 보겠습니다.

## Pod Scale

kubectl scale deployment nginx-volume-deployment --replicas=5

# deployment.apps/nginx-volume-deployment scaled


## Pod Scaled check 

kubectl get pod

# NAME                                       READY   STATUS    RESTARTS   AGE
# nginx-volume-deployment-69c9474d89-dbgm4   1/1     Running   0          47s
# nginx-volume-deployment-69c9474d89-hh48q   1/1     Running   0          47s
# nginx-volume-deployment-69c9474d89-jjxfr   1/1     Running   0          15m
# nginx-volume-deployment-69c9474d89-mlwgc   1/1     Running   0          47s

# nginx-volume-deployment-69c9474d89-v6zdm   1/1     Running   0          47s

Pod 가 정상적으로 확장 되었습니다.
Pod 안에 mount 되어 있는 경로를 확인해 보겠습니다.
확인하게 될 Pod 는 “nginx-volume-deployment-69c9474d89-dbgm4” 입니다.

## Pod 접속

kubectl exec -it  nginx-volume-deployment-69c9474d89-dbgm4 /bin/bash

## Mount 확인

root@nginx-volume-deployment-69c9474d89-dbgm4:/# df -h

# Filesystem                                               Size  Used Avail Use% Mounted on
# overlay                                                   97G  7.1G   90G   8% /
tmpfs                                                     64M     0   64M   0% /dev
# 10.1.0.11:/k8s/pvc-e470f18b-ce5c-497b-91e3-8dd4007a8b0a  466G  231G  236G  50% /vol

# /dev/vda1                                                 97G  7.1G   90G   8% /etc/hosts
# shm                                                       64M     0   64M   0% /dev/shm
# tmpfs                                                     16G   12K   16G   1% /run/secrets/kubernetes.io/serviceaccount
# tmpfs                                                    7.9G     0  7.9G   0% /proc/acpi
# tmpfs                                                    7.9G     0  7.9G   0% /proc/scsi
# tmpfs                                                    7.9G     0  7.9G   0% /sys/firmware

Pod 안에서 볼륨 정보를 ‘df’ 명령어로 살펴 보게 되면, SC 에서 지정된 NAS가 특정 PV 이름으로 마운트 되어 있는 상태를 확인 할 수 있습니다.
확장한 다른 Pod에서도 동일하게 PV가 확인되는 것으로 보아 CSI는 정상적으로 구성된 것 같습니다.

## 다른 Pod 접속

kubectl exec -it  nginx-volume-deployment-69c9474d89-hh48q /bin/bash

## Mount 확인 

root@nginx-volume-deployment-69c9474d89-hh48q:/# df -h

# Filesystem                                               Size  Used Avail Use% Mounted on
# overlay                                                   97G  7.2G   90G   8% /
tmpfs                                                     64M     0   64M   0% /dev
# 10.1.0.11:/k8s/pvc-e470f18b-ce5c-497b-91e3-8dd4007a8b0a  466G  231G  236G  50% /vol

# /dev/vda1                                                 97G  7.2G   90G   8% /etc/hosts
# shm                                                       64M     0   64M   0% /dev/shm
# tmpfs                                                     16G   12K   16G   1% /run/secrets/kubernetes.io/serviceaccount
# tmpfs                                                    7.9G     0  7.9G   0% /proc/acpi
# tmpfs                                                    7.9G     0  7.9G   0% /proc/scsi
# tmpfs                                                    7.9G     0  7.9G   0% /sys/firmware

동일한 경로가 마운트 되어 있는 것을 확인 할 수 있습니다.
CSI를 이용한다면, 상황별 설정이나


CSI 주의 사항

SC, PVC, PV를 이용해서 CSI를 구성할때 주의 할 점은 무엇이 있을까요?
공유 스토리지를 사용할때는 고려해야 할 점이 상당히 많습니다. 우선 1:1 접근이 아닌 N:N의 접속인 만큼 높은 IO처리량이 보장 되어야 하고, 각 Node에서 접근이 가능한 만큼 보안정책도 중요합니다.
물론 NFS를 사용하는 만큼 Node에서도 직접적으로 접근이 가능하므로 Node에서 NFS 마운트는 지양하고 Pod에서만 SC를 이용해 사용해야 겠습니다.

특히 Pod는 사용량에 따라 수평확장이 되므로 지정된 SC안에서 다량으로 늘어나게 되면서 많은 양의 Volume을 생성한 후, 사용 종료에 따라 Volume이 재사용 되지 않고, 스토리지에 유지 된다면, 스토리지 사용에 많은 부담이 될 것입니다.
혹은 사용량 계산의 실수로 지속 생성되는 Volume으로 인해 관리와 인덱싱 이슈가 생길 수 있습니다.

주의 할점을 정리한다면 다음과 같습니다.

  1. 사용 용량과 지속성 고려
  2. Pod의 사용 권한
  3. 사용된 Volume 데이터의 관리
  4. 서비스별 Volume구별
  5. Node에서 공용 스토리지 직접 접근 제한

결론

Kubernetes에서 사용하는 CSI 의 Storage Class를 사용한 Volume을 살펴봤습니다.

다중 접속과 다중 사용이 가능한 구성이다 보니, 처음 구성 후에 쉽게 넘어 갈 수 있는 부분입니다만, 사용 도중 변동이나 관찰이 어려울 수 있습니다.

Kubernetes는 동일한 이미지를 사용해 사용량에 대비해 수평확장이 가능하고, 장애 혹은 사용의 이슈에 따라 자동복구를 하는 시스템입니다. 하지만 수평확장과 자동 복구시에는 사용되고있는 데이터의 처리가 반드시 필요 합니다.

많은 시스템들이 Volume의 크기를 당장 사용분을 보고 계산하지만, 요즘 구축 되는 서비스들은 증가량은 상상이상으로 증가하고 있고, 이를 가공하거나, 재 사용 하는 경우가 상당히 많습니다.

그렇다면 이 데이터의 용량 구분과 서비스별 사용 구분이 필수 일텐데요. 이 사용량은 네트워크 사용량과도 연관이 있으므로 데이터의 크기와 사용도를 고려한다면, NFS의 네트워크를 별도로 구성하는 것도 하나의 방안이 되겠습니다.
Kubernetes이다 보니 사용의 간편함과 운영의 용이로 인해 모든 볼륨을 1개로 사용하실 수 있습니다만, 가능하면 미래 사용량을 가늠하여, SC를 서비스 별, Namespace별 구분하여 구성하기를 추천 드립니다.

Kubernetes란 무엇인가를 시작으로, 고객의 니즈와 서비스 형태를 바탕으로 완성한 쿠버네티스 시스템을 분석하여 블로깅 하고 있습니다.
우리 모두 오픈소스컨설팅의 미션 처럼 기술을 나누고, 모두 함께 성장했으면 좋겠습니다.


참고사이트

kubectl 명령어

Kubernetes Components

Kubernetes Volume

Kubernetes PV

Kubernetes SC

쿠버네티스-위키백과

CNCF 홈페이지

Playce Kube github

Hey guys Hi guys I'm Gunwoong, a technical engineer. I'm dreaming of technology development that everyone can use.

Leave a Reply

Your email address will not be published. Required fields are marked *