2022年の9/14〜15に行われたFIWARE Global Summitでも触れられていましたが、AWSがFIWARE Foundationにjoinし、smartな都市の開発で協力していくとの方針が2022/7/12に発表されています。
その一環として、AWSからaws-stfが公開されました。このSTF(Smart Territory Framework)はAWS上にFIWAREのエコシステムを構築するためのツールキットであり、AWS CDKを用いることで複雑なインフラをTypeScript及びPythonなどで宣言的に定義し、容易に環境を立ち上げることができるようになっています。
そこで今回は、aws-stfを用いてAWS上にFIWAREエコシステムを立ち上げ、実際にどのような環境が構築されるのか確認してみたいと思います。
出典: Smart Territory Framework Core - STF Core using NEC Scorpio Broker
検証環境
今回は以下の環境で検証を行いました。aws cliとnode.jsはイイカンジにインストールしておいてください。AWSの認証情報もさくっと設定しておいておいてください(私は面倒だったので、AdministoratorAccessを持つアカウントを使いました)。
ツール | バージョン |
---|---|
AWS CLI | aws-cli/2.7.31 Python/3.10.6 Darwin/20.6.0 source/x86_64 prompt/off |
node.js | v16.17.0 |
[default]
output = json
region = us-west-2
[default]
aws_secret_access_key = xxxxxxxx
aws_access_key_id = xxxxxxxx
注意
この記事を書いている2022/09/18時点では、regionは us-west-2(オレゴン) を選択するのが無難です。今回利用するScorpio版のSTFは内部的にAWS Aurora ServerlessやAmazon Managed Streaming for Apache Kafkaを立ち上げますが、 バージニア北部やオハイオ、北カリフォルニア、東京といったregionでは、以下のようなエラーが発生して構築途中で失敗し、ROLLBACKすることがあります。これはそれらのregionのAvailability Zoneには、Aurora ServerlessやMSKに対応していないものがあるためです1。
CDKのnetworkまわりのコードに手を入れて、regionに対応する適切なAZを指定できるようにすると良いかもしれませんが、とりあえず今回はコードには手を入れずにus-west-2で検証をすすめることにします。
Your subnet group only has one Availability Zone us-west-1a that is supported by Aurora Serverless.
One or more subnets belong to unsupported availability zones: [us-east-1b]. (Service: Kafka, Status Code: 400
STFスタックの起動
それではさくっとSTFスタックを起動します。
FIWAREエコシステムの根幹となるブローカー機能には、よく使われているNGSI v1/v2対応のFIWARE orionやそのNGSI-LD対応版であるorion-ld、NGSI-LD用に書き起こされたScorpioやstellioなど複数のコンポーネントがあります。STFは構想上、複数のブローカーコンポーネントに対応するつもりのようですが、9/18時点ではScorpioとAWS Serverless Serviceを組み合わせたリリースが利用可能となっています。
We provide below a list of the different flavors of the STF Core integrating the multiple implementations of the FIWARE Context Broker available.
aws-stf(scorpio版)の取得
nmatsui@:aws-stf$ git clone https://github.com/aws-samples/aws-stf-core-scorpio.git
nmatsui@:aws-stf$ cd aws-stf-core-scorpio/
nmatsui@:aws-stf-core-scorpio (main =)$
CDKのライブラリインストール
9/18時点でnode.js用のCDKは2.42.0が最新ですが、リポジトリで指定されている2.13.0をローカルインストールして利用することにします。
nmatsui@:aws-stf-core-scorpio (main =)$ npm install
nmatsui@:aws-stf-core-scorpio (main =)$ npx cdk --version
2.13.0 (build b0b744d)
CDKの準備
まずはbootstrapを実行して、CDKが必要とするアセットを指定したregion上に準備します。二回目以降はbootstrapを実行する必要はありません。
nmatsui@:aws-stf-core-scorpio (main =)$ npx cdk bootstrap
⏳ Bootstrapping environment aws://xxxxxxxxxxxx/us-west-2...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize.
CDKToolkit: creating CloudFormation changeset...
✅ Environment aws://xxxxxxxxxxxx/us-west-2 bootstrapped.
STFスタックのデプロイ
では、本命であるSTFスタックをデプロイします。30~45分程度かかりますので、気長に待ちましょう。
nmatsui@:aws-stf-core-scorpio (main =)$ npx cdk deploy
✨ Synthesis time: 6.4s
StfCore: deploying...
[0%] start: Publishing 4a575666d1c2c6412590d2a56f328e040a81ad1ef59aecee31ae9b393d05f659:current_account-current_region
✅ StfCore
✨ Deployment time: 2264.27s
Outputs:
StfCore.StfCoreEndpoint = https://xxxxxxxx.execute-api.us-west-2.amazonaws.com
StfCore.StfCoreIotQueueArn = arn:aws:sqs:us-west-2:xxxxxxxxxxxx:StfIoTQueue-us-west-2
Stack ARN:
arn:aws:cloudformation:us-west-2:xxxxxxxxxxxx:stack/StfCore/490d76b0-36d0-11ed-bd00-029c3f5cbc51
✨ Total time: 2270.67s
CDKが起動した環境の確認
それでは、CDKによって構築された環境を確認していきましょう。実はこの段階で、ブローカー機能だけでなくIoTデバイスとブローカーを接続するスタック(STF IoT)も立ち上がっています。ただし今回は、ブローカー機能に必要な環境だけ確認することにします。
CloudFormationスタック
AWS CDKは内部的にAWS CloudFormationのテンプレートを生成してAWS上のリソースを立ち上げています。STFスタックは、実際はScorpioServerlessNestedStackとIoTNestedStackから構成されている事がわかります。
nmatsui@:aws-stf-core-scorpio (main =)$ aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE --query "StackSummaries[].StackName"
[
"StfCore-IoTNestedStackIoTNestedStackResource15FB5E08-19AKRXNQCBVG",
"StfCore-ScorpioServerlessNestedStackScorpioServerlessNestedStackResourceF6005A66-1ASHOUNTIXFG0",
"StfCore",
"CDKToolkit"
]
AWS Aurora Serverless
今回利用するScorpioは、内部的にApache KafkaとPostgreSQLに依存しています2。STFはこのPostgreSQLとして、 AWS Aurora ServerlessをPostreSQLエンジンで起動して利用しています。Aurora Serverlessを利用することで、負荷が少ない場合はコストが安くすむようになっています。
nmatsui@:aws-stf-core-scorpio (main =)$ aws rds describe-db-clusters --query "DBClusters[].{name:DatabaseName,engine:Engine,version:EngineVersion,mode:EngineMode}"
[
{
"name": "scorpio",
"engine": "aurora-postgresql",
"version": "10.18",
"mode": "serverless"
}
]
Amazon Managed Streaming for Apache Kafka
Scorpioが依存するkafkaのために、Apache Kafkaのマネージドサービスが起動されています。おそらくコストの問題で、最小ノードサイズの2brokerしか起動させていないのだと思われます。(コストが問題なのであれば、MSK Serverlessにする手もあるように思いますが、Serverlessにできない理由が何かあるのでしょうかね?)
nmatsui@:aws-stf-core-scorpio (main =)$ aws kafka list-clusters --query "ClusterInfoList[].{name:ClusterName,kafkaVersion:CurrentBrokerSoftwareInfo.KafkaVersion,numberOfBrokerNodes:NumberOfBrokerNodes,size:BrokerNodeGroupInfo.InstanceType}"
[
{
"name": "ScorpioCluster",
"kafkaVersion": "2.8.1",
"numberOfBrokerNodes": 2,
"size": "kafka.t3.small"
}
]
Scorpio本体
Scorpio本体は、Fargate上のコンテナとして起動しています。active-activeで2タスクを上げているようです。Scorpioの公式docker imageではなく、Amazon ECR上のカスタムイメージscorpiobroker/scorpio-aioを使っています。このカスタムイメージと公式イメージに差分があるのかは、確認していません。
nmatsui@:aws-stf-core-scorpio (main =)$ export ECS_CLUSTER=$(aws ecs list-clusters --query "clusterArns[0]" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ export ECS_SERVICE=$(aws ecs list-services --cluster $ECS_CLUSTER --query "serviceArns[0]" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ export ECS_TASK=$(aws ecs list-tasks --cluster $ECS_CLUSTER --service-name $ECS_SERVICE --query "taskArns[0]" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ aws ecs describe-services --cluster $ECS_CLUSTER --services $ECS_SERVICE --query "services[].{launchType:launchType,desiredCount:desiredCount,runningCount:runningCount}"
[
{
"launchType": "FARGATE",
"desiredCount": 2,
"runningCount": 2
}
]
nmatsui@:aws-stf-core-scorpio (main =)$ aws ecs describe-tasks --cluster $ECS_CLUSTER --tasks $ECS_TASK --query "tasks[].{containers:containers[].{image:image,status:lastStatus},cpu:cpu,memory:memory}"
[
{
"containers": [
{
"image": "public.ecr.aws/scorpiobroker/scorpio-aio:latest",
"status": "RUNNING"
}
],
"cpu": "512",
"memory": "2048"
}
]
なお、Scorpioのイメージはparameters.ts
で指定されているようです。公式イメージにしても、Aurora ServerlessやMSKで動作するのかな?
export const Parameters = {
image_context_broker: 'public.ecr.aws/scorpiobroker/scorpio-aio:latest',
smart_data_model_url : 'https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld',
shadow_prefix: "Stf",
timeout: '0', // Timeout for the API call in the Lambda that sync with context broker. Has to be a string to pass it in env variable
}
Amazon API Gateway
最後はAmazon API Gatewayです。インターネット上にエンドポイントを公開し、Scoprioのコンテナへリクエストをルーティングします。
なお /iot/things/*
への GET,POST,DELETE
は、今回説明しないSTF IoTスタックが作成したIoTデバイス用のルートです。それ以外のリクエストは ANY /{proxy+}
ルートにヒットして、VPCLink経由でScorpioのコンテナへルーティングされます。
nmatsui@:aws-stf-core-scorpio (main =)$ export API_ID=$(aws apigatewayv2 get-apis --query "Items[0].ApiId" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ aws apigatewayv2 get-routes --api-id $API_ID --query "Items[].RouteKey"
[
"GET /iot/things",
"GET /iot/things/{thingName}",
"DELETE /iot/things/{thingName}",
"ANY /{proxy+}",
"POST /iot/things"
]
では、API Gatewayのエンドポイントに対して /actuator/info
や /actuator/health
というパスでGETリクエストを投げてみます。Scorpioがそれらのリクエストを受け付けると、内部的に利用されているSping bootがビルド情報や動作状況を返します。
nmatsui@:aws-stf-core-scorpio (main =)$ export ENDPOINT=$(aws apigatewayv2 get-apis --query "Items[0].ApiEndpoint" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sS "$ENDPOINT/actuator/info" | jq .
{
"build": {
"artifact": "AllInOneRunner",
"name": "AllInOneRunner",
"time": "2022-09-08T13:51:46.913Z",
"version": "1.1.0-SNAPSHOT",
"group": "eu.neclab.ngsildbroker"
}
}
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sS "$ENDPOINT/actuator/health" | jq .
{
"status": "UP"
}
Amazon API Gatewayへトークン認証を追加
さて、無事にScorpioが動作してインターネットから接続できることが確認できたのですが、ブローカーがインターネット上へ生で公開されているのは、検証用とはいえさすがに問題です。STF Core using NEC Scorpio BrokerスタックのREADMEでも、プロダクションで利用する前にアクセスコントロールを考えなさいと注意されています。
Before using this stack in production, please consider adding a mechanism for controlling and managing access to the StfCoreEndpoint.
本来は、EntityのGET
は無制限だがPOST/PATCH/DELETE
は個別にリクエストを認証して認可された利用者のみ許可、のような認証認可戦略を立て実現すべきですが、今回は検証用にすべてのルートに対して簡易的なトークン認証を実装します。
認証用lambda functionの登録
リクエストの"authorization"ヘッダが特定の文字列であることをチェックする、非常にシンプルな認証用lambda functionを登録します。
認証用lambda functionの登録
nmatsui@:aws-stf-core-scorpio (main =)$ export TOKEN=your_token
nmatsui@:aws-stf-core-scorpio (main =)$ export REGION=$(aws configure get region --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ export ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ aws iam create-role --role-name lambda-auth-role \
--assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
nmatsui@:aws-stf-core-scorpio (main =)$ cat <<__EOF__ > lambda-auth.js
exports.handler = async(event) => {
let response = {
"isAuthorized": false
};
if (event.headers.authorization === "$TOKEN") {
response = {
"isAuthorized": true
};
}
return response;
};
__EOF__
nmatsui@:aws-stf-core-scorpio (main =)$ zip function.zip lambda-auth.js
nmatsui@:aws-stf-core-scorpio (main =)$ aws lambda create-function --function-name lambda-auth \
--zip-file fileb://function.zip \
--handler lambda-auth.handler \
--runtime nodejs12.x \
--role arn:aws:iam::$ACCOUNT:role/lambda-auth-role
Authorizerの作成
作成済みのlambda funtionを、Amazon API Gatewayのauthrizerとして登録します。
Authorizerの作成
nmatsui@:aws-stf-core-scorpio (main =)$ aws apigatewayv2 create-authorizer \
--api-id $API_ID \
--authorizer-type REQUEST \
--identity-source '$request.header.Authorization' \
--name lambda-authorizer \
--authorizer-uri "arn:aws:apigateway:$REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:$REGION:$ACCOUNT:function:lambda-auth/invocations" \
--authorizer-payload-format-version '2.0' \
--enable-simple-responses
lambda functionに権限追加
Amazon API Gatewayがlambda functionを実行できるように、権限を与えます。
lambda functionに権限追加
nmatsui@:aws-stf-core-scorpio (main =)$ export AUTHORIZER_ID=$(aws apigatewayv2 get-authorizers --api-id $API_ID --query "Items[?Name==\`lambda-authorizer\`].AuthorizerId" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ aws lambda add-permission \
--function-name lambda-auth \
--statement-id apigateway-invoke-permission-lambda-authorizer \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:$REGION:$ACCOUNT:$API_ID/authorizers/$AUTHORIZER_ID"
すべてのルートにauthorizerを追加
これでAmazon API Gatewayからauthorizerのlambda functionが呼び出せるようになりましたので、各ルートにauthroizerを割り当てます。
すべてのルートにauthorizerを追加
nmatsui@:aws-stf-core-scorpio (main =)$ export ROUTES=$(aws apigatewayv2 get-routes --api-id $API_ID --query "Items[].RouteId" --output text)
nmatsui@:aws-stf-core-scorpio (main =)$ for route in $ROUTES; do
aws apigatewayv2 update-route \
--api-id $API_ID \
--route-id $route \
--authorization-type CUSTOM \
--authorizer-id $AUTHORIZER_ID
done
トークン認証の動作確認
それでは、トークン認証が有効であることを確認します。最低限ですが、検証なのでこれでOK!
Authorizationヘッダ無し → 401 Unauthorized
nmatsui@:aws-stf-core-scorpio (main =)$ curl -i "$ENDPOINT/actuator/info"
HTTP/2 401
date: Sun, 18 Sep 2022 08:40:16 GMT
content-type: application/json
content-length: 26
apigw-requestid: YpbpliAfPHcEPQw=
{"message":"Unauthorized"}
Authorizationヘッダ有り → 200 OK
nmatsui@:aws-stf-core-scorpio (main =)$ curl -i "$ENDPOINT/actuator/info" \
-H "Authorization: $TOKEN"
HTTP/2 200
date: Sun, 18 Sep 2022 08:41:34 GMT
content-type: application/vnd.spring-boot.actuator.v3+json
content-length: 157
apigw-requestid: Ypb1tg7wPHcEMUw=
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY
{"build":{"artifact":"AllInOneRunner","name":"AllInOneRunner","time":"2022-09-08T13:51:46.913Z","version":"1.1.0-SNAPSHOT","group":"eu.neclab.ngsildbroker"}}
ブローカー機能の確認
無事にAWS上にFIWAREのブローカー機能が立ち上がりましたので、EntityのCRUDを軽く確認してみましょう。
※ NGSI-LDの仕様に関しては、ETSIの公式API仕様やFIWAREのStep by Step、ScorpioのAPI Walkthroughなどを確認してください。
contextの扱い
NGSI v2とは異なり、構造化されたデータの語彙を特定するためにNGSI-LDはcontext
を要求します。このcontextは、以下の2つの方法で指定できる事になっています。
- "Link"ヘッダでcontextを指定し、リクエストボディには何も書かない
- "Content-Type"ヘッダに
application/ld+json
を指定し、リクエストボディの@context
でcontextを指定する
空の状態でEntity一覧を取得
NGSI v2とは異なり、QueryStringでtype
を指定しないと400 Bad Request
になってしまいます。
なお本来、GETはリクエストボディが無いためLinkで指定する方法しか使えないはずですが、Scorpioは"Content-Type"ヘッダにapplication/ld+json
を指定するとリクエストが通るようです。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d type=TempSensor \
| jq .
[]
リクエストボディでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG $ENDPOINT/ngsi-ld/v1/entities/ \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/ld+json" \
-d type=TempSensor \
| jq .
[]
Entityを登録
NGSI-LDのEntityは、NGSI v2と見た目は似ていますがidの付け方やmetadataの付け方が異なります。詳細はNGSI-LDの仕様を確認してください。とりあえず今回は、NGSI v2とほとんど同じようなデータモデルにしています。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -iX POST "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d @- <<EOS
{
"id": "urn:ngsi-ld:TempSensor:001",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 25.2,
"unitCode": "CEL"
}
}
EOS
HTTP/2 201
date: Sun, 18 Sep 2022 11:08:57 GMT
content-length: 0
apigw-requestid: YpxbZgo0PHcEJcA=
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
location: /ngsi-ld/v1/entities/urn:ngsi-ld:TempSensor:001
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY
リクエストボディでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -iX POST "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Content-Type: application/ld+json' \
-d @- <<EOS
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 28.4,
"unitCode": "CEL"
},
"@context": [
"https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld"
]
}
EOS
HTTP/2 201
date: Sun, 18 Sep 2022 11:10:53 GMT
content-length: 0
apigw-requestid: YpxtrhRkPHcEMCw=
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
location: /ngsi-ld/v1/entities/urn:ngsi-ld:TempSensor:002
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY
Entity一覧を取得
/ngsi-ld/v1/entities/
をGETすることで、Entityの一覧を取得できます。本来のLinkヘッダでのcontextの指定をしていない場合、プロパティ名にcontextが入るようです。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d type=TempSensor \
| jq .
[
{
"id": "urn:ngsi-ld:TempSensor:001",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 25.2,
"unitCode": "CEL"
}
},
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 28.4,
"unitCode": "CEL"
}
}
]
リクエストボディでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/ld+json" \
-d type=TempSensor \
| jq .
[
{
"id": "urn:ngsi-ld:TempSensor:001",
"type": "TempSensor",
"https://smart-data-models.github.io/data-models/terms.jsonld#/definitions/category": {
"type": "Property",
"value": "sensor"
},
"https://smart-data-models.github.io/data-models/terms.jsonld#/definitions/temperature": {
"type": "Property",
"value": 25.2,
"unitCode": "CEL"
}
},
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"https://smart-data-models.github.io/data-models/terms.jsonld#/definitions/category": {
"type": "Property",
"value": "sensor"
},
"https://smart-data-models.github.io/data-models/terms.jsonld#/definitions/temperature": {
"type": "Property",
"value": 28.4,
"unitCode": "CEL"
}
}
]
IDを指定してEntityを取得
NGSI v2と同様、/ngsi-ld/v1/entities/<entity id>/
をGETすることで、そのEntityを取得することができます。この場合、QueryStringでtype
を指定する必要はありません。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/urn:ngsi-ld:TempSensor:001" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
| jq .
{
"id": "urn:ngsi-ld:TempSensor:001",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 25.2,
"unitCode": "CEL"
}
}
リクエストボディでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/urn:ngsi-ld:TempSensor:002" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/ld+json" \
| jq .
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"https://smart-data-models.github.io/data-models/terms.jsonld#/definitions/category": {
"type": "Property",
"value": "sensor"
},
"https://smart-data-models.github.io/data-models/terms.jsonld#/definitions/temperature": {
"type": "Property",
"value": 28.4,
"unitCode": "CEL"
}
}
Entityをフィルタ
QueryStringにq
パラメータでフィルタ条件を書くことで、Entityをフィルタすることができます。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d type=TempSensor \
-d q=temperature==26..30 \
| jq .
[
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 28.4,
"unitCode": "CEL"
}
}
]
特定の属性のみ取得
QuetyStringにattrs
パラメータを指定すると、指定した属性のみ取得することができます。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d type=TempSensor \
-d attrs=temperature \
| jq .
[
{
"id": "urn:ngsi-ld:TempSensor:001",
"type": "TempSensor",
"temperature": {
"type": "Property",
"value": 25.2,
"unitCode": "CEL"
}
},
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"temperature": {
"type": "Property",
"value": 28.4,
"unitCode": "CEL"
}
}
]
Entityを更新
更新すべき属性をボディに指定して/ngsi-ld/v1/entities/<entity id>/
にPATCHすることで、その属性を更新することができます。この場合リクエストボディには更新すべき属性のみ記述するため、必然的にcontextは"Link"ヘッダで指定することになります。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -iX PATCH "$ENDPOINT/ngsi-ld/v1/entities/urn:ngsi-ld:TempSensor:002/attrs" \
-H "Authorization: $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d @- <<EOS
{
"temperature": {
"type": "Property",
"value": 31.2,
"unitCode": "CEL"
}
}
EOS
HTTP/2 204
date: Sun, 18 Sep 2022 11:46:52 GMT
apigw-requestid: Yp2-5jksvHcEMQA=
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY
更新したEntityを確認
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d type=TempSensor \
| jq .
[
{
"id": "urn:ngsi-ld:TempSensor:001",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 25.2,
"unitCode": "CEL"
}
},
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 31.2,
"unitCode": "CEL"
}
}
]
Entityの削除
最後に/ngsi-ld/v1/entities/<entity id>/
にDELETEすることで、そのEntityを削除することができます。
Linkヘッダでcontextを指定
nmatsui@:aws-stf-core-scorpio (main =)$ curl -iX DELETE "$ENDPOINT/ngsi-ld/v1/entities/urn:ngsi-ld:TempSensor:001" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json'
HTTP/2 204
date: Sun, 18 Sep 2022 12:00:51 GMT
apigw-requestid: Yp5CGjrfvHcEPWA=
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-frame-options: DENY
更新したEntityを確認
nmatsui@:aws-stf-core-scorpio (main =)$ curl -sSG "$ENDPOINT/ngsi-ld/v1/entities/" \
-H "Authorization: $TOKEN" \
-H 'Link: <https://raw.githubusercontent.com/smart-data-models/data-models/master/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json' \
-d type=TempSensor \
| jq .
[
{
"id": "urn:ngsi-ld:TempSensor:002",
"type": "TempSensor",
"category": {
"type": "Property",
"value": "sensor"
},
"temperature": {
"type": "Property",
"value": 31.2,
"unitCode": "CEL"
}
}
]
まとめ
AWS STF(Smart Territory Framework)を用いて、AWS上にFIWARE NGSI-LD Context Broker(Scorpio)を簡単に構築することができました。
ただしNGSI−LDの検証環境として利用する場合は良いですが、本番環境として使う場合は以下のようなポイントに注意が必要となります。AWS STFをベースに、自らの状況に合わせたCDKスタックを作ると良いでしょう。
リクエストの認証認可
AWS STFをデータ連携基盤として利用する場合、HTTPメソッドとパスごとに適切な認証認可を設定する必要があります。権限のあるユーザーの管理手順など運用負荷も含めて適切な認証認可のミドルウェアを選択し、Amazon API Gatewayと連携させるようにCDKスタックを改修する必要があるでしょう。
Managed Serviceのリソース設定
STF CoreのCDKスタックでは、AWS Aurora Serverlessや小さめのAmazon Managed Streaming for Apache Kafkaと連携するように設定されています。検証用途では良いですが、本番環境として使う場合は性能上のボトルネックになる可能性があります。実際にどの程度の頻度のリクエストが来ているのか、来るはずなのかを監視・予測して、適切なリソースサイズに変更してください。
他のFIWAREコンポーネントや自作コンポーネントとの連携
実際にデータ連携基盤を構築する際には、ブローカー機能だけでなくその他の機能との連携が必要となります。例えばEntityの更新を時系列で記録する時系列DBのコンポーネントや、連携されたデータの状況を可視化するBIコンポーネント、あるいは用途に合わせて自作した特別なコンポーネントなど。それらの連携をCDKスタックに組み込む必要があるでしょう。
そもそも・・・
デジタル田園都市国家構想でもオープンなデータ連携基盤が必要と謳われており3、FIWARE orionをベースとしたブローカー機能が必要と言われています456。そのFIWARE orionもAWS STFが採用しているscorpioも、共にFIWAREのContext Brokerコンポーネントではありますが、NGSI v2のデータモデルを扱うorionとNGSI−LDを扱うscorpio間では互換性がありません。今後データ連携基盤どうしでデータ交換をするようになった時に、困るかもしれません。
-
https://docs.aws.amazon.com/msk/latest/developerguide/msk-create-cluster.html ↩
-
https://github.com/ScorpioBroker/ScorpioBroker/blob/development/docs/en/source/installationGuide.rst ↩
-
https://www.chisou.go.jp/sousei/about/mirai/pdf/digitaldenenseidoyoukou.pdf ↩
-
https://www.chisou.go.jp/sousei/about/mirai/pdf/denenkouhukin_jissou_type23_hyoka.pdf ↩
-
https://www.chisou.go.jp/sousei/about/mirai/pdf/denenkouhukin_jissou_type23_qa1-3.pdf ↩
-
https://www.digital.go.jp/assets/contents/node/basic_page/field_ref_resources/82a1ea56-128f-4cf6-bbd5-9ef6d4b7bafc/40d603f5/20220812_policies_budget_subsidies_02.pdf ↩