最近被问到的 Docker 相关问题的密度有点高有人在 Windows 上装了 Docker Desktop双击图标后直接报 “virtualization support was not detected”有人在 Ubuntu 上把 docker 装好了结果docker ps却提示权限不够也有人兴致勃勃要部署 MySQL结果容器启动后没数据、时区还是 UTC。说实话这些问题几乎都是入门者绕不过去的坎而 Docker 本身恰恰是那种“会者不难难者不会”的工具。这也让我想把“Docker 的简单介绍”这篇文章认真写得更实用一些把镜像、容器、安装、排障这些基础概念揉进真实场景里讲清楚。其实我并不想写一篇教科书式的 Docker 教程而是想以一名使用者的身份把“第一次用 Docker 到今天能熟练部署 MySQL、Redis、微服务”这条路上最有价值的经验沉淀下来。无论你是开发同学想统一本地环境还是运维同学要在一台新服务器上快速拉起一套服务或者只是前端想在自己的电脑上跑一个带 Python 环境的实验项目这篇文章都值得你花十分钟看完。只要顺着文章走一遍你至少能自己完成 Docker 的安装、配置镜像加速、跑起第一个容器并且遇到常见报错时知道该从哪个方向去查。1. 先用一句话说清 Docker 到底是什么Docker 是一个开源的容器化平台它让你能把应用和它需要的运行环境一起打包成一个“镜像”再用这个镜像创建出一个个相互隔离的“容器”。这种隔离不像虚拟机那么重但又比直接跑在宿主机上干净得多。网络上关于 Docker 的定义很多但我更愿意把它理解成“标准化的软件交付方式”你在笔记本上怎么跑起来的到服务器上就能以几乎完全一样的方式跑起来。1.1 容器与虚拟机本质区别在哪里很多人刚开始会把容器和虚拟机混为一谈其实两者的设计思路差别很大。虚拟机需要模拟出一套完整的硬件环境每个虚拟机里都要装一个完整的操作系统所以你启动一台虚拟机等于是在找一台“虚拟电脑”开机资源开销自然大。容器就不一样了它直接共享宿主机的操作系统内核只是在用户空间里做了隔离。你拉下来的镜像里通常只有应用程序、依赖库、配置文件和必要的系统工具链没有完整的操作系统内核。用生活里的比喻来说虚拟机就像搬了一整套房子里面要有水电、家具、装修而容器更像是用标准集装箱运输货物箱子本身是标准化的在不同港口之间搬运时只需要统一的吊装设备也就是宿主机内核就能高效地装卸。这也是为什么容器启动通常只需要“秒”级的时间而虚拟机动不动就要等几十秒甚至几分钟。正因为这种内核共享的机制容器对硬件资源的利用率会高很多。一台 8 核 16G 的机器跑几个重量级虚拟机可能就快顶不住了但跑一二十个轻量容器往往还能轻轻松松。这也是微服务架构和 CI/CD 流水线普遍拥抱容器的重要原因。1.2 Docker 在真实项目里到底能解决什么痛点把 Docker 放在实际工程环境里看它解决的核心痛点是“环境不一致”。同一套代码你在自己电脑上能跑同事电脑上报依赖错误测试服务器上又缺了某个系统库这种“我这边明明是好的”的惨案几乎每个团队都经历过。用 Docker 打包镜像后代码、运行时、依赖、配置全都被固化在镜像里到哪都是同一套环境。另一个痛点是快速交付和弹性伸缩。以前部署一个应用可能要写一堆安装文档什么 GCC、Nginx、Node.js、Python 版本没有人敢保证照着文档走不会出岔子。现在很多项目只需要把镜像往仓库一推目标机器上执行docker pull和docker run就完事了。新机器扩容、灾备切换、临时环境搭建效率都高了不少。Docker 的应用范围也非常宽。大数据领域有人直接拉 Hadoop 镜像做实验AI 领域有人用 Docker 部署模型推理服务比如用 vLLM 镜像加载 Qwen3 这类开源模型安全领域里Kali 的 DVWA 靶场也经常以容器方式部署。可以说只要是需要“研究软件运行环境”的场景Docker 几乎都能以更干净、更可复现的方式介入。2. 安装 DockerWindows 与 Linux 两条路安装是卡住很多人的第一道坎。Docker 的安装方式跟操作系统关系很大而且官方针对桌面端和服务器端提供了不同的解决方案。这里我分别把 Windows 和 Linux 的安装过程说清楚顺便把最常出现的启动失败和权限问题一起处理掉。2.1 Windows 安装 Docker Desktop 与虚拟化检查Windows 上主流的方案是安装 Docker Desktop它基于 WSL 2 或者 Hyper-V 运行。安装过程本身不复杂从官网下载 Docker Desktop 安装包双击运行按提示“同意协议 → 选择是否使用 WSL 2 → 安装完成”真正让人崩溃的是安装之后启动失败。如果你遇到 Docker Desktop 启动时弹窗提示 “Docker Desktop failed to start because virtualization support was not detected” 这类错误说明宿主机上的虚拟化能力没有被正确识别。这时候按顺序做三件事第一重启进 BIOS/UEFI 设置检查 CPU 虚拟化是否开启。Intel 平台找 VT-xAMD 平台找 SVM不同主板叫法不一样但基本都在 Advanced / CPU Configuration 这类菜单里。不少品牌机出厂默认是关闭的这一步最容易漏。第二确认 Windows 功能里已经启用了“适用于 Linux 的 Windows 子系统”和“虚拟机平台”。可以在“启用或关闭 Windows 功能”窗口里勾选也可以在管理员 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 11 上这个过程通常会更顺畅一些老版本 Windows 10 则要特别注意系统版本和补丁是否到位。第三打开 PowerShell 执行wsl --status确认默认版本是否为 2。如果发现当前 WSL 内核版本过旧执行wsl --update更新内核。Docker Desktop 首次启动时会初始化一个后端 Linux 虚拟机这一步需要下载并安装特定内核文件网速不好时可能卡很久甚至让你误以为程序卡死了。多等一会儿或者挂一次系统更新后再试试一般就能解决。Docker Desktop 本身也支持界面截图、容器日志查看和镜像管理对普通用户来说比纯命令行友好太多。Windows 11 上如果看到英文界面不习惯可以在 Settings 里调整界面语言实际功能上中英文的差异并不大。2.2 Linux 安装Ubuntu 与 CentOS 的差异Linux 下安装 Docker 的路径主要有两条一是直接用发行版自带的 docker.io 包二是安装 Docker 官方仓库里的 docker-ce 版本。对大部分学习者来说Ubuntu 用户用系统自带的包就满足需求了sudo apt update sudo apt install docker.io -y sudo systemctl enable --now docker docker version执行完docker version能看到客户端和服务器版本信息就说明 Docker 服务已经跑起来了。需要提一下systemctl enable --now docker的作用是设置开机自启并且立即启动服务。如果漏了这一步服务器重启后 Docker 服务不会自动拉起到时候排查“docker 服务启动失败”又会多绕一圈。CentOS 7 的情况稍微特殊一点。老版本 CentOS 自带源里的 podman 和旧版 docker 包很容易让人踩坑更稳妥的做法是安装 docker-ce。官方仓库地址和安装步骤在网上可以查到大体思路就是先把 Docker 官方仓库加到yum源里再安装docker-ce。如果你机器上已经有旧版本 docker执行更新时要留意要不要保留旧数据目录/var/lib/dockersudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io sudo systemctl start docker sudo systemctl enable dockerCentOS 7 上安装较新版本 Docker 时可能会遇到依赖库版本过老的问题尤其是container-selinux和iptables相关组件。尽量不要盲目卸载系统组件来迁就安装优先选择跟系统版本匹配的 Docker 版本或者考虑把老系统升级到更新的发行版再去跑 Docker。这听起来像是“逃避”但实际部署时旧系统的兼容性黑洞会浪费大量时间。2.3 不用动不动就 sudodocker 权限配置装完 Docker 后普通用户执行docker ps往往会看到“permission denied”或者 “Got permission denied while trying to connect to the Docker daemon socket” 的提示。这是因为 Docker 守护进程的 socket 文件/var/run/docker.sock默认只有 root 用户和 docker 组的成员可以访问。解决办法不是每次命令都加sudo而是把自己加入 docker 用户组sudo usermod -aG docker $USER newgrp dockernewgrp docker是让当前会话立刻生效的命令不用重新登录系统。如果你用的是远程 SSH 连接可能还是要断开重连一次才能让用户组变更完全生效。加完组之后再执行docker ps就不会再报权限问题了。不过这里我要多说一句把用户加入 docker 组相当于把等同于 root 级别的能力交给了该用户因为 Docker CLI 可以通过 socket 操作容器进而操作宿主机文件系统。在自己本机学习时没有太大问题但在生产服务器上添加 docker 组成员要慎重至少不要随便给普通业务账号开放这个权限。3. 镜像、容器、网络与数据卷安装好之后就要开始真正使用 Docker 了。很多初学者喜欢只记命令不去理解背后的对象模型结果遇到组合场景就懵。实际上只要理清“镜像、容器、网络、数据卷”这四个核心对象的关系Docker 的基本玩法你就算掌握一大半了。3.1 先学会拉镜像、查镜像、删镜像镜像可以理解成一个只读的模板里面包含运行应用所需的一切。最常用的获取方式是从镜像仓库拉取比如docker pull mysql:8.0 docker pull redis:7 docker pull python:3.10-slim这里说一下:后面的 tag。mysql:8.0里的“8.0”是版本号用来区分不同版本的镜像。省略 tag 时默认拉取latest但生产环境建议显式指定版本否则哪天基础镜像更新了你的环境可能就变得不可复现了。拉取完成后可以用docker images查看本机已有的镜像。这句话执行后你会看到 REPOSITORY、TAG、IMAGE ID、CREATED、SIZE 这几列。IMAGE ID 是镜像的唯一标识删除镜像时可以用docker rmi mysql:8.0如果镜像已经被某个容器使用了docker rmi会提示无法删除。这就是 Docker 里的一种“依赖关系”容器由镜像创建而来反过来删除镜像前要先清理掉由它创建的容器。这种顺序感在入门阶段要建立起来。3.2 创建和进入容器docker run 的正确打开方式创建容器的命令非常直观docker run -d --name mynginx -p 8080:80 nginx拆开看-d表示后台运行--name mynginx给容器起个名字-p 8080:80把宿主机的 8080 端口映射到容器的 80 端口nginx是使用的镜像名。执行完之后浏览器访问http://localhost:8080就能看到 Nginx 的欢迎页。这台容器现在已经是一个完整的、可访问的 Web 服务了。我遇到过很多次把-p搞反的情况这里重点强调一下端口映射的方向左边是宿主机端口右边是容器内端口。很多人写配置时总写成-p 80:8080然后发现访问不到排查半天其实就是方向反了。进入一个正在运行的容器最常用的是docker execdocker exec -it mynginx bash-it的意思是分配一个交互式终端后面跟容器名和要执行的命令。Nginx 官方镜像里可能没有bash只有sh那就用docker exec -it mynginx sh。在容器内做的修改如果不把数据卷或文件拷贝出来容器一旦被删除修改就全部丢了。这是后话但不提前知道很容易踩坑。查看容器日志用docker logs mynginx停止和删除容器分别用docker stop mynginx和docker rm mynginx。如果容器已经停止但不删它仍然占用着容器的名字和文件系统下次想用同一个名字就不行。所以调试阶段我通常直接用docker rm -f 容器名强制删除一步到位。3.3 数据卷容器再也不是“一次性玩具”刚开始接触 Docker 的人最容易犯的一个错误是在容器里存数据然后随手docker rm把容器删了发现数据全没了。容器本身是瞬态的应该把数据存储在宿主机上或者数据卷里。数据卷可以理解成 Docker 帮你管理的一块独立存储区域它与容器的生命周期解耦。创建容器时挂载数据卷的写法是docker run -d --name mysql8 \ -e MYSQL_ROOT_PASSWORD123456 \ -v mysql-data:/var/lib/mysql \ mysql:8.0这里-v mysql-data:/var/lib/mysql表示把名为mysql-data的卷挂载到容器内的/var/lib/mysql目录。MySQL 的数据文件都写在/var/lib/mysql里所以以后即使容器被删只要卷还在重新创建容器时挂上同一个卷数据就都还在。除了命名卷Docker 也支持 bind mount也就是把宿主机上的某个绝对路径直接挂载进容器-v /home/user/app:/app这种方式适合开发调试因为宿主机上的代码改完之后容器里立刻就能看到。但 bind mount 和容器间的权限映射比较复杂新手阶段建议优先使用命名卷简单且不容易出现权限问题。3.4 网络为什么容器之间连不上“docker 网络不通”是我见过被问得最多的问题之一。要理解容器网络先记住几个默认概念。Docker 安装后会自动创建几个网络其中最常见的是bridge网络。默认情况下容器会加入bridge网络并且有一块独立的 IP 地址宿主机可以通过映射端口访问容器但容器如果要访问另一个容器最好不要依赖动态 IP而是通过容器名去访问。由于默认 bridge 网络里的容器之间不能直接用容器名解析比较稳妥的办法是创建自定义网络并让相关容器加入同一网络docker network create mynet docker run -d --name redis-server --network mynet redis:7 docker run -d --name app --network mynet myapp-image在同一个自定义网络里容器可以直接通过容器名互相访问。比如上面例子中app容器里写redis-server这个主机名就能连上 Redis不用关心 IP 地址是多少。这个特性在做服务编排、微服务联调时特别重要也是后面 Docker Compose 能轻松实现多容器互联的基础。如果容器要访问外部网络默认的 bridge 网络本身就会做 NAT 转发正常情况下不需要额外配置。但有些服务器环境里 iptables 被改过或者 Docker 服务启动时没有正确初始化网络规则就会出现容器能起来但无法访问外网的现象。排查时可以先看宿主机能不能上网再检查容器内/etc/resolv.conf的 DNS 配置很多时候时区不对、DNS 不对都会让人误判成网络问题。4. 高频实操场景MySQL 8.0 与 Redis 主从概念说再多不如亲手部署两个典型的服务。MySQL 和 Redis 可以说是容器化部署最常接触的两个中间件一个代表持久化存储一个代表缓存和数据结构各有各的注意点。4.1 部署 MySQL 8.0 并从宿主机访问部署 MySQL 8.0 的命令本身并不复杂关键是参数要理解到位docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -e MYSQL_DATABASEtestdb \ -e MYSQL_USERtestuser \ -e MYSQL_PASSWORDuser123 \ -e TZAsia/Shanghai \ -v mysql-data:/var/lib/mysql \ mysql:8.0环境变量MYSQL_ROOT_PASSWORD是 root 用户的密码MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD是用来创建一个初始数据库和普通用户的。如果你不写这三个容器启动后只有一个 root 用户。这里建议官方镜像的习惯尽量不要用 root 跑业务连接把普通用户建好密码单独管理。TZAsia/Shanghai是我强烈建议加上的参数。MySQL 容器如果不指定时区默认是 UTC日志时间跟你本地的“北京时间”差 8 个小时。你想象一下半夜排查日志发现时间差了 8 小时有多崩溃。容器里很多软件默认 UTC凡是跟时间打交道的组件都建议显式设置TZ。启动后在宿主机上执行docker exec -it mysql8 mysql -uroot -p输入密码后就能看到 MySQL 命令行。如果你习惯使用 DBeaver、Navicat 这类图形化工具连接连接信息里主机填localhost端口填3306用户名和密码用上面环境变量里设置的即可。有一个很常见的坑宿主机上本来就已经装了 MySQL占了 3306 端口容器再映射 3306 就会冲突。启动报错信息通常是“port is already allocated”或者Bind for 0.0.0.0:3306 failed: port is already in use。解决方案很简单换一个宿主机端口比如-p 3307:3306这样容器对外就是 3307 端口容器内仍然监听标准 3306。这种“宿主端口可以随意变、容器端口保持标准”的思路在容器化环境里非常实用。4.2 用 Docker 搭建 Redis 主从Redis 主从是另一个很适合用容器来演示的功能。原因很简单主从复制需要两个 Redis 实例自己下载源码编译安装一遍再配置费时费力用 Docker 两分钟就能搞定。先创建自定义网络让主从容器可以按容器名通信docker network create reids-net启动主节点docker run -d --name redis-master \ --network reids-net \ -p 6379:6379 \ -v redis-master-data:/data \ redis:7 \ redis-server --appendonly yes启动从节点docker run -d --name redis-slave \ --network reids-net \ -p 6380:6379 \ -v redis-slave-data:/data \ redis:7 \ redis-server --slaveof redis-master 6379--slaveof redis-master 6379是告诉从节点去复制 master 的数据。这里能看到容器网络的价值从节点不需要知道主节点的 IP只需要在同一个自定义网络里通过容器名redis-master就能找到对方。验证复制是否生效可以进入主节点写一条数据再到从节点查询docker exec -it redis-master redis-cli SET foo bar docker exec -it redis-slave redis-cli GET foo如果返回bar说明主从同步已经成功建立。Redis 7 版本里也支持REPLICAOF命令但命令行参数--slaveof仍然是兼容的。需要提醒的是容器里跑 Redis 如果开启了持久化务必挂载/data目录常写-v redis-master-data:/data这样即使容器重建数据也不会丢。如果你只是本地测着玩不挂载卷也能跑但生产环境不持久化的 Redis 容器就是在制造事故。5. 当项目变复杂时Docker Compose 与镜像打包单个容器好管理但真实项目往往由多个组件组成数据库、缓存、后端服务、前端静态文件如果每个都用一条长长的docker run命令来启动命令又多又乱根本无法维护。这时候该上 Docker Compose 了。5.1 Compose 文件的基础写法Docker Compose 的核心思想是用一个 YAML 文件把多个容器的配置集中起来然后一个命令起停整个应用。services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: appdb ports: - 3306:3306 volumes: - db-data:/var/lib/mysql app: build: . ports: - 8080:8080 environment: DB_HOST: db DB_PORT: 3306 depends_on: - db volumes: db-data:这个文件里定义了两个服务db和app。db直接使用 MySQL 镜像app用build: .表示从当前目录下的 Dockerfile 构建镜像。depends_on用来声明服务启动顺序让数据库先启动。注意depends_on只控制容器的启动顺序并不能保证数据库已经完全初始化完成业务代码里最好还是要加一个重试机制。使用 Compose 最常用的命令就三个docker compose up -d docker compose ps docker compose downup -d在后台启动所有服务ps查看当前项目里所有容器的状态down停止并删除容器。加上-v会连命名卷一起删除操作前一定要确认数据是否需要保留。新版本的 Docker 已经内置了 compose 子命令不需要再单独装 docker-compose 那套老工具。5.2 用 Dockerfile 把项目打包成镜像Compose 里的build: .依赖 Dockerfile。Dockerfile 就是用文本格式描述“如何把一个应用构建成镜像”的说明书。以 Python 项目为例最简单的写法是这样FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, app.py]每行都对应镜像构建的一层FROM指定基础镜像WORKDIR设置工作目录COPY把宿主机文件复制进镜像RUN在构建过程中执行命令CMD定义容器启动时要运行的默认命令。构建命令是docker build -t my-python-app .-t给镜像打一个标签比如my-python-app。构建过程中如果多次改动代码重新 build 时只有 COPY 之后的那几层会重建之前依赖安装的层会走缓存所以构建速度很快。这也是 Dockerfile 设计上的一个重要优势把不容易变化的依赖安装放在前面把频繁变化的源码 COPY 放在后面。其他语言项目的套路是类似的。Java 项目可以准备一个带 Maven 或 Gradle 的构建阶段也可以直接写多阶段构建先编译出 jar再复制到带 JRE 的运行时镜像里。IDE 里集成 Docker 插件后打包镜像会进一步简化比如 IDEA 里只要配置好 Docker 插件右键就能执行 Build Image不用每次手动docker build。PHP 项目也一样用 PHP-FPM 镜像做好扩展安装再把代码 COPY 进去即可。5.3 微服务项目与多容器编排注意点微服务项目的容器化很典型的做法是每个服务一个镜像再通过 Compose 把所有服务串起来。这时有几个细节非常影响体验。第一个是服务发现。容器 IP 会变化不能用固定 IP 去互联必须依赖 Compose 自动创建的网络在应用配置里把对其他服务的地址写成服务名。比如 Java 项目里配置数据源jdbc:mysql://db:3306/appdb里的db就是 Compose 服务名而不是 IP。第二个是配置来源。不要把数据库密码、密钥直接写在镜像里最好通过环境变量注入。镜像本身是静态的“可交付物”环境变量是“部署参数”两者分开同一个镜像才能在开发、测试、生产环境复用。第三个是日志。容器内的日志默认写到标准输出用docker logs查看。微服务数量一多建议统一采集到日志系统再聚合查看不然每个容器挨个docker logs调试效率会低到让人怀疑人生。Compose 文件里可以配置 logging 驱动把容器日志转发到统一的日志后端具体配置方式取决于团队用的日志平台。6. 高频问题与排查速查表最后把这几年积累的、群里被问得最多的排障经验整理成一个问题速查表按“启动失败类、下载与网络类、权限类、容器运行期类”这几个方向展开。很多问题看起来五花八门但底层原因就那么几种掌握排查思路比死记报错信息有用得多。6.1 启动失败类Docker Desktop 与 docker 服务现象常见原因处理方向Docker Desktop 启动时提示 virtualization support was not detectedBIOS 虚拟化未开启 / WSL2 未启用开启 BIOS 虚拟化启用 Windows 功能更新 WSLDocker Desktop 图标一直转圈首次初始化 WSL2 内核网络较慢多等一会儿或先手动运行wsl --updateLinux 上systemctl start docker失败Docker 服务配置损坏 / 内核模块不兼容journalctl -u docker查看日志重点看 iptables、overlayfs 相关报错CentOS 7 升级 docker 后起不来旧版配置与新版兼容问题备份/var/lib/docker和/etc/docker再考虑升级这里要特别强调排查服务启动失败的第一步永远不是盲目重装而是看日志。Linux 上执行journalctl -u docker -n 50Windows 上查看 Docker Desktop 的日志文件日志会直接告诉你是网络初始化失败、存储驱动不支持还是配置文件语法错误。我见过太多人重装了三次才发现只是/etc/docker/daemon.json里写了一个无效参数。6.2 下载慢与网络不通镜像拉取慢是初学者最普遍的体验之一。这通常取决于镜像源与当前网络环境的连接质量常规的解决方法是给 Docker 配置镜像加速器。Windows 桌面版可以在 Settings 的 Docker Engine 里修改 JSON 配置Linux 需要编辑/etc/docker/daemon.json{ registry-mirrors: [https://docker.mirrors.example.com] }修改完成后执行systemctl daemon-reload systemctl restart docker再重新拉取镜像速度会明显改善。这里要注意镜像加速器只处理镜像下载这一类问题不改变所谓“外部网络”的访问路径如果需要拉取某些特殊来源的镜像还是要靠配置对应 registry 的认证或合规的网络策略。容器内网络不通的另一个常见场景容器能启动但容器里apt install完全没反应。这时候要先确认宿主机网络正常再进容器检查 DNSdocker run -it --rm alpine cat /etc/resolv.conf如果 DNS 配置异常可以在容器启动参数里手动指定 DNS--dns 8.8.8.8。不过在自定义网络里Docker 会自动处理 DNS 转发绝大多数情况下不需要手动干预。6.3 权限和命令找不到问题权限问题前面已经讲过主流的解法加入 docker 组。这里再补充一个细节如果你是通过脚本安装的 Docker安装完脚本通常已经帮你创建好 docker 组了但如果你的机器之前没有 docker 组直接执行usermod -aG docker $USER会报“group docker does not exist”需要先手动创建sudo groupadd docker sudo usermod -aG docker $USER“命令找不到”这个类别里最典型的是 IDE 或者 CI 环境中报cannot run program docker: createprocess error2, 系统找不到指定的文件。这表示执行环境里找不到 Docker 可执行文件。在 Windows 上如果 Docker Desktop 已经装好检查 Docker CLI 的路径是否在 PATH 里通常它位于C:\Program Files\Docker\Docker\resources\bin下。在 IDE 里打开 Docker 配置手动指定 Docker 可执行文件的完整路径即可解决。这一类问题其实不是 Docker 本身坏了而是系统的 PATH 环境变量没有把 Docker 目录加进去。6.4 容器运行期常见问题容器能启动不代表一切正常。运行期的问题往往更让人困惑这里挑几个高频的列出来。端口映射不生效是最常见的。检查顺序是先docker ps看容器状态和端口映射列是否显示了0.0.0.0:8080-80/tcp再确认宿主机防火墙是否放行了对应端口。如果你开了 ufw 或者云安全组80 端口映射得再对防火墙禁止了照样访问不了。这一步排查不出技术问题就是配置问题。容器内时区不对也比较常见。很多官方镜像的默认时区是 UTC处理方法是在启动参数里加上-e TZAsia/Shanghai或者在 Dockerfile 里显式设置。别小看这个问题日志监控、定时任务、数据库记录全都依赖正确的时间。还有个容易被忽略的问题是容器里的文件权限。挂载宿主机目录后容器内进程可能没有权限读写宿主机文件尤其是以非 root 用户运行的镜像。报错信息往往是Permission denied解决办法通常是确保宿主机目录权限对应用户可写或者在容器命令里显式指定--user参数。这里尽量别用--privileged这种绕开问题的方案权限范围太大会带来安全风险。最后再聊一点个人体会玩 Docker 这几年我最大的感受是它的命令真的不用刻意去背关键是理解“镜像、容器、网络、数据卷”这四个对象之间的关系。很多看似复杂的部署拆开之后不过就是拉镜像、起容器、配网络、挂数据卷这几步的排列组合。初学者最容易走的弯路是嘴上说要学 Docker却一直停留在“能用命令跑起一个实例”的层面对于数据到底存在哪里、容器之间怎么通信、构建镜像时为什么要把依赖安装放前面这些问题缺乏思考。其实这些才是真正决定你能否把 Docker 用顺手的核心点。如果你现在正被某个 docker 问题卡住我的建议是从“日志”开始查而不是反复卸载重装。Docker 的报错信息大多数时候已经把原因写得很清楚了只是我们习惯性忽略它。这篇文章里提到的 MySQL、Redis、Compose 场景你可以自己动手各跑一遍跑通之后再换成自己的项目和配置慢慢你就会发现容器化带来的“环境一致”是如此自然的一件事。后续有机会我还会把镜像仓库的搭建、容器安全加固、以及生产环境里日志和监控的配置这些话题展开写一写。