data engineering

airflow Kubernetes Executor, how helm values.yaml works

qkqhxla1 2021. 9. 5. 18:47

바로 이전글(https://qkqhxla1.tistory.com/1159) 에서는 kubernetes에서 helm을 사용해 airflow를 구성하는데 초점을 두었었다. 그런데 내가 kubernetes와 helm에 익숙하지 않아서 가장 쉽고 익숙한 airflow를 CeleryExecutor로 구성했었다.
airflow-worker에 spark를 통째로 올려놓고, docker-in-docker로 docker를 마운트해놓은다음 대부분의 배치 잡은 DockerOperator로 실행하고, spark잡을 실행할 때는 airflow-worker내에 설치된 스파크를 직접 호출해서 사용한다. 간단한 구성도이다.

CeleryExecutor이 기본 설정으로 되어있기도 하고 구성도 쉬워서 이렇게 만들었었는데, 구성하고 이것저것 글을 찾아 읽다 라인플러스의 기술블로그를 보게되었다. 2편까지 전부 읽고 kubernetes환경에서는 CeleryExecutor + DockerOperator조합보다는 KubernetesExecutor + KubernetesPodOperator조합이 시스템이 커지면 커질수록 자원 활용으로는 더 효율적이라는걸 알게되었다.

CeleryExecutor + DockerOperator + spark 직접설치(현재 내가 구성해놓은거)
장점 : 비교적 배치의 양이 적을 경우에 worker이미지에 대부분의 환경 구성을 미리 한번만 해놓으면 추가적으로 다른 이미지들을 만들지 않아도 되므로 편함. 
단점 : worker가 계속 돌고 있기 때문에 지속적으로 kubernetes의 자원을 점유함, dag가 많아질수록 worker를 수동으로 스케일 아웃해줘야 함. spark를 worker에 직접설치해놔서 worker의 크기가 기본적으로 큼.

KubernetesExecutor + KubernetesPodOperator.
장점 : worker가 항상 돌지 않고 잡이 돌때만 자원을 할당받아서 사용함, worker로 작업을 전달해줄 redis큐가 필요없음, dag가 많아져도 자원 관리는 kubernetes 가 해주기때문에 airflow의 스케일 아웃은 크게 쓸 필요가 없음. 모든 잡을 KubernetesPodOperator로 실행시킬 경우 위에서 적은 초기 세팅(worker에 spark를 설치한다던가)을 해줄 필요가 없음. pod이 worker와 분리되어서 돌아가기 때문에 dag의 task때문에 airflow-worker가 내려가거나 할 걱정할 필요가 없음.
단점 : 이미지를 각각 만들어야 함. 스파크용 이미지, 하둡용 이미지, 파이썬 어플리케이션용 이미지 등. 이미지 하나에 모두 넣으면 가벼운 파이썬 어플리케이션을 도는데 무거운 스파크가 들어가게 되어서 효용성이 떨어짐. 그러므로 이미지를 각각 만들어야함. 큰 시스템이 아니면 귀찮은 작업(예로 돌릴 sqoop 작업이 1~2개만 있다고 가정할때 굳이 이걸 위해 이미지를 만들고 유지보수하기에는 부담스러움.)

바로 구성을 해보고 테스트와 정리를 해봤다. KubernetesExecutor + KubernetesPodOperator는 다음과 같다.

확실히 고정적으로 돌아가던 worker가 빠지고 worker의 역할을 kubernetes가 해주니 자원 효율이 좋다. 그리고 bitnami의 예시를 살펴보니 기본적인 Pod Template을 정의해줄수 있지만 따로 정의가 되어있지 않으면 CeleryExecutor일때 필요한 worker의 이미지를 가져다가 Pod Template로 사용한다. 참조

참조2 : https://bomwo.cc/posts/kubernetespodoperator/

----------------------------------------------------------------------------------------------------------

airflow를 지웠다 설치했다 여러번 하면서 helm구조에 대해 좀더 알아보았다. 여태까지 helm install, uninstall만 해서 그런지 helm이 어떻게 값을 만들어나가는지 디버깅이 거의 불가능했었다. 이번에 삽질하면서 얻은걸 정리한다. 공식홈페이지 읽으면 다 나오는 내용인데 나 자신을 위해서 정리한다.

helm의 구조는 공식홈페이지에 있지만 아래와 같다. 사용시에 우리가 수정하는건 values.yaml파일이고, 이것은 아래에 적혀있듯이 templates내부와 결합해서 사용된다.

wordpress/
  Chart.yaml          # 차트에 대한 정보를 가진 YAML 파일
  LICENSE             # 옵션: 차트의 라이센스 정보를 가진 텍스트 파일
  README.md           # 옵션: README 파일
  values.yaml         # 차트에 대한 기본 환경설정 값들
  values.schema.json  # 옵션: values.yaml 파일의 구조를 제약하는 JSON 파일
  charts/             # 이 차트에 종속된 차트들을 포함하는 디렉터리
  crds/               # 커스텀 자원에 대한 정의
  templates/          # values와 결합될 때, 유효한 쿠버네티스 manifest 파일들이 생성될 템플릿들의 디렉터리
  templates/NOTES.txt # 옵션: 간단한 사용법을 포함하는 텍스트 파일

helm install시에 helm이 구성해주는건 쿠버네티스의 yaml이다. 예로 bitnami/airflow chart의 web.extraVolumes라는 옵션이 존재하는데, 이 web.extraVolumes는 helm이 가져다가 쓰는 변수이며, 이것을 사용해 쿠버네티스의 Volume을 만들어준다. 

chart파일 내부에서  grep을 사용해서 template폴더 내부에서 web.extraVolumes를 쓰는곳을 찾아보았다. ./templates/web/deployment.yaml에서 사용하고 있다고 나오고, 내부를 들어가보았다.

빨간 사각형을 보면 알수 있듯이 web.extraVolumes가 세팅되어 있으면 쿠버네티스의 Volumes: 내부에 추가를 한다. 내부의 문법은 공식문서에서 찾아보면 다 나오고, common.tplvalues.render는 뭔지 찾아보았는데 bitnami에서 만든 함수이다. 구현체는 못찾았는데 설명을 읽어보면 yaml로 넣어주면 되는것 같다. 그럼 yaml로 어떻게 구성하면 될까?

위에 올렸었지만 kubernetes 공식 홈페이지를 참조한다. 만약 docker소켓을 마운트한다고 가정해보자. 쿠버네티스에서는 볼륨을 마운트할때 Volumes와 volumeMounts를 넣어줘야 한다. 이름으로 추측 가능하겠지만 Volumes는 볼륨을 만드는거고(가져오는거고), volumeMounts는 볼륨을 마운트해주는 옵션이다. 
위에서 봤듯이 bitnami chart의 extraVolumes가 kubernetes의 Volumes영역과 연동되어 있고, web.extraVolumeMounts는 volumeMounts과 연동되어있다. values.yaml의 extraVolumes옵션을  세팅해보자. 

extraVolumes:
    - name: docker
      hostPath:
        path: /var/run/docker.sock
        type: Socket

이런식으로 세팅하였다. - name은 이름이고, type은 Volumes부분에서 Socket으로 검색하면 나온다. 이어서 extraVolumeMounts를 세팅하자.

extraVolumeMounts:
    - mountPath: /var/run/docker.sock
      name: docker

volumeMounts는 공식 홈페이지에서도 보면 알겠지만 mountPath와 name만 지정해주면 되는것같다. 이런식으로 values.yaml을 세팅해주면 저 값들이 Volumes와 VolumeMounts로 들어가면서 쿠버네티스 yaml을 만들게 되고, 그 정보로 구성이 된다.