In this post, we will deploy the Drone Server on Kubernetes, which will be based on ARM since my Kubernetes Cluster is running on Raspberry Pi's using k3s.
Gitea
I will be using Gitea with Drone. I already have a Gitea Server running, if you don't and you want to follow along, you can visit Gitea's Documentation to get your server up and running or you can view my gist to setup a Gitea server on Docker. (its based on amd64 but I've added the links to the dockerhub tags page)
So the first thing we need to do is to create a new OAuth2 Application on Gitea.
From Gitea, hit "Settings", then select "Applications", then select, "Create a new OAuth2 Application", provide an identifiable name, and the redirect URI, which should be to your Drone URL + /login
You will receive a Client ID and Client Secret, keep this in a safe place for now.
Kubernetes Secrets
I will be creating 3 secrets:
DRONE_RPC_SECRET
DRONE_GITEA_CLIENT_ID
DRONE_GITEA_CLIENT_SECRET
First we need to generate a RPC Secret:
$ openssl rand -hex 16
7dfda1f55c255194f723cd3bae9b83fd
There's multiple ways to create secrets, but I will write the values to file, then create the secrets from the files:
$ echo -n "7dfda1f55c255194f723cd3bae9b83fd" > env.DRONE_RPC_SECRET
$ echo -n "my-gitea-client-id-xxxxxxxxxxxxx" > env.DRONE_GITEA_CLIENT_ID
$ echo -n "my-gitea-client-secret-xxxxxxxxx" > env.DRONE_GITEA_CLIENT_SECRET
Now that we've written the values to our files, we can create the secrets:
$ kubectl create secret generic drone-rpc-secret --from-file=env.DRONE_RPC_SECRET="env.DRONE_RPC_SECRET"
$ kubectl create secret generic drone-gitea-client-id --from-file=env.DRONE_GITEA_CLIENT_SECRET="env.DRONE_GITEA_CLIENT_ID"
$ kubectl create secret generic drone-gitea-client-secret --from-file=env.DRONE_GITEA_CLIENT_SECRET="env.DRONE_GITEA_CLIENT_SECRET"
Kubernetes Deployment
Our persistent volume using NFS:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: drone-pv-claim
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
labels:
app: drone-server
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
Our Deployment, here you will see we are referencing our secret's as environment variables:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: drone-server
labels:
app: drone-server
category: cicd
spec:
selector:
matchLabels:
app: drone-server
tier: drone-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: drone-server
tier: drone-server
spec:
containers:
- image: drone/drone:1.9-linux-arm
name: drone-server
env:
- name: DRONE_LOGS_TRACE
value: "true"
- name: DRONE_ADMIN
value: "gitadmin"
- name: DRONE_USER_CREATE
value: "username:gitadmin,admin:true"
- name: DRONE_SERVER_PORT
value: ":80"
- name: DRONE_DATABASE_DRIVER
value: sqlite3
- name: DRONE_DATABASE_DATASOURCE
value: /var/lib/drone/database.sqlite
- name: DRONE_GIT_ALWAYS_AUTH
value: "true"
- name: DRONE_GITEA_SERVER
value: "https://gitea.my-domain.com"
- name: DRONE_RPC_SECRET
valueFrom:
secretKeyRef:
name: drone-rpc-secret
key: env.DRONE_RPC_SECRET
- name: DRONE_SERVER_HOST
value: "drone.my-domain.com"
- name: DRONE_HOST
value: "http://drone-server:80"
- name: DRONE_SERVER_PROTO
value: "https"
- name: DRONE_TLS_AUTOCERT
value: "false"
- name: DRONE_AGENTS_ENABLED
value: "true"
- name: DRONE_GITEA_CLIENT_ID
valueFrom:
secretKeyRef:
name: drone-gitea-client-id
key: env.DRONE_GITEA_CLIENT_ID
- name: DRONE_GITEA_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: drone-gitea-client-secret
key: env.DRONE_GITEA_CLIENT_SECRET
ports:
- containerPort: 80
name: drone-server
volumeMounts:
- name: drone-server-persistent-storage
mountPath: /var/lib/drone
volumes:
- name: drone-server-persistent-storage
persistentVolumeClaim:
claimName: drone-pv-claim
Next our service and ingress:
---
apiVersion: v1
kind: Service
metadata:
name: drone-service
labels:
app: drone-server
category: cicd
spec:
ports:
- name: drone-http
port: 80
targetPort: 80
selector:
app: drone-server
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: drone-ingress
namespace: default
labels:
app: drone-server
category: cicd
annotations:
kubernetes.io/ingress.class: traefik
ingress.kubernetes.io/ssl-redirect: "true"
traefik.backend.loadbalancer.stickiness: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- secretName: drone-my-domain-com-tls
hosts:
- drone.my-domain.com
rules:
- host: drone.my-domain.com
http:
paths:
- path: /
backend:
serviceName: drone-service
servicePort: drone-http
And our final concatenated deployment manifest will look like this:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: drone-pv-claim
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
labels:
app: drone-server
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
---
apiVersion: v1
kind: Service
metadata:
name: drone-service
labels:
app: drone-server
category: cicd
spec:
ports:
- name: drone-http
port: 80
targetPort: 80
selector:
app: drone-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: drone-server
labels:
app: drone-server
category: cicd
spec:
selector:
matchLabels:
app: drone-server
tier: drone-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: drone-server
tier: drone-server
spec:
containers:
- image: drone/drone:1.9-linux-arm
name: drone-server
env:
# https://docs.drone.io/server/reference/
- name: DRONE_LOGS_TRACE
value: "true"
- name: DRONE_ADMIN
value: "gitadmin"
- name: DRONE_USER_CREATE
value: "username:gitadmin,admin:true"
- name: DRONE_SERVER_PORT
value: ":80"
- name: DRONE_DATABASE_DRIVER
value: sqlite3
# https://docs.drone.io/installation/reference/drone-database-driver/
- name: DRONE_DATABASE_DATASOURCE
value: /var/lib/drone/database.sqlite
# https://discourse.drone.io/t/fatal-could-not-read-username-for/6198
- name: DRONE_GIT_ALWAYS_AUTH
value: "true"
- name: DRONE_GITEA_SERVER
value: "https://gitea.my-domain.com"
- name: DRONE_RPC_SECRET
valueFrom:
secretKeyRef:
name: drone-rpc-secret
key: env.DRONE_RPC_SECRET
- name: DRONE_SERVER_HOST
value: "drone.my-domain.com"
- name: DRONE_HOST
value: "http://drone-server:80"
- name: DRONE_SERVER_PROTO
value: "https"
- name: DRONE_TLS_AUTOCERT
value: "false"
- name: DRONE_AGENTS_ENABLED
value: "true"
- name: DRONE_GITEA_CLIENT_ID
valueFrom:
secretKeyRef:
name: drone-gitea-client-id
key: env.DRONE_GITEA_CLIENT_ID
- name: DRONE_GITEA_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: drone-gitea-client-secret
key: env.DRONE_GITEA_CLIENT_SECRET
ports:
- containerPort: 80
name: drone-server
volumeMounts:
- name: drone-server-persistent-storage
mountPath: /var/lib/drone
volumes:
- name: drone-server-persistent-storage
persistentVolumeClaim:
claimName: drone-pv-claim
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: drone-ingress
namespace: default
labels:
app: drone-server
category: cicd
annotations:
kubernetes.io/ingress.class: traefik
ingress.kubernetes.io/ssl-redirect: "true"
traefik.backend.loadbalancer.stickiness: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- secretName: drone-my-domain-com-tls
hosts:
- drone.my-domain.com
rules:
- host: drone.my-domain.com
http:
paths:
- path: /
backend:
serviceName: drone-service
servicePort: drone-http
Deploy
Deploy Drone Server to your Kubernetes Cluster:
$ kubectl apply -f drone-server-deployment.yml
After some time verify that the deployment has checked into it's desired state:
$ kubectl get deployments -l app=drone-server
NAME READY UP-TO-DATE AVAILABLE AGE
drone-server 1/1 1 1 1d
Access Drone
To access drone, we need to authorize drone with our configured oauth2 application that we created.
Access drone on the configured url, in this demonstration (drone.my-domain.com) then it will redirect to Gitea to login with your Gitea credentials and once successful authentication you will be redirected back to drone and you should be logged on.
In the next post we will setup a example CI pipeline with Drone.