![Docker Container로 Spark구성해보기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblYYKf%2FbtsL367Ywgf%2FA5w5SUs8b9sd0bOfsfJAE0%2Fimg.png)
들어가며
- 간단하게 Pyspark를 테스트할 수 있는 환경을 구성하고, 조금 더 나아가 여러 개의 컨테이너를 만들어 Master노드와 Worker노드를 연결 해보고자 함
- ubuntu이미지 위에서 별도로 spark를 세팅하기 보다는 이미 만들어져 있는 spark이미지를 최대한 활용해보고자 함
- 컨테이너 실행은 총 4가지 방법으로 실행해 볼 것임
1.bitnami, Apache 이미지로
2.Jupyter Notebook에서 Pyspark 실행
3.Master & Worker 노드 클러스터 만들기 - docker run
4.Master & Worker 노드 클러스터 만들기 - docker compose
- 필자는 M1 맥북을 쓰고 있고, docker desktop Apple Silicon 버전을 깔아놨음
~그럼 출바알~
LV1. bitnami, Apache 이미지 사용
- 정말 심플하게 spark구동만 해보는 것임
docker run --rm -it --name spark bitnami/spark:latest
- 근데, 이렇게만 하면 아무것도 뜨지 않음. 뒤에 spark-shell을 붙여줘야함
- 그럼 짜잔 scala spark 명령어를 입력해볼 수 있음
docker run --rm -it --name spark bitnami/spark:latest spark-shell
- pyspark로 하려면? apache spark 공식 이미지를 이용하면 됨
docker run --rm -it apache/spark-py /opt/spark/bin/pyspark
- 안에서 간단한 코딩 해볼 수 있음
d = [{'name': 'dongmin', 'age': '20'}]
spark.createDataFrame(d).show()
LV2. Jupyter Notebook에서 Pyspark실행
- jupyter에서 생성한 pyspark 이미지가 있음 그걸 사용
- 로컬에 있는 csv 파일을 사용해보려고 .(현재 디렉토리)를 이미지의 workdir에 마운트시킴, 그리고 컨테이너 종료후 컨테이너에서 생성한 노트북 같은 것도 내 로컬에 남아 있게 됨
- (-v .:/home/joyvan에서 아무 옵션도 지정하지 않으면 rw방식으로 컨테이너 안에서 작업한 것도 로컬 경로안에 저장되게 됨. 이게 싫고, 로컬 경로안의 파일만 컨테이너 안으로 복사하고 싶으면 ro라는 옵션을 별도로 줘야함)
- Docker는 기본적으로 컨테이너 생성 시 Anonymous Volume이라는 것을 만든다.
- 하지만, 이 volume은 컨테이너가 실행될 때 매번 초기화됨
- 즉, 컨테이너를 종료하면 저장했던 것들이 사라져버림
- 그래서 편의상 로컬에다 마운트를 해둠 (별도 볼륨을 생성해서 사용할 수도 있음)
docker run --rm \ # --rm 옵션을 사용하면 종료시 컨테이너 삭제
-p 8888:8888 \ # jupyter 접속포트
-p 4040:4040 \ # spark web ui 접속 포트
-v .:/home/jovyan \ # 현재 디렉토리를 마운트
jupyter/pyspark-notebook:latest
- localhost:8888로 접속하면 token을 입력하라고 나옴. 가뿐하게 입력후 안으로 진입
- 어랍쇼? 근데 localhost:4040은 연결이 안됨
- 왜냐하면, Spark Web UI를 실행하려면 Spark App을 먼저 생성해야 하기 때문. 관련된 내용은 요기서 볼 수 있음.
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('test').getOrCreate()
- 앱을 생성하고 나면 Spark Web UI에 접속해볼 수 있음
df = spark.read.option("header", "true").csv("weather_description.csv")
df.show()
- 마운트를 시켜놨기 때문에 로컬 폴더 안에 있는 CSV파일을 불러서 읽어볼 수도 있음. (다만, 꼭 마운트를 안시켜도 Jupyter Notebook에 파일을 업로드할 수도 있기는 함)
LV3. Master & Worker 노드 클러스터 만들기 - docker run
- Spark는 병렬 처리 프레임 워크로 중앙에서 통솔하는 Master Node와 일을 나누어서 수행하는 여러개의 Worker들이 존재함.
- 지금까지 해 본 방식은 단일 노드에서만 데이터를 처리할 수 있도록 구성한 것
- 지금부터는 Master노드용 컨테이너와 Worker노드용 컨테이너를 만들어 엮어볼 것임
- 도커에서는 컨테이너를 생성 시 IP를 할당함. 다만, 컨테이너를 재시작하면 이전과는 다른 IP가 부여됨. 별도의 DNS 시스템이 없기 때문에 IP로 Master <-> Worker를 통신하게 해야 하는데, 컨테이너 재실행 시 IP가 변경되기 때문에 여러모로 애매쓰
- 이럴 때는 별도의 네트워크를 생성해주면 됨. 컨테이너 이름가지고 통신할 수 있기에 IP 변경에 대한 걱정이 없음.
docker network create spark-connect
- 네트워크 이름은 자유! 나는 편의상 spark-connect로 생성함
- bitnami이미지를 사용하니 pyspark를 세팅하는 과정에서 JAVA_HOME path에러가 계속 발생함
- 본인은 trouble shooting에 실패하여 bitnami/spark 이미지 대신 apache/spark 이미지를 사용
- 여러개의 컨테이너를 실행할 것이기 때문에 -d를 붙여 백그라운드에서 실행
- 8080은 공식 이미지에서 spark ui 웹 접속 포트임.
- 7077은 master노드로 연결하는 포트임.
- 전부다 여기 보면 설명 있음
docker run -d --name master # -d는 백그라운드 실행을 의미
--network spark-connect \ # 앞서 생성한 네트워크
-p 8888:8080 \ # spark ui 웹 접속 포트, 8080을 다른 컨테이너에서 이미 사용하고 있어 8888로 세팅
-p 7077:7077 \ # worker -> master로 접속하는 포트 번호
apache/spark \
/opt/spark/bin/spark-class org.apache.spark.deploy.master.Master # Master 클래스로 실행
- 다음으로 워커 컨테이너를 실행할 것임
- 한 가지 주의할 부분은 마지막 spark://master:7077에서 master는 내가 위에서 생성한 master 컨테이너임 이 이름이 동일 해야 함
- spark://master:7077 : 마스터 노드는 저기있어~ 대충 이런 의미
docker run -d --name worker1
--network spark-connect \ # 앞서 생성한 네트워크
-e SPARK_WORKER_MEMORY=1g \ # 설정 안하면 내 모든 RAM -1GB만큼 디폴트 세팅
-e SPARK_WORKER_CORE=2 \ # 설정 안하면 사용 가능한 모든 코어 할당
apache/spark \
/opt/spark/bin/spark-class org.apache.spark.deploy.worker.Worker spark://master:7077
# 여기서 주의 master에는 아까 생성한 master컨테이너의 이름을 넣어야함
- 그리고 worker 컨테이너 하나 더 생성할 것임
- 다 똑같고 이름만 worker2로 바꿈
docker run -d --name worker2
--network spark-connect \
-e SPARK_WORKER_MEMORY=1g \
-e SPARK_WORKER_CORE=2 \
apache/spark \
/opt/spark/bin/spark-class org.apache.spark.deploy.worker.Worker spark://master:7077
- 이제 master노드 터미널 안으로 들어갈 것임
- 터미널 안에서 모듈 설치를 해야 할 수도 있으므로 Root유저로 진입.
docker exec -it --user root master bash
- pip3와 python3가 이미 master 컨테이너 안에 생성되어 있는 상황이므로 pyspark만 별도로 설치
pip3 install pyspark
- python3로 IDE 진입 후 spark 앱 생성
- localhost:8888로 접속해보면
- 아래와 같이 세팅한대로 워커 2개가 남아 있는 것을 알 수 있음 (DEAD는 생성했다 삭제한 worker 컨테이너들임)
- 다음으로, 생성한 spark 클러스터를 jupyter notebook에서도 실행해볼 수 있음.
- 몇 개 테스트 해봤는데, JUPYTERHUB 이미지를 이용하면 JAVA_HOME같은 환경변수를 처음부터 세팅해야 돼서 귀찮음. 걍 pyspark-notebook을 사용하는게 편함(실행하는게 목적이니)
docker run --name jupyternotebook \
--network spark-connect \
-p 8887:8888 \ # 8888을 앞에서 spark web ui로 사용하여 8887로 세팅 (포트 번호는 편한대로..)
-v .:/home/jovyan \
jupyter/pyspark-notebook
- 그리고, master에 spark://master:7077을 입력후 localhost:8888에 다시 접속하면
- jupytertest라는 앱이 정상적으로 생성된 것을 알 수 있음
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.master("spark://master:7077") \
.appName("Jupytertest") \
.getOrCreate()
LV4. Master & Worker 노드 클러스터 만들기 - docker compose
- 지금까지 LV3에서 했던 것을 docker-compose.yml 파일 하나로 한큐에 정리할 것임
- docker compose의 경우 default 네트워크를 생성하여 docker-compose.yml 파일 안의 컨테이너들을 하나의 네트워크로 연결하기 때문에 지금 같은 상황에서는 별도의 네트워크 설정을 할 필요는 없음.
- docker compose up 명령어 실행 시 아래와 같이 default네트워크가 생성되는 것을 볼 수 있음
# docker-compose.yml
services:
master:
image: apache/spark
command: "/opt/spark/bin/spark-class org.apache.spark.deploy.master.Master"
ports:
- "8888:8080"
- "7077:7077"
worker:
image: apache/spark
command: "/opt/spark/bin/spark-class org.apache.spark.deploy.worker.Worker spark://master:7077"
environment:
- SPARK_WORKER_MEMORY=1g
- SPARK_WORKER_CORES=2
depends_on:
- master
jupyter:
image: jupyter/pyspark-notebook
ports:
- "8887:8888"
volumes:
- .:/home/jovyan
depends_on:
- master
- 그리고 아까와 다른점은 worker1, worker2 이런식으로 지정하지 않고 worker라고 해뒀음.
- 이렇게 해두면 --scale 옵션을 통해 worker 수를 여러개 늘릴 수 있음
- docker-compose.yml이 있는 디렉토리로 이동하여 아래의 명령어를 실행하자.
docker compose up --scale worker=4
- 그럼 아래와 같이 master-1, jupyter-1, worker-1, worker-2, worker-3, worker-4 총 6개의 컨테이너가 실행된 것을 알 수 있다.
- 그리고 localhost:8887로 접속하여 앱을 빌드하면
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.master("spark://master:7077") \
.appName('dockercompose') \
.getOrCreate()
- 최종적으로 dockercompose라는 app이름과 4개의 worker가 잘 세팅된 것을 확인할 수 있다.
'딥상어동의 딥한 프로그래밍 > 엔지니어링' 카테고리의 다른 글
MY FIRST DBT - (1) Bigquery 연결해보기 + 폴더 구조에 대한 가벼운 이해 (5) | 2024.11.10 |
---|---|
Airflow DAG 개념 톺아보기 (설치/실행 과정 포함) (4) | 2024.10.13 |
우당탕탕 슬랙 메시지 저장기(2) - 게시글과 쓰레드 조회하기 (2) | 2023.05.21 |
우당탕탕 슬랙 메시지 저장기(1) - 슬랙 메시지 넌 누구냐? (8) | 2023.05.07 |
데이터 마트에서는 뭘 파나요?(feat. OLTP, OLAP) (14) | 2023.03.26 |
제 블로그에 와주셔서 감사합니다! 다들 오늘 하루도 좋은 일 있으시길~~
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!