- 投稿日:2020-08-10T23:39:27+09:00
[AWS] CodeStarを使うと、Lambdaで動作するSpring(Java)のプロジェクトをたった3分で構築できる!!
CodeStar
AWS CodeStarは、アプリケーションを迅速に開発・デプロイすることができるための環境を構築することができます。
いくつかのテンプレートが用意されており、マネジメントコンソール上からポチポチと選択していくだけで、開発プロジェクトが構築できてしまいます。構築される環境
- コードリポジトリ(CodeCommitと、GitHubを選択可能)
- CodeBuilde環境
- CodePipeline環境
- アプリケーション
- IDE(Cloud9選択時)
料金
CodeStar自体は料金は発生しません。
CodeStarによって構築されるコードリポジトリや、パイプライン、デプロイされたアプリケーションに係るリソース使用料のみです。注意事項
リージョンに東京リージョンを選択すると、IDEにCloud9を選択することができません(2020年8月現在)。
今回はCloud9で環境を構築したいので、リージョンにバージニア北部(us-east-1)を選択することとします。環境構築手順
まずは、マネジメントコンソールで、サービスよりCodeStarを選択します。
今回はIDEに
Cloud9を使用したいので、リージョンをバージニア北部にします。アプリケーションの種類や、言語を選択できます。
今回は、Lambda + Java Springのテンプレートでプロジェクトを作成してみようと思います。続いてコードリポジトリを選択します。
今回は、CodeCommitを選択してみますね。内容を確認して、プロジェクトを作成します。
最後にIDEを選択します。
今回は、AWS Cloud9です。リージョンによっては、Cloud9を選択できないのでご注意ください。
Cloud9を選択すると、インスタンスタイプを聞かれますので、今回はデフォルトのt2.microのまま進めます。プロジェクトとIDEの作成が始まります(裏でCloudFormationが動作します)。
プロジェクトが大体3分で環境構築が完了します。
IDEはEC2インスタンスの作成があるので、こちらも大体3分くらいかかります。
両方とも準備が完了すると、下記のような画面になります。リポジトリの状態
左側にあるメニューより「コード」を選択すると、CodeCommitの画面が表示されます。
Springのテンプレート構造が作成されており、buildspec.yml
まで作成されています。なお、
buildspec.yml
の中身はこんな感じです。codespec.ymlversion: 0.2 phases: install: runtime-versions: java: openjdk8 commands: # Upgrade AWS CLI to the latest version - pip install --upgrade awscli pre_build: commands: - echo Test started on `date` - mvn clean compile test build: commands: - echo Build started on `date` - mvn package shade:shade - mv target/HelloWorld-1.0.jar . - unzip HelloWorld-1.0.jar - rm -rf target tst src buildspec.yml pom.xml HelloWorld-1.0.jar - aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.yml post_build: commands: # Do not remove this statement. This command is required for AWS CodeStar projects. # Update the AWS Partition, AWS Region, account ID and project ID in the project ARN on template-configuration.json file so AWS CloudFormation can tag project resources. - sed -i.bak 's/\$PARTITION\$/'${PARTITION}'/g;s/\$AWS_REGION\$/'${AWS_REGION}'/g;s/\$ACCOUNT_ID\$/'${ACCOUNT_ID}'/g;s/\$PROJECT_ID\$/'${PROJECT_ID}'/g' template-configuration.json artifacts: files: - template-export.yml - template-configuration.jsonOpenJDK8で、Mavenでコンパイルされていることがわかりますね。
IDE
では、メニューからIDEを選択してみます。
すると、CLoud9が開始され、CodeCommitのリポジトリから、コードをgit clone
で自動的にコードを取得してくれます。あお、チームの設定を行うと、クロスアカウントでのコードリポジトリの共有や、モブプログラミングなんかも行うことができます。
[AWS] Cloud9でモブプログラミングの環境を作ってみる
アプリ実行
今回は、LambdaによるAPIです。
ダッシュボードにある「アプリの表示」ボタンを押すと、プロジェクトのテンプレートで最初に入っているAPIが呼び出されます。{"Output":"Hello World!"}具体的なAPI GatewayやLambdaはどうなっているのでしょう?
API Gateway
REST APIが一つできていることがわかりますね。
これ自体は、コード上の
tenpkate.yml
に定義されています。template.ymlResources: GetHelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'awscodestar-${ProjectId}-lambda-GetHelloWorld' Handler: com.aws.codestar.projecttemplates.handler.HelloWorldHandler Runtime: java8 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: GetEvent: Type: Api Properties: Path: / Method: get PostHelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'awscodestar-${ProjectId}-lambda-PostHelloWorld' Handler: com.aws.codestar.projecttemplates.handler.HelloWorldHandler Runtime: java8 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: PostEvent: Type: Api Properties: Path: / Method: postLambda
こちらは、Get用とPost用の2つが存在します。これも、API Gatewayで記載した
template.yml
で定義さている内容ですね。Pipelineを動かしてみる
Cloud9上で、
springproject/src/main/java/com/aws/codestar/project/handler/HelloWorldHandler.java
を編集してみます。HelloWorldHandler.java変更前return new GatewayResponse(new JSONObject().put("Output", "Hello World!").toString(), headers, 200);HelloWorldHandler.java変更後return new GatewayResponse(new JSONObject().put("Output", "Hello World Update!").toString(), headers, 200);ファイルを保存したら、Cloud9のターミナルから、CodeCommitにPushしてみましょう。
$ git add . $ git commit -m "update response." [master f0ba0d6] update response. Committer: EC2 Default User <ec2-user@ip-172-31-2-200.ec2.internal> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+), 1 deletion(-) $ git push Enumerating objects: 21, done. Counting objects: 100% (21/21), done. Compressing objects: 100% (6/6), done. Writing objects: 100% (11/11), 719 bytes | 239.00 KiB/s, done. Total 11 (delta 3), reused 0 (delta 0) To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/SpringProject 182e230..f0ba0d6 master -> masterパイプラインを確認すると、ビルドが開始されたことがわかります。
実は、ここでエラーになります。
確認してみると、どうやらテストで失敗しているようですね。[ERROR] Failures: [ERROR] HelloWorldHandlerTest.testHandleRequest:59 expected: <Hello World!> but was: <Hello World Update!> [INFO] [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 10.048 s [INFO] Finished at: 2020-08-10T14:16:40Z [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.0:test (default-test) on project HelloWorld: There are test failures. [ERROR] [ERROR] Please refer to /codebuild/output/src139859990/src/target/surefire-reports for the individual test results. [ERROR] Please refer to dump files (if any exist) [date]-jvmRun[N].dump, [date].dumpstream and [date]-jvmRun[N].dumpstream. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException [Container] 2020/08/10 14:16:40 Command did not exit successfully mvn clean compile test exit status 1 [Container] 2020/08/10 14:16:40 Phase complete: PRE_BUILD State: FAILED [Container] 2020/08/10 14:16:40 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: mvn clean compile test. Reason: exit status 1どうやら、テストコードが期待しているレスポンスと一致していないことが原因のようです。
というわけで、springproject/test/java/com/aws/codestar/project/handler/HelloWorldTest.java
を編集します。HelloWorldTest.java変更前private static final String EXPECTED_RESPONSE_VALUE = "Hello World!";HelloWorldTest.java変更後private static final String EXPECTED_RESPONSE_VALUE = "Hello World Update!";$ git add. $ git commit -m "update test code." [master f003591] update test code. Committer: EC2 Default User <ec2-user@ip-172-31-2-200.ec2.internal> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+), 1 deletion(-) $ git push Enumerating objects: 21, done. Counting objects: 100% (21/21), done. Compressing objects: 100% (5/5), done. Writing objects: 100% (11/11), 715 bytes | 238.00 KiB/s, done. Total 11 (delta 2), reused 0 (delta 0) To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/SpringProject f0ba0d6..f003591 master -> master次はBuildもDeployも成功しました!
しばらくアプリケーションにアクセスすると、結果が変わり、最新のアプリケーションがデプロイされたことが確認できます。{"Output":"Hello World Update!"}まとめ
今回は、IDEにCLoud9を使用するためにリージョンに東京リージョンを使用しませんでしたが、コードリポジトリから普通にGitコマンドを使えば、EclipseやVSSなどのIDEでも開発することはできます。
SAMコマンドなどは、ローカルに環境を作成してからDployする、というアプローチですが、CodeStarの場合は、まずAWS側に雛形を用意して、そこからアプリ〜ケーションを構築していく流れになります。
より簡単に開始するには、コードリポジトリの管理からパイプラインまでオールインワンになっているCodeStar の利用もありかと思います。
できれば、Cloud9が早いうちに東京リージョン対応になることを望みますが、それを差し引いても利用価値はありそうですね。
- 投稿日:2020-08-10T23:39:27+09:00
[AWS] CodeStarを使うと、Lambdaで動作するSpring(Java)のプロジェクトをたった3分で構築できるのです!!
CodeStar
AWS CodeStarは、アプリケーションを迅速に開発・デプロイすることができるための環境を構築することができます。
いくつかのテンプレートが用意されており、マネジメントコンソール上からポチポチと選択していくだけで、開発プロジェクトが構築できてしまいます。構築される環境
- コードリポジトリ(CodeCommitと、GitHubを選択可能)
- CodeBuilde環境
- CodePipeline環境
- アプリケーション
- IDE(Cloud9選択時)
料金
CodeStar自体は料金は発生しません。
CodeStarによって構築されるコードリポジトリや、パイプライン、デプロイされたアプリケーションに係るリソース使用料のみです。注意事項
リージョンに東京リージョンを選択すると、IDEにCloud9を選択することができません(2020年8月現在)。
今回はCloud9で環境を構築したいので、リージョンにバージニア北部(us-east-1)を選択することとします。環境構築手順
まずは、マネジメントコンソールで、サービスよりCodeStarを選択します。
今回はIDEに
Cloud9を使用したいので、リージョンをバージニア北部にします。アプリケーションの種類や、言語を選択できます。
今回は、Lambda + Java Springのテンプレートでプロジェクトを作成してみようと思います。続いてコードリポジトリを選択します。
今回は、CodeCommitを選択してみますね。内容を確認して、プロジェクトを作成します。
最後にIDEを選択します。
今回は、AWS Cloud9です。リージョンによっては、Cloud9を選択できないのでご注意ください。
Cloud9を選択すると、インスタンスタイプを聞かれますので、今回はデフォルトのt2.microのまま進めます。プロジェクトとIDEの作成が始まります(裏でCloudFormationが動作します)。
プロジェクトが大体3分で環境構築が完了します。
IDEはEC2インスタンスの作成があるので、こちらも大体3分くらいかかります。
両方とも準備が完了すると、下記のような画面になります。リポジトリの状態
左側にあるメニューより「コード」を選択すると、CodeCommitの画面が表示されます。
Springのテンプレート構造が作成されており、buildspec.yml
まで作成されています。なお、
buildspec.yml
の中身はこんな感じです。codespec.ymlversion: 0.2 phases: install: runtime-versions: java: openjdk8 commands: # Upgrade AWS CLI to the latest version - pip install --upgrade awscli pre_build: commands: - echo Test started on `date` - mvn clean compile test build: commands: - echo Build started on `date` - mvn package shade:shade - mv target/HelloWorld-1.0.jar . - unzip HelloWorld-1.0.jar - rm -rf target tst src buildspec.yml pom.xml HelloWorld-1.0.jar - aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.yml post_build: commands: # Do not remove this statement. This command is required for AWS CodeStar projects. # Update the AWS Partition, AWS Region, account ID and project ID in the project ARN on template-configuration.json file so AWS CloudFormation can tag project resources. - sed -i.bak 's/\$PARTITION\$/'${PARTITION}'/g;s/\$AWS_REGION\$/'${AWS_REGION}'/g;s/\$ACCOUNT_ID\$/'${ACCOUNT_ID}'/g;s/\$PROJECT_ID\$/'${PROJECT_ID}'/g' template-configuration.json artifacts: files: - template-export.yml - template-configuration.jsonOpenJDK8で、Mavenでコンパイルされていることがわかりますね。
IDE
では、メニューからIDEを選択してみます。
すると、CLoud9が開始され、CodeCommitのリポジトリから、コードをgit clone
で自動的にコードを取得してくれます。なお、チームの設定を行うと、クロスアカウントでのコードリポジトリの共有や、モブプログラミングなんかも行うことができます。
[AWS] Cloud9でモブプログラミングの環境を作ってみる
アプリ実行
今回は、LambdaによるAPIです。
ダッシュボードにある「アプリの表示」ボタンを押すと、プロジェクトのテンプレートで最初に入っているAPIが呼び出されます。{"Output":"Hello World!"}具体的なAPI GatewayやLambdaはどうなっているのでしょう?
API Gateway
REST APIが一つできていることがわかりますね。
これ自体は、コード上の
tenpkate.yml
に定義されています。template.ymlResources: GetHelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'awscodestar-${ProjectId}-lambda-GetHelloWorld' Handler: com.aws.codestar.projecttemplates.handler.HelloWorldHandler Runtime: java8 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: GetEvent: Type: Api Properties: Path: / Method: get PostHelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'awscodestar-${ProjectId}-lambda-PostHelloWorld' Handler: com.aws.codestar.projecttemplates.handler.HelloWorldHandler Runtime: java8 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: PostEvent: Type: Api Properties: Path: / Method: postLambda
こちらは、Get用とPost用の2つが存在します。これも、API Gatewayで記載した
template.yml
で定義さている内容ですね。Pipelineを動かしてみる
Cloud9上で、
springproject/src/main/java/com/aws/codestar/project/handler/HelloWorldHandler.java
を編集してみます。HelloWorldHandler.java変更前return new GatewayResponse(new JSONObject().put("Output", "Hello World!").toString(), headers, 200);HelloWorldHandler.java変更後return new GatewayResponse(new JSONObject().put("Output", "Hello World Update!").toString(), headers, 200);ファイルを保存したら、Cloud9のターミナルから、CodeCommitにPushしてみましょう。
$ git add . $ git commit -m "update response." [master f0ba0d6] update response. Committer: EC2 Default User <ec2-user@ip-172-31-2-200.ec2.internal> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+), 1 deletion(-) $ git push Enumerating objects: 21, done. Counting objects: 100% (21/21), done. Compressing objects: 100% (6/6), done. Writing objects: 100% (11/11), 719 bytes | 239.00 KiB/s, done. Total 11 (delta 3), reused 0 (delta 0) To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/SpringProject 182e230..f0ba0d6 master -> masterパイプラインを確認すると、ビルドが開始されたことがわかります。
実は、ここでエラーになります。
確認してみると、どうやらテストで失敗しているようですね。[ERROR] Failures: [ERROR] HelloWorldHandlerTest.testHandleRequest:59 expected: <Hello World!> but was: <Hello World Update!> [INFO] [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 10.048 s [INFO] Finished at: 2020-08-10T14:16:40Z [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.0:test (default-test) on project HelloWorld: There are test failures. [ERROR] [ERROR] Please refer to /codebuild/output/src139859990/src/target/surefire-reports for the individual test results. [ERROR] Please refer to dump files (if any exist) [date]-jvmRun[N].dump, [date].dumpstream and [date]-jvmRun[N].dumpstream. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException [Container] 2020/08/10 14:16:40 Command did not exit successfully mvn clean compile test exit status 1 [Container] 2020/08/10 14:16:40 Phase complete: PRE_BUILD State: FAILED [Container] 2020/08/10 14:16:40 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: mvn clean compile test. Reason: exit status 1どうやら、テストコードが期待しているレスポンスと一致していないことが原因のようです。
というわけで、springproject/test/java/com/aws/codestar/project/handler/HelloWorldTest.java
を編集します。HelloWorldTest.java変更前private static final String EXPECTED_RESPONSE_VALUE = "Hello World!";HelloWorldTest.java変更後private static final String EXPECTED_RESPONSE_VALUE = "Hello World Update!";$ git add. $ git commit -m "update test code." [master f003591] update test code. Committer: EC2 Default User <ec2-user@ip-172-31-2-200.ec2.internal> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+), 1 deletion(-) $ git push Enumerating objects: 21, done. Counting objects: 100% (21/21), done. Compressing objects: 100% (5/5), done. Writing objects: 100% (11/11), 715 bytes | 238.00 KiB/s, done. Total 11 (delta 2), reused 0 (delta 0) To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/SpringProject f0ba0d6..f003591 master -> master次はBuildもDeployも成功しました!
しばらくアプリケーションにアクセスすると、結果が変わり、最新のアプリケーションがデプロイされたことが確認できます。{"Output":"Hello World Update!"}まとめ
今回は、IDEにCloud9を使用するためにリージョンに東京リージョンを使用しませんでしたが、コードリポジトリから普通にGitコマンドを使えば、EclipseやVSSなどのIDEでも開発することはできます。
SAMコマンドなどは、ローカルに環境を作成してからDployする、というアプローチですが、CodeStarの場合は、まずAWS側に雛形を用意して、そこからアプリ〜ケーションを構築していく流れになります。
より簡単に開始するには、コードリポジトリの管理からパイプラインまでオールインワンになっているCodeStarの利用もありかと思います。
今回は、Lambda + Springの組み合わせでしたが、他にもたくさんのテンプレートがあるので、アプリケーションの要件や、開発メンバーのスキルセットにあわせて、プロジェクト構成を選択できるのもうれしいです。
できれば、Cloud9が早いうちに東京リージョン対応になることを望みますが、それを差し引いても利用価値はありそうですね。
- 投稿日:2020-08-10T23:39:27+09:00
[AWS] CodeStarで、Lambda上でSpringのプロジェクトをたった3分で構築する
CodeStar
AWS CodeStarは、アプリケーションを迅速に開発・デプロイすることができるための環境を構築することができます。
いくつかのテンプレートが用意されており、マネジメントコンソール上からポチポチと選択していくだけで、開発プロジェクトが構築できてしまいます。構築される環境
- コードリポジトリ(CodeCommitと、GitHubを選択可能)
- CodeBuilde環境
- CodePipeline環境
- アプリケーション
- IDE(Cloud9選択時)
料金
CodeStar自体は料金は発生しません。
CodeStarによって構築されるコードリポジトリや、パイプライン、デプロイされたアプリケーションに係るリソース使用料のみです。注意事項
リージョンに東京リージョンを選択すると、IDEにCloud9を選択することができません(2020年8月現在)。
今回はCloud9で環境を構築したいので、リージョンにバージニア北部(us-east-1)を選択することとします。環境構築手順
まずは、マネジメントコンソールで、サービスよりCodeStarを選択します。
今回はIDEに
Cloud9を使用したいので、リージョンをバージニア北部にします。アプリケーションの種類や、言語を選択できます。
今回は、Lambda + Java Springのテンプレートでプロジェクトを作成してみようと思います。続いてコードリポジトリを選択します。
今回は、CodeCommitを選択してみますね。内容を確認して、プロジェクトを作成します。
最後にIDEを選択します。
今回は、AWS Cloud9です。リージョンによっては、Cloud9を選択できないのでご注意ください。
Cloud9を選択すると、インスタンスタイプを聞かれますので、今回はデフォルトのt2.microのまま進めます。プロジェクトとIDEの作成が始まります(裏でCloudFormationが動作します)。
プロジェクトが大体3分で環境構築が完了します。
IDEはEC2インスタンスの作成があるので、こちらも大体3分くらいかかります。
両方とも準備が完了すると、下記のような画面になります。リポジトリの状態
左側にあるメニューより「コード」を選択すると、CodeCommitの画面が表示されます。
Springのテンプレート構造が作成されており、buildspec.yml
まで作成されています。なお、
buildspec.yml
の中身はこんな感じです。codespec.ymlversion: 0.2 phases: install: runtime-versions: java: openjdk8 commands: # Upgrade AWS CLI to the latest version - pip install --upgrade awscli pre_build: commands: - echo Test started on `date` - mvn clean compile test build: commands: - echo Build started on `date` - mvn package shade:shade - mv target/HelloWorld-1.0.jar . - unzip HelloWorld-1.0.jar - rm -rf target tst src buildspec.yml pom.xml HelloWorld-1.0.jar - aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.yml post_build: commands: # Do not remove this statement. This command is required for AWS CodeStar projects. # Update the AWS Partition, AWS Region, account ID and project ID in the project ARN on template-configuration.json file so AWS CloudFormation can tag project resources. - sed -i.bak 's/\$PARTITION\$/'${PARTITION}'/g;s/\$AWS_REGION\$/'${AWS_REGION}'/g;s/\$ACCOUNT_ID\$/'${ACCOUNT_ID}'/g;s/\$PROJECT_ID\$/'${PROJECT_ID}'/g' template-configuration.json artifacts: files: - template-export.yml - template-configuration.jsonOpenJDK8で、Mavenでコンパイルされていることがわかりますね。
IDE
では、メニューからIDEを選択してみます。
すると、CLoud9が開始され、CodeCommitのリポジトリから、コードをgit clone
で自動的にコードを取得してくれます。あお、チームの設定を行うと、クロスアカウントでのコードリポジトリの共有や、モブプログラミングなんかも行うことができます。
アプリ実行
今回は、LambdaによるAPIです。
ダッシュボードにある「アプリの表示」ボタンを押すと、プロジェクトのテンプレートで最初に入っているAPIが呼び出されます。{"Output":"Hello World!"}具体的なAPI GatewayやLambdaはどうなっているのでしょう?
API Gateway
REST APIが一つできていることがわかりますね。
これ自体は、コード上の
tenpkate.yml
に定義されています。template.ymlResources: GetHelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'awscodestar-${ProjectId}-lambda-GetHelloWorld' Handler: com.aws.codestar.projecttemplates.handler.HelloWorldHandler Runtime: java8 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: GetEvent: Type: Api Properties: Path: / Method: get PostHelloWorld: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'awscodestar-${ProjectId}-lambda-PostHelloWorld' Handler: com.aws.codestar.projecttemplates.handler.HelloWorldHandler Runtime: java8 Role: Fn::GetAtt: - LambdaExecutionRole - Arn Events: PostEvent: Type: Api Properties: Path: / Method: postLambda
こちらは、Get用とPost用の2つが存在します。これも、API Gatewayで記載した
template.yml
で定義さている内容ですね。Pipelineを動かしてみる
Cloud9上で、
springproject/src/main/java/com/aws/codestar/project/handler/HelloWorldHandler.java
を編集してみます。HelloWorldHandler.java変更前return new GatewayResponse(new JSONObject().put("Output", "Hello World!").toString(), headers, 200);HelloWorldHandler.java変更後return new GatewayResponse(new JSONObject().put("Output", "Hello World Update!").toString(), headers, 200);ファイルを保存したら、Cloud9のターミナルから、CodeCommitにPushしてみましょう。
$ git add . $ git commit -m "update response." [master f0ba0d6] update response. Committer: EC2 Default User <ec2-user@ip-172-31-2-200.ec2.internal> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+), 1 deletion(-) $ git push Enumerating objects: 21, done. Counting objects: 100% (21/21), done. Compressing objects: 100% (6/6), done. Writing objects: 100% (11/11), 719 bytes | 239.00 KiB/s, done. Total 11 (delta 3), reused 0 (delta 0) To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/SpringProject 182e230..f0ba0d6 master -> masterパイプラインを確認すると、ビルドが開始されたことがわかります。
実は、ここでエラーになります。
確認してみると、どうやらテストで失敗しているようですね。[ERROR] Failures: [ERROR] HelloWorldHandlerTest.testHandleRequest:59 expected: <Hello World!> but was: <Hello World Update!> [INFO] [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 10.048 s [INFO] Finished at: 2020-08-10T14:16:40Z [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.0:test (default-test) on project HelloWorld: There are test failures. [ERROR] [ERROR] Please refer to /codebuild/output/src139859990/src/target/surefire-reports for the individual test results. [ERROR] Please refer to dump files (if any exist) [date]-jvmRun[N].dump, [date].dumpstream and [date]-jvmRun[N].dumpstream. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException [Container] 2020/08/10 14:16:40 Command did not exit successfully mvn clean compile test exit status 1 [Container] 2020/08/10 14:16:40 Phase complete: PRE_BUILD State: FAILED [Container] 2020/08/10 14:16:40 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: mvn clean compile test. Reason: exit status 1どうやら、テストコードが期待しているレスポンスと一致していないことが原因のようです。
というわけで、springproject/test/java/com/aws/codestar/project/handler/HelloWorldTest.java
を編集します。HelloWorldTest.java変更前private static final String EXPECTED_RESPONSE_VALUE = "Hello World!";HelloWorldTest.java変更後private static final String EXPECTED_RESPONSE_VALUE = "Hello World Update!";$ git add. $ git commit -m "update test code." [master f003591] update test code. Committer: EC2 Default User <ec2-user@ip-172-31-2-200.ec2.internal> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly: git config --global user.name "Your Name" git config --global user.email you@example.com After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+), 1 deletion(-) $ git push Enumerating objects: 21, done. Counting objects: 100% (21/21), done. Compressing objects: 100% (5/5), done. Writing objects: 100% (11/11), 715 bytes | 238.00 KiB/s, done. Total 11 (delta 2), reused 0 (delta 0) To https://git-codecommit.us-east-1.amazonaws.com/v1/repos/SpringProject f0ba0d6..f003591 master -> master次はBuildもDeployも成功しました!
しばらくアプリケーションにアクセスすると、結果が変わり、最新のアプリケーションがデプロイされたことが確認できます。{"Output":"Hello World Update!"}まとめ
今回は、IDEにCLoud9を使用するためにリージョンに東京リージョンを使用しませんでしたが、コードリポジトリから普通にGitコマンドを使えば、EclipseやVSSなどのIDEでも開発することはできます。
SAMコマンドなどは、ローカルに環境を作成してからDployする、というアプローチですが、CodeStarの場合は、まずAWS側に雛形を用意して、そこからアプリ〜ケーションを構築していく流れになります。
より簡単に開始するには、コードリポジトリの管理からパイプラインまでオールインワンになっているCodeStar の利用もありかと思います。
できれば、Cloud9が早いうちに東京リージョン対応になることを望みますが、それを差し引いても利用価値はありそうですね。
- 投稿日:2020-08-10T23:31:14+09:00
LocustでWebサイトの性能測定をする(とりあえずDockerで動かしてみた編)
はじめに
Webサイトの性能測定といえばJMeterが定番な感じだが、そもそもなかなか起動させるまでの道のりが長くて学習コスト的にアレだったので、シナリオがPythonでお手軽に書けるという噂のLocustを使ってみた。
今回は、EC2でDockerでインストールしてとりあえず動かすところまで。
ちなみに、t2.microでは動作が不安定なので、t3.mediumくらいで起動すると良い感じであった。Dockerのインストール
これはもう定番の手順だけど一応書いておく。
yumの最新化$ sudo yum update -yDockerのインストール$ sudo yum install -y dockerDockerの開始$ sudo service docker startec2-userをdockerグループに追加$ sudo usermod -a -G docker ec2-userここで1回ログアウトして再ログイン(セッションを作り直さないと設定が反映されない)。
Dockerの起動確認$ docker info↓こんな感じで標準出力されればOK。ダメならDockerが起動してないとかなんとかといったメッセージが出る。
Client: Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 (以下略)docker-composeインストールのためにユーザ変更$ sudo -idocker-composeを拾ってきてインストール(バージョンは適宜変更する)# curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composeパーミッション変更# chmod +x /usr/local/bin/docker-compose戻る# exitこれでDockerの仕様準備完了!簡単!
Locustの動くコンテナを作る
以下のようにDockerfileを書こう。
Pythonを動く環境を作って、locustをインストールして、ローカルのlocustfile.pyをコピーして起動!
ファイルはlocustfile.py固定になってしまうので、適宜変更したらリロードして起動する。DockerfileFROM python RUN pip install locust WORKDIR /locust COPY ./locustfile.py . CMD ["locust"]これを、docker-compose.yml で設定して
docker-compose up
する。docker-compose.ymlversion: '3' services: locust: build: . ports: - "8089:8089"だがちょっと待て。肝心のlocustfile.pyが無いと起動しない。
以下が起動のための最低セットだ。locustfile.pyfrom locust import HttpUser, task, between class QuickstartUser(HttpUser): wait_time = between(1, 1) @task def get_contents(self): self.client.get("/test-contents.html")試験対象のホストにはtest-contents.htmlを置いておこう。
さて、これで
docker-compose up
したら、EC2に8089でHTTP接続する。いざ、動かす!
HTTP接続すると、以下のように設定画面が出る。
接続ユーザ数と、ユーザ生成/秒と、ホスト名だ。
ホスト名の後ろに、locustfile.pyを付けてアクセスするイメージだ。適当に値を入れて起動すると、
といった感じで負荷状況が表示される。
さらに、Chartsを選択すると、
グラフも見られるぞ!
「Download Data」からこのグラフのネタが取れるかと思いきや、取れたcsvにはこの情報は含まれていなかった。なぜ……。
ということで、まずは性能測定の足掛かりができた!
- 投稿日:2020-08-10T23:30:12+09:00
AWS CodeDeploy で Auto Scaling グループ に複数のターゲットグループを紐づける
お疲れ様です。
タイトルの通り備忘録です。
どこかの記事を参考にしたと思ったのですが、見つからなかったので知っている方がいれば教えてください?♂️用意するもの
- appspec.yml
- 紐づけ処理用のシェルスクリプト(before_allow_traffic.sh)
以上
appspec.yml
このファイルは、デプロイを管理するために CodeDeploy によって使用されるファイルです。
appspec.ymlversion: 0.0 os: linux ...中略... hooks: BeforeAllowTraffic: - location: scripts/before_allow_traffic.sh timeout: 300 runas: rootトラフィックを許可する処理で before_allow_traffic.sh を実行します。
before_allow_traffic.sh
スクリプト内にTODOコメントが書いてありますが、
ターゲットグループを直接指定しています。
もしオリジナルのAuto Scalingグループから取得する方法があれば教えてください?♂️before_allow_traffic.sh#!/usr/bin/env bash # Blue/Greenデプロイ時にAutoScalingGroupのターゲットグループが空になるので、引き継ぐ query "TargetGroups[*].TargetGroupArn" --output text) autoscalingGroupName=$(aws deploy get-deployment --deployment-id ${DEPLOYMENT_ID} --query "deploymentInfo.targetInstances.autoScalingGroups" --output text) if [[ "$autoscalingGroupName" != "None" ]] then # TODO ターゲットグループは直接指定ではなくオリジナルから取りたい if [[ "${DEPLOYMENT_GROUP_NAME}" == "***DEPLOYMENT_GROUP_NAME***" ]] then aws autoscaling attach-load-balancer-target-groups --auto-scaling-group-name "$autoscalingGroupName" --target-group-arns "arn:aws:elasticloadbalancing:ap-northeast-1:**********:targetgroup/***TARGET_GROUP_NAME***/*********" "arn:aws:elasticloadbalancing:ap-northeast-1:**********:targetgroup/***TARGET_GROUP_NAME***/*********" fi fi
- 投稿日:2020-08-10T23:29:07+09:00
Django REST Frameworkでdynamodbを操作するREST APIを作成する
はじめに
django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。Dynamodbテーブル作成(事前準備)
下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Namedjangoプロジェクトの作成
django project(dynamo_operation)とapp(api)を作成
$ django-admin startproject dynamo_operation $ cd dynamo_operation/ $ django-admin startapp apisetting.pyの編集
setting.py
にrest_framework
と先ほど作成したapp
のconfigを追加する。dynamo_operation/setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', #追加 'api.apps.ApiConfig', #追加 ]DynamoDBリクエスト用のmodelを作成
djangoではDBの作成、操作に
model
を用意する。
DynamoDBへのリクエストはboto3を使用するので特にmodel
は必要ないが、今回はmodel(dynamo_model.py)
を用意した。api/dynamo_model.pyclass Fruit(): def __init__(self, name): self.name = nameviews.pyの編集
api/views.pyfrom rest_framework import status from rest_framework.views import APIView from rest_framework.response import Response from api.dynamo_models import Fruit from boto3 import client dynamodb_client = client('dynamodb') class DynamoRequest(APIView): # 全体GET def get(self, request): response = [] items = dynamodb_client.scan(TableName='Fruits')['Items'] for item in items: fruit = Fruit(item['Name']['S']) fruit.price = item.get('Price',{}).get('N', '') response.append(fruit.__dict__) return Response(response) def post(self, request): request_data = request.data item = {'Name': {'S': request_data['Name']}} if 'Price' in request_data: item['Price'] = {'N': request_data['Price']} dynamodb_client.put_item( TableName = 'Fruits', Item = item ) return Response(status=status.HTTP_201_CREATED) class DynamoDetailRequest(APIView): #単体GET def get(self, request, pk): item = dynamodb_client.get_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } )['Item'] fruit = Fruit(item['Name']['S']) fruit.price = item.get('Price',{}).get('N', '') return Response(fruit.__dict__) def put(self, request, pk): request_data = request.data item = dynamodb_client.get_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } )['Item'] price = item.get('Price',{}).get('N', '0') if 'Price' in request_data: price = request_data['Price'] dynamodb_client.put_item( TableName = 'Fruits', Item = { 'Name': {'S': item['Name']['S']}, 'Price': {'N': price} } ) return Response(status=status.HTTP_200_OK) def delete(self, request, pk): dynamodb_client.delete_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } ) return Response(status=status.HTTP_204_NO_CONTENT)
rest_framework
のAPIView
を継承したclass
でリクエストを処理する。
DynamoRequest
がpathパラメータなしのリクエストを処理し、DynamoDetailRequest
でpathパラメータ(pk)ありのリクエストの処理を行う。
APIView
を継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。urls.pyの編集
api/urls.pyfrom django.urls import path from api import views urlpatterns = [ path('api/', views.DynamoRequest.as_view()), path('api/<pk>/', views.DynamoDetailRequest.as_view()) ]
dynamo_opration
フォルダのurls.py
も編集するdynamo_opration/urls.pyfrom django.urls import path, include urlpatterns = [ path('', include('api.urls')), ]curlコマンドで動作確認
serverの起動
$ python manage.py runserver
GET(全体検索)
$ curl http://127.0.0.1:8000/api/ # レスポンス [{"name":"orange","price":"200"},{"name":"banana","price":"100"},{"name":"apple","price":"100"}]POST
$ curl -X POST \ -H 'Content-Type:application/json' \ -d '{"Name": "peach", "Price": "400"}' \ http://127.0.0.1:8000/api/peachの項目が追加されている。
GET(単体)
appleの項目を取得
$ curl http://127.0.0.1:8000/api/apple/ # レスポンス {"name":"apple","price":"100"}PUT
appleのpriceを100 -> 200へ変更する
$ curl -X PUT \ -H 'Content-Type:application/json' \ -d '{"Price": "200"}' \ http://127.0.0.1:8000/api/apple/DELETE
peachの項目を削除する。
$ curl -X DELETE http://127.0.0.1:8000/api/peach/おわりに
django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。
- 投稿日:2020-08-10T23:29:07+09:00
[DRF+boto3]Djangoでdynamodbを操作するAPIを作成する
はじめに
django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。Dynamodbテーブル作成(事前準備)
下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Namedjangoプロジェクトの作成
django project(dynamo_operation)とapp(api)を作成
$ django-admin startproject dynamo_operation $ cd dynamo_operation/ $ django-admin startapp apisetting.pyの編集
setting.py
にrest_framework
と先ほど作成したapp
のconfigを追加する。dynamo_operation/setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', #追加 'api.apps.ApiConfig', #追加 ]DynamoDBリクエスト用のmodelを作成
djangoではDBの作成、操作に
model
を用意する。
DynamoDBへのリクエストはboto3を使用するので特にmodel
は必要ないが、今回はmodel(dynamo_model.py)
を用意した。api/dynamo_model.pyclass Fruit(): def __init__(self, name): self.name = nameviews.pyの編集
api/views.pyfrom rest_framework import status from rest_framework.views import APIView from rest_framework.response import Response from api.dynamo_models import Fruit from boto3 import client dynamodb_client = client('dynamodb') class DynamoRequest(APIView): # 全体GET def get(self, request): response = [] items = dynamodb_client.scan(TableName='Fruits')['Items'] for item in items: fruit = Fruit(item['Name']['S']) fruit.price = item.get('Price',{}).get('N', '') response.append(fruit.__dict__) return Response(response) def post(self, request): request_data = request.data item = {'Name': {'S': request_data['Name']}} if 'Price' in request_data: item['Price'] = {'N': request_data['Price']} dynamodb_client.put_item( TableName = 'Fruits', Item = item ) return Response(status=status.HTTP_201_CREATED) class DynamoDetailRequest(APIView): #単体GET def get(self, request, pk): item = dynamodb_client.get_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } )['Item'] fruit = Fruit(item['Name']['S']) fruit.price = item.get('Price',{}).get('N', '') return Response(fruit.__dict__) def put(self, request, pk): request_data = request.data item = dynamodb_client.get_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } )['Item'] price = item.get('Price',{}).get('N', '0') if 'Price' in request_data: price = request_data['Price'] dynamodb_client.put_item( TableName = 'Fruits', Item = { 'Name': {'S': item['Name']['S']}, 'Price': {'N': price} } ) return Response(status=status.HTTP_200_OK) def delete(self, request, pk): dynamodb_client.delete_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } ) return Response(status=status.HTTP_204_NO_CONTENT)
rest_framework
のAPIView
を継承したclass
でリクエストを処理する。
DynamoRequest
がpathパラメータなしのリクエストを処理し、DynamoDetailRequest
でpathパラメータ(pk)ありのリクエストの処理を行う。
APIView
を継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。urls.pyの編集
api/urls.pyfrom django.urls import path from api import views urlpatterns = [ path('api/', views.DynamoRequest.as_view()), path('api/<pk>/', views.DynamoDetailRequest.as_view()) ]
dynamo_opration
フォルダのurls.py
も編集するdynamo_opration/urls.pyfrom django.urls import path, include urlpatterns = [ path('', include('api.urls')), ]curlコマンドで動作確認
serverの起動
$ python manage.py runserver
GET(全体検索)
$ curl http://127.0.0.1:8000/api/ # レスポンス [{"name":"orange","price":"200"},{"name":"banana","price":"100"},{"name":"apple","price":"100"}]POST
$ curl -X POST \ -H 'Content-Type:application/json' \ -d '{"Name": "peach", "Price": "400"}' \ http://127.0.0.1:8000/api/peachの項目が追加されている。
GET(単体)
appleの項目を取得
$ curl http://127.0.0.1:8000/api/apple/ # レスポンス {"name":"apple","price":"100"}PUT
appleのpriceを100 -> 200へ変更する
$ curl -X PUT \ -H 'Content-Type:application/json' \ -d '{"Price": "200"}' \ http://127.0.0.1:8000/api/apple/DELETE
peachの項目を削除する。
$ curl -X DELETE http://127.0.0.1:8000/api/peach/おわりに
django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。
- 投稿日:2020-08-10T23:29:07+09:00
Django REST Frameworkでdynamodbを操作するREST APIを作成する[boto3]
はじめに
django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。Dynamodbテーブル作成(事前準備)
下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Namedjangoプロジェクトの作成
django project(dynamo_operation)とapp(api)を作成
$ django-admin startproject dynamo_operation $ cd dynamo_operation/ $ django-admin startapp apisetting.pyの編集
setting.py
にrest_framework
と先ほど作成したapp
のconfigを追加する。dynamo_operation/setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', #追加 'api.apps.ApiConfig', #追加 ]DynamoDBリクエスト用のmodelを作成
djangoではDBの作成、操作に
model
を用意する。
DynamoDBへのリクエストはboto3を使用するので特にmodel
は必要ないが、今回はmodel(dynamo_model.py)
を用意した。api/dynamo_model.pyclass Fruit(): def __init__(self, name): self.name = nameviews.pyの編集
api/views.pyfrom rest_framework import status from rest_framework.views import APIView from rest_framework.response import Response from api.dynamo_models import Fruit from boto3 import client dynamodb_client = client('dynamodb') class DynamoRequest(APIView): # 全体GET def get(self, request): response = [] items = dynamodb_client.scan(TableName='Fruits')['Items'] for item in items: fruit = Fruit(item['Name']['S']) fruit.price = item.get('Price',{}).get('N', '') response.append(fruit.__dict__) return Response(response) def post(self, request): request_data = request.data item = {'Name': {'S': request_data['Name']}} if 'Price' in request_data: item['Price'] = {'N': request_data['Price']} dynamodb_client.put_item( TableName = 'Fruits', Item = item ) return Response(status=status.HTTP_201_CREATED) class DynamoDetailRequest(APIView): #単体GET def get(self, request, pk): item = dynamodb_client.get_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } )['Item'] fruit = Fruit(item['Name']['S']) fruit.price = item.get('Price',{}).get('N', '') return Response(fruit.__dict__) def put(self, request, pk): request_data = request.data item = dynamodb_client.get_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } )['Item'] price = item.get('Price',{}).get('N', '0') if 'Price' in request_data: price = request_data['Price'] dynamodb_client.put_item( TableName = 'Fruits', Item = { 'Name': {'S': item['Name']['S']}, 'Price': {'N': price} } ) return Response(status=status.HTTP_200_OK) def delete(self, request, pk): dynamodb_client.delete_item( TableName = 'Fruits', Key = { 'Name': {'S': pk}, } ) return Response(status=status.HTTP_204_NO_CONTENT)
rest_framework
のAPIView
を継承したclass
でリクエストを処理する。
DynamoRequest
がpathパラメータなしのリクエストを処理し、DynamoDetailRequest
でpathパラメータ(pk)ありのリクエストの処理を行う。
APIView
を継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。urls.pyの編集
api/urls.pyfrom django.urls import path from api import views urlpatterns = [ path('api/', views.DynamoRequest.as_view()), path('api/<pk>/', views.DynamoDetailRequest.as_view()) ]
dynamo_opration
フォルダのurls.py
も編集するdynamo_opration/urls.pyfrom django.urls import path, include urlpatterns = [ path('', include('api.urls')), ]curlコマンドで動作確認
serverの起動
$ python manage.py runserver
GET(全体検索)
$ curl http://127.0.0.1:8000/api/ # レスポンス [{"name":"orange","price":"200"},{"name":"banana","price":"100"},{"name":"apple","price":"100"}]POST
$ curl -X POST \ -H 'Content-Type:application/json' \ -d '{"Name": "peach", "Price": "400"}' \ http://127.0.0.1:8000/api/peachの項目が追加されている。
GET(単体)
appleの項目を取得
$ curl http://127.0.0.1:8000/api/apple/ # レスポンス {"name":"apple","price":"100"}PUT
appleのpriceを100 -> 200へ変更する
$ curl -X PUT \ -H 'Content-Type:application/json' \ -d '{"Price": "200"}' \ http://127.0.0.1:8000/api/apple/DELETE
peachの項目を削除する。
$ curl -X DELETE http://127.0.0.1:8000/api/peach/おわりに
django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。
- 投稿日:2020-08-10T23:05:15+09:00
AWS IoT CoreのShadowをPHPのSDKを用いて取得
AWS IoT CoreのShadowはIotDataPlaneClientのGetThingShadow()で取得できます。
公式サイトには以下サンプルが記載されています。
https://docs.aws.amazon.com/ja_jp/aws-sdk-php/v3/api/api-data.iot-2015-05-28.html#getthingshadow$result = $client->getThingShadow([ 'shadowName' => '<string>', 'thingName' => '<string>', // REQUIRED ]);しかし、$clientのオブジェクトの生成方法が書いていない。。。
$clientのオブジェクト生成方法は以下です。
$client = new IotDataPlaneClient( array( 'version' => 'latest', 'region' => 'ap-northeast-1', 'endpoint' => 'https://xxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com' ) );以上
- 投稿日:2020-08-10T22:59:57+09:00
S3で色々お試しして遊んでみる(VPCエンドポイント&静的Webホスティング性能編)
はじめに
S3。単なるマネージドでサーバレスなバックエンドストレージなだけでなく(いや、それだけでも充分すごいんだけど)、暗号化できたり、セキュアアクセスができたり、ファイルの中身にクエリ発行できたり、ウェブサイトのホスティングができたり、イベントトリガになったり、なんだか色々できてすごいぞ!
これを使い倒さずに、毎回「とりあえずバケット作っておくか」な使い方をしているだけではもったいない!
ということで今回は、色々ある機能の内、VPCエンドポイントと静的Webホスティングの機能について触れてみる(いずれもさわりだけなので、たいした検証にはなっていないのだが……)。
VPCエンドポイント
VPCエンドポイントの詳細は公式を見てもらうと良い。
要するに、S3のAPIはインターネットに口を向けているので、何も考えずにVPCからaws cliを実行したりすると、インターネットを通ってしまう。内部通信をしたいのに!というときに利用するものである。VPCエンドポイントは色々なAWSサービスに対応しているが、S3とDynamoDBは追加料金不要(ほかのサービスはPrivateLinkを利用するためそこそこのお金がかかる)!これは是非使っておこう。Terraformでの設定
マネージメントコンソール画面ではなくていきなりIaCのことを言い出すのは結構アレではあるが、実際やってみると、画面で設定するのも大して変わらないので、さっさと自動化出来てしまった方が手っ取り早い。
予めVPCとルートテーブルは作成済みで、データリソースで参照できるようにしていることが前提だ。
resource "aws_vpc_endpoint" "s3" { vpc_id = data.aws_vpc.my.id service_name = "com.amazonaws.ap-northeast-1.s3" } resource "aws_vpc_endpoint_route_table_association" "s3" { route_table_id = data.aws_route_table.my.id vpc_endpoint_id = aws_vpc_endpoint.s3.id }これだけでOK!簡単!
性能差を確認する
さて、インターネット通信を内部に閉じるのだから、おそらくレイテンシも解消されるのであろう。
一石二鳥だ!と思い、上記のVPCエンドポイント作成後にVPC内のLambdaからboto3でのアクセスとインターネット経由でのアクセスを両方試してみた。boto3でのアクセスは、VPCエンドポイントを作る前はエラーになっていたので、おそらくVPCエンドポイントを通っているのだろう。
余談ではあるが、VPC内のLambdaは起動時にENIを作成するため、以下のページにあるようにIAMポリシにパーミッションを追加する必要があるので要注意だ。
【AWS公式】VPC 内のリソースにアクセスするための Lambda 関数の設定
さて、これでVPCから起動したLambdaで測定ができるぞ。
以下は、VPCエンドポイントへのアクセス後、静的WebホスティングしたS3へアクセスするという順序で20回計測した結果の平均だ。
VPCエンドポイント 静的Webサイトホスティング 329.4msec 63.8msec ……あれ?なんかインターネットを経由する静的Webサイトホスティングの方が早いぞ?
もしかして、順序がいけないのだろうか。2回目のアクセスはどこかのキャッシュが機能してしまって早いとかか(そんな話は聞いたことが無いが)?ということで、VPCエンドポイントのアクセス2回、静的WebホスティングしたS3へアクセス2回を1回のLambda関数内で実行して、それぞれの2回目のアクセスを20回計測して平均を取った。
VPCエンドポイント 静的Webサイトホスティング 154.9msec 45.0msec うーむ、どちらも2回目の方が早くなった。謎……。
一石二鳥にはならないようだ。静的Webサイトホスティング
静的Webサイトホスティングは、名前の通り、S3に静的コンテンツを突っ込んで置いたらWebサーバを立てなくてもHTTPリクエストに応答してコンテンツを返してくれる機能だ。ちょっとしたコンテンツならこれでサーバレスに賄えてしまう。便利!
ここではサクッとホスティング機能を設定することだけ書いているが、商用で運用する場合はアクセスログの記録やアクセス制御をちゃんとやろう。
マネージメントコンソールでの設定
マネコン画面では以下の画面から設定が可能。簡単!
Terraformでの設定
以下のように
aws_s3
のリソースを設定すれば良い。簡単!resource "aws_s3_bucket" "my" { bucket = local.bucket_name acl = "public-read" website { index_document = "index.html" error_document = "error.html" } }静的Webサイトホスティングの素性能
さて、では静的Webサイトホスティングはどこまで自動でスケールしてくれるのだろうか?
ひとまず、Locustを使って計測してみる。ちなみに、Locustはt3.mediamなEC2上でMasterのみで動かしているので、チューニングが全然できていない。
もしかしたらこれはLocustネックになっている可能性があるので、悪しからず。
別途チューニングして測定をしたら再度結果を載せようと思う。ご覧の通り、70~80rpsあたりでようやく安定するといったところだ。
※それでもまだ少しだけエラーが出る。Locust側のチューニング結果次第だが、CloudFrontでキャッシュしてあげるべきなのだろうか……。
- 投稿日:2020-08-10T22:11:50+09:00
AWSでZabbixを作成する際の注意点
はじめに
AWSのEC2とRDSを利用してZabbix4.2を導入するを参照しながら作成したが、当方が初心者のため一部詳細が書かれていない箇所でどうやったらいいのか戸惑ったり、Macを使っているので一部作業内容が違った。
そのため本項でその点を補足しつつ、説明していきたい。
(すでに作成したため、初めて作成する場合と画像が一部異なる可能性がございますが、ご了承ください。)手順
AWS側の設定
セキュリティグループの作成
VPC>セキュリティグループ>セキュリティグループを作成をクリックする。
セキュリティグループ名、説明を入力する。今回は「security-sg」とする。
VPCは今回デフォルトのものとする。
インバウンドルールやアウトバウンドルールはそのままで「セキュリティグループを作成」を押す。
セキュリティグループで先ほど作成したものをチェックを入れて、下のインバウンドルールをクリック。画像のようなインバウンドルールになるように「インバウンドルールを編集をクリック」する。(あらかじめ作成しているので、すでに入っている状態ですが、本来はこの時点ではインバウンドルールは空です。)
インバウンドルールを編集で、以下のように編集していく。
タイプ ポート範囲 ソース 説明 HTTP 80 マイIP http SSH 22 マイIP ssh カスタムTCP 10051 先ほど作ったセキュリティグループ Zabbix agent-Zabbix server MYSQL/Aurora 3306 先ほど作ったセキュリティグループ MySQL カスタムTCP 10050 先ほど作ったセキュリティグループ Zabbix server-Zabbix agent HTTPS 443 マイIP https 追加できたら「ルールの保存」を押す。
(セキュリティグループを作成した直後だと保存できないことがあるため、うまくいかなかったら少し時間をおいてやると保存できる。)EC2の作成
EC2>「インスタンスの作成」をクリック。
「Amazon Linux 2 AMI (HVM), SSD Volume Type」を選択する。
インスタンスタイプを選択。特にこだわりなければ、無料利用枠の対象のものを選ぶ。「次のステップ:インスタンスの詳細の設定」を押す。
インスタンス数は2、ネットワークはセキュリティルールで設定したVPCを選択。
(今回はデフォルトとする。)「次のステップ:ストレージの追加」ボタンを押す。
特にこだわりがなければデフォルトのままで、「次のステップ:タグの追加」をクリックする。
タグの追加の必要がなければ、「次の手順:セキュリティグループの設定」を押す。
「既存のセキュリティグループを選択する」で先ほど作成したセキュリティグループを選択し、「確認と作成」を押す。
インスタンス作成の確認で内容を確認し、「起動」を押す。
既存のキーペアを選択するか、新しいキーペアを作成する。完了したらインスタンスの作成を押す。
EC2のインスタンスの画面に戻り、作成できたか確認する。
RDSの作成
RDS>データベースの作成を押す。
「データベース作成方法を選択」>「標準作成」を押す。
「エンジンのオプション」から下記の項目を選択する。
エンジンのタイプ : MySQL
バージョン: MySQL 5.7.26
「テンプレート」>「無料利用枠」を押す。
「設定」から下記の項目を入力する。DBインスタンス識別子 : (AWSコンソール上で表示される名前)
マスターユーザー名 : (MySQLのログインユーザー名)
マスターパスワード : (MySQLのパスワード)
DB インスタンスサイズはデフォルトの「db.t2.micro」を選択する。(無料利用枠だとdb.t2.microしか選択できないようだ。)
「ストレージ」から下記のように設定する。
・ストレージタイプ:「汎用(SSD)」
・ストレージ割り当て:20
・ストレージの自動スケーリングを有効にする:チェックを外す
「接続」でセキュリティグループで設定したVPCを選択する。(今回はデフォルトを選択する。)
内容を確認し、「データベースの作成」を押す。
Zabbix Server側の設定
今回はMacのterminalでインスタンス接続を行う。
EC2を作成した際に作った鍵の置き場所とEC2のインスタンスの説明でIPv4 パブリックIPを確認して、ssh接続を行う。
ssh -i 鍵の置き場所 ec2-user@IPv4 パブリックIPZabbixのインストール
yumのパッケージをアップデートする。
$ sudo yum -y update永続的にSELinuxを無効化する。
$ sudo vi /etc/selinux/config SELINUX=disabled $ sudo reboot (getenforceコマンドでdisabledになったか確認できる。)Zabbix4.2のリポジトリをインストールする。
$ sudo rpm -Uvh https://repo.zabbix.com/zabbix/4.2/rhel/7/x86_64/zabbix-release-4.2-2.el7.noarch.rpmzabbix-server-mysql,zabbix-web-mysql,zabbix-web-japanese,zabbix-agentをインストールする。
$ sudo yum -y install zabbix-server-mysql zabbix-web-mysql zabbix-web-japanese zabbix-agentMySQLの設定
MySQLクライアントをインストールする。
$ sudo yum -y install mysql mysql-develRDSに接続する。
$ sudo mysql -h RDSのエンドポイント -P 3306 -u マスターユーザ名 -pZabbixのデータベースを作成する。
> create database zabbix character set utf8 collate utf8_bin;Zabbixユーザを作成する。
> grant all on zabbix.* to zabbix@`%` identified by 'パスワード';MySQLからログアウトする。
> quit;Zabbixの初期データを登録する。
$ sudo zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -h RDSのエンドポイント -P 3306 -u マスターユーザ名 -p zabbix/etc/zabbix/zabbix_server.confにDB情報を登録する。
$ sudo vi /etc/zabbix/zabbix_server.conf DBHost=RDSのエンドポイント DBPassword=RDSのパスワードzabbix-serverの起動・自動起動を設定する。
$ systemctl start httpd $ systemctl enable httpd $ systemctl start zabbix-server $ systemctl enable zabbix-serverZabbixの設定
ブラウザにて「http://(インスタンスのIPv4 パブリックIP)/zabbix」に接続する。
「Welcome to Zabbix 4.2」表示されたら、「Next step」を押す。
以下の画面ですべてOKになってるか、確認する。NGになっている箇所があった場合は表示されるメッセージに従い修正。
私の場合はdata.timezoneでNGになっていたので、zabbix インストール時のTimezone for PHP is not set. Please set "date.timezone" option in php.ini.の記事を参考にして修正した。
全てOKになったら、「Next Step」を押す。
「Configure DB connection」にて下記の項目を入力し「Next step」を押す。
Database host : (RDSのエンドポイント)
Password : (zabbixユーザのパスワード)
「Zabbix server details」にてデフォルトのままで「Next step」をクリックする。
「Pre-installation summary」にて正しい設定値であることを確認し、「Next step」を押す。
「Install」にて下記のように表示されたら無事インストールできたので、「Finish」を押す。
usernameとpasswordを入力し、ログインする。初期状態で設定されているユーザーは、usernameが「Admin」、Passwordが「zabbix」。
ダッシュボードが表示される。
「Zabbix agent on Zabbix server is unreachable for 5 minutes」の警告を消すためにzabbix-agentを起動する。
$ sudo systemctl start zabbix-agent $ sudo systemctl enable zabbix-agentZabbixの日本語化設定
右上のユーザーのマークをクリックし、「ユーザープロファイル: Zabbix Administrator」で言語を日本語に変更。「更新」ボタンを押す。
Zabbix Agent側の設定
Zabbix Server側の設定方法を参考にして、2台目のEC2インスタンスにTerminalでログインする。
アップデート可能な全パッケージをアップデートする。
$ sudo yum -y update永続的にSELinuxを無効化する。(ちなみに私の場合はzabbix-erverで無効化したら、後はdisabledになっていて特に変更する必要がなかった。)
$ sudo vi /etc/selinux/config SELINUX=disabled $ sudo reboot (getenforceコマンドでdisabledになったか確認できる。)Zabbix4.2のリポジトリをインストールする。
$ sudo rpm -Uvh https://repo.zabbix.com/zabbix/4.2/rhel/7/x86_64/zabbix-release-4.2-2.el7.noarch.rpmzabbix-agentをインストールする。
$ sudo yum install -y zabbix-agent/etc/zabbix/zabbix_agentd.confにZabbix Server側のIPアドレスを入力する。
$ sudo vi /etc/zabbix/zabbix_agentd.conf Server=(Zabbix Server側のIPアドレス) ServerActive=(Zabbix Server側のIPアドレス)zabbix-agentの起動・自動起動を設定する。
$ sudo systemctl start zabbix-agent $ sudo systemctl enable zabbix-agentZabbix(GUI)でZabbix Agentの登録
下記の項目を入力し「テンプレート」を押す。
ホスト名 : (分かりやすい名前を入力)
表示名 : (分かりやすい名前を入力)
グループ : Linux servers
IPアドレス : (Zabbix Agent側のIPアドレスを入力)
「新規テンプレートをリンク」で「Template OS Linux」を検索し、2つある追加ボタンのうちの上のボタンをクリック。「テンプレートのリンク」に追加されたら、下の「追加」ボタンを押す。
「ホストを追加しました」と表示されたら、監視データ>グラフを押す。
右上の「グラフ」で「CPU jumps」を選択すると、グラフが表示される。
注意点のまとめ
・最初にセキュリティグループの基本的な詳細のみ入力して作成し、その後インバウンドルールを追加する。(作成したセキュリティグループを使用できるので、混乱が少なかった。)
・MacとWindowsでssh接続の仕方が違う。
・data.timezoneでNGが出るので、適宜修正する。
・グラフの種類を指定しないと、表示されないので注意。
- 投稿日:2020-08-10T18:13:23+09:00
SAMLによって、GoogleアカウントでAWSマネジメントコンソールにSSOログインする
前置き
AWSには、IAM Userというユーザー管理機能があり、AWSアカウント内でユーザー管理することも可能です。
しかし、G Suiteを使っている環境において、メンバーが増えるたびに、Googleアカウントを発行したり、AWS IAMユーザーを発行したりするには、工数が発生するとともに、漏洩のリスクも増やしてしまいます。
そこで、SSOにより、GoogleアカウントにてAWSアカウントにログインできるようにしてみます。AWSおよびGoogle(G Suite)には、これをSAML経由で簡単に実現できるようにしてくれています。
ここでの詳細な説明は割愛しますが、SAML環境は、IdP(認証をつかさどる)とSP(サービス提供)によって構成されます。
ここでは、G SuiteがIdP、AWSがSPとなります。
構成図としては、AWSドキュメントのSSO関連のいたるところでみる、以下がわかりやすいです。
なお、図では「AWS SSO Endpoint」と表記がありますが、今回の手順では、「AWS SSO」は利用しません。
より詳細なコントロールが必要な際などに、「AWS SSO」を利用することになります(最後のまとめにも記載)。前置き2
だいたい、このサイトの手順どおりで実現できます。しかし、いくつか現在(2020/8/10)の環境では差異があるため、手順どおりに進めていく上での留意点を補足する形で、以降の手順を記載します。
https://support.google.com/a/answer/6194963?hl=enStepごとの補足ポイント
Before you begin
以下の手順において、表記されているようなリンクは見つけられませんでした。
しかしながら、ユーザーリスト右上の「More」に、「Manage Custom Attribute」という、両者を混ぜ合わせたようなリンクから、手順を実行できました。3 At the top of Users list, click Manage user attributes User attributes.
4 At the top right, click Add Custom Attribute.ちょいちょいこの手の差異はあったような気もしますが、Googleに限らず、Web UI上の表記が微妙にアップデートされ、公式の手順書と差異があることはままあります。
Step 4: Enable the Amazon Web Services app
正直、英語で読んでも日本語で読んでも、ここで書いている文が言語として難解でした :(
また、これだけではStep 5の検証をとおすのに、不十分に思えます。
検証するユーザー(Chromeを使っているならログイン中のユーザーアカウント)にて、ロールを設定しておかねばなりません(Step 6相当の準備)。Step 2でAWS側に準備しておいたロール群(※)の出番です。
(※) 以下の2つの形式のもの。
- arn:aws:iam::ACCOUNT_NUMBER:role/SSO
- arn:aws:iam::ACCOUNT_NUMBER:saml-provider/GoogleAppsG Suiteのユーザー一覧から、検証したいユーザーのリンクを開きます。
「User Information」を開くと、「Amazon」という項目が見つけられると思います。また、この時点では、Roleは未設定の状態になっているかと思います。
このロールを編集し、Step 2で作成したロールの2つのARNを、カンマ区切りで設定します。これで、Step 5での検証や、Google Appsから、AWSマネジメントコンソールにログインすることが可能になったかと思われます :)
まとめ
個人的には、ユーザーへのロールの設定が罠になるかと思いますが、それ以外については手順は丁寧で、SAMLやSSOについて、あまり理解していなくても表題の実現が可能かと思いますが、SSOの仕組みを自分で手を動かして動かすことで、理解が深まることもあるかと思います。
今回実施した手段は、AWSアカウントとGoogleアカウントそれぞれで設定しました。
複数のAWSアカウントを扱ったりしていったり、ユーザー数が増えて行ったりと、ビジネスが成長していく上でこの手順では厳しくなってきます。
AWS SSOなどを利用した仕組みを構築する必要があります。また、そのあたりの記事も書けたらと思います。
- 投稿日:2020-08-10T17:21:57+09:00
【AWS】S3で静的ホスティングしてRoute53でルーティングする
はじめに
最近、Route53でドメインを作成してみたので、試しに、S3の静的ホスティングを試してみようと思いました。
方法
S3のバケットを静的ホスティング設定する
S3バケットの作成
まず、S3のバケットを作成します。
※バケット名は、ドメイン名と同じ名前でないと正しくルーティングされません。
作成時にパブリックアクセスをブロックするか聞かれますが、チェックを外して、ブロックしないように設定してください。
静的ホスティングの設定
作成が完了したら、当該バケットのプロパティページを開いて、
Static website hosting
を選択します。
このバケットを使用してウェブサイトをホスティングする
を選択します。
そして、今回は「インデックスドキュメント」のみ設定します。
index.html
と入力して、保存
ボタンをクリックします。バケットポリシーの作成
静的配信を行うために、設定を行う必要があります。
バケットのアクセス権限のタブを開いて下記の設定を行います。
設定するバケットポリシー{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::ここにバケット名を記載/*" ] } ] }リソースの
"arn:aws:s3:::ここにバケット名を記載/*"
の部分は自分のバケット名に変更してください。index.htmlの作成
アクセスされた際に表示される、「index.html」ファイルを作成します。
index.html<!DOCTYPE html> <html lang="ja"> <meta charset="utf-8"> <head> <title>ウェブサイトのホームページ</title> </head> <body> <h1>ようこそ!</h1> <p>S3で静的ホスティング!</p> </body> </html>オブジェクトをアップしたら、正しく静的配信されているか確認してみましょう。
下記のように表示されていた場合は、正しく設定出来ています!Route53でルーティングを設定
AWSマネジメントコンソールでRoute53のページを開き、左のメニューからホストゾーンを選択します。
ルーティングを行いたいドメイン名を選択します。※ドメインを取得していない、場合は、取得してください。
レコードの作成
レコードの作成
をクリックします。今回は、
シンプルルーティング
を選択します。
その後、シンプルなレコードを定義
をクリックします。今回は、サブドメインを設定するわけでは無いので、
レコード名
は空白にします。
値/トラフィックのルーティング先
は「S3ウェブサイトエンドポイントへのエイリアス」を選択します。
その次に、リージョンはS3のバケットを作成したリージョンを選択します。
選択できるバケットが存在する場合は、プルダウンに表示されるので、選択します。
レコードタイプ
は「A - IPv4アドレスと一部のAWSリソースにトラフィックをルーティングします。」を選択します。
今回、ターゲットのヘルスを評価
は不要なので、チェックを外します。レコードの情報を入力する事ができれば、右下の
レコードを作成
をクリックします。最後に
設定が完了したら、先ほど登録したレコード名にアクセスしてみましょう!
S3のindex.htmlの内容が表示されると思います!
これで、S3で簡単にウェブページをホスティングする事ができました!
- 投稿日:2020-08-10T14:55:37+09:00
AWS Amplify & Nuxt 使って爆速でWebアプリ作る
AWS でWebアプリ作る機会があったので、色々調べるとAmplifyが便利そうだったので使ってみました。
今回作成するアプリの主な機能は以下:
- NuxtベースのTODOアプリ
- Lambda使ったGraphQLのAPI
- DynamoDBへのデータ保存/読込
- DynamoDBのデータとリアルタイム同期
- Cognitoによる認証機能
基本的には公式チュートリアルをなぞってますが、Nuxtで使うに当たりいくつか変更点があるので、Nuxt使いに役立てば幸いです。
Amplifyの初期設定
まずAmplifyのCLIをインストールしましょう。
$ npm install -g @aws-amplify/cliインストールしたら
amplify
コマンドが使えるようになってるはずです。早速configure
で設定をしてみます。$ amplify configure Scanning for plugins... Plugin scan successful Follow these steps to set up access to your AWS account: Sign in to your AWS administrator account: https://console.aws.amazon.com/ Press Enter to continue上記のようなログが流れて、AWSへのサインインが求められるので、サインインしてください。その次にまたコンソール戻りましょう。
戻ったら、regionの設定です。東京なら
ap-northeast-X
を選べば良いはず。user nameはデフォルトでもいいですが、わかりやすくamplify-admin
としてみました。Specify the AWS Region ? region: ap-northeast-1 Specify the username of the new IAM user: ? user name: amplify-admin Complete the user creation using the AWS console https://console.aws.amazon.com/iam/home?region=undefined#/users$new?step=final&accessKey&userNames=amplify-admin&permissionType=policies&policies=arn:aws:iam::aws:policy%2FAdministratorAccess Press Enter to continueここでAWS Consoleがブラウザで立ち上がり、ユーザー追加が求められます。基本的に全部デフォルトでOKです。
このままで次の画面進みます。
これもこのままで次に進みます。
最終的に以下の画面が表示されます。このページはまだ閉じないでください。
コンソール画面に戻ります。
戻ったら、成功画面に表示されてる値を元にキーを入力してってください。キーは流出しないように注意して管理すること。Profile NameはなんでもOKです。今回は
default
にしました。Enter the access key of the newly created user: ? accessKeyId: # 成功画面に表示されてるアクセスキーID ? secretAccessKey: # 成功画面に表示されてるシークレットアクセスキー This would update/create the AWS Profile in your local machine ? Profile Name: default Successfully set up the new user.Nuxtアプリを用意する
Nuxtを
create-nuxt-app
使ってベースとなるアプリを用意します。アプリを作りたいディレクトリで以下のコマンド実行します。各オプションはお好みで。$ npx create-nuxt-app nuxt-amplify create-nuxt-app v3.2.0 ✨ Generating Nuxt.js project in nuxt-amplify ? Project name: nuxt-amplify ? Programming language: JavaScript ? Package manager: Npm ? UI framework: Vuetify.js ? Nuxt.js modules: Axios ? Linting tools: ESLint, Prettier ? Testing framework: Jest ? Rendering mode: Single Page App ? Deployment target: Static (Static/JAMStack hosting) ? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)完了したら起動して動作するか試してみましょう。
$ npm run dev (省略) i Waiting for file changes i Memory usage: 415 MB (RSS: 516 MB) i Listening on: http://localhost:3000/http://localhost:3000/ にアクセスして下の画面が見えたらOKです。
ついでに、次以降の作業のために
generate
コマンドを使ってファイルを生成しておきます。$ npm run generate
distディレクトリが作られて中身が入ってればOKです。
Amplifyのバックエンドを作成する
Nuxtプロジェクトのルートディレクトリで以下のコマンドを打ち、設定を進めます。
$ amplify init Note: It is recommended to run this command from the root of your app directory ? Enter a name for the project nuxtamplify ? Enter a name for the environment dev ? Choose your default editor: Visual Studio Code ? Choose the type of app that you're building javascript Please tell us about your project ? What javascript framework are you using vue ? Source Directory Path: . ? Distribution Directory Path: dist ? Build Command: npm.cmd run-script generate ? Start Command: npm.cmd run-script startそうすると、以下の内容が更新されます。
/amplify
ディレクトリが作成される。このディレクトリにはバックエンドの定義をコードベースで実装していき、Infrastructure as Codeを実現できる。aws-exports.js
が作成されてバックエンドの情報が保存される。.gitignore
が更新されていくつかのファイルが指定される。- AWS Console上にプロジェクトが作られる。 ???
Amplifyのフロント用ライブラリをインストールする
続いて、Nuxt側から使うフロント用のAmplifyライブラリをnpmからインストールします。
$ npm install aws-amplify @aws-amplify/ui-vueそしてNuxt側で
/plugins
ディレクトリにamplify.js
ファイルを作成し、プラグインとしてインストールしたライブラリを読み込みます。amplify.jsimport Vue from 'vue' import Amplify from 'aws-amplify' import '@aws-amplify/ui-vue' import awsExports from '../aws-exports' Amplify.configure(awsExports) Vue.use(Amplify)作成したプラグインを読み込むよう
nuxt.config.js
のplugins
配列で指定します。nuxt.config.js// ... plugins: [{ src: '~/plugins/amplify.js', ssr: false }], // ...この状態で
npm run dev
してビルドに失敗しなければ問題ないはずです。GraphQL APIを作りデプロイする
続いて、API作っていきます。AmplifyではREST APIとGraphQL APIのどちらかが作れます。今回はGraphQLを使ってみましょう。
$ amplify add api ? Please select from one of the below mentioned services: GraphQL ? Provide API name: todoapi ? Choose the default authorization type for the API API key ? Enter a description for the API key: todo ? After how many days from now the API key should expire (1-365): 7 ? Do you want to configure advanced settings for the GraphQL API No, I am done. ? Do you have an annotated GraphQL schema? No ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) ? Do you want to edit the schema now? No以上で、Graph QLのスキーマも自動で生成されます。ファイル内容を見てみましょう。
/amplify/backend/api/todoapi/schema.graphqltype Todo @model { id: ID! name: String! description: String }
id
とname
とdescription
というデータが格納できるスキーマのモデルが作られてます。@model
と指定されていますがこれはGraph QL transformライブラリが用意するディレクティブです。これがあることにより、テーブルとCRUD機能が用意されます。つまり、APIの実装自体はもうほぼ完成だったりします。なので、GraphQL APIをデプロイしましょう。
$ amplify push Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------- | --------- | ----------------- | | Api | todoapi | Create | awscloudformation | ? Are you sure you want to continue? Yes ? Do you want to generate code for your newly created GraphQL API Yes ? Choose the code generation language target javascript ? Enter the file name pattern of graphql queries, mutations and subscriptions src\graphql\**\*.js ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] 2これでAPIが作成されました。作成されたかを確認してみましょう。
$ amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------- | --------- | ----------------- | | Api | todoapi | No Change | awscloudformation | GraphQL endpoint: https://######################.appsync-api.ap-northeast-1.amazonaws.com/graphql GraphQL API KEY: ######################そしてAWS Console上からも確認してみましょう。
$ amplify console
これで以下画面が開きます。何も表示されなかったらリージョンがオハイオとかになってる可能性あるので、右上のメニューから東京にしてください。
これクリックしてbackendを選択して以下の画面で
View in AppSync
を選択します。フロントエンドとAPIを接続する
フロントのNuxtアプリからAPIを叩くようにページを用意しましょう。
/pages/index.vue<template> <div> <h1>TodoApp</h1> <v-text-field v-model="name" label="Name"></v-text-field> <v-text-field v-model="description" label="Description"></v-text-field> <v-btn @click="createTodo">Create</v-btn> <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.name }} : {{ todo.description }} </li> </ul> </div> </template> <script> import { API } from 'aws-amplify' import { createTodo } from '~/src/graphql/mutations' import { listTodos } from '~/src/graphql/queries' export default { data() { return { name: '', description: '', todos: [], } }, async created() { await this.getTodos() }, methods: { async createTodo() { const { name, description } = this if (!name || !description) return false const todo = { name, description } await API.graphql({ query: createTodo, variables: { input: todo }, }) this.name = '' this.description = '' this.getTodos() }, async getTodos() { const todos = await API.graphql({ query: listTodos, }) this.todos = todos.data.listTodos.items }, }, } </script>このコードだと、初期のTODO取得と新規追加ができます。ですが、AmplifyのGraphQLはsubscriptionという機能を使ってリアルタイムでのデータ更新を実現できます。これはAPI作成時に自動で生成されているので、特に設定は不要で読み込む設定させすればできちゃいます。リアルタイムで同期するので、
createTodo
の末尾にあるthis.getTodos()
は要らなくなります。/pages/index.vue<template> <div> <h1>TodoApp</h1> <v-text-field v-model="name" label="Name"></v-text-field> <v-text-field v-model="description" label="Description"></v-text-field> <v-btn @click="createTodo">Create</v-btn> <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.name }} : {{ todo.description }} </li> </ul> </div> </template> <script> import { API } from 'aws-amplify' import { createTodo } from '~/src/graphql/mutations' import { listTodos } from '~/src/graphql/queries' import { onCreateTodo } from '~/src/graphql/subscriptions' export default { data() { return { name: '', description: '', todos: [], } }, created() { this.getTodos() this.subscribe() }, methods: { async createTodo() { const { name, description } = this if (!name || !description) return false const todo = { name, description } await API.graphql({ query: createTodo, variables: { input: todo }, }) this.name = '' this.description = '' }, async getTodos() { const todos = await API.graphql({ query: listTodos, }) this.todos = todos.data.listTodos.items }, subscribe() { API.graphql({ query: onCreateTodo }).subscribe({ next: (eventData) => { const todo = eventData.value.data.onCreateTodo if (this.todos.some((item) => item.name === todo.name)) return // remove duplications this.todos = [...this.todos, todo] }, }) }, }, } </script>認証機能を追加する
認証もAmplifyで追加できます。認証はAWSのCognitoを利用します。
$ amplify add auth Do you want to use the default authentication and security configuration? Default configuration Warning: you will not be able to edit these selections. # 以下の設定は設定後の変更不可 How do you want users to be able to sign in? Email Do you want to configure advanced settings? No, I am done.これだけで準備完了です。後はサービスをデプロイするためにはpushしてあげる必要があります。
$ amplify push ? Are you sure you want to continue? YこれでCognitoのセットアップが完了します。次はAmplifyが用意してくれてるVueコンポーネントを差し込んでみましょう。
<template> <amplify-authenticator> <h1>TodoApp</h1> <v-text-field v-model="name" label="Name"></v-text-field> <v-text-field v-model="description" label="Description"></v-text-field> <v-btn @click="createTodo">Create</v-btn> <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.name }} : {{ todo.description }} </li> </ul> <amplify-sign-out></amplify-sign-out> </amplify-authenticator> </template>
<amplify-authenticator>
コンポーネントの子ノードは認証が通った後に表示されるようになります。<amplify-sign-out>
コンポーネントは、いわゆるサインアウトボタンを表示させるだけです。スタイルはドキュメント見た限りカスタマイズできそう。以下はデフォルトの見た目です。英語です。サインインすると、子ノードの内容が表示されます。
認証がこれだけで出来ちゃいます。もちろんCognito側でのアカウント管理もできます。サインインについては提供されてるコンポーネントの他にライブラリ側に用意されたAPIを利用して独自の処理を追加できます。例えば、アカウント情報登録時に追加の情報を登録するなども実現できそうです。
アプリをホスティング環境にデプロイする
AmplifyのCLI使ってデプロイするとS3上でホスティングしてくれるようになります。早速設定していきましょう。
$ amplify add hosting ? Select the plugin module to execute Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment) ? Choose a type Manual deploymentこれでもう設定OKです。ではホスティング環境にデプロイしてみましょう。
$ amplify publish Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------- | --------- | ----------------- | | Hosting | amplifyhosting | Create | awscloudformation | | Api | todoapi | No Change | awscloudformation | | Auth | nuxtamplify2271a807 | No Change | awscloudformation | ? Are you sure you want to continue? Yes ... ✔ Deployment complete! https://dev.XXXXXXXXXXX.amplifyapp.comこのURLクリックしてもらうともうアプリに触れると思います。
次のステップ
わずか数時間でシンプルなアプリがもう実現できたかと思います。
冒頭の繰り返しになりますが、Amplifyでは以下がAmplifyの機能を使って簡単に実現できるので、ドキュメントは英語中心になってますが色々調べながら次のステップを踏みアプリを効率よく開発していきましょう!
- Authentication
- DataStore
- User File Storage
- Serverless APIs
- Analytics
- AI/ML
- Push Notification
- PubSub
- AR/VR
https://docs.amplify.aws/start/getting-started/nextsteps/q/integration/vue
- 投稿日:2020-08-10T13:01:40+09:00
【AWS】Amazon SNSからHTTP/Sで通知する時、エンドポイントでやることを調べた
はじめに
Amazon SNSを使ってHTTP/SでAPIを叩いていきます。
ここでは各AWSサービスの説明は省きます。
AWS超初心者です。目的
- Amazon SNSからHTTP/SでAPIを実行する。
- Amazon SNSからWEBAPIを実行するとき、エンドポイント(受信側)でやることはなにか調べる。 (記事がほとんど見つからなかった)
構成
今回は、APIはAPIGateway + Lambdaで用意。
エンドポイント(受信側)でやること
AWSの公式ドキュメントを見たら、やることは大きく分けて3つあるらしい。
- 受信準備が完了していることを確認する(エンドポイントで「私はAmazon SNSからの通知を受け取れる状態にありますよ~」ということを確認する)
- Amazon SNSからの通知を受け取る
- Amazon SNSからサブスクリプション登録解除の情報を受け取る
最初は、ただAmazon SNSから通知先のエンドポイントを設定してメッセージを送信すればよいと思っていましたが
それだけだと上手くいかないようです。APIの用意
まず、エンドポイントであるAPIを作っていきます。
やりたいことができれば何でもいいのですが、今回は入り口をAPI Gateway、実行される処理をLambdaにしてみました。API Gatewayに紐づける
こんな感じでLambdaをAPI Gatewayに紐づけてあげましょう。
エンドポイントの確認
上記のようにLambdaのコンソールから見れます。
「APIエンドポイント」のURLをコピーしておきましょう。Amazon SNSでトピックの作成
次にAmazon SNSを用意して、トピックを作成後、通知先のエンドポイントを設定します。
通知先のエンドポイントを設定する
Amazon SNSのコンソール画面から通知先のエンドポイントを設定してみるとこんな画面になります。
ステータスが「保留中の確認」となっています。
ステータスを「確認済み」にしないと、トピックからメッセージを発行してもエンドポイントに通知されません。
AWSドキュメント受信登録が確認されるまで、Amazon SNS はこのエンドポイントに通知を送信しません。
APIをLambdaで実装
pythonで作りました。boto3は使っていません。
lambda_function.pyimport requests import json def lambda_handler(event, context): path = event['path'] user_agent = event['headers']['User-Agent'] # この関数がAmazon SNS経由で呼ばれた場合 if path == '/testFunction' and user_agent == 'Amazon Simple Notification Service Agent': type = event['headers']['x-amz-sns-message-type'] # 文字列をdictに変換してbodyの中身を参照できるようにする body_unicode = event['body'] body_dict = json.loads(body_unicode) if type == 'SubscriptionConfirmation': # サブスクライブされた場合の受信登録確認用処理 subscribeurl = body_dict['SubscribeURL'] # 受信登録の確認用URLを叩く response = requests.get(subscribeurl) if response.status_code == 200: print("SubscriptionConfirmation") elif type == 'Notification': # 通知がきた場合の処理 message = body_dict['Message'] print('Amazon SNSからのメッセージ:' + message) print("Notification") elif type == 'UnsubscribeConfirmation ': # 退会通知がきた場合の処理 print("UnsubscribeConfirmation") # レスポンス整形 res = { "isBase64Encoded": False, "statusCode": 200, "headers": {}, "body": type } return json.dumps(res) else : print('SNS経由じゃないよ') res = { "isBase64Encoded": "false", "statusCode": "200", "headers": {}, "body": "not testFunction for Amazon SNS" } return json.dumps(res)コードの説明
基本的には前述している「エンドポイントでやること」をもとに作成しています。
Amazon SNSのメッセージタイプがわかるevent['headers']['x-amz-sns-message-type']
の値によって処理を分けています。
参考:AWS公式ドキュメントコードは、Amazon SNS がエンドポイントに送信する HTTP POST リクエストの HTTP ヘッダーを読み取る必要があります。また、コードは Amazon SNS が送信したメッセージのタイプを示すヘッダーフィールド x-amz-sns-message-type を探す必要があります。
- エンドポイントでやること
- 受信準備が完了していることを確認する(エンドポイントで「私はAmazon SNSからの通知を受け取れる状態にありますよ~」ということを確認する)
- Amazon SNSからの通知を受け取る
- Amazon SNSからサブスクリプション登録解除の情報を受け取る
「1. 受信準備が完了していることを確認する」をやってみる
1は、
response = requests.get(subscribeurl)
で実現しています。
Amazon SNSのコンソール画面から「リクエストの確認」をします。
リロードすると、ステータスが「保留中の確認」から「確認済み」になっていることを確認できます。
注意点
さて、ここで2点ほど注意点(というかはまったポイント)を紹介します。
import requests
できない上記のコードはそのままでは実行できません。
requestsは外部モジュールなので、Lambdaで使えるように環境構築してあげる必要があります。
まずrequestsモジュールをローカルにインポート後zip化してから、zipをLambdaにアップロードします。
以下の記事を参考にしました。
https://hacknote.jp/archives/48083/zip化できるならコマンドでもツール(7zipとか)でも構いません。
pipコマンドは使えるようにしなきゃいけませんね…。
- SubscribeURLを取得できない
これ、めちゃくちゃはまりました。
Amazon SNSから取得したリクエストevent
はdict型だと思っていたのですが、
event
を掘り下げていくとbody
部分が文字列になっていて、SubscribeURL
を参照できなかった。。。
以下の素晴らしい記事を参考に解決できました。よかった。
https://dev.classmethod.jp/articles/lambda-python-tips-all-events-are-not-dict/Amazon SNSから通知を送る
最後にPublishしていきましょう。
今回は特にトリガーとなる処理を用意していないので、コンソールから手動でエンドポイントに通知します。メッセージの発行
エンドポイントでメッセージを受信したことの確認
ちゃんとエンドポイントに設定したLambdaの処理が行われていることを確認できました。最後に
これでなんとなくAmazon SNSからHTTP/Sで受信するときの動きがわかったような気がします。
また何か発見があったら更新します。
- 投稿日:2020-08-10T10:43:59+09:00
AWSサーバレスで(SPAではなく)画面遷移型のWebアプリをつくる
経緯
AWSサーバレスを採用してWebアプリ(画面)を作ることになりました。コンシューマ(一般ユーザ)向けの画面ではなく、企業向けの管理画面です。
メンバーの皆さんにReactとかを学んでいただく時間的な余裕はなかったため、SPAではなく、メンバーの皆さんに経験のある「画面遷移型」の構成にしました。
ただ、AWSサーバレスで画面遷移型のWebアプリを作る、という事例を見つけることができず、実現方式をあれこれ考える必要がありました。構成が固まるまでに悩んだことや、自分なりの解を記事にすることで、同じようなことに悩まれている方のヒントになればと思ってます。
アーキテクチャ
ポイントは以下のとおりです。
- Lambdaではaws-serverless-expressを採用しました。エンドポイントごとにLambda関数を定義する必要がなくなるとともに、ExpressのノウハウやNPMライブラリを活用できるためです。
- テンプレートエンジンにはpug.jsを採用しました。Expressのテンプレートエンジンとしてデフォルト採用されているためです。初めて使ってみましたが、簡潔にコーディングできるので使いやすいと感じました。
シーケンス
①ログイン画面の表示
こちらについては特筆すべきことはありません。express-sessionなどについては後述します。
②ログイン処理
ポイントは以下のとおりです。
Cognitoでの認証
- ユーザープール認証フローに沿って、ユーザの認証を行います。公式ドキュメントの通りに、ブラウザ内のJavaScriptからCognitoにID/パスワードを送信します。公式ドキュメントに記載されている「AmazonCognitoIdentity」を利用するには、こちらの手順に従ってamazon-cognito-identity-jsを読み込む必要があります。
- Cognitoでの認証が成功すると、CognitoからIDトークンとアクセストークンが返却されます。今回は認証をしたいので、IDトークンを利用します。ブラウザ内のJavaScriptから、IDトークンをAPI gatewayに送ります(画面遷移型なので、FormをSubmitします)。
- 一方、ユーザープール認証フローの他に、OpenID Connectによる認証フローも用意されています。その場合、Cognitoのログインエンドポイントを使うことで、ログイン画面のUIすら開発しなくてもよくなります。ただ、ログインエンドポイントから返されるログイン画面には、英語のデフォルト文言をカスタマイズできない、という致命的なデメリットがあります。カスタマイズできるのはCSSでのスタイル定義のみです。今回の案件の場合、さすがに英語のデフォルト文言ではNGでしたので、ログインエンドポイントの利用を諦めました。
IDトークンの検証
- ブラウザから送信されたIDトークン(JWT)を検証します。Express側では、送信されてきたIDトークンが、正当なユーザから送信されたものか、あるいは攻撃者によって偽装されたものなのか、検証する必要があります。そこで、(図では記載を省略してますが)jwks-rsaを利用して、Cognitoの公開鍵でIDトークンの署名を検証します。
- その他、有効期限が切れてないか、などの点をjsonwebtokenを使って検証します。
- Cognitoが発行するIDトークンには、以下のとおりユーザの属性が含まれています。ログイン時には、これらの情報をセッションに格納し、次のリクエストで参照できるようにしておきます。
- 「cognito:groups」クレームに、そのユーザがどのCognitoグループに属するか、という情報が入っています。詳しくはこちらを参照。
- 「custom:~~」に、カスタム属性が入っています。ここに、例えば顧客企業のIDなど、業務処理で必要なデータを設定できます(Cognitoにユーザーを登録するときに、設定されるようにしておきます)。
セッション情報の管理
- セッション情報の管理には、express-sessionを使います。Express界隈でのデファクトみたいですね。Expressのミドルウェア(共通処理)として動作します。セッション情報の管理(作成、取得、削除など)をしっかりやってくれるので、とても便利です。
- express-sessionはセッションの保存先(ストア)の実装を持っておらず、ストアへのアクセス部分は別のライブラリが担当します。今回はセッションのストアとしてDynamoDBを利用したかったので、この「別のライブラリ」としてconnect-dynamodbを採用します。
- DynamoDBに、セッション情報を保存するテーブル(セッション管理TBL)を定義する必要があります。詳しくは、connect-dynamodbのドキュメントを参照してください。
セッションIDの返却
- セッション情報が新規に生成されると、express-sessionによってセッションIDが採番されます。このセッションIDをCookieに保存してブラウザに返却します。この時、(常識ですが)CookieにSecure属性を付与する必要があります。ただ、今回の構成の場合、aws-serverless-expressがプロキシの役割を果たすため、aws-serverless-express ⇔ Express間はhttp通信となります。このため、ExpressでSecure属性を付与すると、http通信なのでCookieが欠落した状態でレスポンスが送信されます。これを回避するには、app.set()でtrust proxyを設定する必要があります。今回はLambda内のaws-serverless-expressからしかExpressは呼ばれないので、単に
app.set('trust proxy', true)
)と設定しちゃいました。- (これもまた常識ですが)CookieにはHttpOnly属性を必ず付けましょう。express-sessionの設定で制御可能です。デフォルト設定はONなので、知らなくても問題ないかもしれませんが。
③ログイン後の画面遷移(認証・認可チェック)
ポイントは以下のとおりです。
認証チェック
- ログイン時にセッションに格納しておいたユーザIDをreq.sessionから取得します。以下の場合、未認証と見なすべきです。いずれの場合も
if(req.session.userId)
という感じで判断できます。
- そもそも、セッションIDが送られていない場合。この場合、req.sessionにSessionオブジェクトが生成されます(この時のSessionオブジェクトには、空のCookieしか入ってません)。
- セッションIDは送信されているが、セッション管理TBLに対応する項目(レコード)が無い場合や有効期限が切れている場合。
- 未認証の場合、ログイン画面にリダイレクトします。
- これらの処理は、Expressのミドルウェアとして実施します。
認可チェック
- ログイン時にセッションに格納しておいたCognitoグループ(IDトークンのcognito:groupsクレームに入っていたもの)をreq.sessionから取得します。req.originalUrlからアクセス対象のパスを取得します。ユーザが属するグループに、そのパスを実行する権限があるかを判定し、権限がなければエラー画面を表示します。どのグループにどのパスのどのメソッドの実行が許可されるのか、といった定義については、今回は設定ファイルにベタ書きしちゃいました。
その他
バリデーション
単項目のバリデーションには、express-validatorを利用します。
実戦でこれを使うには、色々と工夫が必要です。最終的には以下のようになりました。
router
// 商品登録処理 router.post('/registerItem', validator.forRegisterItem, controller.registerItem);
- 単項目のバリデーションについてはvalidatorにまとめて実装します。可読性を高めるためです。
validator
const { required, maxLength, alphanumeric } = require('../resources/message').BizError.SingleItemValidationError; // 画面から入力されるのは、itemId(商品ID)、itemName(商品名)とします。 exports.forRegisterItem = [ body('itemId', required).isLength({ min: 1 }), body('itemId', alphanumeric).isAlphanumeric(), body('itemId', maxLength({ max: 10 })).isLength({ max: 10 }), body('itemName', required).isLength({ min: 1 }), body('itemName', maxLength({ max: 10 })).isLength({ max: 10 }), ];
- エラーメッセージの定義を共通化するため、messageに文言を定義します。
- trimやescape(サニタイジング)といった処理は、以下のようにExpressミドルウェアで共通処理として定義します。
app.use([body('*').trim().escape(), query('*').trim().escape(), param('*').trim().escape()]);message
const BizError = { SingleItemValidationError: { /** 必須エラー */ required: '必須項目です。', /** 英数字以外が入力された場合のエラー */ alphanumeric: '英数字で入力してください。', /** 桁数上限エラー */ maxLength: ({ max }) => `${max}文字以下で入力してください。`, }, // 以下、略。
- 「●●文字以下で入力してください」といったように、●●の部分を可変にできるようにすべきです。そこで、maxLengthは関数として定義しています。
controller
// 商品登録 exports.registerItem = commonLayer.wrap(async (req, res) => { const itemId = req.body.itemId; const itemName = req.body.itemName; const errors = validationResult(req); if (!errors.isEmpty()) { const errMsgs = validationUtil.groupMsgsByProp(errors); res.render('customer/registerItem', { ...errMsgs, itemId, itemName }); return; } // 後続処理 });
- validationUtilでは、pugで入力項目の近くにエラーメッセージを表示するためにひと工夫をしています。
- バリデーションとは関係ないですが、
commonLayer.wrap()
でやっていることはこちらの記事と同じです。validationUtil
exports.groupMsgsByProp = (errors) => { const errorsMappings = errors.errors.reduce((prev, current) => { if (!prev[current.param]) { prev[current.param] = []; } prev[current.param].push(current); return prev; }, {}); return { 'errorMappings': errorsMappings, }; };pug
registerItem.pugextends ../common/layout.pug block title title 商品登録 block content .container.mt-5 .d-flex.justify-content-center .col-8 if successMsg p.text #{successMsg} +globalErrMsg() form(method="post") .form-group label(for="itemId") 商品ID input#company_id.form-control(type="text", name="itemId", value=itemId) +errMsgsOf('itemId') .form-group label(for="itemName") 商品名 input#company_id.form-control(type="text", name="itemName", value=itemName) +errMsgsOf('itemName') input.btn.btn-primary(type="submit", value="登録")common/layout.pugmixin errMsgsOf(propName) if errorMappings && errorMappings[propName] each error in errorMappings[propName] div #{error.msg}Expressミドルウェア設定
- helmet、noCacheを利用して、レスポンスヘッダーを設定します。これにより、XSSなどの対策を行い、セキュリティレベルを高めます。
- その他、セキュリティについては、Express公式サイトでの解説をしっかり把握しておくのが良いです。
おわりに
新たな知見が得られましたら、今後も更新していきたいと思います。
- 投稿日:2020-08-10T10:12:46+09:00
自動デプロイ後にCSSが反映されない時の対処法【初学者向け】
ターミナルでpsコマンドを入力してUnicornのプロセスを確認しましょう
psコマンドは、現在動いているプロセスを確認するためのコマンド
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn実行すると以下のように表示されます
ec2-user 17877 0.4 18.1 588472 182840 ? Sl 01:55 0:02 unicorn_rails master -c config/unicorn.rb -E production -D ec2-user 17881 0.0 17.3 589088 175164 ? Sl 01:55 0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -D ec2-user 17911 0.0 0.2 110532 2180 pts/0 S+ 02:05 0:00 grep --color=auto unicornUnicornのプロセスをKillしましょう
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ kill <確認したunicorn rails masterのPID(今回の場合は「17877」)>再度、プロセスを表示させ終了できていることを確認しましょう。
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn ec2-user 17911 0.0 0.2 110532 2180 pts/0 S+ 02:05 0:00 grep --color=auto unicorn実行結果が上記のようになっていればUnicornを停止が完了です。ローカルで行なっていた、ctrl + cでサーバをストップするという作業と同じことをしたことになります。
プロセスが終了できない場合
下記の2行が表示が消えていない場合はプロセスが終了できていないことになります。
ec2-user 17877 0.4 18.1 588472 182840 ? Sl 01:55 0:02 unicorn_rails master -c config/unicorn.rb -E production -D ec2-user 17881 0.0 17.3 589088 175164 ? Sl 01:55 0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -Dオプション-9をkillコマンドにつけると強制終了を実行できます。通常のkillコマンドで削除できない場合はこちらを使用しましょう。
$ kill -9 [プロセスID]先頭にRAILS_SERVE_STATIC_FILES=1をつけて、unicornを起動しましょう
続いて再びunicornを起動しましょう。このとき RAILS_SERVE_STATIC_FILES=1 という指定を先頭に追加してください。これは、コンパイルされたアセットをRailsが見つけられるような指定になります。以下のようにコマンドを実行してください。
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -Dで、再度自動デプロイ
# アプリケーションのディレクトリで実行する $ bundle exec cap production deployまとめ
cssが反映されなかったらkillしてunicornを再起動!
- 投稿日:2020-08-10T07:36:29+09:00
Amplify + AppSync(graphQL) + DynamoDB でどんな感じでリソース作られるのか見てみる
だいたいこのあたりのシリーズです。
Amplify + AppSync + Cognitoで読み書きの制御を試してみる
https://qiita.com/ikegam1/items/4868b8a2b473e7ec8f85やる事
amplify側で
amplify add api
やらamplify push
やらするとして、DynamoDB側のGSIとか制御できるのかどうか気になったので試してみる回です。目次
- 初期設定
- スキーマ作成
- DynamoDB確認
- 参考
1. 初期設定
今回は下記が終わっているものとします。
- amplify cilインストール (4.24.3 でした)
amplify add auth
によるcognitoの設定npx create-react-app react-amplified
reactかつ react-amplified ってプロジェクトフォルダで進める- プロジェクトフォルダ内での
amplify init
- 実行環境はWSL2上のubuntu18.4です。
2. スキーマ設定
2-1.
amplifyで生成されるBlog exampleをモチーフにしつつ下記のようなスキーマを作成してみます。
schema.graphqltype Blog @model @key (fields: ["id"]) @auth(rules: [ { allow: owner }, { allow: private, operations: [read] } ]) { id: ID! title: String! owner: String! updated_dt: AWSDateTime! created_dt: AWSDateTime! TTL: AWSTimestamp! posts: [Post] @connection(keyName: "byBlog", fields: ["id"]) } type Post @model @auth(rules: [ { allow: owner }, { allow: private, operations: [read] } ]) @key (fields: ["id"]) @key ( name: "listByStatus", fields: ["status", "updated_dt"], queryField: "listPostsByStatus" ) @key(name: "byBlog", fields: ["blogID"]) { id: ID! title: String! blogID: ID! status: String! owner: String! updated_dt: AWSDateTime! created_dt: AWSDateTime! TTL: AWSTimestamp! blog: Blog @connection(fields: ["blogID"]) } type Memo @model @auth(rules: [ { allow: owner } ]) @key (fields: ["owner", "updated_dt"]) { id: ID! memo: AWSJSON! owner: String! updated_dt: AWSDateTime! created_dt: AWSDateTime! TTL: AWSTimestamp! }
@auth
を指定してみた@key
でPKやSKを指定してみた@key
でGSIを指定してみた- AWSDateTimeやAWSJSONというAppSyncで使える型を使ってみた
2-2.
次に
amplify add api
します$ amplify add api ? Please select from one of the below mentioned services: GraphQL ? Provide API name: reactamplified ? Choose the default authorization type for the API Amazon Cognito User Pool Use a Cognito user pool configured as a part of this project. ? Do you want to configure advanced settings for the GraphQL API No, I am done. ? Do you have an annotated GraphQL schema? No ? Do you want a guided schema creation? Yes ? What best describes your project: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”) ? Do you want to edit the schema now? Yes GraphQL schema compiled successfully.Do you want to edit the schema now? のとこで前述のスキーマを記述します。
そして
amplify push(そういや、? Choose the code generation language target のとこで出てくる
flow
って何だろね?気が向いたら調べる)pushが完了したら、作成されたリソースを確認してみましょう。
まずはAppSyncのコンソールを3. AppSync コンソール
ちゃんとスキーマが反映されてる。なるほど、BlogとPostのリレーショナルはこんな感じになるのか。
で、こんな感じでMutation実行しときます。
AppSyncコンソールのクエリのとこから実行できます。mutation MyMutation { createBlog(input: {title: "日記ブログログ", updated_dt: "2020-08-10T11:11:11.111Z", created_dt: "2020-08-10T11:11:11.111Z", TTL: 1572268323, owner: "testUser01"}) { id } } mutation MyMutation { createPost(input: {title: "今日のカレー", blogID: "7998ea07-d679-4a6d-bca8-5ce6af692929", status: "ACTIVE", owner: "testUser01", created_dt: "2020-08-10T11:11:11.111Z", TTL: 1572268323, updated_dt: "2020-08-10T11:11:11.111Z"}) { id } } mutation MyMutation { createMemo(input: {memo: "{}", owner: "testUser01", updated_dt: "2020-08-10T11:11:11.111Z", created_dt: "2020-08-10T11:11:11.111Z", TTL: 1572268323}) { id } }
owner
は明示的に記載しない方がいいのかな。cognito上のユーザー名を入力してみたけどあってるかわからん。
あとcreatedAt
とかupdatedAt
も作られてる。これも明示的には作らない方が良さそうだ。なお、Queryはこんな感じ。
query MyQuery { listPostsByStatus(filter: {owner: {beginsWith: "Test"}}, status: "ACTIVE", updated_dt: {ge: "2020-08-01"}) { nextToken items { owner status title id created_dt } } }filterを設定しないとなんか怒られた。
- DynamoDB側
次にDynamoDBの定義がどんな感じになってるか確認。
ちなみにAppSyncのデータソースのところはこういう感じ。Postsの項目
__typename
っていうのが作られてる。
creaetdAt
とかにはnowな時間が勝手に入ってくれるんですね。PostsのGSI
うん。想定通り。
@key
で指定すれば作られる。あんまり複雑な事をしないのであればけっこう簡単そうな印象。
5. 参考
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/scalars.html
https://d1.awsstatic.com/ja_JP/startupday/sudo2020/SUD_Online_2020_Tech07.pdfこれも忘れずに
$ amplify delete
- 投稿日:2020-08-10T07:30:34+09:00
Amazon LightsailでFX自動売買サーバーを構築する
FX自動売買をするため、MetaTrader4(MT4)にてEA(自動売買プログラム)を
24時間365日動かす環境として、Amazon Lightsailを使ってみた概要
Amazon Lightsailとは
- VPS(仮想専用サーバ)を提供するサービス
- ネットワークからサーバーまでの構築および接続設定までマネージドされている
- 追加でELBによる冗長構成、RDS、CDNを利用可能
- バックアップをEC2にエクスポート可能
- 冗長構成をとっている場合はSLAは99.99%
最低限の料金
- インスタンス
- ストレージ
- データ転送(1TB/月を超えた分)
EC2ではなくLightsailを選んだ背景
- とりあえず動かす環境がほしい
- 構築コストが比較的低い
- 細かいネットワーク要件は不要
- 非機能要件が難しくない
- 大量の通信なし
- 計算量も多くない(EAによる)
- 維持費が明確
- 導入スピードを上げるため、コスト計算にリソースを割きたくない
- いつまで稼働するか不明
- 1年先まで稼働させるか現時点では不明
MetaTrader4(MT4)とは
- FXを含め、株などを売買するツール
- チャートを表示し、注文、決済ができる
- 独自プログラム(MQL)の実行が可能
- 計算した線やグラフを描画
- 計算した線やグラフをトリガーとして注文、決済を実行
- 各社がカスタマイズしたMT4を配布している
MQL
- 金融市場での取引や分析のためのプログラミング言語
- C++がベース
- 取引に必要な関数が用意されている
- MQL5があるが、主流はMQL4
サーバー構築
- AWSマネジメントコンソールよりLightsailを選択
![]()
- 日本語を選択
![]()
- チュートリアルに従って構築開始
![]()
- Windowsを選択
![]()
- OSバージョンを選択
![]()
- プランを選択(今回は無料プランを選択)
![]()
- インスタンス名は任意(タグはなし)
![]()
- 起動されるまで待機(保留中)
![]()
- 実行中になった後、インスタンスを選択
![]()
- ブラウザから接続
![]()
- 接続できることを確認
![]()
リモートデスクトップ接続
ブラウザから接続でも問題ないがWindowsのリモートデスクトップ接続を利用する
※リモートデスクトップ接続が見つからない場合、設定変更が必要
日本語設定
デフォルトでは英語のままなので、日本語にする
- 設定を選択
![]()
- 「Time & language」を選択
![]()
- 「Region & language」を選択し、「日本語」をデフォルトに設定
![]()
- オプションを選択
![]()
- 日本語パックをダウンロード
![]()
- 日本語配列のキーボードに変更
![]()
- 再起動する
![]()
- 再起動後、日本語になっていることを確認
![]()
MT4のインストール
- サーバーマネージャーを起動
![]()
- 「ローカルサーバー」から 「IE セキュリティ強化の構成」を選択
![]()
- オフにして保存
![]()
- 時間を置いて 「IE セキュリティ強化の構成」が無効になっていることを確認
![]()
- MT4のインストーラーをダウンロードし、実行
![]()
- 取引で利用するサーバーを選択(ここではデモサーバーを選択)
![]()
- 口座情報を入力(ここでは既存デモ口座情報を利用)
![]()
- チャートが表示されることを確認
![]()
インジゲータの設定
EAの設定
- オプションを選択
![]()
- EAでの自動売買とDLLの利用を許可
![]()
- ナビゲーションウィンドウからEAをチャートにドラッグ&ドロップ
![]()
- EAによる取引、DLLおよび外部EAの利用を許可
![]()
- チャート右上にEA名と顔マークが表示されていることを確認
![]()
- 自動売買を開始
![]()
- 自動売買が緑になり、顔マークが笑顔になることを確認
![]()
- 一度に複数のEAを動かすときはマジックナンバーをずらすこと
![]()
感想
- EC2より楽に作れる
- 無料プランでWindowsはマシンスペック的にギリギリ
- 過去チャートを使ってEAを試すストラテジーテスターは高負荷で使えない
- コスト削減を厳密に考慮する場合はEC2で作った方がよさそう
- 期間限定サーバーやお試しサーバーならLightsailがよさそう
- インスタンスの世代が最新じゃないのはちょっと残念
- VPSのことを調べるときにSLAの話がほとんど出てこなかった
- あんまり重要ではないのか?
- Lightsailは他のVPSと比較すると敷居が高いっぽい
- 普及してない?
- ターゲットはあくまでエンジニア?
- いつかは自動売買で利用料が払えるようになると嬉しい