Owly のサーバ構築で Ansible を利用した話


**※この記事はエキサイトAdvent Calendar 2017 9日目の記事です。**

みなさん、こんにちは！
エキサイトで [Owly](https://owly.jp/) や [電話占い](https://d.excite.co.jp/)、[ウーマン占い](http://fortune.woman.excite.co.jp/)、[フレンズ](http://friends.excite.co.jp/) などの開発を担当している、エンジニア2年生の伊藤です! :wave:

本記事では、先日リリースしたばかりの [Owly](https://owly.jp/) のサーバ構築で利用している **Ansible** についての簡単な紹介、
導入するにあたって気をつけたことを述べようと思います! :yum:

# Ansibleとは
python製の構成管理ツールで、同じ環境の複数のサーバを統一して管理するなどの目的で利用されます。
構成管理ツールとしては **Chef** や **Puppet** がよくとりあげられますが、**Ansible** はこれらと比べて以下の違いがあります。

## yaml形式の設定ファイル
ChefはRubyで、Puppetは独自のDSLで、サーバの設定を記述しなければなりませんが、Ansibleはyamlで設定ファイルを記述することができるので、
初学者でも比較的容易に設定ファイルを記述することができます！

## エージェントレス
ChefやPuppetは、管理対象のサーバに予めエージェントと呼ばれるソフトウェアをインストールしておく必要がありますが、
Ansibleはsshで管理対象のサーバに接続して操作を行うため、管理対象のサーバに対する事前準備がほとんど必要ありません！


# 長い,3行で :sob:
・Python製の構成管理ツール
・設定ファイルの記述にyamlが使える
・エージェントレス

# Ansibleを導入するにあたって気をつけたこと

## 再利用性
[Owly](https://owly.jp/) では、フロントのサーバの他に、API、管理ツール、DB、キャッシュサーバ、etc... のサーバ群で構成されており、DBを除く全てのサーバの構成を
Ansibleで管理しています。
複数のサーバを管理する場合には、**再利用性** を考慮して設計する必要があります。

下記のコードは、Playbookと呼ばれるAnsible設定ファイルの例です。

```
- name: Setup Front
  hosts: all
  become: yes
  become_user: root
  tasks:
    - name: install nginx
      apt: 
        name: nginx-common
        state: present
        update_cache: yes
    - name: copy nginx conf
      copy:
        src: /home/owner/ansible/conf/nginx/default.conf
        dest: /etc/nginx/sites-available/default.conf
    - name: install php
      apt:
        name: php7.0-fpm
        state: present
        update_cache: yes
...
```

この例のように、フロントサーバ用のPlaybookに直接taskを記述してしまうと、他のサーバに対してもNginxやPHPをインストールしたい！
という場面で、ふたたび同じ処理を記述することになってしまいます。

そこで、例えば Nginx に関連したtaskを１つのRoleにまとめておくと、他のサーバのPlaybookを作成するときにNginxのRoleをincludeして使いまわすことが出来ます。

```
setup_front.yml
roles/
    common/
        tasks/
            main.yml
    nginx/
        tasks/
            main.yml
    php/
        tasks/
            main.yml
...     
```
このような感じのディレクトリ構成でroleを用意しておいて、

```
- name: Setup Front
  hosts: all
  become: yes
  become_user: root
  roles:
    - 'common'
    - 'nginx'
    - 'php'
...
```
Playbookで各roleをinclude!
再利用できるだけでなく、playbookの可読性も改善されます！

## 冪等性 (べきとうせい)
冪等性とはなにか。wikipediaによると、
> 数学において、冪等性（べきとうせい、英: idempotence 「巾等性」とも書くが読み方は同じ）は、大雑把に言って、ある操作を1回行っても複数回行っても結果が同じであることをいう概念である。 まれに等冪（とうべき）とも。
-- Wikipedia(日本語訳)

とあります。
Ansibleなどの構成管理ツールは、サーバに対して実行する処理を管理するものではなく、サーバの状態そのものを管理します。
それはつまり、あるサーバに対してPlaybookをN回実行したとしても、状態は常に変わらないということが担保されている必要があります。

例えば、

```
- name: Setup Front
  hosts: all
  become: yes
  become_user: root
  tasks:
    - name: install nginx
      apt: 
        name: nginx-common
        state: present
        update_cache: yes
```
このPlaybookで、task `install nginx` が実行された時、対象のサーバにNginxがインストールされていなければ、

```
TASK [install nginx] ******************************************************
changed: [Front]
```
Nginxがインストールされ、サーバの状態が変更された旨を表す `changed` を示します。

対象のサーバにnginxがインストールされているならば、

```
TASK [install nginx] ******************************************************
OK: [Front]
```
Nginxのインストール処理は実行されず、サーバの状態が変更されずに処理を終えたことを表す `OK` を示します。

2回、３回と実行しようがサーバーの状態は常に一定。つまり上記のPlaybookは冪等性が担保されているといえます。

一方、下記のような、指定のディレクトリが存在するか否かによって実行処理が分岐するPlaybookを実行した場合、

```
- name: check exist cache dir
  command: ls /var/hoge/owly/htdocs
  register: cachedir

- name: make cache dir
  file:
    path: /var/hoge/owly/htdocs/cache
    state: directory
    owner: root
    group: root
    mode: 0755
  when: cachedir.stdout
```

task `check exist cache dir` は、`/var/hoge/owly/htdocs`の存在有無にかかわらず、

```
TASK [check exist cache dir] ******************************************************
changed: [Front]
```
changedを示してしまいます。
このPlaybookは、2回、3回と実行するたびに`changed`を示してしまうために、冪等性が担保されません。

冪等性を破壊しているのは、`command`モジュールで、これは対象サーバに対して指定のコマンドを実行し、常に`changed`を示します。

task `check exist cache dir`は、ファイルの存在確認を行うだけで、サーバの状態に影響を与えるものではないです。
そこで、サブ構文`changed_when: False`を追記し、常にサーバに影響を与えないtaskであるということを明記しておくことで、冪等性を担保することが出来ます。

(冪等性の説明のために、ファイルの存在確認処理を`command`モジュールを用いて記述していますが、`stat`モジュールを用いることでより簡潔に記述することも出来ます)

# 終わりに

いかがでしたでしょうか。
Ansibleは非常に便利なプロダクトですが、**再利用性**や**冪等性**を考慮しなければ、その利便性を十分に活かすことは出来ません。
[Owly](https://owly.jp/)のサーバ構築時には、特に再利用性について、どれくらいの粒度でRoleを分けるのかという点も含めて設計が大変でした。
別の機会があれば、jinja2を活かした効率的なconfの配置など、より込み入った話が出来たらと思います。

明日のアドベントカレンダーは、iOSアプリについてです！お楽しみに！