diff --git a/.github/workflows/talos-boot.yml b/.github/workflows/talos-boot.yml index eb958c2..e2179ad 100644 --- a/.github/workflows/talos-boot.yml +++ b/.github/workflows/talos-boot.yml @@ -91,16 +91,10 @@ jobs: run: | set -euo pipefail talosctl gen secrets -o /tmp/secrets.yaml - # ダミーは fj.doany.io に置く(ghcr.io に置くと kubelet の取得が 403 で落ちる) - cat > /tmp/registries.yaml <<'PATCH' - machine: - registries: - config: - fj.doany.io: - auth: - username: ci-dummy - password: ci-dummy - PATCH + # controlplane.yaml は password(fj.doany.io)だけ暗号化してある。ダミーに置き換える + # (ダミーは fj.doany.io に置いたまま。ghcr.io に置くと kubelet の取得が 403 で落ちる) + yq 'del(.sops) | (.. | select(tag == "!!str" and test("^ENC\\["))) |= "ci-dummy"' \ + talos/controlplane.yaml > /tmp/controlplane.yaml # values.yaml は本物、secret-values.sops.yaml だけダミー(通知先もそこにしか無い)。 # extraObjects は app-of-apps と repo-creds。CI で本物を同期させない echo 'extraObjects: []' > /tmp/argocd-secret-values.yaml @@ -116,10 +110,10 @@ jobs: bootstrap/infisical/secrets.sops.yaml > /tmp/infisical-secret.yaml yq 'del(.sops) | (.. | select(tag == "!!str" and test("^ENC\\["))) |= "ci-dummy"' \ bootstrap/cert-manager/cloudflare-secret.sops.yaml > /tmp/cloudflare-secret.yaml - for f in /tmp/infisical-secret.yaml /tmp/cloudflare-secret.yaml; do + for f in /tmp/controlplane.yaml /tmp/infisical-secret.yaml /tmp/cloudflare-secret.yaml; do ! grep -q 'ENC\[' "$f" || { echo "::error::$f に暗号文が残っている"; exit 1; } done - SECRETS=/tmp/secrets.yaml REGISTRIES=/tmp/registries.yaml \ + SECRETS=/tmp/secrets.yaml CONTROLPLANE=/tmp/controlplane.yaml \ ARGOCD_SECRET_VALUES=/tmp/argocd-secret-values.yaml INFISICAL_SECRET_VALUES=/tmp/infisical-secret-values.yaml \ INFISICAL_SECRET=/tmp/infisical-secret.yaml CLOUDFLARE_SECRET=/tmp/cloudflare-secret.yaml \ ./talos/render.sh /tmp/out @@ -256,17 +250,17 @@ jobs: echo " volume $v: $P" done - WANT=$(yq -r 'select(.kind == "BondConfig") | .hardwareAddr' talos/patches/cluster.yaml) + WANT=$(yq -r 'select(.kind == "BondConfig") | .hardwareAddr' talos/controlplane.yaml) GOT=$(talosctl get links bond0 -o json | jq -r .spec.hardwareAddr) - [ "$GOT" = "$WANT" ] || fail "bond0 の MAC が $GOT(cluster.yaml は $WANT)" + [ "$GOT" = "$WANT" ] || fail "bond0 の MAC が $GOT(controlplane.yaml は $WANT)" echo " bond0 hardwareAddr: $GOT" - # 以下は forwarding=1 のあとで見ないと意味が無い(cluster.yaml の accept_ra) + # 以下は forwarding=1 のあとで見ないと意味が無い(controlplane.yaml の accept_ra) F=$(talosctl read /proc/sys/net/ipv6/conf/bond0/forwarding) [ "$F" = 1 ] || fail "bond0 の forwarding が $F。前提が成り立っていない" # グローバルな IPv6 は静的な ::2 だけ(SLAAC は autoconf=0 で止めている) - STATIC=$(yq -r 'select(.kind == "BondConfig") | .addresses[].address | select(test(":"))' talos/patches/cluster.yaml) + STATIC=$(yq -r 'select(.kind == "BondConfig") | .addresses[].address | select(test(":"))' talos/controlplane.yaml) GLOBAL=$(talosctl get addresses -o json \ | jq -rs '.[] | select(.spec.linkName == "bond0" and .spec.family == "inet6" and .spec.scope != "link") | .spec.address' | sort) echo " bond0 の IPv6(link-local 以外): $(echo $GLOBAL)" diff --git a/.gitignore b/.gitignore index 6db9d05..606a7ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ talos/clusterconfig/ +# **平文のまま置かないこと。** talos/secrets.yaml と controlplane.yaml は SOPS 済みのものをコミットする +# (talos/README.md)。書きかけの平文はこの名前に逃がす。 talos/secrets.plain.yaml -# **平文のまま置かないこと。** talos/registries.yaml は SOPS 済みのものをコミットする -# (talos/README.md「fj.doany.io の資格情報」)。書きかけの平文はこの名前に逃がす。 -talos/registries.plain.yaml # 平文の kubeconfig / talosconfig も置かない。どちらも tools/t.ps1 が毎回その場で作る # (tools/README.md「クラスタの資格情報」)。talosctl kubeconfig と gen config がこの名前で書き出す kubeconfig diff --git a/.sops.yaml b/.sops.yaml index f570779..9ca85f1 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -11,8 +11,12 @@ creation_rules: - path_regex: ^bootstrap/.*\.sops\.yaml$ encrypted_regex: ^(ENCRYPTION_KEY|AUTH_SECRET|CLIENT_ID_GITHUB_LOGIN|CLIENT_SECRET_GITHUB_LOGIN|CF_DNS_API_TOKEN)$ age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r - # 以下は全部が秘密なので丸ごと - - path_regex: ^talos/secrets\.yaml$ + # 手で書く machine config。秘密は password の値だけ(fj.doany.io)。平文の部分は sops を通さず直接直せるよう + # mac_only_encrypted(MAC が暗号化した値だけを見る)。SOPS はドキュメントごとに sops: を付け、インデントを 4 にする + - path_regex: ^talos/controlplane\.yaml$ + encrypted_regex: ^password$ + mac_only_encrypted: true age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r - - path_regex: ^talos/registries\.yaml$ + # 全部が秘密なので丸ごと(CA 一式) + - path_regex: ^talos/secrets\.yaml$ age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r diff --git a/README.md b/README.md index 4da9dc0..b65eb9d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ | [`pulumi/`](pulumi/) | Cloudflare(R2・DNS・ゾーンの設定・通知)・Entra(リダイレクト URI・CI の信頼の設定)・NetBird の設定を Pulumi で持つ。**PR で差分がコメントされ、main に入ると当たる** | | [`mado/`](mado/) | **`main` が落ちたときの入口**。JCOM 側の PC(10.10.0.2)で [mado](https://github.com/danything/mado)(OIDC でログインしてブラウザから RDP する exe)を動かす(`md.doany.io`。クラスタの外) | | [`recovery/`](recovery/) | **まっさらから建て直す入口**(手順・PV を戻す道具・暗号化した鍵)。git と restic だけで戻す | -| [`talos/`](talos/) | ノードの machine config(**v1.14 以降の形**。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が生成物を検証して VM で起動する)。**`bootstrap/` のほぼ全部がここに載る** ── [render.sh](talos/render.sh) が `helm template` して inlineManifest にする | +| [`talos/`](talos/) | ノードの machine config(**v1.14 以降の形**。手で書くのは [controlplane.yaml](talos/controlplane.yaml) の 1 本。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が生成物を検証して VM で起動する)。**`bootstrap/` のほぼ全部がここに載る** ── [render.sh](talos/render.sh) が `helm template` して inlineManifest にする | | [`tools/`](tools/) + `compose.yaml` | **手元の運用のコマンド(cf・sops・infisical・kubectl・talosctl など)はコンテナで動かす**。`tools/t.ps1 <コマンド>`(Windows の WSL コンテナ。wslc-compose)。手元にコマンドは入れない | | [`docs/`](docs/) | [決定の記録](docs/decisions.md)、[Talos の作法と検証](docs/talos.md)、[Entra ID の認可](docs/entra.md)、[家のルーター](docs/router.md)、[Z440 への移行](docs/z440-migration.md)、[Cloudflare の API トークン](docs/cloudflare-tokens.md) | | [`ROADMAP.md`](ROADMAP.md) | やることと進捗(なぜそうしたかは `docs/decisions.md`) | @@ -79,7 +79,8 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble | [`netbird/`](apps/netbird/) | NetBird (VPN。combined コンテナ + 内蔵 IdP) | | [`3proxy/`](apps/3proxy/) | 3proxy (国内IP経由の HTTPS フォワードプロキシ) | | [`k8up/`](apps/k8up/) | バックアップ(restic → Cloudflare R2)。PVC・DB のダンプ・etcd のスナップショット | -| [`infisical/`](apps/infisical/) [`infisical-operator/`](apps/infisical-operator/) [`infisical-push-bridge/`](apps/infisical-push-bridge/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ | +| [`infisical/`](apps/infisical/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ | +| [`cluster/`](apps/cluster/) | ノードまわりの小物。metrics-server(Talos に無い。`kubectl top` と各 UI の使用量表示)と、PT3 をドライバに付け直す DaemonSet | | [`renovate/`](apps/renovate/) | Renovate(GitHub の danything・5ym と Forgejo の doa の全リポジトリの依存を上げる PR)。CronJob で 15 分おき。**秘密の入れ方は [apps/renovate/README.md](apps/renovate/README.md)** | | [`headlamp/`](apps/headlamp/) | Kubernetes の Web UI(`h.doany.io`)。認証は Entra、権限は `bootstrap/apiserver/` | | [`rybbit/`](apps/rybbit/) | Rybbit(`rt.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** | diff --git a/ROADMAP.md b/ROADMAP.md index a713a34..c096973 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -23,8 +23,8 @@ bond0 の MAC は設定で固定してあるのでルーターは触らない。 - [ ] **R2 の容量が下がり切るのを見る。** `host=main` を消した直後の restic の実データは **5.60 GiB**(2026-10-17)。 日曜の prune のあと、`pulumi` ワークフローの週次の Summary で見る - [ ] **Talos 1.15.0 の正式版が出たら乗り換える**(Renovate の PR)。PT3 のモジュールの読み順を守る上流の修正 - ([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))が入ったら、[apps/pt3/rebind.yaml](apps/pt3/rebind.yaml) を外して - `talos/patches/main.yaml` の並び順だけにする。入ったら dvb-pt3 の extension の README も直す(上流) + ([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))が入ったら、[apps/cluster/pt3-rebind.yaml](apps/cluster/pt3-rebind.yaml) を外して + `talos/controlplane.yaml` の並び順だけにする。入ったら dvb-pt3 の extension の README も直す(上流) ## 済んだこと(記録) diff --git a/apps/cloudflare-ddns/application.yaml b/apps/cloudflare-ddns/application.yaml index 0f534c6..415aff9 100644 --- a/apps/cloudflare-ddns/application.yaml +++ b/apps/cloudflare-ddns/application.yaml @@ -27,7 +27,7 @@ spec: domains: doany.io proxied: "true" # AAAA は bond0 の static な ::2。SLAAC のアドレスがあるとそちらを選ぶので、 - # bond0 は autoconf=0 で SLAAC を付けない(talos/patches/cluster.yaml、docs/router.md)。 + # bond0 は autoconf=0 で SLAAC を付けない(talos/controlplane.yaml、docs/router.md)。 # 外向きの probe が拾う一時アドレスではない。この provider は # インタフェースを読んで temporary / deprecated を飛ばす。 ip6Provider: local.iface.stable:bond0 diff --git a/apps/metrics-server/application.yaml b/apps/cluster/metrics-server.yaml similarity index 100% rename from apps/metrics-server/application.yaml rename to apps/cluster/metrics-server.yaml diff --git a/apps/pt3/rebind.yaml b/apps/cluster/pt3-rebind.yaml similarity index 96% rename from apps/pt3/rebind.yaml rename to apps/cluster/pt3-rebind.yaml index 1b185ff..0103b11 100644 --- a/apps/pt3/rebind.yaml +++ b/apps/cluster/pt3-rebind.yaml @@ -1,4 +1,4 @@ -# PT3 をドライバに付け直す(talos/patches/main.yaml の kernel.modules と対)。 +# PT3 をドライバに付け直す(talos/controlplane.yaml の kernel.modules と対)。 # # Talos は kernel.modules を書いた順ではなく名前の順に読むので、earth-pt3 がチューナー # (qm1d1c0042 / mxl301rf)より先に入り、`Failed to create FE0` で /dev/dvb/*/frontend0 ができない。 diff --git a/apps/headlamp/application.yaml b/apps/headlamp/application.yaml index 0c635fb..082f99b 100644 --- a/apps/headlamp/application.yaml +++ b/apps/headlamp/application.yaml @@ -5,7 +5,7 @@ # # 認証は Entra の OIDC。Headlamp は利用者の id_token を API サーバへ転送するだけで、 # 権限を持たない。誰が何をできるかは apiserver 側で決まる -# (talos/patches/apiserver.yaml と bootstrap/apiserver/rbac.yaml)。アプリロール admin を +# (talos/controlplane.yaml と bootstrap/apiserver/rbac.yaml)。アプリロール admin を # 持つ人は cluster-admin、それ以外は読み取り専用。 # # コールバックは Headlamp が要求のホストから組み立てる(Envoy が x-forwarded-proto を diff --git a/apps/infisical-operator/rbac.yaml b/apps/infisical/operator-rbac.yaml similarity index 100% rename from apps/infisical-operator/rbac.yaml rename to apps/infisical/operator-rbac.yaml diff --git a/apps/infisical-operator/application.yaml b/apps/infisical/operator.yaml similarity index 100% rename from apps/infisical-operator/application.yaml rename to apps/infisical/operator.yaml diff --git a/apps/infisical-push-bridge/application.yaml b/apps/infisical/push-bridge.yaml similarity index 100% rename from apps/infisical-push-bridge/application.yaml rename to apps/infisical/push-bridge.yaml diff --git a/apps/k8up/etcd-snapshot.yaml b/apps/k8up/etcd-snapshot.yaml index 58bf59f..1b151ef 100644 --- a/apps/k8up/etcd-snapshot.yaml +++ b/apps/k8up/etcd-snapshot.yaml @@ -5,7 +5,7 @@ # (Argo CD が作った Application の同期状態、cert-manager の発行済み証明書など)まで含めて**一瞬で戻したいとき**の近道。 # 戻し方は docs/talos.md「etcd スナップショットからの復旧」の `bootstrap --recover-from`。 # -# Talos API への権限は `os:etcd:backup` だけ(talos/patches/etcd-backup.yaml)。 +# Talos API への権限は `os:etcd:backup` だけ(talos/controlplane.yaml)。 # 下の ServiceAccount を見て Talos が同名の Secret に talosconfig を書く。 # # **通知は何も足さなくてよい。** notify は過去 8 日に出てきた (host, path) を diff --git a/apps/netbird/routing-peer.yaml b/apps/netbird/routing-peer.yaml index 0fe7e84..77ebcc0 100644 --- a/apps/netbird/routing-peer.yaml +++ b/apps/netbird/routing-peer.yaml @@ -66,7 +66,7 @@ spec: app: netbird-routing-peer spec: # hostNetwork にしない。Pod のネットワークからでも LAN には届く(Cilium が masquerade する)。 - # ホストからは公開 DNS の A(WAN 側)に届かない(talos/patches/cluster.yaml の StaticHostConfig)ので、 + # ホストからは公開 DNS の A(WAN 側)に届かない(talos/controlplane.yaml の StaticHostConfig)ので、 # hostNetwork だと nb.doany.io:443 に繋げず `Management: Disconnected` で止まる。 containers: - name: netbird diff --git a/apps/netbird/server-config.yaml b/apps/netbird/server-config.yaml index 03cbd43..19e0d5b 100644 --- a/apps/netbird/server-config.yaml +++ b/apps/netbird/server-config.yaml @@ -53,7 +53,7 @@ data: reverseProxy: # Envoy(Cilium Gateway)の Pod から来る。X-Forwarded-For を信じる範囲。 - # Pod CIDR(talos/patches/cluster.yaml の podSubnets)。bootstrap/auth の oauth2-proxy と同じ値。 + # Pod CIDR(talos/controlplane.yaml の podSubnets)。bootstrap/auth の oauth2-proxy と同じ値。 trustedHTTPProxies: - "10.42.0.0/16" # NetBird 自身のピア網(既定 100.64.0.0/10) diff --git a/bootstrap/README.md b/bootstrap/README.md index e155c0b..3f0f5ef 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -7,7 +7,7 @@ | **machine config**([../talos/render.sh](../talos/render.sh) が描く) | Helm で入れるもの(cilium・cert-manager・argocd・infisical)、`apiserver/rbac.yaml`、SOPS 済みの Secret 2 つ | `render.sh` → `apply-config` → `upgrade-k8s`([../talos/README.md](../talos/README.md)「上げ方 / 当て直し方」) | | **ArgoCD**(Application `bootstrap`。[argocd/repos.yaml](argocd/repos.yaml)) | 残りの平文のマニフェスト(`ClusterIssuer`・Gateway・auth・`InfisicalSecret` など) | main へのマージ(prune / selfHeal 付き) | -`InfisicalSecret` の CRD だけは `apps/infisical-operator/` が入れるので、まっさらなクラスタでは `bootstrap` の同期が +`InfisicalSecret` の CRD だけは `apps/infisical/operator.yaml` が入れるので、まっさらなクラスタでは `bootstrap` の同期が 最初の何回か落ちて retry で戻る。 **SOPS(age)で暗号化したファイルは ArgoCD が当てない** ── 復号鍵をクラスタにも CI にも渡さないため。 @@ -34,23 +34,23 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。** ## Secret 平文の Secret はアプリのリポジトリにはコミットしない。値は **Infisical**(https://il.doany.io、このクラスタでセルフホスト)が持ち、 -**Infisical 純正 operator**(`apps/infisical-operator/`)が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `Secret` を作る。 +**Infisical 純正 operator**(`apps/infisical/operator.yaml`)が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `Secret` を作る。 認証は Kubernetes 方式(TokenReview・長命の資格情報なし)。 - `infisical/` … Infisical 本体(chart + Postgres/Redis)。公開経路は [`apps/infisical/httproute.yaml`](../apps/infisical/httproute.yaml)。 `secrets.sops.yaml` の `ENCRYPTION_KEY` が DB の暗号鍵で、これを失うと Infisical の中身が全部読めなくなる (クラスタで一番失ってはいけない値)。Postgres の PVC は既定の `local-path` で、`pg_dump` を k8up が日次で取っている。 -- **operator と push-bridge はここには無い**(`apps/infisical-operator/`、`apps/infisical-push-bridge/`)。 +- **operator と push-bridge はここには無い**(`apps/infisical/operator.yaml`、`apps/infisical/push-bridge.yaml`)。 ArgoCD 自身は operator に依存しないので、bootstrap に置く理由がない。 認証は Infisical の Kubernetes auth: operator が SA `infisical-auth` のトークンで Infisical にログインし、 Infisical がそのトークンで TokenReview を呼んで本物か確かめる(なので SA に `system:auth-delegator` を - 付けてある)。SA と RBAC は `apps/infisical-operator/rbac.yaml`。 + 付けてある)。SA と RBAC は `apps/infisical/operator-rbac.yaml`。 operator 側に長期の資格情報は無い(`identityId` は秘密ではない)。 ### API サーバの入口(GitHub Actions と人) **GitHub Actions は読むだけ。** cilium-drift.yml の OIDC トークンを API サーバが直接受け -([../talos/patches/apiserver.yaml](../talos/patches/apiserver.yaml) の `KubeAuthenticationConfig`)、 +([../talos/controlplane.yaml](../talos/controlplane.yaml) の `KubeAuthenticationConfig`)、 権限は `cilium-config` の `get` だけ([apiserver/rbac.yaml](apiserver/rbac.yaml))。 CA は [apiserver/ca.crt](apiserver/ca.crt)(`talos/secrets.yaml` のクラスタ CA。作り直したら替える)。 kubeconfig も age 鍵も GitHub に置いていない。 @@ -60,7 +60,7 @@ Headlamp が利用者本人の id_token を API サーバへ転送する。ア `entra:admin` で cluster-admin、それ以外は `entra:viewer` で読み取り専用(組み込みの `view` + CRD とノードぶんの `readonly-extra`)。**振り分けは CEL で、割り当ては Entra 側で完結する。** ここに置いたのは、ArgoCD も CI も要らずに効く層だから ── 締め出されたときに直せる。 -認証設定([../talos/patches/apiserver.yaml](../talos/patches/apiserver.yaml))も [apiserver/rbac.yaml](apiserver/rbac.yaml) も +認証設定([../talos/controlplane.yaml](../talos/controlplane.yaml))も [apiserver/rbac.yaml](apiserver/rbac.yaml) も machine config に載っているので、変えたら `render.sh` → `apply-config`(RBAC は inlineManifest なので `upgrade-k8s` まで)。 **ArgoCD には当てさせない**(`bootstrap` の exclude に `apiserver/`。締め出されたときに直せる層に置く)。 @@ -93,7 +93,7 @@ ArgoCD の tracking-id までコピーされると、ArgoCD がその `Secret` `Prune=false` も一緒にコピーさせて、prune の対象から外す。 **`SkipDryRunOnMissingResource=true`** ── **まっさらなクラスタでは CRD がまだ無く、`apps/` の同期が丸ごと落ちる** -(CRD を入れる `apps/infisical-operator/` が同じ同期の中にいる)。 +(CRD を入れる `apps/infisical/operator.yaml` が同じ同期の中にいる)。 仕組みは k8up の `Schedule` と同じで、説明は [apps/k8up/README.md](../apps/k8up/README.md)「CRD がまだ無いクラスタで apps を止めないこと」。 `bootstrap/` の `InfisicalSecret`(`argocd/oidc-secret.yaml`・`auth/secrets.yaml`・`infisical/smtp-secret.yaml`)にも付けてある。 diff --git a/bootstrap/apiserver/rbac.yaml b/bootstrap/apiserver/rbac.yaml index 5b6fb95..b24281f 100644 --- a/bootstrap/apiserver/rbac.yaml +++ b/bootstrap/apiserver/rbac.yaml @@ -27,14 +27,14 @@ roleRef: kind: ClusterRole name: cilium-drift subjects: - # talos/patches/apiserver.yaml の claimMappings.username.expression が作る名前。 + # talos/controlplane.yaml の claimMappings.username.expression が作る名前。 # `sub` は組織 ID / リポジトリ ID 入りの形に変わったので使っていない。 - apiGroup: rbac.authorization.k8s.io kind: User name: "gha:danything/gitops:refs/heads/main" --- # ここから下は人。Entra の id_token を Headlamp が API サーバへ転送してくる -# (talos/patches/apiserver.yaml の 2 つめの issuer)。アプリロール admin を持つ人は +# (talos/controlplane.yaml の 2 つめの issuer)。アプリロール admin を持つ人は # entra:admin、それ以外は entra:viewer のグループになって届く。 # # CI と同じくこのファイルは machine config に載るので、クラスタを作り直した diff --git a/bootstrap/cilium/values.yaml b/bootstrap/cilium/values.yaml index 3ff2acb..975cb2f 100644 --- a/bootstrap/cilium/values.yaml +++ b/bootstrap/cilium/values.yaml @@ -1,7 +1,7 @@ # Cilium の値の正本。talos/render.sh が描いて inlineManifest にする(render.sh → apply-config → upgrade-k8s)。 # cilium-config を直接いじらない(次の upgrade-k8s で黙って戻る)。 -# kube-proxy は talos/patches/cni.yaml で止めている +# kube-proxy は talos/controlplane.yaml で止めている kubeProxyReplacement: true # KubePrism。Talos が localhost:7445 で API サーバを負荷分散する。違うと Cilium が上がらない(render.sh の needle が見ている)。 k8sServiceHost: localhost diff --git a/docs/decisions.md b/docs/decisions.md index 8b8022e..4981cf9 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -128,7 +128,7 @@ bootstrap のあとに `helm install` する案は、再構築のたびに「CNI - 代わりに **描くたびに変わる値を潰した**(Hubble の証明書・infisical の `postgres-password`・`updatedAt`)。 同じ入力なら生成物はバイト単位で同じなので、`apply-config --dry-run` の差分がそのまま「何が変わるか」になる -**起動に要らないものは ArgoCD に置く**(metrics-server は `apps/metrics-server/`。版上げが `upgrade-k8s` 無しで済む)。cert-manager は移さない ── CRD を machine config から外すと Talos の prune が CRD を消しうる(消えれば +**起動に要らないものは ArgoCD に置く**(metrics-server は `apps/cluster/metrics-server.yaml`。版上げが `upgrade-k8s` 無しで済む)。cert-manager は移さない ── CRD を machine config から外すと Talos の prune が CRD を消しうる(消えれば 全 namespace の Certificate が道連れ)。 ## ルーティングの選定 @@ -226,10 +226,10 @@ Gateway API の session persistence は experimental チャネル限定、Cilium denpa の tuner-agent は Earthsoft PT3(`earth_pt3`)の `/dev/dvb` と B-CAS リーダーに依存している。 **公式 extension `siderolabs/dvb-pt3` を使う**([siderolabs/pkgs#1682](https://github.com/siderolabs/pkgs/pull/1682))。 v1.15.0-alpha.1 からしか無いので、**正式版を待つより PT3 を先に取り、本番を alpha に乗せている**。 -extension は `earth_pt3` を blacklist して配るので、`talos/patches/main.yaml` の `machine.kernel.modules` で読ませる。 +extension は `earth_pt3` を blacklist して配るので、`talos/controlplane.yaml` の `machine.kernel.modules` で読ませる。 **駄目だったときの保険: KubeVirt の VM にパススルー。** PT3(05:00.0)は IOMMU グループ 36 に単独、B-CAS は USB(GemPC Twin 08e6:3437)。 -`intel_iommu=on` は schematic に残してあるが、vfio は `talos/patches/main.yaml` から外した(PT3 をホストのドライバと取り合う)ので、戻るなら足し直す。 +`intel_iommu=on` は schematic に残してあるが、vfio は `talos/controlplane.yaml` から外した(PT3 をホストのドライバと取り合う)ので、戻るなら足し直す。 **自前 extension を `imager` で焼く案は採らない**(アップグレードのたびに自前ビルドになる)。 ## 依存の更新をどこまで自動で入れるか diff --git a/docs/router.md b/docs/router.md index 3515057..04ce0d3 100644 --- a/docs/router.md +++ b/docs/router.md @@ -20,7 +20,7 @@ | 項目 | 値 | | --- | --- | | LAN | `10.0.0.0/24`、ルーターは `10.0.0.1` | -| ノード `main` | `10.0.0.2`(bond0)、`240f:6d:842b:1::2`。**ノード側の静的設定**(`talos/patches/cluster.yaml`。DHCP は使わない。既定経路は `10.0.0.1`、IPv6 は RA を受ける) | +| ノード `main` | `10.0.0.2`(bond0)、`240f:6d:842b:1::2`。**ノード側の静的設定**(`talos/controlplane.yaml`。DHCP は使わない。既定経路は `10.0.0.1`、IPv6 は RA を受ける) | | 別の LAN | `10.10.0.0/24`(ゲートウェイ `10.10.0.1`)。ノードの eno4 が `10.10.0.4`(静的。既定経路には使わない) | | DHCP | ルーターが配る。`10.0.0.10` から 128 個、リース 24 時間。固定割り当て・除外は 0 件。DHCPv6 のアドレス割り当ては自動 | | DNS | **ルーターの DHCP には DNS の欄が無い**(配るのはルーター自身の `10.0.0.1`)。AdGuard Home を使うのは、端末側で指定したものと、NetBird の端末(NetBird の DNS 設定で AdGuard を引く。pulumi/netbird.ts) | @@ -54,10 +54,10 @@ AdGuard Home は問い合わせ元を絞っておらず(apps/adguardhome/deploym 「IPv6ホスト公開設定」の既定を「遮断」にし、ノード `main` だけをエントリで公開している(既定の「公開」だと LAN の全端末に外から IPv6 で直接届く)。 - **エントリは MAC アドレスで指す**(bond0 の `e6:2c:1b:1a:a0:39`。`talosctl -n 10.0.0.2 get links bond0` で見る。ルーターの「接続端末 情報画面」にも同じ値が出る)。IPv6 アドレスで指すと、RA のプレフィックスが変わったときに外れる -- **この MAC はノードの設定で固定している**(`talos/patches/cluster.yaml` の `BondConfig` の `hardwareAddr`)。固定しないと bond は先頭のメンバーの MAC を引き継ぎ、 +- **この MAC はノードの設定で固定している**(`talos/controlplane.yaml` の `BondConfig` の `hardwareAddr`)。固定しないと bond は先頭のメンバーの MAC を引き継ぎ、 再起動で変わってエントリが外れる。NIC を替えても Z440 に移しても、この値は変わらない - **公開する AAAA は静的な `::2`。** cloudflare-ddns の `local.iface.stable:bond0` は一時アドレス以外から選ぶので、SLAAC のアドレスがあると - そちらを書いてしまう。`bond0` は `autoconf=0`(talos/patches/cluster.yaml)で SLAAC を付けない + そちらを書いてしまう。`bond0` は `autoconf=0`(talos/controlplane.yaml)で SLAAC を付けない - 公開の中身は DMZ と同じ(IPv6 すべて・ポート any)。ノードで受けているものは IPv6 でも全部外に出る - `fj` / `nb` / `m` / `ks` / `lk` などの AAAA はノード(`240f:6d:842b:1::2`)を指すので、**このエントリを消すと IPv6 の利用者から直接つなぐアプリが見えなくなる** - 確かめ方: 別の回線の IPv6 から名前で開く(アドレス直打ちは SNI が無く Gateway が切る)。J:COM 側の PC から diff --git a/docs/talos.md b/docs/talos.md index ce8531c..a35c49c 100644 --- a/docs/talos.md +++ b/docs/talos.md @@ -51,11 +51,11 @@ curl -X POST --data-binary @schematic.yaml https://factory.talos.dev/schematics | インストール先ディスク | `UnattendedInstallConfig` の `provisioning.diskSelector.match`(**CEL 式**。例 `disk.dev_path == "/dev/sda"`) | | カーネル引数 | `machine.install.extraKernelArgs` は使えない(同じく衝突)。**schematic の `customization.extraKernelArgs`** | | host DNS | `ResolverConfig`。**既定で有効**なので普通は書かなくてよい | -| bond とそのメンバー | `BondConfig` / `LinkConfig`。v1alpha1 の `machine.network.interfaces` は bond の MAC を指定できない([patches/cluster.yaml](../talos/patches/cluster.yaml)) | -| PV の置き場 | `UserVolumeConfig`。マウント先(`/var/mnt/`)は kubelet に自動で通るので `extraMounts` は要らない([patches/volumes.yaml](../talos/patches/volumes.yaml))。ディスク上のラベルは `u-` | -| control-plane への scheduling 許可 | v1alpha1 の `allowSchedulingOnControlPlanes` は弾かれる。**`KubeNodeConfig` を `$patch: delete` して `labels` だけ書き戻す**([patches/cluster.yaml](../talos/patches/cluster.yaml)) | -| CNI を止める | `KubeFlannelCNIConfig` を `$patch: delete`、`KubeProxyConfig` を `enabled: false`([patches/cni.yaml](../talos/patches/cni.yaml))。**v1alpha1 の `cni.name: none` はもう書けない** | -| カーネルモジュール | `machine.kernel.modules`。**並べた順ではなく名前の順に読まれる**。Talos 1.14 以降は usermode の modprobe helper が無い(`/proc/sys/kernel/modprobe` が空)ので、`request_module` で依存を引くドライバは順番が狂うと失敗する(PT3 で踏んだ。[patches/main.yaml](../talos/patches/main.yaml)、上流に PR 中) | +| bond とそのメンバー | `BondConfig` / `LinkConfig`。v1alpha1 の `machine.network.interfaces` は bond の MAC を指定できない([controlplane.yaml](../talos/controlplane.yaml)) | +| PV の置き場 | `UserVolumeConfig`。マウント先(`/var/mnt/`)は kubelet に自動で通るので `extraMounts` は要らない([controlplane.yaml](../talos/controlplane.yaml))。ディスク上のラベルは `u-` | +| control-plane への scheduling 許可 | v1alpha1 の `allowSchedulingOnControlPlanes` は弾かれる。**`KubeNodeConfig` を `$patch: delete` して `labels` だけ書き戻す**([controlplane.yaml](../talos/controlplane.yaml)) | +| CNI を止める | `KubeFlannelCNIConfig` を `$patch: delete`、`KubeProxyConfig` を `enabled: false`([controlplane.yaml](../talos/controlplane.yaml))。**v1alpha1 の `cni.name: none` はもう書けない** | +| カーネルモジュール | `machine.kernel.modules`。**並べた順ではなく名前の順に読まれる**。Talos 1.14 以降は usermode の modprobe helper が無い(`/proc/sys/kernel/modprobe` が空)ので、`request_module` で依存を引くドライバは順番が狂うと失敗する(PT3 で踏んだ。[controlplane.yaml](../talos/controlplane.yaml)、上流に PR 中) | ### PSA のラベル(Pod Security Admission が既定で `baseline`) @@ -66,9 +66,9 @@ curl -X POST --data-binary @schematic.yaml https://factory.talos.dev/schematics | namespace | baseline に通らない理由 | | --- | --- | | `kube-system` | Cilium(privileged・hostNetwork・hostPath・SYS_ADMIN/NET_ADMIN・hostPort)。**Talos の側で例外にしてある** | -| `local-path-storage` | ヘルパー Pod の hostPath。[`talos/manifests/local-path.yaml`](../talos/manifests/local-path.yaml) がラベルを付ける | +| `local-path-storage` | ヘルパー Pod の hostPath。[`talos/local-path.yaml`](../talos/local-path.yaml) がラベルを付ける | | `netbird` | hostPort 51820(routing peer の WireGuard)、NET_ADMIN 等、hostPath | -| `denpa` | privileged・hostPath(`/dev/dvb`・`/dev/bus`・`/dev/dri`)、PT3 を付け直す DaemonSet([apps/pt3/](../apps/pt3/))。**ラベルは denpa リポジトリの `deploy/namespace.yaml`** にあり、gitops だけを grep すると抜けて見える | +| `denpa` | privileged・hostPath(`/dev/dvb`・`/dev/bus`・`/dev/dri`)、PT3 を付け直す DaemonSet([apps/cluster/pt3-rebind.yaml](../apps/cluster/pt3-rebind.yaml))。**ラベルは denpa リポジトリの `deploy/namespace.yaml`** にあり、gitops だけを grep すると抜けて見える | | `adguardhome` | hostPort 53 / 853 | | `matrix` | hostPort 8443(LiveKit) | | `3proxy` | hostPort 3129(TLS 終端サイドカー) | @@ -122,7 +122,7 @@ Sidero のドキュメントの「Talos only creates missing resources from inli ## VM ブートドリル [.github/workflows/talos-boot.yml](../.github/workflows/talos-boot.yml) が PR ごとに描いた machine config を QEMU で起動して見る。 -見つけた穴は直した場所(`talos/patches/`・`render.sh`・`add-namespace.awk`)に理由を書いてある。 +見つけた穴は直した場所(`talos/controlplane.yaml`・`render.sh`・`add-namespace.awk`)に理由を書いてある。 VM では確かめられないもの: PT3 のモジュールの読み順、bond のフェイルオーバーの復旧方向(落とす方向は 8 秒以内で `talosctl` は途切れない)。 diff --git a/docs/z440-migration.md b/docs/z440-migration.md index 00a3118..f74119f 100644 --- a/docs/z440-migration.md +++ b/docs/z440-migration.md @@ -38,7 +38,7 @@ Z440 では NIC の名前が DL360 と変わる(`eno1`〜`eno4` にはならな - 選ぶのは **`mac(link.permanent_addr)`**。bond のメンバーになると今の MAC は bond の MAC に変わるので、元の MAC で見る - **`bond0` は残す**(メンバーは `lan0` の 1 本)。MAC は `hardwareAddr: e6:2c:1b:1a:a0:39` で固定してあるので、 **DL360 でも Z440 でも変わらない**。ルーターの IPv6 公開のエントリ([router.md](router.md))は直さなくてよい -- `talos/patches/cluster.yaml` で `eno4` を名指ししているところ(`LinkConfig` と sysctl の `net.ipv6.conf.eno4.disable_ipv6`)も別名に直す +- `talos/controlplane.yaml` で `eno4` を名指ししているところ(`LinkConfig` と sysctl の `net.ipv6.conf.eno4.disable_ipv6`)も別名に直す ```yaml apiVersion: v1alpha1 @@ -49,7 +49,7 @@ selector: # BondConfig の links: [lan0]、LinkConfig の name: jcom0 ``` -**ディスク**: `disk.dev_path == "/dev/sda"` は Z440 でも SSD 1 本なら同じになるはず(`talosctl get disks` で見る。`talos/patches/main.yaml` の注)。 +**ディスク**: `disk.dev_path == "/dev/sda"` は Z440 でも SSD 1 本なら同じになるはず(`talosctl get disks` で見る。`talos/controlplane.yaml` の注)。 **この config は DL360 では効かない**(MAC が当たらず 10.0 側が上がらない)ので、普通に当てない。 `talosctl apply-config --mode=staged` で**次の起動から**効くように置き、電源を切る直前にやる(手順 4)。 @@ -90,7 +90,7 @@ AMT の SOL は、OS からは PCI のシリアルポート(「Intel … KT Cont 1. 番号を確かめる: `talosctl -n 10.0.0.2 dmesg | grep ttyS`(`ttyS4` などになる) 2. Image Factory の schematic の `customization.extraKernelArgs` に `console=tty0` と `console=ttyS<番号>,115200n8` を足し、 新しい schematic ID で `talosctl upgrade --image factory.talos.dev/installer/:<版>`(再起動する)。 - カーネル引数は machine config に書けない(`talos/patches/main.yaml` の注、[talos.md](talos.md))。 + カーネル引数は machine config に書けない(`talos/controlplane.yaml` の注、[talos.md](talos.md))。 schematic ID は `talos/versions.yaml` と `main.yaml` の注も揃える。**Arc の extension を足すのも同じ手順**なので一緒にやる (`siderolabs/i915` で足りるか要確認。Arc B シリーズなどは `siderolabs/xe`。**両方は入れない** ── 同じ GPU を取り合う) @@ -150,6 +150,6 @@ DL360 の `eno2`(bond の 2 本目)のように、片方の機械にしか無い - [router.md](router.md)・この文書の構成表を Z440 に直す - 「iLO(10.0.0.3)」と書いてあるところを AMT(10.0.0.5)に直す: apps/netbird/routing-peer.yaml のコメント、ROADMAP.md、 docs/decisions.md、recovery/README.md(建て直しの入口。ISO の起動と Secure Boot の手順も AMT の IDE-R に)、docs/talos.md の対象の行 -- **10.10 側を外す**(小さいサーバをルーターにしたあと): `talos/patches/cluster.yaml` の 10.10 側の `LinkConfig`・別名・ +- **10.10 側を外す**(小さいサーバをルーターにしたあと): `talos/controlplane.yaml` の 10.10 側の `LinkConfig`・別名・ `disable_ipv6` の sysctl を消し、AdGuard の 10.10.0.4 の待ち受け(apps/adguardhome/service-dns.yaml)を外し、 10.10 側の機器の DNS を 10.0.0.2 にする。NetBird の 10.10.0.0/24 の経路は、BL1500HM の静的ルーティングで届くのでそのままでよい diff --git a/recovery/README.md b/recovery/README.md index 5b659e8..0be1ae3 100644 --- a/recovery/README.md +++ b/recovery/README.md @@ -78,7 +78,7 @@ curl -LO https://factory.talos.dev/image/96371c8f956f7f5f9d09925bb49fe096c0cad05 - **Secure Boot が Disabled**(RBSU は POST 中に F9 → Server Security → Secure Boot Settings)。Boot Mode は UEFI のまま - POST 中に **F11** で USB を選ぶ(iLO の Virtual Media なら Image File CD-ROM/DVD に ISO の URL を貼り、Reset して F11) - コンソールに maintenance mode の IP が出る。インタフェースが `eno1` / `eno2` / `eno4`、インストール先が `/dev/sda`(447 GB)であること - (違ったら `talos/patches/cluster.yaml` / `main.yaml` を直して 1 に戻る。**`diskSelector` の先は書いたら消える**): + (違ったら `talos/controlplane.yaml` / `main.yaml` を直して 1 に戻る。**`diskSelector` の先は書いたら消える**): ```shell IP=<コンソールに出た IP> @@ -250,7 +250,7 @@ kubectl get httproute,grpcroute -A -o jsonpath='{range .items[*]}{range .spec.ho - [ ] `talosctl get routes -o yaml | grep -B10 'protocol: ra'` に `dst: ""`(IPv6 の既定経路)が残っている - [ ] netbird で外から VPN に入れる(ブラウザでは確かめられない)、AdGuard(`dig @10.10.0.4 example.com`) - [ ] **PT3**: `talosctl ls /dev/dvb` に adapter0〜3 と各 `frontend0`、`talosctl ls /dev/bus/usb` に B-CAS リーダー、録画が動く - (frontend が無いときは [../apps/pt3/rebind.yaml](../apps/pt3/rebind.yaml) が付け直しているか) + (frontend が無いときは [../apps/cluster/pt3-rebind.yaml](../apps/cluster/pt3-rebind.yaml) が付け直しているか) - [ ] Renovate の CronJob が次の回で動く(`kubectl -n renovate get jobs`)、k8up の通知が翌日 OK ✅ - [ ] etcd のスナップショットを 1 回手で取る(`kubectl -n k8up create job --from=cronjob/etcd-snapshot etcd-snapshot-first`) @@ -267,7 +267,7 @@ rm -f ~/.config/restic.env ~/infisical-load.log | ノードが上がらない | iLO のコンソール。ダッシュボードに版と健全性が出る | | Pod が Pending | **PSA のラベル**([../docs/talos.md](../docs/talos.md)「PSA のラベル」)か、control-plane の taint | | PVC が Bound しない | local-path の ConfigMap に `setup` / `teardown` があるか | -| 非公開イメージが引けない | `talos/registries.yaml` が machine config に入っているか、Forgejo が戻っているか(8) | +| 非公開イメージが引けない | `talos/controlplane.yaml` の fj.doany.io の `password` が machine config に入っているか、Forgejo が戻っているか(8) | | bootstrap apply が TLS で落ちる | `secrets.yaml` を作り直した。`bootstrap/apiserver/ca.crt` を新しい `secrets.yaml` の Kubernetes の CA(`certs.k8s.crt` を base64 で戻したもの)にする | | Application が `OutOfSync / Missing` のまま | `retry`(約 33 分)で自力で戻る。30 分以上動かなければ `kubectl -n argocd patch application <名前> --type=merge -p '{"operation":{"sync":{"revision":"HEAD"},"initiatedBy":{"username":"me"}}}'`。**chart のものは `HEAD` ではなく `spec.source.targetRevision` の値** | | inlineManifest を直したのに効かない | `render.sh` → `apply-config` → **`upgrade-k8s`**([../talos/README.md](../talos/README.md)「上げ方 / 当て直し方」) | diff --git a/talos/README.md b/talos/README.md index 46547b9..ae6d6da 100644 --- a/talos/README.md +++ b/talos/README.md @@ -8,9 +8,9 @@ 新しい Talos が出たら PR を作る。**適用は自動化しない**(下の「適用は CI にやらせない」)。 コマンドに埋めてある版と schematic も、変えるときは versions.yaml と揃えること。talhelper は使わない。 -**組み立ては [render.sh](render.sh) が全部やる。** 手で書く部分(`patches/`)と、 +**組み立ては [render.sh](render.sh) が全部やる。** 手で書く部分は [controlplane.yaml](controlplane.yaml) の 1 本だけで、 他から描いてくる部分(Cilium・cert-manager・argocd・infisical は `bootstrap//` の version.yaml と values.yaml から、 -local-path-provisioner は `manifests/` から)を 1 か所で組む。**値をどこにも二度書かない**のが目的。 +local-path-provisioner は [local-path.yaml](local-path.yaml) から)と 1 か所で組む。**値をどこにも二度書かない**のが目的。 CI([talos-boot](../.github/workflows/talos-boot.yml))も同じ `render.sh` を使う (別の作り方をすると「CI は通るが実機では通らない」が起きる)。 @@ -18,8 +18,18 @@ CI([talos-boot](../.github/workflows/talos-boot.yml))も同じ `render.sh` を ./talos/render.sh talos/clusterconfig # 出力先は丸ごと消して作り直す(.gitignore 済み) ``` -**秘密は 2 つともコミット済み** ── [secrets.yaml](secrets.yaml) はクラスタの CA 一式、 -[registries.yaml](registries.yaml) は fj.doany.io のイメージ取得の資格情報。 +**controlplane.yaml を丸ごとコミットしないのは**、生成物の 9 割が chart を描いたもの(約 300 KB の 1 行文字列)で、 +読めず、chart や Kubernetes の版を上げるたびに描き直しのコミットが要るから。 +**inlineManifest をファイルで指す仕組みは Talos に無い**(machine config はノードが読むので手元のパスは書けない。 +外を指せるのは URL の `KubeExternalManifestConfig` だけで、CRD はそれで渡している)。ファイルを包むのは render.sh の `inline`。 + +**秘密は 2 か所、どちらも SOPS でコミット済み**(規則は [`../.sops.yaml`](../.sops.yaml)。**平文は git に入れない**)。 + +- [secrets.yaml](secrets.yaml): クラスタの CA 一式(`talosctl gen secrets` の形)。丸ごと暗号化 +- [controlplane.yaml](controlplane.yaml): `password` の値(下の fj.doany.io の資格情報)**だけ**をその場で暗号化。 + ほかは平文のまま読めて、`sops` を通さず直接直せる(`mac_only_encrypted`)。パスワードを変えるときだけ `sops edit talos/controlplane.yaml`。 + SOPS は**ドキュメントごとに `sops:` のメタデータを付け、インデントを 4 にする**(触らない)。render.sh は `sops -d` してから渡す + **`secrets.yaml` を作り直すと別のクラスタになる**(`bootstrap/apiserver/ca.crt` と Infisical の Kubernetes auth の CA も 替えることになる)ので触らない。まっさらから組むときだけ: @@ -29,30 +39,18 @@ talosctl gen secrets -o talos/secrets.yaml && sops -e -i talos/secrets.yaml ## fj.doany.io の資格情報 -ノード単位で持つので **namespace ごとの `imagePullSecrets` が要らない**(`machine.registries`)。 +ノード単位で持つので **namespace ごとの `imagePullSecrets` が要らない**(controlplane.yaml の `machine.registries`)。 持つのは **fj.doany.io**(Forgejo のコンテナレジストリ)だけで、Forgejo のユーザー `yui` の -`read:package` だけのトークン(全リポジトリ。Forgejo の名前は `talos-registry-pull`)。 +`read:package` だけのトークン(全リポジトリ。Forgejo の名前は `talos-registry-pull`)。値は controlplane.yaml の中で暗号化してある。 `username` は Forgejo が見ない(トークンの持ち主で決まる。`info` のままにしてあるが、何でも通る)。無いと非公開のリポジトリの -イメージが引けない。ghcr.io に残るイメージは全部公開。SOPS で暗号化してある(**平文は git に入れない**。規則は [`../.sops.yaml`](../.sops.yaml)): - -```yaml -# talos/registries.yaml (暗号化前) -machine: - registries: - config: - fj.doany.io: - auth: - username: info - password: … -``` +イメージが引けない。ghcr.io に残るイメージは全部公開。 **fj.doany.io はこれだけでは取れない。** 公開 DNS の A(WAN 側)にはノードから届かないので、 -ノードの名前解決で fj.doany.io をノード自身(10.0.0.2 の Gateway)に向ける([patches/cluster.yaml](patches/cluster.yaml) の `StaticHostConfig`)。 +ノードの名前解決で fj.doany.io をノード自身(10.0.0.2 の Gateway)に向ける(controlplane.yaml の `StaticHostConfig`)。 -`render.sh` はこのファイルを復号して `--config-patch` に足す(**無ければ止まる**)。 -CI には age の鍵を渡さないので、`REGISTRIES` にダミーを入れて経路だけ通している。 +CI には age の鍵を渡さないので、controlplane.yaml の `ENC[…]` を `ci-dummy` に置き換えて経路だけ通している(`cloudflare-secret.sops.yaml` と同じやり方)。 -**パッチは PR ごとに CI が検証する。** 使い捨ての秘密で `render.sh` を通す。`render.sh` は `talosctl validate --mode metal` に加えて、 +**controlplane.yaml は PR ごとに CI が検証する。** 使い捨ての秘密で `render.sh` を通す。`render.sh` は `talosctl validate --mode metal` に加えて、 生成物に想定の設定(`ks.doany.io`・OIDC の issuer・bond の `balance-alb` など)が実際に入っているかまで見る ── **`gen config` は知らないキーを警告なく捨てることがある**ので、通ったことだけでは足りない。 needle は `render.sh` の末尾の 1 か所だけに置く。 @@ -70,7 +68,7 @@ needle は `render.sh` の末尾の 1 か所だけに置く。 | --- | --- | --- | | `versions.yaml` の `talos:`(または schematic) | `talosctl upgrade --image factory.talos.dev/installer/:<版>` | **再起動** | | `versions.yaml` の `kubernetes:` | `render.sh` → `apply-config` → `talosctl upgrade-k8s --to <版>` | 無し | -| `patches/` / `manifests/` / `bootstrap/` のほぼ全部(cilium・cert-manager・apiserver/rbac.yaml・{argocd,infisical}/ の values・SOPS の Secret 2 つ) | `render.sh` → `apply-config` → `talosctl upgrade-k8s` | 場合による | +| `controlplane.yaml` / `local-path.yaml` / `bootstrap/` のほぼ全部(cilium・cert-manager・apiserver/rbac.yaml・{argocd,infisical}/ の values・SOPS の Secret 2 つ) | `render.sh` → `apply-config` → `talosctl upgrade-k8s` | 場合による | ### inlineManifests を直したとき @@ -110,7 +108,7 @@ talosctl upgrade --image factory.talos.dev/installer/:<版> ### 当てても効かないもの -- **ディスクの割り方**(`patches/volumes.yaml`)。ボリュームは**まだ確保されていないときにしか +- **ディスクの割り方**(controlplane.yaml の `VolumeConfig` / `UserVolumeConfig`)。ボリュームは**まだ確保されていないときにしか 効かない**ので、あとから `apply-config` しても黙って無視される。変えるには入れ直し - **`talosctl upgrade-k8s` は Kubernetes の API に直接繋ぎに行く。** 既定ではクラスタの endpoint(10.0.0.2)へ行き、 作業用 PC からは届かないので `--endpoint https://10.10.0.4:6443` を渡す diff --git a/talos/controlplane.yaml b/talos/controlplane.yaml new file mode 100644 index 0000000..e65fc93 --- /dev/null +++ b/talos/controlplane.yaml @@ -0,0 +1,481 @@ +# ノード main の machine config のうち、手で書く部分の全部。render.sh が `gen config --config-patch` に渡す。 +# chart から描く部分(Cilium・ArgoCD・infisical・cert-manager・local-path)と版は render.sh が足す。 +# +# **SOPS で password の値だけ暗号化してある**(../.sops.yaml)。平文の部分はそのまま直してよい。 +# パスワードを変えるときは `sops edit talos/controlplane.yaml`。render.sh が `sops -d` してから渡す。 +# 各ドキュメントの末尾の `sops:` は SOPS のメタデータ(触らない)。 +# +# 型付きドキュメントと v1alpha1 で書く場所が決まっている(../docs/talos.md「machine config の作法」)。 +# ドキュメントは上から順に当たる(KubeNodeConfig の delete → 書き戻し は順番に意味がある)。 +machine: + sysctls: + # 無いと IPv6 の既定経路が消える(Kubernetes が forwarding=1 にすると accept_ra=1 は RA を無視する) + net.ipv6.conf.bond0.accept_ra: "2" + # IID を MAC 由来にしない。2(stable-privacy)は stable_secret が無いと書き込みが失敗し続ける + net.ipv6.conf.bond0.addr_gen_mode: "3" + # SLAAC のアドレスを付けない(あると cloudflare-ddns が静的な ::2 でなくそちらを AAAA に書く)。 + # RA の既定経路はそのまま受ける。 + net.ipv6.conf.bond0.autoconf: "0" + # Forgejo の Runner(dind)のファイル監視。既定の 128 では EMFILE で落ちる + fs.inotify.max_user_instances: "8192" + fs.inotify.max_user_watches: "1048576" + # IPv6 は bond0 に一本化する(eno4 の RA に既定経路を奪われると非対称になる) + net.ipv6.conf.eno4.disable_ipv6: "1" + network: + # local-path の PV の nodeAffinity が main を指している + hostname: main + # PT3 のドライバ(../docs/decisions.md「PT3 チューナー」)。dvb-pt3 は earth_pt3 を blacklist しているので明示で読ませる。 + # 復調とチューナーも要る(Talos には request_module を満たす modprobe が無い)。並べた順には読まれないので、 + # 付け直しは ../apps/cluster/pt3-rebind.yaml。 + kernel: + modules: + - name: tc90522 + - name: qm1d1c0042 + - name: mxl301rf + - name: earth-pt3 + # etcd スナップショットの日次取得(../apps/k8up/etcd-snapshot.yaml)のため、Pod から Talos API を叩く。 + # 役割は os:etcd:backup だけ、namespace も k8up に絞る(os:admin を Pod に置かずに済む唯一の仕組み)。 + features: + kubernetesTalosAPIAccess: + enabled: true + allowedRoles: + - os:etcd:backup + allowedKubernetesNamespaces: + - k8up + # fj.doany.io(Forgejo のコンテナレジストリ)の資格情報。ノード単位なので namespace ごとの imagePullSecrets が要らない。 + # username は Forgejo が見ない(トークンの持ち主で決まる)。password は SOPS で暗号化(冒頭)。取りに行く先は下の StaticHostConfig。 + registries: + config: + fj.doany.io: + auth: + username: info + password: ENC[AES256_GCM,data:GHSZMJUQN11ZQxb9MFfSeVBDvIMjcyC+IX4Zpnm6/lHd58A9mIpKJA==,iv:Yi29sF8b3xnXsNy1nMb+f8ZoFaUN0e84Zzc6/mZ4ghI=,tag:RXDdDe6rDngGO2Nl9GcAHA==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# カーネル引数と拡張は machine.install に書けないので schematic に入れている(versions.yaml)。 +apiVersion: v1alpha1 +kind: UnattendedInstallConfig +provisioning: + diskSelector: + # SATA の SSD。箱を替えたら当日 `talosctl get disks` を見てから確定させる + match: disk.dev_path == "/dev/sda" + wipe: false +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# ディスクの割り方。既定だと EPHEMERAL(/var)が全部を取り、PVC の暴走で etcd ごと道連れになる。 +# ボリュームの設定は未確保のときにしか効かない(あとから書き足しても黙って無視され、直すには入れ直し)。 +# EPHEMERAL に maxSize が無いと grow が全部持っていく(siderolabs/talos#12713)。 +# 64GiB は実機の使用量(containerd 22G ほか)から。残りが local-path 用。 +apiVersion: v1alpha1 +kind: VolumeConfig +name: EPHEMERAL +provisioning: + diskSelector: + match: system_disk + maxSize: 64GiB + grow: true +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# PV データの置き場。/var/mnt/ 固定で、local-path.yaml の paths がここを指す。 +apiVersion: v1alpha1 +kind: UserVolumeConfig +name: local-path +provisioning: + diskSelector: + match: system_disk + minSize: 50GiB + grow: true +filesystem: + type: xfs +# /var 系は既定で nosuid,nodev,noexec になり、PVC の中の実行ファイル(Forgejo のサーバ側フック)が動かない。 +# コンテナは PSA と securityContext で押さえている。 +mount: + secure: false +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# MAC を固定する。ルーターの IPv6 ホスト公開がこの MAC を指していて(docs/router.md)、 +# 指定しないと eno1 の MAC を引き継ぎ、NIC や箱を替えた日に外からの IPv6 が全部止まる。 +apiVersion: v1alpha1 +kind: BondConfig +name: bond0 +links: + - eno1 + - eno2 +bondMode: balance-alb +miimon: 100 +hardwareAddr: e6:2c:1b:1a:a0:39 +addresses: + - address: 10.0.0.2/24 + # プレフィックスは ISP 由来で変わりうる(公開は cloudflare-ddns が実際のアドレスで行う) + - address: 240f:6d:842b:1::2/64 +routes: + # IPv6 の既定経路は RA 由来なので書かない(上の accept_ra とセット) + - gateway: 10.0.0.1 +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# 10.10 側。DHCP は明示の設定が無い限り無効 +apiVersion: v1alpha1 +kind: LinkConfig +name: eno4 +addresses: + - address: 10.10.0.4/24 +routes: + - destination: 10.10.0.0/24 + gateway: 10.10.0.1 +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# gen config が入れる `auto: stable` と `hostname` は併記できず、パッチから `auto` を消せないので +# ドキュメントごと落として machine.network.hostname を効かせる。 +apiVersion: v1alpha1 +kind: HostnameConfig +$patch: delete +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# ノード自身の名前解決(クラスタ内は CoreDNS、LAN 向けは AdGuard で別) +apiVersion: v1alpha1 +kind: ResolverConfig +nameservers: + - address: 2606:4700:4700::1111 + - address: 2606:4700:4700::1001 + - address: 1.1.1.1 + - address: 1.0.0.1 +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# ノードから fj.doany.io のイメージを取るため自分に向ける(公開 DNS の A は WAN 側で、ノードからは届かない) +apiVersion: v1alpha1 +kind: StaticHostConfig +name: 10.0.0.2 +hostnames: + - fj.doany.io +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +apiVersion: v1alpha1 +kind: KubeNetworkConfig +podSubnets: + - 10.42.0.0/16 + - fd42::/64 +serviceSubnets: + - 10.43.0.0/16 + # /108 以下が必須 + - fd43::/108 +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# Cilium のために Talos の既定の CNI(flannel)と kube-proxy を止める。CoreDNS は止めない。 +# v1alpha1 の cluster.network.cni は型付きドキュメントと衝突して書けない。 +# KubeFlannelCNIConfig は在ること自体が有効化なので丸ごと消す。 +apiVersion: v1alpha1 +kind: KubeFlannelCNIConfig +$patch: delete +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +apiVersion: v1alpha1 +kind: KubeProxyConfig +enabled: false +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# 単一ノードなので control-plane の NoSchedule を付けない(toleration の無い Pod が全部 Pending になる)。 +# taints はマップなので `{}` では消えず、`$patch: replace` は validate が落ちる。 +# ドキュメントごと消して labels だけ書き戻すのが唯一の手。 +apiVersion: v1alpha1 +kind: KubeNodeConfig +$patch: delete +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +apiVersion: v1alpha1 +kind: KubeNodeConfig +# 消したままだと ROLES が になる +labels: + node-role.kubernetes.io/control-plane: "" + node.kubernetes.io/exclude-from-external-load-balancers: "" +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# API サーバの公開名。外から ks.doany.io を証明書検証つきで使うため(公開名は自動では入らない) +apiVersion: v1alpha1 +kind: KubeAPIServerConfig +certExtraSANs: + - ks.doany.io +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 +--- +# 外から入る CI(cilium-drift.yml の OIDC トークン)と人(Entra)の認証。権限の中身は ../bootstrap/apiserver/rbac.yaml。 +apiVersion: v1alpha1 +kind: KubeAuthenticationConfig +# `anonymous` を消さないこと。与えると既定を置き換えるので、書き忘れると +# /livez /readyz /healthz を匿名で叩けずコントロールプレーンが上がらない。 +configuration: + anonymous: + enabled: true + conditions: + - path: /livez + - path: /readyz + - path: /healthz + jwt: + - issuer: + url: https://token.actions.githubusercontent.com + # 既定の audience(所有者の URL)は他のワークフローと共有されるので使わない + audiences: + - https://ks.doany.io + claimMappings: + # `sub` は形式が変わると RBAC が黙って効かなくなるので、要る情報から組み立てる + username: + expression: '"gha:" + claims.repository + ":" + claims.ref' + # issuer は世界中の GitHub Actions が使う公開の IdP。効いているのはここで、緩めると即座に穴になる。 + claimValidationRules: + - expression: claims.repository == "danything/gitops" + message: danything/gitops 以外は受け付けない + - expression: claims.ref == "refs/heads/main" + message: main 以外のブランチからは受け付けない + - expression: claims.workflow_ref.startsWith("danything/gitops/.github/workflows/cilium-drift.yml@") + message: cilium-drift.yml 以外のワークフローからは受け付けない + # Entra ID。Headlamp が利用者本人の id_token を転送してくる。 + - issuer: + url: https://login.microsoftonline.com/22ac977d-b140-4dc8-8150-90d645d3475b/v2.0 + # oauth2-proxy と同じアプリ登録なので、この組で出た id_token はどれもクラスタの資格情報になる + audiences: + - b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e + claimMappings: + username: + expression: '"entra:" + claims.preferred_username' + # ロールが無い利用者には roles クレーム自体が無いので has() で守る + groups: + expression: '(has(claims.roles) && "admin" in claims.roles) ? ["entra:admin"] : ["entra:viewer"]' +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0MVdVeTNnQ04xOWU3YlNj + UThxRm8yL0UvWDRCVHpmbE1EdGhkeUhWYTA4CkhYU3cwYStTM1R6VmQwNFJGYkxW + Y2hBYklsZ2VLWGpCeW5YNENQTXl3UVEKLS0tIHZYZnk2ZTh6WldUUGpYek1zTmZ1 + cGFPM0VpNU9sRkNQMldlWXBjbjd5S2MKqYTzGc4Cb7WDn/M01Eti/EioAWvqjJMO + DJMOlD4AhXLg4ZqngxfYJVmZLr7JKbwbNg43Q8y1vhB1DaFCuTqTqw== + -----END AGE ENCRYPTED FILE----- + recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + encrypted_regex: ^password$ + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:URGkaw8aWqkLMX9g49eTqpi0J86kUwZmMtIfDrQmeCiPS7SmxJDrMy9Z8co/S1Xiq2ROPAzUSoHBNQ9x/7RvO7Gpy4d3gerGVKYIIxNHfJN/njMjOoau97PGjHi76hUlrIaw/YH+29Nx81VBe1i9N84lvlE17oZhLb1AEwx1VpI=,iv:OexZ7Wueec6bEtzqED2OWCu10byog/61evINCOOKtJk=,tag:0FYbzaNXL3ApUQgCtPCxiw==,type:str] + mac_only_encrypted: true + version: 3.13.3 diff --git a/talos/manifests/local-path.yaml b/talos/local-path.yaml similarity index 97% rename from talos/manifests/local-path.yaml rename to talos/local-path.yaml index 03e9b7d..7797c34 100644 --- a/talos/manifests/local-path.yaml +++ b/talos/local-path.yaml @@ -1,7 +1,7 @@ # local-path-provisioner。Talos には無いので、入れないと PVC が 1 つも bind せず、 # ストレージを持つアプリが全部 Pending で止まる。 # -# - データの置き場は `/var/mnt/local-path`([`talos/patches/volumes.yaml`](../patches/volumes.yaml) の +# - データの置き場は `/var/mnt/local-path`([`controlplane.yaml`](controlplane.yaml) の # `UserVolumeConfig` が切る別パーティション)。ディレクトリは `pvc-__` # - namespace は `local-path-storage`(PSA は privileged。ヘルパー Pod が hostPath を使う) # - StorageClass は既定の `local-path` 1 つだけ。PVC を守るのは Retain ではなく日次の restic diff --git a/talos/patches/apiserver.yaml b/talos/patches/apiserver.yaml deleted file mode 100644 index 14e27d9..0000000 --- a/talos/patches/apiserver.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# API サーバの公開名と、外から入る CI(cilium-drift.yml の OIDC トークン)と人(Entra)の認証。 -# 権限の中身は bootstrap/apiserver/rbac.yaml。 ---- -apiVersion: v1alpha1 -kind: KubeAPIServerConfig -# 外から ks.doany.io を証明書検証つきで使うため(公開名は自動では入らない) -certExtraSANs: - - ks.doany.io ---- -apiVersion: v1alpha1 -kind: KubeAuthenticationConfig -# `anonymous` を消さないこと。与えると既定を置き換えるので、書き忘れると -# /livez /readyz /healthz を匿名で叩けずコントロールプレーンが上がらない。 -configuration: - anonymous: - enabled: true - conditions: - - path: /livez - - path: /readyz - - path: /healthz - jwt: - - issuer: - url: https://token.actions.githubusercontent.com - # 既定の audience(所有者の URL)は他のワークフローと共有されるので使わない - audiences: - - https://ks.doany.io - claimMappings: - # `sub` は形式が変わると RBAC が黙って効かなくなるので、要る情報から組み立てる - username: - expression: '"gha:" + claims.repository + ":" + claims.ref' - # issuer は世界中の GitHub Actions が使う公開の IdP。効いているのはここで、緩めると即座に穴になる。 - claimValidationRules: - - expression: 'claims.repository == "danything/gitops"' - message: "danything/gitops 以外は受け付けない" - - expression: 'claims.ref == "refs/heads/main"' - message: "main 以外のブランチからは受け付けない" - - expression: >- - claims.workflow_ref.startsWith("danything/gitops/.github/workflows/cilium-drift.yml@") - message: "cilium-drift.yml 以外のワークフローからは受け付けない" - # Entra ID。Headlamp が利用者本人の id_token を転送してくる。 - - issuer: - url: https://login.microsoftonline.com/22ac977d-b140-4dc8-8150-90d645d3475b/v2.0 - # oauth2-proxy と同じアプリ登録なので、この組で出た id_token はどれもクラスタの資格情報になる - audiences: - - b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e - claimMappings: - username: - expression: '"entra:" + claims.preferred_username' - # ロールが無い利用者には roles クレーム自体が無いので has() で守る - groups: - expression: '(has(claims.roles) && "admin" in claims.roles) ? ["entra:admin"] : ["entra:viewer"]' diff --git a/talos/patches/cluster.yaml b/talos/patches/cluster.yaml deleted file mode 100644 index b5112de..0000000 --- a/talos/patches/cluster.yaml +++ /dev/null @@ -1,93 +0,0 @@ -# クラスタ共通。型付きドキュメントと v1alpha1 で書く場所が決まっている(../docs/talos.md「machine config の作法」)。 -machine: - sysctls: - # 無いと IPv6 の既定経路が消える(Kubernetes が forwarding=1 にすると accept_ra=1 は RA を無視する) - net.ipv6.conf.bond0.accept_ra: "2" - # IID を MAC 由来にしない。2(stable-privacy)は stable_secret が無いと書き込みが失敗し続ける - net.ipv6.conf.bond0.addr_gen_mode: "3" - # SLAAC のアドレスを付けない(あると cloudflare-ddns が静的な ::2 でなくそちらを AAAA に書く)。 - # RA の既定経路はそのまま受ける。 - net.ipv6.conf.bond0.autoconf: "0" - # Forgejo の Runner(dind)のファイル監視。既定の 128 では EMFILE で落ちる - fs.inotify.max_user_instances: "8192" - fs.inotify.max_user_watches: "1048576" - # IPv6 は bond0 に一本化する(eno4 の RA に既定経路を奪われると非対称になる) - net.ipv6.conf.eno4.disable_ipv6: "1" - network: - # local-path の PV の nodeAffinity が main を指している - hostname: main ---- -# MAC を固定する。ルーターの IPv6 ホスト公開がこの MAC を指していて(docs/router.md)、 -# 指定しないと eno1 の MAC を引き継ぎ、NIC や箱を替えた日に外からの IPv6 が全部止まる。 -apiVersion: v1alpha1 -kind: BondConfig -name: bond0 -links: - - eno1 - - eno2 -bondMode: balance-alb -miimon: 100 -hardwareAddr: e6:2c:1b:1a:a0:39 -addresses: - - address: 10.0.0.2/24 - # プレフィックスは ISP 由来で変わりうる(公開は cloudflare-ddns が実際のアドレスで行う) - - address: 240f:6d:842b:1::2/64 -routes: - # IPv6 の既定経路は RA 由来なので書かない(上の accept_ra とセット) - - gateway: 10.0.0.1 ---- -# 10.10 側。DHCP は明示の設定が無い限り無効 -apiVersion: v1alpha1 -kind: LinkConfig -name: eno4 -addresses: - - address: 10.10.0.4/24 -routes: - - destination: 10.10.0.0/24 - gateway: 10.10.0.1 ---- -apiVersion: v1alpha1 -kind: KubeNetworkConfig -podSubnets: - - 10.42.0.0/16 - - fd42::/64 -serviceSubnets: - - 10.43.0.0/16 - # /108 以下が必須 - - fd43::/108 ---- -# gen config が入れる `auto: stable` と `hostname` は併記できず、パッチから `auto` を消せないので -# ドキュメントごと落として machine.network.hostname を効かせる。 -apiVersion: v1alpha1 -kind: HostnameConfig -$patch: delete ---- -# ノード自身の名前解決(クラスタ内は CoreDNS、LAN 向けは AdGuard で別) -apiVersion: v1alpha1 -kind: ResolverConfig -nameservers: - - address: 2606:4700:4700::1111 - - address: 2606:4700:4700::1001 - - address: 1.1.1.1 - - address: 1.0.0.1 ---- -# ノードから fj.doany.io のイメージを取るため自分に向ける(公開 DNS の A は WAN 側で、ノードからは届かない) -apiVersion: v1alpha1 -kind: StaticHostConfig -name: 10.0.0.2 -hostnames: - - fj.doany.io ---- -# 単一ノードなので control-plane の NoSchedule を付けない(toleration の無い Pod が全部 Pending になる)。 -# taints はマップなので `{}` では消えず、`$patch: replace` は validate が落ちる。 -# ドキュメントごと消して labels だけ書き戻すのが唯一の手。 -apiVersion: v1alpha1 -kind: KubeNodeConfig -$patch: delete ---- -apiVersion: v1alpha1 -kind: KubeNodeConfig -# 消したままだと ROLES が になる -labels: - node-role.kubernetes.io/control-plane: "" - node.kubernetes.io/exclude-from-external-load-balancers: "" diff --git a/talos/patches/cni.yaml b/talos/patches/cni.yaml deleted file mode 100644 index 1d6f165..0000000 --- a/talos/patches/cni.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Cilium のために Talos の既定の CNI(flannel)と kube-proxy を止める。CoreDNS は止めない。 -# v1alpha1 の cluster.network.cni は型付きドキュメントと衝突して書けない。 -# KubeFlannelCNIConfig は在ること自体が有効化なので丸ごと消す。 -apiVersion: v1alpha1 -kind: KubeFlannelCNIConfig -$patch: delete ---- -apiVersion: v1alpha1 -kind: KubeProxyConfig -enabled: false diff --git a/talos/patches/etcd-backup.yaml b/talos/patches/etcd-backup.yaml deleted file mode 100644 index ef0e498..0000000 --- a/talos/patches/etcd-backup.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# etcd スナップショットの日次取得(../../apps/k8up/etcd-snapshot.yaml)のため、Pod から Talos API を叩く。 -# 役割は os:etcd:backup だけ、namespace も k8up に絞る(os:admin を Pod に置かずに済む唯一の仕組み)。 -machine: - features: - kubernetesTalosAPIAccess: - enabled: true - allowedRoles: - - os:etcd:backup - allowedKubernetesNamespaces: - - k8up diff --git a/talos/patches/main.yaml b/talos/patches/main.yaml deleted file mode 100644 index 06600d3..0000000 --- a/talos/patches/main.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# 実機(main)だけの設定。カーネル引数と拡張は machine.install に書けないので schematic に入れている -# (talos/versions.yaml)。 ---- -apiVersion: v1alpha1 -kind: UnattendedInstallConfig -provisioning: - diskSelector: - # SATA の SSD。箱を替えたら当日 `talosctl get disks` を見てから確定させる - match: disk.dev_path == "/dev/sda" - wipe: false ---- -# PT3 のドライバ(../docs/decisions.md「PT3 チューナー」)。dvb-pt3 は earth_pt3 を blacklist しているので明示で読ませる。 -# 復調とチューナーも要る(Talos には request_module を満たす modprobe が無い)。並べた順には読まれないので、 -# 付け直しは ../../apps/pt3/rebind.yaml。 -machine: - kernel: - modules: - - name: tc90522 - - name: qm1d1c0042 - - name: mxl301rf - - name: earth-pt3 diff --git a/talos/patches/volumes.yaml b/talos/patches/volumes.yaml deleted file mode 100644 index 2364f6a..0000000 --- a/talos/patches/volumes.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# ディスクの割り方。既定だと EPHEMERAL(/var)が全部を取り、PVC の暴走で etcd ごと道連れになる。 -# ボリュームの設定は未確保のときにしか効かない(あとから書き足しても黙って無視され、直すには入れ直し)。 -# EPHEMERAL に maxSize が無いと grow が全部持っていく(siderolabs/talos#12713)。 -# 64GiB は実機の使用量(containerd 22G ほか)から。残りが local-path 用。 -apiVersion: v1alpha1 -kind: VolumeConfig -name: EPHEMERAL -provisioning: - diskSelector: - match: system_disk - maxSize: 64GiB - grow: true ---- -# PV データの置き場。/var/mnt/ 固定で、../manifests/local-path.yaml の paths がここを指す。 -apiVersion: v1alpha1 -kind: UserVolumeConfig -name: local-path -provisioning: - diskSelector: - match: system_disk - minSize: 50GiB - grow: true -filesystem: - type: xfs -# /var 系は既定で nosuid,nodev,noexec になり、PVC の中の実行ファイル(Forgejo のサーバ側フック)が動かない。 -# コンテナは PSA と securityContext で押さえている。 -mount: - secure: false diff --git a/talos/registries.yaml b/talos/registries.yaml deleted file mode 100644 index cf42cb7..0000000 --- a/talos/registries.yaml +++ /dev/null @@ -1,22 +0,0 @@ -machine: - registries: - config: - fj.doany.io: - auth: - username: ENC[AES256_GCM,data:2fi9Gw==,iv:IrUHdYAPJqH9jwgyhA1trkmJeDhrvFcKLEQYjUHTP4g=,tag:0bg6wsrUvusRzqSTXHvIxA==,type:str] - password: ENC[AES256_GCM,data:HXa6oBG33x7SpQGpajM9yeRkTloQx8AjP7RctZx/PCKVP0eetxM8Jw==,iv:Jk4D7BYeYxG0Hl9KOeUZ8zU05+8Pau+brTn7f4zCG50=,tag:TRvfm9Q8tIkD0xTQM3mqDw==,type:str] -sops: - age: - - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRFNibzZTMzM5ckRLdEJ1 - ZlYvbFJFbkU1ZkQ0cUZMem4vZnZHelRIUHlJCnpVL1pGUWE2QllpdWRhcW83NXJy - cXR6N1JiUytVdS9UanlKcUQxbllOVVEKLS0tIEFCZHcyTFEwSEpMQUxkV3lrcis5 - NGNBQzB3Wkh6Tm84SkNGdm1BQ3RQSmMK5BEZF/RzotWHQjGPFoyr2GPm3F54pckb - YBuMuLhhiExdR7hA9Zpw+qbStZ7UJcRDXD5Ors+UC4tOYtzUZplN0g== - -----END AGE ENCRYPTED FILE----- - recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r - lastmodified: "2026-10-18T04:19:23Z" - mac: ENC[AES256_GCM,data:UZrapLSRfHO5egiYzaBbGXGPWqTmCfu/0GKtJSL3Ype31RAB5RBTbnx7GwvDsjm1DxWzz6P8LsXnpjIpTHT0vDMYJeGZe7DlaL4svApngVi9OkZo+TfSaVD7eHm4A/aQlGE49RZHH+LgIaT1hpZ8xGVVIk1pSvKcwcS+N4KdMms=,iv:uba7L3QCUe0Gz4aqyultNlv+yXjV//OI9T8rZsbIHUY=,tag:l+xOIANn3vMkPZ2QPIVGBg==,type:str] - unencrypted_suffix: _unencrypted - version: 3.13.3 diff --git a/talos/render.sh b/talos/render.sh index 497eed6..45fc05b 100755 --- a/talos/render.sh +++ b/talos/render.sh @@ -26,11 +26,14 @@ if [ -z "$SECRETS" ]; then sops -d talos/secrets.yaml > "$SECRETS" fi -REGISTRIES=${REGISTRIES:-} -if [ -z "$REGISTRIES" ]; then - REGISTRIES=$WORK/registries.yaml - sops -d talos/registries.yaml > "$REGISTRIES" +# 手で書く部分。password の値だけ SOPS で暗号化してある(../.sops.yaml)。CI は ENC をダミーにしたものを渡す。 +# `sops -d … > file` は失敗すると set -e で止まる(パイプに繋がない。pipefail が無い)。 +CONTROLPLANE=${CONTROLPLANE:-} +if [ -z "$CONTROLPLANE" ]; then + CONTROLPLANE=$WORK/controlplane.yaml + sops -d talos/controlplane.yaml > "$CONTROLPLANE" fi +! grep -q 'ENC\[' "$CONTROLPLANE" || { echo "ERROR: controlplane.yaml に暗号文が残っている" >&2; exit 1; } # キーを名前で引く(位置で数えるとブロックが増えたとき別の値を掴む)。yq は版で構文が違うので awk。 ver() { # $1=トップレベルのキー $2=その下のキー @@ -80,7 +83,7 @@ chart() { # $1=name $2=namespace 残り=helm template に足す引数 # `--api-versions` が無いと helm template は GatewayClass を黙って描かず、公開経路が全部死ぬ。 chart cilium kube-system --api-versions gateway.networking.k8s.io/v1/GatewayClass -inline local-path < talos/manifests/local-path.yaml > "$WORK/inline-local-path.yaml" +inline local-path < talos/local-path.yaml > "$WORK/inline-local-path.yaml" # CI と人の RBAC。締め出されても直せるよう ArgoCD に載せない(repos.yaml の bootstrap は apiserver/ を外す)。 inline apiserver-rbac < bootstrap/apiserver/rbac.yaml > "$WORK/inline-apiserver-rbac.yaml" @@ -148,7 +151,7 @@ PATCH set -- --with-secrets "$SECRETS" \ --kubernetes-version "$K8S" \ --install-image "factory.talos.dev/installer/${SCHEMATIC}:${TALOS}" -for f in talos/patches/*.yaml "$REGISTRIES" "$WORK/gateway-api.yaml" "$WORK/external-crds.yaml" "$WORK"/inline-*.yaml; do +for f in "$CONTROLPLANE" "$WORK/gateway-api.yaml" "$WORK/external-crds.yaml" "$WORK"/inline-*.yaml; do set -- "$@" --config-patch "@$f" done @@ -156,7 +159,7 @@ rm -rf "$OUT" talosctl gen config doany https://10.0.0.2:6443 "$@" --output-dir "$OUT" >/dev/null echo "wrote $OUT/controlplane.yaml ($(wc -l < "$OUT/controlplane.yaml") 行)" -# 描いたものが入ったか。patches/ のキーは gen config が知らないキーで落とすので見ない(helm は黙って捨てる)。 +# 描いたものが入ったか。controlplane.yaml のキーは gen config が知らないキーで落とすので見ない(helm は黙って捨てる)。 # 生成物の manifest は 1 行のエスケープ文字列なので、引用符は \" で探す。 for n in "kube-apiserver:$K8S" "ghcr.io/siderolabs/kubelet:$K8S" \ 'name: cilium' cilium-operator KUBERNETES_SERVICE_PORT 'value: \"7445\"' 'cgroup-root: \"/sys/fs/cgroup\"' \ diff --git a/talos/secrets.yaml b/talos/secrets.yaml index 666d8d6..63b50bb 100644 --- a/talos/secrets.yaml +++ b/talos/secrets.yaml @@ -32,7 +32,7 @@ sops: KpSRt/DkpjwkpfeUTQO5+KOuut00Zv6itVBXLc/u6ViCa2isU46Y/w== -----END AGE ENCRYPTED FILE----- recipient: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r - lastmodified: "2026-09-08T07:41:12Z" - mac: ENC[AES256_GCM,data:Hcf/32Uuw4YXNNqMZg5+uZi9j6IdHgeebHS8Fw4T5iis4lUmP0upvUjRkNJaqydZ4McfmqW84qymUhum77IKG/QtO9DKFmYS01NaNf3m2kYncUKT9rl0TIxbF+S3SYEdWpf72zgvYLZ1rx5ELvvCZILgNJ3kaRtfXC7/f6XluZI=,iv:5dPu0R8D+tbQ/R1MfO+Gcz1XdFJZW8jzttIMuzai0Pc=,tag:a9ExMXMH64rAI18InNUDkA==,type:str] + lastmodified: "2026-10-20T03:48:13Z" + mac: ENC[AES256_GCM,data:Uq97tDwrh60YMHCbv+XjqzKBCLSMpZYUhHG70xg2oU1ZjlSujS47NZTY3IhT4eRucNTRSFFQlDho1QxVLTXv4Ib8mKdLQHps1xwKf9GQQ9nQFRCuySprSrMMStCOMx+bwweXy92T+6N/gpow+V+Vr/ym3Pdja3hpNIX64bA1iX8=,iv:V/Ug9DDaNSjV59a118tfgZSzyyf6YOdM/Anr23J9JLI=,tag:vOBaPcT6m2HVcNZE9p4wFQ==,type:str] unencrypted_suffix: _unencrypted version: 3.13.3