19

I've been investigating creating my own mongodb cluster in AWS. Aws mongodb template provides some good starting points. However, it doesn't cover auto scaling or when a node goes down. For example, if I have 1 primary and 2 secondary nodes. And the primary goes down and auto scaling kicks in. How would I add the newly launched mongodb instance to the replica set?

If you look at the template, it uses an init.sh script to check if the node being launched is a primary node and waits for all other nodes to exist and creates a replica set with thier ip addresses on the primary. When the Replica set is configured initailly, all the nodes already exist.

Not only that, but my node app uses mongoose. Part of the database connection allows you to specify multiple nodes. How would I keep track of what's currently up and running (I guess I could use DynamoDB but not sure).

What's the usual flow if an instance goes down? Do people generally manually re-configure clusters if this happens?

Any thoughts? Thanks.

Sun
  • 2,658
  • 6
  • 28
  • 33

1 Answers1

45

This is a very good question and I went through this very painful journey myself recently. I am writing a fairly extensive answer here in the hope that some of these thoughts of running a MongoDB cluster via CloudFormation are useful to others.

I'm assuming that you're creating a MongoDB production cluster as follows: -

  • 3 config servers (micros/smalls instances can work here)
  • At least 1 shard consisting of e.g. 2 (primary & secondary) shard instances (minimum or large) with large disks configured for data / log / journal disks.
  • arbiter machine for voting (micro probably OK).

i.e. https://docs.mongodb.org/manual/core/sharded-cluster-architectures-production/

Like yourself, I initially tried the AWS MongoDB CloudFormation template that you posted in the link (https://s3.amazonaws.com/quickstart-reference/mongodb/latest/templates/MongoDB-VPC.template) but to be honest it was far, far too complex i.e. it's 9,300 lines long and sets up multiple servers (i.e. replica shards, configs, arbitors, etc). Running the CloudFormation template took ages and it kept failing (e.g. after 15 mintues) which meant the servers all terminated again and I had to try again which was really frustrating / time consuming.

The solution I went for in the end (which I'm super happy with) was to create separate templates for each type of MongoDB server in the cluster e.g.

  1. MongoDbConfigServer.template (template to create config servers - run this 3 times)
  2. MongoDbShardedReplicaServer.template (template to create replica - run 2 times for each shard)
  3. MongoDbArbiterServer.template (template to create arbiter - run once for each shard)

NOTE: templates available at https://github.com/adoreboard/aws-cloudformation-templates

The idea then is to bring up each server in the cluster individually i.e. 3 config servers, 2 sharded replica servers (for 1 shard) and an arbitor. You can then add custom parameters into each of the templates e.g. the parameters for the replica server could include: -

  • InstanceType e.g. t2.micro
  • ReplicaSetName e.g. s1r (shard 1 replica)
  • ReplicaSetNumber e.g. 2 (used with ReplicaSetName to create name e.g. name becomes s1r2)
  • VpcId e.g. vpc-e4ad2b25 (not a real VPC obviously!)
  • SubnetId e.g. subnet-2d39a157 (not a real subnet obviously!)
  • GroupId (name of existing MongoDB group Id)
  • Route53 (boolean to add a record to an internal DNS - best practices)
  • Route53HostedZone (if boolean is true then ID of internal DNS using Route53)

The really cool thing about CloudFormation is that these custom parameters can have (a) a useful description for people running it, (b) special types (e.g. when running creates a prefiltered combobox so mistakes are harder to make) and (c) default values. Here's an example: -

    "Route53HostedZone": {
        "Description": "Route 53 hosted zone for updating internal DNS (Only applicable if the parameter [ UpdateRoute53 ] = \"true\"",
        "Type": "AWS::Route53::HostedZone::Id",
        "Default": "YA3VWJWIX3FDC"
    },

This makes running the CloudFormation template an absolute breeze as a lot of the time we can rely on the default values and only tweak a couple of things depending on the server instance we're creating (or replacing).

As well as parameters, each of the 3 templates mentioned earlier have a "Resources" section which creates the instance. We can do cool things via the "AWS::CloudFormation::Init" section also. e.g.

"Resources": {

    "MongoDbConfigServer": {
        "Type": "AWS::EC2::Instance",
        "Metadata": {
            "AWS::CloudFormation::Init": {
                "configSets" : {
                    "Install" : [ "Metric-Uploading-Config", "Install-MongoDB", "Update-Route53" ]
                },

The "configSets" in the previous example shows that creating a MongoDB server isn't simply a matter of creating an AWS instance and installing MongoDB on it but also we can (a) install CloudWatch disk / memory metrics (b) Update Route53 DNS etc. The idea is you want to automate things like DNS / Monitoring etc as much as possible.

IMO, creating a template, and therefore a stack for each server has the very nice advantage of being able to replace a server extremely quickly via the CloudFormation web console. Also, because we have a server-per-template it's easy to build the MongoDB cluster up bit by bit.

My final bit of advice on creating the templates would be to copy what works for you from other GitHub MongoDB CloudFormation templates e.g. I used the following to create the replica servers to use RAID10 (instead of the massively more expensive AWS provisioned IOPS disks).

https://github.com/CaptainCodeman/mongo-aws-vpc/blob/master/src/templates/mongo-master.template

In your question you mentioned auto-scaling - my preference would be to add a shard / replace a broken instance manually (auto-scaling makes sense with web containers e.g. Tomcat / Apache but a MongoDB cluster should really grow slowly over time). However, monitoring is very important, especially the disk sizes on the shard servers to alert you when disks are filling up (so you can either add a new shard to delete data). Monitoring can be achieved fairly easily using AWS CloudWatch metrics / alarms or using the MongoDB MMS service.

If a node goes down e.g one of the replicas in a shard, then you can simply kill the server, recreate it using your CloudFormation template and the disks will sync across automatically. This is my normal flow if an instance goes down and generally no re-configuration is necessary. I've wasted far too many hours in the past trying to fix servers - sometimes lucky / sometimes not. My backup strategy now is run a mongodump of the important collections of the database once a day via a crontab, zip up and upload to AWS S3. This means if the nuclear option happens (complete database corruption) we can recreate the entire database and mongorestore in an hour or 2.

However, if you create a new shard (because you're running out of space) configuration is necessary. For example, if you are adding a new Shard 3 you would create 2 replica nodes (e.g. primary with name => mongo-s3r1 / secondary with name => mongo-s3r2) and 1 arbitor (e.g. with name mongo-s3r-arb) then you'd connect via a MongoDB shell to a mongos (MongoDB router) and run this command: -

sh.addShard("s3r/mongo-s3r1.internal.mycompany.com:27017,mongo-s3r2.internal.mycompany.com:27017")

NOTE: - This commands assumes you are using private DNS via Route53 (best practice). You can simply use the private IPs of the 2 replicas in the addShard command but I have been very badly burned with this in the past (e.g. serveral months back all the AWS instances were restarted and new private IPs generated for all of them. Fixing the MongoDB cluster took me 2 days as I had to reconfigure everything manually - whereas changing the IPs in Route53 takes a few seconds ... ;-)

You could argue we should also add the addShard command to another CloudFormation template but IMO this adds unnecessary complexity because it has to know about a server which has a MongoDB router (mongos) and connect to that to run the addShard command. Therefore I simply run this after the instances in a new MongoDB shard have been created.

Anyways, that's my rather rambling thoughts on the matter. The main thing is that once you have the templates in place your life becomes much easier and defo worth the effort! Best of luck! :-)

bobmarksie
  • 3,282
  • 1
  • 41
  • 54
  • Thanks for the very detail explanation, I'll definatly give this a go at some point. I ended up going with a hosted solution for the time being because solving this problem isn't easy and can be time consuming, but you have some very good advice here that I would like to revisit. I must admit, the config provided by aws is very complicated. – Sun Jan 11 '16 at 11:03
  • There is advantages and disadvantages to a hosted solution (easy of use, quicker to deploy) vs hosting it yourself (power, control, potentional cheaper total cost of ownership etc). I have done both and both are appropriate in different scenarios. CloudFormation templates are tricky with a hefty learning curve (and AWS Dev-Ops in general) but well worth it! The main advantages of using CloudFormation templates to bring up servers, install and configure software include (a) repeatability (b) infrastructure-as-code i.e. enables code reviews (c) reliability etc. – bobmarksie Jan 11 '16 at 13:32
  • I ran into the same problems while trying to use the template provided by the AWS MongoDB Quickstart... it just took a long time and failed with little or no feedback. I like your approach, @bobmarksie it offers more control. Is there anywhere we can access the mentioned templates? (`MongoDbConfigServer.template`, `MongoDbShardedReplicaServer.template` and `MongoDbArbiterServer.template`) – monsieurBelbo Jan 28 '16 at 13:02
  • Hi @monsieurBelbo - I've uploaded the templates to here - github.com/adoreboard/aws-cloudformation-templates. NOTE: there are no templates for creating (1) a VPC, (2) Security Groups, (3) Route53 private zone & (4) IAM Role for monitoring. If these don't already exist you can create these manually or via more CloudFormation templates. Otherwise, you can simply tweak the templates to suit your use case. I might do an indepth blog article on creating a MongoDB cluster this way when I get a free minute! If you've any tips for improvement I'll be happy to hear! Good luck. – bobmarksie Jan 31 '16 at 13:48
  • @bobmarksie , this is awesome! I've researched a solution for the whole day that. And I decided to give up because I thought that `if I use AWS auto-scaling I never scale a MongoDB server or a cluster. Thereby auto-scaling is not an effective way to scale technical infrastructure -which has a database server`. And now your solution is in front of me! On the other hand, I try to accomplish a secure AWS infrastructure for my app (as cheap as possible). I think your solution is not appropriate to a start-up that has not enough investment. Also there is no data yet. What do you think about that? – efkan Oct 03 '16 at 14:07
  • @efkan - depends on how much money the startup has :-) A single MongoDB machine may be totally fine for lots of startups. It's only when the system must be totally reliable is when you need a production system (i.e. minimum 2 replicas, + 1 arbitor + 3 config servers). This setup provides "no single point of failure". However you can do this later i.e. when you have more money and the cost of annoying a customer with a broken experience could cost more than the AWS server costs. – bobmarksie Oct 03 '16 at 14:25
  • Thanks @bobmarksie . Appearantly AWS AutoScaling is too early for me :) Also I use Nginx reverse-proxy load balancer on top of my infrastructure. It might be complex to manage too. – efkan Oct 03 '16 at 14:52