チュートリアル実践「現代的なウェブアプリケーションの構築」
はじめに
Amazonが提供する現代的なウェブアプリケーションの構築を読み進めることにした。
なお実践はCloud9ではなくローカル端末で試すことにした。
また請求が発生する可能性があるため、早めに済ませること。EC2でめちゃくちゃお金がかかった。
モジュール1「静的ウェブサイトの構築」
(前提)AWS CLIの設定
AWS CLIを使う必要があるのでaws configure
でAWSの設定ファイル(.aws)を作成した。アクセスキーが古くなってきたので新しくした。
(前提)S3とは
S3 は、保存されたオブジェクトをHTTP経由で直接提供できる、耐久性と可用性に優れた、低コストのオブジェクトストレージサービスです。
「ファイルをHTTPで転送できる」というのがポイント。つまりWebサイトにもなりうる。
S3バケットの作成
aws s3 mb
コマンドでS3バケットを作成した。バケット名はaws-modern-application-workshop-kobori
にしたので、具体的には次のコマンドを打つ。
aws s3 mb s3://aws-modern-application-workshop-kobori
S3バケットの設定
静的ウェブサイトのホスティングができるように、「ウェブサイトとして利用」と「外部からのアクセスを許可」の設定をする。
ホスティング
aws s3 website
コマンドで静的Webサイトのホスティング設定をする。--index-document
オプションはindex.html
を指定。
外部からのアクセスを許可
バケットを作成した時点ではプライベート(外部からはアクセスできない)ため、S3バケットポリシーを変更する必要がある。
バケットポリシーは一般的にはJSONファイルを定義して、これをaws s3api put-bucket-policy
コマンドで送るのがいいみたい。そのためJSONファイルを次のように送った
aws s3api put-bucket-policy --bucket aws-modern-application-workshop-kobori --policy file://JSONファイル
JSONファイルの中身は下記の通り。
{
"Id": "MyPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::aws-modern-application-workshop-kobori/*"
}
]
}
index.htmlをデプロイ
最後に実際に利用されるindex.htmlをS3バケットに配置する。これはaws s3 cp
コマンドで設定。
cp
コマンドと同じなので、具体的には次のようにした。
aws s3 cp ローカルのindex.html s3://aws-modern-application-workshop-kobori/index.html
これでウェブサイトが構築できた。というかこんなに簡単なのか。心配なのは金額だけ。
調子がよかったので「モジュール 2: ウェブサーバーでのアプリケーションのホスト」もちょっと読んでみよう。実際のメモは明日以降に。
モジュール2「静的ウェブサイトの構築」
(前提)各サービス、用語の概要
- AWS CloudFormation: Amazon Web Servicesリソースのモデル化およびセットアップのサービス。インフラストラクチャ管理を簡略化する。
- NLB: Network Load Balancer
- Amazon ECR: Elastic Container Registry、完全マネージド型のDockerコンテナレジストリです。このレジストリを使うと、開発者はDockerコンテナイメージを簡単に保存、管理、デプロイできます。
- Amazon ECS: Elastic Container Service、フルマネージド型のコンテナオーケストレーションサービスです。
- AWS CodeCommit: Gitベースのリポジトリをセキュアにホストする完全マネージド型のソース管理サービスです。
- AWS CodeBuild: ソースコードをコンパイルし、テストを実行し、デプロイ可能なソフトウェアパッケージを作成できる完全マネージド型のビルドサービスです。
- AWS CodePipeline: 完全マネージド型の継続的デリバリーサービスで、素早く確実性のあるアプリケーションとインフラストラクチャのアップデートのための、パイプラインのリリースを自動化します
モジュール2A: CloudFormationのテンプレートをデプロイする
aws cloudformation create-stack
コマンドで、ローカルにあるテンプレートのymlとIAMリソースをつかってスタックを作成する。
aws cloudformation create-stack --stack-name MythicalMysfitsCoreStack --capabilities CAPABILITY_NAMED_IAM --template-body file://module-2/cfn/core.yml
スタックとは、
スタックは、単一のユニットとして管理できる AWS リソースのコレクションです。 つまり、スタックを作成、更新、削除することで、リソースのコレクションを作成、更新、削除できます。 … リソースが作成できない場合、AWS CloudFormation はスタックをロールバックし、作成されたリソースを自動的に削除します。
ちなみにスタックをつくるときにIAMロール(ポリシー)でつまづいた。EC2とIAMに関してフルアクセスの権限を付与して解決したが、ベストプラクティスがわからない。このあたりを勉強すべきか迷う。
モジュール2B: サービスをNLB経由でデプロイ・公開する
aws ecr create-repository
コマンドでリポジトリを作成する。AmazonEC2ContainerRegistryFullAccessの権限を付与して実行した(FullAccessがベストではない気がするのだが、コマンドとポリシー、パーミッションの関係が見つけられない)。
このあとDockerログインをするのだが、aws ecr get-login
コマンドはセキュリティの関係から(コマンド履歴にパスワードが残る)使えなくなっていた。そのためこちらを参考にして、aws ecr get-login-password
コマンドでdocker login
を進めた。IssueかPRを出せるといいな。
つぎにECRにプッシュしたイメージをECSにデプロイする。
aws ecs create-cluster
でサーバのクラスター、aws logs create-log-group
でロググループをそれぞれ作成する。
与えられたテンプレートを使って、aws ecs register-task-definition
コマンドでECSタスク定義を登録する。
つぎにサービススタックに必要なインフラストラクチャをプロビジョニングする。
プロビジョニングとは、必要に応じてネットワークやコンピューターの設備などのリソースを提供できるよう予測し、準備しておくことです
https://www.idcf.jp/words/provisioning.html
ただ単純に公開するのではなくNLBをかませることで、実際のサービスのスケールイン/スケールアウトを容易にできる。
というわけでaws elbv2 create-load-balancer
コマンドでロードバランサーを作成する。サブネットはCloudFormationで作成したスタックから利用。
次にaws elbv2 create-target-group
コマンドでNLBターゲットグループ、aws elbv2 create-listener
コマンドでNLBロードバランサーのリスナーをそれぞれ作成。これで80番ポートが受信したリクエストをターゲットグループに登録されているターゲットに転送することをロードバランサーに知らせる。あまり意味はわかっていないけど、とりあえず書いておこう。
よくわからないけど、はじめてECSを利用する場合はaws iam create-service-linked-role --aws-service-name ecs.amazonaws.com
が必要なよう。
ECSで実際のサービスを作成するため、aws ecs create-service
コマンドを実行する
aws ecs create-service --cli-input-json file://service-definition.json
これで実際のサービス(Flask)にNLBを介してアクセスできるようになった。NLBのDNSでcurlを飛ばしてみると、実際にレスポンスが帰ってくる。
ちなみに最初に時間がかかったのはコンテナを作成・起動しているからか。2度目は一瞬だったので。
このサービスを利用するようにフロントエンドのindex.htmlを修正して、モジュールBは終了。
モジュール2C: CD/CIの設定
上記のまま開発を続けると、デプロイする際にはイメージを更新、プッシュして、またタスク定義を更新しないといけなさそう(その方法はチュートリアルにない)。
そのためCD/CIを設定する。
まずCI/CDアーティファクト用のS3バケットをつくる。バケットポリシーはチュートリアル側で用意してくれたものを使う。GETとPUTを許可しているみたい。
つぎにaws codecommit create-repository
でCodeCommitリポジトリ、aws codebuild create-project
でCodeBuildプロジェクトを作成。いろんな値をコピペするので忘れても調べられるようにしないといけないな。
最後に、CodeBuildプロジェクトを使用してCodeCommitリポジトリを継続的に統合することと、新しくビルドされたアーティファクトをECSのサービスに継続的に提供するサービスとして、AWS CodePipelineをつくる。
aws codepipeline create-pipeline
コマンドで、CodePipelineのパイプラインを作成。
そしてCodeBuildがECRリポジトリにアクセスできるよう設定する。具体的にはaws ecr set-repository-policy
コマンドでECRリポジトリのポリシーを設定する。
これで使えるようになったのでテストをする。gitの認証まわりに関する設定を行った後、codecommitの空リポジトリをクローンして、今回のアプリケーションのソースをcommitしてpush。
CodePipelineのコンソールで自動ビルドが行われていることを確認して完了。
モジュール3「情報の保存」
テーブル、データの追加
aws dynamodb create-table
コマンドでデータベースを作成。aws dynamodb scan
コマンドでテーブルの中身が見れる(見れるということは作成に成功している証拠)。
aws dynamodb batch-write-item
コマンドでレコードを追加する。scan
して追加されたことを確認。
ウェブサイトの更新
つぎにこのDBを使うようにアプリを直してcommit, pushしたのだが、403エラーで失敗してしまった。
どうやらHTTPSでやるとMacのキーチェーンの問題があるようなので、SSHを使って回避するようにした。具体的には、
- IAMユーザの「AWS CodeCommit の SSH キー」に公開鍵を設定する
- SSHキーIDが発行されるので、これを使った~/.ssh/configを設定(下記参照)
ssh git-codecommit.ap-northeast-1.amazonaws.com
で疎通確認
`~/.ssh/config
Host git-codecommit.*.amazonaws.com
User <SSHキーID>
IdentityFile ~/.ssh/id_rsa
またフロントのindex.htmlも更新。
パイプラインが成功したらサイトを確認。API、DBを使ってリストを描画していることを確認して終了。
## モジュール4「ユーザ登録の設定」
### (前提)Amazon Cognitoとは
ウェブアプリケーションおよびモバイルアプリに素早く簡単にユーザーのサインアップ/サインインおよびアクセスコントロールの機能を追加する。
### ユーザプールの作成
`aws cognito-idp create-user-pool`コマンドで、Cognitoユーザープールを作成する。オプションも入れると次の通り。
```bash
aws cognito-idp create-user-pool --pool-name MysfitsUserPool --auto-verified-attributes email
またユーザプールとセットで、aws cognito-idp create-user-pool-client
コマンドで、ユーザープールクライアントも作成する。
aws cognito-idp create-user-pool-client --user-pool-id <ユーザプールのID> --client-name MysfitsUserPoolClient
“Id”: “ap-northeast-1RoHXOwkFB”, “Arn”: “arn:aws:cognito-idp:ap-northeast-1:365487138721:userpool/ap-northeast-1RoHXOwkFB”
{ “UserPoolClient”: { “UserPoolId”: “ap-northeast-1_RoHXOwkFB”, “ClientName”: “MysfitsUserPoolClient”, “ClientId”: “71gov86tiiphs9amjmd6kjopje”, “LastModifiedDate”: “2020-09-20T09:36:05.872000+09:00”, “CreationDate”: “2020-09-20T09:36:05.872000+09:00”, “RefreshTokenValidity”: 30, “AllowedOAuthFlowsUserPoolClient”: false } }
REST APIとAmazon API Gatewayの設定
NLBの前にAmazon API Gatewayをはさんで、GatewayがCognitoを利用するようにする。またGatewayがNLBとプライベートで疎通するように、API Gateway VPCリンクも必要となる。
まずaws apigateway create-vpc-link
コマンドでNLBへのVPCリンクを作成。
aws apigateway create-vpc-link --name MysfitsApiVpcLink --target-arns <NLBのarn> > api-gateway-link-output.json
# いまさらだけどarnとは"Amazonリソースネーム"のこと。AWSリソースを一意に識別する。
つぎにaws apigateway import-rest-api
コマンドで
aws apigateway import-rest-api --parameters endpointConfigurationTypes=REGIONAL --body file://api-swagger.json --fail-on-warnings
しかし”Invalid base64”でエラーになってしまった。調べると、
AWS CLIバージョン2では、デフォルトでバイナリパラメータをbase64エンコードされた文字列として渡すようになったことが原因でした。
https://dev.classmethod.jp/articles/got-into-about-blob-type-arguments/
ということでjsonファイルがエンコードされないように(エンコードされていないバイナリとして扱うように)、fileb://
指定で実行すると成功した。
{ “id”: “8wx0hv82z5”, “name”: “MysfitsApi”, “createdDate”: “2020-09-20T09:59:39+09:00”, “apiKeySource”: “HEADER”, “endpointConfiguration”: { “types”: [ “REGIONAL” ] } }
このAPIはまだデプロイされていない。そのためaws apigateway create-deployment
コマンドで(本番ステージの)デプロイを作成する。
aws apigateway create-deployment --rest-api-id <さきほど作成したREST APIのID> --stage-name prod
するとAPIがhttps://<REST APIのID>.execute-api.ap-northeast-1.amazonaws.com/prod
で利用できる。末尾にmysfits
をつけて、レスポンスが帰ってくる(NLBを経由している)ことを確認。
ウェブサイトの更新
用意してくれているソースに更新してpush。
CodePipelineが始まったのだが、ビルドが失敗してしまった。調べてみるとDockerfileが壊れていたので、もとに戻して再度ビルド。
CodePipelineが完了したことを確認して、フロントエンドも修正。
サイトにアクセスして、ユーザ登録。認証メールが送られてくるので、記載のコードを入れて登録完了。
Amazon Cognitoのユーザプールを見てみると、実際にユーザが追加されていることがわかる。
モジュール5「ユーザー行動の把握」
チュートリアルを始める時点では、何をつくろうとしているのかわからなかった。
とりあえずこれまでと同様に、新しいサービスをつくるのでCodeCommitリポジトリ、スタックを作成、デプロイするようだ。
ストリーミングサービスのコードを作成してコミット
aws codecommit create-repository
コマンドで、CodeCommitリポジトリを作成。
aws codecommit create-repository --repository-name MythicalMysfitsStreamingService-Repository
cloneUrlが発行されるので、これをもとに空のgitリポジトリをcloneする。用意してもらっているソースとCloudFormationテンプレートをリポジトリにコピー。
また外部ライブラリ(requests)をLambdaで使えるように、ライブラリをプロジェクト内にインストールしたい。そのとき使えるのがpip install
の-t
オプション。
pip install requests -t .
スタックを作成
aws s3 mb
コマンドで、S3バゲットを作成。名前はaws-modern-application-workshop-kobori-streaming
とした。
aws s3 mb s3://aws-modern-application-workshop-kobori-streaming
つぎにsam package
コマンドで、コードをパッケージ化してアップロードする。
sam package --template-file ./real-time-streaming.yml --output-template-file ./transformed-streaming.yml --s3-bucket aws-modern-application-workshop-kobori-streaming
そしてaws cloudformation deploy
コマンドで、スタックをデプロイ
aws cloudformation deploy --template-file transformed-streaming.yml --stack-name MythicalMysfitsStreamingStack --capabilities CAPABILITY_IAM
aws cloudformation describe-stacks
コマンドで、デプロイしたスタックのAPI Gatewayエンドポイントを確認する。
最後にフロントサイトを更新して、サイトを確認
【重要】ワークショップのクリーンアップ
aws cloudformation delete-stack
コマンドで、今回つくったスタック・デプロイを削除しておくこと。そうしないと請求が発生するため。
aws cloudformation delete-stack --stack-name MythicalMysfitsStreamingStack
aws cloudformation delete-stack --stack-name MythicalMysfitsCoreStack