client.containers.run()仅创建并启动容器一次,反复调用会因名称冲突报409错误;正确做法是先用run(detach=true, remove=false)创建,再通过containers.get()或list(filters)获取容器对象,调用start()/stop()控制,并每次操作前reload()刷新状态,推荐用label过滤实现安全批量启停。

client.containers.run() 不是启停容器的通用接口,它只负责「创建并启动一次」。真要反复启停,得用 containers.get() 或 containers.list() 拿到已有容器对象,再调用 .start()、.stop()。
启停前必须先拿到容器对象,不能直接 run 两次
很多人误以为反复调用 client.containers.run(image="nginx", name="myapp", detach=True) 就能“重启”,其实每次都会报 409 Client Error: Conflict —— 因为名字已存在。run 是创建+启动,不是启停操作。
正确路径是:先确保容器已存在(首次用 run(..., detach=True, remove=False) 创建),后续用名字查出来再控制:
-
container = client.containers.get("myapp")直接按 name 获取(name 必须唯一且已存在) -
containers = client.containers.list(filters={"name": "myapp"})模糊匹配,适合不确定是否运行中 - 查不到会抛
docker.errors.NotFound,记得 try/catch
container.start() 和 container.stop() 的实际行为
start() 对已停止容器有效,对运行中容器无副作用;stop() 发送 SIGTERM,10 秒后强制 SIGKILL(可传 timeout=30 调整)。但注意:
- 容器若处于
created状态(比如刚create()没start()),start()才真正拉起;stop()对created状态无效,会报错 - 执行
stop()后状态变为exited,不是stopped—— Docker 没有 stopped 状态,只有 exited/running/created/paused - 如果容器内进程忽略 SIGTERM(如某些 Java 应用),
stop()会卡满 timeout 时间才杀掉,建议在镜像里配好信号处理
批量启停必须加过滤,否则可能误操作系统容器
别写 for c in client.containers.list(all=True): c.stop() —— 这会停掉所有容器,包括你本地跑的数据库、Redis、甚至 docker buildx 构建器。
安全做法是打 label 启动,启停时只操作带该 label 的容器:
client.containers.run(
"redis:7-alpine",
name="test-redis",
labels={"managed_by": "mytest"},
detach=True
)
# 启动所有标记容器
for c in client.containers.list(filters={"label": "managed_by=mytest"}):
c.start()
# 停止所有标记容器
for c in client.containers.list(filters={"label": "managed_by=mytest"}):
c.stop(timeout=5)
- label 比 name 更灵活,支持多值、版本标识(如
version=2.1) - filters 支持多个条件:
{"label": "managed_by=mytest", "status": "running"} - list() 默认只返回 running 容器,加
all=True才包含 exited 状态
销毁前不 reload() 就 stop/remove,大概率出错
容器状态不是实时的。container.status 是对象初始化时缓存的值,如果外部手动 docker stop myapp,Python 里的 container 对象仍显示 running,直接 container.stop() 就会触发 409 Conflict。
每次操作前必须刷新元数据:
- 调用
container.reload()再判断container.status - stop 前检查:
if container.status in ["running", "created"]: - remove 前检查:
if container.status in ["exited", "created"]:,否则先stop() - force=True 是最后手段,
remove(force=True)能删 running 容器,但会跳过优雅终止流程
容器名冲突、状态缓存、label 过滤、reload 刷新——这四点漏掉任意一个,脚本在 CI 或多环境部署时就容易静默失败。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











