上一篇 下一篇 分享链接 返回 返回顶部

从零跑通 Docker:用一个完整案例搞懂部署、网络与 Compose

发布人:慈云数据-客服中心 发布时间:5小时前 阅读量:1

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:

这个文件定义了四个服务:

  • mysql
  • redis
  • app
  • nginx

并且定义了两个数据卷:

  • mysql_data
  • redis_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 的建议路线

如果你是零基础,可以按照以下路线学习:

  1. 理解镜像、容器、仓库的概念
  2. 学会使用 docker run 启动常见服务
  3. 学会查看日志、进入容器、停止和删除容器
  4. 学会使用数据卷保存数据
  5. 学会创建 Docker 网络,实现容器通信
  6. 学会编写 Dockerfile 构建自己的镜像
  7. 学会使用 Docker Compose 编排多服务项目
  8. 学习镜像优化、安全配置和生产部署实践
  9. 了解 Kubernetes 等容器编排平台

不要一开始就追求所有概念都懂。Docker 最适合边用边学,只要你能通过实际案例跑起来,就会逐渐理解它的设计思想。


十八、总结

本文从零基础角度介绍了 Docker 的基本概念,并通过一个完整实战案例演示了如何部署包含 App、MySQL、Redis 和 Nginx 的项目环境。

通过本文,你应该已经掌握了:

  • Docker 镜像和容器的区别
  • 如何运行 Nginx、MySQL、Redis 容器
  • 如何编写 Dockerfile 构建应用镜像
  • 如何使用数据卷实现持久化
  • 如何通过 Docker 网络实现容器通信
  • 如何使用 Docker Compose 一键启动多服务环境
  • 如何排查端口占用、连接失败、数据丢失等常见问题

Docker 的价值不只是“把项目跑起来”,更重要的是让软件交付过程变得标准化。无论是个人学习、团队开发,还是生产部署,Docker 都能显著降低环境配置成本,提高部署效率。

如果你刚开始学习 Docker,不必被复杂的术语吓到。记住一句话:

镜像负责打包环境,容器负责运行服务,Compose 负责管理多个容器。

只要掌握这条主线,再结合实际项目不断练习,你就能很快把 Docker 应用到真实开发和部署工作中。

目录结构
全文