일회성 앱은 Deployment가 아닌 Job으로

Page content

한 번 실행되면 데몬 처럼 계속해서 동작하는 앱이 아니라 필요한 일을 수행하고 종료되는 앱도 있다. 실행된 시점에 필요한 일을 수행하고 종료하는 형태로 예를 들면 특정 위치에 있는 파일을 처리하고 종료한다거나, 실행된 시점에 외부 서비스에서 필요한 정보를 가져와 어딘가 저장하는 등의 일을 하는. 이런 종류의 앱을 kubernetes에서 Deployment로 배포한 경우 해당 앱은 자신이 해야 할 일을 정상적으로 수행하고 종료되지만, kubernetes scheduler 입장에서는 해당 container가 (의도하지 않게) 종료된 것으로 판단하여 다시 복구하는 절차를 수행한다. 이는 Deployment로 배포된 container는 scheduler를 통해 배포된 것처럼 scheduler를 통해 제거되지 않으면 비정상이라고 판단하기 때문이다.

이때 만일 복구하는데 한도를 두지 않으면 계속해서 앱이 실행되었다 종료되고, 다시 복구되는 작업을 반복하게 되는데 이는 불필요한 에러 로그만 지속적으로 생산하는 상황이 된다.

Use Job instead of Deployment

만일 앱이 실행된 후 필요한 일을 모두 마치고 종료되는 것이 의도된 동작이라면 해당 Pod의 kindDeployment가 아니라 Job 형태로 정의한다.

다만 Job을 이용한 chart를 준비할 때 사소한 이슈라면 helm create 명령으로 만들어진 helm chart template는 기본적으로 DeploymentService등만 만들어 Job 을 위한 YAML 파일을 만들 쉬운 방법이 없다는 점이다.(helm create 명령의 도움말이나 구글링 결과, 2020.10월 기준)

대부분 Job에 대해 설명하는 문서에서는 간단한 YAML 파일을 기준으로 설명하고 있어, helm chart를 사용해서 배포하려는 경우에는 어떻게 chart를 만들어야 할 지 난감하다.

mysqldump vs. mysql

다행히 github에 있는 공식 helm chart를 보면 많은 기여자들이 만든 좋은 예제를 참조할 수 있다.(이것도 역시 open-source가 주는 장점) 그래서 Job 키워드로 검색을 해 보니 몇 개의 chart가 이를 사용하는 것으로 확인되었다. 그 중에 하나 고른 것이 mysqldump. 이름 그래도 mysql의 database를 백업하는 툴 같은데, mysql shell을 이용하거나 API를 이용해서 DB에 연결해서 정보를 백업하는 형태일 것 같다.

이때 서버 역할을 할 mysql은 당연히 늘 실행되어 있는 형태일 거라 일반적인 Deployment를 사용할 듯 하고. 그래서 Job을 사용하는 mysqldump chart와 Deployment를 사용하는 mysql의 chart를 비교해 보면 Job을 사용하는 chart를 어떻게 만들면 되는 지 감을 잡을 수 있을 듯 하다.

job.yaml instead of deployment.yaml

mysqldump의 경우 deployment.yaml 대신 job.yaml 파일과 cron.yaml 파일을 가지고 있었다. 그 외 configmapmysqlmysqldump에서 파일 이름만 다를 뿐 양쪽에서 모두 사용하고 있으므로 차이점은 아니고.

cychong@mini1:~/work/Helm/reference/official-helm-charts/stable/mysqldump/templates$ ls -al
total 44
drwxrwxr-x 2 cychong cychong 4096 Oct  9 09:12 .
drwxrwxr-x 4 cychong cychong 4096 Sep 20  2019 ..
-rw-rw-r-- 1 cychong cychong 1934 Sep 20  2019 NOTES.txt
-rw-rw-r-- 1 cychong cychong 1869 Sep 20  2019 _helpers.tpl
-rwxrwxr-x 1 cychong cychong 5478 Sep 20  2019 configmap.yaml
-rw-rw-r-- 1 cychong cychong  806 Sep 20  2019 cron.yaml
-rw-rw-r-- 1 cychong cychong  691 Sep 20  2019 gcpserviceaccount.yaml
-rw-rw-r-- 1 cychong cychong  298 Sep 20  2019 job.yaml
-rw-rw-r-- 1 cychong cychong  625 Sep 20  2019 pvc.yaml
-rw-rw-r-- 1 cychong cychong 1238 Sep 20  2019 secret.yaml

cychong@mini1:~/work/Helm/reference/official-helm-charts/stable/mysq/templates/ls -al
total 52
drwxrwxr-x 3 cychong cychong 4096 Sep 20  2019 .
drwxrwxr-x 3 cychong cychong 4096 Sep 20  2019 ..
-rw-rw-r-- 1 cychong cychong 1797 Sep 20  2019 NOTES.txt
-rw-rw-r-- 1 cychong cychong  989 Sep 20  2019 _helpers.tpl
-rw-rw-r-- 1 cychong cychong  292 Sep 20  2019 configurationFiles-configmap.yaml
-rw-rw-r-- 1 cychong cychong 8137 Sep 20  2019 deployment.yaml
-rw-rw-r-- 1 cychong cychong  295 Sep 20  2019 initializationFiles-configmap.yaml
-rw-rw-r-- 1 cychong cychong  868 Sep 20  2019 pvc.yaml
-rwxrwxr-x 1 cychong cychong 1245 Sep 20  2019 secrets.yaml
-rw-rw-r-- 1 cychong cychong  800 Sep 20  2019 servicemonitor.yaml
-rw-rw-r-- 1 cychong cychong 1104 Sep 20  2019 svc.yaml
drwxrwxr-x 2 cychong cychong 4096 Sep 20  2019 tests

job.yaml

한번 실행되면 명시적으로 중지시키지 않는 이상 계속해서 실행되는 deployment와 달리 한번 실행되어 필요한 작업을 완료한 후 종료되는 app은 kindDeployment가 아닌 Job으로 지정한다.

$ helm install -f pocket-stat-value.yaml pocket-stat helm-chart/charts/pocket-stat
NAME: pocket-stat
LAST DEPLOYED: Fri Oct  9 16:24:22 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services pocket-stat)
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

Job 타입의 정보는 kubectl get jobs 명령으로 확인할 수 있다.

$ kubectl get jobs
NAME          COMPLETIONS   DURATION   AGE
pocket-stat   0/1           6s         6s

그리고 해당 Job에서 생성했던 Pod 역시 아래와 같이 kubectl get pods명령으로 확인이 가능한데, 다른 Deployment 형태의 Pod와 달리 STATUSCompleted임을 알 수 있다. 즉 필요한 일을 마치 종료되었는데 이를 말 그대로 “Complete” 된 것으로 판단하고, 다시 재실행시키지 않는다는 의미이다.

$ kubectl get pods
NAME                             READY   STATUS      RESTARTS   AGE
grafana-6b66fb5758-gmvgf         1/1     Running     1          4d17h
influxdb-0                       1/1     Running     1          6d6h
pocket-stat-ksrvr                0/1     Completed   0          12s
podcast-nginx-659bcb6485-ps7qq   1/1     Running     1          6d7h
sosa0sa-nginx-87fc9949c-wb4jp    1/1     Running     1          6d7h

Pod의 실행 결과는 여느 Pod 와 동일하게 kubectl logs 명령으로 확인 가능.

$ kubectl logs pocket-stat-ksrvr
Check articles added since 2020-10-08 00:00:00
16 are collected in yesterday