【チュートリアル】Ansible 基本のキ (インストールから複数サーバへの変更適用)

#Ansible基本のキ
![images.png](https://qiita-image-store.s3.amazonaws.com/0/85797/72047ae7-672b-a26d-4651-181d9cf7f134.png)

## Ansibleとは?
Ansibleは、サーバに対する設定を、
再利用性の高いコードで記述するためのツールです。

ChefやPuppetが同様のツールとして挙げられます。
(こうした一連のツールを、プロビジョニングツール、と云うそうです。)

###Ansibleの特徴
####1. YAMLで記述する
モジュールなどはPythonやその他言語を利用することになりますが、
基本的にはYAMLという、データ形式を利用します。
Rubyで記述していくChefなどと比較すると、
(複雑な事をする場合を除いては)
可読性が高く、記述量が少なくて済みます。

*YAMLでの記述*

```yaml:playbook.yml
---
- hosts: webservers
  become: True
  tasks:
  - name: install Apache
    yum: name=httpd
```


####2. プッシュ型
設定を反映したいタイミングで、
対象サーバにSSHで接続し、設定を反映していきます。
他の形式では、プル型があります。Chefはこちらに分類されます。
プル型では、プロビジョニングツールでは、
定期的に対象サーバが、中央サーバに対して変更を取得しに行き、
差分があれば実行する形をとっています。
プル型に対しての優位な点は、SSHで接続できる環境であれば、
変更対象のサーバには何も入れる必要がないことです。

####3. 最低限のことをするだけなら、非常にシンプル
最低限のことをするだけなら、
接続先を定義する**インベントリ**というファイルと、
変更手順が記述された**Playbook**の２つで事足ります。

##チュートリアル
基本的な使い方を一通り試してみましょう。
設定内容は、Vagrantを利用して複数の仮想環境を作成し、
それぞれの環境に対してNginxをインストールする、というものです。


###実行環境
利用しているマシンは、"MacOS X El Capitan"です。
また、作業ディレクトリは、
ホームディレクトリ直下に``ansible``という名前のディレクトリです。


```bash
[you@local] $ cd ~
[you@local] $ mkdir ansible
[you@local] $ cd ansible
```


最終的には、ansibleディレクトリの内部は、以下の様になります。

```
ansible
├── Vagrantfile
├── ansible.cfg
├── hosts
└── nginx.yml

```

###Vagrantでの環境構築
変更を反映するために、Vagrantを利用します。
今回は、80番ポートが開放された仮想環境を２つ作成します。
####1. VirtualBoxのインストール
下記のリンクからダウンロードして、インストールしてください。
https://www.virtualbox.org/
####2. Vagrantのインストール
Vagrantをインストールします。
私の環境はMacOSでhomebrewを導入しているので、
brewでインストールしました。
``brew install vagrant``
もちろん、他のインストール方法をとっても構いません。
####3.Vagrantで仮想環境を立ち上げる
VagrantFileを設置し、仮想環境を立ち上げます。

まず、初期化します。

```bash
[you@local] $ vagrant init bento/centos-6.7
```

VagrantFileを、下記の内容に変更します。

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

Vagrant.configure(2) do |config|
  config.ssh.insert_key = false
  config.vm.define "webserver1" do |webserver1|
    webserver1.vm.hostname = "webserver1"
    webserver1.vm.box = "bento/centos-6.7"
    webserver1.vm.network "private_network", ip: "192.168.11.21"
  end
  config.vm.define "webserver2" do |webserver2|
    webserver2.vm.hostname = "webserver2"
    webserver2.vm.box = "bento/centos-6.7"
    webserver2.vm.network "private_network", ip: "192.168.11.22"
  end
end

```

仮想環境を立ち上げます。

```bash
[you@local] $ vagrant up
```

仮想環境が作成できたら、sshでの接続方法の確認の為、下記のコマンドを実行します

```bash
[you@local] $ vagrant ssh-config
```

私の環境だと、下記のような内容になりました

```
Host webserver1
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/you/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

Host webserver2
  HostName 127.0.0.1
  User vagrant
  Port 2200
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/you/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

```

###Ansibleのインストール
ansibleをインストールします。
Macを利用していて、Homebrewを導入しているなら、下記のコマンドでインストール可能です。

```bash
[you@local] $ brew install ansible
```

あるいは、Pythonのパッケージマネージャ pip が利用可能であればインストールできます。

```bash
[you@local] $ sudo pip install ansible
```

###インベントリ
インベントリは、反映先サーバの一覧です。
一般的には、hostsという名前で利用されるようです。
早速、hostsを作成してみましょう
```bash
[you@local] $ vi hosts
```

```:ansible/hosts
[webservers]
192.168.11.21
192.168.11.22

[webservers:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key

```

webserversというグループに、先ほど作成した仮想環境のIPアドレスを登録し、
webserversのグループに適用する変数として、
- ansible_ssh_user
- ansible_ssh_private_key_file
の２つに値を設定しています。
この２つの変数は、ansibleがSSH接続時に利用するものです。
他にも、
- ansible_ssh_host
- ansible_ssh_port
- ansible_ssh_pass
- ansible_connection
などの変数が利用され、それぞれデフォルトの値を持っています。

###とにかく、疎通を確認してテンションをあげよう
ここで、実際にansibleで通信できるか、一度試してみましょう。

```bash
[you@local] $ ansible webservers -i hosts -m ping
```

-iのオプションで、インベントリ(hosts)を指定します。
-mのオプションで、利用するモジュールを指定しています。
pingは、sshでの疎通を確認するためのモジュールです。

最初の接続では、
``The authenticity of host '192.168.11.21 (192.168.11.21)' can't be established.``
といった表示がでるかと思います。
続けて、yesとタイプしてください。

私の環境では、このように出力されました。

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

```

これで、疎通の確認は完了です。

###ついでに、ansible.cnfも書いておく
ansible.cnfを記述しておくと、インベントリへの記述を減らしたり、
コマンドで記述する内容を減らすことができます。
まず、インベントリを編集します。

```:ansible/hosts
[webservers]
192.168.11.21
192.168.11.22

```

varsの内容を削除しました。
続けて、ansible.cnfを作成します。

```:ansible/ansible.cfg
[defaults]
hostfile=hosts
remote_user=vagrant
private_key_file=~/.vagrant.d/insecure_private_key

```

ansible.cfgのなかで、
- hostfile(インベントリ)
- remote_user(ansible_ssh_user)
- private_key_file(ansible_ssh_private_key_file)
以上3点を指定しています。

今回は、インベントリを指定せず実行します。

```bash
[you@local] $ ansible webservers -m ping
```

同様の内容が返却されれば、ansible.cfgの設置は成功です。

###Playbookでnginxをインストール
準備だけで結構かかっちゃいましたが、
ここでやっと、Playbook(環境構築)の記述です。
冒頭で述べましたが、環境構築をYAML形式で記述します。
nginx.ymlというPlaybookを作成します。

```yaml:ansible/nginx.yml
---
- hosts: webservers
  become: True
  tasks:
  - name: install nginx repo
    yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
  - name: install nginx
    yum: name=nginx
  - name: restart nginx
    service: name=nginx state=restarted

```

基本的なYAMLの基本的な読み方は、[Wikipedia](https://ja.wikipedia.org/wiki/YAML)あたりを参考にしてください。
内容を一行ずつみていきます

| nginx.yml | 説明 |
|:-:|:-:|
|``hosts: webservers``|hostの指定。今回はグループ名webservers|
|``become: True``|sudoerとして実行|
|``tasks:``|実行するタスクを順に並べた辞書|
|``name: install nginx repo``| ひとつ目のタスクの名前。Nginxのレポジトリをインストール|
|``yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present``|yumモジュールを使ったタスクの内容。記載のURLからYUMレポジトリをインストール。 |
|``name: install nginx``|Nginxをインストールする旨の名前|
|``yum: name=nginx``|YUMモジュールを利用してNginxをインストール|
|``name: restart nginx``|Nginxを再起動する旨の名前|
|``service: name=nginx state=restarted``|Serviceモジュールを利用してNginxを再起動|

では、このPlaybookを実行してみましょう。
Playbookの実行には、``ansible-playbook``コマンドを利用します。

```bash
[you@local] $ ansible-playbook nginx.yml
```

Playbookの中で、変更対象のhostsを指定しているため、コマンドの引数はymlファイルのみです。

実行結果は以下のようになります。

```
TASK [setup] *******************************************************************
ok: [192.168.11.22]
ok: [192.168.11.21]

TASK [install nginx repo] ******************************************************
changed: [192.168.11.22]
changed: [192.168.11.21]

TASK [install nginx] ***********************************************************
changed: [192.168.11.21]
changed: [192.168.11.22]

TASK [restart nginx] ***********************************************************
changed: [192.168.11.21]
changed: [192.168.11.22]

PLAY RECAP *********************************************************************
192.168.11.21              : ok=4    changed=3    unreachable=0    failed=0
192.168.11.22              : ok=4    changed=3    unreachable=0    failed=0

```

試しに、ブラウザで反映先にアクセスしてみてください。
http://192.168.11.21
Nginxが正しくインストールされていることがわかるかと思います。
![スクリーンショット 2016-08-03 18.59.51.png](https://qiita-image-store.s3.amazonaws.com/0/85797/36953395-f561-16ff-d4ea-9a37d9f12720.png)


もういちど先ほどの``ansible-playbook``コマンドを実行すると、どうなるでしょうか。

```
PLAY [webservers] **************************************************************

TASK [setup] *******************************************************************
ok: [192.168.11.21]
ok: [192.168.11.22]

TASK [install nginx repo] ******************************************************
ok: [192.168.11.22]
ok: [192.168.11.21]

TASK [install nginx] ***********************************************************
ok: [192.168.11.21]
ok: [192.168.11.22]

TASK [restart nginx] ***********************************************************
changed: [192.168.11.22]
changed: [192.168.11.21]

PLAY RECAP *********************************************************************
192.168.11.21              : ok=4    changed=1    unreachable=0    failed=0
192.168.11.22              : ok=4    changed=1    unreachable=0    failed=0

```

``install nginx repo``と``install nginx``のステータスが、``changed``から``ok``になりました。
これは、YUMモジュールが、インストールがされているかどうかを確認して、インストールされている場合、
OKを返すのみで、実際にインストールを実行していない事を指しています。
何度もインストールすることは、予期しないバグやダウンタイムに繋がります。
それを避けるため、モジュールが勝手にそうしてくれているわけです。


###ハンドラ
しかしながら、nginxの再起動のタスク(restart nginx)は、
それ以上でもそれ以下でもなく、ただただ再起動するだけのタスクであり、
２回目以降の実行でも、再起動後、changedとなっています。
これは、(ごく一瞬ではありますが)ダウンタイムにつながります。
これを避けるために、ハンドラという仕組みがあります。
これは、指定したタスクが終了した場合にのみ実行する変更です。
実際に使ってみるために、nginx.ymlを編集しましょう

```yaml:ansible/nginx.yml
---
- hosts: webservers
  become: True
  tasks:
  - name: install nginx repo
    yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
  - name: install nginx
    yum: name=nginx
    notify: restart nginx
  handlers:
  - name: restart nginx
    service: name=nginx state=restarted

```

変更したのは、``install nginx``の箇所にnotifyを追加し、内容をr``estart nginx``としたこと、
``restart nginx``のタスクを、``tasks``とは別の``handlers``に移動させたことです。
これは、``install nginx``のタスクで、実際に変更が起きた場合にのみ、
``handlers``内の``restart nginx``を実行することを意味しています。

実際に、先ほど同様に``ansible-playbook nginx.yml``のコマンドを叩いてみると、
結果は下の様になるかと思います。

```
PLAY [webservers] **************************************************************

TASK [setup] *******************************************************************
ok: [192.168.11.21]
ok: [192.168.11.22]

TASK [install nginx repo] ******************************************************
ok: [192.168.11.21]
ok: [192.168.11.22]

TASK [install nginx] ***********************************************************
ok: [192.168.11.21]
ok: [192.168.11.22]

PLAY RECAP *********************************************************************
192.168.11.21              : ok=3    changed=0    unreachable=0    failed=0
192.168.11.22              : ok=3    changed=0    unreachable=0    failed=0
```

``restart nginx``のタスクは実行されなくなりました。

では、本当にNginxがインストールされた場合に、Nginxを再起動するのでしょうか。
確認するために、Vagrantの仮想環境を作りなおしてみましょう。

```bash
[you@local] $ vagrant destroy
[you@local] $ vagrant up
```

仮想環境が再び立ち上がったら、もう一度、``ansible-playbook nginx.yml``のコマンドを叩いてみます。

```bash
[you@local] $ ansible-playbook nginx.yml

PLAY [webservers] **************************************************************

TASK [setup] *******************************************************************
ok: [192.168.11.22]
ok: [192.168.11.21]

TASK [install nginx repo] ******************************************************
changed: [192.168.11.21]
changed: [192.168.11.22]

TASK [install nginx] ***********************************************************
changed: [192.168.11.21]
changed: [192.168.11.22]

RUNNING HANDLER [restart nginx] ************************************************
changed: [192.168.11.21]
changed: [192.168.11.22]

PLAY RECAP *********************************************************************
192.168.11.21              : ok=4    changed=3    unreachable=0    failed=0
192.168.11.22              : ok=4    changed=3    unreachable=0    failed=0
```


``restart nginx``が実行されています！成功です!


##終わりに
他にもいろいろなTipsがありますが、基本的な利用の流れは以上かと思います。
一通り、実際に試したコードと手順で説明したつもりですが、
もし、なにか足りない箇所があれば、どんどん突っ込んでくださればとおもいます。
Gitなどと組み合わせて、人に優しいインフラ構築を目指していきましょう！



##参考書籍
1. Lorin Hochstein (2016) [初めての Ansible](https://www.oreilly.co.jp/books/9784873117652/) Sky株式会社 玉川 竜司 訳　　オライリー・ジャパン
2. 新原 雅史 (2016) [サーバ/インフラエンジニア養成読本 DevOps 編 ](http://gihyo.jp/book/2016/978-4-7741-7993-3) 特集1: 最速攻略! Ansible 2 によるサーバ構築　技術評論社