1
3

サーバーレスアーキテクチャで翻訳 Web API を構築

Last updated at Posted at 2024-04-06

本記事の概要

AWS Lambda、Amazon API Gateway、Amazon DynamoDB のうち、DynamoDB及びAWS Lambdaについて概要を理解したい思いから記事にしました。

参考サイト:

作成する構成

image.png
API Gatewayがリクエストを受け付け、Lambdaを呼出し、Lambdaが翻訳機能であるTranslateを呼出してAPI GatewayからResponseを返します。最後にヒストリーテーブルとしてDynamoDBに保存します。

ハンズオン開始

まずLambdaについて公式のコメントを確認しましょう。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html

AWS Lambda はサーバーのプロビジョニングや管理をする必要がなく、コードを実行できるコンピューティングサービスです。

私なりのまとめですが、ミドルウェアの準備なく、小規模システムをコードで実現できるもの。と理解して進めます。

Lambdaを単体で使用してみる。

Lambda Function作成(デフォルトから設定を変更したりします。)
image.png
「関数の作成」から、「設計図の使用」を選択すると、設計図(ブループリント)を利用することができる。S3を呼び出す際にPythonを使用する等の指定ができる。

  1. 「一から作成」を選択
  2. 関数名「translate-function(任意)」
  3. ランタイムは「Python」を選択
  4. デフォルトの実行ロールは、「基本的な Lambda アクセス権限で新しいロールを作成」を選択
  5. オプションを参考に貼ります。
    image.png
  6. このまま関数を作成します。

現時点で呼出可能なサービスが表示されます。
image.png
送信先を追加から、送信先の設定を確認してみます。
image.png
同じようにトリガーを確認してみます。
image.png
多様なサービスとの連携可能なことが確認できます。

  1. 設定タブからメモリを変更し、256MBに設定。
    image.png
    image.png
    ここにあるEphemeralStorageとは、私の解釈ですがRAMに似た機能であり、無料でキャッシュを一時的に保存してくれます。これはインスタンスが動作している間のみ機能する。S3やEBSに保存することも可能。Linux システムの /tmpディレクトリと目的は同じようです。

  2. タイムアウトをデフォルトの3秒から10秒に変更してみます。

  3. 「保存」をクリックし、「テスト」実行を行います。
    image.png
    イベント名を「test1(任意)」と名付けています。
    テンプレートのオプション設定や、共有が可能です。
    image.png
    このまま「テスト」をクリックします。
    Helloが表記されており、問題なく実行できたようです。
    ステータスコード:200です。
    image.png
    image.png

CloudWatch logsにてログを確認することができます。
image.png

  1. ソースコードを編集していきます。
    Pythonを使用している場合のドキュメントを確認していきます。
    Python の AWS Lambda 関数ログ作成:
    https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-logging.html
    loggingモジュールを使用して詳しくログを確認していきます。
import logging
logger = logging.getLogger()
logger.setLevel("INFO")

loggerをインポートしてlogger変数にINFOレベルのログを格納します。
ここでちょこっとCCNAの復習です。
CRITICAL
ERROR
WARNING
INFO(今回はココです。)
DEBUG
赤枠にペーストします。
image.png
続けて、loggingモジュールを実行する上で不足がありますので、次のコードを関数に追加します。

logger.info(event)

赤枠の箇所です。
image.png
5. 「test」をクリックします。
するとFunction logsが出ています。
image.png

Lambdaを動かす前にIAMロールにポリシーを追加する。

Traslateへのアクセスを可能にするため、IAMを設定していきます。
「設定」タブから「アクセス権限」をクリックします。
そして、「ロール名」をクリックしてIAMに遷移します。
image.png

「許可を追加」をクリックし、「ポリシーをアタッチ」します。
image.png
検索窓から「Translate」と検索し、「TranslateReadOnly」を選択します。
これで許可を追加完了です。

LambdaでAmazon Translateを呼出していく。

ここのサイトには、Lambdaで使用できるPythonコードがサービス毎に綺麗に分けられています。
「traslate」と検索窓に入力して検索します。このサイトを開いたまま、次に進みます。
前準備として
1.loggingに関するコードを消します。
image.png

2.リソースを操作するためのライブラリを追加します。

import boto3

client = boto3.client('translate')

3.更に、次の構文を追加して、日本語から英語へ翻訳する。といった構文を完成させます。

response = client.translate_text(
    Text='おやすみなさいませご主人様',
    
    #ここで特定の言語グループを指定することができます。
    #TerminologyNames=[
        #'string',
    #],
    
    #日本語から、英語へ翻訳します。
    SourceLanguageCode='ja',
    TargetLanguageCode='en',
    
    #ここで言葉遣い、不適切な表現、端的に翻訳するか否かを決めることができます。
    Settings={
        'Formality': 'INFORMAL',
        'Profanity': 'MASK',
        'Brevity': 'ON'
    }
)

4.出力させるために以下のようにjson形式コードを編集します。

   output = response.get('TranslatedText')
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps({
            'output' = output
        })
    }

全体のコードを記載しておきます!(パターン1)

import json
import boto3

client = boto3.client('translate')

def lambda_handler(event, context):
    
    response = client.translate_text(
        Text='おやすみなさいませご主人様',
        SourceLanguageCode='ja',
        TargetLanguageCode='en',
        Settings={
            'Formality': 'INFORMAL',
            'Profanity': 'MASK',
            'Brevity': 'ON'
        }
    )
    
    output = response.get('TranslatedText')
    
    return {
        'statusCode': 200,
        'body': json.dumps({
            'output': output
        })
    }

眠いのです(-_-;)
それではテストを実行していきます。
ソースコードの項目に貼り付けたら、Ctrl+Sで上書き保存します。
その後、「Deploy」をクリックした後、「test」をクリックします。
出力結果

Test Event Name
(unsaved) test event

Response
{
  "statusCode": 200,
  "body": "{\"output\": \"Good night, master\"}"
}

Function Logs
START RequestId: 4e91ce47-19c8-439a-9a88-8e06fa3a35e7 Version: $LATEST
END RequestId: 4e91ce47-19c8-439a-9a88-8e06fa3a35e7
REPORT RequestId: 4e91ce47-19c8-439a-9a88-8e06fa3a35e7	Duration: 646.73 ms	Billed Duration: 647 ms	Memory Size: 128 MB	Max Memory Used: 76 MB	Init Duration: 508.21 ms

Request ID
4e91ce47-19c8-439a-9a88-8e06fa3a35e7

グッドナイトマスターって(´▽`)

パターン2(公式さん作成のデフォルトになります。)

import json
import boto3

translate = boto3.client('translate')

def lambda_handler(event, context):

    input_text = 'おはよう'

    response = translate.translate_text(
        Text=input_text,
        SourceLanguageCode='ja',
        TargetLanguageCode='en'
    )

    output_text = response.get('TranslatedText')

    return {
        'statusCode': 200,
        'body': json.dumps({
            'output_text': output_text
            })
    }

出力結果

Test Event Name
(unsaved) test event

Response
{
  "statusCode": 200,
  "body": "{\"output_text\": \"Good morning\"}"
}

Function Logs
START RequestId: ac45a998-e6bf-477a-bcf7-d69accd177c1 Version: $LATEST
END RequestId: ac45a998-e6bf-477a-bcf7-d69accd177c1
REPORT RequestId: ac45a998-e6bf-477a-bcf7-d69accd177c1	Duration: 617.51 ms	Billed Duration: 618 ms	Memory Size: 128 MB	Max Memory Used: 76 MB	Init Duration: 428.80 ms

Request ID
ac45a998-e6bf-477a-bcf7-d69accd177c1

真剣にハンズオンを続けます。

API

これはAPIというと天気予報APIなどを想像する方もいると思います。アプリからAPIに接続して天気予報を表示させることができる一種のアプリからアプリを呼び出すイメージのものと理解しちゃって問題ないと思います。

一方、Amazon API Gatewayは、API+αの機能を有したエンドポイントと理解しちゃいましょう。裏にはLambdaなどが控えており、スケールできる高機能なエンドポイントがあるイメージです。

+αの部分は調べた方が良いかと思います。
https://aws.amazon.com/jp/api-gateway/
ひとまず手を動かします。

APIの画面に移動します。
image.png
image.png
構築をクリックします。
translate-api(任意)
image.png
エンドポイントタイプは、プライベートでもなく、世界からの接続を要している訳ではないので、「リージョン」を選択します。
image.png

+αの部分になりますね。サーバーではなくAmazon API Gateway自身に仕事をさせます。

「リソースを作成」をクリックします。
image.png
リソース名のみsample(任意)と入力して作成します。
image.png
画面下の「メソッドを作成」をクリックします。
image.png
「GET」を選択し、「Mock」を選択します。これで、API自身の動作(XMLからJSONに変換するなど。)を決めることができます。
image.png
ここでちょっと復習です。ここで使用するのはGET(取得)ですね。

CRUD 処理内容 HTTPメソッド
Create データの作成 POST
Read   データの取得 GET
Update データの更新 PUT
Delete データの削除 DELETE

少し参考に設定画面を貼ります。ここで、APIキーを設定することができます。
image.png
クエリも設定できるようです。
image.png
image.png
このままデフォルトで「作成」をクリックします。

そして、スクロールし、「統合レスポンス」タブを選択し、「編集」をクリックします。
image.png
ここで細かく設定できますね。ここは今回スルーします。
image.png
マッピングテンプレートに次のjsonコードをペーストします。
設定する中で様々なレスポンスのテンプレが存在しますね!APIを使いたい用途が異なるので無視します。

{
    "statusCode": 200,
    "body": {
        {
            "report_id": 5,
            "report_title" : "Hello, world"
        },
        {
            "report_id": 7,
            "report_title" : "Good morning!"
        }
    }
}

image.png
「テスト」タブからデフォルトの設定で、テストを実行します。
image.png
するとAPIがhello worldをステータス200で返してますね。
image.png
画面上の「APIをデプロイ」をクリックします。
image.png
「新しいステージ」を選択し、「development(任意)」という名前を付けてデプロイをクリックします。
image.png
ステージ内(development)のGETをクリックし、「URL を呼び出す」にあるURLをブラウザに貼り付けて、APIが機能しているか確認することができます。
image.png
すると次のように表示されます。
image.png

Amazon API GatewayとLambdaの連携

先に先程作成したリソースとメソッドを削除します。
image.png
リソースの削除
image.png
新たにリソースを作成します。
「translate(任意)」
image.png
画面下の「メソッドを作成」をクリックし、再び「GET」を指定します。
image.png
次は、Mock(APIも仕事をする設定)ではなく、Lambda(APIは素通りさせることを想定)を選択します。
image.png
「Lambdaプロキシ統合」にチェックを入れて、先程作成した関数を指定し、メソッドを作成します。
image.png
続いて、スクロールして「メソッドリクエスト」タブから「編集」をクリックします。
image.png
クエリ文字列を追加をクリックします。
image.png
text(任意)と入力して、クエリを必須に設定して、保存します。
image.png

続いて、Lambdaの画面に移動し、コードをAPIに適用するため、エンコードとヘッダーについて設定を追加します。人が読める状態のコードなので、エンコードはFalseです。また、headersも特に設定しません。
公式サイト(APIに接続するためのあれこれ):
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-integration-settings-integration-response.html

完成コード

import json
import boto3

client = boto3.client('translate')

def lambda_handler(event, context):
    
    response = client.translate_text(
        Text=event['queryStringParameters']['Text'],
        SourceLanguageCode='ja',
        TargetLanguageCode='en',
        Settings={
            'Formality': 'INFORMAL',
            'Profanity': 'MASK',
            'Brevity': 'ON'
        }
    )
    
    output = response.get('TranslatedText')
    
    return {
        'statusCode': 200,
        'body': json.dumps({
            'output': output
        }),
        'isBase64Encoded': False,
        'headers': {}
    }

次に、テストのタブから、新しいイベントを作成します。
名前はtest01(任意)です。「Amazon API Gateway proxy」を選択します。
image.png
すると、コードがずらっと出てきますが、次の箇所に手を加えます。
該当箇所にコピペして使用されてください。
なお、このコードは、すぐ上に記載しているコードの「Text」を呼出しますので、上記コードを使用する場合は、このままの下記コードをイベントjsonの該当箇所に貼り付けましょう。

"queryStringParameters": {
    "Text": "こんばんわ"

貼り付けたら、「保存」をクリックします。
ソースコードタブに移動し、上記完成コードを貼り付けて、Ctrl+Sで保存し、デプロイをクリックし、テストを実行します。
image.png

実行結果

Test Event Name
test01

Response
{
  "statusCode": 200,
  "body": "{\"output\": \"Good evening\"}",
  "isBase64Encoded": false,
  "headers": {}
}

Function Logs
START RequestId: a41cfab9-0a88-455e-ae28-af5a9ccfb59e Version: $LATEST
END RequestId: a41cfab9-0a88-455e-ae28-af5a9ccfb59e
REPORT RequestId: a41cfab9-0a88-455e-ae28-af5a9ccfb59e	Duration: 668.80 ms	Billed Duration: 669 ms	Memory Size: 128 MB	Max Memory Used: 77 MB	Init Duration: 450.30 ms

Request ID
a41cfab9-0a88-455e-ae28-af5a9ccfb59e

次にAPIの画面に戻ります。
「APIをデプロイ」から、先程作成した「development」を選択し、デプロイをクリックします。
image.png
先程と同じように、「URLを呼び出す」の箇所のURLをブラウザに貼り付けると次のようなエラーが出ます。
image.png
ブラウザのURL欄の末尾に「?Text=おはようございますご主人様」と手打ちします。
image.png

DynamoDB

やっとの思いで作成できます。DynamoDB
key,value型を体験できるのですね。
現在の構成
image.png
image.png
テーブルの作成をクリックし、テーブル名translate-history(任意)と入力し、パーティションキーにtimestampと入力します。型は文字列のままで大丈夫です。
つまり、レコードに時間を文字列で追加(今回は手動)します。ということ。
image.png
キャパシティユニット(同時処理可能なキャパ)は、デフォルトを解除して、設定します。
image.png
image.png
結果整合性は参照した時に、誤差がある状態で返される可能性があります。読み取りの正確性が必要がない時に設定するものです。
このまま設定を継続します。
image.png
読み取り書き込みのキャパを事前に設定するかしないかの設定を行います。今回は事前に設定していきます。
image.png
image.png
image.png
セカンダリインデックスも設定しないのでこのまま進みます。
複合キーのようなもの。
image.png
image.png
このままテーブルを作成します。

項目(フィールド)を追加していきます。レコードを手動追加してみましょう。
image.png
フィールドと値が手動によって追加されました。
image.png

AWS Lambda、Amazon API Gateway、Amazon DynamoDBを組み合わせて構成します。

Lambda最終形態

まず、ロールにDynamoDBを追加して接続できるようにします。
設定タブからアクセス権限、ロール名をクリックします。
許可を追加をクリックし、PutItemとUpdateItem権限を追加したいのですが、デフォルトのポリシーに丁度良いのがないので、ひとまずFullAccessでいきます。
image.png

DynamoDBを使います。
公式リファレンス:
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/index.html

import json
import boto3
import datetime

client = boto3.client('translate')
dynamodb = boto3.resource('dynamodb').Table('translate-history')

def lambda_handler(event, context):
    
    response = client.translate_text(
        Text=event['queryStringParameters']['Text'],
        SourceLanguageCode='ja',
        TargetLanguageCode='en',
        Settings={
            'Formality': 'INFORMAL',
            'Profanity': 'MASK',
            'Brevity': 'ON'
        }
    )
    
    output = response.get('TranslatedText')
    
    # テーブルへの書き込みには put_item メソッドを使用する
    dynamodb.put_item(
        Item={
            'timestamp': datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
            'Text': event['queryStringParameters']['Text'],
            'output': output
        }
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps({
            'output': output
        }),
        'isBase64Encoded': False,
        'headers': {}
    }

コードを編集しましたので、Ctrl+Sで保存し、デプロイをクリック、testをクリックしていきます。

出力結果

Test Event Name
test01

Response
{
  "statusCode": 200,
  "body": "{\"output\": \"Good evening\"}",
  "isBase64Encoded": false,
  "headers": {}
}

Function Logs
START RequestId: 6ac13179-a0ef-44cd-b95a-ad66be6ae701 Version: $LATEST
END RequestId: 6ac13179-a0ef-44cd-b95a-ad66be6ae701
REPORT RequestId: 6ac13179-a0ef-44cd-b95a-ad66be6ae701	Duration: 1075.79 ms	Billed Duration: 1076 ms	Memory Size: 128 MB	Max Memory Used: 79 MB	Init Duration: 470.03 ms

Request ID
6ac13179-a0ef-44cd-b95a-ad66be6ae701

DynamoDBを確認してきます。
レコードが無事追加されています。
image.png

DynamoDBに対する私見

  1. 少しだけ使ってみてのDynamoDBの私見ですが、使いにくさをあまり感じさせませんが、設定中の翻訳なのかそもそもの機能が違うのか解釈が不足いているものの、「通常のデータベースで使用する言葉」にしたら使いやすい気がしました。
  2. テーブル設計をする必要のないKeyValueの関係であるものは扱えると理解しました。簡単に言うと、「1はa」です。といった公式のデータを大量かつリアルタイムに残したい時に使う。と理解しました。RDBと同じに考えたらいけないですね。データ型も制限があります。
  3. 具体的な使用方法は次のサイトから確認しましょう。
    https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/best-practices.html

リソースの削除

DynamoDB
項目を削除します。
image.png
テーブルを削除します。
image.png

API Gateway
ステージを削除します。
image.png
メソッドを削除します。
image.png
リソースを削除します。
image.png

Lambda
関数を削除します。
image.png

CloudWatch
ロググループを削除します。
image.png

勉強になりました!
では!

1
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3