目录
[Drone Server(调度与管理中心)](#Drone Server(调度与管理中心))
[Drone Runner(任务执行者)](#Drone Runner(任务执行者))
[3.1 配置Gitee](#3.1 配置Gitee)
[3.2 部署Drone Server和Drone Runner](#3.2 部署Drone Server和Drone Runner)
[Drone Server](#Drone Server)
[Drone Runner](#Drone Runner)
[3.3 初始化Drone Web UI](#3.3 初始化Drone Web UI)
[3.4 测试](#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: 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