Part 3 Using DynamoDB for Session Management in PHP/Yii2 Application
version: '3' | |
services: | |
php: | |
build: . | |
volumes: | |
- ~/.composer-docker/cache:/root/.composer/cache:delegated | |
- ./:/app:delegated | |
ports: | |
- '8080:80' | |
environment: | |
AWS_REGION: 'ap-southeast-1' | |
AWS_ACCESS_KEY_ID: 'fake-access-key' | |
AWS_SECRET_ACCESS_KEY: 'fake-secret-key' | |
DYNAMODB_SESSION_TABLE_NAME: 'Sessions' | |
DYNAMODB_ENDPOINT_URL: 'http://localstack:4569' | |
depends_on: | |
- localstack | |
localstack: | |
image: localstack/localstack | |
volumes: | |
- ./bin/localstack-setup.sh:/localstack-setup.sh | |
ports: | |
- "4569:4569" | |
environment: | |
SERVICES: 'dynamodb' | |
DEFAULT_REGION: 'ap-southeast-1' | |
AWS_REGION: 'ap-southeast-1' | |
AWS_ACCESS_KEY_ID: 'fake-access-key' | |
AWS_SECRET_ACCESS_KEY: 'fake-secret-key' |
#!/usr/bin/env bash | |
# file: /bin/localstack-setup.sh | |
echo "Setting Up..." | |
aws --endpoint-url=http://localhost:4569 \ | |
--region=$AWS_REGION \ | |
dynamodb \ | |
create-table --table-name Sessions \ | |
--attribute-definitions AttributeName=id,AttributeType=S \ | |
--key-schema AttributeName=id,KeyType=HASH \ | |
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \ | |
1> /dev/null | |
aws --endpoint-url=http://localstack:4569 \ | |
--region=$AWS_REGION \ | |
dynamodb list-tables |
<?php | |
//file: /components/Session.php | |
namespace app\components; | |
use yii\web\Session as BaseSession; | |
use Aws\DynamoDb\DynamoDbClient; | |
class Session extends BaseSession { | |
private $client; | |
public $clientConfigs = []; | |
public $sessionConfigs = []; | |
public function init() | |
{ | |
$this->createDynamoDbClient(); | |
parent::init(); | |
} | |
protected function registerSessionHandler() | |
{ | |
$configs = array_filter($this->sessionConfigs); | |
$this->handler = $this->client->registerSessionHandler($configs); | |
parent::registerSessionHandler(); | |
} | |
private function createDynamoDbClient() | |
{ | |
$configs = array_filter($this->clientConfigs); | |
$this->client = new DynamoDbClient($configs); | |
} | |
} |
<?php | |
//file /controllers/TestController.php | |
namespace app\controllers; | |
use Yii; | |
use yii\web\Controller; | |
class TestController extends Controller { | |
public function actionIndex($name = '') { | |
$sessionId = Yii::$app->session->id; | |
if (!empty($name)) { | |
Yii::$app->session->set('name', $name); | |
} | |
$name = Yii::$app->session->get('name'); | |
return "My name is {$name}. My session ID is: {$sessionId}"; | |
} | |
} |
<?php | |
//file: /config/web.php | |
$params = require __DIR__ . '/params.php'; | |
$db = require __DIR__ . '/db.php'; | |
$config = [ | |
'id' => 'basic', | |
'basePath' => dirname(__DIR__), | |
'bootstrap' => ['log'], | |
'aliases' => [ | |
'@bower' => '@vendor/bower-asset', | |
'@npm' => '@vendor/npm-asset', | |
], | |
'components' => [ | |
'request' => [ | |
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation | |
'cookieValidationKey' => 'dFq59wCiBJbanLg794ATD34bQNZIrCLf', | |
], | |
'cache' => [ | |
'class' => 'yii\caching\FileCache', | |
], | |
'user' => [ | |
'identityClass' => 'app\models\User', | |
'enableAutoLogin' => true, | |
], | |
'errorHandler' => [ | |
'errorAction' => 'site/error', | |
], | |
'mailer' => [ | |
'class' => 'yii\swiftmailer\Mailer', | |
// send all mails to a file by default. You have to set | |
// 'useFileTransport' to false and configure a transport | |
// for the mailer to send real emails. | |
'useFileTransport' => true, | |
], | |
'log' => [ | |
'traceLevel' => YII_DEBUG ? 3 : 0, | |
'targets' => [ | |
[ | |
'class' => 'yii\log\FileTarget', | |
'levels' => ['error', 'warning'], | |
], | |
], | |
], | |
'db' => $db, | |
'urlManager' => [ | |
'enablePrettyUrl' => true, | |
'showScriptName' => false, | |
'rules' => [ | |
], | |
], | |
'session' => [ | |
'class' => app\components\Session::class, | |
'clientConfigs' => [ | |
'version' => '2012-08-10', | |
'region' => $_ENV['AWS_REGION'], | |
'endpoint' => $_ENV['DYNAMODB_ENDPOINT_URL'] ?? null | |
], | |
'sessionConfigs' => [ | |
'table_name' => $_ENV['DYNAMODB_SESSION_TABLE_NAME'] | |
] | |
] | |
], | |
'params' => $params, | |
]; | |
if (YII_ENV_DEV) { | |
// configuration adjustments for 'dev' environment | |
$config['bootstrap'][] = 'debug'; | |
$config['modules']['debug'] = [ | |
'class' => 'yii\debug\Module', | |
// uncomment the following to add your IP if you are not connecting from localhost. | |
//'allowedIPs' => ['127.0.0.1', '::1'], | |
]; | |
$config['bootstrap'][] = 'gii'; | |
$config['modules']['gii'] = [ | |
'class' => 'yii\gii\Module', | |
// uncomment the following to add your IP if you are not connecting from localhost. | |
//'allowedIPs' => ['127.0.0.1', '::1'], | |
]; | |
} | |
return $config; |
//file /cdk/webapp.ts | |
import cdk = require('@aws-cdk/core'); | |
import ecs = require("@aws-cdk/aws-ecs"); | |
import ecsPatterns = require("@aws-cdk/aws-ecs-patterns"); | |
import ecr = require('@aws-cdk/aws-ecr'); | |
import dynamodb = require('@aws-cdk/aws-dynamodb') | |
import { Cluster } from './cluster'; | |
interface WebAppProps { | |
readonly cluster: Cluster; | |
} | |
class WebApp extends cdk.Construct { | |
private fargateService: ecsPatterns.ApplicationLoadBalancedFargateService; | |
private sessionTable: dynamodb.Table; | |
public readonly service: ecs.IBaseService; | |
public readonly containerName: string; | |
public readonly ecrRepo: ecr.Repository; | |
constructor(scope: cdk.Construct, id: string, props: WebAppProps) { | |
super(scope, id); | |
this.ecrRepo = new ecr.Repository(this, 'ECRRepo'); | |
this.sessionTable = this.createSessionTable(); | |
this.fargateService = this.createService(props.cluster.ecsCluster); | |
this.service = this.fargateService.service; | |
this.containerName = this.fargateService.taskDefinition.defaultContainer!.containerName; | |
this.grantPermissions(); | |
this.addAutoScaling(); | |
this.output(); | |
} | |
private createService(cluster: ecs.Cluster) { | |
const region = cdk.Stack.of(this).region; | |
return new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', { | |
cluster: cluster, | |
taskImageOptions: { | |
image: ecs.ContainerImage.fromAsset('.'), | |
environment: { | |
AWS_REGION: region, | |
DYNAMODB_SESSION_TABLE_NAME: this.sessionTable.tableName, | |
} | |
}, | |
}); | |
} | |
private addAutoScaling() { | |
const autoScalingGroup = this.fargateService.service.autoScaleTaskCount({ | |
minCapacity: 2, | |
maxCapacity: 10 | |
}); | |
autoScalingGroup.scaleOnCpuUtilization('CpuScaling', { | |
targetUtilizationPercent: 50, | |
scaleInCooldown: cdk.Duration.seconds(60), | |
scaleOutCooldown: cdk.Duration.seconds(60), | |
}); | |
} | |
private createSessionTable(): dynamodb.Table { | |
return new dynamodb.Table(this, 'Sessions', { | |
partitionKey: { | |
name: 'id', | |
type: dynamodb.AttributeType.STRING, | |
} | |
}); | |
} | |
private grantPermissions() { | |
const taskDefinition = this.fargateService.taskDefinition; | |
this.ecrRepo.grantPull(taskDefinition.executionRole!); | |
const actions = [ | |
"dynamodb:GetItem", | |
"dynamodb:UpdateItem", | |
"dynamodb:DeleteItem", | |
"dynamodb:Scan", | |
"dynamodb:BatchWriteItem" | |
] | |
this.sessionTable.grant(taskDefinition.taskRole, ...actions); | |
} | |
private output() { | |
new cdk.CfnOutput(this, 'ECRRepoURI', {value: this.ecrRepo.repositoryUri}); | |
new cdk.CfnOutput(this, 'ServiceName', {value: this.service.serviceName}); | |
new cdk.CfnOutput(this, 'ContainerName', {value: this.containerName}); | |
new cdk.CfnOutput(this, 'SessionTableName', {value: this.sessionTable.tableName}); | |
} | |
} | |
export {WebApp, WebAppProps}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment