Linux: Usage of Docker

This article will describe usage of Docker.

2 Image and container

Image is an immutable root filesystem created from Dockerfile. Image is not used at runtime.

Container is a mutable root filesystem created from image. Container is used at runtime.

For example, the following command will result in as below.

$ docker run hello-world
<snip>
Hello from Docker!
<snip>
  • Download hello-world image from remote server.
  • Container with a random name is created from image.
  • A binary hello specified by CMD in Dockerfile outputs "Hello from Docker!".

Running binary hello on host machine outputs the same. But this is not the same environment.

$ sudo /var/lib/docker/aufs/diff/<hash>/hello
<snip>
Hello from Docker!
<snip>

3 Download image and run command on container

There are two way. One is running "docker run". Another is "docker pull", "docker create" and "docker start".

3.1 docker run

Running the following "docker run" will download <image> and run <cmd> on <container>.

$ docker run -it --name <container> <image> <cmd>
  • Omitting –name <container> creates random name.
  • Omitting <cmd> uses CMD in Dockerfile.
  • You can use ID to <container> and <images>.

This "docker run" is same with the following commands. If omitting "docker pull" and <image> is not exist, "docker create" will download <image> from remote server.

$ docker pull <image>
$ docker create -it --name <container> <image> <cmd>
$ docker start -i <container>

3.2 docker pull

"docker pull" is a command for downloading image to local. Image is downloaded from remote server called Registry. The default registry is Docker Hub.

Because "docker create" search will image from local first, you can download images that you will use in advance.

When creating local Registry, you need to run "docker run" first, and then push image to local Registry.

3.3 docker create -it

"docker create" is a command for creating container from image. You need to be careful with -it option.

If -it option is not specified, bash will be terminated and container will be stopped immediately.

$ docker create --name bash-without-it ubuntu:16.04
$ docker start -i bash-without-it
$ # This is host machine's shell prompt.
$ docker attach bash-without-it
You cannot attach to a stopped container, start it first

If -i option is not specified, bash does not accept stdin.

$ docker create -t --name bash-without-i ubuntu:16.04
$ docker start -i bash-without-i
root@35859c489ea1:/# ls # Type ls[RET] but no response

If -t option is not specified, pseudo-tty will no be created. TERM variable will be empty and prompt string will not be displayed.

$ docker create -i --name bash-without-t ubuntu:16.04
$ docker start -i bash-without-t
uname # Type uname[RET]
Linux
ls    # Type ls[RET]
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

If -it option is specified, bash is done normally.

$ docker create -it --name bash ubuntu:16.04
$ docker start -i bash
root@f5fbe8daa835:/# uname
Linux
root@f5fbe8daa835:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

3.4 docker start -i

"docker start" is a command for starting container.

If -i option is specified, attach to the container at the same time as starting the container with connecting stdin.

$ docker start -i <container>

The following command is almost same with "docker run", except that it returns to host machine between start and attach.

$ docker start <container>
$ docker atttach <container>

There is -a option similar with -i option. If -a option is specified, attach to the container at the same time as starting the container without connecting stdin.

$ docker start -i <container>

The following command is almost same with "docker run", except that it returns to host machine between start and attach.

$ docker start <container>
$ docker atttach --no-stdin <container>

4 Create your own image

This article will create image which has a emacs package with using ubuntu:16.04 as a base image.

4.1 Create your own image from container

Create container from ubuntu:16.04.

$ docker run -it --name ubuntu-1604-emacs ubuntu:16.04

This image does not have a emacs package.

root@cf4e53146385:/# emacs --version
bash: emacs: command not found

After installing emacs, terminate bash and stop container.

root@cf4e53146385:/# apt update -y && apt install -y emacs && exit

Create image from container with "docker commit".

$ docker commit ubuntu-1604-emacs ubuntu-1604-emacs-image

Image was created.

$ docker images
REPOSITORY              TAG    IMAGE ID     CREATED            SIZE
ubuntu-1604-emacs-image latest 41716956f654 About a minute ago 546 MB
ubuntu                  16.04  f49eec89601e 3 weeks ago        129 MB

Create container from created image.

$ docker run -it ubuntu-1604-emacs-image

This image has a emacs container.

root@b91c82c0c976:/# emacs --version
GNU Emacs 24.5.1
Copyright (C) 2015 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.

4.2 Create your own image from Dockerfile

Create the following Dockefile.

$ cat Dockerfile
FROM ubuntu:16.04

RUN \
  apt update -y && \
  apt install -y emacs

CMD ["bash"]

Create image from Dockerfile with "docker build". Temporally container will be created.

$ docker build -t ubuntu-1604-emacs-dockerfile .
Sending build context to Docker daemon 2.048 kB
Step 1/3 : FROM ubuntu:16.04
 ---> f49eec89601e
Step 2/3 : RUN apt update -y &&   apt install -y emacs
 ---> Running in d7a592031026

WARNING: apt does not have a stable CLI interface. Use with caution in
scripts.

Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
<snip>
Step 3/3 : CMD bash
 ---> Running in 77adc54aa247
 ---> bcf7de949838
Removing intermediate container 77adc54aa247
Successfully built bcf7de949838

Image was created.

$ docker images
REPOSITORY                    TAG     IMAGE ID      CREATED         SIZE
ubuntu-1604-emacs-dockerfile  latest  bcf7de949838  36 seconds ago  546 MB
ubuntu-1604-emacs-image       latest  41716956f654  34 minutes ago  546 MB
ubuntu                        16.04   f49eec89601e  3 weeks ago     129 MB

Create container from created image.

$ docker run -it ubuntu-1604-emacs-dockerfile

This image has a emacs container.

root@6ac693d1b24a:/# emacs --version
GNU Emacs 24.5.1
Copyright (C) 2015 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.

5 Access to container from external network

This article will access to Jenkins on container from external network. Jenkins will use 8080/tcp.

Docker has three network by default. If ommitinig –network option, bridge network will be used.

bridge bridge network has a bridge names docker0.
  This will connect host machine's NIC with IP masquerade of
  iptables (L3 layer).
  This needs port forwarding for accessing from external network.
host host network uses host machine's NIC apparently.
  External network cannot access to this.
none none network does not create NIC.

NIC of this article is as below.

eth0 Host machine's NIC. IP address is 192.168.11.102/24.
docker0 The bridge which is created when starting docker.
  IP address is 172.17.0.1/16 assigned by bridge network.

5.1 Use port forwarding on bridge network

Map 80/tcp on host machine to 8080/tcp on container. Omitting –network option specifies bridge network.

$ docker run -d -p 80:8080 jenkins

Accessing 80/tcp on host machine will display Jenkins.

http://192.168.11.102

0001_bridge-network.png

5.2 Use the same network address of external network with user defined network

Create another bridge which is L2 layer. The following article will create bridge names br0.

Created bridge is as below.

br0 This is a bridge independently of docker.
  IP address of br0 is 192.168.11.102/24.
  IP address of eth0 is empty.

Create network which has a br0 with "docker network create".

$ docker network create \
       --driver=bridge \
       --subnet=192.168.11.102/24 \
       --gateway=192.168.11.102 \
       -o "com.docker.network.bridge.enable_icc=true" \
       -o "com.docker.network.bridge.enable_ip_masquerade=false" \
       -o "com.docker.network.bridge.host_binding_ipv4=0.0.0.0" \
       -o "com.docker.network.bridge.name=br0" \
       -o "com.docker.network.driver.mtu=1500" \
       br0_network

Create container that network is br0_network, IP address is 192.168.11.200 and DNS is 192.168.11.1. This DNS value will be used by embedded DNS server (127.0.0.11). /etc/resolv.conf on container does not have this DNS value but have nameserver entry to embedded DNS server (127.0.0.11).

$ docker run -d --network=br0_network --ip=192.168.11.200 \
--dns=192.168.11.1 jenkins

Accessing to 8080/tcp on container will display Jenkins.

http://192.168.11.200:8080

0002_user-defined-network.png

5.3 Problem with user defined network

Because docker might not have IPAM driver for using external DHCP server, using user defined network needs implementation of DHCP (Or just write entry to dhcpd.conf) independently of docker.

Though IP masquerade is false, L3 layer setting will be done. This will prevent bridge network working. If you need container without specifing ip, use host network instead of bridge network.

When specifiying multiple –dns option and first DNS server returns REFUSED, the embedded DNS server will not send query to second DNS server. The embedded DNS server will send query when DROP and REJECT. I don't know this is correct working but my Windows, Linux and MacOS will access to second DNS server when first DNS returns REFUSE.

6 Other command

docker <arg> is alias for docker image <arg> and docker container <arg>.

docker images Show images.
docker rmi <image> Remove image.
docker ps [-a] Show containers without stopped ones.
  -a shows all containers.
docker stop <container> Stop container.
docker exec <container> <cmd> Run other command on running container.

"docker ps -q" shows only ID. The following command will stop all running containers.

$ docker stop $(docker ps -q)