从零跑通 Docker:用一个完整案例搞懂部署、网络与 Compose
Docker 实战案例分享|零基础可学
在现代软件开发中,Docker 已经成为后端开发、运维部署、测试环境搭建以及微服务架构中非常重要的工具。很多初学者第一次接触 Docker 时,会觉得它像是“虚拟机”“服务器”“打包工具”的混合体,概念很多,不知道从哪里开始。
其实,Docker 的核心目标非常简单:把应用和运行环境一起打包,让它可以在任何支持 Docker 的机器上稳定运行。
本文将从零基础角度出发,通过一个完整的实战案例,带你理解 Docker 的基本概念、常用命令、镜像构建、容器运行、数据挂载、网络通信以及 Docker Compose 编排。即使你之前没有使用过 Docker,也可以按照本文一步步完成实践。
一、为什么要学习 Docker?
在没有 Docker 之前,我们部署一个项目通常会遇到很多问题。
比如你开发了一个 Java Web 项目,项目依赖:
- JDK 17
- MySQL 8.0
- Redis 7
- Nginx
- 特定版本的配置文件
- 一些系统环境变量
你在自己的电脑上运行正常,但是部署到测试服务器时,可能会出现:
- JDK 版本不一致
- MySQL 字符集配置不同
- Redis 没有安装
- 系统缺少依赖库
- 配置文件路径不一致
- 本地能跑,服务器不能跑
这就是经典的“环境不一致”问题。
Docker 的出现,就是为了解决这类问题。它可以把应用程序、依赖、配置以及运行环境一起封装成镜像,然后在不同机器上以容器形式运行。
简单来说:
Docker 让应用部署变得更标准、更快速、更可靠。
二、Docker 的核心概念
在正式实战之前,我们先了解几个最基础的概念。
1. 镜像 Image
镜像可以理解为一个“应用运行模板”。
比如:
- MySQL 镜像:包含 MySQL 数据库运行所需的环境
- Redis 镜像:包含 Redis 运行环境
- Nginx 镜像:包含 Nginx 服务
- 自定义项目镜像:包含你的项目代码和运行依赖
镜像本身是静态的,不能直接提供服务,需要通过镜像创建容器。
2. 容器 Container
容器是镜像运行起来之后的实例。
可以这样理解:
- 镜像相当于“类”
- 容器相当于“对象”
同一个镜像可以创建多个容器,每个容器之间相互隔离。
例如,我们可以使用同一个 Redis 镜像启动多个 Redis 容器,只要端口不冲突即可。
3. 仓库 Repository
镜像通常存放在镜像仓库中。
常见镜像仓库包括:
- Docker Hub
- 阿里云镜像仓库
- 腾讯云镜像仓库
- 华为云镜像仓库
- 企业内部私有仓库
我们可以从仓库拉取镜像,也可以将自己构建好的镜像推送到仓库。
4. Dockerfile
Dockerfile 是用于构建镜像的脚本文件。
它里面会描述:
- 使用哪个基础镜像
- 拷贝哪些文件
- 安装哪些依赖
- 暴露哪个端口
- 容器启动时执行什么命令
例如,一个简单的 Node.js 项目 Dockerfile 可能长这样:
FROM node:18-alpine
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
5. Docker Compose
Docker Compose 用来管理多个容器。
在实际项目中,一个系统往往不只有一个服务,可能包含:
- 后端应用
- MySQL
- Redis
- Nginx
- 消息队列
- Elasticsearch
如果每个容器都手动启动,会非常麻烦。Docker Compose 可以通过一个 docker-compose.yml 文件统一管理多个服务,一条命令即可启动整个项目环境。
三、实战目标:部署一个 Web 应用环境
本文实战案例将搭建一个简单但完整的 Docker 项目环境。
我们会部署以下服务:
| 服务 | 作用 | 端口 |
|---|---|---|
| Nginx | 提供 Web 访问入口 | 80 |
| MySQL | 存储业务数据 | 3306 |
| Redis | 缓存服务 | 6379 |
| App | 自定义后端应用 | 8080 |
为了方便零基础学习,我们不绑定具体语言框架。你可以把 App 理解为一个普通后端服务,例如 Java Spring Boot、Node.js、Go 或 Python Flask 应用。
本文会以一个简单的 Spring Boot Jar 包为例讲解,但 Docker 思路对其他语言同样适用。
四、准备工作
1. 安装 Docker
如果你使用的是 Windows 或 macOS,推荐安装:
Docker Desktop
如果你使用的是 Linux,可以通过包管理工具安装 Docker。
以 Ubuntu 为例:
sudo apt update
sudo apt install docker.io -y
启动 Docker:
sudo systemctl start docker
sudo systemctl enable docker
查看 Docker 是否安装成功:
docker version
如果能看到客户端和服务端版本信息,说明安装成功。
2. 安装 Docker Compose
新版 Docker 通常已经内置 Compose 插件,可以使用:
docker compose version
如果能够看到版本号,就说明可以正常使用。
注意,现在官方更推荐使用:
docker compose
而不是旧版本的:
docker-compose
五、先体验一个最简单的容器
在学习复杂案例前,我们先运行一个 Nginx 容器。
docker run -d --name my-nginx -p 8088:80 nginx
这条命令含义如下:
docker run:运行一个容器-d:后台运行--name my-nginx:容器名称叫 my-nginx-p 8088:80:把宿主机 8088 端口映射到容器 80 端口nginx:使用 nginx 镜像
运行后访问:
http://localhost:8088
如果看到 Nginx 欢迎页面,说明容器运行成功。
查看正在运行的容器:
docker ps
停止容器:
docker stop my-nginx
删除容器:
docker rm my-nginx
这个小例子说明:我们不需要在本机手动安装 Nginx,只需要一条 Docker 命令,就可以启动一个完整的 Nginx 服务。
六、构建自己的应用镜像
接下来我们假设已经有一个后端应用,打包后生成了一个文件:
app.jar
项目目录结构如下:
docker-demo/
├── app.jar
├── Dockerfile
└── docker-compose.yml
我们先编写 Dockerfile。
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY app.jar /app/app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
逐行解释:
FROM eclipse-temurin:17-jre
表示使用 JDK 17 运行环境作为基础镜像。因为我们只需要运行 Jar 包,不需要编译源码,所以选择 jre 镜像即可。
WORKDIR /app
设置容器内工作目录为 /app。
COPY app.jar /app/app.jar
将当前目录下的 app.jar 复制到容器的 /app 目录中。
EXPOSE 8080
声明容器内部服务使用 8080 端口。
CMD ["java", "-jar", "app.jar"]
容器启动后执行该命令,运行 Spring Boot 应用。
构建镜像:
docker build -t demo-app:1.0 .
参数说明:
-t demo-app:1.0:指定镜像名称和版本标签.:表示使用当前目录作为构建上下文
查看镜像:
docker images
运行容器:
docker run -d --name demo-app -p 8080:8080 demo-app:1.0
访问:
http://localhost:8080
如果你的应用有接口,例如:
http://localhost:8080/health
可以打开浏览器或使用 curl 访问验证。
七、使用 Docker 部署 MySQL
实际项目通常需要数据库。我们可以直接使用官方 MySQL 镜像。
运行 MySQL 容器:
docker run -d \
--name demo-mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=demo_db \
mysql:8.0
参数说明:
--name demo-mysql:容器名称-p 3306:3306:端口映射-e MYSQL_ROOT_PASSWORD=123456:设置 root 密码-e MYSQL_DATABASE=demo_db:初始化数据库mysql:8.0:使用 MySQL 8.0 镜像
查看日志:
docker logs -f demo-mysql
进入 MySQL 容器:
docker exec -it demo-mysql bash
登录数据库:
mysql -uroot -p
输入密码:
123456
查看数据库:
SHOW DATABASES;
八、数据持久化:为什么需要 Volume?
上面的 MySQL 容器虽然可以运行,但有一个问题:如果容器被删除,数据也可能丢失。
这显然不适合生产环境。
Docker 提供了数据卷 Volume,用来保存容器数据。
我们重新运行 MySQL:
docker run -d \
--name demo-mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=demo_db \
-v mysql_data:/var/lib/mysql \
mysql:8.0
其中:
-v mysql_data:/var/lib/mysql
表示创建一个名为 mysql_data 的数据卷,并挂载到容器内部的 MySQL 数据目录。
查看数据卷:
docker volume ls
查看数据卷详情:
docker volume inspect mysql_data
这样即使容器被删除,只要数据卷还在,数据库数据仍然可以保留。
九、部署 Redis 缓存服务
Redis 也是后端项目中非常常见的组件。
运行 Redis:
docker run -d \
--name demo-redis \
-p 6379:6379 \
redis:7
测试 Redis:
docker exec -it demo-redis redis-cli
执行:
set name docker
get name
如果返回:
docker
说明 Redis 正常运行。
如果希望 Redis 数据持久化,可以使用:
docker run -d \
--name demo-redis \
-p 6379:6379 \
-v redis_data:/data \
redis:7 redis-server --appendonly yes
其中:
redis-server --appendonly yes
表示开启 AOF 持久化。
十、容器之间如何通信?
很多初学者会问:应用容器如何连接 MySQL 和 Redis?
如果是在宿主机上运行应用,可以连接:
localhost:3306
localhost:6379
但如果应用也运行在 Docker 容器中,就不能简单使用 localhost。
因为在容器内部,localhost 指的是容器自己,而不是宿主机,也不是其他容器。
正确做法是创建一个 Docker 网络,让容器在同一个网络中通过容器名访问彼此。
创建网络:
docker network create demo-network
启动 MySQL:
docker run -d \
--name demo-mysql \
--network demo-network \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=demo_db \
-v mysql_data:/var/lib/mysql \
mysql:8.0
启动 Redis:
docker run -d \
--name demo-redis \
--network demo-network \
redis:7
启动 App:
docker run -d \
--name demo-app \
--network demo-network \
-p 8080:8080 \
demo-app:1.0
这时应用连接数据库的地址应该写:
spring:
datasource:
url: jdbc:mysql://demo-mysql:3306/demo_db
username: root
password: 123456
redis:
host: demo-redis
port: 6379
也就是说,在同一个 Docker 网络中,容器名可以直接作为主机名使用。
十一、使用 Docker Compose 一键启动完整环境
手动执行多条 docker run 命令不够方便,而且不利于团队协作。
我们可以使用 Docker Compose 来统一编排。
创建 docker-compose.yml:
services:
mysql:
image: mysql:8.0
container_name: demo-mysql
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: demo_db
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
networks:
- demo-network
redis:
image: redis:7
container_name: demo-redis
command: redis-server --appendonly yes
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- demo-network
app:
build:
context: .
dockerfile: Dockerfile
container_name: demo-app
ports:
- "8080:8080"
depends_on:
- mysql
- redis
networks:
- demo-network
nginx:
image: nginx:latest
container_name: demo-nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
networks:
- demo-network
volumes:
mysql_data:
redis_data:
networks:
demo-network:
这个文件定义了四个服务:
mysqlredisappnginx
并且定义了两个数据卷:
mysql_dataredis_data
还定义了一个网络:
demo-network
启动所有服务:
docker compose up -d
查看服务状态:
docker compose ps
查看日志:
docker compose logs -f
停止服务:
docker compose down
如果想停止并删除数据卷:
docker compose down -v
需要注意,-v 会删除数据卷,数据库数据也会被删除,生产环境慎用。
十二、配置 Nginx 反向代理
在实际部署中,通常不会让用户直接访问后端应用端口,而是通过 Nginx 统一入口访问。
创建 nginx.conf:
events {}
http {
server {
listen 80;
location / {
proxy_pass http://app: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;
}
}
}
这里的关键是:
proxy_pass http://app:8080;
因为 Nginx 和 App 在同一个 Docker 网络中,所以 Nginx 可以通过服务名 app 访问应用容器。
启动后访问:
http://localhost
请求会先进入 Nginx,再由 Nginx 转发到 App 服务。
十三、常用 Docker 命令速查
1. 镜像相关
docker images
查看本地镜像。
docker pull nginx
拉取镜像。
docker rmi nginx
删除镜像。
docker build -t demo-app:1.0 .
构建镜像。
2. 容器相关
docker ps
查看运行中的容器。
docker ps -a
查看所有容器。
docker stop 容器名
停止容器。
docker start 容器名
启动容器。
docker restart 容器名
重启容器。
docker rm 容器名
删除容器。
docker logs -f 容器名
查看容器日志。
docker exec -it 容器名 bash
进入容器。
3. 数据卷相关
docker volume ls
查看数据卷。
docker volume inspect 数据卷名
查看数据卷详情。
docker volume rm 数据卷名
删除数据卷。
4. 网络相关
docker network ls
查看网络。
docker network create demo-network
创建网络。
docker network inspect demo-network
查看网络详情。
十四、实战中常见问题
1. 端口被占用
如果启动容器时报错:
Bind for 0.0.0.0:3306 failed: port is already allocated
说明宿主机的 3306 端口已经被占用。
解决方法:
- 停止占用端口的服务
- 或修改端口映射
例如:
ports:
- "3307:3306"
表示宿主机使用 3307 端口访问容器内部的 3306。
2. 应用连接不上数据库
常见原因有:
- 数据库容器未启动成功
- 数据库账号密码错误
- 应用配置中仍然使用 localhost
- App 和 MySQL 不在同一个 Docker 网络
- MySQL 初始化较慢,App 启动过早
可以先查看 MySQL 日志:
docker logs demo-mysql
再进入 App 容器测试网络:
docker exec -it demo-app sh
如果容器内没有 ping,可以使用其他方式排查,例如查看应用日志。
3. Docker Compose 中 depends_on 不等于服务可用
很多人以为:
depends_on:
- mysql
表示 MySQL 完全准备好了,App 才会启动。
其实 depends_on 只保证容器启动顺序,不保证 MySQL 已经可以接受连接。
更可靠的做法是:
- 应用内部实现连接重试
- 为数据库配置 healthcheck
- 使用启动等待脚本
例如 MySQL healthcheck:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p123456"]
interval: 10s
timeout: 5s
retries: 5
4. 容器删除后数据丢失
如果没有挂载数据卷,容器删除后数据可能无法保留。
因此数据库、Redis、上传文件等需要持久化的数据,建议使用:
- Docker volume
- 宿主机目录挂载
- 云存储或外部数据库服务
十五、生产环境部署建议
Docker 很适合部署项目,但生产环境不能只做到“能跑”,还需要考虑稳定性、安全性和可维护性。
1. 不要把密码写死在配置文件中
示例中的密码 123456 只是为了教学方便。
生产环境建议使用:
- 环境变量
.env文件- 密钥管理系统
- Kubernetes Secret
- 云厂商密钥服务
2. 镜像版本尽量固定
不建议生产环境直接使用:
image: nginx:latest
因为 latest 可能随着时间变化,导致不同时间部署出来的版本不一致。
更推荐:
image: nginx:1.25
或更精确的版本号。
3. 配置日志收集
容器日志可以通过:
docker logs
查看,但生产环境通常需要统一日志平台,例如:
- ELK
- Loki
- Prometheus + Grafana
- 云日志服务
4. 设置资源限制
为了避免某个容器占用过多资源,可以设置 CPU 和内存限制。
例如:
services:
app:
image: demo-app:1.0
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
需要注意,Compose 在不同运行模式下对 deploy 的支持略有差异。
5. 使用健康检查
健康检查可以帮助我们判断容器是否真正可用。
例如应用服务:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
十六、完整目录结构参考
最终项目结构可以整理为:
docker-demo/
├── app.jar
├── Dockerfile
├── docker-compose.yml
└── nginx.conf
启动项目:
docker compose up -d
查看状态:
docker compose ps
查看日志:
docker compose logs -f
访问服务:
http://localhost
停止项目:
docker compose down
十七、学习 Docker 的建议路线
如果你是零基础,可以按照以下路线学习:
- 理解镜像、容器、仓库的概念
- 学会使用
docker run启动常见服务 - 学会查看日志、进入容器、停止和删除容器
- 学会使用数据卷保存数据
- 学会创建 Docker 网络,实现容器通信
- 学会编写 Dockerfile 构建自己的镜像
- 学会使用 Docker Compose 编排多服务项目
- 学习镜像优化、安全配置和生产部署实践
- 了解 Kubernetes 等容器编排平台
不要一开始就追求所有概念都懂。Docker 最适合边用边学,只要你能通过实际案例跑起来,就会逐渐理解它的设计思想。
十八、总结
本文从零基础角度介绍了 Docker 的基本概念,并通过一个完整实战案例演示了如何部署包含 App、MySQL、Redis 和 Nginx 的项目环境。
通过本文,你应该已经掌握了:
- Docker 镜像和容器的区别
- 如何运行 Nginx、MySQL、Redis 容器
- 如何编写 Dockerfile 构建应用镜像
- 如何使用数据卷实现持久化
- 如何通过 Docker 网络实现容器通信
- 如何使用 Docker Compose 一键启动多服务环境
- 如何排查端口占用、连接失败、数据丢失等常见问题
Docker 的价值不只是“把项目跑起来”,更重要的是让软件交付过程变得标准化。无论是个人学习、团队开发,还是生产部署,Docker 都能显著降低环境配置成本,提高部署效率。
如果你刚开始学习 Docker,不必被复杂的术语吓到。记住一句话:
镜像负责打包环境,容器负责运行服务,Compose 负责管理多个容器。
只要掌握这条主线,再结合实际项目不断练习,你就能很快把 Docker 应用到真实开发和部署工作中。