Image for post
Image for post

What Is Docker and Does It Have Benefits for Desktop Developers?

Most developers today to some sort of server based development, and are familiar with things such as Vagrant, Chef, Puppet, Virtual Machines etc, as a desktop developer I have no clue what these things are.

If you like me are one of those clueless desktop developers you have probably heard about Docker, but it is not clear whether it is useful for you, or what it even is.

The short answer, yes, docker is really useful to desktop developers as well.

The reason is that it allows you all the software, configurations and tweaking of your development environment into one simple bundle which you can move and install onto any other computer.

Ok, so when I discussed this with a colleague, he wasn’t convinced why this was even necessary. Truth be told, it might not matter to a lot to many developers. It depends entirely on your habits.

If your development consists of installing one IDE, download your code and get cracking, then docker wont matter to you.

So let me explain me explain my habits and why docker matters. When I develop I don’t use the computer as it is out of the box. There is a whole bunch of software I like to install and use.

  1. fish shell. I use the command line a lot, but I am not a power user and don't want to remember all sorts of arcane shell lore. Fish is for people who want easy to use shells.
  2. bpython. Occasionally I use python and the built in REPL is terrible, but bpython is really nice. I always want that available.
  3. cloc counts lines of source code. Simple unix tool I always like handy.
  4. julia this is my bread and butter scripting language. While I don't do professional Julia programming I use it for all sorts of scripting needs and automation.
  5. tree for nice display of directory hierarchies.
  6. Shell configurations. Both for bash and fish I might have different configurations of the shell prompt, handy scripts, environmental variables.
  7. scons build system I use for my C++ projects.
  8. Solarized color scheme for the terminal. I find the strong contrasts on the default terminal color schemes distracting.

Ok I could go on, but you probably get the point. There a lots of little things to modify. For my work there are a whole bunch of particular libraries I need installed, to get our C++ project compiled. And I need all of these with particular versions which might not be the versions the OS I use ship with.

We support Red Hat, but I prefer to work on Ubuntu at work, while I work on a Mac at home. So there are a lot of different systems to manage.

This gives me two problems.

  1. I either have to develop on Red Hat which I don’t like or fiddle with Ubuntu a lot to get it to compile our software.
  2. macOS isn’t a Linux so I can’t compile at all at home.

Docker can solve all these problems. Emphasis on can as I am new to Docker.

With Docker I can reproduce a Red Hat environment on my Ubuntu box and Mac, or rather almost as I think I will have to use CentOS as a substitute for Red Hat.

People usually describe docker as software which manages containers, but since a lot of desktop developers probably aren’t that familiar with containers, I’ll try describing it a different way.

Think of sandboxes, whether for iOS applications or a web browser. It means the app running in your sandbox only sees part of the operating system or whatever environment the sandbox is letting it see. Docker containers are similar.

So e.g. I can create a Red Hat based docker container on my Mac and when I run an app or e.g. the shell all I will see is a Red Hat looking system. My macOS becomes invisible while running a shell process inside the container.

Sounds a lot like a Virtual Machine, like Virtual Box, right? And on Mac and Windows Docker does in fact rely on a Virtual Machine, but that is a distracting detail. It is easier to understand Docker if we consider what it is like on Linux. On Linux there is no Virtual Machine involved. Instead every docker container reuses the same Linux kernel. It is just the environment, consisting of directories, libraries, configuration files etc which will look like different Linux distributions.

It is easies to understand this with some examples. My goal is to get a Linux environment on my Mac which allows me to compile and run programs as if I was on a particular Linux distribution.

So these are the things I want to do.

  1. Start with some Linux distribution.
  2. Install and configure it with software I want on it, such as fish and scons.
  3. Save these changes into a container and move this container to a different computer.

First I got to tell you that I have simplified my explanation so it isn’t entirely true how I’ve explained it. We don’t move docker containers around but rather what is called a docker image.

The makers of Docker has something called Docker Hub, where people have contributed lots of Docker Images. An Image can be a particular Linux distribution or something more elaborate like a Linux distribution with a database and web-server installed on it.

We can get hold of one of these ready made images like this:

$ docker pull centos:6

This specifies image name centos and tag 6. The tag tells docker which CentOS version we want.

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos 6 30365b2e827c 2 weeks ago 195 MB

Images are dead things. They don’t run. They are like programs stored on a disk. For a program to do something it has to be turned into a process, by running the program.

Running an image is what creates a container. Here we create a container based on CentOS to run the echo shell command on Linux:

$ docker run  centos:6  echo "hello world"
hello world

Not terribly useful, so lets run bash instead, which allows us to interact with our Linux system.

$ docker run  centos:6  bash

Oops that doesn’t work. It just returns immediately. Why? Because bash doesn’t know where to get our keyboard input from. What we want is to redirect STDIN to bash in our container.

We do that with the -i switch:

$ docker run -i centos:6  bash

That doesn’t quite work either. While we can issue commands and get output from them:

ls
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
sbin
selinux
srv
sys
tmp
usr
var

The regular text from the terminal telling us where we are in the filesystem, show a prompt etc is missing. And when issuing shell commands there is no formatting on the output:

cd var
ls
cache
db
empty
games
lib
local
lock
log
mail
nis
opt
preserve
run
spool
tmp
yp

We we need in addition is the -t switch which gives terminal output and formatting.

$ docker run -it centos:6  bash
[root@f1b5e4318dd3 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root sbin selinux srv sys tmp usr var
[root@f1b5e4318dd3 /]#

If we open another terminal on the mac we can run it more times. Lets do that and create a file.

$ docker run -it centos:6  bash
[root@981f7f1ea9be /]# touch HELLO
[root@981f7f1ea9be /]# ls
HELLO bin dev etc home lib lib64 lost+found media mnt opt proc root sbin selinux srv sys tmp usr var
[root@981f7f1ea9be /]#

If we go back to our first terminal session and write ls you.

[root@f1b5e4318dd3 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root sbin selinux srv sys tmp usr var
[root@f1b5e4318dd3 /]#

You can see there is no HELLO file there. That is because every time you run a docker image, you create an entirely new container, which is its own little world. Just as when you run a program twice, the two processes you create don't share any state.

And just like you can list active processes with the unix command ps you can use docker ps to list running docker containers. In the example below I've removed some columns for clarity:

$ docker ps
CONTAINER ID IMAGE COMMAND STATUS NAMES
981f7f1ea9be centos:6 "bash" Up 1 minutes happy_galileo
f1b5e4318dd3 centos:6 "bash" Up 2 minutes practical_perlman

So you can see we are now running two separate containers.

docker ps shows some handy information such as a container ID we can use to refer to a particular container, e.g. when we want to stop it, remove it or restart it. Often one can also use on of the funny names docker creates on the fly like happy_galileo and practical_perlman. And of course we can see which image the docker container is based on and what command is being run inside it.

Before we get to actually install stuff in our containers and setup some kind of development environment, I’d like to show you how we work with containers.

Just like a process containers can be paused, restarted and killed. If we exit the shell, the container stops running.

[root@f1b5e4318dd3 ~]# exit
exit
$

If we check the status you can see that we have on less container running:

$ docker ps
CONTAINER ID IMAGE COMMAND STATUS NAMES
981f7f1ea9be centos:6 "bash" Up 5 minutes happy_galileo

But the container is not gone. With the -a switch we can see containers which still exist but are no longer running (edited for clarity).

$ docker ps -a
CONTAINER ID IMAGE COMMAND STATUS NAMES
981f7f1ea9be centos:6 "bash" Up 5 minutes happy_galileo
f1b5e4318dd3 centos:6 "bash" Exited (0) 1 min ago practical_perlman

We can however restart it.

$ docker restart practical_perlman
practical_perlman

Now we can see it shows up as a running container.

$ docker ps
CONTAINER ID IMAGE COMMAND STATUS NAMES
981f7f1ea9be centos:6 "bash" Up 8 minutes happy_galileo
f1b5e4318dd3 centos:6 "bash" Up 3 seconds practical_perlman

Except how exactly are we supposed to interact with our container now?

We use attach, which will get us into the previously running command.

$ docker attach  practical_perlman

However if we want to run a different command with a specific existing container, then we use exec:

$ docker exec practical_perlman echo "hello"
hello

If we want to kill a container, not just stop it we use rm. Lets get rid of the other container.

$ docker rm happy_galileo
Error response from daemon: You cannot remove a running container 981f7f1ea9be187093201be08c9cdf7329e872efaeb31d4a5fc5c75fd484a673. Stop the container before attempting removal or use -f

Ops it is still running so we got to actually stop it, before we can remove it.

$ docker stop happy_galileo
happy_galileo
$ docker rm happy_galileo
happy_galileo

Lets get back to our old container

$ docker attach  practical_perlman

And while inside the container we’ll install some software e.g. scons build system.

[root@f1b5e4318dd3 /]# yum install scons
Loaded plugins: fastestmirror, ovl
Setting up Install Process
Loading mirror speeds from cached hostfile
* base: ftp.uninett.no
* extras: ftp.uninett.no
* updates: centos.vianett.no
Resolving Dependencies
--> Running transaction check
---> Package scons.noarch 0:2.0.1-1.el6 will be installed
--> Finished Dependency Resolution
...
Running Transaction
Installing : scons-2.0.1-1.el6.noarch 1/1
Verifying : scons-2.0.1-1.el6.noarch 1/1

Installed:
scons.noarch 0:2.0.1-1.el6

Complete!
[root@f1b5e4318dd3 /]# exit

Ok, we could go on and install more software but you get the idea. What we really want to do is bundle this up in some package that we can move to another computer and start working with everything in our development environment setup.

Turns out we can do that with commands which reminds me of committing changes to a git repo.

It is super simple. First argument is the name of the container we want to turn into an image, and the second argument is the name of our new image, centos-scons in this case.

$ docker commit practical_perlman centos-scons
sha256:88404002d1719848a5c172d48b238055fbd91209aff23f9c8312eb7b07768802

Of course you can call it whatever you like. If we list all our docker images we can see it pops up in the list:

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos-scons latest 88404002d171 57 minutes ago 246 MB
centos 6 30365b2e827c 2 weeks ago 195 MB

We can of course instantiate containers from this image as any other image. What is cool about this is that no matter how much complicated fiddling you do inside your docker image to get your to compile and run, you never have to repeat that.

$ docker save -o my-image.tar centos-scons

This saves our centos-scons image into a tar file named my-image.tar.

Just to test loading of this exported image, we could delete the image that docker manages with the rmi command.

$ docker rmi centos-scons
Untagged: centos-scons:latest
Deleted: sha256:88404002d1719848a5c172d48b238055fbd91209aff23f9c8312eb7b07768802
Deleted: sha256:e5bd9886793ed4dba5e12c95cd5fd51287b0628107285a15f17611116579c3bf

If you do a docker images you will then se it is gone from the list.

We can then use the load command to get our image back:

$ docker load -i my-image.tar
b2596f85fe7d: Loading layer [==================================================>] 52.06 MB/52.06 MB
Loaded image: centos-scons:latest

Do a docker images to see that the image is back.

One of the IDEs I use for C++ development is Qt Creator. The problem with Docker containers is that programs running in them can’t directly display anything to the screen.

The second problem is that you might not want to use whatever IDE you can install in your docker image.

But there are solutions for this. E.g. with Qt Creator you can use remote debugging. Basically you setup a debug server in the container:

CLion from JetBrains also seem to support remote debugging with GDB. Mind you I have not actually gotten to try any of these solutions. There are even descriptions of how to do LLDB remote debugging with Apple’s Xcode, but I haven’t tried that either.

But lets address the first question. Since Linux applications draws to screen using a client-server architecture, one can actually start an X Server on macOS and then have GUI applications in your docker containers redirect their drawing commands over sockets to your macOS X Server.

The steps involved are:

  1. Install and use XQuartz as an X Server on macOS.
  2. Configure it to allow connections from network clients.
  3. Set the DISPLAY variable in your docker container to point to your mac X server.

Here is a more thorough explanation of the steps by Fredrik Averpil.

Gavin Fleming also has a good explantation of this. I will write about this as well once I’ve tried it out myself.

You can create however complicated, difficult to setup development environment in a docker container as you like. Then with docker commit you can store that to docker image, which you can stuff in a file with docker save, move it to another computer.

And in the last part I went through the info I had dug up about how you can e.g. use a OS X development tool to debug a Linux GUI application running inside a docker container.

Written by

Geek dad, living in Oslo, Norway with passion for UX, Julia programming, science, teaching, reading and writing.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store