A brief introduction to using docker

Posted by Steve on Wed 1 Jan 2014 at 19:34

This article is a brief introduction to docker, which is a utility for manipulating lightweight containers, allowing you to quickly spin up multiple isolated environments on a single host-machine.

To get started you need to ensure that you have a "modern kernel". Happily the kernel which is available in the Wheezy release of Debian GNU/Linux is recent enough to contain all the required features.

  • We'll come back to the kernel later in Appendix 1

Because docker uses the LXC container system you need to install that first:

apt-get install lxc

Once you've got lxc installed you should then download the docker command-line tool, which is what you'll use to interact with the container system:

root@shelob:~# wget https://get.docker.io/builds/Linux/x86_64/docker-latest -O /usr/local/bin/docker
root@shelob:~# chmod 755 /usr/local/bin/docker
  • NOTE: You should ponder the potential risk of pulling an unknown binary from a remote system.

Now that you have the docker tool installed you can start using it. What we're going to do, in brief is:

  • Get an image.
  • Start it interactively.
  • Show how to create named snapshots.
  • Create a useful docker image, in our case a system running memcached.

Because docker works with virtual images we need to find some, or create one. Finding them via the online registry is very simple, and will be a good starting point.

  • If you prefer you can learn how to make your own wheezy image in Appendix 2, via debootstrap.

Before any other steps may be completed you need to be running the docker daemon, so in one terminal please run:

root@shelob:~# docker -d
2014/01/01 18:55:32 WARNING: You are running linux kernel version 3.2.0-4-amd64, which might be unstable running docker. Please upgrade your kernel to 3.8.0.
[/var/lib/docker|fb570453] +job initapi()
[/var/lib/docker|fb570453.initapi()] Creating server
...

Once the daemon is running we can pull down some Ubuntu images via the internet in another terminal:

root@shelob:~# docker pull ubuntu

NOTE: This will pull down approximately 80Mb of data, so it might take a while to complete.

Once you've done that you should find that the images are visible in the output of the "docker images" command:

root@shelob:~# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              12.04               8dbd9e392a96        8 months ago        128 MB
ubuntu              latest              8dbd9e392a96        8 months ago        128 MB
ubuntu              precise             8dbd9e392a96        8 months ago        128 MB

Now we have some images we can launch one, interactively:

root@shelob:~# docker run -i -t ubuntu /bin/bash
root@6959e0664889:/# id
uid=0(root) gid=0(root) groups=0(root)
root@6959e0664889:/# df
df: cannot read table of mounted file systems: No such file or directory
root@6959e0664889:/# uptime
 18:57:10 up 2 days, 11 min,  0 users,  load average: 0.23, 0.15, 0.26
root@6959e0664889:/# exit
root@shelob:~#

Here we've launched a new guest instance, with the name "ubuntu", the two flags we used were :

  • -t - Allocate a psuedo-TTY
  • -i - Keep STDIN open.

Because we were running interactively, and then exited, any changes we made to the system would be lost. We can verify that by running the same thing twice - once making changes, and once to look for them:

root@shelob:~#
root@shelob:~#  # First run
root@shelob:~#
root@shelob:~# docker run -i -t ubuntu /bin/bash
root@1b896de6091c:/# rm /etc/passwd
root@1b896de6091c:/# exit

root@shelob:~#
root@shelob:~#  # Second Run
root@shelob:~#
root@shelob:~# docker run -i -t ubuntu /bin/bash
root@6c7b72d5dc21:/# ls /etc/passwd
/etc/passwd
root@6c7b72d5dc21:/# exit

You'll notice that in both case the prompt changed, this prompt is the ID of the temporary running system.

You can use this ID to create a snapshot of your system, after making any changes which you wish to preserve. For example you might find that the system is missing your favourite editor so you could install it via "apt-get install vim". By default if you exited after running that command the change would be lost.

So let us update our image to contain some additional utiltities:

root@shelob:~# docker run -i -t ubuntu /bin/bash
root@96a3ca7208f1:/# apt-get install screen less
Reading package lists... Done
..
Unpacking less (from .../less_444-1ubuntu1_amd64.deb) ...
Selecting previously unselected package screen.
Unpacking screen (from .../screen_4.0.3-14ubuntu8_amd64.deb) ...
Setting up less (444-1ubuntu1) ...
root@96a3ca7208f1:/# apt-get install screen less

At this point we have a running instance which we've installed two extra packages to. We want to keep this as our base for future work, so we'll give this instance a name:

root@shelob:~# docker commit 96a3ca7208f1 myname/base
00a50d7bcac3e905fb5a0c159e13a4a05cdca41378fd5064f0b792716b5e43f5

NOTE: Providing you can still remember the numeric ID you can carry this operation out even when you've exited the session.

At this point we've created a snapshot of our Ubuntu system which contains the extra screen & less packages. This snapshot will be visible in the output of "docker images":

root@shelob:~# docker images  | grep myname
myname/base         latest              00a50d7bcac3        47 seconds ago      141.2 MB

As you'd expect you can now launch that instance by name:

root@shelob:~# docker run -i -t myname/base /bin/bash
root@8294c9a0ec40:/# which less
/usr/bin/less
root@8294c9a0ec40:/# exit

Because the new docker instances are running as containers, via lxc, none of your home directories, or other mount-points, will be available to it. You can fix this via a bind-mount:

root@shelob:~# docker run  -v /home:/home -i -t myname/base /bin/bash
root@49277c40b630:/# ls /home/
litecoin  lost+found  skx
root@49277c40b630:/# exit

The "-v /home:/home" flag we passed to docker specifies that the /home directory on your host should be mounted at /home within the guest.

At this point we're almost done, having demonstrated how to create named snapshots, mount external directories, and generally have fun with docker.

The remaining example demonstrates how you can use port-forwarding to run services inside a docker-container. In our case we'll demonstrate running memcached instead a container.

As before we'll first launch a container, then configure it, and tag the container with a name. Then we'll enable port-forwarding so that the deamon inside the container can be accessed remotely.

To get started we'll launch another instance, and install the memcached server upon it:

root@shelob:~# docker run -i -t ubuntu /bin/bash
root@1871e9964507:/# apt-get install memcached
Reading package lists...
..
root@1871e9964507:/# sed -i "/127.0.0.1/d" /etc/memcached.conf
root@1871e9964507:/# exit

Now we've installed memcached, and removed the configuration-file item which causes it to listen upon 127.0.0.1. Because we've made changes to the system we now need to tag it, so we can launch it again in the future:

root@shelob:~# docker commit 1871e9964507 myname/cache
557f7d1a3a7385a9e031241ca03584a1b51e14ec1cf76e0219841ced087f36f9

Now that we've done that we have a named container with memcached installed upon it, but we need to launch it in order to use it - and to be useful we need to forward a port to the container so that we can actually access it.

Much like the bind-mount example we saw earlier we nominate an external port, and an internal port. In our case we want to forward port 3000 on the local system to port 11211 (which is the port that memcached listens upon) to the guest.

Before we proceed we should ensure that port-forwarding is generally available:

root@shelob:~# sysctl  "net.ipv4.ip_forward=1"
net.ipv4.ip_forward = 1

With that out of the way we launch docker again, with the port-forwarding option "-p":

root@shelob:~# docker run -d -p 3000:11211 myname/cache /usr/bin/memcached -u memcache
be5d23555f200a54f63d9386604955448bb0ba5fb3e60cdb2e639eb72b666259

Now we've launched it we can contact localhost:3000 which will then be forwarded to port 11211 within the container:

root@shelob:~# echo -e "stats\nquit" | nc  localhost 3000  | head -n 2
STAT pid 1
STAT uptime 32

This is the first time we've launched a container in the background, so if you do wish to stop it you should run the "docker stop" command, with the ID you were given when you launched it:

root@shelob:~# docker stop be5d23555f200a54f63d9386604955448bb0ba5fb3e60cdb2e639eb72b666259

If you forgot the ID which was printed when the guest was started you can look it up via "docker ps":

root@shelob:~# docker ps
CONTAINER ID        IMAGE                     COMMAND                CREATED             STATUS              PORTS                      NAMES
41a5f4a33720        myname/cache:latest       /usr/bin/memcached -   3 seconds ago       Up 2 seconds        0.0.0.0:3000->11211/tcp    lonely_bardeen

And using that short ID you can stop it:

root@shelob:~# docker stop  41a5f4a3372

Hopefully that introduction was useful, I suspect the only part that needs explanation was the way that we launched memcached - because the container boots under the host system and doesn't run any init-system we had to manually launch the deamon ourselves.

 

A1: Testing Your Kernel

To test your kernel you can run the lxc-checkconfig tool, which was installed when you ran "apt-get install lxc".

root@shelob:~# lxc-checkconfig
Kernel config /proc/config.gz not found, looking in other places...
Found kernel config file /boot/config-3.2.0-4-amd64
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/pts instances: enabled

--- Control groups ---
Cgroup: enabled
Cgroup clone_children flag: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
Cgroup cpuset: enabled

--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
File capabilities: enabled

Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig

If there are any errors these should be displayed as guidance, otherwise if all items show as enabled you're good to proceed.

 

A2: Building Images

If you don't trust the images which are available over the internet it is relatively straight-forward to create your own.

The docker tool has two commands for dealing with image and tar files:

  • "docker import .." - To import a tar-file to an image.
  • "docker export .." - To export an image to a tar-file.

For example here is the process of exporting a guest:

root@shelob:~# docker export caafbe5d8f34 > mem.tar
root@shelob:~# tar tf mem.tar | head -n 5
./
./etc/
./etc/hosts
./etc/hostname
./etc/resolv.conf

Importing a guest from a debootstrap-created system is almost as easy as you would expect. First of all we create the new system to the temporary location wheezy/:

root@shelob:~# debootstrap wheezy wheezy/
I: Retrieving Release
I: Retrieving Release.gpg
...
I: Configuring tasksel...
I: Configuring tasksel-data...
I: Base system installed successfully.

Once we have that local chroot we can build a tar-file from it, and then feed it to the import process, giving the new image the name debootstrap/wheezy:

root@shelob:~# cd wheezy && tar cf ../wheezy.tar .
root@shelob:~# cd ; docker import - debootstrap/wheezy < wheezy.tar

Once that is done you can see the image is present:

root@shelob:~# docker images | grep debo
debootstrap/wheezy   latest              fb59cf7301bb        6 seconds ago       218.5 MB

And this can now be used, as you would expect:

root@shelob:~# docker run -i -t debootstrap/wheezy /bin/bash

 

A3: cgroup setup

One thing not explicitly mentioned is that you might receive an error about a missing cgroup mount when you try to launch a container. This error will look like this:

root@shelob:~# docker run -i -t debootstrap/wheezy /bin/sh
lxc-start: No cgroup mounted on the system
..
[error] commands.go:2445 Error getting size: bad file descriptor

To solve this you need to ensure you have a cgroup filesystem mounted. You can do this by running:

# echo "cgroup /sys/fs/cgroup cgroup defaults 0 0" >> /etc/fstab
# mount /sys/fs/cgroup

This wasn't mentioned in the article originally because I already had this mount present upon my host.

 

 


Posted by Anonymous (78.42.xx.xx) on Fri 10 Jan 2014 at 18:44
s/LXE/LXC/g

[ Parent | Reply to this comment ]

Posted by Steve (90.220.xx.xx) on Fri 10 Jan 2014 at 19:47
[ View Steve's Scratchpad | View Weblogs ]

You're right. I'm not sure how I managed to make that mistake, but I've updated things now.

Steve

[ Parent | Reply to this comment ]

Posted by Anonymous (82.169.xx.xx) on Tue 28 Jan 2014 at 09:49
The output from the lxc-checkconfig tool should not be relied upon too much. See https://lkml.org/lkml/2013/7/15/368 for the gospel: "Any user program that depends on [a kernel config file] is broken by design."

[ Parent | Reply to this comment ]

Posted by Anonymous (157.159.xx.xx) on Wed 5 Mar 2014 at 17:36
Note that Docker packages are now available in Debian (called docker.io)

[ Parent | Reply to this comment ]

Posted by Anonymous (157.159.xx.xx) on Thu 6 Mar 2014 at 13:01
Note that I have initiated https://wiki.debian.org/Cloud/CreateDockerImage for images creation

[ Parent | Reply to this comment ]

Posted by mverwijs (62.212.xx.xx) on Thu 6 Mar 2014 at 20:04
I kept running into issues when upgrading the package 'initscripts'. It calls /sbin/ischroot to check if it is in a chroot, and fails. Therefor, it assumes that it is on baremetal, not in a virtual environment. And the upgrade fails.

I've hacked around this by:

mv /sbin/ischroot /sbin/ischroot.off
ln -s /bin/true /sbin/ischroot

This tricks the postinst of initscripts that it is indeed in a chroot, and the install continues.

[ Parent | Reply to this comment ]

Posted by mverwijs (62.212.xx.xx) on Thu 6 Mar 2014 at 20:30
Actually, the path is wrong.

Here is the fix, making the change permanent using dpkg-divert:

dpkg-divert /usr/bin/ischroot
ln -s /bin/true /usr/bin/ischroot

[ Parent | Reply to this comment ]

Posted by pradeepb2b (182.70.xx.xx) on Sat 12 Jul 2014 at 09:43
Hi,

I am trying to run litecoin cryptocurrency daemon under docker, And I am trying to dockerrize using litecoind daemon and i did this:

docker run -d -p 3000:9332 -m 2048m litecoin litecoind

but it seems exit all time and dont start as daemon, can you tell me whats wrong here?

[ Parent | Reply to this comment ]

Sign In

Username:

Password:

[Register|Advanced]

 

Flattr

 

Current Poll

What do you use for configuration management?








( 265 votes ~ 1 comments )