微服务 Docker 容器化部署与 CI/CD 上线开发“在我电脑上跑得好好儿的” 运维“你那叫开发环境我这叫生产环境两者之间隔了100个环境变量、50个依赖版本和无数个’我以为是一样的’。” Docker 站出来说“别吵了打包一下——到我这儿都是同一个环境。”一、Docker 为什么是微服务的最佳拍档微服务架构下你有 10 个 Java 服务、2 个前端、1 个 Python AI 引擎。每个服务依赖不同的运行时版本、环境变量、系统库。传统部署方式你得上 13 台虚拟机/物理机每台安装不同的 JDK 版本——这是运维噩梦。Docker 解决的就是这个环境不一致问题痛点Docker 怎么解决“我机器上能跑”镜像包含完整运行环境部署慢容器秒级启动VM 要分钟级资源浪费共享宿主机内核比VM轻量近百倍配置不一致镜像环境变量完全可控回滚难旧镜像还在秒级回滚二、核心概念速览概念一句话类比镜像Image打包好的程序环境只读一个安装包容器Container镜像的运行时实例安装后打开的程序仓库Registry存放和分发镜像的地方程序下载站Dockerfile镜像的构建配方安装脚本Docker Compose多容器编排定义文件一键启动全家桶三、Dockerfile 编写实战# 多阶段构建阶段1 —— Maven构建 FROM maven:3.8.6-openjdk-17-slim AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests # 阶段2 —— 运行镜像只保留JAR包 FROM eclipse-temurin:17-jdk-alpine RUN apk add --no-cache curl # 健康检查用 WORKDIR /app COPY --frombuilder /build/target/*.jar app.jar # 非 root 用户运行安全最佳实践 RUN addgroup --system app adduser --system --ingroup app app USER app EXPOSE 8080 ENTRYPOINT [java, -jar, -Dspring.profiles.active${PROFILE}, app.jar]多阶段构建的好处第一阶段用 Maven 镜像编译800MB但最终运行镜像只保留 JRE JAR 包200MB。编译工具链全扔了镜像瘦身 75%。构建和运行# 构建镜像dockerbuild-torder-service:1.0.0.# 运行容器dockerrun-d\--nameorder-service\-p8080:8080\-ePROFILEprod\-eNACOS_ADDR192.168.1.100:8848\order-service:1.0.0四、Docker Compose 一键编排一个微服务项目通常包含 6-8 个服务外加依赖中间件。手动 docker run 每个容器配网络、配依赖、配环境变量——键盘都能敲冒烟。docker-compose.yml让你一条命令搞定一切version:3.8services:nacos:image:nacos/nacos-server:v2.2.3environment:MODE:standaloneports:-8848:8848mysql:image:mysql:8.0environment:MYSQL_ROOT_PASSWORD:root123ports:-3306:3306volumes:-mysql_data:/var/lib/mysqlredis:image:redis:7.0ports:-6379:6379command:redis-server--requirepass redis123gateway:build:./gatewayports:-80:8080environment:NACOS_ADDR:nacos:8848depends_on:-nacosorder-service:build:./order-serviceports:-8081:8080environment:PROFILE:prodNACOS_ADDR:nacos:8848DB_URL:jdbc:mysql://mysql:3306/order_dbREDIS_ADDR:redis:6379depends_on:-nacos-mysql-redisproduct-service:build:./product-serviceports:-8082:8080environment:PROFILE:prodNACOS_ADDR:nacos:8848depends_on:-nacos-mysqlvolumes:mysql_data:关键点depends_on控制启动顺序但不等待服务就绪生产环境建议用 healthcheck容器间通过服务名直接通信Docker 内置 DNS 自动解析如nacos:8848volumes持久化 MySQL 数据容器删除后数据不丢动命令dockercompose up-d# 后台启动所有服务dockercomposeps# 查看运状态dockercompose logs-forder-service# 查看某个服务的日志dockercompose down# 停止并删除所有容器五、CI/CD从人肉部署到自动化流水线CI/CD 就是让你的代码从提交到上线全自动化。一条流水线 Click Wait取代打JAR包→上传服务器→杀进程→启动→祈祷的手动操作。代码提交 → 自动构建 → 自动测试 → 自动打包镜像 → 自动推送到仓库 → 自动部署 │ │ └──────────────── 持续集成(CI) ──────────────┘ │ └──────────────── 持续交付(CD) ──────────────────────────────┘六、GitLab CI/CD 配置# .gitlab-ci.ymlstages:-test-build-docker-deployvariables:MAVEN_OPTS:-Dmaven.repo.local$CI_PROJECT_DIR/.m2IMAGE:registry.example.com/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA# 阶段1单元测试test:stage:testimage:maven:3.8.6-openjdk-17-slimscript:-mvn testartifacts:reports:junit:target/surefire-reports/*.xml# 阶段2编译打包build:stage:buildimage:maven:3.8.6-openjdk-17-slimscript:-mvn clean package-DskipTestsartifacts:paths:-target/*.jaronly:-main-tags# 阶段3构建 Docker 镜像并推送docker-build:stage:dockerimage:docker:24.0.5services:-docker:24.0.5-dindscript:-docker build-t $IMAGE .-docker login-u $CI_REGISTRY_USER-p $CI_REGISTRY_PASSWORD registry.example.com-docker push $IMAGEonly:-main# 阶段4自动部署到服务器deploy:stage:deployimage:alpine:3.18before_script:-apk add--no-cache openssh-clientscript:-mkdir-p ~/.ssh-echo $SSH_PRIVATE_KEY~/.ssh/id_rsachmod 600 ~/.ssh/id_rsa-ssh-o StrictHostKeyCheckingno deploy192.168.1.100 docker pull $IMAGEdocker stop order-service||truedocker rm order-service||truedocker run-d--name order-service--network micro_net-e PROFILEprod-e NACOS_ADDR192.168.1.100:8848$IMAGEonly:-main这条流水线实现push 代码到 main 分支 → 自动测 → 自动编译 → 自动打镜像 → 自动推镜像仓库 → 自动拉取/重启容器。七、Jenkins Pipeline 方案如果你的团队用的是 Jenkins 而不是 GitLab CI下面是一个声明式流水线pipeline{agent any environment{IMAGEorder-service:${env.BUILD_NUMBER}REGISTRYregistry.example.com}stages{stage(拉取代码){steps{git url:https://gitlab.example.com/order-service.git}}stage(Maven 构建){steps{shmvn clean package -DskipTests}}stage(Docker 镜像){steps{shdocker build -t${IMAGE}.shdocker tag${IMAGE}${REGISTRY}/${IMAGE}}}stage(推送仓库){steps{withCredentials([string(credentialsId:docker-registry,variable:PWD)]){shdocker login -u admin -p${PWD}${REGISTRY}shdocker push${REGISTRY}/${IMAGE}}}}stage(部署到生产){steps{sh docker pull ${REGISTRY}/${IMAGE} docker stop order-service || true docker rm order-service || true docker run -d --name order-service -p 8080:8080 ${REGISTRY}/${IMAGE} }}}post{success{echo部署成功}failure{echo构建失败请检查日志}}}八、部署策略策略做法适用滚动更新逐个替换旧容器不停服常规发布最常用蓝绿部署保留旧版本新版本就绪后切流量重要系统快速回滚金丝雀发布新版本先放 5% 流量没问题再全量高风险变更逐步验证配合 Kubernetes 或 Docker Swarm这些策略都是原生支持。用 Docker Compose 手动部署的话滚动更新最简单dockercompose up-d--no-deps --force-recreate--buildorder-service九、完整 CI/CD 流程速览Git Push ──┬── GitLab CI 触发 ── 执行 .gitlab-ci.yml ──┐ │ │ ├── 编译 → 测试 → 打镜像 → 推送 Harbor ───────┤ │ │ └── SSH 到服务器 → docker pull → docker run ──┘ │ ▼ □ 应用更新完成一条git push做完所有事开发人员再也不用碰生产服务器——这是 CI/CD 最核心的价值。小结Docker 是微服务的标准集装箱CI/CD 是自动化上线的传送带。从 Dockerfile 到 Docker Compose从 GitLab CI 到 Jenkins Pipeline一条自动化的流水线让团队从手动部署2小时变成push代码2分钟上线。但记住工具只是手段真正的目的不是速度而是可靠性和可重复性——你今天上午部署成功的流程半夜3点按下去也应该是同样的结果。
微服务 Docker 容器化部署与 CI/CD 上线
微服务 Docker 容器化部署与 CI/CD 上线开发“在我电脑上跑得好好儿的” 运维“你那叫开发环境我这叫生产环境两者之间隔了100个环境变量、50个依赖版本和无数个’我以为是一样的’。” Docker 站出来说“别吵了打包一下——到我这儿都是同一个环境。”一、Docker 为什么是微服务的最佳拍档微服务架构下你有 10 个 Java 服务、2 个前端、1 个 Python AI 引擎。每个服务依赖不同的运行时版本、环境变量、系统库。传统部署方式你得上 13 台虚拟机/物理机每台安装不同的 JDK 版本——这是运维噩梦。Docker 解决的就是这个环境不一致问题痛点Docker 怎么解决“我机器上能跑”镜像包含完整运行环境部署慢容器秒级启动VM 要分钟级资源浪费共享宿主机内核比VM轻量近百倍配置不一致镜像环境变量完全可控回滚难旧镜像还在秒级回滚二、核心概念速览概念一句话类比镜像Image打包好的程序环境只读一个安装包容器Container镜像的运行时实例安装后打开的程序仓库Registry存放和分发镜像的地方程序下载站Dockerfile镜像的构建配方安装脚本Docker Compose多容器编排定义文件一键启动全家桶三、Dockerfile 编写实战# 多阶段构建阶段1 —— Maven构建 FROM maven:3.8.6-openjdk-17-slim AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests # 阶段2 —— 运行镜像只保留JAR包 FROM eclipse-temurin:17-jdk-alpine RUN apk add --no-cache curl # 健康检查用 WORKDIR /app COPY --frombuilder /build/target/*.jar app.jar # 非 root 用户运行安全最佳实践 RUN addgroup --system app adduser --system --ingroup app app USER app EXPOSE 8080 ENTRYPOINT [java, -jar, -Dspring.profiles.active${PROFILE}, app.jar]多阶段构建的好处第一阶段用 Maven 镜像编译800MB但最终运行镜像只保留 JRE JAR 包200MB。编译工具链全扔了镜像瘦身 75%。构建和运行# 构建镜像dockerbuild-torder-service:1.0.0.# 运行容器dockerrun-d\--nameorder-service\-p8080:8080\-ePROFILEprod\-eNACOS_ADDR192.168.1.100:8848\order-service:1.0.0四、Docker Compose 一键编排一个微服务项目通常包含 6-8 个服务外加依赖中间件。手动 docker run 每个容器配网络、配依赖、配环境变量——键盘都能敲冒烟。docker-compose.yml让你一条命令搞定一切version:3.8services:nacos:image:nacos/nacos-server:v2.2.3environment:MODE:standaloneports:-8848:8848mysql:image:mysql:8.0environment:MYSQL_ROOT_PASSWORD:root123ports:-3306:3306volumes:-mysql_data:/var/lib/mysqlredis:image:redis:7.0ports:-6379:6379command:redis-server--requirepass redis123gateway:build:./gatewayports:-80:8080environment:NACOS_ADDR:nacos:8848depends_on:-nacosorder-service:build:./order-serviceports:-8081:8080environment:PROFILE:prodNACOS_ADDR:nacos:8848DB_URL:jdbc:mysql://mysql:3306/order_dbREDIS_ADDR:redis:6379depends_on:-nacos-mysql-redisproduct-service:build:./product-serviceports:-8082:8080environment:PROFILE:prodNACOS_ADDR:nacos:8848depends_on:-nacos-mysqlvolumes:mysql_data:关键点depends_on控制启动顺序但不等待服务就绪生产环境建议用 healthcheck容器间通过服务名直接通信Docker 内置 DNS 自动解析如nacos:8848volumes持久化 MySQL 数据容器删除后数据不丢动命令dockercompose up-d# 后台启动所有服务dockercomposeps# 查看运状态dockercompose logs-forder-service# 查看某个服务的日志dockercompose down# 停止并删除所有容器五、CI/CD从人肉部署到自动化流水线CI/CD 就是让你的代码从提交到上线全自动化。一条流水线 Click Wait取代打JAR包→上传服务器→杀进程→启动→祈祷的手动操作。代码提交 → 自动构建 → 自动测试 → 自动打包镜像 → 自动推送到仓库 → 自动部署 │ │ └──────────────── 持续集成(CI) ──────────────┘ │ └──────────────── 持续交付(CD) ──────────────────────────────┘六、GitLab CI/CD 配置# .gitlab-ci.ymlstages:-test-build-docker-deployvariables:MAVEN_OPTS:-Dmaven.repo.local$CI_PROJECT_DIR/.m2IMAGE:registry.example.com/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA# 阶段1单元测试test:stage:testimage:maven:3.8.6-openjdk-17-slimscript:-mvn testartifacts:reports:junit:target/surefire-reports/*.xml# 阶段2编译打包build:stage:buildimage:maven:3.8.6-openjdk-17-slimscript:-mvn clean package-DskipTestsartifacts:paths:-target/*.jaronly:-main-tags# 阶段3构建 Docker 镜像并推送docker-build:stage:dockerimage:docker:24.0.5services:-docker:24.0.5-dindscript:-docker build-t $IMAGE .-docker login-u $CI_REGISTRY_USER-p $CI_REGISTRY_PASSWORD registry.example.com-docker push $IMAGEonly:-main# 阶段4自动部署到服务器deploy:stage:deployimage:alpine:3.18before_script:-apk add--no-cache openssh-clientscript:-mkdir-p ~/.ssh-echo $SSH_PRIVATE_KEY~/.ssh/id_rsachmod 600 ~/.ssh/id_rsa-ssh-o StrictHostKeyCheckingno deploy192.168.1.100 docker pull $IMAGEdocker stop order-service||truedocker rm order-service||truedocker run-d--name order-service--network micro_net-e PROFILEprod-e NACOS_ADDR192.168.1.100:8848$IMAGEonly:-main这条流水线实现push 代码到 main 分支 → 自动测 → 自动编译 → 自动打镜像 → 自动推镜像仓库 → 自动拉取/重启容器。七、Jenkins Pipeline 方案如果你的团队用的是 Jenkins 而不是 GitLab CI下面是一个声明式流水线pipeline{agent any environment{IMAGEorder-service:${env.BUILD_NUMBER}REGISTRYregistry.example.com}stages{stage(拉取代码){steps{git url:https://gitlab.example.com/order-service.git}}stage(Maven 构建){steps{shmvn clean package -DskipTests}}stage(Docker 镜像){steps{shdocker build -t${IMAGE}.shdocker tag${IMAGE}${REGISTRY}/${IMAGE}}}stage(推送仓库){steps{withCredentials([string(credentialsId:docker-registry,variable:PWD)]){shdocker login -u admin -p${PWD}${REGISTRY}shdocker push${REGISTRY}/${IMAGE}}}}stage(部署到生产){steps{sh docker pull ${REGISTRY}/${IMAGE} docker stop order-service || true docker rm order-service || true docker run -d --name order-service -p 8080:8080 ${REGISTRY}/${IMAGE} }}}post{success{echo部署成功}failure{echo构建失败请检查日志}}}八、部署策略策略做法适用滚动更新逐个替换旧容器不停服常规发布最常用蓝绿部署保留旧版本新版本就绪后切流量重要系统快速回滚金丝雀发布新版本先放 5% 流量没问题再全量高风险变更逐步验证配合 Kubernetes 或 Docker Swarm这些策略都是原生支持。用 Docker Compose 手动部署的话滚动更新最简单dockercompose up-d--no-deps --force-recreate--buildorder-service九、完整 CI/CD 流程速览Git Push ──┬── GitLab CI 触发 ── 执行 .gitlab-ci.yml ──┐ │ │ ├── 编译 → 测试 → 打镜像 → 推送 Harbor ───────┤ │ │ └── SSH 到服务器 → docker pull → docker run ──┘ │ ▼ □ 应用更新完成一条git push做完所有事开发人员再也不用碰生产服务器——这是 CI/CD 最核心的价值。小结Docker 是微服务的标准集装箱CI/CD 是自动化上线的传送带。从 Dockerfile 到 Docker Compose从 GitLab CI 到 Jenkins Pipeline一条自动化的流水线让团队从手动部署2小时变成push代码2分钟上线。但记住工具只是手段真正的目的不是速度而是可靠性和可重复性——你今天上午部署成功的流程半夜3点按下去也应该是同样的结果。