| <!-- | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| --> | |
| # Running Python pipelines on a local Flink cluster | |
| This guide describes a contributor workflow for validating Python Beam pipelines | |
| against a real local Flink standalone cluster. It is useful when embedded Flink | |
| is not enough, for example when validating streaming source behavior, checkpoint | |
| boundaries, or runner-visible job state in the Flink dashboard. | |
| The commands assume a Unix shell (Linux, macOS, or WSL2 on Windows) with `curl`, | |
| `tar`, and `java` on the `PATH`. | |
| * [What this setup validates](#what-this-setup-validates) | |
| * [Prerequisites](#prerequisites) | |
| * [Start a local Flink cluster](#start-a-local-flink-cluster) | |
| * [Run a Beam Python pipeline](#run-a-beam-python-pipeline) | |
| * [Troubleshooting](#troubleshooting) | |
| * [Stop the cluster](#stop-the-cluster) | |
| ## What this setup validates | |
| This setup runs three components: | |
| 1. A Flink standalone cluster, consisting of a JobManager and a TaskManager. | |
| 1. A Beam Flink Job Server, started by the Python `FlinkRunner`. | |
| 1. A Python SDK harness, using `--environment_type=LOOPBACK` for local | |
| development. | |
| The Flink dashboard at `http://localhost:8081` shows the submitted Beam jobs. | |
| This is different from embedded Flink mode, where the cluster is started only | |
| for the lifetime of one job and is not useful for manual dashboard inspection. | |
| ## Prerequisites | |
| Install or prepare the following: | |
| * Docker Desktop (optional), only for the alternative method of obtaining the | |
| Flink distribution. | |
| * A Unix shell: Linux, macOS, or WSL2 on Windows. | |
| * Java 11 on the `PATH`. | |
| * A Python environment with the Beam SDK dependencies installed. | |
| * A Beam source checkout for the Python code under test. | |
| * A Flink 1.20 Job Server jar built from the same Beam checkout when validating | |
| unreleased Beam changes. | |
| For a source-built Job Server jar, run this command from the Beam checkout: | |
| ```sh | |
| ./gradlew :runners:flink:1.20:job-server:shadowJar | |
| ``` | |
| The jar is written under: | |
| ```text | |
| runners/flink/1.20/job-server/build/libs/ | |
| ``` | |
| ## Start a local Flink cluster | |
| Use a Flink distribution whose minor version matches a Flink version supported | |
| by your Beam version. See the [Flink Version Compatibility](https://beam.apache.org/documentation/runners/flink/#flink-version-compatibility) | |
| table in the Flink Runner documentation, and confirm the exact patch version on | |
| the [Flink downloads page](https://flink.apache.org/downloads.html). This guide | |
| uses Flink 1.20. | |
| Download and unpack the binary distribution: | |
| ```sh | |
| FLINK_VERSION=1.20.1 | |
| curl -fLO "https://archive.apache.org/dist/flink/flink-${FLINK_VERSION}/flink-${FLINK_VERSION}-bin-scala_2.12.tgz" | |
| tar -xzf "flink-${FLINK_VERSION}-bin-scala_2.12.tgz" -C "$HOME" | |
| export FLINK_HOME="$HOME/flink-${FLINK_VERSION}" | |
| ``` | |
| Ensure these settings exist in `$FLINK_HOME/conf/config.yaml`: | |
| ```yaml | |
| jobmanager.rpc.address: localhost | |
| rest.address: localhost | |
| taskmanager.numberOfTaskSlots: 2 | |
| ``` | |
| Start the cluster. The JobManager and TaskManager run as background daemons: | |
| ```sh | |
| "$FLINK_HOME/bin/start-cluster.sh" | |
| ``` | |
| Verify that the JobManager and TaskManager are available: | |
| ```sh | |
| curl -fsS http://localhost:8081/overview | |
| ``` | |
| Expected output includes one TaskManager and two slots: | |
| ```json | |
| {"taskmanagers":1,"slots-total":2,"slots-available":2,"jobs-running":0} | |
| ``` | |
| You can also open the Flink dashboard in a browser: | |
| ```text | |
| http://localhost:8081 | |
| ``` | |
| ### Alternative: extract Flink from the Docker image | |
| If a direct download is not available, copy the distribution out of the Flink | |
| Docker image with `docker cp`: | |
| ```sh | |
| docker create --name flink-dist flink:1.20 | |
| docker cp flink-dist:/opt/flink "$HOME/flink-1.20" | |
| docker rm flink-dist | |
| export FLINK_HOME="$HOME/flink-1.20" | |
| ``` | |
| A distribution copied out of a Docker image can contain the container hostname in | |
| `conf/config.yaml`; see [Troubleshooting](#troubleshooting). | |
| ## Run a Beam Python pipeline | |
| For local Python development, use `FlinkRunner`, point it at the standalone | |
| cluster, and use `LOOPBACK` so the Python SDK harness runs in the local process. | |
| Use a source checkout on `PYTHONPATH` when validating unreleased Python changes. | |
| Set paths for your environment: | |
| ```sh | |
| export BEAM_CHECKOUT="$HOME/beam" | |
| export PYTHON="$HOME/beamenv/bin/python" | |
| export FLINK_JOB_SERVER_JAR="$(find "$BEAM_CHECKOUT/runners/flink/1.20/job-server/build/libs" \ | |
| -name 'beam-runners-flink-1.20-job-server-*.jar' | head -n 1)" | |
| ``` | |
| Run a small pipeline: | |
| ```sh | |
| printf 'to be or not to be\nbeam runs on flink\n' > /tmp/beam-flink-input.txt | |
| PYTHONPATH="$BEAM_CHECKOUT/sdks/python" "$PYTHON" -m apache_beam.examples.wordcount \ | |
| --runner=FlinkRunner \ | |
| --flink_master=localhost:8081 \ | |
| --flink_version=1.20 \ | |
| --flink_job_server_jar="$FLINK_JOB_SERVER_JAR" \ | |
| --environment_type=LOOPBACK \ | |
| --input=/tmp/beam-flink-input.txt \ | |
| --output=/tmp/beam-flink-counts | |
| ``` | |
| For released Beam, omit `--flink_job_server_jar` and the `PYTHONPATH` prefix; the | |
| `FlinkRunner` downloads a Job Server matching `--flink_version` automatically. The | |
| source checkout and built jar are only needed to test unreleased changes. | |
| Check the dashboard or REST API after the run: | |
| ```sh | |
| curl -fsS http://localhost:8081/jobs/overview | |
| ``` | |
| The job should be `FINISHED`. | |
| ## Troubleshooting | |
| If the TaskManager does not register, check `$FLINK_HOME/conf/config.yaml`. | |
| When a distribution is copied out of a Docker image, the file might contain the | |
| container hostname. Replace it with: | |
| ```yaml | |
| jobmanager.rpc.address: localhost | |
| ``` | |
| If a Python job fails on native Windows with an invalid path containing `:`, | |
| run the Python driver and Job Server from WSL2. Some staged artifact names used | |
| by the portable runner are valid on Linux but invalid as native Windows file | |
| names. | |
| On WSL2, keep at least one shell open in the distribution while the cluster runs. | |
| Closing the last shell can stop the distribution and its background daemons. | |
| If the job starts but the Python transforms do not execute, check the | |
| environment type. `LOOPBACK` is intended for local development. For a remote | |
| or multi-machine Flink cluster, use a containerized environment instead. | |
| ## Stop the cluster | |
| Stop the local cluster when you finish collecting results: | |
| ```sh | |
| "$FLINK_HOME/bin/stop-cluster.sh" | |
| ``` |