9.1 KiB
Self-hosted deploy: Gitea + Caddy + EMS (/opt/ems-deploy)
Runbook pro single-node Debian, Docker Compose, Gitea Actions bez Kubernetes. Doplňuje konkrétní stav serveru (ZFS, zfs storage driver, Caddy na hostu, Gitea v /opt/gitea-stack).
1. Co kde běží
| Oblast | Cesta / adresa | Poznámka |
|---|---|---|
| Gitea (HTTP) | https://git.vojacek.eu → Caddy → 127.0.0.1:3000 |
Gitea publikuje HTTP jen na loopback. |
| Gitea (Git SSH) | git.vojacek.eu:2222 → kontejner :22 |
Host SSH zůstává na :22. V app.ini: START_SSH_SERVER = false (kvůli SSH v base image). |
| Gitea stack | /opt/gitea-stack |
postgres, gitea, gitea-runner; vlastní docker-compose + .env. |
| Gitea runner | stejný stack | act_runner s přepsaným entrypoint/command; registrace ručně → runner/runner.json. |
| EMS deploy | /opt/ems-deploy |
Oddělený Compose projekt od Gitea. |
| EMS checkout | /opt/ems-deploy/app |
Git clone stejného repa jako v Gitea. |
| EMS Compose (runtime) | /opt/ems-deploy/docker-compose.yml |
Kopíruje se ze app/deploy/docker-compose.yml při každém deployi (deploy.sh). |
| EMS secrets | /opt/ems-deploy/.env |
Není v gitu; vzor .env.example v repu. |
PostgREST v produkčním deploy/docker-compose.yml: služba naslouchá v Docker síti na :3000, bez mapování na host — frontend v kontejneru volá postgrest:3000. Tím se vyhne kolizi s Gitea na hostovém 127.0.0.1:3000. Pokud někdy přidáš ports pro ladění, použij jiný host port než 3000.
2. Architektonický verdikt
Směr (push → Actions → skript na hostu → docker compose build && up) je správný pro cíl „jeden server, žádný registry, žádný DinD“.
Tvrdá fakta / rizika
- Job Gitea Actions neběží na hostu, ale ve job kontejneru (Docker executor). Kroky typu
run: /opt/ems-deploy/deploy.shtedy neuvidí hostovské cesty, dokud je nepřimountuješ do job kontejneru a nepřidáš je dovalid_volumesu runneru. - Mount Docker socketu do jobu = plná moc nad hostovským Dockerem (ekvivalent root pro kontejnery). Je to běžný pattern u self-hosted runnerů; bezpečnost = důvěra k přístupu do repa a k labelům runneru, ne veřejný runner.
docker: command not foundv jobu je očekávané u defaultního image — buď použij image sdocker+docker compose, nebo vůbec nevolaj docker uvnitř jobu (u tebe stačí spustitdeploy.sh, který mluví s hostovským daemonem přes socket).- ZFS + Docker
zfsdriver: snapshoty azfs listrostou s image vrstvami;deploy.shuž děládocker image prune -f(dangling). Hlídej místo na poolu a případně periodickýdocker system prune(opatrně — nesmí mazat věci, co potřebuješ). git reset --hard origin/main: rychlé a deterministické; jakýkoli lokální drift v/opt/ems-deploy/appna serveru se ztratí. Na produkční app adresář nesahat ručně — vždy přes git + deploy.- Dvě síťě:
ems_netvsgitea_net— služby se defaultně nevidí; pro EMS to obvykle nevadí. Integrovat Gitea DB s EMS nechceš.
3. Tok deploye (cílový)
flowchart LR
dev[Dev laptop] -->|git push| gitea[Gitea main]
gitea --> act[Gitea Actions]
act --> runner[act_runner + Docker]
runner --> ci[migration-check validate]
ci -->|needs ok| job[deploy job + /opt mount]
job --> sh[deploy.sh]
sh --> git[git fetch / reset main]
sh --> sync[install compose.yml]
sh --> fly[flyway validate then migrate]
sh --> dc["docker compose build && up -d"]
dc --> hostd[Host Docker Engine]
hostd --> ems[EMS stack]
- Vývoj lokálně, merge do
main, push nagit.vojacek.eu:2222. - Workflow
.gitea/workflows/deploy.yml(labelself-hosted) spustí job. - Job kontejner má
docker.socka rw mount/opt/ems-deploy; nainstalujegit,bash,docker-cli,docker-cli-compose(Alpine). /opt/ems-deploy/deploy.sh:flock,gitvapp/, syncdocker-compose.yml,docker compose config,flyway validate(soubory vsflyway_schema_historyna DB),flyway migrate,build,up -d,image prune -f.
Build probíhá na hostovském Dockeru (stejný daemon jako Gitea stack), bez DinD.
4. Bootstrap serveru (jednorázově)
Přesně podle hlavičky v deploy/deploy.sh:
sudo mkdir -p /opt/ems-deploy/app
sudo chown -R "$USER:$USER" /opt/ems-deploy
git clone ssh://git@git.vojacek.eu:2222/vojacekd/ems.git /opt/ems-deploy/app
cp /opt/ems-deploy/app/.env.example /opt/ems-deploy/.env
chmod 600 /opt/ems-deploy/.env
# doplnit secrets / DB / JWT / …
install -m 755 /opt/ems-deploy/app/deploy/deploy.sh /opt/ems-deploy/deploy.sh
/opt/ems-deploy/deploy.sh
Uživatel, pod kterým běží runnerův job (root v Alpine job image), musí mít právo číst .env, psát do app/.git a volat Docker. Typicky runner běží jako root → ověř oprávnění na /opt/ems-deploy.
5. Gitea runner — nutná úprava config.yaml
Aby job směl přimountovat hostové cesty, v runner/config.yaml (na serveru v /opt/gitea-stack/runner/config.yaml) musí být v container.valid_volumes povolené alespoň:
container:
network: host
privileged: false
valid_volumes:
- /opt/ems-deploy
- /var/run/docker.sock
Bez toho Actions mounty odmítnou / job spadne. Po změně restart runner kontejneru.
labels při registraci runneru musí odpovídat runs-on ve workflow (např. výchozí self-hosted).
6. Caddy a EMS (až bude veřejná doména)
Gitea blok v Caddyfile zůstává. Pro EMS přidej samostatný site blok, např.:
ems.vojacek.eu {
encode gzip
@api path /rest*
handle @api {
uri strip_prefix /rest
reverse_proxy 127.0.0.1:8080
}
handle {
reverse_proxy 127.0.0.1:8080
}
}
Upřesnění podle skutečného nginx ve frontend image: pokud API jde přes stejný origin a path /rest, výše může stačit jeden reverse_proxy na 127.0.0.1:8080 bez stripu — ověř v frontend konfiguraci. Pro PostgREST OpenAPI nastav v .env / PGRST_OPENAPI_SERVER_PROXY_URI veřejnou bázi URL.
Produkční compose mapuje frontend na 127.0.0.1:8080, backend na 127.0.0.1:8000.
7. CI a deploy (jeden workflow)
| Soubor | Účel |
|---|---|
.gitea/workflows/deploy.yml |
migration-check na pull_request a push (main, feature/**): checkout přes git fetch (bez Node), skript neměnnosti scripts/ci_check_migration_immutability.sh (proti origin/main nebo base SHA u PR), flyway validate proti DB z JDBC (viz secrets). deploy jen při push na main, needs: migration-check — pak /opt/ems-deploy/deploy.sh. |
Gitea secrets (repo):
| Secret | Účel |
|---|---|
EMS_CI_FLYWAY_URL |
JDBC na staging / sdílenou DB s aktuální flyway_schema_history (např. jdbc:postgresql://host:5432/ems). Pokud není nastaveno, krok remote validate se přeskočí (varování v logu); immutability check pořád běží. |
EMS_CI_FLYWAY_USER / EMS_CI_FLYWAY_PASSWORD |
Volitelně, pokud nejsou v URL. |
Job nemá container: — potřebuje hostovský docker + git (stejně jako deploy). Runner: valid_volumes pro /var/run/docker.sock (sekce 5).
Lokálně před commitem: z kořene repa s rozjetým Postgres z docker-compose.yml a aspoň jednou proběhlým migrate spusť ./scripts/flyway_validate_local.sh (flyway validate vůči lokální DB).
Flyway validate vs migrate: validate nekontroluje „dry-run“ celého SQL nové pending migrace; ověří hlavně shodu již zapsaných řádků ve flyway_schema_history se soubory v repu (checksumy atd.) — proto musí běžet proti běžící DB. migrate teprve aplikuje pending skripty.
workflow_dispatch na větvi main spustí migration-check a potom i deploy (stejná podmínka jako u push na main).
8. Co záměrně neděláme (zatím)
- Privátní Docker registry na stejném stroji.
- Buildkit cache export / remote cache.
- k3s/Kubernetes.
- Spouštění
docker composeuvnitř jobu bez přístupu k jednomu hostovskému daemonu (tj. bez soku bys musel do DinD nebo remote docker).
9. Rychlá kontrola po deployi
docker compose -f /opt/ems-deploy/docker-compose.yml --env-file /opt/ems-deploy/.env ps
curl -sf http://127.0.0.1:8000/docs >/dev/null && echo backend OK
curl -sf http://127.0.0.1:8080/ >/dev/null && echo frontend OK
10. Reset DB a obnova z dumpu
Kompletní postup (zastavení služeb, down, smazání volume db_data, jen db, import_ems_db.sh, Flyway, up): database-reset-and-restore.md.
11. Odkazy v repu
deploy/deploy.sh— jediný produkční vstup „co se na serveru spouští“.deploy/docker-compose.yml— šablona runtime Compose (kopíruje se do/opt/ems-deploy)..gitea/workflows/deploy.yml— napojení na runner + job kontejner.docs/database-reset-and-restore.md— zahození dat Postgres/Timescale a import zálohy.