Docker BuildKitを理解する

## BuildKitとは

- Docker Buildの拡張版
- 最新版(20.10.7)ではデフォルトで有効になっている (デフォルトでDOCKER_BUILDKIT=1の状態になっている)

## BuildKitができること

- ビルドの並列実行
- ビルドキャッシュのインポート/エクスポート
- マルチプラットフォーム対応
etc

## 2つのBuildKit環境

- BuildKitは2つの実行環境がある
  - Docker Engineに内蔵されたBuildKit (デフォルト)
  - buildkitデーモンとして独立した環境のBuildKit
- デフォルトのDocker Engineに内蔵されたBuildKitは一部機能のみしか使えない
- buildkitデーモン版のBuildKitはすべての機能を利用できるがbuildctlというコマンドを使う必要がある
- docker-buildxプラグインを使用すると、docker buildxコマンドからbuildkitデーモンを操作できる # 最新版(20.10.7)ではデフォルトで有効

## ビルダーインスタンス

- BuildKitはbuilderインスタンスというものを起動して、そのインスタンス内部でビルドする仕組みなっている
- builderインスタンスは```docker buildx ls```で確認することができる
- デフォルトではdockerドライバ(Docker内蔵版BuildKitを使う)のビルダーインスタンスが有効になっている
- docker-containerドライバ(buildkitデーモン版BuildKitを使う)のビルダーインスタンスを使うには最初に自分で作成する必要がある

## docker-containerドライバのビルダーインスタンスを作成する

### ビルダーインスタンスの作成

```
% docker buildx create --name docker-container-builder --use
% docker buildx ls
NAME/NODE                   DRIVER/ENDPOINT             STATUS   PLATFORMS
docker-container-builder *  docker-container
  docker-container-builder0 unix:///var/run/docker.sock inactive
desktop-linux               docker
  desktop-linux             desktop-linux               running  linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default                     docker
  default                   default                     running  linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
```

### ビルダーインスタンスの起動 (オプション)

- ビルド時に自動で起動されるので明示的に起動しなくても大丈夫

```
% docker buildx inspect --bootstrap
[+] Building 10.9s (1/1) FINISHED
 => [internal] booting buildkit                                                                                                                                                                                                         10.9s
 => => pulling image moby/buildkit:buildx-stable-1                                                                                                                                                                                       9.7s
 => => creating container buildx_buildkit_docker-container-builder0                                                                                                                                                                      1.1s
Name:   docker-container-builder
Driver: docker-container

Nodes:
Name:      docker-container-builder0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
% docker buildx ls
NAME/NODE                   DRIVER/ENDPOINT             STATUS  PLATFORMS
docker-container-builder *  docker-container
  docker-container-builder0 unix:///var/run/docker.sock running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
desktop-linux               docker
  desktop-linux             desktop-linux               running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default                     docker
  default                   default                     running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
```

## マルチプラットフォーム対応のDockerイメージの作成 

デフォルトではビルド環境と同じプラットフォームのDockerイメージしか作成できないが、
docker-containerドライバのビルダーインスタンスを使うとマルチプラットフォーム環境のDockerイメージを作成することができる。

### サンプルのDockerfile

```dockerfile
FROM alpine:latest
CMD ["arch"]
```

### デフォルトのビルド (シングルプラットフォーム対応)

```
% docker buildx use default
% docker buildx build -t boccifarm/sample-single-platform-image --load .
% docker image inspect boccifarm/sample-single-platform-image | jq -r '.[].Architecture'
amd64
```

### マルチプラットフォーム対応ビルド

現状ではローカルに自アーキテクチャと異なったイメージをロードできないのでDockerHubへプッシュする

```
% docker login
% docker buildx use docker-container-builder
% docker buildx build -t boccifarm/sample-multi-platform-image --platform linux/amd64,linux/arm64 --push .
% docker manifest inspect --verbose boccifarm/sample-multi-platform-image | jq -r '.[].Descriptor.platform'
{
  "architecture": "amd64",
  "os": "linux"
}
{
  "architecture": "arm64",
  "os": "linux"
}
```

### マルチプラットフォーム対応の確認

amd64環境とarm64環境のKubernetes上でマルチプラットファーム対応していることを確認する

```
# amd64のKubernetes
% kubectl run test --restart=Never --image=boccifarm/sample-multi-platform-image
% kubectl logs -f test
x86_64
```

```
# arm64のKubernetes
% kubectl run test --restart=Never --image=boccifarm/sample-multi-platform-image
% kubectl logs -f test
aarch64
```

## 参考
- https://qiita.com/tatsurou313/items/ad86da1bb9e8e570b6fa
- https://roy-n-roy.github.io/Docker/BuildKit/
- https://dev.classmethod.jp/articles/docker-multi-architecture-image-build/