diff --git a/.github/workflows/release-oci-charts.yml b/.github/workflows/release-oci-charts.yml new file mode 100644 index 0000000..90d79da --- /dev/null +++ b/.github/workflows/release-oci-charts.yml @@ -0,0 +1,66 @@ +name: release-oci-charts + +on: + push: + branches: + - main + paths: + - "oci/charts/apps/app-service/**" + - "oci/charts/infra/platform/k3s/**" + - "oci/charts/postgresql/**" + - "oci/charts/observability/**" + - ".github/workflows/release-oci-charts.yml" + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Helm + uses: azure/setup-helm@v4 + with: + version: v3.15.4 + + - name: Login to GHCR + run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u "${{ github.actor }}" --password-stdin + + - name: Lint charts + run: | + set -euo pipefail + charts=( + "oci/charts/apps/app-service" + "oci/charts/infra/platform/k3s" + "oci/charts/postgresql" + "oci/charts/observability" + ) + for chart in "${charts[@]}"; do + helm lint "./${chart}" + done + + - name: Package charts + run: | + set -euo pipefail + mkdir -p dist + charts=( + "oci/charts/apps/app-service" + "oci/charts/infra/platform/k3s" + "oci/charts/postgresql" + "oci/charts/observability" + ) + for chart in "${charts[@]}"; do + helm package "./${chart}" --destination dist + done + + - name: Push charts to GHCR + run: | + set -euo pipefail + for pkg in dist/*.tgz; do + helm push "${pkg}" oci://ghcr.io/x-evor/charts + done diff --git a/oci/charts/README.md b/oci/charts/README.md new file mode 100644 index 0000000..32586b4 --- /dev/null +++ b/oci/charts/README.md @@ -0,0 +1,16 @@ +# OCI Charts + +This repository stores reusable Helm charts published to `ghcr.io/x-evor/charts`. + +## Layout + +- `apps/app-service`: reusable runtime chart for application services +- `infra/platform/k3s`: platform composition chart for `caddy`, `apisix`, and `vault` +- `postgresql`: PostgreSQL service chart with optional `stunnel` server/client +- `observability`: observability composition chart for server and agent components + +## Release Model + +- Registry: `oci://ghcr.io/x-evor/charts` +- Each chart is versioned independently +- Runtime image tags are managed by GitOps values rather than chart versions diff --git a/oci/charts/apps/app-service/Chart.yaml b/oci/charts/apps/app-service/Chart.yaml new file mode 100644 index 0000000..c4a57a7 --- /dev/null +++ b/oci/charts/apps/app-service/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: app-service +description: Reusable chart for core HTTP application services +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/oci/charts/apps/app-service/templates/_helpers.tpl b/oci/charts/apps/app-service/templates/_helpers.tpl new file mode 100644 index 0000000..1230e56 --- /dev/null +++ b/oci/charts/apps/app-service/templates/_helpers.tpl @@ -0,0 +1,26 @@ +{{- define "app-service.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "app-service.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- include "app-service.name" . -}} +{{- end -}} +{{- end -}} + +{{- define "app-service.labels" -}} +app.kubernetes.io/name: {{ include "app-service.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{- end -}} + +{{- define "app-service.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} +{{- default (include "app-service.fullname" .) .Values.serviceAccount.name -}} +{{- else -}} +{{- default "default" .Values.serviceAccount.name -}} +{{- end -}} +{{- end -}} diff --git a/oci/charts/apps/app-service/templates/deployment.yaml b/oci/charts/apps/app-service/templates/deployment.yaml new file mode 100644 index 0000000..28b0850 --- /dev/null +++ b/oci/charts/apps/app-service/templates/deployment.yaml @@ -0,0 +1,83 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "app-service.fullname" . }} + labels: + {{- include "app-service.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: 3 + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxUnavailable: {{ .Values.strategy.rollingUpdate.maxUnavailable }} + maxSurge: {{ .Values.strategy.rollingUpdate.maxSurge }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "app-service.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + {{- include "app-service.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + {{- if and .Values.reloader.enabled .Values.existingSecretName }} + secret.reloader.stakater.com/reload: {{ default .Values.existingSecretName .Values.reloader.secretMatch | quote }} + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "app-service.serviceAccountName" . }} + {{- with .Values.image.pullSecrets }} + imagePullSecrets: + {{- range . }} + - name: {{ . }} + {{- end }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: app + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.containerPort }} + {{- if .Values.env }} + env: + {{- range $key, $value := .Values.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + {{- if or .Values.existingSecretName .Values.envFromSecretRefs }} + envFrom: + {{- if .Values.existingSecretName }} + - secretRef: + name: {{ .Values.existingSecretName }} + {{- end }} + {{- range .Values.envFromSecretRefs }} + - secretRef: + name: {{ . }} + {{- end }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} diff --git a/oci/charts/apps/app-service/templates/ingress.yaml b/oci/charts/apps/app-service/templates/ingress.yaml new file mode 100644 index 0000000..f855a50 --- /dev/null +++ b/oci/charts/apps/app-service/templates/ingress.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "app-service.fullname" . }} + labels: + {{- include "app-service.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- with .Values.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ default "Prefix" .pathType }} + backend: + service: + name: {{ include "app-service.fullname" $ }} + port: + number: {{ default $.Values.service.port .servicePort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/oci/charts/apps/app-service/templates/pdb.yaml b/oci/charts/apps/app-service/templates/pdb.yaml new file mode 100644 index 0000000..ae8398a --- /dev/null +++ b/oci/charts/apps/app-service/templates/pdb.yaml @@ -0,0 +1,14 @@ +{{- if .Values.pdb.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "app-service.fullname" . }} + labels: + {{- include "app-service.labels" . | nindent 4 }} +spec: + minAvailable: {{ .Values.pdb.minAvailable }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "app-service.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/oci/charts/apps/app-service/templates/service.yaml b/oci/charts/apps/app-service/templates/service.yaml new file mode 100644 index 0000000..708d9cd --- /dev/null +++ b/oci/charts/apps/app-service/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "app-service.fullname" . }} + labels: + {{- include "app-service.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + selector: + app.kubernetes.io/name: {{ include "app-service.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http diff --git a/oci/charts/apps/app-service/templates/serviceaccount.yaml b/oci/charts/apps/app-service/templates/serviceaccount.yaml new file mode 100644 index 0000000..889dc1f --- /dev/null +++ b/oci/charts/apps/app-service/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "app-service.serviceAccountName" . }} + labels: + {{- include "app-service.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/oci/charts/apps/app-service/values.yaml b/oci/charts/apps/app-service/values.yaml new file mode 100644 index 0000000..a715d44 --- /dev/null +++ b/oci/charts/apps/app-service/values.yaml @@ -0,0 +1,76 @@ +nameOverride: "" +fullnameOverride: "" + +replicaCount: 1 + +image: + repository: ghcr.io/example/app + tag: latest + pullPolicy: IfNotPresent + pullSecrets: [] + +containerPort: 8080 + +service: + port: 80 + type: ClusterIP + annotations: {} + +strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + +podLabels: {} +podAnnotations: {} + +serviceAccount: + create: false + name: "" + annotations: {} + +env: {} +existingSecretName: "" +envFromSecretRefs: [] + +resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + +readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + +livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + +pdb: + enabled: true + minAvailable: 1 + +ingress: + enabled: false + className: "" + annotations: {} + tls: [] + hosts: [] + +nodeSelector: {} +tolerations: [] +affinity: {} + +reloader: + enabled: true + secretMatch: "" diff --git a/oci/charts/infra/platform/k3s/Chart.yaml b/oci/charts/infra/platform/k3s/Chart.yaml new file mode 100644 index 0000000..11e6923 --- /dev/null +++ b/oci/charts/infra/platform/k3s/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: k3s-platform-chart +description: Composes Flux-managed platform components for the k3s platform +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/oci/charts/infra/platform/k3s/templates/apisix-helmrelease.yaml b/oci/charts/infra/platform/k3s/templates/apisix-helmrelease.yaml new file mode 100644 index 0000000..c564dfc --- /dev/null +++ b/oci/charts/infra/platform/k3s/templates/apisix-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.components.apisix.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.components.apisix.releaseName }} + namespace: {{ .Values.namespaces.platform }} +spec: + interval: 10m0s + releaseName: {{ .Values.components.apisix.releaseName }} + chart: + spec: + chart: {{ .Values.components.apisix.chart.name }} + version: {{ .Values.components.apisix.chart.version | quote }} + sourceRef: + kind: {{ .Values.components.apisix.sourceRef.kind }} + name: {{ .Values.components.apisix.sourceRef.name }} + namespace: {{ .Values.components.apisix.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.components.apisix.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/infra/platform/k3s/templates/apisix-ingress.yaml b/oci/charts/infra/platform/k3s/templates/apisix-ingress.yaml new file mode 100644 index 0000000..4f5779d --- /dev/null +++ b/oci/charts/infra/platform/k3s/templates/apisix-ingress.yaml @@ -0,0 +1,30 @@ +{{- if .Values.apisixIngress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.apisixIngress.name }} + namespace: {{ .Values.apisixIngress.namespace }} + {{- with .Values.apisixIngress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.apisixIngress.className }} + tls: + - hosts: + {{- toYaml .Values.apisixIngress.tls.hosts | nindent 8 }} + secretName: {{ .Values.apisixIngress.tls.secretName }} + rules: + {{- range .Values.apisixIngress.hosts }} + - host: {{ .host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .serviceName }} + port: + number: {{ .servicePort }} + {{- end }} +{{- end }} diff --git a/oci/charts/infra/platform/k3s/templates/caddy-helmrelease.yaml b/oci/charts/infra/platform/k3s/templates/caddy-helmrelease.yaml new file mode 100644 index 0000000..ab26431 --- /dev/null +++ b/oci/charts/infra/platform/k3s/templates/caddy-helmrelease.yaml @@ -0,0 +1,27 @@ +{{- if .Values.components.caddy.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.components.caddy.releaseName }} + namespace: {{ .Values.namespaces.platform }} +spec: + interval: 10m0s + releaseName: {{ .Values.components.caddy.releaseName }} + chart: + spec: + chart: {{ .Values.components.caddy.chart.name }} + version: {{ .Values.components.caddy.chart.version | quote }} + sourceRef: + kind: {{ .Values.components.caddy.sourceRef.kind }} + name: {{ .Values.components.caddy.sourceRef.name }} + namespace: {{ .Values.components.caddy.sourceRef.namespace }} + install: + createNamespace: false + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.components.caddy.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/infra/platform/k3s/templates/extra-objects.yaml b/oci/charts/infra/platform/k3s/templates/extra-objects.yaml new file mode 100644 index 0000000..8dd36ec --- /dev/null +++ b/oci/charts/infra/platform/k3s/templates/extra-objects.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraObjects }} +--- +{{ toYaml . }} +{{- end }} diff --git a/oci/charts/infra/platform/k3s/templates/vault-bootstrap.yaml b/oci/charts/infra/platform/k3s/templates/vault-bootstrap.yaml new file mode 100644 index 0000000..bdbe1e4 --- /dev/null +++ b/oci/charts/infra/platform/k3s/templates/vault-bootstrap.yaml @@ -0,0 +1,75 @@ +{{- if .Values.vaultBootstrap.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: vault-bootstrap + namespace: {{ .Values.namespaces.vault }} +spec: + template: + spec: + serviceAccountName: {{ .Values.vaultBootstrap.serviceAccountName }} + restartPolicy: OnFailure + containers: + - name: bootstrap + image: {{ .Values.vaultBootstrap.image }} + env: + - name: VAULT_ADDR + value: http://vault.{{ .Values.namespaces.vault }}.svc.cluster.local:8200 + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.vaultBootstrap.rootTokenSecretName }} + key: {{ .Values.vaultBootstrap.rootTokenSecretKey }} + - name: CLOUDFLARE_API_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.vaultBootstrap.cloudflareSecretName }} + key: {{ .Values.vaultBootstrap.cloudflareSecretKey }} + command: + - /bin/sh + - -ec + - | + until vault status >/dev/null 2>&1; do + sleep 5 + done + vault secrets enable -path=secret kv-v2 || true + cat <<'EOF' >/tmp/eso-policy.hcl + path "secret/data/*" { + capabilities = ["read"] + } + path "secret/metadata/*" { + capabilities = ["read", "list"] + } + EOF + vault policy write eso-read /tmp/eso-policy.hcl + vault auth enable kubernetes || true + vault write auth/kubernetes/config \ + kubernetes_host="https://kubernetes.default.svc:443" \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" + vault write auth/kubernetes/role/external-secrets \ + bound_service_account_names="external-secrets" \ + bound_service_account_namespaces="{{ .Values.vaultBootstrap.externalSecretsRoleNamespace }}" \ + policies="eso-read" \ + ttl="1h" + vault kv put secret/platform/cloudflare api-token="${CLOUDFLARE_API_TOKEN}" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.vaultBootstrap.serviceAccountName }} + namespace: {{ .Values.namespaces.vault }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: vault-bootstrap-auth-delegator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: + - kind: ServiceAccount + name: {{ .Values.vaultBootstrap.serviceAccountName }} + namespace: {{ .Values.namespaces.vault }} +{{- end }} diff --git a/oci/charts/infra/platform/k3s/templates/vault-helmrelease.yaml b/oci/charts/infra/platform/k3s/templates/vault-helmrelease.yaml new file mode 100644 index 0000000..a379f12 --- /dev/null +++ b/oci/charts/infra/platform/k3s/templates/vault-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.components.vault.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.components.vault.releaseName }} + namespace: {{ .Values.namespaces.vault }} +spec: + interval: 10m0s + releaseName: {{ .Values.components.vault.releaseName }} + chart: + spec: + chart: {{ .Values.components.vault.chart.name }} + version: {{ .Values.components.vault.chart.version | quote }} + sourceRef: + kind: {{ .Values.components.vault.sourceRef.kind }} + name: {{ .Values.components.vault.sourceRef.name }} + namespace: {{ .Values.components.vault.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.components.vault.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/infra/platform/k3s/values.yaml b/oci/charts/infra/platform/k3s/values.yaml new file mode 100644 index 0000000..c7aea93 --- /dev/null +++ b/oci/charts/infra/platform/k3s/values.yaml @@ -0,0 +1,111 @@ +namespaces: + platform: platform + vault: extsvc + +components: + caddy: + enabled: true + releaseName: caddy + sourceRef: + kind: HelmRepository + name: caddy-ingress + namespace: flux-system + chart: + name: caddy-ingress-controller + version: ">=1.0.0 <2.0.0" + values: + ingressController: + enabled: true + ingressClass: + create: true + name: caddy + default: false + service: + type: LoadBalancer + apisix: + enabled: true + releaseName: apisix + sourceRef: + kind: HelmRepository + name: apisix + namespace: flux-system + chart: + name: apisix + version: ">=2.7.0 <3.0.0" + values: + etcd: + enabled: false + ingress-controller: + enabled: false + dashboard: + enabled: false + gateway: + type: ClusterIP + apisix: + deployment: + role: traditional + roleTraditional: + configProvider: yaml + standalone: + enabled: true + admin: + enabled: false + vault: + enabled: true + releaseName: vault + sourceRef: + kind: HelmRepository + name: hashicorp + namespace: flux-system + chart: + name: vault + version: ">=0.28.0 <1.0.0" + values: + injector: + enabled: false + server: + standalone: + enabled: false + dataStorage: + enabled: true + size: 8Gi + ha: + enabled: true + replicas: 1 + raft: + enabled: true + setNodeId: true + service: + enabled: true + +apisixIngress: + enabled: true + name: apisix-gateway + namespace: platform + className: caddy + annotations: + external-dns.alpha.kubernetes.io/hostname: api.svc.plus,api-pre.svc.plus + tls: + secretName: apisix-gateway-tls + hosts: + - api.svc.plus + - api-pre.svc.plus + hosts: + - host: api.svc.plus + serviceName: apisix-gateway + servicePort: 80 + - host: api-pre.svc.plus + serviceName: apisix-gateway + servicePort: 80 + +vaultBootstrap: + enabled: true + image: hashicorp/vault:1.16.3 + serviceAccountName: vault-bootstrap + cloudflareSecretName: vault-bootstrap + cloudflareSecretKey: cloudflareApiToken + rootTokenSecretName: vault-bootstrap + rootTokenSecretKey: rootToken + externalSecretsRoleNamespace: platform + +extraObjects: [] diff --git a/oci/charts/observability/Chart.yaml b/oci/charts/observability/Chart.yaml new file mode 100644 index 0000000..70e5ed2 --- /dev/null +++ b/oci/charts/observability/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: observability +description: Observability composition chart for server and agent components +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - observability + - prometheus + - victoria + - grafana + - otel +home: https://github.com/cloud-neutral-toolkit/observability.svc.plus +sources: + - https://github.com/cloud-neutral-toolkit/observability.svc.plus +maintainers: + - name: Cloud-Neutral Toolkit + email: admin@svc.plus diff --git a/oci/charts/observability/templates/extra-objects.yaml b/oci/charts/observability/templates/extra-objects.yaml new file mode 100644 index 0000000..8dd36ec --- /dev/null +++ b/oci/charts/observability/templates/extra-objects.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraObjects }} +--- +{{ toYaml . }} +{{- end }} diff --git a/oci/charts/observability/templates/grafana-helmrelease.yaml b/oci/charts/observability/templates/grafana-helmrelease.yaml new file mode 100644 index 0000000..14598b0 --- /dev/null +++ b/oci/charts/observability/templates/grafana-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.grafana.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.server.grafana.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.server.grafana.releaseName }} + chart: + spec: + chart: {{ .Values.server.grafana.chart.name }} + version: {{ .Values.server.grafana.chart.version | quote }} + sourceRef: + kind: {{ .Values.server.grafana.sourceRef.kind }} + name: {{ .Values.server.grafana.sourceRef.name }} + namespace: {{ .Values.server.grafana.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.server.grafana.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/templates/node-exporter-helmrelease.yaml b/oci/charts/observability/templates/node-exporter-helmrelease.yaml new file mode 100644 index 0000000..c29fa31 --- /dev/null +++ b/oci/charts/observability/templates/node-exporter-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.agent.nodeExporter.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.agent.nodeExporter.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.agent.nodeExporter.releaseName }} + chart: + spec: + chart: {{ .Values.agent.nodeExporter.chart.name }} + version: {{ .Values.agent.nodeExporter.chart.version | quote }} + sourceRef: + kind: {{ .Values.agent.nodeExporter.sourceRef.kind }} + name: {{ .Values.agent.nodeExporter.sourceRef.name }} + namespace: {{ .Values.agent.nodeExporter.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.agent.nodeExporter.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/templates/otel-connector-helmrelease.yaml b/oci/charts/observability/templates/otel-connector-helmrelease.yaml new file mode 100644 index 0000000..8ea7bc2 --- /dev/null +++ b/oci/charts/observability/templates/otel-connector-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.otelConnector.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.server.otelConnector.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.server.otelConnector.releaseName }} + chart: + spec: + chart: {{ .Values.server.otelConnector.chart.name }} + version: {{ .Values.server.otelConnector.chart.version | quote }} + sourceRef: + kind: {{ .Values.server.otelConnector.sourceRef.kind }} + name: {{ .Values.server.otelConnector.sourceRef.name }} + namespace: {{ .Values.server.otelConnector.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.server.otelConnector.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/templates/process-exporter.yaml b/oci/charts/observability/templates/process-exporter.yaml new file mode 100644 index 0000000..583fc62 --- /dev/null +++ b/oci/charts/observability/templates/process-exporter.yaml @@ -0,0 +1,65 @@ +{{- if .Values.agent.processExporter.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.agent.processExporter.serviceAccountName }} + namespace: {{ .Values.namespaces.observability }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: process-exporter-config + namespace: {{ .Values.namespaces.observability }} +data: + config.yaml: | + {{- .Values.agent.processExporter.config | nindent 4 }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: process-exporter + namespace: {{ .Values.namespaces.observability }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: process-exporter + template: + metadata: + labels: + app.kubernetes.io/name: process-exporter + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .Values.agent.processExporter.port }}" + spec: + serviceAccountName: {{ .Values.agent.processExporter.serviceAccountName }} + hostPID: true + containers: + - name: process-exporter + image: "{{ .Values.agent.processExporter.image.repository }}:{{ .Values.agent.processExporter.image.tag }}" + imagePullPolicy: {{ .Values.agent.processExporter.image.pullPolicy }} + args: + - --procfs + - /host/proc + - --config.path + - /etc/process-exporter/config.yaml + - --web.listen-address=:{{ .Values.agent.processExporter.port }} + ports: + - name: metrics + containerPort: {{ .Values.agent.processExporter.port }} + protocol: TCP + resources: + {{- toYaml .Values.agent.processExporter.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /etc/process-exporter + - name: proc + mountPath: /host/proc + readOnly: true + volumes: + - name: config + configMap: + name: process-exporter-config + - name: proc + hostPath: + path: /proc +{{- end }} diff --git a/oci/charts/observability/templates/prometheus-helmrelease.yaml b/oci/charts/observability/templates/prometheus-helmrelease.yaml new file mode 100644 index 0000000..2e7e511 --- /dev/null +++ b/oci/charts/observability/templates/prometheus-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.prometheus.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.server.prometheus.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.server.prometheus.releaseName }} + chart: + spec: + chart: {{ .Values.server.prometheus.chart.name }} + version: {{ .Values.server.prometheus.chart.version | quote }} + sourceRef: + kind: {{ .Values.server.prometheus.sourceRef.kind }} + name: {{ .Values.server.prometheus.sourceRef.name }} + namespace: {{ .Values.server.prometheus.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.server.prometheus.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/templates/vector-agent.yaml b/oci/charts/observability/templates/vector-agent.yaml new file mode 100644 index 0000000..a7770c4 --- /dev/null +++ b/oci/charts/observability/templates/vector-agent.yaml @@ -0,0 +1,58 @@ +{{- if .Values.agent.vector.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.agent.vector.serviceAccountName }} + namespace: {{ .Values.namespaces.observability }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: vector-agent-config + namespace: {{ .Values.namespaces.observability }} +data: + vector.yaml: | + {{- .Values.agent.vector.config | nindent 4 }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: vector-agent + namespace: {{ .Values.namespaces.observability }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: vector-agent + template: + metadata: + labels: + app.kubernetes.io/name: vector-agent + spec: + serviceAccountName: {{ .Values.agent.vector.serviceAccountName }} + containers: + - name: vector + image: "{{ .Values.agent.vector.image.repository }}:{{ .Values.agent.vector.image.tag }}" + imagePullPolicy: {{ .Values.agent.vector.image.pullPolicy }} + resources: + {{- toYaml .Values.agent.vector.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /etc/vector + - name: var-log + mountPath: /var/log + readOnly: true + - name: machine-id + mountPath: /etc/machine-id + readOnly: true + volumes: + - name: config + configMap: + name: vector-agent-config + - name: var-log + hostPath: + path: /var/log + - name: machine-id + hostPath: + path: /etc/machine-id + type: File +{{- end }} diff --git a/oci/charts/observability/templates/victoria-logs-helmrelease.yaml b/oci/charts/observability/templates/victoria-logs-helmrelease.yaml new file mode 100644 index 0000000..31595b9 --- /dev/null +++ b/oci/charts/observability/templates/victoria-logs-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.victoriaLogs.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.server.victoriaLogs.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.server.victoriaLogs.releaseName }} + chart: + spec: + chart: {{ .Values.server.victoriaLogs.chart.name }} + version: {{ .Values.server.victoriaLogs.chart.version | quote }} + sourceRef: + kind: {{ .Values.server.victoriaLogs.sourceRef.kind }} + name: {{ .Values.server.victoriaLogs.sourceRef.name }} + namespace: {{ .Values.server.victoriaLogs.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.server.victoriaLogs.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/templates/victoria-metrics-helmrelease.yaml b/oci/charts/observability/templates/victoria-metrics-helmrelease.yaml new file mode 100644 index 0000000..137ef1b --- /dev/null +++ b/oci/charts/observability/templates/victoria-metrics-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.victoriaMetrics.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.server.victoriaMetrics.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.server.victoriaMetrics.releaseName }} + chart: + spec: + chart: {{ .Values.server.victoriaMetrics.chart.name }} + version: {{ .Values.server.victoriaMetrics.chart.version | quote }} + sourceRef: + kind: {{ .Values.server.victoriaMetrics.sourceRef.kind }} + name: {{ .Values.server.victoriaMetrics.sourceRef.name }} + namespace: {{ .Values.server.victoriaMetrics.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.server.victoriaMetrics.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/templates/victoria-traces-helmrelease.yaml b/oci/charts/observability/templates/victoria-traces-helmrelease.yaml new file mode 100644 index 0000000..3f1f7d2 --- /dev/null +++ b/oci/charts/observability/templates/victoria-traces-helmrelease.yaml @@ -0,0 +1,26 @@ +{{- if .Values.server.victoriaTraces.enabled }} +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: {{ .Values.server.victoriaTraces.releaseName }} + namespace: {{ .Values.namespaces.observability }} +spec: + interval: 10m0s + releaseName: {{ .Values.server.victoriaTraces.releaseName }} + chart: + spec: + chart: {{ .Values.server.victoriaTraces.chart.name }} + version: {{ .Values.server.victoriaTraces.chart.version | quote }} + sourceRef: + kind: {{ .Values.server.victoriaTraces.sourceRef.kind }} + name: {{ .Values.server.victoriaTraces.sourceRef.name }} + namespace: {{ .Values.server.victoriaTraces.sourceRef.namespace }} + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + values: + {{- toYaml .Values.server.victoriaTraces.values | nindent 4 }} +{{- end }} diff --git a/oci/charts/observability/values.yaml b/oci/charts/observability/values.yaml new file mode 100644 index 0000000..5ebac19 --- /dev/null +++ b/oci/charts/observability/values.yaml @@ -0,0 +1,183 @@ +namespaces: + observability: observability + +server: + prometheus: + enabled: true + releaseName: prometheus + sourceRef: + kind: HelmRepository + name: prometheus-community + namespace: flux-system + chart: + name: prometheus + version: ">=25.0.0 <26.0.0" + values: + server: + persistentVolume: + enabled: true + size: 20Gi + victoriaMetrics: + enabled: true + releaseName: victoria-metrics + sourceRef: + kind: HelmRepository + name: victoria-metrics + namespace: flux-system + chart: + name: victoria-metrics-single + version: ">=0.13.0 <1.0.0" + values: + server: + persistentVolume: + enabled: true + size: 50Gi + victoriaLogs: + enabled: true + releaseName: victoria-logs + sourceRef: + kind: HelmRepository + name: victoria-metrics + namespace: flux-system + chart: + name: victoria-logs-single + version: ">=0.9.0 <1.0.0" + values: + server: + persistentVolume: + enabled: true + size: 50Gi + victoriaTraces: + enabled: true + releaseName: victoria-traces + sourceRef: + kind: HelmRepository + name: victoria-metrics + namespace: flux-system + chart: + name: victoria-traces-single + version: ">=0.0.1 <1.0.0" + values: {} + grafana: + enabled: true + releaseName: grafana + sourceRef: + kind: HelmRepository + name: grafana + namespace: flux-system + chart: + name: grafana + version: ">=8.0.0 <9.0.0" + values: + persistence: + enabled: true + size: 10Gi + otelConnector: + enabled: true + releaseName: otel-connector + sourceRef: + kind: HelmRepository + name: open-telemetry + namespace: flux-system + chart: + name: opentelemetry-collector + version: ">=0.104.0 <1.0.0" + values: + mode: deployment + config: + receivers: + otlp: + protocols: + grpc: {} + http: {} + processors: + batch: {} + exporters: + debug: {} + service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [debug] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [debug] + logs: + receivers: [otlp] + processors: [batch] + exporters: [debug] + +agent: + nodeExporter: + enabled: true + releaseName: node-exporter + sourceRef: + kind: HelmRepository + name: prometheus-community + namespace: flux-system + chart: + name: prometheus-node-exporter + version: ">=4.30.0 <5.0.0" + values: {} + vector: + enabled: true + image: + repository: timberio/vector + tag: "0.36.0-distroless-libc" + pullPolicy: IfNotPresent + serviceAccountName: vector-agent + vlogsEndpoint: http://victoria-logs-victoria-logs-single-server.observability.svc.cluster.local:9428 + config: | + data_dir: /vector-data-dir + + sources: + journald: + type: journald + + transforms: + normalize: + type: remap + inputs: ["journald"] + source: | + .cluster = "k3s" + .origin = "vector-agent" + + sinks: + vlogs: + type: elasticsearch + inputs: ["normalize"] + endpoints: + - http://victoria-logs-victoria-logs-single-server.observability.svc.cluster.local:9428/insert/elasticsearch/ + mode: bulk + compression: gzip + resources: + limits: + cpu: 300m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + processExporter: + enabled: true + image: + repository: ncabatoff/process-exporter + tag: v0.8.3 + pullPolicy: IfNotPresent + serviceAccountName: process-exporter + port: 9256 + config: | + process_names: + - name: "{{.Comm}}" + cmdline: + - '.+' + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 25m + memory: 64Mi + +extraObjects: [] diff --git a/oci/charts/postgresql/Chart.yaml b/oci/charts/postgresql/Chart.yaml new file mode 100644 index 0000000..b73ca1b --- /dev/null +++ b/oci/charts/postgresql/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +name: postgresql +description: PostgreSQL service chart with optional stunnel server and client for cloud-neutral deployments +type: application +version: 1.1.0 +appVersion: "16.4" +keywords: + - postgresql + - database + - vector + - search + - queue +home: https://github.com/cloud-neutral-toolkit/postgresql.svc.plus +sources: + - https://github.com/cloud-neutral-toolkit/postgresql.svc.plus +maintainers: + - name: Cloud-Neutral Toolkit + email: admin@svc.plus +icon: https://www.postgresql.org/media/img/about/press/elephant.png diff --git a/oci/charts/postgresql/templates/_helpers.tpl b/oci/charts/postgresql/templates/_helpers.tpl new file mode 100644 index 0000000..a6fa0e0 --- /dev/null +++ b/oci/charts/postgresql/templates/_helpers.tpl @@ -0,0 +1,82 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "postgresql.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 }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "postgresql.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "postgresql.labels" -}} +helm.sh/chart: {{ include "postgresql.chart" . }} +{{ include "postgresql.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "postgresql.selectorLabels" -}} +app.kubernetes.io/name: {{ include "postgresql.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "postgresql.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "postgresql.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Get the password secret name +*/}} +{{- define "postgresql.secretName" -}} +{{- if .Values.auth.existingSecret -}} +{{- .Values.auth.existingSecret -}} +{{- else -}} +{{- include "postgresql.fullname" . -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password key +*/}} +{{- define "postgresql.secretKey" -}} +{{- if .Values.auth.existingSecret -}} +{{- .Values.auth.secretKey -}} +{{- else -}} +password +{{- end -}} +{{- end -}} diff --git a/oci/charts/postgresql/templates/configmap-init-scripts.yaml b/oci/charts/postgresql/templates/configmap-init-scripts.yaml new file mode 100644 index 0000000..16d2e31 --- /dev/null +++ b/oci/charts/postgresql/templates/configmap-init-scripts.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.server.enabled .Values.initScripts.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "postgresql.fullname" . }}-init-scripts + labels: + {{- include "postgresql.labels" . | nindent 4 }} +data: + {{- range $key, $value := .Values.initScripts.scripts }} + {{ $key }}: | + {{- $value | nindent 4 }} + {{- end }} +{{- end }} diff --git a/oci/charts/postgresql/templates/configmap-stunnel.yaml b/oci/charts/postgresql/templates/configmap-stunnel.yaml new file mode 100644 index 0000000..4a685c9 --- /dev/null +++ b/oci/charts/postgresql/templates/configmap-stunnel.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.server.enabled .Values.stunnel.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "postgresql.fullname" . }}-stunnel-config + labels: + {{- include "postgresql.labels" . | nindent 4 }} +data: + stunnel.conf: | + {{- .Values.stunnel.config | nindent 4 }} +{{- end }} diff --git a/oci/charts/postgresql/templates/configmap.yaml b/oci/charts/postgresql/templates/configmap.yaml new file mode 100644 index 0000000..0ca57a5 --- /dev/null +++ b/oci/charts/postgresql/templates/configmap.yaml @@ -0,0 +1,25 @@ +{{- if .Values.server.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "postgresql.fullname" . }}-config + labels: + {{- include "postgresql.labels" . | nindent 4 }} +data: + postgresql.conf: | + {{- .Values.postgresql.config | nindent 4 }} + + {{- if .Values.postgresql.pgHba }} + pg_hba.conf: | + # TYPE DATABASE USER ADDRESS METHOD + # Default entries + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + host all all 0.0.0.0/0 md5 + host all all ::/0 md5 + + # Custom entries + {{- .Values.postgresql.pgHba | nindent 4 }} + {{- end }} +{{- end }} diff --git a/oci/charts/postgresql/templates/secret.yaml b/oci/charts/postgresql/templates/secret.yaml new file mode 100644 index 0000000..3ed1e10 --- /dev/null +++ b/oci/charts/postgresql/templates/secret.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.server.enabled (not .Values.auth.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} +type: Opaque +data: + password: {{ .Values.auth.password | b64enc | quote }} +{{- end }} diff --git a/oci/charts/postgresql/templates/service-metrics.yaml b/oci/charts/postgresql/templates/service-metrics.yaml new file mode 100644 index 0000000..00b7b70 --- /dev/null +++ b/oci/charts/postgresql/templates/service-metrics.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.server.enabled .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "postgresql.fullname" . }}-metrics + labels: + {{- include "postgresql.labels" . | nindent 4 }} + {{- with .Values.metrics.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.metrics.service.type }} + ports: + - port: {{ .Values.metrics.service.port }} + targetPort: metrics + protocol: TCP + name: metrics + selector: + {{- include "postgresql.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/oci/charts/postgresql/templates/service.yaml b/oci/charts/postgresql/templates/service.yaml new file mode 100644 index 0000000..934ccb0 --- /dev/null +++ b/oci/charts/postgresql/templates/service.yaml @@ -0,0 +1,27 @@ +{{- if .Values.server.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: postgres + protocol: TCP + name: postgres + {{- if .Values.stunnel.enabled }} + - port: {{ .Values.stunnel.port }} + targetPort: stunnel + protocol: TCP + name: stunnel + {{- end }} + selector: + {{- include "postgresql.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/oci/charts/postgresql/templates/serviceaccount.yaml b/oci/charts/postgresql/templates/serviceaccount.yaml new file mode 100644 index 0000000..0c2dfe5 --- /dev/null +++ b/oci/charts/postgresql/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if and .Values.server.enabled .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "postgresql.serviceAccountName" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/oci/charts/postgresql/templates/statefulset.yaml b/oci/charts/postgresql/templates/statefulset.yaml new file mode 100644 index 0000000..ee3021f --- /dev/null +++ b/oci/charts/postgresql/templates/statefulset.yaml @@ -0,0 +1,203 @@ +{{- if .Values.server.enabled }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} +spec: + serviceName: {{ include "postgresql.fullname" . }} + replicas: 1 + selector: + matchLabels: + {{- include "postgresql.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "postgresql.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "postgresql.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: postgresql + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: postgres + containerPort: 5432 + protocol: TCP + env: + - name: POSTGRES_USER + value: {{ .Values.auth.username | quote }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "postgresql.secretName" . }} + key: {{ include "postgresql.secretKey" . }} + - name: POSTGRES_DB + value: {{ .Values.auth.database | quote }} + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready -U {{ .Values.auth.username }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready -U {{ .Values.auth.username }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + - name: config + mountPath: /etc/postgresql + {{- if .Values.initScripts.enabled }} + - name: init-scripts + mountPath: /docker-entrypoint-initdb.d + {{- end }} + {{- if .Values.tls.enabled }} + - name: tls-certs + mountPath: /etc/postgresql/certs + readOnly: true + {{- end }} + + {{- if .Values.stunnel.enabled }} + - name: stunnel + image: "{{ .Values.stunnel.image.repository }}:{{ .Values.stunnel.image.tag }}" + imagePullPolicy: {{ .Values.stunnel.image.pullPolicy }} + ports: + - name: stunnel + containerPort: {{ .Values.stunnel.port }} + protocol: TCP + volumeMounts: + - name: stunnel-config + mountPath: /etc/stunnel/stunnel.conf + subPath: stunnel.conf + {{- if .Values.stunnel.certificatesSecret }} + - name: stunnel-certs + mountPath: /etc/stunnel/certs + readOnly: true + {{- end }} + {{- end }} + + {{- if .Values.metrics.enabled }} + - name: metrics + image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}" + imagePullPolicy: {{ .Values.metrics.image.pullPolicy }} + ports: + - name: metrics + containerPort: 9187 + protocol: TCP + env: + - name: DATA_SOURCE_NAME + value: "postgresql://{{ .Values.auth.username }}:$(POSTGRES_PASSWORD)@localhost:5432/{{ .Values.auth.database }}?sslmode=disable" + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "postgresql.secretName" . }} + key: {{ include "postgresql.secretKey" . }} + resources: + {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} + + volumes: + - name: config + configMap: + name: {{ include "postgresql.fullname" . }}-config + {{- if .Values.initScripts.enabled }} + - name: init-scripts + configMap: + name: {{ include "postgresql.fullname" . }}-init-scripts + {{- end }} + {{- if .Values.tls.enabled }} + - name: tls-certs + secret: + secretName: {{ .Values.tls.certificatesSecret }} + defaultMode: 0600 + {{- end }} + {{- if .Values.stunnel.enabled }} + - name: stunnel-config + configMap: + name: {{ include "postgresql.fullname" . }}-stunnel-config + {{- if .Values.stunnel.certificatesSecret }} + - name: stunnel-certs + secret: + secretName: {{ .Values.stunnel.certificatesSecret }} + defaultMode: 0600 + {{- end }} + {{- end }} + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- with .Values.persistence.selector }} + selector: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- else }} + - name: data + emptyDir: {} + {{- end }} +{{- end }} diff --git a/oci/charts/postgresql/templates/stunnel-client-configmap.yaml b/oci/charts/postgresql/templates/stunnel-client-configmap.yaml new file mode 100644 index 0000000..5369bab --- /dev/null +++ b/oci/charts/postgresql/templates/stunnel-client-configmap.yaml @@ -0,0 +1,11 @@ +{{- if .Values.stunnelClient.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "postgresql.fullname" . }}-stunnel-client + labels: + {{- include "postgresql.labels" . | nindent 4 }} +data: + stunnel.conf: | + {{- .Values.stunnelClient.config | nindent 4 }} +{{- end }} diff --git a/oci/charts/postgresql/templates/stunnel-client-deployment.yaml b/oci/charts/postgresql/templates/stunnel-client-deployment.yaml new file mode 100644 index 0000000..1064163 --- /dev/null +++ b/oci/charts/postgresql/templates/stunnel-client-deployment.yaml @@ -0,0 +1,40 @@ +{{- if .Values.stunnelClient.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "postgresql.fullname" . }}-stunnel-client + labels: + {{- include "postgresql.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.stunnelClient.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "postgresql.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: stunnel-client + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "postgresql.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: stunnel-client + spec: + containers: + - name: stunnel-client + image: "{{ .Values.stunnelClient.image.repository }}:{{ .Values.stunnelClient.image.tag }}" + imagePullPolicy: {{ .Values.stunnelClient.image.pullPolicy }} + ports: + - name: postgres + containerPort: {{ .Values.stunnelClient.service.port }} + protocol: TCP + resources: + {{- toYaml .Values.stunnelClient.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /etc/stunnel/stunnel.conf + subPath: stunnel.conf + volumes: + - name: config + configMap: + name: {{ include "postgresql.fullname" . }}-stunnel-client +{{- end }} diff --git a/oci/charts/postgresql/templates/stunnel-client-service.yaml b/oci/charts/postgresql/templates/stunnel-client-service.yaml new file mode 100644 index 0000000..67e5a7d --- /dev/null +++ b/oci/charts/postgresql/templates/stunnel-client-service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.stunnelClient.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "postgresql.fullname" . }}-stunnel-client + labels: + {{- include "postgresql.labels" . | nindent 4 }} + {{- with .Values.stunnelClient.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.stunnelClient.service.type }} + ports: + - name: postgres + port: {{ .Values.stunnelClient.service.port }} + targetPort: postgres + protocol: TCP + selector: + app.kubernetes.io/name: {{ include "postgresql.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: stunnel-client +{{- end }} diff --git a/oci/charts/postgresql/values.yaml b/oci/charts/postgresql/values.yaml new file mode 100644 index 0000000..0ef3d2a --- /dev/null +++ b/oci/charts/postgresql/values.yaml @@ -0,0 +1,245 @@ +# Default values for postgresql chart + +server: + enabled: true + +# Image configuration +image: + repository: postgres-extensions + tag: "16" + pullPolicy: IfNotPresent + +# Image pull secrets for private registries +imagePullSecrets: [] + +# Override name +nameOverride: "" +fullnameOverride: "" + +# Service account +serviceAccount: + create: true + annotations: {} + name: "" + +# Pod annotations +podAnnotations: {} + +# Pod security context +podSecurityContext: + fsGroup: 999 + +# Container security context +securityContext: + runAsUser: 999 + runAsNonRoot: true + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + +# Service configuration +service: + type: ClusterIP + port: 5432 + annotations: {} + +# Ingress (not typically used for PostgreSQL, but available) +ingress: + enabled: false + className: "" + annotations: {} + hosts: [] + tls: [] + +# PostgreSQL authentication +auth: + username: postgres + password: "" # Set this or use existingSecret + database: postgres + existingSecret: "" # Name of existing secret with password + secretKey: "password" # Key in the secret + +# PostgreSQL configuration +postgresql: + # Custom postgresql.conf settings + config: | + shared_buffers = 256MB + effective_cache_size = 1GB + work_mem = 16MB + maintenance_work_mem = 64MB + max_connections = 100 + wal_buffers = 16MB + checkpoint_completion_target = 0.9 + random_page_cost = 1.1 + effective_io_concurrency = 200 + log_min_duration_statement = 1000 + + # Custom pg_hba.conf entries (appended to defaults) + pgHba: | + # Custom entries + # host all all 0.0.0.0/0 md5 + +# Initialization scripts +initScripts: + enabled: true + # Scripts will be created from the scripts below + scripts: + 01-init-extensions.sql: | + CREATE EXTENSION IF NOT EXISTS vector; + CREATE EXTENSION IF NOT EXISTS pg_jieba; + CREATE EXTENSION IF NOT EXISTS pgmq; + CREATE EXTENSION IF NOT EXISTS pg_trgm; + CREATE EXTENSION IF NOT EXISTS hstore; + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +# Persistence +persistence: + enabled: true + storageClass: "" # Use default storage class + accessModes: + - ReadWriteOnce + size: 10Gi + annotations: {} + selector: {} + +# Resource limits +resources: + limits: + cpu: 2000m + memory: 2Gi + requests: + cpu: 500m + memory: 1Gi + +# Liveness probe +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +# Readiness probe +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +# Node selector +nodeSelector: {} + +# Tolerations +tolerations: [] + +# Affinity +affinity: {} + +# Metrics (for Prometheus) +metrics: + enabled: false + image: + repository: prometheuscommunity/postgres-exporter + tag: v0.15.0 + pullPolicy: IfNotPresent + service: + type: ClusterIP + port: 9187 + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9187" + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 50m + memory: 64Mi + +# Backup configuration (optional) +backup: + enabled: false + schedule: "0 2 * * *" # Daily at 2 AM + retention: 7 # Keep 7 days of backups + storageClass: "" + size: 20Gi + +# TLS/SSL configuration +tls: + enabled: false + certificatesSecret: "" # Name of secret containing tls.crt and tls.key + certFilename: "tls.crt" + certKeyFilename: "tls.key" + certCAFilename: "ca.crt" + +# Stunnel sidecar for TLS over TCP +stunnel: + enabled: false + image: + repository: dweomer/stunnel + tag: latest + pullPolicy: IfNotPresent + port: 5433 + certificatesSecret: "" # Name of secret containing stunnel certificates + config: | + [postgres-tunnel] + client = no + accept = 0.0.0.0:5433 + connect = 127.0.0.1:5432 + cert = /etc/stunnel/certs/server-cert.pem + key = /etc/stunnel/certs/server-key.pem + sslVersion = TLSv1.2 + options = NO_SSLv2 + options = NO_SSLv3 + ciphers = HIGH:!aNULL:!MD5 + +# NetworkPolicy +networkPolicy: + enabled: false + policyTypes: + - Ingress + ingress: + - from: + - podSelector: {} + ports: + - protocol: TCP + port: 5432 + +# PodDisruptionBudget +podDisruptionBudget: + enabled: false + minAvailable: 1 + # maxUnavailable: 1 + +stunnelClient: + enabled: false + replicaCount: 1 + image: + repository: dweomer/stunnel + tag: latest + pullPolicy: IfNotPresent + service: + type: ClusterIP + port: 5432 + annotations: {} + config: | + [postgres-client] + client = yes + accept = 0.0.0.0:5432 + connect = postgresql.database.svc.cluster.local:5433 + verifyChain = no + sslVersion = TLSv1.2 + options = NO_SSLv2 + options = NO_SSLv3 + ciphers = HIGH:!aNULL:!MD5 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 25m + memory: 64Mi