AWSが公開したFinchでコンテナ実行/イメージビルドをする
Back to Topこれは、豆蔵デベロッパーサイトアドベントカレンダー2022第5日目の記事です。
今回はAWSが2022/11/22に公開したコンテナ開発ツールFinchを使ってみた感想を書きたいと思います(ググるとすでに結構な数のブログが出てきますが)。
- Introducing Finch: An Open Source Client for Container Development
 - (邦訳)コンテナ開発用のオープンソースクライアント「Finch」のご紹介
 
Finchはコンテナのビルドから実行までコンテナを利用した開発ワークフローをトータルでサポートするDocker Desktopの代替ツールです。OSSとして公開されており、無償で使うことができます。
現時点では、Mac(Intel/M1アーキテクチャ)のみをサポートしていますが、今後はWindows/Linuxにも対応する予定のようです。
内部的には関連する各種OSSを利用しており、Finchはこれらをオールインワンで管理するツールの位置づけのようです。
- コンテナランタイム: containerd
 - CLI: nerdctl
 - 仮想マシン: Lima
 - イメージビルド: BuildKit
 
なお、現状ではDocker DesktopのダッシュボードのようなGUI[1]やKubernetes拡張機能[2]は提供していません。
Finchをインストールする
#現時点でFinchのインストールは、GitHubリリースページよりパッケージをダウンロードして展開します。
使っているMacのCPUアーキテクチャによってダウンロード対象は異なります。
- Intel: Finch-v<x.x.x>-x86_64.pkg
 - Apple Silicon M1: Finch-v<x.x.x>-aarch64.pkg
 
ここでは、Intel Mac(macOS Monterey)に現時点で最新のv0.1.0をインストールしました。
finch version
> finch version v0.1.0
  
仮想マシンのセットアップ
#まず、コンテナを実行する仮想マシンを初期化して起動します。
Finchの設定は${HOME}/.finch/finch.yamlに格納されます。インストール直後は存在しませんが、何かしらのfinchコマンドを実行すると作成されます。
ここでは以下のようにファイルが作成されていました。
cpus: 3
memory: 8GiB
  
現時点で設定値はこの2つのみです。ここではコンテナランタイムのLima仮想マシンに割り当てるCPUコア/メモリサイズです。
初期値はホストOSの空きスペックを考慮して動的に決定されるようです。もちろん必要に応じて変更可能です。
それでは、まず仮想マシンを初期化・実行します。
finch vm init
> INFO[0000] Initializing and starting Finch virtual machine...
> INFO[0097] Finch virtual machine started successfully
  
少し時間がかかりますが、無事成功しました。
ここでは仮想マシンの初期化とともに開始処理も実行されました。
ここまで実施すると、コンテナの実行やビルドが可能な状態となります。
作成した仮想マシンを停止・削除する場合は以下のコマンドを実行します。
# 停止
finch vm stop
# 削除
finch vm remove
  
停止のみの場合はfinch vm startで再開できます。
コンテナイメージを実行する
#ここではDockerHubに公開されているnginxの公式イメージを実行してみます。
また、Nginxで公開するコンテンツはローカル環境側に配置したものをボリュームとしてマウントします。
# カレントディレクトリのcontents配下に任意のindex.htmlを配置
finch run --name nginx -p 8080:80 -d \
  -v $(pwd)/contents:/usr/share/nginx/html:ro nginx:latest
> docker.io/library/nginx:latest:                                                   resolved       |++++++++++++++++++++++++++++++++++++++| 
> index-sha256:e209ac2f37c70c1e0e9873a5f7231e91dcd83fdf1178d8ed36c2ec09974210ba:    done           |++++++++++++++++++++++++++++++++++++++| 
> manifest-sha256:6ad8394ad31b269b563566998fd80a8f259e8decf16e807f8310ecc10c687385: done           |++++++++++++++++++++++++++++++++++++++| 
> config-sha256:88736fe827391462a4db99252117f136b2b25d1d31719006326a437bb40cb12d:   done           |++++++++++++++++++++++++++++++++++++++| 
> layer-sha256:90cfefba34d7c6a81fe1dfbb4a579998c65ff49092052967f63ddc48f6be85d9:    done           |++++++++++++++++++++++++++++++++++++++| 
> (省略)
> elapsed: 18.3s                                                                    total:  54.2 M (3.0 MiB/s)                                       
> df4f0dc64496cbae501ff5cb5f4542a3410dd36ba808e17bc5bcd9664613a878
  
コンテナの実行方法はDocker CLIとほとんど変わりません。違いはコマンドがdockerではなくfinchに変わったくらいです。
もちろん、それ以外にもDocker CLI同様にimages/ps/start/stop/exec/logs等の利用頻度の高いコマンドもサポートしています。
この辺りはDocker CLI互換のnerdctlを内部的に利用していることからくるのでしょうか。Docker CLIを使っている方であればFinchの使い方で迷うことはなさそうです。
実行中のコンテナを確認するのもいつもの通りです。
finch ps
  
CONTAINER ID    IMAGE                             COMMAND                   CREATED           STATUS    PORTS                   NAMES
df4f0dc64496    docker.io/library/nginx:latest    "/docker-entrypoint.…"    17 minutes ago    Up        0.0.0.0:8080->80/tcp    nginx
上記はローカル環境の8080ポートにポートフォワードしていますので、以下のようにアクセスできます。
curl localhost:8080
  
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Finch</title>
</head>
<body>
  <h1>Hello Finch!!</h1>
</body>
</html>
  
仮想マシンを意識せずにDocker Desktopを使っているのと同じようにlocalhostでコンテナにアクセスできました。
ここではLimaの自動ポートフォワードが活用されているようです。
現在ではM1 Mac(ARMアーキテクチャ)が主流となりつつありますが、筆者のようにIntel Macを使っている開発者もまだ相当数いるでしょう。
Finchでは--platformを指定することで、指定したアーキテクチャ上でコンテナを実行できるようになっています。
以下はARMアーキテクチャでビルドしたコンテナイメージをIntel Macで実行する例です。
# Intelの場合は`amd64`を指定
finch run --name arm-image --platform arm64 -d arm-container-image:v1
  
この指定はビルド(finch build)でも同様で、CPUアーキテクチャにあったイメージを作成できるようになっています。
Composeで複数コンテナを実行する
#Finchでは、内部的に使用しているnerdctlでサポートするDocker Composeにも対応しています。
ここではnerdctlのComposeサンプルとしてGitHubに公開されているものをベースにdocker-compose.ymlを作成しました。
version: '3.7'
services:
  wordpress:
    image: wordpress:5.7
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wordpress:/var/www/html
  db:
    image: mariadb:10.5
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql
volumes:
  wordpress:
  db:
  
シンプルなWordPressとMariaDBのコンテナを起動するDocker Composeのファイルです。
これの起動方法もDocker Composeと同じです。
finch compose up -d
  
初回はコンテナイメージのPULLに時間がかかります。
実行が終わるとブラウザからlocalhost:8080にアクセスすればWordPressのサイトが表示されます。
まとめてコンテナを終了する場合も、Docker Composeを使っていたときと同じです。
finch compose down
  
コンテナイメージをビルド・公開する
#最後に、自作でコンテナイメージを作成してDockerHubにプッシュしてみます。
以前記事作成で使用したGoのHTTPサーバーとDockerfileを使います。
- HTTPサーバー
 
package main
import (
	"fmt"
	"net/http"
)
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello Finch app!!!")
	})
	http.ListenAndServe(":8000", nil)
}
  
- Dockerfile
 
FROM golang:1.16 as builder
WORKDIR /src
COPY main.go /src
RUN CGO_ENABLED=0 GOOS=linux go build -o sample-app main.go
FROM scratch
COPY  /src/sample-app /sample-app
EXPOSE 8000
CMD ["/sample-app"]
  
コンテナイメージのビルドにはマルチステージビルドを使ってイメージサイズを小さくするようにしています。
ビルドもDocker CLIと同じです。
finch build -t sample-app:v1 .
> [+] Building 60.9s (10/10) FINISHED                                                                                                                                                                                                     
> => [internal] load build definition from Dockerfile                                                                                                                                                                               0.2s
> => => transferring dockerfile: 252B                                                                                                                                                                                               0.0s
> => [internal] load .dockerignore                                                                                                                                                                                                  0.2s
> => => transferring context: 2B                                                                                                                                                                                                    0.0s
> => [internal] load metadata for docker.io/library/golang:1.16                                                                                                                                                                     2.3s
> => [builder 1/4] FROM docker.io/library/golang:1.16@sha256:5f6a4662de3efc6d6bb812d02e9de3d8698eea16b8eb7281f03e6f3e8383018e                                                                                                      46.3s
> => => resolve docker.io/library/golang:1.16@sha256:5f6a4662de3efc6d6bb812d02e9de3d8698eea16b8eb7281f03e6f3e8383018e
> (省略)
>  => [builder 2/4] WORKDIR /src                                                                                                                                                                                                     0.1s
>  => [builder 3/4] COPY main.go /src                                                                                                                                                                                                0.1s
>  => [builder 4/4] RUN CGO_ENABLED=0 GOOS=linux go build -o sample-app main.go                                                                                                                                                      9.3s
>  => [stage-1 1/1] COPY --from=builder /src/sample-app /sample-app                                                                                                                                                                  0.1s
>  => exporting to oci image format                                                                                                                                                                                                  2.3s
>  => => exporting layers                                                                                                                                                                                                            1.2s
>  => => exporting manifest sha256:41ce2a3201e448d7ce48c91cf4735490c497e2bd7e268832cdda2cde5dcde94e                                                                                                                                  0.1s
>  => => exporting config sha256:2a21df434ac17596e9217616ee9fcbed79542294365165dd73221e207ae8f4e9                                                                                                                                    0.1s
>  => => sending tarball                                                                                                                                                                                                             0.8s
> unpacking docker.io/library/sample-app:v1 (sha256:41ce2a3201e448d7ce48c91cf4735490c497e2bd7e268832cdda2cde5dcde94e)...
> Loaded image: docker.io/library/sample-app:v1
  
ここでは、BuildKitを使ってイメージのビルドを実行しています。
BuildKitは現在Docker Desktopでもデフォルトで使われるようになっていますので、見慣れた出力の方も多いかと思います。
作成したイメージを確認します。
finch images
> REPOSITORY    TAG       IMAGE ID        CREATED           PLATFORM          SIZE         BLOB SIZE
> sample-app    v1        41ce2a3201e4    9 minutes ago     linux/amd64       5.8 MiB      3.1 MiB
  
次に、このイメージをDockerHubにイメージをプッシュして公開してみます。
DOCKER_HUB_USER_NAME=<docker-hub-user-name>
finch tag sample-app:v1 ${DOCKER_HUB_USER_NAME}/sample-app:v1
finch login # DockerHubにログイン
finch push ${DOCKER_HUB_USER_NAME}/sample-app:v1
> INFO[0000] pushing as a reduced-platform image (application/vnd.docker.distribution.manifest.v2+json, sha256:41ce2a3201e448d7ce48c91cf4735490c497e2bd7e268832cdda2cde5dcde94e) 
> manifest-sha256:41ce2a3201e448d7ce48c91cf4735490c497e2bd7e268832cdda2cde5dcde94e: done           |++++++++++++++++++++++++++++++++++++++| 
> config-sha256:2a21df434ac17596e9217616ee9fcbed79542294365165dd73221e207ae8f4e9:   done           |++++++++++++++++++++++++++++++++++++++| 
> elapsed: 7.0 s                                                                    total:  1.6 Ki (229.0 B/s) 
  
コンテナレジストリへのプッシュも、Docker Desktop利用時と全く変わりませんね。
最後に、ここでDockerHubにプッシュしたイメージをローカル環境のFinchから実行してみます。
# あらかじめローカルイメージを削除しておく
finch rmi ${DOCKER_HUB_USER_NAME}/sample-app:v1
# イメージPULL&実行
finch run --name sample-app -p 8000:8000 -d ${DOCKER_HUB_USER_NAME}/sample-app:v1
> docker.io/xxxxxx/sample-app:v1:                                                   resolved       |++++++++++++++++++++++++++++++++++++++| 
> manifest-sha256:41ce2a3201e448d7ce48c91cf4735490c497e2bd7e268832cdda2cde5dcde94e: exists         |++++++++++++++++++++++++++++++++++++++| 
> config-sha256:2a21df434ac17596e9217616ee9fcbed79542294365165dd73221e207ae8f4e9:   exists         |++++++++++++++++++++++++++++++++++++++| 
> elapsed: 1.5 s                                                                    total:   0.0 B (0.0 B/s)                                         
> c8e647d1a1bde4baf406cdf0297b3fbb95b5ff8813acd9fe226fbba346947227
# APIアクセス
curl localhost:8000
> Hello Finch app!!!
  
いつもと同じワークフローで、イメージのビルドから公開ができていることが分かります。
最後に
#Finchを使ってDocker Desktopでいつもやっていたことをしてみました。ドキュメントが不要なくらいほとんど違和感なく操作できました。
コンテナを使いたいけど、Kubernetesを使うほどではないという場合は利用を検討すると良さそうです(現時点ではMacのみですが)。
AWSがこのようなOSSを公開するのは少し意外な感じがしますが、今後AWSサービスでの活用も視野に入っているのかもしれませんね。
Finch自体まだ公開されたばかりです。現状は内包している各ツールのラッパーのようなイメージです[3]。
今後の機能拡張・エコシステムに期待したいところですね。
とはいえ、Docker DesktopでGUIを使っている方をあまり見かけたことがありませんが。 ↩︎
Kubernetesを使う場合は、minikubeやRancher Desktop等を検討すると良いかと思います。 ↩︎
こちらの記事が参考になります→Finch の内部実装を見てみた。。私もソースコード眺めてみましたが同じ印象でした。 ↩︎

