Apache Spark — 느린 잡의 답은 실행 계획과 이벤트 로그에 있다 · 드라이버와 잡 · 实验
첫 잡을 띄우고 이벤트 로그로 확인한다 — 변환은 게으르고 행동이 일을 시킨다
목표
local 모드 Spark 로 주문 30만 건을 읽어 세고, 변환만 쌓은 앱과 행동을 부른 앱을 이벤트 로그로 견준다. 잡·스테이지·태스크가 언제 생기고 파티션 수가 무엇으로 정해지는지 숫자로 확인한다.
왜 중요한가
Spark 코드를 처음 읽으면 한 줄 한 줄이 그 자리에서 실행되는 것처럼 보인다. 그렇지 않다. filter·withColumn·select 같은 변환은 계획에 한 줄을 더할 뿐이고, count·take·write 같은 행동을 부를 때 그 계획 전체가 잡으로 바뀌어 실행된다. 이것을 모르면 "읽기가 느리다" 고 보이는 곳이 사실은 앞에서 쌓아 둔 변환 전체가 한꺼번에 도는 자리라는 것을 놓친다.
그 차이는 눈으로 확인할 수 있다. Spark 는 앱이 끝나면 무슨 일을 했는지를 이벤트 로그로 남긴다. 잡이 몇 개 떴는지, 스테이지가 몇 개였는지, 태스크가 몇 개였는지, 어떤 물리 계획을 썼는지가 전부 JSON 한 줄씩 들어 있다. 운영에서 끝난 잡을 되짚을 때 보는 것도 이것이다(히스토리 서버가 이 파일을 읽는다).
local 모드에서는 드라이버 JVM 하나가 곧 실행기다. local[2] 는 코어 둘을 쓴다는 뜻이고, 이 숫자가 기본 병렬도와 파일을 몇 조각으로 나눌지를 함께 정한다.
단계
1. spark-submit --version 의 출력(표준 오류 포함)을 /root/spk/first/version.txt 에 저장하세요.
2. /root/spk/first/count.py 를 만들어 앱 이름 spk-first-count 로 /data/shop/orders.csv(머리줄 있음)의 행 수를 세고 /root/spk/first/out/count.json 에 {"rows": 정수} 로 쓰세요.
3. /root/spk/first/lazy.py 를 만들어 앱 이름 spk-first-lazy 로 스키마를 직접 주어 읽고 filter(또는 where)와 withColumn 을 쌓되 행동은 부르지 마세요. 실행한 뒤 그 앱의 잡이 0개여야 합니다.
4. /root/spk/first/actions.py 를 만들어 앱 이름 spk-first-actions 로 행동을 세 번 이상(예: count·take·write) 부르고, 100줄을 /root/spk/first/out/sample 에 JSON 으로 쓰세요. 그 앱의 이벤트 로그에서 잡 시작 이벤트 수를 세어 /root/spk/first/out/jobs.txt 에 정수 하나로 적으세요.
5. /root/spk/first/agg.py 를 만들어 앱 이름 spk-first-agg 로 status 별 주문 수를 /root/spk/first/out/by_status 에 머리줄 있는 CSV 로 쓰세요. 그 앱에서 완료된 스테이지 수를 /root/spk/first/out/stages.txt 에 정수로 적으세요.
6. /root/spk/first/par.py 를 만들어 첫 인자를 앱 이름으로 받게 하고, --master local[1] 로 spk-first-par1, --master local[2] 로 spk-first-par2 를 돌려 각각 /root/spk/first/out/spk-first-par1.json·/root/spk/first/out/spk-first-par2.json 에 master·default_parallelism·partitions(주문 파일을 읽은 DataFrame 의 파티션 수)를 쓰세요.
7. /root/spk/first/fail.py 를 만들어 앱 이름 spk-first-fail 로 없는 경로 /data/shop/order.csv 를 읽게 하고, 잡은 예외의 오류 조건 이름(getCondition())을 /root/spk/first/out/error.txt 첫 줄에 쓰세요.
8. /root/spk/first/report.md 에 ## 잡과 스테이지 ## 게으른 실행 ## 파티션 세 절로 여러분의 숫자를 적으세요. 첫 절에는 4단계의 잡 수를, 셋째 절에는 6단계의 파티션 수 둘을 숫자로 넣으세요.
참고
- 실행은
spark-submit /root/spk/first/count.py처럼 합니다. 기본 설정은/opt/spark/conf/spark-defaults.conf에 있습니다(local[2], 드라이버 메모리 1g, 이벤트 로그 켜짐). - 이벤트 로그는 앱이 끝나면
/root/spark-events/local-<숫자>로 남습니다. 도는 중에는 이름 끝에.inprogress가 붙습니다. 어느 파일이 어느 앱인지는grep -l '"App Name":"spk-first-actions"' /root/spark-events/*로 찾습니다. - 잡 수는
grep -c '"Event":"SparkListenerJobStart"' <파일>, 스테이지 수는"Event":"SparkListenerStageCompleted"를 셉니다.jq -c 'select(.Event=="SparkListenerJobStart")'로 내용을 볼 수도 있습니다. - 흔한 실수:
spark.stop()을 빼서 로그가.inprogress로 남는 것, 머리줄 있는 CSV 에서header=True를 빼서 머리줄을 한 행으로 세는 것, 스키마 없이 읽어 머리줄을 확인하는 잡이 생기는 것(3단계는 잡이 0개여야 합니다). - 공식 문서: [Cluster Mode Overview](https://spark.apache.org/docs/4.2.0/cluster-overview.html) · [RDD Programming Guide — RDD Operations](https://spark.apache.org/docs/4.2.0/rdd-programming-guide.html#rdd-operations) · [Monitoring — Viewing After the Fact](https://spark.apache.org/docs/4.2.0/monitoring.html#viewing-after-the-fact) · [Submitting Applications — Master URLs](https://spark.apache.org/docs/4.2.0/submitting-applications.html#master-urls)
8个步骤
- 어떤 Spark 인지 확인하기
- 첫 잡 — 30만 건 세기
- 변환만 쌓으면 잡이 뜨지 않는다
- 행동마다 잡이 뜬다 — 이벤트 로그로 세기
- 넓은 변환이 스테이지를 가른다
- 코어 수가 파티션 수를 정한다
- 실패도 앱이다 — 오류 조건 이름 읽기
- 무엇을 봤는지 숫자로 남기기