This is a guide to deploying https://github.com/sensu/web from source using Docker and nginx as a reverse proxy.

Why? Link to heading

Limitations

Sensu does already offer a Docker Image for Sensu Enterprise at https://hub.docker.com/r/sensu/sensu, complete with sensu-agent, sensu-backend and an included dashboard. However, the free version of Sensu Enterprise is limited to 100 agents. To support more agents, one has to compile sensu/sensu-go from source, which means that the web dashboard also has to be deployed separately.

Building sensu/sensu-go from source is covered here: https://github.com/sensu/sensu-go#building-from-source.

Resource usage

The deployment guide recommends running sensu/web in production by executing:

node scripts serve

which starts an express.js server. It watches the source code for changes, recompiles the changed files on-the-fly and hot-reloads the webpage to greatly facilitate local development.

However, this is by far not a production-ready deployment for the following reasons:

  • Takes ~5 minutes to compile and start
  • Consumes ~1-2 GB of RAM
  • Watches ~130.000 files for changes to re-compile and hot-reload - the guide mentions this as an unresolved bug and provides a workaround to increase the maximum number of file watches in the OS

Serving static files using nginx is a lot less resource-intensive: the Docker image size is reduced to 71 MB from ~2 GB for an image with node and all dependencies, and the running container consumes almost no memory or CPU resources at all compared to running a full node/express/webpack application.

How? Link to heading

Static build

When running the dev server, sensu/web proxies http requests to certain paths to the sensu-backend API, which can be configured through the environment variable API_URL.

When building a static website, there is no server-side processing that could proxy requests to the sensu-backend API, so we need nginx to do that. We need nginx anyway to serve the static files, so no problem here.

Nginx Reverse Proxy

We are going to replace the dev server with an nginx reverse proxy. The configuration of the dev server as seen in https://github.com/sensu/web/blob/master/scripts/serve.js#L25 is used as a basis for the nginx configuration.

As seen in line 25, requests to /auth /graphql and /api need to be forwarded to the sensu-backend API. All other requests should be forwarded to the deployment of sensu/web.

Deploy an nginx server at http://sensu.example.com with the following configuration:

upstream sensu-backend {
    server sensu-backend.example.com:8080;
}

upstream sensu-web {
    server sensu-web.example.com:5000;
}

server {
    listen 80;
    server_name sensu.example.com;

    location ~ ^/(auth|graphql|api) {
        proxy_pass http://sensu-backend;
    }

    location / {
        proxy_pass https://sensu-web;
    }
}

The Dockerfile

Since sensu/web is a SPA, we need to ensure that all incoming http requests are rewritten to /index.html as seen in line 32 of the dev server by the use of the node package connect-history-api-fallback. For that, we use another custom nginx configuration that we supply during the docker build. Here we also enable compression and caching for static assets to enable faster load times:

server {
    listen 5000;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
        error_page 404 = /index.html;
    }

    location ~ ^/(static)/ {
        gzip_static on;
        gzip_types text/xml text/css text/javascript application/x-javascript;
        expires max;
    }
}

We are using a multi-stage Dockerfile to clone the source, install dependencies, build the static site and then create a docker image based on nginx:alpine that just contains the static build output:

FROM node:14-alpine as build

WORKDIR /home/node/sensu/web

ENV NODE_ENV="production"

ARG VERSION

RUN apk add git && \
    git clone --depth 1 --branch "${VERSION}" https://github.com/sensu/web.git . && \
    yarn install --network-timeout 100000 && \
    yarn build

FROM nginx:alpine

COPY --from=build /home/node/sensu/web/build/app/ /usr/share/nginx/html

COPY ./etc/nginx/ /etc/nginx/

EXPOSE 5000

Deployment

Build the Dockerfile:

docker build --build-arg VERSION="v1.1.0" --tag "sensu-web-docker:v1.1.0" .

Run the image:

docker run -d --rm -p 5000:5000 sensu-web-docker:v1.1.0

Is it working? Link to heading

We are running a Sensu Go Cluster with 5 sensu-backend nodes and ~700 agents to monitor all servers related to a production application with ~2M monthly active users. Our deployment of sensu/web is running on an Openshift cluster, using the exact configuration described above, and it just works.


The code can be found here: https://github.com/baer95/sensu-web-docker