Kubernetes Local PV
개념
rook-ceph 같은 모듈을 사용하지 않고, k8s 클러스터를 구성하는 노드들의 로컬 스토리지를 볼륨으로서 이용하는 방법이 있다.
바로 LocalPV이다.
HostPath와 LocalPV를 헷갈릴 수 있는데, 둘의 가장 큰 차이점은 쿠버네티스 스케줄러가 PV가 어느 노드에 속한지를 알고있다는 것이다.
HostPath의 경우에는 pod가 재기동 될 때, 다른 노드에 스케줄링이 될수 있기 때문에 정상 동작을 보장할 수 없다.
하지만 LocalPV는 쿠버네티스 스케줄러가 PV의 위치를 고려해서 pod의 스케줄링을 해준다.
역으로 생각하면 pod가 특정 노드에 뜨도록 강제할 수도 있다는 것이다.
물론 NodeSelector를 사용하는 방법도 있지만, statefulset 같이 pod를 개별적으로 설정하기 힘든 경우에는 local PV를 사용해서 스케줄링 할 수 있다.
다만 LocalPV는 동적 볼륨 프로비저닝이 안된다. 따라서, PV를 직접 만들어주고 PVC를 만들어야 한다.
예제
가장 먼저 StorageClass를 만든다.
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
volumeBindingMode: WaitForFirstConsumer의 의미는 PVC를 사용하는 pod가 실제로 생성되기 전까지는 PV의 바인딩과 프로비저닝을 기다린다는 의미이다. 쉽게말하면 동적 프로비저닝이 안되기 때문에 쓰는 옵션이다.
그 다음으로 PV를 만든다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: togomi-pv-0
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /tmp
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- mynode-1 # 자신의 노드 이름에 맞게 고쳐줘야함
가장 밑에 values 하위에 들어가는 mynode-1을 자신의 실제 k8s 노드 이름으로 바꿔주면 된다.
이제 PVC를 만든다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-my-redis-0
namespace: togomi
spec:
storageClassName: local-storage
volumeName: togomi-pv-0
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
volumeName을 넣어줘야 원하는 PV와 바운드가 보장이되니 반드시 넣어주자!
드디어 볼륨을 쓸 준비가 됐다. 직접 써보도록 하자.
일반적인 pod는 쉬울테니 스킵하고, 대신에 statefulset을 사용해보자.
아래는 redis statefulset 예시이다.
위 yaml에서는 0번 PVC만 보여줬지만, 나는 1,2까지 총 세개의 PV와 PVC를 만들어두었다.
statefulset은 이름 규칙이 정해져있으니 그 규칙에따라 미리 PVC를 만들어줘야 한다.
그 규칙은 { volumeClaimTemplate 이름 - statefulset - 숫자 } 의 형식이다.
즉 아래와 같은 statefulset은 "local-my-redis-0, local-my-redis-1, local-my-redis-2"의 pvc를 생성할 것이다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
redis.opstreelabs.in: "true"
labels:
app: redis-cluster-leader
redis_setup_type: cluster
role: leader
name: my-redis
namespace: togomi
spec:
podManagementPolicy: OrderedReady
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: redis-cluster-leader
redis_setup_type: cluster
role: leader
serviceName: redis-cluster-leader
template:
metadata:
labels:
app: redis-cluster-leader
redis_setup_type: cluster
role: leader
spec:
containers:
- env:
- name: PERSISTENCE_ENABLED
value: "true"
- name: REDIS_ADDR
value: redis://localhost:6379
- name: REDIS_PASSWORD
value: "test"
- name: SERVER_MODE
value: cluster
- name: SETUP_MODE
value: cluster
image: quay.io/opstree/redis:v6.2.5
imagePullPolicy: IfNotPresent
livenessProbe:
exec:
command:
- bash
- /usr/bin/healthcheck.sh
failureThreshold: 5
initialDelaySeconds: 15
periodSeconds: 15
successThreshold: 1
timeoutSeconds: 5
name: redis-cluster-leader
readinessProbe:
exec:
command:
- bash
- /usr/bin/healthcheck.sh
failureThreshold: 5
initialDelaySeconds: 15
periodSeconds: 15
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
cpu: 101m
memory: 128Mi
requests:
cpu: 101m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /data
name: local
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
volumeClaimTemplates:
- metadata:
name: local
spec:
accessModes:
- ReadWriteOnce
storageClassName: "local-storage"
resources:
requests:
storage: 1Gi
정상적으로 생성이 다 잘되었다면 아래와 같이, 내가 의도한 노드에 파드가 스케줄링 되는 것을 확인할 수 있다.
이런 방식으로 statefulset의 개별 pod들도 특정 노드에 띄우는 것이 가능하다!