Skip to content

Commit 8e5c9df

Browse files
committed
cnpg-cluster chart
1 parent 5075f93 commit 8e5c9df

11 files changed

Lines changed: 540 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# https://helm.sh/docs/howto/chart_releaser_action/#github-actions-workflow
2+
name: Release Charts
3+
4+
on:
5+
push:
6+
branches:
7+
- main
8+
paths:
9+
- ".github/workflows/helm-release.yaml"
10+
- "charts/**"
11+
12+
jobs:
13+
release:
14+
permissions:
15+
contents: write
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Configure Git
24+
run: |
25+
git config user.name "$GITHUB_ACTOR"
26+
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
27+
28+
- name: Run chart-releaser
29+
uses: helm/chart-releaser-action@v1.7.0
30+
env:
31+
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"cSpell.words": [
3+
"cnpg"
4+
]
5+
}

charts/cnpg-cluster/.helmignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/

charts/cnpg-cluster/Chart.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: v2
2+
name: cnpg-cluster
3+
description: CloudNativePG Cluster Helm Chart
4+
5+
# A chart can be either an 'application' or a 'library' chart.
6+
#
7+
# Application charts are a collection of templates that can be packaged into versioned archives
8+
# to be deployed.
9+
#
10+
# Library charts provide useful utilities or functions for the chart developer. They're included as
11+
# a dependency of application charts to inject those utilities and functions into the rendering
12+
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
13+
type: application
14+
15+
# This is the chart version. This version number should be incremented each time you make changes
16+
# to the chart and its templates, including the app version.
17+
# Versions are expected to follow Semantic Versioning (https://semver.org/)
18+
version: 0.1.0
19+
20+
# This is the version number of the application being deployed. This version number should be
21+
# incremented each time you make changes to the application. Versions are not expected to
22+
# follow Semantic Versioning. They should reflect the version the application is using.
23+
# It is recommended to use it with quotes.
24+
appVersion: "1.27.0"
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "cnpg-cluster.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11+
If release name contains chart name it will be used as a full name.
12+
*/}}
13+
{{- define "cnpg-cluster.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" $name .Release.Name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "cnpg-cluster.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "cnpg-cluster.labels" -}}
37+
helm.sh/chart: {{ include "cnpg-cluster.chart" . }}
38+
{{ include "cnpg-cluster.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
{{- end }}
44+
45+
{{/*
46+
Selector labels
47+
*/}}
48+
{{- define "cnpg-cluster.selectorLabels" -}}
49+
app.kubernetes.io/name: {{ include "cnpg-cluster.name" . }}
50+
app.kubernetes.io/instance: {{ .Release.Name }}
51+
{{- end }}
52+
53+
{{/*
54+
Create the name of the service account to use
55+
*/}}
56+
{{- define "cnpg-cluster.serviceAccountName" -}}
57+
{{- if .Values.serviceAccount.create }}
58+
{{- default (include "cnpg-cluster.fullname" .) .Values.serviceAccount.name }}
59+
{{- else }}
60+
{{- default "default" .Values.serviceAccount.name }}
61+
{{- end }}
62+
{{- end }}
63+
64+
{{/*
65+
Sanitized compute size factor (minimum 1)
66+
*/}}
67+
{{- define "cnpg-cluster.computeFactor" -}}
68+
{{- $raw := .Values.computeSizeFactor | default 1 -}}
69+
{{- $f := (printf "%v" $raw | atoi) -}}
70+
{{- if lt $f 1 }}{{- $f = 1 }}{{- end -}}
71+
{{- $f -}}
72+
{{- end }}
73+
74+
{{/*
75+
Memory per factor in MB (override with .Values.memoryPerFactorMB, default 512MB)
76+
*/}}
77+
{{- define "cnpg-cluster.memoryPerFactorMB" -}}
78+
{{- (.Values.memoryPerFactorMB | default 128) | int -}}
79+
{{- end }}
80+
81+
{{/*
82+
Total memory (MB) allocated to a PostgreSQL pod
83+
*/}}
84+
{{- define "cnpg-cluster.totalMemoryMB" -}}
85+
{{- $f := include "cnpg-cluster.computeFactor" . | int -}}
86+
{{- $unit := include "cnpg-cluster.memoryPerFactorMB" . | int -}}
87+
{{- mul $f $unit -}}
88+
{{- end }}
89+
90+
{{/*
91+
CPU per factor in millicores (override with .Values.cpuPerFactorMillicores, default 75m)
92+
*/}}
93+
{{- define "cnpg-cluster.cpuPerFactorMillicores" -}}
94+
{{- (.Values.cpuPerFactorMillicores | default 75) | int -}}
95+
{{- end }}
96+
97+
{{/*
98+
Total CPU (millicores) allocated to a PostgreSQL pod
99+
*/}}
100+
{{- define "cnpg-cluster.totalCPUMillicores" -}}
101+
{{- $f := include "cnpg-cluster.computeFactor" . | int -}}
102+
{{- $unit := include "cnpg-cluster.cpuPerFactorMillicores" . | int -}}
103+
{{- mul $f $unit -}}
104+
{{- end }}
105+
106+
{{/*
107+
shared_buffers = 25% total memory
108+
*/}}
109+
{{- define "cnpg-cluster.sharedBuffersMB" -}}
110+
{{- $total := include "cnpg-cluster.totalMemoryMB" . | int -}}
111+
{{- div (mul $total 25) 100 -}}
112+
{{- end }}
113+
114+
{{/*
115+
max_connections = (compute size factor * 8) + 5
116+
*/}}
117+
{{- define "cnpg-cluster.maxConnections" -}}
118+
{{- .Values.computeSizeFactor | mul 8 | add 5 }}
119+
{{- end }}
120+
121+
{{/*
122+
work_mem = (25% total) / max_connections
123+
*/}}
124+
{{- define "cnpg-cluster.workMemMB" -}}
125+
{{- $total := include "cnpg-cluster.totalMemoryMB" . | int -}}
126+
{{- $maxConn := include "cnpg-cluster.maxConnections" . | int -}}
127+
{{- if lt $maxConn 1 }}{{- $maxConn = 1 }}{{- end -}}
128+
{{- $quarter := div (mul $total 25) 100 -}}
129+
{{- $per := div (max 1 $quarter) $maxConn -}}
130+
{{- if lt $per 1 }}1{{ else }}{{ $per }}{{ end }}
131+
{{- end }}
132+
133+
{{/*
134+
maintenance_work_mem = 5% total
135+
*/}}
136+
{{- define "cnpg-cluster.maintenanceWorkMemMB" -}}
137+
{{- $total := include "cnpg-cluster.totalMemoryMB" . | int -}}
138+
{{ div (mul $total 5) 100 }}
139+
{{- end }}
140+
141+
{{/*
142+
effective_cache_size = 50% total memory
143+
*/}}
144+
{{- define "cnpg-cluster.effectiveCacheSizeMB" -}}
145+
{{- $total := include "cnpg-cluster.totalMemoryMB" . | int -}}
146+
{{- div (mul $total 50) 100 -}}
147+
{{- end }}
148+
149+
{{/*
150+
Pooler sizing helpers
151+
*/}}
152+
{{- define "cnpg-cluster.pooler.maxDbConnections" -}}
153+
{{- $clusterMax := include "cnpg-cluster.maxConnections" . | int -}}
154+
{{- $reserve := 5 -}}
155+
{{- $v := sub $clusterMax $reserve -}}
156+
{{- if lt $v 5 }}5{{ else }}{{ $v }}{{ end }}
157+
{{- end }}
158+
159+
{{- define "cnpg-cluster.pooler.maxClientConn" -}}
160+
{{- /* Allow more frontend connections: 10x db connections */ -}}
161+
{{- $db := include "cnpg-cluster.pooler.maxDbConnections" . | int -}}
162+
{{ mul $db 10 }}
163+
{{- end }}
164+
165+
{{- define "cnpg-cluster.pooler.defaultPoolSize" -}}
166+
{{- /* 50% of max_db_connections, min 5 */ -}}
167+
{{- $db := include "cnpg-cluster.pooler.maxDbConnections" . | int -}}
168+
{{- $v := div (mul $db 50) 100 -}}
169+
{{- if lt $v 5 }}5{{ else }}{{ $v }}{{ end }}
170+
{{- end }}
171+
172+
{{- define "cnpg-cluster.now" -}}
173+
{{ now | date "2006-01-02T15:04:05-07:00" }}
174+
{{- end }}
175+
176+
{{/*
177+
Schedule for automatic backups
178+
*/}}
179+
{{- define "cnpg-cluster.randomBackupSchedule" -}}
180+
{{- /* Deterministic pseudo-random schedule between 03:00–04:59 (UTC+8) */ -}}
181+
{{- /* Uses adler32 hash so it is stable across renders (good for GitOps) */ -}}
182+
{{- $seed := adler32sum (printf "%s-%s-%s" .Release.Name .Release.Namespace .Chart.Version) -}}
183+
{{- $offset := mod $seed 120 -}} {{/* 0..119 minutes in 2h window */}}
184+
{{- $hour := add 19 (div $offset 60) -}} {{/* 3 or 4 */}}
185+
{{- $minute := mod $offset 60 -}}
186+
{{- printf "0 %02d %d * * *" $minute $hour -}}
187+
{{- end }}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
apiVersion: postgresql.cnpg.io/v1
2+
kind: Cluster
3+
metadata:
4+
name: {{ include "cnpg-cluster.fullname" . }}
5+
annotations:
6+
argocd.argoproj.io/sync-wave: "-1"
7+
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
8+
spec:
9+
# https://github.com/cloudnative-pg/postgres-containers/pkgs/container/postgresql
10+
imageName: {{ .Values.imageName }}
11+
12+
instances: {{ .Values.instances }}
13+
14+
affinity: {{ .Values.affinity | toYaml | nindent 4 }}
15+
16+
# https://cloudnative-pg.io/documentation/current/resource_management/
17+
resources:
18+
requests:
19+
memory: {{ include "cnpg-cluster.totalMemoryMB" . }}Mi
20+
cpu: {{ include "cnpg-cluster.totalCPUMillicores" . }}m
21+
limits:
22+
memory: {{ include "cnpg-cluster.totalMemoryMB" . }}Mi
23+
cpu: {{ include "cnpg-cluster.totalCPUMillicores" . }}m
24+
25+
postgresql:
26+
parameters:
27+
# https://postgresqlco.nf/doc/en/param/shared_buffers/
28+
# https://www.enterprisedb.com/postgres-tutorials/how-tune-postgresql-memory#section-1
29+
# Total RAM * 0.25
30+
shared_buffers: {{ include "cnpg-cluster.sharedBuffersMB" . }}MB
31+
32+
# https://postgresqlco.nf/doc/en/param/work_mem/
33+
# https://www.enterprisedb.com/postgres-tutorials/how-tune-postgresql-memory
34+
# Total RAM * 0.25 / max_connections
35+
work_mem: {{ include "cnpg-cluster.workMemMB" . }}MB
36+
37+
# https://postgresqlco.nf/doc/en/param/maintenance_work_mem/
38+
# https://www.enterprisedb.com/postgres-tutorials/how-tune-postgresql-memory#section-3
39+
# Total RAM * 0.05
40+
maintenance_work_mem: {{ include "cnpg-cluster.maintenanceWorkMemMB" . }}MB
41+
42+
# https://postgresqlco.nf/doc/en/param/effective_cache_size/
43+
# https://www.enterprisedb.com/postgres-tutorials/how-tune-postgresql-memory#section-4
44+
# Total RAM * 0.5
45+
effective_cache_size: {{ include "cnpg-cluster.effectiveCacheSizeMB" . }}MB
46+
47+
# https://postgresqlco.nf/doc/en/param/max_connections/
48+
# Max connections from pooler + some overhead
49+
max_connections: {{ include "cnpg-cluster.maxConnections" . | quote }}
50+
51+
# https://cloudnative-pg.io/documentation/current/storage/
52+
storage:
53+
storageClass: {{ .Values.storageClass }}
54+
size: {{ .Values.storageSize }}
55+
56+
{{ if eq .Values.recovery.mode "fresh" -}}
57+
# https://cloudnative-pg.io/documentation/current/bootstrap/#bootstrap-an-empty-cluster-initdb
58+
bootstrap:
59+
initdb:
60+
database: app
61+
encoding: UTF8
62+
localeCType: C
63+
localeCollate: C
64+
owner: app
65+
{{- end }}
66+
67+
{{- if or (eq .Values.recovery.mode "production") (eq .Values.recovery.mode "recovery") }}
68+
# https://cloudnative-pg.io/documentation/current/recovery/#pitr-from-an-object-store
69+
bootstrap:
70+
recovery:
71+
source: recovery-object-store
72+
{{- end }}
73+
74+
# https://cloudnative-pg.io/plugin-barman-cloud/docs/usage/#restoring-a-cluster
75+
externalClusters:
76+
# Recovery Object Store (pull, read-only)
77+
- name: recovery-object-store
78+
plugin:
79+
name: barman-cloud.cloudnative-pg.io
80+
parameters:
81+
barmanObjectName: {{ include "cnpg-cluster.fullname" . }}-object-store
82+
serverName: {{ .Values.recovery.timeline.reader | default .Values.recovery.timeline.writer | default (include "cnpg-cluster.now" .) | quote }}
83+
84+
{{- if .Values.backup.enabled }}
85+
# https://cloudnative-pg.io/plugin-barman-cloud/docs/usage/#configuring-wal-archiving
86+
plugins:
87+
# Backup Object Store (push, read-write)
88+
- name: barman-cloud.cloudnative-pg.io
89+
isWALArchiver: true
90+
parameters:
91+
barmanObjectName: {{ include "cnpg-cluster.fullname" . }}-object-store
92+
serverName: {{ .Values.recovery.timeline.writer | default (include "cnpg-cluster.now" .) | quote }}
93+
{{- end }}
94+
95+
managed:
96+
roles:
97+
- name: app
98+
ensure: present
99+
login: true
100+
createrole: true
101+
bypassrls: true
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: barmancloud.cnpg.io/v1
2+
kind: ObjectStore
3+
metadata:
4+
name: {{ include "cnpg-cluster.fullname" . }}-object-store
5+
annotations:
6+
argocd.argoproj.io/sync-wave: "-2"
7+
spec:
8+
retentionPolicy: 15d
9+
configuration:
10+
endpointURL: {{ .Values.objectStore.endpoint }}
11+
destinationPath: "s3://{{ .Values.objectStore.bucketName }}/{{ .Release.Namespace }}/{{ include "cnpg-cluster.fullname" . }}-backups"
12+
s3Credentials:
13+
accessKeyId:
14+
name: {{ .Values.objectStore.s3CredentialsSecretName }}
15+
key: {{ .Values.objectStore.s3AccessKeyIdSecretKey }}
16+
secretAccessKey:
17+
name: {{ .Values.objectStore.s3CredentialsSecretName }}
18+
key: {{ .Values.objectStore.s3SecretAccessKeySecretKey }}
19+
wal:
20+
maxParallel: 8
21+
compression: gzip

0 commit comments

Comments
 (0)