이전에 docker 실습을 통해 to-do앱을 컨테이너화 하고 배포할 수 있었다. 그리고 쿠버네티스 실습에서는 간단한 텍스트를 웹에 띄워주는 앱이 컨테이너화된 이미지를 deployment 워커로드를 통해 배포하고 관리해보았다.
이제는 좀 더 실제 앱에 가까운 예제로 실습을 해보기 위해 도커 실습 때 사용했던 투두리스트 앱을 쿠버네티스를 통해 배포해보자.
클러스터 내 존재하는 영구 저장 공간을 Persistence Volume이라고 한다.
도커 실습 때를 생각해보자. mysql 컨테이너와 todo-app 컨테이너를 개별로 실행시키되, network를 같게해서 todo-app이 mysql이라는 이름만으로 네트워크 내에 운영되던 mysql 컨테이너를 찾고 db에 접속할 수 있었다.
todo 앱을 쿠버네티스로 배포한다고 생각해보자. 도커의 경우 컨테이너가 하나였으니 docker run을 할 때 로컬 마운트 주소를 기반으로 persistence가 가능했다.
그런데 만일 mysql도 replica가 5개인 형태로 배포된다고 해보자. mysql도 파드 형태로 배포되고 deployment에 의해서 관리되기 때문에 문제가 생기면 기존 파드를 파기하고 새롭게 파드를 만들 수 있다. 하지만 mysql 컨테이너 내에는 데이터를 저장하고 있기 때문에 파드가 새로이 만들어지는 과정에서 데이터가 소실된다는 문제가 발생한다.
따라서 영구적으로 데이터를 저장할 수 있는 장치가 필요했고 그래서 등장한 것이 Persistence Volume인 것이다.
도커는 컨테이너가 네트워크에서 DNS로 Database(MySQL)를 찾았던 것처럼 파드는 PV에 데이터를 요청한다. 클러스터 내부에 영구적으로 존재하기 때문에 파드는 PV에 접근할 수 있다.
mysql 컨테이너가 로컬 저장공간에 volume mount시켰던 것처럼 PV또한 로컬 저장공간에 마운트하며 다른 workload resource처럼 yaml 파일을 명세하여 만든다.
PVC는 PV를 사용하기 위한 요청이다.
PV가 클러스터내에 있으니 파드는 필요하면 그냥 PV에 접근하여 데이터를 가져오면 될 것처럼 보인다. 하지만 클러스터내의 PV 공간은 한정되어있기 때문에 때로는 동적으로 저장공간을 분할하여 필요한 만큼 배정하는 것이 필요하다. 이러한 필요성 때문에 PV에 voluem을 요청하는 PVC가 필요하다.
PV는 PVC에서 명세한 조건들을 충족하는 스토리지를 제공하여 PVC에 할당한다.

쿠버네티스에서 파드가 데이터를 가져오는 과정은 아래 그림으로 요약된다.

생각해보면, 파드가 가져와야하는 데이터는 결국에 서버의 path에 있고 그 path는 PV에 마운트되어있다.
따라서 파드와 PV를 연결시켜주는 명세가 PVC라고 볼 수 있다.
_
공식 문서에 따르면 PV는 동적으로, 정적으로 할당될 수 있고 어떤 경우든 control plane이 pv와 pvc를 매칭시켜주는 작업을 한다고 한다. 그 작업이 바로 binding이다.
파드는 stateless라서 데이터를 저장하기 적합하지 않아 PV이라는 클러스터 내부의 영구 스토리지가 필요하다.
PV는 동적으로 할당될 수 있기 때문에 파드는 PVC를 통해서 PV에 연결되어 데이터를 요청하고 받아온다.
** stateless란 상태를 저장하지않는 방식이다. 대표적으로 앱을 실행하는 파드가 있다. 파드가 터져서 새롭게 시작하면 이전의 state는 상관없다. 어짜피 프론트를 띄우는 목적이라 이름같은걸 바꿔도 유저에게는 문제가 없기 때문이다.
mysql과 같은 데이터베이스 어플리케이션도 여러 파드로 실행해서 트래픽 증가와 같은 여러 상황에 대응할 수 있다. 그런데 파드는 stateless라는 문제가 있다.
todo-app_1 의 파드가 mysql_1 파드에 연동되어 데이터를 쓰고 있었다고 해보자. todo-app 사용자가 갑자기 데이터를 너무 많이 만들어서 mysql_1이 다운되었다. 파드 컨트롤러가 이를 감지하고 mysql_1이 재생성시켰고 mysql_2가 되어버리면 기존의 todo-app은 여전히 mysql_1 파드와 통신하려고 하니 에러가 발생하고 새로 생성된 파드에는 데이터가 없다.
따라서 파드가 종료되어 재생성되어도 기존의 상태를 유지해줄 방식이 필요하다. 그래서 나온 것이 StatefulSets이다.
StatefulSets의 파드에는 고유한 이름이 부여된다. 따라서 파드가 재시작되어도 그 이름을 그대로 유지하여 기존에 이 파드와 통신하는 다른 파드에 영향을 미치지않는다는 장점이 있다.
그래서 SatefulSet은 배포하는 이름 뒤에 -0, -1, -2와 같은 숫자가 붙어 그 고유성을 보장하게 된다.
공식문서에 소개된 statefulsets 명세 예시이다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # by default is 1
minReadySeconds: 10 # by default is 0
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
StatefulSet 블로그글
쿠버네티스 스테이트풀셋(StatefulSets) - 스테이트풀 애플리케이션 배포하기
StatefulSet 공식문서
Persistent Volume 공식문서
PV,PVC 블로그글
PV,PVC 블로그글