For those who are still getting up to speed with Amazon's FaaS Lambda, to quote their own blurb, "AWS Lambda is a compute service that lets you run code without provisioning or managing servers. AWS Lambda executes your code only when needed and scales automatically, from a few requests per day to thousands per second. You pay only for the compute time you consume - there is no charge when your code is not running."
It's an architecture that effectively means an organisation isn't required to spend time and money on building server infrastructure, orchestration and deploy scripts. It is therefore ideal for proof of concept and API endpoint type developments, but it would not be outlandish to suggest it could also be used for hosting websites (static but perhaps dynamic too by using Amazon's DynamoDB NoSQL offering).
The architecture is frequently referred to as serverless - that's a bit of a misnomer since it doesn't mean you don't need servers; you just don't need to build them. Serverless is also a CLI framework that allows developers to build and deploy auto-scaling, pay-per-execution, event driven functions. Serverless used in conjunction with Amazon Lambda is the focus in this introductory tutorial.
Lambda currently supports Node.js, Java, C# and Python applications. So if you are a PHP developer you are out of luck, right? Wrong! The serverless framework can be extended to use PHP libraries (and therefore PHP code) through a neat software shim developed by araines. This is appealing since PHP resource is plentiful and often cheap, opening up the world of Lambda to a very large community.
A prerequisite is getting node installed on your machine. I am a Mac user so I downloaded and installed from the Node.js Download page.
Next you need to check whether you are on PHP 7 or above. If not, and you are a Mac user, follow the instructions here and install composer by using the instructions here
Once you have that working, you can install serverless on the command line with:
# npm install -g serverless
Nigels-MacBook-Pro:Projects nigel$ serverless install --url https://github.com/araines/serverless-php -n serverless-php-demo Serverless: Downloading and installing "serverless-php"... Serverless: Successfully installed "serverless-php" as "serverless-php-demo" Nigels-MacBook-Pro:Projects nigel$ cd serverless-php-demo/ Nigels-MacBook-Pro:serverless-php-demo nigel$ ls -las total 96 0 drwxr-xr-x 16 nigel staff 544 30 Oct 11:50 . 0 drwxr-xr-x 6 nigel staff 204 30 Oct 11:50 .. 8 -rw-r--r-- 1 nigel staff 40 16 Apr 2017 .gitattributes 8 -rw-r--r-- 1 nigel staff 120 16 Apr 2017 .gitignore 8 -rw-r--r-- 1 nigel staff 569 16 Apr 2017 CHANGELOG.md 8 -rw-r--r-- 1 nigel staff 1103 16 Apr 2017 LICENSE 8 -rw-r--r-- 1 nigel staff 3181 16 Apr 2017 README.md 8 -rw-r--r-- 1 nigel staff 659 16 Apr 2017 buildphp.sh 8 -rw-r--r-- 1 nigel staff 430 16 Apr 2017 composer.json 0 drwxr-xr-x 3 nigel staff 102 30 Oct 11:50 config 8 -rw-r--r-- 1 nigel staff 783 16 Apr 2017 dockerfile.buildphp 8 -rw-r--r-- 1 nigel staff 1330 16 Apr 2017 handler.js 8 -rw-r--r-- 1 nigel staff 829 16 Apr 2017 handler.php 8 -rwxr-xr-x 1 nigel staff 133 16 Apr 2017 php 8 -rw-r--r-- 1 nigel staff 3012 30 Oct 11:50 serverless.yml 0 drwxr-xr-x 5 nigel staff 170 30 Oct 11:50 src Nigels-MacBook-Pro:serverless-php-demo nigel$
Nigels-MacBook-Pro:serverless-php nigel$ cp ~/Downloads/php . Nigels-MacBook-Pro:serverless-php nigel$ ls -lash total 49232 0 drwxr-xr-x 17 nigel wheel 578B 6 Nov 17:20 . 0 drwxr-xr-x 4 nigel wheel 136B 6 Nov 17:20 .. 0 drwxr-xr-x 12 nigel wheel 408B 6 Nov 17:20 .git 8 -rw-r--r-- 1 nigel wheel 40B 6 Nov 17:20 .gitattributes 8 -rw-r--r-- 1 nigel wheel 120B 6 Nov 17:20 .gitignore 8 -rw-r--r-- 1 nigel wheel 569B 6 Nov 17:20 CHANGELOG.md 8 -rw-r--r-- 1 nigel wheel 1.1K 6 Nov 17:20 LICENSE 8 -rw-r--r-- 1 nigel wheel 3.1K 6 Nov 17:20 README.md 8 -rw-r--r-- 1 nigel wheel 659B 6 Nov 17:20 buildphp.sh 8 -rw-r--r-- 1 nigel wheel 430B 6 Nov 17:20 composer.json 0 drwxr-xr-x 3 nigel wheel 102B 6 Nov 17:20 config 8 -rw-r--r-- 1 nigel wheel 783B 6 Nov 17:20 dockerfile.buildphp 8 -rw-r--r-- 1 nigel wheel 1.3K 6 Nov 17:20 handler.js 8 -rw-r--r-- 1 nigel wheel 829B 6 Nov 17:20 handler.php 49144 -rwxr-xr-x@ 1 nigel wheel 24M 6 Nov 20:48 php 8 -rw-r--r-- 1 nigel wheel 2.9K 6 Nov 17:20 serverless.yml 0 drwxr-xr-x 5 nigel wheel 170B 6 Nov 17:20 src Nigels-MacBook-Pro:serverless-php nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ composer install -o --no-dev Loading composer repositories with package information Updating dependencies Package operations: 7 installs, 0 updates, 0 removals - Installing psr/log (1.0.2): Downloading (100%) - Installing monolog/monolog (1.23.0): Downloading (100%) - Installing symfony/filesystem (v3.3.10): Downloading (100%) - Installing symfony/config (v3.3.10): Downloading (100%) - Installing psr/container (1.0.0): Downloading (100%) - Installing symfony/dependency-injection (v3.3.10): Downloading (100%) - Installing symfony/yaml (v3.3.10): Downloading (100%) Writing lock file Generating optimized autoload files Nigels-MacBook-Pro:serverless-php-demo nigel$
The configuration of the function is held in serverless.yml which you'll note contains a bunch of commented out code for more advanced usage which will cover in later blogs. However it would be a good idea to make a few changes so that you gain some familiarity with the code base. For now we want to change the service name to something more meaningful i.e. from serverless-php-demo to php-demo (lol) and the provider runtime may need changing depending upon which geographical Amazon region you intend to use. I am going for Ireland so us-east-1 becomes eu-west-1.
Once edited, the top few lines of your yml file should look like this:
Nigels-MacBook-Pro:serverless-php-demo nigel$ head -n 27 serverless.yml # Welcome to Serverless! # # This file is the main config file for your service. # It's very minimal at this point and uses default values. # You can always add more config options for more control. # We've included some commented out config examples here. # Just uncomment any of them to get that config option. # # For full config options, check the docs: # docs.serverless.com # # Happy Coding! service: php-demo # NOTE: update this with your service name # You can pin your service to only deploy with a specific Serverless version # Check out our docs for more details # frameworkVersion: "=X.X.X" provider: name: aws runtime: nodejs6.10 # you can overwrite defaults here # stage: dev region: eu-west-1 Nigels-MacBook-Pro:serverless-php-demo nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ sed -i '' 's/hello/demo/g' serverless.yml Nigels-MacBook-Pro:serverless-php-demo nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ tail -49 serverless.yml | head -9 functions: demo: handler: handler.handle environment: HANDLER: handler.demo # This is the service name which will be used (from services.yml) events: - http: path: demo method: get Nigels-MacBook-Pro:serverless-php-demo nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ cd config/ Nigels-MacBook-Pro:config nigel$ vi services.yml Nigels-MacBook-Pro:config nigel$ cat services.yml | grep demo handler.demo: Nigels-MacBook-Pro:config nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ pwd /Users/nigel/Projects/serverless-php-demo Nigels-MacBook-Pro:serverless-php-demo nigel$ cd src Nigels-MacBook-Pro:src nigel$ ls -las total 24 0 drwxr-xr-x 5 nigel staff 170 30 Oct 14:17 . 0 drwxr-xr-x 18 nigel staff 612 30 Oct 12:30 .. 8 -rw-r--r-- 1 nigel staff 2676 16 Apr 2017 Context.php 8 -rw-r--r-- 1 nigel staff 363 16 Apr 2017 Handler.php 8 -rw-r--r-- 1 nigel staff 418 30 Oct 14:17 HelloHandler.php Nigels-MacBook-Pro:src nigel$
Nigels-MacBook-Pro:src nigel$ mv HelloHandler.php DemoHandler.php Nigels-MacBook-Pro:src nigel$ vi DemoHandler.php Nigels-MacBook-Pro:src nigel$ cat DemoHandler.php | grep Demo class DemoHandler implements Handler Nigels-MacBook-Pro:src nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ cd config Nigels-MacBook-Pro:config nigel$ vi services.yml Nigels-MacBook-Pro:config nigel$ cat services.yml | grep Demo class: Raines\Serverless\DemoHandler Nigels-MacBook-Pro:config nigel$
Nigels-MacBook-Pro:config nigel$ cd ../vendor/composer/ Nigels-MacBook-Pro:composer nigel$ ls ClassLoader.php autoload_classmap.php autoload_psr4.php autoload_static.php LICENSE autoload_namespaces.php autoload_real.php installed.json Nigels-MacBook-Pro:composer nigel$ sed -i '' 's/Hello/Demo/g' autoload_classmap.php autoload_static.php Nigels-MacBook-Pro:composer nigel$
Nigels-MacBook-Pro:serverless-php-demo nigel$ pwd /Users/nigel/Projects/serverless-php-demo Nigels-MacBook-Pro:serverless-php-demo nigel$ serverless invoke local -f demo Got event [] [] { "statusCode": 200, "body": "Go Serverless v1.0! Your function executed successfully!" } Nigels-MacBook-Pro:serverless-php-demo nigel$
Before your code can be deployed you will need an AWS account with the Lambda service enabled. At the time of authoring this there was a free forever Lambda service available, albeit one obviously with restrictions on usage. However these restrictions were more than adequate for my projects.
You will also need to set up permissions for your function to be uploaded to Amazon. I created a user called serverless-admin with a policy of allowing Administrator access to all Amazon services. This can obviously be honed down should you feel this is not required.
I opted for the easiest way of enabling my Macbook access rights to upload the code - by navigating to Security Credentials (under Users) although other options are available. There is an excellent overview on the Serverless website. I set up an Access Key and a Secret Key. These can be set as environmental variables on the command line and the serverless deploy script will use them during the deploy process to authenticate.
Ok - using the keys, lets try out the deployment.
Nigels-MacBook-Pro:serverless-php-demo nigel$ export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXX Nigels-MacBook-Pro:serverless-php-demo nigel$ export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX Nigels-MacBook-Pro:serverless-php-demo nigel$ serverless deploy Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... Serverless: Checking Stack create progress... ..... Serverless: Stack create finished... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (921.53 KB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... .............................. Serverless: Stack update finished... Service Information service: php-demo stage: dev region: eu-west-1 stack: php-demo-dev api keys: None endpoints: GET - https://XXXXXXXX.execute-api.eu-west-1.amazonaws.com/dev/demo functions: demo: php-demo-dev-demo Serverless: Publish service to Serverless Platform... Service successfully published! Your service details are available at: https://platform.serverless.com/services/XXXXXXX/php-demo

Now navigate to your AWS Lambda dashboard and click on Functions - you should see the function you just created. I have two - the one we've created in this tutorial (the bottom one in the screenshot) and one I created a few days earlier which mirrored exactly the Hello function we started off with.
Ok we are now good to go. We can either run the invoke on the command line, or click on the link provided by AWS Lambda when we deployed. Or better still, lets do both :)
Nigels-MacBook-Pro:serverless-php-demo nigel$ serverless invoke -f demo Serverless: Load command run Serverless: Load command config Serverless: Load command config:credentials Serverless: Load command create Serverless: Load command install Serverless: Load command package Serverless: Load command deploy Serverless: Load command deploy:function Serverless: Load command deploy:list Serverless: Load command deploy:list:functions Serverless: Load command invoke Serverless: Load command invoke:local Serverless: Load command info Serverless: Load command logs Serverless: Load command login Serverless: Load command logout Serverless: Load command metrics Serverless: Load command remove Serverless: Load command rollback Serverless: Load command rollback:function Serverless: Load command slstats Serverless: Load command plugin Serverless: Load command plugin Serverless: Load command plugin:install Serverless: Load command plugin Serverless: Load command plugin:uninstall Serverless: Load command plugin Serverless: Load command plugin:list Serverless: Load command plugin Serverless: Load command plugin:search Serverless: Load command emit Serverless: Load command config Serverless: Load command config:credentials Serverless: Load command rollback Serverless: Load command rollback:function Serverless: Invoke invoke { "statusCode": 200, "body": "Go Serverless v1.0! Your function executed successfully!" } Nigels-MacBook-Pro:serverless-php-demo nigel$

Looking good on the browser too!

By clicking on the function name on the dashboard (see earlier dashboard image) it is possible to get some metrics. Click on the Monitoring tab and you'll see something similar to the above screenshot. Note that I had seven invocations in total (five of which were not part of this blog) and five of them failed. They failed purely because I had tried to use the initial corrupted download copy of PHP from GitHub. Took me a few minutes to figure out what had happened - and then I looped back and changed this blog accordingly, but all is good now :)