Dev Guide / 2026 ・ No.05
Docker 入门与实战
从基本原理,到 Java Web 系统的开发、部署与运维
发布
“在我电脑上能跑,到服务器上就不行。”“新同事搭开发环境要花一整天。”
“想升级生产环境的中间件,却不敢动手。”
Docker 就是把这些现场烦恼一并缩小的工具。本文先梳理 Docker 的原理和术语,
介绍它常用的场景,再以 Spring Boot 加 Vue 的客户 Web 系统为例,
结合实际运行过的示例,说明开发、部署、运维、维护各环节能轻松在哪里。
Docker 是什么
Docker 是一种把应用和它运行所需的一切(操作系统的库、Java 等运行环境、配置文件)打包进一个箱子(容器),在任何地方都以同样方式运行的机制。它于 2013 年发布,如今已成为分发和运行应用的标准形式。
打个比方,它就像集装箱。不管装的是家具还是食品,只要放进规格统一的箱子,轮船、卡车、火车都能同样运输。Docker 容器也是如此:里面是 Java 应用也好、数据库也好,只要机器上装了 Docker,就能用同样的步骤启动。
Docker 的价值在于能“连同环境一起”搬走。“Java 25、这个版本的库、这些配置”打成一个整体,开发者电脑、测试服务器、生产服务器上运行的是同一个东西。“在我这儿能跑”的问题,大多在这里消失。
原理:容器与虚拟机的区别
要“整套准备运行环境”,过去就有虚拟机(VM)。两者的区别在于各自带多少操作系统。
VM 连同操作系统,容器只带应用
容器利用 Linux 内核的功能(命名空间划分可见范围,cgroups 限制 CPU 和内存)来隔离进程
| 虚拟机 | 容器 | |
|---|---|---|
| 启动 | 几十秒到几分钟(要启动操作系统) | 几秒以内(只是启动进程) |
| 体积 | 数 GB 起 | 几十到几百 MB |
| 单机可运行数量 | 几台到十几台 | 几十个起 |
| 隔离强度 | 强(内核也独立) | 比 VM 弱(共享内核) |
| 内部操作系统 | Windows、Linux 都可以 | 基本是 Linux(用宿主的内核) |
与其说谁更好,不如说分工不同。无论云上还是企业内部,“在 VM 上跑 Docker”都是非常普遍的组合:用 VM 划分机器,用容器划分应用。
需要掌握的术语
从 Dockerfile 到容器
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(免费・开源) | 生产环境一般用它,容器直接运行 |
| Windows | Docker Desktop(基于 WSL 2) | 源码放在 WSL 一侧,文件读写更快 |
| Mac | Docker Desktop | Apple Silicon 上使用 arm64 镜像 |
| 替代工具 | Rancher Desktop、Podman Desktop 等 | 免费开源,Compose 文件大体通用 |
员工 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 数据库。
按服务拆分容器
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 提供,也在这里转发 API | node 构建 → 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"));
}
}
新成员只需要 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 之后才启动的。
部署方式的变化
只构建一次,分发同一个东西
镜像仓库除了 Docker Hub 的私有仓库,也可以用云上或企业内部的
升级只需改掉 .env 里的 TAG,再执行一次 docker compose up -d。只有变化的服务会被重建,数据库照常运行。
| 以往 | 使用 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 本身就是“环境设计书”,避免知识只在个人身上 |
| 故障排查 | 在本地启动与生产相同的镜像,按相同条件复现和调查 |
维护上最大的好处是“升级”和“回退”都变得轻松。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 中生成镜像 → ④ 测试环境 → ⑤ 生产。确认前一阶段的效果,再一步步推进。
结 — 一页总结
- Docker 把应用和运行环境装进容器这个箱子,在任何地方以同样方式运行。与 VM 不同,它共享操作系统内核,所以轻快。
- 术语串起来是 Dockerfile → build → 镜像 → push → 镜像仓库 → pull → run → 容器。数据放卷里,多个容器用 Compose 编排。
- 在 Java Web 系统中,开发用数据库・中间件、测试、应用和页面、CI、测试・生产都用得上。先从开发用数据库开始。
- 部署是分发同一镜像、用标签管理。升级和回滚都是改 TAG 后 up -d。
- 维护中 Java・中间件升级和漏洞处理,可以按“试一试,不行就退回”推进。注意 Docker Desktop 的许可和 Docker Hub 的拉取上限。
参考
- Docker Docs — What is Docker?
- Docker Docs — Docker Engine release notes
- Docker Docs — Docker Compose
- Docker Docs — Multi-stage builds
- Docker Docs — Docker Desktop license agreement
- Docker Docs — Docker Hub pull usage and limits
- Spring Boot 4.1.1 — Development-time Services(Docker Compose)
- Spring Boot 4.1.1 — Testcontainers
- Spring Boot Maven Plugin — Packaging OCI Images
- Docker Hub — eclipse-temurin
- Docker Hub — postgres
- Docker Hub — nginx
示例于 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