[스파크 완벽 가이드] 18. 모니터링과 디버깅
18.1 모니터링 범위
- 스파크 잡의 어느 지점에서 오류가 발생했는지 파악하려면 스파크 잡을 모니터링해야 한다.
- 실제 모니터링 대상과 모니터링에 필요한 옵션을 알아야 한다.
스파크 애플리케이션과 잡
- 클러스터에서 사용자 애플리케이션이 실행되는 상황을 파악하거나 디버깅하려면 먼저 스파크 UI와 스파크 로그를 확인해야 한다.
- 스파크 UI와 스파크 로그는 실행중인 앱의 RDD와 쿼리 실행 계획 같은 개념적 수준의 정보를 제공한다.
JVM
- 스파크는 모든 익스큐터를 개별 자바 가상 머신(JVM)에서 실행한다. 따라서 코드가 실행되는 과정을 이해하기 위해 JVM을 모니터링해야 한다.
- JVM 도구에는 스택 트레이스를 제공하는 jstack, heap dump를 생성하는 jmap, 시계열 통계 리포트를 제공하는 jstat, 다양한 JVM 속성 변수를 시각화된 방식으로 탐색할수 있는 jconsole 등이 있다.
- jvisualvm을 이용해 스파크 잡의 동작 특성을 알아볼수 있다.
OS와 머신
- JVM은 OS에서 실행되는데, 머신의 상태를 모니터링해 정상 작동 중인지 확인하는것이 매우 중요하다.
- CPU, 네트워크, I/O 등의 자원에 대한 모니터링도 함께 해야 한다.
- dstat, iostat, iotop 같은 OS 명령어를 통해 세밀하게 모니터링 할수 있다.
클러스터
- 스파크 앱이 실행되는 클러스터도 모니터링해야 한다.
- YARN, 메소스, 스탠드얼론 클러스터 매니저가 모니터링 대상이다.
- 모니터링 솔루션을 활용하면 클러스터가 동작하지 않는 상황을 빠르게 알 수 있다.
- 클러스터 모니터링 도구로 Ganglia, Prometheus가 있다.
18.2 모니터링 대대상
- 실행 중인 사용자 애플리케이션의 프로세스(CPU, 메모리 사용률 등)
- 프로세스 내부에서의 쿼리 실행 과정(ex. Job, Task)
18.2.1 드라이버와 익스큐터 프로세스
- 스파크 앱을 모니터링할 때는 스파크 드라이버를 유심히 관찰해야 한다.
- 드라이버에는 모든 앱의 상태가 보관되어 있으며, 안정적으로 실행중인지 확인할 수 있다.
- 스파크는 수월한 모니터링을 지원하기 위해 Dropwizard Metrics Library 기반의 metric system 을 갖추고 있다.
$SPARK_HOME/conf/metric.properties
파일을 구성하여 클러스터 모니터링 솔루션을 포함해 다양한 시스템으로 내보낼 수 있다.
18.2.2 쿼리, 잡, 스테이지, 태스크
- 특정 쿼리에서 무슨 일이 일어나고 있는지 알아야 할 때도 있다.
- 스파크의 쿼리, 잡 스테이지, 태스크의 각각의 정보를 확인할 수 있다.
- 이러한 정보는 클러스터에서 특정 시점에 실행되는 작업을 파악할 수 있으며, 성능 개선이나 디버깅 시 매우 유용하다.
18.3 스파크 로그
- 스파크 앱의 로그나 스파크 자체의 로그에서 발견된 이상한 이벤트는 잡의 실패 지점이나 원인을 파악하는 데 큰 도움이 됩니다.
- 템플릿에 설정된 로깅 프레임워크를 통해 스파크 로그 및 앱 로그를 모두 확인할 수 있다.
- 파이썬에서는 자바 기반 로깅 라이브러리를 사용할 수 없으므로 파이썬의 logging 모듈 혹은 print 구문을 사용해 표준 오류를 결과를 출력해야 한다.
18.4 스파크 UI
- 스파크UI를 통해 실행중인 앱과 스파크 워크로드에 대한 평가지표를 모니터링 할 수 있다.
- 모든 SparkContext는 실행 시 앱과 관련된 유용한 정보를 제공하는 웹 UI를 4040포트로 기본 실행된다.
스파크 UI에서의 모니터링 항목
- Job : 스파크 잡에 대한 정보 제공
- Stages : 개별 스테이지(스테이지의 태스크를 포함) 관련 정보 제공
- Storage : 스파크 앱에 캐싱된 정보와 데이터 정보 제공
- Environment : 스파크 앱의 구성과 설정 관련 정보 제공
- Executors : 앱에서 사용중인 익스큐터의 상세 정보 제공
- SQL : SQL과 Dataframe을 포함한 구조적 API 쿼리 제공
예제
1
2
3
4
5
6
7
8
spark.read\
.option("header", "true")\
.csv("/data/retail-data/all/online-retail-dataset.csv")\
.repartition(2)\
.selectExpr("instr(Description, 'GLASS') >= 1 as is_glass")\
.groupBy("is_glass")\
.count()\
.collect()
쿼리의 요약 통계 정보
스테이지 1
- CSV 파일을 모두 스캔하는 스테이지
- 하단의 박스 영역은 파티션 재분배로 인해 발생하는 셔플 스테이지를 나타낸다.(파티션 확정은 아니고 일단 2개로 분할)
스테이지 2
- 프로젝션(컬럼 선택/추가/필터링)과 집계를 수행
- 데이터를 셔플하기 전에 파티션별로 집계를 수행하고, 위 예제에서는 해시 기반의 집계를 수행한다.
- 파티션별 집계 결과 수와 파티션 수를 곱해 결과로 반환되는 로우수를 계싼할 수 있다.
스테이지3
- 이전 스테이지의 결과에 대한 집계를 수행.
Jobs 탭에서 확인할 수 있는 내용
- 잡에서는 3개의 스테이지로 나뉘어져 있다.(SQL 탭과 일치)
- Description의 링크 중 하나를 클릭하면 스테이지에 대한 자세한 내용을 확인할 수 있다.
3개의 스테이지로 구성되어 있으며, 각각 8,2, 200개의 태스크를 실행했다.
- 스테이지1에서 8개의 태스크를 가진다.
- CSV파일은 분할 가능하기 때문에 스파크는 작업을 나눌 수 있다.
- 작업을 머신의 여러 코어에 비교적 고르게 배분
- 스테이지2에서 2개의 파티션으로 나누기 위해 repartition을 수행했으므로 2개의 태스크를 가진다.
- 마지막 스테이지에서 셔플 파티션의 기본값은 200이므로 200개의 태스크를 가진다.
스파크 태스크 정보
- Summary Metrics 부부분에서 다양한 메트릭 관한 요약 통계를 제공한다.
- 균일하지 않게 분산된 값을 눈여겨보아야 한다.
스파크 UI 설정하기
- 다양한 속성을 사용해 스파크 UI를 설정할 수 있다.
18.4.1 스파크 REST API
- 스파크 UI 외에도 REST API로 스파크의 상태와 매트릭을 확인할 수 있다.
http://localhost:4040/api/v1
으로 API를 처리할 수 있다.
18.4.2 스파크 UI 히스토리 서버
- 스파크 UI는 SparkContenxt가 실행되는 동안 사용할 수 있다.
- 정상적으로 종료되거나 비정상적으로 종료된 앱의 정보를 확인하려면 스파크 히스토리 서버를 이용해야 한다.
- 이벤트 로그를 저장하도록 스파크 앱을 설정하면 히스토리 서버를 이용해 스파크 UI와 REST API를 재구성할 수 있다.
18.5 디버깅 및 스파크 응급 처치
- 이를 위해 스파크 내부에서 발생하는 문제(ex. Out Of Memory Error 등)와 사용자가 경험할 수 있는 증상(느린 태스크)을 포함해 스파크 잡에서 발생할 수 있는 다양한 문제를 알아보자.
18.5.1 스파크 앱이 시작되지 않는 경우
징후와 증상
- 스파크 잡이 실행되지 않음.
- 스파크 UI가 드라이버 노드를 제외한 클러스터 노드의 정보를 전혀 표시하지 못함.
- 스파크 UI가 잘못된 정보를 표시하는 것 같음.
잠재적 대응법
- 클러스터나 앱의 실행에 필요한 자원을 적절하게 설정하지 않았을 경우 발생.
- IP를 잘못 입력했거나, 포트가 열리지 않은 경우 대부분 클러스터 영역, 머신, 설정과 관련된 문제인 경우가 많음.
- 익스큐터 자원을 클러스터 매니저의 유휴 자원 이상으로 요청하는 경우 드라이버는 익스큐터가 실행될 때까지 무한정 대기한다.
18.5.2 스파크 앱 실행 전에 오류가 발생한 경우
- 새로운 애플리케이션을 개발해 클러스터에서 실행할 때 발생.
징후와 증상
- 명령이 전혀 실행되지 않으며 오류 메시지 출력
- 스파크 UI에서 잡, 스테이지, 태스크 정보를 확인할 수 없음.
잠재적 대응법
- 스파크 UI의 Environment 탭에서 앱의 정보가 올바른지 확인하고, 코드를 검토한다.
- 단순 오타나 잘못된 컬럼명을 사용하는 경우 스파크 실행계획을 만드는 과정에서 오류가 발생할 수 있다.
18.5.3 스파크 앱 실행 중에 오류가 발생한 경우
징후와 증상
- 하나의 스파크 잡이 전체 클러스터에서 성공적으로 실행되지만 다음 잡은 실패
- 여러 단계로 처리되는 쿼리의 특정 단계가 실패
- 어제 정상 동작한 예약 작업이 오늘 실패
- 오류 메시지를 해석하기 어려운 경우
잠재적 대응법
- 데이터가 존재하는지, 데이터가 올바른 포맷인지 확인
- 쿼리 실행 즉시 오류가 발생한다면 실행 계획을 만드는 단게에서 발생해 분석 오류일 가능성이 높다. 쿼리 자체에 컬럼명, 뷰, 테이블 존재 여부를 확인.
- 어떤 컴포넌트가 연관되어 있는지 알아내기 위헤ㅐ 스택 트레이스를 분석해 단서를 찾아야 한다.
- 입력 데이터와 데이터 포맷을 확인해본다.
- 잡의 태스크가 잠시 실행되다가 비정상적으로 종료된다면 입력 데이터 자체의 문제일 수 있다.(null값 존재 유무 체크)
18.5.4 느리거나 뒤처진 태스크
- 앱 최적화할떄 매우 흔하게 발생.
- 머신간의 작업이 균등하게 분배되지 않거나(데이터 치우침) 특정 머신이 다른 머신에 비해 처리 속도가 느린 경우(하드웨어 문제)에도 발생.
징후와 증상
- 스파크 스테이지에서 대부분의 태스크가 정상적으로 실행되었으며 소수의 태스크만 남아 있는 경우(오래 실행되는 경우)
- 스파크 UI에서 첫번쨰 증상과 같은 태스크가 확인되고, 동일한 데이터셋을 다룰때 항상 발생
- 여러 스테이지에서 번갈아가며 두번쨰 증상과 같은 현상이 발생
- 여러 스테이지에서 번갈아 가며 두번째 증상과 같은 현상이 발생
- 머신 수를 늘려도 상황이 개선되지 않고 여전히 특정 태스크가 오래 걸리는 경우
- 매트릭을 보면 특정 익스큐터가 다른 익스큐터에 비해 훨씬 많은 데이터를 읽거나 쓰고 있는 경우
잠재적 대응법
느린 태스크를 낙오자(straggler)라 부르는데, DataFrame이나 RDD 파티션에 데이터가 균등하게 분할되지 않는 경우에 주로 발생. 다양한 원인으로 낙오자가 발생하므로 디버깅하기 가장 어려운 문제 중 하나이다. 대부분의 경우 데이터 치우침 현상으로 인해 발생하므로 이를 우선 확인해보면 좋다.
- 파티션별 데이터양을 줄이기 위해 파티션 수를 증가시킨다.
- 다른 컬럼을 조합해 파티션을 재분배한다.
- 가능하다면 익스큐터의 메모리를 증가시킨다.
- 익스큐터에 문제가 있는지 모니터링하고, 문제가 있다면 해당 머신의 다른 잡에서도 동일한 문제가 있는지 확인
- 사용자 정의함수를 구현할 때 객체 할당이나 비즈니스 로직에 쓸모없는 부분이 있는지 확인하고 DataFrame 코드로 변환
18.5.5 느린 집계 속도
데이터 치우침이 있는지 먼저 확인하고 그럼에도 발생할 경우 추가 확인
징후와 증상
- groupBy 호출시 느린 태스크가 발생
- 집계 처리 이후의 잡도 느린 경우
잠재적 대응법
- 집계 연산 전에 파티션 수를 증가시키면 태스크별로 처리할 키 수를 줄일 수 있다.
- 익스큐터의 메모리를 증가시킨 경우 많은 키를 처리하는 익스큐터는 다른 키를 처리하는 익스큐터에 비해 느릴수 있지만 개선의 여지가 있다.
- 집계 처리가 끝나고 이어서 실행되는 태스크가 느리다면 집계 처리된 데이터셋에 불균형 현상이 남아 있다는것을 의미하는데, 이 경우 파티션을 임의로 재부분핼사 웄디록 repartition을 수행한다.
- 모든 필터와 SELECT 구문이 집계 연산보다 먼저 처리된다면 필요한 데이터만을 이용해서 집계를 수행할 수 있다.
- null 값을 나타내기 위해
" "
또는"EMPTY"
와 같은 값을 대체 값으로 사용하는지 확인- 실제 null로 값이 셋팅되어 있어야 최적화를 수행할 수 있다.
- 일부 집계 함수는 달느 함수에 비해 태생적으로 늘니 경우가 있는데 이는 꼭 필요한 경우가 아니라면 사용을 자제해야 한다.
- collect_list, collect_set은 모든 객체를 드라이버로 전송하기 때문에 아주 느리게 동작한다.
18.5.6 느린 조인 속도
조인과 집계는 모두 셔플을 유발하기 때문에 동일한 증상과 대응법을 가진다.
징후와 증상
- 조인 스테이지의 처리 시간이 오래 걸리는 경우
- 조인 전후의 스테이지는 정상적으로 동작
잠재적 대응법
- 많은 조인 연산은 다른 조인 타입으로 변경해 최적회(자동 또는 수동)하낟.
- 조인 순서를 변경하면서 잡의 처리 속다가 올라가는지 테스트
- 조인을 수행하기 전에 데이터셋을 분할하면 클러스터 노드간 데이터 이동을 줄일 수 있다.
- 데이터 치우침 현상은 느린 조인을 유발할 수 있으므로 익스큐터의 자원 할당량을 늘리면 동무이 될 수 있다.
- 나머지는 집계쪽과 동일한 대응방법을 사용한다.
18.5.7 느린 읽기와 쓰기 속도
느린 I/O는 진단이 어려울 수 있다.
징후와 증상
- 분산 파일 시스템이나 외부 시스템의 데이터를 읽는 속도가 느린 경우
- 네트워크 파일 시스템이나 blob 저장소에 데이터를 쓰는 속도가 느린 경우
잠재적 대응법
- 스파크의 투기적 실행(spark.speculation 속성 = true)을 사용하면 느린 읽기와 쓰기 속도를 개선하는데 도움이 될 수 있다.
- 일관성을 보장하는 파일 시스템과 함께 사용하는것이 좋다.
- aws s3와 같이 eventually consistency 방식을 사용하는 저장소에서는 데이터 중복이 발생할 수 있다.
- 스파크 클러스터와 저장소 시스템 간의 네트워크 대역폭이 충분하지 않을 수 있으므로 네트워크 성능 문제가 없는지 반드시 확인
- 단일 클러스터에서 스파크 HDFS 같은 분산파일 시스템을 함께 구성하려면 클러스터의 노드마다 스파크의 분산 파일 시스템 모두를 동일한 호스트명을 인식하는지 확인한디.
- 스파크의 지역성을 고려해 스케줄링하며, 스파크 UI의 locality 컬럼에서 지역성 정보를 확인
18.5.8 드라이버 OutOfmemoryError 또는 응답 없음
드라이버에 너무 많은 데이터를 전송해 메모리를 모두 소비한 경우 자주 발생.
징후와 증상
- 스파크 앱이 응답하지 않거나 비정상적으로 종료
- 드라이버 로그에 OOM 또는 GC 관련된 메시지가 출력
- 명령이 장시간 실행되거나 실행되지 않는 경우
- 반응이 거의 없는 경우
- 드라이버 JVM 메모리 사용량이 높은 경우
잠재적 대응법
- 사용자 코드에서 collect 메서드 같은 연산을 실행해 너무 큰 데이터셋을 드라이버로 전송하려고 시도했는지 확인
- 브로드캐스트하기에 너무 큰 데이터를 브로드캐스트 조인했는지 확인
- 장시간 실행되는 앱은 드라이버에 많은 양의 객체를 생성하고 해제하지 못할 수 있다.
- JVM 힙 덤프 등을 통해 확인해볼 필요가 있다.
- 가능하다면 드라이버의 가용 메모리를 늘린다.
- JVM 메모리 부족은 파이썬과 같은 다른 언어를 함꼐 사용하는 경우 발생할 가능성이 높음.
- 두 언어간의 데이터 변환 과정에서 과도한 메모리가 사용될 수 있다.
- SQL JDBC 서버와 노트북 환경을 이용해 다른 사용자와 SparkContext를 공유하는 상황이라면 동시에 대량의 데이터를 드라이버 메모리로 전송할 수 있는 명령을 실행하지 못하도록 막아야 한다.
18.5.9 익스큐터 OutOfMemoryError 또는 응답 없음
경우에 따라 스파크 앱이 자동으로 복구하는 경우도 있음
징후와 증상
- 익스큐터 로그에 OOM 또는 GC 관련 메시지가 출력되며 스파크 UI에서도 확인할 수 있다.
- 익스큐터가 비정상적으로 종료되거나 응답하지 않는 경우
- 특정 노드의 느린 태스크가 복구되지 않는 경우
잠재적 대응법
- 익스큐터의 가용 메모리와 익스큐터 수를 늘린다.
- 관련 파이썬 설정을 변경해 pyspark 워커의 크기를 증가시킨다.
- GC가 발생했다면 사용자정의함수 등에서 문제가 있는지 확인하고, 데이터 파티션을 재분배하여 병렬성을 높이는 방법을 고민해봐야 한다.
- null 값을 정확히 제어하기 위해
" "
또는 “EMPTY” 같은 값을 사용한것은 아닌지 체크 - RDD와 Dataset은 객체를 생성하므로 문제가 발생할 가능성이 더 크다. 가급적 사용자 정의 함수 사용을 지양해야 한다.
- 힘 덤프를 통해 익스큐터의 상태를 확인해야 한다.
- 키-값 저장소 같이 다른 워크로드를 처리하는 노드에 익스큐터가 위치한다면 스파크 잡을 다른 작업과 분리해야 한다.
18.5.10 의도하지 않은 null 값이 있는 결과 데이터
징후와 증상
- 트랜스포메이션이 실행된 결과에 의도치 않은 null값이 발생
- 잘 동작하던 운영환경의 예약 작업이 더 동작하지 않거나 정확한 결과를 생성하지 못하는 경우
잠재적 대응법
- 비즈니스 로직을 변경하지 않은 경우 데이터 포맷 변경여부 확인(이전에 잘 동작했다면)
- 어큐뮬레이터를 사용해 레코드나 특정 데이터 타입의 수를 확인(비정상 레코드를 확인할 수 있음)
- 트랜스포메이션이 실제 유효한 쿼리 실행 계획을 생성하는지 확인.
- 스파크 SQL에서 암시적 형변환을 수행하는 경우 의도치 않은 값이 나올 수 있다.
5 * "23"
=> 113,5 * " "
=> null
18.5. 11 디스크 공간 없음 오류
징후와 증상
no space left on disk
오류 메시지와 함꼐 잡이 실패
잠재적 대응법
- 더 많은 디스크 공간을 확보
- 제한된 용량의 저장소를 가진 클러스터인 경우 데이터 치우침 현상으로 인해 문제가 생길수 있음.
- 데이터 파티션을 재분배하여 해결
- 몇가지 저장소 설정을 실험해보고 원인 파악
- 문제가 되는 머신의 오래된 로그 파일과 셔플 파일을 수동으로 제거(단기적 문제 해결방법)
18.5.12 직렬화 오류
징후와 증상
- 직렬화 오류와 함꼐 잡이 실패
잠재적 대응법
- 구조적 API를 사용하는 경우 보통 에러가 나타나지 않는다. (이를 사용하는 방향으로 변경)
- UDF, RDD을 이용하는 경우 문제가 발생하는 경우 있음.
- 자바나 스칼라 클래스에서 UDF를 생성할 때는 enclosing object의 필드를 참조하면 안된다.
- enclosing object : 내부 클래스를 감싸고 있는 외부 클래스의 객체 인스턴스
- 스파크는 전체 인클로징 객체를 직렬화하려고 시도하지만 불가능할 수 있다.
- 관련 필드를 클로저와 동일한 범주의 로컬 변수로 복사하고 사용해야 한다.
Comments powered by Disqus.