Prepare a server for a Rails app with MySQL (percona) + NGINX (passenger)
Deploy rails app in new server with Capistrano v3
- server
- git repo with rails app (here i use ruby 2.1.1 and rails 4.1.2)
- knife solo (http://matschaffer.github.io/knife-solo/)
Install the latest Ubuntu server version on your server. (http://www.ubuntu.com/download/server)
Go to local rails app.
Create a /chef
directory and an Gemfile
inside
$ mkdir chef
$ cd chef
$ vi Gemfile
In Gemfile
add gems :
- chef
- knife-solo
- knife-solo_data_bag
- librarian-chef
# Gemfile
source "https://rubygems.org"
# chef
# https://github.com/opscode/chef
gem 'chef', '11.10.4'
# knife-solo
# http://matschaffer.github.io/knife-solo/
gem 'knife-solo', '0.4.2'
# knife-solo_data_bag
# http://thbishop.com/knife-solo_data_bag/
gem 'knife-solo_data_bag', '1.0.1'
# librarian-chef
# https://github.com/applicationsonline/librarian-chef
gem 'librarian-chef', '0.0.4'
So install gems
$ bundle install
Now we are ready for init knife solo.
Check if you are in /chef
directory and execute this commande :
$ knife solo init .
Knife created files and directories into chef directory :
chef
├── .chef
│ └── knife.rb
├── cookbooks
├── data_bags
├── nodes
├── roles
└── site-cookbooks
Let's cook ! We need 2 things :
- cookbooks
- nodes
Cookbook it's similar to a gem.
Find gem with https://rubygems.org/
Find cookbook with https://supermarket.getchef.com/
We will use a set of cookbook, open Cheffile
and copy this list :
site 'http://community.opscode.com/api/v1'
# build-essential
# https://github.com/opscode-cookbooks/build-essential
cookbook 'build-essential', '2.0.0'
# apt
# https://github.com/opscode-cookbooks/apt
cookbook 'apt', '2.3.8'
# ark
# https://github.com/bryanwb/chef-ark
cookbook 'ark', '0.7.2'
# hostname
# https://github.com/3ofcoins/chef-cookbook-hostname
cookbook 'hostname', '0.1.0'
# nginx
# https://github.com/opscode-cookbooks/nginx
cookbook 'nginx', '2.7.4'
# ssh_known_hosts
# https://github.com/opscode-cookbooks/ssh_known_hosts
cookbook 'ssh_known_hosts', '1.3.0'
# user
# https://github.com/fnichol/chef-user
cookbook 'user', '0.3.0'
# apt-periodic
# https://github.com/madwork/chef-apt-periodic
cookbook 'apt-periodic', '0.1.1'
# chruby-build
# https://github.com/madwork/chef-chruby-build
cookbook 'chruby-build', '0.2.1'
# fail2ban
# https://github.com/opscode-cookbooks/fail2ban
cookbook 'fail2ban', '2.1.2'
# database
# https://github.com/opscode-cookbooks/database
cookbook 'database', '2.1.6'
# certificate
# https://github.com/atomic-penguin/cookbook-certificate
cookbook 'certificate', '0.5.2'
# percona
# https://supermarket.getchef.com/cookbooks/percona
cookbook 'percona', '~> 0.15.5'
# rvm
# https://supermarket.getchef.com/cookbooks/rvm
cookbook 'rvm', '~> 0.9.2'
# Curl
# https://supermarket.getchef.com/cookbooks/curl
cookbook 'curl', '~> 2.0.0'
For install cookbook, execute :
$ librarian-chef install
Next create node.
Node it's use for call and config cookbook as you want.
Create a node file named myserver.com.json
into nodes
directory
{
"platform": "ubuntu",
"platform_version": "trusty",
"set_fqdn": "myserver.com",
"ark": {
"prefix_root": "/usr/local/src"
},
"rvm": {
"default_ruby": "ruby-2.1.1",
"rubies": ["ruby-2.1.1"],
"group_users": ["paco","root"],
"global_gems": [
{ "name": "passenger"},
{ "name": "rails", "version": "4.1.2" }
]
},
"users": ["paco"],
"run_list": [
"recipe[apt]",
"recipe[build-essential]",
"recipe[ark]",
"recipe[hostname]",
"recipe[fail2ban]",
"recipe[apt-periodic]",
"recipe[curl]",
"recipe[user::data_bag]",
"recipe[rvm::system]",
"recipe[percona::client]",
"recipe[percona::server]",
"recipe[database]",
"recipe[tool]"
]
}
Each cookbook has his own config. Show https://supermarket.getchef.com/ for more details.
We have to use data bag in conf node for percona and user cookbooks.
Data bag allow to générate encrypt data authentification.
First generate `SECRET_FILE:
openssl rand -base64 512 | tr -d '\r\n' > encrypted_data_bag_secret
$ knife solo data bag create passwords mysql --secret-file '/path/to/encrypted_data_bag_secret'
# knife solo data bag open window with id, add data, save and close file
{
"id": "mysql",
"user_1": "mdp",
"user_2": "mdp"
}
You can verified into data_bags
directory
mysql.json
file is crypted.
If you want edit data bag execute this line :
$ knife solo data bag edit passwords mysql --secret-file '/path/to/encrypted_data_bag_secret'
$ knife solo data bag create users paco
{
"id": "paco",
"comment": "user UNIX",
"groups": [
"adm",
"sudo"
],
"create_group": true,
"shell": "/bin/bash",
"ssh_keygen": true,
"ssh_keys": [
"ssh-rsa 1",
"ssh-rsa 2"
]
}
Prepare chef client on server
$ knife solo prepare [email protected]
Execute chef recipes
$ knife solo cook [email protected]
Add capistrano gem into Gemfile
of rails app
group :development do
# ...
# DEPLOY
gem 'capistrano', '~> 3.1.0'
gem 'capistrano-bundler', '~> 1.1.2'
gem 'capistrano-rails', '~> 1.1.1'
gem 'capistrano-rvm', github: "capistrano/rvm"
#
end
Install gem
$ bundle installl
Init capistrano files
$ cap install
For production environment we have to change 3 files :
- Capfile
- config/deploy.rb
- config/deploy/production.rb
# Capfile
# Load DSL and Setup Up Stages
require 'capistrano/setup'
# Includes default deployment tasks
require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails'
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
# config/deploy.rb
# config valid only for Capistrano 3.1
lock '3.1.0'
set :application, 'name_app'
set :repo_url, '[email protected]:GitName/git_repo.git'
set :tmp_dir, "/path/to/server"
set :scm, :git
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
set :keep_releases, 5
set :default_env, { rvm_bin_path: '/usr/local/rvm/bin' }
namespace :deploy do
# Create data base first deploy
namespace :db do
desc 'Runs rake db:create'
task :create => [:set_rails_env] do
on primary fetch(:migration_role) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:create RAILS_ENV=#{fetch(:rails_env)}"
end
end
end
end
end
# restart passenger
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
# Your restart mechanism here, for example:
execute :touch, release_path.join('tmp/restart.txt')
end
end
before 'deploy:migrate', 'deploy:db:create'
after :deploy, "deploy:migrate"
after :publishing, 'deploy:restart'
after :finishing, 'deploy:cleanup'
end
# config/deploy/production.rb
set :stage, :production
set :branch, "master"
set :deploy_to, '/path/to/server/www/directory'
server 'myserver.com', user: 'paco', roles: %w{web app db}
$ cap production deploy
- Set password when create UNIX user
- Set configuration percona with my.cnf