- DevOps with Kubernetes
- Hideto Saito Hui Chuan Chloe Lee Cheng Yang Wu
- 581字
- 2025-04-04 15:00:00
Composing containers
As Docker compose is the same as Docker in many aspects, it's more efficient to understand how to write docker-compose.yml with examples than start from docker-compose syntax. Let's now go back to the kiosk-example we looked at earlier and start with a version definition and four services:
version: '3'
services:
kiosk-example:
recorder-example:
lcredis:
lmysql:
The docker run arguments for kiosk-example are pretty simple. They include a publishing port and an environment variable. On the Docker compose side, we fill the source image, the publishing port, and environment variables accordingly. Because Docker compose is able to handle docker build, it can build images if those images can't be found locally. We want to use this to decrease the effort of image management:
kiosk-example:
image: kiosk-example
build: ./kiosk
ports:
- "5000:5000"
environment:
REDIS_HOST: lcredis
Converting the Docker run of the recorder-example and redis in the same manner, we have a template that looks as follows:
version: '3'
services:
kiosk-example:
image: kiosk-example
build: ./kiosk
ports:
- "5000:5000"
environment:
REDIS_HOST: lcredis
recorder-example:
image: recorder-example
build: ./recorder
environment:
REDIS_HOST: lcredis
MYSQL_HOST: lmysql
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: mysqlpass
lcredis:
image: redis
ports:
- "6379"
For the MySQL part, MySQL requires a data volume to keep its data as well as its configurations. In addition to the lmysql section, we add volumes at the level of services and an empty map called mysql-vol to claim a data volume:
lmysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: mysqlpass
MYSQL_DATABASE: db
MYSQL_USER: user
MYSQL_PASSWORD: pass
volumes:
- mysql-vol:/var/lib/mysql
ports:
- "3306"
volumes:
mysql-vol: {}
One of the benefits of this is that we can manage the launching order between the components with depends_on. What this does is maintain the order; it can't detect whether the components that it will use are ready. This means our application could still connect and write to the database before the database is ready. All in all, as our program is a part of a distributed system with many moving parts, it's a good idea to make it resilient to the changes of its dependencies.
Combining all of the preceding configurations, including depends_on, we have the final template, as follows:
version: '3'
services:
kiosk-example:
image: kiosk-example
build: ./kiosk
ports:
- "5000:5000"
environment:
REDIS_HOST: lcredis
depends_on:
- lcredis
recorder-example:
image: recorder-example
build: ./recorder
environment:
REDIS_HOST: lcredis
MYSQL_HOST: lmysql
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: mysqlpass
depends_on:
- lmysql
- lcredis
lcredis:
image: redis
ports:
- "6379"
lmysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: mysqlpass
MYSQL_DATABASE: db
MYSQL_USER: user
MYSQL_PASSWORD: pass
volumes:
- mysql-vol:/var/lib/mysql
ports:
- "3306"
volumes:
mysql-vol: {}
This file is put in the root folder of a project. The corresponding file tree is shown here:
├── docker-compose.yml
├── kiosk
│ ├── Dockerfile
│ ├── app.py
│ └── requirements.txt
└── recorder
├── Dockerfile
├── process.py
└── requirements.txt
Finally, run docker-compose up to check everything is fine. We can check every component is linked nicely using kiosk:
$ curl localhost:5000
Gooday!
Import tickets with "curl -XPOST -F 'value=<int>' /tickets"
Purchase a ticket with "curl -XPOST /buy
Get current tickets with "curl -XGET /tickets"
$ curl -XGET localhost:5000/tickets
0
$ curl -XPOST -F 'value=10' localhost:5000/tickets
SUCCESS
$ curl -XGET localhost:5000/tickets
10
$ curl -XPOST localhost:5000/buy
SUCCESS
$ docker exec chapter2_lmysql_1 mysql -u root -pmysqlpass \
-e "select * from kiosk.sellinglog;"
id ts
1 1536704902204
Writing a template for Docker compose is as simple as this. We're now able to run an application in the stack with ease.