目录

前言

Docker的核心设计思想

一、Docker

三大概念

核心原理

有什么用

操作命令

1. 镜像的操作

2. 容器的操作

二、数据卷(容器数据持久化)

是什么

有什么用

操作命令

三、Dockerfile (放在微服务项目)

是什么

操作命令

企业级进阶 Dockerfile

四、从镜像到容器

1. 直接在服务器生成

2. 将本地镜像上传到服务器

五、docker-compose.yml (放在服务器)


前言

新手学习开发、项目部署时,几乎100%都会遇到一个无解的难题:项目在我电脑上完美运行,换一台电脑、发给同事、部署到服务器就直接报错

很多人第一反应会疑惑:这到底是为什么?单纯是软件版本不匹配吗?

其实不只是版本问题,本质是每一台电脑的运行环境都是独一无二的

  • 本机和他人电脑系统不同(Windows / Mac / Linux)

  • 编程语言版本不一致(Python3.9 / Python3.10、JDK8 / JDK17)

  • 项目依赖库缺失、版本冲突

  • 环境变量、系统配置差异

传统开发模式下,想要让项目在另一台电脑跑起来,需要手动配齐所有环境、安装依赖、调试配置,耗时费力,还极易出现新的报错。而 Docker 的诞生,就是为了彻底根治「本地能跑、别处报错」的环境兼容难题

Docker的核心设计思想

用一个生活化比喻:

项目 = 一盆花

Docker = 可独立移植的完整花盆

Dockerfile = 标准化种花、养花盆的说明书

他人/运营电脑 = 任意一块不确定的空地(沙漠、石头地、普通泥地)

  • 传统运行方式的弊端:

你精心养护的花(项目),在自家阳台(本地电脑)温度、土壤、水肥都适配,长势很好。但如果直接把花移栽到别人的空地(其他电脑),土壤不对、温度不符、缺少肥料,花大概率直接枯萎、死掉,对应就是项目报错、无法运行。

  • Docker的核心解决方案:

不单独移栽花,而是把「花+土壤+肥料+适宜温度」整套环境打包成一个独立花盆

无论把这个花盆放到任何空地(任何电脑、服务器),不需要适配环境、不需要额外养护,放下就能活,完美实现 一次打包,处处运行

一、Docker

三大概念

仓库(Repository)→  镜像(Image)→  容器(Container)

仓库中存储着镜像,镜像实例化后生成容器。

核心原理

Docker 核心基于 Linux Namespace 做环境隔离、Cgroups 做资源限制,

结合分层镜像机制实现轻量化虚拟化。

容器共享宿主机内核,资源占用小、启动快、可移植性强,

通过统一封装应用与依赖,解决多环境运行不一致问题,实现一次打包、处处运行。

有什么用

不止服务开发,全员受益!

1. 解决开发协作痛点

团队开发中,不用再统一要求所有人安装相同版本的语言、依赖、中间件,避免因本地环境差异导致的协作冲突,统一项目运行标准。

2. 适配非技术人员使用

运营、产品、测试等非开发岗位的电脑,没有配置任何代码运行环境。传统方式完全无法启动项目,而搭配 Docker 后,只需安装Docker软件,就能一键启动完整项目,无需任何环境配置、无需代码基础。

3. 适配服务器部署

服务器无需配置复杂运行环境,杜绝线上线下环境不一致问题,大幅降低部署故障、运维成本。

操作命令

1. 镜像的操作

2. 容器的操作

创建容器

停止容器,查看后发现没有了(docker ps 默认只展示没有停止的)

docker ps -a (可以展示运行中和停止的镜像)

删除容器:(docker rm 不能删除运行中的容器,除非加上 -f 参数强制删除)

二、数据卷(容器数据持久化)

是什么

数据卷,是一个虚拟目录,通常指向宿主机上的某个目录,这个目录是由Docker虚拟化管理的,它可能位于宿主机的文件系统内部的一个特定区域。

当Docker守护进程运行时,它会管理所有数据卷的生命周期,包括它们的创建、删除和使用。这意味着,即使宿主机上的对应目录被删除,Docker仍然能够控制数据卷的内容和状态。

翻译一下就是:

数据卷 = 容器的 “外置 U 盘”  (把容器里的文件夹,链接到你电脑真实的文件夹上)

有什么用

容器有个致命问题: 容器删了 → 里面的数据也跟着删了!

比如:

  • 你在容器里运行 MySQL,存了 1000 条数据
  • 你把容器删了
  • 数据全部消失,找不回来!

为什么? 因为容器本身是临时的、一次性的

数据卷的作用:让数据不跟着容器一起死!

容器可以随便删、随便重建、随便更新 但数据永久保存在宿主机(你的电脑 / 服务器)上。

容器和数据耦合的问题

使用数据卷的好处包括:

  • 持久化数据:即使容器停止或删除,数据卷中的数据还在。
  • 数据共享:本地 ←→ 容器互通。多个容器可以挂载同一个数据卷,共享数据。
  • 隔离应用和数据:数据卷使得应用和数据分离,便于备份和迁移,独立于容器生命周期。

操作命令


创建数据卷:

查看数据卷:

删除数据卷:

挂载数据卷:

docker run -v /my/volume:/path/in/container -d my_image

上面的命令将宿主机上的/my/volume目录挂载到容器中的/path/in/container路径。这并不意味着/my/volume是容器内部的一个真实目录,而是Docker为这个挂载点创建了一个数据卷,并且这个数据卷在宿主机上有一个特定的存储位置。

三、Dockerfile (放在微服务项目)

是什么

比如你要想运行你的微服务的docker实例(user/order/gateway):

需要:先写 Dockerfile → build 镜像 → 运行实例

如果要想运行中间件(MySQL/Redis/Nacos),直接:从仓库拉取下载 → 运行镜像。 

( 此时不需要自己写 Dockerfile,相当于从仓库直接拉取下来了镜像

官方 / 维护者 自己写了 Dockerfile → 本地 docker build → 生成镜像 → docker push 到仓库。)

Docker 实现环境打包的核心,就是 Dockerfile —— 它是告诉Docker如何构建项目运行环境的配置文件,所有步骤都是标准化、可复用的。

流程:

Dockerfile → build → 镜像 → push → 仓库 → pull → 镜像 → run → 容器

以Python Flask项目为例,极简基础版Dockerfile如下:

FROM python:3.9

WORKDIR /app

COPY . .

RUN pip install flask

CMD ["python", "app.py"]

逐行通俗解析:

  • FROM python:3.9:指定基础运行环境。相当于提前备好一台预装Python3.9的干净设备,容器初始是空环境,必须依托官方基础镜像提供运行所需的底层环境。

  • WORKDIR /app:在容器内创建并指定项目工作目录,后续所有操作都在这个目录下执行,统一项目路径,避免文件混乱。

  • COPY . .:将本机当前文件夹下的所有项目代码、配置文件,全部复制到容器的 /app 目录中,让容器可以读取到项目源码。

  • RUN pip install flask:在容器环境内安装项目所需的第三方依赖包,补齐项目运行必备的依赖环境。

  • CMD ["python", "app.py"]:指定容器启动后的执行命令,也就是项目的启动指令,让容器启动后自动运行项目。
     

为什么必须要有这几步?底层设计逻辑

很多人只会抄写Dockerfile,但不懂底层逻辑。其实所有Dockerfile的编写逻辑,

都围绕「项目能正常启动的四大必备条件」展开,缺一不可:

1. 底层运行环境(FROM)

2. 项目代码与运行目录(WORKDIR + COPY)

3. 第三方依赖(RUN)

4. 项目启动入口(CMD / ENTRYPOINT)

核心总结:环境 + 代码 + 依赖 + 启动命令 = 项目完整运行条件,这也是所有Dockerfile的核心设计逻辑。

操作命令

很多同学学完 Dockerfile 依然懵:我到底从哪一步开始用?完整流程是什么?

这里给大家梳理零基础、标准、完整的 Docker 使用四步曲,适配所有项目,结合花盆比喻全程通透:

第一步:编写 Dockerfile

根据项目类型(Python/Java/前端)编写专属构建文件,定义好「环境、代码、依赖、启动命令」,也就是我们上面讲到的项目运行四大必备条件。

第二步:docker build

执行构建命令,让 Docker 按照说明书,打包生成完整项目镜像。

docker build -t my-app:1.0 .

含义:在当前目录,根据Dockerfile构建镜像,命名为 my-app:1.0。

第三步:docker run

基于镜像启动容器,真正运行项目,对外暴露端口可以访问。

docker run -d -p 5000:5000 --name my-project my-app:1.0

-d 后台运行、-p 端口映射、--name 给容器命名,方便后续管理。

第四步:日常容器管理(养护/关停花盆)

项目运行后,使用简单命令管理容器,无需复杂操作。

企业级进阶 Dockerfile

上面的极简版适合学习、本地测试,而真实公司生产环境中,为了保证镜像安全、体积小、启动快、不泄露源码,会使用多阶段构建,以SpringCloud微服务项目为例,这是企业通用标准写法:

// ==== 第一阶段:构建阶段(仅用于打包项目,不参与最终运行)====
FROM maven:3.8.6-openjdk-17 AS builder
WORKDIR /app
// 优先复制pom文件,利用docker缓存加速构建
COPY pom.xml .
RUN mvn dependency:go-offline -B
// 复制源码并打包
COPY src ./src
RUN mvn clean package -DskipTests

// ==== 第二阶段:运行阶段(最终生产镜像,轻量、安全)====
FROM eclipse-temurin:17-jre-alpine
// 设置时区,解决日志、数据库时间偏差问题
ENV TZ=Asia/Shanghai
WORKDIR /app
// 仅复制打包好的jar包,不复制源码,保障代码安全
COPY --from=builder /app/target/*.jar app.jar
// 项目启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]

四、从镜像到容器

上面我们已经知道了如何通过微服务项目代码中的 dockerfile 生成镜像,接下来思考:

🤔: 生成的镜像最终如何在我们的服务器中形成容器的?

假设现在你的架构:

  • 服务器 1:注册中心 + 网关
  • 服务器 2:业务微服务(user-service、order-service)
  • 服务器 3:数据库 + 缓存

方式1: 直接在服务器生成
方式2:将本地镜像上传到服务器

【项目代码】
    ↓
【Dockerfile】(在项目里)
    ↓
-----------------------------------
方式1:服务器 build → 服务器镜像
方式2:本地 build 镜像 → 传镜像文件 → 服务器镜像
-----------------------------------
    ↓
【服务器上的镜像】
    ↓
【docker-compose.yml】(在服务器上)
    ↓
【docker-compose up -d】
    ↓
【自动生成容器】
    ↓
【服务运行成功】

五、docker-compose.yml (放在服务器)

架构依然是:

  • 服务器 1:注册中心 + 网关
  • 服务器 2:业务微服务(user-service、order-service)
  • 服务器 3:数据库 + 缓存

    如果按照之前手敲命令:docker run → 启动一个服务,你要手动敲 6 条超长命令

    docker run nacos...
    docker run mysql...
    docker run redis...
    docker run gateway...
    docker run -d --name user-service user-service:1.0
    ...

    现在:docker-compose 一台机器启动一套服务  (将来:K8s → 管理多台机器的所有服务)

      以服务器2举例:

      文件名:docker-compose.yml

      version: '3.8'
      
      services:
        # 用户服务
        user-service:
          image: user-service:1.0
          container_name: user-service
          restart: always
      
        # 订单服务
        order-service:
          image: order-service:1.0
          container_name: order-service
          restart: always

      最终只需要,单独运行命令:

      docker-compose up -d

      公式:

      services:
        abc123:               # 随便写
          image: user-service:1.0  # 必须和build一致,之前运行:docker build -t user-service:1.0 .
          container_name: my-user  # 随便写
          restart: always

      另外需要注意,docker-compose 只是帮我们省去了手动 docker run,但是 run 之前的 build 步骤还是依旧不变的,该 build 几次还是几次,本例子中需 build 3次,命令:

      # 网关
      docker build -t gateway:1.0 .
      
      # 用户服务
      docker build -t user-service:1.0 .
      
      # 订单服务
      docker build -t order-service:1.0 .

      Logo

      汇聚全球AI编程工具,助力开发者即刻编程。

      更多推荐