What is a Container Registry?
The containers are created from the images. These images need to be stored and used from a scalable and secure repository. Like the other repositories, the container registry also must provide a fast way to pull and push images with correct policies and credentials.
The most popular container registry hosting the images is Docker Hub. The Docker and Kubernetes are usually using the images via this service. There are many public images freely available, but you don’t have full control over the registry, and day by day, you will meet with some restrictions. Moreover, when it comes to using private images, security is a concern, and it may also be expensive.
Therefore, hosting your private registry can be useful in many situations. Private registries offer many different storage and authentication options and can be customized according to your personal needs.
Why Use a Private Container Registry?
Here are some basic reasons for using your personal private registry instead of a public registry like DockerHub.
- You have full control over the storage location of the private registry.
- You can set up policies as you wish
- Wide options to secure and share your images
- Special configuration options for logging, authentication, load balancing, etc.
AWS ec2-instance Security Settings for Container Registry
We will deploy the registry to an AWS ec2-instance for the showcase. It will host the image repository. That’s why it is important to emphasize the security group permissions.
HTTP TCP 80 IP: 0.0.0.0/0 - HTTP TCP 443, IP: 0.0.0.0/0 - SSH TCP 22 IP: 0.0.0.0/0 - Custom TCP 5000 IP: 0.0.0.0/0 - Custom TCP 5000 IP: ::/0 -
Create a Simple Registry
Let’s create a folder to start our journey at the home directory;
mkdir -p docker-hub/data chmod 777 -R docker-hub/data
data folder will store the
We will use
docker-compose structure to create and manage the registry.
To create a simple registry, we can use a basic
registry image from Docker Hub. The
docker-compose.yaml file content is as follows:
version: '3' services: docker-registry: image: registry:2 restart : always container_name: docker-registry ports: - "5000:5000" environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data
The directory structure is as follows:
docker-hub ├── data └── docker-compose.yaml
Now, run the
docker-compose up -d
Pushing an Image to the Registry
The registry is ready to store the images. To do that, we need to create an image that is labeled correctly. The correct convention for labeling is as follows:
For example, we can pull an image from the docker hub, or create an image from scratch and tag it for our private registry.
docker pull alpine docker tag alpine localhost:5000/my-alpine
To push the image, we need to login into our private registry if it requires the credentials.
docker login localhost:5000
Now we can push the image using the
docker push localhost:5000/my-alpine
Pulling an Image from the Registry
The pulling of images from the private registry is very straightforward. Again, to pull the image, we need to login into our private registry if it requires credentials.
docker login localhost:5000
Now we can pull the image using the
docker push localhost:5000/my-alpine
Adding Web Service to the Private Registry
It is a good idea to monitor or see the images via a web browser, as in Docker Hub. To realize this, we can use
konradkleine/docker-registry-frontend image. There is enough configuration information on the related documentation page. The
docker-compose.yaml file is updated to capture this feature.
version: '3' services: docker-registry: image: registry:2 restart : always container_name: docker-registry ports: - "5000:5000" environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data docker-registry-ui: image: konradkleine/docker-registry-frontend:v2 restart : always container_name: docker-registry-ui ports: - 80:80 environment: ENV_DOCKER_REGISTRY_HOST: ip-172-31-82-125.ec2.internal ENV_DOCKER_REGISTRY_PORT: 5000
ENV_DOCKER_REGISTRY_HOST, we could use a service name, but as we intend to use this registry as a service, we need a secure SSL connection and a
reachable ip or
dns name. That is why we used
ip-172–31–82–125.ec2.internal, which is reachable from the AWS network internally. A DNS name can also be used instead of this static IP. You can also set the storage location with the help of an environment variable,
We have added the
httpd images to the registry. The web browser screenshots are presented.
Adding a Certificate to the Registry
For a secure connection and remote client access, you need certificates. We will show to configure a secure connection with SSL certificates. If you already acquired a
.key from your Certification Authority, then you just need the copy them to under
certs directory. Go to
docker-hub and then,
mkdir certs chmod 777 -R certs
Now copy the
.key file to
If you don’t have them, here is a way of creating the Certification Authorization Key. For this purpose, we need the
openssl package. If
openssl not present, install it.
yum -y install openssl
certification keys for docker-registry. Command prompt must be at the same level as
certs folder. Run the command as follows;
openssl req \ -newkey rsa:4096 -nodes -sha256 -keyout ./certs/domain.key \ -x509 -days 3650 -out ./certs/domain.crt
When creating keys,
Common Name (eg, your name or your server’s hostname) part is critical. It must be the
hostname/ip of the
docker-registry host. When AWS considered,
private ip/hostname of
docker-registry host can be used. The private IP of an ec2 instance is like
ip-172–31–82–125.ec2.internal. You can go with defaults (just press
enter) or enter the appropriate values for the rest.
[ec2-user@ip-172-31-82-125 docker-hub]$ openssl req \ > -newkey rsa:4096 -nodes -sha256 -keyout ./certs/domain.key \ > -x509 -days 3650 -out ./certs/domain.crt Generating a 4096 bit RSA private key ...............................................................................++ .......................................................................................................................................................++ writing new private key to './certs/domain.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]: State or Province Name (full name) : Locality Name (eg, city) [Default City]: Organization Name (eg, company) [Default Company Ltd]: Organizational Unit Name (eg, section) : Common Name (eg, your name or your server's hostname) :ip-172-31-82-125.ec2.internal Email Address :
Adding a Certificate to the Registry with the Help of
You can also use a file to set the
openssl configuration settings as an alternative. For this, prepare an
openssl.conf file for your needs. We will use the following settings for the parameters:
[ req ] distinguished_name = req_distinguished_name x509_extensions = req_ext default_md = sha256 prompt = no encrypt_key = no [ req_distinguished_name ] countryName = "US" localityName = "Bristow" organizationName = "Clarusway" organizationalUnitName = "Clarusway" commonName = "ip-172-31-82-125.ec2.internal" emailAddress = "email@example.com" [ req_ext ] subjectAltName = @alt_names [alt_names] DNS = "ip-172-31-82-125.ec2.internal"
Run the command as follows;
openssl req \ -x509 -newkey rsa:4096 -days 3650 -config openssl.conf \ -keyout certs/domain.key -out certs/domain.crt
The command output should look like this.
openssl req \ > -x509 -newkey rsa:4096 -days 3650 -config openssl.conf \ > -keyout certs/domain.key -out certs/domain.crt Generating a 4096 bit RSA private key ...........................................++ ...............++ writing new private key to 'certs/domain.key' -----
The folder structure should be like this.
docker-hub ├── certs │ ├── domain.crt │ └── domain.key ├── data └── docker-compose.yaml
Now, it is time to update the
docker-compose.yaml file to include the certificates.
version: '3' services: docker-registry: image: registry:2 restart : always container_name: docker-registry ports: - "5000:5000" environment: REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt REGISTRY_HTTP_TLS_KEY: /certs/domain.key REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./certs:/certs - ./data:/data docker-registry-ui: image: konradkleine/docker-registry-frontend:v2 container_name: docker-registry-ui ports: - 443:443 environment: ENV_DOCKER_REGISTRY_HOST: ip-172-31-82-125.ec2.internal ENV_DOCKER_REGISTRY_PORT: 5000 ENV_USE_SSL: "yes" ENV_DOCKER_REGISTRY_USE_SSL: 1 volumes: - ./certs/domain.crt:/etc/apache2/server.crt:ro - ./certs/domain.key:/etc/apache2/server.key:ro
This time, for the web browser link, we have to use
https connection. In our case,
https://ec2-35-172-118-174.compute-1.amazonaws.com/ will be used. As we get a certificate for a private IP address (
ip-172–31–82–125.ec2.internal), we can ignore the security warning and go on.
Client Machine Settings to Use the Registry
domain.crt must be copied to the clients. The
path should be
/etc/docker/certs.d/<private-docker-hub-ip or dns-name>:<port-no>/. In our case, copy the file to the clients and then at clients move the file to the correct path with
root privileges (with
[root@docker-client1 ~]# mkdir -p /etc/docker/certs.d/ip-172-31-82-125.ec2.internal:5000/ [root@docker-client1 ~]# cp -rf ./domain.crt /etc/docker/certs.d/ip-172-31-82-125.ec2.internal:5000/
domain.crt can also be copied to
/root/domain.crt /etc/docker/certs.d/ip-172–31–82–125.ec2.internal:5000/ directory at registry (private-docker-hub) node.
After this setting, we can
push images at the clients with a secure connection.
Creating the Authentification File
In the registry, for authentication, we will use an
username and a
password. To create the password, we will need
htpasswd package to be installed.
If this package is not installed, for ubuntu-based Linux OS;
sudo apt install apache2-utils -y
For fedora-centos-based Linux OS;
sudo yum install httpd-tools -y
After verification of the presence of
htpassw package, we can create a folder to go on our journey. Let’s create the folders to hold the password file;
mkdir -p docker-hub/auth
This is the folder to store our
password file. Next, the password for a selected user can be created. Let’s create a
cd docker-hub/auth htpasswd -Bc registry.password clarusway
The last parameter is the name of the user; in this case
clarusway. After executing the command, you will be prompted to enter your password. In this study,
clarusway is selected for both
docker-compose files with Certification Authorization Key and
version: '3' services: docker-registry: image: registry:2 restart : always container_name: docker-registry ports: - "5000:5000" environment: REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt REGISTRY_HTTP_TLS_KEY: /certs/domain.key REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./certs:/certs - ./auth:/auth - ./data:/data docker-registry-ui: image: konradkleine/docker-registry-frontend:v2 restart : always container_name: docker-registry-ui ports: - 443:443 environment: ENV_DOCKER_REGISTRY_HOST: ip-172-31-82-125.ec2.internal ENV_DOCKER_REGISTRY_PORT: 5000 ENV_USE_SSL: "yes" ENV_DOCKER_REGISTRY_USE_SSL: 1 volumes: - ./certs/domain.crt:/etc/apache2/server.crt:ro - ./certs/domain.key:/etc/apache2/server.key:ro
The folder structure should be like this;
docker-hub ├── auth │ └── registry.password ├── certs │ ├── domain.crt │ └── domain.key ├── data └── docker-compose.yaml
Now, run the
docker-hub directory. Let’s check the web interface. When the default page loaded, we see the Welcome Screen. After hitting the
Browse repositories button, a login attempt is observed.
Sample Usage At Clients
The client should log in and use the reachable IP(DNS)&port number of this registry to
push the images.
After authentication setting activation, we have to log in to
push the images at the clients.
docker login ip-172-31-82-125.ec2.internal:5000
docker pull ip-172-31-82-125.ec2.internal:5000/my-alpine
docker tag <image>:tag ip-172-31-82-125.ec2.internal:5000/<new-name>:tag
docker push ip-172-31-82-125.ec2.internal:5000/<new-name>:tag
A sample image pull showcase is as follows:
[ec2-user@ip-172-31-45-163 ~]$ docker login ip-172-31-82-125.ec2.internal:5000 Username: clarusway Password: WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded [ec2-user@ip-172-31-45-163 ~]$ docker pull ip-172-31-82-125.ec2.internal:5000/my-alpine:latest latest: Pulling from my-alpine Digest: sha256:074d3636ebda6dd446d0d00304c4454f468237fdacf08fb0eeac90bdbfa1bac7 Status: Downloaded newer image for ip-172-31-82-125.ec2.internal:5000/my-alpine:latest ip-172-31-82-125.ec2.internal:5000/my-alpine:latest [ec2-user@ip-172-31-45-163 ~]$
I hope this study addresses the issues of hosting and use a private container registry. You can freely and securely use the containers in popular tools like Docker, contained, Mesos Containerizer, CoreOS rkt, and LXC Linux Containers. I have explained how to use private registries with Kubernetes in the “How to use images from a private container registry for Kubernetes: AWS ECR, Hosted Private Container Registry”.