CI/CD 工具选型与实践
概述
CI/CD 工具是 DevOps 实践的核心组件,负责将代码从提交到部署的流程自动化。本文档对 Jenkins、GitLab Runner 和 Drone CI 三款主流 CI/CD 工具进行深入介绍,涵盖安装部署、配置实践、Pipeline 语法及生产环境最佳实践,并在最后提供对比选型建议。
一、Jenkins
1.1 简介
Jenkins 是市场占有率最高的开源 CI/CD 引擎,基于 Java 开发,拥有超过 1800 款插件,几乎可以集成所有开发、测试、部署工具。其核心优势在于高度可扩展性和成熟的企业级生态。
1.2 Docker 安装
使用 Docker Compose 部署
# docker-compose.yml
version: "3.8"
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins-master
restart: always
ports:
- "8080:8080"
- "50000:50000"
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
- CASC_JENKINS_CONFIG=/var/jenkins_home/casc.yml
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
networks:
- jenkins-net
volumes:
jenkins_home:
networks:
jenkins-net:
driver: bridge首次启动后通过 http://localhost:8080 访问,初始管理员密码可通过以下命令获取:
docker exec jenkins-master cat /var/jenkins_home/secrets/initialAdminPassword使用 War 包部署
# 需预先安装 JDK 17
wget https://get.jenkins.io/war-stable/latest/jenkins.war
java -jar jenkins.war --httpPort=80801.3 插件管理
插件安装方式
- Web UI: 系统管理 -> 插件管理 -> 可选插件 -> 搜索并安装
- 命令行: 通过 Jenkins CLI 安装
- 配置即代码: 通过 Jenkins CasC (Configuration as Code) 声明
# Jenkins CLI 安装插件
java -jar jenkins-cli.jar -s http://localhost:8080/ install-plugin <plugin-id>推荐插件清单
| 类别 | 插件名称 | 用途 |
|---|---|---|
| 代码管理 | Git Plugin | Git 源码拉取 |
| 代码管理 | GitHub/GitLab/Bitbucket 集成 | PR 触发、Webhook |
| 构建工具 | Maven Integration | Maven 构建 |
| 构建工具 | Gradle Plugin | Gradle 构建 |
| 构建工具 | NodeJS Plugin | Node.js 前端构建 |
| 容器化 | Docker Pipeline | Docker 构建与发布 |
| 容器化 | Kubernetes Plugin | K8s 动态 Agent |
| 质量分析 | SonarQube Scanner | 代码质量扫描 |
| 安全扫描 | OWASP Dependency-Check | 依赖漏洞检查 |
| 通知 | Email Extension | 邮件通知 |
| 通知 | Slack Notification | Slack 通知 |
| 凭证管理 | Credentials Binding | 凭证注入 |
| Pipeline | Pipeline | 流水线核心 |
| Pipeline | Pipeline: Stage View | 流水线可视化 |
| 配置即代码 | Configuration as Code | 声明式配置 |
| 蓝绿部署 | Blue Ocean | 现代化 UI |
1.4 全局工具配置
通过 系统管理 -> 全局工具配置 进行设置,也可通过 CasC 配置。
# casc.yml - 配置即代码
jenkins:
systemMessage: "Jenkins managed by Configuration as Code"
numExecutors: 0
scmCheckoutRetryCount: 3
tool:
jdk:
installations:
- name: "JDK-17"
home: "/usr/lib/jvm/java-17-openjdk"
- name: "JDK-11"
properties:
- installSource:
installers:
- adoptiumInstaller:
id: "jdk-11.0.20+8"
maven:
installations:
- name: "Maven-3.9"
properties:
- installSource:
installers:
- mavenInstaller:
id: "3.9.6"
git:
installations:
- name: "Default"
home: "/usr/bin/git"1.5 凭证管理
Jenkins 支持多种凭证类型:用户名密码、SSH 密钥、Secret text、Secret file、证书等。
创建凭证
通过 Web UI:系统管理 -> 凭据管理 -> 全局凭据 -> 添加凭据
Pipeline 中使用凭证
// Declarative Pipeline 中使用凭证
pipeline {
agent any
environment {
DOCKER_HUB_CREDS = credentials('docker-hub-credentials')
GIT_SSH_CREDS = credentials('git-ssh-key')
}
stages {
stage('Checkout') {
steps {
// SSH 方式拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: '*/main']],
userRemoteConfigs: [[url: 'git@github.com:org/repo.git']],
extensions: [[$class: 'GitSCMExtension', credentialsId: 'git-ssh-key']]
])
}
}
stage('Docker Login') {
steps {
sh '''
echo $DOCKER_HUB_CREDS_PSW | docker login -u $DOCKER_HUB_CREDS_USR --password-stdin
'''
}
}
}
}1.6 Pipeline 语法
Declarative Pipeline(声明式)
声明式 Pipeline 是 Jenkins 推荐的流水线定义方式,结构清晰,易于上手。
pipeline {
agent any
parameters {
string(name: 'BRANCH', defaultValue: 'main', description: '分支名称')
choice(name: 'ENV', choices: ['dev', 'staging', 'prod'], description: '部署环境')
}
triggers {
// 定时触发:每天凌晨 2 点
cron('0 2 * * *')
// Webhook 触发(需安装 GitHub/GitLab 插件)
webhook()
}
tools {
jdk 'JDK-17'
maven 'Maven-3.9'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Unit Test') {
steps {
sh 'mvn clean test -DskipITs=true'
}
post {
success {
junit '**/target/surefire-reports/*.xml'
}
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube-Server') {
sh 'mvn sonar:sonar'
}
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests=true'
}
}
stage('Docker Build & Push') {
steps {
script {
docker.withRegistry('https://registry.cn-hangzhou.aliyuncs.com', 'aliyun-docker-registry') {
def app = docker.build("myapp:${env.BUILD_NUMBER}", "./docker")
app.push()
app.push("latest")
}
}
}
}
stage('Deploy to K8s') {
steps {
sh """
kubectl set image deployment/myapp \
myapp=registry.cn-hangzhou.aliyuncs.com/myapp:${env.BUILD_NUMBER} \
-n ${params.ENV}
"""
}
}
}
post {
always {
cleanWs()
}
success {
emailext(
subject: "构建成功: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "查看详情: ${env.BUILD_URL}",
to: 'team@example.com'
)
}
failure {
emailext(
subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "请及时处理: ${env.BUILD_URL}",
to: 'team@example.com'
)
}
}
}Scripted Pipeline(脚本式)
脚本式 Pipeline 提供更高的灵活性,基于 Groovy 语法,适合复杂流程控制。
node('k8s-agent') {
stage('Checkout') {
checkout scm
}
stage('Parallel Stages') {
parallel(
'Unit Tests': {
node('maven') {
sh 'mvn test'
}
},
'Integration Tests': {
node('docker') {
sh 'mvn verify -Pintegration'
}
}
)
}
stage('Build & Deploy') {
// 条件判断
if (env.BRANCH_NAME == 'main') {
sh 'mvn clean deploy'
} else {
sh 'mvn clean package'
}
}
stage('Archive') {
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}1.7 Kubernetes 动态 Agent 配置
K8s 动态 Agent 允许 Jenkins 在需要时自动在 Kubernetes 集群中创建 Pod 作为构建节点,构建完成后自动销毁,实现资源弹性伸缩。
安装 Kubernetes 插件
在插件管理中安装 Kubernetes Plugin。
配置 Kubernetes 云
系统管理 -> 节点管理 -> Configure Clouds -> 添加 Kubernetes:
# Kubernetes Cloud 配置 (通过 CasC)
jenkins:
clouds:
- kubernetes:
name: "k8s-cloud"
serverUrl: "https://kubernetes.default.svc.cluster.local"
namespace: "jenkins-agents"
skipTlsVerify: false
directConnection: true
jenkinsUrl: "http://jenkins-master:8080"
maxRequestsPerHostStr: "32"
waitForPodSec: 300
podTemplatesConfiguration:
- name: "default"
label: "k8s-agent"
containers:
- name: "jnlp"
image: "jenkins/inbound-agent:latest-jdk17"
resourceRequestCpu: "500m"
resourceLimitCpu: "2"
resourceRequestMemory: "512Mi"
resourceLimitMemory: "2Gi"
workingDir: "/home/jenkins/agent"
- name: "docker"
image: "docker:24.0-cli"
command: "cat"
ttyEnabled: true
resourceRequestCpu: "200m"
resourceLimitCpu: "1"
resourceRequestMemory: "256Mi"
resourceLimitMemory: "512Mi"
volumes:
- hostPathVolume:
hostPath: "/var/run/docker.sock"
mountPath: "/var/run/docker.sock"
- name: "maven"
image: "maven:3.9-eclipse-temurin-17"
command: "cat"
ttyEnabled: true
resourceRequestCpu: "500m"
resourceLimitCpu: "2"
resourceRequestMemory: "1Gi"
resourceLimitMemory: "2Gi"
volumes:
- hostPathVolume:
hostPath: "/root/.m2"
mountPath: "/root/.m2"
#### Pod Template YAML 方式
也可以在 Pipeline 中通过 YAML 直接定义 Pod 模板:
```groovy
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: jnlp
image: jenkins/inbound-agent:latest-jdk17
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
- name: docker
image: docker:24.0-cli
command: ["cat"]
tty: true
volumeMounts:
- name: dockersock
mountPath: /var/run/docker.sock
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ["cat"]
tty: true
volumes:
- name: dockersock
hostPath:
path: /var/run/docker.sock
'''
defaultContainer 'maven'
}
}
stages {
stage('Build in K8s Pod') {
steps {
container('maven') {
sh 'mvn clean package'
}
container('docker') {
sh 'docker build -t myapp .'
}
}
}
}
}二、GitLab Runner
2.1 简介
GitLab Runner 是 GitLab CI/CD 的执行组件,负责接收 GitLab 实例发起的流水线任务并在指定环境中执行。它与 GitLab 深度集成,天然支持 Merge Request 触发、环境看板、制品管理等能力。
2.2 安装与注册
Docker 安装 Runner
# 运行 GitLab Runner 容器
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest注册 Runner
注册需要 GitLab 项目的注册令牌(Registration Token),可在项目的 Settings -> CI/CD -> Runners 中找到。
# 交互式注册
docker exec -it gitlab-runner gitlab-runner register
# 非交互式注册(推荐脚本化)
docker exec gitlab-runner gitlab-runner register \
--non-interactive \
--url "https://gitlab.com" \
--registration-token "YOUR_REGISTRATION_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "docker-runner" \
--tag-list "docker,prod" \
--run-untagged="true" \
--locked="false"Kubernetes 安装 Runner
使用 Helm Chart 安装:
# 添加 GitLab Helm 仓库
helm repo add gitlab https://charts.gitlab.io
helm repo update
# 安装 GitLab Runner
helm upgrade --install gitlab-runner gitlab/gitlab-runner \
--namespace gitlab-runner \
--create-namespace \
--set gitlabUrl=https://gitlab.com \
--set runnerRegistrationToken=YOUR_REGISTRATION_TOKEN \
--set runners.executor=kubernetes \
--set runners.tags=k8s \
--set runners.privileged=true注册配置文件示例
注册完成后,Runner 配置保存在 /etc/gitlab-runner/config.toml:
concurrent = 10
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "docker-runner"
url = "https://gitlab.com"
id = 12345
token = "YOUR_TOKEN"
token_obtained_at = 2024-01-01T00:00:00Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
[runners.custom_build_dir]
[runners.cache]
Type = "s3"
Path = "cache"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
AccessKey = "AKIAIOSFODNN7EXAMPLE"
SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
BucketName = "gitlab-runner-cache"
Insecure = false
[runners.docker]
tls_verify = false
image = "alpine:latest"
privileged = true
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
shm_size = 02.3 Runner 类型
| 类型 | 可见范围 | 适用场景 |
|---|---|---|
| Shared Runner | 整个 GitLab 实例 | 多个项目共享,由平台团队统一管理 |
| Group Runner | 指定组内所有项目 | 部门级共享,适用于同一业务线的多个项目 |
| Specific Runner | 单个项目 | 项目级独享,适用于有特殊环境要求的项目 |
# 注册 Shared Runner(需管理员权限)
docker exec gitlab-runner gitlab-runner register --non-interactive \
--url "https://gitlab.com" \
--registration-token "SHARED_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--locked="false"
# 注册 Specific Runner(普通项目)
docker exec gitlab-runner gitlab-runner register --non-interactive \
--url "https://gitlab.com" \
--registration-token "PROJECT_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--locked="true"2.4 .gitlab-ci.yml 配置
基础配置与触发规则
# .gitlab-ci.yml
stages:
- build
- test
- package
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
DOCKER_REGISTRY: "registry.cn-hangzhou.aliyuncs.com"
DOCKER_IMAGE: "$DOCKER_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA"
# 缓存 Maven 依赖
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository/
# 触发规则:仅 main 分支和标签触发
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH =~ /^feature\/.*/
- when: never # 其他分支不触发
before_script:
- export MAVEN_HOME=/usr/share/maven
build:
stage: build
image: maven:3.9-eclipse-temurin-17
script:
- mvn clean compile -DskipTests=true
artifacts:
paths:
- target/
expire_in: 2 hours
unit-test:
stage: test
image: maven:3.9-eclipse-temurin-17
script:
- mvn test
artifacts:
reports:
junit: target/surefire-reports/TEST-*.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
package:
stage: package
image: maven:3.9-eclipse-temurin-17
script:
- mvn clean package -DskipTests=true
artifacts:
paths:
- target/*.jar
expire_in: 1 week
docker-build:
stage: package
image: docker:24.0
services:
- docker:24.0-dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $DOCKER_REGISTRY
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
needs:
- build
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE -n staging
- kubectl rollout status deployment/myapp -n staging
environment:
name: staging
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy-production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE -n production
- kubectl rollout status deployment/myapp -n production
environment:
name: production
rules:
- if: $CI_COMMIT_TAG
when: manual # 生产环境需要手动确认高级触发规则
# 仅特定文件变更时触发
changeset:
script:
- echo "Code changed"
rules:
- changes:
- src/**/*
- pom.xml
when: on_success
- changes:
- docs/**/*
- README.md
when: never
# 条件触发的多种用法
conditional-job:
script:
- echo "Conditional execution"
rules:
# 手动触发
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual
# 定时触发
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: on_success
# 合并请求事件
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: on_success
# 标签推送
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
# 默认不执行
- when: never2.5 缓存与制品配置
# 全局缓存配置
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- node_modules/
- .m2/repository/
policy: pull-push # pull-push: 下载并上传; pull: 仅下载; push: 仅上传
# 按分支设置不同缓存策略
cache:node-modules:
key:
files:
- package-lock.json
prefix: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
# 制品传递
build-jar:
stage: build
script:
- mvn clean package -DskipTests=true
artifacts:
paths:
- target/*.jar
exclude:
- target/*-sources.jar
expose_as: 'build-artifacts' # 在 MR 中显示下载链接
expire_in: 30 days
reports:
dotenv: build.env # 环境变量报告2.6 Docker in Docker(DinD)构建
DinD 是 GitLab Runner 中最常见的 Docker 镜像构建方式,需要在特权模式下运行 Docker 服务。
# .gitlab-ci.yml - DinD 构建示例
variables:
# 配置 Docker 客户端连接到 DinD 服务
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
DOCKER_DRIVER: overlay2
docker-build-dind:
stage: package
image: docker:24.0
services:
- name: docker:24.0-dind
alias: docker
variables:
DOCKER_HOST: tcp://docker:2375
before_script:
- docker info # 验证 Docker 守护进程
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
script:
# 多阶段构建
- docker build
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
--build-arg VCS_REF=$CI_COMMIT_SHORT_SHA
--build-arg VERSION=$CI_COMMIT_TAG
-t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
-t $CI_REGISTRY_IMAGE:latest
.
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
tags:
- dockerDocker 执行器配置要求
# config.toml - Docker 执行器 DinD 配置
[[runners]]
executor = "docker"
[runners.docker]
privileged = true # 必须启用特权模式
volumes = ["/cache"]
shm_size = 0三、Drone CI
3.1 简介
Drone CI 是一个基于容器化的轻量级 CI/CD 平台,使用 Go 语言编写。其核心理念是"一切皆容器"——每个构建步骤都在独立的 Docker 容器中运行,天然保证环境隔离和可重复性。Drone 采用简单的 YAML 配置语法,部署极其轻量,特别适合中小规模团队和云原生场景。
3.2 Docker 部署
部署 Drone Server
Server 是 Drone 的控制平面,负责任务调度、Webhook 接收和 API 服务。
# docker-compose.yml
version: "3.8"
services:
drone-server:
image: drone/drone:2
container_name: drone-server
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- drone_data:/data
environment:
# Drone 实例的外部地址
- DRONE_SERVER_HOST=drone.example.com
- DRONE_SERVER_PROTO=https
# 共享密钥,用于 server 与 runner 通信
- DRONE_RPC_SECRET=your-shared-secret-key
# 用户创建:注册后第一个用户自动成为管理员
- DRONE_USER_CREATE=username:admin,admin:true
# Git 提供商配置(以 GitHub 为例)
- DRONE_GITHUB_CLIENT_ID=your-github-oauth-client-id
- DRONE_GITHUB_CLIENT_SECRET=your-github-oauth-client-secret
# 可选:使用 Gitea / GitLab
# - DRONE_GITEA_SERVER=https://gitea.example.com
# - DRONE_GITEA_CLIENT_ID=xxx
# - DRONE_GITEA_CLIENT_SECRET=xxx
# 可选:使用 GitLab
# - DRONE_GITLAB_SERVER=https://gitlab.example.com
# - DRONE_GITLAB_CLIENT_ID=xxx
# - DRONE_GITLAB_CLIENT_SECRET=xxx
# 日志配置
- DRONE_LOGS_DEBUG=false
- DRONE_LOGS_TRACE=false
networks:
default:
name: drone-net
volumes:
drone_data:部署 Drone Runner
Runner 是 Drone 的执行组件,负责拉取代码并在容器中运行 Pipeline 步骤。
# docker-compose.yml(在 runner 节点上)
version: "3.8"
services:
drone-runner:
image: drone/drone-runner-docker:latest
container_name: drone-runner
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
# 连接 Drone Server
- DRONE_RPC_HOST=drone-server
- DRONE_RPC_PROTO=http
- DRONE_RPC_SECRET=your-shared-secret-key
# Runner 容量
- DRONE_RUNNER_CAPACITY=5
# Runner 名称
- DRONE_RUNNER_NAME=docker-runner-1
# 限制最大并发构建数
- DRONE_LIMIT_CONCURRENT=2
# 构建超时(分钟)
- DRONE_TIMEOUT=30
networks:
default:
external:
name: drone-net启动
# 创建网络
docker network create drone-net
# 启动 Server 和 Runner
docker-compose -f docker-compose-server.yml up -d
docker-compose -f docker-compose-runner.yml up -d3.3 .drone.yml 配置
Drone 使用 .drone.yml 文件定义流水线,语法简洁,每个步骤对应一个容器。
# .drone.yml
---
kind: pipeline
type: docker
name: default
# 触发条件
trigger:
branch:
- main
- develop
event:
- push
- tag
# 环境变量
environment:
APP_NAME: myapp
DOCKER_REGISTRY: registry.cn-hangzhou.aliyuncs.com
steps:
# 第一步:安装依赖
- name: install-deps
image: node:18-alpine
pull: if-not-exists
environment:
NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
commands:
- npm ci
- npm cache clean --force
# 第二步:运行单元测试
- name: unit-test
image: node:18-alpine
depends_on: [install-deps]
commands:
- npm run test:ci
- npm run lint
# 第三步:构建与代码扫描
- name: build
image: node:18-alpine
depends_on: [unit-test]
commands:
- npm run build
- npm run sonar
# 第四步:Docker 构建与推送
- name: docker-publish
image: plugins/docker
depends_on: [build]
settings:
registry: registry.cn-hangzhou.aliyuncs.com
repo: registry.cn-hangzhou.aliyuncs.com/myapp/myapp
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
- ${DRONE_TAG}
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
cache_from: registry.cn-hangzhou.aliyuncs.com/myapp/myapp:latest
# 第五步:部署到 K8s
- name: deploy
image: pelotech/drone-helm3
depends_on: [docker-publish]
settings:
mode: upgrade
chart: ./deploy/charts/myapp
namespace: production
release: myapp
values_files: ["./deploy/values/prod.yaml"]
values:
- image.tag=${DRONE_COMMIT_SHA:0:8}
kube_api_server:
from_secret: kube_api_server
kube_token:
from_secret: kube_token
# 第六步:通知
- name: notify
image: plugins/slack
depends_on: [deploy]
settings:
webhook:
from_secret: slack_webhook
channel: ci-cd
template: >
部署成功: {{ repo.name }} ({{ build.branch }})
提交: {{ build.commit }}
作者: {{ build.author }}
# 服务容器(用于集成测试)
services:
- name: redis
image: redis:7-alpine
- name: postgres
image: postgres:15-alpine
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test多 Pipeline 与条件执行
---
kind: pipeline
type: docker
name: test-and-lint
trigger:
event:
- pull_request
steps:
- name: test
image: node:18-alpine
commands:
- npm test
- name: lint
image: node:18-alpine
commands:
- npm run lint
---
kind: pipeline
type: docker
name: build-and-deploy
trigger:
branch:
- main
event:
- push
steps:
- name: build
image: node:18-alpine
commands:
- npm run build
- name: deploy
image: plugins/docker
settings:
repo: myapp
tags: latest
depends_on:
- test-and-lint3.4 Docker 插件机制
Drone 的插件机制使得每一步都可以复用社区提供的标准化容器。插件的本质是配置了特定参数的标准 Docker 容器,通过环境变量和挂载卷与 Pipeline 交互。
常用插件清单
| 插件 | 镜像 | 功能 |
|---|---|---|
| Docker | plugins/docker | 构建并推送 Docker 镜像 |
| GitHub Release | plugins/github-release | 创建 GitHub Release |
| S3 Sync | plugins/s3-sync | 同步文件到 S3 |
| Slack | plugins/slack | Slack 消息通知 |
| Helm | pelotech/drone-helm3 | Helm Chart 部署 |
| Kubernetes | plugins/kube | 直接操作 K8s 资源 |
| SonarQube | plugins/sonarqube | 代码质量扫描 |
| plugins/email | 邮件通知 | |
| GCS | plugins/gcs | Google Cloud Storage 同步 |
| Cache | meltwater/drone-cache | 缓存恢复与保存 |
自定义插件
Drone 插件本质上是 Docker 容器,可以通过 DRONE_ 前缀环境变量获取 Pipeline 上下文信息。
# Dockerfile.plugin
FROM alpine:latest
RUN apk add --no-cache curl jq
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]# entrypoint.sh
#!/bin/sh
# 从环境变量获取插件参数
PLUGIN_MESSAGE=${PLUGIN_MESSAGE:-"Hello from Drone"}
# 获取构建上下文
echo "Repository: $DRONE_REPO"
echo "Commit: $DRONE_COMMIT"
echo "Branch: $DRONE_BRANCH"
# 执行自定义逻辑
echo "$PLUGIN_MESSAGE"在 Pipeline 中使用自定义插件:
steps:
- name: custom-plugin
image: myregistry/custom-plugin:latest
settings:
message: "Deployment completed"3.5 与 Jenkins / GitLab CI 对比
| 维度 | Drone CI | Jenkins | GitLab CI |
|---|---|---|---|
| 架构复杂度 | 极简(Server + Runner) | 复杂(Master + Agent + 大量插件) | 中等(GitLab 内置 + Runner) |
| 部署成本 | 单容器可启动 | 需要安装配置大量插件 | 需部署 GitLab 实例或使用 SaaS |
| 配置语法 | 极简 YAML,易上手 | Groovy/Pipeline DSL,学习曲线陡峭 | YAML 语法,中等复杂度 |
| 插件机制 | Docker 容器即插件 | 1800+ Java 插件 | 内置功能为主+少量模板 |
| 构建隔离 | 每步独立容器,天然隔离 | Agent 共享环境,需额外配置 | 每 Job 独立容器 |
| 容器化原生 | 是,从设计上完全基于容器 | 后期扩展支持(K8s Plugin) | 原生 Docker 执行器 |
| UI/UX | 简洁现代 | 功能丰富但界面老旧(Blue Ocean 改善) | 与 GitLab 深度集成,体验优秀 |
| 扩展性 | 依赖 Docker/容器化 | 插件生态最丰富 | 依赖 GitLab 生态 |
| 社区规模 | 中小规模 | 最大 | 大规模 |
| 企业级特性 | 较少的开箱即用企业功能 | 最完善 | 完善(取决于 GitLab 版本) |
| 性能/资源占用 | 极低 | 较高 | 中等 |
| 适用场景 | 中小团队、云原生项目 | 大型企业、复杂构建流程 | 使用 GitLab 的团队 |
四、三者对比选型表
4.1 综合对比
| 对比维度 | Jenkins | GitLab Runner | Drone CI |
|---|---|---|---|
| 开源性 | 开源(MIT License) | 开源(MIT License) | 开源(Apache 2.0) |
| 开发语言 | Java | Go | Go |
| 首次安装复杂度 | 高(需安装 Java + 配置插件) | 中(需注册 Runner) | 低(两个容器即可) |
| 日常维护成本 | 高(插件兼容性、安全更新) | 低(随 GitLab 升级) | 低(无状态设计) |
| 插件生态 | 1800+ 插件,最丰富 | 内置功能为主,扩展有限 | 基于 Docker 的插件体系 |
| 社区活跃度 | 极高,企业用户多 | 高,GitLab 用户为主 | 中等,CNCF 生态友好 |
| 云原生支持 | 良好(K8s Plugin) | 良好(K8s 执行器) | 优秀(原生容器化) |
| Pipeline 语法 | Groovy,功能强大但复杂 | YAML,功能完备 | YAML,简洁直观 |
| 安装包大小 | 300MB+(含依赖) | 100MB+ | 50MB 左右 |
| 内存占用 | 1GB+ | 100MB+ | 50MB 左右 |
| 高可用方案 | 官方有成熟方案 | GitLab 自身 HA | 依赖外部数据库 |
| Secret 管理 | 凭据管理插件 | GitLab CI/CD Variables | Drone Secrets 或外部 KMS |
| 多架构支持 | 依赖 Agent 镜像 | 原生支持 | 原生支持(Go 编译) |
| 商业版本 | CloudBees CI | GitLab Enterprise | Drone Cloud(已停止运营) |
4.2 选型建议
选用 Jenkins 的场景
- 已有大量 Jenkins 资产(Pipeline、插件配置),迁移成本高
- 需要高度自定义的构建流程,插件市场有现成方案
- 大型企业,有专职 DevOps 团队维护 Jenkins 集群
- 需要对构建环境进行细粒度权限控制
- 需要支持除容器外的多种执行环境(Windows、Mac、Unix)
选用 GitLab Runner 的场景
- 团队已使用 GitLab 作为代码托管平台
- 追求开发运维一体化,减少工具链数量
- 需要与 Merge Request 深度集成(自动检测、状态反馈)
- 中小团队,希望快速上手 CI/CD
- 需要完善的制品管理、容器注册表、环境看板
选用 Drone CI 的场景
- 团队以容器化部署为主,追求云原生架构
- 团队规模较小(10-50人),需要轻量级 CI/CD 方案
- 对 CI/CD 资源占用有要求(边缘/小规格服务器)
- 希望快速搭建、低维护成本的 CI/CD 系统
- 团队熟悉 YAML 配置,追求简单直接
4.3 组合使用建议
在某些场景下,可以将不同的 CI/CD 工具组合使用,发挥各自优势:
# 组合策略示例
# GitLab 负责代码管理和 MR 流程
# GitLab Runner 执行轻量级 CI(静态检查、单元测试)
# Jenkins 执行重型 CI/CD(集成测试、多环境部署)
# .gitlab-ci.yml - 轻量级 CI
stages:
- lint
- test
- trigger-jenkins
lint:
stage: lint
script:
- npm run lint
unit-test:
stage: test
script:
- npm run test:coverage
trigger-jenkins:
stage: trigger-jenkins
script:
- curl -X POST "https://jenkins.example.com/job/main-pipeline/build"
- -H "Authorization: Bearer $JENKINS_TOKEN"
only:
- main五、总结
选择合适的 CI/CD 工具需要综合考虑团队规模、技术栈、现有基础设施和运维能力:
- Jenkins 功能最全面、生态最成熟,但运维成本也最高,适合大型企业和复杂场景。
- GitLab Runner 与 GitLab 深度集成,提供一体化的 DevOps 体验,是使用 GitLab 团队的天然选择。
- Drone CI 极简轻量,原生容器化,适合云原生架构的中小团队。
无论选择哪种工具,良好的 Pipeline 设计(合理划分阶段、充分利用缓存、完善的错误处理和通知机制)才是 CI/CD 实践成功的关键。