Part 3: Docker Compose File: Working with Multiple containers
Till part 2, we have seen how to build containers and mount volumes from local storage alongwith pushing our repository to docker hub.
In this post, we will look at the usage of docker compose file to implement different services in separate containers and to centralize the production level configuration inside a file to be known as docker-compose.yml.
What to achieve ?
We will create two containers:
1. Python engine to host the product details
2. Apache to host the Website
Products <---> Website
Solution
1. Create Directory Structure.
Make two directories for separate containers. # mkdir website # mkdir product $ ls -ltr total 8 drwxr-xr-x 3 saket1447583 staff 102 Jan 10 13:12 website drwxr-xr-x 6 saket1447583 staff 204 Jan 10 13:12 product And a docker compose file to configure it. # touch docker-compose.yml After this, build two containers inside these directories and use them to host the service together. We will see later what to configure inside it.
2. Configure Product container.
$ ls -l product/ total 24 -rw-r--r-- 1 saket1447583 staff 66 Jan 10 11:51 Dockerfile -rw-r--r-- 1 saket1447583 staff 342 Jan 10 13:12 api.py -rw-r--r-- 1 saket1447583 staff 33 Jan 10 11:48 requirements.txt ### First we need to create the file say api.py with below content. ### Here we created an array products with a list that we show on website. ### This container service is run on Port 80. $ cat api.py # Product Service from flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) class Product(Resource): def get(self): return { 'products': ['Gajak', 'Moofli', 'Dark Choclate', 'Ice'] } api.add_resource(Product, '/') if __name__ == '__main__': app.run(host='0.0.0.0', port=80, debug=True) $ ### In above Python file, we have called Flask library. ### Hence we need Flask and flask-restful to be installed ### By docker during container build. $ cat requirements.txt Flask==0.12 flask-restful==0.3.5 $ ### Dockerfile contains the image from which we will build the container. ### And what to copy on the default path. ### api.py file to run after container build $ cat Dockerfile FROM python:3-onbuild COPY . /usr/src/app CMD ["python","api.py"] $
3. Configure Website Container
### Create a index.php file inside directory $ cd ../website/ $ ls -ltr total 8 -rw-r--r-- 1 saket1447583 staff 324 Jan 10 13:08 index.php ### put the contents to get the file content. ### "product-service" is the service name defined above. ### docker configures the same name as hostname of that container. ### file_get_contents gets the content of the webpage $ cat index.php <html> <head> <title>Our Products</title> </head> <body> <h1>Welcome to our products page</h1> <ul> <?php $json = file_get_contents('http://product-service'); $obj = json_decode($json); $products = $obj->products; foreach ($products as $product) { echo "<li>$product</li>"; } ?> </ul> </body> </html> $
4. Create docker-compose.yml file.
$ cd .. $ ls -ltr total 8 -rw-r--r-- 1 saket1447583 staff 275 Jan 10 13:03 docker-compose.yml drwxr-xr-x 3 saket1447583 staff 102 Jan 10 13:12 website drwxr-xr-x 6 saket1447583 staff 204 Jan 10 13:12 product ### service name is "product-service" ### build specifies the path is "./product" directory ### volumes mention the local system dir path ### ports map container 80 port to 5000 local system port. ### we can access this service on localhost:5000 port. ### website service defines the image first i.e. ### docker container build configuration ### depends_on specifies the service which ### should be started first for it to work. $ cat docker-compose.yml version: '3' services: product-service: build: ./product volumes: - ./product:/usr/src/app ports: - 5001:80 website: image: php:apache volumes: - ./website:/var/www/html ports: - 5000:80 depends_on: - product-service $
5. Run docker compose file
$ docker-compose up Pulling website (php:apache)... apache: Pulling from library/php 177e7ef0df69: Pull complete 9bf89f2eda24: Pull complete 350207dcf1b7: Pull complete a8a33d96b4e7: Pull complete c0421d5b63d6: Pull complete f76e300fbe72: Pull complete af9ff1b9ce5b: Pull complete d9f072d61771: Pull complete 4212b7d006e6: Pull complete 1cca8a5e24f8: Pull complete bd3c820fb415: Pull complete a2761cbd05fb: Pull complete 91d690c757e8: Pull complete 2c2faa0cad4b: Pull complete Starting docker_product-service_1 ... done Creating docker_website_1 ... done Attaching to docker_product-service_1, docker_website_1 website_1 | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message website_1 | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message product-service_1 | * Running on http://0.0.0.0:80/ (Press CTRL+C to quit) product-service_1 | * Restarting with stat website_1 | [Thu Jan 10 21:05:27.368914 2019] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.3.0 configured -- resuming normal operations website_1 | [Thu Jan 10 21:05:27.369008 2019] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND' product-service_1 | * Debugger is active! product-service_1 | * Debugger PIN: 304-853-752 product-service_1 | 172.18.0.1 - - [10/Jan/2019 21:07:05] "GET / HTTP/1.1" 200 - product-service_1 | 172.18.0.1 - - [10/Jan/2019 21:07:06] "GET / HTTP/1.1" 200 - product-service_1 | 172.18.0.1 - - [10/Jan/2019 21:07:06] "GET / HTTP/1.1" 200 -
6. Check the result in browser.
### In product service container: ### In website container:
We will continue to look at docker machine, swarm, and stack in next parts.
References:
1. https://www.youtube.com/watch?v=Qw9zlE3t8Ko
2. And https://docs.docker.com/get-started/part3/