Instalación y Configuración de Chef
Chef is a flexible infrastructure automation platform using a client-server model with a Ruby-based domain-specific language for defining system configurations. Chef separates code (cookbooks) from data (attributes, data bags) and enables powerful, idempotent infrastructure management. This guide covers workstation setup, server installation, node bootstrapping, cookbook creation, recipe development, data bags, and kitchen testing.
Tabla de Contenidos
- Chef Overview
- Chef Server Installation
- Workstation Setup
- Node Bootstrap
- Cookbook Structure
- Writing Recipes
- Attributes and Data Bags
- Testing with Test Kitchen
- Chef Automate Integration
- Conclusion
Descripción General de Chef
Chef is an infrastructure-as-code platform using a client-server architecture where a central Chef Server manages configurations deployed to Chef nodes. Chef uses Ruby for its domain-specific language, providing powerful programming capabilities for infrastructure automation.
Key components:
- Chef Server: Central authority managing node configurations
- Chef Workstation: Development environment for creating cookbooks
- Chef Client: Runs on managed nodes, pulls configuration from server
- Cookbooks: Collections of recipes, attributes, templates, and files
- Recipes: Ruby code defining infrastructure state
- Attributes: Variables and default configuration values
- Data Bags: Encrypted data storage for secrets and configurations
Architecture:
┌──────────────────────┐
│ Chef Server │
│ (Central Authority) │
└──────────┬───────────┘
│ API
┌─────┴─────┬──────────┐
│ │ │
Client Client Client
(Ubuntu) (CentOS) (Debian)
│ │ │
Pull config Pull config Pull config
│ │ │
Converge Converge Converge
(Apply) (Apply) (Apply)
Instalación del Servidor Chef
Install and configure Chef Server for managing infrastructure.
Installation:
# Download Chef Server
wget https://packages.chef.io/files/stable/chef-server/15.3.0/ubuntu/20.04/chef-server-core_15.3.0-1_amd64.deb
# Install
sudo dpkg -i chef-server-core_15.3.0-1_amd64.deb
# Reconfigure
sudo chef-server-ctl reconfigure
# Create admin user
sudo chef-server-ctl user-create admin Admin User [email protected] 'password' --filename admin.pem
# Create organization
sudo chef-server-ctl org-create myorg "My Organization" --association_user admin --filename myorg-validator.pem
# Enable Chef Manage (web UI)
sudo chef-server-ctl install chef-manage
sudo chef-server-ctl reconfigure
Access Chef Server:
# Web UI (requires SSL)
https://chef-server.example.com/organizations/myorg
# API endpoint
https://chef-server.example.com/organizations/myorg
Chef Server configuration:
# /etc/chef-server/chef-server.rb
# Server API port
nginx['port'] = 443
# Enable Chef Analytics
analytics_enabled = true
# Memory allocation
opscode_erchef['max_request_size'] = 2000000
# PostgreSQL settings
postgresql['shared_buffers'] = '256MB'
Configuración de la Estación de Trabajo
Set up Chef on development machines.
Install Chef Workstation:
# macOS
brew install chef-workstation
# Ubuntu/Debian
wget https://packages.chef.io/files/stable/chef-workstation/21.1.263/ubuntu/20.04/chef-workstation_21.1.263-1_amd64.deb
sudo dpkg -i chef-workstation_21.1.263-1_amd64.deb
# Windows
choco install chef-workstation
# Verify
chef -v
kitchen version
cookstyle --version
Configure knife:
# Create knife configuration
mkdir -p ~/.chef
cd ~/.chef
# Create knife.rb
cat > knife.rb << 'EOF'
current_dir = File.dirname(__FILE__)
log_level :info
log_location STDOUT
node_name 'admin'
client_key "#{current_dir}/admin.pem"
validation_client_name 'myorg-validator'
validation_key "#{current_dir}/myorg-validator.pem"
chef_server_url 'https://chef-server.example.com/organizations/myorg'
cookbook_path ["#{current_dir}/../cookbooks"]
EOF
# Copy keys from Chef Server
scp admin@chef-server:admin.pem ~/.chef/
scp admin@chef-server:myorg-validator.pem ~/.chef/
# Test connection
knife client list
Create cookbook:
# Generate cookbook
chef generate cookbook my_app
# Structure created:
# my_app/
# ├── recipes/
# │ └── default.rb
# ├── attributes/
# │ └── default.rb
# ├── templates/
# ├── files/
# ├── spec/
# ├── test/
# ├── metadata.rb
# └── README.md
Bootstrap de Nodos
Add nodes to Chef Server and configure management.
Bootstrap node:
# SSH bootstrap (most common)
knife bootstrap 192.168.1.100 \
--ssh-user ubuntu \
--sudo \
--identity-file ~/.ssh/id_rsa \
--node-name web01 \
--run-list 'recipe[base],recipe[nginx]' \
--chef-license accept
# Bootstrap with custom JSON
knife bootstrap 192.168.1.100 \
--ssh-user ubuntu \
--sudo \
--node-name web02 \
--json-attributes '{"nginx":{"port":8080}}'
# Windows bootstrap
knife bootstrap windows winrm 192.168.1.100 \
--winrm-user Administrator \
--winrm-password 'P@ssw0rd' \
--node-name win-server
Manage nodes:
# List nodes
knife node list
# Show node details
knife node show web01
# Edit node
knife node edit web01
# Delete node
knife node delete web01
# Run Chef on node
knife ssh 'name:web01' 'sudo chef-client'
# SSH to node with knife
knife ssh 'name:web01' 'bash -l' -x ubuntu
Estructura de Cookbook
Organize Chef code in properly structured cookbooks.
Standard cookbook layout:
my_app/
├── metadata.rb # Cookbook metadata
├── README.md # Documentation
├── attributes/
│ └── default.rb # Default attributes
├── recipes/
│ ├── default.rb # Main recipe
│ ├── nginx.rb # Nginx setup
│ └── database.rb # Database setup
├── templates/
│ ├── nginx.conf.erb # Nginx config template
│ └── app.conf.erb # App config template
├── files/
│ ├── default/
│ │ └── app.jar # Static files
│ └── ubuntu/ # OS-specific files
├── libraries/
│ └── helpers.rb # Custom libraries
├── providers/
│ └── custom.rb # Custom resources
├── resources/
│ └── custom.rb # Custom resources
├── spec/
│ ├── spec_helper.rb
│ └── unit/
│ └── recipes/
│ └── default_spec.rb
├── test/
│ └── integration/
│ └── default/
│ └── default_test.rb
└── .kitchen.yml # Test Kitchen config
metadata.rb:
# my_app/metadata.rb
name 'my_app'
description 'Application deployment cookbook'
version '1.0.0'
chef_version '>= 15.0'
maintainer 'DevOps Team'
maintainer_email '[email protected]'
license 'Apache-2.0'
supports 'ubuntu', '= 20.04'
supports 'centos', '>= 7.0'
depends 'ark', '~> 10.0'
depends 'nodejs', '~> 7.0'
depends 'postgresql', '~> 11.0'
Escribiendo Recetas
Create recipes that define infrastructure state.
Basic recipe:
# my_app/recipes/default.rb
# Update package index
apt_update 'update'
# Install packages
package %w[curl wget git vim htop]
# Create directory
directory '/opt/app' do
owner 'app'
group 'app'
mode '0755'
end
# Manage user
user 'app' do
comment 'Application user'
home '/home/app'
shell '/bin/bash'
manage_home true
end
# Create config file
file '/etc/app/config.yml' do
content <<-EOU
app:
name: myapp
version: 1.0.0
EOU
owner 'root'
group 'root'
mode '0644'
action :create
end
Advanced recipe with templates and conditionals:
# my_app/recipes/nginx.rb
# Include attributes
include_recipe 'my_app::attributes'
# Install Nginx
package 'nginx'
# Create config from template
template '/etc/nginx/sites-available/default' do
source 'nginx.conf.erb'
owner 'root'
group 'root'
mode '0644'
variables(
server_name: node['app']['hostname'],
app_port: node['app']['port'],
upstream_servers: node['app']['servers']
)
notifies :restart, 'service[nginx]'
end
# Enable Nginx service
service 'nginx' do
action [:enable, :start]
end
# Conditional installation
if node['platform'] == 'ubuntu'
apt_package 'nginx-full'
elsif node['platform'] == 'centos'
yum_package 'nginx'
end
# Execute command
execute 'configure-app' do
command '/opt/app/setup.sh'
not_if { ::File.exist?('/opt/app/.configured') }
notifies :restart, 'service[myapp]'
end
# Ruby block for complex logic
ruby_block 'setup-database' do
block do
db_password = Chef::EncryptedDataBagItem.load('secrets', 'db')['password']
# Setup database with password
end
only_if { node['app']['enable_db'] }
end
Atributos y Bolsas de Datos
Manage configuration data separately from code.
Attributes:
# my_app/attributes/default.rb
default['app']['name'] = 'myapp'
default['app']['version'] = '1.0.0'
default['app']['port'] = 3000
override['app']['hostname'] = 'app.example.com' if node.chef_environment == 'production'
# Platform-specific
case node['platform']
when 'ubuntu'
default['app']['user'] = 'www-data'
when 'centos'
default['app']['user'] = 'apache'
end
# Array attributes
default['app']['packages'] = %w[
curl
wget
git
]
# Hash attributes
default['nginx']['sites'] = {
'default' => {
port: 80,
root: '/var/www/html'
}
}
Data bags for secrets:
# Create data bag
knife data bag create secrets
# Create encrypted item
knife data bag from file secrets <<'EOF'
{
"id": "db",
"password": "secure_password_here",
"username": "admin"
}
EOF
Use data bag in recipe:
# my_app/recipes/database.rb
# Load encrypted data bag
db_secrets = Chef::EncryptedDataBagItem.load('secrets', 'db')
# Use in resource
execute 'setup-database' do
command "mysql -u #{db_secrets['username']} -p#{db_secrets['password']}"
sensitive true # Don't log password
end
Pruebas con Test Kitchen
Test cookbooks before deploying to production.
Kitchen configuration:
# .kitchen.yml
---
driver:
name: vagrant
provisioner:
name: chef_zero
multiple_converge: 2
enforce_idempotence: true
deprecations_as_errors: true
verifier:
name: inspec
platforms:
- name: ubuntu-20.04
driver:
box: bento/ubuntu-20.04
cpus: 2
memory: 2048
- name: centos-8
driver:
box: bento/centos-8
suites:
- name: default
run_list:
- recipe[my_app::default]
verifier:
inspec_tests:
- test/integration/default
- name: nginx
run_list:
- recipe[my_app::default]
- recipe[my_app::nginx]
verifier:
inspec_tests:
- test/integration/nginx
Run Kitchen tests:
# Create test instances
kitchen create
# Converge (apply recipe)
kitchen converge
# Run verification (tests)
kitchen verify
# Test idempotence
kitchen converge again
# Destroy test instances
kitchen destroy
# All-in-one test
kitchen test
InSpec test example:
# test/integration/default/default_test.rb
describe package('curl') do
it { should be_installed }
end
describe service('nginx') do
it { should be_installed }
it { should be_enabled }
it { should be_running }
end
describe file('/opt/app') do
it { should exist }
its('owner') { should eq 'app' }
its('group') { should eq 'app' }
end
Integración de Chef Automate
Integrate with Chef Automate for compliance and visibility.
Chef Automate setup:
# Install Chef Automate
curl https://packages.chef.io/files/stable/automate/latest/chef-automate_linux_amd64.zip | unzip
sudo ./chef-automate deploy config.toml \
--product automate \
--product infra-server \
--accept-terms-and-log-to-file
# Access dashboard
https://automate.example.com
Configure Chef client for Automate:
# /etc/chef/client.rb
# Send data to Chef Automate
data_collector_url 'https://automate.example.com/data-collector/v0/'
data_collector_token 'AUTOMATE_TOKEN'
# Enable policyfile
use_policyfile = true
policy_name 'prod'
policy_group 'production'
Policyfile for versioning:
# Policyfile.rb
name 'prod'
description 'Production infrastructure'
default_source :chef_server, 'https://chef-server.example.com'
run_list 'recipe[base]', 'recipe[nginx]'
cookbook 'base', '~> 1.0'
cookbook 'nginx', '~> 5.0'
Conclusión
Chef provides powerful infrastructure automation through flexible recipes, reusable cookbooks, and strong testing capabilities. By mastering recipe development, attribute management, data bags for secrets, and Kitchen for testing, you can create reliable, maintainable infrastructure code. Integration with Chef Automate enables comprehensive compliance management and visibility across your infrastructure, making Chef ideal for enterprise-scale automation.


