Finch使ってみた - finchコマンドはほぼdockerやdocker-composeコマンドとして利用できた

先日のAWS re:Invent 2022で発表されたFinchを遅ればせながら使ってみたのでその感想文を書いておく。

# Finchとは？

https://github.com/runfinch/finch

Finchはデスクトップ（ローカル）環境でコンテナをビルドして実行するための環境を提供するOSSであり、Docker Desktopの代わりとなり得る言われている。

Finchがデスクトップコンテナ環境を実現するために活用しているツールセットについて下記のような説明がある。

> Finch provides a simple client which is integrated with nerdctl. For the core build/run/push/pull commands, Finch depends upon nerdctl to handle the heavy lifting. It works with containerd for container management, and with BuildKit to handle Open Container Initiative (OCI) image builds. These components are all pulled together and run within a virtual machine managed by Lima.
Source: https://github.com/runfinch/finch

- コアのbuild/run/push/pullコマンドは[nerdctl](https://github.com/containerd/nerdctl)を活用
- コンテナーランタイムには[containerd](https://containerd.io/)を活用
- Open Container Initiative (OCI) イメージビルドに[BuildKit](https://github.com/moby/buildkit)を活用
- 仮想マシン管理に[Lima](https://github.com/lima-vm/lima/)を活用

デスクトップコンテナ環境を実現するために上記ツールセット達がどのように連携しているのかについては記事「[Container Tools, Tips, and Tricks - Issue #3](https://iximiuz.ck.page/posts/container-tools-tips-and-tricks-issue-3)」にとてもわかりやすい絵があるので紹介しておく。

![](https://embed.filekitcdn.com/e/n2qV5N4FkhT9qZg5ExZtDq/pkiWwNvjrHrTixgC9KxkHA/email)
Source: [Container Tools, Tips, and Tricks - Issue #3](https://iximiuz.ck.page/posts/container-tools-tips-and-tricks-issue-3)


なお、いまのところFinchはmacOSのみサポートしており、WindowsやLinuxでは使えない。

また、当方FinchやDocker Desktop以外は使ったことがないが、他にもデスクトップコンテナ環境を実現するツールはいくつかあるのでここに紹介しておく。

- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
- [Rancher Desktop](https://rancherdesktop.io/)
- [Podman Desktop](https://podman-desktop.io/)
- [Colima](https://github.com/abiosoft/colima)
- [Finch](https://github.com/runfinch/finch)

などなど、ご参考までに。

# Finchで試してみたこと

利用環境
- MacBook Pro (OS: Monterey)
- Processor 2.5 GHz Dual-Core Intel Core i7 
- Memory 16 GB

## finchのインストール

まずはbrewコマンドでfinchをインストールする。

```bash
brew install --cask finch
```

## 仮想マシンの起動

Finchの仮想マシン（以下、VM）を利用するため`finch vm init`コマンドで初期設定する必要がある。これは一回だけやればよい。これでVMが起動される。

```bash
finch vm init
```

なお、2回目以降は、VMの起動、停止をそれぞれ下記のコマンドで行う。

```bash
finch vm start
finch vm stop
```

## Docker Hubのコンテナイメージをpullして実行してみる

それではコンテナの実行準備が整ったのでDocker Hubのコンテナイメージを下記の`finch run`コマンドで実行してみる。

サンプルのコンテナイメージとして[docker-supermario](https://hub.docker.com/r/pengbai/docker-supermario/)を利用する。基本的にdockerコマンドでやっていたのと同じオプションで実行可能であることが分かるかと思う。

```bash
finch run -d -p 8600:8080 docker.io/pengbai/docker-supermario:latest
```

コンテナ起動後に`finch ps`コマンドで起動しているコンテナ一覧を確認する。

```
finch ps

CONTAINER ID    IMAGE                                         COMMAND              CREATED           STATUS    PORTS                     NAMES
05a1aa06b301    docker.io/pengbai/docker-supermario:latest    "catalina.sh run"    19 seconds ago    Up        0.0.0.0:8600->8080/tcp    docker-supermario-05a1a
```

ローカルポート8600でアクセスできることがわかるので、ブラウザでlocalhost:8600を開いてみる。

![Screen Shot 2022-12-20 at 12.09.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/b68fae77-23cc-5a21-73e5-cf0c472a7223.png)

無事に期待通りの結果が表示された。

## Dockerfileからイメージをビルドしてみる

次に、`finch build`コマンドでイメージをビルドしてみる。
サンプルとして当方が作った[awsping](https://github.com/yokawasa/awsping)ツールのコンテナイメージをビルドしてみる。

まずはコードをcloneする。

```bash
git clone git@github.com:yokawasa/awsping.git                          
cd awsping                                                             
```

次のように`finch build`コマンドでDockerfileからイメージをビルドする。

```bash                              
finch build -t awsping .
```

最後にビルドしたイメージを次のように`finch run`コマンドで実行してみる。

```bash
finch run awsping
```

以下がコンテナ実行の出力結果。

```
finch run awsping

 1.  [ap-northeast-1]  80.342267ms
 2.  [ap-northeast-2]  120.614401ms
 3.  [ap-southeast-1]  210.190698ms
 4.  [us-west-2]       279.588928ms
 5.  [ap-southeast-2]  279.89593ms
 6.  [us-west-1]       280.23793ms
 7.  [ap-south-1]      328.27209ms
 8.  [ca-central-1]    401.566333ms
 9.  [us-east-2]       407.018351ms
10.  [eu-west-2]       513.608706ms
11.  [eu-west-1]       537.589785ms
12.  [eu-west-3]       539.03179ms
13.  [eu-central-1]    554.777842ms
14.  [eu-north-1]      559.914859ms
```

ここでも、dockerコマンドでやっていたことを同じオプションでfinchコマンドで利用できた。

## Docker Composeを使ってみる

先述のとおりFinchではbuild/run/push/pullなどのコアコマンドの実行に[nerdctl](https://github.com/containerd/nerdctl)を活用している。そして、このnerdctlはDocker Composeにも対応している。したがってfinchコマンドもDocker Composeにも対応しており、`finch compose`コマンドで利用できる。

サンプルとして[awesome-compose](https://github.com/docker/awesome-compose)リポジトリで管理されている[nginx-golang-mysql](https://github.com/docker/awesome-compose/tree/master/nginx-golang-mysql)を利用する。

まずはコードをcloneする。

```
git clone git@github.com:docker/awesome-compose.git
cd awesome-compose/nginx-golang-mysql
```

同ディレクトリにあるDocker composeファイル（`compose.yaml`）の内容は下記の通り。

```yaml:compose.yaml
services:
  backend:
    build:
      context: backend
      target: builder
    secrets:
      - db-password
    depends_on:
      db:
        condition: service_healthy

  db:
    # We use a mariadb image which supports both amd64 & arm64 architecture
    image: mariadb:10-focal
    # If you really want to use MySQL, uncomment the following line
    #image: mysql:8
    command: '--default-authentication-plugin=mysql_native_password'
    restart: always
    healthcheck:
      test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent']
      interval: 3s
      retries: 5
      start_period: 30s
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=example
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 3306

  proxy:
    image: nginx
    volumes:
      - type: bind
        source: ./proxy/nginx.conf
        target: /etc/nginx/conf.d/default.conf
        read_only: true
    ports:
      - 80:80
    depends_on: 
      - backend

volumes:
  db-data:

secrets:
  db-password:
    file: db/password.txt
```


それでは`finch compose up`コマンドでDocker composeファイルを元にコンテナを作成・開始してみる。docker-composeと同じように`-d`オプションでコンテナをバックグラウンドモードで開始できる。

```bash
finch compose up -d
```

`finch compose ps`コマンドで立ち上げたコンテナ一覧をみる。

```
finch compose ps

NAME                            COMMAND                   SERVICE    STATUS     PORTS
nginx-golang-mysql_db_1         "docker-entrypoint.s…"    db         running
nginx-golang-mysql_backend_1    "/code/bin/backend"       backend    running
nginx-golang-mysql_proxy_1      "/docker-entrypoint.…"    proxy      running    0.0.0.0:80->80/tcp
```

ローカルポート80でアクセスできることがわかるので、curlコマンドでlocalhost:80を叩いてみる。

```bash
curl localhost:80

["Blog post #0","Blog post #1","Blog post #2","Blog post #3","Blog post #4"]
```

期待通りの結果が出力された。

さらに、docker-composeコマンドと同じように`finch compose logs`コマンドでログをtailも可能。

```bash
finch compose logs -f

proxy_1   |/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
proxy_1   |/docker-entrypoint.sh: Configuration complete; ready for start up
backend_1 |10.4.4.3 - - [24/Dec/2022:10:35:37 +0000] "GET / HTTP/1.1" 200 77
proxy_1   |10.4.4.1 - - [24/Dec/2022:10:35:37 +0000] "GET / HTTP/1.1" 200 77 "-" "curl/7.79.1" "-"
```

雑ではあるが`finch compose`コマンドの動作を確認できたので最後に、下記コマンドでコンテナを停止・削除する。

```bash
finch compose down -v

INFO[0000] Removing container nginx-golang-mysql_proxy_1
INFO[0000] Removing container nginx-golang-mysql_backend_1
INFO[0000] Removing container nginx-golang-mysql_db_1
INFO[0001] Removing network nginx-golang-mysql_default
INFO[0001] Removing volume nginx-golang-mysql_db-data
```

:::note warn
注意点: Dockerfile構文の拡張について

今回、最初に[awesome-composeリポジトリ](https://github.com/docker/awesome-compose)にある[nginx-golang-mysql](https://github.com/docker/awesome-compose/tree/master/nginx-golang-mysql)をcloneして`finch compose up -d`を実行したら下記のようなエラーが出力された。

```
Dockerfile:1
--------------------
   1 | >>> # syntax=docker/dockerfile:1.4
   2 |     FROM --platform=$BUILDPLATFORM golang:1.18-alpine AS builder
   3 |
--------------------
error: failed to solve: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/tmp/buildkit-metadata3881195025" to rootfs at "/run/config/buildkit/metadata": mount /tmp/buildkit-metadata3881195025:/run/config/buildkit/metadata (via /proc/self/fd/6), flags: 0x1021: operation not permitted: unknown
FATA[0002] unrecognized image format
FATA[0003] error while building image nginx-golang-mysql_backend: exit status 1
FATA[0003] exit status 1
```

調べてみると、どうやらDockerfileの下記のシンタックスが悪さをしていることが分かったので、この一行を削除して再度`finch compose up -d`を実行したら問題なくコンテナの作成・開始が完了した。

```
# syntax=docker/dockerfile:1.4
```

記事「[Dockerfile v1.4.0 以降のヒアドキュメント機能を使うときにハマったこと](https://qiita.com/yokra9/items/8eec13c83e4c59eb0f8e)」によると、この`# syntax=docker/dockerfile:1.4`はDockerfileの拡張構文であり、BuildKitを有効にしたときにこの構文のあるDockerfileをビルドすると、`docker/dockerfile:1.4`イメージがBuildKitフロントエンドとして使用され、Dockerfileはv1.4の文法で読み込まれるようである。

以上のことから`finch compose`ではこの構文はサポートされていないようなのでご注意ください。
:::

## CPUアーキテクチャを指定する

Finchは`—platform`オプションでCPUアーキテクチャを指摘できる。たとえばApple Silicom M1システムにおいて、x86-64 CPUアーキテクチャ（`ard64`）でコンテナを実行する場合は次のように次のように`—platform`オプションで`amd64`を指定する。

```bash
finch run --rm --platform=amd64 public.ecr.aws/amazonlinux/amazonlinux uname -ms
```

## Finchの設定

Finchの設定ファイルは`${HOME}/.finch/finch.yaml`となっており、最初の起動でこのファイルが作成される。

```bash
cat  ~/.finch/finch.yaml

cpus: 2
memory: 4GiB
```

2022/12時点でcpuとmemoryだけが設定可能となっている。設定ファイルについて詳細は[こちら](https://github.com/runfinch/finch#configuration)を参照ください。


# 感想

今回簡単ではあるがfinchコマンドを使ってみて、ほぼdockerやdocker-composeコマンドとして利用できることが分かった。また、細かくは計測していないが体感的にDocker Desktopと比べ軽く占有メモリもかなり少なくなった印象がある。

一方、Finchリポジトリの[issue一覧](https://github.com/runfinch/finch/issues)をみるかぎり、Docker Desktopと比べて利用できないコマンドがあったり、安定性の面などでいろいろ問題があるように見える。まだmacOSしかサポートされていないということもある。当方の利用ユースケース的にはFinchは十分Docker Desktopの代替になり得るのだが、一般的にこれをDocker Desktop代替というには時期尚早であるとは思う。

今後の進化に期待しよう。