Deploy a Docker Registry to DigitalOcean

  • Post author:
  • Post category:Docker

Introduction

This blog covers how to stand up a public docker registry on Digital Ocean using docker-machine. Steps and examples are given below. Everything can also be cloned from github.com/justsomedevnotes/docker-machine-digital-ocean-registry.

Prerequisites

  • Linux box with docker, docker-compose, and docker-machine (Vagrantfile included in the github.com/justsomedevnotes/docker-machine-digital-ocean-registry)
  • DigitalOcean account

Step 1: Set the Environment

Linux Box

The steps in this blog require a linux box with docker, docker-compose, and docker-machine. The github repository has a Vagrantfile that will stand up a box with everything you need.

$ git clone https://github.com/justsomedevnotes/docker-machine-digital-ocean-registry.git
$ cd docker-machine-digital-ocean-registry
$ vagrant up
$ vagrant ssh
$ cd /vagrant
$ ll
drwxrwxrwx  1 vagrant vagrant  4096 Apr 28 13:23 ./
drwxr-xr-x 24 root    root     4096 Apr 28 13:08 ../
-rwxrwxrwx  1 vagrant vagrant   491 Apr 28 13:19 ca.conf*
-rwxrwxrwx  1 vagrant vagrant  1535 Apr 28 13:19 create_ssl.sh*
-rwxrwxrwx  1 vagrant vagrant   501 Apr 28 13:19 docker-compose.yml*
drwxrwxrwx  1 vagrant vagrant  4096 Apr 28 13:19 .git/
-rwxrwxrwx  1 vagrant vagrant    63 Apr 28 13:19 .gitignore*
-rwxrwxrwx  1 vagrant vagrant  7878 Apr 28 13:19 README.md*
-rwxrwxrwx  1 vagrant vagrant   405 Apr 28 13:19 server.conf*
-rwxrwxrwx  1 vagrant vagrant 47110 Apr 28 13:07 ubuntu-xenial-16.04-cloudimg-console.log*
drwxrwxrwx  1 vagrant vagrant     0 Apr 28 04:50 .vagrant/
-rwxrwxrwx  1 vagrant vagrant  4012 Apr 28 13:06 Vagrantfile*

DigitalOcean Token

Once you have a linux box setup, you need to retrieve your DigitalOcean api token if you don’t have it. Login to your digitalocean account and select your Dashboard. Select API and generate your token. Copy this value and set it to an environment variable.

$ export TOKEN=[token value]  

Step 2: Create a Droplet

There are various drivers you can use with docker-machine to provision a VM. For this blog we are using the digitalocean driver. It will provision a relatively small droplet. Use the create docker-machine command giving it the driver, token, and a name for the instance.

$ docker-machine create --driver digitalocean --digitalocean-access-token $TOKEN registry-01  

After the machine is created run the below command to set the docker-machine environment.

$ eval $(docker-machine env registry-01)

Step 3: Update your /etc/hosts

Since we are not registering our registry instance with a DNS server we need to just update the /etc/hosts file. Get the IP of your droplet instance and add it to /etc/hosts.

$ docker-machine ip [instance name]  

Add a line like the following to the /etc/hosts file replacing the ip with the one you received from above.
107.123.104.184 registry.corp.local

Step 4: Generate Credentials

SSL

In order to have a secure docker registry you need ssl certificates. Since this blog is really over standing up a docker registry on DigitalOcean and not how to create ssl certificates I have provided a script (scripts borrowed and slightly modified from http://serverascode.com/2017/06/05/docker-private-registry-with-ssl.html) that will create ssl certificates. Place the ca.conf, server.conf, and create_ssl.sh below in a working directory. Once you have them execute ./create_ssl.sh. These can also be cloned from the github page. Note: if you want to use a different domain you will need to modify the *.conf files DNS entry.

ca.conf

[req]  
distinguished_name = req_distinguished_name  
req_extensions  = v3_req  
x509_extensions = v3_ca  
prompt = no  
[req_distinguished_name]  
C = US  
ST = SomeState  
L = SomeCity  
O = corp.local  
OU = CA  
CN =registry.corp.local  
[v3_req]  
keyUsage = keyEncipherment, dataEncipherment, keyCertSign  
extendedKeyUsage = serverAuth  
subjectAltName = @alt_names  
[ v3_ca ]  
subjectKeyIdentifier=hash  
authorityKeyIdentifier=keyid:always,issuer  
basicConstraints = CA:true  
[alt_names]  
DNS.1 = registry.corp.local    

server.conf

[req]  
distinguished_name = req_distinguished_name  
x509_extensions = v3_req  
prompt = no  
[req_distinguished_name]  
C = US  
ST = SomeState  
L = SomeCity  
O = corp.local  
OU = Docker  
CN = registry.corp.local  
[v3_req]  
keyUsage = keyEncipherment, dataEncipherment  
extendedKeyUsage = serverAuth  
subjectAltName = @alt_names  
basicConstraints = CA:FALSE  
[alt_names]  
DNS.1 = registry.corp.local    

create_ssl.sh

#!/bin/bash
set -e

# create certs and data directories
rm -rf certs data
mkdir certs data
#generate private rsa key
openssl genrsa -out ca-privkey.pem 2048
# create the certificate from the key
openssl req -config ./ca.conf -new -x509 -key ca-privkey.pem \
     -out cacert.pem -days 365
# generate public keys
openssl req -config ./server.conf -newkey rsa:2048 -days 365 \
     -nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 365 \
      -CA cacert.pem -CAkey ca-privkey.pem \
      -set_serial 01 -out server-cert.pem  \
      -extensions v3_req \
      -extfile server.conf

echo "INFO: print cacert.pem..."
openssl x509 -text -in cacert.pem -noout
echo "INFO: print server-req.pem..."
openssl req -text -in server-req.pem -noout
echo "INFO: print server-cert.pem..."
openssl x509 -text -in server-cert.pem -noout
openssl verify -verbose -CAfile ./cacert.pem server-cert.pem

echo "Info: copying keys to certs"
cp server-cert.pem certs/server.crt
cp server-key.pem certs/server.key

echo "INFO: resetting/updating local CA..."
sudo rm -f /usr/local/share/ca-certificates/cacert.crt
sudo update-ca-certificates --fresh

sudo cp cacert.pem /usr/local/share/ca-certificates/cacert.crt
sudo update-ca-certificates
echo "INFO: restarting docker"
sudo service docker restart

$ ./create_ssl.sh    

Basic Auth

Add a user for basic auth.

$ mkdir auth
$ docker run --entrypoint htpasswd registry:2 -Bbn testuser testpassword > auth/htpasswd    

Step 5: Update files on the Droplet

I use a docker-compose file to launch the registry which has some bind mounts to access the certificates. We need to execute the following commands to copy over the auth, certs, and data directories to the droplet instance.

$ docker-machine scp -r ./auth registry-01:/root/auth  
$ docker-machine scp -r ./certs registry-01:/root/certs  
$ docker-machine scp -r ./data registry-01:/root/data  

Step 6: Start the Registry

Copy the below contents to a docker-compose.yml file in your working directory and then execute the below command. This file is also found in the github repo.

docker-compose.yml

version: '3'
services:
  registry:
    restart: always
    image: registry:2
    ports:
      - 443:443
    environment:
      REGISTRY_HTTP_ADDR: 0.0.0.0:443
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/server.crt
      REGISTRY_HTTP_TLS_KEY: /certs/server.key
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
    volumes:
      - "/root/data:/var/lib/registry"
      - "/root/certs:/certs"
      - "/root/auth:/auth"
$ docker-compose up -d  

Step 7: Test the Registry

If everything is working you should be able to login and push an image to the registry. A simple way to test this is to unset your local docker environment from the registry-01 droplet instance and use the local docker.

$ eval $(docker-machine env -u)  
$ docker login registry.corp.local  
User: testuser  
password: testpassword  

Tag an image and push it to the new registry

$ docker pull busybox:latest  
$ docker tag busybox:latest registry.corp.local/busybox:0.9  
$ docker push registry.corp.local/busybox:0.9  

Delete the local registry copy of the image and pull from the private registry.

docker image rm registry.corp.local/busybox:0.9
docker pull registry.corp.local/busybox:0.9