Cloud Native Buildpacksのextension完全に理解した

Cloud Native Buildpacksでextensionを諦めつつrun-imageを拡張する (2023-12-31) | あーありがち の続き。

確認環境

2024-01-28 時点

  • macOS ( arm64 )
  • colima 0.6.7
  • Docker
    • Client 25.0.0
    • Server 24.0.7
  • pack 0.32.1 および main ブランチ HEAD
  • gcr.io/buildpacks/builder:google-22 Builder
  • Google Cloud Run

で確認。

この時点の pack CLI が suggest してくる trusted builder はどうも –extension オプションを受け取って意図通りに動作することはなさそうだった。

やりたかったこと / 実現したこと

Cloud Native Buildpacks の build image, run image に用意されていないパッケージを独自に追加し、それを利用する rubygems をインストールし、クラウドインフラ(Cloud Run)上で実行する。

image の拡張の部分は具体的には

  • build image に対しては pack コマンドの –pre-buildpack オプションと –extension オプションを用いてパッケージの追加を行う
  • run image に対しては 前回 同様、事前に build しておいた image を –run-image で与える

方法になった。この辺り何をしたかは 成果物 の sh script にまとめてあるので参考になれば幸い。

目指さなかったこと

オリジナル Builder の作成。

分かったこと

ゼロから Builder を作らなくてもなんとか既存の image を拡張して必要な機能を追加することができる。

一方でそのためにはある程度以上の Docker の知識と Cloud Native Buildpacks の知識が必要になる

特に

  • Dockerfile を自分で書ける
  • Builder, Buildpack, Extension に対する理解が整理できている
  • docker build 中の USER と Cloud Native Buildpacks の USER について理解できている

辺りは必須になる。「Dockerfile を書かずに Docker image を作れる」という謳い文句からは急に遠くなり、正直言うと少々ハードルが高い気がする。Buildpack という言葉がすでに長い歴史を持ち、調べ方によっては正しい情報に辿り着くことも難しい。

こうなると逆にゼロから Builder を作る経験はあった方がよいかもしれない1し、むしろ Docker と配布 Docker image に対する知識、multi-stage build に対する知識があるなら素直に自分で Dockerfile を書いた方が楽かもしれない。

Cloud Native Buildpacksを導入しやすいパターン

  • development 環境の再現に際し docker compose を前提としない
    • ツールのインストール、複数バージョンの切り替え以外については Procfile でプロセスを管理すれば問題ない
  • 開発開始時に最終的な実行環境に必要な Docker 周りの準備を決めきっておく必要がない
    • 特殊な拡張が必要かどうかの決断は先送りしてよい

いわゆる普通の Web アプリだと比較的この構成に近い気がする。どうしても開発機の OS が混ざるのでコンテナに閉じ込めておきたいということでなければ、それほど導入は苦しくないように思う。

extensionを利用してまで既存のbuilderを利用するメリット

  • 開発者がインフラに詳しくなくてもまずはそのまま動くものを作ってもらうことができる
  • docker image の選定などは先送りにできる
  • ある程度以上の期間使うようになると Docker image のメンテナンスはそれなりに面倒になるので、クラウドベンダーの準備してくれる Docker image の上に乗れるのは、長期的には考えることが減って助かる

ハマったこと / 注意点

  • project.toml の schema-version をちゃんと “0.2” にしないと exclude は機能しない。schema-version は optional なので無指定でもエラーにならない(これは正しい動作)が、0.1 で対応していない exclude を指定してもやはりエラーにならない
  • 今のところ自分が動作を確認できた Builder は gcr.io/buildpacks/builder:google-22 のみ。例えば同じ手法で heroku/builder:22 に extension でパッケージを追加することはできなかった。また今後対応 builder が増えるのかどうなのかもよく分からない。(そういうプランを見つけることはできなかった。)
  • Google のリファレンス Configure your build and run images  |  Buildpacks  |  Google Cloud によると builder image はもっとカジュアルに Dockerfile で拡張できそうに見えるのだが、実際に試すとできあがった builder image は “could not parse reference” と言われて使えない

考えると schema-version に限らず、Buildpack API などのバージョン情報も意味がよく分からない。どのバージョンにどのような schema 情報が紐づくのか、分かりやすい資料がない。これはぜひ改善してほしい。

おまけ - Cloud Native Buildpacksのextensionとは

Cloud Foundry / Heroku 時代の buildpack と違い、登場人物が多くてややこしいが、公式ドキュメントのサンプルのコードを読み、実際に動かしてみての自分の理解は、こんな感じになった。

ちょっと分かりにくいが、

  • Extension は image に何らかの機能、役割を provide するもの
  • Extension も Buildpack のように detect, generate というフェーズで image に改変を加える
    • この際、公式ドキュメントとサンプルコードを読むに build image にも run image にも改変を加えられそうだったのだが、自分が試したところ build image にしか上手く改変を加えることはできなかった
    • run image は 前回と同じ手法 を採用することにした
  • ただし、provide したものを require する Buildpack が必要
  • Buildpack は本来 Builder image に含まれるものだが、pack コマンド 0.32.1 には –pre-buildpack / –post-buildpack というオプションで image 化していない生ファイルの Buildpack を追加することができる
    • 今回は rubygems のインストール前に必要なパッケージを追加したいので –pre-buildpack を利用した

成果物

wtnabe/example-cnbp-builder-extension-and-extended-run-image

参考

  1. 自分は Heroku / Cloud Foundry 時代の古い buildpack の知識を援用してごまかした。 

More