Practical documentation on how to create a new base image.
First we'll want to identify what image we are going to use as our base image to modify is.
In this example our previous base image is old-oca-bastion
.
We have been tasked with doing the following:
- install the package
openjdk-7-jre
- Deleting the directory
/opt/wowmistake
that contains the 10 gb filemistake.file
that was mistakenly added toold-oca-bastion
When we are done, we should end up with an image called new-oca-bastion-flat
.
We'll want to start up a copy of the old base image so that we can start modifying it with the goal of adding the openjdk-7-jre
package, and deleting the large and unneeded file from it.
The following command will spawn a new instance of our old base image old-oca-bastion
, and pop us into an interactive bash session from which we can make changes:
# On Container Host
#
# Starting with the -t and -i flags will cause docker to create an
# interactive terminal session and attach us to it.
#
# We use the privileged flag to allow us to install packages that require making
# devices, nodes, etc.
#
# Giving our container a name with the --name flag allows us
# to interact with it later in a simple way.
docker run -t -i --privileged=true --name='canvas-container' old-oca-bastion /bin/bash
Once we're in this image, we can go ahead and install the openjdk-7-jre
package as requested.
# Inside Container
apt-get update
apt-get install -y openjdk-7-jre
Let's pretend that after installing the openjdk-7-jre
package, we have to take care of urgent business and our session with the container is somehow interrupted.
We can get back into the container we were previously modifying like this:
# On Container Host
#
# The -i -a arguments appended to docker start allow us to start an interactive session.
docker start -i -a canvas-container
This will pop us back into an interactive session with the container we were previously modifying. Running which java
should confirm this by showing us that the openjdk-7-jre
package we installed a while ago is still there.
Let's go ahead and remove that /opt/wowmistake
directory that contains the 10gb file mistake.file
now.
# Inside Container
rm -rf /opt/wowmistake
We could just exit
now and docker commit
the canvas-container
as a new base image, but we have to remember that we want to trim this image down as much as possible before creating a base image from it.
Start with these commands that will clear out the trash that apt-get and apt leaves behind when installing new packages:
# Inside Container
apt-get clean
rm -rf /var/lib/apt/lists/*
Let's take some time to poke around the container for large files that we don't need. We don't want to have to go through this whole process again to get rid of a mistake.file
if we can help it.
This will give us a list of the 100 largest files in the container.
# Inside Container
find / -type f -printf '%s %p\n'| sort -nr | head -100
If we see something that is obviously not needed and can be deleted, do so.
Next let's go ahead and clear out every user's home directory.
# Inside Container
rm -rf /home/*
Then clear out /var/log
of all log files.
# Inside Container
find /var/log -type f -delete
Then finally, we'll clear out our /root/.bash_history
and exit.
# Inside Container
history -c && exit
Using the magic of docker export
and docker import
, we take the filesystem of the canvas-container
container, convert it to a tar file, then dynamically import the tar file to a Docker Image that contains the entire filesystem of the canvas-container
squished down to a single layer.
# On Container Host
docker export canvas-container | docker import - new-oca-bastion-flat
Looking at the output of:
# On Container Host
docker images --tree | grep -C 8 new-oca-bastion-flat
We can see that the filesystem only has 1 layer, and is quite small:
├─e8779f4aec7e Virtual Size: 1.90 GB Tags: new-oca-bastion-flat:latest
This method of creating base containers is much better than simply doing the standard docker commit
because it is 1 layer in size.
If for example we were to:
# On Container Host
docker commit canvas-container new-oca-bastion-not-flat
And the image that canvas-container was started from had a large history, the output of:
# On Container Host
docker images --tree | grep -B 20 new-oca-bastion-not-flat
Could end up looking something like this:
└─463b8f0dff7c Virtual Size: 4.498 GB
└─f618caf34ebc Virtual Size: 4.498 GB
└─9e7e859c80e6 Virtual Size: 4.556 GB Tags: wow-image:latest
└─75242272e6dd Virtual Size: 4.556 GB
└─279b4553818c Virtual Size: 4.57 GB
└─1211e6623116 Virtual Size: 4.57 GB
└─1a13d179d39d Virtual Size: 4.57 GB
└─f8b5f68495ea Virtual Size: 5.662 GB
└─05f60a45ab23 Virtual Size: 5.662 GB
└─6442b9180e4b Virtual Size: 5.662 GB
└─f7d9c4627b6e Virtual Size: 5.662 GB
└─b4c6fba9f6e3 Virtual Size: 5.662 GB
└─eaeb85aac1ac Virtual Size: 5.804 GB Tags: some-image:latest
├─6f4417959152 Virtual Size: 5.804 GB
└─e3caad62bdbd Virtual Size: 5.804 GB
├─2a9fd632f5f5 Virtual Size: 6.439 GB
└─39fbe6cb5d2d Virtual Size: 6.439 GB
└─b4f4ebc85ac4 Virtual Size: 6.439 GB Tags: old-image:latest
└─0ae3b1019fde Virtual Size: 16.439 GB Tags: old-oca-bastion:latest
└─7186b8e4b3dc Virtual Size: 6.439 GB Tags: new-oca-bastion-not-flat
As a result of this, every time we want to push out this new-oca-bastion-not-flat
image, Docker has to try and send all of the extra layers, and every time we try to run the new-oca-bastion-not-flat
image, Docker has to assemble all of the layers before starting the container.
That's all there is to it, we can now use our new-oca-bastion-flat image for new builds!
It also might be a good idea to push the new base to the registry...
# On Container Host
docker tag new-oca-bastion-flat 10.6.195.13:5000/new-oca-bastion-flat
docker push 10.6.195.13:5000/new-oca-bastion-flat