{"id":16236,"date":"2017-05-23T05:10:13","date_gmt":"2017-05-22T20:10:13","guid":{"rendered":"https:\/\/jirak.net\/wp\/load-balancing-a-dynamic-infrastructure-with-nginx-chef-and-consul\/"},"modified":"2017-05-26T16:40:46","modified_gmt":"2017-05-26T07:40:46","slug":"load-balancing-a-dynamic-infrastructure-with-nginx-chef-and-consul","status":"publish","type":"post","link":"https:\/\/jirak.net\/wp\/load-balancing-a-dynamic-infrastructure-with-nginx-chef-and-consul\/","title":{"rendered":"Load Balancing a Dynamic Infrastructure with NGINX, Chef, and Consul"},"content":{"rendered":"<p>Load Balancing a Dynamic Infrastructure with NGINX, Chef, and Consul<br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/jirak.net\/wp\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide0_title.png\" width=\"1024\" height=\"577\"><\/p>\n<p>    td {<br \/>\n        padding-right: 10px;<br \/>\n    }<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide0_title.png\" alt=\"Title slide for Dynamic Infrastructure with NGINX, Chef, and Consul webinar [presentation by Kevin Reedy of Belly Card at nginx.conf 2014] \" width=\"1024\" height=\"577\" class=\"alignnone size-full wp-image-50382\" style=\"border:2px solid #666666;padding:2px;margin:2px\" \/><br \/>\n<em>This post is adapted from a presentation by Kevin&nbsp;Reedy of Belly&nbsp;Card at nginx.conf in October&nbsp;2014. While that was a couple years ago, the content is still highly relevant today. You can view a recording of the presentation on <a target=\"_blank\" href=\"https:\/\/www.youtube.com\/watch?v=A2NOziRYh7U\">YouTube<\/a>.<\/em><\/p>\n<p><strong>Table of Contents<\/strong><\/p>\n<table class=\"fixed\" width=\"100%\">\n<tbody>\n<col width=\"4%\">\n<tr>\n<td style=\"text-align:right\">0:00<\/td>\n<td><a href=\"#Introduction\">Introduction<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Overview\">Overview<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Disclaimer\">Disclaimer<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">1:16<\/td>\n<td><a href=\"#Belly\">What is Belly?<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#BellysStack\">Belly&#8217;s Stack<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">1:44<\/td>\n<td><a href=\"#Version0TheDarkAge\">Version 0: The Dark Age<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">2:04<\/td>\n<td><a href=\"#Version1ANewHope\">Version 1: A New Hope<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">2:35<\/td>\n<td><a href=\"#Version2ServiceOrientedArchitecture\">Version 2: Separate Home Page and API<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#ServiceOrientedArchitecture\">Buzzword of the Day: Service-Oriented Architecture!<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version2SOA\">Enable SOA<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version2NGINX\">NGINX Replaces ELB<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version2UpstreamExample\">Upstream and Virtual Server Configuration<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version2Results\">Results<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">4:43<\/td>\n<td><a href=\"#ConfigurationWithChef\">Configuration With Chef<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Recipes\">Recipes<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Files\">Files<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Templates2slides\">Templates<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Cookbooks\">Cookbooks<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">6:49<\/td>\n<td><a href=\"#NGINXExample\">Example: Chef for NGINX<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#NGINXExampleTuning\">Installation, Configuration, and Tuning<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#NGINXExampleUpstreamapi\">Routing API Traffic<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">9:46<\/td>\n<td><a href=\"#RealWorldExample\">Real-World Chef Example<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">10:08<\/td>\n<td><a href=\"#Version2DeployingApps\">Version 2: Deploying Apps<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">10:40<\/td>\n<td><a href=\"#Version3SOAAllTheThings\">Version 3: SOA All The Things<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version3Heartbleed\">Then Heartbleed Happened<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version3SSLAllTheThings\">SSL All The Things<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">12:25<\/td>\n<td><a href=\"#Version4Docker\">Version 4: Docker<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version4Benefits\">Benefits<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#Version4DeployingContainers\">Deploying Containers<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">15:45<\/td>\n<td><a href=\"#DeploymentStrategies\">Deployment Strategies<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">17:43<\/td>\n<td><a href=\"#ServiceDiscovery\">Service Discovery Options<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">18:33<\/td>\n<td><a href=\"#Consul\">Consul Architecture<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">19:30<\/td>\n<td><a href=\"#WhatisConsul\">Consul Features<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">22:07<\/td>\n<td><a href=\"#ConsulTemplate\">Consul Template<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#ConsulTemplateExample\">Command to Start Consul Template<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#ConsulTemplateUpstreams\">NGINX Configuration with Consul Template<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#ConsulConfigurationManagement\">Configuration Management with Consul Template<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">24:35<\/td>\n<td><a href=\"#UsingCheftoDeployConsul\">Using Chef to Deploy Consul<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">25:44<\/td>\n<td><a href=\"#ProTips\">Pro Tips<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#ChefPartialSearch\">Chef Partial Search<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#DNS\">DNS<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">&nbsp;<\/td>\n<td><a href=\"#AvoidStaleConfig\">Avoiding Stale Configuration<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">30:35<\/td>\n<td><a href=\"#Questions\">Questions<\/a><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:right\">36:31<\/td>\n<td><a href=\"#ContactInfo\">Contact Info<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"Introduction\">0:00 Introduction<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide1_introduction.png\" alt=\"Title slide from the presentation by Kevin Reedy at nginx.conf 2014 about Load Balancing a Dynamic Infrastructure [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50320\" \/><\/p>\n<p>Good morning, everybody. My name is Kevin&nbsp;Reedy, and I work for a company called Belly&nbsp;Card based out of Chicago. We have about a hundred employees and twenty engineers. Today I&#8217;m going to talk to you about how we do our load balancing, specifically in an always changing dynamic infrastructure.<\/p>\n<h2 id=\"Overview\">0:21 Overview<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide2_overview.png\" alt=\"Overview of Belly's infrastructure, using Chef to configure NGINX, and using data from Consul's service discovery to configure NGINX [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50321\" \/><\/p>\n<p>So first I&#8217;m going to tell you about our infrastructure stack over time starting with version&nbsp;0, which I call the Dark Age. Then we&#8217;ll go over how we use Chef to configure NGINX. After that, we&#8217;ll talk about how we&#8217;ve changed away from that [to] using data from something called Consul service discovery platform to configure NGINX. If time permits, I have some pro tips on configuring NGINX, especially when it comes to configuration management.<\/p>\n<h2 id=\"Disclaimer\">0:49 Disclaimer<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide3_disclaimer.png\" alt=\"Disclaimer about how Chef, Pupper, Ansible, Salt, are great and CFEngine, Consul, etcd, and ZooKeeper are great for service discovery [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50322\" \/><\/p>\n<p>So even though I&#8217;m going to talk to you about Chef and Consul, I want to put out a disclaimer that nothing in this talk is specific to them. Chef, Puppet, Ansible, Salt, CFEngine, and all the rest are great products and you should be using one of them. We enjoy Chef, and I&#8217;m sure a lot of other people do too. <\/p>\n<p>The same goes with service discovery. Consul, etcd, and ZooKeeper are all great, so use what works for you.<\/p>\n<h2 id=\"Belly\">1:16 What is Belly?<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide5_belly.png\" alt=\"Belly is a replacement for a punch card at a coffee shop [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50324\" \/><\/p>\n<p>What is Belly? Essentially it&#8217;s a digital replacement for a punch card at a coffee shop where you buy ten drinks and get one free. We put an iPad in the store, customers scan using their phone or a physical card with a QR code, we track their points, give the rewards, and we actually build marketing tools on top of that. <\/p>\n<h2 id=\"BellysStack\">1:36 Belly&#8217;s Stack<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide6_bellys-stack.png\" alt=\"Introduction to Belly's Stack as it has evolved from Ruby on Rails to elastic load balancing, service discover with consul, and configuration management with Chef [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50325\" \/><\/p>\n<p>But I&#8217;m not here to talk to you about belly as a company. If you&#8217;re interested in that, you can just go to our <a href=\"https:\/\/www.bellycard.com\/\" target=\"_blank\">website<\/a>. I&#8217;m gonna talk to you about our infrastructure stack.<\/p>\n<h2 id=\"Version0TheDarkAge\">1:44 Version 0: The Dark Age<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide7_version-0-the-dark-age.png\" alt=\"Version 0 of Belly's Stack was a monolithic Ruby on Rails Application with MySQL, MongoDB, and Memcache [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50326\" \/><\/p>\n<p>So version&nbsp;0. Back when there were three developers and before I worked there, Belly was a monolithic Ruby on Rails application, which meant that every API call and part of our website went to a single Rails app. It was backed by MySQL, MongoDB, and Memcached, and we deployed it on Heroku, which is a great place to start. <\/p>\n<h2 id=\"Version1ANewHope\">2:04 Version 1: A New Hope<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide8_version-1-a-new-hope.png\" alt=\"Version one of Belly's stack was deployed with Capistrano on six AWS EC2 instances with the AWS Elastic Load Balancer [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50327\" \/><\/p>\n<p>So version 1&nbsp;&ndash; A New Hope. We decided Heroku wasn&#8217;t going meet our needs anymore, so we started deploying something called Capistrano, which is essentially &#8220;better than a shell script&#8221; written in Ruby. It helps you deploy things. <\/p>\n<p>We had about six servers on Amazon Web Services EC2. That was a static number, but we would bump it up if we knew there was something big happening and back down if we needed to. In front that we had AWS Elastic Load Balancer.<\/p>\n<h2 id=\"Version2ServiceOrientedArchitecture\">2:35 Version 2: Separate Home Page and API<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide9_version-2-serviceoriented-architecture.png\" alt=\"Version two of Belly's Stack separated the Homepage from the API [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50328\" \/><\/p>\n<p>So then a project came up where we wanted to separate our home page from the rest of the API. We wanted to do this so we could move faster with our home page and have different developers working on&nbsp;it.<\/p>\n<h2 id=\"ServiceOrientedArchitecture\">2:47 Buzzword of the Day: Service-Oriented Architecture!<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide10_serviceoriented-architecture.png\" alt=\"Service-oriented architecture introductory slide [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50329\" \/><\/p>\n<p>The big buzzword at the time&nbsp;&ndash; and still today&nbsp;&ndash; is service&#8209;oriented architecture.<\/p>\n<h2 id=\"Version2SOA\">2:50 Implement SOA<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide11_version-2-soa.png\" alt=\"Version two of Belly's Stack separated bellycard.com from api.bellycard.com [presentation by Kevin Reedy Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50330\" \/><\/p>\n<p>So version&nbsp;2 of our stack was to enable service&#8209;oriented architecture. Our first plan was to separate the home page from the API. That seems really simple&nbsp;&ndash; we just have <strong>www.bellycard.com<\/strong> and <strong>api.bellycard.com<\/strong>.  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide12_version-2-soa-cont.png\" alt=\"Version two of Belly's Stack on service-oriented architecture still routed to their API servers [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50331\" \/><\/p>\n<p>But it turned out to be really complex and not simple whatsoever, because some of our mobile applications were statically written to use <strong>www.bellycard.com\/api<\/strong> and <strong>\/api&#8209;assets<\/strong>. Our email providers and all of our click links that had already been emailed to people had <strong>\/eo<\/strong> for &#8220;email open&#8221; and <strong>\/ec<\/strong> for &#8220;email click&#8221;. Our Facebook app was configured to <strong>\/facebook\/callback<\/strong>.<\/p>\n<h2 id=\"Version2NGINX\">3:28 NGINX Replaces ELB<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide13_version-2-nginx.png\" alt=\"Version two of Belly's Stack used NGINX for multiple upstreams and the inclusion of rewrite rules [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50332\" \/><\/p>\n<p>So that meant that AWS Elastic Load Balancer no longer gave us the flexibility we needed to route this traffic to different applications. So NGINX came to save the day. Specifically, we could have multiple upstreams and rewrite rules to route traffic to our different applications.<\/p>\n<h2 id=\"Version2UpstreamExample\">3:44 Upstream and Virtual Server Configuration<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide14_version-2-upstream-example.png\" alt=\"NGINX upstream example with upstream API and upstream Homepage [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50333\" \/><\/p>\n<p>In this very basic example we define an <a target=\"_blank\" href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_upstream_module.html#upstream\"><code>upstream<\/code><\/a> block with six servers for the API and a block with two servers for the home page.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide15_version-2-nginx-configuration.png\" alt=\"NGINX configuration of server blocks listening on port 80 for api.bellycard.com and www.bellycard.com [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50334\" \/><\/p>\n<p>There are also two <a target=\"_blank\" href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_core_module.html#server\"><code>server<\/code><\/a> blocks [defining virtual servers]. On one we listen for <strong>api.bellycard.com<\/strong> and route all of that traffic to the API servers. On the other we listen on <strong>www.bellycard.com<\/strong> and route things accordingly.<\/p>\n<h2 id=\"Version2Results\">4:02 Results<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide16_version-2-results.png\" alt=\"Belly continued to implement services in version two of their stack [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50335\" \/><\/p>\n<p>This worked great. It allowed our developers to move fast. Our frontend people were working on one project and not being blocked by our API. So we deployed a third service. I believe it was for integration with apple&#8217;s Passbook. Then a fourth&nbsp;&ndash; we moved all of our Facebook functionality out. Our fifth was to deal with user events on our website. <\/p>\n<p>Then someone decided it was a great idea to write that in Node.js, so now we had five distinct services in two languages. They&#8217;re all deployed independently without much configuration management, and we were manually editing NGINX, which I&#8217;m sure a lot of people start with. <\/p>\n<h2 id=\"ConfigurationWithChef\">4:43 Configuration With Chef<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide17_configuration-with-chef.png\" alt=\"Configuration with Chef introductory slide [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50336\" \/><\/p>\n<p>We needed a better way to scale this process, so we did that with Chef. <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide18_intro-to-chef.png\" alt=\"Chef allows for configuration management through Ruby [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50337\" \/><\/p>\n<p>Chef is a configuration management suite. It&#8217;s written in Ruby and you can write your actual configuration management in Ruby, so it&#8217;s great for developers. <a target=\"_blank\" href=\"https:\/\/learn.chef.io\/\">learnchef.com<\/a> is an amazing resource for learning about Chef; it can teach you way more about Chef than I can in 45&nbsp;minutes. <\/p>\n<p>I will go over some of the basics just so we&#8217;re all in the same page, though. <\/p>\n<h2 id=\"Recipes\">5:17 Recipes<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide19_recipes.png\" alt=\"Recipes are the first building block in Chef for configuration management [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50338\" \/><\/p>\n<p>The first kind of building block in Chef is a <em>recipe<\/em>. Chef uses a lot of clever names like <em>knife<\/em> as a tool and <em>recipe<\/em> and everything&#8217;s in a <em>cookbook<\/em>. That makes it kind of fun and easy to figure out and provides a great metaphor, but also makes it really hard to Google for anything. <\/p>\n<p>So here&#8217;s the most basic recipe I could think of. Install the package <code>nginx<\/code> and start and enable the <code>nginx<\/code> service.  <\/p>\n<h2 id=\"Files\">5:56 Files<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide20_files.png\" alt=\"In Chef, recipes can be built into a templating system [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50339\" \/><\/p>\n<p>You can also have file resources. For example, I want to write the content <code>Hello<\/code>&nbsp;<code>World!<\/code> to the file <strong>\/usr\/share\/NGINX\/html\/index.html<\/strong>.<\/p>\n<h2 id=\"Templates2slides\">6:08 Templates<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide21_templates.png\" alt=\"Chef templates are used for writing content automatically [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50340\" \/><\/p>\n<p>You can also build that into a templating system. Here I have a couple of variables <code>g<\/code> and <code>w<\/code> for <code>Hello<\/code> and <code>Kevin<\/code> and I can pass these variables into a template file, which is in ERB format (a Ruby templating script).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide22_templates-cont.png\" alt=\"Chef templates are used to write content automatically [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50341\" \/><\/p>\n<p>So instead of just writing out the content manually, we can write a template for reading and who to greet.<\/p>\n<h2 id=\"Cookbooks\">6:19 Cookbooks<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide23_cookbooks.png\" alt=\"Chef cookbooks are a collection of recipies, files, and templates [presentation by Kevin Reedy ofBelly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50342\" \/><\/p>\n<p>The last major building block is a cookbook, which is collection of recipes, files, and templates. Also resources, providers, and libraries, which are more complex resources. There are tons of open source and other cookbooks at <a target=\"_blank\" href=\"https:\/\/supermarket.getchef.com\">https:\/\/supermarket.getchef.com<\/a>, including ones for NGINX, MySQL, Java, <code>apt<\/code> and <code>yum<\/code>, even managing your <strong>ssh_known_hosts<\/strong> files. Pretty much the main building blocks of what you&#8217;re trying to do in configuration management you can find in a cookbook already.<\/p>\n<h2 id=\"NGINXExample\">6:49 Example: Chef for NGINX<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide24_nginx-example-.png\" alt=\"Chef was used in the Belly Card stack to configure NGINX to route traffic to their application [presentation by Kevin Reedy Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"aligncenter size-full wp-image-50343\" \/><\/p>\n<p>We&#8217;ll go through a very quick NGINX example with Chef where we&#8217;re going to install NGINX and configure it by changing a couple of variables. We&#8217;ll use Chef to discover where our application is running, and then configure NGINX to route traffic to our application.<\/p>\n<h2 id=\"NGINXExampleTuning\">7:14 Installation, Configuration, and Tuning<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide25_nginx-example-tuning.png\" alt=\"In this example, this first thing that is done in the Chef configuration is to tune the NGINX node attributes, then to install nginx, find application nodes, and generate a site config\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50344\" \/><\/p>\n<p>So in this example, the first thing we do is set some node attributes. For every node, you can set attribute values by appending a JSON document. We&#8217;re going to use a third&#8209;party NGINX cookbook that looks for settings of these attributes. We&#8217;re setting <code>worker_connections<\/code> to a certain value, the <code>worker_rlimit_nofile<\/code> directive, what <code>event<\/code> type we&#8217;re going to use in NGINX, and even <code>client_max_body_size<\/code>.<\/p>\n<p>After that, we include two recipes from the NGINX cookbook. The first is <strong>nginx::repo<\/strong>, which sets up either an <code>apt<\/code> or <code>yum<\/code> repository depending on your operating system. Then [the <strong>nginx<\/strong> recipe] installs NGINX. You can also install NGINX from source, but using packages is faster.<\/p>\n<p>Next we take advantage of Chef&#8217;s search capabilities. When you run Chef, you can either run it as an agent on the machine, or you can run it in solo mode which essentially runs through it like a script. When you have an agent on a machine, you can query the Chef server for nodes and other attributes.<\/p>\n<p>Here we&#8217;re asking Chef to give us a list of all of the nodes that have the recipe <strong>belly&#8209;api<\/strong> attached to them. After that, we template out a site file called <strong>\/sites&#8209;enabled\/api<\/strong>. Its source is the template [file called <strong>api.erb<\/strong> that] I&#8217;ll show you next. The variables we pass in are the nodes we searched for just before.<\/p>\n<p>The last new thing here is a <code>notifies<\/code> statement to reload the <strong>nginx<\/strong> service. This says that whenever this template changes, I want you to reload this service. <\/p>\n<h2 id=\"NGINXExampleUpstreamapi\">8:54 Routing API Traffic<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide26_nginx-example-upstream-api.png\" alt=\"The Chef template file in this example includes an upstream called API and in ERB it iterates over all servers passed through as a variable [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50345\" \/><\/p>\n<p>So our template file here is pretty simple. We have an upstream called <strong>api<\/strong> and in ERB we iterate over all of the servers we pass through as a variable, specifying the server by IP address and port&nbsp;8080. We even throw in a comment with the server name. When an operator is looking for what&#8217;s going on with this service it&#8217;ll be a nice comment for them. <\/p>\n<p>We also define a <code>server<\/code> block to listen on <strong>api.bellycard.com<\/strong>. I just pass all traffic to the <strong>api<\/strong> upstream. <\/p>\n<h2 id=\"RealWorldExample\">9:46 Real-World Chef Example<\/h2>\n<p>I also want to provide a real&#8209;world example. It might be too small for you guys to read, but you can kind of just get a feel for how complicated your Chef recipes can get. This is the actual template and recipe from some of our production servers today.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide27_real-world-example.png\" alt=\"Real world example slide of how complicated Chef recipes can get [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50346\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide28_real-world-example-cont.png\" alt=\"Chef template and recipe from Belly Card's production servers [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50347\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide29_real-world-example-chef.png\" alt=\"Chef template and recipe from Belly Card's production servers [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50348\" \/><\/p>\n<h2 id=\"Version2DeployingApps\">10:08 Version 2: Deploying Apps<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide30_version-2-deploying-apps.png\" alt=\"In version two of Belly Card's stack, they deployed around 40 Ruby applications to their servers using Chef [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50349\" \/><\/p>\n<p>So now back to our stack. I told you we had deployed about five applications and that was going great. We now had a way to route traffic to them, so our developers just took hold of this and today we&#8217;re up to about 40&nbsp;Ruby applications that are deployed onto our servers using Chef. Traffic is routed through completely dynamically using NGINX. <\/p>\n<p>Our developers don&#8217;t really have to do anything other than edit one recipe to include the name of their new service. All of the traffic is routed by NGINX and dynamically configured with Chef. <\/p>\n<h2 id=\"Version3SOAAllTheThings\">10:40 Version 3: SOA All The Things<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide31_version-3-soa-all-the-things.png\" alt=\"Version three of Belly Card's stack included ten frontend Javascript applications deplyed onto S3 \/ Cloudfront and another five on Heroku [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50350\" \/><\/p>\n<p>We also had about ten frontend JavaScript applications that are just static files that we deploy on S3 with a CDN, [Amazon] CloudFront, in front of it. We served <strong>index.html<\/strong> out of the S3 bucket, and the rest of the assets were over CloudFront. We also had about another five apps that developers just pushed to Heroku because it was a one&#8209;off thing that eventually became production and nobody was really told about it. <\/p>\n<h2 id=\"Version3Heartbleed\">11:10 Then Heartbleed Happened<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide32_version-3-heartbleed.png\" alt=\"Version three of Belly Card's stack was affected by the Heartbleed bug [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50351\" \/><\/p>\n<p>[The five apps on Heroku] were separate&nbsp;&ndash; completely outside of our infrastructure&nbsp;&ndash; until a beautiful day in April when the Heartbleed bug was announced. A lot of people scrambled. <a href=\"https:\/\/www.nginx.com\/blog\/nginx-and-the-heartbleed-vulnerability\/\">Heartbleed<\/a> was a bug in OpenSSL that allowed people to potentially extract your private keys. <\/p>\n<p>Since we use Chef, we were able to upgrade our OpenSSL infrastructure across all of our boxes as soon as a patch was available. I believe it was deployed in about 20&nbsp;minutes everywhere in our entire infrastructure. However, AWS (and thus Heroku) took about 24&nbsp;hours to patch their Elastic Load Balancers. <\/p>\n<p>This prompted us to actually move all of our <a href=\"https:\/\/www.nginx.com\/resources\/glossary\/load-balancing\/\">load balancing<\/a> and <a href=\"https:\/\/www.nginx.com\/resources\/glossary\/ssl-load-balancer\/\">SSL termination<\/a> into NGINX regardless of where it was deployed&nbsp;&ndash; both S3 and Heroku. You take a slight penalty for doing so, but at least on that day in April it was worth it to us.<\/p>\n<h2 id=\"Version3SSLAllTheThings\">11:57 SSL All The Things<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide33_version-3-ssl-all-the-things.png\" alt=\"Version three of Belly Card's stack went from service-oriented architecture all the things to SSL all the things [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50352\" \/><\/p>\n<p>So version&nbsp;3 of our stack went from being &#8220;SOA all the things&#8221; to &#8220;SSL all the things&#8221;. All of our traffic to <strong>bellycard.com<\/strong> and its subdomains passes through our load balancers. Our assets are still actually served from CloudFront on a different domain for performance. We don&#8217;t want the overhead of SSL, and also we&#8217;re only a single data center on the East Coast.<\/p>\n<h2 id=\"Version4Docker\">12:25 Version 4: Docker<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide34_version-4-docker.png\" alt=\"Version four of Belly Card's stack introduced Docker [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50353\" \/><\/p>\n<p>Version 4&nbsp;&ndash; the buzzword everyone&#8217;s talking about this year is Docker. Since Docker was at I think version&nbsp;0.5, our developers were excited to start using it. <\/p>\n<h2 id=\"Version4Benefits\">12:38 Benefits<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide35_version-4-benefits.png\" alt=\"The benefits of Docker is that it simplifies deploying applications, it separates responsibility from development and operations, allows developers to easily spin up dependent servies, and it speeds up deployment [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50354\" \/><\/p>\n<p>The reasons [to use Docker] are simple. <\/p>\n<p>It greatly simplifies deploying applications. There are no recipes to write. You don&#8217;t need to worry about how you&#8217;re going to do asset compilation. It doesn&#8217;t matter if you&#8217;re writing your app in Ruby when somebody else is writing their app in Python.<\/p>\n<p>Essentially it separates the responsibilities of Development and Operations. Your developers can worry about code, what libraries to use, even which Linux packages to install. Operations can worry about more things like: where am I going to deploy this? How am I going to capture logging from it? How do I monitor it? Once you&#8217;ve solved those problems and operations, every application will work, regardless of what it&#8217;s written in.<\/p>\n<p>A bonus of this is that it allows our developers to easily spin up dependent services on their own laptops. As I mentioned we&#8217;re up to about 55&nbsp;different applications, many of which are now in Docker containers. <\/p>\n<p>Say you&#8217;re working on a new service for sending out email campaigns. It relies on the email service. So on your development machine you would typically download the code, run <code>bundle<\/code>&nbsp;<code>install<\/code>, start it up in the background, and &#8220;oh&nbsp;&ndash; but actually that relies on another service, the user service&#8221;. <\/p>\n<p>That service relies on something else and something else. So actually the new email&#8209;campaign service relies on about five other services plus MySQL, Elasticsearch, Redis, and the list goes on. <\/p>\n<p>With Docker we can actually build those containers from the same Chef recipes that we use in production and provide our developers with containers they can download extremely quickly. <\/p>\n<p>Thus this speeds up deployment and development. <\/p>\n<h2 id=\"Version4DeployingContainers\">14:23 Deploying Containers<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide36_version-4-deploying-containers.png\" alt=\"In version 4 of Belly Card's stack they went from deploying their applications using Chef to deploying them in containers such as CoreOS and Fleet, Deis, Mesos, Kubernetes, and Flynn [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50355\" \/><\/p>\n<p>So in version&nbsp;4 we went from deploying our applications using Chef to deploying them in containers. There are tons of solutions to do this and I&#8217;m sure there will be even more. <\/p>\n<p>The first is CoreOS and Fleet. CoreOS is a stripped&#8209;out Linux operating system that runs a service discovery engine on top of it called <a href=\"https:\/\/www.nginx.com\/blog\/service-discovery-nginx-plus-etcd\/\">etcd<\/a>. Fleet is a way for you to define things like &#8220;I want a container running five times and I don&#8217;t want it running on the same machines as this other service&#8221;. <\/p>\n<p>There&#8217;s another called Deis that is similar to Heroku in that it&#8217;s got a Git Push interface. There&#8217;s Apache&#8217;s Mesos and Google&#8217;s Kubernetes; Flynn is another one that&#8217;s just come out recently. We actually wrote our own because when we started this voyage most of these tools weren&#8217;t at a place where we could use them. <\/p>\n<p>Ours is called Jockey, and it&#8217;s worked out really great because our deployment process is extremely opinionated. What&#8217;s great about Docker is that it essentially provides a set of common APIs for containerization. We don&#8217;t have to worry about a lot of things we would if we were just using Linux&#8217;s LXC containers. <\/p>\n<p>We plan on open sourcing it, but as I mentioned deploys are really opinionated and what works for us might not work for you. <\/p>\n<h2 id=\"DeploymentStrategies\">15:45 Deployment Strategies<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide37_deployment-strategies.png\" alt=\"There are many deployment strategies, such as deploying in place, blue\/green deployments, and canary releases [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50356\" \/><\/p>\n<p>Also our deployment strategy kind of changed when we moved to Docker. Previously we were doing zero&#8209;downtime deploys by deploying a new version of the application in place. This means that if we had that user fax service on four machines, we would use Chef to pull down the new version, install it on top of [the old version], and then send a signal to the web server to reload. Unicorn is a great Ruby engine for doing that.<\/p>\n<p>However, containers are immutable. Once you have a running container, if you want to make a new build you should make a new container. You can kind of attach to it, run extra commands, and then save it&nbsp;&ndash; kind of like you would in Git. But really if you want to get the most out of Docker, your container should be completely immutable. <\/p>\n<p>So that means we had to figure out a new deployment strategy because we&#8217;re no longer able to deploy in place. There are a few out there. Blue&#8209;green is an interesting one. This is where if you&#8217;ve got an application running on four machines, you bring it up on four new machines and have your load balancer point to the new machines. Once you&#8217;re sure it&#8217;s actually working the way you expect, you can tear down your blue, and your green becomes the blue. Amazon&#8217;s got some really great tooling for doing this with AWS. <\/p>\n<p>There&#8217;s also &#8220;canary&#8221; deploys where I deploy one version of the new machine, monitor it, and if it passes metric checks and health checks then I can continue the deployment. <\/p>\n<p>The difference with containers here is you&#8217;re going to actually tear them down and spin up new ones, so you need some kind of system to track the location of each application running in your infrastructure. <\/p>\n<p>Chef is actually starting to become a really good way to do this. There&#8217;s a cookbook that&#8217;s being merged in called Chef Metal that allows you to actually treat containers like any other resource you would in your Chef infrastructure. <\/p>\n<h2 id=\"ServiceDiscovery\">17:43 Service Discovery Options<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide38_service-discovery.png\" alt=\"ZooKeeper, etcd, and Consul can all be used for service discovery [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50357\" \/><\/p>\n<p>There&#8217;s also service discovery. <a href=\"https:\/\/www.nginx.com\/blog\/service-discovery-nginx-plus-zookeeper\/\">ZooKeeper<\/a> is a very popular tool that&#8217;s used all over Hadoop. [Airbnb] has got a great product called <a target=\"_blank\" href=\"https:\/\/medium.com\/airbnb-engineering\/smartstack-service-discovery-in-the-cloud-4b8a080de619\">SmartStack<\/a> that allows you to do deployments and keep track of them in ZooKeeper. <\/p>\n<p>I mentioned <a href=\"https:\/\/www.nginx.com\/blog\/service-discovery-nginx-plus-etcd\/\">etcd<\/a>, which is essentially a distributed key&#8209;value store with a REST interface. This is great compared to ZooKeeper, which is more of a Java client library, because you can now communicate with etcd from any of your applications. A lot of those open source Platform&#8209;as&#8209;a&#8209;Service things I mentioned earlier&nbsp;&ndash; like Deis and Flynn and even Google&#8217;s Kubernetes&nbsp;&ndash; use etcd. If you&#8217;re using CoreOS, etcd is already there for you and built in. <\/p>\n<p>Another one has come out, also REST&#8209;based, is called <a href=\"https:\/\/www.nginx.com\/blog\/service-discovery-with-nginx-plus-and-consul\/\">Consul<\/a>. It&#8217;s released by the people from HashiCorp that make Vagrant and Packer and tons of other awesome tools. Consul is actually what we decided to use.<\/p>\n<h2 id=\"Consul\">18:33 Consul Architecture<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide39_consul.png\" alt=\"Diagram of how Consul works on a basic level for configuration management [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50358\" \/><\/p>\n<p>Here&#8217;s a diagram of the very basics of Consul. You can see there are multiple data centers, each with three servers. In Data Center&nbsp;1 there are also clients. What&#8217;s interesting is that among your server agents, you&#8217;ll elect a master and then all of your clients are actually aware of not only all the servers but also all the other clients. They then use a gossip protocol to communicate with all the other agents. <\/p>\n<p>It reminds me of an old AT&#038;T commercial where you tell two friends who tell two friends and they tell two friends and the message spreads that way throughout your entire network of nodes. [<em>Editor&nbsp;&ndash; Well, close: it was a commercial for <a target=\"_blank\" href=\"https:\/\/www.youtube.com\/watch?v=mcskckuosxQ\">Faberge Organics shampoo<\/a>.<\/em>] This is compared to etcd or ZooKeeper where you essentially just have server agents and your clients connect to them. <\/p>\n<p>With the Consul way of doing things, you would actually run a Consul client on pretty much every server, everywhere we&#8217;re deploying Docker, and everywhere we run our load balancers. So you can always talk to that API on localhost port 8500. <\/p>\n<h2 id=\"WhatisConsul\">19:30 Consul Features<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide40_what-is-consul.png\" alt=\"Consul is a distributed key-value store with service discovery, health checks, multiple data center support, long polling, and scaling [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50359\" \/><\/p>\n<p>Just like etcd, [Consul] is a distributed key&#8209;value store. You can set and get keys and they&#8217;ll maintain linearizability so you can make sure they&#8217;re always consistent. There are some gotchas there, but their documentation on where those problems are is phenomenal. However, unlike etcd they treat service discovery as a first&#8209;class citizen. <\/p>\n<p>Instead of just key&#8209;value pairs, you can also register services, for example an API. When you register a service, you tell Consul what node it&#8217;s running on. This means that when we deploy a container, we also tell Consul to register this service API on this host as well. <\/p>\n<p>On top of that Consul has distributed health checks, so now you can define Nagios&#8209;style health checks right inside your service&#8209;discovery platform. When you decide to query Consul, you can ask for a list of all locations where the API service is running. You can send a flag for &#8220;all&#8221; or &#8220;only passing&#8221; or &#8220;only unhealthy&#8221;. <\/p>\n<p>It also has support for multiple data centers. I mentioned you should be running that Consul client everywhere. When you start it up, you pass in a parameter specifying the data center. If you have a Data Center East and a Data Center West, you can query your local Consul client without having to be data&#8209;center&ndash;aware, asking for an address or something. <\/p>\n<p>We actually use the data center as a boundary for our staging and production environments. Our configuration for an application in staging might say, &#8220;ask me where <strong>MySQL.services.consul<\/strong> is&#8221;. Whether that&#8217;s actually running on a staging node or a production node, you&#8217;ll get the correct value without ever having to be data&#8209;center[&ndash;aware] or multienvironment&#8209;aware.<\/p>\n<p>The other thing that&#8217;s great about it (and also etcd) is it has long polling on its HTTP interface. This means you can query and ask for a list of all the servers that are running this service, but also keep the connection open for 60&nbsp;seconds and be notified right away if anything changes. The polling time can actually go all the way up to 10&nbsp;minutes. This means you can get instant notification of any changes to your infrastructure. <\/p>\n<p>The other thing I mentioned already is that you can build the ability to scale out your agents by having clients everywhere. This is great because you can always talk to localhost. <\/p>\n<p>The big question, though, is: now that I&#8217;ve got this data living in a distributed service&#8209;discovery platform, how do I configure NGINX from the data?<\/p>\n<h2 id=\"ConsulTemplate\">22:07 Consul Template<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide41_consul-template.png\" alt=\"Consul templates were changed recently with regards to services, health checking capabilities, and the key-value store [presentation by Kevin Reedy of Belly Card at nginx.conf 2014] \" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50360\" \/><\/p>\n<p>Thankfully there&#8217;s a tool called <a target=\"_blank\" href=\"https:\/\/www.hashicorp.com\/blog\/introducing-consul-template\/\">Consul Template<\/a>. It was literally released yesterday, which made me change my slides for today from this point on. It used to be called &#8220;Consul HAProxy&#8221;, which was a terrible name because it wasn&#8217;t actually specific to HAProxy.<\/p>\n<p>Consul Template gives you a very simple interface to build templates to subscribe to changes in services, the health checks of those services, and even a key&#8209;value store. This is so you can store settings like [the number of] NGINX workers inside the key&#8209;value store and regenerate your config when that changes.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide42_consul-template-cont.png\" alt=\"Consul templates now allow you to subscribe to changes, generate files from templates, and optionally calls a command on change [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50361\" \/><\/p>\n<p>The big ability here is to generate files from templates, which sounds really similar to what we were doing with Chef before. The bigger difference here is it uses the Golang templating language as opposed to ERB, which is Ruby. Once you change a file you can optionally call a command on the change, for example <code>service<\/code>&nbsp;<code>nginx<\/code>&nbsp;<code>reload<\/code>.<\/p>\n<h2 id=\"ConsulTemplateExample\">22:59 Command to Start Consul Template<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide43_consul-template-example.png\" alt=\"Example of Consul templates at the command line [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50362\" \/><\/p>\n<p>So here&#8217;s an example of running Consul Template at the command line. You point it to your actual Consul host, which in this case is <strong>consul&#8209;prod.example.com<\/strong>. In a real production environment, I would suggest running Consul everywhere and connecting to localhost.<\/p>\n<p>Then you pass in a template file, which is a Golang template. In our case we&#8217;re passing in <strong>\/etc\/consul&#8209;templates\/api<\/strong>. Then you pass in what file to actually write out, so in our case <strong>\/etc\/NGINX\/sites&#8209;enabled\/api<\/strong>. Then a command to call: <code>service<\/code>&nbsp;<code>nginx<\/code>&nbsp;<code>reload<\/code>.<\/p>\n<h2 id=\"ConsulTemplateUpstreams\">23:32 NGINX Configuration with Consul Template<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide44_consul-template-upstreams.png\" alt=\"Consul template example with multiple upstreams and a server block [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50363\" \/><\/p>\n<p>The Consul template looks really similar to a slide I had before, except instead of ERB it&#8217;s Golang. So we define two upstreams here. One&#8217;s called <strong>api<\/strong> and one&#8217;s called <strong>homepage<\/strong>. We have a <code>range<\/code> statement for the service API, and there are built&#8209;in variables for grabbing the IP and port of where that service is actually running.<\/p>\n<h2 id=\"ConsulConfigurationManagement\">23:54 Configuration Management with Consul Template<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide45_consul-configuration-management.png\" alt=\"With Consul templates, convergence on service changes is fast, but configuration management is still needed to generate templates [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50364\" \/><\/p>\n<p>What&#8217;s great about Consul Template and using it this way is that the convergence on service changes is fast. Previous to deploying with Docker, we had Chef running about every 5&nbsp;or 10&nbsp;minutes depending on the machine. That was fast enough for us because we were deploying in place.<\/p>\n<p>Now that we&#8217;re building a more advanced deployment system, we need to have notification of those changes in the seconds rather than minutes. We actually get it in the milliseconds, on average probably about 200&nbsp;milliseconds. <\/p>\n<p>However, configuration management is still needed to generate templates. We can&#8217;t just replace Chef with Consul. We still need to figure out how we&#8217;re going to deploy Consul and Consul Template and everything.<\/p>\n<h2 id=\"UsingCheftoDeployConsul\">24:35 Using Chef to Deploy Consul<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide46_using-chef-to-deploy-consul.png\" alt=\"Using Chef to deploy Consul templates [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50365\" \/><\/p>\n<p>So we are actually using Chef templates to deploy our Consul templates, which seems a little convoluted. What we do first in our Chef recipe is query our Consul host for a list of all services. Then we generate a Consul template [with the <code>template<\/code> and <code>source<\/code> statements] and we pass in our SSL cert and key locations and reload <code>consul&#8209;template<\/code>. [<em>Editor&nbsp;&ndash; Mr. Reedy initially referred to<\/em> <code>consul&#8209;haproxy<\/code><em>, which appears in the first<\/em> <code>notifies<\/code> <em>statement on the slide. He then corrected himself and clarified that the slide predates the release of Consul Template.<\/em>]<\/p>\n<p>We also write out a config file for Consul Template and we pass in [variables specifying] the Consul host, source, destination, and reload command. This is really the guts of how we&#8217;re using Chef and Consul together to get instantaneous updates to our NGINX configs. <\/p>\n<h2 id=\"ProTips\">25:44 Pro Tips<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide47_pro-tips.png\" alt=\"Pro tips introductory slide [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50366\" \/><\/p>\n<p>Next I&#8217;ve got some pro tips both about Chef, Consul, and NGINX configuration in general. <\/p>\n<h2 id=\"ChefPartialSearch\">25:52 Chef Partial Search<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide48_chef-partial-search.png\" alt=\"Chef partial search is a great way of searching through your services [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50367\" \/><\/p>\n<p>The first tip is if you&#8217;re using Chef search to find where your services are running, you should look into Chef partial search. The problem with Chef search is it makes a copy of the entire node, including all of the attributes. If you save a lot of things to your node&#8217;s attributes you&#8217;re gonna have to pull those over the wire while talking to your Chef server, and also store them in memory.<\/p>\n<p>If the Chef search returns hundreds or thousands of servers, you can easily see how you could run out of memory for your actual recipe. Partial search allows you to return only the keys you care about.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide49_chef-partial-search-example.png\" alt=\"Example of Chef partial search [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50368\" \/><\/p>\n<p>For example, we first had<\/p>\n<pre><code class=\"config\">nodes = search(:node, 'recipe:belly-api')<\/pre>\n<p><\/code><\/p>\n<p>That search returns every single piece of information about every object. This includes information on its disk space, RAM, CPU, and all the applications that have written their own node attributes.<\/p>\n<p>Instead, we can use partial search where we make a new hash called <code>search_keys<\/code> and give it a list of keys we care about&nbsp;&ndash; in this case only the name and IP address. Then we hit the very similar API called <code>partial_search<\/code> and we pass in the additional [<code>keys<\/code>] parameter. <\/p>\n<h2 id=\"DNS\">26:53 DNS<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide50_dns.png\" alt=\"NGINX resolves DNS in upstream blocks at config reload, so it may be necessary to use Chef to trigger NGINX reload on changes [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50369\" \/><\/p>\n<p>The second thing that bit us when we moved to load balancing in front of S3 and Heroku is that NGINX uses DNS to resolve the hostnames in the <code>upstream<\/code> blocks only at configuration reload. This means that if you are setting an upstream server to <strong>s3.amazonaws.com<\/strong> or <strong>anything.herokuapp.com<\/strong> and it changes, your load balancer doesn&#8217;t know about the new IP address until the next reload.<\/p>\n<p>The way we&#8217;ve gotten around that is to actually do the DNS resolution in Chef and then trigger the NGINX reload when that changes. This led to a second problem: [the order of addresses in a DNS response] is not always the same, so make sure you also sort the response before you write it into your template. <\/p>\n<h2 id=\"AvoidStaleConfig\">27:36 Avoiding Stale Config<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide51_avoid-stage-config.png\" alt=\"Avoid stale configurations by adding delete functions to your Chef configs [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50370\" \/><\/p>\n<p>The other issue when you start deploying tons of services is you want to avoid stale configuration. [<em>Editor&nbsp;&ndash; Mr. Reedy pauses the presentation briefly to answer a question from the audience.<\/em>] In Apache, NGINX, and other UNIX things, a common configuration pattern is to have a <strong>conf.d<\/strong> folder and then load everything from it. So if I deploy services A, B, and C, I make <strong>service-a.conf<\/strong>, <strong>service-b.conf<\/strong>, and <strong>service-c.conf<\/strong>. <\/p>\n<p>Then I decide to replace service&nbsp;B with&nbsp;D, and now have four configs in the NGINX configuration folder but service&nbsp;B no longer exists. You might end up with conflicts in&nbsp;B versus&nbsp;D because they have a similar hostname. <\/p>\n<p>There are two ways we&#8217;ve targeted doing this [removing stale configuration] in the past. With templates you can add a <code>:delete<\/code> function, but this became kind of unwieldy really quickly. We essentially had a list of old services that we would always try to delete if they existed. That didn&#8217;t really scale out.<\/li>\n<p>Another thing you can do is render all of your site configs to a single file. A lot of people have referred to this as &#8220;move idempotency upward&#8221;. Essentially, instead of having an NGINX config that loads from multiple sources, throw it all in your <strong>nginx.conf<\/strong>.<\/p>\n<p>That does make it a little unwieldy when you&#8217;re actually logging into the box, but hopefully we&#8217;re getting to the point where you&#8217;re not troubleshooting a config file live in production. If it&#8217;s still a little too unwieldy in your Chef recipes, what we do is have one <strong>nginx.conf<\/strong> that loads a series of different sites. For example one of our site config files lists about 30&nbsp;services, and then we have one for the Heroku one, one for all the S3 ones, etc.<\/p>\n<p>So if you render all of your site configs to a single or small amount of files, you don&#8217;t need to worry about cleaning up after yourself. If from Chef&#8217;s point of view a service goes away, Chef just stops rendering it to the actual file. <\/p>\n<h2 id=\"Questions\">30:35 Questions<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide52_questions.png\" alt=\"Questions [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"alignnone size-full wp-image-50371\" \/><\/p>\n<p><strong>Q:<\/strong> <em>How has Consul worked out for you so far?<\/em><\/p>\n<p><strong>A:<\/strong> We&#8217;ve loved Consul, and the thing I really like about it compared to something like etcd is that there are services that continue to be built on top of it. I don&#8217;t have to worry about service discovery. In fact, if you have legacy applications and you deploy (let&#8217;s say) your MySQL servers using Consul, Consul also provides a DNS interface. <\/p>\n<p>Your application doesn&#8217;t need to be aware of Consul; it can just query &#8220;what address do I get from <strong>mysqldb.services.consul<\/strong>?&#8221;. In this case, Consul is the DNS server. You set up your DNS infrastructure so that if the TLD [top&#8209;level domain] ends in <code>.consul<\/code>, the query is routed to Consul.<\/p>\n<p>It&#8217;s scaled out great for us. We&#8217;re running out about 3&nbsp;nodes in production for server agents and about 60&nbsp;clients. We haven&#8217;t hit any hiccups. They just keep adding features and don&#8217;t break backward compatibility so upgrading has always been great.<\/p>\n<p><strong>Q:<\/strong> <em>Do you use NGINX&nbsp;Plus?<\/em><\/p>\n<p><strong>A:<\/strong> We don&#8217;t. We&#8217;ve looked into it for exactly this, when we started working with Consul. If we didn&#8217;t need a templating system, it would be great because we could just have a script that listens to that HTTP long polling and then speaks to the NGINX API on your behalf. The only issue there is you probably also want a templating system in case NGINX were to restart. It would reload your upstream configurations right there. <\/p>\n<p>There&#8217;s also some work other people have done in NGINX Lua to query Consul directly. I haven&#8217;t seen a good implementation yet, because they tend to fall into one of two buckets. The first is they query Consul on every HTTP request, which is slow. The second is they load it at configuration runtime and you end up with the same problem you have with DNS. So the templating thing is best for now, but I wouldn&#8217;t be surprised if we see a way to do it with NGINX Lua in the future. <\/p>\n<p><strong>Q:<\/strong> <em>Would you ever move away from Chef?<\/em><\/p>\n<p><strong>A:<\/strong> There isn&#8217;t a great way built into Consul to maintain those templates, and we&#8217;re already deploying NGINX with Chef. Our recipes around there are hundreds, if not thousands, of lines long at this point for all the other things we&#8217;re doing with NGINX including logging, tracing, and some OAuth. I don&#8217;t see us moving away from Chef in our infrastructure and replacing it, but it&#8217;s a great augmentation and good way to kind of get rapid notifications.<\/p>\n<p><strong>Q:<\/strong> <em>How much overhead does Chef provide?<\/em><\/p>\n<p><strong>A:<\/strong> Because most of our machines are doing a single task, our actual Chef overhead is pretty low. RAM usage is low while it&#8217;s sitting idle. It actually sits in a timer and can fork off. Other people also run it in a <code>cron<\/code> if they don&#8217;t care about how fast it converges. We see a little bump in CPU in some of our heavier recipes, but nothing we can&#8217;t just deploy another server to fix. <\/p>\n<p><strong>Q:<\/strong> <em>How much have you done with CloudFormation?<\/em><\/p>\n<p><strong>A:<\/strong> We&#8217;re not using CloudFormation yet. It&#8217;s on our radar, but one change we&#8217;ve made is to utilize a lot more Auto Scaling groups. The way that&#8217;ll work is when an instance comes up, it&#8217;ll have a user data that contains its Chef run list. <\/p>\n<p>A new machine will come up into a cluster with a key already based on its image, so it&#8217;s able to talk to our Chef server, register itself, and pull down the cookbooks it needs. That solves most of the CloudFormation stuff for us because we&#8217;re not using a lot of other AWS tools that would go hand in hand with that.<\/p>\n<p><strong>Q:<\/strong> <em>How do you deal with a server being destroyed?<\/em><\/p>\n<p><strong>A:<\/strong> If a server goes away in an Auto Scaling group, you can use Amazon SNS [Simple Notification Service] to get a notification of that. We have a very simple service that listens for those notifications and then cleans up the Chef server. <\/p>\n<p><strong>Q:<\/strong> <em>Have you used OpsWorks from AWS?<\/em><\/p>\n<p><strong>A:<\/strong> We haven&#8217;t. We&#8217;ve been using the hosted Chef from the beginning and it&#8217;s worked great for us. I&#8217;m not sure about this, but I believe that OpsWorks is more like Chef Solo, where you&#8217;re using it just for provisioning. We still run Chef Agent on almost all of our machines, and use those same Chef recipes to generate Docker containers for development. <\/p>\n<h2 id=\"ContactInfo\">36:31 Contact Info<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cdn-1.wp.nginx.com\/wp-content\/uploads\/2017\/05\/reedy-conf2016-slide53_thank-you.png\" alt=\"Thank you [presentation by Kevin Reedy of Belly Card at nginx.conf 2014]\" width=\"1024\" height=\"768\" class=\"aligncenter size-full wp-image-50372\" \/><\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/www.nginx.com\/blog\/load-balance-dynamic-infrastructure-nginx-chef-consul\/\">Load Balancing a Dynamic Infrastructure with NGINX, Chef, and Consul<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/www.nginx.com\">NGINX<\/a>.<\/p>\n<p>Source: <a href=\"https:\/\/www.nginx.com\/blog\/load-balance-dynamic-infrastructure-nginx-chef-consul\/\" target=\"_blank\">Load Balancing a Dynamic Infrastructure with NGINX, Chef, and Consul<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"mh-excerpt\"><p>Load Balancing a Dynamic Infrastructure with NGINX, Chef, and Consul td { padding-right: 10px; } This post is adapted from a presentation by Kevin&nbsp;Reedy of Belly&nbsp;Card at nginx.conf in October&nbsp;2014. While that was a couple years ago, the content is still highly relevant today. You can view a recording of the presentation on YouTube. Table of Contents 0:00 Introduction &nbsp; Overview &nbsp; Disclaimer 1:16 What is Belly? &nbsp; Belly&#8217;s Stack 1:44 Version 0: The Dark Age 2:04 Version 1: A New Hope 2:35 Version 2: Separate Home Page and API &nbsp; Buzzword of the Day: Service-Oriented Architecture! &nbsp; Enable SOA &nbsp; NGINX Replaces ELB &nbsp; Upstream and Virtual Server Configuration &nbsp; Results 4:43 Configuration With Chef &nbsp; Recipes &nbsp; Files &nbsp; Templates &nbsp; Cookbooks 6:49 Example: Chef for NGINX &nbsp; Installation, Configuration, and Tuning &nbsp; Routing API Traffic 9:46 Real-World <a class=\"mh-excerpt-more\" href=\"https:\/\/jirak.net\/wp\/load-balancing-a-dynamic-infrastructure-with-nginx-chef-and-consul\/\" title=\"Load Balancing a Dynamic Infrastructure with NGINX, Chef, and Consul\">[ more&#8230; ]<\/a><\/p>\n<\/div>","protected":false},"author":1,"featured_media":16237,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[169],"tags":[652],"class_list":["post-16236","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news","tag-nginx"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/16236","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/comments?post=16236"}],"version-history":[{"count":1,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/16236\/revisions"}],"predecessor-version":[{"id":16238,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/16236\/revisions\/16238"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/media\/16237"}],"wp:attachment":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/media?parent=16236"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/categories?post=16236"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/tags?post=16236"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}