20200810のAWSに関する記事は19件です。

[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を使用したいので、リージョンをバージニア北部にします。

CodeStar1.png

アプリケーションの種類や、言語を選択できます。
今回は、Lambda + Java Springのテンプレートでプロジェクトを作成してみようと思います。

CodeStar3.png

続いてコードリポジトリを選択します。
今回は、CodeCommitを選択してみますね。

CodeStart4.png

内容を確認して、プロジェクトを作成します。

最後にIDEを選択します。
今回は、AWS Cloud9です。リージョンによっては、Cloud9を選択できないのでご注意ください。
Cloud9を選択すると、インスタンスタイプを聞かれますので、今回はデフォルトのt2.microのまま進めます。

CodeStar5.png

プロジェクトとIDEの作成が始まります(裏でCloudFormationが動作します)。

CodeStar6.png

プロジェクトが大体3分で環境構築が完了します。
IDEはEC2インスタンスの作成があるので、こちらも大体3分くらいかかります。
両方とも準備が完了すると、下記のような画面になります。

CodeStar7.png

リポジトリの状態

左側にあるメニューより「コード」を選択すると、CodeCommitの画面が表示されます。
Springのテンプレート構造が作成されており、buildspec.ymlまで作成されています。

CodeCommit1.png

なお、buildspec.ymlの中身はこんな感じです。

codespec.yml
version: 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.json

OpenJDK8で、Mavenでコンパイルされていることがわかりますね。

IDE

では、メニューからIDEを選択してみます。
すると、CLoud9が開始され、CodeCommitのリポジトリから、コードをgit cloneで自動的にコードを取得してくれます。

CodeCommit1.png

 あお、チームの設定を行うと、クロスアカウントでのコードリポジトリの共有や、モブプログラミングなんかも行うことができます。

[AWS] Cloud9でモブプログラミングの環境を作ってみる

アプリ実行

今回は、LambdaによるAPIです。
ダッシュボードにある「アプリの表示」ボタンを押すと、プロジェクトのテンプレートで最初に入っているAPIが呼び出されます。

{"Output":"Hello World!"}

具体的なAPI GatewayやLambdaはどうなっているのでしょう?

API Gateway

REST APIが一つできていることがわかりますね。

APIGateway.png

これ自体は、コード上のtenpkate.ymlに定義されています。

template.yml
Resources:
  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: post

Lambda

こちらは、Get用とPost用の2つが存在します。これも、API Gatewayで記載したtemplate.ymlで定義さている内容ですね。

Lambda1.png

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

パイプラインを確認すると、ビルドが開始されたことがわかります。

build1.png

実は、ここでエラーになります。
確認してみると、どうやらテストで失敗しているようですね。

[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が早いうちに東京リージョン対応になることを望みますが、それを差し引いても利用価値はありそうですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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を使用したいので、リージョンをバージニア北部にします。

CodeStar1.png

アプリケーションの種類や、言語を選択できます。
今回は、Lambda + Java Springのテンプレートでプロジェクトを作成してみようと思います。

CodeStar3.png

続いてコードリポジトリを選択します。
今回は、CodeCommitを選択してみますね。

CodeStart4.png

内容を確認して、プロジェクトを作成します。

最後にIDEを選択します。
今回は、AWS Cloud9です。リージョンによっては、Cloud9を選択できないのでご注意ください。
Cloud9を選択すると、インスタンスタイプを聞かれますので、今回はデフォルトのt2.microのまま進めます。

CodeStar5.png

プロジェクトとIDEの作成が始まります(裏でCloudFormationが動作します)。

CodeStar6.png

プロジェクトが大体3分で環境構築が完了します。
IDEはEC2インスタンスの作成があるので、こちらも大体3分くらいかかります。
両方とも準備が完了すると、下記のような画面になります。

CodeStar7.png

リポジトリの状態

左側にあるメニューより「コード」を選択すると、CodeCommitの画面が表示されます。
Springのテンプレート構造が作成されており、buildspec.ymlまで作成されています。

CodeCommit1.png

なお、buildspec.ymlの中身はこんな感じです。

codespec.yml
version: 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.json

OpenJDK8で、Mavenでコンパイルされていることがわかりますね。

IDE

では、メニューからIDEを選択してみます。
すると、CLoud9が開始され、CodeCommitのリポジトリから、コードをgit cloneで自動的にコードを取得してくれます。

CodeCommit1.png

なお、チームの設定を行うと、クロスアカウントでのコードリポジトリの共有や、モブプログラミングなんかも行うことができます。

[AWS] Cloud9でモブプログラミングの環境を作ってみる

アプリ実行

今回は、LambdaによるAPIです。
ダッシュボードにある「アプリの表示」ボタンを押すと、プロジェクトのテンプレートで最初に入っているAPIが呼び出されます。

{"Output":"Hello World!"}

具体的なAPI GatewayやLambdaはどうなっているのでしょう?

API Gateway

REST APIが一つできていることがわかりますね。

APIGateway.png

これ自体は、コード上のtenpkate.ymlに定義されています。

template.yml
Resources:
  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: post

Lambda

こちらは、Get用とPost用の2つが存在します。これも、API Gatewayで記載したtemplate.ymlで定義さている内容ですね。

Lambda1.png

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

パイプラインを確認すると、ビルドが開始されたことがわかります。

build1.png

実は、ここでエラーになります。
確認してみると、どうやらテストで失敗しているようですね。

[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が早いうちに東京リージョン対応になることを望みますが、それを差し引いても利用価値はありそうですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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を使用したいので、リージョンをバージニア北部にします。

CodeStar1.png

アプリケーションの種類や、言語を選択できます。
今回は、Lambda + Java Springのテンプレートでプロジェクトを作成してみようと思います。

CodeStar3.png

続いてコードリポジトリを選択します。
今回は、CodeCommitを選択してみますね。

CodeStart4.png

内容を確認して、プロジェクトを作成します。

最後にIDEを選択します。
今回は、AWS Cloud9です。リージョンによっては、Cloud9を選択できないのでご注意ください。
Cloud9を選択すると、インスタンスタイプを聞かれますので、今回はデフォルトのt2.microのまま進めます。

CodeStar5.png

プロジェクトとIDEの作成が始まります(裏でCloudFormationが動作します)。

CodeStar6.png

プロジェクトが大体3分で環境構築が完了します。
IDEはEC2インスタンスの作成があるので、こちらも大体3分くらいかかります。
両方とも準備が完了すると、下記のような画面になります。

CodeStar7.png

リポジトリの状態

左側にあるメニューより「コード」を選択すると、CodeCommitの画面が表示されます。
Springのテンプレート構造が作成されており、buildspec.ymlまで作成されています。

CodeCommit1.png

なお、buildspec.ymlの中身はこんな感じです。

codespec.yml
version: 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.json

OpenJDK8で、Mavenでコンパイルされていることがわかりますね。

IDE

では、メニューからIDEを選択してみます。
すると、CLoud9が開始され、CodeCommitのリポジトリから、コードをgit cloneで自動的にコードを取得してくれます。

CodeCommit1.png

 あお、チームの設定を行うと、クロスアカウントでのコードリポジトリの共有や、モブプログラミングなんかも行うことができます。

アプリ実行

今回は、LambdaによるAPIです。
ダッシュボードにある「アプリの表示」ボタンを押すと、プロジェクトのテンプレートで最初に入っているAPIが呼び出されます。

{"Output":"Hello World!"}

具体的なAPI GatewayやLambdaはどうなっているのでしょう?

API Gateway

REST APIが一つできていることがわかりますね。

APIGateway.png

これ自体は、コード上のtenpkate.ymlに定義されています。

template.yml
Resources:
  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: post

Lambda

こちらは、Get用とPost用の2つが存在します。これも、API Gatewayで記載したtemplate.ymlで定義さている内容ですね。

Lambda1.png

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

パイプラインを確認すると、ビルドが開始されたことがわかります。

build1.png

実は、ここでエラーになります。
確認してみると、どうやらテストで失敗しているようですね。

[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が早いうちに東京リージョン対応になることを望みますが、それを差し引いても利用価値はありそうですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LocustでWebサイトの性能測定をする(とりあえずDockerで動かしてみた編)

はじめに

Webサイトの性能測定といえばJMeterが定番な感じだが、そもそもなかなか起動させるまでの道のりが長くて学習コスト的にアレだったので、シナリオがPythonでお手軽に書けるという噂のLocustを使ってみた。

今回は、EC2でDockerでインストールしてとりあえず動かすところまで。
ちなみに、t2.microでは動作が不安定なので、t3.mediumくらいで起動すると良い感じであった。

Dockerのインストール

これはもう定番の手順だけど一応書いておく。

yumの最新化
$ sudo yum update -y
Dockerのインストール
$ sudo yum install -y docker
Dockerの開始
$ sudo service docker start
ec2-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 -i
docker-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固定になってしまうので、適宜変更したらリロードして起動する。

Dockerfile
FROM python

RUN pip install locust

WORKDIR /locust
COPY ./locustfile.py .

CMD ["locust"]

これを、docker-compose.yml で設定してdocker-compose upする。

docker-compose.yml
version: '3'
services:
  locust:
    build: .
    ports:
      - "8089:8089"

だがちょっと待て。肝心のlocustfile.pyが無いと起動しない。
以下が起動のための最低セットだ。

locustfile.py
from 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接続すると、以下のように設定画面が出る。

キャプチャ4.png

接続ユーザ数と、ユーザ生成/秒と、ホスト名だ。
ホスト名の後ろに、locustfile.pyを付けてアクセスするイメージだ。

適当に値を入れて起動すると、

キャプチャ5.png

といった感じで負荷状況が表示される。

さらに、Chartsを選択すると、

キャプチャ6.png

グラフも見られるぞ!

「Download Data」からこのグラフのネタが取れるかと思いきや、取れたcsvにはこの情報は含まれていなかった。なぜ……。

ということで、まずは性能測定の足掛かりができた!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CodeDeploy で Auto Scaling グループ に複数のターゲットグループを紐づける

お疲れ様です。
タイトルの通り備忘録です。
どこかの記事を参考にしたと思ったのですが、見つからなかったので知っている方がいれば教えてください?‍♂️

用意するもの

  • appspec.yml
  • 紐づけ処理用のシェルスクリプト(before_allow_traffic.sh)

以上

appspec.yml

このファイルは、デプロイを管理するために CodeDeploy によって使用されるファイルです。

appspec.yml
version: 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Django REST Frameworkでdynamodbを操作するREST APIを作成する

はじめに

django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。

Dynamodbテーブル作成(事前準備)

下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Name

スクリーンショット 2020-08-10 22.16.19.png

djangoプロジェクトの作成

django project(dynamo_operation)とapp(api)を作成

$ django-admin startproject dynamo_operation
$ cd dynamo_operation/
$ django-admin startapp api 

setting.pyの編集

setting.pyrest_frameworkと先ほど作成したappのconfigを追加する。

dynamo_operation/setting.py
INSTALLED_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.py
class Fruit():
    def __init__(self, name):
        self.name = name

views.pyの編集

api/views.py
from 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_frameworkAPIViewを継承したclassでリクエストを処理する。
DynamoRequestがpathパラメータなしのリクエストを処理し、DynamoDetailRequestでpathパラメータ(pk)ありのリクエストの処理を行う。
APIViewを継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。

urls.pyの編集

api/urls.py
from 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.py
from 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/

POSTリクエスト後のテーブル
スクリーンショット 2020-08-10 23.16.38.png

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/

PUTリクエスト後のテーブル
スクリーンショット 2020-08-10 23.21.52.png

DELETE

peachの項目を削除する。

$ curl -X DELETE http://127.0.0.1:8000/api/peach/

DELETEリクエスト後のテーブル
スクリーンショット 2020-08-10 23.23.47.png

おわりに

django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[DRF+boto3]Djangoでdynamodbを操作するAPIを作成する

はじめに

django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。

Dynamodbテーブル作成(事前準備)

下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Name

スクリーンショット 2020-08-10 22.16.19.png

djangoプロジェクトの作成

django project(dynamo_operation)とapp(api)を作成

$ django-admin startproject dynamo_operation
$ cd dynamo_operation/
$ django-admin startapp api 

setting.pyの編集

setting.pyrest_frameworkと先ほど作成したappのconfigを追加する。

dynamo_operation/setting.py
INSTALLED_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.py
class Fruit():
    def __init__(self, name):
        self.name = name

views.pyの編集

api/views.py
from 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_frameworkAPIViewを継承したclassでリクエストを処理する。
DynamoRequestがpathパラメータなしのリクエストを処理し、DynamoDetailRequestでpathパラメータ(pk)ありのリクエストの処理を行う。
APIViewを継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。

urls.pyの編集

api/urls.py
from 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.py
from 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/

POSTリクエスト後のテーブル
スクリーンショット 2020-08-10 23.16.38.png

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/

PUTリクエスト後のテーブル
スクリーンショット 2020-08-10 23.21.52.png

DELETE

peachの項目を削除する。

$ curl -X DELETE http://127.0.0.1:8000/api/peach/

DELETEリクエスト後のテーブル
スクリーンショット 2020-08-10 23.23.47.png

おわりに

django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Django REST Frameworkでdynamodbを操作するREST APIを作成する[boto3]

はじめに

django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。

Dynamodbテーブル作成(事前準備)

下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Name

スクリーンショット 2020-08-10 22.16.19.png

djangoプロジェクトの作成

django project(dynamo_operation)とapp(api)を作成

$ django-admin startproject dynamo_operation
$ cd dynamo_operation/
$ django-admin startapp api 

setting.pyの編集

setting.pyrest_frameworkと先ほど作成したappのconfigを追加する。

dynamo_operation/setting.py
INSTALLED_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.py
class Fruit():
    def __init__(self, name):
        self.name = name

views.pyの編集

api/views.py
from 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_frameworkAPIViewを継承したclassでリクエストを処理する。
DynamoRequestがpathパラメータなしのリクエストを処理し、DynamoDetailRequestでpathパラメータ(pk)ありのリクエストの処理を行う。
APIViewを継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。

urls.pyの編集

api/urls.py
from 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.py
from 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/

POSTリクエスト後のテーブル
スクリーンショット 2020-08-10 23.16.38.png

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/

PUTリクエスト後のテーブル
スクリーンショット 2020-08-10 23.21.52.png

DELETE

peachの項目を削除する。

$ curl -X DELETE http://127.0.0.1:8000/api/peach/

DELETEリクエスト後のテーブル
スクリーンショット 2020-08-10 23.23.47.png

おわりに

django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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'
            )
        );

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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リクエストに応答してコンテンツを返してくれる機能だ。ちょっとしたコンテンツならこれでサーバレスに賄えてしまう。便利!

ここではサクッとホスティング機能を設定することだけ書いているが、商用で運用する場合はアクセスログの記録やアクセス制御をちゃんとやろう。

マネージメントコンソールでの設定

マネコン画面では以下の画面から設定が可能。簡単!

キャプチャ.png

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ネックになっている可能性があるので、悪しからず。
別途チューニングして測定をしたら再度結果を載せようと思う。

キャプチャ2.png
キャプチャ3.png

ご覧の通り、70~80rpsあたりでようやく安定するといったところだ。
※それでもまだ少しだけエラーが出る。

Locust側のチューニング結果次第だが、CloudFrontでキャッシュしてあげるべきなのだろうか……。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでZabbixを作成する際の注意点

はじめに

AWSのEC2とRDSを利用してZabbix4.2を導入するを参照しながら作成したが、当方が初心者のため一部詳細が書かれていない箇所でどうやったらいいのか戸惑ったり、Macを使っているので一部作業内容が違った。
そのため本項でその点を補足しつつ、説明していきたい。
(すでに作成したため、初めて作成する場合と画像が一部異なる可能性がございますが、ご了承ください。)

手順

AWS側の設定

セキュリティグループの作成

VPC>セキュリティグループ>セキュリティグループを作成をクリックする。
スクリーンショット 2020-08-10 12.23.08.png
セキュリティグループ名、説明を入力する。今回は「security-sg」とする。
VPCは今回デフォルトのものとする。

スクリーンショット 2020-08-10 12.28.42.png
インバウンドルールやアウトバウンドルールはそのままで「セキュリティグループを作成」を押す。
スクリーンショット 2020-08-10 12.30.32.png
セキュリティグループで先ほど作成したものをチェックを入れて、下のインバウンドルールをクリック。画像のようなインバウンドルールになるように「インバウンドルールを編集をクリック」する。(あらかじめ作成しているので、すでに入っている状態ですが、本来はこの時点ではインバウンドルールは空です。)
スクリーンショット 2020-08-10 12.34.03.png
インバウンドルールを編集で、以下のように編集していく。

タイプ ポート範囲 ソース 説明
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>「インスタンスの作成」をクリック。
スクリーンショット 2020-08-10 12.34.03.png
「Amazon Linux 2 AMI (HVM), SSD Volume Type」を選択する。
スクリーンショット 2020-08-10 16.38.42.png
インスタンスタイプを選択。特にこだわりなければ、無料利用枠の対象のものを選ぶ。「次のステップ:インスタンスの詳細の設定」を押す。
スクリーンショット 2020-08-10 16.39.46.png
インスタンス数は2、ネットワークはセキュリティルールで設定したVPCを選択。
(今回はデフォルトとする。)「次のステップ:ストレージの追加」ボタンを押す。
スクリーンショット 2020-08-10 17.35.35.png
特にこだわりがなければデフォルトのままで、「次のステップ:タグの追加」をクリックする。
スクリーンショット 2020-08-10 17.39.44.png
タグの追加の必要がなければ、「次の手順:セキュリティグループの設定」を押す。
スクリーンショット 2020-08-10 17.41.35.png
「既存のセキュリティグループを選択する」で先ほど作成したセキュリティグループを選択し、「確認と作成」を押す。
スクリーンショット 2020-08-10 17.57.44.png
インスタンス作成の確認で内容を確認し、「起動」を押す。
スクリーンショット 2020-08-10 18.15.35.png
既存のキーペアを選択するか、新しいキーペアを作成する。完了したらインスタンスの作成を押す。
スクリーンショット 2020-08-10 18.17.34.png

EC2のインスタンスの画面に戻り、作成できたか確認する。

RDSの作成

RDS>データベースの作成を押す。
「データベース作成方法を選択」>「標準作成」を押す。
スクリーンショット 2020-08-10 18.27.05.png

「エンジンのオプション」から下記の項目を選択する。

エンジンのタイプ : MySQL
バージョン: MySQL 5.7.26

スクリーンショット 2020-08-10 18.28.01.png
「テンプレート」>「無料利用枠」を押す。
スクリーンショット 2020-08-10 18.28.18.png
「設定」から下記の項目を入力する。

DBインスタンス識別子 : (AWSコンソール上で表示される名前)
マスターユーザー名 : (MySQLのログインユーザー名)
マスターパスワード : (MySQLのパスワード)
スクリーンショット 2020-08-10 18.34.13.png
DB インスタンスサイズはデフォルトの「db.t2.micro」を選択する。(無料利用枠だとdb.t2.microしか選択できないようだ。)
スクリーンショット 2020-08-10 18.36.25.png
「ストレージ」から下記のように設定する。
・ストレージタイプ:「汎用(SSD)」
・ストレージ割り当て:20
・ストレージの自動スケーリングを有効にする:チェックを外す
スクリーンショット 2020-08-10 18.37.56.png
「接続」でセキュリティグループで設定したVPCを選択する。(今回はデフォルトを選択する。)
スクリーンショット 2020-08-10 18.43.36.png
内容を確認し、「データベースの作成」を押す。
スクリーンショット 2020-08-10 18.51.40.png

Zabbix Server側の設定

今回はMacのterminalでインスタンス接続を行う。

EC2を作成した際に作った鍵の置き場所とEC2のインスタンスの説明でIPv4 パブリックIPを確認して、ssh接続を行う。

ssh -i  鍵の置き場所 ec2-user@IPv4 パブリックIP

Zabbixのインストール

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.rpm

zabbix-server-mysql,zabbix-web-mysql,zabbix-web-japanese,zabbix-agentをインストールする。

$ sudo yum -y install zabbix-server-mysql zabbix-web-mysql zabbix-web-japanese zabbix-agent

MySQLの設定

MySQLクライアントをインストールする。

$ sudo yum -y install mysql mysql-devel

RDSに接続する。

$ sudo mysql -h RDSのエンドポイント -P 3306 -u マスターユーザ名 -p

Zabbixのデータベースを作成する。

> 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-server

Zabbixの設定

ブラウザにて「http://(インスタンスのIPv4 パブリックIP)/zabbix」に接続する。
「Welcome to Zabbix 4.2」表示されたら、「Next step」を押す。
スクリーンショット 2020-08-10 19.22.20.png
以下の画面ですべてOKになってるか、確認する。NGになっている箇所があった場合は表示されるメッセージに従い修正。
私の場合はdata.timezoneでNGになっていたので、zabbix インストール時のTimezone for PHP is not set. Please set "date.timezone" option in php.ini.の記事を参考にして修正した。
全てOKになったら、「Next Step」を押す。
スクリーンショット 2020-08-10 19.25.23.png

「Configure DB connection」にて下記の項目を入力し「Next step」を押す。

Database host : (RDSのエンドポイント)
Password : (zabbixユーザのパスワード)
スクリーンショット 2020-08-10 19.30.05.png

「Zabbix server details」にてデフォルトのままで「Next step」をクリックする。
スクリーンショット 2020-08-10 19.36.02.png
「Pre-installation summary」にて正しい設定値であることを確認し、「Next step」を押す。
スクリーンショット 2020-08-10 19.36.44.png
「Install」にて下記のように表示されたら無事インストールできたので、「Finish」を押す。
スクリーンショット 2020-08-10 19.40.42.png

usernameとpasswordを入力し、ログインする。初期状態で設定されているユーザーは、usernameが「Admin」、Passwordが「zabbix」。
スクリーンショット 2020-08-10 20.50.57.png
ダッシュボードが表示される。
スクリーンショット 2020-08-10 20.52.22.png

「Zabbix agent on Zabbix server is unreachable for 5 minutes」の警告を消すためにzabbix-agentを起動する。

$ sudo systemctl start zabbix-agent
$ sudo systemctl enable zabbix-agent

Zabbixの日本語化設定

右上のユーザーのマークをクリックし、「ユーザープロファイル: Zabbix Administrator」で言語を日本語に変更。「更新」ボタンを押す。
スクリーンショット 2020-08-10 20.58.37.png

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.rpm

zabbix-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-agent

Zabbix(GUI)でZabbix Agentの登録

設定>ホスト>ホストの作成を押す。
スクリーンショット 2020-08-10 21.08.05.png

下記の項目を入力し「テンプレート」を押す。

ホスト名 : (分かりやすい名前を入力)
表示名 : (分かりやすい名前を入力)
グループ : Linux servers
IPアドレス : (Zabbix Agent側のIPアドレスを入力)

スクリーンショット 2020-08-10 21.09.20.png
「新規テンプレートをリンク」で「Template OS Linux」を検索し、2つある追加ボタンのうちの上のボタンをクリック。「テンプレートのリンク」に追加されたら、下の「追加」ボタンを押す。
スクリーンショット 2020-08-10 21.10.20.png
「ホストを追加しました」と表示されたら、監視データ>グラフを押す。
右上の「グラフ」で「CPU jumps」を選択すると、グラフが表示される。
スクリーンショット 2020-08-10 21.37.31.png

注意点のまとめ

・最初にセキュリティグループの基本的な詳細のみ入力して作成し、その後インバウンドルールを追加する。(作成したセキュリティグループを使用できるので、混乱が少なかった。)
・MacとWindowsでssh接続の仕方が違う。
・data.timezoneでNGが出るので、適宜修正する。
・グラフの種類を指定しないと、表示されないので注意。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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関連のいたるところでみる、以下がわかりやすいです。
G Suite アカウントを用いた AWS へのシングルサインオン | AWS Startup ブログ 2020-08-10 14-48-00.png

なお、図では「AWS SSO Endpoint」と表記がありますが、今回の手順では、「AWS SSO」は利用しません。
より詳細なコントロールが必要な際などに、「AWS SSO」を利用することになります(最後のまとめにも記載)。

前置き2

だいたい、このサイトの手順どおりで実現できます。しかし、いくつか現在(2020/8/10)の環境では差異があるため、手順どおりに進めていく上での留意点を補足する形で、以降の手順を記載します。
https://support.google.com/a/answer/6194963?hl=en

Stepごとの補足ポイント

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/GoogleApps

G Suiteのユーザー一覧から、検証したいユーザーのリンクを開きます。
「User Information」を開くと、「Amazon」という項目が見つけられると思います。また、この時点では、Roleは未設定の状態になっているかと思います。
このロールを編集し、Step 2で作成したロールの2つのARNを、カンマ区切りで設定します。

これで、Step 5での検証や、Google Appsから、AWSマネジメントコンソールにログインすることが可能になったかと思われます :)

まとめ

個人的には、ユーザーへのロールの設定が罠になるかと思いますが、それ以外については手順は丁寧で、SAMLやSSOについて、あまり理解していなくても表題の実現が可能かと思いますが、SSOの仕組みを自分で手を動かして動かすことで、理解が深まることもあるかと思います。

今回実施した手段は、AWSアカウントとGoogleアカウントそれぞれで設定しました。
複数のAWSアカウントを扱ったりしていったり、ユーザー数が増えて行ったりと、ビジネスが成長していく上でこの手順では厳しくなってきます。
AWS SSOなどを利用した仕組みを構築する必要があります。

また、そのあたりの記事も書けたらと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】S3で静的ホスティングしてRoute53でルーティングする

はじめに

最近、Route53でドメインを作成してみたので、試しに、S3の静的ホスティングを試してみようと思いました。 

方法

S3のバケットを静的ホスティング設定する

S3バケットの作成

まず、S3のバケットを作成します。

※バケット名は、ドメイン名と同じ名前でないと正しくルーティングされません。

作成時にパブリックアクセスをブロックするか聞かれますが、チェックを外して、ブロックしないように設定してください。

静的ホスティングの設定

作成が完了したら、当該バケットのプロパティページを開いて、Static website hostingを選択します。

image.png

このバケットを使用してウェブサイトをホスティングするを選択します。
そして、今回は「インデックスドキュメント」のみ設定します。
index.htmlと入力して、保存ボタンをクリックします。

バケットポリシーの作成

静的配信を行うために、設定を行う必要があります。
バケットのアクセス権限のタブを開いて下記の設定を行います。
image.png

設定するバケットポリシー
 {
    "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>

オブジェクトをアップしたら、正しく静的配信されているか確認してみましょう。
下記のように表示されていた場合は、正しく設定出来ています!

image.png

Route53でルーティングを設定

AWSマネジメントコンソールでRoute53のページを開き、左のメニューからホストゾーンを選択します。
ルーティングを行いたいドメイン名を選択します。

※ドメインを取得していない、場合は、取得してください。

レコードの作成

レコードの作成をクリックします。

今回は、シンプルルーティングを選択します。
その後、シンプルなレコードを定義をクリックします。

今回は、サブドメインを設定するわけでは無いので、レコード名は空白にします。

値/トラフィックのルーティング先は「S3ウェブサイトエンドポイントへのエイリアス」を選択します。
その次に、リージョンはS3のバケットを作成したリージョンを選択します。
選択できるバケットが存在する場合は、プルダウンに表示されるので、選択します。

レコードタイプは「A - IPv4アドレスと一部のAWSリソースにトラフィックをルーティングします。」を選択します。
今回、ターゲットのヘルスを評価は不要なので、チェックを外します。

レコードの情報を入力する事ができれば、右下のレコードを作成をクリックします。

最後に

設定が完了したら、先ほど登録したレコード名にアクセスしてみましょう!

S3のindex.htmlの内容が表示されると思います!

これで、S3で簡単にウェブページをホスティングする事ができました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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です。

image.png

このままで次の画面進みます。

image.png

これもこのままで次に進みます。

image.png

最終的に以下の画面が表示されます。このページはまだ閉じないでください。

image.png

コンソール画面に戻ります。

戻ったら、成功画面に表示されてる値を元にキーを入力してってください。キーは流出しないように注意して管理すること。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です。

image.png

ついでに、次以降の作業のために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.js
import 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.jsplugins配列で指定します。

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.graphql
type Todo @model {
  id: ID!
  name: String!
  description: String
}

idnamedescriptionというデータが格納できるスキーマのモデルが作られてます。@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

これで以下画面が開きます。何も表示されなかったらリージョンがオハイオとかになってる可能性あるので、右上のメニューから東京にしてください。

image.png

これクリックしてbackendを選択して以下の画面でView in AppSyncを選択します。

image.png

フロントエンドと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>コンポーネントは、いわゆるサインアウトボタンを表示させるだけです。スタイルはドキュメント見た限りカスタマイズできそう。以下はデフォルトの見た目です。英語です。

image.png

サインインすると、子ノードの内容が表示されます。

image.png

認証がこれだけで出来ちゃいます。もちろん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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】Amazon SNSからHTTP/Sで通知する時、エンドポイントでやることを調べた

はじめに

Amazon SNSを使ってHTTP/SでAPIを叩いていきます。
ここでは各AWSサービスの説明は省きます。
AWS超初心者です。

目的

  • Amazon SNSからHTTP/SでAPIを実行する。
  • Amazon SNSからWEBAPIを実行するとき、エンドポイント(受信側)でやることはなにか調べる。 (記事がほとんど見つからなかった)

構成

今回は、APIはAPIGateway + Lambdaで用意。
amazon_sns_image.dio.png

エンドポイント(受信側)でやること

AWSの公式ドキュメントを見たら、やることは大きく分けて3つあるらしい。

  1. 受信準備が完了していることを確認する(エンドポイントで「私はAmazon SNSからの通知を受け取れる状態にありますよ~」ということを確認する)
  2. Amazon SNSからの通知を受け取る
  3. Amazon SNSからサブスクリプション登録解除の情報を受け取る

最初は、ただAmazon SNSから通知先のエンドポイントを設定してメッセージを送信すればよいと思っていましたが
それだけだと上手くいかないようです。

APIの用意

まず、エンドポイントであるAPIを作っていきます。
やりたいことができれば何でもいいのですが、今回は入り口をAPI Gateway、実行される処理をLambdaにしてみました。

API Gatewayに紐づける

こんな感じでLambdaをAPI Gatewayに紐づけてあげましょう。
image008.png

エンドポイントの確認

上記のようにLambdaのコンソールから見れます。
「APIエンドポイント」のURLをコピーしておきましょう。

Amazon SNSでトピックの作成

次にAmazon SNSを用意して、トピックを作成後、通知先のエンドポイントを設定します。

通知先のエンドポイントを設定する

Amazon SNSのコンソール画面から通知先のエンドポイントを設定してみるとこんな画面になります。
image004.png

ステータスが「保留中の確認」となっています。
ステータスを「確認済み」にしないと、トピックからメッセージを発行してもエンドポイントに通知されません。
AWSドキュメント

受信登録が確認されるまで、Amazon SNS はこのエンドポイントに通知を送信しません。

APIをLambdaで実装

pythonで作りました。boto3は使っていません。

lambda_function.py
import 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 を探す必要があります。

  • エンドポイントでやること
  1. 受信準備が完了していることを確認する(エンドポイントで「私はAmazon SNSからの通知を受け取れる状態にありますよ~」ということを確認する)
  2. Amazon SNSからの通知を受け取る
  3. Amazon SNSからサブスクリプション登録解除の情報を受け取る

「1. 受信準備が完了していることを確認する」をやってみる

1は、response = requests.get(subscribeurl)で実現しています。
Amazon SNSのコンソール画面から「リクエストの確認」をします。
image010.png

リロードすると、ステータスが「保留中の確認」から「確認済み」になっていることを確認できます。
image012.png

注意点

さて、ここで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していきましょう。
今回は特にトリガーとなる処理を用意していないので、コンソールから手動でエンドポイントに通知します。

メッセージの発行

image014.png
image016.png

エンドポイントでメッセージを受信したことの確認

image018.png
ちゃんとエンドポイントに設定したLambdaの処理が行われていることを確認できました。

最後に

これでなんとなくAmazon SNSからHTTP/Sで受信するときの動きがわかったような気がします。
また何か発見があったら更新します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSサーバレスで(SPAではなく)画面遷移型のWebアプリをつくる

経緯

AWSサーバレスを採用してWebアプリ(画面)を作ることになりました。コンシューマ(一般ユーザ)向けの画面ではなく、企業向けの管理画面です。

メンバーの皆さんにReactとかを学んでいただく時間的な余裕はなかったため、SPAではなく、メンバーの皆さんに経験のある「画面遷移型」の構成にしました。

ただ、AWSサーバレスで画面遷移型のWebアプリを作る、という事例を見つけることができず、実現方式をあれこれ考える必要がありました。構成が固まるまでに悩んだことや、自分なりの解を記事にすることで、同じようなことに悩まれている方のヒントになればと思ってます。

アーキテクチャ

スクリーンショット 2020-08-08 21.04.25.png

ポイントは以下のとおりです。

  • Lambdaではaws-serverless-expressを採用しました。エンドポイントごとにLambda関数を定義する必要がなくなるとともに、ExpressのノウハウやNPMライブラリを活用できるためです。
  • テンプレートエンジンにはpug.jsを採用しました。Expressのテンプレートエンジンとしてデフォルト採用されているためです。初めて使ってみましたが、簡潔にコーディングできるので使いやすいと感じました。

シーケンス

①ログイン画面の表示

aa.png

こちらについては特筆すべきことはありません。express-sessionなどについては後述します。

②ログイン処理

seq.png

ポイントは以下のとおりです。

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なので、知らなくても問題ないかもしれませんが。

③ログイン後の画面遷移(認証・認可チェック)

seq.png

ポイントは以下のとおりです。

認証チェック

  • ログイン時にセッションに格納しておいたユーザ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.pug
extends ../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.pug
mixin errMsgsOf(propName)
  if errorMappings && errorMappings[propName]
    each error in errorMappings[propName]
      div #{error.msg} 

Expressミドルウェア設定

  • helmetnoCacheを利用して、レスポンスヘッダーを設定します。これにより、XSSなどの対策を行い、セキュリティレベルを高めます。
  • その他、セキュリティについては、Express公式サイトでの解説をしっかり把握しておくのが良いです。

おわりに

新たな知見が得られましたら、今後も更新していきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自動デプロイ後に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 unicorn

Unicornのプロセスを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でサーバをストップするという作業と同じことをしたことになります。

:warning:プロセスが終了できない場合

下記の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を再起動!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amplify + AppSync(graphQL) + DynamoDB でどんな感じでリソース作られるのか見てみる

だいたいこのあたりのシリーズです。

Amplify + AppSync + Cognitoで読み書きの制御を試してみる
https://qiita.com/ikegam1/items/4868b8a2b473e7ec8f85

やる事

amplify側で amplify add api やら amplify pushやらするとして、DynamoDB側のGSIとか制御できるのかどうか気になったので試してみる回です。

目次

  1. 初期設定
  2. スキーマ作成
  3. DynamoDB確認
  4. 参考

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.graphql
type 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 コンソール

appsync.png

ちゃんとスキーマが反映されてる。なるほど、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
  }
}

appsync.png

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を設定しないとなんか怒られた。

  1. DynamoDB側

次にDynamoDBの定義がどんな感じになってるか確認。
ちなみにAppSyncのデータソースのところはこういう感じ。

appsync.png

Postsの項目

dynamo.png

__typename っていうのが作られてる。
creaetdAt とかにはnowな時間が勝手に入ってくれるんですね。

PostsのGSI

dynamo.png

うん。想定通り。 @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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

サーバー構築

  1. AWSマネジメントコンソールよりLightsailを選択
    000.JPG
  2. 日本語を選択
    001.JPG
  3. チュートリアルに従って構築開始
    002.JPG
  4. Windowsを選択
    003.JPG
  5. OSバージョンを選択
    004.JPG
  6. プランを選択(今回は無料プランを選択)
    005.JPG
  7. インスタンス名は任意(タグはなし)
    006.JPG
  8. 起動されるまで待機(保留中)
    007.JPG
  9. 実行中になった後、インスタンスを選択
    008.JPG
  10. ブラウザから接続
    010.JPG
  11. 接続できることを確認
    011.JPG

リモートデスクトップ接続

ブラウザから接続でも問題ないがWindowsのリモートデスクトップ接続を利用する

  1. 画面に表示されているパブリックIPを入力(ユーザー名はAdministrator)し、接続
    015.JPG
  2. Administratorのパスワードを入力

    018.JPG

    • パスワードは以下から取得
      016.JPG 017.JPG
  3. 「はい」を選択

    019.JPG

  4. 接続できることを確認

    020.JPG

※リモートデスクトップ接続が見つからない場合、設定変更が必要

  1. ローカルのコントロールパネルから「システムとセキュリティ」を選択 012.JPG
  2. 「Windows ファイアウォールによるプログラムの許可」を選択 013.JPG
  3. リモートデスクトップを許可し、保存 014.JPG

日本語設定

デフォルトでは英語のままなので、日本語にする

  1. 設定を選択
    027.JPG
  2. 「Time & language」を選択
    028.JPG
  3. 「Region & language」を選択し、「日本語」をデフォルトに設定
    029.JPG
  4. オプションを選択
    033.JPG
  5. 日本語パックをダウンロード
    034.JPG
  6. 日本語配列のキーボードに変更
    035.JPG
  7. 再起動する
    030.JPG
  8. 再起動後、日本語になっていることを確認
    036.JPG

MT4のインストール

デフォルトではIEでのファイルダウンロードが制限されている

021.JPG

  1. サーバーマネージャーを起動
    038.JPG
  2. 「ローカルサーバー」から 「IE セキュリティ強化の構成」を選択
    039.JPG
  3. オフにして保存
    040.JPG
  4. 時間を置いて 「IE セキュリティ強化の構成」が無効になっていることを確認
    041.JPG
  5. MT4のインストーラーをダウンロードし、実行
    055.JPG
  6. 取引で利用するサーバーを選択(ここではデモサーバーを選択) 025.JPG
  7. 口座情報を入力(ここでは既存デモ口座情報を利用)
    026.JPG
  8. チャートが表示されることを確認 042.JPG

インジゲータの設定

  1. ナビゲーションウィンドウからインジゲータをチャートにドラッグ&ドロップ
    043.JPG
  2. DLLのインポートが許可されていることを確認し、適用
    050.JPG
  3. インジゲータが適用されていることを確認
    044.JPG

EAの設定

  1. オプションを選択
    051.JPG
  2. EAでの自動売買とDLLの利用を許可
    052.JPG
  3. ナビゲーションウィンドウからEAをチャートにドラッグ&ドロップ
    045.JPG
  4. EAによる取引、DLLおよび外部EAの利用を許可
    046.JPG
  5. チャート右上にEA名と顔マークが表示されていることを確認
    047.JPG
  6. 自動売買を開始 048.JPG
  7. 自動売買が緑になり、顔マークが笑顔になることを確認
    056.JPG
    053.JPG
  8. 一度に複数のEAを動かすときはマジックナンバーをずらすこと 054.JPG

感想

  • EC2より楽に作れる
  • 無料プランでWindowsはマシンスペック的にギリギリ
    • 過去チャートを使ってEAを試すストラテジーテスターは高負荷で使えない
  • コスト削減を厳密に考慮する場合はEC2で作った方がよさそう
    • 期間限定サーバーやお試しサーバーならLightsailがよさそう
  • インスタンスの世代が最新じゃないのはちょっと残念
  • VPSのことを調べるときにSLAの話がほとんど出てこなかった
    • あんまり重要ではないのか?
  • Lightsailは他のVPSと比較すると敷居が高いっぽい
    • 普及してない?
    • ターゲットはあくまでエンジニア?
  • いつかは自動売買で利用料が払えるようになると嬉しい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む