이전 포스팅에서는 아파치 스파크의 역사와 철학, 설치 방법에 대해서 알아보았다.
이번 포스팅에서는 스파크의 기본 배경지식을 알아본 후에 클러스터, 스파크 애플리케이션과 구조적 API를 살펴보면서 스파크의 핵심 용어와 개념을 접하고 사용법을 익혀보자.
스파크의 기본 아키텍처
보통 '컴퓨터'라고 하면 집이나 직장 책상 위에 놓인 장비 한대를 떠올린다.
이 컴퓨터는 영화를 보거나, 문서 작업을 하기에는 아주 적합하지만 한 대의 컴퓨터로는 수행하기 힘든 작업이 존재한다.
특히 데이터를 처리하는 경우를 말하는데, 위에서 말한 컴퓨터는 대규모 정보를 연산할만한 자원이나 성능이 없고, 연산이 가능하다 해도 너무 많은 시간이 소요된다.
때문에 컴퓨터 클러스터라는 기술이 여러 컴퓨터의 자원을 모아 하나의 컴퓨터처럼 사용할 수 있게 만드는데, 클러스터를 구성했더라도 작업을 조율할 수 있는 프레임워크가 따로 필요하다.
여기서 스파크가 작업을 조율하는 프레임워크에 해당하며, 스파크는 클러스터의 데이터 처리 작업을 관리하고 조율한다.
스파크에서 사용하는 클러스터 매니저의 종류는 다음과 같다.
- Spark StandAlone (default)
- Hadoop YARN
- Mesos
- Kubernetes
위의 클러스터 매니저는 스파크 3.0을 기준으로 이전 버전은 Kubernetes를 제외하고 생각하면 될 듯하다.
스파크 애플리케이션
스파크 애플리케이션은 드라이버 프로세스와 다수의 익스큐터 프로세스로 구성된다.
드라이버 프로세스는 클러스터 노드 중 하나에서 실행되며 역할은 다음과 같다.
- main( ) 함수 실행
- 스파크 애플리케이션 정보 유지 관리
- 사용자 프로그램이나 입력에 대한 응답
- 전반적인 익스큐터 프로세스의 작업과 관련된 분석, 배포, 스케줄링 역할 수행
즉, 드라이버 프로세스는 스파크 애플리케이션의 심장과 같은 존재이며 애플리케이션의 수명 주기 동안 관련 정보를 모두 유지한다.
익스큐터 프로세스는 드라이버 프로세스가 할당한 작업을 수행하는 프로세스로 역할은 다음과 같다.
- 드라이버가 할당한 코드 실행
- 진행상황을 드라이버 프로세스에게 보고
즉, 드라이버가 할당한 코드를 실행하고 진행상황을 다시 드라이버 노드에 보고하는 두 가지 역할을 수행한다.
위의 그림은 클러스터 매니저가 물리적 머신을 관리하고 스파크 애플리케이션에 자원을 할당하는 방법을 나타낸다.
위의 그림을 통하여 애플리케이션의 실행 과정을 설명하면 다음과 같다.
- 사용자가 spark-submit을 통해 애플리케이션을 제출한다.(spark-shell은 자동으로 submit 제출됨)
- spark driver가 main( )을 실행하며 SparkSession을 생성한다.
- SparkSession이 클러스터 매니저와 연결된다.
- 드라이버 프로세스가 클러스터 매니저로부터 익스큐터 프로세스 실행을 위한 리소스를 요청한다.
- SparkSession은 작업 내용을 task단위로 분할하여 익스큐터 프로세스에게 보낸다.
- 각 익스큐터 프로세스는 작업을 수행하고, 결과를 저장하며 주기적으로 SparkSession에게 전달한다.
익스큐터 프로세스는 4개로 표현하였지만 여러 개가 될 수 있다. 즉, 사용자가 익스큐터 수를 지정할 수 있다.
또한 스파크는 클러스터와 로컬 모드 둘 다 지원하며, 로컬 모드에서는 드라이버와 익스큐터를 스레드 형태로 실행한다.
스파크 애플리케이션을 이해하기 위한 핵심사항은 다음과 같다.
- 스파크는 사용 가능한 자원을 파악하기 위해 클러스터 매니저를 사용한다.
- 드라이버 프로세스는 주어진 작업을 완료하기 위해 드라이버 프로그램의 명령을 익스큐터에서 실행할 책임이 있다.
스파크의 다양한 언어 API
스파크의 언어 API를 이용하면 다양한 프로그래밍 언어(스칼라, 자바, 파이썬, SQL, R)로 스파크 코드를 실행할 수 있다.
- 스칼라
스파크는 스칼라로 개발되어 스칼라가 스파크의 '기본'언어이다. - 자바
스파크는 스칼라로 개발되었지만 스파크의 창시자들은 자바를 이용해 스파크 코드를 작성할 수 있도록 심혈을 기울였다. - 파이썬
파이썬은 스칼라가 지원하는 거의 모든 구조를 지원한다. - SQL
스파크는 ANSI SQL:2003 표준 중 일부를 지원한다. - R
스파크에는 일반적으로 사용하는 두 개의 R 라이브러리(SparkR, sparklyr)가 있다.
스파크는 위의 언어로 작성한 코드를 익스큐터의 JVM에서 실행할 수 있는 코드로 변환한다.
이때 스파크 코드를 실행하기 위해 SparkSession 객체를 진입점으로 사용할 수 있으며 이를 그림으로 표현하면 다음과 같다.
스파크 시작하기
스파크 애플리케이션 실행하는 방법은 대화형 쉘과 애플리케이션 제출로 2가지 방법이 있다.
대화혈 쉘은 자동으로 SparkSession이 생성되지만 spark-submit은 SparkSession코드를 직접 작성을 해야 한다.
1. SparkSession
대화형 쉘에서 SparkSession을 확인하기 위해서는 spark를 입력하면 된다.
스칼라 콘솔에서는 위의 결과에서와 같이 res0:...라는 결과를 출력한다.
위와 같이 DataFrame을 생성하는 코드를 입력하면 생성 연산에 대한 응답이 출력되는 것을 알 수 있다.
(출력 값이 바로 나오지 않는 이유는 조금 더 아래에!)
다음으로 대화형 쉘이 아닌 spark-submit으로 위와 같은 문제를 해결해보자.
spark-submit의 기본 형태와 옵션들을 먼저 살펴보자.
#spark-submit 기본 형태
bin/spark-submit [Options] <App JAR> [App Options]
- Options : spark-submit의 플래그들로 spark-submit --help를 실행하여 확인 가능
- <App JAR> : 사용자 애플리케이션의 진입점이 될 JAR
- [APP Options] : 사용자 애플리케이션에 전달될 값들로, main()에서 인자 값에 해당
spark-submit Options 종류
spark-submit은 임의의 SparkConf의 설정을 --conf 속성=값이나, 각 속성의 키와 값을 포함한 파일을 --properties-file을 써서 지정해 주는 식으로 전달이 가능하다.
spark-submit --master에 가능한 값
다음은 spark-submit에 대한 예시이다.
./spark-submit --master local ../examples/src/main/python/pi.py 10
위와 같은 연산 결과를 입력하였을 때 나온 결과는 다음과 같다.
위의 예제는 pi값을 계산하는 예제로 10번 반복하라는 인자 값을 전달하였다.
2. DataFrame
가장 대표적인 구조적 API로 테이블의 데이터를 로우와 컬럼으로 단순하게 표현한다.
스파크의 DataFrame은 수천 대의 컴퓨터에 분산되어 저장되는데 그 이유는 한 대의 컴퓨터에 저장하기에는 데이터가 너무 크거나 계산이 너무 오래 걸릴 수 있기 때문이다.
3. 파티션
스파크는 모든 익스큐터가 병렬로 작업을 수행할 수 있도록 파티션이라고 불리는 chunk(청크) 단위로 데이터를 분할한다.
파티션은 클러스터의 물리적 머신에 존재하는 로우의 집합을 의미하며 DataFrame의 파티션은 실행 중에 데이터가 컴퓨터가 클러스터에 물리적으로 분산되는 방식을 나타낸다.
4. 트랜스포메이션
스파크의 핵심 데이터 구조는 불변성(immutable)을 가지는 것이다.
즉, 한번 생성하면 변경할 수 없다는 것인데, 그렇다면 어떻게 사용할 수 있을까?
데이터를 변경하는 방법은 원하는 변경 방법을 스파크에게 알려줘야 한다.
이때 사용하는 명령을 트랜스포메이션이라고 한다.
예를 들어, 위에서 생성하였던 DataFrame에서 짝수만 찾는 명령을 수행해보자.
val divisBy2 = myRange.where("number % 2 = 0")
결과를 보면 응답 값만 출력될 뿐 결과는 출력되지 않는다.
이는 추상적인 트랜스포메이션만 지정한 상태이기 때문에 액션 연산을 호출하지 않으면 실제 연산을 수행하지 않는다.
스파크에서 트랜스포메이션은 비즈니스 로직을 표현하는 핵심 개념이며 좁은 의존성과 넓은 의존성의 두 가지 유형을 갖는다.
좁은 의존성은 각 입력 파티션이 하나의 출력 파티션에만 영향을 미치는 것으로 이전에 했던 짝수 연산을 예로 들 수 있다.
넓은 의존성은 하나의 입력 파티션이 여러 출력 파이션에 영향을 미치는 것으로 이전해했던 짝수 연산에 정렬 연산까지 수행한다면 넓은 의존성이라고 할 수 있다.
5. 지연 연산
스파크가 연산 그래프를 처리하기 직전까지 기다리는 동장 방식을 의미한다.
스파크는 연산 명령이 들어왔을 때 즉시 처리하지 않고 트랜스포메이션의 실행 계획을 생성하는데 마지막까지 대기하다가 원형 DataFrame 트랜스포메이션을 간결한 물리적 실행 계획으로 컴파일한다.
스파크는 이 과정을 거치며 전체 데이터 흐름을 최적화하는 엄청난 강점을 가진다.
6. 액션
트랜스포메이션을 사용해 논리적 실행 계획을 세우고 실제 연산을 수행하려면 액션 명령을 내려야 한다.
액션은 일련의 트랜스포메이션으로부터 결과를 계산하도록 지시하는 명령이다.
앞서 실행한 트랜스포메이션의 개수를 출력하도록 count 연산을 수행해보자.
divisBy2.count()
위의 코드는 500을 출력한다. 이처럼 실제로 연산 수행 결과를 확인하도록 명령을 내리는 것을 액션 연산이라고 한다.
count 이외에도 다음 세 가지 유형의 액션이 있다.
- 콘솔에서 데이터를 보는 액션
- 각 언어로 된 네이티브 객체에 데이터를 모으는 액션
- 출력 데이터 소스에 저장하는 액션
액션을 지정하면 스파크 잡(job)이 시작되며 이는 스파크가 제공하는 스파크 UI로 확인이 가능하다.
7. 스파크 UI
스파크 UI는 드라이버 노드의 4040 포트로 접속이 가능하다.
로컬 모드에서 스파크를 실행했다면 스파크 UI 주소는 http://localhost:4040이 된다.
스파크 UI에서는 스파크 잡의 상태, 환경 설정, 클러스터 상태 등의 정보를 확인할 수 있으며 스파크 잡을 튜닝하고 디버깅할 때 매우 유용하다.
'SEMINAR > 스파크 완벽 가이드' 카테고리의 다른 글
2-1. Spark의 저수준 API (0) | 2021.07.23 |
---|---|
1-1. Apache Spark란 (0) | 2021.07.07 |