GitlabContainerRegistryにコンテナを自動Pushする簡単なCI/CDを作ってみた

## あらすじ
今の趣味プロジェクトは諸事情でGitlabを使用しています。[前回](https://qiita.com/Domao/items/d16ee22d19a03712bb9f) Githubでやったように、Pushしたら雑にコンテナを自動作成できるGitlab CIを欲しくなったため作ってみることにしました。

## 今回作ったアクションの仕様
- Gitlabのmainブランチにマージ(push)されたときコンテナを作る
- Gitlab Container Registryに コンテナをPushする
- AMD64 / ARM64 両対応
- バージョン管理はしない (日付時刻をタグにする)

## 実際作ったアクション
### 実装前提条件
- リポジトリの設定から Gitlab Container Registryを有効にする
  - (デフォルトでは有効)
- リポジトリ直下にDockerfileを置く

### アクション本体

```yaml:.gitlab-ci.yml
variables:
  OUTPUT_NAME: my-awesome-server
  PACKAGE_REGISTRY_URL: "$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/generic/$OUTPUT_NAME"

image: debian:11

include:
  - local: .gitlab/ci/create_tag.yml
  - local: .gitlab/ci/release_container.yml

stages:
  - create_tag
  - release_container
```


```yaml:.gitlab/ci/create_tag.yml
create_tag_datetime:
  stage: create_tag
  script:
    - echo "TAG=$(date +'%Y%m%d%H%M%S')" >> tag.env
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  artifacts:
    reports:
      dotenv: tag.env
```

```yaml:.gitlab/ci/release_container.yml
build_container_image:
  stage: release_container
  image: docker:20.10.22-dind
  services:
    - docker:dind
  variables:
    DOCKER_IMAGE_NAME_AND_TAG: "${CI_REGISTRY_IMAGE}:latest"
    DOCKER_CACHE_FROM: "type=registry,ref=${CI_REGISTRY_IMAGE}:latest-buildcache"
    DOCKER_CACHE_TO: "type=registry,ref=${CI_REGISTRY_IMAGE}:latest-buildcache,mode=max"
  script:
    - echo "Building container image"
    - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
    - docker buildx create --use --driver-opt network=host --driver-opt image=moby/buildkit:v0.10.6
    - docker buildx build -f "Dockerfile" -t "${CI_REGISTRY_IMAGE}:${TAG}" -t "${CI_REGISTRY_IMAGE}:latest" --cache-from "${DOCKER_CACHE_FROM}" --cache-to "${DOCKER_CACHE_TO}" --platform linux/arm64,linux/amd64 --push .
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

```

### 簡単な処理の流れ
- `.gitlab-ci.yaml` の CI定義読み取り
  - 共通環境変数(variables)
  - ベースコンテナイメージ(image)
  - 分割したファイルの置き場所(include)
  - 処理順序(stages)
- `.gitlab-ci.yaml`定義の `create_tag` ステージ開始
  - `.gitlab/ci/create_tag.yml`定義の `create_tag_datetime` ジョブ実行
    - rulesに一致しなければ実行しない
    - 日付からコンテナ名に使うタグを作り できあがった変数をdotenvとして次のステージに渡す
- `.gitlab-ci.yaml`定義の `release_container` ステージ開始
  - `.gitlab/ci/release_container.yml`定義の `build_container_image` ジョブ実行
    - rulesに一致しなければ実行しない
    - dind(docker in docker)イメージを立ち上げ
    - Gitlab CIでデフォルトで定義されている環境変数を使い Gitlab container registryにログイン
    - docker buildxの作業コンテナ作成
    - docker buildxを使い コンテナ作成 + Gitlab container registryにPush

### できあがる例
下記画像のようなクールなやつができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/9ce25fb0-510f-b0c8-321e-6528bf9bb8a3.png)


## 感想
Github Actionsとほぼ同じかと思ったら、かなり書き方が違い、設定に難航しました。
Gitlab CIの記事はGithub Actionsより少ないように感じたので、この記事が少しでもお役に立てば幸いです。


## 参考

https://akyriako.medium.com/build-golang-docker-images-with-gitlab-ci-pipelines-2117f8505350

https://www.docker.com/blog/multi-arch-build-what-about-gitlab-ci/

https://blog.aoirint.com/entry/2023/gitlab_ci_docker_build_2023/

https://stackoverflow.com/questions/67570911/gitlab-how-to-include-the-previous-jobs-artifacts-as-release-assets

https://stackoverflow.com/questions/46810829/use-semver-or-a-timestamp-in-gitlab-piplelines

https://gitlab-docs.creationline.com/ee/ci/variables/predefined_variables.html

https://gitlab.com/gitlab-org/release-cli/-/tree/master/

https://gist.github.com/carlallen/781a3e40247cff021a7e77de5fb81a47