For quite some time I wanted to give Drone a spin. As I had a Gitea running on my Kubernetes cluster already it looked to me like the perfect match. Unfortunately getting started, with the constraints of running the Open Source version on an ARM-based architecture (Raspberry Pis), wasn't as easy as I first thought…
What is the difference between Open Source and Enterprise?
The Open Source Edition is fully capable of executing complex pipelines but is optimized to meet basic needs of individuals and small teams.
The Enterprise Edition:
- Supports distributed runners
- Supports kubernetes runners
- Supports organization secrets, vault secrets, etc
- Supports cron scheduling
- Supports scalable storage (postgres, mysql, s3)
- Supports autoscaling
- Supports extensions
The Open Source Edition:
- Lacks support for extensions
- Lacks support for distributed runners
- Lacks support for kubernetes runners
- Lacks support for organization secrets, vault secrets, etc
- Lacks support for cron scheduling
- Lacks support for autoscaling
- Limited to a single machine
- Limited to an embedded sqlite database
Read more about at: https://docs.drone.io/enterprise/
Building Drone
Github has a good explanation on how to build the Open Source Edition of Drone. Unfortunately they are not releasing the free version as ready-to-use image on Docker Hub. At least there's a Dockerfile to build the standard drone-server on ARM. So I came up with my own version of a Dockerfile:
# docker build --rm -f docker/Dockerfile -t drone/drone .
# Build stage
###################################
# https://hub.docker.com/_/golang?tab=tags
FROM golang:1.13-alpine3.11 AS build-env
ARG DRONE_VERSION
ARG GOARCH
ARG GOOS
ENV GOARCH=${GOARCH:-arm}
ENV GOOS=${GOOS:-linux}
# Build deps
RUN apk --no-cache add build-base ca-certificates git
# Clone repo
RUN git clone https://github.com/drone/drone.git /go/src/github.com/drone/drone
# Change Workdir
WORKDIR /go/src/github.com/drone/drone
# Checkout version tag if set
RUN if [ -n "${DRONE_VERSION}" ]; then git checkout -b "${DRONE_VERSION}" "tags/${DRONE_VERSION}"; fi \
&& echo "Build Drone (Open Source Edition) for ${GOOS}/${GOARCH} ..." \
&& GOARCH="${GOARCH}" GOOS="${GOOS}" go install -tags "oss nolimit" github.com/drone/drone/cmd/drone-server
# Final stage
###################################
# https://hub.docker.com/_/alpine?tab=tags
FROM alpine:3.11
EXPOSE 80 443
VOLUME /data
# Borrowed from: https://github.com/drone/drone/blob/master/docker/Dockerfile.server.linux.arm
RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf
ENV GODEBUG netdns=go
ENV XDG_CACHE_HOME /data
ENV DRONE_DATABASE_DRIVER sqlite3
ENV DRONE_DATABASE_DATASOURCE /data/database.sqlite
ENV DRONE_RUNNER_OS=linux
ENV DRONE_RUNNER_ARCH=arm
ENV DRONE_SERVER_PORT=:80
ENV DRONE_SERVER_HOST=localhost
ENV DRONE_DATADOG_ENABLED=false
COPY --from=build-env /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build-env /go/bin/drone-server /bin/drone-server
CMD ["/bin/drone-server"]
Note: A build takes approximately 11 minutes on a Raspberry Pi 4.
Running Drone
The setup guide for Gitea is pretty straight forward, but it is written for the Enterprise edition and expects you to install a separate Runner, which isn't supported by the OSS version. After some googling, I found an option called DRONE_AGENTS_DISABLED
that isn't mentioned anywhere in the Server reference.
But the Changelog for version 1.5.0 states:
The server has been changed to run in multi-machine mode by default. If you are already running the server in multi-machine mode (e.g. with agents) this does not impact you. If you are running the server in single-machine mode you must set the following configuration parameter moving forward:
DRONE_AGENTS_DISABLED=true
Please note that failing to set this parameter will result in your builds sitting in the queue, in a pending state, waiting for agents to connect to the server and process your build.
Putting things together I came up with the following command:
docker run \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--volume=/var/lib/drone:/data \
--env=DRONE_GITEA_SERVER={{DRONE_GITEA_SERVER}} \
--env=DRONE_GITEA_CLIENT_ID={{DRONE_GITEA_CLIENT_ID}} \
--env=DRONE_GITEA_CLIENT_SECRET={{DRONE_GITEA_CLIENT_SECRET}} \
--env=DRONE_SERVER_PROTO={{DRONE_SERVER_PROTO}} \
--env=DRONE_SERVER_HOST={{DRONE_SERVER_HOST}} \
--env=DRONE_AGENTS_DISABLED=true \
--publish=80:80 \
--publish=443:443 \
--restart=always \
--detach=true \
--name=drone \
drone/drone
Note: You don't have to specify DRONE_RPC_SECRET
since you are running in single-machine mode. And you need to provide a volume for the docker.sock to server instead of the (not existing) agent.
Everytime I tried to initiate an automated build it got stuck in "Pending…" state.
I found two valuable options DRONE_LOGS_DEBUG
and DRONE_LOGS_TRACE
in the server reference to dig into this problem.
This time running the command with trace logs enabled drone gave me the following output:
runner:
local: false
image: drone/controller:1
platform: linux/amd64
os: linux
arch: arm
kernel: ""
variant: ""
machine: {{machine_id}}
capacity: 2
labels: {}
volumes: []
networks: []
devices: []
privileged: []
environ: {}
limits:
memswaplimit: 0
memlimit: 0
shmsize: 0
cpuquota: 0
cpushares: 0
cpuset: ""
After fiddling around, I am now running Drone with the following options:
docker run \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--volume=/var/lib/drone:/data \
--env=DRONE_GITEA_SERVER={{DRONE_GITEA_SERVER}} \
--env=DRONE_GITEA_CLIENT_ID={{DRONE_GITEA_CLIENT_ID}} \
--env=DRONE_GITEA_CLIENT_SECRET={{DRONE_GITEA_CLIENT_SECRET}} \
--env=DRONE_SERVER_PROTO={{DRONE_SERVER_PROTO}} \
--env=DRONE_SERVER_HOST={{DRONE_SERVER_HOST}} \
--env=DRONE_AGENTS_DISABLED=true \
--env=DRONE_RUNNER_LOCAL=true \
--env=DRONE_RUNNER_PLATFORM=linux/arm \
--env=DRONE_RUNNER_ARCH=arm \
--env=DRONE_RUNNER_IMAGE=drone/drone-runner-docker:latest \
--env=DRONE_RUNNER_CAPACITY=1 \
--publish=80:80 \
--publish=443:443 \
--restart=always \
--detach=true \
--name=drone \
drone/drone
Note: DRONE_RUNNER_IMAGE has been changed from drone/controller
to drone/drone-runner-docker
.
Boy, what a ride…
Bonus: Building Drone with Drone
Rounding things up, I wanted to build Drone with Drone. Here's my .drone.yml
file:
---
kind: pipeline
type: docker
name: linux-arm
# Required form ARM: defaults to Linux amd64
platform:
arch: arm
os: linux
steps:
- name: Build & Publish
image: plugins/docker:18
settings:
dockerfile: docker/Dockerfile
repo: {{registry}}/drone/drone
build_args:
- DRONE_VERSION=v1.7.0
tags:
- "v1.7.0"
- "latest"
trigger:
event:
- push
- tag