deploy shared SQLite admin application
Build & Deploy Formulation / Build & Push Image (push) Successful in 34s
Build & Deploy Formulation / deploy (push) Successful in 47s

This commit is contained in:
nicholas committed 2026-09-01 12:01:40 -05:00
1 parent 76b7cf1f80
commit dab4a71c72
7 files changed
+127 -7

No files matched your search

+1
View File
@@ -17,6 +17,7 @@ COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build --chown=node:node /app/dist/app ./dist/app
COPY --from=build --chown=node:node /app/var/recipe-book.sqlite ./var/recipe-book.sqlite
COPY --from=build --chown=node:node /app/var/recipe-book.sqlite ./seed/recipe-book.sqlite
COPY --from=build --chown=node:node /app/public ./public
USER node
EXPOSE 8080
+21
View File
@@ -0,0 +1,21 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: recipe-book-admin
labels:
app.kubernetes.io/name: recipe-book
spec:
# K3s Traefik is reachable through the internal apps host. The edge Traefik
# route and split-horizon DNS are managed separately in home-ops.
ingressClassName: traefik
rules:
- host: recipes-admin.uuard.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: recipe-book-admin
port:
name: http
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: recipe-book-admin
labels:
app.kubernetes.io/name: recipe-book
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: recipe-book
ports:
- name: http
port: 80
targetPort: admin-http
protocol: TCP
+12
View File
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: recipe-book-database
labels:
app.kubernetes.io/name: recipe-book
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
+72 -5
View File
@@ -5,14 +5,13 @@ metadata:
labels:
app.kubernetes.io/name: recipe-book
spec:
replicas: 2
# The public viewer and internal editor deliberately share one local SQLite
# volume, so they run as sidecars in one pod.
replicas: 1
revisionHistoryLimit: 5
progressDeadlineSeconds: 180
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: recipe-book
@@ -21,8 +20,24 @@ spec:
labels:
app.kubernetes.io/name: recipe-book
spec:
securityContext:
fsGroup: 1000
imagePullSecrets:
- name: gitea-registry
initContainers:
- name: initialize-database
image: git.uuard.com/nicholas/formulation
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
if [ ! -s /data/recipe-book.sqlite ]; then
cp /app/seed/recipe-book.sqlite /data/recipe-book.sqlite
fi
volumeMounts:
- name: database
mountPath: /data
containers:
- name: recipe-book
image: git.uuard.com/nicholas/formulation
@@ -58,3 +73,55 @@ spec:
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: database
mountPath: /app/var
readOnly: true
- name: recipe-book-admin
image: git.uuard.com/nicholas/formulation
imagePullPolicy: IfNotPresent
env:
- name: FORMULATION_READ_ONLY
value: "false"
- name: HOST
value: 0.0.0.0
- name: PORT
value: "8081"
ports:
- name: admin-http
containerPort: 8081
protocol: TCP
readinessProbe:
httpGet:
path: /app/
port: admin-http
initialDelaySeconds: 2
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet:
path: /app/
port: admin-http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
cpu: 200m
memory: 384Mi
securityContext:
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: database
mountPath: /app/var
volumes:
- name: database
persistentVolumeClaim:
claimName: recipe-book-database
+3
View File
@@ -2,9 +2,12 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: nicholas
resources:
- database-pvc.yaml
- deployment.yaml
- service.yaml
- admin-service.yaml
- ingress.yaml
- admin-ingress.yaml
images:
- name: git.uuard.com/nicholas/formulation
newTag: "9591742"
+3 -2
View File
@@ -11,7 +11,9 @@ export const databaseProjection = (database: DatabaseSync) => createSiteProjecti
export function openDatabase(options: { readOnly?: boolean } = {}) {
if (!fs.existsSync(databasePath)) return undefined;
const database = new DatabaseSync(databasePath, { readOnly: readOnlyMode || (options.readOnly ?? true) });
const readOnly = readOnlyMode || (options.readOnly ?? true);
const database = new DatabaseSync(databasePath, { readOnly });
database.exec("PRAGMA busy_timeout = 5000");
database.exec("PRAGMA foreign_keys = ON");
return database;
}
@@ -284,4 +286,3 @@ export function permanentlyDeleteArchivedItems(
}
refreshSiteProjection(database);
}