AWS SAMテンプレートのWeb Backendを試す。

はじめに

サーバレスアプリケーショ作ることになり、SAM使おうと思い色々調べていたのですが、 大体がHello Worldであり、DBまでできるものを見たかったので、 同じくSAMのテンプレートで選べるWeb Backendを作ってみました。

SAMの環境構築

基本的にはAWSのガイドを見て進めれば出来ると思います。

Mac
macOS への AWS SAM CLI のインストール - AWS サーバーレスアプリケーションモデル

Win
Windows での AWS SAM CLI のインストール - AWS サーバーレスアプリケーションモデル

  1. AWS アカウントの作成
  2. IAM アクセス許可の設定
  3. Docker をインストールします。注意 Dockerは、アプリケーションをローカルでテストするための前提条件です。
  4. Homebrew をインストールします。
  5. AWS SAM CLI のインストール

1〜4は割愛します。

ステップ 5. AWS SAM CLI のインストール

インストール

$ brew tap aws/tap
$ brew install aws-sam-cli

インストールできたか確認

$ sam --version
SAM CLI, version 1.1.0

初回確認は意外と待ちました。(5秒位)

今回の流れでは不要ですが、アップグレードする場合

$ brew upgrade aws-sam-cli

Web Backendの作成

Hello Worldですが全体の流れは大体同じです。
チュートリアル Hello World アプリケーションの導入 - AWS サーバーレスアプリケーションモデル

ステップ 1. サンプルのダウンロード AWS SAM アプリケーション

sam init

*途中入力を求められます。

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

*1を選択

Which runtime would you like to use?
        1 - nodejs12.x
        2 - python3.8
        3 - ruby2.7
        4 - go1.x
        5 - java11
        6 - dotnetcore3.1
        7 - nodejs10.x
        8 - python3.7
        9 - python3.6
        10 - python2.7
        11 - ruby2.5
        12 - java8.al2
        13 - java8
        14 - dotnetcore2.1
Runtime: 1

* 1 - nodejs12.xを選択

Project name [sam-app]:   ただエンターを押せばsam-appでディレクトリが作られます。

Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git

AWS quick start application templates:
        1 - Hello World Example
        2 - Step Functions Sample App (Stock Trader)
        3 - Quick Start: From Scratch
        4 - Quick Start: Scheduled Events
        5 - Quick Start: S3
        6 - Quick Start: SNS
        7 - Quick Start: SQS
        8 - Quick Start: Web Backend
Template selection: 8

*Web Backendなので8を選択します。


-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: nodejs12.x
Dependency Manager: npm
Application Template: web-backend
Output Directory: .

Next steps can be found in the README file at ./sam-app/README.md

ステップ 2. アプリケーションの構築

$ cd sam-app
$ sam build

Building function 'getAllItemsFunction'
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Building function 'getByIdFunction'
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Building function 'putItemFunction'
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

ステップ 3. アプリケーションをAWSクラウドにデプロイする

sam deploy --guided

デプロイするためには色々と権限が必要です。
awsのcredentialによっては権限不足でエラーになるので適宜必要な権限を付与してあげてください。
私の場合は下記の権限を追加してデプロイ出来るようになりました。

ローカル実行時に「profileを指定することで」任意のcredentialを利用可能です。
使っているcredentialを変えたくない場合は、 ~/.aws/credentialsに定義した上でsamのコマンドを実行しましょう。 下記はローカルで動作させるためのコマンド

sam local start-api --env-vars env.json --profile default --debug
IAM
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:PassRole",
                "iam:DetachRolePolicy",
                "iam:TagRole",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:AttachRolePolicy",
                "iam:UpdateRole",
                "iam:PutRolePolicy"
            ],
            "Resource": "*"
        }
    ]
}
Cloudformation
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "cloudformation:DescribeStackEvents",
                "cloudformation:DescribeStackSet",
                "cloudformation:CreateChangeSet",
                "cloudformation:DescribeChangeSet",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:DeleteChangeSet",
                "cloudformation:DescribeStacks"
            ],
           "Resource": "*"
        }
    ]
}
API_Gateway
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "apigateway:DELETE",
                "apigateway:SetWebACL",
                "apigateway:PUT",
                "apigateway:PATCH",
                "apigateway:POST",
                "apigateway:GET"
            ],
            "Resource": "*"
        }
    ]
}
Lambda
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "lambda:CreateFunction",
                "lambda:AddPermission",
                "lambda:GetFunction",
                "lambda:GetFunctionConfiguration",
                "lambda:DeleteFunction",
                "lambda:RemovePermission"
            ],
            "Resource": "*"
        }
    ]
}
S3
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:PutBucketTagging",
                "s3:PutBucketPolicy",
                "s3:CreateBucket",
                "s3:DeleteBucketPolicy",
                "s3:DeleteBucket",
                "s3:PutBucketVersioning"
            ],
            "Resource": "*"
        }
    ]
}
DynamoDB
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "dynamodb:CreateTable",
                "dynamodb:DescribeTable"
            ],
            "Resource": "*"
        }
    ]
}

クリーンアップ

このチュートリアルを実行して作成した AWS リソースが不要になった場合は、
デプロイした AWS CloudFormation スタックを削除することでリソースを削除できます。

コンソールかコマンド実行のいずれか。

AWSコンソールからCloudformationのページへ行き、スタックよりsam-appを削除
  1. AWS マネジメントコンソール にサインインして、https://console.aws.amazon.com/cloudformationAWS CloudFormation コンソールを開きます。
  2. 左のナビゲーションペインで [スタック] を選択します。
  3. スタックのリストで、[sam-app] (または作成したスタックの名前) を選択します。
  4. [削除] を選択します。
・コマンド実行
aws cloudformation delete-stack --stack-name sam-app --region ap-northeast-1

※スタックの削除を行うには下記権限が別途必要

 cloudformation:DeleteStack

SAMでデプロイしたcredentialで出来上がったアプリケーションの確認をしようとすると色々権限なくて止まる。
ちゃんとやるならSAM作成とアプリ開発でポリシー分けるのがよさげです。

全部かちょっと怪しいですが、途中で追加した権限

"iam:PutRolePolicy",
"iam:GetRolePolicy"
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:DeleteTable"
"s3:DeleteBucket",
"s3:PutBucketVersioning"

Laravelバージョンアップした件(5.5 → 6.0)その2

前回 katutoki.hatenablog.com

ようやく落ち着いたので続きを書きます。

前回の振り返り

見出しだけ

  • configureMonologUsingのエラー
  • 'str'、'array'の修正
  • AbstractRequestのメソッド可視化レベル変更
  • proxy headerの修正
  • codeseptionの修正(バージョンアップ)
  • laravel-moduleのmigrate

Loggerの修正

configureMonologUsingが使えなくなり、logger.phpでログ周りの設定を管理するようになったため、
ログ周りの共通クラスで使用していたLog::getMonolog();でエラーに。

   $monolog = Log::getMonolog();  // 変更前
   $monolog = Log::getLogger();  // 変更後

通化しているロガーが厄介で、呼び出し側のパラメータでログファイル名が動的に変わるようにな作りで、
ログハンドラーを消したり設定したりをしており、単純にlogger.phpに定義できず。
上記のようなloggerの設定を変更している処理だったので、 下記サイトを参考にStreamHandlerを宣言して、ファイル名なんかを設定し直すようにして現行どおりのログにできました。感謝

【Laravel】monologでカスタムログ出力先を変更する方法メモ | SAGA.TXT

    // ロガー
    $log = new Logger('name'); // ログ名を引数にいれます。(ログが出力された時、)
 
    // ハンドラー(メイン処理)に入れる引数を用意する。
    $log_path =  storage_path() .'/logs/login.log'; //ファイルパス
    $log_level =  config('app.log_level'); // ログレベル
    $bubble = true; // 以降のハンドラに処理を続行させるかどうかのフラグ、デフォルトは、true
    $filePermission = 0777; //ログファイルのパーミッションを変更できるので、必要な場合は入れておく
 
    // ★ロガーにハンドラーをpush
    $log->pushHandler(new StreamHandler($log_path, $log_level , $bubble, $filePermission));

画面を開くとFaker\Factoryのエラー

ローカルでは発生しなかったが、STGデプロイしたら発生
テストデータなどで使うfakerをどこかしらで使っているようでした。
具体的な解決策はほとんど見つけられず、単純に対応できそうな下記の対応としました。
composer.jsonではrequire-devに定義されていたので、これをrequireに移動
"fzaninotto/faker": "~1.4"

色々と見つかるので、各バージョンのアップグレードガイドを見直して影響ないか確認することに。
全部見きれないので、影響度が高いものと中程度を中心に確認。

6.X

アップグレードガイド 6.x Laravel

Carbon 1.xのサポート停止

https://carbon.nesbot.com/docs/#api-carbon-2

  • ::create()のmonth、day未指定の場合、1固定となった。Carbon1の場合、カレントの値。
  • マイクロ秒まで精度向上
    日付イコールがイコールにならない可能性。

  • $date->jsonSerialize() and json_encode($date  配列で返していたものを文字列に変化

  • setToStringFormatのクロージャの書き方が変わった。
  • setWeekStartsAt、 setWeekEndsAt非推奨 範囲外の値を指定した場合、例外をスローしなくなった
  • isSameMonth、isCurrentMonth 異なる年の比較においてfalseを返すようになった。   isSameMonthの2番目のパラメーターまたはisCurrentMonthの最初のパラメーターとしてfalseを渡して、年を無視して比較できます。
  • ::compareYearWithMonth()、 ::compareYearWithMonth() 削除
  • ミックスイン時、$selfは不要、代わりに$thisを使用  ミックスインって何って思ったらtraitみたいな仕組みのよう。
     理解するのに一苦労しましたが、mixinを使う際のクロージャの引数に$selfを用いて呼び出し元の変数を参照できていたのが、
     $selfを書かなくても良くなり、$thisで参照できるようになったよ。ってことだと思う。
     carbonのgithubのテストコードか何かに書いてあったソースを見てようやく理解。
     私が見た実装には該当箇所がなかったので、検証はしていません。

Carbonのマクロ使用時におけるクロージャの引数のことだと思われます。
バージョン1では引数に$selfを必要としてたのが、引数が不要となり、$thisで同等の操作が可能になった。
下記Carbonのバージョン違いのテストコードから判断。

https://github.com/briannesbitt/Carbon/blob/version-1.next/tests/Carbon/Fixtures/Mixin.php

https://github.com/briannesbitt/Carbon/blob/master/tests/Carbon/Fixtures/Mixin.php

  • useMicrosecondsFallback、isMicrosecondsFallbackEnabled 削除
  • CarbonIntervalの不明なメソッドの呼び出しの振る舞い null → Exception
  • dayOfYearの開始値 0から → 1から

Redisデフォルトクライアント

現状影響はないが、現バージョンのpredisは7系で廃止となる予定とのこと。
可能であればphpredisへ切り替える。設定変更のみであれば対応したい。

データベースのCapsule::tableメソッド

illuminate/databaseの使用箇所がないため、影響なしと判断

EloquentのArrayableとtoArray

ArrayableとtoArrayで検索。Eloquentとは関係なさそうなため、影響なしと判断

EloquentのBelongsTo::updateメソッド

使用箇所がないと思われるため影響なしと判断。
belogToの実装はあったが、そのメソッドを呼び出している箇所がない。

Eloquentの主キータイプ

主キーがint型でないテーブルが影響
password_resets系が該当。
Laravelのauthにより生成されるテーブルのため問題なしと判断。

他のEloquentについてもprimarykey変数設定がされていたので、こちらも問題なし。
 

言語化のLang::trans and Lang::transChoiceメソッド

Langの使用箇所がないため、影響なしと判断

言語化のLang::getFromJsonメソッド

Langの使用箇所がないため、影響なしと判断

キュー再試行制限

artisanコマンドなのでひとまず影響なし。
環境によってはデプロイツールなどで仕様されている場合もあるかもですね。

メール確認再送信Route

resendの使用箇所がないため、影響なしと判断

メール確認ルートの変更

email/verifyの使用箇所がないため、影響なしと判断

Inputファサード

該当あり。
Input::get → Request::inputに変更
Input::flash(); → $request->flashに変更

5.8

アップグレードガイド 5.8 Laravel

キャッシュ持続時間が秒指定に

要修正

キャッシュロックの安全性向上

Cache::lock()->release()について使用箇所がないため、影響なしと判断

環境変数のパース

.envには#が含まれていると#の後ろが切れてしまう。クオートで囲っていれば問題なし。
使用箇所がないため、影響なし

Markdownファイルのディレクトリ変更

こういった使われ方はしていない。Markdownを用いたメールは未使用なので影響なし

Nexmo/Slack通知チャンネル

未使用であるため影響なし

新しいデフォルトパスワード長

システムでは最低6文字なのでで影響あるかと思いきや自前でバリデーションしているからか特に影響なし。

コンテナジェネレーターとタグ付けサービス

使用箇所がないため、影響なしと判断

SQLiteバージョン制約

使用箇所がないため、影響なしと判断

ヘルパから文字列と配列クラスへ

6.0の対応にて済み

遅延サービスプロバイダ

未使用と思われるため、影響なしと判断

PSR-16準拠

Illuminate\Cache\Repositoryを使用している箇所がないため、影響なしと判断
Illuminate\Support\Facades\Cacheがファサードのためこちらも調査
使用箇所が1箇所あり。
「キャッシュ持続時間が秒指定」の対応箇所と同じため、その確認で影響なしと判断。

不規則変化する複数形のモデル名

ルールに沿った命名であるか、$tableの指定があるため、影響なしと判断

IDが増加するカスタムピボットモデル

カスタムピボットモデルを使用しているかは不明。多対多リレーションは用いていないのでOKか?
Pivotでgrep。該当なしのため、影響なしと判断
カスタム中間テーブルモデルの定義を参考

Eloquent:リレーション 6.x Laravel

LaravelのPivotモデルを使い中間テーブルから関係を取得する - Qiita

Pheanstalk4.0

使用箇所がないため、影響なしと判断

5.7

アップグレードガイド 5.7 Laravel

svgディレクトリの追加

使用しているかは不明だが、コピーして持ってこようかと思いましたが、 特にガイドには記載がなかったが、5.8時点で削除されており、6.0にも存在していないので特に対応しないでおく。

or操作子

bladeのorが使えなくなり、??に変更
単純に置き換えて終了

Cache

アップグレードガイドに従って、各プロジェクトで実行
とわいえ、Cacheは基本redisを用いていることからディレクトリは使われておらず影響はなさそうですが、
バージョンに合わせてディレクトリ構成を変更。

mkdir -p storage/framework/cache/data
cp storage/framework/cache/.gitignore storage/framework/cache/data/.gitignore

storage/framework/cache/.gitignoreの内容を下記に修正

*
!data/
!.gitignore

Route::redirectメソッド

httpステータスが302で問題なかったので対応不要。

SQLite外部キー

使用箇所がないため、影響なしと判断

テンプレートテーマ

使用箇所がないため、影響なしと判断

ネストしたバリデーションデータ

ネストしてそうだったので動作しない可能性ある箇所を発見。 テストコードを作り確認。
すべてのバリデーションルールが実施できたため問題なし。

テスト

artisanでテストは行っていないので、影響なしと判断

5.6

アップグレードガイド 5.6 Laravel

artisan optimizeの廃止

未使用なので問題なし。

HTMLエンティティエンコーディング

 変更前:Blade(およびeヘルパ)は、HTMLエンティティをダブルエンコードしませんでした。
 変更後:ダブルエンコードするように。htmlspecialchars関数のデフォルト動作と同じ挙動になった。
 以前と同様の振る舞いをするには、Blade::withoutDoubleEncodingを用いる。

e(を用いているbladeを対象に、調査。
該当箇所あり、画面で直接確認。
エンコード前情報が出力されていたため、修正。

レートリミッターのtooManyAttemptsメソッド

未使用のため、影響なしと判断

morphsカラムのインデックス順

未使用のため、影響なしと判断

新しい設定ファイル

  • config/hashing.php 追加
  • config/logging.php 上記のログ対応で済み

configureMonologUsingメソッド

こちらも上記のログ対応で済み

ログWriterクラス

Writer指定箇所がないため影響なし

withSwiftMessageコールバック

実装箇所あり。カスタマイズ性の向上でありそうなので、影響なしと判断

ペジネーション Bootstrap4

ページネーションを確認する。見た目上変化なしのため問題なし
カスタマイズしていると引っかかる可能性あるかも。

新しく生成されたモデルの返却

200を限定して見ている処理はなさそう。 テストコードの一部にステータス200を見ているものがあるのでそこがエラーとなる可能性あり。 codeceptionのacceptanceのテストが該当箇所がありましたが、これらはバージョンアップ前からテスト動作しないため、確認できず、放置w

ValidatesWhenResolvedインターフェイス

未使用のため、影響なしと判断

リードオンリーenvヘルパ

https://readouble.com/laravel/5.8/ja/upgrade.html#deferred-service-providers 地味に影響あったのがこれ

テストコード内の共通処理でputenvでDB接続先を変えてたりしていたものが効かなくなっていることが判明。 configヘルパで代用。