業務ではGITリポジトリとしてGitLabを使ってるのですが、ローカルPCでDockerイメージを作成する運用だと

  • ローカルの変更が混じってしまう可能性がある
  • イメージを作成するタイミングがあいまいだし、作成忘れがあったりする

などの問題があり微妙だったので、GitLabにタグ(1.5.2など)をプッシュしたタイミングで、自動的にDockerイメージを作成してGCRにビルドさせることにしました。
少し前に 会社のブログkaniko を使うケースを紹介してくれていたので、やってみることにしました。

GitLabでDockerイメージをビルドする方法の候補

改めてGitLabの ドキュメント を見てみると、以下のように書いてあります。

There are three methods to enable the use of docker build and docker run during jobs, each with their own tradeoffs.
An alternative to using docker build is to use kaniko. This avoids having to execute a runner in privileged mode.

どうやら3つの方法があるけどどれもセキュリティの問題があるので、それを避けるなら kanikoを使う のがおすすめってことのようです。

セキュリティの問題というのは、Dockerコンテナの中でDockerイメージを作成するためには、コンテナ内からホストマシンのDockerデーモンにアクセスできるようにする必要があり、それをできるようにする(privileged containerにする)とコンテナ内からホストマシンの全てのデバイスにアクセスできるようになってしまう、ということみたいです。

kanikoであれば、ホストマシンのDockerデーモンにアクセスする必要がないので安全ということなので、kanikoを利用することにしました。

kanikoとは

  • コンテナまたはKubernetesクラスタ内のDockerfileからDockerイメージを構築するためのツール
  • kanikoはDockerデーモンに依存せず、Dockerfile内の各コマンドを完全にユーザースペースで実行する。これにより、Dockerデーモンを簡単または安全に実行できない環境でコンテナイメージを構築できる
  • kanikoのexecutorはGCRで公開されている

公式ドキュメントのサンプルを確認

サンプルの.gitlab-ci.ymlの内容を確認してみます。

build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags

思った以上にシンプルで、以下の流れで処理されるようです。

  1. Gitのタグがpushされた時に
  2. kanikoのexecutorのイメージ(gcr.io/kaniko-project/executor:debug)をGCRから取得して
  3. /kaniko/.docker フォルダを作って
  4. GitLab ContainerRegistry用の認証情報をconfig.jsonに出力して
  5. /kaniko/executorコマンドで、Dockerイメージを作成してレジストリにpushする
    • --context: ビルド内容を指定。ここでは対象のタグにおけるプロジェクトディレクトリが指定されている
    • --dockerfile: 使用するDockerfileのパスを指定
    • --destination: Dockerイメージ名を指定

$CI_REGISTRYなどの環境変数はどうやら全て 事前定義された環境変数 のようです。
GCRやECRなどのプライベートコンテナレジストリに出力する場合は、認証情報を UIからカスタム変数として設定 して、スクリプトで参照することになりそうです。

実際にやってみる

サンプルでだいたいやり方が分かったので、次は実際にGitLab CIでkanikoを使ってDockerイメージを作成して、GCRにpushしてみようと思います。

GitLab Runnerの設定

実は自分でやったことがないのですが、この辺のドキュメントを見ながらGitLab Runnerを設定する必要があります。ぱらぱらと読んでいくと以下の作業が必要なようでした。

カスタム変数を登録

今回は.gitlab-ci.ymlで利用するため、以下の二つのカスタム変数を登録します。
GitLab管理画面の対象プロジェクトで Settings > CI/CD > Variables > Add variable で登録できます。

  • GCR_CREDENTIAL: GCPサービスアカウントのJSONキーの中身のJSON
  • GCR_REGISTRY_IMAGE: GCR上のDockerイメージ名

.gitlab-ci.ymlを作成

プロジェクト直下に.gitlab-ci.ymlを以下の内容で作成します。サンプルとほぼ同じですが、以下の点を修正しています。

  • カスタム変数GCR_CREDENTIALをjsonファイルに出力する
  • 環境変数GOOGLE_APPLICATION_CREDENTIALSに出力したjsonファイルのパスを設定する
  • GCR上のイメージ名にカスタム変数GCR_REGISTRY_IMAGEを指定する

    stages:
      - build_stage
    
    build:
      stage: build_stage
      image:
        name: gcr.io/kaniko-project/executor:debug
        entrypoint: [""]
      script:
        - echo "$GCR_CREDENTIAL" > $CI_PROJECT_DIR/service_account_key.json
        - export GOOGLE_APPLICATION_CREDENTIALS=$CI_PROJECT_DIR/service_account_key.json
        - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $GCR_REGISTRY_IMAGE:$CI_COMMIT_TAG
      only:
        - tags

Dockerfileの作成

プロジェクト内容を元にDerwentイメージを作成するためのDockerfileをプロジェクト直下に作成します。雑ですが、ubuntuのイメージに、srcディレクトリをコピーするだけの超シンプルな内容にしておきます。

FROM ubuntu:20.04

COPY src ./src/

パイプラインを実行

あとは、Gitの適当なcommitにタグを打って、pushすると自動的にパイプラインが登録・実行されます。実際、思ったよりあっさりと動いてくれました。

さいごに

久々にGitLab CIを触りましたが、kanikoを使ってDockerイメージをビルドする部分はほとんど何も引っかからず、サクッとできました。

実装や設定も非常にシンプルですし、セキュリティの問題もないのでDockerイメージをGitLab CIで作成するならkaniko一択で問題ないかなと思います。