Capistranoを使ってWebアプリケーション(PHP)をAWSにデプロイ

Capistrano を使ったWebアプリケーションデプロイについて自分なりのやり方備忘録

Capistrano は、複数のサーバに対しアプリケーションのデプロイを自動で行えるツールです。

今回は初めてデプロイを試してみるのに、AWS EC2でWebアプリケーション（今回はPHP）を配置するサーバを用意し、
その1台のサーバに対しGithub上のソースをCapistranoを使ってデプロイする、といったことを行いました。
行く行くは複数サーバ立ててJenkins と連携してデプロイできるようにしたい

## 事前準備

* デプロイするサーバについてEC2 インスタンス作成後、
Elastic IP(固定グローバルIPアドレス)を割り当てパブリックIPを固定化しておきました。

## デプロイサーバにRubyをインストール

rbenv, ruby-build を利用してRubyをインストールします

- [x] gitをインストール

```
// SSH接続
# ssh -i [pemファイル] ec2-user@ec2-xxxxxxx.ap-northeast-1.compute.amazonaws.com

// ec2-user でログイン中
$ sudo yum install
$ sudo pip install awscli

$ sudo yum -y install git
$ git --version
git version 2.7.4
```
- [x] Rubyのインストールに必要なパッケージをインストール

```bash
$ sudo yum -y install gcc-c++ 
glibc-headers openssl-devel readline 
libyaml-devel readline-devel zlib zlib-devel libffi-devel libxml2 libxslt libxml2-devel libxslt-devel sqlite-devel
```

- [x] rbenvインストール

```bash
$ sudo git clone https://github.com/sstephenson/rbenv.git /opt/rbenv
$ sudo su -
// パスを通す
$ echo 'export RBENV_ROOT="/opt/rbenv"' >> /etc/profile.d/rbenv.sh
$ echo 'export PATH="${RBENV_ROOT}/bin:${PATH}"' >> /etc/profile.d/rbenv.sh
$ echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh
```

- [x] ruby-build インストール

```bash
$ git clone https://github.com/sstephenson/ruby-build.git /opt/rbenv/plugins/ruby-build
```

- [x] rbenvユーザの作成、インストールしたrbenvのパーミッションを変更する

```
$ sudo adduser rbenv
$ sudo passwd rbenv

# chown -R rbenv:rbenv /opt/rbenv/
# chmod -R 775 /opt/rbenv
# ls -la /opt/
total 16
drwxr-xr-x  4 rbenv rbenv 4096 Sep 22 12:22 .
dr-xr-xr-x 25 rbenv rbenv 4096 Sep 11 10:33 ..
drwxr-xr-x  5 rbenv rbenv 4096 Jun 21 20:10 aws
drwxrwxr-x 10 rbenv rbenv 4096 Sep 22 12:29 rbenv
```

- [x] Rubyをインストールし、バージョンを確認

```bash
$ rbenv install -v 2.1.3
$ rbenv rehash
$ rbenv global 2.1.3
$ ruby -v
ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-linux]
```

## デプロイするユーザの作成

capからデプロイを実行するユーザを作成しておきます

```bash
$ sudo adduser hikari
$ sudo passwd hikari
// 管理者権限も与えておく
$ sudo visudo
// 作成したユーザに切り替えます
$ sudo su - hikari
```

githubのリポジトリにSSH接続できるよう、公開鍵を配置しパーミッションの設定を行います

```bash
$ mkdir .ssh
$ echo "ssh-rsa AAAAB3N…(公開鍵)” >> .ssh/authorized_keys
$ chmod 700 .ssh
$ chmod 600 .ssh/authorized_keys 
```

## Capistranoをインストール

cap install すると以下のようにインストールされます

```bash
$ cap install
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified

     ___   _   ___ ___ ___ _____ ___    _   _  _  ___
    / __| /_\ | _ \_ _/ __|_   _| _ \  /_\ | \| |/ _ \
   | (__ / _ \|  _/| |\__ \ | | |   / / _ \| .` | (_) |
    \___/_/ \_\_| |___|___/ |_| |_|_\/_/ \_\_|\_|\___/

    Learn about our web-based collaboration and
    automation platform for Capistrano: hrw.io/auto-cap


We built Harrow.io, a platform which adds automation,
collaboration and logging features to any command line
tool. Harrow is optimized to work with Capistrano,
but it works with absolutely anything!

Harrow covers everything including testing, deployment
and maintenance for your project. Completely free for
small projects and there's a 14 day trial including
support to get you up and running.

                                -– The Capistrano Team
...

$ cap -V
Capistrano Version: 3.6.1 (Rake Version: 11.3.0)
```

```bash
$ ls -la
total 64
drwx------  8 hikari hikari 4096 Sep 22 15:57 .
drwxr-xr-x  4 root   root   4096 Sep 22 14:42 ..
-rw-------  1 hikari hikari 1098 Sep 22 15:03 .bash_history
-rw-r--r--  1 hikari hikari   18 Feb 19  2016 .bash_logout
-rw-r--r--  1 hikari hikari  370 Sep 22 15:45 .bash_profile
-rw-r--r--  1 hikari hikari  124 Feb 19  2016 .bashrc
drwxrwxr-x  3 hikari hikari 4096 Sep 22 15:55 .bundle
-rw-r--r--  1 hikari hikari  837 Sep 22 15:57 Capfile 
drwxrwxr-x  3 hikari hikari 4096 Sep 22 15:57 config　
drwxrwxr-x  3 hikari hikari 4096 Sep 22 15:55 .gem
drwxrwxr-x  3 hikari hikari 4096 Sep 22 15:57 lib　
drwxrw----  3 hikari hikari 4096 Sep 22 14:47 .pki
drwxrwxr-x 12 hikari hikari 4096 Sep 22 14:56 .rbenv
-rw-------  1 hikari hikari 1394 Sep 22 15:42 .viminfo
```

Capistranoのインストールまで完了したら、デプロイするにあたり設定ファイルを修正します

## 各設定ファイルの編集

Capistranoをインストールした後、tree コマンドで見ると以下のようにファイルが構成されています

```bash
$ tree hikari/
hikari/
├── Capfile
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
├── Gemfile
├── Gemfile.lock
└── lib
    └── capistrano
        └── tasks

5 directories, 6 files

```

このconfig/deploy/には

- production.rb
- staging.rb

の2つのファイルが作成されています。
ここにはその名の通り、デプロイする環境別の設定を記述することで指定した環境に応じてデプロイをすることができます。

そして同じ階層の```deploy.rb``` には、それぞれの環境に依存しない設定を記述する必要があります。
まずは```deploy.rb```から、そのあと環境別の設定を見ていきます

### deploy.rb

```config/deploy.rb
lock '3.6.1'

# Git repository
set :scm,         "git"
## cloneするgithubnのリポジトリ
set :repo_url, "https://ave-hikari:[パスワード]@github.com/ave-hikari/fuel_deploy.git"
## 対象のリポジトリからどのブランチをデプロイするか
set :branch,      "master"

## デプロイするアプリケーション名
set :application, "test"
## デプロイを実行するユーザ
set :user, "hikari"
## デプロイ先のパス　　今回は/home/hikari/test にする
set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}"

## 共有フォルダに配置するファルダ・ファイルの設定
set :linked_dirs, fetch(:linked_dirs, []).push('fuel/app/logs', 'fuel/app/tmp')

# Server setting
## デプロイするサーバを指定
ec2 = "ec2-xxxxx.ap-northeast-1.compute.amazonaws.com"
role :web, ec2
role :app, ec2
role :db,  ec2, :primary => true
#
set :use_sudo, false

namespace :deploy do

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
    end
  end
  after :publishing, :restart

  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
      # Here we can do anything such as:
      #       # within release_path do
      #             #   execute :rake, 'cache:clear'
      #                   # end
     end
  end
end
```

**linked_dirs** には
デプロイ時に上書きを避けたいフォルダなど（ログ、キャッシュに関連するものなど）を指定することができます。
指定したファイルはデプロイ時、共有フォルダにシンボリックリンクが貼られます。

### production.rb

今回はとりあえず本番環境(production)にデプロイすることを想定として、環境別の設定ファイルはproductionだけいじってみます。

```config/deploy/production.rb
# server-based syntax
# ======================
# Defines a single server with a list of roles and multiple properties.
# You can define all roles on a single server, or split them:

# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
# server 'db.example.com', user: 'deploy', roles: %w{db}



# role-based syntax
# ==================

# Defines a role with one or multiple servers. The primary server in each
# group is considered to be the first unless any  hosts have the primary
# property set. Specify the username and a domain or IP for the server.
# Don't use `:all`, it's a meta role.

set :application, "production"
set :user, "hikari"
## デプロイ先のパス
set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}"

role :app, %w{hikari@ec2-xxxxx.ap-northeast-1.compute.amazonaws.com}
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
# role :db,  %w{deploy@example.com}

...

# Custom SSH Options
# ==================
# You may pass any option but keep in mind that net/ssh understands a
# limited set of options, consult the Net::SSH documentation.
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
#
# Global options
# --------------
 set :ssh_options, {
   ## サーバにSSH接続を行う際の設定
   user: 'hikari',
    keys: %w(/home/hikari/.ssh/id_rsa),
    forward_agent: true,
    auth_methods: %w(publickey)
  }
#
# The server-based syntax can be used to override options:
# ------------------------------------
# server 'example.com',
#   user: 'user_name',
#   roles: %w{web app},
#   ssh_options: {
#      ...
#   }
```

一部```deploy.rb```とデプロイ先のパスの指定項目が被っていますが、デプロイコマンドを実行した際には各環境別の設定ファイルに記載されているものが優先されます。

## デプロイ実行

```bash
$ cap production deploy
```

上記のコマンドを実行すると、実行ログからgitからcloneして新しく指定したデプロイ先のパスにソースが配置されていることがなんとなく分かると思います。
デプロイ先のパスを見てみると下記のようになっています。

```bash
$ cd /home/hikari/production/
$ ls -la
total 24
drwxrwxr-x  5 hikari hikari 4096 Nov 13 15:53 .
drwx------ 13 hikari hikari 4096 Nov 13 15:53 ..
lrwxrwxrwx  1 hikari hikari   47 Nov 13 15:53 current -> /home/hikari/production/releases/20161113155309
drwxrwxr-x  3 hikari hikari 4096 Nov 13 15:53 releases
drwxrwxr-x  7 hikari hikari 4096 Nov 13 15:53 repo
-rw-rw-r--  1 hikari hikari  105 Nov 13 15:53 revisions.log
drwxrwxr-x  3 hikari hikari 4096 Nov 13 15:53 shared
```

currentディレクトリには今回のデプロイで取得してきたgithubのソースが配置されています。
releaseディレクトリは以下のように

```bash
$ cd releases
$ ls -la
total 28
drwxrwxr-x 7 hikari hikari 4096 Nov 13 12:29 .
drwxrwxr-x 5 hikari hikari 4096 Nov 13 12:29 ..
drwxrwxr-x 3 hikari hikari 4096 Sep 25 15:58 20160925155835
drwxrwxr-x 3 hikari hikari 4096 Sep 25 16:02 20160925160210
drwxrwxr-x 3 hikari hikari 4096 Sep 25 16:13 20160925161323
drwxrwxr-x 3 hikari hikari 4096 Oct  4 16:52 20161004165245
drwxrwxr-x 3 hikari hikari 4096 Nov 13 15:53 20161113155309
```

デプロイ実行時に取得してきたソースが、実行時の日時でディレクトリ名を生成して配置されていきます。
これならデプロイ後にバグ等が発覚して前のバージョンに切り戻しを行いたい場合も、
currentディレクトリのシンボリックリンクの向き先を前の実行時のものに切り替える（今回で言うと 20161004165245）だけで済むので
すぐに切り戻しが行えるので楽ですね。

sharedディレクトリには先の```linked_dir```の項目で指定したディレクトリが配置されているのが分かります。

```bash
$ tree shared/
shared/
└── fuel
    └── app
        ├── logs
             ....
        └── tmp
             ....
```

また```revisions.log```にはgithubからソースを取得してきた際の、各ブランチの最終コミットハッシュ値が列挙されているので
無事最新のソースでデプロイが完了したかどうかはここで一目で分かるので便利です
(厳密には前バージョンとのdiffをちゃんと見る必要があると思いますが)

```bash
$ less revisions.log

Branch master (at ...) deployed as release 20160925141930 by hikari
Branch master (at ...) deployed as release 20160925144858 by hikari
...
Branch master (at 8df1bcd...) deployed as release 20161113155309 by hikari
```

## 終わりに

今更な説明も多くなりましたがCapistranoかなり有用なツールです。
RubyからCapのインストールまでやり方は様々あるので、自分など知見がない人にはなかなか一からの構築は大変と思われますが実践＆導入の価値ありです。