Dev Guide / 2026 ・ No.05

Docker 入门与实战

从基本原理,到 Java Web 系统的开发、部署与运维

发布

在终端执行 docker compose up -d,db、app、web 三个容器依次启动,再用 docker compose ps 查看状态,用 curl 从 API 取回客户列表 JSON 的画面
启动本文示例的情形:先等数据库就绪,再启动应用,最后启动页面容器,API 返回了客户数据(笔者实际运行的输出,为便于阅读重新绘制成终端画面)

“在我电脑上能跑,到服务器上就不行。”“新同事搭开发环境要花一整天。”
“想升级生产环境的中间件,却不敢动手。”
Docker 就是把这些现场烦恼一并缩小的工具。本文先梳理 Docker 的原理和术语,
介绍它常用的场景,再以 Spring Boot 加 Vue 的客户 Web 系统为例,
结合实际运行过的示例,说明开发、部署、运维、维护各环节能轻松在哪里。

一

Docker 是什么

Docker 是一种把应用和它运行所需的一切(操作系统的库、Java 等运行环境、配置文件)打包进一个箱子(容器),在任何地方都以同样方式运行的机制。它于 2013 年发布,如今已成为分发和运行应用的标准形式。

打个比方,它就像集装箱。不管装的是家具还是食品,只要放进规格统一的箱子,轮船、卡车、火车都能同样运输。Docker 容器也是如此:里面是 Java 应用也好、数据库也好,只要机器上装了 Docker,就能用同样的步骤启动。

CORE

Docker 的价值在于能“连同环境一起”搬走。“Java 25、这个版本的库、这些配置”打成一个整体,开发者电脑、测试服务器、生产服务器上运行的是同一个东西。“在我这儿能跑”的问题,大多在这里消失。

二

原理:容器与虚拟机的区别

要“整套准备运行环境”,过去就有虚拟机(VM)。两者的区别在于各自带多少操作系统。

VM 连同操作系统,容器只带应用

虚拟机(VM) 每个应用一整套操作系统 应用1 库 客户机 OS 应用2 库 客户机 OS 应用3 库 客户机 OS 虚拟化层 宿主 OS 硬件 容器(Docker) 共享操作系统内核 应用1 库 应用2 库 应用3 库 Docker Engine 宿主 OS(Linux 内核) 硬件 没有客户机 OS,容器更轻,几秒就能启动 在 Mac・Windows 上,容器运行在一个小型 Linux 虚拟机里

容器利用 Linux 内核的功能(命名空间划分可见范围,cgroups 限制 CPU 和内存)来隔离进程

虚拟机容器
启动几十秒到几分钟(要启动操作系统)几秒以内(只是启动进程)
体积数 GB 起几十到几百 MB
单机可运行数量几台到十几台几十个起
隔离强度强(内核也独立)比 VM 弱(共享内核)
内部操作系统Windows、Linux 都可以基本是 Linux(用宿主的内核)

与其说谁更好,不如说分工不同。无论云上还是企业内部,“在 VM 上跑 Docker”都是非常普遍的组合:用 VM 划分机器,用容器划分应用。

三

需要掌握的术语

从 Dockerfile 到容器

Dockerfile 设计图 镜像 只读 镜像仓库 Docker Hub 等 服务器 测试・生产 容器 运行中的实体 build push pull run 卷 数据库数据 ← 开发者电脑・CI ← 存放处(也可自建) 一次构建的镜像,在开发、测试、生产中原样复用 容器默认可随时删除重建,要保留的数据放进卷

build、push、pull、run 正是 docker 命令的名字

术语含义比喻
镜像把应用和运行环境固化下来的只读模板。由多层(layer)叠加而成,共同部分可以复用速冻料理包
容器由镜像启动的运行实体。同一镜像可启动任意多个,默认可随时删除重建加热端上的一盘菜
Dockerfile描述镜像如何构建的文本:从哪个基础镜像开始、复制什么、执行什么菜谱
镜像仓库存放镜像的地方。公开的 Docker Hub,以及 AWS ECR、GitHub GHCR、企业内部的 Harbor 等冷库
标签镜像的版本名,例如 postgres:18 中的 18标签
卷删掉容器也会保留的数据存放处。数据库数据放这里外接储藏室
网络连接容器的虚拟网络。Compose 中用服务名(如 db)即可访问对方公司内线电话
Docker Compose把多个容器的组合写进 compose.yaml,一句 docker compose up 一起启动的工具套餐菜单
Docker Engine运行容器的核心(守护进程和命令)。Linux 服务器上装它,开源厨房
Docker Desktop面向 Windows・Mac 的套件,包含 Engine、图形界面和小型 Linux 虚拟机家用厨房套装

镜像采用 OCI 这一业界标准格式,所以用 Docker 构建的镜像在 Podman、Kubernetes 等其他工具中也能直接运行。

四

Docker 的用武之地

场景好处
统一开发环境全队统一数据库和中间件的版本。新成员一句 docker compose up 就准备好
自动化测试每次测试都能临时启动真实数据库(Testcontainers),尽早发现 Mock 发现不了的 SQL 问题
CI/CD每次都在干净环境中构建和测试,产出的镜像直接就是交付物
生产部署测试过的镜像原样上线,回退只需把标签改回上一版
多版本共存客户 A 用 Java 21、客户 B 用 Java 25,也能在一台电脑或服务器上互不干扰地运行
用完即弃的验证环境复现缺陷、演示、培训用的环境,随建随删
微服务各服务语言、库不同,分发和运行方式却一样
老系统续命与迁移把旧运行环境封进镜像搬到新服务器,为迁移争取时间
数据分析・AI把 Python 与 GPU 库的复杂组合作为镜像分发
五

在 Windows・Mac・Linux 上使用

系统安装什么备注
Linux(服务器)Docker Engine(免费・开源)生产环境一般用它,容器直接运行
WindowsDocker Desktop(基于 WSL 2)源码放在 WSL 一侧,文件读写更快
MacDocker DesktopApple Silicon 上使用 arm64 镜像
替代工具Rancher Desktop、Podman Desktop 等免费开源,Compose 文件大体通用
LICENSE

员工 250 人以上或年收入 1,000 万美元以上的组织,在工作中使用 Docker Desktop 需要付费订阅(个人、教育、非商业开源项目,以及低于上述规模的公司免费)。Linux 服务器上的 Docker Engine 不在此列。另外,从 Docker Hub 拉取镜像有次数上限:未登录每 6 小时 100 次(按 IP 地址),免费账号 200 次。CI 中最好准备内部镜像仓库或缓存。

六

实战:Java Web 系统哪里用得上

下面以面向客户的业务 Web 系统(客户管理等 CRM)为例。构成是当下 Java 项目中常见的Spring Boot(Java)API、Vue 页面和 PostgreSQL 数据库。

按服务拆分容器

docker compose(一台服务器) 浏览器 用户 web nginx + Vue app Spring Boot db PostgreSQL 卷 cache Redis(可选) :80 /api 一个服务=一个容器,按职责拆分,一起启动 朱色框=本文示例中实际运行过的部分

web 是浏览器访问的入口,返回页面文件,同时把 /api 转发给 app

部分Docker 的用法镜像示例
开发环境只用容器启动数据库和中间件,应用在 IDE 中运行(便于调试)写进 compose.yaml
数据库每个人的电脑上都用与生产相同的产品和主版本,测试时也临时启动postgres、mysql、mariadb,Oracle 可用 gvenzl/oracle-free
中间件按需加入缓存、消息、搜索、认证、文件存储等redis、rabbitmq、elasticsearch、keycloak、minio
开发辅助工具邮件发送测试、数据库管理界面等生产不需要的东西mailpit、pgadmin
应用本体把 Spring Boot 的 jar 连同 Java 运行环境做成镜像以 eclipse-temurin(JRE)为基础
页面Vue 构建出的静态文件由 nginx 提供,也在这里转发 APInode 构建 → nginx 分发
批处理夜间汇总等,用同一镜像以不同方式启动与 app 相同的镜像
CI构建、测试、生成镜像每次都在同样的环境中进行GitHub Actions、GitLab CI、Jenkins
测试・生产一台服务器用 Compose,台数多时上容器平台Compose/Kubernetes/ECS 等

本文的示例(Spring Boot 4.1.1・Java 25、Vue 3、nginx 1.30、PostgreSQL 18),笔者已在 Docker Engine 29 和 Compose v5 上实际完成了构建、启动、更新和回滚的验证。

七

步骤 1:开发环境的数据库容器化

先从效果最大、风险最小的地方开始:只把数据库放进容器。在项目根目录放下面这个 compose.yaml。

# crm/compose.yaml(开发用:只有数据库)
services:
  postgres:
    image: postgres:18          # 与生产相同的主版本
    environment:
      POSTGRES_DB: crm
      POSTGRES_USER: crm
      POSTGRES_PASSWORD: secret
    ports:
      - "5432"                  # 自动分配电脑上空闲的端口

Spring Boot 提供了与 Docker Compose 的联动功能(spring-boot-docker-compose)。加入依赖后,在 IDE 中启动应用时,Spring Boot 会自动执行 docker compose up,并把数据库连接指向容器,连 application.properties 里的连接信息都不用写。

# 启动应用时的日志(节选・实际输出)
DockerComposeLifecycleManager : Using Docker Compose file .../crm/compose.yaml
DockerCli : Container crm-postgres-1 Started
DockerCli : Container crm-postgres-1 Healthy
DbMigrate : Migrating schema "public" to version "1 - create customer"
CrmApplication : Started CrmApplication in 7.271 seconds

测试可以用 Testcontainers:只在测试运行期间启动真实的 PostgreSQL,结束后自动清理。Spring Boot 中加上 @ServiceConnection 就会自动配置连接。

// 测试配置(与 Spring Initializr 生成的基本相同)
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
    @Bean
    @ServiceConnection
    PostgreSQLContainer postgresContainer() {
        return new PostgreSQLContainer(DockerImageName.parse("postgres:18"));
    }
}
POINT

新成员只需要 Docker、JDK 和 IDE。“安装 PostgreSQL、建用户、设置字符集……”这样的手册就不需要了。表结构在应用启动时用 Flyway 等迁移工具创建,谁的电脑都能从同一状态开始。

八

步骤 2:把应用做成镜像

接下来把 Spring Boot 应用本身做成镜像。要点是多阶段构建:构建用(带 JDK 和 Maven)和运行用(只有 JRE)分开,生产镜像里不放编译器和源码。

# crm/Dockerfile
# syntax=docker/dockerfile:1

# ---- 1. 构建阶段(用 JDK 和 Maven 生成 jar)----
FROM eclipse-temurin:25-jdk AS build
WORKDIR /src
COPY .mvn/ .mvn/
COPY mvnw pom.xml ./
RUN --mount=type=cache,target=/root/.m2 ./mvnw -B -q dependency:go-offline
COPY src/ src/
RUN --mount=type=cache,target=/root/.m2 ./mvnw -B -q -DskipTests package

# ---- 2. 运行阶段(只有 JRE 的小镜像)----
FROM eclipse-temurin:25-jre
WORKDIR /app
RUN useradd --system --uid 10001 app
COPY --from=build /src/target/crm-*.jar app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "-jar", "app.jar"]
  • 先复制依赖描述 ── pom.xml 没变,下载依赖的步骤就会命中缓存而跳过。只改了一点源码,构建几秒到几十秒
  • 不用 root 运行 ── 用专用用户(app)运行,万一出事损失更小
  • 内存按比例 ── -XX:MaxRAMPercentage=75 让堆上限取容器内存的 75%
  • 也可以不写 Dockerfile ── Spring Boot 的 ./mvnw spring-boot:build-image 用 Buildpacks 自动生成镜像

页面(Vue)也是同样思路:用 node 构建,只把生成的静态文件放进 nginx 镜像。

# web/Dockerfile
FROM node:24-alpine AS build
WORKDIR /web
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.30-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /web/dist /usr/share/nginx/html
# web/nginx.conf
server {
    listen 80;
    root /usr/share/nginx/html;

    location / {                       # Vue 页面(SPA)
        try_files $uri $uri/ /index.html;
    }
    location /api/ {                   # API 转发到 Spring Boot 容器
        proxy_pass http://app:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

proxy_pass http://app:8080 里的 app 是下面 Compose 文件中的服务名。不写 IP 地址,用名字就能找到对方。

九

步骤 3:用 Compose 编排生产环境

一台服务器就能承载的规模,Docker Compose 最省事。测试服务器和生产服务器放同一个 compose.yaml。

# deploy/compose.yaml(测试・生产用)
services:
  web:                      # Vue 页面 + 入口(nginx)
    image: registry.example.com/crm/web:${TAG}
    ports:
      - "80:80"
    depends_on:
      - app
    restart: unless-stopped

  app:                      # Spring Boot 业务应用
    image: registry.example.com/crm/app:${TAG}
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/crm
      SPRING_DATASOURCE_USERNAME: crm
      SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
      TZ: Asia/Tokyo
    depends_on:
      db:
        condition: service_healthy   # 等数据库就绪后再启动
    restart: unless-stopped

  db:                       # PostgreSQL(数据保存在卷中)
    image: postgres:18
    environment:
      POSTGRES_DB: crm
      POSTGRES_USER: crm
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      TZ: Asia/Tokyo
    volumes:
      - db-data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U crm -d crm"]
      interval: 5s
      retries: 10
    restart: unless-stopped

volumes:
  db-data:
# deploy/.env(每台服务器各放一份,不提交到 Git)
TAG=1.0.0
DB_PASSWORD=change-me
  • 版本只在 TAG 一处 ── 运行哪个版本由 .env 决定
  • 密码放在文件之外 ── 不写进 Compose 文件,用 .env 或密钥管理机制传入
  • PostgreSQL 18 的卷 ── 从 18 起数据位置有变,卷挂载到 /var/lib/postgresql(17 及以前是 /var/lib/postgresql/data)
  • restart: unless-stopped ── 服务器重启或应用崩溃后自动拉起

启动只需一句 docker compose up -d。文章开头的画面就是实际输出,可以看到应用是在数据库变成 healthy 之后才启动的。

十

部署方式的变化

只构建一次,分发同一个东西

Git push 代码 CI 构建・测试 镜像仓库 app:1.0.1 测试环境 同一镜像 生产环境 同一镜像 回滚只需把标签改回 1.0.0 app:1.0.0 CI:GitHub Actions、GitLab CI、Jenkins 等。测试用 Testcontainers 连真实数据库 “测试时验证过的”和“生产上运行的”字节级完全一致 减少照着手册的手工操作,谁来部署结果都一样

镜像仓库除了 Docker Hub 的私有仓库,也可以用云上或企业内部的

升级只需改掉 .env 里的 TAG,再执行一次 docker compose up -d。只有变化的服务会被重建,数据库照常运行。

在终端把 .env 中的版本改成 1.0.1 并执行 docker compose up -d,db 保持运行,只有 app 和 web 被重建的画面
版本升级操作:只重建发生变化的 app 和 web,存着数据的 db 一直在运行(笔者实际运行的输出,为便于阅读重新绘制成终端画面)
以往使用 Docker 后
交付物war・jar 加操作手册(Java、Tomcat 的设置)一个镜像(含运行环境)
操作登录服务器,手工复制、配置、重启改 TAG,执行 docker compose up -d
与测试环境的差异各服务器的 Java、库版本不一致测试与生产用同一镜像
回滚找出旧 war 重新部署,还要改回配置把 TAG 改回上一版即可(几十秒)
新服务器从操作系统开始逐步搭建,要几天装好 Docker,放两个文件,几十分钟
谁来操作依赖熟悉流程的人步骤短,谁做结果都一样

涉及数据结构变化的更新(如加表),在应用启动时用 Flyway 等自动执行,就只需替换镜像。回滚时数据库如何还原要事先定好(做成可回退的变更,或更新前先备份)。

十一

运维与维护的变化

运维

  • 状态一目了然 ── docker compose ps 看所有服务状态,docker compose logs -f app 看应用日志,每台服务器命令相同
  • 自动恢复 ── 借助重启策略和健康检查,挂掉的容器会自动重启
  • 环境差异导致的故障减少 ── 消除了“只有生产乱码”“只有生产跑不起来”常见根源——操作系统和库的差异
  • 资源可以隔离 ── 可给每个容器设定 CPU 和内存上限,一个失控不至于拖垮全部
  • 规模变大也用同一镜像 ── 用户增多时,原样迁移到 Kubernetes 或云上的容器服务

维护

常见维护工作用 Docker 怎么做
升级 Java(如 21 → 25)把 FROM eclipse-temurin:21-jre 改成 25-jre 后构建,在测试环境验证,不行就改回原标签
安全补丁更新基础镜像后重新构建、重新部署,不用逐台手工更新操作系统
漏洞检查用 Docker Scout 或 Trivy 列出镜像中组件的已知漏洞,可集成到 CI
中间件升级(数据库、nginx 等)用新标签整套搭一个测试环境,与生产并行验证,便于制定应对停止支持(EOSL)的计划
维护多个客户即使各客户版本不同,一台电脑也能切换客户 A・B 的环境来复现问题
人员交接Dockerfile 和 compose.yaml 本身就是“环境设计书”,避免知识只在个人身上
故障排查在本地启动与生产相同的镜像,按相同条件复现和调查
MERIT

维护上最大的好处是“升级”和“回退”都变得轻松。Java、Spring Boot、数据库的旧版本之所以容易遗留,是因为怕升级而停滞不前。如果能在旁边搭好新环境试一试,不行一行就退回,就能在停止支持之前有计划地升级。

十二

引入时的注意事项

注意点对策
数据丢失数据库数据务必放在卷中。docker compose down -v 会连卷一起删,生产中不要用。另外单独备份(如 pg_dump)
latest 标签内容会在不知不觉中变化。像 postgres:18 这样写明版本
机密信息密码和密钥不要放进镜像或 Git,使用 .env、Docker secrets 或云上的密钥管理
基础镜像无人维护不要建完就不管,定期重新构建以纳入补丁
时区・字符集容器默认 UTC,设置 TZ=Asia/Tokyo,并确认数据库字符集
Windows・Mac 的速度与电脑共享大量文件会变慢。源码放在 WSL 一侧,构建利用容器内缓存
生产规模一台服务器用 Compose 足够。需要多台、不停机更新、自动伸缩时,考虑 Kubernetes 或云上的容器服务
客户环境有的客户无法安装 Docker。即便如此,只在开发、测试、验证中使用,效果也很大

不必一开始就全部容器化。推荐的顺序是:① 开发用数据库 → ② 测试(Testcontainers)→ ③ 在 CI 中生成镜像 → ④ 测试环境 → ⑤ 生产。确认前一阶段的效果,再一步步推进。

结 — 一页总结

  1. Docker 把应用和运行环境装进容器这个箱子,在任何地方以同样方式运行。与 VM 不同,它共享操作系统内核,所以轻快。
  2. 术语串起来是 Dockerfile → build → 镜像 → push → 镜像仓库 → pull → run → 容器。数据放卷里,多个容器用 Compose 编排。
  3. 在 Java Web 系统中,开发用数据库・中间件、测试、应用和页面、CI、测试・生产都用得上。先从开发用数据库开始。
  4. 部署是分发同一镜像、用标签管理。升级和回滚都是改 TAG 后 up -d。
  5. 维护中 Java・中间件升级和漏洞处理,可以按“试一试,不行就退回”推进。注意 Docker Desktop 的许可和 Docker Hub 的拉取上限。

参考

示例于 2026 年 10 月 9 日在 Docker Engine 29.4・Compose v5.1 环境中,使用 Spring Boot 4.1.1(Java 25)、Vue 3、nginx 1.30、PostgreSQL 18 实际完成了构建、启动、更新和回滚的验证。画面是把实际输出重新绘制以便阅读,镜像仓库名(registry.example.com)为示例。Docker Desktop 的许可和 Docker Hub 的上限以 2026 年 10 月的官方文档为准。

开发指南 No.5 — Docker 入门与实战 / 2026.10

← 返回文章列表