GitHub Actions 上でに用意した LXD 上で ansible CI を導入する自作ツール「ansible-ci-module」の紹介

# 1. 概要

GitHub Actions 上で LXD/LXC 環境を用意できることを説明しています.

LXD/LXC 上で ansible role の CI を導入するツール「[ansible-ci-module](https://github.com/link-u/ansible-ci-module)」を紹介しています.

ansible-ci-module の使い方について, 「ローカル環境で使う場合」と 「GitHub Actions 上で使う場合」の2パターンに分けて説明しています.

「GitHub Actions 上で使う場合」について, デモ用の role のリポジトリを用意し, そこに ansible-ci-module を導入する流れを説明しています.

<br>

# 2. 背景

## 2.1. ansible CI を導入する意義

ansible は仮想マシンに様々なミドルウェアのインストールや設定の手順書をコードとして記述できるツールです. 手順書通りに実行すれば基本的にミドルウェアは想定通りに動作しなければいけません.

そのためには ansible の動作確認を実行する必要があるのですが, 人間が手動では常に同じように動作確認できるとは限りません. ansible の流し込み自体も非常に時間がかかるため, コードを改変するたびに複数の環境やシナリオで動作確認確認をするのは非常に時間を消費する上にミスも生じやすくなります.

CI サービスを用いることによりこれらの手間を無くすことができ, 更には他者に CI 結果を見せることで動作確認が行き届いている ansible であることを簡単に説明できるようになります.

## 2.2. ansible-ci-module というリポジトリにする意味

ansible CI 用のコードは大体の role で同じようなコードになっていることが多いです. それならば一つのリポジトリとして管理してしまい, サブモジュールとして扱えるようにしたいというのがきっかけです.

## 2.3. docker ではなく LXD を用いる理由

ansible CI の多くの例では docker コンテナをベースとして ansible の流し込みや動作確認をしています.

しかしながら ansible の流し込み先は仮想マシンや物理マシンであることが多いのに対して, docker はアプリケーションコンテナであるため必ずしも想定した CI ができるとは限りません (例えば docker コンテナでは systemctl が使えない等の制限がある). 

この問題に対するアプローチとして, システムコンテナである LXD/LXC を用いることができます. システムコンテナであるため仮想マシンとほとんど同等の扱いができます.

また, LXD/LXC は仮想マシンではないため GitHub Actions 上で動作可能であり, Travis CI などの別サービスに頼る必要もなくなります.

<br>

# 3. ansible-ci-module の紹介

まずは ansible CI 用のツールである「[ansible-ci-module](https://github.com/link-u/ansible-ci-module)」を紹介します.

このツールは CI を導入したい ansible role のリポジトリに対して, このリポジトリをサブモジュールとして追加することでインフラ CI を提供できます.
主に以下のことができるようになります.

* ansible-lint による linting (デフォルト)
* ansible role の流し込み CI (デフォルト)
* testinfra によるテスト (設定の必要あり)

構成要素は主に以下のとおりです.

* ansible
* ansible-lint (ansible コードに対して linting する)
* molecule (ansible の CI 用フレームワーク)
* molecule-lxd (LXD/LXC を molecule から操作するためのドライバ)
* tox (python モジュールの複数バージョンのテストをサポートするテストツール)

基本的には [README.md](https://github.com/link-u/ansible-ci-module/blob/master/README.md) に使い方をまとめていますが, こちらの記事でも基本的な部分は触れておこうかと思います.

<br>

# 4. ローカル環境編

この記事では本来 GitHub Actions 上で CI を走らせることを目標にしているためローカル環境というものは必要ないのですが, 本記事のツールを初めて利用する場合はどこから手を付けて行けばよいかわからないことが多いため, まずはローカルに動作環境を用意することをおすすめします.

## 4.1. インストール

### 4.1.1. 前提

まずは前提として必要なツールをインストールします.

* python (>= 3.6)
* LXD/LXC 環境の用意
* tox のインストール (pip などでインストール)

```
## 必要な deb パッケージのインストール
$ sudo apt update
$ sudo apt install lxd lxd-tools qemu-block-extra python3 python3-pip python3-setuptools

## LXD の初期設定
$ sudo lxd init --auto
$ sudo usermod -aG lxd $(whoami)

## tox のインストール
$ pip3 install tox
```

LXD/LXC についてはその必要性に何度か触れたので割愛します.

tox は python ライブラリの複数バージョンのテストをサポートするテストツールです.
例えば

* ansible のバージョンごとに CI をしたい (ansible 2.9, 2.10 など).
* OS のバージョンごとに CI をしたい (ubuntu18.04, ubuntu20.04 など).

といったケースで tox を使えばマトリックスジョブを構成しやすくするために使います.

### 4.1.2. ansible-ci-module のインストール

このリポジトリをサブモジュールとして role ディレクトリに追加します.

```
$ git submodule add https://github.com/link-u/ansible-ci-module.git
```

role に追加した後は以下のようなディレクトリ構成となります.

```
<role directory>/
├── defaults/...
├── tasks/...
├── ...
│
└── ansible-ci-module/  ## サブモジュールとして追加した ansible-ci-module リポジトリ
```

<br>

## 4.2. 使い方

本記事では [README](https://github.com/link-u/ansible-ci-module#5-%E4%BD%BF%E3%81%84%E6%96%B9-%E5%9F%BA%E6%9C%AC%E7%B7%A8) にある「5. 使い方 (基本編)」を紹介します.

ここまで設定していれば, 特に何もカスタマイズしなくても以下の CI ができるようになります.

* ansible-lint による linting
* LXD コンテナへの ansible の流し込み CI

### 4.2.1. tox の設定ファイルについて

tox の設定は予め `<role directory>/ansible-ci-module/tox.ini` にあります.
予め用意している `tox.ini` は以下の通りです.

```ini
$ cat ansible-ci-module/tox.ini
[tox]
minversion = 3.6
envlist = {ubuntu1804,ubuntu2004}-ansible{28,29}

[testenv]
basepython = python3
deps =
    wheel
    molecule
    molecule-lxd
    testinfra
    ansible28: ansible>=2.8,<2.9
    ansible29: ansible>=2.9,<2.10
    ansible-lint

setenv =
    ubuntu1804: MOLECULE_DISTRO = bionic
    ubuntu2004: MOLECULE_DISTRO = focal

    ansible28: ANSIBLE_VER = ansible28
    ansible29: ANSIBLE_VER = ansible29

passenv =
    LANG
    HOME
    MOLECULE_DISTRO
    ANSIBLE_VER
    MOLECULE_SCENARIO

commands =
    python3 ./scripts/molecule_test.py
```

### 4.2.2. tox コマンドの使い方

本モジュールで利用する tox コマンドの基本的な使い方を説明します.

**CI するマトリックスジョブの確認方法**

```
$ tox -c ansible-ci-module/tox.ini --listenvs
ubuntu1804-ansible28
ubuntu1804-ansible29
ubuntu2004-ansible28
ubuntu2004-ansible29
```

**CI を実行する方法**

```
## 全パターン CI を実行
$ tox -c ansible-ci-module/tox.ini

## ubuntu2004-ansible29 を指定して CI を実行
$ tox -c ansible-ci-module/tox.ini -e ubuntu2004-ansible29
```

<br>

# 5. GitHub Actions 編

本記事の本命である GitHub Actions 上で CI を実行する方法を紹介します.

## 5.1. インストール

### 5.1.1. ansible-ci-module のインストール

ローカル編と同じく, このリポジトリをサブモジュールとして role ディレクトリに追加します.

```
$ git submodule add https://github.com/link-u/ansible-ci-module.git
```

role に追加した後は以下のようなディレクトリ構成となります.

```
<role directory>/
├── defaults/...
├── tasks/...
├── ...
│
└── ansible-ci-module/  ## サブモジュールとして追加した ansible-ci-module リポジトリ
```

### 5.1.2. GitHub Actions 用のワークフローファイルの設置

GitHub Actions を使うには, リポジトリのルート (今回だと `<role directory>`) に `.github/workflows/<ファイル名>.yml` というパスでワークフローファイルを設置します.

ファイル名は何でもよく本記事では ansible-ci.yml というファイル名で保存します.
実はそのまま使えるサンプルが ansible-ci-module サブモジュールに用意されているので, それを指定のディレクトリにコピーします

```
$ cd <role directory>/
$ mkdir -p .github/workflows/
$ cp ansible-ci-module/github_workflow_samples/ansible-ci.yml .github/workflows/
```

設置後は以下のようなディレクトリ構造となります.

```
<role directory>/
├── defaults/...
├── tasks/...
├── ...
│
├── ansible-ci-module/       ## サブモジュールとして追加した ansible-ci-module リポジトリ
└── .github/
    └── workflows/
        └── ansible-ci.yml   ## ワークフローファイル
```

ワークフローファイルの中身はこの様になっています.

```yaml
name: ansible ci

on:
  push:
    branches:
      - "**"
    tags:
      - v*

jobs:
  create-matrix:
    runs-on: ubuntu-20.04
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - name: Install pip3 and tox
        run: |
          sudo apt update
          sudo apt -y install python3 python3-pip python3-setuptools
          sudo pip3 install tox

      - uses: actions/checkout@v2
        with:
          submodules: recursive
          fetch-depth: 0

      - name: Create tox env list
        id: set-matrix
        run: |
          MATRIX=$(python3 ansible-ci-module/scripts/matrix.py)
          echo "::set-output name=matrix::${MATRIX}"

  ansible-ci:
    needs: create-matrix
    runs-on: ubuntu-20.04
    strategy:
      matrix: ${{ fromJson(needs.create-matrix.outputs.matrix) }}
      fail-fast: False
    steps:
      - name: Install LXD and CI tools
        run: |
          sudo apt update
          sudo apt install lxd lxd-tools qemu-block-extra python3 python3-pip python3-setuptools

      - name: Configure LXD
        run: |
          sudo lxd init --auto

      - name: Install tox using python3-pip
        run: |
          sudo pip3 install tox

      - uses: actions/checkout@v2
        with:
          submodules: recursive
          fetch-depth: 0

      - name: ansible-ci
        run: |
          if [ -f ./tox.ini ]; then
            sudo MOLECULE_SCENARIO=${{ matrix.scenario }} tox -e ${{ matrix.tox_env }} -c tox.ini
          else
            sudo MOLECULE_SCENARIO=${{ matrix.scenario }} tox -e ${{ matrix.tox_env }} -c ansible-ci-module/tox.ini
          fi
```

このワークフローでは大きく分けると以下の2つのジョブから構成されています.

* `create-matrix`
* `ansible-ci`

`create-matrix` では `tox --listenvs` によって列挙されるジョブの数だけ GitHub Actions のマトリックスジョブを構成します.

`ansible-ci` では `create-matrix` で構成されたマトリックスジョブを並列に実行します.

## 5.2. 実際に導入する

`Hello, ansible!` と書かれたプレーンテキストファイルをデプロイするだけの demo 用の ansible role を用意しました.

https://github.com/y-hashida/ansible-roles_hello-demo

この role に本記事の手順通りに CI を追加します.

```
## role ディレクトリに移動
cd ansible-roles_hello-demo

## ansible-ci-module をサブモジュールとして追加
$ git submodule add https://github.com/link-u/ansible-ci-module.git

## GitHub Actions のワークフローファイルを用意
$ mkdir -p .github/workflows/
$ cp ansible-ci-module/github_workflow_samples/ansible-ci.yml .github/workflows/
```

GitHub Actions のジョブの状態はこのリンク先で確認できます.
https://github.com/y-hashida/ansible-roles_hello-demo/actions

そのうちの一つとして https://github.com/y-hashida/ansible-roles_hello-demo/actions/runs/410675108 を確認してみます.

先述したように, create-matrix ジョブが先行して走り, ansible-ci ジョブが4個マトリックスとして走っている様子がわかると思います.
![2020-12-09_v2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397341/929c3864-ccad-eea5-cfbf-4d4bad3dea02.png)

また, このようにジョブが走ったログをいつでも確認することができます.
![2020-12-09_v4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397341/f34b1051-7363-1074-2c45-45bd4831ec6a.png)

<br>

# 6. まとめ

GitHub actions 上で LXD/LXC 環境を用意できることを説明しました

また, LXD/LXC 上で ansible role の CI を導入するツール「[ansible-ci-module](https://github.com/link-u/ansible-ci-module)」を紹介し, その使い方について「ローカル環境で使う場合」と 「GitHub Actions 上で使う場合」の2パターンに分けて説明しました.l

最後に「GitHub Actions 上で使う場合」について, デモ用の role のリポジトリを用意しそこに ansible-ci-module を導入する流れを説明しました.

長い説明になってしまいましたが, 「[5.2. 実際に導入する](#52-実際に導入する)」を試しに動かせることを目標にしてもらえると, ansible-ci を簡単に用意できることが実感できると思います.