elktail: commandline tailing of Elasticsearch with Docker

I’ve been working on a system that uses Elasticsearch on Docker Swarm, and today, I really wanted to grep some log files.
The closest thing I found was elktail – see http://knes1.github.io/blog/2016/2016-03-06-elktail-command-line-tool-for-tailing-and-querying-ELK-logs.html

Of course, I needed it in a container, so I could attach it to the Swarm stack’s network…

So I forked, merged in all the other forks I found quickly, and then set up svendowideit/elktail as an autobuild image on Docker Hub.

So now, I have a Bash alias:

alias logs='docker run --rm -it --net elasticsearch_esnetwork svendowideit/elktail --url http://elasticsearch:9200 -f "%log" -i "*"'

and can quickly see what’s up with the system by running:

logs | grep sub-system

Working locally with Docker while on the road.

I just got back from a conference trip to Europe, DockerCon in Copenhagen, All-Systems-Go in Berlin, and then Open Source Summit in Prague.

While I was there, I needed to continue doing the development of some major refactoring of RancherOS for my LinuxKit talk – which meant I needed to be able to “docker pull” from both builds and temporary testing VM’s, from horrible hotel Wifi, and from overloaded conference Wifi – not great.

Because the bandwidth from Australia isn’t wonderful, I had already modified the builds and test tooling to use a local Docker registry mirror – so all I really needed to do was setup the same caching infrastructure I have on my home office network, but on a roaming notebook… and yup, its pretty straightforward:

sven@y260:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc4f1ff259f7 b8efb18f159b "nginx -g 'daemon ..." 2 weeks ago Up 13 days 0.0.0.0:80->80/tcp nginx
458c808f9218 3ebefe7c539b "/entrypoint.sh /e..." 2 weeks ago Up 13 days 0.0.0.0:5555->5000/tcp mirror
e09411b42128 3ebefe7c539b "/entrypoint.sh /e..." 2 weeks ago Up 13 days 0.0.0.0:5000->5000/tcp registry
5d572c8e8658 46fc18186a54 "/bin/sh -c 'chmod..." 2 weeks ago Up 9 days 0.0.0.0:3142->3142/tcp apt-cacher

These containers were started using docker run:


#apt-cacher-ng
docker run -d --network host --restart=always --name apt-cacher -v /var/cache/apt-cacher-ng:/var/cache/apt-cacher-ng svendowideit/apt-cacher-ng
#a docker registry
docker run -d --network host --name registry --restart always registry
#a hub registry mirror
docker run -d --name mirror --network host -v /var/lib/registry-mirror:/registry -e STORAGE_PATH=/registry -e STANDALONE=false -e MIRROR_SOURCE=https:/registry-1.docker.io -e MIRROR_SOURCE_INDEX=https://index.docker.io registry
docker exec mirror sh -c "echo 'proxy:' >> /etc/docker/registry/config.yml"
docker exec mirror sh -c "echo ' remoteurl: https://registry-1.docker.io' >> /etc/docker/registry/config.yml"
docker exec mirror sh -c "sed -i~ 's/addr: :5000/addr: :5555/g' /etc/docker/registry/config.yml"
docker restart mirror

The docker daemon will automatically use the local registry, and the local registry mirror on the “localhost” port 5000&5555 – and so long as you can give your VM’s a valid IP to your host, that too will work..

I have the following in my .bashrc, and these environment vars are used by the build and test tooling to pass on to the VM’s (ok, so this works because I have VMWare workstation on my Linux box):


export APTPROXY=http://$(ip a | grep "inet " | grep global | grep vmnet1 | sed 's/ //' | cut -d " " -f 2 | sed 's/\/.//'):3142
export ENGINE_REGISTRY_MIRROR=http://$(ip a | grep "inet " | grep global | grep vmnet1 | sed 's/ //' | cut -d " " -f 2 | sed 's/\/.//'):5555
export RANCHER_REPO=http://$(ip a | grep "inet " | grep global | grep vmnet1 | sed 's/ //' | cut -d " " -f 2 | sed 's/\/.//')/

Looking for a new challenge

After two and a half years, my contract with Docker Inc has finished.

I’m available for short term Docker consultations and training courses. I’m in Brisbane – but I’m happy to talk to you about flying out to where you are.

After two and a half years, my contract with Docker Inc has finished.

Its been a blast – I’ve never worked in a startup before, and I was hired early enough to have a very broad scope of work – including supporting users, working as a maintainer and oss contributor, and leading the development of Boot2Docker (now replaced by Docker for Mac and Windows) – a micro Linux distribution with OSX and Windows installers to allow users on those platforms to use Docker.

Its been amazing seeing the inside of an incredibly dynamic, game-changing project that has succeeded in growing for the three years that its been around – TWiki was successful around the 2000, but never managed to convert in the way that Docker has.

I’ve spent the last week cleaning up my email, git repositories and getting started on the non-computer projects that have been languishing for the last 3 years – and started playing with the ESP8266 I have – Like I said to Nathan the other day, having a relaxing time writing C++ 🙂

Until I find the next big project, startup, or workplace?:

I’m available for short term Docker consultations and training courses. I’m in Brisbane – but I’m happy to talk to you about flying out to where you are.

Using Docker to quickly and safely reproduce issues

I had a problem following an installation the other day, and eventually we tracked it down.

This week, I was curious to see if things were fixed, but I had already installed the tool on my computer.

So I ran up Docker container:

$ docker run --rm -it --name test debian bash
root@14e9e953d708:/# apt-get update && apt-get install -yq curl sudo vim-tiny
Get:1 http://security.debian.org jessie/updates InRelease [63.1 kB]
Get:2 http://security.debian.org jessie/updates/main amd64 Packages [182 kB]
Ign http://httpredir.debian.org jessie InRelease
Get:3 http://httpredir.debian.org jessie-updates InRelease [135 kB]
Get:4 http://httpredir.debian.org jessie Release.gpg [2373 B]
Get:5 http://httpredir.debian.org jessie Release [148 kB]
Get:6 http://httpredir.debian.org jessie-updates/main amd64 Packages [3653 B]
Get:7 http://httpredir.debian.org jessie/main amd64 Packages [9035 kB]
Fetched 9569 kB in 16s (574 kB/s)
Reading package lists... Done
Reading package lists...
Building dependency tree...
The following extra packages will be installed:
ca-certificates krb5-locales libcurl3 libffi6 libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
libidn11 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4 libp11-kit0
librtmp1 libsasl2-2 libsasl2-modules libsasl2-modules-db libssh2-1 libssl1.0.0 libtasn1-6 openssl
vim-common
Suggested packages:
gnutls-bin krb5-doc krb5-user libsasl2-modules-otp libsasl2-modules-ldap libsasl2-modules-sql
libsasl2-modules-gssapi-mit libsasl2-modules-gssapi-heimdal indent
The following NEW packages will be installed:
ca-certificates curl krb5-locales libcurl3 libffi6 libgmp10 libgnutls-deb0-28 libgssapi-krb5-2
libhogweed2 libidn11 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
libp11-kit0 librtmp1 libsasl2-2 libsasl2-modules libsasl2-modules-db libssh2-1 libssl1.0.0 libtasn1-6
openssl sudo vim-common vim-tiny
0 upgraded, 28 newly installed, 0 to remove and 1 not upgraded.
Need to get 9322 kB of archives.
After this operation, 19.7 MB of additional disk space will be used.
Get:1 http://security.debian.org/ jessie/updates/main libsasl2-modules-db amd64 2.1.26.dfsg1-13+deb8u1 [67.1 kB]
Get:2 http://security.debian.org/ jessie/updates/main libsasl2-2 amd64 2.1.26.dfsg1-13+deb8u1 [105 kB]
Get:3 http://security.debian.org/ jessie/updates/main libldap-2.4-2 amd64 2.4.40+dfsg-1+deb8u1 [218 kB]
...
root@14e9e953d708:/# adduser --ingroup sudo sven
Adding user sven' ...
Adding new user
sven' (1000) with group sudo' ...
Creating home directory
/home/sven' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for sven
Enter the new value, or press ENTER for the default
Full Name []: sven
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
root@14e9e953d708:/#

and then in another terminal:


$ docker exec -it -u sven insane_colden bash
sven@14e9e953d708:/$ sudo env

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.

[sudo] password for sven:
HOSTNAME=14e9e953d708
TERM=xterm
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:.tar=01;31:.tgz=01;31:.arc=01;31:.arj=01;31:.taz=01;31:.lha=01;31:.lz4=01;31:.lzh=01;31:.lzma=01;31:.tlz=01;31:.txz=01;31:.tzo=01;31:.t7z=01;31:.zip=01;31:.z=01;31:.Z=01;31:.dz=01;31:.gz=01;31:.lrz=01;31:.lz=01;31:.lzo=01;31:.xz=01;31:.bz2=01;31:.bz=01;31:.tbz=01;31:.tbz2=01;31:.tz=01;31:.deb=01;31:.rpm=01;31:.jar=01;31:.war=01;31:.ear=01;31:.sar=01;31:.rar=01;31:.alz=01;31:.ace=01;31:.zoo=01;31:.cpio=01;31:.7z=01;31:.rz=01;31:.cab=01;31:.jpg=01;35:.jpeg=01;35:.gif=01;35:.bmp=01;35:.pbm=01;35:.pgm=01;35:.ppm=01;35:.tga=01;35:.xbm=01;35:.xpm=01;35:.tif=01;35:.tiff=01;35:.png=01;35:.svg=01;35:.svgz=01;35:.mng=01;35:.pcx=01;35:.mov=01;35:.mpg=01;35:.mpeg=01;35:.m2v=01;35:.mkv=01;35:.webm=01;35:.ogm=01;35:.mp4=01;35:.m4v=01;35:.mp4v=01;35:.vob=01;35:.qt=01;35:.nuv=01;35:.wmv=01;35:.asf=01;35:.rm=01;35:.rmvb=01;35:.flc=01;35:.avi=01;35:.fli=01;35:.flv=01;35:.gl=01;35:.dl=01;35:.xcf=01;35:.xwd=01;35:.yuv=01;35:.cgm=01;35:.emf=01;35:.axv=01;35:.anx=01;35:.ogv=01;35:.ogx=01;35:.aac=00;36:.au=00;36:.flac=00;36:.m4a=00;36:.mid=00;36:.midi=00;36:.mka=00;36:.mp3=00;36:.mpc=00;36:.ogg=00;36:.ra=00;36:.wav=00;36:.axa=00;36:.oga=00;36:.spx=00;36:.xspf=00;36:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
MAIL=/var/mail/root
LOGNAME=root
USER=root
USERNAME=root
HOME=/root
SUDO_COMMAND=/usr/bin/env
SUDO_USER=sven
SUDO_UID=1000
SUDO_GID=27
sven@14e9e953d708:/$

(ok, so the thing I was testing was something else)

The point is, without using alot of time, diskspace, or effort, I created a debian environment, created the environment I needed, and then could run my test as the user I needed.

If this was more than a once-off, I’d do the setup in a Dockerfile, and make the command I’m testing be that Dockerfile’s ENTRYPOINT – making it possible to run a suite of tests using docker build -t test . && docker --rm run test

Docker on Windows Server 2016 tech preview 3

‘docker run –rm -it vim’ almost works running in a native Windows Container

First thing is to install Windows 2016 – I started in a VM, but I’m rapidly thinking i might try it on my notebook – Windows 10 is getting old already 🙂

Then goto https://msdn.microsoft.com/virtualization/windowscontainers/quick_start/inplace_setup . Note that the powershell script will download another 3GB.

Windows-system32-docker

And now – you can run docker info from either cmd.exe, or powershell.

There’s only a limited set of images you can download from Microsoft – docker search seems to always reply with the same set:

PS C:\Users\Administrator> docker search anything
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
microsoft/iis Internet Information Services (IIS) instal... 1 [OK] [OK]
microsoft/dnx-clr .NET Execution Environment (DNX) installed... 1 [OK] [OK]
microsoft/ruby Ruby installed in a Windows Server Contain... 1 [OK]
microsoft/rubyonrails Ruby on Rails installed in a Windows Serve... 1 [OK]
microsoft/python Python installed in a Windows Server Conta... 1 [OK]
microsoft/go Go Programming Language installed in a Win... 1 [OK]
microsoft/mongodb MongoDB installed in a Windows Server Cont... 1 [OK]
microsoft/redis Redis installed in a Windows Server Contai... 1 [OK]
microsoft/sqlite SQLite installed in a Windows Server Conta... 1 [OK]

I downloaded two, and this shows’s they’re re-using the windowsservercore image as their common base image:

PS C:\Users\Administrator> docker images -a
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
microsoft/go latest 33cac80f92ea 2 days ago 10.09 GB
  8daec63ffb52 2 days ago 9.75 GB
  fbab9eccc1e7 2 days ago 9.697 GB
microsoft/dnx-clr latest 156a0b59c5a8 2 days ago 9.712 GB
  28473be483a9 2 days ago 9.707 GB
  56b7e372f76a 2 days ago 9.697 GB
windowsservercore 10.0.10514.0 0d53944cb84d 6 days ago 9.697 GB
windowsservercore latest 0d53944cb84d 6 days ago 9.697 GB

PS C:\Users\Administrator> docker history microsoft/dnx-clr
IMAGE CREATED CREATED BY SIZE COMMENT
156a0b59c5a8 2 days ago cmd /S /C setx PATH "%PATH%;C:\dnx-clr-win-x6 5.558 MB
28473be483a9 2 days ago cmd /S /C REM (nop) ADD dir:729777dc7e07ff03f 9.962 MB
56b7e372f76a 2 days ago cmd /S /C REM (nop) LABEL Description=.NET Ex 41.41 kB
0d53944cb84d 6 days ago 9.697 GB
PS C:\Users\Administrator> docker history microsoft/go
IMAGE CREATED CREATED BY SIZE COMMENT
33cac80f92ea 2 days ago cmd /S /C C:\build\install.cmd 335 MB
8daec63ffb52 2 days ago cmd /S /C REM (nop) ADD dir:898a4194b45d1cc66 53.7 MB
fbab9eccc1e7 2 days ago cmd /S /C REM (nop) LABEL Description=GO Prog 41.41 kB
0d53944cb84d 6 days ago 9.697 GB

And so the fun begins.

PS C:\Users\Administrator> docker run --rm -it windowsservercore cmd

gives you a containerized shell.

Lets try to build an image that has the chocolatey installer:

FROM windowsservercore

RUN @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))"

CMD powershell

and then use that image to install…. vim

FROM chocolatey

RUN choco install -y vim

It works!

 docker run --rm -it vim cmd

and then run

C:\Program Files (x86)\vim\vim74\vim.exe

Its not currently usable, I suspect because the ANSI terminal driver is really really new code – but BOOM!

I haven’t worked out how to get the Dockerfile

CMD

or

ENTRYPOINT

to work with paths that have spaces – it doesn’t seem to support the array form yet…

I’m going to keep playing, and put the Dockerfiles into https://github.com/SvenDowideit/WindowsDocker

Don’t forget to read the documentation at https://msdn.microsoft.com/en-us/virtualization/windowscontainers/containers_welcome

Slim application containers (using Docker)

Another talk I gave at Linux.conf.au, was about making slim containers (youtube) – ones that contain only the barest essentials needed to run an application.

In the same way that we don’t ship the VM / filesystem of our build server, you should not be shipping the container you’re building from source.

Another talk I gave at Linux.conf.au, was about making slim containers (youtube) –  ones that contain only the barest essentials needed to run an application.

And I thought I’d do it from source, as most “Built from source” images also contain the tools used to build the software.

1. Make the Docker base image you’re going to use to build the software

In January 2015, the main base images and their sizes looked like:

scratch             latest              511136ea3c5a        19 months ago       0 B
busybox             latest              4986bf8c1536        10 days ago         2.433 MB
debian              7.7                 479215127fa7        10 days ago         85.1 MB
ubuntu              15.04               b12dbb6f7084        10 days ago         117.2 MB
centos              centos7             acc1b23376ec        10 days ago         224 MB
fedora              21                  834629358fe2        10 days ago         250.2 MB
crux                3.1                 7a73a3cc03b3        10 days ago         313.5 MB

I’ll pick Debian, as I know it, and it has the fewest restrictions on what contents you’re permitted to redistribute (and because bootstrapping busybox would be an amazing talk on its own).

Because I’m experimenting, I’m starting by seeing how small I can make a new Debian base image –  starting with:

FROM debian:7.7

RUN rm -r /usr/share/doc /usr/share/doc-base \
          /usr/share/man /usr/share/locale /usr/share/zoneinfo

CMD ["/bin/sh"]

Then make a new single layer (squashed image) by running docker export and docker import

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
debian              7.7                 479215127fa7        10 days ago         85.1 MB
our/debian:jessie   latest              cba1d00c3dc0        1 seconds ago       46.6 MB

Ok, not quite half, but you get the idea.

Its well worth continuing this exercise using things like dpkg —get-selections to remove anything else you won’t need.

Importantly, once you’ve made your smaller base image, you should use it consistently for ALL the containers you use. This means that whenever there are important security fixes, that base image will be downloadable as quickly as possible –  and all your related images can be restarted quickly.

This also means that you do NOT want to squish your images to one or two layers, but rather into some logical set of layers that match your deployment update risks –  a common root base, and then layers based on common infrastructure, and lastly application and customisation layers.

2. Build static binaries –  or not

Building a static binary of your application (in typical Go style) makes some things simpler –  but in the end, I’m not really convinced it makes a useful difference.

But in my talk, I did it anyway.

Make a Dockerfile that installs all the tools needed, builds nginx, and then output’s a tar file that is a new build context for another Docker image (and contains the libraries ldd tells us we need):

cat Dockerfile.build-static-nginx | docker build -t build-nginx.static -
docker run --rm build-nginx.static cat /opt/nginx.tar > nginx.tar
cat nginx.tar | docker import - micronginx
docker run --rm -it -p 80:80 micronginx /opt/nginx/sbin/nginx -g "daemon off;"
nginx: [emerg] getpwnam("nobody") failed (2: No such file or directory)

oh. I need more than just libraries?

3. Use inotify to find out what files nginx actually needs!

Use the same image, but start it with Bash –  use that to install and run inotify, and then use docker exec to start nginx:

docker run --rm build-nginx.static bash
$ apt-get install -yq inotify-tools iwatch
# inotifywait -rm /etc /lib /usr/lib /var
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_files-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_nis-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE ld-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libc-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnsl-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_compat-2.13.so
/etc/ OPEN passwd
/etc/ OPEN group
/etc/ ACCESS passwd
/etc/ ACCESS group
/etc/ CLOSE_NOWRITE,CLOSE group
/etc/ CLOSE_NOWRITE,CLOSE passwd
/etc/ OPEN localtime
/etc/ ACCESS localtime
/etc/ CLOSE_NOWRITE,CLOSE localtime

Perhaps it shouldn’t be too surprising that nginx expects to rifle through your user password files when it starts 🙁

4. Generate a new minimal Dockerfile and tar file Docker build context, and pass that to a new `docker build`

The trick is that the build container Dockerfile can generate the minimal Dockerfile and tar context, which can then be used to build a new minimal Docker image.

The excerpt from the Dockerfile that does it looks like:


# Add a Dockerfile to the tar file
RUN echo "FROM busybox" > /Dockerfile \
    && echo "ADD * /" >> /Dockerfile \
    && echo "EXPOSE 80 443" >> /Dockerfile \
    && echo 'CMD ["/opt/nginx/sbin/nginx", "-g", "daemon off;"]' >> /Dockerfile

RUN tar cf /opt/nginx.tar \
           /Dockerfile \
           /opt/nginx \
           /etc/passwd /etc/group /etc/localtime /etc/nsswitch.conf /etc/ld.so.cache \
           /lib/x86_64-linux-gnu

This tar file can then be passed on using

cat nginx.tar | docker build -t busyboxnginx .

Result

Comparing the sizes, our build container is about 1.4GB, the Official nginx image about 100MB, and our minimal nginx container, 21MB to 24MB –  depending if we add busybox to it or not:

REPOSITORY          TAG            IMAGE ID            CREATED              VIRTUAL SIZE
micronginx          latest         52ec332b65fc        53 seconds ago       21.13 MB
nginxbusybox        latest         80a526b043fd        About a minute ago   23.56 MB
build-nginx.static  latest         4ecdd6aabaee        About a minute ago   1.392 GB
nginx               latest         1822529acbbf        8 days ago           91.75 MB

Its interesting to remember that we rely heavily on I know this, its a UNIX system –  application services can have all sorts of hidden assumptions that won’t be revealed without putting them into more constrained environments.

In the same way that we don’t ship the VM / filesystem of our build server, you should not be shipping the container you’re building from source.

This analysis doesn’t try to restrict nginx to only opening certain network ports, devices, or IPC mechanisms – so there’s more to be done…

Kickstart new developers using Docker – Linux.conf.au 2015

One of the talks I gave at Linux.conf.au this year was a quick-start guide to using Docker.

One of the talks I gave at Linux.conf.au this year was a quick-start guide to using Docker.

The slides begin with building Apache from source on your local host, using their documentation, and then how much simpler it is if instead of documentation, the project provides a Dockerfile. I quickly gloss over making a slim production container from that large development container – see my other talk, which I’ll blog about a little later.

The second example, is using a Dockerfile to create and execute a test environment, so everyone can replicate identical test results.

Finally, I end with a quite example of fig (Docker Compose), and running GUI applications in containers.

the Slides

Using the official Go Docker image to try out a library

The official Docker language images can auto build and run your trial application a cinch. See how with golang:onbuild

We received a Pull Request to add Swagger support to document the Docker API, and @proppy asked if we could make sure we could load the schema in a standard json schema loader, for example gojsonschema

The answer is no, not yet – but we’ll work towards it 🙂

But to find out, I added 3 files, a Dockerfile:


FROM golang:onbuild

a Makefile:

default:
docker build -t loadschema .
docker run --rm loadschema

and a tiny 13 line go program:

package main

import (
"github.com/xeipuuv/gojsonschema"
)

func main() {

_, err := gojsonschema.NewJsonSchemaDocument("file:///go/src/app/docker-v1.14.json")
if err != nil {
panic(err.Error())
}
}

The golang:onbuild image has ONBUILD instructions to COPY the current context, download the dependencies, build the code, and then sets that application as the default CMD.

AWE.

Speeding up CPAN module contributions using the Docker language stack images

Using the Docker perl language stack image for speeding up contributions to CPAN modules.

Docker Inc. just released our first set of programming language images on the Docker Hub. They cover c/c++ (gcc), clojure, go (golang), hy (hylang), java, node, perl, php, python, rails, and ruby.

As I need to do some work on API testing when I come back from holidays, I thought I’d look at the Net:Docker CPAN module – and of course, there is no Perl on my Boot2Docker image, so its a perfect opportunity to see what I should do.

After forking and cloning the Git repository, I created the following initial Dockerfile:


FROM perl:5.20
MAINTAINER Sven Dowideit SvenDowideit@home.org.au

COPY . /docker-perl
WORKDIR /docker-perl

RUN cpanm --installdeps .
RUN perl Build.PL
RUN ./Build build
RUN ./Build test

It fails to build during the ‘test’ step:


$ docker build -t docker-perl .

... snip ...

Step 6 : RUN ./Build test
---> Running in 367afe04c77e
Can't open socket var/run/docker.sock: No such file or directory at /usr/local/lib/perl5/site_perl/5.20.0/LWP/Protocol/http/SocketUnixAlt.pm line 27. at t/docker-api.t line 9.

Tests were run but no plan was declared and done_testing() was not seen.

Looks like your test exited with 255 just after 1.

t/docker-api.t ....
Dubious, test returned 255 (wstat 65280, 0xff00)
All 1 subtests passed
Can't locate IO/String.pm in @INC (you may need to install the IO::String module) (@INC contains: /docker-perl/blib/arch /docker-perl/blib/lib /usr/local/lib/perl5/site_perl/5.20.0/x86_64-linux /usr/local/lib/perl5/site_perl/5.20.0 /usr/local/lib/perl5/5.20.0/x86_64-linux /usr/local/lib/perl5/5.20.0 .) at t/docker-start.t line 3.
BEGIN failed--compilation aborted at t/docker-start.t line 3.
t/docker-start.t ..
Dubious, test returned 2 (wstat 512, 0x200)
No subtests run

Test Summary Report

t/docker-api.t (Wstat: 65280 Tests: 1 Failed: 0)
Non-zero exit status: 255
Parse errors: No plan found in TAP output
t/docker-start.t (Wstat: 512 Tests: 0 Failed: 0)
Non-zero exit status: 2
Parse errors: No plan found in TAP output
Files=2, Tests=1, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.21 cusr 0.03 csys = 0.26 CPU)
Result: FAIL
2014/09/26 16:08:19 The command [/bin/sh -c ./Build test] returned a non-zero code: 1

I’m going to have to give this Dockerfile a DOCKER_HOST (incorrectly using http://) setting (to one of my insecure plain text tcp based servers :), and add IO::String and JSON:XS to the cpanfile.

Unfortunately, because cpanm --installdeps . uses the files in the build context, this way does not use the build cache – so its slow. Its worth duplicating the contents of the cpanfile before the COPY instruction for speed.

So the working Dockerfile looks like:


FROM perl:5.20
MAINTAINER Sven Dowideit SvenDowideit@home.org.au

RUN cpanm Module::Build::Tiny
RUN cpanm Moo
#', '1.002000';
RUN cpanm JSON
RUN cpanm JSON::XS
RUN cpanm LWP::UserAgent
RUN cpanm LWP::Protocol::http::SocketUnixAlt
RUN cpanm URI
RUN cpanm AnyEvent
RUN cpanm AnyEvent::HTTP
RUN cpanm IO::String

COPY . /docker-perl
WORKDIR /docker-perl

RUN cpanm --installdeps .
RUN perl Build.PL
RUN ./Build build

This is a terrible cheat.

ENV DOCKER_HOST http://10.10.10.4:2375

RUN ./Build test
RUN ./Build install

CMD ["docker.pl", "ps"]

and then docker build -t docker-perl . results in:


bash-3.2$ docker build -t docker-perl .
Sending build context to Docker daemon 138.8 kB
Sending build context to Docker daemon
Step 0 : FROM perl:5.20
---> 4d4674548e76
Step 1 : MAINTAINER Sven Dowideit SvenDowideit@home.org.au
---> Using cache
---> 4ad0946e76aa
Step 2 : RUN cpanm Module::Build::Tiny
---> Using cache
---> f1b94d36a51c
Step 3 : RUN cpanm Moo
---> Using cache
---> 98de8c3a19a8
Step 4 : RUN cpanm JSON
---> Using cache
---> 73debd4ee367
Step 5 : RUN cpanm JSON::XS
---> Using cache
---> 89378a425f0b
Step 6 : RUN cpanm LWP::UserAgent
---> Using cache
---> 252fe329cf22
Step 7 : RUN cpanm LWP::Protocol::http::SocketUnixAlt
---> Using cache
---> a77d289faf19
Step 8 : RUN cpanm URI
---> Using cache
---> 6804b418778d
Step 9 : RUN cpanm AnyEvent
---> Using cache
---> c595f66bcf73
Step 10 : RUN cpanm AnyEvent::HTTP
---> Using cache
---> 31b25b2da3c4
Step 11 : RUN cpanm IO::String
---> Using cache
---> e54cd3d01988
Step 12 : COPY . /docker-perl
---> 4d4801209a79
Removing intermediate container c42897136186
Step 13 : WORKDIR /docker-perl
---> Running in 36575a59e465
---> 7042c67cf1b7
Removing intermediate container 36575a59e465
Step 14 : RUN cpanm --installdeps .
---> Running in c1b5cbb75c4a
--> Working on .
Configuring Net-Docker-0.002005 ... OK
<== Installed dependencies for .. Finishing.
---> 071f9caca472
Removing intermediate container c1b5cbb75c4a
Step 15 : RUN perl Build.PL
---> Running in fae9bbce142f
Creating new 'Build' script for 'Net-Docker' version '0.002005'
---> 2800182bd0ff
Removing intermediate container fae9bbce142f
Step 16 : RUN ./Build build
---> Running in a98cb6c7a808
cp lib/Net/Docker.pm blib/lib/Net/Docker.pm
cp script/docker.pl blib/script/docker.pl
---> f5ba5be85f9d
Removing intermediate container a98cb6c7a808
Step 17 : ENV DOCKER_HOST http://10.10.10.4:2375
---> Running in 1e8b3273974c
---> fffb42d69011
Removing intermediate container 1e8b3273974c
Step 18 : RUN ./Build test
---> Running in 3baacccbf17e
t/docker-api.t .... ok
t/docker-start.t .. ok
All tests successful.
Files=2, Tests=41, 5 wallclock secs ( 0.02 usr 0.02 sys + 0.26 cusr 0.06 csys = 0.36 CPU)
Result: PASS
---> f5d371cdc1fa
Removing intermediate container 3baacccbf17e
Step 19 : RUN ./Build install
---> Running in 60cd90714e02
Installing /usr/local/lib/perl5/site_perl/5.20.0/Net/Docker.pm
Installing /usr/local/bin/docker.pl
---> 62c6368a2fb0
Removing intermediate container 60cd90714e02
Step 20 : CMD ["docker.pl", "ps"]
---> Running in cb5ade11e146
---> 94984ed5756d
Removing intermediate container cb5ade11e146
Successfully built 94984ed5756d

So that I can use it:


bash-3.2$ docker run --rm -it docker-perl
ID IMAGE COMMAND CREATED STATUS PORTS
e619112eae2f 10.10.10.2:5001/sve bash 1411104597 Up 7 days ARRAY(0x2b84a48)
363ec1c45841 10.10.10.2:5001/sve bash 1411104470 Up 7 days ARRAY(0x29bae20)

You can also run the container with bash – docker run --rm -it docker-perl bash so you can do some more testing, or try out more complex examples.

In this case, the ./Build test step probably needs to happen in the docker run phase, as it needs access to a working Docker daemon – this issue will be true for modules that talk to external resources.

I’ve made a pull request for the tiny changes to get me this far. Perhaps Dockerfiles like this could be a gateway into the world of contributing quick fixes for open source libraries.

Docker, containers and simplicity.

Docker Containers emulate Operating Systems, allowing you to build, manage and run applications and services. And you copy around your application, data and configurations.

I’ve now been working for Docker Inc. for 2 months. My primary role is Enterprise Support Engineer: I’m one of the guys that your company can turn to when the going gets tough, for training, or just generally to ask questions.

In these months, I’ve been working on Boot2Docker (OSX, Windows installers), our Documentation, and generally helping users come to terms with the broad spectrum of effects that Docker has on developing, managing and thinking about software components.

I’m still trying to work out ways to explain what Docker does – this is March’s version:

Virtual machines emulate complete computers, so you setup, maintain and run a complete Operating System, and copy around complete monolithic filesystem images.
Docker Containers emulate Operating Systems, allowing you to build, manage and run applications and services. And you copy around your application, data and configurations.

This might not quite feel right, given that images are build ‘FROM’ a base image – but one thought I have, is that as that base image (and most often some local modifications) are likely to be common to your entire infrastructure, that layer will be shared for all your containers. Chances are, you didn’t build it either – Tianon did :).

Solomon keeps reminding me that Dockerfiles are like Makefiles – and in the back of my mind, I think of our application image layers as packages, thin wrappers around applications that are then orchestrated together to produce your service. The base image you choose is only there to support that, and over time I’m sure we’ll simplify those much more.