From a3b03ee0b9ec94ef1e1545a2bf630017956378c8 Mon Sep 17 00:00:00 2001 From: sunlei Date: Fri, 15 May 2026 19:45:33 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E6=8B=86=E5=88=86=20Jenkins=20=E4=B8=8E?= =?UTF-8?q?=20Agent=20=E8=81=8C=E8=B4=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Jenkinsfile | 6 +-- ci/jenkins-agent/Dockerfile | 27 +++++++++++ ci/jenkins-agent/README.md | 91 +++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 ci/jenkins-agent/Dockerfile create mode 100644 ci/jenkins-agent/README.md diff --git a/.gitignore b/.gitignore index 776a82b..db53b3c 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ lerna-debug.log* .env .env.* !.env.example +ci/jenkins-agent/*.env diff --git a/Jenkinsfile b/Jenkinsfile index 712908b..90d7097 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,7 +22,7 @@ def isPublishBranch(String branchName, String pattern) { } pipeline { - agent any + agent { label 'kt-node-agent' } options { skipDefaultCheckout(true) @@ -33,7 +33,7 @@ pipeline { parameters { booleanParam(name: 'BUILD_DOCKER_IMAGE', defaultValue: true, description: '是否在非 PR 分支使用项目现有 dockerfile 构建镜像') - booleanParam(name: 'PUSH_DOCKER_IMAGE', defaultValue: false, description: '是否执行 docker push;仅发布分支生效,需要 Jenkins Agent 已提前完成 docker login') + booleanParam(name: 'PUSH_DOCKER_IMAGE', defaultValue: false, description: '是否执行 docker push;仅发布分支生效,需要 Agent 已提前完成 docker login') string(name: 'PUBLISH_BRANCH_PATTERN', defaultValue: '^(main|master|release/.+)$', description: '允许推送镜像的分支正则') string(name: 'DOCKER_REGISTRY', defaultValue: '', description: '镜像仓库地址,为空时只生成本地镜像') string(name: 'IMAGE_NAME', defaultValue: 'kt-template-online-api', description: 'Docker 镜像名称') @@ -67,7 +67,7 @@ pipeline { def registry = params.DOCKER_REGISTRY?.trim() env.DOCKER_IMAGE = registry ? "${registry}/${params.IMAGE_NAME}:${env.IMAGE_TAG_FINAL}" : "${params.IMAGE_NAME}:${env.IMAGE_TAG_FINAL}" - // 项目以 pnpm-lock.yaml 为准;Agent 未安装 pnpm 时再通过 Corepack 启用 pnpm 9。 + // Agent 由 NAS 侧预先创建;这里仅确认 CI 所需的 Node/pnpm 环境可用。 if (isUnix()) { runCmd(""" node --version diff --git a/ci/jenkins-agent/Dockerfile b/ci/jenkins-agent/Dockerfile new file mode 100644 index 0000000..125847f --- /dev/null +++ b/ci/jenkins-agent/Dockerfile @@ -0,0 +1,27 @@ +FROM jenkins/inbound-agent:latest-jdk21 + +USER root + +ARG NODE_MAJOR=22 +ARG PNPM_VERSION=9 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl git gnupg openssh-client \ + && install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \ + && chmod a+r /etc/apt/keyrings/docker.gpg \ + && . /etc/os-release \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs docker-ce-cli docker-buildx-plugin docker-compose-plugin \ + && corepack enable \ + && corepack prepare pnpm@${PNPM_VERSION} --activate \ + && node --version \ + && pnpm --version \ + && docker --version \ + && docker compose version \ + && rm -rf /var/lib/apt/lists/* + +USER jenkins diff --git a/ci/jenkins-agent/README.md b/ci/jenkins-agent/README.md new file mode 100644 index 0000000..2ad1a20 --- /dev/null +++ b/ci/jenkins-agent/README.md @@ -0,0 +1,91 @@ +# Jenkins Agent 镜像 + +这个目录只负责提供 NAS 上创建 Jenkins Agent 所需的镜像和启动说明。Jenkinsfile 只做 CI,不再创建或更新 Agent 节点。 + +Agent 镜像内置: + +- Jenkins inbound agent +- Git / OpenSSH client +- Node.js 22 +- pnpm 9 +- Docker CLI / Buildx / Compose plugin + +项目业务镜像仍然使用仓库根目录的 `dockerfile`。本目录的 Dockerfile 是给 Jenkins Agent 用的,不是后端服务运行镜像。 + +## Jenkins 侧配置 + +在 Jenkins 页面手动创建节点: + +```text +Manage Jenkins -> Nodes -> New Node +Node name: kt-node-agent +Type: Permanent Agent +Remote root directory: /home/jenkins/agent +Labels: kt-node-agent nodejs docker +Usage: Only build jobs with label expressions matching this node +Launch method: Launch agent by connecting it to the controller +``` + +保存后进入节点页面,复制 inbound agent 的 `secret`。Jenkinsfile 会通过下面的标签调度到这个节点: + +```groovy +agent { label 'kt-node-agent' } +``` + +## NAS 侧构建镜像 + +在 NAS 上准备 Docker 环境,然后从仓库根目录执行: + +```bash +docker build -t kt-jenkins-agent:node22 -f ci/jenkins-agent/Dockerfile ci/jenkins-agent +``` + +## NAS 侧启动 Agent + +如果 Jenkins Controller 容器名是 `jenkins`,并且你希望 Agent 通过 Docker 网络访问 Jenkins,可以先准备网络: + +```bash +docker network create jenkins +docker network connect jenkins jenkins +``` + +启动 Agent 容器。你的 Jenkins Controller compose 暴露的是 `18080:8080`,如果 Agent 和 Jenkins 在同一个 Docker 网络,容器内仍然使用 `http://jenkins:8080/`;如果 Agent 不在同一个网络,使用 NAS/服务器可访问地址,例如 `http://Jenkins服务器IP:18080/`。 + +```bash +docker run -d \ + --name kt-node-agent \ + --restart=always \ + --network jenkins \ + -u root \ + -e JENKINS_URL=http://jenkins:8080/ \ + -e JENKINS_AGENT_NAME=kt-node-agent \ + -e JENKINS_SECRET=替换成节点页面里的secret \ + -e JENKINS_AGENT_WORKDIR=/home/jenkins/agent \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v kt-node-agent-workdir:/home/jenkins/agent \ + kt-jenkins-agent:node22 +``` + +如果 Jenkins Controller 不在同一台 NAS 上,把 `JENKINS_URL` 改成 Agent 容器可访问的 Jenkins 地址,例如: + +```bash +-e JENKINS_URL=http://192.168.1.10:18080/ +``` + +如果 NAS 上的 Agent 需要执行 `docker build`,保留 `/var/run/docker.sock` 挂载。这个挂载等同于让 Agent 拥有 NAS Docker 控制权限,只建议放在可信内网环境。 + +## 验证 + +查看 Agent 日志: + +```bash +docker logs -f kt-node-agent +``` + +Jenkins 页面确认节点在线: + +```text +Manage Jenkins -> Nodes -> kt-node-agent +``` + +节点在线后,多分支流水线点击构建即可进入 CI 阶段。