Serverless PHP
dengan AWS Lambda

Rio Astamal
Founder BelajarAWS.com
15 Agustus 2020

Rio Astamal

2019 Baby sitter
2017 Lead Backend at ClearView Studios
2016 BelajarAWS.com
2015 Lead Dev at DominoPOS
2014 System Engineer at WowRack 2004 Freelance Web Developer

Materi Presentasi dan Kode Demo

Apa yang akan kamu pelajari

{Pengenalan Serverless} {Pengenalan AWS Lambda} {Serverless PHP} {Serverless Deployment} {Serverless Demo} {Profiling Lambda Function}*

Apa itu Serverless?

Serverless

Menjalankan kode aplikasi tanpa perlu memikirkan server (autoscaling dkk).
Dalam konteks ini adalah Function as a Service (FaaS).

Keuntungan Serverless

{Zero Administration} {Bayar per Eksekusi} {Idle = Gratis} {Auto-Scaling} {Microservice sejak awal} {Event-Driven} {Faster time to market}

VM vs Serverless

Yang akan kamu kerjakan

Task VM Serverless
Manage Auto-Scaling
Konfigurasi Server
Manage OS
Bayar Running Cost
Upload Kode

AWS Lambda

Layanan Serverless dari AWS yang mendukung penggunaan kode NodeJS, Python, Java, Go, .NET Core, Ruby dan Custom Runtime.
Biaya dihitung per 100ms durasi waktu dan Σ eksekusi.

Cara Kerja Lambda

Lambda Runtime

Gabungan dari sistem operasi, bahasa pemrograman dan pustaka-pustaka lain yang dibutuhkan untuk menjalankan kode.

Contoh Kode Python


def my_handler(event, context):
  return {
    "message": "Hello World"
  }
					

Contoh Event dari API Gateway


{
    "resource": "/",
    "path": "/",
    "httpMethod": "GET",
    "requestContext": {
        "resourcePath": "/",
        "httpMethod": "GET",
        "path": "/Prod/",
        ...
    },
    "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "accept-encoding": "gzip, deflate, br",
        "Host": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
        "X-Amzn-Trace-Id": "Root=1-5e66d96f-7491f09xmpl79d18acf3d050",
        ...
    },
    "multiValueHeaders": {
        "accept": [
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
        ],
        "accept-encoding": [
            "gzip, deflate, br"
        ],
        ...
    },
    "queryStringParameters": null,
    "multiValueQueryStringParameters": null,
    "pathParameters": null,
    "stageVariables": null,
    "body": null,
    "isBase64Encoded": false
}
                    

Bagaimana dengan Runtime PHP di AWS Lambda?

Tidak ada :( #justkidding

Perkenalkan Bref PHP

Bref adalah Runtime PHP, Framework dan deployment tool agar PHP dapat dijalankan di AWS Lambda.

Bref PHP Runtime

Dibuat dari amazonlinux:2018.03 Docker image

Layer Cocok untuk
php-* PHP Function (from scratch)
php-*-fpm Website/Framework
console Console (CLI)

Konsep Eksekusi Custom Runtime

Saatnya Demo

Serverless Demo App

App Tipe Lambda Layer
Terbilang HTTP API bref php-73
SQL Query Browser Normal Web bref php-73-fpm
New File Notification* S3 Event bref php-73
What is My IP* HTTP API + Normal Web bref php-73

*) Hanya menggunakan Bref PHP Runtime tanpa Bref Framework

Demo App - Terbilang

Sebuah layanan HTTP API yang akan mengubah angka "123" menjadi bentuk terbilang "seratus dua puluh tiga".

Install Bref dan Pustaka Terbilang

$ cat composer.json

{
  "require": {
	"bref/bref": "^0.5.29",
	"rioastamal/terbilang": "^1.0"
  }
}
					

						$ composer install -vvv
					

Inisialisasi Bref PHP


						$ ./vendor/bin/bref init
					

 What kind of lambda do you want to create? (you will be able to add more functions later by editing `serverless.yml`) [PHP function]:
  [0] PHP function
  [1] HTTP application
  [2] Console application
 > 0
					

File Handler index.php


<?php declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use RioAstamal\AngkaTerbilang\Terbilang;

/**
 * @param array $event
 * @see https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html for example
 * @return callable
 */
return function ($event)
{
	$angka = $event['queryStringParameters']['angka'] ?? '0';
	$options = isset($event['queryStringParameters']['pretty']) ? JSON_PRETTY_PRINT : 0 ;

	$response = [
		'angka' => $angka,
		'terbilang' => Terbilang::create()->t($angka)
	];

	return json_encode($response, $options);
};
					

Deploy Fungsi ke AWS Lambda


						$ serverless deploy
					

Memanggil Terbilang API


$ curl --get -d 'angka=1500,004,300' -d 'pretty' [END_POINT]
					

{
	"angka": "1500,004,300",
	"terbilang": "satu milyar lima ratus juta empat ribu tiga ratus"
}
					

Demo App - SQL Query Browser

Sebuah web app untuk melakukan query pada MySQL dan menampilkan hasilnya secara langsung.

Provision Amazon RDS MySQL


$ cd terraform/
$ export AWS_PROFILE=YOUR_PROFILE AWS_DEFAULT_REGION=YOUR_REGION
$ terraform apply

Outputs:

database = {
  "connect" = "mysql -u awsug -h [HOST_NAME] -p serverlessphp"
  "endpoint" = "[HOST_NAME]:3306"
  "engine" = "mysql 5.7.26"
}
					

Install Bref

$ cat composer.json

{
  "require": {
	"bref/bref": "^0.5.29",
  }
}
					

						$ composer install -vvv
					

Inisialisasi Bref PHP


						$ ./vendor/bin/bref init
					

 What kind of lambda do you want to create? (you will be able to add more functions later by editing `serverless.yml`) [PHP function]:
  [0] PHP function
  [1] HTTP application
  [2] Console application
 > 1
					

File Handler index.php


<?php

$dsn = $_POST['dsn'] ?? '';
$username = $_POST['username'] ?? 'root';
$password = $_POST['password'] ?? '';
$query = $_POST['query'] ?? '';
$pdo = null;

function exec_query($pdo, $query)
{
	if (empty($query)) {
		return '// Result will be shown here';
	}

	$counter = 0;

	try {
		echo '<table>';
		foreach ($pdo->query($query, PDO::FETCH_ASSOC) as $row) {
			// --- HEADING --- //
			if ($counter === 0) {
				echo '<thead><tr>';

				foreach ($row as $key => $val) {
					printf('<td>%s</td>', $key);
				}

				echo '</tr></thead><tbody>';
			}
			// --- /HEADING --- //

			echo '<tr>';
			foreach ($row as $val) {
				printf('<td>%s</td>', $val);
			}
			echo '</tr>';

			$counter++;
		}

		echo '</tbody></table>';
	} catch (PDOException $e) {
		printf('<pre>Error: %s</pre>', $e->getMessage());
	}
}

if ($dsn) {
	try {
		$pdo = new PDO($dsn, $username, $password);
		$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	} catch (PDOException $e) {
		echo 'Connection failed: ' . $e->getMessage();
	}
}

?><!DOCTYPE html>
<html lang="en">
<head>
	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
	<title>Query Browser on AWS Lambda</title>
	<style type="text/css">
		label {
			display: block;
		}
		div.label-wrapper {
			width: 300px;
		}
		table {
			border-collapse: collapse;
			width: 100%;
		}
		thead td {
			background-color: #f1f1f1;
			font-weight: bold;
		}
		tr td {
			border: 1px solid #ccc;
			padding: 4px 8px;
		}
	</style>
</head>
<body>
	<h2>Query Browser on AWS Lambda</h2>
	<form method="post">
		<label>MySQL DSN</label>
		<input type="text" name="dsn" placeholder="mysql:dbname=testdb;host=127.0.0.1" style="width: 300px;" value="<?= $dsn ?>">
		<label>Username / Password</label>
		<input type="text" name="username" placeholder="MySQL Username" value="<?= $username; ?>">
		<input type="password" name="password" placeholder="MySQL Password" value="<?= $password; ?>">

		<br><br>
		<label>SQL Query</label>
		<textarea style="width: 99%;height: 200px; font-size: 24px; color: blue;" name="query"><?= htmlentities($query) ?></textarea>
		<input type="submit" value="RUN">
	</form>

	<hr>
	<?php exec_query($pdo, $query) ?>
</body>
</html>
					

Deploy Web ke AWS Lambda


						$ serverless deploy
					

Demo App - S3 New File Notification

Sebuah fungsi Lambda yang akan mengirimkan notifikasi ke email jika ada file baru pada bucket tertentu.

Memaket Lambda


                        $ bash build.sh
					
> Creating zip file into build/function.zip ...
Generating autoload files
Generated autoload files
< Lambda function has been zipped.

Deploy Infra dan Lambda


				    	$ bash deploy.sh
					
...
Outputs:

demo = {
  "mailhog" = {
    "ec2_ip" = "18.139.0.199"
    "mailhog_auth" = "user = teknocerdas | password = orang.cerdas"
    "ssh_access" = "ssh -i ~/.ssh/teknocerdas.key -o LogLevel=quiet -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null [email protected]"
    "url" = "https://mailhog.teknocerdas.com"
  }
  "s3_bucket" = {
    "butcket_name" = "awsug06-sub-demo"
    "create_example" = "aws s3 cp ./YOUR_FILE.ext s3://awsug06-sub-demo/"
  }
}
					
Bootstrap File

#!/bin/sh
# Main entry point for Lambda execution

# Fail on error
set -e

# Outer main loop for execution.
# So if there's next event it will get execute it immediately
while true
do
  # All errors to STDOUT so it can be captured to CloudWatch
  /opt/bin/php "$_HANDLER" 2>&1
done
                    

File src/LambdaRuntime.php

File src/Handler.php


					

Test Email Notification

$ aws s3 cp /some/file.ext s3://[BUCKET_NAME]/

./end