Setting up Continuous Integration using Drone CI
Published on September 23, 2024 | Last updated on September 23, 2024
In this post I setup Drone CI to build my code automatically when pushed to Github.
I'm choosing Drone CI because it is easy to deploy via Docker, and supports defining builds using a simple YAML syntax.
This is a brief blog post, giving me time to write about the painful process of deploying a site using Ansible alone, which will pave the way for a streamlined process using Terraform later.
Install Drone Control Node
Steps:
- Select a node to run Drone CI on. Setup DNS and SSL termination via a load balancer if desired.
- Whip up a
compose.yaml
file based on their example command line invocation
version: '3'
services:
drone:
image: drone/drone:2
container_name: drone
volumes:
- /var/lib/drone:/data
environment:
- DRONE_GITHUB_CLIENT_ID=xxxx
- DRONE_GITHUB_CLIENT_SECRET=xxxx
- DRONE_RPC_SECRET=xxxx
- DRONE_SERVER_HOST=drone.example.com
- DRONE_SERVER_PROTO=https
- DRONE_USER_FILTER=xxxx
ports:
- "11080:80"
- "11443:443"
restart: always
- Fire it up with
docker compose up -d
- Fire up a runner node by creating a similar
compose.yaml
for the runner:
services:
runner:
image: drone/drone-runner-docker:1
container_name: runner
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_PROTO=https
- DRONE_RPC_HOST=drone.example.com
- DRONE_RPC_SECRET=xxxx
- DRONE_RUNNER_CAPACITY=2
- DRONE_RUNNER_NAME=runner-1
ports:
- "3000:3000"
restart: always
Start a Drone Builder
Start a Drone builder by creating .drone.yml
. You may want to store some secrets in the Drone CI environment. This can be done via the web interface, or via CLI.
The code below is a slightly edited version I created for deploying a simple Django application. It only builds the pipeline for the production
and staging
branches.
kind: pipeline
type: docker
name: default
trigger:
branch:
- staging
- production
event:
- push
- pull_request
steps:
- name: build main docker image
image: plugins/docker
settings:
context: rev
username:
from_secret: dockerhub_username
password:
from_secret: dockerhub_password
repo: ${IMAGE_NAME}-${DRONE_BRANCH}
dockerfile: rev/Dockerfile
cache_from: ${IMAGE_NAME}-${DRONE_BRANCH}
tags:
- latest
when:
changeset:
- rev/Dockerfile
- rev/**/*
- name: build nginx docker image
image: plugins/docker
settings:
context: nginx
username:
from_secret: dockerhub_username
password:
from_secret: dockerhub_password
repo: ${IMAGE_NAME}-${DRONE_BRANCH}
dockerfile: nginx/Dockerfile
cache_from: ${IMAGE_NAME}-nginx-${DRONE_BRANCH}
tags:
- latest
when:
changeset:
- nginx/Dockerfile
- nginx/**/*
- name: run ansible playbook
image: scootekunst/ansible:latest
environment:
SSH_KEY:
from_secret: ssh_key_${DRONE_BRANCH}
ANSIBLE_HOST_KEY_CHECKING: "False"
ANSIBLE_VAULT_PASSWORD:
from_secret: ansible_vault_password_${DRONE_BRANCH}
commands:
- mkdir -p /root/.ssh
- export APP_ENV=${DRONE_BRANCH}
- echo "$${SSH_KEY}" > /root/.ssh/id_ed25519_drone
- chmod 600 /root/.ssh/id_ed25519_drone
- echo "$${ANSIBLE_VAULT_PASSWORD}" > /tmp/vault_password
- cat /tmp/vault_password | base64
- ansible-playbook --inventory playbooks/inventories/${DRONE_BRANCH} --vault-password-file /tmp/vault_password playbooks/playbook.yml --tags "staging,ingress" -vv
---
kind: secret
name: dockerconfig
get:
path: docker
name: config
Off to the races!
With these steps, every time I push to the staging
or production
branch, Drone will build two docker images and run my Ansible playbook, deploying my site to a production or staging environment.