GitLab の private repository を ansible-galaxy collection install する

こんにちは。
初めまして Ansible アドベントカレンダー :a: :sparkles: 

## TL;DR

1.  roles のあるリポジトリ（たとえば `https://gitlab.com/hogehoge/hoge.git`）、に `galaxy.yml` を作成する

    ```yaml:galaxy.yml
    namespace: "hello"
    name: "world"
    version: "1.0.0"
    readme: "README.md"
    <略>
    ```

1.  read_apiとread_repository権限のついた、[gitlab Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)を作る
1.  `~/.netrc` を作る
  
    ```bash:.netrc
    machine gitlab.com
      login yourname@gitlab.com
      password yourpersonalaccesstoken
    ```

1.  playbook があるリポジトリで `requirements.yml` を書く

    ```yaml:requirements.yml
    ---
    collections:
    - name: https://gitlab.com/hogehoge/hoge.git
      type: git
      version: 1.0.0 # あるいは branch 名などでもOK
    ```

1.  `ansible-galaxy collection install -r ./requirements.yml` する
1.  playbook の中で collection の roles を参照する

    ```yaml:playbook.yaml
    ---
    - hosts:
        - some_host
      become: true
      collections:
        - hello.world
      roles:
        - japanese/konnichiwa_sekai
    ```

1.  `ansible playbook ...` すると実行できます :tada: 

## はじめに

我々のチームでは、ansible スクリプトをチーム内で細々と運用管理してきました。
これまでは、ansible で設定が必要な VM も少なく、[公式ベストプラクティスで示される Playbooks のディレクトリ構成](https://docs.ansible.com/ansible/2.8/user_guide/playbooks_best_practices.html#alternative-directory-layout)の1つのリポジトリに全て突っ込んでいて、特に不自由はありませんでした。

ところが、今年に入ってから ansible で設定（もしくはコンテナをデプロイするなど）したい VM 群が急増し、モノリポでの運用が難しくなってきました。これまでの VM とは用途が異なるものが追加され、inventories はきちんと分けたいけど、これまで作ってきた roles は共有したいというシチュエーションがありました。
また、 `ansible-playbook --check` によるいわゆるドライランや、場合によってはデプロイも CICD パイプラインから実施しており、VM 数が増えたことで、パイプラインのコントロールが難しくなったことも、リポジトリを分割したい要因でした。

## やりたいこと

roles内もさまざまなカテゴリーのものが入り乱れていたため、下記の方針を立てました。

- roles 側のリポジトリ
    - roles は再利用性を高め、専用のリポジトリに分ける
        - アプリ開発におけるライブラリ的な位置付け
    - コンテナを操作したりアプリケーションをデプロイするものと、マシンをセットアップする系はリポジトリを分ける
        - メンテナンス頻度や、メンテナそのものが異なる可能性も考慮して
- playbook 側のリポジトリ
    - roles を参照する playbook は、マシンの用途に応じてリポジトリを分ける
    - playbook リポジトリからは、roles リポジトリを git tag で参照できると良い
    - もちろん、CICD パイプラインからも roles リポジトリを参照できないといけない

## やったこと

お待たせしました。本題です。
我々は、開発にGitLabのプライベートリポジトリを使用しているので、GitLab上の Ansible Playbook リポジトリから、別プライベートリポジトリにある、roles を参照しなくてはなりません。
以前、[「gitlab の private repository を go get する」](https://qiita.com/yo_C_ta/items/354c2aeb146cc7be0b40)という記事を書きましたが、やらなければいけないことはほとんど同じでした。

### roles collection リポジトリ設定

まず、既存のモノリポから roles を引き離し、分類して、それぞれ collection として[公式のドキュメント](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections_structure.html)に従って、構成し直しました。

```bash
hoge.git/
├── docs/
├── galaxy.yml
├── README.md
├── roles/
│   ├── role1/
│   ├── role2/
│   └── .../
└── tests/
```

この時、 `galaxy.yml` には[ここにあるパラメータ](https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html)を含めることができます。
例えば、下記のような collection だと、 インストール後に `ansible-galaxy collection list` を実行した際に、 `hello.world 1.0.0` という表示になります。

```yaml
namespace: "hello"
name: "world"
version: "1.0.0"
readme: "README.md"
<略>
```

collection 側の設定はこれで全てです。

### playbook リポジトリ設定

この collection をローカル環境にインストールすることで、playbook 側で参照できるようになります。
プライベートリポジトリにある collection をローカルにインストールするには、認証を行う必要があるのですが、それは `~/.netrc` ファイルを作成することで可能になります。GitLab リポジトリの場合、[gitlab Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)を使用して、下記のようなファイルを作成します。

```bash:.netrc
machine gitlab.com
  login yourname@gitlab.com
  password yourpersonalaccesstoken
```

実際にインストールできるか確かめてみましょう。
例えば下記のコマンドの場合、`git+` で明示的に git リポジトリからのインストールであることを指し、`,` 以降はブランチ名やタグ名が指定できます。他にも[さまざまな形式](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#installing-a-collection-from-a-git-repository-at-the-command-line)がサポートされています。
この例の場合、`https://gitlab.com/hogehoge/hoge.git` という git リポジトリの dev ブランチをインストールしています。

```bash
ansible-galaxy collection install git+https://gitlab.com/hogehoge/hoge.git,dev
```
`hello.world:1.0.0 was installed successfully` というように出力されれば OK です。インストールした collection は `~/.ansible/collections/ansible_collections` に配置されていました。

複数の roles collection をインストールする場合、コマンドを羅列する必要はありません。
playbook　側のリポジトリに、`requirements.yml` という yaml ファイルを作成し、下記のコマンドを実行することで、一括インストールすることができます。

```bash
ansible-galaxy collection install -r ./requirements.yml
```

このとき、 `requirements.yml` には下記のような内容を記述します。先ほどコマンドで指定した内容が記載されます。

```yaml:requirements.yml
---
collections:
- name: https://gitlab.com/hogehoge/hoge.git
  type: git
  version: 1.0.0 # あるいは branch 名などでもOK
```

こちらも先ほどと同様に `hello.world:1.0.0 was installed successfully` というように出力されれば OK です。

最後に、playbook の中で、インストールした roles を使う宣言を書きます。
たとえば下記のように `collections` に先ほどインストールした collection 名を指定し、roles では、collection 側リポジトリの `rols/` ディレクトリ配下のパスを記述します。

```yaml:playbook.yaml
---
- hosts:
    - some_host
  become: true
  collections:
    - hello.world
  roles:
    - japanese/konnichiwa_sekai
```

これで、晴れて collections リポジトリの roles を参照して、playbook が実行できるようになりました :tada:

### おまけ - CICD パイプラインでの collection インストール

GitLab CICDする際に、動的に collection をインストールするには、下記のように、`.gitlab-ci.yml` 内で、`.netrc` ファイルを作成します。
このときの認証情報には、`${CI_JOB_TOKEN}` という runner によって[事前に定義された環境変数](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)が使用できます。

```yaml:.gitlab-ci.yml
ansible-play:
  stage: deploy
  image: your.registry/ansible:4.9.0
  script:
    - |
      cat <<EOL >> $HOME/.netrc
      machine gitlab.com
        login gitlab-ci-token
        password ${CI_JOB_TOKEN}
      EOL
    - ansible-galaxy collection install -r ./requirements.yml
    - ansible-playbook playbook.yml -i inventories/inventory.yml -u user -D
```

## まとめ

これまで、いわゆる Go や Python などのアプリケーションでは、ライブラリの配布方法や、その形態などを考えることが多かったのですが、ansible でもいい感じに再利用可能な package 配布の仕組みを構築することができました :sparkles:
学び :relaxed: