Docker basics: Docker Registry

By default when using docker you pull the images from the Dockerhub docker registry. Most probably you have your own docker images for you application and you want to distribute them and do so in a secure way. One way to do so is to go with the already set options such as a paid plan from Dockerhub or the registries provided by cloud providers like amazon, azure etc.

The other option is setting up your own docker registry.
In any case since you use docker you need to have a registry to distribute your images so that they can make it into production.
There are many benefits on managing your own registry but be aware that it requires effort on your side on provisioning and maintaining it.
Therefore we will create our docker registry

docker run -d -p 5000:5000 --restart=always --name registry registry:2

So we have a docker registry running on port 5000 and the registry will always restart.

Now let’s test our registry and push an image.
First I will build a simple image with no specific purpose.

FROM ubuntu
ENTRYPOINT top

It is just a dummy image printing top.

so we are gonna build it

docker build --tag top-ubuntu:1.0 .

The key is to tag your image based on the domain under which your registry runs.
Currently our registry runs on the localhost therefore by tagging we also specify the location of the registry.

docker tag top-ubuntu:1.0 localhost:5000/top-ubuntu:1.0

And no we push our image

docker push localhost:5000/top-ubuntu:1.0

Now let’s remove our images and see if our image will be downloaded from our running registry

docker rmi top-ubuntu:1.0
docker rmi localhost:5000/top-ubuntu:1.0

And let’s pull

docker pull localhost:5000/top-ubuntu:1.0

As you can see our image has been downloaded from our local registry and is ready to be used.

So far so good. The next step is securing our registry with a username and password.

Let’s start by setting the username and password

First let’s create a directory which shall contain our credentials

mkdir auth

The we shall creae

docker run --entrypoint htpasswd registry:2 -Bbn {your-user} {your-password} > auth/password-file

The file shall contain your username and password information. The password shall be hashed.

Now let’s run our secured registry

 docker run -d -p 5000:5000 --restart=always --name registry -v `pwd`/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/password-file registry:2

As you can see we mounted the credentials file to the docker container and we specified the location of the password-file.

Let’s try to push our image

docker push localhost:5000/top-ubuntu:1.0
.
.
.

059ad60bcacf: Preparing 
8db5f072feec: Preparing 
67885e448177: Preparing 
ec75999a0cb1: Preparing 
65bdd50ee76a: Preparing 
no basic auth credentials

It’s time to login to our registry

docker login localhost:5000

Once your have provided your credentials you will be able to push the image to your local repository.

docker push localhost:5000/top-ubuntu:1.0

Be aware that our registry is not secure. Having your registry secured with credentials does not make it secure since you need to have ssl encryption.

On the next tutorial we will secure a docker registry with ssl.

 

Advertisement

Docker basics: Containers

So we have just created a docker image and now it’s time for us to run a container and interact with it.

docker run nginx

This will run an nginx image and as we can see due to running the container in an interactive mode the terminal we issued the command is no longer available.

We will press ctrl-c. By doing so the docker process exists and our container is no longer running. Our container lifecycle is bound to the main process. If the process exits the container stops.

So let’s run our container in the detached mode and interact with it

docker run -d nginx 

The minus -d option makes the container to run in a detached mode.

Now let’s have a look on our running containers

docker ps

Only one container is listed. The ps command by default show only the running containers. The ps command gives us various information such as when the container was created, the status of the container and the image which was created.
If we want to see our previously stopped container all we have to do is to execute ps with the -a option

docker ps -a

We can remove a container either after stopping the container or we can specify the container to get removed automatically once the container is stopped.

docker run --rm -d nginx 

Otherwise in order to remove a container we just have to issue the rm command

docker rm {container-id|container-name}

Be aware that in order to delete a container, the container must be stopped. Otherwise you must use the -f argument which forces the container to stop and gets deleted.

In order to stop the container issue

docker stop {container-id|container-name}

As we can see we can remove a container by specifying it’s name. In the previous examples we did not any names so let’s do it now.

We can set a name by either renaming a running container

docker rename 53552a9f0a95 my-nginx

Or we can specify the name of the container while we create it.

docker run --name='my-nginx' -d nginx 

Now that we have only one container the ps command serves us well, however in cases where we run multiple containers we need to apply the filter argument which comes along with the ps command.

You can filter by the id, name, label, status etc.
Also you can use filter with substrings in case of name.

docker ps --filter=name="nginx"

So everything is set for us. As we have seen running a container in a detached state does not display any logs in the console. To get the logs we can use the logs command.

docker logs my-nginx

If we want to follow the logs we can also follow the output

docker logs -f my-nginx

The next step is to get into interactive mode with our container while it is running

docker run --rm --name='my-nginx' -it nginx /bin/bash

with this command our container is running and we are logged in to the containers bash console. Be aware that in cases where an entrypoint has been specified you need to override it.

docker run -it --entrypoint "/bin/bash" {image}

However still we might need some extra commands to be run. One way to do this is to use
the docker exec command

docker exec my-nginx echo 'hello wolrd'

As you see we specified the container name and the bash command to execute.

So most of the thing to get us covered has been mentioned. The last tip has to do with exposing the port of our docker container.
Our container is accessible through the ip it has been assigned while creating it however there are cases where we want to map the container’s listening port to our host.

To do so we will use the -p command

docker run -d -p 8080:80 --name my-nginx nginx 

In the above example 8080 is the port of our host and 80 is the port of our docker container.

Last but not least you can create an image by a running container by committing it

docker commit e2e222e22e2e dockerimage:version1

So that was a quick reference on the docker commands which you might use on a daily basis.

Docker basics: Images

So what is a docker image? A docker image is read-only layer consisting of binaries libraries and the software which the container shall execute.

A docker image can have as a base another docker image. Actually they are a read only template.
Once created they are stored in your file system.

To view the docker images currently installed on your system you can issue

docker images

Let’s download an image

docker pull ubuntu

By default your images are downloaded through DockerHub. Also you can upload your images to DockerHub too, if you want to share them.
In cases you want to share your images privately then you have to check the private option on image hosting on dockerhub or other providers such as Google, Amazon, Azure etc.

Since we have just downloaded an image, we will list again our images. Now we will go a bit further and issue

docker images -a

What you can see is that some images don’t have any name on them. Those images are called intermediate images. Behind the scenes the image that you use consists of many other images. Actually it is a parent-child relation. An intermediate image might be contained in two different images due to common dependencies and binaries.
So next time you download an image the intermediate files and your image will be downloaded.

Now let’s filter some of our images.
Current filters supported are
dangling, label, before, since, reference.

Most probably the reference is the one you are gonna use the most.

docker images --filter=reference='*ubuntu*'

What you actually do with the reference filter is filtering based on the image reference and the pattern you specified.

Now let’s build an image. To build an image you need to create a Dockerfile. The dockerfile will specify the image which you will inherit from and any extra action you need to do.

Here’s the content of the Dockerfile

FROM ubuntu
ARG username
ENV USERNAME $username
ENTRYPOINT echo $USERNAME

An argument is passed and the default command that will be executed when the container is running would be to echo the USERNAME environment variable which is set based on the argument.

So let’s build the image

docker build --build-arg username=john .

As you can see you have your intermediate images printed and the segment images. Try building it again and the same steps and ids will fill your screen, changes and new intermediates will happen only if you change your Dockerfile.

Next step is to check all our images.

docker images

Seems like our image is different that the others. The others have name and tags whilst ours has the autogenerated image id.

Let’s add one

docker tag d88ef0502ecf ubuntu-hello:1.0

Also we can do the tagging while we build the image

docker build --build-arg username=john --tag ubuntu-hello:1.0 .

As you can see I have put a version on the image. For your application most probably you are going to have more than one images created therefore you can tag your images in order to keep track of the versions.

Now time to clean up.

docker rmi ubuntu-hello:1.0

This one will not be successful if you have already run the image. You can force it’s deletion by using the -f argument but is not graceful so don’t do it. Instead delete the containers created from this image and then delete the images.

Also be aware that when you issue rmi using tags, if your image has been tagged with more multiple tags then your tags get removed from the image until the image remains with one tag. If your image has only one tag left then the image gets removed as well.

Docker basics: An introduction

On this blog docker is mentioned many times, and many tutorials utilize docker. It is always good to have a go on docker basics and provide a fast reference, in our workday the more we get absorbed with certain problems the easier it gets to forget.

So with docker you can achieve containerization. You can achieve a special runtime for your process to get executed isolated without affecting any other resources of your system.

Instead of spawning a new virtual machine for your applications and consume extra resources, which a vm needs to simulate a machine and the operating system, you can use docker.

So here are some of the benefits on using docker.

  • Isolation
  • Portability
  • Safety

Isolation

If your application needs some certain binaries installed on your os, instead of installing them to your os you can have them installed on your docker image. This way you application does not affect your host system installation.

Portability

You application and it’s dependencies are stored in the form of an image. This image can be shipped, distributed and run on any docker installation without the need of taking any extra action such as installing dependencies.

Safety

The software that comes packaged with your image is used by the container only. It is not installed or used by your os. If there is anything untrusted on that It will be run only by your containers process. Your container is isolated, it cannot interact with other parts of your system.

The main parts which we will discuss are the

In the meantime the best way to start is just by running a hello-world application on docker.

 docker run hello-world 

The best thing with the above image is that we have a step by step documentation on the process of running a docker image.

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

Create a wildcard certificate using let’s encrypt manually

Let’s encrypt has become the number one choice on certificates since it is free and although it is short lived, there is an abundance of tools out there making sure that your certificate will be updated on time.
This post will go through creating a wildcard let’s encrypt certificate using the dns challenge.
All you need is a domain name registered and you being able to add a txt dns record.

I assume you have the latest version of certbot installed. If not you can always run certbot through docker.

docker run -it --entrypoint=/bin/sh certbot/certbot

First step is to issue

certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --work-dir ./work-dir/ --config-dir ./config-dir --logs-dir ./logs-dir/ --manual --preferred-challenges dns -d *.{your domain name}

You will have some questions on the command prompt which you should answer.
Once done you will end up with a screen asking you to take action and add a DNS txt record.

Please deploy a DNS TXT record under the name
_acme-challenge.{your domain} with the following value:

Once you put the entry to your dns record you need to wait until your txt record gets propagated

One of the ways to check if it is ready, is to use the nslookup method on another terminal.

nslookup -type=TXT _acme-challenge.{your domain}

This might take a while so feel free to use the watch command

watch -n 1 nslookup -type=TXT _acme-challenge.{your domain}

Once done you can press enter and your let’s encrypt certificates shall be generated and this would be the result on your screen.

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   {path}/fullchain.pem
   Your key file has been saved at:
   {path}privkey.pem
   Your cert will expire on 2018-08-28. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Now that you have your chain certificate and your private key and your are ready to use them to your applications.