ko を使用して Go アプリケーションを Cloud Run に迅速にデプロイ
Google Cloud Japan Team
※この投稿は米国時間 2021 年 2 月 17 日に、Google Cloud blog に投稿されたものの抄訳です。
開発者がコンテナを使用して作業することが多くなるに従い、ソースコードからアプリケーションがデプロイされるまでの時間を短縮することの重要性が増しています。コンテナ イメージのビルドをより迅速かつ容易にするため、Google は Cloud Build、ko、Jib、Nixery などのテクノロジーを開発し、クラウドネイティブの Buildpacks のサポートを追加しました。これらのツールのいくつかは、Docker エンジンや Dockerfile を使用せず、ソースコードから直接コンテナ イメージをビルドすることに特にフォーカスしています。
特に Go プログラミング言語を使用すると、ソースコードからコンテナ イメージをビルドすることがはるかに簡単になります。この記事では、Google が開発した「ko」というツールによって、Go で記述されたサービスが Docker build と Docker push よりも高速に Cloud Run にデプロイできるようになる仕組みを、Buildpacks などのその他の方法と比較してご説明します。
ko の仕組み
ko は Google が開発したオープンソース ツールで、Go プログラムからコンテナ イメージをビルドして、それをコンテナ レジストリ(Container Registry や Artifact Registry など)に push するために使用します。ko でジョブを実行する際は Dockerfile を記述する必要はなく、Docker 自体をマシンにインストールする必要さえありません。
ko は go-containerregistry ライブラリから分離されているため、コンテナ レジストリやイメージを扱うことができます。これにはもっともな理由があります。というのは、ko の機能の大部分は、この Go モジュールを使用して実装されているからです。特に注目すべき動作として次のようなものが挙げられます。
コンテナ レジストリからベースイメージをダウンロードする
Go バイナリを静的にコンパイルする
Go バイナリを使用して新しいコンテナ イメージ レイヤを作成する
そのレイヤをベースイメージに追加して、新しいイメージを作成する
新しいイメージをリモートのコンテナ レジストリに push する
ko を使用すると、極めて簡単に Go プログラムからコンテナ イメージをビルドして push することができます。
こちらのコマンドでは、ビルドするアプリケーションを参照するため、公開すべきイメージのレジストリを指定してから Go インポートパス(「go build」コマンドで使用するものと同じ、この場合は現在のディレクトリ)を指定しています。
デフォルトでは、ko コマンドはイメージの Distroless コレクションからのセキュアでリーンなベースイメージ(gcr.io/distroless/static:nonroot イメージ)を使用します。これには、コンテナのアタック サーフェス縮小のため、シェルやその他の実行可能ファイルは含まれません。このベースイメージを使用した場合、結果のコンテナに含まれるのは CA 証明書、タイムゾーン データ、静的にコンパイルされた Go アプリケーション バイナリです。
ko は Kubernetes でも極めてうまく機能します。たとえば、「ko resolve」コマンドと「ko apply」コマンドを使用して YAML マニフェストをハイドレートできます。つまり、ko によって YAML 内の「image:」参照が、ビルドするイメージに自動的に置き換えられるため、kubectl を使用して結果の YAML を Kubernetes クラスタにデプロイすることができます。
Cloud Run での ko の使用
ko の構成可能な性質により、gcloud コマンドライン ツールで ko を使用すると、次のように 1 つのコマンドでイメージをビルドして Cloud Run に push できます。
このコマンドでは、push されたイメージ参照全体を ko が stdout ストリームに出力し、それがシェルによってキャプチャされて、--image フラグを介して gcloud に引数として渡されます。
YAML を使用して宣言的にサービスをデプロイしている場合、Kubernetes と同様に、ko は Cloud Run の YAML マニフェストもハイドレートできます。
こちらのコマンドでは、「ko resolve」は YAML ファイルの「image: …」値の Go インポートパスを置き換えて出力を stdout に送信し、これがパイプをまたいで gcloud に渡されます。gcloud はハイドレートされた YAML を(引数「-」により)stdin から読み取り、サービスを Cloud Run にデプロイします。
これが機能するようにするには、次の構文を使用して YAML ファイルの「image:」フィールドに Go プログラムのインポートパスを挿入する必要があります。
ko とその他の方法の比較
以前述べたように、開発者がアプリケーションのイテレーションを行う際は、「リファクタリング、ビルド、デプロイ、テスト」ループを迅速に回すことが極めて重要になります。ko を使用することで、Dockerfile の記述や Docker の実行が不要なために時間とシステム リソースが削減されるだけでなく、イテレーションのスピードが向上します。ここでは ko と以下の 2 つの一般的な方法を比較して、そのことについて説明していきます。
ローカルの docker ビルドコマンドと docker push コマンド(Dockerfile を使用)
Buildpacks(Dockerfile は使用しないが Docker で実行)
次の図は、サンプルの Go アプリケーションをコンテナ イメージにビルドして、そのイメージを Artifact Registry に push する場合のパフォーマンスの比較を示したものです。
注: このグラフの「Cold」ビルドは、ビルドマシンとコンテナ レジストリのいずれでもレイヤをキャッシュすることはありません。一方、「Warm」ビルドは両方のレイヤをキャッシュし(キャッシュがデフォルトで有効になっている場合)、レジストリへのレイヤ blob(すでに存在する場合)の push をスキップします。
ko とローカルの Docker エンジンを比較すると、ko がわずかな差で勝っています。Docker エンジンの場合、「docker build」コマンドでソースコードを tarball にパッケージ化して Docker エンジンに送信すると、Linux 上でネイティブに、または macOS / Windows 上の VM 内で実行されます。その後、Docker は Dockerfile 命令用の新しいコンテナを起動してイメージをビルドし、結果のコンテナのファイルシステムをイメージレイヤにスナップショットします。これらの手順には時間がかかる場合があります。
一方、ko にはこのような欠点がありません。ko ではコンテナを起動せずにイメージレイヤを直接作成し、結果のレイヤ tarball とイメージ マニフェストをレジストリに push します。
このアプローチでは、次のコマンドを使用して Go アプリケーションをビルドして push しました。
ko と Buildpacks(ローカルの Docker 上)を比較すると、Buildpacks では、Dockerfile を記述しなくても多くの言語のイメージをビルドできます。ただし、Buildpacks が機能するには Docker が必要になる点に注意が必要です。Buildpacks は言語を検出し、すべてのビルドツールがインストールされた「ビルダー イメージ」を使用して、最終的に結果のアーティファクトをより小さなイメージにコピーします。
この場合、ビルダー イメージ(gcr.io/buildpacks/builder:v1)は 500 MB 程度になるため、「Cold」ビルドになります。しかし「Warm」ビルドの場合でも、Buildpacks ではローカルの Docker エンジンを使用するため、それだけで ko より時間がかかります。
また、Buildpacks はビルドフェーズでカスタム ロジックを実行しますが、これも Docker より速度が遅くなる原因となります。
このアプローチでは、次のコマンドを使用して Go アプリケーションをビルドして push しました。
まとめ
ko は、コンテナ イメージのビルド方法を合理化することで、開発者の負担を軽減するというより大きな効果を実現する手段になります。buildpacks のサポートを使用すると、Dockerfile をまったく記述せずに多くのプログラミング言語からコンテナ イメージをビルドし、その後 1 つのコマンドでそのイメージを Cloud Run にデプロイできます。
ko は、Go アプリケーションのコンテナ イメージへのビルドをサポートし、そのビルドを Kubernetes や Cloud Run にデプロイすることも容易にします。ko は Google Cloud エコシステムに限定されず、任意のコンテナ レジストリに対して認証でき、任意の Kubernetes クラスタで動作します。
詳細については、GitHub リポジトリで ko のドキュメントをご参照ください。ご自身の Go サービスを Cloud Run にデプロイしてみましょう。
-ソフトウェア エンジニア Jon Johnson
-シニア デベロッパー アドボケイト Ahmet Alp Balkan