Skip to content

admin/dorfarchiv

PublicMirror

Mirror of https://forge.levinkeller.de/levin/dorfarchiv.git

Migrate from Coolify to K3S/Flux GitOps (Variant B)

- Dockerfile: 2-stage node:22-alpine build -> nginx:alpine serving build/
- nginx.conf: listen 8080, relative redirects, try_files, /assets/ caching, 404
- .github/workflows/deploy.yml: native amd64+arm64 runners, per-arch push,
  imagetools merge, kustomize image bump to main (no QEMU)
- deploy/overlays/production: namespace/deployment/service/ingress/kustomization
  for host archiv.xn--rssing-wxa.de (public, no oauth2-proxy), TLS via cert-manager

LFS not required at build time: *.zip/*.MP4 are linked as absolute external
URLs to git-lfs.archiv.xn--rssing-wxa.de; relatively-referenced PDFs/DWGs/JPEG
are committed as normal git blobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Levin Keller authored 1 parent b01dcd4commit 153e29ff47372717ae50357fb96b7db879718549Browse files

Showing 8 changed files with 264 additions and 0 deletions.

Added.github/workflows/deploy.yml+113 -0
@@ -0,0 +1,113 @@
1added: name: Build & Deploy
2added:
3added: # Variant B: Multi-Arch-Image auf NATIVEN Runnern bauen (amd64 + arm64),
4added: # per-arch pushen, mit `docker buildx imagetools create` zu einem Manifest
5added: # zusammenführen — niemals QEMU. Danach den Image-Tag im Production-Overlay
6added: # bumpen und nach main committen ([skip ci]-Loop-Guard). Flux deployt.
7added:
8added: on:
9added: push:
10added: branches: [main]
11added: paths-ignore:
12added: - "deploy/**"
13added: - "**/*.md"
14added: workflow_dispatch:
15added:
16added: concurrency:
17added: group: deploy-${{ github.ref }}
18added: cancel-in-progress: false
19added:
20added: env:
21added: IMAGE: ghcr.io/dorfpflege-rossing/dorfarchiv
22added:
23added: jobs:
24added: # 1) Pro Architektur ein natives Image bauen und als per-arch Tag pushen.
25added: build:
26added: strategy:
27added: fail-fast: true
28added: matrix:
29added: include:
30added: - arch: amd64
31added: runner: ubuntu-latest
32added: - arch: arm64
33added: runner: ubuntu-24.04-arm
34added: runs-on: ${{ matrix.runner }}
35added: permissions:
36added: contents: read
37added: packages: write
38added: steps:
39added: - uses: actions/checkout@v4
40added:
41added: - name: Set up Docker Buildx
42added: uses: docker/setup-buildx-action@v3
43added:
44added: - name: Log in to GHCR
45added: uses: docker/login-action@v3
46added: with:
47added: registry: ghcr.io
48added: username: ${{ github.actor }}
49added: password: ${{ secrets.GITHUB_TOKEN }}
50added:
51added: - name: Build & push (${{ matrix.arch }})
52added: uses: docker/build-push-action@v6
53added: with:
54added: context: .
55added: platforms: linux/${{ matrix.arch }}
56added: push: true
57added: provenance: false
58added: tags: ${{ env.IMAGE }}:${{ github.sha }}-${{ matrix.arch }}
59added: cache-from: type=gha,scope=${{ matrix.arch }}
60added: cache-to: type=gha,scope=${{ matrix.arch }},mode=max
61added:
62added: # 2) Per-arch Tags zu einem Multi-Arch-Manifest zusammenführen (kein QEMU).
63added: merge:
64added: needs: build
65added: runs-on: ubuntu-latest
66added: permissions:
67added: contents: read
68added: packages: write
69added: steps:
70added: - name: Log in to GHCR
71added: uses: docker/login-action@v3
72added: with:
73added: registry: ghcr.io
74added: username: ${{ github.actor }}
75added: password: ${{ secrets.GITHUB_TOKEN }}
76added:
77added: - name: Create multi-arch manifest
78added: run: |
79added: docker buildx imagetools create \
80added: --tag "${IMAGE}:${{ github.sha }}" \
81added: --tag "${IMAGE}:latest" \
82added: "${IMAGE}:${{ github.sha }}-amd64" \
83added: "${IMAGE}:${{ github.sha }}-arm64"
84added:
85added: # 3) Image-Tag im Production-Overlay bumpen und nach main committen.
86added: bump:
87added: needs: merge
88added: runs-on: ubuntu-latest
89added: permissions:
90added: contents: write
91added: steps:
92added: - uses: actions/checkout@v4
93added: with:
94added: ref: main
95added:
96added: - name: Install kustomize
97added: uses: imranismail/setup-kustomize@v2
98added:
99added: - name: Bump image tag in production overlay
100added: working-directory: deploy/overlays/production
101added: run: kustomize edit set image "${IMAGE}=${IMAGE}:${{ github.sha }}"
102added:
103added: - name: Commit & push bump
104added: run: |
105added: git config user.name "github-actions[bot]"
106added: git config user.email "github-actions[bot]@users.noreply.github.com"
107added: if git diff --quiet; then
108added: echo "Kein Image-Bump nötig."
109added: exit 0
110added: fi
111added: git add deploy/overlays/production/kustomization.yaml
112added: git commit -m "ci: deploy dorfarchiv ${{ github.sha }} [skip ci]"
113added: git push
AddedDockerfile+18 -0
@@ -0,0 +1,18 @@
1added: # --- Stage 1: Build the static Docusaurus site ---
2added: FROM node:22-alpine AS build
3added: WORKDIR /app
4added:
5added: # Abhängigkeiten zuerst (Layer-Cache)
6added: COPY package.json package-lock.json* ./
7added: RUN npm ci
8added:
9added: # Quellcode kopieren und statisch bauen
10added: COPY . .
11added: RUN npm run build
12added:
13added: # --- Stage 2: Serve build/ via nginx ---
14added: FROM nginx:alpine AS runtime
15added: COPY nginx.conf /etc/nginx/conf.d/default.conf
16added: COPY --from=build /app/build /usr/share/nginx/html
17added: EXPOSE 8080
18added: CMD ["nginx", "-g", "daemon off;"]
Addeddeploy/overlays/production/deployment.yaml+43 -0
@@ -0,0 +1,43 @@
1added: apiVersion: apps/v1
2added: kind: Deployment
3added: metadata:
4added: name: dorfarchiv
5added: namespace: dorfarchiv
6added: labels:
7added: app: dorfarchiv
8added: spec:
9added: replicas: 1
10added: selector:
11added: matchLabels:
12added: app: dorfarchiv
13added: template:
14added: metadata:
15added: labels:
16added: app: dorfarchiv
17added: spec:
18added: containers:
19added: - name: web
20added: # Image-Referenz wird von kustomize (images:) verwaltet — bare ref.
21added: # Öffentliche Seite: nginx lauscht direkt auf 8080 (kein oauth2-proxy).
22added: image: ghcr.io/dorfpflege-rossing/dorfarchiv
23added: ports:
24added: - containerPort: 8080
25added: name: http
26added: readinessProbe:
27added: httpGet:
28added: path: /
29added: port: 8080
30added: initialDelaySeconds: 3
31added: periodSeconds: 10
32added: livenessProbe:
33added: httpGet:
34added: path: /
35added: port: 8080
36added: initialDelaySeconds: 5
37added: periodSeconds: 20
38added: resources:
39added: requests:
40added: cpu: 10m
41added: memory: 24Mi
42added: limits:
43added: memory: 64Mi
Addeddeploy/overlays/production/ingress.yaml+25 -0
@@ -0,0 +1,25 @@
1added: apiVersion: networking.k8s.io/v1
2added: kind: Ingress
3added: metadata:
4added: name: dorfarchiv
5added: namespace: dorfarchiv
6added: annotations:
7added: cert-manager.io/cluster-issuer: letsencrypt
8added: # Öffentliche Seite — kein Zugriffsschutz. Ingress zeigt direkt auf nginx (8080).
9added: spec:
10added: ingressClassName: traefik
11added: tls:
12added: - hosts:
13added: - archiv.xn--rssing-wxa.de
14added: secretName: dorfarchiv-tls
15added: rules:
16added: - host: archiv.xn--rssing-wxa.de
17added: http:
18added: paths:
19added: - path: /
20added: pathType: Prefix
21added: backend:
22added: service:
23added: name: dorfarchiv
24added: port:
25added: number: 8080
Addeddeploy/overlays/production/kustomization.yaml+16 -0
@@ -0,0 +1,16 @@
1added: apiVersion: kustomize.config.k8s.io/v1beta1
2added: kind: Kustomization
3added:
4added: resources:
5added: - namespace.yaml
6added: - deployment.yaml
7added: - service.yaml
8added: - ingress.yaml
9added:
10added: # Der konkrete Image-Tag wird von der CI via
11added: # kustomize edit set image ghcr.io/dorfpflege-rossing/dorfarchiv=...:<git-sha>
12added: # auf den gebauten Multi-Arch-Tag gesetzt und nach main committet.
13added: images:
14added: - name: ghcr.io/dorfpflege-rossing/dorfarchiv
15added: newName: ghcr.io/dorfpflege-rossing/dorfarchiv
16added: newTag: latest
Addeddeploy/overlays/production/namespace.yaml+4 -0
@@ -0,0 +1,4 @@
1added: apiVersion: v1
2added: kind: Namespace
3added: metadata:
4added: name: dorfarchiv
Addeddeploy/overlays/production/service.yaml+14 -0
@@ -0,0 +1,14 @@
1added: apiVersion: v1
2added: kind: Service
3added: metadata:
4added: name: dorfarchiv
5added: namespace: dorfarchiv
6added: labels:
7added: app: dorfarchiv
8added: spec:
9added: selector:
10added: app: dorfarchiv
11added: ports:
12added: - name: http
13added: port: 8080
14added: targetPort: 8080
Addednginx.conf+31 -0
@@ -0,0 +1,31 @@
1added: server {
2added: listen 8080;
3added: listen [::]:8080;
4added: server_name _;
5added:
6added: root /usr/share/nginx/html;
7added: index index.html;
8added:
9added: # Hinter Traefik: relative Redirects, damit Verzeichnis-Slash-Redirects nicht
10added: # den internen Port 8080 / http durchsickern lassen.
11added: absolute_redirect off;
12added: port_in_redirect off;
13added:
14added: # Docusaurus erzeugt /pfad/index.html — sauberes URL-Routing inkl. Slash-Varianten.
15added: location / {
16added: try_files $uri $uri/ $uri.html /index.html;
17added: }
18added:
19added: # Gehashte Assets aggressiv cachen (Docusaurus legt sie unter /assets/ ab)
20added: location /assets/ {
21added: expires 1y;
22added: add_header Cache-Control "public, immutable";
23added: access_log off;
24added: }
25added:
26added: error_page 404 /404.html;
27added:
28added: gzip on;
29added: gzip_types text/plain text/css application/javascript application/json image/svg+xml;
30added: gzip_min_length 1024;
31added: }