但凡用过Docker的人,几乎都绕不开两类命令——镜像命令和容器命令。我在一线维护服务器多年,见过不少新手一上来就记命令,背了半天还是对着docker ps和docker images犯迷糊。其实这俩命令只是看上去相似,背后是完全不同的对象和生命周期。这篇内容不搞花活,直接把最常用的镜像命令、容器命令拎出来讲透,再结合真实场景告诉你什么时候用哪个,顺便把我踩过的坑、总结的小习惯一起交代了。适合刚入门 Docker 的朋友,也适合想让命令用得更顺手的老手。
1. Docker 镜像命令:从拉取到构建的完整闭环
1.1 镜像到底是什么?命令操作的对象
先搞明白镜像是什么,后面所有命令才记得住。镜像可以理解成一个打包好的、只读的模板,里面装了运行某个程序需要的操作系统底层、代码、依赖、配置。它没法直接执行,只能被用来创建容器。你可以把它类比成 Java 里的 Class,容器就是 new 出来的具体实例;也可以类比成安装光盘,容器就是启动之后的电脑。
所以镜像命令围绕的核心就两件事:拿到镜像(pull、search、images),管理镜像(rmi、tag、inspect、history、build、push)。掌握了这个逻辑,命令不会乱。
1.2 镜像常用命令清单与记忆逻辑
我把日常用得最多的镜像命令列了个表,每个后面都带一句“为什么用这个”,方便你对应到场景。
| 命令 | 作用 | 典型使用场景 |
|---|---|---|
docker search <名称> | 搜索公共仓库里的镜像 | 想看看某个镜像有没有官方版,比如docker search mysql |
docker pull <镜像:标签> | 从仓库拉取镜像到本地 | 部署前先拉镜像,docker pull nginx:alpine |
docker images | 查看本地已有镜像列表 | 确认镜像是否拉下来,看镜像大小 |
docker rmi <镜像ID/名称> | 删除本地镜像 | 清理不再使用的镜像,释放磁盘 |
docker tag <源镜像> <仓库/名称:标签> | 给镜像打标签 | 发布前改成自己仓库的命名格式 |
docker build -t <名称:标签> <路径> | 根据 Dockerfile 构建镜像 | 用项目里的 Dockerfile 生成自定义镜像 |
docker push <镜像:标签> | 把本地镜像推送到仓库 | 传到公司私有仓库或公共仓库 |
docker inspect <镜像名> | 查看镜像的详细配置信息 | 查镜像的环境变量、架构等原始元数据 |
docker history <镜像名> | 查看镜像的分层构建历史 | 排查镜像体积时看谁的 layer 最大 |
docker system df | 查看镜像/容器/缓存占用的磁盘空间 | 清理前先看空间被谁占了 |
记忆逻辑很简单,动词就是操作行为:pull 表示拉,push 表示推,rmi 表示删镜像(注意是 rmi 不是 rm,rm 是删容器的),images 是列表,build 是构建。剩下不常用的了解就行,用到再查。
1.3 镜像命令实操:从拉取一个 nginx 到构建自己的镜像
我用 nginx 举个完整流程。假设你要拉一个 nginx 镜像,先跑docker pull nginx:alpine,这是瘦身版,比官方默认的小很多,适合日常测试。
$ docker pull nginx:alpine看到 Pull complete 就成功了。然后docker images确认一下,输出里能看到仓库名、标签、镜像 ID、大小。注意同一个仓库可以有不同标签,比如nginx:latest和nginx:alpine,ID 可能不同。
接下来我想基于它做一个自定义镜像,往里塞一个自己的 index.html。先建一个目录,写个 Dockerfile:
FROM nginx:alpine COPY index.html /usr/share/nginx/html/然后执行docker build -t mynginx:v1 .,注意最后那个点代表当前目录,也就是 Dockerfile 所在的上下文路径。构建成功后,再用docker images就能看到mynginx:v1了。
做完自定义镜像,习惯上会用 tag 命令打一个新标签:
docker tag mynginx:v1 registry.example.com/team/mynginx:v1这样命名规范了,方便推到公司内网仓库。等你不需要测试镜像了,docker rmi mynginx:v1一键删除。如果删除时报“image is being used by container”,意思是有容器还在用,先停掉并删除容器再说。
提示:docker rmi 同时支持名称和 ID 删除,但 ID 只需输前几位,只要唯一即可,比如
docker rmi f0e48。
2. 容器命令:管理整个容器生命周期
2.1 容器与镜像的关系:类与实例
前面说容器是镜像的实例,这一节展开讲。容器有生命周期,可以被创建、启动、停止、重启、删除。镜像永远是只读的,容器在运行时会生成一个可写层,所有操作都发生在这层。你改完容器里的文件,如果不 commit、不挂数据卷,容器一删就全没了。
容器命令比镜像命令多一些,但这恰好对应它的生命周期。我把整个流程分阶段:创建运行时、查询状态、执行操作、清理。每个阶段都有固定的几个命令,你按阶段记,就不会漏。
2.2 容器常用命令全景
核心命令是这些:
| 命令 | 作用 | 常用参数 / 说明 |
|---|---|---|
docker run <镜像> | 创建并启动一个新容器 | -d后台运行,-it交互模式,-p端口映射,-v数据卷,--name命名,--restart重启策略 |
docker ps | 列出正在运行的容器 | -a显示所有容器(包括已停止的),-q只显示ID |
docker start/stop/restart <容器ID/名称> | 启动/停止/重启一个已存在的容器 | stop 是优雅停止,kill 是强制停止 |
docker rm <容器ID/名称> | 删除已停止的容器 | 需要加-f强制删除运行中的容器 |
docker logs <容器ID> | 查看容器日志 | -f持续跟踪日志输出,--tail只显示最后N行 |
docker exec -it <容器ID> <命令> | 进入正在运行的容器内部 | 常用/bin/bash或sh |
docker cp <源路径> <容器ID:目标路径> | 在容器和宿主机之间拷贝文件 | 从宿主机拷入容器,或从容器拷出 |
docker top <容器ID> | 查看容器内运行的进程 | 类似宿主机 top,但不是所有环境都支持 |
docker stats | 实时查看容器资源占用 | 内存、CPU、网络 IO,压测时很有用 |
docker inspect <容器ID> | 查看容器的详细配置和状态 | 能拿到 IP、挂载、重启次数等 |
所有命令都支持前缀缩写,比如docker ps -aq列出所有容器的 ID,结合删除命令很好用。
2.3 容器命令实操:把 nginx 容器跑起来并进入它
用刚才的mynginx:v1镜像,我一条条演示。
创建并启动容器,命名为web1,把宿主机的 8080 端口映射到容器的 80 端口,挂在后台:
docker run -d --name web1 -p 8080:80 mynginx:v1过两秒,浏览器访问http://localhost:8080,能看到页面。用docker ps确认容器在运行。这时候我想进容器内部看文件结构:
docker exec -it web1 sh进去之后是/目录,可以ls /usr/share/nginx/html/看看我 cp 进去的 index.html。退出输入exit。
如果我想把容器里的配置文件拷到本地分析:
docker cp web1:/etc/nginx/nginx.conf ./nginx.conf反过来也能把本地文件拷进容器。测试过程中想停止容器,就docker stop web1;重新启动docker start web1。日志排查用docker logs --tail 50 web1,看最近 50 行,加-f可以持续盯着日志输出。
注意:docker run 每次都会创建一个新容器,如果你习惯用相同的名称重复运行,会报
Conflict. The container name "/web1" is already in use。这时候要么换名字,要么先docker rm -f web1再 run。
3. 组合应用:真实项目中的命令流
3.1 从零拉起一套 Web 应用 + 数据库
镜像命令和容器命令从来不是割裂的,实际部署时一定是先拉镜像,再跑容器,多个容器靠网络协作。我给你走一遍最典型的流程:跑一个 MySQL,再跑一个需要连它的应用。
先拉 MySQL 镜像:
docker pull mysql:8.0然后跑 MySQL 容器,注意要设 root 密码、建库、挂数据卷防止数据丢:
docker run -d \ --name mysql8 \ -e MYSQL_ROOT_PASSWORD=root123456 \ -e MYSQL_DATABASE=mydb \ -v /opt/mysql_data:/var/lib/mysql \ -p 3306:3306 \ mysql:8.0-e表示环境变量,这里告诉 MySQL 初始化时的 root 密码和要自动创建的库名。-v把宿主机的/opt/mysql_data目录挂载到容器里的数据目录,这样容器删了数据还在。
接着跑一个需要连接数据库的后端服务。假设我有个后端镜像叫myapp:latest,启动时需要传数据库地址,我可以用--link(不推荐了)或者直接让容器跑在自定义网络里。更标准的是创建一个网桥网络,让两个容器用容器名互访:
docker network create mynet docker run -d --name mysql8 --network mynet \ -e MYSQL_ROOT_PASSWORD=root123456 \ -e MYSQL_DATABASE=mydb \ mysql:8.0 docker run -d --name backend --network mynet -p 8080:8080 \ -e DB_HOST=mysql8 \ -e DB_USER=root \ -e DB_PASSWORD=root123456 \ myapp:latest这里--network mynet是关键,之后 backend 容器里访问mysql8这个主机名就能连上数据库,不用关心数据库的 IP 是多少。这一套命令流覆盖了 pull、run、network、logs、ps,你用熟之后,几乎不再碰图形界面。
3.2 多容器协作:docker-compose 还在常用命令范围吗
严格说 compose 不算单条命令,但它是容器命令的编排延伸。当容器多到三五个时,一条条 run 太累,而且启动顺序、网络都难以维护。于是 docker-compose.yml 把镜像、端口的映射、环境变量、数据卷全部声明好,一条docker compose up -d就能拉起整个项目。
虽然本文聚焦常用命令,我还是要提一句:docker compose up -d、docker compose logs -f、docker compose down这三个命令,本质上是对前面说的 run、logs、rm 的批量封装。你理解了单容器的命令,再去看 compose 文件,不会有任何障碍。
经验:我自己判断是否用 compose 的标准是——如果超过两个容器,且它们之间有网络依赖关系,就直接写 compose。别嫌麻烦,后面重启、备份、迁移都会感谢这个决定。
4. 常见问题与避坑技巧
4.1 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 拉镜像超时 | 默认拉取源在国内网络下不稳定 | 在/etc/docker/daemon.json里配置公共镜像加速器,然后重启 Docker |
| docker 命令提示权限不足 | 当前用户不在 docker 用户组 | sudo usermod -aG docker $USER,重登生效;临时用sudo docker |
| 容器启动后马上退出 | 前台进程结束或启动命令出错 | docker logs <容器ID>查报错;确保镜像里 CMD/ENTRYPOINT 是指令型命令而不是 shell 脚本中断 |
| 端口被占用 | 宿主机端口已被其他服务占用 | 换一个宿主机端口映射,比如 8081:80 |
| 容器数据丢了 | 没挂数据卷 | 重新 run 时加-v,挂在宿主机目录;别把数据写在容器可写层 |
| 容器日志越来越大 | 日志驱动默认不轮转 | 在 daemon.json 设置log-driver: "json-file"和log-opts的 max-size、max-file |
| 镜像越积越多 | 多次 build 产生 dangling 镜像 | 定期执行docker image prune -f清理悬空镜像 |
| Docker 启动失败且报虚拟化未开启 | 宿主机没启用硬件虚拟化或没有 WSL2 环境 | 检查 BIOS 里虚拟化设置;Windows 下确认 Hyper-V/WSL 已启用 |
| 容器之间网络不通 | 没放在同一个自定义网络里 | 创建自定义网桥,run 时加--network <网络名>,用容器名互访 |
其中拉镜像慢是刚接触 Docker 的人最常见的问题。我在国内服务器上测试过,不改加速器直接拉 nginx,断断续续要好几分钟,配置好加速器后秒级完成。方法是在/etc/docker/daemon.json里加registry-mirrors字段,然后sudo systemctl restart docker。注意这个文件在 Linux 上路径固定是/etc/docker/daemon.json,没有就创建。Windows 上的 Docker Desktop 也有图形化配置入口,直接在设置里换镜像源。
提醒:别只图快随便找加速器地址,尽量用可靠的大平台提供的公共镜像服务,配置前先看说明。安全问题别忽视。
4.2 我的几个命令使用习惯
最后分享几个让运维变省心的习惯,都是平时踩坑总结出来的。
第一,所有长用的容器都加--name。无名的容器 ID 是一串随机 hex,logs、exec、inspect 都得复制一长串,有了名字就方便多了。第二,临时测试容器加--rm参数。比如docker run --rm -it nginx:alpine sh,退出容器后自动删除,不会留下一堆垃圾容器。第三,正式服务容器加--restart unless-stopped。重启宿主机或 Docker 服务时,容器会自动拉起,避免服务挂掉没人发现。第四,执行删除类命令前先用查询命令确认。比如删镜像前docker images,删容器前docker ps -a,确认 ID 再动手。第五,定期跑一把docker system prune -f。这命令会把停止的容器、没用到的网络、悬空镜像一并清掉,但注意别加-a,加-a会连未使用的镜像全部清理,可能误删你不想删的缓存镜像。
在真实环境里,我见过不少人在运维时对docker ps -a感到惊讶——为什么我停了容器,docker ps里看不到,但重新跑又提示名字冲突?其实就是因为容器还在,只是没运行,docker ps -a才能看到。这是最容易被忽略的点,记住了就不会发懵。
我自己还有个土办法:把每天要用的命令写成一个速查脚本放在服务器上,用docker images、docker ps -a、docker logs --tail 20 <容器名>各起一个别名,排查问题先看这三个状态,能解决八成问题。你也可以试试。
这个内容后续还可以扩展的方向很多,比如 Dockerfile 的优化、镜像体积瘦身、容器日志集中采集、多节点编排等。不过先把最常用的镜像命令和容器命令练到不用查资料就能跑,后面学啥都快。