CapistranoのdeployタスクをVagrantマシンに適用するまで

Capistrano3を使って書いたdeployタスクをいきなり本番に適用するのは怖いので、Vagrantの仮想マシンにdeployして様子を見たい。cap deploy実行までやる。

このドキュメントではプロジェクトリポジトリを仮想マシンに撒くところまで扱うので、capistrano-rbenv等を使ってrubyを併せてまく所等は割愛する。

# やりたいこと

* Mac Book ProからVagrantで立てた仮想マシンへcap deploy
* capのstaging名はdevelopmentとし、ユースケースとしては本番deploy前にvagrantへdeployして動くかカジュアルに確認する用途
* 仮想マシン側にsshログインしなくて済む手順にする。`vagrant up`した直後に`bundle exec cap development deploy`で通ればOK
* アプリとしてRails製のOSSでグラフツールの [yohoushi](http://github.com/yohoushi/yohoushi)をdeployする

なお、vagrantに対して`cap development deploy`が通るところまでやるが、rubyを入れるところや仮想マシン側でアプリを`bundle install`するところは省略する。

# git repository

capistranoタスク保存するgit repository用意する。今回のは[yohoushi-cap](https://github.com/niku4i/yohoushi-cap)においた。

# Vagrantfile

こうなる。後のcapistranoに記述するsshのポート情報を揃える必要があるのと、guest_portの設定以外にforwarded_portの設定を上書く必要があった。`vagrant up`で仮想マシンを起動しておく。

```ruby:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

$script = <<SCRIPT
yum -y install git
SCRIPT
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.define :deploydst do |c|
    c.vm.hostname = 'deploydst'
    # Override default nat port for ssh
    # c.f. https://github.com/mitchellh/vagrant/issues/3232
    c.vm.network :forwarded_port, guest: 22, host: 12222, id: "ssh"
    c.ssh.guest_port = 12222
    c.vm.provision "shell", inline: $script
    c.vm.provider :virtualbox do |provider, override|
      override.vm.box = "chef/centos-6.5"
    end
  end
end
```


# ssh-agent

deploy元(今回の場合はMac Book Pro)にてssh秘密鍵をssh-agentに予め登録しておく。こうしておくとvagrantの仮想マシン側に秘密鍵を置かずにgithub.comからpullできる。

```bash
$ ssh-add  ~/.ssh/id_rsa      # github.com登録済み公開鍵の対の秘密鍵
```

# Gemfile

Gemfileを作って`bundle install`する

```ruby:Gemfile
# A sample Gemfile
source "https://rubygems.org"

gem 'capistrano', '~> 3.3.0'
gem 'capistrano-safe-deploy-to', '~> 1.1.1'
```

[capistrano-safe-deploy-to](https://github.com/capistrano-plugins/capistrano-safe-deploy-to)については後述します。

# ひな形ファイルを生成する

cap installコマンドでひな形を作る

```bash
$ bundle exec cap install STAGES=development
```

今回はお試しなのでステージはdevelopmentのみでよいのでSTAGES=development指定で。

作成したファイルのうち、以下をとりあえず修正。ssh_optionsでvagrantの環境を指定してるところがポイント。

```ruby:config/deploy.rb
set :application, 'yohoushi'
set :repo_url, 'git@github.com:yohoushi/yohoushi.git'
set :deploy_to, '/var/app/yohoushi'
```

```ruby:config/deploy/development.rb
server 'localhost', user: 'vagrant', roles: %w{web app}, my_property: :my_value

set :ssh_options, {
  port: 12222,
  keys: %w(~/.vagrant.d/insecure_private_key),
  forward_agent: true,
  auth_methods: %w(publickey)
}
```


# deploy

おもむろに`cap development deploy`を実行すると以下のエラーのとおりmkdir -p に失敗する。

これは`:deploy_to`で指定した/var/app/yohoushiディレクトリが無いが、`deploy`タスクの中で最初に呼ばれるタスクで`mkdir -p #{deploy_to}/shared #{deploy_to/releases}`するからであるが、解決策があって後述してる。

```
 $ bundle exec cap development deploy
INFO [d02dfa4c] Running /usr/bin/env mkdir -p /tmp/yohoushi/ as vagrant@localhost
DEBUG [d02dfa4c] Command: /usr/bin/env mkdir -p /tmp/yohoushi/
DEBUG Uploading /tmp/yohoushi/git-ssh.sh 0.0%
INFO Uploading /tmp/yohoushi/git-ssh.sh 100.0%
INFO [c31495af] Running /usr/bin/env chmod +x /tmp/yohoushi/git-ssh.sh as vagrant@localhost
DEBUG [c31495af] Command: /usr/bin/env chmod +x /tmp/yohoushi/git-ssh.sh
INFO [cf2b4766] Running /usr/bin/env git ls-remote --heads git@github.com:yohoushi/yohoushi.git as vagrant@localhost
DEBUG [cf2b4766] Command: ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/yohoushi/git-ssh.sh /usr/bin/env git ls-remote --heads git@github.com:yohoushi/yohoushi.git )
DEBUG [cf2b4766]        Warning: Permanently added the RSA host key for IP address '192.30.252.130' to the list of known hosts.
DEBUG [cf2b4766]        e79da3d727a6e1122c9170fe69d48a1ca536662b        refs/heads/delete-tagged-graphs-fix-button
DEBUG [cf2b4766]        18e7dec95011f77a82e907e1e4b4541ca08cd92e        refs/heads/footer
DEBUG [cf2b4766]        2cf4f53873e0cb24790d3885151ed82dc3fc9b62        refs/heads/gh-pages
DEBUG [cf2b4766]        17e868a591875dccd0d21b1486d8aa6b42a5e7c4        refs/heads/master
INFO [9cd2ea40] Running /usr/bin/env mkdir -p /var/app/yohoushi/shared /var/app/yohoushi/releases as vagrant@localhost
DEBUG [9cd2ea40] Command: /usr/bin/env mkdir -p /var/app/yohoushi/shared /var/app/yohoushi/releases
DEBUG [9cd2ea40]        mkdir:
DEBUG [9cd2ea40]        cannot create directory `/var/app'
DEBUG [9cd2ea40]        : Permission denied
DEBUG [9cd2ea40]
DEBUG [9cd2ea40]        mkdir:
DEBUG [9cd2ea40]        cannot create directory `/var/app'
DEBUG [9cd2ea40]        : Permission denied
DEBUG [9cd2ea40]
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as vagrant@localhost: mkdir exit status: 1
mkdir stdout: Nothing written
mkdir stderr: mkdir: cannot create directory `/var/app': Permission denied
mkdir: cannot create directory `/var/app': Permission denied

SSHKit::Command::Failed: mkdir exit status: 1
mkdir stdout: Nothing written
mkdir stderr: mkdir: cannot create directory `/var/app': Permission denied
mkdir: cannot create directory `/var/app': Permission denied

Tasks: TOP => deploy:check:directories
(See full trace by running task with --trace)
The deploy has failed with an error: Exception while executing as vagrant@localhost: mkdir exit status: 1
mkdir stdout: Nothing written
mkdir stderr: mkdir: cannot create directory `/var/app': Permission denied
mkdir: cannot create directory `/var/app': Permission denied
```

deploy_toで指定したディレクトリ以下はリモート実行ユーザで書き込める状態にないと最初に失敗してしまう。ここでつまづく人が多いみたい。自前でmkdir -pしてchownしようかとおもったが [capistrano-safe-deploy-to](https://github.com/capistrano-plugins/capistrano-safe-deploy-to) を使うと最初にディレクトリを作ってくれるみたいなので以下のようにする。


```ruby:Gemfile
# A sample Gemfile
source "https://rubygems.org"

gem 'capistrano', '~> 3.3.0'
gem 'capistrano-safe-deploy-to', '~> 1.1.1'  # 追加

```

```ruby:Capfile
# Include default deployment tasks
require 'capistrano/deploy'

# https://github.com/capistrano-plugins/capistrano-safe-deploy-to
require 'capistrano/safe_deploy_to'          # 追加

(以下略)
...
```

これで`bundle exec cap development deploy`が通った。


というわけでうまくいった。

この後はVagrant側にrubyが無いのでhttps://github.com/capistrano/rbenv などを使っていく。