Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

์•ˆ๋…•, ํ•˜๋ธŒ๋ฅด! ์ด ๊ธฐ์‚ฌ์—์„œ๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด ๊ธฐ์—… DWH ๋˜๋Š” DataLake์˜ ์ธํ”„๋ผ์—์„œ ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•œ ํ›Œ๋ฅญํ•œ ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. Apache Airflow(์ดํ•˜ Airflow)์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Š” Habrรฉ์— ๋Œ€ํ•œ ๊ด€์‹ฌ์„ ๋ถ€๋‹นํ•˜๊ฒŒ ๋นผ์•—๊ธด ๊ฒƒ์ด๋ฉฐ, ์ฃผ์š” ๋ถ€๋ถ„์—์„œ๋Š” ETL/ELT ํ”„๋กœ์„ธ์Šค์— ๋Œ€ํ•œ ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์„ ํƒํ•  ๋•Œ ์ ์–ด๋„ Airflow๋ฅผ ์‚ดํŽด๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ์Œ์„ ํ™•์‹ ์‹œํ‚ค๋ ค๊ณ  ๋…ธ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์ „์— ์ €๋Š” Tinkoff Bank์—์„œ ๊ทผ๋ฌดํ•  ๋•Œ DWH๋ฅผ ์ฃผ์ œ๋กœ ์ผ๋ จ์˜ ๊ธฐ์‚ฌ๋ฅผ ์ผ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ €๋Š” Mail.Ru ๊ทธ๋ฃน ํŒ€์˜ ์ผ์›์ด ๋˜์–ด ๊ฒŒ์ž„ ๋ถ„์•ผ์˜ ๋ฐ์ดํ„ฐ ๋ถ„์„์„ ์œ„ํ•œ ํ”Œ๋žซํผ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋‰ด์Šค์™€ ํฅ๋ฏธ๋กœ์šด ์†”๋ฃจ์…˜์ด ๋‚˜์˜ค๋ฉด ์ €ํฌ ํŒ€๊ณผ ์ €๋Š” ์—ฌ๊ธฐ์„œ ๋ฐ์ดํ„ฐ ๋ถ„์„์„ ์œ„ํ•œ ํ”Œ๋žซํผ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ”„๋กค๋กœ๊ทธ

๊ทธ๋Ÿผ ์‹œ์ž‘ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ณต๊ธฐ ํ๋ฆ„์ด๋ž€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์ด๊ณณ์€ ๋„์„œ๊ด€(ํ˜น์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ธํŠธ) ์ž‘์—… ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฐœ๋ฐœ, ๊ณ„ํš ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. Airflow์˜ ์ฃผ์š” ๊ธฐ๋Šฅ: Python ์ฝ”๋“œ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์„ค๋ช…(๊ฐœ๋ฐœ)ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ”„๋กœ์ ํŠธ ๋ฐ ๊ฐœ๋ฐœ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ๋งŽ์€ ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณธ์งˆ์ ์œผ๋กœ (์˜ˆ๋ฅผ ๋“ค์–ด) ETL ํ”„๋กœ์ ํŠธ๋Š” Python ํ”„๋กœ์ ํŠธ์ผ ๋ฟ์ด๋ฉฐ ์ธํ”„๋ผ, ํŒ€ ๊ทœ๋ชจ ๋ฐ ํ™˜๊ฒฝ์˜ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ๊ณ ๋ คํ•˜์—ฌ ์›ํ•˜๋Š” ๋Œ€๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์š”๊ตฌ ์‚ฌํ•ญ. ๋„๊ตฌ์ ์œผ๋กœ ๋ชจ๋“  ๊ฒƒ์ด ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด PyCharm + Git์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ํ›Œ๋ฅญํ•˜๊ณ  ๋งค์šฐ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค!

์ด์ œ Airflow์˜ ์ฃผ์š” ํ•ญ๋ชฉ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ ๋ณธ์งˆ๊ณผ ๋ชฉ์ ์„ ์ดํ•ดํ•จ์œผ๋กœ์จ ํ”„๋กœ์„ธ์Šค ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ตœ์ ์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ฃผ์š” ์—”ํ„ฐํ‹ฐ๋Š” ๋ฐฉํ–ฅ์„ฑ ๋น„์ˆœํ™˜ ๊ทธ๋ž˜ํ”„(์ดํ•˜ DAG)์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

DAG

DAG๋Š” ํŠน์ • ์ผ์ •์— ๋”ฐ๋ผ ์—„๊ฒฉํ•˜๊ฒŒ ์ •์˜๋œ ์ˆœ์„œ๋กœ ์™„๋ฃŒํ•˜๋ ค๋Š” ์ž‘์—…์˜ ์˜๋ฏธ ์žˆ๋Š” ์—ฐ๊ฒฐ์ž…๋‹ˆ๋‹ค. Airflow๋Š” DAG ๋ฐ ๊ธฐํƒ€ ํ•ญ๋ชฉ ์ž‘์—…์„ ์œ„ํ•œ ํŽธ๋ฆฌํ•œ ์›น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

DAG๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์ž๋Š” DAG๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ DAG ๋‚ด ์ž‘์—…์ด ๊ตฌ์ถ•๋  ์—ฐ์‚ฐ์ž ์„ธํŠธ๋ฅผ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋˜ ๋‹ค๋ฅธ ์ค‘์š”ํ•œ ์—”ํ„ฐํ‹ฐ์ธ Airflow Operator๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์—ฐ์‚ฐ์ž

์šด์˜์ž๋Š” ์ž‘์—… ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ธฐ๋ฐ˜์ด ๋˜๋Š” ์—”ํ„ฐํ‹ฐ๋กœ, ์ž‘์—… ์ธ์Šคํ„ด์Šค ์‹คํ–‰ ์ค‘์— ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. GitHub์˜ Airflow ๋ฆด๋ฆฌ์Šค ์ด๋ฏธ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์—ฐ์‚ฐ์ž ์„ธํŠธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ:

  • BashOperator - bash ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋Š” ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค.
  • PythonOperator - Python ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค.
  • EmailOperator โ€” ์ด๋ฉ”์ผ ์ „์†ก์„ ์œ„ํ•œ ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค.
  • HTTPOperator - http ์š”์ฒญ ์ž‘์—…์„ ์œ„ํ•œ ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค.
  • SqlOperator - SQL ์ฝ”๋“œ ์‹คํ–‰์„ ์œ„ํ•œ ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค.
  • ์„ผ์„œ๋Š” ์ด๋ฒคํŠธ(ํ•„์š”ํ•œ ์‹œ๊ฐ„ ๋„์ฐฉ, ํ•„์š”ํ•œ ํŒŒ์ผ์˜ ๋ชจ์–‘, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ผ์ธ, API์˜ ์‘๋‹ต ๋“ฑ)๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์—ฐ์‚ฐ์ž์ž…๋‹ˆ๋‹ค.

DockerOperator, HiveOperator, S3FileTransferOperator, PrestoToMysqlOperator, SlackOperator์™€ ๊ฐ™์€ ๋ณด๋‹ค ๊ตฌ์ฒด์ ์ธ ์—ฐ์‚ฐ์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ž์‹ ์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ ์—ฐ์‚ฐ์ž๋ฅผ ๊ฐœ๋ฐœํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด MongoDB์—์„œ Hive๋กœ ๋ฌธ์„œ๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” ์—ฐ์‚ฐ์ž์ธ MongoDBToHiveViaHdfsTransfer์™€ ์ž‘์—…์„ ์œ„ํ•œ ์—ฌ๋Ÿฌ ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํด๋ฆญ ํ•˜์šฐ์Šค: CHLoadFromHiveOperator ๋ฐ CHTableLoaderOperator. ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋กœ์ ํŠธ์—์„œ ๊ธฐ๋ณธ ๋ฌธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฅผ ์ƒˆ ๋ฌธ์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ถ”๊ฐ€ ๊ฐœ๋ฐœ์ด ๋‹จ์ˆœํ™”๋˜๊ณ  ํ”„๋กœ์ ํŠธ์—์„œ ์—ฐ์‚ฐ์ž ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ™•์žฅ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ์ด๋Ÿฌํ•œ ๋ชจ๋“  ์ž‘์—… ์ธ์Šคํ„ด์Šค๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋ฉฐ ์ด์ œ ์Šค์ผ€์ค„๋Ÿฌ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์Šค์ผ€์ค„๋Ÿฌ

Airflow์˜ ์ž‘์—… ์Šค์ผ€์ค„๋Ÿฌ๋Š” ๋‹ค์Œ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์…€๋Ÿฌ๋ฆฌ. Celery๋Š” ๋Œ€๊ธฐ์—ด์„ ๊ตฌ์„ฑํ•˜๊ณ  ์ž‘์—…์˜ ๋น„๋™๊ธฐ ๋ฐ ๋ถ„์‚ฐ ์‹คํ–‰์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” Python ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. Airflow ์ธก์—์„œ๋Š” ๋ชจ๋“  ์ž‘์—…์ด ํ’€๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค. ํ’€์€ ์ˆ˜๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๊ทธ ๋ชฉ์ ์€ ์†Œ์Šค ์ž‘์—… ์ž‘์—… ๋ถ€ํ•˜๋ฅผ ์ œํ•œํ•˜๊ฑฐ๋‚˜ DWH ๋‚ด ์ž‘์—…์„ ๋Œ€ํ‘œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ’€์€ ์›น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๊ฐ ํ’€์—๋Š” ์Šฌ๋กฏ ์ˆ˜์— ์ œํ•œ์ด ์žˆ์Šต๋‹ˆ๋‹ค. DAG๋ฅผ ๋งŒ๋“ค ๋•Œ ํ’€์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

ALERT_MAILS =  Variable.get("gv_mail_admin_dwh")
DAG_NAME = 'dma_load'
OWNER = 'Vasya Pupkin'
DEPENDS_ON_PAST = True
EMAIL_ON_FAILURE = True
EMAIL_ON_RETRY = True
RETRIES = int(Variable.get('gv_dag_retries'))
POOL = 'dma_pool'
PRIORITY_WEIGHT = 10

start_dt = datetime.today() - timedelta(1)
start_dt = datetime(start_dt.year, start_dt.month, start_dt.day)

default_args = {
    'owner': OWNER,
    'depends_on_past': DEPENDS_ON_PAST,
    'start_date': start_dt,
    'email': ALERT_MAILS,
    'email_on_failure': EMAIL_ON_FAILURE,
    'email_on_retry': EMAIL_ON_RETRY,
    'retries': RETRIES,
    'pool': POOL,
    'priority_weight': PRIORITY_WEIGHT
}
dag = DAG(DAG_NAME, default_args=default_args)
dag.doc_md = __doc__

DAG ์ˆ˜์ค€์—์„œ ์ •์˜๋œ ํ’€์€ ์ž‘์—… ์ˆ˜์ค€์—์„œ ์žฌ์ •์˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋ณ„๋„์˜ ํ”„๋กœ์„ธ์Šค์ธ Scheduler๊ฐ€ Airflow์˜ ๋ชจ๋“  ์ž‘์—… ์˜ˆ์•ฝ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์Šค์ผ€์ค„๋Ÿฌ๋Š” ์‹คํ–‰ ์ž‘์—…์„ ์„ค์ •ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์ž‘์—…์€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.

  1. ์ด์ „ ์ž‘์—…์€ DAG์—์„œ ์™„๋ฃŒ๋˜์—ˆ์œผ๋ฉฐ ์ƒˆ ์ž‘์—…์ด ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๋Œ€๊ธฐ์—ด์€ ์ž‘์—…์˜ ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ์ •๋ ฌ๋˜๋ฉฐ(์šฐ์„ ์ˆœ์œ„๋„ ์ œ์–ด ๊ฐ€๋Šฅ) ํ’€์— ์—ฌ์œ  ์Šฌ๋กฏ์ด ์žˆ์œผ๋ฉด ์ž‘์—…์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋ฌด๋ฃŒ ์ž‘์—…์ž ์…€๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ž‘์—…์ด ํ•ด๋‹น ์…€๋Ÿฌ๋ฆฌ๋กœ ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ์— ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•œ ์ž‘์—…์€ ํ•˜๋‚˜ ๋˜๋Š” ๋‹ค๋ฅธ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

์ถฉ๋ถ„ํžˆ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

์Šค์ผ€์ค„๋Ÿฌ๋Š” ๋ชจ๋“  DAG ์ง‘ํ•ฉ๊ณผ DAG ๋‚ด์˜ ๋ชจ๋“  ์ž‘์—…์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ DAG ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๋ ค๋ฉด DAG๊ฐ€ ์ผ์ •์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

dag = DAG(DAG_NAME, default_args=default_args, schedule_interval='@hourly')

๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด์ง„ ์‚ฌ์ „ ์„ค์ • ์„ธํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: @once, @hourly, @daily, @weekly, @monthly, @yearly.

cron ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

dag = DAG(DAG_NAME, default_args=default_args, schedule_interval='*/10 * * * *')

์‹คํ–‰ ๋‚ ์งœ

Airflow์˜ ์ž‘๋™ ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๋ ค๋ฉด DAG์˜ ์‹คํ–‰ ๋‚ ์งœ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. Airflow์—์„œ DAG์—๋Š” ์‹คํ–‰ ๋‚ ์งœ ์ฐจ์›์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, DAG์˜ ์ž‘์—… ์ผ์ •์— ๋”ฐ๋ผ ๊ฐ ์‹คํ–‰ ๋‚ ์งœ์— ๋Œ€ํ•ด ์ž‘์—… ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ ์‹คํ–‰ ๋‚ ์งœ์— ๋Œ€ํ•ด ์ž‘์—…์„ ๋‹ค์‹œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด DAG๋Š” ์—ฌ๋Ÿฌ ์‹คํ–‰ ๋‚ ์งœ์— ๋™์‹œ์— ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์—ฌ๊ธฐ์— ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๋ถˆํ–‰ํ•˜๊ฒŒ๋„(๋˜๋Š” ๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„) DAG์˜ ์ž‘์—… ๊ตฌํ˜„์ด ์ˆ˜์ •๋˜๋ฉด ์กฐ์ • ์‚ฌํ•ญ์„ ๊ณ ๋ คํ•˜์—ฌ ์ด์ „ ์‹คํ–‰ ๋‚ ์งœ์˜ ์‹คํ–‰์ด ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ณผ๊ฑฐ ๊ธฐ๊ฐ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ข‹์ง€๋งŒ ๊ฒฐ๊ณผ์˜ ์žฌํ˜„์„ฑ์ด ์†์‹ค๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๋ฌผ๋ก  Git์—์„œ ํ•„์š”ํ•œ ์†Œ์Šค ์ฝ”๋“œ ๋ฒ„์ „์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๋ฌด์—‡์„ ๊ณ„์‚ฐํ•˜๋„๋ก ๊ท€์ฐฎ๊ฒŒ ํ•˜๋Š” ์‚ฌ๋žŒ์€ ์•„๋ฌด๋„ ์—†์Šต๋‹ˆ๋‹ค). ํ•„์š”ํ•œ ๋ฐฉ์‹์œผ๋กœ ์ผํšŒ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค).

์ž‘์—… ์ƒ์„ฑ

DAG์˜ ๊ตฌํ˜„์€ Python์˜ ์ฝ”๋“œ์ด๋ฏ€๋กœ, ์˜ˆ๋ฅผ ๋“ค์–ด ์ƒค๋”ฉ๋œ ์†Œ์Šค๋กœ ์ž‘์—…ํ•  ๋•Œ ์ฝ”๋“œ ์–‘์„ ์ค„์ด๋Š” ๋งค์šฐ ํŽธ๋ฆฌํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์†Œ์Šค๋กœ XNUMX๊ฐœ์˜ MySQL ์ƒค๋“œ๊ฐ€ ์žˆ๊ณ  ๊ฐ ์ƒค๋“œ๋กœ ์ด๋™ํ•˜์—ฌ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋…๋ฆฝ์ ์œผ๋กœ ๊ทธ๋ฆฌ๊ณ  ๋ณ‘๋ ฌ๋กœ. DAG์˜ Python ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

connection_list = lv.get('connection_list')

export_profiles_sql = '''
SELECT
  id,
  user_id,
  nickname,
  gender,
  {{params.shard_id}} as shard_id
FROM profiles
'''

for conn_id in connection_list:
    export_profiles = SqlToHiveViaHdfsTransfer(
        task_id='export_profiles_from_' + conn_id,
        sql=export_profiles_sql,
        hive_table='stg.profiles',
        overwrite=False,
        tmpdir='/data/tmp',
        conn_id=conn_id,
        params={'shard_id': conn_id[-1:], },
        compress=None,
        dag=dag
    )
    export_profiles.set_upstream(exec_truncate_stg)
    export_profiles.set_downstream(load_profiles)

DAG๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ ๊ฐ„๋‹จํžˆ ์„ค์ •์„ ์กฐ์ •ํ•˜๊ณ  DAG๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ƒค๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŽธ์•ˆํ•œ!

์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ˜•ํƒœ์˜ ์†Œ์Šค๋กœ ์ž‘์—…ํ•˜๊ฑฐ๋‚˜ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ, ํ…Œ์ด๋ธ” ์ž‘์—… ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ค๋ช…ํ•˜๊ณ  DWH ์ธํ”„๋ผ์˜ ๊ธฐ๋Šฅ์„ ๊ณ ๋ คํ•˜์—ฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋“ฑ ๋” ๋ณต์žกํ•œ ์ฝ”๋“œ ์ƒ์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. N๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ์Šคํ† ๋ฆฌ์ง€์— ๋กœ๋“œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด ๋ชฉ๋ก ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ž‘์—…์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ชฉ๋ก์—์„œ DAG์— N๊ฐœ์˜ ์ž‘์—…์„ ์ƒ์„ฑํ•˜๊ณ  API์˜ ์š”์ฒญ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ํ’€๋กœ ์ œํ•œํ•˜๊ณ  ์Šคํฌ๋žฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. API์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ. ์œ ์—ฐํ•œ!

์ €์žฅ์†Œ

Airflow์—๋Š” ์ž‘์—… ์ƒํƒœ, DAG, ์—ฐ๊ฒฐ ์„ค์ •, ์ „์—ญ ๋ณ€์ˆ˜ ๋“ฑ์„ ์ €์žฅํ•˜๋Š” ์ž์ฒด ๋ฐฑ์—”๋“œ ์ €์žฅ์†Œ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(MySQL ๋˜๋Š” Postgres์ผ ์ˆ˜ ์žˆ๊ณ  Postgres๊ฐ€ ์žˆ์Œ)๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Airflow์˜ ์ €์žฅ์†Œ๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ณ (ํ…Œ์ด๋ธ” ์•ฝ 20๊ฐœ) ๊ทธ ์œ„์— ์ž์ฒด ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ์ž‘์„ฑ ๋ฐฉ๋ฒ•์„ ์ดํ•ดํ•˜๊ธฐ ์ „์— ์˜ค๋žซ๋™์•ˆ ์—ฐ๊ตฌํ•ด์•ผ ํ–ˆ๋˜ Informatica ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์žˆ๋Š” 100500๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋‹ˆํ„ฐ๋ง

๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๋‹จ์ˆœ์„ฑ์„ ๊ณ ๋ คํ•˜๋ฉด ํŽธ๋ฆฌํ•œ ์ž‘์—… ๋ชจ๋‹ˆํ„ฐ๋ง ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Zeppelin์—์„œ ๋ฉ”๋ชจ์žฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—… ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

์ด๋Š” Airflow ์ž์ฒด์˜ ์›น ์ธํ„ฐํŽ˜์ด์Šค์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Airflow๋Š” ์ผ๊ด„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŽธ๋ฆฌํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

Airflow ์ฝ”๋“œ๋Š” ์˜คํ”ˆ ์†Œ์Šค์ด๋ฏ€๋กœ Telegram์— ์•Œ๋ฆผ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž‘์—…์˜ ๊ฐ ์‹คํ–‰ ์ธ์Šคํ„ด์Šค๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ „์ฒด ๊ฐœ๋ฐœ ๋ฐ ์ง€์› ํŒ€์ด ๊ตฌ์„ฑ๋œ Telegram์˜ ๊ทธ๋ฃน์— ์ŠคํŒธ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

Telegram์„ ํ†ตํ•ด(ํ•„์š”ํ•œ ๊ฒฝ์šฐ) ์ฆ‰๊ฐ์ ์ธ ์‘๋‹ต์„ ๋ฐ›๊ณ , Zeppelin์„ ํ†ตํ•ด Airflow์˜ ์ž‘์—…์— ๋Œ€ํ•œ ์ „๋ฐ˜์ ์ธ ๊ทธ๋ฆผ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์ „์ฒด๋กœ

Airflow๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜คํ”ˆ ์†Œ์Šค์ด๋ฏ€๋กœ ๊ธฐ์ ์„ ๊ธฐ๋Œ€ํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ํšจ๊ณผ์ ์ธ ์†”๋ฃจ์…˜์„ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์„ ํˆฌ์žํ•  ์ค€๋น„๋ฅผ ํ•˜์‹ญ์‹œ์˜ค. ๋ชฉํ‘œ๋Š” ๋‹ฌ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ €๋ฅผ ๋ฏฟ์œผ์„ธ์š”. ๊ทธ๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ ์†๋„, ์œ ์—ฐ์„ฑ, ์ƒˆ๋กœ์šด ํ”„๋กœ์„ธ์Šค ์ถ”๊ฐ€ ์šฉ์ด์„ฑ ๋“ฑ์ด ๋งˆ์Œ์— ๋“œ์‹ค ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฌผ๋ก  ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ, Airflow ์ž์ฒด์˜ ์•ˆ์ •์„ฑ์— ๋งŽ์€ ๊ด€์‹ฌ์„ ๊ธฐ์šธ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์ ์€ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด์ œ Airflow๊ฐ€ ๋งค์ผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์•ฝ 6,5๊ฐœ ์ž‘์—…. ๊ทธ๋“ค์€ ์„ฑ๊ฒฉ์ด ์ƒ๋‹นํžˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•˜๊ณ  ๋งค์šฐ ๊ตฌ์ฒด์ ์ธ ์†Œ์Šค์—์„œ ๊ธฐ๋ณธ DWH๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋Š” ์ž‘์—…์ด ์žˆ๊ณ , ๊ธฐ๋ณธ DWH ๋‚ด๋ถ€์˜ ๋งค์žฅ ์ „๋ฉด์„ ๊ณ„์‚ฐํ•˜๋Š” ์ž‘์—…์ด ์žˆ์œผ๋ฉฐ, ๋น ๋ฅธ DWH์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒŒ์‹œํ•˜๋Š” ์ž‘์—…์ด ์žˆ๊ณ , ๋งค์šฐ ๋‹ค์–‘ํ•œ ์ž‘์—…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค์ผ ๋‹ค ์”น์–ด ๋จน์Šต๋‹ˆ๋‹ค. ์ˆซ์ž๋กœ ๋งํ•˜๋ฉด ์ด๋ ‡๋‹ค. 2,3 ์ฒœ๋ช… DWH(Hadoop) ๋‚ด์—์„œ ๋‹ค์–‘ํ•œ ๋ณต์žก์„ฑ์„ ์ง€๋‹Œ ELT ์ž‘์—…. 2,5๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์†Œ์‹ํ†ต์— ๋”ฐ๋ฅด๋ฉด ์—ฌ๊ธฐ๋Š” ํŒ€์ž…๋‹ˆ๋‹ค. 4๋ช…์˜ ETL ๊ฐœ๋ฐœ์ž, ์ด๋Š” DWH์—์„œ์˜ ETL ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ DWH ๋‚ด๋ถ€์˜ ELT ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ํ•œ ๋ช…, ์„œ๋น„์Šค ์ธํ”„๋ผ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ์˜ ๊ณ„ํš

ํ”„๋กœ์„ธ์Šค ์ˆ˜๋Š” ํ•„์—ฐ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ Airflow ์ธํ”„๋ผ ์ธก๋ฉด์—์„œ ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ฃผ์š” ์ž‘์—…์€ ํ™•์žฅ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Airflow ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ , Celery ์ž‘์—…์ž๋ฅผ ์œ„ํ•œ ํ•œ ์Œ์˜ ๋‹ค๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ , ์ž‘์—… ์ผ์ • ํ”„๋กœ์„ธ์Šค์™€ ์ €์žฅ์†Œ๋ฅผ ๊ฐ–์ถ˜ ์ž์ฒด ๋ณต์ œ ํ—ค๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ปคํŠผ์ฝœ

๋ฌผ๋ก  ์ด๊ฒƒ์ด Airflow์— ๋Œ€ํ•ด ๋งํ•˜๊ณ  ์‹ถ์€ ์ „๋ถ€๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ฃผ์š” ์‚ฌํ•ญ์„ ๊ฐ•์กฐํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. ๋จน์œผ๋ฉด ์‹์š•์ด ๋”ฐ๋ผ์˜ค๋Š”๋ฐ ๊ผญ ๋“œ์…”๋ณด์‹œ๋ฉด ์ข‹์•„ํ•˜์‹ค ๊ฒƒ ๊ฐ™์•„์š” :)

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€