FargateのvCPUを理解する

タスクサイズ -> タスクメモリ(MiB)
タスクサイズ -> タスクCPU(単位)
タスクサイズ -> コンテナの定義 -> メモリ制限 -> ハード制限
タスクサイズ -> コンテナの定義 -> メモリ制限 -> ソフト制限
タスクサイズ -> コンテナの定義 -> CPUユニット数

### ハード制限(Memory)
コンテナに適用されるハードウェアのメモリ制限。上限を超えると強制的に終了となる。
ウェブアプリケーションだと300~500MBがおすすめとのこと

### ソフト制限(memoryReservation)
システムメモリ競合時に維持されるメモリ上限 
状況に応じてハード制限(設定されている場合)かインスタンス自体の利用可能上限まで消費
memory (<= memoryReservation) <= instance memory Fargateの動作ベース https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task-cpu-memory-error.html サポートされる CPU 値は、128CPU 単位 (0.125vCPUs) と10240CPU 単位 (10vCPUs)の間 ### CPU value 256(.25vCPU): 512, 1024, 2048 512(.5vCPU): 1024, 2048, 3072, 4096 1024(1vCPU): 2048, 3072, 4096, 5120, 6144, 7168 2048(2vCPU): Between 4096 and 16384 in increments of 1024 4096(4vCPU): Between 8192 and 30720 in increments of 1024 うーむ、何をベースにvCPU, Memoryを決めたら良いかイマイチわからんな。。

[CakePHP3.10] 認証コンポーネント

UsersController.php

use Cake\Auth\DefaultPasswordHasher;
use Cake\Event\Event;

    public function initialize(){
        parent::initialize();
        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');
        $this->loadComponent('Auth', [
                'authorize' => ['Controller'],
                'authenticate' => [
                    'Form' => [
                        'fields' => [
                            'username' => 'username',
                            'password' => 'password'
                        ]
                    ]
                ],
                'loginRedirect' => [
                    'controller' => 'Users',
                    'action' => 'login'
                ],
                'logoutRedirect' => [
                    'controller' => 'Users',
                    'action' => 'login'
                ],
                'logoutRedirect' => [
                    'controller' => 'Users',
                    'action' => 'logout',
                ],
                'authError' => 'ログインしてください。',
            ]);
    }

    function login(){
        if($this->request->isPost()){
            $user = $this->Auth->identify();
            if(!empty($user)){
                $this->Auth->setUser($user);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error('ユーザ名かパスワードが間違っています。');
        }
    }

    public function logout(){
        $this->request->session()->destroy();
        return $this->redirect($this->Auth->logout());
    }

    public function beforeFilter(Event $event){
        parent::beforeFilter($event);
        $this->Auth->allow(['login', 'index', 'add']);
    }

    public function isAuthorized($user = null){
        if($user['role'] === 'admin'){
            return true;
        }
        if($user['role'] === 'user'){
            return false;
        }
        return false;
    }

authorize, authenticate, loginRedirect, logoutRedirect, authError
$this->request->session()->destroy();

### login.ctpを作成

<div class="users form">
<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
	<fieldset>
		<legend>アカウント名とパスワードを入力して下さい。</legend>
		<?= $this->Form->input('username') ?>
		<?= $this->Form->input('password') ?>
	<fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>

AuctionBaseController.phpを作成し、継承させる
なるほどー

[CakePHP3.10] auctionサイトを作ろう

$ php composer.phar create-project –prefer-dist cakephp/app:3.* auction

bin/cake bake migration CreateUsers username:string[100] password:string[100] role:string[20]
bin/cake bake migration CreateBiditems user_id:integer name:string[100] finished:boolean endtime:datetime created
bin/cake bake migration CreateBidinfo bititem_id:integer user_id:integer price:integer created
bin/cake bake migration CreateBidrequests biditem_id:integer user_id:integer price:integer created
bin/cake bake migration CreateBidmessages bidinfo_id:integer user_id:integer message:text created
bin/cake migrations migrate
bin/cake bake all users
bin/cake bake all biditems
bin/cake bake all bidinfo
bin/cake bake all bidrequests
bin/cake bake all bidmessages

[CakePHP3.10] ページネーション

– コントローラのページネーション: 一定数ごとにレコードを取り出す
– ビューテンプレートのリンク

controller

$this->loadComponent('Paginator');

PeopleController.php

	public $paginate = [
		'limit' => 5,
		'sort' => 'id',
		'direction' => 'asc',
		'contain' => ['Messages'],
	];

	public function initialize(){
		parent::initialize();
		$this->loadComponent('Paginator');
	}

	public function index(){
		$data = $this->paginate($this->People);
		$this->set('data', $data);
	}

template

<p>This is People table records.</p>
<table>
<thead><tr>
	<th>id</th><th>name</th><th>mail</th><th>age</th><th>message</th>
</tr></thead>
<?php foreach($data->toArray() as $obj): ?>
<tr>
	<td><?=h($obj->id) ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"edit"]); ?>?id=<?=$obj->id ?>"><?=h($obj->name) ?></a></td>
	<td><?=h($obj->mail) ?></td>
	<td><?=h($obj->age) ?></td>
	<td><?php foreach($obj->messages as $item): ?>
	"<?=h($item->message) ?>"<br>
	<?php endforeach; ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"delete"]); ?>?id=<?=$obj->id ?>">delete</a></td>
</tr>
<?php endforeach; ?>
</table>
<div class="paginator">
	<ul class="pagination">
		<?=$this->Paginator->first(' |<< ' . '最初へ') ?>
		<?=$this->Paginator->prev(' << ' . '前へ') ?>
		<?=$this->Paginator->next('次へ ' . ' >> ') ?>
		<?=$this->Paginator->last('最後へ' . ' >>| ') ?>
	</ul>
</div>

custom finderを利用することもできる

なるほど、とりあえずOK 次に行こう

[CakePHP3.10] ヘルパー

$this->Html->doctype(document type)
$this->Html->charset(charset)
$this->Html->css(file name)
$this->Html->style()
$this->Html->script()
$this->Html->scriptStart()
$this->Html->scriptEnd()
$this->Html->link()
$this->Html->image()
$this->Html->script()
$this->Html->tableHeaders()
$this->Html->tableCells()

<table>
<?=$this->Html->tableHeaders(["title", "name", "mail"],
	["style"=>["background:#006; color:white"]]) ?>
<?=$this->Html->tableCells([["this is sample", "taro", "taro@yamada"],["this is sample", "taro", "taro@yamada"],["this is sample", "taro", "taro@yamada"]], ['style'=>['background:#ccf']], ['style'=>['background:#ccf']]) ?>
</table>

$this->Html->nestedList()

アクションの指定、クエリパラメータの指定、拡張子の指定などもUrlBuilderで指定できる

### Textヘルパー

<?=$this->Text->autoLinkUrls('http://google.com') ?>
<?=$this->Text->autoLinkEmails('hoge@gmail.com') ?>
<?=$this->Text->autoParagraph('one\ntwo\nthree') ?>

Numberヘルパー

<p>金額は、<?=$this->Number->currency(1234567, 'JPY') ?>です。</p>
<p>2桁で表すと、<?=$this->Number->precision(1234.56789, 2) ?>です。</p>
<p>2桁で表すと、<?=$this->Number->toPercentage(0.12345, 2, ['multiply'=>true]) ?>です。</p>

OK、大分いいところまで来た気がする

[CakePHP3.10]bakeとマイグレーション

config/bootstrap.php

Plugin::load('Migrations');

$ cake bake migration Create 項目1:タイプ

$ bin/cake bake migration CreateMoview title:string content:text stars:integer created:datetime
$ bin/cake bake migration CreateMovie title:string content:text stars:integer created:datetime

Creating file /home/vagrant/dev/cake/mycakeapp/config/Migrations/20220715040907_CreateMoview.php
Wrote `/home/vagrant/dev/cake/mycakeapp/config/Migrations/20220715040907_CreateMoview.php`

$ bin/cake migrations migrate
/config/Migrations/20220715040907_CreateMoview.php

$ bin/cake bake all movies
http://192.168.56.10:8080/movies

src/Model/Entity/Movie.php

    protected $_accessible = [
        'title' => true,
        'content' => true,
        'stars' => true,
        'created' => true,
    ];

src/Model/Table/MoviesTable.php

    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmptyString('id', null, 'create');

        $validator
            ->scalar('title')
            ->maxLength('title', 255)
            ->requirePresence('title', 'create')
            ->notEmptyString('title');

        $validator
            ->scalar('content')
            ->requirePresence('content', 'create')
            ->notEmptyString('content');

        $validator
            ->integer('stars')
            ->requirePresence('stars', 'create')
            ->notEmptyString('stars');

        return $validator;
    }

src/Controller/MoviesController.php

    public function index()
    {
        $movies = $this->paginate($this->Movies);

        $this->set(compact('movies'));
    }

    /**
     * View method
     *
     * @param string|null $id Movie id.
     * @return \Cake\Http\Response|null
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function view($id = null)
    {
        $movie = $this->Movies->get($id, [
            'contain' => [],
        ]);

        $this->set('movie', $movie);
    }

    /**
     * Add method
     *
     * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $movie = $this->Movies->newEntity();
        if ($this->request->is('post')) {
            $movie = $this->Movies->patchEntity($movie, $this->request->getData());
            if ($this->Movies->save($movie)) {
                $this->Flash->success(__('The movie has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The movie could not be saved. Please, try again.'));
        }
        $this->set(compact('movie'));
    }

    /**
     * Edit method
     *
     * @param string|null $id Movie id.
     * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function edit($id = null)
    {
        $movie = $this->Movies->get($id, [
            'contain' => [],
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $movie = $this->Movies->patchEntity($movie, $this->request->getData());
            if ($this->Movies->save($movie)) {
                $this->Flash->success(__('The movie has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The movie could not be saved. Please, try again.'));
        }
        $this->set(compact('movie'));
    }

    /**
     * Delete method
     *
     * @param string|null $id Movie id.
     * @return \Cake\Http\Response|null Redirects to index.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $movie = $this->Movies->get($id);
        if ($this->Movies->delete($movie)) {
            $this->Flash->success(__('The movie has been deleted.'));
        } else {
            $this->Flash->error(__('The movie could not be deleted. Please, try again.'));
        }

        return $this->redirect(['action' => 'index']);
    }

[CakePHP3.10] viewテンプレート

<p>This is People table records.</p>
<?=$this->Form->create($entity,
	['type'=>'post',
	'url'=>['controller'=>'Messages',
	'action'=>'index']]) ?>
	<fieldset class="form">
		person id
		<?= $this->Form->error('Messages.person_id'); ?>
		<?= $this->Form->text('Messages.person_id'); ?>
		Message
		<?= $this->Form->error('Messages.message'); ?>
		<?= $this->Form->text('Messages.message'); ?>
		<?= $this->Form->submit('投稿') ?>
	</fieldset>
<?=$this->Form->end() ?>

<hr>
<table>
	<thead>
		<tr><th>ID</th><th>Message</th><th>name</th><th>created at</th>
		</tr>
	</thead>
	<?php foreach($data->toArray() as $obj): ?>
	<tr>
		<td><?=h($obj->id) ?></td>
		<td><?=h($obj->message) ?></td>
		<td><?=h($obj->person->name) ?></td>
		<td><?=h($obj->created_at) ?></td>
	</tr>
	<?php endforeach; ?>
</table>
<p>This is People table records.</p>
<?=$this->Form->create(null, ["type"=>"post", "url"=>["controller"=>"People", "action"=>"index"]]) ?>
<div>find</div>
<div><?=$this->Form->text("People.find") ?></div>
<div><?=$this->Form->submit("検索") ?></div>
<?=$this->Form->end() ?>

<table>
<thead><tr>
	<th>id</th><th>name</th><th>messages</th>
</tr></thead>
<?php foreach($data->toArray() as $obj): ?>
<tr>
	<td><?=h($obj->id) ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"edit"]); ?>?id=<?=$obj->id ?>"><?=h($obj->name) ?></a></td>
	<td><?php foreach($obj->messages as $item): ?>
	"<?=h($item->message) ?>"<br>
	<?php endforeach; ?></td>
	<td><?=h($obj->age) ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"delete"]); ?>?id=<?=$obj->id ?>">delete</a></td>
</tr>
<?php endforeach; ?>
</table>

ubuntu20.04にredisを入れる

$ sudo apt update
$ sudo apt install redis-server
$ sudo vi /etc/redis/redis.conf

supervised systemd

$ sudo systemctl restart redis.service
$ sudo systemctl status redis
● redis-server.service – Advanced key-value store
Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor >
Active: active (running) since Thu 2022-07-14 09:02:47 UTC; 11s ago
Docs: http://redis.io/documentation,
man:redis-server(1)
Process: 273821 ExecStart=/usr/bin/redis-server /etc/redis/redis.conf (code>
Main PID: 273833 (redis-server)
Tasks: 4 (limit: 4677)
Memory: 2.0M
CGroup: /system.slice/redis-server.service
└─273833 /usr/bin/redis-server 127.0.0.1:6379

$ sudo apt install php-redis
$ redis-cli
127.0.0.1:6379> ping

set

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$redis->set('key', 'PHP');

$redis->close();

get

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$value = $redis->get('key');
print($value);

$redis->close();

ほうほう、php-redisも何となく理解した

セッションをCookieでもつか、サーバ側のファイルで持つか

### Cookieで持つ場合
■メリット
サーバにデータをため込まないので、サーバサイドの負荷を気にしないで良い
アプリケーションサーバを分散させても一貫性を持たせられる
■デメリット
ユーザ側にデータがあるため、改ざんリスクが高まる(Railsは暗号化によって対策してますが)
Cookieの仕様上、4KBの容量制限がある
Cookieを乗っ取られた場合等でもサーバサイドからセッションを破棄する手段がない
ユーザがcookieを有効にしていない場合、ログイン情報などがうまく保存されない

### サーバ側でファイルで持つ場合
CakePHP、Laravelのでファオルトで、CookieにセッションIDだけ持たせて、サーバサイドに実データをファイルとして置く方法
■メリット
サーバサイドにデータがあるので改ざんを回避できる
データ容量制限がない
サーバサイドでセッションの破棄ができる
■デメリット
アプリケーションサーバを分散させた場合、一貫性が確保できない
1セッションにつき1ファイル生成されるので、大量にセッションファイルが出来てファイルシステムに負担が掛かる

### KVS(NoSQL)
CookieにセッションIDだけ持たせて、Memcached、RedisなどサーバサイドのKVSに実データを置く方法。
■メリット
– サーバサイドにデータがあるので改ざんを回避できる
– データ容量制限がない
– サーバサイドでセッションの破棄ができる
– アプリケーションサーバを分散させても一貫性を持たせられる
– 大量の同時I/Oに強く、アクセスが増大しても大丈夫!
■デメリット
セッション専用にひとつデータベースが追加されるのでコストが増
https://qiita.com/SuguruOoki/items/6ca36ad1d366df6c98af

コストに問題なければredis(NoSQL)が良さそうではある