Overhaul build pipeline: pnpm, non-root image, Helm chart, CI+release workflows
- db.rs: fix fresh-PVC startup crash by using SqliteConnectOptions with create_if_missing(true) and foreign_keys(true); drops after_connect - Dockerfile: switch to Node 22 + pnpm (corepack), run pnpm check before build, copy backend/demo/ for TT_TEST_MODE support, non-root app user, add HEALTHCHECK, remove baked-in JWT_SECRET - .dockerignore: exclude node_modules, build artifacts, data/, logs - deploy/: new Helm chart replacing k8s/ — Deployment, Service, HTTPRoute (Gateway API), PVC (hcloud-volumes), CronJob backup, ServiceAccount, VPA; JWT_SECRET sourced from pre-provisioned K8s Secret - k8s/: removed (superseded by deploy/) - ci.yml: replaces test.yml — Node 20->22, same test steps, adds no-push Docker build; triggers on non-main pushes and PRs - release.yml: new tag-driven workflow (v*.*.*) — runs tests, pushes image to registry.itsh.dev/s0wlz/tutortool, deploys via helm upgrade https://claude.ai/code/session_01N1kWaQJkz1fC7mUippdQR5
This commit was merged in pull request #1.
This commit is contained in:
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
||||
frontend/node_modules
|
||||
frontend/build
|
||||
frontend/test-results
|
||||
frontend/playwright-report
|
||||
.svelte-kit
|
||||
backend/target
|
||||
data/
|
||||
*.log
|
||||
*.db
|
||||
node_modules
|
||||
@@ -1,8 +1,11 @@
|
||||
name: Test
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches-ignore:
|
||||
- main
|
||||
tags-ignore:
|
||||
- '**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -13,7 +16,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '22'
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -78,3 +81,12 @@ jobs:
|
||||
frontend/test-results/
|
||||
frontend/playwright-report/
|
||||
retention-days: 7
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker build (no push)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: false
|
||||
tags: tutortool:ci
|
||||
95
.gitea/workflows/release.yml
Normal file
95
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,95 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
IMAGE: registry.itsh.dev/s0wlz/tutortool
|
||||
NAMESPACE: tenant-5
|
||||
RELEASE_NAME: tutortool
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.95.0'
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
backend/target
|
||||
key: cargo-${{ hashFiles('backend/Cargo.lock') }}
|
||||
restore-keys: cargo-
|
||||
|
||||
- name: Cache pnpm store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.local/share/pnpm/store
|
||||
key: pnpm-${{ hashFiles('frontend/pnpm-lock.yaml') }}
|
||||
restore-keys: pnpm-
|
||||
|
||||
- name: Install frontend deps
|
||||
run: pnpm --dir frontend install --frozen-lockfile
|
||||
|
||||
- name: Type check (frontend)
|
||||
run: pnpm --dir frontend exec tsgo --version && pnpm --dir frontend check
|
||||
|
||||
- name: Unit tests (backend)
|
||||
run: cargo test --manifest-path backend/Cargo.toml
|
||||
|
||||
- name: Build frontend
|
||||
run: pnpm --dir frontend build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.itsh.dev
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.IMAGE }}:${{ github.ref_name }}
|
||||
${{ env.IMAGE }}:latest
|
||||
|
||||
- name: Configure kubectl
|
||||
run: |
|
||||
mkdir -p ~/.kube
|
||||
echo "${{ secrets.K8S_CONFIG }}" | base64 -d > ~/.kube/config
|
||||
chmod 600 ~/.kube/config
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.16.2
|
||||
|
||||
- name: Deploy via Helm
|
||||
run: |
|
||||
helm upgrade --install ${{ env.RELEASE_NAME }} ./deploy \
|
||||
-f ./deploy/values_override.yaml \
|
||||
--set image.tag=${{ github.ref_name }} \
|
||||
-n ${{ env.NAMESPACE }} \
|
||||
--wait --timeout 5m
|
||||
@@ -85,10 +85,11 @@ See `docs/testing.md` for the full guide including seed data, MCP-driven verific
|
||||
|
||||
### Container / K8s / CI
|
||||
|
||||
- `Dockerfile`: 3-stage build (Node 20 frontend → Rust 1.95 backend → Debian slim runtime)
|
||||
- `k8s/`: Deployment, Service, PVC for SQLite, CronJob for nightly vacuum + backup rotation
|
||||
- Live at `tutor.puchstein.dev` (tenant-5, ITSH Cloud)
|
||||
- CI: Gitea Actions at `.gitea/workflows/test.yml` — runs `cargo check`, `pnpm check`, `cargo test`, `pnpm build`, and `make test-e2e` on every push to `main` and on PRs
|
||||
- `Dockerfile`: 3-stage build (Node 22/pnpm frontend → Rust 1.95 backend → Debian slim runtime, non-root)
|
||||
- `deploy/`: Helm chart — Deployment, Service, HTTPRoute (Gateway API), PVC, CronJob for nightly vacuum + backup rotation
|
||||
- Live at `tutor.puchstein.dev` (tenant-5, ITSH Cloud); image at `registry.itsh.dev/s0wlz/tutortool`
|
||||
- CI: Gitea Actions at `.gitea/workflows/ci.yml` — runs `cargo check`, `pnpm check` (tsgo + svelte-check), `cargo test`, `pnpm build`, `make test-e2e`, and a no-push Docker build on every non-main push and PR
|
||||
- Release: `.gitea/workflows/release.yml` — triggered by `v*.*.*` tags; builds + pushes image, then `helm upgrade`
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
27
Dockerfile
27
Dockerfile
@@ -1,31 +1,36 @@
|
||||
# --- Frontend Build ---
|
||||
FROM node:20-alpine as frontend-builder
|
||||
FROM node:22-alpine AS frontend-builder
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm install --force
|
||||
RUN corepack enable && corepack prepare pnpm --activate
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
RUN pnpm run check
|
||||
RUN pnpm run build
|
||||
|
||||
# --- Backend Build ---
|
||||
FROM rust:1.95-slim as backend-builder
|
||||
FROM rust:1.95-slim AS backend-builder
|
||||
WORKDIR /app/backend
|
||||
# Pre-build dependencies for caching
|
||||
COPY backend/Cargo.toml backend/Cargo.lock ./
|
||||
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
|
||||
COPY backend/src ./src
|
||||
COPY backend/migrations ./migrations
|
||||
# Ensure we actually rebuild the binary with the real source
|
||||
COPY backend/demo ./demo
|
||||
RUN touch src/main.rs && cargo build --release
|
||||
|
||||
# --- Final Image ---
|
||||
# --- Runtime ---
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y sqlite3 ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y ca-certificates curl && rm -rf /var/lib/apt/lists/*
|
||||
RUN useradd -u 1000 -m app
|
||||
WORKDIR /app
|
||||
COPY --from=backend-builder /app/backend/target/release/attendance ./server
|
||||
COPY --from=frontend-builder /app/frontend/build ./frontend/build
|
||||
COPY --from=backend-builder /app/backend/demo ./backend/demo
|
||||
COPY --from=frontend-builder /app/frontend/build ./frontend/build
|
||||
|
||||
ENV STATIC_DIR=/app/frontend/build
|
||||
ENV DATABASE_URL=sqlite:/data/attendance.db
|
||||
ENV JWT_SECRET=change-me-at-runtime
|
||||
EXPOSE 3000
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
|
||||
CMD curl -fs http://localhost:3000/health || exit 1
|
||||
USER app
|
||||
CMD ["./server"]
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
|
||||
use std::str::FromStr;
|
||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
pub async fn init() -> Result<SqlitePool, sqlx::Error> {
|
||||
let url = std::env::var("DATABASE_URL")
|
||||
.unwrap_or_else(|_| "sqlite:/data/attendance.db".into());
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.after_connect(|conn, _| Box::pin(async move {
|
||||
sqlx::query("PRAGMA foreign_keys = ON")
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
}))
|
||||
.connect(&url).await?;
|
||||
let opts = SqliteConnectOptions::from_str(&url)?
|
||||
.create_if_missing(true)
|
||||
.foreign_keys(true);
|
||||
let pool = SqlitePoolOptions::new().connect_with(opts).await?;
|
||||
sqlx::migrate!("./migrations").run(&pool).await?;
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
6
deploy/Chart.yaml
Normal file
6
deploy/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: tutortool
|
||||
description: TutorTool attendance tracker (Rust + SvelteKit + SQLite)
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
17
deploy/templates/NOTES.txt
Normal file
17
deploy/templates/NOTES.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
TutorTool has been deployed!
|
||||
|
||||
Application URL: https://{{ index .Values.httpRoute.hostnames 0 }}
|
||||
|
||||
To verify the deployment:
|
||||
kubectl -n {{ .Release.Namespace }} rollout status deploy/{{ include "tutortool.fullname" . }}
|
||||
|
||||
Health check:
|
||||
kubectl -n {{ .Release.Namespace }} exec deploy/{{ include "tutortool.fullname" . }} -- curl -s http://localhost:3000/health
|
||||
|
||||
The JWT_SECRET is read from the K8s Secret named "{{ .Values.jwtSecretName }}".
|
||||
Provision it before deploying if it doesn't already exist:
|
||||
kubectl -n {{ .Release.Namespace }} create secret generic {{ .Values.jwtSecretName }} \
|
||||
--from-literal=JWT_SECRET=<your-secret>
|
||||
|
||||
SQLite data persists on PVC: {{ include "tutortool.fullname" . }}-data
|
||||
Nightly VACUUM backups run at 03:00 UTC, retained for 7 days.
|
||||
50
deploy/templates/_helpers.tpl
Normal file
50
deploy/templates/_helpers.tpl
Normal file
@@ -0,0 +1,50 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "tutortool.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "tutortool.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "tutortool.labels" -}}
|
||||
helm.sh/chart: {{ include "tutortool.name" . }}-{{ .Chart.Version }}
|
||||
{{ include "tutortool.selectorLabels" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "tutortool.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "tutortool.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Service account name
|
||||
*/}}
|
||||
{{- define "tutortool.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "tutortool.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
31
deploy/templates/cronjob-backup.yaml
Normal file
31
deploy/templates/cronjob-backup.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}-backup
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
schedule: "0 3 * * *"
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine:latest
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
apk add --no-cache sqlite
|
||||
sqlite3 /data/attendance.db "VACUUM INTO '/data/backup-$(date +%F).sqlite'"
|
||||
find /data -name "backup-*.sqlite" -mtime +7 -delete
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "tutortool.fullname" . }}-data
|
||||
60
deploy/templates/deployment.yaml
Normal file
60
deploy/templates/deployment.yaml
Normal file
@@ -0,0 +1,60 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "tutortool.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "tutortool.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
serviceAccountName: {{ include "tutortool.serviceAccountName" . }}
|
||||
securityContext:
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: app
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.containerPort }}
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
value: {{ .Values.env.DATABASE_URL | quote }}
|
||||
- name: STATIC_DIR
|
||||
value: {{ .Values.env.STATIC_DIR | quote }}
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.jwtSecretName }}
|
||||
key: JWT_SECRET
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: {{ .Values.containerPort }}
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: {{ .Values.containerPort }}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: false
|
||||
allowPrivilegeEscalation: false
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "tutortool.fullname" . }}-data
|
||||
45
deploy/templates/httproute.yaml
Normal file
45
deploy/templates/httproute.yaml
Normal file
@@ -0,0 +1,45 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: itsh-gateway
|
||||
sectionName: {{ .Values.httpRoute.sectionName }}
|
||||
hostnames:
|
||||
{{- range .Values.httpRoute.hostnames }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: {{ include "tutortool.fullname" . }}
|
||||
port: {{ .Values.service.port }}
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}-http-redirect
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: itsh-gateway
|
||||
sectionName: http-tutor-puchstein-dev
|
||||
hostnames:
|
||||
{{- range .Values.httpRoute.hostnames }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- filters:
|
||||
- type: RequestRedirect
|
||||
requestRedirect:
|
||||
scheme: https
|
||||
statusCode: 301
|
||||
14
deploy/templates/pvc.yaml
Normal file
14
deploy/templates/pvc.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}-data
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: {{ .Values.pvc.storageClassName }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.pvc.storage }}
|
||||
13
deploy/templates/service.yaml
Normal file
13
deploy/templates/service.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "tutortool.selectorLabels" . | nindent 4 }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
9
deploy/templates/serviceaccount.yaml
Normal file
9
deploy/templates/serviceaccount.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "tutortool.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
{{- end }}
|
||||
29
deploy/templates/vpa.yaml
Normal file
29
deploy/templates/vpa.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
{{- if .Values.vpa.enabled }}
|
||||
apiVersion: autoscaling.k8s.io/v1
|
||||
kind: VerticalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "tutortool.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "tutortool.labels" . | nindent 4 }}
|
||||
spec:
|
||||
targetRef:
|
||||
apiVersion: "apps/v1"
|
||||
kind: Deployment
|
||||
name: {{ include "tutortool.fullname" . }}
|
||||
updatePolicy:
|
||||
updateMode: {{ .Values.vpa.updateMode | default "Off" | quote }}
|
||||
resourcePolicy:
|
||||
containerPolicies:
|
||||
{{- range .Values.vpa.containerPolicies }}
|
||||
- containerName: {{ .containerName }}
|
||||
minAllowed:
|
||||
{{- toYaml .minAllowed | nindent 10 }}
|
||||
maxAllowed:
|
||||
{{- toYaml .maxAllowed | nindent 10 }}
|
||||
controlledResources:
|
||||
- cpu
|
||||
- memory
|
||||
controlledValues: RequestsAndLimits
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
53
deploy/values.yaml
Normal file
53
deploy/values.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: registry.itsh.dev/s0wlz/tutortool
|
||||
pullPolicy: IfNotPresent
|
||||
tag: latest
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: ""
|
||||
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 3000
|
||||
|
||||
containerPort: 3000
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
pvc:
|
||||
storageClassName: hcloud-volumes
|
||||
storage: 1Gi
|
||||
|
||||
httpRoute:
|
||||
hostnames:
|
||||
- tutor.puchstein.dev
|
||||
sectionName: https-tutor-puchstein-dev
|
||||
|
||||
# JWT_SECRET provisioned as a pre-existing K8s Secret named here.
|
||||
# Do not set jwtSecretValue in committed values — provision via kubectl manually.
|
||||
jwtSecretName: tutortool-jwt
|
||||
|
||||
env:
|
||||
DATABASE_URL: sqlite:/data/attendance.db
|
||||
STATIC_DIR: /app/frontend/build
|
||||
|
||||
vpa:
|
||||
enabled: false
|
||||
updateMode: "Off"
|
||||
containerPolicies:
|
||||
- containerName: app
|
||||
minAllowed:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
maxAllowed:
|
||||
cpu: 1000m
|
||||
memory: 512Mi
|
||||
6
deploy/values_override.yaml
Normal file
6
deploy/values_override.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
httpRoute:
|
||||
hostnames:
|
||||
- tutor.puchstein.dev
|
||||
|
||||
image:
|
||||
tag: latest
|
||||
@@ -1,28 +0,0 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: tutortool-backup
|
||||
spec:
|
||||
schedule: "0 3 * * *"
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine:latest
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
apk add --no-cache sqlite
|
||||
sqlite3 /data/attendance.db "VACUUM INTO '/data/backup-$(date +%F).sqlite'"
|
||||
find /data -name "backup-*.sqlite" -mtime +7 -delete
|
||||
volumeMounts:
|
||||
- name: db-storage
|
||||
mountPath: /data
|
||||
restartPolicy: OnFailure
|
||||
volumes:
|
||||
- name: db-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: tutortool-pvc
|
||||
@@ -1,59 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: tutortool
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: tutortool
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: tutortool
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
image: tutor-registry:latest
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
value: "sqlite:/data/attendance.db"
|
||||
- name: STATIC_DIR
|
||||
value: "/app/frontend/build"
|
||||
volumeMounts:
|
||||
- name: db-storage
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: db-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: tutortool-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tutortool
|
||||
spec:
|
||||
selector:
|
||||
app: tutortool
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 3000
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: tutortool-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: tutor.puchstein.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: tutortool
|
||||
port:
|
||||
number: 80
|
||||
@@ -1,25 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: tutortool
|
||||
namespace: tutortool
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- tutor.puchstein.dev
|
||||
secretName: tutortool-tls
|
||||
rules:
|
||||
- host: tutor.puchstein.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: tutortool
|
||||
port:
|
||||
number: 80
|
||||
10
k8s/pvc.yaml
10
k8s/pvc.yaml
@@ -1,10 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: tutortool-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tutortool
|
||||
namespace: tutortool
|
||||
spec:
|
||||
selector:
|
||||
app: tutortool
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 3000
|
||||
type: ClusterIP
|
||||
Reference in New Issue
Block a user