想定している読者
Go を書いていて、サービスは Linux のサーバー単体で systemd の下に置いている方に向けた本です。バックグラウンドワーカーと SQLite を持つ API かもしれませんし、Webhook の受信サーバーや、社内の皆が頼っているツールかもしれません。動いてはいる。知りたいのは、その境界で何が起きるかです。リクエストが処理中のデプロイ、ヘッダーを送り終えないクライアント、メモリが足りないコンテナ、午前 3 時のクラッシュなどがあります。
Go のチュートリアルではありません。言語を理解していて、何かを出荷した経験があることを前提にしています。goroutine の入門をお探しなら、定評のある Go の入門書のほうが向いています。
収録内容
16 章、約 2,000 行の本文と、本中のすべての Go のコード掲載元になっている小さなリファレンスサービスです。60 個のコードブロックそれぞれが、テスト済みのソースと一致しているかをスクリプトで確認しているので、誰も実行していないスニペットが本文に紛れ込むことはありません。
| # | 章 |
|---|---|
| 1 | 小さなサービスのプロジェクト構成 |
| 2 | 環境変数からの設定: すべて検証し、早く失敗させる |
| 3 | log/slog による構造化ログ、シークレットを漏らさない |
| 4 | HTTP サーバーのタイムアウト |
| 5 | グレースフルシャットダウン |
| 6 | ヘルスとレディネスの違い |
| 7 | context、キャンセル、goroutine リークのチェック |
| 8 | SQLite でのデータベースアクセス: WAL、書き込みは一本、マイグレーション |
| 9 | エラーとパニック |
| 10 | メトリクスと pprof を安全に使う |
| 11 | テスト |
| 12 | ビルド: 静的で再現可能なバイナリ |
| 13 | systemd: ユニット、ハードニング、再起動、停止 |
| 14 | メモリ: 負荷がかかったときの Go プロセス |
| 15 | リリースフォルダによるデプロイ |
| 16 | よくある本番のバグと、それぞれを検出するチェック |
ほかに、リファレンスサービス(標準ライブラリと SQLite ドライバー一つ)、負荷生成ツール、小さなメモリ実験、systemd ユニット、Dockerfile、チェックリスト、そしてクリーンなコンテナから全チェックを再実行する Docker ハーネスも付いています。直近の全実行の出力も含めているので、手元の結果と見比べられます。
テストで分かること
シャットダウンと systemd の章は、実際のバイナリを起動して本物のシグナルを送るテストで裏付けています。
| 試したこと | 結果 |
|---|---|
| 1.5 s のジョブが処理中に SIGTERM | 新規接続は 11 ms 後に拒否され、処理中のリクエストは 1.399 s で完了し、プロセスは 1.669 s 後にコード 0 で終了 |
| アイドル状態のサービスに SIGTERM | 2 ms で終了 |
| 9 s のジョブがあり、シャットダウンの制限が 500 ms のときの SIGTERM | 515 ms 後にコード 1 で終了、ジョブはキューに戻された |
| ヘッダーをゆっくり送るクライアント、ヘッダータイムアウト 300 ms | 295 ms で接続を切断 |
3 s のリクエストが処理中の systemctl stop | リクエストは 200 を返し、停止まで 3.19 s |
systemd の下での kill -9 | 2.10 s 後に正常へ復帰 |
| 別々のディレクトリからの二回のビルド | バイト単位で同一のバイナリ(対照として -trimpath を外すと異なる結果に) |
systemd ユニットにはハードニングのオプションを入れています。ファイルの記述を信じるのではなく、稼働中のプロセスから結果を読み戻して確認しました。ケイパビリティなし、seccomp フィルター、読み取り専用の /usr と /etc、メモリ上限です。systemd-analyze security では、このユニットの露出スコアは 1.4 でした。
スループットとメモリ
条件は次のとおりです。Apple Silicon ノート PC 上の Docker Desktop、8 CPU、サービスと負荷生成ツールとデータベースを一つのコンテナに入れ、クライアント 16、モードごとに 8 秒の実行を三回。
| モード | 毎秒のリクエスト数(3 回の実行) |
|---|---|
| 書き込み | 9,190〜11,189(p99 8.4〜11.7 ms) |
| 読み取り | 13,541〜18,421 |
| ヘルスエンドポイント | 23,032〜26,757 |
常駐メモリは、アイドル時に 13 MiB、負荷テスト後に 23 MiB、ピークは 25 MiB でした。
GOMEMLIMIT を計測
14 章は「GOMEMLIMIT を設定しましょう」で終わりません。ライブヒープ 40 MiB の小さなプログラムを、80 MiB に制限したコンテナの中で動かします。制限なしでは、3 回中 3 回で OOM kill されました。GOMEMLIMIT=56MiB を付けると 3 回中 3 回とも生き延びましたが、代償としてガベージコレクションが約四倍に増えました。この制限がなぜソフトなのか、サイジングにどう響くのかも、本の中で説明しています。
動作要件と形式
Go 1.24 以降が必要です。Docker は、ハーネスを再実行したいときだけ使います。すべて Markdown、Go のソース、シェル、設定ファイルなので、PDF のロックもアカウント登録もなく、どこでも読めます。
よくある質問
初心者向けの本ですか?
いいえ。すでに Go を書いている方が対象です。扱うのは本番運用の側、つまり何かがおかしくなったときにプロセスがどう振る舞うかです。
Postgres も扱いますか?
リファレンスサービスとそのテストは SQLite を使っています。データベースの章では Postgres 向けの指針も載せています。
Go Webhook Receiver Starter とはどう違いますか?
スターターは、そのまま動かす小さな製品です。ハンドブックは、行儀のよい Go サービスの考え方と、その裏付けになるテストを扱います。使っているリファレンスサービスも別のものです。
コードを自分のプロジェクトで使えますか?
はい。ご自身のプロジェクトと、ご自身や勤務先のサーバーで使えます。本とキットの再販や再配布はしないでください。
変更履歴
- 0.1.0: 初回リリース。
続きを読む
- Graceful shutdown for a Go HTTP server under systemd (英語)
- GOMEMLIMIT in an 80 MB container, with the numbers (英語)
- Go SQLite reads returning 404 for rows that exist (英語)
- log/slog setup for a small Go service on journald (英語)
- Go HTTP server timeouts that survive slow and stuck clients (英語)
入手する
支払い、税、請求書は販売代行事業者の Polar が処理します。決済後にダウンロードリンクが届きます。