服务器迁徙大作战(三):基于Drone搭建持续集成环境

服务器迁徙大作战(三):基于Drone搭建持续集成环境

技术博客 admin 496 浏览

继之前完成了服务器硬件搭建、内公网打通后,开始着手将之前基于 github 的流水线迁移到内网环境中。

持续集成/持续部署(Continuous Integration/Continuous Deployment,简称 CI/CD)是一种软件开发实践,旨在通过自动化构建、测试和部署流程来加快软件交付速度、降低错误率,并增强团队协作效率。以下是 CI/CD 的主要概念和流程:

  1. 持续集成(Continuous Integration,CI)
    • 指开发人员频繁将代码集成到共享存储库中,并通过自动化构建和测试流程来验证代码的质量。
    • CI 系统会自动触发构建、运行单元测试、静态代码分析等操作,以确保新代码的质量。
    • 目的是尽早发现和解决代码集成问题,以减少后续集成时的冲突和错误。
  2. 持续部署(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 流程虽然实现了,但其中还是有很多弄得优化细节,感兴趣的同学可以在评论区一起讨论。

  1. 镜像构建时如何做缓存,构建的镜像如何裁剪?
  2. 服务升级后如何自动删除之前的镜像?
  3. 其他好玩的插件也可以推荐推荐。

源文:服务器迁徙大作战(三):基于Drone搭建持续集成环境

如有侵权请联系站点删除!

Technical cooperation service hotline, welcome to inquire!