Let's first see what issue we face and what docker compose is there to help us with
It is going to be a bit long story but bear with me please, it'll be worth it, I promise.
Consider this scenario of this microservices app. The app architecture consists of two microservices:
We'll have the docker file the usual way. But let's now try to run the app fully functional.
Let's consider the scenarios first.
accounts-ms
needs to know the path of students-ms
in order to communticate. Environment variable is the way to go, so we can update the url without a need for redeploymentaccount-ms
depends on students-ms
, so each time we start account-ms
we have to start students-ms
as wellLet's start our app now
Navigate to school-app in ubuntu terminal. Now reflect to our requrements and start executing our commands
docker build -t students-ms:v1 /students-ms
docker build -t accounts-ms:v1 /accounts-ms
docker volume create school-app
docker run --name students-ms -p 8181:8181 -v app/school-app/students-ms students-ms:v1
docker run
-p 8080:8080
-v app/school-app/accounts-ms
-e TUDENTS_MS_URL=http://students-ms:8181
accounts-ms:v1
docker network create school-app
docker network connect school-app students-ms accounts-ms
With these steps our app should be up and running
Now let's assume we have to bring the services down. Image that now we have new features added in the app, we'll have to get rid of the existing containers and create some new one. We'll have to repeat the entrie process again, except for the volume and network creation part but then commands will be additional
docker stop students-ms accounts-ms
docker rm students-ms accounts-ms
docker rmi students-ms:v1 accounts-ms:v1
Thereafter we'll have to repeat the commands mentioned earlier to bring our app to running state.
Do mind this is our dummy app with just two ms. In a production application, there would be much more stuff, much more environment variables, additional arguments and customiztion
As of now, we are well aware of what is the process of getting the app up and running. We have these many commands just for two dummy services,
but on prodcution we can have 100s of micorservices communicating with each other and each one having n number of environment variables, arguments and etc
Doing these things manually is hectic, cumbersome and very much error-prone.
Docker compose provides us a very neat way to write down the specifications of our services and then manage them effectively.
docker compose provides us a way to easily manage our applications. We have to learn the template it provides and write a yml file accordingly. Thereafter, docker compose in one command can perform the following task for us
accounts-ms
relies on students-ms
,
we must start students-ms while running accounts-ms, docker compose can do that as wellThe template for docker compose is this roughly
services:
service-name:
image: image-name:tag # optional - our image name and tag
container_name: container-name # optional our container name
build:
context: ./path-to-docker-file-directory
dockerfile: name-of-docker-file
ports:
- "host-port:container-port" # Map host port to container port
volumes:
- host-path:container-path # Mount a volume
environment:
- ENV_VAR_NAME=value # Set environment variables
depends_on:
- another-service-name # Specify dependencies (optional)
network:
- networks-a # the networks our ms should be a part of
- network-b
another-service-name:
build:
context: ./path-to-dockerfile # Path to Dockerfile
dockerfile: Dockerfile # Optional: Specify Dockerfile name
ports:
- "host-port:container-port"
networks:
- my-network # Custom network (optional)
volumes:
volume-name: # Declare named volumes
networks:
my-network: # Declare custom networks
driver: bridge
Let's go ahead ad write the docker compose file for our app. Remember the conditions we discussed earlier.
services:
accounts-ms:
image: accounts-ms:v1 # repo name and tag, since we are doing it on local else we'll have to attach username as well
container_name: accounts-ms
ports:
- "8080:8080" # maps host machines port 8181 to container's port 8181
build:
- context: ./accounts-ms # that's where our app's docker file is
volumes:
- school-app-data:/app/data/accounts-ms # we don't want to loose data or data inconsistency in multiple instances of our ms
networks:
- school-app # because we need our ms to be able to communicate with each other
environment:
- STUDENTS_MS_URL=http://students-ms:8181
depends_on:
- students-ms # Accounts-ms implicitly depends on students-ms for student data and results.
students-ms:
image: students-ms:v1 # repo name and tag, since we are doing it on local else we'll have to attach username as well
container_name: students-ms
ports:
- "8181:8181" # maps host machines port 8181 to container's port 8181
build:
- context: ./students-ms # that's where our app's docker file is
volumes:
- school-app-data:/app/data/students-ms # we don't want to loose data or data inconsistency in multiple instances of our ms
networks:
- school-app # because we need our ms to be able to communicate with each other
volumes:
school-app-data: # we have to tell docker about our volume
networks:
school-app: # we need to tell it the network
driver: bridge
Checkout the full version of this file here
Once done with this file, we can simply run the command docker compose up
and it'll bring up the services and setup networks and volumes as required
docker compose up
by default looks fordocker-compose.yml
file in the directory, but this can be customsied and we can have any name for the file and run it with-f
flag likedocker compose -f /some_path/abc.yml up
There are few ways we can run the compose command
docker compose up
it'll bring up all the services mentioneddocker compose up <service-name>
this will run container for the particular service and the one it depends ondocker compose up --build
it'll build the image locally prior and then use that to run the containersdocker compose up --no-cache
it'll build the images and not use any layer or existing cache dataExplore more at official doc : https://docs.docker.com/compose/