Ansible

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

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

みなさん、こんにちは!
エキサイトで Owly電話占いウーマン占いフレンズ などの開発を担当している、エンジニア2年生の伊藤です! :wave:

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

Ansibleとは

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

yaml形式の設定ファイル

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

エージェントレス

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

長い,3行で :sob:

・Python製の構成管理ツール
・設定ファイルの記述にyamlが使える
・エージェントレス

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

再利用性

Owly では、フロントのサーバの他に、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を1つの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回、3回と実行しようがサーバーの状態は常に一定。つまり上記の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のサーバ構築時には、特に再利用性について、どれくらいの粒度でRoleを分けるのかという点も含めて設計が大変でした。
別の機会があれば、jinja2を活かした効率的なconfの配置など、より込み入った話が出来たらと思います。

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