ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/9/16 3:20:08 拓冰建站 浏览量
Docker常用命令实战指南:从镜像管理到容器排障的高频操作 入行这些年Docker几乎成了服务器开发和部署的默认起点。但只要你稍微带过团队、帮人看过环境就会发现一个特别普遍的现象很多人的Docker停留在“会装不会用”“会跑不会查”的阶段docker run能敲出来但镜像卡住、容器起不来、日志刷屏、磁盘爆掉的时候就完全抓瞎。这其实不是悟性问题而是命令体系没有建立起来。Docker的命令说多不多说少不少核心链路其实非常固定拉镜像、跑容器、看状态、进容器、查日志、清数据。如果把这六步吃透日常工作中九成的场景都能覆盖住。剩下的就是遇到问题时的排查命令和Compose、Dockerfile这些周边工具。所以这篇我不打算给你罗列命令字典而是直接用一套“镜像、容器、网络、数据、编排、排障”的思路把高频命令串成一个完整的使用闭环配合实例和参数说明让你看完就能照着用。1. 使用Docker前先把“镜像-容器-仓库”这三个角色搞清楚很多新手记不住命令根本原因是没搞懂Docker里几个核心对象之间的关系。你可以把镜像理解成一台电脑的“装机光盘”它里面预装了操作系统、运行环境、应用代码是一个只读的、静态的文件集合。而容器就是基于这张光盘启动出来的“一台真实运行的电脑”它有自己的文件系统、进程、网络接口可以启动、停止、删除也可以被修改。镜像和容器的关系一句话总结就是镜像定义状态容器产生运行。仓库则是存放镜像的地方最常用的就是Docker Hub还有公司内部自建的Registry。你从仓库拉取镜像到本地再从本地镜像启动容器这三者构成了Docker日常操作的主干。理解了这条主线再看命令就会觉得很有逻辑和镜像相关的命令围绕image展开和容器相关的命令围绕container展开而docker pull、docker push这些就是在镜像和仓库之间传输数据。提示Docker命令的通用结构是“docker 对象 动作”。比如docker container ls是列出容器docker image ls是列出镜像。很多老命令比如docker ps、docker rmi其实都有对应的新写法但日常大家更习惯用短命令你只要认识就行不必纠结哪个更规范。2. 镜像管理命令拉取、查看、删除与加速镜像管理是使用Docker的第一步也是接下来所有操作的基础。这部分命令量不大但使用频率极高而且有几个细节特别容易踩坑。2.1 拉取镜像docker pull 的两种常见姿势最基本的拉取命令是docker pull默认从Docker Hub拉取最新标签latestdocker pull nginx实际项目中我强烈建议你指定版本标签不要依赖latest。原因很简单latest是滚动更新的今天拉的和明天拉的可能不是同一个版本这在生产环境里是灾难性的问题。指定版本的方式是后面加上冒号版本号# 拉取MySQL 8.0版本 docker pull mysql:8.0 # 拉取Redis 7版本 docker pull redis:7 # 拉取某个私有仓库里的镜像 docker pull registry.example.com:5000/app/server:v1.2最后一种写法是从自建仓库拉镜像格式是“仓库地址:端口/项目名/镜像名:标签”。公司内部如果上了CI/CD流程基本都会用到这种形式。2.2 查看与删除镜像docker images / docker rmi拉下来的镜像存在本地用docker images查看docker images这个命令会列出仓库名、标签、镜像ID、创建时间和大小。还有一个高频技巧是用docker images | grep keyword来过滤想找的镜像比如docker images | grep mysql。删除镜像用docker rmi后面跟镜像ID或“仓库名:标签”均可# 用镜像ID删除 docker rmi 3f8a4339aadd # 用名称加标签删除 docker rmi nginx:latest这里有一个经常把新手卡住的点image is being used by container。如果你用某个镜像启动了容器在容器还没删除的情况下直接删镜像系统会拒绝执行。正确的顺序是先删除依赖它的容器再删镜像。如果你确定容器没用了可以一条命令连容器带镜像一起收拾掉注意这是危险操作删除后数据无法找回docker rm -f $(docker ps -aq) docker rmi $(docker images -q)这两条会把本机所有容器和镜像全部清空只在确定要彻底重置环境时使用。2.3 镜像下载慢的解决思路镜像下载慢是每个国内Docker用户都会遇到的问题。最常用的解决办法是配置镜像加速器。以Linux系统为例编辑或新建/etc/docker/daemon.json把加速地址填进去{ registry-mirrors: [https://your-mirror-id.mirror.aliyuncs.com] }修改之后需要重启Docker服务sudo systemctl daemon-reload sudo systemctl restart docker如果你用的是Docker Desktop在设置界面的Docker Engine选项里直接编辑JSON并Apply Restart就行。配置完成后再拉镜像速度通常会有明显改善。如果公司有内网自建仓库也可以直接把流量切到内网地址这是在生产环境中最稳的方式。另外拉取超时的时候先确认网络和DNS再考虑加速器的问题别一上来就盲目改配置。注意不要同时配置一堆加速器不仅没用反而可能导致镜像解析异常。留一个可用的就好。3. 容器生命周期管理run、ps、start、stop、rm 的完整用法镜像只是静态模板真正干活的是容器。容器相关命令是Docker中最核心、最常用、也最需要理解参数含义的部分。3.1 docker run最核心的启动命令docker run是把镜像启动为容器的最常用命令我第一次用Docker跑Nginx就是通过它。一个比较完整的启动示例docker run -d \ --name my-nginx \ -p 8080:80 \ -v /opt/nginx/html:/usr/share/nginx/html \ -v /opt/nginx/conf:/etc/nginx/conf.d \ --restartalways \ nginx:1.24每个参数都有自己的用途拆开来看会更好记-d后台运行容器不加容器会占据当前终端按CtrlC就会停止。--name给容器起名字方便后续管理。不加的话Docker会随机生成一个名字很难记住。-p端口映射格式是“宿主机端口:容器内端口”。上面的例子里访问宿主机的8080端口就等于访问容器内的80端口。-v目录挂载数据卷映射格式是“宿主机目录:容器内目录”。这样宿主机和容器可以共享文件也便于持久化数据。--restartalways开机会自动启动容器服务挂了也会自动拉起。生产环境中我基本都会加这个参数。容器启动后可以用docker ps查看正在运行的容器列表docker ps # 查看所有容器包括已经停止的 docker ps -adocker ps的输出包含容器ID、镜像、启动命令、创建时间、状态、端口映射和容器名。状态一栏有Up和Exited两种Exited说明容器停止了这时候需要排查原因。3.2 容器的启停与删除我平时最常用到的容器操作是stop和rm# 停止容器 docker stop my-nginx # 启动已存在的容器 docker start my-nginx # 重启容器 docker restart my-nginx # 删除容器只能删除已停止的容器 docker rm my-nginx # 强制删除容器不需要先停止 docker rm -f my-nginxdocker stop会先给容器发一个停止信号等待一段时间默认10秒再强制停止。如果你的应用有状态需要优雅退出这个等待时间就很重要。容器删除后容器内产生的新数据非挂载目录里的会一起消失所以重要数据一定要用-v挂载到宿主机或者写进数据卷。我遇到过不少同事MySQL容器一删数据全没了就是因为当时没有挂载目录。如果你需要临时进容器里看环境但不希望它被停止后退出可以执行docker start后再用exec进入这是后话。3.3 进入容器内部docker exec 和 attach进入运行中的容器docker exec是首选# 进入容器的交互式Shell docker exec -it my-nginx /bin/bash如果容器镜像里没有bash尝试用shdocker exec -it my-nginx /bin/sh-it是两个参数的组合-i表示保持标准输入打开-t分配一个终端。没有这两个参数进了容器也没法交互。入容器后你的操作都发生在容器内部环境里。查看进程、读写文件、安装调试工具都是常规操作。退出时输入exit即可容器仍然保持运行状态。还有一个docker attach命令它连接的是容器的主进程也就是PID 1。如果你用attach进入容器退出时按CtrlP再按CtrlQ容器还在运行但如果你按CtrlC或者输入exit主进程结束容器就停了。所以我一般只用execattach用得很少容易误操作。3.4 容器日志输出docker logs排查容器问题时第一件事就是看日志# 查看全部日志 docker logs my-nginx # 实时跟踪日志输出 docker logs -f my-nginx # 只看最后100行 docker logs --tail 100 my-nginx # 按时间过滤 docker logs --since 2025-01-01T00:00:00 my-nginx-f参数和Linux下tail -f的效果一样会持续输出新日志调试的时候很顶用。如果容器的日志量特别大加上--tail防止终端被刷爆。另外日志文件长期不清理会越积越大后面“系统清理”部分会讲到怎么处理。4. 网络与容器间通信自定义网络、端口映射和容器名互通单台服务器上跑多个容器是常态容器之间的通信就需要理解Docker的网络机制。默认情况下Docker会创建一个名为bridge的虚拟网络每个容器都会在这个网络里获得一个IP地址。但生产环境我基本不直接用默认bridge而是创建自定义网络。原因有两个第一默认bridge下容器只能通过IP互相访问容器重建后IP是会变的第二自定义网络下容器可以直接用容器名作为域名互相访问稳定又直观。4.1 创建网络并指定容器归属# 创建网络 docker network create my-net # 启动容器时指定网络 docker run -d --name app1 --network my-net myapp:latest docker run -d --name app2 --network my-net myapp:latest此时在app1容器内部直接执行ping app2就能通不需要记IP。这种机制在微服务架构下非常实用比如Spring Cloud的服务注册与发现服务地址直接写容器名就能解析。4.2 查看和断开网络连接# 查看所有网络 docker network ls # 查看某个网络的详情包括所有连接到该网络的容器 docker network inspect my-net # 将运行中的容器连接到指定网络 docker network connect my-net my-nginx # 断开连接 docker network disconnect my-net my-nginxnetwork inspect输出的是JSON格式信息很全包括子网、网关、IP等。用户名下的容器IP变了也可以通过这个命令快速查到。4.3 端口映射的几个细节端口映射时宿主机端口不能冲突。比如MySQL默认3306如果你要跑两个MySQL容器一个映射3306另一个就得换成3307docker run -d --name mysql-1 -p 3306:3306 mysql:8.0 docker run -d --name mysql-2 -p 3307:3306 mysql:8.0还有一种情况你不想让容器对宿主机以外的机器暴露端口只希望本机访问可以绑定127.0.0.1docker run -d -p 127.0.0.1:3306:3306 mysql:8.0这样外部机器无法通过宿主机IP访问这个端口安全性提高不少。生产环境里数据库类服务建议这样操作。5. 数据持久化与数据卷volumes、bind mounts 和 copy容器是临时的但数据必须是持久的。Docker提供了几种数据持久化方式数据卷volume、目录挂载bind mount和容器间共享数据卷。5.1 数据卷Volume和目录挂载Bind Mount前面docker run里的-v参数就是目录挂载。这是最常用也最直观的持久化方式宿主机和容器共享同一个目录宿主机改文件容器立刻能看到。# 目录挂载宿主机目录必须存在不存在会创建 docker run -d --name mysql-test \ -v /opt/mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDroot1234 \ mysql:8.0还有一种方式是数据卷它由Docker管理存储在/var/lib/docker/volumes/下好处是不需要关心宿主机目录的绝对路径迁移时也更方便# 创建数据卷 docker volume create mysql-data # 挂载数据卷 docker run -d --name mysql-test \ -v mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDroot1234 \ mysql:8.0两者的区别在于bind mount是宿主机目录直接绑定运维人员可以直接通过文件系统查看和修改volume方式则隐藏了数据位置更适合“我只管容器不需要关心数据具体放哪”的情况。生产环境里配置类文件推荐bind mount数据库、缓存类数据推荐volume。5.2 容器内文件复制docker cp有时候你只是想临时从容器里拷个文件出来看看或者把宿主机文件塞进容器不需要挂载目录# 从容器复制文件到宿主机 docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf # 从宿主机复制文件到容器 docker cp ./nginx.conf my-nginx:/etc/nginx/nginx.conf复制完成后容器内如果是一个长期运行的服务通常需要重启容器或者热加载配置才能生效。比如Nginx复制了配置需要执行docker exec my-nginx nginx -s reload。另外docker cp会自动创建目标目录不需要提前mkdir。5.3 数据卷备份和恢复数据卷虽然方便但如果不做备份数据依然可能丢失。临时的备份方法是用一个临时容器挂载数据卷并打包docker run --rm \ -v mysql-data:/data \ -v /opt/backup:/backup \ ubuntu tar czf /backup/mysql-data.tar.gz -C /data .恢复数据则反向操作docker run --rm \ -v mysql-data:/data \ -v /opt/backup:/backup \ ubuntu tar xzf /backup/mysql-data.tar.gz -C /data--rm参数表示容器执行完命令后自动删除非常适合这种一次性任务。6. 排查问题必用的五个查询命令inspect、stats、top、port、events日常运维里光会启动和停止容器远远不够。容器状态、资源占用、端口绑定、事件流这些问题排查起来有几个命令非常好用。6.1 docker inspect查看容器/镜像的详细信息docker inspect会输出一个JSON格式的超长信息包括容器的完整配置、挂载信息、网络设置、环境变量、启动命令等# 查看容器详细信息 docker inspect my-nginx # 只查看IP地址 docker inspect -f {{.NetworkSettings.IPAddress}} my-nginx # 只查看挂载信息 docker inspect -f {{json .Mounts}} my-nginx-f是Go模板语法适合提取单个字段。JSON接口很规整脚本自动化时也经常直接用它拿数据。排查“容器起不来”或“网络不通”时inspect是我第一个会敲的命令。6.2 docker stats实时查看容器资源占用docker stats这个命令类似Linux的top实时输出每个容器的CPU、内存、网络IO和磁盘IO。排查“哪只容器把服务器内存吃光了”时特别好用。加上--no-stream可以只输出一次不会一直刷新docker stats --no-stream6.3 docker top查看容器内的进程docker top my-nginx它显示的是容器内主进程和子进程的列表可以和宿主机上的ps命令对应起来。要确认某个进程是否异常或者容器内有没有多余进程用它就对了。6.4 docker port查询端口映射关系docker port my-nginx输出会显示容器内端口和宿主机端口的对应关系比如80/tcp - 0.0.0.0:8080。如果端口映射有问题这个命令能快速帮你确认。6.5 docker events实时监控Docker事件docker events这个命令会持续输出Docker守护进程产生的事件例如容器创建、启动、停止、销毁等。调试自动恢复策略、监听异常重启时很有用。如果你只想看某一种事件可以加过滤条件比如docker events --filter eventstart。7. 系统资源清理docker system df 与 docker system prune跑了一段时间Docker后你会发现磁盘空间越来越少。悬空镜像dangling images、停止的容器、无用的数据卷、没用的构建缓存都是“磁盘杀手”。好在Docker提供了成体系的清理命令。7.1 查看占用情况docker system df输出表格会显示所有镜像、容器、数据卷、构建缓存的总量和占用空间。你一眼就能看出哪一块最吃磁盘。7.2 清理无用的资源# 一键清理所有停止的容器、无用的网络、悬空镜像和构建缓存 docker system prune -a-a参数会把没有被任何容器使用的镜像一并清理掉磁盘回收效果非常明显但也要注意如果之后你还想用某个镜像得重新拉取。如果你不想清理所有镜像只想清理悬空镜像和停止的容器docker container prune docker image prune docker volume prune其中docker volume prune要格外小心它会删除没有被任何容器使用的数据卷如果你有一个数据卷暂时没挂载但里面还有重要数据会被一并清掉。所以在执行volume prune之前先确认一遍再动手。技巧我在清理前一般都先跑一下docker ps -a、docker volume ls确认没有“暂时停用但还需要保留”的容器和数据卷再执行清理。宁可多花两分钟也不冒删数据的风险。8. Dockerfile与镜像打包build、tag、push的路子有些场景需要自己制作镜像比如把公司业务代码打包成镜像再部署到测试或生产环境。这些操作会用到docker build、docker tag和docker push。8.1 编写Dockerfile最简Java/Node镜像示例假设你有一个Node.js项目Dockerfile大致如下FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD [npm, start]FROM指定基础镜像WORKDIR设置工作目录COPY把宿主机文件复制进镜像RUN在构建时执行命令EXPOSE声明容器对外端口只是声明不直接映射CMD指定容器启动时运行的命令。8.2 构建与推送到仓库# 在Dockerfile所在目录执行 docker build -t myapp:1.0 . # 给镜像打一个仓库标签 docker tag myapp:1.0 registry.example.com/app/myapp:1.0 # 推送到仓库 docker push registry.example.com/app/myapp:1.0构建的时候注意一个隐藏问题docker build会把当前目录构建上下文里的所有文件都发送给Docker守护进程。如果你的目录里有node_modules、target、.git这种大目录构建会特别慢。解决方案是在项目根目录创建.dockerignore文件写清楚要排除的文件node_modules .git target logs这个文件的作用和.gitignore类似但容易被忽略。我第一次用Docker打包Java应用时忘了加.dockerignoretarget目录300MB直接被打包发送整个构建卡了将近十分钟加完之后秒级完成。8.3 利用构建缓存加速Docker构建过程中每一行指令都会生成一个缓存层。只要指令和上下文内容没变后续构建就直接复用缓存。所以尽量把“不常变化的依赖安装”放在前面把“频繁修改的业务代码”放在后面。上面的Node示例中先把package.json复制进去执行npm install再复制业务代码就是充分利用缓存的标准写法。9. Docker Compose多容器编排的利器真实项目很少只用一个容器。前端、后端、数据库、缓存、消息队列一旦容器多起来一个个docker run敲简直要命。Docker Compose就是用来解决这个问题的——用一个YAML文件描述所有服务一条命令启动或停止全部容器。9.1 一个常见的docker-compose.yml示例以MySQL加Redis为例version: 3.8 services: mysql: image: mysql:8.0 container_name: app-mysql restart: always environment: MYSQL_ROOT_PASSWORD: root1234 ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql redis: image: redis:7-alpine container_name: app-redis restart: always ports: - 6379:6379 volumes: mysql-data:9.2 核心命令Compose项目中的所有命令基本都以docker compose或docker-compose开头新版Docker推荐前者# 启动所有service-d后台运行 docker compose up -d # 查看服务状态 docker compose ps # 查看服务日志 docker compose logs -f # 修改yml配置后重新构建启动 docker compose up -d --build # 停止并删除容器数据卷默认保留 docker compose down # 停止并删除容器、网络和所有对应的数据卷危险 docker compose down -vdown和down -v的差别要记住前者只删容器和网络数据卷保留后者连数据卷一起删。如果数据库数据只存在数据卷里一条down -v就能让你的所有数据消失。我在很多次“误删数据库”的事故复盘里都看到过这条命令的身影。9.3 编排中的环境变量不同环境开发、测试、生产的配置可以用.env文件配合变量替换environment: MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}项目目录下的.env文件会自动被Compose读取。这样生产环境只需要维护一套YAML替换.env内容即可。10. 高频问题与排查技巧实录下面这些问题都是我在各个环境里真实遇到、排查过、也解决过的整理成速查表你可以直接对照。10.1 容器无法启动 / 启动后立刻退出最常见的表现是docker ps看不到容器docker ps -a显示Exited。这时候第一步先看日志docker logs 容器名很多启动失败的原因日志里已经写清楚了比如端口被占用、配置文件语法错误、环境变量缺失等。如果日志是空的考虑是不是进程启动后马上异常退出可以试着不用-d前台运行容器观察输出docker run --rm 镜像名10.2 端口映射不生效排查顺序一般是先docker ps看端口映射是否出现再docker port确认容器和宿主机的绑定关系然后检查宿主机防火墙是否放行对应端口。有些云服务器还需要在安全组里添加规则。另外确认你的服务是不是监听在容器内的正确IP上比如Nginx有时默认只监听IPv6导致IPv4访问失败。10.3 挂载目录权限不足 / 文件写入失败容器内进程通常以非root用户运行它可能没有宿主机目录的写权限。最简单的办法是给目录设置合适的权限sudo chown -R 1000:1000 /opt/nginx/html这里的1000通常是容器内用户的UID具体以镜像为准。还有一个临时办法是在docker run时加上--user参数指定用户但这会影响其他功能不推荐长期使用。10.4 Docker Desktop启动失败Windows和macOS上Docker Desktop启动失败的原因里最常见的是虚拟化没有开启或者WSL2环境不对。错误提示类似于“Docker Desktop failed to start because virtualization support wasnt detected”。排查步骤确认BIOS里开启了Intel VT-x或AMD-V虚拟化。在Windows“启用或关闭Windows功能”里勾选“虚拟机平台”和“适用于Linux的Windows子系统”。更新WSL2内核执行wsl --update。如果之前装过Docker Toolbox或有旧版WSL卸载后重装Docker Desktop。如果是Linux环境下的Docker服务启动失败先查看错误信息sudo systemctl status docker sudo journalctl -u docker --no-pager | tail -50关键词可以直接指向解决方案比如“permission denied”是权限问题“address already in use”是端口冲突。10.5 容器内执行命令提示Permission denied容器内权限问题通常表现为-bash: ./xxx.sh: Permission denied。先确认文件是否有执行权限docker exec -it 容器名 chmod x /path/to/xxx.sh如果脚本在挂载目录里记得在宿主机上也设置好权限。还有一种情况是容器内缺少所需依赖比如glibc版本不对那就需要在Dockerfile里安装相应依赖或者换更合适的基础镜像。10.6 Docker磁盘空间被占满docker system df能快速查看占用大头。清理思路分四步docker system prune -a清理无用的镜像和构建缓存。docker container prune清理停止的容器。docker volume prune清理无用的数据卷谨慎使用。如果Docker日志文件暴涨检查容器的日志驱动是否配置了max-size和max-file比如在daemon.json中配置{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }配置完成后单个容器日志都会被限制在10MB且最多保留3个文件磁盘压力会小很多。写在最后的一点个人建议用了这么多年Docker我有一个习惯把常用的固定命令写成shell脚本或者直接配好alias。比如启动某个服务、清理某类资源脚本化之后省下大量重复敲命令的时间。还有一个经验是凡是要挂载进容器的宿主机目录尽量先规划好路径比如统一放在/opt/docker/下面别到处乱放不然镜像多了之后管理成本会直线上升。Docker本身不难难的是养成一套规范的、可持续的操作习惯。希望上面这些内容能帮你更快把命令体系建立起来。