Easier Drone Deploys with Docker Images
Custom Docker images for Drone CI can reduce redundancy and simplify setup of new deployments. We'll look at incrementally building custom images for deploys, and how that affects our Drone configuration.
Drone CI configuration files have a straightforward syntax similar to that
of a docker-compose.yml
file: you specify different build stages, pick an image
for each stage, and run commands against that image:
---
pipeline:
build:
image: golang
commands:
- go get
- go build
- go test
Drone also supports secrets, which keep sensitive information like SSH keys and API tokens out of your (possibly public) configuration file. A deploy step might look for credentials in environment variables, store those credentials in a file, and perform commands using those credentials:
---
pipeline:
deploy:
image: alpine
secrets: [ ssh_private_key, ssh_host_key ]
commands:
- mkdir "$${HOME}/.ssh"
- echo -n "$${SSH_PRIVATE_KEY}" > "$${HOME}/.ssh/id_rsa"
- chmod 700 "$${HOME}/.ssh/id_rsa"
- echo "$${SSH_HOST_KEY}" >> "$${HOME}/.ssh/known_hosts"
- scp -r ./output user@deploy.example.com:/var/www/html
This is prone to repetition. For each new repository (at least without the paid global secrets feature), you have to inject secrets which may be the same as other projects in your CI environment, and perform the same setup steps to expose those secrets to shell commands.
Building custom deploy images
Instead of all this repetition, let's build our secrets into images we control. We'll start with a generic image, drone-rsync-ssh:
FROM alpine:3.7
COPY drone-ssh-keys.sh /usr/bin/drone-ssh-keys
RUN apk add --no-cache openssh-client rsync
The mkdir/echo/chmod behavior from our verbose Drone config file is wrapped up
in drone-ssh-keys.sh, for easy calling in the future. Here's how
.drone.yml
changes under this image:
---
pipeline:
deploy:
image: drone-rsync-ssh
secrets: [ ssh_private_key, ssh_host_key ]
commands:
- drone-ssh-keys
- scp -r ./output user@deploy.example.com:/var/www/html
We've already cleaned up this build stage, but there's still redundancy in adding secrets to the Drone repository. Plus, if our secrets change, we have to update every repository that uses them.
We can go a step further if we have access to private Docker images. Let's bundle those credentials right in our image with a new Dockerfile:
FROM alpine:3.7
COPY deploy-assets /deploy-assets
RUN apk add --no-cache openssh-client rsync && \
mkdir /root/.ssh && \
cd /deploy-assets && \
cp deploy.key deploy.pub known_hosts config /root/.ssh && \
chmod 0600 /root/.ssh/deploy.key
The deploy-assets
directory contains everything we need to run deploys,
including secrets and file copying utilities. Our new .drone.yml
is very
compact, and we can call our commands without any additional setup:
---
pipeline:
deploy:
image: drone-rsync-ssh-secrets
commands:
- scp -r ./output deploy:/var/www/html
This image is available in your environment for any pipeline that has similar deploy steps. No more setting up secrets every time you add a repository to Drone.
Security considerations
As always, treat your secrets with care. At minimum, keep these things in mind:
- Don't push your secrets to publicly available image repositories (e.g. public Docker Hub) or Git repositories
- When using Drone secrets, use skip branches to avoid exposing your secrets to untrusted code