본문 바로가기
Bigdata Components/SPARK

[SPARK] Spark 개념 정리 및 Architecture

by Blue____ 2024. 1. 6.

해당 글에서는 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에게 전달합니다.

 

동작 순서

  1. 우선, Spark Driver는 계획한 plan(stage, task)가 실행될 executor를 cluster manager(yarn)에게 요청합니다. 
  2. 그러면 Cluster manager는 제출된 task를 기준으로 executor(요청된 memery, core가 할당된 container)를 할당해줍니다. 
  3. 이후, 각 executor는 HDFS(Disk)에서 데이터를 읽어와 Task단위로 실행됩니다. 위의 그림("Spark Architecture")처럼 task는 하나의 partition과 mapping되어 처리됩니다. (참고로 각 partition 사이의 데이터 이동을 shuffle이라고 하는데 해당 내용은 다음글에서 설명)
  4. 이 과정에서 Cluser Manager는 Driver와 executor 중간에서 상태 및 성능 정보들을 전달해줍니다. 
  5. 각 executor의 tasks들이 마무리되면, 해당 내용들을 driver에게 전달하게 되고 driver는 해당 정보를 종합해 client에게 제공하게 되면 App은 종료됩니다. 

 

이번 글에서는 Spark의 개념 및 아키텍쳐에 대해서 설명을 진행했습니다. 다음 글에서는 Spark의 좀 더 세부적인 동작 과정을 설명하기 위한 글을 정리할 예정입니다.