[dkron] kubernetes에서 job 스케쥴을 대시보드에서 관리해보자.(feat. Dkron)
ruinnel
2816 Words CHANGE ME READ TIME 12 Minutes, 48 Seconds
2020-04-13 15:15 +0000
원하는 것들
- web용 대시보드를 제공 할 것
- kotlin + spring과 함께 쓸 수 있을 것(가능하면 언어 무관하게 사용 가능 할것)
- kubernetes 환경에서 쓸 수 있을 것
- SPOF(단일 장애점)이 없을 것
- 기존에 사용중이 Http 요청방식으로 실행되는 Job도 지원 할 것
- crontab으로 설정해 둔 shell script들도 포용 할수 있었으면..
현재 사용중인 구성(kubernetes cronjob + rancher)과 단점
kubernetes도 cronjob을 지원합니다.
그리고 kubernetes 관리툴로 rancher를 사용하면 kubectl로 볼수 있는 대부분의 조회 및 제어가 가능합니다.
이런 화면에서 cronjob이 동작한 이력과 설정된 시간을 cron 표현식으로 확인 가능하고 정해진 시간에 정해진 동작을 실행하는데는 부족함이 없습니다.
위에서 언급했던
원하는 것들
의 대부분을 만족하지만….
몇가지 (몇몇은 치명적인) 단점이 있습니다.
- kubernetes cronjob의 한계에서 오는 문제점
- 시간마다
약
한번 실행되고 100% 실행보장은 되지 않음 (참고) - 최소 반복 단위가
분
이다. (초단위 반복 작업 불가
) - 매 실행시 마다 Pod 생성이 이루어 진다.(시작~ 하고 실제 실행되기까기 딜레이가 있을 수 있다.)
- 시간 표기/설정이
UTC
만 가능. - 로그를 제한적으로만 남길 수 있다.
- 로그라는게 Pod이 사라지지 않고 유지되는 것이고 만약 AWS의 EKS를 사용하고 있다면 치명적이다.
- AWS EKS의 경우 Pod의 네트워크 구성이 kubernetes의 Node로 동작중인 EC2 Instance에 가상 IP를 추가하고 그걸 Pod에 맵핑하는 방식인데 EC2 Instance 유형별로 가질 수 있는 가상 IP의 개수 제한이 존재합니다.
- 시간마다
- rancher UI의 불만족스러운 부분
- cronjob만 따로 모아서 보여주지 않는다.
- kubernetes에서 UTC로만 설정되는걸 Timezone 변경해서 보여줬음 좋겠다.
사실 초단위로 동작하거나 실행보장 같은 이슈는 별도로 데몬형태로 프로그램을 개발해서 돌리는게 맞다고 생각합니다.
위에 나열한 모든 문제점을 해결 할 수는 없고 UTC
/ 로그 제한
/ AWS EKS의 Pod 개수 이슈
+ rancher UI 불만사항
이라도 개선하고자 시도했고 그 내용을 정리한 글입니다.
Dkron
- 오픈소스(pro 버전은 유료)
- golang로 만들어짐 (단일 실행파일, 의존성이 없어 docker image의 용량을 작게 유지할 수 있음.)
- SPOF(단일 장애점)이 없음 (간단하게 클러스터 구성 가능)
- DB 필요없음(파일로 저장,
경우에 따라선 단점이 될지도) - Restful API 지원(Job 등록을 CI/CD 과정 중에 수행 있음)
- http / shell Executor 지원
- kubernetes에서 쉽게 클러스터를 구성할 수 있는 기능이 포함되어 있음.(go-discover)
- Job 스케쥴 지정시에도
cron 표현식 + Timezone으로 설정 가능.
- 클러스터 구성에 serf, plugin 구성에 go-plugin, 노드간 상태 관리에 raft 사용.
믿음의 hashicorp - 대시보드 지원
- Job 일람 / 수동 실행 / Job 설정 변경 / 삭제
가능!
- 각 Job 별 실행 로그도 열람
가능!
- Job 일람 / 수동 실행 / Job 설정 변경 / 삭제
그런데 Dkron 홈페이지의 기능목록에서 확인 해보면 Docker Executor
가 있지만 Pro 버전만 지원하고, Kubernetes Job Executor
같은건 기능 목록에 보이지 않았습니다.
하지만 Plugin 개발문서를 훑어 봤더니 만드는 것이 생각보다 어렵지 않아 보였습니다.
이렇게 된거.. Kubernetes Job Executor를 만들어 보자.
~golang 어렵지 않아요 ㅎㅎ
Dkron Plugin 만들기
처리 흐름
- dkron에 Job을 등록할때
executor
를 지정executor_config
에 Kubernetes Job 실행을 위한 설정을 넣습니다.- json의 값에 우겨 넣어야 하므로 개행이 없는 JSON 양식으로 하는게 좋습니다.
- 이 json 안에 Docker Image나 Volumn, Secret 등 Kubernetes에서 Job을 실행하기 위한 모든 설정을 넣야 합니다.
- 사족…
kind: Pod
로 하지마세요. Pod은 항상 실행되고 있는걸 전제로 동작하므로 실행 후 종료가 되는 프로그램을 Pod으로 실행하면 Pod이 Failed 상태가 됩니다.
- Dkron Job의
excutor
설정에 의해 추가한 Plugin으로 Dkron Job의 실행 요청이 도착(Execute 함수
가 호출됨)합니다. Execute 함수
호출시executor_config
에 지정한 값도 전달되므로, 해당 값을 이용해 Kubernetes Job을 실행하면 됩니다.
적어놓고 보니 생각보다 간단 할거 같습니다. 하지만… 계획과 실전은 다르죠ㅠ_ㅠ
어떻게 만들지 정보를 수집합시다.
위 2가지를 보면 결국 Dkron에서 기본 제공하는 Http Executor
/ Shell Executor
도 Plugin 인데 배포시 포함된 것 뿐입니다. 즉, 구조 그대로 참고해서 만들면 됩니다. 솔직히 Plugin 문서가 좀 빈약합니다.
만든 후에는 빌드한 실행파일을 dkron-executor-kubejob(executor의 이름이 됩니다.)
형태의 파일명으로 /etc/dkron/
디렉토리 하위에 넣어두고 dkron을 실행하면 알아서 해당 plugin을 로드합니다.
Builtin Plugin을 참조 동일한 구조로 만들고 Execute()
함수를 구현합니다.
어짜피 Kubernetes Cluster내에서 실행할 것이므로 Kubernetes 클러스터 내에서 접속 설정 가져오기를 참고하면 간단하게 접속 권한은 해결 됩니다.
이후는 Kubernetes go client 문서를 참고해서 Kubernetes Job을 생성하면 될거 같습니다.
실제로 만들어 봤습니다.
- 소스 (github)
- 상세 설정등 사용법은 github의 README에 정리해 두었습니다.
dkron을 kubernetes에 plugin을 추가해서 띄워 봅시다.
우선 위에서 작업한 plugin을 빌드해서 실행파일로 만들고 dkron 이미지에 추가해서 새로운 docker 이미지를 만듭니다.
FROM golang:1.14 as build-stage
RUN mkdir -p /app
WORKDIR /app
ENV GO111MODULE=on
COPY go.mod go.mod
COPY go.sum go.sum
COPY main.go main.go
COPY kubejob.go kubejob.go
RUN go mod download
RUN go build -o ./dkron-executor-kubejob
FROM dkron/dkron:v2.2.1 as dkron-image
COPY --from=build-stage /app/dkron-executor-kubejob /etc/dkron/plugins/dkron-executor-kubejob
이제 helm chart를 만들어서 kubernetes에 올립니다.
- https://github.com/ruinnel/dkron-executor-kubejob/tree/master/chart
values.yaml
을 적절히 수정해서 배포 합니다.
replicas: 1
host: dkron.domain.com
images:
dkron:
name: dkron
image: ruinnel/dkron
tag: v2.2.1
service:
extraAnnotations: {}
# ingress configuration for nginx-ingress-controller
ingress:
extraAnnotations:
nginx.ingress.kubernetes.io/proxy-body-size: 5m
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "*"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
실제로 Dkron에 Job을 추가/실행 해봅시다.
dkron의 API를 이용해 테스트용 Job를 등록합니다.
busybox
이미지를 이용해 간단하게 env를 출력하도록 할 예정입니다.
예제 JSON
{
"name": "test-job1",
"schedule": "@every 1m",
"timezone": "Asia/Seoul",
"owner": "ruinnel",
"owner_email": "ruinnel@gmail.com",
"disabled": false,
"tags": {
"job-purpose": "test"
},
"concurrency": "allow",
"executor": "kubejob",
"executor_config": {
"namespace": "default",
"job": "{\"apiVersion\":\"batch/v1\",\"kind\":\"Job\",\"metadata\":{\"name\":\"dkron-test\",\"namespace\":\"default\"},\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"dkron-test-print-env\",\"image\":\"busybox:1.31.1\",\"args\":[\"env\"]}],\"restartPolicy\":\"Never\"}},\"backoffLimit\":4}}",
"successfulJobsHistoryLimit": 3,
"failedJobsHistoryLimit": 1,
"timeout": 10,
}
}
이제 Dkron의 대시보드에 Job이 추가되고 1분마다 실행 후 로그가 쌓입니다!!
kubectl
이나 rancher
에서도 실행된 Job의 이력(Successed 상태로 남은 Pod)을 확인 할 수 있습니다.
(성공한 Pod을 안남기도록 설정하지 않았다면..)
아직 남은 문제점…
- Pod이 너무 많다.
successfulJobsHistoryLimit / failedJobsHistoryLimit
설정으로 조절이 가능하지만 Pod이 유지되어야 하다보니 너무 많이 남기면 리소스가 낭비되고, 너무 적게 남기면 최근 이력 밖에 확인이 안됨.- dkron에 처리 결과가 남으니 위 2개 설정을 0으로 하면 실행 후 Pod이 삭제됨.
하지만 안남기려니 뭔가 불안..
- Kubernetes Job 실행을 위한 설정이
executor_config
에 다 때려 넣어야 하다보니 너무 복잡해 보입니다.
- yaml을 더 많이 쓰는데 yaml로 넣기엔 개행처리가 귀찮고 yaml로 작성 후에 JSON으로 변환 후 붙여 넣게 되는데 번거롭습니다.
- 배포에는 어떻게 녹일 것인가?
- 실제 Kubernetes Job으로 실행 할 Docker 이미지를 빌드하고나서 curl 등을 이용해 dkron API를 호출해 Job을 등록하면 될 듯 합니다.
- 여전히 매번 실행시마다 Pod생성이 이루어져 무겁다.
- job도 결국 spring으로 만들어져 있는데 매번 실행시마다 처리하기엔 spring의 부팅은 너무 느립니다. (ㅜ_ㅜ)