Recently, I have been experimenting with moving my development environment to a Dockerized version, so that I can move it closer to production. In an effort to get there, I converted my personal project ( mocsh.com ) to a docker-compose based setup using Java 15. Haven't pushed it to production yet, because I'm still playing with it, but I think the development environment has reached a stage that I would say is "mature".
I will walk you through the steps to set up a multi-service ( webapi, db ), and use VS Code to do development inside that. This is going to be agnostic of technology, so whether Java/Python/NodeJS/Ruby/PHP, MongoDB/PostgreSQL/MySQL/MariaDB, or your favourite stack, it should all work.
Assumptions:
- you have Docker For Desktop installed on your machine, and it ships with docker-compose
- you are familiar with the basics of docker-compose. In case you are not, here is the official documentation on Getting started with docker-compose
We will do this in a 2 part series, covering the following:
- Part 1 ( this article ) - setup a docker-compose environment for a 2 tier application
- Part 2 - Use VS Code for development within this environment
You can extrapolate / expand this example to as complex an application, but let's get started by creating an empty git repo, so that we can track our progress.
mkdir my-dev-env
cd my-dev-env
git init -q
Docker-Compose starter
Let's start by defining the docker-compose file. For the same of this exercise, I am going to assume that the application that we are going to run is a NodeJS application, running on the latest LTS.
Create a file called docker-compose.yaml
volumes:
webapi-code: {}
webapi-root: {}
services:
webapi:
image: node:lts
volumes:
- webapi-code:/usr/src/app
- webapi-root:/root
command: ["/bin/bash", "-c", "--", "while true; do sleep 86400; done"]
In this directory, run the command:
docker-compose up -d
You should see messages similar to:
Creating my-dev-env_webapi_1 ... done
where my-dev-env
would be replaced with the name of the directory you have this file in.
To clean up, run docker-compose down
in the same directory.
Understanding the configuration
Lets walk through the individual entries in this file. First the top level sections:
volumes
tells docker-compose that we need storage that will not be part of the container, because containers are supposed to be throwaway-capable. We should be able to bring up new containers whenever we want. But then, where does the "persistent" data go ? It stays on volumes.
In this section, we are defining two volumes to help us retain data beyond the lifetime of the container, one for the code, and another for the home directory.
services
is a way to tell docker-compose that this is a "runnable" service, and will have its own container.
The service we have defined is one called webapi
. We have the volumes mounted at the paths specified, and we are specifying that this service should be created from the docker image node:lts
. You can see the available tags for the node
image at: Node image on Docker Hub
One important element here, that is required and important, is command
. It specified the command to run when the container is started. The container stops when this command exits. By default, the gradle
container runs the gradle
command. And, given that we have no code in the container, and we don't intend to run anything automatically, the container would stop.
Experiment with the behaviour by removing this entry, and running the docker-compose up
command again.
Adding the database
Adding a database to this configuration is simple. Assuming we want a MongoDB instance to work with, first step is to look for the volumes that need to be mounted to get the storage to be persistent. You may choose to not have a persistent storage, if this is your throwaway instance, but I'm going to show you the steps to do it.
First, we want the data from the database to be persistent across container creation cycles, in case we want to use a different version of the database. So, we will add another entry to the volumes
section that will look like:
db-data: {}
Mind you, the spaces in any yaml file are important. So, ensure that the three entries align.
Next, adding the database itself. For this, lets look at the How to use this image
section on the Docker Hub page of Mongo.
Add the service entry from there to our docker-compose file, but with some modifications:
- we want the volume to be mounted
- we don't want the username/password since this is a private only instance
Add the following under the services
section of the docker-compose.yaml
file
mongo:
image: mongo
restart: always
volumes:
- db-data:/data/db
Don't need to stop the existing containers. Just run the docker-compose up -d
command again, and all the relevant actions will be taken.
You will see something similar to:
Creating volume "my-dev-env_db-data" with default driver
Pulling mongo (mongo:)...
latest: Pulling from library/mongo
Digest: sha256:02e9941ddcb949424fa4eb01f9d235da91a5b7b64feb5887eab77e1ef84a3bad
Status: Downloaded newer image for mongo:latest
my-dev-env_webapi_1 is up-to-date
Creating my-dev-env_mongo_1 ... done
Next Steps
Great we have a working set of containers created. If you open the Docker Dashboard UI, you will see an entry for my-dev-env
or whatever name you used, and then, when you expand that, you will see the two service containers: webapi and mongo.
Next steps: Connect VS Code to this and ensure that our app can see the Mongo instance.
This article originally published at: blog.mandraketech.in/dev-environment-using-..