Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,19 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble
| [`cloudflare-ddns/`](apps/cloudflare-ddns/) | DDNS |
| [`external-dns/`](apps/external-dns/) | ExternalDNS。**Cloudflare を通すアプリの DNS を、アプリの HTTPRoute の注釈から作る**(gitops には書かない)。直接つなぐアプリはワイルドカードで引ける |
| [`mta-sts/`](apps/mta-sts/) | MTA-STS の方針ファイル(`mta-sts.doany.io`)。DANE・TLS-RPT と合わせて、doany.io 宛てのメールのサーバー間の TLS を守る |
| [`matrix/`](apps/matrix/) | Matrix のサーバー一式(`m.doany.io`。Web 版の Element は `e.doany.io`、スマホと PC は公式のアプリ)。Tuwunel + hookshot(通知の受け口)+ LiveKit(通話。`lk.doany.io`、hostPort 8443 をルーターで転送)。**Zulip からの移行先。使い始めは [apps/matrix/README.md](apps/matrix/README.md)** |
| [`matrix/`](apps/matrix/) | Matrix のサーバー一式(`m.doany.io`。Web 版の Element は `e.doany.io`、スマホと PC は公式のアプリ)。Tuwunel + hookshot(通知の受け口)+ LiveKit(通話。`lk.doany.io`、hostPort 8443 をルーターで転送)。**使い始めは [apps/matrix/README.md](apps/matrix/README.md)** |
| [`forgejo/`](apps/forgejo/) | Forgejo(`fj.doany.io`、git + Actions)+ PostgreSQL + Runner(dind)。GitHub Actions の課金を避けて CI をここで回すため。**使い始めの手順は [apps/forgejo/README.md](apps/forgejo/README.md)** |
| [`netbird/`](apps/netbird/) | NetBird (VPN。combined コンテナ + 内蔵 IdP) |
| [`3proxy/`](apps/3proxy/) | 3proxy (国内IP経由の HTTPS フォワードプロキシ) |
| [`k8up/`](apps/k8up/) | バックアップ(restic → Cloudflare R2)。PVC・DB のダンプ・etcd のスナップショット |
| [`infisical/`](apps/infisical/) [`infisical-operator/`](apps/infisical-operator/) [`infisical-push-bridge/`](apps/infisical-push-bridge/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ |
| [`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`、Portainer の置き換え)。認証は Entra、権限は `bootstrap/apiserver/` |
| [`headlamp/`](apps/headlamp/) | Kubernetes の Web UI(`h.doany.io`)。認証は Entra、権限は `bootstrap/apiserver/` |
| [`rybbit/`](apps/rybbit/) | Rybbit(`rt.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** |

**動かしていないもの**は `apps/_<name>/` に置いてある。`_` で始まるディレクトリは Argo CD が同期しない
([bootstrap/argocd/repos.yaml](bootstrap/argocd/repos.yaml) の `directory.exclude`。全リポジトリ共通の決まり)ので、
クラスタには何も作らない。動かすときは `git mv apps/_<name> apps/<name>`。いまは無い
(GlitchTip を置いていたが、動かさないまま 2026-10-17 に消した。戻すなら git の履歴から)。
クラスタには何も作らない。動かすときは `git mv apps/_<name> apps/<name>`。いまは無い。

**アプリの多くはこのリポジトリに無い。** 各アプリのリポジトリの `deploy/` に置いてあり、
`bootstrap/argocd/repos.yaml` の ApplicationSet が拾う(上の「仕組み」)。
Expand Down
25 changes: 5 additions & 20 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@

## いまここ

**2026-10-17 に DL360 の上で Ubuntu + k3s から Talos(v1.15.0-alpha.1 / Kubernetes v1.37.1)に移した。**
git から建て直して restic から戻し、全部のアプリが動いている。**次は Phase 2.5(Talos のまま HP Z440 へ移す)。**
**DL360 の上の Talos で全部のアプリが動いている。次は Phase 2.5(Talos のまま HP Z440 へ移す)。**
順番の理由は [docs/decisions.md](docs/decisions.md)「Z440 へは Talos のまま移す」。

**実機の RAM は 2026-10-17 から RDIMM 6 枚中 5 枚(40GB)。** 1 枚は Z440 の BIOS 更新に貸している。

## Phase 2.5 — HP Z440 へ移す(Talos のまま)

**手順と確かめることは [docs/z440-migration.md](docs/z440-migration.md)。** 箱の違いは schematic と NIC の名前に閉じ込め、
Expand All @@ -23,25 +20,13 @@ bond0 の MAC は設定で固定してあるのでルーターは触らない。

## Phase 3 — Talos 定常運用

- [ ] **R2 の容量が下がり切るのを見る。** `host=main` を消した直後の restic の実データは **5.60 GiB**(圧縮前 8.52 GiB、
480 スナップショット。2026-10-17)。denpa の録画の forget([apps/k8up/denpa-encoded-forget.yaml](apps/k8up/denpa-encoded-forget.yaml))は
ホストのスナップショットが同じ中身を抱えていたので効いていなかった。日曜の prune のあと、`pulumi` ワークフローの週次の Summary で見る
- [ ] **R2 の容量が下がり切るのを見る。** `host=main` を消した直後の restic の実データは **5.60 GiB**(2026-10-17)。
日曜の prune のあと、`pulumi` ワークフローの週次の Summary で見る
- [ ] **Talos 1.15.0 の正式版が出たら乗り換える**(Renovate の PR)。PT3 のモジュールの読み順を守る上流の修正
([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))が入ったら、[apps/pt3/rebind.yaml](apps/pt3/rebind.yaml) を外して
`talos/patches/main.yaml` の並び順だけにする。入ったら dvb-pt3 の extension の README も直す(上流)

## 済んだこと(記録)

1 行ずつ。詳しいことはリンク先にある。

- **Talos へ移した(2026-10-17)。** git から建て直し、PV を restic から戻した。踏んだ穴は直した場所と [recovery/README.md](recovery/README.md) に
- **後片付け(2026-10-17)。** Ubuntu 期のホストのスナップショット(`host=main` 10 本)を forget + prune。etcd の日次スナップショットを
R2 へ([apps/k8up/etcd-snapshot.yaml](apps/k8up/etcd-snapshot.yaml)、最初の 1 本 `dacc947b` を手で回して確認)
- **四半期ごとの手動の復元リハーサルをやめて自動に(2026-10-17)。** データは月次の CronJob が最新のスナップショットを取り出して開く
([apps/k8up/restore-check.yaml](apps/k8up/restore-check.yaml)。SQLite の integrity_check、pg_dump を使い捨ての postgres に流す、tar の中身。
初回 27 件すべて OK)。ホストの設定は [.github/workflows/talos-boot.yml](.github/workflows/talos-boot.yml) が描いた machine config を QEMU で起動する
- **バックアップを restic + k8up に(2026-09-06〜09-08)。** R2 の `doany-restic` へ日次、論理バックアップと PVC の注釈、失敗通知、`restic check`([apps/k8up/README.md](apps/k8up/README.md))。R2 のずれ検知(2026-10-05)
- **Cilium + Gateway API に(2026-09-06〜07)。** **Talos で後から替えるのが高いのは CNI だけ**なので先に。decisions.md「ルーティングの選定」
- **Talos の検証(2026-09-06〜10-17)。** QEMU で 7 回、いまは PR ごとに自動([docs/talos.md](docs/talos.md)「VM ブートドリル」)
- **運用まわり(2026-09-07〜)。** Cilium の値を git に、Entra をアプリロールに([docs/entra.md](docs/entra.md))、自動マージの範囲(decisions.md「依存の更新をどこまで自動で入れるか」)、Talos の更新は検知だけ自動([talos/versions.yaml](talos/versions.yaml))、通信の可視化は Hubble(`hl.doany.io`)
- **PVC を守る仕掛けをやめた(2026-09-07、`local-path-retain` の撤去は 2026-10-05)** ── decisions.md「PVC を守る仕掛けをやめた」
- **Talos へ移した(2026-10-17)。** git から建て直し、PV を restic から戻した([recovery/README.md](recovery/README.md))
- **バックアップと復元の確認を自動に。** restic + k8up で日次、etcd も日次、月次の取り出し確認、machine config は PR ごとに QEMU で起動([apps/k8up/README.md](apps/k8up/README.md))
15 changes: 1 addition & 14 deletions apps/external-dns/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# external-dns

**Cloudflare を通すアプリの DNS レコードを、アプリの HTTPRoute から作る**([ExternalDNS](https://github.com/kubernetes-sigs/external-dns)。2026-10-06)。
**Cloudflare を通すアプリの DNS レコードを、アプリの HTTPRoute から作る**([ExternalDNS](https://github.com/kubernetes-sigs/external-dns))。

## 方針

Expand Down Expand Up @@ -33,16 +33,3 @@ ExternalDNS は**自分が作ったレコードにしか触れない**。作っ
Cloudflare の Account API token「external-dns (doany.io DNS write)」。権限は **`doany.io` のゾーンだけの Zone Read + DNS Write**。
Infisical `/external-dns/external-dns` の `cloudflare-api-token`。

## 移した記録

| ホスト | アプリ | 移した日 |
| --- | --- | --- |
| `tk.doany.io` | todoroku | 2026-10-06(todoroku#343) |
| `l.doany.io` | lgtm | 2026-10-06(lgtm#61) |
| `x.doany.io` | xool | 2026-10-06(xool#164) |
| `y.doany.io` | yuzuriha | 2026-10-06(yuzuriha#20) |
| `ts.doany.io` | tamasagashi | 2026-10-06(tamasagashi#100) |
| `w.doany.io` | worklog | 2026-10-06(worklog-cloud#143) |

**手で作った proxied の CNAME はもう無い。** Cloudflare を通すアプリを足すときは、アプリのリポジトリの HTTPRoute に注釈を付けるだけでよい。

38 changes: 13 additions & 25 deletions apps/forgejo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
- `/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}`(値は写さず参照)
2. Entra のアプリ登録 Main のリダイレクト URI(Web)に `https://fj.doany.io/user/oauth2/entra/callback` ── **2026-09-15 に `az ad app update` で追加済み**
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 でログインしたときに自動で紐づいて管理者になる。
パスワードは捨てる(表示させない)。組織 `doa` とチーム `members`(読み取り・すべてのリポジトリ)も作る
Expand All @@ -39,23 +39,15 @@

## プライベートのリポジトリを移す(1 本ずつ)

**方針: プライベートは GitHub に置かない**(2026-09-15)。GitHub Actions の課金はプライベートにだけ掛かるので、
公開リポジトリ(gitops・blog など)は GitHub のままでよい。対象は todoroku / tamasagashi / worklog-cloud(noren は閉じた)。
**方針: プライベートは GitHub に置かない。** GitHub Actions の課金はプライベートにだけ掛かるので、
公開リポジトリ(gitops・blog など)は GitHub のままでよい。

**Forgejo が唯一の置き場になる。** バックアップは k8up(R2)の 1 日 1 回だけなので、手元の clone も捨てないこと。

先に 1 回だけ:

1. Forgejo に組織 `doa` を作る
2. ArgoCD 用のアクセストークンを作り(`read:repository` と `read:organization`)、Infisical `/argocd/forgejo-repo-creds` に
`url` = `https://fj.doany.io/doa` / `username` / `password` = トークン で入れる([argocd-creds.yaml](argocd-creds.yaml))。
**スコープは repository・organization・issue の読み取り。** issue が無いと、非公開リポジトリができた時点で
ApplicationSet が `token does not have at least one of required scope(s): [read:issue]` で止まる(2026-09-15)
3. `bootstrap/argocd/repos.yaml` に Forgejo の generator を足す PR をマージする(**Forgejo とトークンが揃ってから**。
API に届かないと ApplicationSet 全体の生成が止まる)。
**2026-10-05 に `repos-forgejo` という別の ApplicationSet に分けた。** 同居していると、まっさらな
クラスタ(Forgejo が空で、トークンもまだ無い)で GitHub 側の Application まで 1 本も作られず、
Forgejo 自身もデプロイされないので抜けられなかった。いまは止まるのは Forgejo 側だけ
ArgoCD が読むトークンは Infisical `/argocd/forgejo-repo-creds`([argocd-creds.yaml](argocd-creds.yaml))。
**スコープは repository・organization・issue の読み取り。** issue が無いと ApplicationSet が
`token does not have at least one of required scope(s): [read:issue]` で止まる。
ApplicationSet は GitHub 側と別の `repos-forgejo`(同居させると、Forgejo が空のクラスタで GitHub 側まで 1 本も作られない)。

リポジトリごとに:

Expand All @@ -78,7 +70,7 @@
| テナントの人(Entra、チーム members) | 見える | 読める | できる | できない | ─ |
| 管理者(Forgejo の管理画面で付ける) | 全部 | 全部 | できる | できる | できる |

- **テナントのゲスト(もとは ERPNext のために招いた人)も tid が同じなので members に入る。** 外したくなったら Forgejo 専用のアプリ登録を作り、
- **テナントのゲストも tid が同じなので members に入る。** 外したくなったら Forgejo 専用のアプリ登録を作り、
「割り当てが必要」にして人ごとに割り当てる(Main の設定を変えると、ロールを割り当てていない人が Main の後ろのアプリ全部から締め出される。
[../../docs/entra.md](../../docs/entra.md))
- **Entra のアプリロール admin は Forgejo の管理者に連動しない**(グループのクレームを tid に使っているため)。管理者は管理画面で付け外しする
Expand All @@ -95,7 +87,7 @@

**公開にしてはいけない。** パッケージ(コンテナイメージ)の読み取り権限はリポジトリではなく**組織の公開範囲**で決まり、
公開の組織だと非公開リポジトリのイメージでも匿名で pull できる(`services/packages/perm.go` の
`HasOrgOrUserVisible`。2026-09-15 に shadai(今の todoroku)のイメージが匿名で取れるのを見つけて limited にした)。
`HasOrgOrUserVisible`)。

## Actions からイメージを push するとき

Expand Down Expand Up @@ -124,18 +116,18 @@ CI から Forgejo の API・git・レジストリに**長く生きる秘密な
- **リポジトリを絞ると**、スコープは repository / issue しか選べず、管理者の権限は効かない(`services/authz/access_token.go`)。
レジストリ(package)と組織の操作は「すべて」で作る
- **Forgejo Actions のジョブは `enable-openid-connect: true`** で JWT を取る。fork からの PR では取れない。
JWT の `workflow` は `name:` ではなく**ファイル名**(`run.WorkflowID`。2026-10-07 に実ジョブの JWT で確かめた。GitHub とは違う)。
JWT の `workflow` は `name:` ではなく**ファイル名**(`run.WorkflowID`。GitHub とは違う)。
再利用ワークフロー(`uses: ./...`)のジョブは、**呼ばれる側**のファイルの設定が効き、JWT の `workflow` は**呼ぶ側**のファイル名になる
- **境界は main に書ける人**: main のワークフローを書き換えられる人は、その JWT で持ち主として書ける(シークレットのときと同じ)。
doa の main に書けるのは bots と Owners だけ(members は読み取り)

| 状態 | 持ち主 | 名前 | 発行者 | クレームのルール | 範囲・スコープ | aud の置き場 | 代わりに消したもの |
| --- | --- | --- | --- | --- | --- | --- | --- |
| **使っている**(2026-10-07) | yui | repo-config | GitHub Actions | GitHub の 5ym/repo-config、`refs/heads/main`、`forgejo-settings.yml`、push / schedule / workflow_dispatch | すべて・`write:organization` `write:repository` | GitHub の 5ym/repo-config の変数 `FORGEJO_AUDIENCE` | repo-config の secrets `FORGEJO_TOKEN` と yui のトークン `repo-config-settings` |
| **使っている** | yui | repo-config | GitHub Actions | GitHub の 5ym/repo-config、`refs/heads/main`、`forgejo-settings.yml`、push / schedule / workflow_dispatch | すべて・`write:organization` `write:repository` | GitHub の 5ym/repo-config の変数 `FORGEJO_AUDIENCE` | repo-config の secrets `FORGEJO_TOKEN` と yui のトークン `repo-config-settings` |
| 見送り | forgejo-bot | doa-registry | Forgejo Actions | doa の todoroku・tamasagashi・worklog-cloud、`refs/heads/main`、`publish.yml` / `fetch-latest.yml`、push / workflow_dispatch / schedule | すべて・`write:package` | 組織 doa の変数 `REGISTRY_AUDIENCE` | (今は組織の `REGISTRY_TOKEN`) |
| 見送り | forgejo-bot | todoroku-deploy | Forgejo Actions | todoroku、`refs/heads/main`、`publish.yml`、push / workflow_dispatch | todoroku だけ・`write:repository` | todoroku の変数 `DEPLOY_AUDIENCE` | (今は todoroku の `DEPLOY_TOKEN`) |

**forgejo-bot の 2 つは見送った**(2026-10-07)。forgejo-bot のものは 16.0.5 では画面にも CLI・API にも一覧・変更・削除の手段が無く
**forgejo-bot の 2 つは見送った。**forgejo-bot のものは 16.0.5 では画面にも CLI・API にも一覧・変更・削除の手段が無く
(forgejo-bot は画面にログインできない。`ENABLE_INTERNAL_SIGNIN: "false"`)、止めるには DB の `authorized_integration` の行を触ることになる。
v17 以降で管理の手段が揃ったら、表のルールで `forgejo admin user create-authorized-integration --username forgejo-bot …` を本番で打ち、
出力の `audience`(`u:<ユーザー ID>:<UUID>`。秘密ではない)を表の変数に入れて、ワークフローの PR
Expand All @@ -147,9 +139,7 @@ yui のもの(repo-config)は画面(設定 → 承認済みインテグレーシ

## ノードからイメージを取る

ノードの名前解決で fj.doany.io をノード自身の Gateway に向ける(`10.0.0.2 fj.doany.io`。`talos/patches/cluster.yaml` の
`StaticHostConfig`。公開 DNS の A にはノードから届かない)+ `talos/registries.yaml` の資格情報
(yui の `read:package`、`talos-registry-pull`)。アプリ側に `imagePullSecrets` は要らない([../../talos/README.md](../../talos/README.md)「fj.doany.io の資格情報」)。
アプリ側に `imagePullSecrets` は要らない([../../talos/README.md](../../talos/README.md)「fj.doany.io の資格情報」)。

## マージはスカッシュ

Expand All @@ -161,5 +151,3 @@ curl -X PATCH -H "Authorization: token $T" -H 'Content-Type: application/json' \
-d '{"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_fast_forward_only_merge":false,"allow_squash_merge":true,"default_merge_style":"squash","default_delete_branch_after_merge":true}' \
https://fj.doany.io/api/v1/repos/doa/<name>
```

2026-09-15 に shadai(今の todoroku)/ renovate / tamasagashi / worklog-cloud へ当てた。
Loading
Loading