Sunday, November 17, 2013

AWS CloudFormation - Tips for the Novice (Load balancing with no server affinity)

Look Ma! No Server Affinity!

In this blog entry I present the result of a weekend of messing with CloudFormation.  A load balanced, SSL enabled, Bedrock stack which has an RDS instance to hold session information.  I've removed server affinity requirements by using an RDS instance of MySQL instead of having each web instance implement their own server.
Working off of the original CloudFormation template...


I've created:


  • adding an RDS instance
  • removing the affinity policies from the load balancer
  • removing the mysql-server packages from the instances
  • modifying the UserData script so that it modifies Bedrock's configuration and points to the new RDS MySQL endpoint to use as it's session database

Add an RDS Instance


"DBSecurityGroup": {
    "Type": "AWS::RDS::DBSecurityGroup",
    "Properties": {
"DBSecurityGroupIngress": { "EC2SecurityGroupName": { "Ref": "WebServerSecurityGroup" } },
"GroupDescription" : "Frontend Access"
    }
},

"BedrockDB" : {
    "Type" : "AWS::RDS::DBInstance",
    "Properties" : {
"DBSecurityGroups" : [
   {"Ref" : "DBSecurityGroup"}
],
"AllocatedStorage" : "10",
"DBInstanceClass" : "db.t1.micro",
"Engine" : "MySQL",
"MasterUsername" : "fred",
"MasterUserPassword" : "flintstone"
    }
}

Modify the UserData script

Couple of other things that we changed in this script include:
  • Updating the helper scripts from Amazon's latest package.
  • I've created a custom ami for Cento OS 6.4 that includes cloud-init, helper scripts, and the AWS CLI tools.
  • Adding a short sleep after cfn-init since in practice, restarting the Apache web server soon after the cfn-init script seemed to create a race condition with Apache
  • we need to create the bedrock database and the session table, but only once.  We're using the clever technique of only performing the actions in the script on the first instance that is launched - identifed by the metadata ami-launch-index.  If the ami-launch-index is 0, then we create the database and session table
  • replace the datatabase configuration in tagx.xml with the name of the RDS host

"UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
    "#!/bin/bash -v\n",
    "\n",
    "sudo yum update -y https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.amzn1.noarch.rpm\n",
    "yum-config-manager --enable epel >/dev/null\n",
    "# Helper function\n",
    "function error_exit\n",
    "{\n",
    "   /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "WaitHandle" }, "'\n",
    "  exit 1\n",
    "}\n",

    "# Install LAMB packages\n",
    "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r LaunchConfig ", "-c bedrockConfig ",
    "    --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n",

    "# configure Bedrock IDE\n",
    "echo 'Include /usr/lib/bedrock-ide/config/perl_bedrock-ide.conf' >> /etc/httpd/conf.d/perl_bedrock.conf\n",
    "# need to sleep a little since Apache may not be started completely\n",
    "sleep 2 && /sbin/service httpd restart >> /var/log/httpd/error_log 2>&1\n",
    "\n",
    "# get RDS host name and update tagx.xml\n",
    "BEDROCK_DB_HOST=",  { "Fn::GetAtt" : [ "BedrockDB", "Endpoint.Address"] }, "\n",
    "echo $BEDROCK_DB_HOST > /tmp/bedrock-db-host\n",
    "sed \"s/:bedrock/:bedrock:$BEDROCK_DB_HOST/;\" /usr/lib/bedrock/config/tagx.xml > /tmp/tagx.xml\n",
    "mv /tmp/tagx.xml /usr/lib/bedrock/config/tagx.xml\n",
    "\n",
    "# create session database if this is instance 0\n",
    "ami_launch_index=$(wget http://169.254.169.254/latest/meta-data/ami-launch-index -O - 2>/dev/null)\n",
    "[ $ami_launch_index = \"0\" ] && mysqladmin -u fred --password=flintstone -h $BEDROCK_DB_HOST create bedrock\n",
    "[ $ami_launch_index = \"0\" ] && cat /usr/share/bedrock/create-session.sql | mysql -u fred --password=flintstone -h $BEDROCK_DB_HOST bedrock\n",
    "# All is well so signal success\n",
    "/opt/aws/bin/cfn-signal -e 0 -r \"LAMB Stack setup complete\" '", { "Ref" : "WaitHandle" }, "'\n"
]]}}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.