Background
Containers enable running of multiple applications instanaces on a single host machine. As part of software development process, sometimes we need
to configure containerized solution and create iterative improvements. In this
process, a docker image is started with base image, and phased wise improvement
is made by adding all the needed component to the image before finalizing the
package. In that case we start with bare metal image and continuously add
additional layer in the process and pass the intermediate product to as
different version. In this process, we must create image from a container after
modifying the image and then pass the image along for further modification. In
this article, we are going to look at the process of creating image, running into
container and modifying image and finally distributing the image using public repository
and file-based approach.
In addition to a will to go through this technical jargon,
the following is needed to do hands on.
- A Docker Hub account – A free registry
account can be created at https://hub.docker.com/
- Docker – Docker Desktop available at https://www.docker.com/products/docker-desktop
can be installed.
- Terminal (e.g. PowerShell) - Any terminal can be
used to execute docker command. In the example, we will be using PowerShell. In
the PowerShell, $ is used to define a variable and # is used to start a comment
The command and example in these articles were made on
Windows 10 machine with Windows PowerShell and Docker version 20.10.2.
Login to Docker Hub
Once the docker starts, we can login to the docker using
DockerId. DockerID can be used as username while authenticating to a Docker
registry for docker images. Docker client can be asked to connect to the
registry to download or upload images.
Docker Hub is one of the registries. An account can be created at https://hub.docker.com/
Let’s start a PowerShell or Terminal window and log on
using:
docker login --username benktesh #password will be entered in the prompt
When the above command is run and user is authenticated, a
successful login prompt show.
Create Docker Image
Docker image can be created from a
publicly available image.
Here we are going to get an Ubuntu 18.04 image from Docker Hub with a
pull command. Note that images can be created from the scratch. Please Creating Docker Image for an illusration about creating image.
Docker pull ubuntu:18.04
After the image has been pulled, we can verify that the
image exists by executing image ls (note it is letter 'L' in lower case and 'one' in 'ls') command.
Docker image ls
At the same time, if the docker desktop is installed, the list
of images shows the existence of the image.
Run Image as Container
So far in the process, we have downloaded an image of Ubuntu
18.04 locally. This is similar to a situation we have got a Virtual Machine but
is not running. To run this image, we need to run inside a container. The image
can be run as container with the following command by specifying a name of the container
suchas ‘ubuntu_container’ based on image ‘ubuntu’ with tag ’18.04’ as below:
docker run -d --name ubuntu_container -i -t ubuntu:18.04
--name argument is defining the name of the container and ubuntu:1804
is represent repository:tag information of the image. Argument -d ensures the container runs in
detached mode. The above command can be made reusable by using variables. For example,
in the PowerShell,
We can define a variable for container name and image and
use it.
$container = “ubuntu_container” #defines variable for
container name
$image = “ubuntuL18:04” #defines a variable for image label
After such variables are defined, the command can use the variables
as below:
docker run -d --name $container -i -t $image
The above command returns runs a container and returns the
id of the container. We can check to see what is inside the container. To do
this, we can open a bash shell in the container by executing
docker exec -it $container bash
which opens the bash prompt where we can execute cat /etc/os-release command to find
out the release information on of the Ubuntu image that is running in the container.
The result showed that we are running Version 18.04.5 (Bionic Beaver) of
Ubuntu.
We verified that we Ubuntu 1804 is locally running as a
container. Issuing exit command gets
us out of the bash.
Modify Docker Image
After we have container running, we do ‘stuffs’ on it. For
example, we add more layers and application to the container to make it more
useful. For example, we could add one application and distribute the image with
application to customers or provide it as a value-added image for later use. To
mimic this, we are going to modify this container and create a new image out of
this container and save to docker repository for public distribution.
For illustration, in the current container when we execute lsb_release -a, the response is
that ‘command not found’ that means the container does not provide features for
lsb_release. Next, we will update this
container by adding lsb_release and make it re-usable for all the applications.
As part of the modification, we will update the base
version, and install lsb_release package and mark the modification complete. We
will first update the Ubuntu with apt
update. Then we will install the lsb-core using apt install lsb-core. After the
install completes, we can execute lsb_release
-a to find the latest information about the Ubuntu.
We can now see that lsb_release
-a is available. So essentially, we have made small update to the
base Ubuntu image.
Like we installed lsb-core, applications can be installed in
this image as part of the containerized solutions. To this document, we
consider that we have now updated our image that can be reused by others thus
is ready for distribution.
Create a new image from modified container for distribution
So far, we have got one image which is Ubuntu 18.04 that was
pulled from the docker repository. We created a container and updated the images
and installed lsb-core into that container. We inspect the status of images by executing
docker images and status of
container executing docker ps -a to
see the list of images and containers, respectively. Note that ps stands for
process status.
We can create a new image out of the currently running
container image which includes the updates we have made by executing docker commit $container which
returns sha256 string of the created image id. Executing docker images shows the newly
created image with id of returned sha256. At this time, we have created a new
image based on the updated container image.
We can tag the newly created image by providing an appropriate
name. We will create a variable to store a new name ‘ubuntu_modified:18.04”
$newImage = "ubuntu_modified:18.04"
We will now commit to create a new image named ‘ubuntu_modified:18:04’.
docker commit $container $newImage
The command returns a sha hash to indicate the id of the newly
created image. This image can be seen with docker images command
In the list we can see the newly created image named ‘ubuntu_modified’
with image id matching the sha256 identified and shows the time of creation.
Note the size of the image – for new container – which is larger than the
original image because we had installed new update to the image.
Now that we have created a fresh image out of the modified
container, the older container can be removed. Frist we will stop the container
and then remove it.
docker stop $container
docker rm $container
We will verify that the container indeed is removed by running
docker ps command.
Now that we have deleted old container, we are going to create
a new container named “modified_container” from the modified image. We can now
run another container with newly created image. Let us create a new variable for new container
$newContainer = “modified_container”
Start a new container with
docker run -d --name $newContainer -i -t $newImage
Open a bash shell on the newly created container.
docker exec -it $newContainer
bash
After we execute lsb_release -a
command, note that the command returned the result without having to need for
an update. Executing exit will get us
out of the bash.
As before, lets stop and remove the newly created container as
we do not need it.
docker stop $newContainer
docker rm $newContainer
Distributing Docker Image
Now that we have container image created, we are ready for
distribution of the image. There are two ways to distribute the image. We can
distribute it by pushing the image to public or private repository. For
illustration, we are going to use Docker Hub Repository (https://hub.docker.com/repositories)
to place the image for distribution.
Distribute using repository
First, we will tag the image to add the repository information.
The docker.io is default repository. We will create a variable $repositoryTag
to have a value of benktesh/ubuntu:1804 and tag the latest image and execute
docker push command
$repositoryTag = “benktesh/ubuntu:18.04”
docker tag $newImage $repositoryTag
docker push $repositoryTag
and a simple docker push will the content to the outside
world as it is publicly available in the repository which we can verify by going
to the Docker Hub at navigating to benktesh/ubuntu
(https://hub.docker.com/repository/docker/benktesh/ubuntu).
Now the file is in repository docker pull command can be used to get an image for further use.
Distribute by creating tar file
Another way is to create a tar file from either image or
container. Here We are going to look at option where docker image can be saved as tar file.
docker save -o ubuntu.tar
$repositoryTag
Command above creates a file ubuntu.tar with a tag defined in variable $repositoryTag which can distributed.
The tar file can be loaded into docker to generate image with simple docker command as below:
docker load -i ubuntu.tar
Now the file is loaded, it can be used.
Conclusion
In this post, we illustrated how to create a docker image by
using a base image from public repository, run that image in a container, and update
the container with necessary installs and upgrades and then create an updated
image from the containerized image modification. We also showed how to push the
image to registry for distribution and looked at file-based approach to save
and load images tar file.
Related Articles
Set up development environment for Azure Kubernetes Service (AKS) deployment
Create a Docker image of an application
Deploy an application image to Kubernetes
Store image to the Azure Container Registry (ACR)
Deploy application image to Azure Kubernetes Service (AKS)
Azure Kubernetes Service (AKS) Deployment Recipes