statik を使って Go バイナリに静的ファイルを埋め込む
statik を使って Go バイナリに静的ファイルを埋め込む
Go でウェブアプリケーションを開発すると、バイナリと HTML ページ、CSS スタイル、JavaScript バンドル、画像、フォントなどの静的アセットを管理するのが煩雑になります。すべてを含む単一の実行ファイルを配布することで、デプロイが簡素化され、ファイルパスの混乱や本番環境でのリソース不足エラーを回避できます。Go エコシステムではこの目的のために便利なヘルパーツール statik が提供されています。
statik は小さなコマンドラインツールで、ディレクトリをスキャンして http.FileSystem にコンテンツを登録する Go ソースファイルを生成します。
このガイドでは、ツールのインストール、埋め込みファイルの生成、コード内での利用方法、そしていくつかのベストプラクティストのヒントを解説します。
statik を使う理由
- 1 ファイルでデプロイ – 外部ファイルを持たず、1 つのバイナリを出荷します。Docker イメージ、サーバーレス関数、CLI にアセットを埋め込む場合に便利です。
- 型安全なアクセス – 生成されたパッケージは型付き fs.FileSystem を提供し、コンパイル時にエラーが早期に検出されます。
- 決定論的な出力 – デフォルトで statik はファイルの修正時刻を保持します。CI 用にオプションフラグで無効にできます。
1. statik CLI をインストールする
# Go 1.22+ では `go install` と @latest が利用できます
go install github.com/rakyll/statik@latest
古い Go バージョンでは手動でモジュールを取得します。
go get github.com/rakyll/statik
バイナリは環境によって $GOPATH/bin あるいは $HOME/go/bin に配置されます。
2. 静的アセットフォルダを準備する
public(好きな名前でも可)というフォルダを作成し、そこにアセットを置きます。
└─ public/
├─ index.html
├─ styles.css
├─ script.js
└─ images/
└─ logo.png
好きなディレクトリ名を使って構いません。statik に渡すパスを覚えておいてください。
3. Go ソースを生成する
フォルダに statik を実行します。一般的なオプションは次のとおりです。
-src: ソースディレクトリへのパス。-srcpath: 生成されるコードが属するパッケージ名。
# 典型的な使用例
statik -src=./public
これで statik フォルダと statik.go ファイルが作成され、public 内のすべてが登録されます。
拡張子でフィルタリング
単一のバイナリに特定のファイルタイプだけを含めたい場合は -include フラグを使用します(サイズを抑えるため)。
statik -src=./public -include=*.html,*.css,*.js
除外したい場合は次のようにします。
statik -src=./public -exclude=*.png,*.jpg
変更時刻を無視する
CI パイプラインでは Git の checkout 時に開発時と異なる mtime が付与され、決定論的テストが壊れることがあります。-m フラグで抑制できます。
statik -m -src=./public
4. コードで埋め込みファイルシステムを利用する
生成されたパッケージをインポートし、fs.FileSystem を初期化します。
package main
import (
"fmt"
"log"
"net/http"
"github.com/rakyll/statik/fs"
// 生成されたパッケージをインポートします。パッケージ名は -srcpath が生成したものになります。
_ "./statik"
)
func main() {
// 新しい statik ファイルシステムを作成
statikFS, err := fs.New()
if err != nil {
log.Fatalf("statik: %v", err)
}
// 必要なら単一ファイルを読み込む
r, err := statikFS.Open("/index.html")
if err != nil {
log.Fatalf("open: %v", err)
}
defer r.Close()
content, _ := io.ReadAll(r)
fmt.Println("Loaded \"index.html\" content:", string(content)[:100])
// HTTP でコンテンツを配信
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(statikFS)))
log.Println("Serving on http://localhost:8080/static/")
log.Fatal(http.ListenAndServe(":8080", nil))
}
プログラムを実行します。
go run main.go
http://localhost:8080/static/index.html を開いて、バイナリから配信される静的ページを確認してください。
5. ヒントとトリック
- アセットは小さく保つ – statik は合計サイズが数メガバイト以内だと最適に動作します。
-md5フラグを使う(将来機能、まだリリースされていません)で MD5 チェックサムを保持し整合性を確認します。- 大きなバイナリの再ビルドを避ける – アセットが変更された時だけ statik を再実行します。Makefile ターゲットや git フックで自動化すると便利です。
- 決定論的ビルドを確認する – 単体テストで生成された FS からファイルを開き、内容やタイムスタンプをアサートできます。
6. statik を使える場面
- ローカルテンプレートを必要とする CLI ツール。
- サーバーレス Go 関数 – 依存関係とアセットを直接埋め込む。
- デスクトップ Go アプリ – アイコン、ヘルプファイル、設定テンプレートを含める。
このツールはアクティブにメンテナンスされており、新リリースや改善点は GitHub リポジトリ で確認できます。
結論
Go バイナリに静的アセットをバンドルすることで、デプロイがスムーズになり、外部依存が減ります。statik は Go の http.FileSystem とシームレスに統合できるエレガントでゼロ設定のソリューションです。上記手順に従うことで、数分で静的ファイルを生成・埋め込み・配信でき、プロダクション環境を軽量で簡潔に保てます。