开源CI&CD工具-Drone

目录

一.简介

二.组件介绍

Drone Server(调度与管理中心)

Drone Runner(任务执行者)

三.配置部署

3.1 配置Gitee

设置仓库Webhooks

设置Gitee第三方应用

仓库中添加配置文件

3.2 部署Drone Server和Drone Runner

Drone Server

Drone Runner

3.3 初始化Drone Web UI

 3.4 测试


一.简介

        在现代软件开发流程中,持续集成(CI)与持续部署(CD)已成为团队快速交付与稳定迭代的关键环节。
        Drone作为一个轻量级、云原生的 CI/CD 系统,以其容器化、易扩展、高度自动化的特性,被越来越多团队用于替代传统的Jenkins、GitLab CI等工具。

        本文将介绍一个基于Drone + GitLab + Harbor + Kubernetes的完整自动化构建与部署架构,帮助开发者实现从代码提交到应用上线的全自动流水线。

        整个CI&CD流程如上面数据访问流所示:

        1.当开发者向代码仓库推送代码或合并分支时,代码仓库通过Webhook通知Drone Server;
        2.Drone Server分配任务给Drone Runner执行代码拉取、编译构建、生成容器镜像、推送到容器仓库、部署到Kubernetes或者服务器上、自定义的测试验证脚本等;
        整个过程完全自动化,构建日志和执行状态可通过Drone Web UI实时查看。

二.组件介绍

Drone Server(调度与管理中心)

        Drone Server是整个流水线的核心调度控制器,主要功能包括:

  • 接收来自GitLab等代码仓库的Webhook请求;

  • 校验签名与权限;

  • 解析项目根目录下的 .drone.yml配置文件;

  • 调度Drone Runner执行任务;

  • 提供Web UI用于实时查看:

    • 仓库与分支列表;

    • 构建历史与执行日志;

    • 每个步骤的状态(成功 / 失败 / 执行中)。

        Server与Runner之间通过gRPC / HTTP API通信,并使用DRONE_RPC_SECRET进行身份认证。

Drone Runner(任务执行者)

        Drone Runner是一个常驻的执行代理,负责从Drone Server拉取构建任务并启动实际的执行环境。它本身并不直接执行构建步骤,而是根据 .drone.yml文件的内容,动态创建临时容器(或 Kubernetes Pod) 来完成各个步骤的操作。

        整个过程如下:

  1. 拉取任务:Drone Runner从Drone Server轮询新的构建请求;

  2. 准备环境:为本次构建创建一个隔离的执行环境(Docker容器或 K8s Pod);

  3. 执行步骤:按照 .drone.yml中定义的每个step,依次启动临时容器执行任务;

  4. 上报结果:任务执行完毕后,Runner收集所有步骤的日志与状态,通过gRPC / HTTP回传给Drone Server;

  5. 清理环境:临时容器或Pod自动销毁,确保环境干净、可复用。

        Drone Runner 支持多实例并行运行,不同Runner可通过标签(Label)区分职责,这样既保证了任务隔离,又能提升整个CI&CD集群的执行效率。

三.配置部署

        我们以Kubernetes和Gitee代码库为例来演示整个Drone环境的搭建并测试CI&CD流程。

        首先规划一个可供访问的域名,来代理Drone Server的Webhook,例如https://drone.fzwtest.xyz,虽然是个人测试环境,但为了保证安全性,我们只允许来自Gitee的出口IP地址段访问Drone的443端口。

3.1 配置Gitee

        创建一个用来测试的代码仓库,然后做如下配置

设置仓库Webhooks

 

设置Gitee第三方应用

        注意这是全局设置,不是代码仓库的设置,用来允许Drone Server回调Gitee API

仓库中添加配置文件

        如下图所示,.drone.yml中定义CI&CD要执行的命令,Dockerfile和Kubernetes-cd.yaml是本次示例的.drone.yml用到的两个文件(根据实际情况添加)

        .drone.yml文件内容如下,其中git clone阶段选择自定义执行命令,没有用它的集成组件:

kind: pipeline
type: kubernetes
name: cidemo
metadata:
  namespace: drone
service_account_name: drone-ci
clone:
  disable: true

steps:
  - name: ci-Gitclone
    image: alpine/git
    environment:
      SSH_PRIVATE_KEY:
        from_secret: ssh_private_key
    commands:
      - echo "beginclone"
      - mkdir -p ~/.ssh
      - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
      - chmod 600 ~/.ssh/id_rsa
      - ssh-keyscan -H gitee.com >> ~/.ssh/known_hosts
      - git clone git@gitee.com:fan-zhiwei1211/fund-distribution.git;cd fund-distribution && git checkout dev
      - cp -r . ../
      - echo "endclone"
      - echo `date +"%Y-%m-%d_%H-%M-%S"`","`git rev-parse HEAD | cut -c 1-7` > /drone/src/.tags
      - cat /drone/src/.tags


  - name: ci-ImagePush
    image: docker:20.10-dind
    privileged: true
    environment:
      DOCKER_USERNAME:
        from_secret: DOCKER_USERNAME
      DOCKER_PASSWORD:
        from_secret: DOCKER_PASSWORD
    volumes:
      - name: dockersock
        host:
          path: /var/run
    commands:
      - dockerd-entrypoint.sh &  # 启动 docker daemon
      - sleep 10
      - docker version
      - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com
      - TAG=$(cut -d ',' -f 1 /drone/src/.tags)
      - echo "Using tag:$TAG"
      - docker build -t crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com/dronefzw/fund-distribution:$TAG .
      - docker push crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com/dronefzw/fund-distribution:$TAG



  - name: cd-DeployToK8s
    image: bitnami/kubectl
    working_dir: /drone/src
    privileged: true
    environment:
      myuser:
        from_secret: DOCKER_USERNAME
      mypass:
        from_secret: DOCKER_PASSWORD
    commands:
      - pwd && ls /drone/src/
      - sed -i "s|IMAGE_TAG|`cat /drone/src/.tags |cut -d "," -f 1`|g" /drone/src/kubernetes-cd.yaml
      - kubectl apply -f /drone/src/kubernetes-cd.yaml
      - kubectl rollout status deployment/fund-distribution -n dev --timeout=10s
      - kubectl get pod -l app=fund-distribution -n dev -o wide

  - name: cd-test-api-with-curl
    image: curlimages/curl:latest
    commands:
      - |
        echo "开始测试API..."
        sleep 10
        RESPONSE_CODE=$(curl -o /dev/null -s -w "%{http_code}" http://fund-distribution.dev.svc.cluster.local:8124/)
        echo "获取到状态码: $RESPONSE_CODE" 
        if [ "$RESPONSE_CODE" != "200" ]; then
          echo "测试失败,状态码: $RESPONSE_CODE (期望200)" >&2
          exit 1  # 明确返回非0状态码
        else
          echo "测试通过"
        fi

trigger:
  event:
    - push
  branches:
    - dev

        Dockerfile内容如下:

# 使用轻量级基础镜像
#FROM docker.m.daocloud.io/library/python:3.11-slim
FROM crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com/dronefzw/fund-distribution:basic
WORKDIR /app
# 复制所有文件到容器中
COPY . /app

# 安装依赖(如果你有 requirements.txt)
#RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
# 暴露 80 端口
EXPOSE 8888

# 启动 nginx
CMD ["python3", "run.py"]

        kubernetes-cd.yaml内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fund-distribution
  namespace: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fund-distribution
  template:
    metadata:
      labels:
        app: fund-distribution
    spec:
      imagePullSecrets:
        - name: acr-plaintext-secret
      containers:
        - name: fund-distribution
          image: crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com/dronefzw/fund-distribution:IMAGE_TAG
          ports:
            - containerPort: 8888

---
apiVersion: v1
kind: Service
metadata:
  name: fund-distribution
  namespace: dev
spec:
  selector:
    app: fund-distribution
  ports:
    - protocol: TCP
      port: 8124
      targetPort: 8888
      nodePort: 32669
  type: NodePort

3.2 部署Drone Server和Drone Runner

Drone Server

apiVersion: v1
kind: ServiceAccount
metadata:
  name: drone-ci
  namespace: drone
automountServiceAccountToken: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: drone-secrets-manager
  namespace: drone
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "pods/exec","secrets"]
  verbs: ["get", "create","update", "delete","list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: drone-secrets-binding
  namespace: drone
subjects:
- kind: ServiceAccount
  name: drone-ci
  namespace: drone
roleRef:
  kind: Role
  name: drone-secrets-manager
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: docker-sock-access
rules:
- apiGroups: [""]
  resources: ["nodes/proxy"]
  verbs: ["get", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: drone-docker-access
subjects:
- kind: ServiceAccount
  name: drone-ci
  namespace: drone
roleRef:
  kind: ClusterRole
  name: docker-sock-access
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: drone-ci-role
  namespace: dev  # 注意:需要与测试用例部署的namespace一致
rules:
- apiGroups: ["apps", ""]
  resources: ["deployments", "pods", "services"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: drone-ci-rolebinding
  namespace: dev # 注意:需要与测试用例部署的namespace一致
subjects:
- kind: ServiceAccount
  name: drone-ci
  namespace: drone
roleRef:
  kind: Role
  name: drone-ci-role
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: drone-config
  namespace: drone
data:
  DRONE_GITEE_CLIENT_ID: "xxxxxxxxx" #自行修改
  DRONE_GITEE_CLIENT_SECRET: "xxxxxxxxxx" #自行修改
  DRONE_GITEE_SECRET:"xxxxxxxxxx"  #自行修改,可选配置
  DRONE_RPC_SECRET: "sdsgrgty54t4rereer"
  DRONE_SERVER_HOST: "drone.fzwtest.xyz"
  DRONE_SERVER_PROTO: "https"
  DRONE_SERVER_PORT: ":80"
  DRONE_LOGS_DEBUG: "true"
  DRONE_USER_CREATE: "username:fan-zhiwei1211_admin,admin:true"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: drone-ingress
  namespace: drone
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - drone.fzwtest.xyz
      secretName: drone-tlss
  rules:
    - host: drone.fzwtest.xyz
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: drone-server
                port:
                  number: 80
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: drone-pvc
  namespace: drone
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: drone-server
  namespace: drone
spec:
  replicas: 1
  selector:
    matchLabels:
      app: drone-server
  template:
    metadata:
      labels:
        app: drone-server
    spec:
      serviceAccountName: drone-ci
      containers:
      - name: drone-server
        image: drone/drone:2.17
        envFrom:
        - configMapRef:
            name: drone-config
        securityContext:
            runAsUser: 0
            capabilities:
                add: ["NET_BIND_SERVICE"]  # 允许绑定特权端口
        ports:
        - containerPort: 80
        volumeMounts:
        - name: drone-data
          mountPath: /data
      volumes:
      - name: drone-data
        persistentVolumeClaim:
          claimName: drone-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: drone-server
  namespace: drone
spec:
  ports:
  - port: 80
    targetPort: 80
    name: http
  - port: 443
    targetPort: 443
    name: https
  selector:
    app: drone-server

Drone Runner

apiVersion: v1
kind: Secret
metadata:
  name: acr-plaintext-secret
  namespace: dev  # 按需修改命名空间
type: kubernetes.io/dockerconfigjson
stringData:  # 注意这里使用 stringData 而非 data
  .dockerconfigjson: |
    {
      "auths": {
        "crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com": {
          "username": "xxxx", #容器仓库鉴权,自行修改
          "password": "xxxxxxx", #容器仓库鉴权,自行修改
          "auth": "xxxxxxxx" #容器仓库鉴权,自行修改
        }
      }
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: drone-runner
  namespace: drone
spec:
  replicas: 1
  selector:
    matchLabels:
      app: drone-runner
  template:
    metadata:
      labels:
        app: drone-runner
    spec:
      serviceAccountName: drone-ci
      containers:
      - name: drone-runner
        image: drone/drone-runner-kube:latest
        env:
        - name: DRONE_RPC_HOST
          value: "drone-server.drone.svc.cluster.local"  # K8S 内部 Service 地址
        - name: DRONE_RPC_SECRET
          valueFrom:
            configMapKeyRef:
              name: drone-config
              key: DRONE_RPC_SECRET
        - name: DRONE_RUNNER_CAPACITY
          value: "2"
        - name: DRONE_RUNNER_PRIVILEGED_IMAGES
          value: "plugins/docker,appleboy/drone-docker"

        部署到Kubernetes中,确保服务运行正常:

# kubectl get pod -n drone
NAME                            READY   STATUS    RESTARTS   AGE
drone-runner-f5cd6496b-s7wbl    1/1     Running   0          31s
drone-server-6c6d4d778b-5tzrv   1/1     Running   0          33s

3.3 初始化Drone Web UI

        登录Drone的域名 https://drone.fzwtest.xyz/ 并同意授权,进入操作页面

        可以点击SYNC同步仓库,点击我们的示例仓库,激活CI&CD集成

        可以添加自己需要的Secrets,用来.drone.yml的参数调用。例如我们示例中的DOCKER_USERNAME和DOCKER_PASSWORD以及ssh_private_key

 3.4 测试

        现在提交代码到dev分支,会看到自动触发了CI&CD流程,并且能看到.drone.yml中定义的几个step的执行日志。

        查看我们的测试服务已经成功部署

# kubectl get pod -n dev
NAME                                READY   STATUS    RESTARTS   AGE
fund-distribution-9478b5f84-825h7   1/1     Running   0          28m

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炸裂狸花猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值