20210220のAWSに関する記事は25件です。

AWS SAM を使って、Lambda のローカル実行環境を試してみる

はじめに

Lambda を開発するときに、どういった環境を使っているでしょうか。AWS マネージドコンソールには、オンラインエディターがあり、ブラウザ上でプログラミングができます。しかし、本格的に開発するときには、多機能で使い慣れた IDE が使いたくなります。手元のノートPC上にインストールしている IDE を使って開発するときに、動作確認のたびに、いちいちプログラミング途中のソースコードをパッケージングして Lambda へアップロードするのは面倒です。出来るだけローカルで完結させて、素早く動作確認と修正を行いたい気持ちになります。

こういう時に便利に使えるのが、SAM (Serverless Application Model) です。SAM は、Lambda を開発するときに便利に使えるオープンソースフレームワークで、次の特徴があります。

  • Lambda ライクなローカル環境が使えて、手元で動作確認が出来る
  • Lambda Function と、それに付随する API Gateway などの定義して、デプロイ出来る

今回は、SAM の Quick Start をやってみたので、備忘録的にメモを残しておきます。Quick Start は、このドキュメントに書かれているものを参考にしています。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install-linux.html

SAM CLI を使って、Lambda Function を更新

ソースコードで、Hello World 2 に変更後、sam deploy を再度行うと、AWS 上で構成された、Lambda Function が更新される

Bash 使っているとき

sam build && sam deploy

Fish 使っているとき

sam build; and sam deploy

実行

> curl https://hdz2hgwco1.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
{"message": "hello world2"}

前提条件

  • Ubuntu 環境 (WSL2)
  • SAM CLI インストール済み
  • AWS CLI インストール済み
  • Docker インストール済み
  • Homebrew on Linux インストール済み

memo : Docker Install

次の URL を参考に適当にインストールします

https://docs.docker.com/engine/install/ubuntu/

Docker 起動

sudo service docker start

memo : Homebrew Install

Homebrew を Ubuntu に適当にインストールします
https://docs.brew.sh/Homebrew-on-Linux

fish の場合

vim ~/.config/fish/config.fish

末尾に追記

set -x HOMEBREW_PREFIX /home/linuxbrew/.linuxbrew
set -x HOMEBREW_CELLAR /home/linuxbrew/.linuxbrew/Cellar
set -x HOMEBREW_REPOSITORY /home/linuxbrew/.linuxbrew/Homebrew
set -x PATH /home/linuxbrew/.linuxbrew/bin $PATH
set -x PATH /home/linuxbrew/.linuxbrew/sbin $PATH
set -x MANPATH /home/linuxbrew/.linuxbrew/share/man $MANPATH
set -x INFOPATH /home/linuxbrew/.linuxbrew/share/info $INFOPATH

SAM Init

SAM の Document を参考に、Quick Start を進めていきます。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html

作業用 dir 作成

mkdir ~/samdir
cd ~/samdir

sam init で初期設定

sam init

1の AWS Quick Start Templates を選択

> sam init

        SAM CLI now collects telemetry to better understand customer needs.

        You can OPT OUT and disable telemetry collection by setting the
        environment variable SAM_CLI_TELEMETRY=0 in your shell.
        Thanks for your help!

        Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html

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

1 の Zip を選択

What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

10 の Python を選択

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

Project Name は、hello-world

Project name [sam-app]: hello-world

1の Hello World Example を選択

AWS quick start application templates:
        1 - Hello World Example
        2 - EventBridge Hello World
        3 - EventBridge App from scratch (100+ Event Schemas)
        4 - Step Functions Sample App (Stock Trader)
Template selection: 1

こんな感じで自動生成されている

> tree hello-world
hello-world
├── README.md
├── __init__.py
├── events
│   └── event.json
├── hello_world
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── template.yaml
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_handler.py

4 directories, 10 files

template.yaml の中身はこんな感じ

> cat hello-world/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  hello-world

  Sample SAM Template for hello-world

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.6
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

SAM Build

init で生成した環境を Build します

cd hello-world/
sam build

実行例

> sam build
Building codeuri: hello_world/ runtime: python3.6 metadata: {} functions: ['HelloWorldFunction']
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

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

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

SAM Deploy

Build が正常にできたので、SAM をつかって Deploy

sam deploy --guided

CloudFormation の Stack 名入力 : sam-hello-world-stack

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: sam-hello-world-stack

Region このまま Enter

        AWS Region [ap-northeast-1]:

変更点を確認するために Yy

        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]:y

必要な IAM Role を自動作成 y

        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]:y

Public に公開する y

        HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y

Config の Save で y

        Save arguments to configuration file [Y/n]: y

このまま Enter

        SAM configuration file [samconfig.toml]:

Default

        SAM configuration environment [default]:

CloudFormation で Stack が作成されている。これは、SAM のメタデータ管理のために、S3 バケットが作成されている

image-20210220173809555.png

SAM を経由して、CloudFormation の ChangeSet(変更点) が表示されます

Initiating deployment
=====================
HelloWorldFunction may not have authorization defined.
Uploading to sam-hello-world-stack/1b9a60561935a1e52fff89b5860c516a.template  1111 / 1111  (100.00%)

Waiting for changeset to be created..

CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                     LogicalResourceId                             ResourceType                                  Replacement
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                         HelloWorldFunctionHelloWorldPermissionProd    AWS::Lambda::Permission                       N/A
+ Add                                         HelloWorldFunctionRole                        AWS::IAM::Role                                N/A
+ Add                                         HelloWorldFunction                            AWS::Lambda::Function                         N/A
+ Add                                         ServerlessRestApiDeployment47fc2d5f9d         AWS::ApiGateway::Deployment                   N/A
+ Add                                         ServerlessRestApiProdStage                    AWS::ApiGateway::Stage                        N/A
+ Add                                         ServerlessRestApi                             AWS::ApiGateway::RestApi                      N/A
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:539926008561:changeSet/samcli-deploy1613810205/c6116475-2528-40ab-8a50-0555891081b0

これで問題ないことを確認し、y

Deploy this changeset? [y/N]: y

CloudFormation 上で、Resource 群が作成されている

image-20210220174037338.png

作成完了

Successfully created/updated stack - sam-hello-world-stack in ap-northeast-1

image-20210220174138660.png

Lambda Function や、API Gateway などが作成されている

image-20210220174204731.png

CloudFormation Stack の Output には、API Gateway の Endpoint が出力されています

https://hdz2hgwco1.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/

image-20210220192910787.png

実行すると、Hello World が返ってきます

> curl https://hdz2hgwco1.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
{"message": "hello world"}

API Gateway のページでも、Endpoint が確認可能

image-20210220211832062.png

SAM Local Test

SAM CLI には、Docker を使って Lambda のローカル実行環境をシミュレートする機能があります。次の2つのローカル実行方法があります。

  • ローカルAPI を生成して実行
  • 直接実行

ローカルAPIを生成

ローカルで、API 実行エンドポイントを生成

sam local start-api

実行例。プロンプトは返ってこない

> sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-02-20 21:58:34  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

別の Shell から実行

curl http://127.0.0.1:3000/hello

返ってこないプロンプト上で Build が走る

Invoking app.lambda_handler (python3.6)
Image was not found.
Building image................................................................................................................................................................................................................................................................................................................

Build が完了すると、レスポンスが返ってくる

> curl http://127.0.0.1:3000/hello
{"message": "hello world"}⏎

直接実行

sam local invoke "HelloWorldFunction" -e events/event.json

実行例

> sam local invoke "HelloWorldFunction" -e events/event.json
Invoking app.lambda_handler (python3.6)
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-python3.6:rapid-1.18.2.

Mounting /home/sugi/samdir/hello-world/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: 45d0fd3b-1f3a-4dbf-8cfc-f15ca9fa6153 Version: $LATEST
END RequestId: 45d0fd3b-1f3a-4dbf-8cfc-f15ca9fa6153
REPORT RequestId: 45d0fd3b-1f3a-4dbf-8cfc-f15ca9fa6153  Init Duration: 0.16 ms  Duration: 112.95 ms     Billed Duration: 200 ms Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}⏎

ローカルで新しいプログラムの動作確認

手元のノートPC 上で、プログラミングをしている途中で、すぐに動作確認したいときは、Build をしたあとに、ローカルで実行するコマンドを続けて打てばよいです

Bash 使っているとき

sam build && sam local invoke

Fish 使っているとき

sam build; and sam local invoke

IDE で開発しているときの動作確認

手元のノートPC 上で、プログラミングをしている途中で、すぐに動作確認したいときは、Build をしたあとに、ローカルで実行するコマンドを続けて打てばよいです

Bash 使っているとき

sam build && sam local invoke

Fish 使っているとき

sam build; and sam local invoke

参考URL

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-sam.html

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

DockerプロジェクトをAWSのECRにプッシュしようとしたらエラーが起きたときに話

DockerのプロジェクトをAWSのECRにプッシュするときに以下のようなエラーが起きました。

An error occurred (InvalidSignatureException) when calling the GetAuthorizationToken operation:
The request signature we calculated does not match the signatAn error occurred (InvalidSignatureException) when calling the GetAuthorizationToken operation: 
The request signature we calculated does not match the signature you provided. Check your AWS Sontails.
Token operation: The request signature we calculated does not match the signatAn error occurred (InvalidSignatureException) when calling the GetAuthorizationToken operation:
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

エラー内容の通り認証が通ってないのかなと思い、IAMユーザーでアクセスキー、シークレットキーを再発行します。

コンソール画面の右上のユーザーの名前をクリックし「マイセキュリティ資格情報」をクリック。

「アクセスキー」の中の「新しいアクセスキーの作成」をクリックしたらエラーが吐かれました。

どうやらデフォルトではアクセスキーを作成する権限が無いらしい。

※ルートユーザーの方はアクセスキーの発行が可能です。

なので、アクセス権限を追加してあげましょう!

  1. サービスの欄に「IAM」と入力
  2. 権限を追加したいユーザーを選択
  3. 「アクセス権限の追加」をクリック
  4. 「既存のポリシーを直接アタッチ」を選択
  5. 「IAMFullAccess」と入力し、追加する

これでIAMユーザーにアクセスキーを作成する権限が付与されました。

なので、「マイセキュリティ資格情報」からアクセスキーを作成してください。

すると、アクセスキーとシークレットキーが表示されます。

※注意 シークレットキーはこの時にしか見れないので、画面は閉じないで下さい。もし、閉じてしまった場合はもう一度キーを作成してください。

そしたら以下のコマンドを入力してください。

aws configure

そして、今発行したアクセスキーとシークレットキを入力してください。

AWSにDockerプロジェクトをプッシュするのめちゃムズイ。。。

以上、「DockerプロジェクトをAWSのECRにプッシュしようとしたらエラーが起きたときに話」でした!

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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

【Rails6】AWS EC2でデプロイする方法

概要

AWS EC2で、デプロイする方法について述べています。

はじめに

AWS EC2を導入後、いまいちデプロイの仕方が掴めないという方向けの記事となっています。

私も導入したばかりで、デプロイの手順の備忘録として載せています。

前提

AWS EC2を導入済み

バージョン

rubyのバージョン ruby-2.6.5
Railsのバージョン Rails:6.0.0

実行の大まかな流れ

①【GitHub】ローカルでのコードの変更を、masterにpushする

②【ローカル】ターミナルでEC2にログイン

③【EC2】ターミナルで、ps aux | grep unicorn を実行

④【EC2】ターミナルで、kill プロセス番号 を実行

⑤【ローカル】ターミナルで、bundle exec cap production deploy を実行

実行方法

①【GitHub】ローカルでのコードの変更を、masterにpushする

GitHubでコードの変更内容をマージしてください。

②【ローカル】ターミナルでEC2にログイン

以下の手順を、ローカル内のターミナルで実行してください。

$ cd

$ cd .ssh/

$ ssh -i ダウンロードした鍵の名前.pem ec2-user@作成したEC2インスタンスと紐付けたElastic IP

※ダウンロードした鍵の名前:AWS公式ページにて自身が設定したキーペア名のこと

※作成したEC2インスタンスと紐付けたElastic IP:AWS公式ページで記載している個人に割り振られているElastic IPアドレスのこと

※EC2にログイン完了

cd  /var/www/開発中のアプリケーション

③【EC2】ターミナルで、ps aux | grep unicornを実行

以下の手順を、EC2内のターミナルで実行してください。

$ ps aux | grep unicorn

④【EC2】ターミナルで、kill プロセス番号を実行

以下の手順を、EC2内のターミナルで実行してください。

$ kill Unicornのプロセス本体のid

※Unicornのプロセス本体のid:unicorn_rails masterのプロセスidのこと

⑤【ローカル】ターミナルで、bundle exec cap production deployを実行

以下の手順を、ローカル内のターミナルで実行してください。

$ bundle exec cap production deploy

補足説明

AWS EC2とは?

Amazonが提供している仮想サーバー構築サービスです。

②【ローカル】ターミナルでEC2にログイン

ホームディレクトリに移動します。

$ cd


「.ssh」という隠しディレクトリに移動します。
ユーザーに見せる必要がないファイルなので隠しディレクトリに置いています。

$ cd .ssh/

ssh接続を実行して、EC2に接続しています。

$ ssh -i ダウンロードした鍵の名前.pem ec2-user@作成したEC2インスタンスと紐付けたElastic IP


開発中のアプリケーションの格納場所まで移動しています。
※人によってディレクトリの名称は異なります。

cd  /var/www/開発中のアプリケーション

③【EC2】ターミナルで、ps aux | grep unicornを実行

unicornのプロセスを確認しています。

・「プロセス」とは、PC上で動くすべてのプログラムの実行時の単位です。

・「psコマンド」とは、現在動いているプロセスを確認するためのコマンドです。

・「unicorn_rails master」と表示されているプロセスがUnicornのプロセス本体です。

$ ps aux | grep unicorn

④【EC2】ターミナルで、kill プロセス番号を実行

killコマンドは、現在動いているプロセスを停止させるためのコマンドです。

・UnicornのプロセスをKillしています。

・killする理由としてはすでにunicornのサーバーが立ち上がっている状態で自動デプロイをすると、二重でサーバーを立ち上げることになるためです。(自動デプロイもサーバーを立ち上げる役割を担っています)

$ kill Unicornのプロセス本体のid

⑤【ローカル】ターミナルで、bundle exec cap production deployを実行

・自動デプロイのコマンドです。

・EC2を導入するまでで、ローカルのターミナルからコマンド一発でデプロイできるようになっております。

$ bundle exec cap production deploy

間違いやすいポイント(主観)

⑤の、bundle exec cap production deployを実行を誤って、EC2内で行ってしまうことがありました。
EC2内で実行すると以下のエラーが起こります。

bundler: command not found: cap
Install missing gem executables with `bundle install`

意味を理解しながらと言えど、連続して行う作業なので、間違いがちかなって思います(恥)

以上です。

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

【Rails6】AWS EC2でデプロイする手順

概要

AWS EC2で、デプロイする方法について述べています。

はじめに

AWS EC2を導入後、いまいちデプロイの仕方が掴めないという方向けの記事となっています。

私も導入したばかりで、デプロイの手順の備忘録として載せています。

前提

AWS EC2を導入済み

バージョン

rubyのバージョン ruby-2.6.5
Railsのバージョン Rails:6.0.0

実行の大まかな流れ

①【GitHub】ローカルでのコードの変更を、masterにpushする

②【ローカル】ターミナルでEC2にログイン

③【EC2】ターミナルで、ps aux | grep unicorn を実行

④【EC2】ターミナルで、kill プロセス番号 を実行

⑤【ローカル】ターミナルで、bundle exec cap production deploy を実行

実行方法

①【GitHub】ローカルでのコードの変更を、masterにpushする

GitHubでコードの変更内容をマージしてください。

②【ローカル】ターミナルでEC2にログイン

以下の手順を、ローカル内のターミナルで実行してください。

$ cd

$ cd .ssh/

$ ssh -i ダウンロードした鍵の名前.pem ec2-user@作成したEC2インスタンスと紐付けたElastic IP

※ダウンロードした鍵の名前:AWS公式ページにて自身が設定したキーペア名のこと

※作成したEC2インスタンスと紐付けたElastic IP:AWS公式ページで記載している個人に割り振られているElastic IPアドレスのこと

※EC2にログイン完了

cd  /var/www/開発中のアプリケーション

③【EC2】ターミナルで、ps aux | grep unicornを実行

以下の手順を、EC2内のターミナルで実行してください。

$ ps aux | grep unicorn

④【EC2】ターミナルで、kill プロセス番号を実行

以下の手順を、EC2内のターミナルで実行してください。

$ kill Unicornのプロセス本体のid

※Unicornのプロセス本体のid:unicorn_rails masterのプロセスidのこと

⑤【ローカル】ターミナルで、bundle exec cap production deployを実行

以下の手順を、ローカル内のターミナルで実行してください。

$ bundle exec cap production deploy

補足説明

AWS EC2とは?

Amazonが提供している仮想サーバー構築サービスです。

②【ローカル】ターミナルでEC2にログイン

ホームディレクトリに移動します。

$ cd


「.ssh」という隠しディレクトリに移動します。
ユーザーに見せる必要がないファイルなので隠しディレクトリに置いています。

$ cd .ssh/

ssh接続を実行して、EC2に接続しています。

$ ssh -i ダウンロードした鍵の名前.pem ec2-user@作成したEC2インスタンスと紐付けたElastic IP


開発中のアプリケーションの格納場所まで移動しています。
※人によってディレクトリの名称は異なります。

cd  /var/www/開発中のアプリケーション

③【EC2】ターミナルで、ps aux | grep unicornを実行

unicornのプロセスを確認しています。

・「プロセス」とは、PC上で動くすべてのプログラムの実行時の単位です。

・「psコマンド」とは、現在動いているプロセスを確認するためのコマンドです。

・「unicorn_rails master」と表示されているプロセスがUnicornのプロセス本体です。

$ ps aux | grep unicorn

④【EC2】ターミナルで、kill プロセス番号を実行

killコマンドは、現在動いているプロセスを停止させるためのコマンドです。

・UnicornのプロセスをKillしています。

・killする理由としてはすでにunicornのサーバーが立ち上がっている状態で自動デプロイをすると、二重でサーバーを立ち上げることになるためです。(自動デプロイもサーバーを立ち上げる役割を担っています)

$ kill Unicornのプロセス本体のid

⑤【ローカル】ターミナルで、bundle exec cap production deployを実行

・自動デプロイのコマンドです。

・EC2を導入するまでで、ローカルのターミナルからコマンド一発でデプロイできるようになっております。

$ bundle exec cap production deploy

間違いやすいポイント(主観)

⑤の、bundle exec cap production deployを実行を誤って、EC2内で行ってしまうことがありました。
EC2内で実行すると以下のエラーが起こります。

bundler: command not found: cap
Install missing gem executables with `bundle install`

意味を理解しながらと言えど、連続して行う作業なので、間違いがちかなって思います(恥)

以上です。

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

クライアントがアプリの一部機能にドメインを割り当てる際に自動でSSL化する方法

概要

アプリケーションの一部機能、カート機能や予約機能をクライアントのページで作成する時に、ドメインを指定したいという要望に対してHTTPS接続なので、ACM証明書をこちらで追加する必要があります。今回は、その部分を自動化を行います。

使用技術

・AWS ACM
 https://aws.amazon.com/jp/certificate-manager/
・AWS SDK for PHP
 https://docs.aws.amazon.com/aws-sdk-php/v3/api/index.html

作業手順

  1. 特定の機能のURLにクライアント固有のidなど含めます。
  2. クライアントが、【使用したいドメイン】をフォームに入力を行う。(AWS SDKを使用し入力されたドメインをACMに追加 レスポンスに含まれる「エントリー名」「値」を表示)
  3. クライアントが、DNSサービスプロバイダで作成した【使用したいドメイン】にCNAMEで「1.のURL」 「エントリー名」 「値」を登録

上記の3ステップでクライアント側の操作のみで、SSL化されたドメインを割り当てれるようになります。

AWS SDK for PHPを使用しACM登録処理,「エントリー名」 「値」を確認する

参考コード(for_laravel)

use App\Certification;
use Aws\Acm\AcmClient;

public function createACM(Request $request)
{
    $client = new AcmClient([
                    'version'     => 'latest',
                    'region'      => 'ap-northeast-2',
                    'credentials' => [
                    'key'    => config('aws.key'),
                    'secret' => config('aws.secret_key'),
                ],
            ]);

  //ACM登録
  $result = $client->requestCertificate([
            'DomainName' => $request->url;,
            'Tags' => [
                [
                     'Key' => '<string>', // REQUIRED
                     'Value' => '<string>',
                ],
            ],
            'ValidationMethod' => 'DNS',
        ]);

   //こちらで、ACMの情報を呼び出します。
   $resource_name = $result['CertificateArn'],

  $results = $client->describeCertificate([
       'CertificateArn' => $resource_name
  ]);

    $エントリー名 = $results['Certificate']['DomainValidationOptions'][0]["ResourceRecord"]['Name'];
    $値        = $results['Certificate']['DomainValidationOptions'][0]["ResourceRecord"]['Value'];
}

参考URL
https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-acm-2015-12-08.html#requestcertificate

あとがき

半年ほど前に実装した機能で、上手く説明出来なかったのですが記事にして理解できました。
クラウドフロントのキャッシュ削除なんかも下記サイトをみて実装しました。めっちゃ便利です!
・AWS SDK for PHP
 https://docs.aws.amazon.com/aws-sdk-php/v3/api/index.html

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

AWSクラウドプラクティショナー試験

この記事について

・2カ月かけてAWSクラウドプラクティショナーに合格することができましのたので
 備忘録的なメモです。

購入したもの

参考書
・まずは参考書を購入しました。
 「AWS認定資格試験テキスト AWS認定 クラウドプラクティショナー」

Udemy
・安くなっているときを見計らって、これを購入しました(1,500円くらいだったと思います)。
 「この問題だけで合格可能!AWS 認定クラウドプラクティショナー 模擬試験問題集(7回分455問)」
  基礎問題2回+応用問題3回+難易度高問題2回という問題設定になっています。

勉強の仕方

・まずは参考書を1回読む
 参考書は全体的にまとまっていてよい本なのですが、これだけで合格は難しいと思います。
 自分が購入したのは第9刷でしたが、実際のテストには新しい要素が多々含まれていました。
 たとえば、Glacier Deep Archive や AWS Glue などはテストの選択項目にありました。

・Udemyでとにかく問題を解く
 基礎問題と応用問題のレベル差が大きすぎで、最初は50点も取れずに落ち込みましたが、
 5回くらいやることで何とか9割くらいは取れるようになりました。
 (難易度高問題には手を付けませんでした)
 説明読んでも分からないところは、Webで情報収集しました。
 特にクラスメソッドさんのページ には、かなりお世話になりました。
 面白い記事がたくさんあるので、読みすぎると私のようにテスト受けるまで時間がかかるので注意。

・直前は参考書の見直し
 Udemyを繰り返すことで問題覚えてしまうこともあるので、ここでもう一度参考書に戻って見直し。お
 テスト直前になって、「なるほど!」と思うようなところもありました。

試験申し込み&試験

・試験の数日前に、突然「Confirmation of Cancelled Pearson VUE Exam Appointment」 のメールが届いて
 驚きました。
 ピアソンに電話して確認すると、申込み時に登録したカードでの引き落としに失敗したとのこと。
 かなり焦ったのですが、別のカードに変更することで事なきを得ました
 (ピアソンのお姉さん、ありがとうございました!!!)

 後にカード会社に確認したところ、有効期限がまちがっていたたとのこと。
 Web画面でOKでたのになぜ?と、釈然としない部分もありますが、まあいいでしょう

・土曜午前に受験しましたが、かなり混んでいました。
 日本語の問題の分かりづらさは仕方ないですね。
 念のためすべての問題を英語で確認しながら進めたので、40分くらいかかりました。

結果

 終了して、アンケートに回答すると、合格の文字が表示されました。
 家に帰って確認したところ869点だったので、まずまずの出来だったと思います。
 
 ちなみに、今日は朝からAWSで大きい障害が発生していたそうです(まったく知らなかった!)

今後について

 せっかくなので、AWSソリューションアーキテクトの取得を目指そうかと思います。
 
 これから試験受ける方、頑張ってください!!
 

 
 

 
 
 
 

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

Windows10からAWS ECRにDockerイメージをプッシュする

Windows10からAWS ECRにDockerイメージをプッシュします。

環境

Windows10 Pro
Docker for Windows 20.10.2
aws cli 2.1.10

docker --version
Docker version 20.10.2, build 2291f61

aws --version
aws-cli/2.1.10 Python/3.7.3 Linux/4.19.121-linuxkit docker/x86_64.amzn.2 prompt/off

aws cliはDockerで実行するタイプです。

レポジトリを作成します。

image.png

「リポジトリ名」を入力し、「リポジトリを作成」をクリックします。
リポジトリ名=イメージ名です。

image.png

リポジトリが作成されました。

image.png

Dockerイメージをプッシュします。

image.png

リポジトリを選択し、「プッシュコマンドの表示」をクリックします。
OS別にプッシュコマンドが表示されます。Windowsでもaws cliコマンドが使える場合、「macOS/Linux」の方法でプッシュできます。
Windowsの方は初回でエラーになったのでそれ以降試していませんが...

image.png

PS C:\> aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
PS C:\> docker tag my_work_php:latest 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php:latest
PS C:\> docker push 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php:latest
The push refers to repository [013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php]
a1b1f93dda6e: Pushed
a908950ceaa5: Pushed
c237d48abef2: Pushed
8235c11a20e4: Pushed
17c2436afe5e: Pushed
a6f79b9f8213: Pushed
e20f9867e86d: Pushed
7831f8733bf5: Pushed
9c468bc69dbf: Pushed
5f29a23c8e9c: Pushed
0e754215b5eb: Pushed
6c0111550081: Pushed
02c59e64a328: Pushed
8888f89886ba: Pushed
f182865e86e9: Pushed
b2bb5e569df9: Pushed
d0fe97fa8b8c: Pushed
latest: digest: sha256:89fbf2f0ed4ab79debbbeed6bd5bda55eb24e635784efc1987cf3dd1fe934310 size: 3879

*)ここではイメージの作成はスキップしています。

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

Windows10からAWS ECSにイメージをプッシュする

Windows10からAWS ECSにDockerイメージをプッシュします。

環境

Windows10 Pro
Docker for Windows 20.10.2
aws cli

docker --version
Docker version 20.10.2, build 2291f61

aws --version
aws-cli/2.1.10 Python/3.7.3 Linux/4.19.121-linuxkit docker/x86_64.amzn.2 prompt/off

aws cliはDockerで実行するタイプです。

レポジトリを作成します。

image.png

「リポジトリ名」を入力し、「リポジトリを作成」をクリックします。
リポジトリ名=イメージ名です。

image.png

リポジトリが作成されました。

image.png

Dockerイメージをプッシュします。

image.png

リポジトリを選択し、「プッシュコマンドの表示」をクリックします。
OS別にプッシュコマンドが表示されます。Windowsでもaws cliコマンドが使える場合、「macOS/Linux」の方法でプッシュできます。
Windowsの方は初回でエラーになったのでそれ以降試していませんが...

image.png

PS C:\> aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
PS C:\> docker tag my_work_php:latest 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php:latest
PS C:\> docker push 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php:latest
The push refers to repository [013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php]
a1b1f93dda6e: Pushed
a908950ceaa5: Pushed
c237d48abef2: Pushed
8235c11a20e4: Pushed
17c2436afe5e: Pushed
a6f79b9f8213: Pushed
e20f9867e86d: Pushed
7831f8733bf5: Pushed
9c468bc69dbf: Pushed
5f29a23c8e9c: Pushed
0e754215b5eb: Pushed
6c0111550081: Pushed
02c59e64a328: Pushed
8888f89886ba: Pushed
f182865e86e9: Pushed
b2bb5e569df9: Pushed
d0fe97fa8b8c: Pushed
latest: digest: sha256:89fbf2f0ed4ab79debbbeed6bd5bda55eb24e635784efc1987cf3dd1fe934310 size: 3879
  • イメージ作成はスキップしています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10からAWS ECRにイメージをプッシュする

Windows10からAWS ECRにDockerイメージをプッシュします。

環境

Windows10 Pro
Docker for Windows 20.10.2
aws cli

docker --version
Docker version 20.10.2, build 2291f61

aws --version
aws-cli/2.1.10 Python/3.7.3 Linux/4.19.121-linuxkit docker/x86_64.amzn.2 prompt/off

aws cliはDockerで実行するタイプです。

レポジトリを作成します。

image.png

「リポジトリ名」を入力し、「リポジトリを作成」をクリックします。
リポジトリ名=イメージ名です。

image.png

リポジトリが作成されました。

image.png

Dockerイメージをプッシュします。

image.png

リポジトリを選択し、「プッシュコマンドの表示」をクリックします。
OS別にプッシュコマンドが表示されます。Windowsでもaws cliコマンドが使える場合、「macOS/Linux」の方法でプッシュできます。
Windowsの方は初回でエラーになったのでそれ以降試していませんが...

image.png

PS C:\> aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
PS C:\> docker tag my_work_php:latest 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php:latest
PS C:\> docker push 013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php:latest
The push refers to repository [013311312082.dkr.ecr.ap-northeast-1.amazonaws.com/my_work_php]
a1b1f93dda6e: Pushed
a908950ceaa5: Pushed
c237d48abef2: Pushed
8235c11a20e4: Pushed
17c2436afe5e: Pushed
a6f79b9f8213: Pushed
e20f9867e86d: Pushed
7831f8733bf5: Pushed
9c468bc69dbf: Pushed
5f29a23c8e9c: Pushed
0e754215b5eb: Pushed
6c0111550081: Pushed
02c59e64a328: Pushed
8888f89886ba: Pushed
f182865e86e9: Pushed
b2bb5e569df9: Pushed
d0fe97fa8b8c: Pushed
latest: digest: sha256:89fbf2f0ed4ab79debbbeed6bd5bda55eb24e635784efc1987cf3dd1fe934310 size: 3879
  • イメージ作成はスキップしています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS AMIの作成

AMI(Amazon Machine Image)とは?

「AMIは、仮想サーバーであるインスタンスの起動に必要な情報を保存している」

AMIの情報に含まれるもの
・インスタンのOS
・アーキテクチャ
・仮想化方式
・EBSスナップショット
・ブロックデバイスのマッピング 等

AMI作成

①インスタンス選択
②AMIを作成するインスタンス選択
③アクションクリック
④イメージとテンプレートクリック
⑤イメージ作成クリック

スクリーンショット 2021-02-20 14.50.27.png

⑥項目を入力し作成する

スクリーンショット 2021-02-20 14.51.44.png

作成したAMIで起動する場合

①AMI選択
②起動クリック
スクリーンショット 2021-02-20 14.56.26.png

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

[AWSxAmplifyxCognito] Amplifyでグローバルサインアウトがグローバルサインアウトしてくれない問題を解決する

はじめに

こんにちは!
認証機能を実装する場合、別のデバイスでサインアウトしたらユーザーがログインしている他の全てのデバイスでもログアウトして欲しいときがありますよね?

Amplifyではそんな要望に応えるために、singOut関数にglobalオプションがあり、signOut({global:true})のようにするとサインアウトした際に、ユーザーが持つ全トークンが無効化され、全てのデバイスからサインアウトできます。
以下ドキュメントから抜粋
image.png

と、いうのがドキュメントには書かれており、実際ユーザーが認証後に取得する3種類のトークンのうち、アクセストークンとリフレッシュトークンは即座に無効になるのですが、
実は、signOut({global:true})をしてもIDトークンは無効にならないんです!
加えて、セッションを確認するための関数たち(currentSession,userSession)はCognitoにアクセストークンやリフレッシュトークンが有効なのか問い合わせにいったりはせず、ローカルストレージにあるIdトークンの有効期限を確認するだけなんです。

つまり、currentSession,userSession関数では、1つのデバイスでグローバルサインアウトが行われていても、他のデバイスではIdトークンが有効期限切れにならない限り、ユーザーは"セッション切れ"とは判断されないんです。

対応策: 実行時にアクセストークンを使う関数を利用して、セッション切れを判断する

currentSession,userSession関数では、アクセストークン、リフレッシュトークンが有効かどうか見てくれない。
なら、それらが有効かどうかを問い合わせる関数を使えばいいのですが、その確認のためだけの関数がAmplifyリファレンスに見当たらないんです。

なので、実行時にアクセストークンを使う関数を利用して、その有効・無効を判断します。
例えば、currentUserInfo関数がそれにあたります。

この関数は,

  • サインイン状態のとき、以下のようなusername,attributes(cognitoで設定したユーザーの属性)を含むオブジェクトが返ります。
{
    "UserAttributes": [
        {
            "Name": "sub",
            "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        },
        {
            "Name": "email_verified",
            "Value": "true"
        },
        {
            "Name": "email",
            "Value": "xxxxxxxxx@xxxxxxxxx.com"
        }
    ],
    "Username": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
}
  • そのデバイスでサインアウトして、デバイスにトークンがないときはnull
  • 別のデバイスでサインアウトして、そのデバイスに各種トークンはあるが、アクセストークンが無効なときは、空オブジェクト{}が返ります

したがって、返り値が null または 空オブジェクト のときユーザーはサインアウト状態であるといえます。

以下はサインイン画面での実装例です。

サインイン画面側の実装例

サインインしてる時のみ、処理を行いたいので
サインアウト状態(null or 空オブジェクト)の否定である nullでない かつ 空オブジェクトでない でサインイン状態かどうかを見ています。(空オブジェクトでない はプロパティがundefinedかどうかで見ています。)

const getCurrentUserInfo = async () => {
    const currentUserInfo = await Auth.currentUserInfo();
    return currentUserInfo;
}
getCurrentUserInfo()
    .then(currentUserInfo =>{
        //以下の場合はサインアウト状態
        //1.currentUserInfo === null:そのデバイスでサインアウトして、デバイスにトークンがないとき
        //2.currentUserInfo.username(などユーザーに関するプロパティ) === undefined: 別のデバイスでサインアウトして、そのデバイスにトークンはあるが、アクセストークンが無効なとき
        if(currentUserInfo !== null && currentUserInfo.username !== undefined){
            //サインインしているときにしたい処理を書く。ログイン画面に飛ばすとか。
        }
    })
    .catch(err=> {
        console.log(err)
    })

サインイン後の画面の実装例

サインアウトしてる時のみ、処理を行いたいので
サインアウト状態(null or 空オブジェクト)でサインアウト状態かどうかを見ています。

const getCurrentUserInfo = async () => {
    const currentUserInfo = await Auth.currentUserInfo();
    return currentUserInfo
}
getCurrentUserInfo()
    .then(currentUserInfo =>{
        // 以下の場合はサインアウト状態なのでログイン画面に飛ばす。
        //1.currentUserInfo === null:そのデバイスでサインアウトして、デバイスにトークンがないとき
        //2.currentUserInfo.username(などユーザーに関するプロパティ) === undefined: 別のデバイスでサインアウトして、そのデバイスにトークンはあるが、アクセストークンが無効なとき
        if(currentUserInfo === null || currentUserInfo.username === undefined){
            //ログイン画面に飛ばす処理
        }
    })
    .catch(err=> {
        console.log(err)
        //currentUserInfoが取得できないときはログイン画面に飛ばす。
        //ログイン画面に飛ばす処理
    })

以上です。
上記の処理をページの描画ごとに行えば、
Idトークンが有効であっても、
そのデバイスでサインアウトしている場合だけでなく、別のデバイスでサインアウトしているかどうかも判定ができます。

自分の方法が唯一解ではないと思うので、別案・改善案あればぜひ共有していただければ!

終わりに

公式の見解

実は、signOut({global:true})がドキュメント通りの挙動でない問題は現在openなissueとしてあがっています。
cognito.user.signOut() does not invalidate tokens

このissueは2019年6月にopenされて現在もまだcloseされていません。このissue以外にも同様の件に関するissueは何件か見られました。

ユーザーの1人がコメントで個人的にAWSに問い合わせした際の返事を転記していて、それを信じるなら、AWS側はこの問題を認知していて現在取り組み中だそうです。。
image.png

以下のどちらかがいつか実装されるといいなと思います。できれば前者。

  • グローバルサインアウトでidトークンが即時無効になり、かつ session関数がローカルストレージのトークン有効期限ではなくcognitoに有効かどうかを確認しに行く
  • アクセストークンが有効かどうかをCognitoに問い合わせる関数が生える
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWSxAmplifyxCognito] Amplifyでグローバルサインアウトがグローバルサインアウトしてくれない問題の応急処置

はじめに

こんにちは!
認証機能を実装する場合、別のデバイスでサインアウトしたらユーザーがログインしている他の全てのデバイスでもログアウトして欲しいときがありますよね?

Amplifyではそんな要望に応えるために、singOut関数にglobalオプションがあり、signOut({global:true})のようにするとサインアウトした際に、ユーザーが持つ全トークンが無効化され、全てのデバイスからサインアウトできます。
以下ドキュメントから抜粋
image.png

と、いうのがドキュメントには書かれており、実際ユーザーが認証後に取得する3種類のトークンのうち、アクセストークンとリフレッシュトークンは即座に無効になるのですが、
実は、signOut({global:true})をしてもIDトークンは無効にならないんです!
加えて、セッションを確認するための関数たち(currentSession,userSession)はCognitoにアクセストークンやリフレッシュトークンが有効なのか問い合わせにいったりはせず、ローカルストレージにあるIdトークンの有効期限を確認するだけなんです。

つまり、currentSession,userSession関数では、1つのデバイスでグローバルサインアウトが行われていても、他のデバイスではIdトークンが有効期限切れにならない限り、ユーザーは"セッション切れ"とは判断されないんです。

対応策: 実行時にアクセストークンを使う関数を利用して、セッション切れを判断する

currentSession,userSession関数では、アクセストークン、リフレッシュトークンが有効かどうか見てくれない。
なら、それらが有効かどうかを問い合わせる関数を使えばいいのですが、その確認のためだけの関数がAmplifyリファレンスに見当たらないんです。

なので、実行時にアクセストークンを使う関数を利用して、その有効・無効を判断します。
例えば、currentUserInfo関数がそれにあたります。

この関数は,

  • サインイン状態のとき、以下のようなusername,attributes(cognitoで設定したユーザーの属性)を含むオブジェクトが返ります。
{
    "UserAttributes": [
        {
            "Name": "sub",
            "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        },
        {
            "Name": "email_verified",
            "Value": "true"
        },
        {
            "Name": "email",
            "Value": "xxxxxxxxx@xxxxxxxxx.com"
        }
    ],
    "Username": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
}
  • そのデバイスでサインアウトして、デバイスにトークンがないときはnull
  • 別のデバイスでサインアウトして、そのデバイスに各種トークンはあるが、アクセストークンが無効なときは、空オブジェクト{}が返ります

したがって、返り値が null または 空オブジェクト のときユーザーはサインアウト状態であるといえます。

以下はサインイン画面での実装例です。

サインイン画面側の実装例

サインインしてる時のみ、処理を行いたいので
サインアウト状態(null or 空オブジェクト)の否定である nullでない かつ 空オブジェクトでない でサインイン状態かどうかを見ています。(空オブジェクトでない はプロパティがundefinedかどうかで見ています。)

const getCurrentUserInfo = async () => {
    const currentUserInfo = await Auth.currentUserInfo();
    return currentUserInfo;
}
getCurrentUserInfo()
    .then(currentUserInfo =>{
        //以下の場合はサインアウト状態
        //1.currentUserInfo === null:そのデバイスでサインアウトして、デバイスにトークンがないとき
        //2.currentUserInfo.username(などユーザーに関するプロパティ) === undefined: 別のデバイスでサインアウトして、そのデバイスにトークンはあるが、アクセストークンが無効なとき
        if(currentUserInfo !== null && currentUserInfo.username !== undefined){
            //サインインしているときにしたい処理を書く。ログイン画面に飛ばすとか。
        }
    })
    .catch(err=> {
        console.log(err)
    })

サインイン後の画面の実装例

サインアウトしてる時のみ、処理を行いたいので
サインアウト状態(null or 空オブジェクト)でサインアウト状態かどうかを見ています。

const getCurrentUserInfo = async () => {
    const currentUserInfo = await Auth.currentUserInfo();
    return currentUserInfo
}
getCurrentUserInfo()
    .then(currentUserInfo =>{
        // 以下の場合はサインアウト状態なのでログイン画面に飛ばす。
        //1.currentUserInfo === null:そのデバイスでサインアウトして、デバイスにトークンがないとき
        //2.currentUserInfo.username(などユーザーに関するプロパティ) === undefined: 別のデバイスでサインアウトして、そのデバイスにトークンはあるが、アクセストークンが無効なとき
        if(currentUserInfo === null || currentUserInfo.username === undefined){
            //ログイン画面に飛ばす処理
        }
    })
    .catch(err=> {
        console.log(err)
        //currentUserInfoが取得できないときはログイン画面に飛ばす。
        //ログイン画面に飛ばす処理
    })

以上です。
上記の処理をページの描画ごとに行えば、
Idトークンが有効であっても、
そのデバイスでサインアウトしている場合だけでなく、別のデバイスでサインアウトしているかどうかも判定ができます。

自分の方法が唯一解ではないと思うので、別案・改善案あればぜひ共有していただければ!

終わりに

公式の見解

実は、signOut({global:true})がドキュメント通りの挙動でない問題は現在openなissueとしてあがっています。
cognito.user.signOut() does not invalidate tokens

このissueは2019年6月にopenされて現在もまだcloseされていません。このissue以外にも同様の件に関するissueは何件か見られました。

ユーザーの1人がコメントで個人的にAWSに問い合わせした際の返事を転記していて、それを信じるなら、AWS側はこの問題を認知していて現在取り組み中だそうです。。
image.png

以下のどちらかがいつか実装されるといいなと思います。できれば前者。

  • グローバルサインアウトでidトークンが即時無効になり、かつ session関数がローカルストレージのトークン有効期限ではなくcognitoに有効かどうかを確認しに行く
  • アクセストークンが有効かどうかをCognitoに問い合わせる関数が生える
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 MacでのECRへのpushで ここに気をつけろ❗️

horizontal-logo-monochromatic-white.png

要点

  • CodeBuild でエラー。何が起きたか気づきにくい
    standard_init_linux.go:211: exec user process caused "exec format error"
  • amd64 のダイジェストを指定してpullしましょう

内容

2020年から Docker Hub の Pull回数制限がかかり、
回避のためにローカルからpushする方などいるのではないでしょうか。

M1 Macでも いつもの通り、
$ docker pull XX:XX
と普通に pull してしまうかもしれませんが、

対象のimageがマルチCPUアーキテクチャ対応だと、
自動選択で arm のイメージを持ってきてしまいます?

ですので、amd64 のダイジェストを指定してpullしましょう。

php imageでの簡易例

  1. Tagsタブ -> OS/ARCH のlinux/amd64 を選択
    スクリーンショット 2021-02-20 15.58.40.png

  2. DIGEST: の右をコピー
    スクリーンショット 2021-02-20 15.33.26.png

  3. 以下のように pull する

    $ docker pull php:fpm-alpine3.13@sha256:4b90222a821ef24358d305cbe51ff65f31edea6e323b06f40ee1f800401ebaf0
    
  4. 試しに run で確認して、arm という文字列がなければOK

    $ docker run 932a0ce9593c uname -a
    Linux 2309aaa4729b 4.19.121-linuxkit #1 SMP Tue Dec 1 17:50:32 UTC 2020 x86_64 Linux
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】Route 53で独自ドメインを取得後にElastic IPを紐付ける〜世界のAmazon様、お世話になります〜

今度はなんの話し?

AWSにデプロイしたアプリケーションを独自ドメインで公開するまでの話しです。
独自ドメイン取得に関しては初めて行いました。
他のドメイン会社で取得も考えましたがAWS内で全て完結させたほうがややこしくなくていいかと思い、一本化することにしました。
これから行う方の参考になれば幸いです。

1・Route 53のダッシュボードからドメインの登録

1.png

2・ドメインの種類を決定

.comの場合はそこまで入れるように
ここで使われているかどうかのチェック。
ちなみにwwwはつかないので、.comであれば
http://好きなドメイン.comという感じになる。
2.png

カートに入れて一番下の続行へ。

3・個人情報の入力

3.png

入力終わったら続行

4・手続きが終わったら保留中のリクエストに取得したドメインが表示される

4.png

自分の場合は10分ぐらいで保留中から登録済みになった。

5・登録済みになったら、ホストゾーンの詳細からElastic IPを紐付ける

5.png

6.png

以上で取得したドメインに飛ぶと表示されるようになった。
独自ドメインの取得で色々知らない単語とかわかったつもりになっていて全然理解していないものが出てきた。
インフラ系はめちゃくちゃ奥が深いけど楽しい。
今後も学習を続けようと思えたいい作業になりました。

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

AWS IoT Core Job 基本操作

Job基本動作

image-20210220112425258.png

1.Job document を格納

Job documentは、以下のようなJSON形式のファイルで、デバイスに対する指示内容を記載したもの

   {
     "operation": "update"
   }

2.デバイスでダウンロードしたいファイルを格納

デバイスでダウンロードしたいファイルがない場合は本手順不要
デバイスでダウンロードしたいファイルがある場合はS3にファイルを格納すると共に、
S3のURLをJob documentに記載する、URLは以下の形式で記載する

   ${aws:iot:s3-presigned-url:https://s3.amazonaws.com/bucket/key} # bucket,keyは置き換える

この形式で記載することでJobが実行されたタイミング(デバイスに対してJob documentの内容が通知されるタイミング)で署名付きURLに変換される
以下Job document

   {
     "operation": "update",
     "url": "${aws:iot:s3-presigned-url:https://s3.amazonaws.com/test-bucket/folder/job.zip}"
   }

3.Job を作成

Jobの作成は、Job IDJobの対象Jobのタイプを指定する

  • Job ID:Jobを識別するもの、値は任意、デバイスにも通知される
  • Jobの対象:Jobを実行する対象、モノまたはグループを選択可能
  • Jobのタイプsnapshotまたはcontinuous
    • snapshot:Job作成時に指定したJobの対象にJobが実行される
    • continuous:Job作成時に指定したJobの対象(グループ)と、Job作成後に指定したJobの対象(グループ)に追加されたモノに対してJobが実行される

4.Job document の内容を通知

Jobを作成するとデバイスに対してJob IDJob documentの内容が通知される

5.Job document の内容に応じた処理を実行

6.ファイルダウンロード等

デバイスはJob documentの内容に応じてファイルダウンロード等の処理を実行する

7.Job 状態の通知

デバイスはJobの実行結果をIoT Coreに対して通知する

Job状態

Jobの状態はマネジメントコンソール上で以下のように表示される

image-20210220145019980.png

Job生成時は、Jobの対象モノ数分キュー状態となる
デバイスがJobを開始すると進行中となり、
デバイスからの状態更新によって各状態のモノ数が変化する

デバイス側のJob操作

ジョブデバイス MQTT および HTTPS APIs
デバイスでJob関連の通知を受けるために各種TopicのSubscribe、Jobの開始、状態更新を行うために各種TopicのPublishが必要となる
※TOPICに含まれるthingNamejobIdは適切なものに置き換える

Job追加等の変化検出

Pub/Sub:Subscribe
コマンド:NextJobExecutionChanged
TOPIC:$aws/things/thingName/jobs/notify-next

保留中Job開始

Pub/Sub:Publish
コマンド:StartNextPendingJobExecution
TOPIC:$aws/things/thingName/jobs/start-next

デバイスがオンライン時にJobが追加された場合は、NextJobExecutionChangedでJobの追加を検出し保留中Job開始を行う
デバイスがオフライン時にJobが追加された場合は、デバイス起動時に保留中Job開始を行う

$aws/things/thingName/jobs/start-nextPublish専用Topicのため、ルールを使用してLambda等で受けることはできない(予約済みトピック

保留中Job開始結果の取得

Pub/Sub:Subscribe
コマンド:StartNextPendingJobExecution
TOPIC(成功時):$aws/things/thingName/jobs/start-next/accepted
TOPIC(失敗時):$aws/things/thingName/jobs/start-next/rejected

$aws/things/thingName/jobs/start-next/acceptedに以下のようにjobIdjobDocumentが通知される

{
  "execution" : {
    "jobId" : "022",
    "thingName" : "MyThing",
    "jobDocument" : "< contents of job document >",
    "status" : "IN_PROGRESS",
    "queuedAt" : 1489096123309,
    "lastUpdatedAt" : 1489096123309,
    "versionNumber" : 1,
    "executionNumber" : 1234567890
},
  "clientToken" : "client-1",
  "timestamp" : 1489088524284,
}

Job状態更新

Pub/Sub:Publish
コマンド:UpdateJobExecution
TOPIC:$aws/things/thingName/jobs/jobId/update

TopicでJobIdを指定し、ペイロードのstatusにIN_PROGRESS、FAILED、SUCCEEDED、または REJECTEDを指定しJob状態の更新を行う

$aws/things/thingName/jobs/jobId/updatePublish専用Topicのため、ルールを使用してLambda等で受けることはできない(予約済みトピック

Job状態更新結果の取得

Pub/Sub:Subscribe
コマンド:UpdateJobExecution
TOPIC(成功時):$aws/things/thingName/jobs/jobId/update/accepted
TOPIC(失敗時):$aws/things/thingName/jobs/jobId/update/rejected

AWS側のJob操作

ジョブ管理と制御の API
Boto3 API

Job作成(create_job

パラメータdocumentを指定することで、事前にJob documentを作成してS3に格納する必要はない
以下コード例

document = {
  'operation': 'update',
  'version': version,
  'url': '${aws:iot:s3-presigned-url:https://s3.amazonaws.com/%s/%s}' % (FirmwareBucketName, S3key)
}
target = f'arn:aws:iot:{REGION}:{ACCOUNT_ID}:thing/{thingName}'
iotClient.create_job(
  jobId = jobId,
  targets = [target],
  document = json.dumps(document),
  targetSelection = 'SNAPSHOT',
  presignedUrlConfig={
      'roleArn': RoleArn,
      'expiresInSec': 3600
  }

Job状態の取得(describe_job_execution

thingNameまで限定したJob状態の取得
以下コード例

response = iotClient.describe_job_execution(
  jobId = jobId,
  thingName = thingName
)
status = response['execution']['status']
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】デプロイ後にadminユーザーを追加する方法〜seedsファイルの実行〜

なんの話し?

AWSにデプロイした後にadminユーザーを追加する方法がわからなくて悩んだ結果の話しです。
開発環境ではseedsファイルにadmin: trueの記述をしていた。

awsにデプロイした後にadminユーザーを追加したい
などで検索するとIAMアカウント関連の結果ばかり出てきてしまい、なかなか欲しい情報にたどり着けない。
そもそもseedsを追加した時のコマンドは開発環境で下記のように実行していた。

開発環境のとき

% rails db:seed

そしてHerokuにデプロイした時にも同じことで悩み、実行したコマンドがこちら。

Herokuにデプロイしたとき

# アプリケーションのディレクトリで実行

% heroku run rails db:seed

EC2のリポジトリでRailsを起動するときのコマンドを見ていたら、
rails~コマンドを使えれば追加できるのではないか?」と考え、実行してみた。

AWSにデプロイしたとき

# EC2のリポジトリ内で実行

$ rails db:seed RAILS_ENV=production

これでadminユーザーが追加された。

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

【AWS】組織管理下アカウントのAWSメンテナンスイベント/非推奨情報を集約する【Organizational View】

はじめに

AWS Organizationsサービスを利用されている方は、複数のAWSアカウントを管理する役割の方だと思います。
管理するアカウント数が増えてくると、Personal Health Dashboard上で確認可能な各アカウントに対して送信されるメンテナンスイベント(ex.ホスト部分のアップデートによるEC2の再起動)、非推奨情報(ex.RDSにおけるPostgreSQLの旧バージョンサポート停止)、サービスダウン情報を横断的に閲覧したくなりませんか。

そんな要望を叶えてくれる機能が、AWSより2020年12月にリリースされていますので利用してみようと思います。

Personal Health Dashboardとは

各アカウント向けに特化したメンテナンスイベントや非推奨情報を閲覧することができます。
特化した とは、メンテナンスにより影響を受けるEC2インスタンスがどれか、非推奨となりバージョン変更が必要となるRDSインスタンスはどれか等が具体的に分かることを指しています。

気になる費用については、無料となっています。
ただし、イベントの情報をAPI(Health API)経由で取得したい場合(ex. イベントを基にした通知の仕組みを実装したい)には、有償のサポート契約が必要となります。

こちらの機能をOrganizations単位で集約可能とした機能(以降、管理コンソールの表示名に合わせてOrganizational Viewと呼びます)が今回利用してみる機能となります。
Organizational Viewも同様に無料で利用可能です。

Organizational Viewを使ってみる

terraformを利用し、以下の順番で有効化まで試してみようと思います。

  1. Organizations作成
  2. メンバーアカウント作成
  3. Organizational View 有効化(※残念ながらterraform非対応)

Organization作成

aws_organizations_organizationリソースを利用して作成します

resource "aws_organizations_organization" "organization" {
}

メールアドレス検証依頼メールが届くので、「Verify your email address」を押下します
スクリーンショット 2021-02-11 18.05.36.png

以下の表示が確認できればOKです
スクリーンショット 2021-02-11 18.06.11.png

Member Account作成

Organizationsを作成したので、メンバーアカウントを作成します。
(Organizationsの管理アカウント=マスターアカウントに対して、配下のアカウントをメンバーアカウントと呼びます)

resource aws_organizations_account account {
  name  = "member_account"
  email = "xxxxx@xxxxx"
}

こちらはメールアドレス検証作業は不要です

AWSサービスアクセスの有効化

Personal Health Dashboardを提供しているAWS Healthサービスから自アカウントへのAPI操作を許可する設定を行います

aws_service_access_principalshealth.amazonaws.comを追加します

resource "aws_organizations_organization" "organization" {
  aws_service_access_principals = [
    "health.amazonaws.com"
  ]
}

Organizational View 有効化

terraformでの有効化手段は現状なさそうです。
AWS ProviderのGithub上でもissueは挙がっていなさそうです。

CLIでの有効化手段はありましたが、サポート契約を行っていないアカウントのため、IaCは諦めてPersonal Health Dashboardのコンソール画面から有効化を行います

  1. Persoanl Health Dashboardのサービス画面に遷移し、左側のメニューより「Organizational View」→「Configurations」を押下します。
    スクリーンショット 2021-02-15 23.15.36.png

  2. 画面右下にある「Enable organizational view」を押下します。
    スクリーンショット 2021-02-15 23.16.30.png

  3. 有効化されるまで数分かかるので待ちます。
    スクリーンショット 2021-02-14 22.39.03.png

  4. 画面上部に成功した旨のメッセージが表示され、PendingSuccessになっていれば完了です。
    スクリーンショット 2021-02-14 22.39.47.png

スクリーンショット 2021-02-14 22.40.13.png

(実際はこの時点で、前の手順で実施したAWSサービスアクセス有効化も行われるので、前の手順はスキップ可能です)

イベントの確認

左側のメニューより「Organizational View」→「Dashboard」を押下します。

スクリーンショット 2021-02-20 13.58.15.png

Deprecatedやメンテナンスのイベントが無い個人アカウントと新規作成したアカウントしか存在しない状態のため、実際のイベントは確認できていませんが、以下のような画面で集約されたイベントの表示が確認可能となります。
ステータスも確認できるので、各アカウントで当該イベントに対して対応済みか否かがわかるようです。

スクリーンショット 2021-02-15 22.29.17.png

さいごに

既にOrganizationsを利用されている方にとっては、管理コンソール上で1度ボタンを押すだけでイベントを集約してダッシュボードで確認することが可能となりますので、ROIが非常に高い機能だと思います。

さらに価値を上げたい場合は、サポートプランの契約が必要となりますが、Health APIをAWS ChatBotと連携してSlackなどで各アカウントの担当者宛に通知を行ってあげると良いと思います。

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

AWS初心者がお名前ドットコム、S3、CloudFront、CertificateManager、Route53を使ってHTTPS化されたサイトを公開してみた(パート②)

今回はAWS初心者がお名前ドットコム、S3、CloudFront、CertificateManager、Route53を使ってHTTPS化されたサイトを公開してみた(パート①)こちらの続きをやっていきたいと思います。

まだこちらの記事を見ていない方はパート①を見てからこちらの記事を見て下さい。

  1. 静的サイトをS3へデプロイ
  2. お名前ドットコムでドメイン登録
  3. Route53で独自ドメイン登録(ここまでがパート①)
  4. CertificateManagerでSSL証明書の発行(ここからパート②)
  5. CloudFrontで独自ドメインでアクセス

パート②ではこの4と5をやっていきます。

まずHTTPS化について説明します。

今やほとんどのサイトがHTTPSとして始まっています。

これはいわゆる安全なサイトであることの証明です。

そして、これを証明するには「SSL証明書」というものが必要です。

SSL証明書を発行するにはいろいろなサービスがあるのですが、AWSの「CertificateManager」を使うことで無料でSSL証明書を発行することができます!

そして、パート①のお名前ドットコムで登録したドメインを「CloufFront」のドメインとすることで静的サイトを独自のドメインで公開することができるんです!!!

今回はこんな感じでやっていこうかなと思います!

それでは説明を一緒に見ていきましょう!

4. CertificateManagerでSSL証明書の発行

お名前ドットコムのネームサーバー設定

まず、証明書を発行する前にパート①のRoute53で登録したホストゾーンをクリックしてください。

以下、説明用のドメインはexample.comとします。

また、Route53に登録したホストゾーン名はwww.example.comとします。

すると、レコード名www.example.comのタイプがNSのところに以下のような4つのルーティング先があると思います。

ns-111.awsdns-11.com.
ns-2222.awsdns-22.org.
ns-3333.awsdns-33.co.uk.
ns-444.awsdns-44.net.

※注意 これは悪魔で例なのでこれは使わないでください。

お名前ドットコムのDNS設定

そしたらお名前ドットコムへログインしてください。

下の画像のように「DNS」をクリックし「ドメインのDNS設定」をクリック。

「ドメインのDNS関連機能設定」を選択し「次へ」をクリック。

image.png

ドメイン名を選択する画面が表示されるので、登録したドメイン名を選択し「次へ」をクリック。

image.png

「DNSレコード設定を利用する」の右にある「設定する」をクリック。

すると、このような画面になります。

無題.png

そして、先ほどRoute53で確認した4つのルーティングの設定を行います。

ns-111.awsdns-11.com.
ns-2222.awsdns-22.org.
ns-3333.awsdns-33.co.uk.
ns-444.awsdns-44.net.

こちらですね。

下の画像のようにこの4つを追加してください。

無題.png

※注意 Route53のルーティングの値の最後には.com.となっていますが、お名前ドットコムに登録するときは.comとして最後の.を消して登録してください。

「TYPE」は4つとも「NS」に設定してください。

ホスト名にはRoute53に登録したホストゾーン名を入れて下さい。

例:www.example.com

image.png

そして、画面を下へスクロールし、「DNSレコード設定用ネームサーバー変更確認」にチェックが外します。これをしないと今登録したルーティングの設定がリセットされるのでご注意。

そしたら「確認画面へ進む」をクリックし、「設定する」をクリック。

お名前ドットコムのネームサーバー設定

無事設定が完了したら左のメニューバーの「ネームサーバーの設定」にある「ネームサーバーの変更」をクリック。

image.png

ドメイン名では自分が登録したドメインにチェックします。(例:example.com)

ネームサーバーの選択では「その他」→「その他のネームサーバーを使う」を選択し、画像のようにRoute53のルーティングの値を4つ入れます。

image.png

下の「確認」を押し、設定します。

ネームサーバーの移行には少し時間がかかります。

僕の体感ではそこまで時間はかかりませんでした。

完了したら登録したメールアドレス宛に完了通知メールが届くと思います。

そしたら、コマンドプロンプトで以下のコマンドを打ってください。

nslookup -type=ns www.example.com

権限のない回答:
www.example.com   nameserver = ns-111.awsdns-11.co.uk
www.example.com   nameserver = ns-2222.awsdns-22.com
www.example.com   nameserver = ns-3333.awsdns-33.net
www.example.com   nameserver = ns-444.awsdns-444.org

ちゃんと設定したネームサーバーの値が返ってきたらOKです!

CertificateManagerでSSL証明書の発行

次は、いよいよ大詰め。

SSL証明書の発行をしていきます。

AWSコンソールのサービスに「Certificate Manager」と入力しクリックします。

「証明書のリクエスト」をクリック。

image.png

「パブリック証明書のリクエスト」にチェック。

image.png

ドメイン名を入力

image.png

「DNSの検証」にチェック

「タグ」は何も入力せずに「次へ」をクリック

「確定とリクエスト」をクリック

無題.png

ドメインのをクリックするとこのように「CNAME」というタイプのものが表示されます。

※例ではwww.example.comとしていたのですが、www.example.comの証明書が発行できなかったので適当にドメイン名を入力しました。気にせず次へ進んでください。

そしたらこの値をお名前ドットコムのDNSで設定します。

例で言うと、ホスト名には_aaaaaaaaaaaaaaaaaaaaa.www、VALUEには_bbbbbbbbbbbbbbbbbbbbbbbb.ccccccc.acm-validations.awsを入力します。

無題.png

このように設定すればOK!

そしたら、Route53でも同じようにレコードを作成します。

無題.png

「Certificate Manager」の戻り、数分待ち証明書の検証が成功すればOK!!

5. CloudFrontで独自ドメインでアクセス

いや~ここまで長かったですね。

最後はすごく簡単です!!

ここでの作業は4番の10分の1くらいの作業ですので、ごゆっくりご覧ください!

AWSコンソールにて「CloudFront」と入力しクリック

「Create Distribution」をクリック

無題.png

「Origin Domain Name」にはS3のバケットを選択してください。

すると、「Origin ID」も自動で入力してくれます。

無題.png

「Alternate Domain Names(CNAMEs)」には自分で登録したドメイン名を入力してください。

「SSL Certificate」では「Custom SSL Certificate (example.com):」を選択し、先ほど作成したSSL証明書を選択してください。(SSLの証明書がしっかりと発行されていれば候補が出てきます)

image.png

作成したら「Status」が「Deployed」になるまで待つ。

最後にhttps://www.example.comにアクセス!!!

表示されたら完璧です!

いかがだったでしょうか?

初めての方は恐らく疲れたと思います。

ただ、AWSを使うことで自作サイトを初心者でも公開することができるんです!

AWSは「革命」と言っても過言ではないですね。

引き続き勉強頑張りましょう!

以上、「AWS初心者がお名前ドットコム、S3、CloudFront、CertificateManager、Route53を使ってHTTPS化されたサイトを公開してみた(パート②)」でした!

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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

【Django】Cloud9でSQLIte3のアップデート

AWS Cloud9で ライブラリを色々アップデートしていると以下に遭遇。

django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).

というエラーが出たのでアップデートをする方法

参考:https://qiita.com/rururu_kenken/items/8202b30b50e3bfa75821

# SQLite3を最新化する
# ソースを取得 (ホームディレクトリで実行してます)
$ cd

# バージョンは「https://www.sqlite.org/download.html」で確認して、
# 新しいバージョンのファイル名を指定してください (2019/12/06追記)
# 例) SQLite 3.31.1の場合

$ wget https://www.sqlite.org/2020/sqlite-autoconf-3310100.tar.gz
$ tar xvfz sqlite-autoconf-3310100.tar.gz

# ビルドしてインストール
$ cd sqlite-autoconf-3310100
$ ./configure --prefix=/usr/local
$ make
$ sudo make install
$ sudo find /usr/ -name sqlite3

# コマンド結果
# /usr/lib64/python2.7/sqlite3と/usr/lib64/python3.7/sqlite3はディレクトリ
/usr/bin/sqlite3
/usr/lib64/python2.7/sqlite3
/usr/lib64/python3.7/sqlite3
/usr/local/bin/sqlite3

# 不要なファイル、ディレクトリ削除
$ cd 
$ rm sqlite-autoconf-3310100.tar.gz
$ rm -rf ./sqlite-autoconf-3310100

# バージョン確認
$ /usr/local/bin/sqlite3 --version

3.31.1 2020-01-27 19:55:54 3bfa9cc97da10598521b342961df8f5f68c7388fa117345eeb516eaa837bb4d6

$ /usr/bin/sqlite3 --version

3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668

$ sqlite3 --version

3.31.1 2020-01-27 19:55:54 3bfa9cc97da10598521b342961df8f5f68c7388fa117345eeb516eaa837bb4d6

$ sudo mv /usr/bin/sqlite3 /usr/bin/sqlite3_old
$ sudo ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3

# 共有ライブラリへパスを通す
# vi ~/.bashrcで設定を追加(すぐに反映する場合 source ~/.bashrc)しないとターミナルを起動するたびに実行することになります
$ export LD_LIBRARY_PATH="/usr/local/lib"

# PythonのSQLite3バージョン確認
$ python
Python 3.7.4 (default, Dec 13 2019, 01:02:18) 
[GCC 7.3.1 20180712 (Red Hat 7.3.1-6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> sqlite3.sqlite_version
'3.31.1'
>>> exit()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3 syncコマンドを利用してS3バケット間を同期する

S3 syncコマンドを利用してS3バケット間を同期する

最近S3バケットデータ移行の要望が来ていました。

バケット間同期用のコマンドを調査しましたので、備忘として残ります。

やりたいこと

AアカウントにあるS3バケットのデータをBアカウントのS3に移行したい

実現方法を簡単に説明

  1. BアカウントにS3バケットを作成する
  2. オンプレミス環境で実行するので、S3操作権限あるIAMユーザを作成する
  3. AアカウントのS3バケットにBアカウントで作成したIAMユーザのアクセス許可ポリシーを追加する
  4. S3バケット間でデータを同期する
  5. データが正しくコピーされているかを確認する

同期コマンド

cpコマンドを利用して再帰的にコピーできますが、今回利用するもっと楽の同期コマンドをご紹介します。

aws s3 sync s3://BUCKET-SOURCE s3://BUCKET-TARGET

sync コマンドは CopyObject API を使用して S3 バケット間でオブジェクトの移動を行います。sync コマンドは、ソースとターゲットのバケットをリストアップし、ソース側に存在しターゲット側にないバケットを特定します。また、このコマンドは、LastModified の日付がターゲットバケット内のものと異なる、ソースバケット内のオブジェクトも識別します。

結構使えるオプションもあるので、今回使ったオプションをご紹介します。

他にはAWSの公式ドキュメントをご参照ください。

aws s3 sync

オプション 説明
--only-show-errors デフォルトではコピープロセスが表示されます。オブジェクトが多いので、エラー情報のみ出力するように設定する。
--exact-timestamps S3からローカルに同期する場合、同じサイズのアイテムは、タイムスタンプが完全に一致する場合にのみ無視されます。 デフォルトの動作では、ローカルバージョンがS3バージョンよりも新しい場合を除き、同じサイズのアイテムは無視されます。
--delete コピー先には存在するがコピー元には存在しないファイルは、同期中に削除されます

コピー結果を確認

次のコマンドを実行して、ソースとターゲットバケットの内容を確認する

aws s3 ls --recursive s3://BUCKET-SOURCE --summarize > bucket-contents-source.txt

aws s3 ls --recursive s3://BUCKET-TARGET --summarize > bucket-contents-target.txt

上記のコマンドを実行すると、バケット内容にあるオブジェクトリスト一覧とオブジェクト数とサマリーサイズが出力されます。

少しハマった話

コピーした後に上記の確認コマンドでリスト一覧を出力してみたら、

あれ、オブジェクト数が異なっています。

いろいろ調査し、試してみたら、以下のことがわかりました。

元のバケットでは、基本フォルダを作成した後に、ファイルをアップロードしています。

とすると、サイズゼロのオブジェクト(フォルダ)がリスト一覧に出力されます。
s3_list_size0.png

但し、オブジェクトをコピーする際に、サイズゼロのフォルダが作成しないので(プレフィックス付きのオブジェクトのみ作成する)

コピー後のTotla Objectsが異なっています。

s3_list.png

なんか、サイズゼロのオブジェクトを出力しないオプションがなさそうなので

トータルサイズを比較し、オブジェクト一覧をExcelにコピーして整形してから比較するようにしました。

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

Amazon MQ とは?

勉強前イメージ

メッセージ送る系のなにかのサービスかな?

調査

Amazon MQ とは?

Apache ActiveMQ向けのマネージド型メッセージブローカーサービスでsy

そもそも メッセージブローカーとは?

異なるシステム間でメッセージのやり取りの際に、直接データのやり取りを行うのではなく
中間システムとしてメッセージ格納領域を管理する機能のことになります。

メッセージキューイング とは?

サービス名にもある MQ はメッセージキューイングのことを指しており、
メッセージの送信元・送信先とは別に第三者を介することで、送受信のタイミングを図らずとも処理を行うことが出来ます。
メッセージキューイングシステムを行う際の中間システムを担うのがメッセージブローカーになります。

メッセージブローカーAWS MQとは? - diagrams.net - Google Chrome 2021-02-1.png

AWS MQ を使用するメリット

  • 迅速な移行

業界標準APIやメッセージング用プロトコルを使用しているため
アプリケーションを簡単に Amazon MQ に接続することができます

  • 運用上の責任を軽減

ソフトウェアをインストールして管理したりする必要はなく、
MQがソフトウェアのアップグレードやセキュリティの更新、障害の検出と回復などのタスクを自動的に管理します

  • 耐久性のあるメッセージングを簡単に実現

リージョン内の複数のAZにメッセージを冗長的に保存しているので、
障害が発生した場合でも利用することが出来ます

勉強後イメージ

メッセージキューを中間的に管理するシステム...
実際に触ったことないからイメージつかめないけど、全体像はなんとなくイメージついた

参考

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

DatadogでAWSイベントを定期監視する

金曜の夜に見たくないアラートが。

Screen_Shot_2021-02-20_at_8_40_26.png

続いて複数プロダクトで強制シャットダウン祭り。

Screen Shot 2021-02-20 at 8.08.12.png

DatadogでAWSイベントを監視していたのでアラートは飛んできたけど、全てのイベントが通知されてないことに気付いた。8イベント中、最初と最後の2件しかきてない。

Screen Shot 2021-02-20 at 8.04.24.png

これよく考えたら当前で、障害自体は5時間近く続いた1イベントなので、障害中のイベントは一切飛んでこない。

Screen Shot 2021-02-20 at 8.22.33.png

中間イベントも送信したかったら監視モニターにある「automatically resolve...」なんちゃらのオプションを有効化しておけば良い。例えば1時間後にアラートを自働で閉じて、その後も障害が続く場合は続報も飛ばしてくれるようになる。

Screen_Shot_2021-02-20_at_8_10_58.png

Terraformならこんな感じ。

resource "datadog_monitor" "aws_service_check" {
  name                = "AWS service check"
  type                = "service check"
  message             = local.message
  query               = "'aws.status'.over('region:ap-northeast-1').by('region','service').last(2).count_by_status()"
  renotify_interval = 60

  thresholds = {
    critical = var.aws_service_check["thresholds.critical"]
  }

  tags = ["aws"]
}

朝6時にようやく落ち着いた。寝る。

Screen_Shot_2021-02-20_at_8_52_18.png

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

AWSのEC2へのデプロイ②(必要なパッケージのインストール)

AWSのEC2へのデプロイをしたいと思い備忘録のために手順を記録します。
初学者の為、ご指摘等ありましたらご連絡いただければ幸いです。

前回の記事はこちら
AWSのEC2へのデプロイ①(EC2インスタンス作成からログインまで)

前回の内容は言語関係ないのですが今回から必要なパッケージが言語によって異なるのでご注意ください。

はじめに

私自身この手順で無事にデプロイができましたが、途中でエラーが出たことによりいろいろな記事を参考にさせて頂きました。
この場を借りて御礼申し上げます。

そのため、私に知識がないばかりに私の環境に必要のない操作もあるかもしれません。
初学者のためご承知おきください。
お気づきの点ございましたらご連絡いただければとても喜びます。

環境

Version
PHP 7.4.14
Laravel 8.24.0
mysql 8.0.23
docker 20.10.2
docker-compose 1.27.4

EC2ではAmazon Linux 2を利用しています。

前提

  • AWSのアカウントを作成済みである
  • EC2のインスタンス作成済みでsshにログインできる

EC2インスタンスに接続

1.AWSのEC2の画面で下記画像のようにインスタンスから接続を押下します。

スクリーンショット 2021-02-13 22.40.27.png

2.下記画像のようにSSHクライアントを押下後、一番下の文字列をコピーします。

スクリーンショット 2021-02-13 22.45.31.png

3.コピーしたssh -i ~をターミナルで入力すると

ターミナル
Are you sure you want to continue connecting (yes/no/[fingerprint])?

と出るのでyesと入力します。

4.下記のような内容が出力されていればOKです。

ターミナル
[ec2-user@ip-×××-××-××-××× ~]$

gitのインストール

コマンドは全てEC2インスタンス内で行います。

1.下記コマンドでyumをupdateします。

ターミナル
$ sudo yum update -y

2.下記コマンドでgitをインストールします。

ターミナル
$ sudo yum install git

3.下記コマンドご自身のユーザー名とパスワードを設定します。

ターミナル
$ git config --global user.name [ユーザー名]
$ git config --global user.email [メールアドレス]

4.下記コマンドで確認します。

ターミナル
$ git config --list | grep user

5.下記のように出ていればOKです。

ターミナル
user.name=[ユーザー名]
user.email=[メールアドレス]

Dockerのインストール

コマンドは全てEC2インスタンス内で行います。
Dockerのインストールは公式のAmazonECSにおけるDockerの基本で確認しました。

1.下記コマンドで最新のDocker Engineパッケージをインストールします(Amazon Linux 2用のコマンドです。)

ターミナル
sudo amazon-linux-extras install docker

2.下記コマンドでDockerサービスを開始します。

ターミナル
sudo service docker start

3.下記コマンドでec2-userdockerグループに追加するとsudoコマンドを使用せずにDockerコマンドを実行できます。

ターミナル
sudo usermod -a -G docker ec2-user

4.一旦EC2インスタンスからログアウト(exit)して再ログインします。
※これをしなくて私はエラーになりましたので大事です。

5.下記コマンドでec2-usersudoコマンドを使用せずにDockerコマンドを実行できることを確認します。

ターミナル
docker info

下記のようなメッセージが出ればOKです。

ターミナル
Client:
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
//省略

docker-composeのインストール

参考記事:M1 Mac + Laravel + Docker + AWS でポートフォリオを作るまで[初心者][未経験]

コマンドは全てEC2インスタンス内で行います。

1.下記コマンドでsuに入ります。

ターミナル
sudo -i

2.下記コマンドでcomposeをインストールします。

ターミナル
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

3.下記コマンドで実行権限を付与します。

ターミナル
chmod +x /usr/local/bin/docker-compose

4.exitをして抜けます。

ターミナル
exit

5.下記コマンドでdocker-composeがインストールされているか確認します。

ターミナル
docker-compose --version

下記のようにバージョンが出てくればOKです。

ターミナル
docker-compose version 1.27.4, build 40524192

参考にした先ほどの記事でdocker-compose.ymlのバージョンを書き換えました。

docker-compose.yml
version: "3.3" 変更
services:
  app:
    build: ./infra/php
    volumes:
      - ./backend:/work
//省略

EC2インスタンス内でDockerを起動

コマンドは全てEC2インスタンス内で行います。

1.下記コマンドでgitからcloneします。

ターミナル
git clone [githubのURL]

2.下記コマンドでフォルダがあるか確認します。

ターミナル
ls

3.下記コマンドでディレクトリを移動します。

ターミナル
cd [ディレクトリ名]

4.下記コマンドでdockerコンテナを立ち上げます。
少し長いメッセージが出ます。

ターミナル
docker-compose up -d

下記のようなメッセージが出ればOKです。

ターミナル
Creating ×××_app_1      ... done
Creating ×××_web_1      ... done
Creating ×××_db_1       ... done
Creating phpmyadmin         ... done

5.下記コマンドでステータスを確認します。

ターミナル
docker-compose ps

Stateが全てupになっていればOKです。

PHPのインストール

参考記事:AWS EC2 AmazonLinux2 PHPをインストールする

コマンドは全てEC2インスタンス内で行います。

1.下記コマンドでインストールリポジトリを追加します。

ターミナル
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
ターミナル
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

2.下記コマンドで既存のPHPパッケージを削除します。(削除するものがなくても問題ないです)

ターミナル
sudo yum remove php

3.下記コマンドでPHPのパッケージをインストールします。

ターミナル
sudo yum install --enablerepo=remi,remi-php74 php php-devel php-mbstring php-pdo php-gd php-xml php-mcrypt

4.下記コマンドでバージョンを指定してインストールします。
途中で[y/d/N]とインストールしてもいいかの選択が出るのでyを押しEnterで進めます。

ターミナル
sudo amazon-linux-extras install php7.4

5.下記コマンドでその他の必要なパッケージをインストールします。
こちらも途中で[y/d/N]とインストールしてもいいかの選択が出るのでyを押しEnterで進めます。

ターミナル
sudo yum install --enablerepo=remi,amzn2extra-php7.4 php-xml php-mbstring

6.下記コマンドでyumにてインストールされたphpと名前がつくパッケージの一覧を表示し確認します。

ターミナル
yum list installed | grep php

下記のように表示されればOKです。

ターミナル
php-cli.x86_64                  7.4.7-1.amzn2                 @amzn2extra-php7.4
php-common.x86_64               7.4.7-1.amzn2                 @amzn2extra-php7.4
php-fpm.x86_64                  7.4.7-1.amzn2                 @amzn2extra-php7.4
php-json.x86_64                 7.4.7-1.amzn2                 @amzn2extra-php7.4
php-mbstring.x86_64             7.4.7-1.amzn2                 @amzn2extra-php7.4
php-mysqlnd.x86_64              7.4.7-1.amzn2                 @amzn2extra-php7.4
php-pdo.x86_64                  7.4.7-1.amzn2                 @amzn2extra-php7.4
php-xml.x86_64                  7.4.7-1.amzn2                 @amzn2extra-php7.4

7.無事に成功すれば下記コマンドでバージョンが表示されます。

ターミナル
php --version

MariaDBのインストール

コマンドは全てEC2インスタンス内で行います。

1.下記コマンドでMariaDBのインストールをします。

ターミナル
sudo yum -y install mysql56-server mysql56-devel mysql56 mariadb-server mysql-devel

2.下記コマンドでデータベースを起動します。

ターミナル
sudo systemctl start mariadb

下記コマンドで起動できたかを確認します。

ターミナル
sudo systemctl status mariadb

緑の文字でactive (running)と出ていればOKです。

3.データベースのrootパスワードを設定します。

まずは下記のコマンドを入力します。

ターミナル
sudo /usr/bin/mysql_secure_installation

次からどんどん質問が出てくるので答えていきます。

Enter current password for root (enter for none):→Enterキーを押す。
Set root password? [Y/n]→「Y」を入力してEnterキーを押す。
New password:→ご自身で決めたパスワードを入力する。(画面には表示されないけど入力できています)
Re-enter new password:→もう1度ご自身で決めたパスワードを入力する。
Remove anonymous users? [Y/n]→「Y」を入力してEnterキーを押す。
Disallow root login remotely? [Y/n]→「Y」を入力してEnterキーを押す。
Remove test database and access to it? [Y/n]→「Y」を入力してEnterキーを押す。
Reload privilege tables now? [Y/n]→「Y」を入力してEnterキーを押す。

4.データベースに接続を確認します。

下記コマンドでデータベースに接続できるか確認します。

ターミナル
mysql -u root -p

パスワードを求められるので入力しEnterキーを押します。

ターミナル
MariaDB [(none)]>

上記のように表示されれば接続できています。
抜け出したいときはexitで抜け出せます。

Laravel環境設定

参考記事:AWSにEC2上にdockerを使用したlaravelをデプロイ④(gitクローン〜デプロイ、マイグレーション)

コマンドは全てEC2インスタンス内で行います。

1.下記コマンドでコンテナ内に入ります。

ターミナル
docker-compose exec app bash

2.下記コマンドで.envファイルを作成します。
.envファイルはgitリポジトリにpushされないためです。

ターミナル
cp .env.example .env

3.下記コマンドでcomposerをインストールします。

ターミナル
composer install

4.下記コマンドでAPP_KEYを発行します。

ターミナル
php artisan key:generate

5.下記コマンドで権限を変更します。

ターミナル
chmod 777 storage/logs vendor
chmod 777 storage/framework/views
chmod 777 storage/framework/sessions

6.画像投稿がある場合は下記コマンドも必要みたいです。

ターミナル
php artisan storage:link
chown -R www-data:root .

7.下記コマンドでマイグレーションを実行します。

ターミナル
php artisan migrate
php aritsan db:seed

以上で無事にデプロイ ができました。

ただ、現在S3への画像の保存ができない状態なので解決出来次第修正致します。

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

Docker,CircleCI,AWS,Railsでポートフォリオを作成

前書き

就職活動用のポートフォリオをDocker,CircleCI,AWS(ECS)を用いてWebアプリケーションを作成しました。
バックエンドにRuby on Railsを用いました。
今回は作成したポートフォリオの機能、参考記事などを紹介したいと思います。

概要と作成背景

今回作成したアプリはスイーツのデリバリーに特化したECサイトです。

  • ケーキ屋のアルバイト経験からお店の業務改善をしたい
  • 手軽にお店のスイーツを食べたい
  • 店舗で販売しているようなスイーツを配達するサービスが存在しない(自分調べ)

上記の理由により飲食店にもユーザーにもメリットのあるサービスを作成しようと思いました。

アプリのURL: http://sweetsdeli.com

GitHubのURL: https://github.com/sekine617/sweetsdeli

画面イメージ紹介

トップページ

新着商品、人気商品などを表示
人気商品はユーザーの購入履歴から購入数が多い商品を表示しています。
スクリーンショット 2021-02-19 15.07.58.png

商品一覧ページ

こちらではカテゴリー検索、商品名検索などができます。
スクリーンショット 2021-02-19 15.27.36.png

商品詳細ページ

こちらでは商品をカートに入れる、お気に入り登録、商品のレビュー投稿などができます。
スクリーンショット 2021-02-19 15.27.58.png

マイページ

マイページではプロフィール編集、お気に入り、購入履歴、投稿したレビューを閲覧できます。

スクリーンショット 2021-02-19 15.21.47.png

スクリーンショット 2021-02-19 15.25.44.png

スクリーンショット 2021-02-19 15.25.57.png

スクリーンショット 2021-02-19 15.30.25.png

ショッピングカート

こちらではカートに入れた商品の確認、個数の変更、カートから削除などができます。
スクリーンショット 2021-02-19 15.29.35.png

購入画面

カートに入れた商品から合計金額を確認し、クレジットカードによる決済が可能です。実際に決済する場合はpay.jpのてすとカードを使用してください。
スクリーンショット 2021-02-19 15.30.02.png
このようにカード情報の入力フォームがモーダルウィンドウで出てきます。
スクリーンショット 2021-02-19 15.46.47.png

使用言語

  • フロントエンド
    • jQuery
    • HTML
    • CSS/Sass
    • Bootstrap
  • バックエンド
    • Ruby on Rails
    • Ruby
    • PAY.JP(外部API)
  • インフラ
    • Docker/docker-compose
    • CircleCI
    • nginx
    • mysql
    • AWS(ECS, ALB, S3, RDS, Route53, ECR, VPC, IAM)

インフラ構成図

スクリーンショット 2021-02-19 18.41.03.png

開発環境

機能一覧

  • ユーザー関連(devise)
    • 登録機能
    • プロフィール編集機能
    • ログイン・ログアウト機能
  • 決済機能(PAY.JP API)
  • 人気商品表示機能
  • タグ機能
  • 商品登録機能
  • 画像アップロード機能(AWS S3バケット, carrierwave)
  • お気に入り機能(非同期処理, jQuery)
  • フラッシュメッセージ表示機能
  • レビュー投稿機能
  • カート関連
    • カート登録機能
    • カート編集機能
  • 住所自動入力機能(jQuery)
  • 商品名検索機能
  • カテゴリー検索機能
  • ページネーション機能(kaminari)

データベース設計

スクリーンショット 2021-02-19 21.28.45.png

テーブル説明

テーブル名 説明
users ユーザー情報
orders 注文管理(受け取り日時など)
orders_products  注文商品管理
products 商品情報
reviews 商品に対するレビューを管理
likes 商品へのお気に入り情報
address ユーザーの住所
cart_items cartsとproductsの中間テーブル
shops ショップ情報
carts カート追加した商品情報一時的に保存
product_tag_relations productsとtagsの中間テーブル
tags 商品のカテゴリ情報

ポイントはproductsテーブルのquantity_per_dayで、1日の提供数を指定し注文数が1日の提供数を上回らないようにしました。
また注文時に受け取り日時を指定し、各店舗ごと、日にちごとに管理が可能です。

苦労した点

CircleCIでAWSへの自動デプロイ

CircleCIを用いて開発環境のDockerイメージをECRにpushし、ECSのタスク定義を更新してデプロイをしましたが、config.ymlの設定でのエラーに悩まされました。
またAWSの各設定にもかなり悩まされ、インフラの知識がないことで時間が結構かかりました。

credentials.ymlでのpay.jpなどのアクセスキー管理でなかなかうまく行かずインデントの重要性がよくわかりました。

参考記事

Railsでのテストを参考にしました。
【Rails】はじめてのRSpec!テストコードを書こう!

deviseのログイン機能を参考にしました
【Rails】ログイン機能を実装する

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

AWS初心者がお名前ドットコム、S3、CloudFront、CertificateManager、Route53を使ってHTTPS化されたサイトを公開してみた(パート①)

皆さんこんにちは!

最近、暖かったり寒かったり気温の変化が激しく大変ですね。

インドア大学生の僕にはあまり関係ないんですけどね(笑)

ってことで今回はAWS初心者がHTTPS化されたサイトをアップロードしていく流れをご紹介したいと思います。

タイトルが長いということは、書く内容も必然と長くなっていくので2つに分けて説明していきます。

流れとしては以下のようになります。

  1. 静的サイトをS3へデプロイ
  2. お名前ドットコムでドメイン登録
  3. Route53で独自ドメイン登録(ここまでがパート①)
  4. CertificateManagerでSSL証明書の発行(ここからパート②)
  5. CloudFrontで独自ドメインでアクセス

こんな感じになります。

パート1に関しては、他の方が記事を挙げているのでそれらを見て頂く形になります。

パート2からはつまずくポイントが少しあるので、それらをしっかりと理解しながら説明していきます。

何事も最初ってかなり時間がかかったり、つまづいたりすることが多いのでぜひこの記事を参考にして頂けたらなと思います。

よかったら、LGTMお願いします!

それでは一緒に説明を見ていきましょう!

1. 静的サイトをS3へデプロイ

これは僕が記事を書くよりも下記の記事の方が分かりやすいので、こちらを参照の下、S3へデプロイしてください!

Amazon S3でSPAをサクッと公開する

S3へデプロイする時が一番壁にぶち当たるポイントだと思うので、デプロイするときにエラーが出た場合はこちらの記事を見てみてください。

AWSのS3でデプロイするときにエラーが出た時の話

結構時間がかかると思うので頑張ってください!

2. お名前ドットコムでドメイン登録

こちらも僕が記事として書くよりも、他の方の記事の方が円滑に登録を行えるのでいくつか記事をご紹介して頂きます。

ドメイン登録を行う際は自分の用途に合わせてドメインを指定してください。

お名前.comの登録方法・ドメイン取得方法【初心者向けに解説!】

【ブログ初心者向け】お名前.comで独自ドメインを取得する方法をステップごとに解説

ご協力誠にありがとうございます。

以下、説明用のドメインはexample.comとします。

3. Route53で独自ドメイン登録

これでパート①は最後になります。

ここまで中々骨のある作業だった思いますが、最後はすごく簡単なので肩の力を抜いてご覧下さい!

AWSコンソールの検索欄で「Route53」と入力してクリックしてください。

するとこのような画面になると思います。

image.png

もしかしたら違うかも。

そしたら、左のメニュー欄の「ホストゾーン」をクリックし、ホストゾーンの作成をクリックしてください。

image.png

※重要 画像のようにドメイン名には自分で決めたドメインの前にwww.のように何か名前をを付けて下さい。今回は分かりやすく説明するためにwww.example.comとします。

タイプはサイトを一般公開するので「パブリックホストゾーン」とします。

そして、無事完成されたら成功です!

パート①ここまでとします。

S3へデプロイするのが一番難しいと思います。

ただ、されさえできてしまえば後は簡単な作業です。

パート②はこちらの記事になります。

AWS初心者がお名前ドットコム、S3、CloudFront、CertificateManager、Route53を使ってHTTPS化されたサイトを公開してみた(パート②)

パート②はパート①に比べてやることも少ないので、あともう少し頑張りましょう!

以上、「AWS初心者がお名前ドットコム、S3、CloudFront、CertificateManager、Route53を使ってHTTPS化されたサイトを公開してみた(パート①)」

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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