Hot-reloading on a dockerized flask app
--
At Hootsuite, my team was working with a dockerized flask app. Because we hadn’t set up hot-reloading, when we were making updates to the flask app and testing locally, we had to manually tear down the containers and bring them back up between each change. This took some time, and was mildly disruptive to our workflow, so I worked on setting up hot-reloading.
Table of Contents
Prerequisites
Setting up the flask app
First, let’s create a directory to house the code.
mkdir hot-reloading
cd hot-reloading
Create a requirements.txt
file.
touch requirements.txt
And add flask to the requirements.txt
file
Now let’s make the actual flask
app.
Check that the app is working.
export FLASK_APP=app.py
flask run
We export FLASK_APP
to tell flask where to find our application. flask run
has to be run in the same terminal as export FLASK_APP=app.py
is run.
Example output:
* Serving Flask app "app"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Visit the site, and you should see something like this:
Creating the dockerized flask app
If you skipped the previous step, you can use this starter code
Create the dockerfile for our flask app
We’ll have a separate dockerfile for Nginx, so let’s call this Dockerfile-flask
.
We start with the base Python 3 image.
FROM python:3
Create and designate /app
as the work directory.
RUN mkdir /app
WORKDIR /app
Expose the port for our app.
EXPOSE 5000
Because of how docker caches, it’s best to first copy the files that we don’t expect to change often. For this project, that’s the requirements.txt
file.
COPY requirements.txt .
Install the requirements.
RUN pip install -r requirements.txt
Copy the rest of the files.
COPY . .
Run uwsgi
with a config file.
CMD [ "uwsgi", "--ini", "app.ini" ]
Create the app.ini config for uwsgi
Add uwsgi to the requirements.txt
file
Create the dockerfile for Nginx
Start with the Nginx
image.
FROM nginx:latest
Expose port 80 for Nginx
.
EXPOSE 80
Replace the default config with a custom config
RUN rm /etc/nginx/conf.d/default.confCOPY app.conf /etc/nginx/conf.d
Create the app.conf for Nginx
Create the docker-compose yaml
image:
defines the name of the image, which can be anything we want
context:
tells docker where to look for files to build the image
dockerfile:
defines the dockerfile to use, which is just the dockerfiles we created in the previous steps.
volumes:
- "./:/app"
This mounts the root directory of our project on the host machine (./
) onto a directory named app under the root directory in the container (/app
).
ports:
- 5000:80
This maps port 5000 on our machine to port 80 on the Nginx
container. This is important because Nginx
listens on port 80 by default, but our app is running on port 5000.
You should now be able to reach the app on localhost:5000
after running docker-compose up
Adding hot-reloading
If you skipped the previous steps, you can use this starter code
uwsgi
has a touch-reload
configuration which allows us to designate a file to cause an update whenever the touch
command is applied on it. However, we can only supply one file, so we use watchman
to detect updates in our python files, and run touch <FILE>
to trigger the uwsgi
reload. I’ll be using a dedicated file named uwsgi-reload
. To do this, add touch-reload = uwsgi-reload
to the app.ini
file.
To check for updates to our python files, we can run watchman-make -p '**/*.py' -s 1 — run ‘touch uwsgi-reload’
.
-p '**/*.py'
specifies the pattern for which files to watch for changes. Specifically, we’re looking for any file that ends in .py
(*.py
) at any level (**/
) in our project.
-run 'touch uwsgi-reload'
tells watchman to run touch uwsgi-reload
whenever it detects an update. This will then trigger uwsgi
to reload, since it’s watching the uwsgi-reload
file.
-s 1
tells watchman to wait until there is 1 second without any updates before running touch uwsgi-reload
. This essentially means that it will wait for you to stop typing for 1 second before triggering the reload, so it’s not constantly triggering reloads as you’re typing.
Now, if we run the watchman-make
command after docker-compose up
, the project will reload whenever you make an update to a .py
file. We can use make
to run this automatically instead.
We run docker-compose up
with the -d
flag so it runs in a detached state. This allows the terminal to hit the watchman-make
command without shutting down the containers.
With this makefile, we can start the project with hot-reloading
with make start
, and stop it with make stop
(after terminating the watchman-make
command).
Now if you run make start
, you should be able to see your changes in real time. Try changing the 'Hello World!'
to something else!
Finalized hot-reloading dockerized flask app
Creation of dockerized flask app based on this article