Chapter 21. Setting Up MongoDB in Production

In Chapter 2, we covered the basics of starting MongoDB. This chapter will go into more detail about which options are important for setting up MongoDB in production, including:

  • Commonly used options

  • Starting up and shutting down MongoDB

  • Security-related options

  • Logging considerations

Starting from the Command Line

The MongoDB server is started with the mongod executable. mongod has many configurable startup options; to view all of them, run mongod --help from the command line. A couple of the options are widely used and important to be aware of:

--dbpath

Specify an alternate directory to use as the data directory; the default is /data/db/ (or, on Windows, datadb on the MongoDB binary’s volume). Each mongod process on a machine needs its own data directory, so if you are running three instances of mongod on one machine, you’ll need three separate data directories. When mongod starts up, it creates a mongod.lock file in its data directory, which prevents any other mongod process from using that directory. If you attempt to start another MongoDB server using the same data directory, it will give an error:

exception in initAndListen: DBPathInUse: Unable to lock the
      lock file:  data/db/mongod.lock (Resource temporarily unavailable).
      Another mongod instance is already running on the 
      data/db directory,
       terminating
--port

Specify the port number for the server to listen on. By default, mongod uses port 27017, which is unlikely to be used by another process (besides other mongod processes). If you would like to run more than one mongod process on a single machine, you’ll need to specify different ports for each one. If you try to start mongod on a port that is already being used, it will give an error:

Failed to set up listener: SocketException: Address already in use.
--fork

On Unix-based systems, fork the server process, running MongoDB as a daemon.

If you are starting up mongod for the first time (with an empty data directory), it can take the filesystem a few minutes to allocate database files. The parent process will not return from forking until the preallocation is done and mongod is ready to start accepting connections. Thus, fork may appear to hang. You can tail the log to see what it is doing. You must use --logpath if you specify --fork.

--logpath

Send all output to the specified file rather than outputting on the command line. This will create the file if it does not exist, assuming you have write permissions to the directory. It will also overwrite the log file if it already exists, erasing any older log entries. If you’d like to keep old logs around, use the --logappend option in addition to --logpath (highly recommended).

--directoryperdb

Put each database in its own directory. This allows you to mount different databases on different disks, if necessary or desired. Common uses for this are putting a local database on its own disk (replication) or moving a database to a different disk if the original one fills up. You could also put databases that handle more load on faster disks and databases with a lower load on slower disks. This basically gives you more flexibility to move things around later.

--config

Use a configuration file for additional options not specified on the command line. This is typically used to make sure options are the same between restarts. See “File-Based Configuration” for details.

For example, to start the server as a daemon listening on port 5586 and sending all output to mongodb.log, we could run this:

$ ./mongod --dbpath data/db --port 5586 --fork --logpath
    mongodb.log --logappend 2019-09-06T22:52:25.376-0500 I CONTROL [main]
    Automatically disabling TLS 1.0,  to force-enable TLS 1.0 specify
    --sslDisabledProtocols 'none' about to fork child process, waiting until
    server is ready for connections. forked process: 27610 child process
    started successfully, parent exiting

When you first install and start MongoDB, it is a good idea to look at the log. This might be an easy thing to miss, especially if MongoDB is being started from an init script, but the log often contains important warnings that prevent later errors from occurring. If you don’t see any warnings in the MongoDB log on startup, then you are all set. (Startup warnings will also appear on shell startup.)

If there are any warnings in the startup banner, take note of them. MongoDB will warn you about a variety of issues: that you’re running on a 32-bit machine (which MongoDB is not designed for), that you have NUMA enabled (which can slow your application to a crawl), or that your system does not allow enough open file descriptors (MongoDB uses a lot of file descriptors).

The log preamble won’t change when you restart the database, so feel free to run MongoDB from an init script and ignore the logs, once you know what they say. However, it’s a good idea to check again each time you do an install, upgrade, or recover from a crash, just to make sure MongoDB and your system are on the same page.

When you start the database, MongoDB will write a document to the local.startup_log collection that describes the version of MongoDB, underlying system, and flags used. We can look at this document using the mongo shell:

> use local
switched to db local
> db.startup_log.find().sort({startTime: -1}).limit(1).pretty()
{
    "_id" : "server1-1544192927184",
    "hostname" : "server1.example.net",
    "startTime" : ISODate("2019-09-06T22:50:47Z"),
    "startTimeLocal" : "Fri Sep  6 22:57:47.184",
    "cmdLine" : {
        "net" : {
            "port" : 5586
        },
        "processManagement" : {
            "fork" : true
        },
        "storage" : {
            "dbPath" : "data/db"
        },
        "systemLog" : {
            "destination" : "file",
            "logAppend" : true,
            "path" : "mongodb.log"
        }
    },
    "pid" : NumberLong(27278),
    "buildinfo" : {
        "version" : "4.2.0",
        "gitVersion" : "a4b751dcf51dd249c5865812b390cfd1c0129c30",
        "modules" : [
            "enterprise"
        ],
        "allocator" : "system",
        "javascriptEngine" : "mozjs",
        "sysInfo" : "deprecated",
        "versionArray" : [
            4,
            2,
            0,
            0
        ],
        "openssl" : {
            "running" : "Apple Secure Transport"
        },
        "buildEnvironment" : {
            "distmod" : "",
            "distarch" : "x86_64",
            "cc" : "gcc: Apple LLVM version 8.1.0 (clang-802.0.42)",
            "ccflags" : "-mmacosx-version-min=10.10 -fno-omit
                       -frame-pointer -fno-strict-aliasing 
                       -ggdb -pthread -Wall 
                       -Wsign-compare -Wno-unknown-pragmas 
                       -Winvalid-pch -Werror -O2 -Wno-unused
                       -local-typedefs -Wno-unused-function 
                       -Wno-unused-private-field 
                       -Wno-deprecated-declarations 
                       -Wno-tautological-constant-out-of
                       -range-compare 
                       -Wno-unused-const-variable -Wno
                       -missing-braces -Wno-inconsistent
                       -missing-override 
                       -Wno-potentially-evaluated-expression 
                       -Wno-exceptions -fstack-protector
                       -strong -fno-builtin-memcmp",
            "cxx" : "g++: Apple LLVM version 8.1.0 (clang-802.0.42)",
            "cxxflags" : "-Woverloaded-virtual -Werror=unused-result 
                       -Wpessimizing-move -Wredundant-move 
                       -Wno-undefined-var-template -stdlib=libc++ 
                       -std=c++14",
            "linkflags" : "-mmacosx-version-min=10.10 -Wl, 
                       -bind_at_load -Wl,-fatal_warnings 
                       -fstack-protector-strong 
                       -stdlib=libc++",
            "target_arch" : "x86_64",
            "target_os" : "macOS"
        },
        "bits" : 64,
        "debug" : false,
        "maxBsonObjectSize" : 16777216,
        "storageEngines" : [
            "biggie",
            "devnull",
            "ephemeralForTest",
            "inMemory",
            "queryable_wt",
            "wiredTiger"
        ]
    }
}

This collection can be useful for tracking upgrades and changes in behavior.

File-Based Configuration

MongoDB supports reading configuration information from a file. This can be useful if you have a large set of options you want to use or are automating the task of starting up MongoDB. To tell the server to get options from a configuration file, use the -f or --config flags. For example, run mongod --config ~/.mongodb.conf to use ~/.mongodb.conf as a configuration file.

The options supported in a configuration file are the same as those accepted at the command line. However, the format is different. As of MongoDB 2.6, MongoDB configuration files use the YAML format. Here’s an example configuration file:

systemLog:
   destination: file
   path: "mongod.log"
   logAppend: true
storage:
   dbPath: data/db
processManagement:
   fork: true
net:
   port: 5586
...

This configuration file specifies the same options we used earlier when starting with regular command-line arguments. Note that these same options are reflected in the startup_log collection document we looked at in the previous section. The only real difference is that the options are specified using JSON rather than YAML.

In MongoDB 4.2, expansion directives were added to allow the loading of specific configuration file options or loading of the entire configuration file. The advantage of expansion directives is that confidential information, such as passwords and security certificates, does not have to be stored in the config file directly. The --configExpand command-line option enables this feature and must include the expansion directives you wish to enable. __rest and __exec are the current implementation of the expansion directives in MongoDB. The __rest expansion directive loads specific configuration file values or loads the entire configuration file from a REST endpoint. The __exec expansion directive loads specific configuration file values or loads the entire configuration file from a shell or terminal command.

Stopping MongoDB

Being able to safely stop a running MongoDB server is at least as important as being able to start one. There are a couple of different options for doing this effectively.

The cleanest way to shut down a running server is to use the shutdown command, {"shutdown" : 1}. This is an admin command and must be run on the admin database. The shell features a helper function to make this easier:

> use admin
switched to db admin
> db.shutdownServer()
server should be down...

When run on a primary, the shutdown command steps down the primary and waits for a secondary to catch up before shutting down the server. This minimizes the chance of rollback, but the shutdown isn’t guaranteed to succeed. If there is no secondary available that can catch up within a few seconds, the shutdown command will fail and the (former) primary will not shut down:

> db.shutdownServer()
{
    "closest" : NumberLong(1349465327),
    "difference" : NumberLong(20),
    "errmsg" : "no secondaries within 10 seconds of my optime",
    "ok" : 0
}

You can force the shutdown command to shut down a primary by using the force option:

db.adminCommand({"shutdown" : 1, "force" : true})

This is equivalent to sending a SIGINT or SIGTERM signal (all three of these options result in a clean shutdown, but there may be unreplicated data). If the server is running as the foreground process in a terminal, a SIGINT can be sent by pressing Ctrl-C. Otherwise, a command like kill can be used to send the signal. If mongod had 10014 as its PID, the command would be kill -2 10014 (SIGINT) or kill 10014 (SIGTERM).

When mongod receives a SIGINT or SIGTERM, it will do a clean shutdown. This means it will wait for any running operations or file preallocations to finish (this could take a moment), close all open connections, flush all data to disk, and halt.

Security

Do not set up publicly addressable MongoDB servers. You should restrict access as tightly as possible between the outside world and MongoDB. The best way to do this is to set up firewalls and only allow MongoDB to be reachable on internal network addresses. Chapter 24 covers what connections it’s necessary to allow between MongoDB servers and clients.

Beyond firewalls, there are a few options you can add to your config file to make it more secure:

--bind_ip

Specify the interfaces that you want MongoDB to listen on. Generally you want this to be an internal IP: something application servers and other members of your cluster can access but that is inaccessible to the outside world. localhost is fine for mongos processes if you’re running the application server on the same machine. For config servers and shards, they’ll need to be addressable from other machines, so stick with non-localhost addresses.

Starting in MongoDB 3.6, mongod and mongos processes bind to localhost by default. When bound only to localhost, mongod and mongos will only accept connections from clients running on the same machine. This helps limit the exposure of unsecured MongoDB instances. To bind to other addresses, use the net.bindIp configuration file setting or the --bind_ip command-line option to specify a list of hostnames or IP addresses.

--nounixsocket

Disable listening on the UNIX domain socket. If you’re not planning to connect via filesystem socket, you might as well disallow it. You would only connect via filesystem socket on a machine that is also running an application server: you must be local to use a filesystem socket.

--noscripting

Disable server-side JavaScript execution. Some security issues that have been reported with MongoDB have been JavaScript-related, so it’s generally safer to disallow it, if your application allows.

Note

Several shell helpers assume that JavaScript is available on the server, notably sh.status(). You will see errors if you attempt to run any of these helpers with JavaScript disabled.

Data Encryption

Data encryption is available in MongoDB Enterprise. These options are not supported in the Community version of MongoDB.

The data encryption process includes the following steps:

  • Generate a master key.

  • Generate keys for each database.

  • Encrypt data with the database keys.

  • Encrypt the database keys with the master key.

When using data encryption, all data files are encrypted in the filesystem. Data is only unencrypted in memory and during transmission. To encrypt all of MongoDB’s network traffic, you can use TLS/SSL. The data encryption options that MongoDB Enterprise users can add to their config files are:

--enableEncryption

Enables encryption in the WiredTiger storage engine. With this option, data stored in memory and on disk will be encrypted. This is sometimes referred to as “encryption at rest.” You must set this to true in order to pass in encryption keys and to configure encryption. This option is false by default.

--encryptionCipherMode

Set the cipher mode for encryption at rest in WiredTiger. There are two modes available: AES256-CBC and AES256-GCM. AES256-CBC is an acronym for 256-bit Advanced Encryption Standard in Cipher Block Chaining Mode. AES256-GCM uses Galois/Counter Mode. Both are standard encryption ciphers. As of MongoDB 4.0, MongoDB Enterprise on Windows no longer supports AES256-GCM.

--encryptionKeyFile

Specify the path to the local keyfile if you are managing keys using a process other than the Key Management Interoperability Protocol (KMIP).

MongoDB Enterprise also supports key management using KMIP. A discussion of KMIP is beyond the scope of this book. Please see the MongoDB documentation for details on using KMIP with MongoDB.

SSL Connections

As we saw in Chapter 18, MongoDB supports transport encryption using TLS/SSL. This feature is available in all editions of MongoDB. By default, connections to MongoDB transfer data unencrypted. However, TLS/SSL ensures transport encryption. MongoDB uses native TSL/SSL libraries available on your operating system. Use the option --tlsMode and related options to configure TLS/SSL. Refer to Chapter 18 for more detail, and consult your driver’s documentation on how to create TLS/SSL connections using your language.

Logging

By default, mongod sends its logs to stdout. Most init scripts use the --logpath option to send logs to a file. If you have multiple MongoDB instances on a single machine (say, a mongod and a mongos), make sure that their logs are stored in separate files. Be sure that you know where the logs are and have read access to the files.

MongoDB spits out a lot of log messages, but please do not run with the --quiet option (which suppresses some of them). Leaving the log level at the default is usually perfect: there is enough information for basic debugging (why is this slow, why isn’t this starting up, etc.), but the logs do not take up too much space.

If you are debugging a specific issue with your application, there are a couple of options for getting more information from the logs. You can change the log level by running the setParameter command, or by setting the log level at startup time by passing it as a string using the --setParameter option.

> db.adminCommand({"setParameter" : 1, "logLevel" : 3})

You can also change the log level for a particular component. This is helpful if you are debugging a specific aspect of your application and require more information, but only from that component. In this example, we set the default log verbosity to 1 and the query component verbosity to 2:

> db.adminCommand({"setParameter" : 1, logComponentVerbosity:
        { verbosity: 1, query: { verbosity: 2 }}})

Remember to turn the log level back down to 0 when you’re done debugging, or your logs may be needlessly noisy. You can turn the level all the way up to 5, at which point mongod will print out almost every action it takes, including the contents of every request handled. This can cause a lot of I/O as mongod writes everything to the log file, which can slow down a busy system. Turning on profiling is a better option if you need to see every operation as it’s happening.

By default, MongoDB logs information about queries that take longer than 100 ms to run. If 100 ms is too short or too long for your application, you can change the threshold with setProfilingLevel:

> // Only log queries that take longer than 500 ms
> db.setProfilingLevel(1, 500)
{ "was" : 0, "slowms" : 100, "ok" : 1 }
> db.setProfilingLevel(0)
{ "was" : 1, "slowms" : 500, "ok" : 1 }

The second line will turn off profiling, but the value in milliseconds given in the first line will continue to be used as a threshold for the log (across all databases). You can also set this parameter by restarting MongoDB with the --slowms option.

Finally, set up a cron job that rotates your log every day or week. If MongoDB was started with --logpath, sending the process a SIGUSR1 signal will make it rotate the log. There is also a logRotate command that does the same thing:

> db.adminCommand({"logRotate" : 1})

You cannot rotate logs if MongoDB was not started with --logpath.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.174.156