
说实话我见过太多人学 Docker第一步就倒在安装上。Docker Desktop 明明装好了一启动就报 virtualization support not detected好不容易跑起来了拉个 MySQL 镜像又卡在“镜像下载慢”容器是创建了外部程序却连不进去还有人直接在宿主机上执行 docker ps被 permission denied 怼了一脸。这些坑我都踩过所以一直想写一篇能“从头到尾走一遍”的 Docker 实操笔记——从最基础的概念讲起到 Windows/Linux 安装到用 MySQL 和 Redis 做实战到 Compose 编排整套环境再到自己写 Dockerfile 构建镜像最后把高频报错整理成一份小抄。这篇的目标很明确你跟着做一遍日常开发和部署中 80% 的 Docker 场景都能自己搞定了。1. 镜像、容器、仓库先弄清 Docker 的三大基础概念很多人一上来就敲 docker run却搞不清自己到底在操作什么。这就好比你要学开车至少得知道油门、刹车、方向盘各自是干嘛的。Docker 的核心概念其实就三个镜像Image、容器Container、仓库Repository。1.1 从“集装箱”理解 Docker 的价值Docker 这个名字本身就在说它的设计思路——集装箱。传统运输靠散货装卸大小不一、规格混乱效率极低集装箱出现后所有货物装进统一规格的铁箱子里吊车一吊就走轮船、卡车、铁路全都能无缝衔接。软件世界的问题也是一样的。你写好的程序依赖特定的操作系统版本、特定的运行时、一堆系统库换一台机器就“水土不服”。以前的做法是搞虚拟机——每个虚拟机里塞一个完整的操作系统重量大、启动慢、资源浪费严重。Docker 的做法则是把应用程序和它需要的所有依赖代码、运行时、系统工具、库、配置文件全部打包成一个标准化的镜像然后放到任何装了 Docker 的机器上运行。容器共享宿主机内核不需要自带系统所以秒级启动、占用极小。打个比方虚拟机是一整套毛坯房每套都自带承重墙容器是一间标准酒店客房基础设施共用拎包入住。对开发者来说最直观的感受就是——换电脑、换服务器、给别人部署项目再也不用“在我电脑上是好的呀”。1.2 镜像、容器和仓库分别是什么镜像Image一个只读的模板相当于“安装包”或者“类”。里面包含了运行一个程序所需的全部文件和环境配置。你可以把它理解成一张光盘内容刻好之后就不可变了。容器Container镜像运行起来之后的实例相当于“安装好的程序”或者“对象”。同一个镜像可以同时启动多个容器每个容器相互隔离可以启动、停止、删除。仓库Repository存放镜像的地方。最常用的是 Docker Hub相当于镜像界的应用商店也可以理解成代码界的 GitHub。你可以从仓库拉取pull别人做好的镜像也可以把自己构建的镜像推push上去分享或备份。镜像还有一个重要特性——分层存储。镜像由一层层只读文件系统叠加而成拉镜像时看到的一行行 Pulling fs layer 就是在下载这些层。基于这个特性多个镜像可以共享底层比如你同时装了 MySQL 5.7 和 8.0底层的操作系统层可能是同一份不会重复占用磁盘。1.3 认识 Docker 的客户端和守护进程日常用的 docker 命令是客户端工具真正干活的叫 Docker daemon守护进程。客户端把命令发给守护进程由守护进程负责拉镜像、创建容器、管理网络和卷。这就能解释两个新手常见问题执行 docker ps 提示 “Cannot connect to the Docker daemon”说明客户端在但守护进程没跑起来。Linux 下就是 Docker 服务没启动Windows 下就是 Docker Desktop 没开。执行 docker 命令提示 permission denied客户端没权限访问守护进程的接口文件 /var/run/docker.sock。解决办法后面安装章节会细说。理解了这三件套和客户端/服务端的结构后面所有操作就有了脚手架不会再觉得 Docker 是一堆零散命令的集合。2. 安装避坑指南Windows 和 Linux 下把 Docker 真正跑起来下面的内容会覆盖 Windows 11 安装 Docker Desktop 和 Linux以 Ubuntu 为例安装 Docker Engine 的完整流程。这个过程是坑最多的地方我会把高频报错直接对到解决方案上。2.1 Linux 安装 Docker Engine不要直接用旧版 apt 源Linux 下装 Docker 最大的坑是用系统自带的 apt 源直接安装版本非常老功能不全。推荐先卸载可能存在的旧包然后通过 Docker 官方源安装。# 卸载可能存在的旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc # 添加 Docker 官方 GPG 密钥和软件源 sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装 Docker Engine、CLI 和 Compose 插件 sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin安装完之后让 Docker 开机自启并启动服务sudo systemctl enable docker --now验证是否成功sudo docker run hello-world能打印出 Hello from Docker! 就算通了。如果直接执行 docker run hello-world 报 permission denied原因就是我上一节说的——普通用户没有权限访问 docker.sock。当前用户加入 docker 组即可sudo usermod -aG docker $USER # 刷新组权限或重新登录 newgrp docker注意加组之后一定要重新登录会话或执行 newgrp docker 才能真正生效。还有一点必须提醒docker 组的权限约等于 root能操作 Docker 的人基本能控制宿主机所以不要把无关账号随便加进 docker 组。2.2 Windows 安装 Docker DesktopVirtualization 报错的完整解法Windows 下装 Docker Desktop最常见的失败画面是启动时弹窗报 “virtualization support not detected” 或者 “failed to start because virtualisation support wasnt detected”。这句话翻译过来是虚拟化支持没检测到。很多人到这里就放弃了其实问题基本出在两个地方。第一BIOS 里的虚拟化开关没打开。Intel 平台叫 Intel VT-xAMD 平台叫 AMD-V。进 BIOS 找到类似 Intel Virtualization Technology 的选项设置为 Enabled保存重启。这一步在台式机上尤其容易踩因为不少主板 BIOS 默认关着。第二Windows 的虚拟机平台和 WSL2 功能没启用。Docker Desktop 在 Windows 上依赖 WSL2 后端需要先启用相关功能。用管理员权限打开 PowerShell执行# 启用虚拟机平台和 Linux 子系统功能 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart # 重启电脑 Restart-Computer重启后安装 WSL2 内核并设为默认版本wsl --update wsl --set-default-version 2再安装 Docker Desktop。安装完成后打开如果还报 “failed to start docker application container engine”多半是 WSL 内核过旧再执行一次 wsl --update或者到 Docker Desktop 的设置里把 Use the WSL 2 based engine 勾选上并重启。还有一个隐蔽问题如果你装了 VMware Workstation 或 VirtualBox和 Hyper-V 的虚拟化层会冲突。Docker Desktop 用 WSL2 时依赖 Windows 的虚拟化平台VMware 16 以下版本可能直接冲突导致启动失败。要么升级 VMware 到支持 Hyper-V 共存的版本要么卸载第三方虚拟机软件。装好之后从 Docker Desktop 界面确认右下角状态是 Engine running再看一眼 docker version 是否正确显示客户端与服务端版本。2.3 镜像下载慢配置加速器而不是干等热词里“docker 镜像下载慢”是我被问得最多的问题之一。Docker Hub 的服务器在海外国内网络拉取大镜像时经常超时或者只有几十 KB/s。解决办法是配置镜像加速器而不是用代理Docker 对代理配置比较挑剔。Linux 下编辑 /etc/docker/daemon.jsonWindows 下在 Docker Desktop 的 Settings - Docker Engine 里编辑同样的 JSON 配置{ registry-mirrors: [ https://docker.m.daocloud.io ] }修改后必须重启 Docker 才能生效sudo systemctl daemon-reload sudo systemctl restart docker配置加速器不是魔法公共加速源偶尔也不稳定。如果拉取还是失败可以多试几个公共源或者换一个拉取量更小的官方镜像 tag比如把 redis:latest 换成 redis:7.2。3. 第一个容器实战跑 MySQL 8.0 并让外部程序连上它安装通了前面一直铺垫现在终于要跑第一个有实际意义的容器。选 MySQL 8.0 当实战对象是因为它最贴近日常开发而且踩坑概率极高——认证插件、端口映射、数据卷、容器内执行命令这一套下来能覆盖单容器管理的所有核心操作。3.1 docker run 参数逐项拆解先拉镜像再启动也可以直接用 run 命令一步到位docker pull mysql:8.0 docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyour_password \ -v mysql_data:/var/lib/mysql \ mysql:8.0每个参数都值得看懂这决定了你后面会不会被坑-d后台运行不占用当前终端。没加它关掉终端或 CtrlC 容器就停了。--name mysql8给容器起名字。不加的话 Docker 会随机生成一个难记的名字管理起来非常痛苦。-p 3306:3306端口映射。左边是宿主机端口右边是容器内部端口。格式是“宿主机IP:宿主机端口:容器端口”不写 IP 表示绑定所有网卡。本地程序连接 127.0.0.1:3306实际上就是访问容器里的 MySQL 3306。-e MYSQL_ROOT_PASSWORDyour_password环境变量。MySQL 官方镜像通过这个变量完成 root 用户的初始密码设置。-v mysql_data:/var/lib/mysql数据卷挂载把容器里的 MySQL 数据目录映射到宿主机上一个叫 mysql_data 的卷里。启动成功后先用 docker ps 看看状态。如果发现容器没起来用 docker ps -a 能看到退出记录再用 docker logs mysql8 看日志。MySQL 容器启动失败常见原因无非端口被占或者内存不足端口占用时代会直接报。查看端口占用可以用docker logs mysql8 lsof -i :3306 # Linux/macOS netstat -ano | findstr 3306 # Windows3.2 MySQL 8.0 的外部连接问题认证插件和 Host 权限容器跑起来了但很多人的下一步就卡住了——用 Navicat、DBeaver 或者老版本的 JDBC 驱动连接 127.0.0.1:3306报错 Authentication plugin caching_sha2_password cannot be loaded。这是 MySQL 8.0 默认认证插件从 mysql_native_password 改成了 caching_sha2_password老客户端不认识新插件。解决办法不是换数据库版本而是进容器把 root 用户的认证方式改回旧的docker exec -it mysql8 mysql -uroot -p # 进入 MySQL 后执行 ALTER USER root% IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;这里还有第二个隐蔽问题如果你只想让 root 能从外部登录Docker 官方镜像默认创建的 root 用户 host 就是 %允许任意主机连接所以按上面改动就行。但如果是自己建的普通用户创建时一定要显式指定 hostCREATE USER demo% IDENTIFIED WITH mysql_native_password BY demo123; GRANT ALL PRIVILEGES ON *.* TO demo%; FLUSH PRIVILEGES;统一用 %否则你会遇到账号建了、密码也对了但连接时一直报 Access denied 的诡异问题——那多半是用户只允许从 localhost 登录。3.3 数据持久化容器删了数据必须还在启动命令里我已经用了-v mysql_data:/var/lib/mysql这个卷的含义必须理解透。容器是一个轻量、可随时销毁的实例如果用默认匿名存储容器一删除里面所有数据就跟着没了。这对数据库来说是灾难性的。具名卷named volume就是给数据一个宿主机上的永久存储位置。查看它到底存在哪docker volume inspect mysql_data输出里会有 Mountpoint 字段指向宿主机上的实际目录。你随时可以把这个目录里的文件备份到别处。也正因为有这个机制升级 MySQL 版本时可以先停旧容器再起新容器挂载同一个卷数据无缝衔接。除了具名卷还有一种挂载方式是绑定挂载bind mount直接指定宿主机目录映射进容器docker run -d --name mysql8 -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyour_password \ -v /data/mysql:/var/lib/mysql \ mysql:8.0绑定挂载的好处是文件直接落在宿主机目录里方便查看和备份适合有明确目录规划的生产环境。具名卷则胜在由 Docker 统一管理路径不用自己操心适合开发环境。开发阶段我建议一律使用具名卷省心。3.4 进入容器想执行什么命令都直接来排查问题和执行管理操作时需要进入容器内部。MySQL 官方镜像自带客户端所以可以直接docker exec -it mysql8 mysql -uroot -p # 然后输入密码进入 SQL 命令行如果镜像里没有你想要的工具比如需要查看容器内网络情况可以进入容器的 shelldocker exec -it mysql8 bash注意 MySQL 镜像基于 Debian/Ubuntu里面不一定装了 ping、vi 等工具真要装得先 apt-get install。这暴露了一个通用规律容器是精简环境不是完整操作系统。能不用容器里的工具就不要依赖宿主机能完成的操作用宿主机做即可。4. Docker 网络模型从 Redis 主从架构理解容器之间如何通信mysql 那节解决了“宿主机访问容器”的问题这节解决“容器访问容器”的问题。热词里“docker 网络不通”出现频率极高而且绝大多数场景都是同一个原因——容器不在同一个自定义网络里。4.1 三种网络模式的适用场景Docker 默认提供三种网络模式bridge默认模式。容器通过虚拟网桥与宿主机通信外部访问需要端口映射。适合单机上的多个容器互相隔离但需要网络通信的场景。host容器直接使用宿主机网络栈不做隔离。性能最好但端口直接暴露在宿主机上容易冲突。仅 Linux 支持Windows 和 Mac 的 Docker Desktop 不支持。none禁用网络相当于完全隔离。适合只跑离线任务的容器。习惯上直接 docker run 没指定网络时容器会连到默认的 bridge 网络。默认 bridge 有名字解析功能吗有但很弱——容器之间只能用 IP 访问而且容器重建后 IP 会变。这就是“网络不通”的一大来源。我们需要的是一种“不用记住 IP、用名字互访”的方案这就是自定义 bridge 网络。4.2 自定义网络容器间用服务名互访创建网络并让容器加入一条命令开一台主从 Redis# 创建自定义 bridge 网络 docker network create redis-net # 启动 Redis 主节点 docker run -d --name redis-master --network redis-net -p 6379:6379 redis:7 # 启动 Redis 从节点注意没有映射端口外部不需要直接访问它 docker run -d --name redis-slave --network redis-net redis:7 \ redis-server --replicaof redis-master 6379核心魔法就在--network redis-net。在这个网络里Docker 内置 DNS 会把容器名解析为对应 IP。从节点配置里的 redis-master 并不是宿主机主机名而是主节点容器名。只要在同一个自定义网络里这个名字就能被解析网络就通了。验证主从是否建立成功docker exec -it redis-slave redis-cli info replication输出里如果显示 role:slave 和 master_link_status:up说明主从就绪。如果换成默认 bridge 网络从节点根本解析不了 redis-master会一直报 “Cant connect to Redis server”。这也是“docker 网络不通”最常见的真相。4.3 容器访问宿主机服务host.docker.internal 这个彩蛋实际开发里还有一种需求容器里的程序要连宿主机上的某个服务比如 MySQL 装在宿主机而非容器里。容器和宿主机之间什么地址是通的Linux 下最简单的方式是用--networkhost直接共享宿主机网络栈容器内访问 localhost 就是访问宿主机。但 Docker DesktopWindows/Mac不支持 host 模式这时可以用 Docker 内置的host.docker.internal域名它专门解析到宿主机的 IP。docker run -d --name myapp --add-hosthost.docker.internal:host-gateway myapp:latest在 Linux 上想彻底模拟 Docker Desktop 的体验加这个参数即可。容器里配置数据库连接地址时用jdbc:mysql://host.docker.internal:3306/demo从此不再纠结宿主机 IP 到底是 192.168.x.x 还是 172.17.x.x。4.4 容器网络排错三板斧遇到容器间访问失败我通常按三步走效率很高第一确认两个容器在同一个自定义网络docker network inspect redis-net看 Containers 字段列出的容器是否都在。第二进入容器内部测试连通性。比如 redis-slave 里 ping redis-master 不通问题大概率是 DNS 解析或网络隔离而不是业务层面的问题。第三查看进程监听端口docker exec 容器名 netstat -tlnp确认目标进程确实监听在容器内的正确端口上。很多“连不上”最后发现是服务本身没起来或配置监听地址不对。5. Docker Compose用一份 YAML 管理整套环境单个容器用 docker run两个容器手动加网络也能凑合。但到了“应用 MySQL Redis 队列”这种典型组合一条条命令敲下去就是灾难。Docker Compose 就是干这个的用一份 YAML 声明所有服务、网络、卷然后docker compose up -d一键拉起整套环境。5.1 Compose 文件的结构和语义以一个典型的后端项目为例你有一个 Spring Boot或任意 Web 服务应用依赖 MySQL 和 Redis。docker-compose.yml 长这样services: mysql: image: mysql:8.0 container_name: app-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: demo ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 5s timeout: 3s retries: 10 redis: image: redis:7 container_name: app-redis ports: - 6379:6379 app: build: . container_name: app-server depends_on: mysql: condition: service_healthy redis: condition: service_started ports: - 8080:8080 environment: SPRING_PROFILES_ACTIVE: dev volumes: mysql_data: networks: default: name: app-net这里有几个值得说明的设计点services下每个服务等同于一个容器定义。image可以直接用现成镜像build则指定从当前目录的 Dockerfile 构建。同一份 Compose 文件里的服务默认自动加入同一个网络上面的 networks 配置把网络命名为 app-net。在应用代码里配置数据库地址直接写jdbc:mysql://mysql:3306/demoRedis 地址写redis://redis:6379这里的 mysql、redis 就是服务名由 Compose 负责解析成容器 IP。depends_on控制启动顺序。我特意加了 healthcheck因为 depends_on 只保证先启动不保证“可用”。不加健康检查应用启动时 MySQL 还在初始化就会连库失败。这是 Compose 使用中很经典的一个坑。volumes顶层声明了具名卷 mysql_data 供 mysql 服务使用数据和容器生命周期解耦。5.2 常用命令和更新流程# 启动所有服务后台 docker compose up -d # 查看状态 docker compose ps # 查看日志-f 持续跟踪 docker compose logs -f app # 进入某个服务容器 docker compose exec app bash # 停止并删除所有服务保留数据卷 docker compose down # 停止并删除所有服务同时删除声明的卷数据没了慎用 docker compose down -v # 校验 YAML 配置 docker compose config日常更新服务的标准流程是这样改完代码重新 build然后 up -d。因为配置没变Compose 只会重建受影响的容器其他服务保持不动。这比手动停掉再启动要安全得多。docker compose build app docker compose up -d5.3 迁移和备份的便利性Compose 的价值在换环境时体现得淋漓尽致。公司新员工配开发环境以前要写一份好几页的 Wiki 安装文档现在丢一个 docker-compose.yml 过去让他们自己docker compose up -d十分钟搞定。我自己的小项目也是这种模式代码仓库里放一份 Compose 文件本地开发、测试服务器、朋友要跑一套环境同一份配置通吃。要注意的是新版 Docker Compose 已经全面使用docker compose子命令带空格旧版的docker-compose独立命令已经逐步淘汰。如果你用的是新版 Docker Engine已经包含 compose 插件直接用新命令即可。6. 构建自己的镜像Dockerfile 入门与层缓存优化用完别人的镜像迟早要自己做镜像——打包自己的应用、定制运行环境、给项目做交付。Dockerfile 就是构建镜像的“菜谱”一条指令对应镜像中的一层。6.1 一个最小可用的 Node.js 镜像假设有一个 Node.js 应用根目录下有 app.js 和 package.json。最直观的 Dockerfile 这么写FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD [node, app.js]逐条解读一下FROM指定基础镜像。node:20-alpine 是精简版体积比 node:20 小一半以上适合最终部署。WORKDIR切换工作目录后续命令都在这个目录下执行。COPY package*.json ./只先把依赖清单拷贝进镜像。RUN npm install在镜像里安装依赖。COPY . .把项目代码拷贝进去。EXPOSE声明容器对外端口纯文档性质。CMD容器启动时执行的命令。构建命令docker build -t myapp:v1.0 .注意最后的点表示构建上下文是当前目录。Docker 会把这个目录打包发送给守护进程作为上下文所以不需要的文件最好别往里放。6.2 层的原理和理解缓存才是关键很多人写完 Dockerfile 发现每次改代码都要重新 npm install 一遍慢得要命。理解了镜像分层就懂了——每条 COPY 和 RUN 产生一个新层Docker 构建时如果发现某一层没有变化会直接复用缓存。所以上面这个 Dockerfile 的顺序是刻意设计的package.json 在代码前面拷贝npm install 在前面执行。这样你只改 app.js 时package.json 没变RUN npm install 这层会命中缓存整个镜像构建只需要拷贝代码这一层的时间。反过来如果把COPY . .放在前面每次代码变更 npm install 都会全部重跑。你会发现很多老手写 Dockerfile 特别讲究指令顺序不是洁癖是实打实的构建速度优化。6.3 多阶段构建镜像瘦身的重要手段以 Java/Go 这类编译型语言为例构建需要完整 JDK但运行只需要一个小型运行时。多阶段构建让一个 Dockerfile 里出现多个 FROM最后一个 FROM 是最终镜像# 构建阶段 FROM maven:3.9-eclipse-temurin-17 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests # 运行阶段 FROM eclipse-temurin:17-jre WORKDIR /app COPY --frombuild /app/target/*.jar app.jar EXPOSE 8080 CMD [java, -jar, app.jar]最终镜像只包含 JRE 和一个 jar 包体积从带全套 Maven 和源码的几百 MB 缩到一百多 MB。这一套思路同样适用于所有编译型语言。6.4 常用工具链的镜像打包思路热词里提到 “IDEA 打包 docker 镜像” 和 “php 使用 docker 打包镜像”。思路其实都是同一个写好 Dockerfile然后交给任何可以调 docker build 的工具去执行。IDEA 里装好 Docker 插件并配置连接本机 Docker 后可以直接右键 Dockerfile 选择 Build Image本质就是图形化调 docker build。PHP 项目打包时需要注意基础镜像选择。官方 php 镜像分 cli 和 fpm 版本项目需要哪些扩展就用 docker-php-ext-install 安装第三方扩展有时需要编译。比如FROM php:8.2-fpm RUN docker-php-ext-install pdo_mysql COPY . /var/www/html这类镜像无法做到开箱即用是因为 PHP 扩展生态太丰富官方不可能全预装。理解 Dockerfile 的指令逻辑之后这类定制完全在自己掌控中。7. 高频报错排查手册一张表解决 90% 的启动和连接问题最后这部分是我的实战小抄。做 Docker 这些年日常被问到最多的报错就集中在下面这十来条。整理成表格遇到问题直接对表操作。7.1 常见报错自查表报错信息关键词常见原因处理思路permission denied while trying to connect to the Docker daemon当前用户不在 docker 组sudo usermod -aG docker $USER重新登录Cannot connect to the Docker daemon at unix:///var/run/docker.sockDocker 服务没启动Linux 执行 systemctl start dockerWindows 打开 Docker Desktopvirtualization support wasnt detectedBIOS 虚拟化没开 / WSL2 功能未启用BIOS 开启 VT-x/AMD-V启用虚拟机平台和 WSLfailed to start docker application container engineWSL 内核过旧 / 资源不足wsl --update给 Docker Desktop 分配更多内存driver failed programming external connectivity on network宿主机端口被占用docker ps 看现有端口占用或改 -p 宿主机端口name already in use容器名冲突docker rm -f 旧容器或换 --namepull access denied / manifest unknown镜像名或 tag 写错docker search 确认正确的仓库名和 tagno space left on device磁盘空间不足docker system prune -a 清理无用镜像缓存Error response from daemon: conflict端口或资源冲突docker ps -a、docker inspect 详细排查Authentication plugin caching_sha2_password cannot be loadedMySQL 8 默认认证插件和旧客户端不兼容在容器内 ALTER USER 改认证插件7.2 排查思路不要瞎试按链路走遇到报错第一反应不是去搜报错全文而是做好三件事看状态、看日志、看配置。看状态docker ps -a确认容器是 Exited 还是 Up。Exited (0) 表示正常退出查应用日志Exited (1) 通常表示启动时有非零退出码多半是初始化脚本或命令出错。看日志docker logs --tail 100 容器名。这是最直接的线索来源比任何猜测都靠谱。启动失败看启动日志运行异常看运行日志。看配置docker inspect 容器名会打印容器的完整元数据环境变量、挂载卷、网络、端口映射全在里面。遇到“明明配置了为什么没生效”的诡异问题先 inspect 一遍很多答案直接就能看出来。7.3 日常运维必须养成的习惯容器跑久了磁盘被吃掉是必然的。Docker 会积累镜像、容器、卷、构建缓存每一样都可能占大量空间。先用docker system df看看空间被谁占了再执行清理# 清理停止的容器、悬空镜像、无用网络 docker system prune # 更彻底连未使用的镜像和构建缓存一起删 docker system prune -a警示一下docker system prune -a会把没在使用的镜像全部删掉下次启动需要重新拉取。还有卷并不会被上面的命令自动清理如果确认卷数据不要了用docker volume prune但这操作对数据库是毁灭性的——执行前一定想清楚。还有两个实用习惯一是启动命令加--restart unless-stopped让机器重启后容器自动拉起二是给容器设置资源约束避免某个容器吃掉整个宿主机的内存docker run -d --name myapp --restart unless-stopped --memory 1g --cpus 1.0 myapp:latest7.4 容器日志无限增长的处理容器日志默认存到 /var/lib/docker/containers/{id}/*-json.log不设上限的话一个高频日志的服务几周就能写出几个 GB。建议在 daemon.json 里统一配置日志轮转{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }改完重启 Docker 才生效而且只对之后创建的容器生效。已经跑着的容器需要重建docker rm -f 重新 run才能应用新策略。手动截断当前日志的办法是truncate -s 0 /var/lib/docker/containers/{容器ID}/*-json.log容器还开着也能用这个方法清空文件不需要重启比删文件安全——删文件可能会导致容器内文件描述符失效。我自己的体会是Docker 的报错看着吓人但只要养成了“状态—日志—配置”这个排查链路绝大多数问题都能在三五分钟内定位。热词里那些几十次被搜索的报错推到最后都是几个基础概念没搞清楚daemon 没起、权限不够、容器不在同一网络、端口冲突、数据没卷挂载。把这套从安装到编排的流程完整走一遍再回来看这些错误它们就不再是“玄学”而是一道道有标准答案的送分题了。真心建议你把手上常用的 MySQL、Redis 都顺手容器化一遍踩过一轮坑比看十遍教程都管用。