buildkitを使ってKubernetesクラスタでDockerのコンテナイメージをビルドする方法

# はじめに

こんにちは、(株)日立製作所 研究開発グループ サービスコンピューティング研究部の露木です。

最近のKubernetesの隆盛により，Dockerコンテナの実行環境はとても充実しています。一方で，コンテナイメージのビルド環境はどうでしょうか。開発者が，手元のノートPCの限られた計算リソースをやりくりしながら docker build しているのはよくある話です。Kubernetesクラスタには潤沢な計算リソースがあるのですから，これを利用してビルド環境も効率化してしまいましょう。

# 目指す構成

このような背景から，本記事では

1. [moby/buildkit](https://github.com/moby/buildkit) を既存のKubernetesクラスタにデプロイ
2. 手元のノートPCで開発したDockerfileをKubernetes上でビルド
3. 社内にあるDockerレジストリへ自動的にpushする

までの手順を公開します。この構成では KubernetesからDockerレジストリへコンテナイメージを送信するため，手元のノートPCの計算リソースは一切消費しません。

なお，buildkitと同様にKubernetesでDockerfileをビルドするシステムにはKanikoがあります。 [buildkitはDockerに組み込まれている](https://docs.docker.com/develop/develop-images/build_enhancements/) ため，今回はこちらを利用することにしました。


# 前提環境

さっそくですが，buildkitを使う手順を解説していきます。本記事では，以下の環境はすでにあるものとします。

- buildkitのクライアント側 (手元のノートPC)
    - Ubuntu 18.04
    - Docker 19.03
    - kubectlの利用設定済み

- buildkitのサーバ側 (KubernetesのPod)
    - Buildkit version 0.5.1

- プライベートなDockerレジストリのサーバがある

# 準備

buildkitを使えるように環境を整えていきます。ます，buildkitのクライアント側 (手元のノートPC) に操作用のコマンド `buildctl` をインストールします。

```
wget https://github.com/moby/buildkit/releases/download/v0.5.1/buildkit-v0.5.1.linux-amd64.tar.gz
tar xzf buildkit-v0.5.1.linux-amd64.tar.gz
rm -f buildkit-v0.5.1.linux-amd64.tar.gz
sudo mv ./bin/* /usr/local/bin/
buildctl --version
```

次に，buildkitのサーバ側のDockerイメージを作成します。ここ実行する処理はSSL証明書 `myoriginal.crt` を組み込むのみです (社内のオンプレ環境にあるDockerレジストリを利用したいから)。特別な証明書が必要ない場合は，この手順は不要です。

buildkitのコンテナイメージについて，技術的な詳細は [公式ドキュメント](https://github.com/moby/buildkit/blob/master/docs/rootless.md) を参考にしてください。

```
FROM moby/buildkit:latest

# 証明書を追加
RUN apk update \
&& apk add ca-certificates \
&& rm -rf /var/cache/apk/*
ADD myoriginal.crt /usr/share/ca-certificates/
RUN echo myoriginal.crt >> /etc/ca-certificates.conf
RUN update-ca-certificates
```

上記のDockerfileをビルドし，DockerレジストリへPushしておきます。`<DockerレジストリのURL>` と `<プロジェクト名>` の部分はご自身の環境に合わせて書き換えてください。

```
docker login <DockerレジストリのURL>
docker build -t <DockerレジストリのURL>/<プロジェクト名>/buildkit .
docker push
```

buildkitのサーバをKubernetesへデプロイするための `deployment.yaml` を作成します。
なお，[公式ドキュメント](https://github.com/moby/buildkit/blob/master/docs/rootless.md)にあるyamlとの差分は，image名のみです。

```
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: buildkitd
  name: buildkitd
spec:
  selector:
    matchLabels:
      app: buildkitd
  template:
    metadata:
      labels:
        app: buildkitd
    spec:
      containers:
      - image: <DockerレジストリのURL>/<プロジェクト名>/buildkit
        args:
        - --addr
        - tcp://0.0.0.0:1234
        name: buildkitd
        ports:
        - containerPort: 1234
        securityContext:
          privileged: true
```

buildkitのサーバをKubernetesへデプロイします。

```
kubectl apply -f deployment.yaml
```

以上で準備は終了です。

# 使い方

デプロイしたbuildkitのpod名を調べます。

```
kubectl get pods
```

buildkitのpodに接続できるように，`kubectl port-forward` でportを転送します。もし，tcp (httpではない) でKubernetesに直接接続できるネットワーク環境であれば，この手順は不要です。

```
kubectl port-forward pod/<buildkitのpod名> 1234:1234
```

環境変数でbuildctlコマンドがport転送先を参照するように指定します。

```
export BUILDKIT_HOST=tcp://127.0.0.1:1234
```

buildctlコマンドでコンテナイメージのbuildを実行します。
コマンドオプションがとても長いのでスクリプト化して利用しましょう。

```
buildctl build --output type=image,name=<Dockerレジストリ名>/<プロジェクト名>/<イメージ名>:<タグ>,push=true --frontend=dockerfile.v0 --local context=. --local dockerfile=<ビルドしたいDockerfileのパス>
```

なお，Dockerfile名まで指定したい場合は，こちらのようなコマンドになります: [specify name of dockerfile · Issue #684 · moby/buildkit · GitHub](https://github.com/moby/buildkit/issues/684) 。また，ビルドしたコンテナイメージのpushに失敗することがあるので，buildの最後に `ERROR exporting to image` というエラーが出たら再度実行してみてください。

# まとめ

以上の手順が正しく動作していれば，Dockerレジストリに新しいコンテナイメージが登録されているはずです。本記事で解説したようにbuildkitを使えばコンテナイメージのビルドもKubernetesで実行し，潤沢な計算リソースを利用できる利点があります。

# 補足

今回は利用しませんでしたが， `docker` コマンドからbuildKitを利用可能にする[docker/buildx](https://github.com/docker/buildx)プラグインがあります。将来的にはこちらを利用すれば ，buildctlは不要になると思います。buildkitの動作確認をしていた時期 (2019年7月) には，このプラグインではレジストリへの自動pushする機能  (`--push`オプション) を実行できず，ローカルマシンのストレージ容量を消費するため利用しませんでした。また，調べきれていませんが，環境変数 `BUILDKIT_HOST` に対応する設定が見当たらず，リモートのbuildkitdを指定する方法も2019年7月当時には不明でした。

もし，buildxプラグインを使ってみたい場合は，下記手順を参考にしてください。

まず，[docker/buildx](https://github.com/docker/buildx)プラグインをインストールします。

```
export DOCKER_BUILDKIT=1
docker build --platform=local -o . https://github.com/docker/buildx.git
mkdir -p ~/.docker/cli-plugins/
mv buildx ~/.docker/cli-plugins/docker-buildx
```

動作確認します。

```
 ~ $ docker buildx --help

Usage:  docker buildx COMMAND

Build with BuildKit

Management Commands:
  imagetools  Commands to work on images in registry

Commands:
  bake        Build from a file
  build       Start a build
  create      Create a new builder instance
  inspect     Inspect current builder instance
  ls          List builder instances
  rm          Remove a builder instance
  stop        Stop builder instance
  use         Set the current builder instance
  version     Show buildx version information

Run 'docker buildx COMMAND --help' for more information on a command.
```

`docker build` を `docker buildx build` に差し替えます。

```
docker buildx install
```

`docker build` で `docker buildx build` が実行されることを確認します。

```
$ docker build
"docker buildx build" requires exactly 1 argument.
```

なお，元に戻すときは `docker buildx uninstall` を実行すればOKです。