Lambdaからcurlしようとしたらエラー
Lambdaで遊んでるんだけど、ちょっとした検証をしたくてLambdaからhttpリクエストをcurlで出そうとした。ちなみに言語はPython。requestsをimportしてもいいが、zipにしてあげるのがめんどくさかった。
subprocessをimportして、curlをしてみたらエラーが。。。
どうやら標準でインストールされていないらしい。
[ERROR] FileNotFoundError: [Errno 2] No such file or directory: 'curl' Traceback (most recent call last): File "/var/task/lambda_function.py", line 51, in lambda_handler subprocess.run(['curl', 'example.com']) File "/var/lang/lib/python3.8/subprocess.py", line 489, in run with Popen(*popenargs, **kwargs) as process: File "/var/lang/lib/python3.8/subprocess.py", line 854, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "/var/lang/lib/python3.8/subprocess.py", line 1702, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename)END RequestId: 54771e23-e24a-4e75-9de9-fa9729347f06
残念だけど、まあ納得。requestsをimportがいいのかな。
ちなみに/usr/bin配下をみてみた。
alias arch awk base64 basename bash bashbug bashbug-64 bg ca-legacy captoinfo cat catchsegv cd chcon chgrp chmod chown cksum clear comm command cp csplit cut date dd df dgawk dir dircolors dirname du echo egrep env expand expr factor false fc fg fgrep fmt fold gawk gencat getconf getent getopts grep groups head hostid iconv id igawk info infocmp infokey infotocap install jobs join ldd link ln locale localedef logname ls makedb md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od p11-kit paste pathchk pgawk pinky pldd pr printenv printf ptx pwd read readlink realpath reset rm rmdir rpcgen runcon sed seq sh sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf sleep sort sotruss split sprof stat stdbuf stty sum sync tabs tac tail tee test tic timeout toe touch tput tr true truncate trust tset tsort tty tzselect umask unalias uname unexpand uniq unlink update-ca-trust users vdir wait wc who whoami yes
ぐへ。
リードレプリカを含むRDSは停止できない
開発・検証環境ではLambdaを使ってRDSを夜間停止している。
ログをみたところ、エラーになっており正常に停止できていないことに気づく。
An error occurred (InvalidDBInstanceState) when calling the StopDBInstance operation: Cannot stop or start a Read-Replica instance: InvalidDBInstanceStateFault Traceback (most recent call last): File "/var/task/lambda_function.py", line 44, in lambda_handler done = rds_stop(TARGET_RDS) File "/var/task/lambda_function.py", line 87, in rds_stop resp = rds.stop_db_instance(DBInstanceIdentifier=rds_identifier) File "/var/runtime/botocore/client.py", line 272, in _api_call return self._make_api_call(operation_name, kwargs) File "/var/runtime/botocore/client.py", line 576, in _make_api_call raise error_class(parsed_response, operation_name) botocore.errorfactory.InvalidDBInstanceStateFault: An error occurred (InvalidDBInstanceState) when calling the StopDBInstance operation: Cannot stop or start a Read-Replica instance
そういえばリードレプリカは最近立てたんだった。のでその影響らしい。
公式サイトを確認したところ、リードレプリカを含むRDSは、マスター側もリードレプリカ 側も停止することができなくなるらしい。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_StopInstance.html
リードレプリカが含まれているか、リードレプリカである DB インスタンスは停止できません。
なるほどなー。
PyCharmをインストールしてみた
Pythonは今までvscodeで書いていたんだけど、仕事でそれなりにコードを書くことが増えそうなのでPyCharmをインストールしてみた。
まだ全然試していないから、ちょう上部だけの感想だけど、
プロジェクト作成時にvenvの設定とかできる。
ただvenv使うようなプロジェクトかっていったらそんなこともないので、微妙に使い道ない感じ。
Existing Interpriterを使えば、venvとかの環境を使わないでのプロジェクト作成もできた。
ライブラリとしては、pipとsetuptoolsのみインストールされている。
GUIからライブラリは検索・インストールできる。 まあ便利っちゃ便利。
あといいところとしては、多分デフォルト設定?でPEP8のコーディング規約チェックをしているところ。
コード補完とかはやっぱりいいよね。
多分めちゃくちゃ高機能なんだろうけど、vscodeでよくねって思った。慣れの問題もあるだろうけど。学習コストがね...。
暇なときにちょくちょく使ってみよう。
opensslコマンドでbase64エンコードしたら改行されてしまった
APIの開発を行っていて、curlでリクエストを送る機会があった。
リクエストのイメージとしてはこんな感じ。
curl -k "https://sample-api/v1/users" -X POST -d "id=XXX" -H "Authorization:Basic $(echo -n api-user:EtKWah5ALFQhwPAf7YFx3grY57YQz6kXLh4579MZ | openssl base64)"
APIの中身は今回関係ないのでおいといて、AuthorizationヘッダにAPI利用者の情報をbase64エンコードした状態で設定し、アプリ側でデコードして利用者認証をする。
しかし、curlでこのリクエストを投げると「curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)」というエラーになる。
これについて少しハマったのでメモ。
前提として、システム構成はAWSのALBにEC2をぶら下げている。 Webサーバはnginxを使用。
エラーについて調べる
このエラーについてググってみると、ALB - EC2間の通信はhttp1.1を使っているから云々とか、nginx側がhttp2で受けようとしている云々とか出てくる(本当にそういう話なのかどうもしっくりこないけど...)。
とりあえずネットの情報を信じてHTTPのバージョンを指定し、curl -k -http1.1 "https://sample-api/v1/users" -X POST -d "id=XXX" -H "Authorization:Basic $(echo -n user:EtKWah5ALFQhwPAf7YFx3grY57YQz6kXLh4579MZ | openssl base64)"
で叩いてみるとリクエスト自体は成功した。じゃあやっぱりそういう問題なのかなとも思ったが、今度は何やらアプリ側で利用者認証ができていない...。
そこでcurlではなくPostmanでAPIを叩いてみると正常に動いた。この場合は利用者認証もできている...。
次に、curlで叩いた時とPostmanで叩いた時でアプリをデバッグしてみる。どうもAuthorizationヘッダの値が違う...?正確にいうと、curlで叩いた時の方がPostmanで叩いた時よりもAuthorizationヘッダが短い。
ということはcurlしたときにはAuthorizationヘッダが正しく送れていないっぽい。
opensslの改行について見ていく
怪しいところはわかってきたので、curlは一旦おいといてopensslでエンコードしている部分を見ていく。
~ $ echo -n "Authorization:Basic $(echo -n api-user:EtKWah5ALFQhwPAf7YFx3grY57YQz6kXLh4579MZ | openssl base64)" Authorization:Basic YXBpLXVzZXI6RXRLV2FoNUFMRlFod1BBZjdZRngzZ3JZNTdZUXo2a1hMaDQ1NzlN Wg==~ $
Authorizationヘッダの部分をechoしてみると、出力が改行されている...なぜだ...絶対ここが原因だ...
そこでopensslの改行について調べてみると、似たような症状の人もいた。
opensslでBASE64エンコードされた文字列をdecryptしようとしたら769bytes以上になるとエラーになる件 - だいたいよくわからないブログ
なるほどねー、文字数で改行が行われるわけね。
試しにエンコードする文字列を短くしたら、改行はされなくなった。
原因は分かったので、最初のコマンドに-Aオプションをつけてみる。
~ $ echo -n "Authorization:Basic $(echo -n api-user:EtKWah5ALFQhwPAf7YFx3grY57YQz6kXLh4579MZ | openssl base64 -A)" Authorization:Basic YXBpLXVzZXI6RXRLV2FoNUFMRlFod1BBZjdZRngzZ3JZNTdZUXo2a1hMaDQ1NzlNWg==~ $
改行されなくなり、リクエストも正常に処理された。
ちなみに、Macに入っているopensslのマニュアルにも書いてあった、
-A If the -a option is set, then base64 process the data on one line. -a, -base64 Base64 process the data. This means that if encryption is taking place, the data is base64-encoded after encryption. If decryp- tion is set, the input data is base64-decoded before being decrypted.
解決までだいぶ遠回りした気がする...
ちなみに上でも書いたこのコマンド。
curl -k -http1.1 "https://sample-api/v1/users" -X POST -d "id=XXX" -H "Authorization:Basic $(echo -n user:EtKWah5ALFQhwPAf7YFx3grY57YQz6kXLh4579MZ | openssl base64)"
でリクエストが通ったのは謎。
参考
【AWS】DynamoDBの料金(超ざっくり)
初めてDynamoDBを運用することになりそうなので、プロビジョンドモードとオンデマンドモードの料金体系の違いを超ざっくりメモ。
プロビジョンドモード
1秒あたりの書き込みキャパシティーユニット(WCU)と読み込みキャパシティーユニット(RCU)を指定する。
料金は1時間あたりのWCU/RCUの設定値で決まる。(実際の読み込み、書き込みの回数は関係しない)
書き込みキャパシティーユニット (WCU) --> 0.000742USD/WCU
読み込みキャパシティーユニット (RCU) --> 0.0001484USD/RCU
「書き込み、読み込みのサイズがXXバイトまでなら、WCU/RCUを1消費する」みたいなイメージ。
例えば、WCU/RCUが5の場合、1秒間にXXバイト以下の処理を5回実行できる。
正確には処理の特徴(結果整合性やトランザクション)の設定で閾値は変わってくる。詳細は公式サイトで。
プロビジョニング済みキャパシティーの料金 - Amazon DynamoDB | AWS
オートスケールの設定もできる。
オンデマンドモード
WCUやRCUの設定の必要はなく、読み込みリクエスト単位で課金される。
書き込みリクエスト単位 --> 書き込みリクエスト 100 万単位あたり 1.4269USD
読み込みリクエスト単位 --> 読み込みリクエスト 100 万単位あたり 0.285USD
「書き込み、読み込みのリクエストがXXバイトまでなら、読み書きリクエストが1単位必要」みたいなイメージ。
こちらも処理の特徴(結果整合性やトランザクション)の設定で閾値は変わってくる。詳細は公式サイトで。
オンデマンドキャパシティーの料金 - Amazon DynamoDB | AWS
どっちのモードがいいかは悩みどころ。
とりあえず今の用途としては、Read/Writeの回数もそこまで多くはないし、オンデマンドでいいかなって感じ。
WCU/RCUの検討やオートスケールの検討をしなくてもいいっていうのも大きい。
参考
DynamoDBのオンデマンドとプロビジョニングの料金を比較をしてみた #reinvent | Developers.IO
【DB】あえて正規化しない設計の学び
新年ですな。
今新規でAPIの設計を進めているが、データ設計で学びがあったのでメモ。
API仕様(超ざっくり)
前提
API利用者の情報を持つ「認証情報テーブル」と、
申し込み情報を持つ「申し込み情報テーブル」がある。リクエストパラメータ
?Id=XXX&Key=YYY&applicationId=ZZZ
※実際はIdとKeyは暗号化するけど細かいことは置いておく。処理概要
1. 認証情報テーブルのIdとKeyでAPI利用者認証。
2. 申し込み情報テーブルから、リクエストパラメータのapplicationIdが一致するカラムの情報を取得。
3. レスポンスパラメータは利用者ごとに異なるため、その利用者に沿った項目を返却。申し込み情報テーブル
applicationId | Name | Address | PhoneNo | Status | |
---|---|---|---|---|---|
1 | tanaka | tanaka@mail.com | hoge町 | 090-XXXX-XXXX | 1 |
2 | yamane | yamane@mail.com | piyo町 | 080-XXXX-XXXX | 2 |
- 課題
要件通り、API利用者ごとにレスポンスを変える必要がある。
認証情報テーブルの設計をどうするかが問題。
当初案
申し込み情報テーブルと同じカラムを作っておいて、返却する・しないのフラグを立てる。
この場合、利用者がhogeの場合は全項目を返却する。
piyoの場合は、Name,Email,Statusを返却する。
Id | Key | Name | Address | PhoneNo | Status | |
---|---|---|---|---|---|---|
hoge | key1 | 1 | 1 | 1 | 1 | 1 |
piyo | key2 | 1 | 1 | 0 | 0 | 1 |
メリット:テーブルを見ただけでどの項目を返却するかがわかりやすい。
デメリット:カラム数が多いと見づらい。返却項目が増えた場合に、いちいちALTER TABLEしないといけない。
最終案
Responsesカラムに返却項目をカンマ区切りでつなげる。
Id | Key | Responses |
---|---|---|
hoge | key1 | Name,Email,Address,PhoneNo,Status |
piyo | key2 | Name,Email,Address |
メリット: 返却項目が増えてもカラム追加する必要がない。
デメリット:INSERTするときのチェックなどがめんどう。プログラム側でResponsesカラムのパースなりが必要。
所感
自分としては上記の当初案もしくは、ResponsesIdみたいなカラムを作成しそのIDに応じて返却する項目情報を別のファイルとかに書き出すとかでもいいのかな〜みたいに思っていたが、今回のケースでは利用のされ方や、Responses登録時の運用手順を想像した上で、最終案がいいのではというアドバイスをいただいた。
本来は正規化するべきなので、安易に最終案の形にするのはバッドだと思うが、場合によってはありなんだな〜と思った。