first commit
This commit is contained in:
121
PORTAINER.md
Normal file
121
PORTAINER.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Portainer deployment
|
||||
|
||||
DTF follows the same operating model as Graphs and ComporHUB: Gitea builds
|
||||
prebuilt images, pushes them to the Gitea registry, and calls one Portainer
|
||||
webhook. Portainer owns and redeploys one Docker Swarm stack named `dtf-cloud`.
|
||||
|
||||
The production stack is `deploy/stack.yaml`. It contains Site, Kanban, API,
|
||||
worker, PostgreSQL, ClamAV, and a one-time database initializer. Production uses
|
||||
Cloudflare R2, so MinIO is not part of this stack.
|
||||
|
||||
## 1. Gitea configuration
|
||||
|
||||
The single workflow is `.gitea/workflows/deploy.yml`. Pull requests run static
|
||||
validation. A push to `main` runs the full isolated test suite, builds and scans
|
||||
the production images, publishes both `latest` and the full commit SHA, then
|
||||
calls Portainer.
|
||||
|
||||
Repository variables:
|
||||
|
||||
- `REGISTRY_HOST` — normally `gitea.blyzer.com.br`.
|
||||
- `REGISTRY_OWNER` — normally `blyzer`.
|
||||
- `PYTHON_BASE_IMAGE`, `NGINX_BASE_IMAGE`, `TRIVY_IMAGE` — approved immutable
|
||||
`@sha256:` image references.
|
||||
|
||||
Repository secrets:
|
||||
|
||||
- `REGISTRY_USERNAME` and `REGISTRY_TOKEN` — package write credentials.
|
||||
- `PORTAINER_WEBHOOK` — webhook generated by the `dtf-cloud` Portainer stack.
|
||||
|
||||
The webhook is called only after tests and HIGH/CRITICAL secret,
|
||||
misconfiguration, and image gates pass. The current local-only application
|
||||
fails the source preflight intentionally, so it cannot publish yet.
|
||||
|
||||
## 2. One-time Portainer resources
|
||||
|
||||
Use a Docker Swarm environment. Create a dedicated production PostgreSQL volume
|
||||
and set its name as `POSTGRES_VOLUME`. Label its Swarm node
|
||||
`dtf_database=true`. Do not reuse the localhost Compose volume.
|
||||
|
||||
Create each application credential under **Secrets**. Use versioned names such
|
||||
as `dtf_prod_database_url_v1`, then place only those names in the corresponding
|
||||
`*_SECRET` stack variables. The stack expects distinct secrets for:
|
||||
|
||||
- runtime and administrator database URLs;
|
||||
- database administrator and application-role passwords;
|
||||
- R2 access key and secret key;
|
||||
- operator password;
|
||||
- Mercado Pago token and webhook secret;
|
||||
- Tiny/Olist token;
|
||||
- WhatsApp token.
|
||||
|
||||
Credential values must never be entered into Git, `portainer.env`, or Gitea
|
||||
workflow variables.
|
||||
|
||||
## 3. Create the stack once
|
||||
|
||||
In Portainer select **Stacks → Add stack → Git repository**:
|
||||
|
||||
- Name: `dtf-cloud`
|
||||
- Repository: this Gitea repository
|
||||
- Reference: `main`
|
||||
- Compose path: `deploy/stack.yaml`
|
||||
- Registry: the private `gitea.blyzer.com.br` registry
|
||||
- Re-pull image: enabled
|
||||
- Automatic update: webhook
|
||||
|
||||
Load a completed copy of `deploy/portainer.env.example` as the stack's
|
||||
non-secret environment variables. Do not load the example unchanged: `TBD` and
|
||||
zero digests are deliberate blockers. Keep `IMAGE_TAG=latest` for normal
|
||||
webhook deployments.
|
||||
|
||||
Before entering those values in Portainer, validate the completed ignored file:
|
||||
|
||||
```bash
|
||||
set -a
|
||||
. deploy/portainer.env
|
||||
set +a
|
||||
python3 deploy/production_preflight.py
|
||||
```
|
||||
|
||||
The public reverse proxy should send the Site hostname to `SITE_PORT` and the
|
||||
Kanban hostname to `KANBAN_PORT`. `/api/` and `/portal.html` are served through
|
||||
the Site gateway. Preserve the original Host header. Do not expose PostgreSQL,
|
||||
API, worker, or ClamAV. Restrict the
|
||||
two published web ports at the host firewall so only the reverse proxy can use
|
||||
them, and terminate HTTPS at the proxy.
|
||||
|
||||
The `db-init` service completing and stopping is expected. The other six
|
||||
services must be healthy. A failed `db-init` task or an unhealthy service blocks
|
||||
acceptance.
|
||||
|
||||
## 4. Normal deployment
|
||||
|
||||
Push to `main`. Gitea validates, tests, scans, publishes these images, and calls
|
||||
the webhook:
|
||||
|
||||
```text
|
||||
gitea.blyzer.com.br/blyzer/dtf-api:latest
|
||||
gitea.blyzer.com.br/blyzer/dtf-api:<full-commit-sha>
|
||||
gitea.blyzer.com.br/blyzer/dtf-web:latest
|
||||
gitea.blyzer.com.br/blyzer/dtf-web:<full-commit-sha>
|
||||
```
|
||||
|
||||
In Portainer, verify the new image digests, service health, `db-init` result, and
|
||||
the public `/health` endpoints. Back up PostgreSQL and R2 before changes that
|
||||
affect stored data or retention.
|
||||
|
||||
## 5. Rollback
|
||||
|
||||
In the Portainer stack variables, change `IMAGE_TAG` from `latest` to the full
|
||||
SHA of the last known-good Gitea commit and redeploy. This rolls back Site, API,
|
||||
Kanban, and worker code; it does not undo database migrations or restore data.
|
||||
After recovery and a corrected release, return `IMAGE_TAG` to `latest`.
|
||||
|
||||
## Current blocker
|
||||
|
||||
This is the final deployment shape, but it is not authorized for production
|
||||
today. Production adapters, Docker-secret file loading, provider sandbox tests,
|
||||
current clean base images, approved inputs, and a production restore rehearsal
|
||||
are still required. Do not bypass `deploy/production_preflight.py` or the Gitea
|
||||
scan gates to make a deployment run.
|
||||
Reference in New Issue
Block a user