Skip to content

Commit 7c11330

Browse files
committed
Terraform Kubernetes MongoDB replicaset via terraform kubernetes provider
1 parent cd11d74 commit 7c11330

File tree

3 files changed

+327
-0
lines changed

3 files changed

+327
-0
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# terraform-kubernetes-mongodb
2+
MongoDB on Kubernetes
3+
4+
Tested on GKE but it should work for any kubernetes cluster given the right terraform-provider-kubernetes setup.
5+
6+
## Inputs
7+
8+
- **name** : name of the deployment
9+
- **namespace** : kubernetes namespace to be deployed
10+
- **replicacount** : replica instance count
11+
12+
13+
## Dependencies
14+
15+
Terraform Kubernetes Provider
16+
17+
## Tested With
18+
19+
- terraform-providers/kubernetes : 1.9.0
20+
- mongodb:bionic(4.2) docker image
21+
- kubernetes 1.13.7-gke.8
22+
23+
## Credits
24+
25+
This module was initially generated from helm/stable/mongodb-replicaset via (k2tf)[https://github.com/sl1pm4t/k2tf] project.

main.tf

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
resource "kubernetes_config_map" "mongodb_replicaset_init" {
2+
metadata {
3+
name = "${var.name}-mongodb-replicaset-init"
4+
namespace = "${var.namespace}"
5+
6+
labels = {
7+
app = "mongodb-replicaset"
8+
release = "${var.name}"
9+
}
10+
}
11+
12+
data = {
13+
"on-start.sh" = "#!/usr/bin/env bash\n\n# Copyright 2018 The Kubernetes Authors. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -e pipefail\n\nport=27017\nreplica_set=\"$REPLICA_SET\"\nscript_name=$${0##*/}\nSECONDS=0\ntimeout=\"$${TIMEOUT:-900}\"\n\nif [[ \"$AUTH\" == \"true\" ]]; then\n admin_user=\"$ADMIN_USER\"\n admin_password=\"$ADMIN_PASSWORD\"\n admin_creds=(-u \"$admin_user\" -p \"$admin_password\")\n if [[ \"$METRICS\" == \"true\" ]]; then\n metrics_user=\"$METRICS_USER\"\n metrics_password=\"$METRICS_PASSWORD\"\n fi\n auth_args=(\"--auth\" \"--keyFile=/data/configdb/key.txt\")\nfi\n\nlog() {\n local msg=\"$1\"\n local timestamp\n timestamp=$(date --iso-8601=ns)\n echo \"[$timestamp] [$script_name] $msg\" 2>&1 | tee -a /work-dir/log.txt 1>&2\n}\n\nretry_until() {\n local host=\"$${1}\"\n local command=\"$${2}\"\n local expected=\"$${3}\"\n local creds=(\"$${admin_creds[@]}\")\n\n # Don't need credentials for admin user creation and pings that run on localhost\n if [[ \"$${host}\" =~ ^localhost ]]; then\n creds=()\n fi\n\n until [[ $(mongo admin --host \"$${host}\" \"$${creds[@]}\" \"$${ssl_args[@]}\" --quiet --eval \"$${command}\") == \"$${expected}\" ]]; do\n sleep 1\n\n if (! ps \"$${pid}\" &>/dev/null); then\n log \"mongod shutdown unexpectedly\"\n exit 1\n fi\n if [[ \"$${SECONDS}\" -ge \"$${timeout}\" ]]; then\n log \"Timed out after $${timeout}s attempting to bootstrap mongod\"\n exit 1\n fi\n\n log \"Retrying $${command} on $${host}\"\n done\n}\n\nshutdown_mongo() {\n local host=\"$${1:-localhost}\"\n local args='force: true'\n log \"Shutting down MongoDB ($args)...\"\n if (! mongo admin --host \"$${host}\" \"$${admin_creds[@]}\" \"$${ssl_args[@]}\" --eval \"db.shutdownServer({$args})\"); then\n log \"db.shutdownServer() failed, sending the terminate signal\"\n kill -TERM \"$${pid}\"\n fi\n}\n\ninit_mongod_standalone() {\n if [[ ! -f /init/initMongodStandalone.js ]]; then\n log \"Skipping init mongod standalone script\"\n return 0\n elif [[ -z \"$(ls -1A /data/db)\" ]]; then\n log \"mongod standalone script currently not supported on initial install\"\n return 0\n fi\n\n local port=\"27018\"\n log \"Starting a MongoDB instance as standalone...\"\n 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 &\n export pid=$!\n trap shutdown_mongo EXIT\n log \"Waiting for MongoDB to be ready...\"\n retry_until \"localhost:$${port}\" \"db.adminCommand('ping').ok\" \"1\"\n log \"Running init js script on standalone mongod\"\n mongo admin --port \"$${port}\" \"$${admin_creds[@]}\" \"$${ssl_args[@]}\" /init/initMongodStandalone.js\n shutdown_mongo \"localhost:$${port}\"\n}\n\nmy_hostname=$(hostname)\nlog \"Bootstrapping MongoDB replica set member: $my_hostname\"\n\nlog \"Reading standard input...\"\nwhile read -ra line; do\n if [[ \"$${line}\" == *\"$${my_hostname}\"* ]]; then\n service_name=\"$line\"\n fi\n peers=(\"$${peers[@]}\" \"$line\")\ndone\n\n# Generate the ca cert\nca_crt=/data/configdb/tls.crt\nif [ -f \"$ca_crt\" ]; then\n log \"Generating certificate\"\n ca_key=/data/configdb/tls.key\n pem=/work-dir/mongo.pem\n ssl_args=(--ssl --sslCAFile \"$ca_crt\" --sslPEMKeyFile \"$pem\")\n\n# Move into /work-dir\npushd /work-dir\n\ncat >openssl.cnf <<EOL\n[req]\nreq_extensions = v3_req\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\n[ v3_req ]\nbasicConstraints = CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\nsubjectAltName = @alt_names\n[alt_names]\nDNS.1 = $(echo -n \"$my_hostname\" | sed s/-[0-9]*$//)\nDNS.2 = $my_hostname\nDNS.3 = $service_name\nDNS.4 = localhost\nDNS.5 = 127.0.0.1\nEOL\n\n # Generate the certs\n openssl genrsa -out mongo.key 2048\n openssl req -new -key mongo.key -out mongo.csr -subj \"/OU=MongoDB/CN=$my_hostname\" -config openssl.cnf\n openssl x509 -req -in mongo.csr \\\n -CA \"$ca_crt\" -CAkey \"$ca_key\" -CAcreateserial \\\n -out mongo.crt -days 3650 -extensions v3_req -extfile openssl.cnf\n\n rm mongo.csr\n cat mongo.crt mongo.key > $pem\n rm mongo.key mongo.crt\nfi\n\ninit_mongod_standalone\n\nlog \"Peers: $${peers[*]}\"\nlog \"Starting a MongoDB replica\"\nmongod --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 &\npid=$!\ntrap shutdown_mongo EXIT\n\nlog \"Waiting for MongoDB to be ready...\"\nretry_until \"localhost\" \"db.adminCommand('ping').ok\" \"1\"\nlog \"Initialized.\"\n\n# try to find a master\nfor peer in \"$${peers[@]}\"; do\n log \"Checking if $${peer} is primary\"\n # Check rs.status() first since it could be in primary catch up mode which db.isMaster() doesn't show\n if [[ $(mongo admin --host \"$${peer}\" \"$${admin_creds[@]}\" \"$${ssl_args[@]}\" --quiet --eval \"rs.status().myState\") == \"1\" ]]; then\n retry_until \"$${peer}\" \"db.isMaster().ismaster\" \"true\"\n log \"Found primary: $${peer}\"\n primary=\"$${peer}\"\n break\n fi\ndone\n\nif [[ \"$${primary}\" = \"$${service_name}\" ]]; then\n log \"This replica is already PRIMARY\"\nelif [[ -n \"$${primary}\" ]]; then\n if [[ $(mongo admin --host \"$${primary}\" \"$${admin_creds[@]}\" \"$${ssl_args[@]}\" --quiet --eval \"rs.conf().members.findIndex(m => m.host == '$${service_name}:$${port}')\") == \"-1\" ]]; then\n log \"Adding myself ($${service_name}) to replica set...\"\n if (mongo admin --host \"$${primary}\" \"$${admin_creds[@]}\" \"$${ssl_args[@]}\" --eval \"rs.add('$${service_name}')\" | grep 'Quorum check failed'); then\n log 'Quorum check failed, unable to join replicaset. Exiting prematurely.'\n exit 1\n fi\n fi\n\n sleep 3\n log 'Waiting for replica to reach SECONDARY state...'\n retry_until \"$${service_name}\" \"rs.status().myState\" \"2\"\n log '✓ Replica reached SECONDARY state.'\n\nelif (mongo \"$${ssl_args[@]}\" --eval \"rs.status()\" | grep \"no replset config has been received\"); then\n log \"Initiating a new replica set with myself ($service_name)...\"\n mongo \"$${ssl_args[@]}\" --eval \"rs.initiate({'_id': '$replica_set', 'members': [{'_id': 0, 'host': '$service_name'}]})\"\n\n sleep 3\n log 'Waiting for replica to reach PRIMARY state...'\n retry_until \"localhost\" \"db.isMaster().ismaster\" \"true\"\n primary=\"$${service_name}\"\n log '✓ Replica reached PRIMARY state.'\n\n if [[ \"$${AUTH}\" == \"true\" ]]; then\n log \"Creating admin user...\"\n mongo admin \"$${ssl_args[@]}\" --eval \"db.createUser({user: '$${admin_user}', pwd: '$${admin_password}', roles: [{role: 'root', db: 'admin'}]})\"\n fi\nfi\n\n# User creation\nif [[ -n \"$${primary}\" && \"$AUTH\" == \"true\" && \"$METRICS\" == \"true\" ]]; then\n metric_user_count=$(mongo admin --host \"$${primary}\" \"$${admin_creds[@]}\" \"$${ssl_args[@]}\" --eval \"db.system.users.find({user: '$${metrics_user}'}).count()\" --quiet)\n if [[ \"$${metric_user_count}\" == \"0\" ]]; then\n log \"Creating clusterMonitor user...\"\n 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'}]})\"\n fi\nfi\n\nlog \"MongoDB bootstrap complete\"\nexit 0\n"
14+
}
15+
}
16+
17+
resource "kubernetes_config_map" "mongodb_replicaset_mongodb" {
18+
metadata {
19+
name = "${var.name}-mongodb-replicaset-mongodb"
20+
namespace = "${var.namespace}"
21+
22+
labels = {
23+
app = "mongodb-replicaset"
24+
release = "${var.name}"
25+
}
26+
}
27+
28+
data = {
29+
"mongod.conf" = "{}\n"
30+
}
31+
}
32+
33+
resource "kubernetes_service" "mongodb_replicaset" {
34+
metadata {
35+
name = "${var.name}-mongodb-replicaset"
36+
namespace = "${var.namespace}"
37+
38+
labels = {
39+
app = "mongodb-replicaset"
40+
release = "${var.name}"
41+
}
42+
43+
annotations = {
44+
"service.alpha.kubernetes.io/tolerate-unready-endpoints" = "true"
45+
}
46+
}
47+
48+
spec {
49+
port {
50+
name = "mongodb"
51+
port = 27017
52+
}
53+
54+
selector = {
55+
app = "mongodb-replicaset"
56+
release = "${var.name}"
57+
}
58+
59+
cluster_ip = "None"
60+
type = "ClusterIP"
61+
publish_not_ready_addresses = true
62+
}
63+
}
64+
65+
resource "kubernetes_stateful_set" "mongodb_replicaset" {
66+
metadata {
67+
name = "${var.name}-mongodb-replicaset"
68+
namespace = "${var.namespace}"
69+
70+
labels = {
71+
app = "mongodb-replicaset"
72+
release = "${var.name}"
73+
}
74+
}
75+
76+
spec {
77+
replicas = "${var.replicacount}"
78+
79+
selector {
80+
match_labels = {
81+
app = "mongodb-replicaset"
82+
release = "${var.name}"
83+
}
84+
}
85+
86+
template {
87+
metadata {
88+
labels = {
89+
app = "mongodb-replicaset"
90+
91+
release = "${var.name}"
92+
}
93+
94+
annotations = {
95+
"checksum/config" = "d2443db7eccf79039fa12519adbce04b24232c89bff87ff7dada29bd0fdd3f48"
96+
}
97+
}
98+
99+
spec {
100+
volume {
101+
name = "config"
102+
103+
config_map {
104+
name = "mongodb-replicaset-mongodb"
105+
}
106+
}
107+
108+
volume {
109+
name = "init"
110+
111+
config_map {
112+
name = "mongodb-replicaset-init"
113+
default_mode = "0755"
114+
}
115+
}
116+
117+
volume {
118+
name = "workdir"
119+
}
120+
121+
volume {
122+
name = "configdir"
123+
}
124+
125+
init_container {
126+
name = "copy-config"
127+
image = "busybox:1.29.3"
128+
command = ["sh"]
129+
args = ["-c", "set -e\nset -x\n\ncp /configdb-readonly/mongod.conf /data/configdb/mongod.conf\n"]
130+
131+
volume_mount {
132+
name = "workdir"
133+
mount_path = "/work-dir"
134+
}
135+
136+
volume_mount {
137+
name = "config"
138+
mount_path = "/configdb-readonly"
139+
}
140+
141+
volume_mount {
142+
name = "configdir"
143+
mount_path = "/data/configdb"
144+
}
145+
146+
image_pull_policy = "IfNotPresent"
147+
}
148+
149+
init_container {
150+
name = "install"
151+
image = "unguiculus/mongodb-install:0.7"
152+
args = ["--work-dir=/work-dir"]
153+
154+
volume_mount {
155+
name = "workdir"
156+
mount_path = "/work-dir"
157+
}
158+
159+
image_pull_policy = "IfNotPresent"
160+
}
161+
162+
init_container {
163+
name = "bootstrap"
164+
image = "mongo:bionic"
165+
command = ["/work-dir/peer-finder"]
166+
args = ["-on-start=/init/on-start.sh", "-service=$(POD_NAME)-mongodb-replicaset"]
167+
168+
env {
169+
name = "POD_NAME"
170+
value = "${var.name}"
171+
}
172+
173+
env {
174+
name = "POD_NAMESPACE"
175+
176+
value_from {
177+
field_ref {
178+
api_version = "v1"
179+
field_path = "metadata.namespace"
180+
}
181+
}
182+
}
183+
184+
env {
185+
name = "REPLICA_SET"
186+
value = "rs0"
187+
}
188+
189+
env {
190+
name = "TIMEOUT"
191+
value = "900"
192+
}
193+
194+
volume_mount {
195+
name = "workdir"
196+
mount_path = "/work-dir"
197+
}
198+
199+
volume_mount {
200+
name = "init"
201+
mount_path = "/init"
202+
}
203+
204+
volume_mount {
205+
name = "configdir"
206+
mount_path = "/data/configdb"
207+
}
208+
209+
volume_mount {
210+
name = "datadir"
211+
mount_path = "/data/db"
212+
}
213+
214+
image_pull_policy = "IfNotPresent"
215+
}
216+
217+
container {
218+
name = "mongodb-replicaset"
219+
image = "mongo:bionic"
220+
command = ["mongod"]
221+
args = ["--config=/data/configdb/mongod.conf", "--dbpath=/data/db", "--replSet=rs0", "--port=27017", "--bind_ip=0.0.0.0"]
222+
223+
port {
224+
name = "mongodb"
225+
container_port = 27017
226+
}
227+
228+
volume_mount {
229+
name = "datadir"
230+
mount_path = "/data/db"
231+
}
232+
233+
volume_mount {
234+
name = "configdir"
235+
mount_path = "/data/configdb"
236+
}
237+
238+
volume_mount {
239+
name = "workdir"
240+
mount_path = "/work-dir"
241+
}
242+
243+
liveness_probe {
244+
exec {
245+
command = ["mongo", "--eval", "db.adminCommand('ping')"]
246+
}
247+
248+
initial_delay_seconds = 30
249+
timeout_seconds = 5
250+
period_seconds = 10
251+
success_threshold = 1
252+
failure_threshold = 3
253+
}
254+
255+
readiness_probe {
256+
exec {
257+
command = ["mongo", "--eval", "db.adminCommand('ping')"]
258+
}
259+
260+
initial_delay_seconds = 5
261+
timeout_seconds = 1
262+
period_seconds = 10
263+
success_threshold = 1
264+
failure_threshold = 3
265+
}
266+
267+
image_pull_policy = "IfNotPresent"
268+
}
269+
270+
termination_grace_period_seconds = 30
271+
272+
security_context {
273+
run_as_user = 999
274+
run_as_non_root = true
275+
fs_group = 999
276+
}
277+
}
278+
}
279+
280+
volume_claim_template {
281+
metadata {
282+
name = "datadir"
283+
}
284+
285+
spec {
286+
access_modes = ["ReadWriteOnce"]
287+
288+
resources {
289+
requests = {
290+
storage = "10Gi"
291+
}
292+
}
293+
}
294+
}
295+
296+
service_name = "${var.name}-mongodb-replicaset"
297+
}
298+
}
299+

variables.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
variable "namespace" {}
2+
variable "name" {}
3+
variable "replicacount" {}

0 commit comments

Comments
 (0)