🧰

Fastly Compute 開発の基礎 (3) はじめてのデバッグ

2023/12/07に公開

この記事は Fastly Compute (旧 Compute@Edge) 一人アドベントカレンダー 6 日目の記事です。

これから開発環境を整備して初めてビルドやデプロイをする方向けの手順を紹介するシリーズ(Fastly Compute 開発の基礎)の第三回目です。本稿ではデバッグやロギング周りの基礎知識を紹介します。

Fastly Compute でのエラーとデバッグ

Fastly Compute 環境でデバッグを行う際、大別して以下 3 種類のエラーに対処することになります。

  • ビルド時のエラー
  • ローカルでのテスト時のエラー
  • Production環境でのエラー

以下順番によくあるエラーや対処方法のパターンについて議論していきます。

ビルド時のエラーとデバッグ

$fastly compute build が失敗する場合、その原因をいくつかの種類に分けることができます。

  1. 自分が書いたコードのコンパイルやビルドに失敗する
    • 例: 構文エラー、メソッド/変数が定義されていない(※)、型の不一致(※) 等
  2. 外部依存パッケージのコンパイルやビルドに失敗する
    • 例: Wasm環境での動作をサポートしていない
  3. (JS SDKの場合のみ) wizer の初期化で失敗してビルドに失敗する

※ JS SDK の場合 TypeScript を利用していれば構文エラー以外もチェックされますが、JavaScript の場合は構文エラーが主にチェックされるためこれらのエラーは検出されないことが多いです

ビルド時のエラーログを見て上記のいずれのパターンに該当するか判別して対応を行いますが、(2)と(3)の場合が特に注意が必要です。(2) については明確にパッケージの README 等で Wasm/WASI 環境での動作をサポートしているかどうか明確にしているケースもありますが、そうではないケースも多く[1]実際に動作するか依存関係を追加してビルドして試行錯誤する、というプロセスを踏むことがよくあります。現時点での WASI 環境ではファイルシステムだったり極端な例だとシェル経由で外部コマンドを実行する、といった処理を含む外部パッケージは当然ながらそのままでは動かないため、パッケージのソースコードを一部修正してビルドを通す等の工夫が必要になる場面もあります。(3) については (2) のパターンに近いですが、言語ツールチェインでのコンパイルは通ったと思ったらその後続処理の wizer でのコンパイル処理で Error: the wizer.initialize function trapped のようなエラーメッセージが吐かれてビルドが終了する、というケースにもよく遭遇します。(wizer については後日投稿予定の SDK の記事でも少し触れる予定です)

ビルド時のエラーについては万能の対応方法があるわけではないので、上記パターンのどれに該当するかを適宜判断しながら、ケースバイケースでの対応を行っていくことになります。

ローカルでのテスト時のエラーとデバッグ

$fastly compute build (あるいは $fastly compute publish$fastly compute serve 等のビルドを伴うコマンド) でビルドが通ると拡張子が .tar.gz であるパッケージファイルが pkg/ フォルダ以下に生成されます。いまこのパッケージファイルを pkg/sample.tar.gz というファイル名とすると、この .tar.gz の中身は以下の構成で格納されます;

x sample/
x sample/bin/
x sample/bin/main.wasm
x sample/fastly.toml

ファイルの実態としては bin/main.wasmfastly.toml のみのシンプルな構成です。この .tar.gz ファイルをローカルのテスト用サーバで利用するためのコマンドが $fastly compute serve です。

$fastly compute serve または viceroy コマンドを利用したローカルでのテストではターミナルに stdout と stderr の両方が出力されるので、ターミナルの出力を見ながらデバッグを進めます。注意点としては、このローカルでのテスト環境(正確には Viceroy)は Fastly の機能の全てを使えるわけではなく、一部未実装の機能や挙動の差異が発生します。例えばキャッシュ機構や WebSocket/Fanout といった機能が未実装です。詳細な未実装の機能についてご覧になりたい方は Developer Hub の記載(Q4に記載が追記改訂されました)を参照してください。

動作の差異はありますが、Production 環境でデバッグをするとデプロイの時間待ち(1-2分/デプロイ)が発生してしまうため、ローカルでのテストを活用してできるだけ高速に開発のイテレーションのサイクルを回し、大方のエラーを取り除いた状態で Production 環境でのデバッグに進むことが望ましいでしょう。

Production環境でのエラーとデバッグ

Production 環境での stdout と stderr を取得する方法は全部で 3 つあります。

  • $fastly log-tail コマンドを利用してターミナルに出力する方法
  • Fastly のコントロールパネル(Web UI)の Observability > Logs を利用する方法
  • ログストリーミング(Real-time log streaming)機能を利用する方法

個人的には、開発中のデバッグでは $fastly log-tail コマンドを最もよく利用しますが、Web UI の Observability での表示(https://manage.fastly.com/observe/logs/tail/)もコマンドが利用できない環境では非常に便利です。最後のログストリーミングは覚えておくと便利な tips なのですが、以下のようにエンドポイントの名前に stdout または stderr と設定することで、stdout/stderr をログストリーミングに流し込むことができます。(先日先輩 Fastly 社員の方に教えていただき初めて知りました)

これは公式でも解説されている公式なテクニックで、一定期間 stdout/stderr の内容を保管しておくと開発/運用上便利なシーンも多いと思いますので、覚えておくと便利です。ログストリーミングの接続先としては S3/GCS のようなストレージや New Relic/Datadog といったよく利用されるロギングのエンドポイント、あるいは BigQuery や Syslog、FTP といった多様なエンドポイントがサポートされているので状況に合わせて適切なものを選べます。

Production 環境でのデバッグにマストで必要なロギング時のイディオム

Fastly Compute の Production fleet 上での開発時には FASTLY_SERVICE_VERSION 環境変数をログ出力することを強く推奨します。本稿で先ほど毎回のデプロイには1-2分かかる点について言及しましたが、これをより正確に書くと ".tar.gz のパッケージをアップロードして新しいサービスのバージョンを Activate するプロセス自体はすぐに完了(10-15秒前後)するのですが、アップロードされた最新のパッケージが Production fleet 上で実際にアクセス可能になるまでは別途 1 分前後のラグが発生します"。Fastly のコントロールパネル上は最新バージョンが Active 状態と表記されていても、実際に Compute platform 上で処理を返すパッケージは、実は一つ前のバージョンのパッケージであるということが、パッケージをデプロイした直後 1-2 分間発生するのです。Production 環境上でのデバッグがほぼ発生しないような開発であれば問題にはならないのですが、実際は Production 環境でしか動作しない機能(Cache 機構や WebSocket/Fanout 等)を利用した開発を行う機会も多いと思いますので、そうした際に少しでも素早く開発のイテレーションを回すためにこの Service Version の情報をログ出力しておくことはマストで必要になってきます。

前置きが長くなりましたが、その際に必要になる各言語でのロギングのイディオムは以下の通りです。

Rust の場合

println!(
  "FASTLY_SERVICE_VERSION: {}",
  std::env::var("FASTLY_SERVICE_VERSION").unwrap_or_else(|_| String::new())
);

Go の場合(osfmt をインポートしていない場合はインポートを忘れずに)

fmt.Println("FASTLY_SERVICE_VERSION:", os.Getenv("FASTLY_SERVICE_VERSION"))

JavaScript の場合(env をインポートしていない場合は import { env } from "fastly:env"; も忘れずに)

console.log("FASTLY_SERVICE_VERSION:", env('FASTLY_SERVICE_VERSION'));

ターミナルに出力されるこの Service Version 番号が、デプロイ直後に払い出された最新の Service Version 番号と一致していることを確認しながら、Production 環境でのデバッグを進めていきましょう。

あわせて読みたい

hkakehashiさんのCompute@Edge のデバッグツールとログについてという記事には、本稿で触れることができなかったローカルの開発環境の詳細な設定方法(fastly.toml にて設定を記述)、ログストリーミングを利用したログ転送のサンプルコード等が分かりやすく解説されていますので、一読されることを推奨します。

まとめ

本稿ではデバッグやロギング周りの基礎知識について紹介しました。次回はブラウザで手軽にビルドとデバッグを試せる Fiddle について紹介したいと思います。

脚注
  1. 例えば Go の場合、Go で WASI がサポートされたのが今年の 9 月でサポートされてから日が浅く、必然的に WASI 環境での動作実績についての情報がまだ少ないという状況があります ↩︎

Discussion