首页 / 资讯中心 / 文章详情

docker ps 高级玩法:用 --filter 和 --format 精准筛选与格式化输出容器

docker ps 高级玩法:用 --filter 和 --format 精准筛选与格式化输出容器 ★ FEATURED ARTICLE
1. docker ps 默认输出解读你看到的每一列都是筛选的切入点干运维和开发这些年docker ps 是每天敲得最多的命令之一。它的地位有点像 Linux 里的 ps无非是“当前有哪些容器在跑”可很多人只会敲 docker ps真正遇到问题要定位、要写自动化脚本的时候光靠默认那一屏表格远远不够。之前帮某个客户排查线上问题几十个容器堆在同一台机器上想找出链路里的依赖容器是不是悄悄挂了一个个肉眼去扫根本不现实最后还是靠 docker ps 的 --filter 和 --format 组合几秒就把结果捞出来了。从那之后我就觉得这两个参数才是 docker ps 真正的灵魂。1.1 docker ps 默认输出里每一列的实际含义先过一遍基础。不带参数执行 docker ps输出包含 CONTAINER ID、IMAGE、COMMAND、CREATED、STATUS、PORTS、NAMES 七列。每列背后都有讲究。CONTAINER ID容器的短 ID默认取 64 位完整 ID 的前 12 位。它就像身份证号的前几位日常定位用足够但要拿去精确匹配、拼接命令可能因为截断产生歧义。IMAGE容器基于哪个镜像启动的比如 nginx:1.25。注意这里显示的是启动时用的镜像引用不一定是容器内正在跑的实际版本。COMMAND容器启动时执行的命令。默认会截断显示想看全需要加 --no-trunc。CREATED创建时间显示成 “N seconds ago” 这种相对时间比绝对时间直观但正因为它不是绝对时间脚本里很难直接计算。STATUS当前状态最常见的是 Up 2 hours也有 Exited (0) 3 minutes ago。状态字段是动态文本做精确匹配时容易踩坑。PORTS端口映射或暴露信息例如 0.0.0.0:8080-80/tcp。多端口时会用逗号分隔。NAMES容器名称没显式指定的话 Docker 会随机生成比如 jovial_knuth。这列在 filter 里是正则匹配的入口后面会专门讲。这里最容易被忽略的一点是docker ps 默认只显示 status 为 running 的容器。已退出的容器、刚创建未启动的容器、被暂停的容器默认都不出现。想看到全部必须 docker ps -a。这个“默认只看运行的”看着简单却会引出后面很多坑尤其是过滤 exited 状态容器时不加 -a 就什么都看不到。1.2 什么时候你必须动用 filter 和 format默认输出够用吗少量容器时当然够。但一旦出现下面几类场景你就会被默认输出折磨。第一机器上容器数量一多默认的一张宽表格撑满整个屏幕想看某一个容器的状态眼睛得在几百行里扫。第二想写 Shell 脚本批量操作某些容器比如清掉所有退出的、重启所有状态异常的默认输出没法直接当输入还得靠 awk、grep 二次处理。第三想拿到某个固定字段去做监控采集比如把每个容器的名称和状态发给监控系统默认输出的格式还得再切一遍。这三个场景对应两个参数--filter 负责“筛选”让输出只包含你关心的容器--format 负责“塑形”让输出的内容、顺序、格式都由你定义。两个合起来docker ps 就从“人眼查看工具”变成了“命令行查询接口”。甚至可以这样理解filter 控制的是 SQL 里的 WHEREformat 控制的是 SELECT 和结果列两者结合你就拥有了一条针对容器列表的只读查询语言。2. --filter 参数详解从全量容器里精准捞人--filter 的用法一句话说清docker ps --filter 键值。可以写多个 --filter一个条件对应一个。多个条件之间的逻辑关系是不同键之间是 AND相同键之间是 OR。比如 --filter statusrunning --filter statusexited 会同时匹配这两种状态相当于没筛但 --filter statusrunning --filter healthhealthy 就要求“状态是运行中”且“健康检查是 healthy”两个条件同时成立。新版本 Docker 还支持不等于写法比如 --filter name!web表示排除名为 web 的容器。这个在实际操作中很实用比如“列出所有不是测试环境的容器”一条命令就能完成反向筛选。要注意的是! 不等于“是否存在某个标签”它只是值不相等的语义。2.1 最常用的几个筛选键id、name、status、exited先说 id。--filter idabc123 会匹配 ID 以 abc123 开头的容器支持短 ID但最短得 3 个字符。这个适合在脚本里精确锁定某个容器即使你不知道全名也没关系。再说 name。这里很多人第一次用会懵--filter nameweb 不是精确匹配而是正则匹配。它匹配的是容器名称包含 web 的容器。想精确匹配得用 --filter name^web$。Go 正则表达式的规则在这里都生效比如 --filter nameweb.* 匹配以 web 开头的容器名。不注意锚点的话你以为筛出来的是 web 容器结果 dev_web_backend、test_web_gateway 全出来了这就是真实的坑。status 直观多了可选值包括 created、restarting、running、removing、paused、exited、dead。比如 docker ps -a --filter statusexited 列出所有已退出的容器这对下一步清理很有用。exited 键用来按退出码筛选比如 --filter exited0 列出正常退出的容器--filter exited1 列出因错误崩溃的容器。要提醒的是如果不加 -adocker ps 默认只看 running 的一个 exited 容器都不会出现所以这两个键通常跟 -a 搭配。2.2 时间与镜像相关的筛选ancestor、before、sinceancestor 这个键官方的含义是“镜像名或镜像 ID”实际用起来有几种方式。按镜像名过滤--filter ancestornginx 会匹配所有基于 nginx 镜像启动的容器包括 nginx:1.25 和 nginx:latest按镜像 ID 过滤--filter ancestord8a3...也支持镜像名加 tag--filter ancestornginx:1.25。这个键在“找出所有用旧镜像启动的容器”场景里特别好用配合 format 列出容器名马上能定位到需要重新部署的对象。before 和 since 是我很喜欢用的一对它们按容器创建时间过滤。before某个容器列出在这个容器之前创建的since某个容器列出在这个容器之后创建的。注意这里接受的值是容器 ID 或容器名不是时间字符串。比如想排查从某次发布开始创建的所有容器可以先找到发布动作对应的容器然后 --filter since那个容器的 ID把之后创建的容器全列出来。两个键还能一起用划出一个时间窗口比如“早上发布前后创建的容器”。2.3 相对冷门但很能打的筛选label、volume、network、publish、healthlabel 筛选在项目化管理时很有用。假设你给容器打了 label比如 projectpayment、envproduction就可以用 --filter labelprojectpayment 把支付项目的容器全列出。不带值的写法 --filter labelproject 匹配所有带有 project 这个 label 的容器不管值是什么。新版还支持 label!projectpayment。这个筛法在规模不大的部署场景里几乎是手工管理容器的一把好手比靠名字正则匹配靠谱得多。volume 和 network 对应容器挂载的卷和加入的网络。--filter volumemy_volume 列出挂载了卷 my_volume 的容器--filter networkmy_net 列出加入网络 my_net 的容器。这两个在排查“哪个容器还在占用某个卷或网络”时非常顺手尤其是想清理自定义网络却提示 still in use 的时候一条 filter 就能把占用者揪出来。publish 和 expose 按端口来筛。publish 匹配已发布到宿主机的端口--filter publish8080 列出宿主机映射到 8080 的所有容器适合排查端口冲突。expose 匹配容器内部暴露的端口--filter expose3306 会列出暴露了 3306 的容器不管有没有发布到宿主机。两者的差别正好对应 Dockerfile 里的 EXPOSE 和 docker run 的 -p 参数理解了这个就不会把两个键混用。health 是配合 HEALTHCHECK 使用的可选 starting、healthy、unhealthy。这个在监控里太常用了docker ps --filter healthunhealthy 直接列出健康检查异常的容器。注意它只对有健康检查配置的容器生效没配置健康检查的容器不会出现在结果里。2.4 filter 的几个边界踩过才知道第一id 匹配是前缀匹配不是正则但 name 是正则不是前缀两者规则不一样很容易记混。第二组合筛选同键是 OR不同键是 AND不是所有条件都严格 AND。想表达“名字叫 web 或者名字叫 db 的容器”可以写两个同键 filter--filter nameweb --filter namedb或者先筛两次再合并。第三filter 是在默认 running 状态的基础上叠加的。所以 --filter statusexited 单独用没有任何输出--filter healthunhealthy 也只能命中运行中的不健康容器这也是很多人排查半天发现列表为空的原因。建议平时把 filter 的组合逻辑记成一句话先有默认限制再叠加条件同键取并集异键取交集。3. --format 参数详解把输出塑造成脚本友好的形状--format 的本质是 Go 模板。Docker 把容器的信息字段注入模板模板渲染后按行输出。很多人一听“模板”就头大其实常用语法就那么几个掌握了基本盘剩下的就是不断加需求、查文档。3.1 模板语法快速上手双花括号里的字段名最小可用的写法docker ps --format {{.ID}} {{.Names}}输出由容器 ID 和名称组成的一行行文本。字段就是前面七列对应的.ID、.Image、.Command、.CreatedAt、.RunningFor、.Status、.Ports、.Names另外还有 .Size、.Labels、.Mounts、.Networks 等扩展字段。有个细节很多人会忽略默认表格输出里 ID 是截断的 12 位但 --format {{.ID}} 输出的是完整 64 位 ID。想输出短 ID得自己截断{{.ID | slice 0 12}}或者用 printf 格式化{{.ID | printf %.12s}}。这个规则记不住的话写脚本时会吃不少亏比如用完整 ID 去匹配别的地方拿到的短 ID怎么都对不上。3.2 字符串拼接与函数管道组合出任何你想要的文本Go 模板的管道符 | 相当于把前一个结果传给后一个函数处理。比如想给输出加个前缀可以写 {{printf 容器名: %s .Names}}。想截断用 slice想按条件输出就用 if 和 eq。例如只想在容器状态包含 Up 时打一个标记可以这么写docker ps --format {{if eq .Status Up}}{{.Names}}{{end}}不过 .Status 是动态文本比如 Up 2 hours、Exited (0) 5 minutes ago直接用 eq 匹配很可能失败。这时候可以用 contains 判断{{if contains .Status Up}}。注意 contains 在 Docker 模板里是支持的但大小写敏感匹配时得看清状态文本的实际样子。日期格式是最常用的处理点。.CreatedAt 输出的是 RFC3339 格式比如 2024-05-20T14:30:00Z对人眼不友善。Docker 的模板内置了 date 函数可以按 Go 的时间布局格式化{{.CreatedAt | date 2006-01-02 15:04:05}}。这个布局字符串很反直觉2006-01-02 15:04:05 是 Go 规定的参考时间记住照着写就行想要别的格式再调整年月日时分秒的相对位置。3.3 table 前缀与自定义表头既要对齐又要好看--format 直接写模板输出的行不会对齐因为少了默认表格的 tabwriter 对齐过程。想要对齐有个近乎作弊的写法在模板最前面写 table比如 docker ps --format table {{.ID}}\t{{.Names}}。这样 ID 和容器名的列会自动对齐还带表头。默认表头是字段名比如 NAMES、ID如果想换成中文或者自定义名称第一行自己写第二行再写数据行docker ps --format table 容器ID\t容器名称\n{{.ID}}\t{{.Names}}注意这里的 \n 在单引号内会生效如果写成双引号Shell 会先把 \n 吃掉变成空格模板就废了。这个细节坑过不少同事。另一个实用技巧是如果模板不是以 table 开头输出就是普通的“每条记录一行字段间按分隔符排列”。这种格式最适合管道处理。用 | 做分隔符比用空格或 tab 更稳因为字段值本身可能包含空格例如镜像名和状态文本。3.4 JSON 输出一条命令把容器列表变成结构化数据--format 里最容易被忽略的是 {{json .}}。这条模板会把当前容器的所有字段以 JSON 对象形式输出。写成 docker ps --format {{json .}}每一行是一个容器的完整 JSON。配合 jq 工具docker ps 的输出就被结构化、可编程了。比如要把容器名和镜像名提取成 tab 分隔的表格可以先跑一遍 {json .} 看字段结构再写 jq 表达式筛选。另外模板里还可以用 index 函数取 map 的指定 key比如某个 label 的值{{index .Labels project}}。这在按 label 维度聚合输出时很好用比如把每个容器所属项目和状态一起列出来比默认表格更贴合自己的需求。4. filter format 组合实战这些场景我天天用单独学 filter 和 format 不难真正的价值在组合。下面几个场景都是我在日常干活时反复用到的可以直接抄。4.1 场景一找出所有不健康容器并批量重启先列出所有健康检查不通过的容器确认哪些是需要处理的docker ps --filter healthunhealthy --format table {{.Names}}\t{{.Status}}确认无误后批量重启for name in $(docker ps --filter healthunhealthy --format {{.Names}}); do echo 重启容器: $name docker restart $name done注意 health 筛选只在容器配置了健康检查时有效而且对状态为 exited 的容器无效想排查退出容器里的不健康状态得先 -a 看全部容器。这个场景用在监控告警后的自动恢复脚本里非常稳但建议加一个重试次数限制避免容器一直起不来造成死循环。4.2 场景二按启动顺序梳理依赖链容器之间有先后依赖时用 before/since 可以按创建时间查看。假设某个中间件容器在早上 8 点挂了我想看它之前有哪些容器、之后有哪些容器docker ps -a --filter before中间件容器ID --format {{.Names}} {{.CreatedAt | date 15:04:05}} docker ps -a --filter since中间件容器ID --format {{.Names}} {{.CreatedAt | date 15:04:05}}这里 -a 不能省否则早退出的依赖容器不会出现在结果里。还有一个容易忽略的点before 和 since 按的是创建时间不是启动时间。如果某个容器创建得很早但最近重启过用 before 定位它的上下文会误导人。想按真实启动时间排序还是要用 docker inspect 拿 .State.StartedAt再配合 sort 命令处理。4.3 场景三按项目标签批量清理我习惯给每个项目容器打 label比如 projectorder、projectpay。要一次性看某个项目所有容器状态docker ps -a --filter labelprojectorder --format table {{.Names}}\t{{.Status}}\t{{.Ports}}批量清理该项目的退出容器docker ps -a --filter labelprojectorder --filter statusexited --format {{.ID}} | xargs -r docker rmxargs 加 -r 是为了防止没有结果时报错。label 方案比用 name 正则匹配靠谱得多因为名字可以乱起但 label 是你自己定的规范。项目里多个环境用 envstaging、envproduction 再叠加 project 标签就能做到一套命令管多个环境。4.4 场景四定位端口占用“80 端口被谁占了”是高频排查题。一条命令docker ps --filter publish80 --format {{.Names}} {{.Ports}}如果宿主机上还有非容器进程占用 80就得配合 ss -lntp 看。容器侧的定位用这条就够了。如果想排查某个新容器要用的端口是不是跟现网容器冲突可以在部署前先跑一遍把可能冲突的容器列出来省得启动时报 bind 失败再去翻日志。4.5 场景五生成巡检报告日常巡检时我经常要输出一份容器清单交给监控存档docker ps -a --format {{.Names}}|{{.Image}}|{{.Status}}|{{.CreatedAt | date 2006-01-02}} | sort -t | -k 4用管道分隔符避免空格干扰再用 sort 按日期排序这样一份报告就有了基本形态。如果监控系统要 JSON直接把模板换成 {{json .}}再配合 jq 转成自己的上报格式非常顺滑。5. 摸爬滚打出来的常见问题与避坑清单5.1 常见问题速查表把上面提到的坑整理成一张表遇到问题直接查。场景推荐写法坑与建议列出退出容器docker ps -a --filter statusexited不加 -a 永远为空清除正常退出的容器docker ps -a --filter exited0 -qexited0 表示退出码为 0精确匹配容器名--filter name^web$name 是正则不加锚点会多匹配按镜像找容器--filter ancestornginx支持镜像名、镜像 ID、tag组合多个条件--filter statusrunning --filter healthhealthy不同键是 AND相同键是 OR反向排除容器--filter name!web不等于语法注意 Shell 转义输出对齐--format table {{.ID}}\t{{.Names}}直接写模板不会自动对齐输出短 ID--format {{.IDslice 0 12}}时间可读化--format {{.CreatedAtdate 2006-01-02 15:04:05}}按卷找容器--filter volumemy_volume卷名要准确别名不认按网络找容器--filter networkmy_net适合排查网络占用按端口找容器--filter publish80只匹配宿主机已发布端口结构化输出--format {{json .}}配 jq 使用先跑一次看字段名5.2 几个反复踩的坑第一个坑docker ps -q 和 --format 混用。有同事问我为什么 docker ps -q --format {{.Names}} 输出的还是 ID因为 -q 本质上是 --format {{.ID}} 的语法糖一旦用了 -q后面给的 format 不生效。脚本里二选一别写一起。第二个坑命令里有感叹号或特殊字符时双引号里的 ! 会被 Shell 历史展开搞乱。用单引号包模板基本不会错尤其在写 --filter name!web 这类反向筛选时更要注意。第三个坑--format 里写错字段名比如 {{.NotExist}}不会报错而是输出 。这种静默失败在脚本里非常隐蔽会导致后续逻辑误判。查模板字段时可以先跑一遍 {{json .}} 看真实字段名再决定怎么写模板。第四个坑before 和 since 是按创建时间过滤不是启动时间。这个问题我前面提过但实在太容易踩必须再强调一次。想按实际启动时间过滤请走 docker inspect 路线。第五个坑模板里的 {{.Status}} 是动态文本。拿它做 eq 判断很危险比如 Up 5 minutes 和 Up 2 hours 都不是 Up。要么用 contains 判断前缀类内容要么干脆根据 exit 状态去 sort别想着用 eq 一锤定音。我个人在实际操作中已经把常用组合固化成了别名和脚本。比如别名里加一个alias dpsdocker ps --format table {{.ID | slice 0 12}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}} alias ddowndocker ps -a --filter statusexited --format {{.ID}} | xargs -r docker rm跑起来顺手的命令才叫工具。docker ps 的 filter 和 format 绝对是命令行管理容器时投入产出比最高的一小块技能花半小时彻底吃透后面能省下无数个小时的肉眼排查时间。最后再分享一个小技巧别光在交互终端里用把它写进部署脚本、巡检脚本、清理脚本这俩参数的价值才真正发挥出来。
阅读完成 · 觉得有帮助?
咨询建站