[stable/stolon] external secret, auto cluster spec update, pdb (#9739)

* add support for external stolon secret

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* fix version

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* fix rbac

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* fix Chart.yaml

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* add pdb

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* update README.md

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* auto update cluster spec

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* disable pdb by default

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* add failKeeper hook

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* add sentinel pod checksum annotation

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>

* remove podDisruptionBudget.enabled

Signed-off-by: Taehyun Kim <kgyoo8232@gmail.com>
This commit is contained in:
Taehyun Kim
2019-01-22 01:54:37 -08:00
committed by Kubernetes Prow Robot
parent 13341ab488
commit e26b7ee873
14 changed files with 266 additions and 21 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
name: stolon
version: 1.0.0
appVersion: 0.12.0
version: 1.1.0
appVersion: 0.13.0
description: Stolon - PostgreSQL cloud native High Availability.
home: https://github.com/sorintlab/stolon
icon: https://i.imgur.com/tIW8sAW.png
+22 -4
View File
@@ -26,7 +26,7 @@ Kubernetes is the default store backend. `consul`, `etcdv2` or `etcdv3` can also
| Parameter | Description | Default |
| --------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------ |
| `image.repository` | `stolon` image repository | `sorintlab/stolon` |
| `image.tag` | `stolon` image tag | `v0.12.0-pg10` |
| `image.tag` | `stolon` image tag | `v0.13.0-pg10` |
| `image.pullPolicy` | `stolon` image pull policy | `IfNotPresent` |
| `etcdImage.repository` | `etcd` image repository | `k8s.gcr.io/etcd-amd64` |
| `etcdImage.tag` | `etcd` image tag | `2.3.7` |
@@ -34,21 +34,29 @@ Kubernetes is the default store backend. `consul`, `etcdv2` or `etcdv3` can also
| `debug` | Debug mode | `false` |
| `persistence.enabled` | Use a PVC to persist data | `true` |
| `persistence.storageClassName` | Storage class name of backing PVC | `""` |
| `persistence.accessModes` | Persistent volumes access modes | `["ReadWriteOnce"]` |
| `persistence.accessModes` | Persistent volumes access modes | `["ReadWriteOnce"]` |
| `persistence.size` | Size of data volume | `10Gi` |
| `rbac.create` | Specifies if RBAC resources should be created | `true` |
| `serviceAccount.create` | Specifies if ServiceAccount should be created | `true` |
| `serviceAccount.name` | Name of the generated ServiceAccount | Defaults to fullname template |
| `superuserSecret.name` | Postgres superuser credential secret name | `""` |
| `superuserSecret.usernameKey` | Username key of Postgres superuser in secret | `pg_su_username` |
| `superuserSecret.passwordKey` | Password key of Postgres superuser in secret | `pg_su_password` |
| `superuserUsername` | Postgres superuser username | `stolon` |
| `superuserPassword` | Postgres superuser password | (Required) |
| `superuserPassword` | Postgres superuser password | (Required if `superuserSecret.name` is not set) |
| `replicationSecret.name` | Postgres replication credential secret name | `""` |
| `replicationSecret.usernameKey` | Username key of Postgres replication in secret | `pg_repl_username` |
| `replicationSecret.passwordKey` | Password key of Postgres replication in secret | `pg_repl_password` |
| `replicationUsername` | Replication username | `repluser` |
| `replicationPassword` | Replication password | (Required) |
| `replicationPassword` | Replication password | (Required if `replicationSecret.name` is not set) |
| `store.backend` | Store backend (kubernetes/consul/etcd) | `kubernetes` |
| `store.endpoints` | Store backend endpoints | `nil` |
| `store.kubeResourceKind` | Kubernetes resource kind (only for kubernetes) | `configmap` |
| `pgParameters` | [`postgresql.conf`][pgconf] options used during cluster creation | `{}` |
| `ports` | Ports to expose on pods | `{"stolon":{"containerPort": 5432},"metrics":{"containerPort": 8080}}`|
| `job.autoCreateCluster` | Set to `false` to force-disable auto-cluster-creation which may clear pre-existing postgres db data | `true` |
| `job.autoUpdateClusterSpec` | Set to `false` to force-disable auto-cluster-spec-update | `true` |
| `clusterSpec` | Stolon cluster spec [reference](https://github.com/sorintlab/stolon/blob/master/doc/cluster_spec.md) | `{}` |
| `keeper.replicaCount` | Number of keeper nodes | `2` |
| `keeper.resources` | Keeper resource requests/limit | `{}` |
| `keeper.priorityClassName` | Keeper priorityClassName | `nil` |
@@ -57,18 +65,28 @@ Kubernetes is the default store backend. `consul`, `etcdv2` or `etcdv3` can also
| `keeper.tolerations` | Toleration labels for keeper pod assignment | `[]` |
| `keeper.volumes` | Additional volumes | `[]` |
| `keeper.volumeMounts` | Mount paths for `keeper.volumes` | `[]` |
| `keeper.hooks.failKeeper.enabled` | Enable failkeeper pre-stop hook | `false` |
| `keeper.podDisruptionBudget.enabled` | If true, create a pod disruption budget for keeper pods. | `false` |
| `keeper.podDisruptionBudget.minAvailable` | Minimum number / percentage of pods that should remain scheduled | `""` |
| `keeper.podDisruptionBudget.maxUnavailable` | Maximum number / percentage of pods that may be made unavailable | `""` |
| `proxy.replicaCount` | Number of proxy nodes | `2` |
| `proxy.resources` | Proxy resource requests/limit | `{}` |
| `proxy.priorityClassName` | Proxy priorityClassName | `nil` |
| `proxy.nodeSelector` | Node labels for proxy pod assignment | `{}` |
| `proxy.affinity` | Affinity settings for proxy pod assignment | `{}` |
| `proxy.tolerations` | Toleration labels for proxy pod assignment | `[]` |
| `proxy.podDisruptionBudget.enabled` | If true, create a pod disruption budget for proxy pods. | `false` |
| `proxy.podDisruptionBudget.minAvailable` | Minimum number / percentage of pods that should remain scheduled | `""` |
| `proxy.podDisruptionBudget.maxUnavailable` | Maximum number / percentage of pods that may be made unavailable | `""` |
| `sentinel.replicaCount` | Number of sentinel nodes | `2` |
| `sentinel.resources` | Sentinel resource requests/limit | `{}` |
| `sentinel.priorityClassName` | Sentinel priorityClassName | `nil` |
| `sentinel.nodeSelector` | Node labels for sentinel pod assignment | `{}` |
| `sentinel.affinity` | Affinity settings for sentinel pod assignment | `{}` |
| `sentinel.tolerations` | Toleration labels for sentinel pod assignment | `[]` |
| `sentinel.podDisruptionBudget.enabled` | If true, create a pod disruption budget for sentinel pods. | `false` |
| `sentinel.podDisruptionBudget.minAvailable` | Minimum number / percentage of pods that should remain scheduled | `""` |
| `sentinel.podDisruptionBudget.maxUnavailable` | Maximum number / percentage of pods that may be made unavailable | `""` |
[pgconf]: https://github.com/postgres/postgres/blob/master/src/backend/utils/misc/postgresql.conf.sample
+4
View File
@@ -1,4 +1,8 @@
Stolon cluster installed and initialized.
To get superuser password run
{{ if not (empty .Values.superuserSecret.name) }}
PGPASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ .Values.superuserSecret.name }} -o jsonpath="{.data.{{.Values.superuserSecret.passwordKey}}}" | base64 --decode; echo)
{{ else }}
PGPASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "stolon.fullname" . }} -o jsonpath="{.data.pg_su_password}" | base64 --decode; echo)
{{ end }}
+32
View File
@@ -0,0 +1,32 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "stolon.fullname" . }}
labels:
app: {{ template "stolon.name" . }}
chart: {{ template "stolon.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
data:
{{- if .Values.keeper.hooks.failKeeper.enabled }}
pre-stop-hook.sh: |-
#!/bin/bash
exec &> >(tee -a "/var/log/stolon-hooks.log")
NODE_NAME=${HOSTNAME}
IFS='-' read -ra ADDR <<< "$(hostname)"
STKEEPER_UID="keeper${ADDR[-1]}"
echo "keeper [${STKEEPER_UID}] is failing"
stolonctl \
--cluster-name={{ template "stolon.fullname" . }} \
--store-backend={{ .Values.store.backend }} \
{{- if eq .Values.store.backend "kubernetes" }}
--kube-resource-kind={{ .Values.store.kubeResourceKind }} \
{{- else }}
--store-endpoints={{ .Values.store.endpoints }} \
{{- end }}
failkeeper ${STKEEPER_UID} || true
echo "Node ${NODE_NAME} is ready to shutdown"
{{- end }}
@@ -9,7 +9,8 @@ metadata:
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
"helm.sh/hook-delete-policy": hook-succeeded
"helm.sh/hook": post-install
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
metadata:
@@ -27,7 +28,7 @@ spec:
command: ["sh", "-c", "while ! etcdctl --endpoints {{ .Values.store.endpoints }} cluster-health; do sleep 1 && echo -n .; done"]
{{- end }}
containers:
- name: {{ template "stolon.fullname" . }}
- name: {{ template "stolon.fullname" . }}-create-cluster
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/usr/local/bin/stolonctl"]
@@ -41,6 +42,5 @@ spec:
- --store-endpoints={{ .Values.store.endpoints }}
{{- end }}
- --yes
- '{"initMode":"new"{{- if .Values.pgParameters }},"pgParameters":{{ with .Values.pgParameters }}{{ toJson . }}{{- end }}{{- end }}}'
- '{ "initMode": "new", {{- range $key, $value := .Values.clusterSpec }} {{ $key | quote }}: {{ if typeIs "string" $value }} {{ $value | quote }} {{ else }} {{ $value }} {{ end }}, {{- end }} "pgParameters": {{ toJson .Values.pgParameters }} }'
{{ end }}
@@ -0,0 +1,47 @@
{{ if .Values.job.autoUpdateClusterSpec }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "stolon.fullname" . }}-update-cluster-spec
labels:
app: {{ template "stolon.name" . }}
chart: {{ template "stolon.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
"helm.sh/hook": post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
metadata:
labels:
app: {{ template "stolon.fullname" . }}
release: {{ .Release.Name }}
spec:
restartPolicy: OnFailure
serviceAccountName: {{ template "stolon.serviceAccountName" . }}
{{- if eq .Values.store.backend "etcdv2" "etcdv3" }}
initContainers:
- name: {{ .Chart.Name }}-etcd-wait
image: "{{ .Values.etcdImage.repository }}:{{ .Values.etcdImage.tag }}"
imagePullPolicy: {{ .Values.etcdImage.pullPolicy }}
command: ["sh", "-c", "while ! etcdctl --endpoints {{ .Values.store.endpoints }} cluster-health; do sleep 1 && echo -n .; done"]
{{- end }}
containers:
- name: {{ template "stolon.fullname" . }}-update-cluster-spec
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/usr/local/bin/stolonctl"]
args:
- update
- --cluster-name={{ template "stolon.fullname" . }}
- --store-backend={{ .Values.store.backend }}
{{- if eq .Values.store.backend "kubernetes" }}
- --kube-resource-kind={{ .Values.store.kubeResourceKind }}
{{- else }}
- --store-endpoints={{ .Values.store.endpoints }}
{{- end }}
- -p
- '{ {{- range $key, $value := .Values.clusterSpec }} {{ $key | quote }}: {{ if typeIs "string" $value }} {{ $value | quote }} {{ else }} {{ $value }} {{ end }}, {{- end }} "pgParameters": {{ toJson .Values.pgParameters }} }'
{{ end }}
+18
View File
@@ -0,0 +1,18 @@
{{- if .Values.keeper.podDisruptionBudget }}
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: {{ template "stolon.fullname" . }}-keeper
labels:
app: {{ template "stolon.name" . }}
chart: {{ template "stolon.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
selector:
matchLabels:
app: {{ template "stolon.name" . }}
release: {{ .Release.Name }}
component: stolon-keeper
{{ toYaml .Values.keeper.podDisruptionBudget | indent 2 }}
{{- end }}
@@ -56,22 +56,44 @@ spec:
- name: STKEEPER_CLUSTER_NAME
value: {{ template "stolon.fullname" . }}
- name: STKEEPER_STORE_BACKEND
value: {{ .Values.store.backend | quote}}
value: {{ .Values.store.backend | quote }}
{{- if eq .Values.store.backend "kubernetes" }}
- name: STKEEPER_KUBE_RESOURCE_KIND
value: {{ .Values.store.kubeResourceKind | quote}}
value: {{ .Values.store.kubeResourceKind | quote }}
{{- else }}
- name: STKEEPER_STORE_ENDPOINTS
value: {{ .Values.store.endpoints | quote}}
value: {{ .Values.store.endpoints | quote }}
{{- end }}
- name: STKEEPER_PG_REPL_USERNAME
{{ if not (empty .Values.replicationSecret.name) }}
valueFrom:
secretKeyRef:
name: {{ .Values.replicationSecret.name }}
key: {{ .Values.replicationSecret.usernameKey }}
{{ else }}
value: {{ .Values.replicationUsername | quote}}
{{ end }}
- name: STKEEPER_PG_REPL_PASSWORDFILE
{{ if not (empty .Values.replicationSecret.name) }}
value: /etc/secrets/stolon-{{ .Values.replicationSecret.name }}/{{ .Values.replicationSecret.passwordKey }}
{{ else }}
value: "/etc/secrets/stolon/pg_repl_password"
{{ end }}
- name: STKEEPER_PG_SU_USERNAME
value: {{ .Values.superuserUsername | quote}}
{{ if not (empty .Values.superuserSecret.name) }}
valueFrom:
secretKeyRef:
name: {{ .Values.superuserSecret.name }}
key: {{ .Values.superuserSecret.usernameKey }}
{{ else }}
value: {{ .Values.superuserUsername | quote }}
{{ end }}
- name: STKEEPER_PG_SU_PASSWORDFILE
{{ if not (empty .Values.superuserSecret.name) }}
value: /etc/secrets/stolon-{{ .Values.superuserSecret.name }}/{{ .Values.superuserSecret.passwordKey }}
{{ else }}
value: "/etc/secrets/stolon/pg_su_password"
{{ end }}
- name: STKEEPER_METRICS_LISTEN_ADDRESS
value: "0.0.0.0:8080"
- name: STKEEPER_DEBUG
@@ -86,12 +108,31 @@ spec:
volumeMounts:
- name: data
mountPath: /stolon-data
{{ if or (empty .Values.superuserSecret.name) (empty .Values.replicationSecret.name) }}
- name: stolon-secrets
mountPath: /etc/secrets/stolon
{{ end }}
{{ if not (empty .Values.superuserSecret.name) }}
- name: stolon-secret-{{ .Values.superuserSecret.name }}
mountPath: /etc/secrets/stolon-{{ .Values.superuserSecret.name }}
{{ end }}
{{ if and (not (empty .Values.replicationSecret.name)) (not (eq .Values.superuserSecret.name .Values.replicationSecret.name)) }}
- name: stolon-secret-{{ .Values.replicationSecret.name }}
mountPath: /etc/secrets/stolon-{{ .Values.replicationSecret.name }}
{{ end }}
{{- range $key, $value := .Values.keeper.volumeMounts }}
- name: {{ $key }}
{{ toYaml $value | indent 12 }}
{{- end }}
{{- if .Values.keeper.hooks.failKeeper.enabled }}
- name: config
mountPath: /pre-stop-hook.sh
subPath: pre-stop-hook.sh
lifecycle:
preStop:
exec:
command: ["/bin/bash", "-e", "/pre-stop-hook.sh"]
{{ end }}
{{- with .Values.keeper.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
@@ -105,9 +146,24 @@ spec:
{{ toYaml . | indent 8 }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ template "stolon.fullname" . }}
{{ if or (empty .Values.superuserSecret.name) (empty .Values.replicationSecret.name) }}
- name: stolon-secrets
secret:
secretName: {{ template "stolon.fullname" . }}
{{ end }}
{{ if not (empty .Values.superuserSecret.name) }}
- name: stolon-secret-{{ .Values.superuserSecret.name }}
secret:
secretName: {{ .Values.superuserSecret.name }}
{{ end }}
{{ if and (not (empty .Values.replicationSecret.name)) (not (eq .Values.superuserSecret.name .Values.replicationSecret.name)) }}
- name: stolon-secret-{{ .Values.replicationSecret.name }}
secret:
secretName: {{ .Values.replicationSecret.name }}
{{ end }}
{{- range $key, $value := .Values.keeper.volumes }}
- name: {{ $key }}
{{ toYaml $value | indent 10 }}
+18
View File
@@ -0,0 +1,18 @@
{{- if .Values.proxy.podDisruptionBudget }}
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: {{ template "stolon.fullname" . }}-proxy
labels:
app: {{ template "stolon.name" . }}
chart: {{ template "stolon.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
selector:
matchLabels:
app: {{ template "stolon.name" . }}
release: {{ .Release.Name }}
component: stolon-proxy
{{ toYaml .Values.proxy.podDisruptionBudget | indent 2 }}
{{- end }}
+1
View File
@@ -15,6 +15,7 @@ rules:
- pods
- endpoints
- configmaps
- events
verbs:
- "*"
{{- end -}}
+8 -2
View File
@@ -1,3 +1,4 @@
{{ if or (empty .Values.superuserSecret.name) (empty .Values.replicationSecret.name) }}
apiVersion: v1
kind: Secret
metadata:
@@ -9,5 +10,10 @@ metadata:
heritage: {{ .Release.Service }}
type: Opaque
data:
pg_su_password: {{ required "A valid .Values.superuserPassword entry is required!" .Values.superuserPassword | b64enc | quote }}
pg_repl_password: {{ required "A valid .Values.replicationPassword entry is required!" .Values.replicationPassword | b64enc | quote }}
{{ if empty .Values.superuserSecret.name }}
pg_su_password: {{ required "A valid .Values.superuserPassword entry is required!" .Values.superuserPassword | b64enc | quote }}
{{ end }}
{{ if empty .Values.replicationSecret.name }}
pg_repl_password: {{ required "A valid .Values.replicationPassword entry is required!" .Values.replicationPassword | b64enc | quote }}
{{ end }}
{{ end }}
@@ -22,6 +22,7 @@ spec:
component: stolon-sentinel
stolon-cluster: {{ template "stolon.fullname" . }}
annotations:
checksum/config: {{ include (print .Template.BasePath "/hooks/update-cluster-spec-job.yaml") . | sha256sum }}
{{- with .Values.keeper.annotations }}
{{ toYaml . | indent 8 }}
{{- end }}
+18
View File
@@ -0,0 +1,18 @@
{{- if .Values.sentinel.podDisruptionBudget }}
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: {{ template "stolon.fullname" . }}-sentinel
labels:
app: {{ template "stolon.name" . }}
chart: {{ template "stolon.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
selector:
matchLabels:
app: {{ template "stolon.name" . }}
release: {{ .Release.Name }}
component: stolon-sentinel
{{ toYaml .Values.sentinel.podDisruptionBudget | indent 2 }}
{{- end }}
+31 -5
View File
@@ -1,6 +1,6 @@
image:
repository: sorintlab/stolon
tag: v0.12.0-pg10
tag: v0.13.0-pg10
pullPolicy: IfNotPresent
# used by create-cluster-job when store.backend is etcd
@@ -32,14 +32,22 @@ serviceAccount:
# The name of the ServiceAccount to use. If not set and create is true, a name is generated using the fullname template
name:
superuserUsername: "stolon"
superuserSecret:
name: ""
usernameKey: pg_su_username
passwordKey: pg_su_password
## password for the superuser (REQUIRED)
replicationSecret:
name: ""
usernameKey: pg_repl_username
passwordKey: pg_repl_password
superuserUsername: "stolon"
## password for the superuser (REQUIRED if superuserSecret is not set)
superuserPassword:
replicationUsername: "repluser"
## password for the replication user (REQUIRED)
## password for the replication user (REQUIRED if replicationSecret is not set)
replicationPassword:
## backend could be one of the following: consul, etcdv2, etcdv3 or kubernetes
@@ -49,6 +57,7 @@ store:
kubeResourceKind: configmap
pgParameters: {}
# maxConnections: 1000
ports:
stolon:
@@ -58,6 +67,11 @@ ports:
job:
autoCreateCluster: true
autoUpdateClusterSpec: true
clusterSpec: {}
# sleepInterval: 1s
# maxStandbys: 5
keeper:
replicaCount: 2
@@ -77,6 +91,12 @@ keeper:
tolerations: []
volumes: []
volumeMounts: []
hooks:
failKeeper:
enabled: false
podDisruptionBudget:
# minAvailable: 1
# maxUnavailable: 1
proxy:
replicaCount: 2
@@ -95,6 +115,9 @@ proxy:
nodeSelector: {}
affinity: {}
tolerations: []
podDisruptionBudget:
# minAvailable: 1
# maxUnavailable: 1
sentinel:
replicaCount: 2
@@ -104,3 +127,6 @@ sentinel:
nodeSelector: {}
affinity: {}
tolerations: []
podDisruptionBudget:
# minAvailable: 1
# maxUnavailable: 1