How to use capistrano to implement automatic deployment

# How to use capistrano to implement automatic deployment

## STEP-1 (Dev Machine)

Before you need to setup the Rails environment for development
- create your project
1. ```rvm use 2.3.1```
2. ```rvm gemset create demo```
3. ```rvm use 2.3.1@demo```
4. ```gem install bundler```
5. ```gem install rails -v 5.0.1```
6. ```sudo apt-get install libmysqlclient-dev```
6. ```rails new demo -d mysql```

- configure config/database.yml
```
default: &default
adapter: mysql2
encoding: utf-8
pool: 5
username: root
passwrod: your_password
socket: /va/run/mysqld.sock

development:
<<: *default
database: demo_development

test:
<<: *default
database: demo_test

production:
<<: *default
database: demo_production
username: root
password: <%= ENV['DEMO_DATABASE_PASSEORD'] %>
```
- create database for the project
```RAILS_ENV=development rails db:drop db:create db:migrate```

- use scaffold to create user
```rails g scaffold user```

- edit config/routes.rb to configure root_path
```root 'users#index' ```

- edit .gitignore, add db/schema.rb into it
```db/schema.rb```

- continue to migrate the database
```RAILS_ENV=development rails db:migrate```

- start your server
```rails s -b 0.0.0.0 -p 3000```

- push your code into your remote repository
```git init```
```git remote add origin git@bitbucket.org:Stavenvanderbilt/demo.git```
```git push -u origin master```


## STEP-2 (Target Machine, as production or staging machine [Server])

### Create deploy user for automatic deployment

- Create user
Prerequisites: switch root role to execute the following command
```
#useradd -d /home/deploy -s /bin/bash -m deploy
#passwd deploy
#your_password (such as: D..l0y#..17)
```

- Set sudo permission for deploy
```
sudo vim /etc/sudoers
deploy ALL=(ALL) ALL

```

### Configure remote login via ssh

- Install openssh-server
```sudo apt-get install openssh-server```

- Edit openssh configuration
```vim /etc/ssh/sshd_config```

```
#default port,the range from 1025 to 65536
Port 22

# Not Allow root user to login via ssh
PermitRootLogin no


#forbiden login using the password
PasswordAuthentication no
PermitEmptyPasswords no
PasswordAuthentication yes

# At the end of this configuration file, add more one line to permit
# which user can login via ssh
AllowUsers deploy
```

- restart openssh service
```sudo service sshd restart```


### Install Mysql Client and Server
```
$sudo apt-get install mysql--server-5.6
$sudo apt-get install mysql--client-5.6
$sudo service mysql restart
```

- Modify the password for root user via mysql command
```
mysql -uroot -p
typing your password

use mysql;
select user,host,password from user;
update user set password=password('your_new_password') where user='root';
```

- Configure allow to remote access
```
mysql -uroot -p
typing your password

grant all on *.* to root@'%' identified by 'password';
flush privileges;
```

- Must restart mysqld service
```sudo service mysql restart```

### Install Nginx service

- use the following command to install

```
sudo apt-get install nginx -y
```

- configure firewall

```
sudo iptalbes -L
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
#sudo iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
sudo iptables -L --line-numbers
sudo iptables -F
```

### Install rvm, ruby, gemset, bundler, rails

```
sudo apt-get curl
curl -L https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.3.1
rvm use 2.3.1 --default
rvm gemset create your_project_name
rvm use 2.3.1@your_project_name
gem sources list
gem sources -r https://rubygems.org/
gem sources -a https://gems.ruby-china.org/
bundle config mirror.https://rubygems.org https://gems.ruby-china.org
vim ~/.gemrc
gem: "--no-ri --no-rdoc"
echo "rvm_auto_reload_flag=2" > ~/.rvmrc
gem install bundler
sudo apt-get install libmysqlclient-dev
gem install mysql2 -v '0.4.5'
apt-get install build-essential
gem install therubyracer
gem install rails -v 5.0.1 (optional)
```

### Create directory for your project's deployment

- Create deployment directory and other directories
```
cd ~
su - deploy

#create directory for deployment directory and shared directory
mkdir -p ~/apps/your_project_name
mkdir -p ~/apps/your_project_name/shared/config

cd apps/your_project_name
rvm use 2.3.1
rvm gemset create your_project_name
rvm use 2.3.1@your_project_name

# after execute the `cap staging deploy` command
bundle install
RAILS_ENV=production rails db:create
RAILS_ENV=staging rails db:create
```
- configure environemnt variable
```
rake secret


vim .bash_profile
export SECRET_KEY_BASE=123454f5gdf1g541g3d1g3f1g3d1gdfg54dfgd12g1df5g4dfghfg43d1
export DEMO_DATABASE_PASSEORD=your_password_for_database
verify it whether it works
echo $SECRET_KEY_BASE

ENV["SECRET_KEY_BASE"]
ENV["DEMO_DATABASE_PASSEORD"]

```

#### Create shared/config/database.yml

```
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password: your_password
socket: /var/run/mysqld/mysqld.sock

development:
<<: *default
database: demo_development

test:
<<: *default
database: demo_test

staging:
<<: *default
database: demo_staging

production:
<<: *default
database: demo_production
username: root
password: <%= ENV['DEMO_DATABASE_PASSEORD'] %>
```

#### Create shared/config/puma.rb

```
#!/usr/bin/env puma

app = "your_project_name"
app_dir = File.expand_path("../../..", __FILE__)

puts "+++++++++++++++++++++++++++++++"
puts app_dir
puts "+++++++++++++++++++++++++++++++"

directory "#{app_dir}/current"
rackup "#{app_dir}/current/config.ru"
puts "==========================================================================="
rails_env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "staging"
environment rails_env
puts rails_env
puts "==========================================================================="

pidfile "#{app_dir}/shared/tmp/pids/puma.pid"
state_path "#{app_dir}/shared/tmp/pids/puma.state"
stdout_redirect "#{app_dir}/current/log/puma.access.log", "#{app_dir}/current/log/puma.error.log", true
threads 0,8
bind "unix://#{app_dir}/shared/tmp/sockets/#{app}-puma.sock"

preload_app!

on_restart do
puts 'Refreshing Gemfile'
ENV["BUNDLE_GEMFILE"] = "#{app_dir}/current/Gemfile"
end
```


#### Create shared/config/configuration.yml

```
# Official Website Version
version: 0.0.1
name: Demo
```

#### Create shared/config/secrets.yml

```
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
secret_key_base: d16965211db142d1597c55437f476529005d29143136b0f1720391b8f7469021a00120c64c48376557ef77fb437fd115f2a340eadc59a614b1fbacf6113fb555

test:
secret_key_base: 2f2b82cba7e0c98d9d7525f6aaf7f499c3cc74a10012687afc57b6ec9fd7c86c33b9e8e8469ca91de3bcb430536bcf23dc123b4ca1d0e76bcdc2ded6e7a46fef

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
# secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
secret_key_base: 2f2b82cba7e0c98d9d7525f6aaf7f499c3cc74a10012687afc57b6ec9fd7c86c33b9e8e8469ca91de3bcb430536bcf23dc123b4ca1d0e76bcdc2ded6e7a46fef

staging:
secret_key_base: e595fe0023b232ee4de1ec715cecaf68354ed8c7f24f03fb28967a048be0dcc5a5c61fad3170e5b4e04bdc62a2b22ea7ca259e7686bd97a71efe5bfb8b061a93

local_production:
# secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
secret_key_base: 2f2b82cba7e0c98d9d7525f6aaf7f499c3cc74a10012687afc57b6ec9fd7c86c33b9e8e8469ca91de3bcb430536bcf23dc123b4ca1d0e76bcdc2ded6e7a46fef
```


#### Create shared/config/nginx.conf

```
vim ~/apps/your_project_name/shared/config/nginx.conf


upstream puma {
server unix:///home/deploy/apps/your_project_name/shared/tmp/sockets/your_project_name-puma.sock fail_timeout=0;
}

server {
listen 80 deferred;
server_name 192.168.10.129; # According to your actual situation

root /home/deploy/apps/your_project_name/current/public;
access_log /home/deploy/apps/your_project_name/current/log/nginx.access.log;
error_log /home/deploy/apps/your_project_name/current/log/nginx.error.log info;


location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}

try_files $uri/index.html $uri @puma;

location @puma {
proxy_pass http://puma;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}

error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}

```

 


## STEP-3 (Dev Machine)

- upload your public key into your Target Machine (Server)
solve ssh into target server need to type your password
```ssh-copy-id -i deploy@your_target_server_ip```

- add capistrano gem into Gemfile
```
group :development do
# configuration for Capistrano
gem 'capistrano', require: false
gem 'capistrano-rvm', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano3-puma', require: false
end
```

- update your gems
```bundle install```

- use cap command to init capistrano
```bundle exec cap install```

- edit Capfile
```
require "capistrano/rvm"
require "capistrano/bundler"
require "capistrano/rails"
require "capistrano/puma"
install_plugin Capistrano::Puma # Default puma tasks
```

- edit deploy/staging.rb
```
# staging server machine
server '192.168.10.129', port: 22, user: "deploy", roles: %w{app db web}
set :user, "deploy"
set :stage, "staging"
set :ssh_options, { :forward_agent => true }
set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}"

set :puma_threads, [0, 4]
set :puma_workers, 1
set :puma_env, "#{fetch(:stage)}"
```

- edit deploy/production.rb
```
# production server machine
server '192.168.10.129', port: 22, user: "deploy", roles: %w{app db web}
set :user, "deploy"
set :stage, "production"
set :ssh_options, { :forward_agent => true }
set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}"

set :puma_threads, [0, 8]
set :puma_workers, 4
set :puma_env, "#{fetch(:stage)}"

```

- edit config/deploy.rb
```
lock "~> 3.10.0"

puts "=========================================================================="
puts "=============== deploy.rb ================"
puts "=========================================================================="

set :application, "demo"
set :repo_url, "git@bitbucket.org:StavenVanderbilt/demo.git"

set :stages, ["staging", "production"]
set :default_stage, "staging"

# Default value for :linked_files is []
#append :linked_files, 'config/database.yml', 'config/puma.rb', 'config/configuration.yml', 'config/secrets.yml'
append :linked_files, 'config/database.yml', 'config/puma.rb', 'config/configuration.yml', 'config/secrets.yml', 'config/nginx.conf'

# Default value for linked_dirs is []
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle'

# use rvm gemset rather than bundle path for working with launchctl
set :rvm_type, :user
set :rvm_ruby_version, "2.3.1@#{fetch(:application)}"
set :bundle_path, nil
set :bundle_binstubs, nil
set :bundle_flags, '--system'

## Defaults
set :format, :pretty
set :log_level, :debug
set :keep_releases, 5

set :pty, true
set :use_sudo, false
set :puma_user, fetch(:user)
set :puma_state, -> { "#{shared_path}/tmp/pids/puma.state" }
set :puma_pid, -> { "#{shared_path}/tmp/pids/puma.pid" }
set :puma_bind, -> { "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock" }
set :puma_conf, -> { "#{shared_path}/config/puma.rb" }
set :puma_access_log, -> { "#{release_path}/log/puma.access.log" }
set :puma_error_log, -> { "#{release_path}/log/puma.error.log" }
set :puma_role, :app
set :puma_init_active_record, false
set :puma_preload_app, true

def branch_name(default_branch)
branch = ENV.fetch('BRANCH', default_branch)
if branch == '.'
# current branch
`git rev-parse --abbrev-ref HEAD`.chomp
else
branch
end
end

set :branch, branch_name('master')

namespace :nginx do
desc 'setup config for nginx server ( how to use: cap stage_environment nginx:set_config )'
task :set_config do
on roles(:web) do
execute :sudo, "rm -f /etc/nginx/sites-enabled/*.conf"
#execute :sudo, "ln -s #{fetch(:deploy_to)}/current/config/nginx.conf /etc/nginx/sites-enabled/#{fetch(:application)}.conf"
execute :sudo, "ln -s #{fetch(:deploy_to)}/shared/config/nginx.conf /etc/nginx/sites-enabled/#{fetch(:application)}.conf"
execute :sudo, "/usr/sbin/nginx -s quit || true"
execute :sudo, "/usr/sbin/nginx"
end
end

desc "Reload nginx ( how to use: cap stage_environment nginx:reload )"
task :reload do
on roles(:web) do
execute :sudo, "service nginx reload"
end
end
end
```

- create config/nginx.conf (Optional)
```
upstream puma {
server unix:///home/deploy/apps/your_project_name/shared/tmp/sockets/your_project_name-puma.sock fail_timeout=0;
}

server {
listen 80 deferred;
server_name 192.168.10.129; #according to your actual situation

root /home/deploy/apps/your_project_name/current/public;
access_log /home/deploy/apps/your_project_name/current/log/nginx.access.log;
error_log /home/deploy/apps/your_project_name/current/log/nginx.error.log info;


location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}

try_files $uri/index.html $uri @puma;

location @puma {
proxy_pass http://puma;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}

error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}

```

- add all of file and push them onto repository

```git add .```
```git commit -m "complete to automatic deploy"```
```git push -u origin master```


- Try to deploy your project into your target machine (server)
```
# Leave out the step of inputing the password for id_rsa.pub
eval "$(ssh-agent -s)"
ssh-add

# At first time, need to run these two command to initalize the nginx
cap staging nginx:set_config
cap staging nginx:reload
```

- start to deploy via cap command
```cap staging deploy```

 


## STEP-4 (Target Machine [ Server ])

#### Target machine (install gems when fail to deploy at the first time)
If you fail to deploy at the first time, you need to go [the target machine] to install gems
```
cd ~/apps/your_project_name/release/XXXX
rvm use 2.3.1
rvm gemset create your_project_name
rvm use 2.3.1@your_project_name
gem install bundler
sudo apt-get install libmysqlclient-dev
apt-get install build-essential
bundle install
```

```
rake secret

vim .bash_profile
export SECRET_KEY_BASE=123454f5gdf1g541g3d1g3f1g3d1gdfg54dfgd12g1df5g4dfghfg43d1
export DEMO_DATABASE_PASSEORD=your_password_for_database
verify it whether it works
echo $SECRET_KEY_BASE

ENV["SECRET_KEY_BASE"]
ENV["DEMO_DATABASE_PASSEORD"]
```

- At first time, need to create the database for your project.
```
RAILS_ENV=staging rails db:create
RAILS_ENV=production rails db:create
```

posted @ 2017-11-03 00:12  StavenVanderbilt  阅读(284)  评论(0编辑  收藏  举报