Unverified Commit 244d1087 by Denise Committed by GitHub

Merge pull request #216 from guangbochen/mongo2.3

Bump mongodb-replicaset chart to 3.9.6
parents 820b5eda 2578effd
# 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
install
apiVersion: v1
name: mongodb-replicaset
home: https://github.com/mongodb/mongo
version: 3.9.6
appVersion: 3.6
description: NoSQL document-oriented database that stores JSON-like documents with
dynamic schemas, simplifying the integration of data in content-driven applications.
icon: file://../mongo-logo.jpg
sources:
- https://github.com/mongodb/mongo
- https://github.com/percona/mongodb_exporter
maintainers:
- name: unguiculus
email: unguiculus@gmail.com
- name: steven-sheehy
email: ssheehy@firescope.com
approvers:
- unguiculus
- steven-sheehy
reviewers:
- unguiculus
- steven-sheehy
## Configuration
The following table lists the configurable parameters of the mongodb chart and their default values.
| Parameter | Description | Default |
| ----------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------- |
| `replicas` | Number of replicas in the replica set | `3` |
| `replicaSetName` | The name of the replica set | `rs0` |
| `podDisruptionBudget` | Pod disruption budget | `{}` |
| `port` | MongoDB port | `27017` |
| `imagePullSecrets` | Image pull secrets | `[]` |
| `installImage.repository` | Image name for the install container | `unguiculus/mongodb-install` |
| `installImage.tag` | Image tag for the install container | `0.7` |
| `installImage.pullPolicy` | Image pull policy for the init container that establishes the replica set | `IfNotPresent` |
| `copyConfigImage.repository` | Image name for the copy config init container | `busybox` |
| `copyConfigImage.tag` | Image tag for the copy config init container | `1.29.3` |
| `copyConfigImage.pullPolicy` | Image pull policy for the copy config init container | `IfNotPresent` |
| `image.repository` | MongoDB image name | `mongo` |
| `image.tag` | MongoDB image tag | `3.6` |
| `image.pullPolicy` | MongoDB image pull policy | `IfNotPresent` |
| `podAnnotations` | Annotations to be added to MongoDB pods | `{}` |
| `securityContext.enabled` | Enable security context | `true` |
| `securityContext.fsGroup` | Group ID for the container | `999` |
| `securityContext.runAsUser` | User ID for the container | `999` |
| `securityContext.runAsNonRoot` | | `true` |
| `resources` | Pod resource requests and limits | `{}` |
| `persistentVolume.enabled` | If `true`, persistent volume claims are created | `true` |
| `persistentVolume.storageClass` | Persistent volume storage class | `` |
| `persistentVolume.accessModes` | Persistent volume access modes | `[ReadWriteOnce]` |
| `persistentVolume.size` | Persistent volume size | `10Gi` |
| `persistentVolume.annotations` | Persistent volume annotations | `{}` |
| `terminationGracePeriodSeconds` | Duration in seconds the pod needs to terminate gracefully | `30` |
| `tls.enabled` | Enable MongoDB TLS support including authentication | `false` |
| `tls.cacert` | The CA certificate used for the members | Our self signed CA certificate |
| `tls.cakey` | The CA key used for the members | Our key for the self signed CA certificate |
| `init.resources` | Pod resource requests and limits (for init containers) | `{}` |
| `init.timeout` | The amount of time in seconds to wait for bootstrap to finish | `900` |
| `metrics.enabled` | Enable Prometheus compatible metrics for pods and replicasets | `false` |
| `metrics.image.repository` | Image name for metrics exporter | `ssalaues/mongodb-exporter` |
| `metrics.image.tag` | Image tag for metrics exporter | `0.6.1` |
| `metrics.image.pullPolicy` | Image pull policy for metrics exporter | `IfNotPresent` |
| `metrics.port` | Port for metrics exporter | `9216` |
| `metrics.path` | URL Path to expose metics | `/metrics` |
| `metrics.resources` | Metrics pod resource requests and limits | `{}` |
| `metrics.socketTimeout` | Time to wait for a non-responding socket | `3s` |
| `metrics.syncTimeout` | Time an operation with this session will wait before returning an error | `1m` |
| `metrics.prometheusServiceDiscovery`| Adds annotations for Prometheus ServiceDiscovery | `true` |
| `auth.enabled` | If `true`, keyfile access control is enabled | `false` |
| `auth.key` | Key for internal authentication | `` |
| `auth.existingKeySecret` | If set, an existing secret with this name for the key is used | `` |
| `auth.adminUser` | MongoDB admin user | `` |
| `auth.adminPassword` | MongoDB admin password | `` |
| `auth.metricsUser` | MongoDB clusterMonitor user | `` |
| `auth.metricsPassword` | MongoDB clusterMonitor password | `` |
| `auth.existingMetricsSecret` | If set, and existing secret with this name is used for the metrics user | `` |
| `auth.existingAdminSecret` | If set, and existing secret with this name is used for the admin user | `` |
| `serviceAnnotations` | Annotations to be added to the service | `{}` |
| `configmap` | Content of the MongoDB config file | `` |
| `initMongodStandalone` | If set, initContainer executes script in standalone mode | `` |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `affinity` | Node/pod affinities | `{}` |
| `tolerations` | List of node taints to tolerate | `[]` |
| `priorityClassName` | Pod priority class name | `` |
| `livenessProbe.failureThreshold` | Liveness probe failure threshold | `3` |
| `livenessProbe.initialDelaySeconds` | Liveness probe initial delay seconds | `30` |
| `livenessProbe.periodSeconds` | Liveness probe period seconds | `10` |
| `livenessProbe.successThreshold` | Liveness probe success threshold | `1` |
| `livenessProbe.timeoutSeconds` | Liveness probe timeout seconds | `5` |
| `readinessProbe.failureThreshold` | Readiness probe failure threshold | `3` |
| `readinessProbe.initialDelaySeconds`| Readiness probe initial delay seconds | `5` |
| `readinessProbe.periodSeconds` | Readiness probe period seconds | `10` |
| `readinessProbe.successThreshold` | Readiness probe success threshold | `1` |
| `readinessProbe.timeoutSeconds` | Readiness probe timeout seconds | `1` |
| `extraVars` | Set environment variables for the main container | `{}` |
| `extraLabels` | Additional labels to add to resources | `{}` |
*MongoDB config file*
All options that depended on the chart configuration are supplied as command-line arguments to `mongod`. By default, the chart creates an empty config file. Entries may be added via the `configmap` configuration value.
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
``` console
helm install --name my-release -f values.yaml stable/mongodb-replicaset
```
> **Tip**: You can use the default [values.yaml](values.yaml)
Once you have all 3 nodes in running, you can run the "test.sh" script in this directory, which will insert a key into the primary and check the secondaries for output. This script requires that the `$RELEASE_NAME` environment variable be set, in order to access the pods.
## Authentication
By default, this chart creates a MongoDB replica set without authentication. Authentication can be
enabled using the parameter `auth.enabled`. Once enabled, keyfile access control is set up and an
admin user with root privileges is created. User credentials and keyfile may be specified directly.
Alternatively, existing secrets may be provided. The secret for the admin user must contain the
keys `user` and `password`, that for the key file must contain `key.txt`. The user is created with
full `root` permissions but is restricted to the `admin` database for security purposes. It can be
used to create additional users with more specific permissions.
To connect to the mongo shell with authentication enabled, use a command similar to the following (substituting values as appropriate):
```shell
kubectl exec -it mongodb-replicaset-0 -- mongo mydb -u admin -p password --authenticationDatabase admin
```
## TLS support
To enable full TLS encryption set `tls.enabled` to `true`. It is recommended to create your own CA by executing:
```console
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 10000 -out ca.crt -subj "/CN=mydomain.com"
```
After that paste the base64 encoded (`cat ca.key | base64 -w0`) cert and key into the fields `tls.cacert` and
`tls.cakey`. Adapt the configmap for the replicaset as follows:
```yml
configmap:
storage:
dbPath: /data/db
net:
port: 27017
ssl:
mode: requireSSL
CAFile: /data/configdb/tls.crt
PEMKeyFile: /work-dir/mongo.pem
# Set to false to require mutual TLS encryption
allowConnectionsWithoutCertificates: true
replication:
replSetName: rs0
security:
authorization: enabled
# # Uncomment to enable mutual TLS encryption
# clusterAuthMode: x509
keyFile: /keydir/key.txt
```
To access the cluster you need one of the certificates generated during cluster setup in `/work-dir/mongo.pem` of the
certain container or you generate your own one via:
```console
$ cat >openssl.cnf <<EOL
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $HOSTNAME1
DNS.1 = $HOSTNAME2
EOL
$ openssl genrsa -out mongo.key 2048
$ openssl req -new -key mongo.key -out mongo.csr -subj "/CN=$HOSTNAME" -config openssl.cnf
$ openssl x509 -req -in mongo.csr \
-CA $MONGOCACRT -CAkey $MONGOCAKEY -CAcreateserial \
-out mongo.crt -days 3650 -extensions v3_req -extfile openssl.cnf
$ rm mongo.csr
$ cat mongo.crt mongo.key > mongo.pem
$ rm mongo.key mongo.crt
```
Please ensure that you exchange the `$HOSTNAME` with your actual hostname and the `$HOSTNAME1`, `$HOSTNAME2`, etc. with
alternative hostnames you want to allow access to the MongoDB replicaset. You should now be able to authenticate to the
mongodb with your `mongo.pem` certificate:
```console
mongo --ssl --sslCAFile=ca.crt --sslPEMKeyFile=mongo.pem --eval "db.adminCommand('ping')"
```
## Promethus metrics
Enabling the metrics as follows will allow for each replicaset pod to export Prometheus compatible metrics
on server status, individual replicaset information, replication oplogs, and storage engine.
```yaml
metrics:
enabled: true
image:
repository: ssalaues/mongodb-exporter
tag: 0.6.1
pullPolicy: IfNotPresent
port: 9216
path: "/metrics"
socketTimeout: 3s
syncTimeout: 1m
prometheusServiceDiscovery: true
resources: {}
```
More information on [MongoDB Exporter](https://github.com/percona/mongodb_exporter) metrics available.
## Deep dive
Because the pod names are dependent on the name chosen for it, the following examples use the
environment variable `RELEASENAME`. For example, if the helm release name is `messy-hydra`, one would need to set the following before proceeding. The example scripts below assume 3 pods only.
```console
export RELEASE_NAME=messy-hydra
```
### Cluster Health
```console
for i in 0 1 2; do kubectl exec $RELEASE_NAME-mongodb-replicaset-$i -- sh -c 'mongo --eval="printjson(db.serverStatus())"'; done
```
### Failover
One can check the roles being played by each node by using the following:
```console
$ for i in 0 1 2; do kubectl exec $RELEASE_NAME-mongodb-replicaset-$i -- sh -c 'mongo --eval="printjson(rs.isMaster())"'; done
MongoDB shell version: 3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
{
"hosts" : [
"messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
"messy-hydra-mongodb-1.messy-hydra-mongodb.default.svc.cluster.local:27017",
"messy-hydra-mongodb-2.messy-hydra-mongodb.default.svc.cluster.local:27017"
],
"setName" : "rs0",
"setVersion" : 3,
"ismaster" : true,
"secondary" : false,
"primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
"me" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
"electionId" : ObjectId("7fffffff0000000000000001"),
"maxBsonObjectSize" : 16777216,
"maxMessageSizeBytes" : 48000000,
"maxWriteBatchSize" : 1000,
"localTime" : ISODate("2016-09-13T01:10:12.680Z"),
"maxWireVersion" : 4,
"minWireVersion" : 0,
"ok" : 1
}
```
This lets us see which member is primary.
Let us now test persistence and failover. First, we insert a key (in the below example, we assume pod 0 is the master):
```console
$ kubectl exec $RELEASE_NAME-mongodb-replicaset-0 -- mongo --eval="printjson(db.test.insert({key1: 'value1'}))"
MongoDB shell version: 3.6.3
connecting to: mongodb://127.0.0.1:27017
{ "nInserted" : 1 }
```
Watch existing members:
```console
$ kubectl run --attach bbox --image=mongo:3.6 --restart=Never --env="RELEASE_NAME=$RELEASE_NAME" -- sh -c 'while true; do for i in 0 1 2; do echo $RELEASE_NAME-mongodb-replicaset-$i $(mongo --host=$RELEASE_NAME-mongodb-replicaset-$i.$RELEASE_NAME-mongodb-replicaset --eval="printjson(rs.isMaster())" | grep primary); sleep 1; done; done';
Waiting for pod default/bbox2 to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
messy-hydra-mongodb-2 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
messy-hydra-mongodb-0 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
messy-hydra-mongodb-1 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
messy-hydra-mongodb-2 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
messy-hydra-mongodb-0 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
```
Kill the primary and watch as a new master getting elected.
```console
$ kubectl delete pod $RELEASE_NAME-mongodb-replicaset-0
pod "messy-hydra-mongodb-0" deleted
```
Delete all pods and let the statefulset controller bring it up.
```console
$ kubectl delete po -l "app=mongodb-replicaset,release=$RELEASE_NAME"
$ kubectl get po --watch-only
NAME READY STATUS RESTARTS AGE
messy-hydra-mongodb-0 0/1 Pending 0 0s
messy-hydra-mongodb-0 0/1 Pending 0 0s
messy-hydra-mongodb-0 0/1 Pending 0 7s
messy-hydra-mongodb-0 0/1 Init:0/2 0 7s
messy-hydra-mongodb-0 0/1 Init:1/2 0 27s
messy-hydra-mongodb-0 0/1 Init:1/2 0 28s
messy-hydra-mongodb-0 0/1 PodInitializing 0 31s
messy-hydra-mongodb-0 0/1 Running 0 32s
messy-hydra-mongodb-0 1/1 Running 0 37s
messy-hydra-mongodb-1 0/1 Pending 0 0s
messy-hydra-mongodb-1 0/1 Pending 0 0s
messy-hydra-mongodb-1 0/1 Init:0/2 0 0s
messy-hydra-mongodb-1 0/1 Init:1/2 0 20s
messy-hydra-mongodb-1 0/1 Init:1/2 0 21s
messy-hydra-mongodb-1 0/1 PodInitializing 0 24s
messy-hydra-mongodb-1 0/1 Running 0 25s
messy-hydra-mongodb-1 1/1 Running 0 30s
messy-hydra-mongodb-2 0/1 Pending 0 0s
messy-hydra-mongodb-2 0/1 Pending 0 0s
messy-hydra-mongodb-2 0/1 Init:0/2 0 0s
messy-hydra-mongodb-2 0/1 Init:1/2 0 21s
messy-hydra-mongodb-2 0/1 Init:1/2 0 22s
messy-hydra-mongodb-2 0/1 PodInitializing 0 25s
messy-hydra-mongodb-2 0/1 Running 0 26s
messy-hydra-mongodb-2 1/1 Running 0 30s
...
messy-hydra-mongodb-0 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
messy-hydra-mongodb-1 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
messy-hydra-mongodb-2 "primary" : "messy-hydra-mongodb-0.messy-hydra-mongodb.default.svc.cluster.local:27017",
```
Check the previously inserted key:
```console
$ kubectl exec $RELEASE_NAME-mongodb-replicaset-1 -- mongo --eval="rs.slaveOk(); db.test.find({key1:{\$exists:true}}).forEach(printjson)"
MongoDB shell version: 3.6.3
connecting to: mongodb://127.0.0.1:27017
{ "_id" : ObjectId("57b180b1a7311d08f2bfb617"), "key1" : "value1" }
```
### Scaling
Scaling should be managed by `helm upgrade`, which is the recommended way.
### Indexes and Maintenance
You can run Mongo in standalone mode and execute Javascript code on each replica at initContainer time using `initMongodStandalone`.
This allows you to create indexes on replicasets following [best practices](https://docs.mongodb.com/manual/tutorial/build-indexes-on-replica-sets/).
#### Example: Creating Indexes
```js
initMongodStandalone: |+
db = db.getSiblingDB("mydb")
db.my_users.createIndex({email: 1})
```
Tail the logs to debug running indexes or to follow their progress
```sh
kubectl exec -it $RELEASE-mongodb-replicaset-0 -c bootstrap -- tail -f /work-dir/log.txt
```
# MongoDB Helm Chart
## Prerequisites Details
* Kubernetes 1.9+
* Kubernetes beta APIs enabled only if `podDisruptionBudget` is enabled
* PV support on the underlying infrastructure
## Chart Details
This chart implements a dynamically scalable [MongoDB replica set](https://docs.mongodb.com/manual/tutorial/deploy-replica-set/)
using Kubernetes StatefulSets and Init Containers.
\ No newline at end of file
#!/usr/bin/env bash
# Copyright 2018 The Kubernetes Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e pipefail
port=27017
replica_set="$REPLICA_SET"
script_name=${0##*/}
SECONDS=0
timeout="${TIMEOUT:-900}"
if [[ "$AUTH" == "true" ]]; then
admin_user="$ADMIN_USER"
admin_password="$ADMIN_PASSWORD"
admin_creds=(-u "$admin_user" -p "$admin_password")
if [[ "$METRICS" == "true" ]]; then
metrics_user="$METRICS_USER"
metrics_password="$METRICS_PASSWORD"
fi
auth_args=("--auth" "--keyFile=/data/configdb/key.txt")
fi
log() {
local msg="$1"
local timestamp
timestamp=$(date --iso-8601=ns)
echo "[$timestamp] [$script_name] $msg" 2>&1 | tee -a /work-dir/log.txt 1>&2
}
retry_until() {
local host="${1}"
local command="${2}"
local expected="${3}"
local creds=("${admin_creds[@]}")
# Don't need credentials for admin user creation and pings that run on localhost
if [[ "${host}" =~ ^localhost ]]; then
creds=()
fi
until [[ $(mongo admin --host "${host}" "${creds[@]}" "${ssl_args[@]}" --quiet --eval "${command}") == "${expected}" ]]; do
sleep 1
if (! ps "${pid}" &>/dev/null); then
log "mongod shutdown unexpectedly"
exit 1
fi
if [[ "${SECONDS}" -ge "${timeout}" ]]; then
log "Timed out after ${timeout}s attempting to bootstrap mongod"
exit 1
fi
log "Retrying ${command} on ${host}"
done
}
shutdown_mongo() {
local host="${1:-localhost}"
local args='force: true'
log "Shutting down MongoDB ($args)..."
if (! mongo admin --host "${host}" "${admin_creds[@]}" "${ssl_args[@]}" --eval "db.shutdownServer({$args})"); then
log "db.shutdownServer() failed, sending the terminate signal"
kill -TERM "${pid}"
fi
}
init_mongod_standalone() {
if [[ ! -f /init/initMongodStandalone.js ]]; then
log "Skipping init mongod standalone script"
return 0
elif [[ -z "$(ls -1A /data/db)" ]]; then
log "mongod standalone script currently not supported on initial install"
return 0
fi
local port="27018"
log "Starting a MongoDB instance as standalone..."
mongod --config /data/configdb/mongod.conf --dbpath=/data/db "${auth_args[@]}" --port "${port}" --bind_ip=0.0.0.0 2>&1 | tee -a /work-dir/log.txt 1>&2 &
export pid=$!
trap shutdown_mongo EXIT
log "Waiting for MongoDB to be ready..."
retry_until "localhost:${port}" "db.adminCommand('ping').ok" "1"
log "Running init js script on standalone mongod"
mongo admin --port "${port}" "${admin_creds[@]}" "${ssl_args[@]}" /init/initMongodStandalone.js
shutdown_mongo "localhost:${port}"
}
my_hostname=$(hostname)
log "Bootstrapping MongoDB replica set member: $my_hostname"
log "Reading standard input..."
while read -ra line; do
if [[ "${line}" == *"${my_hostname}"* ]]; then
service_name="$line"
fi
peers=("${peers[@]}" "$line")
done
# Generate the ca cert
ca_crt=/data/configdb/tls.crt
if [ -f "$ca_crt" ]; then
log "Generating certificate"
ca_key=/data/configdb/tls.key
pem=/work-dir/mongo.pem
ssl_args=(--ssl --sslCAFile "$ca_crt" --sslPEMKeyFile "$pem")
# Move into /work-dir
pushd /work-dir
cat >openssl.cnf <<EOL
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $(echo -n "$my_hostname" | sed s/-[0-9]*$//)
DNS.2 = $my_hostname
DNS.3 = $service_name
DNS.4 = localhost
DNS.5 = 127.0.0.1
EOL
# Generate the certs
openssl genrsa -out mongo.key 2048
openssl req -new -key mongo.key -out mongo.csr -subj "/OU=MongoDB/CN=$my_hostname" -config openssl.cnf
openssl x509 -req -in mongo.csr \
-CA "$ca_crt" -CAkey "$ca_key" -CAcreateserial \
-out mongo.crt -days 3650 -extensions v3_req -extfile openssl.cnf
rm mongo.csr
cat mongo.crt mongo.key > $pem
rm mongo.key mongo.crt
fi
init_mongod_standalone
log "Peers: ${peers[*]}"
log "Starting a MongoDB replica"
mongod --config /data/configdb/mongod.conf --dbpath=/data/db --replSet="$replica_set" --port="${port}" "${auth_args[@]}" --bind_ip=0.0.0.0 2>&1 | tee -a /work-dir/log.txt 1>&2 &
pid=$!
trap shutdown_mongo EXIT
log "Waiting for MongoDB to be ready..."
retry_until "localhost" "db.adminCommand('ping').ok" "1"
log "Initialized."
# try to find a master
for peer in "${peers[@]}"; do
log "Checking if ${peer} is primary"
# Check rs.status() first since it could be in primary catch up mode which db.isMaster() doesn't show
if [[ $(mongo admin --host "${peer}" "${admin_creds[@]}" "${ssl_args[@]}" --quiet --eval "rs.status().myState") == "1" ]]; then
retry_until "${peer}" "db.isMaster().ismaster" "true"
log "Found primary: ${peer}"
primary="${peer}"
break
fi
done
if [[ "${primary}" = "${service_name}" ]]; then
log "This replica is already PRIMARY"
elif [[ -n "${primary}" ]]; then
if [[ $(mongo admin --host "${primary}" "${admin_creds[@]}" "${ssl_args[@]}" --quiet --eval "rs.conf().members.findIndex(m => m.host == '${service_name}:${port}')") == "-1" ]]; then
log "Adding myself (${service_name}) to replica set..."
if (mongo admin --host "${primary}" "${admin_creds[@]}" "${ssl_args[@]}" --eval "rs.add('${service_name}')" | grep 'Quorum check failed'); then
log 'Quorum check failed, unable to join replicaset. Exiting prematurely.'
exit 1
fi
fi
sleep 3
log 'Waiting for replica to reach SECONDARY state...'
retry_until "${service_name}" "rs.status().myState" "2"
log '✓ Replica reached SECONDARY state.'
elif (mongo "${ssl_args[@]}" --eval "rs.status()" | grep "no replset config has been received"); then
log "Initiating a new replica set with myself ($service_name)..."
mongo "${ssl_args[@]}" --eval "rs.initiate({'_id': '$replica_set', 'members': [{'_id': 0, 'host': '$service_name'}]})"
sleep 3
log 'Waiting for replica to reach PRIMARY state...'
retry_until "localhost" "db.isMaster().ismaster" "true"
primary="${service_name}"
log '✓ Replica reached PRIMARY state.'
if [[ "${AUTH}" == "true" ]]; then
log "Creating admin user..."
mongo admin "${ssl_args[@]}" --eval "db.createUser({user: '${admin_user}', pwd: '${admin_password}', roles: [{role: 'root', db: 'admin'}]})"
fi
fi
# User creation
if [[ -n "${primary}" && "$AUTH" == "true" && "$METRICS" == "true" ]]; then
metric_user_count=$(mongo admin --host "${primary}" "${admin_creds[@]}" "${ssl_args[@]}" --eval "db.system.users.find({user: '${metrics_user}'}).count()" --quiet)
if [[ "${metric_user_count}" == "0" ]]; then
log "Creating clusterMonitor user..."
mongo admin --host "${primary}" "${admin_creds[@]}" "${ssl_args[@]}" --eval "db.createUser({user: '${metrics_user}', pwd: '${metrics_password}', roles: [{role: 'clusterMonitor', db: 'admin'}, {role: 'read', db: 'local'}]})"
fi
fi
log "MongoDB bootstrap complete"
exit 0
# Copyright 2016 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM alpine:3.8
LABEL maintainer="Reinhard Nägele <unguiculus@gmail.com>"
RUN apk update && apk add bash openssl && wget -qO /peer-finder http://storage.googleapis.com/kubernetes-release/pets/peer-finder
ENTRYPOINT ["/install.sh"]
COPY install.sh /
RUN chmod -c 755 /install.sh /peer-finder
# Copyright 2016 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
all: push
TAG = 0.7
PREFIX = unguiculus/mongodb-install
build:
docker build -t $(PREFIX):$(TAG) .
push: build
docker push $(PREFIX):$(TAG)
clean:
docker rmi $(PREFIX):$(TAG)
#!/usr/bin/env bash
# Copyright 2016 The Kubernetes Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This volume is assumed to exist and is shared with the peer-finder
# init container. It contains on-start/change configuration scripts.
WORKDIR_VOLUME="/work-dir"
for i in "$@"; do
case "$i" in
-w=*|--work-dir=*)
WORKDIR_VOLUME="${i#*=}"
shift
;;
*)
# unknown option
;;
esac
done
echo Installing config scripts into "${WORKDIR_VOLUME}"
mkdir -p "${WORKDIR_VOLUME}"
cp /peer-finder "${WORKDIR_VOLUME}"/
categories:
- Database
- NoSQL
labels:
io.cattle.role: project # options are cluster/project
questions:
- variable: defaultImage
default: true
description: "Use default Docker images"
label: Use Default Images
type: boolean
show_subquestion_if: false
group: "Container Images"
subquestions:
- variable: image.repository
default: "ranchercharts/mongo"
description: "MongoDB image name"
type: string
label: MongoDB Image Name
- variable: image.tag
default: "3.6"
description: "MongoDB image tag"
type: string
label: Image Tag
- variable: installImage.repository
default: "ranchercharts/unguiculus-mongodb-install"
description: "Image name for the install container"
type: string
label: Init-Container Image Name
- variable: installImage.tag
default: "0.7"
description: "Image tag for the install container"
type: string
label: Init-Container Image Tag
- variable: copyConfigImage.repository
default: "ranchercharts/busybox"
description: "Image name for the CopyConfig container"
type: string
label: Copy Config Image Name
- variable: copyConfigImage.tag
default: "1.29.3"
description: "Image tag for the CopyConfig container"
type: string
label: Copy Config Image Tag
- variable: replicas
default: 3
description: "Number of replicas in the replica set"
type: int
min: 3
max: 12
label: Number of Replicas
required: true
group: "MongoDB Settings"
- variable: port
default: "27017"
description: "MongoDB port"
type: string
label: MongoDB port
required: true
group: "MongoDB Settings"
- variable: replicaSetName
default: "rs0"
description: "The name of the replica set"
type: string
label: ReplicaSet Name
required: true
group: "MongoDB Settings"
- variable: auth.enabled
default: true
description: "If true, keyfile access control is enabled"
type: boolean
label: Enable Auth
required: true
show_subquestion_if: true
group: "MongoDB Settings"
subquestions:
- variable: auth.key
default: "myauthkey"
description: "Key for internal authentication, https://docs.mongodb.com/v3.0/tutorial/enable-internal-authentication/"
type: string
label: Auth Key
required: true
- variable: auth.adminUser
default: "admin"
description: "MongoDB admin user"
type: string
label: MongoDB Admin User
required: true
- variable: auth.adminPassword
default: ""
description: "MongoDB admin password"
type: password
label: MongoDB Admin Password
required: true
- variable: persistentVolume.enabled
default: false
description: "Enable persistent volume for MongoDB"
type: boolean
required: true
label: MongoDB Persistent Volume Enabled
show_subquestion_if: true
group: "Persistent Volume"
subquestions:
- variable: persistentVolume.size
default: "10Gi"
description: "MongoDB Persistent Volume Size"
type: string
label: MongoDB Volume Size
- variable: persistentVolume.storageClass
default: ""
description: "If unndefined or null, uses the default StorageClass. Defaults to null."
type: storageclass
label: Storage Class for MongoDB
1. After the statefulset is created completely, one can check which instance is primary by running:
$ for ((i = 0; i < {{ .Values.replicas }}; ++i)); do kubectl exec --namespace {{ .Release.Namespace }} {{ template "mongodb-replicaset.fullname" . }}-$i -- sh -c 'mongo --eval="printjson(rs.isMaster())"'; done
2. One can insert a key into the primary instance of the mongodb replica set by running the following:
MASTER_POD_NAME must be replaced with the name of the master found from the previous step.
$ kubectl exec --namespace {{ .Release.Namespace }} MASTER_POD_NAME -- mongo --eval="printjson(db.test.insert({key1: 'value1'}))"
3. One can fetch the keys stored in the primary or any of the slave nodes in the following manner.
POD_NAME must be replaced by the name of the pod being queried.
$ kubectl exec --namespace {{ .Release.Namespace }} POD_NAME -- mongo --eval="rs.slaveOk(); db.test.find().forEach(printjson)"
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "mongodb-replicaset.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).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "mongodb-replicaset.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 "mongodb-replicaset.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name for the admin secret.
*/}}
{{- define "mongodb-replicaset.adminSecret" -}}
{{- if .Values.auth.existingAdminSecret -}}
{{- .Values.auth.existingAdminSecret -}}
{{- else -}}
{{- template "mongodb-replicaset.fullname" . -}}-admin
{{- end -}}
{{- end -}}
{{- define "mongodb-replicaset.metricsSecret" -}}
{{- if .Values.auth.existingMetricsSecret -}}
{{- .Values.auth.existingMetricsSecret -}}
{{- else -}}
{{- template "mongodb-replicaset.fullname" . -}}-metrics
{{- end -}}
{{- end -}}
{{/*
Create the name for the key secret.
*/}}
{{- define "mongodb-replicaset.keySecret" -}}
{{- if .Values.auth.existingKeySecret -}}
{{- .Values.auth.existingKeySecret -}}
{{- else -}}
{{- template "mongodb-replicaset.fullname" . -}}-keyfile
{{- end -}}
{{- end -}}
{{- if and (.Values.auth.enabled) (not .Values.auth.existingAdminSecret) -}}
apiVersion: v1
kind: Secret
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.adminSecret" . }}
type: Opaque
data:
user: {{ .Values.auth.adminUser | b64enc }}
password: {{ .Values.auth.adminPassword | b64enc }}
{{- end -}}
{{- if .Values.tls.enabled -}}
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}-ca
data:
tls.key: {{ .Values.tls.cakey }}
tls.crt: {{ .Values.tls.cacert }}
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}-init
data:
on-start.sh: |
{{ .Files.Get "init/on-start.sh" | indent 4 }}
{{- if .Values.initMongodStandalone }}
initMongodStandalone.js: |
{{ .Values.initMongodStandalone | indent 4 }}
{{- end }}
{{- if and (.Values.auth.enabled) (not .Values.auth.existingKeySecret) -}}
apiVersion: v1
kind: Secret
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.keySecret" . }}
type: Opaque
data:
key.txt: {{ .Values.auth.key | b64enc }}
{{- end -}}
{{- if and (.Values.auth.enabled) (not .Values.auth.existingMetricsSecret) (.Values.metrics.enabled) -}}
apiVersion: v1
kind: Secret
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.metricsSecret" . }}
type: Opaque
data:
user: {{ .Values.auth.metricsUser | b64enc }}
password: {{ .Values.auth.metricsPassword | b64enc }}
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}-mongodb
data:
mongod.conf: |
{{ toYaml .Values.configmap | indent 4 }}
{{- if .Values.podDisruptionBudget -}}
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}
spec:
selector:
matchLabels:
app: {{ template "mongodb-replicaset.name" . }}
release: {{ .Release.Name }}
{{ toYaml .Values.podDisruptionBudget | indent 2 }}
{{- end -}}
# A headless service for client applications to use
apiVersion: v1
kind: Service
metadata:
annotations:
{{- if .Values.serviceAnnotations }}
{{ toYaml .Values.serviceAnnotations | indent 4 }}
{{- end }}
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}-client
spec:
type: ClusterIP
clusterIP: None
ports:
- name: mongodb
port: {{ .Values.port }}
{{- if .Values.metrics.enabled }}
- name: metrics
port: {{ .Values.metrics.port }}
targetPort: metrics
{{- end }}
selector:
app: {{ template "mongodb-replicaset.name" . }}
release: {{ .Release.Name }}
# A headless service to create DNS records for discovery purposes. Use the -client service to connect applications
apiVersion: v1
kind: Service
metadata:
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}
spec:
type: ClusterIP
clusterIP: None
ports:
- name: mongodb
port: {{ .Values.port }}
publishNotReadyAddresses: true
selector:
app: {{ template "mongodb-replicaset.name" . }}
release: {{ .Release.Name }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ template "mongodb-replicaset.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
name: {{ template "mongodb-replicaset.fullname" . }}
spec:
selector:
matchLabels:
app: {{ template "mongodb-replicaset.name" . }}
release: {{ .Release.Name }}
serviceName: {{ template "mongodb-replicaset.fullname" . }}
replicas: {{ .Values.replicas }}
template:
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
release: {{ .Release.Name }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/mongodb-mongodb-configmap.yaml") . | sha256sum }}
{{- if and (.Values.metrics.prometheusServiceDiscovery) (.Values.metrics.enabled) }}
prometheus.io/scrape: "true"
prometheus.io/port: {{ .Values.metrics.port | quote }}
prometheus.io/path: {{ .Values.metrics.path | quote }}
{{- end }}
{{- if .Values.podAnnotations }}
{{ toYaml .Values.podAnnotations | indent 8 }}
{{- end }}
spec:
{{- if .Values.priorityClassName }}
priorityClassName: {{ .Values.priorityClassName }}
{{- end }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{- range .Values.imagePullSecrets }}
- name: {{ . }}
{{- end}}
{{- end }}
{{- if .Values.securityContext.enabled }}
securityContext:
runAsUser: {{ .Values.securityContext.runAsUser }}
fsGroup: {{ .Values.securityContext.fsGroup }}
runAsNonRoot: {{ .Values.securityContext.runAsNonRoot }}
{{- end }}
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
initContainers:
- name: copy-config
image: "{{ .Values.copyConfigImage.repository }}:{{ .Values.copyConfigImage.tag }}"
imagePullPolicy: {{ .Values.copyConfigImage.pullPolicy | quote }}
command:
- "sh"
args:
- "-c"
- |
set -e
set -x
cp /configdb-readonly/mongod.conf /data/configdb/mongod.conf
{{- if .Values.tls.enabled }}
cp /ca-readonly/tls.key /data/configdb/tls.key
cp /ca-readonly/tls.crt /data/configdb/tls.crt
{{- end }}
{{- if .Values.auth.enabled }}
cp /keydir-readonly/key.txt /data/configdb/key.txt
chmod 600 /data/configdb/key.txt
{{- end }}
volumeMounts:
- name: workdir
mountPath: /work-dir
- name: config
mountPath: /configdb-readonly
- name: configdir
mountPath: /data/configdb
{{- if .Values.tls.enabled }}
- name: ca
mountPath: /ca-readonly
{{- end }}
{{- if .Values.auth.enabled }}
- name: keydir
mountPath: /keydir-readonly
{{- end }}
resources:
{{ toYaml .Values.init.resources | indent 12 }}
- name: install
image: "{{ .Values.installImage.repository }}:{{ .Values.installImage.tag }}"
args:
- --work-dir=/work-dir
imagePullPolicy: "{{ .Values.installImage.pullPolicy }}"
volumeMounts:
- name: workdir
mountPath: /work-dir
resources:
{{ toYaml .Values.init.resources | indent 12 }}
- name: bootstrap
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command:
- /work-dir/peer-finder
args:
- -on-start=/init/on-start.sh
- "-service={{ template "mongodb-replicaset.fullname" . }}"
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: REPLICA_SET
value: {{ .Values.replicaSetName }}
- name: TIMEOUT
value: "{{ .Values.init.timeout }}"
{{- if .Values.auth.enabled }}
- name: AUTH
value: "true"
- name: ADMIN_USER
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.adminSecret" . }}"
key: user
- name: ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.adminSecret" . }}"
key: password
{{- if .Values.metrics.enabled }}
- name: METRICS
value: "true"
- name: METRICS_USER
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.metricsSecret" . }}"
key: user
- name: METRICS_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.metricsSecret" . }}"
key: password
{{- end }}
{{- end }}
volumeMounts:
- name: workdir
mountPath: /work-dir
- name: init
mountPath: /init
- name: configdir
mountPath: /data/configdb
- name: datadir
mountPath: /data/db
resources:
{{ toYaml .Values.init.resources | indent 12 }}
containers:
- name: {{ template "mongodb-replicaset.name" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
{{- if .Values.extraVars }}
env:
{{ toYaml .Values.extraVars | indent 12 }}
{{- end }}
ports:
- name: mongodb
containerPort: 27017
resources:
{{ toYaml .Values.resources | indent 12 }}
command:
- mongod
args:
- --config=/data/configdb/mongod.conf
- --dbpath=/data/db
- --replSet={{ .Values.replicaSetName }}
- --port=27017
- --bind_ip=0.0.0.0
{{- if .Values.auth.enabled }}
- --auth
- --keyFile=/data/configdb/key.txt
{{- end }}
{{- if .Values.tls.enabled }}
- --sslMode=requireSSL
- --sslCAFile=/data/configdb/tls.crt
- --sslPEMKeyFile=/work-dir/mongo.pem
{{- end }}
livenessProbe:
exec:
command:
- mongo
{{- if .Values.tls.enabled }}
- --ssl
- --sslCAFile=/data/configdb/tls.crt
- --sslPEMKeyFile=/work-dir/mongo.pem
{{- end }}
- --eval
- "db.adminCommand('ping')"
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
readinessProbe:
exec:
command:
- mongo
{{- if .Values.tls.enabled }}
- --ssl
- --sslCAFile=/data/configdb/tls.crt
- --sslPEMKeyFile=/work-dir/mongo.pem
{{- end }}
- --eval
- "db.adminCommand('ping')"
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
volumeMounts:
- name: datadir
mountPath: /data/db
- name: configdir
mountPath: /data/configdb
- name: workdir
mountPath: /work-dir
{{ if .Values.metrics.enabled }}
- name: metrics
image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}"
imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }}
command:
- sh
- -ec
- >-
/bin/mongodb_exporter
{{- if .Values.auth.enabled }}
-mongodb.uri mongodb://$METRICS_USER:$METRICS_PASSWORD@localhost:{{ .Values.port }}
{{- else }}
-mongodb.uri mongodb://localhost:{{ .Values.port }}
{{- end }}
{{- if .Values.tls.enabled }}
-mongodb.tls
-mongodb.tls-ca=/ca/tls.crt
-mongodb.tls-cert=/work-dir/mongo.pem
{{- end }}
-mongodb.socket-timeout={{ .Values.metrics.socketTimeout }}
-mongodb.sync-timeout={{ .Values.metrics.syncTimeout }}
-web.metrics-path={{ .Values.metrics.path }}
-web.listen-address=:{{ .Values.metrics.port }}
volumeMounts:
{{- if and (.Values.tls.enabled) }}
- name: ca
mountPath: /ca
readOnly: true
{{- end }}
- name: workdir
mountPath: /work-dir
readOnly: true
env:
{{- if .Values.auth.enabled }}
- name: METRICS_USER
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.metricsSecret" . }}"
key: user
- name: METRICS_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.metricsSecret" . }}"
key: password
{{- end }}
ports:
- name: metrics
containerPort: {{ .Values.metrics.port }}
resources:
{{ toYaml .Values.metrics.resources | indent 12 }}
livenessProbe:
exec:
command:
- sh
- -ec
- >-
/bin/mongodb_exporter
{{- if .Values.auth.enabled }}
-mongodb.uri mongodb://$METRICS_USER:$METRICS_PASSWORD@localhost:{{ .Values.port }}
{{- else }}
-mongodb.uri mongodb://localhost:{{ .Values.port }}
{{- end }}
{{- if .Values.tls.enabled }}
-mongodb.tls
-mongodb.tls-ca=/ca/tls.crt
-mongodb.tls-cert=/work-dir/mongo.pem
{{- end }}
-test
initialDelaySeconds: 30
periodSeconds: 10
{{ end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ template "mongodb-replicaset.fullname" . }}-mongodb
- name: init
configMap:
defaultMode: 0755
name: {{ template "mongodb-replicaset.fullname" . }}-init
{{- if .Values.tls.enabled }}
- name: ca
secret:
defaultMode: 0400
secretName: {{ template "mongodb-replicaset.fullname" . }}-ca
{{- end }}
{{- if .Values.auth.enabled }}
- name: keydir
secret:
defaultMode: 0400
secretName: {{ template "mongodb-replicaset.keySecret" . }}
{{- end }}
- name: workdir
emptyDir: {}
- name: configdir
emptyDir: {}
{{- if .Values.persistentVolume.enabled }}
volumeClaimTemplates:
- metadata:
name: datadir
annotations:
{{- range $key, $value := .Values.persistentVolume.annotations }}
{{ $key }}: "{{ $value }}"
{{- end }}
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 }}
{{- else }}
- name: datadir
emptyDir: {}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
name: {{ template "mongodb-replicaset.fullname" . }}-tests
data:
mongodb-up-test.sh: |
{{ .Files.Get "tests/mongodb-up-test.sh" | indent 4 }}
apiVersion: v1
kind: Pod
metadata:
labels:
app: {{ template "mongodb-replicaset.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
name: {{ template "mongodb-replicaset.fullname" . }}-test
annotations:
"helm.sh/hook": test-success
spec:
initContainers:
- name: test-framework
image: dduportal/bats:0.4.0
command:
- bash
- -c
- |
set -ex
# copy bats to tools dir
cp -R /usr/local/libexec/ /tools/bats/
volumeMounts:
- name: tools
mountPath: /tools
containers:
- name: mongo
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command:
- /tools/bats/bats
- -t
- /tests/mongodb-up-test.sh
env:
- name: FULL_NAME
value: {{ template "mongodb-replicaset.fullname" . }}
- name: REPLICAS
value: "{{ .Values.replicas }}"
{{- if .Values.auth.enabled }}
- name: AUTH
value: "true"
- name: ADMIN_USER
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.adminSecret" . }}"
key: user
- name: ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ template "mongodb-replicaset.adminSecret" . }}"
key: password
{{- end }}
volumeMounts:
- name: tools
mountPath: /tools
- name: tests
mountPath: /tests
volumes:
- name: tools
emptyDir: {}
- name: tests
configMap:
name: {{ template "mongodb-replicaset.fullname" . }}-tests
restartPolicy: Never
#! /bin/bash
# Copyright 2016 The Kubernetes Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
NS="${RELEASE_NAMESPACE:-default}"
POD_NAME="${RELEASE_NAME:-mongo}-mongodb-replicaset"
MONGOCACRT=/ca/tls.crt
MONGOPEM=/work-dir/mongo.pem
if [ -f $MONGOPEM ]; then
MONGOARGS="--ssl --sslCAFile $MONGOCACRT --sslPEMKeyFile $MONGOPEM"
fi
for i in $(seq 0 2); do
pod="${POD_NAME}-$i"
kubectl exec --namespace $NS $pod -- sh -c 'mongo '"$MONGOARGS"' --eval="printjson(rs.isMaster())"' | grep '"ismaster" : true'
if [ $? -eq 0 ]; then
echo "Found master: $pod"
MASTER=$pod
break
fi
done
kubectl exec --namespace $NS $MASTER -- mongo "$MONGOARGS" --eval='printjson(db.test.insert({"status": "success"}))'
# TODO: find maximum duration to wait for slaves to be up-to-date with master.
sleep 2
for i in $(seq 0 2); do
pod="${POD_NAME}-$i"
if [[ $pod != $MASTER ]]; then
echo "Reading from slave: $pod"
kubectl exec --namespace $NS $pod -- mongo "$MONGOARGS" --eval='rs.slaveOk(); db.test.find().forEach(printjson)'
fi
done
#!/usr/bin/env bash
MONGOCACRT=/ca/tls.crt
MONGOPEM=/work-dir/mongo.pem
MONGOARGS="--quiet"
if [ -f "$MONGOPEM" ]; then
MONGOARGS="$MONGOARGS --ssl --sslCAFile $MONGOCACRT --sslPEMKeyFile $MONGOPEM"
fi
if [[ "${AUTH}" == "true" ]]; then
MONGOARGS="$MONGOARGS --username $ADMIN_USER --password $ADMIN_PASSWORD --authenticationDatabase admin"
fi
pod_name() {
local full_name="${FULL_NAME?Environment variable FULL_NAME not set}"
local index="$1"
echo "$full_name-$index.$full_name"
}
replicas() {
echo "${REPLICAS?Environment variable REPLICAS not set}"
}
master_pod() {
for ((i = 0; i < $(replicas); ++i)); do
response=$(mongo $MONGOARGS "--host=$(pod_name "$i")" "--eval=rs.isMaster().ismaster")
if [[ "$response" == "true" ]]; then
pod_name "$i"
break
fi
done
}
setup() {
local ready=0
until [[ "$ready" -eq $(replicas) ]]; do
echo "Waiting for application to become ready" >&2
sleep 1
for ((i = 0; i < $(replicas); ++i)); do
response=$(mongo $MONGOARGS "--host=$(pod_name "$i")" "--eval=rs.status().ok" || true)
if [[ "$response" -eq 1 ]]; then
ready=$((ready + 1))
fi
done
done
}
@test "Testing mongodb client is executable" {
mongo -h
[ "$?" -eq 0 ]
}
@test "Connect mongodb client to mongodb pods" {
for ((i = 0; i < $(replicas); ++i)); do
response=$(mongo $MONGOARGS "--host=$(pod_name "$i")" "--eval=rs.status().ok")
if [[ ! "$response" -eq 1 ]]; then
exit 1
fi
done
}
@test "Write key to primary" {
response=$(mongo $MONGOARGS --host=$(master_pod) "--eval=db.test.insert({\"abc\": \"def\"}).nInserted")
if [[ ! "$response" -eq 1 ]]; then
exit 1
fi
}
@test "Read key from slaves" {
# wait for slaves to catch up
sleep 10
for ((i = 0; i < $(replicas); ++i)); do
response=$(mongo $MONGOARGS --host=$(pod_name "$i") "--eval=rs.slaveOk(); db.test.find({\"abc\":\"def\"})")
if [[ ! "$response" =~ .*def.* ]]; then
exit 1
fi
done
# Clean up a document after test
mongo $MONGOARGS --host=$(master_pod) "--eval=db.test.deleteMany({\"abc\": \"def\"})"
}
replicas: 3
port: 27017
replicaSetName: rs0
podDisruptionBudget: {}
# maxUnavailable: 1
# minAvailable: 2
auth:
enabled: false
# adminUser: username
# adminPassword: password
# metricsUser: metrics
# metricsPassword: password
# key: keycontent
# existingKeySecret:
# existingAdminSecret:
# existingMetricsSecret:
## Optionally specify an array of imagePullSecrets.
## Secrets must be manually created in the namespace.
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
##
imagePullSecrets: []
# - myRegistrKeySecretName
# Specs for the Docker image for the init container that establishes the replica set
installImage:
repository: ranchercharts/unguiculus-mongodb-install
tag: 0.7
pullPolicy: IfNotPresent
# Specs for the Docker image for the copyConfig init container
copyConfigImage:
repository: ranchercharts/busybox
tag: 1.29.3
pullPolicy: IfNotPresent
# Specs for the MongoDB image
image:
repository: ranchercharts/mongo
tag: 3.6
pullPolicy: IfNotPresent
# Additional environment variables to be set in the container
extraVars: {}
# - name: TCMALLOC_AGGRESSIVE_DECOMMIT
# value: "true"
# Prometheus Metrics Exporter
metrics:
enabled: false
image:
repository: ranchercharts/ssalaues-mongodb-exporter
tag: 0.6.1
pullPolicy: IfNotPresent
port: 9216
path: "/metrics"
socketTimeout: 3s
syncTimeout: 1m
prometheusServiceDiscovery: true
resources: {}
# Annotations to be added to MongoDB pods
podAnnotations: {}
securityContext:
enabled: true
runAsUser: 999
fsGroup: 999
runAsNonRoot: true
init:
resources: {}
timeout: 900
resources: {}
# limits:
# cpu: 500m
# memory: 512Mi
# requests:
# cpu: 100m
# memory: 256Mi
## Node selector
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
nodeSelector: {}
affinity: {}
tolerations: []
extraLabels: {}
# priorityClassName: ""
persistentVolume:
enabled: true
## mongodb-replicaset data Persistent Volume Storage Class
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
##
# storageClass: "-"
accessModes:
- ReadWriteOnce
size: 10Gi
annotations: {}
# Annotations to be added to the service
serviceAnnotations: {}
terminationGracePeriodSeconds: 30
tls:
# Enable or disable MongoDB TLS support
enabled: false
# Please generate your own TLS CA by generating it via:
# $ openssl genrsa -out ca.key 2048
# $ openssl req -x509 -new -nodes -key ca.key -days 10000 -out ca.crt -subj "/CN=mydomain.com"
# After that you can base64 encode it and paste it here:
# $ cat ca.key | base64 -w0
# cacert:
# cakey:
# Entries for the MongoDB config file
configmap: {}
# Javascript code to execute on each replica at initContainer time
# This is the recommended way to create indexes on replicasets.
# Below is an example that creates indexes in foreground on each replica in standalone mode.
# ref: https://docs.mongodb.com/manual/tutorial/build-indexes-on-replica-sets/
# initMongodStandalone: |+
# db = db.getSiblingDB("mydb")
# db.my_users.createIndex({email: 1})
# Readiness probe
readinessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
# Liveness probe
livenessProbe:
initialDelaySeconds: 30
timeoutSeconds: 5
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment