cloudpack あら便利カレンダー 2019 の記事となります。誕生秘話 はこちら。
Amazon Managed Blockchain(AMB)でブロックチェーンネットワークを構築するのが手間になったので、AWS CloudFormation(CFn)を利用して構築できるようにしてみました。使い方は下記をご参考ください。
Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークをさくっと構築するAWS CloudFormationのテンプレートを作ってみた(使い方編) - Qiita
https://qiita.com/kai_kou/items/dc7dbe947a7cc6d0a8db
Cfnのテンプレートを作成するのにいくつかハマったりしたので解説がてらまとめてみます。
テンプレートはGitHubにアップしています。
kai-kou/amazon-managed-blockchain-cfn-template
https://github.com/kai-kou/amazon-managed-blockchain-cfn-template
CFnがAMBリソースに対応していない
AMBのネットワークやメンバーなどのリソースがCFnでサポートされていません。(2019/07/03時点)
AWS Resource and Property Types Reference - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
Release History - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/ReleaseHistory.html
そのためAWS Lambda-backedカスタムリソースを利用してLambda関数でAWS SDKを利用してAMBのネットワークやメンバーを作成する必要があります。
詳細は下記が参考になります。
AWS SDK for Python(boto3)でAmazon Managed Blockchainのブロックチェーンネットワークを作成してみた - Qiita
https://qiita.com/kai_kou/items/9e16274b6a5c30aa6086
AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 - Qiita
https://qiita.com/kai_kou/items/7be2eb9a36611bb5da12
リソースの作成順を制御する
AMBのPeerノードはネットワーク(メンバー)が利用可能になってから追加する必要があるため、CFnのDependsOn
属性でリソースの作成順を制御してネットワーク(→メンバー)→Peerノードの順にリソースを作成します。
※ネットワークで最初のメンバーはネットワークと同時に作成する必要があります。
DependsOn 属性 - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html
AWS Lambda-backedカスタムリソースでリソース作成完了を待ち受ける
DependsOn
属性でリソースの作成順を制御することができたのですが、AMBのリソース(ネットワーク、Peerノード)をAWS CLIやAWS SDKで作成するとNetworkId
などがすぐにレスポンスとして返ってきます。AMBだと「レスポンスが返ってきた=リソース作成できた」ではなく**「レスポンスが返ってきた=リソース作成が開始された」**となります。リソースが作成完了し利用可能になるには20分ほどかかります。
なので、CFnでネットワーク作成してすぐにPeerノードを作成しようとするとネットワークが利用可能になっておらずエラーになります。
そのためCFnでリソースの作成を待ち受けるのにAWS Lambda-backedカスタムリソースでどうにかする必要がありますが、AMBネットワークの作成には20分程度かかり、AWS Lmabdaのタイムアウト(15分)を超えてしまうため、作成用のカスタムリソースだけでは待受けすることができません。
そこで、待受用のカスタムリソースを定義することで、AWS Lambdaのタイムタウト(15分) x リトライ回数(3回)で45分まで待ち受けられるようにします。詳細は下記が参考になります。
AWS CloudFormationのLambda-Backedカスタムリソースでリソース作成を待ち受けできるようにする - Qiita
https://qiita.com/kai_kou/items/57699a4e84ded2c1bfc4
このリソース作成を待ち受ける実装とDependsOn
属性を利用することでAMBのリソースが作成できるようになりました。下記はCFnのテンプレートから抜粋したリソース定義となります。
Resources:
# ネットワーク作成用のカスタムリソース
CreateBlockchainNetwork:
Type: Custom::CustomResource
# リソース作成待受用のカスタムリソース
BlockchainNetwork:
Type: Custom::CustomResource
DependsOn: CreateBlockchainNetwork
# リソース情報取得用のカスタムリソース
BlockchainMember:
Type: Custom::CustomResource
DependsOn: BlockchainNetwork
# Peerノード作成用のカスタムリソース
CreateBlockchainPeerNode:
Type: Custom::CustomResource
DependsOn: BlockchainMember
# リソース作成待受用のカスタムリソース
BlockchainPeerNode:
Type: Custom::CustomResource
DependsOn: CreateBlockchainPeerNode
AWS Lambdaで利用できるAWS SDKを最新にする
AMBは2019/05/01にGAとなったサービスです。
New – Amazon Managed Blockchain – Create & Manage Scalable Blockchain Networks | AWS News Blog
https://aws.amazon.com/jp/blogs/aws/new-amazon-managed-blockchain-create-manage-scalable-blockchain-networks
AWS Lambdaの関数(Python)で利用できるAWS SDK(boto3 1.9.42)だとAMBが対応していないバージョンとなるため、AWS Lambda Layersを利用して最新のAWS SDK(boto3 1.9.139 以上)を利用する必要があります。(2019/07/03時点)
boto3/CHANGELOG.rst at develop · boto/boto3
https://github.com/boto/boto3/blob/develop/CHANGELOG.rst#19139
api-change:managedblockchain: [botocore] Update managedblockchain client to latest version
エラー例
import json
import boto3
def lambda_handler(event, context):
print(boto3.__version__)
client = boto3.client("managedblockchain")
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
START RequestId: 1f574950-f61d-4d05-a2b6-a56e11eb2201 Version: $LATEST
1.9.42
[ERROR] UnknownServiceError: Unknown service: 'managedblockchain'. Valid service names are: acm, acm-pca, alexaforbusiness, apigateway, application-autoscaling, appstream, appsync, athena, autoscaling, autoscaling-plans, batch, budgets, ce, chime, cloud9, clouddirectory, cloudformation, cloudfront, cloudhsm, cloudhsmv2, cloudsearch, cloudsearchdomain, cloudtrail, cloudwatch, codebuild, codecommit, codedeploy, codepipeline, codestar, cognito-identity, cognito-idp, cognito-sync, comprehend, config, connect, cur, datapipeline, dax, devicefarm, directconnect, discovery, dlm, dms, ds, dynamodb, dynamodbstreams, ec2, ecr, ecs, efs, eks, elasticache, elasticbeanstalk, elastictranscoder, elb, elbv2, emr, es, events, firehose, fms, gamelift, glacier, glue, greengrass, guardduty, health, iam, importexport, inspector, iot, iot-data, iot-jobs-data, iot1click-devices, iot1click-projects, iotanalytics, kinesis, kinesis-video-archived-media, kinesis-video-media, kinesisanalytics, kinesisvideo, kms, lambda, lex-models, lex-runtime, lightsail, logs, machinelearning, macie, marketplace-entitlement, marketplacecommerceanalytics, mediaconvert, medialive, mediapackage, mediastore, mediastore-data, mediatailor, meteringmarketplace, mgh, mobile, mq, mturk, neptune, opsworks, opsworkscm, organizations, pi, pinpoint, pinpoint-email, polly, pricing, rds, redshift, rekognition, resource-groups, resourcegroupstaggingapi, route53, route53domains, s3, sagemaker, sagemaker-runtime, sdb, secretsmanager, serverlessrepo, servicecatalog, servicediscovery, ses, shield, signer, sms, snowball, sns, sqs, ssm, stepfunctions, storagegateway, sts, support, swf, transcribe, translate, waf, waf-regional, workdocs, workmail, workspaces, xray
(略)
AWS Lambda Layersを利用して最新のAWS SDKを利用する方法は下記が参考になります。
AWS CloudFormationのAWS Lambda-backedカスタムリソースで最新のAWS SDKを利用する - Qiita
https://qiita.com/kai_kou/items/db0924f8bbd0fd03eb94
AMBリソースの更新・削除に対応する
AMBリソースはAWS Lambda-backedカスタムリソースで作成しているので、更新や削除もLambda-backedカスタムリソースで行う必要があります。
詳細は下記が参考になりますが、こちらもリソース作成の待受と同じく、作成とは別のカスタムリソースを定義する必要があります。
AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 - Qiita
https://qiita.com/kai_kou/items/7be2eb9a36611bb5da12
作成待ちと更新・削除を担うカスタムリソースは共通化できたので、最終的には下記のようなリソース定義となりました。
Resources:
# ネットワーク作成用のカスタムリソース
CreateBlockchainNetwork:
Type: Custom::CustomResource
# リソース作成待受とリソース情報取得・更新・削除用のカスタムリソース
BlockchainNetwork:
Type: Custom::CustomResource
Properties:
NetworkId: !GetAtt CreateBlockchainNetwork.NetworkId
DependsOn: CreateBlockchainNetwork
# リソース情報取得用のカスタムリソース
BlockchainMember:
Type: Custom::CustomResource
Properties:
NetworkId: !GetAtt BlockchainNetwork.Network.Id
MemberId: !GetAtt CreateBlockchainNetwork.MemberId
DependsOn: BlockchainNetwork
# Peerノード作成用のカスタムリソース
CreateBlockchainPeerNode:
Type: Custom::CustomResource
Properties:
NetworkId: !GetAtt BlockchainNetwork.Network.Id
MemberId: !GetAtt BlockchainMember.Member.Id
DependsOn: BlockchainMember
# リソース作成待受とリソース情報取得・更新・削除用のカスタムリソース
BlockchainPeerNode:
Type: Custom::CustomResource
Properties:
NetworkId: !GetAtt BlockchainNetwork.Network.Id
MemberId: !GetAtt BlockchainMember.Member.Id
NodeId: !GetAtt CreateBlockchainPeerNode.NodeId
DependsOn: CreateBlockchainPeerNode
AWS lambda-backedカスタムリソースで返すリソース情報(JSON)に気をつける
AWS lambda-backedカスタムリソースのLambda関数ではcfnresponse.send(event, context, cfnresponse.SUCCESS, data)
のようにして処理結果をCFnに返すことができます。
最後のパラメータdata
はJSONとなり、CFnのFn::GetAtt
で参照可能です。
カスタムリソースの応答オブジェクト - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html
応答で送信される、custom resource providerによって定義された名前と値のペア。ここで指定する値には、Fn::GetAtt を使用して、テンプレート内の名前でアクセスできます。
ただし、ネストされたJSONをdata
に含めてもNetwork.Id
のようにして参照できないため、AWS SDKでAMBのリソース情報を取得して得られるJSONをそのままでは返すことができませんでした。
AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない - Qiita
https://qiita.com/kai_kou/items/61a2b3c69ae2af4f2e40
なので、AWS SDKでAMBのリソース情報を取得して得られるJSONを加工する必要があります。
JSONのキーをNetwork.Id
とすると、CFn側でもNetwork.Id
と参照できるようにしています。
import cfnresponse
import boto3
import json
from datetime import date, datetime
def handler(event, context):
client = boto3.client("managedblockchain")
networkId = event['ResourceProperties']['NetworkId']
response = {}
if event['RequestType'] == 'Create':
network = client.get_network(
NetworkId=networkId
)
orderingServiceEndpoint = network['Network']['FrameworkAttributes']['Fabric']['OrderingServiceEndpoint']
vpcEndpointServiceName = network['Network']['VpcEndpointServiceName']
response = {
"Network.Id": networkId,
"Network.FrameworkAttributes.Fabric.OrderingServiceEndpoint": orderingServiceEndpoint,
"Network.VpcEndpointServiceName": vpcEndpointServiceName
}
cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
詳細は下記が参考になります。
AWS CloudFormationのLambda-BackedカスタムリソースでネストされてるっぽいJSONを返す方法 - Qiita
https://qiita.com/kai_kou/items/d030117c694531410203
Hyperledger FabricのクライアントをEC2インスタンスで構築する
Hyperledger FabricのクライアントをEC2インスタンスで構築する定義は下記を参考にさせてもらいました。
awslabs/amazon-managed-blockchain-client-templates: AWS CloudFormation templates to provision Amazon EC2 instances and install and configure clients for use with blockchain frameworks in Amazon Managed Blockchain
https://github.com/awslabs/amazon-managed-blockchain-client-templates
こちらはHyperledger FabricのクライアントとなるEC2インスタンスを作成するテンプレートでしたので、自前のテンプレートへ組み込み、AMBで必要となるリソースの作成後、インスタンスが作成されるようにしました。
ポイントとしては以下となります。
CFnのcfn-signal
ヘルパースクリプトで完了シグナルをCFnに返す
CFnでEC2インスタンスを作成すると、EC2インスタンスのステータスがRunning
となった時点でリソース作成完了となります。
そのため、テンプレートのUserData
で定義しているコマンド実行でエラーとなっても正常完了扱いとなり不便だったので、CFnのcfn-signal
ヘルパースクリプトで完了シグナルをCFnに返すようにしました。
cfn-signal
を利用するには、CreationPolicy
を定義する必要があります。
BlockchainClient:
Type: AWS::EC2::Instance
UserData:
Fn::Base64:
Fn::Sub:
- |
#!/bin/bash
(略)
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource BlockchainClient --region ${AWS::Region}
CreationPolicy:
ResourceSignal:
Timeout: PT20M
詳細は下記が参考になります。
cfn-signal - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-signal.html
Fn::Sub
で変数の取扱い
UserData
に指定する値はFn::Base64
とFn::Sub
を用いて指定しています。
Fn::Sub
は文字列中に置き換えたい変数がある場合に利用する関数となっています。
Fn::Sub - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html
テンプレートで、スタックを作成または更新するまで使用できない値を含むコマンドまたは出力を作成するために、この関数を使用できます。
UserData
を編集しているとシェル変数を利用したくなるケースがでてきますが、そのまま利用するとエラーとなるため注意が必要です。シェル変数の利用方法は下記が参考になりました。
CloudFormationの中のEC2のユーザーデータでシェル変数を使用する | DevelopersIO
https://dev.classmethod.jp/cloud/aws/using-variables-in-ec2-user-data-in-cloudformation/
UserData:
Fn::Base64:
Fn::Sub:
- |
#!/bin/bash
# これはOK
echo ${HOGE}
HOGE2=hogehoge
# これはエラーになる
echo ${HOGE2}
# こうするとOK
echo ${!HOGE2}
- {
HOGE: hoge
}
Hyperledger Fabricのcliでコマンドを実行するタイミングに気をつける
以下は、UserData
の後半部分の抜粋となります。ところどころでsleep
コマンドを実行して待受けています。
リソース作成を繰り返し試行錯誤した結果となりますが、主に下記の理由からとなります。
- Peerノードが利用可能になるのを待ち受け
- OrdererからPeerノードへのデータ送信待ち
(略)
/usr/local/bin/docker-compose -f docker-compose-cli.yaml up -d
sleep 5m
# enroll
fabric-ca-client enroll -u https://${ADMIN_USERNAME}:${ADMIN_PASSWORD}@${FABRIC_CA_ENDPOINT} --tls.certfiles /home/ec2-user/${FABRIC_CA_FILE} -M /home/ec2-user/admin-msp
cp -r /home/ec2-user/admin-msp/signcerts /home/ec2-user/admin-msp/admincerts
echo '
Organizations:
(略)
' > /home/ec2-user/configtx.yaml
docker exec cli configtxgen -outputCreateChannelTx /opt/home/mychannel.pb -profile OneOrgChannel -channelID mychannel --configPath /opt/home/
sleep 30s
# Create Channel
docker exec cli peer channel create -c mychannel -f /opt/home/mychannel.pb -o ${ORDERING_SERVICE_ENDPOINT} --cafile /opt/home/${FABRIC_CA_FILE} --tls
sleep 30s
docker exec cli peer channel join -b mychannel.block -o ${ORDERING_SERVICE_ENDPOINT} --cafile /opt/home/${FABRIC_CA_FILE} --tls
sleep 30s
# Install ChainCode
docker exec cli peer chaincode install -n mycc -v v0 -p github.com/chaincode_example02/go
docker exec cli peer chaincode instantiate -o ${ORDERING_SERVICE_ENDPOINT} -C mychannel -n mycc -v v0 -c '{"Args":["init","a","100","b","200"]}' --cafile /opt/home/${FABRIC_CA_FILE} --tls
sleep 30s
docker exec cli peer chaincode list --instantiated -o ${ORDERING_SERVICE_ENDPOINT} -C mychannel --cafile /opt/home/${FABRIC_CA_FILE} --tls
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource BlockchainClient --region ${AWS::Region}
UserData
の実行ログの確認方法
UserData
で指定したコマンドの実行ログが確認できないか調べてみたらしっかりと出力されていました。
下記が参考になりました。
Linux インスタンスでの起動時のコマンドの実行 - Amazon Elastic Compute Cloud
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html
AWSのCloud initのログの場所 | しびら
http://yamada.daiji.ro/blog/?p=191
$ cat /var/log/cloud-init-output.log
(略)
+ docker exec cli peer channel join -b mychannel.block -o orderer.n-xxxxxxxxxxxxxxxxxxxxxxxxxx.managedblockchain.us-east-1.amazonaws.com:30001 --cafile /opt/home/managedblockchain-tls-chain.pem --tls
2019-07-01 09:17:03.663 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2019-07-01 09:17:03.903 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
+ sleep 30s
+ docker exec cli peer chaincode install -n mycc -v v0 -p github.com/chaincode_example02/go
2019-07-01 09:17:34.076 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2019-07-01 09:17:34.076 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2019-07-01 09:17:34.490 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >
+ sleep 30s
+ docker exec cli peer chaincode instantiate -o orderer.n-xxxxxxxxxxxxxxxxxxxxxxxxxx.managedblockchain.us-east-1.amazonaws.com:30001 -C mychannel -n mycc -v v0 -c '{"Args":["init","a","100","b","200"]}' --cafile /opt/home/managedblockchain-tls-chain.pem --tls
2019-07-01 09:17:44.686 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2019-07-01 09:17:44.686 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
+ sleep 30s
+ docker exec cli peer chaincode list --instantiated -o orderer.n-xxxxxxxxxxxxxxxxxxxxxxxxxx.managedblockchain.us-east-1.amazonaws.com:30001 -C mychannel --cafile /opt/home/managedblockchain-tls-chain.pem --tls
Get instantiated chaincodes on channel mychannel:
Name: mycc, Version: v0, Path: github.com/chaincode_example02/go, Escc: escc, Vscc: vscc
+ /opt/aws/bin/cfn-signal -e 0 --stack amb-cfn-test --resource BlockchainClient --region us-east-1
Cloud-init v. 0.7.6 finished at Mon, 01 Jul 2019 09:19:16 +0000. Datasource DataSourceEc2. Up 692.70 seconds
まとめ
AMBのリソースをCFnで管理するのにAWS Lambda-backedカスタムリソースを利用することができましたが、そこそこハマるところがあり、テンプレート作成に時間がかかりました。
1度作成できたら応用を効かせることができそうですので、個人的には良いテンプレートができたなと思ってます^^
参考
Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークをさくっと構築するAWS CloudFormationのテンプレートを作ってみた(使い方編) - Qiita
https://qiita.com/kai_kou/items/dc7dbe947a7cc6d0a8db
kai-kou/amazon-managed-blockchain-cfn-template
https://github.com/kai-kou/amazon-managed-blockchain-cfn-template
AWS Resource and Property Types Reference - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
Release History - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/ReleaseHistory.html
AWS SDK for Python(boto3)でAmazon Managed Blockchainのブロックチェーンネットワークを作成してみた - Qiita
https://qiita.com/kai_kou/items/9e16274b6a5c30aa6086
AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 - Qiita
https://qiita.com/kai_kou/items/7be2eb9a36611bb5da12
DependsOn 属性 - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html
AWS CloudFormationのLambda-Backedカスタムリソースでリソース作成を待ち受けできるようにする - Qiita
https://qiita.com/kai_kou/items/57699a4e84ded2c1bfc4
New – Amazon Managed Blockchain – Create & Manage Scalable Blockchain Networks | AWS News Blog
https://aws.amazon.com/jp/blogs/aws/new-amazon-managed-blockchain-create-manage-scalable-blockchain-networks
boto3/CHANGELOG.rst at develop · boto/boto3
https://github.com/boto/boto3/blob/develop/CHANGELOG.rst#19139
AWS CloudFormationのAWS Lambda-backedカスタムリソースで最新のAWS SDKを利用する - Qiita
https://qiita.com/kai_kou/items/db0924f8bbd0fd03eb94
カスタムリソースの応答オブジェクト - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html
AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない - Qiita
https://qiita.com/kai_kou/items/61a2b3c69ae2af4f2e40
AWS CloudFormationのLambda-BackedカスタムリソースでネストされてるっぽいJSONを返す方法 - Qiita
https://qiita.com/kai_kou/items/d030117c694531410203
awslabs/amazon-managed-blockchain-client-templates: AWS CloudFormation templates to provision Amazon EC2 instances and install and configure clients for use with blockchain frameworks in Amazon Managed Blockchain
https://github.com/awslabs/amazon-managed-blockchain-client-templates
cfn-signal - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-signal.html
Fn::Sub - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html
CloudFormationの中のEC2のユーザーデータでシェル変数を使用する | DevelopersIO
https://dev.classmethod.jp/cloud/aws/using-variables-in-ec2-user-data-in-cloudformation/
Linux インスタンスでの起動時のコマンドの実行 - Amazon Elastic Compute Cloud
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html
AWSのCloud initのログの場所 | しびら
http://yamada.daiji.ro/blog/?p=191