Sử dụng gem Capistrano deploy ứng dụng Ruby on Rails lên server AWS

Mở đầu

Xin chào! Ồ! Mình biết bạn đấy! Mấy tháng trước, mình cũng giống như bạn bây giờ, cũng giành hàng đống thời gian để deploy code lên server, dù có cố như thế nào cũng không tự động hóa được quá trình deploy. Điều đó thật không công bằng đúng không? Chứ còn gì nữa! Mình đã tìm kiếm rất nhiều cách và rồi sau đó, mình đã đọc được một bài viết giống như bài viết bạn đang xem bây giờ. Mới đầu mình nghĩ, cách này rồi cũng giống như các cách deploy khác. Nhưng, mình tự nhủ, mình không còn cách nào khác và rồi sau đó, mình đã đi đến một quyết định, và đó là quyết định sáng suốt nhất trong cuộc đời mình! Sau một ngày, mình đã tự động hóa được phần lớn công việc deploy code lên server. Sau hai ngày, mình đã có thể deploy code lên nhiều server cùng một lúc. Bằng cách nào ư? Mình đã thử deploy code sử dụng gem Capistrano. Hóa ra, để deploy code không tốn nhiều thời gian như mình nghĩ, chỉ cần gem Capistrano là đủ. Bạn chỉ cần vào terminal và gõ duy nhất 1 câu lệnh cap production deploy. Cùng tính thời gian nào! Vâng, chỉ sau 2 phút 17 giây, mình đã đưa được app của mình lên server. Nghe có vẻ khó tin, nhưng đó là sự thật. Trong bài viết này, chúng ta sẽ cùng deploy một ứng dụng thực tế lên server AWS nhé

Deploy là gì? Tại sao lại cần Capistrano

Deploy hiểu đơn giản là quá trình bạn đưa code lên server và config cho server để code của bạn có thể chạy được trên đó. Nghe qua có vẻ đơn giản tuy nhiên việc deploy bao gồm rất nhiều công đoạn như: cài đặt môi trường cho server, pull code từ github, cài đặt các gem cần thiết, tạo database, chạy rake task, config nginx. Các công đoạn này thường lặp đi lặp lại rất mất thời gian nếu thực hiện bằng tay mà nếu sai ở bước nào đó thì quá nhọ vì vậy Capistrano được sinh ra để tự động hóa các công đoạn trên. Ngoài ra, Capistrano còn cho phép chúng ta deploy code lên nhiều server cùng một lúc, rollback nếu deploy lôi, lưu trữ các bản deploy gần đây nhất, chạy custom task trong quá trình deploy. Việc deploy với gem Capistrano gồm 3 bước

  • Tạo môi trường trên server
  • Config cho Capistrano
  • Config Nginx

Trong bài viết này mình sử dụng project Ruby on Rails mình đã viết sẵn khi còn là intern. Các bạn có thể xem repo tại đây

Cách hoạt động của Capistrano

Capistrano hoạt động bằng cách chia việc deploy thành các Rake task. Để thực hiện 1 task, nó sẽ truy cập vào một hoặc một số server (mà chúng ta thiết lập) thông qua SSH connection để thực hiện các đoạn script trên server. Để hiểu rõ hơn chúng ta cùng đi sâu vào phân tích task deploy:publishing được định nghĩa trong code của Capistrano nhé

namespace :deploy do
  task :publishing do
    invoke "deploy:symlink:release"
  end
end

Đoạn code trên dùng để định nghĩa task deploy:publishing khi thực hiện task này nó sẽ gọi đến một task khác có tên là deploy:symlink:release

namespace :deploy do
  namespace :symlink do
    desc "Symlink release to current"
    task :release do
      on release_roles :all do
        tmp_current_path = release_path.parent.join(current_path.basename)
        execute :ln, "-s", release_path, tmp_current_path
        execute :mv, tmp_current_path, current_path.parent
      end
    end
  end
end

execute trong đoạn code tạo ra một SSH connection và thực thi lệnh ln -smv trên server

Capistrano định nghĩa sẵn cho chúng ta các task khi chúng ta thực hiện lệnh cap production deploy như sau:

deploy
  deploy:starting
    [before]
      deploy:ensure_stage
      deploy:set_shared_assets
    deploy:check
  deploy:started
  deploy:updating
    git:create_release
    deploy:symlink:shared
  deploy:updated
    [before]
      deploy:bundle
    [after]
      deploy:migrate
      deploy:compile_assets
      deploy:normalize_assets
  deploy:publishing
    deploy:symlink:release
  deploy:published
  deploy:finishing
    deploy:cleanup
  deploy:finished
    deploy:log_revision

Ngoài ra chúng ta có thể định nghĩa các custom task để thực hiện các công việc trong quá trình deploy thông qua after hookbefore hook hay đơn giản là ghi đè lại task do Capistrano định nghĩa. Điều này làm cho Capistrano là một công cụ deploy mạnh mẽ, dễ tùy biến và vô cùng mềm dẻo.

Tạo môi trường trên server

Tạo tài khoản AWS

Nếu các bạn chưa có tài khoản AWS thì có thể truy cập link để tạo một cái. Lưu ý đến bước này, các bạn cần có thẻ visa hoặc credit để hoàn tất đăng ký, Amazon sẽ trừ 1$ trong tài khoản và sẽ trả lại sau khi đăng ký thành công

Khi đăng ký thành công, AWS sẽ cho chúng ta sử dụng một số dịch vụ của nó miễn phí trong vòng 12 tháng (quá đã). Ở đây, mình đã tạo sẵn một tài khoản rồi nên chúng ta tiếp tục bước tiếp theo nhé

Tạo EC2 instance

Đăng nhập bằng tài khoản vừa đăng ký thôi, ở góc trên bên phải click vào button Sign In to the console

Chúng ta sẽ vào trang quản lý các dịch vụ của AWS, trong mục All services chọn EC2

Trong phần create instance, click vào Launch Instance

Ở trang này nó sẽ cho phép chúng ta chọn loại instance để deploy, có thể xem instance là các máy ảo đã cài hệ điều hành và một số phần mềm cần thiết như git, vim, openssh... để ta có thể thao tác với server, chú ý các loại instance có phần chú thích Free tier eligible có nghĩa là chúng ta có thể dùng nó free với tài khoản vừa mới đăng ký. Có rất nhiều loại OS chúng ta có thể chọn, tuy nhiên để dễ thao tác, ta chọn Ubuntu Server 18.04 LTS (HVM), SSD Volume Type

Tiếp tục click vào Review and launch

Nó sẽ cho chúng ta xem qua các thông số của server, cứ để mọi thứ ở mặc định, click vào Launch

Tiếp theo hệ thống yêu cầu chúng ta tạo một cái key pair hoặc upload public key, mục đích là để chúng ta có thể truy cập được vào server thông qua SSH, chọn Create new key pair, ô key pair name có thể nhập cái gì cũng được, mình sẽ đặt theo tên của project, sau đó nhấn vào Download key pair để tải private key về. Tránh để mất cái private key này, nếu mất sẽ không vào được server, người khác có private key này cũng có thể truy cập được vào server của chúng ta(lol). Sau đó click vào Launch instance và đợi nó mấy phút để start instance nhé.

Trong phần Description có 2 mục mà ta cần quan tâm đến đó là Public DNS (IPv4)IPv4 Public IP, chúng ta có thể dùng 2 cái này để truy cập vào trang web sau khi deploy xong. Ngoài ra cái IPv4 Public IP dùng để ta SSH vào và config cho Capistrano

SSH vào EC2 instance

Trước hết ta vào thư mục mà ta mới tải cái private key kia về sau đó cấp quyền cho nó. Bật terminal lên cd vào thư mục chứa key (Trong máy mình để ở thư mục Downloads) sau đó gõ lệnh chmod 400 demo_danghh.pem

Tiếp theo, ta SSH vào server thông qua cái private key kia, mặc định AWS sẽ tạo cho chúng ta một instance với user ubuntu và IP của server chính là cái IPv4 Public IP mình vừa đề cập bên trên.

ssh -i demo_danghh.pem [email protected]

Tạo user deploy và cấp quyền sudo

Khi làm việc chung với một nhóm, chúng ta cần share instance kia với những người khác để họ có thể deploy code lên server, ta không nên share private key vì rất dễ lộ key ra bên ngoài(lý do bảo mật) thay vào đó ta nên tạo một user, sau đó cấp quyền cho user đó, những người khác có thể truy cập thông qua public key, cùng bắt tay vào làm nào.

Tạo một user có tên deploy, tên này các bạn đặt là gì cũng được

sudo adduser deploy

Trong quá trình tạo user, nó sẽ yêu cầu ta nhập mật khẩu và các thông tin liên quan khác, mật khẩu thì bắt buộc còn các thông tin khác thì có thể để trống nhé, sau đó chọn Y để xác nhận việc tạo user mới

Khi deploy ta cần cài đặt một số package do vậy user deploy cần có quyền root, ta add user deploy vào group sudo như sau

sudo usermod -aG sudo deploy

Cẩn tắc vô áy náy, thử test lại bằng cách SSH vào server bằng user mới tạo

Bị dính lỗi Permission denied (public key) do chúng ta chưa cho phép local ssh vào server. Ở máy local ta tạo mới ssh key như sau

ssh-keygen -t rsa -b 4096 -C "[email protected]"

Cái email kia các bạn nhập gì cũng được

Sau đó lên server add public key vừa mới tạo vào file authorezied_keys của user deploy trên server. Ở máy local ta copy public key

cat ~/.ssh/id_rsa.pub | pbcopy

Sau đó ssh lại vào server, chuyển qua user deploy tạo file authorized_keys và paste public key vào

ssh -i demo_danghh.pem [email protected]
su deploy
mkdir ~/.ssh
cd ~/.ssh
touch authorized_keys
vim authorized_keys

Đến đây coi như chúng ta đã hoàn thành việc setup tài khoản ở server, từ giờ chúng ta sẽ làm việc với user deploy mà chúng ta đã tạo mà không thông qua private key kia nữa

Cài đặt các package cần thiết

Tiếp theo mình cần cài đặt một số package để web app của chúng ta có thể chạy được trên đó, ở đây mình deploy ứng dụng Ruby on Rails nên sẽ cần cài đặt một số thứ như RVM, ruby, rails, bundler, nginx, mysql server, nodejs, yarn và một số thư viện liên qua khác

Cài đặt RVM, Ruby và Rails

sudo apt install gnupg2
gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable --rails

Cài đặt Nodejs

sudo apt-get install curl software-properties-common
curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -
sudo apt-get install nodejs -y

Cài đặt yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update
sudo apt install yarn

Cài đặt các thư viện cần thiết khác

sudo apt install build-essential libreadline-dev libxml2-dev libxslt1-dev libcurl4-openssl-dev libmysqlclient-dev

Cài đặt mysql và tạo database

sudo apt install -y mysql-server-5.7

Sau đó chúng ta cần config cho mysql

sudo mysql_secure_installation

Validate Password Plugin? n

Change the password for root? n

Remove anonymous users? y

Disallow root login remotely? y

Remove test database and access to it? y

Reload privilege tables now? y

Cuối cùng tạo database

mysql -uroot -p
CREATE DATABASE demo_danghh CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Nếu bạn chạy lệnh mysql -uroot -p sau đó nhập mật khẩu mà gặp lỗi Access denied for user 'root'@'localhost' thì ta xử lý như sau

Trước hết ta cần dừng tiến trình mysql hiện đang chạy và truy cập mysql thông qua skip-grant-tables

sudo service mysql stop
sudo mkdir -p /var/run/mysqld
sudo chown mysql:mysql /var/run/mysqld
sudo /usr/sbin/mysqld --skip-grant-tables --skip-networking &
mysql -u root

Tiếp đến ta tiến hành reset password cho user root của mysql

USE mysql;
UPDATE user SET authentication_string=PASSWORD("12345678") WHERE User='root';
UPDATE user SET plugin="mysql_native_password" WHERE User='root';
quit

Trong đó 12345678 chính là mật khẩu ta đặt lại cho user root. Cuối cùng restart lại mysql

sudo pkill mysqld
sudo service mysql start

Đến đây ta đã xong bước đầu tiên cài đặt môi trường server để app của chúng ta có thể hoạt động được, tiếp theo ta sẽ cùng config cho Capistrano

Config cho Capistrano

Cài đặt các gem cần thiết

Trong Gemfile thêm vào group production những gem sau

gem "capistrano"
gem "capistrano3-puma"
gem "capistrano-rails", require: false
gem 'capistrano-passenger'
gem "capistrano-yarn"
gem "capistrano-bundler", require: false
gem "capistrano-rvm"

Sau đó chạy bundle install

Cài đặt Capistrano

Để cài đặt Capistrano, ta chạy lệnh cap install. Sau khi chạy nó sẽ sinh cho chúng ta các file cấu hình

  • config/deploy.rb: Cấu hình chung

  • config/deploy/production.rb: Cấu hình cho môi trường product

  • config/deploy/staging.rb: Cấu hình cho môi trường staging

  • Capfile: Dùng để import các module cần cho deploy

  • lib/capistrano/tasks: Đây là thư mục nơi sẽ định nghĩa các custom task chạy trong quá trình deploy

Capfile

Đây là nơi import các module, mỗi module được thêm vào sẽ bao gồm nhiều task, các task này sẽ được chạy tự động trong quá trính deploy. Ví dụ khi require "capistrano/passenger" thì task deploy:restart sẽ được thêm vào quá trình deploy sau khi deploy:publishing hoàn tất. Ta thêm các module vào Capfile như sau

require "capistrano/setup"
require "capistrano/deploy"

require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

require "capistrano/rvm"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require "capistrano/passenger"
require 'capistrano/yarn'
require 'capistrano/puma'
install_plugin Capistrano::Puma
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

Kết hợp thứ tự chạy capitrano và Capfile ta có thứ tự các task chạy như sau:

deploy
  deploy:starting
    [before]
      deploy:ensure_stage
      deploy:set_shared_assets
    deploy:check
  deploy:started
  deploy:updating
    git:create_release
    deploy:symlink:shared
  deploy:updated
    [before]
      deploy:bundle
      yarn:install
    [after]
      deploy:migrate
      deploy:compile_assets
      deploy:normalize_assets
  deploy:publishing
    deploy:symlink:release
  deploy:published
    deploy:restart
  deploy:finishing
    deploy:cleanup
  deploy:finished
    deploy:log_revision

config/deploy.rb

Đây là file config chung cho tất cả các môi trường, file này sẽ chứa các config về tên app, đường dẫn đến repo github, đường dẫn đến app trên server, config cho puma.... Ta config lại file này như sau

lock "~> 3.11.1"

set :application, "demo_danghh"
set :repo_url, "[email protected]:hoanghaidangdev/demo_danghh.git"
set :ssh_options, { :forward_agent => true }

set :pty, true
set :linked_files, %w(config/database.yml config/application.yml)
set :linked_dirs, %w(log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads)
set :keep_releases, 5
set :rvm_type, :user
set :passenger_restart_with_touch, true

set :puma_rackup, -> {File.join(current_path, "config.ru")}
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/puma.sock"}
set :puma_conf, -> {"#{shared_path}/puma.rb"}
set :puma_access_log, -> {"#{shared_path}/log/puma_access.log"}
set :puma_error_log, -> {"#{shared_path}/log/puma_error.log"}
set :puma_role, :app
set :puma_env, fetch(:rack_env, fetch(:rails_env, "production"))
set :puma_threads, [0, 8]
set :puma_workers, 0
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :puma_preload_app, false

Có ba điểm cần lưu ý trong file trên là

set :application, "demo_danghh" chính là tên app

set :repo_url, "[email protected]:hoanghaidangdev/demo_danghh.git" là đường dẫn đến repo github

set :linked_files, %w(config/database.yml config/application.yml) là các file sẽ được ta tạo trên server để Capistrano có thể kết nối được với mysql, file application.yml chứa secret_key_base cho ứng dụng Rails

Tiếp đến ta sẽ config cho từng môi trường, mặc định Capistrano sẽ sinh ra hai file là config/deploy/production.rbconfig/deploy/staging.rb để config cho môi trường production và staging ở đây ta chỉ config cho file config/deploy/production.rb, môi trường staging làm tương tự.

config/deploy/production.rb

File này chứa các config cho môi trường production, gồm các config liên quan đến branch dể deploy, ip và user_name của server. Ta cấu hình cho môi trường này như sau

set :stage, :production
set :rails_env, :production
set :branch, "master"
set :deploy_to, "/home/deploy/deploy/demo_danghh"
server "18.141.8.95", user: "deploy", roles: %w{app db web}

set :deploy_to, "/home/deploy/deploy/demo_danghh chính là đường dẫn đến thư mực deploy trên server

server "18.141.8.95" chính là ip của server

user: "deploy" chính là user trên server thực hiện deploy.

Xong phần config cho Capistrano

Config cho Nginx và tạo thư mục deploy trên server

Tạo cấu trúc thư mục deploy

mkdir -p deploy/demo_danghh/shared/config && cd $_
touch application.yml database.yml

mkdir

Trong file database.yml ta config như sau:

production:
  adapter: mysql2
  encoding: utf8mb4
  pool: "5"
  database: demo_danghh
  username: root
  password: "12345678"
  socket: /var/run/mysqld/mysqld.sock

Trong file application.yml ta config như sau:

SECRET_KEY_BASE: "SECRET_KEY_BASE"

Cái secret key base ở trên ta có thể tạo ra bằng lệnh rake secret. Copy cái key đó bỏ vào file application.yml như trên

Config Nginx

Đầu tiên ta cài đặt Nginx: sudo apt install nginx

Sửa lại file config nginx cd /etc/nginx/sites_enabled/default

upstream app {
  server unix:/home/deploy/deploy/demo_danghh/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
  listen 80 default_server;
  listen [::]:80 default_server;
  index index.html index.htm index.nginx-debian.html;
  server_name _;
  root /home/deploy/deploy/demo_danghh/current/public;
  try_files $uri/index.html $uri @app;
  location / {
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_redirect off;
    proxy_set_header Connection '';
    proxy_pass http://app;
    proxy_read_timeout 150;
  }
  location ~* ^/assets/ {
        expires 1y;
        add_header Cache-Control public;
        add_header Last-Modified "";
        add_header ETag "";
        break;
    }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
      root   html;
  }
}

Trong file trên ta chú ý đến 3 dòng:

server unix:/home/deploy/deploy/demo_danghh/shared/tmp/sockets/puma.sock fail_timeout=0; cái là đường dẫn đến file puma.sock

listen 80 default_server; cho nginx đón các request đến thông qua cổng 80, lát nữa là sẽ mở công 80 trên instance để bên ngoài có thể truy cập vào được.

root /home/deploy/deploy/demo_danghh/current/public; thư mục public, các file sau khi precomplie đều nằm ở thư mục này

Restart lại Nginx sudo service nginx restart

Sau đó ta lên trang quản lý instance của AWS để mở port 80

Trong phần network and security ta chọn mục Security Groups

Chọn cái security group của chúng ta, sau đó kích chuột phải vào, chọn Edit inbound rules

Tiếp tục click vào button Add rule, click vào cái dropdown trong phần type sau đó chọn HTTP cuối cùng save lại

Bây giờ ta quay lại máy local và chạy lệnh cap production deploy

Bây giờ ta thử lên server xem kết quả như thế nào nhé

Pơ phệch! Vậy là chúng ta đã deploy thành công một ứng dụng Rails lên server AWS rồi đó

Cấu trúc thư mục deploy trên server

├── current -> /home/deploy/deploy/demo_danghh/releases/20190916200944/
├── releases
│   ├── 20190916192621
│   ├── 20190916194120
│   ├── 20190916200119
│   ├── 20190916200522
│   └── 20190916200944
├── repo
│   └── git data file
├── revisions.log
└── shared
    └── linked_files and linked_dirs

Thư mục releases giữ tất cả các phiên bản deploy thành công folder được đặt tên theo định dạng [year][month][day][timestamp]

Thư mục current đây là một symlink trỏ tới bản release cuối cùng thành công, nếu deploy thất bại thì thư mục current sẽ giữ bản release lần gần nhất thành công trước đó

Thư mục repo giữ một bản copy của git repo, giúp các lần sau pull code từ Github về nhanh hơn

File revisions.log chứa log dùng để debug khi deploy fail hoặc xem log các lần deploy trước đó

Thư mục shared chứa các linked_files và linked_dirs. Trong một Rails app bình thường, các file chứa các thông tin nhạy cảm như application.yml, database.yml, secret.yml sẽ không được đưa lên Github (lý do bảo mật) nên khi Capistrano pull code từ Github về sẽ không có các file này. Chẳng lẽ mỗi lần deploy ta lại lên server set lại các biến môi trường? Không hề, các file nằm trong thư mục share này sẽ được dùng chung cho tất cả các lần deploy và sẽ được link đến các file tương ứng trong quá trình deploy

set :linked_files, %w(config/database.yml config/application.yml)
set :linked_dirs, %w(log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads)

Một vài thứ hay ho khác có thể làm với Capistrano

Deploy lên nhiều server cùng một lúc

Trong file config/deploy/production.rb ta có thể config để deploy lên nhiều server cùng lúc như sau

server "32.68.168.32", user: "deploy", roles: %w{app db web}
server "34.70.227.168", user: "dummy", roles: %w{app web}
server "3.72.123.12", user: "database", roles: :db

Role filtering

Ví dụ chúng ta có 2 server dùng cho 2 mục đích: Server thứ nhất là webserver chỉ chạy Ruby on Rails app, server thứ hai là Database server chỉ chạy mysql. Vì vậy chúng ta không thể ép cả hai server chạy các task như nhau được vì sẽ lỗi trong quá trình deploy. Thay vào đó, ta sẽ chỉ định server chạy những task nhất định nếu có role tương ứng. Ta có thể hình dung việc config hai server đó như sau

server "34.70.227.168", user: "ruby", roles: %w{app web}
server "3.72.123.12", user: "database", roles: :db

Ngoài ra các task do lập trình viên định nghĩa cũng có thể chỉ định role, ví dụ

task :say_hello, role: :web do
  puts "Hello, world"
end

Hơn nữa, ta có thể thêm các role tùy ý để chỉ định chạy trên các server, ví dụ

task :show_name, role: :super_server do
  puts "Foo bar"
end

Trong file config/deploy/production.rb

server "32.68.168.32", user: "deploy", roles: %w{app db web}
server "34.70.227.168", user: "dummy", roles: %w{app db web}
server "3.72.123.12", user: "database", roles: %w{db super_server}

Nếu config như trên thì chỉ có server thứ ba mới có quyền chạy task show_name

Chạy custom task trong khi deploy

Trong phần hướng dẫn bên trên, mình phải tạo database trước khi tiến hành deploy, vậy có cách nào tự động tạo database nếu như database chưa có trong khi deploy không? Có nhé, để mị nói cho mà nghe

Trong thư mục lib/capistrano/tasks tạo file create_database.rake, nội dung file này để định nghĩa rake task mà chúng ta sẽ chạy khi deploy

namespace :deploy do
  desc "Create database"
  task :create_database do
    ask :db_root_password, ''
    ask :db_name, ''
    ask :db_pass, ''
    on roles(:web) do
      execute "mysql --user=root --password=#{fetch(:db_root_password)} -e \"CREATE DATABASE IF NOT EXISTS #{fetch(:db_name)}\""
    end
  end
end

Để trong quá trình deploy Capistrano có thể gọi được task trên ta cần dùng before hook. Trong file config/deploy ta thêm dòng sau

before "deploy:migrate", "deploy:create_database"

Bây giờ trước khi chạy migrate nó sẽ chạy task tạo database nếu chưa có

Ta cũng có thể áp dụng cách trên để tạo thêm các task khác như: tạo sẵn thư mục deploy, cài đặt các package cần thiết trước khi tiến hành deploy...

Tổng kết

Trên đây mình đã giới thiệu cho các bạn cách thức hoạt động của Capistrano cũng như demo cách deploy một ứng dụng Ruby on Rails lên server AWS, bài viết dựa theo những kiến thức mình tổng hợp được nên không tránh khỏi những thiếu sót, rất mong nhận được những ý kiến đóng góp để mình có thể tiến bộ hơn trong tương lai. Nếu thấy bài viết bổ ích, đừng quên upvote cho mình nhé.

Tài liệu tham khảo