공부하고 기록하는, 경제학과 출신 개발자의 노트

학습일지/kubernetes

Kubernetes Deep Dive - (8). Storage

inspirit941 2021. 10. 9. 08:21
반응형

Storage

스크린샷 2021-09-17 오후 3 31 32
  1. Container File System
    • ephemeral. 컨테이너를 삭제하거나 재실행하면 컨테이너 file system의 데이터도 같이 삭제됨.

컨테이너가 계속 사용해야 하는 데이터라면 Volumes를 사용하는 편이 좋다.

스크린샷 2021-09-17 오후 3 33 54
  1. Volumes
    • Runtime에는 컨테이너가 접근 가능한 outside-container storage.
    • real time change - 컨테이너가 참조하고 있는 파일 값이 변경될 경우 external storage의 값도 같이 바뀐다.
스크린샷 2021-09-17 오후 3 36 24
  1. Persistent Volumes
    • 보다 추상화된 형태. k8s object처럼 Persistent Volumes를 생성해서 pod이 사용할 수 있게 한다.
스크린샷 2021-09-17 오후 3 38 27

runtime 때, 컨테이너는 storage에 직접 접근하는 대신 Persistent Volume에 요청 후 접근한다. 컨테이너가 재실행되더라도 Persistent Volume 객체는 그대로이므로, container의 data consistency는 유지할 수 있다.

스크린샷 2021-09-17 오후 3 42 10

Volume에는 여러 타입이 있고, 각 타입별로 '어디에 어떻게' volume이 생성되는지 결정되기 때문.


K8s Volumes

스크린샷 2021-09-18 오후 12 26 18
  • volume: pod의 specification. 구체적인 volumeType을 지정하고, 데이터를 어디에 저장할 것인지 명시한다.
    • 예시의 경우 hostPath가 volumeType이고, 어디에 저장할 것인지는 path에 지정했다.
  • volumeMount: container 단위의 specification. pod의 spec을 참조하고 실제 mountPath를 지정한다.
    • 예시의 경우 pod에서 정의한 volume과 동일한 name으로 지정했고, mountPath를 지정한다.

예시의 yaml을 실행하게 되면

  1. sample-vol 이라는 이름의 volume을 생성하고, node의 /data 경로를 hostPath로 지정한다.
  2. 컨테이너는 host machine에 /data/output 경로를 생성하고, 해당 경로의 file system을 사용한다.
    • 컨테이너를 재시작해도 /data/output 경로의 데이터는 소멸하지 않는다.
스크린샷 2021-09-18 오후 12 34 28
  • EmptyDir volume: pod가 생성될 때 만들어지고, Pod가 사라지면 같이 삭제된다.
    • 해당 Pod에 있는 Multiple Container가 동일한 volume을 참조할 수 있게 됨. 동일한 데이터를 여러 container에서 read / write할 수 있게 된다.
    • container가 정의한 mountPath 경로와 관계없이, 같은 파일을 읽고 쓸 수 있게 됨
스크린샷 2021-09-18 오후 12 39 13

물론 emptyDir만 shared Volume 구조를 지원하는 건 아니다. hostPath도 shared Volume을 지원함.

컨테이너 간 데이터 공유가 가능하기 때문에, data processing in multiple format일 때 특히 유용하다.


스크린샷 2021-09-18 오후 8 00 06

아래의 yaml 파일로 pod를 생성한 결과.

  1. 노드의 /var/tmp 디렉토리가 존재하는지 확인
  2. yaml파일로 pod 생성
  3. /var/tmp 디렉토리에 새 파일 output.txt 파일이 생성됨
  4. 파일을 확인해보면 로그가 계속 기록되는 것을 볼 수 있음.
    • restart policy를 따로 설정하지 않으면 기본적으로 restart가 적용되기 때문. pod 명령이 끝나고 자동으로 컨테이너가 삭제되고, policy 때문에 다시 생성되어 명령어를 실행하기 때문에 로그가 계속 기록된다.
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  volumes:
  - name: hostpath-vol
    hostPath:
      path: /var/tmp # k8s node의 경로. k8s에 이 경로가 반드시 존재해야만 volumes가 제대로 작동함.
  containers:
  - name: hostpath-pod
    image: 'k8s.gcr.io/busybox'
    command: ["/bin/sh", "-c", "echo Hello Team, This is Sample File for HostVolume - $(date) >> /output/output.txt"]
    volumeMounts:
    - name: hostpath-vol
      mountPath: /output

스크린샷 2021-09-18 오후 8 04 46

  • 또한 kubectl delete로 pod 자체를 삭제한 뒤 다시 apply로 생성했을 때, output.txt 파일의 마지막 줄에서부터 새 로그가 쌓이는 걸 볼 수 있다.
  • pod 자체를 삭제하고 재시작한다 해도, mount된 파일의 consistency는 유지하는 것을 확인할 수 있다.

emptyDir 예시

스크린샷 2021-09-18 오후 10 21 11

  • 아래의 yaml 파일로 pod를 생성하고, kubectl exec로 pod에 직접 들어가서 /data/redis 디렉토리에 파일을 하나 생성한다.
apiVersion: v1
kind: Pod
metadata:
  name: redis-emptydir
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {} # dynamically created dir. host machine에서 알아서 경로를 지정한다.

스크린샷 2021-09-18 오후 10 21 55

  • 만약 Pod를 삭제한 뒤 다시 생성하면, emptyDir에 생성해뒀던 output.txt 파일이 사라진 걸 확인할 수 있다.
  • emptyDir은 pod가 persistent할 때에만 접근 가능한 volume이며, pod가 삭제될 경우 데이터가 같이 삭제됨.
    • container의 재실행 여부와는 관계없이 volume의 데이터가 유지된다.

apiVersion: v1
kind: Pod
metadata:
  name: shared-multi-container
spec:
  volumes:
    - name: html
      emptyDir: {}
  containers:
    - name: nginx-container
      image: nginx
      volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
    - name: debian-container
      image: debian
      volumeMounts:
        - name: html
          mountPath: /html
      command:
        - /bin/sh
        - '-c'
      args:
        - while true; do date >> /html/index.html; sleep 5; done
        # 5초마다 date를 index.html에 저장.
  • nginx의 /usr/share/nginx/html을 emptyDir Volume에 마운트.
  • debian의 /html 경로를 mount.
  • 결과적으로, debian에서 업데이트한 index.html 파일이 nginx에서도 그대로 적용됨.
    • emptyDir 특성상 같은 volume을 공유하고 있다면 common Directory.
    • nginx가 mount한 경로에 index.html 파일이 있는데, debian에서 해당 경로의 index.html 파일을 5초마다 업데이트하기 때문.

스크린샷 2021-09-19 오후 12 33 19

curl로 접근하면, Port 80인 Nginx 컨테이너에 요청을 보냈을 때 리턴값으로 default html 파일이 아니라 date 로그가 쌓인 걸 확인할 수 있다.

Persistent Volumes

스크린샷 2021-09-19 오후 12 38 12

  • k8s에서 제공하는 Object로, storage를 일종의 리소스처럼 사용할 수 있게 해 주는 기능.
  • 예시처럼 kind에 PersistentVolume을 명시해주며, spec에 capacity 같은 설정을 추가할 수 있다.
  • hostPath를 지정하면, 해당 경로에 Persistent Volume이 생성된다.

스크린샷 2021-09-19 오후 12 44 24

Storage Class

  • k8s에서 제공하는 Storage Service의 종류를 규정하는 Object.
  • provisioner에 no-provisioner를 지정하면, 해당 object를 생성한 k8s node가 provisioning 권한을 갖는다. (local machine이라고 생각하면 쉬움)
  • volumeBindingMode: 해당 storage가 생성되면 어디에 binding할 것인지 지정. 어느 pod에 지정될 것인지 지정하는 거라고 생각하면 됨.

스크린샷 2021-09-19 오후 12 45 46

  • slow / fast : 적용할 수 있는 storageClass 종류들. cloud storage를 provisioner에 지정할 수 있으며 (각각 aws / gcp), 목적에 맞게 설정하면 된다.

스크린샷 2021-09-19 오후 12 49 00

  • 생성한 뒤 resize가 가능한지 여부를 설정하는 옵션 값.

스크린샷 2021-09-19 오후 12 52 49

  • overview에서 언급한 것처럼, pod이 Persistent Volume에 접근하기 위해서는 persistent Volume Claim을 통해야 한다.
  • 만약 Persistent Volume Claim 객체가 사라질 경우의 대응방식을 설정할 수 있음.

스크린샷 2021-09-19 오후 9 07 15

PersistentVolumeClaim

  • user가 Persistent Volume을 사용하기 위해 요청을 날리는 대상 object
  • PVC는 정의한 조건에 맞는 PV가 있는지 찾아보고, 없으면 직접 조건에 맞는 PV를 생성한 뒤 연결한다.

StorageClass 생성.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner # created on local machine running on k8s
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

스크린샷 2021-09-19 오후 9 33 08

PersistentVolume 생성.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-persistnt-vol
spec:
  storageClassName: local-storage # 미리 StorageClass를 정의해뒀으니 그 설정을 그대로 쓰지만, 만약 해당 class명이 없을 경우 storageClass를 생성한다.
  persistentVolumeReclaimPolicy: Recycle
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /var/tmp

스크린샷 2021-09-19 오후 9 35 44

PersistentVolumeClaim 생성.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: local-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi
# 이 기준을 충족하는 PV가 존재하므로, 직전에 생성한 PV를 그대로 사용한다.

스크린샷 2021-09-19 오후 10 56 17

status를 확인해보면 PVC의 상태가 pending이다. PV에 요청을 보낼 client를 기다리는 상태.

스크린샷 2021-09-19 오후 10 56 54

따라서 client 역할을 할 pod를 생성해준다.

apiVersion: v1
kind: Pod
metadata:
  name: my-pv-pod
spec:
  restartPolicy: Never
  containers:
    - name: busybox
      image: busybox
      command: ["sh", "-c", "echo Hello Team, This is Persistnent Volume Claim >> /output/success.txt"]
      ## output 경로에 success.txt 파일 생성
      volumeMounts:
      - mountPath: /output
        name: my-pv
  volumes:
    - name: my-pv
      persistentVolumeClaim:
        claimName: my-pvc # 생성해뒀던 pvc를 연결한다.

스크린샷 2021-09-19 오후 11 01 26

pod를 생성한 뒤 pv의 상태를 확인하면,

  • 이전의 Available 상태와 달리 Bound가 되어 있다.
  • claim 필드도 my-pvc가 등록되어 있다.

pvc의 상태를 확인하면

  • status가 bound로 변경되었다
  • capacity, access mode 등 storage 정보가 활성화되어 있다.

PV가 PVC를 통해 pod에 연결되었다는 의미.

반응형