ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Docker ps 命令详解:参数、格式化过滤与容器运维排错

Docker ps 命令详解:参数、格式化过滤与容器运维排错 docker ps 这条命令大概是所有人接触容器技术时敲得最多、也最容易被一带而过的一条。装完环境第一件事是敲它确认服务有没有正常起来容器跑不起来第一反应还是加个 -a 翻一遍历史。它看起来简单到不需要解释——不就是列个表吗可真到了线上排查问题的时候很多人盯着那张只有容器 ID、镜像、状态、端口四列的默认输出愣是看不出容器到底为什么退出、端口为什么连不上、哪个容器在偷偷吃磁盘空间。这篇内容我把 docker ps 从参数到格式化、从过滤到排错完整拆一遍顺带聊聊几个只有踩过坑才知道的细节。不管你刚装完环境想确认是否正常还是已经在维护几十个容器、需要把这条命令接进脚本和监控下面这些应该都能直接用上。读完你至少能做成两件事看状态列就能大致判断容器死因用 format 和 filter 把默认那张表改造成适合自己工作流的形态。1. 先搞清楚 docker ps 到底在列什么1.1 一个“查看”命令为什么值得单独拆很多人对 docker ps 的理解停留在“容器版的 ls”觉得它没有任何技术含量。这个判断在只有一两个容器的时候成立一旦容器数量上到两位数、或者容器生命周期短到几秒钟就退出这条命令的信息密度就会突然变得极其关键。原因在于容器本身是个黑盒进程你没有办法像查普通进程那样直接 attach 上去看内部状态docker ps 输出的那几列实际上是守护进程从内部数据结构里摘出来的、关于这个容器当前处境的全部公开线索。我举个具体的例子。一个容器反复重启你在默认输出里看到的是Restarting (1) 5 seconds ago。这个(1)是退出码1意味着应用层自己报错退出而不是被信号杀掉如果是(137)那是 128 加 9说明进程收到了强制终止信号常见于内存超限被系统干掉。同样一列文字懂和不懂的人排查方向完全相反。所以这条命令的价值不在于“能看”而在于“看得懂”它把容器的创建时间、运行时长、端口绑定、健康检查结果、退出原因全部压缩进几行文本里你需要的是一套解读方法。另外一层原因是可编程性。docker ps 支持结构化的过滤和模板输出意味着它不只是给人看的还能直接喂给 shell 脚本、监控采集器、告警系统。日常运维里大量重复动作——批量停掉某个镜像的所有容器、找出所有退出码非零的实例、清理超过一定时间的僵尸容器——底层几乎都靠这条命令先筛出目标集合。把它的参数吃透后面写脚本能省掉一大半的解析逻辑。1.2 默认输出那几个字段各自意味着什么敲一次不带任何参数的 docker ps出来的是七列容器 ID、镜像、启动命令、创建时间、状态、端口、名称。这七列不是随便排的从左到右基本遵循“从唯一标识到人类可读描述”的顺序。容器 ID 是十二位短哈希足够日常区分但真要拿去写进配置文件或者做精确匹配建议用--no-trunc拿完整的六十四位短 ID 在容器数量多的时候存在极小概率的前缀碰撞虽然概率低但脚本里因为这种问题排查半天不划算。镜像列显示的是容器创建时所用的镜像名注意这里不会告诉你镜像后来有没有被更新过容器一旦创建它的镜像引用就固定了所以看到镜像标签是 latest 也别以为它一定是最新的。启动命令列默认会被截断长命令后面跟省略号想看全得加--no-trunc。创建时间列是相对时间比如“5 minutes ago”这个相对时间在跨时区协作或者写进日志的时候容易产生歧义用格式化输出改成绝对时间更稳妥。状态列信息量最大它同时包含了运行时长、健康检查结果和退出原因三部分。端口列只在容器做了端口映射时才有内容格式是“宿主地址:宿主端口-容器端口/协议”IPv4 和 IPv6 会各占一条用冒号分隔。名称列是容器名不指定的话系统会随机生成一个由形容词加人名组成的组合正式环境建议自己命名否则批量操作时根本认不出谁是谁。1.3 命令家族与版本兼容的那点事早些年这条命令只有docker ps一种写法后来命令行做了重组按对象类型归类于是多出了docker container ls和docker container list两个等价写法。三者执行结果完全一致底层走的是同一个接口区别只在于语义清晰度。新写法在脚本里更推荐因为它明确表达了“我在列容器对象”可读性更好而docker ps胜在短日常手敲更省事。两种写法混用完全没问题不用纠结统一。需要留意的是版本差异。老版本里有些选项的行为和新版本不完全一致比如--format支持的字段在不同大版本之间有过增补健康检查相关的状态字段是后来才加的。如果你在写跨环境的部署脚本最好先确认目标环境的版本用一个最小字段集做兼容别一上来就用最新字段否则换台机器就报模板解析错误。我一般的做法是脚本里只依赖容器 ID、名称、状态、镜像这四个最稳定的字段需要更多信息时再单独查一次。2. 参数逐项拆解哪些每天都在用哪些关键时刻才想起2.1 控制显示范围-a、-l、-n、-q 的取舍默认行为只列正在运行的容器这一点新手最容易忽略。容器退出了docker ps里就看不见了于是很多人第一反应是“我的容器没了”其实它只是停了加-a就能看到全部包括已退出、已创建但没启动的。这个-a是使用频率仅次于默认命令的选项排查问题的第一步永远是docker ps -a先确认容器到底是没创建、创建了没起来、还是起来又挂了。-l是只显示最近创建的那个容器-n后面跟数字显示最近创建的若干个。这两个选项在交互式操作里很顺手比如你刚docker run完一个容器想确认状态docker ps -l比翻全表快得多。但它们按创建时间排序不是按启动时间容器重建之后顺序会变脚本里依赖这个顺序要小心。-q只输出容器 ID是批量操作的标配。配合命令替换可以直接把结果喂给其他命令比如停掉所有运行中的容器一行就够。这个选项的输出是纯净的 ID 列表没有表头没有对齐空格天然适合管道。我见过有人在脚本里用docker ps加一堆文本处理去提取 ID其实一个-q就解决了省下来的解析逻辑全是潜在的 bug 来源。2.2 看清全量信息--no-trunc 与 -s 的使用场景--no-trunc的作用是不截断输出主要影响三列容器 ID 变成完整哈希启动命令完整显示镜像摘要也完整展示。什么时候必须用一种是要把容器 ID 写进审计记录或者工单系统短 ID 不够严谨另一种是排查启动命令问题时被截断的那半截命令恰恰是关键比如一个带了很多参数的 Java 启动脚本省略号后面可能藏着错误的环境变量。-s会额外加两列显示每个容器的可写层大小和虚拟磁盘总大小。这个选项要慎用因为它需要向守护进程查询每个容器的存储占用容器数量多的时候会明显变慢我在几十个容器的环境里试过加上这个参数响应时间能从几十毫秒涨到一两秒。它的正确用法是定点排查比如磁盘告警了用它找出哪个容器的可写层异常膨胀而不是把它写进日常巡检脚本里天天跑。另外注意可写层大小只统计容器自己写入的数据挂载的卷不计算在内所以看到数字很小但磁盘还是满的要去查卷的占用。2.3 参数组合时的优先级与相互影响有几个组合需要提前说清楚避免用的时候犯迷糊。-q和--format同时出现时--format优先因为格式化是更明确的输出意图-q只是它的一个简化特例。-a和-f statusexited组合时过滤条件在全集上生效不用额外再加-a因为状态过滤本身就隐含了“要看已退出的”但为了语义清楚我一般还是会把-a写上。-l和-n同时出现时以-n为准。--filter多个条件之间的默认关系是“与”也就是必须同时满足这点和很多人直觉一致但涉及名称过滤的正则匹配时容易踩坑后面过滤那一节会细说。还有个隐性的坑多个--filter如果用了同一个键、不同值比如两个status过滤实际效果是“或”而不是报错这个行为在不同版本上表现一致但文档里写得不显眼值得记一下。3. --format 实战把列表变成自己的运维看板3.1 模板语法的门槛其实比想象中低--format用的是 Go 模板语法听起来吓人实际常用的就三样东西字段引用、制表符、换行。字段引用写成点加字段名比如容器 ID 就是.ID名称是.Names状态是.Status。要在同一行拼多个字段中间用\t分隔这是制表符的转义写法能让输出自动对齐成表格。最常用的写法是在模板前面加table关键字它会自动补上表头并按列对齐。不加table的话就是纯文本逐行输出适合喂给脚本。我第一次用的时候不知道有这个关键字手动用空格对齐了半天结果字段长度一变全乱后来发现一个 table 就解决了所有对齐问题。值得强调的是模板里可以塞普通文本和符号比如给每行加个前缀、在字段之间加竖线做视觉分隔、甚至拼出类似 JSON 的结构。这让同一条命令既能输出给人看的报表也能输出给程序解析的数据不用再写两套逻辑。3.2 常用字段清单与拼接技巧日常用得最多的字段就那么几个我把它们和默认列做个对应方便迁移习惯。.ID对应容器 ID.Image对应镜像.Command对应启动命令.CreatedAt对应创建时间.RunningFor是运行时长.Ports是端口.Names是名称.State是简短状态词.Status是带细节的完整状态描述。区分.State和.Status很重要前者只有 running、exited 这种单词适合做条件判断后者带时长和退出码适合给人看。还有几个不那么常用但很有价值的字段。.Labels输出容器的所有标签做标签化管理的团队可以用它快速核对.Mounts列出挂载信息排查卷映射问题时直接看.Networks显示容器接入的网络.LocalVolumes是本地卷数量。健康检查相关的状态在.Status里以括号形式体现想要单独取出来需要一些字符串处理模板本身不支持条件分支的复杂逻辑这时候就退回 shell 去切。拼接上我常用的一个套路是给输出加统一前缀比如在每行前面加上时间戳方便和其他日志合并。模板里直接写死文本没问题但动态时间戳得靠 shell 变量传进去命令替换和引号转义要小心双引号里套单引号、再套反引号是最容易出错的组合我一般先用 echo 把完整命令打出来确认转义正确再去执行。3.3 面向脚本的输出从人读到机器读人看的表格和机器解析的格式要分开设计。给人看的时候列宽对齐、表头清晰、信息密度适中给脚本用的时候字段越少越好、分隔符越明确越好、不要表头。我的习惯是维护两个模板一个用于日常查看包含名称、状态、端口、镜像四列另一个用于脚本只输出 ID 和名称用冒号分隔方便后面用 cut 或者 read 拆。举个实际用法批量重启某个镜像下的所有容器先筛出目标再循环操作整个过程不用碰任何解析 HTML 或者 JSON 的库。这里的关键是让 docker ps 的输出格式固定下来脚本里只依赖位置不依赖文本匹配稳定性会高很多。如果你需要处理容器名里带空格或者特殊字符的情况记得在 shell 循环里用 while read 而不是 forfor 会按空格拆分遇到带空格的名称直接崩。4. --filter 过滤从一堆容器里精准捞针4.1 常用过滤键与表达式写法过滤是这条命令里最被低估的能力。常用的过滤键有七八个按状态status、按名称name、按镜像ancestor、按标签label、按退出码exited、按端口publish和expose、按网络network、按卷volume、按创建时间先后before和since。写法统一是-f 键值等号两边不要加空格否则会被 shell 当成两个参数。名称过滤用的是子串匹配不是精确匹配也就是说-f nameweb会同时命中 web、web-1、my-web-test。想要精确匹配得用正则的锚定写法加^和$。这一点在批量操作里特别危险你以为只选中了一个容器实际选中了一批。我吃过一次亏清理测试容器的时候用了宽泛的名称匹配结果把同前缀的生产容器一起停了从那以后所有涉及写操作的过滤一律加锚定。按镜像过滤要理解它的语义匹配的是容器创建时引用的镜像包括该镜像的所有历史版本关系。用镜像名过滤时同名不同标签的容器都会被选中用镜像 ID 过滤则更精确。如果你不确定某个容器到底基于哪个镜像ancestor配合完整 ID 最保险。4.2 多条件组合的与或关系默认多个过滤条件之间是“与”也就是取交集这个符合直觉。但有两种情况会打破这个默认。第一种是同一个键给多个值比如两个status条件实际是“或”的关系等于列出状态 A 或者状态 B 的容器。第二种是部分版本对label键的处理同一个标签键给不同的值行为可能和预期有出入。稳妥的做法是遇到多值场景先在测试环境验证一下结果集别直接写进自动化流程。组合的实用例子列出所有已退出且退出码非零的容器用两个条件就行statusexited加上exited1之类别。再比如找出某个网络下、某个镜像的所有运行中容器三个条件叠加一行命令就能把目标锁定到一个很小的集合后面接操作命令风险低很多。条件越多集合越小出错的代价也越小这是我写运维脚本时的一条基本原则。还有个容易忽略的点过滤是作用在命令看到的全集上的-a影响的是默认展示范围但一旦你用了状态过滤范围会自动扩展到匹配该状态的容器不需要额外加-a。理解这一点能少写很多冗余参数。4.3 状态过滤与健康检查的细节状态过滤常用的值有 running、exited、created、paused、restarting、dead。这里要特别注意 paused 和 restarting 这两个中间态它们既不是完全活着也不是完全死了很多监控脚本只判断 running结果漏掉了卡在 restarting 里的容器。我一般会单独维护一个“异常状态”过滤把 restarting 和 dead 都拉出来告警。健康检查的判断不在状态过滤里它藏在完整状态描述里。一个容器可能是 running 但健康检查结果是 unhealthy这种情况在默认视图里只有状态列的括号能看出来。做健康巡检的时候不能只看statusrunning还得对输出做二次判断或者用格式化把完整状态取出来后匹配 unhealthy 关键字。这块是我觉得这条命令设计上稍微别扭的地方希望以后能直接支持健康状态的过滤键。5. 几个真实场景的排查过程5.1 容器启动就退出从状态列读出死因容器起来几秒钟就退出是最高频的问题。排查顺序我固定为三步。第一步docker ps -a找到那个容器重点看状态列的退出码。退出码 0 表示进程正常结束多半是启动命令跑完就退出了比如一个前台进程写成了后台执行或者脚本里缺了保持前台运行的指令。退出码 1 是应用自己报错得去看日志。退出码 126 和 127 分别是命令不可执行和命令找不到通常是启动命令写错或者镜像里没有这个可执行文件。第二步是如果退出码是 137说明进程收到了强制终止信号最常见的诱因是内存超过限制被系统终止也可能是有人手动发了终止命令。这时候去看容器的资源限制配置和宿主机的内存水位基本能定位。退出码 143 对应的是正常的终止信号一般是容器被优雅停止属于正常退出范畴。第三步才去看日志。很多人一上来就翻日志其实状态列已经把大方向指明了先读退出码能省掉大量无效翻找。这个习惯我建议所有人都养成尤其是在容器数量多、日志量大的环境里。5.2 端口映射异常从端口列还原绑定关系服务连不上端口问题占很大比例。默认输出的端口列写得很紧凑格式是宿主地址冒号宿主端口箭头指向容器端口和协议。看这一列要关注两件事绑定地址是什么宿主端口是多少。绑定地址显示为全零地址表示监听所有网卡显示为回环地址则表示只有本机能访问外部当然连不上。这个区别在本地开发时经常被忽略容器里服务正常、本机 curl 也通换个机器就是不通原因往往就在这里。另一个高频问题是宿主端口冲突或者没映射。容器内部的端口是容器自己的不映射到宿主外面根本摸不到。想确认某个端口被谁占了用端口过滤能快速定位到具体容器。如果发现同一个宿主端口出现多条映射记录说明有容器启动失败或者配置写重复了这时候要结合状态列看哪个是真的在运行。5.3 批量清理前的安全确认清理容器是危险操作尤其是带数据卷的。我的固定流程是先列后删绝不直接删。先用过滤把目标集合列出来加上完整的状态、名称、镜像、创建时间四列人工扫一遍确认没有误伤再执行删除。列表这一步省不得我曾经写过一个自以为很稳的过滤条件结果因为名称匹配太宽泛选中了不该选的容器好在先列后删的习惯救了我一次。对于已经退出的容器清理前还要确认它挂载的卷有没有数据。容器删了卷默认保留但如果当初是用自动清理方式启动的卷会跟着走。这个判断靠容器详情里的挂载信息光看列表不够得再查一次。批量操作里我一般会加一个确认环节把要删的容器数量打印出来数量超过预期就中止人眼过一遍再放行。6. 常见问题与踩坑记录6.1 问题速查表现象可能原因排查动作容器列表里找不到刚启动的容器容器已退出默认只显示运行中加-a查看全部状态显示 Restarting 反复出现应用启动失败触发重启策略看退出码和日志确认重启策略是否合理状态显示 Up 但服务不通端口未映射或绑定地址受限检查端口列和绑定地址状态含 unhealthy健康检查探测失败单独查健康检查配置和探测日志容器 ID 短哈希重复前缀碰撞容器数量大用--no-trunc取完整 ID格式化输出报模板错误字段名写错或版本不支持确认字段拼写和目标环境版本名称过滤选中了多余容器子串匹配未加锚定用锚定写法做精确匹配磁盘满但可写层很小数据在挂载卷里单独查卷占用6.2 几条不太容易在网上找到的经验第一条别把docker ps的调用频率开太高。有些监控方案图省事每隔一两秒轮询一次容器数量一多守护进程的响应压力会上来极端情况下会拖慢其他操作的响应。轮询间隔我一般设在十秒以上真要秒级感知状态变化应该走事件订阅而不是轮询。第二条脚本里永远不要依赖默认输出的列顺序和对齐。默认表格是为了人眼优化字段之间的空格数量会随内容长度变化用文本切割去解析必然翻车。要解析就用--format固定分隔符或者干脆用-q只拿 ID。第三条涉及写操作的过滤条件先在末尾加一个--format把目标打印出来跑一遍。这个习惯看起来多一步实际能挡掉绝大多数误操作。我在好几个环境里把这个动作做成了肌肉记忆宁可慢十秒不要让一次误删变成事故。最后分享一个我自己常用的小组合一条命令打印出所有容器的名称、完整状态、镜像和创建时间按创建时间倒序专治“我记得刚跑过一个什么但不知道叫啥”的健忘症。字段不多信息够全扫一眼就能定位。容器多起来之后这种自己顺手的小命令比任何复杂的监控面板都来得快。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表