Sunday, November 17, 2013

AWS CloudFormation - Tips for the Novice (create a load balanced stack)

Creating a load balanced stack with ElasticLoadBalancer


Following up on the previous blog post on this subject, we now want to create a load balanced LAMB stack.  The ELB cost is currently $.025.hour.  Do the math:

.025 * 24 * 365 = $219/year

Seems expensive, however the upside is reliability and scalability.  One issue seems to be that the ELB instance does not necessarily handle spikey volume very well according to some reports.  What this means is that under rapidly increasing load, the ELB might not do a great job balancing the traffic.  There are ways to mitigate this however by something called "pre-warming" or apparently begging tech support at Amazon for help.  More research needed...



Good EC2/ELB info links:

http://www.datadoghq.com/wp-content/uploads/2013/07/top_5_aws_ec2_performance_problems_ebook.pdf

http://harish11g.blogspot.com/2012/07/aws-elastic-load-balancing-elb-amazon.html

https://s3.amazonaws.com/cloudformation-examples/BoostrappingApplicationsWithAWSCloudFormation.pdf

On with the show

I created a load balanced instance of the Bedrock LAMB stack by following a template found here:

https://s3.amazonaws.com/cloudformation-templates-us-east-1/Insoshi_Multi_AZ.template

The template I created for this example uses Bedrock's session cookie to achieve server affinity (stickiness).  In the next version of this template, I'll add an RDS server and configure both servers to use the RDS server for it's session store.  This will remove the need for server affinity.

Note: Applications that use the session directory capabilities of Bedrock will need to re-think that approach since session directories are created on the web server.  An implementation of session directories that use S3 is in the works.

The gist of the changes from my original EC2 template (bedrock.tmpl) is the creation of an AWS::EC2::AutoScalingGroup.

"WebServerGroup" : {
    "Type" : "AWS::AutoScaling::AutoScalingGroup",
    "Properties" : {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MinSize" : "1",
"MaxSize" : "5",
"DesiredCapacity" : { "Ref" : "WebServerCapacity" },
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
    }
}


...and an AWS::AutoScaling::LaunchConfiguration which essentially replaced the AWS::EC2::Instance.
"LaunchConfig": {  
    "Type": "AWS::AutoScaling::LaunchConfiguration",
    "Metadata" : {

"AWS::CloudFormation::Init" : {
   "configSets" : { 
"bedrockConfig" : ["rpmPrep", "config"]
   },
            "rpmPrep" : {
"files" : {
   "/etc/yum.repos.d/bedrock.repo" : {
"source" : "https://s3.amazonaws.com/openbedrock/bedrock.repo",
"mode"   : "000644",
"owner"  : "root",
"group"  : "root"
   }
}
   },
   "config" : {

"packages" : {
   "yum" : {
"mysql"        : [],
"mysql-server" : [],
"mysql-libs"   : [],
"bedrock"      : [],
"bedrock-ide"  : []
   }
},

"services" : {
   "sysvinit" : {  
"mysqld" : {
   "enabled"       : "true",
   "ensureRunning" : "true"                
},
"httpd" : {
   "enabled"       : "true",
   "ensureRunning" : "true"                
}
   }
}
   }
}
    },
    "Properties": {
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
 { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
"InstanceType"   : { "Ref" : "InstanceType" },
"SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
"KeyName"        : { "Ref" : "KeyName"},
"UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
   "#!/bin/bash -v\n",
   "yum-config-manager --enable epel\n",
   "# Helper function\n",
   "function error_exit\n",
   "{\n",
   "  /usr/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "WaitHandle" }, "'\n",
   "  exit 1\n",
   "}\n",

   "# Install LAMB packages\n",
   "/usr/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",
   "/sbin/service httpd restart\n",

   "# create session database\n",
   "mysqladmin create bedrock\n",
   "cat /usr/share/bedrock/create-session.sql | mysql -u root bedrock\n",

   "# All is well so signal success\n",
   "/usr/bin/cfn-signal -e 0 -r \"LAMB Stack setup complete\" '", { "Ref" : "WaitHandle" }, "'\n"
]]}}
    }
},

"WaitHandle" : {
    "Type" : "AWS::CloudFormation::WaitConditionHandle"
},

"WaitCondition" : {
    "Type" : "AWS::CloudFormation::WaitCondition",
    "DependsOn" : "WebServerGroup",
    "Properties" : {
"Handle" : {"Ref" : "WaitHandle"},
"Timeout" : "300"
    }
},

"WebServerSecurityGroup" : {
    "Type" : "AWS::EC2::SecurityGroup",
    "Properties" : {
"GroupDescription" : "Enable HTTP access via port 80, SSH via port 22",
"SecurityGroupIngress" : [
   {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"},
   {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "0.0.0.0/0"}
]
    }      
}          

Essentially, just some minor changes to the template required,  so here's the entire new template:

https://s3.amazonaws.com/openbedrock/elb-bedrock.tmpl

I also introduced SSL.  The ELB performs SSL termination.  To configure that required uploading a creating and uploading an SSL certificate.  More info on SSL certificates, uploading them to AWS, etc.

http://openbedrock.blogspot.com/2013/11/ssl-certificate-stuff.html

No comments:

Post a Comment

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