Changelog
版本号格式与发布流程见 版本管理。新条目置顶,标题即 git 标签名。
v2026.10.30
把 4 台节点的 IPv6 身份(node-ip 的 IPv6、Node 的 IPv6 InternalIP、CiliumNode
地址、hostNetwork Pod 的 IPv6 endpoint)从 ISP 下发的 GUA 改为固定站点 ULA
fdb5:92d0:b067:1::/64。前一轮的 240e: 前缀轮换
(240e:3a3:20ba:ae43::/64 → 240e:3a3:2002:6440::/64)暴露了这个问题:kubelet
每 10 秒报 failed to validate secondaryNodeIP,Cilium agent 把旧地址写进
CiliumNode,auto-direct-node-routes 一条跨节点 IPv6 路由都装不出来 ——
跨节点 IPv6 pod 通信实际是断的,只是没人踩到。
变更
feat(metal/inventories):新增dualstack_ipv6_node_cidr: fdb5:92d0:b067:1::/64与每台 host 的node_ipv6(::11~::14)。feat(metal/prerequisites):渲染/etc/netplan/60-homelab-ula.yaml(0600,只写 ULA)并netplan apply。netplan 的addresses是追加语义,cloud-init 的 IPv4 与dhcp6: yes不受影响。refactor(metal/roles/k3s):删除「运行时探测主接口第一个 GUA」的两个 task,改为 断言 inventory 的node_ipv6已在接口上,缺地址直接失败(不静默降级)。feat(metal/roles/pxe_server):新装机器的 netplanaddresses直接带 ULA。test:新增TestNodeAddressing(集群验收:每节点只有一个 IPv6 InternalIP, 且等于地址规划的 ULA)。docs:重写 Networking 参考页(地址规划 / 为什么不用 GUA / 路由器无需改动);新增 节点 IPv6 前缀漂移 排障页。
线上状态
- 4 台 Node 的 IPv6 InternalIP 依次为
::11/::12/::13/::14,全部 Ready。 - 4 台 k3s journal 的
secondaryNodeIP计数为 0;cilium-dbg node list的 IPv6 列为 各自 ULA。 ip -6 route出现 3 条远端 Pod/64直连路由,下一跳是对方节点 ULA;跨节点 IPv6 pod 连通实测通过(cheshi-0 → jumper-1)。- hostNetwork 的 semaphore IPv6 EndpointSlice 自动更新为
fdb5:92d0:b067:1::12。 - 三个业务命名空间保持 Running,lobe / casdoor / semaphore HTTP 可用。
- 路由器(192.168.3.1)未做任何改动:节点间 IPv6 走
br-lan二层转发,路由器 既没有也不需要该/64的地址或路由(只读核查:无 ULA 配置、无该网段路由、 无邻居表项)。
v2026.10.29
修复内核升级到 7.0.0-42 后 rsshub、lobe-chat、semaphore 三个命名空间的
Pod CrashLoop。表象是应用连不上数据库,实际是数据库容器起不来:内核
7.0.0-42 下 Ubuntu 的 kernel.apparmor_restrict_unprivileged_unconfined=1 会把容器
profile 转换改成 stacking(标签 cri-containerd.apparmor.d//&unconfined),
该标签不匹配 containerd CRI profile 的 signal 规则,导致容器内所有信号投递
返回 EPERM。gosu 用全线程 SIGSETXID 广播做 setgid/setuid,信号被丢弃后
永久自旋在 sched_yield(100% CPU),永远不会 exec 出 mysqld/postgres。
casdoor 报的 no route to host 只是数据库未 Ready 时的下游症状。
修复
fix(metal/prerequisites):sysctl 任务新增kernel.apparmor_restrict_unprivileged_unconfined = 0,落盘/etc/sysctl.d/90-homelab-prerequisites.conf(排序晚于/usr/lib/sysctl.d/10-apparmor.conf,重启后仍生效)。kernel.apparmor_restrict_unprivileged_userns保持1,未关闭 AppArmor 或 k3s 安全特性,也未改动任何工作负载清单。
文档
docs(011):新增 容器内信号被 AppArmor 拒绝 排障指南(症状、判定命令、根因、修复、验证、回退、风险);specs/011-fix-apparmor-signal/补齐 spec/plan/tasks 与质量清单;AGENTS.md当前 Plan 指向specs/011-fix-apparmor-signal/plan.md。
线上状态
- 4 台节点
kernel.apparmor_restrict_unprivileged_unconfined=0、..._userns=1。 - 重建
lobe-chat/{postgresql,casdoor,lobe}、semaphore/{semaphore-mysql,semaphore}、rsshub/database-postgres后,三个命名空间全部 Running;两个 PostgreSQLpg_isready通过、MySQLmysqld is alive;lobe/casdoor/semaphore HTTP 均 200。 - 容器内
kill -TERM兄弟进程恢复为kill_rc=0(此前Permission denied)。 - 已知遗留:
node-ip中 IPv6 前缀随 ISP 轮换过期,kubelet 持续报failed to validate secondaryNodeIP,与本次故障无因果关系,另行处理。
v2026.10.17.2
为 4 台节点启用 Kubernetes 节点交换内存(NodeSwap)。Kubernetes 自 v1.34 起
NodeSwap GA;本集群 k3s v1.36.5+k3s1、cgroup v2、4 台节点均已有 /swap.img,
缺的只是 kubelet 的 memorySwap.swapBehavior(默认 NoSwap,Pod 的
memory.swap.max 恒为 0)。
新增
feat(metal):k3s role 新增 kubelet drop-in90-swap.conf(failSwapOn: false+memorySwap.swapBehavior: LimitedSwap), 写到/var/lib/rancher/k3s/agent/etc/kubelet.conf.d/;该目录由 k3s 以--config-dir加载,k3s 每次启动只重写自己的00-k3s-defaults.conf,不会覆盖 该文件。仅对 Burstable 且 memory request < limit 的非关键 Pod 生效。
文档
docs(010):新增「启用节点交换内存(Node Swap)」how-to,写明前提、LimitedSwap 的适用条件与额度公式、验证命令、回退步骤与风险;AGENTS.md当前 Plan 指向specs/010-node-swap/plan.md。
线上状态
- 4 台节点逐台重启 k3s 后,
/configz均为{'failSwapOn': false, 'memorySwap': {'swapBehavior': 'LimitedSwap'}},节点全部Ready,kubectl top nodes --show-swap正常。 - 测试 Pod 实测:Burstable(request 64Mi / limit 256Mi)的
memory.swap.max=18100224(约 17 MiB);Guaranteed 与 BestEffort 均为0。 验证后已删除测试 Pod。
v2026.10.17
一次性清空 issue 列表(20 个 issue;renovate 常驻看板不计)。主线是安全边界(Grafana
明文口令、公开注册、公开 MAC)、Ansible 可靠性(改配置不生效、必然失败的 role、占位值会被
推到生产)、监控告警的真实性(kor 分级误报、dashboard 过滤、ntfy 丢告警)与CI 门禁
(pre-commit 恒过、测试锁在 EOL 的 nixpkgs)。全部改动经 PR 合并后由 ArgoCD 同步,线上实际
状态变更单列一节。
修复
fix(kor-audit):VolumeAttachment不再无条件判为「A 类可直接批量清理」(改为 C 类,附 CSI 运行时状态说明),RESOURCES未覆盖的 kind 单列U类,不再把「脚本未覆盖」误报成 「集群里已不存在」(#525 #528)。fix(monitoring-system):kor dashboard 的 namespace 变量补上 Custom all value.*——原先 104 条集群级序列(CRD/ClusterRole/StorageClass)在任何过滤了 namespace 的面板里都不可见, 与同页 Sum 面板自相矛盾;KorAuditNoData改用 exporter 扫描存活信号 (absent(...) and on() (max(increase(process_cpu_seconds_total{job="kor-exporter"}[3h])) < 1)), 集群被清理干净后不再持续误报;ntfy 转换器渲染整组告警而不是只发alerts[0],priority 取组内 最高严重度(#526 #527 #548)。fix(kor):镜像钉 digest(与线上容器imageID一致),补runAsNonRoot/只读根文件系统/drop: ALL/seccompProfile,并把「每次升 kor chart 必须逐项对照上游rbac.rules」写进specs/008-kor-monitoring/quickstart.md(#532)。fix(metal):真实值(registries 配置、tailscale 凭据、节点 MAC)移出仓库到 gitignore 的inventories/group_vars/all/zz-secrets.yml,缺值时在写任何配置之前就 assert 失败;k3s 配置/二进制/unit 变更补 handlers 并重启;删除必然失败的prometheus_operator_crdsrole (CRD 真实归属是system/monitoring-system);修好 UFW 的两个恒假条件 (services['ufw.service']、'inactive' is search('active')为真)与 5 个未定义变量; 补no_log/requirements.yml/幂等性/FQCN/tag;关闭${distro_codename}-proposed与 无人值守自动重启(#530 #531 #546)。fix(grafana)+fix(global-secrets):删除 values 里的明文adminPassword,改由 secret-generator 生成grafana.admin、由 ExternalSecret 模板提供admin-user/admin-password;因为 Grafana 只在数据库首次初始化时读该 env,另用一次性 initContainer 把库里仍然有效的旧口令真正轮换掉,验证后即删除脚手架(#533)。fix(gitea)/fix(woodpecker):关闭自助注册与 Web 端开放注册(#550)。fix(backup):Makefile的备份入口改为真实存在的卷(paperless / kanidm / jellyfin,删掉 集群里不存在的 actualbudget),并新增scripts/backup-coverage逐个 PVC 核对 ReplicationSource(#540)。fix(scripts):configure的域名替换覆盖全仓库(167/167 处、66 个文件,去掉不存在的.ci),并修掉git grep对非 ASCII 路径加引号导致的崩溃(#549)。fix(homepage):依赖仓库http://jameswynn.github.io/helm-charts改为 https——提交 Chart.lock 后 ArgoCD 改走helm dependency build,只有这个 http 仓库报no repository definition,app 卡在ComparisonError(#542 的连带回归)。- 更早两处:
fixargocd-repo-server OOMKilled(6b652ef4)、fix(prometheus)NAS 磁盘温度 告警阈值 50→55°C(4a081d78)。
新增
scripts/backup-coverage:巡检「有 PVC 但没有 ReplicationSource」,豁免项必须逐条写明理由。metal/requirements.yml+roles/guard+vars/placeholders.yml+inventories/group_vars/all/zz-secrets.yml.example:把「操作者必须提供的值」集中成一处, 缺值即拒绝执行,而不是拿占位值覆盖生产。- 公共基线:
SECURITY.md、CONTRIBUTING.md、CHANGELOG.md、CODEOWNERS(#551)。 - flake 新增
devShells.ci:只含 CI 静态检查需要的 git/helm/opentofu/pre-commit/shellcheck/ yamllint,避免为跑 hook 拉整个 devShell。 test/README.md:写明哪些测试在 CI 跑、TestSmoke/TestArgoCDCheck属人工验收。
CI 与测试
.woodpecker/static-checks.yaml的pre-commit run补上--all-files:不带它时只检查已 staged 文件,CI 是全新 clone → 所有 hook 跳过并 exit 0,静态检查恒过;镜像从nixery.dev/...换成nixos/nix+devShells.ci(前者缺 shellcheck/opentofu/helm/grep, 且冷镜像会拉取超时——PR #566 的 static-checks 就是这样红的)(#534)。flake.nix/flake.lock从 EOL 的 nixos-24.05 升到 nixos-26.05(kanidm→kanidm_1_11、kube3d→k3d);test/tools_test.go版本区间同步,kubectl 提到 1.36.x 回到线上 v1.36.5+k3s1 的 ±1 minor 窗口;kubectl 改用version --client(原写法会查 API server, 无 kubeconfig 的 CI 必失败);external_test.go显式指定TerraformBinary: "tofu"(#543)。- 仓库一次性通过全量门禁:30 个历史文件的行尾空白/缺失末尾换行、
system/kubevirt/tests/run.py补执行位;.specify/(spec-kit 生成管理)与system/kubevirt/examples/(含 Helm 占位符) 在 hook 配置里排除。
文档
docs(installation):podCIDR 不再写死「网段→节点」映射(该映射由 controller-manager 动态 分配,实测量与节点名顺序不一致),改为让操作者用kubectl get nodes -o custom-columns读取; 文档站只内联脱敏的metal/inventories/prod.yml.example,并用 snippets 白名单保证真实 inventory 无法被内联(#535 #547)。docs(prune-unused-resources):按当次输出重写分级说明,删除按文档执行会误删活跃挂载的那一批 (#525 #529)。docs(metal):记录操作者必须提供的值、为什么、轮换注意事项。- 修
mkdocs --strict因metal/README.md相对链接失败(#569 与 #566 合并后的集成回归),并让mkdocs.yml打开链接校验——原先--strictexit 0 也拦不住真实存在的 3 处链接问题(#551)。
依赖与构建
- 提交 31 个
Chart.lock(不再被 gitignore)与external/.terraform.lock.hcl;本地渲染与集群 恢复一致(styleferry 从 5.0.1 变回与线上相同的 app-template 5.2.1)(#542)。 - 文档工具链改用
requirements.txt单一来源(Dockerfilepip install -r+mkdocs build --strict),.dockerignore排除.venv/site/tmp(构建上下文 373MB → 5.6MB)。 - Renovate 聚合:kube-prometheus-stack v92、renovate v46.341.0 及若干 non-major 依赖;styleferry
1.2.0 → 1.5.0(含风格路由
deepseek-v4-flash→deepseek-flash)。
线上状态变更(ArgoCD 已同步,不在 Git 内)
- Grafana 的 admin 口令已真正轮换:仓库历史里那个口令实测 401,新口令
(
kubectl -n global-secrets get secret grafana.admin -o jsonpath='{.data.password}' | base64 -d) 实测 200;chart 自建的grafanaSecret 已被回收,4 个容器改读grafana-secrets。切换分两阶段 (先生成口令、再切引用),避免了约 20 分钟的 CreateContainerConfigError。 - Gitea 自助注册关闭(注册页显示 "Registration is disabled")、Woodpecker
WOODPECKER_OPEN=false。 - kor exporter 以非 root + 只读根文件系统滚动成功;homepage app 从
ComparisonError恢复Synced/Healthy。
验证
- 与 CI 相同的容器里
nix develop .#ci --command pre-commit run --all-files→ 12/12 通过;nix develop --command make -C test filter=ToolsVersions→ 10/10 通过。 mkdocs build --strictexit 0、0 warning;31/31Chart.lock与同目录Chart.yaml一致。- 全部 ArgoCD app
Synced+Healthy,无异常 Pod;Grafana/Gitea/Woodpecker/文档站均可访问。 - 发版前复核:真实 MAC 与 Grafana 明文口令在
git grep中命中 0。
v2026.10.07.2
接上一版:把双栈的 Service 声明尽可能写进 Git。上一版写入的 12 个是 app-template 系 chart,本次处理原先剩下的 16 个 Tailscale Ingress 后端 Service——其中 9 个成功 声明,7 个经查证在其上游 chart 里根本无法声明,后者的现状被本文档永久记录。
新增
feat(lobe-chat):templates/svc-lobe.yaml、templates/svc-casdoor.yaml加ipFamilyPolicy: PreferDualStack。这两个是自研 chart(kompose 生成、无依赖), 模板在仓库内,直接改。feat(rsshub):templates/service-rsshub.yaml、templates/service-service-rss.yaml同上(自研 chart)。feat(argocd):global.dualStack.ipFamilyPolicy: PreferDualStack。argo-cd 的_helpers.tpl用这个全局键统一注入到它管理的各 Service,实测本次渲染出 9 个 Service 带上该字段(含argocd-server)。这也顺带把argocd-server-metrics等 一并覆盖。feat(gitea):service.http.ipFamilyPolicy(模板gitea/httpService.yaml读它)。 注意gitea.service.ssh也能设,但gitea-ssh不在本次的 Ingress 后端清单内, 未改动。feat(grafana):service.ipFamilyPolicy(模板templates/service.yaml读它)。feat(monitoring-system):prometheus.service.ipDualStack与alertmanager.service.ipDualStack。这两个键有个门:模板整块被{{- if .Values.<c>.service.ipDualStack.enabled }}拦住,只写ipFamilyPolicy不会有任何效果(实测渲染 0 处)。另外 chart 的默认值是ipFamilies: ["IPv6","IPv4"],而ipFamilies不可变、本集群这两个 Service 是 从 IPv4-only 原地转换来的(主族仍是 IPv4),照默认写会被 apiserver 拒绝——所以 显式写成enabled: true+ipFamilies: ["IPv4","IPv6"],渲染结果与线上逐字一致。
文档
docs(009):specs/009-dual-stack-network/runbook.md增补 Service 声明矩阵, 记录这 9 个的声明位置与验证方式,以及下面 7 个的结论与人工补丁命令。
线上数据修复(不在 Git 内)
承接上一版同名小节:以下 7 个 Service 在其上游 chart 的 Service 模板里 完全没有
ipFamilyPolicy字段(已逐个读取上游 chart 源码确认,不是路径没找对), 因此无法通过 values 声明。集群侧它们已是双栈,此处永久记录该现状。
| Service | chart | 结论 |
|---|---|---|
dex/dex |
dex@0.24.1 |
模板未接线 |
helm-dashboard/helm-dashboard |
helm-dashboard@2.0.7 |
模板未接线 |
homepage/homepage |
homepage@2.1.0 |
模板未接线 |
rook-ceph/rook-ceph-mgr-dashboard |
rook-ceph-cluster@v1.20.7 |
模板未接线 |
rustfs/rustfs-svc |
rustfs@1.0.0 |
模板未接线 |
woodpecker/woodpecker-server |
woodpecker@3.7.3 |
模板未接线 |
kube-system/hubble-ui |
cilium@1.20.2(templates/hubble-ui/service.yaml) |
模板未接线 |
要声明它们只能把这些应用从 Helm source 改成 Kustomize + helmCharts 膨胀 + JSON
patch(并给 ArgoCD 开 --enable-helm),而本仓库内并没有 Application/ApplicationSet
清单(只存在于集群侧)——属架构改动,经决定不做。
因此这 7 个若被重建(例如换集群、或有人删掉再让 ArgoCD 重建),会退回单栈。届时的 补救命令是逐个人工打补丁,可重复执行、幂等:
for s in dex/dex helm-dashboard/helm-dashboard homepage/homepage \
rook-ceph/rook-ceph-mgr-dashboard rustfs/rustfs-svc \
woodpecker/woodpecker-server kube-system/hubble-ui; do
kubectl -n "${s%%/*}" patch svc "${s##*/}" \
-p '{"spec":{"ipFamilyPolicy":"PreferDualStack"}}'
done
验证
8 个改动文件,全部用上游真实 chart + 仓库 values 实际渲染验证(把 wrapper 的
嵌套 values 抽出后喂给 chart,逐 Service 核对渲染出的 ipFamilyPolicy /
ipFamilies):argo-cd 9 处、gitea 1 处、grafana 1 处、
kube-prometheus-stack 2 处(prometheus 与 alertmanager,且 ipFamilies 为
["IPv4","IPv6"]);lobe-chat 渲染 7 个 Service 命中 2 个、rsshub 渲染 9 个命中 2 个。
yamllint 全部通过。
v2026.10.07
集群从 IPv4-only 转为 IPv4/IPv6 双栈。这是本仓库历史上第一个需要停机的变更:
k3s 官方明确双栈必须在集群创建时配置,无法在已运行的 IPv4-only 集群上直接启用,
且 k8s 禁止给既有 Node 增加 podCIDR(node updates may not change podCIDR except
from "" to valid),删节点重注册又被 k3s 的 wrangler.cattle.io/managed-etcd-controller
finalizer 挡住——因此走的是「停 etcd → 改三处 k3s 配置 → 原地更新 ServiceCIDR →
换 Cilium IPAM 模式」这条路,全流程先在本地用同版本容器复现验证后才上生产。
地址规划用站点 ULA fdb5:92d0:b067::/48:Pod 10.42.0.0/16 +
fdb5:92d0:b067:4200::/56(每节点 /64),Service 10.43.0.0/16 +
fdb5:92d0:b067:4300::/112。
新增
feat(k3s):cluster-cidr/service-cidr增加 IPv6 段,node-ip与kubelet-arg: node-ip=传双族地址。三条硬约束都写进了注释:逗号后不能有空格 (k3s 用SplitStringSlice切分且不 trim)、三个 flag 必须同时改且地址族形状一致 (只改其一会 fatal)、IPv4 必须在前(etcd 与 apiserver 只用主族)。node-ip之外还要kubelet-arg,是因为本集群disable-cloud-controller: true时 k3s 不会把 node-ip 传给 kubelet(上游注释 "don't assume that dual-stack node IPs are safe"),不显式传则 Node 的InternalIP里没有 IPv6。 刻意不设cluster-dns,保持 k3s 按每个 service-cidr 各派生一个 DNS IP 并把 CoreDNS 置为RequireDualStack的默认行为。feat(k3s):新增导出tailscale_ipv6与node_ipv6的 task;node-external-ip补上 Tailscale IPv6。node_ipv6的取法要排除fe80:与fd7a:115c:a1e0:——Tailscale 自己的 ULA 也会以 global scope 出现在tailscale0上。feat(cilium):ipam.mode由kubernetes改为cluster-pool,并声明双族池 (IPv4 逐条列出那 4 个 /24、mask 24;IPv6 一个 /56、mask 64);新增ipv6.enabled与ipv6NativeRoutingCIDR。必须改 IPAM 模式而不是只开ipv6.enabled:kubernetes(host-scope) 模式下 agent 要求每个启用的地址族都有Node.spec.podCIDRs条目,而如上一段所述既有节点拿不到 IPv6 PodCIDR,结果是 agent 报required IPv6 PodCIDR not available→ panic + CrashLoopBackOff → 全集群 Pod 网络中断。cluster-pool由 operator 自行分配,不依赖该字段。- 12 个 app-template 系 chart 声明
app-template.service.<name>.ipFamilyPolicy: PreferDualStack(excalidraw、jellyfin、ollama、pairdrop、paperless、searxng、speedtest、styleferry、upsnap、kanidm、kube-explorer、semaphore)。这些 Service 在集群侧已是双栈,本次是把它写进 Git 以获得可复现性 ——否则如「线上数据修复」一节所述,Declarative 与线上会静默分叉。
文档
docs(009):新增specs/009-dual-stack-network/,含方案(plan.md)、调研 (research.md)与实施手册(runbook.md)。手册覆盖备份、分阶段步骤与决策点、 逐项验收门、回退方案 R1–R6 与应急预案,并完整记录了 P1/P2 与收尾阶段的实测结果。
线上数据修复(不在 Git 内)
本节记录集群侧无法由 Git 表达的状态,它们不随 tag 回滚,重建集群时需重放。
CiliumNode.spec.ipam.podCIDRs需手工补 IPv6:从kubernetes切到cluster-pool时,节点上残留的该字段(仅 IPv4)会让 operator 静默跳过分配 ——没有日志、没有报错,新 agent 永远等不到 IPv6 PodCIDR,DaemonSet 卡在 2/4。 给 4 个CiliumNode各补上 IPv6 /64 后 30 秒内恢复 4/4。副作用是好的:手工补 而不是删CiliumNode让 operator 重分配,使既有 IPv4 映射逐字节保留,路由器上那 4 条静态路由一条都不用改。- Service 双栈是 opt-in:改
ipFamilyPolicy不会给既有 Service 追溯补发第二个 ClusterIP。本次转换了 28 个 Tailscale Ingress 后端 +kube-dns(它由 k3s 自动置为RequireDualStack),共 29 个 Service 具备 IPv6 ClusterIP;剩余 16 个属上游 chart, 字段路径各异,集群侧已是双栈但尚未写入 Git。 - ArgoCD 对 Service 的
ipFamilyPolicy/clusterIPs做了归一化,视为可忽略差异。 两面影响:恢复 selfHeal 不会回退它们;但改动对 GitOps 是"隐形"的,故必须如上写进 chart values 才不会被重建时丢失。
验证
4 节点均有 IPv6 InternalIP;ServiceCIDR kubernetes 原地更新为
10.43.0.0/16,fdb5:92d0:b067:4300::/112;Pod 双栈覆盖 168/168;
29 个 Service 有 IPv6 ClusterIP;PVC 50/50 Bound;Ceph HEALTH_OK(81 GiB 数据、
4 OSD up/in、81 pgs active+clean,与改造前基线逐项一致);36/36 ArgoCD Application
Synced;无 Pending/异常 Pod。
过程中三个值得记的坑:① 不要用 ping -6 测 ClusterIP——Cilium LB 不转发 ICMP
到 ClusterIP,100% 丢包是正常的,必须用 TCP/UDP;② 不要用 kubectl get nodes -o wide
判断节点双栈——它只渲染一个 InternalIP,要用
-o custom-columns 展开 status.addresses;③ 被 operator 托管的 workload 用
rollout restart 可能被静默撤销(operator 抹掉 restartedAt 注解,StatefulSet
只重建了序号最大的那个就停了),判定方法是看
.spec.template.metadata.annotations,可靠替代是直接 delete pod。
v2026.10.03.4
Grafana 的内存上限一直贴着稳态用量:新增 dashboard 时 provisioner 的瞬时分配就会把 512Mi 顶穿,26 小时内 OOMKilled 重启 7 次。本轮把上限放宽到 1Gi,顺带认领了一条只在 线上存在的插件 env,并清掉 Grafana 存储里一对重复的 dashboard 记录。
修复
fix(grafana):platform/grafana内存上限512Mi -> 1Gi、request254Mi -> 512Mi, chart 按0.9 × limit注入的GOMEMLIMIT随之460MiB -> 921MiB。判据是线上实测: 稳态 RSS 已 ~460Mi(go_memstats_heap_sys_bytes335Mi、heap_released_bytes只有 2Mi),container_memory_working_set_bytes常态 390–440Mi(limit 的 76%–86%); 每次 dashboard 增删或 ArgoCD 同步,sidecar 重写/tmp/dashboards、文件 provisioner 重放全部 dashboard 并重建 unified-storage 的 bleve 搜索索引,working set 一两分钟内 多出 ~100Mi、冲到 500Mi 以上——重启时刻 11:47 / 13:17 / 16:37 都与 dashboard 变更 吻合。告警自带的Node allocated memory 0.00% out of 31382MB allocatable说明这与 节点无关,打出的是容器 cgroup limitfix(grafana):GF_INSTALL_PLUGINS=performancecopilot-pcp-app写进 values.yaml。 它此前只存在于线上 StatefulSet(out-of-band 添加),而 ArgoCD 用 server-side apply, 按 name 合并的列表项多出来的条目不会被 prune,所以应用一直显示 Synced、这条 env 无人 认领。刻意不用 chart 的plugins:键——chart 13.2.5 把它渲染成GF_PLUGINS_PREINSTALL_SYNC(另一个变量),写env映射才能与线上逐字一致: 渲染后的 env 与线上 13 == 13 项相同,剩余差异只有本次调整带来的GOMEMLIMIT
线上数据修复(不在 Git 内)
prometheus.json(kube-prometheus-stack 的 Prometheus / Overview)在 Grafana 存储里
存在两份记录,同为 sourcePath=/tmp/dashboards/prometheus.json、同带 legacy id
777586274930688。文件 provisioner 的保存路径是「按 legacy id 查、且必须恰好 1 条」,
因此每 30s 失败一次:
unexpected number of dashboards for id 777586274930688. found: 2. desired: 1
该面板自 2025-07 起就无法更新(version 停在 1)。/tmp/dashboards/ 目录本身只有一份
文件,重复发生在 Grafana 的存储里。
根因:kube-prometheus-stack 75.12.0 -> 75.13.0 给这个 dashboard 的 JSON 补了写死的
"uid":"9fa0d141-…"(75.12.0 的 prometheus.yaml 里没有 uid 字段)。provisioner 按
uid 识别 dashboard,于是 2025-07-23 新建了一条记录,而 2025-07-18 生成随机 uid
644d6b21-… 的那条留了下来。两条记录的创建时间正好落在两次 chart 发布之后:
75.12.0 发布 2025-07-18T15:00Z -> 记录 2025-07-18T22:13Z;75.13.0 发布
2025-07-22T17:55Z -> 记录 2025-07-23T01:09Z。
处理:删除孤儿记录。Grafana 会拦 provisioned dashboard cannot be deleted,所以先经
dashboard apiserver(/apis/dashboard.grafana.app/v2beta1)清掉它的
grafana.app/managedBy 标记,再 DELETE。删除后下一个 provisioning 周期即保存成功
(面板 version 1 -> 2、updated=2026-10-03T08:50:19Z),报错从 45 分钟 33 次归零,
带该 legacy id 的记录只剩 1 条,dashboard 总数 109 -> 108。PVC 每天由 VolSync 备份
(replicationsource/storage-grafana-0),可回滚。
两点遗留:同名面板还有第三份来自 Git Sync 仓库
(east4ming/homelab-grafana-gitsync
的 k8s/prometheus-overview.json,文件夹 k8s),属另一条 provisioning 通道,本轮未动;
另外上游 chart 今后若再次改这个 dashboard 的 uid,同样的重复会重演,识别信号就是上面
那条 unexpected number of dashboards 报错。
v2026.10.03.3
kor 从「装上」变成「能用」:分级脚本与清理 runbook、exporter 的假阳性过滤、4 条告警 规则;并据此清理了 449 个确认无用的对象。未使用资源指标 925 → 183,全程只读。
新增
- 清理流程(spec-kit
008-kor-monitoring):scripts/kor-audit把 kor 的发现结果 与集群实况(GitOps 归属、ownerReferences)交叉核对,输出 A/B/C 分级。判级的关键是 两条分界线:派生对象(有 owner)只看 owner,声明式对象才看 GitOps 标记—— 486 个 ReplicaSet 里有 258 个继承了 Deployment 的 ArgoCD 注解,按标签判会全部误判 - 清理 runbook:
docs/how-to-guides/prune-unused-resources.md,逐 kind 说明 kor 的 判定语义与假阳性来源、三级清单、备份/删除/误删恢复三阶段,以及 12–24 小时的 etcd 快照恢复窗口 - exporter 假阳性过滤:
--exclude-labels app.kubernetes.io/managed-by=Helm与tailscale.com/managed=true(实测 secret 发现数 62 → 25)。后者是因为 Tailscale operator 会重写 Secret 元数据、把外部打的kor/used=true冲掉,只能从 exporter 侧过滤 - 保护性标签:给 48 个高危假阳性打
kor/used=true(k3s etcd 快照凭据、21 个 VolSync 备份仓库口令、6 个 rook CSI 密钥;Tailscale 的 21 张证书被 operator 冲掉)。该标签 同时保证kor --delete永不触碰这些对象 - 告警规则:kor 的 PrometheusRule 4 条(审计指标消失、总量 7 天增长、陈旧 VolumeAttachment、未挂载 PVC 增长)。刻意不做绝对数量告警——规模受过滤配置、集群历史 与假阳性三者影响
修复
fix(kor):补齐 RBAC 的nodes/csidrivers。上游 chart 默认表里没有这两项,导致 kor 查节点必然失败、把全部 25 条 VolumeAttachment 报成「Node does not exist」, 而这些 VA 的 PV 与节点都存在、属活跃挂载——照那份报告删会触发卷 detach。修正后 VA 发现数归零fix(kor-audit):指标里有、集群里已不存在的对象不再让脚本崩溃(新增?级别)
文档
docs(kor):更正 #518 里「集群级 CRD 不受 --exclude-labels 影响」的错误结论。processCrds有两个调用者:pkg/kor/all.go的getUnusedCrds传入真实 filterOpts (exporter 走这条,CRD 会被过滤),pkg/kor/crds.go的GetUnusedCrds丢弃参数 (独立子命令走这条)。同口径实测 871 → 622
集群清理(本轮实际删除的 449 个对象)
| 批次 | 对象 | 数量 | 依据与影响 |
|---|---|---|---|
| 1 | Woodpecker 孤儿流水线 Service + PVC | 7 + 7 | 无 owner、无 Pod 挂载;PV 57→50、Ceph 镜像 55→48,实测仅回收约 0.5 GiB |
| 2 | pod-impersonation-shell-* 6 对 + loft-cluster-* 4 个 ClusterRole |
16 | 已卸载的 Rancher/Loft 遗留,集群内无任何相关组件 |
| 3 | Loft 收尾(3 ClusterRole + 3 ClusterRoleBinding) | 6 | vcluster 的 SA 所在 namespace 已不存在,引用本已悬空 |
| 4 | 旧 ReplicaSet + 已完成 Job | 381 + 32 | 独立校验:RS 副本为 0 且无 Pod 引用,Job 全部 Completed |
删除前的 YAML 备份与核对记录在操作机 tmp/kor-backup-2026-10-03-*/(该目录不入库)。
刻意未删的 25 条 VolumeAttachment 见上文「修复」。
v2026.10.03.2
kor 接入集群:以 Prometheus exporter 形式常驻,暴露「无人引用的资源」指标, 配套 Grafana dashboard;全程只读。
新增
- 未使用资源审计(spec-kit
008-kor-monitoring):system/kor/包装上游 yonahd/kor chart0.2.16,namespace=kor。 ApplicationSet 按system/*自动发现,因此没有新增任何 Application 清单 - 只启用 exporter 模式:CronJob 模式需要 Slack webhook 或频道上传来投递报告,
而仓库内没有 Slack 凭据(宪法禁止提交凭据),报告只能落进 Pod 日志,故不启用。
exporter 每 30 分钟重扫一次(上游默认 10 分钟;每轮是整集群 LIST,对 4 节点
homelab 无需如此频繁),暴露
kubernetes_orphaned_resources{kind,namespace,resourceName} - 抓取无需人工登记:chart 自带 ServiceMonitor,且被
.Capabilities.APIVersions.Has "monitoring.coreos.com/v1"门控。ArgoCD v3.5.3 会把 目标集群的 API 版本透传给helm template --api-versions(controller/state.go→argo.APIResourcesToStrings→reposerver/repository.go), 因此门控能反映真实集群能力;既有 Prometheus 的serviceMonitorSelectorNilUsesHelmValues=false使其无需额外标签即被发现 - Grafana dashboard:导入上游 19863 到
system/monitoring-system(sidecar 只搜索该 namespace),改两处:datasource 硬编码 uidprometheus;kind="$kind"改正则kind=~"$kind"($kind是 multi + includeAll,All 在等值匹配下匹配不到序列) - 只读边界:RBAC 仅授予
get/list/watch,kor 的--delete能力不配置; 镜像钉v0.6.9+IfNotPresent(替代上游默认的latest+Always), 并显式声明 requests/limits(10m/64Mi、500m/256Mi)
修复
fix(kor):dashboard 的命名空间过滤条件曾按 kor 源码的标签名写成namespace, 实测为错——prometheus-operator 会打上目标标签namespace="kor",Prometheus 把指标 自带的同名标签重命名为exported_namespace。改用namespace后过滤器静默失效 (取值只剩 1 个),改回exported_namespace后为 41 个命名空间取值、sum by(exported_namespace)返回 42 组。教训与实测数据记入 research.md
文档
docs(008):kor 审计的 spec-kit 文档(spec / plan / research / tasks / quickstart / checklist)。research.md 记录了 ServiceMonitor 门控的 ArgoCD 源码取证、namespace与exported_namespace的踩坑实测,以及 Grafana sidecar 的grafana_dashboard_folder标签实际失效(面板目录来自另一条 provisioning 通道)这一既有现象
v2026.10.03
集群外的 QNAP NAS 接入监控:系统指标与磁盘 SMART 采集、告警规则与 Grafana dashboard; 另为桌面机新增 Tailscale 抓取目标。
新增
- 桌面机 Tailscale 抓取:
tailscale-nodesjob 新增192.168.3.246:5252(node: casey-desktop) - QNAP NAS 监控(spec-kit
007-nas-monitoring):NAS33657A(TS-453Bmini,QTS 5.2.10) 以 Container Station 应用运行两个 collector ——node-exporter(9100)与smartctl-exporter(9633),均走 host 网络,Prometheus 通过新增的nas-node/nas-smartctl两个静态 job 抓取并附加nas标签。NAS 是集群外设备,ArgoCD 无法管理其容器, 故 compose 入库存放在metal/nas/compose.yml并由 Container Station 导入 - 磁盘 SMART 采集:实测发现该 QNAP 控制器会让
smartctl --scan-open把 4 块 SATA 盘 误判为scsi,温度返回0且 ATA 属性全缺(exporter 照常有输出,属静默失效)。 必须使用smartctl-exporter:v0.14.0并以--smartctl.device=/dev/sdX;sat强制设备类型。 修复后 5 块盘返回真实温度 44–49°C、smart_status全为 1;SMART 指标另加disk盘位标签 (依据qcli_storage -d实测映射 sdc→bay1、sdd→bay2、sda→bay3、sdb→bay4) - 告警与可视化:新增 6 条告警规则(卷容量两级阈值、卷只读、RAID 失去冗余、SMART 健康失败、
磁盘高温)与 Grafana
NASdashboard(13 面板);抓取中断复用既有TargetDown。 规则按实测样本逐条评估,排除 QTS 内部固定布局对象(md9/md13固件镜像 32 槽位只用 4 槽、/mnt/ext固件 DOM 设计上仅剩 7.69% 可用),否则上线即永久误报
依赖与构建
chore(deps):kube-prometheus-stack91.5.2→91.5.3、renovate chart46.321.2→46.321.8、Markdown3.10.3→3.11、platformdirs4.11.12→4.11.14- 新增
.gitignore条目.helm_ls_cache/,避免本地 Helm 语言服务缓存入库
文档
docs(007):QNAP NAS 监控的 spec-kit 文档(spec / plan / research / tasks / quickstart / checklist), 其中 research.md 记录了 SNMP、Entware、QCLI 等被否决路径的实测依据
v2026.10.02.4
Tailscale 指标监控上线:4 台节点与 operator ProxyGroup 代理的客户端指标接入 Prometheus,
配套告警规则与 Grafana dashboard;同时消除 KubeVirt CDI CRD 的持续 OutOfSync。
新增
- Tailscale 节点指标(spec-kit
006-tailscale-monitoring):4 台节点由新增的tailscale-metrics.service以只读方式暴露tailscale web --readonly --listen <node-ip>:5252, Prometheus 通过additionalScrapeConfigs的tailscale-nodesjob 采集并附加node标签 - operator 代理指标:新增
tailscale-metricsProxyClass(metrics + ServiceMonitor), 由ingress-proxies/egress-proxies两个 ProxyGroup 通过spec.proxyClass引用,共 8 个采集目标。 实测否决了更省事的PROXY_DEFAULT_CLASS方案:8 个带experimental-forward-cluster-traffic-via-ingress注解的独立 Ingress 代理上游不支持 metrics, 默认类会为它们建出永远失败的 target 并触发TargetDown误报 - 告警与可视化:新增 3 条告警规则(节点健康消息、节点异常丢包、代理健康消息)与
Grafana
Tailscaledashboard(14 面板);抓取中断复用既有TargetDown/KubePodNotReady
修复
fix(kubevirt):从 vendoredsystem/kubevirt/templates/cdi-operator.yaml的cdis.cdi.kubevirt.ioCRD 中移除v1alpha1版本块(2517 行),只保留 operator 认可的v1beta1。 CDI operator 每次 reconcile 都会删除非最新版本,与 Git 中声明的版本列表来回拉锯, 导致应用持续OutOfSync;README 排障表补上该条
依赖与构建
chore(deps):kube-prometheus-stack91.5.1→91.5.2、renovate chart46.317.2→46.321.2chore:Cilium pin1.20.1→1.20.2、Tailscale pin →1.102.4chore(lobe-chat):lobe-chat 镜像更新到2.2.18chore:合并上游 khuedoan/homelab master,补上落后的提交(含build: containerize docs带来的Dockerfile/.dockerignore)
文档
- 同时使用 GitHub 和 Gitea 新增 「upstream remote 与从 GitHub 同步上游」章节
docs(006):Tailscale 监控的 spec-kit 文档(spec / plan / research / tasks / quickstart / checklist)
v2026.10.02.3
Rook-Ceph CephX 密钥加固,以及 OSD down/out 故障的排查文档补全。
修复
fix(rook-ceph):按 Rook CVE-2025-30156 指南,将 CSI 与 RBD mirror peer 密钥轮换到aes256k(节点内核均已升级到 7.0+)fix(rook-ceph):密钥迁移完成后删除旧 CSI 密钥,并移除临时告警 mutefix(rook-ceph):将 CephX allowed ciphers 限制为aes256k
文档
- 新增 Rook-Ceph OSD down/out 修复:mon secret FSID 失配:
rook-ceph-osd-1在n100-cheshi-0升级后长期down/out的完整根因分析。 根因是rook-ceph-monSecret 的fsid过期,operator 据此判定 OSD 磁盘 「属于另一个 Ceph 集群」而拒绝创建 OSD Deployment —— 磁盘数据自始至终完好。 修复只需改回真实ceph fsid,无需ceph osd purge或重建 OSD - 修正 Ubuntu 24.04 → 26.04 升级指南
「检查 7」中错误的 OSD 恢复建议:原文要求
ceph osd purge+ 抹掉分区, 会销毁完好副本;现改为指向上述 FSID 修复流程 - 将两份 Rook-Ceph 排障文档从
system/rook-ceph/docs/迁入docs/并加入 mkdocs 导航 (此前未发布)
v2026.10.02.2
修正 metal/roles/*/defaults/main.yml 中落后的版本 pin,使仓库与集群实跑版本一致。
修正前 Cilium pin 为 1.20.0、k3s pin 为 v1.35.4+k3s1,而集群实跑 1.20.1 / v1.36.5+k3s1,
直接执行 make -C metal cluster 会把两者一起回退。
修复
fix(cilium):Cilium 版本 pin 由1.20.0更新为1.20.1,与集群实跑 chart 版本一致fix(k3s):k3s 版本 pin 由v1.35.4+k3s1更新为v1.36.5+k3s1,与节点实跑版本一致
v2026.10.02
4 台节点由 Ubuntu 24.04 LTS 就地升级到 26.04 LTS(Resolute Raccoon)后的适配与修复。
完整明细见 git log v2026.09.21..v2026.10.02 --no-merges。
修复
- 内核参数持久化:
prerequisites角色原先把 sysctl 写入/etc/sysctl.conf,而 26.04 的procps将其标记为remove-on-upgrade并在升级时删除,导致fs.inotify.max_user_instances从 8192 掉回 128,fwupd每小时失败、KubeVirtvirt-handler在 inotify 用量高的节点上 CrashLoopBackOff。四组 sysctl 全部改写到/etc/sysctl.d/90-homelab-prerequisites.conf - PXE/autoinstall 适配 26.04.1:ISO 与 netboot.xyz squash 资源更新到 26.04.1;
autoinstall 包列表移除 26.04 已不存在的
libpcre3/libpcre3-dev,dnsutils换成bind9-dnsutils(否则全新装机在packages:阶段失败);netplan 由gateway4改为routes fix(lobe-chat):.helmignore排除AGENTS.md而非已不存在的CLAUDE.mdfix(docs):修复 CLAUDE.md 重命名后遗留的悬空引用
文档
- 新增 Ubuntu 24.04 LTS 升级到 26.04 LTS: 升级流程 + 升级后必查清单(sysctl 丢失、第三方 apt 源被禁用、残留 conffile 导致 logrotate 失败、 旧内核残留、netplan 弃用、Ceph OSD 未被 Rook 接管)
- 全仓库的 24.04 版本描述更新为 26.04.1:README、PXE 引导、沙箱、路线图、决策记录、固件裁剪说明
docs(versioning):补上gh release create必需的-R参数
依赖
- Renovate 持续更新 Helm Chart 与容器镜像,非 major 依赖按周聚合合并(含 rustfs 1.x、renovate 46.310.0、pymdown-extensions 12)
v2026.09.21
自 v2025.02.17 以来的状态快照,覆盖 830 个非合并提交。完整明细见 git log v2025.02.17..v2026.09.21。
新增应用
- RustFS:S3 兼容对象存储,启用 TLS、日志轮转与 Tailscale Ingress;lobe-chat 的对象存储由 MinIO 迁移至此
- RSSHub:RSS 聚合服务,含 PostgreSQL
- Upsnap:网络唤醒(Wake-on-LAN)管理面板
- SearXNG:自建元搜索引擎,并接入 lobe-chat
- Semaphore:任务编排面板,配置了 dnsPolicy 与时区
- Kube Explorer:Kubernetes 资源浏览器
- Eraser:节点镜像清理
移除
- Webtop 与 MinIO:不再使用,已移除部署与相关 egress 配置
KubeVirt 虚拟机
- 引入 KubeVirt 与 CDI,新增虚拟机测试套件(unit / integration / e2e)
- 用 DataVolume PVC 作为 rootdisk,替换 containerDisk
- 支持从外部 Secret 注入 cloud-init,避免在公开仓库中泄露凭据
- control-plane 副本数降为 1,适配 4 节点规模
- Cilium datapathMode 由
netkit调整为netkit-l2
网络与 Tailscale
- Tailscale Operator 接管 Ingress / DNS / 证书 / Funnel,替代 nginx-ingress、external-dns、cert-manager、cloudflared
- 新增出口代理组(proxygroup)、proxyclass 与出口服务配置
- 多个外部服务补齐 HTTPS 端口与 proxy-class 注解
- Cilium 由 1.19.1 升级到 1.19.6,保持 native routing、BPF masquerade、DSR、netkit、Bandwidth Manager
- 因内核与 datapathMode 变更导致带 NetworkPolicy 的 Pod 异常,临时禁用本仓库全部 NetworkPolicy
系统与升级
- 新增系统升级流程,配合 kured 完成滚动重启
- csi-driver-nfs 补齐 resources requests/limits
- 适配 k3s 移除 kube-proxy IPVS 模式
- journald 日志接入
- 清理 linux-firmware 中不再需要的厂商子包
可观测性
- 4 个节点全部接入磁盘 SMART 监控,含仪表盘与告警规则(展示节点主机名与 IP)
- 新增 VolSync 与 kube-explorer 相关监控
- Grafana 12:启用 git sync、图像渲染器等新特性
文档与工程
- 重组 troubleshooting 文档结构,新增节点挂起故障排除指南
- 更新架构文档,移除 Zot Registry 相关内容
- 新增 KubeVirt、SMART 监控等能力的 Spec Kit 规格(
specs/)
依赖
- Renovate 持续更新 Helm Chart 与容器镜像,非 major 依赖按周聚合合并
v2025.02.17
- 进入 homelab2 目录后自动运行:
nix develop --extra-experimental-features nix-command --extra-experimental-features flakes
bash 实现
通过 bash 的重载 cd 函数来实现。
- 创建一个脚本文件:
创建一个脚本文件(例如
run_nix_develop.sh),内容如下:
#!/bin/sh
nix develop --extra-experimental-features nix-command --extra-experimental-features flakes
确保脚本具有可执行权限:
chmod +x run_nix_develop.sh
- 修改
~/.bashrc文件: 在~/.bashrc文件中添加以下内容,以便在每次进入目录时检查是否在特定目录中,并执行相应的脚本。
# ~/.bashrc
function cd() {
builtin cd "$@" || return
if [ "$(pwd)" == "~/projects/homelab2" ]; then
~/projects/homelab2/run_nix_develop.sh
fi
}
这个脚本会重写 cd 命令,使其在每次切换目录后检查当前目录是否为 ~/projects/homelab2,如果是,则执行 run_nix_develop.sh 脚本。
- 重新加载
~/.bashrc文件: 重新加载~/.bashrc文件以使更改生效:
source ~/.bashrc
zsh 实现
在 zsh 中设置进入当前目录后自动执行 run_nix_develop.sh 脚本,可以通过以下方法实现:
使用 precmd 钩子
precmd 钩子会在每次显示命令提示符前触发,但需结合目录检查逻辑。
- 修改
~/.zshrc文件:
# ~/.zshrc
autoload -U add-zsh-hook
# 定义钩子函数
run_on_prompt() {
if [[ "$(pwd)" == "~/projects/homelab2" ]]; then
~/projects/homelab2/run_nix_develop.sh
fi
}
# 将函数绑定到 precmd 钩子
add-zsh-hook precmd run_on_prompt
- 重新加载
~/.zshrc文件: 重新加载~/.zshrc文件以使更改生效:
source ~/.zshrc
v2025.02.16
- Remove Loki-stack, as Loki-stack is no longer actively maintained.
- Use Grafana Cloud -> grafana/k8s-monitoring to monitor logs and Profiles. See kubernetes-monitoring/configuration for more details. Since this involves Grafana Cloud secrets, install directly using helm-dashboard; the helm chart's values.yaml is not maintained in this repo.
上游历史(fork 前)
以下条目继承自上游 khuedoan/homelab,正文逐字保留。
其中的版本号与 -alpha 后缀属于上游命名,不适用本仓库的 CalVer 版式。
v0.0.8
Notable changes:
- build: run post install scripts by default
- build: set
KUBECONFIGfrom global Makefile - feat(external-dns)!: add cluster name as owner ID
- feat(tools): install
yamllint,ansible-lintandk9s - feat(tools): set
KUBECONFIGby default - feat: add pre-commit hooks
- feat: add script to setup Gitea tokens and OAuth apps
- perf(argocd): turning on selective sync
- refactor(docs): migrate to mkdocs
- refactor(metal): migrate to Fedora 36 for newer packages
- refactor(pxe)!: combine dhcpd and tftpd to dnsmasq
- Many bug fixes
Please see git log for full change log.
0.0.7-alpha
- Replace standard Vault with Vault Operator
- Automatically initialize and unseal Vault
- Declarative secret generation and management
- Declarative Gitea configuration with YAML
- Automatic OS rolling upgrade
- Automatic Kubernetes rolling upgrade
- Automatic application updates using Renovate (still require manual token generation)
- Add script to wait for essential services after deployment
- Add icons and bookmarks to the home page
- Deploy Matrix chat
- Replace Authentik with Dex for SSO (still require manual token generation)
- Switch to Mermaid for diagrams in documentation
- Replace Vagrant with k3d for development environment
- Use nip.io domain for development environment
- Remove Backblaze (S3 Glacier and/or Minio will be added in future version)
- Enable monitor for the majority of applications
- Many code refactorings and bug fixes
0.0.6-alpha
- Upgrade to Kubernetes 1.23
- Support external resources:
- Cloudflare DNS and Tunnel
- Backblaze for backup
- Auto inject secrets to required namespaces
- Replace self-signed certificates with Let's Encrypt production (with API token injected from the
externallayer) - Add DNS records automatically using external-dns
- Easy Cloudflare Tunnel configuration with annotations
- Offsite backup to Backblaze B2 bucket using k8up-operator
- Add private container registry
- Remove Knative to save resources (temporarily)
- Enable encryption at rest for Kubernetes Secrets
- Add more Tekton tasks and pipelines
- Initialize GitOps repository on Gitea automatically after install
- Generate MetalLB address pool automatically (default to the last
/27subnet) - Some bug fixes
0.0.5-alpha
- Add convenience scripts
- Add Loki for logging
- Add custom health check for Application and ApplicationSet
- Use Vault with dev mode on (temporarily until we hit beta)
- Replace Authelia with Authentik
- Upgrade to Kubernetes 1.22
- Upgrade most services to the latest version
- Set ingress class and storage class explicitly
- Initial Linkerd and Knative setup (not working yet)
- Set up Hajimari for home page with automatic ingress discovery
- Add dev VM for local development or evaluation
- Optimize bare metal provisioning performance
- Replace Syncthing with Seafile (may use both in the feature)
- Enable Gitea SSH cloning via Ingress
- Various code clean up
- Add more documents
0.0.4-alpha
- Switch to Rocky Linux
- Some optimization for bare metal provisioning
- Switch to k3s and combine Kubernetes cluster config in
./infralayer to./metallayer (because k3s is also configured using Ansible) - Split boostrap Helm charts in
./infralayer to./bootstraplayer (with new ArgoCD pattern) and./systemlayer - Add
./platformlayer for some applications like Gitea, Tekton... - User only need to provision
./metalandbootstraplayer, the./bootstraplayer will deploy the remaining layers - Provisioning time from empty disk to running services is significantly reduced (thanks to k3s and new bootstrap pattern)
- Use mdBook for documents
- Replace Drone CI with Tekton
- Enable TLS on all Ingresses (using cert-manager)
- Add some new applications
0.0.3-alpha
- Generate Terraform backend config automatically
- Switch to CoreOS
- Better PXE boot setup
- Diagrams as code
0.0.2-alpha
- Ensure idempotency for bare metal provisioning
- Extract instead of mounting the OS ISO file
- Easy initial controller setup (with only Docker)
- Switch to Fedora
- Remove LXD
- Move etcd (Terraform state backend) back to Docker
0.0.1-alpha
- Bare metal provisioning with PXE
- LXD cluster
- Terraform state backend (etcd)
- RKE cluster
- Core services (Vault, Gitea, ArgoCD,...)
- Public services to the internet (via port forwarding or Cloudflare Tunnel)