Docker核心命令实战指南:从镜像管理到容器编排的35个必备技巧
1. 从“启动失败”到“命令精通”:一个Docker老兵的实战心路
如果你在搜索引擎里敲下“docker desktop failed to start because virtualisation support wasn’t detected”这行字,恭喜你,你正站在Docker世界的大门口,准备迎接第一个,也是最常见的一个下马威。这行冰冷的错误提示,几乎成了每个Docker新手的“成人礼”。我见过太多人,兴致勃勃地下载了Docker Desktop,双击图标,然后就被这盆冷水浇得透心凉,瞬间从云端跌回地面,感觉Docker这座大厦还没进门就塌了。
别慌,这太正常了。Docker的强大,恰恰建立在它对系统底层虚拟化技术的依赖上。这个“virtualisation support not detected”错误,本质上是你电脑的BIOS/UEFI设置里,虚拟化技术(Intel VT-x 或 AMD-V)没有开启,或者被某些安全软件、Hyper-V冲突给屏蔽了。解决它,通常只需要重启电脑,进入BIOS,找到类似“Intel Virtualization Technology”或“SVM Mode”的选项,把它从“Disabled”改成“Enabled”。在Windows上,可能还需要在“启用或关闭Windows功能”里,确保“Hyper-V”和“Windows虚拟机监控程序平台”是勾选状态。这个小小的门槛,筛掉的是那些缺乏耐心和动手能力的人。跨过去,你才算真正拿到了入场券。
而当你终于看到那只小鲸鱼欢快地游动起来,准备大展拳脚时,迎面而来的就是另一片“命令的海洋”。docker run, docker ps, docker build... 这些命令就像乐高积木,单个看很简单,但组合起来却能构建出复杂的应用世界。网上的“常用命令大全”铺天盖地,但往往只是罗列,告诉你“是什么”,很少告诉你“为什么”以及“什么时候用”。作为一个在容器化浪潮里扑腾了多年的老兵,我深知,死记硬背命令列表效率极低,且容易遗忘。真正高效的方式,是理解Docker的核心工作流,并将命令嵌入到这个流程的每一个关键节点中,形成肌肉记忆。
所以,这篇文章不会是一份冰冷的命令手册。我想带你走一遍我当年走过的路:从解决那个恼人的启动错误开始,到理解Docker的镜像与容器这两个核心概念,再到通过一张逻辑图看清命令之间的关联,最后,我会为你梳理出35条最常用、最核心的命令,并附上我踩过无数坑才总结出的使用场景、参数精髓和避坑指南。我们的目标不是记住所有命令,而是掌握足以应对80%日常工作的核心命令集,并懂得在遇到另外20%问题时,如何快速找到解决方案。
2. 理解核心心智模型:镜像、容器与仓库
在敲下任何命令之前,我们必须统一思想,建立正确的心智模型。如果把Docker比作一个现代化的物流仓储系统,那么:
镜像(Image) 就是产品的“模具”或“标准化集装箱”。它是一个只读的模板,里面包含了运行某个软件所需的一切:代码、运行时环境、系统工具、库文件、环境变量和配置文件。比如一个nginx:latest镜像,就是一个已经装好Nginx Web服务器并配置好基本运行环境的“标准箱”。镜像是静态的、不可变的,它被存储在本地或远程的仓库里。你可以基于一个镜像,启动无数个完全一样的容器。
容器(Container) 则是根据镜像“模具”生产出来的、正在运行的“产品实例”。它是镜像的运行实体。容器可以被创建、启动、停止、删除。每个容器都是相互隔离的、安全的独立运行环境。继续用物流比喻,容器就是那个从“nginx标准箱”里拿出来,正在某个服务器上实际提供网页服务的具体货品。容器运行时,会在镜像的只读层之上,创建一个可写的“容器层”,所有对运行环境的修改都发生在这里,而不会影响底层的镜像。
仓库(Repository) 就是存放“模具”的仓库,比如Docker Hub。你可以从仓库拉取(pull)公共的镜像,也可以将你自己构建的镜像推送(push)到仓库(无论是公共的还是私有的)进行分享和存储。
这个“镜像-容器”模型是Docker一切操作的基石。几乎所有的Docker命令,都是围绕对这三者的操作展开的:拉取镜像、运行容器、管理容器生命周期、构建和推送镜像。理解了这一点,命令就不再是孤立的字符串,而是有逻辑关联的工具。
注意:很多新手会把“容器”想象成一个轻量级虚拟机。虽然类似,但本质不同。容器共享宿主机的操作系统内核,只是通过命名空间和控制组(cgroups)实现进程、网络、文件系统等资源的隔离,因此它比虚拟机更轻量、启动更快。理解这个区别,能帮你更好地理解一些网络和存储相关的命令行为。
3. Docker命令全景逻辑图与核心工作流
面对几十条命令,最容易让人困惑的是:“我该先用哪个?它们之间有什么关系?” 下面这张我绘制的核心命令关系图,或许能帮你瞬间理清思路:
这个流程图描述了一个最典型的工作流:
- 获取镜像:从仓库
pull一个镜像到本地。 - 运行容器:使用
run命令,基于镜像创建一个新的容器并启动它。 - 管理容器:使用
ps查看容器状态,用start/stop/restart控制其运行,用exec进入容器内部操作。 - 持久化与清理:用
commit将容器保存为新镜像,或用rm/rmi删除不再需要的容器和镜像。
所有的其他命令,都是对这个主干工作流的补充和增强。比如build命令是从Dockerfile构建镜像,logs和stats是观察容器,network和volume是配置容器的运行环境。
接下来,我们就沿着这个工作流,拆解那35条最常用的命令。我会把它们分成几组,并重点讲解那些“看起来简单但坑最多”的参数和组合用法。
4. 镜像管理:一切的起点
镜像操作是Docker的“供给侧”。你的所有容器都源于此。
4.1 搜索与获取镜像
docker search [关键词]
这通常是你的第一个命令。用于从Docker Hub搜索镜像。但别太依赖它,Docker Hub的网页搜索更直观。命令行的主要作用是快速检查某个镜像是否存在。
docker pull [镜像名:标签]
这是你从仓库下载镜像到本地的命令。最关键的技巧在于“标签”。永远不要想当然地使用latest标签。
docker pull nginx默认拉取nginx:latest。docker pull nginx:1.21-alpine拉取特定版本(1.21)和特定变体(Alpine Linux发行版,更小巧)。- 最佳实践:在生产环境中,务必指定明确的版本号,如
nginx:1.21.6。latest标签是流动的,今天和明天拉取的可能是两个不同的版本,这会导致不可预知的行为。
docker images 或 docker image ls
列出本地所有镜像。常用参数:
-a:显示所有镜像(包括中间层镜像,通常很少用)。-q:只显示镜像ID,常用于脚本中需要传递ID的场景。--digests:显示镜像的摘要(SHA256),用于唯一性校验。
一个我常用的组合命令是 docker images --format “table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}”,可以定制化输出格式,让信息更整洁。
4.2 删除与清理镜像
docker rmi [镜像ID或镜像名] 或 docker image rm
删除本地镜像。如果镜像有对应的容器存在(即使容器已停止),删除会失败。你需要先删除容器。
- 强制删除:
docker rmi -f [镜像ID]。慎用!如果该镜像正在被某个容器使用(即使容器没运行),强制删除会导致出现“悬空镜像”,并可能使容器无法重新启动。 - 批量删除:
docker rmi $(docker images -q)会删除所有镜像,这是核弹级别的操作,务必确认!更常见的需求是删除所有未被使用的镜像(悬空镜像):docker image prune。系统会询问你确认,也可以加-f参数强制直接执行。
docker image prune
上面提到过,专门用于删除未被任何容器引用的“悬空镜像”。在持续集成/持续部署(CI/CD)环境中频繁构建镜像时,运行此命令可以释放大量磁盘空间。可以定期执行。
5. 容器生命周期:从创建到消亡
这是Docker命令的核心区,也是最容易出彩和踩坑的地方。
5.1 创建与运行容器
docker run [选项] [镜像名] [命令]
这是Docker最重要的命令,没有之一。它完成了从镜像创建并启动容器的全过程。它的参数多达几十个,但掌握以下几个,你就能应对90%的场景:
-d(detach):后台运行容器。这是运行服务类容器(如Nginx, MySQL)的标配。不加-d,容器会占用当前终端,日志直接输出到屏幕上,你按Ctrl+C容器就会停止。-it:这是两个参数-i(保持标准输入打开)和-t(分配一个伪终端)的组合拳。它是运行交互式容器(比如一个Ubuntu系统)的黄金搭档。docker run -it ubuntu /bin/bash会让你进入一个全新的Ubuntu容器的bash shell,就像登录了一台新服务器。--name:为容器指定一个自定义名称,而不是使用随机生成的字符串。docker run --name my_nginx -d nginx。好的命名能让管理变得无比轻松。-p(publish):端口映射,格式为-p [宿主机端口]:[容器内端口]。docker run -d -p 8080:80 nginx将容器的80端口映射到宿主机的8080端口,这样你访问http://localhost:8080就能看到Nginx页面。这是容器与外界通信的关键。-v(volume):数据卷映射,用于持久化容器内数据或共享宿主机目录。格式为-v [宿主机路径]:[容器内路径]。docker run -d -v /my/data:/var/lib/mysql mysql将MySQL的数据目录挂载到宿主机,这样即使容器被删除,数据也不会丢失。-e(environment):设置环境变量。docker run -d -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql这是启动MySQL容器时必须的。--restart:设置容器退出后的重启策略。--restart=always意味着无论容器因何退出,Docker都会尝试重启它。对于需要保持常驻的服务,这是必选项。--restart=on-failure则只在非正常退出时重启。
一个综合示例:docker run -d --name my_web --restart=always -p 80:80 -v /opt/html:/usr/share/nginx/html nginx:alpine
这个命令启动了一个名为my_web的Nginx容器,它总是自动重启,将宿主机的/opt/html目录挂载到容器的网页根目录,并把容器的80端口映射到宿主机的80端口。
5.2 查看与管理运行状态
docker ps
查看正在运行的容器。这是你使用频率最高的命令之一。
-a:查看所有容器(包括已停止的)。当你找不到某个容器时,首先应该用docker ps -a。-q:只显示容器ID。常用于脚本,例如docker stop $(docker ps -q)可以停止所有正在运行的容器。-f(filter):按条件过滤。docker ps -f “status=exited”查看所有已退出的容器。docker ps -f “name=my_”查看名字包含my_的容器。
docker start/stop/restart [容器名或ID]
启动、停止、重启一个已存在的容器。stop是发送SIGTERM信号允许容器优雅退出,超时后再发SIGKILL。docker restart在需要重新加载配置时非常有用。
docker pause/unpause [容器名或ID]
暂停和恢复容器内所有进程。这不会释放容器占用的内存,但会暂停CPU调度。常用于调试或临时冻结容器状态。
5.3 进入容器与执行命令
docker exec [选项] [容器名] [命令]
在正在运行的容器中执行命令。这是与容器内部交互的主要方式。
-it:同样,黄金搭档。docker exec -it my_nginx /bin/bash会进入一个正在运行的Nginx容器的bash环境。-u:以指定用户身份执行命令,如docker exec -u root my_app whoami。- 与
run的区别:run是从镜像创建一个新容器并执行命令;exec是在一个已存在且运行中的容器内执行额外命令。
docker attach [容器名]
连接到容器的主进程(即docker run时启动的那个进程)的输入输出流。如果你用docker run -d启动了一个容器,后来想看看它的输出日志,可以用attach。但要注意:如果你在attach的终端里按Ctrl+C,会向容器主进程发送SIGINT信号,这通常会导致容器停止! 退出attach而不影响容器,需要按Ctrl+P然后Ctrl+Q。因此,查看日志更推荐使用docker logs命令。
5.4 容器内窥视与信息获取
docker logs [容器名]
获取容器的日志输出。这是排查问题的第一利器。
-f(follow):实时跟踪日志输出,就像tail -f一样。-t(timestamps):显示每条日志的时间戳。--tail N:只显示最后N行日志。docker logs --tail 100 -f my_app是监控应用最新状态的经典组合。--since:显示某个时间点之后的日志,如--since 2023-10-01或--since 10m(最近10分钟)。
docker stats [容器名]
实时显示容器的资源使用情况统计,包括CPU、内存、网络I/O、块I/O。不加容器名则显示所有运行中容器的状态。这是一个非常直观的性能监控工具。
docker top [容器名]
查看容器内部运行的进程列表,类似于在宿主机上执行ps命令。可以快速了解容器内有哪些进程在运行。
docker inspect [容器名或镜像名]
这是一个“瑞士军刀”命令,用于获取容器或镜像的底层详细信息(JSON格式)。当你想知道容器的IP地址、挂载的卷、网络配置、启动命令等所有元数据时,就用它。通常我们会配合grep或格式化工具使用,例如:
docker inspect my_nginx | grep IPAddress获取容器的IP地址。docker inspect --format=’{{.NetworkSettings.Networks.bridge.IPAddress}}’ my_nginx更精确地获取IP。
5.5 容器的转化与清理
docker commit [容器名] [新镜像名:标签]
将容器的当前状态(可写层)保存为一个新的镜像。注意:这不是构建镜像的推荐方式! 它会导致镜像臃肿、构建过程不透明、不可重复。Dockerfile才是构建镜像的正统方法。commit通常只用于紧急情况下的现场保存,或者从运行中的容器中快速创建一个用于调试的临时镜像。
docker cp [容器名]:[容器内路径] [宿主机路径] 和反向操作
在容器和宿主机之间复制文件。docker cp my_nginx:/etc/nginx/nginx.conf ./ 将容器内的配置文件复制到宿主机当前目录。反向操作同理。这是在容器没有挂载卷时,进行文件交换的应急手段。
docker rm [容器名]
删除已停止的容器。如果容器正在运行,需要先docker stop,或者使用docker rm -f强制删除。
- 批量删除所有已停止的容器:
docker container prune。这是清理空间的常用命令,系统会询问确认。也可以使用docker rm $(docker ps -aq),但prune更安全。 - 一个有用的别名:我经常在
~/.bashrc里设置alias drm=’docker rm -f $(docker ps -aq)’,用于快速清理所有容器(慎用)。
6. 镜像构建的艺术:Dockerfile与Build
虽然docker commit可以创建镜像,但真正的工业化标准是使用Dockerfile。
docker build [选项] [构建上下文路径]
根据Dockerfile构建镜像。
-t(tag):为构建的镜像打标签,格式为-t 名称:标签。docker build -t myapp:v1 ..(点)的重要性:命令最后的.指的是“构建上下文”的路径。Docker守护进程会将这个目录下的所有文件(受.dockerignore影响)打包发送给Docker引擎,然后在引擎端根据Dockerfile的指令进行构建。所以,Dockerfile中COPY或ADD命令的源路径,是相对于这个上下文路径的,而不是你执行build命令的终端所在路径。 这是一个常见的混淆点。-f:指定Dockerfile文件路径。如果你的Dockerfile不叫Dockerfile或者不在上下文根目录,可以用-f指定,如docker build -f dockerfiles/Dockerfile.prod .。
构建镜像是一个独立且深入的话题,涉及Dockerfile的指令优化(如多阶段构建)、缓存利用、.dockerignore文件编写等。这里只需记住,docker build是将你的应用代码和环境打包成可移植镜像的标准入口。
7. 网络与存储:容器的左膀右臂
容器默认运行在一个隔离的网络空间里。要让容器之间、容器与外界通信,就需要理解Docker网络。
7.1 网络管理
docker network ls
列出所有Docker网络。默认会有bridge(默认网络)、host(共享宿主机网络)、none(无网络)三个。
docker network create [网络名]
创建一个自定义的桥接网络。相比于默认的bridge网络,自定义网络提供了更好的容器发现功能(可以通过容器名直接通信)和隔离性。docker network create my_net。
docker run --network [网络名] ...
在创建容器时,将其连接到指定网络。docker run -d --name app1 --network my_net myapp。
docker network connect/disconnect [网络名] [容器名]
将一个已运行的容器连接到某个网络,或从某个网络断开。
docker network inspect [网络名]
查看网络的详细信息,包括连接了哪些容器、子网、网关等。
7.2 数据卷管理
数据卷是独立于容器生命周期的持久化数据存储方式,比绑定挂载(-v /host/path:/container/path)更受Docker管理。
docker volume create [卷名]
创建一个命名的数据卷。docker volume create my_data。
docker volume ls
列出所有数据卷。
docker run -v [卷名]:[容器内路径] ...
使用一个已存在的卷。docker run -d -v my_data:/var/lib/mysql mysql。如果my_data卷不存在,Docker会自动创建它。
docker volume inspect [卷名]
查看卷的详细信息,最重要的是它在宿主机上的实际存储路径(Mountpoint)。
docker volume prune
删除所有未被任何容器使用的“悬空卷”。在清理磁盘空间时非常有用,但同样需要谨慎,确认数据已备份。
8. 系统级维护与信息查看
这些命令帮助你管理Docker守护进程本身和查看系统状态。
docker version
显示Docker客户端和服务端的版本信息。在排查问题时,首先确认版本环境是否一致。
docker info
显示详细的Docker系统信息,包括容器和镜像数量、存储驱动、内核版本、CPU/内存总量、运行中的容器数等。这是一个全局健康检查命令。
docker system df
类似于Linux的df命令,显示Docker磁盘使用情况,清晰地告诉你镜像、容器、数据卷和构建缓存各占用了多少空间。当你发现磁盘空间不足时,这是第一个要运行的命令。
docker system prune
这是一个“一键清理”命令。它会删除所有已停止的容器、所有未被使用的网络、所有悬空镜像以及构建缓存。这是一个威力巨大的命令,执行前务必三思,确认没有需要保留的停止状态的容器或镜像。 可以加上 -a 参数来删除所有未被使用的镜像(而不仅仅是悬空镜像)。
9. 组合拳:Docker Compose简化多容器应用
当你需要同时管理多个相互关联的容器(例如一个Web应用容器+一个数据库容器)时,反复使用docker run会非常繁琐。Docker Compose应运而生。
它通过一个docker-compose.yml文件来定义和运行多容器应用。相关的命令也变得非常简单:
docker-compose up:根据docker-compose.yml配置,创建并启动所有服务。-d参数用于后台运行。docker-compose down:停止并删除所有由up创建的容器、网络等资源。docker-compose ps/logs/start/stop/restart:这些命令与单容器的docker命令类似,但作用于docker-compose.yml中定义的所有服务。
虽然docker-compose是一个独立的工具,但在管理复杂应用时,它极大地提升了效率,是单机多容器编排的事实标准。
10. 实战避坑:那些命令背后容易忽略的细节
最后,分享几条从血泪教训中总结出的命令使用心得:
-
-v挂载的权限问题:当你用-v将宿主机目录挂载到容器内时,容器内进程访问该目录的权限,取决于宿主机目录的权限和容器内运行进程的用户UID。如果容器内进程以root(UID=0)运行,通常没问题。但如果以非root用户(如nginx用户,UID可能是101)运行,就很可能因权限不足而失败。解决方法:要么确保宿主机目录对“其他用户”有读写权限(chmod 777,不安全),要么在Dockerfile中创建相同UID的用户,或者在docker run时使用-u参数指定用户。 -
-p端口映射冲突:docker run -p 80:80如果宿主机80端口已被占用,容器会启动失败。使用docker ps看不到,但docker ps -a可以看到一个退出状态的容器,其日志会显示“端口已被占用”的错误。务必先用netstat或ss命令检查宿主机端口占用情况。 -
--restart策略的陷阱:--restart=always很强大,但如果你容器内的应用本身有bug导致快速崩溃重启,Docker会不断地重启它,可能拖垮宿主机。可以配合--restart=on-failure:5来限制最大重启次数。同时,务必确保你的应用有合理的日志输出,方便通过docker logs排查崩溃原因。 -
镜像标签的玄机:拉取或构建镜像时,养成打标签的好习惯。
myapp:v1.0.0远比myapp:latest清晰。在CI/CD流水线中,可以使用Git提交哈希作为标签的一部分,如myapp:git-abc1234,实现构建与代码的精确对应。 -
善用
docker system df和prune:Docker用久了,磁盘空间会被各种缓存、停止的容器、悬空镜像占满。定期执行docker system df查看,并使用docker system prune进行清理(记得先确认)。可以将此命令加入定时任务,但生产环境需格外小心。
命令是工具,理解其背后的设计哲学和工作流程才是关键。把这35条命令融入到“镜像->容器->仓库”的核心流程和“拉取->运行->管理->清理”的日常操作中,它们就不再是冰冷的字符,而是你手中构建、交付和运行应用的得力助手。当你再遇到“virtualisation support not detected”时,你会会心一笑,因为你知道,门后的世界,值得你花时间跨过那道门槛。