Creating a Lambda Layer for running PHP on Lambda

Lambda Layers

  • A lambda layer is a bundle that can be uploaded and used with multiple lambda functions.
  • This method of putting PHP in to a layer is similar to the old trick of creating a single binary enterpreter which gets called from a shim in another language.
  • Claire Liguori of AWS has created a great tool called img2lamba which will convert a Docker Image to a Lambda Layer.
  • The method below is derived from the example included with the img2lambda project

Determine version of Amazon Linux currently in use with Lambda

  • We need to build our version of PHP on the same version of Amazon Linux used with Lambda.
  • Create a simple Lambda function to cat the contents of /etc/*release*, which will contain the Amazon Linux version.
exports.handler = function(event, context) {

  const execSync = require('child_process').execSync;
  const output   = execSync('cat /etc/*release*', { encoding: 'utf-8' });
  console.log('Determining Amazon Linux Version');
  console.log(output);
  console.log("Complete.");

  context.succeed("");
};

Lambda is currently using Amazon Linux AMI 2018.03

lambda_javascript_cat_release.png

docker pull amazonlinux:2018.03.0.20190514
docker pull lambci/lambda:provided

Build PHP and the lambda-php image.

  • The Dockerfile will use AmazonLinux to build PHP.
  • The Dockerfile will take the lambci/lambda:provided image and install PHP from the build image.
  • The final image will be our lambda-php image which willbe turned in to a layer.

lambda-php-layers.png

bootstrap

Test Lambda Functions

hello.php

  • Pass a JSON string with the variable name set and the script will parrot the name back as: hello, NAME
{
  "name": "Figaro"
}

goodbye.php

  • Same as hello.php, but with goodbye, NAME instead of hello, NAME
{
  "name": "Captain Hook"
}

Dockerfile

FROM amazonlinux:2018.03.0.20190514 as builder

##
## Set desired PHP Version
##
ARG php_version="7.3.6"
ARG amazonlinux_version="2018.03"

##
## Lock to ${amazonlinux_version} release and install compilation dependencies
##
RUN sed -i 's;^releasever.*;releasever=${amazonlinux_version};;' /etc/yum.conf && \
    yum clean all && \
    yum install -y autoconf \
                bison \
                bzip2-devel \
                gcc \
                gcc-c++ \
                git \
                gzip \
                libcurl-devel \
                libxml2-devel \
                make \
                openssl-devel \
                tar \
                unzip \
                zip

##
## Download and compile PHP
## Install Composer
##
RUN curl -sL https://github.com/php/php-src/archive/php-${php_version}.tar.gz | tar -xvz && \
    cd php-src-php-${php_version} && \
    ./buildconf --force && \
    ./configure --prefix=/opt/php-7-bin/ --with-openssl --with-curl --with-zlib --without-pear --enable-bcmath --with-bz2 --enable-mbstring --with-mysqli && \
    make -j 5 && \
    make install && \
    /opt/php-7-bin/bin/php -v && \
    curl -sS https://getcomposer.org/installer | /opt/php-7-bin/bin/php -- --install-dir=/opt/php-7-bin/bin/ --filename=composer

##
## Install PHP binary
## Copy in bootstrap script
##
RUN mkdir -p /lambda-php-runtime/bin && \
    cp /opt/php-7-bin/bin/php /lambda-php-runtime/bin/php

COPY runtime/bootstrap /lambda-php-runtime/

##
## Install Guzzle
##
RUN mkdir /lambda-php-vendor && \
    cd /lambda-php-vendor && \
    /opt/php-7-bin/bin/php /opt/php-7-bin/bin/composer require guzzlehttp/guzzle

#############
## Runtime ##
#############

FROM lambci/lambda:provided as runtime

# Layer 1
COPY --from=builder /lambda-php-runtime /opt/

# Layer 2
COPY --from=builder /lambda-php-vendor/vendor /opt/vendor

##############
## Function ##
##############

FROM runtime as function

COPY function/src /var/task/src/

Build the docker image

docker build -t lambda-php .

Test the custome lambda runtime by running the hello function:

docker run lambda-php hello '{"name": "World"}'

Install img2lambda

wget -O /usr/local/bin/img2lambda https://github.com/awslabs/aws-lambda-container-image-converter/releases/download/0.3.0/linux-amd64-img2lambda
chmod 0755 /usr/local/bin/img2lambda

Convert Docker image to Lambda layer and upload to AWS

img2lambda -i lambda-php:latest -r us-east-1 -n lambda-php

Use SAM to package lambda functions in function/src and deploy to AWS

export AWS_REGION="us-east-1"
export MY_LAMBDA_LAYER_BUCKET="devokun-lambda-php"

cd function

sed -i 's/^- /      - /' ../output/layers.yaml && \
sed -e "/LAYERS_PLACEHOLDER/r ../output/layers.yaml" \
    -e "s///" template.yaml > template-with-layers.yaml

aws s3 mb s3://${MY_LAMBDA_LAYER_BUCKET}

pip3 install aws-sam-cli

sam package --template-file template-with-layers.yaml \
            --output-template-file packaged.yaml \
            --region ${AWS_REGION} \
            --s3-bucket ${MY_LAMBDA_LAYER_BUCKET}

sam deploy --template-file packaged.yaml \
           --capabilities CAPABILITY_IAM  \
           --region ${AWS_REGION} \
           --stack-name img2lambda-php

Two functions will now exist:

  1. sam-php-example-hello
  2. sam-php-example-goodbye

Test hello

Create a Test Event with a data input of:

{
  "name": "Maui"
}

lambda-hello-test-maui.png

categories: AWS | Lambda | Lambda-Layers | PHP |