Ansible を使って EC2 に Railsサーバーを立ち上げる

## バージョン＆記載ソースリポジトリ

| version | 
|:---|
| ansible 1.9.2 |

ポイントとなるソースのみ載せています。全てのソースが見たい場合は、以下のリポジトリを参照してください。

* https://github.com/katsuhiko/ec2-playbooks

Ansible で構築したサーバーのアプリの各バージョン

| version |
|:---|
| rbenv 0.4.0-151-g83ac0fb |
| ruby 2.2.2p95 |
| bundler 1.10.5 |
| node.js 0.10.40 |
| npm 1.4.28 |
| nginx 1.6.2 |

## 概要

AWS EC2 Amazon Linux 環境に Rails アプリを nginx + unicorn で動かすための playbook を作成しました。

rbenv を利用した ruby、node.js、bundler、nginx をインストールします。
DB は RDS を利用する予定です。DataBase Server のインストールはしません。
unicorn は gem にてインストールするため playbook での記述は特にありません。

capistrano3 を利用した unicorn Rails アプリのデプロイについては、
[Capistrano3 を使って Rails4 + unicorn + nginx + rbenv にデプロイする](http://qiita.com/katsuhiko/items/90cb84033c305df2238b)
に記載しました。

## 注意点

EC2 インスタンスを作るときに注意点があります。
インスタンスタイプが t2.micro, t2.small では rbenv での ruby 2.2.2 のインストールが最後まで終わりませんでした。
エラーも発生せず、途中で止まっているような状態です。インストールのプロセスが消えていることは確認しましたが、詳細は確認していません。

t2.medium で問題なく ruby 2.2.2 がインストールできることを確認しました。

## rbenv, ruby のインストール

[Ansible と Vagrant を使って Rails 開発環境（Ubuntu + rbenv + MySQL5.6 + node.js）を構築する - rbenv, ruby のインストール](http://qiita.com/katsuhiko/items/56935225754a90d58314#rbenv-ruby-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)

以前、ubuntu にインストールする手順を書いていたので、この内容を Amazon Linux でも動くように修正しました。

```yaml:roles/ec2-rbenv/taks/main.yml
---
- name: Install dependencies for rbenv
  yum: name={{ item }} state=latest
  with_items:
    - git

- name: Install rbenv
  sudo: yes
  sudo_user: "{{ rbenv_user }}"
  git: repo=https://github.com/sstephenson/rbenv.git dest=~/.rbenv

- name: Add ~.rbenv/bin to PATH
  sudo: yes
  sudo_user: "{{ rbenv_user }}"
  lineinfile: >
    dest="~/.bash_profile"
    line="export PATH=$HOME/.rbenv/bin:$PATH"

- name: Eval rbenv init in ~/.bash_profile
  sudo: yes
  sudo_user: "{{ rbenv_user }}"
  lineinfile: >
    dest="~/.bash_profile"
    line='eval "$(rbenv init -)"'

- name: Install dependencies for ruby-build (see. https://github.com/sstephenson/ruby-build/wiki)
  yum: name={{ item }} state=latest
  with_items:
    - gcc
    - openssl-devel
    - libyaml-devel
    - libffi-devel
    - readline-devel
    - zlib-devel
    - gdbm-devel
    - ncurses-devel

- name: Install ruby-build as rbenv plugin
  sudo: yes
  sudo_user: "{{ rbenv_user }}"
  git: repo=https://github.com/sstephenson/ruby-build.git dest=~/.rbenv/plugins/ruby-build

- name: Check if version is installed ruby
  shell: "sudo -iu {{ rbenv_user }} rbenv versions | grep {{ rbenv_ruby_version }}"
  register: rbenv_check_install
  changed_when: False
  ignore_errors: yes

- name: Install ruby
  command: "sudo -iu {{ rbenv_user }} rbenv install {{ rbenv_ruby_version }}"
  when: rbenv_check_install|failed

- name: Check if version is the default ruby version
  shell: "sudo -iu {{ rbenv_user }} rbenv version | grep {{ rbenv_ruby_version }}"
  register: rbenv_check_default
  changed_when: False
  ignore_errors: yes

- name: Set default ruby version
  command: "sudo -iu {{ rbenv_user }} rbenv global {{ rbenv_ruby_version }}"
  when: rbenv_check_default|failed
```

## node.js のインストール

therubyracer でも良いのですが、node.js をインストールします。

[Ansible と Vagrant を使って Rails 開発環境（Ubuntu + rbenv + MySQL5.6 + node.js）を構築する - node.js のインストール](http://qiita.com/katsuhiko/items/56935225754a90d58314#nodejs-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)

node.js も、以前、ubuntu にインストールする手順を書いていたので、この内容を Amazon Linux でも動くように修正しました。

get_url の url を変更したのみです。

```yaml:roles/ec2-nodejs/tasks/main.yml
---
- name: Download setup shell for nodejs
  get_url: url="https://rpm.nodesource.com/setup" dest="/tmp/setup_node.sh" mode=0755

- name: Setup nodejs
  shell: /tmp/setup_node.sh

- name: Install nodejs
  yum: name=nodejs state=latest
```

2015.07.12時点では、https://rpm.nodesource.com/setup_0.12 を利用して 0.12 系をインストールしようとすると以下のエラーが出ました。

```
## Installing the NodeSource Node.js 0.12 repo...


## Inspecting system...

+ rpm -q --whatprovides redhat-release || rpm -q --whatprovides centos-release || rpm -q --whatprovides cloudlinux-release
+ uname -m

## Confirming "el7-x86_64" is supported...

+ curl -sLf -o /dev/null 'https://rpm.nodesource.com/pub_0.12/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'

## Your distribution, identified as "system-release-2015.03-0.1.noarch", is not currently supported, please contact NodeSource at https://github.com/nodesource/distributions/issues if you think this is incorrect or would like your distribution to be considered for support

FATAL: all hosts have already failed -- aborting
```

ざっと見る限り、RHEL7（epel7）からの対応ということでしょうか。
最新の nodejs を利用したい場合／開発で利用する場合は、nvm, nodebrew からインストールしたほうが良さそうです。複数バージョンの管理もできるようになります。

今回は、ツールとして利用するだけですので 0.10系で問題ないと判断しました。


## rails 環境用の各種インストール

RDS（MySQL）を利用する予定なので、gem mysql2 をインストールできるように mysql-devel をインストールします。
あと Rails アプリデプロイ時に必須となる bundler もインストールします。

```yaml:riles/ec2-railsenv/tasks/main.yml
---
- name: Install mysql-devel
  yum: name={{ item }} state=latest
  with_items:
    - mysql-devel

- name: Install dependencies for nokogiri
  yum: name={{ item }} state=latest
  with_items:
    - patch

- name: Install bundler
  sudo_user: "{{ rbenv_user }}"
  gem: name={{ item }} executable=.rbenv/shims/gem user_install=False
  with_items:
    - bundler
```

2015.07.14 nokogiri のインストールで以下のエラーが発生しました。
patch.log を見ると patch がないためにエラーが発生していることがわかりましたので、上記の main.yml に patch のインストールを追加しました。

```
ERROR:  Error installing nokogiri:
	ERROR: Failed to build gem native extension.

    /home/ec2-user/.rbenv/versions/2.2.2/bin/ruby -r ./siteconf20150713-20402-2gqq60.rb extconf.rb
checking if the C compiler accepts ... yes
Building nokogiri using packaged libraries.
checking for gzdopen() in -lz... yes
checking for iconv... yes
************************************************************************
IMPORTANT NOTICE:

Building Nokogiri with a packaged version of libxml2-2.9.2
with the following patches applied:
	- 0001-Revert-Missing-initialization-for-the-catalog-module.patch
	- 0002-Fix-missing-entities-after-CVE-2014-3660-fix.patch

Team Nokogiri will keep on doing their best to provide security
updates in a timely manner, but if this is a concern for you and want
to use the system library instead; abort this installation process and
reinstall nokogiri as follows:

    gem install nokogiri -- --use-system-libraries
        [--with-xml2-config=/path/to/xml2-config]
        [--with-xslt-config=/path/to/xslt-config]

If you are using Bundler, tell it to use the option:

    bundle config build.nokogiri --use-system-libraries
    bundle install

Note, however, that nokogiri is not fully compatible with arbitrary
versions of libxml2 provided by OS/package vendors.
************************************************************************
Extracting libxml2-2.9.2.tar.gz into tmp/x86_64-unknown-linux-gnu/ports/libxml2/2.9.2... OK
Running patch with /home/ec2-user/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/nokogiri-1.6.6.2/ports/patches/libxml2/0001-Revert-Missing-initialization-for-the-catalog-module.patch...
Running 'patch' for libxml2 2.9.2... ERROR, review '/home/ec2-user/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/nokogiri-1.6.6.2/ext/nokogiri/tmp/x86_64-unknown-linux-gnu/ports/libxml2/2.9.2/patch.log' to see what happened.
*** extconf.rb failed ***
```

## Nginx のインストール

本番サーバーでは使うことはないと思いますが、SSL を利用するための自己証明書（オレオレ証明書）も以下を参考に作っています。

* http://qiita.com/hnakamur/items/d6c93442ce28fb2e9aad

デフォルトの index.html, 404.html, 50x.html の置き換えもここで実施しています。
今はできていませんが、上記の html は、playbook で変更できるようにしたかったです。

Nginx の設定は、改善する必要があります。
最低限の設定のみ行っています。設定内容は以下で確認できます。

* https://github.com/katsuhiko/ec2-playbooks/blob/master/roles/ec2-nginx/templates/nginx.conf.j2
* https://github.com/katsuhiko/ec2-playbooks/blob/master/roles/ec2-nginx/defaults/main.yml


```yaml:roles/ec2-nginx/tasks/main.yml
---
- name: Install nginx
  yum: name=nginx state=latest

- name: Set nginx service to start on boot
  service: name=nginx enabled=true

- name: Put nginx.conf
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf backup=true mode=0644
  notify: restart nginx

- name: Create self signed certificate
  shell: |
    mkdir -p /etc/nginx/certs &&
    crt_file="/etc/nginx/certs/local-ssl.crt" &&
    key_file="/etc/nginx/certs/local-ssl.key" &&
    crt_and_key_file="/etc/nginx/certs/local-ssl.crt_and_key" &&
    subject="/C=JP/ST=Osaka/L=Osaka City/CN=local-ssl" &&
    openssl req -new -newkey rsa:2048 -sha1 -x509 -nodes \
      -set_serial 1 \
      -days 3650 \
      -subj "$subject" \
      -out "$crt_file" \
      -keyout "$key_file" &&
    cat "$crt_file" "$key_file" >> "$crt_and_key_file" &&
    chmod 400 "$key_file" "$crt_and_key_file"
    creates="/etc/nginx/certs/local-ssl.crt"

- name: Put share index.html
  template: src=index.html.j2 dest=/usr/share/nginx/html/index.html mode=644

- name: Put share 404.html
  template: src=404.html.j2 dest=/usr/share/nginx/html/404.html mode=644

- name: Put share 50x.html
  template: src=50x.html.j2 dest=/usr/share/nginx/html/50x.html mode=644
```

## Playbook のサンプルと実行

必要な role を読み込みます。

```yaml:ec2-rails.example.yml
- hosts: all
  sudo: yes
  vars:
    rbenv_user: ec2-user
    rbenv_ruby_version: 2.2.2
  roles:
    - ec2-yum-update
    - ec2-rbenv
    - ec2-nodejs
    - ec2-railsenv
    - ec2-nginx
```

以下で inventory ファイルなしで実行できます。
ぼくは相変わらず inventory ファイルなしで実行してばかりです。
本格的な運用をしてないからだと思います。

```bash
$ ansible-playbook -i "xxx.xxx.xxx.xxx," --user=ec2-user --private-key=xxx.pem ec2-rails.example.yml
```

## 感想

[Ansible と Vagrant を使って Rails 開発環境（Ubuntu + rbenv + MySQL5.6 + node.js）を構築する](http://qiita.com/katsuhiko/items/56935225754a90d58314)

で、Rails 開発環境構築を行う playbook を作っていたので、簡単に作ることができると思いましたが、インスタンスタイプで ruby 2.2.2 のインストールができないところでつまづきました。
エラーすら発生しなかったので、終わらないなーっと思いながら、ボーッと待って時間を無駄にしました。