Single command server deployment

Written by Ger Apeldoorn on. Posted in Sysadmin

This article is in English because it was mentioned in a PuppetLabs newsletter.

Although Puppet Enterprise gives you the wonderful tools to automate your Linux administration, you still cannot deploy a server with a single command. With the following script, you can.

Why is this so cool?

When, for instance, a new Tomcat server is needed, I create a DNS entry for that server and give a single command and get some coffee. Before it has cooled down enough to drink, the developer has his Tomcat-app deployed and the server is operational. (It takes about 8 minutes to get from scratch to a fully operational server.)

Automate Linux deployments

This script really just glues the excellent Puppet Enterprise-tools together, especially the VMWare-integration stuff.

This is what it does step-by-step:

  • Runs a series of checks to see if a succesful deployment is likely.
  • Clones a VMWare template and boots it.
  • When it has booted, the script asks VMWare what ipaddress the new server got from DHCP.
  • If you give the extra parameter ‘dev’ or ‘test’, it adds a line to the configfile of the agent. (like “environment = development”).
  • Configures the network by changing the network-config files over SSH.
  • Installs the Puppet-client on the new machine.
  • Stops and starts the new machine if it is on the default network (LAN).

After the new server is started, it will start the Puppet Enterprise client. If you Puppet install is setup properly, it will configure itself in a few minutes.

Prerequisites

Make sure that all prerequisites are met before you try to run the script, or it will not produce the desired results.

Adapting the script to meet your needs

There are a few things that need to be customized before you can use it.

  • Template locations
  • Networks

Template locations

Right at the top of the script, you can set the locations of the templates that you want to use.

If you do not know the path, run:

puppet node_vmware list 

….to get a list of all the hosts and templates on your VMWare cluster. This is also a nice test if vmware-integration is setup properly.

At my current client, we have got a seperate cluster for testservers. If I give an extra parameter ‘dev’ or ‘test’, the script clones another template that is on the test-cluster.

Networks

In my situation, I have got three networks. To configure the network properly, the script needs to know what network the server is in. It tries to deduce that from the ip-address it finds in DNS. This keeps the per-host-configuration to a minimum.

You can find these setting after the “CONFIGURATION NETWORK” banner. Just add or remove your own networks at will.

Deploying a shiny new Linux server

  • This is the easy part, just add a DNS A record for your new server
  • use the script like this:
./deployserver hostname [dev|test]
  • If the destination network is known as “LAN”, the server is rebooted at the end.
  • If it is on one of the other networks defined, you need to re-configure the VM in order to get the NIC on the right network and reboot manually (those are the servers that require the hard work). 🙂

The shell script

DISCLAIMER: I take no responsibility for any damage you cause using this script. If you’re not sure about it, don’t use it. Please do not redistribute my script without previous consent from me. If you have a way to make it better please do so by submitting changes to me and I will implement and give credit where it’s due. The script below comes AS-IS. Please contact me directly if you have any questions or concerns.
#!/bin/bash
#########################################################################################
##
##  This script:
##       - clones a VMWare template
##       - Configures the network
##       - Installs the Puppet-client on the new machine
##       - Stops and starts the new machine if it is on the default network (LAN) 
##  
#########################################################################################
##  PREREQUISITES:
##  - A working Puppet Enterprise 2.x server (puppetmaster) with:
##    - VMWare integration working (Try the command 'puppet node_vmware list')
##      - http://docs.puppetlabs.com/pe/2.7/cloudprovisioner_vmware.html
##      - http://docs.puppetlabs.com/pe/2.7/cloudprovisioner_configuring.html
##    - SSH keypair for logging in remotely without a password
##  - A (RHEL) template with:
##    - The public SSH key from the puppetmaster as /root/.ssh/authorized_keys
##    - VMWare-tools installed
##    - DHCP enabled
##    - No entries in /etc/udev/rules.d/70-persistent-net.rules
##        (clean it up each time the template is booted for updates/maintenance etc.)
#########################################################################################
##  PREREQUISITES FOR EACH HOST TO BE DEPLOYED:
##  - An 'A' DNS entry pointing to a free ip address.
#########################################################################################
##  Author: Ger Apeldoorn, Freelance Puppet consultant/trainer (info@gerapeldoorn.nl)
##                           https://puppetspecialist.nl
#########################################################################################

TEMPLATELOC="/Datacenters/HQ/vm/Templates/" #Trailingslash
TEMPLATENAME="RHEL6template"
HOSTNAME=$1

# dev and test servers need a different template.
if [ "$2" == "dev" ]; then
    TEMPLATENAME="RHEL6templateDev"
fi

if [ "$2" == "test" ]; then
    TEMPLATENAME="RHEL6templateTest"
fi

#########################################################
###   PRE FLIGHT CHECKS
#########################################################
echo ""
echo "#########################################################"
echo "                  Run pre-flight checks"
echo "#########################################################"
echo ""
echo -ne "Root rights                           : "
if [ "$(id -u)" != "0" ]; then
   echo "ERROR"; echo "Run this script as root"
   exit 1
fi
echo "OK"

echo -ne "Parameters                            : "
if [ "$HOSTNAME" == "" ]; then
   echo "ERROR"; echo "Give the desired hostname as a parameter."
   exit 1
fi
echo "OK"

echo -ne "DNS-check hostname                    : "
DESIREDIP=$(host $HOSTNAME | awk '{print $4}')
if [ $? -ne 0 ]; then echo "ERROR"; echo "Error checking hostname; first register the host in DNS!"; exit 1; fi
if [[ ! $DESIREDIP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
    echo "Error: Not a valid ip address: $DESIREDIP"
    exit 1
fi
echo "OK ($DESIREDIP)"

echo -ne "Is address is available? (ping)       : "
if ping -c 1 -w 1 "$DESIREDIP" >/dev/null ; then 
   echo "ERROR"; echo "Error: Address $DESIREDIP is not free!"
   exit 1
fi
echo "OK"

echo -ne "Check if certificate already exists   : "
SIGNEDCERT=$(puppet cert list --all | grep $HOSTNAME >/dev/null)
if [ $? -eq 0 ]; then echo "ERROR"; echo "The certificate for $HOSTNAME already exists, remove it with this command:"; echo "puppet cert clean $1"; exit 1; fi
echo "OK"

echo ""
echo "===================================================================================================="
echo "               This script creates a new node in VMWare and initializes Puppet."
echo "           Hostname: $HOSTNAME      Ip: $DESIREDIP    Template: $TEMPLATENAME"
echo "===================================================================================================="
echo ""
read -p "Continue? " -n 1
if [[ ! $REPLY =~ ^[YyJj]$ ]]
then
    exit 1
fi

echo " "
echo " "
echo "#########################################################"
echo "###   CLONE TEMPLATE"
echo "#########################################################"

puppet node_vmware create --template "$TEMPLATELOC$TEMPLATENAME" --name $HOSTNAME --wait-for-boot
if [ $? -ne 0 ]; then echo "Error cloning the template!"; exit 1; fi

TEMPIP=$(puppet node_vmware find "$TEMPLATELOC$HOSTNAME" | grep ipaddress: | awk -F: '{print $2}' | sed -e 's/^[ \t]*//')
if [[ $TEMPIP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
    echo $TEMPIP
else
    echo "Error finding ipaddress!"
    exit 1
fi

echo ""
echo "#########################################################"
echo "### INSTALLATION PUPPET CLIENT ON TARGET MACHINE"
echo "#########################################################"

puppet node install --login=root --keyfile=~/.ssh/id_rsa --install-script puppet-enterprise --installer-payload ~/Puppet/puppet-enterprise-2.7-el-6-x86_64.tar.gz  --installer-answers ~/Puppet/agent.answer --puppetagent-certname $HOSTNAME $TEMPIP
if [ $? -ne 0 ]; then echo "Error during install of Puppet client!"; exit 1; fi

echo ""
echo "#########################################################"
echo "### INSTALLATION PUPPET COMPLETE, WAIT 5 SECs"
echo "#########################################################"
sleep 5

echo ""
echo "#########################################################"
echo "###   SIGN CERTIFICATE FOR PUPPET CLIENT"
echo "#########################################################"

puppet cert sign $HOSTNAME
if [ $? -ne 0 ]; then echo "Error sigining certificate!"; exit 1; fi

if [ "$2" == "dev" ]; then
    echo ""
    echo "#########################################################"
    echo "###   REGISTER IN DEVELOPMENT PUPPET ENVIRONMENT"
    echo "#########################################################"
    ssh $TEMPIP 'echo "    environment = development" >> /etc/puppetlabs/puppet/puppet.conf; /etc/init.d/pe-puppet start'
fi

if [ "$2" == "test" ]; then
    echo ""
    echo "#########################################################"
    echo "###   REGISTER IN TESTING PUPPET ENVIRONMENT"
    echo "#########################################################"
    ssh $TEMPIP 'echo "    environment = testing" >> /etc/puppetlabs/puppet/puppet.conf; /etc/init.d/pe-puppet restart'
fi

echo ""
echo "#########################################################"
echo "###   CONFIGURATION NETWORK"
echo "#########################################################"

# This determines what network the DNS-name of the server is in and sets 
#  the variables so the network can be configured properly later on.
case "$DESIREDIP" in
    10.10.0.*)      NETWORK="DMZ1"
                    NETMASK="255.255.255.0"
                    GATEWAY="10.10.0.1"
                    DNS1="10.10.0.2"
                    DNS2="192.168.1.2"
                    ;;
    192.168.1.*)    NETWORK="DMZ2"
                    NETMASK="255.255.255.0"
                    GATEWAY="192.168.1.1"
                    DNS1="192.168.1.2"
                    DNS2="10.10.0.2"
                    ;;
    *)              NETWORK="LAN"
                    NETMASK="255.255.0.0"
                    GATEWAY="10.1.1.1"
                    DNS1="10.1.1.2"
                    DNS2="10.1.1.3"
                    ;;
esac

echo "###   IP      = $DESIREDIP"
echo "###   NETWORK = $NETWORK"
echo "###   NETMASK = $NETMASK"
echo "###   GATEWAY = $GATEWAY"
echo "###   DNS1    = $DNS1"
echo "###   DNS2    = $DNS2"
echo "#########################################################"

# set ip configuration
ssh $TEMPIP "sed -i.bak \"s/BOOTPROTO=dhcp/BOOTPROTO=static/;s/DNS1=10.1.2.*/DNS1=$DNS1/;s/DNS2=10.1.2.*/DNS2=$DNS2/\" /etc/sysconfig/network-scripts/ifcfg-eth0"
if [ $? -ne 0 ]; then echo "ERROR during pushing networkconfiguration (sed)!"; exit 1; fi
ssh $TEMPIP "echo IPADDR=$DESIREDIP >> /etc/sysconfig/network-scripts/ifcfg-eth0"
if [ $? -ne 0 ]; then echo "ERROR during pushing networkconfiguration (cat)!"; exit 1; fi
ssh $TEMPIP "echo NETMASK=$NETMASK >> /etc/sysconfig/network-scripts/ifcfg-eth0"
if [ $? -ne 0 ]; then echo "ERROR during pushing networkconfiguration (cat)!"; exit 1; fi
ssh $TEMPIP "echo GATEWAY=$GATEWAY >> /etc/sysconfig/network"
if [ $? -ne 0 ]; then echo "ERROR during pushing networkconfiguration (cat)!"; exit 1; fi

# set hostname and gateway
ssh $TEMPIP "sed -i.bak \"s/HOSTNAME=.*/HOSTNAME=$HOSTNAME/;s/GATEWAY=.*/GATEWAY=$GATEWAY/\" /etc/sysconfig/network"

# Only reboot if network is LAN, otherwise we need to put the server in a different network via the VMWare console and reboot manually.
if [ "$NETWORK" == "LAN" ]; then
    echo ""
    echo "===================================================================================================="
    echo "               Installation is completed, rebooting new server with networkconfig"
    echo "===================================================================================================="
    echo ""
    puppet node_vmware stop "$TEMPLATELOC$HOSTNAME"
    if [ $? -ne 0 ]; then echo "ERROR stopping the host in VMWare!"; exit 1; fi
    echo "#########################################################"
    echo "###   WAIT UNTIL HOST IS DOWN, THEN START IT AGAIN"
    echo "#########################################################"
    STATE=$(puppet node_vmware find "$TEMPLATELOC$HOSTNAME" 2>&1 | grep powerstate| awk '{print $2}')
    while [ "$STATE" != "poweredOff" ]; do
        sleep 1
        echo "###   Checking... "
        STATE=$(puppet node_vmware find "$TEMPLATELOC$HOSTNAME" 2>&1 | grep powerstate| awk '{print $2}')
    done
    echo ""
    echo "#########################################################"
    echo "###   HOST IS DOWN, STARTING...."
    echo "#########################################################"
    puppet node_vmware start "$TEMPLATELOC$HOSTNAME"
    if [ $? -ne 0 ]; then echo "ERROR starting the host in VMWare!"; exit 1; fi
fi

if [ "$NETWORK" != "LAN" ]; then
    echo ""
    echo "#########################################################"
    echo "###   INSTALLATION COMPLETED, REBOOT THE SERVER. "
    echo "###  NOTE: PUT THE HOST IN THE CORRECT NETWORK: $NETWORK"
    echo "#########################################################"
fi

Labels:, , ,