Docker入门到实战:镜像容器、数据卷、Compose编排全栈项目详解

Docker容器化镜像
于 2026-10-01 05:11:45 修改
·本内容遵循CC 4.0 BY-SA版权协议

先把话说在前头:Docker 这东西,我最早看教程时也是一头雾水,什么镜像、容器、仓库,听着全是概念。直到有人跟我说了一句“你就把镜像当成一张光盘,容器就是你拿光盘放出来的 DVD 播放器里的画面”,我一下就通了。后来我在 Windows、Linux 上装了一遍,拿 MySQL、Redis 这些中间件练手,再把一个正经的前后端全栈项目塞进容器里跑起来,踩了一路的坑。这篇就按我自己从光盘到实战的路径来写,把 Docker 的安装、核心概念、镜像加速、常见中间件部署,以及全栈项目的 Compose 编排全串一遍,新手可以照着抄,有点基础的也能补上几个平时没人告诉你的细节。

1. 内容整体设计与思路拆解

1.1 为什么是“光盘与 DVD”?这个类比到底准不准

“光盘与 DVD”这个类比,核心解决的是 Docker 最容易劝退新人的概念门槛:镜像(Image)和容器(Container)到底是什么关系。

你可以这样理解:一张光盘(镜像)里刻的是标准化的内容,不管是《泰坦尼克号》还是 Linux 系统,内容本身是只读的。你把它塞进任何一台 DVD 播放器(Docker 引擎),放出来的画面就是一个正在运行的实例(容器)。同一个光盘,你可以在十台播放器上同时放,每一台放出来的内容一模一样,互不干扰。这就是 Docker 最核心的价值——环境一致性。

更深一层,光盘还有一个特点:它是按行业标准生产的,任何 DVD 播放器都能读。Docker 镜像也是这么设计的,它把应用代码、运行时、系统库、配置全部打成标准格式,只要有 Docker 引擎就能跑,不管底层是 Windows、macOS 还是 Linux。这就是容器化与虚拟机最大的区别:虚拟机虚拟的是整台硬件,而容器虚拟的是操作系统层,镜像里包含的是“应用 + 它需要的所有运行环境”,而不是一整套操作系统。

不过类比到这里就有点不够用了。光盘是一次性刻录的,但容器是动态的——你可以进到容器里执行命令、写文件、跑进程。所以更准确的说法是:镜像是一张“母盘”,容器是根据母盘复制出来的“可读写副本”。母盘永远不变,副本随便你怎么折腾,删了再复制一个就是。这个设计带来的好处非常实际:生产环境出问题了,直接把容器删掉,用同一个镜像重新起一个,环境瞬间恢复干净。这在以前虚拟机时代是不可想象的。

1.2 这个方案解决了什么问题,适合谁看

Docker 解决的最痛的问题,就是那句经典的“在我机器上是好的啊”。

传统部署流程是这样的:开发在本地配好环境,代码能跑;测试部署到服务器,结果缺个依赖、版本不对、系统库不一样,跑不起来;运维再上去调半天,好不容易通了,开发又改了一行代码,重新部署,又炸了。这个循环我经历过太多次,尤其是在 Java 项目里,JDK 版本、Maven 依赖、配置文件,任何一个不一致都能让你排查一下午。

容器化之后,开发把环境和代码一起打成镜像,测试和运维拿到的就是同一个“光盘”,上面是什么版本就是什么版本,不存在“你机器和我不一样”的问题。

那这篇文章适合谁看?我总结成三类:

  • 零基础新手:完全没碰过 Docker,但听说过“容器化部署”这个词,想搞明白它到底是怎么回事。
  • 用 Docker 装过一两次软件、但没系统梳理过的人:会 docker run 命令,但不知道为什么数据一删就没了,也不知道 Compose 是干嘛的。
  • 准备把全栈项目容器化的开发者:想把手里的前端、后端、数据库、中间件串起来做一键部署,需要一个完整可参考的模板。

下面所有内容,我都会按这个思路展开,从安装到实战再到排错,每一步都会告诉你“为什么这么做”,而不是只给命令。

2. 核心概念拆解:镜像、容器、数据卷与 Compose

2.1 镜像:只读的“母盘”与分层存储原理

镜像,Docker 里用 Image 表示,本质上是一个只读的模板,里面打包了应用代码、运行时、系统工具和依赖库。你可以用 docker pull 从镜像仓库拉一个现成的,也可以用 Dockerfile 自己构建一个。

镜像的分层存储是很多人忽略但非常重要的细节。一个镜像不是一个大文件,而是由很多只读层(Layer)堆叠组成的。每一层对应 Dockerfile 里的一条指令,比如 FROM、RUN、COPY。这样设计的好处是:多个镜像可以共享底层。

举个例子,你拉了 mysql:8.0 和 wordpress:php8.1 两个镜像,它们底层可能都基于 debian 或 ubuntu,这部分底层是共享的,不会重复占用磁盘。而且你更新镜像时,只有变化的层需要重新下载,没变的层直接复用本地缓存,速度飞快。

查看镜像用 docker images,删除镜像用 docker rmi,想省磁盘用 docker image prune清理悬空镜像(即那些已经没有容器引用的中间层镜像)。我自己的习惯是每过一段时间就执行一次:

BASH
docker image prune -f

这个命令会清理掉所有 dangling 镜像,也就是标签为 <none> 的中间层。第一次跑之前磁盘占用 20G,跑完之后可能只剩 8G,效果非常明显。

2.2 容器:镜像的运行实例,以及它的生命周期

容器是镜像的运行实例。你 docker run 一次,就基于镜像创建一个容器。容器可以被启动、停止、删除,也可以进入它的命令行执行操作。

这里有一个新手常常困惑的点:容器和镜像的区别到底是什么?我做一个表格帮你理清:

对比项 镜像(Image) 容器(Container)
本质 只读模板 镜像的运行实例
是否可写 不可写 可写,但写入内容只在容器生命周期内存在
生命周期 永久存在,除非主动删除 可启动、停止、删除
类比 母盘 放母盘读出来的可操作副本
常见命令 docker images, docker pull, docker rmi docker run, docker ps, docker stop, docker rm

容器生命周期管理有几个最常用的命令:

BASH
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器,包括已停止的
docker start <容器名或ID> # 启动已停止的容器
docker stop <容器名或ID> # 停止容器
docker rm <容器名或ID> # 删除容器(需先停止)
docker logs <容器名或ID> # 查看容器日志
docker exec -it <容器名或ID> /bin/bash # 进入容器命令行

每次 docker run 其实都是“基于镜像新建一个容器”,所以如果你只是改了一下启动参数,不要想着“改”现有容器,直接删了重新 run 是更干净的做法。容器本身是瞬态的、可抛弃的,这恰恰是容器化的哲学。

2.3 数据卷:容器删了数据不丢的关键

这是新手最容易翻车的地方:Docker 容器一旦删除,里面写的文件全部消失。MySQL 数据、Redis 持久化文件、应用上传的图片,全部没。这不是 Docker 设计失误,而是它默认把容器当“无状态”的来处理。

解决这个问题的方式是数据卷(Volume)。数据卷本质上是宿主机上的一个目录,挂载到容器内部,容器写这个目录,实际上是写宿主机,容器删了数据还在。

我个人的经验:所有有状态的服务,启动时第一件事先把数据目录挂出来。MySQL 挂 /var/lib/mysql,Redis 挂持久化目录,应用挂上传目录。不要在容器里存任何重要数据。

数据卷的挂载有两种方式,一是 -v 参数指定宿主机目录和容器目录的映射关系,二是用 Docker Volume 管理。前者直观,后者更规范,我后面实战部分会分别演示。

2.4 Docker Compose:从单容器到多服务编排的桥梁

当你只需要跑一个 MySQL 容器时,一条 docker run 就够了。但当你的全栈项目要同时跑前端、后端、数据库、缓存、消息队列时,手动敲命令会疯掉,而且每次启动还要保证顺序正确、网络互通。

Docker Compose 就是干这个的。它用一个 docker-compose.yml 文件描述整个应用的服务组成,然后一条 docker compose up -d 把全部服务拉起来。

Compose 的核心价值有三个:可重复、可共享、可编排。

  • 可重复:配置文件写好了,团队任何人拿到项目,执行一行命令就能把整套环境启动起来。
  • 可共享:配置文件跟着代码仓库走,新同事入职不用再翻文档配环境。
  • 可编排:依赖关系、网络、数据卷、端口映射都在一个文件里声明,不用手动一个个处理。

我后面全栈项目实战部分会给一个完整的 Compose 文件,包含构建镜像、环境变量、数据卷、网络配置等,照着抄就能用。

3. Docker 安装与启动:三大平台实操与 Windows 虚拟化问题排查

3.1 Windows 安装 Docker Desktop 与虚拟化报错处理

Windows 上装 Docker 走的是 Docker Desktop,这是官方出的带图形界面的客户端。安装包直接去官网 Docker Hub 下载,选对应 Windows 版本即可。安装过程基本上是下一步下一步,但装完之后很多人会遇到一个坎儿:

Docker Desktop failed to start because virtualisation support wasn't detected.

这个报错的意思是 Docker Desktop 检测不到虚拟化支持。你机器可能装了 VMware 或 VirtualBox 的虚拟机,或者 CPU 虚拟化没开,也可能 Windows 的 Hyper-V 组件没启用。我按排查顺序说:

第一,检查 BIOS/固件设置里的虚拟化开关。重启电脑,进 BIOS(不同品牌按键不一样,通常是 DEL 或 F2),找到 Intel Virtualization Technology 或 AMD SVM Mode,确认是 Enabled。现在很多新电脑默认就是开的,但也有品牌机默认关闭的情况,这个得自己看一眼。

第二,确认 Windows 的虚拟化平台组件没被关掉。在“控制面板—程序—启用或关闭 Windows 功能”里,勾选“适用于 Linux 的 Windows 子系统”和“虚拟机平台”两项。Docker Desktop 从较新的版本开始依赖 WSL2,这两项不勾上,Docker 是起不来的。勾选后重启电脑。

第三,如果还不行,在管理员 PowerShell 里手动执行:

POWERSHELL
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

然后重启。这两条命令是强行把 WSL 和虚拟化平台的系统组件启用,比在面板里勾选更彻底。

如果用的是 Windows 10 家庭版,可能会出现没有 Hyper-V 选项的情况。这时候不用慌,Docker Desktop 走 WSL2 后端是不需要完整 Hyper-V 的,只要虚拟化平台和 WSL 正常,就能跑。我之前给一台老 Windows 10 笔记本排查过,就是 BIOS 里虚拟化关了,打开就解决了。

3.2 Linux 安装 Docker Engine 与权限问题

Linux 上装 Docker 就是装 Docker Engine,通过系统的包管理器来装。以 Ubuntu 为例,官方安装方式是添加 Docker 的 apt 源:

BASH
sudo apt update
sudo apt 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 a+r /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
 
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

CentOS 7 之类的老系统,网络热词里也有人提到 “centos7升级docker”,这个需要注意:CentOS 7 默认 yum 源的 docker 版本比较老,要升级的话,先卸载旧版本:

BASH
sudo yum remove docker docker-client docker-common docker-engine

然后添加 Docker 官方 yum 源,再安装 docker-ce。CentOS 7 的内核比较老,Docker 新版本对内核有要求,最好确认内核版本不低于 3.10。实际装完 dockerd 起不来的话,优先查 systemctl status docker 和 /var/log/docker 日志。

装完 Docker 之后,普通用户直接执行 docker 命令通常会报权限错误:

Got permission denied while trying to connect to the Docker daemon socket.

原因是 Docker 守护进程默认监听在 /var/run/docker.sock 这个 socket 上,普通用户没有访问权限。解决办法是把当前用户加入 docker 组:

BASH
sudo usermod -aG docker $USER

然后重新登录(或者 newgrp docker 再刷新会话),就正常了。我不建议图省事直接 sudo chmod 666 /var/run/docker.sock,这会让任何用户都能操作 Docker,等于把服务器最高权限敞开了,非常危险。

3.3 macOS 与“安装到 D 盘”的需求

macOS 上同样用 Docker Desktop,装完就是图形界面的 Docker 守护进程,日常操作跟 Windows 版几乎一样,没什么特别的坑,唯一要注意的是 Apple Silicon 芯片要选对安装包,有专门的 arm64 版本。

Windows 上“docker 安装到 D 盘”这个需求我也见过不少。Docker Desktop 默认把镜像和数据放在 C 盘用户目录下,C 盘空间不足的时候,很多人想把整个 Docker 数据目录挪到 D 盘。操作路径是:

打开 Docker Desktop — Settings — Resources — Advanced — Disk image location,把路径改成 D 盘某个目录,然后 Apply & Restart。改完之后,C 盘上的旧数据不会自动迁移,你可以手动把旧的 %LOCALAPPDATA%\Docker\wsl 目录清理掉,或者先把需要的镜像重新 pull 一遍。

这个方法只适用于 Docker Desktop 的 WSL2 后端,如果是用 Docker Toolbox 那种老方案,数据目录是 VirtualBox 虚拟机磁盘文件,迁移逻辑完全不同,这里不展开。

安装完 Docker,无论哪个平台,先验证一下:

BASH
docker --version
docker run hello-world

能输出版本信息、能跑起来 hello-world 容器并打印提示文本,说明引擎工作正常。接下来就该处理一个每天都躲不开的问题——镜像下载慢。

4. 镜像加速与仓库配置:解决下载慢的常见手段

4.1 为什么新装完的 Docker 下载镜像特别慢

Docker 默认从 Docker Hub 拉取镜像,而 Docker Hub 的服务器在海外,国内网络环境下直连速度极不稳定。拉一个几百 MB 的镜像等十几分钟是常有的事,失败重试也频繁。这不是你网速的问题,是网络链路的问题。

解决思路是给 Docker 配置镜像加速器,也就是国内的一些 Docker 镜像仓库。原理是这些镜像仓库做了 Docker Hub 的同步缓存,你从离你近、链路快的仓库拉,速度会好很多。

不过这里我要说明一点:国内加速源本身也在频繁变动,有的需要注册账号才能用,有的过段时间就关了。所以下面的配置方法我会写清楚原理,具体地址你需要用当前可用的,不要死记某一个。

4.2 配置镜像加速:Linux 的 daemon.json 与 Docker Desktop 的设置

Linux 上配置镜像加速,需要修改 Docker 守护进程配置文件 /etc/docker/daemon.json。文件默认不存在,自己创建即可:

JSON
{
"registry-mirrors": [
"https://docker.m.technicalurl.com",
"https://docker.1panel.live",
"https://dockerproxy.net"
]
}

写完以后执行:

BASH
sudo systemctl daemon-reload
sudo systemctl restart docker

然后可以验证一下配置是否生效:

BASH
docker info | grep -A 5 "Registry Mirrors"

能看到你填的加速地址列表,就说明配置成功了。

Windows 和 macOS 的 Docker Desktop 有图形界面:Settings — Docker Engine,里面会显示一份 JSON 配置,把上面的 registry-mirrors 数组合并进去,点 Apply & Restart 即可。注意直接改界面里的 JSON 就行,不要乱动其他默认字段。

配置完加速器之后,原来十几分钟拉不下来的 mysql:8.0 可能一分钟内就拉完了。如果还是慢,可以试试换几个加速地址,或者避开高峰时段。镜像下载速度这事没有一劳永逸的解法,我在服务器上就放了两三个可用的加速源,哪个通走哪个。

4.3 内网环境的镜像仓库方案

如果你是在内网部署,不能访问外网镜像源,那就需要搭一个私有镜像仓库。最轻量的是用 Docker 官方提供的 registry 镜像,一条命令搞定:

BASH
docker run -d -p 5000:5000 --name registry --restart=always -v /opt/registry:/var/lib/registry registry:2

然后其他机器拉取镜像时,把镜像 tag 改成 内网IP:5000/镜像名:tag 就能从内网仓库拉。全栈项目如果涉及多台服务器,建议搞一个私有仓库,不然每台机器都往公网拉,既慢又不稳定。

5. 实战一:用 Docker 安装 MySQL 8.0 并完成基本使用

5.1 拉取镜像与容器启动参数详解

装 MySQL 8.0 是 Docker 入门最经典、也最有代表性的实战,因为它涉及环境变量、数据卷、端口映射、网络四个最核心的要素。

先拉镜像:

BASH
docker pull mysql:8.0

拉完看一下:

BASH
docker images | grep mysql

启动容器,我给出一个推荐写法,并逐步解释:

BASH
docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=appdb \
-v /data/mysql/data:/var/lib/mysql \
-v /data/mysql/conf:/etc/mysql/conf.d \
--restart=always \
mysql:8.0

逐行解释:

  • -d:后台运行,不占用当前终端。
  • --name mysql8:给容器起个名字,之后操作都用这个名字。
  • -p 3306:3306:端口映射。宿主机 3306 端口映射到容器 3306 端口,外部访问就是宿主机 IP:3306。
  • -e MYSQL_ROOT_PASSWORD=123456:环境变量,告诉 MySQL 镜像初始化时把 root 密码设为 123456。首次启动有效,数据目录已初始化后,这个变量不再生效。
  • -e MYSQL_DATABASE=appdb:额外创建一个名为 appdb 的数据库。这个可以留空,但初创阶段加上很方便。
  • -v /data/mysql/data:/var/lib/mysql:把宿主机 /data/mysql/data 目录挂到容器的 MySQL 数据目录。容器删了,数据都在这。
  • -v /data/mysql/conf:/etc/mysql/conf.d:挂载自定义配置目录,MySQL 容器会自动读取该目录下的 .cnf 配置文件。
  • --restart=always:容器异常退出或 Docker 重启时自动拉起。生产环境必备。

启动之后,检查容器状态:

BASH
docker ps | grep mysql8
docker logs mysql8 | tail -20

第一次启动会有一段初始化输出,看到 “ready for connections” 就说明 MySQL 起来了。

5.2 连接测试与容器外管理 MySQL

容器起来了,接下来验证能不能连。在宿主机上用 MySQL 客户端连(如果没有客户端,先进容器里连也行):

BASH
docker exec -it mysql8 mysql -uroot -p123456

进到 MySQL 里可以执行 show databases; 验证。

如果是从局域网其他机器连,要注意几个问题。一是防火墙,宿主机 3306 端口要放行;二是 MySQL 8.0 默认的认证插件是 caching_sha2_password,老客户端可能连不上,需要用 mysql_native_password 重设密码:

SQL
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

三是 MySQL 8.0 默认 root 用户只允许 localhost 登录,如果你需要远程连 root,需要自己创建用户并授权。我建议不要直接远程用 root,而是创建一个业务专用账号:

SQL
CREATE USER 'appuser'@'%' IDENTIFIED BY 'app_pass';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'%';
FLUSH PRIVILEGES;

启动 MySQL 时你创建了 appdb 数据库,这里再把一个账号授权给它,业务代码就用这个账号连,权限收得越小越安全。

5.3 常见失败场景:启动失败、端口占用与认证问题

启动失败。最常见的原因是自己加的 -v 挂载目录权限不对,MySQL 容器内进程用 mysql 用户跑,对挂载目录没有写权限,初始化就失败。排查方式是:

BASH
chmod -R 777 /data/mysql

然后删掉容器重来。生产环境不要用 777,更适合的做法是 chown -R 999:999 /data/mysql,因为 MySQL 镜像内部 uid 是 999。权限问题不解决,你怎么启动都是失败,还会在日志里看到 Permission denied 之类的字样。

端口占用。宿主机上已经有 MySQL 在跑,3306 被占了。这种情况 docker logs 会显示端口绑定失败,把 -p 3306:3306 改成 -p 3307:3306,外部访问就用 3307。你本机没装 MySQL 但端口被占,用 netstat -tlnp | grep 3306 查是谁。

镜像里 MySQL 容器初始化时间很长。有些机器磁盘慢,初始化可能要两三分钟,不要一看到日志没输出就以为失败了。也有可能是内存不足,MySQL 8.0 启动内存要求不低,在低配机器上建议给容器加个内存限制,比如 -m 1g,或者调低 innodb_buffer_pool_size。

6. 实战二:用 Docker 部署 Redis 主从与网络隔离

6.1 Redis 主从的容器化设计思路

Redis 主从复制是经典的高可用方案,主节点负责写,从节点负责读,数据实时同步。用 Docker 部署的时候就涉及一个新问题:多个容器之间的通信。

默认情况下,容器和容器之间可以通过 Docker 内部网络互通,但是 IP 是不固定的,每次重建容器 IP 都会变。这显然不能用于服务发现,所以 Docker 提供了自定义网络(Custom Network)的功能。在同一个自定义网络里的容器,可以通过容器名互相访问,不需要关心 IP 变化。

Redis 主从部署的时候,从节点配置里需要指定主节点的地址,这时候直接用容器名解析即可。如果不做自定义网络,你得去查容器 IP,写死进去,容器一重建就失效,这个坑我替你先踩了。

6.2 创建网络并启动主从容器

先创建一个自定义网络:

BASH
docker network create redis-net

启动 Redis 主节点:

BASH
docker run -d \
--name redis-master \
--network redis-net \
-p 6379:6379 \
-v /data/redis/master:/data \
redis:7.0 redis-server --appendonly yes

redis:7.0 后面的部分是覆盖容器默认启动命令,用 redis-server 启动并开启 AOF 持久化。--appendonly yes 让 Redis 每次写操作都追加到持久化文件里,容器重启数据不丢。

再启动从节点:

BASH
docker run -d \
--name redis-slave \
--network redis-net \
-p 6380:6379 \
-v /data/redis/slave:/data \
redis:7.0 redis-server --appendonly yes --slaveof redis-master 6379

关键参数是 --slaveof redis-master 6379,它告诉从节点“你的主节点在 redis-master 这个地址的 6379 端口”。因为两个容器在同一个自定义网络里,这里直接写容器名就行。

从节点端口映射我用的是 6380:6379,目的是不跟主节点的 6379 冲突。你不映射 6380 也可以,因为从节点通常不需要外部直接访问,内网通信走的是 Docker 网络,不经过端口映射。

6.3 验证主从复制是否生效

启动完成后,验证一下主从状态。进入主节点容器写入数据:

BASH
docker exec -it redis-master redis-cli set test_key hello

然后进从节点查:

BASH
docker exec -it redis-slave redis-cli get test_key

能返回 hello,说明从节点已经同步了主节点的数据。再查看复制信息:

BASH
docker exec -it redis-slave redis-cli info replication

输出里的 master_link_status 字段如果是 up,就说明主从连接正常。

这里有个细节值得说:从节点默认是只读的,你往从节点写数据会报错,这是对的。Redis 主从本身就是一主多从、读写分离的架构,想要高可用还需要引入哨兵(Sentinel)集群,但那个复杂度就上去了。作为容器化入门,能把主从复制跑通就足够理解 Docker 网络和数据卷的精髓了。

6.4 数据持久化的选择:RDB vs AOF

用 Docker 跑 Redis,数据持久化文件放哪里、用哪种方式,也是要注意的。

Redis 持久化有两种方式。RDB 是定期全量快照,恢复快但是可能丢最近几分钟的数据;AOF 是追加写日志,最多丢 1 秒的数据(取决于 appendfsync 策略),恢复慢一点但数据更安全。我建议数据比较重要的场景开 AOF,不重要的可以只开 RDB 甚至都关掉,当纯缓存用。

我在命令里用了 --appendonly yes 开启 AOF,持久化文件就在宿主机 /data/redis/master 和 /data/redis/slave 目录下。容器删了重建,只要挂载目录还在,数据就还在。

有一点要注意,容器里的 Redis 默认跑在 /data 目录做持久化,你挂载宿主机目录的时候也要对应挂 /data,不要挂错成别的路径。我之前就是挂到 /home,Redis 写文件时提示权限异常,折腾了十几分钟才反应过来。

7. 全栈项目容器化实战:用 Docker Compose 编排完整应用

7.1 全栈项目为什么需要 Compose

全栈项目跟单个中间件最大的区别在于组件多。一个典型的中小型全栈项目,通常包含:

  • 前端:Nginx 托管静态资源,反向代理 API
  • 后端:Spring Boot 或 Node.js 应用,提供 REST API
  • 数据库:MySQL
  • 缓存:Redis
  • 消息队列:RabbitMQ(可选)

如果一个个手动 docker run,你需要记住每个容器的端口、网络、数据卷、环境变量,还要保证启动顺序:先数据库,再后端,最后前端。一旦中间某个容器重建,IP 还会变,连锁问题一堆。

Docker Compose 把这一切固化为代码。你写一个 docker-compose.yml,把每个服务的信息描述清楚,然后在项目根目录执行一条命令,整条链路全部起来。

7.2 一个可以直接抄的 docker-compose.yml 模板

我以一个经典的 “Nginx + Node.js + MySQL + Redis” 全栈结构为例,给出一份 Compose 文件。这份文件我自己在项目里改一改就能用,读者拿到手改几个环境变量即可。

YAML
version: "3.8"
 
services:
mysql:
image: mysql:8.0
container_name: app-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: webapp
MYSQL_USER: webapp
MYSQL_PASSWORD: webapp123
volumes:
- ./data/mysql:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d
ports:
- "3306:3306"
networks:
- app-network
 
redis:
image: redis:7.0
container_name: app-redis
restart: always
command: redis-server --appendonly yes
volumes:
- ./data/redis:/data
ports:
- "6379:6379"
networks:
- app-network
 
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: app-backend
restart: always
depends_on:
- mysql
- redis
environment:
DB_HOST: mysql
DB_PORT: 3306
DB_NAME: webapp
DB_USER: webapp
DB_PASSWORD: webapp123
REDIS_HOST: redis
REDIS_PORT: 6379
ports:
- "8080:8080"
networks:
- app-network
 
frontend:
image: nginx:1.24
container_name: app-frontend
restart: always
depends_on:
- backend
volumes:
- ./frontend/dist:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "80:80"
networks:
- app-network
 
networks:
app-network:
driver: bridge

这个文件里值得拆开讲的细节有几个。

depends_on 控制启动顺序,但它只保证“先启动依赖服务”,不保证“依赖服务已经就绪”。比如 MySQL 容器启动了,但初始化还没完成,后端直接连数据库就会失败。解决办法是在应用代码里加数据库连接重试逻辑,或者用健康检查,这是初学 Compose 最容易忽略的点。

build.context 和 dockerfile 表示该服务不是从远程镜像直接跑,而是根据当前目录下的 Dockerfile 构建镜像。后端代码写在 ./backend 目录,编写 Dockerfile 把项目打包成镜像。这样整个团队拿到 Compose 文件后,执行 docker compose up --build 就能跑起来,不用手动先 build 镜像。

./init:/docker-entrypoint-initdb.d 这个挂载是 MySQL 镜像的特性:它在首次初始化数据库时,会自动执行挂载目录里的 .sql 脚本。我习惯把建表语句放在这个目录下,这样新环境第一次启动,数据库表结构就自动创建好了。

7.3 后端 Dockerfile 的写法与多阶段构建

后端如果是 Node.js 项目,Dockerfile 可以这么写:

DOCKERFILE
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
 
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 8080
CMD ["node", "dist/index.js"]

这里用到了多阶段构建(Multi-stage Build)。第一阶段用完整 node 环境安装依赖并构建产物,第二阶段只复制构建产物和运行时依赖,镜像体积会小很多。一个包含 devDependencies 的镜像可能 1G 多,多阶段构建后能压到 300M 以内。

Java 后端的 Dockerfile 大体类似,常见的是基于 Maven 构建,再用 JRE 运行:

DOCKERFILE
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
 
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

注意 COPY pom.xml . 先执行 mvn dependency:go-offline,这一步是把依赖先拉下来打缓存,之后源码变动重建时不会重复下载依赖,构建速度快很多。这是一个很常用的 Docker 构建优化技巧。

7.4 前端 Nginx 配置与反向代理

前端在容器里托管静态文件后,还要处理 API 请求,否则浏览器访问页面时,接口调用因为跨域问题就断了。

我的做法是用 Nginx 做反向代理,把 /api 开头的请求转发到后端服务。因为前端容器和后端容器在同一个 Compose 网络里,Nginx 配置里可以直接写后端容器名:

NGINX
server {
listen 80;
server_name localhost;
 
root /usr/share/nginx/html;
index index.html;
 
location / {
try_files $uri $uri/ /index.html;
}
 
location /api/ {
proxy_pass http://backend:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

这段配置放在 nginx.conf 文件里,通过 Compose 文件挂载到了容器内 /etc/nginx/conf.d/default.conf。proxy_pass http://backend:8080/ 里的 backend 就是 Compose 里后端服务的容器名,Docker 内部的 DNS 会自动解析到它的 IP。

try_files $uri $uri/ /index.html; 是前端路由必需的,否则刷新页面时 Nginx 会去找真实的文件路径,找不到就 404,而你的 Vue 或 React 应用的路由需要始终回到 index.html 由前端路由接管。

整体起来后,访问 http://服务器IP 就能看到前端页面,接口请求自动转发到后端,后端连接 MySQL 和 Redis,一个完整的全栈应用就在一台机器上全部容器化跑起来了。

7.5 Compose 常用运维命令速查

Compose 日常使用频率最高的几个命令,我放在这里:

BASH
docker compose up -d # 启动所有服务(后台)
docker compose up -d --build # 改动代码后重新构建镜像并启动
docker compose down # 停止并删除所有容器(数据卷默认保留)
docker compose down -v # 停止并删除容器、网络和数据卷(慎用)
docker compose logs -f backend # 查看某个服务的实时日志
docker compose ps # 查看所有服务状态
docker compose exec backend sh # 进入某个容器的命令行

用 down -v 要极度谨慎,-v 会把数据卷一起删掉,MySQL 和 Redis 的数据全没。我在测试环境用过一次,当场数据清空,幸好是测试库。

8. 常见问题排查与避坑指南

8.1 高频报错与排查方法速查表

我把实际使用中遇到的高频问题整理成一张表,按出现频率排序,基本涵盖了大家搜索量最高的那几类问题:

现象 可能原因 排查与解决
Docker Desktop 启动失败,提示虚拟化不支持 BIOS 虚拟化未开启 / Windows 功能组件缺失 开 BIOS 虚拟化,启用 WSL 和虚拟机平台,必要时用 dism 命令强制启用
docker 服务启动失败,dockerd 报错 配置错误 / 内核版本过低 systemctl status docker 看具体错误,核对 daemon.json 语法
镜像下载慢或超时 默认连 Docker Hub 慢 配置国内镜像加速,换可用加速地址
普通用户执行 docker 命令权限不足 用户不在 docker 组 sudo usermod -aG docker $USER 后重新登录
容器网络不通,容器间无法互相访问 不在同一个自定义网络 创建自定义网络并让相关容器加入同一网络
MySQL 容器启动后立刻退出 挂载目录权限 / 环境变量不完整 查看 docker logs mysql8,修复目录权限,确认初始化参数
容器删掉后数据丢失 没有挂载数据卷 启动时加 -v 挂载宿主机目录到容器数据目录
docker compose 服务启动顺序不对 只依赖 depends_on 应用层加重试逻辑,或配置健康检查
端口被占用导致容器启动失败 宿主机端口已被监听 netstat -tlnp 查端口占用,换映射端口
Nginx 刷新页面 404 前端为 SPA 路由,Nginx 没有回退 配置 try_files $uri $uri/ /index.html;

8.2 容器网络不通的排查思路

“docker 网络不通”是热词里出现频率极高的一个问题。我讲一个最典型的场景:你写了一个应用容器想连本机另一个容器,发现连不上。

排查的顺序是这样的:

第一步,确认容器在同一条网络。运行 docker inspect <容器名> | grep -A 100 "Networks",看看两个容器的网络归属。如果不一样,把它们加入同一个自定义网络,或者用容器 IP 确认能 ping 通。

第二步,确认你用的连接地址是正确的。在自定义网络里,要用容器名而不是 localhost,因为在容器里 localhost 指的是容器自己,不是宿主机,更不是其他容器。

第三步,确认目标服务的监听地址。很多应用默认监听 127.0.0.1,比如 MySQL 在容器里如果只配置了 bind-address 为 127.0.0.1,其他容器是访问不到的,要改成 0.0.0.0 或在启动命令中指定绑定所有地址。

最后,进入容器做连通性测试:

BASH
docker exec -it app-backend ping redis
docker exec -it app-backend curl http://backend:8080/health

能通,说明网络没问题;不能通,按上述步骤逐层排查。

8.3 镜像下载和构建的几个实用技巧

镜像下载慢的问题前面讲过了,补充几个实用技巧。

一是给镜像打标签时注意仓库地址。如果你从 docker.m.technicalurl.com/library/mysql:8.0 拉了一个镜像,它的全名就是这个,直接 docker run mysql:8.0 会找不到,因为 Docker 会去默认仓库找。要么用全名跑,要么 docker tag 重新打个短标签。

二是构建镜像时注意缓存失效问题。Dockerfile 里 COPY 的文件如果频繁变动,缓存容易失效。把依赖安装步骤放在 COPY 源码之前,能最大限度利用缓存。这就是我在 Node 和 Java Dockerfile 里先复制 package.json / pom.xml 再安装依赖的原因。

三是清理无用资源。容器、镜像、数据卷、网络会不断积累,磁盘空间不知不觉就满了。一套清理命令:

BASH
docker system df # 查看占用的磁盘空间分布
docker container prune -f # 删除所有已停止的容器
docker image prune -f # 删除悬空镜像
docker volume prune -f # 删除未被任何容器引用的数据卷
docker system prune -a -f # 全量清理,慎用

docker system prune -a 会把所有未运行的容器、未使用的镜像全部删掉,如果之后还要用,就得重新拉取,所以慎用。

8.4 内存与磁盘吃紧时的容器资源限制

服务器配置不高的话,多个容器一起跑很容易把内存占满。Docker 允许在启动时限制容器资源:

BASH
docker run -d --name mysql8 -m 1g --memory-swap 1g mysql:8.0

-m 1g 限制内存上限 1G,--memory-swap 1g 表示不额外使用 swap,防止容器把内存打到宿主机爆掉。Compose 文件里对应的写法是:

YAML
services:
mysql:
image: mysql:8.0
mem_limit: 1g

N100 这种低功耗小主机跑 20 个容器,核心就是严格控制每个容器的资源占用。镜像尽量选 alpine 版本,体积小、内存占用低,启动也快。一个 node:18-alpine 镜像 70M 左右,而完整版 node 镜像要 350M 以上。

9. 写在最后的实际操作体会

Docker 学了和用了是两回事。我在实际项目里最大的收获,是它彻底改变了“部署”这个动作的体验:以前部署一个全栈项目,要先装 MySQL、配 Redis、部署 Nginx、拉代码、装依赖、改配置,一套下来至少折腾大半天;现在写一个 Compose 文件,新服务器上一条 docker compose up -d,十分钟之内整套环境就绪。

最后再分享几个我踩过之后特别想告诉你的点。

第一,数据卷能挂就挂。我刚开始用 Docker 时图省事,MySQL 数据直接放容器里,后来电脑重启容器异常,数据全丢,当时心态直接炸了。从那次以后,任何有状态的服务,启动参数里第一件事就是挂数据卷。

第二,容器名和网络规划要提前想。项目一多,容器名如果乱起,后面维护的时候根本分不清谁是谁。我现在的习惯是 项目名-服务名 的格式,比如 blog-mysql、blog-backend。容器之间通信一律用自定义网络加容器名,不用 IP,省得容器重建后 IP 变了抓瞎。

第三,日志才是排错的第一入口。任何容器启动失败、连接失败,先跑 docker logs <容器名>,它给你的信息往往比搜索引擎准确得多。启动失败不等于 Docker 坏了,大多数时候是你自己的参数配错了。

这篇从光盘类比讲起,把 Docker 的核心概念、安装、镜像加速、中间件部署、全栈 Compose 编排到常见问题排查全部串了一遍。东西不少,但只要跟着实操一遍,你就会发现容器化这件事,真的没有想象中那么难。

Docker 系列之 docker-compose 容器编排详解
本文围绕Docker-compose展开,介绍其是用于定义和运行多容器应用的工具,可对容器集群快速编排。阐述了安装与卸载方法,列举常用命令及选项,如docker-compose up、ps等。还讲解模板文件,包含image、build等配置项,并给出编写示例及服务访问方式,能助力快速部署分布式应用。
Microi风闲
4051
Docker最全总结,DockerFile,Docker编排容器,Docker镜像,Docker-compose构建
本文详细介绍了 Docker 的核心概念和技术,包括 Docker 的安装、使用、镜像管理、容器操作、Docker Compose 编排等内容。文章还深入探讨了 Dockerfile 的编写技巧、Docker 数据卷的使用方法以及如何构建复杂的服务集群。
kkk三石
3685
docker - 镜像、容器、网络、数据卷、compose、自制镜像
本文详细介绍了Docker的基本使用方法,包括镜像、容器、网络、数据卷等核心命令的应用,并通过实例展示了如何利用docker-compose进行容器集群的快速编排。
程序员笑笑
940
Docker Compose 从入门到实战:Java 开发者必须掌握的容器编排利器
本文系统介绍Docker Compose的核心概念、常用指令及在Java微服务中的应用,涵盖服务编排、网络配置、数据卷挂载等关键技术点,并通过WordPress部署实战演示完整流程,帮助开发者提升容器化应用管理能力。
爱吃烤鸡翅的酸菜鱼
2131
Docker 从入门到实践系列四 - Docker 容器编排利器 Docker Compose
Docker Compose是Docker官方的容器编排工具,用于管理多容器的Docker应用。它通过docker-compose.yml文件定义和运行多个容器服务。本文详细介绍了Compose的安装、卸载、文件配置(如服务、网络、数据卷)以及常用命令,包括启动、停止、日志查看等。此外,还提供了一个Nginx服务的配置案例,展示了如何通过Compose启动和管理容器。
RyanJohnson
3805
Docker Compose 从入门到精通:多容器应用编排实战指南
本文介绍了Docker Compose这一多容器编排工具,阐述其解决多容器管理痛点的作用。涵盖核心概念、全平台安装方法,通过部署Nginx+Node.js+MySQL服务栈的实战案例快速入门。还深入解析配置文件,给出常用命令速查表和最佳实践,能提升开发效率与环境一致性。
1315
Docker多容器编排:Compose 实战教程——从入门到精通
本文介绍了 Docker Compose 在多容器编排中的实际应用,涵盖了关键概念、YAML 文件编写、服务依赖管理、开发与测试环境的应用场景及详细代码案例。文章还探讨了 Docker Compose 的未来发展,包括更强的编排能力和与其他工具的集成。
数智顾问
731
Docker入门到实战:镜像、容器、数据卷与Compose编排全攻略
本文面向初中级技术人员,系统讲解Docker核心概念与实战技能。重点涵盖镜像与容器的本质区别、Windows/Linux环境安装与配置、高频命令组合拳、镜像加速配置、数据卷持久化(命名卷与bind mount)、端口映射与自定义网络通信、Docker Compose多服务编排(MySQL 8.0、Redis主从、kodbox网盘),以及日志与inspect排障方法。强调容器临时性、数据分离原则和生产环境最佳实践。
weixin_34414650
388
深入浅出容器化技术:Docker镜像构建与Docker-Compose编排实战
本文聚焦容器化技术,深入探讨Docker镜像构建和Docker - Compose编排。介绍了Docker核心概念,阐述镜像构建最佳实践、进阶技巧及优化方法,讲解Compose编排艺术与微服务编排示例,还给出生产环境最佳实践和常见问题解决方案,助开发者掌握容器化关键技能。
闯的朋友圈
857
【Docker】核心概念解析:镜像、容器、数据卷、Dockerfile、Compose 的关系与区别
本文以面向对象编程为类比,深入解析Docker的五大核心概念:Dockerfile(类定义)、镜像(只读模板)、容器(运行实例)、数据卷(持久化存储)和Docker Compose(多服务编排)。阐述其定义、作用及相互关系,强调镜像分层、容器隔离、数据卷持久化、Dockerfile可复现构建及Compose声明式编排等关键技术特性。
谢却
421
Docker从入门到实践:镜像、容器、数据卷与Compose编排全解析
本文系统讲解Docker镜像管理、容器生命周期控制、容器交互排障、网络与数据卷持久化、Compose多容器编排等关键技术。涵盖镜像拉取构建、容器启停日志查看、端口映射与网络排查、命名卷与绑定挂载、docker-compose.yml编写与服务编排,并包含权限配置、资源清理及常见故障速查等生产实践要点。
蝨孨槑黽
282
Docker Compose 详解与实战:从入门到精通的容器编排技术
本文详细介绍 Docker Compose,它是用于定义和运行多容器 Docker 应用的工具,可简化部署流程。文中阐述了其核心概念、常用命令、配置方法,还通过 Node.js + MySQL 应用部署案例展示实战应用,支持多环境配置,是 DevOps 流程重要部分,能提升容器化应用开发效率。
风偷走了蒲公
1047
Docker从入门到实战:镜像、容器、Compose编排与排错
本文系统讲解Docker核心概念(镜像、容器、仓库)、跨平台安装配置、高频命令操作、Dockerfile镜像构建(含多阶段优化)、Docker Compose多容器编排(MySQL+Redis主从、微服务实践),以及典型排错技巧。内容聚焦实际开发部署场景,覆盖环境准备、数据持久化、日志监控、权限管理与资源清理等关键技术点。
weixin_34327761
333
Docker入门必备:镜像、容器、数据卷与Compose编排实战解析
本文系统讲解Docker四大核心要素:镜像(只读模板,含运行环境)、容器(镜像的动态实例)、数据卷(实现数据持久化)和Compose(多容器编排)。通过MySQL 8.0、Redis主从等真实案例,详解安装配置、镜像加速、端口映射、挂载策略、日志排查、容器网络及安全实践,强调状态管理主线,夯实容器化技术基础。
赶稿某张
302
Docker从入门到实战:容器、镜像、数据卷与Compose编排全解析
本文系统讲解Docker容器化技术的核心概念(镜像、容器、仓库)、环境安装(Windows/Linux+镜像加速)、数据持久化(数据卷)、网络通信及多容器编排(Docker Compose)。涵盖高频故障排查(启动失败、拉取慢、网络不通)和进阶实践(Dockerfile构建、微服务部署、现成镜像应用),强调环境一致性、可复现性与生产避坑经验,面向开发与运维人员提供全链路实操指导。
SeigRobotics
342
Docker 从入门到实践:镜像、容器、数据卷与 Docker Compose
本文系统讲解Docker三大核心要素:镜像管理(拉取、构建、推送)、容器生命周期操作(运行、启停、日志、进入)、数据卷持久化机制,以及Docker Compose多容器编排。涵盖Dockerfile语法、Volume挂载、镜像标签与Docker Hub交互等关键技术点,面向运维与开发人员提供可落地的容器化实践指南。
青禾837
224
Docker零基础入门:镜像、容器、数据卷与Compose编排实战
本文面向零基础读者,系统讲解Docker核心概念与实操技能:镜像、容器、数据卷的原理与使用;Windows/Linux/macOS安装配置;Nginx、MySQL 8.0、Redis主从的容器化部署;Dockerfile构建与多阶段瘦身;Docker Compose项目级编排;以及常见排错方法(虚拟化支持、权限、端口冲突、卷权限、时区等)。强调环境一致性、快速交付与资源隔离三大真实价值。
hyaliney
317
IDEA中使用Docker Compose容器编排的实现
"这篇文章主要讲解了如何在IntelliJ IDEA(IDEA)中利用Docker Compose进行容器编排,包括连接Docker、打包镜像、编写Docker Compose配置文件以及使用Doc
weixin_38748580
1230
Docker入门教程+Docker实战项目
"该资源包括一个Docker的入门教程和一个Docker实战项目,旨在帮助初学者和进阶开发者掌握Docker技术。教程涵盖了Docker的基础知识,如安装、基本命令、镜像与容器管理、网络配置和数据卷
技术宅program
771
Docker数据卷和容器编排的使用
此外,还有第三方工具如Kubernetes和Docker Compose可以用于容器编排。通过这些工具,我们可以实现容器的快速部署、扩缩容、负载均衡、故障恢复等高级功能。
weixin_38508549
56
Docker系列之使用Docker Compose编排容器
"Docker Compose 是 Docker 容器编排的工具,用于定义和运行多容器的应用,使得可以通过一条命令启动多个容器。"在 Docker 的世界中,Docker Compose 提供了一
weixin_38682254
291
Docker数据卷容器创建及使用方法解析
- Docker容器编排,例如使用Docker Compose或Kubernetes,可简化多容器应用的部署和管理。
weixin_38519060
371
使用Docker Compose进行容器编排
# 1. 引言Docker Compose是一款用于定义和运行多容器Docker应用程序的工具。通过一个单文件的配置,我们可以轻松地定义应用程序的各个服务、网络和数据卷等方面,然后使用一个命令即可启动整个应用程序。## 1.1 什么是Docker ComposeDocker Compose允许用户通过一个简单的YAML配置文件定义一个应用的服务、网络和数据卷。使用docker-compose命令,用户可以方便地启动、停止和管理整个应用程序,而无需手动操作多个Docker容器。## 1.2 为什么需要容器编排在现代应用程序中,通常需要多个容器一起协同工作,比如Web应用程序需
Davider_Wu
Docker 容器编排利器Compose(起步篇)
"本文主要介绍Docker容器编排工具Compose的基础知识,包括Compose的作用、简介、安装方法,并简述了其在多容器应用管理中的重要性。"在Docker的世界中,随着微服务架构的普及,单
weixin_38696582
259
docker compose容器编排实战
本文通过创建一个Python Web应用程序实例,详细介绍了如何使用Docker Compose进行多容器应用的编排。内容包括项目结构的创建、依赖项的定义、编写docker-compose.yml配置文件以及如何构建和运行容器。
懒大王928
详解Docker之Compose服务编排
Docker Compose是Docker生态系统中的关键组件,它是一种用于管理和部署多容器应用程序的服务编排工具。Compose的设计初衷是为了简化在本地开发环境中管理和协调多个Docker容器的工
weixin_38580959
128