Subscribe to PHP Freaks RSS

Using img2lambda to publish your Serverless PHP layer

syndicated from planet-php.net on January 31, 2019

This interesting tweet by Clare Liguori came to my attention last week:

Img2lambda tweet2

This new img2lambda tool will take the layers of a Docker container and convert them to AWS layers for use in Lambda.

I poked around with Clare's example and updated my lambda-php project in order to understand how it works. I also rewrote my runtime's bootstrap to make it clearer.

The clever thing from my point of view is that you can build your PHP runtime layer locally using Docker and then publish the layers to AWS for use in your Lambda functions. This means you can now use the layer in different projects and have them all reference the same PHP runtime.

The magic of Docker

This is all done with the magic of Docker. Specifically, we create a Dockerfile that creates to containers:

  • A container to build the PHP binary
  • A container containing the layers that img2lambda will upload to AWS

Simplified, it looks like this:

# Build PHP in the Lambda container
FROM amazonlinux:2017.03.1.20170812 as builder

# PHP version of create ARG ver="7.3.1"

RUN sed -i 's;^releasever.*;releasever=2017.03;;' /etc/yum.conf && \ yum clean all && \ yum install -y autoconf bison gcc gcc-c++ make libcurl-devel {etc}

RUN curl -sL https://github.com/php/php-src/archive/php-${ver}.tar.gz | tar -xvz && \ cd php-src-php-${ver} && \ ./buildconf --force && \ ./configure --prefix=/opt/php/ --with-openssl --with-curl --{etc} && \ make install && \ /opt/php/bin/php -v

# copy php binary into /runtime/bin/php RUN mkdir -p /runtime/bin && \ cp /opt/php/bin/php /runtime/bin/php

# copy bootstrap files into /runtime COPY src/* /runtime/

# Create runtime container for use with img2lambda FROM lambci/lambda:provided as runtime

COPY --from=builder /runtime /opt/

The first part download and compiles the PHP binary and puts it into /runtime/bin/php and also copies in the bootstrap files required to make the layer act as a Lambda runtime.

The second part simply copies all the files in /runtime in the first container into a new container. This new container containers a single layer which is our PHP runtime.

To build it we do:

$ docker build -t lambda-php-runtime .

The tag name can be anything.

Create the the layer in AWS

To create the AWS layer we use img2lambda:

$ img2lambda -i lambda-php-runtime:latest -r eu-west-2 -n lambda-php73

This tool will find the layer in the runtime container and upload it to eu-west2 and then store the identifier in output/layers.json which looks like this.

[
  "arn:aws:lambda:eu-west-2:66...06:layer:lambda-php73-sha256-e3c4...2e618b:1"
]

Using in Serverless Framework

You can take the ARN number and use it for your actions in serverless.yml like this:

functions:
  hello:
    handler: handler.hello
    layers:
      - "arn:aws:lambda:eu-west-2:66...06:layer:lambda-php73-sha256-e3c4...2e618b:1"

This is the way that we can re-use our PHP runtime in all our actions across multiple projects.

Serverless also allows you to import data from JSON files into your serverless.yml file, so you can also do:

functions:
  hello:
    handler: handler.hello    # {file name}.{function name}. In this case: hello() in handler.php
    layers:
      ${file(../php-runtime/output/layers.json)}

This has the advantage that when you re-build and upload PHP runtime layer, your project picks it up, which could also be convenient.

Testing locally

One of the benefits of building the runtime in Docker is that we can run it locally for testing our Lambda function. As our runtime container inherits from lambci/lambda:provided, we get an environment that looks very much like Lambda itself.

To test locally, we use another container and inherits from our runtime one and copies in the PHP files for our function. The Dockerfile looks something like this:

FROM lambda-php-runtime as function

COPY handler.php /var/task/src/handler.php

We create our cont

Truncated by Planet PHP, read more at the original (another 1321 bytes)