ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker常用命令实战指南:从镜像管理到容器排障

Docker常用命令实战指南:从镜像管理到容器排障 从第一次敲docker run到能独立排查一套生产环境我大概花了两年多时间。这些年我观察下来新手学 Docker 最容易犯的错不是不努力而是捧着命令清单从头背到尾真正上手时还是一脸懵。Docker 常用命令真没那么多关键在于你得清楚每条命令对应哪个典型场景、背后机制是什么遇到报错知道往哪个方向查。这篇文章我就完全按真实工作流来写拉镜像、跑容器、看日志、理网络、管数据、编排服务、排障。你顺着读一遍再照着敲一遍基本就够日常开发用了。1. 先弄明白 Docker 命令的底层逻辑1.1 Docker 真正在操作的只有三类对象Docker 的命令看似几十条但抽象一下绝大部分命令都在做三件事操作镜像、操作容器、操作数据。这三者关系很像程序安装包、运行中的进程、程序产生的配置文件之间的关系。镜像Image只读的模板包含代码、运行时、系统库、环境变量相当于一个打包好的环境快照。容器Container镜像运行时的实例每次启动是基于镜像加一层可写层你对容器的修改不会污染镜像本身。数据卷Volume/网络Network容器本身是无状态的删掉就没了需要持久化的数据放 Volume多个容器要通信就得靠 Network。理解了这三层再看命令就顺了docker image开头的操作镜像裸的docker run/start/stop是容器生命周期docker volume/network负责配套资源docker system管全局状态。官方文档把所有命令按对象分组也是这个思路。有个常见的理解误区要澄清docker commit能把一个改过的容器保存成新镜像很多新手喜欢这么干。但生产环境几乎不用它因为 commit 出来的镜像没有构建历史体积臃肿也无法复现。正确姿势永远是改 Dockerfile 重新 build这点后面专门说。1.2 命令的组合规律动词 对象 参数如果你观察得够仔细Docker 命令的语法规律性很强大部分是动词 对象类型 具体操作后面再接参数。比如docker image ls # 列出镜像ls 是 list 的缩写 docker image pull # 拉取镜像 docker container ls # 列出容器 docker container rm # 删除容器 docker volume ls # 列出数据卷 docker network ls # 列出网络而且 Docker 提供了一个偷懒的规则如果你写docker ls这种没带对象的命令它会默认按容器处理因为容器是日常操作最频繁的东西。所以docker ps其实等同于docker container lsdocker rm等同于docker container rm。这个默认行为让很多老手直接写短命令。我的建议是学习阶段习惯写docker container ls这种全称能强迫自己想清楚操作对象是什么日常使用图效率再用docker ps这种简写。两条路径互相印证记忆会深刻很多。2. 镜像命令先学会把安装包管理好2.1 拉取镜像的正确姿势pulll 不是只有一个 -d镜像的获取是万里长征第一步。最常见的命令是docker pull nginx:1.25 docker pull mysql:8.0这里有几个细节值得注意。第一版本号一定要带。写docker pull nginx默认拉 latest 标签这个标签不代表最新稳定版只是最新推送的版本很可能是个 beta 或者你不想要的大版本。生产环境脚本里出现无 tag 的镜像就是给自己埋雷。第二tag 的规范是仓库名:标签标签还可以细分比如nginx:1.25-alpine表示基于 Alpine Linux 的精简版镜像体积小很多适合追求构建速度和部署体积的场景。拉取镜像时你会看到一层一层往下 pull这个过程能看到类似Pull complete的输出。每一层其实就是一个文件系统变更的增量包多个镜像之间可以共享相同的层。这就是为什么你同时跑了 nginx 和另一个基于相同基础镜像的容器磁盘占用不会成倍增加的原因。理解了层的存在后面也就能理解为什么精简基础镜像、合并 RUN 指令能显著减小体积。查看本地镜像docker images docker image ls -a-a会显示所有中间层镜像dangling 镜像也算如果你发现磁盘空间莫名其妙没了docker image ls -a能看到很多none的悬空镜像它们通常是反复 build 产生的废弃中间产物可以放心清理。2.2 镜像重命名与推送tag 和 push把本地镜像推到自己的私有仓库或者给镜像换个名字方便管理要用docker tagdocker tag nginx:1.25 registry.example.com/ops/nginx:v1.25 docker push registry.example.com/ops/nginx:v1.25tag 执行的本质不是复制一份镜像而是给同一个镜像多打了一个引用标签所以瞬间完成也不额外占空间。仓库地址的前缀决定了 push 的目标不写前缀默认推 Docker Hub。2.3 删除镜像与磁盘清理docker rmi nginx:1.25 # 删除指定镜像 docker image prune # 清理悬空镜像 docker image prune -a # 清理所有未被容器使用的镜像rmi删的是引用只有当镜像只剩一个引用时才会真正释放层数据。如果镜像正在被容器使用会出现image is being used by running container的报错强制删除要加-f但我不推荐不如先停容器再删。prune系列是安全的清理手段-a会把所有没有关联运行容器的镜像都删掉用之前先docker images看清楚有哪些免得删掉之后要重新拉。2.4 离线迁移save 与 load内网服务器不能直接访问外网时最常用的方案就是在能联网的机器上把镜像导出再拷贝进去导入docker save nginx:1.25 | gzip nginx-1.25.tar.gz docker load nginx-1.25.tar.gz注意这里用管道接 gzip 压缩能把几百 MB 的镜像压掉不少。docker save默认输出 tar 流docker load会解析其中的镜像层并加载。这个操作在离线环境部署时几乎是必用组合比如给客户的隔离机房装一套服务没有这招根本玩不转。3. 容器生命周期命令每天用最多的就是这群3.1 docker run 的参数拆解容器命令里docker run的门道最多它其实是创建 启动两个动作的组合。一个比较全的示例docker run -d \ --name web \ -p 8080:80 \ -v /data/web:/usr/share/nginx/html \ -e TZAsia/Shanghai \ --restart always \ nginx:1.25-alpine逐个解释一下高频参数的含义参数作用使用场景-d后台运行不占终端跑常驻服务日常必备--name给容器命名不命名会生成随机名后续不好操作-p端口映射宿主机端口:容器端口外部要访问容器内服务时必须加-v目录/数据卷挂载持久化数据或把宿主机配置挂进容器-e注入环境变量配置时区、数据库密码等--restart always容器退出后自动重启守护型服务的标配--rm容器退出自动删除临时工具类容器用完即焚-it交互式进入终端进入容器调试时使用理解-p端口映射的坑很重要。容器里的进程监听的是容器自己的端口比如 nginx 监听 80宿主机想访问就得靠-p转一道访问宿主机的 8080流量会转发到容器的 80。这里最容易犯的错是只对宿主机端口做防火墙放行却忘了宿主机 8080 和容器 80 的映射关系导致外部访问不了。-d配合--name基本是启动任何服务的标准姿势因为后台运行不会占你的 Shell重新开终端也能通过名字去操作它。而--rm适合跑一次性任务比如临时起一个 MySQL 客户端做个查询跑完自动删不留垃圾容器。3.2 容器状态查看与启停操作docker ps # 查看运行中的容器 docker ps -a # 查看所有容器包括退出状态 docker ps -a --format table {{.Names}}\t{{.Status}}docker ps -a一定要养成习惯排查容器哪去了的问题全靠它。status 列会显示 Up、Exited、Created、Restarting 等状态Exited 不等于容器被删了它只是停了里面的文件系统还在随时可以重新 start 起来。启停一条龙docker start web # 启动已存在的容器 docker stop web # 优雅停止 docker restart web # 重启常用于改了配置后生效 docker pause web # 暂停保留进程但冻结stop和kill的区别值得单独说。stop发的是 SIGTERM给进程一个优雅退出的机会可以写自己的收尾逻辑比如 flush 缓存的日志kill直接发 SIGKILL强制杀掉可能导致数据未落盘。线上操作优先用stop实在没响应再用kill兜底。3.3 进入容器与拷贝文件调试容器内部最常用的三个命令docker exec -it web bash # 进入容器执行交互式命令 docker exec web nginx -t # 在容器内执行非交互命令 docker cp ./config.json web:/etc/nginx/conf.d/exec是在运行中的容器里执行命令-it加上才是进终端。有个细节很多精简镜像如 alpine里没有 bash 只有 sh进不去 bash 时会报exec: bash: executable file not found这时换docker exec -it web sh就行这是新手最容易卡住的地方。attach也能连接到容器但它会直接挂到容器的主进程上一旦你在终端里按了 CtrlC很可能把容器主进程也杀了。老手基本只用 execattach 我建议直接不用少踩一个坑。docker cp是从容器内外互拷文件的快捷通道适合临时改配置、导日志。但注意cp 进容器改的文件容器一删就没了。想持久保存配置和日志得靠数据卷见后面专题。3.4 connect 容器状态与镜像转换排查时要看的容器详情docker inspect web # 查看容器的完整配置和运行状态 JSON docker inspect web -f {{.State.Status}} # 只提取状态字段 docker inspect web -f {{json .Mounts}} # 看挂载信息 docker top web # 等价于 ps看容器内进程列表 docker port web # 快捷查看映射端口inspect返回的 JSON 信息量极大挂载、网络、环境变量、重启策略全在里面。我排查问题时的固定套路是先docker ps -a看状态再docker logs看输出最后docker inspect看配置细节。三个命令走完80% 的问题能定位。4. 日志、网络与数据的配套命令4.1 日志查看与搜索的组合拳看过日志的人都知道docker logs不只是看看输出那么简单关键在于怎么高效地在大日志量里捞有效信息docker logs web # 输出全部日志 docker logs -f --tail 100 web # 实时跟踪只显示最近 100 行 docker logs --since 30m web # 最近 30 分钟日志 docker logs web 21 | grep ERROR # 过滤错误日志-f是 follow适合部署完服务后实时盯输出--tail避免一次性刷出几十万行。grep 过滤要注意 stdout 和 stderr 都要接上21把错误流合并进标准输出再交给 grep否则有些错误日志根本搜不到。日志量特别大、需要长期采集的场景建议给容器配置 JSON File 或更专业的日志驱动绕开docker logs本身的性能瓶颈但那是后话开发环境先用这套组合拳完全够。4.2 资源监控与磁盘占用docker stats # 实时输出每个容器的 CPU、内存、网络 IO docker system df # 看镜像、容器、数据卷各占了多少磁盘 docker system df -v # 明细到每个对象 docker system prune -af --volumes # 全量清理慎用docker stats在排查哪个容器把宿主机内存吃光了时一测便知。system df则是我每月例行巡检的第一条命令看到 IMAGE 和 LOCAL VOLUMES 数据异常增长就该做清理了。system prune -a会删掉所有未被使用的容器、网络、镜像--volumes还会把孤儿数据卷一并删除——这个操作不可逆使用前务必用docker system df -v确认别把还有用的数据库数据卷给清了。4.3 网络管理理解 bridge 是核心Docker 安装后默认有一个 bridge 网络容器不指定网络时默认挂在这个网上容器之间通过 IP 能互通但直接用容器名访问不行。实际工作中建议创建自定义网络docker network create app-net docker run -d --name web --network app-net nginx docker network connect app-net mysql-server docker network ls docker network inspect app-net自定义网络比默认 bridge 强在两点一是内置 DNS 解析同一网络内的容器可以互相通过容器名访问不用记住 IP这在配置微服务互相调用时是刚需二是可以对网络做隔离按业务域拆网不相关的服务不互通。常见的网络故障也多发生在这一层。比如容器能访问宿主机但访问不了外网优先检查宿主机 IP 转发net.ipv4.ip_forward是否开启容器内ping不通外网但 curl 能通别慌很多精简镜像根本没装 ping先确认工具存在。4.4 数据卷持久化的正确玩法docker volume create app-data docker run -d --name mysql8 -v app-data:/var/lib/mysql mysql:8.0 docker volume ls docker volume inspect app-data docker volume prune这里要区分两种挂载-v app-data:/var/lib/mysql是命名卷数据由 Docker 管理删容器数据还在-v /host/path:/container/path是绑定挂载直接映射宿主机的绝对路径。命名卷适合数据库这种需要被 Docker 托管的数据绑定挂载适合我要直接用宿主机编辑器改配置文件的场景比如 nginx 的 conf 目录挂出来改完重载。一个实操建议生产容器务必挂数据卷且数据卷不要放在系统盘尤其是 Docker 默认的/var/lib/docker/volumes。数据库类的卷长期写入会导致系统盘膨胀拖垮整个宿主机。我一般会把 Docker 数据目录整体挪到大容量数据盘上这也是初始化服务器时就要做完的配置。5. Dockerfile 构建与 Compose 编排实战5.1 写一个标准的多阶段构建镜像构建的核心是 Dockerfile。看一个 Node.js 应用的多阶段构建示例# 阶段一构建依赖 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 阶段二只保留运行产物 FROM nginx:1.25-alpine COPY --frombuilder /app/dist /usr/share/nginx/html EXPOSE 80 CMD [nginx, -g, daemon off;]构建命令docker build -t my-app:v1.0 . docker build --no-cache -t my-app:v1.0 . # 绕过缓存排查问题用多阶段构建的价值一句话概括构建工具和运行环境分离产物镜像只留最小运行集。上面的例子最终镜像里只有 nginx 和静态文件体积可能只相当于 node 镜像的十分之一。同时注意三个细节COPY package*.json ./和RUN npm ci放在COPY . .之前是为了利用构建缓存——只要 package.json 没变后面的依赖安装层就命中缓存重新构建秒过。.dockerignore必须写把node_modules、.git、dist排除掉否则会把几百 MB 的垃圾一起拷进构建上下文。CMD 是容器的默认启动命令daemon off;是因为 nginx 在前台运行时容器才存活你必须保证你的主进程不 daemonize这是容器能不能持续运行的关键。5.2 用 Compose 一键拉起 MySQL 和 Redis单机多容器编排docker compose是事实标准。一个跑 MySQL 8.0 Redis 的示例version: 3.8 services: mysql8: image: mysql:8.0 container_name: mysql8 ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: root123 TZ: Asia/Shanghai volumes: - mysql-data:/var/lib/mysql restart: always redis7: image: redis:7-alpine container_name: redis7 ports: - 6379:6379 command: [redis-server, --requirepass, redis123] restart: always volumes: mysql-data:启动与日常运维docker compose up -d # 按配置启动所有服务 docker compose ps # 查看服务状态 docker compose logs -f mysql8 # 跟踪某个服务的日志 docker compose exec mysql8 bash # 进入某个容器 docker compose down # 停止并删除网络和容器注意到我特意没在 MySQL 部分写command: --default-authentication-pluginmysql_native_password因为 MySQL 8.0 默认用 caching_sha2_password老版本客户端会连不上报Authentication plugin caching_sha2_password cannot be loaded。你要么给客户端升级驱动要么在容器启动时加上这个参数兼容旧客户端二选一别在配置文件里瞎折腾。Redis 用 alpine 镜像加command参数指定密码不需要挂载 conf 文件简单场景够用。真要上主从复制官方推荐用redis-server --replicaof参数或者直接挂一份专门的 redis.conf比在 command 里堆参数好维护得多。down命令有个隐藏行为要提醒它会删除容器和默认网络但不会删除命名卷。所以数据还在下次up -d还能接上。想连数据一起清才用down -v。日常测试环境无所谓生产环境绝对别手滑。6. 高频报错与排查实录6.1 权限问题Got permission deniedLinux 上刚装完 Docker直接敲docker ps最常见的是这个报错Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock原因是当前用户不在 docker 组里无法访问/var/run/docker.sock。标准解法sudo usermod -aG docker $USER newgrp docker改完重新登录即可。加 docker 组等于把管理 root 权限的钥匙交给你这对自己单机开发没问题但团队服务器上要慎重别给所有普通账号都开 docker 组权限。6.2 镜像拉取慢或超时docker pull经常遇到要么卡在Waiting要么直接EOF超时。原因是 Docker Hub 官方仓库的网络状况不稳定这是全球使用者都碰过的普遍问题跟你的环境无关。日常最直接的解法是配置镜像加速器Docker Desktop在 Settings - Docker Engine 里改 registry-mirrors。Linux 服务器改/etc/docker/daemon.json增加 registry-mirrors 配置然后systemctl restart docker。改完验证是否生效可以docker info看 Registry Mirrors 列表。这里有个细节加速器只是帮你快速拉取常见镜像不代表所有镜像都能加速遇到个别仓库拉不动的换 tag 版本或等网络好转再拉都行。6.3 容器启动后马上退出docker run -d nginx之后docker ps发现容器 Exited这是第二高频的问题基本原因就一个容器里的主进程没有保持在前台。Docker 认为容器主进程退出容器就该结束你的进程如果被 daemonize 了docker 看不到它还活着就会判定容器退出。排查方法永远是先看日志docker logs -f your-container日志为空大概率是前台进程问题日志有报错则是配置问题比如端口被占、挂载目录不存在。解决秒退的思路是给你的服务进程设计前台模式常见镜像的官方文档都会注明怎么前台运行。6.4 端口冲突与网络不通启动时如果报bind: address already in use说明宿主机端口已经被占用。用ss -tlnp | grep 端口号找到占用进程要么换个映射端口要么停掉冲突的服务。这个操作要小心别把宿主机系统服务误杀了。网络不通是最磨人的一类问题。容器内curl外网失败先测四件事宿主机能不能上外网、容器能不能 ping 通网关、容器 DNS 是否正常、net.ipv4.ip_forward是否为 1。我遇到过好几次容器启动正常但就是访问不了外网最后都是因为装了 Docker 之后忘了启用 IP 转发改一下内核参数重启 Docker 就好。6.5 Windows Docker Desktop 启动失败的常见场景Windows 上打开 Docker Desktop报Docker Desktop failed to start because virtualization support wasnt detected或者virtualization support not detected这类报错基本都是虚拟化能力没开或没生效。排查顺序是BIOS/UEFI 里确认 VT-x/AMD-V 已开启尤其是笔记本很多新设备默认关闭虚拟化。检查是否装有 WSL2wsl --version能输出版本号才算 WSL2只装了旧 WSL1 的话Docker Desktop 认不出来。如果是在虚拟机里再套一层 Docker Desktop必须开启嵌套虚拟化否则永远报这个错。顺带说一句Windows 上跑 Docker 对内存很敏感Docker Desktop 的设置里可以把 WSL2 的内存上限调大否则同时跑多个容器很容易直接卡死。6.6 生成一份适合自己的速查清单排查到最后我还是建议你建一个自己的笔记文件不用长按场景 - 命令 - 坑三个字段记。比如场景命令坑查容器状态docker ps -a区分 Up 和 Exited跟日志docker logs -f --tail 100报错日志要加 21进容器docker exec -it name sh精简镜像没有 bash清磁盘docker system df先看prune 慎带 --volumes看端口docker port name和 ss 对照看记的时候不用追求全你在实际工作里用过、踩过的才是最容易记住也最值得记的。多年后再看这份笔记就是你自己的 Docker 排障史。我个人最后再分享一个小习惯高频命令我会在 Shell 里配 alias比如dkdocker、dpsdocker ps -a --format table {{.Names}}\t{{.Status}}\t{{.Ports}}。别小看这种小优化每天敲几十次 Docker 命令输错一次名的概率和时间成本都在这里省出来。当然alias 只给单机的自己用没问题团队协作的脚本里永远写完整命令别人读你脚本时不需要猜。这就是我对 Docker 命令最真实的态度工具是为场景服务的别为了炫技写复杂的命令能解决当下问题、别人能看懂就是好命令。
返回列表