Monday, May 30, 2022

Docker 101 - Container Image

  


What is Container Image?



When running a container, it uses an isolated filesystem. This custom filesystem is provided by a container image. Since the image contains the container's filesystem, it must contain everything needed to run an application - all dependencies, configuration, scripts, binaries, etc. The image also contains other configuration for the container, such as environment variables, a default command to run, and other metadata. Reference


What commands we can use for iamge?



We can get more detail of docker image commands by the following command.

Ex:
$ docker image --help

Result:
Usage:  docker image COMMAND

  Manage images

  Commands:
  build       Build an image from a Dockerfile
  history     Show the history of an image
  import      Import the contents from a tarball to create a filesystem
image
  inspect     Display detailed information on one or more images
  load        Load an image from a tar archive or STDIN
  ls          List images
  prune       Remove unused images
  pull        Pull an image or a repository from a registry
  push        Push an image or a repository to a registry
  rm          Remove one or more images
  save        Save one or more images to a tar archive
(streamed to STDOUT by default)
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

  Run 'docker image COMMAND --help' for more information on a command.


How to get Container Images?


1. Pull images from Container Registry


We can pull images from Container Registry (Using Docker Hub in this exp).

Ex:
$ docker image pull alpine

Result:
Using default tag: latest
  latest: Pulling from library/alpine
  2408cc74d12b: Pull complete
  Digest: sha256:686d8c9dfa6f3ccfc8230bc3178d23f84...
  Status: Downloaded newer image for alpine:latest
  docker.io/library/alpine:latest

Ex:
$ docker image ls

Result:
REPOSITORY               TAG       IMAGE ID       CREATED       SIZE
  alpine                   latest    e66264b98777   5 days ago    5.53MB

Since we did not specify the version of alpine image, by default, we will pull the latest one.
Let's find an version of alpine from Docker Hub.

Ex:
$ docker image pull alpine:3.16.0

Result:
3.16.0: Pulling from library/alpine
  Digest: sha256:686d8c9dfa6f3ccfc8230bc3178d23f84...
  Status: Downloaded newer image for alpine:3.16.0
  docker.io/library/alpine:3.16.0

Ex:
$ docker image ls

Result:
REPOSITORY               TAG       IMAGE ID       CREATED       SIZE
  alpine                   3.16.0    e66264b98777   5 days ago    5.53MB
  alpine                   latest    e66264b98777   5 days ago    5.53MB

2. Load from a file


Sometimes, if we are in intra-network environment and only have limit access to the public Container Registry, then we can load image which is saved by others who have permissions.

For example, in computer A, we'd like to save an existing image to our filesystem.

Let's check the command detail first.

Ex:
$ docker image save --help

Result:
Usage:  docker image save [OPTIONS] IMAGE [IMAGE...]

  Save one or more images to a tar archive (streamed to STDOUT by default)

  Options:
  -o, --output string   Write to a file, instead of STDOUT

Then we can use the following commands to save an image to a tar archive.

Ex:
$ docker image save -o alpine-exp alpine:3.16.0

Then we can forward this tar archive to the computer B.

In computer B, let's check the existing image list first.

Ex:
$ docker image ls

Result:
REPOSITORY               TAG       IMAGE ID       CREATED       SIZE

It is empty, and then let's check how to load the image from tar archive.

Ex:
$ docker image load --help

Result:
Usage:  docker image load [OPTIONS]

  Load an image from a tar archive or STDIN

  Options:
  -i, --input string   Read from tar archive file, instead of STDIN
  -q, --quiet          Suppress the load output

Then we can use the following command to load it.

Ex:
$ docker image load -i alpine-exp

Result:
24302eb7d908: Loading layer [========================>]  5.811MB/5.811MB
  Loaded image: alpine:3.16.0

Check the image list again.

Ex:
$ docker image ls

Result:
REPOSITORY               TAG       IMAGE ID       CREATED       SIZE
  alpine                   3.16.0    e66264b98777   5 days ago    5.53MB

3. Generated by an existing Container including changes


Imagine that we pulled the alpine image to our local and use interactive mode to setup python environment. And also, we have added a hello-word.py to verify whether python works or not.
In the end we want to save those changes to a new image and we can use it in the future without configuring python env again.

Let's pull and run alpine image with interactive mode.

Ex:
$ docker container run -it alpine /bin/sh

Result:
Unable to find image 'alpine:latest' locally
  latest: Pulling from library/alpine
  2408cc74d12b: Pull complete
  Digest: sha256:686d8c9dfa6f3ccfc8230bc3178d23f84...
  Status: Downloaded newer image for alpine:latest
  / #

Then inside this container, let's install python env.

Ex:
/ # apk add python3

Result:
fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/main/x86_64/
APKINDEX.tar.gz
  fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/community/x86_64/
APKINDEX.tar.gz
  (1/13) Installing libbz2 (1.0.8-r1)
  (2/13) Installing expat (2.4.8-r0)
  (3/13) Installing libffi (3.4.2-r1)
  (4/13) Installing gdbm (1.23-r0)
  (5/13) Installing xz-libs (5.2.5-r1)
  (6/13) Installing libgcc (11.2.1_git20220219-r2)
  (7/13) Installing libstdc++ (11.2.1_git20220219-r2)
  (8/13) Installing mpdecimal (2.5.1-r1)
  (9/13) Installing ncurses-terminfo-base (6.3_p20220521-r0)
  (10/13) Installing ncurses-libs (6.3_p20220521-r0)
  (11/13) Installing readline (8.1.2-r0)
  (12/13) Installing sqlite-libs (3.38.5-r0)
  (13/13) Installing python3 (3.10.4-r0)
  Executing busybox-1.35.0-r13.trigger
  OK: 57 MiB in 27 packages

Then we can add a file 'hello-world.py' in root folder, and run it to see if python works.

Ex:
/ # echo "print('hello-world')" > ./hello-world.py
  / # python3 ./hello-world.py

Result:
hello-world

Exit the interactive mode, and check the container list first.

Ex:
$ docker container ls -a

Result:
CONTAINER ID   IMAGE     COMMAND     CREATED              
1a6048c9b895   alpine    "/bin/sh"   About a minute ago  

STATUS                      PORTS     NAMES
Exited (0) 13 seconds ago             pensive_dhawan

Let's check the helper to see how to commit changes to a new image.

Ex:
$ docker container --help

Result:
Usage:  docker container COMMAND

  Manage containers

  Commands:
  attach      Attach local standard input, output, and error streams to
a running container
  commit      Create a new image from a container's changes
  cp          Copy files/folders between a container and the local
filesystem
  create      Create a new container
  diff        Inspect changes to files or directories on a container's
filesystem
  exec        Run a command in a running container
  export      Export a container's filesystem as a tar archive
  inspect     Display detailed information on one or more containers
  kill        Kill one or more running containers
  logs        Fetch the logs of a container
  ls          List containers
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  prune       Remove all stopped containers
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  run         Run a command in a new container
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage
statistics
  stop        Stop one or more running containers
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  wait        Block until one or more containers stop, then print their
exit codes

  Run 'docker container COMMAND --help' for more information on a command.

Then use the following command to generate a new image.

Ex:
$ docker container commit 1a6 alpine-py-hello-world

Result:
sha256:7c1c7aa3d87...

Ex:
$ docker image ls

Result:
REPOSITORY               TAG       IMAGE ID      
alpine-py-hello-world   latest    7c1c7aa3d87b  
alpine                   latest    e66264b98777  

CREATED         SIZE
18 seconds ago   55.8MB
7 days ago       5.53MB

Then run this new image with COMMAND to see if python is available in this container.

Ex:
$ docker container run -it alpine-py-hello-world python3 hello-world.py

Result:
hello-world

Ex:
$ docker container ls -a

Result:
CONTAINER ID   IMAGE                   COMMAND                  
  4bb3e1d4672f   alpine-py-hello-world   "python3 hello-world…"  
  1a6048c9b895   alpine                   "/bin/sh"                

CREATED         STATUS                     PORTS     NAMES
2 seconds ago   Exited (0) 1 second ago              awesome_raman
4 minutes ago   Exited (0) 2 minutes ago             pensive_dhawan

4. Build from Dockerfile


Docker can build images automatically by reading the instructions from a Dockerfile.
A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Reference

From the previous example, we can use Dockerfile instead.

Dockerfile:
FROM alpine
  RUN apk add python3
  ADD hello-world.py /

Generate hello-world.py in root folder.

Ex:
$ echo "print('hello-world')" > ./hello-world.py

Ex:
$ ls

Result:
Dockerfile  hello-world.py

Check how to use build command.

Ex:
$ docker build --help

Result:
Usage:  docker build [OPTIONS] PATH | URL | -

  Build an image from a Dockerfile

  Options:
      --add-host list           Add a custom host-to-IP mapping (host:ip)
      --build-arg list          Set build-time variables
      --cache-from strings      Images to consider as cache sources
      --cgroup-parent string    Optional parent cgroup for the container
      --compress                Compress the build context using gzip
      --cpu-period int          Limit the CPU CFS (Completely Fair
Scheduler) period
      --cpu-quota int           Limit the CPU CFS (Completely Fair
Scheduler) quota
  -c, --cpu-shares int          CPU shares (relative weight)
      --cpuset-cpus string      CPUs in which to allow execution (0-3, 0,1)
      --cpuset-mems string      MEMs in which to allow execution (0-3, 0,1)
      --disable-content-trust   Skip image verification (default true)
  -f, --file string             Name of the Dockerfile
(Default is 'PATH/Dockerfile')
      --force-rm                Always remove intermediate containers
      --iidfile string          Write the image ID to the file
      --isolation string        Container isolation technology
      --label list              Set metadata for an image
  -m, --memory bytes            Memory limit
      --memory-swap bytes       Swap limit equal to memory plus swap: '-1'
to enable unlimited swap
      --network string          Set the networking mode for the RUN
instructions during build
(default "default")
      --no-cache                Do not use cache when building the image
      --pull                    Always attempt to pull a newer version of
the image
  -q, --quiet                   Suppress the build output and print image
ID on success
      --rm                      Remove intermediate containers after a
successful build (default true)
      --security-opt strings    Security options
      --shm-size bytes          Size of /dev/shm
  -t, --tag list                Name and optionally a tag in the 'name:tag'
format
      --target string           Set the target build stage to build.
      --ulimit ulimit           Ulimit options (default [])

Use the following command to build an image.

Ex:
$ docker build -t alpine-py-hello-world-by-dockerfile .

Result:
Sending build context to Docker daemon  3.072kB
  Step 1/3 : FROM alpine
  ---> e66264b98777
  Step 2/3 : RUN apk add python3
  ---> Running in 5a96b7d6b078
  fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/main/x86_64/
APKINDEX.tar.gz
  fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/community/x86_64/
APKINDEX.tar.gz
  (1/13) Installing libbz2 (1.0.8-r1)
  (2/13) Installing expat (2.4.8-r0)
  (3/13) Installing libffi (3.4.2-r1)
  (4/13) Installing gdbm (1.23-r0)
  (5/13) Installing xz-libs (5.2.5-r1)
  (6/13) Installing libgcc (11.2.1_git20220219-r2)
  (7/13) Installing libstdc++ (11.2.1_git20220219-r2)
  (8/13) Installing mpdecimal (2.5.1-r1)
  (9/13) Installing ncurses-terminfo-base (6.3_p20220521-r0)
  (10/13) Installing ncurses-libs (6.3_p20220521-r0)
  (11/13) Installing readline (8.1.2-r0)
  (12/13) Installing sqlite-libs (3.38.5-r0)
  (13/13) Installing python3 (3.10.4-r0)
  Executing busybox-1.35.0-r13.trigger
  OK: 57 MiB in 27 packages
  Removing intermediate container 5a96b7d6b078
  ---> f2528d14c5a3
  Step 3/3 : ADD hello-world.py /
  ---> ba66ce4b5f95
  Successfully built ba66ce4b5f95
  Successfully tagged alpine-py-hello-world-by-dockerfile:latest

Check the image list:

Ex:
$ docker image ls

Result:
REPOSITORY                            TAG       IMAGE ID      
  alpine-py-hello-world-by-dockerfile   latest    ba66ce4b5f95  
  alpine-py-hello-world                 latest    7c1c7aa3d87b  
  alpine                                latest    e66264b98777  

CREATED         SIZE
2 seconds ago   55.8MB
7 minutes ago   55.8MB
7 days ago      5.53MB

Then we can run it and test the python.

Ex:
$ docker container run -it alpine-py-hello-world-by-dockerfile
python3 ./hello-world.py

Result:
 hello-world

Ex:
$ docker container ls -a

Result:
 CONTAINER ID   IMAGE                                
  3eb64a6f21f7   alpine-py-hello-world-by-dockerfile  
  4bb3e1d4672f   alpine-py-hello-world                
  1a6048c9b895   alpine                                

COMMAND                  CREATED          STATUS                      
"python3 ./hello-wor…"   3 seconds ago    Exited (0) 2 seconds ago    
"python3 hello-world…"   8 minutes ago    Exited (0) 8 minutes ago      
"/bin/sh"                12 minutes ago   Exited (0) 10 minutes ago    

PORTS     NAMES
          quizzical_swirles
        awesome_raman
        pensive_dhawan


What is the scratch Image?



This image is most useful in the context of building base images (such as debian and busybox) or super minimal images (that contain only a single binary and whatever it requires, such as hello-world).


Dockerfile: Reference
FROM scratch
  COPY hello /
  CMD ["/hello"]

No comments:

Post a Comment