AWS REST APIを公開して使用量プランを設定する
chaliceを使ってREST APIを公開する手順とAWS GatewayでAPIキーを使って使用量プランを設定する方法について記述します。
1 開発環境の準備
1-1 開発用AWSアカウントの作成
すでにアカウントが準備されている場合は飛ばしてください。
- IAMで開発用アカウントを作成しアクセスキーを作成する。
/IAM/ユーザー/user_name/認証情報 - 自分のローカル端末にAWSの認証情報を設定する。
~/.aws/credentials
[default]
aws_access_key_id = AKI...
aws_secret_access_key = pwv...
~/.aws/config
[default]
region=ap-northeast-1
1-2 chaliceのインストール
venvを使ってchalice用の環境を構築する。 chaliceを作成してプロジェクトフォルダを配下に配置すると管理が簡単です。
mkdir chalice && cd chalice
python3 -m venv env
. env/bin/activate
pip install chalice
chalice --version
# chalice 1.24.2, python 3.8.2, darwin 20.5.0
2 Hellow Worldを動かす
2-1 プロジェクトの新規作成
chalice new-project プロジェクト名でプロジェクトが作成されます。
chalice new-project helloworld
プロジェクトフォルダの中身を確認します。
cd hellowold
ls -al
# drwxr-xr-x .chalice
# -rw-r--r-- app.py
# -rw-r--r-- requirements.txt
2-2 app.py(アプリケーション本体)の中身
app.pyにAPIのルーティング情報とメソッドの処理を記載します。
エディタで開くとすでにhello worldが生成されているのが確認できると思います。
from chalice import Chalice
app = Chalice(app_name='helloworld')
@app.route('/')
def index():
return {'hello': 'world'}
2-3 ローカルでのテスト
local オプションで、ローカルマシンでテストが可能です。
$chalice local
Serving on http://127.0.0.1:8000
2-4 AWS上にdeployする
当たり前ですが、deployするたびに各種設定は上書きされます。毎回設定するのは手間なので.chalice/config.jsonで設定します。(後述)
$chalice deploy
Creating deployment package.
Creating IAM role: helloworld-dev
Creating lambda function: helloworld-dev
Creating Rest API
Resources deployed:
- Lambda ARN: arn:aws:lambda:ap-northeast-1:************:function:helloworld-dev
- Rest API URL: https://*********.execute-api.ap-northeast-1.amazonaws.com/api/
エラーが出る場合は、認証情報やリージョン情報を確認してください。
AWSコンソールでデプロイされた内容を確認する
AWSコンソールにログインして、リージョンをデプロイしたリージョンに変更します。
API Gatewayを開きます。
helloworldができていることを確認します。
Lambda関数ができていることを確認します。
Lambda>関数>helloworld-dev
URLの再確認方法
$chalice url
https://********.execute-api.ap-northeast-1.amazonaws.com/api/
2-5 deployした内容を削除する
AWS上の追加された項目が綺麗さっぱりなくなります。
API Gateway
Lambda 関数
$chalice delete
2-6 外部モジュールを追加する
requirement.txtに必要なモジュールを追加します。
pipreqsを使うと簡単に生成できます。
pip install pipreqs
pipreqs --force .
3 APIキーと使用量プランによるアクセス制御
APIキーとと使用量プランプランのセットでアクセスを制御することができます。
手順を示します。
- app.pyのルーティング情報にapi_key_required=Trueを記載する
- 使用量プランを作成しAPIキーに適用する
3-1 app.pyのルーティング情報にapi_key_required=Trueを記載する
from chalice import Chalice
app = Chalice(app_name='helloworld')
@app.route('/', api_key_required=True)
def index():
return {'hello': 'world'}
この状態でAPIにアクセスするとForbiddenが返って来ることが確認できると思います。
{"message":"Forbidden"}
3-2 使用量プランを作成しAPIキーに適用する
使用量プランを作成
AWS ConsoleでAPI Gateway/helloworld/使用量プラン
スロットリングとクォータについては、AWSの説明を確認してください。
両方を無効にすると無制限にアクセスできます。
使用量プランにステージを追加する
APIにhelloworld、ステージにapiを選択してチェックマークをクリックします。
メソッド毎に細かく設置も可能です。
APIキーを作成して使用量プランに追加
APIキーはここで追加、作成することができます。(メニューのAPIキーからも可能)
今回は、”テスト”を作成します。
注意:PIキーは使用量プランを適用するまで無効です。
使用量プランの確認
使用量プランを確認するとAPIキーにテストと表示されていると思います。
API毎の使用量や延長の処理もここで行えます。これで準備完了です。
APIキーを使ってアクセスする
APIキーはAPIキーを選択して、表示をクリックすると確認できます。
ヘッダーにx-api-keyを追加します。
{
"x-api-key":"gjL................"
}
cURLの例
curl 'https://*******.execute-api.ap-northeast-1.amazonaws.com/api/' \
-H 'x-api-key: gjL................'
{"hello":"world"}
以上で基本の流れは完了です
4 TIPS
lamdbaや権限周りをほぼ自動でやってくれるのでとても便利ですが、ハマると思われる箇所もありましたので列記します。
4-1 日本語が文字化け(unicode-escape)される
helloworldを日本語にする。
@app.route('/')
def index():
return {'こんにちは': '世界'}
レスポンス本体
{"\u3053\u3093\u306b\u3061\u306f":"\u4e16\u754c"}
実際はunicode-escapeされているだけなのでdecodeすれば良いです
res = requets.get('apiのurl')
print(res.content.decode('unicode-escape'))
# {'こんにちは': '世界'}
response bodyをUTF-8にしたい場合は、json.dumpsでensure_ascii=Falseを指定します。
import json
@app.route('/')
def index():
return json.dumps({'こんにちは': '世界'}, ensure_ascii=False)
# {"こんにちは": "世界"}
4-2 自作のモジュールが呼び出せない(import error)
外部モジュールはpipでインストールすることで自動で配置されます。
自作モジュールは、chalicelibに配置する必要があります。その他の自作ファイルもここに配置します。
from chalicelib.my_module import myClass
gitでサブモジュールを追加する際はchalice配下に入れます。
mkdir chalicelib
cd chalicelib
git submodule add 〜
4-3 deployできるのは外部モジュールを含めて50MBまで
chaliceは、外部モジュールも自動で配置してくれますが、deployできるのは50MBまでという制限があります。サイズの大きいファイルはS3から読み込むか、zip等で圧縮して配置することになると思います。
解決策1 zipfileを使う例
zipファイルを解凍すると通常はファイルができますが、メモリ上で解凍します。
ファイル名をキーとしたデータを格納する関数例:
import zipfile
def read_zipdata(zip_file_path):
'''zipファイルからデータを取得する
'''
files = {}
with zipfile.ZipFile(zip_file_path, 'r') as zipdata:
for info in zipdata.infolist():
if info.is_dir():
continue
files[info.filename] = zipdata.read(info.filename)
return files
解決策2 AWS S3を使う例
前準備として、S3でBucketを作成しておく必要があります。
ロールに必要なS3のアクセス権限は、chaliceが自動で生成してくれます。
boto3のインストール
pip install boto3
import boto3
from chalice import Chalice
app = Chalice(app_name='helloworld')
app.debug = True
AWS_S3_BUCKET_NAME = 'interman.hoge'
# boto3.resource('s3')では、ポリシーは自動生成されないもよう 2021.09現在
s3 = boto3.client('s3')
@app.route('/')
def index():
s3.put_object(Bucket=AWS_S3_BUCKET_NAME, Key='helloworld.json', Body=b'{"Hellow", "World"}')
res = s3.get_object(Bucket=AWS_S3_BUCKET_NAME, Key='helloworld.json')['Body'].read()
return res
4-4 メモリが足りない
lambdaのメモリは128MBがデフォルトになっています。
設定ファイル編集します。設定ファイルの詳細は下記を参照してください。
メモリを2048MBにする例
{
"version": "2.0",
"app_name": "address-normalize",
"stages": {
"dev": {
"api_gateway_stage": "api",
"lambda_memory_size":2048
}
}
}
Discussion