Using NewRelic with Bref on AWS Lambda

3 minute read

Running serverless is great. When migrating our applications to use Bref we realized that we need a non-standard php-extension. With “non-standard” I mean that the extension is not included by default by Bref. With some help I manage to make every thing work as I wanted it to. Here is what I did:

I cloned the Bref repository to be able to build my own image. Then I added the NewRelic extension and config to php.Dockerfile in the /runtime/php directory.

## Install NewRelic

RUN \
  curl -L https://download.newrelic.com/php_agent/release/newrelic-php5-8.7.0.242-linux.tar.gz | tar -C /tmp -zx && \
  export NR_INSTALL_USE_CP_NOT_LN=1 && \
  export NR_INSTALL_DAEMONPATH=${INSTALL_DIR}/sbin/newrelic-daemon && \
  export NR_INSTALL_SILENT=1 && \
  /tmp/newrelic-php5-*/newrelic-install install && \
  rm -rf /tmp/newrelic-php5-* /tmp/nrinstall*

RUN echo $' \n\
extension = "newrelic.so" \n\
newrelic.appname = "BrefLambda" \n\
newrelic.license = "0123456789" \n\
newrelic.logfile = "/dev/null" \n\
newrelic.loglevel = "error" \n\
' >> ${INSTALL_DIR}/etc/php/php.ini

RUN mkdir -p ${INSTALL_DIR}/etc/newrelic && \
  echo "loglevel=error" > ${INSTALL_DIR}/etc/newrelic/newrelic.cfg && \
  echo "logfile=/dev/null" >> ${INSTALL_DIR}/etc/newrelic/newrelic.cfg

I disable logging since I run this on a read-only environment.

This is great and it should have been working on a normal server. However, NewRelic is configured to automatically start with the PHP-FPM server. Since Bref has it custom implementation of PHP-FPM that will not work. We need to start the the newrelic-daemon manually every time we load our Lambda function.

Let’s start with the function layer:

# runtime/php/layers/function/bootstrap

# Start NewRelic daemon
/opt/bref/sbin/newrelic-daemon -c /opt/bref/etc/newrelic/newrelic.cfg

Finally done, but let’s move on to the website layer. First I rename the boostrap file to bootstrap-php. Then I create a new bootstrap file with the following contents:

#!/bin/sh

# Start NewRelic daemon
/opt/bref/sbin/newrelic-daemon -c /opt/bref/etc/newrelic/newrelic.cfg

# Start PHP-FPM
/opt/bootstrap-php

Now I add a line in bootstrap-php to make sure NewRelic is not considering my requests as background jobs:

// bootstrap-php

// ...
ini_set('display_errors', '1');
error_reporting(E_ALL);

// Tell NewRelic that this is not a background job
newrelic_background_job(false);

// ...

Last thing to do is to make sure the new bootstrap-php is part of the runtime zip file.

# export.sh

# ...

# Create the PHP FPM layer
# Add files specific to this layer
cp /layers/fpm/bootstrap bootstrap
cp /layers/fpm/bootstrap-php bootstrap-php   # <---- New line
chmod 755 bootstrap bootstrap-php            # <---- Modified line
cp /layers/fpm/php.ini bref/etc/php/conf.d/bref.ini
cp /layers/fpm/php-fpm.conf bref/etc/php-fpm.conf
# Zip the layer
zip --quiet --recurse-paths /export/php-${PHP_SHORT_VERSION}-fpm.zip . --exclude "*php-cgi"
# Cleanup the files specific to this layer
rm bootstrap bootstrap-php                   # <---- Modified line
rm bref/etc/php/conf.d/bref.ini
rm bref/etc/php-fpm.conf

Now we are finally done. Just rebuild your php docker, package your zip files and publish them on AWS.

cd runtime/php
make build
make distribution
aws lambda publish-layer-version --layer-name bref-php-73 --profile happyr --zip-file fileb://../export/php-73.zip
aws lambda publish-layer-version --layer-name bref-php-73-fpm --profile happyr --zip-file fileb://../export/php-73-fpm.zip

In the response to the aws lambda publish-layer-version you will see the new ARN to use.

Resources:
    Website:
        Type: AWS::Serverless::Function
        Properties:
            FunctionName: 'my website'
            Runtime: provided
            Layers:
                - 'arn:aws:lambda:eu-central-1:123456789:layer:bref-php-73-fpm:1'

When everything is deployed, you need to make sure that your lambda function can reach internet. The NewRelic daemon must be able to send data to NewRelic’s servers. You should also add newrelic_start_transaction and newrelic_end_of_transaction on appropriate places in your application. Or else NewRelic will log really long response times. If you are using Symfony and EkinoNewRelic bundle, you should configure it as you were using Symfony HTTP Cache.

To accomplish this setup took me way more hours then I want to admit. So I hope this small post could help someone to be a little faster than I was.

Categories:

Updated:

Leave a Comment