Add Naviya production GitOps deployment

This commit is contained in:
2026-08-08 00:30:25 +02:00
parent 1cf87c2470
commit aa9b8da16d
19 changed files with 945 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: application-metier
labels:
app.kubernetes.io/part-of: application-metier
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: application-metier
imagePullSecrets:
- name: application-metier-registry
+21
View File
@@ -0,0 +1,21 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: application-metier-config
namespace: application-metier
data:
APP_ENV: production
LOG_LEVEL: info
HTTP_ADDRESS: ":8080"
REDIS_ADDRESS: application-metier-redis:6379
REDIS_DATABASE: "0"
JWT_ISSUER: application-metier
JWT_AUDIENCE: application-metier-api
JWT_ACCESS_TTL: 15m
JWT_REFRESH_TTL: 720h
OPEN_METEO_FORECAST_URL: https://api.open-meteo.com/v1/forecast
OPEN_METEO_ARCHIVE_URL: https://archive-api.open-meteo.com/v1/archive
OPEN_METEO_MODEL: best_match
OPEN_METEO_REANALYSIS_DATASET: best_match
METEO_FRANCE_STATION_LIST_URL: https://public-api.meteofrance.fr/public/DPObs/liste-stations
METEO_FRANCE_OBSERVATION_URL: https://public-api.meteofrance.fr/public/DPObs/v2/station/horaire
+216
View File
@@ -0,0 +1,216 @@
apiVersion: v1
kind: Service
metadata:
name: application-metier-postgres
namespace: application-metier
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
spec:
ports:
- name: postgres
port: 5432
targetPort: postgres
selector:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: application-metier-postgres
namespace: application-metier
spec:
serviceName: application-metier-postgres
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
template:
metadata:
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
app.kubernetes.io/part-of: application-metier
spec:
terminationGracePeriodSeconds: 60
securityContext:
runAsNonRoot: true
runAsUser: 70
runAsGroup: 70
seccompProfile:
type: RuntimeDefault
fsGroup: 70
containers:
- name: postgres
image: postgres:17.10-alpine
imagePullPolicy: IfNotPresent
ports:
- name: postgres
containerPort: 5432
env:
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: application-metier-secrets
key: POSTGRES_DB
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: application-metier-secrets
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: application-metier-secrets
key: POSTGRES_PASSWORD
- name: POSTGRES_INITDB_ARGS
value: --auth-host=scram-sha-256
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
readinessProbe:
exec:
command:
- sh
- -ec
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 12
livenessProbe:
exec:
command:
- sh
- -ec
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: Service
metadata:
name: application-metier-redis
namespace: application-metier
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: redis
spec:
ports:
- name: redis
port: 6379
targetPort: redis
selector:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: application-metier-redis
namespace: application-metier
spec:
serviceName: application-metier-redis
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: redis
template:
metadata:
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: redis
app.kubernetes.io/part-of: application-metier
spec:
terminationGracePeriodSeconds: 30
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: redis
image: redis:7.4.9-alpine
imagePullPolicy: IfNotPresent
command: ["sh", "-ec"]
args:
- exec redis-server --appendonly yes --requirepass "$REDIS_PASSWORD"
ports:
- name: redis
containerPort: 6379
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: application-metier-secrets
key: REDIS_PASSWORD
readinessProbe:
exec:
command:
- sh
- -ec
- redis-cli -a "$REDIS_PASSWORD" ping | grep -q PONG
initialDelaySeconds: 3
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 12
livenessProbe:
exec:
command:
- sh
- -ec
- redis-cli -a "$REDIS_PASSWORD" ping | grep -q PONG
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 250m
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
+74
View File
@@ -0,0 +1,74 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: application-metier-postgres-backups
namespace: application-metier
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: application-metier-postgres-backup
namespace: application-metier
spec:
schedule: "17 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: backup
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 70
runAsGroup: 70
seccompProfile:
type: RuntimeDefault
fsGroup: 70
containers:
- name: pg-dump
image: postgres:17.10-alpine
imagePullPolicy: IfNotPresent
command: ["sh", "-ec"]
args:
- |
umask 077
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
pg_dump --format=custom --no-owner --no-acl "$DATABASE_URL" > "/backups/application-metier-${stamp}.dump"
find /backups -type f -name 'application-metier-*.dump' -mtime +14 -delete
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: application-metier-secrets
key: DATABASE_URL
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 500m
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: backups
mountPath: /backups
volumes:
- name: backups
persistentVolumeClaim:
claimName: application-metier-postgres-backups
+53
View File
@@ -0,0 +1,53 @@
apiVersion: batch/v1
kind: Job
metadata:
name: application-metier-migrate
namespace: application-metier
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: migration
spec:
backoffLimit: 2
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: migration
spec:
restartPolicy: Never
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: goose
image: git.guillin.ovh/baptiste/application-metier/api@sha256:6e0707e2deb796718f432f111cfa96afb60c890ec74c7dc5ab4e7c0ed7904991
imagePullPolicy: IfNotPresent
command: ["/app/goose"]
args: ["up"]
env:
- name: GOOSE_DRIVER
value: postgres
- name: GOOSE_MIGRATION_DIR
value: /app/migrations
- name: GOOSE_DBSTRING
valueFrom:
secretKeyRef:
name: application-metier-secrets
key: DATABASE_URL
- name: NO_COLOR
value: "1"
resources:
requests:
cpu: 25m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
+110
View File
@@ -0,0 +1,110 @@
apiVersion: v1
kind: Service
metadata:
name: application-metier-api
namespace: application-metier
spec:
ports:
- name: http
port: 8080
targetPort: http
selector:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: application-metier-api
namespace: application-metier
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
template:
metadata:
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
app.kubernetes.io/part-of: application-metier
spec:
automountServiceAccountToken: false
terminationGracePeriodSeconds: 30
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
containers:
- name: api
image: git.guillin.ovh/baptiste/application-metier/api@sha256:6e0707e2deb796718f432f111cfa96afb60c890ec74c7dc5ab4e7c0ed7904991
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
envFrom:
- configMapRef:
name: application-metier-config
- secretRef:
name: application-metier-secrets
startupProbe:
httpGet:
path: /livez
port: http
periodSeconds: 2
failureThreshold: 30
livenessProbe:
httpGet:
path: /livez
port: http
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: application-metier-api
namespace: application-metier
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
+121
View File
@@ -0,0 +1,121 @@
apiVersion: v1
kind: Service
metadata:
name: application-metier-frontend
namespace: application-metier
spec:
ports:
- name: http
port: 8080
targetPort: http
selector:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: frontend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: application-metier-frontend
namespace: application-metier
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: frontend
template:
metadata:
labels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: frontend
app.kubernetes.io/part-of: application-metier
spec:
automountServiceAccountToken: false
terminationGracePeriodSeconds: 20
securityContext:
runAsNonRoot: true
runAsUser: 101
runAsGroup: 101
fsGroup: 101
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: frontend
containers:
- name: frontend
image: git.guillin.ovh/baptiste/application-metier/frontend@sha256:b308c5632f385059124d0e5416150e8298eccefea160179423f453c5c20c2a49
imagePullPolicy: IfNotPresent
command: ["nginx"]
args: ["-g", "daemon off;"]
ports:
- name: http
containerPort: 8080
startupProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 2
failureThreshold: 30
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
cpu: 25m
memory: 32Mi
limits:
cpu: 150m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: nginx-cache
mountPath: /var/cache/nginx
- name: tmp
mountPath: /tmp
volumes:
- name: nginx-cache
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: application-metier-frontend
namespace: application-metier
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: frontend
+161
View File
@@ -0,0 +1,161 @@
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: application-metier-frontend
namespace: application-metier
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: frontend
ingress:
- fromEntities:
- ingress
- host
toPorts:
- ports:
- port: "8080"
protocol: TCP
egress:
- toEndpoints:
- matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
toPorts:
- ports:
- port: "8080"
protocol: TCP
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
- port: "53"
protocol: TCP
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: application-metier-api
namespace: application-metier
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
ingress:
- fromEntities:
- cluster
- host
toPorts:
- ports:
- port: "8080"
protocol: TCP
egress:
- toEndpoints:
- matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
toPorts:
- ports:
- port: "5432"
protocol: TCP
- toEndpoints:
- matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: redis
toPorts:
- ports:
- port: "6379"
protocol: TCP
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
- port: "53"
protocol: TCP
- toEntities:
- world
toPorts:
- ports:
- port: "443"
protocol: TCP
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: application-metier-postgres
namespace: application-metier
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
ingress:
- fromEndpoints:
- matchExpressions:
- key: app.kubernetes.io/component
operator: In
values: [api, migration, bootstrap, backup, restore]
toPorts:
- ports:
- port: "5432"
protocol: TCP
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: application-metier-redis
namespace: application-metier
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: redis
ingress:
- fromEndpoints:
- matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: api
toPorts:
- ports:
- port: "6379"
protocol: TCP
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: application-metier-jobs
namespace: application-metier
spec:
endpointSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values: [migration, bootstrap, backup, restore]
egress:
- toEndpoints:
- matchLabels:
app.kubernetes.io/name: application-metier
app.kubernetes.io/component: postgres
toPorts:
- ports:
- port: "5432"
protocol: TCP
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
- port: "53"
protocol: TCP
+89
View File
@@ -0,0 +1,89 @@
apiVersion: cilium.io/v2alpha1
kind: CiliumL2AnnouncementPolicy
metadata:
name: application-metier-gateway
spec:
serviceSelector:
matchExpressions:
- key: io.kubernetes.service.namespace
operator: In
values: ["application-metier"]
- key: io.kubernetes.service.name
operator: In
values: ["cilium-gateway-application-metier"]
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
loadBalancerIPs: true
externalIPs: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: application-metier
namespace: application-metier
spec:
gatewayClassName: cilium
infrastructure:
annotations:
metallb.io/address-pool: bgp-pool
metallb.io/loadBalancerIPs: "172.16.10.201"
listeners:
- name: http
protocol: HTTP
port: 80
hostname: naviya.guillin.ovh
allowedRoutes:
namespaces:
from: Same
- name: https
protocol: HTTPS
port: 443
hostname: naviya.guillin.ovh
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: application-metier-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: application-metier-http-redirect
namespace: application-metier
spec:
parentRefs:
- name: application-metier
sectionName: http
hostnames:
- naviya.guillin.ovh
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: application-metier
namespace: application-metier
spec:
parentRefs:
- name: application-metier
sectionName: https
hostnames:
- naviya.guillin.ovh
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: application-metier-frontend
port: 8080
+22
View File
@@ -0,0 +1,22 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- 00-namespace.yaml
- 05-serviceaccount.yaml
- 10-configmap.yaml
- 20-data.yaml
- 30-backup.yaml
- 40-migrate.yaml
- 50-api.yaml
- 60-frontend.yaml
- 70-policies.yaml
- 80-gateway.yaml
patches:
- path: patch-migrate.yaml
- path: patch-api.yaml
- path: patch-frontend.yaml
- path: patch-gateway.yaml
- path: patch-route.yaml
- path: patch-route-redirect.yaml
+7
View File
@@ -0,0 +1,7 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: application-metier-api
namespace: application-metier
annotations:
argocd.argoproj.io/sync-wave: "2"
+7
View File
@@ -0,0 +1,7 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: application-metier-frontend
namespace: application-metier
annotations:
argocd.argoproj.io/sync-wave: "2"
+7
View File
@@ -0,0 +1,7 @@
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: application-metier
namespace: application-metier
annotations:
argocd.argoproj.io/sync-wave: "3"
+9
View File
@@ -0,0 +1,9 @@
apiVersion: batch/v1
kind: Job
metadata:
name: application-metier-migrate
namespace: application-metier
annotations:
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
argocd.argoproj.io/sync-wave: "1"
+7
View File
@@ -0,0 +1,7 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: application-metier-http-redirect
namespace: application-metier
annotations:
argocd.argoproj.io/sync-wave: "3"
+7
View File
@@ -0,0 +1,7 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: application-metier
namespace: application-metier
annotations:
argocd.argoproj.io/sync-wave: "3"
+1
View File
@@ -7,3 +7,4 @@ resources:
- cert-manager-config.yaml
- web-test.yaml
- application.yaml
- naviya.yaml
+20
View File
@@ -0,0 +1,20 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: naviya
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: homelab
source:
repoURL: gitea@172.16.2.52:baptiste/k8s.git
targetRevision: main
path: apps/naviya
destination:
server: https://kubernetes.default.svc
namespace: application-metier
syncPolicy:
syncOptions:
- CreateNamespace=true
- PruneLast=true