services: add minio, outline, planka

This commit is contained in:
Brad Stein 2026-01-12 21:22:54 -03:00
parent 5f4d9b498e
commit 29192b9e7f
25 changed files with 666 additions and 0 deletions

View File

@ -25,3 +25,6 @@ resources:
- ai-llm/kustomization.yaml
- nextcloud/kustomization.yaml
- nextcloud-mail-sync/kustomization.yaml
- minio/kustomization.yaml
- outline/kustomization.yaml
- planka/kustomization.yaml

View File

@ -0,0 +1,24 @@
# clusters/atlas/flux-system/applications/minio/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: minio
namespace: flux-system
spec:
interval: 10m
path: ./services/minio
prune: true
sourceRef:
kind: GitRepository
name: flux-system
targetNamespace: minio
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: minio
namespace: minio
- apiVersion: v1
kind: Service
name: minio
namespace: minio
wait: false

View File

@ -0,0 +1,29 @@
# clusters/atlas/flux-system/applications/outline/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: outline
namespace: flux-system
spec:
interval: 10m
path: ./services/outline
prune: true
sourceRef:
kind: GitRepository
name: flux-system
targetNamespace: outline
dependsOn:
- name: keycloak
- name: mailu
- name: minio
- name: traefik
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: outline
namespace: outline
- apiVersion: v1
kind: Service
name: outline
namespace: outline
wait: false

View File

@ -0,0 +1,28 @@
# clusters/atlas/flux-system/applications/planka/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: planka
namespace: flux-system
spec:
interval: 10m
path: ./services/planka
prune: true
sourceRef:
kind: GitRepository
name: flux-system
targetNamespace: planka
dependsOn:
- name: keycloak
- name: mailu
- name: traefik
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: planka
namespace: planka
- apiVersion: v1
kind: Service
name: planka
namespace: planka
wait: false

View File

@ -0,0 +1,50 @@
# services/minio/bucket-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: minio-bucket-bootstrap-1
namespace: minio
spec:
backoffLimit: 1
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
containers:
- name: mc
image: minio/mc:RELEASE.2025-08-13T08-35-41Z
command: ["/bin/sh", "-c"]
args:
- |
set -euo pipefail
mc alias set local http://minio.minio.svc.cluster.local:9000 "${MINIO_ROOT_USER}" "${MINIO_ROOT_PASSWORD}"
mc mb -p local/outline || true
mc mb -p local/planka || true
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootUser
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootPassword
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi

View File

@ -0,0 +1,68 @@
# services/minio/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
namespace: minio
labels:
app: minio
spec:
replicas: 1
selector:
matchLabels:
app: minio
strategy:
type: Recreate
template:
metadata:
labels:
app: minio
spec:
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
containers:
- name: minio
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
args:
- server
- /data
- --console-address
- ":9001"
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootUser
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootPassword
ports:
- name: api
containerPort: 9000
- name: console
containerPort: 9001
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumes:
- name: data
persistentVolumeClaim:
claimName: minio-data

View File

@ -0,0 +1,10 @@
# services/minio/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: minio
resources:
- namespace.yaml
- pvc.yaml
- deployment.yaml
- bucket-job.yaml
- service.yaml

View File

@ -0,0 +1,5 @@
# services/minio/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: minio

12
services/minio/pvc.yaml Normal file
View File

@ -0,0 +1,12 @@
# services/minio/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data
namespace: minio
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: asteria
resources:
requests:
storage: 100Gi

View File

@ -0,0 +1,18 @@
# services/minio/service.yaml
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: minio
labels:
app: minio
spec:
selector:
app: minio
ports:
- name: api
port: 9000
targetPort: api
- name: console
port: 9001
targetPort: console

View File

@ -0,0 +1,12 @@
# services/outline/app-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: outline-app
namespace: outline
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: astreae
resources:
requests:
storage: 5Gi

View File

@ -0,0 +1,111 @@
# services/outline/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: outline
namespace: outline
labels:
app: outline
spec:
replicas: 1
selector:
matchLabels:
app: outline
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
template:
metadata:
labels:
app: outline
spec:
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
containers:
- name: outline
image: outlinewiki/outline:1.2.0
ports:
- name: http
containerPort: 3000
env:
- name: NODE_ENV
value: production
- name: URL
value: https://notes.bstein.dev
- name: PORT
value: "3000"
- name: REDIS_URL
value: redis://outline-redis:6379
- name: FILE_STORAGE
value: s3
- name: AWS_REGION
value: us-east-1
- name: AWS_S3_FORCE_PATH_STYLE
value: "true"
- name: AWS_S3_ACL
value: private
- name: FORCE_HTTPS
value: "true"
- name: OIDC_ENFORCED
value: "true"
- name: OIDC_SCOPES
value: openid profile email
- name: OIDC_USERNAME_CLAIM
value: preferred_username
- name: OIDC_DISPLAY_NAME
value: Atlas SSO
- name: SMTP_SECURE
value: "false"
- name: SMTP_PORT
value: "25"
envFrom:
- secretRef:
name: outline-db
- secretRef:
name: outline-secrets
- secretRef:
name: outline-oidc
- secretRef:
name: outline-s3
- secretRef:
name: outline-smtp
volumeMounts:
- name: app-data
mountPath: /var/lib/outline
readinessProbe:
httpGet:
path: /_health
port: http
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
livenessProbe:
httpGet:
path: /_health
port: http
initialDelaySeconds: 30
periodSeconds: 20
timeoutSeconds: 3
failureThreshold: 6
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumes:
- name: app-data
persistentVolumeClaim:
claimName: outline-app

View File

@ -0,0 +1,26 @@
# services/outline/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: outline
namespace: outline
annotations:
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts: ["notes.bstein.dev"]
secretName: outline-tls
rules:
- host: notes.bstein.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: outline
port:
number: 80

View File

@ -0,0 +1,12 @@
# services/outline/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: outline
resources:
- namespace.yaml
- app-pvc.yaml
- redis-deployment.yaml
- redis-service.yaml
- deployment.yaml
- service.yaml
- ingress.yaml

View File

@ -0,0 +1,5 @@
# services/outline/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: outline

View File

@ -0,0 +1,47 @@
# services/outline/redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: outline-redis
namespace: outline
labels:
app: outline-redis
spec:
replicas: 1
selector:
matchLabels:
app: outline-redis
template:
metadata:
labels:
app: outline-redis
spec:
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
containers:
- name: redis
image: redis:7.4.1-alpine
ports:
- name: redis
containerPort: 6379
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}

View File

@ -0,0 +1,15 @@
# services/outline/redis-service.yaml
apiVersion: v1
kind: Service
metadata:
name: outline-redis
namespace: outline
labels:
app: outline-redis
spec:
selector:
app: outline-redis
ports:
- name: redis
port: 6379
targetPort: redis

View File

@ -0,0 +1,15 @@
# services/outline/service.yaml
apiVersion: v1
kind: Service
metadata:
name: outline
namespace: outline
labels:
app: outline
spec:
selector:
app: outline
ports:
- name: http
port: 80
targetPort: http

View File

@ -0,0 +1,12 @@
# services/planka/app-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: planka-app-data
namespace: planka
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: astreae
resources:
requests:
storage: 2Gi

View File

@ -0,0 +1,95 @@
# services/planka/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: planka
namespace: planka
labels:
app: planka
spec:
replicas: 1
selector:
matchLabels:
app: planka
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
template:
metadata:
labels:
app: planka
spec:
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: planka
image: ghcr.io/plankanban/planka:2.0.0-rc.4
ports:
- name: http
containerPort: 1337
env:
- name: BASE_URL
value: https://tasks.bstein.dev
- name: TRUST_PROXY
value: "true"
envFrom:
- secretRef:
name: planka-db
- secretRef:
name: planka-secrets
- secretRef:
name: planka-oidc
- secretRef:
name: planka-smtp
volumeMounts:
- name: user-data
mountPath: /app/public
- name: user-data
mountPath: /app/private
- name: app-data
mountPath: /app/.tmp
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30
periodSeconds: 20
timeoutSeconds: 3
failureThreshold: 6
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumes:
- name: user-data
persistentVolumeClaim:
claimName: planka-user-data
- name: app-data
persistentVolumeClaim:
claimName: planka-app-data

View File

@ -0,0 +1,26 @@
# services/planka/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: planka
namespace: planka
annotations:
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts: ["tasks.bstein.dev"]
secretName: planka-tls
rules:
- host: tasks.bstein.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: planka
port:
number: 80

View File

@ -0,0 +1,11 @@
# services/planka/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: planka
resources:
- namespace.yaml
- user-data-pvc.yaml
- app-pvc.yaml
- deployment.yaml
- service.yaml
- ingress.yaml

View File

@ -0,0 +1,5 @@
# services/planka/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: planka

View File

@ -0,0 +1,15 @@
# services/planka/service.yaml
apiVersion: v1
kind: Service
metadata:
name: planka
namespace: planka
labels:
app: planka
spec:
selector:
app: planka
ports:
- name: http
port: 80
targetPort: http

View File

@ -0,0 +1,12 @@
# services/planka/user-data-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: planka-user-data
namespace: planka
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: asteria
resources:
requests:
storage: 20Gi