サーバ障害を学ぶ環境をansibleを使って作る話

この記事は[TECH::CAMP Advent Calendar 2017](https://qiita.com/advent-calendar/2017/tech-camp)の24日目の記事です。
これは初学者向けにサーバ学習のための環境を作るために、EC2 + ansibleを利用した内容となります。

# おことわり

ansibleは最終的にはサーバの安定稼働のために利用するものと思いますが、この記事でやろうとしていることはその逆です（笑）。そのためこの記事の内容が役立つ人が世の中にどのくらいいるかわかりませんが、なにかの役に立つ人がいましたら幸いです。

また、普通にansibleを動かすための情報はネット上にいくつもあります。そこでこの記事では、ansibleを動かすための説明は必要最低限に絞らせていただきます。

# なんでこういうのを考えたのか？
## そもそもの背景

Webサービス開発に必要となるスキルの一つとして、いまではサーバの構築もあると考えています。というのも、AWSのようなパブリッククラウドのおかげでインフラエンジニアでなくてもサーバ構築ができるくらいに難易度が下がったためです。

ただそうであっても、障害が発生した時に原因を見つけたり解決方法を身につけるには知識と経験が必要と言われています。

しかしこうした知識は重要であることは分かっていても、座学ではなかなか頭に入ってこない性質のものだと思います。実際、かつて私も教えてもらっただけでは頭に入ってきませんでした。

そこで障害が実際に起きているサーバを用意することでそのサーバに学習者がSSHでログインしてもらい、自由に触れる環境の中で原因と解決方法を学べるとよいのでは？と考えました。

## ansibleを使った背景

学習用サーバ環境は業務用サーバと違って、１人１台や数人に１台のように、同じようなサーバが何台も必要となります。しかしこうした同じことをわざわざ手動でサーバを作っていたら時間かかるし、間違いなく作業漏れも発生します。

そこでansibleを利用してサーバ構築をコード化し、短時間＆作業漏れがないようにサーバが構築できるようにしたというものです。

# 環境の説明

今回作った環境は以下のようになります。
<img width="677" alt="ansible環境.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/77532/bf578645-2245-cf05-5386-d3782f788521.jpeg">

今回ansibleを利用して以下の部分をコード化しました。

1. EC2インスタンスの作成
2. EC2インスタンスの設定

そして、ansibleを動かすためだけにEC2インスタンスを作りたくなかったので、手元のPC(Mac)でansibleを動かすことにしました。

このようにして作ったEC2インスタンスに対し、学習者にSSHログインしてもらって障害の原因調査と対処方法を解いてもらう形にしました。

# 事前準備
動かす前にansibleを動く環境を作っておきます。またansibleでAWSを操作するため、以下も用意しておきます。なおこれらのお話はansibleを動かす時には一般的な話となるため、本記事ではひとつひとつの説明を省きます。

- pipを利用してboto3導入
- productionディレクトリ内に[ec2.py](https://github.com/ansible/ansible/tree/devel/contrib/inventory/ec2.py)と[ec2.ini](https://github.com/ansible/ansible/tree/devel/contrib/inventory/ec2.ini)を置いておく
- IAMを利用して、EC2やElasticIP割り当て可能なユーザの作成
- 上記IAMユーザのアクセスキーでAWSにアクセスできるように設定
- この設定は`aws configure`コマンドで設定します。
    - 本記事では`awsprofile`というプロフィール名で設定しました。

また、以下のようなansible設定ファイルを用意します。

```:ansible.cfg
[defaults]
private_key_file={EC2作成時に利用した鍵ファイルを絶対パスで指定}
host_key_checking = False
```

# ①EC2作成要求

上記の準備ができたら、実際にEC2作成要求ができるようにymlファイルやロールを作成していきます。

## ファイル準備
### 実行用ymlファイル

EC2作成要求用ymlファイルを用意します。ここではserver.ymlというファイル名にしています。なおymlファイルのenvname,keypairの説明は以下の通りです。

- envname
    - タグの指定です。最終的に`env`というキーに`server`という値を設定するために、下記のように記載しています。
- keypair
    - EC2作成時に利用するキーの指定(このファイルでは`sshkey`というキー名になってます)
- image
    - AMIのIDを指定します。下記ファイルでは、2017/12/01時点の最新のAmazonLinuxの公式イメージを指定しています。

```:EC2作成用ymlファイル(server.ymlとします)
- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    ec2:
      envname: "server"
      image: "ami-2803ac4e"
      keypair: "sshkey"
      count: 1
  roles:
    - createEC2
```

### ロール

ここでは、EC2作成とElasitcIPを割り当てるロールを作ります。
なお、ansibleではロールはroleディレクトリ以下に置くため、説明の通りの場所に置きましょう。

また、事前に以下の準備やファイル内の読み替えを行ってください。

- 事前にec2-secrity-group という名前のセキュリティグループ作成しておく
- subnet-XXXXXXXXは自分の環境に合わせて、EC2を作成したいサブネットのサブネットIDを記載する

```:role/createEC2/tasks/main.yml
- name: "create ec2 instance"
  ec2:
    instance_tags:
      env: "{{ ec2.envname }}"
    region: ap-northeast-1
    key_name: "{{ ec2.keypair }}"
    instance_type: "t2.micro"
    image: "{{ ec2.image }}"
    wait: yes
    group: ec2-secrity-group
    count:  "{{ ec2.count }}"
    vpc_subnet_id: "subnet-XXXXXXXX"
    assign_public_ip: yes
  register: ec2_rgst

- name: "wait for ssh"
  wait_for:
    host: "{{ ec2_rgst.instances[0].public_ip }}"
    port: 22

- name: "associate new ElasticIP"
  ec2_eip:
    region: ap-northeast-1
    device_id: "{{ ec2_rgst.instances[0].id }}"
  register: eip
```

## 実行
ここまで準備ができたら、動作確認をした後で実行します。
1〜2分でEC2インスタンスの構築とElasticIPの割り当てを自動的にやってくれます。

```:実行方法
# 今回利用するIAMユーザのプロフィールを指定(指定していない場合は不要)
% export AWS_PROFILE=awsprofile

# 動作確認
% ansible-playbook -vv --diff -i production server.yml --check

# 実行
% ansible-playbook -vv --diff -i production server.yml
```

productionディレクトリにec2.py,ec2.iniを置くことで、ansible-playbook実行時に`-i production`と指定するだけでこれらを利用することが可能となります。

# ②EC2設定

EC2インスタンスができたら、AWSのWebコンソールを上からホスト名をつけます。今回は`server001`という名前にしました。

## ファイル準備 

先ほどと同じように実行用ymlファイルとロールを用意します。

### 実行用ymlファイル

```:EC2設定用ymlファイル(ec2setting.ymlとします)
- name: サーバ設定
  gather_facts: no
  hosts: tag_Name_{{ hosts }}
  remote_user: ec2-user
  vars:
    mysql_port: 3306
    keyfile: {受講生用の公開鍵を絶対パスで指定}
  roles:
    - common
    - mysql-error
```

### ロール
ここではサーバの時間をUTCからJSTにするなどの基本設定、及び必要となるソフトウェア導入を行うためのロールと、サーバ障害が起きているロールを適用するために作成しました。

ただ今回はサーバを学ぶ環境がテーマなので基本設定を行うためのansible設定については説明を省略し、サーバ障害が起きているロールの一例を紹介します。

このロールの内容は、ダミーでソケットファイルができていてMySQLが起動しない障害を起こすものです。

```:role/mysql-error/tasks/main.yml
---
- name: Create Mysql configuration file
  template: src=my.cnf.j2 dest=/etc/my.cnf

- name: Start Mysql Service
  service: name=mysqld state=started enabled=yes
  ignore_errors: true

- name: Stop Mysql Service
  service: name=mysqld state=stopped enabled=yes
  ignore_errors: true

- name: dummy socket file
  file:
    path: /var/lib/mysql/mysql.sock
    state: touch
    owner: root
    mode: "u=rw,g=r,o=r"
```
```:role/mysql-error/templetes/my.cnf.j2
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
port={{ mysql_port }}

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
```

## 実行
先程と同じように、変更に関する動作確認をしてから実行します。

```:実行
# 今回利用するIAMユーザのプロフィールを指定(指定していない場合は不要)
% export AWS_PROFILE=awsprofile

# 動作確認
$ ansible-playbook -vv --diff -i production ec2setting.yml -s --extra-vars "hosts=server001" --check

# 実行
$ ansible-playbook -vv --diff -i production ec2setting.yml -s --extra-vars "hosts=server001"
```

通常、管理対象となるサーバはインベントリhostファイルの中に記載し、`-i`でそのファイルを指定して実行すると思います。

しかし今回はNameタグに記載した値をEC2のホスト名にするように設定が行われるように、ansible実行時にNameタグの値を指定させることにしました。これによって、インベントリhostsファイルを作る必要はありません。

# まとめ

今回はサーバ障害を学ぶ環境を作るためにansibleを使ってEC2インスタンスを作りました。これは思っていたよりも相性がよく、短時間で設定漏れの無いサーバが作れるので学習者が何人もいても待ち時間が少なくできました。

また問題が解けて不要になったらすぐに削除して作り直すことができるのは、過去問の影響を引きずらないためにいろいろなことを考慮しなくてもよくなりました。もしサーバの作り直しに時間がかかるなら、時間短縮のために考慮せざるを得なかったと思います。

こうした学習環境ができるようになったのはAWSのようなパブリッククラウドがあったからで、今までなら経験でしか学べないものが学習環境を作ることで学べるようになったというのは本当に良い時代になったものだと思いました。

# 今後

まだまだできたてなので、改良したい点はいくつかあります。特にいまだと2回ansibleを実行する必要がありますが、これを1回で実行できるようにしたいところです。（たぶんできるはず）

またサーバ障害の問題をもっと増やすこと、またchatops化してansible実行用PCがなくてもSlack上でコマンドを実行すれば学習用環境ができるようにするなど、内容の充実や改良を加えられればと思っています。

それでは、みなさまよいクリスマスを！