refactor(gitops): move cluster roots to apps and split db chain

This commit is contained in:
Haitao Pan 2026-04-03 15:43:06 +08:00
parent dfa55bc313
commit 20e3be0a99
51 changed files with 414 additions and 284 deletions

View File

@ -12,4 +12,4 @@ spec:
name: platform-config
path: ./apps/core/accounts/pre
dependsOn:
- name: database-stack
- name: stunnel-client-pre

View File

@ -1,6 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- stunnel-client-pre-kustomization.yaml
- console-pre-kustomization.yaml
- accounts-pre-kustomization.yaml

View File

@ -0,0 +1,16 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: stunnel-client-pre
namespace: flux-system
spec:
interval: 5m0s
prune: true
wait: true
timeout: 10m0s
sourceRef:
kind: GitRepository
name: platform-config
path: ./apps/core/stunnel-client/pre
dependsOn:
- name: stunnel-server

View File

@ -12,4 +12,4 @@ spec:
name: platform-config
path: ./apps/core/accounts/prod
dependsOn:
- name: database-stack
- name: stunnel-client-prod

View File

@ -2,10 +2,12 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespaces.yaml
- database-kustomization.yaml
- observability-kustomization.yaml
- postgresql-prod-kustomization.yaml
- stunnel-server-kustomization.yaml
- stunnel-client-prod-kustomization.yaml
- console-prod-kustomization.yaml
- accounts-prod-kustomization.yaml
- observability-kustomization.yaml
- pre-kustomization.yaml
# Sync marker for Flux reconciliation on jp-k3s-vultr.svc.plus.

View File

@ -27,4 +27,3 @@ apiVersion: v1
kind: Namespace
metadata:
name: core-pre

View File

@ -1,7 +1,7 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: database-stack
name: postgresql-prod
namespace: flux-system
spec:
interval: 5m0s
@ -11,4 +11,4 @@ spec:
sourceRef:
kind: GitRepository
name: platform-config
path: ./databases/postgresql
path: ./databases/postgresql-core

View File

@ -10,4 +10,4 @@ spec:
sourceRef:
kind: GitRepository
name: platform-config
path: ./infra/clusters/pre
path: ./apps/clusters/pre

View File

@ -0,0 +1,16 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: stunnel-client-prod
namespace: flux-system
spec:
interval: 5m0s
prune: true
wait: true
timeout: 10m0s
sourceRef:
kind: GitRepository
name: platform-config
path: ./apps/core/stunnel-client/prod
dependsOn:
- name: stunnel-server

View File

@ -1,7 +1,7 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infrastructure-stack
name: stunnel-server
namespace: flux-system
spec:
interval: 5m0s
@ -11,6 +11,6 @@ spec:
sourceRef:
kind: GitRepository
name: platform-config
path: ./databases/postgresql
path: ./databases/stunnel-server
dependsOn:
- name: platform-k3s
- name: postgresql-prod

View File

@ -1,6 +1,7 @@
service:
replicaCount: 1
global:
imagePullPolicy: Always
tag: latest
env:
APP_ENV: pre

View File

@ -1,7 +1,8 @@
service:
replicaCount: 2
global:
# Production consumes an explicitly published stable tag such as `release`.
imagePullPolicy: IfNotPresent
# Production consumes an explicitly published release tag or version.
tag: release
env:
APP_ENV: production

View File

@ -1,6 +1,7 @@
service:
replicaCount: 1
global:
imagePullPolicy: Always
tag: latest
env:
APP_ENV: pre

View File

@ -1,7 +1,8 @@
service:
replicaCount: 2
global:
# Production consumes an explicitly published stable tag such as `release`.
imagePullPolicy: IfNotPresent
# Production consumes an explicitly published release tag or version.
tag: release
env:
APP_ENV: production

View File

@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- stunnel-client-configmap.yaml
- stunnel-client-deployment.yaml
- stunnel-client-service.yaml

View File

@ -0,0 +1,17 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: postgresql-stunnel-client
data:
stunnel.conf: |
foreground = yes
debug = 5
[postgres-client]
client = yes
accept = 0.0.0.0:15432
connect = postgresql-stunnel-server.database.svc.cluster.local:5433
verifyChain = no
sslVersionMin = TLSv1.2
options = NO_SSLv2
options = NO_SSLv3
TIMEOUTclose = 0

View File

@ -0,0 +1,31 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql-stunnel-client
spec:
replicas: 1
selector:
matchLabels:
app: postgresql-stunnel-client
template:
metadata:
labels:
app: postgresql-stunnel-client
spec:
imagePullSecrets:
- name: postgresql-ghcr-pull
containers:
- name: stunnel-client
image: ghcr.io/x-evor/postgresql.svc.plus/stunnel-client:latest
imagePullPolicy: IfNotPresent
command: ["stunnel", "/etc/stunnel/stunnel.conf"]
ports:
- containerPort: 15432
volumeMounts:
- name: stunnel-config
mountPath: /etc/stunnel/stunnel.conf
subPath: stunnel.conf
volumes:
- name: stunnel-config
configMap:
name: postgresql-stunnel-client

View File

@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: postgresql-stunnel-client
spec:
selector:
app: postgresql-stunnel-client
ports:
- name: postgres
port: 15432
targetPort: 15432

View File

@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: core-pre
resources:
- namespace.yaml
- release.yaml
- ../base

View File

@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: core-prod
resources:
- namespace.yaml
- release.yaml
- ../base

View File

@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: demo-c
labels:
app.kubernetes.io/component: demo-c

View File

@ -1,30 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: stable
namespace: demo-c
spec:
interval: 1m
url: https://charts.onwalk.net/
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: cp-app
namespace: demo-c
spec:
chart:
spec:
chart: app
version: "0.1.1"
sourceRef:
kind: HelmRepository
name: stable
namespace: demo-c
interval: 1m
values:
image:
repository: artifact.onwalk.net/base/scaffolding-design/c
tag: "dee1c17b11822997e16e71244b1a1e98fe919688"
ingress:
className: "nginx"

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- release.yaml

View File

@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: demo-go
labels:
app.kubernetes.io/component: demo-go

View File

@ -1,30 +0,0 @@
apps/go-demo/release.yaml apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: stable
namespace: demo-go
spec:
interval: 1m
url: https://charts.onwalk.net/
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: stable
namespace: demo-go
spec:
chart:
spec:
chart: app
version: "0.1.1"
sourceRef:
kind: HelmRepository
name: stable
namespace: demo-go
interval: 1m
values:
image:
repository: artifact.onwalk.net/base/scaffolding-design/go
tag: "fe2a0fba3014709b26d8acd75bacb661bf2522a4"
ingress:
className: "nginx"

View File

@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: demo-js
labels:
app.kubernetes.io/component: demo

View File

@ -1,30 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: stable
namespace: demo-js
spec:
interval: 1m
url: https://charts.onwalk.net/
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: stable
namespace: demo-js
spec:
chart:
spec:
chart: app
version: "0.1.1"
sourceRef:
kind: HelmRepository
name: stable
namespace: demo-python
interval: 1m
values:
image:
repository: artifact.onwalk.net/base/scaffolding-design/javascript-frontend
tag: "fc998a6d433c45986dc7d51ab62bf7aa48613d62"
ingress:
className: "nginx"

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- release.yaml

View File

@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: demo-python
labels:
app.kubernetes.io/component: demo-python

View File

@ -1,30 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: stable
namespace: demo-python
spec:
interval: 1m
url: https://charts.onwalk.net/
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: stable
namespace: demo-python
spec:
chart:
spec:
chart: app
version: "0.1.1"
sourceRef:
kind: HelmRepository
name: stable
namespace: demo-python
interval: 1m
values:
image:
repository: artifact.onwalk.net/base/scaffolding-design/python
tag: "d72ba38f7a3a76b71eb50f00fe46a94497e6ecaa"
ingress:
className: "nginx"

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- release.yaml

View File

@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: demo-rust
labels:
app.kubernetes.io/component: demo-rust

View File

@ -1,30 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: stable
namespace: demo-rust
spec:
interval: 1m
url: https://charts.onwalk.net/
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: stable
namespace: demo-rust
spec:
chart:
spec:
chart: app
version: "0.1.1"
sourceRef:
kind: HelmRepository
name: stable
namespace: demo-rust
interval: 1m
values:
image:
repository: artifact.onwalk.net/base/scaffolding-design/rust
tag: "84a66d19f29c20c57127f5c896d00b0b84dcd986"
ingress:
className: "nginx"

View File

@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: itsm-dev
resources:
- release.yaml

View File

@ -1,40 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: itsm-dev
namespace: itsm-dev
spec:
interval: 1m
chart:
spec:
version: "0.1.16"
chart: itsm
sourceRef:
kind: HelmRepository
name: stable
namespace: itsm-dev
interval: 1m
values:
novu:
web:
ingress:
enabled: true
hostname: novu-web.onwalk.net
ingressClassName: 'nginx'
apisix:
dashboard:
ingress:
enabled: true
className: "nginx"
hosts:
- host: apisix-dashboard.onwalk.net
paths:
- /*
etcd-adapter:
enabled: true
mysql:
host: mysql
port: 3306
username: apisix
password: apisix
database: apisix

View File

@ -6,6 +6,9 @@ metadata:
spec:
interval: 10m0s
releaseName: k3s-platform
dependsOn:
- name: external-secrets
namespace: platform
chartRef:
kind: OCIRepository
name: k3s-platform-chart

View File

@ -0,0 +1,22 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: postgresql
namespace: database
spec:
interval: 10m0s
releaseName: postgresql
chartRef:
kind: OCIRepository
name: postgresql-chart
namespace: database
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
valuesFrom:
- kind: ConfigMap
name: postgresql-values
valuesKey: values.yaml

View File

@ -0,0 +1,12 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: database
resources:
- oci-repository.yaml
- helmrelease.yaml
configMapGenerator:
- name: postgresql-values
files:
- values.yaml=values.yaml
generatorOptions:
disableNameSuffixHash: true

View File

@ -0,0 +1,12 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: postgresql-chart
namespace: database
spec:
interval: 5m0s
url: oci://ghcr.io/x-evor/postgresql-chart
ref:
semver: "1.1.0"
secretRef:
name: postgresql-ghcr-pull

View File

@ -0,0 +1,55 @@
server:
enabled: true
image:
repository: ghcr.io/x-evor/postgresql.svc.plus/postgresql-svc-plus
tag: "17"
pullPolicy: IfNotPresent
imagePullSecrets:
- name: postgresql-ghcr-pull
auth:
username: postgres
database: postgres
existingSecret: postgresql-auth
secretKey: password
initScripts:
enabled: true
scripts:
01-core-schemas.sql: |
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'core_prod_user') THEN
CREATE ROLE core_prod_user LOGIN;
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'core_pre_user') THEN
CREATE ROLE core_pre_user LOGIN;
END IF;
END
$$;
CREATE SCHEMA IF NOT EXISTS core_prod AUTHORIZATION postgres;
CREATE SCHEMA IF NOT EXISTS core_pre AUTHORIZATION postgres;
GRANT USAGE ON SCHEMA core_prod TO core_prod_user;
GRANT USAGE ON SCHEMA core_pre TO core_pre_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA core_prod
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO core_prod_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA core_pre
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO core_pre_user;
persistence:
enabled: true
size: 20Gi
metrics:
enabled: false
stunnel:
enabled: false
stunnelClient:
enabled: false

View File

@ -0,0 +1,26 @@
# PostgreSQL GitOps Bootstrap
This stack uses ExternalSecrets to materialize runtime credentials from Vault.
The GitOps manifests intentionally do not store secret values.
## Vault paths expected by this stack
- `postgresql.svc.plus`
- `POSTGRES_USER`
- `POSTGRES_PASSWORD`
- `GHCR_USERNAME`
- `GHCR_TOKEN`
## Bootstrap rule
Before or during initial reconciliation, the Vault key `postgresql.svc.plus`
must be seeded with the runtime credentials expected by the manifests in this
directory. Otherwise the ExternalSecrets controller will report
`Secret does not exist`.
## Helper
Use `scripts/seed-vault-postgresql.sh` from a trusted admin shell to write the
expected Vault keys from local environment variables or existing K8s Secrets.
The shared TLS Secret for `postgresql-vultr.svc.plus` is synchronized by the
`k3s-platform` Helm chart into `database/postgresql-vultr-tls`, which
`stunnel-server` consumes directly. Do not commit the secret values to Git.

View File

@ -1,22 +0,0 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: postgresql-stunnel-server
namespace: database
spec:
refreshInterval: 1m
secretStoreRef:
kind: ClusterSecretStore
name: vault-platform
target:
name: postgresql-stunnel-server
creationPolicy: Owner
data:
- secretKey: server-cert.pem
remoteRef:
key: database/postgresql-stunnel
property: server-cert.pem
- secretKey: server-key.pem
remoteRef:
key: database/postgresql-stunnel
property: server-key.pem

View File

@ -38,13 +38,13 @@ spec:
name: postgresql-stunnel-server
- name: stunnel-cert
secret:
secretName: postgresql-stunnel-server
secretName: postgresql-vultr-tls
items:
- key: server-cert.pem
- key: tls.crt
path: server-cert.pem
- name: stunnel-key
secret:
secretName: postgresql-stunnel-server
secretName: postgresql-vultr-tls
items:
- key: server-key.pem
- key: tls.key
path: server-key.pem

View File

@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: database
resources:
- stunnel-server-configmap.yaml
- stunnel-server-deployment.yaml
- stunnel-server-service.yaml

View File

@ -0,0 +1,22 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: postgresql-stunnel-server
data:
stunnel.conf: |
foreground = yes
debug = 5
[postgres-tls-server]
client = no
accept = 0.0.0.0:5433
connect = postgresql.database.svc.cluster.local:5432
cert = /etc/stunnel/certs/server-cert.pem
key = /etc/stunnel/certs/server-key.pem
sslVersionMin = TLSv1.2
options = NO_SSLv2
options = NO_SSLv3
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
socket = l:SO_KEEPALIVE=1
TIMEOUTclose = 0
TIMEOUTidle = 43200

View File

@ -0,0 +1,49 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql-stunnel-server
spec:
replicas: 1
selector:
matchLabels:
app: postgresql-stunnel-server
template:
metadata:
labels:
app: postgresql-stunnel-server
spec:
imagePullSecrets:
- name: postgresql-ghcr-pull
containers:
- name: stunnel-server
image: ghcr.io/x-evor/postgresql.svc.plus/stunnel-server:latest
imagePullPolicy: IfNotPresent
command: ["stunnel", "/etc/stunnel/stunnel.conf"]
ports:
- containerPort: 5433
volumeMounts:
- name: stunnel-conf
mountPath: /etc/stunnel/stunnel.conf
subPath: stunnel.conf
- name: stunnel-cert
mountPath: /etc/stunnel/certs/server-cert.pem
subPath: server-cert.pem
- name: stunnel-key
mountPath: /etc/stunnel/certs/server-key.pem
subPath: server-key.pem
volumes:
- name: stunnel-conf
configMap:
name: postgresql-stunnel-server
- name: stunnel-cert
secret:
secretName: postgresql-vultr-tls
items:
- key: tls.crt
path: server-cert.pem
- name: stunnel-key
secret:
secretName: postgresql-vultr-tls
items:
- key: tls.key
path: server-key.pem

View File

@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: postgresql-stunnel-server
spec:
selector:
app: postgresql-stunnel-server
ports:
- name: tls
port: 5433
targetPort: 5433

View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
seed-vault-postgresql.sh --vault-addr <addr> --vault-token <token> \
--postgres-user <user> --postgres-password <password> \
--ghcr-username <user> --ghcr-token <token> \
--tls-crt <path> --tls-key <path>
Options may also be provided via environment variables:
VAULT_ADDR, VAULT_TOKEN, POSTGRES_USER, POSTGRES_PASSWORD,
GHCR_USERNAME, GHCR_TOKEN, TLS_CRT, TLS_KEY
This writes the runtime values expected by databases/postgresql/*.yaml to the
Vault KV path postgresql.svc.plus and the shared TLS material for
postgresql-vultr.svc.plus.
EOF
}
VAULT_ADDR="${VAULT_ADDR:-}"
VAULT_TOKEN="${VAULT_TOKEN:-}"
POSTGRES_USER="${POSTGRES_USER:-}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}"
GHCR_USERNAME="${GHCR_USERNAME:-}"
GHCR_TOKEN="${GHCR_TOKEN:-}"
TLS_CRT="${TLS_CRT:-}"
TLS_KEY="${TLS_KEY:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--vault-addr) VAULT_ADDR="${2:-}"; shift 2 ;;
--vault-token) VAULT_TOKEN="${2:-}"; shift 2 ;;
--postgres-user) POSTGRES_USER="${2:-}"; shift 2 ;;
--postgres-password) POSTGRES_PASSWORD="${2:-}"; shift 2 ;;
--ghcr-username) GHCR_USERNAME="${2:-}"; shift 2 ;;
--ghcr-token) GHCR_TOKEN="${2:-}"; shift 2 ;;
--tls-crt) TLS_CRT="${2:-}"; shift 2 ;;
--tls-key) TLS_KEY="${2:-}"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "unknown argument: $1" >&2; usage >&2; exit 1 ;;
esac
done
missing=0
for name in VAULT_ADDR VAULT_TOKEN POSTGRES_USER POSTGRES_PASSWORD GHCR_USERNAME GHCR_TOKEN TLS_CRT TLS_KEY; do
if [[ -z "${!name}" ]]; then
echo "missing required value: $name" >&2
missing=1
fi
done
[[ "$missing" -eq 0 ]] || exit 1
if ! command -v vault >/dev/null 2>&1; then
echo "missing required command: vault" >&2
exit 1
fi
export VAULT_ADDR VAULT_TOKEN
vault kv put postgresql.svc.plus \
POSTGRES_USER="$POSTGRES_USER" \
POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
GHCR_USERNAME="$GHCR_USERNAME" \
GHCR_TOKEN="$GHCR_TOKEN"
vault kv put postgresql-vultr.svc.plus \
tls.crt="$(cat "$TLS_CRT")" \
tls.key="$(cat "$TLS_KEY")"