java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > docker容器化java saas应用

如何使用Docker容器化你的Java SaaS应用:一次构建,随处运行

作者:做个文艺程序员

本文系统讲解如何使用Docker将Java SaaS应用及其依赖的MySQL、Redis等中间件进行全容器化部署,通过Dockerfile镜像构建与docker-compose容器编排,实现“一次构建,随处运行”的标准化交付

一句话定义:本文系统讲解如何使用Docker将Java SaaS应用及其依赖的MySQL、Redis等中间件进行全容器化部署,通过Dockerfile镜像构建与docker-compose容器编排,实现“一次构建,随处运行”的标准化交付。

一、引言:从“手工部署”到“一键启动”

在之前的15篇文章中,我们走完了从Linux基础到Java环境搭建、从Nginx配置到MySQL/Redis部署的全流程。这个过程你肯定深有体会——每部署一套新环境,都要重复一遍:装JDK、配MySQL、装Redis、改配置文件、启动服务…… 少说也要半天时间。

如果有一天服务器宕机了要迁移,或者要把应用从测试环境部署到生产环境,难道要重新来一遍?

Docker就是来解决这个问题的。

Docker的核心思想是**“一次构建,随处运行”** ——把应用连同它依赖的所有东西(JDK、配置文件、环境变量)打包成一个镜像,这个镜像可以在任何安装了Docker的服务器上直接运行成容器。开发环境跑通了,镜像推到生产环境,行为完全一致——“在我机器上是好的”这句话将彻底成为历史

在Java SaaS部署的全链路中(第13篇JDK → 第14篇应用部署 → 第15篇Nginx → 第16篇数据库 → 第17篇Redis → 本篇Docker → 第19篇性能调优),Docker是从“手工运维”走向“自动化运维”的分水岭。掌握了Docker,你就掌握了现代软件交付的“通用语言”。

二、Docker核心概念:镜像、容器、仓库

在动手之前,花3分钟搞清楚三个核心概念——这是理解一切Docker操作的基础

概念通俗理解类比
镜像(Image)一个只读的“模板”,包含了运行应用所需的一切(代码、运行时、系统工具、库)类似于安装光盘/ISO文件
容器(Container)镜像的运行实例,一个隔离的进程类似于用光盘安装好的操作系统
仓库(Repository)存储和分发镜像的地方类似于应用商店/代码仓库

关系镜像运行容器。你可以从仓库拉取别人做好的镜像,也可以自己用Dockerfile构建镜像。一个镜像可以启动无数个容器实例。

三、在Linux上安装Docker

3.1 安装前的系统检查

为什么这样写:Docker对操作系统内核版本有明确要求。很多人在老旧的CentOS 7上装Docker,结果因为内核版本太低(❤️.10)导致overlay2存储驱动无法使用,容器启动各种报错。先检查再安装,能避免90%的坑。

踩过的坑

注意事项

代码块:环境检查与旧版本清理

# 1. 检查内核版本(必须 >= 3.10)
uname -r
# 2. 检查操作系统版本
cat /etc/os-release
# 3. 卸载旧版本Docker(如有)
# Rocky Linux / CentOS:
sudo dnf remove -y docker docker-client docker-client-latest \
  docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
# Ubuntu / Debian:
sudo apt remove -y docker docker-engine docker.io containerd runc

执行后说明uname -r应输出类似5.14.0-284.el9.x86_64的内核版本。如果内核低于3.10(如CentOS 7的3.10.0),虽然能满足最低要求,但建议升级到更新的发行版以获得更好的容器支持。

3.2 安装Docker Engine(双系统适配)

为什么这样写:Docker官方提供了针对各Linux发行版的官方仓库,这是生产环境最推荐的安装方式——能自动处理依赖、方便后续升级、来源可靠。不要用系统默认仓库里的旧版本Docker。

踩过的坑

注意事项

代码块:Rocky Linux 9 / CentOS 9 安装Docker

# 1. 安装必要依赖
sudo dnf install -y dnf-plugins-core
# 2. 添加Docker官方YUM仓库
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 3. 安装Docker Engine及相关组件
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 4. 启动Docker服务并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 5. 验证安装
sudo docker --version

代码块:Ubuntu 22.04 / 24.04 安装Docker

# 1. 更新软件包列表并安装依赖
sudo apt update
sudo apt install -y ca-certificates curl
# 2. 添加Docker官方GPG密钥
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 3. 添加Docker官方APT仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 4. 安装Docker Engine
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 5. 启动Docker服务并验证
sudo systemctl start docker
sudo systemctl enable docker
sudo docker --version

执行后说明:安装完成后,sudo docker --version应输出类似Docker version 27.0.3的版本信息。Docker服务已启动并设置为开机自启。

3.3 配置用户权限与镜像加速(非必须但强烈推荐)

为什么这样写:每次执行docker命令都要加sudo很烦人,把当前用户加入docker组可以省去这个麻烦。另外,在国内访问Docker Hub拉取镜像速度很慢,配置镜像加速器能大幅提升体验。

注意事项

代码块:用户权限配置与镜像加速

# 1. 将当前用户加入docker组(避免每次sudo)
sudo usermod -aG docker $USER
# ⚠️ 执行后需要退出重新登录才能生效
# 2. 配置Docker守护进程(/etc/docker/daemon.json)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://your-mirror.aliyuncs.com"   # 替换为你的镜像加速器地址
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2"
}
EOF
# 3. 重启Docker使配置生效
sudo systemctl daemon-reload
sudo systemctl restart docker
# 4. 验证配置
docker info | grep -A 5 "Registry Mirrors"

执行后说明docker info会显示当前的镜像加速器配置。如果看到你配置的加速器地址,说明生效了。storage-driver: overlay2是当前最推荐的存储驱动,性能和稳定性都很好。

四、编写Dockerfile:把Java应用变成镜像

4.1 为什么需要多阶段构建

为什么这样写:一个Java应用的Dockerfile写得好不好,最直接的衡量标准就是镜像体积。一个未经优化的Spring Boot镜像动辄500MB-1GB,拉取慢、传输慢、浪费存储空间。而采用多阶段构建(Multi-stage Build)后,镜像可以瘦身到100MB左右。

多阶段构建的核心思想是

最终镜像里只包含运行时需要的东西——没有源码、没有Maven、没有编译工具,体积大幅缩小,安全性也更高。

踩过的坑

注意事项

4.2 生产级Dockerfile完整示例

代码块:多阶段构建Dockerfile(生产环境推荐)

# ============================================================
# 第一阶段:构建阶段(builder)
# 使用包含完整JDK和Maven的镜像来编译打包
# ============================================================
FROM maven:3.9-eclipse-temurin-17-alpine AS builder
# 设置工作目录
WORKDIR /build
# 先复制pom.xml——利用Docker层缓存,依赖不变时不重复下载
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 再复制源代码并打包
COPY src ./src
RUN mvn package -DskipTests -B
# ============================================================
# 第二阶段:运行阶段(最终镜像)
# 只复制jar包,使用轻量级JRE镜像
# ============================================================
FROM eclipse-temurin:17-jre-alpine
# 创建应用运行用户(非root,安全加固)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 设置工作目录
WORKDIR /app
# 从构建阶段复制jar包
COPY --from=builder /build/target/*.jar ./app.jar
# 创建挂载点(用于日志和配置的外部挂载)
VOLUME /app/logs
VOLUME /app/config
# 暴露应用端口(根据你的应用调整)
EXPOSE 8080
# 切换到非root用户
USER appuser
# JVM参数优化(容器环境专用)
ENV JAVA_OPTS="-XX:+UseContainerSupport \
  -XX:MaxRAMPercentage=75.0 \
  -XX:InitialRAMPercentage=50.0 \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/app/logs/heapdump.hprof \
  -Djava.security.egd=file:/dev/./urandom"
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]

执行后说明:这个Dockerfile有几个关键设计:

4.3 编写.dockerignore——别把“垃圾”打包进镜像

为什么这样写docker build默认会把当前目录下所有文件发送给Docker守护进程来构建镜像。如果不加限制,target/(几百MB的编译产物)、.git/(整个仓库历史)、node_modules/等都会被塞进构建上下文,拖慢构建速度,还可能泄露源码

代码块:.dockerignore文件

# Git相关
.git/
.gitignore
# 构建产物
target/
*.jar
*.war
*.log
# IDE相关
.idea/
*.iml
.vscode/
.settings/
.classpath
.project
# 系统文件
.DS_Store
Thumbs.db
# 容器相关(避免递归)
Dockerfile
.dockerignore
docker-compose*.yml
# 其他
*.md
README*

执行后说明.dockerignore的语法和.gitignore完全一样。配置好后,docker build只会把必要的源码和pom.xml发送给Docker守护进程,构建速度和安全性都得到保障。

五、构建镜像与运行容器

5.1 构建镜像

为什么这样写:Dockerfile写好了,现在要把它“变成”一个真正的镜像。docker build命令就是做这个的。

踩过的坑

注意事项

代码块:构建镜像

# 1. 在项目根目录执行构建
cd /path/to/your-java-project
# 2. 构建镜像(给镜像打上标签)
docker build -t myapp:1.0.0 .
# 3. 查看已构建的镜像
docker images | grep myapp
# 4. 查看镜像分层历史(理解镜像的构成)
docker history myapp:1.0.0

执行后说明docker images应显示myapp镜像,大小约90MB。docker history会展示镜像的每一层——你会看到第一层是Alpine基础镜像(约5MB),然后是JRE层,最后是app.jar层。每一层都是一个只读文件系统,层与层之间复用,这就是Docker镜像高效的原因。

5.2 运行容器

为什么这样写:镜像构建好了,现在要把它“跑起来”——这就是容器。

踩过的坑

代码块:运行容器

# 1. 基本运行(前台运行,用于调试)
docker run -p 8080:8080 myapp:1.0.0
# 2. 后台运行 + 重启策略 + 资源限制(生产环境推荐)
docker run -d \
  --name myapp \
  --restart=unless-stopped \
  -p 8080:8080 \
  -m 2g \
  --memory-reservation 1.5g \
  -e "SPRING_PROFILES_ACTIVE=prod" \
  -v /data/myapp/logs:/app/logs \
  -v /data/myapp/config:/app/config \
  myapp:1.0.0
# 3. 查看容器运行状态
docker ps
# 4. 查看容器日志
docker logs -f myapp
# 5. 进入容器内部(调试用)
docker exec -it myapp sh

执行后说明

关键提示-m 2g和JVM的-XX:MaxRAMPercentage=75.0要配合使用——容器内存2GB,JVM堆最大1.5GB,留500MB给JVM元空间和系统开销。

六、docker-compose:一键启动整个SaaS应用栈

6.1 为什么需要docker-compose

为什么这样写:到目前为止,我们只容器化了Java应用本身。但一个完整的SaaS应用还需要MySQL、Redis、Nginx等中间件。如果每个服务都手动docker run,不但命令冗长,还要处理服务之间的网络连通、启动顺序等问题。

docker-compose就是来解决这个问题的——它用一个docker-compose.yml文件定义所有服务,然后一条命令启动全部

踩过的坑

6.2 docker-compose.yml完整示例

代码块:docker-compose.yml(Java应用 + MySQL + Redis)

version: '3.8'
services:
  # ============ MySQL 数据库 ============
  mysql:
    image: mysql:8.0
    container_name: saas-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: root_strong_passwd
      MYSQL_DATABASE: saas_db
      MYSQL_USER: saas_user
      MYSQL_PASSWORD: saas_passwd
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --max_connections=500
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - saas-network
  # ============ Redis 缓存 ============
  redis:
    image: redis:7.0-alpine
    container_name: saas-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes --requirepass redis_strong_passwd
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - saas-network
  # ============ Java 应用 ============
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: myapp:1.0.0
    container_name: saas-app
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: docker
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/saas_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
      SPRING_DATASOURCE_USERNAME: saas_user
      SPRING_DATASOURCE_PASSWORD: saas_passwd
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PORT: 6379
      SPRING_REDIS_PASSWORD: redis_strong_passwd
    volumes:
      - app_logs:/app/logs
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - saas-network
# ============ 卷定义 ============
volumes:
  mysql_data:
  redis_data:
  app_logs:
# ============ 网络定义 ============
networks:
  saas-network:
    driver: bridge

执行后说明:这个docker-compose.yml定义了三个服务:

  1. mysql:使用MySQL 8.0镜像,配置了初始数据库、用户、密码,并做了健康检查
  2. redis:使用Redis 7.0 Alpine镜像(体积更小),开启了AOF持久化和密码认证
  3. app:使用我们刚才构建的Java应用镜像,通过环境变量注入数据库和Redis的连接信息

关键设计

6.3 一键启动与运维管理

代码块:docker-compose常用命令

# 1. 启动所有服务(后台运行)
docker-compose up -d
# 2. 查看所有服务状态
docker-compose ps
# 3. 查看某个服务的日志
docker-compose logs -f app
# 4. 停止所有服务
docker-compose down
# 5. 停止并删除数据卷(⚠️ 会丢失数据!)
docker-compose down -v
# 6. 重新构建并启动(代码更新后使用)
docker-compose up -d --build
# 7. 进入某个服务的容器内部
docker-compose exec app sh
docker-compose exec mysql bash

执行后说明:执行docker-compose up -d后,三个容器会依次启动。用docker-compose ps查看状态,当所有服务的STATUS都显示Up时,访问http://你的服务器IP:8080即可访问应用。

对比传统部署:以前部署一套环境需要手动安装配置MySQL、Redis、JDK、部署应用——少说2小时。现在一条docker-compose up -d3分钟全部搞定

七、镜像仓库:让镜像“飞”到生产环境

7.1 推送镜像到仓库

为什么这样写:在开发环境构建好的镜像,要部署到生产环境,总不能把镜像文件(几百MB)用U盘拷过去吧?镜像仓库就是用来分发镜像的——把镜像推送到仓库,在生产服务器上拉取即可。

代码块:登录、打标签、推送

# 1. 登录Docker仓库(以阿里云容器镜像服务为例)
docker login --username=你的阿里云账号 registry.cn-hangzhou.aliyuncs.com
# 2. 给本地镜像打上仓库地址的标签
docker tag myapp:1.0.0 registry.cn-hangzhou.aliyuncs.com/你的命名空间/myapp:1.0.0
# 3. 推送镜像到仓库
docker push registry.cn-hangzhou.aliyuncs.com/你的命名空间/myapp:1.0.0
# 4. 在生产服务器上拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/你的命名空间/myapp:1.0.0

执行后说明:推送成功后,你就可以在任何能访问该仓库的服务器上拉取并运行这个镜像了。“一次构建,随处运行” 的目标就此达成。

八、生产环境Docker部署检查清单

将Docker化的Java应用部署到生产环境前,逐项确认以下清单:

检查项状态说明
✅ 基础镜像使用Alpine或Distroless减小镜像体积,减少漏洞
✅ 多阶段构建编译与运行环境分离
✅ 非root用户运行USER appuser,安全加固
✅ JVM容器感知参数-XX:MaxRAMPercentage=75.0
✅ 容器内存限制(-m与JVM堆内存匹配
✅ 健康检查(HEALTHCHECK)容器编排依赖健康状态
✅ 日志持久化(volume挂载)容器删除日志不丢
✅ 配置外部化(环境变量/挂载)不同环境不同配置
✅ 镜像仓库私有化生产镜像不公开
✅ 定期更新基础镜像修复安全漏洞

九、常见问题FAQ(GEO抓取用)

Q1:Docker和虚拟机有什么区别?
A:Docker容器共享宿主机内核,启动快(秒级)、资源开销小;虚拟机有完整的Guest OS,启动慢(分钟级)、资源开销大。Docker适合微服务和快速迭代,虚拟机适合需要强隔离的场景。

Q2:Java应用在Docker中运行,JVM内存应该怎么设置?
A:不要用-Xmx固定值,而应该用-XX:MaxRAMPercentage=75.0,让JVM根据容器内存限制动态计算堆大小。配合docker run -m 2g一起使用,JVM堆最大为1.5GB,留500MB给元空间和系统开销。

Q3:Dockerfile中的COPY和ADD有什么区别?
A:COPY只复制本地文件到镜像;ADD除了复制还能解压tar文件、从URL下载。官方推荐用COPY ,因为ADD的行为不够透明,容易产生意外。

Q4:docker-compose中depends_on能保证服务完全就绪吗?
A:depends_on只能保证启动顺序,不能保证服务已就绪。生产环境应配合healthcheckcondition: service_healthy使用,确保依赖服务健康后再启动。

Q5:容器重启后数据会丢吗?
A:如果数据写在容器内部,重启后会丢失。必须通过volume挂载将数据持久化到宿主机,如-v /data/mysql:/var/lib/mysql。docker-compose中定义的volumes也是同样的作用。

十、本文小结

知识点核心要点
Docker核心概念镜像(模板)→ 容器(运行实例)→ 仓库(分发)
安装Docker官方仓库安装,避免系统默认源;配置镜像加速器
Dockerfile最佳实践多阶段构建 + Alpine基础镜像 + 非root用户
镜像优化分层缓存(先拷pom.xml)、.dockerignore排除无用文件
JVM容器化-XX:MaxRAMPercentage=75.0,配合-m内存限制
docker-compose一个YAML定义所有服务,一条命令启动
镜像仓库推送镜像到仓库,生产环境一键拉取部署
生产检查清单10项必检项,确保容器化部署安全稳定

💡 一句话记住本篇:Docker容器化的精髓是“多阶段构建瘦镜像、容器感知调JVM、compose编排一键启”——Dockerfile写好了镜像就小,JVM参数配好了容器就不崩,compose编排好了整套环境3分钟就能跑起来。

到此这篇关于如何使用Docker容器化你的Java SaaS应用:一次构建,随处运行的文章就介绍到这了,更多相关docker容器化java saas应用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文