John Siu Blog

Tech - Business Tool, Personal Toys

Docker Commands

☰ Table of Content

Docker misc.


Install

Alpine

1
apk add docker docker-compose

Ubuntu

1
2
3
4
5
apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] http://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt update
apt install docker-ce docker-ce-cli containerd.io

Non-root

1
sudo adduser <user> docker

Import/Export Image

Manual

1
2
docker save -o <output tar> <image name>
docker load -i <tar file>

Push

Push from source to target.

1
docker save <image> | bzip2 | ssh user@host 'bunzip2 | docker load'

OR

1
docker save <image> | bzip2 | pv | ssh user@host 'bunzip2 | docker load'

Pull

1
ssh target_server 'docker save image:latest | bzip2' | pv | bunzip2 | docker load

Dockerfile

Alpine Base

apk update is not necessary if apk --no-cache add ... is used for pulling packages.

Common Steps

If creating a lot of Dockerfile with similar base packages like tzdata, ca-certificates, they should be moved to the top and separate from container specific steps.

Container & Startup Script

GID/UID

  1. Environment Variables

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    #!/bin/ash
    
    PUSR=mpd
    PHOME=/${PUSR}
    
    echo PGID:${PGID}
    echo PUID:${PUID}
    
    if [ "${PUID}" -lt "1000" ]
    then
      echo PUID cannot be \< 1000
      exit 1
    fi
    
    if [ "${PGID}" -lt "1000" ]
    then
      echo PGID cannot be \< 1000
      exit 1
    fi
    
    addgroup -g ${PGID} ${PUSR}
    adduser -D -h ${PHOME} -G ${PUSR} -u ${PUID} ${PUSR}
    

    Full example here.

  2. Docker Option

    1
    
    -u, --user string        Username or UID (format: <name|uid>[:<group|gid>])
    

Time Zone

There are multiple ways to set time zone inside container. Following are 2:

In Dockerfile, install tzdata.

  1. Environment Variables

    Pass P_TZ=America/New_York into container.

    In start up script:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    echo P_TZ:${P_TZ}
    if [ "${#P_TZ}" -gt "0" ]; then
      TZ="/usr/share/zoneinfo/${P_TZ}"
      if [ -f "${TZ}" ]; then
        cp ${TZ} /etc/localtime
        echo "${P_TZ}" >/etc/timezone
      else
        echo "${P_TZ}" not available.
      fi
    fi
    

    This is more reliable if no control of hosting OS, like cloud or Windows.

  2. Direct Mapping

    Use:

    1
    2
    
    -v /etc/localtime:/etc/localtime \
    -v /etc/timezone:/etc/timezone
    

    This is simpler if Linux host is guaranteed and always follow host’s time zone.

Exit Shell

If shell script is used in CMD or ENTRYPOINT to setup container environment, use exec to execute the final command so the shell can exit.

1
2
3
4
5
6
7
#!/bin/sh

# Preparation
  ...
# Done

exec <cmd>

This work for su <cmd> also.

Command Check

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/sh

# Run cmd with error check
RUN_CMD() {
	CMD=$1
	$CMD
	RTN=$?
	if [ ${RTN} -ne 0 ]; then
		echo \"$CMD\" error:${RTN}
		exit ${RTN}
	fi
	return ${RTN}
}

RUN_CMD "git submodule update --init --recursive"

Full example here.

Override ENTRYPOINT

Use --entrypoint sh to start shell in container using ENTRYPOINT:

1
2
docker run --rm -it --entrypoint sh <image>
docker-compose run --rm -it --entrypoint sh <service>

Docker Compose

Specify compose file

1
docker-compose -f <filename> up

Daemon mode

1
docker-compose -f <filename> up -d

This will also start compose container if docker is auto start during reboot.

Enter shell of running compose container

1
docker-compose -f <filename> exec <appname> sh

Docker Daemon Configuration

URI

TCP

To enable remote/tcp docker daemon access, edit docker.server

1
systemctl edit docker.service

with following content:

1
2
3
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2345 --containerd=/run/containerd/containerd.sock

IPv6 use:

1
2
3
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://[::1]:2345 --containerd=/run/containerd/containerd.sock
Unix Socket

Docker API socket is at /var/run/docker.sock

Data Root

–data-root, used to be -g, –graph, default to /var/lib/docker

Override in Systemd

1
systemctl edit docker.service

with following content:

1
2
3
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --data-root /new/location

Override with /etc/docker/daemon.json

1
2
3
{
  "data-root":"/new/location"
}

Log to Journald

All process stdout/stderr inside container go into docker log. To have that log into journald:

/etc/docker/daemon.json

1
2
3
{
  "log-driver": "journald"
}

/dev/log

For processes(eg. postfix) that write to system log, map /dev/log:

1
-v /dev/log:/dev/log

In compose

1
2
volumes:
  - /dev/log:/dev/log

daemon.json

From: Docker daemon configuration file

 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{
  "authorization-plugins": [],
  "data-root": "",
  "dns": [],
  "dns-opts": [],
  "dns-search": [],
  "exec-opts": [],
  "exec-root": "",
  "experimental": false,
  "features": {},
  "storage-driver": "",
  "storage-opts": [],
  "labels": [],
  "live-restore": true,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file":"5",
    "labels": "somelabel",
    "env": "os,customer"
  },
  "mtu": 0,
  "pidfile": "",
  "cluster-store": "",
  "cluster-store-opts": {},
  "cluster-advertise": "",
  "max-concurrent-downloads": 3,
  "max-concurrent-uploads": 5,
  "default-shm-size": "64M",
  "shutdown-timeout": 15,
  "debug": true,
  "hosts": [],
  "log-level": "",
  "tls": true,
  "tlsverify": true,
  "tlscacert": "",
  "tlscert": "",
  "tlskey": "",
  "swarm-default-advertise-addr": "",
  "api-cors-header": "",
  "selinux-enabled": false,
  "userns-remap": "",
  "group": "",
  "cgroup-parent": "",
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 64000,
      "Soft": 64000
    }
  },
  "init": false,
  "init-path": "/usr/libexec/docker-init",
  "ipv6": false,
  "iptables": false,
  "ip-forward": false,
  "ip-masq": false,
  "userland-proxy": false,
  "userland-proxy-path": "/usr/libexec/docker-proxy",
  "ip": "0.0.0.0",
  "bridge": "",
  "bip": "",
  "fixed-cidr": "",
  "fixed-cidr-v6": "",
  "default-gateway": "",
  "default-gateway-v6": "",
  "icc": false,
  "raw-logs": false,
  "allow-nondistributable-artifacts": [],
  "registry-mirrors": [],
  "seccomp-profile": "",
  "insecure-registries": [],
  "no-new-privileges": false,
  "default-runtime": "runc",
  "oom-score-adjust": -500,
  "node-generic-resources": ["NVIDIA-GPU=UUID1", "NVIDIA-GPU=UUID2"],
  "runtimes": {
    "cc-runtime": {
      "path": "/usr/bin/cc-runtime"
    },
    "custom": {
      "path": "/usr/local/bin/my-runc-replacement",
      "runtimeArgs": [
        "--debug"
      ]
    }
  },
  "default-address-pools":[
    {"base":"172.80.0.0/16","size":24},
    {"base":"172.90.0.0/16","size":24}
  ]
}

John Siu

Update: 2020-09-01
comments powered by Disqus