[incubator/couchdb] Add a chart for Apache CouchDB (#2964)

* Add scaffolding for CouchDB chart

* Move to a StatefulSet rather than a Deployment

* Configure a headless service

This configuration is necessary to ensure that the individual Pods
have well-defined hostnames that are discoverable via DNS.

* Make the minimal set of changes for a StatefulSet

Lots of these changes need to be properly parameterized, but first
let's get the basics working.

* Fix the default variables for a 3 node cluster

* Support generic Erlang VM flags as Values

* Specify the location of sidecar image in values

* Allow to override the podManagementPolicy

* Ensure exact match between labels and selector

* Simplify headless service definition

The current version of the sidecar container expects the headless
service to export SRV records for a _couchdb service, so there's no
point in parameterizing the name here. In theory the port could be
omitted, but there are a few unresolved issues with that configuration
as of v1.7 so we just put in the stock CouchDB port number.

* Do not pretend that we configure the CouchDB port

In a future version we may be able to pass the port configuration to
CouchDB using a ConfigMap, but for now the port on which the Pods are
listening for HTTP requests is fixed.

* Enable configuration of PVCs for the statefulset

* Choose a more user-friendly name for spec.replicas

Users familiar with CouchDB may wonder if "replicaCount" refers to the
desired number of replicas per document, as opposed to the size of the
cluster.

* Clean up comments, improve example resource limit

* Improve the Service / Ingress configuration

This re-adds a traditional Service alongside the headless one so
clients have a stable way to access the CouchDB cluster. It also
refines some of the documentation around the Ingress resource.

* Update the chart metadata

* Add a basic README

* Make some small doc changes to align w/ standards

* Enable CouchDB configuration through ConfigMap

* Configure admin account, cookie auth using Secrets

* Tell the user to run finish_cluster after startup

* Tweak the notes to use interactive password prompt

* Fix the database-storage mount point

* Automatically create admin account secrets

This change enables the chart to be automically installed in a secure
fashion with a single command.

* Use GitHub username for maintainer name

* Target latest StatefulSet API

This also means we require Kube 1.8.
This commit is contained in:
Adam Kocoloski
2018-01-31 07:14:11 -08:00
committed by k8s-ci-robot
parent 5fa9ef5e2e
commit 960eb5a89f
12 changed files with 530 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
+13
View File
@@ -0,0 +1,13 @@
name: couchdb
version: 0.1.0
description: A database featuring seamless multi-master sync, that scales from
big data to mobile, with an intuitive HTTP/JSON API and designed for
reliability.
home: https://couchdb.apache.org/
sources:
- https://github.com/apache/couchdb
- https://github.com/kocolosk/couchdb-statefulset-assembler
maintainers:
- name: kocolosk
email: kocolosk@apache.org
icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg
+109
View File
@@ -0,0 +1,109 @@
# CouchDB
Apache CouchDB is a database featuring seamless multi-master sync, that scales
from big data to mobile, with an intuitive HTTP/JSON API and designed for
reliability.
This chart deploys a CouchDB cluster as a StatefulSet. It creates a ClusterIP
Service in front of the deployment for load balancing by default, but can also
be configured to deploy other Service types or an Ingress controller. The
default persistence mechanism is simply the ephemeral local filesystem, but
production deployments should set `persistentVolume.enabled` to `true` to attach
storage volumes to each Pod in the deployment.
## TL;DR
```bash
$ helm install incubator/couchdb --set allowAdminParty=true
```
## Prerequisites
- Kubernetes 1.8+ with Beta APIs enabled
## Installing the Chart
To install the chart with the release name `my-release`:
```bash
$ helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
$ helm install --name my-release incubator/couchdb
```
This will create a Secret containing the admin credentials for the cluster.
Those credentials can be retrieved as follows:
```bash
$ kubectl get secret my-release-couchdb -o go-template='{{ .data.adminPassword }}' | base64 --decode
```
If you prefer to configure the admin credentials directly you can create a
secret containing `adminUsername`, `adminPassword` and `cookieAuthSecret` keys:
```bash
$ kubectl create secret generic my-release-couchdb --from-literal=adminUsername=foo --from-literal=adminPassword=bar --from-literal=cookieAuthSecret=baz
```
and then install the chart while overriding the `createAdminSecret` setting:
```bash
$ helm install --name my-release --set createAdminSecret=false incubator/couchdb
```
This Helm chart deploys CouchDB on the Kubernetes cluster in a default
configuration. The [configuration](#configuration) section lists
the parameters that can be configured during installation.
> **Tip**: List all releases using `helm list`
## Uninstalling the Chart
To uninstall/delete the `my-release` deployment:
```bash
$ helm delete my-release
```
The command removes all the Kubernetes components associated with the chart and
deletes the release.
## Configuration
The following tables lists the most commonly configured parameters of the
CouchDB chart and their default values:
| Parameter | Description | Default |
|---------------------------------|-------------------------------------------------------|----------------------------------------|
| `clusterSize` | The initial number of nodes in the CouchDB cluster | 3 |
| `couchdbConfig` | Map allowing override elements of server .ini config | {} |
| `allowAdminParty` | If enabled, start cluster without admin account | false (requires creating a Secret) |
| `createAdminSecret` | If enabled, create an admin account and cookie secret | true |
| `erlangFlags` | Map of flags supplied to the underlying Erlang VM | name: couchdb, setcookie: monster
| `persistentVolume.enabled` | Boolean determining whether to attach a PV to each node | false
| `persistentVolume.size` | If enabled, the size of the persistent volume to attach | 10Gi
A variety of other parameters are also configurable. See the comments in the
`values.yaml` file for further details:
| Parameter | Default |
|---------------------------------|----------------------------------------|
| `adminUsername` | admin |
| `adminPassword` | auto-generated |
| `cookieAuthSecret | auto-generated |
| `helperImage.repository` | kocolosk/couchdb-statefulset-assembler |
| `helperImage.tag` | 0.1.0 |
| `helperImage.pullPolicy` | IfNotPresent |
| `image.repository` | couchdb |
| `image.tag` | 2.1.1 |
| `image.pullPolicy` | IfNotPresent |
| `ingress.enabled` | false |
| `ingress.hosts` | chart-example.local |
| `ingress.annotations` | |
| `ingress.tls` | |
| `persistentVolume.accessModes` | ReadWriteOnce |
| `persistentVolume.storageClass` | Default for the Kube cluster |
| `podManagementPolicy` | Parallel |
| `resources` | |
| `service.enabled` | true |
| `service.type` | ClusterIP |
| `service.externalPort` | 5984 |
+19
View File
@@ -0,0 +1,19 @@
Apache CouchDB is starting. Check the status of the Pods using:
kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "couchdb.name" . }},release={{ .Release.Name }}"
Once all of the Pods are fully Ready, execute the following command to create
some required system databases:
kubectl exec {{ if not .Values.allowAdminParty }}-it {{ end }}{{ template "couchdb.fullname" . }}-0 -c couchdb -- curl -s \
http://127.0.0.1:5984/_cluster_setup \
-X POST \
-H "Content-Type: application/json" \
{{- if .Values.allowAdminParty }}
-d '{"action": "finish_cluster"}'
{{- else }}
-d '{"action": "finish_cluster"}' \
-u <adminUsername>
{{- end }}
Then it's time to relax.
+36
View File
@@ -0,0 +1,36 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "couchdb.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "couchdb.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
In the event that we create both a headless service and a traditional one,
ensure that the latter gets a unique name.
*/}}
{{- define "couchdb.svcname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-svc-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a random string if the supplied key does not exist
*/}}
{{- define "couchdb.defaultsecret" -}}
{{- if . -}}
{{- . | b64enc | quote -}}
{{- else -}}
{{- randAlphaNum 10 | b64enc | quote -}}
{{- end -}}
{{- end -}}
@@ -0,0 +1,17 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "couchdb.fullname" . }}
labels:
app: {{ template "couchdb.name" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
heritage: {{ .Release.Service | quote }}
release: {{ .Release.Name | quote }}
data:
inifile: |
{{ range $section, $settings := .Values.couchdbConfig -}}
{{ printf "[%s]" $section }}
{{ range $key, $value := $settings -}}
{{ printf "%s = %s" $key ($value | toString) }}
{{ end }}
{{ end }}
+17
View File
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "couchdb.fullname" . }}
labels:
app: {{ template "couchdb.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
clusterIP: None
ports:
- name: couchdb
port: 5984
selector:
app: {{ template "couchdb.name" . }}
release: {{ .Release.Name }}
+32
View File
@@ -0,0 +1,32 @@
{{- if .Values.ingress.enabled -}}
{{- $serviceName := include "couchdb.fullname" . -}}
{{- $servicePort := .Values.service.externalPort -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "couchdb.fullname" . }}
labels:
app: {{ template "couchdb.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
{{- range $key, $value := .Values.ingress.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
rules:
{{- range $host := .Values.ingress.hosts }}
- host: {{ $host }}
http:
paths:
- path: /
backend:
serviceName: {{ $serviceName }}
servicePort: {{ $servicePort }}
{{- end -}}
{{- if .Values.ingress.tls }}
tls:
{{ toYaml .Values.ingress.tls | indent 4 }}
{{- end -}}
{{- end -}}
+16
View File
@@ -0,0 +1,16 @@
{{- if .Values.createAdminSecret -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "couchdb.fullname" . }}
labels:
app: {{ template "couchdb.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
type: Opaque
data:
adminUsername: {{ template "couchdb.defaultsecret" .Values.adminUsername }}
adminPassword: {{ template "couchdb.defaultsecret" .Values.adminPassword }}
cookieAuthSecret: {{ template "couchdb.defaultsecret" .Values.cookieAuthSecret }}
{{- end -}}
+20
View File
@@ -0,0 +1,20 @@
{{- if .Values.service.enabled -}}
apiVersion: v1
kind: Service
metadata:
name: {{ template "couchdb.svcname" . }}
labels:
app: {{ template "couchdb.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
ports:
- port: {{ .Values.service.externalPort }}
protocol: TCP
targetPort: 5984
type: {{ .Values.service.type }}
selector:
app: {{ template "couchdb.name" . }}
release: {{ .Release.Name }}
{{- end -}}
@@ -0,0 +1,123 @@
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: {{ template "couchdb.fullname" . }}
labels:
app: {{ template "couchdb.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.clusterSize }}
serviceName: {{ template "couchdb.fullname" . }}
podManagementPolicy: {{ .Values.podManagementPolicy }}
selector:
matchLabels:
app: {{ template "couchdb.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "couchdb.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: couchdb
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: couchdb
containerPort: 5984
- name: epmd
containerPort: 4369
- containerPort: 9100
env:
{{- if not .Values.allowAdminParty }}
- name: COUCHDB_USER
valueFrom:
secretKeyRef:
name: {{ template "couchdb.fullname" . }}
key: adminUsername
- name: COUCHDB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "couchdb.fullname" . }}
key: adminPassword
- name: COUCHDB_SECRET
valueFrom:
secretKeyRef:
name: {{ template "couchdb.fullname" . }}
key: cookieAuthSecret
{{- end }}
- name: ERL_FLAGS
value: "{{ range $k, $v := .Values.erlangFlags }} -{{ $k }} {{ $v }} {{ end }}"
livenessProbe:
httpGet:
path: /
port: 5984
readinessProbe:
httpGet:
path: /_up
port: 5984
resources:
{{ toYaml .Values.resources | indent 12 }}
volumeMounts:
- name: database-storage
mountPath: /opt/couchdb/data
- name: config
mountPath: /opt/couchdb/etc/default.d
- name: couchdb-statefulset-assembler
image: "{{ .Values.helperImage.repository }}:{{ .Values.helperImage.tag }}"
imagePullPolicy: {{ .Values.helperImage.pullPolicy }}
{{- if not .Values.allowAdminParty }}
env:
- name: COUCHDB_USER
valueFrom:
secretKeyRef:
name: {{ template "couchdb.fullname" . }}
key: adminUsername
- name: COUCHDB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "couchdb.fullname" . }}
key: adminPassword
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ template "couchdb.fullname" . }}
items:
- key: inifile
path: chart.ini
{{- if not .Values.persistentVolume.enabled }}
- name: database-storage
emptyDir: {}
{{- else }}
volumeClaimTemplates:
- metadata:
name: database-storage
labels:
app: {{ template "couchdb.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
accessModes:
{{- range .Values.persistentVolume.accessModes }}
- {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistentVolume.size | quote }}
{{- if .Values.persistentVolume.storageClass }}
{{- if (eq "-" .Values.persistentVolume.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistentVolume.storageClass }}"
{{- end }}
{{- end }}
{{- end }}
+107
View File
@@ -0,0 +1,107 @@
## clusterSize is the initial size of the CouchDB cluster.
clusterSize: 3
## If allowAdminParty is enabled the cluster will start up without any database
## administrator account; i.e., all users will be granted administrative
## access. Otherwise, the system will look for a Secret called
## <ReleaseName>-couchdb containing `adminUsername`, `adminPassword` and
## `cookieAuthSecret` keys. See the `createAdminSecret` flag.
## ref: https://kubernetes.io/docs/concepts/configuration/secret/
allowAdminParty: false
## If createAdminSecret is enabled a Secret called <ReleaseName>-couchdb will
## be created containing auto-generated credentials. Users who prefer to set
## these values themselves have a couple of options:
##
## 1) The `adminUsername`, `adminPassword`, and `cookieAuthSecret` can be
## defined directly in the chart's values. Note that all of a chart's values
## are currently stored in plaintext in a ConfigMap in the tiller namespace.
##
## 2) This flag can be disabled and a Secret with the required keys can be
## created ahead of time.
createAdminSecret: true
adminUsername: admin
# adminPassword: this_is_not_secure
# cookieAuthSecret: neither_is_this
## The storage volume used by each Pod in the StatefulSet. If a
## persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral
## local storage. Setting the storageClass attribute to "-" disables dynamic
## provisioning of Persistent Volumes; leaving it unset will invoke the default
## provisioner.
persistentVolume:
enabled: false
accessModes:
- ReadWriteOnce
size: 10Gi
# storageClass: "-"
## The CouchDB image
image:
repository: couchdb
tag: 2.1.1
pullPolicy: IfNotPresent
## Sidecar that connects the individual Pods into a cluster
helperImage:
repository: kocolosk/couchdb-statefulset-assembler
tag: 1.1.0
pullPolicy: IfNotPresent
## CouchDB is happy to spin up cluster nodes in parallel, but if you encounter
## problems you can try setting podManagementPolicy to the StatefulSet default
## `OrderedReady`
podManagementPolicy: Parallel
## A StatefulSet requires a headless Service to establish the stable network
## identities of the Pods, and that Service is created automatically by this
## chart without any additional configuration. The Service block below refers
## to a second Service that governs how clients connect to the CouchDB cluster.
service:
enabled: true
type: ClusterIP
externalPort: 5984
## An Ingress resource can provide name-based virtual hosting and TLS
## termination among other things for CouchDB deployments which are accessed
## from outside the Kubernetes cluster.
## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/
ingress:
enabled: false
hosts:
- chart-example.local
annotations:
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
tls:
# Secrets must be manually created in the namespace.
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
## Optional resource requests and limits for the CouchDB container
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
resources: {}
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 56
# memory: 256Gi
## erlangFlags is a map that is passed to the Erlang VM as flags using the
## ERL_FLAGS env. `name` and `setcookie` flags are minimally required to
## establish connectivity between cluster nodes.
## ref: http://erlang.org/doc/man/erl.html#init_flags
erlangFlags:
name: couchdb
setcookie: monster
## couchdbConfig will override default CouchDB configuration settings.
## The contents of this map are reformatted into a .ini file laid down
## by a ConfigMap object.
## ref: http://docs.couchdb.org/en/latest/config/index.html
couchdbConfig:
# cluster:
# q: 8 # Create 8 shards for each database