We all know and love Docker, a platform to create, manage and distribute application containers across multiple machines. Docker Inc. provides a service to host open source containers to be downloaded (or pulled) like a git repository known as the “Docker Registry.” Think of it like a GitHub for Docker containers.
But what if you want to host your own registry separated from the public one? Well, Docker Inc. has open sourced their Registry application on GitHub.
This tutorial will take you though the process of setting up a private Docker registry using CoreOS on a new VPS.
CoreOS + Docker
We won’t be spending a ton of time going over exactly what Docker and CoreOS can do, as it’s outside the scope of this tutorial. In essence, CoreOS is designed for massive server clusters, it’s small, fast and gets regular security updates automatically. Its root file system is also read-only, meaning you must use Docker to run any kind of software that is not included with the base install.
This makes Core OS a perfect host system for Docker!
Pulling and running the latest registry
Docker Inc. has provided the Registry as a top level image, this means that we can pull it down with a simple:
docker pull registry
This can take a few minutes depending on the connection speed.
A plus of being a top level image also means it gets regular support and updates.
Now let’s test out the registry. We can create a new container using the registry image:
docker run -p 5000:5000 -d --name=basic_registry registry
For those who haven’t used Docker too much, the -p
flag stands for PORT
, meaning we are exposing port 5000 on from the container onto host port 5000.
The -d
flag stands for daemon
, this will cause the container to run in the background and not print output to the current SSH session, we also want to name this basic test container using the --name
option so that we can easily manage it later.
Make sure that your basic registry container is running using docker ps
. The output should look similar to:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
edfb54e4a8c4 registry:0.8.1 "/bin/sh -c 'exec do 7 seconds ago Up 6 seconds 0.0.0.0:5000->5000/tcp basic_registry
Also, visit http://YOUR_IP:5000
in your web browser, and you should get a message like the following:
"docker-registry server (dev) (v0.8.1)"
Notice the word
dev
in the parenthesis. This means that the server is currently running the dev config. We’ll look over more configuration soon.
You now have your own (very basic) container registry running! But we’re not done yet.
Maybe you want to keep this private from prying eyes, or maybe store your images on Amazon S3 instead of local storage. Let’s go over the various configuration options in the next section.
Before we move on, let’s kill the test container so that we don’t run into conflicting ports.
docker kill basic_registry
Registry configuration
There are two ways that we can pass configuration to the Docker Registry. One way is by passing environment variables to a new container, and the other is to add a configuration file.
Here’s a few of the common configuration options we’ll be using:
loglevel
– The minimum amount of information to log to the console. Default isinfo
.standalone
– Should this Registry act on its own? (Never queries the public registry.) Default istrue
.index_endpoint
– If not standalone, what other index will we query? Defaultindex.docker.io
.cache
andcache_lru
– Options pertaining to using a Redis cache for small files, we’ll touch on this later.storage
– What storage backend should we use for this server? (In this tutorial we’ll be using local).storage_path
– If using local storage, what directory should we use to keep files in?
Before we get working with the configuration, we need a base file to work with. The file from the Docker Registry repository on GitHub will work just fine:
wget https://raw.githubusercontent.com/docker/docker-registry/0.8/config/config_sample.yml
The file should save successfully with an output such as:
2014-09-14 14:09:01 (156 MB/s) - 'config_sample.yml' saved [5384/5384]
Great! Now we can modify this file to fit our needs.
The only text editor that comes with Core OS is vim
, but don’t worry if you’ve never used it before, this will explain step by step what to edit and how to do it.
vim config_sample.yml
Once you have the file open, hit the I
and the bottom right corner should display: -- INSERT --
for insert mode. Scroll all the way to the bottom of the file using your arrow keys, you should see a section labeled prod
.
We’ll be changing the two lines, the changes are below
prod:
<<: *local
storage_path: _env:STORAGE_PATH:/data
What we’ve done is change the prod
configuration to derive from the local
section instead of the s3
section. Then we overwrote the storage_path
to use the path /data
inside the new container.
Once you’ve confirmed that all the changes are correct, hit ESC
to exit insert mode and type :wq
(this means write the changes to the file, and quit vim.)
Now let’s rename the file to just config.yml
mv config_sample.yml config.yml
Redis caching (optional)
If you’d like to use redis to speed up your container registry, it’s as simple as pulling a new container from the public registry and adding a few more lines of configuration.
First, pull the top level image of Redis:
docker pull redis
Once the image is pulled successfully, we can run it and name it just like we did with the test registry:
docker run -d --name registry-redis redis
Because redis is in memory, we don’t need to do any configuration for it, as we will link it to the registry container in later steps.
Once again, ensure it’s running by using docker ps
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
54f65641eccf redis:2.8 "redis-server" 2 seconds ago Up 1 seconds 6379/tcp registry-redis
Now re-open the config.yml
in vim
and enter insert mode just like the first time we edited it.
Add the following lines below the prod
section, being sure to indent properly. This time we’re only adding cache
and cache_lru
.
cache:
host: _env:REDIS_PORT_6379_TCP_ADDR
port: _env:REDIS_PORT_6379_TCP_PORT
db: 0
cache_lru:
host: _env:REDIS_PORT_6379_TCP_ADDR
port: _env:REDIS_PORT_6379_TCP_PORT
db: 1
The environment variables REDIS_PORT_6379_TCP_ADDR
and REDIS_PORT_6379_TCP_PORT
are passed to the registry container upon linkage with the Redis container.
With that, you now have setup a Redis container that will work hand-in-hand with your registry container. Now onto building the registry!
Building the container
We have all the configuration set and ready, now we need to build the actual registry container.
Fire up vim Dockerfile
to create a new Dockerfile. Enter insert mode and follow the edits below.
FROM registry:latest
# Add the custom configuration file we made
ADD ./config.yml /docker-registry/config/config.yml
# Create the data directory
RUN mkdir /data
# Set the configuration file to config.yml
env DOCKER_REGISTRY_CONFIG /docker-registry/config/config.yml
# Make sure we use the prod configuration settings
env SETTINGS_FLAVOR prod
What we’ve done above is essentially extend the registry image so it will use our configuration file and settings. A Dockerfile is a set of build instructions for Docker to read and build. If you’d like to learn more on Dockerfiles and their syntax, take a look at the official Docker site documentation.
Next we need to build the container for use.
docker build -t my_registry .
Sending build context to Docker daemon 13.82 kB
Sending build context to Docker daemon
Step 0 : FROM registry
---> e42d15ec8417
Step 1 : ADD ./config.yml /docker-registry/config/config.yml
---> 4339f026d459
Removing intermediate container 2d5138fbcd34
Step 2 : RUN mkdir /data
---> Running in a090f0bdbfd1
---> 8eb27ba6e12a
Removing intermediate container a090f0bdbfd1
Step 3 : env DOCKER_REGISTRY_CONFIG /docker-registry/config/config.yml
---> Running in 565b5bfb2b22
---> 914462e46dc0
Removing intermediate container 565b5bfb2b22
Step 4 : env SETTINGS_FLAVOR prod
---> Running in 31a92847b851
---> b5949575c374
Removing intermediate container 31a92847b851
Successfully built b5949575c374
Now we are ready to run!
Let’s make a directory on our host system to mount in the container as the /data
volume.
mkdir registry-data
Now we can spin up a new container. If you plan on using the Redis cache, use the 2nd command below.
# For a non-Redis cache registry
docker run -d -p 5000:5000 -v /home/core/registry-data:/data --name=private_reg my_registry
# For a Redis cached registry (Must have followed Redis Caching section above)
docker run -d -p 5000:5000 -v /home/core/registry-data:/data --name=private_reg --link registry-redis:redis my_registry
To ensure that your server is running correctly, visit http://YOUR_IP:5000
. You will see the following message:
"docker-registry server (prod) (v0.8.1)"
Notice the (prod)
meaning our configuration changes were successful!
Configure your local Docker client
Now that we have our own running registry, we want the Docker client on our local machines to start using it. Usually you would use the command: docker login
, but for our use, we need to add one more argument to the login command:
docker login YOUR_IP:5000
Enter a username and password (think of this like making a new account) and ignore the message that states you must activate it.
Next, let’s pull a stock image, and push it up to our own repository.
# Pull the busybox image from the public registry
docker pull busybox
# Tag it with our IP/URL
docker tag busybox YOUR_IP:5000/busybox
# Push it to our newly made registry
docker push YOUR_IP:5000/busybox
If everything pushes correctly the final message should be along the lines of:
Pushing tag for rev [a9eb17255234] on
Congratulations! You have setup your very own docker repository.
What’s next?
Here’s some ideas on how to improve your new private registry:
- Reverse proxy using Nginx or Apache to place additional security in front of it, like simple HTTP auth.
- Get a domain for your server and setup it up so you may access your registry with something like: registry.mysite.com
- Purchase (or self-sign) an SSL certificate to add even more protection if your containers contain sensitive information.