用 Jenkins 实现自动化 CI/CD_jenkins api自动执行
off999 2025-10-14 03:37 2 浏览 0 评论
场景设定(可替换为你的技术栈)
- 语言:Node.js(示例简单,任何语言思路一致)
- 制品:Docker 镜像(推送到 Docker Hub/Harbor)
- 运行环境:Kubernetes(staging + prod 两个命名空间)
- 流程:Push 触发 → 单元测试 → Lint → 构建镜像 → Trivy 安全扫描 → 推送镜像 → Helm 部署到 Staging → 人工审批 → 部署到 Prod → 通知
1. 环境准备
1.1 服务器要求
- 一台 Linux 主机(Ubuntu 22.04 例子)
- 已安装:docker、docker compose、kubectl、helm(Jenkins 也会用到)
- 有一个可访问的镜像仓库(Docker Hub/Harbor)
- 有可访问的 K8s 集群(含 staging 与 prod 两个 namespace)
# Docker / Compose(示例)
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
# ...(略:Docker 官方安装步骤)
docker --version
docker compose version
1.2 用 Docker Compose 启动 Jenkins(含 Docker 构建能力)
这种方式简单、隔离好、可快速恢复。
docker-compose.yml
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins
user: root
ports:
- "8080:8080"
- "50000:50000" # JNLP agent 端口
volumes:
- jenkins_home:/var/jenkins_home
-
/var/run/docker.sock:/var/run/docker.sock # 让 Jenkins 内能使用宿主的 docker
- /usr/local/bin/kubectl:/usr/local/bin/kubectl
- /usr/local/bin/helm:/usr/local/bin/helm
environment:
- JAVA_OPTS=-Dorg.jenkinsci.plugins.durabletask.BourneShellScript.LAUNCH_DIAGNOSTICS=true
volumes:
jenkins_home:
docker compose up -d
docker logs -f jenkins # 取初始管理员密码
# 初始密码文件路径(容器内):
# /var/jenkins_home/secrets/initialAdminPassword
1.3 Jenkins 插件建议安装
- Pipeline, Git, Blue Ocean, Credentials Binding
- Docker Pipeline, Docker, Kubernetes, Kubernetes CLI
- Pipeline: Stage View, Timestamper
- Warnings NG(代码质量)
- Generic Webhook Trigger(或使用 GitHub 插件)
- Slack(或企业微信/钉钉通知插件)
1.4 Jenkins 凭据准备(Credentials)
在 Manage Jenkins → Credentials 新建:
- dockerhub-cred:类型 Username/Password,用于 docker login
- kubeconfig:Secret file(上传 kubeconfig)
- slack-webhook:Secret text(可选)
- git-ssh:SSH Username with private key(如果仓库用 SSH)
2. 示例项目源码
2.1 目录结构
demo-ci-cd/
├─ app/
│ ├─ app.js
│ ├─ package.json
│ └─ __tests__/app.test.js
├─ Dockerfile
├─ helm/
│ └─ demo/
│ ├─ Chart.yaml
│ ├─ values.yaml
│ └─ templates/
│ ├─ deployment.yaml
│ ├─ service.yaml
│ └─ ingress.yaml # 可选
└─ Jenkinsfile
2.2 Node.js 应用与测试
app/app.js
const express = require('express');
const app = express();
app.get('/', (_, res) => res.send('Hello CI/CD v1'));
app.get('/healthz', (_, res) => res.status(200).send('ok'));
const port = process.env.PORT || 3000;
if (require.main === module) {
app.listen(port, () => console.log(`Server on :${port}`));
}
module.exports = app;
app/package.json
{
"name": "demo-ci-cd",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "jest --runInBand",
"lint": "npx eslint . || true"
},
"dependencies": {
"express": "^4.19.2"
},
"devDependencies": {
"jest": "^29.7.0",
"supertest": "^6.3.3",
"eslint": "^9.8.0"
}
}
app/tests/app.test.js
const request = require('supertest');
const app = require('../app');
describe('GET /', () => {
it('returns hello', async () => {
const res = await request(app).get('/');
expect(res.statusCode).toBe(200);
expect(res.text).toMatch(/Hello CI\/CD/);
});
});
describe('GET /healthz', () => {
it('returns ok', async () => {
const res = await request(app).get('/healthz');
expect(res.statusCode).toBe(200);
});
});
2.3 Dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY app/package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY app/ .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/healthz || exit 1
CMD ["node", "app.js"]
2.4 Helm Chart(K8s 部署)
helm/demo/Chart.yaml
apiVersion: v2
name: demo
version: 0.1.0
appVersion: "1.0.0"
helm/demo/values.yaml
image:
repository: your-dockerhub-username/demo-ci-cd
tag: "latest"
pullPolicy: IfNotPresent
replicaCount: 2
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: false
className: ""
hosts:
- host: demo.example.com
paths:
- path: /
pathType: Prefix
resources: {}
helm/demo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "demo.fullname" . }}
labels: { app: {{ include "demo.name" . }} }
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels: { app: {{ include "demo.name" . }} }
template:
metadata:
labels: { app: {{ include "demo.name" . }} }
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 3000
readinessProbe:
httpGet: { path: /healthz, port: 3000 }
initialDelaySeconds: 3
livenessProbe:
httpGet: { path: /healthz, port: 3000 }
initialDelaySeconds: 10
helm/demo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "demo.fullname" . }}
spec:
type: {{ .Values.service.type }}
selector: { app: {{ include "demo.name" . }} }
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
helm/demo/templates/ingress.yaml(可选)
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "demo.fullname" . }}
spec:
ingressClassName: {{ .Values.ingress.className }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "demo.fullname" $ }}
port: { number: {{ $.Values.service.port }} }
{{- end }}
{{- end }}
{{- end }}
3. Jenkins Pipeline(Jenkinsfile)
Jenkinsfile(Declarative,含并行测试、Trivy 扫描、Helm 部署与人工审批)
pipeline {
agent any
options {
timestamps()
ansiColor('xterm')
buildDiscarder(logRotator(numToKeepStr: '30'))
}
environment {
REGISTRY = 'docker.io'
IMAGE = '
your-dockerhub-username/demo-ci-cd' // ← 改成你的仓库
COMMIT = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
DOCKER_CREDS = 'dockerhub-cred'
KUBECONFIG_CRED = 'kubeconfig'
CHART_DIR = 'helm/demo'
APP_NS_STG = 'staging'
APP_NS_PROD = 'prod'
TRIVY_CACHE_DIR = "${WORKSPACE}/.trivycache"
}
stages {
stage('Checkout') {
steps {
checkout scm
sh 'node -v || true'
}
}
stage('Install deps & Lint & Test') {
parallel {
stage('Install deps') {
steps { sh 'cd app && npm ci' }
}
stage('Lint') {
steps { sh 'cd app && npm run lint' }
}
stage('Unit Test') {
steps { sh 'cd app && npm test -- --ci --reporters=default' }
post { always { junit '**/junit*.xml' /* 如果用 jest-junit 可生成 */ } }
}
}
}
stage('Build Docker Image') {
steps {
script {
docker.withServer('unix:///var/run/docker.sock') {
withCredentials([usernamePassword(credentialsId: DOCKER_CREDS, usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh '''
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin
docker build -t $IMAGE:$COMMIT -t $IMAGE:latest .
'''
}
}
}
}
}
stage('Security Scan (Trivy)') {
steps {
sh '''
mkdir -p "$TRIVY_CACHE_DIR"
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$TRIVY_CACHE_DIR":/root/.cache/ aquasec/trivy:0.54.1 image --exit-code 0 --severity HIGH,CRITICAL "$IMAGE:$COMMIT"
'''
}
}
stage('Push Image') {
steps {
withCredentials([usernamePassword(credentialsId: DOCKER_CREDS, usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh '''
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin
docker push $IMAGE:$COMMIT
docker push $IMAGE:latest
'''
}
}
}
stage('Deploy to Staging') {
steps {
withCredentials([file(credentialsId: KUBECONFIG_CRED, variable: 'KUBECONFIG_FILE')]) {
sh '''
export KUBECONFIG="$KUBECONFIG_FILE"
helm upgrade --install demo-stg "$CHART_DIR" \
--namespace "$APP_NS_STG" --create-namespace \
--set image.repository=$IMAGE --set image.tag=$COMMIT
kubectl -n "$APP_NS_STG" rollout status deploy/demo
kubectl -n "$APP_NS_STG" get svc,po -o wide
'''
}
}
}
stage('Manual Approval for Prod') {
steps {
timeout(time: 30, unit: 'MINUTES') {
input message: "是否发布到生产?镜像: $IMAGE:$COMMIT", ok: '发布'
}
}
}
stage('Deploy to Prod') {
steps {
withCredentials([file(credentialsId: KUBECONFIG_CRED, variable: 'KUBECONFIG_FILE')]) {
sh '''
export KUBECONFIG="$KUBECONFIG_FILE"
helm upgrade --install demo-prod "$CHART_DIR" \
--namespace "$APP_NS_PROD" --create-namespace \
--set image.repository=$IMAGE --set image.tag=$COMMIT \
--set replicaCount=3
kubectl -n "$APP_NS_PROD" rollout status deploy/demo
'''
}
}
}
}
post {
success {
echo " 成功发布:$IMAGE:$COMMIT"
// 可接入 Slack/企业微信
// slackSend channel: '#devops', color: 'good', message: "Build ${env.BUILD_URL} success: $IMAGE:$COMMIT"
}
failure {
echo " 失败,触发回滚提示"
}
}
}
如果你的 Jenkins 控制节点无法使用 Docker,可在 agent 节点安装 Docker 并用 agent { label 'docker' } 指定标签。
4. 创建 Jenkins 任务并联动 Git
4.1 创建 Multibranch Pipeline(推荐)
- New Item → Multibranch Pipeline
- Branch Sources 选择你的 Git(GitHub/GitLab),配置凭据
- Build Configuration:按仓库根目录的 Jenkinsfile
- 开启 Webhook(GitHub/GitLab):Push/PR 自动触发
4.2 单分支 Pipeline(简化)
- New Item → Pipeline
- Pipeline script from SCM,指向仓库和分支
5. 运行与验证
- 首次运行将拉依赖、构建镜像、Trivy 扫描、推送、Helm 部署到 staging。
- 打开 kubectl get svc -n staging 查看服务 IP/Ingress;访问 / 应返回 “Hello CI/CD v1”。
- 在 Jenkins 审批步骤点“发布”,流水线继续将生产环境 prod 升级。
- 灰度/回滚:使用 Helm 历史与回滚命令:
# 查看历史
helm history demo-prod -n prod
# 回滚到上一个版本
helm rollback demo-prod 1 -n prod
6. 常见扩展与优化
- 并行化:更多语言的测试矩阵(Node 18/20、不同 OS)
- 缓存:Docker BuildKit + 多阶段 + npm ci 缓存
- 质量门禁:接入 SonarQube(withSonarQubeEnv)
- 镜像瘦身:使用 distroless/alpine,锁定 CVE
- 蓝绿/金丝雀:在 Helm 值里加 label/selector,或用 Istio/NGINX Ingress 分权重
- 自动回滚:结合 Prometheus 监控、错误率门限 + Jenkins 失败后脚本
- 密钥管理:K8s Secret/External Secrets/HashiCorp Vault
7. 一键初始化脚本(可选)
scripts/bootstrap.sh(在 Jenkins 容器里执行,装 Node/Trivy CLI 等)
#!/usr/bin/env bash
set -euo pipefail
# Node & npm 缓存目录(可选)
mkdir -p /var/jenkins_home/.npm
npm config set cache /var/jenkins_home/.npm --global || true
# 安装 trivy CLI 到宿主/容器(也可直接用 docker 运行 trivy)
which trivy || {
echo "Use docker run aquasec/trivy ... in Jenkinsfile"
}
echo "Bootstrap done."
8. 你可以直接用的命令清单(运维侧)
# 手动部署到 staging(本地验证)
helm upgrade --install demo-stg helm/demo -n staging --create-namespace \
--set image.repository=your-dockerhub-username/demo-ci-cd --set image.tag=latest
# 查看部署状态
kubectl -n staging get deploy,po,svc
# 查看 Pod 日志
kubectl -n staging logs -f deploy/demo
# 检查服务可达性
kubectl -n staging port-forward svc/demo 8080:80
curl http://localhost:8080/
结语
你现在拿到了一整套可运行的 Jenkins CI/CD 工程模板:
- Jenkins 容器化部署
- 完整 Jenkinsfile 流程(测试/扫描/打包/推送/Helm 部署/审批/发布)
- 示例应用与 Helm Chart
相关推荐
- Kubernetes 核心概念全景图:Pod、Node、Cluster、Control Plane 等
-
想真正读懂Kubernetes的底层运作,你必须理解它的“权力架构”。Pod是什么?Node是什么?ControlPlane又是做什么的?它们之间有什么关系?怎么协同工作?本篇带你构建一个...
- Helm 实战:用 Helm 部署一个 Nginx 应用
-
这一篇,我们将动手实战:用Helm从零部署一个Nginx应用,并掌握HelmChart的结构和参数化技巧。一、准备环境在开始之前,你需要确保环境中具备以下工具:已部署的Kubernet...
- 从零开始:如何在 Linux 上搭建 Nginx + Node.js 高性能 Web 服务
-
在现代互联网服务架构中,Nginx+Node.js已成为轻量级、高性能网站的首选组合。本文将带你从零开始,一步步搭建一个高并发、高可用的Web服务平台,让新手也能轻松掌握生产级部署思路。一、...
- NetBox 最新版 4.4.1 完整安装指南
-
NetBox最新版4.4.1完整安装指南(修正版)by大牛蛙1.系统准备#关闭SELinux和防火墙(仅测试环境)systemctldisable--nowfirewalldse...
- Termux 安装 linux 宝塔面板,搭建 Nginx+PHP+Mysql web 网站环境
-
Termux安装linux宝塔面板,搭建Nginx+PHP+Mysqlweb服务环境,解决启动故障奶妈级教程1.到宝塔面板官网:https://www.bt.cn/new/download...
- OpenEuler系统安装Nginx安装配置_openwrt安装nginx
-
NginxWEB安装时可以指定很多的模块,默认需要安装Rewrite模块,也即是需要系统有PCRE库,安装Pcre支持Rewrite功能。如下为安装NginxWEB服务器方法:源码的路径,而不是编...
- 多级缓存架构实战:从OpenResty到Redis,打造毫秒级响应系统
-
在传统的Web架构中,当用户发起请求时,应用通常会直接查询数据库。这种模式在低并发场景下尚可工作,但当流量激增时,数据库很容易成为性能瓶颈。多级缓存通过在数据路径的不同层级设置缓存,可以显著降低数据库...
- 如何使用 Nginx 缓存提高网站性能 ?
-
快速加载的站点提供了更好的用户体验并且可以拥有更高的搜索引擎排名。通过Nginx缓存提高你的网站性能是一个有效的方法。Nginx是一个流行的开源web服务器,也可以作为web服务器反向代...
- 如何构建企业级Docker Registry Server
-
很多人问我,虚拟机镜像和docker镜像的区别是什么?其实区别非常明显,我们可以通过阅读Dockerfile文件就可以知道这个镜像都做了哪些操作,能提供什么服务;但通过虚拟机镜像,你能一眼看出来虚拟机...
- 如何解决局域网SSL证书问题?使用mkcert证书生成工具轻松搞定
-
“局域网里弹出‘不安全’红锁,老板就在身后盯着演示,那一刻只想原地消失。”别笑,九成前端都经历过。自签证书被Chrome标红,客户以为网站被黑,其实只是缺一张被信任的证。mkcert把这事从半小时缩到...
- Docker 安全与权限控制:别让你的容器变成“漏洞盒子”
-
在享受容器带来的轻量与灵活的同时,我们也必须面对一个现实问题:安全隐患。容器并不是天然安全,错误配置甚至可能让攻击者“越狱”入侵主机!本篇将带你从多个层面强化Docker的安全防护,构建真正可放心...
- Kubernetes生产级管理指南(2025版)
-
在云原生技术持续演进的2025年,Kubernetes已成为企业数字化转型的核心引擎。然而,生产环境中的集群管理仍面临基础设施配置、安全漏洞、运维复杂度攀升等挑战。本文将结合最新行业实践,从基础设施即...
- 云原生工程师日常使用最多的工具和100条高频命令
-
在云原生时代,工程师不仅要熟悉容器化、编排和服务网格,还要掌握大量工具和命令来进行日常运维与开发。本文将从工具篇和命令篇两个角度,详细介绍云原生工程师每天都会用到的核心技能。一、云原生工程师常...
- 用 Jenkins 实现自动化 CI/CD_jenkins api自动执行
-
场景设定(可替换为你的技术栈)语言:Node.js(示例简单,任何语言思路一致)制品:Docker镜像(推送到DockerHub/Harbor)运行环境:Kubernetes(staging...
- 5款好用开源云笔记虚拟主机部署项目推荐
-
在个人数据管理与协同办公场景中,开源云笔记项目凭借可自主部署、数据可控的优势,成为众多用户的首选。以下推荐5款适配虚拟主机部署、功能完善的开源项目,附核心特性与部署要点,助力快速搭建专属云笔记系统。...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Kubernetes 核心概念全景图:Pod、Node、Cluster、Control Plane 等
- Helm 实战:用 Helm 部署一个 Nginx 应用
- 从零开始:如何在 Linux 上搭建 Nginx + Node.js 高性能 Web 服务
- NetBox 最新版 4.4.1 完整安装指南
- Termux 安装 linux 宝塔面板,搭建 Nginx+PHP+Mysql web 网站环境
- OpenEuler系统安装Nginx安装配置_openwrt安装nginx
- 多级缓存架构实战:从OpenResty到Redis,打造毫秒级响应系统
- 如何使用 Nginx 缓存提高网站性能 ?
- 如何构建企业级Docker Registry Server
- 如何解决局域网SSL证书问题?使用mkcert证书生成工具轻松搞定
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)