Python: New Relic instrumentation for Flask app deployed with Gunicorn

python-logoGunicorn is a WSGI HTTP server commonly used to run Flask applications in production.  If you are running these types of workloads in production, you should consider an observability platform such a New Relic to ensure availability, service levels, and visibility into transactions and logging.

In a previous article, we created a Docker image of a Flask application deployed on Gunicorn.  We will take this one step further by enabling the New Relic agent inside the container, and watch it report back to the New Relic One APM dashboard.

New Relic Account and License Key

To use New Relic, you first need to sign up for a free account (no credit card required).

Then click on the top-right menu pulldown, and select “API keys”, or use this direct link.

If you have an “INGEST – LICENSE”, then right-click the 3 dot icon next to it and “copy key”.  This is your New Relic License Key and will be passed into the container as an environment variable in a later section.

If you do not have an “INGEST-LICENSE”, then press the “Create a key” button, select the type “Ingest – License” and press “Create a key”.

Article prerequisites

To ensure that you are ready to build this image instrumented with a New Relic agent, first follow these sections from my previous article:

  • Docker prerequisites
  • Python prerequisites
  • Download project
  • Create Python virtualenv
  • Test Gunicorn server run directly
  • Build Gunicorn docker image
  • Test Gunicorn docker image

With these steps complete, you can move on to adding a New Relic Agent to the image.

Switch branch of project

In the previous article you downloaded the github project and used the 1.0.0 tag.  Now switch to the “2.0.0” tag of my github project code, which has the New Relic Agent.

# get tag with New Relic instrumentation
git checkout 2.0.0

# should report back '2.0.0'
grep ^VERSION Makefile

# should report back 'newrelic-admin' entry point
grep ^ENTRYPOINT Dockerfile

Dockerfile with New Relic Agent

Instead of the Dockerfile using gunicorn as the entrypoint, it now uses the newrelic-admin and passes the original entrypoint as parameters as described in the documentation.

# install agent
RUN pip install newrelic

# env vars that control reporting
ENV NEW_RELIC_APP_NAME="docker-gunicorn-hello-world-web"
ENV NEW_RELIC_LOG=stdout
ENV NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=true
ENV NEW_RELIC_LICENSE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ENV NEW_RELIC_LOG_LEVEL=info

# New Relic entrypoint calls gunicorn
ENTRYPOINT [ "newrelic-admin", "run-program", "/usr/local/bin/gunicorn", "--config", "gunicorn.conf.py", "--log-config", "gunicorn-logging.conf", "myflaskpackage.flask_module:app" ]

Build Gunicorn docker image instrumented with New Relic

Copy the configuration and source code into a container using our Dockerfile to build an image.

# build docker image
docker build -f Dockerfile -t fabianlee/docker-gunicorn-hello-world-web:2.0.0 .

If you list the docker images available, you should see the base python-slim-bullseye take up 125Mb, while our 2.0.0 custom image uses ~143Mb and 1.0.0 uses ~139Mb.

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
fabianlee/docker-gunicorn-hello-world-web 1.0.0 fa8e9a65c5c5 58 minutes ago 139MB
fabianlee/docker-gunicorn-hello-world-web 2.0.0 d8ae4158f2ac 3 hours ago 143MB
...
python 3.10.4-slim-bullseye c3e38abaf684 2 weeks ago 125MB

Run image instrumented with New Relic agent

To run this local image in the foreground, invoke docker and expose the port on local 8000.

# the NR license key you retrieved in first section
export NEW_RELIC_LICENSE_KEY=xxxxxxxxxxxx

# run container, pass NR license key as environment variable
docker run -it -p 8000:8000 -e NEW_RELIC_LICENSE_KEY=$NEW_RELIC_LICENSE_KEY --rm fabianlee/docker-gunicorn-hello-world-web:2.0.0

From another console, validate a curl against the localhost on port 8000.

$ curl http://localhost:8000
Hello, gunicorn
request 0 GET /
Host: localhost:8000

Run batch test against container

Instead of running just one curl request, let’s create some traffic that New Relic can measure.  Use Apache Bench to run 10k requests with 25 user concurrency against the container.

# install Apache Bench
$ sudo apt install apache2-utils -y

# run 10k tests with 25 concurrent users
$ ab -n 10000 -c 25 http://localhost:8000/

New Relic UI reports

Within 2-5 minutes, you should be able to see the 10k requests from above show up in the New Relic web UI.  Click on “APM” in the top menu bar, and you should see our “docker-gunicorn-hello-world-web” app.

Click on our application, and the summary will be brought up that displays the latest requests, including our large block of requests created by Apache Bench.

 

REFERENCES

New Relic, key types

New Relic, install the Python agent for docker

New Relic, Python Agent advanced usage

github project for this article