Docker 101

Tutorials – Docker

Author(s):

You might think Docker is a tool reserved for gnarly sys admins, useful only to service companies that run complicated SaaS applications, but that is not true: Docker is useful for everybody.

Docker [1] manages and runs containers, a thing that acts like an operating system. It is similar to a virtual machine, but a container uses a lot of the underlying operating system (called the "host") to work. Instead of building a whole operating system with emulated hardware, its own kernel, and so on and so forth, a container uses everything it can from the underlying machine, and, if it is well-designed, implements only the bare essentials to run the application or service you want it to run.

Whereas virtual machines are designed to run everything a regular machine can run, containers are usually designed to run very specific jobs. That is why Docker is so popular for online platforms: You can have a blogging system in one container, a user forum in another, a store in another, and the database engine they all use in the background in another. Every container is perfectly isolated from the others. Docker allows you to link them up and pass information between them. If one goes down, the rest continue working; when the time comes to migrate to a new host, you just have to copy over the containers.

But there's more: Docker is building a library of images [2] that lets you enjoy whole services just by downloading and running them. These libraries are provided by the Docker company or shared by users and go from the very, very general, like a WordPress container [3], to the very, very niche, like a container that provides the framework to run a Minetest [4] server [5].

This means exactly what you think it means: Download the image, run it (with certain parameters), and your service is ready, madam – no dependency hunting, very little configuring, and not much more beyond hooking up the service to a database (running in another container) and setting your password as the service administrator.

Getting Started

To enjoy the marvels of Docker, first install it on your box. Most, if not all, of the main distributions have relatively modern versions of the Docker packages in their repositories. In Debian, Ubuntu, and other Debian-based distributions, look for a package called docker.io. In Fedora, openSUSE, Arch, Manjaro, Antergos, and others, it is simply docker. You will also find official and updated versions of the software for several systems at the Docker website [6].

Once Docker is downloaded and installed, check that the daemon is running:

systemctl status docker

If it is not, start it and enable it so that it runs every time you boot your machine:

sudo systemctl start docker
sudo systemctl enable docker

Now Docker is running, it is time to get some images.

Imagine

An image is similar to an ISO image you would use to install a GNU/Linux operating system, except you don't need to burn it to a DVD or USB thumb drive.

You can use the docker utility to search for images like this:

docker search peertube

Docker will show you all the available images that contain the word "peertube" in the name or description (Figure 1). It will also tell you its rating given by users – more stars is better.

Figure 1: You can search Docker for images just as if you were using your software manager.

To install an image, you can pull it from a repository:

docker pull chocobozzz/peertube

This will download a PeerTube image (see the "What is PeerTube?" box) from Docker's repository and add it to your roster.

What is PeerTube?

PeerTube [7] is a video portal service akin to YouTube and Vimeo (Figure 2), but without any of the dumb restrictions of those closed and proprietary alternatives. It is called PeerTube because anyone can set up a server and join a federated network of PeerTube instances; any video a user uploads to one instance gets propagated to the other instances. All instances share the load of streaming the videos to visitors using P2P technology.

Figure 2: PeerTube is a much more democratic and freedom-respecting video platform than YouTube and Vimeo.

You can check that the image is now installed by running:

docker image list

Among other things, the list will give you a unique identifier (just in case you have two images with the same name) and will tell you how much space the image takes up on disk.

You could also just run the image, even before downloading it. The command

docker run chocobozzz/peertube

will have Docker look for the PeerTube image on your hard disk, and, if it can't find it, it will download it, drag in all the dependencies it needs (including other images, like an image for a PostgreSQL server), and run it (Figure 3, top).

Figure 3: Running an image not already on your hard disk makes Docker download it and then run it.

When you run an image, Docker creates a container with the software running inside it. In many cases, it will show the software's output so you can check that everything is working correctly (Figure 3, bottom). In this case, the output tells you that your PeerTube instance is running on localhost. However, if you visit http://localhost:80 with your browser, you probably won't see the PeerTube interface, because Docker sets up its own network for its containers.

To know which IP PeerTube is running on, first list your running docker containers like this:

docker container list

This will give you a container ID (something like 8577b5867b93) and a name that Docker makes up by mashing together random words (something like hopeful_volhard) for your container. You can use either to identify your container and get some details using:

docker container inspect <container_id_or_name>

Toward the end of the output, you will see a line that says "IPAddress" : and, well, an IP address. If you haven't changed Docker's default configuration, it will be something like 172.17.0.2. Point your browser at that, and … Voilà! PeerTube (Figure 4).

Figure 4: Setting up a PeerTube video platform requires virtually no work if you use a Docker image.

You can stop a container with stop:

docker stop <container_id_or_name>

And start it again with start:

docker start <container_id_or_name>

Using run will create a completely new Docker container from your original image. If you have made changes (like created or modified a file) within a Docker container of the same image, your changes will not be in the new container. The "Getting Rid of Stuff" box explains how you can cleanly remove both containers and images.

Getting Rid of Stuff

List the images you have installed and use the ID of the one you want to remove to delete it:

docker image rm <id_number>

You may get an error informing you that the image is in use or needed by a certain container. Note that, even if all your containers are stopped, they are not necessarily removed and are sitting there waiting to be restarted. You can see all you containers, even those that are not running, with:

docker container list --all

and then you can remove the offending container with:

docker container rm <container_id_or_name>

After that, you can go back and remove the image.

Inside the Container

Finishing off the configuration of PeerTube would require an article of its own (watch this space!), so I'll move on to a more generic image for experimentation. Grab yourself a Linux distro image, like Ubuntu,

docker pull ubuntu

and run it with:

docker run -i -t ubuntu bash

After a few seconds, Docker will dump you into a shell within the container. Unpacking that last command line, the -i option tells Docker that you want an interactive exchange with the container, which means that the commands you type into the host's stdin (usually your shell) will be pushed to the Docker image. The -t option tells docker to emulate a terminal over which you can send the commands. You will often see both options combined together as -it.

Next comes the ID or name of the image you want to interact with (ubuntu in this case). Finally, you pass the name of the command you want to run, in this case a Bash shell.

Find out what the name or ID of the container is (docker container list), and you can open a new shell in the running container using the exec command:

docker exec -it <container_id_or_name> bash

The instruction above logs you into the container, and you can install and remove software, edit files, start and stop services, and so on.

To stop the shell in the container, issue an exit as you would do to exit a regular shell. Once you log out from all the shells, and as long as no other processes are executing, your Ubuntu container will stop. Docker containers are designed to run one process and one process only. Although you can run more, this is frowned upon by Docker purists and considered suboptimal. When that unique process ends, Docker is designed to close down the container.

However, if you want to keep a container running in the background (so you can have it run a non-interactive command sent to it from time to time), you can do this:

docker run -t -d <image_id_or_name>

As you saw above, -t tells Docker to create a faux terminal. The -d option stands for detached and tells Docker to run the container in the background.

To run a command non-interactively in a running container and have the output appear under the command, enter

docker exec <container_id_or_name> ls

which will show the default working directory's contents. You can also show the contents of a directory that is not the default by adding the path, as you would with a regular ls command:

docker exec <container_id_or_name> ls </path/to/container/directory>

Talking of working directories, if you are not sure which is the container's current working directory, try this:

docker exec <container_id_or_name> pwd

Another thing you can do is share directories between the host and a container. For example:

docker run -it -v /home/<your_username>:/home/brian ubuntu bash

The -v option takes the path to the directory on the host (in this case, your own home directory) and maps it to the directory within the container. If either of these directories do not exist, Docker will try and create them for you.

Once you have shared your directory, from within the container, ls the /home/brian directory, and you will see the files from your own home directory. If you execute touch /home/brian/from_docker.txt from inside your container, you will see the file from_docker.txt pop up in your home directory on the outside.

This is very useful for when you want to use a Docker container to do some dirty work for you, like when you want to make an app for Android.

Docker for Developers

More precisely I should call this section "Docker for Fly-By Developers," because everybody likes a spot of coding, right?

One would presume developing for mobile devices would have become democratized by now. It is true that there are plenty of frameworks that let you use your favorite programming language to create apps for the likes of Android; however, setting up the SDK, NDK, libraries, dependencies, toolchains, and so on is a bit complicated … . No! Scratch that. More like: "Setting up the Android SDK, NDK, and so on is an absolute hell-hole of broken dependencies that will drive the hardiest among us to total and irredeemable insanity." There, much better.

What is the lazy and cavalier programmer to do? Use Docker, of course.

Turns out there are Docker images that provide all the hooks and doodads for Cordova (see the "Cordova" box). They also provide a correct and working installation of the Android tools you need to build and deploy your apps.

Cordova

Cordova [8] is a framework that allows you to create Android, iOS, and Windows apps using HTML, JavaScript, and CSS. Its aim is to democratize the creation of apps for mobile devices, especially Android. However, the complexity of installing the Android bits and pieces and making it work with a real phone dampens this lofty goal.

To get started, check what the Docker repository has to offer,

docker search cordova

and grab the image with the highest score. At the moment of writing, that was beevelop/cordova:

docker pull beevelop/cordova

Cordova provides a way of generating a skeleton app that you can run to test things, and later you can expand it to include your own features.

To build it, move to the directory to the location in which you want to store your app and run:

docker run --rm -i -v /$PWD:/workspace -w /workspace --privileged beevelop/cordovacordova create hello come.example.helloHelloWorld

Don't panic: It isn't as hard as it looks. The first option, --rm, makes sure that Docker deletes the container after it has run the command you pass to it. Because you don't want the container hanging around after every step of the process, this is a good idea.

You already know what -i does: It gives you interactivity with the container. If there are any questions to answer or fields to fill in, -i makes sure you will be able to do that.

The -v option is also familiar: It mounts the directory from the host entered before the colon into the container at the directory indicated after the colon. In this case, it will mount your current directory ($PWD) into a directory called workspace/ within the container.

The option -w is new, but not difficult to grasp: It tells Docker which directory it should use as the working directory. In this case, all the files Cordova generates will go into the workspace/ directory you created with the -v option.

The --privileged option gives superuser-like privileges to the container and allows Cordova to read from and write to the directories you mounted.

The cordova create hello come.example.hello HelloWorld chain is the Cordova command line to run. The cordova create part creates a new project, and hello is the directory where all the projects files will live. The come.example.hello part provides the basic building template for the HelloWorld project (you can call it something else, by the way) and is part of the standard Cordova package.

After running the instruction, a hello/ subdirectory will pop up in your current directory that contains a basic framework for an app. I will not go into each of the bits and pieces, because I will be looking at Cordova-based mobile applications in a future article, but you should now change into your hello/ directory, because the rest of the instructions in this tutorial require that you execute them from within a Cordova-generated folder.

So far, Cordova has generated a platform-neutral application. Because Cordova allows you to build for more than one platform, the next step is to download all the stuff you need for Cordova to adapt the app to a specific platform.

To see what platforms are available, you can enter:

docker run --rm -i -v /$PWD:/workspace -w /workspace --privileged beevelop/cordovacordova platform list

The only new thing here is cordova platform list, which is self-explanatory (Figure 5).

Figure 5: Cordova tells you for which platforms you can build your app.

To add a platform (e.g., Android), you use cordova platform add:

docker run --rm -i -v /$PWD:/workspace -w /workspace --privileged beevelop/cordovacordova platform add android

You can add as many platforms as you like.

The next step is building the code for the platform(s) you just added:

docker run --rm -i -v /$PWD:/workspace -w /workspace --privileged beevelop/cordovacordova build

After some rather profuse output, Cordova informs you it has built an Android Package Kit (APK) and placed it into the platforms/android/app/build/outputs/apk/debug/ subdirectory.

You could now copy the APK to your device and install it by hand, or you can have Cordova do that for you and run the app as a test.

The first matter of business is to make sure Cordova can talk to your device. First, make sure your phone is ready and in development mode. Follow the instructions in the "Prepare Your Phone" box. Second, run the instruction:

Prepare Your Phone

To make your phone ready for development, go to Settings | About phone and scroll down until you see the Build number section. Tap on that several times until your phone tells you that you have become a developer.

Connect your phone to your computer using a USB cable and move back to Settings. Now, you will see a new submenu called Developer options. Tap on that and scroll down until you see the USB debugging option. Activate it.

Your phone is ready.

docker run --rm -i --privileged -v /dev/bus/usb:/dev/bus/usbbeevelop/cordova adb devices

In this command line, you are sharing your /dev/bus/usb/ directory with your container, since that is where Cordova will find your phone. Cordova itself is using the Android Debug Bridge (adb) to try and locate your phone. The devices option shows a list of connected devices.

The first time around, your device may show up as unauthorized. This is normal. Go into Settings | Developer options and make sure you have enabled USB debugging. While you are there, again run

docker run --rm -i --privileged -v /dev/bus/usb:/dev/bus/usbbeevelop/cordova adb devices

and a dialog will pop up on your phone asking you to authorize your computer (Figure 6). Give your computer permission, and try listing your devices again. Your phone should now appear as available. Now you can push your app to your phone:

docker run --rm -i -v /$PWD:/workspace -w /workspace --privileged-v /dev/bus/usb/:/dev/bus/usb/beevelop/cordova cordova run android
Figure 6: You need to authorize your computer before you can transfer your app to your phone.

Cordova will automatically install and run the app, so you can check that everything is okay (Figure 7).

Figure 7: Cordova's Hello World app running on a phone.

Conclusion

Docker is being marketed as a solution for professional sys admins who manage dozens of services on busy server farms. True, Docker and all the other technologies built up around it are super-useful for those guys.

However, it is also useful for the rest of us: the casual home admin or amateur developer who wants to tinker or build stuff for personal use. That is why I will be incorporating Docker into my toolbox in future installments of this series.

In the meantime, have fun playing with Docker!