{"id":46216,"date":"2019-08-27T22:56:01","date_gmt":"2019-08-27T19:56:01","guid":{"rendered":"https:\/\/www.altoros.com\/blog\/?p=46216"},"modified":"2019-11-14T19:14:28","modified_gmt":"2019-11-14T16:14:28","slug":"consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft","status":"publish","type":"post","link":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/","title":{"rendered":"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_79_2 counter-hierarchy ez-toc-counter ez-toc-transparent ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#A_new_ordering_service_available\" >A new ordering service available<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Launch_a_network_with_a_Kafka_orderer\" >Launch a network with a Kafka orderer<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Determine_the_amount_of_channels_to_modify\" >Determine the amount of channels to modify<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Put_the_network_into_the_maintenance_mode\" >Put the network into the maintenance mode<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Migrate_actually\" >Migrate, actually<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Disable_the_maintenance_mode\" >Disable the maintenance mode<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Execute_the_chaincode\" >Execute the chaincode<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#Further_reading\" >Further reading<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#About_the_author\" >About the author<\/a><\/li><\/ul><\/nav><\/div>\n<h3><span class=\"ez-toc-section\" id=\"A_new_ordering_service_available\"><\/span>A new ordering service available<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Kafka-based consensus (ordering service) poses a viable fault-tolerant solution for Hyperl\u0443dger Fabric to be used in a production-grade network. Since version 1.4.1, the Fabric introduced the <a href=\"https:\/\/hyperledger-fabric.readthedocs.io\/en\/release-1.4\/whatsnew.html#raft-ordering-service\" rel=\"noopener noreferrer\" target=\"_blank\">Raft ordering service<\/a>. With this option, you get a distributed fault-tolerant service, which is easier to set up and maintain. It can extend a network with new organizations, as we don\u2019t need to rely on a third-party Kafka cluster.<\/p>\n<p>In the previous versions of the platform, it was impossible to change a consensus type of a blockchain network without full redeployment. However, Hyperledger Fabric v1.4.2 introduced a mechanism that makes it possible to migrate a network from a Kafka consensus to a Raft-based one.<\/p>\n<p>The <a href=\"https:\/\/hyperledger-fabric.readthedocs.io\/en\/release-1.4\/kafka_raft_migration.html\" rel=\"noopener noreferrer\" target=\"_blank\">official documentation<\/a> for this version describes the migration process from a high-level perspective, assuming that a user has sufficient expertise around channel configuration update transactions. So, we decided to provide a more detailed, step-by-step tutorial\u2014exemplified on the &#8220;Building Your First Network&#8221; (<a href=\"https:\/\/hyperledger-fabric.readthedocs.io\/en\/release-1.4\/build_network.html\" rel=\"noopener noreferrer\" target=\"_blank\">BYFN<\/a>) scenario. We also deliver recommendations on configuring the Raft ordering service and testing chaincode invocation.<\/p>\n<p>To follow the instructions below, you need to be familiar with the basics of Hyperledger Fabric architecture, as well as Docker Compose. Before proceeding with our tutorial, please check out the <a href=\"https:\/\/hyperledger-fabric.readthedocs.io\/en\/release-1.4\/prereqs.html\" rel=\"noopener noreferrer\" target=\"_blank\">official prerequisites<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Launch_a_network_with_a_Kafka_orderer\"><\/span>Launch a network with a Kafka orderer<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>With the following commands, we will clone a repository with the BYFN example and download Docker&#8217;s Hyperledger Fabric images and binary files to create our blockchain network.<\/p>\n<pre>\r\ngit clone https:\/\/github.com\/hyperledger\/fabric-samples.git\r\ncd fabric-samples\r\ngit checkout v1.4.2\r\n\r\ncurl -sS https:\/\/raw.githubusercontent.com\/hyperledger\/fabric\/master\/scripts\/bootstrap.sh -o .\/scripts\/bootstrap.sh\r\n\r\n# Change file mode to executable\r\nchmod +x .\/scripts\/bootstrap.sh\r\n\r\n# Download binaries and docker images\r\n.\/scripts\/bootstrap.sh 1.4.2\r\n<\/pre>\n<p>We recommend to mount <code style=\"color: black; background-color: #e6e6e6;\">workdirectory<\/code> of the CLI container to your host machine. It will help us to edit configuration files with some nice GUI text editors or easily copy configs from a remote to a local machine.<\/p>\n<p>So, open <code style=\"color: black; background-color: #e6e6e6;\">first-network\/docker-compose-cli.yml<\/code>. It should look like in the screenshot below.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-channel-update.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-channel-update-1024x519.png\" alt=\"\" width=\"640\" class=\"aligncenter size-large wp-image-46235\" \/><\/a><\/p>\n<p>Add the string as below.<\/p>\n<pre>\r\n- .\/..\/workdir:\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\r\n<\/pre>\n<p>After that, we may finally launch the network.<\/p>\n<pre>\r\ncd first-network\r\n\r\necho Y | bash byfn.sh -m generate -o kafka\r\necho Y | bash byfn.sh -m up -o kafka\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Determine_the_amount_of_channels_to_modify\"><\/span>Determine the amount of channels to modify<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>First, we need to know a system channel name. In the BYFN scenario, it&#8217;s called <code style=\"color: black; background-color: #e6e6e6;\">byfn-sys-channel<\/code>, while the default name is <code style=\"color: black; background-color: #e6e6e6;\">testchainid<\/code>, so we need to know what name exactly your network has. The easiest way to do it is to check orderer logs.<\/p>\n<pre>\r\ndocker logs orderer.example.com\r\n<\/pre>\n<p>By running this command, you will get the output with the system channel name as highlighted in the screenshot below.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-configue-file.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-configue-file-1024x502.png\" alt=\"\" width=\"640\" class=\"aligncenter size-large wp-image-46229\" \/><\/a><\/p>\n<p>Remember this value, we will need it in the future.<\/p>\n<p>After that, we need a full list of channels inside your Hyperledger Fabric network. To obtain it, use the <code style=\"color: black; background-color: #e6e6e6;\">peer<\/code> command in the CLI container. In our case, we will have only one additional channel to modify, it&#8217;s called <code style=\"color: black; background-color: #e6e6e6;\">mychannel<\/code>.<\/p>\n<pre>\r\n# Login to cli container\r\ndocker exec -ti cli bash\r\n\r\n# Determine channel names, peer has joined. We will need to modify all of those channels plus a system one.\r\npeer channel list\r\n<\/pre>\n<p>After running the command, you will get the following output.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Htperledger-channel-peers.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Htperledger-channel-peers-1024x500.png\" alt=\"\" width=\"640\" class=\"aligncenter size-large wp-image-46233\" \/><\/a><\/p>\n<p>Please be aware that the peer you&#8217;ll have credentials for may not be joined to all of the network channels. So, this may be performed multiple times with several different access rights and configurations.<\/p>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Put_the_network_into_the_maintenance_mode\"><\/span>Put the network into the maintenance mode<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Make sure that you have logged into the CLI container. If not, log in with the following command.<\/p>\n<pre>\r\ndocker exec -ti cli bash\r\n<\/pre>\n<p>In the next section, we will fetch configuration from our working channel called <code style=\"color: black; background-color: #e6e6e6;\">mychannel<\/code>. Make sure that <code style=\"color: black; background-color: #e6e6e6;\">CHANNEL_NAME<\/code> equals to your actual channel name we determined in the previous step.<\/p>\n<pre>\r\n# export all needed env vars\r\nexport ORDERER_CA=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/msp\/tlscacerts\/tlsca.example.com-cert.pem\r\nexport CHANNEL_NAME=mychannel # make sure channel name is correct\r\n\r\n# change work directories\r\nmkdir maintenance_on_$CHANNEL_NAME && cd maintenance_on_$CHANNEL_NAME\r\n\r\n# fetch current channel config\r\npeer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA\r\n\r\n# decode fetched channel config\r\nconfigtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json\r\n\r\n# save old config, to calculate delta in the future\r\ncp config.json config_mod.json\r\n<\/pre>\n<p>Next, we&#8217;ll need to determine how an <a href=\"https:\/\/hyperledger-fabric.readthedocs.io\/en\/release-1.4\/msp.html\" rel=\"noopener noreferrer\" target=\"_blank\">orderer organization MSP<\/a> (Membership Service Provider) is called. Usually, it is specified in <code style=\"color: black; background-color: #e6e6e6;\">configtx.yml<\/code> and equals to <code style=\"color: black; background-color: #e6e6e6;\">OrdererMSP<\/code>.<\/p>\n<pre>\r\ncat config.json | grep name\r\n<\/pre>\n<p>After running the command, we get the following output.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-config-mod.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-config-mod-1024x497.png\" alt=\"\" width=\"640\" class=\"aligncenter size-large wp-image-46236\" \/><\/a><\/p>\n<p>Remember this value, we will need it later.<\/p>\n<p>Then, we need to modify channel configurations by putting them into the maintenance mode. Make sure that <code style=\"color: black; background-color: #e6e6e6;\">CORE_PEER_LOCALMSPID<\/code> has the value you&#8217;ve obtained in the previous step, as well as <code style=\"color: black; background-color: #e6e6e6;\">channel_id<\/code> points to the actual name of the channel we are modifying now.<\/p>\n<pre>\r\n# set maintenance mode in configs\r\nsed -i 's\/NORMAL\/MAINTENANCE\/g' config_mod.json\r\n\r\n# encode old config to protopuf\r\nconfigtxlator proto_encode --input config.json --type common.Config --output config.pb\r\n\r\n# encode new config to protopuf\r\nconfigtxlator proto_encode --input config_mod.json --type common.Config --output modified_config.pb\r\n\r\n# compute delta between configs\r\nconfigtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output config_update.pb\r\n\r\n# decode delta config\r\nconfigtxlator proto_decode --input config_update.pb --type common.ConfigUpdate | jq . > config_update.json\r\n\r\n# wrap delta config with a header\r\necho '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"mychannel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json\r\n\r\n# encode wrapped config to protopuf\r\nconfigtxlator proto_encode --input config_update_envelope.json --type common.Envelope --output config_update_in_envelope.pb\r\n\r\n# sign channel update config\r\npeer channel signconfigtx -f config_update_in_envelope.pb\r\n\r\n# export all needed env vars\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# submit new channel config\r\npeer channel update -f config_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>A successful channel update will look like shown below.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-Docker-Iogs.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-Docker-Iogs-1024x58.png\" alt=\"\" width=\"750\" class=\"aligncenter size-large wp-image-46237\" \/><\/a><\/p>\n<p>Repeat these steps for all of your channels.<\/p>\n<p>Next, we need to put a system channel\u2014in our case, it\u2019s <code style=\"color: black; background-color: #e6e6e6;\">byfn-system-channel<\/code>\u2014to maintenance. The steps are pretty much the same, so, make sure all the <code style=\"color: black; background-color: #e6e6e6;\">channel names<\/code> and <code style=\"color: black; background-color: #e6e6e6;\">orderer msp<\/code> are configured correctly, which should be performed in the CLI container.<\/p>\n<pre>\r\n# export all needed env vars\r\nexport CHANNEL_NAME=byfn-sys-channel\r\n\r\n# change work directories\r\ncd ..\r\nmkdir maintenance_on_$CHANNEL_NAME && cd maintenance_on_$CHANNEL_NAME\r\n\r\n# fetch current channel config\r\npeer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA\r\n\r\n# decode fetched channel config\r\nconfigtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json\r\n\r\n# save old config, to calculate delta in the future\r\ncp config.json config_mod.json\r\n\r\n# set maintenance mode in configs\r\nsed -i 's\/NORMAL\/MAINTENANCE\/g' config_mod.json\r\n\r\n# encode old config to protopuf\r\nconfigtxlator proto_encode --input config.json --type common.Config --output config.pb\r\n\r\n# encode new config to protopuf\r\nconfigtxlator proto_encode --input config_mod.json --type common.Config --output modified_config.pb\r\n\r\n# compute delta between configs\r\nconfigtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output config_update.pb\r\n\r\n# decode delta config\r\nconfigtxlator proto_decode --input config_update.pb --type common.ConfigUpdate | jq . > config_update.json\r\n\r\n# wrap delta config with a header\r\necho '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"byfn-sys-channel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json\r\n\r\n# encode wrapped config to protopuf\r\nconfigtxlator proto_encode --input config_update_envelope.json --type common.Envelope --output config_update_in_envelope.pb\r\n\r\n# sign channel update config\r\npeer channel signconfigtx -f config_update_in_envelope.pb\r\n\r\n# export all needed env vars\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# submit new channel config\r\npeer channel update -f config_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>After all the configuration updates were performed successfully, log out from the CLI container.<\/p>\n<pre>exit<\/pre>\n<p>Then, restart all the containers.<\/p>\n<pre>docker restart $(docker ps -a | grep \"hyperledger\/fabric\" | awk '{print $1}')<\/pre>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Migrate_actually\"><\/span>Migrate, actually<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Log in back to the CLI container.<\/p>\n<pre>\r\ndocker exec -ti cli bash\r\n<\/pre>\n<p>Then, make sure that your code has the following properties:<\/p>\n<ul>\n<li><code style=\"color: black; background-color: #e6e6e6;\">export CHANNEL_NAME=mychannel<\/code><\/li>\n<li><code style=\"color: black; background-color: #e6e6e6;\">export CORE_PEER_LOCALMSPID=\"OrdererMSP\"<\/code><\/li>\n<li><code style=\"color: black; background-color: #e6e6e6;\">echo<\/code><br \/>\n<code style=\"color: black; background-color: #e6e6e6;\">'{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"mychannel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json<\/code><\/li>\n<\/ul>\n<p>Actually, those are the values we determined earlier. If everything is okey, you can start the migration.<\/p>\n<pre>\r\n# export all needed env vars\r\nexport ORDERER_CA=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/msp\/tlscacerts\/tlsca.example.com-cert.pem\r\nexport CHANNEL_NAME=mychannel\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# change work directories\r\nmkdir switch_to_raft_${CHANNEL_NAME} && cd switch_to_raft_${CHANNEL_NAME}\r\n\r\n# fetch current channel config\r\npeer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA\r\n\r\n# decode fetched channel config\r\nconfigtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json\r\n\r\n# save old config, to calculate delta in the future\r\ncp config.json config_mod.json\r\n<\/pre>\n<p>Open <code style=\"color: black; background-color: #e6e6e6;\">config_mod.json<\/code> in the editor of choice. It should be located in the <code style=\"color: black; background-color: #e6e6e6;\">workdir\/switch_to_raft_mychannel<\/code> directory of your repository, as we mounted it earlier. Find the <code style=\"color: black; background-color: #e6e6e6;\">ConsensusType<\/code> block. We need to modify the <code style=\"color: black; background-color: #e6e6e6;\">metadata<\/code> and <code style=\"color: black; background-color: #e6e6e6;\">type<\/code> fields to make them look similar to what is displayed below.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n&quot;ConsensusType&quot;: {\r\n            &quot;mod_policy&quot;: &quot;Admins&quot;,\r\n            &quot;value&quot;: {\r\n              &quot;metadata&quot;: {\r\n                &quot;consenters&quot;: &#x5B;\r\n                  {\r\n                    &quot;client_tls_cert&quot;: &quot;LS0tLS1&lt;\u2026&gt;LS0tLQo=&quot;,\r\n                    &quot;host&quot;: &quot;orderer.example.com&quot;,\r\n                    &quot;port&quot;: 7050,\r\n                    &quot;server_tls_cert&quot;: &quot;LS0tLS1&lt;...&gt;tLQo=&quot;\r\n                  }\r\n                ],\r\n                &quot;options&quot;: {\r\n                  &quot;election_tick&quot;: 10,\r\n                  &quot;heartbeat_tick&quot;: 1,\r\n                  &quot;max_inflight_blocks&quot;: 5,\r\n                  &quot;snapshot_interval_size&quot;: 20971520,\r\n                  &quot;tick_interval&quot;: &quot;500ms&quot;\r\n                }\r\n              },\r\n              &quot;state&quot;: &quot;STATE_MAINTENANCE&quot;,\r\n              &quot;type&quot;: &quot;etcdraft&quot;\r\n            },\r\n            &quot;version&quot;: &quot;1&quot;\r\n          }\r\n<\/pre>\n<p>The <code style=\"color: black; background-color: #e6e6e6;\">client_tls_cert<\/code> and <code style=\"color: black; background-color: #e6e6e6;\">server_tls_cert<\/code> fields are actually equal and should contain the <code style=\"color: black; background-color: #e6e6e6;\">base64-encoded<\/code> orderer service certificate. You can obtain this value by executing the following command in the CLI container we&#8217;ve already opened.<\/p>\n<pre>\r\nbase64 \/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/server.crt -w0 && echo \"\"\r\n<\/pre>\n<p>After the modification is done, save the file and go back to your CLI container console. Check that you have a correct channel ID in these properties:<\/p>\n<ul>\n<li><code style=\"color: black; background-color: #e6e6e6;\">echo<br \/>\n'{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"mychannel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json<\/code><\/li>\n<li><code style=\"color: black; background-color: #e6e6e6;\">CORE_PEER_LOCALMSPID<\/code><\/li>\n<\/ul>\n<p>The following command will perform a channel configuration update after encoding channel JSON configurations to the <code style=\"color: black; background-color: #e6e6e6;\">protobuf<\/code> files.<\/p>\n<pre>\r\n# encode old config to protopuf\r\nconfigtxlator proto_encode --input config.json --type common.Config --output config.pb\r\n\r\n# encode new config to protopuf\r\nconfigtxlator proto_encode --input config_mod.json --type common.Config --output modified_config.pb\r\n\r\n# compute delta between configs\r\nconfigtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output config_update.pb\r\n\r\n# decode delta config\r\nconfigtxlator proto_decode --input config_update.pb --type common.ConfigUpdate | jq . > config_update.json\r\n\r\n# wrap delta config with a header\r\necho '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"mychannel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json\r\n\r\n# encode wrapped config to protopuf\r\nconfigtxlator proto_encode --input config_update_envelope.json --type common.Envelope --output config_update_in_envelope.pb\r\n\r\n# sign channel update config\r\npeer channel signconfigtx -f config_update_in_envelope.pb\r\n\r\n# export all needed env vars\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# submit new channel config\r\npeer channel update -f config_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>Repeat these steps for all your channels. Next, we need to perform absolutely the same changes in our system channel.<\/p>\n<pre>\r\nexport CHANNEL_NAME=byfn-sys-channel\r\ncd ..\r\n# change work directories\r\nmkdir switch_to_raft_${CHANNEL_NAME} && cd switch_to_raft_${CHANNEL_NAME}\r\n\r\n# fetch current channel config\r\npeer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA\r\n\r\n# decode fetched channel config\r\nconfigtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json\r\n\r\n# save old config, to calculate delta in the future\r\ncp config.json config_mod.json\r\n<\/pre>\n<p>Make sure to adjust <code style=\"color: black; background-color: #e6e6e6;\">config_mod.json<\/code> as desplayed below.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-system-channel.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-system-channel-1024x369.png\" alt=\"\" width=\"640\" class=\"aligncenter size-large wp-image-46232\" \/><\/a><\/p>\n<p>Finally, perform the system channel upgrade, but don&#8217;t forget to check channel names and <code style=\"color: black; background-color: #e6e6e6;\">orderer msp<\/code>.<\/p>\n<pre>\r\n# encode old config to protopuf\r\nconfigtxlator proto_encode --input config.json --type common.Config --output config.pb\r\n\r\n# encode new config to protopuf\r\nconfigtxlator proto_encode --input config_mod.json --type common.Config --output modified_config.pb\r\n\r\n# compute delta between configs\r\nconfigtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output config_update.pb\r\n\r\n# decode delta config\r\nconfigtxlator proto_decode --input config_update.pb --type common.ConfigUpdate | jq . > config_update.json\r\n\r\n# wrap delta config with a header\r\necho '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"byfn-sys-channel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json\r\n\r\n# encode wrapped config to protopuf\r\nconfigtxlator proto_encode --input config_update_envelope.json --type common.Envelope --output config_update_in_envelope.pb\r\n\r\n# sign channel update config\r\npeer channel signconfigtx -f config_update_in_envelope.pb\r\n\r\n# export all needed env vars\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# submit new channel config\r\npeer channel update -f config_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>If everything has been done properly and you passed the step without any error, log out from the CLI container.<\/p>\n<pre>exit<\/pre>\n<p>Then, restart the network.<\/p>\n<pre>docker restart $(docker ps -a | grep \"hyperledger\/fabric\" | awk '{print $1}')<\/pre>\n<p>After 10\u201320 seconds, check orderer logs to understand whether migration was completed successfully\u2014by running the following command.<\/p>\n<pre>\r\ndocker logs orderer.example.com\r\n<\/pre>\n<p>Below, you can see the output provided by Docker logs.<\/p>\n<p><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-OrdererMSP1.png\"><img decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-OrdererMSP1-1024x329.png\" alt=\"\" width=\"640\" class=\"aligncenter size-large wp-image-46249\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Disable_the_maintenance_mode\"><\/span>Disable the maintenance mode<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>After migration is completed, we can disable the maintenance mode and get rid of the Kafka cluster. Run the following commands to disable the maintenance mode on the working channel, but, first make sure that channel names and <code style=\"color: black; background-color: #e6e6e6;\">orderer msp<\/code> are specified correctly.<\/p>\n<p>Log in to the CLI container.<\/p>\n<pre>\r\ndocker exec -ti cli bash\r\n<\/pre>\n<p>Then, submit the channel update.<\/p>\n<pre>\r\n# export all needed env vars\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\nexport ORDERER_CA=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/msp\/tlscacerts\/tlsca.example.com-cert.pem\r\nexport CHANNEL_NAME=mychannel\r\n\r\n# change working directory\r\nmkdir maintenance_off_$CHANNEL_NAME && cd maintenance_off_$CHANNEL_NAME\r\n\r\n# fetch current channel config\r\npeer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA\r\n\r\n# decode current config\r\nconfigtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json\r\n\r\n# save to perform changes in the configuration\r\ncp config.json config_mod.json\r\n\r\n# modify new config file\r\nsed -i 's\/MAINTENANCE\/NORMAL\/g' config_mod.json\r\n\r\n# encode old config to protopuf\r\nconfigtxlator proto_encode --input config.json --type common.Config --output config.pb\r\n\r\n# encode new config to protopuf\r\nconfigtxlator proto_encode --input config_mod.json --type common.Config --output modified_config.pb\r\n\r\n# compute delta in configurations\r\nconfigtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output config_update.pb\r\n\r\n# decode delta\r\nconfigtxlator proto_decode --input config_update.pb --type common.ConfigUpdate | jq . > config_update.json\r\n\r\n# wrap delta with header\r\necho '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"mychannel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json\r\n\r\n# encode delta config\r\nconfigtxlator proto_encode --input config_update_envelope.json --type common.Envelope --output config_update_in_envelope.pb\r\n\r\n# sign config update transaction\r\npeer channel signconfigtx -f config_update_in_envelope.pb\r\n\r\n# export needed env vars\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# submit channel update\r\npeer channel update -f config_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>Repeat for all the channels. Next, we need to switch off the maintenance mode for the system channel.<\/p>\n<pre>\r\nexport CHANNEL_NAME=byfn-sys-channel\r\ncd ..\r\n\r\n# change working directories\r\nmkdir maintenance_on_$CHANNEL_NAME && cd maintenance_on_$CHANNEL_NAME\r\n\r\n# fetch current channel config\r\npeer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA\r\n\r\n# decode fetched config\r\nconfigtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json\r\n\r\n# save config for further modifications\r\ncp config.json config_mod.json\r\n\r\n# modify channel config\r\nsed -i 's\/MAINTENANCE\/NORMAL\/g' config_mod.json\r\n\r\n# encode old channel config\r\nconfigtxlator proto_encode --input config.json --type common.Config --output config.pb\r\n\r\n# encode new channel config\r\nconfigtxlator proto_encode --input config_mod.json --type common.Config --output modified_config.pb\r\n\r\n# compute delta between configuration\r\nconfigtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output config_update.pb\r\n\r\n# decode delta\r\nconfigtxlator proto_decode --input config_update.pb --type common.ConfigUpdate | jq . > config_update.json\r\n\r\n# wrap delta with header\r\necho '{\"payload\":{\"header\":{\"channel_header\":{\"channel_id\":\"byfn-sys-channel\", \"type\":2}},\"data\":{\"config_update\":'$(cat config_update.json)'}}}' | jq . > config_update_envelope.json\r\n\r\n# encode wrapped delta\r\nconfigtxlator proto_encode --input config_update_envelope.json --type common.Envelope --output config_update_in_envelope.pb\r\n\r\n# sign peer channel update\r\npeer channel signconfigtx -f config_update_in_envelope.pb\r\n\r\n# export all needed env vars\r\nexport CORE_PEER_LOCALMSPID=\"OrdererMSP\"\r\nexport CORE_PEER_TLS_ROOTCERT_FILE=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/tls\/ca.crt\r\nexport CORE_PEER_MSPCONFIGPATH=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/users\/Admin@example.com\/msp\/\r\nexport CORE_PEER_ADDRESS=peer0.org1.example.com:7051\r\n\r\n# submit new channel config\r\npeer channel update -f config_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>After that, log out from the CLI container.<\/p>\n<pre>exit<\/pre>\n<p>Then, restart the containers.<\/p>\n<pre>docker restart $(docker ps -a | grep \"hyperledger\/fabric\" | awk '{print $1}')<\/pre>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Execute_the_chaincode\"><\/span>Execute the chaincode<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>To test whether the network actually operates, you can execute the chaincode (<code style=\"color: black; background-color: #e6e6e6;\">mycc<\/code> in our case) with the following commands.<\/p>\n<p>Log in to the CLI container.<\/p>\n<pre>\r\ndocker exec -ti cli bash\r\n<\/pre>\n<p>Then, finally, submit the channel update.<\/p>\n<pre>\r\n# export env vars\r\nexport ORDERER_CA=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/ordererOrganizations\/example.com\/orderers\/orderer.example.com\/msp\/tlscacerts\/tlsca.example.com-cert.pem\r\nexport CHANNEL_NAME=mychannel\r\nexport PEER0_ORG1_CA=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/peerOrganizations\/org1.example.com\/peers\/peer0.org1.example.com\/tls\/ca.crt\r\nexport PEER0_ORG2_CA=\/opt\/gopath\/src\/github.com\/hyperledger\/fabric\/peer\/crypto\/peerOrganizations\/org2.example.com\/peers\/peer0.org2.example.com\/tls\/ca.crt\r\n\r\n# invoke chaincode\r\npeer chaincode invoke -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{\"Args\":[\"invoke\",\"a\",\"b\",\"10\"]}' --tls --cafile $ORDERER_CA\r\n\r\n# query chaincode\r\npeer chaincode invoke -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{\"Args\":[\"query\",\"a\"]}' --tls --cafile $ORDERER_CA\r\n<\/pre>\n<p>So, this is all for the process behind migration from Kafka-based consensus to a single-node Raft. To tune the ordering service and the amount of orderers, refer to the <a href=\"https:\/\/hyperledger-fabric.readthedocs.io\/en\/release-1.4\/raft_configuration.html\" rel=\"noopener noreferrer\" target=\"_blank\">official guide<\/a>. For the source code used in this tutorial, explore <a href=\"https:\/\/github.com\/Altoros\/Hyperledger-fabric-kafka-to-raft-migration\" rel=\"noopener noreferrer\" target=\"_blank\">our GitHub repository<\/a>.<\/p>\n<p>&nbsp;\t \t <\/p>\n<h3><span class=\"ez-toc-section\" id=\"Further_reading\"><\/span>Further reading<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><a href=\"https:\/\/www.altoros.com\/blog\/ansible-fabric-starter-deploy-hyperledger-fabric-to-multiple-instances\/\">Ansible Fabric Starter: Deploy Hyperledger Fabric to Multiple Instances<\/a><\/li>\n<li><a href=\"https:\/\/www.altoros.com\/blog\/securing-a-blockchain-with-a-noninteractive-zero-knowledge-proof\/\">Securing a Blockchain with a Noninteractive Zero-Knowledge Proof<\/a><\/li>\n<li><a href=\"https:\/\/www.altoros.com\/blog\/deploying-a-multi-node-hyperledger-fabric-network-in-5-steps\/\">Deploying a Multi-Node Hyperledger Fabric Network in 5 Steps<\/a><\/li>\n<\/ul>\n<p>&nbsp;\t \t<\/p>\n<h3><span class=\"ez-toc-section\" id=\"About_the_author\"><\/span>About the author<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<div style=\"float: right;\"><a href=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2018\/08\/Gleb-Iodo-bio.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2018\/08\/Gleb-Iodo-bio.png\" alt=\"\" width=\"120\" height=\"120\" class=\"aligncenter size-full wp-image-36131\" \/><\/a><\/div>\n<div style=\"width: 650px;\"><small><b>Hleb Ioda<\/b> is Blockchain DevOps Engineer at Altoros with 5+ years of experience with network engineering. He specializes in network configuration and maintenance, as well as continuous integration and delivery. Hleb&#8217;s interests include deployment automation, cloud-native apps, blockchain, and distributed software. He is also skilled in working with GNU\/Linux and various cloud providers.<\/small><\/div>\n<hr \/>\n<p><center><small>The post was written by Hleb Ioda; edited and published by <a href=\"https:\/\/www.altoros.com\/blog\/author\/sophie.turol\/\">Sophia Turol<\/a> and <a href=\"https:\/\/www.altoros.com\/blog\/author\/alex\/\">Alex Khizhniak<\/a>.<\/small><\/center><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A new ordering service available<\/p>\n<p>Kafka-based consensus (ordering service) poses a viable fault-tolerant solution for Hyperl\u0443dger Fabric to be used in a production-grade network. Since version 1.4.1, the Fabric introduced the Raft ordering service. With this option, you get a distributed fault-tolerant service, which is easier to set up and maintain. [&#8230;]<\/p>\n","protected":false},"author":3,"featured_media":46251,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":"","_links_to":"","_links_to_target":""},"categories":[214],"tags":[672,753],"class_list":["post-46216","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-blockchain","tag-hyperledger"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Consensus in Hyperledger Fabric: Migrating from Kafka to Raft | Altoros<\/title>\n<meta name=\"description\" content=\"These detailed step-by-step instructions explain how to launch a network, determine the number of channels to modify, enable the maintenance mode, etc.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft | Altoros\" \/>\n<meta property=\"og:description\" content=\"A new ordering service available Kafka-based consensus (ordering service) poses a viable fault-tolerant solution for Hyperl\u0443dger Fabric to be used in a production-grade network. Since version 1.4.1, the Fabric introduced the Raft ordering service. With this option, you get a distributed fault-tolerant service, which is easier to set up and maintain. [...]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/\" \/>\n<meta property=\"og:site_name\" content=\"Altoros\" \/>\n<meta property=\"article:published_time\" content=\"2019-08-27T19:56:01+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-11-14T16:14:28+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif\" \/>\n\t<meta property=\"og:image:width\" content=\"1422\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/gif\" \/>\n<meta name=\"author\" content=\"Sophia Turol\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sophia Turol\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"19 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/\",\"url\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/\",\"name\":\"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft | Altoros\",\"isPartOf\":{\"@id\":\"https:\/\/www.altoros.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif\",\"datePublished\":\"2019-08-27T19:56:01+00:00\",\"dateModified\":\"2019-11-14T16:14:28+00:00\",\"author\":{\"@id\":\"https:\/\/www.altoros.com\/blog\/#\/schema\/person\/58194952af19fe7b2b830846e077a58e\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#primaryimage\",\"url\":\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif\",\"contentUrl\":\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif\",\"width\":1422,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.altoros.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.altoros.com\/blog\/#website\",\"url\":\"https:\/\/www.altoros.com\/blog\/\",\"name\":\"Altoros\",\"description\":\"Insight\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.altoros.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.altoros.com\/blog\/#\/schema\/person\/58194952af19fe7b2b830846e077a58e\",\"name\":\"Sophia Turol\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.altoros.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/05\/trello_card-96x96.jpg\",\"contentUrl\":\"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/05\/trello_card-96x96.jpg\",\"caption\":\"Sophia Turol\"},\"description\":\"Sophia Turol is passionate about delivering well-structured articles that cater for picky technical audience. With 3+ years in technical writing and 5+ years in editorship, she enjoys collaboration with developers to create insightful, yet intelligible technical tutorials, overviews, and case studies. Sophie is enthusiastic about deep learning solutions\u2014TensorFlow in particular\u2014and PaaS systems, such as Cloud Foundry.\",\"url\":\"https:\/\/www.altoros.com\/blog\/author\/sophie-turol\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft | Altoros","description":"These detailed step-by-step instructions explain how to launch a network, determine the number of channels to modify, enable the maintenance mode, etc.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/","og_locale":"en_US","og_type":"article","og_title":"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft | Altoros","og_description":"A new ordering service available Kafka-based consensus (ordering service) poses a viable fault-tolerant solution for Hyperl\u0443dger Fabric to be used in a production-grade network. Since version 1.4.1, the Fabric introduced the Raft ordering service. With this option, you get a distributed fault-tolerant service, which is easier to set up and maintain. [...]","og_url":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/","og_site_name":"Altoros","article_published_time":"2019-08-27T19:56:01+00:00","article_modified_time":"2019-11-14T16:14:28+00:00","og_image":[{"width":1422,"height":630,"url":"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif","type":"image\/gif"}],"author":"Sophia Turol","twitter_misc":{"Written by":"Sophia Turol","Est. reading time":"19 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/","url":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/","name":"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft | Altoros","isPartOf":{"@id":"https:\/\/www.altoros.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#primaryimage"},"image":{"@id":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#primaryimage"},"thumbnailUrl":"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif","datePublished":"2019-08-27T19:56:01+00:00","dateModified":"2019-11-14T16:14:28+00:00","author":{"@id":"https:\/\/www.altoros.com\/blog\/#\/schema\/person\/58194952af19fe7b2b830846e077a58e"},"breadcrumb":{"@id":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#primaryimage","url":"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif","contentUrl":"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/08\/Blockchain-Hyperledger-gif2.gif","width":1422,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.altoros.com\/blog\/consensus-in-hyperledger-fabric-migrating-from-kafka-to-raft\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.altoros.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Consensus in Hyperledger Fabric: Migrating from Kafka to Raft"}]},{"@type":"WebSite","@id":"https:\/\/www.altoros.com\/blog\/#website","url":"https:\/\/www.altoros.com\/blog\/","name":"Altoros","description":"Insight","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.altoros.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.altoros.com\/blog\/#\/schema\/person\/58194952af19fe7b2b830846e077a58e","name":"Sophia Turol","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.altoros.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/05\/trello_card-96x96.jpg","contentUrl":"https:\/\/www.altoros.com\/blog\/wp-content\/uploads\/2019\/05\/trello_card-96x96.jpg","caption":"Sophia Turol"},"description":"Sophia Turol is passionate about delivering well-structured articles that cater for picky technical audience. With 3+ years in technical writing and 5+ years in editorship, she enjoys collaboration with developers to create insightful, yet intelligible technical tutorials, overviews, and case studies. Sophie is enthusiastic about deep learning solutions\u2014TensorFlow in particular\u2014and PaaS systems, such as Cloud Foundry.","url":"https:\/\/www.altoros.com\/blog\/author\/sophie-turol\/"}]}},"_links":{"self":[{"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/posts\/46216","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/comments?post=46216"}],"version-history":[{"count":43,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/posts\/46216\/revisions"}],"predecessor-version":[{"id":50597,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/posts\/46216\/revisions\/50597"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/media\/46251"}],"wp:attachment":[{"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/media?parent=46216"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/categories?post=46216"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.altoros.com\/blog\/wp-json\/wp\/v2\/tags?post=46216"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}