├── .ansiblespec ├── .gitignore ├── 01_create_cluster.sh ├── 01_create_cluster.yml ├── 02_update_cluster.sh ├── 02_update_cluster.yml ├── LICENSE.md ├── README.md ├── Rakefile ├── Vagrantfile ├── group_vars └── all ├── hosts ├── images └── replica_set.png ├── roles ├── common │ ├── files │ │ ├── 10gen.repo.j2 │ │ ├── RPM-GPG-KEY-EPEL-7 │ │ ├── epel.repo.j2 │ │ └── tuned.conf │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── hosts.j2 ├── mongod_base │ ├── files │ │ ├── mongod.service │ │ ├── mongod_sysconfig │ │ ├── mongod_tempfiles.conf │ │ └── setreadahead.service │ ├── spec │ │ └── mongod_spec.rb │ └── tasks │ │ └── main.yml ├── mongod_primary │ ├── spec │ │ └── mongod_spec.rb │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── mongod_unauthenticated.conf.j2 │ │ └── repset_init.j2 ├── mongod_replicaset │ ├── files │ │ └── secret │ ├── handlers │ │ └── main.yml │ ├── spec │ │ └── mongod_spec.rb │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── mongod.conf.j2 ├── mongod_replicaset_arbiters │ ├── spec │ │ └── mongod_spec.rb │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── repset_addArbiter.j2 │ │ └── repset_init.j2 └── mongod_replicaset_slaves │ ├── spec │ └── mongod_spec.rb │ ├── tasks │ └── main.yml │ └── templates │ ├── repset_add.j2 │ └── repset_init.j2 └── spec └── spec_helper.rb /.ansiblespec: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | playbook: 01_create_cluster.yml 4 | inventory: hosts 5 | 6 | - 7 | playbook: 02_update_cluster.yml 8 | inventory: hosts 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vagrant 2 | -------------------------------------------------------------------------------- /01_create_cluster.sh: -------------------------------------------------------------------------------- 1 | ansible-playbook -i hosts 01_create_cluster.yml -u root -k 2 | -------------------------------------------------------------------------------- /01_create_cluster.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This Playbook would deploy the whole mongodb cluster with replication. 3 | - name: common 4 | hosts: all 5 | roles: 6 | - role: common 7 | 8 | - name: Install MongoDB 9 | hosts: mongo_servers 10 | roles: 11 | - role: mongod_base 12 | 13 | - name: Setup the Primary Node 14 | hosts: mongod_primary 15 | roles: 16 | - role: mongod_primary 17 | 18 | - name: Configure the replicaset security across all nodes 19 | hosts: mongo_servers 20 | roles: 21 | - role: mongod_replicaset 22 | 23 | - name: Add the nodes to the cluster 24 | hosts: mongod_slaves 25 | serial: 1 26 | roles: 27 | - role: mongod_replicaset_slaves 28 | 29 | - name: Add mongo arbiters to the cluster 30 | hosts: mongod_arbiters 31 | serial: 1 32 | roles: 33 | - role: mongod_replicaset_arbiters 34 | -------------------------------------------------------------------------------- /02_update_cluster.sh: -------------------------------------------------------------------------------- 1 | ansible-playbook -i hosts 02_update_cluster.yml -u root -k 2 | -------------------------------------------------------------------------------- /02_update_cluster.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This Playbook would deploy the whole mongodb cluster with replication. 3 | - name: common 4 | hosts: all 5 | roles: 6 | - role: common 7 | 8 | - name: Install MongoDB 9 | hosts: mongo_servers 10 | roles: 11 | - role: mongod_base 12 | 13 | - name: Configure the replicaset security across all nodes 14 | hosts: mongo_servers 15 | roles: 16 | - role: mongod_replicaset 17 | 18 | - name: Add the nodes to the cluster 19 | hosts: mongod_slaves 20 | serial: 1 21 | roles: 22 | - role: mongod_replicaset_slaves 23 | 24 | - name: Add mongo arbiters to the cluster 25 | hosts: mongod_arbiters 26 | serial: 1 27 | roles: 28 | - role: mongod_replicaset_arbiters 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 AnsibleWorks, Inc. 2 | 3 | This work is licensed under the Creative Commons Attribution 3.0 Unported License. 4 | To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/deed.en_US. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Deploying a MongoDB cluster with Ansible 2 | 3 | - Tested with Ansible 1.9.4 4 | - Expects CentOS/RHEL 7 hosts 5 | 6 | ### Data Replication 7 | 8 | ![Alt text](images/replica_set.png "Replica Set") 9 | 10 | Data backup is achieved in MongoDB via _replica sets_. As the figure above shows, 11 | a single replication set consists of a replication master (active) and several 12 | other replications slaves (passive). All the database operations like 13 | add/delete/update happen on the replication master and the master replicates 14 | the data to the slave nodes. _mongod_ is the process which is responsible for all 15 | the database activities as well as replication processes. The minimum 16 | recommended number of slave servers are 3. 17 | 18 | ### Deploying MongoDB Ansible 19 | 20 | #### Prerequisites 21 | 22 | Edit the group_vars/all file to reflect the below variables. 23 | 24 | - Use the provided Vagrant file with VirtualBox to create servers to host the 25 | cluster and edit your hosts file to include your new servers, e.g.: 26 | 27 | 10.0.0.101 mongo1 28 | 10.0.0.102 mongo2 29 | 10.0.0.103 mongo3 30 | 10.0.0.104 mongo4 31 | 10.0.0.105 mongo4 32 | 33 | - If you decide to use some other virtual machines, update the name of the 34 | ethernet adaptor (iface variable) in the /group_vars/all file and ensure that 35 | ports 22 and 27017 are accessible. 36 | 37 | enp0s8 # the interface to be used for all communication. 38 | 39 | - The default directory for storing data is /data, please change it if 40 | required. Make sure it has sufficient space: 10G is recommended. 41 | 42 | - The secret file at /roles/mongod_replicaset/secret should be replaced with 43 | a custom secret, generated by the following command. See documentation at 44 | https://docs.mongodb.org/manual/tutorial/generate-key-file/ 45 | 46 | openssl rand -base64 741 > secret 47 | 48 | ### Deployment Example 49 | 50 | The inventory file looks as follows: 51 | 52 | [mongo_servers] 53 | mongo1 mongod_port=27017 54 | mongo2 mongod_port=27017 55 | mongo3 mongod_port=27017 56 | mongo4 mongod_port=27017 57 | mongo5 mongod_port=27017 58 | 59 | [mongod_primary] 60 | mongo1 mongod_port=27017 61 | 62 | [mongod_slaves] 63 | mongo2 mongod_port=27017 64 | mongo3 mongod_port=27017 65 | mongo4 mongod_port=27017 66 | 67 | [mongod_arbiters] 68 | mongo5 69 | 70 | Build the site with the following command: 71 | 72 | ansible-playbook -i hosts 01_create_cluster.yml -u root -k 73 | 74 | #### Verifying the Deployment 75 | 76 | Once configuration and deployment has completed we can check replication set 77 | availability by connecting to individual primary replication set nodes: 78 | 79 | mongo --host mongo1 --port 27017 80 | 81 | When connected, issue the following commands to query the status of the 82 | replication set and you should get a similar output. 83 | 84 | mongo_replication:PRIMARY> use admin 85 | switched to db admin 86 | mongo_replication:PRIMARY> db.auth("admin", "123456") 87 | 1 88 | mongo_replication:PRIMARY> rs.status() 89 | { 90 | "set" : "mongo_replication", 91 | "date" : ISODate("2015-10-21T19:43:54.932Z"), 92 | "myState" : 1, 93 | "members" : [ 94 | { 95 | "_id" : 0, 96 | "name" : "mongo1:27017", 97 | "health" : 1, 98 | "state" : 1, 99 | "stateStr" : "PRIMARY", 100 | "uptime" : 100, 101 | "optime" : Timestamp(1445456545, 1), 102 | "optimeDate" : ISODate("2015-10-21T19:42:25Z"), 103 | "electionTime" : Timestamp(1445456537, 2), 104 | "electionDate" : ISODate("2015-10-21T19:42:17Z"), 105 | "configVersion" : 5, 106 | "self" : true 107 | }, 108 | { 109 | "_id" : 1, 110 | "name" : "mongo2:27017", 111 | "health" : 1, 112 | "state" : 2, 113 | "stateStr" : "SECONDARY", 114 | "uptime" : 96, 115 | "optime" : Timestamp(1445456545, 1), 116 | "optimeDate" : ISODate("2015-10-21T19:42:25Z"), 117 | "lastHeartbeat" : ISODate("2015-10-21T19:43:53.716Z"), 118 | "lastHeartbeatRecv" : ISODate("2015-10-21T19:43:54.473Z"), 119 | "pingMs" : 1, 120 | "lastHeartbeatMessage" : "could not find member to sync from", 121 | "configVersion" : 5 122 | }, 123 | { 124 | "_id" : 2, 125 | "name" : "mongo3:27017", 126 | "health" : 1, 127 | "state" : 2, 128 | "stateStr" : "SECONDARY", 129 | "uptime" : 94, 130 | "optime" : Timestamp(1445456545, 1), 131 | "optimeDate" : ISODate("2015-10-21T19:42:25Z"), 132 | "lastHeartbeat" : ISODate("2015-10-21T19:43:53.732Z"), 133 | "lastHeartbeatRecv" : ISODate("2015-10-21T19:43:54.906Z"), 134 | "pingMs" : 2, 135 | "lastHeartbeatMessage" : "could not find member to sync from", 136 | "configVersion" : 5 137 | }, 138 | { 139 | "_id" : 3, 140 | "name" : "mongo4:27017", 141 | "health" : 1, 142 | "state" : 2, 143 | "stateStr" : "SECONDARY", 144 | "uptime" : 92, 145 | "optime" : Timestamp(1445456545, 1), 146 | "optimeDate" : ISODate("2015-10-21T19:42:25Z"), 147 | "lastHeartbeat" : ISODate("2015-10-21T19:43:53.732Z"), 148 | "lastHeartbeatRecv" : ISODate("2015-10-21T19:43:53.184Z"), 149 | "pingMs" : 3, 150 | "syncingTo" : "mongo3:27017", 151 | "configVersion" : 5 152 | }, 153 | { 154 | "_id" : 4, 155 | "name" : "mongo5:27017", 156 | "health" : 1, 157 | "state" : 7, 158 | "stateStr" : "ARBITER", 159 | "uptime" : 89, 160 | "lastHeartbeat" : ISODate("2015-10-21T19:43:53.732Z"), 161 | "lastHeartbeatRecv" : ISODate("2015-10-21T19:43:53.840Z"), 162 | "pingMs" : 0, 163 | "configVersion" : 5 164 | } 165 | ], 166 | "ok" : 1 167 | } 168 | 169 | ### Scaling the Cluster 170 | --------------------------------------- 171 | 172 | To add a new node to the existing MongoDB Cluster, modify the inventory file 173 | (/hosts) to add a new server into the mongo_servers section and either the 174 | mongod_slaves or mongod_arbiters section. 175 | 176 | ansible-playbook -i hosts 02_update_cluster.yml -u root -k 177 | 178 | A new server can be created by updating the Vagrant file, and calling "vagrant 179 | up", remember to update your /etc/hosts file to include the new server. 180 | 181 | ###Verification 182 | 183 | The newly added node can be easily verified by checking the replication status 184 | and seeing the data being copied to the newly added node. 185 | 186 | ###Serverspec 187 | 188 | Verify the servers using serverspec with ansible_spec 189 | 190 | $gem install ansible_spec 191 | $rake T 192 | rake serverspec:common 193 | rake serverspec:mongod 194 | $rake serverspec:mongod 195 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rspec/core/rake_task' 3 | require 'yaml' 4 | require 'ansible_spec' 5 | 6 | properties = AnsibleSpec.get_properties 7 | 8 | namespace :serverspec do 9 | properties.each do |property| 10 | property["hosts"].each do |host| 11 | desc "Run serverspec for #{property["name"]}" 12 | RSpec::Core::RakeTask.new(property["name"].to_sym) do |t| 13 | puts "Run serverspec for #{property["name"]} to #{host}" 14 | if host.instance_of?(Hash) 15 | ENV['TARGET_HOST'] = host["uri"] 16 | ENV['TARGET_PORT'] = host["port"].to_s 17 | ENV['TARGET_PRIVATE_KEY'] = host["private_key"] 18 | unless host["user"].nil? 19 | ENV['TARGET_USER'] = host["user"] 20 | else 21 | ENV['TARGET_USER'] = property["user"] 22 | end 23 | else 24 | ENV['TARGET_HOST'] = host 25 | ENV['TARGET_PRIVATE_KEY'] = '~/.ssh/id_rsa' 26 | ENV['TARGET_USER'] = property["user"] 27 | end 28 | t.pattern = 'roles/{' + property["roles"].join(',') + '}/spec/*_spec.rb' 29 | end 30 | end 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | config.vm.box = "bento/centos-7.1" 10 | config.ssh.insert_key = false 11 | 12 | # Primary data centre 13 | config.vm.define "mongo_dc1_1" do |server| 14 | server.vm.network :forwarded_port, host: 27017, guest: 27017 15 | server.vm.network :forwarded_port, host: 27018, guest: 22 16 | server.vm.network "private_network", ip: "10.0.0.101", :netmask => "255.255.0.0" 17 | server.vm.hostname = "mongo1" 18 | end 19 | 20 | config.vm.define "mongo_dc1_2" do |server| 21 | server.vm.network :forwarded_port, host: 27019, guest: 27017 22 | server.vm.network :forwarded_port, host: 27020, guest: 22 23 | server.vm.network "private_network", ip: "10.0.0.102", :netmask => "255.255.0.0" 24 | server.vm.hostname = "mongo2" 25 | end 26 | 27 | config.vm.define "mongo_dc2_1" do |server| 28 | server.vm.network :forwarded_port, host: 27021, guest: 27017 29 | server.vm.network :forwarded_port, host: 27022, guest: 22 30 | server.vm.network "private_network", ip: "10.0.0.103", :netmask => "255.255.0.0" 31 | server.vm.hostname = "mongo3" 32 | end 33 | 34 | config.vm.define "mongo_dc2_2" do |server| 35 | server.vm.network :forwarded_port, host: 27023, guest: 27017 36 | server.vm.network :forwarded_port, host: 27024, guest: 22 37 | server.vm.network "private_network", ip: "10.0.0.104", :netmask => "255.255.0.0" 38 | server.vm.hostname = "mongo4" 39 | end 40 | 41 | config.vm.define "mongo_dc3_1" do |server| 42 | server.vm.network :forwarded_port, host: 27025, guest: 27017 43 | server.vm.network :forwarded_port, host: 27026, guest: 22 44 | server.vm.network "private_network", ip: "10.0.0.105", :netmask => "255.255.0.0" 45 | server.vm.hostname = "mongo5" 46 | end 47 | 48 | # config.vm.define "mongo_dc3_2" do |server| 49 | # server.vm.network :forwarded_port, host: 27027, guest: 27017 50 | # server.vm.network :forwarded_port, host: 27028, guest: 22 51 | # server.vm.network "private_network", ip: "10.0.0.106", :netmask => "255.255.0.0" 52 | # server.vm.hostname = "mongo6" 53 | # end 54 | 55 | config.vm.provision :shell, inline: "systemctl enable firewalld" 56 | config.vm.provision :shell, inline: "systemctl start firewalld" 57 | 58 | config.vm.provider "virtualbox" do |v| 59 | v.gui = false 60 | memory = "1024" 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /group_vars/all: -------------------------------------------------------------------------------- 1 | # The global variable file mongodb installation 2 | 3 | # The chunksize for shards in MB 4 | mongos_chunk_size: 1 5 | 6 | # The port in which mongos server should listen on 7 | mongos_port: 8888 8 | 9 | # The port for mongo config server 10 | mongoc_port: 7777 11 | 12 | # The directory prefix where the database files would be stored 13 | mongodb_datadir_prefix: /data/ 14 | 15 | # The interface where the mongodb process should listen on. 16 | # Defaults to the first interface. Change this to: 17 | # 18 | # iface: eth1 19 | # 20 | # ...to override. 21 | # 22 | # iface: '{{ ansible_default_ipv4.interface }}' 23 | iface: enp0s8 24 | 25 | # The password for admin user 26 | mongo_admin_pass: 123456 27 | 28 | # The name of the replication set 29 | replica_set_name: mongo_replication 30 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | # The site wide list of mongodb servers 2 | 3 | [mongo_servers] 4 | mongo1 mongod_port=27017 5 | mongo2 mongod_port=27017 6 | mongo3 mongod_port=27017 7 | mongo4 mongod_port=27017 8 | mongo5 mongod_port=27017 9 | # Remember to add the machine to your /etc/hosts file 10 | # mongo6 mongod_port=27017 11 | 12 | [mongod_primary] 13 | mongo1 mongod_port=27017 14 | 15 | [mongod_slaves] 16 | mongo2 mongod_port=27017 17 | mongo3 mongod_port=27017 18 | mongo4 mongod_port=27017 19 | # mongo6 mongod_port=27017 20 | 21 | [mongod_arbiters] 22 | mongo5 mongod_port=27017 23 | -------------------------------------------------------------------------------- /images/replica_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-h/ansible-mongodb-cluster/132c2a5ad7e9175d38670982fd5902f282169379/images/replica_set.png -------------------------------------------------------------------------------- /roles/common/files/10gen.repo.j2: -------------------------------------------------------------------------------- 1 | [mongodb-org-3.0] 2 | name=MongoDB Repository 3 | baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.0/x86_64/ 4 | gpgcheck=0 5 | enabled=1 6 | -------------------------------------------------------------------------------- /roles/common/files/RPM-GPG-KEY-EPEL-7: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v1.4.11 (GNU/Linux) 3 | 4 | mQINBFKuaIQBEAC1UphXwMqCAarPUH/ZsOFslabeTVO2pDk5YnO96f+rgZB7xArB 5 | OSeQk7B90iqSJ85/c72OAn4OXYvT63gfCeXpJs5M7emXkPsNQWWSju99lW+AqSNm 6 | jYWhmRlLRGl0OO7gIwj776dIXvcMNFlzSPj00N2xAqjMbjlnV2n2abAE5gq6VpqP 7 | vFXVyfrVa/ualogDVmf6h2t4Rdpifq8qTHsHFU3xpCz+T6/dGWKGQ42ZQfTaLnDM 8 | jToAsmY0AyevkIbX6iZVtzGvanYpPcWW4X0RDPcpqfFNZk643xI4lsZ+Y2Er9Yu5 9 | S/8x0ly+tmmIokaE0wwbdUu740YTZjCesroYWiRg5zuQ2xfKxJoV5E+Eh+tYwGDJ 10 | n6HfWhRgnudRRwvuJ45ztYVtKulKw8QQpd2STWrcQQDJaRWmnMooX/PATTjCBExB 11 | 9dkz38Druvk7IkHMtsIqlkAOQMdsX1d3Tov6BE2XDjIG0zFxLduJGbVwc/6rIc95 12 | T055j36Ez0HrjxdpTGOOHxRqMK5m9flFbaxxtDnS7w77WqzW7HjFrD0VeTx2vnjj 13 | GqchHEQpfDpFOzb8LTFhgYidyRNUflQY35WLOzLNV+pV3eQ3Jg11UFwelSNLqfQf 14 | uFRGc+zcwkNjHh5yPvm9odR1BIfqJ6sKGPGbtPNXo7ERMRypWyRz0zi0twARAQAB 15 | tChGZWRvcmEgRVBFTCAoNykgPGVwZWxAZmVkb3JhcHJvamVjdC5vcmc+iQI4BBMB 16 | AgAiBQJSrmiEAhsPBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBqL66iNSxk 17 | 5cfGD/4spqpsTjtDM7qpytKLHKruZtvuWiqt5RfvT9ww9GUUFMZ4ZZGX4nUXg49q 18 | ixDLayWR8ddG/s5kyOi3C0uX/6inzaYyRg+Bh70brqKUK14F1BrrPi29eaKfG+Gu 19 | MFtXdBG2a7OtPmw3yuKmq9Epv6B0mP6E5KSdvSRSqJWtGcA6wRS/wDzXJENHp5re 20 | 9Ism3CYydpy0GLRA5wo4fPB5uLdUhLEUDvh2KK//fMjja3o0L+SNz8N0aDZyn5Ax 21 | CU9RB3EHcTecFgoy5umRj99BZrebR1NO+4gBrivIfdvD4fJNfNBHXwhSH9ACGCNv 22 | HnXVjHQF9iHWApKkRIeh8Fr2n5dtfJEF7SEX8GbX7FbsWo29kXMrVgNqHNyDnfAB 23 | VoPubgQdtJZJkVZAkaHrMu8AytwT62Q4eNqmJI1aWbZQNI5jWYqc6RKuCK6/F99q 24 | thFT9gJO17+yRuL6Uv2/vgzVR1RGdwVLKwlUjGPAjYflpCQwWMAASxiv9uPyYPHc 25 | ErSrbRG0wjIfAR3vus1OSOx3xZHZpXFfmQTsDP7zVROLzV98R3JwFAxJ4/xqeON4 26 | vCPFU6OsT3lWQ8w7il5ohY95wmujfr6lk89kEzJdOTzcn7DBbUru33CQMGKZ3Evt 27 | RjsC7FDbL017qxS+ZVA/HGkyfiu4cpgV8VUnbql5eAZ+1Ll6Dw== 28 | =hdPa 29 | -----END PGP PUBLIC KEY BLOCK----- 30 | -------------------------------------------------------------------------------- /roles/common/files/epel.repo.j2: -------------------------------------------------------------------------------- 1 | [epel] 2 | name=Extra Packages for Enterprise Linux 7 - $basearch 3 | #baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch 4 | mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearch 5 | failovermethod=priority 6 | enabled=1 7 | gpgcheck=1 8 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 9 | 10 | [epel-debuginfo] 11 | name=Extra Packages for Enterprise Linux 7 - $basearch - Debug 12 | #baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch/debug 13 | mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-7&arch=$basearch 14 | failovermethod=priority 15 | enabled=0 16 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 17 | gpgcheck=1 18 | 19 | [epel-source] 20 | name=Extra Packages for Enterprise Linux 7 - $basearch - Source 21 | #baseurl=http://download.fedoraproject.org/pub/epel/7/SRPMS 22 | mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-7&arch=$basearch 23 | failovermethod=priority 24 | enabled=0 25 | gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 26 | gpgcheck=1 27 | -------------------------------------------------------------------------------- /roles/common/files/tuned.conf: -------------------------------------------------------------------------------- 1 | [main] 2 | include=virtual-guest 3 | 4 | [vm] 5 | transparent_hugepages=never 6 | -------------------------------------------------------------------------------- /roles/common/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Handler for mongod 3 | -------------------------------------------------------------------------------- /roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This Playbook runs all the common plays in the deployment 3 | - name: Create the hosts file for all machines 4 | template: src=hosts.j2 dest=/etc/hosts 5 | 6 | - name: Create the repository for 10Gen 7 | copy: src=10gen.repo.j2 dest=/etc/yum.repos.d/10gen.repo 8 | 9 | - name: Create the EPEL Repository. 10 | copy: src=epel.repo.j2 dest=/etc/yum.repos.d/epel.repo 11 | 12 | - name: Create the GPG key for EPEL 13 | copy: src=RPM-GPG-KEY-EPEL-7 dest=/etc/pki/rpm-gpg 14 | 15 | - name: Create the mongod user 16 | user: name=mongod comment="MongoD" 17 | 18 | - name: Create the data directory for the namenode metadata 19 | file: path={{ mongodb_datadir_prefix }} owner=mongod group=mongod state=directory 20 | 21 | - name: Install the mongodb package 22 | yum: name={{ item }} state=installed 23 | with_items: 24 | - libselinux-python 25 | - mongodb-org 26 | - mongodb-org-server 27 | - bc 28 | - python-pip 29 | - ntp 30 | 31 | - name: Enable NTPD 32 | service: name=ntpd enabled=yes state=started 33 | 34 | - name: Install the latest pymongo package 35 | pip: name=pymongo state=latest use_mirrors=no 36 | 37 | # Disable transparent_hugepages 38 | - name: Disable transparent_hugepages, create the profile directory for tuned (1/3) 39 | file: path=/etc/tuned/no-thp state=directory 40 | 41 | - name: Copy the tuned.conf to the new directory (2/3) 42 | copy: src=tuned.conf dest=/etc/tuned/no-thp 43 | 44 | - name: Enable the new profile 45 | command: tuned-adm profile no-thp 46 | -------------------------------------------------------------------------------- /roles/common/templates/hosts.j2: -------------------------------------------------------------------------------- 1 | 127.0.0.1 localhost {{ ansible_hostname }} 2 | {% for host in groups['all'] %} 3 | {{ hostvars[host]['ansible_' + iface].ipv4.address }} {{ host }} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /roles/mongod_base/files/mongod.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=High-performance, schema-free document-oriented database 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | Type=forking 7 | User=mongod 8 | Group=mongod 9 | PIDFile=/var/run/mongo/mongod.pid 10 | EnvironmentFile=/etc/sysconfig/mongod 11 | ExecStart=/bin/mongod $OPTIONS run 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /roles/mongod_base/files/mongod_sysconfig: -------------------------------------------------------------------------------- 1 | OPTIONS="--quiet -f /etc/mongod.conf" 2 | -------------------------------------------------------------------------------- /roles/mongod_base/files/mongod_tempfiles.conf: -------------------------------------------------------------------------------- 1 | d /var/run/mongo 0755 mongod mongod 2 | -------------------------------------------------------------------------------- /roles/mongod_base/files/setreadahead.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Set readahead 3 | After=syslog.target network.target 4 | Before=mongod.service 5 | 6 | [Service] 7 | Type=oneshot 8 | ExecStart=/usr/bin/sudo /usr/sbin/blockdev --setra 32 /dev/mapper/centos-root 9 | User=root 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /roles/mongod_base/spec/mongod_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'firewalld should be enabled and running' do 4 | describe service('firewalld') do 5 | it { should be_running } 6 | it { should be_enabled } 7 | end 8 | end 9 | 10 | describe 'mongod should be enabled and running' do 11 | describe service('mongod') do 12 | it { should be_enabled } 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /roles/mongod_base/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable the mongod port. 3 | firewalld: port={{ mongod_port }}/tcp permanent=true state=enabled immediate=true 4 | 5 | # This role deploys the mongod processes. 6 | - name: create data directory for mongodb data members 7 | file: path={{ mongodb_datadir_prefix }}/db state=directory owner=mongod group=mongod 8 | 9 | - name: create arbiter directory for mongodb arbiters 10 | file: path={{ mongodb_datadir_prefix }}/arb state=directory owner=mongod group=mongod 11 | 12 | - name: create log directory for mongodb 13 | file: path=/var/log/mongodb state=directory owner=mongod group=mongod 14 | 15 | - name: Create run directory for mongodb - Make sure that the run temp file directory gets created by systemd on reboot 16 | copy: src=mongod_tempfiles.conf dest=/lib/tmpfiles.d/mongod_tempfiles.conf 17 | 18 | - name: Create run directory - do it now 19 | command: systemd-tmpfiles --create mongod_tempfiles.conf 20 | 21 | # http://tom-chapman.uk/2012/12/28/installing-mongodb-on-a-linux-distro-using-systemd-instead-of-inittab/ 22 | - name: Copy the systemd init file 23 | copy: src=mongod.service dest=/lib/systemd/system/mongod.service 24 | 25 | - name: Copy the mongod sysconfig file 26 | copy: src=mongod_sysconfig dest=/etc/sysconfig/mongod 27 | 28 | - name: Set up the mongodb service using systemd 29 | command: systemctl --system daemon-reload 30 | command: systemctl enable mongod.service 31 | 32 | # Use the blockdev command to set the readahead to 32 to increase performance 33 | - name: Set readahead on system startup 34 | copy: src=setreadahead.service dest=/lib/systemd/system/setreadahead.service 35 | 36 | - name: Set up the blockdev script to run every time the system starts using systemd 37 | command: systemctl --system daemon-reload 38 | command: systemctl enable setreadahead.service 39 | 40 | - name: Run it 41 | service: name=setreadahead.service enabled=yes state=restarted 42 | -------------------------------------------------------------------------------- /roles/mongod_primary/spec/mongod_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'firewalld should be enabled and running' do 4 | describe service('firewalld') do 5 | it { should be_running } 6 | it { should be_enabled } 7 | end 8 | end 9 | 10 | describe 'mongod should be enabled and running' do 11 | describe service('mongod') do 12 | it { should be_running } 13 | it { should be_enabled } 14 | end 15 | end 16 | 17 | describe 'Should be listening on mongo port' do 18 | describe port(27017) do 19 | it { should be_listening } 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /roles/mongod_primary/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # We need to start mongo without authentication or a keyfile enabled first, 3 | # otherwise it's not possible to configure the users, so the first version 4 | # of the configuration won't have authentication enabled. 5 | - name: Create the unauthenticated mongodb configuration file 6 | template: src=mongod_unauthenticated.conf.j2 dest=/etc/mongod.conf 7 | 8 | - name: Enable at boot and start mongodb 9 | service: name=mongod.service enabled=yes state=started 10 | 11 | - name: Add the admin user to the replicaset 12 | mongodb_user: database=admin name=admin password={{ mongo_admin_pass }} login_port={{ mongod_port }} roles='root' state=present 13 | ignore_errors: yes 14 | 15 | - name: Create the file to initialize the replicaset 16 | template: src=repset_init.j2 dest=/tmp/repset_init.js 17 | 18 | - name: Initialize the replication set on the primary 19 | shell: /usr/bin/mongo -u admin -p {{ mongo_admin_pass }} --authenticationDatabase admin /tmp/repset_init.js 20 | run_once: true 21 | -------------------------------------------------------------------------------- /roles/mongod_primary/templates/mongod_unauthenticated.conf.j2: -------------------------------------------------------------------------------- 1 | # mongod.conf 2 | 3 | # for documentation of all options, see: 4 | # http://docs.mongodb.org/manual/reference/configuration-options/ 5 | 6 | # where to write logging data. 7 | systemLog: 8 | destination: file 9 | logAppend: true 10 | path: /var/log/mongodb/mongod.log 11 | 12 | # Where and how to store data. 13 | storage: 14 | dbPath: {{ mongodb_datadir_prefix }}db 15 | journal: 16 | enabled: true 17 | # engine: 18 | # mmapv1: 19 | # wiredTiger: 20 | 21 | # how the process runs 22 | processManagement: 23 | fork: true # fork and run in background 24 | pidFilePath: /var/run/mongo/mongod.pid # location of pidfile 25 | 26 | # network interfaces 27 | net: 28 | port: {{ mongod_port }} 29 | # bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. 30 | 31 | # Configure as standalone first, before converting it to a replicaset member. 32 | # replication: 33 | # replSetName: {{ replica_set_name }} 34 | -------------------------------------------------------------------------------- /roles/mongod_primary/templates/repset_init.j2: -------------------------------------------------------------------------------- 1 | rs.initiate(); 2 | printjson(rs.status()); 3 | -------------------------------------------------------------------------------- /roles/mongod_replicaset/files/secret: -------------------------------------------------------------------------------- 1 | qGO6OYb64Uth9p9Tm8s9kqarydmAg1AUdgVz+ecjinaLZ1SlWxXMY1ug8AO7C/Vu 2 | D8kA3+rE37Gv1GuZyPYi87NSfDhKXo4nJWxI00BxTBppmv2PTzbi7xLCx1+8A1uQ 3 | 4XU0HA 4 | -------------------------------------------------------------------------------- /roles/mongod_replicaset/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart mongodb 3 | service: name=mongod.service enabled=yes state=restarted 4 | -------------------------------------------------------------------------------- /roles/mongod_replicaset/spec/mongod_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'firewalld should be enabled and running' do 4 | describe service('firewalld') do 5 | it { should be_running } 6 | it { should be_enabled } 7 | end 8 | end 9 | 10 | describe 'mongod should be enabled and running' do 11 | describe service('mongod') do 12 | it { should be_running } 13 | it { should be_enabled } 14 | end 15 | end 16 | 17 | describe 'Should be listening on mongo port' do 18 | describe port(27017) do 19 | it { should be_listening } 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /roles/mongod_replicaset/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Secure replication using the secret 3 | - name: Copy the keyfile for replicated authentication 4 | copy: src=secret dest={{ mongodb_datadir_prefix }}secret owner=mongod group=mongod mode=0400 5 | notify: 6 | - Restart mongodb 7 | 8 | - name: Create the authenticated mongodb configuration file 9 | template: src=mongod.conf.j2 dest=/etc/mongod.conf 10 | notify: 11 | - Restart mongodb 12 | -------------------------------------------------------------------------------- /roles/mongod_replicaset/templates/mongod.conf.j2: -------------------------------------------------------------------------------- 1 | # mongod.conf 2 | 3 | # for documentation of all options, see: 4 | # http://docs.mongodb.org/manual/reference/configuration-options/ 5 | 6 | # where to write logging data. 7 | systemLog: 8 | destination: file 9 | logAppend: true 10 | path: /var/log/mongodb/mongod.log 11 | 12 | # Where and how to store data. 13 | storage: 14 | dbPath: {{ mongodb_datadir_prefix }}db 15 | journal: 16 | enabled: {% if 'mongod_arbiters' in group_names %}false{% else %}true{% endif %} 17 | {% if 'mongod_arbiters' in group_names %} 18 | mmapv1: 19 | smallFiles: true 20 | {% endif %} 21 | # wiredTiger: 22 | 23 | # how the process runs 24 | processManagement: 25 | fork: true # fork and run in background 26 | pidFilePath: /var/run/mongo/mongod.pid # location of pidfile 27 | 28 | # network interfaces 29 | net: 30 | port: {{ mongod_port }} 31 | # bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. 32 | 33 | security: 34 | keyFile: {{ mongodb_datadir_prefix }}secret 35 | 36 | #operationProfiling: 37 | 38 | replication: 39 | replSetName: {{ replica_set_name }} 40 | 41 | #sharding: 42 | 43 | ## Enterprise-Only Options 44 | 45 | #auditLog: 46 | 47 | #snmp: 48 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_arbiters/spec/mongod_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'firewalld should be enabled and running' do 4 | describe service('firewalld') do 5 | it { should be_running } 6 | it { should be_enabled } 7 | end 8 | end 9 | 10 | describe 'mongod should be enabled and running' do 11 | describe service('mongod') do 12 | it { should be_running } 13 | it { should be_enabled } 14 | end 15 | end 16 | 17 | describe 'Should be listening on mongo port' do 18 | describe port(27017) do 19 | it { should be_listening } 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_arbiters/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create the file to initiate the replica set 3 | template: src=repset_init.j2 dest=/tmp/repset_init.js 4 | 5 | - name: Call out and initiate the replication set, just in case it hasn't already 6 | shell: /usr/bin/mongo -u admin -p {{ mongo_admin_pass }} --authenticationDatabase admin -host {{ groups["mongod_primary"][0] }}:{{ hostvars[groups["mongod_primary"][0]]["mongod_port"] }} /tmp/repset_init.js 7 | args: 8 | creates: initiateReplicaset 9 | 10 | - name: Create the file to add the current node to the replica set as an arbiter 11 | template: src=repset_addArbiter.j2 dest=/tmp/repset_addArbiter.js 12 | 13 | - name: Add arbiter to the replication set 14 | shell: /usr/bin/mongo -u admin -p {{ mongo_admin_pass }} --authenticationDatabase admin -host {{ groups["mongod_primary"][0] }}:{{ hostvars[groups["mongod_primary"][0]]["mongod_port"] }} /tmp/repset_addArbiter.js 15 | args: 16 | creates: addArbiterToReplicaset 17 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_arbiters/templates/repset_addArbiter.j2: -------------------------------------------------------------------------------- 1 | rs.addArb("{{ ansible_hostname }}:{{ mongod_port }}"); 2 | printjson(rs.status()); 3 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_arbiters/templates/repset_init.j2: -------------------------------------------------------------------------------- 1 | rs.initiate(); 2 | printjson(rs.status()); 3 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_slaves/spec/mongod_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'firewalld should be enabled and running' do 4 | describe service('firewalld') do 5 | it { should be_running } 6 | it { should be_enabled } 7 | end 8 | end 9 | 10 | describe 'mongod should be enabled and running' do 11 | describe service('mongod') do 12 | it { should be_running } 13 | it { should be_enabled } 14 | end 15 | end 16 | 17 | describe 'Should be listening on mongo port' do 18 | describe port(27017) do 19 | it { should be_listening } 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_slaves/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Setup the replica set on the slaves 3 | - name: Create the file to initiate the replica set 4 | template: src=repset_init.j2 dest=/tmp/repset_init.js 5 | 6 | - name: Call out and initiate the replication set, just in case it hasn't already 7 | shell: /usr/bin/mongo -u admin -p {{ mongo_admin_pass }} --authenticationDatabase admin -host {{ groups["mongod_primary"][0] }}:{{ hostvars[groups["mongod_primary"][0]]["mongod_port"] }} /tmp/repset_init.js 8 | args: 9 | creates: initiateReplicaset 10 | 11 | - name: Create the file to add the current node to the replica set 12 | template: src=repset_add.j2 dest=/tmp/repset_add.js 13 | 14 | - name: Add the current node to the replication set 15 | shell: /usr/bin/mongo -u admin -p {{ mongo_admin_pass }} --authenticationDatabase admin -host {{ groups["mongod_primary"][0] }}:{{ hostvars[groups["mongod_primary"][0]]["mongod_port"] }} /tmp/repset_add.js 16 | args: 17 | creates: addNodeToReplicaset 18 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_slaves/templates/repset_add.j2: -------------------------------------------------------------------------------- 1 | rs.add("{{ ansible_hostname }}:{{ mongod_port }}"); 2 | printjson(rs.status()); 3 | -------------------------------------------------------------------------------- /roles/mongod_replicaset_slaves/templates/repset_init.j2: -------------------------------------------------------------------------------- 1 | rs.initiate(); 2 | printjson(rs.status()); 3 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'serverspec' 2 | require 'net/ssh' 3 | 4 | set :backend, :ssh 5 | 6 | if ENV['ASK_SUDO_PASSWORD'] 7 | begin 8 | require 'highline/import' 9 | rescue LoadError 10 | fail "highline is not available. Try installing it." 11 | end 12 | set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false } 13 | else 14 | set :sudo_password, ENV['SUDO_PASSWORD'] 15 | end 16 | 17 | host = ENV['TARGET_HOST'] 18 | 19 | options = Net::SSH::Config.for(host) 20 | 21 | options[:user] ||= ENV['TARGET_USER'] 22 | options[:user] ||= 'vagrant' 23 | options[:port] ||= ENV['TARGET_PORT'] 24 | options[:keys] ||= ENV['TARGET_PRIVATE_KEY'] 25 | options[:keys] ||= ['~/.vagrant.d/insecure_private_key'] 26 | 27 | set :host, options[:host_name] || host 28 | set :ssh_options, options 29 | 30 | # Disable sudo 31 | # set :disable_sudo, true 32 | 33 | 34 | # Set environment variables 35 | # set :env, :LANG => 'C', :LC_MESSAGES => 'C' 36 | 37 | # Set PATH 38 | # set :path, '/sbin:/usr/local/sbin:$PATH' 39 | --------------------------------------------------------------------------------