我先把结论放在这里:Docker 命令一点不难,难的是不知道每条命令背后到底在干什么。平时接手一台新服务器,从零部署一套容器化应用,敲来敲去也就是那几十条命令。可偏偏这几十条命令,很多人一遇到异常就开始慌:docker ps 能看到容器,logs 也拉出来了,但就是找不到问题在哪。我把日常运维和开发中用得最顺手的命令整理成了一份可以直接抄的清单,按镜像、容器、网络、数据卷、构建、编排、排查七块来写,还带上了用法场景和踩坑点。新手可以照着一步步做,老手也可以拿来当速查手册,遇到不确定时翻一翻。
1. 命令体系怎么学才不混乱
1.1 把 Docker 命令想成一套对象管理模型
一个常见误区是“命令太多背不下来”。其实命令结构非常固定:所有命令都在管理五种对象——镜像(image)、容器(container)、网络(network)、数据卷(volume)和编排栈(stack/compose)。语法是docker + 对象子命令 + 动作 + 参数。举几个例子:
docker image ls docker container ls docker network ls docker volume ls看到规律了吗?都是“docker + 对象 + 动作”。早期版本的docker ps、docker run这些旧写法,其实都可以展开成docker container ps、docker container run。Docker 为了方便保留了短命令,但理解时你完全可以用对象管理的思路去归类。比如docker rm管理的是容器,docker rmi管理的是镜像(i 表示 image),看似容易混,用对象去理解就不会错。
提示:
docker rm删容器,docker rmi删镜像,docker volume rm删数据卷,一句话就能记住——动词相同,对象不同。
1.2 一次性摸清命令背后的交互原理
我用一个生活类比来说:Docker 就像一个管家,你的命令相当于给管家打电话。所有docker xxx命令都会通过本地的 socket 文件和后台的dockerd守护进程通信,由守护进程去实际执行创建、销毁、拉取等一系列操作。所以当你看到 “Cannot connect to the Docker daemon” 的时候,第一反应应该是管家不在线,而不是命令敲错了。
按使用频率来排序,最常用的动作是:
- 镜像:pull、build、images、rmi、tag、save、load
- 容器:run、ps、start、stop、restart、exec、logs、rm、cp、stats
- 网络:create、connect、disconnect、inspect、prune
- 数据卷:create、inspect、ls、prune
- 编排:compose 的一整套 up、down、ps、logs、exec、config
这些都是高频操作。建议的学习顺序也别按官方文档来,先跑第一个容器,再往后端数据、网络、构建、编排一步步扩展,符合真实业务推进的节奏,理解起来也顺。
另外提醒一句,执行docker run时背后其实有好几步:第一步,向 dockerd 发起请求;第二步,dockerd 检查本地有没有镜像,没有就去仓库拉取;第三步,推送镜像创建容器;第四步,分配网络与存储;第五步,启动进程。了解这个链路,后面排查问题就快得多。
2. 生命周期管理命令:镜像到容器的日常操作
2.1 镜像操作:拉取、查看、删除、备份
镜像命令最常见的三个场景是拉镜像、清镜像、导镜像。
拉镜像。docker pull nginx:alpine,这里:alpine是标签。镜像名带着仓库地址的写法是docker pull registry.example.com/team/nginx:1.24,从私有仓库拉。拉取时如果不带标签,默认会拉 latest。生产环境我强烈建议写明确版本,因为 latest 是个会移动的指针,一段时间后再次 pull 拿到的很可能不是你以为的那个版本。
查看镜像。docker images是最常用的,补充两个参数:
-a显示所有镜像(包含中间层镜像,一般排查时才用)--format自定义输出,比如docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"可以按表格输出仓库、标签、大小
真正需要看镜像“里面是什么”的时候用docker history 镜像名,能一层层看构建历史;docker inspect可以拿到架构、入口点、暴露端口等元数据。
删除镜像。docker rmi nginx:alpine可以删除指定镜像;docker rmi $(docker images -q)会删除全部镜像,谨慎使用。常见的报错是image is being used by running container,说明当前有容器在用这个镜像。解决方式是把关联的容器停掉并删除,再删镜像。docker image prune可以清理悬空镜像(dangling 镜像,也就是没有 tag 的中间层镜像),加上-a可以清理所有没被容器使用的镜像,加-f跳过确认。
备份与迁移。docker save -o nginx.tar nginx:alpine能把镜像打包成文件;另一台机器上用docker load -i nginx.tar导入。这个操作在没有外网、需要离线交付的场景特别实用。我经历过一次内网环境部署,应用在开发机打好包,通过 save/load 搬到生产服务器,整包传输比直接在目标机拉仓库靠谱得多。
2.2 容器生命周期:run是起点,ps/logs/exec是日常
docker run是整个使用频率最高的入口。关键参数列张表:
| 参数 | 作用 | 示例 |
|---|---|---|
-d | 后台运行容器 | docker run -d nginx |
-p 宿主机端口:容器端口 | 端口映射 | docker run -p 8080:80 nginx |
-P | 随机映射容器端口到宿主机 | docker run -P nginx |
--name | 给容器命名 | docker run --name web nginx |
-v 卷:容器路径 | 挂载数据卷/目录 | docker run -v /data:/var/lib/mysql mysql |
-e 变量=值 | 注入环境变量 | docker run -e MYSQL_ROOT_PASSWORD=xxx mysql |
--restart=策略 | 设置重启策略 | --restart=always |
--rm | 容器退出后自动删除 | 适合临时测试 |
-it | 交互式终端 | docker run -it ubuntu bash |
--network 名字 | 指定网络 | --network=mynet |
实际项目中我习惯把 run、start、stop、restart、rm 这五个动作串起来理解:run 是新建并启动,start 是启动已经存在的停止状态的容器,stop 是优雅停机(发终止信号,等应用清理完再强制),restart 等价 stop+start,rm 是删除。kill 和 stop 的区别也提一嘴:stop 是给应用留时间做收尾,kill 是直接强杀,数据可能没落盘。除非进程真的卡死,正常优先用 stop。
看容器状态用docker ps,注意:
docker ps只显示运行中的容器;docker ps -a显示所有容器,也包括退出状态的docker ps -a --format可以定制输出,比如docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"-f过滤:docker ps -a -f status=exited
进入容器执行命令:docker exec -it web bash。这是日常进入正在运行的容器的主要方式,附带的好处是-it给你 tty 和 stdin。docker attach是“直接挂到”容器当前的终端上,但一敲 Ctrl+C 容易把容器主进程也停了,日常排查我基本不用它。如果当前终端需要退出又不想把容器停掉,用 Ctrl+P、Ctrl+Q 让容器保持后台再退出。
看日志:docker logs -f --tail 200 web。这是新手最容易忽略的一招,很多容器启动就退出,必须先看 logs 而不是反复 start。容器文件复制:docker cp web:/etc/nginx/nginx.conf ./nginx.conf,可以把容器里的文件拷出来备份或排查。容器重命名docker rename web web-bak,有时候版本发布要保留旧容器时就很好用。资源查看:docker stats可以实时显示所有容器的 CPU、内存、网络 I/O 和磁盘占用,压测或定位内存泄漏时先盯它;docker top web可以看容器里跑的进程列表,相当于 ps。
2.3 清理回收:不清理,后端磁盘迟早爆
容器和镜像都是占磁盘的大户。三条清理命令要分清:
docker container prune:清理所有已停止的容器,-f跳过确认docker image prune:清理悬空镜像,-a清理所有没被容器使用的镜像docker system prune:容器、网络、悬空镜像、构建缓存全套清理,-a会把没被使用的镜像也清掉
docker system df可以看整体磁盘占用统计,先分析再决定清理哪些。我自己的经验是:CI 环境里跑完构建之后,镜像一层层的缓存会非常多,每次发布前先看一遍docker system df,够用就别乱清,避免误删正在用的东西。注意:prune 不会清理运行中的容器,安全的边界还算清晰,但system prune -a依然是危险操作,执行前一定要确认没有需要回滚的旧版本镜像。
3. 网络与数据卷:容器之间怎么连,数据怎么留存
3.1 三种内置网络模型
容器的网络模型必须选对,否则服务之间互访会出现“明明在跑,但访问不通”的诡异情况。
- bridge:默认网络,容器 NAT 模式,类似虚拟机里的桥接。宿主机之外访问容器需要
-p映射端口 - host:容器与宿主机共享网络栈,没有独立 IP,端口直接绑宿主机。性能好一点,但端口冲突管理不方便
- none:隔离网络,常用于跑一些完全不需要网络的离线任务
自定义 bridge 是生产中多服务互访的重要手段:
docker network create --driver bridge mynet docker run -d --name web --network mynet nginx docker run -d --name app --network mynet nginx这样容器之间就可以直接用容器名互相 ping 和调用,比如 app 要访问 web,直接http://web:80,而不是拿那个动态变化的 IP。为什么默认的 bridge 不行?默认 bridge 上虽然也有网络,但不支持容器名自动解析,容器名不能作为一个稳定的主机名来用。自定义 bridge 才有内置的 DNS 解析能力。
查看网络用docker network ls;查看某个网络下挂了多少容器、每个容器的 IP,用docker network inspect mynet。把运行中的容器动态接入一个网络:docker network connect mynet web;断开是docker network disconnect mynet web。在进行服务迁移或者临时联调的时候非常有用,不用重启容器。网络清理:docker network prune会清掉所有未被容器使用的自定义网络,慎用,因为有些“没用”的网络可能是你刚从 compose down 出来的,稍后还要再用。
3.2 数据卷与挂载方式
容器的一个重要特点是没有持久化,只要容器删除,内部写的数据就没了。为了让数据库、日志文件、上传目录保留下来,必须用 volume 或者 bind mount。两种方式:
- bind mount:把宿主机目录直接挂进去,
docker run -v /宿主机目录:/容器目录。优点是直接能看到文件,备份方便;缺点是宿主机目录不存在时很多版本会自动创建,但权限容易出问题 - named volume:
docker volume create pgdata,然后docker run -v pgdata:/var/lib/postgresql/data。管理起来像一块真正的卷,位置在/var/lib/docker/volumes/pgdata/_data,但平时你不需要去动它
还有一种挂载语法是--mount type=volume,source=pgdata,target=/var/lib/postgresql/data,相比-v更适合脚本化表达,可读性强,不易混淆。
看卷用docker volume ls、docker volume inspect pgdata能看到挂载点、驱动等信息。删卷用docker volume rm pgdata。如果你用 Docker Compose,卷在 compose 文件里声明,一键 up 自动创建。
关于权限问题,这是实际踩坑最多的点。容器里的进程是以它自己的 UID 运行的,如果 bind mount 的宿主机目录归属 root,容器内非 root 进程往往写不进去。经验做法是在挂载前先chown -R 1000:1000 /data或者直接指定正确的 uid:gid。
3.3 端口映射与连接排查
端口映射是否生效,docker port web可以列出来,比如0.0.0.0:8080->80/tcp。如果多个项目同时用到 8080,run 的时候会直接报bind: address already in use,那就要改宿主机端口或者把旧容器停掉。
进入容器排查连不上别服务:docker exec -it web bash,然后curl或者ping对端容器名。如果发现 ping 不通但网络没问题,先确认两边是不是在同一个自定义网络里,其次确认对端进程有没有监听端口。
提示:Docker 网络切换后,已运行的容器名称解析可能不会立即生效,最稳妥的办法是重建容器并用新网络重新跑一遍,然后再验证。
4. 从镜像到应用:构建、仓库与编排
4.1 Dockerfile 与构建命令
docker build -t myapp:1.0 .是最常用构建命令。几个重要参数:
-f:指定 Dockerfile 路径,比如docker build -f docker/Dockerfile -t myapp:1.0 .--no-cache:禁用缓存,编译类镜像更新依赖时特别有用--build-arg:传入构建参数,比如版本号--target:多阶段构建时指定最终阶段名,只构建中间某个阶段
构建是一条命令,但坑不少。有一个冷知识:Dockerfile 里面的每一行 RUN 指令都会生成一层镜像层,写在同一 RUN 里的多个命令会合并成一层。层数越多镜像越重,执行docker history 镜像名就能看到每一层的大小。真实项目里更推荐把软件源设置、基础依赖安装放在早期层,因为层缓存命中机制是“前几步没变就能复用缓存”,把不变的东西放前面、变的东西放后面,构建速度能差好几倍。
如果构建时网络慢,可以在 RUN 里换本地软件源,这点大家应该都懂。但构建卡住时别急着 Ctrl+C,先看docker build输出的步骤编号,确定卡在第几步,再对症下药。多阶段构建是另一个值得用起来的特性:第一阶段装编译工具链把代码编成产物,第二阶段只把产物拷进一个精简运行镜像,能有效把最终镜像体积从几个 GB 降到几百 MB。
4.2 镜像仓库与离线迁移
镜像仓库可以是公共仓库,也可以是自建的私有仓库。推送流程三步:
docker tag myapp:1.0 registry.example.com/team/myapp:1.0 docker login registry.example.com docker push registry.example.com/team/myapp:1.0拉取私有镜像:docker pull registry.example.com/team/myapp:1.0。
离线迁移前面提过,docker save -o 文件名.tar 镜像名,目标机器docker load -i 文件名.tar。这里要注意:save 打包的是镜像,不是容器。如果想保存容器本身的数据,应该用 commit 或导出容器的数据卷,但 commit 会产生大量中间层,实际生产不推荐把它当作持久化手段。
一个常见困惑:docker export和docker save有什么区别?export 导出的是容器的文件系统快照,不含镜像层的元数据、历史、标签;save 导出的是完整镜像。前者适合临时快照,后者适合镜像投递。
4.3 Compose 编排命令
单容器管理用命令足够,多个服务串起来最好用 Docker Compose。常用命令与 docker 的对应关系我列一下:
| 命令 | 作用 | 等价理解 |
|---|---|---|
docker-compose up -d | 按配置启动所有服务 | docker run批量版 |
docker-compose down | 停止并移除所有服务容器,默认不删数据卷 | 批量 rm |
docker-compose ps | 查看服务状态 | docker ps |
docker-compose logs -f | 查看多服务日志 | docker logs |
docker-compose exec web bash | 进入某个服务容器 | docker exec |
docker-compose config | 校验配置文件并展开合并后的配置 | 语法检查 |
实操场景里最重要的参数是-f,指定 compose 文件:docker-compose -f dev.yml up -d,方便开发、测试、生产共用同一份镜像用不同的配置。另一个高频参数是 up 后面不写-d,直接docker-compose up会把所有日志打到当前终端,前台跑适合排查。down和stop的区别与docker rm和docker stop的区别类似:stop 只停,down 停掉再删容器。如果要连数据卷一起删,down -v,这个操作会把数据库文件一并删掉,千万要小心。
5. 高频故障与排查实战
5.1 报错对应表
实际使用中会遇到的一堆常见报错,我把最典型的列成表:
| 报错现象 | 原因 | 最可能的解决办法 |
|---|---|---|
docker: Error response from daemon: Conflict. The container name "/web" is already in use | 容器名冲突 | docker ps -a找到同名容器,删掉或者docker rename改名 |
port is already allocated | 宿主机端口被占用 | 换宿主机端口,或docker ps检查占用端口的容器 |
Cannot connect to the Docker daemon at unix:///var/run/docker.sock | Docker 服务没启动 | 启动 daemon 服务,比如systemctl start docker |
No such image: xxx:latest | 本地拉不到镜像 | docker pull xxx,确认镜像名和标签是否正确 |
permission denied while trying to connect | 当前用户没权限访问 docker socket | 将用户加入 docker 组并重新登录,或使用 sudo |
unable to resolve host | 容器之间无法用名称解析 | 检查是否在同一自定义网络中 |
5.2 容器“启动即退出”排查五步
新手最容易卡的问题是容器一启动就 Exit,然后反复 start,越点越急。我的排查习惯是五步:
docker ps -a看退出码。退出码很关键,Exit 0 说明进程正常运行完主动退出,Exit 1 或 127 多半是命令或环境问题docker logs 容器名看启动日志。很多镜像(如数据库、反向代理)会把出错原因直接打出来docker inspect 容器名对比镜像的入口点和启动命令。有时候是你 run 的时候把命令写错了,覆盖了镜像原本的启动命令docker exec手动执行启动脚本,或docker run -it 镜像名 bash进容器手工跑一遍,看缺少什么依赖- 检查启动参数,尤其
-v挂载目录权限、-e环境变量是否正确。比如数据库镜像必须要指定管理员密码,缺了就直接退出
5.3 日志分析的几个实用技巧
docker logs可以加--tail只看末尾,加-f实时跟随,加--since 30m看最近半小时。假如日志乱码或者时间不对,先确认容器内时区。设置容器的 TZ 环境变量,比如-e TZ=Asia/Shanghai,很多容器镜像默认是 UTC,日志时间差 8 小时会误导排查方向。
资源问题在docker stats里是能看出来的:CPU 一直 100%,多半是你的业务逻辑死循环;内存缓慢上涨,可能是容器内程序内存泄漏。日志和资源配合使用,基本能定位大部分应用层问题。另外,docker inspect里的State.ExitCode、State.Error、Mounts字段,是排查“挂载盘没生效”“容器启动失败”的关键信息,比瞎猜快得多。
最后讲几点我个人操作中的体会。第一,命令别死记,把 docker 想象成对象管理,查一个、记一个,比照抄速查表管用得多。第二,我的 .bashrc 里常驻三个 alias:
alias dps='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"' alias dlog='docker logs -f --tail 100' alias dcup='docker-compose up -d --build'第三,也是最重要的一条:不要在跑着业务的服务器上随手执行docker system prune -a。我在测试环境误删过一次镜像缓存,虽然正在上线的应用没挂,但回滚时发现旧镜像没了,只好重新拉取,白白耽误了半小时。几个教训:生产环境少用-f,写清版本号,编排文件提交到版本库。Docker 命令本身只是工具,真正让你不慌的,是你对每一步操作后果都心里有数。