专注系统底层与高性能服务开发,持续记录 Go / Rust / C++ / 云原生的一线实践。
从源码细节到线上治理,尽量少空话,多代码。
专注系统底层与高性能服务开发,持续记录 Go / Rust / C++ / 云原生的一线实践。
从源码细节到线上治理,尽量少空话,多代码。
目标先定清楚 供应链安全的核心不是“发现漏洞”,而是回答三个问题: 产物是谁构建的? 构建过程是否可复现、可验证? 已知风险是否能阻断上线? 最小可行链路 构建时生成 SBOM(CycloneDX/SPDX)。 对镜像和 SBOM 进行签名(cosign)。 在部署前做签名与策略校验(admission policy)。 流水线分层 Build:固定基础镜像 digest,避免 tag 漂移。 Attest:记录构建来源、commit、runner 信息。 Verify:发布阶段验证签名、来源、漏洞阈值。 策略示例 rules: - require_signature: true - require_sbom: true - max_critical_vulns: 0 - trusted_builders: - ci-prod-runner 常见失败点 只签镜像不签 SBOM,证据链断裂。 允许“人工例外”但不留审计记录。 漏洞阈值过严导致全线绕过,最终失去治理。 小结 把“生成证据、验证证据、阻断风险”串成默认流水线,供应链安全才会从一次性项目变成长期能力。
背景 很多集群在节点升级、手动驱逐时出现服务抖动,根因常常是没配或错配 PDB。 基本配置 apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: api-pdb spec: minAvailable: 2 selector: matchLabels: app: api 总结 PDB 不是“可选增强”,而是生产集群进行维护操作的安全护栏。 维护窗口稳定与否,很多时候取决于这几行 YAML。
背景 很多后台系统首屏不慢,但页面切换时常有明显等待。通常是路由组件和数据都在点击后才加载。 实用做法 鼠标悬停时预取路由 chunk 关键列表页预取首屏数据 低网速场景要可降级关闭 const UserPage = () => import('@/pages/UserPage.vue') function prefetchUserPage() { void UserPage() } 总结 预取策略要适度,目标是减少等待感,不是盲目预加载所有页面。 用户感知速度,很多时候来自“提前一步”。
背景 很多 allocator 优化在 micro benchmark 里很好看,上线却收益一般。原因是评测维度不完整。 建议指标 p50/p95/p99 分配耗时 长时间运行碎片率 多线程争用下吞吐波动 auto begin = std::chrono::steady_clock::now(); void* p = alloc.allocate(256); alloc.deallocate(p, 256); auto end = std::chrono::steady_clock::now(); 总结 评测方法比结果数值更重要,先保证实验可信,再比较方案优劣。 性能数据要能解释真实场景,才有决策价值。
背景 镜像漏洞扫描只是起点。上线前还需要回答:这个镜像是谁构建的,包含了什么依赖,是否被篡改。 落地步骤 构建后生成 SBOM 对镜像做签名 部署侧验证签名 syft packages ghcr.io/org/app:latest -o spdx-json > sbom.json cosign sign ghcr.io/org/app:latest cosign verify ghcr.io/org/app:latest 总结 供应链安全要形成闭环:生成、签名、验证,缺一不可。 可观测运行时,也要可追溯构建时。
为什么会“越跑越慢” Raft 日志长期不压缩会拖慢重放与追赶;但压缩过于激进又会让慢 follower 频繁走全量快照,网络和磁盘双杀。 压缩策略建议 用“日志条目数 + 磁盘占用”双阈值触发快照。 快照频率按写入速率自适应,而不是固定分钟数。 仅在 commit index 安全推进后截断日志。 安装快照时的工程点 分块传输并带 offset 校验。 最后一块再原子切换状态机数据目录。 快照应用期间限制客户端写入峰值。 Go 侧接口设计 type SnapshotStore interface { Create(meta SnapshotMeta) (io.WriteCloser, error) Open(id string) (io.ReadCloser, SnapshotMeta, error) List(limit int) ([]SnapshotMeta, error) } 常见故障 截断日志早于快照持久化完成,导致节点重启后状态缺口。 快照元数据未纳入 WAL,断电恢复后索引错位。 对慢 follower 缺少限流,拖累 leader。 小结 Raft 的可用性不只来自选主,更来自“日志、快照、复制窗口”三者的节奏匹配。压缩策略设计得当,集群才能长期平稳运行。
背景 微服务调用里,服务发现经常被当成理所当然的基础设施。但注册中心一旦抖动,调用链就会被放大影响。 实用策略 本地缓存上次可用实例列表 失败时指数退避刷新 查询失败时优先用“最近成功快照” type Resolver interface { Resolve(ctx context.Context, service string) ([]string, error) } type SnapshotCache struct { mu sync.RWMutex data map[string][]string } 总结 服务发现的核心目标是“可用优先”,而不是“每次都拿最新”。 基础组件会失败,容错设计要把失败当常态。
问题不在快,而在失控 很多 io_uring 服务在压测里吞吐漂亮,线上却出现尾延迟飙升。根因通常是提交队列无限推进,完成队列消费跟不上。 背压触发条件 SQ ring 使用率超过 80%。 CQ backlog 持续增长。 业务层处理耗时超过 IO 完成速率。 建议的三层背压 提交层:限制 in-flight 请求上限。 协程层:队列超阈值时暂停新任务调度。 入口层:对上游返回 retry-after 或降级响应。 伪代码 if (inflight > max_inflight || cq_backlog > cq_limit) { pause_accept(); shed_low_priority(); } while (io_uring_peek_cqe(&ring, &cqe) == 0) { handle_cqe(cqe); io_uring_cqe_seen(&ring, cqe); } 关键指标 submit_to_complete_latency cq_backlog inflight shed_count 小结 io_uring 的上限很高,但系统稳定性的上限由背压机制决定。先把“慢下来也不爆炸”做对,再追求“快”。
背景 Tokio 里 select! 和超时很常用,但取消发生在任意 await 点,任务可能停在中间状态。 实践建议 把副作用操作放在不可分割阶段 写操作尽量幂等 关键路径加补偿或重试机制 tokio::select! { _ = shutdown.recv() => { tracing::info!("cancelled"); } res = do_commit_work() => { res?; } } 总结 取消安全本质是状态机设计,不是语法问题。 异步代码能停下来不难,停得干净才难。
典型事故 业务高峰来临,HPA 根据 CPU 拉副本;同时 VPA 建议提升 requests,导致 Pod 频繁重建。结果不是稳定,而是不断抖动。 协同原则 HPA 负责“横向弹性”(副本数)。 VPA 负责“纵向建议”(资源基线)。 两者不要同时直接控制同一 Deployment 的同一资源维度。 实操模式 在线服务:HPA + VPA(recommendation only)。 离线作业:VPA(auto) + 关闭 HPA。 把 VPA 建议周期性写回 Helm values,再经灰度发布生效。 关键参数 HPA 目标指标建议使用自定义业务指标(QPS、队列深度),不要只盯 CPU。 HPA behavior 里设置 scaleDown 稳定窗口,防止“刚扩就缩”。 VPA 设置 minAllowed/maxAllowed,避免极端建议。 观测面板建议 当前副本数与目标副本数差值。 Pod 重建频率与重建原因。 requests 利用率分布(而非平均值)。 小结 HPA 与 VPA 本质上是两个控制器。让它们协同的关键,不是“都开”,而是“职责分离 + 变更节流 + 可观测闭环”。