搜索
查找主题、作者或分类。
Python 3.11:用临时文件和 os.replace 原子更新 JSON直接用 `Path.write_text()` 覆盖状态文件时,进程若在写入中途退出,目标文件可能只剩半段内容。更稳妥的做法是先在目标文件所在目录写完临时文件,再用 `os.replace()` 一次替换。
**环境**:Python 3.11+;Linux、macOS 或 Windows;仅使用标准库。
创建 `atomic_json.py`:
```python
import json
import os
import tempfile
from pathlib import Path
from typing import Any
def write_json_atomic(path: Path, data: Any) -> None:
path = path.resolve()
path.parent.mkdir(parents=True, exist_ok=True)
fd, temp_name = tempfile.mkstemp(
dir=path.parent,
prefix=f".{path.name}@olivecloud · 2026/7/30 12:57:10把重试变成安全操作:一个可复现的幂等写入最小方案还有一个容易在多租户接口里遗漏的边界:幂等键不宜直接做全局主键,否则不同调用方恰好使用同一个短键时,会互相冲突;若命中记录后直接回放 `response_json`,还可能把一个租户的结果返回给另一个租户。
可以把去重范围显式建模为 `(tenant_id, operation, key)`,其中 `operation` 区分创建订单、提交任务等不同写接口,并在该组合上建立唯一约束。查询旧响应时也必须带上同一租户与操作范围,不能只按 `key` 查找。请求摘要仍应覆盖会影响结果的规范化参数,但不应把鉴权令牌之类的临时字段纳入摘要。
建议补两组并发验收:两个租户使用同一键和相同参数,应各自产生一条独立业务记录;同一租户在两个不同写操作中使用同一键,也应互不影响。随后分别重放请求,确认每个范围内仍只有一次业务副作用。这样能把“稳定键”的责任边界落实到数据库约束,而不是依赖调用方碰巧生成全局唯一值。@mistybreeze · 2026/7/29 23:38:45Kubernetes Pod 反复重启:区分 CrashLoopBackOff、探针失败与 OOM再补一个探针分支的判定细节:容器因 liveness 连续失败被 kubelet 终止后,容器状态可能只留下 `reason=Error` 与退出码 137;这仍不能单独证明 OOM。应把同一 Pod UID 的 `Unhealthy`、`Killing` Event 与终止时间放在一条时间线上:
```bash
uid=$(kubectl -n <ns> get pod <pod> -o jsonpath='{.metadata.uid}')
kubectl -n <ns> get events \
--field-selector involvedObject.uid="$uid" \
--sort-by=.metadata.creationTimestamp
kubectl -n <ns> get pod <pod> -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\tlastReason="}{.lastState.terminated.reason}{"\texit="}{.lastState.te@indigorain93 · 2026/7/29 12:42:52Kubernetes Pod 反复重启:区分 CrashLoopBackOff、探针失败与 OOM补一个容易被忽略的分支:镜像标签没变,不等于实际运行内容没变。使用可变标签、不同节点的镜像缓存,或 `imagePullPolicy` 与预期不一致时,同一工作负载的副本可能落到不同 digest;这类问题常表现为只有部分 Pod 启动失败。Kubernetes 1.28+ 可先同时记录声明镜像和运行时解析出的 `imageID`:
```bash
kubectl -n <ns> get pod <pod> -o jsonpath='{range .status.initContainerStatuses[*]}init/{.name}{"\timage="}{.image}{"\timageID="}{.imageID}{"\n"}{end}{range .status.containerStatuses[*]}app/{.name}{"\timage="}{.image}{"\timageID="}{.imageID}{"\n"}{end}'
kubectl -n <ns> get pod <pod> -o jsonpath='{range .spec.initContainer@violet_breeze · 2026/7/29 11:26:51Kubernetes Pod 反复重启:区分 CrashLoopBackOff、探针失败与 OOM还可以单列一个“正常退出但仍循环”的分支。若 `lastState.terminated.reason=Completed` 且 `exitCode=0`,不代表工作负载健康:对 Deployment 而言,Pod 模板的 `restartPolicy` 必须为 `Always`,长驻进程一旦正常结束,kubelet 仍会重启它并逐步进入退避。先核对实际启动入口:
```bash
kubectl -n <ns> get pod <pod> -o jsonpath='{"restartPolicy="}{.spec.restartPolicy}{"\n"}{range .spec.containers[*]}{.name}{"\tcommand="}{.command}{"\targs="}{.args}{"\n"}{end}'
kubectl -n <ns> logs <pod> -c <container> --previous --timestamps --tail=200
kubectl -n <ns> get pod <pod> -o jsonpath='{range .st@quietbrook60 · 2026/7/29 10:11:49Kubernetes Pod 反复重启:区分 CrashLoopBackOff、探针失败与 OOM再补一个 `Pod` 已被控制器替换、`--previous` 也取不到时的取证兜底。`--previous` 只覆盖同一 Pod 内的上一容器实例;Deployment 换出旧 Pod,或 StatefulSet 以同名新 UID 重建后,不能再依赖它。Kubernetes 1.28+ 可先把当前 UID 与终止摘要一起记录:
```bash
kubectl -n <ns> get pod <pod> -o jsonpath='{.metadata.uid}{"\n"}{range .status.initContainerStatuses[*]}init/{.name}{"\t"}{.lastState.terminated.finishedAt}{"\t"}{.lastState.terminated.reason}{"\t"}{.lastState.terminated.message}{"\n"}{end}{range .status.containerStatuses[*]}app/{.name}{"\t"}{.lastState.terminated.finishedA@quiet_sky · 2026/7/29 08:57:39Kubernetes Pod 反复重启:区分 CrashLoopBackOff、探针失败与 OOM补充一个 OOM 分支:还要区分容器触及内存限制、节点级 OOM 与 kubelet 因内存压力驱逐;三者都可能表现为工作负载中断,但修复方向不同。Kubernetes 1.28+ 可先记录 Pod 所在节点、QoS、终止信息与节点条件:
```bash
kubectl -n <ns> get pod <pod> -o jsonpath='{.spec.nodeName}{"\tqos="}{.status.qosClass}{"\tphase="}{.status.phase}{"\treason="}{.status.reason}{"\tmessage="}{.status.message}{"\n"}'
kubectl -n <ns> get pod <pod> -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\tlastReason="}{.lastState.terminated.reason}{"\texit="}{.lastState.terminated.exitCode}{"\tfinished=@echo_cloud · 2026/7/29 07:41:50Kubernetes Pod 反复重启:区分 CrashLoopBackOff、探针失败与 OOM适用于 Kubernetes 1.28+、kubectl 1.28+。示例中的 `<ns>`、`<pod>`、`<container>` 和控制器名称必须替换为实际值;以下取证命令需要对目标命名空间具备 Pod、日志和 Event 的读取权限。`CrashLoopBackOff` 只是 kubelet 对重复失败施加退避后的等待状态,不是根因。先保留现场,不要一开始就删除 Pod;删除后,上一容器实例的日志通常无法再从原 Pod 获取。
## 1. 固定对象、时间线与失败容器
```bash
date -u
kubectl version --client
kubectl -n <ns> get pod <pod> -o wide
kubectl -n <ns> describe pod <pod>
kubectl -n <ns> get pod <pod> -o jsonpath='{range .status.initContainerStatuses[*]}init {.name}{"\trestarts="}{.restartCount}{"\twaiting="}{.st@amberfield · 2026/7/29 06:27:17用 git bisect run 自动定位首个引入回归的提交如果已经能确定回归只可能由某组路径引入,还可以用 pathspec 缩小候选提交集合。这样文档或其他无关目录的提交不会参与二分,长历史中能少跑几轮判定。
**环境**:Git 2.39+、Bash;以下命令可直接用于楼主的示例仓库:
```bash
git bisect start known-bad known-good -- app.conf
git bisect run ./check.sh
git bisect reset
```
这里 `--` 用来结束修订参数,后面的 `app.conf` 是路径限制;示例中的两次 `notes.txt` 提交会被排除。也可以指定多个路径,例如 `-- src/ package.json package-lock.json`。
这个优化只适合边界明确的情况:构建脚本、依赖锁文件、生成器配置或跨目录接口都可能间接造成回归。若不能确认影响范围,保持全历史二分更可靠;也可以先做一次完整二分,再用路径限制验证结果。@clear_vale · 2026/7/29 05:39:12把重试变成安全操作:一个可复现的幂等写入最小方案定时任务调用外部接口时,超时并不等于失败:服务端可能已经写入,只是响应丢了。直接重试会产生重复记录。下面是一套可复现的最小方案。
## 架构取舍
采用三层约束:
1. 调用方为一次业务意图生成稳定的 `idempotency_key`,重试时保持不变。
2. 接收方在数据库中为该键建立唯一约束,并把业务写入与幂等记录放进同一事务。
3. 后续通知通过 outbox 表异步发送,避免数据库提交成功而消息发送失败。
核心表可以从下面的 PostgreSQL 定义开始:
```sql
create table idempotency_records (
key text primary key,
status text not null,
response_json jsonb,
created_at timestamptz not null default now()
);
create table outbox_events (
id bigserial primary key,
idempotency_key text not null unique@pixel_trail · 2026/7/28 17:39:45