job "[[ .instance ]]" { [[ template "common/job_start" . ]] group "mongo" { [[- $c := merge .mongo . ]] [[ template "common/group_start" $c ]] network { mode = "bridge" port "mongo" { [[- if has $c "port" ]] static = [[ $c.port ]] [[- end ]] } [[- if conv.ToBool $c.prometheus.enabled ]] port "metrics" {} [[- end ]] } [[ template "common/volumes" $c ]] # The main service client will use to locate mongo servers service { name = "[[ .instance ]][[ .consul.suffix ]]" port = "mongo" [[ template "common/service_meta" $c ]] check { type = "script" command = "sh" args = ["-c", "/local/bin/mongosh --eval 'db.runCommand(\"ping\").ok'"] interval = "[[ $c.consul.check.interval ]]" timeout = "[[ $c.consul.check.timeout ]]" task = "mongod" } tags = [ "mongo-${NOMAD_ALLOC_INDEX}" ] } # This service is just used by the different nodes to find themself and configure the replica set service { name = "[[ .instance ]]-rs[[ .consul.suffix ]]" port = "mongo" meta { [[- range $k, $v := $c.consul.meta ]] [[ $k ]] = "[[ $v ]]" [[- end ]] } tags = [ "mongo-${NOMAD_ALLOC_INDEX}" ] } # This task will init the cluster on first boot task "mongo-init" { driver = "docker" lifecycle { hook = "poststart" } config { [[ template "common/image" $c ]] command = "/usr/local/bin/rs_config.sh" pids_limit = 200 # Mount a few files from the main task # Just to reduce the verbosity of templates {} sections volumes = [ "../mongod/secrets/mongo.bundle.pem:/secrets/mongo.bundle.pem:ro", "../mongod/secrets/mongo.ca.pem:/secrets/mongo.ca.pem:ro", "../mongod/local/bin/mongosh:/usr/local/bin/mongosh:ro", "../mongod/local/bin/rs_config.sh:/usr/local/bin/rs_config.sh:ro" ] [[ template "common/tmpfs" "/tmp" ]] } # As the task exits after its done, only reserve little memory # but allow it to use up to 256MB resources { cpu = 20 memory = 20 memory_max = 256 } } [[- if conv.ToBool $c.prometheus.enabled ]] task "exporter" { [[- $e := merge $c.exporter $c ]] driver = "[[ $e.nomad.driver ]]" user = "9216" config { [[ template "common/image" $e ]] args = [ "--mongodb.uri=mongodb://127.0.0.1:${NOMAD_ALLOC_PORT_mongo}/%24external?replicaSet=[[ .mongo.replica_set ]]&authMechanism=MONGODB-X509&tls=true&tlsCertificateKeyFile=%2Fsecrets%2Fmongo.bundle.pem&tlsCAFile=%2Fsecrets%2Fmongo.ca.pem", "--web.listen-address=:${NOMAD_ALLOC_PORT_metrics}", "--web.config=/local/web_tls.yml", "--collect-all" ] pids_limit = 100 } lifecycle { hook = "poststart" sidecar = true } [[ template "common/vault.policies" $e ]] [[ template "common/artifacts" $e ]] [[ template "common/metrics_cert" $e ]] # TLS config for the exporter template { data = <<_EOT [[ template "mongodb/exporter_tls.yml" $e ]] _EOT destination = "local/web_tls.yml" } # Get a certificate with monitoring capabilities template { data = <<_EOT {{ with pkiCert "[[ $e.vault.pki.path ]]/issue/mongo-monitor" "common_name=mongo.monitor" "ttl=168h" }} {{ .Cert }} {{ .Key }} {{ end }} _EOT destination = "secrets/mongo.bundle.pem" uid = 109216 gid = 109216 perms = "0640" } # CA cert used to validate remote (mongo servers') certificate. Both root and intermediate template { data = <<_EOT {{ with secret "[[ $e.vault.pki.path ]]/cert/ca_chain" }}{{ .Data.ca_chain }}{{ end }} _EOT destination = "secrets/mongo.ca.pem" } [[ template "common/resources" $c.exporter ]] } [[- end ]] task "mongod" { driver = "[[ $c.nomad.driver ]]" leader = true # Give mongod some time to shutdown kill_timeout = "60s" config { [[ template "common/image" $c ]] command = "mongod" args = ["--config", "/local/mongod.conf"] pids_limit = 500 volumes = [ "local/bin/mongosh:/usr/local/bin/mongosh:ro" ] [[ template "common/tmpfs" "/tmp" ]] } [[ template "common/vault.policies" $c ]] [[ template "common/artifacts" $c ]] template { data = <<_EOT [[- if isKind "map" $c.config ]] [[ merge $c.config (tmpl.Exec "mongodb/mongod.conf" $c | yaml) | toYAML ]] [[- else if isKind "string" $c.config ]] [[ merge ($c.config | yaml) (tmpl.Exec "mongodb/mongod.conf" $c | yaml) | toYAML ]] [[- else ]] [[ template "mongodb/mongod.conf" $c ]] [[- end ]] _EOT destination = "local/mongod.conf" } # This is the certificate used by the mongod process # Note : we ask for a cert valid for the host IP address, and also for the XXXXX.addr.dc1.consul address # as it's what is published in the SRV record. The XXXX is the hex representation of the IP. # Something like {{ range (env "NOMAD_IP_mongo" | split ".") }}{{ printf "%02x" (. | parseInt) }}{{ end }} # would be cleaner, but can't find how to use it in this context # Also : each instance will have a slightly differnt ttl so all cert will not expire at the same time template { data = <<_EOT {{- with pkiCert "[[ $c.vault.pki.path ]]/issue/mongod" (printf "common_name=mongo-%s.[[ .instance ]][[ .consul.suffix ]].service.[[ .consul.domain ]]" (env "NOMAD_ALLOC_INDEX")) ( printf "alt_names=%02x%02x%02x%02x.addr.dc1.[[ .consul.domain ]]" (env "NOMAD_HOST_IP_mongo" | regexReplaceAll "^(\\d+)\\..*" "$1" | parseInt) (env "NOMAD_HOST_IP_mongo" | regexReplaceAll "^\\d+\\.(\\d+)\\..*" "$1" | parseInt) (env "NOMAD_HOST_IP_mongo" | regexReplaceAll "^\\d+\\.\\d+\\.(\\d+)\\..*" "$1" | parseInt) (env "NOMAD_HOST_IP_mongo" | regexReplaceAll "^\\d+\\.\\d+\\.\\d+\\.(\\d+)$" "$1" | parseInt) ) (printf "ip_sans=%s,127.0.0.1" (env "NOMAD_HOST_IP_mongo")) (printf "ttl=%dh" (env "NOMAD_ALLOC_INDEX" | parseInt | multiply 8 | add 600)) }} {{ .Cert }} {{ .Key }} {{- end }} _EOT destination = "secrets/mongo.bundle.pem" uid = 100999 gid = 100999 perms = "0640" change_mode = "script" change_script { command = "/local/bin/rotate-cert.sh" # If cert rotation fails, kill the task and restart it fail_on_error = true } } # CA cert used to validate remote certs template { data = <<_EOT {{- with secret "[[ $c.vault.pki.path ]]/cert/ca_chain" }} {{ .Data.ca_chain }} {{- end }} _EOT destination = "secrets/mongo.ca.pem" change_mode = "script" change_script { command = "/local/bin/rotate-cert.sh" # If cert rotation fails, kill the task and restart it fail_on_error = true } } # A mongosh wrapper which will automatically use the x509 root cert template { data = <<_EOT [[ template "mongodb/mongosh" $c ]] _EOT destination = "local/bin/mongosh" uid = 100000 gid = 100000 perms = "0755" } # A script to dynamically reconfigure members' address when mongo instances restart template { data = <<_EOT [[ template "mongodb/rs_config.sh" $c ]] _EOT destination = "local/bin/rs_config.sh" uid = 100000 gid = 100000 perms = "0755" change_mode = "script" change_script { command = "/local/bin/rs_config.sh" # Will fail when mongod is shuting down. We don't want Nomad to kill the task fail_on_error = false } } # A script to rotate certificates template { data = <<_EOT [[ template "mongodb/rotate-cert.sh" $c ]] _EOT destination = "local/bin/rotate-cert.sh" uid = 100000 gid = 100000 perms = "0755" } # Persistent data volume_mount { volume = "mongo" destination = "/data" } [[ template "common/resources" $c ]] } } }