airflow를 이용해 apbot 의 글발행을 자동화했다.
처음에는 도커로 띄운 로컬 airflow에서 작업하다 worker에서 apbot를 구동하는 데 환경설정 문제에 부딪혔다. 그래서 이리저리 찾다가 apbot app & 환경설정을 도커 이미지로 만든 후 이미지를 실행할 수 있는 operator를 실행하기로 했다.
처음으로 찾은 것은 KubernetePodOperator
였다. airflow 쿠버네티스 환경에서 사용하는 회사들의 글들을 읽어보다가 찾은 것이었는데, 쿠버네티스와 클라우드도 겸사겸사 사용해볼 겸 로컬에서 GKE로 airflow 환경을 옮겼다. 하지만 이 시도는 결국 보류하게 되었다. 쿠버네티스 환경에 익숙치 않으니 테스트와 삽을 들고 있는 시간이 길어지면서 과금문제가 생겼기 때문이다.
다시 도커 로컬 작업환경으로 돌아와 방법을 찾았다.
worker에서 당면한 모든 문제들과 해결책
- 로그파일 기록 문제 → 로그가 많이 생기지 않기 때문에 로그스태시에 http 방식을 이용해 보냄.
- 워커 실행 위치로 인한 가상환경 실행 문제/ 환경변수 문제 → worker가 작업하는 디렉토리인 /tmp에 가상환경 만들어서 app이 돌아갈 가상환경 생성
- 크롬브라우저, 기타 필요한 패키지 문제 → 로컬에서는 worker가 컨테이너로 계속 구동되고 있기 때문에 컴포즈 파일에서
build:
값을 설정해 설치할 수 있도록 수정하거나 커밋해서 새 이미지 생성.
- 컨테이너에서 atlas 몽고DB 커넥션 connection timeout문제 (free 티어라 그런 것으로 추정)
사용 Operator
위 문제를 해결한 후 BashOperator
로 start_for_worker.sh를 실행하니 드디어 자동발행을 성공할 수 있었다. 이 방법의 문제점은 app을 실행할 때마다 가상환경을 새로 만들어야 해서 시간이 오래 걸린다는 점이었다. worker는 /tmp
밖에서는 작업을 하지 않는 듯 보였다.
또 다른 앱을 실행한다고 했을 때 그 앱이 apt-get 명령어로 패키지를 설치해야 하면 worker airflow를 새로 띄워야 하고 무척 번거로워진다.
![](https://blog.kakaocdn.net/dn/bacLpb/btrs3RPH0hD/774OtkFl7ephk0W1TrkBR1/img.png)
이 방법을 성공한 후 찾은 다른 방법은 dockerOperator
였다. 이걸보니 작업할 때 오퍼레이터도 먼저 꼼꼼히 찾아야겠다는 생각이 들었다. 사용자가 많아 다양한 오퍼레이터들이 이미 많이 존재할 테니..로컬에는 도커만 설치된 상태이고, APP을 이미지화하면 앞선 방법에서처럼 매번 환경설정을 해줄 필요가 없으니 훨씬 효율적이라 생각되었다.
참조:
실행은 성공했지만 이 또한 문제가 있었다. airflow volume에 /var/run/docker.sock
을 마운트하는데 권한문제로 worker가 작업을 도커에 대한 권한거부(permission denied)를 일으켰기 때문이다.
이 문제에 대해서는 호스트의 docker.sock 퍼미션을 777로 바꾸라는 등의 답변을 찾을 수 있었는데 그 방법을 쓰고 싶지 않아서 다른 방법을 찾다 docker-proxy 이미지를 사용해 우회해서 docker.sock에 접근하는 방법을 사용했다. 이 방법도 문제가 있는데, 환경변수에 DOCKER_HOST를 등록하면 docker 커멘드가 먹통이 된다.
참조:
![](https://blog.kakaocdn.net/dn/cnXwlq/btrs4M8QUdv/5D4qbxL7xT2bL8PH1TPrFk/img.png)
내 로컬 /var/run/docker.sock 우회 컨테이너
![](https://blog.kakaocdn.net/dn/bcDz9R/btrs9jw6tFS/tKHgdkLwGkNKkhKXknBOkK/img.png)
테스트 DAG의 dockerOperator
from airflow import DAG from airflow.operators.bash_operator import BashOperator from airflow.operators.docker_operator import DockerOperator from airflow.operators.python_operator import BranchPythonOperator from airflow.operators.dummy_operator import DummyOperator default_args = { 'owner' : 'airflow', 'description' : 'Use of the DockerOperator', 'depend_on_past' : False, 'start_date' : datetime(2021, 5, 1), 'email_on_failure' : False, 'email_on_retry' : False, 'retries' : 1, 'retry_delay' : timedelta(minutes=5) } with DAG('lucca_docker_operator_dag', default_args=default_args, schedule_interval="5 * * * *", catchup=False) as dag: start_dag = DummyOperator( task_id='start_dag' ) t2 = DockerOperator( task_id='docker_command_sleep', image='lucca_apbot:1.0.0', container_name='task___command_sleep', api_version='auto', auto_remove=True, #command="/bin/sleep 30", #docker_url="unix://var/run/docker.sock", docker_url='tcp://docker-proxy:2375', network_mode="bridge" )
결과 로그
![](https://blog.kakaocdn.net/dn/dYqZGy/btrs67q5AeM/BEyZuHtsLNOMvcgDX2QHFk/img.png)
![](https://blog.kakaocdn.net/dn/cskJVI/btrs7FOiakJ/qKwBDpUnb9d9IlWeIkZsW0/img.png)
![](https://blog.kakaocdn.net/dn/Zt1ht/btrs7BLY51r/KmsCi20BirCQUOe451HpR1/img.png)
신경써야 할 것들
- 빌드시 이미지 사이즈 : python3.9 이미지를 기본을 가져다 썼는데 이미지를 만들고 보니 크기가 2GB였다. 패키지 사이즈도 고려해서 빌드를 해야함을 느꼈다.
- 레이어를 생각한 빌드 스크립트 짜기: 가급적 변동이 없는 스크립트들을 먼저 적고 앱소스 등의 변화가 많은 부분을 적는 부분은 뒤에 넣자.