Posted at
NIJIBOXDay 1

GitLab-CI+Ansibleで組み立てる、快適なRoute53マネージメント(α版)

More than 1 year has passed since last update.

ニジボックスはWeb制作の受託なんかもやっているのですが、その辺りも含めてドメインの管理数がそこそこ多かったりします。

現状Route53で管理しているのですが、


  1. ドメインをレジストラから取得(手作業)

  2. Route53でゾーンを作成(手作業)

  3. NSレコードに用意されたネームサーバーをレジストラに登録(手作業)

  4. ゾーンにレコードを追加(手作業)

という手作業祭りになっています。

いい加減面倒になってきたので、ちょっと仕組みを作ってみることにしました。


登場人物


GitLab.com

「プライベートリポジトリ」「CIサービス」「Dockerレジストリ」と便利なものが揃って無料という素敵サービス。

今回はリポジトリでのドメイン情報やPlaybookの管理に、CIでの設定反映を一手に引き受ける今回の主人公


Ansible

おなじみ構成管理ツール。「こんなのにもモジュールがあるのか」という驚きに定評がある。


Slack

おなじみチャットツール。お知らせを受け取るのが仕事。


その1: Route53管理用のPlaybookを用意する

(本当は社内情報を抜き出してリポジトリにした方がよかったのですが、無理だったので要点を抜粋します)



  • files/


    • example.com.yml



  • group_vars/


    • all.yml



  • tasks/


    • zone.yml

    • record.yml



  • templaes/


    • records.j2



  • run.yml


よくあるAnsibleのベストプラクティスを魔改造気味にいじった感じになっています。

当然ながら、run.ymlが実行の起点になっています。

group_vars/all.ymlにゾーン情報、files/example.com.ymlにはドメインごとのレコード情報を定義しています。


run.yml


run.yml

---

- hosts: runner
connection: local
vars:
zone_list: '{{ zones|map(attribute="zone")|list }}'
tasks:
- name: Manage zones
include: tasks/zone.yml
with_items: '{{ zones }}'
- name: Generate
template:
src: templates/records.j2
dest: files/records.yml
- name: load record list
include_vars: files/records.yml
- name: Manage records
include: tasks/record.yml
with_items: '{{ record_list }}'

run.yml内ではループしながらtasks/をインクルードして中のタスクを実行させるのが大半の役割です。

他にも、謎のGenerateが走るのですが、後述します。


zone.yml

---

- name: Enable zones - {{ item.zone }}
route53_zone:
zone: '{{ item.zone }}'
state: present
register: zone_added
- name: Fetch zone information - {{ item.zone }}
route53:
command: get
zone: '{{ item.zone }}'
record: '{{ item.zone }}'
type: NS
register: zone_info
when: zone_added.changed
- name: Report to Slack channel - {{ item.zone }}
slack:
token: '{{ slack.token }}'
channel: '{{ item.slack_channel|default(slack.channel) }}'
msg: |
Route53に、新規ゾーン"{{ item.zone }}"を作成しました。
レジストラに次のネームサーバーを登録してください。
{% for ns in zone_info.nameservers %}
- {{ ns }}
{% endfor %}
when: zone_added.changed

zone.ymlの中身は「Route53にドメインを登録してSlackにネームサーバーを通知する」のみです。

route53_zoneモジュールは実行時にゾーンを追加した時に限りchangedになるので、通知は最初の1回のみです。

(2回目以降はdigでも使いましょう)


record.yml


record.yml

---

- name: Enable record - {{ item.name }}
route53:
zone: '{{ item.zone }}'
record: '{{ item.name }}'
value: '{{ item.value }}'
type: '{{ item.type }}'
command: create

ひたすら、レコードを追加するのがrecord.ymlの仕事。

レコード削除?まだないです(むしろつくらない気がする)

基本的には、


  1. all.ymlにドメインを追加して、ゾーンに登録

  2. ドメイン用のレコード情報YAMLをfilesに置いて、レコードを登録する

というような運用になっています。


その2: GitLab-CIで最低限のチェックとPlaybook実行する

---

stages:
- lint
- deploy

check_yaml_format:
stage: lint
image: python:2-alpine
script:
- pip install yamllint
- yamllint .

run_ansible:
stage: deploy
only:
- master
image: ansible/ubuntu14.04-ansible
script:
- pip install boto
- ansible --version
- |
if [ $CI_PROJECT_NAMESPACE = "attakei" ] ; then
ansible-playbook -i hosts/gitlab-ci run.yml
else
ansible-playbook -i hosts/gitlab-ci run.yml -C
fi

GitLab-CIが便利です。

GitLab-CIはタスク処理を段階的に分ける仕組みが備わっているのですが、

ブランチ条件の指定も可能なので、特定条件の反映に限り実行するという定義も可能です。

ここでは、check_yaml_formatrun_ansibleという二つの処理に分けて、



  1. check_yaml_formatは毎回実行してリポジトリ配下のYAMLが文法上正しいかをチェックする


  2. run_ansible


    1. masterブランチであり」 「namespaceがattakeiである」時に限りrun.ymlのPlaybookを実行する

    2. masterブランチであり」 「namespaceがattakeiでない」時はrun.ymlのPlaybookをdry-runする



と分けることにしています。

AnsibleはDockerHubに実行可能なコンテナイメージを用意してくれているので、今回依存しているbotoさえついかでインストールさえすれば、簡単にGitLab-CI上でAnsibleを実行できます。

GitLab-CI実行時には環境変数なども見ることができるため、AWS_ACCESS_KEYAWS_SECRET_KEYを登録しておけばリポジトリにキーをおかずに済むので安心。


その3: (その1) と (その2) が合わさり最強に見える

これらの実装を組み合わせると、


  1. オリジナル(upstream)はmasterブランチをロックして、マージのみ受け付ける

  2. 新しく管理ドメインを足したい時は、group_vars/all.ymlを更新してマージリクエストを出すようにする

  3. 既存のドメインに新しくレコードを追加したい時は、files/example.com.ymlを更新してマージリクエストを出すようにする

  4. マージリクエストはマージ元のCIが通ったことを確認してからマージする

  5. マージされる先がmasterブランチとなるため、run_ansibleが自動で実行されて必要に応じてSlackに報告が上がる

  6. Ansibleが冪等性を保ってくれるため、何度やっても問題ない

という、嬉しい実装になります。


こうなった


  1. ドメインをレジストラから取得(手作業)

  2. Route53でゾーンを作成(手作業)

  3. NSレコードに用意されたネームサーバーをレジストラに登録(手作業)

  4. ゾーンにレコードを追加(手作業)

↓これを


  1. ドメインをレジストラから取得(手作業)


  2. group_vars/all.ymlを更新してRoute53でゾーンを作成(ファイル更新だけ手作業

  3. NSレコードに用意されたネームサーバーをレジストラに登録(手作業)


  4. files/example.com.ymlを追加/更新してRoute53でレコードを登録(ファイル更新だけ手作業

こうしました。結局全部手作業があるのは変わっていないのですが、


  • 2,4の手作業枠のうち、AWSコンソールのログインなどがバッサリ消えた

  • コード化されたので、確認するためにAWSコンソールに行く必要もなくなった

という恩恵にあずかることができます。


オチ

というのを実装したものの、まだ実運用に至っていないので、

既存ドメインを持ってきたり、啓蒙活動をちょこちょこしないといけないですね。