AnsibleでRuby環境構築

# はじめに
AnsibleでRuby環境を構築するハンズオン？資料？メモ？です。

本記事は下記を経験したことがある人向けとなります。
- Ruby環境を構築したことがある
- Ansibleを触ったことがある


# 事前環境準備
- *vagrant*と*virtualbox*をインストール

```
$ brew cask install vagrant
$ brew cask install virtualbox
```


# 環境
- OS X Sierra 10.12.6
- Vagrant 2.1.2（ansible 2.6.2）
- VirtualBox 5.2.14


# vagrant設定

###### 作業ディレクトリを作成し移動
```
$ mkdir ansible-ruby; cd $_
```

###### vagrant初期設定

```
$ vagrant init mvbcoding/awslinux
```

###### 仮想マシンの設定

```text:Vagrantfile
Vagrant.configure(2) do |config|
  config.vm.define :ansible_host do |node|
    node.vm.box = "mvbcoding/awslinux"
    node.vm.network :forwarded_port, guest: 22, host: 2001, id: "ssh"
    node.vm.network :private_network, ip: "192.168.33.11"
  end

  config.vm.define :server_1 do |node|
    node.vm.box = "mvbcoding/awslinux"
    node.vm.network :forwarded_port, guest: 22, host: 2002, id: "ssh"
    node.vm.network :forwarded_port, guest: 80, host: 8000, id: "http"
    node.vm.network :private_network, ip: "192.168.33.12"
  end
end
```

###### 仮想マシン起動

```
$ vagrant up
```

###### ステータス確認

```
$ vagrant status
```

###### 仮想マシンにssh接続

```
$ vagrant ssh ansible_host
$ vagrant ssh server_1
```

###### （補足）ちょくちょく使うコマンド

```
# 仮想マシンリロード
$ vagrant reload

# 仮想マシン停止
$ vagrant halt

# 仮想マシン削除
$ vagrant destroy
```

###### トラブルシューティング

ホストOSとファイル共有設定をしている場合、`vagrant up` 時にGuest Additionsバージョン違いのエラーが出ると思いますので、`vagrant-vbguest`プラグインをインストールし、`vagrant up`と`vagrant halt`を繰り返して下さい。。。。（数回）

```
$ vagrant plugin install vagrant-vbguest
```


# ansible_hostサーバにAnsibleをインストール
```sh:ansible_host
# Ansibleインストール
$ sudo yum -y --enablerepo=epel install ansible

# バージョン確認
$ ansible --version
ansible 2.6.2
```
AmazonLinux2にAnsibleをインストールする場合は`amazon-linux-extras` コマンド経由で
インストールすると色々と便利です。
`$ sudo amazon-linux-extras install ansible2`


# Ansible実行前準備
- Ansibleは操作したい対象サーバにSSH接続して環境を構築します。  

```sh:ansible_host
# SSH鍵作成
$ ssh-keygen -t rsa
```

```sh:server_1
# ユーザー作成
$ sudo useradd ansible

# ansibleユーザーにホストサーバで作成した公開鍵（id_rsa.pub）を設置
$ sudo su - ansible
$ mkdir .ssh
$ chmod 700 .ssh
$ vim ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys

# ansibleユーザーにsudo権限付与（rootユーザーで実行してください）
$ visudo
(下記1行を追加)
ansible   ALL=(ALL)       NOPASSWD: ALL
```

```sh:ansible_host
# SSH接続
$ ssh ansible@192.168.33.12
```

※ 今回は**sudo**コマンドをパスワードなしで実行させてますが、管理上好ましくないためご注意ください。 :warning: 


# インベントリファイルの作成
- 対象サーバを指定するためにインベントリファイルを作成します。

```sh:ansible_host
$ vi hosts

---
[server1]
192.168.33.12
```

- 対象サーバにansibleコマンドで接続確認（ユーザーを指定）

```sh:ansible_host
$ ansible server1 -i hosts -u ansible -m ping

192.168.33.12 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
```


# Ruby環境構築時のおさらい
プレイブックに書き出す前にイメージし易いようRuby環境構築手順の流れをざっくり書いておきます。

1. gitやgcc、openssl-develなど必要なパッケージ群をインストール
2. rbenvインストール
3. システムワイドに使えるよう`/etc/profile.d/`配下にシェルを設置
4. ruby-buildインストール
5. rbenvコマンドが実行出来るグループを作成
6. 適宜ユーザーをグループに所属させる
7. rbenvディレクトリ配下の所有グループを変更
8. rbenvを使ってrubyをインストール
9. bundlerインストール


# プレイブックファイルの作成
それでは上述した環境構築手順を参考にプレイブックファイルを作成していきます。
※今回ansibleの書き方に慣れてもらうためフォルダ構成はシンプルにしています。

- 初期定義

```sh:ansible_host
$ vi site.yml

---
- hosts: server1 # インベントリファイルの対象サーバを指定
  become: yes # rootユーザーでの実行
```

- yum update（[yum-module](https://docs.ansible.com/ansible/2.5/modules/yum_module.html)）

```yaml:site.yml
  tasks:
    - name: 'yum update'
      yum:
        name: '*'
        state: latest
```

- 必要なパッケージ群をインストール（[yum-module](https://docs.ansible.com/ansible/2.5/modules/yum_module.html)）

```yaml:site.yml
...
    - name: 'Install packages'
      yum:
        name: "{{ item }}"
      with_items:
        - git
        - gcc
        - openssl-devel
        - libyaml-devel
        - readline-devel
        - zlib-devel
        - libffi-devel
```

- rbenvインストール（[git-module](https://docs.ansible.com/ansible/2.5/modules/git_module.html)）

```yaml:site.yml
    - name: 'Install rbenv'
      git:
        repo: https://github.com/sstephenson/rbenv.git
        dest: /usr/local/rbenv
```

- rbenvのパス設定（[template-module](https://docs.ansible.com/ansible/2.5/modules/template_module.html)）

システムワイドに利用出来るように`/etc/profile.d/`配下にこのようなshファイルを配置します。

```sh:rbenv.sh
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init -)"
```

`site.yml`と同じディレクトリ内に「templates」フォルダを作成し、テンプレートファイルを`.j2`拡張子ファイルで格納しておきます。（j2とはpython用のテンプレートエンジンライブラリ **jinja2**）

```sh:templates/rbenv_system.sh.j2
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init -)"
```

```yaml:site.yml
    - name: 'rbenvのshファイルを配置'
      template:
        src: rbenv_system.sh.j2
        dest: /etc/profile.d/rbenv.sh
        owner: root
        group: root
        mode: 0755
```

（補足）shellコマンドを使って下記のようにも書けますが・・・なんかイマイチ。。。

```
shell: echo 'export PATH="export RBENV_ROOT=/usr/local/rbenv"' >> /etc/profile.d/rbenv.sh
shell: echo 'export PATH="export PATH="${RBENV_ROOT}/bin:${PATH}""' >> /etc/profile.d/rbenv.sh
shell: echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh
```


- ruby-buildインストール（[git-module](https://docs.ansible.com/ansible/2.5/modules/git_module.html)）

```yaml:site.yml
    - name: 'ruby-buildインストール'
      git:
        repo: https://github.com/sstephenson/ruby-build.git
        dest: /usr/local/rbenv/plugins/ruby-build
```

- rbenvコマンドが実行出来るグループを作成（[group-module](https://docs.ansible.com/ansible/2.5/modules/group_module.html)）

```yaml:site.yml
    - name: 'deploy用のグループを作成'
      group:
        name: deploy
```

- ansibleユーザーをdeploy用グループに所属させる（[user-module](https://docs.ansible.com/ansible/2.5/modules/user_module.html)）

```yaml:site.yml
    - name: 'ansibleユーザーをdeployグループに所属'
      user:
        name: ansible
        groups: deploy
```

- rbenvディレクトリの所有グループをdeployグループに変更（[file-module](https://docs.ansible.com/ansible/2.5/modules/file_module.html#file-module)）

```yaml:site.yml
    - name: 'rbenvディレクトリの所有グループをdeployに変更'
      file:
        path: /usr/local/rbenv
        owner: root
        group: deploy
        recurse: yes
        state: directory
```

- rubyインストール（[shell-module](https://docs.ansible.com/ansible/2.5/modules/shell_module.html)）

```yaml:site.yml
    - name: 'rubyインストール'
      shell: bash -lc "rbenv install --skip-existing 2.5.0"
```

- インストールしたRubyバージョンをglobalセット（[shell-module](https://docs.ansible.com/ansible/2.5/modules/shell_module.html)）

```yaml:site.yml
    - name: 'globalセット'
      shell: bash -lc "rbenv global 2.5.0"
```

- bundlerインストール（[gem-module](https://docs.ansible.com/ansible/2.5/modules/gem_module.html)）

```yaml:site.yml
    - name: 'bundlerインストール'
      gem:
        name: bundler
        user_install: no
        executable: /usr/local/rbenv/shims/gem
```


# 確認〜実行
##### Syntax Check
`$ ansible-playbook site.yml -i hosts --syntax-check`

##### Dry Run
`$ ansible-playbook site.yml -i hosts -u ansible --check`

##### Run ♪
`$ ansible-playbook site.yml -i hosts -u ansible`

```text:server_1
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
```
:tada:


# Dry Run実行時の注意点
dry-run実行時はリモートシステム上での変更は行われないため実行したコマンド結果に依存する処理の場合、何かしらエラーになってしまいます。
ansible 2.2以降から`check_mode`オプションが追加され、
下記のように記述することで`--check`オプション実行時にtaskをスキップしてくれます。

```yaml:site.yml
  tasks:
    - name: 'yum update'
      yum:
        name: '*'
        state: latest
      check_mode: yes
```

# まとめ
Ansibleでは様々な書き方ができ、模範となる回答が探し難いのですがとりあえず動けばいいやのノリで作成してみました。（色々とツッコミあればください :bow:）
次回はディレクトリ構成や冪等性などに触れてみたいと思います :writing_hand:
[今回のソース置き場](https://github.com/Caad-Fukushi/ansible-ruby) 


# 参考
[Ansible role for installing rbenv.](https://github.com/zzet/ansible-rbenv-role)