Sunday, January 22, 2017

Wrapup: Docker Developer - Beginner Linux Containers course

So, I did another quick run-through of the subject course this morning prior to taking the test, and here are my thoughts.

  • I'd forgotten about a few potentially useful commands like "docker inspect", so the review was useful.
  • Lesson 3 (docker-compose) still feels ... compressed. There could have been more content, or it could have been a complete course all by itself. The overall structure of the lesson was 1) download the files from git; 2) bring up the swarm; 3) a brief explanation of some of the lines in the yml file; and 4) here's how to publish the swarm. I would like to see the lesson expanded to get a better sense of how the yml file was developed step by step. In fact, the example included four other yml files, so that might have been where the direction the author(s) was (were) originally headed. Update: found the tutorial which this lesson appears to be a summary of:
I scored 13/14 on the quiz, so yay me.

I'm going to take the rest of the free courses offered by Docker, just to see if they issues like resource control.

Saturday, January 21, 2017

More fun with the Docker Developer Beginner Linux Containers course

This time in lesson 3. Problems bringing up the docker swarm.

docker compose up -d

Only redis stayed up, according to docker ps -a. The other four containers exited prematurely.

First action: run compose up again without the -d option. This results in more output from each container. Summary of the output:

vote_1: python can't open file '': [Errno 13] Permission denied
worker_1: System.AggregationException: One or more errors occurred. (No such device or address)
db: exited with code 1

Well, after spending about a day debugging this, I found some comments on github that fedora uses a fork of docker, and the version may be out of date. So I uninstalled docker:

sudo dnf remove docker-engine docker-compose docker

Reinstalled from the official repos using instructions from:

And rebooted the machine because systemctl start docker-engine complained "docker-engine.service not found". After all this, the docker-compose tutorial worked as expected.

Here are some debugging notes, even if the correct solution ultimately turned out to be "reinstall from official sources."

As always, focus on one problem at a time. I did this by commenting out all but one member of the swarm in docker-compose.yml, and running "docker-compose up" to debug. The "command:" line in the yml file can be changed to try different things in the container. For example, replacing "command: python" with "command: ls -l /" allowed me to see that the ownership of the app directory (1000:1000) did not match the user (root), which explains the permission denied error.

Learn how to clean up and run things manually.

docker ps # show running containers
docker ps -a # show all containers
docker rm <container id> # remove containers listed in docker ps -a
docker rm -f <container id>
docker images
docker rmi <image id> # remove images

A neat shortcut is you only need to pass the first three digits of a container or image id.

Note that running "docker run -it examplevotingapp_vote /bin/sh" produced different results (i.e. /app was owned by root instead of 1000). My guess is it's because the docker-compose.yml file has this line:

  - ./vote:/app

This tells docker-compose to mount the "vote" directory in the cwd on the host as "/app". Of course, the vote directory is owned by me (uid 1000) instead of root, so that probably explains the ownership weirdness.

The difference from running the container manually and using docker-compose ultimately led me to realize that docker-compose starts up container examplevotingapp_vote_1. My current theory is that docker-compose takes the base _vote image and adds another layer to it according to the configuration in the yml file (e.g. mounts the /app volume, connects to the swarm networks, etc). Using layers is consistent with the docker architecture from what I've seen so far, so let's run with that for now.

One final note about the last build-push-pull steps from the end of the lesson. The steps do allow you to access (pull) your voting app containers from another machine. But they fail to mention what else you'll to bring up the swarm. Certainly, docker-compose up failed to do anything until I cloned the example app from github. And running docker-compose up now, it seems to be rebuilding the swarm instead of using the containers I already pulled down. And yes, docker images confirms there is now images beginning with examplevotingapp* as well as bobthesquirrel/votingapp*. Needs further investigation.

Thursday, January 19, 2017

Notes on Docker Course - Developer Beginner Linux Containers

I walked through the Developer Beginner Linux Containers course offered by Docker today, and hit a few snags. There's no obvious way to post comments or feedback to get the instructions updated, so here are my notes, for what they're worth.

Lesson 2.0 Webapps with Docker

This is the first snag:

$ sudo docker build -t bob_the_squirrel/myfirstapp .
[sudo] password for bob:
Sending build context to Docker daemon 8.192 kB
Step 1 : FROM alpine:latest
---> 88e169ea8f46
Step 2 : RUN apk add --update py-pip
---> Running in 15deabd22845
ERROR: unsatisfiable constraints:
 py-pip (virtual):
   provided by: py2-pip
   required by: world[py-pip]
The command '/bin/sh -c apk add --update py-pip' returned a non-zero code: 1

Easily fixed by modifying the Dockerfile:

# install the pip package and any dependencies
RUN apk add --update py2-pip # the lesson says to use py-pip

The next snag was this:

$ sudo docker build -t bob_the_squirrel/myfirstapp .
[sudo] password for bob:
Sending build context to Docker daemon 8.192 kB
Step 1 : FROM alpine:latest
---> 88e169ea8f46
Step 2 : RUN apk add --update py2-pip
---> Running in 33207dfe8efd
(1/12) Installing libbz2 (1.0.6-r5)
(2/12) Installing expat (2.2.0-r0)
(3/12) Installing libffi (3.2.1-r2)
(4/12) Installing gdbm (1.12-r0)
(5/12) Installing ncurses-terminfo-base (6.0-r7)
(6/12) Installing ncurses-terminfo (6.0-r7)
(7/12) Installing ncurses-libs (6.0-r7)
(8/12) Installing readline (6.3.008-r4)
(9/12) Installing sqlite-libs (3.15.2-r0)
(10/12) Installing python2 (2.7.13-r0)
(11/12) Installing py-setuptools (29.0.1-r0)
(12/12) Installing py2-pip (9.0.0-r0)
Executing busybox-1.25.1-r0.trigger
OK: 61 MiB in 23 packages
---> 840b9af43db6
Removing intermediate container 33207dfe8efd
Step 3 : COPY requirements.txt /usr/src/app
---> 0eac1e0f7a53
Removing intermediate container e9c368692b72
Step 4 : RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt
---> Running in cd9acc93e4f3
Could not open requirements file: [Errno 20] Not a directory: '/usr/src/app/requirements.txt'
You are using pip version 9.0.0, however version 9.0.1 is available.

You should consider upgrading via the 'pip install --upgrade pip' command. 

The command '/bin/sh -c pip install --no-cache-dir -r

/usr/src/app/requirements.txt' returned a non-zero code: 1

Turns out this was my fault because I forgot to add a trailing slash to the Dockerfile COPY command:

# Add files that make up the Flask application
COPY requirements.txt /usr/src/app # oops
RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt

If the trailing slash is included (/usr/src/app/), it looks like the COPY command will make the directory if it doesn't already exist.