cloudpack あら便利カレンダー 2019 の記事となります。誕生秘話 はこちら。
Amazon Managed Blockchain(AMB)でブロックチェーンネットワークを構築するのが手間になったので、AWS CloudFormation(CFn)を利用して構築できるようにしてみました。使い方は下記をご参考ください。
Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークをさくっと構築するAWS CloudFormationのテンプレートを作ってみた(使い方編) – Qiita
https://cloudpack.media/48077
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://cloudpack.media/47241
AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 – Qiita
https://cloudpack.media/48205
リソースの作成順を制御する
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://cloudpack.media/48222
このリソース作成を待ち受ける実装と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
エラー例
Lambda関数の実装
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://cloudpack.media/48058
AMBリソースの更新・削除に対応する
AMBリソースはAWS Lambda-backedカスタムリソースで作成しているので、更新や削除もLambda-backedカスタムリソースで行う必要があります。
詳細は下記が参考になりますが、こちらもリソース作成の待受と同じく、作成とは別のカスタムリソースを定義する必要があります。
AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 – Qiita
https://cloudpack.media/48205
作成待ちと更新・削除を担うカスタムリソースは共通化できたので、最終的には下記のようなリソース定義となりました。
テンプレート抜粋
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://cloudpack.media/48318
なので、AWS SDKでAMBのリソース情報を取得して得られるJSONを加工する必要があります。
JSONのキーをNetwork.Id
とすると、CFn側でもNetwork.Id
と参照できるようにしています。
LambdaからCFnにネットワーク情報を返す例
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://cloudpack.media/48329
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
を定義する必要があります。
EC2インスタンス定義_一部抜粋
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で変数を利用する例
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ノードへのデータ送信待ち
UserData一部抜粋
(略) /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
EC2インスタンス内
$ 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://cloudpack.media/48077
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://cloudpack.media/47241
AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 – Qiita
https://cloudpack.media/48205
DependsOn 属性 – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html
AWS CloudFormationのLambda-Backedカスタムリソースでリソース作成を待ち受けできるようにする – Qiita
https://cloudpack.media/48222
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://cloudpack.media/48058
カスタムリソースの応答オブジェクト – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html
AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない – Qiita
https://cloudpack.media/48318
AWS CloudFormationのLambda-BackedカスタムリソースでネストされてるっぽいJSONを返す方法 – Qiita
https://cloudpack.media/48329
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