-
Notifications
You must be signed in to change notification settings - Fork 7
feat(extensions): add flower-docker monitoring for celery-worker #188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # Flower for Celery (extension bank) | ||
|
|
||
| Maintainer-facing notes for the **flower-docker** extension. | ||
|
|
||
| Copied into generated projects (via `template/`): | ||
|
|
||
| | Path | Purpose | | ||
| |------|---------| | ||
| | `Dockerfile` | uv-based image; Celery worker CMD (flower runs via `celery flower`) | | ||
| | `.dockerignore` | Excludes `.venv`, caches, git metadata | | ||
| | `compose.yml` | Dev compose: `redis` + `worker` + `flower` (port 5555) with healthchecks | | ||
| | `compose.prod.yml` | Prod overlay (`restart: always`, concurrency, healthchecks) | | ||
| | `pyproject.toml` | Adds `flower>=2.0.1` dependency | | ||
| | `.env.example.append` | `FLOWER_BASIC_AUTH` / `FLOWER_PORT` examples | | ||
| | `docs/FLOWER_GUIDE.md` | Long-form guide | | ||
| | `docs/README.md.append` | Index bullet | | ||
|
|
||
| Compose includes a Redis broker. Env vars are `BROKER_URL` / `RESULT_BACKEND` | ||
| (matching `worker/config.py`). Flower listens on `5555` and shares the same | ||
| image + broker env. Healthcheck probes `http://localhost:5555` via Python. | ||
|
|
||
| `flower-docker` is **incompatible** with `celery-docker` — both ship | ||
| `Dockerfile` / `compose.yml` for `celery-worker` and would overwrite the same | ||
| paths (see `templates.json:c/incompatibleWith`). Use one or the other. | ||
|
|
||
| ## Apply | ||
|
|
||
| ```sh | ||
| uvx create-awesome-python-app my-worker \ | ||
| --template celery-worker \ | ||
| --addons flower-docker \ | ||
| --yes | ||
| ``` | ||
|
|
||
| To try Flower alongside an existing `celery-docker` scaffold, replace the | ||
| addon: | ||
|
|
||
| ```sh | ||
| uvx create-awesome-python-app my-worker \ | ||
| --template celery-worker \ | ||
| --addons flower-docker \ | ||
| --yes | ||
| ``` | ||
|
|
||
| ## Verify | ||
|
|
||
| ```sh | ||
| docker compose up --build | ||
| # Flower dashboard: http://localhost:5555 | ||
| # Worker log shows ready; flower log shows "Visit me at http://0.0.0.0:5555" | ||
| ``` | ||
|
|
||
| With basic auth (optional): | ||
|
|
||
| ```sh | ||
| # .env | ||
| FLOWER_BASIC_AUTH=user:password | ||
| docker compose up --build | ||
| # http://user:password@localhost:5555 | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| .venv | ||
| __pycache__ | ||
| *.py[cod] | ||
| .pytest_cache | ||
| .ruff_cache | ||
| .git | ||
| .env | ||
| data | ||
| *.egg-info | ||
| dist | ||
| .mypy_cache |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| BROKER_URL=redis://redis:6379/0 | ||
| RESULT_BACKEND=redis://redis:6379/1 | ||
| # Flower dashboard (http://localhost:5555) | ||
| # Optional HTTP basic auth for Flower — format user:password | ||
| # FLOWER_BASIC_AUTH=user:password | ||
| FLOWER_PORT=5555 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| ENV UV_COMPILE_BYTECODE=1 | ||
| ENV UV_LINK_MODE=copy | ||
| ENV PYTHONDONTWRITEBYTECODE=1 | ||
| ENV PYTHONUNBUFFERED=1 | ||
|
|
||
| COPY pyproject.toml README.md ./ | ||
| COPY worker ./worker | ||
|
|
||
| RUN uv sync --no-dev | ||
|
|
||
| CMD ["uv", "run", "celery", "-A", "worker.celery_app", "worker", "--loglevel=INFO"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| services: | ||
| redis: | ||
| image: redis:7-alpine | ||
| restart: always | ||
| healthcheck: | ||
| test: ["CMD", "redis-cli", "ping"] | ||
| interval: 5s | ||
| timeout: 3s | ||
| retries: 5 | ||
|
Comment on lines
+2
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Persist Redis data in a named volume. This production service has no explicit volume for Proposed fix services:
redis:
image: redis:7-alpine
+ volumes:
+ - redis-data:/data
restart: always
+
+volumes:
+ redis-data:🤖 Prompt for AI Agents |
||
| worker: | ||
| build: . | ||
| env_file: | ||
| - .env | ||
| environment: | ||
| BROKER_URL: redis://redis:6379/0 | ||
| RESULT_BACKEND: redis://redis:6379/1 | ||
| restart: always | ||
| depends_on: | ||
| redis: | ||
| condition: service_healthy | ||
| command: uv run celery -A worker.celery_app worker --loglevel=INFO --concurrency=2 | ||
| flower: | ||
| build: . | ||
| env_file: | ||
| - .env | ||
| environment: | ||
| BROKER_URL: redis://redis:6379/0 | ||
| RESULT_BACKEND: redis://redis:6379/1 | ||
| ports: | ||
| - "5555:5555" | ||
|
Comment on lines
+29
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target file ---'
cat -n extensions/flower-docker/template/compose.prod.yml
printf '%s\n' '--- related Compose files ---'
find extensions/flower-docker -maxdepth 3 -type f -print | sort
printf '%s\n' '--- authentication and port references ---'
rg -n -C 3 'FLOWER_BASIC_AUTH|5555|basic.?auth|flower' extensions/flower-docker
printf '%s\n' '--- repository validation guidance and tests ---'
rg -n -C 3 'flower-docker|uv sync|uv run pytest|compose.prod.yml|template' README.md CONTRIBUTING.md .github extensions 2>/dev/null | head -300Repository: Create-Python-App/cpa-templates Length of output: 38769 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- extension metadata ---'
rg -n -C 8 '"flower-docker"|compose.prod|\.env\.example' templates.json
printf '%s\n' '--- environment templates ---'
find extensions/flower-docker templates/celery-worker -maxdepth 3 -type f -print | sort | grep -E 'env|compose|config|README'
printf '%s\n' '--- environment contents ---'
for f in $(find extensions/flower-docker templates/celery-worker -maxdepth 3 -type f -print | sort | grep -E 'env|compose'); do
printf '\n--- %s ---\n' "$f"
cat -n "$f"
done
printf '%s\n' '--- production compose references ---'
rg -n -C 5 'compose\.prod|docker compose.*prod|FLOWER_BASIC_AUTH|FLOWER_PORT' . --glob '!**/.git/**' --glob '!**/README.md' | head -300
printf '%s\n' '--- available local tools ---'
command -v uv || true
command -v docker || true
command -v yq || trueRepository: Create-Python-App/cpa-templates Length of output: 21480 🌐 Web query:
💡 Result: In Flower 2.0, the FLOWER_BASIC_AUTH environment variable does not have a default authentication value; its default is None [1][2]. To enable HTTP Basic authentication, you must explicitly set the FLOWER_BASIC_AUTH environment variable to a comma-separated list of username:password pairs [1][3]. Each pair defines a valid combination for accessing the Flower UI [1]. Example configuration: export FLOWER_BASIC_AUTH=user1:password1,user2:password2 [1][2] When properly configured, this enables built-in HTTP Basic authentication, protecting the Flower dashboard [4][5]. If this variable is not set, authentication remains disabled by default [1][2]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
compose = Path("extensions/flower-docker/template/compose.prod.yml").read_text()
env_example = Path("extensions/flower-docker/template/.env.example").read_text()
guide = Path("extensions/flower-docker/template/docs/FLOWER_GUIDE.md").read_text()
port = re.search(r'^\s*-\s*"([^"]+)"\s*$', compose, re.M)
auth_in_example = re.search(r'(?m)^\s*FLOWER_BASIC_AUTH\s*=', env_example)
auth_commented = re.search(r'(?m)^\s*#\s*FLOWER_BASIC_AUTH\s*=', env_example)
uses_env_file = bool(re.search(r'(?m)^\s*-\s*\.env\s*$', compose))
prod_command = re.search(r'(?m)^\s*command:\s*(.+flower.+)$', compose)
prod_doc = re.search(r'(?m)docker compose -f compose\.yml -f compose\.prod\.yml up', guide)
print({
"published_mapping": port.group(1) if port else None,
"explicit_auth_default": bool(auth_in_example),
"auth_commented_in_example": bool(auth_commented),
"flower_reads_dot_env_via_compose": uses_env_file,
"flower_command": prod_command.group(1).strip() if prod_command else None,
"documented_production_start": bool(prod_doc),
})
PYRepository: Create-Python-App/cpa-templates Length of output: 425 Require authentication before exposing Flower in production. The template publishes 🤖 Prompt for AI Agents |
||
| restart: always | ||
| depends_on: | ||
| redis: | ||
| condition: service_healthy | ||
| worker: | ||
| condition: service_started | ||
| command: uv run celery -A worker.celery_app flower --port=5555 | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:5555')\""] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
| start_period: 15s | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| services: | ||
| redis: | ||
| image: redis:7-alpine | ||
| ports: | ||
| - "6379:6379" | ||
|
Comment on lines
+3
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- compose.yml ---'
cat -n extensions/flower-docker/template/compose.yml
printf '%s\n' '--- related template files ---'
git ls-files extensions/flower-docker | sed -n '1,120p'
printf '%s\n' '--- Redis and port references ---'
rg -n -C 3 'redis:7-alpine|6379|REDIS|broker|ports:' extensions/flower-docker .github 2>/dev/null | sed -n '1,240p'
printf '%s\n' '--- project validation guidance and relevant scripts ---'
rg -n -C 3 'uv sync|uv run pytest|flower-docker|compose.yml|docker compose' README.md CONTRIBUTING.md pyproject.toml extensions 2>/dev/null | sed -n '1,260p'Repository: Create-Python-App/cpa-templates Length of output: 29479 🌐 Web query:
💡 Result: In the official Redis Docker image, "protected mode" is disabled by default [1][2]. This is a intentional configuration choice made by the maintainers to facilitate inter-container networking, as the port is not automatically exposed to the host network unless explicitly configured by the user [3][4]. Because protected mode is disabled, if you run a Redis container and publish the port (e.g., using the -p 6379:6379 flag), the instance will be reachable from external networks without authentication [1][2][5]. To secure your Redis instance, it is highly recommended to set a password and, if possible, avoid exposing the port to the public internet [1][5]. To set a password, you can override the default command to include the --requirepass argument [6][7]. Example using docker run: docker run -d --name my-redis -p 6379:6379 redis:7-alpine redis-server --requirepass "your_strong_password" If you are using Docker Compose, you can define it in your service configuration: services: redis: image: redis:7-alpine command: redis-server --requirepass "your_strong_password" ports: - "6379:6379" Security Recommendations: 1. Use --requirepass: Always set a strong password if there is any chance the port could be reached outside of your local container network [1][7]. 2. Avoid publishing the port: If the Redis instance only needs to communicate with other containers, do not use the -p flag. The service will remain accessible to other containers on the same Docker network [5][7]. 3. Bind to localhost: If you must access Redis from your host machine but do not want it exposed to the LAN or internet, bind the port to the loopback interface: -p 127.0.0.1:6379:6379 [7]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- flower-docker README ---'
cat -n extensions/flower-docker/README.md | sed -n '1,140p'
printf '%s\n' '--- Flower guide ---'
cat -n extensions/flower-docker/template/docs/FLOWER_GUIDE.md | sed -n '1,125p'
printf '%s\n' '--- production compose ---'
cat -n extensions/flower-docker/template/compose.prod.yml
printf '%s\n' '--- authentication configuration ---'
rg -n -C 4 'REDIS_PASSWORD|requirepass|--requirepass|AUTH|FLOWER_BASIC_AUTH|broker_url|result_backend' extensions/flower-docker | sed -n '1,240p'
printf '%s\n' '--- available local validators ---'
command -v docker-compose || true
command -v docker || true
command -v uv || true
command -v pytest || true
printf '%s\n' '--- deterministic port mapping check ---'
python3 - <<'PY'
from pathlib import Path
import re
text = Path("extensions/flower-docker/template/compose.yml").read_text()
match = re.search(r'(?ms)^ redis:\n(.*?)(?=^ \w+:|\Z)', text)
assert match, "redis service missing"
service = match.group(1)
port = re.search(r'(?m)^\s*-\s*"([^"]+)"\s*$', service)
assert port, "redis port mapping missing"
mapping = port.group(1)
host, container = mapping.rsplit(":", 1)
print({"mapping": mapping, "host_address": None, "host_port": host, "container_port": container})
assert host == "6379" and container == "6379"
print("Unqualified short syntax has no host-address field; Docker uses its default published-host binding.")
PYRepository: Create-Python-App/cpa-templates Length of output: 14997 Do not publish the unauthenticated Redis broker by default.
🤖 Prompt for AI Agents |
||
| healthcheck: | ||
| test: ["CMD", "redis-cli", "ping"] | ||
| interval: 5s | ||
| timeout: 3s | ||
| retries: 5 | ||
| worker: | ||
| build: . | ||
| env_file: | ||
| - .env | ||
| environment: | ||
| BROKER_URL: redis://redis:6379/0 | ||
| RESULT_BACKEND: redis://redis:6379/1 | ||
| volumes: | ||
| - .:/app | ||
| depends_on: | ||
| redis: | ||
| condition: service_healthy | ||
| command: uv run celery -A worker.celery_app worker --loglevel=INFO | ||
| flower: | ||
| build: . | ||
| env_file: | ||
| - .env | ||
| environment: | ||
| BROKER_URL: redis://redis:6379/0 | ||
| RESULT_BACKEND: redis://redis:6379/1 | ||
| ports: | ||
| - "5555:5555" | ||
| depends_on: | ||
| redis: | ||
| condition: service_healthy | ||
| worker: | ||
| condition: service_started | ||
| command: uv run celery -A worker.celery_app flower --port=5555 | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:5555')\""] | ||
|
Comment on lines
+31
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Apply The generated Compose files hard-code 📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
| start_period: 10s | ||
|
Comment on lines
+39
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- files ---'
git ls-files 'extensions/flower-docker/template/compose.yml' \
'extensions/flower-docker/template/compose.prod.yml' \
'extensions/flower-docker/template/pyproject.toml'
printf '%s\n' '--- compose.yml ---'
cat -n extensions/flower-docker/template/compose.yml | sed -n '1,70p'
printf '%s\n' '--- compose.prod.yml ---'
cat -n extensions/flower-docker/template/compose.prod.yml | sed -n '1,70p'
printf '%s\n' '--- template pyproject.toml ---'
cat -n extensions/flower-docker/template/pyproject.toml | sed -n '1,180p'
printf '%s\n' '--- related references ---'
rg -n --glob '!**/.git/**' 'FLOWER_BASIC_AUTH|FLOWER_PORT|healthcheck|/healthcheck|flower-docker' extensions/flower-docker .Repository: Create-Python-App/cpa-templates Length of output: 9541 🌐 Web query:
💡 Result: In Flower 2.0 and later versions, the /healthcheck endpoint is exempt from authentication by default [1][2]. This allows the route to be accessed without providing HTTP Basic Authentication credentials, which is intended to support health checks by orchestration services (like Kubernetes or Docker) even when authentication is enabled for the rest of the application [3]. No additional configuration is required to exempt this route; it is hard-coded as an exempt endpoint within the application [1][4]. If you are experiencing issues where the /healthcheck route is still requesting credentials, ensure that you are running a recent version of Flower, as this behavior was explicitly introduced to provide an auth-free way to verify the application's status [3]. While the /metrics endpoint is also documented as exempt from authentication in some versions [1][2], note that there has been community discussion regarding its exposure [5]. If you have specific security requirements, ensure that your infrastructure properly secures access to these endpoints if they are exposed to untrusted networks. Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- flower template files ---'
git ls-files extensions/flower-docker/template | sort
printf '%s\n' '--- environment and command references ---'
rg -n -C 3 'FLOWER_BASIC_AUTH|FLOWER_PORT|flower --port|command:|env_file' \
extensions/flower-docker/template extensions/flower-docker/README.md
printf '%s\n' '--- exact healthcheck URLs and surrounding YAML ---'
python3 - <<'PY'
from pathlib import Path
import re
for name in (
"extensions/flower-docker/template/compose.yml",
"extensions/flower-docker/template/compose.prod.yml",
):
text = Path(name).read_text()
urls = re.findall(r"urlopen\\\('([^']+)'\\\)", text)
ports = re.findall(r'--port=(\d+)', text)
print(name)
print("healthcheck_urls:", urls)
print("flower_command_ports:", ports)
print("basic_auth_reference:", "FLOWER_BASIC_AUTH" in text or "FLOWER_BASIC_AUTH" in Path(name).with_name(".env.example").read_text() if Path(name).with_name(".env.example").exists() else False)
PYRepository: Create-Python-App/cpa-templates Length of output: 9328 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from urllib.error import HTTPError
from urllib.request import HTTPErrorProcessor, Request
from io import BytesIO
from pathlib import Path
import re
class Response:
def __init__(self, code, url):
self.code = code
self.status = code
self.url = url
self.msg = "test"
self.headers = {}
def getcode(self):
return self.code
def info(self):
return self.headers
def geturl(self):
return self.url
def read(self, *args):
return b""
processor = HTTPErrorProcessor()
for code in (200, 401):
response = Response(code, "http://localhost:5555/")
try:
processor.http_response(None, response)
outcome = "accepted"
except HTTPError:
outcome = "raises HTTPError"
print(f"status {code}: {outcome}")
for path in (
"extensions/flower-docker/template/compose.yml",
"extensions/flower-docker/template/compose.prod.yml",
):
text = Path(path).read_text()
match = re.search(r"urlopen\('([^']+)'\)", text)
print(f"{path}: configured URL = {match.group(1) if match else 'not found'}")
PYRepository: Create-Python-App/cpa-templates Length of output: 444 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from urllib.error import HTTPError
from urllib.request import HTTPErrorProcessor, Request
from pathlib import Path
import re
class Response:
def __init__(self, code, url):
self.code = code
self.status = code
self.url = url
self.msg = "test"
self.headers = {}
def getcode(self):
return self.code
def info(self):
return self.headers
def geturl(self):
return self.url
def read(self, *args):
return b""
class Parent:
def error(self, _protocol, request, response, code, message, headers):
raise HTTPError(request.full_url, code, message, headers, response)
processor = HTTPErrorProcessor()
processor.parent = Parent()
request = Request("http://localhost:5555/")
for code in (200, 401):
response = Response(code, request.full_url)
try:
processor.http_response(request, response)
outcome = "accepted"
except HTTPError as error:
outcome = f"raises HTTPError({error.code})"
print(f"status {code}: {outcome}")
for path in (
"extensions/flower-docker/template/compose.yml",
"extensions/flower-docker/template/compose.prod.yml",
):
text = Path(path).read_text()
urls = re.findall(r"urlopen\('([^']+)'\)", text)
print(f"{path}: configured URLs = {urls}")
PYRepository: Create-Python-App/cpa-templates Length of output: 750 Use Flower’s unauthenticated health endpoint. When 📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # Flower guide (Celery) | ||
|
|
||
| ## Overview | ||
|
|
||
| The **flower-docker** extension packages the Celery worker with a **Flower** | ||
| monitoring dashboard for local and production-style containers. It includes a | ||
| Redis broker, a worker, and a Flower service. | ||
|
|
||
| Use it when you want real-time task monitoring (`http://localhost:5555`) without | ||
| installing Flower on the host. It is mutually exclusive with `celery-docker` | ||
| (both ship `Dockerfile` / `compose.yml` for `celery-worker`). | ||
|
|
||
| ## What it adds | ||
|
|
||
| | Path | Purpose | | ||
| |------|---------| | ||
| | `Dockerfile` | Image based on `ghcr.io/astral-sh/uv:python3.12-bookworm-slim` | | ||
| | `.dockerignore` | Keeps `.venv`, caches, and git metadata out of the build context | | ||
| | `compose.yml` | Dev: `redis` + `worker` + `flower` (port 5555) with healthchecks | | ||
| | `compose.prod.yml` | Prod overlay: `restart: always`, `--concurrency=2`, healthchecks | | ||
| | `pyproject.toml` | Merges `flower>=2.0.1` into project dependencies | | ||
| | `.env.example` / `.env.example.append` | Flower env examples (`FLOWER_BASIC_AUTH`, `FLOWER_PORT`) | | ||
|
|
||
| Env overrides in Compose: `BROKER_URL` / `RESULT_BACKEND` point at the | ||
| `redis` service (not `localhost`). These names match `worker/config.py` | ||
| (pydantic-settings fields `broker_url` / `result_backend`). | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Development | ||
|
|
||
| ```sh | ||
| docker compose up --build | ||
| ``` | ||
|
|
||
| - Flower dashboard: http://localhost:5555 | ||
| - Redis: localhost:6379 | ||
|
|
||
| The dev compose file bind-mounts the project directory for the worker. | ||
|
|
||
| ### Production-style run | ||
|
|
||
| ```sh | ||
| docker compose -f compose.yml -f compose.prod.yml up --build -d | ||
| ``` | ||
|
|
||
| The prod overlay removes `--reload` concerns, sets `restart: always`, and | ||
| uses `--concurrency=2` for the worker. | ||
|
|
||
| ### With basic auth (recommended for non-local) | ||
|
|
||
| 1. Set in `.env`: | ||
|
|
||
| ```env | ||
| FLOWER_BASIC_AUTH=user:password | ||
| ``` | ||
|
|
||
| 2. Restart: `docker compose up --build` | ||
| 3. Open http://localhost:5555 — browser prompts for user/password. | ||
|
|
||
| Flower can expose task arguments and results. Never commit `.env` to version | ||
| control; add `.env` to `.gitignore`. In production, place Flower behind a | ||
| reverse proxy with TLS. | ||
|
|
||
| ## Configuration | ||
|
|
||
| Create `.env` at the project root (copy from `.env.example` after scaffold). | ||
|
|
||
| | Variable | Default | Notes | | ||
| |----------|---------|-------| | ||
| | `BROKER_URL` | `redis://redis:6379/0` | Broker for worker + flower (Compose overrides to service name) | | ||
| | `RESULT_BACKEND` | `redis://redis:6379/1` | Result backend | | ||
| | `FLOWER_BASIC_AUTH` | (unset) | `user:password` for HTTP basic auth; leave unset for local dev | | ||
| | `FLOWER_PORT` | `5555` | Flower listen port (Compose maps `5555:5555`) | | ||
|
|
||
| For the worker, `BROKER_URL` / `RESULT_BACKEND` are read via `worker/config.py`. | ||
| Flower reuses the same broker env (`--broker` defaults to `BROKER_URL`). | ||
|
|
||
| ## Verification | ||
|
|
||
| 1. `docker compose up --build` | ||
| 2. Confirm Redis is healthy: `docker compose ps` shows `healthy` for `redis` | ||
| 3. Confirm worker is ready: log shows `celery@... ready` | ||
| 4. Confirm Flower is healthy: `docker compose ps` shows `healthy` for `flower` and log shows `Visit me at http://0.0.0.0:5555` | ||
| 5. Open http://localhost:5555 — dashboard lists workers and tasks | ||
| 6. Enqueue a task: | ||
|
|
||
| ```sh | ||
| docker compose exec worker uv run python -c \ | ||
| "from worker.tasks import ping; print(ping.delay().get(timeout=10))" | ||
| ``` | ||
|
|
||
| Flower should show the task in the dashboard. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| | Symptom | Fix | | ||
| |---------|-----| | ||
| | Cannot connect to Redis | Use `redis://redis:6379/0` inside Compose (service name), not `localhost` | | ||
| | Flower not reachable on 5555 | Check `docker compose ps`; flower healthcheck may still be starting (10s start period) | | ||
| | Worker not appearing in Flower | Ensure `BROKER_URL` matches for both services; restart `docker compose up --build` | | ||
| | Flower asks for password unexpectedly | Unset `FLOWER_BASIC_AUTH` in `.env` for local dev, or provide `user:password` correctly | | ||
| | Import errors for `worker` | Confirm `COPY worker` matches the template layout | | ||
| | `flower` command fails | Confirm `flower` is installed: `uv run python -c "import flower"` after `uv sync` | | ||
|
|
||
| ## Resources | ||
|
|
||
| - [Flower docs](https://flower.readthedocs.io/) | ||
| - [Celery first steps](https://docs.celeryq.dev/en/stable/getting-started/first-steps-with-celery.html) | ||
| - [Celery monitoring and management guide](https://docs.celeryq.dev/en/stable/userguide/monitoring.html) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| - [Flower](./FLOWER_GUIDE.md) — Celery monitoring dashboard (port 5555) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| [project] | ||
| dependencies = [ | ||
| "flower>=2.0.1", | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Clarify the migration workflow.
This section describes an existing
celery-dockerscaffold but repeats the fresh-project command. It does not explain how to replace the incompatible addon or whether regeneration is required.Document the supported migration steps, or remove this duplicate section.
🤖 Prompt for AI Agents