Skip to content

Commit d0d46bf

Browse files
committed
Add one-command self-contained local devbox
Signed-off-by: Dejan Zele Pejchev <pejcev.dejan@gmail.com>
1 parent 26a9b6c commit d0d46bf

18 files changed

Lines changed: 777 additions & 186 deletions

README.md

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
[Flyte 2](https://github.com/flyteorg/flyte) lets you write batch workflows as plain async Python.
66
[Armada](https://github.com/armadaproject/armada) schedules millions of jobs a day across many
7-
Kubernetes clusters, with fair-share, gang scheduling, and preemption. `armada-flyte` connects the
8-
two: your Flyte task runs as an Armada job, with one line of config and no new API to learn.
7+
Kubernetes clusters, with fair-share, gang scheduling (running a group of pods all-or-nothing), and
8+
preemption. `armada-flyte` connects the two: your Flyte task runs as an Armada job, with one line of
9+
config and no new API to learn.
910

1011
## The whole integration
1112

@@ -25,10 +26,11 @@ async def greet(name: str) -> str:
2526
return f"hello {name}, from an Armada pod" # runs in an Armada-scheduled pod
2627
```
2728

28-
A stock `@env.task` and one `plugin_config` line. Fan out with `asyncio.gather`, pass dataclasses
29-
between tasks, gang-schedule a group: it is all just Flyte, running on Armada.
29+
The `queue` names the Armada queue the job goes into, its fair-share bucket. A stock `@env.task` and
30+
one `plugin_config` line. Fan out with `asyncio.gather`, pass dataclasses between tasks, or gang-schedule
31+
a group all-or-nothing with `armada_flyte.Gang`: it is all just Flyte, running on Armada.
3032

31-
The connector submits to the Armada at `ARMADA_URL` (default `localhost:50051`). Point it at a
33+
The connector submits to Armada at `ARMADA_URL` (default `localhost:50051`). Point it at a
3234
remote cluster by setting that env var, or in code:
3335

3436
```python
@@ -41,18 +43,22 @@ never lands in the control plane. See [docs/getting-started.md](docs/getting-sta
4143

4244
## See it run
4345

44-
With a local Armada cluster up, the [demo](demo/) stands up a Flyte backend and the connector in one
45-
command, then you submit the task:
46+
`./hack/up.sh` brings up Armada and Flyte in one local kind cluster (see the
47+
[quickstart](demo/README.md)). Submitting a task then returns its typed result to your terminal, and the
48+
run appears in the Flyte UI:
4649

4750
```console
48-
$ ./demo/setup.sh
4951
$ ./.venv/bin/python examples/hello.py
5052
submitted run rxc4nspfkjqr5px6q9nj
51-
UI: http://localhost:30080/v2/.../runs/rxc4nspfkjqr5px6q9nj
53+
Flyte console: http://localhost:5001/v2/.../runs/rxc4nspfkjqr5px6q9nj
54+
Armada Lookout: http://localhost:30000
55+
56+
hello armada, from an Armada pod
5257
```
5358

54-
The run shows up in the Flyte UI, scheduled and executed by Armada. See
55-
[getting started](docs/getting-started.md) for the walkthrough.
59+
This is an integration between two systems, so there is real setup, but `./hack/up.sh` does it in one
60+
command: the [quickstart](demo/README.md) covers what it stands up and how to run this. From there,
61+
[examples/](examples/) ladder from `hello` up to a gang inside a DAG.
5662

5763
## Why both
5864

@@ -62,7 +68,7 @@ The run shows up in the Flyte UI, scheduled and executed by Armada. See
6268
| The Flyte console: runs, lineage, logs | Fair-share between queues, gang scheduling, preemption |
6369
| Local execution for fast iteration | Battle-tested at millions of jobs a day |
6470

65-
You keep Flyte's authoring and console; Armada does the scheduling. No rewrite, no second SDK.
71+
You keep Flyte's authoring and console, and Armada does the scheduling. No rewrite, no second SDK.
6672

6773
## How it works
6874

@@ -83,12 +89,14 @@ Flyte UI.
8389

8490
## Where to go next
8591

86-
- **Run it locally.** [demo/](demo/) stands up the backend and connector in one command (the
87-
[See it run](#see-it-run) commands above).
92+
- **Run it locally.** `./hack/up.sh` brings up Armada, Flyte, and the connector in one kind cluster;
93+
the [quickstart](demo/README.md) covers it.
8894
- **Write tasks.** Start from [examples/hello.py](examples/hello.py), then [examples/](examples/) for
8995
fan-out, gang scheduling, and a gang inside a DAG.
9096
- **Run against your own backend.** [Getting started](docs/getting-started.md) covers installing the
91-
connector, running it as a service, and building the task image.
97+
connector, running it as a service, and building the task image. The examples read `FLYTE_ENDPOINT`
98+
(the Flyte API) and `FLYTE_UI_BASE` (the console link they print) from the environment, both
99+
defaulting to the local devbox, so point them at your backend there.
92100
- **Understand the internals.** [How it works](docs/architecture.md): the connector, state mapping,
93101
and gang scheduling.
94102
- **Deploy the connector as a service.** [deploy/](deploy/).

demo/README.md

Lines changed: 111 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,142 @@
1-
# Local quickstart
1+
# Quickstart
22

3-
Run a Flyte 2 backend in the same Kind cluster Armada already schedules onto, then submit a task.
4-
This is the one-command way to see the connector work end to end.
3+
`armada-flyte` runs Flyte tasks as Armada jobs, so trying it means bringing up both. `./hack/up.sh`
4+
does that in one command: a local kind cluster with Armada (operator, dependencies, and the component
5+
CRs), Traefik, the Flyte 2 backend, the connector, and the `flyte` queue, all wired together.
56

67
```
7-
host Kind cluster "armada-test"
8-
┌────────────┐ localhost:30080 ┌─────────────────────────────────────────┐
9-
│ Flyte CLI │ ─────────────────▶│ flyte-binary (API + console + TaskAction │
10-
│ (examples) │ localhost:30900 │ reconciler) │
11-
├────────────┤ ─────────────────▶│ postgres (metadata + runs) │
12-
│ connector │◀──── :8000 ───────│ minio (blob store) │
13-
│ c0 │ ──┐ │ armada-<jobid> pods ──in-cluster──▶ minio │
14-
└────────────┘ │ :50051 └─────────────────────────────────────────┘
15-
└──▶ Armada control plane (docker, from `dev:full`)
8+
host kind cluster "armada"
9+
┌──────────┐ localhost:30080 ┌───────────────────────────────────────────┐
10+
│ examples │ ─────────────────▶│ flyte-binary (API + console) │
11+
│ (client) │ │ armada-flyte-connector ──▶ armada-server │
12+
│ browser │ localhost:5001 │ minio (blob store), postgres (metadata) │
13+
│ │ ─────────────────▶│ armada (operator): server, scheduler, │
14+
└──────────┘ │ executor ──▶ job pods │
15+
└───────────────────────────────────────────┘
1616
```
1717

18-
`flyte-binary`, minio, and postgres run in the cluster. The connector `c0` runs on the host and is
19-
the only process bridging Flyte and Armada. The Armada pods reach minio in-cluster at
20-
`minio.flyte:9000`, and `setup.sh` port-forwards the Flyte API and minio to `localhost` so the client
21-
reaches them without any kind port mapping.
18+
Everything runs in the cluster. `hack/kind-config.yaml` maps every host-facing port to a NodePort, so
19+
the client (the examples) and your browser reach the API and console straight from the host with no
20+
port-forwards. Every hop between components is cluster DNS.
2221

2322
## Prerequisites
2423

25-
1. **Armada**, with a real executor against the Kind cluster and the `flyte` queue created. Clone
26-
[armada](https://github.com/armadaproject/armada), then:
27-
```
28-
go run github.com/magefile/mage@v1.17.2 dev:full # kind "armada-test" + full stack
29-
go run cmd/armadactl/main.go create queue flyte --armadaUrl localhost:50051
30-
```
31-
Wait for the executor to log `Reporting current free resource` before submitting.
32-
2. **This repo's venv** (an arm64 Python on Apple Silicon, since Flyte's `obstore` wheel has no x86 build):
33-
```
34-
python3.11 -m venv .venv && ./.venv/bin/pip install -e .
35-
```
36-
3. `docker`, `helm`, `kubectl`, `kind` on PATH.
37-
38-
`setup.sh` pulls the published flyte-binary chart from the flyteorg helm repo and a stock upstream
39-
`cr.flyte.org/flyteorg/flyte-binary-v2` image, so no Flyte checkout is needed. The pinned image is the
40-
first commit that registers the connector-service plugin in the v2 executor
41-
([flyteorg/flyte#7565](https://github.com/flyteorg/flyte/pull/7565)). Earlier stock builds do not have
42-
it.
43-
44-
## Run
24+
- `docker` (running), `helm`, `kubectl`, `kind`, and `python3` (3.10+) on your PATH. `up.sh` checks
25+
these before it does any work.
26+
- `armadactl` is fetched to `~/bin` if it is not already on your PATH; add `~/bin` to your PATH if the
27+
command is not found afterwards.
28+
29+
## Bring it up
4530

4631
```
47-
export KUBECONFIG=<armada-checkout>/.kube/external/config # the kubeconfig `dev:full` writes
48-
./demo/setup.sh
32+
./hack/up.sh
33+
./.venv/bin/python examples/hello.py
4934
```
5035

51-
`setup.sh` deploys minio + postgres, installs the flyte-binary chart, builds and loads the task
52-
image, starts the connector, and port-forwards the Flyte API and minio to `localhost`. Then submit an
53-
example. It points at `localhost:30080` and targets queue `flyte`:
36+
`up.sh` creates the kind cluster, installs Armada and its dependencies (cert-manager, Pulsar, Postgres,
37+
Redis) via the operator, builds and loads the task image, deploys Traefik and the Flyte 2 backend
38+
(`flyte-binary`) with the connector, creates the `flyte` queue, and builds the repo's virtualenv. The
39+
first run pulls a lot of images before it settles. It waits for Armada to register executor capacity, so
40+
when it prints `Devbox up` the integration is ready to submit:
5441

5542
```
56-
./.venv/bin/python examples/hello.py
43+
Devbox up. Run an example:
44+
./.venv/bin/python examples/hello.py
45+
./.venv/bin/python examples/dag.py
5746
```
5847

59-
Open the printed `UI:` link to watch Armada schedule the pod and record the typed result. Other
60-
examples work the same way (`examples/fanout.py`, `examples/gang.py`, `examples/dag.py`).
48+
`hello.py` prints its result to your terminal (`hello armada, from an Armada pod`), and the run appears
49+
in the Flyte console at the link it prints. That is the whole loop: a Flyte task, scheduled and run by
50+
Armada.
6151

62-
## Files
52+
## The UIs
6353

64-
| File | What it is |
65-
|------|-----------|
66-
| `setup.sh` | One-command stand-up of the backend + connector. Idempotent. |
67-
| `minio.yaml` | Blob store. Pods reach it in-cluster. The host reaches it via a port-forward `setup.sh` starts. |
68-
| `postgres.yaml` | Flyte's `flyte` (metadata) and `runs` (run-graph) databases. |
69-
| `flyte-binary-values.yaml` | Chart overrides. `__HOST_IP__` (the connector endpoint) is filled in by `setup.sh`. |
54+
Each example prints two links:
7055

71-
## Overrides
56+
- **Flyte console** (`http://localhost:5001/v2`) - the run graph, status, and pod logs. Served by
57+
Traefik through the flyte-binary chart's own ingress.
58+
- **Armada Lookout** (`http://localhost:30000`) - the job and pod as Armada sees them.
59+
60+
Per-task input/output values do not render in the Flyte console yet. The Flyte 2 console is pre-release
61+
and its I/O panel is incomplete upstream. The values are recorded correctly: each example prints its
62+
result, and the data is in the blob store.
63+
64+
## Work through the examples
65+
66+
Once `hello.py` runs, the [examples](../examples/) build up in order:
67+
68+
1. [`function.py`](../examples/function.py) - one task doing real work (a Black-Scholes price).
69+
2. [`fanout.py`](../examples/fanout.py) - a typed dataclass through a parallel fan-out / fan-in.
70+
3. [`gang.py`](../examples/gang.py) - N co-scheduled workers as one Armada gang (all-or-nothing).
71+
4. [`dag.py`](../examples/dag.py) - the full shape: generate a dataset, run a gang over it, aggregate.
7272

73-
`setup.sh` reads these env vars: `KIND_CLUSTER` (default `armada-test`), `FLYTE_CHART` (default
74-
`flyteorg/flyte-binary`, or a local directory for an unreleased chart), `FLYTE_CHART_VERSION`
75-
(default `v2.0.27`), `FLYTE_IMAGE` (default the pinned stock `flyte-binary-v2` build), `TASK_IMAGE`
76-
(default `armada-flyte-task:v1`), `ARMADA_URL` (default `localhost:50051`), `HOST_IP` (auto-detected).
73+
Each runs the same way: `./.venv/bin/python examples/<name>.py`.
7774

7875
## Teardown
7976

8077
```
81-
helm -n flyte uninstall flyte-binary
82-
kubectl delete namespace flyte
83-
pkill -f "bin/c0 --port 8000"
84-
pkill -f "port-forward svc/flyte-binary-http"
85-
pkill -f "port-forward svc/minio"
78+
./hack/down.sh
8679
```
8780

88-
The kind cluster and Armada come down with `go run github.com/magefile/mage@v1.17.2 dev:fullDown` in
89-
the armada repo.
81+
`down.sh` deletes the kind cluster and clears the Flyte client's upload cache. The cache is keyed by the
82+
endpoint (`localhost:30080`, the same for every devbox), so clearing it here keeps the next `up.sh` from
83+
reusing entries that point at the deleted cluster's minio and 404-ing on their code bundle.
84+
85+
## Overrides
86+
87+
`up.sh` reads `KIND_CLUSTER` (default `armada`). `demo/setup.sh`, which it calls for the Flyte side,
88+
reads `FLYTE_CHART` (default `flyteorg/flyte-binary`, or a local directory for an unreleased chart),
89+
`FLYTE_CHART_VERSION` (default `v2.0.27`), `FLYTE_IMAGE` (default the pinned stock `flyte-binary-v2`
90+
build), `TASK_IMAGE` (default `armada-flyte-task:v1`), and `CONNECTOR_IMAGE` (default
91+
`gresearch/armada-flyte-connector:0.2.0`). The pinned Flyte image is the first that registers the
92+
connector plugin in the executor
93+
([flyteorg/flyte#7565](https://github.com/flyteorg/flyte/pull/7565)).
94+
95+
## Manual setup without the devbox script
96+
97+
`up.sh` is the supported path. If you want the steps by hand, or to submit to an Armada cluster you
98+
already run, the pieces are:
99+
100+
1. **Armada.** `hack/setup-armada.sh` installs Armada into the current kind context (operator,
101+
dependencies, and the CRs in `hack/armada/`). The stock
102+
[armada-operator](https://github.com/armadaproject/armada-operator) `make kind-all` also works, but
103+
its kind config maps only ports 30000-30002, so the Flyte console on 5001 will not be reachable from
104+
the host - create the cluster from `hack/kind-config.yaml` for that.
105+
2. **Flyte.** `./demo/setup.sh` deploys Traefik, the `flyte-binary` backend (with its minio blob store
106+
and postgres), builds the task image, and deploys the connector.
107+
3. **Queue.** `armadactl create queue flyte`.
108+
109+
## Files
110+
111+
| File | What it is |
112+
|------|-----------|
113+
| `setup.sh` | Stands up the Flyte side (Traefik + backend + connector) in the cluster. Idempotent. |
114+
| `minio.yaml` | Blob store. Pods reach it in-cluster; the host reaches it via the kind NodePort mapping. |
115+
| `postgres.yaml` | Flyte's `flyte` (metadata) and `runs` (run-graph) databases. |
116+
| `flyte-binary-values.yaml` | Chart overrides: storage, the chart ingress, and the connector routing. |
90117

91118
## Troubleshooting
92119

93-
- **`no connector found for task type [armada]`**: the connector config did not reach the running
120+
- **`no connector found for task type [armada]`**: the connector routing did not reach the running
94121
config. It must live under `configuration.inline.plugins.connector-service` with
95-
`supportedTaskTypes: [armada]`. The chart's top-level `configuration.connectorService` is not wired
96-
in. Confirm with `kubectl -n flyte get cm flyte-binary-config -o yaml | grep -A4 connector-service`.
97-
- **`TaskAction` stuck `Queued`**: Armada has no executor yet, or the `flyte` queue is missing.
98-
Check the connector log (`/tmp/armada-flyte-c0.log`) and `armadactl get queue flyte`.
99-
- **`localhost:30080` connection refused**: the port-forward is not running. Re-run `setup.sh`, or
100-
start it by hand: `kubectl -n flyte port-forward svc/flyte-binary-http 30080:8090`.
122+
`supportedTaskTypes: [armada]`; the chart's top-level `configuration.connectorService` is not wired in.
123+
Confirm with `kubectl -n flyte get cm flyte-binary-config -o yaml | grep -A4 connector-service`.
124+
- **Task stuck `Queued`**: Armada has no executor yet, or the `flyte` queue is missing. Check
125+
`kubectl -n armada get pods` and `armadactl get queue flyte`.
126+
- **A gang (`gang.py` / `dag.py`) is `REJECTED` or stuck `Queued`**: on your own Armada, the gang's
127+
node-uniformity label must be tracked by the executor and indexed by the scheduler, or the scheduler
128+
cannot place the gang. Confirm `kubernetes.io/hostname` is in the executor's
129+
`kubernetes.trackedNodeLabels` and the scheduler's `scheduling.indexedNodeLabels`. The devbox CRs
130+
(`hack/armada/armada-crs.yaml`) set both already. Also make sure the whole gang fits: with
131+
`kubernetes.io/hostname` uniformity all members land on one node, so the sum of their requests must
132+
fit that node's free capacity.
133+
- **`localhost:30080` connection refused**: the cluster is not up, or was created without
134+
`hack/kind-config.yaml`, so the NodePort is not mapped to the host. Bring the devbox up with
135+
`./hack/up.sh`.
136+
- **Flyte console shows "No actions found"**: open the exact `http://localhost:5001/v2/...` link the
137+
example prints. The console reaches the API through Traefik on the same origin.
101138
- **Pod `Completed` but the run fails resolving outputs**: the pod could not reach minio in-cluster.
102-
Confirm the connector was started with `FLYTE_BLOB_ENDPOINT=http://minio.flyte.svc.cluster.local:9000`
103-
and that the minio credentials match `minio.yaml`.
104-
- **Pod fails with a 404 on `fast<hash>.tar.gz` after a re-run**: you recreated the blob store (a
105-
fresh minio) but the client cached the previous upload and skipped re-uploading the code bundle to
106-
the new one. Clear the cache and rerun: `rm -f ~/.flyte/local-cache/cache.db`.
139+
Confirm the connector's `FLYTE_BLOB_ENDPOINT` is `http://minio.flyte.svc.cluster.local:9000` and the
140+
minio credentials match `minio.yaml`.
141+
- **Pod fails with a 404 on `fast<hash>.tar.gz` after a re-run**: you recreated the blob store but the
142+
client cached the previous upload. Tear down with `./hack/down.sh`, which clears the cache.

0 commit comments

Comments
 (0)