每次带新人或者给团队做分享,我都会被问到同一个问题:Docker命令这么多,到底该怎么记,哪些又是真正常用的?说实话,网上关于Docker常用命令的文章一抓一大把,但很多要么只丢给你一张命令速查表,要么堆了一堆生僻参数,看完还是不知道该在什么场景下用。这篇文章我不打算做那种“命令字典”,而是按实际使用场景把高频命令串起来讲,从镜像管理、容器生命周期到网络、数据卷、Compose,每一组命令我都会告诉你它是用来干什么的、为什么要这样写,以及我在真实运维中踩过哪些坑。不管你是刚接触Docker的新手,还是在Linux服务器上部署过几个容器的开发者,看这一篇应该都能有收获。
1. 镜像管理:拉取、查看、删除的一整套打法
镜像之于容器,就好比ISO安装包之于虚拟机。日常操作中,绝大部分时间都花在“找镜像”和“拉镜像”上。这部分命令虽然简单,但组合起来用能解决不少低级问题。
1.1 拉取镜像时最容易被忽略的两个细节
拉镜像的命令是docker pull,但你真不一定每次都把参数写对。我在服务器上执行过最多的完整写法是:
docker pull mysql:8.0 docker pull nginx:1.25-alpine这里有两个细节值得你注意。
第一,一定要写tag,不要直接docker pull mysql。不写tag时默认拉取latest,而latest是个会变动的标签。今天拉下来的是8.0,过几个月再拉可能就变了。生产环境里我见过不止一次因为latest漂移导致重启服务后行为不一致的事故,所以现在凡是写进脚本或compose文件的镜像,全部固定到具体版本号。
第二,能选alpine就尽量选alpine变体。同样的Nginx,普通版本镜像体积在190MB左右,而nginx:1.25-alpine只有40多MB。不仅是下载快、占磁盘少,更重要的是alpine系镜像的攻击面更小,里面默认没有bash、没有多余工具链,出了问题反而不容易被乱改。代价是一些依赖glibc的软件跑不了,但这在绝大多数Web服务场景下不算问题。
如果你要查某个镜像有哪些tag,可以去仓库官网看,或者直接用命令拉取时让服务端报错——当tag不存在时,Docker会返回可用列表。虽然不优雅,但应急时能用。
1.2 给镜像打tag:比想象中更有用的操作
很多人以为docker tag就是给镜像起个外号,实际上它的作用是把镜像归入某一个仓库路径和版本下。我在和内网镜像仓库打交道时,最常用的是这样:
docker tag nginx:1.25-alpine registry.internal.example.com/nginx:1.25-alpine docker push registry.internal.example.com/nginx:1.25-alpine打tag的本质是给镜像ID加了一个引用。同一个镜像ID可以挂多个tag,都不会额外占用磁盘空间。所以你在本地测试时想模拟“某个版本被重新发布”,完全不用重新拉镜像,打好tag直接推就行。
还有个小技巧:当你想把本地镜像导出给同事用时,除了docker save存成tar包,打tag推到公共仓库再让他pull也是一种协作方式。虽然多了一步认证,但胜在方便增量传输。
1.3 查看镜像:别只会用docker images
列出本地镜像用docker images确实没错,但有个更直观的命令容易被忽略——docker image ls。两者输出几乎一样,我习惯把docker image当作一个子命令体系来记:
docker image ls docker image inspect mysql:8.0 docker image history mysql:8.0inspect命令会输出一个超长的JSON,里面有镜像的环境变量、工作目录、端口声明等所有原数据。调试容器起不来但不知道默认配置时,inspect就是你能找到的最权威的文档之一,而且它是直接从镜像本身读出来的,绝对和实际运行一致。
docker image history则能看到镜像的每一层是怎么构建出来的,包括每一层的命令和大小。排查“为什么镜像这么大”时,这个命令比docker images有用得多。我曾经排查过一个莫名其妙涨到2GB的Java镜像,一查history才发现是构建过程中把整个.m2仓库拷进去了,后面没清理干净,白白多了1.5GB。
1.4 删除镜像的进阶操作
删除镜像的基本命令是docker rmi <镜像ID或名称>,但实际场景往往是容器还在占用镜像,导致删除报错。这时候要先删容器或先停容器。
批量清理时我推荐两条命令,一个是删除所有悬空镜像:
docker image prune悬空镜像指的是没有tag、也没有被任何容器引用的镜像层残留。每次用docker build重新构建同名镜像后,旧版本就会变成悬空镜像,日积月累非常吃磁盘。加上-a参数会连未被容器使用的所有镜像一起删,谨慎使用,建议在测试环境跑一次看清楚再上生产。
这里我要特别提醒一句:千万不要在服务器上随手执行docker system prune -a --volumes。这条组合命令会把所有未运行的容器、未使用的网络、没有容器引用的镜像和所有未被容器使用的数据卷全部干掉。数据卷一旦删除,想找回比登天还难。我见过有人辛辛苦苦搭的数据库容器,一条清理命令下去,数据直接蒸发。真要清理空间,咱们把命令拆开一点一点执行。
2. 容器生命周期管理:核心中的核心
这一章节是Docker命令里最值钱的部分。容器生命周期管理命令包括创建、启动、停止、删除、暂停等,一旦理解清楚容器的状态流转,后面的网络、数据卷都好学得多。
2.1 docker run的关键参数,别急着背全
docker run是参数最多、最灵活的一条命令,但多数人只需要记住几个高频组合。
一个典型的Web服务启动命令长这样:
docker run -d \ --name my-web \ -p 8080:80 \ -v /opt/nginx/html:/usr/share/nginx/html:ro \ --restart unless-stopped \ nginx:1.25-alpine参数拆开来看:
-d:后台运行。不加它,前台日志会一直刷屏,按Ctrl+C容器就停了。--name:给容器起名字。之后所有操作都可以用名字来引用,不用记那一长串容器ID。-p 8080:80:端口映射。宿主机8080端口转发到容器内80端口。-v /opt/nginx/html:/usr/share/nginx/html:ro:将宿主机目录挂载到容器内,ro表示容器内只读。这是修改Nginx页面而不重新进入容器最常用的方式。--restart unless-stopped:重启策略。服务器重启后Docker会自动拉起容器,但如果你手动docker stop了它,它就不会被再次启动。生产环境绝大多数容器都该配这个策略。
-e环境变量参数我再单独提一下。很多镜像首次启动时需要注入配置,比如MySQL的root密码:
docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=MyPass123456 \ -e TZ=Asia/Shanghai \ mysql:8.0-e是明文写在命令行里的,因此不要在里面传生产环境的敏感密钥。你在服务器上执行history命令时,这些密码会暴露在shell历史里。真到了生产环境,优先使用--env-file参数从文件加载:
docker run -d --env-file .env mysql:8.02.2 查看容器状态:ps命令的三种打开方式
列出正在运行的容器用docker ps,列出所有容器(包括已停止的)用docker ps -a。这个命令太常用了,但还有两个值得记的参数:
docker ps -a docker ps -a --filter "status=exited" docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"--filter可以按状态过滤容器,比如只看异常退出的容器,在批量清理时非常有用。--format可以用Go模板自定义输出字段,服务器上容器一多,默认输出能把屏幕挤爆,精简成三列后看着清爽多了。
我给新人培训时常说一句话:先ps看清现状,再决定下一步操作,永远不要在不清不楚的情况下执行批量清理命令。
2.3 启动、停止、重启和“暂停”的区别
启动和停止容器的命令其实是一组对称操作:
docker start my-container docker stop my-container docker restart my-containerstop会先给容器内主进程发SIGTERM信号,等待一段宽限期(默认10秒)后再发SIGKILL强杀。这个宽限期可以通过-t参数调整,比如:
docker stop -t 30 my-container如果你的应用在停止前需要做数据落盘、清理临时文件之类的操作,把宽限期调大一点是必要的。我调优过一个内部服务,默认10秒停止时间总是不够,日志里频繁出现“killed before graceful shutdown completed”,后来调整到60秒就正常了。
docker pause和docker unpause用的是Linux的cgroup freezer机制,把容器内的所有进程挂起,但不终止进程。这个命令在诊断故障时非常有用——比如容器内出现CPU飙高问题,你想保留现场又不让它继续产生日志时,先pause再调查,比直接stop更从容。
2.4 删除容器时那些纠结的选择
删除已停止的容器用docker rm <容器名>,删除正在运行的容器需要加-f强制删,但我不建议你一上来就rm -f。
有一种情况必须依靠强制删除:容器内部进程卡死,stop等了很久也停不掉。这时候可以:
docker rm -f my-containerrm -f的原理是先对容器主进程发SIGKILL,再删除容器的可写层。注意,可写层里如果存了数据,删除容器后这些数据就没了。所以再次提醒:需要持久化的数据一定要用数据卷或挂载目录,而不是顺手写在容器里。
批量删除已退出容器是我几乎每天都会执行的操作:
docker rm $(docker ps -aq --filter "status=exited")先拿到所有已退出容器的ID列表,再传给docker rm。不放心的话可以先执行docker ps -aq --filter "status=exited" | head看几条再删。命令行操作最忌“凭感觉写一条没人验证过的清理命令”。
3. 进入容器、看日志和拷贝文件
容器跑起来了,但你不可能永远只靠docker ps看个健康状态。日常维护中更常做的是进入容器内部执行命令、看报错日志、把文件拷出来分析。
3.1 进入容器的exec和attach到底有什么区别
进入正在运行的容器执行命令,推荐使用docker exec:
docker exec -it my-container bash-i表示保持标准输入打开,-t表示分配一个伪终端,两个参数配合起来才能得到一个可以交互的shell。如果容器内没有bash,就用sh:
docker exec -it my-container shalpine镜像默认只有sh,Ubuntu系镜像才有bash。很多人在alpine容器里执行docker exec -it xxx bash然后报错,并不是命令错了,而是容器里没装bash,换sh就好。
docker attach则是把当前终端连接到容器主进程的标准输入输出上。它的行为更像“接入主进程”,如果你在容器里跑的是Nginx,attach进去不会得到shell,只会看到Nginx的访问日志。按Ctrl+C可能直接给容器主进程发送中断信号。所以我几乎不用attach做日常操作,只有在排查某些初始化脚本交互行为时才会用它。
如果你只是想执行一条命令而不想进入交互shell,可以直接:
docker exec my-container cat /etc/nginx/nginx.conf这条命令会把文件内容直接输出到当前终端,不会真的“进入”容器。批量巡检时非常方便,比如同时查看多个容器的运行时间:
docker ps --format "{{.Names}}" | xargs -I {} docker inspect --format '{{.Name}} Uptime={{.State.StartedAt}}' {}3.2 看日志的高频套路
查看容器日志用docker logs,这是排查问题时我第一个敲的命令:
docker logs my-container docker logs --tail 200 -f my-container--tail 200只显示最近200行,避免日志量太大刷屏;-f是持续跟踪输出,类似tail -f,适合启动容器后观察是否正常。
一个容易踩坑的地方是:有些进程把日志输出到文件而不是标准输出,这时候docker logs什么都看不到。Docker日志收集机制只捕获容器内PID 1进程的stdout和stderr。要解决这个问题,要么在启动命令里把日志同时symlink到/dev/stdout,要么用docker exec去容器里看实际日志文件。
日志量大的服务,默认的json-file日志驱动会无限占用磁盘。强烈建议给Docker加一个日志轮转配置,在/etc/docker/daemon.json里写好:
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" } }这个配置表示单个日志文件最大50MB,最多保留5个文件。改完配置要重启Docker守护进程才会生效,而且只对之后创建的容器生效。已经跑着的容器,趁版本发布重启时顺便重建一下即可。
3.3 与宿主机之间拷贝文件
容器里有个配置文件想改一下,又不想进容器装vim,最简单的方式是拷出来改完再拷回去:
docker cp my-container:/etc/nginx/nginx.conf ./nginx.conf.bak docker cp ./nginx.conf my-container:/etc/nginx/nginx.confdocker cp还能在容器和容器之间做中转,先从一个容器拷出来,再拷进另一个容器,虽然少见,但比绕道宿主机要省事得多。
拷贝完文件后记得重启容器让配置生效,很多nginx配置不是热加载的,别折腾半天发现改动没被读进去。
3.4 资源占用查看:top、stats和port
查看容器内部进程列表用:
docker top my-container它本质上读取的是宿主机上对应进程的信息,输出格式和Linux的top类似。实时查看所有容器的CPU、内存、网络IO,用:
docker statsdocker stats就像Docker版本的htop,不加参数时每秒刷新一次。排查“哪个容器把服务器内存吃光了”时,这是最直接的手段。也可以指定容器名只盯一个:
docker stats --no-stream my-container加--no-stream是只输出当前一次快照而不持续刷新,适合在脚本里抓数据。
查看端口映射关系用:
docker port my-container它会列出容器内端口映射到了宿主机的哪个端口。当你在服务器上配置防火墙时,需要确认的就是这个映射结果,比如容器跑起来了但外部无法访问,先用docker port确认宿主机端口是不是真的被监听了。
4. 网络与数据卷:容器间协作的两大基础设施
单容器跑服务不难,难的是多个容器互联、数据持久化。这部分命令不多,但背后涉及Docker的网络模型和数据卷设计,理解它们才能真正做到“部署不慌”。
4.1 玩转docker network:从默认bridge到自定义网络
Docker安装后默认有三个网络:bridge(默认桥接网络)、host(主机网络)、none(无网络)。默认情况下,所有容器都会连到bridge网络上,容器之间通过IP可以互访,但不推荐直接依赖IP,因为容器重建后IP会变。
最优雅的做法是创建一个自定义网络,然后让容器用容器名互相访问:
docker network create my-net docker run -d --name web1 --network my-net nginx:1.25-alpine docker run -d --name web2 --network my-net nginx:1.25-alpine docker exec web1 ping web2在自定义网络里,Docker内置DNS会把容器名解析成对应IP。这就是我建议所有多容器项目都创建自定义网络的原因。默认bridge网络虽然能容器互联,但不支持这种自动DNS解析,用起来特别憋屈。
查看网络列表和网络详情:
docker network ls docker network inspect my-netinspect会列出网络里有哪几个容器、IP分配情况、子网掩码等。排查两个容器能不能通信时,先确认它们是不是在同一个网络下。
把运行中的容器连到另一个网络,用:
docker network connect my-net web1 docker network disconnect old-net web1这在多环境迁移时很实用。容器启动后才发现没连对网络,不需要重启重建,直接connect就能补上。
4.2 数据卷:命名卷和bind mount的正确选择
Docker数据持久化有两个主流方案:命名卷(named volume)和bind mount(宿主机目录挂载)。
命名卷的常见命令是:
docker volume create mydata docker volume ls docker volume inspect mydata docker run -d --name db -v mydata:/var/lib/mysql mysql:8.0命名卷由Docker管理,位置在/var/lib/docker/volumes/下,你不用关心它在宿主机上的具体路径,迁移时用docker run --volumes-from或者备份卷目录即可。缺点是文件分散在系统目录内,想直接在宿主机上查看不太方便。
bind mount则直接指定宿主机路径:
docker run -d --name web -v /opt/html:/usr/share/nginx/html:ro nginx:1.25-alpine优点是你可以在宿主机上用vim直接改文件,改完容器里立刻可见,这个方式特别适合开发调试,或者部署那些配置在宿主机上改起来更顺手的应用。一个常见的取舍是:数据库等需要严格备份的应用,用命名卷;Web页面、配置文件等需要经常在宿主机改的,用bind mount。
清理数据卷用:
docker volume prune它会删除所有没有被容器使用的数据卷。执行之前务必看清楚列表。
还有一个经常被忽略的命令docker run --volumes-from,可以复制另一个容器的数据卷挂载。在做数据迁移或者临时开一个备份容器时很实用:
docker run --rm --volumes-from db -v $(pwd):/backup alpine tar cvf /backup/db-backup.tar /var/lib/mysql这条命令利用一个临时alpine容器,把db容器里的MySQL数据目录打包成tar放到宿主机当前目录。不需要停库,虽然数据一致性问题在写频繁的库里存在,但做个粗略备份足够了。
4.3 容器间互联的host网络和端口映射经验
默认bridge模式下,容器访问宿主机服务,不少人会误写成容器IP或localhost。实际上容器内的localhost是容器自己,要访问宿主机上的服务时,常见做法是使用host.docker.internal(Docker Desktop环境)或者在Linux服务器上用--network host直接共享宿主机的网络栈。
docker run -d --name app --network host my-imagehost模式下容器不会创建自己的网络命名空间,直接使用宿主机IP,因此也不需要-p做端口映射。性能好一些,但容器之间没法用隔离的端口空间,容易出现端口冲突。我的经验是:单机部署、追求低延迟的场景可以用host模式,其他情况优先用bridge +-p映射,这样更灵活也更安全。
5. 构建镜像与Docker Compose:批量部署的利器
拉取现成镜像只是Docker用法的第一步。日常开发中,你还需要构建自定义镜像,并且用Compose把多个服务一次性编排起来。
5.1 docker build和.dockerignore的使用要点
构建镜像的核心命令是:
docker build -t my-app:1.0.0 .-t指定镜像名称和tag,最后的.是构建上下文路径。Docker会把这个目录下的所有文件发送给守护进程作为构建上下文。为了不让本地垃圾文件(比如node_modules、.git、target)一起打过去,要养成写.dockerignore的习惯,类似.gitignore:
node_modules .git *.log Dockerfile .dockerignoreDockerfile文件本身默认也会被当作构建上下文的一部分,但写进.dockerignore后就没法在构建中使用它了,一般不会有人需要,所以不写进去就行。
构建时如果希望复用缓存加速,构建命令会自动做缓存,只要Dockerfile每层指令没有变化,后续构建会快很多。但缓存有时会让人踩坑——比如你用apt-get install装包,源更新了但Docker仍用缓存层。碰到这种情况,可以在Dockerfile中该指令之前用ARG CACHEBUST=1并在构建时传不同值,强制绕开缓存。或者干脆加一行:
docker build --no-cache -t my-app:1.0.0 .5.2 docker commit:临时镜像的救命稻草
docker commit能把一个正在运行的容器保存为新镜像:
docker commit my-container my-image:snapshot这种方式不推荐用于生产,因为它把容器的可写层整个打包,镜像是黑盒、不可复现。但在应急场景下它是救命稻草——比如某个容器里手工装了一堆工具、改了配置,还没写成Dockerfile,容器又急着迁移或备份,commit一下能快速度过难关。
提交之后记得用上-m写清备注,不然过了两周你自己也看不出这个镜像是什么状态下提交的。
5.3 docker compose:优雅编排多容器
手动跑docker run解决单容器很方便,但一套系统需要MySQL、Redis、后端服务、前端页面四个容器时,每一条都要手动敲参数,改一个端口还得重新复制粘贴,很容易出错。Docker Compose就是为了解决这个问题。
一个最简的docker-compose.yml长这样:
version: "3.9" services: mysql: image: mysql:8.0 container_name: my-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: MyPass123456 TZ: Asia/Shanghai ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql redis: image: redis:7-alpine container_name: my-redis restart: unless-stopped ports: - "6379:6379" app: build: ./app container_name: my-app restart: unless-stopped depends_on: - mysql - redis ports: - "8080:8080" volumes: mysql-data:在项目目录下执行:
docker compose up -dup会根据YAML文件创建并启动所有服务,-d表示后台运行。常用的组合还有:
docker compose ps docker compose logs -f app docker compose exec app bash docker compose down docker compose down -vdown会停止并删除所有由up创建的资源。down -v代表连定义在volumes块里的数据卷也一起删除。如果你只是想停服务保留数据,用down不加-v,或者干脆docker compose stop。
docker compose up -d之后如果要重建某个服务,比如改动了Dockerfile或YAML配置:
docker compose up -d --build app这条命令只会重建app服务,不影响其它已经在跑的服务。Compose是为单机多容器设计的,远程集群场景交给Kubernetes或Docker Swarm处理。
6. 系统级命令与空间回收:做一个负责任的运维
除了针对单个镜像或者容器的命令,Docker还提供了一组系统级命令,用来查看守护进程状态、清理磁盘空间、跟踪事件。很多“服务器空间不足”的告警,最终都是靠这些命令定位的。
6.1 查看Docker引擎信息和版本
刚接手一台陌生服务器时,先敲三条命令确认环境:
docker version docker info docker system dfdocker version显示客户端和守护进程的版本号。注意返回内容包含Client和Server两部分,如果Server部分连不上,说明Docker守护进程没起来,先查systemctl status docker。
docker info内容更丰富,包含存储驱动、CPU核数、内存大小、镜像数量、容器数量等。排查资源问题时,我习惯先看这里确认Docker能使用的系统资源上限。
docker system df是磁盘空间排查利器,它会把Docker各类资源占用的磁盘列成一张表,包括镜像、容器、本地卷、构建缓存各自占了多少,以及可回收空间是多少。
6.2 清理系统空间的正确姿势
我把空间清理分成三级:
第一级,只清悬空数据:
docker image prune docker builder prune这两条分别清理悬空镜像和不再被使用的BuildKit构建缓存,是相对安全的操作,不会影响正在运行的容器。
第二级,清未被容器使用的资源:
docker container prune docker network prunedocker container prune只删已停止的容器,不会动运行中的。network prune删掉未被任何容器引用的自定义网络。这两步执行完往往能腾出不少空间。
第三级,全面清理但保留数据卷:
docker system prune -a这条命令会删除所有未被运行中容器使用的镜像,包括那些没有tag的中间镜像和下载后用完的临时镜像,但默认不会删除数据卷。虽然清理力度大,但执行前最好先跑一次docker system df看清要被清掉的东西有多大。
如果在最前面加上--volumes,就是我一直警告大家慎用的“核弹级清理”:
docker system prune -a --volumes数据卷是容器持久化数据的根本,一条命令全没了。我宁可让你分三步执行,也别为了省事一键梭哈。
6.3 查看实时事件日志
docker events是一条比较低调但实用的命令,它会实时输出Docker守护进程接收到的操作事件:
docker events --filter "type=container" --filter "event=die"比如你可以用这条命令监控有没有容器意外退出。在自动化运维脚本里,结合docker events和另一个进程做告警,能第一时间发现异常。平时排障时,直接docker events挂在那里,再手动启动一个容器,你就能在事件流里看到pull、create、start、die等完整流程。
7. 高频故障排查:那些绕不开的坑
最后分享一组我在实际运维中反复遇到的故障场景,每个问题都对应明确的现象和排查思路。这部分内容算是我个人经验的沉淀,不是网上常见的报错列表,而是真实踩坑后的记录。
7.1 容器启动后立刻退出
现象:docker ps看不到容器,但docker ps -a能看到一堆状态为Exited (0)或Exited (1)的记录。
排查方法:先看退出码。0表示进程主动正常退出,比如你启动一个没灌前台命令的busybox容器,它跑完默认cmd就退出了;非0退出码则说明进程出错。看日志:
docker logs 容器名常见原因:前台进程没有保持运行。比如启动Nginx时必须让nginx以daemon off方式跑,否则Nginx进程fork到后台后,容器的PID 1进程就认为任务结束直接退出。运行一个Web服务,其实本质是要让一个进程持续占着前台。很多镜像默认CMD已经做好了,但你要覆盖默认CMD时就会遇到这坑。解决方案是启动命令里加-g "daemon off;"或使用tail -f /dev/null保持前台(临时测试用,不推荐生产)。
7.2 端口被占用
现象:docker run -p 8080:80报错,提示bind: address already in use。
排查方法:
lsof -i :8080 ss -ltnp | grep 8080看到是哪个进程占了端口后,要么停掉旧进程,要么把宿主机端口换一个。这里有一个经验之谈:不要为了省事直接用-p 80:80去碰1024以下端口,非root进程无法绑定,Docker虽然能通过自己的iptables规则处理,但如果你机器上还有别的Web服务,很容易冲突。测试环境统一用8000以上的高位端口,生产再按需调整。
7.3 Docker Desktop报virtualization support not detected
Windows上使用Docker Desktop时,偶尔会碰到启动失败,提示“Docker Desktop failed to start because virtualisation support wasn't detected”之类的错误。
排查思路:Docker Desktop在Windows上依赖虚拟化技术,报错通常意味着系统的虚拟化功能没有正常开启。先打开任务管理器,在“性能”页签里看“虚拟化”这一行是否显示“已启用”。如果显示禁用,需要进BIOS/UEFI把Intel VT-x或AMD-V打开。笔记本用户还要检查是不是开了某些虚拟机监控程序独占虚拟化资源。开启后重启电脑,再启动Docker Desktop一般能恢复正常。另外Windows自带的Hyper-V和Windows虚拟机监控程序平台也要保证处于启用状态。如果你不想用Hyper-V架构,新版Docker Desktop的WSL2后端也可以,但同样依赖虚拟化支持。
7.4 镜像拉取极慢或超时
这个问题在国内环境尤其常见。直接拉Docker Hub官方镜像时经常慢到让人抓狂,很多人第一反应是配置一个镜像加速器。加速器的地址一般在云服务商的控制台就能找到,把你自己的专属地址配置到Docker配置里即可。
Linux服务器上改/etc/docker/daemon.json:
{ "registry-mirrors": ["https://your-mirror-server.example.com"] }配置完成后执行:
sudo systemctl daemon-reload sudo systemctl restart docker注意,改镜像加速只影响后续拉取操作,已经存在的镜像不会重新下载。Docker Desktop用户可以在Settings -> Docker Engine里直接编辑JSON配置。
还有一点容易忽略:确保服务器时间和镜像仓库服务器时间一致。如果时间偏差太大,HTTPS证书校验会失败,拉取时报证书相关错误,看起来像网络问题,其实是时间问题。date -R看一眼,偏差大就及时同步。
7.5 容器内命令找不到
进入容器执行ps、vim、netstat等命令时报command not found,这不是你的Docker坏了,而是容器镜像本身精简掉了这些工具。
处理方式:先用镜像自带的工具代替。比如查看监听端口用netstat没有,可以试ss;查看进程用ps没有,可以到/proc目录手工查看,或者用docker top从宿主机侧看。真需要调试工具,临时装一套,但注意容器重启后工具会丢失,因为容器可写层不会持久化。生产环境建议在Dockerfile里把调试工具提前打好,或者准备一个专用的debug镜像。
7.6 容器内文件修改后不生效
修改了容器内文件但访问没有变化,先别急着怀疑缓存,按照这个顺序排查:
第一,确认改动保存位置是否和实际生效路径一致。很多镜像会有一层软链接,比如某些发行版镜像的/etc/localtime是指向/usr/share/zoneinfo/...的软链,直接覆盖会得到“Text file busy”或明明写了却没变化的结果。
第二,确认改动有没有被子进程重新拉取。Nginx这类进程会缓存配置,改完文件后需要reload:
docker exec my-nginx nginx -s reload第三,确认是不是有多个实例。你改了A容器,请求却负载到了B容器,这在docker compose和swarm架构下经常发生。搜一下到底有哪些容器在跑这台服务。
7.7 如何面对那些“找不到命令帮助”的时刻
当你记不清某个命令的参数时,最靠谱的办法不是上搜索引擎,而是先看本机帮助:
docker run --help docker ps --help docker network --helpDocker命令体系挺清晰的,每个子命令都能用--help查看详细选项。操作系统上的命令也同理,man docker-run或者直接看官方文档。很多参数光靠死记硬背容易漏,用到什么查什么,慢慢就形成肌肉记忆了。
我在实际使用中还有一个习惯,把常用命令的“冷门却实用”参数写在shell的alias或者notes里,比如给docker ps加一个docker ps --format的别名,界面清爽很多。工具存在的意义是减轻人的负担,不是增加背诵压力。
写在最后
整理这篇Docker常用命令时,我回顾的不只是每个命令的用法,还有那些让新手怀疑人生的排障经历。Docker本身并不复杂,难的是在你已经有一堆业务系统在跑时,还能冷静地判断容器状态、排除网络问题、管理数据持久化。我的建议依然是在测试环境多造几个“事故场景”,比如故意把端口占用、故意退出容器、故意删掉数据卷,然后自己一步步查日志、看状态、恢复。摔过几次之后,这些命令才会真正内化成你的本能反应。最后,希望这篇梳理能帮你节省一点满地翻文档的时间。