1. 先搞明白Docker命令的核心逻辑
很多朋友一上来就背命令,docker run、docker ps背得滚瓜烂熟,遇到实际问题照样抓瞎。原因很简单——你没有理解Docker命令的设计思路。
Docker这套命令体系,本质上是围绕五个核心对象展开的:镜像、容器、数据卷、网络、编排。你日常所有操作,无非就是对这些对象进行增删改查。一旦理解了这套逻辑,你会发现命令根本不用背,顺着思路推理就能推出来。
我拿生活里的例子类比一下。镜像就是一个"安装包",它只读、不可变、里面打包好了运行环境;容器就是你双击安装包之后跑起来的那个"应用程序实例",一个镜像可以同时跑出多个互不干扰的容器。容器可以被启动、停止、删除,而你删掉容器并不会影响镜像本身——就像卸载软件不会删掉安装包一样。
还有一个关键概念叫数据卷。容器默认是"用完即弃"的,你删掉容器,容器里产生的数据也跟着没了。数据卷就是把你宿主机上的一个目录"映射"进容器里,让数据持久化存在宿主机上。这个设计非常巧妙:容器保持轻量和可重建性,数据保持独立和可迁移性。
网络这块更简单。Docker默认会创建几个网络,比如bridge(桥接网络)就是最常见的模式。你启动容器时可以指定-p 8080:80这种参数,意思就是把宿主机的8080端口"转发"到容器的80端口上,外面访问宿主机8080端口就能到达容器内的服务。
最后是编排工具。当你的应用需要好几个容器配合(比如前端、后端、数据库),挨个docker run会非常痛苦。这时候用docker compose,写一个YAML文件,把所有容器的配置都声明好,一条命令搞定整套环境的启动、停止、重建。
明白了这个框架,接下来我按照对象维度逐个拆解命令,每一组命令我都会讲清楚适用场景、常用参数和容易踩的坑。
2. 镜像操作命令:拉取、查看与删除
2.1 镜像拉取与查看
先看最常用的镜像操作,基本围绕pull、images、rmi三个命令展开。
# 从仓库拉取镜像 docker pull nginx docker pull nginx:1.25 # 列出本地已有的镜像 docker images # 删除镜像 docker rmi nginxdocker pull如果不加标签,默认拉取latest(最新版)。我强烈建议实际项目里给镜像打上明确的版本号,比如nginx:1.25而不是nginx:latest。原因很简单:latest指向的镜像是会变的,可能你昨天拉的和今天拉的不是同一个版本。哪天服务器上莫名其妙出现一个诡异问题,排查半天发现是镜像版本悄悄变了,这种教训我经历过不止一次。
看过我上篇文章的朋友可能还有印象,我把一台生产服务器上MySQL的镜像从mysql:latest更新后,配置文件的默认行为发生了变化,导致整个应用起不来。所以这里再强调一遍:能用具体标签就别用latest。
docker images输出里有个SIZE列,如果你发现某个镜像体积异常大,可以用docker history <镜像名>看看每一层做了什么操作,定位是哪一层撑大了体积。
2.2 镜像标签与导入导出
有时候你需要给镜像打标签,或者在没有网络的环境下传输镜像,这时候需要用到tag、save、load。
# 给镜像重新打标签 docker tag nginx:1.25 myregistry.example.com/nginx:1.25 # 把镜像保存成tar文件 docker save -o nginx.tar nginx:1.25 # 从tar文件加载镜像 docker load -i nginx.tardocker save和docker load真的是救命工具。有一次我在机房部署服务,内网环境完全拉不了外部镜像,我就是在自己电脑上把需要的镜像save成tar包,然后用U盘拷进去,再load进内网服务器。整个过程干净利落。
命令输出的信息也要学会看。比如docker pull的时候,你会看到类似Pull complete、Digest: sha256:xxx这样的输出。那个Digest是镜像内容的哈希值,相当于镜像的"身份证号"。你要是怀疑镜像被篡改过,对比一下pull出来的Digest和官方Markdown文档里公布的Digest就知道真伪了。
2.3 镜像加速配置与国内拉取技巧
国内环境拉取Docker Hub镜像慢甚至超时,是一个绕不开的痛点。这里说的不是绕过限制,而是配置镜像加速源。原理很简单:镜像是通过HTTPS从Registry拉取的,你配置一个国内可访问的加速地址,Docker就会优先从那儿下载。各大云厂商都提供过加速服务,但现在很多已经调整策略了,这个你得根据自己的实际情况去搜索当前可用的加速源。
配置位置在Docker的守护进程配置文件里。Linux上通常是/etc/docker/daemon.json:
{ "registry-mirrors": ["https://你的加速地址"] }配置完重启Docker服务生效。macOS和Windows的Docker Desktop则在设置界面里直接填加速地址就行。
需要提醒的是,优先从可信渠道获取加速源地址,不要随便用网上流传的来路不明的地址,防人之心不可无。
2.4 镜像清理与体积诊断
镜像占空间的坑,我专门拿出来说一说。用docker images看到的是每个镜像单独的体积,但Docker镜像是分层存储的,多个镜像可能共享底层的只读层,所以实际占用的磁盘空间可能比docker images显示的总数要少。
要看真实占用,用这个命令:
docker system df它会显示镜像、容器、数据卷、构建缓存各自的占用空间。你会发现构建缓存占的空间往往超乎想象,尤其是频繁构建镜像的时候。
清理镜像用docker rmi,但如果镜像被某个容器引用了,直接删会报错。这时候要么先删掉容器,要么用docker rmi -f强制删除。我一般不用-f,因为强制删除可能导致一些依赖关系悬空,还是先把引用它的容器处理干净更稳妥。
删除所有不再使用的镜像可以用:
docker image prune -a这个命令会把所有没有被容器引用的镜像全删了。删之前确认一下自己是不是真的要清理这么多东西。
3. 容器生命周期管理命令:从run到rm
3.1 docker run:启动容器的完整参数解析
docker run是用的最多的命令,没有之一。它的参数组合非常多,我会按实际使用频率逐个拆解。
docker run -d \ --name my-nginx \ -p 8080:80 \ -e ENV_NAME=production \ -v /host/data:/container/data \ --restart unless-stopped \ nginx:1.25每个参数的含义:
-d:后台运行容器,不占用当前终端。不加这个参数,容器会以前台模式跑,日志直接打到终端上,Ctrl+C之后容器就停了。日常调试时可以不加-d,这样能直接看到容器的输出,排错方便。--name:给容器起名字。没名字的话Docker会随机分配一个,比如angry_heisenberg,后续管理起来非常痛苦。-p:端口映射,格式是宿主机端口:容器端口。一个容器可以加多个-p参数。这里有个高频问题:如果启动时报port is already allocated,说明这个宿主机的端口已经被别的程序占了,用netstat -tunlp | grep 8080可以查出来是谁占用的。-e:传入环境变量。很多镜像的行为是靠环境变量控制的,比如MySQL的MYSQL_ROOT_PASSWORD、Redis的REDIS_PASSWORD。-v:数据卷挂载,把宿主机的目录或文件挂载到容器里。--restart:重启策略,常用的是unless-stopped,意思是"只要不是手动stop的,容器挂了都会自动重启"。服务器重启之后这个容器也会自动拉起,生产环境强烈建议加上。
我用一个实际案例来说明。假设我要部署一个Nginx,同时要改它的配置:
docker run -d \ --name web-nginx \ -p 80:80 \ -v /home/user/myweb:/usr/share/nginx/html:ro \ -v /home/user/nginx.conf:/etc/nginx/nginx.conf:ro \ --restart unless-stopped \ nginx:1.25第一个-v把网站目录挂进去,第二个-v把自定义的Nginx配置文件挂进去。这里:ro表示只读挂载,容器里改不了宿主机的文件,这是一个安全习惯。这样部署之后,我改了宿主机上的网页代码,刷新页面就生效了,根本不用进容器里操作文件。
3.2 容器查看、启停与删除
容器已经跑起来了,后续的管理命令围绕这几个:
# 查看正在运行的容器 docker ps # 查看所有容器(包括已停止的) docker ps -a # 停止容器 docker stop my-nginx # 启动已停止的容器 docker start my-nginx # 重启容器 docker restart my-nginx # 删除容器(必须先停止) docker rm my-nginx # 强制删除正在运行的容器 docker rm -f my-nginxdocker ps的输出有几个关键信息要会看:STATUS列显示容器的状态,Up 2 hours表示正常,Exited (0)表示正常退出,Exited (1)之类的非零码就是异常退出;PORTS列显示端口映射情况,0.0.0.0:8080->80/tcp表示宿主机的8080端口映射到了容器的80端口。
我这儿有个统计:我参与过的项目中,大概有70%的容器故障第一眼是从docker ps -a的STATUS列发现异常迹象的,所以docker ps -a真的是排查第一利器,尤其要看那些状态异常的容器。
3.3 进入容器内部的两个方法
容器跑起来之后,有时候你需要在容器里执行命令、查看文件、调试问题。进入容器有两种方式,网上很多人混用,我讲清楚区别:
# 方式一:在容器内执行一条命令 docker exec -it my-nginx bash # 方式二:查看容器主进程的输出日志 docker logs my-nginx注意了,docker exec是"在运行中的容器里执行命令",-it这两个参数要连在一起用,-i是交互模式,-t是分配一个终端。不加-it你执行bash命令是进不了交互shell的,很多人第一次在这里卡住。
执行docker exec -it my-nginx bash之后,你会进入容器的shell,看到类似root@容器ID:/#这样的提示符。这时候你就在容器内部了,可以执行ls、cat、ps等命令,不过容器的文件系统通常是精简过的,很多命令可能没有,比如没有vim、没有ping。这也提醒你:有事没事别老进容器里改东西,容器是尽量保持不可变才最安全。
那怎么查看容器日志呢?用docker logs。这个命令后面再详解,这里先记住一行:docker logs --tail 100 -f my-nginx,看最后100行日志并且持续跟踪输出。
3.4 容器与宿主机之间的文件拷贝
有时你需要把宿主机上的文件拷进容器,或者把容器里的文件拿回来。用docker cp:
# 宿主机文件拷入容器 docker cp ./my.conf my-nginx:/etc/nginx/conf.d/ # 容器文件拷到宿主机 docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf.bak说句实话,docker cp只是临时救急用的,不是常规操作手段。真正规范的做法是:宿主机上用-v挂载目录,配置文件放宿主机上,直接改宿主机文件、重启容器或reload即可。这样容器可以随时重建而不丢配置。
4. 容器日志、监控与排障实战
4.1 docker logs的正确使用方式
日志排障是Docker使用中最常见的场景,绕不开也躲不掉。先看基础用法:
# 查看全部日志 docker logs my-nginx # 查看最近100条日志 docker logs --tail 100 my-nginx # 持续跟踪日志输出(类似tail -f) docker logs -f my-nginx # 查看带时间戳的日志 docker logs -t my-nginx # 只看最近5分钟的日志 docker logs --since 5m my-nginxdocker logs默认输出的是容器的标准输出(stdout)和标准错误(stderr)。这意味着:你在容器里跑的应用程序,只要它往控制台打日志,都能被docker logs捕获到。反过来,如果应用日志只写到文件里不往控制台打,docker logs就看不到。这种情况就得进容器看日志文件,或者更推荐的做法:启动应用时把日志同时输出到控制台。
--since参数配合时间排查很好用。比如线上的服务凌晨3点出问题了,你可以:
docker logs --since 2025-01-15T03:00:00 my-app这样能精确拿到那个时间点之后的日志,不用在大堆日志里捞。排查完别忘了加--until截断范围:
docker logs --since 2025-01-15T03:00:00 --until 2025-01-15T03:30:00 my-app这比直接打开日志文件Ctrl+F高效得多。
还有一个实际经验:容器运行时间长了,日志文件会变得非常大。Docker默认的日志驱动是json-file,日志文件默认存放在/var/lib/docker/containers/<容器ID>/目录下。如果不限制日志大小,硬盘会被撑爆。建议在daemon.json里限制日志大小和数量:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }这样每个容器的日志上限就是30MB,轮转3个文件,写满了自动清理老的。我接手过的一个项目因为没配这个,三个月后硬盘爆满,整个服务器都卡死了,这个配置建议每个使用Docker的人都加上。
4.2 查看容器进程与资源占用
排查性能问题时会用到这两个命令:
# 查看容器里正在运行的进程 docker top my-nginx # 查看所有容器的CPU、内存、网络、磁盘IO docker statsdocker top相当于在宿主机上执行ps,但展示的是容器内的进程。docker stats非常直观,能看到每个容器的实时资源占用。如果某个容器CPU飙到100%,大概率是应用出问题了,进去看看是不是死循环或者GC线程在疯狂工作。
注意两个点:
docker stats显示的CPU%是相对宿主机所有CPU核数的百分比,比如你的服务器是4核,一个容器显示CPU%是200%,说明它占用了2个核心的算力。- 如果是排查历史资源占用情况,
docker stats不够用,得靠容器监控平台(如Prometheus + cAdvisor)采集历史数据。Docker命令行本身不提供历史回溯。
4.3 容器状态异常排查思路
我整理一下自己平时排查容器异常的一套步骤:
- 先看
docker ps -a,确认容器状态是Exited还是Restarting。 docker logs --tail 50 <容器名>,看退出前最后打的日志。docker inspect <容器名>,看容器配置和状态信息(后面会讲)。- 如果日志里没线索,把容器删掉用前台模式重新跑一次,比如去掉
-d,直接让日志打到终端上。 - 如果依然查不出来,检查宿主机资源:
free -h看内存,df -h看磁盘。
第三步的docker inspect是排查利器。它的输出是JSON格式,信息量很大,我平时主要用它查这几个字段:
# 查看容器重启次数 docker inspect -f '{{.RestartCount}}' my-nginx # 查看容器主进程PID docker inspect -f '{{.State.Pid}}' my-nginx # 查看容器IP地址 docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-nginx # 查看容器的挂载情况 docker inspect -f '{{json .Mounts}}' my-nginx-f是Go模板语法,用来提取JSON里的某个字段。这个技巧特别好用,在脚本里做自动化判断时非常管用。
5. 数据卷与网络配置:容器内外沟通的桥梁
5.1 数据卷的创建、挂载与备份
前面提到数据卷是持久化数据的关键,详细展开讲讲。
Docker里数据有几种存法:
- 匿名卷:启动容器时不指定宿主机目录,只写容器内路径,比如
-v /var/lib/mysql。Docker会随机创建一个目录来对应这个挂载点。 - 命名卷:用
docker volume create明确创建的卷,比如-v mysql-data:/var/lib/mysql。 - 绑定挂载:直接指定宿主机路径,比如
-v /home/user/data:/var/lib/mysql。
我强烈建议在正式环境使用命名卷或绑定挂载,尽量别用匿名卷。匿名卷的目录是随机生成的,没人知道它在哪儿,容器删了之后数据找起来非常麻烦。
命名卷的好处是可以用docker volume命令管理:
# 创建数据卷 docker volume create mysql-data # 查看所有数据卷 docker volume ls # 查看数据卷详情 docker volume inspect mysql-data # 删除数据卷 docker volume rm mysql-data举个例子,我用MySQL容器,数据目录一定要用命名卷:
docker run -d \ --name mysql8 \ -e MYSQL_ROOT_PASSWORD=mysecret \ -v mysql-data:/var/lib/mysql \ -p 3306:3306 \ mysql:8.0这样无论容器怎么删怎么重建,数据都在mysql-data这个卷里,新的容器挂载同一个卷,数据就回来了。
数据卷的迁移备份:
# 备份命名卷数据 docker run --rm -v mysql-data:/source -v /backup:/target ubuntu tar czf /target/mysql-backup.tar.gz -C /source . # 恢复命名卷数据 docker run --rm -v mysql-data:/target -v /backup:/source ubuntu tar xzf /source/mysql-backup.tar.gz -C /target这两条命令看起来有点绕,其实思路是借用ubuntu镜像临时起一个容器,同时挂载数据卷和备份目,然后在容器里执行tar打包或解包。--rm表示容器用完了就自动删除,非常干净。
5.2 容器网络的常用操作
Docker网络这块,日常会用到的场景大概是这几个:端口映射、自定义网络、容器间通信。
先看端口映射排查。我们经常遇到"明明-p 8080:80了,为什么外部还是访问不了"这类问题。排查思路如下:
# 1. 确认端口映射是否生效 docker port my-nginx # 2. 确认容器内服务是否正常监听80端口 docker exec my-nginx curl http://127.0.0.1:80 # 3. 确认宿主机防火墙是否放行8080端口 firewall-cmd --list-portsdocker port会显示容器端口的映射关系,比如80/tcp -> 0.0.0.0:8080。如果显示为空,说明容器启动时没加-p参数,这时候要么重建容器,要么直接改宿主机防火墙做转发,但改防火墙不如重建容器干净。
再说自定义网络。默认的bridge网络下,容器之间靠IP地址通信,但容器重建后IP会变,这就很麻烦。解决办法是创建自定义网络,在自定义网络里,Docker内置DNS解析,容器之间可以直接用容器名当域名互相访问:
# 创建自定义网络 docker network create my-net # 启动两个容器并加入同一网络 docker run -d --name app --network my-net myapp:latest docker run -d --name mysql8 --network my-net -e MYSQL_ROOT_PASSWORD=secret mysql:8.0 # 在app容器里通过容器名访问mysql docker exec app ping mysql8这样app容器里配置数据库地址时,直接写mysql8:3306就行了,不管MySQL容器怎么重建,只要容器名不变、网络不变,这个地址就永远有效。这个方法在微服务架构里几乎是标配。
还有些不常用的网络命令,了解即可:
# 查看所有网络 docker network ls # 查看网络详情 docker network inspect my-net # 把已启动的容器加入网络 docker network connect my-net my-nginx # 把容器从网络断开 docker network disconnect my-net my-nginx5.3 跨主机容器通信的提示
跨主机场景下,容器通信就不能靠Docker默认网络了,通常需要借助overlay网络(配合Swarm或Kubernetes使用)或者直接发布端口。这套东西内容很大,只是提个醒:如果你有多台服务器的容器需要互相访问,别试图把两个Docker主机的容器放进同一个默认网络,这不可行。要么用Swarm/K8s做集群,要么就通过宿主机的端口映射来互相访问,后者运维成本低很多。
6. 容器编排入门:用Docker Compose管理复杂应用
6.1 为什么需要Compose
当你需要部署的应用变复杂之后,比如一个Web项目包含前端、后端、数据库、Redis、消息队列五个组件,挨个docker run大概得敲几十条指令,还要记住每个容器的参数、网络、依赖关系。人记这些东西是记不牢的,忘掉一个参数就环境起不来。
Docker Compose就是为了解决这个问题。它用一份YAML文件把所有容器的配置声明好,一条docker compose up -d就能把整套环境拉起来。而且这份YAML文件是可以提交到Git仓库里做版本管理的,环境配置变成代码,团队协作太方便了。
6.2 compose文件怎么写
以最常见的"MySQL + Redis"组合为例。很多应用依赖MySQL和Redis,通常的做法就是在compose文件里把这两个服务定义好。
version: "3.8" services: mysql8: image: mysql:8.0 container_name: my-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: myapp MYSQL_USER: myapp MYSQL_PASSWORD: myapp123 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql networks: - app-net redis: image: redis:7.0 container_name: my-redis restart: unless-stopped ports: - "6379:6379" volumes: - redis-data:/data command: redis-server --appendonly yes networks: - app-net volumes: mysql-data: redis-data: networks: app-net: driver: bridge这份文件做的事情,和前面一堆docker run命令是一样的。services定义了两个容器,volumes声明了两个命名卷,networks创建了一个自定义网络,两个容器在这个网络里可以互相用容器名访问。
command字段可以覆盖镜像默认的启动命令。上面Redis例子里的--appendonly yes是开启AOF持久化,保证Redis重启数据不丢。
6.3 compose常用命令
写好了YAML文件,后续操作非常顺畅:
# 启动全部服务(-d 后台运行) docker compose up -d # 查看服务状态 docker compose ps # 查看所有服务的日志 docker compose logs -f # 查看单个服务的日志 docker compose logs -f mysql8 # 停止全部服务(容器不删除) docker compose stop # 停止并删除容器、网络 docker compose down # 停止并删除容器、网络、数据卷 docker compose down -v关键提醒:
docker compose down不会删除数据卷,所以数据是安全的。但docker compose down -v会连数据卷一起删,用之前必须确认里面的数据确实不要了。我见过有人跑了这条命令,MySQL数据全没了,那个表情我一辈子忘不了。- 修改了compose文件之后,重新执行
docker compose up -d,Compose会智能对比配置差异,只重建有变更的容器,没有变更的容器不会动。 - 新版本Docker已经内置了
docker compose子命令(带空格),旧版本的docker-compose(带横线)需要单独安装。两者语法基本一致,但优先用新版内置的。
6.4 实际项目中的Compose使用建议
我平时部署微服务项目,compose文件会按环境拆分成多份:docker-compose.yml(公共配置)、docker-compose.dev.yml(开发环境覆盖)、docker-compose.prod.yml(生产环境覆盖)。启动时指定文件:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d这样公共部分只写一遍,不同环境的差异用覆盖文件解决,比维护三份完整文件省心很多。
还有一点,compose文件里的container_name尽量写成有辨识度的名字。docker ps的时候如果看到一堆自动生成的名字,你根本分不清哪个是哪个。但注意container_name在同一个宿主机上全局唯一,不能有两个容器重名。
7. 构建镜像与清理:打造极简Docker镜像
7.1 docker build与Dockerfile基础
虽然标题是"常用docker命令",但docker build必须提,因为它是把应用变成镜像的核心门槛。我用一个最简单的Node.js应用举例:
先写一个Dockerfile:
FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install --production COPY . . EXPOSE 3000 CMD ["node", "app.js"]然后在项目根目录执行:
docker build -t myapp:1.0.0 .-t指定镜像名和标签,最后的.是构建上下文路径,也就是Dockerfile所在的目录。构建过程会逐行执行Dockerfile里的指令,每一条指令生成一个镜像层。
构建过程中有个参数很常用:
docker build -t myapp:1.0.0 --no-cache .--no-cache会忽略之前构建的缓存,从头构建。有时候改了package.json但镜像没生效,大概率是缓存问题,用这个参数强制重建就好了。
7.2 镜像瘦身的常用技巧
镜像瘦身这件事,直接影响部署速度和占用的存储空间。我分享几个实际收益很大的策略:
基础镜像尽量选alpine版本。同样一个Nginx,nginx:latest体积大概190MB,nginx:alpine只有60多MB。三倍差距,而且alpine的包管理器是apk,换源也方便。
用多阶段构建。前端或者编译型语言的项目,构建环境和运行环境分开。第一阶段装全量依赖、编译代码,第二阶段只拷贝编译产物和必要依赖。
# 第一阶段:构建 FROM golang:1.20 AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 go build -o myapp # 第二阶段:运行 FROM alpine:latest WORKDIR /root/ COPY --from=builder /app/myapp . EXPOSE 8080 CMD ["./myapp"]这样最终镜像只包含编译好的二进制和最小的运行环境,体积能缩小好几个数量级。
清理apk/apt缓存。安装依赖之后顺手清掉包管理器缓存,镜像能小几十MB。
RUN apt-get update && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/*用.dockerignore排除无关文件。项目里的node_modules、.git、tests目录如果没排除,它们会被发送到构建上下文里,不仅构建慢,还可能被误打包进镜像。.dockerignore的写法和.gitignore基本一样。
7.3 构建缓存的重要说明
Docker构建是有缓存机制的:如果Dockerfile的某条指令和之前完全一样,并且涉及的上下文文件没变,Docker就直接复用之前的缓存层,构建速度会快很多。
但缓存有时候会坑人。比如你执行了RUN git clone xxx,如果远端仓库有更新,Docker不会自动告诉你,它认为这条指令没变就用缓存了。解决办法就是加--no-cache,或者把依赖版本固定到具体commit上,避免"代码变了但镜像没变"的灵异事件。
8. 日常维护:清理、监控与一键接入容器
8.1 一键清理Docker环境
时间长了,宿主机上会堆积大量停止的容器、无用的镜像、悬空的数据卷、构建缓存。手动一个个删太痛苦,Docker提供了批量清理的命令:
# 清理所有已停止的容器 docker container prune # 清理所有未被使用的镜像 docker image prune -a # 清理所有未被使用(没被容器引用)的数据卷 docker volume prune # 清理构建缓存 docker builder prune # 一键清理所有未使用的资源 docker system prune -a --volumesdocker system prune -a --volumes是"核弹"级别的清理命令,它会删掉所有没被运行中容器使用的镜像、所有停止的容器、所有数据卷。执行之前一定要仔细确认,尤其注意--volumes会把数据卷一起删掉。建议平时只跑不带--volumes的docker system prune -a,数据卷单独审着删。
如果空间还是不够,还需要检查一下docker的根目录路径:日志和容器存储都在/var/lib/docker下,如果系统盘分区小、数据盘大,可以考虑把docker根目录迁移到数据盘,或者确认是否有过多大日志文件堆积。
8.2 宿主机直接操作容器:docker run --rm临时容器
另一个很实用的技巧是用--rm参数跑一次性容器。比如你想在不污染环境的情况下测试某个工具:
# 临时起一个带curl的容器测试接口,用完自动删除 docker run --rm curlimages/curl curl -I http://example.com # 临时用mysql客户端连接数据库 docker run --rm -it mysql:8.0 mysql -h172.17.0.2 -uroot -p这样不会在你宿主机上装任何多余的东西,容器跑完命令就自动删了,非常适合做一次性环境验证。
8.3 Windows环境安装Docker Desktop的常见问题与排查
最后专门说说Windows上安装和使用Docker Desktop的常见问题,因为社区里问的人太多了。
问题一:Docker Desktop启动失败,提示虚拟化未检测到(virtualization support not detected)
这个提示翻译成人话就是:你的电脑没有开启硬件虚拟化功能。排查路径如下:
- 打开任务管理器,点击"性能"选项卡,看右下角"虚拟化"是否显示"已启用"。如果显示"已禁用",就需要进BIOS开启Intel VT-x或AMD-V。
- 确认Windows的"Hyper-V"和"适用于Linux的Windows子系统"(WSL)功能有没有开启。以管理员身份运行PowerShell:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart开启之后重启电脑,再装WSL2:
wsl --install装完把默认版本设置为2:
wsl --set-default-version 2问题二:Docker Desktop提示Windows版本不兼容(incompatible version of windows)
Docker Desktop对Windows版本有要求,一般是Windows 10 64位专业版/企业版/教育版,或者Windows 11。如果你是Windows家庭版,大概率会碰到问题,因为Hyper-V这个特性在家庭版上默认不带。解决办法是装Docker Desktop的时候选择WSL 2后端(现在默认就是),并且把WSL2配置好。如果你用的系统版本确实过旧,那就要么升级系统,要么换用Docker Toolbox这类老方案——但我建议能升级就升级,别折腾老方案了。
问题三:Docker Desktop启动后一直卡在"Starting"状态
这种一般跟WSL2有关。进PowerShell执行wsl --shutdown强制关闭WSL,再重新打开Docker Desktop。如果还不行,检查BIOS里虚拟化是否被安全软件误关了。
问题四:Docker Desktop里的容器和宿主机怎么互通
Windows上,容器里的服务通过localhost访问宿主机的服务通常是不行的,反过来宿主机访问容器映射端口一般可以直接用localhost。要在容器里访问宿主机的服务,需要用host.docker.internal这个特殊域名,Docker Desktop会自动把它解析到宿主机IP。
9. 把这些命令串起来:一个可落地的部署示例
光列命令不给一个完整的实战串联,总觉得少了最后一环。我拿一个真实的部署场景——Nginx + MySQL 8.0 + Redis,用Compose整体拉起来——把前文讲过的知识点串一遍,你可以照着这个模板改造自己的项目。
假设项目目录结构:
myproject/ ├── docker-compose.yml ├── myweb/ │ └── index.html └── nginx/ └── default.confnginx/default.conf,一个反向代理配置,把请求转发到后端的容器服务:
server { listen 80; server_name _; location / { proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }docker-compose.yml:
version: "3.8" services: mysql8: image: mysql:8.0 container_name: my-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: mydb ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql networks: - my-net healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7.0-alpine container_name: my-redis restart: unless-stopped command: redis-server --appendonly yes ports: - "6379:6379" volumes: - redis-data:/data networks: - my-net nginx: image: nginx:1.25-alpine container_name: my-nginx restart: unless-stopped ports: - "8080:80" volumes: - ./myweb:/usr/share/nginx/html:ro - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro depends_on: mysql8: condition: service_healthy redis: condition: service_started networks: - my-net volumes: mysql-data: redis-data: networks: my-net: driver: bridge这份compose文件里我加了一个healthcheck配置,让MySQL容器启动后主动做健康检查,Nginx等MySQL确认可连接之后才启动,避免"依赖服务还没就绪导致启动失败"的经典问题。
启动整套环境:
docker compose up -d查看状态:
docker compose ps打开浏览器访问http://localhost:8080,能看到你自己的网页内容。访问http://localhost:8080时如果Nginx配置了代理,也就能走到后端服务了。
整个过程中你实际用到的命令:
docker compose up -d docker compose ps docker compose logs -f docker compose down就这么几条,比手动跑三个docker run再去配置网络,逻辑上清晰太多了。这也是Compose值得投入时间学习的原因——它能让你把精力从"记命令参数"中解放出来,放到真正需要思考的"服务如何编排"上。
我个人的体会是,Docker命令的学习曲线其实很缓,只要抓住"镜像-容器-卷-网络-编排"这五个关键字,日常90%的操作都逃不出这个框架。剩下那10%的冷门命令,等你真碰到那个场景的时候,翻文档自然就学会了。不用一开始就试图背完所有命令,把最常用的这几组练熟,遇到问题时知道往哪个方向排查,你就算是真正入门了。