Sections

  1. Introduction
  2. Domain/Server
  3. Prerequisites
    1. Docker/Docker Compose
    2. Gitea Configuration
  4. Installing Drone
    1. Drone Server
    2. Drone Runner
  5. Setting up an example Pipeline
    1. Setting Up a Simple Node.js App
    2. Setting Up Drone Pipeline
  6. Resources

Introduction

Drone.io is a simple to use tool for automating continuous integration/continuous deployment that you can also self-host! For those less familiar with CI/CD, this means that we can automate the building, testing, and deployment process of software development with Drone. Automating testing and deploying lets developers focus more on coding, and less on the hassle of manually monitoring changes and making sure everyone’s changes work nicely with each other. This ultimately helps developers push out software quicker with less bugs.

Personally, I picked drone out of all the solutions I considered because it was simple to set up, scalable in nature, integrates well with Node/Docker, and has an open source version. Here are a few of the pros and cons to Drone:

Pros

  • Integrates well with most cloud git providers. (Easy to setup with the Gitea server we set up last week)
  • Containers-first architecture
  • Simple to set up (Distributed as a Docker image)
  • Scalable
  • Flexible
  • Syntax is easy to understand

Cons

  • Less features due to its simple nature
  • Documentation could be improved

Drone is scalable by nature due to how it works. It mainly has two components: the Drone Server and Drone Runners. The server is in charge of detecting changes in the git repository and then issuing tasks to the Runners to do based on the configuration. Due to this, the actual server does not need a lot of computing power. The Drone runners on the other hand, do all the heavy lifting and benefit from being hosted on powerful computers. The runners wait and listen for tasks issued by the server. If you have a complex build, such as supporting multiple architectures, the server can have multiple runners work on the build at the same time. This make drone very scalable.

Today I’ll go over how to set up a simple Drone pipeline to deploy a Node web app.

Domain and Server

To get Drone up and running, you’ll need to have a domain and server set up. I’ve gone through setting up a server and domain on Digital Ocean before, but I will be going through how to set things up with Linode today because I think they are also work checking out. They have good hardware and amazing customer service, with some additional features that Digital Ocean doesn’t have.

First, you’ll want to register for an account at Linode. It may take some time before getting approved after signing up, it took about 30 minutes for me to be able to log in and set up a server. After your account is ready to go, we can start setting up a server by clicking Create > Linode in the upper right hand corner.

Create a Linode

After being taken to the Linode creation page, select Ubuntu 20.04 LTS for your distribution, a data center of your choosing, and the specifications you want for your server.

Select server settings

Next, a root password must be assigned for the server, and I would recommend adding SSH keys too. If you don’t have any SSH keys to add, you can generate some in the terminal:

ssh-keygen

Upon successful generation, keys can be added to the server by clicking on Add an SSH Key. In the SSH Public Key box, paste the data inside of id_rsa.pub if you went with the default location while generating keys, it will be located inside of ~/.ssh/.

While the server is spinning up, we can set up the domain. Personally, I use NameCheap for domains as I feel like they offer a good price for what they provide, and WHOIS protection is included for free. After acquiring a domain we’ll need to set up the name servers. If you already have a domain you own, you can set up a subdomain like drone.yourdomain.com after setting up the name servers. For the name servers, we’ll want to point them to Linode’s DNS servers on the domain name provider’s website:

DNS settings

Once your DNS settings are set up, we need to go back to Linode to configure out domain. Click on the Domains tab on the left and then select Add a Domain. Fill out the form with your information and select Do not insert default records for me:

Add a domain

Linode will take you to the management page for the domain you just added. We’ll want to add an A/AAAA record that points the domain name to the server we just set up. To do this click on Add an A/AAAA record. For the hostname you’ll want to put in @ for the hostname if you just purchased the domain and want the drone server to be hosted on the root domain, otherwise, if you just want to use a subdomain like drone.yourdomain.com then fill in drone for the hostname. Then put in the IP address of the server you just created and click Save.

Now that we have our domain set up and server created, we can do an initial configuration on the server. First we can ssh into the server:

ssh root@drone.yourdomain.com

Next we’ll want to update and reboot the server:

apt update
apt full-upgrade
reboot

When you hit enter after typing in reboot, the SSH session will get disconnected because the server is restarting. Give it a minute and then reconnect by using the command from above. After you’ve reconnected, we’ll set up the firewall and a new user for normal every day use:

adduser username

Follow the prompts to finish creating the user. This will give us a user to use so that we aren’t logged in as root when doing things. However, to do stuff that requires more permission than a normal user would have, we’ll need to put the user in the group that can use sudo:

usermod -aG username

Now, we’ll have to give remote connections the ability to connect to the server as the user, namely importing the authorized keys from the root user to the user you just created:

rsync --archive --chown=username:username ~/.ssh /home/username

Next we’ll set up the firewall:

ufw allow OpenSSH
ufw allow http
ufw allow https
ufw enable

If everything is set up correctly, we’ll be able to exit the root ssh session and reconnect as the user we just created:

exit
ssh username@drone.yourdomain.com

Prerequisites

There are a few prerequisites needed for getting drone up and running. You need to be hosting your code on one of the supported git services and have Docker installed. I went through how to set up your own Git server with Gitea in my last post. I will be using Gitea in this example, however, Drone does support most of the popular cloud git services out there.

I’ll be installing Docker and Docker Compose as I like to have the compose file handy so that I can turn it into a Linux service that’s more comfortable for me to monitor.

Docker/Docker Compose

Let’s start by installing Docker. To do this we need to first add Docker’s GPG key:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Now we want to verify that the key we have is correct. To do this we run the command below and make sure it is equal to 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88:

sudo apt-key fingerprint 0EBFCD88

Next we’ll add the repository with Docker in it:

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Finally, we can update our repository sources and install Docker:

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

Docker is now installed! Now, let’s install Docker Compose. Docker Compose isn’t strictly needed in this scenario, as it’s meant more for running multi-container Docker applications, however, Docker Composes lets me turn the Drone Docker container in a Linux service which is nice for me since most of my stuff is run as Linux services.

To install the latest version of Docker Compose, we’ll need to figure out what the latest version is. You can do so by going to the official git repo. At the time of writing this post, it is version 1.26.0. Let’s start installing it:

sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

You should replace the 1.26.0 with the version number that is currently the latest. Then you need to grant the binary file permission to run:

sudo chmod +x /usr/local/bin/docker-compose

Gitea Configuration

Drone will need to be added to your Gitea account as an application. To do this go to Settings > Applications. Then fill out the application name with the name you want and fill out the Redirect URI with the domain name you’re using to host drone + /login (i.e. https://drone.yourdomain.com/login).

Drone Config

You will then be taken to the next page where Client Id and Client Secret will be given to you. Remember both of these, particularly the client secret. Personally, I would probably just leave the tab open so we can retrieve the two things when we need it.

Drone Settings

Congratulations, now we are ready to start installing Drone.

Installing Drone

Drone Server <a name=“droneserver>

However, with both ways, we’ll need to generate a secret that will be shared between the Runners and Server:

openssl rand -hex 16

Keep this handy as you’ll need it to set up both the Server and Runners. Next let’s create a folder to put our Docker Compose file and environment file in:

sudo mkdir /etc/drone/
sudo touch /etc/drone/docker-compose.yml
sudo touch /etc/drone/server.env

Open up the Docker Compose file for editing:

sudo vim /etc/drone/docker-compose.yml

Now hit i to start editing and configure the settings:

version: '3'

services:
  drone:
    container_name: drone
    image: drone/drone:1
    ports:
            - 127.0.0.1:3000:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/lib/drone:/data
    restart: always
    env_file:
      - /etc/drone/server.env

Be very particular about the spacing as this is a .yml file. This file tells docker compose that Drone should be accessible at 127.0.0.1:3000 and to pull configuration settings from /etc/drone/server.env. Next we’ll need to populate /etc/drone/server.env, so save and exit the current file by typing :wq, then open up the .env file:

sudo vim /etc/drone/server.env

Hit i to start editing and fill out your server.env file, this is where you’ll need all the things I said to keep handy:

# Service settings
DRONE_SERVER_HOST=drone.yourdomain.com
DRONE_SERVER_PROTO=https

# Gitea Settings
DRONE_GITEA_SERVER=https://git.yourdomain.com
DRONE_GITEA_CLIENT_ID=(ID FROM PREVIOUS STEP)
DRONE_GITEA_CLIENT_SECRET=(SECRET FROM PREVIOUS STEP)
DRONE_RPC_SECRET=(SECRET THAT YOU GENERATED)

# User setings
DRONE_USER_CREATE=username:(GIT USER),machine:false,admin:true
DRONE_USER_FILTER=(GIT USER)

Type :wq to save and exit. Most of these settings should be pretty straightforward except the last two. DRONE_USER_CREATE is used to seed the server with an admin account. You should put your Git username here. DRONE_USER_FILTER limits the users of this server to the accounts here. In this cases we only want one user. The full list of configuration options can be found on the Drone website.

Now we can create the linux server that will run the Docker Compose file we just created:

sudo touch /etc/systemd/system/drone.service
sudo vim /etc/systemd/system/drone.service

Hit i to start editing and paste the following:

[Unit]
Description=Drone server
After=docker.service nginx.service

[Service]
Restart=always
ExecStart=/usr/local/bin/docker-compose -f /etc/drone/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f /etc/drone/docker-compose.yml stop

[Install]
WantedBy=multi-user.target

Now let’s reload the services and start the Drone service:

sudo systemctl daemon-reload
sudo systemctl enable drone.service
sudo systemctl start drone.service

The service should be up and running now, you can check the status:

sudo systemctl status drone.service

We’re almost done setting up the server. All that’s left is installing NGINX and pointing a reverse proxy to the Drone server that is already running. Let’s start by installing and running certbot to get the SSL/TLS certificates:

sudo apt install certbot
sudo certbot certonly --standalone

Fill out the following prompts that come up. Next, let’s install NGINX:

sudo apt install nginx

Next, we can remove the default configuration and configure the server to point to the Drone server:

sudo rm -i /etc/nginx/sites-enabled/default
sudo touch /etc/nginx/sites-enabled/drone.conf

Now we can set up our NGINX server blocks:

server {

  listen 80 default_server;

  server_name _;

  return 301 https://$host$request_uri;

}
server {

  listen 443 ssl http2;

  ssl_protocols TLSv1.2 TLSv1.3;

  ssl_certificate /etc/letsencrypt/live/drone.yourdomain.com/fullchain.pem;

  ssl_certificate_key /etc/letsencrypt/live/drone.yourdomain.com/privkey.pem;

  ssl_trusted_certificate /etc/letsencrypt/live/drone.yourdomain.com/chain.pem;

  ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA HIGH !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

  add_header Strict-Transport-Security "max-age=31536000" always;

  # Add headers to serve security related headers
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";
  add_header X-Robots-Tag none;
  add_header X-Download-Options noopen;
  add_header X-Permitted-Cross-Domain-Policies none;
  add_header Referrer-Policy no-referrer;

  #I found this header is needed on Ubuntu, but not on Arch Linux.
  add_header X-Frame-Options "SAMEORIGIN";

  server_name drone.yourdomain.com;

  location / {
      proxy_pass http://localhost:3000/;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection upgrade;
      proxy_set_header Accept-Encoding gzip;

  }

}

You can now access the drone server through the browser by going to your domain. When you go there, you’ll be asked to log into your Git provider account (in this case your Gitea account). Once you login, you’ll be prompted to authorize the application. Grant access and your Drone Server is now set up!

Drone Runner

With only the Server set up, Drone will only be able to recognize updates to your git repository, however, it won’t be able to execute and tasks because of the lack of Runners. So let us set up a Runner. In most larger use cases, the Server would be on one computer with the Runners all being separate computers. However, in this case we can install the Runner on the same computer as the runner. We can start this process the same way we did with the Server, by creating a folder, the Docker Compose file, and .env file:

sudo mkdir /etc/drone-runner
sudo touch /etc/drone-runner/docker-compose.yml
sudo touch /etc/drone-runner/runner.env

To edit the Docker Compose file, open the file:

sudo vim /etc/drone-runner/docker-compose.yml

Type i to start typing and insert the following:

version: '3'

services:
  drone-runner:
    container_name: drone-runner
    image: drone/drone-runner-docker:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: always
    env_file:
      - /etc/drone-runner/runner.env

To save and exit, type :wq. Now we have to put our configuration settings inside of runner.env:

sudo vim /etc/drone-runner/runner.env

Hit i to start typing and type the following configuration:

DRONE_RPC_PROTO=https
DRONE_RPC_HOST=drone.yourdomain.com
DRONE_RPC_SECRET=(THE SECRET YOU GENERATED EARLIER THAT YOU PUT IN THE SERVER CONFIG)
DRONE_RUNNER_CAPACITY=2
DRONE_RUNNER_NAME=COOL_DRONE_RUNNER_NAME_1

The configuration parameters should be pretty straightforward. If you want to learn more about them or want to see the other configuration parameters possible, check out the Drone website. Now let’s create the Linux service:

sudo touch /etc/systemd/system/drone-runner.service
sudo vim /etc/systemd/system/drone-runner.service

Hit i to start editing and add the following:

[Unit]
Description=Drone runner
After=docker.service nginx.service

[Service]
Restart=always
ExecStart=/usr/local/bin/docker-compose -f /etc/drone-runner/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f /etc/drone-runner/docker-compose.yml stop

[Install]
WantedBy=multi-user.target

Last of all we need to enable and start the service

sudo systemctl daemon-reload
sudo systemctl enable drone-runner.service
sudo systemctl start drone-runner.service

Drone is now all set up and ready to be used! We just have to give it something to do by configuring a pipeline. You can double check that it is up and running by going to the domain that is connected to your Drone server.

Phew, this post is already getting long. Originally, I planned to go over how to configure a pipeline in this post too, but that would make this most wayyyy tooooo long. I will definitely being going over setting up a simple pipeline in my next post though!

Resources

  1. Git repos supported by Drone.io. (https://docs.drone.io/server/overview/)
  2. Installing Docker. (https://docs.docker.com/engine/install/ubuntu/)
  3. Installing Docker Compose. (https://docs.docker.com/compose/install/)
  4. Installing Drone Server. (https://docs.drone.io/server/provider/gitea/)
  5. Drone Server Configuration Parameters. (https://docs.drone.io/server/reference/)
  6. Installing Drone Runner. (https://docs.drone.io/runner/docker/installation/linux/)
  7. Drone Runner Configuration Parameters. (https://docs.drone.io/runner/docker/configuration/reference/)