Skip to content

Instantly share code, notes, and snippets.

@jeffwilcox
Last active December 18, 2015 22:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffwilcox/5856302 to your computer and use it in GitHub Desktop.
Save jeffwilcox/5856302 to your computer and use it in GitHub Desktop.
Bash script for bringing online a node in a MongoDB cluster powered by Windows Azure IaaS VMs
#!/bin/bash
#
# Copyright (c) Microsoft. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# setupMongoNode.sh : MongoDB Node Configuration Script by Jeff Wilcox
#
# Target Image: OpenLogic CentOS, Windows Azure IaaS VM
#
# A specialized script specific to Windows Azure for configuring a
# MongoDB cluster (without sharding, and without anything fancy
# like RAID disks).
#
# Helps setup a primary node, join an existing cluster, or setup an
# arbiter.
#
# Optionally supports prepping, mounting and storing MongoDB data on
# an attached empty Windows Azure disk. This is recommended as you
# should get additional dedicated IOPS for that extra disk.
#
# Per the available Windows Azure performance whitepapers, it is not
# recommended to use RAID configurations for increasing IOPS or
# availability. This differs some from the standard guidance for
# using MongoDB on some other cloud providers based in the Seattle
# area, so we'll need to revisit this as more people use MongoDB
# on IaaS VMs I assume. I'm no performance expert.
#
# This script doesn't do well with error handling or restarting, so
# be sure you're ready to run it when you get going. If you need to
# try again, just delete the /etc/mongod.conf file and stop the
# mongod service if it has run before + blow away the db data.
#
# No warranties or anything implied by this script, but I do hope
# it helps!
#
echo Specialized MongoDB on Windows Azure configuration script
echo by Jeff Wilcox
echo
pushd /tmp > /dev/null
### PREREQ SOFTWARE
echo Installing Node.js...
wget https://raw.github.com/isaacs/nave/master/nave.sh > /tmp/naveNode.log 2>&1
chmod +x nave.sh
sudo ./nave.sh usemain 0.10.10 > /tmp/naveNodeUseMain.log 2>&1
# ./nave.sh install 0.10.10
# ./nave.sh use 0.10.10
nodeInstalled=$(node -v)
if [ "$nodeInstalled" != "v0.10.10" ]; then
echo Node.js could not be installed.
exit 1
fi
echo Installing Windows Azure Node.js module...
npm install azure > /tmp/nodeInstall.log 2>&1
echo Installing Windows Azure storage utility...
wget https://gist.github.com/jeffwilcox/5833947/raw/3543159943a088ac226433f471e20cecffb627ad/updown.js > /tmp/updownInstall.log 2>&1
### MONGODB
echo Adding 10gen MongoDB repos to the system...
cat > ./10gen.repo << "YUM10GEN"
[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
gpgcheck=0
enabled=1
YUM10GEN
sudo mv 10gen.repo /etc/yum.repos.d/
sudo yum update -y > /tmp/updatingRepos.log
sudo yum install -y mongo-10gen mongo-10gen-server > /tmp/installingMongo.log
### AZURE STORAGE CONFIG
if [ -z "$AZURE_STORAGE_ACCOUNT" ]; then
read -p "Windows Azure storage account name? " storageAccount
export AZURE_STORAGE_ACCOUNT=$storageAccount
echo
fi
if [ -z "$AZURE_STORAGE_ACCESS_KEY" ]; then
read -s -p "Account access key? " storageKey
export AZURE_STORAGE_ACCESS_KEY=$storageKey
echo
fi
: ${AZURE_STORAGE_ACCOUNT?"Need to set AZURE_STORAGE_ACCOUNT"}
: ${AZURE_STORAGE_ACCESS_KEY?"Need to set AZURE_STORAGE_ACCESS_KEY"}
# Awesome ask function by @davejamesmiller https://gist.github.com/davejamesmiller/1965569
function ask {
while true; do
if [ "${2:-}" = "Y" ]; then
prompt="Y/n"
default=Y
elif [ "${2:-}" = "N" ]; then
prompt="y/N"
default=N
else
prompt="y/n"
default=
fi
# Ask the question
read -p "$1 [$prompt] " REPLY
# Default?
if [ -z "$REPLY" ]; then
REPLY=$default
fi
# Check if the reply is valid
case "$REPLY" in
Y*|y*) return 0 ;;
N*|n*) return 1 ;;
esac
done
}
### VARIABLES
isPrimary=true
isArbiter=false
isUsingDataDisk=true
mongoDataPath=/var/lib/mongo
primaryPasscode=
primaryHostname=$(hostname)
### CONFIGURATION
read -p "What is the name of the replicaset? (Recommended: rs0) " replicaSetName
if [ -z "$replicaSetName" ]; then
replicaSetName=rs0
fi
replicaSetKey=$replicaSetName.key
if ! ask "Is this the first node in the replica set? "; then
isPrimary=false
if ask "Is this an arbiter?"; then
isArbiter=true
isUsingDataDisk=false
fi
echo
read -p "Primary node hostname? " primaryHostname
read -p "Primary node cluster administrator password? " -s primaryPasscode
echo
echo
fi
if ! $isArbiter; then
echo You may attach an empty data disk to this VM at any time now
echo if you would like to utilize the extra IOPS you get in such a
echo scenario. Recommended for a production instance, this is not
echo required.
echo
if ! ask "Would you like to use a data disk? "; then
isUsingDataDisk=false
fi
fi
if $isPrimary; then
echo
echo This primary VM has the hostname $primaryHostname - that will
echo be needed to bring online new nodes in the cluster.
echo
npm install node-uuid > /tmp/npm-temp.log 2>&1
echo Time to set a password for the 'clusteradmin' user. This user will not
echo directly have access to data stored in the cluster, but it will be able
echo to create and modify such credentials.
echo
echo Here is a suggested password that is a random UUID, in case you like
echo what you see:
node -e "var uuid = require('node-uuid'); console.log(uuid.v4());"
echo
read -s -p "Please enter a new password for the 'clusteradmin' MongoDB user: " primaryPasscode
echo
read -s -p "Please confirm that awesome new password: " primaryPasscodeConfirmation
echo
if [ "$primaryPasscode" != "$primaryPasscodeConfirmation" ]; then
echo The passwords did not match. Sorry. Goodbye.
exit 1
fi
fi
echo
echo MongoDB VM will be configured as:
echo - Replica set named $replicaSetName
if $isPrimary; then
echo - Primary node in the replica set
echo - New 'clusteradmin' user with a password you set.
fi
if $isArbiter; then
echo - Replica set arbiter
echo
echo DISK NOTE:
echo There is no need to attach a data disk to this VM.
fi
if ! $isPrimary && ! $isArbiter ; then
echo - Additional node in the replica set
fi
if $isUsingDataDisk; then
echo - Additional data disk that will mount to /mnt/data
fi
echo
echo
echo OK. Please sit back, relax, and enjoy the show...
echo
### DATA DISK
if $isUsingDataDisk; then
mongoDataPath=/mnt/data
echo Checking for attached Windows Azure data disk...
while [ ! -e /dev/sdc ]; do echo waiting for /dev/sdc empty disk to attach; sleep 20; done
echo Partitioning...
sudo fdisk /dev/sdc <<ENDPARTITION > /tmp/fdisk.log 2>&1
n
p
1
1
w
ENDPARTITION
echo Formatting w/ext4...
sudo mkfs.ext4 /dev/sdc1 > /tmp/format.log 2>&1
echo Preparing permanent data disk mount point at /mnt/data...
sudo mkdir /mnt/data
echo '/dev/sdc1 /mnt/data ext4 defaults,auto,noatime,nodiratime,noexec 0 0' | sudo tee -a /etc/fstab
echo Mounting the new disk...
sudo mount /mnt/data
sudo e2label /dev/sdc1 /mnt/data
fi
### MONGODB
echo Creating MongoDB folders on the disk owned by the mongod user in $mongoDataPath...
sudo mkdir $mongoDataPath/log
sudo mkdir $mongoDataPath/db
sudo chown -R mongod:mongod $mongoDataPath
echo Configuring MongoDB...
sudo tee /etc/mongod.conf > /dev/null <<EOF
logpath=$mongoDataPath/log/mongod.log
logappend=true
fork=true
dbpath=$mongoDataPath/db
directoryperdb=true
replSet=$replicaSetName
auth=true
rest=true
pidfilepath = /var/run/mongodb/mongod.pid
keyFile=/etc/$replicaSetKey
EOF
if $isPrimary; then
echo Generating replica set security key...
openssl rand -base64 753 > $replicaSetKey
echo Securely storing replica set key in Windows Azure storage...
node updown.js mongodb up $replicaSetKey
else
echo Acquiring replica set security key from the cloud...
node updown.js mongodb down $replicaSetKey
fi
echo Installing replica set key on the machine...
sudo chown mongod:mongod $replicaSetKey
sudo chmod 0600 $replicaSetKey
sudo mv $replicaSetKey /etc/$replicaSetKey
echo
echo About to bring online MongoDB.
echo This may take a few minutes as the initial journal is preallocated.
echo
echo Starting MongoDB service...
sudo service mongod start
sudo chkconfig mongod on
if $isPrimary; then
echo Initializing the replica set...
sleep 2
cat <<EOF > /tmp/initializeReplicaSetPrimary.js
rsconfig = {_id: "$replicaSetName",members:[{_id:0,host:"$primaryHostname"}]}
rs.initiate(rsconfig);
rs.conf();
EOF
/usr/bin/mongo /tmp/initializeReplicaSetPrimary.js > /tmp/creatingMongoCluster.log 2>&1
sleep 10
echo Creating cluster administrator account...
cat <<EOF > /tmp/initializeAuthentication.js
db = db.getSiblingDB('admin');
db.addUser({
user:'clusteradmin',
pwd:'$primaryPasscode',
roles:[
'userAdminAnyDatabase',
'clusterAdmin'
],
otherDBRoles: {
config: ['readWrite'],
local: ['read']
}
});
EOF
/usr/bin/mongo /tmp/initializeAuthentication.js --verbose > /tmp/creatingMongoClusterAdmin.log 2>&1
echo Authentication ready. Restarting MongoDB...
sudo service mongod restart
# remove credentials trace
rm /tmp/initializeAuthentication.js
echo
echo So you now have a 'clusteradmin' user that can administer the replica set
echo and also add new users to databases. The password was set in this session.
echo
echo You should probably connect now and create databases, users on any new
echo databases, etc.
echo
echo Read up on this here:
echo http://docs.mongodb.org/manual/tutorial/add-user-to-database/
echo
echo To connect to a Mongo instance:
echo mongo MYDB -u Username -p
echo
if ask "Would you like to connect to MongoDB Shell now as 'clusteradmin' to do this? "; then
/usr/bin/mongo admin -uclusteradmin -p$primaryPasscode
fi
else
ourHostname=$(hostname)
if $isArbiter; then
cat <<EOF > /tmp/joinCluster.js
rs.addArb('$ourHostname');
rs.conf();
rs.status();
EOF
else
cat <<EOF > /tmp/joinCluster.js
rs.add('$ourHostname');
rs.conf();
rs.status();
EOF
fi
echo Joining the MongoDB cluster...
/usr/bin/mongo $primaryHostname/admin -uclusteradmin -p$primaryPasscode /tmp/joinCluster.js --verbose > /tmp/joinCluster.log 2>&1
if ask "Would you like to view the replica set status? "; then
/usr/bin/mongo $primaryHostname/admin -uclusteradmin -p$primaryPasscode << EOF
rs.status();
EOF
fi
if ask "Would you like to connect to the primary node to look around? "; then
/usr/bin/mongo $primaryHostname/admin -uclusteradmin -p$primaryPasscode
fi
fi
echo
echo Well, that looks like a wrap. Have a nice day!
echo
popd > /dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment