Ansistranoでnode.jsのアプリケーションをインストールする

リモートマシンにデプロイするのに[Capistrano](http://capistranorb.com/)が鉄板ですが、インフラの構築にAnsibleを使っていると全部Ansibleでやれないものかと。

探してみると、やはりありました[Ansistrano](https://github.com/ansistrano/deploy)！

## おさらい capistranoとは

* 元々はRailsのデプロイツール
    * 今はRails非依存
    * Ruby,PHPなどのスクリプト言語のデプロイが得意
* だいたいこんな流れ
    1.  対象のサーバーにSSH接続し
    2. ファイルを配置して
        * gitから取得するパターンが多い
    3. 何らかのコマンドを流す

参考：http://qiita.com/mr-myself/items/2769676e6dcf43db41ef

## まずはインストール

[Ansible Galaxy](https://galaxy.ansible.com/intro)で公開されているのでansible-galaxy

```
$  sudo ansible-galaxy install carlosbuenosvinos.ansistrano-deploy
```

## どう使うか

```yaml:deploy.yml
- hosts: api
  name: "○○のAPIサーバー"
  vars:
    ansistrano_deploy_to: /opt/myqpp/api
    ansistrano_deploy_via: git
    ansistrano_git_repo: git@github.com:NewGyu/myapp.git
    ansistrano_git_branch: master
    ansistrano_shared_paths: ["log","config"]
    ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/roles/myapp/ansistrano/after_update_code.yml"
    ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/roles/myapp/ansistrano/after_symlink.yml"
  vars_files:
    - vars/all/main.yml
    - vars/{{ stage }}/main.yml
    - vars/{{ stage }}/secret.yml
  roles:
    - carlosbuenosvinos.ansistrano-deploy
```

#### Roleです

AnsistranoはRoleです（Ansistranoに限らずAnsible Galaxyでインストールするものはそうですが）。そのため基本的な使い方はrolesに```carlosbuenosvinos.ansistrano-deploy```を指定するだけです。

#### パラメータ

Roleに指図するためには規定の名前の変数に情報をセットします。
例えばAnsistranoのタスク定義の一部はこんな感じになっています。
https://github.com/ansistrano/deploy/blob/master/tasks/setup.yml#L6-L8

```yaml
- name: ANSISTRANO | Set releases path
  command: echo "{{ ansistrano_deploy_to }}/{{ ansistrano_version_dir }}"
  register: ansistrano_releases_path
```

上記の例はトップレベルYAMLのvarsに書いていますが、group_varsやvars_filesに指定したファイルなどに書いてもよいでしょう。
ただし、```ansistrano_deploy_via```については[Ansistranoの中でincludeを使ったトリックが使われている](https://github.com/ansistrano/deploy/blob/master/tasks/update-code.yml#L17)のでvars_filesではうまく行きませんでした。

パラメータについては[本家のドキュメント](https://github.com/ansistrano/deploy/blob/master/README.md#role-variables)を読むのが良いのですが、いくつかピックアップします。

|変数名|意味|例|
|-----|----|---|
|ansistrano_deploy_to|リモートサーバーのデプロイ先パス |/opt/myqpp/api|
|ansistrano_keep_releases|保持する世代数|3|
|ansistrano_shared_paths|sharedアイテム（後述)|["logs","config"]|
|ansistrano_deploy_via|ソースコードを取得する手段。git,rsync,s3ｎいずれか|git|
|ansistrano_git_repo|GitリポジトリのURL| git@github.com:USERNAME/REPO.git|
|ansistrano_git_branch|cloneするGitのブランチ| master |
|ansistrano_git_identity_key_path|Gitにssh接続するときの鍵ファイルのリモートサーバー上のパス|/opt/myapp/.ssh/id_rsa|

上記を理解するにはAnsistrano(Capistrano)でデプロイされた時のディレクトリ構成を理解する必要があります。

```
/opt/myqpp/api
 |-- current -> /opt/myapp/api/releases/20100512131539
 |-- releases
 |   |-- 20100512131539
 |   |   |-- package.json
 |   |   |-- app
 |   |   |   |-- index.js
 |   |   |-- logs -> /opt/myapp/api/shared/logs
 |   |   |-- config -> /opt/myapp/api/shared/config
 |   |-- 20100509150741
 |   |-- 20100509145325
 |
 |-- shared
 |   |--logs -> /var/log/myapp
 |   |--config -> /etc/myqpp
```

##### ansistrano_deploy_to

* 上記のツリー構造のベースとなるパスを指定します
* Ansistranoはこのディレクトリ配下に色々と展開します

##### ansistrano_keep_releases

* Ansistranoはreleasesディレクトリ以下にGitから取得したソース一式を格納します
* デプロイが実行された日時タイムスタンプがディレクトリ名になります
* 何回もデプロイを実行した場合に直近のn回分保持されます  
  nはansistrano_keep_releasesに指定した数

##### ansistrano_shared_paths

* sharedのディレクトリにはGitなどのバージョン管理に含まれないものを入れます
    * 例えばlogなど
* 配列で指定したものにシンボリックリンクが張られます
* 上記の例では/var/log/myapp ,/etc/myappへのシンボリックリンクがsharedに入っていますが、直接ディレクトリをおいても、ファイルをおいてもＯＫです

#### フックするポイント

https://github.com/ansistrano/deploy/blob/master/README.md#main-workflow

![](https://raw.githubusercontent.com/ansistrano/deploy/master/docs/ansistrano-flow.png)

この図の黄色がフック可能なポイントで、これらもパラメータ指定できます。

```yaml
 # Hooks: custom tasks if you need them
  ansistrano_before_setup_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-before-setup-tasks.yml"
  ansistrano_after_setup_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-after-setup-tasks.yml"
  ansistrano_before_update_code_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-before-update-code-tasks.yml"
  ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-after-update-code-tasks.yml"
  ansistrano_before_symlink_shared_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-before-symlink-shared-tasks.yml"
  ansistrano_after_symlink_shared_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-after-symlink-shared-tasks.yml"
  ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-before-symlink-tasks.yml"
  ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-after-symlink-tasks.yml"
  ansistrano_before_cleanup_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-before-cleanup-tasks.yml"
  ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}/<your-deployment-config>/my-after-cleanup-tasks.yml"
```

* いずれもフックで実行されるタスクを定義したymlファイルのパス（playbookのパス）を指定します
* ファイル名などに縛りはなくファイルパスなら何でもＯＫですが、 ```{{ playbook_dir }}/....``` としてplaybookのルートからの相対になるようにすべきでしょう

こちらの例ではnode.jsのアプリケーションなので、

* ソースコードが取得できたら ```npm install```
* symlinkを切り替えたらサービス再起動

としています。

```
ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/roles/myapp/ansistrano/after_update_code.yml"
```

```yaml:/roles/myapp/ansistrano/after_update_code.yml
- name: npm install/update
  shell: /bin/bash -lc "npm install"
  args:
    chdir: "{{ ansistrano_release_path.stdout }}"
```

```
ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/roles/myapp/ansistrano/after_symlink.yml"
```

```yaml:/roles/myapp/ansistrano/after_symlink.yml
---
- name: restart api
  service: name=myapp-api state=restarted
```


Ansibleはインフラのセットアップだけでなく、デプロイにも使えるよというお話でした。