ARTICLE DETAIL

资讯详情

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

Docker常用命令实战:从镜像管理到排障全攻略

Docker常用命令实战:从镜像管理到排障全攻略 1. 内容整体设计与思路拆解1.1 为什么你总是记不住Docker命令我见过太多人把Docker当虚拟机用docker run启动一个容器docker exec进去敲命令然后就没有然后了。一旦容器删了就啥都不剩数据丢了才想起来没挂载卷IP变了才想起来没用--network。说句不好听的这不是Docker的问题是你根本没建立Docker的思维模型。Docker和虚拟机最大的区别在于虚拟机是完整的一台“电脑”你把它当服务器折腾而容器是“进程”它天生就是临时、易碎、可丢弃的。你见过有人给一个进程做备份吗没有。所以Docker的设计哲学是容器随时可以删镜像随时可以重新拉数据必须放卷里配置必须用环境变量注入。理解了这句话你再看那些命令思路就顺了。那为什么还要整理“常用命令”因为Docker的命令确实不少而且很多命令的--选项多得吓人。docker run一个命令就有上百个参数你不可能全记住也没必要。真正的用法是先掌握最核心的30个命令把日常开发、部署、排障能跑通其他的用到再查。这篇东西我不会像man手册一样给你罗列所有命令而是按照实际操作场景来组织安装启动、镜像管理、容器生命周期、数据卷、网络、日志与排障、镜像构建、Compose编排。每个场景里我只挑真正高频的、踩过坑的命令附上为什么这么用的解释。1.2 这套命令体系的适用场景与核心价值先看这套东西能解决什么问题。本地开发环境搭建装个 MySQL、Redis、Nginx用 Docker 一条命令起来不用再往自己电脑里装一堆原生服务脏了系统还得卸载。项目交付部署用 Dockerfile 把应用打成镜像到服务器上docker run直接跑依赖、环境变量、启动命令全都固化在镜像里从“在我机器上能跑”变成“在哪都能跑”。多环境一致性开发、测试、生产用同一套镜像避免“开发好好的测试崩了”的经典问题。微服务本地联调一个docker-compose.yml把 MySQL、Redis、Nacos、微服务全拉起来一条命令搞定。这些场景解决的是同一个核心痛点环境的一致性与交付效率。我给你的价值不是列命令而是把每条命令背后的判断逻辑讲清楚。比如同样都是删容器docker rm和docker rm -f有什么区别docker rmi和docker image prune分别什么时候用这些如果只背命令不去理解遇到真实场景还是会卡壳。适合看这篇内容的读者我大致分三类Docker 新手刚装好环境想知道从哪下手怎么不把系统搞坏。用了半年一年但一直靠复制粘贴想系统梳理一下命令体系的开发者。要把 Docker 用到生产环境的运维或全栈工程师需要掌握排障思路和最佳实践。无论你是哪一类我的建议是别背命令跟着场景走。你在真实需求里用过的命令比看十遍文档记得都牢。2. 安装与基础环境配置实操2.1 Docker Engine 与 Docker Desktop 怎么选先解决安装问题。现在装 Docker 基本两条路装 Docker Engine服务端命令行客户端或者装 Docker Desktop带图形界面的一体化工具。如果你用的是 Linux直接装 Docker Engine。装完以后顺手做两件事# 把当前用户加入 docker 组避免每次敲命令都加 sudo sudo usermod -aG docker $USER # 让 Docker 开机自启 sudo systemctl enable docker sudo systemctl start docker注意第一个命令执行完要重新登录终端才生效。很多人改完直接敲docker ps发现还是提示权限不够其实是没重新登录。如果你是 Windows 或 macOS正常推荐 Docker Desktop。但我必须提醒几个常见坑。Docker Desktop 启动的时候报 “WSL 2 kernel version is too low” 或者 “Virtualization support was detected but it is disabled” 这种错误基本都是两个原因一是 Windows 的虚拟化功能没开去 BIOS 里把 Intel VT-x 或 AMD-V 打开二是没启用 Windows 的“适用于 Linux 的 Windows 子系统”和“虚拟机平台”这两个可选功能用管理员权限跑一下# Windows 10/11 管理员 PowerShell dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart执行完重启然后再装或重启 Docker Desktop。还有一个坑某些 Windows 版本对 Docker Desktop 有版本要求系统太旧会直接提示“incompatible version of Windows”那就先升级系统补丁别硬装。2.2 配置镜像加速解决拉取慢的老大难问题装好之后第一个痛点必然是拉镜像慢。国内直连 Docker Hub 的速度懂的都懂一个几百兆的镜像可能拉到一半超时。解决办法是配置镜像加速器。Linux 下在/etc/docker/daemon.json里写{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.nju.edu.cn ] }然后重启sudo systemctl daemon-reload sudo systemctl restart dockerDocker Desktop 直接在设置界面的 Docker Engine 里改同样的 JSON点 Apply Restart 就行。这里有个要点加速器只对 Docker Hub 上的官方镜像和标准仓库生效。你要是从私有仓库或者 ghcr.io 拉镜像加速器没用得另想办法。另外这些加速地址时效性比较强建议用之前先确认哪些还活着多配几个作为兜底也是可以的。配置完怎么看有没有生效docker info输出的 Registry Mirrors 一节里能看到你配置的地址。然后试拉一个镜像测速度docker pull alpine docker imagesAlpine 才几兆拉得快又不占空间拿来做连通性测试最合适。2.3 验证环境是否正常的三个命令环境装好、加速配好之后用这三条命令确认一切正常docker version docker info docker run --rm hello-worlddocker version看客户端和服务端版本两个都要显示才说明 daemon 在跑。docker info看存储驱动、CPU/内存配额、镜像加速器的配置。最后跑hello-world是经典的“验证容器能正常创建和运行”的手段--rm让它跑完自动删掉不残留垃圾。如果你执行docker run hello-world报错Cannot connect to the Docker daemon通常就是 daemon 没启动或者/var/run/docker.sock权限不对。按之前说的加入 docker 组重新登录或者先sudo systemctl start docker拉起来再跑。3. 镜像管理的核心命令与实操细节3.1 镜像的拉取、查看与删除镜像Image是容器的模板。理解这一点很重要镜像相当于一个打包好的操作系统运行环境的只读快照容器是它的一次运行实例。你可以从同一个镜像启动无数个容器它们互不影响因为每个容器都有独立的可写层。拉镜像的命令docker pull ubuntu:22.04 docker pull mysql:8.0 docker pull nginx:latest语法是docker pull 仓库名:标签。标签不写默认是latest但生产环境我强烈建议永远指定具体版本否则过段时间你再docker pull拉下来一个完全不同的镜像可能 API 变了、配置不兼容了全得返工。看本地有哪些镜像docker images # 或 docker image ls效果一样这个命令输出五列REPOSITORY仓库名、TAG标签、IMAGE ID镜像ID、CREATED创建时间、SIZE大小。有个小技巧docker images -a能看到所有镜像包括中间层镜像平时不用加除非要排查构建缓存问题。删除镜像docker rmi ubuntu:22.04 # 强制删除 docker rmi -f ubuntu:22.04正常删除前Docker 会检查是否有容器正在使用这个镜像有的话会报错提示。所以想删除一个镜像得先把相关容器停掉删掉。-f强制删除我不是很推荐日常用容易顺手把正在使用的镜像删了后面排查问题才想起来。另外docker image prune能清理所有悬空镜像即没有任何标签、也没有被容器引用的中间层镜像# 清理所有悬空镜像 docker image prune # 连未被使用的镜像一起清掉-a 表示 all docker image prune -a这个命令配合docker system df看磁盘占用情况特别有效。磁盘不够了先docker system df看看哪一类占用最大再针对性地清。3.2 镜像标签的补充技巧给本地镜像打标签有两种场景一是本地构建完镜像想推送到自己的仓库二是想给某个镜像加个别名方便后续操作。docker tag myapp:v1.0 registry.example.com/myapp:v1.0打完标签后你会发现docker images里两条记录镜像 ID 一样。这其实没有占双倍空间只是同一份镜像多了两个引用。打标签在推送镜像前是必须的一步因为docker push时仓库地址和命名空间都写在标签里。还有一个挺管用的操作用镜像 ID 来操作镜像。比如docker rmi 3b5a4a5b5e7a这种直接删不用记完整仓库名。但注意 ID 可以只写前几位只要不冲突就行一般写前4位就够了懒人福音。3.3 镜像的导入导出在某些内网环境里没法直接拉镜像就得用 tar 包离线搬运# 导出镜像为 tar 文件 docker save -o ubuntu-22.04.tar ubuntu:22.04 # 导入 tar 文件为镜像 docker load -i ubuntu-22.04.tar注意docker save和docker export有本质区别。save是把镜像完整的层级结构保存下来适合镜像分发export是把容器的文件系统导出成一个 tar会丢失历史、层信息和元数据适合做文件系统快照不适合做镜像迁移。这个区别我踩过坑有次图省事用docker export导容器再docker import结果起来了发现 CMD、环境变量全丢了应用起不来。你要迁移镜像就用save/load别混。4. 容器生命周期从启动到删除的完整实操4.1 docker run 的关键参数容器是 Docker 的核心操作对象docker run是最常用的命令没有之一。每次创建一个新容器并启动就是把镜像的实例跑起来。先看一个比较完整的例子docker run -d \ --name my-mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -v /my/data/mysql:/var/lib/mysql \ --network my-net \ --restart always \ mysql:8.0我来逐项拆解这些参数。-d意思是后台运行不加的话容器会挂在前台你一关终端容器就停了。--name给容器起名字不然 Docker 随机生成一个又长又怪的名字管理起来非常痛苦。-p 3306:3306做端口映射宿主机端口:容器端口这样外部才能通过宿主机的3306端口访问容器里的 MySQL。-e注入环境变量MySQL 镜像就是靠MYSQL_ROOT_PASSWORD这个变量来初始化密码的。-v挂载数据卷把容器里的数据目录映射到宿主机路径这样容器删了数据也还在。--network指定网络多个容器可以通过自定义网桥互联。--restart always设置容器退出后自动重启服务挂了能自动拉起来。这里的核心思想是run是一次性的。如果你只是敲docker run nginx然后 CtrlC 退出容器就变成 Exited 状态下次你还得重新docker run。所以要么-d后台跑要么配合--restart保证服务可用。4.2 容器的查看与进入查看正在运行的容器docker ps看所有容器包括已停止的docker ps -adocker ps -a在排障时尤其有用。容器秒退、不断重启你首先就要docker ps -a看它是不是 Exited 了然后docker logs看日志找原因。进入正在运行的容器主流有两种方式# 方式一exec 进入并启动 bash docker exec -it my-mysql bash # 方式二如果你只想去执行单条命令 docker exec -it my-mysql mysql -uroot -p-i是交互模式-t是分配一个伪终端两个组合在一起才像是“打开了一个终端窗口”。注意exec不启动新容器是在现有容器里执行命令这是和run最本质的区别。容器里没有 bash 怎么办有些精简镜像只有 shdocker exec -it my-container sh连 sh 都没有的极简镜像比如scratch或某些 distroless 镜像你甚至没法进去只能靠日志排障。这也提醒你基础镜像选型时如果后续有调试需求别一味追求小留个 shell 能省很多事。4.3 容器的启停、重启与删除docker stop my-container # 优雅停止默认等10秒后再杀 docker start my-container # 启动已存在的容器 docker restart my-container # 重启 docker kill my-container # 强制杀掉stop和kill的区别值得展开说。stop会先给容器内主进程发一个 SIGTERM 信号让它有善后清理的机会等默认 10 秒超时后再发 SIGKILL。kill是直接 SIGKILL立刻杀掉不给机会。日常首选stop除非进程卡死了再kill。删除容器docker rm my-container # 强制删除运行中的容器 docker rm -f my-container # 清理所有已停止的容器 docker container prunedocker rm只能删已停止的容器删运行中的会报错。-f会先强制 stop 再删。docker container prune会把所有已停止容器一次性清理掉包括之前忘删的。批量清理容器的常用组合# 删除所有已停止容器 docker rm $(docker ps -aq --filter statusexited) # 停止所有容器 docker stop $(docker ps -q)这个$(docker ps -q)的用法很香-q只输出容器 ID配合其他命令做批量操作非常方便。不过批量操作前先用docker ps -a确认一下到底会影响到哪些容器别手滑把该留的也删了。4.4 容器与宿主机之间的文件拷贝开发调试时经常要把本地文件拷进容器或者从容器里拷日志出来# 从宿主机拷进容器 docker cp ./config.yml my-container:/app/config.yml # 从容器拷到宿主机 docker cp my-container:/var/log/nginx/access.log ./access.logdocker cp不需要容器在运行状态已停止的容器也能拷。这个很实用比如容器起不来但你想看看容器里的配置文件时docker cp出来看就行。还有一个场景docker cp拷贝整个目录也行docker cp ./data/ my-container:/data/注意目录拷目录时目标路径末尾是否带/会影响结果——带/表示拷贝到该目录下不带则可能改名细节参照cp命令的行为。5. 数据卷、网络与日志排障的实战加深5.1 数据卷的挂载与权限踩坑前面提到-v挂载数据卷这是 Docker 里最容易踩坑的地方。先看两种挂载方式# 方式一绑定挂载把宿主机的文件或目录挂进去 docker run -d --name nginx -v /home/user/html:/usr/share/nginx/html nginx # 方式二命名卷Docker 管理存储位置数据在宿主机上但不用关心路径 docker volume create mydata docker run -d --name mysql -v mydata:/var/lib/mysql mysql:8.0两种方式的区别绑定挂载适合你把配置文件放宿主机、直接改文件就生效的场景命名卷适合数据库这种纯数据存储Docker 会在宿主机上找一个卷目录帮你存不用自己指定路径。权限坑是重点。很多人挂载宿主机目录后容器里进程报权限不足。原因是容器内进程默认以 root 或指定 UID 运行而宿主机目录可能是别的用户所有或者 SELinux 标签不对。一个典型场景MySQL 容器挂载了一个目录结果报[ERROR] [MY-011011] ... Directory /var/lib/mysql ... permission denied。解决办法要么给目录开权限chmod -R 777要么指定容器内用户--user 1000:1000要和宿主机目录属主一致要么用命名卷让 Docker 管理权限。我一般更推荐用命名卷解决权限问题省心很多。5.2 网络模式与容器互联Docker 网络是个大话题但日常你只需要掌握几个基础点。默认情况下 Docker 有一个 bridge 网桥容器通过它联网和互相通信。# 创建自定义网络 docker network create my-net # 指定容器使用该网络 docker run -d --name mysql --network my-net mysql:8.0 docker run -d --name app --network my-net myapp:latest在同一个自定义网络里容器之间可以通过容器名直接互相访问。比如 app 容器里连数据库host 写mysql:3306而不是 IP。这比用 IP 靠谱多了容器重建 IP 会变但名字不变。查看网络docker network ls docker network inspect my-netinspect会列出网络里的容器、网关、子网等详细信息。排查容器为什么互相 ping 不通首先就该检查它们是否在同一个网络里。比较常见的坑是启动容器时忘了指定网络容器都在默认 bridge 里理论也能互联但默认 bridge 不支持 DNS 解析你得用 IP 访问容器一重建 IP 就变了。所以多容器之间要通信一定建自定义网络这算是我比较强烈的一个建议。5.3 日志查看的常用姿势容器排障第一工具就是日志# 查看最近日志 docker logs my-container # 实时跟踪日志输出相当于 tail -f docker logs -f my-container # 只看最后100行 docker logs --tail 100 my-container # 按时间过滤 docker logs --since 2024-01-01T08:00:00 my-container # 或只看最近30分钟 docker logs --since 30m my-container日志这块有个实际经验如果容器一直报错刷日志导致磁盘被写满Docker 默认的 json-file 日志驱动不限制大小短时间内能占到几十个 G。建议在daemon.json里做全局日志轮转配置{ log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }配置完重启 Docker只对新建容器生效已有容器不受影响。或者也可以在docker run时单独加--log-opt max-size100m --log-opt max-file3。这个坑我是在生产环境被教育过的不配日志轮转的容器就像不清理的日志文件迟早把磁盘搞爆。5.4 inspect 与 system df 的排障用法日志看完依然找不到原因就需要看容器细节docker inspect my-containerinspect输出是一个巨大的 JSON包含容器的全部元数据环境变量、挂载点、网络配置、启动命令、重启策略、运行状态等。内容太多不好找可以用--format配合 Go 模板筛选# 只看容器的 IP 地址 docker inspect -f {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} my-container # 只看挂载情况 docker inspect -f {{json .Mounts}} my-container # 只看重启次数 docker inspect -f {{.RestartCount}} my-container关于--format这个玩法我建议你收藏几个高频模板用得多了自然就明白 Go 模板的基本写法。不用每个字段都背用到时docker inspect输出 JSON 里搜就行。再看磁盘占用docker system df输出有四行Images、Containers、Local Volumes、Build Cache分别显示占用磁盘大小。这是个很直观的“Docker 吃了我多少磁盘”答案来源。依次执行docker image prune、docker container prune、docker volume prune、docker builder prune就能逐个清理。特别提醒docker volume prune很重要如果目录是数据卷没有容器引用时它不会被自动删除长期积累会很占空间。但反过来这个命令会把所有没被容器引用的卷全删掉如果有你想留的得先确认再执行。6. 日志排障之外的常见场景速查6.1 构建镜像需要掌握的 Dockerfile 基础日常开发中你不会只docker pull别人的镜像还得构建自己的镜像。Docker 构建镜像的入口是docker build# 使用当前目录下的 Dockerfile 构建 docker build -t myapp:v1.0 . # 指定 Dockerfile 路径 docker build -f /path/to/Dockerfile -t myapp:v1.0 .-t是打标签.表示构建上下文路径。注意这里的上下文docker build会把.目录下所有文件打包发给 daemon所以目录里没什么用的文件尽量清理掉或者用.dockerignore排除不然构建会把很多垃圾传进去速度慢镜像还大。一个最简单的 Node.js 项目 Dockerfile 示例FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD [node, app.js]大致的意义是基于 node 基础镜像设置工作目录先把 package.json 拷进去装依赖利用层缓存提升效率再拷源码暴露 3000 端口指定启动命令。构建完成后查看镜像docker images | grep myapp如果要推送到镜像仓库先打标签再推送docker tag myapp:v1.0 myregistry.com/library/myapp:v1.0 docker push myregistry.com/library/myapp:v1.06.2 Docker Compose 一次拉起整套服务微服务或本地开发环境往往不止一个容器靠docker run一行行敲太痛苦。Docker Compose 可以让你用 YAML 声明式地定义多个服务一条命令搞定。先看一个经典的docker-compose.yml新版 Compose 可以不带version字段services: mysql: image: mysql:8.0 container_name: my-mysql environment: - MYSQL_ROOT_PASSWORDroot123 - MYSQL_DATABASEappdb ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql restart: always redis: image: redis:7.0 container_name: my-redis ports: - 6379:6379 restart: always app: build: ./app container_name: my-app depends_on: - mysql - redis environment: - DB_HOSTmysql - REDIS_HOSTredis ports: - 8080:8080 restart: always volumes: mysql-data:在docker-compose.yml所在目录执行# 启动所有服务 docker compose up -d # 查看服务状态 docker compose ps # 查看日志 docker compose logs -f # 停止所有服务 docker compose down # 停止同时删除数据卷慎用会清数据 docker compose down -v注意新版 Docker 已经用docker compose中间没有横杠替代docker-compose旧版独立命令。如果你还在用docker-compose建议升级并改用新命令。depends_on只能保证启动顺序不保证依赖完全就绪。比如 app 的 DB_HOST 指向 mysql但 mysql 还在初始化app 连接数据库就报错。解决方式通常是让应用具备重试机制或者在 Compose 文件里用healthcheck加健康检查再配合condition: service_healthy。这涉及一点高级用法但这个知识点在生产中非常重要建议有空就去查一下相关文档。6.3 从容器生成新镜像有时候你在容器里手动改了配置、装了软件想让这个状态成为新镜像保存下来用docker commitdocker commit my-container myapp:manual这个用法我一般不建议当成常规操作。因为commit会把容器可写层整体打包成镜像里面可能包含临时文件、历史操作残留镜像臃肿不说整个构建过程也不可重复。更规范的方式永远是写 Dockerfile让构建流程可审计、可复现。commit只适合紧急情况下快速保存现场比如容器快挂了先存个快照。6.4 常见报错与排查速查表最后把实际中高频遇到的报错整理成一个速查表直接照着办报错信息原因排查及解决Cannot connect to the Docker daemondaemon 未启动或 socket 权限不足systemctl start docker或检查用户是否在 docker 组Port is already allocated端口被占用docker ps查看占用端口的容器删掉或换端口No space left on device磁盘满了或 inode 用完docker system df清理镜像、容器、卷df -h确认宿主磁盘pull access denied镜像不存在或没有权限检查仓库名和标签是否写错私有仓库需要docker loginexec: bash: executable file not found容器内没有 bash改用docker exec -it 容器名 shrestarting (x) ... seconds ago容器启动后崩溃在反复重启docker logs 容器名看日志定位崩溃原因OCI runtime exec failed容器已停止或 exec 参数错误docker ps -a确认容器还在运行确认命令路径存在mounted volume permission denied挂载目录权限不足chmod 目录或换命名卷必要时--user指定 UID这些报错基本覆盖了日常使用和环境搭建的 80% 问题。真遇到没见过的情况第一反应应该是docker logs和docker inspect先把可观测数据拿全再决定怎么处理别上来就删容器删完要再拉数据就麻烦了。7. 我踩过的坑与给你的建议7.1 别拿容器当虚拟机我见过很多人的用法是docker run起一个 Ubuntu 容器然后docker exec进去像在虚拟机里一样装软件改配置。这种做法首先是不符合镜像不可变、容器可丢弃的核心理念其次是容器一删所有改动全没了如果没提前 commit 或挂数据卷等于白忙。正确的做法是把应用、依赖、启动命令都固化在 Dockerfile 里。容器只是运行应用的一个进程壳出了任何问题直接删掉重新run一个几秒钟的事。数据靠卷持久化配置靠环境变量注入日志靠docker logs和日志驱动收集。这套思路想明白了你才算真正用对了 Docker。7.2 批量操作之前务必先看一眼docker rm $(docker ps -aq)、docker volume prune -a这类批量命令很高效但也危险。我建议你在敲这类命令之前先跑一下不带删除动作的查询命令确认自己到底会删掉哪些东西。比如# 先看看有多少容器、哪些状态 docker ps -a --format table {{.Names}}\t{{.Status}}确认无误再执行批量删除。尤其docker volume prune删了数据卷就是真的删了没有后悔药。宁可多花十秒看清楚也别事故后拍大腿。7.3 随手加 --rm 让临时容器自动清理如果是跑一次性任务的容器比如临时跑个脚本、试个镜像我建议直接加--rmdocker run --rm -it ubuntu:22.04 bash这样容器退出时自动清理文件系统不会留一堆已停止的容器堆着。这个习惯能很好保持开发机干净整洁。7.4 给容器起个好名字--name字段别偷懒。docker run --name test看着像没问题但过了一周你根本想不起来这个容器是干嘛的。推荐命名规则项目名-服务名-环境比如order-service-prod、mall-mysql-dev。配合 Compose 时container_name保持一样。好的命名能让后续docker ps一眼看明白省不少心智负担。7.5 定期做一次 Docker 环境体检一个小习惯不定时执行docker system df看磁盘占用再执行docker ps -a看有没有堆积的旧容器然后趁机清理。同时看一眼docker info里Server Version和存储驱动确保版本处于可维护状态。这个“体检”不需要很频繁一到两周一次即可但能避免很多因为磁盘占满、容器冲突引发的问题。我个人实际用下来docker 这套命令体系并不难难的是改变“把容器当虚拟机”的思维习惯。真正常用命令其实就十几个你梳理清楚场景每个场景再沉淀出几条固定写法日常效率就能提升非常明显。后续如果有需要还可以往镜像多阶段构建、Compose 健康检查、容器监控方向继续深入。先把这篇文章里的命令亲手敲一遍再回去读 Docker 文档你会发现理解完全不一样。
返回列表