Ansible AWXでアプリケーションテスト用のMySQLコンテナを作成する

この記事は、[MicroAd (マイクロアド) Advent Calendar 2020 - Qiita](https://qiita.com/advent-calendar/2020/microad)の24日目の記事です。

# はじめに
マイクロアドでは、MySQLの本番スキーマを入れたDockerコンテナを用意し、開発チームのテスト用に使っていただいています。これ自体は、日次実行で最新のスキーマと同期したものが用意できていたのですが、開発案件が複数並列で進む関係上「更新が入る前のスキーマでテストがしたい」という要望が生じてきました。これまでは、要望がある度にMySQL管理者側でコンテナのビルドとプッシュを手動実行していましたが、これを開発者側でも行えるよう、AnsibleとAWXを使ったビルド・プッシュの仕組みを構築してみました。


# Playbook
```YAML
---
- name: Dockerイメージのビルド、プッシュを実行
  hosts: "{{ docker_operation_host }}"
  gather_facts: no
  vars:
    github_repository_name: docker-images-mysqldb
  tasks:
    - name: GitHubのリポジトリを取得
      git:
        repo: git@{{ github_domain }}/{{ github_organization }}/{{ github_repository_name }}
        dest: /opt/{{ github_repository_name }}
        accept_hostkey: true
      no_log: true

    - name: Dockerレジストリにログイン
      docker_login:
        registry_url: "{{ docker_registry_domain }}"
        username: "{{ docker_registry_user }}"
        password: "{{ docker_registry_token }}"
      no_log: true

    - name: ビルド用キャッシュとしてlatestタグのイメージをプル
      docker_image:
        name: "{{ docker_registry_domain }}/{{ github_organization }}/{{ mysql_hostname }}"
        tag: latest
        source: pull

    - name: イメージをビルドし、Dockerレジストリにプッシュ
      docker_image:
        build:
          path: /opt/{{ github_repository_name }}/images/{{ mysql_hostname }}
          cache_from:
            - "{{ docker_registry_domain }}/{{ github_organization }}/{{ mysql_hostname }}:latest"
          args:
            MYSQL_HOSTNAME: "{{ mysql_hostname }}"
            MYSQL_USER: "{{ mysql_docker_user }}"
            MYSQL_PASSWORD: "{{ mysql_docker_password }}"
        name: "{{ docker_registry_domain }}/{{ github_organization }}/{{ mysql_hostname }}"
        tag: "{{ lookup('pipe','date +%Y%m%d') }}"
        push: yes
        source: build
      no_log: true

    - name: プッシュしたイメージをローカルから削除
      docker_image:
        state: absent
        name: "{{ docker_registry_domain }}/{{ github_organization }}/{{ mysql_db_name }}"
        tag: "{{ lookup('pipe','date +%Y%m%d') }}"
```

MySQLのDockerfileはGitHubリポジトリ上で管理しているため、まずDockerイメージをビルドするホストにリポジトリをクローンし、ホストでビルドした後、Dockerレジストリにプッシュしています。
`docker_image` タスクで `tag: "{{ lookup('pipe','date +%Y%m%d') }}"` とすることにより、YYYYMMDD形式での実行日がタグとなってビルド、プッシュされます。

### 変数について
`github_domain` : リポジトリを取得するGitHubのドメイン名
`github_organization` : リポジトリを取得するGitHubのオーガナイゼーション名
`github_repository_name` : 取得するGitHubリポジトリ
`docker_registry_domain` : プッシュするDockerレジストリのドメイン名
`docker_registry_user` : Dockerレジストリにログインするユーザ名
`docker_registry_token` : Dockerレジストリにログインするユーザのトークン
`mysql_hostname` : イメージを作成するMySQLのホスト名
`mysql_docker_user` : MySQLのスキーマを取得するためのユーザ（要、SELECT権限）
`mysql_docker_password` : MySQLユーザのパスワード

これらの変数のうち、Playbook内で与えていないものについてはgroup_vars（秘匿情報はansible-vaultで暗号化）またはAnsible AWXの追加変数で値を付与しています。


# Dockerfile

```
FROM mysql:8.0

ARG MYSQL_HOSTNAME=unknown
ARG MYSQL_USER=unknown
ARG MYSQL_PASSWORD=unknown

RUN apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* && \
    # MySQLのスキーマをインポート
    mysqldump -h${MYSQL_HOSTNAME} -u${MYSQL_USER} -p${MYSQL_PASSWORD} -B xxxx_db --no-data --lock-tables=false --set-gtid-purged=OFF > /docker-entrypoint-initdb.d/dump.sql

# 外部から渡される引数 (docker build時に渡される)
ARG GIT_REVISION=unknown
ARG GIT_ORIGIN=unknown
ARG IMAGE_NAME=unknown
LABEL git-revision=$GIT_REVISION \
      git-origin=$GIT_ORIGIN \
      image-name=$IMAGE_NAME
```

Oracle公式で提供されているMySQLイメージには `/docker-entrypoint-initdb.d/` 配下にあるSQLファイルをビルド時に実行する機能があるため、 mysqldump の `--no-data` オプションでスキーマ情報のみを取得したダンプファイルを作成し、取得した環境と同一のスキーマを持ったDockerイメージを生成します。
Dockerのビルドを行う環境に上記ファイルをクローンし、 `docker_image` タスクで `MYSQL_HOSTNAME` などの引数をAnsibleから渡すことでmysqldumpを実行できるようにしています。


# Ansible AWXでのジョブテンプレート化
上記のPlaybookを使用したジョブテンプレートをAnsible AWX上で作成し、開発チームのユーザにジョブの実行権限を付与することで、ワンクリックのジョブ実行でDockerイメージのプッシュまでが完了するようになっています。
![スクリーンショット 2020-12-24 20.48.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226717/b285333d-be16-3b45-d7e3-82d104a555a0.png)