|
| 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 | + |
0 commit comments