Docker Tips : Development With NodemonTracking changes in real timeLuc JuggeryBlockedUnblockFollowFollowingDec 20, 2017Photo by Jefferson Santos on UnsplashTL;DRI have delivered several Docker trainings during the last few weeks and some questions come back quite often.
For example:Q: When developing my application, how can I get my changes to be taken into account automatically in the containers?The idea behind this question is, for a developer, to make changes in his/her local IDE (Atom, Visual Studio Code, Eclipse, vi…) and then see the changes taken into account in real time in the running application.
Usually, I respond:R: While in development, you need to mount the local source code in the services’ containers and start the main process through a utility like nodemon, which will watch files and restart the application when some changes are observed.
I realized this answer deserved some additional details and examples.
This is what this post is all about.
Example ApplicationLet’s consider the Docker Voting Application.
This one is used very often for demos and presentations.
This application follows a micro-services architecture.
It is made of 5 services as illustrated below.
Docker’s voting app architecture (https://github.
com/docker/example-voting-app)vote: front end that enables a user to choose between a cat and a dogredis: database where votes are storedworker: gets votes from redis and stores the results in a postgres databasedb: the postgres database in which vote’s results are storedresult: front end displaying the results of the voteLet’s clone the Voting App repository:git clone https://github.
com/dockersamples/example-voting-appAs we can see, several Docker Compose files are defined here.
We’ll use the default, named docker-compose.
yml, in this article.
version: "3.
3"services: vote: build: .
/vote command: python app.
py volumes: – .
/vote:/app ports: – "5000:80" networks: – front-tier – back-tier result: build: .
/result command: nodemon server.
js volumes: – .
/result:/app ports: – "5001:80" – "5858:5858" networks: – front-tier – back-tier worker: build: context: .
/worker depends_on: – "redis" networks: – back-tier redis: image: redis:alpine container_name: redis ports: ["6379"] networks: – back-tier db: image: postgres:9.
4 container_name: db volumes: – "db-data:/var/lib/postgresql/data" networks: – back-tiervolumes: db-data:networks: front-tier: back-tier:Illustration on the result serviceLet’s have a closer look at the way the result service is defined in this file.
result: build: .
/result command: nodemon server.
js volumes: – .
/result:/app ports: – "5001:80" – "5858:5858" networks: – front-tier – back-tierSeveral interesting things here:the local result folder is bind-mounted into the /app folderthe command used to run the service is nodemon server.
jsBind-mounting the source folderThe local source code, the one we continuously change within our favorite IDE, will be available as-is within the service’s container.
In other words, every change we will do locally will be reflected in the running application.
But, if we want it to be taken into account by the application, this one needs to be reloaded.
Enter nodemon!Running the service with nodemonNodemon is a great piece of software.
Take a look at the official description.
Nodemon is a utility that will monitor for any changes in your source and automatically restart your server.
Perfect for development.
— NodemonIn other words, this guy is there to supervise the main process running in the container, and to restart it if it detects some changes on surrounding files.
The result service is developed with Node.
js so it’s very easy to run it with nodemon instead of with the default node command.
As we can see, the command defined in in the docker-compose.
yml file overrides the one defined in the Dockerfile.
// Command instruction in the DockerfileCMD [“node”, “server.
js”]// Command instruction in the docker-compose.
yml filecommand: nodemon server.
jsFor nodemon to react on changes made on html files located in the views folder, we will change the command specified in the docker-compose.
yml file a little bit and make it look like the following.
// Slight modification of the command instructioncommand: nodemon –watch views -e js,html server.
jsOf course, in order for the result service to start with nodemon, we need to have it available within the container.
The instruction to install it in the Dockerfile of the result service is the following one.
RUN npm install -g nodemonNot a Node.
js application ?Nodemon integrates very well with a Node.
js application, but what if the application we use is developed with another language ?Not a problem… nodemon can run several types of applications.
It also provides out of the box several options to watch changes into specific folders and allows to provide file extension as well.
Let’s illustrate this on the vote service, this one is a python flask application.
The first thing to do is to make sure nodemon is available in the service’s image.
To do so, we start by changing the Dockerfile of the service adding the following instruction:# Install nodemonRUN apk update && apk add nodejs && npm i -g nodemonNote1: Since Alpine 3.
8, npm is not installed with Node.
js, the nodejsnpm package needs to be added in the above command next to nodejs.
Thanks Omar Quiroz for pointing this out.
Note2: As nodemon will not be needed inside the production image, it could be good to add a condition based on a build arg; it could be installed in dev only.
The second thing is to override the command used to run the service so it uses nodemon.
The definition of the vote service can be changed to:services: vote: build: .
/vote command: nodemon –watch template –exec "python" app.
py volumes: — .
/vote:/app ports: — "5000:80" networks: — front-tier — back-tierWe provide some additional options to nodemon–exec “python”: indicates the type of application we need to run–watch template: on top of the app.
py script run, we want to watch changes done in the template folderIt is also possible to provide the list of extensions we need to watch through an -e flag.
The nodemon documentation provides a list of the available options.
Let’s testWe can now build the vote service so the changes we have done in the Dockerfile are taken into account, and then start the application.
$ docker-compose build vote$ docker-compose upThe vote interface is available on port 5000, the result one on port 5001.
Modify the source code of the vote serviceLet’s modify the selection options and change Cats into Kitten in the app.
py fileoption_a = os.
getenv(‘OPTION_A’, “Kitten”)option_b = os.
getenv(‘OPTION_B’, “Dogs”)We can observe the automatic reload of vote in the Compose logs.
vote_1 | * Detected change in ‘/app/app.
py’, reloadingvote_1 | * Restarting with statvote_1 | * Debugger is active!vote_1 | * Debugger PIN: 161–189–800Reloading the web interface shows the changesModify the source code of the result serviceLet’s also modify the labels which appear in the web interface of the result service.
Those ones are located in the views/index.
html file.
<head> <meta charset="utf-8"> <title>Kitten vs Dogs — Result</title> .
</head><body ng-controller="statsCtrl" > .
<div id="content-container"> <div id="content-container-center"> <div id="choice"> <div class="choice cats"> <div class="label">Kitten</div> <div class="stat">{{aPercent | number:1}}%</div> </div> <div class="divider"></div> <div class="choice dogs"> <div class="label">Dogs</div> <div class="stat">{{bPercent | number:1}}%</div> </div> </div></div>.
We can observe the automatic reload of result in the Compose logs.
result_1 | [nodemon] restarting due to changes…result_1 | [nodemon] starting `node server.
js`Reloading the web interface shows the changes.
SummaryIn this post, we have seen the setup needed for a developer to be able to work on the source code and have it automatically be taken into account into the application running in container.
Once we are happy with the code changes, we can use docker-compose to build and push the images to the registry and thus trigger the CI pipeline.
.. More details