- 投稿日:2019-05-06T22:39:09+09:00
Lambda から Google API を呼ぶと権限ファイルの書き込みで `[Errno 30] Read-only file system` になる
背景
- 以下の記事に沿って Gmail API を叩く Python スクリプトを作成
- スクリプトを Lambda で動くようにして、実行したところ
[Errno 30] Read-only file system
が発生
- ローカルで実行したときには起きなかった
原因
- 権限ファイル (client_id.json) の書き込みでエラーになっている模様
- エラーのスタックトレースを見ると、oauth2client ライブラリ内で発生しているようだった
"errorMessage": "[Errno 30] Read-only file system: 'client_id.json'", : [ "/var/task/oauth2client/file.py", 79, "locked_put", "f = open(self._filename, 'w')" ], :
self._filename
は Storage クラスのコンストラクタ引数
- https://github.com/googleapis/oauth2client/blob/master/oauth2client/file.py#L33
- 作成したスクリプトでは client_id.json のパスを渡しており、client_id.json はスクリプトと同じディレクトリに置いていた
- Lambda が書き込みできるのは
/tmp
配下のファイルのみなので、Read-only エラーとなった対応
- client_id.json を一旦
/tmp
に退避させてから Storage クラスを作成するbeforeuser_secret_file = 'client_id.json' store = Storage(user_secret_file) credentials = store.get()afterimport shutil : user_secret_file = 'client_id.json' user_secret_tmp_path = '/tmp/' + user_secret_file shutil.copy2(user_secret_file, user_secret_tmp_path) store = Storage(user_secret_tmp_path) credentials = store.get()参考
- 投稿日:2019-05-06T22:33:05+09:00
RailsアプリをHerokuでデプロイする際の注意点
はじめに
初投稿です。本とネット教材で半年学んだ程度のヒヨッコです。
AWS cloud9でRuby on Railsプロジェクトを作成し、GitHub経由でHerokuでデプロイしました。
その際に発生したエラー・注意点を備忘録として投稿させていただきます。Herokuの注意点
Herokuではsqlite3が使えないようなので、本番環境用のgemを別に設定する必要があります。
以下リンクを参考にPostgreSQLを入れました。
(参考)HerokuではSQLite3が使えない?! https://qiita.com/MosamosaPoodle/items/7149aa66f1c087472777
・Rails Gemfileを修正
Gemfilegem 'sqlite3'↓変更する
Gemfilegem 'sqlite3', groups: %w(test development), require: false gem 'pg', groups: %w(production), require: false・Rails Gemfile.lockを削除しbundle installし直す
Terminal$ rm Gemfile.lock $ bundle installこれでRails上の設定は完了です。
AWS cloud9からgit push時の注意点
安全性の観点から、GitHubを二段階認証で管理すべきなのですが、
そうするとcloud9からpushする際にGitHubアカウントパスワードを入力してもエラーになります。TerminalUsername for 'https://github.com': hoge-user Password for 'https://hoge-user@github.com': remote: Invalid username or password. fatal: Authentication failed for 'https://github.com/hoge-user/github-repository-name.git/'対策として
・GitHubでPersonal Access tokensを発行し、tokenをPassword欄に入力する(https接続)
・SSH接続するよう設定を変更する
の2つがありますが、今回は前者を選びました。
(参考)GitHub「Personal access tokens」の設定方法 https://qiita.com/kz800/items/497ec70bff3e555dacd0ちなみに個人的にハマった罠が2つありますのでそちらも書いておきます。
1. Terminal上のPassword入力時は、画面は反応しないが文字はちゃんと入力されている。心配せずに入力する!
2. コピーした文字をTerminal上にペーストするときはcommand+v (Ctrl+v)で入力しない。右クリック→Pasteでペーストする。成果
半年間HTML, CSS, JavaScript, Ruby, Rails, AWS cloud9などと悪戦苦闘しましたが、素人でもHerokuでデプロイできました。
飲み会のお会計をシチュエーション別で傾斜計算する簡単なツールです。
Warikan - https://warikanappbygmnori.herokuapp.com/デザインや計算方法の拙さは本人が一番実感しています。次はもっと良いものを作れるよう頑張ります。
参考リンク
HerokuではSQLite3が使えない?! https://qiita.com/MosamosaPoodle/items/7149aa66f1c087472777
GitHub「Personal access tokens」の設定方法 https://qiita.com/kz800/items/497ec70bff3e555dacd0
- 投稿日:2019-05-06T22:33:05+09:00
【初学者向け】RailsアプリをHerokuでデプロイする際の注意点
はじめに
初投稿です。本とネット教材で半年学んだ程度のヒヨッコです。
AWS cloud9でRuby on Railsプロジェクトを作成し、GitHub経由でHerokuでデプロイしました。
その際に発生したエラー・注意点を備忘録として投稿させていただきます。Herokuの注意点
Herokuではsqlite3が使えないようなので、本番環境用のgemを別に設定する必要があります。
以下リンクを参考にPostgreSQLを入れました。
(参考)HerokuではSQLite3が使えない?! https://qiita.com/MosamosaPoodle/items/7149aa66f1c087472777
・Rails Gemfileを修正
Gemfilegem 'sqlite3'↓変更する
Gemfilegem 'sqlite3', groups: %w(test development), require: false gem 'pg', groups: %w(production), require: false・Rails Gemfile.lockを削除しbundle installし直す
Terminal$ rm Gemfile.lock $ bundle installこれでRails上の設定は完了です。
AWS cloud9からgit push時の注意点
安全性の観点から、GitHubを二段階認証で管理すべきなのですが、
そうするとcloud9からpushする際にGitHubアカウントパスワードを入力してもエラーになります。TerminalUsername for 'https://github.com': hoge-user Password for 'https://hoge-user@github.com': remote: Invalid username or password. fatal: Authentication failed for 'https://github.com/hoge-user/github-repository-name.git/'対策として
・GitHubでPersonal Access tokensを発行し、tokenをPassword欄に入力する(https接続)
・SSH接続するよう設定を変更する
の2つがありますが、今回は前者を選びました。
(参考)GitHub「Personal access tokens」の設定方法 https://qiita.com/kz800/items/497ec70bff3e555dacd0ちなみに個人的にハマった罠が2つありますのでそちらも書いておきます。
1. Terminal上のPassword入力時は、画面は反応しないが文字はちゃんと入力されている。心配せずに入力する!
2. コピーした文字をTerminal上にペーストするときはcommand+v (Ctrl+v)で入力しない。右クリック→Pasteでペーストする。成果
半年間HTML, CSS, JavaScript, Ruby, Rails, AWS cloud9などと悪戦苦闘しましたが、素人でもHerokuでデプロイできました。
飲み会のお会計をシチュエーション別で傾斜計算する簡単なツールです。
Warikan - https://warikanappbygmnori.herokuapp.com/デザインや計算方法の拙さは本人が一番実感しています。次はもっと良いものを作れるよう頑張ります。
参考リンク
HerokuではSQLite3が使えない?! https://qiita.com/MosamosaPoodle/items/7149aa66f1c087472777
GitHub「Personal access tokens」の設定方法 https://qiita.com/kz800/items/497ec70bff3e555dacd0
- 投稿日:2019-05-06T21:18:15+09:00
Amazon WorkSpacesで利用できるショートカットキー
WorkSpacesに接続した際、クライアントからアクセスした時とブラウザからアクセスした時で挙動が異なっていたため、まとめました。
試した環境
- バンドル:Standard with Windows 10
- Amazon WorkSpacesクライアント:Version 2.5.5.1172
- Amazon WorkSpaces web client:version 2.4.1
挙動
一般的なWindows端末と同様の挙動をしたところは〇、そうでないところには挙動を記載しました。
ショートカットキー 目的 クライアント ブラウザ Ctrl + C 選択した項目をコピーする。 〇 〇 Ctrl + V 選択した項目を貼り付ける。 〇 〇 Alt + Tab ウィンドウを切り替える。 〇 作業端末でアプリの切り替えが行われる。 Windows ロゴ キー + L PC をロックする。 作業端末とクライアントの両方でロックがかかる。 作業端末のみロックがかかり、WorkSpace内ではスタートメニューが開く。 Windows ロゴ キー + A アクションセンターを開く。 〇 アクションセンターが開くと同時にスタートメニューも開く。 Windows ロゴ キー + ↑ ウィンドウを最大化する。 〇 ウィンドウの大きさは変わらず、スタートメニューが開く。 Windows ロゴ キー + P プレゼンテーション表示モードを選択する。 〇 作業端末のみプレゼンテーション表示モードの選択画面が開き、WorkSpace内ではスタートメニューが開く。 F2 選択された項目の名前を変更する。 〇 〇 Ctrl + Alt + Tab 方向キーを使って、ウィンドウを切り替える。 〇 作業端末でアプリの切り替えが行われる。 Ctrl + Alt + delete Windowsのセキュリティやタスクマネージャを呼び出す。 作業端末とクライアントの両方で呼び出される。 作業端末のみ呼び出される。
- 投稿日:2019-05-06T19:21:10+09:00
メモ CodeBuild docker on docker -> ECR pushなど
やること
※この記事には画像がなく痩せた大地のようなのでCodeBuildでツラまった時に見るとかがいい
- マネージドイメージ内でmvnビルド、生成されたjarをS3にアップロード AWS CodeBuildの使用開始
- カスタムイメージ(自作dockerイメージ)内でmvnビルド、生成されたjarをS3にアップロード
- マネージドイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
- カスタムイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
最終的にやりたかった事は自前のdockerイメージ内でビルドして、そのイメージをECRにpushすること
詰まった時の問題点を特定しやすくするために段階を分割してる
1でCodeBuildの設定などを把握、2でカスタムイメージ利用、3でdockerイメージビルドからpush、4で2と3を合わせるCodeBuildからECRへのpull/push権限とdocker in docker周りが理解できれば詰まることは無くなると思う
CodeBuildはUI上からビルド開始出来る、GitHubからのwebhookなどは検証してないけどそれも出来る(サンプルにはある)
サンプルが多いのでとりあえず迷ったらサンプル集を見てみたら解決しそう、ドキュメントが揃っていてえらい
CodeBuildサンプル環境
- AWSマネジメントコンソールにログイン出来る (ECR/IAM/CodeBuild/S3周りの権限がある)
- dockerイメージがローカルでビルド出来る
- AWS CLIがローカルで使える
作業
マネージドイメージ内でmvnビルド、生成されたjarをS3にアップロード
まずはCodeBuildのイメージを掴むためにAWSが用意しているチュートリアルをやる
本家を見たほうが参考になると思う
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/getting-started.htmlやること
- s3 input/outputの定義
- ビルド用プロジェクトの作成/buildspec.yml作成/s3アップロード
- CodeBuild ビルドプロジェクト作成
- ビルド実行
- ビルドログ/成果物確認
ではやっていく
S3バケット作成 input/output用
このサンプルではソースコード取得/成果物出力をS3バケットに行うのでそれ用のバケットを作成する
今回の名前はこんな感じ、名前は好きに付けてOK
入力用バケット:
codebuild-AWSアカウントID-yyyyMMdd-input-bucket
出力用バケット:codebuild-AWSアカウントID-yyyyMMdd-output-bucket
入力用バケットはCodeBuildの
Source
で
出力用バケットはCodeBuildのArtifacts
で指定する※バケットが1つでも動作するが確認しやすいので入力出力を分けているらしい
※CodeBuildプロジェクトとS3バケットは同一リージョンに作成する必要があるビルド用mvnプロジェクト作成
ローカルの好きなところにmvnプロジェクト用ディレクトリを作成する
mvnプロジェクトの作成mkdir codebuild_lesson cd codebuild_lesson mkdir -p mkdir -p ./src/main ./src/test touch src/main/MessageUtil.java src/test/TestMessageUtil.java touch pom.xmlそれぞれのファイルに追記
実行しないので正直中身はビルドできれば何でもいい
サンプル通りに貼り付けるMessageUtil.javapublic class MessageUtil { private String message; public MessageUtil(String message) { this.message = message; } public String printMessage() { System.out.println(message); return message; } public String salutationMessage() { message = "Hi!" + message; System.out.println(message); return message; } }TestMessageUtil.javaimport org.junit.Test; import org.junit.Ignore; import static org.junit.Assert.assertEquals; public class TestMessageUtil { String message = "Robert"; MessageUtil messageUtil = new MessageUtil(message); @Test public void testPrintMessage() { System.out.println("Inside testPrintMessage()"); assertEquals(message,messageUtil.printMessage()); } @Test public void testSalutationMessage() { System.out.println("Inside testSalutationMessage()"); message = "Hi!" + "Robert"; assertEquals(message,messageUtil.salutationMessage()); } }pom.xml<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>messageUtil</artifactId> <version>1.0</version> <packaging>jar</packaging> <name>Message Utility Java Sample App</name> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> </project>ここまででmvnビルド用の用意は完了
次にCodeBuildビルド仕様ファイルの追加
アプリケーションルートにbuildspec.yml
というファイルを作成
この名前はCodeBuildが認識するためこの名前にする必要がある(プロジェクト側の設定でビルド仕様ファイル名を任意の名前に設定できるが特に問題がない場合そのままで良い)touch buildspec.ymlbuildspec.ymlversion: 0.2 phases: install: commands: - echo Nothing to do in the install phase... pre_build: commands: - echo Nothing to do in the pre_build phase... build: commands: - echo Build started on `date` - mvn install post_build: commands: - echo Build completed on `date` artifacts: files: - target/messageUtil-1.0.jarビルドバージョンは最新の
0.2
を指定
phases
にそれぞれあるinstall
、pre_build
、build
、post_build
の4つがビルドフェーズとして存在する、名前はこれ以外に設定できないし新しいフェーズを追加することも出来ない
必要のないフェーズを省略することも出来るがecho
で特にやることがない的なメッセージを出力しておいて必要になったら追記するような感じでやるとフェーズ名を調べる手間が省けそう
commands
は実行したいLinuxコマンド
artifacts
は成果物を設定した出力場所にどのファイルを配置するかの設定、このサンプルでは生成されたjarをS3に出力するがS3という設定はCodeBuildプロジェクト側で設定するmvnプロジェクトをzipしてinput用s3バケットにアップロード
アプリケーションルート以下のファイルをzipして
MessageUtil.zip
というファイル名にするzip -r MessageUtil.zip ./生成されたファイルを
codebuild-AWSアカウントID-yyyyMMdd-input-bucket/MessageUtil.zip
にアップロードする今回の全サンプルのソースはS3から取得するためビルド仕様ファイルの仕様をあまり読んでないが困ったらこれを読めば良さそう
CodeBuild のビルド仕様に関するリファレンスビルドプロジェクトを作成する
入力/プロジェクト/出力が揃ったのでようやくCodeBuildプロジェクトを作成する
AWSコンソールからCodeBuildのプロジェクト作成ボタンを押す以下設定の入力項目と値
特に書いていない箇所はデフォルト設定Project name:
codebuild-AWSアカウントID-yyyyMMdd-mvn-project
Source provider:Amazon S3
バケット:codebuild-AWSアカウントID-yyyyMMdd-input-bucket
S3 object key or S3 folder:MessageUtil.zip
Environment image:Managed image
Operating system:Ubuntu
Runtime(s):Standard
Image:aws/codebuild/standard:1.0
Artifacts Type:Amazon S3
Artifacts Bucket name:codebuild-AWSアカウントID-yyyyMMdd-output-bucket
Logs CloudWatch Logs:チェックする
Logs Group name:codebuild-AWSアカウントID-yyyyMMdd-mvn-project-log
サンプルと違う箇所はログ名を付けたくらい
この状態で作成するビルドする
ビルドプロジェクトを作成したので後はUI上でビルドを開始して結果を確認するだけ
AWSコンソールから先程作成したビルドプロジェクトに入って
Start build
->Start build
でビルド開始
ビルド中はタブでBuild logs
やPhase details
などあるので確認するPhase detailsで全ての工程がSucceededになったらS3にjarが出力されているか確認する
出力用S3バケットには以下のように出力される
codebuild-AWSアカウントID-yyyyMMdd-output-bucket/codebuild-AWSアカウントID-yyyyMMdd-mvn-project/target/messageUtil-1.0.jar
ここまではほぼサンプル通りのビルドなので特に問題は無いと思う
カスタムイメージ(自作dockerイメージ)内でmvnビルド、生成されたjarをS3にアップロード
次はカスタムイメージをビルド環境にしてみる
対応するCodeBuildサンプルは以下
CodeBuildのAmazon ECRサンプル
サンプルはGoプロジェクトになっているが、前回のmvnプロジェクトを使用するやること
- ECRリポジトリの作成
- ローカルでmvnがビルドできるdockerイメージの作成
- dockerイメージをECRにpush
- CodeBuildプロジェクトでpushしたdockerイメージを利用するように変更
- ECRリポジトリにCodeBuild実行roleからのイメージ取得権限を設定
注意点としては、ECRリポジトリにポリシー設定を行わないと同じAWSアカウントからでもpullが出来ないのでビルド時にエラーが出る
それ以外は特に前回と変わらないので前回と同じプロジェクトを編集していくmvnビルド出来るdockerイメージの作成
その前にECRにリポジトリを作成しておく
UIからでもCLIからでもいいので名前をmvn_for_ecr
としておくアプリケーションルートにDockerfile用ディレクトリを作成する
個人的にdockerfiles
というディレクトリにそれぞれのDockerfileを置いているのでそうするmkdir -p dockerfiles/mvn_for_ecr touch dockerfiles/mvn_for_ecr/DockerfileDockerfileの中身は以下
DockerhubのmavenをpullしてECR push用にタグ付けすれば問題なく動きそうだけど自分で色々出来るように一応Dockerfile書くdockerfiles/mvn_for_ecr/DockerfileFROM java:8-jdk-alpine WORKDIR /tmp RUN apk update && apk add openssl && apk add curl RUN curl -OL https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven-3.6.1-bin.tar.gz RUN tar -xzvf apache-maven-3.6.1-bin.tar.gz RUN mv apache-maven-3.6.1 /usr/local/bin/ ENV PATH $PATH:/usr/local/bin/apache-maven-3.6.1/bin RUN echo $PATH RUN which mvn WORKDIR /app CMD ["/bin/ash"]イメージのビルド/タグ付け/push
※ECRにpush出来るようにaws ecr get-loginでdockerログインしておくaws ecr get-login --region リージョン --no-include-email > docker login -u AWS -p ~~~~~~ https://AWSアカウントID.dkr.ecr.リージョン.amazonaws.com docker build -t mvn_for_ecr ./dockerfiles/mvn_for_ecr docker tag mvn_for_ecr:latest AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.0.0 docker push AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.0.0pushが完了したらAWSコンソール上でECRのリポジトリを確認する、1.0.0があればOK
CodeBuildプロジェクトでpushしたdockerイメージを利用するように変更
AWSコンソールで
codebuild-AWSアカウントID-yyyyMMdd-mvn-project
に入り編集
->Environment
を選択してEdit Environment
画面に入る
Override image
を押下New environment image:
Custom image
Environment type:Linux
Image registry:Amazon ECR
ECR account:My ECR account
Amazon ECR repository:mvn_for_ecr
Amazon ECR image:1.0.0
Image pull credentials:Project service role
Service role:codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role
※多分ロールの名前はこんな感じポイントは
Image pull credentials
をProject service role
に設定するところ
後でECRリポジトリにポリシーで、指定しているロールからのアクセスを許可することでイメージをpull出来るようにするここまでの設定で一旦ビルドしてみる
※ECRリポジトリのポリシーを設定していないのでエラーが出る、一回リポジトリのポリシーを有効にすると数時間間違った設定でもSucceededになるので注意
こんな感じ at Phase detailsBUILD_CONTAINER_UNABLE_TO_PULL_IMAGE: Unable to pull customer's container image. CannotPullContainerError: Error response from daemon: pull access denied for AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr, repository does not exist or may require 'docker login'次にECRリポジトリ
mvn_for_ecr
のポリシーを以下のように設定する
PermissionsというリンクをクリックするとポリシーJSONを編集と出るのでそこで行うECRリポジトリのポリシー{ "Version": "2008-10-17", "Statement": [ { "Sid": "mvn_for_ecr_policy", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::AWSアカウントID:role/service-role/codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role" }, "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer" ] } ] }特定のロールに対してECR取得系のアクションを許す
これを保存してもう一度CodeBuildでビルドしてみると全てSucceededになると思う
ならなかったらCodeBuildのenvironmentを同じ設定で保存し直すといけるかも、検証してないマネージドイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
次はdockerイメージをビルドしてECRに対してpushする
CodeBuildサンプルは以下
CodeBuildのDockerサンプルやること
- ECRリポジトリの作成(push用)
- 作成したECRリポジトリのポリシー設定
- buildspec.ymlの変更
- イメージ作成用のDockerfile作成
- プロジェクトをS3へ再アップロード
- CodeBuildのEnvironment設定
dockerイメージをCodeBuildからECRにpushするのでECRリポジトリのポリシーにpush用の権限を設定する必要がある
dockerコマンドで利用する環境変数をCodeBuildプロジェクトのEnvironmentsから設定するECRリポジトリの作成
push用リポジトリを作成する
名前はbuild_from_codebuild_echoalpine
にする
今回はmvnコマンドを利用せずに単純にdocker build
やdocker push
がちゃんと行えるようになるかを確認していくUIやCLIから作成する
build_from_codebuild_echoalpine
のポリシー設定を行うビルドしたイメージをpushするリポジトリのポリシーJSON
pushを行うので更新系のアクションを許可するようにする
こんな感じ、ロール名は予測なのでサジェストに出てきた対応するやつを使うbuild_from_codebuild_echoalpine{ "Version": "2008-10-17", "Statement": [ { "Sid": "build_from_codebuild_echoalpine_policy", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::AWSアカウントID:role/service-role/codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role" }, "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:InitiateLayerUpload", "ecr:PutImage", "ecr:UploadLayerPart" ] } ] }buildspec.ymlの変更
今までのbuildspec.ymlだとmvnコマンドを実行してからartifactsでS3に成果物を置くようにしていたが、今回はビルドしたdockerイメージをECRにpushするのでそれ用に書き直す
buildspec.ymlversion: 0.2 phases: install: commands: - echo Logging in to commands... pre_build: commands: - echo Logging in to Amazon ECR... - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION) build: commands: - echo Build started on `date` - echo Building the Docker image... - echo $DOCKER_VERSION - which docker - pwd - ls -l - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAGdockerイメージを成果物として捉えるならartifactsに書きたくなるが、docker pushは
post_build
フェーズにcommandsとして書く
pre_build
フェーズでaws ecr get-login
コマンドがあるが、environmentsでマネージドイメージを選択するので実行可能になっている、嬉しい
いくつも環境変数を利用している、CodeBuildプロジェクトのenvironmentsで環境変数設定で定義する
build
フェーズのecho $DOCKER_VERSION
which docker
pwd
ls -l
などはなんか確認したくて書いてる、別に必要はないイメージ作成用のDockerfile作成
buildspec.yml内で
docker build
コマンドを実行しているのでDockerfileを作成する作成したdockerfilesディレクトリに置いたほうが恐らく良いが、怠惰男になったので一旦アプリケーションルートに置く、ゴメス
touch Dockerfile touch echo.shDockerfile# 成果物を実行させるイメージを生成する FROM alpine:3.9.3 ADD ./echo.sh /tmp/echo.sh CMD /tmp/echo.shecho.sh#!/bin/ash count=0 while true do echo "my alpine container build from CodeBuild!! 1.0.0" sleep 10 count=$(expr $count + 1) if [ $count -ge 10 ]; then echo "echoalpineの死!!" exit fi doneプロジェクトをS3へ再アップロード
CodeBuildプロジェクトに必要なファイルが揃ったので再びzipにしてinput用S3にアップロードする
zip -r MessageUtil.zip ./S3にアップロードする
codebuild-AWSアカウントID-yyyyMMdd-input-bucket/MessageUtil.zip
CodeBuildのEnvironment設定
後はCodeBuildの設定を更新してビルドすれば問題ないはず、恐れずに行け。
AWSコンソールの作成しているプロジェクト ->
編集
->Environment
Edit Environment
画面で以下の設定にする
Override image
を押下してNew environment image:
Managed image
Operating system:Ubuntu
Runtime(s):Standard
Image:aws/codebuild/standard:1.0
Image version:latest
Privileged:チェックする
Service role:arn:aws:iam::AWSアカウントID:role/service-role/codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role
追加設定を開いて
Environment variables
を追加していく、以下key
:value
で全てPlaintext
AWSアカウントIDは自分のに変更、リージョンも今まで指定してきたリージョンに変更
IMAGE_REPO_NAME
:build_from_codebuild_echoalpine
IMAGE_TAG
:latest
AWS_ACCOUNT_ID
:AWSアカウントID
AWS_DEFAULT_REGION
:ap-northeast-1
最も重要なのは
Privileged
にチェックを入れること、これをチェックしないとCannot connect to the Docker daemon
とエラーが出てdockerが使えない
Runtimeにはdockerを指定しないといけないと思いきやStandardでOKこれでECR push用の設定は完了したのでビルド実行
恐らくSucceededになるはず(記憶を頼りに書いてる)
ECRのpush対象リポジトリに入ってイメージが追加されているか確認
動作確認もしたければローカル等にpullしてrunする、ECR pushが目的だったので動作はあまり気にしてないけど
エラーが出たときはリポジトリのポリシーとCodeBuild environments設定のPrivilegedチェックを確認してみる
…散々Privilegedチェックを書いているけど、検証時はカスタムイメージでdocker:dind
を指定してたからもしかしたらマネージドイメージでは不要かもしれない…すみませんカスタムイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
さあ本命の登場だ
これまでのことを組み合わせると簡単に行けるだろうと思っていたがdocker on dockerなのでカスタムイメージ設定で苦戦したほぼ参考にしてないけどCodeBuild対応サンプル
CodeBuild のカスタム Docker イメージのサンプルやること
- カスタムイメージの作成
- buildspec.ymlの変更
- CodeBuildのEnvironmentsの変更
カスタムイメージではmvnを入れているけどdocker buildでは結局echoalpineをビルドしているという点は許してほしい、カスタムイメージ内でdockerが使えるようになればあとはどうにでもなる、何を作るかなど今はどうでも良いのだ…
カスタムイメージの作成
最も重要な点として、ベースイメージを
docker:dind
系にしないといけないということ
※dindはdocker in dockerの略
前回のベースイメージはjava:8-jdk-alpine
だったのでdind
にするとjdkをインストールしなければいけない
それではカスタムイメージのDockerfile編集dockerfiles/mvn_for_ecr/DockerfileFROM docker:18.09.5-dind WORKDIR /tmp RUN apk update && apk add openssl && apk add curl RUN apk add openssh RUN apk add bash # javaのダウンロー # ド RUN apk add openjdk8 RUN curl -OL https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven-3.6.1-bin.tar.gz RUN tar -xzvf apache-maven-3.6.1-bin.tar.gz RUN mv apache-maven-3.6.1 /usr/local/bin/ ENV PATH $PATH:/usr/local/bin/apache-maven-3.6.1/bin RUN echo $PATH RUN which mvn WORKDIR /app重要なのは
docker:18.09.5-dind
これ以外のイメージをFROMに指定して出来るもんなのか、dockerインストールするとか以外でDockerfile内でCMD指定してないのは
docker:18.09.5-dind
のCMDを利用するため、Dockerシェフは素材の味を活かす…
※そもそもCMD指定しなかったらFROMのCMDを利用するのか知らない、雰囲気でそうしてる、検証します…上記のDockerfileでCodeBuildでdockerコマンドが利用できるのはちゃんと確認したカスタムイメージ用のDockerfileを編集したのでビルドしてECRにpushする
ここはECRにpushするサンプルとほぼ同じなのでパラメータとかを適当なものに
ここではタグを1.1.0
にするaws ecr get-login --region リージョン --no-include-email > docker login -u AWS -p ~~~~~~ https://AWSアカウントID.dkr.ecr.リージョン.amazonaws.com docker build -t mvn_for_ecr ./dockerfiles/mvn_for_ecr docker tag mvn_for_ecr:latest AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.1.0 docker push AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.1.0buildspec.ymlの変更
カスタムイメージを作成したので次はその中でどんなコマンドを実行するかを変更していく
buildspec.ymlversion: 0.2 phases: install: commands: - echo Logging in to commands... - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2& - timeout -t 15 sh -c "until docker info; do echo .; sleep 1; done" pre_build: commands: - echo Logging in to Amazon ECR... - docker login -u AWS -p $ECR_LOGIN_PASS https://$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com build: commands: - echo Build started on `date` - echo Building the Docker image... - echo $DOCKER_VERSION - which docker - pwd - ls -l - which mvn - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
install
フェーズに追加しているnohup...
でホスト上のdockerをコンテナ上で利用できるようにしてる的なAWS CLIのインストールをサボってpull出来るように直接docker loginをかましているけどもっと良い方法があるはず(絶対)
そもそもこの方法だとECRへのdocker login有効期限12時間が経過する度に$ECR_LOGIN_PASS
を更新しないといけないのでダメ
mvnコマンドを利用しない代わりにとりあえずwhich mvn
でコマンドがあるかだけ確認しているbuildspec.ymlの編集が完了したらまたzipしてinput用S3バケットにアップロードする
zipコマンドだけzip -r MessageUtil.zip ./CodeBuildのEnvironmentsの変更
CodeBuildのenvironmentsを編集する
New environment image:
Custom image
Environment type:Linux
Image registry:Amazon ECR
ECR account:My ECR account
Amazon ECR repository:mvn_for_ecr
Amazon ECR image:1.1.0
Image pull credentials:Project service role
Privileged:チェックする
Service role:codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role
環境変数に以下を追加
ローカルでaws ecr get-login --region リージョン --no-include-email
を実行して-pの文字列をコピーしてくる
ECR_LOGIN_PASS
:コピーした文字列
これで更新
今回は絶対Privilegedチェック必要
ビルド実行してSucceededになってpush先のECRリポジトリが更新されていればOK
これまでの全てを組み合わせればエラーが出ても解決できるはずじゃ…(記憶とメモを頼りに書いてる)まとめ
後半になるにつれて雑になってきたけど多分要所は抑えているのでエラーが出たらサンプルを頼りにがんばる、ツラくなったらFutureFunkやElectroSwingを聞いて踊れ、この記事で伝えたい全てはそれだ。
困ったらBuild logsを見よう、問題はそこに出ている
AWSサービスは概念掴めばドキュメント見てterraform化とか出来るのでそこまでは
マルチステージビルドとかソース取得元とGitHubにしたりとかまだやりたいことはあるけどとりあえずこれで問題
定期的に権限の有効期限が切れているので解決する
参考
- 投稿日:2019-05-06T19:21:10+09:00
メモ CodeBuild docker in docker -> ECR pushなど
やること
※この記事には画像がなく痩せた大地のようなのでCodeBuildでツラまった時に見るとかがいい
- マネージドイメージ内でmvnビルド、生成されたjarをS3にアップロード AWS CodeBuildの使用開始
- カスタムイメージ(自作dockerイメージ)内でmvnビルド、生成されたjarをS3にアップロード
- マネージドイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
- カスタムイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
最終的にやりたかった事は自前のdockerイメージ内でビルドして、そのイメージをECRにpushすること
詰まった時の問題点を特定しやすくするために段階を分割してる
1でCodeBuildの設定などを把握、2でカスタムイメージ利用、3でdockerイメージビルドからpush、4で2と3を合わせるCodeBuildからECRへのpull/push権限とdocker in docker周りが理解できれば詰まることは無くなると思う
CodeBuildはUI上からビルド開始出来る、GitHubからのwebhookなどは検証してないけどそれも出来る(サンプルにはある)
サンプルが多いのでとりあえず迷ったらサンプル集を見てみたら解決しそう、ドキュメントが揃っていてえらい
CodeBuildサンプル環境
- AWSマネジメントコンソールにログイン出来る (ECR/IAM/CodeBuild/S3周りの権限がある)
- dockerイメージがローカルでビルド出来る
- AWS CLIがローカルで使える
作業
マネージドイメージ内でmvnビルド、生成されたjarをS3にアップロード
まずはCodeBuildのイメージを掴むためにAWSが用意しているチュートリアルをやる
本家を見たほうが参考になると思う
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/getting-started.htmlやること
- s3 input/outputの定義
- ビルド用プロジェクトの作成/buildspec.yml作成/s3アップロード
- CodeBuild ビルドプロジェクト作成
- ビルド実行
- ビルドログ/成果物確認
ではやっていく
S3バケット作成 input/output用
このサンプルではソースコード取得/成果物出力をS3バケットに行うのでそれ用のバケットを作成する
今回の名前はこんな感じ、名前は好きに付けてOK
入力用バケット:
codebuild-AWSアカウントID-yyyyMMdd-input-bucket
出力用バケット:codebuild-AWSアカウントID-yyyyMMdd-output-bucket
入力用バケットはCodeBuildの
Source
で
出力用バケットはCodeBuildのArtifacts
で指定する※バケットが1つでも動作するが確認しやすいので入力出力を分けているらしい
※CodeBuildプロジェクトとS3バケットは同一リージョンに作成する必要があるビルド用mvnプロジェクト作成
ローカルの好きなところにmvnプロジェクト用ディレクトリを作成する
mvnプロジェクトの作成mkdir codebuild_lesson cd codebuild_lesson mkdir -p ./src/main ./src/test touch src/main/MessageUtil.java src/test/TestMessageUtil.java touch pom.xmlそれぞれのファイルに追記
実行しないので正直中身はビルドできれば何でもいい
サンプル通りに貼り付けるMessageUtil.javapublic class MessageUtil { private String message; public MessageUtil(String message) { this.message = message; } public String printMessage() { System.out.println(message); return message; } public String salutationMessage() { message = "Hi!" + message; System.out.println(message); return message; } }TestMessageUtil.javaimport org.junit.Test; import org.junit.Ignore; import static org.junit.Assert.assertEquals; public class TestMessageUtil { String message = "Robert"; MessageUtil messageUtil = new MessageUtil(message); @Test public void testPrintMessage() { System.out.println("Inside testPrintMessage()"); assertEquals(message,messageUtil.printMessage()); } @Test public void testSalutationMessage() { System.out.println("Inside testSalutationMessage()"); message = "Hi!" + "Robert"; assertEquals(message,messageUtil.salutationMessage()); } }pom.xml<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>messageUtil</artifactId> <version>1.0</version> <packaging>jar</packaging> <name>Message Utility Java Sample App</name> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> </project>ここまででmvnビルド用の用意は完了
次にCodeBuildビルド仕様ファイルの追加
アプリケーションルートにbuildspec.yml
というファイルを作成
この名前はCodeBuildが認識するためこの名前にする必要がある(プロジェクト側の設定でビルド仕様ファイル名を任意の名前に設定できるが特に問題がない場合そのままで良い)touch buildspec.ymlbuildspec.ymlversion: 0.2 phases: install: commands: - echo Nothing to do in the install phase... pre_build: commands: - echo Nothing to do in the pre_build phase... build: commands: - echo Build started on `date` - mvn install post_build: commands: - echo Build completed on `date` artifacts: files: - target/messageUtil-1.0.jarビルドバージョンは最新の
0.2
を指定
phases
にそれぞれあるinstall
、pre_build
、build
、post_build
の4つがビルドフェーズとして存在する、名前はこれ以外に設定できないし新しいフェーズを追加することも出来ない
必要のないフェーズを省略することも出来るがecho
で特にやることがない的なメッセージを出力しておいて必要になったら追記するような感じでやるとフェーズ名を調べる手間が省けそう
commands
は実行したいLinuxコマンド
artifacts
は成果物を設定した出力場所にどのファイルを配置するかの設定、このサンプルでは生成されたjarをS3に出力するがS3という設定はCodeBuildプロジェクト側で設定するmvnプロジェクトをzipしてinput用s3バケットにアップロード
アプリケーションルート以下のファイルをzipして
MessageUtil.zip
というファイル名にするzip -r MessageUtil.zip ./生成されたファイルを
codebuild-AWSアカウントID-yyyyMMdd-input-bucket/MessageUtil.zip
にアップロードする今回の全サンプルのソースはS3から取得するためビルド仕様ファイルの仕様をあまり読んでないが困ったらこれを読めば良さそう
CodeBuild のビルド仕様に関するリファレンスビルドプロジェクトを作成する
入力/プロジェクト/出力が揃ったのでようやくCodeBuildプロジェクトを作成する
AWSコンソールからCodeBuildのプロジェクト作成ボタンを押す以下設定の入力項目と値
特に書いていない箇所はデフォルト設定Project name:
codebuild-AWSアカウントID-yyyyMMdd-mvn-project
Source provider:Amazon S3
バケット:codebuild-AWSアカウントID-yyyyMMdd-input-bucket
S3 object key or S3 folder:MessageUtil.zip
Environment image:Managed image
Operating system:Ubuntu
Runtime(s):Standard
Image:aws/codebuild/standard:1.0
Artifacts Type:Amazon S3
Artifacts Bucket name:codebuild-AWSアカウントID-yyyyMMdd-output-bucket
Logs CloudWatch Logs:チェックする
Logs Group name:codebuild-AWSアカウントID-yyyyMMdd-mvn-project-log
サンプルと違う箇所はログ名を付けたくらい
この状態で作成するビルドする
ビルドプロジェクトを作成したので後はUI上でビルドを開始して結果を確認するだけ
AWSコンソールから先程作成したビルドプロジェクトに入って
Start build
->Start build
でビルド開始
ビルド中はタブでBuild logs
やPhase details
などあるので確認するPhase detailsで全ての工程がSucceededになったらS3にjarが出力されているか確認する
出力用S3バケットには以下のように出力される
codebuild-AWSアカウントID-yyyyMMdd-output-bucket/codebuild-AWSアカウントID-yyyyMMdd-mvn-project/target/messageUtil-1.0.jar
ここまではほぼサンプル通りのビルドなので特に問題は無いと思う
カスタムイメージ(自作dockerイメージ)内でmvnビルド、生成されたjarをS3にアップロード
次はカスタムイメージをビルド環境にしてみる
対応するCodeBuildサンプルは以下
CodeBuildのAmazon ECRサンプル
サンプルはGoプロジェクトになっているが、前回のmvnプロジェクトを使用するやること
- ECRリポジトリの作成
- ローカルでmvnがビルドできるdockerイメージの作成
- dockerイメージをECRにpush
- CodeBuildプロジェクトでpushしたdockerイメージを利用するように変更
- ECRリポジトリにCodeBuild実行roleからのイメージ取得権限を設定
注意点としては、ECRリポジトリにポリシー設定を行わないと同じAWSアカウントからでもpullが出来ないのでビルド時にエラーが出る
それ以外は特に前回と変わらないので前回と同じプロジェクトを編集していくmvnビルド出来るdockerイメージの作成
その前にECRにリポジトリを作成しておく
UIからでもCLIからでもいいので名前をmvn_for_ecr
としておくアプリケーションルートにDockerfile用ディレクトリを作成する
個人的にdockerfiles
というディレクトリにそれぞれのDockerfileを置いているのでそうするmkdir -p dockerfiles/mvn_for_ecr touch dockerfiles/mvn_for_ecr/DockerfileDockerfileの中身は以下
DockerhubのmavenをpullしてECR push用にタグ付けすれば問題なく動きそうだけど自分で色々出来るように一応Dockerfile書くdockerfiles/mvn_for_ecr/DockerfileFROM java:8-jdk-alpine WORKDIR /tmp RUN apk update && apk add openssl && apk add curl RUN curl -OL https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven-3.6.1-bin.tar.gz RUN tar -xzvf apache-maven-3.6.1-bin.tar.gz RUN mv apache-maven-3.6.1 /usr/local/bin/ ENV PATH $PATH:/usr/local/bin/apache-maven-3.6.1/bin RUN echo $PATH RUN which mvn WORKDIR /app CMD ["/bin/ash"]イメージのビルド/タグ付け/push
※ECRにpush出来るようにaws ecr get-loginでdockerログインしておくaws ecr get-login --region リージョン --no-include-email > docker login -u AWS -p ~~~~~~ https://AWSアカウントID.dkr.ecr.リージョン.amazonaws.com docker build -t mvn_for_ecr ./dockerfiles/mvn_for_ecr docker tag mvn_for_ecr:latest AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.0.0 docker push AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.0.0pushが完了したらAWSコンソール上でECRのリポジトリを確認する、1.0.0があればOK
CodeBuildプロジェクトでpushしたdockerイメージを利用するように変更
AWSコンソールで
codebuild-AWSアカウントID-yyyyMMdd-mvn-project
に入り編集
->Environment
を選択してEdit Environment
画面に入る
Override image
を押下New environment image:
Custom image
Environment type:Linux
Image registry:Amazon ECR
ECR account:My ECR account
Amazon ECR repository:mvn_for_ecr
Amazon ECR image:1.0.0
Image pull credentials:Project service role
Service role:codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role
※多分ロールの名前はこんな感じポイントは
Image pull credentials
をProject service role
に設定するところ
後でECRリポジトリにポリシーで、指定しているロールからのアクセスを許可することでイメージをpull出来るようにするここまでの設定で一旦ビルドしてみる
※ECRリポジトリのポリシーを設定していないのでエラーが出る、一回リポジトリのポリシーを有効にすると数時間間違った設定でもSucceededになるので注意
こんな感じ at Phase detailsBUILD_CONTAINER_UNABLE_TO_PULL_IMAGE: Unable to pull customer's container image. CannotPullContainerError: Error response from daemon: pull access denied for AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr, repository does not exist or may require 'docker login'次にECRリポジトリ
mvn_for_ecr
のポリシーを以下のように設定する
PermissionsというリンクをクリックするとポリシーJSONを編集と出るのでそこで行うECRリポジトリのポリシー{ "Version": "2008-10-17", "Statement": [ { "Sid": "mvn_for_ecr_policy", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::AWSアカウントID:role/service-role/codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role" }, "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer" ] } ] }特定のロールに対してECR取得系のアクションを許す
これを保存してもう一度CodeBuildでビルドしてみると全てSucceededになると思う
ならなかったらCodeBuildのenvironmentを同じ設定で保存し直すといけるかも、検証してないマネージドイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
次はdockerイメージをビルドしてECRに対してpushする
CodeBuildサンプルは以下
CodeBuildのDockerサンプルやること
- ECRリポジトリの作成(push用)
- 作成したECRリポジトリのポリシー設定
- buildspec.ymlの変更
- イメージ作成用のDockerfile作成
- プロジェクトをS3へ再アップロード
- CodeBuildのEnvironment設定
dockerイメージをCodeBuildからECRにpushするのでECRリポジトリのポリシーにpush用の権限を設定する必要がある
dockerコマンドで利用する環境変数をCodeBuildプロジェクトのEnvironmentsから設定するECRリポジトリの作成
push用リポジトリを作成する
名前はbuild_from_codebuild_echoalpine
にする
今回はmvnコマンドを利用せずに単純にdocker build
やdocker push
がちゃんと行えるようになるかを確認していくUIやCLIから作成する
build_from_codebuild_echoalpine
のポリシー設定を行うビルドしたイメージをpushするリポジトリのポリシーJSON
pushを行うので更新系のアクションを許可するようにする
こんな感じ、ロール名は予測なのでサジェストに出てきた対応するやつを使うbuild_from_codebuild_echoalpine{ "Version": "2008-10-17", "Statement": [ { "Sid": "build_from_codebuild_echoalpine_policy", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::AWSアカウントID:role/service-role/codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role" }, "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:InitiateLayerUpload", "ecr:PutImage", "ecr:UploadLayerPart" ] } ] }buildspec.ymlの変更
今までのbuildspec.ymlだとmvnコマンドを実行してからartifactsでS3に成果物を置くようにしていたが、今回はビルドしたdockerイメージをECRにpushするのでそれ用に書き直す
buildspec.ymlversion: 0.2 phases: install: commands: - echo Logging in to commands... pre_build: commands: - echo Logging in to Amazon ECR... - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION) build: commands: - echo Build started on `date` - echo Building the Docker image... - echo $DOCKER_VERSION - which docker - pwd - ls -l - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAGdockerイメージを成果物として捉えるならartifactsに書きたくなるが、docker pushは
post_build
フェーズにcommandsとして書く
pre_build
フェーズでaws ecr get-login
コマンドがあるが、environmentsでマネージドイメージを選択するので実行可能になっている、嬉しい
いくつも環境変数を利用している、CodeBuildプロジェクトのenvironmentsで環境変数設定で定義する
build
フェーズのecho $DOCKER_VERSION
which docker
pwd
ls -l
などはなんか確認したくて書いてる、別に必要はないイメージ作成用のDockerfile作成
buildspec.yml内で
docker build
コマンドを実行しているのでDockerfileを作成する作成したdockerfilesディレクトリに置いたほうが恐らく良いが、怠惰男になったので一旦アプリケーションルートに置く、ゴメス
touch Dockerfile touch echo.shDockerfile# 成果物を実行させるイメージを生成する FROM alpine:3.9.3 ADD ./echo.sh /tmp/echo.sh CMD /tmp/echo.shecho.sh#!/bin/ash count=0 while true do echo "my alpine container build from CodeBuild!! 1.0.0" sleep 10 count=$(expr $count + 1) if [ $count -ge 10 ]; then echo "echoalpineの死!!" exit fi doneプロジェクトをS3へ再アップロード
CodeBuildプロジェクトに必要なファイルが揃ったので再びzipにしてinput用S3にアップロードする
zip -r MessageUtil.zip ./S3にアップロードする
codebuild-AWSアカウントID-yyyyMMdd-input-bucket/MessageUtil.zip
CodeBuildのEnvironment設定
後はCodeBuildの設定を更新してビルドすれば問題ないはず、恐れずに行け。
AWSコンソールの作成しているプロジェクト ->
編集
->Environment
Edit Environment
画面で以下の設定にする
Override image
を押下してNew environment image:
Managed image
Operating system:Ubuntu
Runtime(s):Standard
Image:aws/codebuild/standard:1.0
Image version:latest
Privileged:チェックする
Service role:arn:aws:iam::AWSアカウントID:role/service-role/codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role
追加設定を開いて
Environment variables
を追加していく、以下key
:value
で全てPlaintext
AWSアカウントIDは自分のに変更、リージョンも今まで指定してきたリージョンに変更
IMAGE_REPO_NAME
:build_from_codebuild_echoalpine
IMAGE_TAG
:latest
AWS_ACCOUNT_ID
:AWSアカウントID
AWS_DEFAULT_REGION
:ap-northeast-1
最も重要なのは
Privileged
にチェックを入れること、これをチェックしないとCannot connect to the Docker daemon
とエラーが出てdockerが使えない
Runtimeにはdockerを指定しないといけないと思いきやStandardでOKこれでECR push用の設定は完了したのでビルド実行
恐らくSucceededになるはず(記憶を頼りに書いてる)
ECRのpush対象リポジトリに入ってイメージが追加されているか確認
動作確認もしたければローカル等にpullしてrunする、ECR pushが目的だったので動作はあまり気にしてないけど
エラーが出たときはリポジトリのポリシーとCodeBuild environments設定のPrivilegedチェックを確認してみる
…散々Privilegedチェックを書いているけど、検証時はカスタムイメージでdocker:dind
を指定してたからもしかしたらマネージドイメージでは不要かもしれない…すみませんカスタムイメージ内でdockerイメージをビルド、ビルドしたイメージをECRにpush
さあ本命の登場だ
これまでのことを組み合わせると簡単に行けるだろうと思っていたがdocker in dockerなのでカスタムイメージ設定で苦戦したほぼ参考にしてないけどCodeBuild対応サンプル
CodeBuild のカスタム Docker イメージのサンプルやること
- カスタムイメージの作成
- buildspec.ymlの変更
- CodeBuildのEnvironmentsの変更
カスタムイメージではmvnを入れているけどdocker buildでは結局echoalpineをビルドしているという点は許してほしい、カスタムイメージ内でdockerが使えるようになればあとはどうにでもなる、何を作るかなど今はどうでも良いのだ…
カスタムイメージの作成
最も重要な点として、ベースイメージを
docker:dind
系にしないといけないということ
※dindはdocker in dockerの略
前回のベースイメージはjava:8-jdk-alpine
だったのでdind
にするとjdkをインストールしなければいけない
それではカスタムイメージのDockerfile編集dockerfiles/mvn_for_ecr/DockerfileFROM docker:18.09.5-dind WORKDIR /tmp RUN apk update && apk add openssl && apk add curl RUN apk add openssh RUN apk add bash # javaのダウンロー # ド RUN apk add openjdk8 RUN curl -OL https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven-3.6.1-bin.tar.gz RUN tar -xzvf apache-maven-3.6.1-bin.tar.gz RUN mv apache-maven-3.6.1 /usr/local/bin/ ENV PATH $PATH:/usr/local/bin/apache-maven-3.6.1/bin RUN echo $PATH RUN which mvn WORKDIR /app重要なのは
docker:18.09.5-dind
これ以外のイメージをFROMに指定して出来るもんなのか、dockerインストールするとか以外でDockerfile内でCMD指定してないのは
docker:18.09.5-dind
のCMDを利用するため、Dockerシェフは素材の味を活かす…
※そもそもCMD指定しなかったらFROMのCMDを利用するのか知らない、雰囲気でそうしてる、検証します…上記のDockerfileでCodeBuildでdockerコマンドが利用できるのはちゃんと確認したカスタムイメージ用のDockerfileを編集したのでビルドしてECRにpushする
ここはECRにpushするサンプルとほぼ同じなのでパラメータとかを適当なものに
ここではタグを1.1.0
にするaws ecr get-login --region リージョン --no-include-email > docker login -u AWS -p ~~~~~~ https://AWSアカウントID.dkr.ecr.リージョン.amazonaws.com docker build -t mvn_for_ecr ./dockerfiles/mvn_for_ecr docker tag mvn_for_ecr:latest AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.1.0 docker push AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/mvn_for_ecr:1.1.0buildspec.ymlの変更
カスタムイメージを作成したので次はその中でどんなコマンドを実行するかを変更していく
buildspec.ymlversion: 0.2 phases: install: commands: - echo Logging in to commands... - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2& - timeout -t 15 sh -c "until docker info; do echo .; sleep 1; done" pre_build: commands: - echo Logging in to Amazon ECR... - docker login -u AWS -p $ECR_LOGIN_PASS https://$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com build: commands: - echo Build started on `date` - echo Building the Docker image... - echo $DOCKER_VERSION - which docker - pwd - ls -l - which mvn - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
install
フェーズに追加しているnohup...
でホスト上のdockerをコンテナ上で利用できるようにしてる的なAWS CLIのインストールをサボってpull出来るように直接docker loginをかましているけどもっと良い方法があるはず(絶対)
そもそもこの方法だとECRへのdocker login有効期限12時間が経過する度に$ECR_LOGIN_PASS
を更新しないといけないのでダメ
mvnコマンドを利用しない代わりにとりあえずwhich mvn
でコマンドがあるかだけ確認しているbuildspec.ymlの編集が完了したらまたzipしてinput用S3バケットにアップロードする
zipコマンドだけzip -r MessageUtil.zip ./CodeBuildのEnvironmentsの変更
CodeBuildのenvironmentsを編集する
New environment image:
Custom image
Environment type:Linux
Image registry:Amazon ECR
ECR account:My ECR account
Amazon ECR repository:mvn_for_ecr
Amazon ECR image:1.1.0
Image pull credentials:Project service role
Privileged:チェックする
Service role:codebuild-codebuild-AWSアカウントID-yyyyMMdd-mvn-project-service-role
環境変数に以下を追加
ローカルでaws ecr get-login --region リージョン --no-include-email
を実行して-pの文字列をコピーしてくる
ECR_LOGIN_PASS
:コピーした文字列
これで更新
今回は絶対Privilegedチェック必要
ビルド実行してSucceededになってpush先のECRリポジトリが更新されていればOK
これまでの全てを組み合わせればエラーが出ても解決できるはずじゃ…(記憶とメモを頼りに書いてる)まとめ
後半になるにつれて雑になってきたけど多分要所は抑えているのでエラーが出たらサンプルを頼りにがんばる、ツラくなったらFutureFunkやElectroSwingを聞いて踊れ、この記事で伝えたい全てはそれだ。
困ったらBuild logsを見よう、問題はそこに出ている
AWSサービスは概念掴めばドキュメント見てterraform化とか出来るのでそこまでは
マルチステージビルドとかソース取得元とGitHubにしたりとかまだやりたいことはあるけどとりあえずこれで問題
定期的に権限の有効期限が切れているので解決する
参考
- 投稿日:2019-05-06T17:48:54+09:00
Amazon Connectにおけるユーザーとオペレータ―との間の音声を取得する方法
はじめに
Amazon Connectで通話した音声を保存したいということで、Amazon Connectを介してユーザーとエージェントとの間の音声を取得する方法について確認してみました。
音声取得方法
現状では次の二つの方法で音声を取得することが可能でした。
1.通話録音機能
2.Kinesis Video Stream1.通話録音機能
コンタクトフローに[通話記録動作の設定]ブロックを設定することで通話を録音することができます。
オプションとしては次の4つを指定することが可能です。
顧客とエージェントの通話が終了した後に、wav形式の音声データがS3に保存されます。
構成は次のような感じ。
数分程度の短い通話であればほぼ即時で作成されました。通話時間に比例して作成時間も長くなるようです。
通話記録機能のオプションで「エージェント AND 顧客」を選択した場合は1つのファイルとして保存されました。
採取した音声データを視聴したところ、ステレオ音声として保存されており、左チャネルに顧客側の音声が、右チャネルにエージェントの音声が保存されましたのでAWSドキュメントの通りでした。片方が会話している際は基本的には(せっかちな方でなければ)もう片方の音声は保存されませんので音声のデータとしては長時間になると思われます。また、分析などを行う際はチャネルがどちらの会話を扱っているのか、会話の流れを正しく把握する部分に注意したほうがよさそうです。
2.Kinesis Video Stream
Amazon Kinesis Video Streams を使用すると、分析、機械学習 (ML)、再生、およびその他の処理のために、接続されたデバイスから AWS へ動画を簡単かつ安全にストリーミングできるようになります。
Amazon Connectでは、コンタクトフローに[メディアストリーミングの開始]ブロックを設定することで通話をリアルタイムでストリーミングできます。
ユーザーとオペレータ―間の通話ごとにストリームが作成されました。
AWSマネジメントコンソールでは視聴することができませんでした。AWSドキュメントに従い、コンシューマで音声データを取得します。
構成は次のような感じ。コンシューマで取得した音声ファイルはRAW形式のファイルとなり、最小単位は1024バイトでした。ストリームされる音声データは、Single 16bit PCM 8000HzのRAWデータで、バイトオーダーはリトルエンディアンでした。
動作確認では通話開始からおおよそ20sec後にストリームが流れ始めたためリアルタイムに状況を分析してフィードバックするような処理には向いていないと考えられ、扱い方は検討が必要そうです。まとめ
Amazon Connectにおけるユーザーとオペレータ―との間の音声の取得方法の確認結果は次の通りです。
通話録音機能
- 通話が完了した後にwavファイルが保存される
- 通話の時間に比例してwavファイルが保存される時間が長くなる(実測60secの通話でファイル作成まで10sec)
- 顧客とオペレータ両方の通話を保存する場合
- wavファイルは1つ作成される
- ステレオ音声の左チャネルに顧客側の音声が保存され、右チャネルにエージェントの音声が保存されるKinesis Video Stream
- 音声データはコンシューマ(SDK開発が必要)を介してRAWファイルとして採取可能
- 通話が開始してからおおよそ20sec後に音声データが取得できる
- 取得できる音声データ
- Single 16bit PCM 8000HzのRAWデータ
- バイトオーダーはリトルエンディアン
- 最小単位は1024バイト
- 投稿日:2019-05-06T17:06:56+09:00
JavaでAWS Signature V4を生成してAPIをリクエストする
はじめに
AWS APIは、リクエストにAWS Signature V4という署名をつけることでIAM認証を利用できます。普通はAWS SDKを利用することでSignature V4の仕様をさほど意識する必要はないのですが、諸事情で、自前によるSignature V4実装をする機会がありましたので、メモを残します。
Java 11で実装しており、HTTPクライアントはApache HttpComponents Clientを使ってます。
コードは切り貼り、加工しているので、正しく動作するか若干怪しいです。
署名しない時のリクエスト
わかりやすくするため、署名しないときのリクエストサンプルを示します。 Elasticsearch APIで検索リクエストするサンプルです。このサンプルではPOSTメソッドを利用しているためクエリパラメータはHTTP bodyに書いています。
このリクエストにAWS Signature V4署名を施し、認証されたユーザからのみAPIを受けるように改修していきます。
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; void reqFunc(){ // リクエストの生成 URI uri = new URI(AWS_ELASTICSEARCH_URL + "/index/hoge/_search?"); HttpPost post = new HttpPost(uri); post.setHeader("Content-Type", "application/json"); post.setEntity(new StringEntity( "クエリー文字列(省略)", "UTF-8")); // HTTPクライアントでリクエストの実行 HttpClient client = HttpClientBuilder.create().build(); CloseableHttpResponse res = (CloseableHttpResponse) client.execute(post); // ...レスポンスに対する処理... }署名に関する情報
AWS Signature V4はAmazonのWebサイトで情報提供がされています。下記のURLからたどって情報を得ました。
https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-4.htmlまた、認証に必要となるアクセスキーIDとシークレットアクセスキーの取得方法は、下記のURLからたどって情報を得ています。
https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-sec-cred-types.html署名の実装
AWS Signature V4は、リクエスト情報そのものと、予め払い出されているアクセスキーID/シークレットアクセスキーを使いハッシュを生成し、リクエストヘッダに付与します。何度も繰り返しハッシュ化したり、ハッシュの対象対象がどこまでなのか分からず、試行錯誤しました。
HttpRequestInterceptorクラスの作成
最初に送信するリクエスト自身の情報を取得するため、HttpRequestInterceptorクラスと、それを挟み込んだHTTPクライアントを用意します。HTTPクライアントを生成する際にこのクラスを挟むことで、サーバへのリクエスト送信直前に処理を差し込むことができます。
- HttpRequestInterceptor
import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.protocol.HttpContext; public class AmazonizeInterceptor implements HttpRequestInterceptor { @Override public void process(HttpRequest request, HttpContext context) throws Exception{ AwsSigner4(request); } private void AwsSigner4(HttpRequest request) throws Exception { /* この関数実装内でAWS Signature V4の実装をします。 */ } }
- HTTPクライアント呼び出し部の修正
//...略... void reqFunc(){ //...略... // HTTPクライアントでリクエストの実行 HttpClient client = HttpClientBuilder.create() .addInterceptorLast(new AmazonizeInterceptor()) // ←これを追加する .build(); CloseableHttpResponse res = (CloseableHttpResponse) client.execute(post); //...レスポンスに対する処理... }これで、RequestInterceptorを挟み込んだHTTPクライアントでリクエストを行うと、サーバ送信前に先程定義したAwsSigner4関数が実行されるようになります。
AwsSigner4関数の実装
次に実際の署名処理を行うAwsSigner4関数の実装をしていきます。
処理内容は、リクエストヘッダーに「X-Amz-Date」と「Authorization」を追加するだけですが、Authorizationの値を求めるのに何段階か必要になります。
- 正規リクエスト文字列(canonicalRequest)の生成
- 署名文字列(StringToSign)の生成
- 署名キー(SigningKey)の生成
- Authorizationヘッダー文字列の生成
参考までに、canonicalRequestに含まれるヘッダー情報は、host,x-amz-dateが必須ですが、それ以外のヘッダー情報を追加しても良いみたいです。
import org.apache.http.util.EntityUtils; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.text.SimpleDateFormat; private void AwsSigner4(HttpRequest request) throws Exception { /* X-Amz-Dateヘッダーの生成 */ SimpleDateFormat xAmzDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); xAmzDateFormatter.setTimeZone(Calendar.getInstance(TimeZone.getTimeZone("UTC"))); // Signerの有効期限はUTCで判定される String xAmzDate = xAmzDateFormatter.format(new Date()).trim(); request.setHeader("X-Amz-Date", xAmzDate); /* Authorizationヘッダー文字列の生成 */ /* 1. 正規リクエスト文字列(canonicalRequest)の生成 */ String path = getPath(request); String xAmzContentSha = getBodyHash(request); String canonicalRequest = "POST\n" + path + "\n" + "\n" + "host:" + request.getFirstHeader("Host").getValue() + "\n" + "x-amz-date:" + xAmzDate + "\n" + "\n" + "host;x-amz-date\n" + xAmzContentSha; /* 2. 署名文字列(StringToSign)の生成 */ String awsRegion = "ap-northeast-1" ; // AWS APIリクエスト先のリージョン情報 String awsNameSpace = "es" ; // リクエストするAWSサービスの名前空間 String StringToSign = "AWS4-HMAC-SHA256\n" + xAmzDate + "\n" + xAmzDate.substring(0, 8) + "/" + awsRegion + "/" + awsNameSpace +"/aws4_request\n" + DigestUtils.sha256Hex(canonicalRequest); /* 3. 署名キー(SigningKey)の生成 */ String awsSecretAccessKey = "AWSシークレットアクセスキー" ; // X-Amz-Date → リージョン → AWSサービス名前空間 → 固定文字(aws4_request) の順でハッシュ化していく String hashStr = getHmacSha256ByStrKey("AWS4" + awsSecretAccessKey, xAmzDate.substring(0, 8)); hashStr = getHmacSha256ByHexKey(hashStr, awsRegion); hashStr = getHmacSha256ByHexKey(hashStr, awsNameSpace); String SigningKey = getHmacSha256ByHexKey(hashStr, "aws4_request"); /* 4. Authorizationヘッダー文字列の生成 */ String awsAccessKeyId = "AWSアクセスキーID" ; String sig = getHmacSha256ByHexKey(SigningKey, StringToSign); String authorization = "AWS4-HMAC-SHA256 Credential=" + awsAccessKeyId + "/" + xAmzDate.substring(0, 8) + "/" + awsRegion + "/" + awsNameSpace + "/aws4_request," + "SignedHeaders=host;x-amz-date," + "Signature=" + sig; request.setHeader("Authorization", authorization); } /*** リクエストパスの取得 */ private String getPath(HttpRequest req) throws Exception { String uri = req.getRequestLine().getUri(); // URLのクエリ文字列とのセパレータである「?」はpathに含めない if (uri.endsWith("?")) uri = uri.substring(0, uri.length()-1); // URLエンコードも色々種類があるらしく、Amazonが指定したロジックでエンコードする return awsUriEncode(uri,true); } /*** リクスとボディのハッシュ取得 */ private String getBodyHash(HttpRequest req) throws Exception{ HttpEntityEnclosingRequest ereq = (HttpEntityEnclosingRequest) req; String body = EntityUtils.toString(ereq.getEntity()); return DigestUtils.sha256Hex(body); } /*** * AWS指定スペックのURLエンコーダ * @param input * @param encodeSlash * @return * @throws UnsupportedEncodingException */ private String awsUriEncode(CharSequence input, boolean encodeSlash) throws UnsupportedEncodingException { StringBuilder result = new StringBuilder(); boolean queryIn = false; for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') { result.append(ch); } else if (ch == '/') { if (queryIn) result.append(encodeSlash ? "%2F" : ch); else result.append(ch); } else { if(!queryIn && ch=='?') { queryIn = true; result.append(ch); }else { byte[] bytes = new String(new char[] {ch}).getBytes("UTF-8"); result.append("%" + Hex.encodeHexString(bytes,false)); } } } return result.toString(); } private String getHmacSha256ByStrKey(String strkey, String target) throws Exception { return getHmacSha256(strkey.getBytes(), target); } private String getHmacSha256ByHexKey(String hexkey, String target) throws Exception { return getHmacSha256(Hex.decodeHex(hexkey), target); } /*** * target文字列をKeyを使いHMAC-SHA-256にハッシュ化する * @param target ハッシュ対象 * @param key キー * @return Hex形式のハッシュ値 */ private String getHmacSha256(byte[] key, String target) throws Exception { final Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(key, "HmacSHA256")); return String.valueOf(Hex.encodeHex(mac.doFinal(target.getBytes()), true)); }まとめ
最終的にまとめると下記のようになりました。
リクエスト毎にフォーマッターを生成したり、定数が埋め込みになっていたりでアレですが、そこら辺はよしなに直してください。import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.binary.Hex; import java.text.SimpleDateFormat; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class AmazonizeInterceptor implements HttpRequestInterceptor { @Override public void process(HttpRequest request, HttpContext context) throws Exception{ AwsSigner4(request); } private void AwsSigner4(HttpRequest request) throws Exception { /* X-Amz-Dateヘッダーの生成 */ SimpleDateFormat xAmzDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); xAmzDateFormatter.setTimeZone(Calendar.getInstance(TimeZone.getTimeZone("UTC"))); // Signerの有効期限はUTCで判定される String xAmzDate = xAmzDateFormatter.format(new Date()).trim(); request.setHeader("X-Amz-Date", xAmzDate); /* Authorizationヘッダー文字列の生成 */ /* 1. 正規リクエスト文字列(canonicalRequest)の生成 */ String path = getPath(request); String xAmzContentSha = getBodyHash(request); String canonicalRequest = "POST\n" + path + "\n" + "\n" + "host:" + request.getFirstHeader("Host").getValue() + "\n" + "x-amz-date:" + xAmzDate + "\n" + "\n" + "host;x-amz-date\n" + xAmzContentSha; /* 2. 署名文字列(StringToSign)の生成 */ String awsRegion = "ap-northeast-1" ; // AWS APIリクエスト先のリージョン情報 String awsNameSpace = "es" ; // リクエストするAWSサービスの名前空間 String StringToSign = "AWS4-HMAC-SHA256\n" + xAmzDate + "\n" + xAmzDate.substring(0, 8) + "/" + awsRegion + "/" + awsNameSpace +"/aws4_request\n" + DigestUtils.sha256Hex(canonicalRequest); /* 3. 署名キー(SigningKey)の生成 */ String awsSecretAccessKey = "AWSシークレットアクセスキー" ; // X-Amz-Date → リージョン → AWSサービス名前空間 → 固定文字(aws4_request) の順でハッシュ化していく String hashStr = getHmacSha256ByStrKey("AWS4" + awsSecretAccessKey, xAmzDate.substring(0, 8)); hashStr = getHmacSha256ByHexKey(hashStr, awsRegion); hashStr = getHmacSha256ByHexKey(hashStr, awsNameSpace); String SigningKey = getHmacSha256ByHexKey(hashStr, "aws4_request"); /* 4. Authorizationヘッダー文字列の生成 */ String awsAccessKeyId = "AWSアクセスキーID" ; String sig = getHmacSha256ByHexKey(SigningKey, StringToSign); String authorization = "AWS4-HMAC-SHA256 Credential=" + awsAccessKeyId + "/" + xAmzDate.substring(0, 8) + "/" + awsRegion + "/" + awsNameSpace + "/aws4_request," + "SignedHeaders=host;x-amz-date," + "Signature=" + sig; request.setHeader("Authorization", authorization); } /*** リクエストパスの取得 */ private String getPath(HttpRequest req) throws Exception { String uri = req.getRequestLine().getUri(); // URLのクエリ文字列とのセパレータである「?」はpathに含めない if (uri.endsWith("?")) uri = uri.substring(0, uri.length()-1); // URLエンコードも色々種類があるらしく、Amazonが指定したロジックでエンコードする return awsUriEncode(uri,true); } /*** リクスとボディのハッシュ取得 */ private String getBodyHash(HttpRequest req) throws Exception{ HttpEntityEnclosingRequest ereq = (HttpEntityEnclosingRequest) req; String body = EntityUtils.toString(ereq.getEntity()); return DigestUtils.sha256Hex(body); } /*** * AWS指定スペックのURLエンコーダ * @param input * @param encodeSlash * @return * @throws UnsupportedEncodingException */ private String awsUriEncode(CharSequence input, boolean encodeSlash) throws UnsupportedEncodingException { StringBuilder result = new StringBuilder(); boolean queryIn = false; for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') { result.append(ch); } else if (ch == '/') { if (queryIn) result.append(encodeSlash ? "%2F" : ch); else result.append(ch); } else { if(!queryIn && ch=='?') { queryIn = true; result.append(ch); }else { byte[] bytes = new String(new char[] {ch}).getBytes("UTF-8"); result.append("%" + Hex.encodeHexString(bytes,false)); } } } return result.toString(); } private String getHmacSha256ByStrKey(String strkey, String target) throws Exception { return getHmacSha256(strkey.getBytes(), target); } private String getHmacSha256ByHexKey(String hexkey, String target) throws Exception { return getHmacSha256(Hex.decodeHex(hexkey), target); } /*** * target文字列をKeyを使いHMAC-SHA-256にハッシュ化する * @param target ハッシュ対象 * @param key キー * @return Hex形式のハッシュ値 */ private String getHmacSha256(byte[] key, String target) throws Exception { final Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(key, "HmacSHA256")); return String.valueOf(Hex.encodeHex(mac.doFinal(target.getBytes()), true)); } }あとがき
今回、私が実際に動作確認したのはElasticsearch APIのみですが、IAMの権限が正しく設定されていれば、他のAPIも同じ方法で利用できるかと思います。もし動作確認が取れたAWSサービスがあったら、コメントに書いていただけるとありがたいです。
お役に立ちましたら幸いです。
- 投稿日:2019-05-06T14:54:12+09:00
ラズパイとAWS IoT連携 (Pythonで温度情報をクラウド送信)
はじめに
例えばラズパイを使用して、センサーの情報を取得し、クラウドにデータをアップロードし活用したいですよね!
今回はその手始めとして、ラズパイとAWS IoTを連携してみたいと思います。
今後、DBへの保存や、グラフの表示などもまとめていきたいと思います!使用機材
安価に以下を用意して試してみました。
- Raspberry Pi Zero WH
- ブレッドボード(165408010E)
- デジタル温度センサADT7310 DIP化モジュール[MDK001]
- ジャンパーワイヤ
ラズパイで温度情報を取得
Myブログで恐縮ですが、ラズパイで温度センサーから温度を取得する記事をまとめましたので参考にしてください。
AWS IoTの設定
AWSアカウントを取得後、AWS IoTの設定を行います。
結構AWS IoTの設定が多く、私は最初戸惑いました
一度設定をしてみると、流れを把握できると思います。AWS IoT
AWS IoTについては、AWS ドキュメントに記載があります。
AWS IoT では、インターネットに接続されたデバイス (センサー、アクチュエーター、組み込みマイクロコントローラー、スマート家電など) と AWS クラウドとのセキュアな双方向通信が可能になります。これにより、複数のデバイスからテレメトリデータを収集し、保存して、分析できます。また、これらのデバイスをユーザーが各自の電話やタブレットから制御できるようにするアプリケーションを作成することもできます。
デバイスの登録
パブリックキーとプライベートキー、証明書をダウンロードします。
また、「有効化」をクリックします。
このページを閉じると、パブリックキーとプライベートキーが取得できなくなるので注意が必要です。
その後、「ポリシーをアタッチ」をクリックします。
ルート認証機関 (CA) は以下のURLからダウンロードします。
RSA 2048 ビットキー: Amazon ルート CA 1を選択しました。
AmazonRootCA1.pemという名前で保存します。「安全性→ポリシー」をクリックし、「ポリシーの作成」をクリックします。
ポリシーの「名前」を任意で入力します。
アクションに「iot:*」と入力し、リソース ARNに「*」と入力します。
「許可」にチェックを入れ、「作成」をクリックします。
(許可範囲が広いので、本番稼働時は適宜設定をしてください)
管理をクリックし、先ほど作成したモノをクリックします。
「セキュリティ」をクリックし証明書をクリックします。
作成したポリシーをチェックし、「アタッチ」をクリックします。
以上で、AWS IoTの設定が完了しました。
ラズパイ側のコーディング
続いてラズパイ側の実装を行います。
以降、ラズパイ上での作業になります。ラズパイで温度情報を取得
Pythonで温度を取得する処理を作成しました。
(詳しくはブログ記事を参照)get_temp.pyimport time import spidev import sys spi_ch = 0 try: spi = spidev.SpiDev() spi.open(0,spi_ch) spi.mode = 0x03 spi.max_speed_hz = 1000000 time.sleep(0.01) spi.xfer2([0xff, 0xff, 0xff, 0xff]) while True: spi.xfer2([0x54]) time.sleep(0.5) adc = spi.xfer2([0xff,0xff]) temp = (adc[0] << 8) | adc[1] temp = temp >> 3 if(temp >= 4096): temp = temp - 8192 print(temp / 16.0) time.sleep(1) except KeyboardInterrupt: spi.close() sys.exit(0)AWS IoTに送信
この処理をもとに、温度をAWS IoTに送信したいと思います。
デバイスからは443ポートで、TLSクライアント認証によるMQTT通信をします。PythonでMQTTの通信処理を書いてもよいのですが、今回はaws-iot-device-sdk-pythonを使用しました。
https://github.com/aws/aws-iot-device-sdk-pythonラズパイにインストールします。
自分のラズパイにpipもgitも入ってなかったので、今回はzipからインストールしました。mkdir AWSIoTPythonSDK cd AWSIoTPythonSDK wget https://s3.amazonaws.com/aws-iot-device-sdk-python/aws-iot-device-sdk-python-latest.zip unzip AWSIoTPythonSDK sudo python3 setup.py installファイルの構成は以下です。
AWS IoTの設定時にダウンロードしたキーなども配置します。
(xxxは各自のファイル名で読み替えてください)/
├ aws-test.py
└ cert/
├ AmazonRootCA1.pem
├ xxx-private.pem.key
└ xxx-certificate.pem.crt実装は以下になりました。
まずは単純にデータの送信を試します。エンドポイントURLは、「AWS IoT→管理→モノ→操作」から確認できます。
configureCredentialsの引数は、設定時にダウンロードしたファイルのパスを指定します。aws-test.pyfrom AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient import json # 初期化 myMQTTClient = AWSIoTMQTTClient("raspi-zero") # MQTTクライアントの設定 myMQTTClient.configureEndpoint("エンドポイントURL", 443) myMQTTClient.configureCredentials("/xxx/xxx/cert/AmazonRootCA1.pem", "/xxx/xxx/cert/xxx-private.pem.key", "/xxx/xxx/cert/xxx-certificate.pem.crt") myMQTTClient.configureOfflinePublishQueueing(-1) myMQTTClient.configureDrainingFrequency(2) myMQTTClient.configureConnectDisconnectTimeout(10) myMQTTClient.configureMQTTOperationTimeout(5) # Connect to AWS IoT endpoint and publish a message myMQTTClient.connect() print ("Connected to AWS IoT") myMQTTClient.publish("awsiot/test", json.dumps({"test": "test message!"}), 0) myMQTTClient.disconnect()動作確認
AWS IoTのコンソールから「テスト」をクリックし、トピックのサブスクリプションに「awsiot/test」と入力しサブスクライブします。
作成したPythonを実行します。
python3 aws-test.py温度情報を定期的に送信
温度取得処理とAWS IoT送信処理とくっつけて、1分毎にデータを送信する処理を作成しました。
(ちょっとやっつけ感ありますが)
aws-send-temp.pyfrom AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient import json import time import spidev import sys # 初期化 myMQTTClient = AWSIoTMQTTClient("raspi-zero") # MQTTクライアントの設定 myMQTTClient.configureEndpoint("エンドポイントURL", 443) myMQTTClient.configureCredentials("/xxx/xxx/cert/AmazonRootCA1.pem", "/xxx/xxx/cert/xxx-private.pem.key", "/xxx/xxx/cert/xxx-certificate.pem.crt") myMQTTClient.configureOfflinePublishQueueing(-1) myMQTTClient.configureDrainingFrequency(2) myMQTTClient.configureConnectDisconnectTimeout(10) myMQTTClient.configureMQTTOperationTimeout(5) # Connect to AWS IoT endpoint and publish a message myMQTTClient.connect() print ("Connected to AWS IoT") # 温度情報取得・送信 spi_ch = 0 try: spi = spidev.SpiDev() spi.open(0,spi_ch) spi.mode = 0x03 spi.max_speed_hz = 1000000 time.sleep(0.01) spi.xfer2([0xff, 0xff, 0xff, 0xff]) while True: spi.xfer2([0x54]) time.sleep(0.5) adc = spi.xfer2([0xff,0xff]) temp = (adc[0] << 8) | adc[1] temp = temp >> 3 if(temp >= 4096): temp = temp - 8192 # 温度情報送信 print(temp / 16.0) myMQTTClient.publish("awsiot/test", json.dumps({"temperature": (temp / 16.0)}), 0) # 一分待機 time.sleep(60) except KeyboardInterrupt: spi.close() myMQTTClient.disconnect() sys.exit(0)今後について
まずはAWS IoTと連携できました。
次は例えばDBにデータを保存したり、グラフ化したりできますね!
またの機会にまとめたいです
- 投稿日:2019-05-06T14:29:29+09:00
aws cloud9を軽く触ってみた結果をまとめてみる
初めに
はじめまして。raqwelと言います。
普段は新卒として入った都内のITベンチャーでSREとして働いています。
業務中で気になった技術やらなんやらを休みの日に調べたり試したりすることが増えてきたので、
備忘録がてらまとめて見たいと思い、qiitaに投稿しようと思いました。やったこと
今回はaws cloud9を軽く触ってみたので、自身が感じたメリデメ等をまとめようかと思います。
cloud9に関しては↓を見ていただけると良いかと思います。簡潔に述べると、awsが提供しているクラウドIDEで、
awsアカウントとブラウザとネット環境があればどこでもコーディングできるものです。
最近だと東京リージョンに来たことがちょっとしたニュースになりました。実際にはこちらの方が行ったことをなぞるような形で手を動かしました。
https://qiita.com/icck/items/7705ef6871e261d6535e
この場を借りてお礼申し上げます。実際に触ってみた感想
実際に触ってみた感想を以下にメリデメで書いていきます。
あくまで個人の意見なので、その点配慮して見ていただけたら幸いです。メリット
- ブラウザがあればどこでもコーディングできる
- ペアプロが容易なため、手戻りの発生を抑えられる
- セットアップの容易さ
- cloud9で実際に開発しようとしたとき、基本的には以下のステップを行うだけで簡単に用意できます。しかもcloud9自体は無料で使えます。
setup1. コンソールを開いて、 2. cloud9を選択して、 3. Create Environmentをクリックして、 4. Name(作業環境名)とDescription(環境の説明)を入力して、 5. そのまま進めて「Create environment」を押して、 6. 少し待つ。
- 対応言語が豊富
- node.js, python, js, java, Go等々、数多くの言語に対応しています。
- localstackとかのモック化したawsサービスを用意せずともそのまま連携できる。
- ローカル開発環境でawsのサービスを用いた機能を開発しているとデバッグするのに大変です。
- その際によく用いられるのは、fakes3やlocalstack等のawsのサービスをモックしたようなものです。
- もしawsが新たなサービスを発表しそれを用いるとなった時や、なにかがあって配布が中止されてしまった時に、まだlocalstackが使えなくなってしまうケースが考えられます。
- しかし、aws cloud9はaws上のサービスなので、他のサービスと連携した際のデバッグ、テストができます。これによって実際のawsのサービスを用いて開発ができます。
デメリット
- 普段利用しているIDEからの移行コストが高そう
- 特に中〜大規模のプロダクトに関して移行が厳しい印象。
- 実際に全員が使える様になるまでの学習コスト
- 複数人が同じenvironmentで触った時の動作が重くならないか・・・
開発者全員分のawsアカウントを用意し無くてはならない。ということは組織的にアカウント管理が抜けもれなく土壌が備わっているか・・・IAMユーザの間違いでした。(@tokusyuさんありがとうございます!)- gitのGUIがない
- 自分はいつもIDE付属のGUIでgit操作を行っています。
- その方が直感的に理解、操作がしやすいためです。
- しかし、cloud9にはGUI機能が見当たりませんでした。(もしかして見逃してるかもしれません)
- git操作を普段からコマンドで行っている方は問題ないと思いますが、GUIを利用する人の事を考えると、どちらも用意してある環境が欲しいな〜と思ってしまいます。
まとめ
今回はawsがリリースしたクラウドIDE「aws cloud9」を触ってみました。
実際触ってみて感じたのは、中〜大規模のプロジェクトがIDEからcloud9に移行するのは結構大変そうな印象を受けました。
しかし、セットアップの容易さ、対応言語の多さ等を考えると、個人や小規模のプロジェクトの開発にはむしろ使えるのではないかと感じました。
今後、どのようにアップデートしていくかが気になります。というわけで以上です。
初記事なので至らぬ点が多いかと思いますが、温かい目で見ていただけたら幸いです。
- 投稿日:2019-05-06T12:53:22+09:00
AWS Innovate Online Conference 「試験対策セッション1:回復性の高いアーキテクチャを設計する」をまとめてみた
参考文献
AWS Innovate Online Conference
AWS 認定 - 試験対策 「ソリューションアーキテクト - アソシエイト」
セッション1:回復性の高いアーキテクチャを設計するを視聴して、まとめた内容を記載しています。
1.1 信頼性と回復性に優れたストレージを選択する
EC2インスタンスストア
- エフェメラルボリューム
- 特定のEC2インスタンスのみ
- キャパシティ固定
- ディスクタイプとキャパシティはEC2インスタンスタイプによって異なる
- アプリケーションレベルの耐久性
- 無料で利用できる
- 一時的なストレージ
- キャッシュや一時コンテンツなど、頻繁に更新される情報のストレージ
EBS
- EC2で使用するためのボリュームタイプ
- 暗号化
- スナップショット
- プロビジョンドキャパシティー
- EC2インスタンスに依存しないライフサイクル(永続的なストレージ)
- EC2の料金とは別に、料金が発生する
- データにすばやくアクセスする場合や継続性が必要な場合に推奨
- ランダムや読み取り書き込みを行う
- ユースケースに応じた4つのボリュームタイプから選択(特徴とユースケースを理解すべし)
EFS
- AWSクラウド上のファイルストレージ
- 複数のEC2から同時にアクセスできる共有ストレージ
- ギガバイトからペタバイトまで自動的にスケール
- 高い耐久性と可用性を考慮
- NFS v4.0およびNFS v4.1プロトコルをサポート
Amazon S3
- インターネット経由で利用可能なオブジェクトストレージ
- 信頼性が高く高速で安価なデータストレージ
- 容量は事実上無制限
- 99.999999999%の高い耐久性(9が11個)
- リージョン内で複数の施設にまたがる複数のデバイスにデータを冗長的に格納→高い耐久性
- S3上のデータ要件に応じてストレージクラスを使い分けることでコスト最適化
- データの暗号化が可能
- SSE-S3
- SSE-KMS
- SSE-C
- サーバサイドで暗号化が可能
- HTTPS
- バージョニング
- マルチパートアップロード機能(大規模データを分割してアップロード)
Amazon Glacier
- データのバックアップおよびアーカイブ用のストレージ
- 低コスト
- S3と似ているが、こちらはアーカイブ用のストレージ
- データを取り出すことにコストと時間がかかる
- データサイズに対するコストがS3より安い
- データを取り出すことに時間がかかるが、3つの取り出しオプションがある
- 迅速
- 標準
- 大容量
- 暗号化
- S3のライフサイクルポリシー
- リージョン内の可用性
- 99.999999999%の高い耐久性(S3と一緒)
1.2 AWSのサービスを使用した疎結合化メカニズムの設計方法を決定する
1.3 多層アーキテクチャソリューションの設計方法を決定する
- 疎結合化のメリット
- 可用性を向上
- スケーラビリティ
Webサーバからメールを送信する場合
- 密結合の場合
- Eメールサーバが利用できなくなりメール送信が失敗
- Webサーバの処理に影響
- 疎結合の場合
- Webサーバからのメッセージをキューで受け止める
- 非同期でメール送信する仕組み
- Webサーバの処理に影響せず、システム全体で可用性を向上
Webサーバからdynamo DBにログを格納する場合
- 密結合の場合
- ログ記録サービスが過負荷な状態でスローダウン
- Webサーバの処理が遅延
- 疎結合の場合
- Webサーバからのログ送信は一旦キューに格納
- イベントログ記録サービスが非同期でdynamo DBに格納
- ログ記録サービスの負荷が足りなかったら、そのコンポーネントを増やすことでWebサーバに影響なくシステムをスケールさせることができる
クラウドは、オンプレミスと異なり、必要なインスタンスを短時間で起動してシステムを組めることがメリット
- 密結合の場合
- サーバを増やしてシステム処理をスケールした場合、結合相手に影響が出る
- 柔軟なスケーラビリティを確保できない
- 障害で停止したり、過負荷により反応が遅いと他コンポーネントに影響が出る
- 疎結合の場合
- 性能不足になった自身のコンポーネントを増強することが容易。
- システム全体のスケーラビリティを向上
- コンポーネント同士のやり取りを非同期で行い、他のコンポーネントをブラックボックスとみなすことができる
疎結合に利用されるサービス
- SQS
- ELB
- ELP
- Route 53
1.4 可用性や耐障害性に優れたソリューションの設計方法を決定する
高可用性の観点では、いつ故障してもおかしくないという前提でシステムを設計/構築することが重要
高可用性とは
システムを構成するコンポーネントが故障しても、長時間停止することなく、自動的にすばやく復旧すること。
- 各コンポーネントをグループ化する
- 単一障害点を避ける実現方法
- 複数のAZとリージョンを使用する。
- オートスケーリングを使用する。
- 可用性の高いマネージド型サービスを利用する。
耐障害性とは
アプリケーション内のコンポーネントが備える冗長性。
アーキテクチャ内のいずれかのコンポーネントが完全に機能を失っても、アプリケーションはパフォーマンスを低下させることなく機能し続けること。
フォールトトレランスと呼ぶ場合もある。コンポーネント同士を疎結合にすることで、高可用性に不可欠な対象外も向上する。
- 疎結合のシステムでは、1つのコンポーネントの障害はレイヤー内に完結して管理。
- ほかのインスタンスに飛び越えて障害が拡散することはない。可用性の例
前提:SLAを満たすには4つのEC2インスタンスが必要。AZで障害あった場合を考える
- 2つのAZに2つずつEC2インスタンスを起動
- AZ-aに障害が起きた場合、残るのはAZ-bに2つのインスタンスのみ
- 残ったAZ-bに、新しい2つのEC2インスタンスが起動してくるまではSLAが満たせない。(オートスケーリングで起動)
- コスト的には、通常運用時は4インスタンス分のコストのみ
耐障害性の例
前提:SLAを満たすには4つのEC2インスタンスが必要。AZで障害あった場合を考える
- 2つのAZに4つずつEC2インスタンスを起動
- AZ-aに障害が起きた場合でも、AZ-bに4つのインスタンスがある
- SLAは満たし続けることができる
- コストが通常運用でも8インスタンス分のコストが発生する
耐障害性の構成をとると、コストが高くなる傾向がある。
CloudFormation
システムの復旧には、必要なリソースを1から構成する必要があるが、手動だと時間がかかる。
そのため、自動構成する仕組みが重要。
CloudFormationは、必要なリソースをテンプレートとして定義し、実行させることで必要なリソースが自動構成される。
1つのテンプレートから作成されたリソースは、スタックによって一元管理される。
リソースへの更新が必要な場合は、テンプレートを変更する。Lambda
回復性の高いアーキテクチャを構築/運用しやすくなる。
サーバレスアーキテクチャを実現できる。
コードを用意して、Lambda関数として登録すれば、AWSで管理している実行基盤で自動的に実行される。
実行タイミングは、様々なAWS上のイベントで紐付けることが可能。
- メリット
- 運用上の様々な操作を自動化できる。
- 障害などのシステム上のイベントを契機にして、自動化するような処理を実装しておけば、Lambdaが自動実行される。
- 運用者が可用性やスケーラビリティを管理する必要がない(マネージド型サービスで、コードの実行環境はAWSが全て管理しているため。)
まとめ
- 「単一のAZ」が正解ではないという前提で考える
- AWSマネージド型サービスの使用を常に優先する
- 耐障害性と高可用性は同じではない
- 投稿日:2019-05-06T12:06:59+09:00
(メモ) AWS CDK Pythonサンプルお試し
まだGAではないですがAWS CDKがPython対応したのでサンプルを動かしながらメモ。
CDKサンプルは他言語も含めてここにあります。
https://github.com/aws-samples/aws-cdk-examples動かしたサンプル
ECS Service on EC2 with ALBのサンプルアプリケーション
https://github.com/aws-samples/aws-cdk-examples/tree/master/python/ecs/ecs-load-balanced-service/app.py
リソースが記述された本体ファイル。これだけの記述。app.pyfrom aws_cdk import ( aws_ec2 as ec2, aws_ecs as ecs, cdk, ) class BonjourECS(cdk.Stack): def __init__(self, scope: cdk.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, *kwargs) vpc = ec2.VpcNetwork( self, "MyVpc", max_a_zs=2 ) cluster = ecs.Cluster( self, 'Ec2Cluster', vpc=vpc ) cluster.add_capacity("DefaultAutoScalingGroup", instance_type=ec2.InstanceType("t2.micro")) ecs_service = ecs.LoadBalancedEc2Service( self, "Ec2Service", cluster=cluster, memory_limit_mi_b=512, image=ecs.ContainerImage.from_registry("amazon/amazon-ecs-sample") ) cdk.CfnOutput( self, "LoadBalancerDNS", value=ecs_service.load_balancer.dns_name ) app = cdk.App() BonjourECS(app, "Bonjour") app.run()cdk.json
CDKがどのランタイムとコードでスタックを作成するかを指定するファイルcdk.json{ "app": "python3 app.py" }requirements.txt
必要なライブラリrequirements.txtaws-cdk.cdk aws-cdk.aws_ec2 aws-cdk.aws_ecs # Work around for jsii#413 aws-cdk.aws-autoscaling-commonデプロイ手順
PyCharm CE 2018.3 on MacOS Sierra(古...)上で作業しています
コードのclone
コマンドsudo git clone https://github.com/aws-samples/aws-cdk-examples.git cd aws-cdk-examples/python/ecs/ecs-load-balanced-service/aws cdk インストール/アップデート
コマンドsudo npm -g install aws-cdk cdk --version 0.30.0 (build 4740446)ライブラリインストール
コマンドpip install -r requirements.txtリソースの差分確認 (オプション)
cdk diffでリソースの差分確認を実施します。初期構築なので全てのリソースが追加差分として表示されます。
セキュリティ関連の変更をわかりやすく表示してくれるようになっていました。
*これ以降はAWSクレデンシャルとデフォルトリージョンが設定されていないとエラーになります。コマンドcdk diffOutputStack Bonjour IAM Statement Changes ┌───┬─────────────────────────────────────────┬────────┬─────────────────────────────────────────┬─────────────────────────────────────────┬───────────────────────────────────────────┐ │ │ Resource │ Effect │ Action │ Principal │ Condition │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Dr │ Allow │ lambda:InvokeFunction │ Service:sns.amazonaws.com │ "ArnLike": { │ │ │ ainECSHook/Function.Arn} │ │ │ │ "AWS:SourceArn": "${Ec2Cluster/DefaultA │ │ │ │ │ │ │ utoScalingGroup/DrainECSHook/Topic}" │ │ │ │ │ │ │ } │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Dr │ Allow │ sts:AssumeRole │ Service:lambda.${AWS::URLSuffix} │ │ │ │ ainECSHook/Function/ServiceRole.Arn} │ │ │ │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Dr │ Allow │ sns:Publish │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ ainECSHook/Topic} │ │ │ p/LifecycleHookDrainHook/Role} │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/In │ Allow │ sts:AssumeRole │ Service:ec2.${AWS::URLSuffix} │ │ │ │ stanceRole.Arn} │ │ │ │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Li │ Allow │ sts:AssumeRole │ Service:autoscaling.${AWS::URLSuffix} │ │ │ │ fecycleHookDrainHook/Role.Arn} │ │ │ │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Service/TaskDef/TaskRole.Arn} │ Allow │ sts:AssumeRole │ Service:ecs-tasks.${AWS::URLSuffix} │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ * │ Allow │ ecr:GetAuthorizationToken │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ │ │ ecs:CreateCluster │ p/InstanceRole} │ │ │ │ │ │ ecs:DeregisterContainerInstance │ │ │ │ │ │ │ ecs:DiscoverPollEndpoint │ │ │ │ │ │ │ ecs:Poll │ │ │ │ │ │ │ ecs:RegisterContainerInstance │ │ │ │ │ │ │ ecs:StartTelemetrySession │ │ │ │ │ │ │ ecs:Submit* │ │ │ │ │ │ │ logs:CreateLogStream │ │ │ │ │ │ │ logs:PutLogEvents │ │ │ │ + │ * │ Allow │ autoscaling:CompleteLifecycleAction │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ │ │ ec2:DescribeHosts │ p/DrainECSHook/Function/ServiceRole} │ │ │ │ │ │ ec2:DescribeInstanceAttribute │ │ │ │ │ │ │ ec2:DescribeInstanceStatus │ │ │ │ │ │ │ ec2:DescribeInstances │ │ │ │ + │ * │ Allow │ ecs:DescribeContainerInstances │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ │ │ ecs:DescribeTasks │ p/DrainECSHook/Function/ServiceRole} │ │ │ │ │ │ ecs:ListContainerInstances │ │ │ │ │ │ │ ecs:ListTasks │ │ │ │ │ │ │ ecs:SubmitContainerStateChange │ │ │ │ │ │ │ ecs:SubmitTaskStateChange │ │ │ │ │ │ │ ecs:UpdateContainerInstancesState │ │ │ └───┴─────────────────────────────────────────┴────────┴─────────────────────────────────────────┴─────────────────────────────────────────┴───────────────────────────────────────────┘ IAM Policy Changes ┌───┬─────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐ │ │ Resource │ Managed Policy ARN │ ├───┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │ └───┴─────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘ Security Group Changes ┌───┬─────────────────────────────────────────────────────────────────────┬─────┬─────────────────┬─────────────────────────────────────────────────────────────────────┐ │ │ Group │ Dir │ Protocol │ Peer │ ├───┼─────────────────────────────────────────────────────────────────────┼─────┼─────────────────┼─────────────────────────────────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup.GroupId} │ In │ TCP 32768-65535 │ ${Ec2Service/LB/SecurityGroup.GroupId} │ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │ ├───┼─────────────────────────────────────────────────────────────────────┼─────┼─────────────────┼─────────────────────────────────────────────────────────────────────┤ │ + │ ${Ec2Service/LB/SecurityGroup.GroupId} │ In │ TCP 80 │ Everyone (IPv4) │ │ + │ ${Ec2Service/LB/SecurityGroup.GroupId} │ Out │ TCP 32768-65535 │ ${Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup.GroupId} │ └───┴─────────────────────────────────────────────────────────────────────┴─────┴─────────────────┴─────────────────────────────────────────────────────────────────────┘ (NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np) Resources [+] AWS::EC2::VPC MyVpc MyVpcF9F0CA6F [+] AWS::EC2::Subnet MyVpc/PublicSubnet1/Subnet MyVpcPublicSubnet1SubnetF6608456 [+] AWS::EC2::RouteTable MyVpc/PublicSubnet1/RouteTable MyVpcPublicSubnet1RouteTableC46AB2F4 [+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnet1/RouteTableAssociation MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB [+] AWS::EC2::Route MyVpc/PublicSubnet1/DefaultRoute MyVpcPublicSubnet1DefaultRoute95FDF9EB [+] AWS::EC2::EIP MyVpc/PublicSubnet1/EIP MyVpcPublicSubnet1EIP096967CB [+] AWS::EC2::NatGateway MyVpc/PublicSubnet1/NATGateway MyVpcPublicSubnet1NATGatewayAD3400C1 [+] AWS::EC2::Subnet MyVpc/PublicSubnet2/Subnet MyVpcPublicSubnet2Subnet492B6BFB [+] AWS::EC2::RouteTable MyVpc/PublicSubnet2/RouteTable MyVpcPublicSubnet2RouteTable1DF17386 [+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnet2/RouteTableAssociation MyVpcPublicSubnet2RouteTableAssociation227DE78D [+] AWS::EC2::Route MyVpc/PublicSubnet2/DefaultRoute MyVpcPublicSubnet2DefaultRoute052936F6 [+] AWS::EC2::EIP MyVpc/PublicSubnet2/EIP MyVpcPublicSubnet2EIP8CCBA239 [+] AWS::EC2::NatGateway MyVpc/PublicSubnet2/NATGateway MyVpcPublicSubnet2NATGateway91BFBEC9 [+] AWS::EC2::Subnet MyVpc/PrivateSubnet1/Subnet MyVpcPrivateSubnet1Subnet5057CF7E [+] AWS::EC2::RouteTable MyVpc/PrivateSubnet1/RouteTable MyVpcPrivateSubnet1RouteTable8819E6E2 [+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PrivateSubnet1/RouteTableAssociation MyVpcPrivateSubnet1RouteTableAssociation56D38C7E [+] AWS::EC2::Route MyVpc/PrivateSubnet1/DefaultRoute MyVpcPrivateSubnet1DefaultRouteA8CDE2FA [+] AWS::EC2::Subnet MyVpc/PrivateSubnet2/Subnet MyVpcPrivateSubnet2Subnet0040C983 [+] AWS::EC2::RouteTable MyVpc/PrivateSubnet2/RouteTable MyVpcPrivateSubnet2RouteTableCEDCEECE [+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PrivateSubnet2/RouteTableAssociation MyVpcPrivateSubnet2RouteTableAssociation86A610DA [+] AWS::EC2::Route MyVpc/PrivateSubnet2/DefaultRoute MyVpcPrivateSubnet2DefaultRoute9CE96294 [+] AWS::EC2::InternetGateway MyVpc/IGW MyVpcIGW5C4A4F63 [+] AWS::EC2::VPCGatewayAttachment MyVpc/VPCGW MyVpcVPCGW488ACE0D [+] AWS::ECS::Cluster Ec2Cluster Ec2ClusterEE43E89D [+] AWS::EC2::SecurityGroup Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroup149B0A9E [+] AWS::EC2::SecurityGroupIngress Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup/from BonjourEc2ServiceLBSecurityGroup2185A60D:32768-65535 Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroupfromBonjourEc2ServiceLBSecurityGroup2185A60D3276865535EC4EE766 [+] AWS::IAM::Role Ec2Cluster/DefaultAutoScalingGroup/InstanceRole Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898 [+] AWS::IAM::Policy Ec2Cluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD [+] AWS::IAM::InstanceProfile Ec2Cluster/DefaultAutoScalingGroup/InstanceProfile Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471 [+] AWS::AutoScaling::LaunchConfiguration Ec2Cluster/DefaultAutoScalingGroup/LaunchConfig Ec2ClusterDefaultAutoScalingGroupLaunchConfig7B2FED3A [+] AWS::AutoScaling::AutoScalingGroup Ec2Cluster/DefaultAutoScalingGroup/ASG Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0 [+] AWS::SNS::Topic Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Topic Ec2ClusterDefaultAutoScalingGroupDrainECSHookTopic798CDC5F [+] AWS::IAM::Role Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3 [+] AWS::IAM::Policy Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33 [+] AWS::Lambda::Function Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31 [+] AWS::SNS::Subscription Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/TopicSubscription Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicSubscription5DE5A98D [+] AWS::Lambda::Permission Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic6C30136B [+] AWS::IAM::Role Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7 [+] AWS::IAM::Policy Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B [+] AWS::AutoScaling::LifecycleHook Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHook5CB1467E [+] AWS::ElasticLoadBalancingV2::LoadBalancer Ec2Service/LB Ec2ServiceLB381329CE [+] AWS::EC2::SecurityGroup Ec2Service/LB/SecurityGroup Ec2ServiceLBSecurityGroup45FED6DF [+] AWS::EC2::SecurityGroupEgress Ec2Service/LB/SecurityGroup/to BonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF5:32768-65535 Ec2ServiceLBSecurityGrouptoBonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF53276865535AC4204BB [+] AWS::ElasticLoadBalancingV2::Listener Ec2Service/LB/PublicListener Ec2ServiceLBPublicListenerA941070C [+] AWS::ElasticLoadBalancingV2::TargetGroup Ec2Service/LB/PublicListener/ECSGroup Ec2ServiceLBPublicListenerECSGroup3DC8690E [+] AWS::IAM::Role Ec2Service/TaskDef/TaskRole Ec2ServiceTaskDefTaskRole27A5D642 [+] AWS::ECS::TaskDefinition Ec2Service/TaskDef Ec2ServiceTaskDef8D94BAA3 [+] AWS::ECS::Service Ec2Service/Service/Service Ec2Service398F0E46 Outputs [+] Output Ec2Service/LoadBalancerDNS Ec2ServiceLoadBalancerDNS6983C9B2: {"Value":{"Fn::GetAtt":["Ec2ServiceLB381329CE","DNSName"]}} [+] Output LoadBalancerDNS LoadBalancerDNS: {"Value":{"Fn::GetAtt":["Ec2ServiceLB381329CE","DNSName"]}}CloudFormationテンプレート確認 (オプション)
cdk synthコマンドで実際に展開されるCloudFormationテンプレートのResourcesを確認できます。
コマンドcdk synthOutputMyVpcF9F0CA6F: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: Bonjour/MyVpc Metadata: aws:cdk:path: Bonjour/MyVpc/Resource MyVpcPublicSubnet1SubnetF6608456: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.0.0.0/18 VpcId: Ref: MyVpcF9F0CA6F AvailabilityZone: ap-northeast-1a MapPublicIpOnLaunch: true Tags: - Key: Name Value: Bonjour/MyVpc/PublicSubnet1 - Key: aws-cdk:subnet-name Value: Public - Key: aws-cdk:subnet-type Value: Public <...snip...>長いので省略。792行ありました。CDKを使うとCloudFormationを生で書くより記述量を削減できることがわかります。
デプロイ
cdk deployでデプロイします。
コマンドcdk deployOutputThis deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening). Please confirm you intend to make the following modifications: IAM Statement Changes ┌───┬─────────────────────────────────────────┬────────┬─────────────────────────────────────────┬─────────────────────────────────────────┬───────────────────────────────────────────┐ │ │ Resource │ Effect │ Action │ Principal │ Condition │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Dr │ Allow │ lambda:InvokeFunction │ Service:sns.amazonaws.com │ "ArnLike": { │ │ │ ainECSHook/Function.Arn} │ │ │ │ "AWS:SourceArn": "${Ec2Cluster/DefaultA │ │ │ │ │ │ │ utoScalingGroup/DrainECSHook/Topic}" │ │ │ │ │ │ │ } │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Dr │ Allow │ sts:AssumeRole │ Service:lambda.${AWS::URLSuffix} │ │ │ │ ainECSHook/Function/ServiceRole.Arn} │ │ │ │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Dr │ Allow │ sns:Publish │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ ainECSHook/Topic} │ │ │ p/LifecycleHookDrainHook/Role} │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/In │ Allow │ sts:AssumeRole │ Service:ec2.${AWS::URLSuffix} │ │ │ │ stanceRole.Arn} │ │ │ │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/Li │ Allow │ sts:AssumeRole │ Service:autoscaling.${AWS::URLSuffix} │ │ │ │ fecycleHookDrainHook/Role.Arn} │ │ │ │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ ${Ec2Service/TaskDef/TaskRole.Arn} │ Allow │ sts:AssumeRole │ Service:ecs-tasks.${AWS::URLSuffix} │ │ ├───┼─────────────────────────────────────────┼────────┼─────────────────────────────────────────┼─────────────────────────────────────────┼───────────────────────────────────────────┤ │ + │ * │ Allow │ ecr:GetAuthorizationToken │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ │ │ ecs:CreateCluster │ p/InstanceRole} │ │ │ │ │ │ ecs:DeregisterContainerInstance │ │ │ │ │ │ │ ecs:DiscoverPollEndpoint │ │ │ │ │ │ │ ecs:Poll │ │ │ │ │ │ │ ecs:RegisterContainerInstance │ │ │ │ │ │ │ ecs:StartTelemetrySession │ │ │ │ │ │ │ ecs:Submit* │ │ │ │ │ │ │ logs:CreateLogStream │ │ │ │ │ │ │ logs:PutLogEvents │ │ │ │ + │ * │ Allow │ autoscaling:CompleteLifecycleAction │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ │ │ ec2:DescribeHosts │ p/DrainECSHook/Function/ServiceRole} │ │ │ │ │ │ ec2:DescribeInstanceAttribute │ │ │ │ │ │ │ ec2:DescribeInstanceStatus │ │ │ │ │ │ │ ec2:DescribeInstances │ │ │ │ + │ * │ Allow │ ecs:DescribeContainerInstances │ AWS:${Ec2Cluster/DefaultAutoScalingGrou │ │ │ │ │ │ ecs:DescribeTasks │ p/DrainECSHook/Function/ServiceRole} │ │ │ │ │ │ ecs:ListContainerInstances │ │ │ │ │ │ │ ecs:ListTasks │ │ │ │ │ │ │ ecs:SubmitContainerStateChange │ │ │ │ │ │ │ ecs:SubmitTaskStateChange │ │ │ │ │ │ │ ecs:UpdateContainerInstancesState │ │ │ └───┴─────────────────────────────────────────┴────────┴─────────────────────────────────────────┴─────────────────────────────────────────┴───────────────────────────────────────────┘ IAM Policy Changes ┌───┬─────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐ │ │ Resource │ Managed Policy ARN │ ├───┼─────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │ └───┴─────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘ Security Group Changes ┌───┬─────────────────────────────────────────────────────────────────────┬─────┬─────────────────┬─────────────────────────────────────────────────────────────────────┐ │ │ Group │ Dir │ Protocol │ Peer │ ├───┼─────────────────────────────────────────────────────────────────────┼─────┼─────────────────┼─────────────────────────────────────────────────────────────────────┤ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup.GroupId} │ In │ TCP 32768-65535 │ ${Ec2Service/LB/SecurityGroup.GroupId} │ │ + │ ${Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │ ├───┼─────────────────────────────────────────────────────────────────────┼─────┼─────────────────┼─────────────────────────────────────────────────────────────────────┤ │ + │ ${Ec2Service/LB/SecurityGroup.GroupId} │ In │ TCP 80 │ Everyone (IPv4) │ │ + │ ${Ec2Service/LB/SecurityGroup.GroupId} │ Out │ TCP 32768-65535 │ ${Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup.GroupId} │ └───┴─────────────────────────────────────────────────────────────────────┴─────┴─────────────────┴─────────────────────────────────────────────────────────────────────┘ (NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np) Bonjour: deploying... Bonjour: creating CloudFormation changeset... 0/50 | 11:35:04 AM | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata 0/50 | 11:35:04 AM | CREATE_IN_PROGRESS | AWS::SNS::Topic | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Topic (Ec2ClusterDefaultAutoScalingGroupDrainECSHookTopic798CDC5F) 0/50 | 11:35:04 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7) 0/50 | 11:35:04 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Service/TaskDef/TaskRole (Ec2ServiceTaskDefTaskRole27A5D642) 0/50 | 11:35:04 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3) 0/50 | 11:35:04 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/InstanceRole (Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898) 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::EC2::EIP | MyVpc/PublicSubnet1/EIP (MyVpcPublicSubnet1EIP096967CB) 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::ECS::Cluster | Ec2Cluster (Ec2ClusterEE43E89D) 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::EC2::VPC | MyVpc (MyVpcF9F0CA6F) 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Service/TaskDef/TaskRole (Ec2ServiceTaskDefTaskRole27A5D642) Resource creation Initiated 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::SNS::Topic | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Topic (Ec2ClusterDefaultAutoScalingGroupDrainECSHookTopic798CDC5F) Resource creation Initiated 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7) Resource creation Initiated 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::EC2::EIP | MyVpc/PublicSubnet2/EIP (MyVpcPublicSubnet2EIP8CCBA239) 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::ECS::Cluster | Ec2Cluster (Ec2ClusterEE43E89D) Resource creation Initiated 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::EC2::InternetGateway | MyVpc/IGW (MyVpcIGW5C4A4F63) 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::EC2::EIP | MyVpc/PublicSubnet1/EIP (MyVpcPublicSubnet1EIP096967CB) Resource creation Initiated 0/50 | 11:35:05 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3) Resource creation Initiated 0/50 | 11:35:06 AM | CREATE_IN_PROGRESS | AWS::EC2::VPC | MyVpc (MyVpcF9F0CA6F) Resource creation Initiated 1/50 | 11:35:06 AM | CREATE_COMPLETE | AWS::ECS::Cluster | Ec2Cluster (Ec2ClusterEE43E89D) 1/50 | 11:35:06 AM | CREATE_IN_PROGRESS | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/InstanceRole (Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898) Resource creation Initiated 1/50 | 11:35:06 AM | CREATE_IN_PROGRESS | AWS::EC2::InternetGateway | MyVpc/IGW (MyVpcIGW5C4A4F63) Resource creation Initiated 1/50 | 11:35:06 AM | CREATE_IN_PROGRESS | AWS::EC2::EIP | MyVpc/PublicSubnet2/EIP (MyVpcPublicSubnet2EIP8CCBA239) Resource creation Initiated 1/50 | 11:35:07 AM | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata Resource creation Initiated 2/50 | 11:35:07 AM | CREATE_COMPLETE | AWS::CDK::Metadata | CDKMetadata 3/50 | 11:35:16 AM | CREATE_COMPLETE | AWS::SNS::Topic | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Topic (Ec2ClusterDefaultAutoScalingGroupDrainECSHookTopic798CDC5F) 4/50 | 11:35:21 AM | CREATE_COMPLETE | AWS::EC2::EIP | MyVpc/PublicSubnet1/EIP (MyVpcPublicSubnet1EIP096967CB) 5/50 | 11:35:22 AM | CREATE_COMPLETE | AWS::EC2::EIP | MyVpc/PublicSubnet2/EIP (MyVpcPublicSubnet2EIP8CCBA239) 6/50 | 11:35:22 AM | CREATE_COMPLETE | AWS::EC2::InternetGateway | MyVpc/IGW (MyVpcIGW5C4A4F63) 7/50 | 11:35:22 AM | CREATE_COMPLETE | AWS::EC2::VPC | MyVpc (MyVpcF9F0CA6F) 8/50 | 11:35:23 AM | CREATE_COMPLETE | AWS::IAM::Role | Ec2Service/TaskDef/TaskRole (Ec2ServiceTaskDefTaskRole27A5D642) 9/50 | 11:35:23 AM | CREATE_COMPLETE | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7) 10/50 | 11:35:23 AM | CREATE_COMPLETE | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3) 11/50 | 11:35:23 AM | CREATE_COMPLETE | AWS::IAM::Role | Ec2Cluster/DefaultAutoScalingGroup/InstanceRole (Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898) 11/50 | 11:35:26 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PublicSubnet2/Subnet (MyVpcPublicSubnet2Subnet492B6BFB) 11/50 | 11:35:26 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PublicSubnet1/Subnet (MyVpcPublicSubnet1SubnetF6608456) 11/50 | 11:35:26 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PublicSubnet1/RouteTable (MyVpcPublicSubnet1RouteTableC46AB2F4) 11/50 | 11:35:26 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PrivateSubnet2/Subnet (MyVpcPrivateSubnet2Subnet0040C983) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup (Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroup149B0A9E) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PrivateSubnet2/RouteTable (MyVpcPrivateSubnet2RouteTableCEDCEECE) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PrivateSubnet1/RouteTable (MyVpcPrivateSubnet1RouteTable8819E6E2) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | Ec2Service/LB/SecurityGroup (Ec2ServiceLBSecurityGroup45FED6DF) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::ElasticLoadBalancingV2::TargetGroup | Ec2Service/LB/PublicListener/ECSGroup (Ec2ServiceLBPublicListenerECSGroup3DC8690E) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PrivateSubnet1/Subnet (MyVpcPrivateSubnet1Subnet5057CF7E) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PublicSubnet2/Subnet (MyVpcPublicSubnet2Subnet492B6BFB) Resource creation Initiated 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::VPCGatewayAttachment | MyVpc/VPCGW (MyVpcVPCGW488ACE0D) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::IAM::InstanceProfile | Ec2Cluster/DefaultAutoScalingGroup/InstanceProfile (Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PrivateSubnet2/RouteTable (MyVpcPrivateSubnet2RouteTableCEDCEECE) Resource creation Initiated 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PublicSubnet1/RouteTable (MyVpcPublicSubnet1RouteTableC46AB2F4) Resource creation Initiated 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PublicSubnet1/Subnet (MyVpcPublicSubnet1SubnetF6608456) Resource creation Initiated 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PrivateSubnet2/Subnet (MyVpcPrivateSubnet2Subnet0040C983) Resource creation Initiated 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PrivateSubnet1/RouteTable (MyVpcPrivateSubnet1RouteTable8819E6E2) Resource creation Initiated 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PublicSubnet2/RouteTable (MyVpcPublicSubnet2RouteTable1DF17386) 11/50 | 11:35:27 AM | CREATE_IN_PROGRESS | AWS::EC2::VPCGatewayAttachment | MyVpc/VPCGW (MyVpcVPCGW488ACE0D) Resource creation Initiated 11/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33) 11/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::ElasticLoadBalancingV2::TargetGroup | Ec2Service/LB/PublicListener/ECSGroup (Ec2ServiceLBPublicListenerECSGroup3DC8690E) Resource creation Initiated 11/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | MyVpc/PrivateSubnet1/Subnet (MyVpcPrivateSubnet1Subnet5057CF7E) Resource creation Initiated 11/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::ECS::TaskDefinition | Ec2Service/TaskDef (Ec2ServiceTaskDef8D94BAA3) 12/50 | 11:35:28 AM | CREATE_COMPLETE | AWS::ElasticLoadBalancingV2::TargetGroup | Ec2Service/LB/PublicListener/ECSGroup (Ec2ServiceLBPublicListenerECSGroup3DC8690E) 12/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::EC2::RouteTable | MyVpc/PublicSubnet2/RouteTable (MyVpcPublicSubnet2RouteTable1DF17386) Resource creation Initiated 13/50 | 11:35:28 AM | CREATE_COMPLETE | AWS::EC2::RouteTable | MyVpc/PrivateSubnet2/RouteTable (MyVpcPrivateSubnet2RouteTableCEDCEECE) 13/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::ECS::TaskDefinition | Ec2Service/TaskDef (Ec2ServiceTaskDef8D94BAA3) Resource creation Initiated 14/50 | 11:35:28 AM | CREATE_COMPLETE | AWS::EC2::RouteTable | MyVpc/PublicSubnet1/RouteTable (MyVpcPublicSubnet1RouteTableC46AB2F4) 15/50 | 11:35:28 AM | CREATE_COMPLETE | AWS::EC2::RouteTable | MyVpc/PrivateSubnet1/RouteTable (MyVpcPrivateSubnet1RouteTable8819E6E2) 15/50 | 11:35:28 AM | CREATE_IN_PROGRESS | AWS::IAM::InstanceProfile | Ec2Cluster/DefaultAutoScalingGroup/InstanceProfile (Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471) Resource creation Initiated 16/50 | 11:35:29 AM | CREATE_COMPLETE | AWS::ECS::TaskDefinition | Ec2Service/TaskDef (Ec2ServiceTaskDef8D94BAA3) 16/50 | 11:35:29 AM | CREATE_IN_PROGRESS | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B) Resource creation Initiated 16/50 | 11:35:29 AM | CREATE_IN_PROGRESS | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD) Resource creation Initiated 17/50 | 11:35:29 AM | CREATE_COMPLETE | AWS::EC2::RouteTable | MyVpc/PublicSubnet2/RouteTable (MyVpcPublicSubnet2RouteTable1DF17386) 17/50 | 11:35:30 AM | CREATE_IN_PROGRESS | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33) Resource creation Initiated 17/50 | 11:35:32 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup (Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroup149B0A9E) Resource creation Initiated 17/50 | 11:35:32 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | Ec2Service/LB/SecurityGroup (Ec2ServiceLBSecurityGroup45FED6DF) Resource creation Initiated 18/50 | 11:35:33 AM | CREATE_COMPLETE | AWS::EC2::SecurityGroup | Ec2Service/LB/SecurityGroup (Ec2ServiceLBSecurityGroup45FED6DF) 19/50 | 11:35:34 AM | CREATE_COMPLETE | AWS::EC2::SecurityGroup | Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup (Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroup149B0A9E) 20/50 | 11:35:37 AM | CREATE_COMPLETE | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B) 21/50 | 11:35:37 AM | CREATE_COMPLETE | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD) 21/50 | 11:35:38 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroupEgress | Ec2Service/LB/SecurityGroup/to BonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF5:32768-65535 (Ec2ServiceLBSecurityGrouptoBonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF53276865535AC4204BB) 21/50 | 11:35:38 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroupIngress | Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup/from BonjourEc2ServiceLBSecurityGroup2185A60D:32768-65535 (Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroupfromBonjourEc2ServiceLBSecurityGroup2185A60D3276865535EC4EE766) 22/50 | 11:35:38 AM | CREATE_COMPLETE | AWS::IAM::Policy | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33) 22/50 | 11:35:38 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroupIngress | Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup/from BonjourEc2ServiceLBSecurityGroup2185A60D:32768-65535 (Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroupfromBonjourEc2ServiceLBSecurityGroup2185A60D3276865535EC4EE766) Resource creation Initiated 22/50 | 11:35:38 AM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroupEgress | Ec2Service/LB/SecurityGroup/to BonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF5:32768-65535 (Ec2ServiceLBSecurityGrouptoBonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF53276865535AC4204BB) Resource creation Initiated 23/50 | 11:35:39 AM | CREATE_COMPLETE | AWS::EC2::SecurityGroupIngress | Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup/from BonjourEc2ServiceLBSecurityGroup2185A60D:32768-65535 (Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroupfromBonjourEc2ServiceLBSecurityGroup2185A60D3276865535EC4EE766) 24/50 | 11:35:40 AM | CREATE_COMPLETE | AWS::EC2::SecurityGroupEgress | Ec2Service/LB/SecurityGroup/to BonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF5:32768-65535 (Ec2ServiceLBSecurityGrouptoBonjourEc2ClusterDefaultAutoScalingGroupInstanceSecurityGroupE49ADAF53276865535AC4204BB) 24/50 | 11:35:42 AM | CREATE_IN_PROGRESS | AWS::Lambda::Function | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31) 25/50 | 11:35:43 AM | CREATE_COMPLETE | AWS::EC2::VPCGatewayAttachment | MyVpc/VPCGW (MyVpcVPCGW488ACE0D) 25/50 | 11:35:43 AM | CREATE_IN_PROGRESS | AWS::Lambda::Function | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31) Resource creation Initiated 26/50 | 11:35:44 AM | CREATE_COMPLETE | AWS::EC2::Subnet | MyVpc/PrivateSubnet2/Subnet (MyVpcPrivateSubnet2Subnet0040C983) 27/50 | 11:35:44 AM | CREATE_COMPLETE | AWS::EC2::Subnet | MyVpc/PublicSubnet2/Subnet (MyVpcPublicSubnet2Subnet492B6BFB) 28/50 | 11:35:44 AM | CREATE_COMPLETE | AWS::Lambda::Function | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31) 29/50 | 11:35:44 AM | CREATE_COMPLETE | AWS::EC2::Subnet | MyVpc/PublicSubnet1/Subnet (MyVpcPublicSubnet1SubnetF6608456) 30/50 | 11:35:44 AM | CREATE_COMPLETE | AWS::EC2::Subnet | MyVpc/PrivateSubnet1/Subnet (MyVpcPrivateSubnet1Subnet5057CF7E) 30/50 | 11:35:47 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PublicSubnet2/DefaultRoute (MyVpcPublicSubnet2DefaultRoute052936F6) 30/50 | 11:35:47 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PublicSubnet1/DefaultRoute (MyVpcPublicSubnet1DefaultRoute95FDF9EB) 30/50 | 11:35:47 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PrivateSubnet2/RouteTableAssociation (MyVpcPrivateSubnet2RouteTableAssociation86A610DA) 30/50 | 11:35:47 AM | CREATE_IN_PROGRESS | AWS::SNS::Subscription | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/TopicSubscription (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicSubscription5DE5A98D) 30/50 | 11:35:47 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PublicSubnet1/DefaultRoute (MyVpcPublicSubnet1DefaultRoute95FDF9EB) Resource creation Initiated 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PublicSubnet2/RouteTableAssociation (MyVpcPublicSubnet2RouteTableAssociation227DE78D) 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PublicSubnet2/DefaultRoute (MyVpcPublicSubnet2DefaultRoute052936F6) Resource creation Initiated 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::Lambda::Permission | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic6C30136B) 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::NatGateway | MyVpc/PublicSubnet1/NATGateway (MyVpcPublicSubnet1NATGatewayAD3400C1) 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::NatGateway | MyVpc/PublicSubnet2/NATGateway (MyVpcPublicSubnet2NATGateway91BFBEC9) 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PrivateSubnet1/RouteTableAssociation (MyVpcPrivateSubnet1RouteTableAssociation56D38C7E) 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PublicSubnet1/RouteTableAssociation (MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB) 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::Lambda::Permission | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic6C30136B) Resource creation Initiated 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PrivateSubnet2/RouteTableAssociation (MyVpcPrivateSubnet2RouteTableAssociation86A610DA) Resource creation Initiated 30/50 | 11:35:48 AM | CREATE_IN_PROGRESS | AWS::EC2::NatGateway | MyVpc/PublicSubnet1/NATGateway (MyVpcPublicSubnet1NATGatewayAD3400C1) Resource creation Initiated 30/50 | 11:35:49 AM | CREATE_IN_PROGRESS | AWS::EC2::NatGateway | MyVpc/PublicSubnet2/NATGateway (MyVpcPublicSubnet2NATGateway91BFBEC9) Resource creation Initiated 30/50 | 11:35:49 AM | CREATE_IN_PROGRESS | AWS::SNS::Subscription | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/TopicSubscription (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicSubscription5DE5A98D) Resource creation Initiated 31/50 | 11:35:49 AM | CREATE_COMPLETE | AWS::SNS::Subscription | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/TopicSubscription (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicSubscription5DE5A98D) 31/50 | 11:35:49 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PublicSubnet2/RouteTableAssociation (MyVpcPublicSubnet2RouteTableAssociation227DE78D) Resource creation Initiated 31/50 | 11:35:49 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PublicSubnet1/RouteTableAssociation (MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB) Resource creation Initiated 31/50 | 11:35:49 AM | CREATE_IN_PROGRESS | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PrivateSubnet1/RouteTableAssociation (MyVpcPrivateSubnet1RouteTableAssociation56D38C7E) Resource creation Initiated 32/50 | 11:35:58 AM | CREATE_COMPLETE | AWS::Lambda::Permission | Ec2Cluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic (Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic6C30136B) 33/50 | 11:36:03 AM | CREATE_COMPLETE | AWS::EC2::Route | MyVpc/PublicSubnet1/DefaultRoute (MyVpcPublicSubnet1DefaultRoute95FDF9EB) 34/50 | 11:36:03 AM | CREATE_COMPLETE | AWS::EC2::Route | MyVpc/PublicSubnet2/DefaultRoute (MyVpcPublicSubnet2DefaultRoute052936F6) 35/50 | 11:36:04 AM | CREATE_COMPLETE | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PrivateSubnet2/RouteTableAssociation (MyVpcPrivateSubnet2RouteTableAssociation86A610DA) 36/50 | 11:36:04 AM | CREATE_COMPLETE | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PublicSubnet2/RouteTableAssociation (MyVpcPublicSubnet2RouteTableAssociation227DE78D) 37/50 | 11:36:05 AM | CREATE_COMPLETE | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PublicSubnet1/RouteTableAssociation (MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB) 38/50 | 11:36:05 AM | CREATE_COMPLETE | AWS::EC2::SubnetRouteTableAssociation | MyVpc/PrivateSubnet1/RouteTableAssociation (MyVpcPrivateSubnet1RouteTableAssociation56D38C7E) 38/50 | 11:36:08 AM | CREATE_IN_PROGRESS | AWS::ElasticLoadBalancingV2::LoadBalancer | Ec2Service/LB (Ec2ServiceLB381329CE) 38/50 | 11:36:10 AM | CREATE_IN_PROGRESS | AWS::ElasticLoadBalancingV2::LoadBalancer | Ec2Service/LB (Ec2ServiceLB381329CE) Resource creation Initiated 38/50 Currently in progress: Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471, MyVpcPublicSubnet1NATGatewayAD3400C1, MyVpcPublicSubnet2NATGateway91BFBEC9, Ec2ServiceLB381329CE 39/50 | 11:37:22 AM | CREATE_COMPLETE | AWS::EC2::NatGateway | MyVpc/PublicSubnet1/NATGateway (MyVpcPublicSubnet1NATGatewayAD3400C1) 39/50 | 11:37:26 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PrivateSubnet1/DefaultRoute (MyVpcPrivateSubnet1DefaultRouteA8CDE2FA) 39/50 | 11:37:27 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PrivateSubnet1/DefaultRoute (MyVpcPrivateSubnet1DefaultRouteA8CDE2FA) Resource creation Initiated 40/50 | 11:37:30 AM | CREATE_COMPLETE | AWS::IAM::InstanceProfile | Ec2Cluster/DefaultAutoScalingGroup/InstanceProfile (Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471) 40/50 | 11:37:34 AM | CREATE_IN_PROGRESS | AWS::AutoScaling::LaunchConfiguration | Ec2Cluster/DefaultAutoScalingGroup/LaunchConfig (Ec2ClusterDefaultAutoScalingGroupLaunchConfig7B2FED3A) 40/50 | 11:37:35 AM | CREATE_IN_PROGRESS | AWS::AutoScaling::LaunchConfiguration | Ec2Cluster/DefaultAutoScalingGroup/LaunchConfig (Ec2ClusterDefaultAutoScalingGroupLaunchConfig7B2FED3A) Resource creation Initiated 41/50 | 11:37:35 AM | CREATE_COMPLETE | AWS::AutoScaling::LaunchConfiguration | Ec2Cluster/DefaultAutoScalingGroup/LaunchConfig (Ec2ClusterDefaultAutoScalingGroupLaunchConfig7B2FED3A) 42/50 | 11:37:38 AM | CREATE_COMPLETE | AWS::EC2::NatGateway | MyVpc/PublicSubnet2/NATGateway (MyVpcPublicSubnet2NATGateway91BFBEC9) 42/50 | 11:37:39 AM | CREATE_IN_PROGRESS | AWS::AutoScaling::AutoScalingGroup | Ec2Cluster/DefaultAutoScalingGroup/ASG (Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0) 42/50 | 11:37:41 AM | CREATE_IN_PROGRESS | AWS::AutoScaling::AutoScalingGroup | Ec2Cluster/DefaultAutoScalingGroup/ASG (Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0) Resource creation Initiated 42/50 | 11:37:42 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PrivateSubnet2/DefaultRoute (MyVpcPrivateSubnet2DefaultRoute9CE96294) 43/50 | 11:37:43 AM | CREATE_COMPLETE | AWS::EC2::Route | MyVpc/PrivateSubnet1/DefaultRoute (MyVpcPrivateSubnet1DefaultRouteA8CDE2FA) 43/50 | 11:37:43 AM | CREATE_IN_PROGRESS | AWS::EC2::Route | MyVpc/PrivateSubnet2/DefaultRoute (MyVpcPrivateSubnet2DefaultRoute9CE96294) Resource creation Initiated 44/50 | 11:37:59 AM | CREATE_COMPLETE | AWS::EC2::Route | MyVpc/PrivateSubnet2/DefaultRoute (MyVpcPrivateSubnet2DefaultRoute9CE96294) 45/50 | 11:38:11 AM | CREATE_COMPLETE | AWS::ElasticLoadBalancingV2::LoadBalancer | Ec2Service/LB (Ec2ServiceLB381329CE) 45/50 | 11:38:15 AM | CREATE_IN_PROGRESS | AWS::ElasticLoadBalancingV2::Listener | Ec2Service/LB/PublicListener (Ec2ServiceLBPublicListenerA941070C) 45/50 | 11:38:15 AM | CREATE_IN_PROGRESS | AWS::ElasticLoadBalancingV2::Listener | Ec2Service/LB/PublicListener (Ec2ServiceLBPublicListenerA941070C) Resource creation Initiated 46/50 | 11:38:16 AM | CREATE_COMPLETE | AWS::ElasticLoadBalancingV2::Listener | Ec2Service/LB/PublicListener (Ec2ServiceLBPublicListenerA941070C) 46/50 | 11:38:20 AM | CREATE_IN_PROGRESS | AWS::ECS::Service | Ec2Service/Service/Service (Ec2Service398F0E46) 46/50 | 11:38:21 AM | CREATE_IN_PROGRESS | AWS::ECS::Service | Ec2Service/Service/Service (Ec2Service398F0E46) Resource creation Initiated 46/50 Currently in progress: Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0, Ec2Service398F0E46 47/50 | 11:39:12 AM | CREATE_COMPLETE | AWS::AutoScaling::AutoScalingGroup | Ec2Cluster/DefaultAutoScalingGroup/ASG (Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0) 47/50 | 11:39:17 AM | CREATE_IN_PROGRESS | AWS::AutoScaling::LifecycleHook | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHook5CB1467E) 47/50 | 11:39:18 AM | CREATE_IN_PROGRESS | AWS::AutoScaling::LifecycleHook | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHook5CB1467E) Resource creation Initiated 48/50 | 11:39:18 AM | CREATE_COMPLETE | AWS::AutoScaling::LifecycleHook | Ec2Cluster/DefaultAutoScalingGroup/LifecycleHookDrainHook (Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHook5CB1467E) 48/50 Currently in progress: Ec2Service398F0E46 49/50 | 11:40:22 AM | CREATE_COMPLETE | AWS::ECS::Service | Ec2Service/Service/Service (Ec2Service398F0E46) 50/50 | 11:40:26 AM | CREATE_COMPLETE | AWS::CloudFormation::Stack | Bonjour ✅ Bonjour Outputs: Bonjour.Ec2ServiceLoadBalancerDNS6983C9B2 = Bonjo-Ec2Se-1A3MUCXOPEOUH-xxxxxxxxxx.ap-northeast-1.elb.amazonaws.com Bonjour.LoadBalancerDNS = Bonjo-Ec2Se-1A3MUCXOPEOUH-xxxxxxxxxx.ap-northeast-1.elb.amazonaws.com Stack ARN: arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxx:stack/Bonjour/73780da0-6fa7-11e9-9437-0e72822fc3e0動作確認
OutputされたELBのURLにアクセスしてみます。
サンプルアプリが表示されました。ECS Serviceを確認します。
aws ecs describe-services --cluster Bonjour-Ec2ClusterEE43E89D-10ZEEGEWTOSJM --services Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC{ "services": [ { "serviceArn": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxx:service/Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC", "serviceName": "Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC", "clusterArn": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxx:cluster/Bonjour-Ec2ClusterEE43E89D-10ZEEGEWTOSJM", "loadBalancers": [ { "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxx:targetgroup/Bonjo-Ec2Se-ZEBMGDX3701N/d7c55f76db539196", "containerName": "web", "containerPort": 80 } ], "serviceRegistries": [], "status": "ACTIVE", "desiredCount": 1, "runningCount": 1, "pendingCount": 0, "launchType": "EC2", "taskDefinition": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxx:task-definition/BonjourEc2ServiceTaskDef2C3EE7C1:1", "deploymentConfiguration": { "maximumPercent": 200, "minimumHealthyPercent": 50 }, "deployments": [ { "id": "ecs-svc/9223370479744474602", "status": "PRIMARY", "taskDefinition": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxx:task-definition/BonjourEc2ServiceTaskDef2C3EE7C1:1", "desiredCount": 1, "pendingCount": 0, "runningCount": 1, "createdAt": 1557110301.205, "updatedAt": 1557110412.881, "launchType": "EC2" } ], "roleArn": "arn:aws:iam::xxxxxxxxxx:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS", "events": [ { "id": "37a6a1c7-a198-42b9-9af3-b1f6ba01b735", "createdAt": 1557110412.889, "message": "(service Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC) has reached a steady state." }, { "id": "24d2abc5-c8c1-4e82-ac1f-99ae8651b337", "createdAt": 1557110390.039, "message": "(service Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC) registered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxx:targetgroup/Bonjo-Ec2Se-ZEBMGDX3701N/d7c55f76db539196)" }, { "id": "2cd54fa3-8f26-4f7c-9a0e-556920496323", "createdAt": 1557110368.539, "message": "(service Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC) has started 1 tasks: (task 9f502db6-5a7a-433b-858b-18feb80ce01a)." }, { "id": "8fe3d401-4dee-404e-bb30-b6154570177c", "createdAt": 1557110302.67, "message": "(service Bonjour-Ec2Service398F0E46-1D6RQQKKMTFQC) was unable to place a task because no container instance met all of its requirements. Reason: No Container Instances were found in your cluster. For more information, see the Troubleshooting section of the Amazon ECS Developer Guide." } ], "createdAt": 1557110301.205, "placementConstraints": [], "placementStrategy": [], "healthCheckGracePeriodSeconds": 0, "schedulingStrategy": "REPLICA", "enableECSManagedTags": false, "propagateTags": "NONE" } ], "failures": [] }ECS Service、ホストのEC2インスタンスのAutoScalingはスケーリングポリシーは設定されずDesired Capacityを指定して手動スケールする設定となっていました。
- 投稿日:2019-05-06T10:31:17+09:00
EC2サーバが重いのでインスタンスストアボリュームを初期化した
はじめに
EC2インスタンスでインスタンスストアのボリュームを使う際は、最初に初期化をしなければなりません。
このことを知らず、初期化をしないで使い続けた結果、サーバに負荷をかけてしまったことと、初期化によって解決したことの記録です。起きたこと
・Webアプリの画面に、2時間ほど接続できなくなった
・Webアプリのバッチが、通常2分ほどで終わるものが30分かかった
・これらのことが不定期に起こった調査
sarコマンドで調べたところ、iowaitとロードアベレージの負荷が高いことはわかりました。
しかし、根本的な原因がわからないため、AWSサポートへ質問したところ、・インスタンスストアがボトルネックになった可能性がある
・インスタンスストアが高いディスクパフォーマンスを必要とする場合には一度初期化が必要だが、実施しているか
との回答をいただきました。インスタンスストアとは
インスタンスに刺さっているストレージです。高速に読み書きができる一方、サーバを再起動するとデータが消えてしまうという特徴があります。
本件では一時ファイルの置き場として利用しています。詳しくは公式ドキュメントを参照してください。
AWSドキュメント - インスタンスストア初期化とは
インスタンスストアボリュームへの最初の書き込みは、速度が遅いという特性があります。
先にすべての場所に書き込みを行うことで、パフォーマンスを上げることを「初期化」と呼ぶようです。数年前までは、EBSも同じように「プレウォーミング」と呼ばれる初期化を必要としていましたが、現在は不要になっています。
対応
インスタンスストアボリュームの初期化を実行しました。
方法は、dd
コマンドでインスタンスストアボリュームに書き込みをします。一度書き込むことで、そのあとの読み書きがスムーズになるという寸法です。!!!注意!!!
インスタンスを作成した直後に行うこと。
既に運用が始まっている場合は、バッチやCGIに影響を与えないよう、一時ファイル置き場の参照先を変更するなどの対策が必要です。$ dd if=/dev/zero of=/dev/sdb bs=1M $ dd if=/dev/zero of=/dev/sdc bs=1M $ dd if=/dev/zero of=/dev/sdd bs=1M $ dd if=/dev/zero of=/dev/sde bs=1M結果
不定期に起きていたサーバの高負荷が起きなくなくなりました。
注意点
インスタンスストアボリュームの初期化は、インスタンスを停止/起動(再起動)したときに都度行う必要があります。
起動スクリプトを組んでおくとよいでしょう。参考
- 投稿日:2019-05-06T08:20:08+09:00
AWS Machine Learning のサンプルを動かしてみる
あらすじ
これまで、混同行列とROC 曲線について学んでいきました。その知識を活かしてAWS Machine Learning を触っていきたいと思います。
前回までの話:
混同行列(Confusion Matrix) とは 〜 2 値分類の機械学習のクラス分類について
ROC 曲線とAUC を用いて2値分類機械学習モデルの性能を計測・チューニングするストーリー
今回はAWS 側のチュートリアルで準備されている「とあるお店にて新しい商品を出したときにそれに顧客は反応(購入)をしてくれるか?」という推測を機械学習(ロジスティック回帰)を利用して学んでいきます。
利用するデータについて
今回はMachine Learning(以下:ML)でチュートリアルとして公開されている以下のデータを使います。
ML チュートリアル用データ(banking.csv)
https://s3.amazonaws.com/aml-sample-data/banking.csvこのデータは過去の顧客の詳細な動向(商品の購入や銀行の引き落としなど)の情報がCSV 形式で記録されていてこれを学習用データとして使います。
そして学習後、特定の顧客があなたの新しい製品を出したときにアクションを起こす可能性が高いかどうかを機械学習を使って推測してみましょう。学習用データの形式
学習用データはAmazon ML 用の独自CSV 形式で準備します。どういったところが独自CSV 形式なのかというと、例えばCSV の先頭1
行目にはカラム名を挿入する等といったルールがあります。
詳しいデータのフォーマット形式については、以下のドキュメントを参考にしてください。Understanding the Data Format for Amazon ML
https://docs.aws.amazon.com/machine-learning/latest/dg/understanding-the-data-format-for-amazon-ml.html実際に上記の学習用データをテキストエディタで開くと、以下のように1 行目にカラム名が来てそれ以降はアトリビュートとして顧客の実データが入っています。
データの解説を少しすると、例えば上記の
job
はカスタマーの職業を表しており、contact
は問合せしたときのデバイスが記録されています。
そして一番右のフィールドはML モデルに「このカスタマーは新しい製品をサブスクライブしますか?」という質問への回答になり、いわゆるこれが、機械学習でいわれる教師データになります(Amazon ML ではターゲット属性と呼んでいる)。 この値は0(no) or 1(yes) の値になっており、オリジナルのデータはyes, no という文字列から、ML で学習させるために変換されています。
データの変換が完了したらML から読み取らせるために、あなたのS3 バケットにアップロードしてください。ただし、今回はAmazon で用意されているチュートリアルのファイルs3://ami-sample-data/banking.csv
を使ってを動かすため、S3 へのアップロード手順及び作業は割愛します。その他の機械学習で使えそうなデータについて
今回利用するお店の顧客の動向以外にも、機械学習に使えそうなデータが公開されているUCI Machine Learning Repository という場所があります。
UCI Machine Learning Repository
http://archive.ics.uci.edu/ml/index.php例えば上記サイトでは山火事が発生した場所の学習用データであったり、アイリスの花弁や萼片の長さと種類のデータであったり機械学習で使えるデータを無料で利用することができます。
CSV 形式で提供されてはいますが、Amazon ML で使う場合は一部CSV の形式をAmazon ML 形式に適用するよう修正する必要があります。データソースを作成する
学習用のデータが準備できたら、ML のデータソース作成へ進みます。
データソースとは、Amazon ML で使用する入力データのロケーション情報といった重要なデータやメタデータを含むAmazon ML のオブジェクトです。 Amazon ML はトレーニングや評価といったオペレーションのために、このデータソースを使います。
データソースを作成するために、以下の情報を設定します。
- データのS3 のロケーションとアクセス権限
- データ属性の名前及び型(Numeric(数値), Text(文字), Categorical(分類), binary(0 か1 か))
- ML が予測フェーズで回答してほしい属性名。すなわちターゲット属性、教師データの属性名
それでは実際にチュートリアル用のデータソースを作成していきましょう。まずはデータソース作成画面を開きます。
学習用データはAmazon S3 もしくはAmazon Redshift 上に格納されているものが利用できます。今回指定するのは公開S3 上に格納されている
s3://ami-sample-data/banking.csv
です。
必要事項を入力すると以下のようになります。
Verify
をクリックして検証を開始します。 ML がS3 のデータにアクセスする権限がない場合は、ここで権限を与えてよいか質問が来ます。
検証が完了すると次へ進めるのでContinue をクリックします。
スキーマ作成画面へ進みます。スキーマを作成する方法は主に以下の2 通りの方法があります。
- S3 にデータをアップロードする時に分割されたスキーマファイルをアップロードする方法
- Amazon ML サービス側に推測させる方法
今回はAmazon ML サービス側に推測させる方法で行ない、推測の結果を我々でレビューし、適宜修正して上げる方法で実施していきます。
【補足】スキーマファイルについての説明
https://docs.aws.amazon.com/machine-learning/latest/dg/creating-a-data-schema-for-amazon-ml.htmlスキーマ作成画面を見るとCSV データの1 行目に書いてあるカラム名からデータの型が推測されるので、これをレビューして正しいデータの型となるように適宜修正していきます。
設定できるデータの型のとしては以下のものがあります。
- Binary: yes またはno 等の2 つの状態のみを持つ属性
- Category: 種類を示すために数値または文字列が使用される属性
- Numeric: 順序に意味を持つ数値が使用される属性
- Text: 空白で区切られた、単語から成る文字列として使用される属性(「空白で区切られた」ということは現状英語しか対応してなさそう)
Binary yes またはno 等の2 つの状態のみを持つ属性 Category 種類を示すために数値または文字列が使用される属性 Numeric 順序に意味を持つ数値が使用される属性 Text 空白で区切られた、単語から成る文字列として使用される属性(「空白で区切られた」ということは現状英語しか対応してなさそう) このデータが間違ったままだとML が正しく学習を行えず、予測フェーズにて支障をきたす場合がありますので、注意してレビューを行うようにします。
内容を確認したら
Continue
をクリックします(今回のチュートリアル用データでは、全て正しく推測されるようになっている)。
すると、次はターゲット属性(教師データ)の選択画面へ移動します。
y
カラムを選択してContinue
をクリックします。
任意で行識別子(予測の時に、インプットデータのどの行に対応するのかを指し示すためのもの)を挿入するかどうかの選択が聞かれますので
no
を選択してReview
をクリックします。
内容に問題なければ
Continue
をクリックします。これでML モデルを作成する準備ができました。ML(Machine Learning) モデルを作成する
データソースを作成後、ML モデルを作成してトレーニングし、その結果を評価します。 ML モデルとはAmazon ML がトレーニング中にデータを見つけるパターン(どのような解析法を使う、学習の方法等)の集合でモデルを作成して予測に利用することができます。
ML モデル設定画面で、モデルの設定を行ないます。
ML model type BINARY 2 値分類。ロジスティック回帰アルゴリズムを使用してトレーニングされます ML model target y ターゲット属性。教師データとする属性 ML model target(任意) Banking tutorial ML model 名。デフォルトでデータソース作成時に指定した値が表示される Select training and evaluation settings Default (Recommended) ML の学習プロセスにおいてそれをコントロールするレシピとトレーニングパラメータ。今回は推奨となっている Default
を選択Evaluation Name Banking tutorial この評価の名前。デフォルトでデータソース作成時に指定した値が表示される 内容を確認したら
Review
をクリックします。
内容に問題がなければ
Create ML model
をクリックしてML モデルを作成します。
ボタンをクリックするとAmazon ML はプロセスキューにあなたのML 入れます。
ML を作成した直後はStatus がPending
になり、学習処理が開始されるとIn Progress
に変わり、完了するとCompleted
に変わります。
なおML で行われている学習ですが、今回のML ではデータ全体のうち70% のデータを学習用のデータとして抽出して残り30% を評価(テスト)として利用します。モデルの予測のパフォーマンスの確認と、スコアの閾値の設定
業界標準のArea Under the (Receiver Operating Characteristic) Curve (AUC: 曲線下面積)メトリクスというML モデルのパフォーマンス品質を計測する精度法を提供しています。 AUC はネガティブ(ポジティブ)なサンプルとポジティブ(ネガティブ)なサンプルに対する、正しさの精度(ML としての性能)を計測する指標を持っています。
ここでは実際にAUC を使ってML の性能を確認し、ML が使われるビジネスの戦略に適合した挙動となるように閾値を調整してみましょう。Amazon ML のリストページに移動しましょう。すると、以下のようなリストが表示されます。
その中で
Evaluation
を選択します。
すると上記のようなページが表示されるのでAUC(Area Under the Curve)パフォーマンスメロリックを確認しましょう。 するとAUC の値が0.936 と表示されており性能が良いということを表しています。
上記のままML を利用するのもアリなのですが、閾値を調整してML 推測の傾向をチューニングしてみましょう。 別の言い方をすれば、ML が「顧客は新しい商品を買ってくれますか?」という推測に対して、甘めの判断でYes!と推測するようにするか、厳し目の判断でNo!と推測とするかといった塩梅を、閾値を変更することで調整することができます。では閾値を調整するために
Adjust score threshold
をクリックしてみましょう。
するとTP(True Positive), TN(True Negative), FP(False Positive), FN(False Negative)の成績及び閾値を調整する項目が出現します。
表示されている内容を確認すると、正しく識別できている確率は91%($\dfrac{TP + TN}{TP + TN + FP + FN}=0.91$) で、だいたいの推測は問題なくできていることを示しています。この画面で調整できるのは主に閾値です。
閾値を上げたり下げたりすることで、ML の推測の性能がどのように変わっていくかという簡単な表は以下の通りです(ML モデルが出した推測結果の分布によっては必ずしもこうなるとは限りません)。
閾値 TP TN FP FN ↑ ↓ ↑ ↓ ↑ ↓ ↑ ↓ ↑ ↓ また、Advanced Metric に表示されている各項目の意味は以下の通りです。
False Positive Rate (FPR: 誤検出率)
本当は0(negative) と判断すべきところを誤って1(positive) と判断してしまう割合です。値が小さいほど性能が良いことを意味します。
- False Positive Rate(FPR)
$FPR=\dfrac{FP}{FP + TN}$
Presision(精度)
1(Positive) と予測されるべき全結果の中で実際に1(Positive) として予測できた割合です。この値が大きいほど性能が良いことを意味します。
- Presision
$Presision=\dfrac{TP}{TP + FP}$
Recall(リコール)
実際に1(Positive) を識別されるべき全データ(TP + FN)の内、実際に1(Positive)と識別されたデータ(TP)の割合を示します。この値が大きいほど性能が良いことを意味します。
- Recall
$Recall=\dfrac{TP}{TP + FN}$
Accuracy(正確さ)
全予測結果のうち、正しく1(Positive)と推測できたものと、正しく0(Negative)
と推測できたものの割合です。正しく予測できた割合です。この値が大きいほど性能が良いことを意味します。
- Accuracy
$Accuracy = \dfrac{TP + TN}{TP + FP + FN + TN}$
実際どれくらいの閾値に設定するか
今回のように新しい製品を打ってどれだけの人が反応してくれるかという問題については、本当は0(Negative)と判断すべき所を1(Positive)と判断してしまう(False Positive(FP))ことによって、余計な広告コストを払ってしまう可能性が高くなります(FP な人は買ってくれる見込みが殆どないので)。
そのため今回は誤検出率(FPR)を【下げる】ために、閾値を【上げる】というチューニングをして、余計な広告コストを払わない戦略を取るようにしてみましょう。ということで、画面より閾値を
0.77
あたりまで上げてみることにします。
すると全体の約3% の顧客を1(Positive) として推測されるようになります。そして、誤検出率(FPR) については0.8% 程までに下がるようになります。
(幾つか注意してほしい点としては、この約3% の中にはTP もFP も含まれていること、閾値0.5 の時よりPositive の総数が下がっていること、閾値を上げることでTP をいくつか失うこと等があります)
以上で閾値のチューニングは完了です。設定を保存してML の完成です。
誤検出率を下げすぎる(閾値を上げすぎる)とどうなるか
誤検出率を下げすぎると、ML はほぼすべてのケースで1(Positive) を回答してくれなくなります。
そして最大限まで誤検出率を下げる(閾値を上げる)と、ML はすべての推測要求に対して0(Negative)を返すようになってしまいます。 そのため、閾値は適度に設定するよう注意しましょう。
ML を使って予測をする
ML ができたところで、次はML を使って予測を実施してみましょう。
予測を実施する方式として、Amazon ML では主に以下の2 つの方法が用意されています。
- リアルタイム推測(real-time predictions)
- バッチ推測(iBatch Predictions)
リアルタイム推測(Try real-time predictions)
Web 画面から気軽に単発の推測要求を出すことができる、リアルタイム推測のやり方です。
ML model report
のナビゲーションペインからTry real-time predictions
を選択します。
Paste a record
を選択して以下の通りCSV レコードを入力します。
32,services,divorced,basic.9y,no,unknown,yes,cellular,dec,mon,110,1,11,0,nonexistent,-1.8,94.465,-36.1,0.883,5228.1
このCSV レコードはとある32 歳の人のデータになります。この32 歳の人が新しい商品を買ってくれそうか推測してみましょう。
ペーストして、Submit ボタンを押すとCSV の定義が画面に整形されてた状態で表示されます。
入力内容を確認したら
Create Prediction
をクリックします。
すると、ページ右側に予測の結果が出力されます。
結果を確認すると
predictedLabel
が0 となっています。すなわち、この32 歳の顧客は新しい製品を出しても、きっと購入してくれないだろうというML の予測結果になりました。
このようにしてリアルタイム推測を使うことで、単発の推測を即時で実行することができます。バッチ推測(Batch Predictions)
バッチ推測はあらかじめ推測させたい対象をCSV 形式でまとめておき、それをバッチ処理として推測させる方式です。
バッチ推測を利用するには画面左上のAmazon Machine Learning
のメニューをクリックしBatch Predictions
をクリックします。
Create new batch prediction
をクリックします。
作成したML モデルの一覧が表示されるので、先程作成したML を選択します。
内容を確認して
Continue
をクリックします。
バッチ推測で使用するデータは、サンプルとして用意されている
s3://aml-sample-data/banking-batch.csv
を利用します。
ラジオボタンでMy data is in S3, and I need to create a datasource
を選択し、その他ファイルのロケーション情報等を入力しContinue
ボタンをクリックします。
次に、結果を格納するS3 バケットのロケーションを設定します。このS3 ロケーションは予め作成しておく必要がありますが、S3 バケットの作成方法については説明を割愛します。
以下のように書き込み権限を要求されたら、Yes をクリックして書き込み権限を与えるようにしてください。
確認画面が表示されるので、確認して
Create batch prediction
をクリックします。
すると処理キューの中に推測バッチ処理が入り
Batch prediction summary
が表示され、処理が完了していない間はStatus がIn progress
で表示されます。
しばらくして画面を更新するとStatus
がCompleted
に更新されます。
/batch-prediction/result/
ディレクトリ配下にCSV ファイルをgz 圧縮したファイルが格納されているので、それをダウンロードして展開して中身を確認してみましょう。
中身はbestAnswer, score
の2 項目が表示されています。 score は指数表記になっている場合があるのでスプレッドシートなどに張り替えて変換すると良いでしょう。結果を確認すると総計4119 件で、1: Positiveは122 件、0: Negativeは 3997件あり、Positive とNegative の境界はML の評価のページで設定した0.77 になっています。
1 0.771587 1 0.771126 1 0.770161 0 0.767255 0 0.765271またPositive の中での最高スコアは約0.993179で最低スコアは約0.770161、Negative の中での最高スコアは約0.767255で最低スコアは約0.000678 となっていることがわかります。
本チュートリアルを通して、新しい商品に対して購入のアクションをしてくれそうな人をML で予測してコストを削減しながら効率的な宣伝をするといった戦略をとる手助けとしてAmazon ML を使うこともできるのです。
参考
Amazon Machine Learning
https://aws.amazon.com/jp/machine-learning/UCI Machine Learning Repository
http://archive.ics.uci.edu/ml/datasets.htmlチュートリアルの解説
https://docs.aws.amazon.com/machine-learning/latest/dg/tutorial.html
- 投稿日:2019-05-06T02:29:50+09:00
AWS Glue&Scala:ETLジョブでAmazon AthenaのPartitionKeyを追加する
Glue ジョブ スクリプトの編集にて
1.ソースファイルにimport文を追加
import com.amazonaws.services.glue.DynamicFrame import com.amazonaws.services.glue.DynamicRecord import com.amazonaws.services.glue.types.StringNode //今回はStringのみ。2.カラム追加処理(DynamicFrame.mapメソッド、DynamicRecord.addFieldメソッド/getFieldメソッド、StringNode)
val datasource0 = glueContext.getCatalogSource( -- 中略 -- ).getDynamicFrame() //カラム追加処理 //今回は、誕生日(birthday)を西暦、月、日にパーティショニングしたかったため以下の処理。 //用途に応じて読み替えてください。 val addField1 = datasource0.map((rec: DynamicRecord) => { val mbody = rec.getField("birthday")// DynamicRecord.getFieldメソッドで指定カラムの値をOption型で取得 val datePattern = """(\d{4})-(\d{2})-(\d{2})""".r //パターンマッチ用の正規表現 mbody match { case Some(mval:String) => { mval match { case datePattern(y,m,d)=>{ rec.addField("year",StringNode(y)) //西暦カラムを追加。birthdayの西暦をセット rec.addField("month",StringNode(m)) //月カラムを追加。birthdayの月をセット rec.addField("day",StringNode(d)) //日カラムを追加。birthdayの日をセット rec } case _ => rec } } case _ => rec } }) (以下略)以下、本家より
DynamicFrame
- def map
- def map( f : DynamicRecord => DynamicRecord, errorMsg : String = "", transformationContext : String = "", callSite : CallSite = CallSite("Not provided", ""), stageThreshold : Long = 0, totalThreshold : Long = 0 ) : DynamicFrame
- 指定した関数 "f" をこの DynamicFrame の各レコードに適用することで生成された新しい DynamicFrame を返します。
- このメソッドは、指定した関数を適用する前に各レコードをコピーするため、レコードを安全に変更できます。特定のレコードでマッピング関数から例外がスローされた場合、そのレコードはエラーとしてマークされ、スタックトレースがエラーレコードの列として保存されます。
DynamicRecord
- def addField
- def addField( path : String,dynamicNode : DynamicNode) : Unit
- 指定したパスに DynamicNode を追加します。
- path — 追加するフィールドのパス。
- dynamicNode — 指定したパスに追加する DynamicNode。
- def getField
- def getField( path : String ) : Option[Any]
- DynamicNode のオプションとして指定した path でフィールドの値を取得します。
- scala.Some Some (値) を返します。
3.追加マッピング
// val applymapping1 = datasource0.applyMapping( val applymapping1 = addField1.applyMapping( mappings = Seq( -- 中略 -- ("year","string","year","string"), //西暦カラムをStringで追加マッピング ("month","string","month","string"), //月カラムをStringで追加マッピング ("day","string","day","string"), //日カラムをStringで追加マッピング -- 中略 -- ), caseSensitive = false, transformationContext = "applymapping1" ) (中略)4.パーティショニングの設定
val datasink4 = glueContext.getSinkWithFormat( connectionType = "s3", options = JsonOptions( Map( "path" -> "s3://【s3のパス】", "partitionKeys" -> Seq( "year", "month","day") //西暦、月、日のパーティショニングを設定 ) ), transformationContext = "datasink4", format = "parquet" ).writeDynamicFrame(dropnullfields3)あとがき
- ScalaでのETLプログラミングに関する情報が少なく苦労した。(Pythonはそれなりにある)
- DynamicFrameを Apache Spark の DataFrame に変換してからカラム追加し、DynamicFrameに戻すやり方もあった。が、DynamicFrameで行えることはDynamicFrameで行うほうがよい。
- 困ったら本家のマニュアル
参考