docker-compose is used build to a project that involves building and running multiple containers. To be more precise, docker-compose can be used to define, build and bring up a stack of containers that can communicate with each other to collectively act an application.

Clone demo from github

fork-git

In this example, we will build an application with two containers namely container-client and container-web. container-web will be a nginx web server and container-client will be used to send a simple http request to container-web and return the response.

In order for docker-compose to build and run multiple containers at once, a docker-compose.yml file is used. A docker-compose.yml file contains the names and details of containers to be brought up.

File: docker-compose.yml

version: '3'
services:
  container-web:
    image: 'nginx'
  container-client:
    build: .

This file defines the two container names under ‘services’ block, namely container-web and container-client. The container-web service takes an argument named ‘image’. This takes the name of the image that needs to be run. In this case ‘nginx’ webserver image will be downloaded and run from docker hub.

For the above docker-compose.yml to work, we also need to ensure that we get our container-client Dockerfile ready, since container-client is our custom image that is yet to be built. By passing the ‘.’ to ”build’ argument, we instruct the docker-compose to look for a Dockerfile in the present working directory to build an image out of it.

Our present directory structure looks like below.

$ tree project/
project/
├── docker-compose.yml
├── Dockerfile 
├── python_http_req.py 
 

File: Dockerfile

FROM centos
COPY ./python_http_req.py /root/send_req.py
RUN chmod +x /root/send_req.py
ENTRYPOINT /root/send_req.py

In this Dockerfile we use a centos image. We copy the below python script that will be used to send a simple HTTP request to container_web and print the response. This script will be executed when the container is started.

File: python_http_req.py

Note that, for the communication to happen between the two containers we are not setting up networking manually. When docker-compose is used to build container images, the two containers are made available to each other automatically by placing them in the same network. The hostnames of containers can be referenced by their container names respectively.

NOTE: In the below script, the container name ‘container-web’ is used for hostname to send request to the webserver.

#!/bin/python
import socket
import sys

webserver = 'container-web'
port = 80  # web

# create socket
print('# Creating socket')
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
    print('Failed to create socket')
    sys.exit()

print('# Getting remote IP address')
try:
    remote_ip = socket.gethostbyname( webserver )
except socket.gaierror:
    print('webservername could not be resolved. Exiting')
    sys.exit()

# Connect to web server
print('# Connecting to server, ' + webserver + ' (' + remote_ip + ')')
s.connect((remote_ip , port))

# Send data to remote server
print('# Sending data to server')
request = "GET / HTTP/1.0\r\n\r\n"

try:
    s.sendall(request)
except socket.error:
    print 'Send failed'
    sys.exit()

# Receive data from web server
print('# Receive data from server')
reply = s.recv(4096)

print reply

Build and Run docker-compose.yml

The below build and run commands are to be executed from the same working directory as the docker-compose.yml file.

$ docker-compose build

container-web uses an image, skipping
Building container-client
Step 1/4 : FROM centos
 ---> 9f38484d220f
Step 2/4 : COPY ./python_http_req.py /root/send_req.py
 ---> Using cache
 ---> fce3483eb01d
Step 3/4 : RUN chmod +x /root/send_req.py
 ---> Using cache
 ---> 35230d32626d
Step 4/4 : ENTRYPOINT /root/send_req.py
 ---> Running in 7b4cc02f146f
Removing intermediate container 7b4cc02f146f
 ---> 9e34f31dc0a8
Successfully built 9e34f31dc0a8
Successfully tagged project_container-client:latest

$ docker-compose up

Starting project_container-web_1      ... done
Recreating project_container-client ... done
Attaching to project_container-web_1, project_container-client
container-client  | # Creating socket
container-client  | # Getting remote IP address
container-client  | # Connecting to server, container-web (172.22.0.2)
container-client  | # Sending data to server
container-client  | # Receive data from server
container-client  | HTTP/1.1 200 OK
container-client  | Server: nginx/1.17.1
container-client  | Date: Mon, 22 Jul 2019 12:30:42 GMT
container-client  | Content-Type: text/html
container-client  | Content-Length: 612
container-client  | Last-Modified: Tue, 25 Jun 2019 12:19:45 GMT
container-client  | Connection: close
container-client  | ETag: "5d121161-264"
container-client  | Accept-Ranges: bytes
container-client  |
container-client  |
container-web_1     | 172.22.0.3 - - [22/Jul/2019:12:30:42 +0000] "GET / HTTP/1.0" 200 612 "-" "-" "-"
project_container-client exited with code 0

The above output shows that container-client sent a successful http request to container-web and printed its response. Similarly the container stack can be stopped by running the below stop command.

$ docker-compose stop