做Docker这几年我最常被问到的不是“这个命令什么意思”而是“我照教程敲了为什么跑不起来”。其实绝大多数问题都出在同一个地方对Docker命令背后的对象模型不够熟。镜像、容器、网络、存储卷这四类对象各有各的生命周期命令再多也就是对这四类对象的增删改查。这篇就把我日常用得上的Docker命令按场景重新捋一遍从装好Docker到生产环境排障每条命令都会说清楚它解决什么问题、什么时候用、坑在哪。新手可以按顺序读老手建议直接跳到第6节的排障速查表。1. 先搞明白Docker命令从来不是背出来的而是一套对象操作逻辑接触过虚拟机的人上手Docker通常会觉得很顺手因为思路很像装系统、装软件、打快照、克隆。但Docker真正好用的点在于它把“环境”本身拆成了可移动、可复制的对象。你拉一个镜像等于下载了一份完整的运行环境模板你启动一个容器等于基于这个模板开了一个实例你把某个目录挂进容器等于让实例里的文件跟宿主机实时同步。理解了这个模型后面所有命令都只是“在哪个对象上做什么动作”的选择题。1.1 从“装个软件”到“跑一个环境”Docker到底改变了什么以前部署一个新项目流程大概是买服务器、装系统、装数据库、装运行时、配环境变量、调防火墙、试运行、再调、再试。这一套下来小半天就没了而且每一步都可能因为系统版本、依赖冲突、端口占用而卡住。换成Docker之后部署这个动作变成了两句话拉镜像起容器。我举个具体的例子。你需要在服务器上跑一个MySQL 8.0传统装法要处理yum源、初始化密码、配置字符集、开放端口一不小心还能把系统自带的MariaDB冲突给踩出来。Docker的装法是这样docker pull mysql:8.0 docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -v /data/mysql:/var/lib/mysql \ mysql:8.0两条命令做完数据库就起来了数据落在宿主机的/data/mysql目录里密码通过环境变量注入。换一台机器同样的两条命令一模一样的环境。这就是“环境即代码”的直观感受。Docker命令的价值不在于“能敲出什么”而在于你使用命令时是否清楚这一条命令在改变哪个对象、影响哪些范围。1.2 Docker命令的全景地图镜像、容器、网络、存储我自己总结过一个粗线条的划分镜像命令以docker image开头容器命令以docker container开头网络命令以docker network开头存储命令以docker volume开头。新版本官方鼓励使用这种“对象动作”的完整写法但为了敲起来快很多简写也保留着比如docker ps其实就是docker container lsdocker run本质上是docker container run的简写。初学者常犯的一个毛病是把命令记混比如docker rm和docker rmi一个删容器一个删镜像拼写只差一个字母。我见过不止一次有人想删镜像结果把正在跑的容器给删了虽然通过rmi删镜像时它会提示容器还在运行但如果你先手动rm了容器接着又去rmi一旦镜像被多个容器引用Docker会拒绝删除并提示冲突。这些细节光靠背命令是记不住的必须在实际使用中踩过一次才有感觉。所以这篇我不会做成“字典式命令罗列”而是按照一条真实的使用路径来讲装环境、拉镜像、跑容器、配网络、挂存储、上生产、排故障。每一步涉及哪些命令我会把参数讲透顺带说清楚哪些命令我实际生产中很少碰哪些是一定要刻进肌肉记忆的。2. 环境准备安装、守护进程与基础姿势在敲任何Docker命令之前你得先确保Docker本体是健康的。很多人拿Linux服务器练手装完Docker就直接docker run结果报一堆莫名其妙的错最后发现是Docker服务压根没启动。这个问题比你想的常见得多。2.1 不同系统上的安装方式与注意点Linux上最省事的安装方式是用官方脚本但生产环境我建议走发行版自带的包管理这样后续升级和依赖管理都更可控。Ubuntu和Debian系的安装可以这样处理# 更新索引并安装依赖 sudo apt update sudo apt install -y ca-certificates curl gnupg # 添加Docker官方GPG密钥和源 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-pluginCentOS系则是用yum或新版系统的dnf添加源后安装docker-ce。这里有个很容易被忽略的点装完Docker之后默认只有root用户能和Docker守护进程通信普通用户直接敲docker命令会报“permission denied”。解决办法是把用户加进docker组sudo usermod -aG docker $USER # 退出终端重新登录组权限才会生效你可能会在网上看到别人建议改/var/run/docker.sock权限来绕过去我不建议那么做那等于把Docker的管理权限对所有人敞开了存在被人借权限做危险操作的可能比如挂载宿主机根目录到容器里面改你的系统文件。Windows和macOS上基本就是安装Docker Desktop一个图形界面跑完所有事。但Windows上有个前提条件容易栽跟头CPU的虚拟化功能得开着Hyper-V或者WSL2要处于可用状态。不少人卡在安装后的下一步双击Docker Desktop图标状态栏提示“virtualization support not detected”这就是虚拟化没开。2.2 安装后必做的三个检查命令装完Docker别急着拉镜像先确认三件事Docker服务是不是活着、命令行能不能连通守护进程、当前版本号是多少。# 查看Docker服务状态systemd环境下 systemctl status docker # 或者直接看docker info这个命令同时会给出版本、存储驱动、镜像数、容器数等一大堆信息 docker info # 单独确认版本 docker version我给新人讲这三个命令时通常会强调docker version不像别的软件那样只打印一个版本号它分两段输出一段是Client客户端版本一段是Server服务端版本。如果Server那段是空白或者报错Unavailable说明你的命令行根本没连上Docker守护进程后面的命令大概率全是“Cannot connect to the Docker daemon”。服务端没起来就systemctl start docker套接字权限不对就从用户组和socket文件两个方向排查。还有个小习惯把当前用户加进docker组之后重新登录终端先敲一句docker info看看有没有继续报权限错。这一步确认干净了后面所有命令才能顺畅执行。2.3 启动失败的常见原因与处理路线接手过的机器里Docker Desktop启动失败最常出现的报错有几种。一种是刚才说的“virtualization support not detected”多半是BIOS里Intel VT-x或者AMD-V没开进BIOS找Intel Virtualization Technology或者SVM Mode这类选项打开就行。另一种是提示WSL2相关错误比如“WSL 2 installation is incomplete”这种一般要去启用Windows的“适用于Linux的Windows子系统”和“虚拟机平台”两个功能然后重启加载WSL2内核。再有一种报错很磨人“failed to connect to the docker api at npipe:////./pipe/dockerdesktoplinuxen...”。npipe是Windows上的命名管道出现这个报错一般意味着Docker Desktop的后端引擎还处于启动中或者已经崩溃。我自己的处理顺序是先右键托盘图标看引擎是否Running不行就Quit Docker Desktop后重新打开再不行去命令行执行docker context ls看看当前上下文绝大多数情况下切换回default上下文或者重启引擎能解决。Linux服务器上则常见另外几种启动失败比如镜像存储目录磁盘满了、daemon.json配置有语法问题、selinux或防火墙拦截了容器流量。排查统一用journalctl -u docker看守护进程日志能直接定位到具体原因。3. 镜像操作命令从拉取到瘦身镜像就是容器的模板。你对模板操作得熟练容器才能真正跑得干净、跑得稳定。拉镜像谁都会真正拉开差距的是对镜像细节的把控版本标签、多架构、层结构和体积。3.1 拉取、查看、打标签与删除镜像拉镜像最常用的是docker pull后面跟镜像名加版本标签。比如# 拉取最新版 docker pull nginx # 实际上等于拉了nginx:latest # 拉取指定版本 docker pull mysql:8.0 # 拉取指定平台的镜像这在Mac或者ARM服务器上非常有用 docker pull --platform linux/amd64 mysql:8.0别小看latest这个标签它是很多事故的根源。latest会随官方仓库更新而漂移今天拉的和下周拉的可能不是同一个版本。凡是线上跑着的服务镜像标签必须钉死到具体版本号甚至再严谨一点钉到镜像摘要Digest——就是镜像ID那一长串sha256值比如mysqlsha256:xxxx漏掉一个字节都不影响唯一指向。查看本地镜像和删除镜像的常用命令# 列出本地镜像 docker images # 只看镜像ID适合写脚本 docker images -q # 给镜像打标签本质是给同一个镜像加一个引用 docker tag nginx:latest myapp/web:v1 # 删除镜像 docker rmi nginx:latest删除镜像这里有个连环坑docker rmi删除的是“标签”不是删除镜像内容所以当你用同一个镜像打过好几个tag删掉其中一个标签本地镜像库里可能还剩着其他标签对应的同一份镜像层。如果多个镜像共用了父层只有所有引用都被删干净那些共用层才会真正被清除。我见过有人在服务器上docker rmi之后发现磁盘空间几乎没有释放就是这个原因。3.2 镜像导出save与load离线部署的黄金搭档生产环境里很多服务器是不能直接访问公网镜像仓库的。这时候把镜像从能上网的机器导出再拷进内网机器导入就是绕不开的操作。# 把镜像保存成tar包 docker save -o nginx.tar nginx:1.26 # 从tar包加载镜像 docker load -i nginx.tar我习惯在打包的时候打个压缩毕竟上G的镜像当裸tar拷来拷去很浪费带宽docker save nginx:1.26 | gzip nginx-1.26.tar.gz # 加载时直接解压 gunzip -c nginx-1.26.tar.gz | docker load这里有个最容易被坑到的地方docker save产生的tar包后续在另一台机器上load回来镜像名和标签是跟着走的但如果源机器上镜像名写得不规范、带了一堆临时tagload出来的镜像也会是那副乱糟糟的样子。所以做离线包之前先docker tag把目标命好再save。不然到达目标机器上docker images看到一堆没有规律的名字想run的时候还得猜。3.3 不要随便commit镜像构建镜像的正确姿势docker commit可以把一个运行中的容器状态保存为新镜像。网上有些教程会让你在容器里手工装了一堆软件后commit一下把这个容器变成“万能镜像”我不建议大家在生产上这么干。为什么因为commit生成的新镜像只有一部分状态你手工在容器里改动的过程没有沉淀成可重复执行的步骤文档别人接手的时候完全不知道这个镜像里装了什么东西、改过什么文件。镜像变得像一个黑盒出问题只能靠拆解调试没法重放。正确的构建姿势是Dockerfile加docker builddocker build -t myapp:v1 .Dockerfile把“从哪个基础镜像开始”“装什么依赖”“复制哪些文件”“启动时跑什么命令”都明文写死在文件里整个构建过程可重放、可审计、可code review。生产环境里Dockerfile就跟源码一样重要丢了它等于丢了镜像的“灵魂”只剩下一个快照壳子。4. 容器核心命令run起来只是开始镜像是模板容器才是真正干活的实例。docker run是所有人最早接触的命令但大多数人只用了其中三四个参数。我会把run的每个常用参数当一组来拆然后讲清楚容器起来之后怎么管理和交互。4.1 docker run的黄金参数矩阵一条典型的docker run可能是长这个样子的docker run -d \ --name app-server \ -p 8080:80 \ -v /data/app:/app/data \ -e ENVproduction \ --restart unless-stopped \ --network mynet \ --cpus 1.5 \ --memory 1g \ nginx:1.26一个一个拆开看。-d是后台运行不加的话容器会在前台占用你的终端日志刷屏刷到天荒地老CtrlC直接就停掉容器了。实际部署服务几乎都会加-d。--name给容器起名字好记也好用后续日志、查看、删除都用这个名字比容器ID好记太多了。-p把宿主机的端口映射到容器的端口格式是“宿主机端口:容器端口”。前面8080是宿主机对外提供的端口后面80是容器里nginx监听的端口。生产上端口规划是个大活有时候为了省事直接-p 80:80但一旦多服务同机部署端口冲突就冒出来了所以规划时最好显式区分。-v挂载数据卷左边是宿主机目录右边是容器目录。这样容器可以被删掉重建但数据还留在宿主机上这就是“容器无状态、数据有状态”的基本盘。-e注入环境变量MySQL的root密码、连接串、各种开关配置都靠这里传。很多人忽略的一点是环境变量里放密码这类敏感信息生产环境更推荐用docker secret或者外部密钥管理方案直接写进命令会出现在命令行历史里有一定风险。自己实验环境无所谓生产环境还是小心为妙。--restart设置的是容器退出后的策略后面细讲它几乎决定了你的服务能不能“半夜挂了第二天早上仍在”。--network指定网络--cpus和--memory是资源限制这两项在生产上不能省不然一个失控容器能把整台机器吃干净。还有两个容易混淆但实战中会交替用到的是-i和-t。单独一个-i表示交互式保持标准输入打开配合-t伪终端就是经典的-it组合用来进入容器执行命令。4.2 容器日常管理ps、logs、exec、cp、port容器启动之后日常打交道最多的就是下面这几个命令。docker ps用于查看运行中的容器加-a看所有容器包括已停止的。它输出的信息里“STATUS”列很关键Up表示在跑Exited表示退出了后面跟的“(0)”表示退出码非0值得注意比如137表示被SIGKILL杀掉了一般是OOM或者手动kill125/126/127这类则可能是启动参数或者执行入口有问题。docker logs取容器日志# 实时追踪日志 docker logs -f web # 只看最后100行 docker logs --tail 100 web # 只看某时间点之后的 docker logs --since 2025-01-01T00:00:00 web排查问题第一步永远是看日志别急着重启容器先把现场保住。docker logs还有个大坑如果你没有配置Docker日志驱动和轮转策略一个高并发服务跑几天日志文件能把磁盘写满。这个问题我会在后面的生产章节里详细说。docker exec进入容器# 进入一个运行中的容器打开交互式shell docker exec -it web /bin/bash # 不在容器内进shell只执行一条命令 docker exec web ls /etc/nginx很多初次接触Docker的人会把docker exec和docker run搞混。docker run是“新建并启动一个容器”docker exec是“在已经运行的容器里执行命令”。排障的时候你想看容器内部的文件、环境变量、进程状态exec就是你的入口。需要注意容器镜像里不一定有bashAlpine这种精简镜像通常只有sh所以失败时可以换成/bin/sh试试。docker cp负责容器与宿主机之间拷贝文件# 从容器拷贝到宿主机 docker cp web:/etc/nginx/nginx.conf ./nginx.conf # 从宿主机拷贝到容器 docker cp ./myconfig.conf web:/etc/nginx/conf.d/我常用它来临时修正容器里的配置尤其是手头没有Dockerfile又想快速验证某个配置时。但改完容器里的文件后记住这个改动不会持久保留除非你commit下来容器一重建改动就丢了。所以cp进容器只适合临时调试正经做法还是把文件挂载进容器。docker port用来查询端口映射docker port web这个命令输出信息虽然简单但当你忘了当初-p映射了什么端口的时候它是唯一能快速答上来的查法。docker inspect还可以拿到更多底层信息比如容器IP、挂载列表、环境变量配合--format参数可以只提取指定字段docker inspect -f {{.NetworkSettings.IPAddress}} web4.3 实战一条命令跑通MySQL 8.0和Redis主从结合项目中遇到的问题我拿两个最常见的中间件实例来讲清楚实战闭环。跑MySQL 8.0的典型命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRoot123456 \ -e MYSQL_DATABASEappdb \ -e TZAsia/Shanghai \ -v /data/mysql8:/var/lib/mysql \ --restart unless-stopped \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci最后跟的那两段不是Docker参数而是传给mysql服务的启动参数Docker会把它拼到容器入口命令后面这样比进容器改my.cnf要优雅得多。数据目录-v挂到宿主机容器随便删数据安然无恙。初始化数据库的SQL脚本也可以挂载进/docker-entrypoint-initdb.d目录首次容器启动时会自动执行注意是只在数据目录为空的情况下执行。Redis主从稍微绕一点但道理一样。先起主节点docker network create redis-net docker run -d \ --name redis-master \ --network redis-net \ -p 6379:6379 \ -v /data/redis-master:/data \ redis:7 redis-server --appendonly yes再起从节点从节点上的replicaof参数指向主节点容器名因为同一个Docker网络下容器名会被DNS自动解析成对应IP所以不需要查IP直接写容器名就可以docker run -d \ --name redis-slave \ --network redis-net \ -p 6380:6379 \ -v /data/redis-slave:/data \ redis:7 redis-server --replicaof redis-master 6379这里最能体现自定义网络的好处容器间通信不用管IP地址变化容器重建之后名字不变、网络不变DNS解析自动跟随新IP比维护一堆静态IP舒服太多。官方还把shard-group-based redis cluster加进来了但最通用的主从方案还是这套逻辑。5. 网络与存储生产容器稳定运行的两块基石容器跑起来了数据能不能持久保存、容器之间能不能互相通信这两个问题会一直陪着你从开发到上线。很多人觉得docker run能跑起来就是成功其实网络和存储才决定着你这套架构能不能经得住运维。5.1 bridge、host、none三种网络模式的适用场景Docker默认的网络是bridge模式容器通过一个虚拟网桥跟宿主机通信对外通过端口映射暴露服务。这种模式好处是容器隔离性好互不干扰缺点是每次启动容器内部IP都可能变容器名之间的DNS解析只在自定义网络里才生效。另一个常用模式是host模式容器直接使用宿主机的网络栈不需要做端口映射容器里监听的端口就等同于宿主机的端口。好处是性能好、没有NAT一层坏处是失去了网络隔离端口冲突要靠人工管理。我一般只在对网络性能极其敏感的场景才用它比如某些压测工具、日志采集组件。none模式最冷门容器完全独立于网络只有回环接口适合本地跑一些不需要联网的批处理任务。还有macvlan模式可以让容器直接获得局域网IP看起来就像一台独立的物理机器适合做IoT网关、NAS这类场景。默认的bridge网络有个很大的限制容器用IP通信时每次重建容器IP就变脚本里写死IP就完蛋了。所以凡是需要互相调用的服务强烈建议建一个自定义网络docker network create myapp-net创建之后同一网络里的容器互相ping或者访问服务时直接写容器名就行# 容器A访问B的80端口 docker exec app-a curl http://app-b:80这个特性在生产里帮了大忙。我之前排查过一个微服务调用偶尔超时的问题最后发现是某个服务容器重建后IP变化另一个服务里硬编码了旧IP白查了半天。换成自定义网络加容器名之后再没出过这个问题。5.2 端口映射与容器互通“docker网络不通”排查实录很多人见“docker网络不通”这几个字当场懵掉其实先想清楚“哪到哪不通”再说。不通的可能有很多层是宿主机访问容器里服务不通还是容器访问外网不通还是容器之间不通这三类问题的排查入口完全不同。宿主机访问容器服务不通优先查端口映射docker port web # 0.0.0.0:8080 - 80/tcp然后看防火墙。Linux服务器上常见的事故就是docker -p 8080:80跑起来了防火墙挡在宿主机外外部机器访问8080超时但curl localhost:8080是通的。这种情况要放行宿主机端口而不是容器端口因为外部流量到这里直接撞上宿主机的防火墙。云服务器还要记得看安全组规则很多新手部署后访问不了问题都出在安全策略上。容器之间不通先检查它们是不是在同一个网络docker inspect web | grep -i network如果在同一个自定义网络里却还是不通那多半是容器内的服务监听地址或者端口不对。比如有的服务默认只监听127.0.0.1容器IP是172.x.x.x自然连不上这种就要去改服务配置。容器访问外网不通最常见的原因是宿主机的IP转发没有打开。Linux下临时开启sysctl net.ipv4.ip_forward1 # 永久开启就写进 /etc/sysctl.conf如果宿主机有环境变量配置了代理部分容器需要走代理访问外网也要注意代理设置是否生效。不过这些属于企业网络环境特例我在自己团队里通常建议尽量让容器走直连或者通过反向代理统一出网省得每个容器都要配一套代理参数。5.3 数据持久化volume和bind mount怎么选容器是临时的数据必须放到宿主机上。Docker提供两种主要持久化方式命名卷docker volume和绑定挂载bind mount。命名卷的创建方式docker volume create mydata docker run -d -v mydata:/app/data myapp也可以不预先createdocker run时直接-v我随便起个名字:/容器目录Docker会自动创建。命名卷的优点是不用手工管理宿主机目录路径Docker自己把数据存放在它的工作目录下适合给数据库这类“只知道挂到容器路径不关心宿主机路径”的场景。绑定挂载则是把你指定的宿主机目录直接挂进去docker run -d -v /home/user/app:/app myapp绑定挂载的优势是你直接能看着宿主机文件修改也很直观改完容器内立刻同步生效非常适合开发调试和配置文件管理。我之前调试nginx配置就喜欢直接-v把nginx.conf挂进去改完宿主机文件再在容器里reload。数据卷也会踩坑。最常见的一个是你在容器里写了数据挂载目录权限不对容器内进程无法写入宿主机目录直接报Permission denied。这个和宿主机目录的属主有关运行容器的用户是root还好如果容器里是普通用户比如很多官方镜像默认uid 1000那宿主机目录可能需要chown一下。另一个坑是把宿主机目录bind mount到了容器里已有数据的路径上容器的原目录会被挂载层“遮住”目录里原有的东西在容器里就看不到了。挂载前先看清容器镜像的WORKDIR和VOLUME声明。6. 生产环境高频命令重启策略、日志清理与资源管控开发环境里容器挂了就重启一下没什么心理负担。但生产环境不行半夜两点容器挂了没人守着的话服务就一直瘫着。好在这里有一些办法让容器“自己站起来”还有一些资源管控手段防止一颗老鼠屎坏了一锅汤。6.1 容器崩溃自愈restart策略与docker updatedocker run时加了一个--restart参数这个参数正是自愈能力的关键。它有几种取值no默认值容器退出后不自动重启。on-failure:3容器以非0退出码退出时自动重启最多重试3次。always只要容器退出就自动重启包括守护进程重启之后。unless-stopped除非手动docker stop否则总会自动重启。生产上我最喜欢用的是unless-stopped。它的好处非常实际如果你手滑docker stop了一个容器它会尊重你的意愿保持停止而always策略在容器被stop之后Docker服务一旦重启它又会自己拉起来反而让你觉得“明明停了怎么又跑了”。如果容器已经创建了但当初没加--restart不需要删除重建一个命令就能改docker update --restart unless-stopped app-serverdocker update还可以改资源限制。6.2 日志与资源限制避免磁盘写爆和OOM开发环境瓶颈不常出现生产环境跑三个多月最典型的伤害来自两个地方日志写满磁盘、内存失控导致OOM。默认情况下Docker会把容器的stdout和stderr收集成json-file日志文件放在宿主机的/var/lib/docker/containers/目录下。没有任何旧日志清理机制跑上几周日志文件很容易膨胀到几个G甚至几十个G。解决办法是在daemon.json里配置日志轮转{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }改完重启Docker服务对存量容器不生效存量容器需要重建。所以我一般在一台新机器装完Docker之后先改好daemon.json再开始起容器。已有的容器如果不想重建可以临时用docker logs --tail手动把日志截断或者直接删掉对应的日志文件再通知容器重新打开fd比较麻烦我更建议容器定期重建时把日志策略一起带上。资源限制上docker run的几个参数就够了docker run -d \ --cpus 1.5 \ --memory 2g \ --memory-swap 2g \ --pids-limit 512 \ myapp--cpus限制CPU核数--memory限制内存上限如果只设--memory不设--memory-swap容器可以使用两倍于--memory的swap有时候并不符合预期我通常会显式把两者设为一致让容器不可用swap。--pids-limit是限制容器内最大进程数防住某些脚本疯狂fork把宿主机进程表打满。容器OOM之后会怎样容器会直接被杀掉docker ps看到的状态是Exited (137)。这种OOM不一定每次都在docker logs里留下明显的错误记录所以要养成看退出码的习惯。6.3 批量清理与Compose快速编排一台机器上容器跑多了镜像、悬空镜像、无用卷、停止的容器会堆积得很厉害。手工一个个删又怕删错我一般用几条prune命令# 清理停止的容器 docker container prune # 清理悬空镜像没有标签也没有容器引用的镜像 docker image prune # 清理所有未使用的镜像 docker image prune -a # 清理所有未使用的卷高危务必确认 docker volume prune # 一键清理所有未使用资源 docker system prune -a --volumesprune命令带有“打扫”的性质建议在低峰期执行。docker system prune -a --volumes会把所有没有被运行中容器使用的镜像和卷都清掉如果构建缓存里存着老版本镜像想做回滚用也会一并清理所以这套重拳要慎用。至于多服务编排绕不开Docker Compose。它不需要额外命令体系只要写一个compose.yml然后用docker compose命令整体管理docker compose up -d docker compose down docker compose ps docker compose logs -fCompose的好处是服务间的依赖关系、网络、存储卷、资源限制都写在一个文件里整个环境的启停由一条命令完成。比如上面提到的MySQL和Redis主从如果写成Compose文件新环境直接docker compose up -d全部按配置跑起来不比一条条docker run香得多。7. Docker命令排障速查表那些让人血压飙升的场景多年代运维下来我发现绝大多数Docker问题都有清晰的规律可循排查步骤也是套路化的。下面我把本地项目、Linux服务器和Windows环境里见过的高频报错整理成一个速查表按“症状-可能原因-排查命令-解决方案”的方式列出来适配日常运维和新人学习。症状可能原因排查命令解决思路Docker命令报Cannot connect to the Docker daemonDocker服务未启动/守护进程崩溃systemctl status dockersystemctl start docker看journalctl -u dockerWindows上Docker Desktop启动提示virtualization support not detectedBIOS虚拟化没开/WSL2未启用系统信息看虚拟化状态BIOS打开VT-x/AMD-V启用WSL2并安装内核Windows上提示failed to connect to the docker api at npipeDocker Desktop引擎未就绪docker context ls重启Docker Desktop/切换default上下文/重置WSLdocker run直接退出docker logs没输出启动命令/入口脚本执行失败docker inspect查看Entrypoint手动docker exec进入容器调试线索检查挂载目录权限宿主机能访问容器外部机器访问不了防火墙/安全组拦截telnet 宿主机IP 端口或nc -vz放行宿主机对应端口检查云安全组规则容器之间无法用容器名互相访问不在同一自定义网络docker network inspect net名创建自定义网络让所有容器加入同一个网络docker pull一直超时网络到镜像仓库不通docker info看registry配置配置可用的镜像加速地址或通过代理下载再用save/load导入删除镜像时报image is being used by container镜像被容器引用docker ps -a先删容器再删镜像或强制删除docker rmi -f容器日志文件巨大磁盘满日志轮转未配置du -sh /var/lib/docker/containers配置log-opts max-size并重建容器容器被杀退出码137内存超限被OOM Killdmesg | grep -i oom查看内核日志加大--memory或优化应用内存占用宿主机目录挂载进容器后权限报错目录属主和容器进程UID不一致ls -n宿主机目录chown调整宿主机目录属主docker save的tar包load回来没有原来的名字save前tag不规范docker imagessave前显式docker tag好最终名字Pull拉取的镜像没有预期的版本tag写错/仓库registry写错docker manifest inspect 镜像:tag检查镜像名/标签/私有仓库前缀7.1 两个一定不能省的基础动作docker inspect和docker events排障时除了看日志我给新人的建议是多用docker inspect它把容器、镜像、网络、存储卷的配置以JSON形式完整dump出来是定位问题的放大镜。比如想确认容器内部环境变量是否生效docker inspect -f {{.Config.Env}}容器名一条命令搞定。想确认挂载的是不是宿主机指定目录看Mounts字段挂载类型、源路径、目标路径都清清楚楚。容器异常退出时看State.Restarting和State.ExitCode重启计数和退出码一目了然。还有一个常被忽略的命令docker events它实时输出Docker守护进程收到的事件流。写脚本做自动化、观察容器重启规律时特别好用比如docker events --filter containerweb --filter eventdie容器一挂这条命令当场就能给你推送一个die事件再配合一些监控工具能让运维系统立刻感知故障。7.2 容器内调试三板斧logs、exec、cp遇到容器正常但应用报错我习惯按顺序做三件事。先看日志docker logs往往直接给出错误堆栈或业务异常信息。如果项目日志是写文件而不是stdout那就得用第二板斧docker exec进入容器内部看业务日志文件命令前面加--user指定用户能避免误碰容器里的权限限制。如果容器里连工具都没有精简镜像没有vi、没有curl、没有ping又不想老去装依赖就用第三板斧docker cp把宿主机上的静态排查工具或调试脚本拷进容器执行。比如宿主机上编译好的curl、数据库客户端拷进去就能用比在容器里边装依赖边调试快得多。这三个工具配合起来基本能覆盖容器内“服务没起来、起来了报错、能跑但行为异常”三类问题。7.3 我的排障习惯三个命令先摸清现状接手的机器出问题时我从来不急着docker rm -f或者docker compose down脑子会因为慌乱而短路。首先执行三个命令摸清现状docker ps -a docker images df -h第一句看容器状态哪些在跑、哪些退出、退出码是多少第二句看本地镜像体积确认磁盘占用大头在哪里第三句确认磁盘有没有被写满。磁盘满是最容易被忽略但导致大量“Connection refused/无法创建文件/容器写日志失败”现象的根因一上来先看它能省掉后面一大半无效操作。这三个命令执行完再决定删哪个容器、重建哪个镜像、清理哪个卷基本不会误伤。最后再分享一个我自己踩过的坑写这种全场景命令梳理的文章本质上是在帮读者形成“命令围绕对象转”的思维方式。但光记住命令还不够更重要的是有一套自己的排障动作。我现在处理任何Docker问题固定流程一定是“先看现状再看日志最后动刀”。现状看的是docker ps -a、docker images、df -h日志看的是docker logs和docker inspect动刀之前再罗列受影响容器清单。这套流程听着笨但它能拦住绝大多数“手比脑子快”引发的次生事故。另外建议新入手的同事把常用的命令整理成自己的速查卡不要依赖记忆或者幸运。我在团队里会要求提交部署文档时必须写明白镜像版本和启动参数这不是形式主义而是因为Docker的命令执行成本很低但产生的影响范围很广一条-v写错可能就把宿主机根目录挂进容器了。有个小检查办法凡是docker run命令里涉及-v的参数先从右往左看一遍确认右边的容器内路径是预期的再确认左边的宿主机路径存在、可控、不是/、/etc这种系统级目录。这个习惯帮我挡掉过好几次足以毁掉环境的误操作。