Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Working with Docker Images and Registries

Tech 2

Let's start with a common container creation command:

docker run -i -t --name custom_centos7 centos:7 /bin/bash

This spins up a new container named custom_centos7 from the CentOS 7 base image and drops you into an interactive Bash shell.

What is a Docker Image?

Docker images are constructed from a stacked layered filesystem:

  1. The bottom layer is the boot filesystem (bootfs), which handles system bootstrapping
  2. The next layer is the root filesystem (rootfs), which is mounted read-only. Docker uses union mount technology to stack multiple read-only filesystems on top of each other

This stacked filesystem structure is what Docker calls an image. Images are fully stackable, and only the topmost layer is writable — all lower layers remain read-only.

List Local Docker Images

Use the docker images command to view all images stored on your local system:

sudo docker images

Example output:

REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    d2c94e258dcb   11 months ago   13.3kB
centos        7         eeb6ee3f44bd   2 years ago     204MB

By default, Docker stores image files locally in the /var/lib/docker directory.

To download a new image from a remote registry, use the pull command:

sudo docker pull centos:8

After pulling, you'll see the new tagged image in your local list:

REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    d2c94e258dcb   11 months ago   13.3kB
centos        7         eeb6ee3f44bd   2 years ago     204MB
centos        8         5d0da3dc9764   2 years ago     231MB

Pulling Images

When you use docker run to start a container, Docker will automatically pull the image from Docker Hub (the default public registry) if it doesn't exist locally. If you don't specify a tag, Docker will default to pulling the latest tag.

You can filter the output of docker images to only show images from a specific repository:

docker images centos

Output:

REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
centos       7         eeb6ee3f44bd   2 years ago   204MB
centos       8         5d0da3dc9764   2 years ago   231MB

To pull a specific tagged version of an image, specify the tag as shown below:

sudo docker pull centos:8

Search for Public Images

Use the docker search command to find public images hosted on Docker Hub:

docker search mysql

Example output:

NAME                            DESCRIPTION                                     STARS     OFFICIAL
mariadb                         MariaDB Server is a high performing open sou…   5716      [OK]
mysql                           MySQL is a widely used, open-source relation…   14989     [OK]
percona                         Percona Server is a fork of the MySQL relati…   627       [OK]
phpmyadmin                      phpMyAdmin - A web interface for MySQL and M…   964       [OK]

The output includes four columns: repository name, image description, star count (user rating), and an indicator for official images.

Building Custom Docker Images

There are two common methods to create and manage your own Docker images:

  • The docker commit command
  • The docker build command paired with a Dockerfile

The Dockerfile method is the current industry standard, but we cover both approaches below.

Authenticate with Docker Hub

First, create an acccount on Docker Hub. You can register directly or use an existing GitHub/Google account, but you will need to set an account password for CLI login.

Use the docker login command to authanticate your local CLI:

sudo docker login

Example successful output:

Log in with your Docker ID or email address to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com/ to create one.
You can log in with your password or a Personal Access Token (PAT). Using a limited-scope PAT grants better security and is required for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/

Username: your_username
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

You can use docker logout to sign out of your account at any time.

Create an Image with docker commit

The docker commit command works similar to committing changes in version control: you modify a running container, then save those changes as a new image layer.

First, start a new base container:

docker run -i -t centos:7 /bin/bash

Inside the container, install Apache HTTP server:

yum install httpd

Exit the container with exit, then run docker ps -a to get the container ID, then commit the changes to a new image:

docker commit <container-id> your_username/httpd-test

Example output:

sha256:7b2dde3fde6ed893914216d5693e305c7bdc47934879a3f0c4da5cc0e5fe27a8

You can verify the new image exists with docker images:

REPOSITORY           TAG       IMAGE ID       CREATED          SIZE
your_username/httpd-test   latest    7b2dde3fde6e   18 minutes ago   885MB

You can add commit metadata and author information with additional flags:

# -m = commit message, -a = author name, final argument sets tagged version
docker commit -m"Added httpd server" -a"Your Name" 2cdeb9fa7543 your_username/httpd:prod

Start a new container from your custom image with:

docker run -t -i your_username/httpd:prod

Create an Image with a Dockerfile

The recommended approach is to use a Dockerfile, a text file that defines all steps to build your image. Start by creating a new working directory and an empty Dockerfile:

mkdir custom-nginx-repo
cd custom-nginx-repo
touch Dockerfile

This directory is your build context, the root environment Docker uses to build your image.

Edit the Dockerfile with the following content:

# Base image to build from
FROM centos:7
# Maintainer information
MAINTAINER Your Name <you@example.com>
# Install Nginx
RUN yum -y install epel-release && \
    yum -y update && \
    yum -y install nginx

# Add custom default landing page
COPY custom-index.html /usr/share/nginx/html/index.html

# Expose container port 80 for incoming traffic
EXPOSE 80

Create the custom-index.html file in your build context with the following content (note the charset declaration to avoid garbled non-ASCII text):

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
Hello from my custom Docker image!
你好,这是我的自定义 Nginx 镜像
</body>
</html>

Optionally, you can add a .dockerignore file to exclude files from being added to the build context:

.git
venv
*.log

Now build the image with the docker build command, using the -t flag to tag the image with your repository name:

docker build -t="your_username/nginx-demo" .

Example build output:

[+] Building 78.0s (6/6) FINISHED                                                                             
 => [internal] load build definition from Dockerfile                                                                    0.0s
 => => transferring dockerfile: 288B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/centos:7                                                             0.0s
 => [internal] load .dockerignore                                                                                       0.0s
 => => transferring context: 2B                                                                                         0.0s
 => CACHED [1/2] FROM docker.io/library/centos:7                                                                        0.0s
 => [2/2] RUN yum -y install epel-release &&     yum -y update &&     yum -y install nginx                             73.5s
 => exporting to image                                                                                                  4.3s 
 => => exporting layers                                                                                                 4.2s 
 => => writing image sha256:7305f86bc0d28253df007343fc302c375d434602fef502b5ceaf118a98a71ba5                            0.0s 
 => => naming to docker.io/your_username/nginx-demo                                                                 0.0s

Check the built image:

docker images

Output:

REPOSITORY           TAG       IMAGE ID       CREATED              SIZE                                                       
your_username/nginx-demo     latest    7305f86bc0d2   About a minute ago   659MB

Note the dot (.) at the end of the build command — it tells Docker the Dockerfile is in your current working directory. You can also build from a remote git repository:

docker build -t="your_username/nginx-demo" git@github.com:your-username/dockerfile-repo.git

Or specify a custom path to your Dockerfile with the -f flag:

docker build -t="your_username/nginx-demo" -f ~/projects/my-docker/Dockerfile .

Build Cache and Troubleshooting

If a build fails, Docker will show you exactly which step failed. When you re-run a build after fixing errors, Docker will reuse cached layers from all previously successful steps to speed up builds. To force a full rebuild without cache, add the --no-cache flag:

docker build --no-cache -t="your_username/nginx-demo" .

Run a Container from Your Custom Image

You can inspect the layer history of your new image with the docker history command:

docker history 7305f86bc0d2

Output will show you every layer and the command that created it.

Start a new detached container from your image with port mapping:

# -d = run in background, -p = map host port to container port
docker run -d -p 80 --name custom-nginx your_username/nginx-demo nginx -g "daemon off;"

If you don't specify a host port, Docker will randomly assign a high port between 32768 and 61000. You can check the mapped port with either docker ps -l or docker port:

docker port custom-nginx 80

Output:

0.0.0.0:32768
[::]:32768

To bind a specific host port, use one of the following formats:

# Bind all interfaces on host port 80 to container port 80
docker run -d -p 80:80 \
--name custom-nginx your_username/nginx-demo nginx -g "daemon off;"

# Bind only localhost port 80 to container port 80 for local testing
docker run -d -p 127.0.0.1:80:80 --name custom-nginx your_username/nginx-demo nginx -g "daemon off;"

Useful Additional Dockerfile Instructions

Here are other common Dockerfile instructions you will use frequently:

  1. CMD: Specifies the default command to run when a container starts. This will be overridden if you specify a custom command when running docker run
  2. WORKDIR: Set the default working directory for subsequent commands and running containers
  3. ENV: Set environment variables inside the image
  4. USER: Specify the user that should run commands and the container process
  5. COPY: Copy files from your local build context into the image. If the target path does not exist, Docker will create it automatically.

Push Your Image to Docker Hub

Once you've built your image, you can push it to Docker Hub using the docker push command. First, tag your image with your Docker Hub username and repository name:

docker tag your_username/nginx-demo your_username/nginx-demo:1.0.0

Then push the tagged image:

docker push your_username/nginx-demo:1.0.0

If the repository does not exist on Docker Hub, Docker will automatically create it for you.

Delete Local Images

Use docker rmi (remove image) to delete a local image:

docker rmi your_username/httpd:prod

If any existing container is using the image, you will get an error:

Error response from daemon: conflict: unable to remove repository reference "your_username/httpd:prod" (must force) - container 6d00a319a9e8 is using its referenced image 8ce4bf297625

You need to delete the dependent container first with docker rm <container-id> before deleting the image. Docker deletes images one layer at a time, as shown in the output:

Untagged: your_username/httpd:prod
Deleted: sha256:8ce4bf297625ff3693db4e89acb515824f27b391f2e4b3e7e8784beb2fce4cb0
Deleted: sha256:06d5068d22b6af1ad81c235a57579125c1770b4e030c376c7eebd1ba57a57e58

This only deletes the local copy of the image; you need to delete the repository through the Docker Hub web interface if you want to remove it remotely.

To delete all local Docker images at once, run:

docker rmi `docker images -a -q`

Host Your Own Private Docker Registry

All images pushed to public Docker Hub are visible to everyone. If you need a private registry, you have two options:

  • Use Docker Hub's paid private repository feature
  • Host your own private registry on your infrastructure

To spin up your own registry, use the official registry image from Docker:

docker run -d -p 5000:5000 registry

After starting, you can verify it's working by visiting http://<your-server-ip>:5000/v2/, which will return an empty {} response if running correctly.

Use Your Private Registry

First, pull an image to test with:

docker pull nginx:1.24

Tag the image to point to your private registry:

docker tag nginx:1.24 192.168.10.15:5000/test-nginx:1.24

Push the tagged image to your registry:

docker push 192.168.10.15:5000/test-nginx:1.24

Fix HTTP Push Errors

By default, Docker expects all registries to use HTTPS. Self-hosted registries use HTTP by default, so you will get a push error unless you configure Docker to allow insecure HTTP access to your registry.

To fix this, edit (or create if it doesn't exist) the Docker daemon config at /etc/docker/daemon.json and add your registry to the insecure-registries list:

{
  "insecure-registries":[
    "192.168.10.15:5000"
  ]
}

Restart the Docker service to apply the change:

service docker restart

Once Docker restarts, you should be able to push successfully.

Verify and Pull from Your Private Registry

To list all tags for an image in your registry, visit: http://192.168.10.15:5000/v2/test-nginx/tags/list You will get a response like:

{"name":"test-nginx","tags":["1.24"]}

To list all repositories in your registry: http://192.168.10.15:5000/v2/_catalog Response:

{"repositories":["test-nginx"]}

Delete the local copy of the image to test pulling from your private registry:

docker rmi nginx:1.24
docker rmi 192.168.10.15:5000/test-nginx:1.24

Pull the image back from your private registry:

docker pull 192.168.10.15:5000/test-nginx:1.24

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.