Spark 스탠드얼론 모드
Spark 스탠드얼론 모드 (Spark Standalone Mode)
YARN 클러스터 매니저에서 실행하는 것 외에도, Spark는 간단한 스탠드얼론(standalone) 배포 모드를 제공해요. 마스터와 워커를 손으로 시작해 수동으로 클러스터를 실행하거나, 제공되는 실행 스크립트를 사용할 수 있어요. 테스트를 위해 이 데몬들을 단일 머신에서 실행하는 것도 가능해요.
본문
보안(Security)
인증 같은 보안 기능은 기본적으로 활성화되지 않아요. 인터넷이나 신뢰할 수 없는 네트워크에 열려 있는 클러스터를 배포할 때는, 권한 없는 애플리케이션이 클러스터에서 실행되지 않도록 클러스터 접근을 보호하는 것이 중요해요. Spark를 실행하기 전에 Spark Security와 이 문서의 관련 보안 섹션을 꼭 보세요.
클러스터에 Spark Standalone 설치하기
Spark Standalone 모드를 설치하려면 컴파일된 Spark 버전을 클러스터의 각 노드에 배치하면 돼요. 각 릴리스에서 미리 빌드된 Spark 버전을 얻거나 직접 빌드할 수 있어요.
수동으로 클러스터 시작하기
다음을 실행해 스탠드얼론 마스터 서버를 시작할 수 있어요.
./sbin/start-master.sh
시작되면 마스터는 자신의 spark://HOST:PORT URL을 출력하는데, 이 URL로 워커를 연결하거나 SparkContext에 "master" 인자로 전달할 수 있어요. 이 URL은 마스터의 웹 UI에서도 찾을 수 있는데, 기본적으로 http://localhost:8080이에요.
마찬가지로 다음을 통해 하나 이상의 워커를 시작하고 마스터에 연결할 수 있어요.
./sbin/start-worker.sh <master-spark-URL>
워커를 시작한 뒤 마스터의 웹 UI(http://localhost:8080 기본값)를 보세요. 새 노드가 CPU 수와 메모리(OS용으로 1기가를 뺀)와 함께 나열된 것을 볼 수 있을 거예요.
마지막으로 다음 구성 옵션을 마스터와 워커에 전달할 수 있어요.
| 인자 | 의미 |
|---|---|
| -h HOST, --host HOST | 수신할 호스트 이름 |
| -p PORT, --port PORT | 서비스가 수신할 포트 (기본: 마스터 7077, 워커는 랜덤) |
| --webui-port PORT | 웹 UI용 포트 (기본: 마스터 8080, 워커 8081) |
| -c CORES, --cores CORES | Spark 애플리케이션이 머신에서 사용할 수 있는 총 CPU 코어 수 (기본: 사용 가능한 전체); 워커에만 해당 |
| -m MEM, --memory MEM | Spark 애플리케이션이 머신에서 사용할 수 있는 총 메모리, 1000M 또는 2G 같은 형식 (기본: 머신 전체 RAM - 1GiB); 워커에만 해당 |
| -d DIR, --work-dir DIR | 임시 공간과 작업 출력 로그용 디렉터리 (기본: SPARK_HOME/work); 워커에만 해당 |
| --properties-file FILE | 로드할 사용자 지정 Spark 속성 파일 경로 (기본: conf/spark-defaults.conf) |
클러스터 실행 스크립트(Cluster Launch Scripts)
실행 스크립트로 Spark 스탠드얼론 클러스터를 실행하려면 Spark 디렉터리에 conf/workers라는 파일을 만들어야 해요. 이 파일에는 Spark 워커를 시작할 모든 머신의 호스트 이름을 한 줄에 하나씩 포함해야 해요. conf/workers가 없으면 실행 스크립트는 단일 머신(localhost)을 기본으로 하는데, 테스트에 유용해요. 참고로 마스터 머신은 ssh로 각 워커 머신에 접근해요. 기본적으로 ssh는 병렬로 실행되며, 비밀번호 없이(개인 키를 사용해) 접근하도록 설정해야 해요. 비밀번호 없는 설정이 없다면 SPARK_SSH_FOREGROUND 환경 변수를 설정해 각 워커에 비밀번호를 직렬로 제공할 수 있어요.
이 파일을 설정한 뒤, Hadoop의 배포 스크립트를 기반으로 하며 SPARK_HOME/sbin에 있는 다음 셸 스크립트로 클러스터를 시작하거나 중지할 수 있어요.
sbin/start-master.sh— 스크립트가 실행된 머신에서 마스터 인스턴스를 시작해요.sbin/start-workers.sh—conf/workers파일에 지정된 각 머신에서 워커 인스턴스를 시작해요.sbin/start-worker.sh— 스크립트가 실행된 머신에서 워커 인스턴스를 시작해요.sbin/start-connect-server.sh— 스크립트가 실행된 머신에서 Spark Connect 서버를 시작해요.sbin/start-all.sh— 위에서 설명한 대로 마스터와 여러 워커를 모두 시작해요.sbin/stop-master.sh—sbin/start-master.sh스크립트로 시작된 마스터를 중지해요.sbin/stop-worker.sh— 스크립트가 실행된 머신의 모든 워커 인스턴스를 중지해요.sbin/stop-workers.sh—conf/workers파일에 지정된 머신의 모든 워커 인스턴스를 중지해요.sbin/stop-connect-server.sh— 스크립트가 실행된 머신의 모든 Spark Connect 서버 인스턴스를 중지해요.sbin/stop-all.sh— 위에서 설명한 대로 마스터와 워커를 모두 중지해요.
이 스크립트들은 로컬 머신이 아니라 Spark 마스터를 실행하려는 머신에서 실행해야 한다는 점에 유의하세요.
conf/spark-env.sh에 환경 변수를 설정해 클러스터를 추가로 구성할 수 있어요. conf/spark-env.sh.template에서 시작해 이 파일을 만들고, 설정이 적용되도록 모든 워커 머신에 복사하세요. 다음 설정을 사용할 수 있어요.
| 환경 변수 | 의미 |
|---|---|
| SPARK_MASTER_HOST | 마스터를 특정 호스트 이름이나 IP 주소(예: 공인 주소)에 바인딩해요. |
| SPARK_MASTER_PORT | 마스터를 다른 포트에서 시작해요 (기본: 7077). |
| SPARK_MASTER_WEBUI_PORT | 마스터 웹 UI용 포트 (기본: 8080). |
| SPARK_MASTER_OPTS | "-Dx=y" 형식으로 마스터에만 적용되는 구성 속성 (기본: 없음). 가능한 옵션 목록은 아래 참고. |
| SPARK_LOCAL_DIRS | Spark에서 "임시" 공간으로 쓸 디렉터리. map 출력 파일과 디스크에 저장되는 RDD를 포함해요. 시스템의 빠른 로컬 디스크여야 해요. 여러 디스크의 여러 디렉터리를 쉼표로 구분한 목록일 수도 있어요. |
| SPARK_LOG_DIR | 로그 파일이 저장되는 곳 (기본: SPARK_HOME/logs). |
| SPARK_LOG_MAX_FILES | 최대 로그 파일 수 (기본: 5). |
| SPARK_PID_DIR | pid 파일이 저장되는 곳 (기본: /tmp). |
| SPARK_WORKER_CORES | Spark 애플리케이션이 머신에서 사용할 수 있는 총 코어 수 (기본: 사용 가능한 전체 코어). |
| SPARK_WORKER_MEMORY | Spark 애플리케이션이 머신에서 사용할 수 있는 총 메모리, 예: 1000m, 2g (기본: 전체 메모리 - 1GiB); 각 애플리케이션의 개별 메모리는 spark.executor.memory 속성으로 구성한다는 점에 유의하세요. |
| SPARK_WORKER_PORT | Spark 워커를 특정 포트에서 시작해요 (기본: 랜덤). |
| SPARK_WORKER_WEBUI_PORT | 워커 웹 UI용 포트 (기본: 8081). |
| SPARK_WORKER_DIR | 로그와 임시 공간을 모두 포함할 애플리케이션 실행 디렉터리 (기본: SPARK_HOME/work). |
| SPARK_WORKER_OPTS | "-Dx=y" 형식으로 워커에만 적용되는 구성 속성 (기본: 없음). 가능한 옵션 목록은 아래 참고. |
| SPARK_DAEMON_MEMORY | Spark 마스터와 워커 데몬 자체에 할당할 메모리 (기본: 1g). |
| SPARK_DAEMON_JAVA_OPTS | "-Dx=y" 형식으로 Spark 마스터와 워커 데몬 자체에 대한 JVM 옵션 (기본: 없음). |
| SPARK_DAEMON_CLASSPATH | Spark 마스터와 워커 데몬 자체에 대한 클래스패스 (기본: 없음). |
| SPARK_PUBLIC_DNS | Spark 마스터와 워커의 공인 DNS 이름 (기본: 없음). |
참고: 실행 스크립트는 현재 Windows를 지원하지 않아요. Windows에서 Spark 클러스터를 실행하려면 마스터와 워커를 손으로 시작하세요.
SPARK_MASTER_OPTS는 다음 시스템 속성을 지원해요.
| 속성 이름 | 기본값 | 의미 | 버전 |
|---|---|---|---|
| spark.master.ui.port | 8080 | Master Web UI 엔드포인트의 포트 번호를 지정해요. | 1.1.0 |
| spark.master.ui.title | (None) | Master UI 페이지의 제목을 지정해요. 설정하지 않으면 기본적으로 'master url'에서 Spark Master를 사용해요. | 4.0.0 |
| spark.master.ui.decommission.allow.mode | LOCAL | Master Web UI의 /workers/kill 엔드포인트 동작을 지정해요. 가능한 값: LOCAL은 Master가 실행되는 머신에 로컬인 IP에서 이 엔드포인트를 허용, DENY는 이 엔드포인트를 완전히 비활성화, ALLOW는 어떤 IP에서든 이 엔드포인트 호출을 허용. | 3.1.0 |
| spark.master.ui.historyServerUrl | (None) | Spark history server가 실행 중인 URL. 모든 Spark 작업이 history server가 접근하는 같은 이벤트 로그 위치를 공유한다고 가정한다는 점에 유의하세요. | 4.0.0 |
| spark.master.rest.enabled | true | Master REST API 엔드포인트를 사용할지 여부. | 1.3.0 |
| spark.master.rest.host | (None) | Master REST API 엔드포인트의 호스트를 지정해요. | 4.0.0 |
| spark.master.rest.port | 6066 | Master REST API 엔드포인트의 포트 번호를 지정해요. | 1.3.0 |
| spark.master.rest.filters | (None) | Master REST API에 적용할 필터 클래스 이름의 쉼표 구분 목록. | 4.0.0 |
| spark.master.useAppNameAsAppId.enabled | false | (실험적) true면 Spark master가 appId에 사용자가 제공한 appName을 사용해요. | 4.0.0 |
| spark.deploy.retainedApplications | 200 | 표시할 완료된 애플리케이션의 최대 수. 이 제한을 유지하기 위해 오래된 애플리케이션은 UI에서 제거돼요. | 0.8.0 |
| spark.deploy.retainedDrivers | 200 | 표시할 완료된 드라이버의 최대 수. 이 제한을 유지하기 위해 오래된 드라이버는 UI에서 제거돼요. | 1.1.0 |
| spark.deploy.spreadOutDrivers | true | 스탠드얼론 클러스터 매니저가 드라이버를 노드 전체에 퍼뜨릴지, 아니면 가능한 한 적은 노드에 모을지. 퍼뜨리는 것이 보통 HDFS의 데이터 지역성에 좋지만, 컴퓨팅 집약적 워크로드에는 모으는 것이 더 효율적이에요. | 4.0.0 |
| spark.deploy.spreadOutApps | true | 스탠드얼론 클러스터 매니저가 애플리케이션을 노드 전체에 퍼뜨릴지, 아니면 가능한 한 적은 노드에 모을지. 퍼뜨리는 것이 보통 HDFS의 데이터 지역성에 좋지만, 컴퓨팅 집약적 워크로드에는 모으는 것이 더 효율적이에요. | 0.6.1 |
| spark.deploy.defaultCores | Int.MaxValue | 스탠드얼론 모드에서 spark.cores.max를 설정하지 않은 애플리케이션에 줄 기본 코어 수. 설정하지 않으면 애플리케이션은 spark.cores.max를 설정하지 않는 한 항상 사용 가능한 모든 코어를 얻어요. 공유 클러스터에서는 사용자가 기본적으로 전체 클러스터를 차지하지 못하도록 이 값은 낮추는 게 좋아요. | 0.9.0 |
| spark.deploy.maxExecutorRetries | 10 | 스탠드얼론 클러스터 매니저가 잘못된 애플리케이션을 제거하기 전에 발생할 수 있는 연속 실행자 실패의 최대 수. 실행 중인 실행자가 있으면 애플리케이션은 절대 제거되지 않아요. 애플리케이션이 spark.deploy.maxExecutorRetries보다 많은 실패를 연속으로 겪고, 그 실패 사이에 성공적으로 시작된 실행자가 없으며, 실행 중인 실행자도 없으면 스탠드얼론 클러스터 매니저는 애플리케이션을 제거하고 실패로 표시해요. 이 자동 제거를 비활성화하려면 spark.deploy.maxExecutorRetries를 -1로 설정하세요. | 1.6.3 |
| spark.deploy.maxDrivers | Int.MaxValue | 실행 중인 드라이버의 최대 수. | 4.0.0 |
| spark.deploy.appNumberModulo | (None) | 앱 번호의 모듈로. 기본적으로 app-yyyyMMddHHmmss-9999의 다음은 app-yyyyMMddHHmmss-10000이에요. 모듈로를 10000으로 하면 app-yyyyMMddHHmmss-0000이 돼요. 대부분의 경우 10000개 앱을 만드는 동안 접두사 app-yyyyMMddHHmmss가 이미 증가돼요. | 4.0.0 |
| spark.deploy.driverIdPattern | driver-%s-%04d | Java String.format 메서드 기반 드라이버 ID 생성 패턴. 기본값은 driver-%s-%04d로 기존 드라이버 id 문자열(예: driver-20231031224459-0019)을 나타내요. 고유한 ID를 생성하도록 주의하세요. | 4.0.0 |
| spark.deploy.appIdPattern | app-%s-%04d | Java String.format 메서드 기반 앱 ID 생성 패턴. 기본값은 app-%s-%04d로 기존 앱 id 문자열(예: app-20231031224509-0008)을 나타내요. 고유한 ID를 생성하도록 주의하세요. | 4.0.0 |
| spark.worker.timeout | 60 | 스탠드얼론 배포 마스터가 하트비트를 받지 못하면 워커가 유실된 것으로 간주하는 시간(초). | 0.6.2 |
| spark.dead.worker.persistence | 15 | UI에서 죽은 워커 정보를 유지할 반복 횟수. 기본적으로 죽은 워커는 마지막 하트비트 이후 (15 + 1) * spark.worker.timeout 동안 보여요. | 0.8.0 |
| spark.worker.resource.{name}.amount | (none) | 워커에서 사용할 특정 리소스의 양. | 3.0.0 |
| spark.worker.resource.{name}.discoveryScript | (none) | 워커 시작 시 특정 리소스를 찾는 데 쓰는 리소스 탐색 스크립트 경로. 스크립트 출력은 ResourceInformation 클래스처럼 형식화돼야 해요. | 3.0.0 |
| spark.worker.resourcesFile | (none) | 워커 시작 시 다양한 리소스를 찾는 데 쓰는 리소스 파일 경로. 리소스 파일 내용은 [{"id":{"componentName": "spark.worker", "resourceName":"gpu"}, "addresses":["0","1","2"]}]처럼 형식화돼야 해요. 특정 리소스가 리소스 파일에서 발견되지 않으면 그 리소스를 찾기 위해 탐색 스크립트를 사용해요. 탐색 스크립트도 리소스를 찾지 못하면 워커가 시작에 실패해요. | 3.0.0 |
SPARK_WORKER_OPTS는 다음 시스템 속성을 지원해요.
| 속성 이름 | 기본값 | 의미 | 버전 |
|---|---|---|---|
| spark.worker.initialRegistrationRetries | 6 | 짧은 간격(5~15초)으로 재연결하는 재시도 횟수. | 4.0.0 |
| spark.worker.maxRegistrationRetries | 16 | 재연결의 최대 재시도 횟수. spark.worker.initialRegistrationRetries 시도 후 간격은 30~90초예요. | 4.0.0 |
| spark.worker.cleanup.enabled | true | 워커/애플리케이션 디렉터리의 주기적 정리를 활성화해요. YARN은 다르게 동작하므로 이는 스탠드얼론 모드에만 영향을 줘요. 중지된 애플리케이션의 디렉터리만 정리돼요. spark.shuffle.service.db.enabled가 "true"이면 활성화해야 해요. | 1.0.0 |
| spark.worker.cleanup.interval | 1800 (30분) | 워커가 로컬 머신의 오래된 애플리케이션 작업 디렉터리를 정리하는 간격(초). | 1.0.0 |
| spark.worker.cleanup.appDataTtl | 604800 (7일, 7 * 24 * 3600) | 각 워커에 애플리케이션 작업 디렉터리를 보존하는 시간(초). 이는 시간 투 라이브(TTL)이며 사용 가능한 디스크 공간에 따라 달라져야 해요. 애플리케이션 로그와 jar는 각 애플리케이션 작업 디렉터리로 다운로드돼요. 시간이 지나면 작업 디렉터리가 특히 매우 자주 작업을 실행하면 디스크 공간을 빠르게 채울 수 있어요. | 1.0.0 |
| spark.shuffle.service.db.enabled | true | External Shuffle 서비스 상태를 로컬 디스크에 저장해 외부 shuffle 서비스가 재시작될 때 현재 실행자에 대한 정보를 자동으로 다시 로드하게 해요. 이는 스탠드얼론 모드에만 영향을 줘요(yarn은 항상 이 동작이 활성화돼 있어요). 상태가 결국 정리되도록 spark.worker.cleanup.enabled도 활성화해야 해요. 이 구성은 미래에 제거될 수 있어요. | 3.0.0 |
| spark.shuffle.service.db.backend | ROCKSDB | spark.shuffle.service.db.enabled가 true일 때 shuffle 서비스 상태 저장소에서 사용하는 디스크 기반 저장소 종류를 지정할 수 있어요. 현재 ROCKSDB와 LEVELDB(더 이상 권장되지 않음)를 지원하며 기본값은 ROCKSDB예요. RocksDB/LevelDB의 원래 데이터 저장소는 자동으로 다른 종류의 저장소로 변환되지 않아요. | 3.4.0 |
| spark.storage.cleanupFilesAfterExecutorExit | true | 실행자 종료 후 워커 디렉터리의 비-shuffle 파일(임시 shuffle 블록, 캐시된 RDD/broadcast 블록, 넘침 파일 등) 정리를 활성화해요. 이는 spark.worker.cleanup.enabled와 겹치지 않는데, 이는 죽은 실행자의 로컬 디렉터리에서 비-shuffle 파일 정리를 가능하게 하고, spark.worker.cleanup.enabled는 중지되고 타임아웃된 애플리케이션의 모든 파일/하위 디렉터리 정리를 가능하게 하기 때문이에요. 이는 스탠드얼론 모드에만 영향을 주며, 다른 클러스터 매니저 지원은 미래에 추가될 수 있어요. | 2.4.0 |
| spark.worker.ui.compressedLogFileLengthCacheSize | 100 | 압축 로그 파일의 경우 압축 해제된 파일 크기는 파일을 풀어야만 계산할 수 있어요. Spark은 압축 로그 파일의 압축 해제 크기를 캐시해요. 이 속성은 캐시 크기를 제어해요. | 2.0.2 |
| spark.worker.idPattern | worker-%s-%s-%d | Java String.format 메서드 기반 워커 ID 생성 패턴. 기본값은 worker-%s-%s-%d로 기존 워커 id 문자열(예: worker-20231109183042-[fe80::1%lo0]-39729)을 나타내요. 고유한 ID를 생성하도록 주의하세요. | 4.0.0 |
리소스 할당과 구성 개요
구성 페이지의 Custom Resource Scheduling and Configuration Overview 섹션을 읽었는지 확인하세요. 이 섹션은 Spark Standalone 고유의 리소스 스케줄링 측면만 다뤄요.
Spark Standalone에는 두 부분이 있는데, 첫 번째는 Worker의 리소스를 구성하는 것, 두 번째는 특정 애플리케이션에 대한 리소스 할당이에요.
사용자는 Worker가 Executor에 할당할 수 있도록 일련의 리소스를 사용할 수 있게 구성해야 해요. spark.worker.resource.{resourceName}.amount는 워커가 할당한 각 리소스의 양을 제어하는 데 쓰여요. 또한 워커가 할당된 리소스를 어떻게 발견하는지 지정하려면 spark.worker.resourcesFile 또는 spark.worker.resource.{resourceName}.discoveryScript 중 하나를 지정해야 해요. 각각의 설명을 위에서 확인해 자신의 설정에 어떤 방법이 가장 잘 맞는지 보세요.
두 번째 부분은 Spark Standalone에서 애플리케이션을 실행하는 것이에요. 표준 Spark 리소스 구성과의 유일한 특수 사례는 Driver를 client 모드에서 실행할 때예요. client 모드의 Driver는 spark.driver.resourcesFile 또는 spark.driver.resource.{resourceName}.discoveryScript로 자신이 사용할 리소스를 지정할 수 있어요. Driver가 다른 Driver와 같은 호스트에서 실행된다면, 리소스 파일이나 탐색 스크립트가 같은 노드에서 실행 중인 다른 Driver와 충돌하지 않는 리소스만 반환하는지 확인하세요.
참고로, 애플리케이션 제출 시 사용자가 탐색 스크립트를 지정할 필요가 없어요. Worker가 할당한 리소스로 각 Executor를 시작하니까요.
애플리케이션을 클러스터에 연결하기
Spark 클러스터에서 애플리케이션을 실행하려면 마스터의 spark://IP:PORT URL을 SparkContext 생성자에 전달하면 돼요.
클러스터에 대화형 Spark 셸을 실행하려면 다음 명령을 실행해요.
./bin/spark-shell --master spark://IP:PORT
또한 --total-executor-cores <numCores> 옵션을 전달해 spark-shell이 클러스터에서 사용할 코어 수를 제어할 수 있어요.
클라이언트 속성(Client Properties)
Spark 애플리케이션은 스탠드얼론 모드 고유의 다음 구성 속성을 지원해요.
| 속성 이름 | 기본값 | 의미 | 버전 |
|---|---|---|---|
| spark.standalone.submit.waitAppCompletion | false | 스탠드얼론 cluster 모드에서 클라이언트가 애플리케이션이 완료될 때까지 기다렸다가 종료할지 제어해요. true로 설정하면 클라이언트 프로세스가 드라이버의 상태를 폴링하며 살아 있게 돼요. 그렇지 않으면 클라이언트 프로세스는 제출 후 종료돼요. | 3.1.0 |
Spark 애플리케이션 실행하기
Spark 프로토콜
spark-submit 스크립트는 컴파일된 Spark 애플리케이션을 클러스터에 제출하는 가장 간단한 방법을 제공해요. 스탠드얼론 클러스터는 현재 두 가지 배포 모드를 지원해요. client 모드에서는 드라이버가 애플리케이션을 제출하는 클라이언트와 같은 프로세스에서 실행돼요. 그러나 cluster 모드에서는 드라이버가 클러스터 내부의 Worker 프로세스 중 하나에서 실행되고, 클라이언트 프로세스는 애플리케이션 제출이라는 책임을 다하는 즉시, 애플리케이션이 끝나기를 기다리지 않고 종료돼요.
애플리케이션이 Spark submit으로 실행되면 애플리케이션 jar가 모든 워커 노드에 자동으로 배포돼요. 애플리케이션이 의존하는 추가 jar는 콤마를 구분자로 --jars 플래그로 지정해야 해요(예: --jars jar1,jar2). 애플리케이션의 구성이나 실행 환경을 제어하려면 Spark Configuration을 보세요.
추가로 스탠드얼론 cluster 모드는 애플리케이션이 0이 아닌 종료 코드로 종료됐을 때 자동으로 재시작하는 것을 지원해요. 이 기능을 쓰려면 애플리케이션 실행 시 spark-submit에 --supervise 플래그를 전달할 수 있어요. 그런 다음 반복적으로 실패하는 애플리케이션을 종료하려면 다음을 통해 종료할 수 있어요.
./bin/spark-class org.apache.spark.deploy.Client kill <master url> <driver ID>
드라이버 ID는 스탠드얼론 Master 웹 UI(http://<master url>:8080)에서 찾을 수 있어요.
REST API
spark.master.rest.enabled가 활성화되면 Spark 마스터는 http://[host:port]/[version]/submissions/[action]을 통해 추가 REST API를 제공해요. 여기서 host는 마스터 호스트, port는 spark.master.rest.port(기본: 6066)로 지정된 포트 번호, version은 프로토콜 버전(현재는 v1), action은 다음 지원되는 동작 중 하나예요.
| 명령 | HTTP 메서드 | 설명 | 버전 |
|---|---|---|---|
| create | POST | cluster 모드로 Spark 드라이버를 생성해요. 4.0.0부터 Spark master는 Spark 속성과 환경 변수 값에 대한 서버 측 변수 대체를 지원해요. | 1.3.0 |
| kill | POST | 단일 Spark 드라이버를 종료해요. | 1.3.0 |
| killall | POST | 실행 중인 모든 Spark 드라이버를 종료해요. | 4.0.0 |
| status | GET | Spark 작업의 상태를 확인해요. | 1.3.0 |
| clear | POST | 완료된 드라이버와 애플리케이션을 정리해요. | 4.0.0 |
pi.py와 REST API를 사용한 curl CLI 명령 예시는 다음과 같아요.
$ curl -XPOST http://IP:PORT/v1/submissions/create \
--header "Content-Type:application/json;charset=UTF-8" \
--data '{
"appResource": "",
"sparkProperties": {
"spark.master": "spark://master:7077",
"spark.app.name": "Spark Pi",
"spark.driver.memory": "1g",
"spark.driver.cores": "1",
"spark.jars": ""
},
"clientSparkVersion": "",
"mainClass": "org.apache.spark.deploy.SparkSubmit",
"environmentVariables": { },
"action": "CreateSubmissionRequest",
"appArgs": [ "/opt/spark/examples/src/main/python/pi.py", "10" ]
}'
위 create 요청에 대한 REST API 응답은 다음과 같아요.
{
"action" : "CreateSubmissionResponse",
"message" : "Driver successfully submitted as driver-20231124153531-0000",
"serverSparkVersion" : "4.0.0",
"submissionId" : "driver-20231124153531-0000",
"success" : true
}
Spark 마스터가 spark.master.rest.filters=org.apache.spark.ui.JWSFilter 및 spark.org.apache.spark.ui.JWSFilter.param.secretKey=BASE64URL-ENCODED-KEY 구성으로 HTTP Authorization 헤더를 요구할 때, curl CLI 명령은 다음과 같이 필요한 헤더를 제공할 수 있어요.
$ curl -XPOST http://IP:PORT/v1/submissions/create \
--header "Authorization: Bearer USER-P...-KEY"
...
sparkProperties와 environmentVariables에 대해 사용자는 다음과 같이 서버 측 환경 변수용 자리 표시자를 사용할 수 있어요.
...
"sparkProperties": {
"spark.hadoop.fs.s3a.endpoint": "{{AWS_ENDPOINT_URL}}",
"spark.hadoop.fs.s3a.endpoint.region": "{{AWS_REGION}}"
},
"environmentVariables": {
"AWS_CA_BUNDLE": "{{AWS_CA_BUNDLE}}"
},
...
리소스 스케줄링(Resource Scheduling)
스탠드얼론 cluster 모드는 현재 애플리케이션 간 단순한 FIFO 스케줄러만 지원해요. 하지만 여러 동시 사용자를 허용하려면 각 애플리케이션이 사용할 최대 리소스 수를 제어할 수 있어요. 기본적으로 클러스터의 모든 코어를 획득하는데, 이는 한 번에 애플리케이션 하나만 실행한다면 말이 돼요. SparkConf에서 spark.cores.max를 설정해 코어 수를 제한할 수 있어요. 예:
val conf = new SparkConf()
.setMaster(...)
.setAppName(...)
.set("spark.cores.max", "10")
val sc = new SparkContext(conf)
추가로 클러스터 마스터 프로세스에서 spark.deploy.defaultCores를 구성해 spark.cores.max를 설정하지 않는 애플리케이션의 기본값을 무한대보다 작게 바꿀 수 있어요. conf/spark-env.sh에 다음을 추가해서요.
export SPARK_MASTER_OPTS="-Dspark.deploy.defaultCores=<value>"
이것은 사용자가 개별적으로 최대 코어 수를 설정하지 않았을 수 있는 공유 클러스터에서 유용해요.
실행자 스케줄링(Executors Scheduling)
각 실행자에 할당되는 코어 수는 구성 가능해요. spark.executor.cores가 명시적으로 설정되면, 워커가 충분한 코어와 메모리를 가질 때 같은 애플리케이션의 여러 실행자가 같은 워커에서 시작될 수 있어요. 그렇지 않으면 각 실행자가 기본적으로 워커에서 사용 가능한 모든 코어를 가져가며, 이 경우 단일 스케줄 반복 동안 각 워커에서 애플리케이션당 실행자 하나만 시작될 수 있어요.
스테이지 레벨 스케줄링 개요
스테이지 레벨 스케줄링은 Standalone에서 지원돼요.
- 동적 할당이 비활성화되면: 사용자가 스테이지 레벨에서 다른 태스크 리소스 요구사항을 지정할 수 있고, 시작 시 요청된 같은 실행자를 사용해요.
- 동적 할당이 활성화되면: 현재 Master가 애플리케이션 하나에 실행자를 할당할 때, 여러 ResourceProfile에 대해 ResourceProfile id 순서에 따라 스케줄링해요. 더 작은 id의 ResourceProfile이 먼저 스케줄링돼요. 보통 Spark이 한 스테이지를 끝내고 다른 스테이지를 시작하므로 이는 문제가 되지 않지만, 이것이 영향을 줄 수 있는 유일한 경우는 job server 유형의 시나리오이므로 염두에 두세요. 스케줄링을 위해 내장 실행자 리소스에서 실행자 메모리와 실행자 코어만 가져오고, ResourceProfile에서 다른 모든 사용자 지정 리소스를 가져와요. offHeap, memoryOverhead 같은 다른 내장 실행자 리소스는 효과가 없어요. 기본 default 프로파일은 애플리케이션 제출 시 spark 구성에 기반해 생성돼요. 기본 default 프로파일의 실행자 메모리와 실행자 코어는 사용자 지정 ResourceProfile로 전파될 수 있지만, 다른 모든 사용자 지정 리소스는 전파될 수 없어요.
주의사항(Caveats)
Dynamic Resource Allocation에서 언급했듯이, 동적 할당이 활성화된 상태에서 각 실행자의 코어가 명시적으로 지정되지 않으면 spark은 예상보다 훨씬 많은 실행자를 획득할 수 있어요. 따라서 스테이지 레벨 스케줄링을 사용할 때 각 리소스 프로파일에 실행자 코어를 명시적으로 설정하는 것이 좋아요.
모니터링과 로깅(Monitoring and Logging)
Spark의 스탠드얼론 모드는 클러스터를 모니터링하기 위한 웹 기반 사용자 인터페이스를 제공해요. 마스터와 각 워커는 클러스터와 작업 통계를 보여주는 자체 웹 UI를 가져요. 기본적으로 마스터의 웹 UI는 포트 8080에서 접근할 수 있어요. 포트는 구성 파일에서 또는 명령줄 옵션으로 변경할 수 있어요.
추가로 각 작업의 상세 로그 출력도 각 워커 노드의 작업 디렉터리(SPARK_HOME/work 기본값)에 기록돼요. 각 작업마다 콘솔에 쓴 모든 출력이 담긴 stdout과 stderr 두 파일을 보게 될 거예요.
Hadoop과 함께 실행하기
Spark을 기존 Hadoop 클러스터 옆에서 같은 머신에 별도 서비스로 실행하면 함께 실행할 수 있어요. Spark에서 Hadoop 데이터에 접근하려면 hdfs:// URL(보통 hdfs://<namenode>:9000/path지만, Hadoop Namenode의 웹 UI에서 올바른 URL을 찾을 수 있어요)을 사용하면 돼요. 또는 Spark용 별도 클러스터를 설정하고 여전히 네트워크를 통해 HDFS에 접근하게 할 수도 있어요. 이는 디스크 로컬 접근보다 느리지만, 같은 지역 네트워크에서 실행 중이라면(예: Hadoop이 있는 각 랙에 Spark 머신 몇 대를 배치한다면) 문제가 되지 않을 수 있어요.
네트워크 보안을 위한 포트 구성
일반적으로 Spark 클러스터와 그 서비스는 공개 인터넷에 배포되지 않아요. 보통 사설 서비스이며 Spark를 배포하는 조직의 네트워크 내에서만 접근 가능해야 해요. Spark 서비스가 사용하는 호스트와 포트에 대한 접근은 서비스에 접근해야 하는 원본 호스트로 제한해야 해요.
이는 다른 리소스 매니저처럼 세밀한 접근 제어를 지원하지 않는 스탠드얼론 리소스 매니저를 쓰는 클러스터에서 특히 중요해요.
구성할 포트의 전체 목록은 security page를 보세요.
고가용성(High Availability)
기본적으로 스탠드얼론 스케줄링 클러스터는 Worker 실패에 내성이 있어요(Spark 자체가 작업을 다른 워커로 옮겨 잃는 것에 내성이 있는 한). 하지만 스케줄러는 스케줄링 결정을 내리기 위해 Master를 사용하고, 이는 (기본적으로) 단일 실패 지점을 만든다는 점에서, Master가 다운되면 새 애플리케이션을 만들 수 없어요. 이를 우회하기 위해 아래에 자세히 설명할 두 가지 고가용성 방식이 있어요.
ZooKeeper를 이용한 대기 마스터(Standby Masters)
개요
리더 선출과 일부 상태 저장에 ZooKeeper를 활용하면, 같은 ZooKeeper 인스턴스에 연결된 여러 Master를 클러스터에서 실행할 수 있어요. 하나가 "리더"로 선출되고 나머지는 대기(standby) 모드로 남아요. 현재 리더가 죽으면 다른 Master가 선출되어 이전 Master의 상태를 복구한 뒤 스케줄링을 재개해요. 전체 복구 과정(첫 리더가 다운된 시점부터)은 1~2분이 걸려야 해요. 이 지연은 새 애플리케이션의 스케줄링에만 영향을 준다는 점에 유의하세요. Master 장애 중에 이미 실행 중이던 애플리케이션은 영향받지 않아요.
ZooKeeper 시작에 대해 더 배우려면 여기를 보세요.
구성
이 복구 모드를 활성화하려면 spark-env에서 spark.deploy.recoveryMode와 관련 spark.deploy.zookeeper.* 구성을 설정해 SPARK_DAEMON_JAVA_OPTS를 설정할 수 있어요.
가능한 함정: 클러스터에 여러 Master가 있지만 Master들이 ZooKeeper를 사용하도록 올바르게 구성하지 못하면, Master들은 서로를 발견하지 못하고 모두 자신이 리더라고 생각해요. 이는 건강한 클러스터 상태로 이어지지 않아요(모든 Master가 독립적으로 스케줄링하므로).
세부 사항
ZooKeeper 클러스터를 설정한 뒤 고가용성을 여는 것은 간단해요. 같은 ZooKeeper 구성(ZooKeeper URL과 디렉터리)으로 다른 노드에서 여러 Master 프로세스를 시작하면 돼요. Master는 언제든 추가·제거할 수 있어요.
새 애플리케이션을 스케줄링하거나 Worker를 클러스터에 추가하려면, 현재 리더의 IP 주소를 알아야 해요. 이전에 단일 Master를 전달하던 곳에 Master 목록을 전달하면 돼요. 예를 들어 SparkContext가 spark://host1:port1,host2:port2를 가리키도록 시작할 수 있어요. 이렇게 하면 SparkContext가 두 Master 모두에 등록을 시도해요. host1이 다운되면 새 리더인 host2를 찾으므로 이 구성은 여전히 올바르게 동작해요.
"Master에 등록하는 것"과 정상 동작 사이에는 중요한 차이가 있어요. 시작할 때 애플리케이션 또는 Worker는 현재 리드 Master를 찾아 등록할 수 있어야 해요. 하지만 성공적으로 등록하면 "시스템 안"(즉, ZooKeeper에 저장)에 있게 돼요. 장애 조치가 발생하면 새 리더가 이전에 등록된 모든 애플리케이션과 Worker에 연락해 리더십 변경을 알려주므로, 시작 시 새 Master의 존재를 몰랐어도 돼요.
이 속성 덕분에 새 Master는 언제든 만들 수 있고, 신경 쓸 것은 새 애플리케이션과 Worker가 리더가 될 경우 등록할 Master를 찾을 수 있게 하는 것뿐이에요. 일단 등록되면 알아서 처리돼요.
로컬 파일 시스템을 이용한 단일 노드 복구
개요
ZooKeeper는 프로덕션 수준 고가용성에 가장 좋은 방법이지만, Master가 다운됐을 때 재시작만 할 수 있으면 된다면 FILESYSTEM 모드가 처리해줘요. 애플리케이션과 Worker가 등록할 때, Master 프로세스 재시작 시 복구할 수 있도록 충분한 상태가 제공된 디렉터리에 기록돼요.
구성
이 복구 모드를 활성화하려면 spark-env에서 이 구성을 사용해 SPARK_DAEMON_JAVA_OPTS를 설정할 수 있어요.
| 시스템 속성 | 기본값 | 의미 | 버전 |
|---|---|---|---|
| spark.deploy.recoveryMode | NONE | cluster 모드로 제출된 Spark 작업이 실패하면 복구하고 재실행하는 복구 모드 설정. FILESYSTEM으로 설정하면 파일 시스템 기반 단일 노드 복구 모드를, ROCKSDB로 설정하면 RocksDB 기반 단일 노드 복구 모드를, ZOOKEEPER로 설정하면 Zookeeper 기반 복구 모드를, CUSTOM으로 설정하면 추가 spark.deploy.recoveryMode.factory 구성을 통해 고객 제공자 클래스를 사용하게 해요. NONE은 이 복구 모드를 비활성화하는 기본값이에요. |
0.8.1 |
| spark.deploy.recoveryDirectory | "" | Spark이 복구 상태를 저장하고 Master의 관점에서 접근 가능한 디렉터리. spark.deploy.recoveryMode나 spark.deploy.recoveryCompressionCodec가 변경되면 디렉터리를 수동으로 명확히 해야 한다는 점에 유의하세요. | 0.8.1 |
| spark.deploy.recoveryCompressionCodec | (none) | 영속화 엔진용 압축 코덱. none (기본), lz4, lzf, snappy, zstd. 현재 이 구성을 지원하는 것은 FILESYSTEM 모드뿐이에요. | 4.0.0 |
| spark.deploy.recoveryTimeout | (none) | 복구 프로세스의 타임아웃. 기본값은 spark.worker.timeout과 같아요. | 4.0.0 |
| spark.deploy.recoveryMode.factory | "" | StandaloneRecoveryModeFactory 인터페이스를 구현할 클래스 | 1.2.0 |
| spark.deploy.zookeeper.url | None | spark.deploy.recoveryMode가 ZOOKEEPER로 설정되면, 이 구성은 연결할 zookeeper URL을 설정하는 데 쓰여요. | 0.8.1 |
| spark.deploy.zookeeper.dir | None | spark.deploy.recoveryMode가 ZOOKEEPER로 설정되면, 이 구성은 복구 상태를 저장할 zookeeper 디렉터리를 설정하는 데 쓰여요. | 0.8.1 |
세부 사항
- 이 해결책은 monit 같은 프로세스 모니터/매니저와 함께 쓰거나, 재시작을 통한 수동 복구를 활성화하는 것만으로도 쓸 수 있어요.
- 파일 시스템 복구가 전혀 복구하지 않는 것보다 확실히 나아 보이지만, 이 모드는 특정 개발이나 실험 목적에는 차선일 수 있어요. 특히 stop-master.sh로 마스터를 종료해도 복구 상태가 정리되지 않아서, 새 Master를 시작할 때마다 복구 모드로 들어가요. 이전에 등록된 모든 Worker/클라이언트가 타임아웃되기를 기다려야 한다면 시작 시간이 최대 1분 늘어날 수 있어요.
- 공식적으로 지원되진 않지만, 복구 디렉터리로 NFS 디렉터리를 마운트할 수 있어요. 원래 Master 노드가 완전히 죽으면 다른 노드에서 Master를 시작할 수 있는데, 이전에 등록된 모든 Worker/애플리케이션을 올바르게 복구해요(ZooKeeper 복구와 동등). 다만 미래의 애플리케이션이 등록하려면 새 Master를 찾을 수 있어야 해요.
더 알아보기 (Learn more)
- Spark Security — 클러스터 보안과 포트 구성.
- Spark 프로그래밍 가이드 — Spark 애플리케이션 시작하기.
- Spark 구성 — SparkConf와 실행자/코어 설정.