【AWS认证备考】零成本在线实践学习AWS入门(API Gateway, Lambda, DynamoDB)【适合初学者】
章节索引
-
- どんな記事?
-
- 何を作るのか
-
- 環境
-
- LocalStack環境を保存する方法
LocalStackを起動してみよう!
AWS CLIでLambdaが実行できるか確認する
AWS CLI Localをインストールする
サーバーレスアーキテクチャで翻訳Web APIを構築する
みんなの自動翻訳API呼び出すLambdaを作成する(Node.js)
Nodeをインストールする
Lambda関数の作成、デプロイしてテスト
API Gatewayの構築とLambdaとの連携
DynamoDBの構築
LambdaとDynamoDBを連携
API Gateway, Lambda, 翻訳API, DynamoDBが連携されているか確認する
終わり
什么样的文章?
在云计算领域,亚马逊网络服务(AWS)被广泛使用。然而,为了获得与AWS相关的资质,通常需要一些教育材料和培训的费用。对于初学者来说,这样的费用可能成为障碍。
在成为工程师之前,我私下进行了AWS的实践学习,但我担心会不会不小心产生费用(我在某个学习网站的AWS实践课程问答页面上看到一个问题,问:“我产生了20万的费用,我该怎么办?”让我感到很震惊)。
因此,本文将重点介绍初学者可以免费便捷地学习AWS认证考试的实践学习方法。
我们将使用一种名为LocalStack的工具,在本地环境中模拟AWS服务,来进行实践。通过模拟AWS服务,LocalStack可以进行与实际的AWS环境相类似的操作和测试。这篇文章详细介绍了LocalStack 2.0的发布和功能改进。
希望能为初学者们在AWS认证备考上提供帮助,助他们提升技能!
你要做什么?
我知道CLI的操作可能会让人感到困难,但是我会尽量在解释的过程中使用图解来帮助理解!
环境
我使用的开发环境是WSL2 Ubuntu 20.04 LTS。由于有关WSL1无法启动Docker的报道,我建议将其升级至WSL2。(因为我最初尝试在WSL1上启动,遇到了相当大的困难)
在这篇文章中(关于在WSL上运行Docker时遇到的问题),似乎即使是在WSL1也可以运行。。。
以下是环境的详细信息。
-
- Docker 23.0.4:Docker Desktop for Windowsは使わずUbuntuに直接インストールしています。
-
- docker-compose v1.29.2
-
- Python 3.8.10
-
- pip 23.1.1
- Visual Studio Code
请注意:文章撰写时所提到的环境版本可能与最新的LocalStack使用条件不同,请及时查阅最新的LocalStack安装页面以获取准确信息。
保存LocalStack环境的方式
由于LocalStack在容器中运行,因此当停止容器时,所有数据都会消失。因此,LocalStack V2引入了Community Cloud Pods功能,可以使用它来进行快照。请务必使用该功能。参考:LocalStack Community Cloud Pods
操作步骤
-
- 本地堆栈 CLI 安装
-
- 参考:本地堆栈 CLI 安装
使用 localstack pod save file://<保存路径, 文件名> 命令保存快照
使用 localstack pod load file://<保存路径, 文件名> 命令加载快照
现在让我们启动LocalStack吧!
首先,我們將安裝並嘗試啟動LocalStack。雖然有很多安裝方法可選,但這次我們將使用docker-compose來啟動。
另外,我们还可以在启动LocalStack后使用localhost:4566/health来检查各种服务的状态。
# 作業用ディレクトリ作成後に作業ディレクトリに移動
Ubuntu@dev01:~$ mkdir ./workplace && cd $_
# GithubからLocalStackをクローン
Ubuntu@dev01:~/workplace$ git clone https://github.com/localstack/localstack.git
Ubuntu@dev01:~/workplace$ ls
localstack
Ubuntu@dev01:~/workplace$ cd localstack/
# docker composeでLocalStack起動
Ubuntu@dev01:~/workplace/localstack$ sudo docker-compose up -d
Ubuntu@dev01:~/workplace/localstack$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
843a54664031 localstack/localstack "docker-entrypoint.sh" 3 minutes ago Up 3 minutes (healthy) 127.0.0.1:4510-4559->4510-4559/tcp, 127.0.0.1:4566->4566/tcp, 5678/tcp localstack_main
# jsonを整形するツールjqインストール
Ubuntu@dev01:~/workplace/localstack$ sudo apt install jq
# LocalStackの利用可能なサービス一覧を確認する
Ubuntu@dev01:~/workplace/localstack$ curl localhost:4566/health | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 876 100 876 0 0 171k 0 --:--:-- --:--:-- --:--:-- 171k
{
"services": {
"acm": "available",
"apigateway": "available",
"cloudformation": "available",
"cloudwatch": "available",
"config": "available",
"dynamodb": "available",
"dynamodbstreams": "available",
"ec2": "available",
"es": "available",
"events": "available",
"firehose": "available",
"iam": "available",
"kinesis": "available",
"kms": "available",
"lambda": "available",
"logs": "available",
"opensearch": "available",
"redshift": "available",
"resource-groups": "available",
"resourcegroupstaggingapi": "available",
"route53": "available",
"route53resolver": "available",
"s3": "available",
"s3control": "available",
"secretsmanager": "available",
"ses": "available",
"sns": "available",
"sqs": "available",
"ssm": "available",
"stepfunctions": "available",
"sts": "available",
"support": "available",
"swf": "available",
"transcribe": "available"
},
"version": "2.0.3.dev"
}
请使用AWS CLI来验证是否可以执行Lambda函数。
当使用AWS CLI时,由于根据选择的服务和区域自动确定端点URL,因此如果要使用LocalStack,需要使用–endpoint-url进行指定。
后来安装AWS CLI Local并使用awslcoal命令。如果使用awslocal命令,可以省略指定端点和配置文件,所以可以跳过安装AWS CLI。
# zip, unzipをインストール
Ubuntu@dev01:~$ sudo apt install zip unzip
# AWS CLIインストーラーをダウンロード
Ubuntu@dev01:~$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
Ubuntu@dev01:~$ unzip awscliv2.zip
# AWS CLIをインストール
Ubuntu@dev01:~$ sudo ./aws/install
# AWS Credentialsを設定
Ubuntu@dev01:~$ aws configure --profile=localstack
AWS Access Key ID [None]:None
AWS Secret Access Key [None]:None
Default region name [None]: ap-notheast-1
Default output format [None]: json
请按照这个页面的说明,创建Lambda函数文件并压缩为zip文件。
# Lambdaで実行するファイルを作成する作業ディレクトリ作成して、移動
Ubuntu@dev01:~$ mkdir -p ./workplace/demo/ && cd $_
# 関数を作成する
Ubuntu@dev01:~/workplace/demo$ vi index.js
exports.handler = async function(event, context) {
console.log("ENVIRONMENT VARIABLES\n" + JSON.stringify(process.env, null, 2))
console.log("EVENT\n" + JSON.stringify(event, null, 2))
return context.logStreamName
}
# 関数ファイルをデプロイパッケージにする
Ubuntu@dev01:~/workplace/demo$ zip function.zip index.js
接下来,使用lambda create-function命令将zip文件部署到Lambda。
有关lambda create-function命令的详细信息,请参阅此页面。
# 作成したデプロイパッケージを使ってLambda関数を作成する
Ubuntu@dev01:~/workplace/demo$ aws --endpoint-url=http://localhost:4566 lambda create-function --function-name demo-function --zip-file fileb://function.zip --handler index.handler --runtime nodejs18.x --role arn:aws:iam::123456789012:role/lambda-demo1 --profile=localstack
{
"FunctionName": "demo-function",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:demo-function",
"Runtime": "nodejs18.x",
"Role": "arn:aws:iam::123456789012:role/lambda-demo1",
"Handler": "index.handler",
"CodeSize": 325,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2023-04-23T05:57:03.340517+0000",
"CodeSha256": "c1MYsVaMfs4+EDf4IRHEVTtsD0X6CnwxI7OE2oqwN98=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "a330e7cb-045e-488d-ac15-fec69a4abac2",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
}
}
在LocalStack中,由于Lambda是在容器中管理的,所以在首次调用Lambda时可能需要花费时间来下载映像。
# Lambdaを実行する
Ubuntu@dev01:~/workplace/demo$ aws --endpoint-url=http://localhost:4566 lambda invoke --function-name demo-function outfile --profile localstack
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
安装AWS CLI Local
使用AWS CLI需要指定端点和配置文件,但使用LocalStack AWS CLI可以省略这些步骤,因此我们会进行安装。
LocalStack AWS CLI- Github
# pip経由でインストールするため、まずpipインストール
Ubuntu@dev01:~/workplace/localstack$ sudo apt install python3-pip
Ubuntu@dev01:~/workplace/localstack$ pip3 install awscli-local
# awslocalコマンドが使用可能か確認
Ubuntu@dev01:~/workplace/localstack$ awslocal --version
aws-cli/2.11.15 Python/3.11.3 Linux/5.15.90.1-microsoft-standard-WSL2 exe/x86_64.ubuntu.20 prompt/off
从现在开始,我们将使用AWS CLI Local(awslcoal)进行操作。
使用无服务器架构构建翻译Web API。
-
- 手順
翻訳API呼び出すLambdaを作成する。
みんなの自動翻訳API呼び出すLambdaを作成する(Node.js)
Nodeをインストールする
Lambda関数の作成、デプロイしてテスト
API Gatewayの構築
API Gatewayの構築
创建调用DeppL API的Lambda函数
由于信用卡注册出现频繁错误,我感到灰心丧气。
我进行了一番调查,发现大家的自动翻译工具似乎非常易于使用,所以我决定尝试一下。
创建一个调用大家的自动翻译API的Lambda函数(使用Node.js)
在Node.js中创建Lambda函数。如果想要在Lambda中使用外部模块如requests,就需要创建Lambda层。
哪怕是、、、
在LocalStack中,Lambda层是付费支持的,因此本次我们仅使用Node.js的标准模块进行创建!
参考:LocalStack Lambda层
安装Node
请根据这个页面(Node.js 在Linux的Windows子系统(WSL2)上安装)进行安装。
:::note warn
请注意,目前通过Ubuntu的apt-get命令安装的Node.js版本已经过期,请注意。
安装Node.js在Linux的Windows子系统(WSL2)上。
Ubuntu@dev01:~/workplace/localstack$ cd ../
Ubuntu@dev01:~/workplace/$ mkdir ./translate-function && cd $_
Ubuntu@dev01:~/workplace/translate-function$ mkdir nodejs && cd $_
Ubuntu@dev01:~/workplace/translate-function/nodejs$ curl -o- [https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh](https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh) | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 15916 100 15916 0 0 46133 0 --:--:-- --:--:-- --:--:-- 46267
=> Downloading nvm from git to '/home/Ubuntu/.nvm'
=> Cloning into '/home/Ubuntu/.nvm'...
remote: Enumerating objects: 359, done.
remote: Counting objects: 100% (359/359), done.
remote: Compressing objects: 100% (305/305), done.
remote: Total 359 (delta 40), reused 168 (delta 28), pack-reused 0
Receiving objects: 100% (359/359), 219.46 KiB | 2.44 MiB/s, done.
Resolving deltas: 100% (40/40), done.
- (HEAD detached at FETCH_HEAD)
master
=> Compressing and cleaning up git repository
=> Appending nvm source string to /home/Ubuntu/.bashrc
=> Appending bash_completion source string to /home/Ubuntu/.bashrc
/usr/bin/env: ‘bash\r’: No such file or directory
=> Close and reopen your terminal to start using nvm or run the following to use it now:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
Ubuntu@dev01:~/workplace/translate-function/nodejs$ export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
-s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ nvm install --lts
Installing latest LTS version.
Downloading and installing node v18.16.0...
Downloading https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-x64.tar.xz...
############################################################################################################# 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v18.16.0 (npm v9.5.1)
Creating default alias: default -> lts/* (-> v18.16.0)
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ node -v
v18.16.0
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ npm -v
9.5.1
创建Lambda函数并部署和测试。
通过将VScode与Ubuntu进行远程连接,可以顺利进行编码。
参考:在Windows上使用VS Code的Remote-WSL功能,访问WSL2中的Ubuntu 20.04。
# nodejs配下で作成していますが特に意図はありません
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ touch translate.js
const https = require("https");
const querystring = require("querystring");
const BASE_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp"; // 基底URL (https://xxx.jpまでを入力)
const API_KEY = "***"; // API key
const API_SECRET = "***"; // API secret
const LOGIN_ID = "***"; // ログインID
const API_NAME = "mt"; // API名 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"mt")
const API_PARAM = "generalNT_ja_en"; // API値 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"generalNT_ja_en")
const callTranslateApi = async (accessToken, input_text) => {
const postData = querystring.stringify({
access_token: accessToken,
key: API_KEY, // API Key
api_name: API_NAME,
api_param: API_PARAM,
name: LOGIN_ID, // ログインID
type: "json", // レスポンスタイプ
text: input_text, // 以下、APIごとのパラメータ
});
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": postData.length,
},
};
return new Promise((resolve, reject) => {
const req = https.request(BASE_URL + "/api/", options, (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
bodyJson = JSON.parse(body);
const output_text = bodyJson.resultset.result.text;
resolve(output_text);
});
});
req.on("error", (err) => {
reject(err);
});
req.write(postData);
req.end();
});
};
const getAccessToken = async () => {
const tokenData = querystring.stringify({
grant_type: "client_credentials",
client_id: API_KEY, // API Key
client_secret: API_SECRET, // API secret
urlAccessToken: BASE_URL + "/oauth2/token.php", // アクセストークン取得URI
});
const tokenOptions = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": tokenData.length,
},
};
return new Promise((resolve, reject) => {
const tokenReq = https.request(
BASE_URL + "/oauth2/token.php",
tokenOptions,
(res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
const accessToken = JSON.parse(body).access_token; // アクセストークン
if (!accessToken) {
console.error("Access token not found.");
reject("Error");
} else {
resolve(accessToken);
}
});
}
);
tokenReq.on("error", (err) => {
reject(err);
});
tokenReq.write(tokenData);
tokenReq.end();
});
};
exports.handler = async (event, context) => {
try {
const INPUT_TEXT = "おはようございます。調子はどうですか";
const accessToken = await getAccessToken();
const OUTPUT_TEXT = await callTranslateApi(accessToken, INPUT_TEXT);
console.log("INPUT_TEXT : " + INPUT_TEXT);
console.log("OUTPUT_TEXT : " + OUTPUT_TEXT);
const response = {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: "success",
OUTPUT_TEXT: OUTPUT_TEXT,
}),
};
return response;
} catch (error) {
console.log(error.stack);
}
};
将编写好的translate.js文件压缩成zip格式,然后部署到Lambda函数中。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ zip translate.zip translate.js
adding: translate.js (deflated 62%)
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda create-function --function-name translateFunction01 --runtime nodejs18.x --handler translate.handler --zip-file fileb://translate.zip --role arn::iam::000000000000:role/demo
{
"FunctionName": "translateFunction01",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction01",
"Runtime": "nodejs18.x",
"Role": "arn::iam::000000000000:role/demo",
"Handler": "translate.handler",
"CodeSize": 1516,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2023-05-14T01:40:23.596074+0000",
"CodeSha256": "/GgVaQ6xCUWytX6IzPI9QMXYDMVG5FrgdvebCwmwcEo=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "f43c33e0-bcf0-460b-bca1-9cbdcc33efa0",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
}
}
你可以使用lambda list-functions来确认部署的Lambda函数。参考:Lambda list-functions
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda list-functions
{
"Functions": [
{
"FunctionName": "translateFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
"Runtime": "nodejs18.x",
"Role": "arn::iam::000000000000:role/demo",
"Handler": "translate.handler",
"CodeSize": 1516,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2023-05-14T01:50:32.206828+0000",
"CodeSha256": "/GgVaQ6xCUWytX6IzPI9QMXYDMVG5FrgdvebCwmwcEo=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "6b458364-8281-4d8c-89b5-c58f6513a692",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
}
}
]
}
我尝试执行部署的Lambda函数。
参考:Lambda调用
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda invoke --function-name translateFunction outputfile.txt
{
"StatusCode": 200,
"FunctionError": "Unhandled",
"ExecutedVersion": "$LATEST"
}
出现了 FunctionError?♂️
我将检查输出的outputfile.txt文件。
参考:Node.js的AWS Lambda函数错误。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ cat outputfile.txt
{"errorMessage":"2023-05-14T02:25:34Z fa5edebc-c824-46f0-8283-3fc8c474c438 Task timed out after 3.00 seconds"}
由於出現超時錯誤,我們將將 Lambda 函數的執行時間設定為 60 秒並重新執行 ?(預設執行時間為 3 秒)。
您可以使用 update-function-configuration 命令進行設定。除了執行時間之外,您還可以更改內存大小,詳情請參閱以下頁面。
參考:Lambda update-function-configuration
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda update-function-configuration --function-name translateFunction --timeout 60
{
"FunctionName": "translateFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
"Runtime": "nodejs18.x",
"Role": "arn::iam::000000000000:role/demo",
"Handler": "translate.handler",
"CodeSize": 1516,
"Description": "",
"Timeout": 60,
"MemorySize": 128,
"LastModified": "2023-05-14T02:32:45.737909+0000",
"CodeSha256": "/GgVaQ6xCUWytX6IzPI9QMXYDMVG5FrgdvebCwmwcEo=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "327b9f37-5db2-447d-9114-1eeb3477c11c",
"State": "Active",
"LastUpdateStatus": "InProgress",
"LastUpdateStatusReason": "The function is being created.",
"LastUpdateStatusReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
}
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda invoke --function-name translateFunction outputfile.txt
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
由于更改了执行时间,Lambda函数能够成功执行,因此我们将继续检查输出的日志。
参考:使用命令行执行AWS Lambda并将日志输出到标准输出。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda invoke --function-name translateFunction --log-type Tail outputfile.txt --query 'LogResult' | tr -d '"' | base64 -d
START RequestId: 37d137f7-7503-464c-aa84-87db0bc383e6 Version: $LATEST
2023-05-14T02:39:27.955Z 37d137f7-7503-464c-aa84-87db0bc383e6 INFO INPUT_TEXT : おはようございます。調子は どうですか
2023-05-14T02:39:27.957Z 37d137f7-7503-464c-aa84-87db0bc383e6 INFO OUTPUT_TEXT : Good morning. How are you?
END RequestId: 37d137f7-7503-464c-aa84-87db0bc383e6
REPORT RequestId: 37d137f7-7503-464c-aa84-87db0bc383e6 Duration: 4079.20 ms Billed Duration: 4080 ms Memory Size: 128 MB Max Memory Used: 128 MB
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$
搭建API网关和Lambda的协同连接。
创建REST API
在 API Gateway 中创建的命令是 apigateway create-rest-api。在执行命令后显示的 id 是 API 的标识(请注意,不是资源的标识)。
请充分利用apigateway get-rest-apis命令,可以以列表形式查看已创建的API。
创建REST API
20190514 AWS黑带在线研讨会 亚马逊API网关
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-rest-api --name translate-api --endpoint-configuration types=REGIONAL
{
"id": "hpvqgkbms0",
"name": "translate-api",
"createdDate": "2023-05-14T12:30:39+09:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
]
},
"disableExecuteApiEndpoint": false
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
"items": [
{
"id": "hpvqgkbms0",
"name": "translate-api",
"createdDate": "2023-05-14T12:30:39+09:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
]
},
"disableExecuteApiEndpoint": false
}
]
}
创建测试资源
创建资源需要API ID和指定父资源ID的位置。
# API IDを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
"items": [
{
"id": "hpvqgkbms0",
"name": "translate-api",
"createdDate": "2023-05-14T12:30:39+09:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
]
},
"disableExecuteApiEndpoint": false
}
]
}
确认资源ID。在创建API之后,将会创建一个资源。新的资源将被创建在现有资源的下一级。
# リソースIDを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-resources --rest-api-id hpvqgkbms0
{
"items": [
{
"id": "55ezw5mmr6",
"path": "/"
}
]
}
然后使用apigateway create-resource命令创建一个名为/sample的测试资源。
参考:apigateway 创建资源。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-resource --rest-api-id hpvqgkbms0 --parent-id 55ezw5mmr6 --path-part sample
{
"id": "zqgeelqr0b",
"parentId": "55ezw5mmr6",
"pathPart": "sample",
"path": "/sample"
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-resources --rest-api-id hpvqgkbms0
{
"items": [
{
"id": "55ezw5mmr6",
"path": "/"
},
{
"id": "zqgeelqr0b",
"parentId": "55ezw5mmr6",
"pathPart": "sample",
"path": "/sample"
}
]
}
使用apigateway put-method命令在/sample资源中添加GET方法。
参考:API Gateway put-method
# API IDを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
"items": [
{
"id": "hpvqgkbms0",
"name": "translate-api",
"createdDate": "2023-05-14T12:30:39+09:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
]
},
"disableExecuteApiEndpoint": false
}
]
}
# GETメソッドの追加
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-method --rest-api-id hpvqgkbms0 --resource-id zqgeelqr0b --http-method GET --authorization-type NONE
{
"httpMethod": "GET",
"authorizationType": "NONE",
"apiKeyRequired": false
}
# /sampleリソースにGETメソッドが追加できたことを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-resources --rest-api-id hpvqgkbms0
{
"items": [
{
"id": "55ezw5mmr6",
"path": "/"
},
{
"id": "zqgeelqr0b",
"parentId": "55ezw5mmr6",
"pathPart": "sample",
"path": "/sample",
"resourceMethods": {
"GET": {
"httpMethod": "GET",
"authorizationType": "NONE",
"apiKeyRequired": false,
"methodResponses": {}
}
}
}
]
}
这样,您可以从客户端向/sample资源发送GET请求。然后,使用put-integration命令来定义集成请求(即从API网关发送请求到后端,并指定后端)。为了测试目的,我们暂时将–type设置为MOCK,并固定响应。
参考:
API网关put-integration命令
API集成请求的基本任务
参考:在AWS CLI中使用引号的字符串
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-integration --rest-api-id hpvqgkbms0 --resource-id zqgeelqr0b --http-method GET --type MOCK --request-template '{"application/json" : "{\"report_id\": \"1\", \"report_title\": \"Hello World\"}"}'
{
"type": "MOCK",
"requestParameters": {},
"requestTemplates": {
"application/json": "{\"report_id\": \"1\", \"report_title\": \"Hello World\"}"
},
"cacheNamespace": "zqgeelqr0b",
"cacheKeyParameters": []
}
在部署之前,使用apigateway test-invoke-method命令进行测试?。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway test-invoke-method --rest-api-id hpvqgkbms0 --resource-id zqgeelqr0b --http-method GET | jq .
{
"status": 200,
"body": "{\"report_id\": \"1\", \"report_title\": \"Hello World\"}",
"headers": {
"Host": "localhost:4566",
"Accept-Encoding": "identity",
"Content-Type": "application/json",
"User-Agent": "aws-cli/2.11.15 Python/3.11.3 Linux/5.15.90.1-microsoft-standard-WSL2 exe/x86_64.ubuntu.20 prompt/off command/apigateway.test-invoke-method",
"Accept": "application/json",
"X-Amz-Date": "20230514T113523Z",
"Authorization": "AWS4-HMAC-SHA256 Credential=test/20230514/us-east-1/apigateway/aws4_request, SignedHeaders=accept;content-type;host;x-amz-date, Signature=37de1d70843bf497460854f3f549fec76ee7ebf947b9d545f7b8847c70da07cc",
"Content-Length": "2",
"x-localstack-tgt-api": "apigateway",
"x-moto-account-id": "000000000000",
"X-Forwarded-For": "172.20.0.1, localhost:4566",
"x-localstack-edge": "http://localhost:4566"
}
}
使用apigateway create-deployment命令创建一个阶段(在此处为dev),以发布API。
参考:API create-deployment
# API IDを確認
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
"items": [
{
"id": "hpvqgkbms0",
"name": "translate-api",
"createdDate": "2023-05-14T12:30:39+09:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
]
},
"disableExecuteApiEndpoint": false
}
]
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-deployment --rest-api-id hpvqgkbms0 --stage-name dev
{
"id": "yxoalhr8v5",
"createdDate": "2023-05-14T20:31:55+09:00"
}
由于部署已完成,我将调用API以确认是否返回响应。
参考:LocalStack亚马逊 API Gateway
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ curl -X GET http://localhost:4566/restapis/hpvqgkbms0/dev/_user_request_/sample
{"report_id": "1", "report_title": "Hello World"}
创建翻译资源
现在我们已经完成了API测试的执行,接下来将开始创建translate资源!
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-resource --rest-api-id hpvqgkbms0 --parent-id 55ezw5mmr6 --path-part translate
{
"id": "2ebfoxwva3",
"parentId": "55ezw5mmr6",
"pathPart": "translate",
"path": "/translate"
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-method --rest-api-id hpvqgkbms0 -
-resource-id 2ebfoxwva3 --http-method GET --authorization-type NONE
{
"httpMethod": "GET",
"authorizationType": "NONE",
"apiKeyRequired": false
}
在API Gateway中,我们将为资源添加GET方法,并使用–request-parameters命令来接受input_text参数。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-method --rest-api-id hpvqgkbms0
--resource-id 2ebfoxwva3 --http-method GET --authorization-type NONE --request-parameters "{\"method.request.querystrin
g.input_text\": true}"
{
"httpMethod": "GET",
"authorizationType": "NONE",
"apiKeyRequired": false,
"requestParameters": {
"method.request.querystring.input_text": true
}
}
接下来,我们将进行集成请求的配置。在这里,您需要使用–type AWS_PROXY将后端集成到Lambda函数,并使用–uri指定要集成的Lambda函数的ARN。
参考:API Gateway put-integration –type
# 統合したいLambda関数のARNを確認します
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda get-function --function-name translateFunction
{
"Configuration": {
"FunctionName": "translateFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
"Runtime": "nodejs18.x",
"Role": "arn::iam::000000000000:role/demo",
"Handler": "translate.handler",
"CodeSize": 1504,
"Description": "",
"Timeout": 60,
"MemorySize": 128,
"LastModified": "2023-05-14T02:39:20.431503+0000",
"CodeSha256": "GQG2B5diM6Pe1+yZ7MkxoINkq25iX07DF1d/Co9WZpM=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "9d5a5a51-dcaa-49f1-8103-a0fb39964d59",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "http://s3.localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/translateFunction-e510ad6d-bc56-4bb9-bf13-b21b2ed9dcd6?AWSAccessKeyId=000000000000&Signature=aKGyEdrwShjbF6AQIuWyuu5ufTg%3D&Expires=1684073259"
}
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-integration --rest-api-id hpvqgkbms0 --resource-id 2ebfoxwva3 --http-method GET --integration-http-method GET --type AWS_PROXY --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:translateFunction/invocations
{
"type": "AWS_PROXY",
"httpMethod": "GET",
"uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:translateFunction/invocations",
"requestParameters": {},
"cacheNamespace": "2ebfoxwva3",
"cacheKeyParameters": []
}
在Lambda函数中,我将进行修改以接收参数。(https://maku.blog/p/5mv5dkt/)
const https = require("https");
const querystring = require("querystring");
const BASE_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp"; // 基底URL (https://xxx.jpまでを入力)
const API_KEY = "***"; // API key
const API_SECRET = "***"; // API secret
const LOGIN_ID = "***"; // ログインID
const API_NAME = "mt"; // API名 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"mt")
const API_PARAM = "generalNT_ja_en"; // API値 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"generalNT_ja_en")
const callTranslateApi = async (accessToken, input_text) => {
const postData = querystring.stringify({
access_token: accessToken,
key: API_KEY, // API Key
api_name: API_NAME,
api_param: API_PARAM,
name: LOGIN_ID, // ログインID
type: "json", // レスポンスタイプ
text: input_text, // 以下、APIごとのパラメータ
});
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": postData.length,
},
};
return new Promise((resolve, reject) => {
const req = https.request(BASE_URL + "/api/", options, (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
bodyJson = JSON.parse(body);
const output_text = bodyJson.resultset.result.text;
resolve(output_text);
});
});
req.on("error", (err) => {
reject(err);
});
req.write(postData);
req.end();
});
};
const getAccessToken = async () => {
const tokenData = querystring.stringify({
grant_type: "client_credentials",
client_id: API_KEY, // API Key
client_secret: API_SECRET, // API secret
urlAccessToken: BASE_URL + "/oauth2/token.php", // アクセストークン取得URI
});
const tokenOptions = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": tokenData.length,
},
};
return new Promise((resolve, reject) => {
const tokenReq = https.request(
BASE_URL + "/oauth2/token.php",
tokenOptions,
(res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
const accessToken = JSON.parse(body).access_token; // アクセストークン
if (!accessToken) {
console.error("Access token not found.");
reject("Error");
} else {
resolve(accessToken);
}
});
}
);
tokenReq.on("error", (err) => {
reject(err);
});
tokenReq.write(tokenData);
tokenReq.end();
});
};
exports.handler = async (event, context) => {
try {
let message = "failed...";
let output_text = "none";
if (event.queryStringParameters !== null) {
const INPUT_TEXT = event.queryStringParameters.input_text;
const accessToken = await getAccessToken();
output_text = await callTranslateApi(accessToken, INPUT_TEXT);
message = "success";
console.log("INPUT_TEXT : " + INPUT_TEXT);
}
console.log("output_text : " + output_text);
const response = {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: message,
output_text: output_text,
}),
};
return response;
} catch (error) {
console.log(error.stack);
}
};
将修正后的 translate.js 压缩为 zip 文件,并使用 lambda update-function-code 命令来更新 translateFunction。
参考:Lambda update-function-code
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ zip translate.zip translate.js
updating: translate.js (deflated 62%)
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda update-function-code --function-name translateFunction --zip-file fileb://translate.zip
"Description": "",
"Timeout": 60,
"MemorySize": 128,
"LastModified": "2023-05-14T15:05:13.069049+0000",
"CodeSha256": "HnS2h+dTY1696Pz9BVMmS+qrC4cLpG5Lls2kruAKWNQ=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "6b97a5ef-763d-462a-85f3-4a3b89346e1a",
"State": "Active",
"LastUpdateStatus": "InProgress",
"LastUpdateStatusReason": "The function is being created.",
"LastUpdateStatusReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
}
}
構築DynamoDB
创建 DynamoDB 表格
–attribute-definitionsでは属性とその属性のタイプを指定します。下記のコードでは属性名timestampでタイプが文字列です。
–key-schemaではプライマリーを構成する属性を指定します(–attribute-definitionsで指定している中から選択します)下記のコードではtimestamp属性をパーティションキーと指定しています。
参考:DynamoDB create-table
参考:读取/写入容量模式。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb create-table --table-name translate-history --attribute-definitions AttributeName=timestamp,AttributeType=S --key-schema AttributeName=timestamp,KeyType=HASH --billing-mode PAY_PER_REQUEST
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "timestamp",
"AttributeType": "S"
}
],
"TableName": "translate-history",
"KeySchema": [
{
"AttributeName": "timestamp",
"KeyType": "HASH"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": "2023-05-20T17:29:44.130000+09:00",
"ProvisionedThroughput": {
"LastIncreaseDateTime": "1970-01-01T09:00:00+09:00",
"LastDecreaseDateTime": "1970-01-01T09:00:00+09:00",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 0,
"WriteCapacityUnits": 0
},
"TableSizeBytes": 0,
"ItemCount": 0,
"TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/translate-history",
"TableId": "d7231038-ef8f-4b49-a830-1d2b616b8978",
"BillingModeSummary": {
"BillingMode": "PAY_PER_REQUEST",
"LastUpdateToPayPerRequestDateTime": "2023-05-20T17:29:44.130000+09:00"
}
}
}
我将尝试通过CLI添加项目并进行确认。
使用put-item函数来添加项,并使用scan函数来查看表中的所有项。
参考:
DynamoDB put-item
DynamoDB scan
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb put-item --table-name translate-history --item "{\"timestamp\":{\"S\":\"203505082144\"},\"input_text\":{\"S\":\"おはようございます\"},\"output_text\":{\"S\":\"Good morning\"}}"
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb scan --table-name translate-history
{
"Items": [
{
"input_text": {
"S": "おはようございます"
},
"timestamp": {
"S": "203505082144"
},
"output_text": {
"S": "Good morning"
}
}
],
"Count": 1,
"ScannedCount": 1,
"ConsumedCapacity": null
}
将Lambda和DynamoDB进行整合
参考:
使用Node.js操作DynamoDB(适用于SDK版本3)
使用@aws-sdk/client-dynamod的PutItemCommand命令。
参考:LocalStack Transparent Endpoint Injection
const https = require("https");
const querystring = require("querystring");
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");
const BASE_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp"; // 基底URL (https://xxx.jpまでを入力)
const API_KEY = "***"; // API key
const API_SECRET = "***"; // API secret
const LOGIN_ID = "***"; // ログインID
const API_NAME = "mt"; // API名 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"mt")
const API_PARAM = "generalNT_ja_en"; // API値 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"generalNT_ja_en")
const callTranslateApi = async (accessToken, input_text) => {
const postData = querystring.stringify({
access_token: accessToken,
key: API_KEY, // API Key
api_name: API_NAME,
api_param: API_PARAM,
name: LOGIN_ID, // ログインID
type: "json", // レスポンスタイプ
text: input_text, // 以下、APIごとのパラメータ
});
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": postData.length,
},
};
return new Promise((resolve, reject) => {
const req = https.request(BASE_URL + "/api/", options, (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
bodyJson = JSON.parse(body);
const output_text = bodyJson.resultset.result.text;
resolve(output_text);
});
});
req.on("error", (err) => {
reject(err);
});
req.write(postData);
req.end();
});
};
const getAccessToken = async () => {
const tokenData = querystring.stringify({
grant_type: "client_credentials",
client_id: API_KEY, // API Key
client_secret: API_SECRET, // API secret
urlAccessToken: BASE_URL + "/oauth2/token.php", // アクセストークン取得URI
});
const tokenOptions = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": tokenData.length,
},
};
return new Promise((resolve, reject) => {
const tokenReq = https.request(
BASE_URL + "/oauth2/token.php",
tokenOptions,
(res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
const accessToken = JSON.parse(body).access_token; // アクセストークン
if (!accessToken) {
console.error("Access token not found.");
reject("Error");
} else {
resolve(accessToken);
}
});
}
);
tokenReq.on("error", (err) => {
reject(err);
});
tokenReq.write(tokenData);
tokenReq.end();
});
};
const callDynamoDB = async (input_text, output_text) => {
const dynamoConfig = {
endpoint: `http://${process.env["LOCALSTACK_HOSTNAME"]}:${process.env["EDGE_PORT"]}`,
region: "us-east-1",
};
const dynamoDBClient = new DynamoDBClient(dynamoConfig);
const createdAt = new Date().toLocaleDateString("ja-JP", {
year: "numeric",
month: "2-digit",
day: "2-digit",
});
const putItemCommand = new PutItemCommand({
TableName: "translate-history",
Item: {
timestamp: { S: createdAt },
input_text: { S: input_text },
output_text: { S: output_text },
},
});
await dynamoDBClient.send(putItemCommand);
};
exports.handler = async (event, context) => {
try {
let message = "failed...";
let output_text = "none";
if (event.queryStringParameters !== null) {
const INPUT_TEXT = event.queryStringParameters.input_text;
const accessToken = await getAccessToken();
output_text = await callTranslateApi(accessToken, INPUT_TEXT);
message = "success";
await callDynamoDB(INPUT_TEXT, output_text);
}
const response = {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: message,
output_text: output_text,
}),
};
return response;
} catch (error) {
console.log(error.stack);
}
};
将修正过的Lambda函数打包成zip文件并进行部署。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ zip translate.zip translate.js
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda update-function-code --function-name translateFunction --zip-file fileb://translate.zip
{
"FunctionName": "translateFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
"Runtime": "nodejs18.x",
"Role": "arn::iam::000000000000:role/demo",
"Handler": "translate.handler",
"CodeSize": 3500,
"Description": "",
"Timeout": 60,
"MemorySize": 128,
"LastModified": "2023-05-20T09:39:03.989290+0000",
"CodeSha256": "eXu7kuOEmiXkW5T85scNKFpH9a2akXman1OxKFjxNWk=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "61d78423-094c-4bc6-a242-7b55f891e4fa",
"State": "Active",
"LastUpdateStatus": "InProgress",
"LastUpdateStatusReason": "The function is being created.",
"LastUpdateStatusReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
}
}
确认API Gateway、Lambda、翻译API和DynamoDB是否已连接。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb scan --table-name translate-history
{
"Items": [
{
"input_text": {
"S": "おはようございます"
},
"timestamp": {
"S": "203505082144"
},
"output_text": {
"S": "Good morning"
}
},
{
"input_text": {
"S": "これでハンズオンは完了です"
},
"timestamp": {
"S": "2023/05/20"
},
"output_text": {
"S": "This completes the hands-on."
}
}
],
"Count": 2,
"ScannedCount": 2,
"ConsumedCapacity": null
}
我确认DynamoDB中保存了历史记录!恭喜?
结束
感谢您阅读【AWS资格准备】免费实践学习AWS入门#1【初学者专属】!本文的实践部分到此结束。
希望这对于追求AWS资格认证的人们能成为有价值的信息源?