Ansible自体を知らない人が本番に使える人になるまでのスタディー

いまさらAnsibleスタディー？！
と思う方もいらっしゃるかもしれませんが、
特にDevOpsの自動化ツールをご利用していない方に
Ansibleを教えてあげたいと思っております。

初心者方にも役に立つと思っております。
~~( 実は、社内の新人にAnsibleをスタディーするためw )~~

アジェンダは以下の通りです。
1．Ansibleとは？
2．Ansibleに関する必要知識
3．Ansibleの構成
4．Ansibleの実習

## 1．Ansibleとは？
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/9d440eb2-4fac-a264-c5df-cc6053835261.png)
[🔗公式ページ](https://www.ansible.com/)
[🔗ドキュメントページ](https://docs.ansible.com/ansible-container/installation.html)
**Python**で開発された**Provision & Configuration**マネージャーツールの一つで
構築及び管理をより簡単にすることができる **DevOpsの自動化ツール** の一つです。
基本オープンソースツールで無料でございます。**（Github 6位）**
似ているツールとしては、`Chef` , `Puppet` があります。
デプロイ先のエージェントが不要でrubyが不要、設定ファイルが少ないなどの
特徴がChefとの大きな違いのようです。

他のツールよりAnsibleのメリットは以下の通りです。
	- Ad-hocサポート
	- Simple
	- SSH(非Agent)
	- Not DSL (非 Ruby開発者)
	- 並列 provisiongサポート
	- plug-and-play (module, plugin 利用及び開発可能)

[🔗DevOpsの自動化ツール、Ansibleの人気の理由は](http://itpro.nikkeibp.co.jp/atcl/idg/17/060700044/060700001/)
[🔗構成管理ツールとしてAnsibleを選ぶべき理由](https://thinkit.co.jp/article/9445)

## 2．Ansibleに関する必要知識
### 1) DevOps
  何回か**DevOps**というキーワードが出てきたんですね。
  Ansibleを学ぶ前には **DevOps** の知識が必要でございます。
**DevOps** とはContinuous Delivery(持続的なデリバリー) の
メカニズムを運用まで拡張して具現したことです。

一つのSoftwareを開発するためには色んな人が同時に開発できる環境が必要、
数千人の利用者を対象にするためには、サーバとストレージ、
運用体制など、裏側で管理しないといけないインフラ環境も備えなければならないです。
それが **Ops(運営者)** の役割です。

この上で、**Dev(開発者)** はどんどんシステムが拡張したくて開発生産性を高める新しいFWを
導入したいですが、**Ops(運営者)** は提供するサービス及びソフトウェアの安全性にもっと関心
を寄せて、安全性が保障されない理由で避けています。

お互いにそれぞれの目的を持ってそれぞれのプロセスでそれぞれのツールを利用して開発に着手していることです。こんな違いのため、DevとOpsには衝突が発生します。
こういう背景で出てきたのが **DevOps** でございます。

https://ja.wikipedia.org/wiki/DevOps
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/ace988b9-2ea6-7fc4-c989-4b93790d9a11.png)


**DepOps** とはソフトウェア開発者とIT従事者の間で*疎通、協業、融合*を強調した
ソフトウェア開発方法論であり、ソフトウェア開発とIT運営間の相互依存関係についた産物でございます。

組織でDevOpsの役割はソフトウェア商品とサービスを早く生産することに
役立つ任務を遂行することになります。
DevとOps間にその目的を一致させ、プロセスや道具に対する接近を共有し、
その差を減らすことを目的としている。

##### **このような自動化ツールの導入は開発から運営に至るまで開発の全過程において、進捗状況に合わせて必要な時に必要な環境を自由に拡大縮小できるようにすることで、システムスケイルリにすごく自由度を提供します。**

### 2) 冪等性
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/22379c20-48e7-b1a9-5586-03ca0b2f32b3.png)
Ansibleの特徴のひとつは、**冪等性（べきとうせい）**です。 
冪等性はリモートホストの状態を一定に保つためのポリシーですね。
原則としては**【Playbookは繰り返し実行してもリモートホストの状態が変更されないように記述】**することです。
もちろん、初回実行時はリモートホストに変更が全て行われます。
しかし、Playbookを変更しない限り、繰り返して実行してもリモートホストに変更が行われません。
それが重要ポイントです！

これは、もしリモートホストに意図しない変更が行われた場合、
Playbookの実行によりその変更を検知できるとも言えますね。

冪等性を保ったPlaybookを維持することで、
####継続的なリモートホストの状態管理を行えるようになります！

あとで、【4．Ansibleの実習】で ansibleを動かしながら、
各タスクのモジュールの結果が**"changed"**以外、**"ok"**というところが、
実際冪等性が実行されるところなので、一緒に見てみましょう！

もちろんansibleでは冪等性をサポートしていないモジュールも存在します。
その体表的なものが*shell, command, file*モジュールでございます。
なるべく、利用することを避けた方が良いと思っております。

### 3) YMAL
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/094548c0-7f37-4661-59a2-71356849067a.png)
**YAML**(YAML Ain't Markup Language)は*XML, C, Python, Perl, RFC2822*で定義されたe-mail 様式で概念を得て作られた'人が簡単に読める'データ直列化様式でございます。
文法は相対的に理解し易いので、可読性が良いです。
ruby, python開発者は propertyファイルで活用していますが、c, c++, java開発者にはよく使われないらしいです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/9407d411-320c-ce83-9e23-443010633c2c.png)

### 4) インベントリファイル
Ansible は、インフラ内の複数システムに対して同時に動作します。 
**Inventory file** は、 Ansible が動作する`対象のシステムを列挙`するためのファイルです。
もちろん必須ではありません。
使い方としては、Hostだけを列挙して設定したり、
グループを指定してHostを列挙して設定したり色んな便利な追加方があります。
デフォルトのパッチは`/etc/ansible/hosts` でございます。

私は、Ansible以外にも`Capistrano`なども利用していますので、
`ssh config`に設定するのが便利です。
なので、`ssh config`のHostネームだけを`./ansible/host`に列挙して
グルーピングだけをしています。
グルーピングのメリットはインベントリファイル内で複数のホストのグループして、
独自的にタスクを実行することができます。
あとで、【4．Ansibleの実習】で利用することを見てみましょう。

### 5) Playbook
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/60bcf7ec-4745-3611-8f82-d8d2ee397ac0.png)
**Playbook**は遠隔ホストの状態を定義します。
もちろん、yaml文法を採用してポリシーを記述します。
一つのPlaybookは一つまたは一つ以上の**play**を持ち、
**play**の目的はそれぞれのホストに良く定義されている**role**と**task**を
マッピングする役割を引き取っています。
場合によって、Playbookに`role`を定義せずにすぐ**task**を利用することも可能ですが、
`再利用, タスクハンドリング`などを考えたら、良い利用方ではありません。
以下に例のよう、指定Host, 権限, 変数設定, マッピングするroleなどを定義することが可能です。

```playbook.yml
#./playbook.yml
---
- name: web
  become: true
  hosts: all
  user: fusic
  vars_files:
    - vars.yml
  roles:
    - common
    - apache
    - php
    - postgresql
```
あとで、【4．Ansibleの実習】の例示を見たらすぐ理解できると思っております。

## 3．Ansibleの構成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/06b382d2-3c47-2fdb-5c74-491e5d6c88f9.png)

構成を説明する予定でしたらが、上の【2．Ansibleに関する必要知識】でほぼ出てしまいまして、
残りは、【4．Ansibleの実習】から説明させて頂きます。

## 4. Ansible実習
実習環境はMacでございます。

### ・実習構成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/f5d9c8dc-1b5d-188b-84c2-0d82fde571cc.png)
Ansibleを動かすための**Hostサーバ**があります。
そして、2つの**Webサーバ**と 1つの**DBサーバ**をAnsibleで構築します。
その後、2つの**Webサーバ**に`CakePHPFrameworkのWebアプリケーション`を入れるため、
**task**を調整した後、Ansibleを動かしてDBサーバを変更します。（冪等性）
その後、2つの**Webサーバ**に`CakePHPFrameworkのWebアプリケーション`を入れて
DBサーバ(外部サーバ)の接続設定をします。
そして、動作確認したらゴールになるます。
よろしくお願いいたします。

※ 実習に必要なリソースは以下のリポジトリで共有させて頂きます。
[🔗leedohyung-dba/ansible_study](https://github.com/leedohyung-dba/ansible_study)

### 1) 各サーバ生成
今回は、vagrantで**VirtualBox** の 仮想サーバ(VM)にサーバを立ち上げます。
以下の**Vagrant Cloud**で`centos7`を検索すると、vagrant boxを共有していますので、
そのboxを利用しましょう。
[🔗Vagrant Cloud](https://app.vagrantup.com/boxes/search)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/7d8731af-fdaf-d737-da8e-e4129cb73b21.png)

クリックして詳細ページに行くと、以下のように導入する方法がありますが、
そのまま実行せずに、私のリポジトリの`vagrant/Vagrantfile`を自分のディレクトリにコピーして、
中のboxの名をVagrant Cloudで探したbox名に変更してから実行します。

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

Vagrant.configure(2) do |config|
  config.vm.define "host" do |node|
    node.vm.box = "geerlingguy/centos7"
    node.vm.hostname = "host"
    node.vm.network :private_network, ip: "192.168.43.51"
  end

  config.vm.define "web.a" do |node|
    node.vm.box = "geerlingguy/centos7"
    node.vm.hostname = "web.a"
    node.vm.network :private_network, ip: "192.168.43.52"
  end

  config.vm.define "web.b" do |node|
    node.vm.box = "geerlingguy/centos7"
    node.vm.hostname = "web.b"
    node.vm.network :private_network, ip: "192.168.43.53"
  end

  config.vm.define "db" do |node|
    node.vm.box = "geerlingguy/centos7"
    node.vm.hostname = "db"
    node.vm.network :private_network, ip: "192.168.43.55"
  end

  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--cableconnected1", "on"]
  end
end
```

```sh
vagrant up
```

すると、4つのサーバを一気で立ち上げます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/2931f602-f193-229d-1ea9-3c4635ada92b.png)

ちょっと時間掛かります！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/84e8c3f3-fd13-e577-92bf-0cf099cf5f5a.png)

※ これからは全てhostのVM中で行うことになります。

### 2) HostサーバにAnsibleインストール
Hostサーバにsshアクセス後、追加パッケージEPELをインストールします。

```sh
# ssh アクセス
vagrant ssh host

# EPELインストール
wget https://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

# 有効にする
sudo rpm -Uvh epel-release-6-8.noarch.rpm
```
そして、yumでAnsibleをインストールします。

```sh
# Ansibleインストール
sudo yum -y install ansible

# バージョン確認
ansible --version
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/084264a9-f273-e93b-8029-6926217f355a.png)
実習のAnsibleバージョンは `2.3.2` になります。

### 3) web_a, web_b, dbのそれぞれのサーバにssh config設定をします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/33ada514-ce2f-2c75-60a7-0184d3880b5d.png)
hostの中でweb_a, web_b, dbのサーバにssh [HostName]でアクセス出来るように設定します。
※ 詳しい内容省略

### 4) ansibleコマンド
`ansible`のコマンドには以下の二つがあります。
 - **ansible** : ホストに一つの状態を定義できるコマンドです。
 - **ansible-playbook** ： ホストにのPlaybookとして状態を定義するコマンドです。
※ 詳しい使い方としては、ドキュメントから頂けます。

まず、`ansible`コマンドを使ってみましょう。
学習するansibleディレクトリを生成して、
その中に`host`というインベントリファイルを生成します。

```:host
[web_group]
web_a
web_b

[db_group]
db
```
※ デフォルトインベントリファイル：`/etc/ansible/hosts`

各ホストにPingを投げてみましょう。

```sh
ansible all -i host -m ping
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/c2d5aade-0750-6ae2-0a11-c731ca13e4db.png)
sshの設定もいい感じらしいですね−

また、web_groupのホストたちに`tree`というツールをインストールしてみましょう。

```sh
ansible web_group -i host -m yum -a 'name=tree state=present' --sudo
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/ce6f5409-b38e-da8a-bec6-39bb66d2bc15.png)
設置が成功してますが、注目するところは、**changed:**のところです。
もう一度同じ命令を投げると**changed**ではなく、**ok**が出ます。**（冪等性）**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/6ab22803-1067-ebae-9e5d-e0bbb97ff1c7.png)

`-m`のオプションを入れないとデフォルトモジュールは`Command`になります。

```sh
ansible all -i host -a "/bin/echo hello"
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/3be74bb7-fb76-1449-63e7-ce780d4555d6.png)

Localhostにも命令を投げることができます。

```sh
ansible "127.0.0.1," -c local -m yum -a 'name=tree state=present' --sudo
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/64fa4980-2db1-c123-6e59-0897ae8aa7be.png)

インベントリファイルがなくても、ssh configのホスト名をそのまま使えます。

```sh
ansible all -i "web_a,"  -a "/bin/echo hello"
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/642308b7-cb13-430f-6c8f-6727d922913d.png)

### 5) playbookでサーバ構築
じゃー、本格的に上に差し上げた構成図の通りサーバを構築してみましょうかね−
もう一度構成図を置いておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/3d81c746-f931-e4ff-90ac-50ae400db8f0.png)
さて、私の学習リポジトリにあるansibleディレクトリを皆さんの環境に
コピーして配置してください。
(ansibleコマンドを実行するhost VM内で)
[🔗leedohyung-dba/ansible_study](https://github.com/leedohyung-dba/ansible_study)

ざっくり、その中の構成を説明させて頂きます。
#### ・ansible playbookの構成

```text
.
├── host  👉 【インベントリファイル】
├── db.yml  👉 【DBサーバのPlaybook】
├── web.yml  👉 【WEBサーバのPlaybook】
├── roles  👉 【それぞれの状態定義(タスク)があるroleたち】
│   ├── apache
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── ...
│   ├── aws-cli
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── ...
│   ├── common
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── ...
│   ├── php
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── ...
│   └── postgresql
│       ├── tasks
│       │   └── main.yml
│       └── ...
└── vars.yml  👉 【交通で使う変数定義】
```
上端の【2．Ansibleに関する必要知識】でPlaybookの紹介をさせていただきましたとおり、
各Playbookがありまして、一つまたは一つ以上の**play**を持ちます。
そして、**play**の目的はそれぞれのホストに良く定義されている**role**と**task**をマッピングしています。
**web.yml**のPlaybookを説明させて頂きます。

```web.yml
---
- name: web
  # Ansible1.9からはsudo/suの代わりにbecomeを使う
  become: true
  # webグループだけにタスクする
  hosts: web_group
  user: vagrant
  vars_files:
    - vars.yml
  roles:
    - common
    - apache
    - php
```
実行する権限、ホスト(グループ)、ユーザー、編集、rolesを定義しています。

playbook及び各状態定義ファイル(task)の中に何をやっているのか細かく説明コメントを書いて置きましたので、ご覧下と、モジュールの使い方がわからない方は、ドキュメントをご覧ください。
例えば copyのモジュールに関しては[🔗こちら](http://docs.ansible.com/ansible/latest/copy_module.html)を…

vars.ymlに定義されている`main_username`などを好きに変更した後、
他のところは見て大体理解してくださったら嬉しいです。

#### ・ansibleでサーバ構築
じゃーまず、web_aサーバとweb_bサーバを構築してみます。

```sh
ansible-playbook -i host web.yml
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/d041448b-51cf-8924-80ac-cac38dfba3d1.png)
...
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/93db7761-564b-4050-8f80-440eb25acce4.png)

いい感じですね。
次はdbサーバを構築してみます。

```sh
ansible-playbook -i host db.yml
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/d729e91f-f02a-b458-b9d7-571dc1cb1fc6.png)
...
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/89de1779-01fc-e3e9-e4df-b88128f8a7b1.png)
44つのok(冪等性でチェックだけ)と41のchanged(状態変更)
になりました。
こちらもいい感じですね。

#### ・構築されたWEBサーバの動作テイスト
次は`ansible`コマンドで./index.php を各Webサーバに配置して、
正常に動作しているか確認します。
※ scp, mount などで簡単に可能ですが、練習のためです。

```sh
ansible all -i "web_a," -m template -a "src=./index.php dest=/var/www/html/index.php owner=lee group=lee" --extra-vars '{"host_name":"WEB_A"}'  --sudo
ansible all -i "web_b," -m template -a "src=./index.php dest=/var/www/html/index.php owner=lee group=lee" --extra-vars '{"host_name":"WEB_B"}'  --sudo
```
その後、以下のように動作テストしまします。
・アクセスurl: http://192.168.43.52/
・結果：以下の文言があちこちふざけているとオケーです！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/6d2cb825-7e2b-4140-7691-76334af82570.png)
・アクセスurl: http://192.168.43.53/
・結果：以下の文言があちこちふざけているとオケーです！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/59a39773-5f03-0105-0ec5-3bfd251d5047.png)

#### ・CakePHP3のアプリケーションを入れてDBサーバの動作テスト
次は、各WebサーバにCakePHP3のFrameworkを入れて見ましょう！

**しかし、その前に必要なAnsible改善事項がいくつかあります。**
それは、以下の通りです。

**👉 DBサーバに外部からアクセス出来るように設定する**
postgresql.confに以下を行を追加します。

```postgresql.conf
#./ansible/roles/postgresql/files/postgresql.conf

...
listen_addresses = '*'
...
```

pg_hba.confに以下を行を追加します。

```pg_hba.conf2.j2
#./ansible/roles/postgresql/templates/pg_hba.conf2.j2

...
host all all 0.0.0.0/0 trust
```

そして、DB生成ぐらいはやって置きますかね。
以下の行を追加します。

```main.yml
#./ansible/roles/postgresql/tasks/main.yml

...
# データベースを作成
- name: create database
  become: true
  become_user: postgres
  postgresql_db: name={{ db_name }} login_password={{db_user_password}} 
```

```vars.yml
#./ansible/vars.yml

...
db_name: ansible_std
...
```

その後、ansibleをまた動かすと、追加した部分だけ変更されます。（冪等性👍）

```sh
ansible-playbook -i host db.yml
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/b4ba7124-4b02-c6a6-52f3-8c667663f0fe.png)
素晴らしい〜

```:参考
※ ansibleコマンドにはテストするための-Cオプションがあります。
実際は反映しなく、状態定義の実装に失敗があるか確認するための目的です。
ansibleは残念ながら失敗した時、失敗する前のやつがRollBackされないです。
なので、冪等性がないモジュールが含めているTaskでしたら、
一回テストすることをオススメします。
```
じゃー環境の準備は出来ましたので、CakePHPアプリケーションを設置します。
各Webサーバに以下のようにCakePHP3のFrameworkを入れて見ます。
これからは、web_a, web_b で直接アクセスして作業しましょう。
※ 以下のやつもAnsibleで頑張ったら出来ますが、これは環境よりアプリケーションの配置なので、
Capistranoとかの別のツールが正常系です。

```sh
# composer.phar設置
curl -s http://getcomposer.org/installer | php

# CakePHPアプリケーション設置
php composer.phar create-project --prefer-dist cakephp/app lee
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/c2a95376-5e97-9ca3-1137-98a4f62a5a67.png)
そして、CakePHPアプリケーションの外部DBサーバの接続設定をします。

```app.php
// ./lee/config/app.php

'Datasources' => [
    'default' => [
        'className' => 'Cake\Database\Connection',
        'driver' => 'Cake\Database\Driver\Postgres',
        'persistent' => false,
        'host' => '192.168.43.55',
        'port' => '5432',
        'username' => 'postgres',
        'password' => '1q2w3e',
        'database' => 'ansible_std',
        'encoding' => 'utf8',
        'timezone' => 'UTC',
        'flags' => [],
        'cacheMetadata' => true,
        'log' => false,
        'quoteIdentifiers' => false,
        'url' => env('192.168.43.55', null),
    ],
],
```

そうして以下のurlにアクセスすると
http://192.168.43.52/lee/
http://192.168.43.53/lee/
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/459c54d7-a251-a8b6-5430-fb0c2bf34b46.png)

CakePHP3のアプリケーションとして設定など全ての準備完了されたことが確認できます。

最後にテーブルを一つ生成して、基本的な掲示板ページを`back`でパバット生成します。

```sh
bin/cake bake all users
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/8af91bff-030c-f617-4b56-155986496f0d.png)

その後、
以下のそれぞれのWEBサーバにアクセスすると
http://192.168.43.52/lee/users
http://192.168.43.53/lee/users
**同じDB(外部サーバ)を見ているそれぞれのWEBアプリケーションを開けます。**
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/cb737c56-4b5b-34fa-e8a6-3be56a130501.png)


これは一つのサーバが死んだら他のもう一つのサーバを立ち上げる
デュアルサーバー見たいになっていますねw！！！

ある程度終わって気がしますので、構成図をもう一度確認しましょうか。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/108364/0bd0a9eb-5316-108d-beed-73ce34edc67a.png)


Um! いい感じですね〜

構成図にAWS S3にバックアップすることが表示していますが、
taskの内容を見ると頑張って実現しておきましたので、
これは皆さんがPlaybookになれるため、頑張って理解してみましょう！

以上、ちょっと長くなりましたが、
**Ansible自体を知らない人が本番に使える人になるまでのスタディー**
の記事でした。

(韓国人で頑張って日本語書きましたが、やっぱり文書が文法問題だらけかも…)
ありがとうございます！👏👏