×
Community Blog Set up a Private Docker Registry With a UI on Alibaba Cloud

Set up a Private Docker Registry With a UI on Alibaba Cloud

In this article, I will show how to set up a private Docker Registry service with a user interface.

Alibaba Cloud proposes a Container Registry service to store Docker images, but in some contexts, you can need to create your own private Docker Registry. In this article, I will show how to set up a private Docker Registry service with a user interface.

You need to create an ECS instance with a public IP address (in my example: 8.208.90.125):

1

Then, create a security group to authorize the following ports:

  • 5000 for the Docker Registry
  • 8086 for the Docker Registry UI

2

Add two rules for the ports 5000 and 8086:

3

Connect by SSH on the ECS instance:

ssh root@8.208.90.125

The output is:

root@8.208.90.125's password: 
Last failed login: Mon Sep 21 02:53:29 CST 2020 from 86.208.0.102 on ssh:notty
There were 356 failed login attempts since the last successful login.
Last login: Sat Sep 19 01:39:43 2020 from 82.67.244.69

Welcome to Alibaba Cloud Elastic Compute Service !

Install Docker and Docker Compose

On the ECS instance, install Docker and Docker Compose as the root:

For Docker:

yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io
systemctl start docker

For Docker Compose:

curl -L "https://github.com/docker/compose/releases/download/1.27.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Let's verify that Docker and Docker Compose are well installed:

docker --version 
docker-compose --version

Set up Docker Registry

Now, let's install the private Docker Registry. The Docker images will be stored in the /tmp/docker_registry directory. So, let's create this directory:

mkdir /tmp/docker_registry

Let's create a Docker Compose file to run the Docker Registry service on the port 5000:

cat << EOF > docker-compose.yml
version: '3'
services:
    docker-registry:
        image: registry:2
        volumes:
        - "/tmp/docker_registry:/var/lib/registry"
        ports:
        - "5000:5000"
        restart: always
EOF

So, we expose the ports from 5000/tcp to 8888/tcp on the host and we use a volume on /tmp/docker_registry to store the images.

Let's create the service:

docker-compose up ®Cd

Display the logs of the container:

docker-compose logs

The output is:

Attaching to root_docker-registry_1
docker-registry_1  | time="2020-09-20T18:57:22.21325251Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1  | time="2020-09-20T18:57:22.214008926Z" level=info msg="redis not configured" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1  | time="2020-09-20T18:57:22.215304875Z" level=info msg="Starting upload purge in 42m0s" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1  | time="2020-09-20T18:57:22.23182228Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1  | time="2020-09-20T18:57:22.232164343Z" level=info msg="listening on [::]:5000" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1

Set up a UI for Docker Registry

Now, let's add a service for a user interface in the Docker Compose file. For that, we use the image parabuzzle/craneoperator and create the credentials admin / mypassword:

cat << EOF > docker-compose.yml
version: '3'
services:
    docker-registry:
        image: registry:2
        volumes:
        - "/tmp/docker_registry:/var/lib/registry"
        ports:
        - "5000:5000"
        restart: always
    docker-registry-ui:
        image: parabuzzle/craneoperator:latest
        ports:
        - "8086:80"
        environment:
        - REGISTRY_HOST=docker-registry
        - REGISTRY_PORT=5000
        - REGISTRY_PROTOCOL=http
        - SSL_VERIFY=false
        - USERNAME=admin
        - PASSWORD=mypassword
        restart: always
        depends_on:
        - docker-registry
EOF

Launch the stack:

docker-compose up ®Cd

Verify that the services have started by displaying the logs of the containers:

docker-compose logs

The output is:

Attaching to root_docker-registry-ui_1, root_docker-registry_1
docker-registry-ui_1  | 18:58:13 web.1  | started with pid 7
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.553334 #7]  INFO -- : Refreshing Gem list
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.908830 #7]  INFO -- : listening on addr=0.0.0.0:80 fd=9
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.909368 #7]  INFO -- : worker=0 spawning...
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.910919 #7]  INFO -- : worker=1 spawning...
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.912083 #7]  INFO -- : worker=2 spawning...
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.912894 #10]  INFO -- : worker=0 spawned pid=10
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.913891 #7]  INFO -- : master process ready
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.914310 #10]  INFO -- : worker=0 ready
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.915186 #12]  INFO -- : worker=1 spawned pid=12
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.915516 #12]  INFO -- : worker=1 ready
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.916320 #15]  INFO -- : worker=2 spawned pid=15
docker-registry-ui_1  | 18:58:13 web.1  | I, [2020-09-20T18:58:13.916582 #15]  INFO -- : worker=2 ready
docker-registry_1     | time="2020-09-20T18:57:22.21325251Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1     | time="2020-09-20T18:57:22.214008926Z" level=info msg="redis not configured" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1     | time="2020-09-20T18:57:22.215304875Z" level=info msg="Starting upload purge in 42m0s" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1     | time="2020-09-20T18:57:22.23182228Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1 
docker-registry_1     | time="2020-09-20T18:57:22.232164343Z" level=info msg="listening on [::]:5000" go.version=go1.11.2 instance.id=b1068e37-1960-4336-bd53-a4014b15c66e service=registry version=v2.7.1

Push an Image on Docker Registry

Now, let's push an image on our private Docker Registry.

On a desktop, pull the image alpine:

docker pull alpine

The output is:

Using default tag: latest
latest: Pulling from library/alpine
df20fa9351a1: Already exists 
Digest: sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest

Let's tag this image:

docker tag alpine 8.208.90.125:5000/alpine

Then, push it to the Docker Registry:

docker push 8.208.90.125:5000/alpine

We get an error:

The push refers to repository [8.208.90.125:5000/alpine]
Get https://8.208.90.125:5000/v2/: http: server gave HTTP response to HTTPS client

As we configure the Docker Registry on HTTP instead of HTTPS, we have to authorize the connection to the Docker client. On MacOS, open the Docker Desktop and under the Preferences section, in the Docker Engine tab, enter the following configuration:

{ "insecure-registries":["8.208.90.125"] }

4

On Linux, we would put this configuration in the /etc/docker/daemon.json file.

Now, let's try again to push the alpine on the Docker Registry:

docker push 8.208.90.125:5000/alpine

This time, it works because we authorized Docker Engine to use a Docker Registry that is not HTTPS:

The push refers to repository [8.208.90.125:5000/alpine]
50644c29ef5a: Layer already exists 
latest: digest: sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65 size: 528

On a web browser on the desktop, log in on the Docker Registry UI by visiting the webpage http://8.208.90.125:8086/. We have to authenticate with the credentials.

Then, the list of the containers in the Docker Registry is displayed:

5

Now, we can see the alpine image. Everything works!

Now you know how to set up a Docker Registry with an HTML user interface. Docker Registry doesn't have HTTPS. In general, the best practice is to use the SSL termination provided by a Load Balancer (SLB) or by a proxy server like NGINX.

By Bruno Delb

0 0 0
Share on

Bruno Delb

8 posts | 1 followers

You may also like

Comments

Bruno Delb

8 posts | 1 followers

Related Products