CHAPTER 02 · 실습환경 구축

내 PC에서
Spark 실습 환경 만들기

네 컨테이너의 역할을 배우고, Docker 설치부터 합계 5050 확인까지 실습합니다.

강의 56슬라이드실습 원본 6파일개념 → 준비 → 검증 → 정리
이번 장의 확인 순서

이미지 검사 → Worker 2개 등록 → 네트워크 연결 → 합계 5050 → 정상 종료

아래 출력은 예상 예시입니다. 자신의 PC에서 Docker를 실행해 결과를 확인합니다.

01

같은 실습 환경이 필요한 이유

코드가 같아도 Java나 Python 버전이 다르면 결과가 달라지거나 실행에 실패할 수 있습니다. Docker는 프로그램과 필요한 실행 환경을 묶어 이런 차이를 줄입니다.

프로세스·서버·클러스터

학습용 비유 · 하나의 작업대, 나뉜 작업 공간

한 작업대의 상자마다 도구와 재료를 구분해 담듯, 컨테이너도 실행 환경을 구분합니다. 이미지는 각 환경을 만드는 공통 틀입니다.

하나의 작업대 위에 같은 크기의 투명 상자 네 개가 놓여 있고, 상자마다 별도 도구와 재료가 담긴 모습
AI로 생성한 학습용 비유이며 실제 실행 화면이 아닙니다. 네 컨테이너는 같은 호스트를 공유하는 격리된 실행 환경으로, 상자는 실제 프로세스 구조를 나타내지 않습니다.

Docker 도구와 실행 환경

처음 만나는 Docker 용어
용어쉽게 풀어 쓰면이번 실습에서
Docker DesktopWindows에서 Docker 실행 환경과 도구를 관리하는 앱먼저 열고 Engine running 상태를 기다립니다.
Engine뒤에서 이미지를 받고 컨테이너를 만들고 실행하는 서비스꺼져 있으면 명령 도구만 있어도 컨테이너가 실행되지 않습니다.
CLICommand Line Interface. 글자로 명령을 보내는 도구PowerShell에서 docker를 입력해 Engine에 요청합니다.
Image · 이미지프로그램·라이브러리·기본 파일·실행 명령을 담은 읽기 전용 틀Spark, Java, Python을 포함한 이미지 하나를 사용합니다.
Container · 컨테이너이미지를 바탕으로 만든 실행 환경과 프로세스같은 이미지로 Master, Worker 2개, Client를 만듭니다.
Registry · 레지스트리이미지를 보관하고 배포하는 서버기본 공개 저장소인 Docker Hub에서 docker pull로 받습니다.
Compose여러 컨테이너의 구성을 파일로 선언하고 함께 관리하는 도구compose.yaml을 읽어 같은 구성을 재현합니다.

이름과 태그 읽기

spark:4.1.3-scala2.13-java21-python3-ubuntu를 콜론으로 나누어 읽습니다.

서비스 이름과 컨테이너 이름

같은 Spark 이미지로 Master와 Worker를 함께 만들 수 있을까요?

출처: 2장 강의자료 슬라이드 1–10. 슬라이드 번호는 파일의 실제 순서 기준입니다.

02

Windows와 Spark 이미지 준비하기

실습 경로 선택
  • 이 장의 제공 실습: 아래에서 실습자료/2장_실습자료의 파일을 확인하고 진행합니다.
  • 별도 보충 실습: 보충 실습 파일과 준비 안내에서 Compose·Python·Bash 전체 파일을 받을 수 있습니다. 보충 합계 실습은 별도 practice 폴더와 Master 28080·Driver 24041 포트를 사용합니다.

두 실습은 폴더와 포트가 다릅니다. 선택한 실습의 안내에 따라 명령을 실행합니다.

Linux용 Spark 환경을 Windows에서 실행하려고 WSL 2(Windows Subsystem for Linux 2)를 사용합니다. Docker Desktop이 이 환경을 이용해 Linux 컨테이너를 실행합니다.

학습용 비유 · 실험실의 전원 확인

실험 전 전원을 확인하듯, 먼저 WSL과 Docker Engine의 준비 상태를 확인합니다. 그다음 실습 이미지를 받습니다.

Docker Desktop이 없다면 공식 Windows 설치 안내에서 설치 조건과 절차를 확인합니다. WSL이 없다면 아래 WSL 준비 절차부터 진행합니다.

  1. Docker Desktop 실행과 Engine 확인

    실행 위치: Windows PowerShell, 어느 폴더에서나. Desktop에서 Engine running을 기다린 뒤 아래를 실행합니다.

    docker --version
    docker compose version
    docker version
    docker run --rm hello-world
    • docker --version: Docker 명령 도구의 버전을 확인합니다.
    • docker compose version: Compose 도구의 버전을 확인합니다.
    • docker version: Client와 Server(Engine)의 연결을 함께 확인합니다.
    • docker run --rm hello-world: 작은 시험 컨테이너를 실행합니다. --rm은 종료 후 그 컨테이너를 제거합니다.

    Server 연결 오류라면 Desktop 실행 상태부터 확인하세요.

  2. WSL 상태 확인

    실행 위치: 일반 PowerShell에서 상태 확인.

    wsl --status
    wsl --version
    WSL 설치가 필요한 경우만 펼치기

    실행 위치: 관리자 권한 PowerShell. 설치가 안 된 경우 아래 설치 명령을 실행하고 Windows를 재시작합니다. 업데이트가 필요한 경우 업데이트 명령을 사용합니다. Docker Desktop 설치 후 WSL 2 기반 Engine 설정을 확인하세요.

    wsl --install
    wsl --update

    Microsoft 공식 WSL 설치 안내와 Docker Desktop Windows 설치 안내에서 자신의 환경에 맞는 설치 조건을 확인할 수 있습니다.

  3. 실습 폴더로 이동

    실행 위치: 이 프로젝트의 BigData 최상단에서 Windows PowerShell. 이후 docker compose 명령은 이동한 실습 폴더에서 실행합니다.

    cd ".\실습자료\2장_실습자료"
    Get-ChildItem
    Get-ChildItem .\apps

    강의의 “week02-lab 폴더”에 해당합니다. 다음 6개 파일이 있는지 확인합니다.

    • compose.yaml: 서비스 구성 파일입니다.
    • README.md: 배포 절차를 안내합니다.
    • apps/check_pyspark.py: 이미지의 PySpark 실행을 검사합니다.
    • apps/check_network.py: 컨테이너 간 네트워크를 검사합니다.
    • apps/run.sh: 앱 제출 명령을 담습니다.
    • apps/first_app.py: 계산과 정답 검증을 수행합니다.
  4. 지정된 이미지 다운로드

    실행 위치: 실습 폴더의 Windows PowerShell.

    $sparkImage = "spark:4.1.3-scala2.13-java21-python3-ubuntu"
    docker pull $sparkImage
    docker image ls $sparkImage

    확인: 목록의 이름·태그가 위 값과 일치해야 합니다. 다운로드에 실패하면 네트워크를 확인하고 오류 메시지를 교수님께 전달하세요.

이미지 다운로드 실패 시 대안

교수자가 별도 TAR 파일을 제공한 경우에만 아래 명령으로 불러온 뒤 이미지 검사를 수행합니다. 이 프로젝트에는 이미지 TAR와 README가 참조하는 강의노트 v5·부록 A가 없습니다.

docker image load -i <파일경로>

위 <파일경로>는 그대로 실행하는 값이 아닙니다. 교수자가 제공한 TAR의 실제 경로로 바꿉니다.

확인 질문

docker --version만 성공했다면 Engine까지 실행 중이라고 말할 수 있을까요? 아직 아닙니다. docker version의 Server 응답과 시험 컨테이너 실행을 확인해야 합니다.

출처: 강의 슬라이드 11–16 · 배포 README의 이미지 확보·Plan B. Get-ChildItem .\apps는 파일 확인을 돕는 보충 명령입니다.

03

Compose로 컨테이너 설정 읽기

Compose 파일은 실행할 서비스와 설정을 정의합니다. 공통 설정을 한 번 정의하고, 서비스마다 실행 명령과 개별 설정을 추가합니다.

학습용 비유 · 공통 준비물과 개인 역할표

팀의 공통 준비물을 한 번 적고 개인 역할을 덧붙이는 방식입니다. YAML의 공통 묶음도 설정을 재사용하며, 별도 컨테이너를 만들지는 않습니다.

공통 설정: 한 번 쓰고 재사용

YAML은 들여쓰기로 포함 관계를 표현하는 설정 형식입니다. 아래는 compose.yaml 전체 원본에서 발췌한 공통 설정입니다.

name: bigdata-week02

x-spark: &spark
  image: spark:4.1.3-scala2.13-java21-python3-ubuntu
  environment:
    PYSPARK_PYTHON: python3
  init: true
  stop_grace_period: 30s

services:
공통 설정을 한 줄씩 해석하기
설정뜻
nameCompose 프로젝트 이름입니다. 이 구성의 컨테이너와 네트워크를 묶어 구분합니다.
x-spark: &sparkx-는 재사용용 확장 필드, &spark는 설정 묶음에 붙인 anchor(참조 이름)입니다.
<<: *spark각 서비스에서 공통 설정을 가져와 합칩니다. *spark는 그 이름을 다시 가리키고 <<는 합치기를 뜻합니다.
PYSPARK_PYTHON: python3Python으로 Spark를 사용하는 PySpark가 컨테이너 내부의 python3를 쓰도록 합니다.
init: true작은 관리 프로세스를 맨 먼저 실행해 종료 신호 전달과 자식 프로세스 종료 정리를 돕습니다.
stop_grace_period: 30s중지 시 정상 종료를 위해 최대 30초를 기다립니다.

Master: 상태 검사와 서비스 시작 조건

  spark-master:
    <<: *spark
    hostname: spark-master
    entrypoint: ["/opt/spark/bin/spark-class"]
    command: ["org.apache.spark.deploy.master.Master", "--host", "spark-master", "--port", "7077", "--webui-port", "8080"]
    ports:
      - "127.0.0.1:8080:8080"
    healthcheck:
      test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080', timeout=3)"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 20s
healthcheck 시간 옵션 더 보기
  • interval: 검사 간격입니다.
  • timeout: 한 검사에 허용하는 시간입니다.
  • retries: 연속 실패 판단 기준입니다.
  • start_period: 초기 준비 시간을 고려하는 설정입니다.

healthy는 이 UI 검사에 통과했다는 뜻이며 계산 성공을 증명하지는 않습니다.

Worker: 제공할 CPU와 메모리 알리기

  spark-worker-1:
    <<: *spark
    hostname: spark-worker-1
    entrypoint: ["/opt/spark/bin/spark-class"]
    command: ["org.apache.spark.deploy.worker.Worker", "--host", "spark-worker-1", "--cores", "1", "--memory", "1g", "--webui-port", "8081", "--work-dir", "/tmp/spark-work", "spark://spark-master:7077"]
    ports:
      - "127.0.0.1:8081:8081"
    depends_on:
      spark-master:
        condition: service_healthy

Worker 하나가 Master에 알리는 자원은 1 core, 1 GiB입니다.

Worker 2는 같은 역할이며 이름은 spark-worker-2, 호스트 UI 포트는 8082입니다.

설정의 적용 범위
  • --memory 1g: Spark에 제공할 자원 선언입니다. 컨테이너 전체의 메모리 상한은 아니며, 실제 자원은 PC·Docker 설정에도 좌우됩니다.
  • depends_on: service_healthy: 시작 조건입니다. 이후 Master 장애를 자동 복구하지는 않습니다.

Client: 실습 폴더 연결과 대기

  spark-client:
    <<: *spark
    hostname: spark-client
    entrypoint: ["/bin/bash", "-lc"]
    command: ["exec sleep infinity"]
    ports:
      - "127.0.0.1:4040:4040"
    volumes:
      - ./apps:/opt/spark/apps:ro
    depends_on:
      spark-master:
        condition: service_healthy
Bash의 시작 옵션 더 보기

shell은 명령을 해석하는 프로그램입니다. 여기서는 Bash를 사용합니다.

  • -l: login 모드의 시작 설정을 사용합니다.
  • -c: 뒤 문자열을 명령으로 실행합니다.

exec sleep infinity는 shell을 계속 대기하는 프로그램으로 바꿔 Client가 종료되지 않게 합니다. 이 시점에는 Driver가 없습니다.

실습 폴더 연결: bind mount

bind mount는 호스트 폴더를 컨테이너에서 보이게 연결하는 방식입니다. ./apps:/opt/spark/apps:ro는 세 부분으로 구성됩니다.

파일을 따로 복사하지 않아도 Client에서 Windows의 실습 코드를 읽어 실행할 수 있습니다.

네 서비스를 지정해 실행해도 x-spark 때문에 컨테이너가 하나 더 생길까요?

출처: 강의 슬라이드 17–29 · compose.yaml. Master·Worker 1·Worker 2·Client 네 서비스가 정의돼 있습니다.

04

Spark 구성요소의 역할과 실행 순서

Spark는 데이터를 나누어 여러 프로세스에서 처리하는 엔진입니다. 이번 실습에서는 Spark 자체의 클러스터 관리 방식인 Standalone을 사용합니다.

학습용 비유 · 팀 실험의 자리와 작업

Master는 자리를 배정하고, Worker는 계산 담당자를 준비하며, Driver는 실험 순서를 조정하는 역할에 비유할 수 있습니다. 실제로는 프로세스끼리 통신합니다.

Spark 구성요소의 역할
역할언제, 어디에서하는 일
MasterMaster 컨테이너에서 계속 실행Worker와 가용 자원을 관리하고 Executor를 어느 Worker에서 시작할지 배정합니다.
Worker두 Worker 컨테이너에서 계속 실행Master에 등록하고, 지시에 따라 자기 쪽에서 Executor를 시작·관리합니다.
ClientClient 컨테이너는 처음에는 대기실습 코드를 제출하는 실행 장소입니다.
Driver앱 제출 후 Client에서 실행앱의 진행을 조정하고 Executor에 Task를 배정하며 결과를 확인합니다.
Executor앱 제출 후 Worker 쪽에서 실행실제 데이터 처리 Task를 실행하는 애플리케이션별 프로세스입니다.

준비 → 제출 → 계산

  1. 준비: Compose가 네 컨테이너 실행을 요청하고 Worker가 Master에 등록합니다. 별도 SSH 로그인이나 키 복사는 필요 없습니다.
  2. 제출: Client에서 spark-submit을 실행하면 Driver가 앱을 등록하고 자원을 요청합니다. Master는 Worker에 Executor 시작을 지시합니다.
  3. 계산: Executor가 Driver에 연결합니다. Driver와 Executor가 Task 지시·상태·결과를 직접 주고받습니다. Master가 모든 계산 메시지를 중계하지 않습니다.

앱을 제출하기 전, Worker 2개가 ALIVE라면?

출처: 강의 슬라이드 18, 23–25, 37, 39–40, 45–48 · 역할 정의 보충: Apache Spark 공식 클러스터 개요. 이 실습의 실행 옵션과 버전은 제공 파일에 따릅니다.

05

접속 위치에 맞는 주소와 포트 고르기

프로그램에 연결할 때는 주소와 포트로 목적지를 지정합니다.

학습용 비유 · 건물 주소와 창구 번호

IP는 건물 주소, 포트는 창구 번호, DNS는 이름으로 주소를 찾는 안내부에 비유할 수 있습니다. 주소를 찾은 뒤에는 프로그램끼리 직접 통신합니다.

실습의 접속 경로
출발 위치·목적쓸 주소도착 위치
Windows 브라우저 → Master UIlocalhost:8080Master 내부 8080
Windows 브라우저 → Worker 1 UIlocalhost:8081Worker 1 내부 8081
Windows 브라우저 → Worker 2 UIlocalhost:8082Worker 2 내부 8081
Windows 브라우저 → Driver UIlocalhost:4040앱 실행 중 Client 내부 4040
Worker → Masterspark-master:7077Compose 내부 네트워크의 Master

포트 설정의 세 자리

127.0.0.1:8082:8081은 호스트 IP : 호스트 포트 : 컨테이너 포트 순서입니다.

두 Worker는 서로 다른 컨테이너이므로 내부에서 각각 8081을 쓸 수 있습니다. Windows에서는 같은 IP의 같은 포트를 두 곳에 연결할 수 없어 8081과 8082로 나눕니다.

localhost가 가리키는 대상
  • Windows 브라우저에서: localhost는 Windows PC입니다.
  • Worker 안에서: localhost는 그 Worker 컨테이너입니다.

그래서 Worker가 Master를 찾을 때는 localhost:7077 대신 spark-master:7077을 씁니다.

내부 연결은 서비스 이름으로

DNS(Domain Name System)는 이름을 IP 주소로 바꾸는 체계입니다. Compose 내부에서는 서비스 이름을 사용합니다.

Windows 브라우저에서 Worker 2 UI를 볼 주소는?

출처: 강의 슬라이드 9, 26–31, 42–43 · 실제 포트 설정 · 보충 확인: Docker 공식 Compose 네트워크 설명.

06

이미지에서 PySpark 실행 확인하기

컨테이너 간 연결 없이 작은 계산을 실행합니다. 이 단계에서 실패하면 이미지와 실행 환경을 먼저 점검합니다.

학습용 비유 · 합주 전 악기 조율

합주 전 악기 하나를 조율하듯, 이미지 안에서 작은 계산부터 확인합니다. 컨테이너 간 협력은 다음 단계에서 검사합니다.

① Compose 구성 확인

실행 위치: 실습자료/2장_실습자료 폴더의 Windows PowerShell.

docker compose config --quiet
docker compose config --services

이번 실습에서는 Worker 1·2를 사용합니다. 다음 단계에서 위 네 서비스를 실행합니다.

오류 시: 현재 폴더와 YAML 들여쓰기를 확인하세요.

② 일회용 컨테이너에서 PySpark 검사

실행 위치: 같은 PowerShell에서 입력하며, 검사는 일회용 컨테이너 안에서 실행됩니다.

docker compose run --rm --no-deps --entrypoint /opt/spark/bin/spark-submit spark-client --master "local[1]" /opt/spark/apps/check_pyspark.py
$LASTEXITCODE

로컬 검사입니다. $LASTEXITCODE는 직전 외부 프로그램의 종료 코드이며, 0이면 정상 종료입니다. 다른 명령으로 값이 바뀌기 전에 바로 확인하세요.

예상 출력과 성공 기준

PYSPARK_VERSION=4.1.3
PYSPARK_IMAGE_CHECK=PASS

성공 기준: 위 두 표시와 종료 코드 0. 추가 Spark 로그가 섞여 나올 수 있습니다.

검사 코드가 하는 일

검사 코드는 버전 확인 → SparkSession 생성 → 행 수 확인 → 종료 순입니다.

  • SparkSession: Python에서 Spark 기능을 사용하는 시작점입니다.
  • spark.range(1).count() == 1: 값 0 한 개를 만들고 행 수가 1인지 확인합니다.
실패 원인별 확인
  • 파일 없음: 작업 폴더와 apps 연결을 확인합니다.
  • 버전 불일치: 지정 태그의 이미지를 받은 뒤 재검사합니다.

통과하면 이미지에서 PySpark를 실행할 준비가 된 것입니다.

확인 질문: 이 검사에 PASS가 나오면 Worker 등록도 완료된 걸까요? 아닙니다. --no-deps와 local[1] 때문에 아직 Worker를 사용하지 않았습니다.

출처: 강의 슬라이드 32–35 · check_pyspark.py · README의 이미지 실행 검사.

07

컨테이너 실행과 Worker 등록 확인하기

컨테이너 상태, Spark 등록, 네트워크는 서로 다른 확인 항목입니다. 셋을 나누어 보면 어디에서 문제가 생겼는지 찾기 쉽습니다.

학습용 비유 · 출석과 전화 확인

전원, 팀원의 출석, 전화 연결, 과제 완성을 따로 확인하듯, Up → ALIVE → NETWORK_CHECK → 계산 결과도 각각 확인합니다.

  1. 네 서비스 실행

    실행 위치: 실습 폴더의 Windows PowerShell.

    docker compose up -d --remove-orphans spark-master spark-worker-1 spark-worker-2 spark-client
    docker compose ps -a

    -d는 백그라운드 실행입니다. 첫 줄은 이번 실습에 쓸 네 서비스를 지정합니다. --remove-orphans는 같은 Compose 프로젝트에서 현재 파일에 정의되지 않은 서비스의 컨테이너를 제거합니다. 실행 전에 같은 프로젝트에서 유지해야 할 컨테이너가 있는지 확인하세요.

    준비가 끝날 때까지 기다린 뒤 네 컨테이너의 상태를 확인하세요. 옵션의 의미는 Docker Compose up 공식 문서에서 확인할 수 있습니다.

    • Master: healthy
    • Worker 2개·Client: Up
  2. Master UI에서 Worker 등록 확인

    실행 위치: Windows 브라우저에서 Master UI 열기.

    • Worker: ALIVE 2개
    • 각 Worker 자원: 1 core, 약 1024 MiB
    • Executor: 앱 제출 전에는 없어도 정상
  3. Client에서 네트워크 검사

    입력 위치: 실습 폴더의 Windows PowerShell. 실제 검사 위치: Client 컨테이너 안.

    docker compose exec spark-client python3 /opt/spark/apps/check_network.py
    $LASTEXITCODE

    exec는 이미 실행 중인 컨테이너 안에서 명령을 수행합니다. run과 달리 새 컨테이너를 만들지 않습니다.

check_network.py가 확인하는 범위
검사대상읽는 방법
DNSMaster, Worker 1, Worker 2, Client 이름 4개DNS spark-master -> … 뒤의 IP가 조회됐는지 봅니다. 숫자는 실행 환경에 따라 달라집니다.
TCPspark-master:7077, 두 Worker의 8081TCP … -> OK는 해당 포트로 연결할 수 있다는 뜻입니다.
종합이름 조회와 세 포트 연결NETWORK_CHECK=PASS와 종료 코드 0을 확인합니다.

TCP는 프로그램 간 연결을 맺고 데이터를 주고받는 통신 방식입니다. 이 검사는 연결을 열 수 있는지만 봅니다. 실패 표시는 다음처럼 구분하세요.

서비스 이름·실행 상태·로그를 순서대로 확인하세요.

NETWORK_CHECK=PASS라면 합계 5050도 보장될까요?

출처: 강의 슬라이드 36–43 · check_network.py. 종료 코드 0/1 확인은 보충 단계입니다.

08

1부터 100까지 더하고 5050 확인하기

1부터 100까지 더하면 5050입니다. 정답을 미리 아는 문제로 Spark가 올바른 클러스터에서 실행됐는지 확인합니다.

학습용 비유 · 문제지를 나눠 풀고 합치기

숫자를 네 묶음으로 나눠 부분 합을 구하고 합치는 방식입니다. 아래는 학습용 모형이며, 실제 Spark의 Task와 집계 단계는 실행 계획에 따라 달라집니다.

계산과 검증 코드

numbers = spark.range(1, 101, 1, 4)
total = numbers.selectExpr("sum(id) AS total").first()["total"]
assert total == 5050, f"Unexpected result: {total}"

입력: 1~100을 네 조각으로

데이터를 표현하는 두 용어

계산과 확인: 합계가 5050인지

손으로도 100 × 101 ÷ 2 = 5050인지 대조할 수 있습니다.

분할 수와 실행 자원은 다릅니다
  • 입력: partition 4개
  • 실행 자원: 총 Executor core 2개. 네 조각이 모두 동시에 처리되지는 않습니다.
  • 전체 Task 수: 집계 단계가 추가될 수 있어 4개로 단정할 수 없습니다.

run.sh가 정하는 실행 조건

제출 옵션과 목적
옵션이번 실행에서의 의미
--master spark://spark-master:7077로컬 모드 대신 Standalone Master에 자원을 요청합니다.
--deploy-mode client제출 명령이 실행되는 Client에 Driver를 둡니다.
Driver 512m / Executor 512m각 메모리 설정은 512 MiB입니다. 프로세스 전체 사용량과 같다고 단정하지 않습니다.
Executor 1 core / 총 2 cores두 Worker에서 Executor 하나씩 실행할 수 있도록 구성합니다.
dynamicAllocation.enabled=false실행 중 Executor 수를 동적으로 늘이거나 줄이는 기능을 끕니다.
spark.driver.host=spark-clientExecutor가 되돌아 연결할 Driver의 이름을 알립니다.
bindAddress=0.0.0.0Driver가 컨테이너 안의 네트워크 인터페이스에서 연결을 받게 합니다. 브라우저에 입력할 주소가 아닙니다.
Driver 39000 / BlockManager 39001내부 통신 포트를 고정합니다. BlockManager는 Spark 데이터 블록을 관리하는 구성요소입니다. 이 포트들은 Windows UI 포트가 아닙니다.
run.sh 전체 원본 읽기

이 파일은 Linux의 Bash 스크립트입니다. PowerShell에 본문을 직접 붙여넣지 말고 아래의 docker compose exec … bash … 명령으로 실행합니다.

  • set -euo pipefail: 오류를 놓치지 않도록 하는 shell 옵션입니다.
  • "$@": 추가로 받은 인수를 Python 앱에 전달합니다.
#!/usr/bin/env bash
set -euo pipefail
exec /opt/spark/bin/spark-submit \
  --master spark://spark-master:7077 \
  --deploy-mode client \
  --driver-memory 512m \
  --executor-memory 512m \
  --executor-cores 1 \
  --total-executor-cores 2 \
  --conf spark.dynamicAllocation.enabled=false \
  --conf spark.driver.host=spark-client \
  --conf spark.driver.bindAddress=0.0.0.0 \
  --conf spark.driver.port=39000 \
  --conf spark.blockManager.port=39001 \
  /opt/spark/apps/first_app.py "$@"
first_app.py 전체 원본 읽기
"""Week 2: submit, verify a known answer, observe the UI, and stop."""
import argparse
import time
from pyspark.sql import SparkSession

parser = argparse.ArgumentParser()
parser.add_argument("--wait-seconds", type=int, default=120)
args = parser.parse_args()

spark = SparkSession.builder.appName("Week02-First-App").getOrCreate()
try:
    spark.sparkContext.setLogLevel("WARN")
    print(f"MASTER={spark.sparkContext.master}", flush=True)
    print(f"APP_ID={spark.sparkContext.applicationId}", flush=True)
    assert spark.sparkContext.master == "spark://spark-master:7077"

    # Four input partitions. DataFrame syntax is covered next week.
    numbers = spark.range(1, 101, 1, 4)
    total = numbers.selectExpr("sum(id) AS total").first()["total"]
    print(f"SUM_1_TO_100={total}", flush=True)
    assert total == 5050, f"Unexpected result: {total}"
    print("CHECK=PASS", flush=True)
    print(f"UI observation: {max(0, args.wait_seconds)} seconds", flush=True)
    time.sleep(max(0, args.wait_seconds))
finally:
    spark.stop()
print("APP_STOPPED", flush=True)
  • argparse: 실행 인수를 읽습니다.
  • try: 계산을 수행하는 부분입니다.
  • finally: 정상 진행이나 Python 예외 시 정리를 시도하는 부분입니다.
  • time.sleep: 기본 120초 동안 UI를 볼 시간을 남깁니다.
  • spark.stop(): SparkSession을 종료합니다.

앱 제출

입력 위치: 실습 폴더의 Windows PowerShell. 앱은 Client 컨테이너에서 실행됩니다.

  1. 첫 줄로 앱을 제출하고 다음 절의 UI를 120초 동안 관찰합니다.
  2. 프롬프트가 돌아오면 즉시 두 번째 줄로 종료 코드를 확인합니다.
docker compose exec spark-client bash /opt/spark/apps/run.sh
$LASTEXITCODE

예상 출력

MASTER=spark://spark-master:7077
SUM_1_TO_100=5050
CHECK=PASS
UI observation: 120 seconds
APP_STOPPED

APP_ID=…도 출력되며 값은 실행마다 달라질 수 있습니다. 성공 여부는 아래 네 표시를 함께 확인하세요.

  • SUM_1_TO_100=5050: 합계가 정답과 일치합니다.
  • CHECK=PASS: 코드 안의 검증을 통과했습니다.
  • APP_STOPPED: 관찰 대기와 SparkSession 종료 후에 나오는 표시입니다.
  • 종료 코드 0: 실행 명령이 정상 종료됐습니다.

입력 partition이 4개라면 반드시 4개 core가 동시에 일하나요?

출처: 강의 슬라이드 44–50 · run.sh · first_app.py. 분할 그림과 손계산은 학습용 보충 설명입니다.

09

UI와 로그로 실행 상태 확인하기

계산 결과와 함께 로그·UI를 보면 앱이 어떻게 실행됐는지 확인할 수 있습니다.

학습용 비유 · 답안과 풀이 과정 함께 보기

정답과 풀이 과정을 함께 보듯, 코드의 검증 결과와 UI·로그를 함께 확인합니다.

Master UI · 자원과 앱

localhost:8080 열기

  • ALIVE Worker가 두 개 있는지
  • 관찰 대기 중 Running Applications에 앱이 있는지
  • 각 Worker에 Executor가 배정됐는지

Driver UI · 작업의 진행

localhost:4040 열기

  • Jobs: 실행 요청과 완료 상태를 확인합니다.
  • Stages: 작업을 나눈 처리 단계를 확인합니다.
  • Executors: Driver와 Executor 정보를 확인합니다.

합계 계산은 빠르게 끝날 수 있습니다. 120초 대기 시간에는 이미 완료된 작업과 앱 상태를 확인합니다.

앱 종료 후 달라지는 상태
  • Driver·4040 UI: 관찰 대기가 끝나고 앱이 종료되면 함께 사라집니다.
  • Client 컨테이너: 앱 종료 후에도 계속 대기할 수 있습니다.

화면이 기대와 다르면 최근 로그부터

실행 위치: 같은 실습 폴더의 Windows PowerShell. 앱을 관찰 중이라면 두 번째 PowerShell을 열어 같은 폴더로 이동한 뒤 실행합니다.

docker compose logs --tail 80 spark-master spark-worker-1 spark-worker-2

실시간 추적은 아래 명령을 사용합니다. Ctrl+C로 로그 추적을 끝냅니다.

docker compose logs -f spark-worker-1
증상별 점검 항목
증상먼저 확인할 점
Docker Server 연결 실패Desktop이 실행 중인지, Engine running인지, WSL 상태가 정상인지 확인합니다.
Compose 파일을 못 찾음현재 폴더에 compose.yaml이 있는지 Get-ChildItem으로 봅니다.
포트 사용 중 오류오류에 나온 8080·8081·8082·4040을 다른 프로그램이나 이전 컨테이너가 사용 중인지 확인합니다. 실습 구성의 주소를 임의로 바꾸기 전에 충돌 원인을 찾습니다.
Master unhealthy / Worker 미등록docker compose ps -a와 Master·Worker 로그를 봅니다. healthcheck 통과와 Worker 등록을 각각 확인합니다.
DNS_FAIL / TCP_FAIL네 서비스 상태, 서비스 이름, 컨테이너 내부 포트를 확인합니다. 내부 Worker 2 포트는 8081입니다.
4040이 열리지 않음앱 제출 전이거나 120초 후 종료된 것은 아닌지 확인합니다. 실행 중이면 제출 로그와 Client 상태를 확인합니다.
스크립트 오류 / 파일을 못 찾음apps 파일과 bind mount 경로, UTF-8·LF 줄바꿈을 확인합니다. 원본 복사본을 사용할 때도 형식을 유지합니다.
CHECK=PASS가 없거나 종료 코드가 0이 아님오류가 난 첫 지점과 Master 주소·이미지 태그·Executor 상태를 확인합니다. 성공으로 기록하지 말고 로그를 근거로 원인을 좁힙니다.

확인 질문: 합계가 보인 후 4040이 닫혔습니다. 무조건 장애일까요? APP_STOPPED와 종료 코드 0이 확인되고 관찰 시간이 끝났다면 정상적인 종료입니다.

출처: 강의 슬라이드 51–52 · README의 관찰·종료 확인. 증상별 점검 표는 보충 안내입니다.

10

컨테이너 중지·재시작·정리하기

앱이 종료돼도 컨테이너는 남을 수 있습니다. 실습을 잠시 중단할 때는 컨테이너를 중지하고, 마칠 때는 제거합니다.

학습용 비유 · 일시 정지와 자리 정리

stop은 자리를 남긴 채 멈추기, start는 다시 시작하기, down은 자리 정리에 비유할 수 있습니다. 이미지와 호스트의 실습 파일은 남습니다.

종료 명령이 남기는 상태
명령컨테이너다음 행동
stop중지된 상태로 남음start로 같은 컨테이너 재시작
start기존의 중지된 컨테이너 실행Master 상태와 Worker 재등록 확인
down이 Compose 프로젝트의 컨테이너와 기본 네트워크 제거다음에는 사용할 네 서비스를 지정해 up -d로 다시 구성

잠깐 중지하고 다시 시작할 때

실행 위치: 실습 폴더의 Windows PowerShell. 앱이 정상 종료된 뒤 실행합니다.

docker compose stop
docker compose ps -a
docker compose start spark-master spark-worker-1 spark-worker-2 spark-client
docker compose ps -a

실습을 끝낼 때

docker compose down
docker compose ps -a

확인: 이 프로젝트의 컨테이너 목록이 비어야 합니다. Spark 이미지와 Windows의 apps 파일은 다음 실습에서 재사용합니다.

다시 시작할 때는 클러스터 실행 단계로 돌아가 네 서비스를 지정한 명령을 사용하세요.

학습 확인 항목

docker compose down 후 다음 수업에 다시 구성하려면?

강의 제공 코드와 실행 조건 확인

아래는 강의에서 제공한 실습 파일입니다. 같은 폴더 구조로 저장한 뒤 이 장의 준비·검사·제출 순서로 실행합니다.

실행 후에는 위 확인 항목으로 이미지·등록·통신·계산·종료 상태를 각각 점검합니다.

출처: 강의 슬라이드 53–56 · README의 종료 절차.