What is docker-compose
Like docker itself allows managing single container, docker-compose makes it easy to control not just one, but all containers that make distributed app. This includes containers, networks, volumes and all related settings.
If you think about it, starting an app that has more than one container is less than trivial task and it gets exponentially harder as you add more or them. Let’s check out simple example: distributed web-application that consists of two containers – one with web content and one with database.
It will require at least three startup steps:
- create user-defined network (to allow containers talk to each other by name),
- launch container with database, providing its name (e.g. “db”) and newly created network to attach to,
- launch container with web content, also providing its name (e.g. “web“) and target network.
And here are some problems. Firstly, order of execution matters. Obviously, you cannot connect to non-existing network, and web-container might fail, if its database service doesn’t exist. Secondly, some of components may or may not exist. E.g. web may need to be built first, and user-defined network doesn’t have to be created if it’s already there.
Any self-respecting programmer can create a script to automate that, but why bother, if there’s docker-compose.
With compose you can create docker-compose.yml, which acts like Dockerfile, but for whole application. Once it contains description of all services-containers that make an app, compose will infer startup order, create or reuse missing dependencies during the launch and even will perform proper cleanup after application is shut down.
Installing docker-compose
Mac and windows users are in for a treat: Docker installer includes compose by default. Linux users are less lucky today and have to consult installation guide.
docker-compose.yml
As I mentioned, docker-compose.yml acts like a Dockerfile, but at application level. Coming back to distributed web-app example we started with, let’s assume we need to build web server container from Dockerfile, and database layer is good old mysql. Here’s how compose file would look like:
1 2 3 4 5 6 7 8 9 10 |
version: '2' services: web: build: ./web-app depends_on: - db db: image: mysql environment: - MYSQL_ROOT_PASSWORD=somepassword |
Pretty cool, right? The file literally says that application consists of two services: web and db. First one needs to be built from Dockerfile located at ./web-app and it needs db to run. The second container is clean mysql image from official repo, and we even specified its root password in environmental variables.
As a free bonus, compose creates new user-defined network and connect all containers to it. It will use default network driver (bridge), but it’s changeable.
There’s one caveat, though. depends_on will make sure that container starts immediately after dependency is started, but this doesn’t mean that dependency will be ready, whatever that means for you application. For instance, when I start mysql container, container itself will start almost immediately. However, mysql server inside it usually takes several seconds to start. During that seconds container is started. But as a service, it’s not ready. Because ‘ready’ means different things for different applications, compose waits for ‘started’ state only.
Starting and stopping app with compose
Compose has two sets of commands for starting and stopping an app. up and down affect whole application, whereas start and stop control individual services.
docker-compose up
The command starts application from the ground up: creates network, builds Dockerfiles and starts services. Adding -d argument will start application in background.
1 2 3 4 |
docker-compose up -d #Creating network "dockercompose_default" with the default #driver #Creating dockercompose_db_1 #Creating dockercompose_web_1 |
Compose will prefix container names with application name (current folder name), but within application network original names like web and db are still available.
1 2 3 4 5 6 7 |
docker-compose exec web bash root@19004b178ed1:/# ping db #PING db (172.18.0.3): 56 data bytes #... root@19004b178ed1:/# ping dockercompose_db_1 #PING dockercompose_db_1 (172.18.0.3): 56 data bytes #... |
docker-compose ps
…will show which application services are running. It’s very similar to docker ps .
1 2 3 4 5 |
docker-compose ps # Name Command State Ports #--------------------------------------------------------------------------- #dockercompose_db_1 docker-entrypoint.sh mysqld Up 3306/tcp #dockercompose_web_1 nginx -g daemon off; Up 443/tcp, 80/tcp |
docker-compose down
…undoes everything that up did: shutdowns and deletes containers, networks, etc.
1 2 3 4 5 6 7 8 9 10 |
docker-compose down #Stopping dockercompose_web_1 ... done #Stopping dockercompose_db_1 ... done #Removing dockercompose_web_1 ... done #Removing dockercompose_db_1 ... done #Removing network dockercompose_default docker-compose ps #Name Command State Ports #------------------------------ |
docker-compose start/stop
When application is running sometimes it’s necessary to suspend certain components without deleting them. This is where start/stop come in handy.
1 2 3 4 5 |
docker-compose stop web #Stopping dockercompose_web_1 ... done #...do some stuff docker-compose start web #Starting web ... done |
However, if I wanted to upgrade the container, just stopping, building and starting it wouldn’t work. This would – docker-compose up --no-deps --build web
Conclusion.
docker-compose is a tool to have nearby when number of application containers becomes greater than one. Its docker-compose.yml file format is simple and it has way more options than we’ve just covered, such as build parameters, networks and volumes, environment vars imports, etc.. Compose itself is not limited with start/stop commands, but also allows app monitoring, executing arbitrary commands against services, and few others.
3 thoughts on “Quick intro to docker-compose”