Using Docker – First Steps

Welcome back! In this post I will cover my experience going over chapter 3 “First Steps” from the book “Using Docker” by Adrian Mouat published by O’REILLY. It is a fact that when you read a technical book you will not be able to learn as much as if you spend the time in front of a computer experimenting and making mistakes.

In this post I will cover some of my experimentation with the concepts presented in the third chapter of the book. If you are reading this post, I strongly recommend reading the third chapter, then experiment with the concepts and finally go over this post. You never know which things you might have missed to understand by only reading.

I am experimenting with Docker on Linux. Sometime ago I spent time with an earlier version of Docker on Windows. I am sure you can use Docker on different cloud platforms (i.e., AWS, Azure and Google).

For starters you need to have Docker installed. Their web site contains helpful information on how to get this done. Last year I installed Docker on one of my Linux machines running CentOS 7. For this post I made sure all was well and started experimenting. If interested, you could check my previous post here.

When I am starting to work with Docker I like to make sure that all is up and running.

# **** check start and stop Docker (1) ****
$ docker version
Client:
 Version:           18.09.2
 API version:       1.39
 Go version:        go1.10.6
 Git commit:        6247962
 Built:             Sun Feb 10 04:13:27 2019
 OS/Arch:           linux/amd64
 Experimental:      false
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

$ sudo service docker start
Redirecting to /bin/systemctl start docker.service

$ docker version           
Client:
 Version:           18.09.2
 API version:       1.39
 Go version:        go1.10.6
 Git commit:        6247962
 Built:             Sun Feb 10 04:13:27 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.2
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.6
  Git commit:       6247962
  Built:            Sun Feb 10 03:47:25 2019
  OS/Arch:          linux/amd64
  Experimental:     false

$ sudo service docker stop
Redirecting to /bin/systemctl stop docker.service

I issue the docker version command. As you can see, if the Docker daemon is not responding chances are that it is not running. After restarting it you should get information about the Docker engine. The last command in the previous screen capture shows how to stop the Docker daemon. You can also restart it if you feel the need.

# **** run a container and display a message (2)****
$ docker run debian echo "Hello John!!!"
Hello John!!!

$ docker run ubuntu echo "Hello World!!!"
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6cf436f81810: Pull complete 
987088a85b96: Pull complete 
b4624b3efe06: Pull complete 
d42beb8ded59: Pull complete 
Digest: sha256:7a47ccc3bbe8a451b500d2b53104868b46d60ee8f5b35a24b41a86077c650210
Status: Downloaded newer image for ubuntu:latest
Hello World!!!

$ docker run debian echo "Hello John!!!" 
Hello John!!!

$ docker run ubuntu echo "Hello World!!!"
Hello World!!!

In this example, we started a Debian image and requested it to run the echo shell command with a specified string.

The second command is quite similar but in that case we requested to use a Linux Ubuntu image which I did not have in my machine. Docker pulled it down from a repository, started a container and passed the command to display a welcome message.

As you can see, when an image is reused, if it is up to date, Docker uses your local copy and starts a microservice passing it the desired command.

# **** docker run -i -t (3) ****
$ docker run -i -t debian /bin/bash
root@683155bd7506:/# whoami
root

root@683155bd7506:/# echo "This is a test"
This is a test

root@683155bd7506:/# echo "How are you?"
How are you?

root@683155bd7506:/# whoami
root

root@683155bd7506:/# echo "How are you?"
How are you?

root@b2d13846a6a4:/# uname -a
Linux b2d13846a6a4 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 GNU/Linux

root@b2d13846a6a4:/# exit
exit
$ 

We can also start a shell in a new microservice. In this case we used the Debian image and asked Docker to keep STDIN open to run an interactive session using a TTY interface. You can get help on Docker commands by entering “Docker help” or “Docker help run” at the command prompt. This is illustrated in the following screen capture:

# **** docker help (4) ****
$ docker help run

Usage:	docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Run a command in a new container

Options:
      --add-host list                  Add a custom host-to-IP mapping (host:ip)
  -a, --attach list                    Attach to STDIN, STDOUT or STDERR
      --blkio-weight uint16            Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)
      --blkio-weight-device list       Block IO weight (relative device weight) (default [])
      --cap-add list                   Add Linux capabilities
      --cap-drop list                  Drop Linux capabilities
      --cgroup-parent string           Optional parent cgroup for the container
      --cidfile string                 Write the container ID to the file
      --cpu-period int                 Limit CPU CFS (Completely Fair Scheduler) period
      --cpu-quota int                  Limit CPU CFS (Completely Fair Scheduler) quota
      --cpu-rt-period int              Limit CPU real-time period in microseconds
      --cpu-rt-runtime int             Limit CPU real-time runtime in microseconds
  -c, --cpu-shares int                 CPU shares (relative weight)
      --cpus decimal                   Number of CPUs
      --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)
  -d, --detach                         Run container in background and print container ID
      --detach-keys string             Override the key sequence for detaching a container
      --device list                    Add a host device to the container
      --device-cgroup-rule list        Add a rule to the cgroup allowed devices list
      --device-read-bps list           Limit read rate (bytes per second) from a device (default [])
      --device-read-iops list          Limit read rate (IO per second) from a device (default [])
      --device-write-bps list          Limit write rate (bytes per second) to a device (default [])
      --device-write-iops list         Limit write rate (IO per second) to a device (default [])
      --disable-content-trust          Skip image verification (default true)
      --dns list                       Set custom DNS servers
      --dns-option list                Set DNS options
      --dns-search list                Set custom DNS search domains
      --entrypoint string              Overwrite the default ENTRYPOINT of the image
  -e, --env list                       Set environment variables
      --env-file list                  Read in a file of environment variables
      --expose list                    Expose a port or a range of ports
      --group-add list                 Add additional groups to join
      --health-cmd string              Command to run to check health
      --health-interval duration       Time between running the check (ms|s|m|h) (default 0s)
      --health-retries int             Consecutive failures needed to report unhealthy
      --health-start-period duration   Start period for the container to initialize before starting health-retries countdown
                                       (ms|s|m|h) (default 0s)
      --health-timeout duration        Maximum time to allow one check to run (ms|s|m|h) (default 0s)
      --help                           Print usage
  -h, --hostname string                Container host name
      --init                           Run an init inside the container that forwards signals and reaps processes
  -i, --interactive                    Keep STDIN open even if not attached
      --ip string                      IPv4 address (e.g., 172.30.100.104)
      --ip6 string                     IPv6 address (e.g., 2001:db8::33)
      --ipc string                     IPC mode to use
      --isolation string               Container isolation technology
      --kernel-memory bytes            Kernel memory limit
  -l, --label list                     Set meta data on a container
      --label-file list                Read in a line delimited file of labels
      --link list                      Add link to another container
      --link-local-ip list             Container IPv4/IPv6 link-local addresses
      --log-driver string              Logging driver for the container
      --log-opt list                   Log driver options
      --mac-address string             Container MAC address (e.g., 92:d0:c6:0a:29:33)
  -m, --memory bytes                   Memory limit
      --memory-reservation bytes       Memory soft limit
      --memory-swap bytes              Swap limit equal to memory plus swap: '-1' to enable unlimited swap
      --memory-swappiness int          Tune container memory swappiness (0 to 100) (default -1)
      --mount mount                    Attach a filesystem mount to the container
      --name string                    Assign a name to the container
      --network string                 Connect a container to a network (default "default")
      --network-alias list             Add network-scoped alias for the container
      --no-healthcheck                 Disable any container-specified HEALTHCHECK
      --oom-kill-disable               Disable OOM Killer
      --oom-score-adj int              Tune host's OOM preferences (-1000 to 1000)
      --pid string                     PID namespace to use
      --pids-limit int                 Tune container pids limit (set -1 for unlimited)
      --privileged                     Give extended privileges to this container
  -p, --publish list                   Publish a container's port(s) to the host
  -P, --publish-all                    Publish all exposed ports to random ports
      --read-only                      Mount the container's root filesystem as read only
      --restart string                 Restart policy to apply when a container exits (default "no")
      --rm                             Automatically remove the container when it exits
      --runtime string                 Runtime to use for this container
      --security-opt list              Security Options
      --shm-size bytes                 Size of /dev/shm
      --sig-proxy                      Proxy received signals to the process (default true)
      --stop-signal string             Signal to stop a container (default "SIGTERM")
      --stop-timeout int               Timeout (in seconds) to stop a container
      --storage-opt list               Storage driver options for the container
      --sysctl map                     Sysctl options (default map[])
      --tmpfs list                     Mount a tmpfs directory
  -t, --tty                            Allocate a pseudo-TTY
      --ulimit ulimit                  Ulimit options (default [])
  -u, --user string                    Username or UID (format: <name|uid>[:<group|gid>])
      --userns string                  User namespace to use
      --uts string                     UTS namespace to use
  -v, --volume list                    Bind mount a volume
      --volume-driver string           Optional volume driver for the container
      --volumes-from list              Mount volumes from the specified container(s)
  -w, --workdir string                 Working directory inside the container

In the next screen capture we run a container. We interact with the bash shell and move the /bin folder to the /basket folder. Let’s see what happens and what can we find about what we did.

# **** create and damage a container and inspect it (5) ****
$ docker run -h container -i -t debian /bin/bash
root@container:/# uname -a
Linux container 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 GNU/Linux
root@container:/# mv /bin /basket
root@container:/# ls
bash: ls: command not found
root@container:/# 


$ docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
ed6681b6bef7        debian              "/bin/bash"         12 minutes ago      Up 12 minutes                           happy_driscoll


$ docker inspect happy_driscoll
[
    {
        "Id": "ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018",
        "Created": "2019-02-22T20:18:05.535986117Z",
        "Path": "/bin/bash",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 22645,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2019-02-22T20:18:06.313357844Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:1b3ec9d977fb413627aca6244b27538013905167db25702a500474616ac2e3c8",
        "ResolvConfPath": "/var/lib/docker/containers/ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018/hostname",
        "HostsPath": "/var/lib/docker/containers/ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018/hosts",
        "LogPath": "/var/lib/docker/containers/ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018/ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018-json.log",
        "Name": "/happy_driscoll",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "shareable",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/4d257caf5eb1e56c3e243f84b84071274d070cdc875b55e3e5392404b8cec702-init/diff:/var/lib/docker/overlay2/42c4b2ba5b2f938f3663f21d1d91c7882056d90502147ffafdd005a1d596bf5d/diff",
                "MergedDir": "/var/lib/docker/overlay2/4d257caf5eb1e56c3e243f84b84071274d070cdc875b55e3e5392404b8cec702/merged",
                "UpperDir": "/var/lib/docker/overlay2/4d257caf5eb1e56c3e243f84b84071274d070cdc875b55e3e5392404b8cec702/diff",
                "WorkDir": "/var/lib/docker/overlay2/4d257caf5eb1e56c3e243f84b84071274d070cdc875b55e3e5392404b8cec702/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "container",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "debian",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "f9c32d774e2b20878fec78d24b0cfb0b7fad4f0530b8f9d1ea2a712fde22b9ff",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/f9c32d774e2b",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "a2a4afefd1cfb6dc751930bb793d3fc230ade6d921d4a7f547fe3d786ce0a80d",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "1bd33f685f4a0b5dc59abb4ac9c4582158c634f0d8dcacc4f297d123d457c249",
                    "EndpointID": "a2a4afefd1cfb6dc751930bb793d3fc230ade6d921d4a7f547fe3d786ce0a80d",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]


$ docker inspect happy_driscoll | grep "IPAddress"
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",


$ ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.088 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.043 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.036 ms
64 bytes from 172.17.0.2: icmp_seq=5 ttl=64 time=0.054 ms
64 bytes from 172.17.0.2: icmp_seq=6 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=7 ttl=64 time=0.036 ms
64 bytes from 172.17.0.2: icmp_seq=8 ttl=64 time=0.050 ms
64 bytes from 172.17.0.2: icmp_seq=9 ttl=64 time=0.045 ms
64 bytes from 172.17.0.2: icmp_seq=10 ttl=64 time=0.040 ms
64 bytes from 172.17.0.2: icmp_seq=11 ttl=64 time=0.047 ms
^C
--- 172.17.0.2 ping statistics ---
11 packets transmitted, 11 received, 0% packet loss, time 9999ms
rtt min/avg/max/mdev = 0.036/0.050/0.088/0.016 ms


$ docker inspect happy_driscoll | grep "Hostname"
        "HostnamePath": "/var/lib/docker/containers/ed6681b6bef74239d9c953adda98cb184902bc6b8bee7d6ecbc91a54e4364018/hostname",
            "Hostname": "container",


$ docker diff happy_driscoll   
A /basket
A /basket/zfgrep
A /basket/gunzip
A /basket/ping6
A /basket/zcmp
A /basket/grep
A /basket/su
A /basket/dd
A /basket/dnsdomainname
A /basket/echo
A /basket/tar
A /basket/wdctl
A /basket/chmod
A /basket/findmnt
A /basket/more
A /basket/egrep
A /basket/run-parts
A /basket/zdiff
A /basket/sleep
A /basket/zless
A /basket/chown
A /basket/cp
A /basket/gzexe
A /basket/touch
A /basket/zmore
A /basket/domainname
A /basket/mount
A /basket/tailf
A /basket/rbash
A /basket/vdir
A /basket/znew
A /basket/stty
A /basket/zforce
A /basket/df
A /basket/ip
A /basket/sed
A /basket/ping
A /basket/pwd
A /basket/true
A /basket/ypdomainname
A /basket/mv
A /basket/nisdomainname
A /basket/pidof
A /basket/readlink
A /basket/zgrep
A /basket/cat
A /basket/dir
A /basket/false
A /basket/sh.distrib
A /basket/ss
A /basket/tempfile
A /basket/uncompress
A /basket/zcat
A /basket/dash
A /basket/dmesg
A /basket/mktemp
A /basket/mountpoint
A /basket/rmdir
A /basket/which
A /basket/chgrp
A /basket/gzip
A /basket/hostname
A /basket/uname
A /basket/zegrep
A /basket/lsblk
A /basket/rm
A /basket/sync
A /basket/ls
A /basket/mkdir
A /basket/sh
A /basket/umount
A /basket/fgrep
A /basket/ln
A /basket/login
A /basket/bash
A /basket/date
A /basket/mknod
D /bin

We definitely rendered unusable the microservice. After we exit the shell we check if the microservice is running. It seems it is still around. We can now inspect it and get a large amount of information as we can see in the output of the docker inspect command. As we get more familiar we can specify what we need by piping the “inspect” command to grep. In this case we are interested in the IP.

Once we get the IP, we are able to ping it from our console. We can also get the host name used by our container.

If we use the “docker diff” command we can see that some folders were appended “A” to the file system and one was deleted “D”. These are the results of the mv command we issued.

# **** exit the shell in the container (6) ****
root@container:/# exit
exit


$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES


$ docker rm happy_driscoll
happy_driscoll

In the last screen capture we exit from the shell of the container. We check that we do not have containers running using the “docker ps” command and we remove the happy_driscoll container. The unique name comes from Docker. If you do not assign a name to a container, Docker does it for you.

# **** remove exited containers (7)****
$ docker rm -v $(docker ps -aq -f status=exited)
c3dbb5f7a9c3
b2d13846a6a4
683155bd7506
0ff06b0179f0
b048c489380e
e30e366505dc
004c767ea207
2d11d4d477f9
ed7b0d2e2005
06cbab8bc22a
359981fceeac
44cf60a90c60
7e54f3a173a8
ae502e0268d9
5824ee5dbae0
b157a29fe4c1
f9568b4820ce
67de28eba86a
1d6951788ebf
367c20bef351
fef40a90b975
488b4ff9532f
ef1a4e5ba280
403044466fef
02453c26151c
882a3bcaf9a5
385bdf2674da
b9ba5e3ad4dd
33158afa30fa


$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
$

When you work with containers, you may end up with dozens which acted as a step in a long journey. In addition we just checked if we have containers running. It seems that all is well and clean.

Next we are going to create a microservice cowsay that will display some text defined by the user or a quote. You can download and install the two parts in your computer or you can just build a microservice and use it as needed.

# **** cowsay (8) ****
$ docker run -it --name cowsay --hostname cowsay debian bash
root@cowsay:/# apt-get update
Get:1 http://security.debian.org stretch/updates InRelease [94.3 kB]
Ign:2 http://deb.debian.org/debian stretch InRelease      
Get:3 http://security.debian.org stretch/updates/main amd64 Packages [592 kB]
Get:4 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB]
Get:5 http://deb.debian.org/debian stretch Release [118 kB]
Get:6 http://deb.debian.org/debian stretch-updates/main amd64 Packages [12.3 kB]
Get:7 http://deb.debian.org/debian stretch Release.gpg [2434 B]
Get:8 http://deb.debian.org/debian stretch/main amd64 Packages [9478 kB]
Fetched 10.4 MB in 2s (4062 kB/s)   
Reading package lists... Done
root@cowsay:/# 

We created a new microservice using the Debian image which we did not have in our local machine. We are running are interactive session and using the bash shell.

# **** apt-get (9) ****
root@cowsay:/# apt-get
apt 1.4.8 (amd64)
Usage: apt-get [options] command
       apt-get [options] install|remove pkg1 [pkg2 ...]
       apt-get [options] source pkg1 [pkg2 ...]

apt-get is a command line interface for retrieval of packages
and information about them from authenticated sources and
for installation, upgrade and removal of packages together
with their dependencies.

Most used commands:
  update - Retrieve new lists of packages
  upgrade - Perform an upgrade
  install - Install new packages (pkg is libc6 not libc6.deb)
  remove - Remove packages
  purge - Remove packages and config files
  autoremove - Remove automatically all unused packages
  dist-upgrade - Distribution upgrade, see apt-get(8)
  dselect-upgrade - Follow dselect selections
  build-dep - Configure build-dependencies for source packages
  clean - Erase downloaded archive files
  autoclean - Erase old downloaded archive files
  check - Verify that there are no broken dependencies
  source - Download source archives
  download - Download the binary package into the current directory
  changelog - Download and display the changelog for the given package

See apt-get(8) for more information about the available commands.
Configuration options and syntax is detailed in apt.conf(5).
Information about how to configure sources can be found in sources.list(5).
Package and version choices can be expressed via apt_preferences(5).
Security details are available in apt-secure(8).
                                        This APT has Super Cow Powers.
root@cowsay:/# 

The apt-get is a package handling utility found in most Linux distributions. It happens that it is available in the Debian distribution. It is quite simple to use and we will in the next screen capture.

# **** install cowsay and fortune (10) ****
root@cowsay:~# apt-get install -y cowsay fortune
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Note, selecting 'fortune-mod' instead of 'fortune'
The following additional packages will be installed:
  cowsay-off fortunes-min libgdbm3 libperl5.24 librecode0 libtext-charwidth-perl perl perl-base perl-modules-5.24 rename
Suggested packages:
  filters fortunes x11-utils bsdmainutils perl-doc libterm-readline-gnu-perl | libterm-readline-perl-perl make
The following NEW packages will be installed:
  cowsay cowsay-off fortune-mod fortunes-min libgdbm3 libperl5.24 librecode0 libtext-charwidth-perl perl perl-modules-5.24 rename
The following packages will be upgraded:
  perl-base
1 upgraded, 11 newly installed, 0 to remove and 27 not upgraded.
Need to get 8522 kB of archives.
After this operation, 42.6 MB of additional disk space will be used.
Get:1 http://deb.debian.org/debian stretch/main amd64 perl-base amd64 5.24.1-3+deb9u5 [1345 kB]
Get:2 http://deb.debian.org/debian stretch/main amd64 perl-modules-5.24 all 5.24.1-3+deb9u5 [2722 kB]
Get:3 http://deb.debian.org/debian stretch/main amd64 libgdbm3 amd64 1.8.3-14 [30.0 kB]
Get:4 http://deb.debian.org/debian stretch/main amd64 libperl5.24 amd64 5.24.1-3+deb9u5 [3501 kB]
Get:5 http://deb.debian.org/debian stretch/main amd64 perl amd64 5.24.1-3+deb9u5 [219 kB]
Get:6 http://deb.debian.org/debian stretch/main amd64 libtext-charwidth-perl amd64 0.04-7+b5 [9870 B]
Get:7 http://deb.debian.org/debian stretch/main amd64 cowsay all 3.03+dfsg2-3 [20.1 kB]
Get:8 http://deb.debian.org/debian stretch/main amd64 cowsay-off all 3.03+dfsg2-3 [7816 B]
Get:9 http://deb.debian.org/debian stretch/main amd64 librecode0 amd64 3.6-23 [532 kB]
Get:10 http://deb.debian.org/debian stretch/main amd64 fortune-mod amd64 1:1.99.1-7+b1 [49.5 kB]
Get:11 http://deb.debian.org/debian stretch/main amd64 fortunes-min all 1:1.99.1-7 [74.3 kB]
Get:12 http://deb.debian.org/debian stretch/main amd64 rename all 0.20-4 [12.5 kB]
Fetched 8522 kB in 1s (6771 kB/s) 
debconf: delaying package configuration, since apt-utils is not installed
(Reading database ... 6487 files and directories currently installed.)
Preparing to unpack .../perl-base_5.24.1-3+deb9u5_amd64.deb ...
Unpacking perl-base (5.24.1-3+deb9u5) over (5.24.1-3+deb9u2) ...
Setting up perl-base (5.24.1-3+deb9u5) ...
Selecting previously unselected package perl-modules-5.24.
(Reading database ... 6487 files and directories currently installed.)
Preparing to unpack .../00-perl-modules-5.24_5.24.1-3+deb9u5_all.deb ...
Unpacking perl-modules-5.24 (5.24.1-3+deb9u5) ...
Selecting previously unselected package libgdbm3:amd64.
Preparing to unpack .../01-libgdbm3_1.8.3-14_amd64.deb ...
Unpacking libgdbm3:amd64 (1.8.3-14) ...
Selecting previously unselected package libperl5.24:amd64.
Preparing to unpack .../02-libperl5.24_5.24.1-3+deb9u5_amd64.deb ...
Unpacking libperl5.24:amd64 (5.24.1-3+deb9u5) ...
Selecting previously unselected package perl.
Preparing to unpack .../03-perl_5.24.1-3+deb9u5_amd64.deb ...
Unpacking perl (5.24.1-3+deb9u5) ...
Selecting previously unselected package libtext-charwidth-perl.
Preparing to unpack .../04-libtext-charwidth-perl_0.04-7+b5_amd64.deb ...
Unpacking libtext-charwidth-perl (0.04-7+b5) ...
Selecting previously unselected package cowsay.
Preparing to unpack .../05-cowsay_3.03+dfsg2-3_all.deb ...
Unpacking cowsay (3.03+dfsg2-3) ...
Selecting previously unselected package cowsay-off.
Preparing to unpack .../06-cowsay-off_3.03+dfsg2-3_all.deb ...
Unpacking cowsay-off (3.03+dfsg2-3) ...
Selecting previously unselected package librecode0:amd64.
Preparing to unpack .../07-librecode0_3.6-23_amd64.deb ...
Unpacking librecode0:amd64 (3.6-23) ...
Selecting previously unselected package fortune-mod.
Preparing to unpack .../08-fortune-mod_1%3a1.99.1-7+b1_amd64.deb ...
Unpacking fortune-mod (1:1.99.1-7+b1) ...
Selecting previously unselected package fortunes-min.
Preparing to unpack .../09-fortunes-min_1%3a1.99.1-7_all.deb ...
Unpacking fortunes-min (1:1.99.1-7) ...
Selecting previously unselected package rename.
Preparing to unpack .../10-rename_0.20-4_all.deb ...
Unpacking rename (0.20-4) ...
Setting up perl-modules-5.24 (5.24.1-3+deb9u5) ...
Setting up libgdbm3:amd64 (1.8.3-14) ...
Setting up libperl5.24:amd64 (5.24.1-3+deb9u5) ...
Setting up fortunes-min (1:1.99.1-7) ...
Setting up perl (5.24.1-3+deb9u5) ...
update-alternatives: using /usr/bin/prename to provide /usr/bin/rename (rename) in auto mode
Processing triggers for libc-bin (2.24-11+deb9u1) ...
Setting up librecode0:amd64 (3.6-23) ...
Setting up libtext-charwidth-perl (0.04-7+b5) ...
Setting up fortune-mod (1:1.99.1-7+b1) ...
Setting up cowsay (3.03+dfsg2-3) ...
Setting up cowsay-off (3.03+dfsg2-3) ...
Setting up rename (0.20-4) ...
update-alternatives: using /usr/bin/file-rename to provide /usr/bin/rename (rename) in auto mode
Processing triggers for libc-bin (2.24-11+deb9u1) ...


root@cowsay:~# ls -l /usr/games
total 36
-rwxr-xr-x. 1 root root  4664 Jan 16  2017 cowsay
lrwxrwxrwx. 1 root root     6 Jan 16  2017 cowthink -> cowsay
-rwxr-xr-x. 1 root root 27320 Aug 15  2013 fortune


root@cowsay:~# /usr/games/fortune
You attempt things that you do not even plan because of your extreme stupidity.
root@cowsay:~# /usr/games/fortune
You're ugly and your mother dresses you funny.
root@cowsay:~# /usr/games/fortune
You are capable of planning your future.

We need to install the cowsay package which displays a cow with a caption. The text for the caption can be piped in from the console (e.g., echo “Hello John”) or from fortune into cowsay. It is interesting to see the end result.

We can see that cowsay and fortune were installed in the microservice in the /usr/games folder. If you have not used fortune it displays quotes. The program was available on UNIX. Most developers at work including myself, used to display a quote when you would log into your machine.

# **** echo and fortune | cowsay (11) ****
root@cowsay:~# echo "Hello John" | /usr/games/cowsay
 ____________
< Hello John >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
root@cowsay:~# /usr/games/fortune | /usr/games/cowsay
 ________________________________________
< Your ignorance cramps my conversation. >
 ----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
root@cowsay:~# 

We echoed “Hello John” and piped it to cowsay. A cow is displayed with a caption holding the text you entered. In the second invocation we just piped a fortune into cowsay. Pretty nifty I would say.

# **** save the cowsay image (12) ****
root@cowsay:~# exit          
exit

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
3c320cc69f32        debian              "bash"              25 minutes ago      Exited (1) 15 seconds ago                       cowsay

$ pwd                            
/home/johncanessa/Docker

$ docker commit cowsay test/cowsayimage
sha256:e5a22e4037288055708bdd64be834c2ef6e4ebe972964fa6109f10d222d5429b

We exit the microservice. We verify that the microservice has exited using the “docker ps” command. I checked our current directory and then used “docker commit” to save the cowsay image into our local repository (not current folder).

# **** run the saved image (13) ****
$ docker run test/cowsayimage /usr/games/cowsay "Hello John !!!"
 ________________
< Hello John !!! >
 ----------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


# **** list or images ****
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
test/cowsayimage    latest              e5a22e403728        4 minutes ago       164MB
ubuntu              latest              47b19964fb50        2 weeks ago         88.1MB
debian              latest              1b3ec9d977fb        12 months ago       100MB
centos              latest              ff426288ea90        13 months ago       207MB

We just run the container we created. It seems to work. In addition we use the “docker images” command to list the images we have locally. The test/cowsayimage is there.

# **** run cowsay by image ID (14) ****
$ docker run e5a22e403728 /usr/games/cowsay "Hello"
 _______
< Hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

We ran the image in a microservice but we called it by ID instead of by name.

# **** build image using Dockerfile (15) ****
$ mkdir cowsay
$ cd cowsay/
$ touch Dockerfile
$ ls -al
total 0
drwxr-xr-x. 2 johncanessa johncanessa  24 Feb 22 15:37 .
drwxr-xr-x. 3 johncanessa johncanessa 102 Feb 22 15:37 ..
-rw-r--r--. 1 johncanessa johncanessa   0 Feb 22 15:37 Dockerfile

In this screen capture we make a directory and move to it. We then create an empty file named Dockerfile.

# **** edit Docker file as follows (16) ****
$ cat Dockerfile
FROM debian:wheezy
RUN apt-get update && apt-get install -y cowsay fortune

We edited the Dockerfile and added two lines. Each line represents a command. In the first line we tell Docker to use a fresh copy of the Debian image labeled wheezy. In the second we want to install cowsay and fortune.

# **** build the image (17) ****
$ pwd
/home/johncanessa/Docker/cowsay
$ ls
Dockerfile
$ docker build -t test/cowsay-dockerfile .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM debian:wheezy
 ---> 8c971baff57b
Step 2/2 : RUN apt-get update && apt-get install -y cowsay fortune
 ---> Running in fb3769c00c90
Get:1 http://deb.debian.org wheezy Release.gpg [2373 B]
Get:2 http://deb.debian.org wheezy-updates Release.gpg [1601 B]
Get:3 http://deb.debian.org wheezy Release [191 kB]
Get:4 http://deb.debian.org wheezy-updates Release [155 kB]
Get:5 http://deb.debian.org wheezy/main amd64 Packages [7634 kB]
Get:6 http://deb.debian.org wheezy-updates/main amd64 Packages [7481 B]
Get:7 http://security.debian.org wheezy/updates Release.gpg [1601 B]
Get:8 http://security.debian.org wheezy/updates Release [52.3 kB]
Get:9 http://security.debian.org wheezy/updates/main amd64 Packages [743 kB]
Fetched 8788 kB in 2min 6s (69.5 kB/s)
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  fortunes-min ifupdown libclass-isa-perl libgdbm3 librecode0 libswitch-perl
  netbase perl perl-modules
Suggested packages:
  filters fortunes x11-utils bsdmainutils isc-dhcp-client dhcp-client ppp
  rdnssd net-tools perl-doc libterm-readline-gnu-perl
  libterm-readline-perl-perl make libpod-plainer-perl
The following NEW packages will be installed:
  cowsay fortune-mod fortunes-min ifupdown libclass-isa-perl libgdbm3
  librecode0 libswitch-perl netbase perl perl-modules
0 upgraded, 11 newly installed, 0 to remove and 0 not upgraded.
Need to get 8965 kB of archives.
After this operation, 34.7 MB of additional disk space will be used.
Get:1 http://deb.debian.org/debian/ wheezy/main ifupdown amd64 0.7.8 [64.9 kB]
Get:2 http://deb.debian.org/debian/ wheezy/main libgdbm3 amd64 1.8.3-11 [46.9 kB]
Get:3 http://deb.debian.org/debian/ wheezy/main librecode0 amd64 3.6-20 [779 kB]
Get:4 http://deb.debian.org/debian/ wheezy/main netbase all 5.0 [20.1 kB]
Get:5 http://deb.debian.org/debian/ wheezy/main libclass-isa-perl all 0.36-3 [12.3 kB]
Get:6 http://deb.debian.org/debian/ wheezy/main libswitch-perl all 2.16-2 [21.0 kB]
Get:7 http://deb.debian.org/debian/ wheezy/main cowsay all 3.03+dfsg1-4 [21.9 kB]
Get:8 http://deb.debian.org/debian/ wheezy/main fortune-mod amd64 1:1.99.1-4 [51.3 kB]
Get:9 http://deb.debian.org/debian/ wheezy/main fortunes-min all 1:1.99.1-4 [73.0 kB]
Get:10 http://security.debian.org/debian-security/ wheezy/updates/main perl-modules all 5.14.2-21+deb7u6 [3441 kB]
Get:11 http://security.debian.org/debian-security/ wheezy/updates/main perl amd64 5.14.2-21+deb7u6 [4434 kB]
debconf: delaying package configuration, since apt-utils is not installed
Fetched 8965 kB in 2min 0s (74.2 kB/s)
Selecting previously unselected package ifupdown.
(Reading database ... 6755 files and directories currently installed.)
Unpacking ifupdown (from .../ifupdown_0.7.8_amd64.deb) ...
Selecting previously unselected package libgdbm3:amd64.
Unpacking libgdbm3:amd64 (from .../libgdbm3_1.8.3-11_amd64.deb) ...
Selecting previously unselected package librecode0:amd64.
Unpacking librecode0:amd64 (from .../librecode0_3.6-20_amd64.deb) ...
Selecting previously unselected package netbase.
Unpacking netbase (from .../archives/netbase_5.0_all.deb) ...
Selecting previously unselected package libclass-isa-perl.
Unpacking libclass-isa-perl (from .../libclass-isa-perl_0.36-3_all.deb) ...
Selecting previously unselected package perl-modules.
Unpacking perl-modules (from .../perl-modules_5.14.2-21+deb7u6_all.deb) ...
Selecting previously unselected package perl.
Unpacking perl (from .../perl_5.14.2-21+deb7u6_amd64.deb) ...
Selecting previously unselected package libswitch-perl.
Unpacking libswitch-perl (from .../libswitch-perl_2.16-2_all.deb) ...
Selecting previously unselected package cowsay.
Unpacking cowsay (from .../cowsay_3.03+dfsg1-4_all.deb) ...
Selecting previously unselected package fortune-mod.
Unpacking fortune-mod (from .../fortune-mod_1%3a1.99.1-4_amd64.deb) ...
Selecting previously unselected package fortunes-min.
Unpacking fortunes-min (from .../fortunes-min_1%3a1.99.1-4_all.deb) ...
Setting up ifupdown (0.7.8) ...
Creating /etc/network/interfaces.
Setting up libgdbm3:amd64 (1.8.3-11) ...
Setting up librecode0:amd64 (3.6-20) ...
Setting up netbase (5.0) ...
Setting up libclass-isa-perl (0.36-3) ...
Setting up fortune-mod (1:1.99.1-4) ...
Setting up fortunes-min (1:1.99.1-4) ...
Setting up libswitch-perl (2.16-2) ...
Setting up perl-modules (5.14.2-21+deb7u6) ...
Setting up perl (5.14.2-21+deb7u6) ...
update-alternatives: using /usr/bin/prename to provide /usr/bin/rename (rename) in auto mode
Setting up cowsay (3.03+dfsg1-4) ...
Removing intermediate container fb3769c00c90
 ---> 2ea885a81c30
Successfully built 2ea885a81c30
Successfully tagged test/cowsay-dockerfile:latest

Now we are ready to run “docker build” to create an image as specified by the Dockerfile we just edited.

# **** list images (18) ****
$ docker images             
REPOSITORY               TAG                 IMAGE ID            CREATED              SIZE
test/cowsay-dockerfile   latest              2ea885a81c30        About a minute ago   131MB
test/cowsayimage         latest              e5a22e403728        2 days ago           164MB
ubuntu                   latest              47b19964fb50        2 weeks ago          88.1MB
debian                   wheezy              8c971baff57b        2 weeks ago          88.3MB
debian                   latest              1b3ec9d977fb        12 months ago        100MB
centos                   latest              ff426288ea90        13 months ago        207MB

We can see by using “docker images” that we have created in our local repository the image test/cowsay-dockerfile.

# **** running image test/cowsay-dockerfile (19) ****
$ docker run test/cowsay-dockerfile /usr/games/cowsay "Hello John!!!"
 _______________
< Hello John!!! >
 ---------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

We can test the new image and cowsay displays our message.

# **** determine which Union File Systems is used by Docker (20) ****
$ docker info | grep "Storage Driver"
Storage Driver: overlay2

Docker uses a union file system to build images. Different file systems have different features. In our case we seem to be using Overlay2. Please do not change the union file system unless you have experience with the different features available.

# **** add ENTRYPOINT to the Dockerfile (21) ****
$ cat Dockerfile
FROM debian:wheezy
RUN apt-get update && apt-get install -y cowsay fortune
ENTRYPOINT ["/usr/games/cowsay"]

In the previous command we specified which program should be executed. We can add such information into Dockerfile.

# **** build the image (22) ****
$ docker build -t test/cowsay-dockerfile .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM debian:wheezy
 ---> 8c971baff57b
Step 2/3 : RUN apt-get update && apt-get install -y cowsay fortune
 ---> Using cache
 ---> 8cd4c4c8cb47
Step 3/3 : ENTRYPOINT ["/usr/games/cowsay"]
 ---> Running in 5ebc18f7e3e4
Removing intermediate container 5ebc18f7e3e4
 ---> 4504a08e5228
Successfully built 4504a08e5228
Successfully tagged test/cowsay-dockerfile:latest

$ docker images                           
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
test/cowsay-dockerfile   latest              4504a08e5228        18 seconds ago      131MB
test/cowsayimage         latest              e5a22e403728        2 days ago          164MB
ubuntu                   latest              47b19964fb50        2 weeks ago         88.1MB
debian                   wheezy              8c971baff57b        2 weeks ago         88.3MB
debian                   latest              1b3ec9d977fb        12 months ago       100MB
centos                   latest              ff426288ea90        13 months ago       207MB

We rebuild the image. The image ID has changed.

# **** run the image (23) ****
$ docker run test/cowsay-dockerfile "Hi John, again !!!!"
 _____________________
< Hi John, again !!!! >
 ---------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

We can now run the updated image by just passing the string we wish to display.

# **** entrypoint.sh (24) ****
$ cat entrypoint.sh
#!/bin/bash
if [ $# -eq 0];
then
	/usr/games/fortune /user/games/cowsay
else
	/usr/games/cowsay "$@"
fi

The issue with our current solution is that if we do not pass a string, cowsay will display a blank caption. We can solve this by first creating the entrypoint.sh file.

# **** change mode to be able to execute it (25) ****
$ chmod +x entrypoint.sh
$ ls -l entrypoint.sh
-rwxr-xr-x. 1 johncanessa johncanessa 104 Feb 25 13:39 entrypoint.sh

We change the mode to be able to execute it.

# **** edit Dockerfile (26) ****
$ cat Dockerfile
FROM debian:wheezy

RUN apt-get update && apt-get install -y cowsay fortune

COPY entrypoint.sh /
#COPY entrypoint.ksh /

ENTRYPOINT ["/entrypoint.sh"]
#ENTRYPOINT ["/entrypoint.ksh"]

We update the Dockerfile. I left a couple lines commented out. The reason was to test what if I write a Korn shell script but the Debian image does not contain the Korn shell. You can comment and edit the appropriate lines and see the results. They appear to be rather cryptic.

# **** buid and execute the image (27) ****
$ docker run test/cowsay-dockerfile          
standard_init_linux.go:207: exec user process caused "no such file or directory"

The following capture illustrates that the Debian image does not include the Korn shell.

# **** no ksh in debian image (28) ****
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
root@a0ebfcbcae81:/# 

Now that all is working (make sure you are not using the Korn shell) let’s login into Docker Hub.

# **** log into Docker Hub (29) ****
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: johncanessa
Password: 
WARNING! Your password will be stored unencrypted in /home/johncanessa/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

You need to register for an account for free. This is true if your repository is public. I believe there is a charge for private ones.

# **** build the image for the repository (30) ****
$ docker build -t johncanessa/cowsay .
Sending build context to Docker daemon   5.12kB
Step 1/5 : FROM debian:wheezy
 ---> 8c971baff57b
Step 2/5 : MAINTAINER John Canessa <john.canessa@gmail.com>
 ---> Using cache
 ---> 531b2b19d881
Step 3/5 : RUN apt-get update && apt-get install -y cowsay fortune
 ---> Using cache
 ---> 0273e5ec1af2
Step 4/5 : COPY entrypoint.sh /
 ---> Using cache
 ---> bbde42c4599c
Step 5/5 : ENTRYPOINT ["/entrypoint.sh"]
 ---> Using cache
 ---> 30d74744acb1
Successfully built 30d74744acb1
Successfully tagged johncanessa/cowsay:latest

We build an image for the repository at Docker Hub.

# **** push the image to the repository (31) ****
$ docker push johncanessa/cowsay      
The push refers to repository [docker.io/johncanessa/cowsay]
c360ce0549f2: Pushed 
13025371f5d4: Pushed 
0e9f3c1de5b3: Mounted from library/debian 
latest: digest: sha256:98cdcc6365e9e61030bcaf035fa055d59007f3fd818861641a17dea0bffb1553 size: 948

Now that the image has been created we can push it.

# **** make a folder to pull the image (32) ****
$ mkdir temp
$ cd temp
$ pwd
/home/johncanessa/temp

$ docker pull johncanessa/cowsay
Using default tag: latest
latest: Pulling from johncanessa/cowsay
Digest: sha256:98cdcc6365e9e61030bcaf035fa055d59007f3fd818861641a17dea0bffb1553
Status: Image is up to date for johncanessa/cowsay:latest

$ ls -al
total 4
drwxr-xr-x.  2 johncanessa johncanessa    6 Feb 25 16:10 .
drwx------. 31 johncanessa johncanessa 4096 Feb 25 16:10 ..

We create a local folder to receive the image that we will pull from Docker Hub.  We then pull the image using “docker pull”. As we can see the image was not returned to a folder. As we will see in a few more screen captures, the image is placed in our local repository.

# **** remove all images (33) ****
$ docker rmi $(docker images -aq)
Untagged: johncanessa/cowsay:latest
Untagged: johncanessa/cowsay@sha256:98cdcc6365e9e61030bcaf035fa055d59007f3fd818861641a17dea0bffb1553
Deleted: sha256:30d74744acb1d957c9bb85ee7f6eaefcfff2a06ffec085e996a4be7fcb077426
Deleted: sha256:bbde42c4599cdcdb78c7d0043a139a1a6cdb77eaba8db61bd1627b1c54ec032c
Deleted: sha256:1d61e0855fafc29ae5c82f73dd3438914b3ab6a4d1b090ca84ec523df4a38ed9
Deleted: sha256:0273e5ec1af24eb4296454a47664da22cc8fdd0994ee1dce39329894a812d873
Deleted: sha256:b0e7648693826c9a5c145509dae3cd30851c25b02f1a0f95bf08456b013871c5
Deleted: sha256:531b2b19d8819bdad653d1a2ccfffc3fdbc97dc90596623b352aaaa1a4ec0161
Untagged: test/cowsay-dockerfile:latest
Deleted: sha256:46b203b2adb31ad517c532d0637cbe6311ff21b6945f4955a2d4c4b841753184
Deleted: sha256:1e1762b7d4276ded8e21a9bd3cea1ab681dbbee0f2957dea8cf16eeefe44feda
Deleted: sha256:ecebdf102966db54550d4319ad1fd48c1a6e6fed0f50d7e1b0ed5816326fa910
Deleted: sha256:a2881c3b00b34193dfa00f7af64bf7009326fa07172078628f1cf9a629d22f78
Deleted: sha256:e6bc48513b4a2c6aaa7e744538e01234569c59110082e5787f93b88117fa6cff
Deleted: sha256:1df45fbba7b4664acc84d6b2a150a9e5c9b7615752e2c2397f922cfe3a09cc59
Deleted: sha256:ced0b68c8a547e5eaaaa5ca1115d40ce149b1e7fa659511dbbc789c6b24053fe
Deleted: sha256:351a2d353e3158d7c524181d70be63bfb4fb16edde68bc72fb8d1fcc80607304
Deleted: sha256:91125e99f81e5efd6b0aef2ebff4ad9da577c0bc677b23720c64c75d4d140ddf
Deleted: sha256:e069aeea43ebee4fa7dbc3d2fdac93c73280cdb1b72880beaa3e0f348a1ad105
Deleted: sha256:0d39b6306a1b1468f57d01e40425c68e3ed956da26ccf3cb2505fe0822391420
Deleted: sha256:38b289fe568c6f15c7f59f69f731f3db24de75808525b511af15fada6e6272a7
Deleted: sha256:b90bfb8219e4408fc9a52b8bdf1e25f7068d975b99a8efa83255b7817ba89a0d
Deleted: sha256:cf6d00692eb772c7692abb3eab9431c48457afe9896e1e5694c106e09b24dc05
Deleted: sha256:83992f1037a6dfef2526d266c9e6a0e457e077c674bc6d5927960a1002b8f977
Deleted: sha256:1b6f3ceb73bfa9a71962706ae7e19eac87a6c7c289085895e20ca92e55615807
Deleted: sha256:d9fbbf8da9a96a4113890e7cf38c36df618db3edcce922c130507c6feadb57ad
Deleted: sha256:ec8a4ff13d1e73f2605a0ff0791c4fd33f7ac4f2a719348d58ea0e0c8dd6ebd3
Deleted: sha256:18e260d9c6687c3a8b1936c5015e84360600d1705ca6b5eefad9dd54804f0472
Deleted: sha256:8b0ddef44e8c400a33bb03393a8257010df7513fe8b65add5fb6f5ec819a8e7a
Deleted: sha256:f58efd7b7bf2b0f7ba37b96f383138370b323807b6c777319d06973bcfd42698
Deleted: sha256:8dadfd7bfc60c96adf26b307370e6a91256cd35cf210efb08355577016577870
Deleted: sha256:df2c7d3bbaa2443c2769ffebe12bc5b56020e096009ee88259f21c6d4d5a1614
Deleted: sha256:e57f146ec12b4450775d8699f52ad30549a5926c4c53b3fc4a795aec5d748a11
Deleted: sha256:cce26fa707586d937047fb3cf98a38103fc05f85af4543c24c78527c22687421
Deleted: sha256:b73f5a25818051c5eb6e4e1f17eee683973fac004872112e0f7d25f6538d31b0
Deleted: sha256:2c2e68719bb179679acd874d8006ba296701a9ebd3dc56222b9ff0bfb2616af5
Deleted: sha256:e582e3501440c7c3e94f5dd8e125c3efff2864b6f17353cbd14f0da925d756cb
Deleted: sha256:8cd4c4c8cb47b88c28a3a18b4c03577240f0f5416009210e270e29a2da364cc1
Deleted: sha256:12e2bafa888d534a124dca677f4c3ada2d888350b786a7784e7999a0123e0b74
Untagged: test/cowsayimage:latest
Deleted: sha256:e5a22e4037288055708bdd64be834c2ef6e4ebe972964fa6109f10d222d5429b
Deleted: sha256:0c2f0c6ac672bbfd4a9a31ebf1dd9c37d5fd991a62c822e1cd0b25607eb23324
Untagged: ubuntu:latest
Untagged: ubuntu@sha256:7a47ccc3bbe8a451b500d2b53104868b46d60ee8f5b35a24b41a86077c650210
Deleted: sha256:47b19964fb500f3158ae57f20d16d8784cc4af37c52c49d3b4f5bc5eede49541
Deleted: sha256:d4c69838355b876cd3eb0d92b4ef27b1839f5b094a4eb1ad2a1d747dd5d6088f
Deleted: sha256:1c29a32189d8f2738d0d99378dc0912c9f9d289b52fb698bdd6c1c8cd7a33727
Deleted: sha256:d801a12f6af7beff367268f99607376584d8b2da656dcd8656973b7ad9779ab4
Deleted: sha256:bebe7ce6215aee349bee5d67222abeb5c5a834bbeaa2f2f5d05363d9fd68db41
Untagged: debian:wheezy
Untagged: debian@sha256:62b635539e55e0ddadacb5ec5fb39ca1d89b8b0bd1b19e1fd516b7a0b2416137
Deleted: sha256:8c971baff57ba9c8cf39355e5b8e04981436bea82b28f1a55bf875d0f3d2ae06
Deleted: sha256:0e9f3c1de5b3836f41678653a8b16ebb3b1a2020cd1b0dc065b682a7a672b1f2
Untagged: debian:latest
Untagged: debian@sha256:4fcd8c0b6f5e3bd44a3e63be259fd0c038476d432953d449ef34aedf16def331
Deleted: sha256:1b3ec9d977fb413627aca6244b27538013905167db25702a500474616ac2e3c8
Deleted: sha256:8568818b1f7f534832b393c531edfcb4a30e7eb40b573e68fdea90358987231f
Untagged: centos:latest
Untagged: centos@sha256:2671f7a3eea36ce43609e9fe7435ade83094291055f1c96d9d1d1d7c0b986a5d
Deleted: sha256:ff426288ea903fcf8d91aca97460c613348f7a27195606b45f19ae91776ca23d
Deleted: sha256:e15afa4858b655f8a5da4c4a41e05b908229f6fab8543434db79207478511ff7
Error: No such image: bbde42c4599c
Error: No such image: 0273e5ec1af2
Error: No such image: 531b2b19d881
Error: No such image: 1e1762b7d427
Error: No such image: 1df45fbba7b4
Error: No such image: 91125e99f81e
Error: No such image: 38b289fe568c
Error response from daemon: conflict: unable to delete 83992f1037a6 (cannot be forced) - image has dependent child images
Error: No such image: ec8a4ff13d1e
Error: No such image: f58efd7b7bf2
Error: No such image: e57f146ec12b
Error: No such image: 2c2e68719bb1
Error: No such image: 8cd4c4c8cb47

$ docker images -a
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

Since we have been experimenting we have many images left behind. We use “docker rmi” to remove all images from our local repository. We have a clean local repository.

# **** log into repository (34) ****
$ docker login
Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /home/johncanessa/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

We login into the Docker Hub. Note you need to do this when you wish to use the Docker Hub repository.

# **** pull cowsay from the repository (35) ****
$ docker pull johncanessa/cowsay
Using default tag: latest
latest: Pulling from johncanessa/cowsay
2eaed095b90d: Pull complete 
bc1ad329fa69: Pull complete 
3783076db0a1: Pull complete 
Digest: sha256:98cdcc6365e9e61030bcaf035fa055d59007f3fd818861641a17dea0bffb1553
Status: Downloaded newer image for johncanessa/cowsay:latest

$ docker images     
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
johncanessa/cowsay   latest              30d74744acb1        17 hours ago        131MB

We now pull the johncanessa/cowsay image and verify that a copy is found in our local repo.

# **** run the image we just downloaded (36) ****
$ docker run johncanessa/cowsay
 ________________________________
< Tomorrow, you can be anywhere. >
 --------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

When we run the image without adding text, cowsay displays a fortune message. The issue has been solved. Note that we did not have to push it to Docker Hub to make it work. This was just a way to kill two birds with one stone.

Now we will move to the final example using Redis and will try additional Docker features.

# **** Redis (37) ****
https://en.wikipedia.org/wiki/Redis
a key-value (disctionary) store
written in ANCI C
Has been around since 2009

Now that we know something about Redis, we will start with an image that has Redis already installed.

# **** pull latest redis image (38) ****
$ docker pull redis
Using default tag: latest
latest: Pulling from library/redis
6ae821421a7d: Pull complete 
e3717477b42d: Pull complete 
8e70bf6cc2e6: Pull complete 
0f84ab76ce60: Pull complete 
0903bdecada2: Pull complete 
492876061fbd: Pull complete 
Digest: sha256:dd5b84ce536dffdcab79024f4df5485d010affa09e6c399b215e199a0dca38c4
Status: Downloaded newer image for redis:latest

$ docker images -a
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
johncanessa/cowsay   latest              30d74744acb1        22 hours ago        131MB
redis                latest              0f55cf3661e9        2 weeks ago         95MB

Using “docker pull” we get the Redis image. We then verify that it is in our local repository.

# **** run redis container in the background (39) ****
--detach                         Run container in background and print container ID
--name string                    Assign a name to the container

$ docker run --name myredis -d redis
6eba1f137463439f56a54c1de774aa40b2e5f3f71a7e1201642fefa7dfcdd58a

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
6eba1f137463        redis               "docker-entrypoint.s…"   40 seconds ago      Up 38 seconds       6379/tcp            myredis

We will now run “docker run” with a couple additional arguments. We then verify that the container is up and running.

# **** display output from the logs of the container (40) ****
$ docker logs myredis
1:C 26 Feb 2019 20:11:16.577 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 26 Feb 2019 20:11:16.577 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 26 Feb 2019 20:11:16.577 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 26 Feb 2019 20:11:16.579 * Running mode=standalone, port=6379.
1:M 26 Feb 2019 20:11:16.579 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 26 Feb 2019 20:11:16.579 # Server initialized
1:M 26 Feb 2019 20:11:16.579 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 26 Feb 2019 20:11:16.579 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 26 Feb 2019 20:11:16.579 * Ready to accept connections

By using the “docker logs” command we can display the contents of the log for the container in question. Of interest is the port=6379 message that indicates that Redis is listening on that port for commands. We could download into our computer system redis-cli and used it to communicate with the Redis server running in the container. That takes time and we might not need the Redis CLI for later use. How about using a second container to communicate with the Redis server?

# **** start new container (41) ****
--rm                             Automatically remove the container when it exits
-i                               Keep STDIN open even if not attached
-t                               Allocate a pseudo-TTY
--link list                      Add link to another container

$ docker run --rm -it --link myredis:redis redis /bin/bash

We start a second container and specify the –link (to be deprecated in the future, but for experimenting it is simple to use) option to be able to communicate with the Redis server.

# **** run redis-cli (42) ****
root@ae1a14eaf7e4:/data# redis-cli -h redis -p 6379

redis:6379> ping
PONG

redis:6379> 

redis:6379> set "abc" 123
OK

redis:6379> get "abc"
"123"

redis:6379> exit

root@ae1a14eaf7e4:/data# exit

exit

Since we started the service with a shell we can invoke the redis-cli to communicate with the specified host at the specified port. We verify that we can communicate with the Redis server and set and get a value pair. We then exit the redis-cli application and the container.

# **** backup the redis container (43)****
$ docker run --rm -it --link myredis:redis redis /bin/bash 

root@e09755c9171c:/data# redis-cli -h redis -p 6379

redis:6379> ping

PONG

redis:6379> set "abc" 123
OK

redis:6379> set "xyz" 789
OK

redis:6379> get "abc"
"123"

redis:6379> get "xyz"
"789"

Our Redis server container is still up and running. We start the second container and expose the bash shell. We check that we are able to communicate with the Redis server. We set and get a couple value pairs. All seems to be working fine.

# **** save the redis dataset end exit the container (44) ****
redis:6379> lastsave
(integer) 1551219125

redis:6379> save
OK

redis:6379> lastsave
(integer) 1551220166

redis:6379> bgsave
Background saving started

redis:6379> lastsave
(integer) 1551220183

redis:6379> exit
root@e09755c9171c:/data# exit
exit

On the container running redis-cli we issue a set of Redis commands. The idea is to make a backup of the Redis database. When done we exit the shell and the container.

# **** check some arguments (45) ****
--rm                             Automatically remove the container when it exits
--volumes-from list              Mount volumes from the specified container(s)
-v, --volume list                Bind mount a volume

$ docker help cp

Usage:	docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
	docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

Copy files/folders between a container and the local filesystem

Options:
  -a, --archive       Archive mode (copy all uid/gid information)
  -L, --follow-link   Always follow symbol link in SRC_PATH

$ docker run --rm --volumes-from myredis -v $PWD/backup:/backup debian cp /data/dump.rdb /backup/
Unable to find image 'debian:latest' locally
latest: Pulling from library/debian
741437d97401: Pull complete 
Digest: sha256:066051f6674f6a3293bbd5a190081b1ae7fcae655a3884db59ebb3a2831da623
Status: Downloaded newer image for debian:latest

We now check some arguments by using “docker help” and run “docker run” to produce the dump.rdb file in our local file system.

# **** locate and dump the dump.rdb file (46) ****
$ cd backup

$ ls -l
total 4
-rw-r--r--. 1 root root 112 Feb 26 16:39 dump.rdb

$ xxd dump.rdb
0000000: 5245 4449 5330 3030 39fa 0972 6564 6973  REDIS0009..redis
0000010: 2d76 6572 0535 2e30 2e33 fa0a 7265 6469  -ver.5.0.3..redi
0000020: 732d 6269 7473 c040 fa05 6374 696d 65c2  s-bits.@..ctime.
0000030: d6bd 755c fa08 7573 6564 2d6d 656d c208  ..u\..used-mem..
0000040: 040d 00fa 0c61 6f66 2d70 7265 616d 626c  .....aof-preambl
0000050: 65c0 00fe 00fb 0200 0003 7879 7ac1 1503  e.........xyz...
0000060: 0003 6162 63c0 7bff bded e0b5 7ea9 118d  ..abc.{.....~...

We locate the folder in which the dump was generated. We then dump the binary file. It seems that worked.

# **** stop and delete the container (47) ****
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
6eba1f137463        redis               "docker-entrypoint.s…"   3 hours ago         Up 3 hours          6379/tcp            myredis

$ docker stop myredis
myredis

$ docker ps -a       
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
6eba1f137463        redis               "docker-entrypoint.s…"   3 hours ago         Exited (0) 5 seconds ago                       myredis

We stop and delete the container running the Redis server.

# **** remove all leftover containers (48) ****
$ docker ps -a                      
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
6eba1f137463        redis               "docker-entrypoint.s…"   3 hours ago         Exited (0) 2 minutes ago                       myredis

$ docker rm $(docker ps -aq)
6eba1f137463

$ docker ps -a              
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Since we are done experimenting, we can safely remove all leftover containers and images.

Hopefully this was helpful for you as it was for me. One needs to read and experiment in order to make sure that the concepts were understood.

If you have comments or questions regarding this or any other post in this blog please leave me a note bellow. The same holds true if you need assistance with your software development project.

Keep on learning, experimenting and having fun developing software.

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.