[Spark] 도커 스웜으로 스파크 클러스터 구축하기!
- -
저번 포스팅에서 도커 스웜을 구축하는 방법에 대해 포스팅했으니,
이번 포스팅에선 도커 스웜을 활용하여 스파크 클러스터를 구축하는 방법에 대해 포스팅하려한다.
아직 스파크 기본 개념과 도커 스웜에 대한 포스팅을 못 본 분들은 여기..
[Spark] 스파크...왜 쓰는걸까? (하둡부터 스파크까지)
지금까지는 전체적인 데이터 파이프라인 작업을 관리하는 Airflow에 대해 알아봤으니,이번엔 작업 들 중 '분산 데이터 처리'의 대명사로 불리는 Spark에 대한 포스팅을 시작해보려한다.당연히 이번
9unu.tistory.com
TODO list
1. 도커 스웜과 스파크 클러스터의 관계 복기
2. 커스텀 스파크 이미지 생성
3. 스파크 클러스터 docker-compose.yml 파일 작성
4. 스택 배포
5. 마무리
1. 도커 스웜과 스파크 클러스터의 관계 복기
일단 본격적으로 구현에 들어가기전에, 잠깐 도커 스웜과 스파크를 복기해보면,
스파크는 기본적으로
'여러 컴퓨터의 RAM을 마치 하나의 RAM처럼 바라보고 연산한다.'
그럼 여러 컴퓨터를 마치 하나의 컴퓨터처럼 연결하는 과정이 필요한데,
도커 스웜은
'여러 컴퓨터에 컨테이너 이미지를 오케스트레이션(분산 배치)하는 툴이다.'
두가지를 합쳐서 생각해보면
도커 스웜으로 스파크 컨테이너 이미지를 여러 컴퓨터에 잘~ 배포하면,
자동으로 스파크 클러스터가 구성된다.
(노드 스케일링은 더 쉬워진다.)
(이게 바로 내가 스파크 기본이론 포스팅 다음으로 도커 스웜을 다룬 이유이다...ㅎㅎ)
2. 커스텀 스파크 이미지 생성
스파크도 에어플로우랑 비슷하게 나에게 필요한 라이브러리 등을 추가한 커스텀 이미지를 만들어야한다.
나의 경우, Google Cloud Storage에서 항공권 데이터 파일을 가져와 파싱하여 Postgres 테이블에 삽입해야했기 때문에
JDBC와 GCS커넥터를 설치해야했다.
추가로, 에어플로우를 빌드할 때와 한가지 다른 점이 있다면,
에어플로우는 DAG파일, 즉 실행파일을 커스텀 이미지에 같이 포함시켜서 빌드하지만,
스파크는 실행파일은 submit해서 실행시키고, 이미지에는 커넥터 등만 같이 포함시키는 경우가 많다.
그 이유는 스파크 애플리케이션 코드는 자주 변경되지만, 스파크 환경 설정(JDBC, GCS 등의 커넥터)은 자주 변경되지 않기 때문이다.
매번 코드 변경때마다 이미지를 다시 빌드하는 것은 시간도 많이 걸리고 비효율적이다.
따라서 기본 환경만 이미지로 만들고, 애플리케이션 코드는 spark-submit
명령어로 제출하는 것이 일반적이다.
내가 사용한 커스텀 Dockerfile은 다음과 같다.
FROM apache/spark:latest
USER root
# 1. PostgreSQL JDBC 드라이버 추가
RUN curl -L -o /opt/spark/jars/postgresql-42.5.1.jar \
https://jdbc.postgresql.org/download/postgresql-42.5.1.jar
# 2. GCS 커넥터 추가
RUN curl -L -o /opt/spark/jars/gcs-connector-hadoop3-latest.jar \
https://storage.googleapis.com/hadoop-lib/gcs/gcs-connector-hadoop3-latest.jar
# 3. 필요한 Python 패키지 설치
RUN pip install pandas psycopg2-binary pytz
RUN pip install google-cloud-storage
# 4. SPARK_MODE에 따라 master/worker 실행
ENV SPARK_HOME=/opt/spark
ENTRYPOINT ["/bin/bash", "-c", "\
if [ \"$SPARK_MODE\" = \"master\" ]; then \
echo Starting Spark Master && \
/opt/spark/bin/spark-class org.apache.spark.deploy.master.Master; \
elif [ \"$SPARK_MODE\" = \"worker\" ]; then \
echo Starting Spark Worker && \
/opt/spark/bin/spark-class org.apache.spark.deploy.worker.Worker $SPARK_MASTER_URL; \
else \
echo Unknown SPARK_MODE: $SPARK_MODE && exit 1; \
fi"]
이 Dockerfile을 자세히 보면 다음과 같은 부분으로 나눌 수 있다:
2.1 기본 이미지 설정
FROM apache/spark:latest
USER root
스파크 공식 이미지를 베이스로 사용하고, 패키지 설치를 위해 root 권한으로 전환한다.
2.2 커넥터 설치
# 1. PostgreSQL JDBC 드라이버 추가
RUN curl -L -o /opt/spark/jars/postgresql-42.5.1.jar \
https://jdbc.postgresql.org/download/postgresql-42.5.1.jar
# 2. GCS 커넥터 추가
RUN curl -L -o /opt/spark/jars/gcs-connector-hadoop3-latest.jar \
https://storage.googleapis.com/hadoop-lib/gcs/gcs-connector-hadoop3-latest.jar
PostgreSQL 데이터베이스 연결을 위한 JDBC 드라이버와 Google Cloud Storage 연동을 위한 GCS 커넥터를 설치한다.
중요!!:
스파크에서 외부 시스템 연결용 JAR 파일들은 /opt/spark/jars/
디렉토리에 저장되어야 한다.
스파크가 시작할 때 이 디렉토리에 있는 모든 JAR 파일들을 클래스패스에 추가하여 사용한다.
2.3 파이썬 패키지 설치
# 3. 필요한 Python 패키지 설치
RUN pip install pandas psycopg2-binary pytz
RUN pip install google-cloud-storage
PySpark 사용을 위한 필수 파이썬 패키지들을 설치한다.
pandas는 데이터 처리, psycopg2는 PostgreSQL 접속, pytz는 타임존 처리, google-cloud-storage는 GCS 연동을 위한 패키지다.
이건 필요한 라이브러리를 설치하면 되는거라, 각자 알아서 requirements.txt로 설치해도 된다!
2.4 엔트리포인트 설정
# 5. SPARK_MODE에 따라 master/worker 실행
ENV SPARK_HOME=/opt/spark
ENTRYPOINT ["/bin/bash", "-c", "\
if [ \"$SPARK_MODE\" = \"master\" ]; then \
echo Starting Spark Master && \
/opt/spark/bin/spark-class org.apache.spark.deploy.master.Master; \
elif [ \"$SPARK_MODE\" = \"worker\" ]; then \
echo Starting Spark Worker && \
/opt/spark/bin/spark-class org.apache.spark.deploy.worker.Worker $SPARK_MASTER_URL; \
else \
echo Unknown SPARK_MODE: $SPARK_MODE && exit 1; \
fi"]
이 부분이 매우 중요하다. 이 스크립트는 환경 변수 SPARK_MODE
에 따라:
master
일 경우: 스파크 마스터 프로세스를 실행worker
일 경우: 스파크 워커 프로세스를 실행하고SPARK_MASTER_URL
환경변수로 마스터 위치를 지정- 그 외의 경우: 오류 메시지 출력 후 종료
이렇게 하나의 이미지로 마스터와 워커 모두 실행할 수 있게 만들었다.
도커 컴포즈에서는 서비스마다 environment
섹션을 통해 각각 다른 모드로 실행할 수 있다.
2.6 이미지 빌드
이미지를 빌드하고 도커 허브에 push하면 된다.
docker buildx build --platform linux/amd64 --no-cache -t vimcat23/spark-gcs-postgres:v2 --push .
3. 스파크 클러스터 docker-compose.yml 파일 작성
이제 이미지가 준비되었으니, 도커 스웜에 배포하기 위한 docker-compose.yml 파일을 작성하자.
version: '3.8'
services:
spark-master:
image: vimcat23/spark-gcs-postgres:v2
ports:
- "9090:8080" # 웹 UI
- "7077:7077" # 스파크 마스터 포트
environment:
- SPARK_MODE=master
- SPARK_RPC_AUTHENTICATION_ENABLED=no
- SPARK_RPC_ENCRYPTION_ENABLED=no
- SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
- SPARK_SSL_ENABLED=no
- SPARK_MASTER_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM -Dspark.deploy.recoveryDirectory=/opt/spark/recovery"
- SPARK_NO_DAEMONIZE=true
volumes:
- spark-data:/opt/spark/data
deploy:
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
spark-worker:
image: vimcat23/spark-gcs-postgres:v2
depends_on:
- spark-master
environment:
- SPARK_MODE=worker
- SPARK_MASTER_URL=spark://spark-master:7077
- SPARK_RPC_AUTHENTICATION_ENABLED=no
- SPARK_RPC_ENCRYPTION_ENABLED=no
- SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
- SPARK_SSL_ENABLED=no
volumes:
- spark-data:/opt/spark/data
deploy:
mode: global
placement:
constraints:
- node.labels.service_type == spark
restart_policy:
condition: on-failure
volumes:
spark-data:
driver: local
docker-compose.yml 파일을 섹션별로 분석해보자:
3.1 마스터 노드 구성
spark-master:
image: vimcat23/spark-gcs-postgres:v2
ports:
- "9090:8080" # 웹 UI
- "7077:7077" # 스파크 마스터 포트
environment:
- SPARK_MODE=master
- SPARK_RPC_AUTHENTICATION_ENABLED=no
- SPARK_RPC_ENCRYPTION_ENABLED=no
- SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
- SPARK_SSL_ENABLED=no
- SPARK_MASTER_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM -Dspark.deploy.recoveryDirectory=/opt/spark/recovery"
- SPARK_NO_DAEMONIZE=true
volumes:
- spark-data:/opt/spark/data
deploy:
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
여기서 중요한 부분들을 살펴보자:
- image: 앞서 만든 커스텀 이미지
- ports:
- 9090:8080 - 스파크 마스터 웹 UI (호스트의 9090 포트로 접속 가능)
- 7077:7077 - 스파크 마스터 통신 포트
- environment:
- SPARK_MODE=master - 마스터 모드로 실행
- 나머지는 보안 관련 설정들 (개발 환경이라 비활성화 했음)
- SPARK_MASTER_OPTS - 마스터 장애 복구 관련 설정
- volumes: 데이터 저장 공간 호스트 마운트
- deploy:
- node.role == manager - 매니저 노드에만 배포 (마스터는 한 개만 필요)
- restart_policy - 장애시 재시작
3.3 워커 노드 구성
spark-worker:
image: vimcat23/spark-gcs-postgres:v2
depends_on:
- spark-master
environment:
- SPARK_MODE=worker
- SPARK_MASTER_URL=spark://spark-master:7077
- SPARK_RPC_AUTHENTICATION_ENABLED=no
- SPARK_RPC_ENCRYPTION_ENABLED=no
- SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no
- SPARK_SSL_ENABLED=no
volumes:
- spark-data:/opt/spark/data
deploy:
mode: global
placement:
constraints:
- node.labels.service_type == spark
restart_policy:
condition: on-failure
워커 노드 설정의 중요 포인트:
- depends_on: 마스터 노드가 먼저 실행되어야 함 (완전 실행을 보장하는건 아님!! -> 쿠버네티스는 가능)
- environment:
- SPARK_MODE=worker - 워커 모드로 실행
- SPARK_MASTER_URL - 마스터 노드의 주소 지정
- deploy:
- mode: global - 조건에 맞는 모든 노드에 워커를 1개씩 배포
- constraints - node.labels.service_type == spark 라벨이 있는 노드에만 배포
node.labels.service_type == spark
이 부분은 특히 중요하다.
특정 노드에 "service_type=spark" 라벨을 붙여서, 스파크 워커 노드로 사용할 서버를 지정하는 것이다.
노드에 라벨을 추가하는 방법은 다음과 같다:
docker node update --label-add service_type=spark [노드ID 또는 호스트명]
3.4 볼륨 설정
volumes:
spark-data:
driver: local
스파크 데이터 저장용 볼륨을 정의한다. 로컬 드라이버를 사용하므로 각 노드별로 별도의 볼륨이 생성된다.
4. 스택 배포
이제 마스터 노드에서 다음 명령어로 스파크 클러스터를 배포할 수 있다:
docker stack deploy -c docker-compose.yml spark-stack
배포 후 서비스 상태 확인:
docker service ls
예상 출력:
ID NAME MODE REPLICAS IMAGE PORTS
rayhtvec8rw1 spark-stack_spark-master replicated 1/1 vimcat23/spark-gcs-postgres:v2 *:7077->7077/tcp, *:9090->8080/tcp
i6in8yn7d92e spark-stack_spark-worker global 2/2 vimcat23/spark-gcs-postgres:v2
스파크 마스터 웹 UI에 접속하려면 마스터 노드의 IP:9090으로 접속하면 된다.
만약 스케일 아웃이 필요하다면, 새 노드를 스웜에 추가하고 라벨을 부여하기만 하면 된다. (앞에서 mode를 global로 해놨기 때문)
# 새 노드를 워커로 추가
docker swarm join --token [토큰] [매니저IP]:2377
# 매니저 노드에서 라벨 추가
docker node update --label-add service_type=spark [새 노드 ID]
5. 마무리
이번 포스팅에서는 도커 스웜을 활용하여 스파크 클러스터를 구축하는 방법에 대해 알아봤다.
핵심 내용을 정리하면:
- 커스텀 스파크 이미지 생성: 필요한 커넥터(JDBC, GCS)와 라이브러리를 설치한 이미지 빌드
- docker-compose.yml 작성: 마스터와 워커 노드 설정을 정의
- 스택으로 배포:
docker stack deploy
명령어로 클러스터 구성 - 스케일링: 필요시 노드 추가 및 라벨링으로 손쉬운 확장
이렇게 구축한 스파크 클러스터는 마스터 노드 장애 시 자동 복구되고, 새 노드를 쉽게 추가하여 확장할 수 있다.
다음 포스팅에서는 개발을 위해 로컬 머신에 테스트용 스파크 환경을 구축하는 방법부터,
pyspark를 활용하여 json 데이터를 데이터프레임화하고, 필터링, 집계 함수 등을 활용하는 방법에 대해 다뤄볼 예정이다.
화이팅해보자~!!
"언제나, 이 세상 어딘가에 있는 나같은 사람을 위해 족적을 남긴다."
ㄱㅊㅁ_ㅇㅈ
'데이터 엔지니어링 > 기술스택 및 알쓸신잡' 카테고리의 다른 글
[Spark] 로컬 환경 구축하기! (0) | 2025.05.08 |
---|---|
[Docker] 도커 스웜 = 분산 시스템 구축 도우미? (0) | 2025.05.07 |
[Spark] 스파크...왜 쓰는걸까? (하둡부터 스파크까지) (0) | 2025.05.07 |
[Airflow] 커스텀 센서 구현하기! (Redis key value sensor) (0) | 2025.05.07 |
[Redis] 레디스 MQ 구현 및 활용법! (Python, Docker) (0) | 2025.05.06 |
소중한 공감 감사합니다