imhamburger 님의 블로그

스파크 애플리케이션 개념의 이해 본문

스파크(Spark)

스파크 애플리케이션 개념의 이해

imhamburger 2026. 1. 21. 21:26

애플리케이션

API를 써서 스파크 위에서 돌아가는 사용자 프로그램. 드라이버 프로그램과 클러스터의 실행기로 이루어진다.

 

 

SparkSession

스파크 코어 기능들과 상호 작용할 수 있는 진입점 제공. 그 API로 프로그래밍을 할 수 있게 해주는 객체.

스파크 셸에서 스파크 드라이버는 기본적으로 SparkSession을 제공하지만 스파크 애플리케이션에서는 사용자가 SparkSession 객체를 생성해서 써야한다.

 

 

잡(job)

스파크 액션(save(), collect())에 대한 응답으로 생성되는 여러 태스크로 이루어진 병렬 연산

 

 

스테이지(stage)

각 잡은 스테이지라 불리는 서로 의존성을 가지는 다수의 태스크 모음으로 나뉜다.

 

 

태스크(task)

스파크 익그제큐터로 보내지는 작업 실행의 가장 기본적인 단위

 

 

스파크 애플리케이션과 SparkSession

모든 스파크 애플리케이션의 핵심에는 스파크 드라이버 프로그램이 있다. 

이 드라이버는 SparkSession 객체를 만든다.

스파크 셸을 써서 작업할 때 드라이버는 셸에 포함되어 있는 형태이다. 스파크 셸을 노트북에서 로컬로 실행하면 모든 연산 또한 단이 JVM에서 로컬 실행된다.

spark-shell --help 나 pyspark --help 명령을 주면 스파크 클러스터 매니저에 어떻게 연결해야 할지 보여준다.

 

SparkSession 객체를 만들었으면 스파크 연산을 수행하는 API를 써서 프로그래밍이 가능하다.

 

 

스파크 잡

스파크 셸로 상호 작용하는 작업 동안, 드라이버는 스파크 애플리케이션을 하나 이상의 스파크 잡으로 변환한다. 그리고 그 잡은 DAG로 변환된다. 본질적으로 이것이 스파크의 실행계획이 되며 DAG그래프에서 각각의 노드는 하나 이상의 스파크 스테이지에 해당한다.

 

 

스파크 스테이지

어떤 작업이 연속적으로 또는 병렬적으로 수행되는지에 맞춰 스테이지에 해당하는 DAG노드가 생성된다.

모든 스파크 연산이 하나의 스테이지 안에서 실행될 수 없으므로 여러 스테이지로 나뉘어야 한다.

종종 스파크 이그제큐터끼리의 데이터 전송이 이루어지는 연산 범위 경계 위에서 스테이지가 결정되기도 한다.

 

 

스파크 태스크

각 스테이지는 최소 실행 단위이며 스파크 이그제큐터들 위에서 연합 실행되는 스파크 태스크들로 이루어진다.

각 태스크는 개별 CPU 코어에 할당되고 데이터의 개별 파티션을 갖고 작업한다.

16코어 이그제큐터라면 16개 이상의 파티션을 갖는 16개 이상의 태스크를 할당받아 작업하게 되며 이런 식으로 철저한 병렬 처리가 이루어지는 것이다.

 

 

트랜스포메이션, 액션, 지연평가

트랜스포메이션: 이미 불변성의 특징을 가진 원본 데이터를 수정하지 않고 하나의 스파크 데이터 프레임을 새로운 데이터 프레임으로 그 이름처럼 변형한다.

select() 나 filter() 같은 연산은 원본 데이터 프레임을 수정하지 않으며, 대신 새로운 데이터 프레임으로 연산 결과를 만들어 되돌려 준다.

 

모든 트랜스포메이션은 뒤늦게 평가된다. 다시 말해 결과가 즉시 계산되는 게 아니라 계보라 불리는 형태로 기록된다.

기록된 리니지(계보)는 실행 계획에서 후반쯤에 스파크가 확실한 트랜스포메이션들끼리 재배열하거나 합치거나 해서 더 효율적으로 실행할 수 있도록 최적화하도록 한다.

 

지연평가는 액션이 실행되는 시점이나 데이터에 실제 접근하는 시점(디스크에서 읽거나 쓰는 시점)까지 실제 실행을 미루는 스파크의 전략이다.

하나의 액션은 모든 기록된 트랜스포메이션의 지연 연산을 발동시킨다.

지연평가는 스파크가 사용자의 연계된 트랜스포메이션들을 살펴봄으로써 쿼리 최적화를 가능하게 하는 반면, 리니지와 데이터 불변성은 장애에 대한 데이터 내구성을 제공한다.

 

스파크는 리니지에 각 트랜스포메이션을 기록해놓고 데이터 프레임들은 트랜스포메이션을 거치는 동안 변하지 않기 때문에 단순히 기록된 리니지를 재실행하는 것만으로도 원래 상태를 다시 만들어 낼 수 있으며 이 덕분에 장애 상황에도 유연성을 확보할 수 있다.

트랜스포메이션 액션
orderBy() show()
groupBy() take()
filter() count()
select() collect()
join() save()

 

좁은/넓은 트랜스포메이션

트랜스포메이션은 스파크가 지연 평가하는 연산 종류이다.

지연 연산 개념의 큰 이득은 스파크가 연산 쿼리를 분석하고 어디를 최적화할지 알 수 있다는 점이다.

이 최적화는 조인이나 파이프라이닝이 될 수도 있고 연산들을 한 스테이지로 합치거나 반대로 어떤 연산이 셔플이나 클러스터 데이터 교환이 필요한지를 파악해 나누거나 하는 식으로 이루어질 수 있다.

 

트랜스포메이션은 좁은 의존성과 넓은 의존성으로 분류할 수 있다.

하나의 입력 파티션을 연산하여 하나의 결과 파티션을 내놓는 트랜스포메이션은 어느 것이든 좁은 트랜스포메이션이다.

filter()와 contains()는 하나의 파티션을 처리하여 데이터 교환 없이 결과 파티션을 생성해 내므로 좁은 트랜스포메이션이라 할 수 있다.

 

하지만, groupBy()나 orderBy()는 스파크가 넓은 트랜스포메이션을 수행하게 되는데, 다른 파티션으로부터 데이터를 읽어 들여서 합치고 디스크에 쓰는 등의 일을 하기 때문이다.