
앞선 글에서는 Flink DataStream API를 사용해 WordCount 프로그램을 작성하고 shadowJar로 실행 JAR을 생성하였다. 이번 글에서는 Docker 기반으로 Flink 로컬 클러스터를 구성하고, 1편에서 생성한 JAR을 Web UI를 통해 제출하여 실제 분산 실행 환경에서 프로그램이 어떻게 동작하는지 확인한다. Flink는 JobManager와 TaskManager라는 두 컴포넌트로 이루어진 분산 엔진이므로 로컬 환경에서도 이 구조를 이해하는 것이 중요하다.
로컬에서 Flink를 실행하기 위해 docker-compose를 사용해 JobManager와 TaskManager를 포함한 미니 클러스터를 구성한다. Flink 1.19부터 공식 Docker 이미지가 Java 17 버전을 지원하므로 동일한 버전을 사용한다.
version: '3.8'
services:
jobmanager:
image: flink:1.19.1-scala_2.12-java17
container_name: flink-jobmanager
ports:
- '8081:8081' # Flink Web UI
command: jobmanager
environment:
- JOB_MANAGER_RPC_ADDRESS=jobmanager
volumes:
- flink_data:/opt/flink/usrlib
networks:
- flink-net
taskmanager:
image: flink:1.19.1-scala_2.12-java17
container_name: flink-taskmanager
depends_on:
- jobmanager
command: taskmanager
environment:
- JOB_MANAGER_RPC_ADDRESS=jobmanager
- TASK_MANAGER_NUMBER_OF_TASK_SLOTS=2
networks:
- flink-net
volumes:
flink_data:
networks:
flink-net:
driver: bridge
/opt/flink/usrlib에 업로드되는 파일을 저장아래 명령으로 클러스터를 실행한다.
docker-compose up -d
정상 실행되면 다음 주소에서 Web UI에 접근할 수 있다.
http://localhost:8081

Web UI는 현재 실행 중인 Job, TaskManager 상태, 슬롯(Slot) 정보 등을 실시간으로 보여준다. 클러스터가 정상적으로 기동되면 Overview 화면에서 다음 정보를 확인할 수 있다.
이 화면에서 전체 클러스터의 상태를 간단히 파악할 수 있다.
아래 명령으로 JAR을 생성한다.
./gradlew clean shadowJar
생성된 파일은 다음 경로에 위치한다.
build/libs/<프로젝트명>-all.jar

/opt/flink/usrlib 에 매핑된 volume에 JAR 업로드com.flinkhandson.WordCountJob
Submit과 동시에 Flink는 WordCount 프로그램을 Job 형태로 등록하고, JobManager가 실행 계획을 생성한 뒤 TaskManager로 서브태스크를 분배한다.
제출된 Job은 RUNNING 상태로 전환되며, TaskManager가 실제 계산을 수행한다.
아래 명령으로 로그를 실시간 조회할 수 있다.
docker logs flink-taskmanager -f
예시 출력은 다음과 같다.
1> (hello,2)
2> (flink,3)
1> (world,1)
1> (apache,1)
...
앞의 숫자는 각 서브태스크의 ID이며 병렬도(parallelism)가 2 이상일 경우 여러 서브태스크가 병렬로 연산을 수행한다.
WordCount Job을 클릭하면 다음 정보를 확인할 수 있다.
이 화면은 Flink 스트림 애플리케이션의 상태를 실시간으로 파악하는 데 중요한 역할을 한다.
스트림 Job은 기본적으로 종료되지 않으므로 필요할 때 수동으로 종료해야 한다.
docker exec -it flink-jobmanager flink cancel <job-id>