Running pre-made images is very useful and can save you a lot of time, but at some point you will want to create your own images.

The training/webapp image is limited in that it can only say "Hello World" or "Hello Your Name", let’s see if we can expand on the original image to display a value stored in a Postgres database.

1. What do we need to change?

postgres container

Here is an overview of what we need to change (detailed instructions below):

  1. Expand the original image

    1. Install Python dependencies in the container to enable connections to a Postgres database

    2. Replace the original Python application with our new version that uses the database

  2. Setup a docker network so that the webserver can get its data from the database container

  3. Run a database with data

    1. Run a Postgres database image

    2. Add our data to the running database

  4. Run the newly built image

    1. Display the data from the database on the webpage

The following sections will discuss all these steps one-by-one.

2. Understanding the Dockerfile used to build the image

In order to build a Docker image yourself you need a Dockerfile. In this case we will supply you with the Dockerfile necessary to create the image. But you will have to run the build process yourself to make the image available to your Docker daemon.

  1. Download the workshop zip file and unzip it on your local harddisk

  2. Look at the Dockerfile in the docker-images/webserver folder.

    [a] FROM training/webapp
    [b] ENV DEBIAN_FRONTEND=noninteractive
    [c] RUN apt-get -y update && apt-get -y install libpq-dev python-dev
    [c] RUN pip install pygresql
    [d] ADD app.py /opt/webapp/app.py

Each line in the Dockerfile is explained below:

  1. We use the training/webapp image as a base-image

  2. We set the DEBIAN_FRONTEND environment variable to make sure that the next command runs in noninteractive mode

  3. We run 2 commands inside the container to install python and pygresql (a library that lets python code talk to PostgreSQL)

  4. We overwrite the existing app.py file with our new app.py file that will check the database.

3. Building the new image

We now have to instruct Docker to use the Dockerfile and build it. Additionally, we assign the tag webserver to the new image.

Make sure your working directory is docker-images/
Build the new image with a tag
docker build -t webserver webserver/
Read the documentation of the build command: docker build --help

The command should exit output similar to:

Successfully built 98ac0aa0d27d
Successfully tagged webserver:latest

4. Setup a docker network

If we want docker containers to be able to reach each other, we need to setup a docker container network. A Docker network automatically allows Docker containers to reach each other by container name, as long as they are part of the same network. This works via the DNS server on the Docker daemon. To create a docker network called "training" we need to run the following command:

docker network create training

5. Starting the new image

Before we start a container with the webserver image we first need to start a Postgres database that will store our data for us.

Luckily, we can use an off-the-shelf image provided by Postgres.

Run the Postgres image with a variable to set the password. Also notice the "--network training" parameter.
docker run --rm --network training --name database -e POSTGRES_PASSWORD=myPassword -d postgres
If you want to check which containers are part of the network, you can run the command below. The output will contain a list of containers, each of which should be able to reach the others based on container name.
docker network inspect training

6. Add data to the database

Before we can get data from the database, we first need to put the data in of course. For that, we need a database client to connect to the Postgres database. Luckily, the Postgres image also contains a client! We can start another container from the Postgres image, but instead of the database, we’ll start the client. From there we can connect to the previously started database container so we can add some data to the database to display on the webpage.

Connect with PSQL to the database
docker run -it --rm --network training postgres psql -h database -U postgres

This starts a new instance of the postgres image in interactive mode (-it), removes it after stopping the container (--rm), makes it part of the training-network and executes psql -h database -U postgres inside the container in order to connect to the running database container.

Execute commands in the running database to prepare data
CREATE DATABASE mydata;
\c mydata
CREATE TABLE kv (key varchar(100) PRIMARY KEY, value varchar(100));
INSERT INTO kv VALUES ('provider','Now getting data from Postgres!');
SELECT * FROM kv; -- Check that the data is really there
\q

7. Run the newly built webserver image

Now run the app and check that your data is displayed
docker run -it --rm --network training --name webserver -p 5000:5000 webserver
In foreground mode (the default when -d is not specified), docker run can start the process in the container and attach the console to the process’s standard input, output, and standard error. It can even pretend to be a TTY (this is what most command line executables expect) and pass along signals. For interactive processes (like a shell), you must use -i -t together in order to allocate a tty for the container process. -i -t is often written -it.
Error on Windows: the input device is not a TTY.
On Windows/GIT Bash/Cygwin you may get the following error the input device is not a TTY. If you are using mintty, try prefixing the command with 'winpty'. Run the command again with winpty in front of it. Refer to this page for details.