×
Community Blog Getting to Know Dockerfile Instructions: Part 2

Getting to Know Dockerfile Instructions: Part 2

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.

This tutorial continues to give you practical experience using Dockerfile instructions. To follow the steps in this tutorial, make sure you have access to an Alibaba Cloud Elastic Compute Service instance with a recent version of Docker already installed. You can refer to this tutorial to learn how to install Docker on your Linux server.

It explores:

  1. Dockerfile HEALTHCHECKs
  2. Dockerfile environment variables - ENV
  3. Dockerfile ports - PORT

Dockerfile HEALTHCHECK

In this section you are going to learn how to use HEALTHCHECK in a Dockerfile.

HEALTHCHECK automate checks the health of your containers on a schedule you specify.

HEALTHCHECK [OPTIONS] CMD command

The options that can appear before CMD are:

--interval=DURATION (default: 30s)
--timeout=DURATION (default: 30s)
--start-period=DURATION (default: 0s)
--retries=N (default: 3)

For the purposes of this tutorial those durations are VERY long. We want to immediatly test and see the checks work.

Add only this to Dockerfile using

nano Dockerfile

FROM alpine:3.8
HEALTHCHECK --interval=3s --timeout=1s \
   CMD curl -f http://localhost/ || exit 1

Important: please note the curl -f http://localhost/ health check command will ALWAYS fail in this specific Alpine container: we have no webserver running in the container.

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'

After the container starts, you have to execute docker ps -a repeatedly every second ( for 10 seconds ) to see how the health checks progress.

docker ps -a

Expected output:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                           PORTS               NAMES

27134771d5dd        tutorial:demo       "/bin/sh -c 'while t�"   3 seconds ago       Up 1 second (health: starting)

ten seconds later below:

docker ps -a

Expected output

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES

27134771d5dd        tutorial:demo       "/bin/sh -c 'while t�"   12 seconds ago      Up 10 seconds (unhealthy)                       tutorial

(health: starting) gets shown for 10 seconds since the checks get done in intervals of 3 seconds ( --interval=3s) and we specified a --timeout=1s.

Let's reduce the interval to 1 second so that we can see the health results faster.

Let's also reduce the retries from the default of 3 to just 1 using: --retries=1

In a PROD environment you will almost never use retries = 1. The 3 is a much better realistic real-life value.

Add this to Dockerfile using

nano Dockerfile

FROM alpine:3.8
HEALTHCHECK --interval=1s --timeout=1s --retries=1 \
   CMD curl -f http://localhost/ || exit 1

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 ps -a; sleep .4; docker ps -a; sleep .4; docker ps -a; sleep .4; docker ps -a; sleep .4;

Notice I am checking the container status every .4 seconds.

Expected output:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                                     PORTS               NAMES

1e83c476c2ee        tutorial:demo       "/bin/sh -c 'while t�"   1 second ago        Up Less than a second (health: starting)                       tutorial
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                                     PORTS               NAMES

1e83c476c2ee        tutorial:demo       "/bin/sh -c 'while t�"   2 seconds ago       Up Less than a second (health: starting)                       tutorial
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES

1e83c476c2ee        tutorial:demo       "/bin/sh -c 'while t�"   2 seconds ago       Up 1 second (unhealthy)                       tutorial
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES

1e83c476c2ee        tutorial:demo       "/bin/sh -c 'while t�"   3 seconds ago       Up 1 second (unhealthy)                       tutorial

For 0.8 seconds the health status is (health: starting)

After one second the status changes to (unhealthy).

It is up to you as admin to tinker these settings to suit your actual prod env:

--interval=DURATION (default: 30s)
--timeout=DURATION (default: 30s)
--start-period=DURATION (default: 0s)
--retries=N (default: 3)

You as admin must also determine in your prod env what appropriate health check commands would be. Web server containers, database containers, haproxy containers all have different health check requirements.

One second is too low a duration: it was just used here to show quickly how the status changes.

Just to see what healthy looks like, let's do a sample health check that succeeds.

Add this to Dockerfile using

nano Dockerfile

FROM alpine:3.8
HEALTHCHECK --interval=.1s --timeout=.4s  --retries=1\
  CMD sleep .1 || exit 1

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 ps -a; sleep .4; docker ps -a; sleep .4; docker ps -a; sleep .4; docker ps -a; sleep .4;

Expected output:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                                     PORTS               NAMES

c83552f66511        tutorial:demo       "/bin/sh -c 'while t�"   1 second ago        Up Less than a second (health: starting)                       tutorial
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES

c83552f66511        tutorial:demo       "/bin/sh -c 'while t�"   2 seconds ago       Up Less than a second (healthy)                       tutorial
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                  PORTS               NAMES

c83552f66511        tutorial:demo       "/bin/sh -c 'while t�"   2 seconds ago       Up 1 second (healthy)                       tutorial
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS               NAMES

c83552f66511        tutorial:demo       "/bin/sh -c 'while t�"   3 seconds ago       Up 2 seconds (healthy)                       tutorial

Notice how the status changes to (healthy) in less that a second: the sleep .1 sleeps successfully for a tenth of a second then returns a successful status return code.

Unfortunately the docker ps -a command status filter does NOT match containers by HEALTH status. You can ONLY filter using created, restarting, running, removing, paused, exited and dead.

You cannot execute: docker ps --filter status=healthy

You cannot execute: docker ps --filter status=unhealthy

You have to use:

docker ps -a | grep '(healthy)'

and

docker ps -a | grep '(unhealthy)'

Dockerfile EXPOSE Ports

In this section you will learn how Dockerfile uses EXPOSE to expose ports of the container.

Containers are isolated running processes. They must specify explicitly which ports are accessible to other containers.

A port is the official way a process allows other processes to contact it and send it commands.

For example with Apache port 80 in normally exposed. Apache hides isolated within its container. The ONLY way to get it to do something is to access it via port 80.

The EXPOSE instruction declares the ports the container listens on.

You can state whether the port listens on the TCP or UDP protocol, The default is TCP if the protocol is not specified.

The EXPOSE instruction does not actually publish the port - it does not make it available to others.

EXPOSE only serves as documentation. It declares which ports in the container are intended to be available to be published.

From https://docs.docker.com/engine/reference/builder/#expose

To actually publish the port when running the container, use the -p flag on docker run to publish and map one or more ports, or the -P flag to publish all exposed ports and map them to high-order ports.

By default, EXPOSE assumes TCP. You can also specify UDP:

EXPOSE 80/tcp

EXPOSE 80/udp

In this tutorial we are going to expose port 80, tcp and udp.

Edit your Dockerfile to look like:

nano Dockerfile

FROM alpine:3.8
EXPOSE 80/tcp
EXPOSE 80/udp

Build the image using

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

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

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

Expected output

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                                     PORTS               NAMES

6269a988e1dd        tutorial:demo       "/bin/sh -c 'while t�"   1 second ago        Up Less than a second (health: starting)   80/tcp, 80/udp      tutorial

In the ports column we can see those ports exposed: but they are not available for use. We must publish them. We do this using -p 30000:80/tcp on the docker run command.

-p 30000:80/tcp

The 30000 specifies the host port number. 80/tcp specifies the container port number.

Let's create the container and see the result:

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

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

Expected output:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                  PORTS               NAMES

8b3e43916708        tutorial:demo       "/bin/sh -c 'while t�"   1 second ago        Up Less than a second   0.0.0.0:30000->80/tcp, 80/udp      tutorial

0.0.0.0:30000->80/tcp

Port 30000 on localhost is mapped to port 80 in the container.

We can check that port 30000 is now open on the host:

The ss command is used to show information about sockets. It is similar to netstat command. ( The netstat command no longer exists in default installation of the CentOS distro. It is replaced with service command. )

ss -t -a -n

short, long versions of the options:

  1. -t, --tcp display only TCP sockets
  2. -a, --all display all sockets
  3. -n, --numeric don't resolve service names, show numeric port number

Expected output

State       Recv-Q Send-Q Local Address:Port                Peer Address:Port
LISTEN      0      128                *:22                             *:*
ESTAB       0      0      192.168.56.44:22                 192.168.56.11:4118
ESTAB       0      64     192.168.56.44:22                 192.168.56.11:2719
LISTEN      0      128               :::30000                         :::*
LISTEN      0      128               :::22                            :::*

We can see that port 30000 is now listening on the host.

The 30000 has no special meaning. Its just used here as an easy to find port number.

Let's publish udp port 80 as well. Run:

docker stop -t 0 tutorial; ;   docker container prune -f  
docker run -p 40080:80/udp 30000:80/tcp -

Check to see if the publish worked:

ss -u -a -n

Expected output

State       Recv-Q Send-Q Local Address:Port                Peer Address:Port
UNCONN      0      0                 :::40080                         :::*

Noticed this time I used -u argument to show udp ports.

Dockerfile ENV Variables

You can use environment variables to send values to running containers.

They are part of the environment in which a container runs: you will see how this works below.

Syntax:

ENV

ENV =

The ENV instruction sets the environment variable to the value .

Examples:

ENV myVar1=1
ENV myVar42=42
ENV myAlfaVar=alfa value abc

Let's use our small Alpine Linux distro to see env variables within the env.

Create a Dockerfile with this content:

FROM alpine:3.8
ENV myVar1 1
ENV my42 42
ENV myVar42=42
ENV myAlfaVar='alfa abc'

Notice I used 2 different ways to declare env variables: one is space separated, one is = separated.

Build the image using

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

Run:

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 the printenv command at the # prompt:

Expected output

/ # printenv
HOSTNAME=1314796592cf
SHLVL=1
HOME=/root
my42=42
TERM=xterm
myVar1=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
myAlfaVar=alfa abc
myVar42=42
PWD=/
/ # exit

All the env variables we declared are there.

You can override the env variables when using docker run:

Run:

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

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

If you now enter the container you will see my42 is 44000.

docker exec -it tutorial /bin/sh

Expected output

/ # printenv
HOSTNAME=1190753a779e
SHLVL=1
HOME=/root
my42=44000
TERM=xterm
myVar1=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
myAlfaVar=alfa abc
myVar42=42
PWD=/

You now have some practical experience declaring ENV variables in a Dockerfile as well as overriding them when using docker run -e 'my-env-var-name=new value'

You can also inspect the content of env variables using printenv. ( You could also use set or env to show env variables at the shell prompt. )

More information https://en.wikipedia.org/wiki/Environment_variable

https://docs.docker.com/engine/reference/builder/#env

https://docs.docker.com/engine/reference/builder/#environment-replacement

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

0 0 0
Share on

Alibaba Clouder

2,599 posts | 762 followers

You may also like

Comments