SlideShare a Scribd company logo
Chef Provisioning a Chef Server Cluster - ChefConf 2015
Chef Provisioning a Chef Server Cluster - ChefConf 2015
Chef Provisioning a Chef Server Cluster
Joshua Timberman
joshua@chef.io
@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
Chef Provisioning a Chef Server Cluster - ChefConf 2015
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
Chef Provisioning a Chef Server Cluster - ChefConf 2015
Bootstrap a Chef Server with Chef Solo
sudo	
  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 reconfigure
frontend-­‐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
VPC
Our 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 12
Or, "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 12
sudo	
  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	
  root@frontend.example.com:/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	
  root@frontend.example.com:/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 Provisioning a Chef Server Cluster - ChefConf 2015
Chef Provisioning
Inception 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` resources
machine	
  '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 Cluster - ChefConf 2015
Chef Provisioning a Chef Server Cluster
machine	
  '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 Provisioning a Chef Server Cluster - ChefConf 2015
Chef Server
(Hosted Chef)
Provisioner
(laptop)
AWS EC2
backend
analytics
frontend
chef-client
chef/knife
chef-provisioning-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 recipe
ssh_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 cookbook
Reference Example, repo:
https://github.com/opscode-cookbooks/
chef-server-cluster
https://www.flickr.com/photos/dittaeva/5605435423
Chef Server
Configuration
Attributes, 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 attributes
default['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-provisioner
node['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-provision
include_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,	
  :d
estroy,	
  :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 bags
chef_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":	
  "ops@jtimberman.name"	
  
}	
  
Configuring the Cluster - bootstrap recipe
chef_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 template
template '/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.rb
topology	
  '<%=	
  @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: backend
topology	
  '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: frontend
topology	
  '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: analytics
topology	
  '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 template
template '/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_ingredient
action	
  :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	
  root@frontend.example.com:/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 resource
machine	
  '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-­‐
provision@0.0.9	
  (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
joshua@chef.io, @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

More Related Content

Chef Provisioning a Chef Server Cluster - ChefConf 2015

  • 3. Chef Provisioning a Chef Server Cluster Joshua Timberman joshua@chef.io @jtimberman https://www.flickr.com/photos/jamidwyer/2844765976
  • 4. 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
  • 7. Where are we going*? *And why are we in this handbasket? https://www.flickr.com/photos/steevithak/6936667291
  • 10. Bootstrap a Chef Server with Chef Solo sudo  chef-­‐solo            -­‐c  /etc/chef/solo.rb            -­‐j  ~/chef.json            -­‐r  http://s3.amazonaws.com/chef-­‐solo/bootstrap-­‐latest.tar.gz  
  • 11. As it turns out... This is a pretty good idea! https://www.flickr.com/photos/nao904/6084536885
  • 12. chef-server-ctl reconfigure frontend-­‐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!
  • 13. 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
  • 14. 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")
  • 15. Hosted Chef's Chef Server Cluster Hosted Chef Is a Chef Server Cluster VPC Our Use Case: Hosted Chef and its Chef Server running in AWS EC2
  • 16. 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/
  • 17. 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
  • 18. Current state: Installing Chef Server 12 Or, "this is how you do it manually per the documentation at docs.chef.io" http://docs.chef.io/server/install_server.html
  • 19. Installing Chef Server 12 sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb   sudo  vi  /etc/opscode/chef-­‐server.rb   sudo  chef-­‐server-­‐ctl  reconfigure  
  • 20. 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]'  
  • 21. 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  root@frontend.example.com:/etc   ##  On  the  second  node  ("frontend")   sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb   sudo  chef-­‐server-­‐ctl  reconfigure  
  • 23. 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  root@frontend.example.com:/etc   ##  On  the  second  node  ("frontend")   sudo  dpkg  -­‐i  chef-­‐server-­‐core*.deb   sudo  chef-­‐server-­‐ctl  reconfigure  
  • 24. rsync  /etc/opscode  ?!?! Nevermind that we're installing a package with dpkg...
  • 29. 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
  • 30. Chef Provisioning has `machine` resources machine  '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
  • 31. 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...'      }   )
  • 33. Chef Provisioning a Chef Server Cluster machine  '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
  • 35. Chef Server (Hosted Chef) Provisioner (laptop) AWS EC2 backend analytics frontend chef-client chef/knife chef-provisioning-aws SSH SSH SSH
  • 36. Chef Provisioning Requires a... Provisioner • What is a provisioner? • What does it provision? • What does it need on the Chef Server?
  • 37. AWS Authentication %  cat  ~/.aws/config   [default]   aws_access_key_id=AN_ACCESS_KEY_FOR_YOUR_IAM_USER   aws_secret_access_key=MATCHING_SECRET_KEY
  • 38. SSH Access - Provisioner options {      :ssh_username  =>  "ubuntu",      :bootstrap_options  =>  {          :key_name  =>  "hc-­‐metal-­‐provisioner"      }   }
  • 39. SSH Keys - Data Bag Item {      "id":  "hc-­‐metal-­‐provisioner",      "private_ssh_key":  "-­‐-­‐-­‐-­‐-­‐BEGIN  RSA  PRIVATE   KEY-­‐-­‐-­‐-­‐-­‐nSNIP-­‐-­‐-­‐-­‐-­‐END  RSA  PRIVATE  KEY-­‐-­‐-­‐-­‐-­‐ n"   }  
  • 40. setup-ssh-keys recipe ssh_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  
  • 41. chef-server-cluster cookbook Reference Example, repo: https://github.com/opscode-cookbooks/ chef-server-cluster https://www.flickr.com/photos/dittaeva/5605435423
  • 42. Chef Server Configuration Attributes, data bags, etc. https://www.flickr.com/photos/cote/6549279435
  • 44. 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
  • 45. 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:
  • 46. chef-server-cluster cookbook attributes default['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' } }
  • 47. chef-server-cluster::setup-provisioner node['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)  
  • 48. 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'      }   })  
  • 49. 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"                  }              }          }      }   }  
  • 50. chef-server-cluster::cluster-provision include_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
  • 51. machine resource <machine[bootstrap-­‐backend]      @name:  "bootstrap-­‐backend"      @allowed_actions:   [:nothing,  :allocate,  :ready,  :setup,  :converge,  :converge_only,  :d estroy,  :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"          }      }
  • 52. 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   >
  • 53. Data bags chef_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":  "ops@jtimberman.name"   }  
  • 54. Configuring the Cluster - bootstrap recipe chef_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'  }  
  • 55. 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)
  • 56. Configuration template template '/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
  • 57. Render configuration: /etc/opscode/chef-server.rb topology  '<%=  @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']  %>'      }   }  
  • 58. /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
  • 59. Rendered: backend topology  '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'  
  • 60. Rendered: frontend topology  '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'  
  • 61. Rendered: analytics topology  'standalone'   analytics_fqdn  'analytics.jtimberman.name'   Analytics config for the Chef Server is in: /etc/opscode/analytics/actions-source.json
  • 62. /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
  • 63. Configuration template template '/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??
  • 64. 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
  • 65. This automates these manual steps %  sudo  chef-­‐server-­‐ctl  install  opscode-­‐manage   %  sudo  opscode-­‐manage-­‐ctl  reconfigure  
  • 66. 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
  • 69. chef_server_ingredient action  :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
  • 70. 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?
  • 71. Remember this? sudo  rsync  -­‐avz  /etc/opscode  root@frontend.example.com:/etc   Hint: No one wants to remember this
  • 72. 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
  • 73. And the 'files' property of machine resource machine  '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
  • 74. 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...
  • 75. 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-­‐ provision@0.0.9  (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
  • 76. 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  
  • 77. 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
  • 78. 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
  • 79. 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  
  • 80. 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
  • 82. 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