YAOTU INSIGHTS

Docker命令大全:从入门到生产,核心指令与实战排查全覆盖

Docker命令大全:从入门到生产,核心指令与实战排查全覆盖
Docker 命令大全从入门到生产全场景核心指令全覆盖我最初学 Docker 的时候也是照着网上的命令清单一条条背docker pull、docker run、docker ps背得滚瓜烂熟可真到生产环境一上手就傻眼。有一回半夜上线我起了一个 MySQL 容器敲docker ps却怎么也看不到它急得满头大汗后来才发现容器启动后秒退了而docker ps默认只显示运行中的容器。那一刻我才意识到Docker 命令不是靠背的是靠场景去理解的。这篇文章我不打算再给你罗列一份“字典式清单”而是按日常干活的实际顺序——镜像构建、容器运行、文件交互、网络存储、编排部署、故障排查、日常清理——把所有核心指令串起来。刚入门的人可以照着一步步操作有经验的人也能在中间找到不少平时容易忽略的细节。1. 先搞懂命令背后的三个概念比背命令重要得多很多人卡在 Docker 命令记不住根源是没有理解命令操作的对象到底是什么。Docker 体系里最基础也最绕不开的三个概念是镜像、容器、仓库理解它们的区别再去看命令思路会清楚很多。1.1 镜像、容器、仓库用“表单”和“填好的表”来理解镜像可以理解成一个模板里面包含了运行某个应用所需要的完整文件系统、依赖库、配置文件和启动参数。容器则是这个模板的一个运行实例。打个比方镜像好比一张空白的报名表容器就是已经填好信息、盖好章的那一页你可以从同一张报名表复印出无数页也可以把填错的页扔掉再重新填。仓库则是存放和分发镜像的地方类似代码托管平台。默认的公共仓库是 Docker Hub你从上面拉取别人做好的镜像这就是docker pull做的事你把自己构建好的镜像传上去供团队使用就是docker push做的事。1.2 命令的主干结构docker 对象 动作Docker 命令的结构非常规律基本都是docker 对象 动作 [参数] [对象名]的格式。对象就是 image、container、network、volume、context 这些资源类型动作就是 get、run、stop、rm 这些操作。比如docker image pull nginx docker container start web docker volume ls老版本 Docker 允许省略对象名写成docker pull nginx、docker run nginx新版本也兼容这种写法。理解了主干结构遇到不熟悉的命令时你可以先用docker help查看某个对象支持哪些动作再顺藤摸瓜去查具体参数就不用硬背了。提示生产环境里我习惯写完整格式明确操作对象比如docker container ls而不是docker ps。一是可读性好二是有些精简命令在不同 Docker 版本里行为有细微差异写清楚了不容易踩坑。2. 镜像管理拉取、构建、提交与仓库操作镜像管理是 Docker 使用的起点哪怕是运维老手也常常在镜像 tag 管理上栽跟头。这一节把从零开始接触镜像的高频命令都理一遍。2.1 拉取镜像不指定 tag 的后果你可能没想过docker pull nginx默认拉取的是nginx:latest这个标签代表最新版本但“最新”不代表“最稳定”。在生产环境我强烈建议指定明确版本docker pull nginx:1.25.3 docker pull mysql:8.0.36拉取前可以用docker search nginx --limit 10查看有哪些可用的镜像名称。docker search在教育演示时比较实用但在生产环境我更习惯直接去镜像仓库官网确认 tag 列表因为仓库里的搜索结果很多是个人上传的来源和安全性需要自行判断。2.2 查看镜像与清理负责的镜像管理习惯本地镜像列表用docker image ls能显示仓库名、标签、镜像 ID、创建时间和大小。真正容易被忽略的是清理。长期拉取不同 tag 的镜像磁盘上会积累一堆none悬空镜像这时候用docker image prune -f会清除所有悬空镜像。如果确定不再需要某些镜像用docker rmi nginx:1.25.3删除指定镜像。注意如果容器还在使用该镜像运行删除会报错需要先停止并删除容器。我试过的经验是每台机器上尽量只保留正在使用的镜像版本给旧版本留一个最近回退即可不要囤积太多。镜像占的磁盘空间比想象中大一个开发环境镜像动辄几个 G囤多了重建环境的时候会非常痛苦。2.3 docker build构建镜像的核心命令大部分场景下我们不会从零手写一个镜像而是基于已有镜像加自己的代码和配置。创建一个Dockerfile然后用docker build构建docker build -t myapp:v1.0 .这里有一个非常关键的隐性问题构建上下文。命令末尾的.代表把当前目录作为构建上下文发送给 Docker 守护进程Dockerfile 里COPY和ADD只能引用上下文内的文件。如果Dockerfile在有几百 MB 的目录里执行构建哪怕你只 COPY 一个文件整个目录也会被一起发送过去构建速度会非常慢甚至会把不该带进镜像的密钥文件打进去。生产环境一定要通过.dockerignore文件排除node_modules、.git、日志文件等。构建时间优化方面我踩过的坑是每次代码改动都会导致后续层缓存失效。正确做法是把依赖安装和代码复制的顺序分开先在 Dockerfile 里复制依赖清单文件并执行安装再复制业务代码这样依赖层可以被复用。比如FROM node:18 WORKDIR /app COPY package.json package-lock.json ./ RUN npm install COPY . . CMD [node, server.js]2.4 打 tag 与推送命名不规范回滚两行泪构建出来的镜像需要一个明确的标记方便溯源和回滚用docker tag给镜像重新打标签docker tag myapp:v1.0 registry.example.com/team/myapp:20240601推送到私有仓库用docker push。这里最容易被忽略的是镜像命名。Docker 默认把仓库地址作为前缀如果仓库地址不在命名中push 的时候会默认上传到 Docker Hub。企业内部一般用私有仓库比如 Harbor需要把仓库地址写在镜像名的最前面。我认为最实用的习惯是在 tag 里同时埋入版本号和构建日期比如myapp:1.2.0-20240601。真出问题的时候一看 tag 就知道这个镜像对应哪次发布不需要翻构建系统日志。3. 容器生命周期管理从 run 到 rm 的完整状态机容器管理的核心是docker run、docker start/stop/restart、docker exec、docker rm这一组命令组合。理解容器的状态转换对你的日常排障会有很大帮助。3.1 docker run 参数拆解每个参数都是一类需求docker run是 Docker 使用中出现频率最高的命令之一也是参数最多的命令之一。很多新手只记住docker run -d -p 8080:80 nginx一旦遇到需要特殊配置的场景就不知道怎么办了。下面是我在生产环境中使用率最高的参数组合参数作用实际案例-d后台运行容器docker run -d nginx--name给容器起名字docker run --name web -d nginx-p端口映射docker run -p 8080:80 nginx-e设置环境变量docker run -e MYSQL_ROOT_PASSWORD123456 mysql-v挂载目录或卷docker run -v /data:/app/data nginx--restart容器退出后的重启策略docker run --restartalways nginx--network指定网络docker run --network mynet nginx--cpus/--memory限制资源docker run --cpus1 --memory512m nginx-p参数是最容易产生困惑的地方。-p 8080:80的含义是宿主机 8080 端口映射到容器的 80 端口。如果省略宿主机的端口写成-p 80Docker 会随机分配一个主机端口实际访问起来很不方便生产环境不会这么用。-P大写是把容器 EXPOSE 的所有端口随机映射到宿主机调试可以生产慎用。容器命名这个细节我强烈建议每次都写。不写--name的话Docker 会随机生成一个名字比如happy_curie。临时验证没问题但生产环境你不可能通过随机名去管理容器而且在docker logs、docker exec的时候都要靠名字来定位起一个好的业务名比如web-api、mysql-master一眼就看明白这是干什么的。3.2 查看容器状态ps 和 ps -a 的区别docker ps只看运行中的容器docker ps -a看所有容器包括已经退出的。我的习惯是前面先docker ps找运行中的容器如果确认之前启动过但找不到了立刻补一个docker ps -a看一下容器是不是已经退出。容器退出后终端上是不显示的很多新手以为容器“消失了”其实它只是在 Exit 状态里躺着原因往往可以从docker inspect或者docker logs里找到。docker ps还有两个实用参数docker ps -a --filter statusexited docker ps -a --format table {{.Names}}\t{{.Status}}\t{{.Ports}}--filter可以筛选指定状态--format可以只输出你关心的列机器很多的时候可以少很多干扰信息。3.3 启动、停止、重启与删除信号与时机docker stop web docker start web docker restart web docker rm webdocker stop会先发送 SIGTERM 信号给容器里的主进程一点优雅关闭的时间默认等待 10 秒如果 10 秒后还没退出再发 SIGKILL 强杀。docker kill web是直接发 SIGKILL强制杀死容器一般不要轻易用。生产上短暂的维护操作可以用docker restart但我们一般会在重启前用docker inspect确认容器健康状态避免盲目重启。删除容器一定要确认业务没有在跑尤其是挂载了数据卷的容器。docker rm -f web会直接强制删除一个运行中的容器并且不会自动删除它挂载的卷但如果你在 run 的时候用了匿名卷容器删除后匿名卷就成了孤儿时间长了会越积越多。清理孤儿卷可以用docker volume prune。3.4 进容器里做事exec 的正确用法日常排查基本靠docker exec它是在运行中的容器里执行命令docker exec -it web bash-it是-i和-t的组合-i保持标准输入打开-t分配一个终端。如果容器里没有 bash可以换成sh很多精简镜像默认只有 shell。docker exec适合临时进入容器看文件、跑命令不适合长期当作 SSH 用。这里要提醒一个实际问题很多官方镜像为了控制体积连vim、curl、ping这些常用调试工具都没有。你进容器之后经常发现要啥没啥。我的解决方式是优先用宿主机工具访问容器的网络端口不一定非要进容器。如果必须要调试临时装一个调试镜像并挂载同一个网络比如docker run -it --rm --network container:web nicolaka/netshootnetshoot 集合了网路调试要用的全套工具用完自动删除不会污染业务容器。同时建议在基础镜像阶段就把调试工具装好但要评估体积影响生产环境别带不必要的包。3.5 退出容器的小技巧进入容器后想退出直接敲exit是停止容器吗不是exit只是退出容器的 shell 进程如果这个 shell 是容器的主进程那容器会跟着退出如果只是docker exec进去的 shell退出后容器继续运行。更保险的快捷键是CtrlPQ这是从容器中脱出不会停止容器。不过在较新版本的 Docker 里由于特性调整这个快捷键不一定总是生效最可靠的还是exit后确认docker ps或者直接另开一个终端检查容器状态。4. 生产环境高频操作日志、文件、资源监控进了生产环境真正每天用到的不是那些花哨的参数而是查看日志、确认资源占用、与容器互传文件这几件事。4.1 查看日志别让日志把磁盘塞满docker logs是排查问题的第一站docker logs web docker logs --tail 200 web docker logs -f web第一条输出全部日志对一把梭来说大日志量下会很卡生产上我基本都用--tail只看最近的行数或者配合--since 5m看最近五分钟的内容-f是实时跟踪适合看启动过程有没有报错。日志还有一个常见坑默认的 json-file 日志驱动会把所有 stdout 输出写到宿主机文件里如果不限制容器长期运行后日志文件能撑满磁盘。必须在 run 时限制 log 大小docker run --log-opt max-size50m --log-opt max-file3 nginxmax-size控制单文件大小max-file控制文件数量超过后自动 rotate。很多线上事故的最后一根稻草就是日志把磁盘写满这个参数我在所有新容器一定会加。4.2 与容器之间互传文件cp 和挂载双方案把文件拷进或者拷出容器用docker cpdocker cp ./app.jar web:/app/app.jar docker cp web:/var/log/nginx/access.log ./access.logdocker cp适合临时性操作比如把日志拉出来分析、把一个配置文件临时替换进去。但我不建议用它做正式发布用的文件传输因为容器一旦重建拷进去的文件就没了。长期需要共享的场景应该在run的时候就挂载目录。4.3 容器资源监控top、stats、events 一对组合拳docker top web docker stats docker events --since 1hdocker top查看容器里正在运行的进程能判断主进程还活没活着docker stats实时查看所有容器 CPU、内存、网络、磁盘 IO 占用是排查“哪只容器吃资源”最直接的命令docker events观察 Docker 守护进程事件流比如容器创建、启动、异常退出批量排查此类情况时很有价值。有一次我遇到某个容器频繁重启用docker logs看不出明显错误后来就是靠docker events --since 30m配合docker inspect发现它一直在 OOM Kill。这种事件单看节点监控很难发现Docker 自己的事件流反而是最快的。4.4 查看容器的详细配置inspectdocker inspect webdocker inspect会输出容器的完整元信息包括挂在哪个网桥、IP 是多少、端口映射规则、挂载卷、环境变量、健康检查状态等。信息量很大建议配合特定提取方式使用docker inspect --format {{json .State}} web docker inspect --format {{.NetworkSettings.IPAddress}} web docker inspect -f {{.Mounts}} web-f配合 Go 模板语法能只提取你想要的字段这是批量获取容器 IP、挂载路径时最省事的办法。5. 网络与数据持久化让容器互联并且不丢数据单个容器跑起来只是第一步真实业务几乎一定是多容器协作还需要把数据留存在容器之外。这一节解决的就是“容器之间怎么连通”“数据怎么不丢”这两个问题。5.1 容器互联的核心自定义网络Docker 安装后默认有一个 bridge 网络所有不指定网络的容器都会接到这个网桥上它们之间可以通过 IP 通信。但 IP 是会变的容器重建后 IP 可能就换了所以生产推荐的做法是创建自定义网络让容器通过名字互相访问docker network create mynet docker run --network mynet --name mysql-master -d mysql:8.0 docker run --network mynet --name backend -d myapp在同一个自定义网络里backend容器可以直接通过mysql-master这个主机名连接数据库。这比记 IP 靠谱得多。网络模式有几种常见选择模式说明适用场景bridge默认容器通过 NAT 访问外部单机多容器互通host容器直接使用宿主机网络对网络延迟敏感的中间件但端口容易冲突none无网络需要完全隔离的特殊场景container:xxx共享其他容器的网络栈调试、Sidecar 场景host模式曾经很受欢迎因为性能接近宿主机。但它有个隐蔽的坑容器里看到的就是宿主机网卡端口映射参数会被忽略如果同一宿主机上有多个容器用同一个端口直接冲突挂掉。能用自定义 bridge 解决的尽量别用 host。5.2 常见网络故障容器里访问不了外部网络遇到容器里ping 8.8.8.8不通先别急着怀疑命令缺失按顺序检查docker network inspect bridge确认容器拿到了 IP。宿主机上检查 iptables 的 FORWARD 链是否放行 Docker 流量。检查宿主机是否开了 firewalld 或 ufwDocker 需要自己管理 iptables 规则防火墙如果抢在前面改了 FORWARD 策略容器出网就会失败。我遇到过的真实案例是宿主机安全加固脚本把 FORWARD 默认策略改成了 DROP导致所有容器网络全断而 Docker 自检一切正常。这种情况下查看 Docker 日志没用必须检查宿主机防火墙规则。5.3 数据持久化卷和挂载怎么选容器天生是“用完即扔”的所以数据要放到容器之外。Docker 提供了两种主流方式卷volume和绑定挂载bind mount。docker run -v /data/mysql:/var/lib/mysql mysql:8.0 docker run --mount sourcemyvolume,target/var/lib/mysql mysql:8.0-v老式写法比较常见--mount语义更清晰。卷由 Docker 管理适合数据库这类需要可靠读写的场景绑定挂载直接映射宿主机目录适合开发时与代码同步。我最想强调的一点是文件权限问题。容器里的进程以什么用户运行宿主机挂载目录的属主对不对这两者对不上就会出现“Permission denied”。MySQL 镜像举例它默认用 mysql 用户跑数据目录如果宿主机挂载目录属主是 root启动时很容易报权限错误。简单处理是让宿主机目录权限为 777但生产上更严谨的做法是把目录属主改成镜像内使用者的 UID。这个坑不遇到一次很难记住排查方式就是docker logs加ls -l看目录权限。5.4 备份数据卷的正确姿势容器原生没有“备份某个卷”的命令常见做法是起一个临时容器挂载卷并打包docker run --rm -v myvolume:/data -v /backup:/backup alpine tar czf /backup/mysql-data.tar.gz -C /data .这是个非常实用的小技巧用--rm创建一次性容器同时挂载目标卷和备份目录在容器里执行 tar。备份结束容器自动删除不留痕迹。6. docker compose从单机命令到编排语句当容器数量变多用一条条docker run去维护会非常痛苦。这也是大多数项目最终会走到 docker compose 的原因。compose 能把一组容器的启动参数固化在一个 YAML 文件里一条命令启动全部一条命令清理全部。6.1 为什么需要 compose一个真实的 Redis 主从例子之前我搭了一个 Redis 主从验证环境按传统方式要敲十几条docker run还要给每个容器单独配置网络、挂载卷、重启策略。用 compose 之后一个文件搞定services: redis-master: image: redis:7 container_name: redis-master ports: - 6379:6379 volumes: - master-data:/data command: [redis-server, --appendonly, yes] redis-slave: image: redis:7 container_name: redis-slave depends_on: - redis-master ports: - 6380:6379 command: [redis-server, --slaveof, redis-master, 6379] volumes: master-data:写完后一条命令拉起docker compose up -d服务名redis-master同时也是容器在网络里的主机名从节点通过服务名就能找到主节点不用再关心 IP。6.2 compose 常用命令全景命令作用docker compose up -d构建并后台启动所有服务docker compose down停止并删除所有服务相关容器docker compose ps查看服务状态docker compose logs -f跟进所有服务的日志输出docker compose exec web bash进入某个服务容器docker compose config校验并输出最终解析后的配置docker compose build只构建镜像不启动容器down命令有一个隐藏特性默认不会删除数据卷只会删容器和网络。如果你想一起清掉数据卷要加-v。执行“清理环境”之前一定确认这个数据卷是不是没用了不然连着数据库文件一起删了哭都来不及。6.3 生产用 compose 需要注意的进阶配置compose 在开发环境很爽生产环境也能用但有几个坑必须提前处理restart策略。在 compose 文件里为每个服务写上restart: unless-stopped不然宿主机重启后容器不一定会自动拉起。healthcheck。给数据库、业务服务配置健康检查compose 在依赖启动顺序上会更精确services: backend: healthcheck: test: [CMD, curl, -f, http://localhost:8080/health] interval: 30s timeout: 5s retries: 3.env文件管理环境差异。compose 自动读取同目录下的.env文件把数据库密码、端口这类按部署环境不同的参数写在里面不要写死在 YAML 里。这样同一套 compose 文件在测试和正式环境可以复用。7. 故障排查实战我遇到过的几个典型 Docker 问题没有人能保证 Docker 环境一直不出问题掌握排查思路比记住命令更重要。这里分享我真实遇到过的几个高发故障并给出相对完整的排查链路。7.1 启动的 MySQL 容器“秒退”怎么一步步找到原因这是最常见的现象docker run执行完docker ps -a看到容器状态是 Exited退出码不是 0。排查链路先看退出码。docker inspect --format {{.State.ExitCode}} mysql-test退出码 1 通常是进程主动报错137 基本是内存不足或被人 kill。再看日志。docker logs mysql-test。MySQL 启动失败最常见的是权限问题或者挂载目录里已有数据文件不匹配。确认端口。MySQL 默认 3306如果宿主机的 3306 已被占用容器绑定会失败日志会提示port is already allocated。检查内存。MySQL 很吃内存如果宿主机内存不足容器会被 OOM Kill。看日志如果有Killed字样多半是它。之前有人反馈“docker 安装 mysql 失败”日志里报的其实是“数据库目录权限不对”原因是宿主机挂载目录属主和容器内 mysql 用户不一致按前面讲的方式把目录属主调整之后一次通过。7.2 容器能启动但宿主机访问不到服务容器docker ps显示 Up但浏览器访问http://宿主机IP:端口不通。排查顺序docker port web看端口映射是否正确宿主机端口有没有被占用。docker inspect web | grep IPAddress确认容器拿到了 IP。在宿主机上curl 容器IP:端口如果宿主机能通、外部不通就去查安全组和宿主机防火墙如果宿主机也不通说明服务没监听在预期端口上进容器看进程状态。如果服务本身监听的是 localhost那端口映射外部永远无法访问比如有些应用默认只绑 127.0.0.1需要改应用配置监听 0.0.0.0。7.3 Docker Desktop 启动失败virtualization support 相关很多人遇到过virtualization support wasnt detected这样的提示导致 Docker Desktop 无法启动。这一般不是 Docker 的问题而是 Windows 的虚拟化功能没有打开。排查要点确认主板 BIOS 里的 VT-x/AMD-V 已开启。Windows 功能里确认 Hyper-V、Windows 虚拟机监控程序平台、虚拟机平台这几个功能都已经勾选启用。如果这些功能都开了还是不行大概率是系统里同时还运行了其他虚拟化软件占用了虚拟化接口或者 Windows 系统更新后虚拟化相关服务被重置了。这不是任何“旁门左道”能绕过去的问题虚拟化后端没起来Docker Desktop 无论如何都无法运行。老老实实排查系统虚拟化配置才是正路。7.4 docker 网络不通宿主机防火墙规则的锅有一次搭建内网环境宿主机能访问但容器之间互相 ping 不通最后发现是宿主机上的 iptables FORWARD 链策略被改成了 DROP。Docker 需要 FORWARD 链来转发容器流量一旦这条策略禁用所有跨容器的网络都会断。检查命令iptables -L FORWARD -n --line-numbers如果发现策略挡了 Docker 的流量合理调整策略或者把 Docker 的网段加入白名单。这个故障隐蔽在“Docker 里一切配置都对但网络就是不通”排查方向很容易跑偏。8. 占用与清理给宿主机减负的资源管理心得Docker 用得越久宿主机上的镜像、容器、网络、卷就会越积越多。磁盘告警往往就是这一天的问题常规的docker system df可以一眼看到底哪些资源占了多大空间。8.1 清理命令全家桶docker system df docker container prune docker image prune -a docker volume prune docker network prune docker system prune -a分开说docker container prune删除所有已停止的容器保留运行中的。docker image prune -a删除所有未被任何容器使用的镜像不只是悬空镜像注意这个比较猛烈镜像重建会麻烦。docker volume prune删除所有未被容器使用的卷。docker network prune删除没有容器接入的网络。docker system prune -a是上述清理的总和尽量不要在没确认前直接在生产环境用删错了恢复成本比较高。我个人的习惯是先看docker system df再根据输出单独执行container prune和image prune -a卷的清理会谨慎很多因为数据卷里可能是重要数据。8.2 一个最值得养成的习惯使用 --rm 管理临时容器大部分普通操作其实只需要临时容器比如用容器跑一个构建脚本、测试一个配置甚至用某个工具镜像做一次数据转换。这类容器用完即走最优雅的写法是docker run --rm -it --name temp ubuntu bash--rm表示容器退出后自动删除自己不给宿主机留一点垃圾。这条习惯能让你在不知不觉中少做很多清理工作。实践下来凡是临时验证性质的容器我都加--rm涉及数据卷的则不会加因为--rm不会帮你删数据卷还是要手动清理。8.3 关于镜像瘦身和依赖管理热搜词里有“青龙依赖管理”这类场景的核心其实是一样的容器依赖多、更新频繁直接往运行中的容器里装依赖是临时方案容器重建就丢了。更可靠的做法是把依赖变更写进 Dockerfile重新构建镜像再通过 compose 或者重建容器滚动升级。依赖管理不应该是“进容器敲命令”而应该是“改镜像文件重新构建”。比如我维护的很多 Python 项目项目依赖有新增时只更新 requirements.txt 和 Dockerfile然后docker compose build docker compose up -d完成升级。这套流程简单稳定比进容器手动pip install可维护性强太多而且镜像历史可以追溯依赖变更都有记录。9. 据实总结把命令理解成一套“场景工具箱”这篇文章没有按命令字母序罗列而是按一个真实使用者从入门到生产会遇到的各种场景来讲。回想我自己的经历Docker 命令学得快不快根本不在于背的条数多不多而在于能不能把一个命令放到真实场景里理解它的用途、参数、副作用。docker run不只代表“启动一个容器”还代表端口、网络、存储、资源限制、重启策略这些运维维度的取舍docker system prune也不只是“清理垃圾”更是一道数据安全判断题。写命令前先想清楚场景执行命令前先确认影响范围这是 Docker 使用中最重要的两个习惯。最后分享一个实际的小建议给自己准备一个“常用命令备忘”文档不要求完整只记录自己在这台机器上实际用过的命令和参数。每次遇到一个不熟悉的场景先在文档里记下来一个月后回头看你会发现大多数命令的规律已经刻在脑子里了。这套思路不仅适用于 Docker也是所有命令行工具学习的通用路径。