Docker 101 Training Notes (part 3 of 4)

I attended a four part Docker 101 Training course at Virginia Tech. This post contains notes on part 3 of 4.

Understanding what’s in an image:

$ docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                          NAMES
6027bb44a67d        zookeeper           "/docker-entrypoint.…"   7 weeks ago         Up 2 days           2181/tcp, 2888/tcp, 3888/tcp   hyku_zoo1_1
6a9585f4b0bc        zookeeper           "/docker-entrypoint.…"   7 weeks ago         Up 2 days           2181/tcp, 2888/tcp, 3888/tcp   hyku_zoo2_1
5bd742a7314c        zookeeper           "/docker-entrypoint.…"   7 weeks ago         Up 2 days           2181/tcp, 2888/tcp, 3888/tcp   hyku_zoo3_1
$ docker image history zookeeper

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
397be0d8fa45        7 weeks ago         /bin/sh -c #(nop)  CMD ["" "start…   0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop) COPY file:5cb6c695778a88d6…   941B                
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV PATH=/usr/local/sbin:…   0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop)  EXPOSE 2181/tcp 2888/tcp …   0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop)  VOLUME [/data /datalog]      0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop) WORKDIR /zookeeper-3.4.12     0B                  
<missing>           7 weeks ago         |2 DISTRO_NAME=zookeeper-3.4.12 GPG_KEY=586E…   60MB                
<missing>           7 weeks ago         /bin/sh -c #(nop)  ARG DISTRO_NAME=zookeeper…   0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop)  ARG GPG_KEY=586EFEF859AF2…   0B                  
<missing>           7 weeks ago         /bin/sh -c set -ex;     adduser -D "$ZOO_USE…   4.82kB              
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV ZOO_USER=zookeeper ZO…   0B                  
<missing>           7 weeks ago         /bin/sh -c apk add --no-cache     bash     s…   4.21MB              
<missing>           7 weeks ago         /bin/sh -c set -x  && apk add --no-cache   o…   77.8MB              
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV JAVA_ALPINE_VERSION=8…   0B                  
<missing>           7 weeks ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=8u171       0B                  
<missing>           2 months ago        /bin/sh -c #(nop)  ENV PATH=/usr/local/sbin:…   0B                  
<missing>           2 months ago        /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/lib/jv…   0B                  
<missing>           2 months ago        /bin/sh -c {   echo '#!/bin/sh';   echo 'set…   87B                 
<missing>           2 months ago        /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0B                  
<missing>           7 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B                  
<missing>           7 months ago        /bin/sh -c #(nop) ADD file:093f0723fa46f6cdb…   4.15MB 

Create a tar of a Docker image:

$ docker image save zookeeper | tar x -
$ ls -latr

total 32
-rw-r--r--   1 janicekim  staff    92 Dec 31  1969 repositories
-rw-r--r--   1 janicekim  staff   667 Dec 31  1969 manifest.json
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 f489fb9bc024e38efcd28c3d574b04568ad8db2ecf4e04f850e230b52970c5b4
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 bc689ee72c370143bedbce7354c4ab6d875c9ff0770fa5fc30767e43eefe5f69
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 b7c8de9af3495335f18f0ed1dde5f34a589d06d84e25c7fe75eb2987a8e5205a
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 8c3cedd6ec82ada9c5ffbbdce49e2e004bf3e9f8af1d0c986af084ee555e6f5b
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 498654318d0999ce36c7b90901ed8bd8cb63d86837cb101ea1ec9bb092f44e59
-rw-r--r--   1 janicekim  staff  7338 Jun 16 05:16 397be0d8fa458782ea28b39e9a7480272519c522f6416ee3cae2efe204f324d3.json
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 26353606c6538cd3386157cf987e132c9809e0b8fa413fe9a6f35b20a0884ccc
drwxr-xr-x   5 janicekim  staff   160 Jun 16 05:16 146c29a4165a3791184d9b4631a0f0488f8eb4a282d606ed53b484fbf14de7fe
drwxr-xr-x   7 janicekim  staff   224 Aug 10 12:17 ..
drwxr-xr-x  12 janicekim  staff   384 Aug 10 14:33 .

Look at the generated manifest:

$ cat manifest.json | python -m json.tool 

        "Config": "397be0d8fa458782ea28b39e9a7480272519c522f6416ee3cae2efe204f324d3.json",
        "Layers": [
        "RepoTags": [
    • Each layer shows actual filesystem changes
    • The layers are unioned together, they provide a full filesystem
      • Each layer can add files as needed
      • Files in “higher” layers replace the same file in “lower” layers
    • Deleted files are represented in a layer as a “whiteout” file
    • Whiteout files are only used by the filesystem driver and not visible in the merged filesystem

Popular Base Distributions

  • Ubuntu/Debian – larger and familiar, results in larger image size
  • Alpine Linux – smaller results in smaller image size


How to Install Rails on CentOS 7

Install prerequisite dependencies

$ sudo yum install -y git-core zlib zlib-devel gcc-c++ patch readline readline-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison curl sqlite-devel

Install rbenv

$ git clone ~/.rbenv
$ cd ~/.rbenv && src/configure && make -C src
$ echo 'export PATH=$HOME/.rbenv/bin:$PATH' >> ~/.bash_profile

Run rbenv init, and follow the instructions.

$ rbenv init
# Load rbenv automatically by appending
# the following to ~/.bash_profile:

eval "$(rbenv init -)"

Restart your shell.

Install ruby-build plugin to get access to rbenv install.

$ git clone ~/.rbenv/plugins/ruby-build

Finally, install ruby, and confirm version.

$ rbenv install 2.2.2
$ ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

Confirm sqlite3 is installed

$ sqlite3 --version
3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668

Install rails using gem.
NOTE: I originally had some trouble running this command because the version of ruby kept going back to 2.0.0, which is the default version for my installation of CentOS. I had to manually add a .ruby-version file into my working directory with the contents 2.2.2 in order for the gem command to proceed with the rails installation. I don’t know if this is the correct way of doing this.

$ gem install rails
$ rails --version

Manage Docker Images

List docker images

$ docker images -a

Remove docker images

$ docker rmi 

Listing and Removing dangling docker images

$ docker images -f dangling=true
$ docker rmi $(docker images -f dangling=true -q)

Generate SSL key and certificate using openssl

$ mkdir ~/gencerts
$ cd ~/gencerts
$ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
$ openssl rsa -passin pass:x -in server.pass.key -out server.key
$ openssl req -new -key server.key -out server.csr

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:Massachusetts
Locality Name (eg, city) [Default City]:Boston
Organization Name (eg, company) [Default Company Ltd]:MyOrg
Organizational Unit Name (eg, section) []:MyUnit
Common Name (eg, your name or your server's hostname) []:
Email Address []

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
$ mkdir ../ssl
$ cp server.key ../ssl/dev.key
$ cp server.crt ../ssl/dev.crt

Install local docker and docker-compose on CentOS 7

Configure the docker repository with yum

$ sudo tee /etc/yum.repos.d/docker.repo <<-'EOF' > [dockerrepo]
> name=Docker Repository
> baseurl=
> enabled=1
> gpgcheck=1
> gpgkey=

Install and start local docker

$ sudo yum install docker-engine
$ sudo systemctl enable docker.service
$ sudo systemctl start docker

Run a sanity check to see if docker is working properly

$ sudo docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c04b14da8d14: Pull complete 
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:

For more examples and ideas, visit:

To allow the user to run docker without sudo, create a “docker” group and add yourusername to it.

$ sudo groupadd docker
$ sudo usermod -aG docker yourusername

Log out and log back in for the group changes to go into effect. Try it out:

$ docker run --rm hello-world

Enable docker to be run after reboot

$ sudo systemctl enable docker

Install docker-compose

$ sudo -i
# curl -L`uname -s`-`uname -m` > /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   600    0   600    0     0   2848      0 --:--:-- --:--:-- --:--:--  2870
100 7857k  100 7857k    0     0  15.5M      0 --:--:-- --:--:-- --:--:-- 15.5M
# chmod +x /usr/local/bin/docker-compose
# exit
$ logout

Check that docker is running

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

Google Maps with input events

This example code updates a set of input fields with latitude-longitude values in response to mouse clicks on an instance of Google Maps. It can also redraw map markers upon typing in new coordinates.

    <style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 10; padding: 10 }
      #map_canvas { height: 100% }
    <script type="text/javascript" src=""></script>
    <script type="text/javascript">
      var map;
      var currMarker = new google.maps.Marker();

      function initialize() {
        var myLatLng = new google.maps.LatLng(0, 0);
        var mapOptions = {
          center: myLatLng,
          zoom: 1,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        map = new google.maps.Map(document.getElementById("map_canvas"),

        google.maps.event.addListener(map, 'click', function(event) {

      function placeMarker(location) {
        var marker = new google.maps.Marker({
            position: location,
            map: map


      function updateMarker() {
        var lat = document.getElementById("lat").value;
        var lng = document.getElementById("lng").value;
        var newLatLng = new google.maps.LatLng(lat, lng);
        var marker = new google.maps.Marker({
            position: newLatLng,
            map: map
  <body onload="initialize()">
    <div id="map_canvas" style="width:400px; height:400px"></div>
    <div id="coordinates">
      lat: <input id="lat" type="text" onchange="updateMarker()" value="0" />
      lng: <input id="lng" type="text" onchange="updateMarker()" value="0" />

Below is a screenshot of what the code creates: