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
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