해당 글에서는 spark를 관리/운영해보면서 자세히 알지 못했던 부분, 그리고 경험적으로 알게된 내용들을 정리하고 공유하는데 목적이 있습니다. 따라서, spark의 기본적인 개념들을 보다 쉬운 언어로 설명하고, 동작 원리를 직관적으로 정리할 예정입니다.
Spark 개념 및 아키텍쳐
Spark 배경
우선, spark가 왜 탄생하게 되었는지를 한마디로 설명하면, "Hadoop의 mapreduce의 한계를 극복하기 위해"입니다. hadoop의 M/R(MapReduce)는 대량의 데이터를 처리할 수 있게 한다는 것에서 큰 의미가 있지만, 실제 execute engine으로 M/R을 사용해보신 분 들은 아시겠지만 Map과 Reduce 사이의 중간 데이터를 HDFS에 Write 후에 Reduce 과정에서 다시 Read를 하기 때문에 DISK I/O가 발생 즉, MR의 성능의 한계를 Spark는 RDD와 in-memory cache라는 특징을 통해 MR의 한계를 개선합니다.
(MapReduce 처리 과정 on Disk)
위의 그림처럼 MR은 각 Iteration(Stage) 사이마다 생성되는 중간 데이터를 HDFS(on Disk)에 Write/Read 과정을 반복하며, 많은 Disk I/O가 일어납니다. 이로 연산 과정으로 인한 시간보다는 Disk I/O에서 대부분의 시간을 잡아먹습니다.
(Hive on MR( execute engine))
위의 그림은 Hive에서 실행 엔진을 mapreduce로 했을 경우의 처리 과정입니다. 각 Query는 HDFS(On disk)에서 각각 읽어오고, 각 Query의 연산은 "MapReduce의 처리 과정" 처럼 Map/Reduce의 중간 데이터를 Disk 에 R/W합니다.
(Spark on in-memory)
반면, Spark는 연산 과정에서 MR과 다르게 중간 작업의 결과를 Disk(Spill이 일어나면 Disk에 저장하는 경우가 생길 수 있습니다.)가 아닌, 분산된 메모리에 저장합니다. 또한 동시에 여러 Query가 다양하게 들어오더라도, RDD데이터는 메모리에 로드되어있다면 disk I/O보다 빠르게 응답할 수 있습니다. 단, 이는 RDD는 action 요청이 들어 올때마다 다시 계산을 하기 때문에 RDD를 메모리에 persist 한다는 경우에만 해당된다. (자세한 내용은 다음글의 Transformation과 Action 과정에서 설명)
Spark Architecture
(Spark Architecture)
동작 방식을 설명하기 전에 각 프로세스에 대해서 간략하게 설명이 필요합니다.
- Driver
- spark의 드라이버는 말 그대로 spark app의 중앙 처리자, 혹은 관리자라고 생각하면 편합니다. 우선, Driver 프로세스 안에 spark context라는 클래스 객체를 생성하고, 해당 객체는 app의 실행을 담당합니다.
- app의 실행은 제출된 코드를 기반으로 job의 순서(DAG 형태)나뉘고, 해당 job은 Stage 단위로 나뉘며, 각 Stage는 작은 task 단위로 생성됩니다. (spark history web ui에서 확인 가능)
- 즉, Driver는 제출된 코드를 DAG라는 논리적 Plan을 짜고, 최적화된 stage, task을 계획하게 된다.
- 위의 그림에서는 Driver 프로세스 안에, spark session이라고 적혀있는데, spark session안에 spark context, SharedState, SessionState, SparkSessionExetensions와 같은 객체들이 포함되어 있다고 생각하면 되고, spark session은 하위에 4개의 객체들이 생성이 된 후에 생성이 가능합니다.
- 그렇다면 spark session안에 State와 같은 상태 객체들이 포함된 이유 설명이 필요한데, spark context 객체는 statusTracker, dagScheduler, taskScheduler(위에서 설명한 것과 같이 plan을 계획하는 객체들)과 같은 백엔드 쪽의 서비스들에 접근 및 참조를 해야 하는데, 이 과정에서 State(상태) 객체들이 필요해 해당 State 객체들이 포함되어 있다고 생각하시면 됩니다.
- 또한, driver(정확히는 spark context)는 데이터 저장 위치(위의 그림에서 partition은 RDD의 데이터 단위이다.), 로깅, 어플리케이션 상태, 스케쥴링 정보 등의 메타데이터를 가지고 있습니다.
- 추가적으로, Driver는 SparkContext 객체를 통해 스파크에 접속하는데, shell에서 이 SparkContext 객체는 자동적으로 sc라는 변수에 만들어집니다. (spark-shell에서 sc 변수를 확인해보면 SparkContext 객체가 확인 가능합니다)
- 실제 spark-shell은 접근과 동시에 spark session이 생성이 되는데, 해당 변수를 확인해보면 아래와 같이 sparkcontext가 생성 된 것을 확인할 수 있다.
Using Python version 3.6.6 (default, Oct 9 2018 12:34:16)
SparkSession available as 'spark'.
>>> sc
<SparkContext master=yarn appName=PySparkShell>
- Cluster Manager
- driver는 실제 task를 실행할 executor(worker node)의 가용한 자원을 Cluster Manager에 요청하고, Cluser Manager (ex YARN)는 요청받은 리소스를 할당해줍니다.
- Cluster Manager는 executor의 자원 사용, 성능, job의 종료 후, Driver에게 알립니다.
- 또한, 작업 도중 worker node가 Task를 실패하게 되면, 같은 Task를 다른 노드에 재할당해서 작업을 이어갑니다.
- Executor
- Executor는 실제로 데이터에 접근할 수 있고, 할당된 Task를 수행할수 있는 Worker이며, Spark job 을 submit 할때 executor 의 수를 지정할 수 있습니다.
- 각 executor는 몇개의 (virtual) core를 사용할지, 얼마의 memory를 사용할지 지정할 수 있습니다.
- executor는 수행할 복수의 task들을 할당받아 수행하고, 수행 결과는 driver에게 전달합니다.
동작 순서
- 우선, Spark Driver는 계획한 plan(stage, task)가 실행될 executor를 cluster manager(yarn)에게 요청합니다.
- 그러면 Cluster manager는 제출된 task를 기준으로 executor(요청된 memery, core가 할당된 container)를 할당해줍니다.
- 이후, 각 executor는 HDFS(Disk)에서 데이터를 읽어와 Task단위로 실행됩니다. 위의 그림("Spark Architecture")처럼 task는 하나의 partition과 mapping되어 처리됩니다. (참고로 각 partition 사이의 데이터 이동을 shuffle이라고 하는데 해당 내용은 다음글에서 설명)
- 이 과정에서 Cluser Manager는 Driver와 executor 중간에서 상태 및 성능 정보들을 전달해줍니다.
- 각 executor의 tasks들이 마무리되면, 해당 내용들을 driver에게 전달하게 되고 driver는 해당 정보를 종합해 client에게 제공하게 되면 App은 종료됩니다.
이번 글에서는 Spark의 개념 및 아키텍쳐에 대해서 설명을 진행했습니다. 다음 글에서는 Spark의 좀 더 세부적인 동작 과정을 설명하기 위한 글을 정리할 예정입니다.
'Bigdata Components > SPARK' 카테고리의 다른 글
[SPARK] spark(pyspark)-shell 환경 변수 변경하기 (1) | 2024.01.10 |
---|---|
[SPARK] java.io.FileNotFoundException: {file_name}.conf ERROR 해결 방법 (0) | 2024.01.08 |
[SPARK] spark job과 partition의 개념 (1) | 2024.01.06 |
[SPARK] Spark의 Execuotr memory 구조 (0) | 2024.01.06 |
[SPARK] Spark 세부 동작(Transformation, Action) (0) | 2024.01.06 |