通过Docker搭建4节点的Tendermint集群
Tendermint:0.34.24
Docker:20.10.21
Docker-Compose:2.20.2
OS:Ubuntu 20.04
Go:1.19.2 Linux/amd64
1 修改Tendermint源码
1.1 修改监听IP
为什么要将127.0.1修改成0.0.0.0呢?因为容器内的服务如果是以127.0.0.1暴露的话,外部是无法通过端口映射访问docker容器内对应服务的。
127.0.0.1是一个特殊的IP地址,称为本地回环地址,只能用于在同一台计算机上的进程之间进行通信。当您将服务绑定到127.0.0.1地址时,它将只能在本机进行访问,无法通过外部网络访问该应用程序。

1.2 不产生空区块

1.3 统一ChainID

1.4 修改AddBookStrict为false,用来支持局域网通信

2 编译生成二进制可执行文件
make install
make install_abci
3 编写dockerfile定制镜像
FROM ubuntu
ENV MYPATH /usr/local
WORKDIR $MYPATH
ADD ./tendermint $MYPATH
ADD ./abci-cli $MYPATH
RUN apt update
RUN apt install curl
注意要将abci-cli和tendermint可执行文件与dockerfile文件放在同一目录下:

查看定制的docker镜像:

4 编写docker-compose编排容器
网络采用桥接模式,并对每个节点的RPC服务端口26657进行外部映射。
version: "3"
services:node0:image: tmcontainer_name: node0ports:- "26657:26657"volumes:- /usr/local/node0:$HOME/.tendermintnetworks:mynet:ipv4_address: 172.20.20.0command: - /bin/bash- -c- |./tendermint init./tendermint show-node-id > $HOME/.tendermint/nodeid.txt./abci-cli kvstorenode1:image: tmcontainer_name: node1ports:- "36657:26657"volumes:- /usr/local/node1:$HOME/.tendermintnetworks:mynet:ipv4_address: 172.20.20.1command:- /bin/bash- -c- |./tendermint init./tendermint show-node-id > $HOME/.tendermint/nodeid.txt./abci-cli kvstorenode2:image: tmcontainer_name: node2ports:- "46657:26657"networks:mynet:ipv4_address: 172.20.20.2volumes:- /usr/local/node2:$HOME/.tendermintcommand:- /bin/bash- -c- |./tendermint init./tendermint show-node-id > $HOME/.tendermint/nodeid.txt./abci-cli kvstorenode3: image: tmcontainer_name: node3ports:- "56657:26657"networks:mynet:ipv4_address: 172.20.20.3volumes: - /usr/local/node3:$HOME/.tendermintcommand:- /bin/bash- -c- |./tendermint init./tendermint show-node-id > $HOME/.tendermint/nodeid.txt./abci-cli kvstore
networks:mynet:ipam:driver: defaultconfig:- subnet: 172.20.0.0/16
启动容器:
docker-compose up -d

5 统一的genesis.json
因为容器卷的缘故,宿主机本地可以直接查看每个节点的config和data目录。
集群要求每个节点的genesis.json文件完全相同,并且包含所有节点的validator信息。
这里通过一个简单的Python脚本快速构建统一的genesis.json文件:
linesArr = []for i in range(0, 4):f = open("/usr/local/node%d/config/genesis.json" % i, 'r')linesArr.append(f.readlines())f.close()f = open("./genesis.json", 'w')for i in range(len(linesArr)):lines = linesArr[i]if i == 0:for j in range(23):f.write(lines[j])for j in range(23, 32):if i == len(linesArr) - 1:f.write(lines[j])else:if j < 31:f.write(lines[j])else:f.write(lines[j][:len(lines[j]) - 1])f.write(",\n")for i in range(32, len(linesArr[0])):f.write(linesArr[0][i])f.close()
生成的统一的genesis.json创世文件:
{"genesis_time": "2024-01-06T02:17:15.806171802Z","chain_id": "test-chain","initial_height": "0","consensus_params": {"block": {"max_bytes": "22020096","max_gas": "50","time_iota_ms": "1000"},"evidence": {"max_age_num_blocks": "100000","max_age_duration": "172800000000000","max_bytes": "1048576"},"validator": {"pub_key_types": ["ed25519"]},"version": {}},"validators": [{"address": "6DE6C6F2CD5AD081A1B6C7A87FC915E04B1E2219","pub_key": {"type": "tendermint/PubKeyEd25519","value": "iKt2epWBuZHFayrS4qb7AJAwbfSlrJxOsLSbwwsUn9A="},"power": "10","name": ""},{"address": "5993A76300771C1EF633D6801FF53D5B6527127A","pub_key": {"type": "tendermint/PubKeyEd25519","value": "uGc87MkuEtnMTcIQGUD22mNdTAQ7400FDqtIOkctbDg="},"power": "10","name": ""},{"address": "039D64F5C23FD4CA0E4563B282B0B6F96CE278CB","pub_key": {"type": "tendermint/PubKeyEd25519","value": "FWFS6CuHP+iOk8tktKfMm1A8WJIldFP+chjDj9AbD78="},"power": "10","name": ""},{"address": "73BFE884FB6C97F170FED9A9699EDB1B3E5341C4","pub_key": {"type": "tendermint/PubKeyEd25519","value": "xpPW7WNXZYJnUCOLT/Uv1czyfLEhpiDo7jN/DS/Ad2s="},"power": "10","name": ""}],"app_hash": ""
}
覆盖掉原来的genesis.json文件:

6 启动集群
如果没在docker-compose手动指定各容器的IP,则通过docker inspect查询各容器的IP:

查看每个节点的nodeID,由于容器卷以及容器启动时会将nodeID重定向到nodeid.txt文件,我们在宿主机本地就能够访问。

最后构造出统一的启动命令:
./tendermint node --p2p.persistent_peers=“15352fccfb6a2a177fa18253bfb4bd6cd71c0894@172.20.20.0:26656,c61f1ed46fef14b5d518dbe5f9831134cba72518@172.20.20.1:26656,891df109a91fb4fc97c936594aa694206fdbb8de@172.20.20.2:26656,fba18b7bc2d04a6a5aac41a40ab5f230be51b031@172.20.20.3:26656”
7 验证集群是否启动成功
7.1 第一种验证方式:

可以看到,node2的peer数量为3
7.2 第二种验证方式:
向node0发送一条name=jackie的交易:

分别从node1、node2、node3查询交易,得到的结果是base64格式:

对返回的结果进行base64解码:

不过,通过命令行提交交易太繁琐了,我们干脆写个简单的测试程序,向node0提交500个交易:
func main() {cli, err := http.New("http://localhost:26657", "/websocket")if err != nil {panic(err)}for i := 0; i < 500; i++ {_, err = cli.BroadcastTxAsync(context.TODO(), types.Tx(strconv.Itoa(i)))if err != nil {panic(err)}}
}
会发现四个节点的区块全部同步到同一个高度:

如果我们让节点node0宕机,会发现其他三个节点会打印出node0宕机的信息:

最后优雅关闭所有容器:

至此。
相关文章:
通过Docker搭建4节点的Tendermint集群
Tendermint:0.34.24 Docker:20.10.21 Docker-Compose:2.20.2 OS:Ubuntu 20.04 Go:1.19.2 Linux/amd64 1 修改Tendermint源码 1.1 修改监听IP 为什么要将127.0.1修改成0.0.0.0呢?因为容器内的服务如果是以…...
element plus 表格组件怎样在表格中显示图片
官方给的: <el-table-column label"Thumbnail" width"180"><template #default"scope"><div style"display: flex; align-items: center"><el-image :preview-src-list"srcList"/><…...
cad快速看图软件免费版(手机在线cad快速看图)
cad快速看图软件免费版(手机在线cad快速看图) 很多机械设计师日常工作过程中涉及到多种格式的cad图纸,cad图纸大多都需要cad设计软件才能打开,然而很多小伙伴并没有下载相应的cad设计软件,这种情况下如何进行cad快速看图呢? 今天…...
C#: Label、TextBox 鼠标停留时显示提示信息
说明:记录在 Label、TextBox 控件上 鼠标停留时显示提示信息的方法。 1.效果图 2.具体实现步骤 1. 在Form 窗口中先创建 Label 并取名:KEY_label ,或 TextBox 取名:KEY_textBox 2. lable控件的 tips 实现方法1 :代码…...
.NET 8.0 本机 AOT
在软件开发领域,优化性能和简化效率仍然至关重要。.NET 平台二十年来不断创新,为开发人员提供了构建弹性且高效的软件解决方案的基础架构。 与本机 AOT(提前)编译相结合,取得了显着的进步。本文深入研究.NET Native AO…...
phpcms v9未审核推荐信息出现在推荐列表中【BUG修复】
修改文件:phpcms/modules/content/class/content_tag.class.php 调整过的方法: public function __construct() {$this->db pc_base::load_model(content_model);$this->position pc_base::load_model(position_model);$this->position_da…...
Linux第20步_在虚拟机上安装“Visual Studio Code”
1、双击windows系统桌面上的“FileZilla Client.exe”,打开FTP客户端,点击03软件下的Visual Studio Code,发现code_1.50.1-1602600906_amd64。 2、点击“文件”,然后点击“站点管理器”,见下图操作: 3、点…...
【服务器数据恢复】Raid5热备盘同步失败导致lvm结构损坏的数据恢复案例
服务器数据恢复环境: 两组由4块磁盘组建的raid5磁盘阵列,两组raid5阵列划分为lun并组成了lvm结构,ext3文件系统。 服务器故障: 一组raid5阵列中的一块硬盘离线,热备盘自动上线并开始同步数据。在热备盘完成同步之前&am…...
react+AntDesign 之 pc端项目案例
1.环境搭建以及初始化目录 CRA是一个底层基于webpack快速创建React项目的脚手架工具 # 使用npx创建项目 npx create-react-app react-jike# 进入到项 cd react-jike# 启动项目 npm start2.安装SCSS SASS 是一种预编译的 CSS,支持一些比较高级的语法,…...
实验笔记之——基于COLMAP的Instant-NGP与3D Gaussian Splatting的对比
之前博客进行了COLMAP在服务器下的测试 实验笔记之——Linux实现COLMAP-CSDN博客文章浏览阅读794次,点赞24次,收藏6次。学习笔记之——NeRF SLAM(基于神经辐射场的SLAM)-CSDN博客NeRF 所做的任务是 Novel View Synthesis…...
实战环境搭建-linux下安装悟空CRM
下载地址如下: 链接:https://pan.baidu.com/s/1OI9EA8Nc8ymWlERS9i0vjg?pwd=ws5c 提取码:ws5c 上传crm的程序包,如下图: 输入 unzip 72crm-java-master.zip 进行解压 create database crm9; use crm9; source /opt/72crm-java-master/docs/crm9.sql 修改/home/wukongcr…...
Redis 7.0性能大揭秘:如何优化缓存命中率?
Redis 7.0,这货不仅仅是一个简单的缓存工具,它更是一款高性能的数据结构服务器。现在,大家都知道缓存命中率对性能影响特别大,但怎么优化它呢? 本文,已收录于,我的技术网站 ddkk.com࿰…...
【深度学习每日小知识】Data Augmentation 数据增强
数据增强是通过对原始数据进行各种转换和修改来人工生成附加数据的过程,旨在增加机器学习模型中训练数据的大小和多样性。这对于计算机视觉领域尤为重要,因为图像经常被用作输入数据。 计算机视觉中的数据增强 数据增强的主要目标是解决过拟合问题&…...
网络调试 UDP1,开发板用动态地址-入门6
https://www.bilibili.com/video/BV1zx411d7eC?p11&vd_source109fb20ee1f39e5212cd7a443a0286c5 1, 开发板连接路由器 1.1,烧录无OS UDP例程 1.2,Mini USB连接电脑 1.3,开发板LAN接口连接路由器 2. Ping开发板与电脑之间通信* 2.1 根据…...
【Gin实战教程】快速入门
Gin是一个轻量级的Web框架,使用Go语言开发。它具有高性能、易用性和灵活性的特点,是构建可扩展的Web应用程序的理想选择。 首先,Gin是一个高性能的框架。它基于Go语言的原生HTTP包进行开发,利用了Go语言的并发特性和协程模型&…...
WPF真入门教程26--项目案例--欧姆龙PLC通讯工具
1、案例介绍 前面已经完成了25篇的文章介绍,概括起来就是从0开始,一步步熟悉了wpf的概念,UI布局控件,资源样式文件的使用,MVVM模式介绍,命令Command等内容,这节来完成一个实际的项目开发&#…...
C++ OpenGL 3D Game Tutorial 2: Making OpenGL 3D Engine学习笔记
视频地址https://www.youtube.com/watch?vPH5kH8h82L8&listPLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg&index3 一、main类 接上一篇内容,main.cpp的内容增加了一些代码,显得严谨一些: #include<OGL3D/Game/OGame.h> #include<i…...
Redis小计(4)
目录 1.Set和Get操作 2.mset和mget 3.mset,mget,set后加参数的优点 4.incr,incrby,incrbyfloat 1.Set和Get操作 flushall:清除所有k-v键值对。(删库跑路小技巧) set k v[ex | px]:设置超时…...
【React】常用Hook函数的梳理和总结(第二篇)
1. 前言 本篇梳理和总结React中常用的Hook函数。 欢迎大家来到Hook的世界,真的贼好用~ 2. 常用Hook函数 Hook说明示例useState(initialState) 功能:为函数组件添加状态变量,输入可是基本数据类型或引用数据类型,也可以是不带参数…...
【JaveWeb教程】(15) SpringBootWeb之 响应 详细代码示例讲解
目录 SpringBootWeb请求响应2. 响应2.1 ResponseBody2.2 统一响应结果2.3 案例2.3.1 需求说明2.3.2 准备工作2.3.3 实现步骤2.3.4 代码实现2.3.5 测试2.3.6 问题分析 SpringBootWeb请求响应 2. 响应 前面我们学习过HTTL协议的交互方式:请求响应模式(有…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...
