GitHub Actions + CircleCI でDocker HubへDockerイメージを登録、定期的にre-buildする

GitHub Actions + CircleCIを利用してDocker HubへDockerイメージを登録、定期的にrebuildする方法を解説します。

実際に動いているサンプルコードは[こちら](https://github.com/docker-hub-tm/ansible-test)で確認出来ます。

## DockerイメージのビルドのCIを行う

CI用途のためリポジトリにpushがあったら全てのブランチでCIが実行されるように設定します。

<img width="1376" alt="スクリーンショット 2020-05-10 14.53.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/24ec359a-59a5-340d-1394-6e2299667c8e.png">



### 利用するAction

 [Build and push Docker images · Actions · GitHub Marketplace](https://github.com/marketplace/actions/build-and-push-docker-images)
 DockerfileからDockerイメージのビルドとDocker HubへDockerイメージの登録を行うAction（**Docker Hub公式**）

[Hadolint Action · Actions · GitHub Marketplace](https://github.com/marketplace/actions/hadolint-action)
Hadolint[^1]（Dockerfileのlint処理）を実行するAction

[ibiqlik/action-yamllint: GitHub Action - Yaml Lint](https://github.com/ibiqlik/action-yamllint)
yamllint[^2]を実行するAction

[Find Trailing Whitespace · Actions · GitHub Marketplace](https://github.com/marketplace/actions/find-trailing-whitespace)
末尾の空白を検知するAction

### GitHub Actionsの設定

「**Setting**」→「**Secrets**」→「**Add a new secret**」の順にクリックし「**DOCKER_UERNAME**」にDocker Hubのログインユーザー名、「**DOCKER_PASSWORD**」にDocker Hubのログインパスワードにを設定します。

| ラベル名 | 値 | 説明 |
|:-:|:-:|:-:|
|  Name | DOCKER_UERNAME  |  Docker Hubのログインユーザー名 |
|  Value | DOCKER_PASSWORD  |  Docker Hubのログインパスワード |

<img width="1440" alt="スクリーンショット 2020-05-10 1.05.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/fc6d8ba1-001f-832d-0860-b835f2099e04.png">

### ディレクトリ構成

それぞれのActionファイルを別々に配置する事でCIが並列に実行されます。

```bash
├── .circleci
│   └── config.yml
├── .git/
├── .github
│   └── workflows
│       ├── docker-build.yml
│       ├── hadolint.yml
│       ├── trailing-whitespace.yml
│       └── yamllint.yml
├── .yamllint
├── Dockerfile
└── README.md
```

Matrix build利用時

```bash
├── .circleci
│   └── config.yml
├── .git/
├── .github
│   └── workflows
│       ├── docker-build.yml
│       ├── hadolint.yml
│       ├── trailing-whitespace.yml
│       └── yamllint.yml
├── .yamllint
├── README.md
├── centos7
│   └── Dockerfile
└── centos8
    └── Dockerfile
```

### GitHub Actionsを実行するファイル


DockerイメージをDockerfileからビルド、Docker Hubへイメージを登録する処理部分

```yaml:.github/workflows/docker-build.yml
---
name: docker build
on: push

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Register docker image
        uses: docker/build-push-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
          repository: tomonorimatsumura/ansible-test
          tags: latest
```

Job matrixを利用した場合[^3]

```yaml:.github/workflows/docker-build.yml
---
name: docker build
on: push

jobs:
  check:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        base-image: [centos7, centos8]
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Register docker image
        uses: docker/build-push-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
          repository: tomonorimatsumura/ansible-test
          dockerfile: ${{ matrix.base-image }}/Dockerfile
          tags: ${{ matrix.base-image }}-latest
```


Hadolintを実行しDockerfileの妥当性を調べる処理部分[^4]

```yaml:.github/workflows/hadolint.yml
---
name: hadolint
on: push

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Dockerfile lint
        uses: brpaz/hadolint-action@eb9b96b
```

Job matrixを利用した場合[^3]

```yaml:.github/workflows/hadolint.yml
---
name: hadolint
on: push

jobs:
  check:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        base-image: [centos7, centos8]
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Dockerfile lint
        uses: brpaz/hadolint-action@eb9b96b
        with:
          dockerfile: ${{ matrix.base-image }}/Dockerfile
```

末尾の空白を調べる処理部分[^4]

```yaml:.github/workflows/trailing-whitespace.yml
---
name: trailing whitespace
on: push

jobs:
  execute:
    name: Find Trailing Whitespace
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: harupy/find-trailing-whitespace@56310d7
```

yamllintを実行しリポジトリ内のYAMLファイルの形式の妥当性を調べる処理部分[^4]

```yaml:.github/workflows/yamllint.yml
---
name: yamllint
on: push

jobs:
  lintAllTheThings:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: yaml-lint
        uses: ibiqlik/action-yamllint@665205c
        with:
          config_file: .yamllint
```

```yaml:.yamllint
---
rules:
  braces: enable
  brackets: enable
  colons: enable
  commas: enable
  comments:
    level: error
  comments-indentation:
    level: error
  document-end: disable
  document-start:
    level: error
  empty-lines: enable
  empty-values: disable
  hyphens: enable
  indentation: enable
  key-duplicates: enable
  key-ordering: disable
  line-length: enable
  new-line-at-end-of-file: enable
  new-lines: enable
  octal-values: disable
  quoted-strings: disable
  trailing-spaces: enable
  truthy: disable
---
```


## Dockerイメージを定期ビルドする

GitHub Actionsの制限で定期実行機能（いわゆるcron）は **デフォルトブランチでしか動作しません**[^5]。 ブランチ戦略に大きな制約が課せられてしまうため定期ビルドにはCircleCIを利用します。 **CircleCIのcronはどのブランチでも実行されます**。

<img width="1065" alt="スクリーンショット 2020-05-10 14.49.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/a9ed779e-bc68-2272-aa50-4ca4e7d5ad5d.png">



### CircleCIの設定

管理画面から「**Project settings**」→「**Environment Variables**」→「**Add Variables**」の順にクリックしDocker Hubのユーザー名、ログインパスワードを設定します。

| ラベル名 | 値 | 説明 |
|:-:|:-:|:-:|
|  Name | DOCKER_LOGIN  |  Docker Hubのログインユーザー名 |
|  Value | DOCKER_PASSWORD  |  Docker Hubのログインパスワード |

<img width="1440" alt="スクリーンショット 2020-05-10 13.17.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/aaa9427b-6ab7-5a33-11a3-b151846ed80d.png">
<img width="1440" alt="スクリーンショット 2020-05-10 14.01.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/70d9a226-3a53-e354-896f-a00748b73229.png">

<img width="1440" alt="スクリーンショット 2020-05-10 13.21.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/f42138e9-214a-816b-d191-6eeb41ac45ba.png">


### Orbを利用する

[CircleCI Orb Registry - circleci/docker](https://circleci.com/orbs/registry/orb/circleci/docker)（CircleCI公式Orb）

### Dockerイメージを定期ビルドするCircleCIのconfig.yml

```yaml:.circleci/config.yml
jobs:
  build-and-push:
    executor: docker/docker
    steps:
      - setup_remote_docker
      - checkout
      - docker/check
      - docker/build:
          # Docker HubのRepositoryを設定する
          image: tomonorimatsumura/ansible-test
　　　　　　# 未設定だとCircleCIがランダムな英数文字列を付与する
          tag: latest
      - docker/push:
　　　　　　# Docker HubのRepositoryを設定する
          image: tomonorimatsumura/ansible-test
          # 未設定だとCircleCIがランダムな英数文字列を付与する
          tag: latest

orbs:
  docker: circleci/docker@1.2.1

version: 2.1
workflows:
  version: 2
  build:
    triggers:
      - schedule:
          # 日本時間で毎日夜９時に実行される
          cron: "0 12 * * *"
          filters:
            branches:
              only:
                # masterブランチでのみcronが実行される
                - master
    jobs:
      - build-and-push
```

matrix jobsを利用した場合[^6]

```yaml:.circleci/config.yml
---
jobs:
  build-and-push:
    executor: docker/docker
    parameters:
      base-image:
        type: string
    steps:
      - setup_remote_docker
      - checkout
      - docker/check
      - docker/build:
          image: tomonorimatsumura/ansible-test
          path: << parameters.base-image >>
          tag: << parameters.base-image >>-latest
      - docker/push:
          image: tomonorimatsumura/ansible-test
          tag: << parameters.base-image >>-latest

orbs:
  docker: circleci/docker@1.2.1

version: 2.1
workflows:
  version: 2
  build:
    triggers:
      - schedule:
          cron: "0 12 * * *"
          filters:
            branches:
              only:
                - master
    jobs:
      - build-and-push:
          matrix:
            parameters:
              base-image: ['centos7', 'centos8']
```

[^1]: [hadolint/hadolint: Dockerfile linter, validate inline bash, written in Haskell | GitHub](https://github.com/hadolint/hadolint)
[^2]: [adrienverge/yamllint: A linter for YAML files. | GitHub](https://github.com/adrienverge/yamllint)
[^3]: Job matrix　[Workflow syntax for GitHub Actions - GitHub Help](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix)
[^4]: 公式Action以外を利用する場合は必ずコミットSHAを指定する [Github Actions 4ヶ月使ってみてわかったことまとめ - Qiita](https://qiita.com/bigwheel/items/2ab7deb237122db2fb8d)
[^5]: "Scheduled workflows run on the latest commit on the default or base branch."  [Workflow syntax for GitHub Actions - GitHub Help](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#onschedule)
[^6]: [Matrix jobs - Matrix builds | CircleCI](https://circleci.com/blog/circleci-matrix-jobs/)