简介这份指南是一份面向容器技术入门者与进阶者的系统教程定位在帮助开发、运维与架构师快速掌握Docker在DevOps与微服务场景下的实际用法。内容从容器与虚拟机的本质差异讲起覆盖Linux/Windows/macOS安装配置、镜像加速、容器生命周期管理、Dockerfile多阶段构建、数据卷与网络模式选择以及Docker Compose编排与常见故障排查结构清晰适合按章节逐步实战。资源包内共1个PDF文件大小约1004KB为纯文字电子文档方便通读与检索也适合作为团队内部培训手册。目前已有200人浏览学习。读者阅读后可独立完成镜像构建、容器编排和基础排错尤其能解决拉取镜像慢、数据持久化、多容器协作等高频问题是一份性价比很高的Docker入门实战手册。1. 为什么说 Docker 是新手必须跨过的第一道坎Docker 真正解决的不是“把应用跑起来”而是“把应用跑起来的环境一并复制过去”。很多新手本地调试好的服务一上传到服务器就缺库、缺依赖、版本不一致本质上是环境上下文丢失。Docker 把操作系统依赖、运行时、配置全部写进镜像容器启动时就是锁定好的环境。这里不绕概念直接从安装、镜像操作、镜像构建、Compose 编排到排错清理走一遍适合刚接触 Docker 的开发者也适合用过一段时间但命令和参数没有系统整理的读者。读完能自己把 MySQL、Redis 和微服务打包上线就算真正入门了。2. Docker 安装分水岭Linux 源管理、Docker Desktop 与权限处理2.1 Linux 使用 apt 还是 get.docker.com先想清楚升级路径对于 Ubuntu/Debian 系的服务器最常见的做法是把 Docker 官方源写进 apt然后用 apt 统一安装和升级。下面这组命令把基础依赖、GPG 签名和源列表一次处理好apt-get update apt-get install -y ca-certificates curl install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod ar /etc/apt/keyrings/docker.asc echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable /etc/apt/sources.list.d/docker.list apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这段命令的逻辑是从 Docker 官方地址导入 GPG 密钥再根据当前系统的架构和版本代号生成 apt 源。$(dpkg --print-architecture)会自动输出 amd64、arm64 等值$(. /etc/os-release echo $VERSION_CODENAME)则是读取发行版代号比如 jammy 或 noble不要把代号写死。docker-buildx-plugin用于多架构镜像构建docker-compose-plugin提供新版docker compose子命令建议和 docker-ce 一起装省得后面单独配 Python 版的 docker-compose。另一种常见的快速安装是官方脚本curl -fsSL https://get.docker.com | sh脚本会自动识别发行版、配置源并完成安装。它适合临时虚拟机但对于需要锁定版本和做安全审计的环境我一般会手动配置源然后通过 apt 的版本号固定 docker-ce。CentOS/RHEL 系的写法类似只是把包管理器换成 yumyum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io systemctl start docker systemctl enable docker安装完成后先验证版本docker version docker compose version注意docker version会同时输出 Client 和 Server 两部分。如果只有 Client 有内容Server 显示permission denied那是当前用户不在 docker 组后面会处理如果提示连接不上守护进程先检查 docker 服务有没有启动。2.2 Docker Desktop 的虚拟化检测与 Failed to start 报错Windows 上安装 Docker Desktop 前先判断 CPU 虚拟化有没有开启。很多笔记本 BIOS 默认关闭 VT-x 或 AMD-V导致 Docker Desktop 启动时直接跳到经典报错“virtualization support not detected” 或者 “Docker Desktop failed to start because virtualisation support wasnt detected”。在 PowerShell 里跑一条命令确认systeminfo看输出中“Hyper-V 要求”一栏四个项目全部为“是”才说明虚拟化可用。也可以运行wmic cpu get VirtualizationFirmwareEnabled返回TRUE说明 BIOS 里已经打开。Linux 主机需要确认 CPU 是否支持硬件虚拟化egrep -c (vmx|svm) /proc/cpuinfo返回值大于 0 表示 CPU 支持但支持不代表开启如果返回 0 或者 Docker Desktop 的虚拟机在启动时报 KVM 相关错误要去 BIOS 里打开虚拟化开关然后重启机器。Docker Desktop 的 WSL2 后端依赖“虚拟化平台”这个 Windows 功能如果关闭即使安装过程没出错桌面端也起不来。还有一个非常高频的 Windows 报错failed to connect to the docker api at npipe:////./pipe/dockerDesktopLinuxEngine。这个不是虚拟化问题而是 Docker Desktop 的 Linux 引擎没有真正启动。常见的处理顺序是先彻底退出 Docker Desktop再用管理员身份重新打开在设置里确认当前切换到 Linux containers 模式然后检查“Use the WSL 2 based engine”是否勾选应用并重启。如果仍然不行就把 Docker Desktop 的缓存清理后重装一次多数 npipe 连接问题都能解决。Windows 11 安装 Docker Desktop 通常比 Windows 10 省心因为新版系统的虚拟化平台默认开启但 Windows 10 用户踩坑概率显著更高。我的建议是安装前先把上面的检测跑一遍能省掉大半启动问题。2.3 启动 docker 服务systemctl、用户组和权限错误Linux 安装完 Docker 后需要先启动守护进程。如果你的发行版使用 systemd执行systemctl start docker systemctl enable docker systemctl status docker启动后状态显示active (running)基本就绪。如果状态是failed说明 docker.service 启动失败先看最近日志journalctl -u docker -n 50常见原因包括daemon.json里镜像源地址写错导致 dockerd 解析失败、containerd 未启动、以及端口占用。这时候把/etc/docker/daemon.json临时改成空配置文件再重启一次能快速判断是不是配置文件的问题。不要一上来就擅自卸载重装日志里通常已经有明确提示。接着用docker run hello-world做第一个容器冒烟测试。如果报permission denied while trying to connect to the Docker daemon socket说明当前用户不在 docker 用户组sudo usermod -aG docker $USER newgrp dockernewgrp docker是让当前 shell 临时切换到 docker 组免去一次重新登录但新开的终端要重启后才能永久生效。长期用sudo docker不推荐因为 root 创建的容器文件和普通用户的配置混在一起后面清理镜像、删除卷时经常出现权限错误。提示修改用户组后务必重新登录终端newgrp只是临时的组切换IDE 里的终端也要重新打开才能拿到新权限。服务正常启动后下一步就是拉镜像和管镜像这是 Docker 日常使用频率最高的动作。3. Docker 镜像操作与镜像源从 pull 到 rmi 再到 registry-mirrors 配置3.1 镜像、容器和仓库的关系用 Git 类比最直接镜像相当于一次 commit 之后的仓库快照容器则是基于这个快照启动的进程。docker pull类似 clone 远程仓库docker commit可以把运行中的容器固化成新镜像但工程上不推荐因为它会把临时文件和 shell 历史也打进去。容器和镜像不是同一份文件镜像删了已经启动的容器还能继续运行容器删了镜像也还在除非你加了--rm并在启动时用--volumes-from之类共享了数据卷。新手最容易混淆的是标签和镜像 ID。一个镜像可以有多个 tag比如nginx:latest和nginx:1.27可能指向同一个镜像 ID。删除 tag 只会减少一条引用并不会立刻释放磁盘只有所有引用都被删除镜像层才可能被清理。3.2 高频命令表pull、images、rmi、tag 的关键参数下面这张表覆盖日常镜像管理最常用的一组命令命令常用参数实际场景docker pull nginx-q只输出镜像 ID--platform linux/amd64指定架构拉取指定版本docker images-a显示中间层--filter danglingtrue过滤悬空镜像查看本地镜像列表docker rmi nginx-f强制删除多个名称空格分隔删除镜像docker tag nginx mynginx:v1无给镜像打新标签docker inspect nginx--format {{.Id}}提取指定字段查看镜像元数据实际操作一次拉取、打标签、删除标签docker pull mysql:8.0 docker tag mysql:8.0 myrepo/mysql:8.0 docker rmi myrepo/mysql:8.0docker pull后加具体版本标签是最安全的mysql:8.0不会像latest那样在语义上漂移。docker tag不是复制镜像而是给同一个镜像 ID 增加一条引用。docker rmi删除的是某个 tag只有该镜像 ID 没有任何引用时它占用的磁盘空间才会真正释放。因此当你删除一个镜像后df -h没看到空间变化不用惊讶可以继续用docker image prune做二次清理。3.3 拉取慢、超时的处理配置 registry-mirrors镜像下载慢是新手使用 Docker 最先遇到的硬伤。Docker 官方仓库节点在部分网络环境下超时频繁一个 100MB 的镜像能拉十几分钟最后还大概率报context canceled。常见做法是给 dockerd 配置镜像源地址也就是 registry-mirrors。Linux 下修改/etc/docker/daemon.jsonsudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json /dev/null EOF { registry-mirrors: [ https://docker.mirrors.ustc.edu.cn, https://hub-mirror.c.163.com ] } EOF systemctl daemon-reload systemctl restart docker这里的每个registry-mirrors数组元素都是一个兼容 Docker Registry HTTP API 的节点dockerd 拉取时先尝试这些地址都不通再回退到 Docker Hub。公共镜像源存在失效和限速的情况生产环境更好的选择是使用云厂商容器镜像服务控制台分配的专属镜像源地址稳定性比公共源高出一个量级。Windows 下配置位置在 Docker Desktop 的 Settings 中 Docker Engine 的 JSON 编辑框里修改后点 Apply Restart 即可。配置完验证是否生效docker info | grep -A 4 Registry Mirrors输出中能看到自己写入的地址就说明 dockerd 已经加载。如果docker pull仍然很慢优先检查 DNS 解析和镜像源到服务器的网络连通性不要反复重启 docker 服务。配置镜像源只是第一步真正到了部署阶段还需要理解容器是怎么跑起来的。4. Docker 实战从 docker run 到 docker compose 编排 MySQL、Redis 主从与微服务4.1 用 docker run 启动 Nginx 并理解端口、挂载与日志先从一个最小的 Nginx 开始跑通“容器是一个独立进程”这个概念docker run -d --name web -p 8080:80 -v /opt/html:/usr/share/nginx/html nginx:stable这条命令的参数含义分别是-d让容器在后台运行--name web给容器命名为 web-p 8080:80把宿主机 8080 端口映射到容器的 80 端口-v /opt/html:/usr/share/nginx/html把宿主机目录挂载到容器内的 Nginx HTML 目录。挂载的意义在于宿主机上的静态文件修改后容器里立即可见不需要进入容器编辑。启动后查看容器状态和输出docker ps docker logs web docker exec -it web bashdocker logs web能看到 Nginx 的访问日志和错误输出排障时第一件事就是看这里。docker exec -it web bash进入容器相当于 SSH 到一台精简 Linux。如果端口被占用docker run会报bind: address already in use先用docker ps -a | grep web看有没有同名容器再docker rm -f web删除。4.2 用多阶段 Dockerfile 打包 Spring Boot 微服务镜像实际部署微服务时不会手动在容器里装 JDK、拷 jar而是把打包过程写进 Dockerfile。用多阶段构建可以做到“构建环境用完整 Maven运行环境只留 JRE”这是目前打包 Java 微服务的主流方式。FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src . RUN mvn clean package -DskipTests FROM eclipse-temurin:17-jre WORKDIR /app COPY --frombuilder /build/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]这个 Dockerfile 的第一阶段先独立复制pom.xml并执行dependency:go-offline这一步会把项目所有依赖下载到镜像的一个独立层。之后即使源码经常改动只要pom.xml没变构建时就会命中 Docker 的层缓存大幅缩短重复构建时间。第二阶段从运行时基础镜像把 jar 复制过来只保留 JRE最终镜像比直接放一个完整 JDK 小很多。构建命令docker build -t myapp:1.0 .-t是给镜像打标签.指定构建上下文。IDEA 里通过 Docker 插件打包镜像底层同样是执行这条命令。需要注意.dockerignore要提前写好把target/、.git/排除掉避免构建上下文太大。4.3 用 docker compose 编排 MySQL 8.0、Redis 主从和应用多容器环境下逐个docker run很容易漏参数而且容器间的网络访问要手动搭。compose 把服务、网络、卷声明在一份 YAML 里是最常见的团队协作方式。下面这份编排同时包含 MySQL 8.0、Redis 主从和上一节构建的微服务services: mysql: image: mysql:8.0 container_name: demo-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: appdb TZ: Asia/Shanghai ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -proot123] interval: 5s retries: 10 networks: [demo-net] redis-master: image: redis:7 container_name: demo-redis-master command: [redis-server, --requirepass, redis123] volumes: - redis-master-data:/data healthcheck: test: [CMD, redis-cli, -a, redis123, ping] interval: 5s retries: 10 networks: [demo-net] redis-slave: image: redis:7 container_name: demo-redis-slave command: [redis-server, --replicaof, demo-redis-master, 6379, --masterauth, redis123, --requirepass, redis123] depends_on: - redis-master networks: [demo-net] app: build: context: . dockerfile: Dockerfile image: myapp:1.0 ports: - 8080:8080 environment: SPRING_DATASOURCE_URL: jdbc:mysql://demo-mysql:3306/appdb?useUnicodetruecharacterEncodingutf8 SPRING_DATA_REDIS_HOST: demo-redis-master SPRING_DATA_REDIS_PASSWORD: redis123 depends_on: mysql: condition: service_healthy redis-master: condition: service_healthy networks: [demo-net] networks: demo-net: volumes: mysql-data: redis-master-data:在 compose 里service 名称同时是容器在自定义 network 中的 DNS 主机名所以应用连接串里写demo-mysql而不是localhost。MySQL 的MYSQL_DATABASE会在首次初始化时自动创建 appdb但不会在已有数据后重复执行MYSQL_ROOT_PASSWORD只对首次创建的容器生效改密码时需要配合数据卷一起处理。Redis 主从这里用--replicaof demo-redis-master 6379注意从节点要同时配置--masterauth和--requirepass否则主从同步会因为认证失败断掉。depends_on加上condition: service_healthy后应用容器会等 MySQL 和 Redis 的健康检查通过后再启动避免出现“应用启动时连不上数据库就退出”的经典问题。启动和管理命令docker compose up -d docker compose ps docker compose logs -f app docker compose downup -d会构建镜像并后台启动所有 serviceps查看状态logs -f app跟踪应用日志down会停止容器并删除默认网络但不会删除 volumes因此 MySQL 数据还在。一旦写成 YAML整套环境就在团队内可复制这也正是docker compose在开发环境里比脚本更具优势的原因。5. Docker 资源限制、镜像清理与退出码排错技巧5.1 给容器设置 memory 和 cpus避免一台机器被吃满不设资源限制的容器可以消耗宿主机全部内存和 CPU这在开发机和小型服务器上很容易造成雪崩。最常见的做法是在docker run时直接加参数docker run -d --name myapp --memory512m --cpus1 myapp:1.0--memory表示容器可用内存上限--cpus表示容器可用的 CPU 配额1 表示一个核心。compose 文件中可以在 service 级别配置services: app: image: myapp:1.0 mem_limit: 512m cpus: 1.0mem_limit和cpus是兼容字段docker compose up -d会直接透传给容器运行时。deploy.resources.limits是 Docker Stack 的语法在普通docker compose up非 Swarm 模式下并不保证生效不要踩这个坑。docker compose up -d后可以用docker stats实时观察每个容器的 CPU、内存、网络和块设备读写。如果容器因为内存超限被杀状态会显示为exited (137)原因大多是 OOM。用docker inspect -f {{.State.OOMKilled}} myapp可以直接看到是否被 OOM Manager 杀掉这个命令在 MySQL、Redis 这类内存敏感服务的排障中非常实用。内存上限不要设太紧Java 应用还需要额外留出 JVM 堆外的空间一般建议堆大小不超过容器内存的 75%。5.2 用 docker system df 清理悬空镜像与日志容器运行久了磁盘里会堆积悬空镜像、停止的容器和不再使用的网络。清理前先看占用分布docker system df输出按 Images、Containers、Local Volumes、Build Cache 四类列出数量和占用方便决定先清哪里。常规清理命令是docker image prune -a docker container prune docker system prune -a --volumesdocker image prune -a会把没有被任何容器引用的镜像全部删除谨慎使用因为可能误删本地想要留着的旧版本。docker system prune -a --volumes更彻底会连未使用的构建缓存和数据卷一起清空适合在磁盘告警时执行但生产环境执行前最好先docker system df做确认。容器日志是无上限增长的建议运行容器时加上--log-opt max-size10m --log-opt max-file3或者在使用 JSON 文件日志驱动时于daemon.json中全局配置避免单个日志文件膨胀到几 GB。如果日志文件已经很大先停止容器再清理/var/lib/docker/containers/容器完整ID/*.log不要直接在线删除不然日志句柄还在磁盘空间不会立刻释放。5.3 通过 exit code 和 docker inspect 快速定位启动失败最后给一个非常实用的排查技巧容器启动失败时先看退出码。退出码含义优先排查方向0正常退出检查进程预期是否短生命周期1程序内部错误docker logs看应用报错137被 SIGKILL 强杀内存超限或手动 kill139段错误基础镜像不兼容、JNI 问题127命令找不到入口命令路径或 shell 语法错误配合两条命令就能快速判断问题范围docker inspect -f {{.State.ExitCode}} {{.State.OOMKilled}} container_name docker logs --tail 200 container_name如果OOMKilled为true优先调内存限制而不是改代码。如果退出码是 127通常ENTRYPOINT中执行的命令不存在可以用docker run --rm -it image entrypoint sh绕过默认入口进入容器手动执行命令验证路径。切换entrypoint排查镜像环境问题时不要忘记加--rm否则每次都会留下停止的容器。先把 docker 升级到插件版再用journalctl -u docker -n 50配合docker info查看 daemon 状态基本能解决 90% 的启动类问题。本文还有配套的精品资源点击获取