Posted at

PHP Slim 環境構築(12) 前回構築した環境をEC2上に


PHP Slim 環境構築(12) 前回構築した環境をEC2上に


Introduction

前回は、DockerコンテナにNginxとPHP-FPMの両方を格納した構成をローカル環境に構築しました。

今回は、それをEC2上に構築します。

この一連のシリーズは、自分への備忘録が第一目的のため、相当不親切です。

すみません・・・


変更点

今回構築した環境では、ローカルPC上に構築したものをlocal環境、EC2上に構築したものをdevelopment環境と呼ぶことにしています。

今回は、前回作成したlocal環境をdevelopment環境にも適用するとともに、ソースを少し整理しました。


ソースツリー

今回の変更箇所です。

$(PROJECTROOT)

/aws
ec2-userdata.txt (新規)
/compose
/web_front
development.conf
/web_hoge
Dockerfile
settings.php (削除)
settings-development.yml (新規)
settings-local.yml (新規)
/src
/hoge
/lib
/Controller
DynamodbController.php
StorageController.php
MyContainerBuilder.php (新規)
/public
index.php
/test
RequestTest.php
composer.json
composer.lock


/aws/ec2-userdata.txt

EC2のユーザーデータをリポジトリに追加しました。


/aws/ec2-userdata.txt

#!/bin/bash

cat <<'SYS_EOF' >> /etc/sysctl.conf
net.core.somaxconn=65535
SYS_EOF

sysctl -p /etc/sysctl.conf

cat > /etc/security/limits.d/20-filelimits.conf <<'FLIMIT_EOF'
* soft nofile 32768
* hard nofile 32768
FLIMIT_EOF
ulimit -n 32768

amazon-linux-extras install docker -y
systemctl enable docker
usermod -a -G docker ec2-user
service docker start

curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

yum install -yq git

cd /home/ec2-user
git clone https://github.com/dynamitecoolguy/slimtemplate.git
cd slimtemplate
git checkout chapter12
chown -R ec2-user:ec2-user .

cd compose
docker-compose -f docker-compose-common.yml -f docker-compose-development.yml up -d



/compose/web_front/development.conf

前回変更したlocal.confと同様に、nginxは単なるreverse proxyになります。


/compose/web_front/development.conf

ssl_certificate /etc/certs/server.crt;

ssl_certificate_key /etc/certs/server.key;

ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

server {
listen 3128 default_server;
listen 8443 ssl default_server;
server_name _;
access_log /dev/stdout;
error_log /dev/stderr;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

location / {
proxy_pass http://web_hoge:3128/;
}
}



/compose/web_hoge/Dockerfile

環境用設定ファイルをPHP直書きから、ymlに変更しました。

また、各環境用の設定ファイルを用意することにしました。


/compose/web_hoge/Dockerfilee

...(略)...

# COPY settings.php /var/www/settings.php
COPY settings-${environment}.yml /var/www/settings.yml
...(略)...


/compose/web_hoge/settings-development.yml, settings-local.yml

以前までのsettings.phpをymlに変更しました。また、ソースに直書きしていたキーなどもこのファイルに抜き出しました。

なお、settings-development.ymlとsettings-local.ymlの内容は同じです。


/compose/web_hoge/settings-development.yml

userdb:

host: mysql
dbname: userdb
user: scott
password: tiger

logdb:
host: postgresql
dbname: logdb
user: root
password: hogehoge

redis:
host: redis

dynamodb:
endpoint: http://dynamodb:8000
region: ap-northeast-1
key: dummy-key
secret: dummy-secret
table: hogehoge

storage:
endpoint: http://storage:9000
region: ap-northeast-1
key: minio
secret: miniminio
bucket: dummy



/src/hoge/lib/Controller/DynamodbController.php, StorageController.php

ソースコードに直接記述していたdynamoDBのテーブル名、S3のバケット名を設定ファイルを参照するようにしました。


/src/hoge/lib/Controller/DynamodbController.php

...()...

$result = $dynamodb->describeTable([
//'TableName' => 'hogehoge'
'TableName' => $this->getTable()
]);

...()...
...('hogehoge'を変更した箇所が後3か所)...
...()...

private function getTable(): string
{
$setting = $this->container->get('settings')['dynamodb'];
return $setting['table'];
}
...()...



/src/hoge/lib/Controller/StorageController.php

...()...

//$s3->headBucket(['Bucket' => 'dummy']);
$s3->headBucket(['Bucket' => $this->getBucket()]);

...()...
...('dummy'を変更した箇所が後3か所)...
...()...

private function getBucket(): string
{
$setting = $this->container->get('settings')['storage'];
return $setting['bucket'];
}
...()...



/src/hoge/lib/MyContainerBuilder.php

設定用ymlファイルを読み込み、必要な定義を組み込み済みのContainerBuilderです。

DI\ContainerBuilderを継承しています。


/src/hoge/lib/MyContainerBuilder.php

<?php

namespace Hoge;

use Aws\Sdk;
use PDO;
use Psr\Container\ContainerInterface;

use DI\Container;
use DI\ContainerBuilder;
use Redis;

class MyContainerBuilder extends ContainerBuilder
{
public function __construct(string $containerClass = Container::class)
{
parent::__construct($containerClass);

$this->addDefaultDefinitions();
}

private function addDefaultDefinitions(): void
{
$parsed = yaml_parse_file(__DIR__ . '/../../settings.yml');

$this->addDefinitions(['settings' => $parsed]);
$this->addDefinitions([
'userdb' => function (ContainerInterface $container)
{
$settings = $container->get('settings')['userdb'];
$dsn = 'mysql:host=' . $settings['host'] . ';dbname=' . $settings['dbname'];
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];
return new PDO($dsn, $settings['user'], $settings['password'], $options);
},
'logdb' => function (ContainerInterface $container)
{
$settings = $container->get('settings')['logdb'];
$dsn = 'pgsql:host=' . $settings['host'] . ';dbname=' . $settings['dbname'];
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];
return new PDO($dsn, $settings['user'], $settings['password'], $options);
},
'redis' => function (ContainerInterface $container)
{
$settings = $container->get('settings')['redis'];
$redis = new Redis();
$redis->connect($settings['host']);
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);

return $redis;
},
'dynamodb' => function (ContainerInterface $container)
{
$settings = $container->get('settings')['dynamodb'];

$sdk = new Sdk([
'endpoint' => $settings['endpoint'],
'region' => $settings['region'],
'version' => '2012-08-10',
'credentials' => [
'key' => $settings['key'],
'secret' => $settings['secret']
]
]);

$dynamodb = $sdk->createDynamoDb();
return $dynamodb;
},
'storage' => function (ContainerInterface $container)
{
$settings = $container->get('settings')['storage'];
$sdk = new Sdk([
'endpoint' => $settings['endpoint'],
'region' => $settings['region'],
'version' => '2006-03-01',
'credentials' => [
'key' => $settings['key'],
'secret' => $settings['secret']
],
'use_path_style_endpoint' => true
]);
$s3 = $sdk->createS3();
return $s3;
}
]);
}
}



/src/hoge/public/index.php

DI部分を上記のMyContainerBuilderに分割したので、必要ない部分を削除しました。


/src/hoge/public/index.php

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteCollectorProxy;

use Hoge\ExampleAfterMiddleware;
use Hoge\ExampleBeforeMiddleware;
use Hoge\MyContainerBuilder;
use Hoge\Controller\PlayerController;
use Hoge\Controller\PlayerCreatedLogController;
use Hoge\Controller\RedisController;
use Hoge\Controller\DynamodbController;
use Hoge\Controller\StorageController;

/** @var Composer\Autoload\ClassLoader $loader */
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->addPsr4('Hoge\\', __DIR__ . '/../lib');

$containerBuilder = new MyContainerBuilder();
$container = $containerBuilder->build();

AppFactory::setContainer($container);
$app = AppFactory::create();

// Middleware
$contentLengthMiddleware = new \Slim\Middleware\ContentLengthMiddleware();
$app->add($contentLengthMiddleware);

$app->get('/', function (Request $request, Response $response, array $args) {
$response->getBody()->write('Hello, World!');
return $response;
})
->add(new ExampleBeforeMiddleware())
->add(new ExampleAfterMiddleware());

$app->group('/player', function (RouteCollectorProxy $group) {
$group->get('/{id}', PlayerController::class . ':get');
$group->post('', PlayerController::class . ':post');
$group->put('/{id}', PlayerController::class . ':put');
});

$app->group('/player_created', function (RouteCollectorProxy $group) {
$group->get('/{id}', PlayerCreatedLogController::class . ':get');
$group->get('', PlayerCreatedLogController::class . ':list');
});

$app->group('/redis', function (RouteCollectorProxy $group) {
$group->get('/{key}', RedisController::class . ':get');
$group->post('', RedisController::class . ':post');
$group->put('/{key}', RedisController::class . ':put');
$group->delete('/{key}', RedisController::class . ':delete');
});

$app->group('/dynamodb', function (RouteCollectorProxy $group) {
$group->get('/{key}', DynamodbController::class . ':get');
$group->post('', DynamodbController::class . ':post');
});

$app->group('/storage', function (RouteCollectorProxy $group) {
$group->get('/{filename}', StorageController::class . ':get');
$group->post('', StorageController::class . ':post');
});

$app->run();



/src/hoge/test/RequestTest.php

テスト用コードも、設定用ymlから情報を取り込むように変更しました。

(ソースコード略)。


/src/hoge/composer.json, compser.lock

yamlの使用に伴い、composer require yamlを行いました。


composer.json

{

"require": {
"slim/slim": "4.2.0",
"slim/psr7": "0.5.0",
"php-di/php-di": "6.0.9",
"ext-pdo": "^7.2",
"ext-json": "^1.6",
"ext-redis": "^3.1",
"ext-curl": "*",
"ext-yaml": "*",
"aws/aws-sdk-php": "^3.112"
},
"require-dev": {
"phpunit/phpunit": "^8.4"
}
}


ここまでのソース

こちらでどうぞ。