Docker basics - connecting containers


Introduction

This is the second post of the Docker Basics series. Last time, we talked about what are images and containers and how to use them to achieve a simple use case - run a Scala REPL. If you haven’t read that yet and you’re interested, you can see it here :) You should also start with that post if you haven’t yet installed Docker, as in this post I assume you have it done.

THis time we’ll consider a scenario where we have two containers and we want them to talk to each other. The first use case that came to my mind when thinking about two cooperating containers is an application-database relation. Therefore, we’re going to create the following containers:

Very simple and illustrates the point. Let’s get going then!


One network to bind them

Linking containers together in the past was done with the –link flag. That method, however, is deprecated and should not be used. The reason I’m mentioning it is because there are quite a lot of tutorials out there that describe that method - you should know they’re not up to date.

The proper method of linking containers together is with the use of the new networking feature.

Let’s bring up our Docker console and run the docker network ls command. You should see something like this:

NETWORK ID      NAME    DRIVER  SCOPE
9872c9881f6e    bridge  bridge  local
6fc119c0ceda    host    host    local
c3fdf8d5c56e    none    null    local

These are the networks that are available by default:

If you need to know the details of a network, you can use the docker network inspect <name> command.

The recommended way to control which containers can communicate with each other is to use user-defined networks, which are simply networks that we create ourselves.

Let’s create one with docker network create my-network and docker network ls to see it on the list:

NETWORK ID      NAME        DRIVER  SCOPE
9872c9881f6e    bridge      bridge  local
6fc119c0ceda    host        host    local
c3fdf8d5c56e    none        null    local
19671b2b8b20    my-network  bridge  local

Our user-defined network is up and ready to be used, so let’s move on to a MySQL database server container now.


MySQL container

If you remember from the previous post, in order to spin up a container, we first need an image. In this case, we’ll use the default mysql image - pull it with a docker pull mysql command.

Once we have it, we can start a mysql server instance with the following command: docker run -d --name mysql-server --network my-network -e MYSQL_ROOT_PASSWORD=secret mysql

Let’s break it down into pieces:

Quite simple. Our MySQL server should be up and running, which we can check with a docker ps command.

Let’s now try to connect to our database server in order to create a database, a simple table and add some simple data. This will actually be the first demonstration that our network connects the two containers together as we need to start a separate container to connect to the existing server.

The command to do that is as follows (quite a long one - don’t forget the ' symbol at the end :) docker run -it --rm --network my-network mysql sh -c 'exec mysql -h"mysql-server" -P"3306" -uroot -p"secret"'

If everything is fine, we should see the following:

mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.18 MySQL Community Server (GPL)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

So, we just connected to our mysql server container from a separate container running the mysql shell :) Let’s break down the command, though:

Basically, this is where we could end this tutorial, but let’s create a Python script that will query our database from a separate container just for completion’s sake. Before we do that, though, we need to define some data in our database, so run the following while still connected to the mysql shell:

These will create a mydb database with a person table and a single row containing the Mick Jagger person entity. You can verify that by running SELECT * FROM person;

Alright, let’s now move on to the Python part. Oh, you can leave the mysql shell with Ctrl-D.


One script to query them all

In this part we’ll have a peek at something we have not yet discussed - creating our own image. I won’t go into details, as that’s a topic for a separate post, so please just assume a magic box approach for now :)

A note to Windows users: we need to create our folder and script inside the C:\Users\<someuser> directory due to Docker limitations on Windows. I believe this is not an issue when working with native Docker, though, but to avoid problems let’s stick to it.

Create a folder, for example C:\Users\myuser\my-script and inside that folder create a simple Dockerfile file (yes, no extension). The contents of the Dockerfile are as follows:

FROM python:2

WORKDIR /usr/src/app

RUN pip install MySQL-python

COPY . .

CMD [ "python", "./script.py" ]

As mentioned, I will not go into details of image creation in this post, so just a short explanation: this file will make our image extend the python:2 image, setup a working directory, install the MySQL-python package, copy the contents of the current directory to the working directory inside the container (which we defined with WORKDIR) and execute a script.py file.

Ok, so we now need our script.py file with the following contents:

#!/usr/bin/python

import MySQLdb

db = MySQLdb.connect("mysql-server", "root", "secret", "mydb")
cursor = db.cursor()
cursor.execute("SELECT * FROM person")
data = cursor.fetchone()
fname = data[0]
lname = data[1]
print "fname=%s, lname=%s" % (fname, lname)
db.close()

This script will connect to our MySQL database (remember the name of the container is the host name) and select data from our table.

From the Docker terminal, we first need to move to our newly created folder containing the Dockerfile and script: cd /c/users/myuser/my-script

Now, that we’re inside the proper directory, let’s build our image: docker build -t my-script . (don’t forget the dot at the end).

Our image is now built, we should see it when running docker image ls.

And now for the grand finale, run our newly created image: docker run -it --rm --network my-network my-script

The very humble output: fname=Mick, lname=Jagger, proves that our script, launched from one container, was able to connect to a mysql database in another container and query it, which concludes this post :)


Summary

Commands to take away: