{"id":2059,"date":"2015-08-11T08:06:29","date_gmt":"2015-08-11T14:06:29","guid":{"rendered":"http:\/\/www.mooreds.com\/wordpress\/?p=2059"},"modified":"2015-07-29T06:04:37","modified_gmt":"2015-07-29T12:04:37","slug":"masterless-puppet-and-cloudformation","status":"publish","type":"post","link":"https:\/\/www.mooreds.com\/wordpress\/archives\/2059","title":{"rendered":"Masterless puppet and CloudFormation"},"content":{"rendered":"<figure style=\"width: 150px\" class=\"wp-caption alignright\"><img loading=\"lazy\" decoding=\"async\" title=\"puppet on a chain #2, hodko banni by nevil zaveri (thank you for 10million+ views :)\" src=\"http:\/\/www.mooreds.com\/wordpress\/wp-content\/uploads\/2015\/07\/3375437538_2b22afa3c7_q_puppet.jpg\" alt=\"puppet photo\" width=\"150\" height=\"150\" \/><figcaption class=\"wp-caption-text\"><small>Photo by <a href=\"http:\/\/www.flickr.com\/photos\/43109416@N00\/3375437538\" target=\"_blank\">nevil zaveri (thank you for 10million+ views \ud83d\ude42<\/a> <a title=\"Attribution License\" href=\"http:\/\/creativecommons.org\/licenses\/by\/2.0\/\" target=\"_blank\" rel=\"nofollow\"><img decoding=\"async\" src=\"http:\/\/www.mooreds.com\/wordpress\/wp-content\/plugins\/wp-inject\/images\/cc.png\" alt=\"\" \/><\/a><\/small><\/figcaption><\/figure>\n<p>I&#8217;ve had some experience with CloudFormation <a href=\"\/wordpress\/archives\/1870\">in the past<\/a>, and recently gained some puppet expertise.\u00a0 I thought it&#8217;d be great to combine the two, working on a new project to set up the <a href=\"https:\/\/www.elastic.co\/webinars\/introduction-elk-stack\">ELK stack<\/a> for a client.<\/p>\n<p>Basically, we are creating an ec2 instance (or a number of them) from a vanilla image using a CloudFormation template, doing a small amount of initialization via the <a href=\"https:\/\/docs.aws.amazon.com\/AWSCloudFormation\/latest\/UserGuide\/aws-properties-ec2-instance.html#cfn-ec2-instance-userdata\">UserData<\/a> section and then using puppet to configure them further.\u00a0 However, puppet is used in a masterless context, where the intelligence (of knowing which machine should be configured which way) isn&#8217;t in the manifest file, but rather in the code that checks out the modules and manifests. Here&#8217;s a great example of a <a href=\"https:\/\/github.com\/jordansissel\/puppet-examples\/tree\/master\/masterless\">project set up to use masterless puppet<\/a>.<\/p>\n<p>Before I dive into more details, other solutions I looked at included:<\/p>\n<ul>\n<li>doing all the machine setup in UserData\n<ul>\n<li>This is a bad idea because it forces you to set up and tear down machines each time you want to make a configuration change.\u00a0 Leads to a longer development cycle, especially at first.\u00a0 Plus bash is great for small configurations, but when you have dependencies and other complexities, the scripts can get hairy.<\/li>\n<\/ul>\n<\/li>\n<li>pulling a bash script from s3\/github in UserData\n<ul>\n<li>puppet is made for configuration management and handles more complexity than a bash script.\u00a0 I&#8217;ll admit, I used puppet with an eye to the future when we had more machines and more types of machines.\u00a0 I suppose you could do the same with bash, but puppet handles more of typical CM tasks, including setting up cron jobs, making sure services run, and deriving dependencies between services, files and artifacts.<\/li>\n<\/ul>\n<\/li>\n<li>using a different CM tool, like ansible or chef\n<ul>\n<li>I was familiar with puppet.\u00a0 I imagine the same solution would work with other CM tools.<\/li>\n<\/ul>\n<\/li>\n<li>using a puppet master\n<ul>\n<li><a href=\"https:\/\/puppetlabs.com\/presentations\/de-centralise-and-conquer-masterless-puppet-dynamic-environment\">This presentation<\/a> convinced me to avoid setting up a puppet master.\u00a0 Cattle not pets.<\/li>\n<\/ul>\n<\/li>\n<li>using cloud-init instead of UserData for initial setup\n<ul>\n<li>I tried.\u00a0 I couldn&#8217;t figure out cloud-init, even with <a href=\"http:\/\/www.knowceantech.com\/2014\/03\/amazon-cloud-bootstrap-with-userdata-cloudinit-github-puppet\/\">this great post<\/a>.\u00a0 It&#8217;s been a few months, so I&#8217;m afraid I don&#8217;t even remember what the issue was, but I remember this solution not working for me.<\/li>\n<\/ul>\n<\/li>\n<li>create an instance\/AMI with all software installed\n<ul>\n<li>puppet allows for more flexibility, is quicker to setup, and allows you to manage your configuration in a VCS rather than a pile of different AMIs.<\/li>\n<\/ul>\n<\/li>\n<li>use a container instead of AMIs\n<ul>\n<li>isn&#8217;t docker the answer to everything? I didn&#8217;t choose this because I was entirely new to containerization and didn&#8217;t want to take the risk.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Since I&#8217;ve already outlined how the solution works, let&#8217;s dive into details.<\/p>\n<p>Here&#8217;s the UserData section of the CloudFormation template:<\/p>\n<pre><code>\r\n          \"Fn::Base64\": {\r\n            \"Fn::Join\": [\r\n              \"\",\r\n              [\r\n                \"#!\/bin\/bash \\n\",\r\n                \"exec &gt; \/tmp\/part-001.log 2&gt;&amp;1 \\n\",\r\n                \"date &gt;&gt; \/etc\/provisioned.date \\n\",\r\n                \"yum install puppet -y \\n\",\r\n                \"yum install git -y \\n\",\r\n                \"aws --region us-west-2 s3 cp s3:\/\/s3bucket\/auth-files\/id_rsa\/root\/.ssh\/id_rsa &amp;&amp; chmod 600 \/root\/.ssh\/id_rsa \\n\",\r\n                \"# connect once to github, so we know the host \\n\",\r\n                \"ssh -T -oStrictHostKeyChecking=no git@github.com \\n\",\r\n                \"git clone git@github.com:client\/repo.git \\n\",\r\n                \"puppet apply --modulepath repo\/infra\/puppet\/modules pure-spider\/infra\/puppet\/manifests\/\",\r\n                { \"Ref\" : \"Environment\" },\r\n                \"\/logstash.pp \\n\",\r\n                \"date &gt;&gt; \/etc\/provisioned.date\\n\"\r\n              ]\r\n            ]\r\n<\/code>\r\n<\/pre>\n<p>So, we are using a bash script, but only for a little bit.\u00a0 The second line (starting with <code>exec<\/code>) stores output into a logfile for debugging purposes.\u00a0 We then store off the date and install <code>puppet<\/code> and <code>git<\/code>.\u00a0 The <code>aws<\/code> command pulls down a private key stored in s3.\u00a0 This instance has access to s3 because of an <a href=\"https:\/\/stackoverflow.com\/questions\/14170132\/creating-an-ec2-instance-along-with-iam-roles-using-cloud-formation\/17821581#17821581\">IAM setup<\/a> elsewhere in the CloudFormation template&#8211;the access we have is read-only and the private key has already been added to our github repository.\u00a0 Then we connect to github via <code>ssh<\/code> to &#8216;get to know the host&#8217;.\u00a0 Then we clone the repository containing the infrastructure code.\u00a0 Finally, we apply the manifest, which is partially determined by a parameter to the CloudFormation template.<\/p>\n<p>This bash script will run on creation of the EC2 instance.\u00a0 Once this script is solid, if you are testing adding additional puppet modules, you only have to do a <code>git pull<\/code> and <code>puppet apply<\/code> to add more functionality to the modules.\u00a0 (Of course, at the end you should stand up and tear down via CloudFormation just to test end to end.)\u00a0 You can also see how it&#8217;d be easy to have the <code>logstash.conf<\/code> file be a parameter to the CloudFormation template, which would let you store your configuration for web servers, database servers, etc, in puppet as well.<\/p>\n<p>I&#8217;m happy with how flexible this solution is.\u00a0 CloudFormation manages the machine creation as well as any other resources, puppet manages the software installed in those machines, and git allows you to maintain all that configuration in one place.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve had some experience with CloudFormation in the past, and recently gained some puppet expertise.\u00a0 I thought it&#8217;d be great to combine the two, working on a new project to [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[39,10],"tags":[],"class_list":["post-2059","post","type-post","status-publish","format-standard","hentry","category-cloud-computing","category-dynamic-languages"],"_links":{"self":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/2059","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/comments?post=2059"}],"version-history":[{"count":3,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/2059\/revisions"}],"predecessor-version":[{"id":2088,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/2059\/revisions\/2088"}],"wp:attachment":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/media?parent=2059"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/categories?post=2059"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/tags?post=2059"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}