继之前完成了服务器硬件搭建、内公网打通后,开始着手将之前基于 github 的流水线迁移到内网环境中。
持续集成/持续部署(Continuous Integration/Continuous Deployment,简称 CI/CD)是一种软件开发实践,旨在通过自动化构建、测试和部署流程来加快软件交付速度、降低错误率,并增强团队协作效率。以下是 CI/CD 的主要概念和流程:
- 持续集成(Continuous Integration,CI):
- 指开发人员频繁将代码集成到共享存储库中,并通过自动化构建和测试流程来验证代码的质量。
- CI 系统会自动触发构建、运行单元测试、静态代码分析等操作,以确保新代码的质量。
- 目的是尽早发现和解决代码集成问题,以减少后续集成时的冲突和错误。
- 持续部署(Continuous Deployment,CD):
- 指自动化部署已通过测试的代码到生产环境,以实现快速、可靠的软件交付。
- CD 流程包括自动化构建部署流程、自动化测试、环境配置管理等步骤,以确保部署的软件质量和稳定性。
- 目的是缩短软件交付周期,降低部署风险,提高部署的可重复性和一致性。
目前业内开源的不在少数,诸如 CircleCI、GitLab、Travis CI、鼎鼎大名的 Jenkins、以及后起之秀 Drone,基于这几种方案,简单做了下对比。
Jenkins | Drone | GitLab | CircleCI | Travis CI | |
---|---|---|---|---|---|
收费 | 开源免费 | 开源离线部署免费 提供商业收费版本 |
免费的自托管版本以及付费的托管服务 | 提供免费套餐和付费套餐 | 提供免费套餐和付费套餐 |
资源消耗 | 高 | 低 | 高 | 云平台作业 | 云平台作业 |
配置文件 | jenkins file | .drone.yml | .gitlab-ci.yml | 未实践 | 未实践 |
私有化部署 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
开发语言 | Java | Go | Ruby On Rails | 未知 | 未知 |
插件生态 | 插件丰富 | 插件有限,但够用 | 有限 | 未实践 | 未实践 |
学习曲线 | ☆☆☆☆☆ | ☆☆ | ☆☆☆☆ | 未实践 | 未实践 |
Jenkins 主要是因为运行慢,耗资源太高,因此踢出了方案选择,其余的几款云平台的方案,担心后续的收费问题也被踢出了备选,最后选择了Go语言的 Drone。资源消耗小,配置文件简单,关键是支持完整的离线部署,插件虽然有限,但基本够用,在不得已的情况下也能自己开发插件。
本次实践最后需要达到的实际效果(之前已经搭建了简易版的 K8S 环境,所以打算直接把 Drone 丢进了集群中。):
Drone 介绍
Dron 是一个现代化的持续集成平台,它使用强大的云原生pipeline引擎自动化构建、测试和发布工作流。Drone 与多个源代码管理系统无缝集成,包括 GitHub、GitLab、Gitee 和 Gitea 等;它的每个构建都在一个隔离的 Docker 容器中运行;另外它也支持插件,可以使用你熟知的语言轻松的扩展它们。
环境搭建
Drone 提供了集成多种 Serve 类型,原计划是直接采用 Github,但由于国内访问 Github 速度堪忧(服务器上未处理代理),故最后采用了 Gitee。
准备 OAuth
创建一个Gitee OAuth application。Consumer Key和 Consumer Secret用于授权访问Gitee资源(图粘贴自官网)。
备注:应用回调地址必须以 login 结尾,否则登录授权后无法重定向到首页。
创建共享密钥
创建一个共享密钥来验证跑步者和中央无人机服务器之间的通信,随便定义即可,也可以用 openssl 生成共享机密。
yaml
复制代码
$ openssl rand -hex 16
bea26a2221fd8090ea38720fc445eca6
Drone 服务部署
本文以 K8S 集群部署为例,官网 中有 Docker 的部署方式,可以 移步参考。
yaml
复制代码
# 配置信息
apiVersion: v1
kind: ConfigMap
metadata:
name: drone-config
namespace: drone
data:
DRONE_GITEE_CLIENT_ID: '上述截图中的 Client ID'
DRONE_GITEE_CLIENT_SECRET: '上述截图中的 Client Secret'
DRONE_GITEE_SERVER: 'https://gitee.com'
DRONE_GITEE_API_SERVER: 'https://gitee.com/api/v5'
DRONE_SERVER_PROTO: 'http'
DRONE_SERVER_HOST: 'http://192.168.3.26:5080' # Drone 最后部署的地址
DRONE_RPC_SECRET: '上步骤中生成的共享密钥'
DRONE_NAMESPACE_DEFAULT: 'drone'
DRONE_USER_CREATE: 'username:你的Gitee用户名,admin:true' ## 非常重要,配置哪个gitee 用户具备超级管理权限
---
# 定义Drone 专用的PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: drone-pv-10gi
labels:
type: local
spec:
storageClassName: manual-drone
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce # 卷可以被一个节点以读写方式挂载
hostPath:
path: "/mnt/data"
---
# pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
# pvc名称
name: drone-pvc
namespace: drone
labels:
type: local
spec:
# 读写权限
accessModes:
- ReadWriteOnce
# 使用的存储类
storageClassName: manual-drone
# 定义容量
resources:
requests:
storage: 10Gi
---
# service 部署
apiVersion: v1
kind: Service
metadata:
name: drone-service
namespace: drone
spec:
selector:
app: drone
type: LoadBalancer ## 因为是单节点, 因此直接采用了 LoadBalancer
ports:
- name: http
protocol: TCP
port: 5080
targetPort: 80
- name: https
protocol: TCP
port: 5443
targetPort: 443
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: drone-deployment
namespace: drone
spec:
replicas: 1
selector:
matchLabels:
app: drone
template:
metadata:
labels:
app: drone
spec:
containers:
- name: drone
image: drone/drone:2
volumeMounts:
- name: drone-data
mountPath: /data
envFrom:
- configMapRef:
name: drone-config
ports:
- containerPort: 80
- containerPort: 443
resources:
limits:
cpu: 3000m
memory: 2048Mi
requests:
cpu: 150m
memory: 512Mi
volumes:
- name: drone-data
persistentVolumeClaim:
claimName: drone-pvc
直接执行部署
yaml
复制代码
kubectl apply -f deployment.yaml
查看 pod情况, Drone 已经在运行中。
Drone Runner 部署
个人推荐 Docker 形式的 Runner,还是以 K8S 方式部署。
yaml
复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
name: drone-runner
namespace: drone
labels:
app.kubernetes.io/name: drone-runner
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: drone-runner
template:
metadata:
labels:
app.kubernetes.io/name: drone-runner
spec:
containers:
- name: drone-runner
image: drone/drone-runner-kube:latest
ports:
- containerPort: 3000
env:
- name: DRONE_RPC_HOST
#value: drone-service.drone.svc.cluster.local # Drone Server地址
value: http://192.168.3.26:5080 # Drone Server地址
- name: DRONE_RPC_PROTO
value: http
- name: DRONE_RPC_SECRET
valueFrom:
configMapKeyRef:
name: drone-config
key: DRONE_RPC_SECRET # Drone Server部署时候填写的secret共享密
- name: DRONE_NAMESPACE_DEFAULT
valueFrom:
configMapKeyRef:
name: drone-config
key: DRONE_NAMESPACE_DEFAULT # Drone Server部署时候填写的secret共享密
---
apiVersion: v1
kind: Service
metadata:
name: drone-runner-service
namespace: drone
spec:
type: LoadBalancer
selector:
app: drone-runner
ports:
- protocol: TCP
port: 5300
targetPort: 3000
直接执行部署
yaml
复制代码
kubectl apply -f runner.yaml
查看 pod情况, Runner 已经在运行中。
等 Drone Server 和 Runner 完成启动后访问机器Ip+端口, 就看到类似的页面。
流水线配置
服务完成启动成功后就能配置流水线了,本文以一个 NodeJs 的服务为例,描述如何编写流水线的每个步骤(构建镜像、上传镜像、更新服务、飞书群通知)。
DockerFile准备
DockerFile的定义相对简单,可以自行编写,此处是一个简单的示例:
yaml
复制代码
#!/bin/bash
FROM node:16.18.0-alpine
MAINTAINER Marvin
ENV TIME_ZONE Asia/Shanghai
RUN mkdir -p /app ## 执行构建产物的拷贝
COPY ./war/ /app/war
WORKDIR /app
EXPOSE 10001
CMD ["node", "/app/war/index.js" ]
.drone.yml准备
.drone.yml 是 Drone 的规范文件,采用 yml 格式,用来描述流水线步骤。
构建产物
因为是 node 的服务,避免每次构建都从远程下载依赖,所以在流水线中增加了 node_modules 的缓存能力,流水线中通过 volumes 映射宿主文件地址。
yaml
复制代码
kind: pipeline
type: kubernetes
name: default
volumes:
- name: node_modules
host:
path: /home/marvin/cicd/drone/node/node_modules ## 宿主机中的地址,地址可以自己定义
- name: cache
host:
path: /home/marvin/cicd/drone/node/pnpm_cache
steps:
- name: build
image: node:18.20.3-alpine
pull: if-not-exists
volumes:
- name: cache
path: /drone/src/.pnpm-store
commands: ## 此处自己定义项目工程的构建命令
- npm config set registry https://registry.npmmirror.com/ ## 设置淘宝镜像
- npm install -g pnpm
- pnpm config set registry https://registry.npmmirror.com/
- pnpm config set store-dir .pnpm-store
- pnpm i
- pnpm run build:min ## 构建命令,定义在 package.json, 有开发的基础的应该都能理解
when: ## 定义在什么情况下触发构建任务
event:
- push
- tag
branch: master #定义在哪个分支触发构建任务
镜像构建
此步骤主要用于将上个步骤中构建的产物打包成镜像并且上传到自己的镜像仓库,此步骤中采用了 Drone 的插件 Docker, 可以移步查看其它的插件。
yaml
复制代码
- name: image
depends_on: [build] ## 定义这个步骤依赖哪些步骤
image: plugins/docker
privileged: true
pull: if-not-exists
settings:
registry: registry.cn-hangzhou.aliyuncs.com
repo: registry.cn-hangzhou.aliyuncs.com/XXXXXXxxx # 需要上传的镜像仓库地址
use_cache: true
username: # 我使用了阿里云的镜像仓库
from_secret: aliyun_docker_username
password:
from_secret: aliyun_docker_password
tags:
- ${DRONE_COMMIT_SHA:0:7} # 直接截取了commit的7位hash值作为镜像的tag
when:
event:
- push
- tag
branch: master
服务更新
在上个步骤中完成了镜像打包并上传到了镜像仓库,之后就是要将集群中的服务升级到最新的版本(CD 的环节)此步骤中采用了 Drone 的插件 dron8s, 可以移步查看其它的插件。
yaml
复制代码
- name: deploy
image: ghcr.io/bh90210/dron8s:latest
pull: if-not-exists
depends_on: [build-notice]
settings:
yaml: ./deploy/deploy.yaml ## k8s 更新服务的ymal 文件
image_tag: ${DRONE_COMMIT_SHA:0:7}
kubeconfig:
from_secret: kubeconfig # kube 的配置文件
when:
event:
- push
- tag
branch: master
以下是示例的deploy.yaml:
yaml
复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: gpt-user-server
name: gpt-user-server
namespace: gpt
spec:
replicas: 1
selector:
matchLabels:
app: gpt-user-server
template:
metadata:
labels:
app: gpt-user-server
spec:
containers:
image: registry.cn-hangzhou.aliyuncs.com/xxxxxxxx:{{.image_tag}} ## 镜像地址,用双大括号代表变量
imagePullPolicy: IfNotPresent
name: gpt-user-server
ports:
- containerPort: 80
imagePullSecrets:
- name: aliyun-register-pwd
---
apiVersion: v1
kind: Service
metadata:
labels:
app: gpt-user-server
name: gpt-user-server-service
namespace: gpt
spec:
type: NodePort
ports:
- name: gpt-user-server-port
port: 80
protocol: TCP
targetPort: 80
selector:
app: gpt-user-server
飞书通知
按照一般的流程,打包成功、服务更新成功,都需要通知对应的人员,通知的方式非常多,诸如钉钉、飞书、邮件,笔者计划采用飞书群通知。
Drone 没有提供飞书通知类似的插件,应该我自己采用 Go 语言魔改了一个,感兴趣的移步仓库地址。
yaml
复制代码
- name: deploy-notice
depends_on: [deploy]
image: registry.cn-hangzhou.aliyuncs.com/xxxxxxxxxxx/drone-plugin-feishu:4.0
pull: if-not-exists
when:
status:
- success
- failure
settings:
messagetype: DEPLOY
dockergroup:
from_secret: docker_image_group
webhook:
from_secret: feishu_notice_webhook ## 通知的飞书的 webhook 具体如何获取可以参考飞书的文档
secret:
from_secret: feishu_notice_secret ## 飞书应用的密钥
debug: true
仓库激活
以上准备完毕后进入后台管理界面,首先同步仓库,找到需要配置流水线的仓库,进入详情页点击激活仓库按钮。完成激活后在设置tab 打开 Trusted 开关。
- 同步仓库
- 激活仓库(背后的逻辑:Drone 调用了 Gitee 的 OpenApi 给对应的仓库绑定了webhook)。
- 开启仓库为信任的(如果不开启这个配置,可能会导致任务执行过程中 代码clone 失败)。
总结
上述完成后基本就完成了大部分的配置工作,仓库本地修改代码后 push 远程 master 分支后即可会触发构建任务。
Drone 内部的调用也非常简洁,Drone Server 接收到 Gitee 的 WebHook 调用请求后会分配构建任务给可用的 Runner,默认 Runner 会拉取代码(当然开发也可以自定义这个步骤),代码克隆完毕后开始执行开发者通过 .drone.yml 定义的步骤,以下以笔者使用的流水线为例:
备注
基本的 CICD 流程虽然实现了,但其中还是有很多弄得优化细节,感兴趣的同学可以在评论区一起讨论。
- 镜像构建时如何做缓存,构建的镜像如何裁剪?
- 服务升级后如何自动删除之前的镜像?
- 其他好玩的插件也可以推荐推荐。
源文:服务器迁徙大作战(三):基于Drone搭建持续集成环境
如有侵权请联系站点删除!
Technical cooperation service hotline, welcome to inquire!