基于Drone CI的持续集成方案

我最早接触的持续集成工具是大名鼎鼎的 Jenkins,接手同事留下的一个单节点 Jenkins。由于没有太强烈的使用需求,体会不到 Jenkins 的强大,印象停留在 “麻烦” 两个字:需要给研发同事手动添加账号,然后用一个有很多很多方框的表单来设置权限。而 Drone CI 在账号权限方面就给人眼前一亮的感觉,直接集成代码仓库,和 Github Actions,Travis CI 等使用流程类似。另外,Drone CI 原生基于容器,插件直接做成镜像,这样写出来的 yaml 配置文件可以做到简短清晰。本文记录 Drone CI 使用过程中遇到的问题及解决方案,基于 1.6.1 版本,可能不适用于最新版本。

如有兴趣,可以看看我个人的 Drone CI 的运行效果:https://ci.annhe.net/annProg/chart

安装

代码仓库为 Gitea,使用 Kubernetes 安装,参考配置如下。

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: drone-server-rbd-rbd1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: rbd
  resources:
    requests:
      storage: 200Gi
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: drone-rbac
subjects:
  - kind: ServiceAccount
    name: default
    namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: drone-server
  namespace: default
  labels:
    app: drone-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: drone-server
  strategy:
    type: RollingUpdate
    # 由于挂载了 rbd,需要先销毁pod才能创建新的
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 0
  template:
    metadata:
      labels:
        app: drone-server
    spec:
      containers:
        - name: drone-server
          image: cr.xxx.com/drone/drone:1.6.1
          env:
            - name: UPDATEAT
              value: 2019-10-27-21:22
            - name: DRONE_KUBERNETES_ENABLED
              value: "true"
            - name: DRONE_KUBERNETES_NAMESPACE
              value: default
            - name: DRONE_GITEA_SERVER
              value: https://git.xxx.com
            - name: DRONE_GITEA_CLIENT_ID
              value: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
            - name: DRONE_GITEA_CLIENT_SECRET
              value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            - name: DRONE_SERVER_HOST
              value: ci.xxx.com
            - name: DRONE_SERVER_PROTO
              value: https
            - name: DRONE_USER_CREATE
              value: username:root,admin:true
            - name: DRONE_GIT_ALWAYS_AUTH
              value: "true"
            - name: DRONE_RPC_SECRET
              value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            - name: DRONE_LOGS_DEBUG
              value: "true"
            - name: DRONE_LOGS_TRACE
              value: "true"
            - name: DRONE_LOGS_PRETTY
              value: "true"
            - name: DRONE_GIT_IMAGE
              value: cr.xxx.com/drone/git:latest 
            # 此镜像修复了 k8s 上 image pull policy 问题
            # tag 为 latest 且未指定 pull policy 时,policy 为 always
            - name: DRONE_KUBERNETES_IMAGE
              value: cr.xxx.com/drone/controller:1.6.1-1
            - name: DRONE_KUBERNETES_IMAGE_PULL
              value: IfNotExists
          ports:
            - name: http
              containerPort: 80
          volumeMounts:
            - name: data
              mountPath: "/data"
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: drone-server-rbd-rbd1
      restartPolicy: Always

---
kind: Service
apiVersion: v1
metadata:
  name: drone-server-service
  namespace: default
spec:
  type: NodePort
  selector:
    app: drone-server
  ports:
    - protocol: TCP
      port: 80
      name: http
      targetPort: 80
      nodePort: 30000

然后使用 Ingress Nginx 做负载均衡,注意需要增大 proxy_read_timeout 的值,默认的 3s 对于 drone 的 api/stream 接口可能太短。

Drone CI nginx 配置

使用kaniko

我对 CI 的主要需求是做镜像,kaniko 这种不需要特权模式就能生成镜像的工具是很好的选择。使用 Drone CI 的 kaniko 插件,可以参考以下几点:

用 Drone CI 生成镜像的一个例子,可以看到 yaml 配置很简洁。

kind: pipeline
name: chart

steps:
- name: build
  image: banzaicloud/drone-kaniko:0.5.1
  settings:
    repo: ann17/chart
    auto_tag: true
    username:
      from_secret: username
    password:
      from_secret: password

问题记录

插件镜像不更新

安装部分 yaml 中有以下代码:

            # 此镜像修复了 k8s 上 image pull policy 问题
            # tag 为 latest 且未指定 pull policy 时,policy 为 always
            - name: DRONE_KUBERNETES_IMAGE
              value: cr.xxx.com/drone/controller:1.6.1-1

这里的修复方案针对 1.6.1 版本,新版本可能不存在此问题了。修复方案见 Github。使用此分支重新编译 drone-controller。在 Drone 代码的 go.mod 中添加以下代码后重新编译。

replace github.com/drone/drone-runtime => github.com/ops-itop/drone-runtime k8s-pullpolicy

另外,如果不重新编译,也可以在 yaml 中手动指定 pull policy 来解决:

pull: always

但是对于 taglatest 的镜像,自动设置为 always 才更合理。

secret管理

只能给 repo 添加 secret,secret 更改比较麻烦。考虑提issue,增加用户级别的 secret,比如,用户级别的 docker push password,当 repo 中未定义 docker push password,yaml中又引用时,使用用户级别的 docker push password。但是搜索到一个 issue,见 drone/issues/1619 ,提出一个 Organization 级别的 secret 概念,被作者给否了,作者提出一个变通的方案:使用命令行批量设置 secret,代码如下。

drone repo ls | xargs -I{} drone secret add {} KEY VALUE

特定分支触发

以下示例,在 k8s-sidecar 分支 push 或者创建 tag 时触发 CI。

  when:
    ref:
    - refs/heads/k8s-sidecar
    - refs/tags/*
 
----或者用trigger
trigger:
  ref:
  - refs/heads/test
  - refs/tags/* 

参考资料

1. https://drone.io/
2. https://discourse.drone.io/t/drone-1-3-1-on-kubernetes-not-pull-latest-plugin-image/6088
3. https://docker-runner.docs.drone.io/configuration/conditions/
4. https://discourse.drone.io/t/execute-a-build-step-only-when-a-feature-branch-is-tagged-with-a-specific-pattern/1237/3

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注