/chef-fundamentals

This code is the result of the chef fundamentals course at Avenue Code

Primary LanguageRuby

Chef Fundamentals

This training is about the basics of the chef. Considering the bellow inputs:

  • ip 192.168.3.xx
  • mask 255.255.252.0

At any error check if exist some tip on the Troubleshooting at the end of the page

Steps

1st Creating the project

  • node 🍲 vagrant init centos/7
  • node 🍲 vagrant up --provider virtualbox
  • node 🍲 [manual] Change Vagrantfile to use the given ip/mask and to be public
  config.vm.network "public_network", ip: "[ip]", netmask: "[mask]"
  • node 🍲 [manual] Change Vagrantfile to disable selinux, it will avoid problems with ports
  config.vm.provision "shell", inline: <<-SHELL
    sudo setenforce 0
    sudo sed -i 's/SELINUX=\(enforcing\|permissive\)/SELINUX=disabled/g' /etc/selinux/config
  SHELL
  • chef 🔪 [manual] Create a account at chef.io
  • chef 🔪 [manual] Download the skeleton project

2nd Registering the node on chef

  • node 🍲 vagrant destroy
  • node 🍲 vagrant up
  • node 🍲 vagrant ssh
  • node 🍲 ip addr list
  • chef 🔪 knife list
  • chef 🔪 knife client list
  • node 🍲 new_ip=$(vagrant ssh -c "ip address show eth1 | grep 'inet ' | sed -e 's/^.inet //' -e 's//.$//'")
  • chef 🔪 knife bootstrap $new_ip --sudo -x vagrant -P vagrant -N "node1"

3rd Creating and applying the first cookbook

  • node 🍲 vagrant ssh -c "sudo sed -i '$ a log_level :info' /etc/chef/client.rb"
  • chef 🔪 knife cookbook create apache
  • chef 🔪 [manual] Edit apache/recipes/default.rb
package 'httpd' { action :install }
service 'httpd' { action [ :enable, :start ] }
cookbook_file '/var/www/html/index.html' do
  source 'index.html'
  mode '0644'
end
  • chef 🔪 [manual] Create apache/files/default/index.html with some html content
  • chef 🔪 knife cookbook upload apache
  • chef 🔪 knife node run_list add node1 "recipe[apache]"
  • node 🍲 vagrant ssh -c "sudo chef-client"

Then check the result on the browser: http://[ip]/

4th Listing the result

  • chef 🔪 knife node list
  • chef 🔪 knife node show node1
  • node 🍲 vagrant ssh -c "ohai" | less
  • chef 🔪 knife node show node1 -l
  • chef 🔪 knife node show node1 -a fqdn
  • chef 🔪 knife search node ':' -a fqdn

5ft Using attributes

  • chef 🔪 [manual] Create apache/attributes/default.rb
default['apache']['indexfile'] = 'index1.html'
  • chef 🔪 [manual] Clone the index.html to index1.html and add something
  • chef 🔪 [manual] Change the default recipe to use the new property
...
cookbook_file '/var/www/html/index.html' do
  source node['apache']['indexfile']
...
  • chef 🔪 knife cookbook upload apache
  • node 🍲 vagrant ssh -c "sudo chef-client"

Then check the result on the browser: http://[ip]/

6th Creating new cookbooks

  • chef 🔪 knife cookbook create motd
  • chef 🔪 echo "default['motd']['company'] = 'Chef'" >> cookbooks/motd/attributes/default.rb
  • chef 🔪 [manual] Add a template to the recipe
template '/etc/motd' do
  source 'motd.erb'
  mode '0644'
end
  • chef 🔪 [manual] Create motd/template/default/motd.erb
This server is property of <%= node['motd']['company'] %>
<% if node['pci']['in_scope'] -%>
  This server is in-scope for PCI compliance
<% end -%>
  • chef 🔪 knife cookbook upload motd
  • chef 🔪 knife cookbook create pci
  • chef 🔪 echo "default['pci']['in_scope'] = true" >> cookbooks/pci/attributes/default.rb
  • chef 🔪 knife cookbook upload pci
  • chef 🔪 knife node run_list add node1 "recipe[motd]"
  • node 🍲 vagrant ssh -c "sudo chef-client"

This will fail because the pci is not associated to motd

  • chef 🔪 echo "depends 'pci'" >> cookbooks/motd/metadata.rb
  • chef 🔪 knife cookbook upload motd
  • node 🍲 vagrant ssh -c "sudo chef-client"

Run vagrant ssh to see the message after the login

7th Notifications

  • chef 🔪 echo "default['apache']['sites']['clowns'] = { 'port' => 80 }" >> cookbooks/apache/attributes/default.rb
  • chef 🔪 echo "default['apache']['sites']['bears'] = { 'port' => 81 }" >> cookbooks/apache/attributes/default.rb
  • chef 🔪 [manual] Disable the default virtual host at apache/recipes/default.rb
# Disable the default virtual host
execute 'mv /etc/httpd/conf.d/welcome.conf /etc/httpd/conf.d/welcome.conf.disable' do
  only_if do
    File.exist? '/etc/httpd/conf.d/welcome.conf'
  end
  notifies :restart, 'service[httpd]'
end
  • chef 🔪 [manual] Remove the cookbook_file block from apache/recipes/default.rb
  • chef 🔪 [manual] Add the new sites in apache/recipes/default.rb
# Iterate over the apache sites
node['apache']['sites'].each do |site_name,site_data|
  # Set the document root
  document_root = "/var/www/html/#{site_name}"

  template "/etc/httpd/conf.d/#{site_name}.conf" do
    source 'custom.erb'
    mode '0644'
    variables(
      document_root: document_root,
      port: site_data['port']
    )
    notifies :restart, 'service[httpd]'
  end

  directory document_root do
    mode '0755'
    recursive true
  end

  template "#{document_root}/index.html" do
    source 'index.html.erb'
    mode '0644'
    variables(
      site_name: site_name,
      port: site_data['port']
    )
  end
end
  • chef 🔪 [manual] Create template apache/templates/default/custom.rb from gist 8955103
  • chef 🔪 [manual] Create template apache/templates/default/index.html.rb from gist 8955080
  • chef 🔪 knife cookbook upload apache
  • node 🍲 vagrant ssh -c "sudo chef-client"

This will fail because the custom.rb has a tag without end

  • chef 🔪 [manual] Fix the template apache/templates/default/custom.rb
...
<Directory <%= @document_root %> >
...
  • chef 🔪 knife cookbook upload apache
  • node 🍲 vagrant ssh -c "sudo chef-client"

This will fail because start service is before the template render

  • chef 🔪 [manual] Move the service block to the end of the file at apache/recipe/default.rb
...
node['apache']['sites'].each do |site_name,site_data|
...
end

# Service need to start after all the configurations
service 'httpd' do
  action [ :enable, :start ]
end
  • chef 🔪 knife cookbook upload apache
  • node 🍲 vagrant ssh -c "sudo chef-client"

Then check the result on the browser: http://[ip]:80/ and http://[ip]:81/

8th Queries

  • chef 🔪 knife node show node1 -a fqdn -a ipaddress
  • chef 🔪 knife search node 'ipaddress:10* and platform_family:rhe1'
  • chef 🔪 [manual] Create recipe apache/recipes/ip-logger.rb
search('node', 'platform:centos').each do |server|
  log "The CentOS servers in your organization have the following FQDN/IP Addresses:- #{server['fqdn']}/#{server['ipaddress']}"
end
  • chef 🔪 knife cookbook upload apache
  • chef 🔪 knife node run_list add node1 "recipe[apache::ip-logger]"
  • node 🍲 vagrant ssh -c "sudo chef-client"

The log will print the message

  • chef 🔪 knife node run_list remove node1 "recipe[apache::ip-logger]"

9th User and groups

  • chef 🔪 mkdir -p data_bags/users
  • chef 🔪 knife data_bag create users
  • chef 🔪 Create the file data_bags/users/bobo.json
{
  "id": "bobo",
  "comment": "Bobo T. Chown",
  "uid": 2000,
  "gid": 0,
  "home": "/home/bobo",
  "shell": "/bin/bash"
}
  • chef 🔪 knife data_bag from file users bobo.json
  • chef 🔪 Create the file data_bags/users/frank.json
{
  "id": "frank",
  "comment": "Frank Belson",
  "uid": 2001,
  "gid": 0,
  "home": "/home/frank",
  "shell": "/bin/bash"
}
  • chef 🔪 knife data_bag from file users frank.json
  • chef 🔪 knife search users ':'
  • chef 🔪 knife search users 'id:bobo' -a shell
  • chef 🔪 mkdir -p data_bags/groups
  • chef 🔪 knife data_bag create groups
  • chef 🔪 Create the file data_bags/groups/clowns.json
{
  "id": "clowns",
  "gid": 3000,
  "members": ["bobo","frank"]
}
  • chef 🔪 knife data_bag from file groups clowns.json
  • chef 🔪 knife search groups ':'
  • chef 🔪 knife cookbook create users
  • chef 🔪 [manual] Edit the recipe users/recipes/default.rb
search('users', '*:*').each do |user_data|
  user user_data['id'] do
    comment user_data['comment']
    uid user_data['uid']
    gid user_data['gid']
    home user_data['home']
    shell user_data['shell']
  end
end

include_recipe 'users::groups'
  • chef 🔪 [manual] Create a new recipe users/recipes/groups.rb
search('groups', '*:*').each do |group_data|
  group group_data['id'] do
    gid group_data['gid']
    members group_data['members']
  end
end
  • chef 🔪 knife cookbook upload users
  • chef 🔪 knife node run_list add node1 "recipe[users]"
  • node 🍲 vagrant ssh -c "sudo chef-client"
  • node 🍲 vagrant ssh -c "cat /etc/group | grep clowns"
  • node 🍲 vagrant ssh -c "cat /etc/passwd | grep 200"

10th Role-based Attributes and Merge Order Precedence

  • node 🍲 vagrant ssh -c "setenforce 0;sed -i 's/SELINUX=(enforcing|permissive)/SELINUX=disabled/g' /etc/selinux/config"

I updated the 1st part, you can put this command on the vagrantfile to avoid do it manual and also to avoid problems if you need to recreate the node from scratch

  • node 🍲 vagrant reload
  • chef 🔪 [manual] Create new role file roles/webserver.rb
name 'webserver'
description 'web Server'
run_list 'recipe[apache]'
default_attributes({
                     apache: {
                       sites: {
                         admin: {
                           port: 8000
                         },
                         bears: {
                           port: 8001
                         }
                       }
                     }
})
  • chef 🔪 knife role from file webserver.rb
  • chef 🔪 knife role show webserver
  • chef 🔪 [manual] Go on the manager server at Nodes > Edit Run List
  • chef 🔪 [manual] Drag webserver to the run list and remove the apache
  • node 🍲 vagrant ssh -c "sudo chef-client"

Then check the result on the browser: http://[ip]:8000/ and http://[ip]:8001/

  • chef 🔪 [manual] Create new role file roles/base.rb
name 'base'
description 'Base Server Role'
run_list 'recipe[motd]', 'recipe[users]'
  • chef 🔪 knife role from file base.rb
  • chef 🔪 [manual] Edit roles/webserver.rb
...
run_list 'role[base]','recipe[apache]'
...
  • chef 🔪 knife role from file webserver.rb
  • chef 🔪 knife node run_list remove node1 "recipe[motd]"
  • chef 🔪 knife node run_list remove node1 "recipe[users]"
  • node 🍲 vagrant ssh -c "sudo chef-client"

Then check the result on the browser: http://[ip]:8000/ and http://[ip]:8001/

11th Environments

  • chef 🔪 mkdir environments
  • chef 🔪 [manual] Create the environment file environments/dev.rb
name 'dev'
description 'For developers!'
cookbook 'apache', '= 0.2.0'
  • chef 🔪 knife environment from file dev.rb
  • chef 🔪 knife environment show dev
  • chef 🔪 [manual] Go on the manager server at Nodes
  • chef 🔪 [manual] Change the enviroment of the node1 to use dev
  • chef 🔪 [manual] Create the environment file environments/production.rb
name 'production'
description 'For prods!'
cookbook 'apache', '= 0.1.0'
override_attributes({
  pci: {
    in_scope: true
  }
})
  • chef 🔪 knife environment from file production.rb
  • chef 🔪 knife node environment_set node1 production
  • node 🍲 vagrant ssh -c "sudo chef-client"

12th Chef Supermarket

  • chef 🔪 [manual] Go to supermarket
  • chef 🔪 [manual] Search chef-client

You will see all the information about this cookbook and others

  • chef 🔪 knife cookbook site search chef-client
  • chef 🔪 knife cookbook site show chef-client

Like the browser, a command way to search for new cookbooks

  • chef 🔪 knife cookbook site download chef-client
  • chef 🔪 tar -zxvf chef-client*.tar.gz -C cookbooks
  • chef 🔪 [manual] Check the file chef-client/recipes/delete_validation.rb
  • chef 🔪 [manual] Edit the role roles/base.rb
...
run_list "recipe[chef-client::delete_validation]", "recipe[motd]", "recipe[users]"
  • chef 🔪 knife role from file base.rb
  • chef 🔪 [manual] Check the file chef-client/recipes/default.rb

This class is just the default call and will delegate to the service recipe

  • chef 🔪 [manual] Check the file chef-client/recipes/service.rb
  • chef 🔪 knife cookbook upload chef-client

Will fail with dependence to cron

  • chef 🔪 [manual] knife cookbook site download cron
  • chef 🔪 [manual] tar -zxvf cron*.tar.gz -C cookbooks
  • chef 🔪 knife cookbook upload cron
  • chef 🔪 knife cookbook upload chef-client

Will fail with dependence to log rotate

  • chef 🔪 knife cookbook site download logrotate
  • chef 🔪 tar -zxvf logrotate*.tar.gz -C cookbooks
  • chef 🔪 knife cookbook site download windows
  • chef 🔪 tar -zxvf windows*.tar.gz -C cookbooks
  • chef 🔪 knife cookbook site download chef_handler
  • chef 🔪 tar -zxvf chef_handler*.tar.gz -C cookbooks
  • chef 🔪 knife cookbook upload logrotate
  • chef 🔪 knife cookbook upload chef_handler
  • chef 🔪 knife cookbook upload windows
  • chef 🔪 knife cookbook upload chef-client
  • node 🍲 vagrant ssh -c "sudo chef-client"

Finally, it will delete the validation pem

  • chef 🔪 knife cookbook site download ntp
  • chef 🔪 tar -zxvf ntp*.tar.gz -C cookbooks/
  • chef 🔪 knife cookbook upload ntp
  • chef 🔪 [manual] Edit the role roles/base.rb
...
run_list "recipe[chef-client::delete_validation]", "recipe[chef-client]", "recipe[motd]", "recipe[users]"
  • chef 🔪 knife role from file base.rb
  • node 🍲 vagrant ssh -c "sudo chef-client"

Cool, finally done

13th Working chef

Just read the pdf XD

Troubleshooting

  • To solve the clock problem that happens on Mac, put this line in the Vagrantfile.
config.vm.provision :shell, :inline => "sudo rm /etc/localtime && sudo ln -s /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime", run: "always"
  • To solve the problem with the kitchen about the conflicts of gems, just drop the file ~/.checfdk2.

  • If you need to destroy and create again

  • node 🍲 vagrant destroy

  • node 🍲 vagrant up

  • node 🍲 new_ip=$(vagrant ssh -c "ip address show eth1 | grep 'inet ' | sed -e 's/^.inet //' -e 's//.$//'")

  • node 🍲 ssh-keygen -R ${new_ip//[^([:alnum:]|\.)]/}

  • chef 🔪 knife node delete node1

  • chef 🔪 (cd ../chef-repo;knife bootstrap $new_ip --sudo -x vagrant -P vagrant -N "node1")

  • chef 🔪 knife node run_list add node1 "role[webserver]"

  • node 🍲 vagrant ssh -c "sudo sed -i '$ a log_level :info' /etc/chef/client.rb"

  • node 🍲 vagrant ssh -c "sudo chef-client"

You can use the script at node/recreate.sh

  • node 🍲 sh recreate.sh