Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ようこそ!

Tazuna ドキュメントへようこそ。

Tazuna は、マルチクラスタ Kubernetes 環境のブートストラップライフサイクルを管理するための CLI ツールです。 tazuna.yaml に「どのクラスタへ、どのマニフェストを、どの順で適用するか」を宣言的に記述し、 その内容を apply / destroy / state といったコマンドで一貫して運用できます。

このドキュメントについて

このドキュメントは、Tazuna を初めて触る人から、CI に組み込んで継続的に運用する人、 そして Tazuna 本体に手を入れる人までを対象に、次の流れで構成しています。

  • はじめかた — Tazuna を手元で初めて動かせる状態にするまで。
  • 概念 — Tazuna が解こうとしている問題と、その設計思想・アーキテクチャ。「なぜそうなっているか」を扱います。
  • ガイドtazuna.yaml を書くなど、実際に手を動かすためのタスク単位の手順。「何を、どの順で、どのコマンドで行うか」を扱います。
  • 運用destroy の運用、drift モニタリング、CI パイプラインなど、継続的に使う局面の指針。
  • リファレンス — 入力ファイル・CLI・内部データ構造の仕様。フィールド・型・デフォルト・例を規約書として参照できます。
  • コントリビュート — 開発環境・テスト・ドキュメント・リリースなど、Tazuna に変更を入れる人向けの案内。

どこから読むか

  • まだ Tazuna に触れたことがなければ、はじめかた から順に進むのがおすすめです。
  • 設計の背景や用語を先に押さえたい場合は 概念 から。
  • 特定のコマンドやフィールドの仕様だけを調べたい場合は リファレンス を辞書的に参照してください。

はじめかた

このセクションは、Tazuna を初めて手元で動かせる状態にするまでを通します。 ここを通したあと、実際の tazuna.yaml の書きかたは ガイド - 最初の tazuna.yaml を書く に進んでください。

ゴール:

  • 自分のマシンに tazuna バイナリが入っている
  • tazuna version が動く
  • どんなクラスタ・どんな外部ツールが前提なのか把握できている

1. インストール

tazuna は単一バイナリです。以下のいずれかの方法で入れます。

リリースバイナリを使う

GitHub の Releases から、 お使いの OS / arch のアーカイブをダウンロードし、tazunaPATH に置きます。

# 例(macOS arm64 / Linux amd64 などのアーカイブを展開後)
mv tazuna /usr/local/bin/
tazuna version

CI で固定バージョンを使いたい場合や、再現性を担保したい場合はこの方法を推奨します。

go install を使う

Go 1.x の toolchain がある場合は、ソースから直接ビルドできます。

go install github.com/pepabo/tazuna@latest

@latest の代わりに @v0.x.y でバージョンを固定できます。 バイナリは $(go env GOBIN)(未設定なら $(go env GOPATH)/bin)に入ります。

ソースから直接ビルドする

リポジトリを clone した状態から自分でビルドする場合は次のとおりです。

make build      # ./tazuna が生成される
make install    # /usr/local/bin にインストール(sudo を使う)

2. 動作確認

インストール後、何も設定せずに次のコマンドが通ることを確認します。

tazuna version
tazuna --help

tazuna version の出力は次のような形式です(実際の値は環境によって変わります)。

tazuna v0.1.0 (commit abc1234, built 2026-05-25T03:21:00Z, darwin/arm64)

ローカルビルドの場合は version / commit / built がそれぞれ dev / none / unknown になります(tazuna version を参照)。

3. 前提を揃える

tazuna がクラスタに対して動作するためには、いくつかの前提が必要です。 すべて必須ではありません。下の表の「必須」だけまず揃えればよく、 1Password / git は使う機能に応じて任意で揃えます。

依存必須?説明
Kubernetes クラスタ◯(apply / destroy / state ... を使うとき)コントロールプレーンの用意は Tazuna の範囲外です。手元で試すなら KinD / minikube、リモートなら EKS / GKE / AKS / kubeadm などで構いません。
kubeconfig の current-context◯(同上)kubectl config current-context で、入れたい先のクラスタを指していることを確認します。kubectl get nodes が通る状態にしておきます。
kubectl-(推奨)Tazuna 自身は使いませんが、kubeconfig 操作や反映結果の確認で使います。
kustomize / helmfile / helm バイナリ-(不要)Tazuna は Go ライブラリとして組み込んでいるため、外部バイナリのインストールは不要です。
1Password CLI (op)-(type: genesissecrethelmfile.vars の from: op を使うときのみ)op コマンドが PATH にあり、サービスアカウントで認証されていることが前提です。
git-(任意)tazuna state sync 時に _metadata.gitCommitHash を埋めるために使います。リポジトリ外で実行した場合や git が無い場合は、エラーにせず空文字で記録されます。

クラスタの選び方や kubeconfig の扱いについては、ガイドの 前提 でも触れています。

4. 次に進む

ここまでで tazuna が手元で動く状態になりました。

概念

このセクションでは、Tazuna がどのような問題を解こうとしているのか、 そしてそのためにどのような設計を採用しているのかを順を追って説明します。

「動かし方」よりも「なぜそのような形になっているか」を中心に書いているため、 具体的なコマンド一覧や CLI フラグについては リファレンス、 具体的な手順については ガイド を参照してください。

読み進め方

おすすめの読み順は次のとおりです。

  1. 設計思想と想定ユースケース — Tazuna が何のために存在し、 どんな現場で使われることを想定しているかを最初に押さえます。
  2. 全体アーキテクチャtazuna バイナリの中に どんなコンポーネントが入っていて、どのように協調するかを俯瞰します。
  3. 用語集 — このセクションで使われる用語の定義をまとめています。

tazuna.yaml のスキーマや各 Manifest backend、State / マルチクラスタ / Secret 連携の挙動など、より具体的な仕様は リファレンス を参照してください。 途中で分からない言葉が出てきた場合は、まず用語集を参照してください。

設計思想と想定ユースケース

Tazuna は「マルチクラスタ Kubernetes のブートストラップライフサイクルを管理する CLI」です。 ここでは、なぜそういうツールが必要になるのか、Tazuna が何を肩代わりし、何は肩代わりしないのか、 という設計上のスタンスを整理します。

解こうとしている問題

Kubernetes クラスタは、API サーバが立ち上がっただけでは実用には使えません。 そこから「自分たちのクラスタ」と呼べる状態にするまでには、

  • CNI / Ingress / cert-manager のような インフラ層のアドオン
  • ArgoCD / Flux のような デプロイ基盤そのもの
  • 各種オペレータや CRD

といったレイヤを、正しい順序で、依存関係を保ちながら クラスタに入れる必要があります。

これを手作業や 複雑な運用手順書、シンプルなシェルスクリプトで代用すると、

  • どこまで適用したか分からなくなる(=「state がない」)
  • クラスタのブートストラップ手順が古くなる

といった問題が積み上がります。Tazuna はここに 単一の宣言的な設定ファイル統一された CLI を持ち込み、ブートストラップという「特定の段階」だけを明示的に扱います。

Kubernetesクラスタをイミュータブルに運用するアイデア

Tazuna の背景にある考え方のひとつが、Kubernetes クラスタそのものを イミュータブル(不変)なリソースとして扱う という発想です。 ここでいうイミュータブルとは、「動いているクラスタを継ぎ足しで直していく」のをやめて、 クラスタは常に同じ手順で作り直せる状態にしておく ことを指します。

ここで「不変」と言っているのは クラスタの土台レイヤ の話です。 Deployment で動いているアプリケーションの Pod が新しいイメージに置き換わったり、 HPA でレプリカ数が変化したりすることを mutate と呼んでいるわけではありません。 対象にしているのは、CNI / Ingress / cert-manager / ArgoCD といった インフラアドオンや、それらが依存する CRD やオペレータといった、 クラスタを「クラスタとして成立させている」基盤レイヤです。 アプリケーションレイヤは、その上で GitOps によって自由に mutate されてかまいません。

長期間運用していると、クラスタには次のような変更が少しずつ溜まっていきます。

  • 障害対応のなかで誰かが kubectl apply した暫定マニフェスト
  • バージョン調査のために手で入れ替えた CRD やオペレータ
  • 手元の helm install で入った add-on

これらが積み重なると、「実際に動いているクラスタ」と「コードで宣言したクラスタ」が 徐々にずれていく(コンフィグレーションドリフト)状態になります。 こうなるとクラスタを作り直すコストが跳ね上がり、DR / リージョン追加 / Kubernetes バージョンアップといった「クラスタごと入れ替えたい」場面で動けなくなります。

イミュータブルに運用するというのは、この状況を裏返して、

  • クラスタの中身は すべて宣言された設定から再生成できる ことを前提にする
  • ブートストラップ後の add-on レイヤは、必要なら 一度クラスタを捨てて作り直しても同じになる
  • 手で入れたものは「いつ消えても良いもの」と割り切るか、宣言側に取り込む

という運用スタイルを選ぶことです。アプリケーションレイヤであれば、これは ArgoCD や Flux のような GitOps ツールがすでに担保しています。 一方で、その GitOps ツール自身を含む「ブートストラップ層」 は、 従来は手順書とシェルスクリプトに頼りがちで、イミュータブルさが最も崩れやすい場所でした。

Tazuna は、このブートストラップ層をイミュータブル運用の枠に乗せるための部品として位置付けています。

  • tazuna.yaml という 単一の宣言的な設定ファイル に CNI / Ingress / cert-manager / ArgoCD などの構成順序を書き切ることで、クラスタの初期状態を コードから一意に再生成できるようにします。
  • tazuna apply は、その宣言と実クラスタの差分を埋める操作です。 「新規クラスタの初日」と「既存クラスタへの追従」を 同じコマンド で扱うので、 作り直したクラスタも既存クラスタも、最終的に同じ宣言に収束します。
  • state によって「いま何が入っているか」を持ち、context_matches で どのクラスタに対する宣言なのかを縛ることで、 「別のクラスタに間違って適用する」「適用したかどうか分からない」 を仕組みで防ぎます。
  • ブートストラップが終わったあとのアプリケーション運用は ArgoCD / Flux に渡します。 ここでイミュータブル運用のバトンがアプリケーションレイヤに引き継がれ、 ブートストラップ層と継続的デリバリ層の両方 が宣言から再現可能になります。

なお Tazuna は「クラスタを毎回作り直して運用すること」を強制するわけではありません。 実際にイミュータブルに運用する(クラスタごと作り直して切り替える)スタイルも 想定の中に入っていますが、より重視しているのは、その一歩手前の性質、すなわち 「Kubernetes クラスタが任意のタイミングで壊れたとしても、保証された手順で 自動的に再構築できる」 状態を常に保てることです。

この性質さえあれば、

  • 普段は同じクラスタを使い続けて運用する
  • DR や大きなバージョンアップのタイミングだけ、別のクラスタを丸ごと立てて切り替える
  • 検証用の使い捨てクラスタを必要なときに立ち上げる

といった選択肢を そのときの状況に応じて選べる ようになります。

Tazuna は、その「保証された再構築手順」を tazuna.yaml というコードと tazuna apply という一つのコマンドに集約することで、 イミュータブル運用を選べる状態を維持し続ける ための土台を提供します。

Tazuna が肩代わりしないこと

意図的に踏み込まない領域もあります。

  • 継続的デリバリ — ArgoCD や Flux の代替ではありません。Tazuna の役目は、 そうしたツールをクラスタに 入れるまで です。入ったあとは ArgoCD/Flux に渡します。
  • マニフェストの中身を書く — kustomize の overlay、helmfile の構成、Helm chart の値は、 既存の各ツールの作法で書きます。Tazuna はそれらを 呼び出して結果をクラスタへ流す だけです。
  • コントロールプレーンの作成kubeadm / kops / マネージドサービスのクラスタ自体は前提です。 Tazuna は kubeconfig を介して既存クラスタへ接続します。
  • Secret 管理基盤そのもの — Secret の格納先の参照を宣言しますが、 Tazuna 自身は秘密を保管しません。
  • GitOps のロールバック・履歴管理state は「いま何が入っているか」を表すもので、 歴史的なバージョン管理は git に任せます。

想定ユースケース

具体的に Tazuna が嬉しい場面はおおむね次のようなときです。

  • 新規クラスタを立ち上げる初日tazuna apply 1 発で、 CNI からデプロイ基盤までを所定の順序で入れたい。
  • Kubernetesクラスタの構築手順が妥当であることを自動検証する
  • 同じ役割のクラスタを複数立てる — staging / production / dr といった 類似クラスタを、ほぼ同じ tazuna.yaml で立ち上げたい。

次は 全体アーキテクチャ で、これらをどんな部品で実現しているかを見ます。

全体アーキテクチャ

このページでは Tazuna を構成する主なコンポーネントと、 それらが tazuna apply のときにどう協調するかを俯瞰します。

ここでは責務の境界だけを扱います。具体的なディレクトリ構成や Go パッケージの分割方針は意図的に触れません。

レイヤ図

おおまかには次の 3 層構造です。

+--------------------------------------------------+
|  CLI                                             |
|   apply / build / check / destroy / state ...    |
+--------------------------------------------------+
                         |
                         v
+--------------------------------------------------+
|  Runner                                          |
|   - load tazuna.yaml / expand includes           |
|   - verify context_matches / filter by tags      |
|   - dispatch each manifest to a Manager          |
+--------------------------------------------------+
                         |
           +-------------+-------------+
           v                           v
+---------------------+     +---------------------+
|  Manager            |     |  Test plugin        |
|                     |     |   wait-until /      |
|  kustomize /        |     |   exist-nonexist    |
|  helmfile /         |     +---------------------+
|  oras /             |
|  genesissecret /    |
|  parallel           |
+---------------------+
           |
           v
+---------------------+
|  Kubernetes cluster |
+---------------------+

各 manifest は 1 つの Manager を介してクラスタに反映されます。 適用前後の検証は Test plugin に切り出されており、 manifest 単位でも tazuna.yaml 全体でも実行できます。

主要コンポーネント

Tazuna は内部的に次のコンポーネントが組み合わさって動きます。 ここでは各コンポーネントが「何を入力に取り、何を出力するか」という責務だけを示します。

CLI

サブコマンド(apply, build, check, destroy, state list, state diff, state sync, secret-to-genesissecret, version など)の入り口です。 フラグの解釈と Runner の組み立てだけを担当し、ロジック自体は持ちません。

Runner

Tazuna 全体のオーケストレータです。次を担当します。

  • tazuna.yaml のロード
  • includes の展開
  • tazuna.yaml 起点の相対パスを実行時パスへの変換
  • --tags によるフィルタ
  • 各 manifest を対応する Manager へ渡す
  • 適用後の Test plugin の実行

Runner は manifest type の種類を知りません。 「どの type に対してどの Manager を使うか」というマップだけを持ち、 それ以外は Manager 側に委ねます。

Manager

manifest type ごとの「実際の適用方法」を実装するコンポーネントです。 すべての Manager は次の 3 つの操作を提供します。

  • Apply — その manifest をクラスタへ反映する
  • Destroy — その manifest が入れたものをクラスタから取り除く
  • Build — クラスタには触らず、適用したら何が入るかだけを生成する

Runner はこの 3 操作だけを介して manifest type を均一に扱います。 個別のバックエンドの責務分担は リファレンス を参照してください。

Validator

tazuna.yaml のロード直後に走るスキーマ・パス整合性検証です。 applycheck も最初にここを通り、不正な YAML や 存在しない pathクラスタに触る前に 弾きます。

Context guard

tazuna.yamlcontext_matches を読み、現在の kubeconfig context 名が そのパターン(複数の正規表現)に合致しているかを検証します。 合致しなければ apply / destroy はここで終了し、クラスタには一切触れません。

State store

Tazuna が「自分が入れたリソースの指紋」をクラスタに記録するための仕組みです。 保存先はクラスタ内の ConfigMap で、state list / state diff / state sync は ここを起点に動きます。

Secret provider

GenesisSecret および helmfile の vars.op から参照される、 「シークレットの取得元」を抽象化したコンポーネントです。 現状は 1Password 向けの実装が組み込まれています。

Test plugin

manifest 適用後(または tazuna.yaml 全体の適用後)に走らせる検証の仕組みです。 組み込みで次の 2 種が利用できます。

  • wait-until — 指定リソースが存在 / Ready / Available になるまで待つ
  • exist-nonexist — 指定リソースが存在 / 非在 であるべきことを表明する

Hint

helmfile の vars が取りうる値の型・フォーマット (hostname / URL / email / IP / CIDR / UUID / semver / datetime など)を 宣言的に検証するための補助コンポーネントです。

Prompt

破壊的操作の Yes/No 確認といった対話 I/O を抽象化するコンポーネントです。 非対話モードやテスト時に振る舞いを差し替えるためのものです。

tazuna apply のときの流れ

ここでは「どのコンポーネントが何をするか」の観点で並べておきます。

  1. CLI がフラグを解釈し、Runner を組み立てる。
  2. Validatortazuna.yaml を読み、スキーマと path の存在を検証する。
  3. spec.context_matches があれば Context guard が kubeconfig を検証する。
  4. Runnerincludes を展開し、manifests[].path を実行時パスへ変換する。
  5. manifests を順に走査し、--tags で除外されないものを 対応する Manager に渡す。
  6. 各 Manager は内部で kustomize / helmfile / oras pull / 1Password 取得 などを呼び出し、 結果をクラスタへ反映する。State store に指紋が書き込まれるのもここ。
  7. manifest 単位 / 全体の Tests があれば Test plugin が走る。

tazuna state sync はこの 1〜4 までを使ったうえで、 各 Manager の Build 結果から State diff を組み、追加分・変更分だけを apply します。

用語集

このセクションで頻出する用語の定義をまとめます。 より詳しい仕様は リファレンス を参照してください。

Tazuna 固有の用語

tazuna.yaml

Tazuna に対する唯一の入力ファイル。apiVersion: tazuna.pepabo.com/v1, kind: Tazuna を持ち、spec.manifests[] に「適用したい manifest 群」を宣言する。

Manifest

tazuna.yaml 内の manifests[] の 1 エントリのこと。 name / type / path を持ち、type ごとに対応する Manager が処理する。 Kubernetes における「マニフェスト」(YAML ファイル)とは指す対象が違う点に注意。

Manifest type

Manifest の処理方法を指定する文字列。 kustomize / helmfile / genesissecret / parallel / oras の 5 種類。

Manager

ある Manifest type に対する処理を担うコンポーネント。 Apply(クラスタへの反映)/ Destroy(取り外し)/ Build(クラスタに触らない生成)の 3 つの操作を提供し、Runner はこの 3 つを介してすべての backend を均一に扱う。

Runner

Tazuna の中心オーケストレータ。tazuna.yaml のロード、includes 展開、--tags フィルタ、 Manager の呼び出し、Test plugin の起動などをまとめて担当する。

Test plugin

Manifest 適用の前後で行いたい検証を表現する仕組み。 組み込みで wait-until(Ready/存在まで待つ)と exist-nonexist(存在/非在の表明)がある。 spec.manifests[].testsspec.tests に書く。

State

Tazuna が「自分が入れたリソース」を追跡するための記録。 クラスタ内 tazuna namespace の ConfigMap (tazuna-state-<manifest-name>) に保存される。

State key

State の 1 エントリを指すキー文字列。 namespaced なら {manifest}/{group}/{version}/{kind}/{namespace}/{name}、 cluster-scoped なら {manifest}/{group}/{version}/{kind}/{name}

ContentHash

State の各エントリが持つ SHA-256 ハッシュ値。 リソース YAML から metadata.resourceVersion / uid / creationTimestamp / generation / managedFields / selfLinkstatus を除いて計算する。 このハッシュの一致/不一致が state diff の判定基準。

Diff type

tazuna state diff がリソースごとに付ける分類。 added / modified / removed / always-sync の 4 種類。

always-sync

GenesisSecret 由来 Secret のように、差分計算をスキップして毎回同期する扱いの Diff type。 ContentHash で変化を判定できない / させない対象に使う。 1Password 側で値が更新されてもクラスタ側のハッシュは変わらないため、 ContentHash を使わずに毎回 Provider に問い合わせる形で同期する設計になっている。 利用例と運用上の扱いは GenesisSecret スキーマ - State と always-syncDrift モニタリング を参照。

GenesisSecret

1Password に保管された秘密情報から、Kubernetes Secret を 生成 するための宣言。 Kubernetes CRD ではなく、Tazuna が読む YAML スキーマ。 type: genesissecret の manifest として tazuna.yaml から参照する。

Provider (SecretProvider)

GenesisSecret が秘匿情報を取り出す元を抽象化したインターフェース。 現状は 1Password (op) 向けの実装が組み込まれている。

context_matches

tazuna.yamlspec.context_matches。 現在の kubeconfig context 名がマッチすべき正規表現の配列で、 誤クラスタへの apply を防ぐためのガード。

context_match_mode

context_matches の評価方式。or(デフォルト)または and

includes

manifests[] のエントリで、別ファイルの tazuna.yaml を読み込み、 その manifests[] を展開するための仕組み。ネストは不可。

Tag (manifest tag)

manifests[].tags に書く文字列。tazuna apply --tags foo,bar のように 適用対象を絞り込むときに使う。複数タグは OR で評価される。

Manifest path

manifests[].pathtazuna.yaml 自身の置かれているディレクトリ起点の相対パスとして書く。 Tazuna は実行時に cwd 起点へ変換する。

tazuna.hint.yaml

helmfile の vars が取りうる値の型・フォーマット制約を宣言するヒントファイル。 pkg/hint/ で読まれる。

Kubernetes 側の用語(補足)

ここに挙げるのは Tazuna 内で頻出する Kubernetes 標準用語の短い定義です。

kubeconfig

クラスタ接続情報(cluster / user / context)をまとめた YAML。 Tazuna はここから current-context を読み、そのクラスタへ操作を行う。

context (kubeconfig context)

「どの cluster にどの user で繋ぐか」を 1 名前にまとめた kubeconfig の要素。 context_matches が正規表現でチェックするのはこの context 名。

GVK (Group/Version/Kind)

Kubernetes リソースの種別を一意に特定する 3 つ組。 Tazuna の State key も GVK を含む。

namespaced / cluster-scoped

リソースが namespace に属するか属さないか。 State key の長さ(5 パートか 6 パート)に反映される。

ConfigMap

任意の key-value をクラスタに保存するための組み込みリソース。 Tazuna の State の保存先として使われる。

外部ツール

kustomize

Kubernetes manifest の overlay / patch 機構。 type: kustomize の Manager から呼び出される。

helmfile

複数の Helm release を 1 YAML から束ねるツール。 type: helmfile の Manager から呼び出される。

Helm

Helm chart を扱うパッケージマネージャ。helmfile から内部的に使われる。

ORAS / OCI artifact

OCI registry に Kubernetes manifest のようなコンテナ以外の成果物を置く規格。 type: oras で pull し、delegate に指定した helmfile / kustomize に処理を委譲する。

1Password

Tazuna が GenesisSecrethelmfile.vars.op で参照する Secret の格納先。 取得は op コマンド経由。

ガイド

このセクションでは、Tazuna を使って実際に手を動かすための手順をまとめます。 概念 が「なぜそうなっているか」を扱うのに対し、 ここでは「何を、どの順で、どのコマンドで行うか」をタスク単位で示します。

各ガイドは独立して読めるように書いていますが、まだ Tazuna に触れたことがない場合は、 順番に通すと無理なく進められます。コマンドの細かい挙動やフラグの一覧は リファレンス を参照してください。

入門

新しく Tazuna を導入したいときに最初に通すグループです。

  1. 最初の tazuna.yaml を書く — 1 つの Kubernetes クラスタに kustomize で書いた add-on を 1 つ入れるところまでを、 tazuna.yaml の最初の 1 枚から tazuna apply までひと通り通します。

これ以降のテーマ(複数 manifest の順序付け、--tags による絞り込み、 State の確認、GenesisSecret、CI 連携など)は、ここで作った tazuna.yaml を 徐々に拡張していく形で順次追加していきます。それまでの間、各テーマの 仕様 は以下のリファレンスから引けます。

最初の tazuna.yaml を書く

このガイドは、これから Tazuna を導入する人が 最初の tazuna.yaml を書き、 1 つの Kubernetes クラスタに 1 つのアドオンを入れるところまでを通すための手順です。

ここでは題材として、kustomize で書いた小さな Deployment を 1 つ、 Tazuna 経由でクラスタに入れます。実運用ではここに ingress-nginx や cert-manager、 ArgoCD などが並びますが、最初の 1 枚を理解するためには Manifest は 1 つあれば十分です。

このガイドを終えると、次の状態になっています。

  • 自分のリポジトリに tazuna.yaml と kustomize ディレクトリが 1 セットある
  • tazuna check でその tazuna.yaml が妥当だと確認できる
  • tazuna build でクラスタに触れずに「何が入るか」を確認できる
  • tazuna apply でその内容がクラスタに反映されている

前提

Kubernetes クラスタを 1 つ用意する

Tazuna はクラスタの作成そのものは行いません。コントロールプレーンの準備は前提です。 試すだけであれば、手元で動かせる軽量なクラスタで十分です。

  • 手元で完結させたい場合: KinDminikube で 1 ノードのクラスタを立てる
  • リモートでまず触ってみたい場合: EKS / GKE / AKS などのマネージドクラスタ、 または kubeadm で立てた検証用クラスタ

kubectl get nodesReady を返す状態になっていれば、どれを選んでも構いません。

tazuna バイナリを用意する

リリースページからバイナリを取得して PATH に置くか、go install で導入します。 詳細は はじめかた を参照してください。

tazuna version が動けば準備完了です。

kubeconfig の current-context を確認する

Tazuna は kubeconfig の現在の context が指すクラスタに対して動きます。 入れたい先のクラスタに current-context が向いていること を、手を動かす前に必ず確認します。

kubectl config current-context
kubectl get nodes

複数クラスタを行き来する環境では、これを取り違えるのが最も典型的な事故の入口です。 context を取り違えても気付けるようにする仕組みとして context_matches がありますが、 最初の 1 枚では使わずに進めます(後続のガイドで扱います)。

作るもの

このガイドでは、次のようなディレクトリ構成を作ります。

my-cluster/
├── tazuna.yaml
└── kustomize/
    └── nginx/
        ├── kustomization.yaml
        └── deployment.yaml

tazuna.yaml が「何を入れるか」を宣言し、kustomize/nginx/ がその実体です。 このガイドではこの 2 階層だけを覚えておけば十分です。

1. kustomize ディレクトリを作る

まず、Tazuna が呼び出す側、つまり kustomize で書いた「入れたいもの」を用意します。 ここでは練習用に、nginx という Deployment を 1 つだけ持つ最小構成にします。

my-cluster/kustomize/nginx/kustomization.yaml:

resources:
  - deployment.yaml

my-cluster/kustomize/nginx/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80

この時点で、Tazuna を使わなくても kubectl apply -k my-cluster/kustomize/nginx で同じものをクラスタに入れることができます。Tazuna はあくまでこれを 宣言的に管理するための一段上の層 として乗せるだけ、と捉えておくと理解しやすくなります。

2. tazuna.yaml を書く

次に、Tazuna に対する「唯一の入力ファイル」である tazuna.yaml を書きます。

my-cluster/tazuna.yaml:

apiVersion: tazuna.pepabo.com/v1
kind: Tazuna
spec:
  manifests:
    - name: nginx
      type: kustomize
      path: ./kustomize/nginx

最初は次の 3 つを押さえれば動かせます。

  • name — この Manifest の識別子。State やログでこの名前が使われます。 半角英数字とハイフン・アンダースコアで付けます。
  • type — Tazuna がどのバックエンドでこの Manifest を扱うか。 ここでは kustomize を指定します。helmfileoras といった選択肢もあります (Manifest type 参照)。
  • path — 実体のあるディレクトリ。tazuna.yaml 自身の置かれているディレクトリ起点 の相対パスで書きます。

path の起点が「コマンドを実行した cwd」ではなく「tazuna.yaml 自身の場所」である点には注意してください。 リポジトリのどこから tazuna を呼び出しても結果が変わらないように、こちらに合わせています。

3. check で妥当性を確認する

ファイルを書き終えたら、まずクラスタには触れずに tazuna checktazuna.yaml を検証します。

cd my-cluster
tazuna check

ok と出れば、次の検査をすべて通っています。

  • YAML としてパースできる
  • name が必須でユニーク、使える文字種に収まっている
  • path の指す場所が実際に存在する

ここで失敗するものは、すべて クラスタに触る前に 落とせます。 CI で最初に通す対象として組み込みやすいコマンドでもあります。

4. build でクラスタに触らず中身を見る

次に、tazuna build を使って「tazuna apply したら何が入るか」を、 クラスタには一切触れずに stdout に書き出します。

tazuna build

kustomize build 相当の Kubernetes manifest が出力されます。 今回の例では先ほど書いた Deployment/nginx がそのまま出てくるはずです。

build はクラスタへの接続を必要としません。 意図したものが本当に入ろうとしているか をレビューしたい場面や、 レンダリング結果を別のツールに食わせたい場面で使います。

5. apply でクラスタに反映する

ここまで来れば、本番作業はあと 1 コマンドです。 current-context が入れたいクラスタを指していることをもう一度確認してから、 tazuna apply を実行します。

kubectl config current-context   # 入れたいクラスタを指していること
tazuna apply

Tazuna は次のことを順に行います。

  1. tazuna.yaml をロードして検証する
  2. manifests[] を宣言順に走査する
  3. 各 Manifest を対応する Manager(ここでは kustomize Manager)に渡す
  4. kustomize Manager が path をレンダリングし、結果をクラスタに反映する
  5. 反映した結果を State としてクラスタ内に記録する

State はクラスタ内 tazuna namespace の ConfigMap (tazuna-state-<manifest-name>) に置かれます。tazuna namespace が無ければ自動的に作られるので、 事前に作っておく必要はありません。

反映されたことを確認する

普段どおり kubectl で見ても良いですし、Tazuna 側からも確認できます。

kubectl get deployment -n default nginx
tazuna state list

tazuna state list には、いま Tazuna が「自分が入れたもの」として記録している リソースが一覧で出ます。今回のガイドでは Deployment/nginx 1 件が出ているはずです。 ここに出ているリソースは、後で tazuna destroy で安全に取り外せる対象でもあります。

よくある落とし穴

最初の 1 枚を書くときによく踏むものを並べておきます。

  • path の起点を勘違いするtazuna.yaml 自身のディレクトリ起点です。 cd した場所からの相対パスのつもりで書くと、CI とローカルで挙動が食い違う原因になります。
  • クラスタを取り違えるkubectl config current-context の確認を apply の直前に必ず入れる運用にします。context_matches を導入すれば仕組みで防げますが、 まずは目視確認を習慣にします。
  • kustomize 側のエラーを Tazuna のエラーだと思うtazuna build が失敗するときは、 ほとんどの場合 kustomize 側のエラーがそのまま伝播してきています。 kustomize build ./path を直接実行して切り分けると速いです。
  • 手で kubectl apply したものを混ぜる — 同じクラスタに対して Tazuna 経由と 手作業を混在させると、State と実体がずれていきます。混ぜる場合は、 あとから tazuna state diff で差分を見られることだけ覚えておきます。

次に進む

次のガイドでは、ここで作った tazuna.yaml に Manifest を増やし、 複数のアドオンを順序付きでクラスタに入れる ところを扱う予定です。 そこから先で --tags による絞り込み、includes による分割、context_matches による 誤クラスタ事故の防止といった話に段階的に踏み込んでいきます。

運用

このセクションは、Tazuna を 継続的に使う 局面の指針をまとめます。 タスク単位の手順(「新しい add-on を入れる」「tazuna.yaml を書く」など)は ガイド を、コマンドやスキーマの仕様は リファレンス を参照してください。

このセクションでは、事故を起こさない運用drift を発見できる運用 を中心に扱います。

一覧

  • tazuna destroy の運用 — 本番クラスタで destroy を打つときに踏むべき手順、TAZUNA_DESTROY_EXECUTABLEcontext_matches の二段ガード、事故が起きやすいシナリオ。
  • Drift モニタリングtazuna state diff を定期実行して drift を可視化する運用、出力フォーマットと 通知の組み立て方。
  • CI パイプライン — PR で check / buildmain マージで apply を回す典型構成、 destroy の置き場、状態同期の選択。

tazuna destroy の運用

このページは、本番に近いクラスタで tazuna destroy を打つときの 手順と備えるべきガード をまとめます。 コマンドの仕様そのものは tazuna destroy リファレンス を参照してください。

なぜ runbook 化するか

destroy は Tazuna 管理下のリソースをクラスタから取り除く操作で、影響範囲が クラスタ全体に及び得る 唯一の系統的な書き込みコマンドです。手で kubectl delete していくのと違い、Tazuna が「自分の管理対象」と見なすものはまとめて消えます。

二段ガード(プロンプト + 環境変数)は仕組みとしての防御線ですが、運用側でも 「何が消えるかを事前に確認する」「context を取り違えない」 を組み立てておく必要があります。

前提として整える

destroy をクラスタに対して使う可能性があるなら、その tazuna.yaml には spec.context_matches を入れておく ことを強く推奨します。

spec:
  context_matches:
    - ^prod-tokyo$
  context_match_mode: or
  manifests:
    # ...

これがあると、destroy 時に current-context が prod-tokyo でなければ クラスタには一切触れずに失敗します。手元の context 設定ミスに対する、もっとも安価な 保険です。詳細は tazuna.yaml - context_matches を参照。

標準フロー

以下の 1〜3 は tazuna destroy の内部処理ではなく、運用上の事前確認手順 です。 Tazuna 自身は state list を呼びませんが、destroy 前に手で打つことを強く推奨します。

# 1. current-context を確認(destroy 直前は必ず)
kubectl config current-context
kubectl get nodes

# 2. 何が消えるかを把握する(推奨)
tazuna state list

# 3. 必要なら範囲を絞る(実行時とまったく同じ --tags を付ける)
tazuna state list --tags experimental   # 確認用

# 4. 本実行。プロンプトと環境変数の両方を満たして初めて削除が走る
TAZUNA_DESTROY_EXECUTABLE=true tazuna destroy --tags experimental

tazuna destroy 自体は次の順で動きます。

  1. tazuna.yaml をロード・バリデーションする。
  2. context_matches を評価する。不一致なら即終了。
  3. --force が無ければ Y/N のプロンプトを出す。
  4. TAZUNA_DESTROY_EXECUTABLE=true でなければ、ログだけ出して終了。
  5. すべて満たしていれば、Manager の Destroy を順に呼び出す。

「プロンプトに Yes」と「環境変数 TAZUNA_DESTROY_EXECUTABLE=true」の 両方 が 揃わない限り、リソースは消えません。

範囲を絞る運用

クラスタ全体ではなく、特定のグループだけを取り外したい場合は --tags で絞り込みます。

TAZUNA_DESTROY_EXECUTABLE=true tazuna destroy --tags experimental

--tags は OR 評価です(tazuna.yaml - tags)。 廃止予定のものに lifecycle:deprecated のような専用タグを付けておき、その単位で削除する、 というパターンが扱いやすくなります。

tazuna destroy --tags <空> は全 Manifest が対象です。絞らない destroy を行うときは 事前に tazuna state list で全量を見ておき、想定外のリソースが入っていないことを確認してください。

事故が起きやすいシナリオ

実際にやりがちなパターンと、対応策の対応表です。

シナリオどこで止まる / なぜ事故になるか推奨対応
current-context を staging のつもりが production になっていたcontext_matches を書いていれば 2 で停止する。書いていないと進む。本番系の tazuna.yaml には必ず context_matches を入れる。プロンプトで止まれるよう --force は付けない。
CI に destroy を組み込み、誤って main で発火環境変数を付けていないなら 4 で止まる。TAZUNA_DESTROY_EXECUTABLE=true を CI 全体に常設していると進んでしまう。CI には destroy を組み込まない。どうしても必要なら、専用の手動 workflow を作り、その job だけで一時的に環境変数を渡す。
--tags を付け忘れて全消ししてしまうプロンプトと環境変数の両方を満たすと進む。「絞りなしの destroy」を本番に近いクラスタで打たない運用にする。打つときは事前 state list を必ず通す。
手作業で kubectl apply したものは destroy で消えないState に無いリソースは Tazuna 管理対象外として扱われ、destroy の対象にならない。状態が乖離していると判断したら、tazuna state diff で乖離を見える化した上で対応を決める。

destroy の代わりに使える緩い手段

リソースを「いま完全に消す」必要がない場合、次の選択肢があることを覚えておきます。

  • tazuna state sync + TAZUNA_STATE_SYNC_DELETE=truetazuna.yaml から Manifest を消したうえで state sync を打つと、removed 分類で 削除されます(tazuna state sync)。 tazuna.yaml のソースの真実を変えずに reset したいときには使えません。
  • --tags で絞った destroy — 上記参照。

関連

Drift モニタリング

このページは、tazuna state diff定期的に回して drift を可視化する運用 の作り方をまとめます。 コマンドの仕様は tazuna state diff を、 State の中身の仕様は State の内部構造 を参照してください。

何を drift と呼ぶか

ここでの drift は、tazuna.yaml から生成されるべきリソース集合(Build 結果)と、 クラスタ内 State に記録されているリソース集合の差です。 これは tazuna state diff の出力そのものに当たります。

Diff type検出されるケースdrift の典型例
addedBuild 結果に存在し、State に無いtazuna.yaml を更新して Manifest を増やしたが、まだ反映していない
modified両方に存在するが内容が違うHelm values 変更、kustomize overlay 変更、image tag 更新の未反映
removedState にあって Build 結果に無いManifest を tazuna.yaml から外したが、リソースはクラスタに残っている
always-sync常に同期扱いGenesisSecret 由来 Secret。drift ではなく「毎回チェックする箇所」

tazuna state diffクラスタの実体までは見ていません。 クラスタに対して手で kubectl apply した結果(State には無いリソース)は ここでは検出されません。Tazuna の管理対象外として無視されます。

出力フォーマット

tazuna state diff は Manifest 単位で次のような出力を出します。

Manifest: ingress-nginx
  STATUS         RESOURCE                                                     HASH
  modified       ingress-nginx/apps/v1/Deployment/ingress-nginx/controller    abc123... -> def456...

Manifest: aws-credentials
  STATUS         RESOURCE                                                     HASH
  always-sync    aws-credentials//v1/Secret/default/aws-credentials           xyz789...

差分が無い場合は次の 1 行だけが出ます。

No changes detected.

「drift なし」の判定は この 1 行で判断するのが現状もっとも素朴 です (出力に No changes detected. を含むかどうかでフィルタする)。 tazuna state diff 自体は差分の有無で終了コードは変えません。 差分があってもエラーにはならない、という点に注意してください。

監視のかたち

実運用での「drift モニタリング」は次のいずれか(または組み合わせ)になります。

a. CI ジョブを定期実行する

GitHub Actions の schedule で 1 日に数回 tazuna state diff を回し、出力を保存します。

  • メリット: 既存の CI 認証を再利用できる。差分が出たら Slack 等に投げやすい。
  • デメリット: クラスタ接続情報を CI に持ち込む必要がある。短い周期には向かない。

ポイント:

  • ジョブには クラスタへの read 権限だけ あれば足ります(tazuna state diff は クラスタを変更しません)。
  • 出力を tazuna state diff -f path/to/tazuna.yaml > diff.txt のようにファイルに落とし、 No changes detected. を含まない場合だけ通知を投げると、無風時のノイズが消えます。

b. クラスタ内 Job として走らせる

tazuna バイナリを含むコンテナイメージを用意し、CronJob として定期実行する方法です。

  • メリット: 認証は ServiceAccount に閉じる。短い周期にしやすい。
  • デメリット: イメージのビルド・配布が必要。CI と同じ tazuna.yaml リポジトリへの アクセスを Job 側にも持たせる必要がある。

type: oras を使って tazuna.yaml 一式を OCI artifact として配布しておくと、Job 側で リポジトリの clone を持たずに済みます(tazuna apply --offline と組み合わせると registry も不要になります)。

通知の組み立て

通知側で読みたい情報は次の 3 つです。

  • どの Manifest に差分があるか
  • どの Diff type か(特に removed は注意)
  • どの リソース か(State key の形式で)

State key の形式は manifest/group/version/kind/namespace/name(cluster-scoped は namespace 抜き)で固定なので、grep ベースの後段処理に十分なります。 詳細は State の内部構造 - State key を参照してください。

通知の最小プロトタイプ:

if ! tazuna state diff -f tazuna.yaml | tee diff.txt | grep -q "No changes detected."; then
  curl -X POST "$SLACK_WEBHOOK_URL" --data "$(jq -Rs '{text: .}' < diff.txt)"
fi

jq -Rs '{text: .}' は、diff.txt の中身を 生文字列のまま Slack の Incoming Webhook が 期待する {"text": "..."} 形式 JSON に包み直すための定型です(-R で raw 入力、-s で 全行を 1 つの文字列にスラープ)。

検知後の対応

drift が出たときの選択肢は次のいずれかです。

  • 意図した変更だった: tazuna apply(または tazuna state sync)で State をクラスタに追従させる。
  • 意図しない変更だった:
    • modified: 誰がいつ変えたかを git log / クラスタの監査ログ等で追い、 変更を巻き戻すか tazuna.yaml 側に取り込むかを判断する。
    • added: tazuna.yaml 側に Manifest を増やしたが反映していない、というケースが多い。 意図に合わせて apply するか、tazuna.yaml 側を元に戻す。
    • removed: Tazuna から Manifest を外したが、リソースはクラスタに残っている。 tazuna destroy--tags 絞り込みや、 tazuna state sync + TAZUNA_STATE_SYNC_DELETE=true で片付ける。
  • GenesisSecret の always-sync: drift ではないので通知から除外して構いません。

関連

CI パイプライン

このページは、tazuna.yaml を持つリポジトリで CI / CD パイプラインに Tazuna を組み込む ときの典型構成をまとめます。Tazuna は手元で apply するためにも、CI から apply するためにも使えます。ここでは後者の組み立て方を中心に扱います。

典型構成

役割を分けると次の 3 ステージになります。

ステージ目的走らせるコマンドクラスタアクセス
検証tazuna.yaml が壊れていないことの保証tazuna checktazuna buildなし
反映main の内容をクラスタに反映するtazuna apply(または tazuna state syncあり
取り外しTazuna 管理リソースを削除するtazuna destroyあり

「検証」は全 PR で走らせて構いません。「反映」は通常 main への push をトリガにします。 「取り外し」は CI に常設しない ことを推奨します(tazuna destroy の運用 参照)。

検証ステージ

tazuna.yaml を CI に乗せる場合、最低限の通過条件として tazuna check を入れます。 クラスタに触れずに走るので、PR でのコストはほぼゼロです。

# GitHub Actions の例(要点だけ)
- name: tazuna check
  run: tazuna check -f tazuna.yaml

- name: tazuna build (preview)
  run: tazuna build -f tazuna.yaml > rendered.yaml

PR で build 結果をアーティファクトに残しておくと、レビュー時に「最終的に何が apply されるか」を レンダリング結果ベースで確認できます。type: oras を使っているなら --offline を 付けて先取り cache を使う構成も検討できます(tazuna build)。

反映ステージ

main への push をトリガに tazuna apply を走らせる構成が基本です。

on:
  push:
    branches: ["main"]

jobs:
  apply:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write   # OIDC でクラスタに繋ぐ場合
    steps:
      - uses: actions/checkout@v6
      - name: install tazuna
        run: |
          curl -L https://github.com/pepabo/tazuna/releases/download/v0.1.0/tazuna_Linux_x86_64.tar.gz \
            | tar xz -C /usr/local/bin tazuna
      - name: configure kubeconfig
        run: |
          # クラスタ側の都合に応じて、aws-iam-authenticator / gke-gcloud-auth-plugin
          # / kubeconfig secret 等で current-context を設定する
          aws eks update-kubeconfig --name prod-tokyo --region ap-northeast-1
      - name: tazuna apply
        run: tazuna apply -f tazuna.yaml

ポイント:

  • tazuna apply の current-context は kubeconfig の current-context そのものです。 CI で current-context を切り替えるステップを必ず明示します。
  • tazuna.yamlspec.context_matches を 入れておけば、誤って違うクラスタを向いた kubeconfig で apply しようとしても 即終了します。CI でも有効な保険になります。
  • Tazuna は失敗時に非ゼロ終了するので、CI 側で特別なエラーハンドリングは不要です (CLI - 終了コード)。

applystate sync

CI で回すコマンドには 2 通りあります。

コマンドいつ何が走るかdrift 検知
tazuna applytazuna.yaml で宣言された Manifest を 全部 Manager に通すしない(毎回上書き)
tazuna state syncBuild 結果と State を比較し、差分(added / modified / always-sync)だけ反映する

おおまかな指針:

  • ブートストラップ初期 / Manifest 数が少ない: tazuna apply が単純で予測しやすい。
  • Manifest 数が増えて 1 回の apply が重くなった: tazuna state sync で差分のみに絞る。
  • removed の自動削除が欲しい: state sync + TAZUNA_STATE_SYNC_DELETE=true。誤削除のリスクと相談。

--atomic を使うか

tazuna state sync --atomic を付けると、いずれかのリソースでエラーが出たときに State を更新せず終了します。反映自体は途中まで進む ため、CI で「全部入ったか 何も入らなかったか」の二値にはなりませんが、State 上の整合性は守れます。 詳細は tazuna state sync を参照。

取り外しステージ

CI から tazuna destroy を回す運用は推奨しません。

  • 環境変数 TAZUNA_DESTROY_EXECUTABLE=true を CI に常設すると、誤発火時のガードが プロンプトしか残らなくなる(しかも CI ではプロンプトに答えられないので、--force と組み合わさると無条件で消えます)。
  • どうしても CI から消す必要がある場合は、手動トリガの専用 workflow を作り、 その job に限って環境変数を渡す構成にします。

詳細は tazuna destroy の運用 を参照してください。

Tag を使った段階的反映

tazuna apply --tags で、CI で反映する Manifest を絞り込めます。 たとえば「インフラレイヤと application レイヤを分けて回す」「実験 add-on を独立した job で回す」といった段階的反映に向きます。

tazuna apply --tags infra        # 先に基盤を入れる
tazuna apply --tags application  # 次にアプリ側

タグの設計は tazuna.yaml 側の manifests[].tags で 行います。

監視・通知との接続

CI とは別系で、tazuna state diff の定期実行を回しておくのが推奨構成です。 詳細は Drift モニタリング を参照してください。 CI で apply の成否を見るだけだと、「tazuna.yaml の更新が無いのに drift が起きている」 ケースを取り逃がします。

関連

リファレンス

このセクションは、Tazuna が受け付ける入力ファイルや CLI、内部データ構造の仕様を、 規約書として参照しやすい形でまとめます。

「なぜそうなっているか」は 概念、 「どう使うか」の手順は ガイド を参照してください。 リファレンスは事実の列挙に徹し、フィールド・型・デフォルト・例を中心に書きます。

一覧

現在掲載しているリファレンスは次のとおりです。 ここから順次、Manifest type 別の詳細や CLI、Test plugin、State の内部構造などを拡充していきます。

  • tazuna.yaml スキーマ — Tazuna への唯一の入力ファイルである tazuna.yaml のトップレベル構造と、 spec.manifests[] / spec.context_matches / includes などの共通フィールドの仕様。
  • tazuna.hint.yaml スキーマ — helmfile Manifest の vars に対する制約を宣言するヒントファイルのスキーマ。 型・必須・条件付き必須・フォーマット検証ルールと、oneof_required などのトップレベルルール。
  • GenesisSecret スキーマ — 外部 Secret ストア(1Password)から Kubernetes Secret を生成するための YAML スキーマ。 type: genesissecret の Manifest として tazuna.yaml から参照されます。
  • Test pluginmanifests[].tests および spec.tests に書く TestPluginSpec の共通フィールドと、 組み込みプラグイン WaitUntil / ExistNonExist の仕様。
  • State の内部構造 — State の保存先(tazuna namespace の ConfigMap)、State key の文字列形式、 ContentHash の計算ルール、Diff type の分類仕様。
  • Manifest type 別kustomize / helmfile / oras / parallel / genesissecret の 5 type について、 path の意味・固有フィールド・apply / destroy / build 時の振る舞いを 1 ページずつ。
  • CLItazuna バイナリのサブコマンド・グローバルフラグ・環境変数の仕様。 各サブコマンドは 1 ページずつに分けて、フラグと振る舞いをまとめています。

読み方の約束

  • フィールド名は YAML での表記(小文字キャメル or スネーク)で示します。
  • 必須 と注記のないフィールドはすべて optional です。
  • 「デフォルト」は値を省略したときに Tazuna が採用する値を示します。 ゼロ値(空文字 / 空スライス / false / 0)はとくに注記しない限りそのまま採用されます。
  • 例示する YAML は最小構成で書きます。 実運用で必要になる追加フィールドは各セクションで個別に説明します。

tazuna.yaml スキーマ

このページは Tazuna への唯一の入力ファイルである tazuna.yaml の仕様をまとめます。 ここでは Manifest type 別の固有フィールド(kustomize / helmfile / genesissecret / parallel / oras)と Test plugin のフィールドには深入りしません。 それらは順次、専用のリファレンスページで扱います。

ルート (Tazuna)

tazuna.yaml のルートオブジェクトです。Kubernetes manifest と同じ apiVersion / kind / spec の 3 つを持ちます。

フィールド必須デフォルト説明
apiVersionstring--設定する場合は tazuna.pepabo.com/v1 と完全一致である必要があります。省略可。
kindstring--設定する場合は Tazuna と完全一致である必要があります。省略可。
specTazunaSpec-Tazuna の振る舞いを定義する本体。

最小例:

apiVersion: tazuna.pepabo.com/v1
kind: Tazuna
spec:
  manifests:
    - name: nginx
      type: kustomize
      path: ./kustomize/nginx

TazunaSpec

フィールド必須デフォルト説明
manifests[Manifest]-Tazuna が順に処理する Manifest の配列。空配列は許容されません。
context_matches[string]-[]現在の kubeconfig context 名がマッチすべき正規表現の配列。空でなければ apply / destroy 前に評価されます。
context_match_modestring-orcontext_matches の評価モード。or(いずれかに一致)または and(すべてに一致)。
tests[TestPluginSpec]-[]すべての Manifest 適用後に実行される Test plugin の配列。

context_matches

  • 各要素は Go の regexp パッケージでコンパイル可能な正規表現でなければなりません。 コンパイルに失敗すると tazuna check の段階で弾かれます。
  • 空配列または未設定の場合、context のチェックは行われません。
  • 設定されている場合、tazuna apply / tazuna destroy はクラスタに触る前に current-context を検証します。マッチしないと処理を中断します。

context_match_mode

  • or(デフォルト): context_matches のいずれか 1 つにでもマッチすれば OK。
  • and: context_matchesすべて にマッチする必要があります。
  • それ以外の値を指定するとバリデーションエラーになります。

例:

spec:
  context_matches:
    - ^staging-
    - -tokyo$
  context_match_mode: and
  manifests: []

Manifest

spec.manifests[] の各要素です。1 つの Manifest が、1 つのバックエンド (kustomize / helmfile / 他)による「クラスタへ入れる単位」に対応します。

フィールド必須デフォルト説明
namestring-Manifest 識別子。^[a-zA-Z0-9_-]+$ にマッチする必要があり、includes 展開後の全 Manifest 間でユニーク。_metadata は予約済みで使用不可。
descriptionstring-""人間向けの説明。挙動には影響しません。
typestring△ (※)-kustomize / helmfile / genesissecret / parallel / oras のいずれか。
pathstring△ (※)-tazuna.yaml 自身の置かれているディレクトリ起点の相対パス。
tags[string]-[]tazuna apply --tags ... などで絞り込みに使うタグ。OR 評価。
includes[IncludeFile]-[]別の tazuna.yaml を読み込むエントリ。設定時は他の Manifest 固有フィールドは無視されます。詳細は includes を使う を参照。
kustomizeManifestKustomize-nulltype: kustomize のときに参照されるオプション。
helmfileManifestHelmfile-nulltype: helmfile のときに参照されるオプション。
genesisSecretobject-nulltype: genesissecret のときに参照されるオプション。現状は空オブジェクト。
parallelManifestParallel-nulltype: parallel のときに参照されるオプション。children[] に Manifest を入れ子で書く。
orasManifestORAS-nulltype: oras のときに参照されるオプション。
tests[TestPluginSpec]-[]この Manifest の apply 後に実行される Test plugin の配列。

(※) includes を指定するときは type / path は不要。 それ以外のときは typepath が必須です。

name

  • 必須。
  • 使える文字種は ^[a-zA-Z0-9_-]+$
  • _metadata は内部利用のため予約されており、Manifest 名としては使えません。
  • includes 展開後の全 Manifest 間で 一意 である必要があります。 重複していると tazuna check でエラーになります。

tazuna check では name のバリデーションは エラー として扱いますが、tazuna apply / build / destroy では移行期間として 警告ログのみ が出る挙動になっています。 新規導入時は tazuna check で先に通しておくのが安全です。

path

  • includes を使わないときは必須。
  • tazuna.yaml 自身の置かれているディレクトリ起点 の相対パスとして解釈されます。 コマンドを実行した cwd 起点ではありません。
  • tazuna check の時点で実在チェックが行われます。
  • type ごとに path が指すべき先が異なります。
typepath が指す先
kustomizekustomization.yaml を含むディレクトリ
helmfilehelmfile.yaml を含むディレクトリ
genesissecretGenesisSecret 定義 YAML ファイル(ディレクトリではない)
parallel実体としては使用されません。children[] 側の path が使われます。バリデーション都合で空にはできません。
oras実体としては使用されません。バリデーション都合で空にはできないため、適当なディレクトリを書きます。

詳細な解釈は各 Manifest type 別ページ を参照してください。

type

  • includes を使わないときは必須。
  • 値の一覧は Manifest type を参照。
  • 未対応の値を指定するとバリデーションエラーになります。

tags

  • 文字列の配列。Tazuna 自身は内容を解釈しません。
  • --tags フラグでの絞り込み時に、指定されたタグのいずれかが付いている Manifest だけが処理対象になります(OR 評価)。

IncludeFile

manifests[].includes[] の各要素です。別の tazuna.yaml を読み込み、その manifests[] を展開します。

フィールド必須デフォルト説明
pathstring-読み込む tazuna.yaml のパス。呼び出し元の tazuna.yaml からの相対 で書きます。

includes を使う

spec:
  manifests:
    - name: infra
      includes:
        - path: ./infra/tazuna.yaml
        - path: ./addons/tazuna.yaml
  • includes を持つ Manifest は、自身が持つ type / path / tags などの 「Manifest 本体」のフィールドは 無視 されます。
  • includesネスト不可 です。include 先の tazuna.yaml が さらに includes を持っていても展開されません。
  • include 先で定義された Manifest の name も含めて、最終的な全 Manifest 間で name がユニークである必要があります。

Manifest type 別フィールド

type に対応するフィールド(kustomize / helmfile / genesisSecret / parallel / oras)は、それぞれ専用のリファレンスページに切り出しています。

ここでは存在と最低限の役割だけを示します。

フィールド役割
kustomizetype: kustomize 向けオプション。defaultNamespace を持つ。
helmfiletype: helmfile 向けオプション。vars / includeCRDs / wait / kubeVersion などを持つ。
genesisSecrettype: genesissecret 向けの拡張点。現バージョンでは空オブジェクト。
paralleltype: parallel 向けオプション。children[] に Manifest を入れる。
orastype: oras 向けオプション。reference / delegate を持つ。

tests フィールド

spec.tests および manifests[].tests の要素である TestPluginSpec の詳細仕様は Test plugin を参照してください。 ここでは置かれる位置と実行タイミングだけを示します。

  • 全体 tests (spec.tests): すべての Manifest 適用が終わったあとに実行されます。
  • 個別 tests (manifests[].tests): その Manifest の適用直後に実行されます。

バリデーションのまとめ

tazuna checktazuna.yaml に対して行う検証を一覧にしておきます。 クラスタには触れず、ここで失敗するものはすべて事前に弾けます。

  • apiVersion / kind を設定するなら値が正規値と完全一致すること。
  • spec.manifests[] の各要素について:
    • includes が無い場合: pathtype が設定されていること。
    • type が既知の値(kustomize / helmfile / genesissecret / parallel / oras)であること。
    • path の指す場所が実在すること。
  • spec.manifests[].name が必須・使用可能文字・ユニーク・予約語禁止を満たすこと。
  • spec.context_matches が正規表現としてコンパイル可能であること。
  • spec.context_match_modeor / and / 未設定のいずれかであること。
  • type: helmfile の場合: helmfile.vars の各値が env / static / op の いずれかを満たすこと(詳細は helmfile のリファレンスページ)。
  • type: parallel の場合: parallel.children[] が空でなく、各 child も妥当な Manifest であること。
  • type: oras の場合: oras.reference が必須、oras.delegate.typehelmfile / kustomize のいずれかであること。
  • includes を指定する場合: 各 include.path が必須で、ファイルが実在すること。

tazuna.hint.yaml スキーマ

tazuna.hint.yaml は、type: helmfile の Manifest で使う vars に対して 「どんな値が取りうるか」「どれが必須か」 を宣言的に縛るためのヒントファイルです。 helmfile Manifest と同じディレクトリに置き、Tazuna が vars を解決する過程で参照します。

tazuna.yaml 本体のスキーマは tazuna.yaml スキーマ を参照してください。

置く場所と読み込み

Tazuna は helmfile Manifest の path ディレクトリ直下 から tazuna.hint.yaml を探します。

  • ファイル名は tazuna.hint.yaml 固定。
  • 存在しなければそのまま無視されます(後方互換のため、エラーにはなりません)。
  • 1 つの helmfile Manifest につき高々 1 つです。

helmfile 以外の Manifest type からは読まれません。

ルート (TazunaHint)

フィールド必須デフォルト説明
apiVersionstring--スキーマバージョンを示します。値は現状検証されません。
kindstring--リソース種別を示します。値は現状検証されません。
varsmap<string, HintVar>-varName をキーとする宣言の集合。
rules[HintRule]-[]var 横断のトップレベルバリデーションルール。

apiVersion / kind値の検証は行われません が、慣習として apiVersion: tazuna.pepabo.com/v1 / kind: TazunaHint を書いておくと、 後で検証が入った場合にも揃えやすくなります。

最小例:

apiVersion: tazuna.pepabo.com/v1
kind: TazunaHint
vars:
  clusterName:
    type: string
    required: true
  replicas:
    type: string
    default: "3"

HintVar

vars の各エントリ(var 1 つ分の宣言)です。

フィールド必須デフォルト説明
typestring-var の型。string / slice / map のいずれか。
requiredbool-falseユーザーから必ず提供されなければならないかどうか。
defaultany-null未提供時に注入する値。required: true との併用不可。
descriptionstring-""人間向けの説明。挙動には影響しません。
formatstring-""値のフォーマット検証ルール。type: string のときだけ指定可能。詳細は format 参照。
required_with[string]-[]「ここで挙げた var のいずれかが提供されているとき、この var も必須」を意味する条件付き必須。required: true との併用不可。参照先は vars に存在しなければなりません。
required_without[string]-[]「ここで挙げた var が すべて 未提供のとき、この var が必須」を意味する条件付き必須。required: true との併用不可。参照先は vars に存在しなければなりません。

値が未提供のときの振る舞い

helmfile Manifest 側の vars 解決後、各 var に対して次の順で処理されます。

  1. すでに値が提供されていれば、そのまま採用する。
  2. 提供されておらず required: true ならエラー。
  3. default があればその値を注入する。
  4. それ以外は 型ごとのゼロ値 を注入する(string""slice[]map{})。

条件付き必須(required_with / required_without)の判定は、上記のゼロ値注入の結果ではなく、 ユーザーから明示的に提供された値の集合 に対して行われます。 ゼロ値が入った var が「提供済み」と誤判定されないようにするための実装です。

format

formattype: string の var に対する文字列フォーマット検証です。 値が空文字列(ゼロ値注入を含む)の場合は検証はスキップ されます。 非空文字列が入っているときだけ検証が走ります。

検証
hostnameRFC 952 / 1123 に準拠したホスト名パターン(英数字 / - / .、各ラベルは英数字で開始・終了)
urlnet/url.ParseRequestURI で解釈でき、かつ scheme が空でないこと
email簡易的な user@domain.tld 形式(簡易正規表現)
ipnet.ParseIP で解釈できる IPv4 / IPv6 アドレス
cidrnet.ParseCIDR で解釈できる CIDR 表記
uuidRFC 4122 形式(ハイフン区切り)
semverセマンティックバージョン。v 接頭辞はオプション。pre-release / build metadata まで対応
datetimetime.RFC3339 形式の日時文字列

ここに無い値を指定するとバリデーションエラーになります。

HintRule

rules は var 横断のバリデーションを宣言するためのトップレベルルールです。 個別 var の処理が一通り終わったあとに評価されます。

フィールド必須デフォルト説明
typestring-ルール種別。現状は oneof_required のみ。
vars[string]-ルールが対象とする var 名の配列。2 件以上 が必要。参照先は vars に存在しなければなりません。
messagestring-""バリデーションエラー時に表示するカスタムメッセージ。

oneof_required

vars に挙げた var のうち、少なくとも 1 つがユーザーから提供されていなければエラー、というルールです。

rules:
  - type: oneof_required
    vars:
      - certManagerIssuerName
      - certManagerClusterIssuerName
    message: "either certManagerIssuerName or certManagerClusterIssuerName must be set"

判定基準は条件付き必須と同じく、ゼロ値注入前の ユーザー提供 の値の集合です。 default で値が入った var は「提供された」とは扱われません。

バリデーション

tazuna.hint.yaml がロードされると、まずスキーマレベルで次の検証が行われます。

  • vars[*].typestring / slice / map のいずれかであること。
  • vars[*].required: truevars[*].default を併用していないこと。
  • vars[*].format を指定しているときに type: string であること。
  • vars[*].format が既知の値であること(format 参照)。
  • vars[*].required_with / vars[*].required_without の参照先が vars に存在すること。
  • vars[*].required: truerequired_with / required_without を併用していないこと。
  • rules[*].type が既知の値であること(現状は oneof_required のみ)。
  • rules[*].vars の長さが 2 以上であること。
  • rules[*].vars の参照先が vars に存在すること。

それらを通過したのち、helmfile Manifest の vars 解決結果に対して 次の検証が走ります(値が未提供のときの振る舞いformat を参照)。

  • hint で宣言された型と、helmfile 側に渡された値の型が整合すること (例: type: slice と宣言した var に staticMap が来たらエラー)。
  • required: true の var に値が来ていること。
  • required_with / required_without の条件を満たしていること。
  • format を持つ string var の値が、空文字列でない場合に format パターンを満たしていること。
  • rules を満たしていること(oneof_required の少なくとも 1 つが提供されていること)。

関連

GenesisSecret スキーマ

GenesisSecret は、外部の Secret ストア(現バージョンでは 1Password)にある秘匿情報を取得し、 Kubernetes Secret として 生成 するための宣言です。

GenesisSecret は Kubernetes の CRD ではなく、Tazuna が読む YAML スキーマ です。 クラスタに GenesisSecret リソースが現れるわけではなく、適用結果として Secret が現れます。

tazuna.yaml からは type: genesissecret の Manifest として参照します。

# tazuna.yaml
spec:
  manifests:
    - name: aws-credentials
      type: genesissecret
      path: ./genesissecrets/aws.yaml

type: genesissecretpathYAML ファイル 1 つを直接指します (他の Manifest type のようにディレクトリを指すのではありません)。

ルート (GenesisSecret)

フィールド必須デフォルト説明
apiVersionstring--スキーマバージョンを示します。値は現状検証されません。
kindstring--リソース種別を示します。値は現状検証されません。
specGenesisSecretSpec-GenesisSecret 本体。

apiVersion / kind のフィールドは構造体に対応する宣言がなく、書いても 読まれずに無視されますが、慣習として apiVersion: tazuna.pepabo.com/v1 / kind: GenesisSecret を書いておくと、後から検証が入っても揃えやすくなります。

GenesisSecretSpec

フィールド必須デフォルト説明
providerstring-""取得元 Provider の指定。現バージョンの Manager は値を参照していません。 Provider は Tazuna 全体で 1 つ(1Password 向け実装)が組み込まれていて、tazuna apply の起動時に決定されます。
secrets[GenesisSecretGenerate]-取得対象。複数書けます。
outputs[GenesisSecretOutput]-出力先。複数書けます。

GenesisSecretGenerate

secrets[] の各要素です。1 つの「Provider 上のアイテム」を表します。

フィールド必須デフォルト説明
uristring-Provider 上のアイテムを指す URI。詳細は uri の形式 参照。
itemsmap<string, GenesisSecretGenerateItem>-Provider から取得した key と、出力 Secret 上のキー名の対応表。
preferLabelbool-falseProvider が返したフィールドを ラベル名 でキー化するかどうか。false のときは ID(ランダム文字列になる場合がある)でキー化されます。1Password で人間が付けたフィールド名を items のキーに書きたい場合は true にします。

uri の形式

1Password Provider では、url.Parse の結果のうち path の 1 つ目を vault 名、2 つ目を item 名 として解釈します。scheme やホストは現バージョンでは使われません。

tazuna secret-to-genesissecret が自動生成するときは次の形式で書き出します。

op://<op-host>/<vault>/<item>

例:

uri: op://example.1password.com/Platform/aws-credentials

scheme やホストはパースには通りますが、参照されません。 将来別 Provider を増やしたときに使い分けるためのスペースとして残されている、と理解しておくと安全です。

GenesisSecretGenerateItem

items マップの にあたる構造です(キーは Provider から返ってきた field の ID または label)。

フィールド必須デフォルト説明
mapTostring-出力先 Kubernetes Secret の data キー名。Provider から取得した値はこのキー名で Secret に格納されます。

例:

items:
  accessKeyID:
    mapTo: AWS_ACCESS_KEY_ID
  secretAccessKey:
    mapTo: AWS_SECRET_ACCESS_KEY

items のキー accessKeyID が Provider 上のフィールド名(preferLabel: true ならラベル名)に対応し、 mapTo がそのまま Kubernetes Secret のキー名になります。 items のキーが Provider 側に存在しないとエラーになります。

GenesisSecretOutput

outputs[] の各要素です。1 つの「出力先」を表します。

フィールド必須デフォルト説明
kubernetesSecretGenesisSecretOutputKubernetesSecret△ (※)null出力先として Kubernetes Secret を作る場合に指定します。
stdoutobject-nullスキーマ上は存在しますが 現バージョンでは未対応 です。kubernetesSecretnull の場合は実行時にエラーになります。

(※) 現バージョンでは outputs[] の各要素は kubernetesSecret を必須 とします。 構造体上は stdout も存在しますが、kubernetesSecret == nil だと .spec.output currently supports only KubernetesSecret というエラーで失敗します。

GenesisSecretOutputKubernetesSecret

フィールド必須デフォルト説明
namespacestring-出力する Secret の namespace。
namestring-出力する Secret の name。
labelsmap<string, string>-null出力する Secret に付ける labels。
annotationsmap<string, string>-null出力する Secret に付ける annotations。
typestring-Opaquecorev1 の SecretType。空文字列のときは Opaque(厳密には kubernetes.io/opaque ではなく Kubernetes のデフォルト Opaque)として扱われます。kubernetes.io/tls などを指定できます。
contextstring-""構造体上は存在しますが、現バージョンの Manager 実装では参照されません。 出力先クラスタは Tazuna 全体の current-context が使われます。

解決の流れ

tazuna apply 時、type: genesissecret の Manifest は次のように処理されます。

  1. manifests[].path の指す YAML ファイルtazuna.yaml 自身のディレクトリ起点)を読む。
  2. spec.secrets[] の各要素を Provider に渡し、フィールド集合を取得する。
  3. itemsmapTo でキー名をリネームしながら、すべての secrets[] の結果を 1 つの map[string]string にマージする(同じキーが衝突した場合は 後勝ち)。
  4. spec.outputs[] の各 kubernetesSecret について、namespace / name を持つ Kubernetes SecretCreateOrUpdate する。
    • StringData にマージ済みの map がそのまま入る。
    • labels / annotations / type は宣言どおりに付与される。

tazuna destroy 時も同じ Provider 取得が走り、outputs[].kubernetesSecretnamespace / name で示される Secret を削除します。

tazuna build 時は、出力対象が outputs[0].kubernetesSecret 1 件分の Secret YAML として 標準出力に書き出されます(複数 outputs を書いていても、build では先頭 1 件のみが対象)。

State と always-sync

GenesisSecret から生成される Secret は、tazuna state diff 上で常に always-sync 分類になります。 ContentHash で差分判定できる対象ではなく、Provider 側を真実の源として 毎回同期する扱いです。詳細は Diff type / always-sync を参照してください。

最小例:

apiVersion: tazuna.pepabo.com/v1
kind: GenesisSecret
spec:
  secrets:
    - uri: op://example.1password.com/Platform/aws-credentials
      preferLabel: true
      items:
        accessKeyID:
          mapTo: AWS_ACCESS_KEY_ID
        secretAccessKey:
          mapTo: AWS_SECRET_ACCESS_KEY
  outputs:
    - kubernetesSecret:
        namespace: default
        name: aws-credentials

type: kubernetes.io/tls を出力する例:

apiVersion: tazuna.pepabo.com/v1
kind: GenesisSecret
spec:
  secrets:
    - uri: op://example.1password.com/Platform/tls-wildcard
      preferLabel: true
      items:
        certificate:
          mapTo: tls.crt
        privateKey:
          mapTo: tls.key
  outputs:
    - kubernetesSecret:
        namespace: ingress-nginx
        name: wildcard-tls
        type: kubernetes.io/tls
        labels:
          managed-by: tazuna

関連

Test plugin

Test plugin は、Manifest の適用前後にクラスタの状態を検証するための仕組みです。 tazuna.yaml に書き、tazuna apply のフローに組み込まれます。

このページでは Test plugin の YAML スキーマと、組み込みの 2 種(WaitUntil / ExistNonExist) の仕様をまとめます。

配置と実行タイミング

Test plugin は tazuna.yaml の 2 箇所に書けます。

配置箇所実行タイミング
manifests[].testsその Manifest を apply した 直後 に実行
spec.testsすべての Manifest の apply が終わったあと に実行

tazuna build / tazuna check / tazuna state diff などクラスタを変更しない コマンドでは Test plugin は実行されません。tazuna destroy 時にも実行されません。

TestPluginSpec(共通フィールド)

manifests[].testsspec.tests の各要素は同じ構造 TestPluginSpec を取ります。

フィールド必須デフォルト説明
typestring-プラグイン種別。WaitUntil または ExistNonExist(大文字小文字をそのまま)。
waitUntilWaitUntilArgs△ (※)nulltype: WaitUntil のときに必須。
existNonExistExistNonExistArgs△ (※)nulltype: ExistNonExist のときに必須。
minConsecutiveSuccessCountint-1テスト関数が連続でこの回数だけ成功したら、テストプラグイン全体を 成功 とする。0 を指定したときも内部で 1 に補正されます。
minConsecutiveFailureCountint-0テスト関数が連続でこの回数だけ失敗したら、テストプラグイン全体を 失敗 として打ち切る。0 のときはこのチェックは行われず、timeoutSeconds のみが打ち切り条件になります。
timeoutSecondsint-実質無限全体タイムアウト秒。指定時間を超えると失敗。0(未指定)のときは実質無期限(内部で約 280 日が設定されます)。
intervalSecondsint-0テスト関数の再実行間に挟む待機秒。0 のときは即座に再実行されます。

(※) typewaitUntil / existNonExist の対応関係は実行時に検証されます。 waitUntil を指定して type: ExistNonExist を書く、といった食い違いがあると実行時にエラーになります。

評価ループの挙動

すべての Test plugin は次のループで動きます。

  1. テスト関数(プラグイン固有のロジック)を 1 回実行する。
  2. 結果(成功/失敗)を履歴に追記する。
  3. 直近 minConsecutiveSuccessCount 件がすべて成功なら 成功 として終了。
  4. minConsecutiveFailureCount が 0 でなく、直近の minConsecutiveFailureCount 件が すべて失敗なら 失敗 として終了。
  5. timeoutSeconds が経過したら 失敗 として終了。
  6. intervalSeconds 秒スリープして 1 に戻る(0 なら即時)。

「1 回成功すれば OK」「再試行間隔は 1 秒」「最大 60 秒で打ち切り」を表現したい場合は次のようになります。

tests:
  - type: WaitUntil
    timeoutSeconds: 60
    intervalSeconds: 1
    waitUntil:
      # ...

WaitUntilArgs

指定したリソースが「目的の状態」になるまでループで待つプラグインです。 判定条件は CEL 式で表現します。

フィールド必須説明
resource.apiVersionstring対象リソースの apiVersion。例: apps/v1cert-manager.io/v1
resource.kindstring対象リソースの kind。例: DeploymentCertificate
namespacestring対象リソースの namespace。
namestring対象リソースの name。
conditionstring真偽を返す CEL 式。式の中では object という名前で、取得したリソースが unstructured な map として参照できます。式の評価結果は bool 型でなければなりません(コンパイル時に型チェックされます)。

各イテレーションは「リソースを Get → CEL 式を評価」の組み合わせで動きます。 リソースの取得に失敗した(404 を含む)回もそのループの「失敗」として扱われます。

代表的な condition 例:

# Deployment が要求どおり Ready になっている
condition: "object.status.readyReplicas == object.spec.replicas"

# Available conditions が True になっている(条件 list 評価)
condition: >-
  object.status.conditions.exists(c,
    c.type == "Available" && c.status == "True")

CEL 式自体の言語仕様は CEL の公式ドキュメントを参照してください。 Tazuna は object 変数の追加と「結果は bool」という制約だけを掛けています。

例(WaitUntil)

tests:
  - type: WaitUntil
    timeoutSeconds: 120
    intervalSeconds: 2
    waitUntil:
      resource:
        apiVersion: apps/v1
        kind: Deployment
      namespace: ingress-nginx
      name: ingress-nginx-controller
      condition: "object.status.readyReplicas == object.spec.replicas"

ExistNonExistArgs

指定したリソースが「存在しているべき」または「存在していないべき」を表明するプラグインです。

フィールド必須説明
resource.apiVersionstring対象リソースの apiVersion。
resource.kindstring対象リソースの kind。
namespacestring対象リソースの namespace。
namestring対象リソースの name。
shouldExistbooltrue のとき、リソースが存在すれば成功。false のとき、存在しなければ成功。

判定は 1 回の Get 結果で行われます。 GetNotFound を返すかどうかで存在判定し、shouldExist と突き合わせます。 NotFound 以外のエラー(権限不足など)はそのイテレーションの「失敗」として扱われます。

例(ExistNonExist)

tests:
  # 想定したリソースが入ったことの表明
  - type: ExistNonExist
    existNonExist:
      resource:
        apiVersion: apps/v1
        kind: Deployment
      namespace: tazuna-managed
      name: nginx
      shouldExist: true

  # 廃止したリソースが残っていないことの表明
  - type: ExistNonExist
    timeoutSeconds: 10
    intervalSeconds: 1
    existNonExist:
      resource:
        apiVersion: v1
        kind: Secret
      namespace: tazuna-managed
      name: legacy-token
      shouldExist: false

関連

State の内部構造

State は、Tazuna が「自分が入れたリソース」を追跡するための記録です。 クラスタ内の ConfigMap に保存され、tazuna state list / tazuna state diff / tazuna state sync の起点になります。

このページでは State の保存形式と、それを支える State key / ContentHash / Diff type の 仕様をまとめます。

保存場所

State は クラスタ内 に保存されます。Tazuna のローカルファイルやリモートストレージは使いません。

要素
Namespacetazuna(無ければ自動作成される)
ConfigMap 名tazuna-state-<manifest-name>
形式ConfigMap.data に key/value で 1 リソース 1 エントリ

1 つの Manifest が 1 つの ConfigMap に対応します。 includes 展開後の各 Manifest 名はユニーク(tazuna.yamlname 参照)なので、 ConfigMap 名は衝突しません。

State key

State key は、ConfigMap 内の 1 エントリを指す識別子です。 構造体としては manifestName / group / version / kind / namespace / name を持ち、 文字列化のフォーマットは次の 2 通りです。

リソースのスコープ形式パート数
namespaced{manifest}/{group}/{version}/{kind}/{namespace}/{name}6
cluster-scoped{manifest}/{group}/{version}/{kind}/{name}5

group は core group("")の場合は空セグメントになります。 例: core/v1 の ConfigMap(namespaced)であれば、my-manifest//v1/ConfigMap/default/my-cm のように 2 つ目のスラッシュ間が空になります。

ConfigMap data key へのエンコード

Kubernetes の ConfigMap.data の key は [-._a-zA-Z0-9]+ しか許さず、/ を含められません。 そのため Tazuna は ConfigMap への書き込み時に State key 文字列の /__ に置換します。 読み込み時には逆変換します。

state key:    nginx/apps/v1/Deployment/default/nginx
data key:     nginx__apps__v1__Deployment__default__nginx

Kubernetes の DNS-1123 名(manifest 名 / group / namespace / name)に _ は含まれないため、 __ は安全なセパレータとして使えます。

State エントリ (StateEntry)

各エントリは ConfigMap 上 1 つの value として、次の JSON 形で書き込まれます。

{"contentHash":"<hex sha256>"}
フィールド説明
contentHashstringリソースの内容に対する SHA-256 ハッシュ。詳細は ContentHash 参照。

_metadata キー

ConfigMap には予約キー _metadata がもう 1 つ入ります。 これは State エントリではなく、State 全体のメタ情報です。

{"gitCommitHash":"<sha>","lastSyncedAt":"<rfc3339>"}
フィールド説明
gitCommitHashstring同期時に記録された git commit hash。
lastSyncedAtstring同期時刻。

Manifest の name には _metadata は使えません (tazuna.yamlname で予約語として扱われています)。 この衝突を避けるためのガードです。

ContentHash

ContentHash は、各リソースの YAML 表現から計算される SHA-256 の hex 文字列です。 Tazuna は server-side で付与される fieldstatus を除いて計算します。

除外するフィールド:

フィールド除外理由
metadata.resourceVersionクラスタの世代管理用、適用ごとに変動するため
metadata.uidクラスタ採番、適用ごとに変動するため
metadata.creationTimestampクラスタ採番、適用ごとに変動するため
metadata.generationクラスタ採番、適用ごとに変動するため
metadata.managedFieldsserver-side apply の追跡情報
metadata.selfLinkクラスタ採番
statusコントローラが書く動的フィールド

計算手順:

  1. オブジェクトを JSON マーシャル / アンマーシャルして deep copy する。
  2. 上記の除外対象フィールドを削除する。
  3. 残りを JSON マーシャルする。
  4. SHA-256 を取り hex 文字列化する。

status を含めると Pod の再起動などで毎回ハッシュが変わってしまうので、 tazuna.yaml から見て同じ状態か」 を判定できる粒度に絞っています。

Diff type

tazuna state difftazuna state sync は、Build 結果と既存 State の比較結果を Diff type で分類します。

Diff type意味state sync の挙動
addedBuild 結果に存在し、State に無い反映する
modified両方に存在するが、ContentHash が異なる反映する
removedState に存在し、Build 結果に無いデフォルトはスキップ。TAZUNA_STATE_SYNC_DELETE=true のときだけ削除
always-sync差分計算をスキップし、常に同期扱いとする反映する

state diff の出力は addedmodifiedremovedalways-sync の順、 同 Diff type 内は State key 昇順で安定ソートされます。

always-sync の対象

現バージョンでは、type: genesissecret 由来の Secret が always-sync 分類になります。 これらは Provider 側を真実の源として毎回同期する性質を持ち、ContentHash で差分判定できる対象ではありません。 詳細は GenesisSecret スキーマ - State と always-sync を参照してください。

関連

Manifest type 別リファレンス

tazuna.yamlmanifests[].type には 5 種類の値を取れます。 このセクションでは type ごとの path が指す先固有フィールドapply / destroy / build 時の振る舞い を 1 ページずつにまとめます。

Manifest の共通フィールド(name / path / type / tags / includes / tests)の仕様は tazuna.yaml スキーマ - Manifest を参照してください。

一覧

  • kustomize — kustomize でレンダリングしたリソースを反映する
  • helmfile — helmfile template の結果を反映する
  • oras — OCI registry から artifact を pull し、helmfile / kustomize に委譲する
  • parallel — 子 Manifest を並列に処理する
  • genesissecret — GenesisSecret YAML から Kubernetes Secret を生成する

type と固有フィールドの対応

各 type は manifests[] 内で 対応するオプションオブジェクト を持ちます。 type と対応するフィールドだけが読まれ、他は無視されます。

type固有フィールド名フィールド型
kustomizekustomizeManifestKustomize
helmfilehelmfileManifestHelmfile
orasorasManifestORAS
parallelparallelManifestParallel
genesissecretgenesisSecret空オブジェクト(現バージョンではフィールドを持たない)

type: genesissecret は全部小文字ですが、対応するフィールド名は genesisSecret(camelCase)です。 YAML キーは camelCase に統一されており、type の値だけがプレーンな識別子(全部小文字)になっています。

type と path の対応

pathtazuna.yaml 自身の置かれているディレクトリ起点 の相対パスとして解釈されます。 type ごとに何を指すべきかが異なります。

typepath が指す先
kustomizekustomization.yaml を含むディレクトリ
helmfilehelmfile.yaml を含むディレクトリ
oras実体としては使用されません。バリデーション都合で空にはできないため、適当なディレクトリを書きます。
parallel実体としては使用されません。children[] 側の path が使われます。バリデーション都合で空にはできません。
genesissecretGenesisSecret YAML ファイル(他の type と違い、ディレクトリではなく単一ファイル)

type: kustomize

kustomize Manifest は、kustomize でレンダリングした Kubernetes manifest をクラスタへ反映します。 Tazuna は内部で kustomize (sigs.k8s.io/kustomize) を呼び、kustomize build <path> 相当の 結果を生成して使います。

path

kustomization.yaml が置かれているディレクトリを指します。 tazuna.yaml 自身のディレクトリ起点 の相対パスで書きます。 ディレクトリ内に妥当な kustomization.yaml が無いと tazuna build / apply が失敗します。

固有フィールド

manifests[].kustomize のオブジェクトに書きます。

フィールド必須デフォルト説明
defaultNamespacestring-""レンダリング結果のリソースで metadata.namespace が未指定のものに付与する namespace。空のときはリソース側に書かれた namespace(無ければ Kubernetes 既定の default)が使われます。

振る舞い

操作内部処理
Buildkustomize build <path> 相当のレンダリングを行い、結果 YAML を標準出力に書く。
Applyレンダリング結果を unstructured オブジェクト群に変換し、defaultNamespace を補完したうえで 1 つずつ CreateOrUpdate する。
Destroyレンダリング結果を unstructured オブジェクト群に変換し、defaultNamespace を補完したうえで 1 つずつ削除する。

kustomize build 自体はクラスタへの接続を必要としません。 ローカルファイルだけで完結します。

manifests:
  - name: ingress-nginx
    type: kustomize
    path: ./kustomize/ingress-nginx
    tags:
      - infra

  - name: app-overlay
    type: kustomize
    path: ./kustomize/app/overlays/staging
    kustomize:
      defaultNamespace: staging

関連

type: helmfile

helmfile Manifest は、helmfile で記述された複数の Helm release を、 helmfile template 相当でレンダリングしてからクラスタへ反映 する Manifest type です。

Tazuna は内部で helmfile/helmfile パッケージの app.Template を呼び、その出力 YAML を unstructured オブジェクトに変換して CreateOrUpdate します。helm の release 履歴は クラスタ側に保存しません(helm rollback は使えなくなります)。 ブートストラップにおいては rollback よりも宣言的な再生成を優先する、というスタンスです。

path

helmfile.yaml(または helmfile.yaml.gotmpl などの helmfile が認識するファイル)が 置かれているディレクトリを指します。 tazuna.yaml 自身のディレクトリ起点 の相対パスで書きます。

固有フィールド

manifests[].helmfile のオブジェクトに書きます。

フィールド必須デフォルト説明
varsmap<string, HelmFileVar>-{}helmfile に渡す変数。詳細は vars 参照。
includeCRDsbool-falsehelmfile template に --include-crds 相当を渡します。
defaultNamespacestring-""レンダリング結果のリソースで metadata.namespace が未指定のものに付与する namespace。
extraValueFiles[string]-[]helmfile template に追加で渡す --values ファイル群。
waitbool-falsetrue のとき、Apply 後に対象リソースが Ready になるまで待ちます。詳細は wait の挙動 参照。
timeoutSecondsint-0wait の最大待機秒数。0 のときは内部で 300 秒(5 分)が使われます。
kubeVersionstring-""helmfile template に渡す --kube-version の値。

vars

vars のキーは helmfile 側の変数名、値が HelmFileVar です。

tazuna.yaml のロード時、vars は次の順序で解決されます。

  1. 各 var の from (env / static / op) に応じて値を取得する。
  2. 同じディレクトリに tazuna.hint.yaml があれば、tazuna.hint.yaml の 検証・デフォルト注入を行う。

vars に書かないでも tazuna.hint.yamldefault から値が入る、というケースもあります。 逆に tazuna.hint.yaml の制約に違反するとここでエラーになります。

HelmFileVar

フィールド必須説明
fromstring値の取得元。env / static / op のいずれか。
envstring△ (※)from: env のとき必須。参照する環境変数名。
staticstring△ (※)from: static のときに使う。スカラー値。
staticSlice[string]△ (※)from: static のときに使う。スライス値。
staticMapmap<string, string>△ (※)from: static のときに使う。マップ値。
opOnePasswordVaultSelector△ (※)from: op のとき必須。

(※) from の値に応じて、env / static 系のいずれか 1 つ / op が必須です。 from: static の場合、static / staticSlice / staticMap のうち 1 つだけ が設定されている必要があります。

OnePasswordVaultSelector

フィールド必須説明
keystringフィールドを id で参照するか label で参照するか。id または label
vaultstring1Password の Vault 名。
itemstring1Password の Item 名。
fieldstring取得するフィールド。keyid ならフィールドの ID、label ならラベル。

wait の挙動

wait: true のとき、Apply の終わりに対象リソース全部の Ready 待ちが走ります。 2 秒間隔で polling し、timeoutSeconds(未指定なら 300 秒)を超えるとエラー。

各 Kind の Ready 判定:

KindReady 条件
Deploymentspec.replicas == 0 なら即 Ready。それ以外は status.readyReplicas == status.replicas かつ status.availableReplicas == status.replicas かつ status.replicas > 0
StatefulSetspec.replicas == 0 なら即 Ready。それ以外は status.readyReplicas == status.replicas かつ status.replicas > 0
DaemonSetstatus.numberReady == status.desiredNumberScheduled かつ status.desiredNumberScheduled > 0
Podstatus.phase == "Running" かつ Ready condition が True
その他取得できれば即 Ready 扱い(ConfigMap / Secret / Service など)

wait で待ちきれないリソース固有の条件(CRD の status など)を表現したい場合は、 Test pluginWaitUntil(CEL 式)を使うほうが柔軟です。

振る舞い

操作内部処理
Buildhelmfile template の出力 YAML を標準出力に書く。
Applyhelmfile template の結果を unstructured 化し、defaultNamespace を補完して順に CreateOrUpdatewaittrue なら Ready 待ち。
Destroyhelmfile template の結果を unstructured 化し、defaultNamespace を補完して順に削除。wait は適用されません。

Apply / Destroy / Build のいずれも helmfile template の段階で vars を解決します。 解決に失敗した場合(環境変数未設定、1Password の Item にフィールドが無い等)はクラスタには触れずに失敗します。

manifests:
  - name: cert-manager
    type: helmfile
    path: ./helmfile/cert-manager
    helmfile:
      includeCRDs: true
      wait: true
      timeoutSeconds: 120
      vars:
        clusterIssuerEmail:
          from: env
          env: CLUSTER_ISSUER_EMAIL
        dnsProviderApiToken:
          from: op
          op:
            key: label
            vault: Platform
            item: cert-manager
            field: cloudflare-api-token
        extraLabels:
          from: static
          staticMap:
            managed-by: tazuna
            tier: platform

関連

type: oras

oras Manifest は、OCI registry に置かれた artifact を pull し、その内容を helmfile または kustomize委譲して クラスタへ反映する Manifest type です。

pull → 展開 → 委譲先 Manager の呼び出し、までを ORAS Manager がまとめて行います。 artifact の中身そのものは helmfile / kustomize と同じ作法で書きます。

path

ORAS Manifest の manifests[].path実体としては使用されません。 バリデーション都合で空にはできないため、何かしらのディレクトリを書きます。

委譲先 Manager に渡される path は、pull 後にローカルへ展開された キャッシュディレクトリ(必要なら target でサブパスへ降りたもの)になります。

固有フィールド

manifests[].oras のオブジェクトに書きます。

フィールド必須デフォルト説明
referencestring-OCI artifact の reference。tag 形式 (ghcr.io/example/foo:v1.0.0) と digest 形式 (ghcr.io/example/foo@sha256:...) の両方を受け付けます。
targetstring-""展開後の artifact root からの相対サブパス。省略時は root を指します。.. などで artifact root を脱出する値は拒否されます。
plainHTTPbool-falsetrue のとき registry への接続を HTTP(非 TLS)で行います。
insecureSkipVerifybool-falsetrue のとき registry 接続時の TLS 証明書検証をスキップします。
authORASAuth-nullregistry の認証情報を override します。省略時は docker config.json を使用します。詳細は 認証の解決 参照。
delegateORASDelegate-pull 後の委譲先 Manager の設定。

ORASAuth

フィールド必須説明
usernamestring-registry のユーザー名。
passwordstring-registry のパスワード。

両フィールドが空のときは override 扱いになりません(認証の解決 参照)。

ORASDelegate

フィールド必須デフォルト説明
typestring-委譲先の Manifest type。helmfile または kustomize
helmfileManifestHelmfile-nulltype: helmfile のときに委譲先へそのまま渡されるオプション。
kustomizeManifestKustomize-nulltype: kustomize のときに委譲先へそのまま渡されるオプション。

振る舞い

操作内部処理
Buildartifact を pull → 委譲先の Build を呼ぶ。
Applyartifact を pull → 委譲先の Apply を呼ぶ。
Destroyartifact を pull → 委譲先の Destroy を呼ぶ。

委譲先には次のように新しい Manifest が組み立てられて渡されます。

  • name / description / tags / tests は元の ORAS Manifest からそのまま引き継ぐ。
  • typedelegate.typehelmfile / kustomize)。
  • path は pull 後のローカルパス(target 指定があれば追加でサブパスを結合)。
  • 固有フィールドは delegate.helmfile / delegate.kustomize をそのまま使う。

Test plugin も同じ Manifest の文脈で評価されます。

Pull とキャッシュ

ORAS の pull は digest 単位でローカルにキャッシュ されます。 同じ digest を持つ artifact の 2 回目以降の pull は registry にアクセスしません。

  • キャッシュディレクトリ:
    • $XDG_CACHE_HOME が設定されていれば $XDG_CACHE_HOME/tazuna/oras
    • そうでなければ $HOME/.cache/tazuna/oras
  • キャッシュ構造:
    • blobs/<sanitized digest>/ 配下に artifact を展開する
    • refs/<sanitized reference> に tag → digest のマッピングを記録する
  • --no-cacheapply / build / destroy に指定するとキャッシュを無視して常に registry から再取得します。
  • --offline を指定すると registry へのアクセスを禁止します。キャッシュにヒットしなければエラーになります。
  • --no-cache--offline は同時には指定できません。
  • CLI フラグは apply / build / destroy のページを参照。

展開時の制約

artifact 内の tar 展開には ADR004 で定めた上限 が適用されます。

上限
展開後合計サイズ1 GiB
tar entry 数10000

以下の不正な entry は拒否されます。

  • 絶対パスを含む entry
  • .. で展開先ディレクトリを脱出する entry(zip slip)
  • 展開先を脱出する symlink / hardlink
  • サポート外の type(character device / block device / FIFO 等)

artifact の OCI manifest は layer が 1 つだけ であることが前提です。複数 layer の artifact は受け付けません。

認証の解決

registry に対する credential は次の優先順位で解決されます。

  1. oras.auth の override(username / password の少なくとも一方が非空)
  2. docker の credential store($DOCKER_CONFIG または ~/.docker/config.json
  3. anonymous(認証なし)

oras.auth を書いていても両フィールドが空のときは override 扱いされず、 docker 側へフォールバックします(意図しない anonymous 化を避けるため)。

同一プロセス内では token のキャッシュが共有されるため、同じ registry に対する複数の pull で token を取り直すコストはかかりません。

tag 指定 + helmfile 委譲:

manifests:
  - name: cert-manager
    type: oras
    path: ./oras/cert-manager   # 実体としては使われないが必須
    oras:
      reference: ghcr.io/example/cert-manager-helmfile:v1.14.0
      delegate:
        type: helmfile
        helmfile:
          includeCRDs: true
          wait: true

digest 指定 + kustomize 委譲 + サブパス:

manifests:
  - name: ingress-nginx
    type: oras
    path: ./oras/ingress-nginx
    oras:
      reference: registry.example.com/platform/ingress-bundle@sha256:abc123...
      target: kustomize/ingress-nginx
      auth:
        username: ci-bot
        password: ${REGISTRY_TOKEN}
      delegate:
        type: kustomize
        kustomize:
          defaultNamespace: ingress-nginx

関連

type: parallel

parallel Manifest は、複数の子 Manifest を 並列に処理する ためのコンテナです。 子の typekustomize / helmfile / genesissecret / oras のいずれか (parallel のネストは想定していません)。

path

parallel Manifest 自体の manifests[].path実体としては使用されません。 バリデーション都合で空にはできないため、何かしらのディレクトリを書きます。

実際の処理対象パスは、各 children[].path 側で指定します。

固有フィールド

manifests[].parallel のオブジェクトに書きます。

フィールド必須デフォルト説明
children[Manifest]-並列に処理する子 Manifest の配列。1 件以上 必要。

children[] の各要素は Manifest と同じ構造です。 children[] 内の Manifest が持つ name も、include 展開後の全 Manifest 名と同じ空間で 一意でなければなりません(tazuna check で検証されます)。

振る舞い

操作内部処理
Applychildren[] の各要素に対応する Manager の Applygoroutine で並列 に呼ぶ。エラーは集約して返す。
Destroychildren[] の各要素に対応する Manager の Destroy を並列に呼ぶ。
Buildchildren[] の各要素に対応する Manager の Build を並列に呼び、宣言順を保ったまま \n---\n で結合した文字列を返す。空文字の出力はスキップ。

children[] の処理順序は保証されません。並列で動かして問題ないグループにだけ使ってください。 順序依存(A の CRD を待ってから B を入れる、など)がある場合は、parallel を使わず 通常の manifests[] の宣言順に並べるか、子 Manifest 側の Test plugin で Ready 待ちを表現します。

並列に入れて構わない 2 つの kustomize を 1 つの parallel に束ねる例:

manifests:
  - name: observability
    type: parallel
    path: ./parallel/observability   # 実体としては使われないが必須
    parallel:
      children:
        - name: prometheus
          type: kustomize
          path: ./kustomize/prometheus
          tags:
            - observability
        - name: grafana
          type: kustomize
          path: ./kustomize/grafana
          tags:
            - observability

関連

type: genesissecret

genesissecret Manifest は、別ファイルに書いた GenesisSecret YAML を読み込み、外部 Secret ストア(現バージョンでは 1Password)から取得した値で Kubernetes Secret を生成する Manifest type です。

tazuna.yaml 側でこの Manifest type が担うのは「どの GenesisSecret YAML を読むか」だけです。 中の spec.secrets / spec.outputs などの仕様は GenesisSecret スキーマ を参照してください。

path

他の Manifest type と違い、pathディレクトリではなく YAML ファイル 1 つを直接指しますtazuna.yaml 自身のディレクトリ起点 の相対パスで書きます。

manifests:
  - name: aws-credentials
    type: genesissecret
    path: ./genesissecrets/aws.yaml   # ← ファイルを直接指す

固有フィールド

manifests[].genesisSecret のオブジェクトに書きます。

現バージョンでは フィールドを持たない空オブジェクト です。 将来の拡張のために予約されているフィールド名です。

manifests:
  - name: aws-credentials
    type: genesissecret
    path: ./genesissecrets/aws.yaml
    # genesisSecret: {}  # 現状は中身が空のため書く必要なし

振る舞い

操作内部処理
BuildGenesisSecret YAML を読み込み、Provider から値を取得し、outputs[0].kubernetesSecret 1 件分の Secret YAML を標準出力に書く。
ApplyGenesisSecret YAML を読み込み、Provider から値を取得し、outputs[].kubernetesSecret の各エントリに対して Kubernetes Secret を CreateOrUpdate する。
DestroyGenesisSecret YAML を読み込み(Provider 取得も実行されます)、outputs[].kubernetesSecretnamespace / name に該当する Secret を削除する。

Buildoutputs の先頭 1 件のみを出力する点が Apply と異なります(複数 outputs を 書いていても、tazuna build の出力は 1 件分です)。詳細は GenesisSecret - 解決の流れ を参照してください。

State との関係

type: genesissecret から生成される Secret は、tazuna state diff 上で常に always-sync として扱われます。ContentHash で差分判定する対象ではなく、Provider 側を真実の源として 毎回同期されます。詳細は State の内部構造 - Diff typeGenesisSecret - State と always-sync を参照してください。

関連

CLI

このセクションは tazuna バイナリが提供するすべてのサブコマンドの仕様を、 1 コマンド 1 ページで網羅します。

ページは規約書として読まれることを想定しています。 コマンドの選び方や運用上の使い分けは ガイド を、 そもそも各コマンドが何を解いているかは 概念 を参照してください。

サブコマンド一覧

グローバルフラグ

すべてのサブコマンドが継承する persistent フラグです。

フラグエイリアスデフォルト説明
--file-path-fstringtazuna.yamltazuna.yaml のパス。
--log-level-lstringinfoログレベル。debug / info / warn / error のいずれか。
--version---ルートコマンドにのみ付くフラグ。バージョン情報を出して終了する。tazuna version と等価。

共通の振る舞い

kubeconfig

クラスタアクセスを行うサブコマンドは、起動時に kubeconfig をロードし、 current-context が指すクラスタ に対して動作します。 KUBECONFIG 環境変数や --kubeconfig 相当のフラグは Tazuna 側では持たず、 kubectl と同じ解決ルールに従います。

context_matches の評価

tazuna.yamlspec.context_matches が設定されている場合、 クラスタに触る直前 に current-context 名と照合します。

  • 評価が走るコマンド: apply / destroy
  • 評価が走らないコマンド: build / check / state list / state diff / state sync / tags / version / secret-to-genesissecret

評価モードは spec.context_match_modeor / and、デフォルト or)に従います。 詳細は tazuna.yaml スキーマ - context_matches を参照。

tazuna.yaml のバリデーション

apply / build / destroy / check / tags は、いずれも実行の最初に tazuna.yaml をロードしてバリデーションします。 バリデーションで失敗するとクラスタには触れません。 チェック項目の一覧は tazuna.yaml スキーマ - バリデーションのまとめ を参照。

終了コード

終了コード意味
0成功
非ゼロ失敗。標準エラーに error: ... 形式でエラーが出力されます。

非ゼロ終了は CI でそのまま失敗扱いにできます。 コマンドごとに異なる終了コードを返すような区別は現状ありません。

環境変数

CLI フラグに加えて、Tazuna が参照する環境変数を一覧にしておきます。

環境変数影響するコマンド効果
TAZUNA_DESTROY_EXECUTABLEtruedestroyこれが true に設定されていない限り、destroy は実体の削除を行いません。プロンプトで Yes と答えても、この環境変数が無ければ何も起こりません。
TAZUNA_STATE_SYNC_DELETEtruestate syncこれが true のときだけ、State には残っていてクラスタには無くなったリソース(removed)を削除します。デフォルトは削除をスキップします。
KUBECONFIGパスクラスタを触る全コマンド通常の kubectl と同じ kubeconfig 解決ルールに従います。

tazuna apply

tazuna.yaml で宣言された Manifest 群をクラスタへ反映します。 Tazuna の中心となるコマンドです。

tazuna apply [-f tazuna.yaml] [--tags ...] [--no-cache | --offline]

振る舞い

実行順序は次のとおりです。クラスタに触れるのは 5 以降です。

  1. tazuna.yaml をロードしてバリデーションする。
  2. spec.context_matches が設定されていれば、current-context と照合する。 合致しなければ即終了する。
  3. --tags でフィルタする。
  4. manifests[]宣言順 に走査する。
  5. 各 Manifest を対応する Manager に渡し、クラスタへ反映する。
  6. 各 Manifest の tests を実行する。
  7. すべての Manifest 適用後、spec.tests(全体 Tests)を実行する。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグエイリアスデフォルト説明
--tags-t[]string[]指定したタグのいずれかが付いている Manifest だけを処理対象にします(OR 評価)。
--no-cache-boolfalsetype: oras の Manifest で、キャッシュを使わずに常に registry から再取得します。
--offline-boolfalsetype: oras の Manifest で、registry へのアクセスを禁止します。キャッシュにヒットしなければエラーになります。

--no-cache--offline は同時に指定できません。

tazuna apply -f tazuna.yaml
tazuna apply -f tazuna.yaml --tags web,batch
tazuna apply -f tazuna.yaml --log-level debug

関連

tazuna build

tazuna.yaml で宣言された Manifest 群をレンダリングし、結果を標準出力に書き出します。 クラスタは変更しません。 apply 前のプレビューや、別ツールへのパイプ入力として使います。

tazuna build [-f tazuna.yaml] [--tags ...] [--no-cache | --offline]

振る舞い

  1. tazuna.yaml をロードしてバリデーションする。
  2. --tags でフィルタする。
  3. 各 Manifest を対応する Manager の Build に渡し、結果を連結して標準出力に書き出す。

context_matches の評価は行いません。 クラスタへの reach も Manager の Build 実装次第ですが、 組み込みの Manager は基本的に kubeconfig を要求しません (ORAS の registry pull は別途ネットワークアクセスを行います)。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグエイリアスデフォルト説明
--tags-t[]string[]指定したタグのいずれかが付いている Manifest だけを処理対象にします(OR 評価)。
--no-cache-boolfalsetype: oras の Manifest で、キャッシュを使わずに常に registry から再取得します。
--offline-boolfalsetype: oras の Manifest で、registry へのアクセスを禁止します。キャッシュにヒットしなければエラーになります。

--no-cache--offline は同時に指定できません。

tazuna build -f tazuna.yaml
tazuna build -f tazuna.yaml --tags web
tazuna build -f tazuna.yaml | kubectl diff -f -

関連

tazuna check

tazuna.yaml の妥当性を、クラスタには触れずに検証します。 CI で最初に通す対象に向きます。

tazuna check [-f tazuna.yaml] [--fix]

振る舞い

  1. tazuna.yaml をロードする。
  2. ファイルおよび展開後の manifests[] 全体に対してバリデーションを実行する。
  3. 問題が無ければ ok を標準出力に書き、終了コード 0 で抜ける。
  4. --fix を指定した場合は、name 未設定の Manifest に自動採番した上で tazuna.yaml を書き戻し、fixed: <path> を標準出力に書く。

検証項目の一覧は tazuna.yaml スキーマ - バリデーションのまとめ を参照してください。 クラスタへのアクセスは行いません。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグエイリアスデフォルト説明
--fix-boolfalsename 未設定の Manifest を自動採番し、tazuna.yaml を書き戻します。

--fix はファイルを上書きします。バージョン管理下で実行することを推奨します。

tazuna check
tazuna check -f path/to/tazuna.yaml
tazuna check --fix

関連

tazuna destroy

Tazuna が管理しているリソースをクラスタから削除します。 事故を防ぐために 二段階のガード が掛かっています。

TAZUNA_DESTROY_EXECUTABLE=true tazuna destroy [-f tazuna.yaml] \
  [--tags ...] [--force] [--no-cache | --offline]

振る舞い

  1. tazuna.yaml をロードしてバリデーションする。

  2. spec.context_matches が設定されていれば、current-context と照合する。 合致しなければ即終了する。

  3. --force が無ければ、次のプロンプトを出して Y/N の確認を取る。

    !!! All resources managed by Tazuna will be deleted !!!
    Are you sure you want to delete them?
    
  4. 環境変数 TAZUNA_DESTROY_EXECUTABLEtrue でなければ、 ログだけ出してクラスタには 触れず に終了する。

  5. ガードを通過した場合のみ、--tags フィルタを適用したうえで Manager の Destroy を順に呼び出し、対応リソースをクラスタから削除する。

つまり「プロンプトで Yes」「TAZUNA_DESTROY_EXECUTABLE=true」の 両方 を満たさない限り、 リソースは消えません。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグエイリアスデフォルト説明
--force-boolfalse削除前の確認プロンプトをスキップします。環境変数のガードはスキップしません。
--tags-t[]string[]指定したタグのいずれかが付いている Manifest だけを削除対象にします(OR 評価)。
--no-cache-boolfalsetype: oras の Manifest で、キャッシュを使わずに常に registry から再取得します。
--offline-boolfalsetype: oras の Manifest で、registry へのアクセスを禁止します。

--no-cache--offline は同時に指定できません。

環境変数

環境変数説明
TAZUNA_DESTROY_EXECUTABLEtrueこれが設定されていない限り、destroy は何も削除しません。CI で誤って destroy が走るのを防ぐためのキルスイッチです。

TAZUNA_DESTROY_EXECUTABLE=true tazuna destroy
TAZUNA_DESTROY_EXECUTABLE=true tazuna destroy --tags experimental
TAZUNA_DESTROY_EXECUTABLE=true tazuna destroy --force

関連

tazuna state list

クラスタに保存されている Tazuna の State を読み、 Tazuna 管理下にあるリソースとその content hash を一覧します。

tazuna state list [-f tazuna.yaml]

振る舞い

  1. tazuna.yaml をロードする。
  2. 各 Manifest の name から対応する State ConfigMap (tazuna namespace の tazuna-state-<manifest-name>)を読む。
  3. State に記録されている各リソースの GVK / namespace / name / content hash を 標準出力に整形して出力する。

context_matches の評価は行いません。 クラスタへの read アクセスのみを行い、State を含むいかなるリソースも変更しません。

フラグ

グローバルフラグ 以外に固有フラグはありません。

tazuna state list
tazuna state list -f tazuna.yaml

関連

tazuna state diff

各 Manager の Build 結果と、クラスタに保存されている State を比較し、 リソース単位の差分を出力します。クラスタは変更しません。

tazuna state diff [-f tazuna.yaml]

振る舞い

  1. tazuna.yaml をロードする。
  2. 各 Manifest について、Manager の Build を呼び出して 「いま tazuna.yaml から生成されるべきリソース」を組み立てる。
  3. クラスタ上の State と突き合わせて、リソース単位で次のいずれかに分類して出力する。
Diff type意味
addedBuild 結果には存在し、State には存在しない
modified両方に存在するが、content hash が異なる
removedState にあるが、Build 結果には存在しない
always-sync差分計算をスキップし、常に同期する扱いの分類。type: genesissecret 由来の Secret はここに入る

context_matches の評価は行いません。 クラスタへの read アクセスのみを行い、何も変更しません。

フラグ

グローバルフラグ 以外に固有フラグはありません。

tazuna state diff
tazuna state diff -f tazuna.yaml

関連

tazuna state sync

各 Manager の Build 結果と State を比較し、追加・変更されたリソースだけを クラスタへ反映します。同期に成功したリソースの State は ConfigMap に書き戻されます。

tazuna state sync [-f tazuna.yaml] [--atomic]

振る舞い

  1. tazuna.yaml をロードする。
  2. 各 Manifest について Build を呼び出し、State との差分を計算する。
  3. added / modified / always-sync 分類のリソースをクラスタへ反映する。
  4. removed 分類のリソースは デフォルトではスキップ される。 TAZUNA_STATE_SYNC_DELETE=true を設定したときに限り、削除を行う。
  5. 同期に成功したリソースの State を書き戻す。

--atomic を指定した場合、いずれかのリソースでエラーが発生したときは State をまったく更新せずに終了します(途中まで進んだ反映自体は巻き戻りません)。

context_matches の評価は行いません。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグエイリアスデフォルト説明
--atomic-boolfalseエラーが発生したときに State を更新せずに終了します。

環境変数

環境変数説明
TAZUNA_STATE_SYNC_DELETEtrueremoved 分類のリソースを削除します。設定されていない場合は削除を行いません。

tazuna state sync
tazuna state sync -f tazuna.yaml
tazuna state sync --atomic
TAZUNA_STATE_SYNC_DELETE=true tazuna state sync

関連

tazuna secret-to-genesissecret

クラスタ上の既存 Secret を 1Password に書き出し、 それを参照する GenesisSecret YAML を生成します。 移行 / 棚卸し用の片道のコマンド であり、定常運用で繰り返し叩くものではありません。

tazuna secret-to-genesissecret \
  --op-host <host> \
  [--namespace <ns>] \
  [--label-selector <sel>] [--name-regex <re>] \
  [--vault <vault>] [--note <note>] \
  [--dump-dir <dir>] [--dry-run]

振る舞い

  1. --namespace(デフォルト default)の Secret を --label-selector / --name-regex で絞り込む。
  2. 各 Secret のデータを 1Password の --vault に Item として書き出す。
  3. その Item を参照する GenesisSecret YAML を --dump-dir(デフォルト .)に出力する。
  4. --dry-run のときは、1Password への書き込みも YAML の生成も行わずに、 対象 Secret の選定結果だけを出力する。

tazuna.yaml は読まないので -f / --file-path は無視されます。 グローバルフラグのうち実際に効くのは -l / --log-level だけです。 クラスタへの read と 1Password への write の両方が走るため、 1Password CLI (op) が認証済みであること が前提です。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグデフォルト必須説明
--op-hoststring-1Password サービスアカウント URL のホスト部分(例: example.1password.com)。
--namespacestringdefault-対象 Secret が存在する Kubernetes namespace。シェル補完で実クラスタの namespace を列挙します。
--label-selectorstring""-対象 Secret を絞る label selector。例: app=foo,tier=db
--name-regexstring""-対象 Secret の name に対する正規表現。
--vaultstring""-1Password の vault 名。シェル補完で実 vault を列挙します。
--notestring""-生成される 1Password Item に付ける note。
--dump-dirstring.-生成された GenesisSecret YAML の出力先ディレクトリ。
--dry-runboolfalse-書き込みを行わず、選定結果だけを出力します。

tazuna secret-to-genesissecret \
  --op-host example.1password.com \
  --namespace production \
  --label-selector tazuna.pepabo.com/migrate=true \
  --vault platform \
  --dump-dir ./genesissecrets

tazuna secret-to-genesissecret \
  --op-host example.1password.com \
  --name-regex '^db-.*' \
  --dry-run

関連

tazuna tags

tazuna.yaml で宣言されているタグを一覧します。 タグごとに、そのタグが付いている Manifest の name を表示します。

tazuna tags [-f tazuna.yaml] [--tags ...]

振る舞い

  1. tazuna.yaml をロードしてバリデーションする。
  2. includes 展開後の全 Manifest を走査し、tagsタグ名 → Manifest 名のリスト のマップに集約する。
  3. タグ名でソートして標準出力に出力する。出力は次の形式です。
<tag>:
- <manifest-name>
- <manifest-name>
  1. --tags が指定された場合は、そのタグ名に絞って出力する。

クラスタへのアクセスはありません。

フラグ

グローバルフラグ に加えて次を受け付けます。

フラグエイリアスデフォルト説明
--tags-t[]string[]出力対象を指定したタグ名に絞り込みます。

tazuna tags
tazuna tags -f tazuna.yaml
tazuna tags --tags frontend,backend

関連

tazuna version

バイナリに埋め込まれたバージョン情報を出力します。

tazuna version
tazuna --version

両者は等価です。

振る舞い

次の形式で 1 行出力して終了します。

tazuna <version> (commit <commit>, built <date>, <os>/<arch>)
  • <version> — リリース時のタグ。ローカルビルドでは dev
  • <commit> — リリース時の commit hash。未注入時は none
  • <date> — リリース時のビルド日時。未注入時は unknown
  • <os>/<arch>runtime.GOOS / runtime.GOARCH

<version> / <commit> / <date> は goreleaser によるリリース時に注入されます。 go install / go run などローカルビルドでは未注入のためデフォルト値が出ます。

フラグ

固有フラグはありません。引数も受け付けません。

tazuna version
tazuna --version

コントリビュート

このセクションは、Tazuna のコードベース・ドキュメント・リリースに変更を入れる人向けの案内です。 リポジトリのルートにある CONTRIBUTING.md が一次情報で、ここではそれを補う形で各トピックを 1 ページずつにまとめます。

一覧

  • 開発環境mise で toolchain を揃え、make build でローカルバイナリを作るまでの手順とリポジトリ構成。
  • テスト — unit / integration / e2e の 3 レイヤと make ターゲットの対応、KinD クラスタの用意。
  • ドキュメントdocs/ の構造、mdbook でのプレビュー、po/en.po を使った英語訳の更新フロー、GitHub Pages への公開。
  • リリース — tag push を起点とした goreleaser によるリリース、バージョン埋め込み、SBOM / 署名 / 来歴。

バグ報告 / 機能提案

GitHub の Issue テンプレート を使ってください。テンプレート無しの自由記述 issue は受け付けます。

セキュリティ起因の問題は SECURITY.md の手順に従ってください。公開 issue は作らないでください。

Pull Request の流れ

CONTRIBUTING.md の記述と同じ流れです。再掲しておきます。

  1. main から作業ブランチを切る。
  2. 変更はトピックごとに小さくまとめる。
  3. push する前にローカルで make testmake lint を通す。
  4. main 宛に PR を作成。CI が green になるまでレビュー対象にならない。

PR テンプレートはリポジトリの .github/PULL_REQUEST_TEMPLATE.md を使います。

開発環境

このページは、Tazuna 本体に手を入れたい人がローカル環境を整え、 コード変更をビルド・実行・確認するまでの手順をまとめます。 ドキュメントやリリースについては別ページ(ドキュメント / リリース)を参照してください。

toolchain は mise で揃える

リポジトリには mise.toml が 入っており、必要な toolchain がピン留めされています。

[tools]
go = "1.26.0"
golangci-lint = "latest"
helm = "latest"

mise をインストール済みであれば、リポジトリのルートで mise install を実行すれば これらが揃います。Go や golangci-lint をシステム側で別管理している場合は、mise.toml と一致するバージョンを自分で揃えてください。

なお go.mod が要求する Go のバージョン (go 1.26.x) が mise.toml のピン留めより 新しい場合は、ビルド時に Go の toolchain ダウンロード機構が差分を吸収します。意図的に toolchain ダウンロードを避けたい場合は、mise.tomlgo.modgo 行に揃えて使ってください。

helm バイナリは Tazuna 自身は使いません(Helmfile backend は Go ライブラリとして組み込んでいます)。mise.toml に入っているのは、helm を 依存ツールとして扱う開発フローを将来サポートする余地を残すためのものです。

主要な make ターゲット

Makefile の中身に対応するターゲットだけ列挙します。

ターゲット中身
make buildgo build ../tazuna を生成
make installmake build の後 sudo mv tazuna /usr/local/bin
make formatgo fmt ./...
make lintgolangci-lint run
make testgo test ./...(unit テスト)
make test-integrationgo test -tags=integration ./...
make test-e2emake build && make devenv-create の後 go test -tags=e2e -count=1 ./test/e2e/...
make test-allunit + integration + e2e
make cover-race -covermode=atomic -coverprofile=coverage.out でテスト後、サマリ出力
make allformat → test → build → lint を順に実行
make devenv-createkindtazuna という名前のクラスタを立てる(既存なら context を切り替え)
make devenv-destroykind delete cluster --name tazuna

KinD クラスタ名は 固定で tazuna、kubeconfig context 名は kind-tazuna です。 e2e は KinD クラスタを前提にしているため、make test-e2e を初めて走らせるときは 内部で make devenv-create が動きます(テスト も参照)。

リポジトリ構成

主要なディレクトリの責務はおおむね次のとおりです。

パス役割
main.goエントリポイント。cmd.Execute() を呼ぶだけ。
cmd/Cobra のサブコマンド定義(apply / build / check / destroy / state ... / secret-to-genesissecret / tags / version)。
cmd/internal/サブコマンド間で共有する内部ユーティリティ。
api/v1/YAML スキーマに対応する Go 構造体定義(tazuna.yaml / tazuna.hint.yaml / GenesisSecret / TestPluginSpec / ORAS)。
pkg/runner/tazuna apply 全体のオーケストレーション。
pkg/manager/Manifest type 別の Manager 実装(kustomize / helmfile / genesis_secret / parallel、および oras/ サブパッケージ)。
pkg/state/State の表現と ConfigMap 永続化。
pkg/testplugin/WaitUntil / ExistNonExist 実装。
pkg/genesissecret/Provider インターフェースと 1Password 向け実装。
pkg/hint/tazuna.hint.yaml のロードと検証。
pkg/op/op(1Password CLI)の呼び出し。
pkg/validator/tazuna.yaml のバリデーション。
pkg/context/context_matches の評価。
pkg/prompt/対話入力の抽象(destroy 時の Yes/No など)。
pkg/resource/反映時に共通で使う Kubernetes リソース操作ヘルパ。
test/e2e/E2E テスト本体と fixture (testdata/)。
docs/このドキュメントサイト。

リファレンスでよく出てくる Manager / Runner / Validator などの責務分担は 全体アーキテクチャ を参照すると引き合わせやすいです。

ローカルバイナリで挙動を試す

make build で生成した ./tazuna を直接呼べば、リリース版のかわりに開発中の バイナリで挙動を試せます。

make build
./tazuna check -f path/to/tazuna.yaml
./tazuna build -f path/to/tazuna.yaml --tags infra

PATH に置きたいときは make install を使います(sudo が必要です)。 KinD で実機検証する場合は make devenv-create でクラスタを立て、 kubectl config use-context kind-tazuna で current-context を切り替えてから動かします。

テスト

Tazuna のテストは unit / integration / e2e の 3 レイヤです。 各レイヤは make ターゲットと 1 対 1 で対応しており、PR で常に動くのは unit (CI)、e2e は KinD を前提にした手動レイヤです。

レイヤと走らせ方

レイヤコマンド対象前提
unitmake testgo test ./...なし。GitHub Actions の CI ワークフローで毎 push / PR 時に走る。
integrationmake test-integrationgo test -tags=integration ./...なし。build tag integration を付けた追加テストが対象。
e2emake test-e2ego test -tags=e2e -count=1 ./test/e2e/...KinD クラスタ。内部で make build && make devenv-create が走る。
すべてmake test-allunit → integration → e2ee2e と同じ
カバレッジmake cover-race -covermode=atomic -coverprofile=coverage.out で unit を実行、サマリ出力なし

CI(.github/workflows/ci.yaml)では go test -race -covermode=atomic -coverprofile=coverage.out ./... が走るため、内容は make cover とおおむね一致します。

unit テスト

すべてのパッケージで *_test.go として書かれています。Go の標準的なテストです。 KinD / 外部 CLI に依存せず、go test ./... のみで完結します。

PR レビュー対象になるのはこのレイヤが green であることが大前提です。

integration テスト

build tag integration を付けた追加テストです。make test-integration で実行します。 外部依存はないが、unit テストには重すぎるシナリオを切り分ける場所として使われます。

go test -tags=integration ./... を直接叩いても同じです。

e2e テスト

test/e2e/ に置かれた本物のクラスタを使ったテストです。 build tag e2e で隔離され、./test/e2e/... だけが対象になります。

make test-e2e を実行すると、内部で次が順に走ります。

  1. make build./tazuna を作る)
  2. make devenv-create(KinD クラスタ tazuna を立てる、既存なら context を切り替え)
  3. go test -tags=e2e -count=1 ./test/e2e/...

-count=1 が付くのは、e2e テストのキャッシュを禁止して毎回実走させるためです。 make devenv-destroy で KinD クラスタは片付きます(kind delete cluster --name tazuna)。

KinD クラスタの仕様

項目
クラスタ名tazuna
kubeconfig contextkind-tazuna
設定ファイル.github/kind-config.yaml

CI 用と開発者ローカル用で同じ KinD 設定を共有しているため、ローカルで通る e2e は 基本的に CI でも通ります。

テストデータ

e2e の fixture は test/e2e/testdata/ 配下にケースごとのディレクトリで置かれています。 tazuna.yaml と、type に対応した実体(kustomize/ / helmfile/ 等)が同居する作りです。

test/e2e/testdata/
├── kustomize-minimal/         # type: kustomize の最小ケース
├── helmfile-minimal/          # type: helmfile の最小ケース
├── parallel-minimal/          # type: parallel
├── testplugin-minimal/        # Test plugin (WaitUntil/ExistNonExist) の基本
├── testplugin-cel/            # WaitUntil の CEL 式が複雑なケース
├── tags-filter-minimal/       # --tags フィルタ
├── state-minimal/             # state list/diff/sync
├── state-modified/            # state diff の "modified" 判定
├── destroy-minimal/           # tazuna destroy
└── check-invalid/             # tazuna check の異常系

新しい機能を入れるときは、対応する fixture ディレクトリを追加し、最小構成の tazuna.yaml を 1 つ書き、test/e2e/ 配下に *_test.go を追加する流れが一般的です。

lint

make lint

golangci-lint run を呼ぶだけです。golangci-lint のバージョンは mise.toml で 管理しているため、mise install で揃えれば追加の手当ては不要です。

CI でも同じ golangci-lint が走ります。PR を出す前にローカルで通しておくと往復が減ります。

ドキュメント

このページは、Tazuna のドキュメントサイト(いま読んでいるサイト)に変更を入れる人向けの案内です。 サイトは docs/ 配下で完結しており、mdBook + mdbook-i18n-helpers(gettext / PO ファイル) で構築しています。

ソースは 日本語 で書き、英語版は po/en.po の翻訳で派生させる構成です。

ディレクトリ構成

docs/
├── book.toml            # mdBook の設定
├── src/                 # ドキュメント本体(日本語ソース)
│   ├── SUMMARY.md
│   ├── introduction.md
│   ├── concepts/
│   ├── getting-started/
│   ├── guides/
│   ├── operations/
│   ├── reference/
│   └── contributing/
├── po/
│   └── en.po            # 英語訳
├── theme/
│   └── fonts.css        # Google Fonts (M PLUS U) の上書き
├── static/
│   └── index.html       # /en/ と /ja/ をリンクする landing
└── THIRDPARTY.md        # フォント等のサードパーティ資産

docs/src/SUMMARY.md がページの目次そのものです。新しいページを追加するときは ここにも 1 行追記する必要があります(追記漏れがあると mdBook はビルドできても、 そのページはサイトに出ません)。

必要なツール

cargo install mdbook --locked
cargo install mdbook-i18n-helpers --locked

msgmerge(gettext 同梱)は PO の更新で使います。macOS なら brew install gettext

ローカルでプレビュー

日本語版(ソース言語):

cd docs
mdbook serve --open

英語版:

cd docs
MDBOOK_BOOK__LANGUAGE=en mdbook serve --open

ファイル保存のたびに再ビルドされてブラウザがリロードされます。

ローカルでビルド

cd docs
mdbook build -d book/ja
MDBOOK_BOOK__LANGUAGE=en mdbook build -d book/en
cp static/index.html book/index.html

book/index.html をブラウザで開けば、リンクで /ja//en/ を切り替えられます。

英語訳の更新

docs/src/ 配下の文章を編集したら、PO テンプレートを再生成して en.po に merge します。

cd docs
MDBOOK_OUTPUT__XGETTEXT__POT_FILE=messages.pot \
  mdbook build -d po --no-create-missing
msgmerge --update po/en.po po/messages.pot

このあと po/en.po を開いて、追加された msgid に対応する msgstr を埋めます。 ソース側(日本語)と英語訳の対応が崩れているとサイト上で空白や欠落が見えるので、 ソース変更と同じ PR で en.po も更新するのが基本フローです。

デプロイ

.github/workflows/docs.yaml が公開を担当します。

  • main への push: サイトをビルドし、GitHub Pages へ公開する (actions/upload-pages-artifact + actions/deploy-pages
  • PR: ビルドのみ。生成物は github-pages という名前で workflow run の artifact として ダウンロードできます。ローカルで展開して目で見るのが推奨レビュー手順です。

GitHub Pages は 1 サイトにつき 1 デプロイメントしか持てないため、PR ごとの live プレビュー URL は意図的に提供していません。

サードパーティ資産

サイトは Google Fonts の M PLUS U を ランタイムでロードしています。フォント / アイコン / 画像など新しい外部資産を入れる場合は、 docs/THIRDPARTY.md にライセンスと帰属を追記します。

ドキュメント側の規約

ここまでに書いた既存ページが従っている規約を、参考として残しておきます。

  • トーンの使い分け: 概念は散文中心(concepts/)、ガイドは「目的→前提→手順」、 リファレンスはフィールド表とコード断片中心。
  • アンカーリンク: glossary や他リファレンスへの導線は #anchor まで含めて貼る。 例: [Manifest type](../concepts/glossary.md#manifest-type)
  • コードと識別子: tazuna.yaml のフィールド名、CLI フラグ、Go の型名はバッククォートで囲む。
  • 言語ポリシー: ソースは日本語で書く。コード中のコメントは日本語で書いてよいが、 ログ・エラーメッセージ・CLI 出力は英語に統一する(プロジェクト全体の方針)。

リリース

Tazuna のリリースは タグ push をトリガに GitHub Actions が goreleaser を回す という構成です。リリースを切るときに手元から goreleaser を直接呼ぶ場面は通常ありません。

ここでは「リリースが切られたときに何が起きるか」「バージョン文字列の供給元」 「成果物の検証手段」をまとめます。

トリガと出力

タグ名はそのまま version 文字列として埋め込まれます。 セマンティックバージョニング(vX.Y.Z)に従うことを推奨します(changelog の自動生成 も参照)。

出力物

.goreleaser.yaml の ビルド対象は次のとおりです。

GOOSlinux / darwin
GOARCHamd64 / arm64
CGOCGO_ENABLED=0
ldflags-s -w -trimpath + バージョン埋め込み

成果物のアーカイブ名は tazuna_<Os>_<Arch>.tar.gzamd64x86_64 に正規化)で、 リリースには次のものが添付されます。

  • 各 OS/arch の .tar.gz(バイナリ)
  • checksums.txt(SHA256)
  • 各アーカイブ + ソースの SBOM (*.sbom.json、SPDX)
  • checksums.txt.sigstore.json(cosign のキーレス署名バンドル)

GitHub Actions の actions/attest-build-provenance が SLSA build provenance を別途生成し、gh attestation verify で検証できる形に 紐付けます。

バージョン文字列の埋め込み

main.go には次の var が宣言されていて、リリースビルド時に -X で値が注入されます。

var (
    version = "dev"
    commit  = "none"
    date    = "unknown"
)

.goreleaser.yamlldflags で:

-X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}

注入された値は cmd.SetVersionInfo 経由で tazuna version に表示されます。

go install / make build などローカルビルドの場合は注入されないため、 version / commit / date はそのまま dev / none / unknown になります。

changelog

.goreleaser.yamlchangelog セクションが GitHub Releases の説明文を組み立てます。

  • 並び: コミット昇順 (sort: asc)
  • 除外: ^docs: / ^test: プレフィックスの commit
  • フォーマット: ヘッダ ## Tazuna {{.Version}} と、フッタに前リリースとの compare リンク

PR のタイトル / commit メッセージ規約は CONTRIBUTING.md / PR テンプレートが基準です。 docs: / test: プレフィックスはリリースノートに出ない ので、プロダクトの動作変更 ではない PR にはこれらを付けると棚卸しがしやすくなります。

成果物の検証

リリースを使う側の検証手段はおおむね 3 つあります。

# 1. checksum の検証
sha256sum -c checksums.txt

# 2. checksums.txt の署名検証(cosign keyless)
cosign verify-blob \
  --bundle checksums.txt.sigstore.json \
  checksums.txt

# 3. SLSA provenance の検証
gh attestation verify <file> --repo pepabo/tazuna

3 つ目は GitHub CLI 経由で SLSA build provenance を確認する手段です。 リリース直後でも内部 CI でも使えます。

切るときの実務

  1. main が安定していることを確認する(CI が green)。
  2. vX.Y.Z 形式のタグを切って push する。
  3. Release ワークフローが走り、Releases が公開されることを確認する。
  4. 必要なら自動生成された changelog を手で整える。

ドキュメントサイトの公開は別ワークフロー(docs.yaml) で、こちらは main への push をトリガに動きます。リリース切りとは独立しています。