chef provisioning a chef server cluster - chefconf 2015

83

Upload: chef

Post on 07-Aug-2015

229 views

Category:

Technology


0 download

TRANSCRIPT

Chef Provisioning a Chef Server ClusterJoshua Timberman [email protected] @jtimberman

https://www.flickr.com/photos/jamidwyer/2844765976

Before we begin, provisioning a Chef Server:• Run chef-client... • Which talks to a different Chef Server... • Which downloads a recipe that... • Creates machines that run chef-client... • That install Chef Server packages... • Which then run chef-server-ctl reconfigure... • Which runs chef-solo to configure the Chef Server

Dense Content Ahead

https://www.flickr.com/photos/nicholas_t/9657510889

Where are we going*?*And why are we in this handbasket?

https://www.flickr.com/photos/steevithak/6936667291

Background

https://www.flickr.com/photos/malindaratz/13976135197

Bootstrap a Chef Server with Chef Solosudo  chef-­‐solo  \          -­‐c  /etc/chef/solo.rb  \          -­‐j  ~/chef.json  \          -­‐r  http://s3.amazonaws.com/chef-­‐solo/bootstrap-­‐latest.tar.gz  

As it turns out...This is a pretty good idea!

https://www.flickr.com/photos/nao904/6084536885

chef-server-ctl reconfigurefrontend-­‐chef-­‐server%  sudo  chef-­‐server-­‐ctl  reconfigure  Starting  Chef  Client,  version  11.18.0  Compiling  Cookbooks...  Recipe:  private-­‐chef::default  ....  Recipe:  private-­‐chef::default      *  file[/etc/opscode/chef-­‐server-­‐running.json]  action  create  (up  to  date)  

Running  handlers:  Running  handlers  complete  Chef  Client  finished,  7/228  resources  updated  in  7.282379304  seconds  opscode  Reconfigured!

omnibus-ctl reconfigure...def  reconfigure(exit_on_success=true)      status  =  run_command(          "chef-­‐solo  -­‐c  #{base_path}/embedded/cookbooks/solo.rb  -­‐j            #{base_path}/embedded/cookbooks/dna.json"      )      if  status.success?          log  "#{display_name}  Reconfigured!"          exit!  0  if  exit_on_success      else          exit!  1      end  end

Hosted Chef... is different (and that's the problem)

• Built using Chef cookbooks • (yay! ...but) • Many forked community cookbooks • (before berkshelf/librarian) • One cookbook per component/service • (postgresql, erchef, authz, rabbitmq, solr, etc) • Growth over time • (over 10k commits) • Not the same as what customers use • (chef-server-ctl reconfigure vs "knife ssh and chef-client")

Hosted Chef's Chef

Server Cluster

Hosted Chef Is a

Chef Server Cluster

VPCOur Use Case:Hosted Chef and its Chef Server running in AWS EC2

Chef Server 12"There is One Chef Server, and it is Open Source" - Adam Jacob

https://www.chef.io/blog/2014/09/08/there-is-one-chef-server-and-it-is-open-source/

You've probably heard this by now...• Multi-tenancy - required feature for Hosted Chef • Chef Push Jobs is opened now • Remove tension between Open Source Chef and Enterprise Chef codebase

• Remove tension between Hosted Enterprise Chef and Enterprise Chef code, too

Current state: Installing Chef Server 12Or, "this is how you do it manually per the documentation at docs.chef.io"

http://docs.chef.io/server/install_server.html

Installing Chef Server 12sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb  sudo  vi  /etc/opscode/chef-­‐server.rb  sudo  chef-­‐server-­‐ctl  reconfigure  

Or there's a cookbook for that...curl  -­‐L  https://www.chef.io/chef/install.sh  |  sudo  bash  sudo  mkdir  -­‐p  /var/chef/cache  /var/chef/cookbooks  wget  -­‐qO-­‐  https://supermarket.chef.io/cookbooks/chef-­‐server/download  |  sudo  tar  xvzC  /var/chef/cookbooks  wget  -­‐qO-­‐  https://supermarket.chef.io/cookbooks/chef-­‐server-­‐ingredient/download  |  sudo  tar  xvzC  /var/chef/cookbooks  wget  -­‐qO-­‐  https://supermarket.chef.io/cookbooks/packagecloud/download  |  sudo  tar  xvzC  /var/chef/cookbooks  

sudo  chef-­‐solo  -­‐o  'recipe[chef-­‐server::default]'  

But if you want a cluster...##  On  the  first  node  ("bootstrap  backend")  sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb  sudo  vi  /etc/opscode/chef-­‐server.rb  

##  manage  some  server  blocks  for  the  cluster  per  docs  sudo  chef-­‐server-­‐ctl  reconfigure  sudo  rsync  -­‐avz  /etc/opscode  [email protected]:/etc  

##  On  the  second  node  ("frontend")  sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb  sudo  chef-­‐server-­‐ctl  reconfigure  

Wait... what was that?

Wait. What was that?##  On  the  first  node  ("bootstrap  backend")  sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb  sudo  vi  /etc/opscode/chef-­‐server.rb  

##  manage  some  server  blocks  according  to  docs.chef.io...  sudo  chef-­‐server-­‐ctl  reconfigure  sudo  rsync  -­‐avz  /etc/opscode  [email protected]:/etc  

##  On  the  second  node  ("frontend")  sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb  sudo  chef-­‐server-­‐ctl  reconfigure  

rsync  /etc/opscode  ?!?!

Nevermind that we're installing a package with dpkg...

http://knowyourmeme.com/photos/388388-stahp

https://imgflip.com/i/hh9c4

Chef ProvisioningInception begins

https://www.flickr.com/photos/kija_kaji/14074377096

What is Chef Provisioning?• Previously known as "Chef Metal" • Manage machines as Chef resources • Various provisioners available • several come with ChefDK, e.g., aws, azure • Available as rubygems • Makes it easy to reason about standing up a cluster

Chef Provisioning has `machine` resourcesmachine  'database'  do      recipe  'example-­‐postgresql::server'  end  

machine  'cache'  do      recipe  'example-­‐memcached'  end  

machine  'www1'  do      recipe  'example-­‐nginx'  end  

machine  'www2'  do      recipe  'example-­‐nginx  end

Chef Provisioning extends Chef's Recipe DSL#  AWS  EC2...  with_driver('aws::us-­‐west-­‐2')  with_machine_options(      :bootstrap_options  =>  {          :key_name  =>  'hc-­‐metal-­‐provisioner',          :image_id  =>  'ami-­‐b99ed989',          :instance_type  =>  'm3.medium'      }  )  

#  Microsoft  Azure...  with_driver('azure')  with_machine_options(      :image_id  =>  'Ubuntu-­‐14_04_1-­‐LTS-­‐amd64-­‐server-­‐20140927-­‐en-­‐us-­‐30GB',      :bootstrap_options  =>  {          :vm_size  =>  'Standard_D1',          :other_options  =>  'Slides  are  only  so  big...'      }  )

Chef Provisioning a Chef Server Clustermachine  'backend'  do      recipe  'chef-­‐server-­‐cluster::bootstrap-­‐backend'  end  

machine  'frontend'  do      recipe  'chef-­‐server-­‐cluster::frontend'  end  

machine  'analytics'  do      recipe  'chef-­‐server-­‐cluster::analytics'  end

Chef Server (Hosted Chef)

Provisioner (laptop)

AWS EC2

backend

analytics

frontend

chef-client

chef

/kni

fe

chef-p

rovisioning-aws

SSH

SSH

SSH

Chef Provisioning Requires a... Provisioner• What is a provisioner? • What does it provision? • What does it need on the Chef Server?

AWS Authentication%  cat  ~/.aws/config  [default]  aws_access_key_id=AN_ACCESS_KEY_FOR_YOUR_IAM_USER  aws_secret_access_key=MATCHING_SECRET_KEY

SSH Access - Provisioner options{      :ssh_username  =>  "ubuntu",      :bootstrap_options  =>  {          :key_name  =>  "hc-­‐metal-­‐provisioner"      }  }

SSH Keys - Data Bag Item{      "id":  "hc-­‐metal-­‐provisioner",      "private_ssh_key":  "-­‐-­‐-­‐-­‐-­‐BEGIN  RSA  PRIVATE  KEY-­‐-­‐-­‐-­‐-­‐\nSNIP-­‐-­‐-­‐-­‐-­‐END  RSA  PRIVATE  KEY-­‐-­‐-­‐-­‐-­‐\n"  }  

setup-ssh-keys recipessh_keys  =  data_bag_item('secrets',  'hc-­‐metal-­‐provisioner')  key_dir    =  File.join(Dir.home,  '.ssh')  

directory  key_dir  do      recursive  true  end  

file  File.join(key_dir,  key_name)  do      content  ssh_keys['private_ssh_key']      sensitive  true  end  

chef-server-cluster cookbookReference Example, repo: https://github.com/opscode-cookbooks/chef-server-cluster

https://www.flickr.com/photos/dittaeva/5605435423

Chef Server ConfigurationAttributes, data bags, etc.

https://www.flickr.com/photos/cote/6549279435

Hand Wavy Magic

https://www.flickr.com/photos/michaelphotogs/5567990789/

Checkpoint!• ./.chef/config.rb for knife and chef-client • Uploaded cookbooks (using Policyfiles*) • Uploaded data bags • AWS authentication credentials in ~/.aws/config • SSH private key in ~/.ssh/keyname

* Due to time constraints, Policyfile discussion is not appearing in this talk

Provisioner node run list%  knife  node  show  chefconf-­‐provisioner  Node  Name:      chefconf-­‐provisioner  Environment:  _default  FQDN:  IP:                    10.13.37.102  Run  List:        recipe[chef-­‐server-­‐cluster::cluster-­‐provision]  Roles:  Recipes:          chef-­‐server-­‐cluster::cluster-­‐provision,  chef-­‐server-­‐cluster::setup-­‐provisioner,  chef-­‐server-­‐cluster::setup-­‐ssh-­‐keys  Platform:        mac_os_x  10.10.2  Tags:

chef-server-cluster cookbook attributesdefault['chef-server-cluster']['topology'] = 'tier' default['chef-server-cluster']['role'] = 'frontend' default['chef-server-cluster']['bootstrap']['enable'] = false default['chef-server-cluster']['chef-provisioner-key-name'] = ‘keyname’

default['chef-server-cluster']['driver'] = { 'gems' => [ { 'name' => 'chef-provisioning-aws', 'require' => 'chef/provisioning/aws_driver' } ], 'with-parameter' => 'aws::us-west-2' }

default['chef-server-cluster']['driver']['machine_options'] = { 'ssh_username' => 'ubuntu', 'use_private_ip_for_ssh' => false, 'bootstrap_options' => { 'key_name' => ‘keyname’, 'image_id' => 'ami-b99ed989', 'instance_type' => 'm3.medium' } }

chef-server-cluster::setup-provisionernode['chef-­‐server-­‐cluster']['driver']['gems'].each  do  |g|      chef_gem  g['name']      require  g['require']  if  g.has_key?('require')  end  

provisioner_machine_opts  =  node['chef-­‐server-­‐cluster']['driver']['machine_options'].to_hash  ChefHelpers.symbolize_keys_deep!(provisioner_machine_opts)  

with_driver(node['chef-­‐server-­‐cluster']['driver']['with-­‐parameter'])  with_machine_options(provisioner_machine_opts)  

What that actually looks like...chef_gem  'chef-­‐provisioning-­‐aws'  require  'chef/provisioning/aws'  

with_driver('aws::us-­‐west-­‐2')  with_machine_options({      :ssh_username                      =>  'ubuntu',      :use_private_ip_for_ssh  =>  false,      :bootstrap_options            =>  {          :key_name            =>  'hc-­‐metal-­‐provisioner',          :image_id            =>  'ami-­‐b99ed989',          :instance_type  =>'m3.medium'      }  })  

So in theory...{      "id":  "azure-­‐provisioner",      "default_attributes":  {          "chef-­‐server-­‐cluster":  {              "driver":  {                  "gems":  [{                      "name":  "chef-­‐provisioning-­‐azure",                      "require":  "chef/provisioning/azure_driver"                  }],                  "with-­‐parameter":  "azure",                  "machine_options":  {                      "bootstrap_options":  {                          "cloud_service_name":  "chef-­‐provisioner",                          "storage_aaccount_name":  "chef-­‐provisioner",                          "vm_size":  "Standard_D1",                          "location":  "West  US",                          "tcp_endpoints":  "80:80"                      },                      "image_id":  "b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-­‐14_04_1-­‐LTS-­‐amd64-­‐server-­‐20140927-­‐en-­‐us-­‐30GB",                      "password":  "SshKeysArentSupportedYet"                  }              }          }      }  }  

chef-server-cluster::cluster-provisioninclude_recipe  'chef-­‐server-­‐cluster::setup-­‐provisioner'  include_recipe  'chef-­‐server-­‐cluster::setup-­‐ssh-­‐keys'  

directory  '/tmp/stash'  do      recursive  true  end  

machine  'bootstrap-­‐backend'  do      recipe  'chef-­‐server-­‐cluster::bootstrap'      action  :converge      converge  true  end  #  machine  files  in  here...  machine  'frontend'  do      recipe  'chef-­‐server-­‐cluster::frontend'      action  :converge      converge  true      #  files  property...  end  

machine  'analytics'  do      recipe  'chef-­‐server-­‐cluster::analytics'      action  :converge      converge  true  end

machine resource<machine[bootstrap-­‐backend]      @name:  "bootstrap-­‐backend"      @allowed_actions:  [:nothing,  :allocate,  :ready,  :setup,  :converge,  :converge_only,  :destroy,  :stop]      @action:  [:converge]      @chef_server:  {          :chef_server_url=>"https://api.opscode.com/organizations/jtimberman-­‐chefconf",          :options=>{              :client_name=>"jtimberman",              :signing_key_filename=>"/Users/jtimberman/.chef/jtimberman.pem"          }      }

machine resource continued    @driver:  "aws::us-­‐west-­‐2"      @machine_options:  {          "ssh_username"=>"ubuntu",          "use_private_ip_for_ssh"=>false,          "bootstrap_options"=>{              "key_name"=>"hc-­‐metal-­‐provisioner",              "image_id"=>"ami-­‐b99ed989",              "instance_type"=>"m3.medium"          }      }      @run_list_modifiers:  [#<Chef::RunList::RunListItem:0x007f8cbe394d90  @version=nil,  @type=:recipe,  @name="chef-­‐server-­‐cluster::bootstrap">]      @ohai_hints:  {"ec2"=>"{}"}      @converge:  true  >

Data bagschef_server/topology.json  {      "id":  "topology",      "topology":  "tier",      "disabled_svcs":  [],      "enabled_svcs":  [],      "vips":  [],      "dark_launch":  {          "actions":  true      },      "api_fqdn":  "chef.jtimberman.name",      "analytics_fqdn":  "analytics.jtimberman.name",      "notification_email":  "[email protected]"  }  

Configuring the Cluster - bootstrap recipechef_server_config  =  data_bag_item('chef_server',  'topology').to_hash  chef_server_config.delete('id')  

chef_servers  =  search('node',  'chef-­‐server-­‐cluster_role:backend').map  do  |server|      {          :fqdn  =>  server['fqdn'],          :ipaddress  =>  server['ipaddress'],          :bootstrap  =>  server['chef-­‐server-­‐cluster']['bootstrap']['enable'],          :role  =>  server['chef-­‐server-­‐cluster']['role']      }  end  

if  chef_servers.empty?      chef_servers  =  [                                      {                                          :fqdn  =>  node['fqdn'],                                          :ipaddress  =>  node['ipaddress'],                                          :bootstrap  =>  true,                                          :role  =>  'backend'                                      }                                    ]  end  

chef_server_config['vips']  =  {  'rabbitmq'  =>  node['ipaddress']  }  chef_server_config['rabbitmq']  =  {  'node_ip_address'  =>  '0.0.0.0'  }  

Merge the configuration#  Merge  the  attributes  with  the  data  bag  values,  and  the  search  #  results  for  other  servers.  

node.default['chef-­‐server-­‐cluster'].merge!(chef_server_config)

Configuration templatetemplate '/etc/opscode/chef-server.rb' do source 'chef-server.rb.erb' variables(:chef_server_config => node['chef-server-cluster'], :chef_servers => chef_servers) notifies :reconfigure, 'chef_server_ingredient[chef-server-core]' end

Render configuration: /etc/opscode/chef-server.rbtopology  '<%=  @chef_server_config['topology']  %>'  api_fqdn  '<%=  @chef_server_config['api_fqdn']  %>'  

#  Analytics  configuration  dark_launch['actions']  =  <%=  @chef_server_config['dark_launch']['actions']  %>  <%  if  @chef_server_config.has_key?('vips')  -­‐%>  <%      @chef_server_config['vips'].each  do  |vip_name,  vip_add|  -­‐%>  <%=        vip_name  %>['vip']  =  '<%=  vip_add  %>'  <%      end  -­‐%>  <%  end  -­‐%>  <%  if  @chef_server_config.has_key?('rabbitmq')  &&  @chef_server_config['rabbitmq'].has_key?('node_ip_address')  -­‐%>  rabbitmq['node_ip_address']  =  '<%=  @chef_server_config['rabbitmq']['node_ip_address']  %>'  <%  end  -­‐%>  

oc_id['applications']  =  {      'analytics'  =>  {          'redirect_uri'  =>  'https://<%=  @chef_server_config['analytics_fqdn']  %>'      }  }  

/etc/opscode/chef-server.rb#  Server  blocks  <%  @chef_servers.each  do  |server|  -­‐%>  server  '<%=  server[:fqdn]  %>',      :ipaddress  =>  '<%=  server[:ipaddress]  %>',      <%  if  server[:bootstrap]  -­‐%>      :bootstrap  =>  true,      <%  end  -­‐%>      :role  =>  '<%=  server[:role]  %>'  <%      if  server[:role]  ==  'backend'  -­‐%>  backend_vip  '<%=  server[:fqdn]  %>',      :ipaddress  =>  '<%=  server[:ipaddress]  %>'  <%      end  -­‐%>  

<%  end  -­‐%>  

`server` blocks describe frontend and backend nodes

Rendered: backendtopology  'tier'  api_fqdn  'chef.jtimberman.name'  

dark_launch['actions']  =  true  rabbitmq['vip']  =  '172.31.12.241'  rabbitmq['node_ip_address']  =  '0.0.0.0'  

oc_id['applications']  =  {      'analytics'  =>  {          'redirect_uri'  =>  'https://analytics.jtimberman.name'      }  }  

server  'ip-­‐172-­‐31-­‐12-­‐241.us-­‐west-­‐2.compute.internal',      :ipaddress  =>  '172.31.12.241',      :bootstrap  =>  true,      :role  =>  'backend'  backend_vip  'ip-­‐172-­‐31-­‐12-­‐241.us-­‐west-­‐2.compute.internal',      :ipaddress  =>  '172.31.12.241'  

Rendered: frontendtopology  'tier'  api_fqdn  'chef.jtimberman.name'  

dark_launch['actions']  =  true  

oc_id['applications']  =  {      'analytics'  =>  {          'redirect_uri'  =>  'https://analytics.jtimberman.name'      }  }  

server  'ip-­‐172-­‐31-­‐12-­‐241.us-­‐west-­‐2.compute.internal',      :ipaddress  =>  '172.31.12.241',      :bootstrap  =>  true,      :role  =>  'backend'  backend_vip  'ip-­‐172-­‐31-­‐12-­‐241.us-­‐west-­‐2.compute.internal',      :ipaddress  =>  '172.31.12.241'  

server  'ip-­‐172-­‐31-­‐11-­‐8.us-­‐west-­‐2.compute.internal',      :ipaddress  =>  '172.31.11.8',      :role  =>  'frontend'  

Rendered: analyticstopology  'standalone'  analytics_fqdn  'analytics.jtimberman.name'  

Analytics config for the Chef Server is in:

/etc/opscode/analytics/actions-source.json

/etc/opscode-analytics/actions-source.json{      "private_chef":  {          "api_fqdn":  "chef.jtimberman.name",          "oc_id_application":  {              "name":  "analytics",              "uid":  "56d493b4ef2290cb29d9e73d34bd89688667b9f0d4583ac273e6e9de79ba3cb7",              "secret":  "Generated-­‐long-­‐secret",              "redirect_uri":  "https://analytics.jtimberman.name"          },          "rabbitmq_host":  "172.31.10.165",          "rabbitmq_port":  "5672",          "rabbitmq_vhost":  "/analytics",          "rabbitmq_exchange":  "actions",          "rabbitmq_user":  "actions",          "rabbitmq_password":  "generated-­‐long-­‐secret"      }

the bootstrap backend node

Configuration templatetemplate '/etc/opscode/chef-server.rb' do source 'chef-server.rb.erb' variables(:chef_server_config => node['chef-server-cluster'], :chef_servers => chef_servers) notifies :reconfigure, 'chef_server_ingredient[chef-server-core]' end

What is chef_server_ingredient??

chef-server-ingredient cookbook• What is an ingredient? • Clever, what's an addon? • What does the cookbook do? • How does the resource work?

• Primitive resource for installing/managing Chef Server add-ons

This automates these manual steps%  sudo  chef-­‐server-­‐ctl  install  opscode-­‐manage  %  sudo  opscode-­‐manage-­‐ctl  reconfigure  

chef_server_ingredient resources...chef_server_ingredient  'chef-­‐server-­‐core'  do      notifies  :reconfigure,  'chef_server_ingredient[chef-­‐server-­‐core]'  end  

chef_server_ingredient  'opscode-­‐reporting'  do      notifies  :reconfigure,  'chef_server_ingredient[opscode-­‐reporting]'  end  

chef_server_ingredient  'opscode-­‐manage'  do      notifies  :reconfigure,  'chef_server_ingredient[opscode-­‐manage]'  end  

chef_server_ingredient  'opscode-­‐analytics'  do      notifies  :reconfigure,  'chef_server_ingredient[opscode-­‐analytics]'  end

https://imgflip.com/i/hkkh3

http://www.warnerbros.com/matrix

chef_server_ingredientaction  :install  do      packagecloud_repo  'chef/stable'  do          type  value_for_platform_family(:debian  =>  'deb',  :rhel  =>  'rpm')      end  

   package  new_resource.package_name  do          options  new_resource.options          version  new_resource.version      end  end  

action  :reconfigure  do      ctl_cmd  =  ctl_command      execute  "#{new_resource.package_name}-­‐reconfigure"  do          command  "#{ctl_cmd}  reconfigure"      end  end

Omnibus package pattern is consistent:• Install the package • Write the configuration* • Run the reconfigure command

• Configuration can happen first - and does with the Chef Provisioning recipes

* or rsync it from a node, RIGHT?

Remember this?sudo  rsync  -­‐avz  /etc/opscode  [email protected]:/etc  

Hint: No one wants to remember this

Hello, machine_file!%w{  actions-­‐source.json  webui_priv.pem  }.each  do  |analytics_file|    machine_file  "/etc/opscode-­‐analytics/#{analytics_file}"  do          local_path  "/tmp/stash/#{analytics_file}"          machine  'bootstrap-­‐backend'          action  :download      end  end  

%w{  pivotal.pem  webui_pub.pem  }.each  do  |opscode_file|    machine_file  "/etc/opscode/#{opscode_file}"  do          local_path  "/tmp/stash/#{opscode_file}"          machine  'bootstrap-­‐backend'          action  :download      end  end

And the 'files' property of machine resourcemachine  'frontend'  do      recipe  'chef-­‐server-­‐cluster::frontend'      action  :converge      converge  true      files('/etc/opscode/webui_priv.pem'  =>  '/tmp/stash/webui_priv.pem',                  '/etc/opscode/webui_pub.pem'  =>  '/tmp/stash/webui_pub.pem',                  '/etc/opscode/pivotal.pem'  =>  '/tmp/stash/pivotal.pem')  end  

machine  'analytics'  do      recipe  'chef-­‐server-­‐cluster::analytics'      action  :converge      converge  true      files('/etc/opscode-­‐analytics/actions-­‐source.json'  =>  '/tmp/stash/actions-­‐source.json',                  '/etc/opscode-­‐analytics/webui_priv.pem'  =>  '/tmp/stash/webui_priv.pem')  end

Sure, we could rsync in the recipe...• But then we have to setup SSH keys between the nodes

• And all files in /etc/opscode, including ones put there by someone that shouldn't be there...

chef-client on the provisioner%  CHEF_NODE=chefconf-­‐provisioner  chef-­‐client  -­‐c  .chef/config.rb  Starting  Chef  Client,  version  12.0.3  [2015-­‐02-­‐18T14:28:12-­‐07:00]  WARN:  Using  experimental  Policyfile  feature  resolving  cookbooks  for  run  list:  ["chef-­‐server-­‐cluster::cluster-­‐[email protected]  (e1e803c)"]  Synchronizing  Cookbooks:      -­‐  chef-­‐server-­‐ingredient      -­‐  chef-­‐server-­‐cluster      -­‐  apt      -­‐  packagecloud      -­‐  chef-­‐vault  Compiling  Cookbooks...  ...  SNIP  converging  3  machines  ...  Chef  Client  finished,  11/16  resources  updated  in  1248.519725  seconds

machine resources converging*  machine[bootstrap-­‐backend]  action  converge      -­‐  Create  bootstrap-­‐backend  with  AMI  ami-­‐b99ed989  in  us-­‐west-­‐2      -­‐  create  node  bootstrap-­‐backend  at  https://api.opscode.com/organizations/jtimberman-­‐chefconf      -­‐      update  run_list  from  []  to  ["recipe[chef-­‐server-­‐cluster::bootstrap]"]      -­‐  waiting  for  bootstrap-­‐backend  (i-­‐553a519c  on  aws::us-­‐west-­‐2)  to  be  connectable      -­‐  bootstrap-­‐backend  is  now  connectable  

   -­‐  generate  private  key  (2048  bits)      -­‐  create  directory  /etc/chef  on  bootstrap-­‐backend      -­‐  write  file  /etc/chef/client.pem  on  bootstrap-­‐backend      -­‐  create  client  bootstrap-­‐backend  at  clients      -­‐      add  public_key  =  "-­‐-­‐-­‐-­‐-­‐BEGIN  PUBLIC  KEY-­‐-­‐-­‐-­‐-­‐\n...SNIP...-­‐-­‐-­‐-­‐-­‐END  PUBLIC  KEY-­‐-­‐-­‐-­‐-­‐\n"      -­‐  Add  bootstrap-­‐backend  to  client  read  ACLs      -­‐  Add  bootstrap-­‐backend  to  client  update  ACLs      -­‐  create  directory  /etc/chef/ohai/hints  on  bootstrap-­‐backend      -­‐  write  file  /etc/chef/ohai/hints/ec2.json  on  bootstrap-­‐backend      -­‐  write  file  /etc/chef/client.rb  on  bootstrap-­‐backend      -­‐  write  file  /tmp/chef-­‐install.sh  on  bootstrap-­‐backend      -­‐  run  'bash  -­‐c  '  bash  /tmp/chef-­‐install.sh''  on  bootstrap-­‐backend      [bootstrap-­‐backend]  Starting  Chef  Client,  version  12.1.1                                              Chef  Client  finished,  25/32  resources  updated  in  453.570204517  seconds      -­‐  run  'chef-­‐client  -­‐l  auto'  on  bootstrap-­‐backend  

Create and connect to EC2 instance*  machine[bootstrap-­‐backend]  action  converge      -­‐  Create  bootstrap-­‐backend  with  AMI  ami-­‐b99ed989  in  us-­‐west-­‐2      -­‐  create  node  bootstrap-­‐backend  at  https://api.opscode.com/organizations/jtimberman-­‐chefconf      -­‐      update  run_list  from  []  to  ["recipe[chef-­‐server-­‐cluster::bootstrap]"]      -­‐  waiting  for  bootstrap-­‐backend  (i-­‐553a519c  on  aws::us-­‐west-­‐2)  to  be  connectable      -­‐  bootstrap-­‐backend  is  now  connectable

Create the API client and give permission*  machine[bootstrap-­‐backend]  action  converge    -­‐  generate  private  key  (2048  bits)      -­‐  create  directory  /etc/chef  on  bootstrap-­‐backend      -­‐  write  file  /etc/chef/client.pem  on  bootstrap-­‐backend      -­‐  create  client  bootstrap-­‐backend  at  clients      -­‐      add  public_key  =  "RSA  Public  key  content"      -­‐  Add  bootstrap-­‐backend  to  client  read  ACLs      -­‐  Add  bootstrap-­‐backend  to  client  update  ACLs

Bootstrap like you may have seen...*  machine[bootstrap-­‐backend]  action  converge      -­‐  create  directory  /etc/chef/ohai/hints  on  bootstrap-­‐backend      -­‐  write  file  /etc/chef/ohai/hints/ec2.json  on  bootstrap-­‐backend      -­‐  write  file  /etc/chef/client.rb  on  bootstrap-­‐backend      -­‐  write  file  /tmp/chef-­‐install.sh  on  bootstrap-­‐backend      -­‐  run  'bash  -­‐c  '  bash  /tmp/chef-­‐install.sh''  on  bootstrap-­‐backend      [bootstrap-­‐backend]  Starting  Chef  Client,  version  12.1.1      Chef  Client  finished,  25/32  resources  updated  in  453.57  seconds      -­‐  run  'chef-­‐client  -­‐l  auto'  on  bootstrap-­‐backend  

machine files    *  machine_file[/etc/opscode-­‐analytics/actions-­‐source.json]  action  download          -­‐  download  file  /etc/opscode-­‐analytics/actions-­‐source.json  on  bootstrap-­‐backend  to  /tmp/stash/actions-­‐source.json      *  machine_file[/etc/opscode-­‐analytics/webui_priv.pem]  action  download          -­‐  download  file  /etc/opscode-­‐analytics/webui_priv.pem  on  bootstrap-­‐backend  to  /tmp/stash/webui_priv.pem      *  machine_file[/etc/opscode/pivotal.pem]  action  download          -­‐  download  file  /etc/opscode/pivotal.pem  on  bootstrap-­‐backend  to  /tmp/stash/pivotal.pem      *  machine_file[/etc/opscode/webui_pub.pem]  action  download          -­‐  download  file  /etc/opscode/webui_pub.pem  on  bootstrap-­‐backend  to  /tmp/stash/webui_pub.pem  ...SNIP          -­‐  upload  file  /tmp/stash/webui_priv.pem  to  /etc/opscode/webui_priv.pem  on  frontend          -­‐  upload  file  /tmp/stash/webui_pub.pem  to  /etc/opscode/webui_pub.pem  on  frontend          -­‐  upload  file  /tmp/stash/pivotal.pem  to  /etc/opscode/pivotal.pem  on  frontend

LIVE DEMO!

https://www.flickr.com/photos/drainrat/15222195545

Wrap-up and takeaways• Chef Server 12 is totally what you want to use • Using Chef to build Chef is awesome • Chef Provisioning makes deploying to EC2 easy • chef-server-cluster is a full working example • chef-server-ingredient is a lower level primitive • (and used by chef-server cookbook, too!) • Build your own with chef-server-ingredient

Questions?Joshua Timberman [email protected], @jtimberman

Links https://www.chef.io/blog/2014/09/08/there-is-one-chef-server-and-it-is-open-source/ https://github.com/opscode-cookbooks/chef-server-cluster https://github.com/chef/chef-dk/blob/master/POLICYFILE_README.md https://github.com/jtimberman/chefconf2015-chef-repo http://jtimberman.housepub.org/blog/2014/10/07/reflecting-on-six-years-with-chef/

https://www.flickr.com/photos/debord/4932655275