
搞技术这么多年Docker 的命令翻来覆去就那些但每次一到关键场景总有人开始现场查文档。尤其是现在容器化已经成了标配连写个小的演示项目都要起个数据库容器Docker 用得熟不熟直接决定你排查问题的效率。我整理了一份自己平时用得最多、也最值得记的 Docker 常用命令偏实战向按照镜像、容器、网络存储、编排、故障排查这几个维度来组织。不管你是刚入门还是已经写了好几年服务这份清单都能让你少踩一些坑。很多人觉得 Docker 命令随便搜一下就有一堆但真正用起来你会发现网上很多资料要么太老要么只贴命令不讲为什么。比如docker run的参数几十个你不可能全记住但你得知道哪些是天天用的哪些是出问题才用的。我这篇不会把文档抄一遍而是挑出真实场景里高频出现的操作把参数背后的逻辑讲清楚顺手把我踩过的坑也一并列出来。1. 镜像管理一切容器的起点镜像这块是最基础的部分但也是很多人一开始就搞混的地方。镜像和容器的关系可以理解成“程序文件”和“运行中的进程”镜像是静态的容器是运行态。你从仓库拉镜像、构建镜像、给镜像打标签、清理没用的镜像这些操作几乎每天都在发生。1.1 拉取与查看先搞清楚仓库里的东西长什么样拉镜像是所有操作的第一步。最常见的命令就是docker pull比如要拉一个基础镜像docker pull ubuntu:22.04这里有个细节值得注意如果你不写标签默认拉的是latest。latest这个名字只代表“最新推送的”并不保证稳定也不能保证和你的代码兼容。所以正式项目里我强烈建议任何镜像都带上明确的版本号比如ubuntu:22.04、node:20-alpine别等到某天latest变了内容导致服务起不来的时候再后悔。拉完镜像之后用docker images查看本地有哪些镜像docker images输出里会显示仓库名、标签、镜像 ID、创建时间和大小。偶尔你会发现同一个镜像有多个标签其实它们可能指向同一个镜像 ID本质上是同一份内容只是起了几个别名。查看更详细的信息可以用docker inspect 镜像名或ID这个命令输出的是一大段 JSON里面包含镜像的架构、环境变量、默认命令、入口点等元信息。很多时候容器起不来就是因为镜像的默认Entrypoint或Cmd和你的预期不一样inspect就能帮你发现这些隐藏配置。1.2 构建与标签管理把项目变成镜像的核心操作自己在本地构建镜像时最常用的是docker builddocker build -t myapp:1.0.0 .-t就是给镜像起名字和标签myapp是仓库名1.0.0是标签最后的.表示使用当前目录下的Dockerfile作为构建上下文。一个常见的误区是想用 Dockerfile 里的东西去替换本地文件或者反过来。实际上docker build打包的是构建上下文里的文件而不是宿主机任意路径的文件。你在 Dockerfile 里写COPY ./app /app这个./app是相对构建上下文说的不是相对宿主机当前目录的。所以如果你的项目很大构建上下文里有很多无关文件记得写一个.dockerignore文件和.gitignore一个道理把node_modules、.git、target这些目录排除掉构建速度会有肉眼可见的提升。构建这块这两年有个重要变化就是默认的构建引擎已经切到了 BuildKit。你不需要额外做什么只要你的 Docker 版本够新docker build就是在用 BuildKit。它的好处是可以并发执行构建步骤、更好地利用缓存、支持跨平台构建还可以把密文参数在构建时传递而不写入镜像层。如果你需要指定平台比如在苹果芯片上构建 amd64 的镜像可以加平台参数docker build --platform linux/amd64 -t myapp:1.0.0 .构建完之后如果你想给镜像换个标签或者准备推到远程仓库用docker tagdocker tag myapp:1.0.0 registry.example.com/myapp:1.0.0这个命令不会复制镜像而是给同一个镜像增加一个引用名称操作非常轻量。推送到远程仓库用docker pushdocker push registry.example.com/myapp:1.0.0推送前需要先用docker login登录对应的镜像仓库这一步经常有人漏了导致 push 的时候报权限错误。1.3 镜像清理与瘦身本地磁盘告急时的救命操作跑时间长了本地会堆积大量镜像特别是每次构建都会产生新的层和悬空镜像。悬空镜像就是那些没有标签、也没有被任何容器引用的镜像层通常用none:none显示。查看占用空间可以用docker system df这个命令会列出镜像、容器、卷、构建缓存各自占了多少空间。清理悬空镜像docker image prune如果想把没被任何容器使用的镜像全都删掉用docker image prune -a但这里要提醒一下-a很激进它会把所有没有被容器引用的镜像都清理掉哪怕你只是暂时不用它。如果你只是想清理构建缓存用docker builder prune这个也是 BuildKit 时代才有的命令以前管理构建缓存只能靠删除整个 Docker 数据目录非常痛苦。镜像瘦身还有一个思路是在构建阶段就控制体积。比如优先选alpine这类瘦身版基础镜像用多阶段构建把编译工具链和运行产物分离开最终只把运行所需的内容复制到最终镜像。这类操作直接影响的是镜像仓库的拉取速度和部署效率值得花时间优化。2. 容器生命周期命令日常使用频率最高的一批镜像只是静态产物真正干活的是容器。创建容器、启动、停止、进入容器、看日志、删容器这些操作构成了日常工作的主旋律。这一部分的命令虽然看着简单但每个参数背后都有具体的场景用对了能省很多事情。2.1 创建与启动run 命令的常用参数一定要背熟docker run是使用频率最高的命令它负责从镜像创建并启动一个容器。最简单的用法docker run -d --name web -p 8080:80 nginx:1.25-d表示后台运行--name给容器起个名字-p做端口映射把宿主机的 8080 端口映射到容器内的 80 端口最后是镜像名。启动后终端会直接返回一个容器 ID然后你就可以用其他命令去管理它了。我这里解释一下-p的底层逻辑因为很多人只记住了格式不理解为什么有时映射不上。-p 8080:80宿主机端口在左容器端口在右。访问外网的http://宿主机IP:8080流量会进入容器内的 80 端口。如果你只写-p 80Docker 会随机分配一个宿主机端口你反而不容易确定访问地址。另一个高频参数是-v用于挂载存储卷docker run -d --name db -v mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORDsecret mysql:8.0-v mysql_data:/var/lib/mysql表示把名为mysql_data的存储卷挂载到容器内的/var/lib/mysql数据写在卷里容器删了数据还在。卷的细节后面单独讲这里只要明白-v就是用来持久化数据的。还有一个容易被忽略但在生产环境非常关键的参数docker run -d --name app --restart unless-stopped myapp:1.0.0--restart unless-stopped表示如果容器因为异常退出Docker 会自动重启它。唯一不自动重启的情况是管理员手动执行了docker stop。这个参数对长期运行的容器来说几乎是必加的很多人刚起步时不加结果服务器一重启或者进程崩了容器就永远停在退出状态其他人还得手动拉起来。-e用来设置环境变量比如刚才的MYSQL_ROOT_PASSWORD。对于需要注入配置的场景环境变量是最常用的方式。可以在宿主机上提前准备好一个环境变量文件然后一次性传入docker run -d --env-file .env myapp:1.0.0这种方式比直接在命令行写一串-e要清爽得多而且.env文件可以用 git 管理模板避免密码暴露在 shell 历史记录里。如果只是临时跑一个容器做测试用完就删可以用--rmdocker run --rm -it ubuntu:22.04 bash加上--rm后容器退出时会自动删除。配合-it交互模式加伪终端可以进到容器里操作。这个组合太适合调试了不用考虑清理残留容器的问题。2.2 进入容器与日志不重启服务解决问题容器运行中你需要进入容器内查看状态或者执行命令。这时候用docker execdocker exec -it web bashexec是在运行中的容器里再启动一个新的进程-it表示以交互方式进入。比如容器里装的是 alpine 镜像可能没有 bash只有 sh那就得换成docker exec -it web sh这里想提醒一个常见误区不要在容器里装很多调试工具特别是生产环境里的容器尽量保持最小化。你需要调试时可以用docker exec在运行时安装临时工具或者更推荐的方式是用docker cp把宿主机上的静态编译工具拷进容器用用完再丢掉。看日志是定位问题最直接的方式docker logs web默认只显示标准输出和标准错误。如果日志太多加--tail只看最近多少行docker logs --tail 100 web如果服务一直在打印日志你希望实时看加-fdocker logs -f web这个命令会持续输出日志直到你按 CtrlC 中断。注意docker logs只能看到容器内进程写到标准输出和标准错误的内容。如果应用自己写日志文件比如写到了/var/log/app.log那docker logs什么也看不到你得exec进去看文件或者在启动容器时把日志文件重定向到标准输出很多镜像的官方 Dockerfile 就是这么设计的。如果你想看容器里跑了哪些进程docker top web这个命令相当于容器内的ps可以快速确认进程是否存在、PID 是多少。查看容器状态和配置信息用docker inspect web同样是一大段 JSON包含容器的网络 IP、挂载卷、环境变量、启动命令、重启次数等。排查问题时优先看这里面的State字段它会告诉你容器当前处于running、exited、restarting还是paused状态还有退出码和退出时间。2.3 停止、删除与批量操作清理现场的基础功停止一个容器docker stop web停止所有容器docker stop $(docker ps -q)docker ps -q只输出容器的 ID 列表这个组合非常实用。docker ps默认只显示运行中的容器如果想看所有容器包括已经退出的加-adocker ps -a清理已经退出的容器docker container prune这个命令会删除所有处于exited状态的容器。如果你确定某个容器没用了直接删docker rm web如果有运行中的容器要删得先停再删或者用强制删除docker rm -f web-f会先发 SIGKILL 信号直接杀掉容器进程再删除。能不用就不用因为应用可能没来得及做优雅关闭导致数据损坏。如果你用的是--rm启动的容器停止后容器会被自动清理不需要你再手动rm。批量删除所有容器包括运行中的可以组合docker rm -f $(docker ps -aq)这个命令在你要清空整个环境做测试时非常管用但生产环境慎用你确认没有任何有用的容器再执行。3. 网络与存储两个最容易被忽视的模块容器跑起来只是第一步真正让它变得好用网络和存储的配置是分不开的。容器之间要通信就得配置网络数据要持久化就得用存储卷。很多人命令背得很熟但到网络不通、数据丢失的时候才意识到这两个模块有多重要。3.1 容器网络从 bridge 到自定义网络Docker 默认有三种网络模式bridge、host、none。平时我们用docker run启动容器时默认就是bridge网络容器会获得一个虚拟 IP宿主机可以通过端口映射访问它。查看当前网络列表docker network ls查看某个网络里有哪些容器docker network inspect bridge这个命令会输出连接在这个网络下的所有容器及其 IP 地址。多容器服务部署时我强烈建议你创建一个自定义网络而不是把所有容器都放在默认的bridge里docker network create mynet创建好后在docker run时指定网络docker run -d --name app --network mynet myapp:1.0.0 docker run -d --name db --network mynet -e DB_HOSTdb mysql:8.0自定义网络里有一个非常重要的特性容器之间可以直接用容器名互相访问。上面例子中app容器访问数据库只需要配置DB_HOSTdbDocker 内置的 DNS 会自动把db解析成对应容器的 IP。这就是容器编排的基础比你去查 IP 再手动配置可靠得多。排查网络问题的时候经常用到docker exec app ping db注意有些容器镜像里没有ping命令那就用docker exec app getent hosts db如果getent hosts能解析出 IP但网络还是不通再看防火墙和网络模式。这里有个经验同一台宿主机上优先用自定义网络而不是host网络。host模式虽然性能好一些但容器直接用宿主机网络端口冲突和安全隐患都比较麻烦日常开发场景不推荐。3.2 存储卷容器删了数据不能丢用-v挂载数据卷是持久化的核心方案。Docker 的存储卷分两种一种是 Docker 管理的卷另一种是绑定挂载宿主机目录。使用 Docker 卷的方式docker volume create data_vol docker run -d --name db -v data_vol:/var/lib/mysql mysql:8.0查看所有卷docker volume ls查看某个卷的挂载位置和占用情况docker volume inspect data_vol绑定挂载宿主机目录的方式docker run -d --name web -v /opt/site:/usr/share/nginx/html nginx:1.25这种方式的优势是你可以直接在宿主机上修改文件容器内立刻就能看到非常适合本地开发。但在生产环境我更推荐用 Docker 卷因为它的数据由 Docker 统一管理迁移、备份更容易权限问题也更少。清理卷时注意docker volume prune会删除所有未被容器引用的卷。这个命令要谨慎因为如果容器已经删了但你以为卷里还有数据一旦执行 prune数据就没了。删除单个卷用docker volume rm data_vol3.3 备份、恢复与迁移容器间的数据搬运数据卷的备份是一个经常被问到的问题。假设你有一个容器db挂载了卷data_vol需要把卷里的数据打包备份。一个很实用的思路是借助临时容器来打包docker run --rm -v data_vol:/source -v $(pwd):/backup alpine tar czf /backup/db_backup.tar.gz -C /source .这条命令的含义是启动一个临时 alpine 容器把data_vol挂载到/source把当前目录挂载到/backup然后在容器内执行tar打包/source目录的内容到/backup/db_backup.tar.gz。因为用了--rm容器打包完就自动删除了宿主机的当前目录下就会生成备份文件。恢复数据的方向反过来即可docker run --rm -v data_vol:/target -v $(pwd):/backup alpine sh -c tar xzf /backup/db_backup.tar.gz -C /target如果你只是想从一个容器把文件拷到宿主机或者从宿主机拷进容器用docker cpdocker cp web:/etc/nginx/nginx.conf ./nginx.conf docker cp ./nginx.conf web:/etc/nginx/nginx.confdocker cp不用启动容器的 shell非常适合快速提取配置和日志。4. Compose 编排命令多容器场景的默认答案单容器用docker run还行一旦服务多起来比如应用加数据库加缓存加消息队列手写一长串docker run命令既不直观也不好维护。Compose 这个工具就是干这个的它用 YAML 文件描述多容器的启动配置一条命令拉起整个服务栈。4.1 Compose v2 与旧命令的变化用 Compose 之前你需要写一个docker-compose.yml文件。以前大家用的命令是docker-compose up注意有连字符。现在新版 Docker 把 Compose 集成进了 Docker CLI直接用docker compose中间是空格。如果你用的是新版 Docker建议直接用docker compose命令更统一也避免和旧版工具混淆。集成后的docker compose和旧版docker-compose大部分子命令是兼容的。如果你是照着老教程操作发现docker-compose命令提示不存在不用惊慌换成docker compose即可。4.2 常用命令序列一条命令拉起整个服务栈最核心的就是docker compose up -d-d表示后台运行加上它之后Compose 会根据 YAML 文件里定义的服务依次创建和启动容器。首次执行会先拉取镜像再启动之后执行时如果配置没变化容器会被复用只会重建有改动的服务。查看当前 Compose 项目下所有服务的状态docker compose ps查看日志docker compose logs -f如果只想看某个服务的日志docker compose logs -f web停止服务但保留容器和网络docker compose stop停止并删除所有容器、网络docker compose down注意down默认不会删除存储卷。如果你想把卷也删了加-vdocker compose down -v这个操作会连数据一起清掉正式环境要三思。进入了 Compose 项目目录后服务之间直接用服务名互通。比如 YAML 里定义了一个db服务那web服务里配置连接地址时直接写db:3306Compose 会自动创建网络并完成 DNS 解析这一点和之前讲的自定义网络是同一个原理。想进入某个服务的容器里调试docker compose exec web sh这个命令和单容器的docker exec一样只是通过服务名找到对应的容器。4.3 多环境配置与变量管理不同环境开发、测试、生产需要不同的配置Compose 支持用环境变量插值。在 YAML 文件里可以这样写services: app: image: myapp:${TAG:-latest}${TAG:-latest}表示读取当前 shell 环境变量里的TAG如果没有设置就用默认值latest。更方便的做法是用.env文件。Compose 会自动读取项目根目录下的.env文件里的变量你可以在里面配置TAG1.2.0 DB_PASSWORDsecret然后在 YAML 里引用services: app: image: myapp:${TAG} db: environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}这种做法最大的好处是配置和代码可以分离提交到版本库的时候把.env加入.gitignore避免密码泄露。5. 故障排查与实用组合技前面讲的都是标准操作但实际工作里你总会遇到一些莫名其妙的问题。容器起不来、网络不通、磁盘涨满、日志刷屏这些问题单独看都不难难的是快速定位到正确方向。这里我整理了几个高频问题的排查思路和几个能大幅提升效率的命令组合。5.1 容器卡在退出状态先看退出码和完整日志容器启动后马上退出这是遇到最多的一个问题。第一步永远是用docker ps -a看容器的状态和退出码第二步用docker logs看日志。退出码是一个很重要的线索。比如退出码为 1通常是应用本身的错误比如配置参数不对、端口被占用、数据库连不上。退出码为 127通常是命令找不到比如镜像里没有你指定的启动命令。退出码为 137说明容器被强制杀掉了通常是内存超限被 OOM Killer 杀掉或者是执行了docker kill。如果docker logs输出很少或者什么也没有可以用docker inspect看State.ExitCode和Error字段有时候 Docker 会直接告诉你启动失败的原因。再不行就手动在前台跑一次去掉-d参数docker run --rm myapp:1.0.0前台模式启动时应用日志会直接打到终端上很多问题一眼就能看出来。这个方法听着简单但在定位问题时比反复看logs要快得多。5.2 清理僵尸容器与悬空镜像磁盘爆满的应急方案服务器磁盘满了很多服务会异常这时候要快速找到谁在占用空间。先看全局概况docker system df然后按占用大小排序找到最大的容器日志文件。容器日志文件默认被 Docker 写在宿主机的/var/lib/docker/containers/容器ID/目录下用du可以快速定位du -sh /var/lib/docker/containers/* | sort -rh | head -20找到特别大的日志文件后会看到形如*-json.log的文件。这个日志文件如果不加限制会一直增长解决方案有两个。临时处理是清空文件内容truncate -s 0 /var/lib/docker/containers/容器ID/*-json.log永久方案是在 Docker 配置里限制日志大小比如在/etc/docker/daemon.json里加{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }然后重启 Docker 生效。这个配置建议在搭建环境的时候就加上不然等到磁盘报警再来处理节点上的服务可能已经因为磁盘写满而挂掉了。清理完日志之后批量清理无用资源可以用docker system prune -a --volumes这条命令会尝试删除所有未被使用的镜像、容器、网络、构建缓存和卷。它非常激进我建议平时只用docker system prune不带--volumes的版本卷要单独确认后再清理。5.3 资源占用分析容器为什么越来越慢容器运行变慢最直接的原因就是资源配额或者宿主机资源不够。查看容器资源占用docker statsdocker stats会实时显示所有容器的 CPU、内存、网络 I/O、磁盘 I/O。如果某一个容器内存一路飙升你可以用--memory参数给它限制内存上限docker run -d --memory 512m --memory-swap 512m myapp:1.0.0--memory-swap设置为和--memory一样表示禁止使用 swap避免内存超限后性能骤降。写服务的同学一定深有体会没有限制的应用在内存泄漏时能直接把整台机器拖垮加个配额能保护旁边的容器。查看容器内进程的 CPU 占用更加精细一些docker top web再配合宿主机的ps或者top来判断是不是有其它进程干扰。如果是宿主机上其它非 Docker 进程占用了大量 CPU那问题就出在 Docker 外部不要在容器里反复找原因。5.4 高频命令组合与快捷操作下面这几个组合命令我写进了 shell 的 alias几乎每天都会用到。一键进入容器。如果容器用docker compose管理就dc exec web bash其中dc是docker compose的简写。在命令行里可以这样设置alias dcdocker compose一键查看所有容器的 IPdocker ps -q | xargs -I {} docker inspect -f {{.Name}} {{.NetworkSettings.IPAddress}} {}一键清理退出容器和悬空镜像docker container prune -f docker image prune -f一键停止再删除 Compose 管理的所有容器docker compose down这几个组合背后的思路其实就一个单个命令能做的事组合起来一次性完成减少人肉重复操作也能避免漏清理。排查问题的时候减少手动步骤就是在降低出错概率。我自己的感受是Docker 命令不需要背全部但常用的核心命令一定要形成肌肉记忆。尤其是run、exec、logs、inspect、compose up这几个几乎每天都要用用熟了之后排查问题的速度会快很多。另外新版本 Docker 的命令一直在迭代比如docker image prune、docker builder prune这类子命令就是近几年的新东西如果你还在用很老的命令版本遇到一些提示不存在的情况先确认一下 Docker 版本很多问题其实是版本差异导致的。