Skip to content

Latest commit

 

History

History
227 lines (159 loc) · 7.27 KB

10_build_image.md

File metadata and controls

227 lines (159 loc) · 7.27 KB

Docker Lab: Building Your Own Docker Image

Previously in the lab...

Question: I don't want to go to the Docker instance and install every missing extension manually. Is there a way to solve this problem?

Answer: Yes there is. Create your own Dockerfile which describes the content of a Docker image.

Dockerfile

Docker can build Docker images by reading the instructions on how to build the image from a so called Dockerfile.

The basic docs on how Dockerfiles work can be found at https://docs.docker.com/engine/reference/builder/.

Write Your First Dockerfile

For that we create a new directory and create an empty Dockerfile in there.

mkdir myfirstimage
cd myfirstimage
vim Dockerfile

Add the following content to your Dockerfile:

FROM ubuntu
RUN apt-get update
RUN apt-get install -y figlet

Build the Image

docker build -t myfirstimage .
  • -t indicates the tag to apply to the image
  • . indicates the location of the build context (about which we will talk more later but is basically the directory where our Dockerfile is located)

Please note that the tag can be omitted in most Docker commands and instructions. In that case the tag defaults to latest. Besides being the default tag there's nothing special about latest. Despite its name it does not necessarily identify the latest version of an image. Depending on the build system it can point to the last image pushed, to the last image built from some branch or to some old image. It can even not exist at all. Because of this you must never use the latest tag in production, always use a specific image version.

Also see: https://medium.com/@mccode/the-misunderstood-docker-tag-latest-af3babfd6375

What Happens When We Build the Image

The output of the Docker build looks like this:

docker build -t myfirstimage .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu
 ---> 42118e3df429
Step 2/3 : RUN apt-get update
 ---> 48fb734e0326
Step 3/3 : RUN apt-get install -y figlet
 ---> ccd7cf351f38
Successfully built ccd7cf351f38
Successfully tagged myfirstimage:latest

(The output of the run commands has been omitted).

Sending the Build Context to Docker

Sending build context to Docker daemon 84.48 kB
  • The build context is the . directory given to docker build
  • It is sent (as an archive) by the Docker client to the Docker daemon
  • This allows to use a remote machine to build using local files
  • Be careful (or patient) if that directory is big and your link is slow

Inspecting Step Execution

docker build -t myfirstimage .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu
 ---> 42118e3df429
Step 2/3 : RUN apt-get update
 ---> 48fb734e0326
Step 3/3 : RUN apt-get install -y figlet
 ---> ccd7cf351f38
Successfully built ccd7cf351f38
Successfully tagged myfirstimage:latest
  • A container (42118e3df429) is created from the base image
    • The base image will be pulled, if it was not pulled before
  • The RUN command is executed in this container
  • The container is committed into an image (48fb734e0326)
  • The build container (42118e3df429) is removed
  • The output of this step will be the base image for the next one
  • ...

The Caching System

If you run the same build again, it will be instantaneous. Why?

  • After each build step, Docker takes a snapshot
  • Before executing a step, Docker checks if it has already built the same sequence
  • Docker uses the exact strings defined in your Dockerfile:
    • RUN apt-get install figlet cowsay is different from
    • RUN apt-get install cowsay figlet
    • RUN apt-get update is not re-executed when the mirrors are updated
  • All steps after a modified step are re-executed since the filesystem it's based on may have changed

You can force a rebuild with docker build --no-cache ...

If you only want to trigger a partial rebuild, e.g. run apt-get update to install the latest updates, you can use the following pattern:

ENV REFRESHED_AT 2018-03-13
RUN apt-get update

If you update the value of REFRESHED_AT it will invalidate the Docker build cache of that and all the following steps, thus installing the latest updates.

Run It

docker run -ti myfirstimage
root@00f0766080ed:/# figlet hello
 _          _ _
| |__   ___| | | ___
| '_ \ / _ \ | |/ _ \
| | | |  __/ | | (_) |
|_| |_|\___|_|_|\___/

root@00f0766080ed:/#

The CMD Instruction in Dockerfile

With the CMD instruction in the Dockerfile we have the possibility to define the command that is executed by default if a container is started:

FROM ubuntu
RUN apt-get update
RUN apt-get install -y figlet

CMD ["figlet", "hello"]

After building the image with docker build -t myfirstimagecmd ., we simply run it:

docker run -ti myfirstimagecmd

It directly executes the defined command and prints out

 _          _ _
| |__   ___| | | ___
| '_ \ / _ \ | |/ _ \
| | | |  __/ | | (_) |
|_| |_|\___|_|_|\___/

Checkout https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact for more information.

Frontend App Image Build

We now want to include the source code of our frontend app into a built docker image, so we use the Dockerfile within this lab.

The base image is our php:7-apache image which we used before. The ADD command allows us to add files from our current directory into the Docker image. We use this command to add the application source code into the image.

Hint: Use .dockerignore to exclude files from the Docker context being added to the container. It works the same as .gitignore: https://docs.docker.com/engine/reference/builder/#dockerignore-file

FROM php:7-apache

# Copies the php source code to the correct location
ADD ./php-app/ /var/www/html/

# Install additional php extension
RUN docker-php-ext-install mysqli

Build the php-app Image

Note: Stop and delete the running php-app container first. Leave the database container running.

If you're not still inside the php-app directory, now's the time to change into it. Let's build the image:

docker build -t php-app .

Run the php-app Container

docker run -itd --network docker-techlab --name php-app -p80:80 php-app

Now open a browser and navigate to http://localhost/db.php.

Additional Lab

Configuration should always be separate from the source code, so the database connection details must not be inside the php file db.php. Fix the code in the db.php file. According to the continuous delivery principles we don't want usernames and passwords in our source code.

Hint: Use the PHP function getenv("WHAT_EVER_KEY") to read config values from environment variables inside the frontend container. You might want to use the -e parameter to set an environment variable inside a container while running it (docker run -e).

← Linking Frontend and Backend | Multi Stage Builds →