Jenkins
架构
| 组件 | 说明 |
|---|---|
| Master | 主节点,负责调度 Job、管理 Agent、提供 Web UI |
| Agent(Node) | 执行 Job 的工作节点,通过 SSH / JNLP 连接 |
| Pipeline | 用 Jenkinsfile 定义的自动化流水线 |
| Plugin | 插件生态,1800+ 插件扩展各种功能 |
Jenkinsfile(声明式)
Jenkinsfile
pipeline {
// 在任何可用 Agent 上运行
agent any
// 环境变量
environment {
REGISTRY = 'registry.example.com'
IMAGE = "${REGISTRY}/myapp"
CRED_ID = 'docker-registry-cred'
}
// 构建参数
parameters {
choice(name: 'DEPLOY_ENV', choices: ['staging', 'production'], description: '部署环境')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '是否跳过测试')
}
// 全局选项
options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Lint') {
steps {
sh 'pnpm lint'
}
}
stage('Test') {
when {
expression { !params.SKIP_TESTS }
}
steps {
sh 'pnpm test -- --coverage'
}
post {
always {
junit 'test-results/*.xml'
publishHTML([
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('Build Docker Image') {
steps {
script {
def tag = "${env.BUILD_NUMBER}-${env.GIT_COMMIT[0..6]}"
docker.withRegistry("https://${REGISTRY}", CRED_ID) {
def img = docker.build("${IMAGE}:${tag}")
img.push()
img.push('latest')
}
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
// 生产环境需要人工审批
script {
if (params.DEPLOY_ENV == 'production') {
input message: '确认部署到生产环境?', ok: '部署'
}
}
sh """
kubectl set image deployment/myapp \
myapp=${IMAGE}:${env.BUILD_NUMBER}-${env.GIT_COMMIT[0..6]} \
-n ${params.DEPLOY_ENV}
"""
}
}
}
post {
success {
slackSend(color: 'good', message: "✅ 构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
failure {
slackSend(color: 'danger', message: "❌ 构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
}
}
Jenkins 管理
插件推荐
| 类型 | 插件 | 用途 |
|---|---|---|
| Pipeline | Pipeline / Blue Ocean | 流水线定义和可视化 |
| 代码管理 | Git / GitHub | SCM 集成 |
| Docker | Docker Pipeline | Docker 构建推送 |
| K8s | Kubernetes | 动态 Agent Pod |
| 通知 | Slack / 钉钉 | 构建通知 |
| 安全 | Role-based Auth | 权限管理 |
| 质量 | SonarQube Scanner | 代码质量扫描 |
Kubernetes Agent(动态 Pod)
K8s Agent Jenkinsfile
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: node
image: node:20-alpine
command: ['sleep', 'infinity']
- name: docker
image: docker:24-dind
securityContext:
privileged: true
"""
}
}
stages {
stage('Build') {
steps {
container('node') {
sh 'npm ci && npm run build'
}
}
}
stage('Docker') {
steps {
container('docker') {
sh 'docker build -t myapp:latest .'
}
}
}
}
}
Shared Library(共享库)
vars/deployToK8s.groovy
// 共享库:可复用的部署步骤
def call(Map config) {
sh """
kubectl set image deployment/${config.app} \
${config.app}=${config.image} \
-n ${config.namespace}
kubectl rollout status deployment/${config.app} \
-n ${config.namespace} --timeout=300s
"""
}
Jenkinsfile 中调用
@Library('my-shared-lib') _
pipeline {
stages {
stage('Deploy') {
steps {
deployToK8s(app: 'myapp', image: 'myapp:v2', namespace: 'production')
}
}
}
}
常见面试问题
Q1: Jenkins 声明式和脚本式 Pipeline 的区别?
答案:
| 特性 | 声明式(Declarative) | 脚本式(Scripted) |
|---|---|---|
| 语法 | 结构化的 pipeline {} | 自由的 Groovy 脚本 |
| 学习曲线 | 低,有固定结构 | 高,需要 Groovy 知识 |
| 灵活性 | 受限,但可通过 script {} 扩展 | 完全自由 |
| 错误检查 | 启动前语法校验 | 运行时才报错 |
| 推荐度 | ✅ 推荐(日常使用) | 复杂逻辑使用 |
Q2: Jenkins 如何实现高可用?
答案:
- Master HA:多 Master + 共享存储(NFS/EFS)或 Jenkins HA 插件
- Agent 弹性:K8s Agent 动态创建 Pod,按需伸缩
- 数据备份:定期备份
JENKINS_HOME(配置、Job 定义、插件) - 配置即代码:Jenkinsfile 存在 Git,Job 用 JCasC 插件管理
- 分布式构建:多 Agent 分担构建负载