I have configured chef environment and i am able to deploy my application using capistrano . Now i want to my chef to handle capistrano to deploy my apps . How can it be done ?
Capistrano is a framework written in Ruby that provides automated deploy scripts. It connects to your web server via SSH and executes a bunch of tasks that will make your app ready to shine.
Navigate to your application's root directory in Terminal and run the following command: capify . This command creates a special file called Capfile in your project, and adds a template deployment recipe at config/deploy.
Capistrano is a framework written in Ruby language that provides automated deployment. Similar to Rake tasks in Ruby language, it runs on the remote servers via SSH. It can be configured on the local or remote server.
I do the opposite, ie. deploy my Chef recipes via Capistrano. I recommend it.
#config/deploy.rb
...
before 'bundle:install', "provision:default", "deploy:config_db_yml_symlink"
...
This will execute the chef config for a server after before bundle install, which is important because a lot of gems rely on packages being installed to the OS.
#config/deploy/provision.rb
Capistrano::Configuration.instance(:must_exist).load do
namespace :provision do
task :default do
provision.setup
provision.web
provision.db
provision.db_slave
end
task :setup, once: true do
provision.get_environment_variables
provision.update_cookbooks
end
task :db, :roles => :db do
next if find_servers_for_task(current_task).empty?
if rails_env == 'staging'
run %{cd #{release_path}/provision; sudo chef-solo -c solo.rb -j db.json -l debug}
else
run %{cd #{release_path}/provision; sudo chef-solo -c solo.rb -j db_master.json -l debug}
end
end
task :db_slave, :roles => :db_slave do
next if find_servers_for_task(current_task).empty?
run %{cd #{release_path}/provision; sudo chef-solo -c solo.rb -j db_slave.json -l debug}
end
task :web, :roles => :web do
next if find_servers_for_task(current_task).empty?
run %{cd #{release_path}/provision; sudo chef-solo -c solo.rb -j web.json -l debug}
end
task :get_environment_variables do
run "if [ -d ~/.config ]; then " +
"cd ~/.config && git fetch && git reset origin/master --hard; " +
"else git clone [email protected]:mycompany/config.git .config; fi"
run "sudo cp ~/.config/secureshare/#{rails_env}/environment /etc/environment"
end
task :update_cookbooks do
run "if [ -d /u/chef ]; then " +
"cd /u/chef && git fetch && git reset origin/master --hard; " +
"else git clone [email protected]:mycompany/chef.git /u/chef; fi"
end
end
namespace :deploy do
task :setup, :except => { :no_release => true } do
dirs = [deploy_to, releases_path, shared_path]
dirs += shared_children.map { |d| File.join(shared_path, d.split('/').last) }
dirs += [File.join(shared_path, 'sockets')]
run "#{try_sudo} mkdir -p #{dirs.join(' ')}"
run "#{try_sudo} chmod g+w #{dirs.join(' ')}" if fetch(:group_writable, true)
run "#{try_sudo} chown -R ubuntu:ubuntu #{dirs.join(' ')}" if fetch(:group_writable, true)
end
task :config_db_yml_symlink do
run "ln -s #{shared_path}/database.yml #{release_path}/config/database.yml"
end
end
end
I have a folder in my project named provision, to handle the definition of chef roles, though the recipes are in a different repository.
#provision/solo.rb
root = File.absolute_path(File.dirname(__FILE__))
cookbook_path '/u/chef'
role_path root + "/roles"
log_level :debug
log_location STDOUT
Nodes are defined in the project
#provision/db_slave.json
{
"run_list": ["role[db_slave]"]
}
And roles
#provision/roles/db_slave.rb
name "db_slave"
description 'A postgresql slave.'
run_list(["recipe[base]", "recipe[postgresql::slave]", "recipe[rails]","recipe[papertrail]", "recipe[fail2ban]"])
override_attributes(
'kernel' => {
'shmmax' => ENV['KERNEL_SHMMAX'],
'shmall' => ENV['KERNEL_SHMALL'],
'msgmax' => ENV['KERNEL_MSGMAX'],
'msgmnb' => ENV['KERNEL_MSGMNB']
},
'postgresql' => {
'user' => ENV['PG_USER'],
'password' => ENV['PG_PASSWORD'],
'database' => ENV['PG_DATABASE'],
'master_host' => ENV['PG_HOST']
},
'app_dir' => ENV['APP_DIR'],
'papertrail' => {
'port' => ENV['PAPERTRAIL_PORT'],
'log_files' => [
"#{ENV['APP_DIR']}/shared/log/*.log",
"/var/log/*.log",
"/var/log/syslog",
"/var/log/upstart/*.log",
"/var/log/postgresql/*.log"
]
},
'new_relic' => {
'key' => ENV['NEW_RELIC_LICENSE_KEY']
})
All without keeping any sensitive information within the app. I also use capistrano-ec2group in order to map servers to roles using EC2 security groups.
group :myapp_web, :web
group :myapp_web, :app
group :myapp_db, :db, :primary=>true
group :myapp_db_slave, :db_slave
So basically you keep your chef recipes in one repo, your environment variables in another repo, and your app in another repo - and use Capistrano to both provision servers and deploy your app.
You could also keep your chef recipes in your application repo, but that inhibits reuse between project. The key is to put everything that changes into environment variables and store them separate to the app and the recipes.
When this is configured correctly, to add new servers you simply need to spin one up in EC2, apply the desired security group and then
cap deploy
You could watch that Foodfightshow Episode about Application Deployment.
You can e.g. put the configuration files (with e.g. the database credentials) to the server with Chef, while pushing the source code with Capistrano.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With