×
Community Blog Getting to Know Dockerfile Instructions: Part 1

Getting to Know Dockerfile Instructions: Part 1

This set of tutorials focuses on giving you practical experience on using Dockerfile on Alibaba Cloud.

By Alwyn Botha, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

The official Dockerfile reference is excellent as a REFERENCE: https://docs.docker.com/engine/reference/builder/

This set of tutorials focus on giving you practical experience of using Dockerfile, focusing on interactively using the shell a lot .

After you complete this set of 4 tutorials, you can spend time reading the official Dockerfile reference. Everything will make a lot more sense since you now have actual practical experience with most of the Dockerfile instructions.

Suggestion: on day one skip those 15 first screenfuls of heavy syntax rules and regulations.

Start at https://docs.docker.com/engine/reference/builder/#from. Once you have read through it and played around another day, you are ready to read those 15 screens of information.

Enough theory, let's get started.

Prerequisites

You need access to an Alibaba Cloud Elastic Compute Service instance with a recent version of Docker already installed.

I am writing this tutorial using CentOS. You can use Debian / Ubuntu. 99% of this tutorial will work on any Linux distro since it mostly uses Docker commands. You can refer to this tutorial to learn how to install Docker on your Linux server.

You need a very basic understanding of Docker, images, containers and using docker run and docker ps -a ... basic knowledge.

The purpose of this tutorial is to get you to build several very simple Dockerfiles. Your understanding of Docker concepts will grow as you use it.

Preparation

It will really help if you have only a few ( preferably no ) containers running. That way you can easily find your tutorial container in docker ps -a output lists.

So stop and prune all the containers you do not need running.

You can quickly do that ( in your DEVELOPMENT environment ) using:

docker stop $(docker ps -a -q) #stop ALL containers

To now remove all containers, run

docker rm -f $(docker ps -a -q) # remove ALL containers

Dockerfile FROM Instruction

This initial part of the tutorial is not very exciting. It only downloads an Alpine version 3.8 image and lets us build our own container using it as base. That is all.

The Dockerfile FROM instruction specifies the base image / Linux distro we want to use to build a container.

Every Dockerfile must have a FROM instruction.

For all our Dockerfile tutorials the tiny Alpine base image is good enough.

https://hub.docker.com/_/alpine/

I prefer to pull / download images before I need them. ( If you refer to an image in a Dockerfile it will be downloaded automatically - you will see this below. )

Run:

docker pull alpine:3.8

Expected output

3.8: Pulling from library/alpine
4fe2ade4980c: Already exists
Digest: sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528
Status: Downloaded newer image for alpine:3.8

Its better to have your Dockerfiles in separate directories. All the files and directories that are in the same directory are used during a docker build. Only include the files and directories that you need to build your image. One directory per image: neat, organized, small, efficient, understandable in its entirely due to all these attributes.

Therefore, enter:

mkdir dockerfile-tutorials
cd dockerfile-tutorials

There are two editors frequently used in Linux distros: vim and nano. I prefer nano so will be using only it.

To get nano installed:

Debian / Ubuntu :

apt install nano

CentOS :

yum install nano

Nano beginners guide:

  1. nano file-to-edit ... this starts the nano editor
  2. cursor keys move around as expected
  3. cut Dockerfile text from alibaba tutorials web pages work as expected
  4. pasting into your Dockerfile work as expected
  5. press F3 to save your work
  6. press F2 to exit the editor.

You are now a nano expert. Those are the only things you need to know to use nano for all 4 of these tutorials.

Use nano to create a Dockerfile like this:

nano Dockerfile

FROM alpine:3.8

run

docker build --file Dockerfile .

Expected output

Sending build context to Docker daemon  84.48kB
Step 1/1 : FROM alpine:3.8
 ---> 196d12cf6ab1
Successfully built 196d12cf6ab1
Successfully tagged tutorial:demo

Notice the Sending build context to Docker daemon 84.48kB . This means that the FULL content of the directory wherein our Dockerfile is in is used as build context. We can selectively copy files and directories into our final build image: you will see this in action in the ADD / COPY parts of this tutorial further below.

The FROM alpine:3.8 reads the Alpine 3.8 image we downloaded and adds it to our build image. We now have the tiny Alpine Linux distro inside our build image: a complete distro to be used as base operating system for our application.

Build context is all the files and directories in the same directory as your Dockerfile. Therefore you need to place your Dockerfiles in separate directories. If you have your Dockerfile in your root directory, THE FULL LINUX DISTRO is part of you build context: not a good idea.

Let's delete our Alpine image to prove that Dockerfile will automate download it when needed.

docker image remove alpine:3.8

Expected output :

Untagged: alpine:3.8
Untagged: alpine@sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528
Deleted: sha256:196d12cf6ab19273823e700516e98eb1910b03b17840f9d5509f03858484d321

rerun

docker build --file Dockerfile .

Noticed the Pulling from library/alpine below. It takes longer to download an image from the Internet.

Sending build context to Docker daemon  2.048kB
Step 1/1 : FROM alpine:3.8
3.8: Pulling from library/alpine
4fe2ade4980c: Already exists
Digest: sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528
Status: Downloaded newer image for alpine:3.8
 ---> 196d12cf6ab1
Successfully built 196d12cf6ab1

If you refer to an image in a Dockerfile it will be downloaded automatically. We removed alpine:3.8 image so when we referred to it in a Dockerfile it got downloaded automatically.

Suggestion: I prefer to pull images onto my local machine. Then I can work all day without needing a live Internet connection.

Summary:

  1. Nano the editor
  2. Build context and the need for separate directories
  3. Use docker pull to download images
  4. Use FROM alpine:3.8 to use the Alpine version 3.8 image
  5. Use docker build --file Dockerfile . to build our own image to use as basis.

ADD and COPY

For our container to provide a useful functionality it has to contain some software.

During this step we are going to learn how to use ADD and COPY to add files and dirs to our container.

First we need to create some files and tarred / zip files.

Run:

touch in-tar-file-1
touch in-tar-file-2
touch in-tar-file-3

tar -cvf tarredfiles.tar in-tar-*

The touch command creates empty files.

The tar command adds all files starting with in-tar- into tarredfiles.tar .

  1. The c flag creates a new .tar archive file.
  2. The v flag verbosely show the .tar file progress on file at a time
  3. The f flag names the output file of this archive operation.

We need to create 3 more files and add them to another tar ( zip archive ): more-tarredfiles.tar

touch more-in-tar-file-1 ; touch more-in-tar-file-2 ; touch more-in-tar-file-3 ;
tar -cvf more-tarredfiles.tar more-in-tar-*

We now have 2 tarred archive: tarredfiles.tar and more-tarredfiles.tar

Add this to Dockerfile using

nano Dockerfile

FROM alpine:3.8
ADD tarredfiles.tar /root
COPY more-tarredfiles.tar /root

Build the image using

docker build --tag tutorial:demo --file Dockerfile  .

Expected output

Sending build context to Docker daemon  84.48kB
Step 1/3 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/3 : ADD tarredfiles.tar /root
 ---> f2ac949250b2
Step 3/3 : COPY more-tarredfiles.tar /root
 ---> 972dbad70e51
Successfully built 972dbad70e51
Successfully tagged tutorial:demo

This tutorial contains many very tiny Dockerfile examples that you run.

Every next run MUST stop and prune the previous container that is still running. If you do not, then you will get this error message :

docker: Error response from daemon: Conflict. The container name "/tutorial" is already in use by container "d2f5d27b1edc16e33bb8378b555fd61fd1e3ca244b5e8c1b682e79b76e39c660". You have to remove (or rename) that container to be able to reuse that name.

Therefore

docker stop -t 0 tutorial   ;   docker container prune -f   ;   docker ps -a

So you will see these command frequently: they stop the previous container and deletes it / prune it. docker ps -a then runs to show that no containers are running.

Let's start up a container to see the result.

docker stop -t 0 tutorial ;   docker container prune -f  
docker run -ti -d --name tutorial tutorial:demo /bin/sh -c 'while true; do sleep 60; done'
docker exec -it tutorial /bin/sh

Enter ls /root/ at the /# shell prompt to list the files in the current directory.

Expected output

/ # ls /root
in-tar-file-1         in-tar-file-2         in-tar-file-3         more-tarredfiles.tar
/ # exit

As expected. ADD automate untars tar files.

As expected. COPY does not automate untars tar files.

Easy to remember: ADD does Double Duty ... it adds and untars / unzips.

COPY just copies like copy and paste - no extra processing done. A photo copier just copies - no double duty.

Best Practice: only use ADD when you need this double duty functionality.

The ADD tarredfiles.tar /root in the Dockerfile automate untars tarredfiles.tar into /root/.

The COPY more-tarredfiles.tar /root in the Dockerfile merely copies more-tarredfiles.tar into /root/.

You use the exact same syntax to copy individual files into your container.

You can get considerable more information on ADD and COPY at https://docs.docker.com/engine/reference/builder/#add

At least now you have practical experience using those 2 commands. Reading those official Docker reference docs will make more sense now.

Using chown to Change File Ownership

This section shows how you use chown to change file ownerships with ADD or COPY.

Let's create 2 files using:

touch gamefile
touch anothergame

Add this to Dockerfile using

nano Dockerfile

FROM alpine:3.8
COPY --chown=games:games gamefile /root/
COPY --chown=35:35 anothergame /root/

Build the image using

docker build --tag tutorial:demo --file Dockerfile  .

Let's start up a container to see the result.

docker stop -t 0 tutorial ;   docker container prune -f  
docker run -ti -d --name tutorial tutorial:demo /bin/sh -c 'while true; do sleep 60; done'
docker exec -it tutorial /bin/sh

Expected output

/ # ls -l /root
total 0
-rw-r--r--    1 games    games            0 Oct 16 14:26 anothergame
-rw-r--r--    1 games    games            0 Oct 16 14:26 gamefile
/ # exit

What happened : extract from the Dockerfile:

COPY --chown=games:games gamefile /root
COPY --chown=35:35 anothergame /root

Notice the first copy command uses a username and a groupname to change ownerships.

Notice the second copy uses a UID and a GID to change ownerships. 35 is the UID for the games user id.

GID = group identifier (GID)

UID = user identifier (GID)

https://en.wikipedia.org/wiki/User_identifier

If you peek at your password file you will find long text user IDs and those UIDs. We humans use easy text user names, but in the kernel uses only short, efficient numbers: UIDs.

Entering just those 2 commands in a Dockerfile and creating a running container from the image allowed you to learn more about --chgown compared to reading 5 paragraphs of theory.

I used COPY to copy one file on each line, not ADD.

Recursive Copy of dirs Using Dockerfile COPY

Purpose: demo recursive copy of dirs using Dockerfile COPY

First we need to install a tiny but useful tool to display nested directories.

The tree command is a useful tool to neatly show nested directories as a tree structure.

I am using CentOS so I need to run:

yum install tree

( On Ubuntu / Debian you need: apt install tree )

mkdir -p demo-directories/dir-a/dir-a1/dir-a2

The mkdir creates directories. The -p automatically creates parent directories as required.

The beginner way to create those directories would have been:

mkdir demo-directories
cd demo-directories
mkdir dir-a
cd dir-a
mkdir dir-a1
cd dir-a1
mkdir dir-a2

You could use man mkdir at your shell to read about all the features of mkdir.

Let's see what that nested directories looks like

Run:

tree demo-directories

Expected output

demo-directories/
+-- dir-a
    +-- dir-a1
        +-- dir-a2
3 directories, 0 files

Now run ls -R demo-directories at your shell. See the difference. Tree is more understandable.

Let's add some files into each of those dirs.

touch demo-directories/demo-file
touch demo-directories/dir-a/file-a
touch demo-directories/dir-a/dir-a1/file-a1
touch demo-directories/dir-a/dir-a1/dir-a2/file-a2

It would have been cool if we could ONLY have used these touch commands if touch could automatically create those nested directories. Unfortunately it does not. So we need to create directories first, then touch to create files in the directories.

Run:

tree demo-directories

Expected output

demo-directories
+-- demo-file
+-- dir-a
    +-- dir-a1
    ��� +-- dir-a2
    ��� ��� +-- file-a2
    ��� +-- file-a1
    +-- file-a
3 directories, 4 files

We now have one file in each of those dirs.

Edit the Dockerfile to look like this:

FROM alpine:3.8

WORKDIR demo-work-dir
COPY demo-directories/ .

The . at the end of the COPY command means current dir. Since we used WORKDIR demo-work-dir the current dir is demo-work-dir

Therefore the COPY will copy all files from demo-directories into demo-work-dir.

Run:

docker build --tag tutorial:demo --file Dockerfile .

Expected output

Sending build context to Docker daemon  84.48kB
Step 1/3 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/3 : WORKDIR demo-work-dir
 ---> Using cache
 ---> c9996f2cdedc
Step 3/3 : COPY demo-directories/ .
 ---> Using cache
 ---> 350a93964377
Successfully built 350a93964377
Successfully tagged tutorial:demo

Build complete, run it:

docker stop -t 0 tutorial ; docker container prune -f;docker ps -a

docker run -ti -d --name tutorial tutorial:demo /bin/sh -c 'while true; do sleep 60; done'

Docker will show container id of the running container.

Let's enter the container to see the effect of the build. Enter ls -R at the # shell prompt.

docker exec -it tutorial /bin/sh

Expected output

/demo-work-dir # ls -R
.:
demo-file  dir-a
./dir-a:
dir-a1  file-a
./dir-a/dir-a1:
dir-a2   file-a1
./dir-a/dir-a1/dir-a2:
file-a2

Notice we are working in the demo-work-dir. This dir got created by WORKDIR demo-work-dir in the Dockerfile.

The COPY demo-directories/ . command in the Dockerfile copied the full recursive content of demo-directories/ into demo-work-dir .

Important: notice the demo-directories dir itself does not get copied, only its contents.

If you wanted the demo-directories dir itself created in the container, you would specify WORKDIR demo-directories in the Dockerfile. WORKDIR creates dirs if it does not exist.

To truly test you understand the above, rename both

demo-directories

and

demo-work-dir

Then change the Dockerfile to work with your new dir names. Then rebuild the image, run it, enter the container and look for content in your new dir.

If that works, good. Now make a deliberate mistake with one of those dir names. Rerun all the above. Read the error messages you get and fix it. NOW you understand WORKDIR and COPY better than any theory can teach you.

This concludes Part 1 of 4: Get to know all the Dockerfile Instructions. Continue reading Part 2 to learn more.

0 0 0
Share on

Alibaba Clouder

2,599 posts | 758 followers

You may also like

Comments