docker-create-image

Create docker image

There are several ways to create an image, you can create it by

  • $ docker commit # from an existing container
  • $ docker load # from a saved tar package
  • $ docker build # from a Dockerfile or from standard input

Here I only say how to create an image from Dockerfile, for other ways, please refer to docker command that I posted earlier.

Basically speaking, Dockerfile is a sequence of instructions in order, docker parses these instructions one by one created layer when necessary, at last generates an image with these layers and conf.

Here is an overview of all instructions that docker provided, I will explain some of them later on with more details.

docker instructions

instructions

Here is just a basic usage of some commands, details refer to

  • FROM: Sets the base image for subsequent
  • MAINTAINER: set the author field of the generated images(deprecated use LABEL instead)
  • RUN: Execute commands in a new layer on top of the current image and commit the result
  • CMD: Allow only one CMD as the start command when container starts
  • LABEL: Add metadata to an image
  • EXPOSE: Informs container runtime that the container listens on the specified network ports at runtime, it’s just declaration
  • ENV: Sets an environment variable
  • ADD: Copy new files or directories or remote file URLs from into the filesystem of the container, it can process tar, deb etc uncompressed then copy
  • COPY: Copy new files or directories into the filesystem of the container
  • ENTRYPOINT: Allows you to configure a container that will run as an executable
  • VOLUME: Creates a mount point and marks it as holding externally mounted volumes from native host or other containers

Here is an example of Dockerfile, see comments for more details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# from base image to create new
FROM ubuntu:18.04
LABEL maintainer="jason_lkm@163.com"
LABEL version="1.0"
# remember to delete intermediate files for apt
RUN apt-get update && apt-get install -y \
automake \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# set start command
CMD ["echo", "hello"]
# if run docker with docker -P(no parameter for it), map random port from host to 80(EXPOSE here)
# if docker doesn't run with -P, here is an description at all!!!!
EXPOSE 80/udp
# better use '-p' to explicitly map host port to docker port when docker run
# docker run -p 8080:80 nginx # map host 8080 to container 80, will create a DNAT rule on host
# 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.1:80

ENV NAME="jason" EMAIL="jason@163.com"
# ADD COPY support pattern matching
ADD ./test.tar /test
COPY ./*.txt /
COPY ./b.c /

# create volume for each container automatically, remember to delete volume
# when remove that docker
# /data is dst path
VOLUME ["/data"]

There are several rules that you must obey to avoid troubles

  • Write one CMD in Dockerfile, if many, the last on takes effect
  • Better to use CMD or ENTRYPOINT, not both, if both used, CMD will be passed to ENTRYPOINT as parameter
  • CMD may be overwritten by command from cli $docker run nginx /bin/bash, /bin/bash overwrites CMD in nginx image
  • Remove unnecessary files when do apt-get
  • Avoid RUN apt-get upgrade and dist-upgrade

NOTE: instruction in Dockerfile may be overwritten by parameter from cli, like ENTRYPOINT may be overwritten by docker run –entrypoint, so check what conf container uses by
$docker inspect $container_id(name)

how build works

When you issue a docker build command, the current working directory is called the build context, all recursive contents of files and directories in the current directory are sent to the Docker daemon, but only the ones used by Dockerfile is copied to image, other is not used!!!, docker client(issuer) and docker daemon can be at different machines, in that case, make sure there is no unnecessary files under current dir, as all files will be sent to docker daemon, it may take long timer if the current dir is large, after docker daemon receives all and parses Dockerfile and execute each instruction one by one with order, create layer if necessary or set conf for this image, at last save image to the local registry of machine where docker daemon is running.

Actually before it runs, docker daemon created a temporary for this image and chroot to it, so that the destination path(ADD ./test.tar /) in Dockefile is for the new rootfs

how build docker image works

build an image

from a file

1
2
3
4
5
$ ls
Dockerfile
$ docker build --no-cache -t $image_name:$tagname .
# it will read Dockerfile from current dir
# and send all files/dirs to docker daemon

from standard input

1
2
3
4
5
6
7
8
9
10
11
# t is image name
$ docker build -t tcpdump - <<EOF
FROM ubuntu
RUN apt-get update && \
apt-get install -y tcpdump && \
apt-get install -y net-tools && \
rm -rf /var/lib/apt/lists/* && \
apt-get clean

CMD /bin/bash
EOF

check info about image

  • check instructions used by an image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ docker history $image_id(name) --no-trunc
    $ docker history $image_id(name)
    IMAGE CREATED CREATED BY SIZE COMMENT

    output:
    5a9061639d0a 26 hours ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
    <missing> 26 hours ago /bin/sh -c #(nop) STOPSIGNAL SIGTERM 0B
    <missing> 26 hours ago /bin/sh -c #(nop) EXPOSE 80 0B
    <missing> 26 hours ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B
  • check instructions with full content of an image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    cat /var/lib/docker/image/aufs/imagedb/content/sha256/5a9061639d0aeca4b13f8e18b985eea79e55168969d069febdb6723993ebba7d  | python -m json.tool

    output:
    ...
    "history":[
    {
    "created": "2019-10-17T04:43:59.291372925Z",
    "created_by": "/bin/sh -c ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log"
    },
    {
    "created": "2019-10-17T04:43:59.546729941Z",
    "created_by": "/bin/sh -c #(nop) EXPOSE 80",
    "empty_layer": true
    },
    {
    "created": "2019-10-17T04:43:59.807233891Z",
    "created_by": "/bin/sh -c #(nop) STOPSIGNAL SIGTERM",
    "empty_layer": true
    },
    {
    "created": "2019-10-17T04:44:00.059327535Z",
    "created_by": "/bin/sh -c #(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]",
    "empty_layer": true
    }
    ]

    As you can see some instructions Did NOT create layer at all, see ‘empty_layer’ field.

  • check the config for an image

    1
    2
    3
    $ docker images
    $ docker inspect 5a9061639d0a
    # this will show what's the cmd, env, entrypoint, volume, rootfs etc for this image