Docker as Local Development Environment
- Install Docker
- Install Docker Compose
Docker is a tool designed to make it easier to create, deploy, and run applications by using containers. Containers allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and ship it all out as one package.
To form a mental picture, for the time being just consider a Docker Container as an extremely light weight isolated linux based virtual machine inside which we will run our application service (although containers are not exactly VMs). The container will contain our code and all of its dependencies (system libraries, tools, etc). For our setup we will use one docker container per service and separate docker containers for our database.
Docker compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.
TLDR; Docker compose lets you run all your services at once (also in the right order) and manage them via a unified interface.
Docker compose in general will contain:
- Services: Services are the list of induvidual docker containers that will be run by the compose tool. We will specify the ports and other configurations needed to run the docker containers here.
- Networks: Network provides a way by which different services can interact with each other. Each container can attach itself to a network and all containers within same networks can communicate with each other. We will use a single network for our case.
- Volumes: Docker containers by default do not contain any kind of persistence storage. If a docker container is killed then all the data in its memory gets lost. So in order to save some persistant data you need volumes. Think of volumes as permanent hard drives for these containers. We will have one volume per service.
Setting it up
I have created a basic setup of services as described above. To follow along, take a clone of the repo with the following commands.
Now you should have the following project structure to start with:
You could browse through each project inside and read the README to understand how to run those projects induvidually.
Make sure you have docker running by following the instructions here https://www.docker.com/get-started .
First step is to create a docker file for our service njs1. Create a Dockerfile: blog-docker-dev-environment-example/njs1/Dockerfile
Create the file at blog-docker-dev-environment-example/docker-compose.yml . Now let jump to docker-compose.
Lets add our first service (njs1) to it.
The above docker compose file has only one service (njs1). We will add more services incrementally. Before that lets run it and see what we get.
In the folder which contains our docker-compose.yml run:
if all goes well you should see it building our app container and in the very end
Open it up on the browser: http://localhost:7000 to test it out.
docker-compose service in detail:
- build: : Path to the dockerfile. Note: you can either specify the folder which contains the Dockerfile or the complete path to Dockerfile itself. Both works.
- command: : Command to run when docker container is started.
- environment: : All the environment variables you need to set.
- ports:: This specifies the mapping of the port inside the container to that of the host machine. They need not be same.
- working_dir: : This is the path inside the container where you want to the run the command you specified above.
All this is good and fine but how can I use it for efficient development?
To make an efficient development environment we need to be able to edit the source code (which with current setup is not possible without re building container again and again). To achieve this we will make use of volumes.
Make the following changes. First lets tell our njs1/Dockerfile to not copy the project files to the container.
then tell docker-compose to mount our project directory from our local machine as a directory inside the container.
- volumes: - volumes gives us a way to map our local directories to a directory inside the container. Here we are saying map njs1 folder from our local machine to /root/njs1 inside the docker container. Here we are not copying the files into the container, instead we are mounting it as a shared volume. And thats the trick that makes it useful.
To test it out. Lets add nodemon to our njs1 service.
Now make the following change in njs1/package.json
Time to test it out! Go to the root folder and run
The –build tells docker-compose to rebuild the images.
Try editing njs1 source files! 🎉
Try making some changes in the njs1/index.js file and you should see nodemon auto reloading on file change.
Finishing things up! 🏁
Once you add other services to the docker-compose file. It should look something like this:
- image: instead of build : In docker-compose we can specify the docker image from docker-hub directly instead of a dockerfile using the image: property. Hence for simple setups we dont need to write our own Dockerfile.
There are many more configuration options which you can use in your docker-compose.yml file. To see a complete reference of those, you can visit this link: https://docs.docker.com/compose/compose-file/
Now that you have setup your services to run via docker-compose for local development. There are few commands that can help.
Start all services
This will start all services in the docker-compose file and detach from the terminal. So your services can run in background.
Stop all services
Corresponding stop command
Launch a specific service
This will only launch njs1 from the list of services in the docker-compose.yml
You can use similar commands to stop, start induvidual services as well.
Restart a single service
logs from specific service
This will show logs of only njs1 and also watch for more logs
ssh into a particular service container
Some Tips for smoother workflow
Containers / services running too slow?
You might notice that your services are running/launching at extremely slow as compared to when you launch them without docker-compose. This might be because you have allocated less CPU/RAM to docker service. The default values are very low and that causes issues when launching multiple services.
Go to : dockerIcon -> preferences -> Advanced
Change the slider to give CPU > 3 cores and RAM > 6GB
Low on space / messed up and want to restart everything from scratch ?
Removing all images and then refreshing the entire thing.
To remove all docker containers:
To remove all docker images: