In-depth dive to images
Images are the basic building blocks for containers and other images. When you "containerize" an application you work towards creating the image.
By learning what images are and how to create them you are ready to start utilizing containers in your own projects.
docker cp
Now executing the application is as simple as running docker run hello-docker
. Try it! During the build we see that there are multiple steps with hashes and intermediate containers. The steps here represent the layers so that each step is a new layer to the image.
The layers have multiple functions. We often try to limit the number of layers to save on storage space but layers can work as a cache during build time. If we just edit the last lines of Dockerfile the build command can start from the previous layer and skip straight to the section that has changed. COPY automatically detects changes in the files, so if we change the hello.sh it'll run from step 3/4, skipping 1 and 2. This can be used to create faster build pipelines. We'll talk more about optimization in part 3.
The intermediate containers are containers created from the image in which the command is executed. Then the changed state is stored into an image. We can do similiar task and a new layer manually. Create a new file called additional.txt
and let's copy it inside the container and learn new trick while we're at it!
$ docker run -it hello-docker sh
/usr/src/app #
Now we're inside of the container. We replaced the CMD we defined earlier with sh
and used -i and -t to start the container so that we can interact with it. In the second terminal we will copy the file here.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c06b95e3e85 hello-docker "sh" 4 minutes ago Up 4 minutes zen_rosalind
$ docker cp ./additional.txt zen_rosalind:/usr/src/app/
I created the file with touch right before copying it in. Now it's there and we can confirm that with ls:
/usr/src/app # ls
additional.txt hello.sh
docker diff and commit
Great! Now we've made a change to the container. We can use diff
to check what has changed
$ docker diff zen_rosalind
C /usr
C /usr/src
C /usr/src/app
A /usr/src/app/additional.txt
C /root
A /root/.ash_history
The character in front of the file name indicates the type of the change in the container's filesystem: A = added, D = deleted, C = changed. The additional.txt was created and our ls
created .ash_history. Next we will save the changes as a new layer!
$ docker commit zen_rosalind hello-docker-additional
sha256:2f63baa355ce5976bf89fe6000b92717f25dd91172aed716208e784315bfc4fd
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-docker-additional latest 2f63baa355ce 3 seconds ago 5.57MB
hello-docker latest 444f21cf7bd5 31 minutes ago 5.57MB
Now, if we create another container from this image, we can see that the additional file is there.
$ docker run hello-docker-additional ls
additional.txt
hello.sh
We will actually never use docker commit again. This is because defining the changes to the Dockerfile is much more sustainable method of managing changes. No magic actions or scripts, just a Dockerfile that can be version controlled.
image v2
Let's do just that and create hello-docker with v2 tag that includes additional.txt.
Dockerfile
# Start from the alpine image that is smaller but no fancy tools
FROM alpine:3.13
# Use /usr/src/app as our workdir. The following instructions will be executed in this location.
WORKDIR /usr/src/app
# Copy the hello.sh file from this location to /usr/src/app/ creating /usr/src/app/hello.sh
COPY hello.sh .
# Alternatively, if we skipped chmod earlier, we can add execution permissions during the build.
RUN chmod +x hello.sh
# Execute a command with `/bin/sh -c` prefix.
RUN touch additional.txt
# When running docker run the command will be ./hello.sh
CMD ./hello.sh
Build it with docker build . -t hello-docker:v2
and we are done! Let's compare the output of ls:
$ docker build . -t hello-docker:v2
$ docker run hello-docker-additional ls
additional.txt
hello.sh
$ docker run hello-docker:v2 ls
additional.txt
hello.sh
Now we know that all instructions in a Dockerfile except CMD (and one other that we will learn about soon) are executed during build time. CMD is executed when we call docker run, unless we overwrite it.
Publishing images
Go to https://hub.docker.com/ to create an account. You can configure docker hub to build your images for you, but using push
works as well.
Let's publish the hello-docker image. Log in and navigate to your dashboard and press Create Repository. The namespace can be either your personal account or an organization account. For now, let's stick to personal accounts and write something descriptive such as hello-docker to repository name. We will need to remember it in part 2.
Set visibility to public.
And the last thing we need is to authenticate our push by logging in:
docker login
Next, you will need to rename the image to include your username, and then you can push it:
$ docker tag hello-docker <username>/<repository>
...
$ docker push <username>/<repository>
...
ghcr
https://github.com/matti/tailer/pkgs/container/tailer
docker tag hello-docker ghcr.io/matti/tailer/hello-docker
docker push ghcr.io/matti/tailer/hello-docker
Exercise 1.8: Image for script
curler/
We can improve our previous solutions now that we know how to create and build a Dockerfile.
Create a new file on your local machine with and append the script we used previously into that file
curler/curler.sh
#!/bin/sh
echo "Input website:"
read website
echo "Searching.."
sleep 1
curl "http://$website"
Create a Dockerfile for a new image that starts from ubuntu:22.04 and add instructions to install curl into that image. Then add instructions to copy the script file into that image and finally set it to run on container start using CMD.
After you have filled the Dockerfile, build the image with the tag "curler".
- If you are getting permission denied, use
chmod
to give permission to run the script.
The following should now work:
$ docker run -it curler
Input website:
helsinki.fi
Searching..
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://www.helsinki.fi/">here</a>.</p>
</body></html>
curler/Dockerfile
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y curl
WORKDIR /app
COPY curler.sh .
RUN chmod +x curler.sh
CMD /app/curler.sh
curler/curler.sh
#!/bin/sh
echo "Input website:"
read website
echo "Searching.."
sleep 1
curl http://$website;