我最早开始系统性整理 Docker 指令,是因为团队里连续几次有人把容器目录挂载错了,还有人分不清docker restart和docker stop配合docker start在行为上的区别。这些看起来都是小问题,但在生产环境里,随便一个误操作都可能把服务搞得起不来。后来我把日常高频命令按使用场景重新梳理了一遍,发现真正绕不开的核心指令其实没有那么多,但每一组都必须理解到位,而不是死记参数。这份整合版不是照着官方文档抄一遍,而是把我自己这些年实操验证过、踩过坑、确认能用的整理成果拿出来分享。内容覆盖镜像管理、容器生命周期、日志监控、网络与数据卷、Compose 编排以及高频排障组合,每一条都会说明应用场景、常见参数和需要注意的地方。无论你是刚上手 Docker 的新手,还是已经被业务逼着开始搞容器的开发,我相信这份内容都能让你在处理问题时少走弯路。1. 镜像生命周期管理:从拉取到清理的完整闭环1.1 拉取镜像时不能忽略的两个细节docker pull看起来就是把镜像拉到本地,实际使用中却藏着不少细节。比如很多人直接写docker pull nginx,这会默认拉取 nginx:latest 标签,但 latest 这个标签并不一定指向最新版本,它只代表维护者最新推送的那个镜像。在生产环境我更推荐显式指定版本号,像docker pull nginx:1.25.3,这样能保证每次拉取的结果可复现。如果是在本地开发环境,为了提升拉取速度,可以预先配置镜像加速地址。但这个操作需要修改 Docker 的 daemon 配置文件,不同系统的路径略有差别,常见的路径在/etc/docker/daemon.json。修改完后务必执行systemctl restart docker让配置生效。还有一个细节是拉取镜像时输出层信息里包含的Digest字段,它表示镜像内容的唯一指纹,当你怀疑镜像内容被人篡改时,可以用docker image inspect --format='{{index .RepoDigests 0}}' nginx去核验这个值。1.2 查看镜像信息不只是 docker imagesdocker images是最常用的查看命令,但它输出的信息其实有限,默认只展示仓库名、标签、镜像 ID、创建时间和大小。当你需要了解镜像内部的配置信息时,就要用到docker inspect。比如查看镜像暴露了哪些端口,可以执行:docker inspect nginx:1.25.3 --format='{{json .Config.ExposedPorts}}'查看镜像入口点和默认启动命令:docker inspect nginx:1.25.3 --format='{{.Config.Entrypoint}} {{.Config.Cmd}}'镜像 ID 和容器 ID 都是 12 位短标识,实际使用中也可以只输入前几位。但要注意镜像 ID 的唯一性只在本机生效,换一台机器同一个镜像的 ID 不一定相同,所以不要把镜像 ID 写死在部署脚本里,这将导致在另一台机器上执行失败。如果你需要精确引用某个镜像,最稳妥的方式是用镜像名加完整 tag,或者直接用 Digest。1.3 构建镜像的常用参数和踩坑点docker build应该是平时使用频率最高的镜像构建命令,几个关键参数值得认真掌握。-t指定镜像名称和标签,最好在构建时把镜像的版本号一起打上,否则构建出来的全是最新版本,无法区分不同的迭代记录。-f指定 Dockerfile 路径,默认使用当前目录下名为 Dockerfile 的文件,如果你的构建文件叫 dockerfile.prod,就必须用-f显式指定。--build-arg用于向 Dockerfile 传递构建参数,比如可以把软件源地址、版本号通过这种方式传入。注意--build-arg传递的值可以通过docker history被看到,所以不要在--build-arg里传密码、密钥一类的敏感信息。--no-cache会禁用构建缓存,在依赖导致构建异常时往往能派上用场。比如某次我把一个基础镜像升级了,但项目中依赖了旧版运行环境,Dockerfile 的前半段又没有变化,这时复用缓存会让后续步骤直接报错。执行docker build --no-cache后问题得到解决。不过这个命令会导致构建速度明显变慢,尤其是依赖下载量大的项目,要在确认缓存确实不可用的情况下再用。构建完成后检查镜像分层信息,可以用docker history,它能展示每一层的构建命令和大小,这对排查镜像体积异常大、帮忙减肥非常有帮助。1.4 镜像清理的完整方案docker rmi删除镜像时,如果有容器正在使用该镜像,会提示删除失败。所以标准操作顺序是:先删除依赖该镜像的容器,再删除镜像。如果只是想让容器停止运行,可以使用docker stop,但这并不会解除容器对镜像的引用,镜像依然没办法直接删除。批量清理不用的镜像时,可以用:docker image prune加上-a参数会删除所有未被任何容器引用的镜像:docker image prune -a这个操作在本地开发环境很爽,但生产环境发布前务必看清楚,它会把那些留作回滚用的旧版本镜像一并清理掉。我一般会先执行docker images确认目前可用的镜像列表,再决定哪些镜像需要保留,哪些可以清理,避免盲目清理导致回滚时找不到镜像。如果镜像占用了大量磁盘空间,可以先通过docker system df查看空间分布,确认究竟是镜像、容器、数据卷还是构建缓存占了大头。很多人只清理镜像,却忘了构建缓存,docker builder prune才是专门清理构建缓存空间的命令。这和使用rm -rf ~/.cache类似,不会影响已有的镜像和容器,非常安全。1.5 打标签与推送镜像docker tag用于给本地镜像重新打标签,它的语法是:docker tag 原镜像名:原标签 新镜像名:新标签通常在准备把镜像推送到私有仓库或用新版本号发布时使用。推送镜像到仓库前先要登录:docker login 仓库地址登录信息保存在本地配置文件中,默认有效期是较长时间,但在脚本化构建环境里,我更推荐通过环境变量或 CI 平台内置的凭证登录方式,避免把控制台登录状态带到自动化流程外。推送使用:docker push 仓库地址/镜像名:版本号推送前建议先确认本地镜像的架构,docker build --platform参数可以指定构建目标架构。如果你的服务器是 ARM 架构,却用 X86 的镜像直接推送到平台,生产环境拉取后会出现 exec format error,这种报错非常棘手,因为从日志看根本不知道是架构不匹配。2. 容器核心操作:跑起来只是开始2.1 理解 docker run 的本质docker run是所有容器操作里最核心的一条命令。它的本质是“创建并启动一个新的容器”,这个容器和镜像之间是一种实例关系。理解这一点之后,很多疑惑都会迎刃而解。比如你修改了容器内部的某个文件,这个修改并不会影响镜像本身,只会体现在当前容器层里,一旦容器被删除,所有修改都会随之消失。创建容器并保持后台运行,最常用的写法是:docker run -d --name app-service -p 8080:80 -e MYSQL_PASSWORD=xxx -v /data/app:/usr/share/nginx/html nginx:1.25.3拆开看这里面有几个关键设置:-d表示后台运行;--name为容器指定一个可记忆的名字;-p做端口映射;-e设置环境变量;-v挂载数据卷。它们之间没有严格的先后顺序,但为了日常维护方便,建议按照-d --name -p -e -v 镜像名 启动命令的顺序书写。--rm是一个容易被忽略但非常实用的参数。它告诉 Docker 在容器停止后立即删除容器本身。适用于临时容器、测试容器和一次性执行任务的场景。如果你不加--rm,每次停止容器后文件系统仍然保留,久而久之会累积堆积大量已停止的容器,磁盘空间会被慢慢占完。-i和-t参数在需要与容器进行交互时使用,-it结合在一起表示交互式终端。例如进入容器内调试:docker run -it --rm node:20 /bin/bash这会在容器内启动一个 bash 会话,退出后容器自动删除。这种临时环境调试方式适合快速验证镜像内命令行为,不会污染当前环境。2.2 查看容器状态不能只靠 psdocker ps默认只展示正在运行中的容器,加上-a才会展示包括已停止在内的所有容器。这一点经常让人困惑,刚入门时以为容器停了就等于删除了,实际上容器停止后依然占有磁盘空间。当容器数量多了之后,输出列表会变得很长,我习惯用:docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'自定义格式能让我快速聚焦需要的信息。查看容器最近创建的时间、启动时长、端口映射以及状态信息,在排查问题时非常有用。-q参数只输出容器 ID,它在组合命令里极其好用。比如想一次性删除所有已停止的容器:docker rm $(docker ps -aq --filter status=exited)这种组合理念同样适用于镜像、网络和数据卷的操作。2.3 容器生命周期管理命令对比docker start用于启动已创建的容器;docker stop会给容器内主进程发送终止信号,默认等待一定时间后强制执行;docker restart则是先停止再启动,等价于 stop 加 start 的组合。这三条命令在表面逻辑上差别不大,但实际工作中docker stop和docker restart的默认行为差异很容易被忽略。docker stop默认情况下会等待容器中的主进程处理终止信号,这段时间是 10 秒。如果你运行的是一个需要优雅停机的服务,这个时间窗口不够时,可用-t参数扩展,例如docker stop -t 30 容器名。而docker restart同样会先执行停止流程再启动,如果容器内的初始化逻辑比较耗时,你会观察到 restart 比 start 更慢。docker pause和docker unpause是另一组容易被忽视的命令。docker pause会暂停容器中的所有进程,并冻结当前状态,适合在需要临时停止对外提供服务但又不关闭容器的时候使用。它与stop的最大区别是容器内的进程并未终止,而是被挂起,所以docker ps中仍能看到容器处于 Up 状态,只是状态列会显示 paused。需要恢复时执行docker unpause。2.4 进入容器的正确姿势与几个常见误区docker exec是在运行中的容器内执行命令的最常用手段,也是最实用的调试工具。进入容器终端最常用的命令:docker exec -it 容器名 bash这里解释一下为什么不用docker attach。attach是直接连接到容器的主进程,如果你在容器里运行的是一个启动 Web 服务的命令,那么 attach 后你会看到服务日志,但此时按下Ctrl+C会直接把容器主进程终止,导致容器被停止运行。而docker exec -it 容器名 bash是启动一个独立的 shell 进程,在这个 shell 里做的任何操作都不会影响主进程运行,退出时按exit或Ctrl+D只是退出这个 shell。新手容易犯的另一个问题是进容器后直接安装调试工具。适合生产调试的操作是尽量不更改容器内文件系统。你可以用docker exec执行一组临时命令来完成排查,例如:docker exec 容器名 cat /etc/os-release docker exec 容器名 env而不是进入容器后安装 vim、curl 等工具,这会让容器层增加额外内容,一旦重新构建镜像这些修改又会全部丢失,等于白干。如果当前基础镜像确实缺少某些工具,可以直接在宿主机上验证网络连通性,或者使用docker run创建一个包含所需工具的新容器并挂载共享网络,借助docker network来实现跨容器访问。2.5 在容器与宿主机之间拷贝文件docker cp用于在宿主机和容器之间复制文件或目录,这个命令不需要容器处于运行状态。典型场景是把宿主机上的配置文件覆盖到容器内部:docker cp /data/config.yml 容器名:/etc/app/config.yml反向操作是把容器内的日志文件拉取到宿主机分析:docker cp 容器名:/var/log/app.log /data/logs/很多人把docker cp和挂载卷混为一谈。docker cp是临时的单向或双向复制,挂载卷则是宿主机目录与容器目录实时同步。如果文件需要被容器长期使用并修改,应该优先考虑挂载卷,而不是每次手动拷贝。2.6 删除容器的基本操作删除容器使用:docker rm 容器名如果容器正在运行,需要先停止再删除,或者加-f强制删除。docker rm -f会直接发送终止信号并删除容器。关于-f的使用,我个人建议在开发环境随便用,但在生产环境慎用,因为强制删除会跳过优雅停机逻辑,可能导致数据未落盘或持久化状态异常。批量删除所有未运行的容器:docker container prune加上-f则跳过确认提示。如果你在清理后想去追查某个容器的历史日志,那已经无法恢复了,所以在清理前先确认这些容器确实不再需要。3. 日志、资源监控与故障排查的硬核手段3.1 用 docker logs 定位问题的关键参数docker logs用于查看容器日志。默认展示标准输出流,加-f可以持续跟踪日志输出,效果等同于tail -f。加--tail控制显示最近的行数,比如--tail 100只显示最后 100 行日志。-t参数为每条日志增加时间戳,这在排查请求顺序问题时非常有用。容器内应用日志输出到 stdout 的标准做法对 Docker 日志体系十分关键。如果你的应用把日志写入文件而不输出到 stdout,那么docker logs往往只能看到容器启动信息,根本看不到业务日志。这种时候就要检查应用日志配置,或者用docker exec直接查看日志文件。还有一个容易踩坑的点:容器日志会不断增长并占用宿主机磁盘空间。长期运行且输出大量日志的容器,其日志文件可能膨胀到好几 GB。合理的做法是在创建容器时通过--log-opt max-size和--log-opt max-file限制单文件大小和保留文件数量,例如:docker run -d --name app --log-opt max-size=50m --log-opt max-file=3 nginx这样单个日志文件最大 50MB,最多保留 3 份,自动滚动覆盖,避免日志无限增长。3.2 实时资源监控命令组合docker stats用来查看所有运行中容器的 CPU、内存、网络和磁盘占用情况,不指定容器名称时会默认输出所有正在运行的容器。如果你只想看某几个容器,可以直接在命令后面跟上容器名:docker stats 容器A 容器B默认刷新频率是每秒一次,加--no-stream可以让命令执行一次后立即退出,适合脚本采集。docker top用于查看容器内主进程及相关子进程的信息,等于把ps -ef限制在容器内部。如果发现容器内某个进程 CPU 飙高,可以通过docker top 容器名快速定位进程 PID,再决定是否进入容器调整。3.3 深度检查:docker inspect 与 jq 配合docker inspect可以查看容器和镜像的详细配置信息,输出格式是一大段 JSON。手动翻 JSON 效率比较低,我通常与jq配合使用。比如快速查看容器的挂载信息:docker inspect 容器名 | jq '.[0].Mounts'查看端口绑定情况:docker inspect 容器名 | jq '.[0].NetworkSettings.Ports'查看容器的运行状态和启动时间:docker inspect 容器名 | jq '.[0].State'docker inspect 输出的信息特别全面,包括容器的 ID、创建时间、状态、重启策略、环境变量、挂载信息、网络配置、健康检查状态等。当需要确认某个容器到底设置了什么环境变量时,可以这样查询:docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' 容器名它在排查环境变量遗漏问题上会比进入容器逐条查看方便得多。3.4 实时事件流与变更追踪docker events用来监听 Docker 守护进程产生的事件流。当你怀疑某个容器被意外删除或重启时,执行docker events后复现操作,能看到类似container stop、container destroy等事件记录。这个命令不会输出历史事件,只显示从执行时刻开始的实时事件。生产环境排查事故原因时,它可以帮你还原操作时间轴,辅助定位问题到底出现在手动误操作、定时任务还是自动化脚本上。如果需要保留事件记录,可以结合其他日志方案把事件输出到文件,但大多数时候直接在异常发生现场实时观察就足够了。3.5 常见排障命令组合下面这一组命令组合我几乎每周都会用到。先看容器状态:docker ps -a | grep 容器名容器是运行中还是退出状态,退出状态码是多少,这些信息会直接决定下一步排查方向。再查看容器日志:docker logs --tail 200 容器名如果容器启动后立即崩溃,使用docker logs看最后的报错信息,往往能直接发现启动参数错误或依赖缺失。再深入检查容器配置:docker inspect 容器名 | jq '.[0].Config, .[0].HostConfig'会列出完整的环境变量、挂载目录和资源限制,排查配置项是否填写完整。如果怀疑是容器内程序问题,可以再执行:docker exec -it 容器名 sh逐步验证容器内的进程和文件。4. 网络与数据卷:容器之间的沟通方式和持久化方案4.1 理解 Docker 网络的核心模式Docker 容器默认使用 bridge 模式,也就是通过虚拟网桥让同一宿主机上的容器相互通信。此外还有 host、none 和 container 等模式。大部分场景下,bridge 网络就能满足需求。docker network ls可以查看当前存在的所有网络,默认会有 bridge、host、none 三个。创建自定义网络是常用的操作:docker network create app-net创建时加-d bridge指定驱动类型,也可以使用--subnet和--gateway自定义网段,但这属于高级玩法。日常开发直接使用默认网段即可,不过在生产环境规划容器 IP 地址时,建议提前规划好网段,避免后期容器数量增加导致网段冲突。4.2 容器互联的核心方式容器之间通信有几种方式。最简单的是使用--link,但这种方式现在已经不太推荐了。更现代、更可靠的方案,是让容器加入同一个自定义网络,然后通过容器名直接访问。例如先创建一个自定义网络 app-net,然后用它启动两个容器:docker network create app-net docker run -d --name web --network app-net nginx docker run -d --name api --network app-net node:20在 web 容器内可以直接通过api这个容器名访问 api 容器,Docker 内置的 DNS 会自动解析。这种方式最大的优势是容器重启后只要名字不变,IP 地址无论如何变化都能正确访问目标服务。如果某个容器启动时没有指定网络,也可以用docker network connect后续加入:docker network connect app-net 容器名同样地,docker network disconnect可以移除容器与网络的连接。4.3 暴露端口与映射端口的区别-p参数负责把容器端口映射到宿主机端口,例如-p 8080:80,表示访问宿主机 8080 端口时会被转发到容器的 80 端口。-P则会随机分配宿主机的端口映射到容器暴露的端口,用于调试阶段非常方便,但生产环境基本不用。关于宿主机端口的选择,需要提前确认端口没有被占用。docker run --name web -p 8080:80 nginx执行后如果端口被占用,会直接报错。所以启动前建议先执行ss -lntp | grep 8080检查端口占用情况。多次遇到端口被占用的场景后,你会发现这个检查步骤真的能免掉很多麻烦。4.4 数据卷的核心操作docker volume ls查看当前所有数据卷,docker volume create 卷名创建数据卷,docker volume rm 卷名删除数据卷。在容器中使用数据卷的推荐方式:docker run -d --name app --mount source=app-data,target=/usr/share/nginx/html nginx使用--mount语法比-v更加清晰易懂,它让卷名、目标路径一目了然。如果需要用宿主机目录直接挂载,可以这样写:docker run -d --name app -v /data/app:/usr/share/nginx/html nginx这两者本质没有区别。但要注意-v在目录不存在时可能会自动创建,而--mount对路径的要求更严格,使用不当会直接报错。如果你要挂载宿主机目录,建议提前把目录建好,避免出现挂载路径不对的情况。数据卷相对目录挂载更推荐用在数据库容器上,因为数据卷由 Docker 管理,备份、迁移和恢复都比较方便。目录挂载则更适合挂载配置文件、日志目录等需要对宿主机文件直接操作的场景。4.5 将修改后的容器保存为镜像的注意事项docker commit可以把一个容器的当前状态打包成新镜像。这个命令在快速复制排查环境或者固定某个临时补丁时很好用,但它有比较明显的劣势:打包出来的镜像会包含容器运行时无意产生的临时文件、日志和历史命令记录,所以它不应该是构建生产镜像的首选方式。正常的工作流应该是用 Dockerfile 来构建镜像。commit 只能算是应急方案,比如某个容器里手工修改了很多配置,想临时保存现场,可以用:docker commit 容器名 新镜像名:版本号此时新镜像会有多个层,而且这些层中包含大量无意义修改记录,导致镜像体积变大且难以复现构建过程。导出与导入镜像同样是一组重要操作。docker save可以把镜像保存为 tar 文件,适合离线迁移镜像:docker save -o nginx.tar nginx:1.25.3docker load用于导入 tar 包:docker load -i nginx.tardocker export和docker import对应的则是容器级操作,感兴趣可以研究一下和 save、load 的区别。核心区别在于 save 保存的是镜像的完整分层记录,而 export 保存的是容器当时的文件系统快照,会丢失镜像历史和标签。5. Docker Compose:多容器编排的日常操作5.1 为什么后端开发一定要掌握 ComposeDocker 指令单条操作比较细碎,一旦涉及多个容器协同工作,比如一个服务需要数据库、缓存和消息队列,一条条执行docker run不仅繁琐,还容易漏配置。docker-compose 的价值就是把多容器编排定义成声明式配置,然后通过一组简洁的指令完成启动、停止、重建和查看等操作。docker-compose 的核心是一个 yaml 配置文件,通常命名为 docker-compose.yml 或 compose.yaml。一个简单的服务编排示例:services: web: image: nginx:1.25.3 ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html redis: image: redis:7然后执行:docker compose up -d就能把两个服务一并启动,这就是 Compose 最基本的用法。5.2 高频 Compose 指令逐个拆解docker compose up -d的作用是按照配置文件创建并启动所有服务,加-d表示后台运行。如果你改动了 yaml 文件并想应用变更,直接重新执行docker compose up -d即可,它只会增量更新有变化的服务,不会影响其他服务运行。docker compose ps查看当前项目所有服务的状态,显示每个服务对应容器是运行中还是已退出。docker compose logs -f --tail 200查看多个服务的聚合日志,并且自动跟随输出,这是排查多服务链路问题的重要帮手。docker compose exec 服务名 命令用于进入某个服务的容器内执行命令,值得注意的是它直接跟随的是服务名,而不是容器名,这与docker exec存在区别。比如:docker compose exec redis redis-cli ping会直接在 redis 服务对应的容器中执行 redis-cli 命令。docker compose stop与docker compose down很容易弄混淆。stop只是停止所有服务的容器,但容器和网络仍然保留,执行start可以恢复运行。down则会把服务相关的容器、网络一并删除,默认情况下不会删除数据卷,不过加-v会连同匿名数据卷一起删除,这个参数在使用时需要格外谨慎。在数据库容器场景下,如果盲目执行docker compose down -v,数据库里的数据会出现严重问题,所以这个命令一定要看清楚后再用。docker compose build用于重新构建项目中定义了构建上下文的服务镜像,通常在修改 Dockerfile 或应用代码后执行。如果你在up之前改了 Dockerfile,直接执行docker compose up -d --build会更方便,一步完成重新构建并启动。5.3 Compose 文件里值得注意的细节版本号在现在的 Compose 规范里不用再显式写version字段,早期版本才需要。如果你在网上看到旧教程里写version: '3',也能用,只是新规范已经建议不再声明。服务名就是 Docker 内置的 DNS 名称,其他服务可以直接通过服务名访问它对应的容器 IP。例如在 web 服务里连接 redis,连接地址写redis:6379,不需要再关心 IP 是多少。这利用了 Compose 自动创建网络的优势,让服务间通信变得非常简洁。环境变量可以在 yaml 中使用environment,也可以使用env_file引用外部环境变量文件。如果配置里包含密码、密钥等敏感信息,建议用.env文件管理,并在该文件中做注释说明,同时记得把该文件加入版本管理忽略规则,避免密钥入库。5.4 Compose 生产环境的实用组合操作发布新版时,最常规的组合是:git pull docker compose build docker compose up -d --remove-orphans--remove-orphans参数会清理掉不再被 Compose 文件定义但仍在运行的旧容器,避免遗留孤儿容器占用端口或资源。但在执行之前,建议先确认这些孤儿容器确实不是其他服务依赖的,否则会出现误删。如果发现某个服务出现异常,需要单独重启:docker compose restart 服务名单独查看某个服务日志:docker compose logs -f 服务名需要单独扩展服务实例数量时,可以使用:docker compose up -d --scale web=3注意要确保 web 服务没有绑定宿主端口,否则会因端口冲突而无法完成扩展创建,这时应配合负载均衡来做流量分发。6. 效率技巧与高频报错速查6.1 给常用指令配置别名Docker 默认命令比较长,每天高频输入难免繁琐。我在 shell 配置里加了几条别名,操作效率提升明显。alias dps='docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"' alias dlogs='docker logs --tail 100 -f' alias dex='docker exec -it' alias dcup='docker compose up -d' alias dcdown='docker compose down' alias drestart='docker restart'配置完执行source ~/.bashrc或对应的 shell 配置文件,命令就能立即生效。有了这些别名后,平时输入量少了一小半,特别是在频繁查看状态和日志的时候,体感非常明显。6.2 一条命令完成容器全量清理开发环境中容器、镜像和网络会快速堆积,清理时要分批执行。可以选择组合命令一次搞定:docker container prune -f docker image prune -f docker network prune -f docker builder prune -f这个组合会删除所有已停止的容器、未被引用的镜像、未被使用的网络以及构建缓存。有一点要说明,docker image prune -f默认不带-a,只会清理悬空镜像,也就是没有标签的镜像,所以不会把本地已打好标签的镜像删除。如果你需要连未被容器引用的镜像一并清理,才需要加-a。执行这套清理前,务必确认没有正在运行的容器依赖这些镜像或网络。命令行没有任何二次确认机制,误删后数据是无法找回的。6.3 高频报错与快速定位容器端口被占用是最常见的问题。报错信息通常会直接提示端口已被占用。快速排查方法:ss -lntp | grep 端口号找到占用进程后,要么停止该进程,要么换一个宿主端口。容器启动后立即退出的原因很多,常见的有启动命令错误、环境变量缺失、依赖服务未就绪。排查思路是先用docker logs看最后输出,再检查启动命令是否能独立运行。还可以先以docker run --rm -it 镜像名 启动命令的方式在前台运行,观察完整的输出过程,比直接后台启动更容易定位问题。镜像拉取失败常见原因是网络不通或镜像仓库地址配置错误。先确认本地是否有该镜像,再检查指定的镜像名和 tag 是否存在。经常有人把仓库地址和命名空间搞错,导致收到 not found 提示,这时检查官方仓库或私有仓库的命名规则能快速解决问题。容器删除失败提示正在使用时,表面上容器的状态是 stopped,但仍然存在引用关系。最直接的方案是加-f删除,但如果是数据卷还在被使用,就还要先清理数据卷引用。6.4 编写 Docker 指令时的几个个人建议在整理完这么多指令后,我想额外补充几个实践心得。第一,给容器命名。不用--name的容器会获得一个随机 ID,后续操作必须通过 ID 定位,极其不方便,而且容器多了之后几乎无法通过输入前几位 ID 来区分目标容器。所以每次都建议顺手加--name。第二,模板化输出。docker ps和docker inspect的默认输出够用但不聚焦。多用--format自定义输出,用 jq 解析 JSON,这样在容器数量多的场景下定位效率会大大提高。第三,谨慎对待-v的自动创建目录行为。如果你在-v /data/db:/var/lib/mysql挂载数据库目录,宿主机-v指定的目录在不存在时会自动创建,而且新目录权限往往是 root 所有。如果容器内进程以普通用户身份运行,可能会出现无法写入该目录的情况,导致数据库容器反复启动失败。这时候用--mount并提前建好目录、设置好属主,能规避这个问题。第四,分清持久层和临时层。在容器里做的一切修改在容器删除后都会消失,这是 Docker 设计逻辑的核心特征。想要持久化,必须提前规划好用数据卷还是目录挂载,而不是等容器跑了几天后再去倒腾数据,那样不仅容易丢数据,还会增加很多不必要的操作成本。7. 最后一页:一些值得长期养成的使用习惯整篇内容走到这里,Docker 的常用指令基本已经覆盖到位。最后分享几个我在实际工作中反复落地、确实回报率很高的习惯。每次创建容器前,尽量先写出完整的启动命令,确认参数后执行。特别是-v和-p这两个参数,一旦写错,后期修改的成本远高于一开始多花一分钟检查。端口映射如果一开始弄错,需要删除容器重新创建才能修正,而删除容器本身就会导致容器内的临时数据丢失。日志和容器的保留期限要有明确预期。开发环境无所谓,数据随便造,容器随手删。但生产环境,日志保留策略要提前规划好,容器退出后是否需要保留现场、日志是否需要上传到集中日志平台,这些最好提前设计好。你每次执行docker rm或docker volume rm之前,先问自己一句:这个容器或数据卷是否真的不再需要了?这简单一步能避免很多不可逆的误操作。这套命令看起来多,但真正高频使用的其实就集中在镜像构建、容器生命周期、日志查看、网络与数据卷管理这四块。你只要把这一份整合的指令真正用熟,日常开发工作中的绝大多数容器场景都能应付自如。之后再遇到极端特殊的问题,再来查官方文档就会有明确的问题定位能力,而不是从头到尾大海捞针。
我最早开始系统性整理 Docker 指令,是因为团队里连续几次有人把容器目录挂载错了,还有人分不清docker restart和docker stop配合docker start在行为上的区别。这些看起来都是小问题,但在生产环境里,随便一个误操作都可能把服务搞得起不来。后来我把日常高频命令按使用场景重新梳理了一遍,发现真正绕不开的核心指令其实没有那么多,但每一组都必须理解到位,而不是死记参数。这份整合版不是照着官方文档抄一遍,而是把我自己这些年实操验证过、踩过坑、确认能用的整理成果拿出来分享。内容覆盖镜像管理、容器生命周期、日志监控、网络与数据卷、Compose 编排以及高频排障组合,每一条都会说明应用场景、常见参数和需要注意的地方。无论你是刚上手 Docker 的新手,还是已经被业务逼着开始搞容器的开发,我相信这份内容都能让你在处理问题时少走弯路。1. 镜像生命周期管理:从拉取到清理的完整闭环1.1 拉取镜像时不能忽略的两个细节docker pull看起来就是把镜像拉到本地,实际使用中却藏着不少细节。比如很多人直接写docker pull nginx,这会默认拉取 nginx:latest 标签,但 latest 这个标签并不一定指向最新版本,它只代表维护者最新推送的那个镜像。在生产环境我更推荐显式指定版本号,像docker pull nginx:1.25.3,这样能保证每次拉取的结果可复现。如果是在本地开发环境,为了提升拉取速度,可以预先配置镜像加速地址。但这个操作需要修改 Docker 的 daemon 配置文件,不同系统的路径略有差别,常见的路径在/etc/docker/daemon.json。修改完后务必执行systemctl restart docker让配置生效。还有一个细节是拉取镜像时输出层信息里包含的Digest字段,它表示镜像内容的唯一指纹,当你怀疑镜像内容被人篡改时,可以用docker image inspect --format='{{index .RepoDigests 0}}' nginx去核验这个值。1.2 查看镜像信息不只是 docker imagesdocker images是最常用的查看命令,但它输出的信息其实有限,默认只展示仓库名、标签、镜像 ID、创建时间和大小。当你需要了解镜像内部的配置信息时,就要用到docker inspect。比如查看镜像暴露了哪些端口,可以执行:docker inspect nginx:1.25.3 --format='{{json .Config.ExposedPorts}}'查看镜像入口点和默认启动命令:docker inspect nginx:1.25.3 --format='{{.Config.Entrypoint}} {{.Config.Cmd}}'镜像 ID 和容器 ID 都是 12 位短标识,实际使用中也可以只输入前几位。但要注意镜像 ID 的唯一性只在本机生效,换一台机器同一个镜像的 ID 不一定相同,所以不要把镜像 ID 写死在部署脚本里,这将导致在另一台机器上执行失败。如果你需要精确引用某个镜像,最稳妥的方式是用镜像名加完整 tag,或者直接用 Digest。1.3 构建镜像的常用参数和踩坑点docker build应该是平时使用频率最高的镜像构建命令,几个关键参数值得认真掌握。-t指定镜像名称和标签,最好在构建时把镜像的版本号一起打上,否则构建出来的全是最新版本,无法区分不同的迭代记录。-f指定 Dockerfile 路径,默认使用当前目录下名为 Dockerfile 的文件,如果你的构建文件叫 dockerfile.prod,就必须用-f显式指定。--build-arg用于向 Dockerfile 传递构建参数,比如可以把软件源地址、版本号通过这种方式传入。注意--build-arg传递的值可以通过docker history被看到,所以不要在--build-arg里传密码、密钥一类的敏感信息。--no-cache会禁用构建缓存,在依赖导致构建异常时往往能派上用场。比如某次我把一个基础镜像升级了,但项目中依赖了旧版运行环境,Dockerfile 的前半段又没有变化,这时复用缓存会让后续步骤直接报错。执行docker build --no-cache后问题得到解决。不过这个命令会导致构建速度明显变慢,尤其是依赖下载量大的项目,要在确认缓存确实不可用的情况下再用。构建完成后检查镜像分层信息,可以用docker history,它能展示每一层的构建命令和大小,这对排查镜像体积异常大、帮忙减肥非常有帮助。1.4 镜像清理的完整方案docker rmi删除镜像时,如果有容器正在使用该镜像,会提示删除失败。所以标准操作顺序是:先删除依赖该镜像的容器,再删除镜像。如果只是想让容器停止运行,可以使用docker stop,但这并不会解除容器对镜像的引用,镜像依然没办法直接删除。批量清理不用的镜像时,可以用:docker image prune加上-a参数会删除所有未被任何容器引用的镜像:docker image prune -a这个操作在本地开发环境很爽,但生产环境发布前务必看清楚,它会把那些留作回滚用的旧版本镜像一并清理掉。我一般会先执行docker images确认目前可用的镜像列表,再决定哪些镜像需要保留,哪些可以清理,避免盲目清理导致回滚时找不到镜像。如果镜像占用了大量磁盘空间,可以先通过docker system df查看空间分布,确认究竟是镜像、容器、数据卷还是构建缓存占了大头。很多人只清理镜像,却忘了构建缓存,docker builder prune才是专门清理构建缓存空间的命令。这和使用rm -rf ~/.cache类似,不会影响已有的镜像和容器,非常安全。1.5 打标签与推送镜像docker tag用于给本地镜像重新打标签,它的语法是:docker tag 原镜像名:原标签 新镜像名:新标签通常在准备把镜像推送到私有仓库或用新版本号发布时使用。推送镜像到仓库前先要登录:docker login 仓库地址登录信息保存在本地配置文件中,默认有效期是较长时间,但在脚本化构建环境里,我更推荐通过环境变量或 CI 平台内置的凭证登录方式,避免把控制台登录状态带到自动化流程外。推送使用:docker push 仓库地址/镜像名:版本号推送前建议先确认本地镜像的架构,docker build --platform参数可以指定构建目标架构。如果你的服务器是 ARM 架构,却用 X86 的镜像直接推送到平台,生产环境拉取后会出现 exec format error,这种报错非常棘手,因为从日志看根本不知道是架构不匹配。2. 容器核心操作:跑起来只是开始2.1 理解 docker run 的本质docker run是所有容器操作里最核心的一条命令。它的本质是“创建并启动一个新的容器”,这个容器和镜像之间是一种实例关系。理解这一点之后,很多疑惑都会迎刃而解。比如你修改了容器内部的某个文件,这个修改并不会影响镜像本身,只会体现在当前容器层里,一旦容器被删除,所有修改都会随之消失。创建容器并保持后台运行,最常用的写法是:docker run -d --name app-service -p 8080:80 -e MYSQL_PASSWORD=xxx -v /data/app:/usr/share/nginx/html nginx:1.25.3拆开看这里面有几个关键设置:-d表示后台运行;--name为容器指定一个可记忆的名字;-p做端口映射;-e设置环境变量;-v挂载数据卷。它们之间没有严格的先后顺序,但为了日常维护方便,建议按照-d --name -p -e -v 镜像名 启动命令的顺序书写。--rm是一个容易被忽略但非常实用的参数。它告诉 Docker 在容器停止后立即删除容器本身。适用于临时容器、测试容器和一次性执行任务的场景。如果你不加--rm,每次停止容器后文件系统仍然保留,久而久之会累积堆积大量已停止的容器,磁盘空间会被慢慢占完。-i和-t参数在需要与容器进行交互时使用,-it结合在一起表示交互式终端。例如进入容器内调试:docker run -it --rm node:20 /bin/bash这会在容器内启动一个 bash 会话,退出后容器自动删除。这种临时环境调试方式适合快速验证镜像内命令行为,不会污染当前环境。2.2 查看容器状态不能只靠 psdocker ps默认只展示正在运行中的容器,加上-a才会展示包括已停止在内的所有容器。这一点经常让人困惑,刚入门时以为容器停了就等于删除了,实际上容器停止后依然占有磁盘空间。当容器数量多了之后,输出列表会变得很长,我习惯用:docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'自定义格式能让我快速聚焦需要的信息。查看容器最近创建的时间、启动时长、端口映射以及状态信息,在排查问题时非常有用。-q参数只输出容器 ID,它在组合命令里极其好用。比如想一次性删除所有已停止的容器:docker rm $(docker ps -aq --filter status=exited)这种组合理念同样适用于镜像、网络和数据卷的操作。2.3 容器生命周期管理命令对比docker start用于启动已创建的容器;docker stop会给容器内主进程发送终止信号,默认等待一定时间后强制执行;docker restart则是先停止再启动,等价于 stop 加 start 的组合。这三条命令在表面逻辑上差别不大,但实际工作中docker stop和docker restart的默认行为差异很容易被忽略。docker stop默认情况下会等待容器中的主进程处理终止信号,这段时间是 10 秒。如果你运行的是一个需要优雅停机的服务,这个时间窗口不够时,可用-t参数扩展,例如docker stop -t 30 容器名。而docker restart同样会先执行停止流程再启动,如果容器内的初始化逻辑比较耗时,你会观察到 restart 比 start 更慢。docker pause和docker unpause是另一组容易被忽视的命令。docker pause会暂停容器中的所有进程,并冻结当前状态,适合在需要临时停止对外提供服务但又不关闭容器的时候使用。它与stop的最大区别是容器内的进程并未终止,而是被挂起,所以docker ps中仍能看到容器处于 Up 状态,只是状态列会显示 paused。需要恢复时执行docker unpause。2.4 进入容器的正确姿势与几个常见误区docker exec是在运行中的容器内执行命令的最常用手段,也是最实用的调试工具。进入容器终端最常用的命令:docker exec -it 容器名 bash这里解释一下为什么不用docker attach。attach是直接连接到容器的主进程,如果你在容器里运行的是一个启动 Web 服务的命令,那么 attach 后你会看到服务日志,但此时按下Ctrl+C会直接把容器主进程终止,导致容器被停止运行。而docker exec -it 容器名 bash是启动一个独立的 shell 进程,在这个 shell 里做的任何操作都不会影响主进程运行,退出时按exit或Ctrl+D只是退出这个 shell。新手容易犯的另一个问题是进容器后直接安装调试工具。适合生产调试的操作是尽量不更改容器内文件系统。你可以用docker exec执行一组临时命令来完成排查,例如:docker exec 容器名 cat /etc/os-release docker exec 容器名 env而不是进入容器后安装 vim、curl 等工具,这会让容器层增加额外内容,一旦重新构建镜像这些修改又会全部丢失,等于白干。如果当前基础镜像确实缺少某些工具,可以直接在宿主机上验证网络连通性,或者使用docker run创建一个包含所需工具的新容器并挂载共享网络,借助docker network来实现跨容器访问。2.5 在容器与宿主机之间拷贝文件docker cp用于在宿主机和容器之间复制文件或目录,这个命令不需要容器处于运行状态。典型场景是把宿主机上的配置文件覆盖到容器内部:docker cp /data/config.yml 容器名:/etc/app/config.yml反向操作是把容器内的日志文件拉取到宿主机分析:docker cp 容器名:/var/log/app.log /data/logs/很多人把docker cp和挂载卷混为一谈。docker cp是临时的单向或双向复制,挂载卷则是宿主机目录与容器目录实时同步。如果文件需要被容器长期使用并修改,应该优先考虑挂载卷,而不是每次手动拷贝。2.6 删除容器的基本操作删除容器使用:docker rm 容器名如果容器正在运行,需要先停止再删除,或者加-f强制删除。docker rm -f会直接发送终止信号并删除容器。关于-f的使用,我个人建议在开发环境随便用,但在生产环境慎用,因为强制删除会跳过优雅停机逻辑,可能导致数据未落盘或持久化状态异常。批量删除所有未运行的容器:docker container prune加上-f则跳过确认提示。如果你在清理后想去追查某个容器的历史日志,那已经无法恢复了,所以在清理前先确认这些容器确实不再需要。3. 日志、资源监控与故障排查的硬核手段3.1 用 docker logs 定位问题的关键参数docker logs用于查看容器日志。默认展示标准输出流,加-f可以持续跟踪日志输出,效果等同于tail -f。加--tail控制显示最近的行数,比如--tail 100只显示最后 100 行日志。-t参数为每条日志增加时间戳,这在排查请求顺序问题时非常有用。容器内应用日志输出到 stdout 的标准做法对 Docker 日志体系十分关键。如果你的应用把日志写入文件而不输出到 stdout,那么docker logs往往只能看到容器启动信息,根本看不到业务日志。这种时候就要检查应用日志配置,或者用docker exec直接查看日志文件。还有一个容易踩坑的点:容器日志会不断增长并占用宿主机磁盘空间。长期运行且输出大量日志的容器,其日志文件可能膨胀到好几 GB。合理的做法是在创建容器时通过--log-opt max-size和--log-opt max-file限制单文件大小和保留文件数量,例如:docker run -d --name app --log-opt max-size=50m --log-opt max-file=3 nginx这样单个日志文件最大 50MB,最多保留 3 份,自动滚动覆盖,避免日志无限增长。3.2 实时资源监控命令组合docker stats用来查看所有运行中容器的 CPU、内存、网络和磁盘占用情况,不指定容器名称时会默认输出所有正在运行的容器。如果你只想看某几个容器,可以直接在命令后面跟上容器名:docker stats 容器A 容器B默认刷新频率是每秒一次,加--no-stream可以让命令执行一次后立即退出,适合脚本采集。docker top用于查看容器内主进程及相关子进程的信息,等于把ps -ef限制在容器内部。如果发现容器内某个进程 CPU 飙高,可以通过docker top 容器名快速定位进程 PID,再决定是否进入容器调整。3.3 深度检查:docker inspect 与 jq 配合docker inspect可以查看容器和镜像的详细配置信息,输出格式是一大段 JSON。手动翻 JSON 效率比较低,我通常与jq配合使用。比如快速查看容器的挂载信息:docker inspect 容器名 | jq '.[0].Mounts'查看端口绑定情况:docker inspect 容器名 | jq '.[0].NetworkSettings.Ports'查看容器的运行状态和启动时间:docker inspect 容器名 | jq '.[0].State'docker inspect 输出的信息特别全面,包括容器的 ID、创建时间、状态、重启策略、环境变量、挂载信息、网络配置、健康检查状态等。当需要确认某个容器到底设置了什么环境变量时,可以这样查询:docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' 容器名它在排查环境变量遗漏问题上会比进入容器逐条查看方便得多。3.4 实时事件流与变更追踪docker events用来监听 Docker 守护进程产生的事件流。当你怀疑某个容器被意外删除或重启时,执行docker events后复现操作,能看到类似container stop、container destroy等事件记录。这个命令不会输出历史事件,只显示从执行时刻开始的实时事件。生产环境排查事故原因时,它可以帮你还原操作时间轴,辅助定位问题到底出现在手动误操作、定时任务还是自动化脚本上。如果需要保留事件记录,可以结合其他日志方案把事件输出到文件,但大多数时候直接在异常发生现场实时观察就足够了。3.5 常见排障命令组合下面这一组命令组合我几乎每周都会用到。先看容器状态:docker ps -a | grep 容器名容器是运行中还是退出状态,退出状态码是多少,这些信息会直接决定下一步排查方向。再查看容器日志:docker logs --tail 200 容器名如果容器启动后立即崩溃,使用docker logs看最后的报错信息,往往能直接发现启动参数错误或依赖缺失。再深入检查容器配置:docker inspect 容器名 | jq '.[0].Config, .[0].HostConfig'会列出完整的环境变量、挂载目录和资源限制,排查配置项是否填写完整。如果怀疑是容器内程序问题,可以再执行:docker exec -it 容器名 sh逐步验证容器内的进程和文件。4. 网络与数据卷:容器之间的沟通方式和持久化方案4.1 理解 Docker 网络的核心模式Docker 容器默认使用 bridge 模式,也就是通过虚拟网桥让同一宿主机上的容器相互通信。此外还有 host、none 和 container 等模式。大部分场景下,bridge 网络就能满足需求。docker network ls可以查看当前存在的所有网络,默认会有 bridge、host、none 三个。创建自定义网络是常用的操作:docker network create app-net创建时加-d bridge指定驱动类型,也可以使用--subnet和--gateway自定义网段,但这属于高级玩法。日常开发直接使用默认网段即可,不过在生产环境规划容器 IP 地址时,建议提前规划好网段,避免后期容器数量增加导致网段冲突。4.2 容器互联的核心方式容器之间通信有几种方式。最简单的是使用--link,但这种方式现在已经不太推荐了。更现代、更可靠的方案,是让容器加入同一个自定义网络,然后通过容器名直接访问。例如先创建一个自定义网络 app-net,然后用它启动两个容器:docker network create app-net docker run -d --name web --network app-net nginx docker run -d --name api --network app-net node:20在 web 容器内可以直接通过api这个容器名访问 api 容器,Docker 内置的 DNS 会自动解析。这种方式最大的优势是容器重启后只要名字不变,IP 地址无论如何变化都能正确访问目标服务。如果某个容器启动时没有指定网络,也可以用docker network connect后续加入:docker network connect app-net 容器名同样地,docker network disconnect可以移除容器与网络的连接。4.3 暴露端口与映射端口的区别-p参数负责把容器端口映射到宿主机端口,例如-p 8080:80,表示访问宿主机 8080 端口时会被转发到容器的 80 端口。-P则会随机分配宿主机的端口映射到容器暴露的端口,用于调试阶段非常方便,但生产环境基本不用。关于宿主机端口的选择,需要提前确认端口没有被占用。docker run --name web -p 8080:80 nginx执行后如果端口被占用,会直接报错。所以启动前建议先执行ss -lntp | grep 8080检查端口占用情况。多次遇到端口被占用的场景后,你会发现这个检查步骤真的能免掉很多麻烦。4.4 数据卷的核心操作docker volume ls查看当前所有数据卷,docker volume create 卷名创建数据卷,docker volume rm 卷名删除数据卷。在容器中使用数据卷的推荐方式:docker run -d --name app --mount source=app-data,target=/usr/share/nginx/html nginx使用--mount语法比-v更加清晰易懂,它让卷名、目标路径一目了然。如果需要用宿主机目录直接挂载,可以这样写:docker run -d --name app -v /data/app:/usr/share/nginx/html nginx这两者本质没有区别。但要注意-v在目录不存在时可能会自动创建,而--mount对路径的要求更严格,使用不当会直接报错。如果你要挂载宿主机目录,建议提前把目录建好,避免出现挂载路径不对的情况。数据卷相对目录挂载更推荐用在数据库容器上,因为数据卷由 Docker 管理,备份、迁移和恢复都比较方便。目录挂载则更适合挂载配置文件、日志目录等需要对宿主机文件直接操作的场景。4.5 将修改后的容器保存为镜像的注意事项docker commit可以把一个容器的当前状态打包成新镜像。这个命令在快速复制排查环境或者固定某个临时补丁时很好用,但它有比较明显的劣势:打包出来的镜像会包含容器运行时无意产生的临时文件、日志和历史命令记录,所以它不应该是构建生产镜像的首选方式。正常的工作流应该是用 Dockerfile 来构建镜像。commit 只能算是应急方案,比如某个容器里手工修改了很多配置,想临时保存现场,可以用:docker commit 容器名 新镜像名:版本号此时新镜像会有多个层,而且这些层中包含大量无意义修改记录,导致镜像体积变大且难以复现构建过程。导出与导入镜像同样是一组重要操作。docker save可以把镜像保存为 tar 文件,适合离线迁移镜像:docker save -o nginx.tar nginx:1.25.3docker load用于导入 tar 包:docker load -i nginx.tardocker export和docker import对应的则是容器级操作,感兴趣可以研究一下和 save、load 的区别。核心区别在于 save 保存的是镜像的完整分层记录,而 export 保存的是容器当时的文件系统快照,会丢失镜像历史和标签。5. Docker Compose:多容器编排的日常操作5.1 为什么后端开发一定要掌握 ComposeDocker 指令单条操作比较细碎,一旦涉及多个容器协同工作,比如一个服务需要数据库、缓存和消息队列,一条条执行docker run不仅繁琐,还容易漏配置。docker-compose 的价值就是把多容器编排定义成声明式配置,然后通过一组简洁的指令完成启动、停止、重建和查看等操作。docker-compose 的核心是一个 yaml 配置文件,通常命名为 docker-compose.yml 或 compose.yaml。一个简单的服务编排示例:services: web: image: nginx:1.25.3 ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html redis: image: redis:7然后执行:docker compose up -d就能把两个服务一并启动,这就是 Compose 最基本的用法。5.2 高频 Compose 指令逐个拆解docker compose up -d的作用是按照配置文件创建并启动所有服务,加-d表示后台运行。如果你改动了 yaml 文件并想应用变更,直接重新执行docker compose up -d即可,它只会增量更新有变化的服务,不会影响其他服务运行。docker compose ps查看当前项目所有服务的状态,显示每个服务对应容器是运行中还是已退出。docker compose logs -f --tail 200查看多个服务的聚合日志,并且自动跟随输出,这是排查多服务链路问题的重要帮手。docker compose exec 服务名 命令用于进入某个服务的容器内执行命令,值得注意的是它直接跟随的是服务名,而不是容器名,这与docker exec存在区别。比如:docker compose exec redis redis-cli ping会直接在 redis 服务对应的容器中执行 redis-cli 命令。docker compose stop与docker compose down很容易弄混淆。stop只是停止所有服务的容器,但容器和网络仍然保留,执行start可以恢复运行。down则会把服务相关的容器、网络一并删除,默认情况下不会删除数据卷,不过加-v会连同匿名数据卷一起删除,这个参数在使用时需要格外谨慎。在数据库容器场景下,如果盲目执行docker compose down -v,数据库里的数据会出现严重问题,所以这个命令一定要看清楚后再用。docker compose build用于重新构建项目中定义了构建上下文的服务镜像,通常在修改 Dockerfile 或应用代码后执行。如果你在up之前改了 Dockerfile,直接执行docker compose up -d --build会更方便,一步完成重新构建并启动。5.3 Compose 文件里值得注意的细节版本号在现在的 Compose 规范里不用再显式写version字段,早期版本才需要。如果你在网上看到旧教程里写version: '3',也能用,只是新规范已经建议不再声明。服务名就是 Docker 内置的 DNS 名称,其他服务可以直接通过服务名访问它对应的容器 IP。例如在 web 服务里连接 redis,连接地址写redis:6379,不需要再关心 IP 是多少。这利用了 Compose 自动创建网络的优势,让服务间通信变得非常简洁。环境变量可以在 yaml 中使用environment,也可以使用env_file引用外部环境变量文件。如果配置里包含密码、密钥等敏感信息,建议用.env文件管理,并在该文件中做注释说明,同时记得把该文件加入版本管理忽略规则,避免密钥入库。5.4 Compose 生产环境的实用组合操作发布新版时,最常规的组合是:git pull docker compose build docker compose up -d --remove-orphans--remove-orphans参数会清理掉不再被 Compose 文件定义但仍在运行的旧容器,避免遗留孤儿容器占用端口或资源。但在执行之前,建议先确认这些孤儿容器确实不是其他服务依赖的,否则会出现误删。如果发现某个服务出现异常,需要单独重启:docker compose restart 服务名单独查看某个服务日志:docker compose logs -f 服务名需要单独扩展服务实例数量时,可以使用:docker compose up -d --scale web=3注意要确保 web 服务没有绑定宿主端口,否则会因端口冲突而无法完成扩展创建,这时应配合负载均衡来做流量分发。6. 效率技巧与高频报错速查6.1 给常用指令配置别名Docker 默认命令比较长,每天高频输入难免繁琐。我在 shell 配置里加了几条别名,操作效率提升明显。alias dps='docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"' alias dlogs='docker logs --tail 100 -f' alias dex='docker exec -it' alias dcup='docker compose up -d' alias dcdown='docker compose down' alias drestart='docker restart'配置完执行source ~/.bashrc或对应的 shell 配置文件,命令就能立即生效。有了这些别名后,平时输入量少了一小半,特别是在频繁查看状态和日志的时候,体感非常明显。6.2 一条命令完成容器全量清理开发环境中容器、镜像和网络会快速堆积,清理时要分批执行。可以选择组合命令一次搞定:docker container prune -f docker image prune -f docker network prune -f docker builder prune -f这个组合会删除所有已停止的容器、未被引用的镜像、未被使用的网络以及构建缓存。有一点要说明,docker image prune -f默认不带-a,只会清理悬空镜像,也就是没有标签的镜像,所以不会把本地已打好标签的镜像删除。如果你需要连未被容器引用的镜像一并清理,才需要加-a。执行这套清理前,务必确认没有正在运行的容器依赖这些镜像或网络。命令行没有任何二次确认机制,误删后数据是无法找回的。6.3 高频报错与快速定位容器端口被占用是最常见的问题。报错信息通常会直接提示端口已被占用。快速排查方法:ss -lntp | grep 端口号找到占用进程后,要么停止该进程,要么换一个宿主端口。容器启动后立即退出的原因很多,常见的有启动命令错误、环境变量缺失、依赖服务未就绪。排查思路是先用docker logs看最后输出,再检查启动命令是否能独立运行。还可以先以docker run --rm -it 镜像名 启动命令的方式在前台运行,观察完整的输出过程,比直接后台启动更容易定位问题。镜像拉取失败常见原因是网络不通或镜像仓库地址配置错误。先确认本地是否有该镜像,再检查指定的镜像名和 tag 是否存在。经常有人把仓库地址和命名空间搞错,导致收到 not found 提示,这时检查官方仓库或私有仓库的命名规则能快速解决问题。容器删除失败提示正在使用时,表面上容器的状态是 stopped,但仍然存在引用关系。最直接的方案是加-f删除,但如果是数据卷还在被使用,就还要先清理数据卷引用。6.4 编写 Docker 指令时的几个个人建议在整理完这么多指令后,我想额外补充几个实践心得。第一,给容器命名。不用--name的容器会获得一个随机 ID,后续操作必须通过 ID 定位,极其不方便,而且容器多了之后几乎无法通过输入前几位 ID 来区分目标容器。所以每次都建议顺手加--name。第二,模板化输出。docker ps和docker inspect的默认输出够用但不聚焦。多用--format自定义输出,用 jq 解析 JSON,这样在容器数量多的场景下定位效率会大大提高。第三,谨慎对待-v的自动创建目录行为。如果你在-v /data/db:/var/lib/mysql挂载数据库目录,宿主机-v指定的目录在不存在时会自动创建,而且新目录权限往往是 root 所有。如果容器内进程以普通用户身份运行,可能会出现无法写入该目录的情况,导致数据库容器反复启动失败。这时候用--mount并提前建好目录、设置好属主,能规避这个问题。第四,分清持久层和临时层。在容器里做的一切修改在容器删除后都会消失,这是 Docker 设计逻辑的核心特征。想要持久化,必须提前规划好用数据卷还是目录挂载,而不是等容器跑了几天后再去倒腾数据,那样不仅容易丢数据,还会增加很多不必要的操作成本。7. 最后一页:一些值得长期养成的使用习惯整篇内容走到这里,Docker 的常用指令基本已经覆盖到位。最后分享几个我在实际工作中反复落地、确实回报率很高的习惯。每次创建容器前,尽量先写出完整的启动命令,确认参数后执行。特别是-v和-p这两个参数,一旦写错,后期修改的成本远高于一开始多花一分钟检查。端口映射如果一开始弄错,需要删除容器重新创建才能修正,而删除容器本身就会导致容器内的临时数据丢失。日志和容器的保留期限要有明确预期。开发环境无所谓,数据随便造,容器随手删。但生产环境,日志保留策略要提前规划好,容器退出后是否需要保留现场、日志是否需要上传到集中日志平台,这些最好提前设计好。你每次执行docker rm或docker volume rm之前,先问自己一句:这个容器或数据卷是否真的不再需要了?这简单一步能避免很多不可逆的误操作。这套命令看起来多,但真正高频使用的其实就集中在镜像构建、容器生命周期、日志查看、网络与数据卷管理这四块。你只要把这一份整合的指令真正用熟,日常开发工作中的绝大多数容器场景都能应付自如。之后再遇到极端特殊的问题,再来查官方文档就会有明确的问题定位能力,而不是从头到尾大海捞针。