diff --git a/.github/workflows/cilium-drift.yml b/.github/workflows/cilium-drift.yml index ee50aea2..f815d3b4 100644 --- a/.github/workflows/cilium-drift.yml +++ b/.github/workflows/cilium-drift.yml @@ -1,13 +1,13 @@ # cilium の値が git とクラスタでズレていないかを見る(読むだけ。権限は cilium-config の get だけ)。 # cilium は inlineManifest なので、人が render.sh → apply-config → upgrade-k8s を流すまで届かない。 -# 当てるのは自動化しない(talos/README.md)。 +# 当てるのは自動化しない(bootstrap/talos.md)。 name: cilium drift on: push: branches: [main] paths: - - bootstrap/cilium/** + - bootstrap/cilium-values.yaml - .github/workflows/cilium-drift.yml # クラスタ側だけ手でいじったときのため schedule: @@ -30,7 +30,7 @@ jobs: id: v run: | set -euo pipefail - V=$(yq -r '.version' bootstrap/cilium/version.yaml) + V=$(sed -n '/^# version: /{s/^# version: *//p;q;}' bootstrap/cilium-values.yaml) echo "chart=$V" >> "$GITHUB_OUTPUT" echo "cilium chart $V" @@ -44,24 +44,24 @@ jobs: echo "::add-mask::${T}" echo "token=${T}" >> "$GITHUB_OUTPUT" - - name: values.yaml から ConfigMap を描いて live と比べる + - name: cilium-values.yaml から ConfigMap を描いて live と比べる env: T: ${{ steps.token.outputs.token }} run: | set -euo pipefail k() { kubectl --server=https://ks.doany.io:6443 \ - --certificate-authority=bootstrap/apiserver/ca.crt \ + --certificate-authority=bootstrap/apiserver-ca.crt \ --token="$T" "$@" } # --kube-version が無いと helm の既定版で描かれて偽の差分が出る KV=$(k version -o json | jq -r .serverVersion.gitVersion) echo "kube-version=$KV" - export HOWTO="talos/README.md「inlineManifests を直したとき」の render.sh → apply-config → upgrade-k8s を流すこと" + export HOWTO="bootstrap/talos.md「inlineManifests を直したとき」の render.sh → apply-config → upgrade-k8s を流すこと" helm repo add cilium https://helm.cilium.io >/dev/null helm template cilium cilium/cilium --version "${{ steps.v.outputs.chart }}" \ - -n kube-system -f bootstrap/cilium/values.yaml --kube-version "$KV" \ + -n kube-system -f bootstrap/cilium-values.yaml --kube-version "$KV" \ --show-only templates/cilium-configmap.yaml \ | yq -o=json '.data' > rendered.json diff --git a/.github/workflows/talos-boot.yml b/.github/workflows/talos-boot.yml index 9c818548..fd98ae02 100644 --- a/.github/workflows/talos-boot.yml +++ b/.github/workflows/talos-boot.yml @@ -1,4 +1,4 @@ -# talos/render.sh の machine config を runner の QEMU(KVM)で起動し、クラスタが立つところまで見る。 +# bootstrap/render.sh の machine config を runner の QEMU(KVM)で起動し、クラスタが立つところまで見る。 # 秘密はゼロ(使い捨てとダミー)。外に副作用を出さない: ArgoCD の extraObjects と通知先、 # 本物の ClusterIssuer(自己署名に差し替え)、infisical の SMTP を抜く。手順の元は docs/talos.md。 name: talos boot @@ -7,13 +7,9 @@ on: pull_request: # YAML のアンカーは読むが、マージキー `<<:` は読まない paths: &paths - - talos/** - - bootstrap/cilium/** - - bootstrap/cert-manager/** - - bootstrap/argocd/** - - bootstrap/infisical/** - - bootstrap/gateway/** - - bootstrap/apiserver/** + - bootstrap/** + - apps/argocd/httproute.yaml + - apps/gateway/** - .github/workflows/talos-boot.yml push: branches: [main] @@ -46,13 +42,13 @@ jobs: steps: - uses: actions/checkout@v7 - - name: versions.yaml から版と schematic を読む + - name: bootstrap/versions.yaml から版と schematic を読む id: v run: | set -euo pipefail - VER=$(yq -r '.talos.version' talos/versions.yaml) - SCH=$(yq -r '.talos.schematic' talos/versions.yaml) - K8S=$(yq -r '.kubernetes.version' talos/versions.yaml) + VER=$(yq -r '.talos.version' bootstrap/versions.yaml) + SCH=$(yq -r '.talos.schematic' bootstrap/versions.yaml) + K8S=$(yq -r '.kubernetes.version' bootstrap/versions.yaml) echo " talos=$VER schematic=$SCH k8s=$K8S" echo "version=$VER" >> "$GITHUB_OUTPUT" echo "schematic=$SCH" >> "$GITHUB_OUTPUT" @@ -102,32 +98,32 @@ jobs: run: | set -euo pipefail talosctl gen secrets -o /tmp/secrets.yaml - # controlplane.yaml は password(fj.doany.io)だけ暗号化してある。ダミーに置き換える + # talos.sops.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 だけダミー(通知先もそこにしか無い)。 + bootstrap/talos.sops.yaml > /tmp/controlplane.yaml + # *-values.yaml は本物、*-secret-values.sops.yaml だけダミー(通知先もそこにしか無い)。 # extraObjects は app-of-apps と repo-creds。CI で本物を同期させない echo 'extraObjects: []' > /tmp/argocd-secret-values.yaml # SMTP の env を抜く(リストなので丸ごと置き換える) yq '{"infisical": {"extraEnv": [.infisical.extraEnv[] | select(.name | test("^SMTP_") | not)]}, "postgresql": {"auth": {"password": "ci-dummy"}}, "redis": {"auth": {"password": "ci-dummy"}}}' \ - bootstrap/infisical/values.yaml > /tmp/infisical-secret-values.yaml + bootstrap/infisical-values.yaml > /tmp/infisical-secret-values.yaml # ENCRYPTION_KEY は 16 バイトの hex でないと infisical が起動しない yq -I4 "del(.sops) | del(.stringData.CLIENT_ID_GITHUB_LOGIN) | del(.stringData.CLIENT_SECRET_GITHUB_LOGIN) | .stringData.ENCRYPTION_KEY = \"$(openssl rand -hex 16)\" | .stringData.AUTH_SECRET = \"$(openssl rand -base64 32)\" | .stringData.TELEMETRY_ENABLED = \"false\"" \ - bootstrap/infisical/secrets.sops.yaml > /tmp/infisical-secret.yaml + 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 + bootstrap/cert-manager-cloudflare-secret.sops.yaml > /tmp/cloudflare-secret.yaml 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 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 + ./bootstrap/render.sh /tmp/out talosctl config endpoint 127.0.0.1 talosctl config node "$NODE" @@ -234,7 +230,7 @@ jobs: selfSigned: {} YAML until kubectl apply -f /tmp/issuer.yaml 2>/dev/null; do sleep 5; done - until kubectl apply -f bootstrap/gateway/gateway.yaml -f bootstrap/gateway/redirect-https.yaml -f bootstrap/argocd/httproute.yaml; do sleep 5; done + until kubectl apply -f apps/gateway/gateway.yaml -f apps/gateway/redirect-https.yaml -f apps/argocd/httproute.yaml; do sleep 5; done kubectl -n kube-system wait --for=condition=Ready certificate --all --timeout=3m kubectl -n kube-system wait --for=condition=Programmed gateway/doany --timeout=3m # http は https への 301、https は argocd-server まで @@ -261,17 +257,17 @@ jobs: echo " volume $v: $P" done - WANT=$(yq -r 'select(.kind == "BondConfig") | .hardwareAddr' talos/controlplane.yaml) + WANT=$(yq -r 'select(.kind == "BondConfig") | .hardwareAddr' bootstrap/talos.sops.yaml) GOT=$(talosctl get links bond0 -o json | jq -r .spec.hardwareAddr) - [ "$GOT" = "$WANT" ] || fail "bond0 の MAC が $GOT(controlplane.yaml は $WANT)" + [ "$GOT" = "$WANT" ] || fail "bond0 の MAC が $GOT(talos.sops.yaml は $WANT)" echo " bond0 hardwareAddr: $GOT" - # 以下は forwarding=1 のあとで見ないと意味が無い(controlplane.yaml の accept_ra) + # 以下は forwarding=1 のあとで見ないと意味が無い(talos.sops.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/controlplane.yaml) + STATIC=$(yq -r 'select(.kind == "BondConfig") | .addresses[].address | select(test(":"))' bootstrap/talos.sops.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 606a7ced..f4281d38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -talos/clusterconfig/ -# **平文のまま置かないこと。** talos/secrets.yaml と controlplane.yaml は SOPS 済みのものをコミットする -# (talos/README.md)。書きかけの平文はこの名前に逃がす。 -talos/secrets.plain.yaml +bootstrap/clusterconfig/ +# 2026-10 までの出力先(talos/ を bootstrap/ にまとめる前)。手元に残っていても入らないように +/talos/ +# **平文のまま置かないこと。** bootstrap/talos-secrets.sops.yaml と talos.sops.yaml は SOPS 済みのものをコミットする +# (bootstrap/talos.md)。書きかけの平文はこの名前に逃がす。 +bootstrap/talos-secrets.plain.yaml # 平文の kubeconfig / talosconfig も置かない。どちらも tools/t.ps1 が毎回その場で作る # (tools/README.md「クラスタの資格情報」)。talosctl kubeconfig と gen config がこの名前で書き出す kubeconfig diff --git a/.sops.yaml b/.sops.yaml index 9ca85f1e..15a40714 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -2,21 +2,22 @@ # (env.age と同じパスフレーズ)。復号は # tools/t.ps1 sh -c 'mkdir -p ~/.config/sops/age && age -d -o ~/.config/sops/age/keys.txt recovery/sops-age.key.age' # のあと `tools/t.ps1 sops -d`(tools/t.ps1 の中の ~ はリポジトリの .home/)。 +# 上から順に見て、最初に当たった規則を使う。 creation_rules: - # secret-values.sops.yaml は秘密しか入れないので丸ごと - - path_regex: ^bootstrap/[^/]+/secret-values\.sops\.yaml$ - age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r - # 残りはキーだけ(構造をレビューできるように)。bootstrap/ で暗号化するファイルは `.sops.yaml` で終える - # (ArgoCD の `bootstrap` はその名前で外す)。 - - path_regex: ^bootstrap/.*\.sops\.yaml$ - encrypted_regex: ^(ENCRYPTION_KEY|AUTH_SECRET|CLIENT_ID_GITHUB_LOGIN|CLIENT_SECRET_GITHUB_LOGIN|CF_DNS_API_TOKEN)$ + # -secret-values.sops.yaml は秘密しか入れないので丸ごと + - path_regex: ^bootstrap/[^/]+-secret-values\.sops\.yaml$ age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r # 手で書く machine config。秘密は password の値だけ(fj.doany.io)。平文の部分は sops を通さず直接直せるよう # mac_only_encrypted(MAC が暗号化した値だけを見る)。SOPS はドキュメントごとに sops: を付け、インデントを 4 にする - - path_regex: ^talos/controlplane\.yaml$ + - path_regex: ^bootstrap/talos\.sops\.yaml$ encrypted_regex: ^password$ mac_only_encrypted: true age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r # 全部が秘密なので丸ごと(CA 一式) - - path_regex: ^talos/secrets\.yaml$ + - path_regex: ^bootstrap/talos-secrets\.sops\.yaml$ + age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r + # 残りはキーだけ(構造をレビューできるように)。上に当たらなかった bootstrap/ の `*.sops.yaml` はここに落ちるので最後に置く。 + # bootstrap/ で暗号化するファイルは `.sops.yaml` で終える(どの規則もその名前にしか当たらない)。 + - 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 diff --git a/README.md b/README.md index 4893129a..6090d2d3 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ [`bootstrap/argocd/repos.yaml`](bootstrap/argocd/repos.yaml) の ApplicationSet が org 内のリポジトリを走査し、 各リポジトリの `deploy/argocd.yaml` を見つけて Argo CD の Application を生成する。 このリポジトリがどうデプロイされるか(同期対象パス・autoSync 等)もクラスタ側ではなく -[`deploy/argocd.yaml`](deploy/argocd.yaml) で決まる。**Argo CD が同期するのは `apps/` 以下と、`bootstrap/` の平文のマニフェスト** -(Application `bootstrap`。Helm の値・SOPS のファイル・`apiserver/` は machine config 側なので外してある)。 +[`deploy/argocd.yaml`](deploy/argocd.yaml) で決まる。**Argo CD が同期するのは `apps/` 以下だけ** +(`bootstrap/` は machine config 側。Argo CD が見るのは `bootstrap/argocd/repos.yaml` の 1 本だけ)。 `repos.yaml` は Argo CD の chart が作る `apps` Application が拾うので、**まっさらなクラスタでも CI を待たずに走りはじめる** (詳しくは [`bootstrap/README.md`](bootstrap/README.md))。 @@ -18,16 +18,15 @@ | | | | --- | --- | | `apps/` | Argo CD が再帰的に同期するアプリのマニフェスト。**`_` で始まるディレクトリは同期しない** (動かしていないものの置き場。下の「動かしていないもの」) | -| [`bootstrap/`](bootstrap/) | クラスタそのものを組む層(Argo CD 本体・Infisical・cert-manager・Gateway・auth)。Helm で入れるもの・RBAC・SOPS 済みのものは **machine config に載り**(`talos/render.sh`)、残りの平文は **Argo CD の Application `bootstrap` が当てる**(`argocd/repos.yaml`) | +| [`bootstrap/`](bootstrap/) | クラスタそのものを組む層。**ここにあるものは全部 machine config に載る**([render.sh](bootstrap/render.sh) が `helm template` して inlineManifest にする)。ノードの Talos の設定(**v1.14 以降の形**。手で書くのは [talos.sops.yaml](bootstrap/talos.sops.yaml) の 1 本。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が生成物を検証して VM で起動する)、Helm で入れるもの(Cilium・Argo CD 本体・Infisical・cert-manager。部品ごとに `-values.yaml` 1 本)、RBAC、SOPS 済みのもの | | [`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 以降の形**。手で書くのは [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`) | | `deploy/argocd.yaml` | このリポジトリ自身の Application 定義(他のリポジトリと同じ場所) | -| [`.sops.yaml`](.sops.yaml) | 平文の秘密を SOPS(age)で暗号化する規則(`bootstrap/` の 4 ファイルと `talos/` の 2 つ) | +| [`.sops.yaml`](.sops.yaml) | 平文の秘密を SOPS(age)で暗号化する規則(`bootstrap/` の `*.sops.yaml` の 6 つ) | ## 秘密の扱い @@ -39,12 +38,12 @@ **Infisical より下の層だけは Infisical から取れない**ので、`bootstrap/` の 4 ファイル(Infisical 自身の鍵、 Argo CD が git を読む GitHub App 鍵、cert-manager の Cloudflare トークン)は **SOPS + age** で該当キーだけ暗号化してある。 鍵は [`recovery/sops-age.key.age`](recovery/)(バックアップの `env.age` と同じパスフレーズ)。 -当てるのは machine config(`talos/render.sh` が復号して載せる)。 +当てるのは machine config(`bootstrap/render.sh` が復号して載せる)。 ```shell # 鍵はリポジトリの .home/(git に入らない)に置く。コマンドは tools/t.ps1 で動かす(tools/README.md) tools/t.ps1 sh -c 'mkdir -p ~/.config/sops/age && age -d -o ~/.config/sops/age/keys.txt recovery/sops-age.key.age' -tools/t.ps1 sops -d bootstrap/infisical/secrets.sops.yaml # 中を見る(画面に出すと平文になるので注意) +tools/t.ps1 sops -d bootstrap/infisical-secrets.sops.yaml # 中を見る(画面に出すと平文になるので注意) ``` ## ホスト名の付け方 @@ -64,12 +63,16 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble `doany.io` そのものはブログ。`*.s.doany.io` は SSO を通して LAN のホストへ中継する口で、 `dp.l.doany.io` のように途中に段が入るものは**宅内からしか引けない名前** -(Gateway のリスナーが段ごとに分かれている。[bootstrap/gateway/](bootstrap/gateway/))。 +(Gateway のリスナーが段ごとに分かれている。[apps/gateway/](apps/gateway/))。 ## アプリ | ディレクトリ | 内容 | | --- | --- | +| [`gateway/`](apps/gateway/) | 公開の入口の Gateway(ワイルドカードの証明書付き)と http → https。Cilium 本体は `bootstrap/` | +| [`auth/`](apps/auth/) | SSO(`a.doany.io`)。oauth2-proxy で、SSO を持たない UI(AdGuard Home・Hubble)と `*.s.doany.io` の前に立つ | +| [`cert-manager/`](apps/cert-manager/) | `ClusterIssuer`(Let's Encrypt、DNS-01)。cert-manager 本体と Cloudflare のトークンは `bootstrap/` | +| [`argocd/`](apps/argocd/) | Argo CD の公開経路(`ac.doany.io`)と OIDC の client secret。本体は `bootstrap/` | | [`adguardhome/`](apps/adguardhome/) | AdGuard Home (DNS フィルタ) | | [`cloudflare-ddns/`](apps/cloudflare-ddns/) | DDNS | | [`external-dns/`](apps/external-dns/) | ExternalDNS。**Cloudflare を通すアプリの DNS を、アプリの HTTPRoute の注釈から作る**(gitops には書かない)。直接つなぐアプリはワイルドカードで引ける | @@ -79,11 +82,11 @@ 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/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ | +| [`infisical/`](apps/infisical/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジと SMTP のパスワード | | [`cluster/`](apps/cluster/) | ノードまわりの小物。metrics-server(Talos に無い。`kubectl top` と各 UI の使用量表示)と、PT3 をドライバに付け直す DaemonSet | | [`smtp-relay/`](apps/smtp-relay/) | メールの中継。アプリの SMTP を受けて Microsoft 365 へ Graph で送る(Exchange Online の SMTP AUTH の Basic 認証の廃止に備えて)。**送る側の設定は [apps/smtp-relay/README.md](apps/smtp-relay/README.md)** | | [`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/` | +| [`headlamp/`](apps/headlamp/) | Kubernetes の Web UI(`h.doany.io`)。認証は Entra、権限は `bootstrap/apiserver-rbac.yaml` | | [`rybbit/`](apps/rybbit/) | Rybbit(`rt.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** | **動かしていないもの**は `apps/_/` に置いてある。`_` で始まるディレクトリは Argo CD が同期しない diff --git a/ROADMAP.md b/ROADMAP.md index 7cb0e6e8..0fd40295 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -28,7 +28,7 @@ bond0 の MAC は設定で固定してあるのでルーターは触らない。 - [ ] **Talos 1.15.0 の正式版が出たら乗り換える**(Renovate の PR) - [x] **上流がカーネルにモジュールを必要な時に読ませる仕組み(modprobe の helper)を入れたら**、pt3-rebind を外し、 - `talos/controlplane.yaml` の `kernel.modules` を `earth-pt3` だけにする。並び順を守る PR([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))は + `bootstrap/talos.sops.yaml` の `kernel.modules` を `earth-pt3` だけにする。並び順を守る PR([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))は 2026-10-23 に「並び順ではなく、カーネルが必要な時に読み込む形にすべき。こちらで別に実装する」(smira)として閉じられ、 その実装 [siderolabs/talos#14724](https://github.com/siderolabs/talos/pull/14724) が 2026-10-24 にマージされて v1.15.0-alpha.2 に入った。 2026-10-24 に v1.15.0-alpha.1 + この PR を自前で組んだ installer で実機を起こし、`earth-pt3` だけでチューナー 4 つが付くことは確かめてあった。 diff --git a/apps/adguardhome/deployment.yaml b/apps/adguardhome/deployment.yaml index c867d562..c6b099bc 100644 --- a/apps/adguardhome/deployment.yaml +++ b/apps/adguardhome/deployment.yaml @@ -5,7 +5,7 @@ metadata: namespace: adguardhome annotations: # AdGuard 自身の認証は切ってある(`users: []`)。守っているのは前段の oauth2-proxy - # (bootstrap/auth/adguard-proxy.yaml、Entra のアプリロール `admin`)。 + # (apps/auth/adguard-proxy.yaml、Entra のアプリロール `admin`)。 # Web の口はクラスタ内からは無認証になる。外からは Gateway 経由だけで、その手前に SSO がある。 # クエリログの保持(querylog.interval)は 90d だと querylog.json が 2 GB に育ちバックアップに乗るので 7d。 spec: diff --git a/apps/adguardhome/httproute.yaml b/apps/adguardhome/httproute.yaml index 33a3812e..ac7619fc 100644 --- a/apps/adguardhome/httproute.yaml +++ b/apps/adguardhome/httproute.yaml @@ -1,4 +1,4 @@ -# 公開経路。証明書は Gateway(bootstrap/gateway/)のリスナーに付いているので指定しない。 +# 公開経路。証明書は Gateway(apps/gateway/)のリスナーに付いているので指定しない。 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: @@ -31,7 +31,7 @@ spec: type: PathPrefix value: / backendRefs: - # 管理画面は oauth2-proxy(bootstrap/auth/adguard-proxy.yaml)の後ろ。auth 側に ReferenceGrant がある。 + # 管理画面は oauth2-proxy(apps/auth/adguard-proxy.yaml)の後ろ。auth 側に ReferenceGrant がある。 # `/dns-query` は上のルールで先に拾われる。ここを通すと DoH に認証がかかって # 名前解決が止まる。 - name: auth-adguard diff --git a/bootstrap/argocd/httproute.yaml b/apps/argocd/httproute.yaml similarity index 100% rename from bootstrap/argocd/httproute.yaml rename to apps/argocd/httproute.yaml diff --git a/bootstrap/argocd/oidc-secret.yaml b/apps/argocd/oidc-secret.yaml similarity index 89% rename from bootstrap/argocd/oidc-secret.yaml rename to apps/argocd/oidc-secret.yaml index f3b29b59..1a6a9bab 100644 --- a/bootstrap/argocd/oidc-secret.yaml +++ b/apps/argocd/oidc-secret.yaml @@ -1,4 +1,4 @@ -# ArgoCD の OIDC ログインの client secret(values.yaml の $argocd-oidc:client-secret)。ラベルで argocd-server が読む。 +# ArgoCD の OIDC ログインの client secret(bootstrap/argocd-values.yaml の $argocd-oidc:client-secret)。ラベルで argocd-server が読む。 # 同期は GitHub App の鍵で動くので、Infisical が落ちても復旧は止まらない。 apiVersion: secrets.infisical.com/v1alpha1 kind: InfisicalSecret diff --git a/bootstrap/auth/adguard-proxy.yaml b/apps/auth/adguard-proxy.yaml similarity index 100% rename from bootstrap/auth/adguard-proxy.yaml rename to apps/auth/adguard-proxy.yaml diff --git a/bootstrap/auth/deployment.yaml b/apps/auth/deployment.yaml similarity index 100% rename from bootstrap/auth/deployment.yaml rename to apps/auth/deployment.yaml diff --git a/bootstrap/auth/httproute-hubble.yaml b/apps/auth/httproute-hubble.yaml similarity index 100% rename from bootstrap/auth/httproute-hubble.yaml rename to apps/auth/httproute-hubble.yaml diff --git a/bootstrap/auth/httproute-sub.yaml b/apps/auth/httproute-sub.yaml similarity index 100% rename from bootstrap/auth/httproute-sub.yaml rename to apps/auth/httproute-sub.yaml diff --git a/bootstrap/auth/httproute.yaml b/apps/auth/httproute.yaml similarity index 100% rename from bootstrap/auth/httproute.yaml rename to apps/auth/httproute.yaml diff --git a/bootstrap/auth/hubble-proxy.yaml b/apps/auth/hubble-proxy.yaml similarity index 100% rename from bootstrap/auth/hubble-proxy.yaml rename to apps/auth/hubble-proxy.yaml diff --git a/bootstrap/auth/namespace.yaml b/apps/auth/namespace.yaml similarity index 100% rename from bootstrap/auth/namespace.yaml rename to apps/auth/namespace.yaml diff --git a/bootstrap/auth/referencegrant-adguard.yaml b/apps/auth/referencegrant-adguard.yaml similarity index 100% rename from bootstrap/auth/referencegrant-adguard.yaml rename to apps/auth/referencegrant-adguard.yaml diff --git a/bootstrap/auth/secrets.yaml b/apps/auth/secrets.yaml similarity index 100% rename from bootstrap/auth/secrets.yaml rename to apps/auth/secrets.yaml diff --git a/bootstrap/auth/service.yaml b/apps/auth/service.yaml similarity index 100% rename from bootstrap/auth/service.yaml rename to apps/auth/service.yaml diff --git a/bootstrap/auth/sub-backend.yaml b/apps/auth/sub-backend.yaml similarity index 100% rename from bootstrap/auth/sub-backend.yaml rename to apps/auth/sub-backend.yaml diff --git a/bootstrap/auth/sub-proxy.yaml b/apps/auth/sub-proxy.yaml similarity index 100% rename from bootstrap/auth/sub-proxy.yaml rename to apps/auth/sub-proxy.yaml diff --git a/bootstrap/cert-manager/clusterissuer.yaml b/apps/cert-manager/clusterissuer.yaml similarity index 79% rename from bootstrap/cert-manager/clusterissuer.yaml rename to apps/cert-manager/clusterissuer.yaml index fb54db86..6706a163 100644 --- a/bootstrap/cert-manager/clusterissuer.yaml +++ b/apps/cert-manager/clusterissuer.yaml @@ -1,4 +1,4 @@ -# ワイルドカードを取るので DNS-01。トークンは cert-manager の namespace の Secret(cloudflare-secret.sops.yaml)。 +# ワイルドカードを取るので DNS-01。トークンは cert-manager の namespace の Secret(bootstrap/cert-manager-cloudflare-secret.sops.yaml。machine config に載る)。 apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: diff --git a/apps/cloudflare-ddns/application.yaml b/apps/cloudflare-ddns/application.yaml index 415aff92..e2711a9e 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/controlplane.yaml、docs/router.md)。 + # bond0 は autoconf=0 で SLAAC を付けない(bootstrap/talos.sops.yaml、docs/router.md)。 # 外向きの probe が拾う一時アドレスではない。この provider は # インタフェースを読んで temporary / deprecated を飛ばす。 ip6Provider: local.iface.stable:bond0 diff --git a/apps/external-dns/application.yaml b/apps/external-dns/application.yaml index d9d1c2b7..6dc9abdb 100644 --- a/apps/external-dns/application.yaml +++ b/apps/external-dns/application.yaml @@ -11,7 +11,7 @@ # HTTPRoute の無い名前(ks・px・*.s)だけは pulumi/dns.ts に明示する # # だから「注釈がある = Cloudflare を通さない = 明示のレコードを持つ」。注釈の無い HTTPRoute には触れない -# (annotationFilter)。CNAME の宛先は Gateway の注釈(bootstrap/gateway/ の external-dns.kubernetes.io/target)。 +# (annotationFilter)。CNAME の宛先は Gateway の注釈(apps/gateway/ の external-dns.kubernetes.io/target)。 # 前の方針の名残で "true" が付いた HTTPRoute があっても拾わない(ワイルドカードで Cloudflare を通るので同じ結果になる)。 # # **ExternalDNS が触るのは自分で作ったレコードだけ。** 作ったレコードの隣に所有の印の TXT diff --git a/apps/forgejo/README.md b/apps/forgejo/README.md index f724cddd..9c3ac0af 100644 --- a/apps/forgejo/README.md +++ b/apps/forgejo/README.md @@ -18,7 +18,7 @@ 1. **Infisical に 2 つ入れる(同期より先に)** - `/forgejo/forgejo-db`: `postgres-password` ── 英数字だけのランダム(`openssl rand -hex 32` など)。 postgres は最初の起動でしかパスワードを設定しないので、**後から変えるなら DB 側も変える** - - `/forgejo/forgejo-oauth`: `key` = `b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e`(Main のクライアント ID。秘密ではなく bootstrap/auth にも平文で書いてある)、`secret` = `${prod.auth.auth-secrets.oidc-client-secret}`(値は写さず参照) + - `/forgejo/forgejo-oauth`: `key` = `b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e`(Main のクライアント ID。秘密ではなく apps/auth にも平文で書いてある)、`secret` = `${prod.auth.auth-secrets.oidc-client-secret}`(値は写さず参照) 2. Entra のアプリ登録 Main のリダイレクト URI に `https://fj.doany.io/user/oauth2/entra/callback`([pulumi/entra.ts](../../pulumi/entra.ts) にある) 3. main にマージ → ArgoCD が同期 4. **管理者を作る**(1 度だけ)。Entra のメールと同じにしておくと、Entra でログインしたときに自動で紐づいて管理者になる。 @@ -139,7 +139,7 @@ yui のもの(repo-config)は画面(設定 → 承認済みインテグレーシ ## ノードからイメージを取る -アプリ側に `imagePullSecrets` は要らない([../../talos/README.md](../../talos/README.md)「fj.doany.io の資格情報」)。 +アプリ側に `imagePullSecrets` は要らない([../../bootstrap/talos.md](../../bootstrap/talos.md)「fj.doany.io の資格情報」)。 ## マージはスカッシュ diff --git a/apps/forgejo/argocd-creds.yaml b/apps/forgejo/argocd-creds.yaml index ef80b0a9..abe48e75 100644 --- a/apps/forgejo/argocd-creds.yaml +++ b/apps/forgejo/argocd-creds.yaml @@ -6,7 +6,7 @@ # username トークンを作ったユーザー名 # password Forgejo のアクセストークン(read:repository と read:organization だけ) # -# GitHub App の資格情報(bootstrap/argocd/values.yaml の extraObjects)と違って Infisical から取ってよい。 +# GitHub App の資格情報(bootstrap/argocd-values.yaml の extraObjects)と違って Infisical から取ってよい。 # Forgejo 自体が ArgoCD と Infisical の上に載っているので、復旧の順番が逆転しない。 apiVersion: secrets.infisical.com/v1alpha1 kind: InfisicalSecret diff --git a/apps/forgejo/forgejo.yaml b/apps/forgejo/forgejo.yaml index 0e9176f5..d35f6c35 100644 --- a/apps/forgejo/forgejo.yaml +++ b/apps/forgejo/forgejo.yaml @@ -1,7 +1,7 @@ # Forgejo 本体(git のホスティング + Forgejo Actions)。fj.doany.io。 # # **chart(forgejo-helm)は使わない。** chart は app.ini の元と init のスクリプトを Secret で作るが、 -# このクラスタの ArgoCD は Secret を丸ごと管理対象から外している(bootstrap/argocd/values.yaml の +# このクラスタの ArgoCD は Secret を丸ごと管理対象から外している(bootstrap/argocd-values.yaml の # resource.exclusions)ので、それらが作られず Pod が起動しない。 # 設定は環境変数(FORGEJO__
__)で渡し、イメージの docker-setup.sh が app.ini に書く。 # diff --git a/apps/forgejo/httproute.yaml b/apps/forgejo/httproute.yaml index 2d5b1db0..802af033 100644 --- a/apps/forgejo/httproute.yaml +++ b/apps/forgejo/httproute.yaml @@ -1,4 +1,4 @@ -# 公開経路。証明書は Gateway(bootstrap/gateway/)のワイルドカードなので指定しない。 +# 公開経路。証明書は Gateway(apps/gateway/)のワイルドカードなので指定しない。 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: diff --git a/bootstrap/gateway/gateway.yaml b/apps/gateway/gateway.yaml similarity index 100% rename from bootstrap/gateway/gateway.yaml rename to apps/gateway/gateway.yaml diff --git a/bootstrap/gateway/redirect-https.yaml b/apps/gateway/redirect-https.yaml similarity index 100% rename from bootstrap/gateway/redirect-https.yaml rename to apps/gateway/redirect-https.yaml diff --git a/apps/headlamp/application.yaml b/apps/headlamp/application.yaml index 082f99bd..683c5944 100644 --- a/apps/headlamp/application.yaml +++ b/apps/headlamp/application.yaml @@ -5,7 +5,7 @@ # # 認証は Entra の OIDC。Headlamp は利用者の id_token を API サーバへ転送するだけで、 # 権限を持たない。誰が何をできるかは apiserver 側で決まる -# (talos/controlplane.yaml と bootstrap/apiserver/rbac.yaml)。アプリロール admin を +# (bootstrap/talos.sops.yaml と bootstrap/apiserver-rbac.yaml)。アプリロール admin を # 持つ人は cluster-admin、それ以外は読み取り専用。 # # コールバックは Headlamp が要求のホストから組み立てる(Envoy が x-forwarded-proto を diff --git a/apps/headlamp/headlamp-secrets.yaml b/apps/headlamp/headlamp-secrets.yaml index f3703f8d..a5661c15 100644 --- a/apps/headlamp/headlamp-secrets.yaml +++ b/apps/headlamp/headlamp-secrets.yaml @@ -2,7 +2,7 @@ # chart は externalSecret を envFrom で丸ごと読むので、キーは Headlamp の # 環境変数名そのままにする: OIDC_CLIENT_ID / OIDC_CLIENT_SECRET / OIDC_ISSUER_URL。 # -# 秘密なのは OIDC_CLIENT_SECRET だけ。残り 2 つは bootstrap/auth/ にも平文で +# 秘密なのは OIDC_CLIENT_SECRET だけ。残り 2 つは apps/auth/ にも平文で # 書いてあるが、chart が Secret 1 本から envFrom で読む作りなので同居させる。 # アプリ登録は oauth2-proxy と同じ (b0fa498f-...)。 # diff --git a/apps/infisical/httproute.yaml b/apps/infisical/httproute.yaml index 89595d84..a09c83e6 100644 --- a/apps/infisical/httproute.yaml +++ b/apps/infisical/httproute.yaml @@ -1,4 +1,4 @@ -# 公開経路。証明書は Gateway(bootstrap/gateway/)のリスナーに付いているので指定しない。 +# 公開経路。証明書は Gateway(apps/gateway/)のリスナーに付いているので指定しない。 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: diff --git a/bootstrap/infisical/smtp-secret.yaml b/apps/infisical/smtp-secret.yaml similarity index 100% rename from bootstrap/infisical/smtp-secret.yaml rename to apps/infisical/smtp-secret.yaml diff --git a/apps/k8up/README.md b/apps/k8up/README.md index 6c5df49d..51b1793d 100644 --- a/apps/k8up/README.md +++ b/apps/k8up/README.md @@ -14,7 +14,7 @@ | --- | --- | --- | | forgejo | postgres の `pg_dump` + リポジトリの PVC(ファイル) | [../forgejo/postgres.yaml](../forgejo/postgres.yaml) | | rybbit | postgres の `pg_dump` + ClickHouse の PVC(ファイル。パートは追記だけなので動いたまま取る) | [../rybbit/postgres.yaml](../rybbit/postgres.yaml) / [../rybbit/clickhouse.yaml](../rybbit/clickhouse.yaml) | -| infisical | postgres の `pg_dump` | `bootstrap/infisical/values.yaml` の `postgresql.primary.podAnnotations` | +| infisical | postgres の `pg_dump` | `bootstrap/infisical-values.yaml` の `postgresql.primary.podAnnotations` | | lgtm / xool / worklog / denpa / blog / todoroku | SQLite を `serialize()` した 1 ファイル | 各アプリのリポジトリの `deploy/`(denpa と yosegaki は chart) | | netbird | `store.db` / `idp.db` / `events.db` を tar 1 本に(bun のサイドカー) | [../netbird/deployment.yaml](../netbird/deployment.yaml) | | matrix | Tuwunel 自身のバックアップと `media/` を tar 1 本に(DB は動いたまま写さない) | [../matrix/tuwunel.yaml](../matrix/tuwunel.yaml) | diff --git a/apps/k8up/etcd-snapshot.yaml b/apps/k8up/etcd-snapshot.yaml index 19cef223..3647f542 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/controlplane.yaml)。 +# Talos API への権限は `os:etcd:backup` だけ(bootstrap/talos.sops.yaml)。 # 下の ServiceAccount を見て Talos が同名の Secret に talosconfig を書く。 # # **通知は何も足さなくてよい。** notify は過去 8 日に出てきた (host, path) を @@ -46,7 +46,7 @@ spec: runAsNonRoot: true initContainers: # talosctl だけのイメージ(シェルが無い)なので、取るだけを init に分ける。 - # 版は talos/versions.yaml と揃える(クライアントとノードの版は離れすぎないこと)。 + # 版は bootstrap/versions.yaml と揃える(クライアントとノードの版は離れすぎないこと)。 - name: snapshot image: ghcr.io/siderolabs/talosctl:v1.15.0-alpha.2 # ノードは 1 台。Pod からノードの 50000 に直接届く(Talos はファイアウォールを張っていない) diff --git a/apps/matrix/README.md b/apps/matrix/README.md index ec44fcf4..9bd0c0be 100644 --- a/apps/matrix/README.md +++ b/apps/matrix/README.md @@ -59,7 +59,7 @@ Entra だけ(Tuwunel の `[[global.identity_provider]]`)。パスワードのロ | `server` | k8up の通知 | Infisical `/k8up/k8up-global` の `mattermostWebhook`(名前は据え置き) | | 〃 | Infisical(秘密の変更) | Infisical のプロジェクトの webhook(Project Settings → Webhooks。環境 prod、パス `/`) | | 〃 | Cloudflare(証明書・DDoS・オリジン不達など) | Cloudflare の通知の webhook の送り先([../../pulumi/notifications.ts](../../pulumi/notifications.ts)) | -| `argocd` | Argo CD | `bootstrap/argocd/secret-values.sops.yaml`(SOPS)の `service.webhook.mattermost` | +| `argocd` | Argo CD | `bootstrap/argocd-secret-values.sops.yaml`(SOPS)の `service.webhook.mattermost` | | `ashi` | ashi | Infisical `/ashi/ashi-secrets` の `notify-webhook-url` | | `todoroku` | todoroku | Infisical `/todoroku/todoroku-secrets` の `mattermost-webhook-url` | | `worklog` | worklog | Infisical `/worklog/worklog-secrets` の `mattermost-webhook-url` | diff --git a/apps/matrix/hookshot.yaml b/apps/matrix/hookshot.yaml index 2f927ffd..77be98ed 100644 --- a/apps/matrix/hookshot.yaml +++ b/apps/matrix/hookshot.yaml @@ -88,7 +88,7 @@ data: result = parts.length ? { version: "v2", plain: parts.join("\n\n"), msgtype: "m.notice" } : { version: "v2", empty: true }; - # 部屋 argocd: Argo CD の通知(bootstrap/argocd/values.yaml・secret-values.sops.yaml) + # 部屋 argocd: Argo CD の通知(bootstrap/argocd-values.yaml・argocd-secret-values.sops.yaml) - connectionType: uk.half-shot.matrix-hookshot.generic.hook stateKey: ${HOOK_ARGOCD} roomId: "!BeZ3yus1s1E22DPYFauQjZ1AMGT2VkusuLzLk2r1cJk" diff --git a/apps/netbird/routing-peer.yaml b/apps/netbird/routing-peer.yaml index 3bf06e2b..ff7aae73 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/controlplane.yaml の StaticHostConfig)ので、 + # ホストからは公開 DNS の A(WAN 側)に届かない(bootstrap/talos.sops.yaml の StaticHostConfig)ので、 # hostNetwork だと nb.doany.io:443 に繋げず `Management: Disconnected` で止まる。 containers: - name: netbird @@ -106,7 +106,7 @@ spec: - name: data mountPath: /var/lib/netbird # カーネルの wireguard が使えないときのユーザ空間フォールバック用。 - # Talos でもカーネル組み込みは確認済み(talos/README.md)なので通常は使わない + # Talos でもカーネル組み込みは確認済み(bootstrap/talos.md)なので通常は使わない - name: tun mountPath: /dev/net/tun volumes: diff --git a/apps/netbird/server-config.yaml b/apps/netbird/server-config.yaml index 19e0d5b6..e571fc45 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/controlplane.yaml の podSubnets)。bootstrap/auth の oauth2-proxy と同じ値。 + # Pod CIDR(bootstrap/talos.sops.yaml の podSubnets)。apps/auth の oauth2-proxy と同じ値。 trustedHTTPProxies: - "10.42.0.0/16" # NetBird 自身のピア網(既定 100.64.0.0/10) diff --git a/apps/rybbit/httproute.yaml b/apps/rybbit/httproute.yaml index c57b9f71..d27312ef 100644 --- a/apps/rybbit/httproute.yaml +++ b/apps/rybbit/httproute.yaml @@ -1,4 +1,4 @@ -# 公開経路。証明書は Gateway(bootstrap/gateway/)のワイルドカードなので指定しない。 +# 公開経路。証明書は Gateway(apps/gateway/)のワイルドカードなので指定しない。 # 振り分けは /api が backend、ほかは client。上流の Caddyfile はほかに /.well-known/oauth-* と # /.well-known/openid-configuration(MCP クライアント向けの OAuth の案内。Rybbit が OAuth サーバになる側)も # backend に流すが、MCP は使わないので置かない。Gateway API の PathPrefix は / 区切りの要素単位で、 diff --git a/apps/smtp-relay/README.md b/apps/smtp-relay/README.md index bf07b7a2..dd2b2502 100644 --- a/apps/smtp-relay/README.md +++ b/apps/smtp-relay/README.md @@ -31,7 +31,7 @@ SMTP AUTH をやめて **Graph の `sendMail`** で送る。Infisical のよう | パスワード | Infisical の `/infisical/infisical-smtp` の `SMTP_PASSWORD`(Infisical と todoroku が使う)、`/yosegaki/yosegaki-secrets` の `smtp-password` | | 差出人 | `info@doany.io`(ほかのアドレスは Exchange が拒む) | -使っているのは Infisical(`bootstrap/infisical/values.yaml`)、yosegaki(blog のリポジトリ)、todoroku(Forgejo の doa/todoroku)。 +使っているのは Infisical(`bootstrap/infisical-values.yaml`)、yosegaki(blog のリポジトリ)、todoroku(Forgejo の doa/todoroku)。 **送る側を足すときは network-policy.yaml にその namespace を足す。** ## Entra のアプリと Exchange の絞り込み(1 回だけ。済み) diff --git a/bootstrap/README.md b/bootstrap/README.md index f6d01f2a..632190dc 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -1,35 +1,107 @@ # bootstrap -クラスタそのものを組む層。2 つの経路で当たる([docs/decisions.md](../docs/decisions.md)「層の分け方」): +クラスタそのものを組む層。**ここにあるものは全部 machine config に載る**([render.sh](render.sh) が描く)。 +ArgoCD はここを見ない(例外は [argocd/repos.yaml](argocd/repos.yaml) だけ。下の「ArgoCD が当てるもの」)。 +層の分け方は [docs/decisions.md](../docs/decisions.md)「層の分け方」。 | | 何を | いつ | | --- | --- | --- | -| **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 付き) | +| **machine config**(このディレクトリ) | Talos の設定、Helm で入れるもの(cilium・cert-manager・argocd・infisical)、`apiserver-rbac.yaml`、`local-path.yaml`、SOPS 済みの Secret 2 つ | `render.sh` → `apply-config` → `upgrade-k8s`([talos.md](talos.md)「上げ方 / 当て直し方」) | +| **ArgoCD**(`apps/`) | 平文のマニフェスト(`ClusterIssuer`・Gateway・auth・`InfisicalSecret` など)。2026-10 まではここにあって、Application `bootstrap` が当てていた | main へのマージ(prune / selfHeal 付き) | -`InfisicalSecret` の CRD だけは `apps/infisical/operator.yaml` が入れるので、まっさらなクラスタでは `bootstrap` の同期が -最初の何回か落ちて retry で戻る。 +## ファイル -**SOPS(age)で暗号化したファイルは ArgoCD が当てない** ── 復号鍵をクラスタにも CI にも渡さないため。 -**名前を `*.sops.yaml` で終える**と `bootstrap` の `exclude` が外す(`.sops.yaml` の規則も -その名前にしか一致しないので、別の名前では暗号化できない)。 -`infisical/secrets.sops.yaml`、`cert-manager/cloudflare-secret.sops.yaml`、`{argocd,infisical}/secret-values.sops.yaml`。 +部品ごとに 1 ファイルを基本にして、ディレクトリを切らない。部品の名前を頭に付ける。 + +| ファイル | 中身 | +| --- | --- | +| [render.sh](render.sh) | machine config を組む。下のファイルを全部読む | +| [talos.md](talos.md) | Talos の組み立てと当て直し方 | +| [talos.sops.yaml](talos.sops.yaml) | 手で書く machine config(`password` の値だけ SOPS) | +| [talos-secrets.sops.yaml](talos-secrets.sops.yaml) | クラスタの CA 一式(丸ごと SOPS) | +| [versions.yaml](versions.yaml) | Talos・Kubernetes・Gateway API の版と schematic | +| `-values.yaml` | chart の値と版(cilium・cert-manager・argocd・infisical。下の「Helm で入れるもの」) | +| `-secret-values.sops.yaml` | chart の値のうち秘密だけ(argocd・infisical。丸ごと SOPS) | +| [infisical-secrets.sops.yaml](infisical-secrets.sops.yaml) | infisical の起動に要る Secret(`ENCRYPTION_KEY` など) | +| [cert-manager-cloudflare-secret.sops.yaml](cert-manager-cloudflare-secret.sops.yaml) | DNS-01 の Cloudflare のトークン | +| [apiserver-rbac.yaml](apiserver-rbac.yaml) / [apiserver-ca.crt](apiserver-ca.crt) | API サーバの入口の RBAC と CA(下の「API サーバの入口」) | +| [local-path.yaml](local-path.yaml) | local-path-provisioner | +| [argocd/repos.yaml](argocd/repos.yaml) | ArgoCD が当てる唯一のファイル(下の「ArgoCD が当てるもの」) | + +**SOPS(age)で暗号化したファイルは名前を `*.sops.yaml` で終える**(`.sops.yaml` の規則もその名前にしか一致しないので、 +別の名前では暗号化できない)。復号鍵はクラスタにも CI にも渡さない。 鍵は [`recovery/sops-age.key.age`](../recovery/)。**中を編集するときも `sops edit` を通すこと。** MAC は暗号化して -いない値も含めて計算されるので、**平文で 1 行足すだけでも `sops -d` が MAC 不一致で落ちる。** +いない値も含めて計算されるので、**平文で 1 行足すだけでも `sops -d` が MAC 不一致で落ちる** +(例外は `talos.sops.yaml` で、`mac_only_encrypted` なので平文の部分は直接直せる)。 + +## ArgoCD が当てるもの + +**ここで ArgoCD が当てるのは [argocd/repos.yaml](argocd/repos.yaml) だけ。** machine config に載っている +`apps` の Application(`argocd-values.yaml` の `extraObjects`)が拾い、そこから ApplicationSet が `apps/` を当てる。 +場所が machine config に焼いてあるので、このファイルだけディレクトリに入っている(動かすと machine config を当て直すことになる)。 + +ArgoCD の平文のマニフェストは `apps/` にある: +[apps/gateway/](../apps/gateway/)(Gateway と http → https)、[apps/auth/](../apps/auth/)(SSO の oauth2-proxy)、 +[apps/cert-manager/](../apps/cert-manager/)(`ClusterIssuer`)、[apps/argocd/](../apps/argocd/)(HTTPRoute と OIDC の `InfisicalSecret`)、 +[apps/infisical/smtp-secret.yaml](../apps/infisical/smtp-secret.yaml)。 +`ClusterIssuer` や Gateway は本体(cert-manager・cilium)が machine config に居ても、書き換えるのは平文のマニフェストなので +ArgoCD に任せる。 ## Helm で入れるもの どれも `render.sh` が `helm template` して machine config に載せる。どれも同じ形で、 -`/version.yaml`(chart・repo・版。Renovate が上げる)と `/values.yaml`(値)。 -argocd と infisical は、値のうち秘密だけを `/secret-values.sops.yaml`(SOPS、丸ごと暗号化)に分けて上に重ねる。 +**`-values.yaml` の 1 本**に chart・repo・版と値をまとめる。先頭の 2 行が chart と版: + +```yaml +# 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)。 +argocd と infisical は、値のうち秘密だけを `-secret-values.sops.yaml`(SOPS、丸ごと暗号化)に分けて上に重ねる。 argocd・infisical の版は自動マージしない(`renovate.json` の `bootstrap charts`)。 +Namespace は chart が描かないので、`render.sh` が kube-system 以外のものに付ける。 ## git と live がずれていないか **cilium は [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が週次で見ている**(値が `cilium-config` という ConfigMap に出ているので、読むだけの権限で比べられる)。**残りは見ていない。** 当て忘れは `upgrade-k8s` の差分に出るので、 直したら流す(流し忘れたことは誰も教えてくれない)。描くたびに変わる値は潰してあるので、 -`apply-config --dry-run` に出る差分は全部本物([../talos/README.md](../talos/README.md)「inlineManifests を直したとき」)。 +`apply-config --dry-run` に出る差分は全部本物([talos.md](talos.md)「inlineManifests を直したとき」)。 + +## cilium + +**クラスタの土台。** CNI・kube-proxy の代替・Gateway API を全部これが担う。 +決定の経緯は [../docs/decisions.md](../docs/decisions.md)「ルーティングの選定」。 + +[cilium-values.yaml](cilium-values.yaml) を直したら **`render.sh` → `apply-config` → `upgrade-k8s`** +([talos.md](talos.md)「上げ方 / 当て直し方」)。`helm upgrade` は使わない。値はここが正本で、machine config は +`render.sh` がここから描く派生物(二度書かない。decisions.md「machine config と Cilium の chart をどう連動させるか」)。 +**どの設定が何のために要るかは [cilium-values.yaml](cilium-values.yaml) のコメントにある**(ここには写さない)。 + +### 描き直すたびに Hubble の証明書が入れ替わる + +チャートの `hubble/tls-helm/*` は毎回 `genCA` / `genSignedCert` で作り直す +(`cilium.io/helm-template-non-idempotent: "true"`)。**値を 1 行も変えなくても、`hubble-ca-secret` / +`hubble-server-certs` / `hubble-relay-client-certs` の 3 つは `upgrade-k8s` の差分に出る。** +実害は無い(エージェントも Relay もファイルの入れ替わりに追従する。Pod の再起動は要らない)。 +`cilium-dbg status` の `Hubble: Ok` と `hubble-relay` の Ready で確かめる。 + +### ズレは CI が見ている + +**当て忘れとクラスタ側の直接編集は [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が見つける。** +`cilium-values.yaml` から ConfigMap を描いて live と突き合わせるだけで、**読むだけ**(権限は `cilium-config` の `get` 1 つ)。 +`cilium-values.yaml` を触ったときと毎週月曜に走る。当てるのは自動化しない(Talos API の os:admin を CI に置かない)。 + +### 当てたあとに確認すること + +```shell +# ホストのポートが張られているか(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 doany +``` ## Secret @@ -37,8 +109,8 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。** **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 の中身が全部読めなくなる +- `infisical-*` … Infisical 本体(chart + Postgres/Redis)。公開経路は [`apps/infisical/httproute.yaml`](../apps/infisical/httproute.yaml)。 + `infisical-secrets.sops.yaml` の `ENCRYPTION_KEY` が DB の暗号鍵で、これを失うと Infisical の中身が全部読めなくなる (クラスタで一番失ってはいけない値)。Postgres の PVC は既定の `local-path` で、`pg_dump` を k8up が日次で取っている。 - **operator と push-bridge はここには無い**(`apps/infisical/operator.yaml`、`apps/infisical/push-bridge.yaml`)。 ArgoCD 自身は operator に依存しないので、bootstrap に置く理由がない。 @@ -50,9 +122,9 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。** ### API サーバの入口(GitHub Actions と人) **GitHub Actions は読むだけ。** cilium-drift.yml の OIDC トークンを API サーバが直接受け -([../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。作り直したら替える)。 +([talos.sops.yaml](talos.sops.yaml) の `KubeAuthenticationConfig`)、 +権限は `cilium-config` の `get` だけ([apiserver-rbac.yaml](apiserver-rbac.yaml))。 +CA は [apiserver-ca.crt](apiserver-ca.crt)(`talos-secrets.sops.yaml` のクラスタ CA。作り直したら替える)。 kubeconfig も age 鍵も GitHub に置いていない。 **同じ認証設定と RBAC が人の入口でもある。** Entra を 2 つめの issuer として足し、 @@ -60,14 +132,9 @@ Headlamp が利用者本人の id_token を API サーバへ転送する。ア `entra:admin` で cluster-admin、それ以外は `entra:viewer` で読み取り専用(組み込みの `view` + CRD とノードぶんの `readonly-extra`)。**振り分けは CEL で、割り当ては Entra 側で完結する。** ここに置いたのは、ArgoCD も CI も要らずに効く層だから ── 締め出されたときに直せる。 -認証設定([../talos/controlplane.yaml](../talos/controlplane.yaml))も [apiserver/rbac.yaml](apiserver/rbac.yaml) も +認証設定([talos.sops.yaml](talos.sops.yaml))も [apiserver-rbac.yaml](apiserver-rbac.yaml) も machine config に載っているので、変えたら `render.sh` → `apply-config`(RBAC は inlineManifest なので `upgrade-k8s` まで)。 -**ArgoCD には当てさせない**(`bootstrap` の exclude に `apiserver/`。締め出されたときに直せる層に置く)。 - -### 公開経路(HTTPRoute)もここにある - -`argocd` と `auth` の HTTPRoute と `gateway/redirect-https.yaml` はここ -([docs/decisions.md](../docs/decisions.md)「公開経路(HTTPRoute)をどこに置くか」)。 +**ArgoCD には当てさせない**(締め出されたときに直せる層に置く)。 ### Infisical の中の構成 @@ -77,9 +144,25 @@ machine config に載っているので、変えたら `render.sh` → `apply-co | フォルダ | `//`(例: `/denpa/denpa-oidc`、`/worklog/ghcr-pull`) | | シークレット名 | 作られる `Secret` の **キー名そのまま**(`client-id`、`.dockerconfigjson` など) | | Machine Identity | `infisical-operator`(Kubernetes auth、許可 SA は `infisical/infisical-auth`、project の viewer) | -| 管理者 | 個人アカウント。資格情報はリポジトリに置かない。復旧手順は `infisical/README.md` | +| 管理者 | 個人アカウント。資格情報はリポジトリに置かない。復旧手順は下の「Infisical の管理者アカウント」 | | 共有値 | `/shared/entra`(Entra 共用アプリの client-id / client-secret / issuer / role / tenant-id)と `/shared/smtp`(info@doany.io)。各アプリのフォルダは `${prod.shared.entra.client-secret}` のような参照で、ローテーションは shared 側の 1 回で済む(operator が展開する) | -| SMTP | info@doany.io から、クラスタの中の中継 [apps/smtp-relay](../apps/smtp-relay/README.md) 経由(Graph の sendMail)。パスワード(中継のアプリのクライアントシークレット)だけ Infisical の `/infisical/infisical-smtp` に置き、`infisical/smtp-secret.yaml` が Secret にする | +| SMTP | info@doany.io から、クラスタの中の中継 [apps/smtp-relay](../apps/smtp-relay/README.md) 経由(Graph の sendMail)。パスワード(中継のアプリのクライアントシークレット)だけ Infisical の `/infisical/infisical-smtp` に置き、[apps/infisical/smtp-secret.yaml](../apps/infisical/smtp-secret.yaml) が Secret にする | + +### Infisical の管理者アカウント + +初期化(`POST /api/v1/admin/bootstrap`)で作った `admin@doany.io` は現存しない +(2026-09-02 にログインを試して "Failed to find user")。いまの管理者は個人アカウントで、 +資格情報はこのリポジトリに置かない。 + +ロックアウトしたときの復旧は、SMTP(info@doany.io 経由)が生きていればログイン画面の +パスワードリセット。それも届かない場合は `infisical` ns の PostgreSQL に入って +`users` テーブルから管理者を特定し、Infisical のドキュメントにある手順でリセットする。 + +初期化で一緒に作られる "Instance Admin Identity"(root 相当の Machine Identity)は +セットアップ後に削除してある。必要なら UI の Organization Settings > Identities で作り直す。 + +DB は手元の restic から直接戻す(k8up の資格情報が Infisical から来るので)。戻したら operator の +Kubernetes auth の CA も差し替える。手順は [../recovery/README.md](../recovery/README.md) の 6・7。 ### `InfisicalSecret` には sync-options を 2 つ付けること @@ -96,7 +179,7 @@ ArgoCD の tracking-id までコピーされると、ArgoCD がその `Secret` (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`)にも付けてある。 +このリポジトリの `InfisicalSecret`(`apps/argocd/oidc-secret.yaml`・`apps/auth/secrets.yaml`・`apps/infisical/smtp-secret.yaml` など)にも付けてある。 ### アプリ側の書き方 diff --git a/bootstrap/apiserver/ca.crt b/bootstrap/apiserver-ca.crt similarity index 100% rename from bootstrap/apiserver/ca.crt rename to bootstrap/apiserver-ca.crt diff --git a/bootstrap/apiserver/rbac.yaml b/bootstrap/apiserver-rbac.yaml similarity index 90% rename from bootstrap/apiserver/rbac.yaml rename to bootstrap/apiserver-rbac.yaml index b24281fe..90b64508 100644 --- a/bootstrap/apiserver/rbac.yaml +++ b/bootstrap/apiserver-rbac.yaml @@ -1,9 +1,9 @@ # GitHub Actions がこのクラスタで何をしてよいかの定義。使うのは cilium-drift.yml だけで、読むだけ。 # # 2026-10-17 まで bootstrap-apply.yml が bootstrap/ を当てるための書き込み権限もここにあった。 -# いまは bootstrap/ の平文の層は ArgoCD(bootstrap/argocd/repos.yaml の `bootstrap`)が当てる。 +# いまは平文の層は ArgoCD が apps/ から当てる(2026-10 までは bootstrap/ の Application `bootstrap`)。 # -# 当てるのは machine config。このファイルは [../../talos/render.sh](../../talos/render.sh) が +# 当てるのは machine config。このファイルは [render.sh](render.sh) が # inlineManifest に描くので、machine config を当てた時点で入っている(このファイルが正本で、写しは作らない)。 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -27,14 +27,14 @@ roleRef: kind: ClusterRole name: cilium-drift subjects: - # talos/controlplane.yaml の claimMappings.username.expression が作る名前。 + # talos.sops.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/controlplane.yaml の 2 つめの issuer)。アプリロール admin を持つ人は +# (talos.sops.yaml の 2 つめの issuer)。アプリロール admin を持つ人は # entra:admin、それ以外は entra:viewer のグループになって届く。 # # CI と同じくこのファイルは machine config に載るので、クラスタを作り直した diff --git a/bootstrap/argocd/secret-values.sops.yaml b/bootstrap/argocd-secret-values.sops.yaml similarity index 100% rename from bootstrap/argocd/secret-values.sops.yaml rename to bootstrap/argocd-secret-values.sops.yaml diff --git a/bootstrap/argocd/values.yaml b/bootstrap/argocd-values.yaml similarity index 89% rename from bootstrap/argocd/values.yaml rename to bootstrap/argocd-values.yaml index e23c18c7..d8210f65 100644 --- a/bootstrap/argocd/values.yaml +++ b/bootstrap/argocd-values.yaml @@ -1,4 +1,7 @@ -# argo-cd の chart の値。秘密の値は secret-values.sops.yaml。描いて machine config に載せるのは talos/render.sh。 +# chart と版。Renovate が上げ、render.sh が読む(README.md「Helm で入れるもの」)。 +# renovate: datasource=helm depName=argo-cd registryUrl=https://argoproj.github.io/argo-helm +# version: 10.11.6 +# argo-cd の chart の値。秘密の値は argocd-secret-values.sops.yaml。描いて machine config に載せるのは render.sh。 # CRD は描かせない(1.83 MB で描き出しの 95%)。render.sh が URL で渡す。 crds: install: false @@ -79,9 +82,9 @@ notifications: # (ApplicationSet)は repos.yaml に置く。 extraObjects: # danything/* を読む GitHub App の repo-creds(git から取れないのでここに置く)。名前は UI で作ったときのもの。 - # 鍵は secret-values.sops.yaml。リストは値ファイルを重ねると丸ごと置き換わるので、map を toYaml して 1 要素にする。 + # 鍵は argocd-secret-values.sops.yaml。リストは値ファイルを重ねると丸ごと置き換わるので、map を toYaml して 1 要素にする。 - '{{ toYaml .Values.githubAppRepoCreds }}' - # app-of-apps。include を広げると values.yaml まで prune/selfHeal の対象になり、消すと ArgoCD が消える。 + # app-of-apps。include を広げると bootstrap/ の他のファイルまで prune/selfHeal の対象になり、消すと ArgoCD が消える。 - apiVersion: argoproj.io/v1alpha1 kind: Application metadata: diff --git a/bootstrap/argocd/namespace.yaml b/bootstrap/argocd/namespace.yaml deleted file mode 100644 index a040f2ba..00000000 --- a/bootstrap/argocd/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: argocd diff --git a/bootstrap/argocd/repos.yaml b/bootstrap/argocd/repos.yaml index 3be14c01..edf04568 100644 --- a/bootstrap/argocd/repos.yaml +++ b/bootstrap/argocd/repos.yaml @@ -1,3 +1,5 @@ +# bootstrap/ で ArgoCD が当てるのはこのファイルだけ(machine config の `apps` が拾う。場所は argocd-values.yaml の +# extraObjects に焼いてあるので、動かすと machine config を当て直すことになる)。平文のマニフェストは apps/ にある。 # Forgejo 側は別の ApplicationSet(下の `repos-forgejo`)に分ける。generator が 1 つでも失敗すると # Application を 1 本も作らないので、まっさらなクラスタ(Forgejo も forgejo-repo-creds もまだ無い)で # 同居させると自力で抜けられない。2 つの template は同じ形を保つこと。 @@ -73,42 +75,6 @@ spec: prune: {{ .prune }} selfHeal: {{ .selfHeal }} --- -# bootstrap/ の平文のマニフェスト(Gateway・auth・ClusterIssuer・InfisicalSecret など)。 -# machine config が持つもの(値・版・SOPS・Namespace・apiserver/・cilium/)と、`apps` が持つこのファイルは外す。 -# `*` は `/` もまたぐ。 -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: bootstrap - namespace: argocd -spec: - project: default - source: - repoURL: https://github.com/danything/gitops.git - targetRevision: main - path: bootstrap - directory: - recurse: true - exclude: '{*values.yaml,*version.yaml,*.sops.yaml,apiserver/*,cilium/*,argocd/repos.yaml,argocd/namespace.yaml,cert-manager/namespace.yaml,infisical/namespace.yaml}' - destination: - server: https://kubernetes.default.svc - namespace: argocd - syncPolicy: - # 平文のマニフェストを apps/ へ移す間、自動の同期(と prune)を止める。移す PR で bootstrap/ の中身が - # 入れ替わったとき、古い exclude のまま同期して移したもの(auth の Namespace ごと)を消さないため。 - # 移し終えたらこの Application ごと消す(finalizer が無いので、消しても中のリソースは残る)。 - automated: - enabled: false - prune: true - selfHeal: true - # InfisicalSecret の CRD は別の Application が入れるので、まっさらなクラスタでは最初は落ちる - retry: - limit: 10 - backoff: - duration: 15s - factor: 2 - maxDuration: 5m ---- # Forgejo(fj.doany.io)の doa を見る。失敗の理由は `get applicationset repos-forgejo -o yaml` の ErrorOccurred。 apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet diff --git a/bootstrap/argocd/version.yaml b/bootstrap/argocd/version.yaml deleted file mode 100644 index 57fca34a..00000000 --- a/bootstrap/argocd/version.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# renovate: datasource=helm depName=argo-cd registryUrl=https://argoproj.github.io/argo-helm -version: 10.11.6 diff --git a/bootstrap/cert-manager/cloudflare-secret.sops.yaml b/bootstrap/cert-manager-cloudflare-secret.sops.yaml similarity index 100% rename from bootstrap/cert-manager/cloudflare-secret.sops.yaml rename to bootstrap/cert-manager-cloudflare-secret.sops.yaml diff --git a/bootstrap/cert-manager/values.yaml b/bootstrap/cert-manager-values.yaml similarity index 59% rename from bootstrap/cert-manager/values.yaml rename to bootstrap/cert-manager-values.yaml index 8bf1eea4..68b77eac 100644 --- a/bootstrap/cert-manager/values.yaml +++ b/bootstrap/cert-manager-values.yaml @@ -1,4 +1,7 @@ -# cert-manager の chart の値。描いて machine config に載せるのは ../../talos/render.sh。 +# chart と版。Renovate が上げ、render.sh が読む(README.md「Helm で入れるもの」)。 +# renovate: datasource=helm depName=cert-manager registryUrl=https://charts.jetstack.io +# version: v1.21.3 +# cert-manager の chart の値。描いて machine config に載せるのは render.sh。 # 無いと Gateway の `cert-manager.io/cluster-issuer` 注釈から Certificate を作らず、証明書が 1 枚も出ない config: diff --git a/bootstrap/cert-manager/namespace.yaml b/bootstrap/cert-manager/namespace.yaml deleted file mode 100644 index 1f1b8ee1..00000000 --- a/bootstrap/cert-manager/namespace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# chart は Namespace を描かないので git に出す。render.sh が chart の描き出しの先頭に付けて -# machine config に載せる([../../talos/render.sh](../../talos/render.sh))。argocd / infisical と同じ形。 -apiVersion: v1 -kind: Namespace -metadata: - name: cert-manager diff --git a/bootstrap/cert-manager/version.yaml b/bootstrap/cert-manager/version.yaml deleted file mode 100644 index b29a82ab..00000000 --- a/bootstrap/cert-manager/version.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# renovate: datasource=helm depName=cert-manager registryUrl=https://charts.jetstack.io -version: v1.21.3 diff --git a/bootstrap/cilium/values.yaml b/bootstrap/cilium-values.yaml similarity index 88% rename from bootstrap/cilium/values.yaml rename to bootstrap/cilium-values.yaml index 975cb2f5..5a9453a9 100644 --- a/bootstrap/cilium/values.yaml +++ b/bootstrap/cilium-values.yaml @@ -1,7 +1,10 @@ -# Cilium の値の正本。talos/render.sh が描いて inlineManifest にする(render.sh → apply-config → upgrade-k8s)。 +# chart と版。Renovate が上げ、render.sh が読む(README.md「Helm で入れるもの」)。 +# renovate: datasource=helm depName=cilium registryUrl=https://helm.cilium.io +# version: 1.20.4 +# Cilium の値の正本。render.sh が描いて inlineManifest にする(render.sh → apply-config → upgrade-k8s)。 # cilium-config を直接いじらない(次の upgrade-k8s で黙って戻る)。 -# kube-proxy は talos/controlplane.yaml で止めている +# kube-proxy は talos.sops.yaml で止めている kubeProxyReplacement: true # KubePrism。Talos が localhost:7445 で API サーバを負荷分散する。違うと Cilium が上がらない(render.sh の needle が見ている)。 k8sServiceHost: localhost diff --git a/bootstrap/cilium/README.md b/bootstrap/cilium/README.md deleted file mode 100644 index 54943d0f..00000000 --- a/bootstrap/cilium/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# cilium - -**クラスタの土台。** CNI・kube-proxy の代替・Gateway API を全部これが担う。 -決定の経緯は [../../docs/decisions.md](../../docs/decisions.md)「ルーティングの選定」。 - -## 当て方 - -Argo CD は同期しない(`bootstrap/` は対象外)。**machine config の inlineManifest に載っている**ので、 -[values.yaml](values.yaml) か [version.yaml](version.yaml) を直したら -**`render.sh` → `apply-config` → `upgrade-k8s`**([../../talos/README.md](../../talos/README.md)「上げ方 / 当て直し方」)。 -`helm upgrade` は使わない。値はここが正本で、machine config は [../../talos/render.sh](../../talos/render.sh) が -ここから描く派生物(二度書かない。decisions.md「machine config と Cilium の chart をどう連動させるか」)。 - -**どの設定が何のために要るかは [values.yaml](values.yaml) のコメントにある**(ここには写さない)。 -版は [version.yaml](version.yaml) が唯一の出どころ(Renovate がここを追う)。 - -## 描き直すたびに Hubble の証明書が入れ替わる - -チャートの `hubble/tls-helm/*` は毎回 `genCA` / `genSignedCert` で作り直す -(`cilium.io/helm-template-non-idempotent: "true"`)。**値を 1 行も変えなくても、`hubble-ca-secret` / -`hubble-server-certs` / `hubble-relay-client-certs` の 3 つは `upgrade-k8s` の差分に出る。** -実害は無い(エージェントも Relay もファイルの入れ替わりに追従する。Pod の再起動は要らない)。 -`cilium-dbg status` の `Hubble: Ok` と `hubble-relay` の Ready で確かめる。 - -## ズレは CI が見ている - -**当て忘れとクラスタ側の直接編集は [../../.github/workflows/cilium-drift.yml](../../.github/workflows/cilium-drift.yml) が見つける。** -`values.yaml` から ConfigMap を描いて live と突き合わせるだけで、**読むだけ**(権限は `cilium-config` の `get` 1 つ)。 -`bootstrap/cilium/**` を触ったときと毎週月曜に走る。当てるのは自動化しない(Talos API の os:admin を CI に置かない)。 - -## 当てたあとに確認すること - -```shell -# ホストのポートが張られているか(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 doany -``` diff --git a/bootstrap/cilium/version.yaml b/bootstrap/cilium/version.yaml deleted file mode 100644 index 8ffa7779..00000000 --- a/bootstrap/cilium/version.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# renovate: datasource=helm depName=cilium registryUrl=https://helm.cilium.io -version: 1.20.4 diff --git a/bootstrap/infisical/secret-values.sops.yaml b/bootstrap/infisical-secret-values.sops.yaml similarity index 100% rename from bootstrap/infisical/secret-values.sops.yaml rename to bootstrap/infisical-secret-values.sops.yaml diff --git a/bootstrap/infisical/secrets.sops.yaml b/bootstrap/infisical-secrets.sops.yaml similarity index 100% rename from bootstrap/infisical/secrets.sops.yaml rename to bootstrap/infisical-secrets.sops.yaml diff --git a/bootstrap/infisical/values.yaml b/bootstrap/infisical-values.yaml similarity index 85% rename from bootstrap/infisical/values.yaml rename to bootstrap/infisical-values.yaml index 89ec5c1a..30b75159 100644 --- a/bootstrap/infisical/values.yaml +++ b/bootstrap/infisical-values.yaml @@ -1,5 +1,8 @@ -# infisical-standalone の chart の値(Postgres + Redis 同梱)。DB と Redis のパスワードは secret-values.sops.yaml。 -# 描いて machine config に載せるのは talos/render.sh(ArgoCD に置けない理由は docs/decisions.md)。 +# chart と版。Renovate が上げ、render.sh が読む(README.md「Helm で入れるもの」)。 +# renovate: datasource=helm depName=infisical-standalone registryUrl=https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/ +# version: 1.12.0 +# infisical-standalone の chart の値(Postgres + Redis 同梱)。DB と Redis のパスワードは infisical-secret-values.sops.yaml。 +# 描いて machine config に載せるのは render.sh(ArgoCD に置けない理由は docs/decisions.md)。 infisical: # 無いと名前が infisical-infisical-standalone-infisical になる fullnameOverride: infisical diff --git a/bootstrap/infisical/README.md b/bootstrap/infisical/README.md deleted file mode 100644 index 20b6b5f5..00000000 --- a/bootstrap/infisical/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# infisical - -https://il.doany.io -- セルフホストの Infisical。中身の説明は上位の README の「Secret」を参照。 - -## 管理者アカウント - -初期化(`POST /api/v1/admin/bootstrap`)で作った `admin@doany.io` は現存しない -(2026-09-02 にログインを試して "Failed to find user")。いまの管理者は個人アカウントで、 -資格情報はこのリポジトリに置かない。 - -ロックアウトしたときの復旧は、SMTP(info@doany.io 経由)が生きていればログイン画面の -パスワードリセット。それも届かない場合は `infisical` ns の PostgreSQL に入って -`users` テーブルから管理者を特定し、Infisical のドキュメントにある手順でリセットする。 - -初期化で一緒に作られる "Instance Admin Identity"(root 相当の Machine Identity)は -セットアップ後に削除してある。必要なら UI の Organization Settings > Identities で作り直す。 - -## 戻し方 - -DB は手元の restic から直接戻す(k8up の資格情報が Infisical から来るので)。戻したら operator の -Kubernetes auth の CA も差し替える。手順は [../../recovery/README.md](../../recovery/README.md) の 6・7。 diff --git a/bootstrap/infisical/namespace.yaml b/bootstrap/infisical/namespace.yaml deleted file mode 100644 index acc316bc..00000000 --- a/bootstrap/infisical/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: infisical diff --git a/bootstrap/infisical/version.yaml b/bootstrap/infisical/version.yaml deleted file mode 100644 index a3aada8d..00000000 --- a/bootstrap/infisical/version.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# renovate: datasource=helm depName=infisical-standalone registryUrl=https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/ -version: 1.12.0 diff --git a/talos/local-path.yaml b/bootstrap/local-path.yaml similarity index 97% rename from talos/local-path.yaml rename to bootstrap/local-path.yaml index 7797c34a..64caf5c4 100644 --- a/talos/local-path.yaml +++ b/bootstrap/local-path.yaml @@ -1,7 +1,7 @@ # local-path-provisioner。Talos には無いので、入れないと PVC が 1 つも bind せず、 # ストレージを持つアプリが全部 Pending で止まる。 # -# - データの置き場は `/var/mnt/local-path`([`controlplane.yaml`](controlplane.yaml) の +# - データの置き場は `/var/mnt/local-path`([`talos.sops.yaml`](talos.sops.yaml) の # `UserVolumeConfig` が切る別パーティション)。ディレクトリは `pvc-__` # - namespace は `local-path-storage`(PSA は privileged。ヘルパー Pod が hostPath を使う) # - StorageClass は既定の `local-path` 1 つだけ。PVC を守るのは Retain ではなく日次の restic diff --git a/talos/render.sh b/bootstrap/render.sh similarity index 68% rename from talos/render.sh rename to bootstrap/render.sh index 45fc05b7..6c94542a 100755 --- a/talos/render.sh +++ b/bootstrap/render.sh @@ -1,8 +1,8 @@ #!/bin/sh -# machine config を作る: ./talos/render.sh talos/clusterconfig -# chart の値は bootstrap/*/values.yaml が正本。ここに写さない(../docs/decisions.md)。 +# machine config を作る: ./bootstrap/render.sh bootstrap/clusterconfig +# chart の値と版は bootstrap/-values.yaml が正本。ここに写さない(../docs/decisions.md)。 # SOPS は自分で復号する。CI は鍵が無いので SECRETS などの env にダミーを渡す。 -# 出力は秘密を含み、出力先は丸ごと作り直す。当て方は talos/README.md。 +# 出力は秘密を含み、出力先は丸ごと作り直す。当て方は talos.md。 set -eu OUT=${1:?usage: render.sh } @@ -23,7 +23,7 @@ SECRETS=${SECRETS:-} if [ -z "$SECRETS" ]; then OWN_SECRETS=$WORK/secrets.yaml SECRETS=$OWN_SECRETS - sops -d talos/secrets.yaml > "$SECRETS" + sops -d bootstrap/talos-secrets.sops.yaml > "$SECRETS" fi # 手で書く部分。password の値だけ SOPS で暗号化してある(../.sops.yaml)。CI は ENC をダミーにしたものを渡す。 @@ -31,25 +31,26 @@ fi CONTROLPLANE=${CONTROLPLANE:-} if [ -z "$CONTROLPLANE" ]; then CONTROLPLANE=$WORK/controlplane.yaml - sops -d talos/controlplane.yaml > "$CONTROLPLANE" + sops -d bootstrap/talos.sops.yaml > "$CONTROLPLANE" fi -! grep -q 'ENC\[' "$CONTROLPLANE" || { echo "ERROR: controlplane.yaml に暗号文が残っている" >&2; exit 1; } +! grep -q 'ENC\[' "$CONTROLPLANE" || { echo "ERROR: talos.sops.yaml に暗号文が残っている" >&2; exit 1; } # キーを名前で引く(位置で数えるとブロックが増えたとき別の値を掴む)。yq は版で構文が違うので awk。 ver() { # $1=トップレベルのキー $2=その下のキー - awk -v top="$1:" -v key=" $2: " '$0 == top {f=1; next} /^[^ #]/ {f=0} f && index($0, key) == 1 {print substr($0, length(key)+1); exit}' talos/versions.yaml + awk -v top="$1:" -v key=" $2: " '$0 == top {f=1; next} /^[^ #]/ {f=0} f && index($0, key) == 1 {print substr($0, length(key)+1); exit}' bootstrap/versions.yaml } TALOS=$(ver talos version) SCHEMATIC=$(ver talos schematic) K8S=$(ver kubernetes version) GWAPI=$(ver gatewayAPI version) -bver() { sed -n 's/^version: //p' "bootstrap/$1/version.yaml"; } +# chart の版は -values.yaml の先頭の `# version:` の行(Renovate が上げる)。最初の 1 行だけ見る。 +bver() { sed -n '/^# version: /{s/^# version: *//p;q;}' "bootstrap/$1-values.yaml"; } CILIUM=$(bver cilium) CERTMGR=$(bver cert-manager) ARGOCD=$(bver argocd) INFISICAL=$(bver infisical) for v in "$TALOS" "$SCHEMATIC" "$K8S" "$GWAPI" "$CILIUM" "$CERTMGR" "$ARGOCD" "$INFISICAL"; do - [ -n "$v" ] || { echo "ERROR: versions.yaml / version.yaml から版を読めなかった" >&2; exit 1; } + [ -n "$v" ] || { echo "ERROR: versions.yaml / *-values.yaml の # version: から版を読めなかった" >&2; exit 1; } done echo "talos=$TALOS k8s=$K8S gateway-api=$GWAPI" echo "cilium=$CILIUM cert-manager=$CERTMGR argo-cd=$ARGOCD infisical-standalone=$INFISICAL" @@ -59,23 +60,60 @@ inline() { # $1=name stdin=manifest sed 's/^/ /' } -# chart と repo は version.yaml の `# renovate:` 行から読む。namespace.yaml は先頭に付ける(最初は誰も作らない)。 +# helm の描き出しに namespace を補う(chart によっては書かず、Talos の適用では default に落ちる)。 +add_namespace() { # $1=namespace stdin=helm の描き出し + awk -v ns="$1" ' +BEGIN { + # クラスタスコープのものには足さない(足すと API が弾く) + split("Namespace ClusterRole ClusterRoleBinding CustomResourceDefinition " \ + "ValidatingWebhookConfiguration MutatingWebhookConfiguration APIService " \ + "StorageClass PriorityClass RuntimeClass IngressClass PersistentVolume " \ + "ClusterIssuer CiliumClusterwideNetworkPolicy", a, " ") + for (i in a) cluster[a[i]] = 1 + n = 0 +} +/^---$/ { flush(); print; next } +{ buf[n++] = $0 } +END { flush() } + +function flush( i, kind, mi, hasns, inmeta) { + if (n == 0) return + kind = ""; mi = -1; hasns = 0; inmeta = 0 + for (i = 0; i < n; i++) { + if (buf[i] ~ /^kind: /) { kind = substr(buf[i], 7) } + if (buf[i] ~ /^metadata:$/) { mi = i; inmeta = 1; continue } + if (inmeta && buf[i] ~ /^[^ ]/) inmeta = 0 + if (inmeta && buf[i] ~ /^ namespace:/) hasns = 1 + } + for (i = 0; i < n; i++) { + print buf[i] + if (i == mi && !hasns && kind != "" && !(kind in cluster)) + print " namespace: " ns + } + n = 0 +}' +} + +# chart と repo は -values.yaml の `# renovate:` の行から読む。 +# kube-system 以外は Namespace を先頭に付ける(chart は描かず、最初は誰も作らない)。 # `updatedAt` は infisical の chart が付ける時刻(values で消せない)。残すと当て直すたびに再起動する。 chart() { # $1=name $2=namespace 残り=helm template に足す引数 _n=$1 _ns=$2 shift 2 - _d=bootstrap/$_n - _repo=$(sed -n 's/.* registryUrl=\([^ ]*\).*/\1/p' "$_d/version.yaml") - _chart=$(sed -n 's/.* depName=\([^ ]*\).*/\1/p' "$_d/version.yaml") + _f=bootstrap/$_n-values.yaml + _repo=$(sed -n 's/^# renovate:.* registryUrl=\([^ ]*\).*/\1/p' "$_f") + _chart=$(sed -n 's/^# renovate:.* depName=\([^ ]*\).*/\1/p' "$_f") [ -n "$_repo" ] && [ -n "$_chart" ] \ - || { echo "ERROR: $_d/version.yaml の # renovate: の行から chart と repo を読めなかった" >&2; exit 1; } + || { echo "ERROR: $_f の # renovate: の行から chart と repo を読めなかった" >&2; exit 1; } helm repo add --force-update "$_n" "$_repo" >/dev/null { - if [ -f "$_d/namespace.yaml" ]; then cat "$_d/namespace.yaml"; echo ---; fi + if [ "$_ns" != kube-system ]; then + printf 'apiVersion: v1\nkind: Namespace\nmetadata:\n name: %s\n---\n' "$_ns" + fi helm template "$_n" "$_n/$_chart" \ - --version "$(bver "$_n")" -n "$_ns" -f "$_d/values.yaml" "$@" --kube-version "$K8S" \ + --version "$(bver "$_n")" -n "$_ns" -f "$_f" "$@" --kube-version "$K8S" \ | sed '/^ *updatedAt: "/d' \ - | awk -v ns="$_ns" -f talos/add-namespace.awk + | add_namespace "$_ns" } | inline "$_n" > "$WORK/inline-$_n.yaml" echo " $_n -> $(wc -c < "$WORK/inline-$_n.yaml") バイト" } @@ -83,16 +121,16 @@ 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/local-path.yaml > "$WORK/inline-local-path.yaml" +inline local-path < bootstrap/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" +# CI と人の RBAC。締め出されても直せるよう ArgoCD に載せない(ArgoCD は bootstrap/ を repos.yaml しか見ない)。 +inline apiserver-rbac < bootstrap/apiserver-rbac.yaml > "$WORK/inline-apiserver-rbac.yaml" -# 秘密の値は secret-values.sops.yaml を values.yaml に重ねる。infisical は chart がパスワードを +# 秘密の値は -secret-values.sops.yaml を -values.yaml に重ねる。infisical は chart がパスワードを # 平文 env に焼くので ArgoCD に置けない(../docs/decisions.md)。 secret_values() { # $1=name $2=env の上書き if [ -n "$2" ]; then echo "$2"; return 0; fi - sops -d "bootstrap/$1/secret-values.sops.yaml" > "$WORK/$1-secret-values.yaml" + sops -d "bootstrap/$1-secret-values.sops.yaml" > "$WORK/$1-secret-values.yaml" echo "$WORK/$1-secret-values.yaml" } # 先に変数に取る。`chart … -f "$(secret_values …)"` だと復号の失敗を `set -e` が拾わない @@ -101,7 +139,7 @@ INFISICAL_SV=$(secret_values infisical "${INFISICAL_SECRET_VALUES:-}") chart argocd argocd -f "$ARGOCD_SV" chart infisical infisical -f "$INFISICAL_SV" -# CRD は URL で渡す(理由は values.yaml の `crds`)。版は chart の appVersion から取る。 +# CRD は URL で渡す(理由は argocd-values.yaml の `crds`)。版は chart の appVersion から取る。 ARGOCD_APP=$(helm show chart argocd/argo-cd --version "$ARGOCD" 2>/dev/null \ | sed -n 's/^appVersion: *//p' | tr -d "\"'") [ -n "$ARGOCD_APP" ] || { echo "ERROR: argo-cd の appVersion を読めなかった" >&2; exit 1; } @@ -128,10 +166,10 @@ inline_secret() { # $1=inline の名前 $2=SOPS ファイル $3=env の上書 sops -d "$2" > "$WORK/plain-$1.yaml" inline "$1" < "$WORK/plain-$1.yaml" > "$WORK/inline-$1.yaml" } -inline_secret infisical-secrets bootstrap/infisical/secrets.sops.yaml "${INFISICAL_SECRET:-}" -inline_secret cloudflare-secret bootstrap/cert-manager/cloudflare-secret.sops.yaml "${CLOUDFLARE_SECRET:-}" +inline_secret infisical-secrets bootstrap/infisical-secrets.sops.yaml "${INFISICAL_SECRET:-}" +inline_secret cloudflare-secret bootstrap/cert-manager-cloudflare-secret.sops.yaml "${CLOUDFLARE_SECRET:-}" -# cert-manager の CRD も URL(理由は values.yaml の `crds`)。 +# cert-manager の CRD も URL(理由は cert-manager-values.yaml の `crds`)。 cat >> "$WORK/external-crds.yaml" <&2; exit 1; } done -# Hubble の証明書はクラスタ内で作る(cilium/values.yaml の `hubble.tls`) +# Hubble の証明書はクラスタ内で作る(cilium-values.yaml の `hubble.tls`) ! grep -q '^ kind: Secret$' "$WORK/inline-cilium.yaml" \ || { echo "ERROR: cilium の inline に Secret(Hubble の証明書)が入っている" >&2; exit 1; } talosctl validate --config "$OUT/controlplane.yaml" --mode metal diff --git a/talos/secrets.yaml b/bootstrap/talos-secrets.sops.yaml similarity index 100% rename from talos/secrets.yaml rename to bootstrap/talos-secrets.sops.yaml diff --git a/talos/README.md b/bootstrap/talos.md similarity index 78% rename from talos/README.md rename to bootstrap/talos.md index 330f1e86..6cb94917 100644 --- a/talos/README.md +++ b/bootstrap/talos.md @@ -8,49 +8,49 @@ 新しい Talos が出たら PR を作る。**適用は自動化しない**(下の「適用は CI にやらせない」)。 コマンドに埋めてある版と schematic も、変えるときは versions.yaml と揃えること。talhelper は使わない。 -**組み立ては [render.sh](render.sh) が全部やる。** 手で書く部分は [controlplane.yaml](controlplane.yaml) の 1 本だけで、 -他から描いてくる部分(Cilium・cert-manager・argocd・infisical は `bootstrap//` の version.yaml と values.yaml から、 +**組み立ては [render.sh](render.sh) が全部やる。** 手で書く部分は [talos.sops.yaml](talos.sops.yaml) の 1 本だけで、 +他から描いてくる部分(Cilium・cert-manager・argocd・infisical は `-values.yaml` から、 local-path-provisioner は [local-path.yaml](local-path.yaml) から)と 1 か所で組む。**値をどこにも二度書かない**のが目的。 CI([talos-boot](../.github/workflows/talos-boot.yml))も同じ `render.sh` を使う (別の作り方をすると「CI は通るが実機では通らない」が起きる)。 ```shell -./talos/render.sh talos/clusterconfig # 出力先は丸ごと消して作り直す(.gitignore 済み) +./bootstrap/render.sh bootstrap/clusterconfig # 出力先は丸ごと消して作り直す(.gitignore 済み) ``` -**controlplane.yaml を丸ごとコミットしないのは**、生成物の 9 割が chart を描いたもの(約 300 KB の 1 行文字列)で、 +**描いた machine config(`clusterconfig/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`。 +- [talos-secrets.sops.yaml](talos-secrets.sops.yaml): クラスタの CA 一式(`talosctl gen secrets` の形)。丸ごと暗号化 +- [talos.sops.yaml](talos.sops.yaml): `password` の値(下の fj.doany.io の資格情報)**だけ**をその場で暗号化。 + ほかは平文のまま読めて、`sops` を通さず直接直せる(`mac_only_encrypted`)。パスワードを変えるときだけ `sops edit bootstrap/talos.sops.yaml`。 SOPS は**ドキュメントごとに `sops:` のメタデータを付け、インデントを 4 にする**(触らない)。render.sh は `sops -d` してから渡す -**`secrets.yaml` を作り直すと別のクラスタになる**(`bootstrap/apiserver/ca.crt` と Infisical の Kubernetes auth の CA も +**`talos-secrets.sops.yaml` を作り直すと別のクラスタになる**(`apiserver-ca.crt` と Infisical の Kubernetes auth の CA も 替えることになる)ので触らない。まっさらから組むときだけ: ```shell -talosctl gen secrets -o talos/secrets.yaml && sops -e -i talos/secrets.yaml +talosctl gen secrets -o bootstrap/talos-secrets.sops.yaml && sops -e -i bootstrap/talos-secrets.sops.yaml ``` ## fj.doany.io の資格情報 -ノード単位で持つので **namespace ごとの `imagePullSecrets` が要らない**(controlplane.yaml の `machine.registries`)。 +ノード単位で持つので **namespace ごとの `imagePullSecrets` が要らない**(talos.sops.yaml の `machine.registries`)。 持つのは **fj.doany.io**(Forgejo のコンテナレジストリ)だけで、Forgejo のユーザー `yui` の -`read:package` だけのトークン(全リポジトリ。Forgejo の名前は `talos-registry-pull`)。値は controlplane.yaml の中で暗号化してある。 +`read:package` だけのトークン(全リポジトリ。Forgejo の名前は `talos-registry-pull`)。値は talos.sops.yaml の中で暗号化してある。 `username` は Forgejo が見ない(トークンの持ち主で決まる。`info` のままにしてあるが、何でも通る)。無いと非公開のリポジトリの イメージが引けない。ghcr.io に残るイメージは全部公開。 **fj.doany.io はこれだけでは取れない。** 公開 DNS の A(WAN 側)にはノードから届かないので、 -ノードの名前解決で fj.doany.io をノード自身(10.0.0.2 の Gateway)に向ける(controlplane.yaml の `StaticHostConfig`)。 +ノードの名前解決で fj.doany.io をノード自身(10.0.0.2 の Gateway)に向ける(talos.sops.yaml の `StaticHostConfig`)。 -CI には age の鍵を渡さないので、controlplane.yaml の `ENC[…]` を `ci-dummy` に置き換えて経路だけ通している(`cloudflare-secret.sops.yaml` と同じやり方)。 +CI には age の鍵を渡さないので、talos.sops.yaml の `ENC[…]` を `ci-dummy` に置き換えて経路だけ通している(`cert-manager-cloudflare-secret.sops.yaml` と同じやり方)。 -**controlplane.yaml は PR ごとに CI が検証する。** 使い捨ての秘密で `render.sh` を通す。`render.sh` は `talosctl validate --mode metal` に加えて、 +**talos.sops.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 か所だけに置く。 @@ -68,22 +68,22 @@ needle は `render.sh` の末尾の 1 か所だけに置く。 | --- | --- | --- | | `versions.yaml` の `talos:`(または schematic) | `talosctl upgrade --image factory.talos.dev/installer/:<版> --k8s-endpoint https://10.10.0.4:6443` | **再起動** | | `versions.yaml` の `kubernetes:` | `render.sh` → `apply-config` → `talosctl upgrade-k8s --to <版>` | 無し | -| `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` | 場合による | +| `talos.sops.yaml` と、`bootstrap/` の残りの全部(`*-values.yaml`・`*-secret-values.sops.yaml`・`apiserver-rbac.yaml`・`local-path.yaml`・SOPS の Secret 2 つ) | `render.sh` → `apply-config` → `talosctl upgrade-k8s` | 場合による | ### inlineManifests を直したとき -**Cilium・local-path・`apiserver/rbac.yaml`・ArgoCD・infisical・ +**Cilium・local-path・`apiserver-rbac.yaml`・ArgoCD・infisical・ cert-manager と、SOPS 済みの Secret 2 つは machine config の中にいる。** だから**マニフェストを直しただけでは何も起きない。** 描き直して当てる: ```shell -./talos/render.sh talos/clusterconfig -talosctl apply-config --mode=no-reboot -f talos/clusterconfig/controlplane.yaml # 再起動が要るなら当てずに止まる +./bootstrap/render.sh bootstrap/clusterconfig +talosctl apply-config --mode=no-reboot -f bootstrap/clusterconfig/controlplane.yaml # 再起動が要るなら当てずに止まる talosctl get manifests -o yaml | grep -c '<新しくなるはずの版(例: cilium:v1.20.4)>' # 反映を待つ(下) talosctl upgrade-k8s --to --endpoint https://10.10.0.4:6443 # ← これが inlineManifests を reconcile する ``` -`render.sh` は出力先を丸ごと消すので、`talos/clusterconfig/` に置いたものは消える。 +`render.sh` は出力先を丸ごと消すので、`bootstrap/clusterconfig/` に置いたものは消える。 - **`upgrade-k8s` には必ず `--to` を付ける。** 省くと talosctl の既定の版に上がる(Talos の alpha の talosctl なら Kubernetes も alpha)。 版を変えないなら `versions.yaml` の今の版を渡す @@ -119,7 +119,7 @@ talosctl upgrade --image factory.talos.dev/installer/:<版> --k8s-end ### 当てても効かないもの -- **ディスクの割り方**(controlplane.yaml の `VolumeConfig` / `UserVolumeConfig`)。ボリュームは**まだ確保されていないときにしか +- **ディスクの割り方**(talos.sops.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/bootstrap/talos.sops.yaml similarity index 99% rename from talos/controlplane.yaml rename to bootstrap/talos.sops.yaml index cf9744c3..68ae90cc 100644 --- a/talos/controlplane.yaml +++ b/bootstrap/talos.sops.yaml @@ -2,7 +2,7 @@ # chart から描く部分(Cilium・ArgoCD・infisical・cert-manager・local-path)と版は render.sh が足す。 # # **SOPS で password の値だけ暗号化してある**(../.sops.yaml)。平文の部分はそのまま直してよい。 -# パスワードを変えるときは `sops edit talos/controlplane.yaml`。render.sh が `sops -d` してから渡す。 +# パスワードを変えるときは `sops edit bootstrap/talos.sops.yaml`。render.sh が `sops -d` してから渡す。 # 各ドキュメントの末尾の `sops:` は SOPS のメタデータ(触らない)。 # # 型付きドキュメントと v1alpha1 で書く場所が決まっている(../docs/talos.md「machine config の作法」)。 @@ -417,7 +417,7 @@ sops: mac_only_encrypted: true version: 3.13.3 --- -# 外から入る CI(cilium-drift.yml の OIDC トークン)と人(Entra)の認証。権限の中身は ../bootstrap/apiserver/rbac.yaml。 +# 外から入る CI(cilium-drift.yml の OIDC トークン)と人(Entra)の認証。権限の中身は apiserver-rbac.yaml。 apiVersion: v1alpha1 kind: KubeAuthenticationConfig # `anonymous` を消さないこと。与えると既定を置き換えるので、書き忘れると diff --git a/talos/versions.yaml b/bootstrap/versions.yaml similarity index 94% rename from talos/versions.yaml rename to bootstrap/versions.yaml index 96fef9bc..40e96468 100644 --- a/talos/versions.yaml +++ b/bootstrap/versions.yaml @@ -1,5 +1,5 @@ # Talos と Kubernetes の版。Renovate が書き換えるが、適用は手で(ノード 1 台なので upgrade は全停止)。 -# 手順は README.md「上げ方 / 当て直し方」。 +# 手順は talos.md「上げ方 / 当て直し方」。 talos: # Factory のレジストリはタグ一覧を返さないので github-releases で追う。tools/Dockerfile の talosctl と同じ depName。 diff --git a/docs/cloudflare-tokens.md b/docs/cloudflare-tokens.md index 2aa13a28..112ed5a4 100644 --- a/docs/cloudflare-tokens.md +++ b/docs/cloudflare-tokens.md @@ -11,7 +11,7 @@ Pulumi で作ると値を Infisical に書く鍵が CI に要る。その鍵は | `external-dns (doany.io DNS write)` | アカウント | `doany.io` の Zone Read + DNS Write | Infisical `/external-dns/external-dns` の `cloudflare-api-token` | apps/external-dns | | `doany-restic` | アカウント | R2 バケット `doany-restic` だけの Object Read & Write | Infisical `/k8up/k8up-global`、`recovery/env.age` | k8up、復元(手元の restic) | | cloudflare-ddns | ユーザー(アカウントの一覧に無い) | 画面で確かめる(DNS Write が要る) | Infisical `/cloudflare-ddns/cloudflare-ddns-secrets` の `CLOUDFLARE_API_TOKEN` | apps/cloudflare-ddns | -| cert-manager | ユーザー(アカウントの一覧に無い) | 画面で確かめる(DNS Write が要る) | SOPS の `bootstrap/cert-manager/cloudflare-secret.sops.yaml` | cert-manager(DNS-01) | +| cert-manager | ユーザー(アカウントの一覧に無い) | 画面で確かめる(DNS Write が要る) | SOPS の `bootstrap/cert-manager-cloudflare-secret.sops.yaml` | cert-manager(DNS-01) | | `todoroku-pulumi (state)` | アカウント | R2 バケット `todoroku-pulumi` だけの Object Read & Write | Forgejo `doa/todoroku` の secrets `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`(ID と値の SHA-256) | todoroku の `infra/`(Pulumi の state) | | `gitops pulumi apply` / `preview` | アカウント | pulumi/README.md「鍵と state」 | GitHub の secrets | gitops の Pulumi | @@ -25,7 +25,7 @@ Pulumi で作ると値を Infisical に書く鍵が CI に要る。その鍵は - **external-dns / cloudflare-ddns:** Infisical の値を差し替える。operator が Secret を書き換え、 push-bridge が同期させる。Deployment の `secrets.infisical.com/auto-reload` 注釈で入れ替わる。 - **todoroku-pulumi (state):** Forgejo の 2 つの secrets を差し替える(S3 の鍵の導き方は下の doany-restic と同じ)。 -- **cert-manager:** `tools/t.ps1 sops bootstrap/cert-manager/cloudflare-secret.sops.yaml` で書き換えてコミットする。 +- **cert-manager:** `tools/t.ps1 sops bootstrap/cert-manager-cloudflare-secret.sops.yaml` で書き換えてコミットする。 - **doany-restic(R2):** S3 の鍵はトークンから導く。アクセスキー ID はトークンの ID、シークレットはトークンの値の SHA-256(16 進)。**2 か所を両方変える**。Infisical が古いとバックアップが落ち(k8up の通知で気づく)、`env.age` が古いと建て直しで Infisical を戻せない(こちらは誰も気づかない)。 1. Infisical `/k8up/k8up-global` の `accessKeyId` / `secretAccessKey` diff --git a/docs/decisions.md b/docs/decisions.md index e1b2c5cd..0a74652f 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -49,14 +49,14 @@ Object Read & Write をこのバケットだけに絞った Account API token。 | --- | --- | --- | | R2 の鍵 + restic のパスフレーズ | `recovery/env.age`(公開 repo) | **パスフレーズで直接**(`age -d`) | | SOPS の age 秘密鍵 | `recovery/sops-age.key.age`(公開 repo) | **パスフレーズで直接**(`age -d`) | -| Talos の `secrets.yaml` | `talos/secrets.yaml`(公開 repo・SOPS 済み) | 上の age 鍵で(`sops -d`) | +| Talos の `secrets.yaml` | `bootstrap/talos-secrets.sops.yaml`(公開 repo・SOPS 済み) | 上の age 鍵で(`sops -d`) | | `bootstrap/` の Secret 4 つ | 公開 repo・SOPS 済み | 同上 | **`recovery/` の 2 つだけ `age -p` なのは、それが「まだ何も無い状態で最初に開けるもの」だから。** それ以外は age 鍵さえ手に入れば開くので、`bootstrap/` と同じ SOPS に揃えてある。 **`talosconfig` は保存しない。** `secrets.yaml` から `talosctl gen config` で毎回出てくる派生物で、 -TTL も付いている。使い捨てにするほうが安全(../talos/README.md)。 +TTL も付いている。使い捨てにするほうが安全(../bootstrap/talos.md)。 R2 の鍵を GitHub の秘密(gist・Actions)に置かないのは、GitHub のトークンがあちこちに散っているため。 GitHub 側が丸ごと漏れても、バックアップ(を消せる鍵)だけは無事な状態を残す。 @@ -98,27 +98,27 @@ Talos には helm-controller が無く、inlineManifests は Helm を実行で | 層 | 中身 | 資格情報 | | --- | --- | --- | -| **machine config** | **`bootstrap/` のほぼ全部。** inline が 8 つ(cilium / local-path / `apiserver/rbac.yaml` / argocd / cert-manager / infisical / SOPS の Secret 2 つ)、CRD だけ URL で 5 つ(gateway-api / argocd ×3 / cert-manager) | 入力(`talos/secrets.yaml` など)が SOPS 済みで、生成物は git に置かない | -| **ArgoCD** | `bootstrap/` の残り(`ClusterIssuer`・Gateway・auth・`InfisicalSecret` など、平文のもの。Application `bootstrap`)と `apps/`(**infisical は来ない**。下の「infisical だけは ArgoCD に移せない」) | — | +| **machine config** | **`bootstrap/` の全部**(`argocd/repos.yaml` を除く)。inline が 8 つ(cilium / local-path / `apiserver-rbac.yaml` / argocd / cert-manager / infisical / SOPS の Secret 2 つ)、CRD だけ URL で 5 つ(gateway-api / argocd ×3 / cert-manager) | 入力(`bootstrap/talos-secrets.sops.yaml` など)が SOPS 済みで、生成物は git に置かない | +| **ArgoCD** | `apps/`。`ClusterIssuer`・Gateway・auth・`InfisicalSecret` など、本体が machine config に居る部品の平文のマニフェストもここ(2026-10 までは `bootstrap/` に混ぜて Application `bootstrap` が当てていた)。**infisical の本体は来ない**(下の「infisical だけは ArgoCD に移せない」) | — | - **CRD だけ URL で渡すのは大きさの都合。** 埋めると machine config が 341 KB → 4 MB を超える。 - `KubeExternalManifestConfig` は Talos がそのために用意している入口([talos/render.sh](../talos/render.sh)) + `KubeExternalManifestConfig` は Talos がそのために用意している入口([bootstrap/render.sh](../bootstrap/render.sh)) - **平文の残りは ArgoCD。** 鶏と卵にならない ── ArgoCD が動くのに要るもの(本体・repo の資格情報・入口の `apps`)は全部 machine config にあり、この層に頼っていない。 この層が要る CRD も machine config が入れる(`InfisicalSecret` の CRD だけ `apps/` から来るので、最初の何回かは retry)。 - **SOPS の Secret を machine config に入れる。** 復号鍵はクラスタにも CI にも渡さないので、ArgoCD からは当てられない。 - machine config の入力(`talos/secrets.yaml`)もクラスタ CA の秘密鍵を含む SOPS なので信頼水準は変わらず、起動時点で存在する + machine config の入力(`bootstrap/talos-secrets.sops.yaml`)もクラスタ CA の秘密鍵を含む SOPS なので信頼水準は変わらず、起動時点で存在する - **Helm を実行する場所はクラスタの外(手元の生成時)だけ。** Cilium は CNI なので inlineManifests 以外に置きようがなく、 ArgoCD も同じ扱いにすればクラスタ内に Helm コントローラを持たなくて済む。版の追従は Renovate ## machine config と Cilium の chart をどう連動させるか **machine config を「派生物」にする。** `helm template` の出力を `KubeInlineManifestConfig` に包んで `--config-patch` として渡す -([talos/render.sh](../talos/render.sh))ので、Cilium の値がリポジトリに二度書かれない。Renovate は -`bootstrap/cilium/version.yaml` を見ていて、machine config は毎回そこから描き直されるだけ。 +([bootstrap/render.sh](../bootstrap/render.sh))ので、Cilium の値がリポジトリに二度書かれない。Renovate は +`bootstrap/cilium-values.yaml` の先頭の `# version:` を見ていて、machine config は毎回そこから描き直されるだけ。 -**`helm upgrade` は使わない。** 更新経路は「`values.yaml` を直す → 描き直す → `upgrade-k8s`」。古い machine config のまま -`upgrade-k8s` を流すと走っている Cilium が巻き戻る([../talos/README.md](../talos/README.md)「上げ方 / 当て直し方」)。 +**`helm upgrade` は使わない。** 更新経路は「`cilium-values.yaml` を直す → 描き直す → `upgrade-k8s`」。古い machine config のまま +`upgrade-k8s` を流すと走っている Cilium が巻き戻る([../bootstrap/talos.md](../bootstrap/talos.md)「上げ方 / 当て直し方」)。 流し忘れは [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が週次で live と突き合わせる。 bootstrap のあとに `helm install` する案は、再構築のたびに「CNI が無いので何も動かない」状態での手作業が増えるので採らない。 @@ -156,7 +156,7 @@ hostPort ならノードの全アドレスで受けられ、送信元 IP も保 | 対象 | hostPort | | --- | --- | -| Gateway(`cilium-gateway-doany`) | 80 / 443。Envoy が hostNetwork で bind し、外からの通信はそこに直接届く。Service の nodePort はランダムなままでよい(`bootstrap/cilium/values.yaml` の `nodePort`) | +| Gateway(`cilium-gateway-doany`) | 80 / 443。Envoy が hostNetwork で bind し、外からの通信はそこに直接届く。Service の nodePort はランダムなままでよい(`bootstrap/cilium-values.yaml` の `nodePort`) | | adguardhome | 53 UDP・53 TCP・853 TCP | | matrix の LiveKit | 8443 UDP・8443 TCP | | 3proxy(tls-terminator サイドカー) | 3129 TCP | @@ -195,7 +195,7 @@ Entra のトークンは大きく(セッション実測 4293 バイト)、Cookie Gateway に sticky cookie が無い(要るのは tamasagashi 1 本。更新中に HTML と CSS/JS が別の Pod から返る 404 を防ぐため)。 Gateway API の session persistence は experimental チャネル限定、Cilium は 1.21 から(cilium/cilium#48029)、 `sessionAffinity: ClientIP` は Gateway 経路に効かない。いまは replicas 1 で穴を受け入れている。 -**1.21 に上げるときに Gateway API CRD を experimental に替え(`talos/render.sh` の URL)、tamasagashi の HTTPRoute に `sessionPersistence` を足す。** +**1.21 に上げるときに Gateway API CRD を experimental に替え(`bootstrap/render.sh` の URL)、tamasagashi の HTTPRoute に `sessionPersistence` を足す。** それまでにアプリを複数 replica にするなら、セッション固定が無いことを先に確かめる。 ## chart を配るときと、chart で入れるとき @@ -226,12 +226,12 @@ 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/controlplane.yaml` の `machine.kernel.modules` で読ませる。 +extension は `earth_pt3` を blacklist して配るので、`bootstrap/talos.sops.yaml` の `machine.kernel.modules` で読ませる。 復調とチューナーのモジュールは、v1.15.0-alpha.2 から Talos がカーネルの要求(`request_module`)で読む([siderolabs/talos#14724](https://github.com/siderolabs/talos/pull/14724))。 それまでは 3 つも並べて書き、起動後に PT3 を付け直す DaemonSet(pt3-rebind)で順番の問題を避けていた。 **駄目だったときの保険: KubeVirt の VM にパススルー。** PT3(05:00.0)は IOMMU グループ 36 に単独、B-CAS は USB(GemPC Twin 08e6:3437)。 -`intel_iommu=on` は schematic に残してあるが、vfio は `talos/controlplane.yaml` から外した(PT3 をホストのドライバと取り合う)ので、戻るなら足し直す。 +`intel_iommu=on` は schematic に残してあるが、vfio は `bootstrap/talos.sops.yaml` から外した(PT3 をホストのドライバと取り合う)ので、戻るなら足し直す。 **自前 extension を `imager` で焼く案は採らない**(アップグレードのたびに自前ビルドになる)。 ## 依存の更新をどこまで自動で入れるか @@ -322,14 +322,14 @@ v0.65.0 から management/signal/relay が 1 つの combined コンテナなの ## 公開経路(HTTPRoute)をどこに置くか **それぞれが公開しているものと同じ場所**に置く -(このリポジトリの `apps//httproute.yaml`、各アプリのリポジトリの `deploy/httproute.yaml`、`bootstrap/` の argocd / auth / redirect-https)。 +(このリポジトリの `apps//httproute.yaml`、各アプリのリポジトリの `deploy/httproute.yaml`。argocd・auth・gateway の経路も `apps/` の下)。 -`bootstrap/` の経路も ArgoCD(Application `bootstrap`)が当てる。ArgoCD が壊れても当たっている経路は消えず、 +これらは全部 ArgoCD が当てる。ArgoCD が壊れても当たっている経路は消えず、 直すのは Headlamp か `tools/t.ps1 --admin` の kubectl で足りる。 外部露出の一覧は `kubectl get httproute,grpcroute -A` が正確で、git を読むより早い(一覧の一枚は作らない)。 **落とさずに動かす**: ArgoCD は「git から消えた + 自分が追跡している」ものを prune する。別リポジトリへは移送先を**先に**マージして -tracking-id が書き換わってから元を消す。`bootstrap/` へは、先に live に `Prune=false` を効かせてから移し、tracking-id 注釈を剥がす。 +tracking-id が書き換わってから元を消す。同じリポジトリの中で Application をまたぐときは、元の Application の自動同期を先に止めてから移す(2026-10 に `bootstrap` から `apps/` へ移したときのやり方)。 ## DNS:直接つなぐアプリと Cloudflare を通すアプリを分ける diff --git a/docs/entra.md b/docs/entra.md index f7cf4588..fd524dce 100644 --- a/docs/entra.md +++ b/docs/entra.md @@ -46,4 +46,4 @@ kubectl logs -n auth deploy/auth --since=10m | grep AuthSuccess ``` **`OIDC_GROUPS_CLAIM` を generic な `oidc` プロバイダで使うと `groups` スコープを勝手に要求し、Entra に AADSTS650053 で断られる** -(`bootstrap/auth/deployment.yaml` のコメント)。いまは `entra-id` プロバイダで `OAUTH2_PROXY_SCOPE` も明示している。 +(`apps/auth/deployment.yaml` のコメント)。いまは `entra-id` プロバイダで `OAUTH2_PROXY_SCOPE` も明示している。 diff --git a/docs/router.md b/docs/router.md index 04ce0d30..d7955791 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/controlplane.yaml`。DHCP は使わない。既定経路は `10.0.0.1`、IPv6 は RA を受ける) | +| ノード `main` | `10.0.0.2`(bond0)、`240f:6d:842b:1::2`。**ノード側の静的設定**(`bootstrap/talos.sops.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/controlplane.yaml` の `BondConfig` の `hardwareAddr`)。固定しないと bond は先頭のメンバーの MAC を引き継ぎ、 +- **この MAC はノードの設定で固定している**(`bootstrap/talos.sops.yaml` の `BondConfig` の `hardwareAddr`)。固定しないと bond は先頭のメンバーの MAC を引き継ぎ、 再起動で変わってエントリが外れる。NIC を替えても Z440 に移しても、この値は変わらない - **公開する AAAA は静的な `::2`。** cloudflare-ddns の `local.iface.stable:bond0` は一時アドレス以外から選ぶので、SLAAC のアドレスがあると - そちらを書いてしまう。`bond0` は `autoconf=0`(talos/controlplane.yaml)で SLAAC を付けない + そちらを書いてしまう。`bond0` は `autoconf=0`(bootstrap/talos.sops.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 04243581..8b174269 100644 --- a/docs/talos.md +++ b/docs/talos.md @@ -2,8 +2,8 @@ 対象: HP ProLiant DL360 Gen9(UEFI、iLO は 10.0.0.3、Xeon E5-2696 v4、RDIMM は 6 枚中 5 枚で 40 GB、NIC は BCM5719 ×4)。 ディスクは **SATA の SSD(Radeon R7、447 GB)を Smart Array(hpsa)越しに**見ている。Linux からは `/dev/sda`(`TRAN=sas` と出るが SAS ではない)。 -版は [talos/versions.yaml](../talos/versions.yaml) が正。 -入れ方は [recovery/README.md](../recovery/README.md)、組み立てと当て直しは [talos/README.md](../talos/README.md)。 +版は [bootstrap/versions.yaml](../bootstrap/versions.yaml) が正。 +入れ方は [recovery/README.md](../recovery/README.md)、組み立てと当て直しは [bootstrap/talos.md](../bootstrap/talos.md)。 ## Image Factory の schematic @@ -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 を指定できない([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)。上流は並び順ではなく、カーネルが必要な時に読み込む形で直す方針。ROADMAP) | +| bond とそのメンバー | `BondConfig` / `LinkConfig`。v1alpha1 の `machine.network.interfaces` は bond の MAC を指定できない([talos.sops.yaml](../bootstrap/talos.sops.yaml)) | +| PV の置き場 | `UserVolumeConfig`。マウント先(`/var/mnt/`)は kubelet に自動で通るので `extraMounts` は要らない([talos.sops.yaml](../bootstrap/talos.sops.yaml))。ディスク上のラベルは `u-` | +| control-plane への scheduling 許可 | v1alpha1 の `allowSchedulingOnControlPlanes` は弾かれる。**`KubeNodeConfig` を `$patch: delete` して `labels` だけ書き戻す**([talos.sops.yaml](../bootstrap/talos.sops.yaml)) | +| CNI を止める | `KubeFlannelCNIConfig` を `$patch: delete`、`KubeProxyConfig` を `enabled: false`([talos.sops.yaml](../bootstrap/talos.sops.yaml))。**v1alpha1 の `cni.name: none` はもう書けない** | +| カーネルモジュール | `machine.kernel.modules`。**並べた順ではなく名前の順に読まれる**。Talos 1.14 以降は usermode の modprobe helper が無い(`/proc/sys/kernel/modprobe` が空)ので、`request_module` で依存を引くドライバは順番が狂うと失敗する(PT3 で踏んだ。[talos.sops.yaml](../bootstrap/talos.sops.yaml)。上流は並び順ではなく、カーネルが必要な時に読み込む形で直す方針。ROADMAP) | ### PSA のラベル(Pod Security Admission が既定で `baseline`) @@ -66,7 +66,7 @@ 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/local-path.yaml`](../talos/local-path.yaml) がラベルを付ける | +| `local-path-storage` | ヘルパー Pod の hostPath。[`bootstrap/local-path.yaml`](../bootstrap/local-path.yaml) がラベルを付ける | | `netbird` | hostPort 51820(routing peer の WireGuard)、NET_ADMIN 等、hostPath | | `denpa` | privileged・hostPath(`/dev/dvb`・`/dev/bus`・`/dev/dri`)。**ラベルは denpa リポジトリの `deploy/namespace.yaml`** にあり、gitops だけを grep すると抜けて見える | | `adguardhome` | hostPort 53 / 853 | @@ -117,12 +117,12 @@ restic dump /work/etcd.snapshot > etcd.snapshot Sidero のドキュメントの「Talos only creates missing resources from inline manifests」は起動時の適用の話で、 **`talosctl apply-config` では更新も削除もされないが、`talosctl upgrade-k8s`(同じ版でも)を通せば SSA とインベントリで更新も削除もされる** -(prune は既定で有効)。当て直しの手順は [talos/README.md](../talos/README.md)「上げ方 / 当て直し方」。 +(prune は既定で有効)。当て直しの手順は [bootstrap/talos.md](../bootstrap/talos.md)「上げ方 / 当て直し方」。 ## VM ブートドリル [.github/workflows/talos-boot.yml](../.github/workflows/talos-boot.yml) が PR ごとに描いた machine config を QEMU で起動して見る。 -見つけた穴は直した場所(`talos/controlplane.yaml`・`render.sh`・`add-namespace.awk`)に理由を書いてある。 +見つけた穴は直した場所(`bootstrap/talos.sops.yaml`・`bootstrap/render.sh`)に理由を書いてある。 VM では確かめられないもの: PT3 のモジュールの読み順、bond のフェイルオーバーの復旧方向(落とす方向は 8 秒以内で `talosctl` は途切れない)。 @@ -136,7 +136,7 @@ Z440 に移すときに変わるもの(NIC の名前・ディスク・AMT・GPU) ## ネットワークまわりの前提 Talos の既定(Flannel + kube-proxy)を止めて **Cilium に替えてある**(decisions.md「ルーティングの選定」。 -CNI の交換は構築時にやる ── 稼働中の差し替えは全 Pod 再起動が前提)。Cilium の値は [bootstrap/cilium/values.yaml](../bootstrap/cilium/values.yaml)。 +CNI の交換は構築時にやる ── 稼働中の差し替えは全 Pod 再起動が前提)。Cilium の値は [bootstrap/cilium-values.yaml](../bootstrap/cilium-values.yaml)。 LoadBalancer とクライアント IP も decisions.md「ルーティングの選定」。 - `NetworkDefaultActionConfig: block` を使うなら、`NetworkRuleConfig` で要るポートを明示的に開ける diff --git a/docs/z440-migration.md b/docs/z440-migration.md index 5d6c8f3b..d5477963 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/controlplane.yaml` で `eno4` を名指ししているところ(`LinkConfig` と sysctl の `net.ipv6.conf.eno4.disable_ipv6`)も別名に直す +- `bootstrap/talos.sops.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/controlplane.yaml` の注)。 +**ディスク**: `disk.dev_path == "/dev/sda"` は Z440 でも SSD 1 本なら同じになるはず(`talosctl get disks` で見る。`bootstrap/talos.sops.yaml` の注)。 **入れ直しのついでに、local-path の `u-local-path` を LVM のボリュームグループに切り替える**(ROADMAP の Phase 2.5、 [decisions.md](decisions.md)「Z440 で、ストレージを OpenEBS LVM LocalPV に…」)。ボリュームは未割り当ての領域にしか作れないので、このときしか切り直せない。 @@ -92,8 +92,8 @@ 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/controlplane.yaml` の注、[talos.md](talos.md))。 - schematic ID は `talos/versions.yaml` と `main.yaml` の注も揃える。**Arc の extension を足すのも同じ手順**なので一緒にやる + カーネル引数は machine config に書けない(`bootstrap/talos.sops.yaml` の注、[talos.md](talos.md))。 + schematic ID は `bootstrap/versions.yaml` と `main.yaml` の注も揃える。**Arc の extension を足すのも同じ手順**なので一緒にやる (`siderolabs/i915` で足りるか要確認。Arc B シリーズなどは `siderolabs/xe`。**両方は入れない** ── 同じ GPU を取り合う) ### つなぎ方 @@ -115,7 +115,7 @@ AMT の SOL は、OS からは PCI のシリアルポート(「Intel … KT Cont 1. **k8up のバックアップが成功している**こと: 夜のぶん(03:00 JST の通知)が OK ✅。手で確かめるなら `kubectl -n k8up create job --from=cronjob/k8up-notify notify-manual`([../apps/k8up/README.md](../apps/k8up/README.md)) 2. 計画停止を知らせる(止まるもの: 全部の Web、DNS、NetBird、Matrix、録画) -3. Z440 用の config を置く: `render.sh` → `talosctl -n 10.0.0.2 apply-config --mode=staged -f talos/clusterconfig/controlplane.yaml` +3. Z440 用の config を置く: `render.sh` → `talosctl -n 10.0.0.2 apply-config --mode=staged -f bootstrap/clusterconfig/controlplane.yaml` 4. `talosctl -n 10.0.0.2 shutdown` ## 5. 載せ替え @@ -152,6 +152,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/controlplane.yaml` の 10.10 側の `LinkConfig`・別名・ +- **10.10 側を外す**(小さいサーバをルーターにしたあと): `bootstrap/talos.sops.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 b079b644..41438375 100644 --- a/recovery/README.md +++ b/recovery/README.md @@ -7,7 +7,7 @@ | | | | --- | --- | | `env.age` | バックアップ先の資格情報(R2 の鍵、restic のパスワード)。age のパスフレーズで暗号化 | -| `sops-age.key.age` | `bootstrap/` と `talos/` の SOPS ファイルを復号する age 秘密鍵。**同じパスフレーズ**で暗号化 | +| `sops-age.key.age` | `bootstrap/` の SOPS ファイルを復号する age 秘密鍵。**同じパスフレーズ**で暗号化 | | [pv.sh](pv.sh) | PV のデータを k8up の `Restore` で戻す道具(下の 8) | | [infisical.sh](infisical.sh) / [infisical-ca.mjs](infisical-ca.mjs) | Infisical を restic から直接戻す道具(下の 6・7) | @@ -33,8 +33,8 @@ base64 recovery/env.age recovery/sops-age.key.age # 印刷して封筒へ | 層 | 何で戻すか | | --- | --- | -| ホストの設定 | **machine config**。`talos/render.sh` が repo から描く(`talos/secrets.yaml` が同じなら同じクラスタ CA) | -| k8s オブジェクト | **git から Argo CD で再構築**。`bootstrap/` は machine config と Argo CD(Application `bootstrap`)が当てる | +| ホストの設定 | **machine config**。`bootstrap/render.sh` が repo から描く(`bootstrap/talos-secrets.sops.yaml` が同じなら同じクラスタ CA) | +| k8s オブジェクト | **git から Argo CD で再構築**。`bootstrap/` は machine config が当てる | | Infisical の DB | **手元の restic で直接**(k8up の資格情報 `k8up-global` が Infisical から来るので、k8up では戻せない) | | ほかの PV データ | **k8up の `Restore`**([pv.sh](pv.sh)) | @@ -45,7 +45,7 @@ git に無い状態まで一度に戻したいときの近道で、主経路で **コマンドは全部 `tools/t.ps1 --admin` で入ったコンテナのシェルで打つ**(`HOME` はリポジトリの `.home/` で、 age 鍵と restic の資格情報はそこに残る。ノードにシェルは無い)。**talosctl / kubectl の資格情報は t.ps1 が用意する** ── -talosconfig も管理者の kubeconfig も `talos/secrets.yaml` の CA からその場で作る(ファイルでは持たない) +talosconfig も管理者の kubeconfig も `bootstrap/talos-secrets.sops.yaml` の CA からその場で作る(ファイルでは持たない) ([../tools/README.md](../tools/README.md)「クラスタの資格情報」)。GitHub の操作(5)だけ手元の `gh`。 - **作業用 PC(J:COM 側の LAN)からは 10.10.0.4(eno4)にしか届かない**(10.0.0.2 は届かない)。t.ps1 の行き先はそこに固定してある @@ -57,13 +57,13 @@ talosconfig も管理者の kubeconfig も `talos/secrets.yaml` の CA からそ ```shell mkdir -p ~/.config/sops/age && age -d -o ~/.config/sops/age/keys.txt recovery/sops-age.key.age # パスフレーズ -./talos/render.sh talos/clusterconfig # `valid for metal mode` まで出ること +./bootstrap/render.sh bootstrap/clusterconfig # `valid for metal mode` まで出ること ``` -- **`talos/secrets.yaml` を作り直さない。** 作り直すと別のクラスタ CA になり、`bootstrap/apiserver/ca.crt`・ +- **`bootstrap/talos-secrets.sops.yaml` を作り直さない。** 作り直すと別のクラスタ CA になり、`bootstrap/apiserver-ca.crt`・ Infisical の Kubernetes auth(7)が合わなくなる -- **`render.sh` は出力先を丸ごと消して作り直す。** `talos/clusterconfig/` に kubeconfig などを置いていたら消える -- 版は [../talos/versions.yaml](../talos/versions.yaml)。**上げるなら建て直しの日ではない日に** +- **`render.sh` は出力先を丸ごと消して作り直す。** `bootstrap/clusterconfig/` に kubeconfig などを置いていたら消える +- 版は [../bootstrap/versions.yaml](../bootstrap/versions.yaml)。**上げるなら建て直しの日ではない日に** ## 2. ISO で起動する @@ -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/controlplane.yaml` / `main.yaml` を直して 1 に戻る。**`diskSelector` の先は書いたら消える**): + (違ったら `bootstrap/talos.sops.yaml` / `main.yaml` を直して 1 に戻る。**`diskSelector` の先は書いたら消える**): ```shell IP=<コンソールに出た IP> @@ -89,7 +89,7 @@ talosctl get disks --insecure -n $IP -e $IP ## 3. 当てる(インストールは無人で進む) ```shell -talosctl apply-config --insecure -n $IP -e $IP -f talos/clusterconfig/controlplane.yaml +talosctl apply-config --insecure -n $IP -e $IP -f bootstrap/clusterconfig/controlplane.yaml ``` `UnattendedInstallConfig` があるので、ディスクに書いて自分で再起動する。**ここで SSD が上書きされる。** @@ -109,19 +109,19 @@ kubectl get pods -A # cilium・coredns・local-path・argocd・cert-manag kubectl get gatewayclass # cilium が Accepted(無いと Gateway が `Waiting for controller` のまま) ``` -`talosctl kubeconfig` は使わない(取り消せない 1 年の証明書が呼ぶたびに出る)。t.ps1 が `secrets.yaml` の CA でその場で作る。 +`talosctl kubeconfig` は使わない(取り消せない 1 年の証明書が呼ぶたびに出る)。t.ps1 が `talos-secrets.sops.yaml` の CA でその場で作る。 -## 5. 残りの bootstrap/ が入るのを見る(ArgoCD) +## 5. Gateway・auth などが入るのを見る(ArgoCD) -手ですることは無い。ArgoCD の Application `bootstrap`([../bootstrap/argocd/repos.yaml](../bootstrap/argocd/repos.yaml))が +手ですることは無い。ArgoCD の `gitops` の Application([../bootstrap/argocd/repos.yaml](../bootstrap/argocd/repos.yaml) の ApplicationSet が作る)が `apps/` の `ClusterIssuer`・Gateway・auth・`InfisicalSecret` を当てる。 ```shell -kubectl -n argocd get application bootstrap # InfisicalSecret の CRD が来るまでは何度か落ちて retry で戻る +kubectl -n argocd get application gitops # InfisicalSecret の CRD が来るまでは何度か落ちて retry で戻る kubectl get certificate -A # Gateway に証明書が付く ``` -Argo CD はこの間に `apps/` と各リポジトリの `deploy/` を同期しはじめる。**中身はまだ空**なので、 +Argo CD はこの間に `apps/` の残りと各リポジトリの `deploy/` も同期しはじめる。**中身はまだ空**なので、 Infisical の Secret を待つアプリと、Forgejo から来る非公開アプリ(`repos-forgejo` が `ErrorOccurred`)は上がらない。想定どおり。 ## 6. Infisical の DB を戻す @@ -155,7 +155,7 @@ sh recovery/infisical.sh ca apply # 差し替えて、operator を蹴り直 kubectl get infisicalsecret -A # 全部 Ready になる。k8up/k8up-global もここで入り、k8up が動き出す ``` -`secrets.yaml` を作り直していなければ CA は前と同じなので、`ca` で頭が一致していれば `apply` は要らない。 +`talos-secrets.sops.yaml` を作り直していなければ CA は前と同じなので、`ca` で頭が一致していれば `apply` は要らない。 ## 8. PV データを戻す(k8up) @@ -237,7 +237,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/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 で戻したもの)にする | +| 非公開イメージが引けない | `bootstrap/talos.sops.yaml` の fj.doany.io の `password` が machine config に入っているか、Forgejo が戻っているか(8) | +| bootstrap apply が TLS で落ちる | `talos-secrets.sops.yaml` を作り直した。`bootstrap/apiserver-ca.crt` を新しい `talos-secrets.sops.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)「上げ方 / 当て直し方」) | +| inlineManifest を直したのに効かない | `render.sh` → `apply-config` → **`upgrade-k8s`**([../bootstrap/talos.md](../bootstrap/talos.md)「上げ方 / 当て直し方」) | diff --git a/renovate.json b/renovate.json index 7deba25d..a272973e 100644 --- a/renovate.json +++ b/renovate.json @@ -9,6 +9,12 @@ "**/*.yaml" ] }, + "helm-values": { + "description": "bootstrap/ の chart の値は -values.yaml という名前なので、既定のパターン(values.yaml)に足す。", + "managerFilePatterns": [ + "/^bootstrap/[^/]+-values\\.yaml$/" + ] + }, "customManagers": [ { "description": "tools/Dockerfile の `# renovate:` の次の行の `ARG 名前=版`。managerFilePatterns の正規表現は `/…/` で囲む(囲まないと glob と読まれ何にも当たらない)。", @@ -21,14 +27,14 @@ ] }, { - "description": "versions.yaml / version.yaml の `# renovate:` の次の行の version。", + "description": "bootstrap/ の `# renovate:` の次の行の version。versions.yaml は `version: 版`、chart の -values.yaml は値を邪魔しないようコメントの `# version: 版`(どちらも render.sh が読む)。", "customType": "regex", "managerFilePatterns": [ - "/^talos/versions\\.yaml$/", - "/^bootstrap/[^/]+/version\\.yaml$/" + "/^bootstrap/versions\\.yaml$/", + "/^bootstrap/[^/]+-values\\.yaml$/" ], "matchStrings": [ - "#\\s*renovate:\\s*datasource=(?\\S+)\\s+depName=(?\\S+)(?:\\s+registryUrl=(?\\S+))?\\s*\\n\\s*version:\\s*(?\\S+)" + "#\\s*renovate:\\s*datasource=(?\\S+)\\s+depName=(?\\S+)(?:\\s+registryUrl=(?\\S+))?\\s*\\n\\s*(?:#\\s*)?version:\\s*(?\\S+)" ] }, { @@ -142,13 +148,13 @@ ] }, { - "description": "values.yaml で上書きしているイメージも chart と同じ扱い(machine config に載る)。", + "description": "-values.yaml で上書きしているイメージも chart と同じ扱い(machine config に載る)。", "matchDatasources": [ "docker" ], "matchFileNames": [ - "bootstrap/argocd/values.yaml", - "bootstrap/infisical/values.yaml" + "bootstrap/argocd-values.yaml", + "bootstrap/infisical-values.yaml" ], "groupName": "bootstrap charts", "groupSlug": "bootstrap-charts", diff --git a/talos/add-namespace.awk b/talos/add-namespace.awk deleted file mode 100644 index 02463e6a..00000000 --- a/talos/add-namespace.awk +++ /dev/null @@ -1,31 +0,0 @@ -# helm の描き出しに namespace を補う(chart によっては書かず、Talos の適用では default に落ちる)。 -# awk -v ns=infisical -f add-namespace.awk < helm-output.yaml -BEGIN { - # クラスタスコープのものには足さない(足すと API が弾く) - split("Namespace ClusterRole ClusterRoleBinding CustomResourceDefinition " \ - "ValidatingWebhookConfiguration MutatingWebhookConfiguration APIService " \ - "StorageClass PriorityClass RuntimeClass IngressClass PersistentVolume " \ - "ClusterIssuer CiliumClusterwideNetworkPolicy", a, " ") - for (i in a) cluster[a[i]] = 1 - n = 0 -} -/^---$/ { flush(); print; next } -{ buf[n++] = $0 } -END { flush() } - -function flush( i, kind, mi, hasns, inmeta) { - if (n == 0) return - kind = ""; mi = -1; hasns = 0; inmeta = 0 - for (i = 0; i < n; i++) { - if (buf[i] ~ /^kind: /) { kind = substr(buf[i], 7) } - if (buf[i] ~ /^metadata:$/) { mi = i; inmeta = 1; continue } - if (inmeta && buf[i] ~ /^[^ ]/) inmeta = 0 - if (inmeta && buf[i] ~ /^ namespace:/) hasns = 1 - } - for (i = 0; i < n; i++) { - print buf[i] - if (i == mi && !hasns && kind != "" && !(kind in cluster)) - print " namespace: " ns - } - n = 0 -} diff --git a/tools/README.md b/tools/README.md index 4dafb5f3..8b5c3019 100644 --- a/tools/README.md +++ b/tools/README.md @@ -5,7 +5,7 @@ ```powershell tools/t.ps1 cf r2 buckets list -tools/t.ps1 sops -d bootstrap/infisical/secrets.sops.yaml +tools/t.ps1 sops -d bootstrap/infisical-secrets.sops.yaml $env:INFISICAL_PROFILE = 'info@doany.io--doa'; tools/t.ps1 infisical secrets --env prod --path /matrix/matrix tools/t.ps1 kubectl get pods -A # クラスタは読み取り用で繋ぐ tools/t.ps1 --admin kubectl apply -f x.yaml # 書くときだけ管理者(下の「クラスタの資格情報」) @@ -15,7 +15,7 @@ tools/t.ps1 # 引数なしならシェルに入る | 入っているもの | 用途 | | --- | --- | | cf | Cloudflare(R2 など) | -| sops / age | `bootstrap/` と `talos/` の暗号化したファイル | +| sops / age | `bootstrap/` の暗号化したファイル | | infisical | 秘密の読み書き | | kubectl / talosctl / helm | クラスタ(資格情報は下の「クラスタの資格情報」) | | restic | R2 のバックアップの中身を見る・戻す | @@ -54,16 +54,16 @@ tools/t.ps1 # 引数なしならシェルに入る | | 読み取り用(既定) | 管理者(`--admin`) | | --- | --- | --- | -| kubectl / helm | その場で作る。`talos/secrets.yaml` の Kubernetes の CA で、グループ `entra:viewer` の証明書(1 日で切れる) | 同じく `system:masters` | +| kubectl / helm | その場で作る。`bootstrap/talos-secrets.sops.yaml` の Kubernetes の CA で、グループ `entra:viewer` の証明書(1 日で切れる) | 同じく `system:masters` | | talosctl | その場で作る。Talos の CA で `os:reader` の証明書(1 日で切れる) | 同じく `os:admin` | -- 読み取り用の権限は [`../bootstrap/apiserver/rbac.yaml`](../bootstrap/apiserver/rbac.yaml) の `entra:viewer` +- 読み取り用の権限は [`../bootstrap/apiserver-rbac.yaml`](../bootstrap/apiserver-rbac.yaml) の `entra:viewer` (組み込みの `view` と `readonly-extra`。ログは読めて、Secret は読めない)。Headlamp で Entra の viewer が持つものと同じ - **helm はリリースの情報を Secret に持つ**ので、`helm list` や `helm status` も `--admin` を付ける - **talosconfig は保存しない**([../docs/decisions.md](../docs/decisions.md)「手で持つもの」)。`secrets.yaml` から毎回作る - **管理者の kubeconfig も保存しない。** 毎回その場で作るので、作り直しも期限切れも無い。 `talosctl kubeconfig` は 1 年もつ証明書を呼ぶたびに出す(取り消せない)ので使わない -- 行き先はどれも `10.10.0.4`(ノードの、作業用 PC の LAN 側の口。`creds.sh` の `ep`)。`talos/render.sh` の `10.0.0.2` は +- 行き先はどれも `10.10.0.4`(ノードの、作業用 PC の LAN 側の口。`creds.sh` の `ep`)。`bootstrap/render.sh` の `10.0.0.2` は クラスタの LAN で手元から届かず、NetBird はクラスタの上で動くので落ちたときに使えない。API は外に開かない。 外からは mado で作業用 PC に入って打つ。`10.10.0.4` は Talos の証明書の SAN に入っている。`KUBECONFIG` / `TALOSCONFIG` や `--kubeconfig` / `--talosconfig` / `--server` を自分で渡したときは何もしない。繋がないもの(`kubectl kustomize`、`helm template`、`talosctl gen`、`--client` など)も同じ diff --git a/tools/creds.sh b/tools/creds.sh index b0d3a6a5..a749602e 100755 --- a/tools/creds.sh +++ b/tools/creds.sh @@ -1,5 +1,5 @@ #!/bin/sh -# kubectl / talosctl / helm の入口(tools/README.md「クラスタの資格情報」)。繋ぐときだけ talos/secrets.yaml の CA で +# kubectl / talosctl / helm の入口(tools/README.md「クラスタの資格情報」)。繋ぐときだけ bootstrap/talos-secrets.sops.yaml の CA で # 1 日で切れる証明書を作り、/dev/shm(tmpfs)に置いて渡す。平文はディスクに書かない。 # 既定は読み取り用。管理者は `tools/t.ps1 --admin …`(GITOPS_ADMIN=1)のときだけ。 set -eu @@ -52,7 +52,7 @@ mint() { set -e umask 077 for k in crt key; do - sops -d --extract "[\"certs\"][\"$1\"][\"$k\"]" talos/secrets.yaml >"$w/ca.$k.b64" + sops -d --extract "[\"certs\"][\"$1\"][\"$k\"]" bootstrap/talos-secrets.sops.yaml >"$w/ca.$k.b64" base64 -d "$w/ca.$k.b64" >"$w/ca.$k" done # Talos の ed25519 は独自の見出し。中身は PKCS#8 なので見出しだけ直す @@ -72,7 +72,7 @@ mint() { rc=$? set -e rm -rf "$w" - [ $rc = 0 ] || { rm -f "$3".*; die "証明書を作れなかった(talos/secrets.yaml を age の鍵で開けるか)"; } + [ $rc = 0 ] || { rm -f "$3".*; die "証明書を作れなかった(bootstrap/talos-secrets.sops.yaml を age の鍵で開けるか)"; } } # 作業用 PC の LAN 側のノードの口(10.0.0.2 は手元から届かず、NetBird はクラスタが落ちると使えない) @@ -103,7 +103,7 @@ if [ ! -s "$out" ]; then # talosctl kubeconfig は 1 年もつ証明書を出すので使わない subj=/O=system:masters/CN=tools-admin else - # Headlamp の Entra の viewer と同じ権限(bootstrap/apiserver/rbac.yaml) + # Headlamp の Entra の viewer と同じ権限(bootstrap/apiserver-rbac.yaml) subj=/O=entra:viewer/CN=tools-readonly fi mint k8s "$subj" "$out.c.$$" ec diff --git a/tools/t.ps1 b/tools/t.ps1 index 30c0ae2d..e4ce8a25 100644 --- a/tools/t.ps1 +++ b/tools/t.ps1 @@ -1,6 +1,6 @@ # 運用のコマンドを wslc (WSL コンテナ) で動かす。中身は compose.yaml の tools を wslc-compose run する。例: # tools/t.ps1 infisical login --domain https://il.doany.io/api -# tools/t.ps1 sops -d bootstrap/infisical/secrets.sops.yaml +# tools/t.ps1 sops -d bootstrap/infisical-secrets.sops.yaml # tools/t.ps1 kubectl get pods -A # クラスタは読み取り用で繋ぐ # tools/t.ps1 --admin kubectl apply -f x # 書くときだけ管理者 (tools/README.md「クラスタの資格情報」) # 引数が無ければシェルに入る。Dockerfile を変えたら次の実行で作り直す (変わっていなければキャッシュで一瞬)。