☰
Docker命令不用背:镜像、容器、数据卷与网络操作全解析
2026/10/22 3:24:28 网站建设 项目流程

说到Docker基本命令,很多人一上来就盯着docker run背参数,什么-d后台运行、-p端口映射、-v挂载目录,背完一脸懵。我刚开始接触Docker那阵子也是这个状态,命令倒是抄了不少,可真到部署一个项目时还是不知道从哪下手。后来跟着踩了几个坑才想明白一件事:Docker的命令体系根本不需要死记硬背,只要先把“镜像、容器、数据、网络”这四个概念拎清楚,再顺着“拉取-运行-排查-清理”这条操作链去理解,命令自然就记住了,而且用起来顺手得多。

这篇文章我就从一个实际部署者的角度,把Docker最常用、最高频的一批基本命令从头到尾梳理一遍。不追求把官方文档搬过来,而是把每个命令背后“为什么要这么用”“用的时候容易忽略什么”讲清楚,适合刚接触Docker、或者用过一阵子但总觉得命令零零散散的同学参考。

1. 镜像管理:先把“素材”盘明白

Docker里的镜像说白了就是一套打包好的运行环境模板,里面装着代码、依赖、系统库、配置,甚至环境变量。你要跑容器,前提是机器上有这个镜像。这一节先解决镜像相关的几个高频操作。

1.1 获取镜像:pull与版本选择

默认情况下,镜像从公共仓库拉取,最常见的操作就是:

docker pull nginx:latest docker pull nginx:1.24-alpine

第一条会拉取最新版本,第二条会把镜像固定到1.24这个具体版本,并且使用alpine这个精简系统变体。这里我想多说一句版本标签的事情。生产环境里我几乎不推荐latest,因为latest是个浮动指针,今天拉和三个月后拉,得到的内容可能完全不一样。你明明周测还好好的,某天重新部署突然就出诡异的问题,查到最后往往是基础镜像悄悄变了。固定到具体的版本号虽然不能保证绝对不变,但至少能把变化控制在可预期范围内。

至于alpine变体,它体积小,镜像能省几十甚至几百MB,但代价是底层用的是一个精简的Linux发行版,部分依赖需要额外安装,有些软件跑在上面会有兼容性问题。我自己的习惯是:本地调试用普通版本,正式部署时如果确认依赖没问题,再换成alpine版给上线镜像瘦身。

还有一点容易忽略:默认拉的是amd64架构的镜像。如果你用的是ARM架构的服务器或者Apple Silicon的机器,虽然仓库一般会自动匹配,但某些小众镜像是区分平台的。真遇到拉下来跑不了的情况,可以用docker pull --platform linux/amd64强制指定平台。

1.2 查看与删除镜像:images、rmi

docker images docker image ls

这两条命令等价,都是列出本机已有的镜像列表,展示仓库名、标签、镜像ID、创建时间和体积。镜像ID值得多说一句,它默认只显示前12位,但实际操作里用前3到4位就足够定位了,因为冲突概率极低。所以在删除镜像的时候,不需要敲完整的ID,比如:

docker rmi 3f8a

删除镜像用docker rmi,后面跟镜像ID或“仓库名:标签”都可以。但有一类情况会删不掉,系统会提示镜像正被某个容器使用。这个其实不是报错,是Docker的保护机制,防止你把正在运行的容器底座抽走导致现场崩掉。我早期的习惯是直接docker rmi -f强制删,后来基本不这么用了,因为强制删除往往会牵连到依赖它的中间层镜像,反而把环境搞得越来越乱。正确做法是先找到引用这个镜像的容器,把容器处理掉再删镜像。

1.3 离线搬运:save与load

内网环境或者没有公网拉取权限的机器,镜像需要离线导入。这时候用:

docker save -o nginx.tar nginx:1.24-alpine docker load -i nginx.tar

save把镜像打包成tar文件,load再把这个tar还原成本地镜像。需要注意一点:save不会自动压缩,大镜像导出来体积很大。如果你要传文件节省流量,可以先加一层压缩:

docker save nginx:1.24-alpine | gzip > nginx.tar.gz

接收端先解压再load,或者直接用docker load -i配合进程替换也可以。这个操作我在离线部署的场景里用过很多次,尤其是没有内部镜像仓库的临时环境,几乎是唯一可行的分发方式。

2. 容器生命周期:掌控运行中的实例

镜像只是静态模板,真正跑起来的是容器。容器的生命周期操作是整个Docker使用频率最高的一块,也是新手最容易被参数绊住的地方。

2.1 运行容器的完整打开方式

启动容器的基础命令是:

docker run -d \ --name my-nginx \ -p 8080:80 \ -v /opt/html:/usr/share/nginx/html \ nginx:1.24-alpine

拆开看,-d表示后台运行,不加的话终端会直接挂在前台,日志会刷满整个屏幕,一旦Ctrl+C容器也就停了。--name给容器取个名字,不取的话Docker会随机生成一个名字,比如admiring_hopper这种,排查问题时指来指去很不方便。-p 8080:80是端口映射,宿主机8080端口转发到容器内的80端口,语法是“宿主机端口:容器端口”。-v是数据卷挂载,把宿主机的/opt/html目录映射到容器内的Nginx站点目录,这样改宿主机文件就能直接生效,不用进入容器。

这条命令背后还有两个容易忽略的点。一个是容器内的进程必须常驻,否则容器一启动就退出。Nginx本身是前台守护进程所以没问题,但如果你拿它跑一个脚本,脚本执行完容器就变成Exited状态了,这很正常,不是故障。第二个是--rm参数,加了它容器停止时会自动删除自身,适合临时跑测试任务的场景,能省掉手动清理的麻烦,但要注意加了--rm的容器不能用docker commit保存为镜像。

我个人的建议是:docker run里至少把-d、--name、-p、-v这四个参数养成肌肉记忆。这是日常部署最固定的组合,其它参数等到有需求了再查也不晚。

2.2 查看与进入容器:ps、exec、attach

docker ps docker ps -a

docker ps列出正在运行的容器,-a会把已退出的容器也显示出来。这里的表格信息很关键:STATUS列显示Up还是Exited,PORTS列显示端口映射是否生效。我排查问题第一件事就是看docker ps -a,因为很多奇怪的现象,比如“服务连不上”“数据没写入”,本质是容器根本没在运行。

进入运行中容器的标准姿势是:

docker exec -it my-nginx sh

-it代表分配一个交互式终端,后面跟容器名和要执行的命令。这里有个细节:Nginx的官方镜像里不一定有bash,只有sh,所以写docker exec -it my-nginx bash有时候会报错说找不到bash。遇到这种情况换成sh就行,这也是为什么很多文档习惯写sh而不是bash。

attach也可以进入容器,但它有个绕不开的问题:它把容器的标准输入输出直接连接到现在终端上,如果你在某个终端里按了Ctrl+C,信号会直接发给容器内的主进程,可能导致容器停止。所以日常调试我基本只推荐exec,attach我很少用,除非需要观察容器主进程的实时日志。

2.3 停止、重启与删除

docker stop my-nginx docker start my-nginx docker restart my-nginx

stop会先向容器主进程发一个停止信号,留时间给程序做收尾工作,等一段时间后如果还没退出,再强制结束。start是启动一个已存在但处于停止状态的容器,restart就是先停再启,常用于配置改动后的服务重启。

docker kill则是直接强制终止,不给进程留优雅收尾的时间,除非进程卡死无法响应,否则尽量用stop而不是kill。

删除容器用:

docker rm my-nginx

docker rm只能删已停止的容器,运行中的容器会提示错误。确实要强删运行中的容器,加-f参数,但同理,这是最后手段,愿意的话先stop再rm也一样能达到目的。

2.4 把容器保存为镜像:commit正当用途

容器跑起来之后,如果你在里面装了一些东西,比如手动安装了某个工具包,希望之后新起的容器也能用到,可以把它提交成新镜像:

docker commit my-nginx my-nginx:with-curl

但这里我要泼一盆冷水:commit生成的镜像会包含容器里的临时进程数据、可能残留的缓存和会话信息,体积偏大、内容不干净,很难追溯构建过程。团队协作时如果都用commit来维护镜像,最后谁也不知道镜像里装了什么,环境很快变成黑盒。正确做法是用Dockerfile来自动化构建镜像,commit只适合应急和本机快速保存现场,不适合作为常规交付流程。

3. 镜像构建与分发:从Dockerfile到仓库

能跑别人的镜像是基本功,能构建自己的镜像才算真正入了门。这一节讲构建和打标签、上传的基本流程。

3.1 用Dockerfile构建镜像

在项目目录里放好Dockerfile后,一行命令就能构建:

docker build -t my-app:v1.0 .

-t指定新镜像的仓库名和标签,最后的.表示构建上下文是当前目录。这里有个很多人踩过的坑:docker build会把这个目录下的所有内容打包发送给Docker守护进程,如果目录里有node_modules、.git、大视频文件这种东西,构建过程会非常慢。解决方法是写一份.dockerignore文件,把无关内容排除掉,原理和.gitignore一样。我见过有人把几个GB的构建上下文往Docker里塞,光打包就花了十分钟,加了.dockerignore之后整个构建直接缩短到一分钟以内。

Dockerfile里最核心的几条指令也不复杂:FROM选基础镜像,RUN执行构建时命令,COPY复制文件,CMD声明容器启动时的默认命令。新手容易把CMD和RUN搞混,RUN是构建时执行的,CMD是容器启动时执行的,两者时机完全不同。

3.2 镜像命名与推送:tag、push

构建出来的镜像默认只在本地,要推到仓库给其他人用或者部署到服务器,先打个标签再上传:

docker tag my-app:v1.0 your-registry.com/team/my-app:v1.0 docker push your-registry.com/team/my-app:v1.0

tag实际不是复制镜像,而是给同一个镜像ID再增加一个别名引用,所以执行完docker images会看到两个条目,它们的IMAGE ID是一样的,但仓库名和标签不同。推送之前需要先登录仓库,如果用的是公共仓库,登录命令就是:

docker login

如果是自建的私有仓库,登录时把地址带上。这里有另外一个容易出问题的细节:docker push默认要求标签里带上仓库地址前缀,否则它认为是公共仓库的官方地址,会推错地方甚至报错。所以自建仓库的镜像名一定要写成“仓库地址/命名空间/镜像名:标签”这个完整格式。

4. 数据与网络:容器之外的两块拼图

容器本身是无状态的,一旦删除,容器内产生的数据就全没了。网络则是容器与外界、容器与容器通信的通道。这两块内容虽然不属于“基本命令”中最基础的那几个,但实际生产中早晚要碰到,所以单开一节讲。

4.1 数据卷:让数据活下来

docker volume create mydata docker volume ls docker volume rm mydata

创建数据卷之后,运行容器时挂载它:

docker run -d --name app -v mydata:/app/data my-app:v1.0

数据卷和普通目录挂载的区别在于,卷由Docker自己管理,不依赖宿主机的具体路径,而且多个容器之间可以共享同一个卷。数据卷还有一个巨大的优势:备份、迁移、权限管理都比直接挂目录干净。我自己的使用偏好是:临时调试用宿主机目录挂载,正式服务的持久化数据优先用命名卷。

宿主机目录挂载的典型写法是-v /opt/data:/app/data,注意这里有个权限问题:容器内进程运行时会以容器内用户的身份写文件,如果容器内是root,它创建的文件就归root所有,宿主机上的普通用户想删除可能要加sudo。这个坑在日志目录和上传文件目录上经常遇到,解决方法是启动容器时用--user指定UID,或者事后调整宿主机的目录属主。

4.2 网络操作:连接容器与外界

docker network ls docker network create my-net docker network inspect my-net

创建一个自定义网络后,容器可以加入这个网络:

docker run -d --name app1 --network my-net my-app:v1.0 docker run -d --name app2 --network my-net my-app:v1.0

同一个自定义网络里的容器互相访问,不需要通过IP地址,直接用容器名就能解析。比如app1里访问app2,直接写http://app2:8080就可以,这个特性对微服务之间互相调用的场景非常实用。对比一下,如果两个容器在默认的bridge网络上,访问对方需要查IP,而容器重建后IP是会变的。所以我自己只要有多容器互通的需求,一定会先创建自定义网络,让容器名作为稳定的服务地址。

查看一个容器当前的网络配置和端口映射,用:

docker port my-nginx docker inspect my-nginx

docker port只显示端口映射的对应关系,docker inspect会输出一大段JSON格式的详细信息,包括容器的IP地址、挂载卷、环境变量、网络模式等。排查问题的时候,docker inspect几乎是必用的命令,只是输出内容很长,建议配合grep或者--format参数提取关键字段来用。

5. 日志、资源与状态排查

容器跑起来只是第一步,更重要的是持续观察它的状态、日志和资源占用。这一节的命令都是在实际运维过程中高频使用的。

5.1 查看日志:logs

docker logs my-nginx docker logs -f --tail 100 my-nginx

docker logs输出容器内的标准输出和标准错误,-f表示持续跟踪日志,和tail -f行为一致,-t给每行日志加上时间戳,--tail只显示最近N行。我排查问题时的标准操作是先--tail看最近的报错,再-f跟一下实时输出确认问题是否还在复现。日志里没有信息的时候,才考虑进容器看文件。

还有个容易被忽略的点:docker logs只能看到进程打到标准输出和标准错误的日志,如果应用里的日志框架写到了文件而没有输出到控制台,docker logs是什么都看不到的。这也是为什么多数应用镜像都习惯把日志输出重定向到stdout而不是写到容器内的日志文件,因为在容器环境里,stdout日志才能被统一收集。

5.2 资源占用与实时监控:stats、top

docker stats

docker stats会动态显示本机所有运行中容器的CPU、内存、网络、磁盘IO占用情况,相当于一个轻量级的容器任务管理器。它可以不带参数直接运行,也可以跟着具体的容器名只查看特定容器的情况。

docker top可以看容器内的进程列表,类似于在宿主机执行ps aux,但它显示的是容器内部的进程视角。当看到容器CPU飙高、负载异常时,先docker top看有没有额外进程,再用docker stats对比资源使用趋势,基本能定位是容量问题还是进程异常问题。我印象比较深的一个场景是排查内存泄漏,用docker stats --no-stream定时抓取容器内存数据,配合日志分析缓慢增长的趋势,比直接在容器里敲命令要直观。

5.3 查看容器详情:inspect的有用片段

docker inspect输出信息量太大,实际使用需要提取字段。举个例子,想查容器的IP地址:

docker inspect -f '{{.NetworkSettings.IPAddress}}' my-nginx

想查容器的挂载来源:

docker inspect -f '{{json .Mounts}}' my-nginx

-f后面的Go模板语法初看有点陌生,但用多了记住这两个常用写法就够了。我实际使用中更多是在写脚本时用docker inspect去拿容器的状态、端口映射的宿主机端口、挂载卷的名称这些信息,属于自动化场景里的重要数据源。

6. 清理与瘦身:别让Docker悄悄占满磁盘

Docker用得越久,本机的悬空镜像、已退出的容器、废弃的卷和网络会越积越多,磁盘占用经常在不经意间暴涨。这一节讲清理相关的命令和我的日常维护习惯。

6.1 查看磁盘占用

docker system df

这条命令会分项显示镜像、容器、数据卷、构建缓存各自占用的磁盘空间总量,外加一个“可回收”的提示。我建议安装Docker之后先养成定期跑一下docker system df的习惯,尤其服务器磁盘告警的时候,第一件事就是看这里。

6.2 清理悬空资源

docker image prune docker container prune docker volume prune docker network prune

这几条命令分别清理悬空镜像、已停止的容器、未被容器引用的数据卷、未被容器使用的自定义网络。它们各自都有-a参数,加了之后清理范围更大。比如docker image prune -a会把所有没有被容器使用的镜像都删掉,而不只是悬空镜像。

这里我特别提醒docker volume prune:数据卷一旦删除,里面的数据就彻底没了,没有回收站。如果某个数据卷里是数据库文件或者用户上传的文件,直接prune等于事故。生产环境我基本不主动prune卷,宁可它多占点空间,也不敢冒丢失数据的风险。

6.3 一键清理与风险控制

docker system prune docker system prune -a

docker system prune会把所有已停止的容器、悬空镜像、无用网络和构建缓存一并清理,-a还会把所有没有被容器使用的镜像都删掉。用起来确实方便,但我必须泼一盆冷水:这个命令在生产环境要格外谨慎,尤其是-a,它不区分“暂时没用的镜像”和“下个星期要回滚的镜像”,一把梭全没了。我的做法是开发机上随便用system prune,生产机上只做定向清理,每次清理前先docker system df和docker ps -a看清楚现状再动手。

7. 常见问题与排查技巧实录

最后这一节,我整理几个平时被问得最多、也是我自己踩过的坑,按“现象-原因-解决”的方式列出来,给大家当一份速查参考。

7.1 容器一直重启

容器状态显示Restarting,说明容器内部进程启动失败或者在启动后很快崩溃。我习惯先看日志:

docker logs --tail 100 容器名

日志里有明确报错就顺着报错处理,没有日志的话大概率是应用本身启动就抛异常,这时候要检查挂载目录是否为空、环境变量是否缺失。还有一种情况是启动命令写错了,比如Dockerfile的CMD指向了一个不存在的脚本,也会导致无限重启,这种问题看日志一般有No such file之类的提示。

7.2 端口映射不生效

表现是宿主机访问不了服务,但docker ps里端口映射那一列有显示。先确认容器内的服务有没有真的监听容器端口,比如容器内的Nginx如果配置错误没起来,docker ps一样显示端口映射,但实际端口根本没有进程监听。排查方法:

docker exec -it 容器名 netstat -tlnp

容器内没有netstat的话,可以在宿主机验证如果是Nginx这种有默认页面的服务,先curl http://127.0.0.1:映射端口确认是不是服务本身的问题,再考虑防火墙和安全组原因。

7.3 镜像删不掉提示被占用

之前提过,这个不是故障是保护机制。先通过docker ps -a找到引用该镜像的容器,能删的删掉,不能删的就先留着,镜像自然就能删了。简单粗暴用-f容易出现中间层镜像残留,不推荐。

7.4 容器内时区不对

默认的容器时区是UTC,和国内相差8小时,日志时间看起来总是“慢8个小时”。解决方案最标准的是启动时挂载时区文件:

docker run -v /etc/localtime:/etc/localtime:ro ...

但如果容器的系统镜像里没有安装tzdata,光挂载文件可能无效。可靠的做法是在Dockerfile里加:

RUN apt-get update && apt-get install -y tzdata ENV TZ=Asia/Shanghai

或者通过环境变量TZ传入容器。很多应用框架会自动读取TZ环境变量来调整日志时间,这个方式比挂载文件更保险。

7.5 日志文件暴涨

容器一直打印日志,docker logs没有限制的话日志文件会无限增长,最终占满磁盘。最简单有效的办法是在/etc/docker/daemon.json里设置日志轮转:

{ "log-driver": "json-file", "log-opts": { "max-size": "20m", "max-file": "5" } }

设置后每个容器日志文件单份最大20MB,保留5份,超过就自动滚动。配置完记得重启Docker守护进程。这个配置我在多台服务器上都提前布好,省掉了不少半夜清磁盘的麻烦。

7.6 exec进不去容器

docker exec -it 容器名 bash提示unable to find ... bash,前面说过是这个容器镜像里没有bash,换成sh通常就能解决。如果应用镜像是特别精简的发行版,可能sh都没有,只有ls这种极简工具,这时候要考虑用docker inspect看启动命令,或者检查是不是容器本身状态不对。还有一种情况是操作系统没有装载合适的终端设备,试试不加-t只留-i,交互方面会受限,但有时能进去执行些命令。

8. 写在最后的几条实操经验

Docker命令本身不复杂,真正让命令变复杂的是对背后机制的理解。我在实际使用中慢慢总结出几条小经验,分享给大家参考。

第一,多看docker inspect的输出。很多问题的答案其实都在里面,容器的状态、环境变量、挂载源、网络IP、标签信息,它全都记录着。只是默认输出太长,大家懒得看。配合--format筛选关键字段,它就是你最强的排查工具。

第二,养成清理的习惯。每次操作完一批容器和镜像,顺手把不用的一次性容器删掉,悬空镜像定期清理,别等磁盘满了再临时抱佛脚。我自己会在每周找个时间跑一次docker system df,看占用情况再决定要不要清理。

第三,给容器起名字真的很重要。虽然默认随机名看起来很有个性,但生产环境里维护起来不方便。按项目名或者服务用途起名,不管是脚本处理还是人工排查,认名字比认ID高效得多。

第四,善用docker history看镜像的构建记录。当你拿到一个第三方镜像却不清楚它内部做了什么的时候,docker history 镜像名能看到它的每一层构建操作,是了解镜像底细最快的方式。这个命令用的人不多,但排查“为什么这个镜像这么大”“这里面到底装了什么”这类问题时特别有用。

第五,也是我自己最深的体会:从“背命令”转变到“理解生命周期”,Docker才算真正用起来了。镜像、容器、卷、网络这四个对象和它们之间的引用关系,比任何一条具体命令都重要。命令只是操作这些对象的手段,理解对象本身之后,遇到没见过的参数也能猜到它是干什么的,排查问题时也能沿着“镜像对不对-容器状态对不对-网络通不通-数据在不在”这条链路去定位,而不是漫无目的地试命令。

希望这篇梳理对你有用。Docker的基本命令本质上是几个固定套路,动手跑几次、踩过几次坑,比看多少文档都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询