Update infra docs with complete working setup
All checks were successful
Deploy to NAS / deploy (push) Successful in 44s
All checks were successful
Deploy to NAS / deploy (push) Successful in 44s
- README.md: rewrite with accurate setup steps including all lessons learned - CLAUDE.md: update with working CI/CD patterns and known pitfalls - gitea-actions.md: new file documenting Gitea Actions usage, expressions, act_runner config, and troubleshooting for future projects Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
207
infra/CLAUDE.md
207
infra/CLAUDE.md
@@ -1,155 +1,124 @@
|
|||||||
# CLAUDE.md — Pamietnik Infrastruktur
|
# CLAUDE.md — NAS Infrastruktur
|
||||||
|
|
||||||
Diese Datei ist Kontext für eine dedizierte Infra-Session. Hier ist alles beschrieben, was zum Aufbau und Betrieb der Infrastruktur auf der Synology NAS notwendig ist.
|
Kontext für Claude Code Sessions zum Thema Deployment und Infrastruktur auf der Synology NAS.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Umgebung
|
## Umgebung
|
||||||
|
|
||||||
- **NAS:** Synology DiskStation, DSM 7.x
|
- **NAS:** Synology DiskStation, DSM 7.x, IP: `192.168.1.4`
|
||||||
- **Docker-Datenpfad:** `/volume2/docker/` (alle Container-Daten hier ablegen)
|
- **SSH:** `ssh jacek@192.168.1.4` — Docker-Befehle erfordern `sudo`
|
||||||
- **Docker-Socket:** `/var/run/docker.sock`
|
- **Docker-Datenpfad:** `/volume2/docker/` — alle persistenten Daten hier
|
||||||
- **Gitea:** läuft als Docker-Container auf der NAS, erreichbar unter `http://localhost:3000`
|
- **Docker-Binary auf NAS:** `/usr/local/bin/docker`
|
||||||
- **SSH-Zugriff:** `ssh jacek@<NAS-IP>`, sudo-Befehle erforderlich für Docker
|
- **Gitea:** `http://192.168.1.4:3000` (Docker-Container auf NAS)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architektur
|
## Laufende Dienste
|
||||||
|
|
||||||
```
|
| Dienst | Container | Port | Pfad |
|
||||||
Synology NAS
|
|--------|-----------|------|------|
|
||||||
├── Gitea (Docker) :3000 — Git-Server + CI/CD
|
| PostgreSQL | `shared-postgres-1` | 5433 | `/volume2/docker/shared/` |
|
||||||
├── act_runner (Docker) — Gitea Actions Runner
|
| act_runner | `gitea-runner` | — | `/volume2/docker/gitea-runner/` |
|
||||||
│
|
| Pamietnik | `pamietnik-api-1` | 9050 | `/volume2/docker/pamietnik/` |
|
||||||
├── /volume2/docker/shared/
|
|
||||||
│ └── postgres:16-alpine :5433 — Geteilte DB für alle Projekte
|
---
|
||||||
│
|
|
||||||
└── /volume2/docker/pamietnik/
|
## Datenbankzugriff
|
||||||
└── api (Go + SPA) :9050 — Pamietnik App
|
|
||||||
|
**Aus Containern:** `host-gateway:5433`
|
||||||
|
(`host-gateway` ist Docker's eingebauter Alias für den Host — in `extra_hosts` und `docker-compose.yml` deklarieren)
|
||||||
|
|
||||||
|
**Remote (Heimnetz):** `psql -h 192.168.1.4 -p 5433 -U <user> -d <db>`
|
||||||
|
|
||||||
|
**Direkt auf NAS:**
|
||||||
|
```bash
|
||||||
|
sudo docker exec -it shared-postgres-1 psql -U postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
**Verbindung App → Datenbank:** über `host-gateway:5433` (Docker-interner Alias für den NAS-Host)
|
**Wichtig PostgreSQL 15+:** Nach `GRANT ALL PRIVILEGES ON DATABASE` zusätzlich:
|
||||||
|
```sql
|
||||||
---
|
GRANT ALL ON SCHEMA public TO <user>;
|
||||||
|
|
||||||
## Verzeichnisse auf der NAS
|
|
||||||
|
|
||||||
| Pfad | Inhalt |
|
|
||||||
|------|--------|
|
|
||||||
| `/volume2/docker/shared/pgdata` | PostgreSQL-Daten (persistent) |
|
|
||||||
| `/volume2/docker/shared/.env` | Secrets: `POSTGRES_PASSWORD` |
|
|
||||||
| `/volume2/docker/shared/docker-compose.yml` | Shared Stack (Postgres + pgAdmin) |
|
|
||||||
| `/volume2/docker/pamietnik/uploads` | Hochgeladene Bilder (persistent) |
|
|
||||||
| `/volume2/docker/pamietnik/.env` | `DB_PASSWORD`, `APP_PORT` |
|
|
||||||
| `/volume2/docker/pamietnik/docker-compose.yml` | Wird via CI/CD aus dem Repo kopiert |
|
|
||||||
| `/volume2/docker/gitea-runner/` | act_runner Konfiguration & Daten |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Shared Stack (`/volume2/docker/shared/docker-compose.yml`)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "5433:5432"
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
|
||||||
volumes:
|
|
||||||
- /volume2/docker/shared/pgdata:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Pamietnik Stack (`docker-compose.yml` im Repo-Root)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
api:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
|
||||||
- "${APP_PORT:-9050}:8080"
|
|
||||||
extra_hosts:
|
|
||||||
- "host-gateway:host-gateway"
|
|
||||||
environment:
|
|
||||||
DATABASE_URL: postgres://${DB_USER:-pamietnik}:${DB_PASSWORD:?DB_PASSWORD is required}@host-gateway:5433/${DB_NAME:-pamietnik}?sslmode=disable
|
|
||||||
LISTEN_ADDR: :8080
|
|
||||||
UPLOAD_DIR: /uploads
|
|
||||||
volumes:
|
|
||||||
- /volume2/docker/pamietnik/uploads:/uploads
|
|
||||||
restart: unless-stopped
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CI/CD: Gitea Actions
|
## CI/CD: Gitea Actions
|
||||||
|
|
||||||
**Workflow:** `.gitea/workflows/deploy.yml` — wird bei Push auf `main` ausgeführt.
|
**Workflow-Datei:** `.gitea/workflows/deploy.yml`
|
||||||
|
**Trigger:** Push auf `main`
|
||||||
|
|
||||||
**Runner:** `gitea/act_runner` Container auf der NAS mit `--network host` und Docker-Socket-Mount.
|
**Runner-Setup:**
|
||||||
|
- Container: `gitea/act_runner:latest` mit `--network host`
|
||||||
|
- `GITEA_INSTANCE_URL` muss NAS-IP sein (`192.168.1.4:3000`), **nicht** `localhost`
|
||||||
|
- `config.yaml` braucht `valid_volumes: [/volume2/docker]` sonst werden Mounts ignoriert
|
||||||
|
|
||||||
**Gitea Secrets** (Repository → Einstellungen → Actions → Secrets):
|
**Job-Container:** `docker:latest` mit `-v /volume2/docker:/volume2/docker`
|
||||||
- `DB_PASSWORD` — Passwort des `pamietnik` DB-Users
|
- Docker CLI ist im Image enthalten
|
||||||
- `DEPLOY_DIR` — `/volume2/docker/pamietnik`
|
- Socket wird automatisch vom Runner propagiert (nicht nochmal in `options` mounten → Duplicate-Fehler)
|
||||||
|
- `wget` statt `curl` verwenden (`curl` nicht im Image)
|
||||||
|
|
||||||
**Gitea Variables** (Repository → Einstellungen → Actions → Variables):
|
**Gitea Konfiguration:**
|
||||||
- `DB_USER` — `pamietnik`
|
- Nicht-sensitive Werte (Pfade, Ports, DB-Namen) → **Variables** (`vars.NAME`)
|
||||||
- `DB_NAME` — `pamietnik`
|
- Passwörter, Tokens → **Secrets** (`secrets.NAME`)
|
||||||
- `APP_PORT` — `9050`
|
- Variables mit Leerzeichen am Ende → Parsing-Fehler in Expressions
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Dienste & URLs
|
## Deployment-Muster für neue Projekte
|
||||||
|
|
||||||
| Dienst | URL |
|
```yaml
|
||||||
|--------|-----|
|
# docker-compose.yml im Repo
|
||||||
| Pamietnik | `http://<NAS-IP>:9050` |
|
services:
|
||||||
| Gitea | `http://<NAS-IP>:3000` |
|
api:
|
||||||
| PostgreSQL | `<NAS-IP>:5433` |
|
build: .
|
||||||
|
ports:
|
||||||
|
- "${APP_PORT:-8080}:8080"
|
||||||
|
extra_hosts:
|
||||||
|
- "host-gateway:host-gateway"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@host-gateway:5433/${DB_NAME}
|
||||||
|
volumes:
|
||||||
|
- /volume2/docker/<projekt>/uploads:/uploads
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
---
|
```yaml
|
||||||
|
# .gitea/workflows/deploy.yml
|
||||||
## Wichtige Befehle
|
jobs:
|
||||||
|
deploy:
|
||||||
```bash
|
runs-on: self-hosted
|
||||||
# Shared Stack starten
|
container:
|
||||||
cd /volume2/docker/shared && sudo docker compose up -d
|
image: docker:latest
|
||||||
|
options: -v /volume2/docker:/volume2/docker
|
||||||
# Logs
|
steps:
|
||||||
sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f
|
- name: Pull code
|
||||||
sudo docker compose -f /volume2/docker/pamietnik/docker-compose.yml logs -f api
|
run: |
|
||||||
|
if [ -d "${{ vars.DEPLOY_DIR }}/.git" ]; then
|
||||||
# Datenbank-User anlegen
|
git -C ${{ vars.DEPLOY_DIR }} pull
|
||||||
sudo docker exec -it shared-postgres-1 psql -U postgres
|
else
|
||||||
|
git clone http://192.168.1.4:3000/<org>/<repo>.git ${{ vars.DEPLOY_DIR }}
|
||||||
# Backup
|
fi
|
||||||
sudo docker exec shared-postgres-1 pg_dump -U postgres pamietnik \
|
- name: Write .env
|
||||||
> /volume2/docker/shared/backup_$(date +%Y%m%d).sql
|
run: printf 'DB_PASSWORD=%s\n' '${{ secrets.DB_PASSWORD }}' > ${{ vars.DEPLOY_DIR }}/.env
|
||||||
|
- name: Build & Deploy
|
||||||
|
run: docker compose -f ${{ vars.DEPLOY_DIR }}/docker-compose.yml up --build -d
|
||||||
|
- name: Health check
|
||||||
|
run: sleep 15 && wget -qO- http://192.168.1.4:${{ vars.APP_PORT }}/healthz || exit 1
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Synology-spezifische Hinweise
|
## Bekannte Fallstricke
|
||||||
|
|
||||||
- Docker-Befehle erfordern `sudo`
|
- **`Secure: true` auf Session-Cookies** schlägt fehl bei HTTP → `Secure: false` setzen
|
||||||
- Ports unter 1024 können Container nicht binden → `PGADMIN_LISTEN_PORT: 8080` nötig
|
- **Ports unter 1024** können Container auf Synology nicht binden → internen Port > 1024 wählen
|
||||||
- pgAdmin-Verzeichnis braucht UID 5050: `sudo chown -R 5050:5050 /volume2/docker/shared/pgadmin`
|
- **`localhost`** im Job-Container zeigt auf den Container selbst, nicht auf die NAS
|
||||||
- Docker-Socket ist unter `/var/run/docker.sock` erreichbar
|
- **Volume-Mounts** in Workflow `container.options` werden doppelt gemountet wenn identisch mit Runner-Mount
|
||||||
- Container Manager UI unterstützt keinen Datei-Mount für den Docker-Socket → `docker run` via SSH nutzen
|
- **`/volume2/docker`** muss in `valid_volumes` der `config.yaml` stehen sonst wird Mount ignoriert
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Vollständige Setup-Anleitung
|
## Vollständige Setup-Anleitung
|
||||||
|
|
||||||
Siehe `infra/README.md` im Repo.
|
Siehe `infra/README.md`
|
||||||
|
|||||||
245
infra/README.md
245
infra/README.md
@@ -1,85 +1,48 @@
|
|||||||
# Infrastruktur & Deployment
|
# Infrastruktur & Deployment
|
||||||
|
|
||||||
## Übersicht
|
## Architektur
|
||||||
|
|
||||||
```
|
```
|
||||||
Synology NAS
|
Synology NAS (192.168.1.4)
|
||||||
├── /volume2/docker/shared/ ← Geteilte Infrastruktur (PostgreSQL + pgAdmin)
|
├── Gitea :3000 — Git + CI/CD
|
||||||
│ ├── docker-compose.yml
|
├── act_runner — Gitea Actions Runner
|
||||||
│ ├── .env
|
├── /volume2/docker/shared/
|
||||||
│ ├── pgdata/ ← PostgreSQL-Daten (persistent)
|
│ ├── postgres:16-alpine :5433 — Geteilte DB (alle Projekte)
|
||||||
│ └── pgadmin/ ← pgAdmin-Daten (persistent)
|
│ └── pgdata/ — Persistente Daten
|
||||||
│
|
└── /volume2/docker/<projekt>/
|
||||||
└── /volume2/docker/pamietnik/ ← Pamietnik-Deployment
|
├── <app>-Container :<port> — Anwendung
|
||||||
├── docker-compose.yml ← Kopie aus dem Repo (via CI/CD)
|
├── uploads/ — Persistente Uploads
|
||||||
├── .env
|
└── .env — Projekt-Secrets
|
||||||
└── uploads/ ← Hochgeladene Bilder (persistent)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Datenbankverbindung aus Containern:** `host-gateway:5433`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Geteilte Infrastruktur einrichten (einmalig)
|
## 1. Shared PostgreSQL einrichten (einmalig)
|
||||||
|
|
||||||
### Verzeichnisse anlegen
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /volume2/docker/shared/pgdata
|
sudo mkdir -p /volume2/docker/shared/pgdata
|
||||||
|
sudo cp infra/docker-compose.yml /volume2/docker/shared/
|
||||||
|
echo "POSTGRES_PASSWORD=<sicheres-passwort>" | sudo tee /volume2/docker/shared/.env
|
||||||
|
cd /volume2/docker/shared && sudo docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### docker-compose.yml kopieren
|
Datenbank anlegen:
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo cp infra/docker-compose.yml /volume2/docker/shared/docker-compose.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
### .env anlegen
|
|
||||||
|
|
||||||
Datei `/volume2/docker/shared/.env`:
|
|
||||||
|
|
||||||
```env
|
|
||||||
POSTGRES_PASSWORD=<sicheres-passwort>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Starten
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /volume2/docker/shared
|
|
||||||
sudo docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### Datenbank & User anlegen
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo docker exec -it shared-postgres-1 psql -U postgres
|
sudo docker exec -it shared-postgres-1 psql -U postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE DATABASE pamietnik;
|
CREATE DATABASE <dbname>;
|
||||||
CREATE USER pamietnik WITH PASSWORD 'deinPasswort';
|
CREATE USER <dbuser> WITH PASSWORD '<passwort>';
|
||||||
GRANT ALL PRIVILEGES ON DATABASE pamietnik TO pamietnik;
|
GRANT ALL PRIVILEGES ON DATABASE <dbname> TO <dbuser>;
|
||||||
|
GRANT ALL ON SCHEMA public TO <dbuser>; -- wichtig für PostgreSQL 15+
|
||||||
\q
|
\q
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Pamietnik-Deployment einrichten (einmalig)
|
## 2. act_runner einrichten (einmalig)
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo mkdir -p /volume2/docker/pamietnik/uploads
|
|
||||||
```
|
|
||||||
|
|
||||||
Datei `/volume2/docker/pamietnik/.env`:
|
|
||||||
|
|
||||||
```env
|
|
||||||
DB_PASSWORD=<passwort-von-oben>
|
|
||||||
APP_PORT=9050
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Gitea CI/CD einrichten (einmalig)
|
|
||||||
|
|
||||||
### act_runner starten
|
|
||||||
|
|
||||||
Token holen: **Gitea → Site-Administration → Actions → Runner → Runner erstellen**
|
Token holen: **Gitea → Site-Administration → Actions → Runner → Runner erstellen**
|
||||||
|
|
||||||
@@ -90,76 +53,144 @@ sudo docker run -d \
|
|||||||
--network host \
|
--network host \
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
-v /volume2/docker/gitea-runner:/data \
|
-v /volume2/docker/gitea-runner:/data \
|
||||||
-e GITEA_INSTANCE_URL=http://localhost:3000 \
|
-e GITEA_INSTANCE_URL=http://192.168.1.4:3000 \
|
||||||
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token-aus-gitea> \
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
|
||||||
-e GITEA_RUNNER_NAME=nas-runner \
|
-e GITEA_RUNNER_NAME=nas-runner \
|
||||||
-e GITEA_RUNNER_LABELS=self-hosted,linux,amd64 \
|
-e GITEA_RUNNER_LABELS=self-hosted,linux,amd64 \
|
||||||
gitea/act_runner:latest
|
gitea/act_runner:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Gitea Secrets & Variables setzen
|
> **Wichtig:** `GITEA_INSTANCE_URL` muss die NAS-IP sein, nicht `localhost` —
|
||||||
|
> Job-Container können `localhost` nicht auflösen.
|
||||||
|
|
||||||
**Repository → Einstellungen → Actions → Secrets:**
|
`/volume2/docker/gitea-runner/config.yaml` anlegen:
|
||||||
|
```yaml
|
||||||
|
runner:
|
||||||
|
name: "nas-runner"
|
||||||
|
|
||||||
|
container:
|
||||||
|
valid_volumes:
|
||||||
|
- /volume2/docker
|
||||||
|
```
|
||||||
|
|
||||||
|
Runner neu starten: `sudo docker restart gitea-runner`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Neues Projekt deployen
|
||||||
|
|
||||||
|
### 3.1 Verzeichnis anlegen
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /volume2/docker/<projekt>/uploads
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 docker-compose.yml (im Repo)
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "${APP_PORT:-8080}:8080"
|
||||||
|
extra_hosts:
|
||||||
|
- "host-gateway:host-gateway"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@host-gateway:5433/${DB_NAME}
|
||||||
|
volumes:
|
||||||
|
- /volume2/docker/<projekt>/uploads:/uploads
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Gitea Secrets & Variables
|
||||||
|
|
||||||
|
**Secrets** (Repository → Einstellungen → Actions → Secrets):
|
||||||
| Secret | Wert |
|
| Secret | Wert |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| `DB_PASSWORD` | Passwort des `pamietnik` DB-Users |
|
| `DB_PASSWORD` | Datenbankpasswort |
|
||||||
| `DEPLOY_DIR` | `/volume2/docker/pamietnik` |
|
|
||||||
|
|
||||||
**Repository → Einstellungen → Actions → Variables:**
|
|
||||||
|
|
||||||
|
**Variables** (Repository → Einstellungen → Actions → Variables):
|
||||||
| Variable | Wert |
|
| Variable | Wert |
|
||||||
|----------|------|
|
|----------|------|
|
||||||
| `DB_USER` | `pamietnik` |
|
| `DEPLOY_DIR` | `/volume2/docker/<projekt>` |
|
||||||
| `DB_NAME` | `pamietnik` |
|
| `DB_USER` | `<dbuser>` |
|
||||||
| `APP_PORT` | `9050` |
|
| `DB_NAME` | `<dbname>` |
|
||||||
|
| `APP_PORT` | `<port>` |
|
||||||
|
|
||||||
---
|
### 3.4 Gitea Actions Workflow (`.gitea/workflows/deploy.yml`)
|
||||||
|
|
||||||
## 4. Dienste & URLs
|
|
||||||
|
|
||||||
| Dienst | URL |
|
|
||||||
|--------|-----|
|
|
||||||
| Pamietnik App | `http://<NAS-IP>:9050` |
|
|
||||||
| PostgreSQL | `psql -h <NAS-IP> -p 5433 -U pamietnik -d pamietnik` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Neues Projekt hinzufügen
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo docker exec -it shared-postgres-1 psql -U postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE DATABASE neuprojekt;
|
|
||||||
CREATE USER neuprojekt WITH PASSWORD 'passwort';
|
|
||||||
GRANT ALL PRIVILEGES ON DATABASE neuprojekt TO neuprojekt;
|
|
||||||
\q
|
|
||||||
```
|
|
||||||
|
|
||||||
In `docker-compose.yml` des neuen Projekts:
|
|
||||||
```yaml
|
```yaml
|
||||||
extra_hosts:
|
name: Deploy to NAS
|
||||||
- "host-gateway:host-gateway"
|
|
||||||
environment:
|
on:
|
||||||
DATABASE_URL: postgres://neuprojekt:passwort@host-gateway:5433/neuprojekt
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: self-hosted
|
||||||
|
container:
|
||||||
|
image: docker:latest
|
||||||
|
options: -v /volume2/docker:/volume2/docker
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Pull code
|
||||||
|
run: |
|
||||||
|
if [ -d "${{ vars.DEPLOY_DIR }}/.git" ]; then
|
||||||
|
git -C ${{ vars.DEPLOY_DIR }} pull
|
||||||
|
else
|
||||||
|
git clone http://192.168.1.4:3000/<org>/<repo>.git ${{ vars.DEPLOY_DIR }}
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Write .env
|
||||||
|
run: printf 'DB_PASSWORD=%s\n' '${{ secrets.DB_PASSWORD }}' > ${{ vars.DEPLOY_DIR }}/.env
|
||||||
|
|
||||||
|
- name: Build & Deploy
|
||||||
|
run: docker compose -f ${{ vars.DEPLOY_DIR }}/docker-compose.yml up --build -d
|
||||||
|
|
||||||
|
- name: Health check
|
||||||
|
run: |
|
||||||
|
sleep 15
|
||||||
|
wget -qO- http://192.168.1.4:${{ vars.APP_PORT }}/healthz || exit 1
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Wartung
|
## 4. Synology-spezifische Hinweise
|
||||||
|
|
||||||
|
| Problem | Lösung |
|
||||||
|
|---------|--------|
|
||||||
|
| `permission denied` bei Docker-Befehlen | Immer `sudo docker ...` verwenden |
|
||||||
|
| Ports < 1024 nicht bindbar | Internen Port > 1024 wählen, z.B. `LISTEN_PORT: 8080` |
|
||||||
|
| Docker-Socket im Container Manager UI nicht mountbar | `docker run` via SSH ausführen |
|
||||||
|
| `act_runner` kann `localhost:3000` nicht erreichen | NAS-IP statt `localhost` in `GITEA_INSTANCE_URL` |
|
||||||
|
| Volume-Mount wird ignoriert | `valid_volumes` in `config.yaml` eintragen |
|
||||||
|
| Session-Cookies funktionieren nicht | `Secure: false` setzen bei HTTP-Deployment |
|
||||||
|
| `GRANT ALL PRIVILEGES` reicht nicht | Zusätzlich `GRANT ALL ON SCHEMA public` ausführen (PG 15+) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Wartung
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Logs
|
# Logs
|
||||||
sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f
|
sudo docker compose -f /volume2/docker/shared/docker-compose.yml logs -f postgres
|
||||||
sudo docker compose -f /volume2/docker/pamietnik/docker-compose.yml logs -f api
|
sudo docker compose -f /volume2/docker/<projekt>/docker-compose.yml logs -f
|
||||||
|
|
||||||
# Backup
|
# Backup
|
||||||
sudo docker exec shared-postgres-1 pg_dump -U postgres pamietnik \
|
sudo docker exec shared-postgres-1 pg_dump -U postgres <dbname> \
|
||||||
> /volume2/docker/shared/backup_$(date +%Y%m%d).sql
|
> /volume2/docker/shared/backup_$(date +%Y%m%d)_<dbname>.sql
|
||||||
|
|
||||||
# Stoppen
|
# Alle Container stoppen
|
||||||
sudo docker compose -f /volume2/docker/shared/docker-compose.yml down
|
sudo docker compose -f /volume2/docker/shared/docker-compose.yml down
|
||||||
sudo docker compose -f /volume2/docker/pamietnik/docker-compose.yml down
|
sudo docker compose -f /volume2/docker/<projekt>/docker-compose.yml down
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Dienste & Ports
|
||||||
|
|
||||||
|
| Dienst | Port | Beschreibung |
|
||||||
|
|--------|------|--------------|
|
||||||
|
| Gitea | 3000 | Git-Server + CI/CD |
|
||||||
|
| PostgreSQL | 5433 | Geteilte DB (intern: 5432) |
|
||||||
|
| Pamietnik | 9050 | Tagebuch-App |
|
||||||
|
|||||||
156
infra/gitea-actions.md
Normal file
156
infra/gitea-actions.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# Gitea Actions — Verwendung & Referenz
|
||||||
|
|
||||||
|
## Grundprinzip
|
||||||
|
|
||||||
|
Gitea Actions ist kompatibel mit GitHub Actions Syntax. Workflow-Dateien liegen in `.gitea/workflows/*.yml` im Repository. Bei Push auf `main` wird der Workflow automatisch ausgeführt.
|
||||||
|
|
||||||
|
```
|
||||||
|
Push → Gitea → act_runner auf NAS → Job-Container (docker:latest) → Deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow-Struktur
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Deploy to NAS # Anzeigename in Gitea UI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main] # Trigger: Push auf main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy: # Job-Name
|
||||||
|
runs-on: self-hosted # Welcher Runner-Typ
|
||||||
|
container:
|
||||||
|
image: docker:latest # Job läuft in diesem Container
|
||||||
|
options: -v /volume2/docker:/volume2/docker # Volume-Mounts
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Schritt 1
|
||||||
|
run: echo "Shell-Befehl"
|
||||||
|
|
||||||
|
- name: Schritt 2
|
||||||
|
run: |
|
||||||
|
echo "Mehrzeiliger"
|
||||||
|
echo "Shell-Block"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Expressions
|
||||||
|
|
||||||
|
Werte aus Gitea zur Laufzeit einsetzen:
|
||||||
|
|
||||||
|
| Expression | Quelle | Beispiel |
|
||||||
|
|------------|--------|---------|
|
||||||
|
| `${{ secrets.NAME }}` | Secrets (maskiert in Logs) | `${{ secrets.DB_PASSWORD }}` |
|
||||||
|
| `${{ vars.NAME }}` | Variables (sichtbar in Logs) | `${{ vars.DEPLOY_DIR }}` |
|
||||||
|
| `${{ github.repository }}` | Repo-Metadaten | `christoph/pamietnik` |
|
||||||
|
| `${{ github.sha }}` | Aktueller Commit-Hash | `abc1234...` |
|
||||||
|
|
||||||
|
**Secrets vs Variables:**
|
||||||
|
- **Secrets** → Passwörter, Tokens, API-Keys (`secrets.NAME`)
|
||||||
|
- **Variables** → Pfade, Ports, Namen, alles Nicht-Sensible (`vars.NAME`)
|
||||||
|
|
||||||
|
Setzen unter: **Repository → Einstellungen → Actions → Secrets / Variables**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## act_runner auf der NAS
|
||||||
|
|
||||||
|
Der Runner empfängt Jobs von Gitea und führt sie aus.
|
||||||
|
|
||||||
|
### Laufender Container
|
||||||
|
```bash
|
||||||
|
sudo docker ps | grep gitea-runner
|
||||||
|
sudo docker logs gitea-runner -f # Live-Logs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Konfiguration
|
||||||
|
`/volume2/docker/gitea-runner/config.yaml`:
|
||||||
|
```yaml
|
||||||
|
runner:
|
||||||
|
name: "nas-runner"
|
||||||
|
|
||||||
|
container:
|
||||||
|
valid_volumes:
|
||||||
|
- /volume2/docker # Erlaubt Volume-Mounts in Job-Containern
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wichtige Einschränkungen auf Synology
|
||||||
|
- `GITEA_INSTANCE_URL` muss NAS-IP sein (`192.168.1.4:3000`), **nicht** `localhost`
|
||||||
|
→ Job-Container sehen `localhost` als sich selbst, nicht als NAS
|
||||||
|
- Docker-Socket wird automatisch in Job-Container propagiert
|
||||||
|
→ **nicht** nochmals in `container.options` mounten (→ Duplicate-Fehler)
|
||||||
|
- `valid_volumes` in `config.yaml` pflegen, sonst werden Mounts ignoriert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Job-Container: docker:latest
|
||||||
|
|
||||||
|
Für Deployments wird `docker:latest` als Job-Container verwendet:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
container:
|
||||||
|
image: docker:latest
|
||||||
|
options: -v /volume2/docker:/volume2/docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**Was ist enthalten:**
|
||||||
|
- Docker CLI (`docker`, `docker compose`)
|
||||||
|
- `wget`, `sh`, `git`
|
||||||
|
- **Nicht enthalten:** `curl`, `bash` (nur `sh`)
|
||||||
|
|
||||||
|
**Volume-Mount `-v /volume2/docker:/volume2/docker`:**
|
||||||
|
Macht den NAS-Dateipfad im Container verfügbar — notwendig damit git clone, .env schreiben und docker compose auf die richtigen Dateien zugreifen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Secrets & Variables anlegen
|
||||||
|
|
||||||
|
**Gitea UI:**
|
||||||
|
1. Repository → Einstellungen → Actions
|
||||||
|
2. "Secrets" für Passwörter/Tokens → `secrets.NAME`
|
||||||
|
3. "Variables" für Konfiguration → `vars.NAME`
|
||||||
|
|
||||||
|
**Typische Variablen pro Projekt:**
|
||||||
|
|
||||||
|
| Name | Typ | Beispielwert |
|
||||||
|
|------|-----|-------------|
|
||||||
|
| `DEPLOY_DIR` | Variable | `/volume2/docker/pamietnik` |
|
||||||
|
| `APP_PORT` | Variable | `9050` |
|
||||||
|
| `DB_USER` | Variable | `pamietnik` |
|
||||||
|
| `DB_NAME` | Variable | `pamietnik` |
|
||||||
|
| `DB_PASSWORD` | Secret | `<passwort>` |
|
||||||
|
|
||||||
|
> **Achtung:** Keine Leerzeichen am Ende von Variable-Werten — führt zu Parsing-Fehlern in Expressions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow-Status überwachen
|
||||||
|
|
||||||
|
**Gitea UI:** Repository → Actions → laufenden/letzten Job anklicken
|
||||||
|
|
||||||
|
**Runner-Logs live:**
|
||||||
|
```bash
|
||||||
|
sudo docker logs gitea-runner -f
|
||||||
|
```
|
||||||
|
|
||||||
|
**Job neu triggern (leerer Commit):**
|
||||||
|
```bash
|
||||||
|
git commit --allow-empty -m "ci: retrigger deploy" && git push
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlersuche
|
||||||
|
|
||||||
|
| Fehler | Ursache | Lösung |
|
||||||
|
|--------|---------|--------|
|
||||||
|
| `connection refused :3000` | `localhost` statt NAS-IP | `GITEA_INSTANCE_URL=http://192.168.1.4:3000` |
|
||||||
|
| `Duplicate mount point` | Socket doppelt gemountet | Nur einmal in `container.options` oder nur Runner-Mount |
|
||||||
|
| `not a valid volume, will be ignored` | `valid_volumes` fehlt | In `config.yaml` eintragen |
|
||||||
|
| `curl: not found` | nicht in docker:latest | `wget -qO-` verwenden |
|
||||||
|
| `cannot find node in PATH` | `actions/checkout@v4` braucht Node | Stattdessen `git clone/pull` direkt verwenden |
|
||||||
|
| Variable leer (`/docker-compose.yml`) | Als Secret statt Variable gesetzt | Unter Variables (nicht Secrets) anlegen |
|
||||||
Reference in New Issue
Block a user