Serving static using nginx with docker


Imagine common situation when we have a container with a web application and a container with nginx, and we want to serve the web app static using nginx. Sounds very simple but actually it isn’t.

Before I start, source code of the simple web application which I want to run inside a container:

from flask import Flask, url_for

app = Flask(__name__)

@app.route("/")
def main():
    return '''<h1>Hello world!</h1>
              <img src='{}' />'''.format(url_for('static', filename='image.png'))

if __name__ == '__main__':
    app.run(host='0.0.0.0')

And also for proper work of this app there should be image in static/image.png.

Back to the task, the first idea – put static in a volume. So Dockerfile for the application should be like this:

FROM python:3.4
EXPOSE 5000
VOLUME static
COPY . .
RUN pip install flask
ENTRYPOINT python main.py

It’s simple to build and to test:

docker build -t example/app .
docker run -p 5000:5000 --name app example/app

After that you can visit localhost:5000 and ensure that app works.

Now go to the container for nginx. First of all we should write config for nginx:

upstream app_upstream {
  server app:5000;
}

server {
  listen 80;

  location /static {
    alias /static;
  }

  location / {
    proxy_pass  http://app_upstream;
  }
}

And Dockerfile for it:

FROM nginx:1.7
RUN rm /etc/nginx/conf.d/*
COPY app.conf /etc/nginx/conf.d/

So now we can build and run nginx:

docker build -t example/nginx .
docker run -p 8080:80 --link app:app --volumes-from app example/nginx

And it works! You can visit localhost:8080 for ensuring. But actually it don’t – it’ll be not that cool if we want to scale this web app. There will be one container with static and web app and also n containers with just a web app.

So, the second idea – create a data volume container with static. Dockerfile for it:

FROM ubuntu:14.04
VOLUME static
COPY . static

And now we should build it, run it and restart nginx container:

docker build -t example/static .
docker run --name static example/static
docker run -p 8080:80 --link app:app --volumes-from static example/nginx

This variant works great, but isn’t it too complex? Maybe there is a more simpler solution? And probably it exists.

And, the third idea – just cache static in nginx. So we should update nginx config to something like this:

proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:30m max_size=1G;

upstream app_upstream {
  server app:5000;
}

server {
  listen 80;

  location /static {
    proxy_cache cache;
    proxy_cache_valid 30m;
    proxy_pass  http://app_upstream;
  }

  location / {
    proxy_pass  http://app_upstream;
  }
}

And Dockerfile for nginx to:

FROM nginx:1.7
RUN mkdir /tmp/cache
RUN chown www-data /tmp/cache
RUN rm /etc/nginx/conf.d/*
COPY app.conf /etc/nginx/conf.d/

So now we can build it and run it:

docker build -t example/nginx .
docker run -p 8080:80 --link app:app example/nginx

It works well and it’s very simple, for my projects I’ve chosen that solution. But it has one drawback – for updating static we should wait 30 minutes or we should restart nginx container.



comments powered by Disqus