开源CICD工具-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) 来完成各个步骤的操作。
整个过程如下:
-
拉取任务:Drone Runner从Drone Server轮询新的构建请求;
-
准备环境:为本次构建创建一个隔离的执行环境(Docker容器或 K8s Pod);
-
执行步骤:按照
.drone.yml中定义的每个step,依次启动临时容器执行任务; -
上报结果:任务执行完毕后,Runner收集所有步骤的日志与状态,通过gRPC / HTTP回传给Drone Server;
-
清理环境:临时容器或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: truesteps:- name: ci-Gitcloneimage: alpine/gitenvironment:SSH_PRIVATE_KEY:from_secret: ssh_private_keycommands:- 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-ImagePushimage: docker:20.10-dindprivileged: trueenvironment:DOCKER_USERNAME:from_secret: DOCKER_USERNAMEDOCKER_PASSWORD:from_secret: DOCKER_PASSWORDvolumes:- name: dockersockhost:path: /var/runcommands:- 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-DeployToK8simage: bitnami/kubectlworking_dir: /drone/srcprivileged: trueenvironment:myuser:from_secret: DOCKER_USERNAMEmypass:from_secret: DOCKER_PASSWORDcommands:- 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-curlimage: curlimages/curl:latestcommands:- |echo "开始测试API..."sleep 10RESPONSE_CODE=$(curl -o /dev/null -s -w "%{http_code}" http://fund-distribution.dev.svc.cluster.local:8124/)echo "获取到状态码: $RESPONSE_CODE" if [ "$RESPONSE_CODE" != "200" ]; thenecho "测试失败,状态码: $RESPONSE_CODE (期望200)" >&2exit 1 # 明确返回非0状态码elseecho "测试通过"fitrigger:event:- pushbranches:- 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-distributionnamespace: dev
spec:replicas: 1selector:matchLabels:app: fund-distributiontemplate:metadata:labels:app: fund-distributionspec:imagePullSecrets:- name: acr-plaintext-secretcontainers:- name: fund-distributionimage: crpi-d7xhghcft4dbtjor.cn-beijing.personal.cr.aliyuncs.com/dronefzw/fund-distribution:IMAGE_TAGports:- containerPort: 8888---
apiVersion: v1
kind: Service
metadata:name: fund-distributionnamespace: dev
spec:selector:app: fund-distributionports:- protocol: TCPport: 8124targetPort: 8888nodePort: 32669type: NodePort
3.2 部署Drone Server和Drone Runner
Drone Server
apiVersion: v1
kind: ServiceAccount
metadata:name: drone-cinamespace: drone
automountServiceAccountToken: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: drone-secrets-managernamespace: 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-bindingnamespace: drone
subjects:
- kind: ServiceAccountname: drone-cinamespace: drone
roleRef:kind: Rolename: drone-secrets-managerapiGroup: 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: ServiceAccountname: drone-cinamespace: drone
roleRef:kind: ClusterRolename: docker-sock-accessapiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: drone-ci-rolenamespace: 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-rolebindingnamespace: dev # 注意:需要与测试用例部署的namespace一致
subjects:
- kind: ServiceAccountname: drone-cinamespace: drone
roleRef:kind: Rolename: drone-ci-roleapiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:name: drone-confignamespace: 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-ingressnamespace: droneannotations:cert-manager.io/cluster-issuer: "letsencrypt-prod"nginx.ingress.kubernetes.io/backend-protocol: "HTTP"nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:ingressClassName: nginxtls:- hosts:- drone.fzwtest.xyzsecretName: drone-tlssrules:- host: drone.fzwtest.xyzhttp:paths:- path: /pathType: Prefixbackend:service:name: drone-serverport:number: 80
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: drone-pvcnamespace: drone
spec:accessModes:- ReadWriteOnceresources:requests:storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:name: drone-servernamespace: drone
spec:replicas: 1selector:matchLabels:app: drone-servertemplate:metadata:labels:app: drone-serverspec:serviceAccountName: drone-cicontainers:- name: drone-serverimage: drone/drone:2.17envFrom:- configMapRef:name: drone-configsecurityContext:runAsUser: 0capabilities:add: ["NET_BIND_SERVICE"] # 允许绑定特权端口ports:- containerPort: 80volumeMounts:- name: drone-datamountPath: /datavolumes:- name: drone-datapersistentVolumeClaim:claimName: drone-pvc---
apiVersion: v1
kind: Service
metadata:name: drone-servernamespace: drone
spec:ports:- port: 80targetPort: 80name: http- port: 443targetPort: 443name: httpsselector:app: drone-server
Drone Runner
apiVersion: v1
kind: Secret
metadata:name: acr-plaintext-secretnamespace: 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-runnernamespace: drone
spec:replicas: 1selector:matchLabels:app: drone-runnertemplate:metadata:labels:app: drone-runnerspec:serviceAccountName: drone-cicontainers:- name: drone-runnerimage: drone/drone-runner-kube:latestenv:- name: DRONE_RPC_HOSTvalue: "drone-server.drone.svc.cluster.local" # K8S 内部 Service 地址- name: DRONE_RPC_SECRETvalueFrom:configMapKeyRef:name: drone-configkey: DRONE_RPC_SECRET- name: DRONE_RUNNER_CAPACITYvalue: "2"- name: DRONE_RUNNER_PRIVILEGED_IMAGESvalue: "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
