GitLab CIにて、kanikoでDockerイメージの更新をする

# 1. はじめに
- 過去に、「[GitLab CI環境でsphinx文書をビルドしてGitLab Pagesにデプロイする](https://qiita.com/tomoten/items/ee56e91abf5be3121699)」にてSphinx文書をCIで自動作成するものを作成しましたが、Sphinx文書のビルドで使用するコンテナイメージの更新は手動で実施していました。
- そのため、今回はそのあたりをGoogle謹製のkanikoを用いてCI内でイメージビルドして登録→Sphinx文書のビルドというようにjobを修正しました。

※以降の内容は、前提①:「[GitLab CI環境でsphinx文書をビルドしてGitLab Pagesにデプロイする](https://qiita.com/tomoten/items/ee56e91abf5be3121699)」の構築内容をベースに修正を加えたものとなります。<br>
![](https://camo.qiitausercontent.com/5b44ee0ce12179c2a71d237b855249f358db3cb1/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3632343235392f30643761333163652d613363612d303536382d373965372d3736373862303061363334652e706e67)
↑　全体像(前提①より)



# 2. 参考ページ
- [GitLab CI で kaniko を使って Docker Image をビルドしてみる](https://qiita.com/Aruneko/items/7d4474444ff92b76aa88)
  - 今回のkanikoの使い方や設定の仕方を参考にしました。
- [Building images with kaniko and GitLab CI/CD](https://labs.etsi.org/rep/help/ci/docker/using_kaniko.md)
  - 認証が通らないときにこのサイトのHelpを参照しました。
- [gitlabCIでhttp通信による自動デプロイをしようとしたらハマった話](kenhttps://docs.gitlab.com/ee/user/packages/container_registry/)
  - デプロイトークンを作成するときに参照しました。
- [GitLab CI/CD Variables を翻訳しました。](https://qiita.com/ynott/items/4c5085b4cd6221bb71c5)
  - GitLab CI中に使える環境変数の参照として活用させていただきました。

# 3. 準備
## 3.1. プライベートレジストリの有効化
※「[GitLab CI環境でsphinx文書をビルドしてGitLab Pagesにデプロイする](https://qiita.com/tomoten/items/ee56e91abf5be3121699)」では、GitLabのコンテナレジストリをパブリックレジストリで使用していたため、プライベートレジストリ化します。<br>

1. ブラウザで「`https://gitlab-ce.com:8443/developer/study-page/`」にアクセスします。（[前提①](https://qiita.com/tomoten/items/ee56e91abf5be3121699)より）
  - Maintainer権限のあるユーザでログインする必要があります。
  - この部分は、適宜個々の環境で読み替えてください。
1. 「Settings」→「General」から設定情報（Visibility, project features, permissions）を開きます。
1. 「Container registry」を有効化して、保存します。
1. 「Packages & Registry」にContainer Registryが有効になります。
1. GitLabとつながるサーバ（ココではホストのUbuntuとする）にて、以下のような操作をしてdockerを操作してコンテナイメージをpushします。

```bash
# GitLab コンテナレジストにログインする。
$ docker login gitlab-ce.com:25000
# publicレジストリに格納されるコンテナイメージをpullする
$ docker image pull gitlab-ce.com:25000/public-group/docker-registry/sphinx-rtd-container:1.0
# privateレジストリ向けにtag設定する
$ docker image tag gitlab-ce.com:25000/public-group/docker-registry/sphinx-rtd-container:1.0 gitlab-ce.com:25000/developer/study-page/sphinx-rtd-container:1.0
# privateレジストリへpushする
$ docker image push gitlab-ce.com:25000/developer/study-page/sphinx-rtd-container:1.0
```

![private-registry-set.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624259/4df5640e-99db-682b-aa8c-96fe2624039f.png)

## 3.2. Dockerfileの登録
- GitLab CIでイメージビルドするために必要なDockerfileをプロジェクト直下に格納します。
  - [https://github.com/tomoten-umino/sphinx-rtd-container](https://github.com/tomoten-umino/sphinx-rtd-container)にあるDockerfileを登録します。

```Dockerfile
FROM python:3.7-alpine3.13

RUN apk add --update --no-cache \
        make \
        git \
        build-base \
        zlib-dev \
        libjpeg-turbo-dev \
        freetype \
        freetype-dev; \
        pip install sphinx sphinx_rtd_theme myst-parser; \
        apk del build-base

CMD ["/bin/sh"]
```

# 4. kanikoを使ったDockerイメージの更新
## 4.1. .gitlab-ci.ymlの構成
こんな感じです。

```yaml
stages:
  - build
  - deploy

docker-build:
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  stage: build
  tags:
    - sphinx
  only:
    changes:
      - Dockerfile
    refs:
      - master
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$REGISTRY_TOKEN_AUTH\"}}}" > /kaniko/.docker/config.json
    - cat $REGISTRY_CERT >> /kaniko/ssl/certs/ca-certificates.crt
    - TAG=`echo "$CI_COMMIT_SHA" | cut -b -8`
    - /kaniko/executor --context ./ --dockerfile ./Dockerfile --destination $CI_REGISTRY_IMAGE/sphinx-rtd-container:$TAG --destination $CI_REGISTRY_IMAGE/sphinx-rtd-container:latest

pages:
  image: gitlab-ce.com:25000/developer/study-page/sphinx-rtd-container:latest
  stage: deploy
  tags:
    - sphinx
  script:
    - make html
    - mv build/html/* public/
  artifacts:
    paths:
      - public
  only:
    refs:
      - master
```

## 4.2. ポイント①：kanikoの設定
基本的には参考ページで上げた「[GitLab CI で kaniko を使って Docker Image をビルドしてみる](https://qiita.com/Aruneko/items/7d4474444ff92b76aa88)」を参照ください。自分で改変している箇所をピックアップします。

- 今回作成したjobは、イメージビルドして、そのイメージをタグ名にcommitハッシュ値(8桁)とlatestで登録する、というものです。
- CI_REGISTRY, CI_COMMIT_SHA, CI_REGISTRY_IMAGEは、GitLab CIのrunner中にはじめから設定されている環境変数です。
- REGISTRY_TOKEN_AUTHとREGISTRY_CERTは、それぞれ自作の変数とファイルとなります。GitLab Container Registryへのloginに必要な情報となります。
  - REGISTRY_CERTは、[前提①](https://qiita.com/tomoten/items/ee56e91abf5be3121699)で作成したオレオレ証明書を設定します。


### REGISTRY_TOKEN_AUTHの作成
- 「Settings」→「Repository」と進み、「Deploy Token」を開きます。
- テキトーに入力して、トークンを発行します。トークンが発行されるとその内容が表示されるのでメモしてください。消すと二度と確認できません。
  - read_repository, read_registry, write_registryはチェックをつけること。
  - 今回は以下のように作成しました。（このGitLabは検証用でローカルに立てたものなのでToken情報をあえて表示しています。）

![Deploy-token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624259/281abc92-b908-f13c-3771-278b58807d34.png)

- Ubuntuのterminalにて、以下のコマンドを実行して、dockerのauth情報を作成します。

```bash
# echo -n ユーザ名:パスワード | base64
$ echo -n sphinx-deploy-token:r59dp-vxfF42G3XrG8Rn | base64
```

### REGISTRY_TOKEN_AUTHとREGISTRY_CERTを自作変数（またはファイル）として登録
※.gitlab-ci.ymlのファイルにこのようにな認証情報は書くべきでないので、maintainer以上の権限がないと設定・確認ができない変数に登録したほうがいいです。

- 「Settings」→「CI/CD」と進み、「Variables」を開く。
- 「Add variable」を押下すると入力画面となるので、必要な情報を入力して変数（またはファイル）を作成します。
  - 値とファイルどちらで作るか明確な区分は無いが、値：1行で終わるもの、ファイル：複数行必要なもの、で使い分けています。
  - REGISTRY_TOKEN_AUTHには、上記で作成したauth情報を値で作成します。
  - REGISTRY_CERTには、前回作成したオレオレ証明書のBegin〜Endまでをコピペしてファイルで作成します（下図）。

![registry-cert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624259/03bf19d4-ca65-adb9-32d1-d6ce26e66e6b.png)

## 4.3. ポイント②：更新タイミングの制御
- stageの設定でイメージビルド用のステージ「build」を作成し、deployよりも前に動作するようにしています。
- また、only - changesを採用し、Dockerfileが更新された時だけjobが実行されるようにしています。
  - これを設定していないと、masterにMergeされるたびにイメージビルドされてしまいます。
  - 下図は、上側がDockerfileが更新されていない場合、下側がDockerfileが更新された場合のjobの稼働状態です。

![only-changes.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624259/e021fa7f-5565-31cb-f7c8-ccdcd4591449.png)


# 5. おわり
- ビルドで用いるDockerイメージは、やはり定期的に更新したいですね。（というか、定期的にブラッシュアップしたくなります。）
- 自分のGitLab環境がオンプレミスの環境で自作したものなので、オレオレ証明書などで巷の情報をいくつか複合しないといけなくて若干ハマりました。
  - ただ、このkanikoとdeployトークンの方法は結構の自動化で応用が効く（気がする）ので、（写経が多いですが）一から自分で組んで勉強になりました。