クラスタそのものを組む層。ここにあるものは全部 machine config に載る(render.sh が描く)。 ArgoCD はここを見ない(例外は argocd-repos.yaml だけ。下の「ArgoCD が当てるもの」)。 層の分け方は docs/decisions.md「層の分け方」。
| 何を | いつ | |
|---|---|---|
| machine config(このディレクトリ) | Talos の設定、Helm で入れるもの(cilium・cert-manager・argocd・lvm-localpv)、apiserver-rbac.yaml、storage.yaml、SOPS 済みの Secret 2 つ |
apply.sh → upgrade-k8s(talos.md「当て方」) |
ArgoCD(deploy/) |
平文のマニフェスト(ClusterIssuer・Gateway・auth など)と SOPS 済みの SopsSecret。2026-10 まではここにあって、Application bootstrap が当てていた |
main へのマージ(prune / selfHeal 付き) |
部品ごとに 1 ファイルを基本にして、ディレクトリを切らない。部品の名前を頭に付ける。
| ファイル | 中身 |
|---|---|
| render.sh | machine config を組む。下のファイルを全部読む |
| talos.md | Talos の組み立て・書き方・当て方 |
| talos.sops.yaml | 手で書く machine config(password の値だけ SOPS)。先頭のコメントに Talos・Kubernetes・Gateway API の版と schematic |
| talos-secrets.sops.yaml | クラスタの CA 一式(丸ごと SOPS) |
| cilium-values.yaml | chart の値と版(下の「Helm で入れるもの」) |
| argocd-values.sops.yaml | chart の値と版。通知先の URL と GitHub App の鍵だけ SOPS |
| cert-manager-values.sops.yaml | chart の値と版。2 つめのドキュメントが DNS-01 の Cloudflare のトークンの Secret(トークンだけ SOPS) |
| apiserver-rbac.yaml / apiserver-ca.crt | API サーバの入口の RBAC と CA(下の「API サーバの入口」) |
| lvm-localpv-values.yaml | chart の値と版(OpenEBS LVM LocalPV。PVC の置き場。下の「PVC の置き場」) |
| storage.yaml | StorageClass local-path・VolumeSnapshotClass・thin プールを作る DaemonSet(chart が描かないもの) |
| argocd-repos.yaml | ArgoCD が当てる唯一のファイル(下の「ArgoCD が当てるもの」) |
SOPS(age)で暗号化したファイルは名前を *.sops.yaml で終える(.sops.yaml の規則もその名前にしか一致しないので、
別の名前では暗号化できない)。復号鍵はクラスタにも CI にも渡さない。
鍵は recovery/sops-age.key.age。
talos-secrets.sops.yaml 以外は秘密のキーの値だけを暗号化し(.sops.yaml の encrypted_regex)、mac_only_encrypted で
MAC も暗号化した値だけを見る。なので平文の部分(コメントの # version: も)は sops を通さず直接直してよい
(Renovate もそうする)。秘密の値を足す・変えるときは sops edit。秘密のキーを新しく足すときは .sops.yaml の
encrypted_regex にも足す(当たらないキーは平文のまま書かれる)。talos-secrets.sops.yaml は丸ごとなので sops edit だけ。
ここで ArgoCD が当てるのは argocd-repos.yaml だけ。 machine config に載っている
apps の Application(argocd-values.sops.yaml の extraObjects)が拾い、そこから ApplicationSet が deploy/ を当てる。
場所(path: bootstrap と include: argocd-repos.yaml)が machine config に焼いてあるので、名前を変えると machine config を当て直すことになる。
そのときは main にマージしてから当てる。マージから当てるまでの間、apps は古い path が無くて ComparisonError で止まるだけ
(何も消さない)。逆に先に当てると、新しい include が main の何にも当たらず空の描き出しになる(自動同期は空にする
prune を allowEmpty 無しでは拒むが、頼らない)。
ArgoCD の平文のマニフェストは deploy/ にある:
deploy/gateway/(Gateway と http → https)、deploy/auth/(SSO の oauth2-proxy)、
deploy/cert-manager/(ClusterIssuer)、deploy/argocd/(HTTPRoute と OIDC の SopsSecret)。
ClusterIssuer や Gateway は本体(cert-manager・cilium)が machine config に居ても、書き換えるのは平文のマニフェストなので
ArgoCD に任せる。
どれも render.sh が helm template して machine config に載せる。どれも同じ形で、
<name>-values.yaml の 1 本(秘密を含むものは <name>-values.sops.yaml)に chart・repo・版と値をまとめる。
先頭の 2 行が chart と版:
# renovate: datasource=helm depName=cilium registryUrl=https://helm.cilium.io
# version: 1.20.4# renovate: の行から render.sh が chart(depName)と repo(registryUrl)を、次の # version: の行から版を読む。
版をコメントにしているのは、値として書くと chart に渡ってしまうから。Renovate も同じ 2 行を見て上げる(renovate.json の customManagers)。
Talos・Kubernetes・Gateway API の版も同じ形で、talos.sops.yaml の先頭のコメントにある。
秘密を含む argocd・cert-manager は <name>-values.sops.yaml で、秘密の値だけ暗号化してある(上の SOPS の段落)。
render.sh が復号してから helm に渡す。2 つめのドキュメントがあれば、それは chart とは別に machine config に載せる Secret
(cert-manager の Cloudflare のトークン。ArgoCD は復号できないのでここに置く)で、
render.sh が --- で分けて別の inlineManifest にする。
argocd の版は自動マージしない(renovate.json の bootstrap charts)。lvm-localpv も(openebs lvm。storage.yaml の lvm-driver のイメージと同じ PR)。
Namespace は chart が描かないので、render.sh が kube-system 以外のものに付ける。
cilium だけは cilium-drift.yml が見ている(下の「cilium」)。残りは見ていない。
直したら流す(流し忘れたことは誰も教えてくれない)。描くたびに変わる値は潰してあるので、
apply-config --dry-run に出る差分は全部本物(talos.md「machine config と inlineManifests」)。
クラスタの土台。 CNI・kube-proxy の代替・Gateway API を全部これが担う。 決定の経緯は ../docs/decisions.md「ルーティングの選定」。
cilium-values.yaml を直したら apply.sh → upgrade-k8s
(talos.md「当て方」)。helm upgrade は使わない。値はここが正本で、machine config は
render.sh がここから描く派生物(二度書かない。decisions.md「machine config と Cilium の chart をどう連動させるか」)。
どの設定が何のために要るかは cilium-values.yaml のコメントにある(ここには写さない)。
Hubble の証明書はクラスタの中の CronJob が作る(hubble.tls.auto.method: cronJob)。chart の既定(helm)は描くたびに
作り直して machine config に秘密鍵が載り、値を変えなくても upgrade-k8s の差分に出ていた。
当て忘れとクラスタ側の直接編集は cilium-drift.yml が見つける。
cilium-values.yaml から cilium-config の ConfigMap を描いて live と突き合わせるだけで、読むだけ(権限は cilium-config の get 1 つ)。
cilium-values.yaml を触ったときと毎週月曜に走る。当てるのは自動化しない(Talos API の os:admin を CI に置かない)。
# ホストのポートが張られているか(eBPF なので ss には出ない)
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium-dbg service list | grep -i hostport
# Gateway のリスナーが載っているか(住所はノードの IP になる)
kubectl -n kube-system get gateway doanyOpenEBS LVM LocalPV(thin)。PVC ごとに VG data の thin プールから LV を切り、xfs で渡す。
各アプリの PVC が載る土台なので bootstrap 層に居る。選んだ理由と組んだ形は ../docs/decisions.md「ストレージを OpenEBS LVM LocalPV に…」。
| 何を | 誰が |
|---|---|
パーティション r-lvm と VG data |
Talos(talos.sops.yaml の RawVolumeConfig / LVMVolumeGroupConfig)。入れ直したときにしか作られない |
thin プール data_thinpool(VG の 90%) |
storage.yaml の DaemonSet lvm-thinpool。出来たらノードに doany.io/lvm-thinpool=ready を付ける |
CSI ドライバ(local.csi.openebs.io) |
chart(lvm-localpv-values.yaml)。ノードの Pod は上のラベルが付いてから動く |
StorageClass local-path(既定)・VolumeSnapshotClass lvm(既定) |
storage.yaml |
| snapshot-controller と VolumeSnapshot の CRD | render.sh が external-snapshotter のタグの URL で(版は talos.sops.yaml の先頭) |
- PVC の requests が本当の上限。 足りなくなったら PVC の requests を上げる(
allowVolumeExpansion: true。スナップショットのある PVC は広げられない) - thin プールが溢れると全部の PVC の書き込みが止まる。
kubectl -n kube-system get pods -l app=lvm-thinpoolが 90% で NotReady になる。 埋まり具合はkubectl -n kube-system exec ds/lvm-thinpool -c watch -- lvs data。足すときは VG の残り(1 割)からkubectl -n kube-system exec ds/lvm-thinpool -c watch -- lvextend -L +10G data/data_thinpool - 新しい PVC の直下は root の 0755。 root 以外で書くアプリは Pod に
fsGroupを付ける
平文の Secret はどのリポジトリにもコミットしない。値は SOPS で暗号化して git に置き(各リポジトリの deploy/ の
SopsSecret)、クラスタの中の sops-secrets-operator が復号して普通の Secret を作る
(理由は ../docs/decisions.md「秘密は SOPS で git に置く」。2026-11-02 までは Infisical)。
- operator が復号に使うクラスタの age の鍵だけがここ(sops-age.sops.yaml。machine config が
sops-secrets-operatorの namespace ごと入れる)。Argo CD も CI もこの鍵を持たない。 SopsSecretはクラスタの鍵と管理者の鍵の両方で暗号化する(../.sops.yaml のdeploy/の規則)ので、手元でsops editできる。- Secret が変わったときの Pod の入れ替えは Reloader(workload に
reloader.stakater.com/auto: "true")。
GitHub Actions は読むだけ。 cilium-drift.yml の OIDC トークンを API サーバが直接受け
(talos.sops.yaml の KubeAuthenticationConfig)、
権限は cilium-config の get だけ(apiserver-rbac.yaml)。
CA は apiserver-ca.crt(talos-secrets.sops.yaml のクラスタ CA。作り直したら替える)。
kubeconfig も age 鍵も GitHub に置いていない。
同じ認証設定と RBAC が人の入口でもある。 Entra を 2 つめの issuer として足し、
Headlamp が利用者本人の id_token を API サーバへ転送する。アプリロール admin を持つ人は
entra:admin で cluster-admin、それ以外は entra:viewer で読み取り専用(組み込みの view
+ CRD とノードぶんの readonly-extra)。振り分けは CEL で、割り当ては Entra 側で完結する。
認証設定(talos.sops.yaml)も apiserver-rbac.yaml も machine config に載せ、
ArgoCD には当てさせない(ArgoCD も CI も要らずに効く層なので、締め出されたときに直せる)。
変えたら render.sh → apply-config(RBAC は inlineManifest なので upgrade-k8s まで)。
各リポジトリの deploy/secrets.sops.yaml に namespace ごと 1 つの SopsSecret を置き、secretTemplates に Secret を並べる
(例: ../deploy/matrix/secrets.sops.yaml)。暗号化するのは data・stringData の値だけ
(先頭のコメントと名前は平文のまま読める)。各リポジトリの .sops.yaml もこのリポジトリの deploy/ の規則と同じ鍵 2 つにする。
tools/t.ps1 sops edit deploy/<アプリ>/secrets.sops.yaml # 直して push すると、Argo CD が当てた時点で Secret が変わるsync-options の注釈は要らない。 まっさらなクラスタでは SopsSecret の CRD がまだ無く、同じ同期の中の CR の
dry-run で同期が丸ごと落ちる。これは ApplicationSet(argocd-repos.yaml)の syncOptions の SkipDryRunOnMissingResource=true で
全部のアプリに効かせてある。ApplicationSet の外の Application(chart を入れるものなど)で CR を書くときだけ、そちらに付ける。
作られた Secret に Argo CD の tracking-id は付かない(operator は secretTemplates のラベルと注釈しか付けない)ので、Argo CD の資源と見なされず prune されない。
chart が作る Secret は Argo CD がそのまま当てる(以前は resource.exclusions で Secret を丸ごと外していた。docs/decisions.md)。