- 投稿日:2019-12-05T23:47:48+09:00
ParquetにしたOSMのデータをEMRでいじってみる
目的
昨日PBFからParquetに変換したOpen Street Mapのデータを、AWS EMRでspark-sqlを使って触ってみる。
全球データ=大きい → 分散処理したい → AWS EMR今回は、こちらとこちらを参考にして、データ処理していきます。
準備
S3にParquetのデータをアップロード
前回作ったParquetのデータ3ファイルをS3のバケットにアップロードします。僕は簡単にAWSコンソールからアップロードしましたが、aws-cliを使うなり方法は他にもありますね。
キーペアの用意
EMRクラスタ用のキーペアを作って、ダウンロードしておきます。使うOSやSSHクライアントに合わせて鍵ファイルを選んでください。
クラスタの立ち上げ
AWSコンソールからEMRクラスタを立ち上げます。
ログ記録はなしで、Sparkクラスタを指定します。クラウド破産も怖いので、インスタンス数も2個くらいにしておきましょう。インスタンスタイプももっと小さいインスタンスでいい気がしますが、デフォルトで指定されているm5.xlargeをそのまま使っておきます。
「クラスターを作成」すると、しばらくしてクラスターが準備完了になります。セキュリティグループを設定してsshログイン
セキュリティグループがデフォルトで作成されますが、SSHが開いていないので、セキュリティグループを編集してmasterの方だけ外からのSSHを開けます。これで、先ほどのキーペアを使って手元からSSHログインできるようになります。
spark-shellを使って処理してみる
では、立ち上がったEMRクラスタを使って、Parquetに書き出したOSMデータを処理してみます。spark-shellを立ち上げます。
$ spark-shellデータファイルを読み込みます。
scala> import org.apache.spark.sql._ scala> import org.apache.spark.sql.types._ scala> val sqlContext = new SQLContext(sc) scala> val df = sqlContext.read.parquet("s3://bucket-name/Tokyo.osm.pbf.node.parquet")スキーマを確認します。
scala> df.printSchema() root |-- id: long (nullable = true) |-- version: integer (nullable = true) |-- timestamp: long (nullable = true) |-- changeset: long (nullable = true) |-- uid: integer (nullable = true) |-- user_sid: binary (nullable = true) |-- tags: array (nullable = true) | |-- element: struct (containsNull = true) | | |-- key: binary (nullable = true) | | |-- value: binary (nullable = true) |-- latitude: double (nullable = true) |-- longitude: double (nullable = true)緯度と経度だけを抜き出してみます。
scala> df.registerTempTable("Node") scala> val df3 = sqlContext.sql("SELECT id,latitude,longitude FROM Node") scala> df3.show +--------+------------------+------------------+ | id| latitude| longitude| +--------+------------------+------------------+ |31236558| 35.6351506|139.76784320000002| |31236561|35.635041900000004|139.76785900000002| |31236562| 35.6379133| 139.7591729| |31236563|35.638182900000004| 139.7585998| |31236564|35.638516100000004|139.75823490000002| |31236565|35.638767200000004| 139.7580599| |31236566| 35.6390562| 139.7579335| |31236567|35.639158200000004| 139.7579045| |31236568| 35.6393004| 139.7578741| |31236569| 35.6378794| 139.7589461| |31236570|35.637997500000004| 139.7586995| |31236571| 35.6382569| 139.7583386| |31236572| 35.6384817|139.75812480000002| |31236573|35.638980700000005| 139.7578382| |31236574|35.639336400000005| 139.7577578| |31236575| 35.6395142| 139.757745| |31236576| 35.6347773| 139.7699528| |31236579|35.634913100000006| 139.7705506| |31236580| 35.6350156| 139.7709312| |31236581|35.635654800000005| 139.7727102| +--------+------------------+------------------+ only showing top 20 rows結果をS3に保存します。
scala> df3.rdd.saveAsTextFile("s3://bucket-name/Tokyo.osm.pbf.node.txt")まとめ
なんかいけそう!
- 投稿日:2019-12-05T22:28:27+09:00
terraformでさくっとiamユーザーとグループを作ってみる
こんにちは。株式会社アダコテックのテックリードをしておりますkackyと申します。terraform Advent Calendar 2019の5日目の記事です。12月5日の22時から書き始めているのでかなりタイムアタックになっておりますw
やりたいこと
terraformでiamユーザを作って、それをグループに所属させる。そんなことをさくっとしたいときのやり方tipsです。
@raki さんのご指摘により記述を直しました!有用なご指摘ありがとうございます☆
ユーザー管理テーブルを変数定義する
こんな感じでユーザーとグループを対で登録します。
variable "users" { type = map(list(string)) default = { "kacky" = ["developer"], "tech" = ["developer"], "adaco" = ["intern"] ] }iam group作成
グループとして、developer、internに分けるとします。
resource "aws_iam_group" "developer" { name = "developer" path = "/" } resource "aws_iam_group" "intern" { name = "intern" path = "/" }iamユーザーとmembershipの作成
ユーザー、グループはterraform 0.12.6で追加されたfor_each文を使うとあっさりできました。
これを使えばLocal Valuesでちまちまフィルタリングする必要がなくなりました。ナンテコッタイ/(^o^)\resource "aws_iam_user" "users" { for_each = var.users user = each.key path = "/" } resource "aws_iam_user_group_membership" "example" { for_each = var.users user = each.key groups = each.value depends_on = [aws_iam_user.users] }まとめ
terraformの最新機能を使うとiamユーザーとグループといった一対の関係を直感的に変数定義してresourceに展開できるようになりました。他にもいろんな応用が利くと思いますのでお試しください。
※焦って書いているのでtypoしてたらすみません。今週中に直しますm(_ _)m
- 投稿日:2019-12-05T21:41:31+09:00
見せて貰おうか、Amazon新サービス「CodeGuru Reviewer」の性能とやらを
はじめに
これは、NTTコミュニケーションズ Advent Calendar 2019 5日目の記事です.関連記事は目次にまとめられています。前日は@TAR_O_RINさんによるTektonでCI/CDパイプラインを手の内化しよう - Qiitaでしたね!
あなたはだ〜れ?!
昨年書いた深層学習を利用した食事画像変換で飯テロ - Qiitaに既に書いてあるので省略します。好きな言語はObjective-Cです。お世話になった言語もObjective-Cです。嫌いな言語もObjective-Cです。最近はPythonしか書いてないのでマンネリ化が激しいです。最近のマイブームは夜な夜なサイクリングロードです。一緒にサイクリングしましょう。
モチベ
複数人で開発を行う上でコードレビューを行う文化は非常に重要ですよねー。ただ、
- 開発者はコードレビューをする時間を確保する必要がある
- 入念にチェックはしても一部の見逃しは発生する(人間だもの)
- レビュー担当者の技術レベルによってレビュー濃度がまちまち
などなど、他人のコードを読むのは大変勉強になるし、もはやレビューに何も抵抗は無いですが、時間が無い時などは後回しにしがち。ぶっちゃけコードレビューって大変ですよね。自動でコードレビューしてくれたらなあ。。。なんてそんなあなたに、
Amazon CodeGuru - Amazon Web Services
今北産業(三行まとめ)
- 2019年12月3日(米国時間)に公開された機械学習ベースの自動コードレビュー新サービスAmazon CodeGuruのCodeGuru Reviewerについての検証
- Amazon CodeGuruの利用方法について導入方法の解説
- Amazon CodeGuruにより自動生成されるレビューコメントへの言及
CodeGuruとは
CodeGuru自体の説明は既にいくつかのメディアが取上げているので概要についてはリンク先に委ねようと思います。要は機械学習ベースの自動コードレビュー(CodeGuru Reviewer)およびプロファイラー(CodeGuru Profiler)機能を提供するサービスです。CodeGuru ProfilerについてはAmazon CodeGuruを試してみた - Qiitaにて記載がありますが、CodeGuru Reviewerの方はあまり記載が無かったので前記のブログを補填する内容になっています。
- 英語記事
- 中国語記事
- 日本語記事
実際のCodeGuruの基調講演はYouTubeに公式がアップロードしていますので以下のYouTube動画を御覧ください。(Qiitaはインライン表示できないので画像リンクとしています。01:58:40からAmazon CodeGuruの発表が始まります。)
簡単な使用手順
ここでは、Amazon CodeGuruの利用方法について導入方法の解説を行います。
CodeGuru Reviewer利用の際の下準備編
自動レビュー機能を試す前にCodeGuruと自動レビュー対象とするレポジトリの紐付けを行います。Preview版ではGitHubおよびAWS CodeCommitをサポートしているようです。今回はGitHubによる検証を行いたいので、GitHubアカウント、GitHubレポジトリとの紐付けを行う想定で記載します。CodeCommitとの紐付けについては公式ガイドGetting Started with Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideを参照しましょう。
1. AWSマネジメントコンソールからCodeGuruを選択する
AWSにログイン後、 サービスを検索する の入力欄に CodeGuru と入力して、表示項目をクリック。
2. リージョンの変更
2019/12/5時点では、サポートリージョンが図中の5リージョンのみなので、もし同じ画面に遷移した場合は、何れかのリージョンを選択。今回は米国東部(オハイオ)を選択します。
3. Amazon CodeGuruからCodeGuru Reviewerの選択
Amazon CodeGuruのホーム画面です。今回はCodeGuru Reviewerによるコードの自動レビュー機能を試すので図中のいずれかをクリックしてレポジトリとの紐付けを行います。
4. CodeGuru ReviewerとGitHubアカウントの紐付け
CodeGuru Reviewerによる自動レビューの対象とするレポジトリの紐付けを行います。この時、CodeGuru Reviewerによる自動レビューを行う専用のGitHubアカウントを作成することをオススメします。公式ガイドGetting Started with Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideにも記載されていますが、専用のGitHubアカウントを介してCodeGuru Reviewerによるレビューが行われるので、人手によるレビューなのか自動か否かの混乱を防げます。また、複数のレポジトリを自動レビュー対象にする場合は、レポジトリに新規に作成したCodeGuru Reviewer専用のGitHubアカウントをレポジトリにCollaboratorsとして招待すればOKです。※ GitHubアカウントとの紐付け後に別のGitHubアカウントに変更する場合はブラウザのCookieを削除する必要があります。
5. CodeGuru Reviewerとレビュー対象レポジトリの紐付け
GitHub認証が正常に終了したらConnect to GitHubがConnectedへと変わるはずです。これでCodeGuru ReviewerとGitHubとの紐付けが完了します。次に、Repository locationのリストから自動レビューの対象とするレポジトリを選択してAssociateボタンをクリックすれば設定は終了です。(めちゃ楽)※ CodeGuru Reviewer専用のGitHubアカウントを自動レビュー対象レポジトリへ事前に招待して承認しておけば、勿論プライベートレポジトリも全て表示されます。また、Organizationsについても同様です。
6. CodeGuru Reviewer紐付け完了ステータスの確認
Associateボタンをクリック後は以下の画面に遷移します。紐付け直後のStatus部分はAssociatingと表示されますが、(レポジトリの規模によりますが)数分程度でAssociatedへと切り替わり下準備は完成です。(公式ガイドGetting Started with Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideでは数分程度と記載してありますが、空のレポジトリということもあり、今回は数秒でした。)※ ブラウザの更新か、画面中のAction左にある更新ボタンを押す必要があります。
簡単な検証
ここでは、Amazon CodeGuruから実際に自動レビューコメントを受けるまでの検証を行います。※ ここからは実験みたいなものなので文章の雰囲気が変わるやもしれません。
CodeGuru Reviewerによる自動レビューの例その1:まじめなコード編
今回はむかしのむかしに書いたコードをぶっこんで見ることにしましょう。現状はJavaのみのサポートのようなのでそれ以外のコードおよび変更点は多分無視されます。CodeGuru Reviewerによる自動レビューのトリガーはPull Requestにより作動します。おもむろにPRを出してやりましょう。
するとCodeGuruのDashboardに変化が見られると思います。色々と実験をしていたのでLines of code analyzedが実際と乖離していますが、自分が出したPRと書いたコードの行数が簡易的に表示されます。※ Reviewer recommendationsはいくら待っても変わりませんでした。バグなのかな?
(数分後…)適当にPR画面を見ていると何やらコメントが11件来てる。。。
恐る恐る確認してみると…なんか色々書かれていて、公式ガイドRecommendations in Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideによると以下5項目の観点からレビューをしているっぽい?AWS best practices、Concurrency、Sensitive information leak preventionについては今回のPRには含まれていないはずだからそれっぽいレビューコメントはついていないみたいだ。
The following kinds of recommendations are provided:
- AWS best practices
- Concurrency
- Resource leak prevention
- Sensitive information leak prevention
- Common coding best practices11件のコメントの中でユニークコメントだったのが以下の3つで残りはほぼResource leak preventionに関する指摘で全文まったく同じコメントレビューが記載してあった。問題点と解決策、参考リンクなど簡潔なレビューだけどどうやって生成してるんだろう…
CodeGuru Reviewerによる自動レビューの例その2:ウンコード編
皆さんはウンコード・マニアという神サイトはご存知だろうか?その名の通りウンコード撲滅を目論む崇高な神サイトである。読めば読むほど一度は…と心に刺さるものがあるので是非訪れてほしい。
さて、今回はウンコードをCodeGuru Reviewerにレビューしてもらおう。そう君は今日からUnCodeGuru Reviewerなのだから。
代表的なウンコード
現状のCodeGuru ReviewerはJavaのみのサポートであるので、Java ウンコード-ウンコード・マニアから引用させてもらおう。有名所で申し訳ないが好きなのはやっぱり波動拳ネスト型ウンコードである。(※ 画像はIndent Hadouken : ProgrammerHumorから引用。redditで議論されてて楽しい。)
もちろん、ウンコード・マニアも扱っているので、[Java] フルHD推奨。-ウンコード・マニアを引用する。
// [[Java] フルHD推奨。-ウンコード・マニア](http://unkode-mania.net/view/5040f47b9b6066a52e000003)を一部改変。 public class ClassSample { public static void main(String args[]) { int proc = 0; try { if ( proc == 0 ){ // 正常処理 try { if ( proc == 1 ){ // 正常処理 try { if ( proc == 2 ){ // 正常処理 try { if ( proc == 3 ){ // 正常処理 try { if ( proc == 4 ){ // 正常処理 if ( proc == 5 ){ // 正常処理 // まだまだ続くよ! } else { // 異常処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 握りつぶす } System.out.println("Hello! World!"); } }ウンコードのPRを同様に出す
さて先程と同様にPRを出して自動レビューのトリガーを引こう。…が待てよ待てとも一向にレビューが来ない…(ここでボロクソにレビュー来たら面白かったんですがね)前述の5項目の観点外だから音沙汰が無いのか、糞すぎるのか何れにしても何らかの反応はほしいですね。。
まとめ
つい先日公開されましたAmazon CodeGuru - Amazon Web Servicesの導入方法と実際に生成される自動レビューについて簡単に検証してみました。【IT】「Amazon CodeGuru」発表。機械学習したコンピュータが自動でコードレビューは5chスレですが、たしかに自動でコードレビューしてくれても実装難易度爆高な方法を提示されたりなんてことがあったらそれはそれで面白そうです。(囲碁・将棋であったように)実装難易度順に候補複数提示してくれるとかだったら神ですね。
また、今回はまだ試していないマルチスレッディング、AWS API等に関するコードレビューは時間があるときにでも試してみたいです。後、コードレビューの正当性についても言及したい。特にマルチスレッディング系はどんなレビューが生成されるのか気になる。
なにわともあれCodeGuru自体はプレビュー版なのとJavaのみのサポートということもありまだまだ開発途上ではありますが、実際に自動で生成される様を見ると、何やら若干の未来感を感じ心躍る気分になりました。早急にPythonとSwiftに対応してくれ〜〜!使うから!笑
明日は@jkr4869さんの記事です!!お楽しみに〜〜〜!!!
- 投稿日:2019-12-05T19:47:22+09:00
AWS EC2になぜかS3のマウントができない(/xxx/xxx にアクセスできません)
はじめに
- これは自分が実務で大いにハマった事象です。。。
- AWSを商用利用したのは初めてということもあり、初歩的なミスかもしれませんが、もしかしたら他にも苦しんでいる方がいるかもしれないので、ノウハウをシェアします。
事象
- 東京リージョンに作成したEC2を作成。
- データ領域として、S3をマウントしようと試みた。
- EC2インスタンスにS3をマウントする ←こちらの記事のように設定をしました
s3fsコマンド
を導入し、マウントを行ったところ、一見うまく実行できたように見える。
echo $?
による戻り値は0- ところが
lsコマンド
やdfコマンド
で状況を確認しようとすると、以下のエラーが出力される。[ec2-user@ip-xxx-xxx-xxx-xxx ~]$ ls -al /xxx/xxx ls: reading directory /xxx/xxx: 入力/出力エラーです [ec2-user@ip-xxx-xxx-xxx-xxx ~]$ df -k df: /xxx/xxx にアクセスできません: Transport endpoint is not connected ・・・中略・・・ [ec2-user@ip-xxx-xxx-xxx-xxx ~]$ sudo tail -3 /var/log/messages Jan 15 17:54:02 ip-xxx-xxx-xxx-xxx dhclient[1355]: bound to xxx-xxx-xxx-xxx -- renewal in 1693 seconds. Jan 15 17:59:31 ip-xxx-xxx-xxx-xxx s3fs: init $Rev: 367 $ Jan 15 17:59:35 ip-xxx-xxx-xxx-xxx s3fs: init $Rev: 367 $ [ec2-user@ip-xxx-xxx-xxx-xxx ~]$
- df: /xxx/xxx にアクセスできません: Transport endpoint is not connectedというメッセージでWeb検索しても、EC2にS3の許可をしているか?やバケット名は合っているか?という対処法が出てくる。
- 上記を再作成したり、何度も確認を行ったが、設定には問題がなさそう。
原因
- EC2の時間が所属するリージョンの時間と異なった、もしくは大幅に時間がずれていたことで本事象が発生。
- s3fsのデバッグ出力をする中でようやく発見しました
対処方法
- EC2のタイムゾーンをJapanにすることで解決。
[root@ip-xxx-xxx-xxx-xxx ~]# cat /etc/sysconfig/clock ZONE="UTC" UTC=true [root@ip-xxx-xxx-xxx-xxx ~]# view /etc/sysconfig/clock [root@ip-xxx-xxx-xxx-xxx ~]# cat /etc/sysconfig/clock ZONE="Japan" UTC=true [root@ip-xxx-xxx-xxx-xxx ~]# ln -sf /usr/share/zoneinfo/Japan /etc/localtime [root@ip-xxx-xxx-xxx-xxx ~]# reboot
- reboot終了後、
s3fsコマンド
でマウントを改めて実行したところ、問題なくS3がマウントされることを確認。
- 投稿日:2019-12-05T18:52:08+09:00
AWS で ARMのインスタンスを使ってみた。
AWSでARMを使てみる
はじめに
偶々AdventCalenderをを見ていたら12/5の分が余っていたためエントリして急いで作りました。
会議中ですが、爆速で執筆します。2018年11月にAWSでARMが使えるインスタンスの発表がありました。
https://aws.amazon.com/jp/blogs/news/new-ec2-instances-a1-powered-by-arm-based-aws-graviton-processors/こういう機能のようです。
https://aws.amazon.com/jp/ec2/instance-types/a1/ARMって耳にはしますが使ったことがないなー。ということでとりあえず使ってみます。
配備をしてみる
通常のEC2のAMIは使えないようです。
触ってみる
インスタンスを払い出すと通常のEC2インスタンスと同様に操作できるようです。
とりあえずログインしてみます。Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-1048-aws aarch64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Thu Dec 5 08:18:24 UTC 2019 System load: 0.09 Processes: 124 Usage of /: 0.2% of 484.35GB Users logged in: 0 Memory usage: 4% IP address for ens5: 10.10.242.100 Swap usage: 0% 0 packages can be updated. 0 updates are security updates. The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details.適当にpythonを動かしてみましたが動きました。
ubuntu@ip-10-10-242-100:~$ python3 Python 3.6.8 (default, Aug 20 2019, 17:12:48) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> print("watashi ha maloney") watashi ha maloney >>> quit()docker入れてーと進めたいところですが、dockerのインストール手順などは色々投稿されていたようなので、うーんいったい何をすればいいだろう。とりあえずjupyter とよくあるやつをいきなり入れてみます。
$ sudo apt update $ sudo apt install python-pip $ sudo apt install jupyter $ sudo apt install jupyterlab $ sudo apt install python3-dev $ sudo apt install python3-pip起動します。
$ jupyter lab --ip=0.0.0.0 --port 8081 --allow-root http://xxxxxxxxxxx:8081/?token=879077bd96XXXXXXXXXXXXXXXXXXXXXXXXXX32普通にjupyterlab が使えました。特に問題なく動きますね。 すごーい
そして挫折
あとは、matplotを入れてみようとしてみましたが・・・。
$ sudo pip3 install matplotlib --no-cache-dir Collecting matplotlib Downloading https://files.pythonhosted.org/packages/75/81/53ccadcb8cad0a9837f1487b57f2b46b21caa2b3f35f72bc1acb06b5825c/matplotlib-3.1.2.tar.gz (40.9MB) 100% |████████████████████████████████| 40.9MB 39.3MB/sなぜかここで固まってしまいました。
ここに限らず jupyter のインストールの際も、パッケージのインストール時に謎の硬直が度々発生しました。
原因は分かりませんが、これはちょっと気になりますね。
かなり放置してみたところ、無事再開・・・と思ったらインストールに失敗してしまいました。error: command 'aarch64-linux-gnu-gcc' failed with exit status 1いろいろ適当に入れてみます。
$ sudo apt-get install libpng-dev $ sudo apt-get install libfreetype6-devやっつけでしたが無事matplotが入りました。
あとはpython2系で動いてしまっているので、3系に切り替えればmatplotがjupyter上で動作しそうです。時間を見つけて、機械学習をARM上で動かしてみたいですね。
今日はこれまで。
- 投稿日:2019-12-05T18:45:02+09:00
【初心者向け】ゼロからできる!PythonでAWS SAMによるAPI作成とOpenAPIドキュメントの出力
はじめに
以前、Swaggerツールを使ったOpenAPIドキュメント作成とAPI自動生成の流れを紹介しました。
【超初心者向け】5分で試せる!OpenAPI(Swagger3.0)ドキュメント作成〜API自動生成今回は、AWS SAM(Serverless Application Model)を使ってAPI Gateway + AWS LambdaのサーバレスなwebAPIを作成し、OpenAPIドキュメントを出力してみます。
AWS SAMを使うのは初めてです。ゼロからできるように書いてみます。AWS SAMとは
AWS サーバーレスアプリケーションモデル (SAM、Serverless Application Model) は、サーバーレスアプリケーション構築用のオープンソースフレームワークです。迅速に記述可能な構文で関数、API、データベース、イベントソースマッピングを表現できます。リソースごとにわずか数行で、任意のアプリケーションを定義して YAML を使用してモデリングできます。デプロイ中、SAM が SAM 構文を AWS CloudFormation 構文に変換および拡張することで、サーバーレスアプリケーションの構築を高速化することができます。
SAM ベースのアプリケーションの構築を開始するには、AWS SAM CLI を使用します。SAM CLI により Lambda に似た実行環境が提供され、SAM テンプレートで定義されたアプリケーションの構築、テスト、デバッグをローカルで実行できます。お使いのアプリケーションを SAM CLI を使用して AWS にデプロイすることもできます。
https://aws.amazon.com/jp/serverless/sam/
CloudFormationをサーバーレスアプリケーションで利用しやすくしたフレームワークのようです。
API Gateway , AWS Lambda , DynamoDB をひとまとめに管理できます。前提
OS:Mac
AWSアカウント作成済みやること
- 環境構築(pyenv, pipenv)
- AWS CLIを使えるようにする
- AWS SAM CLIを使えるようにする
- AWS SAMでHello Worldしてみる
- OpenAPIドキュメントを出力する
環境構築(pyenv, pipenv)
1. pyenvのインストール
$ brew install pyenvbrewコマンドが使えない場合は、以下からHomebrewをインストール
https://brew.sh/index_ja.html2. pyenvのバージョン確認
$ pyenv --version pyenv 1.2.153. pyenvでpython3.7.0をインストール
$ pyenv install 3.7.04. pyenvでインストールしたバージョンに切り替え
localの場合は今いるディレクトリに、globalの場合は全体に反映されます。
$ pyenv global 3.7.0 $ pyenv local 3.7.05. 反映されているか確認
$ pyenv versions system * 3.7.0 (set by /Users/yusaku/.pyenv/version)6. pipenvのインストール
$ brew install pipenv7. pipenvの仮想環境に入る
$ pipenv shellAWS CLIを使えるようにする
1. aws cliのインストール
$ pipenv install awscli2. aws cliのバージョン確認
$ aws --version aws-cli/1.16.294 Python/3.7.5 Darwin/19.0.0 botocore/1.13.303. アクセスキーIDとシークレットアクセスキーの確認
https://console.aws.amazon.com/iam/home?#/security_credentials
アクセスキー(アクセスキーIDとシークレットアクセスキー)
の新しいアクセスキーの作成
からアクセスキーを作成し、アクセスキーIDとシークレットアクセスキーを確認します。4. awscliの設定
3で確認した情報で設定します。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-welcome.html$ aws configure AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]: jsonAWS SAM CLIを使えるようにする
1. AWS SAM CLIのインストール
$ pipenv install aws-sam-cli2. AWS SAM CLIのバージョン確認
sam --version SAM CLI, version 0.34.0AWS SAMでHello Worldしてみる
今回はpipenv環境にインストールしたpython3.7を使います。
1. アプリケーションの初期化
$ sam init SAM CLI now collects telemetry to better understand customer needs. You can OPT OUT and disable telemetry collection by setting the environment variable SAM_CLI_TELEMETRY=0 in your shell. Thanks for your help! Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 Which runtime would you like to use? 1 - nodejs12.x 2 - python3.8 3 - ruby2.5 4 - go1.x 5 - java11 6 - dotnetcore2.1 7 - nodejs10.x 8 - nodejs8.10 9 - nodejs6.10 10 - python3.7 11 - python3.6 12 - python2.7 13 - java8 14 - dotnetcore2.0 15 - dotnetcore1.0 Runtime: 10 Project name [sam-app]: AWSsam_test Allow SAM CLI to download AWS-provided quick start templates from Github [Y/n]: y ----------------------- Generating application: ----------------------- Name: AWSsam_test Runtime: python3.7 Dependency Manager: pip Application Template: hello-world Output Directory: . Next steps can be found in the README file at ./AWSsam_test/README.md2. アプリケーションをビルドする
$ cd AWSsam_test/ $ sam build3. アプリケーションをデプロイする
$ sam deploy --guided2回目以降は
sam deploy
でOKです。4. 動作確認
3で出力されるエンドポイントにアクセスしてみます。
$ curl https://hoge.hoge-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello world"}hello worldが返ってきました!
AWSコンソールにログインしてLambdaを見てみると、以下のようにAPI GatewayとLambdaが起動していました。
5.Lambda関数を少し変えて実行してみる
hello_world/app.py
を以下のように変更してみます。hello_world/app.pyimport json # import requests def lambda_handler(event, context): return { "statusCode": 200, "body": json.dumps({ "message": "hello Qiita", # "location": ip.text.replace("\n", "") }), }ビルドしてデプロイします。
$ sam build $ sam deployエンドポイントにアクセスしてみます。
$ curl https://hoge.hoge-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello Qiita"}変更が反映されていることが確認できました。
OpenAPIドキュメントを出力する
AWSコンソールからAmazon API Gatewayを選択し、デプロイしたAPIを選びます。
以下の画面が表示されるので、ステージをクリックしてエクスポートを選ぶと、Swagger仕様やOpenAPI仕様のドキュメントをjson形式やyaml形式で出力できます。
おわりに
今回はAWS SAMでREST APIを作成し、OpenAPI仕様ドキュメントをエクスポートしました。
逆にOpenAPI仕様をインポートしてAWS SAMを使ったAPI作成も実施してみたいです。
以下の記事で紹介されていますが、そんなに楽ではなさそう。。
https://dev.classmethod.jp/cloud/aws/serverless-swagger-apigateway/参考
Tutorial: Deploying a Hello World Application
https://dev.classmethod.jp/cloud/aws/aws-sam-simplifies-deployment/
- 投稿日:2019-12-05T17:53:37+09:00
Amazon EKSとTerraformを組み合わせたとある事例
はじめに
これは、Amazon EKS Advent Calendar 2019 6日目の記事です。
現在関わっているプロジェクトにおいて進行中のKubernetes移行で取り入れた技術や方法について、少し紹介したいと思います。
現状の構成がベストプラクティスであるということではなく、試行錯誤した結果の途中経過となりますが、何らかのヒントになれば幸いです。前提
- Amazon EKS 1.14
- ワーカーノードはEC2(Fargateではない)
- IaCはTerraform 0.12系を使う
Terraformでどこまで適用するか
はじめ、EKSクラスタについてはAWS CLIでの構築を考えていました。その理由としては、
- Terraformの適用環境はCodeBuildである(IAMロールを使う)
- kubectl実行環境は別にある(EC2+SessionManager+SSHの踏み台的な環境)
という前提で、kubectlの実行環境でクラスタを作成したほうがaws-authのConfigMapの適用で考えることが少なくて済む、という考えがありました。
しかし、現在はTerraformで管理する対象を増やしています。大きな理由としては、
- アプリケーションをデプロイする手前までの環境は自動化したい
というところです。
Terraformで適用するリソース
AWS Providerを使う
- EKSクラスタ
- ワーカーノード
- Auto Scaling Group
- Security Group
- IAM Role
Kubernetes Providerを使う
https://www.terraform.io/docs/providers/kubernetes/index.html
- ConfigMap: aws-auth
- Helm
- Service Account
- Cluster Role Binding
- External DNS
- Service Account
- Cluster Role
- Cluster Role Binding
- Deployment
Helm Providerを使う
https://www.terraform.io/docs/providers/helm/index.html
- Helm Chart
(できたらContainer Insights on Amazon EKSもHelmで導入したい...!)
これらを一通りTerraformで、 AWS modulesを活用しつつ構築することで、アプリケーション側のデプロイに専念できる形にする、というのが現時点での答えとなっています。
Kustomizeの利用
アプリケーションのマニフェストをdev/staging/productionといった環境ごとに重複しない形で管理できるようにKustomizeを利用しています。
デプロイ
デプロイはArgoCDを選定しています。
Argo CD - Declarative GitOps CD for Kubernetes
https://argoproj.github.io/argo-cd/これがベストかどうかはまだわかりませんが、利用事例の多さ、開発の頻度、Helmで一発で導入できること、Kustomizeへの対応、見やすいWebUIなどが魅力的だと感じています。
まだ確定はしていませんが、ECRへのイメージプッシュからLambdaを起動して、argocdのCLIからプッシュされたイメージのタグを指定してデプロイする方法(GitOps)が良さそうだと考えています。
Parameter Overrides
https://argoproj.github.io/argo-cd/user-guide/parameters/おわりに
とても簡単な解説にはなりますが、以上の組み合わせでKubernetesまわりの環境を構築しています(しようとしています)。
ここはどうなっているの?という質問や疑問がありましたら、Twitter @isaoshimizu まで優しくお気軽にメンションしていただけたら、こちらとしても大変ありがたい限りです。
- 投稿日:2019-12-05T17:53:37+09:00
Amazon EKSとTerraformの組み合わせたとある事例
はじめに
これは、Amazon EKS Advent Calendar 2019 6日目の記事です。
現在関わっているプロジェクトにおいて進行中のKubernetes移行で取り入れた技術や方法について、少し紹介したいと思います。
現状の構成がベストプラクティスであるということではなく、試行錯誤した結果の途中経過となりますが、何らかのヒントになれば幸いです。前提
- Amazon EKS 1.14
- ワーカーノードはEC2(Fargateではない)
- IaCはTerraform 0.12系を使う
Terraformでどこまで適用するか
はじめ、EKSクラスタについてはAWS CLIでの構築を考えていました。その理由としては、
- Terraformの適用環境はCodeBuildである(IAMロールを使う)
- kubectl実行環境は別にある(EC2+SessionManager+SSHの踏み台的な環境)
という前提で、kubectlの実行環境でクラスタを作成したほうがaws-authのConfigMapの適用で考えることが少なくて済む、という考えがありました。
しかし、現在はTerraformで管理する対象を増やしています。大きな理由としては、
- アプリケーションをデプロイする手前までの環境は自動化したい
というところです。
Terraformで適用するリソース
AWS Providerを使う
- EKSクラスタ
- ワーカーノード
- Auto Scaling Group
- Security Group
- IAM Role
Kubernetes Providerを使う
https://www.terraform.io/docs/providers/kubernetes/index.html
- ConfigMap: aws-auth
- Helm
- Service Account
- Cluster Role Binding
- External DNS
- Service Account
- Cluster Role
- Cluster Role Binding
- Deployment
Helm Providerを使う
https://www.terraform.io/docs/providers/helm/index.html
- Helm Chart
(できたらContainer Insights on Amazon EKSもHelmで導入したい...!)
これらを一通りTerraformで、 AWS modulesを活用しつつ構築することで、アプリケーション側のデプロイに専念できる形にする、というのが現時点での答えとなっています。
Kustomizeの利用
アプリケーションのマニフェストをdev/staging/productionといった環境ごとに重複しない形で管理できるようにKustomizeを利用しています。
デプロイ
デプロイはArgoCDを選定しています。
Argo CD - Declarative GitOps CD for Kubernetes
https://argoproj.github.io/argo-cd/これがベストかどうかはまだわかりませんが、利用事例の多さ、開発の頻度、Helmで一発で導入できること、Kustomizeへの対応、見やすいWebUIなどが魅力的だと感じています。
まだ確定はしていませんが、ECRへのイメージプッシュからLambdaを起動して、argocdのCLIからプッシュされたイメージのタグを指定してデプロイする方法(GitOps)が良さそうだと考えています。
Parameter Overrides
https://argoproj.github.io/argo-cd/user-guide/parameters/おわりに
とても簡単な解説にはなりますが、以上の組み合わせでKubernetesまわりの環境を構築しています(しようとしています)。
ここはどうなっているの?という質問や疑問がありましたら、Twitter @isaoshimizu まで優しくお気軽にメンションしていただけたら、こちらとしても大変ありがたい限りです。
- 投稿日:2019-12-05T17:36:55+09:00
IT初心者のSORACOMジャーニー SORACOM Kryptonで認証キーを手放す
本記事はSORACOM Advent Calendar 2019の6日目のものです!
「クラウドって何?サーバーって何?」の状態で、2018年8月からPython・AWSを勉強し始めて、今年はJAWS-FESTA2019にて「IT初心者のクラウドジャーニー」というテーマでプレゼンして、2019年11月12日 SORACOM-UG#1 札幌初開催! 勢いのままに、Qiita初投稿です!
初心者は何から始めるか?
プログラムもデバイスもクラウドサービス利用も全部わからないけど、自己満足できるレベルのアウトプットを求めて、IoT〜通信〜クラウドまで一連で勉強するテーマを決めました。
「定期的に写真を撮るデバイスを作って、簡単なホームページを開設してみる」
初期の構成要素
構成 デバイス 備考 デバイス Raspberry Pi 3B+ 通信手段 SORACOM SIM Plan−D クラウドサービス AWS S3、CLI、IAM プログラミング言語 Python 3.5.3 オープンソースモジュール OpenCV 4.1.1 開発の変遷
【第一段階】
1. ラズパイからPythonでPiカメラをOpenCVで起動させて、画像を撮影。
2. S3へのアクセス権限をIAMユーザから認証キーを払い出す。
3. AWS CLIで認証して、Boto3でS3へアップロード。
4. S3を公開にして、「index.html」に画像を埋め込んで、HTTPSにて静的サイトのURLを払い出す。
5. AWS Certificate Managerからドメインを購入し、静的サイトの独自ドメイン化を図る。
6. S3 → CloudFront → Route53 にて独自ドメインURLの払い出し。この構成で撮影画像をS3の静的サイト上で見れました!
セキュリティを考える
セキュリティの知識ゼロで、デバイスを作成していました。見直した際の問題点を下記に記載。
1.ラズパイにIAMユーザ権限を直接払い出し
2.ラズパイのホームをPi設定、初期パスワードのまま利用
3.S3のポリシーを全公開もうガバガバの状態でした!知らないって怖いです。
少しずつ整理をしていくことにしました。
セキュリティに対して、Kryptonに至る経緯
【まずAWS側でできるセキュリティを確認してみた】
1.AWS Cognitoを用いて、ユーザープールからIDを払い出し、承認キーの取得。
AWS CLIから、Pythonのboto3を用いてS3へアップロード完了!
2.AWS STSで認証をやってみようと思ったけどわからなかったので、やめる。
Cognitoは便利な機能であることがわかった。が、認証キーの管理はいずれにせよ発生する。【続いて、SORACOMのサービス内容を確認】
今回、作成したカメラデバイスとの相性がいいSORACOMサービスを選ぶ必要がある。
クラウドへのデータ転送を実施する上では、下記のサービスが存在する。
サービス名称 評価 結果 Beam 送信前後のプロトコル変換があり △ ※1 Funnel AWS IoTへの接続可能 ✖︎ ※2 Krypton AWS Cognitoの認証を肩代わり ○ ※3 ※1 データの暗号化におけるプロトコル変換が初心者の私にとっては、難しい。
※2 AWS IoTからS3へのデータ容量制限が128kByteであるため、画像をUPする上では不向き。
※3 認証キーの管理不要。認証の再生成時間も設定可能。
Kryptonを実装
SORACOMさんのホームページ(こちら)を参考に実装しました。
ホームページの内容のまま、SORACOM Kryptonを実装することができました。
ただし、画像をS3へUPする際に、PythonのSDKが無い?あるようだけど作る必要あり?とのことで、
手っ取り早く実装したいので、既にSDKが存在するNode.jsを勉強し、S3へのアップロード用のプログラムを一部書き換えました。コード公開です。毎分撮影画像を指定のS3へアップロード。
Node.js'use strict'; const AWS = require('aws-sdk'); const exec = require('await-exec'); const credentialsRefreshInterval = 15 * 60 * 1000; const fs = require('fs'); const cron = require('node-cron') // Executes krypton client to get open ID token async function getToken(){ let execResult = await exec("./krypton-cognito"); return JSON.parse(execResult.stdout); } // Initializes AWS credentials and set timer to refresh periodically async function initializeCredentials(){ let data = await getToken(); AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityId: data.identityId, Logins: { 'cognito-identity.amazonaws.com': data.token } }, {region: data.region}); setInterval( async () => { data = await getToken(); AWS.config.credentials.params.Logins['cognito-identity.amazonaws.com'] = data.token; AWS.config.credentials.refresh(err => { console.log("successfully refreshed AWS credentials"); }); }, credentialsRefreshInterval ); } initializeCredentials().then(() => { console.log("Successfully obtained AWS credentials"); const s3 = new AWS.S3(); const params = { Bucket: 'バケット名称', Key: "S3に保存する名前の画像名称" }; let v = fs.readFileSync("./撮影画像名称"); params.Body=v; cron.schedule(' 0 * * * * *',() => { s3.putObject(params, function(err, data){ if(err) console.log(eer, err.stack); else{ console.log(data); console.log('update image to S3!!'); } }); console.log('wait for next action'); }); })soracom-krypton-client-for-java中の”kryptonExamples”フォルダの中にある”cognito-auth.js”に対して、「定期実行」と「S3へのアップロード」を追加しています。
参照: Node.js 定期実行
参照: Node.js S3へのアップロード最終的な構成
Plan-DのSIMで上記の構成にたどり着きました!
無事に、認証キーを手放すことができました!さっそく大容量アップロード用のPlan-DU SIMで運用開始しようとしたが、Plan-DU用ドングルを購入しておらず、ドコモのWifiルーターで代用中だったことを思い出す。
あれ、そもそもWifiルーターに設置されたSIMに対してもKryptonって使えるんだけっけ?
よくわかっていないことが判明しました。とりあえず、Plan-DU用のドングルを購入しよう。そして、Wifiルーターに対するKrypton設定についてご存知の方、教えて頂きたいです。
出来ないような気もしてます。。。IT初心者が記載したブログとなります。
表現に誤りがある場合には、ご指摘いただけますと幸いです。
Advent Calendar 楽しんで参りましょう〜!参照サイト
【ラズパイの設定に関する資料】
参照: 【python/OpenCV】カメラの歪み補正を行う方法参照: ラズパイ3にOpenCV3/4を簡単に導入
OpenCVがPython2に入ってしまう場合に動きません。python3を起動させてcv2のバージョンをチェックしてみるといいです。参照: boto3を使ってS3にファイルのアップ&ダウンロード
boto3をラズパイに入れる際には、sudo pip3 install boto3
pip3で入れないと、Python3で使えないので注意ください。【ラズパイとクラウド連携に関する資料】
参照: AWS CLIの設定とS3へのアップロード【S3の静的サイト作成に関わる資料】
参照: 【図解】AWS S3を静的なWebサーバーとして利用する【クラウド上のセキュリティを高める資料】
参照: AWSにおける静的コンテンツ配信パターンカタログ(アンチパターン含む)
- 投稿日:2019-12-05T17:19:10+09:00
API Gateway - API キャッシュを有効にして応答性を強化する
■概要
API キャッシュを有効にして、エンドポイントのレスポンスがキャッシュされるようにできます。
- エンドポイントへの呼び出しの数を減らすことができる。
- API へのリクエストの低レイテンシーを実現することができる。
ステージに対してキャッシュを有効にすると、API Gateway は、秒単位で指定した有効期限 (TTL) が切れるまで、エンドポイントからのレスポンスをキャッシュします。
その後、API Gateway は、エンドポイントへのリクエストを行う代わりに、キャッシュからのエンドポイントレスポンスを調べてリクエストに応答します。■やってみる
□APIキャッシュを有効にする
デフォルトでは無効になっているので、
- APIキャッシュを有効化にチェック
- キャッシュキャパシティ:0.5GB
- キャッシュ有効期間:180
ぐらいでやってみる。
※API キャッシュを有効にすると追加料金が発生します。
□詳細 CloudWatch メトリクスを有効化にし、動作確認する
- 詳細 CloudWatch メトリクスを有効化にチェック
CacheMissCountが1、その他はCacheHitCountとしっかりキャッシュが効いているようです。
■リンク
- 投稿日:2019-12-05T17:13:09+09:00
[AWS]Amplify DataStore を Vue で使う
はじめに
先日 Amplify DataStore というものがリリースされました。
詳しくは公式ブログで確認していただければいいのですが、簡単に使ってみたところ、GraphQLの書き方をしなくてもGraphQLを使えるという感想でした。
今までAmplifyでAppSync(GraphQL)を使っていたやり方と比較しながら解説していこうかと思います。(参考記事)Getting Started
まずVueのプロジェクトを作りましょう。
$ npm install -g @vue/cli $ vue create amplify-datastore-sample $ cd amplify-datastore-sampleamplifyの初期化などをします
$ npx amplify-app処理が完了すると
amplify
というディレクトリが生成されています。
その中にはAmplifyで利用するAWSサービスの情報が入っていきます。
amplify/backend/api/<datasourcename>/schema.graphql
の内容を書き換えていきます。schema.graphqlenum PostStatus { ACTIVE INACTIVE } type Post @model { id: ID! title: String! rating: Int! status: PostStatus! }AWSの環境にデプロイしていきます。
$ npm run amplify-modelgen実行するとAppSyncとDynamoDBがデプロイされます。
これでAWS側の準備は完了です。Settings
必要なパッケージをインストールしておきます。
$ npm i @aws-amplify/core @aws-amplify/datastoreインストールしたパッケージをimportしておきます。
models
というディレクトリはnpm run amplify-modelgen
を実行したときにsrc
以下に生成されます。import { DataStore } from "@aws-amplify/datastore" import { Post, PostStatus } from "./models"Save Data
データを書き込むときは
DataStore.save()
を使います。await DataStore.save( new Post({ title: "My First Post", rating: 10, status: PostStatus.ACTIVE }) )GraphQLで書くと…
const saveBody = ` mutation { putData( input: { title: "My First Post", rating: 10, status: "active" } ) } ` await API.graphql(graphqlOperation(saveBody))Query Data
データを取得するときは
DataStore.query()
を使います。const posts = await DataStore.query(Post)limitや条件を指定することもできます。
await DataStore.query( Post, c => c.status("eq", PostStatus.ACTIVE), { limit: 10 } )GraphQLで書くと…
const queryBody = ` query { queryData(limit: 10){ items { id, title, rating, status } } } ` const posts = await API.graphql(graphqlOperation(queryBody))Delete Data
データを削除するときは
DataStore.delete()
を使います。const todelete = await DataStore.query(Post, "1234567") await DataStore.delete(todelete)ちなみに論理削除となるので、DynamoDBからデータが削除されることはありません。
_deleted
というフラグが立ちます。
GraphQLで書くと…
const deleteBody = ` mutation { deleteData( input: { id: "1234567" } ) } ` await API.graphql(graphqlOperation(deleteBody))さいごに
GraphQLのquery文で結構コード量が増えたり、query文の生成がめんどくさかったりとするのですが、AmplifyDataStoreを使うと、そういったところを解消してくれるそうですね。
プロダクションレベルで使うためには認証周りだったりなどを詰めていかないといけませんが、とても使う価値がありそうです。
ではまた!
- 投稿日:2019-12-05T16:40:43+09:00
AWS re:Invent 2019のEXPO出展企業を現地限定ノベルティと共に振り返る
アメリカのラスベガスで開催されている、AWSの年次カンファレンス「re:Invent 2019」には、グローバルから数多くの企業が出展しています。
企業の出展ブースはEXPOと呼ばれ、多くの来場客で賑わっています。
そんなre:InventのEXPOを回ってきましたので、私が会話した出展企業を振り返り、傾向などを分析したいと思います。
でも、ただ振り返るだけでは面白くないので、これだけ回ってきたんだぞと言わんばかりに、各ブースで配っていたノベルティを紹介していきたいと思います。
そもそもノベルティとは
re:InventやIT系イベントにおけるノベルティとは、平たく言えば、各企業が出しているグッズのことです。
ノベルティには、各企業を象徴するロゴやメッセージが載せられています。
米国のみならず、日本でも多くの企業がイベントで何かしらのノベルティを出しています。re:Inventでは、多くの企業がステッカー、Tシャツ、ピンバッジのいずれかを出していました。
ただ、少し変わった路線も増えてきており、フィギュア、帽子、靴下、モバイルバッテリー、さらにはバウンドさせると光るボールなどを出している企業もありました。re:Inventにおけるノベルティ入手方法
基本的には英語で会話でき、ブースの方とユースケースやビジネスの相談等してからでないと、ノベルティは貰えません。
ただ、中にはカンファレンスバッチに個人ごとに付属するバーコードを読み取るだけで、ノベルティがもらえるブースもありました。
基本的に、バーコード読み取りだけで貰えるのは小物が多く、Tシャツがバーコード読み取りだけでもらえるブースはなかったと思います。出展企業とノベルティ
さて、ここから怒涛のように出展企業を振り返っていきたいと思います。
Nutanix
展示員の方にXi Clustersと、マネジメントコンソールの説明をいただきました。
Nutanix on AWSも覚えておきたいキーワードでした。ParkMyCloud
AWSのコストマネジメントツールについて説明をいただきました。
どこにどのくらいのコストが掛かっているのかを1つのMAPのように表示し、対策できます。
一画面で全範囲のコストを確認できるという説明でした。Zendesk
最新バージョンのZendeskの管理画面を見せていただきました。
CTO.ai
CLIやSlackコマンドを通じて、AWSのリソース管理ができるようです。
Slackとのコラボレーションは他のブースでも良く話を聞きました。
開発とコミュニケーションの一体化は、現在のトレンドのように見えました。paloalto
ネットワークルータと仮想アプライアンスの説明をいただきました。
アメリカでは、MarketPlaceを中心としたビジネスが確立されている感があります。IBM
バッグをいただきました。
なんとAWSのセキュリティを管理するためのツールをリリースしているとのこと。
ただ目指す方向はマルチクラウドだそうです。
自社でもクラウドサービスを抱えるIBMが、先導してマルチクラウドに取り組み始めています。AWS China
ask me (AWS様スタッフ) のTシャツをいただきました。
東京リージョンとの接続に関する情報が少ないと言われるお客様が多いそうです、
また、今後はアップデートのスピードを上げていくとのことでした。ExtraHop
Tシャツをいただきました。
『箱の中身は何だ』ゲームに取り組むと貰えました。
ネットワークの分析・監視ツールをリリースされていました。Sophos
セキュリティソリューションは、クラウド特化を進めているようです。
databricks
靴下をいただきました。
データ分析環境をAWS上にデプロイしてNotebookで分析・可視化ができます。TIBCO
Tシャツをいただきました。
イベント駆動型アプリを構築するための、超軽量のGoベースのオープンソースエコシステム「flogo.io」です。splunk
インシデント管理・監査のためのVictorOpsとSplunkの連携について説明いただきました。
Okta
シングルサインオン、MFAなどの認証を統合的に実現するサービスです。
日本でも名が知れてきており、コンソールは日本語にも対応しています。
日本語のコンソールで説明いただき、本当に感謝でした。Rapid7
脆弱性管理、ペネトレーションテスト等を提供するサービスです。
日本でも取り扱いがあります。Capital One
Tシャツをいただきました。
展示員の方のトランプマジックに参加した後、バンキングソリューションとバックエンドの話で盛り上がり、最後に、トランプかTシャツかどちらが良いか、という選択で、Tシャツを貰いました。
SignalFx
こちらもSaaS型の監視・分析サービスです。
Instana
アプリケーションのパフォーマンスモニタリングに関するサービスを提供されています。
Global Knowledge
言わずもがな、技術教育を提供されています。
StackRox
バウンドさせると光るボールをいただきました。
原型をとどめたまま電池を換える方法はなさそうです。
コンテナやKubernetesのセキュリティに特化したサービスです。
AWS
EXPOのサービス別ブースで英語で質問して会話できないと貰えない引換券があり、引換券をSWAG DESKという受付に持っていくと、Tシャツが貰えました。
こちらはManaged BlockchainのTシャツになります。
この他にも、サービス毎に靴下等のノベルティがあったようです。
その他、各種ステッカー類
出展内容から感じた傾向
監視・ログ管理系を中心にSaaS型サービスが豊富
様々な企業が、それぞれ独自のUIで個性を主張し、SaaS型でこれらのサービスを展開される傾向があると感じました。
全て似たように思えるかも知れませんが、例えば同じ分析でも、フィルターしながら各個の項目を詳細に眺めるのと、タイル状のマップで全サービスの状態を俯瞰できるのとでは、やはり違うと思います。
特に監視とログ管理系は各企業でかなり力を入れて豊富に展開されており、この分野で戦うにはかなり強烈な個性が必要だと感じました。
また、利用する側に立てば、製品の特徴を押さえ、自分たちが本当に必要で使いやすいサービスを選んでいくべきだと感じました。
ただ、比較しだすときりがなく、ある程度直感で選ぶことも大事でしょう。ハードウェア中心と思われていた企業はマルチクラウド/ハイブリッドに
例えばIBMやNutanixは、独自のハードだけで攻めるのではなく、クラウドとの連携を打ち出してきていました。
特にIBMは、自社でもIBM Cloudを抱えているのに、AWSに対応したセキュリティサービスを出してきており、その裏側にはやはりマルチクラウドを意識した戦略があると感じました。
Nutanixの場合は、AWSとの接続によるハイブリッドクラウド戦略に重きが置かれていると思いました。
AWS自身もOutpostsをリリースしており、この辺りの市場動向はどうなっていくのかと思うと、目が離せないです。ブース巡りの所感
ノベルティはTシャツなどの衣類が中心に
ブースでTシャツや靴下を配る企業が爆発的に増えていると感じました。
明らかに集客効果があるのだと思います。
また、Tシャツなら、後から身につけてくれればそれだけで宣伝になるなどの考えもあるのだと思います。
多くの企業が効果があると考えているからこそ、ブームになっているのだと思いました。
日本でも、ノベルティに力を入れる企業が増えていきそうな気がします。ブース巡りは英語力も大事
1年前、AWS re:Invent 2018に参加した際は、英語が全く話せず、ただただブースを回って、置いてあるノベルティを持って帰るだけになり、あまりにふがいなく終わっていました。
もちろん、ブースでの会話ができないので、ステッカーやピンバッチといった小物以外のものは貰えませんでした。
一番の目的である、情報収集も殆どできていなかったです。これではまずいですし、面白くないし楽しめていないと思い、今年の1月から英会話の勉強を始め、11月末までにそこそこに聞き取れつつ片言ではあるものの喋れるようになりました。
今年のre:Invent 2019では、その腕試しとの思いでEXPOに挑みましたが、英語が少しでも話せるだけで、ブースでの会話が楽しくなり、情報収集やノベルティ集めも昨年に比べてかなり出来たと感じました。
やはり、re:Inventに行く際は、英語が話せて損はないと感じます。
もちろん英語が話せなくても楽しめますが、話せると楽しめる範囲が格段に広がりますので、良いです。今年のEXPOはかなり楽しめましたが、これで満足せず、もう1年間英語を勉強して、来年は今年以上に楽しもうと思いました!
参考リンク
- 投稿日:2019-12-05T16:01:56+09:00
【Python用】AWS Lambda Layerへのアップロードファイルをササッと作る
前提
Dockerを利用して、Lambdaで使うPythonライブラリのLayerアップロード用zipを作る。
手順はrequestsをzipにする場合のサンプル。手順
ホストOSで、ライブラリを使うバージョンのPythonコンテナにアタッチする。
$ docker pull python:3.8 $ docker images $ docker run -itd ImageID bash $ docker attach ContainerIDコンテナ側では、pipでライブラリを取得し、zipにまとめる。
$ apt-get update & apt-get install zip $ mkdir python & pip install requests -t ./python $ zip -r upload.zip python「Ctrl + p, Ctrl + q」でコンテナから抜け、コンテナからホストにzipをコピーし、コンテナを終了させる。
$ docker cp ContainerID:/root/upload.zip upload.zip $ docker stop ContainerID後はアップロードしておしまい。
- 投稿日:2019-12-05T15:59:41+09:00
今年の自分を振り返る
こんにちは
株式会社Diverseで働いていますpython_spameggsです。
6日目の記事になります。
タイトル通り今年一年の自分がやって来たことを振り返りたいと思います。1月〜3月
社内ツール
AWSへ移転する時に必要ではないものは予め無くしておきたかったので誰も使用していないものを洗い出して停止、削除しました。
弊社の制度で本を購入するのに補助してくるのでその申請が簡単にしやすくするものを作成しました。DNS移管
AWSへ移管しました。
rspec-dnsを使用して漏れがないことを確認しました。AWS CodeBuildとTerraformを使用してAWSサービスを作成
PRを作成すると
terraform plan
が実行されてPRがマージされるとterraform apply
が実行されます。
こちらを参考にしました。Terraform どこで実行していますか?
すでにAWSコンソールから作成されていたサービスがあったのでterraform import
を使用してコード化しました。mackerelの監視ルールをgithubで管理
監視ルールに追加や変更があったら
mkr monitors pull
してgit push
してgithubで管理しています。
githubにpushされた時にCircleCIが動くようになっておりmkr monitors diff
を実行して監視ルールと差分がないかチェックしています。
私以外やっている人がいないので自動化したいなと思っているけど手が回ってないです。dokkuを使用して開発環境を作成
環境を作成したいブランチを選択してボタン一つで作成、更新、削除ができるようになっています。
4月〜6月
AWS Direct Connectの準備
VPCなどの設定やDirect ConnectのVirtual Interfaceなどを開通するのに必要な情報をエクセルにまとめました。
資料をまとめるのは普段の作業と違ってなかなかハードでした。MySQLのバックアップ専用サーバのマシン交換
レプリケーション遅延でバックアップがきちんと出来てなかったのでスケールアップしました。
バックアップ方法や監視方法が古いものを使っていたので見直しました。gRPCのためにプロキシサーバを作成
nginxを使用してプロキシサーバを作成したが最近envoyの方が良かったのではないかと思い始めてます。
RPMをdocker-composeで作成できるように対応
DockerでRPMの作成はしていたけど古くなりドキュメントもなかったのでdocker-composeを使うものを作成しました。
AWS S3にRPM Repositoryとして使用できるように作成
AWSへ移転して使いたいRPMが出てきそうだったので準備しました。
sts:AssumeRole
を使って別のAWSアカウントからも使用できるようにして特定のユーザ or IPアドレスのみアクセスできるようにバケットポリシーを作成しました。
そのままだとs3からyumが出来ないのでAWS EC2インスタンスにyumできるようにプラグインを設定しました。
この時に参考していたものがbotoを使っていて古いのでboto3を使用するに変更しました。7月〜9月
AWS Direct Connectの開通確認
疎通確認して速度計測をa、c、dそれぞれ確認しました。
AMI作成
packerとansibleを使用して作成しました。
MySQLのバージョンアップ
バージョンを5.5から5.6にアップグレードしました。
MHA for MySQLを使用してフェイルオーバーさせて切り替えました。
5.7まであげてもいいじゃないかと話に上がっていたが時間が足りなかったので次の機会に。pt-duplicate-key-checkerを使用して重複したインデックスをチェック
興味があって時間もかからなかったのでさくっと出しただけで使えていません。
SSL/TLS証明書の更新
オンプレミスで使用しているものを更新しました。
10月〜12月
MySQL(オンプレミス)からAWS RDS(MySQL)にレプリケーション
time_zone設定の違うMySQLのレプリケーションについて
上記に気が付かずTIMESTAMP型のカラムがずれてしまったのこちらを参考にtimezoneを変更してmysqldumpを行いリストアして対応しました。
レプリケーション用のユーザパスワードが保存されていたものと違っていたので探索してmysql_historyから作成したログが残っていたので保護してmysql_historyからは削除しました。terraformをv0.11からv0.12にバージョンを変更
AWS CodeBuildでWARNがいっぱい出てしまい実行結果のログが読みにくくなったため変更しました。
一部サーバをAWSへ移転
gRPCサーバとgRPCのプロキシサーバを移転しました。
予めRoute53に移転していてルーティングポリシーを重みを少しずつ変更して徐々に行った。
現在はメールサーバとRDSの移転を行っています。まとめ
やったことをなるべくdocbaseに書いてきましたが詳細が抜けていることが多くて思い出すのが大変でした。
振り返ってみると1年通してこれだけAWSへ移転するための作業をやったことはないのでいい経験なりました。
来年も同じことを続けるけど。明日の7日目は誰かが埋めてくれるはず!
- 投稿日:2019-12-05T14:43:21+09:00
UNHEALTHYのWorkspaces再起動するLambdaをpythonに書き換えました
今年もハンズラボアドベントカレンダーやっていくぞい
概要
弊社ではAWSの仮想デスクトップを提供するサービスWorkspacesを利用しているんですが、
たまにUnhealthyで落ちてたりします\ 何もしてないのに壊れた /
大抵は一時的に負荷がかかって落ちたとかで再起動すればどうにかなることがほとんどです
というわけでひとまず再起動させるLambdaがあるんですが
Node.jsで書かれているのでたびたび発生するバージョンアップ対応・・・シンプルなコードだし、どうせならpythonに書き換えてみようかなと思ってやってみました
処理の流れ
- CloudWatch
- UnhealthyのWorkspacesがあったらアラートでSNS通知
- SNS
- メール送信
- Lambda発火
- Lambda
- Workspaces情報をまるごと取ってきて順にチェック
- Unhealthyだったら再起動
なんで順にチェックしてるんだろ、不調のあるWorkspaceIdをダイレクトに指定して再起動しないのかなと思ったんですけど
アラート情報から取ってくることになるのでWorkspaceごとにCloudWatchでアラートを設定したくなかったのかなと解釈して変更しませんでした書き換えた結果
環境・設定
- ランタイム Python 3.7
- タイムアウト 15秒
ソースコード
import boto3 sns = boto3.client('sns') workspaces = boto3.client('workspaces') def lambda_handler(event, context): workspaces_client_list = workspaces.describe_workspaces() for workspaces_info in workspaces_client_list['Workspaces']: workspace_id = workspaces_info['WorkspaceId'] workspace_state = workspaces_info['State'] if workspace_state == 'UNHEALTHY': reboot_workspaces(workspace_id) def reboot_workspaces(workspace_id): workspaces.reboot_workspaces( RebootWorkspaceRequests = [ { 'WorkspaceId': workspace_id }, ] )終わり
どの言語を選択してもバージョンアップ対応からは逃れられませんが、
最近弊社内でPythonの流れが来ていることもあり、書いてみる機会にできてよかったです明日は @fasahina さんです!
- 投稿日:2019-12-05T14:14:48+09:00
DjangoのテンプレートをAWS boto3を組み込んで翻訳
各国の言語に対応したWebアプリが必要だったのでAWSのboto3を使ってタグを生成
メモとして活用します。
完成したら細かいところを記述したいと思います。
環境
djang == 2.1
boto3 == 1.0.0?
python == 3.6 ?やりたいこと
クライアントが言語選択したらその言語で翻訳して表示中身
boto3を組み込んだタグを作って
HTML内でタグに囲んだ部分を翻訳する。
⇓イメージ
{% translate %}ここ翻訳{% endtranslate %}
すでにDjangoには{% trans %}という翻訳用のタグがあるようだけど
自分でpoファイルやら何やらよりも
AWSの翻訳API使った方が一程度無料でクオリティ高いかな~と思ったので
まずはやってみる。事前準備
・AWS登録
・pip install boto3テンプレートタグについて
djangoはサーバサイドの言語だからHTMLを生成するような便利な機能が必要だったのでDTL(django template language)という便利機能が組み込まれました。
DTLには4つ種類があり、
1つは、変数=>{{ first_name }}
2つ目は、タグ=>{% block %}{% endblock %}
3つ目は、フィルター=>{{ value | filter }}
4つ目は、、、4つ目は、忘れた、とりあえず一時的メモなので後ほどタグの生成
タグの作り方は、djangoのドキュメントでテンプレートエンジンの仕組みのところを通読すると
その後スムーズにできます。タグの生成下準備
①アプリは直下に templatetags ファイル生成
②templatetags 内に init.pyを生成 <=これでテンプレートエンジンが探索してくれる
③templatetags 内に translate.py 生成 <=ファイル名は何でもオッケーtranslate.py の中身(まだ途中)
from django import template import boto3 register = template.Library() REGION = 'ap-northeast-1' SRC_LANG = 'ja' TRG_LANG = 'en' @register.filter(name="translate", is_safe=True, needs_autoescape=True) def translate(value, autoescape=True): translate = boto3.client(service_name='translate', region_name=REGION,use_ssl=True) result = translate.translate_text(Text=value, SourceLanguageCode=SRC_LANG, TargetLanguageCode=TRG_LANG) return result.get('TranslatedText')とりあえずこれはフィルターを生成してみました
HTMLファイルで
{{ '箱' |translate }}
とすると
Box
とブラウザで表示されます。
この'箱'部分に本文をいれれば翻訳されて返ってきますが
今回は{% translate %}ここ翻訳{% endtranslate %}が理想なので
お次はこのタグをつくります。フィルターは簡単そうだったので作ってみました。
これからタグつくるのでメモはここまででまた更新します。
- 投稿日:2019-12-05T14:04:33+09:00
AWSでSQSをLambdaのトリガーをマネージメントコンソールで設定するときに気をつけたい運命の分かれ道
はじめに
AWS LambdaのトリガーにSQSを設定されている方も多いと思います。
SQSのキューにメッセージが送られると、トリガーに設定されたLambdaが呼び出されます。
一度の起動で複数のメッセージを処理することも可能で、このメッセージの数はバッチサイズで指定できます。最大は10です。Lambdaの用途によっては、バッチサイズを1にして運用する場合もあります。
マネージメントコンソールでトリガーを設定する主な導線は、
- SQSからLambdaを設定する
- LambdaからSQSを設定する
の2通りがありますが、これが運命の分かれ道です。
実際にトリガーを設定してみるので、どのような画面遷移なのかご覧ください。状況
sqs_sample
というLambdaとlambda_trigger
というSQSのキューがあります。
lambda_trigger
がメッセージを受信するとlambda_trigger
が発火するようにトリガーを設定します。Lambdaからトリガーを設定する場合
マネージメントコンソールからLambda > 関数 > sqs_sampleを選択します。
この画面で「トリガーを追加」をクリックします。
プルダウンから
lambda_trigger
を選択します。
ここで注目してもらいたいのは、バッチサイズが画面に表示されている点です。
これにより、トリガーのバッチサイズが10であることをユーザーは認識できます。
今回は10で設定しました。トリガーが設定されるまで1分ほど待ちます。
これで設定完了です。
SQSからトリガーを設定する場合
マネージメントコンソールからSQSを選択します。すでにキューが存在する場合は作成済みキューの一覧が表示されます。
lambda_trigger
を選択して「キュー操作」をクリックします。
表示されたメニューから「Lambda関数のトリガーの設定」を選択します。
プルダウンメニューから
sqs_sample
を選択して「保存」をクリックします。
ご覧いただいたように、SQSからトリガーを設定する場合はバッチサイズなんてワードは一切出てきません。
ちなみに、バッチサイズは10で設定されます。まとめ
バッチサイズが1に設定されていることを前提としたLambdaをデプロイするときは気をつけましょう
設定済みのトリガーのバッチサイズを変更する方法はみつからなかったので、一度削除してからLambdaからトリガーを設定しましょう。
ハンズラボ Advent Calendar 2019 17日目は@bakupenさんです?
- 投稿日:2019-12-05T12:53:43+09:00
AWSの避けるべき五つの間違い
開発者およびシステム設計者のMichael Wittigは、Cloudonoutブログで、AWS(Amazon Web Services)を使用する際の最も一般的な誤りに関する興味深い記事を投稿しました。 この記事の要点をご紹介します。
WittigはAWSコンサルタントとして働いており、サイズに関係なく多くのシステムを見てきたそうです。ほとんどの場合、これらは標準のウェブアプリケーションです。 彼は、避けるべき5つの最も一般的な間違いのリストを紹介しました。
典型的なWebアプリケーション
標準のWebアプリケーションは次のものが含まれています。
- ロードバランサー
- スケーラブルなサーバーサイド(ウェブバックエンド)
- ストレージ
図では、次のようになります。
これは一般的なパターンです。 他の方法を使用してアプリケーションを設計する正当な理由がなければなりません。
間違い1.インフラストラクチャを手動で管理すること
コンソールを使用してAWSのリソースが作られているということは、インフラストラクチャが手動で管理されているということです。この制御オプションの主な問題は、リソースの設定は再現できず、どこにも登録されていないことです。その結果、多くのエラーが発生する可能性があります。幸いなことに、この問題を無料で解決できるAWS CloudFormationがあります。
リソース(EC2インスタンス、セキュリティグループ、サブネット)を手動で作成する代わりに、テンプレートでリソースを記述したり、独自に作成したり、既製のものを使用したりできます。 CloudFormationは、それらを現在のスタックに統合する方法を処理します。図の通り、このサービスが正しい順番にリソースを作成してくれます。
間違い2. Auto Scalingグループを使用しないこと
一部の人は、特別な機能に頼らずに自分でリソースを拡張できると信じています。もちろん、それは大間違いです。各EC2インスタンスはAuto Scaling Groupで実行される必要があります。独自のインスタンスであっても。 Auto Scaling Groupは、必要な数のインスタンスの起動を制御します。実際、仮想マシンの論理グループのように動作します。この機能は無料で使用できます。
標準のWebアプリケーションでは、サーバーはAuto Scaling Group内の仮想マシンの中に起動されます。負荷に基づいてコンピューティングリソースの量を増加または減少させることができます。アドミニストレーターは、自動スケーリングを開始する条件を指定できます。これは例えば、論理グループのCPUパフォーマンスのしきい値または負荷分散のためのリクエストの数です。
間違い3. Amazon Cloud Watchの分析を怠ること
AWSサービスに関する必然的なデータは、Cloud Watchから取得できます。 仮想マシンは、プロセッサの負荷、ネットワーク、ディスク操作について通知します。 ストレージは、メモリ使用量とI/O操作の数に関する情報を提供します。 これらの統計を正しく使用するのはあなたです。 一日のCPU使用率のメトリックスを見てみましょう。
ピークをご覧ください。Wittigは、この飛躍が毎日同じ時間に発生していると確信していました。
タスクスケジューラが悪さしているように見えます。 やっぱりそうですね。 このマシンからWebサーバーが起動されたということはスケジューラにより毎日待機時間が増加していたということです。 別の仮想マシンでスケジューラを起動すると、問題は解決するはずです。情報はすべてCloudWatchにありますが、あなたはそれを見る必要があります!
メトリックを分析した後のステップは、メトリックのアラームを定義することです。 その逆ではダメです!
間違い4.Trusted Advisorを無視すること
Trusted Advisorツールは、AWS環境がサービスを操作するベストプラクティスに準拠しているかどうかを確認します。 チェックされるのは:
- コストの最適化
- パフォーマンス
- 安全性
- 耐障害性。
Trusted Advisor管理コンソールが次のようになっている場合:
Wittig氏によると、環境内のプロセスの最適化を開始すべきです。まず、セキュリティに十分注意をした方がいいです。 毎週のフィードバック機能を使うと、Trusted Advisorは現在および解決済みのすべての問題について報告してくれます。 無料版と有料版があります。 有料版により検証の機能がさらに広がります。
間違い5.仮想マシンの効率を管理しないこと
EC2インスタンスが十分に活用していないことに気付いた場合、インスタンスのサイズ(マシンの数またはc3.xlargeからc3.largeへ)を縮小しないようにする理由はありません。手動で管理されているインフラストラクチャなら話は別ですが。
十分に活用していないというのをどうすればわかると? CloudWatchのメトリックスを確認してください! とても簡単です。
Auto Scalingグループを使用している場合は、スケーリングする前にオートスケーリングルールとCloudWatchメトリックスを確認した方が良いです。おまけ
今度インフラストラクチャを確認するのはあなたの番です。
- 投稿日:2019-12-05T10:37:04+09:00
パイソン(python) 吸引力の変わらない、ただひとつの自動化
はじめに
今日は自分が業務上で定期(単純)作業を自動化するツールを パイソン(python) で作った話をします。
パイソン(python)は掃除機ではありません。掃除機をお探しの人はWEBで「ダイソン(Dyson)」と検索してください。自動化にする背景
まず、自動化にする理由は
- 手動で実行すると面倒くさいから
- 自動のほうが人的ミスも起こりにくいから などなど、
実際に私がやっている業務は、
- JIRAで作業依頼を受ける
- 作業内容を確認して、作業を実施する
- 作業完了の連絡をJIRAコメントで知らせる。
- 作業内容によっては、機密情報などをメールで通知する
作業内容には複雑で難しいものもあれば、一瞬で終わる簡単な作業もある。簡単といっても数が多いとけっこう工数がかかってしまうし、メールで通知するパターンだとメール作成が地味に面倒くさい。
なので、これらの作業は 自動化にして結果だけ通知してもらおう!
というのが背景です。
自動化にする内容
超簡単な作業の中に「AWSのIAMユーザー作成」があります。そして「IAMユーザーアクセスキー発行」があります。今日はこれらの作業依頼がJIRAに投稿されて、実行、結果通知までの自動化ツールを紹介します。
使用するのは
python3 (version: 3.7.3)
自動化ツール作成
まずは自動化に必要はパーツ(function)を作成する
- Pythonで「JIRA操作」
- Pythonで「LDAP操作」
- Pythonで「AWS操作」
- Pythonで「SSH操作」※ 踏み台経由パターン
- Pythonで「メール送信」
- Pythonで「HTTP送信」
自動化の処理はPythonのインタフェース実装で作成します。
各パーツ(function)の紹介
各パーツ(function)には必要なライブラリが存在するので、動かす時はインストールしておくこと。以下は
jira
ライブラリのインストール例$ pip install jiraPython de JIRA
- 必要ライブラリ
- jira
- 公式ドキュメント
JIRAオブジェクトの作成
各JIRAの操作はJIRAオブジェクトから実行するので、最初にJIRAオブジェクトを作成しておく。
jira.pyfrom jira import JIRA jira_url="https://jira.xxxxxxxx.jp" jira_user="jirauser" jira_pass="jirapass" jira = JIRA(jira_url, auth=(jira_user, jira_pass))JIRAチケットリスト検索
SQLのWHERE句のようなクエリ(文字列)を作成して検索を行う。
- 使用API
- 実装例
jira.pyissues = jira.search_issues( 'project = {project} AND issuetype in {issuetype} AND status in {status}'.format( project = 'PROJECT_NAME', issuetype = '(issuetype01, issuetype02)', status = '(status_todo, status_doing)' ) ) # print(issues) 結果 # [<JIRA Issue: key='PROJECT_NAME-0001', id='000001'>, <JIRA Issue: key='PROJECT_NAME-0002', id='000002'>]検索結果はkeyとid情報のリスト型で返ってくる。
JIRAチケット情報検索
JIRAチケットリスト検索のkey情報からチケットの詳細情報を取得する。
- 使用API
- 実装例
jira.py# JIRAチケットリスト検索結果をfor文で回しながら、issue(issue_key)でチケットの詳細情報を取得 for issue_key in issues: issue = jira.issue(issue_key)JIRAチケットの項目取得
jira.pyissue = jira.issue(issue_key) # 依頼者(JIRAチケットを投稿した人) issue.raw['fields']['reporter']['name'] # チケットの題名 issue.raw['fileds']['summary']] # チケットタイプ(検索条件にあったissuetype) issue.raw['fields']['issuetype']['id'] # JIRAチケットの入力項目はカスタム可能で、独自の項目を追加することができる。 # カスタム項目の値を取得する場合は、Python de LDAP
ユーザー検索
attiribute情報取得
Python de AWS(SDK for Python)
- 必要ライブラリ
- boto3
import boto3 from boto3.session import Session session = Session( aws_access_key_id='XXXXXXXXXXXXXXXXXXXXXX', aws_secret_access_key='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', region_name = 'ap-northeast-1' ) iam = session.client('iam') def get_user(username): try: return iam.get_user(UserName=username) except iam.exceptions.NoSuchEntityException as e: print("no user(%s)" % username) def create_user(username): return iam.create_user(UserName=username) def create_login_profile(username, password, password_reset_flag): return iam.create_login_profile( UserName=username, Password=password, PasswordResetRequired=password_reset_flag ) def create_accesskey(username): return iam.create_access_key(UserName=username) def add_user_to_group(username, groupname): return iam.add_user_to_group(GroupName=groupname, UserName=username) def get_random_password(password_length, region): secretsmanager = session.client('secretsmanager', region_name=region) return secretsmanager.get_random_password(PasswordLength=password_length)['RandomPassword']認証情報設定
ユーザー作成
パスワード生成
ログイン情報作成
アクセスキー情報発行
Python de SSHTunnel
踏み台経由でコマンド実行
踏み台経由でfunction実行
Python de Sendmail
- 必要ライブラリ
- smtplib
メール送信
import smtplib from_address = "from_address@test.com" #送信元アドレス to_address = "to_address@test.com" #送信先アドレス body="テストメールです" message = MIMEMultipart() message["Subject"] = "メールの表題" message["From"] = from_address message["To"] = to_address message["Date"] = formatdate() message.attach(MIMEText(body)) try: smtp = smtplib.SMTP('mail_server', 25, timeout=10) smtp.sendmail(from_address, to_address, email_message.as_string()) smtp.quit() except Exception as e: print(e)メール送信(添付ファイル付)
import smtplib from_address = "from_address@test.com" #送信元アドレス to_address = "to_address@test.com" #送信先アドレス body="テストメールです" file_path="/tmp/temp_file.csv" file_name="temp_file.csv" message = MIMEMultipart() message["Subject"] = "メールの表題" message["From"] = from_address message["To"] = to_address message["Date"] = formatdate() message.attach(MIMEText(body)) attachment = MIMEBase('text', 'comma-separated-values') file = open(file_path) attachment.set_payload(file.read()) file.close() encoders.encode_base64(attachment) message.attach(attachment) attachment.add_header("Content-Disposition","attachment", filename=file_name) try: smtp = smtplib.SMTP('mail_server', 25, timeout=10) smtp.sendmail(from_address, to_address, email_message.as_string()) smtp.quit() except Exception as e: print(e)Pyrhon de Slack
- 必要ライブラリ
- slackclient
使用例
ログイン可能なユーザーを保持していて、API-Tokenが発行されていることが前提。
import slack token = "xoxp-xxxxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxxx" channel = "#test" message = ":information_source: これはテストです" client = slack.WebClient(token=token) # Slackにメッセージ投稿 response = client.chat_postMessage( channel=channel, test=message )Pythonのインタフェース実装
Baseクラス作成
__init__.py
を実装インタフェースクラス作成
実行例
さいごに
- 投稿日:2019-12-05T10:24:45+09:00
GPUを用いる機械学習推論処理をAWS BatchのSpotインスタンスで実現する
概要
AWSにて機械学習を行うにはまずSageMakerがあります。SageMakerはモデルの学習・管理・デプロイを取りまとめて行えるフルマネージドサービスです。
概ね便利なサービスなのですが、機械学習処理で用いるGPUインスタンスは高額なのでスポットインスタンスでコスト軽減を行いたくなります。
モデルの学習にはスポットインスタンスが使えるのですが、バッチ変換ジョブにはスポットインスタンスが使えません。(2019/12現在)
https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/model-managed-spot-training.html
https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/batch-transform.htmlバッチとして非リアルタイムで推論するGPU処理をAWS Batchを使って実装したのでメモとして残します。
ポイント
AWS Batch
ECSイメージの準備
AWS BatchからはECSが呼ばれるため、EC2インスタンスには制御用のECSエージェントがインストールされている必要があります。
またGPUを利用するためにnvidiaドライバも必要です。
双方を満たすためにec2のGPU用AMIを利用します
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-optimized_AMI.html
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/retrieve-ecs-optimized_AMI.htmlECSAMI: Description: AMI ID Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ecs/optimized-ami/amazon-linux-2/gpu/recommended/image_idランタイムの指定
dockerコンテナ内からnvidiaのデバイスを使いたいのであれば、dockerの起動オプションにruntime=nvidiaを指定する必要があります。
ECSで実現する場合、自作のイメージを作成して設定ファイルを書き換える、ec2のUserDataから設定ファイルを書き換えるなどの方法があります。
AWS Batchで簡易に実現する方法としてJobDefinition
の制約にGPUを指定すると自動的にruntimeがnvidiaになります
しかしながら、この方法ではリソース制約としてGPU1つを要求するため、GPUが1つだけのマシンであれば1インスタンスで1ジョブしか動かせません。スクリプト内でリソースを使い切れるように並列処理を行うことになると思います。ResourceRequirements: - Type: GPU Value: 1スポットインスタンス停止対策
スポットインスタンスを利用するのでインスタンスが強制停止される可能性があります。
強制停止される通知を受け取った後に途中結果をどこかに保存して、途中から再開できるような推論処理にするのが最も効率が良いです。
ただ今回は推論処理に手を入れたくなかったため、AWSバッチの配列ジョブにて処理を細かいジョブに分けた上で正常終了しなかったジョブはAWS Batchのリトライ機構でリトライさせます。配列ジョブ
AWS Batch側でジョブに数字を割り振り、スクリプトからはその数字を環境変数から読み込み、ジョブごとに別の処理をさせることができます。
細かくジョブを分けることでジョブ単位でリトライができるのと、ジョブ単位で並列処理ができます。
ジョブの粒度を小さくしすぎるとコンテナのイメージダウンロードやECS処理などのオーバーヘッドが大きくなるため、いい塩梅で設定してください。
https://docs.aws.amazon.com/ja_jp/batch/latest/userguide/array_jobs.htmlpythonスクリプトから
os.getenv('AWS_BATCH_JOB_ARRAY_INDEX')
のような形で読めますリトライ
念の為上限の10回リトライさせています
RetryStrategy: Attempts: 10 # 1-10 allowedaws batchの呼び出し
airflowからこんな感じのスクリプトでboto3を呼び出しています
arrayProperties
として配列ジョブのサイズを指定しています。def _submit_job(*, target_ymd, array_size): batch = session(env).client('batch', region_name=region) response = batch.submit_job( jobName="", jobQueue="", jobDefinition="", arrayProperties={"size": array_size}, containerOverrides={ "command": [ "python", "script.py", "--target_date", target_ymd, ], "environment": [ { "name": "STAGE", "value": "test" } ] }, ) job_id = response['jobId'] print(f'job has been sent. job id: {job_id}') return job_id def _wait_job_finish(job_id): batch = session(env).client('batch', region_name=region) while True: response = batch.describe_jobs(jobs=[job_id]) status = response['jobs'][0]['status'] if status in ['SUCCEEDED']: return True elif status in ['FAILED']: return False else: sleep(10)呼び出しのlambdaをserverlessから構築してる人もいるようです
https://blog.ikedaosushi.com/entry/2019/04/27/222957region
作業用インスタンスはネットワークの遅延が小さい東京リージョンを使いがちだと思いますが、バッチジョブにおいては多少ネットワークが遅くても問題ないので最安リージョンで実行できます。
最安リージョンとしては北カリフォルニア以外のアメリカ3リージョン(バージニア、オハイオ、オレゴン)を抑えておけば良さそうです。
リージョン差は結構大きくて、例えばp2.xlargeにおいては0.90$/h
(us-east-1)と1.542$/h
(ap-northeast-1)と1.6倍ほどの開きがあります。CloudFormation
CloudFormationにて環境構築をコード化します。下記を参考にしました
AWS CloudFormation のベストプラクティス
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/best-practices.html【AWS】CloudFormationの実践的活用~個人的ベストプラクティス
https://qiita.com/tmiki/items/022df525defa82d1785bCloudFormation Templateの分け方
今回はVPCから環境を構築しました。VPCは別アプリケーションでも使い回す想定で構築するので、AWS Batchのバッチジョブ層とはライフサイクルが異なります。また、ネットワーク周りの環境依存情報(VPC Peeringなど)はわかりやすいよう別にしておきたかったので中間層として切り出しました。合計3つのテンプレートを作ります。
Cloud Formation Template
VPC
2AZ, 3AZ, 4AZのテンプレートは下記サイトにあります。(一番上3つです)
https://templates.cloudonaut.io/en/stable/vpc/
NCALでは制限をかけず、SGでセキュリティを担保する思想のテンプレートなので、NCALを変えたければ改変する必要がありますVPCに関しては繰り返し処理が多くなりがちですのでいろいろいじるのであればcdkで記述する手もありそうです。
https://github.com/aws/aws-cdk中間層
別リージョンの踏み台サーバからアクセスできるようにする想定で、環境依存の設定を切り出します。
セキュリティグループの設定とVPCピアリングの設定があります。
(ちなみにsshできるようにするのはデバッグ用で、AWS Batchで使う分にはIAMの設定だけで大丈夫です。)
別途踏み台サーバ側のルートテーブルとSG, NACLの設定も必要です# this template is supposed to use after make vpc layer --- AWSTemplateFormatVersion: 2010-09-09 Description: Middle Cloud Formation layer depending on environment specific value Parameters: VpcStackName: Type: String BastionServerPrivateIP: Type: String BastionServerVpcId: Type: String BastionServerRegion: Type: String Resources: ##### # Security Group ##### ComputeSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: Fn::ImportValue: !Sub ${VpcStackName}-VPC GroupDescription: Enable SSH access via port 22 from bastion server SecurityGroupIngress: - CidrIp: !Sub ${BastionServerPrivateIP}/32 IpProtocol: tcp FromPort: 22 ToPort: 22 Tags: - Key: Name Value: !Sub ${AWS::StackName}-compute ##### # VPC Peering ##### VPCPeering: Type: AWS::EC2::VPCPeeringConnection Properties: PeerRegion: !Ref BastionServerRegion PeerVpcId: !Ref BastionServerVpcId VpcId: Fn::ImportValue: !Sub ${VpcStackName}-VPC Tags: - Key: Name Value: bastion peering - Key: Desc Value: vpc peering for access via bastion server RouteTablePublicInternetRouteA: Type: 'AWS::EC2::Route' Properties: RouteTableId: Fn::ImportValue: !Sub ${VpcStackName}-RouteTableAPublic DestinationCidrBlock: !Sub ${BastionServerPrivateIP}/32 VpcPeeringConnectionId: !Ref VPCPeering RouteTablePublicInternetRouteB: Type: 'AWS::EC2::Route' Properties: RouteTableId: Fn::ImportValue: !Sub ${VpcStackName}-RouteTableBPublic DestinationCidrBlock: !Sub ${BastionServerPrivateIP}/32 VpcPeeringConnectionId: !Ref VPCPeering RouteTablePublicInternetRouteC: Type: 'AWS::EC2::Route' Properties: RouteTableId: Fn::ImportValue: !Sub ${VpcStackName}-RouteTableCPublic DestinationCidrBlock: !Sub ${BastionServerPrivateIP}/32 VpcPeeringConnectionId: !Ref VPCPeering RouteTablePublicInternetRouteD: Type: 'AWS::EC2::Route' Properties: RouteTableId: Fn::ImportValue: !Sub ${VpcStackName}-RouteTableDPublic DestinationCidrBlock: !Sub ${BastionServerPrivateIP}/32 VpcPeeringConnectionId: !Ref VPCPeering Outputs: ComputeSecurityGroup: Description: security group for general compute instance Value: !Ref ComputeSecurityGroup Export: Name: !Sub ${AWS::StackName}-ComputeSecurityGroupバッチジョブ層
主にIAMの設定とAWS Batchの設定を書いています
IAMには
* AWS Batchの自動制御を許可するroleAWSBatchServiceRole
* ec2インスタンスにecsの自動制御を許可するroleecsInstanceRole
* spot fleetの自動制御を許可するroleAmazonEC2SpotFleetTaggingRole
* ecs内のコンテナにawsサービスの利用を許可するroleJobRole
などを設定する必要があります。AWS Batchの環境構築には
ComputeEnvironment, JobDefinition, JobQueue
の3層が必要なので設定します。
ECSの利用にECRのリポジトリが必要なのでついでに構築しています。開発環境と本番環境でアカウントが異なる場合でも、それぞれのアカウントにリポジトリを作成する想定です。AWSTemplateFormatVersion: 2010-09-09 Description: Build AWS Batch environment Parameters: VpcStackName: Type: String MiddleStackName: Type: String Ec2KeyPair: Type: String EcrPushArn: Type: String EcrRepositoryName: Type: String ECSAMI: Description: AMI ID Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ecs/optimized-ami/amazon-linux-2/gpu/recommended/image_id EnvType: Description: Environment type. Default: test Type: String AllowedValues: - prod - test ConstraintDescription: must specify prod or test. Conditions: IsProd: !Equals [ !Ref EnvType, prod ] Resources: ##### # IAM ##### AWSBatchServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - batch.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole Path: "/service-role/" ecsInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role ecsInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Roles: - !Ref ecsInstanceRole AmazonEC2SpotFleetTaggingRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - spotfleet.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetTaggingRole Path: "/service-role/" JobRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ecs-tasks.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: # set policy your program need - arn:aws:iam::aws:policy/AmazonS3FullAccess - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess ##### # ECR ##### ECR: Type: AWS::ECR::Repository Properties: RepositoryName: !Ref EcrRepositoryName RepositoryPolicyText: Version: "2012-10-17" Statement: - Sid: AllowPushPull Effect: Allow Principal: AWS: - !Ref EcrPushArn Action: - "ecr:GetDownloadUrlForLayer" - "ecr:BatchGetImage" - "ecr:BatchCheckLayerAvailability" - "ecr:PutImage" - "ecr:InitiateLayerUpload" - "ecr:UploadLayerPart" - "ecr:CompleteLayerUpload" ##### # AWS Batch ##### ComputeEnv: Type: AWS::Batch::ComputeEnvironment Properties: # do not specify ComputeEnvironmentName to allow resource update Type: MANAGED ServiceRole: !GetAtt AWSBatchServiceRole.Arn ComputeResources: Tags: Name: Spot Fleet Managed auto_stop: "false" Type: SPOT MaxvCpus: !If [IsProd, 8, 4] # 本番環境でのインスタンス数*4 MinvCpus: 0 InstanceTypes: - p2.xlarge SecurityGroupIds: - Fn::ImportValue: !Sub ${MiddleStackName}-ComputeSecurityGroup Subnets: Fn::Split: - "," - Fn::ImportValue: !Sub ${VpcStackName}-SubnetsPublic ImageId: !Ref ECSAMI Ec2KeyPair: !Ref Ec2KeyPair InstanceRole: !GetAtt ecsInstanceProfile.Arn SpotIamFleetRole: !GetAtt AmazonEC2SpotFleetTaggingRole.Arn BidPercentage: 51 State: ENABLED JobDef: Type: AWS::Batch::JobDefinition Properties: JobDefinitionName: !Sub ${AWS::StackName}-jobdef Type: container ContainerProperties: Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepositoryName}:latest Vcpus: 4 Memory: 50000 # MiB JobRoleArn: !GetAtt JobRole.Arn Privileged: True ResourceRequirements: - Type: GPU Value: 1 RetryStrategy: Attempts: 10 # 1-10 allowed Timeout: AttemptDurationSeconds: 86400 # 24h JobQueue: Type: AWS::Batch::JobQueue Properties: JobQueueName: !Sub ${AWS::StackName}-jobqueue ComputeEnvironmentOrder: - ComputeEnvironment: !Ref ComputeEnv Order: 0 Priority: 100 State: ENABLEDCloudFormation起動スクリプト
こんな感じのスクリプトから呼び出します
#!/bin/bash if [ $# -lt 2 ]; then echo 'two argument required. environment, input command' exit fi env=${1} if [ ${env} = 'test' ]; then profile=ここらへん入れてください region= vpc_stack_name= middle_stack_name= application_stack_name= bastion_private_ip= bastion_vpc_id= key_pair_name= elif [ ${env} = 'prod' ]; then # 略 else echo 'test or prod is required.' >&2 exit 1 fi function all () { create_key_pair create_vpc create_middle create_batch_env } function create_key_pair () { # Key pair is created to project root. Move it to suitable place. aws ec2 create-key-pair \ --profile ${profile} \ --region ${region} \ --key-name ${key_pair_name} \ --query 'KeyMaterial' \ --output text > ${key_pair_name}.pem } function create_vpc () { aws cloudformation deploy \ --profile ${profile} \ --region ${region} \ --template-file template/networking/vpc-4az.yml \ --stack-name ${vpc_stack_name} \ --capabilities CAPABILITY_IAM \ --parameter-overrides \ ClassB=0−255までの数字でVPCのIPアドレス指定に使われる \ ; } function create_middle () { aws cloudformation deploy \ --profile ${profile} \ --region ${region} \ --template-file template/networking/env_specific.yml \ --stack-name ${middle_stack_name} \ --capabilities CAPABILITY_IAM \ --parameter-overrides \ VpcStackName=${vpc_stack_name} \ BastionServerPrivateIP=${bastion_private_ip} \ BastionServerVpcId=${bastion_vpc_id} \ BastionServerRegion=ap-northeast-1 \ ; } function create_batch_env () { aws cloudformation deploy \ --profile ${profile} \ --region ${region} \ --template-file テンプレート.yml \ --stack-name ${application_stack_name} \ --capabilities CAPABILITY_IAM \ --parameter-overrides \ VpcStackName=${vpc_stack_name} \ MiddleStackName=${middle_stack_name} \ Ec2KeyPair=${key_pair_name} \ EcrRepositoryName=名前 \ EcrPushArn=AWSのARN \ EnvType=${env} \ ; } # execute input command ${2}まとめ
スポットインスタンスの利用により6-7割引ほどの恩恵を受けられています。コストを削減すればその分を更にたくさんのマシンリソースに充てられます。積極的に使っていきましょう。
- 投稿日:2019-12-05T09:25:28+09:00
DB 高負荷時に数秒でサクッと原因を特定する (aws RDS)
概要
なんかよくわからないけど DB の負荷が高くなったどー
という監視アラートが出たとき、あなたはどうしますか?そういうときに何が原因で負荷が高くなっているかサクッと追いたいですね。
CPU の状態や、スワップの状態、コネクション数から調べることもできますが、これだけでは何が原因で重くなったのかはっきりしないことは多いです。対象読者
- aws が何なのか知っている人、運用している人 (aws は食べ物ではない)
- RDS で DB 管理していること
- スロークエリログを cloudwatch もしくは DB に保存できる環境であること
準備
DB 側
数秒でサクッと原因を特定できるようにするためには、事前に準備が必要です
まずは RDS のパラメータグループで以下の設定を変更してください (デフォルトのパラメータグループでは変更できないようです)
- slow_query_log: 1
- long_query_time: 0.1 ~ (単位 秒)
- log_output: FILE (TABLE でもよいですが、その場合の方法は今回は割愛)
次に、RDS 自体の設定で [Modify] を設定し、[Slow query log] にチェックをいれます
そうすることで RDS の Configuration に Slow query という cloudwatch へのリンクができます。
リンク先を開くと、キャプチャの ① が log-group-name で ② が log-stream-name です。メモしておきましょう。
集計側 (local など)
セットアップ
mac の場合 homebrew 導入済みの前提で記載しますと、以下のように percona-toolkit を導入します
$ brew install percona-toolkitjq コマンドも導入しておきます
$ brew install jqmac の場合は、以下のように aws-cli を導入します
$ brew install awscli $ aws configure // aws の設定を行うスクリプト配置
ということで、
sloq-query-summary.sh
というスクリプトで中身を以下のようにします
pt-query-digest のオプションを指定すれば、explain も出したり、余計なクエリをフィルタしたりできますが、ここでは割愛します
LOG_GROUP
には先程メモしたlog-group-name
を、LOG_STREAM
には先程メモしたlog-stream-name
をいれます#!/bin/bash -u LOG_GROUP=hogehoge LOG_STREAM=fugafuga SCRIPT_NAME=`basename $0` if [ $# -ne 2 ]; then echo "指定された引数は$#個です。" 1>&2 echo "実行するには2個の引数が必要です。" 1>&2 echo "Usage: $SCRIPT_NAME "\""2019-12-01 12:00:00"\"" "\""2019-12-01 13:00:00"\" 1>&2 exit 1 fi start="$1" end="$2" echo start datetime iso-8601.. echo $(TZ=JST-9 date --iso-8601="seconds" --date "$start"); echo end datetime iso-8601.. echo $(TZ=JST-9 date --iso-8601="seconds" --date "$end"); starttime=$(expr `TZ=JST-9 date --date "$start" +%s` \* 1000) endtime=$(expr `TZ=JST-9 date --date "$end" +%s` \* 1000) aws logs get-log-events \ --log-group-name ${LOG_GROUP} \ --log-stream-name ${LOG_STREAM} \ --start-time ${starttime} \ --end-time ${endtime} \ --query 'events[].message' \ |jq -r '.[]' \ | pt-query-digest --limit 10000最後に実行権限をつけます
$ chmod +x ./sloq-query-summary.sh重くなったなというときに、以下のようにスクリプトを実行すれば、簡単にスロークエリログのサマリが表示されるようになります。
./sloq-query-summary.sh "2019-12-01 12:00:00" "2019-12-01 13:00:00"※キャプチャはサマリの部分だけですが、実際のスロークエリログもでてきます
- 投稿日:2019-12-05T09:03:52+09:00
今日からAmazon Rekognition Custom Labelsで転移学習できるうようになった!
これは、機械学習ツールを掘り下げる by 日経 xTECH ビジネスAI③ Advent Calendar 2019の記事です。12月後半に記事を書こうかなと思ったのですが、12月前半に記事を書いている方が少なそうでしたので、前倒しで書いてみました。本記事では、私のオススメ機械学習ツールとして、先週くらいにアマゾン ウェブ サービス (AWS) から発表があったAmazon Rekognition Custom Labelsを取り上げたいと思います。
参考:日経xTech『画像認識AIの独自開発が容易に、AWSが新サービス「Rekognition Custom Labels」提供へ』
発表があったのは11月25日、使えるようになったのは12月3日(日本時間では12月4日)でしたので、できたてほやほやサービスです。と、いいつつ、本記事は「今日から~」というタイトルにしたのですが、試しているうちに翌日(日本時間12月5日)になってしまいました。まだ日本リージョンでは使えないためバージニア北部リージョンで動かしてみました。そもそもこの機能は日本語設定だと出てこないので、AWSを英語設定にする必要もあります。
サーバレスで画像認識機能を使えるRekogniton!
サーバレスで画像認識を作るのがとても楽なので、従来からRekognitionは気に入っていました。
例えば、LINEで画像メッセージを送ったら、その画像に映っているものが何であるかを判定するようなアプリを作ってみました。次の画像のように、ピザの絵を送ったら、Food:94%やPizza85%といった結果をLINEメッセージで返します。
このようなアプリは、Rekognition含むAWSサーバレスを駆使したら、意外と簡単に作ることができます。無料版のLINE@ Messaging APIを用意してWebhookURLをAWS API Gatewayに設定し、そこで受け取ったRESTリクエストをLambdaで処理するだけです。画像データを一旦S3において、Rekognitionで解析したら、その結果をLambdaからLINE Messaging APIに送るという仕組みです。
でも、従来のRekognitionに不満もあった
従来のRekognitionは、AWSが自前で用意した画像をもとに作られた機械学習モデルです。ですから、AWSが使った学習データの教師ラベルに含まれていないものは、認識できません。海外のベンダーが作っている画像認識モデルですから、日本人感覚からすると、意外だなと思うものが認識できなかったりします。例えば・・・
日本人感覚では、絶対に間違えないであろうクワガタの画像をAWS Rekognitionで解析しても、まずクワガタ(Stag Beetle)とは認識してくれません。たいてい、フンコロガシ(Dung Beetle)と認識されます。USでは、クワガタよりもフンコロガシの方が一般的なのかな・・・(そういえば、ファーブル昆虫記でもフンコロガシが取り上げられていたなぁ)・・・これが、Rekognitionに対する長年の不満でした。こんなに特徴的なカタチをしている分かりやすい昆虫なんですけどね。
Rekognitionでクワガタを認識させたい!
まずは、トレーニング(学習)!
Amazon Rekognition Custom Labelsは、ユーザーが自前の教師データを少量用意すれば、独自の画像認識AIを開発できるというAWSの新しいサービスです。つまり、転移学習ができるようになったわけですね。これを使って、クワガタの画像データを何枚か投入すれば、Amazon Rekognitionでクワガタを判別できるようになるのでは・・・と考えました。
ついに昨日からサービスが使えるようになったので、すぐに試してみました。まず、学習用の画像データはgoogle検索でクワガタ10枚とカブトムシ10枚くらいを探して準備します。AWS Rekognitionをバージニア北部リージョンで、かつ英語版で開くと、Custome Labelsという今までなかった新機能があるのがわかります。
Rekognition Custome Labelsの新しいプロジェクトを作ります。
そして、1匹1匹にラベルと境界線を設定してきます。(意外と面倒・・・もう心が折れてきた・・・)
さぁ、学習データが揃ったので、トレーニング(機械学習)してみます!全データ(画像20枚)のうち、20%を検証用データとすることにしました。
ここでしばらく待っていると・・・トレーニング完了です。F値は、0.345・・・かなり低いですね。画像データが少なすぎるのか?クワガタとカブトムシが似すぎていて判別がつかないのか?など、色々と思うところはありましたが、今回はお試しなので、そのまま進めます。
性能検証
もう少しブレイクダウンして性能を見てみると、カブトムシの精度(Precision)は1.00でクワガタの精度(Presicion)は0.01でした。この数値だけ見ると、カブトムシと認識したものは正確にカブトムシであることを判別できている素晴らしい数値(精度1.0)ともいえなくもないですが、再現率が0.50であるところを見ると、ほとんど全ての検証データをカブトムシと判定してしまっている・・・つまり、あまり意味のない判定機になってしまっているということだと思います。
実際にクワガタの画像を解析してみると、次のような感じです。まず、AWS Rekognitionの元からあるモデルで物体認識して領域を抽出してくれます。それぞれの領域に対して、画像分類を実施してくれるみたいです。この画像の中だと、最もクワガタらしいハサミの部分を緑色の領域で「クワガタ」と認識してくれているようです。まぁ、これだけでも今日は満足です。
おわりに
性能(精度と再現率)を上げていくためには、当然のことながら、学習データを工夫していく必要がありそうです。今回、Amazon Rekognition Custom Labelsを触ってみて、自分用の画像判定AIを、物凄く楽に作れることが分かりました。Rekognitionですから、もちろんAWS Lambdaから簡単に呼び出せますので、サーバレスでの活用も簡単です。つまり、画像判定AIを作る際に、『学習データの準備だけ』に集中できるようになったということだと思います。凄いな。これから、性能を上げていくための学習データの在り方などを、工夫していきたいなと考えています。
- 投稿日:2019-12-05T08:08:06+09:00
AWS re:Invent 2019でワークショップに参加してきた
本記事は、サムザップ Advent Calendar 2019 #1 の12/5の記事です。
はじめに
2019/12/1〜12/6の間、ラスベガスで開催されていたAWS re:Invent2019に参加してきました。
この記事では参加したセッションの中から、クラウド環境でのオペレーションの自動化に関するワークショップ「Intelligently automating cloud operations」の紹介をします。ワークショップというのは一般的な講義形式のセッションとは異なり、自分で手を動かして学習するセッションのことです。ワークショップについて
- 各テーブル6〜7名、x 20テーブルくらい
ワークショップ全体の時間は2〜3時間
用意された復数の課題を様々なAWSサービスを使って解決していく
- 各課題に取り組む前に10分程度の講義がある
- 講義後に共有されるドキュメントに従って作業を進めていく
ワークショップで使うAWSアカウントは運営側で用意してくれた
作業用のノートPCは各自持ち込み
作業中の不明点は会場にいる運営メンバーか、同じテーブルの人に相談する
お互いの詰まったところを助け合う以外に複数人での作業はなく、ほぼ個人作業
参加するワークショップによっては自分でAWSアカウントを用意する必要があったり、
個人ではなくチームで課題に取り組むなどの違いがあるようです。ワークショップでやったこと
用意された以下3つの課題を進めました。
AWS HEALTH ABUSE EVENTS
管理しているEC2インスタンスがDoS攻撃を受けているシーンを想定。
AWS Personal Health Dashboardに届いたインシデント報告をCloudWatch Eventsで検出し、Lambdaを使ってSNS経由でSMSを送信しつつ、攻撃を受けているEC2インスタンスの停止アクションの自動実行を実装しました。Automatic Region Failover
マルチリージョン(us-east-1,2)で動いているシステム。各リージョンにNLBがあり、Webサーバが復数台ぶらさっている構成で片方のリージョン(us-east-1)のトラフィックパターンに異常が発生したシーンを想定。
クロスリージョンフェイルオーバーのオペレーション自動化はこんな感じの流れでした。
- CloudWatchのCanaryResponseTimeメトリックでAnomaly Detectionを有効にする
- CloudWatchアラームを作成し、事前に作成済のSNSをターゲットに指定する
- lambdaにクロスリージョンフェイルオーバーさせる関数を追記する
- 意図的にトラフィック異常を発生させるためのSSMドキュメントを作成する
- tcコマンドでレイテンシ遅延を発生させる処理が書かれていた
- SSMのRunコマンドでus-east-1のインスタンスにレイテンシ遅延を発生させる
- しばらく経過するとCloudWatchがアラーム状態に変化する
- Lambdaが作動して、トラフィックがus-east-1からus-east-2に切り替わる
このシナリオではNLB + Webサーバのシンプルな構成だったけど、データストアを含む構成だと実環境に近くてもっと面白そうです。あとは地味にtcコマンドが便利なので社内の障害試験で今後使っていきたいです。
AUTOMATED EBS USAGE FORECAST AND LIMIT INCREASES
AWSアカウント内にあるすべてのEBSボリューム数を自動集計し、AmazonForecastでデータセットを作って、今後の使用量を予測する。更にEBSの制限緩和も自動化する。みたいなシナリオで面白そうだったんですが、ここまでの2つの課題で結構時間を使ってしまって最後まで進められませんでした...
最後に
DoS攻撃を受けたときやリージョン障害が起きたときに限らず、何か問題が発生したときに行う作業が決まっているのであれば、オペミス防止や復旧時間を短縮するためにも自動化すべきだと思いました。サムザップでも今後更に自動化を進めていきます。
明日は @leosuke さんの記事です。
- 投稿日:2019-12-05T03:46:19+09:00
Athenaで億単位のレコード数のテーブル同士をJOINしてみる
これは何?
Athenaに思いのほかいろんな関数が実装されていた
のおまけ記事です。前記事ではいろんな関数を使うことでクエリ1発でいろんな集計ができるかもしれないと書いたのですが、そのためにはデータ量の多い複数のテーブルを繋げるような処理が必要になることが考えられます。
普通に手元のMySQLでそういったクエリを投げると重すぎて最悪結果が返ってこなくなることもあるので、Athenaだとちゃんと現実的な時間で結果が返ってくるのか試して見ました。問題設定
- キャラクターをひたすら売却するシミュレーション
- キャラクターは25種類いて、レア度(rariry)は[1,2,3,4,5]の5通り、売却価格は10^(rarity)
- プレイヤーが1000人いて、それぞれがキャラクターを1000人持っている
- 毎日ランダムに100000回プレイヤーを選び、手持ちのレア度[1,2,3,4,5]各一人をランダムに選んで売却し、同じレア度のキャラクターをランダムに選んで補充
これを数千日分シミュレーションし、日毎の「新たに生成されたキャラクター」と「プレイヤーが売却したキャラクター」のログをS3に上げていきます。十分ログが溜まったら合計の売却価格やプレイヤーごとの合計の売却価格を計算します。
データ形式
- キャラクターマスタ(master_character)
25行です。
{"id":1,"rarity":5,"price":100000}
- キャラクター生成ログ(character_log)
一日分は500000行です。
{"id":2000001,"master_character_id":1}
- 売却ログ(sell_log)
一日分は100000行です。
{"player_id":533,"sell_character_ids":[1999466,1669252,1965108,1592234,532614]}ポイント
売却ログ(sell_log)からキャラクターの売却価格を知るためには、キャラクター生成ログ(character_log)を通してmaster_character_idを得て、そこからキャラクターマスタ(master_character)を引かなければならない
レア度[1,2,3,4,5]のキャラクターをそれぞれ売却するので、一人が一回で行う売却価格は必ず111110になる。後の集計で111110の倍数でない数字が出たら何かが間違っている。
生成されてすぐに売却されたキャラクターは日付的に近いところに対応するログがあるが、生成された後でなかなか選ばれなかったデータはその分日付的に離れたところに対応するログがある。
実装
適当に実装します。せっかくなので業務では絶対に書かない闇の1行コードを書いたりしてみましたが地獄でした。
https://github.com/random25umezawa/athena_dataset実装できたらEC2に乗せ、一日分シミュレーションを進めてログをgzip圧縮してS3に上げるパッチを数千回呼び出しました。
S3に上げる段階で
/log_date=YYYY-MM-DD/
というパスを含んで保存しているので、log_dateパーテーションを含むAthenaテーブル作成後にMSCK REPAIR TABL table_name
してパーテーションの設定を済ませます。出来上がったデータ
数時間シミュレーションを回し続けたところ、以下のようなデータが出来上がりました。
- 圧縮後約20GBのログ(解凍すると10倍以上になる)
- それぞれ約30億,約6億行のテーブル(sell_logは約6億行だが、長さ5の配列の展開をするので5倍になる)
- 2019-01-01スタートで2035-06-06までのデータ(約6000日分)
投げるクエリ
事前に説明したとおり、sell_logにcharacter_logとmaster_characterをJOINします。
WITHの中でパーテーションの指定をすることで検索範囲を絞ります。最初は1日分のみで試し、可能そうなら徐々に範囲を拡げてみます。
WITH sell_log2 AS ( SELECT * FROM sell_log WHERE log_date >= '2019-01-01' AND log_date <= '2019-01-01' ), character_log2 AS ( SELECT * FROM character_log WHERE log_date >= '2018-12-31' AND log_date <= '2019-01-01' ) SELECT SUM(price) AS price_sum FROM sell_log2 CROSS JOIN UNNEST ( sell_character_ids ) as _ (sell_character_id) INNER JOIN character_log2 ON sell_character_id = character_log2.id INNER JOIN master_character ON master_character_id = master_character.id結果
期間 日数 合計値 ~ 2019-01-01 1 11111000000 (Run time: 5.92 seconds, Data scanned: 6.93 MB) ~ 2019-01-10 10 111110000000 (Run time: 7.85 seconds, Data scanned: 39.28 MB) ~ 2019-03-31 90 999990000000 (Run time: 8.2 seconds, Data scanned: 330.22 MB) ~ 2021-06-30 912 10133232000000 (Run time: 20.22 seconds, Data scanned: 3.3 GB) ~ 2035-06-06 6001 66677111000000 (Run time: 1 minute 20 seconds, Data scanned: 21.92 GB) 1分程度で終わってしまった...。
蛇足
プレイヤーごとの集計もうまくできてそうか見てみます。
売却の合計値は111110の倍数のはずなので、結果のテーブルにさらにMOD(price_sum, 111110)
を計算させると全て0になるはずです。WITH sell_log2 AS ( SELECT * FROM sell_log WHERE log_date >= '2019-01-01' AND log_date <= '2035-06-06' ), character_log2 AS ( SELECT * FROM character_log WHERE log_date >= '2018-12-31' AND log_date <= '2035-06-06' ), result as ( SELECT player_id, SUM(price) AS price_sum FROM sell_log2 CROSS JOIN UNNEST ( sell_character_ids ) as _ (sell_character_id) INNER JOIN character_log2 ON sell_character_id = character_log2.id INNER JOIN master_character ON master_character_id = master_character.id GROUP BY player_id ) SELECT player_id, price_sum, MOD(price_sum, 111110) FROM result集計できていそうです。
まとめ・感想
数分程度で結果が返ってきたうえに、結果も正しそうに見えます。
実際に別のデータでやる場合は、少ないデータ量で結果の整合性・処理時間などをテストした上で投げましょう。
さいごに
初心者なので何か間違いや注意点などあればご指摘ください。
- 投稿日:2019-12-05T03:27:23+09:00
AWSコストエクスプローラAPIと気軽につきあう(2019)
この記事ははてなエンジニアAdvent Calendar 2019の5日目のエントリーです。
はじめに
AWSのコストエクスプローラのデータは12ヶ月しか保存されないので、なるべく外部で保存したいですよね。幸い、コストエクスプローラのデータにアクセスするためのAPIがちゃんと提供されているので、気軽に利用することができます。特に難しいこともないのですが、これから「よーしAWS費用の集計をシステム化しちゃうぞー」という人のために、かんたんに足場を作るための手順を整理しておくことにします。
API更新時の説明において以下のように言及されている通り、コンソール経由よりも詳細なデータアクセスが可能なので、ぜひ面倒くさがらずコスト管理のパイプライン構築を進めていきましょう。
グループ化 – コストエクスプローラーウェブアプリケーションで提供されるグループ化は 1 レベルですが、API では 2 レベルが提供されます。たとえば、まずサービス別にコストまたは RI 使用率をグループ化してから、リージョン別にグループ化できます。
この記事で取り上げているのは2019/12現在の情報であり、APi仕様は変更される可能性があります。また気軽ではありますがコストエクスプローラAPIは呼び出し単価高いAPI(リクエストあたり 0.01USD)であったりするのでご注意ください。
さて。
Cloud9 を起動しましょう
もちろん Cloud9 を使います。そして Typescript です。
特に理由がなければ Amazon Linux で立てるといいでしょう。今回は CostExplorerApiDev という名前で立ち上げました。
起動したら、下部ターミナルから
npm
のライブラリをセットアップします。npm init -y npm install -D typescript ts-node aws-sdk @types/node prettier npx tsc --inittypescript の設定を開き、Format on Save は有効にしておきます。
tsconfig は以下のようにしている。最低限。
tsconfig.json{ "compilerOptions": { "target": "es2018", "module": "commonjs", "lib": ["es2018"], "strict": true, "moduleResolution": "node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true } }さて、
vi index.ts
して、動作確認をします。index.tsconsole.log("hello typescript");
npx ts-node index.ts
してちゃんと実行されることを確認したら、次に進みましょう。CostExplorer APIを利用する
普段見ているコストエクスプローラのレポート画面に相当するデータ取得は、
GetCostAndUsage
呼び出しによって行うことができます。公式の情報は薄めなので、必要に応じてAPI情報をサードパーティから参照します。
- https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html (公式)
- https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CostExplorer.html (公式)
- https://any-api.com/amazonaws_com/ce/docs/_X_Amz_Target_AWSInsightsIndexService_GetCostAndUsage/GetCostAndUsage
ここでは、以下のようなデータがほしいとしましょう。
- 月次で
- 2019/06- 2019/11 の範囲で
- 償却されたコスト(AMORTIZED COST)を
- クレジット(Credit), 払い戻し(Refund)、税金(Tax)、前払い金(Upfront)に関するレコードを除外し 1
- 連結アカウントごとにグループ化する
これは自分が普段利用しているコストエクスプローラのフィルタ条件を整理したものです。
キャッシュフロー上の実際の支払い金額が知りたいのではなく、発生ベースでその月に使用したアカウントごとの費用が知りたいとしましょう。クレジットや払い戻しによる費用の上下、各種の事前チャージのお金の動きは除外したいものとします。簡単のためサンプルでは console に出力しているだけですが、AWSのクレデンシャルがありSDKからAPI叩けているなら、結果を S3 に置くとか SNS に投げるのにとくべつな工夫は必要ないかと思います。
get_cost.tsimport * as AWS from "aws-sdk"; // Cost Explorer は us-east-1 指定が必要 const costexplorer = new AWS.CostExplorer({ region: "us-east-1" }); const params = { TimePeriod: { Start: "2019-06-01", End: "2019-11-01" }, Filter: { Not: { Dimensions: { Key: "RECORD_TYPE", Values: ["Credit", "Refund", "Tax", "Upfront"] } } }, Granularity: "MONTHLY", Metrics: ["AMORTIZED_COST"], GroupBy: [{ Type: "DIMENSION", Key: "LINKED_ACCOUNT" }] }; costexplorer.getCostAndUsage(params, (err, data) => { if (err) { console.error(err); } else { console.log(data); } });これを実行します。うまくデータが取得できれば以下のようにコンソールに出力されると思います。なお今回は簡単のためにページングは考慮していません。結果オブジェクトに
NextPageToken
が含まれていたら後続を読み込む処理が必要になります。ma2saka:~/environment $ npx ts-node get_cost.ts { GroupDefinitions: [ { Type: 'DIMENSION', Key: 'LINKED_ACCOUNT' } ], ResultsByTime: [ { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false } ] }中身を実際に見たければ
console.log(JSON.stringify(data));
とかすると良いです。ここでは事情により2割愛します。
ResultsByTime
に月ごとのデータが配列として格納されています。ResultsByTime[].TimePeriod
はStart,End
が入ってきます。MONTHLY
なので、Startが対象月の初日(もしくは指定した開始日)、Endが対象月の翌月初日(もしくは指定した終了日)になります。
ResultsByTime[].Estimated
は推定値が集計範囲に含まれていることを表します。今回のような集計をする場合、まだ締まっていない月のデータがEstimated: true
になると思えばよさそうです。
ResultsByTime[].Groups
にその集計期間におけるアカウントごとの集計値が並びます。ここでは税金や前払金などを除いたデータを月次で集計した値が得られます。今回は償却コスト(Amotized Cost)をメトリクスとして集計対象にしました。これによってRIの費用などが月々に償却されて乗ります。指定できるのはこの他に、
BLENDED_COST, UNBLENDED_COST, AMORTIZED_COST, NET_UNBLENDED_COST, NET_AMORTIZED_COST, USAGE_QUANTITY, NORMALIZED_USAGE_AMOUNT
などがありますね。 https://docs.aws.amazon.com/ja_jp/awsaccountbilling/latest/aboutv2/ce-advanced.html などでなんとなく解説されている。3メトリクスは同時に複数指定できます。メトリクス名は Screaming snake case でも Upper Camel Case でも有効みたいだけど、どっちが正なんだろう。
グループ化のタイプとして
DIMENSION
を、キーとしてLINKED_ACCOUNT
を指定しています。連結アカウント単位です。タイプをDIMENSION
とした場合、値としては以下のようなものが指定できます。AZ, INSTANCE_TYPE, LINKED_ACCOUNT, OPERATION, PURCHASE_TYPE, REGION, SERVICE, USAGE_TYPE, USAGE_TYPE_GROUP, RECORD_TYPE, OPERATING_SYSTEM, TENANCY, SCOPE, PLATFORM, SUBSCRIPTION_ID, LEGAL_ENTITY_NAME, DEPLOYMENT_OPTION, DATABASE_ENGINE, CACHE_ENGINE, INSTANCE_TYPE_FAMILY, BILLING_ENTITY, RESERVATION_ID
なお、
GetCostAndUsage
API 経由ではアカウント情報はIDまでしか取れません。アカウントの一覧を取得して付き合わせるには一度Organizations#listAccounts
API を呼び出して辞書を作っておくのが楽だと思います。グループ化のタイプには
TAG
を指定することもできます。その場合にはキーとしてリソースタグのキー名を指定します。日本語で書くとちょっとわかりづらいところですが、クエリ書いてみればまあわかる。以下は、「ある特定のアカウントID」に対し、「
OwnerTeam
」というリソースタグの値で月額費用を集計します。変更点は Filter 条件と GroupBy の指定です。awesome.tsconst params = { TimePeriod: { Start: '2019-06-01', End: '2019-11-01', }, Filter: { And: [{ Dimensions: { Key: "LINKED_ACCOUNT", Values: ["特定のアカウントID"] } }, { Not: { Dimensions: { Key: "RECORD_TYPE", Values: ["Credit", "Refund", "Tax", "Upfront"] } } }, ] }, Granularity: "MONTHLY", Metrics: ["AMORTIZED_COST"], GroupBy: [{ Type: "TAG", Key: "OwnerTeam" }] }Filter はちょっと試行錯誤が必要かもしれませんが、そんなに複雑なものでないのですぐ慣れます。Expressionを適宜参照してください。
Lambda などで動かす場合、
ce:Get*
を許可する
ce:Get*
を許してあげましょう。公式のサンプルではce:*
を許可していますが、権限は狭いに越したことはありません。{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ce:Get*", "Resource": "*" } ] }まとめ
Cloud9 は実行環境がついたターミナルとしか使ってなくて、実はぜんぶ vim で書いてました。すみません。Typescript ではあるんですが、Javascript と書いたコードは区別がつきませんでした。これも、そういうもんですね。
6日目は @ne_sachirou さんです。よろしくお願いします。
- 投稿日:2019-12-05T03:27:23+09:00
AWSコストエクスプローラーAPIと気軽につきあう(2019)
この記事ははてなエンジニアAdvent Calendar 2019の5日目のエントリーです。
はじめに
AWSのコストエクスプローラのデータは12ヶ月しか保存されないので、なるべく外部で保存したいですよね。幸い、コストエクスプローラのデータにアクセスするためのAPIがちゃんと提供されているので、気軽に利用することができます。特に難しいこともないのですが、これから「よーしAWS費用の集計をシステム化しちゃうぞー」という人のために、かんたんに足場を作るための手順を整理しておくことにします。
API更新時の説明において以下のように言及されている通り、コンソール経由よりも詳細なデータアクセスが可能なので、ぜひ面倒くさがらずコスト管理のパイプライン構築を進めていきましょう。
グループ化 – コストエクスプローラーウェブアプリケーションで提供されるグループ化は 1 レベルですが、API では 2 レベルが提供されます。たとえば、まずサービス別にコストまたは RI 使用率をグループ化してから、リージョン別にグループ化できます。
この記事で取り上げているのは2019/12現在の情報であり、API仕様は変更される可能性があります。また気軽ではありますがコストエクスプローラAPIは呼び出し単価高いAPI(リクエストあたり 0.01USD)であったりするのでご注意ください。
さて。
Cloud9 を起動しましょう
もちろん Cloud9 を使います。そして Typescript です。
特に理由がなければ Amazon Linux で立てるといいでしょう。気をつけるべきなのは、パブリックサブネットで立てるということです。今回は CostExplorerApiDev という名前で立ち上げました。
起動したら、下部ターミナルから
npm
のライブラリをセットアップします。npm init -y npm install -D typescript ts-node aws-sdk @types/node prettier npx tsc --inittypescript の設定を開き、Format on Save は有効にしておきます。
tsconfig は以下のようにしている。最低限。
tsconfig.json{ "compilerOptions": { "target": "es2018", "module": "commonjs", "lib": ["es2018"], "strict": true, "moduleResolution": "node", "esModuleInterop": true, "forceConsistentCasingInFileNames": true } }さて、
vi index.ts
して、動作確認をします。index.tsconsole.log("hello typescript");
npx ts-node index.ts
してちゃんと実行されることを確認したら、次に進みましょう。CostExplorer APIを利用する
普段見ているコストエクスプローラのレポート画面に相当するデータ取得は、
GetCostAndUsage
呼び出しによって行うことができます。公式の情報は薄めなので、必要に応じてAPI情報をサードパーティから参照します。
- https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html (公式)
- https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CostExplorer.html (公式)
- https://any-api.com/amazonaws_com/ce/docs/_X_Amz_Target_AWSInsightsIndexService_GetCostAndUsage/GetCostAndUsage
ここでは、以下のようなデータがほしいとしましょう。
- 月次で
- 2019/06- 2019/11 の範囲で
- 償却されたコスト(AMORTIZED COST)を
- クレジット(Credit), 払い戻し(Refund)、税金(Tax)、前払い金(Upfront)に関するレコードを除外し 1
- 連結アカウントごとにグループ化する
これは自分が普段利用しているコストエクスプローラのフィルタ条件を整理したものです。
キャッシュフロー上の実際の支払い金額が知りたいのではなく、発生ベースでその月に使用したアカウントごとの費用が知りたいとしましょう。クレジットや払い戻しによる費用の上下、各種の事前チャージのお金の動きは除外したいものとします。簡単のためサンプルでは console に出力しているだけですが、AWSのクレデンシャルがありSDKからAPI叩けているなら、結果を S3 に置くとか SNS に投げるのにとくべつな工夫は必要ないかと思います。
get_cost.tsimport * as AWS from "aws-sdk"; // Cost Explorer は us-east-1 指定が必要 const costexplorer = new AWS.CostExplorer({ region: "us-east-1" }); const params = { TimePeriod: { Start: "2019-06-01", End: "2019-11-01" }, Filter: { Not: { Dimensions: { Key: "RECORD_TYPE", Values: ["Credit", "Refund", "Tax", "Upfront"] } } }, Granularity: "MONTHLY", Metrics: ["AMORTIZED_COST"], GroupBy: [{ Type: "DIMENSION", Key: "LINKED_ACCOUNT" }] }; costexplorer.getCostAndUsage(params, (err, data) => { if (err) { console.error(err); } else { console.log(data); } });これを実行します。うまくデータが取得できれば以下のようにコンソールに出力されると思います。なお今回は簡単のためにページングは考慮していません。結果オブジェクトに
NextPageToken
が含まれていたら後続を読み込む処理が必要になります。ma2saka:~/environment $ npx ts-node get_cost.ts { GroupDefinitions: [ { Type: 'DIMENSION', Key: 'LINKED_ACCOUNT' } ], ResultsByTime: [ { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false }, { TimePeriod: [Object], Total: {}, Groups: [Array], Estimated: false } ] }中身を実際に見たければ
console.log(JSON.stringify(data));
とかすると良いです。ここでは事情により2割愛します。
ResultsByTime
に月ごとのデータが配列として格納されています。ResultsByTime[].TimePeriod
はStart,End
が入ってきます。MONTHLY
なので、Startが対象月の初日(もしくは指定した開始日)、Endが対象月の翌月初日(もしくは指定した終了日)になります。
ResultsByTime[].Estimated
は推定値が集計範囲に含まれていることを表します。今回のような集計をする場合、まだ締まっていない月のデータがEstimated: true
になると思えばよさそうです。
ResultsByTime[].Groups
にその集計期間におけるアカウントごとの集計値が並びます。ここでは税金や前払金などを除いたデータを月次で集計した値が得られます。今回は償却コスト(Amotized Cost)をメトリクスとして集計対象にしました。これによってRIの費用などが月々に償却されて乗ります。指定できるのはこの他に、
BLENDED_COST, UNBLENDED_COST, AMORTIZED_COST, NET_UNBLENDED_COST, NET_AMORTIZED_COST, USAGE_QUANTITY, NORMALIZED_USAGE_AMOUNT
などがありますね。 https://docs.aws.amazon.com/ja_jp/awsaccountbilling/latest/aboutv2/ce-advanced.html などでなんとなく解説されている。3メトリクスは同時に複数指定できます。メトリクス名は Screaming snake case でも Upper Camel Case でも有効みたいだけど、どっちが正なんだろう。
グループ化のタイプとして
DIMENSION
を、キーとしてLINKED_ACCOUNT
を指定しています。連結アカウント単位です。タイプをDIMENSION
とした場合、値としては以下のようなものが指定できます。AZ, INSTANCE_TYPE, LINKED_ACCOUNT, OPERATION, PURCHASE_TYPE, REGION, SERVICE, USAGE_TYPE, USAGE_TYPE_GROUP, RECORD_TYPE, OPERATING_SYSTEM, TENANCY, SCOPE, PLATFORM, SUBSCRIPTION_ID, LEGAL_ENTITY_NAME, DEPLOYMENT_OPTION, DATABASE_ENGINE, CACHE_ENGINE, INSTANCE_TYPE_FAMILY, BILLING_ENTITY, RESERVATION_ID
なお、
GetCostAndUsage
API 経由ではアカウント情報はIDまでしか取れません。アカウントの一覧を取得して付き合わせるには一度Organizations#listAccounts
API を呼び出して辞書を作っておくのが楽だと思います。グループ化のタイプには
TAG
を指定することもできます。その場合にはキーとしてリソースタグのキー名を指定します。日本語で書くとちょっとわかりづらいところですが、クエリ書いてみればまあわかる。以下は、「ある特定のアカウントID」に対し、「
OwnerTeam
」というリソースタグの値で月額費用を集計します。変更点は Filter 条件と GroupBy の指定です。awesome.tsconst params = { TimePeriod: { Start: '2019-06-01', End: '2019-11-01', }, Filter: { And: [{ Dimensions: { Key: "LINKED_ACCOUNT", Values: ["特定のアカウントID"] } }, { Not: { Dimensions: { Key: "RECORD_TYPE", Values: ["Credit", "Refund", "Tax", "Upfront"] } } }, ] }, Granularity: "MONTHLY", Metrics: ["AMORTIZED_COST"], GroupBy: [{ Type: "TAG", Key: "OwnerTeam" }] }Filter はちょっと試行錯誤が必要かもしれませんが、そんなに複雑なものでないのですぐ慣れます。Expressionを適宜参照してください。
Lambda などで動かす場合、
ce:Get*
を許可する
ce:Get*
を許してあげましょう。公式のサンプルではce:*
を許可していますが、権限は狭いに越したことはありません。{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ce:Get*", "Resource": "*" } ] }まとめ
Cloud9 は実行環境がついたターミナルとしか使ってなくて、実はぜんぶ vim で書いてました。すみません。Typescript ではあるんですが、Javascript と書いたコードは区別がつきませんでした。これも、そういうもんですね。
6日目は @ne_sachirou さんです。よろしくお願いします。
- 投稿日:2019-12-05T02:42:46+09:00
AWS CognitoのGo SDKを使ってみた
AWS cognitoのドキュメントに関する自分用メモです。
サンプルコード*管理者権限を使用するにはAWS CLIによる認証情報の設定が必要です。詳しくはこちらを参照してください。
サインアップ
AdminCreateUser
管理者権限でユーザーを作成。
ユーザー名と、電話番号またはメールアドレスのどちらか一方を設定。
ユーザーはSMSやemailにより一時パスワードを与えられる。
ユーザーはFORCE_CHANGE_PASSWORD状態になり、初回signin時にパスワードを変更する必要がある。
DesiredDeliveryMediumsで一時パスワードをSMSとemailのどちら(または両方)に送信するか設定可能。
TemporaryPasswordで一時パスワードを指定可能。指定しなかった場合はcognitoが自動生成。SignUp
通常権限でユーザーを作成。
ユーザー名、パスワード、電話番号またはメールアドレスのどちらか一方を設定。
ユーザーはUNCONFIRMED状態になり、有効化するために認証が必要。
認証リンク or コードがSMS or EMAILでcognitoから送信される。
認証方法はcognitoのユーザープール作成時の設定に依存する。認証
AdminConfirmSignUp
管理者権限により、リンクやコードによる認証を無視して認証を許可する。
ConfirmSignUp
SignUp後、コードによる認証に使用する。
サインイン
AdminInitiateAuth
一般的なサインイン。
AccessToken、ExpiresIn、IdToken、RefreshTokenが返ってくる。InitiateAuth
Adminの場合と同じ。
削除
AdminDeleteUser
管理者権限によりユーザーを削除。
DeleteUser
通常権限でユーザーを削除。
サインイン時に取得できるAccess Tokenが必要。
- 投稿日:2019-12-05T00:56:12+09:00
Serverless時代のWordPress構築
みなさんこんにちは。
aratana Advent Calendar 2019の5日目です。
各所でServelessの波が押し寄せていますが、何よりもサーバーリソースや、ソフトウエアのバージョンアップなどのメンテコストを下げられる点はとても嬉しいです。
そんな中、世の中でよく使われているWordPressはServelessとは少し真逆のイメージがあります。
サーバーを用意、LAMP環境を構築し、WordPressをインストール。
WordPressのセキュリティーアップデートはもちろんメジャーアップデートについていく必要がある。。。WordPressそのものは好きですが、しっかりと運用を行なっていく必要のある立場の人からするとちょっと気軽に導入したいものではないですね。。
■ Shifter とは?
そんなわがままな私たちにオススメのサービスが
Shifter
です。
https://www.getshifter.io/japanese/簡潔に言うと、WordPressの記事を静的サイトとして書き出し、Servelessの仕組みで記事が見れるようにする。と言うのを自動でやってくれるサービスです。
管理画面も必要な時だけ起動しアクセスするといった感じです。■ Shifter で記事を公開してみる
思い立ったらすぐ行動と言うことで、Shifterを利用してサイトを公開してみようと思います。
(ここから下のキャプチャは12月3日時点なのでUI等は今後変更される可能性ありです)まずは会員登録
以下のリンクから会員登録出来ます。
https://go.getshifter.io/register
Continueを押すと認証のメールが入力したメールアドレスに送られるので、メール内の認証URLをクリックすれば認証完了となり、会員登録完了となります。
登録完了画面の後に再度ログインが必要なようなので、登録したメールアドレス、パスワードでログインしてください。管理画面の中
ログイン後は以下のような画面が表示されました。
シンプルなダッシュボードで、やることはわかりやすいですね。
WordPressサイトを立ち上げてみる
早速画面の真ん中に聳え立っている
Create Now
を押して記事を作成してみましょう。プランの選択
最初は、プランを選ぶようです。今回はお試しですので、
Free Tier
を選びました。
有料プランは、月払いと年払いを選べるようで、ずっと使う場合は年払いが結構安くなってお得なようですね。
とはいえ、VPSを1台借りたと思えばそんなに高い料金ではないと思ます。
基本情報の入力
プランを選択後、基本情報を入力するポップアップが表示されるので、入力していきます。
Team
の欄がありますが、特に設定してない場合、Defaultで問題ないです。(アカウントの中にTeamの概念があるようです。)
サイトが作成された後
とりあえずWordPressのサイトは作成されました。
とはいえ、Shifterの中でWordPressが作成されただけなので、現時点では世界には公開されていません。
現時点でのサイトを確認しよう
サイトが作成された後に表示されている、
Visit Site
をクリックすると、Shifterの中で一時的に発行されるURLにリダイレクトされ、その中で現段階のサイトをプレビューすることが出来ます。
とりあえず初期のサイトが表示されたようです。*あくまでも一時的なプレビューなので、リダイレクト先のリンクを保存するとかはあまり意味がなさそうです。ある一定時間たったら変更されます。
WordPress管理画面に入ろう
今度は、サイト作成ごの画面の、
Dashboard
のボタンを押すとプレビューと同じく管理画面が開かれます。この時ログインした状態で表示されます。
見慣れた管理画面です。初期設定は英語のようです。必要だったら設定で日本語にすぐ変えれるので問題ないですね。
サイトを公開する
取り急ぎ一つ記事を追加したので、この記事を世界に公開したいと思います。
公開の手順としては、以下の2つです。
1. アーティファクトの作成
2. 作成したアーティファクトのデプロイ(初期設定では、アーティファクト生成後に自動デプロイとなっている)
AWSのCode3兄弟を使っている人とかだとなんとなく中の仕様がわかりそうな気がしますが。。1. Artifactの名前を入れて、Generateを実行
First Build Artifact
を作成します。この時に、静的サイトに書き出しが行われているようです。
2. サイトの公開
今回は、自動デプロイがONになっているので、公開自体は勝手にされます。
OFFにする場合は、Settings > Auto Deploy Artifact のToggleをOFFにしてください。
ここまで完了するとサイトは公開されており、View Site
をクリックすると公開されたサイトに飛ぶことが出来ます。
無料プランなので、URLはランダムです。
サイトの更新について
基本的にはサイト公開と同じ流れで、
1. 再度アーティファクトの作成
2. 作成したアーティファクトのデプロイ(自動デプロイとなっている場合は不要)
となりますが、今回はあえて自動デプロイをOFFでやってみます。
Second Build Artifact
を作成しましたが、この状態ではまだFires Build Artifact
が公開されています。
ここでいいなと思ったのは、一度作成した後デプロイの前にPreview
が出来る点です。安心して公開が出来ますね。
また、新しいBuildを公開しても元のBuildは残っているので何かあってもすぐ切り戻し出来ます。
ここまでくると、公開の時限設定とか欲しいですね。。
まとめ
今回は、基本的な使い方の部分しか触れませんでしたが、他にもWebhookや、API、Team管理など色々な機能がありそうです。
小中規模の、コーポレートサイトorブログサイトなどにはとても向いてると思いました。
静的ページで収まる要件ならとても有効です。
裏はCloudFrontをしようしているみたいなので、Aliasさえ張ってしまえば、独自のURLをしようできるなど、そこそこ自由度は高いです。
何かと忌み嫌われることもあるWordPressですが、Shifterのような楽に管理できるツールを使って運用も保守もしやすいシステムづくりを目指したいです。
- 投稿日:2019-12-05T00:05:36+09:00
【待望リリース!】もう Lambda×RDS は怖くない!LambdaでRDSプロキシを徹底的に検証してみた 〜全てがサーバレスになる〜
本日の reinvent でのリリースで衝撃のアップデートがたくさん出ましたね。EKS on Fargate や SageMaker の大幅アップデートも魅力的ですが Lambda の常識をくつがえす RDS のプロキシ機能が登場しました ?
ついに出ました!これでLambdaからRDS使うの怖くなくなります。Lambdaからも使えるコネクションプールマネージャ。これでLambdaとRDBMSの相性問題は先日のVPCの改善と合わせて解消されますー。SQL最高!! / “Using Amazon RDS Proxy with AWS Lambda | AWS Compute Blog” https://t.co/YWgIu19GoH
— Keisuke69@AWS re:Invent (@Keisuke69) December 4, 2019Lambda から RDS に対するアクセスはコネクション数の上限に達してしまうという理由からアンチパターンとされてきました。そのため、RDS をデータストアに選択する場合は ECS や EC2 上にアプリケーションをホストする事が一般的でした。Lambda の接続先 DB に RDS を選べるということはほとんどのWebアプリケーションがサーバレスで実行できるようになるので夢が広がります。
本記事では RDS プロキシを使った Lambda の構成を作ってコネクション数の挙動について検証してみました。
https://aws.amazon.com/blogs/compute/using-amazon-rds-proxy-with-aws-lambda/
※本記事は上記のブログを参考にしています。一部文脈で引用している箇所があります。RDS プロキシは、データベースへの接続プールを維持します。これにより Lambda から RDS データベースへの多数の接続を管理できます。
Lambda 関数は、データベースインスタンスの代わりに RDS プロキシと通信します。スケーリング起動した Lambda 関数によって作成された多くの同時接続を保持するために必要な接続プーリングを処理します。これにより、Lambda アプリケーションは関数呼び出しごとに新しい接続を作成するのではなく、既存の接続を再利用できます。従来はアイドル接続のクリーンアップと接続プールの管理を処理するコードを用意していたのではないでしょうか。これが不要になります。劇的な進化です。関数コードは、より簡潔でシンプルで、保守が容易になります。
現在はまだプレビュー版ですがこの機能を徹底検証していきましょう。
せっかく検証するのですから従来 ECS などで一般的に使ってたフレームワークを例に上げてみましょう。今回は NestJS を Lambda にデプロイして RDS と接続してみます。
NestJS を Lambda にデプロイする
Serverless Framework を使用して NestJS アプリケーションを AWS Lambda にデプロイします。
こちらのサンプルソースが参考になりました。ほぼそのまま引用させていただきます。Lambda がデプロイできたことを確認しておきましょう。
$ sls deploy Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... ~~~~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~~~~ endpoints: ANY - https://djpjh5aklf.execute-api.us-east-1.amazonaws.com/dev/ ANY - https://djpjh5aklf.execute-api.us-east-1.amazonaws.com/dev/{proxy+} functions: index: serverless-nestjs-dev-index layers: None Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.生成されたエンドポイントにアクセスします。
これで準備ができました。まずは Hello World! と文字列を返す NestJS アプリケーションを Lambda をデプロイできました。これから MySQL と接続できるアプリケーションを作っていきます。開発過程は省略しますが、以下のリポジトリに完成品をアップロードしておきます。
完成品:https://github.com/daisuke-awaji/serverless-nestjs
参考:Nest(TypeScript)で遊んでみる 〜DB 連携編〜
タスクの CRUD 操作ができるアプリケーションを用意しました。
Secret Manger に RDS への接続情報を登録
まずは Secret Manger コンソールで RDS への接続情報を登録するようです。
シークレットができたら ARN をメモしておきましょう。あとで使います。
IAM
次に、RDS プロキシがこのシークレットを読み取ることができる IAM ロールを作成します。RDS プロキシはこのシークレットを使用して、データベースへの接続プールを維持します。IAM コンソールに移動して、新しいロールを作成します。 前の手順で作成したシークレットに secretsmanager アクセス許可を提供するポリシーを追加します 。
IAM ポリシー
IAM ロール
rds-get-secret-role という名前で IAM ロールを作成しました。
RDS Proxy
さて、ここからが本題です。
RDS のコンソールを開くと Proxies の項目があります。Lambda の接続先をこのプロキシに向けることでコネクションプールをうまく使いまわしてくれるようです。作成してみましょう。先ほど作成した IAM ロールや RDS を入力します。
Lambda の向き先を RDS から RDS Proxy に切り替える
RDS インスタンスに対して直接接続する代わりに、RDS プロキシに接続します。これを行うには、2 つのセキュリティオプションがあります。IAM 認証を使用するか、Secrets Manager に保存されているネイティブのデータベース認証情報を使用できます。IAM 認証は、機能コードに認証情報を埋め込む必要がないため、推奨されているようです。この記事では、Secrets Manager で以前に作成したデータベース資格情報を使用します。
DBに接続するアプリケーションの設定を変更してデプロイしましょう。
db.config.tsimport { TypeOrmModuleOptions } from "@nestjs/typeorm"; import { TaskEntity } from "./tasks/entities/task.entity"; export const dbConfig: TypeOrmModuleOptions = { type: "mysql", host: "rds-proxy.proxy-ch39q0fyjmuq.us-east-1.rds.amazonaws.com", // <-- DBの向き先をProxyに切り替える port: 3306, username: "user", password: "password", database: "test_db", entities: [TaskEntity], synchronize: false };$ npm run build && sls deployまだ Serverless では RDS Proxy をサポートしていないようでしたので Lambda のコンソールから設定してみます。セキュリティグループやサブネットなどは適宜各自の環境に合わせて作成してください。
RDS Proxy 経由でも無事に接続できました ?
※事前に DB にはテストデータを入れてあります
準備に使用した SQL
CREATE TABLE `tasks` ( `id` int(36) unsigned NOT NULL AUTO_INCREMENT, `overview` varchar(256) DEFAULT NULL, `priority` int(11) DEFAULT NULL, `deadline` date DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=94000001 DEFAULT CHARSET=utf8mb4; INSERT INTO `tasks` (`id`, `overview`, `priority`, `deadline`) VALUES (1, '掃除', 0, '2020-11-11'), (2, '洗濯', 2, '2020-12-03'), (3, '買い物', 0, '2020-11-28');負荷テストを実行してみる
コネクション数が Lambda のスケールに合わせて増え続けるような挙動を取らないか確認してみましょう。
今回は負荷のために Artillary を使用します。
yaml ファイルでシナリオを記述して実行する Nodejs 製の負荷テストツールです。Artillary のインストール
$ npm install -g artillery実行
yaml ファイルを記述しなくてもワンラインで実行できる手軽さも魅力的なツールで愛用しています。
以下のようなコマンドで簡単に実行できます。30 ユーザが 300 回リクエストを送るといった内容です。$ artillery quick --count 300 -n 30 https://djpjh5aklf.execute-api.us-east-1.amazonaws.com/dev/tasks実行された Lambda を確認します。Invocations が 9000 回を記録しています。
一方で RDS のコネクション数はなんと 43 になっていました。すごい。
ちなみに MySQL の現在のコネクション数はshow status like 'Threads_connected'
で確認できます。
負荷テスト開始前 最大リクエスト時 18 43 RDS Proxy を使わない場合はどうなるか
アプリケーションの向き先を RDS 本体に直接接続するように変更してみます。
この状態でもう一度負荷テストを行うとどうなるでしょうか。import { TypeOrmModuleOptions } from '@nestjs/typeorm'; import { TaskEntity } from './tasks/entities/task.entity'; export const dbConfig: TypeOrmModuleOptions = { type: 'mysql', host: 'aurora.cluster-ch39q0fyjmuq.us-east-1.rds.amazonaws.com', // <-- RDS 本体に向ける port: 3306, username: 'user', password: 'password', database: 'test_db', entities: [TaskEntity], synchronize: false, };実行
コネクション数が 124 まで膨れ上がってしまいました。
やはりプロダクションロードで普通に Lambda+RDS の組み合わせはやってはいけないアンチパターンになりそうですね。RDS Proxy の威力を改めて感じることができました。$ artillery quick --count 300 -n 10 https://djpjh5aklf.execute-api.us-east-1.amazonaws.com/dev/tasks
負荷テスト開始前 最大リクエスト時 18 124 まとめ
RDS プロキシを使用することで、データベースへの接続プールを保持することが確認できました。これで API やユーザリクエストを受けるようなワークロードでも Lambda から RDS への多数の接続を管理できます。とてつもなく強力なアップデートを体感できました。今後追加で RDS Proxy を使用する場合と使用しない場合とで、レスポンスタイムに違いが出てくるのかなど細かなところまで検証したいと思います。
クラウドはいよいよここまで成長してきました。
次は RDS がインスタンスを意識することなく水平にスケールするようになるのでしょうか。
その時は完全にサーバレスなクラウドが完成しますね。待ち遠しいです。
- 投稿日:2019-12-05T00:00:00+09:00
CDP:EC2を使わずLambdaで署名つきURLを作成する
EC2は使わない
CDP(Cloud Design Pattern)で紹介されているPCD(Private Cache Distribution)はEC2を使った方法が記載されていますが、EC2はできることが多いので使いたくない、、
そんな訳でLambda上で署名つきURLを作成します。
構成
流れとしてはAPIを投げ、API GatewayはLambdaをキックします。
LambdaはSecertManagerから各パラメータを受け取り、署名つきURLを作成し、値を返却します。
クライアント側は受け取った署名つきURLでデータを取得する。という流れになります。準備
まず、CloudFrontのキーペアを作成します。※ルートのアカウントしか作成できません!
作り方は以下を参照してください。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs-procedureキーペアIDとプライベートキーはSecretManagerに保存します。
キーは「KEY_PAIR_ID」、「PRIVATE_KEY」としておきます。準備は以上です!
CDKでリソースを作り上げる!
CDK(Cloud Development Kit)で簡単に作っちゃいます。
まずは一番重要なLambdaに置くコード
~/src/lambda/index.tsimport { CloudFront, SecretsManager } from 'aws-sdk' import { Signer } from 'aws-sdk/lib/cloudfront/signer' import { APIGatewayProxyEvent } from 'aws-lambda' function getSignedUrlAsync( keyPairId: string, privateKey: string, options: Signer.SignerOptionsWithPolicy | Signer.SignerOptionsWithoutPolicy ) { return new Promise<string>((resolve, reject) => { const signer = new CloudFront.Signer(keyPairId, privateKey) signer.getSignedUrl(options, (err, url) => { if (err) { reject(err) } resolve(url) }) }) } const formatPrivateKey = (privateKey: string) => { return privateKey.replace(/(-----) /, '$1\n').replace(/ (-----)/, '\n$1') // --BEGIN と --END の間には改行がないと[no start line]エラーになる為 } const parseStringifyToJson = (data: any) => { return JSON.parse(data) } export const handler = async (event: APIGatewayProxyEvent) => { const param = parseStringifyToJson(event.body) const url = param.bucket.url const secretId = param.secretsManager.id const client = new SecretsManager({ region: 'ap-northeast-1' }) const data = await client.getSecretValue({ SecretId: secretId }).promise() const secretJson = JSON.parse(data.SecretString!) const keyPairId = secretJson.KEY_PAIR_ID const privateKey = formatPrivateKey(secretJson.PRIVATE_KEY) const expires = new Date().getTime() + 30 * 60 * 1000 const preURL = await getSignedUrlAsync(keyPairId, privateKey, { url, expires }) const responseParam = { statusCode: 200, body: JSON.stringify({ message: 'success', preURL }) } return responseParam }これができたらLambda、API Gateway、S3、CloudFrontの順で作成します。
~/src/resrouce/Lambda.tsimport * as cdk from '@aws-cdk/core' import * as lambda from '@aws-cdk/aws-lambda' import * as iam from '@aws-cdk/aws-iam' const createLambda = (scope: cdk.Construct) => { const handler = new lambda.Function(scope, 'Function', { code: new lambda.AssetCode('src/lambda'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_10_X }) handler.addToRolePolicy( new iam.PolicyStatement({ resources: ['*'], actions: ['secretsmanager:GetSecretValue'] }) ) return handler } export default createLambda~/src/resources/ApiGateway.tsimport * as cdk from '@aws-cdk/core' import * as apiGateway from '@aws-cdk/aws-apigateway' import * as lambda from '@aws-cdk/aws-lambda' const createApiGateway = (scope: cdk.Construct, handler: lambda.IFunction) => { const apiKey = new apiGateway.ApiKey(scope, 'ApiKey') const privateDistributionApiGateway = new apiGateway.LambdaRestApi( scope, 'RestApi', { handler, deploy: true, deployOptions: { stageName: 'Stage' }, proxy: false } ) new apiGateway.Method(scope, 'Method', { httpMethod: 'POST', resource: privateDistributionApiGateway.root, options: { apiKeyRequired: true } }) new apiGateway.UsagePlan(scope, 'UsePlan', { apiKey, apiStages: [ { api: privateDistributionApiGateway, stage: privateDistributionApiGateway.deploymentStage } ] }) return privateDistributionApiGateway } export default createApiGateway~/src/resources/S3.tsimport * as cdk from '@aws-cdk/core' import * as s3 from '@aws-cdk/aws-s3' import * as cloudFront from '@aws-cdk/aws-cloudfront' import * as iam from '@aws-cdk/aws-iam' const createS3 = (scope: cdk.Construct) => { const privateDistributionBucket = new s3.Bucket(scope, 'Bucket', { blockPublicAccess: new s3.BlockPublicAccess({ blockPublicAcls: true, blockPublicPolicy: true, ignorePublicAcls: true, restrictPublicBuckets: true }) }) const privateDistirbutionCloudFrontOriginAccessIdentity = new cloudFront.CfnCloudFrontOriginAccessIdentity( scope, 'CloudFrontOriginAccessIdentity', { cloudFrontOriginAccessIdentityConfig: { comment: `access-identity-${privateDistributionBucket.bucketName}` } } ) privateDistributionBucket.addToResourcePolicy( new iam.PolicyStatement({ actions: ['s3:GetObject'], effect: iam.Effect.ALLOW, principals: [ new iam.ArnPrincipal( `arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${privateDistirbutionCloudFrontOriginAccessIdentity.ref}` ) ], resources: [`arn:aws:s3:::${privateDistributionBucket.bucketName}/*`] }) ) return { privateDistributionBucket, privateDistirbutionCloudFrontOriginAccessIdentity } } export default createS3~/src/resources/CloudFront.tsimport * as cdk from '@aws-cdk/core' import * as CloudFront from '@aws-cdk/aws-cloudfront' import * as s3 from '@aws-cdk/aws-s3' import * as route53 from '@aws-cdk/aws-route53' import * as route53Targets from '@aws-cdk/aws-route53-targets' const createCloudFront = ( scope: cdk.Construct, bucket: s3.Bucket, originAccessIdentityId: string, ) => { new CloudFront.CloudFrontWebDistribution( scope, 'Distribution', { originConfigs: [ { s3OriginSource: { s3BucketSource: bucket, originAccessIdentityId }, behaviors: [ { compress: false, trustedSigners: ['{your iam account}'], isDefaultBehavior: true } ] } ] } ) } export default createCloudFrontスタック
~/src/stacks/PrivateDistributionStack.tsimport * as cdk from '@aws-cdk/core' import createApiGateway from '../resources/ApiGateway' import createLambda from '../resources/Lambda' import createCloudFront from '../resources/CloudFront' import createS3 from '../resources/S3' class PrivateDistributionStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props) const handler = createLambda(this) createApiGateway(this, handler) const { privateDistributionBucket, privateDistirbutionCloudFrontOriginAccessIdentity } = createS3(this) createCloudFront( this, privateDistributionBucket, privateDistirbutionCloudFrontOriginAccessIdentity.ref ) } } export default PrivateDistributionStackSynth & Deploy
LambdaはTypeScriptに対応してないので、index.tsはトランスパイルします。
synth時に勝手にトランスパイルしてくれるようにpackage.jsonに追記しておきましょう!package.json{ "scripts": { "build": "npx tsc src/lambda/index.ts && npx cdk synth", "deploy": "npx cdk deploy --profile {profile_name}", "destroy": "npx cdk destroy PrivateDistributionStack --profile {profile_name}" } }下記コマンドでsynth・deploy
$ npm run build $ npm run deployこれでリソースは完成しました!
リクエストを投げてみる
Postmanを起動してメソッドはPOSTにし、Headerにx-api-keyを指定して値には先ほど作ったAPIKeyを入れます。
Bodyには下記のように指定します。{ "Bucket": { "url": "https://{CloudFrontのドメイン}/{バケットのデータの場所}" }, "secretsManager": { "id": "{最初に作ったSecretManagerのID}" } }署名つきURLが返却され、無事にS3のデータを取得することができました!
まとめ
時間がなく、駆け足で書いてしまったので内容薄めになってしまいました...。
最後までありがとうございました!