GitHub Packages 发布 Maven 库:从 Token 配置到 BOM 管理

GitHub Packages 发布 Maven 库:从 Token 配置到 BOM 管理 目标 把一个多模块 Maven 项目发布到 GitHub Packages,并让其他项目通过 BOM 统一引用。全程使用 GitHub 免费额度,不搭私有 Nexus。 前置条件 条件 说明 GitHub 账号 一个,免费套餐即可 Personal Access Token write:packages + read:packages 权限 Maven 3.6+ 构建工具 Java 17+ 运行时 环境搭建 第 1 步:生成 GitHub Token GitHub 右上角头像 → Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token 勾选权限范围: ✅ write:packages (发布包) ✅ read:packages (下载包) ✅ repo (访问仓库,write:packages 自动依赖) Token 创建后只会显示一次,复制保存好。有效期的建议:如果用于本地开发,设 30 ~ 90 天;如果用于 CI/CD,设成永不过期并定期轮换。 ...

三月 10, 2023 · 3 分钟 · 553 字 · yaomingye

统一开发团队的流水线哲学

统一开发团队的流水线哲学:从 .gitlab-ci.yml 到 IDP 平台化治理 问题:为什么每个项目的流水线都长得不一样? 团队规模还小的时候,CI/CD 流水线通常是怎么来的?某个开发者把上个项目的 .gitlab-ci.yml 拷过来,改两行,能跑就行。再过两个月新开一个服务,又从那个改过的版本拷过去再改两行。一年下来,十个微服务有十种写法,review 流水线配置的时间比 review 业务代码还长。 这不是某个团队的个例,而是缺少 统一流水线规范 的必然结果。 造成这种混乱的根源有三层: 第一层,认知门槛。GitLab CI 的配置语法看似简单——stages、jobs、script、only/except,但真正写好需要理解 runner 的执行模型、cache 和 artifact 的区别、image 与 service 的作用域。大部分人止步于"能跑就行",不会主动深究。 第二层,缺乏约束。GitLab CI 本身不做 schema 校验,before_script 里写什么都行,Dockerfile 里的 RUN 指令堆多少层也没人管。没有门禁、没有模板、没有 review 机制,流水线质量完全依赖开发者个人习惯。 第三层,业务压力。“先把功能上线"永远排在"把流水线写好"前面。流水线的技术债不像业务代码那样直接影响用户,于是越欠越多,直到有一天构建 40 分钟没人敢动。 📌 前置知识——GitLab CI 基础:建议先理解 .gitlab-ci.yml 的 stages、jobs、script、image、cache、artifacts 六个核心关键字(只需理解各自的职责和生效范围即可)。 结构:一条理想流水线的骨架 先从最核心的问题开始: 一条"完美"的 .gitlab-ci.yml 应该长什么样? 答案不是给你一个 500 行的 YAML 文件,而是说清楚 原则。原则对了,具体写法可以按项目微调。 四阶段流水线 flowchart TD S([📥 代码提交]) --> A1 subgraph Stage1["🔍 验证阶段"] A1[📌 编译检查]:::process A2[📌 代码风格]:::process A3[📌 单元测试]:::process end Stage1 --> Stage2 subgraph Stage2["📦 构建阶段"] B1[📌 构建镜像]:::process B2[📌 推送镜像仓库]:::process B3[📌 导出制品]:::process end Stage2 --> Stage3 subgraph Stage3["🚀 部署阶段"] C1[📌 部署开发环境]:::highlight C2[📌 集成测试]:::process C3{📌 验收通过?}:::condition end C3 -->|✅ 是| Stage4 C3 -->|❌ 否| Rollback[⛔ 回滚通知]:::reject subgraph Stage4["📡 生产发布"] D1[📌 灰度发布]:::highlight D2[📌 全量上线]:::startEnd end classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#fce7f3,font-weight:bold; classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ede9fe,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; classDef reject fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; 四个阶段各司其职: ...

一月 29, 2023 · 6 分钟 · 1241 字 · yaomingye

从写完代码到上线运行

从写完代码到上线运行:SpringBoot微服务CI/CD完整链路 目标说明 这篇教程要解决一个很实际的问题:写完SpringBoot微服务代码之后,怎么把它弄到线上稳定运行? 很多开发者(尤其是刚入行的)对这块的认知是模糊的——“代码写完了,接下来是不是找个服务器丢上去就行了?” 实际过程远比这个复杂,涉及到测试验证、容器化、CI/CD流水线、配置中心、网关路由等一系列环节。 本教程将以一个典型的SpringBoot微服务项目为例,从代码提交前的本地测试开始,一步步走到Kubernetes集群上的生产环境部署。每个环节都给出完整可复制的脚本和配置文件,不跳步,不给半截代码。 ⚠️ 新手提示:这篇教程假设读者能独立用SpringBoot写CRUD接口,但对DevOps/运维侧的流程不熟悉。如果连SpringBoot项目怎么创建都还不太清楚,建议先去翻翻SpringBoot入门文档再回来看。 前置条件 开始之前,先确认本地环境是否满足以下条件。每项后面附了验证命令,直接在终端里跑一下就能确认。 序号 前置条件 最低版本 验证命令 说明 1 JDK 8+ java -version 编译和运行SpringBoot项目 2 Maven 3.6+ mvn -version 项目构建和依赖管理 3 Docker 20.10+ docker version 容器镜像构建 4 Git 2.30+ git version 版本控制和协作 5 SpringBoot项目 2.x mvn spring-boot:run 已有可正常启动的项目 6 kubectl 1.20+ kubectl version 部署阶段需要(可最后装) 📌 前置知识:Docker的基础概念(镜像、容器、仓库三者的关系)。如果不清楚,可以先跑一遍 docker run hello-world 感受一下,然后大致了解 docker build、docker push、docker pull 三条命令的作用。 环境搭建 开始实践之前,先把必要的环境准备到位。下面按依赖顺序逐步完成。 确认Docker环境 # 检查Docker是否安装并运行 docker version # 预期输出(版本号可能不同): # Client: Docker Engine - Community # Version: 20.10.16 # Server: Docker Engine - Community # Engine: # Version: 20.10.16 # 如果Docker daemon没启动,先启动它 # Linux: sudo systemctl start docker # Mac/Windows: 打开Docker Desktop 安装Docker Compose(用于本地集成测试) # 检查是否已安装 docker compose version # 预期输出:Docker Compose version v2.10.2 # 如果没有,参考官方文档安装: # https://docs.docker.com/compose/install/ 配置Maven settings.xml Maven默认从中央仓库拉依赖,在国内网络环境下可能很慢。建议配置国内镜像: ...

一月 25, 2023 · 13 分钟 · 2676 字 · yaomingye

GitLab CI/CD 多环境部署与生产实践

从 dev 一路跑到 prod——点个按钮就上线 📖 前置阅读:本文假设读者已搭建 GitLab CI/CD 流水线(编译 → 测试 → 扫描 → 构建镜像 → 推送 Harbor),并已将微服务部署在 Kubernetes 上。如果还不熟悉,建议先阅读 搭建与 Pipeline 语法精讲 和 流水线实战。 一、⚡ 镜像推到 Harbor 了——但你还得手动 SSH 上去 kubectl apply——这叫啥 CI/CD? 前两篇搭好了 CI/CD Pipeline——代码 push → 编译 → 测试 → 扫描 → 构建镜像 → 推送到 Harbor。 但 Pipeline 到这里就停了——后面的部署还是人来操作: 当前状态(半自动): ✅ 代码 push → 自动编译、测试、扫描、构建镜像、推送 Harbor ❌ 然后——SSH 到跳板机 → kubectl set image → 看有没有报错 ❌ 然后——curl 验证——发现不对——kubectl rollout undo ❌ 然后——staging 和 prod 没有隔离——改了什么全凭记忆力 → CI 有了——CD 没做——半吊子自动化 真正的 CD——镜像推送到 Harbor 后——自动部署到 dev——验证通过——自动部署到 staging——人工审批——部署到 prod。人对生产的操作只剩下"点一个按钮"。 ...

十二月 26, 2022 · 9 分钟 · 1903 字 · yaomingye

GitLab CI/CD 流水线实战——编译、扫描、构建镜像、推送仓库

代码 push 之后——五道关卡自动跑完 📖 前置阅读:本文假设读者已搭建 GitLab + Runner 并理解 .gitlab-ci.yml 基础语法(stages/jobs/artifacts/cache/rules)。如果还不熟悉,建议先阅读 GitLab CI/CD 搭建与 Pipeline 语法精讲。 一、⚡ 编译过了——但你敢直接部署吗?代码质量谁保证? 上一篇文章的 Pipeline 只做了编译和测试——但真正的 CI/CD 不止这些: 真正的 CI/CD 流水线要回答 5 个问题: ① 编译成功了吗? → mvn compile ② 测试通过了吗? → mvn test + 覆盖率报告 ③ 代码质量合格吗? → SonarQube 扫描 + Quality Gate ④ 镜像构建成功了吗? → docker build ⑤ 镜像推送到仓库了吗? → docker push → Harbor 这 5 步全自动——缺一步都不能算 CI/CD 这篇的目标——搭一条完整的流水线:代码 push → 自动跑完上述 5 步——任何一个环节失败——Pipeline 变红——阻止部署。 二、🏗️ 完整的 Pipeline 架构 flowchart LR Push["git push"] --> Compile["① 编译\nmvn compile"] Compile --> Test["② 单元测试\nmvn test\n+ 覆盖率报告"] Test --> SonarQube["③ 代码扫描\nSonarQube\n+ Quality Gate"] SonarQube --> Package["④ 打包\nmvn package"] Package --> DockerBuild["⑤ 构建镜像\ndocker build"] DockerBuild --> HarborPush["⑥ 推送仓库\ndocker push\n→ Harbor"] HarborPush --> Notify["⑦ 通知\n企业微信/钉钉"] classDef style_SonarQube fill:#450a0a,stroke:#dc2626,stroke-width:2px,color:#fecaca; classDef style_HarborPush fill:#0f172a,stroke:#3b82f6,stroke-width:2px,color:#bfdbfe; class SonarQube style_SonarQube; class HarborPush style_HarborPush;``` ## 三、🔧 基础设施——SonarQube + Harbor 搭建 ### 3.1 Docker Compose——加 SonarQube 和 Harbor ```yaml # 在上一篇文章的 docker-compose.yml 基础上加两个服务 version: '3.8' services: # ===== GitLab + Runner(同上一篇——省略)===== # ... # ===== SonarQube——代码质量扫描 ===== sonarqube: image: sonarqube:10.3.0-community container_name: sonarqube environment: SONAR_JDBC_URL: jdbc:postgresql://sonarqube-db:5432/sonarqube SONAR_JDBC_USERNAME: sonar SONAR_JDBC_PASSWORD: sonar123 ports: - "9000:9000" volumes: - sonarqube-data:/opt/sonarqube/data - sonarqube-extensions:/opt/sonarqube/extensions depends_on: - sonarqube-db sonarqube-db: image: postgres:15-alpine container_name: sonarqube-db environment: POSTGRES_USER: sonar POSTGRES_PASSWORD: sonar123 POSTGRES_DB: sonarqube volumes: - sonarqube-db-data:/var/lib/postgresql/data # ===== Harbor——私有 Docker 镜像仓库 ===== # Harbor 官方推荐用 docker-compose 独立部署——这里简化 # 生产环境参考 https://goharbor.io/docs harbor: image: goharbor/registry-photon:v2.9.0 container_name: harbor-registry ports: - "5000:5000" volumes: - harbor-data:/var/lib/registry volumes: sonarqube-data: sonarqube-extensions: sonarqube-db-data: harbor-data: 3.2 SonarQube 初始化——创建项目 Token ① 浏览器打开 http://gitlab.local:9000 ② 默认登录:admin / admin——首次强制修改密码 ③ Administration → Projects → Create Project → Project key: order-service → Project name: order-service → 创建 ④ 创建 Token:My Account → Security → Generate Token → Token name: gitlab-ci → 复制 Token——后续要放在 GitLab CI/CD 变量中 ⑤ 在 GitLab 中配置 SonarQube 变量: GitLab → 项目 → Settings → CI/CD → Variables 添加: SONAR_HOST_URL = http://sonarqube:9000 SONAR_TOKEN = squ_xxxxxxxxxxxxxxxxxxxxxxxxxx ← 刚才复制的 Token 3.3 Maven 项目的 SonarQube 配置 <!-- pom.xml——加 JaCoCo 覆盖率插件 + SonarQube 插件 --> <build> <plugins> <!-- JaCoCo——代码覆盖率 --> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.11</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <properties> <!-- SonarQube 配置 --> <sonar.host.url>${env.SONAR_HOST_URL}</sonar.host.url> <sonar.login>${env.SONAR_TOKEN}</sonar.login> <sonar.projectKey>order-service</sonar.projectKey> <sonar.projectName>order-service</sonar.projectName> <sonar.java.binaries>target/classes</sonar.java.binaries> <sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths> </properties> 四、📝 完整的 .gitlab-ci.yml——从编译到推送镜像 4.1 完整 Pipeline 定义 # order-service/.gitlab-ci.yml # 完整的 CI/CD Pipeline——5 个阶段 stages: - compile # ① 编译 - test # ② 测试 + 覆盖率 - quality # ③ SonarQube 扫描 - package # ④ 打包 + 构建镜像 - push # ⑤ 推送镜像到 Harbor # ===== 全局变量 ===== variables: MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository" MAVEN_CLI_OPTS: "-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN" # Harbor 地址——在 GitLab CI/CD Variables 中配置 HARBOR_URL: "harbor.local:5000" IMAGE_NAME: "$HARBOR_URL/order-service" # ===== 全局缓存——Maven 依赖 ===== cache: key: maven-${CI_COMMIT_REF_SLUG} paths: - .m2/repository/ policy: pull-push # ===== Stage 1: 编译 ===== compile: stage: compile image: maven:3.9-eclipse-temurin-17 script: - mvn $MAVEN_CLI_OPTS compile artifacts: paths: - target/classes/ expire_in: 1 hour tags: - docker # ===== Stage 2: 单元测试 + 覆盖率 ===== unit-test: stage: test image: maven:3.9-eclipse-temurin-17 script: - mvn $MAVEN_CLI_OPTS test jacoco:report artifacts: when: always paths: - target/surefire-reports/ - target/site/jacoco/ # ← JaCoCo 报告——给 SonarQube 用 expire_in: 7 days reports: junit: target/surefire-reports/TEST-*.xml # ← GitLab 自动展示测试结果 coverage: '/Total.*?([0-9]{1,3})%/' # ← GitLab 自动展示覆盖率百分比 tags: - docker # ===== Stage 3: SonarQube 代码扫描 ===== sonarqube-check: stage: quality image: maven:3.9-eclipse-temurin-17 script: - mvn $MAVEN_CLI_OPTS sonar:sonar -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_TOKEN # 只在 MR 或 main 分支扫描——feature 分支不扫(浪费 SonarQube 资源) rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == "main" tags: - docker # ===== Stage 4: 打包 ===== package: stage: package image: maven:3.9-eclipse-temurin-17 script: - mvn $MAVEN_CLI_OPTS package -DskipTests artifacts: paths: - target/*.jar expire_in: 1 hour tags: - docker # ===== Stage 5: 构建 Docker 镜像并推送到 Harbor ===== docker-build-push: stage: push image: docker:24-dind # ← Docker-in-Docker 镜像——在容器内跑 Docker services: - docker:24-dind # ← 启动 Docker daemon sidecar before_script: - apk add --no-cache bash # Alpine 需要 bash # 等待 Docker daemon 启动 - until docker info > /dev/null 2>&1; do sleep 1; done script: # ① 构建镜像——用 commit SHA 作为 tag - docker build -t $IMAGE_NAME:$CI_COMMIT_SHORT_SHA . # ② 打标签——如果 main 分支——打 latest;如果有 tag——打 release 版本 - | if [ "$CI_COMMIT_BRANCH" = "main" ]; then docker tag $IMAGE_NAME:$CI_COMMIT_SHORT_SHA $IMAGE_NAME:latest fi - | if [ -n "$CI_COMMIT_TAG" ]; then docker tag $IMAGE_NAME:$CI_COMMIT_SHORT_SHA $IMAGE_NAME:$CI_COMMIT_TAG fi # ③ 登录 Harbor——用户名密码配在 GitLab CI/CD Variables 中 - echo "$HARBOR_PASSWORD" | docker login $HARBOR_URL -u "$HARBOR_USERNAME" --password-stdin # ④ 推送所有标签 - docker push $IMAGE_NAME:$CI_COMMIT_SHORT_SHA - | if [ "$CI_COMMIT_BRANCH" = "main" ]; then docker push $IMAGE_NAME:latest fi - | if [ -n "$CI_COMMIT_TAG" ]; then docker push $IMAGE_NAME:$CI_COMMIT_TAG fi tags: - docker 4.2 Dockerfile——配合 CI/CD 的镜像构建 # order-service/Dockerfile # 多阶段构建——分离构建和运行——最终镜像只含 JRE # ===== Stage 1: 构建——用 Maven 编译 ===== FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /build COPY pom.xml . # 先下载依赖——利用 Docker 缓存层——pom.xml 不变就不重新下载 RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -B # ===== Stage 2: 运行——只含 JRE——镜像小 ===== FROM eclipse-temurin:17-jre-alpine WORKDIR /app # 创建非 root 用户——安全最佳实践 RUN addgroup -S appgroup && adduser -S appuser -G appgroup # 从构建阶段复制 jar COPY --from=builder /build/target/*.jar app.jar # 切换到非 root 用户 USER appuser # Health check——K8s 会调用这个 HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD wget -qO- http://localhost:8081/actuator/health || exit 1 EXPOSE 8081 ENTRYPOINT ["java", "-jar", "app.jar"] ⚠️ 新手提示:上面的 Dockerfile 是"CI 内编译"的方式——jar 包在 CI Pipeline 中由 Maven 打好——Dockerfile 只需要 COPY jar。还有一种方式是"Dockerfile 内编译"——CI 不编译——Dockerfile 用多阶段构建完成编译。两种方式的区别: ...

十二月 25, 2022 · 8 分钟 · 1615 字 · yaomingye

GitLab CI/CD 搭建与 Pipeline 语法精讲

把 15 步手动操作变成一次 git push 一、⚡ 周五下午 5 点上线——你手动执行了 15 步操作——到第 12 步出错了 回想一下你现在的发布流程: 发布一个微服务的流程: ① git pull latest ② mvn clean package -DskipTests("测试先跳过——着急") ③ 手动改 application-prod.yml 中的配置("这个值上次没改对") ④ docker build -t order-service:v1.2.3 . ⑤ docker tag order-service:v1.2.3 harbor.internal/order-service:v1.2.3 ⑥ docker push harbor.internal/order-service:v1.2.3 ⑦ ssh root@k8s-master ⑧ kubectl set image deployment/order-service order-service=harbor.internal/order-service:v1.2.3 ⑨ kubectl rollout status deployment/order-service ⑩ curl 验证——啊——404——服务没起来 ⑪ kubectl logs——发现是 application.yml 中的 Nacos 地址配错了 ⑫ kubectl rollout undo——回滚 ⑬ 改配置——重新来——docker build + push + deploy ⑭ 又发现 product-service 没同步上线——接口报错了 ⑮ 告警响了——用户已经在群里骂了 → 每次发布都像拆炸弹——不知道哪一步会出问题 CI/CD 要解决的就是:把人从 15 步中解放出来——每次 git push——自动编译、自动测试、自动构建镜像、自动部署——15 步变成 1 步。 ...

十二月 24, 2022 · 9 分钟 · 1883 字 · yaomingye
Cat Radio