在 NodeJS Docker 容器上使用 Hyperledger Fabric Gateway

问题描述 投票:0回答:1

我配置了一个由三个组织组成的 Hyperledger Fabric 网络:一个组织托管三个排序者节点,另外两个组织各包含两个对等点。

为了与网络交互,我正在构建一个 NodeJS + Express 服务器,它将使用

hyperledger/fabric-gateway
模块来建立通信。出于测试目的,我在通道“ch1”上安装了由 Fabric-samples 存储库提供的 Java 版本的 asset-transfer-basic 包。

在我的expressJs服务器中,我配置了一个端点,它将尝试调用网络的“GetAllAssets”函数。

function envOrDefault(key: string, defaultValue: string): string {
    return process.env[key] || defaultValue;
}

const channelName = envOrDefault('CHANNEL_NAME', 'ch1');
const chaincodeName = envOrDefault('CHAINCODE_NAME', 'basic');

// Path to crypto materials.
const cryptoPath = envOrDefault('CRYPTO_PATH', path.resolve('path', 'to', 'org1'));

// Path to admin private key directory.
const keyDirectoryPath = envOrDefault('KEY_DIRECTORY_PATH', path.resolve(cryptoPath, 'path', 'to', 'keystore'));

// Path to admin certificate.
const certPath = envOrDefault('CERT_PATH', path.resolve(cryptoPath, 'path', 'to', 'signcerts', 'cert.pem'));

// Path to peer tls certificate.
const tlsCertPath = envOrDefault('TLS_CERT_PATH', path.resolve(cryptoPath, 'peer1', 'tls-msp', 'tlscacerts', 'tlsca.pem'));

// Gateway peer endpoint.
const peerEndpoint = envOrDefault('PEER_ENDPOINT', 'localhost:7051');

// Gateway peer SSL host name override.
const peerHostAlias = envOrDefault('PEER_HOST_ALIAS', 'peer1-org1');

// MSP of the organization
const mspId = envOrDefault('MSP_ID', 'org1MSP');


// The configuration object
const configurations: Config = new Config(
    keyDirectoryPath, 
    certPath, 
    tlsCertPath, 
    peerEndpoint, 
    peerHostAlias, 
    mspId
);

const utf8Decoder = new TextDecoder();

app.get("/gateway", async (req: Request, res: Response, next: NextFunction) => { 
    const client: grpc.Client = await configurations.createGrpcClient();
    const identity: Identity = await configurations.createIdentity();
    const signer: Signer = await configurations.createSigner();
    
    const gateway: Gateway = connect({
        client,
        identity,
        signer,
        // Default timeouts for different gRPC calls
        evaluateOptions: () => {
            return { deadline: Date.now() + 5000 }; // 5 seconds
        },
        endorseOptions: () => {
            return { deadline: Date.now() + 15000 }; // 15 seconds
        },
        submitOptions: () => {
            return { deadline: Date.now() + 5000 }; // 5 seconds
        },
        commitStatusOptions: () => {
            return { deadline: Date.now() + 60000 }; // 1 minute
        },
    });

    try{
        let network: Network = gateway.getNetwork(channelName);
        let contract: Contract = network.getContract(chaincodeName);

        await getAllAssets(contract);
        
    } finally {
        gateway.close();
        client.close();
    }
})

configurations.createGrpcClient()
configurations.createIdentity()
configurations.createSigner()
定义如下:

async createGrpcClient(): Promise<grpc.Client> {
        const tlsRootCert = await fs.readFile(this._peerTlsPath);
        console.log("TLS Root Cert ----->");
        console.log(tlsRootCert);
        const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
        return new grpc.Client(this._peerEndpoint, tlsCredentials, {
            'grpc.ssl_target_name_override': this._hostAlias,
        });
}

async createGrpcClient(): Promise<grpc.Client> {
        const tlsRootCert = await fs.readFile(this._peerTlsPath);
        console.log("TLS Root Cert ----->");
        console.log(tlsRootCert);
        const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
        return new grpc.Client(this._peerEndpoint, tlsCredentials, {
            'grpc.ssl_target_name_override': this._hostAlias,
        });
}

async createIdentity(): Promise<Identity> {
        const credentials: Buffer = await fs.readFile(this._certPath);
        console.log("Credentials Cert ----->");
        console.log(credentials);
        const mspId: string = this._mspId;
        return { mspId, credentials };
    }

如果我直接在计算机上执行网络服务器,这将起作用,事实上,如果我尝试启动服务器并导航到

/gateway
,这将返回存储在区块链中的资产:

--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger
*** Result: [
  {
    appraisedValue: 300,
    assetID: 'asset1',
    color: 'blue',
    owner: 'Tomoko',
    size: 5
  },
  {
    appraisedValue: 400,
    assetID: 'asset2',
    color: 'red',
    owner: 'Brad',
    size: 5
  },
  {
    appraisedValue: 500,
    assetID: 'asset3',
    color: 'green',
    owner: 'Jin Soo',
    size: 10
  },
  {
    appraisedValue: 600,
    assetID: 'asset4',
    color: 'yellow',
    owner: 'Max',
    size: 10
  },
  {
    appraisedValue: 700,
    assetID: 'asset5',
    color: 'black',
    owner: 'Adrian',
    size: 15
  },
  {
    appraisedValue: 700,
    assetID: 'asset6',
    color: 'white',
    owner: 'Michel',
    size: 15
  }
]

不幸的是,我无法使用 docker 容器重现此行为:我配置了以下 Dockerfile:

Dockerfile:

FROM node:20-alpine

WORKDIR /usr/src/app
COPY . .

RUN npm install
RUN npm run prepare

ENV GRPC_TRACE=all
ENV GRPC_VERBOSITY=DEBUG

EXPOSE 8080

CMD [ "npm", "start" ]

然后使用

docker build . -t test
构建镜像,使用
docker run -p:8080:8080 -v /tmp/hyperledger:/tmp/hyperledger --name test test
启动,最后使用
docker network connect blockchain_fabric-ca test
连接到 Fabric 网络(通过 docker-compose 构建)。

我确定容器已添加到网络中,因为如果我使用

docker network inspect blockchain_fabric-ca
检查网络,“测试”位于活动容器列表中。

如果我在与之前相同的端点上导航,则会收到以下连接错误:

--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger
D 2023-08-10T20:03:40.623Z | channel | (1) dns:localhost:7051 createResolvingCall [0] method="/gateway.Gateway/Evaluate", deadline=2023-08-10T20:03:45.622Z
D 2023-08-10T20:03:40.624Z | resolving_call | [0] Created
D 2023-08-10T20:03:40.624Z | resolving_call | [0] Deadline: 2023-08-10T20:03:45.622Z
D 2023-08-10T20:03:40.625Z | resolving_call | [0] Deadline will be reached in 4997ms
D 2023-08-10T20:03:40.626Z | resolving_call | [0] start called
D 2023-08-10T20:03:40.627Z | dns_resolver | Looking up DNS hostname localhost
D 2023-08-10T20:03:40.628Z | resolving_load_balancer | dns:localhost:7051 IDLE -> CONNECTING
D 2023-08-10T20:03:40.628Z | connectivity_state | (1) dns:localhost:7051 IDLE -> CONNECTING
D 2023-08-10T20:03:40.628Z | channel | (1) dns:localhost:7051 callRefTimer.ref | configSelectionQueue.length=1 pickQueue.length=0
D 2023-08-10T20:03:40.628Z | resolving_call | [0] startRead called
D 2023-08-10T20:03:40.629Z | resolving_call | [0] write() called with message of length 1285
D 2023-08-10T20:03:40.629Z | resolving_call | [0] halfClose called
D 2023-08-10T20:03:40.630Z | dns_resolver | Resolved addresses for target dns:localhost:7051: [::1:7051,127.0.0.1:7051]
D 2023-08-10T20:03:40.631Z | subchannel | (2) ::1:7051 Subchannel constructed with options {
  "grpc.ssl_target_name_override": "peer1-org1"
}
D 2023-08-10T20:03:40.631Z | subchannel_refcount | (2) ::1:7051 refcount 0 -> 1
D 2023-08-10T20:03:40.631Z | subchannel | (3) 127.0.0.1:7051 Subchannel constructed with options {
  "grpc.ssl_target_name_override": "peer1-org1"
}
D 2023-08-10T20:03:40.631Z | subchannel_refcount | (3) 127.0.0.1:7051 refcount 0 -> 1
D 2023-08-10T20:03:40.631Z | subchannel_refcount | (2) ::1:7051 refcount 1 -> 2
D 2023-08-10T20:03:40.632Z | subchannel_refcount | (3) 127.0.0.1:7051 refcount 1 -> 2
D 2023-08-10T20:03:40.632Z | pick_first | Start connecting to subchannel with address ::1:7051
D 2023-08-10T20:03:40.632Z | pick_first | IDLE -> CONNECTING
D 2023-08-10T20:03:40.632Z | resolving_load_balancer | dns:localhost:7051 CONNECTING -> CONNECTING
D 2023-08-10T20:03:40.632Z | channel | (1) dns:localhost:7051 callRefTimer.unref | configSelectionQueue.length=1 pickQueue.length=0
D 2023-08-10T20:03:40.632Z | connectivity_state | (1) dns:localhost:7051 CONNECTING -> CONNECTING
D 2023-08-10T20:03:40.633Z | subchannel | (2) ::1:7051 IDLE -> CONNECTING
D 2023-08-10T20:03:40.634Z | transport | dns:localhost:7051 creating HTTP/2 session to ::1:7051
D 2023-08-10T20:03:40.637Z | channel | (1) dns:localhost:7051 createRetryingCall [1] method="/gateway.Gateway/Evaluate"
D 2023-08-10T20:03:40.637Z | resolving_call | [0] Created child [1]
D 2023-08-10T20:03:40.637Z | retrying_call | [1] start called
D 2023-08-10T20:03:40.637Z | channel | (1) dns:localhost:7051 createLoadBalancingCall [2] method="/gateway.Gateway/Evaluate"
D 2023-08-10T20:03:40.637Z | retrying_call | [1] Created child call [2] for attempt 1
D 2023-08-10T20:03:40.638Z | load_balancing_call | [2] start called
D 2023-08-10T20:03:40.638Z | load_balancing_call | [2] Pick called
D 2023-08-10T20:03:40.638Z | load_balancing_call | [2] Pick result: QUEUE subchannel: null status: undefined undefined
D 2023-08-10T20:03:40.638Z | channel | (1) dns:localhost:7051 callRefTimer.ref | configSelectionQueue.length=0 pickQueue.length=1
D 2023-08-10T20:03:40.638Z | retrying_call | [1] startRead called
D 2023-08-10T20:03:40.638Z | load_balancing_call | [2] startRead called
D 2023-08-10T20:03:40.639Z | retrying_call | [1] write() called with message of length 1290
D 2023-08-10T20:03:40.639Z | load_balancing_call | [2] write() called with message of length 1290
D 2023-08-10T20:03:40.639Z | retrying_call | [1] halfClose called
D 2023-08-10T20:03:40.641Z | transport | dns:localhost:7051 connection failed with error connect EADDRNOTAVAIL ::1:7051 - Local (:::0)
D 2023-08-10T20:03:40.641Z | subchannel | (2) ::1:7051 CONNECTING -> TRANSIENT_FAILURE
D 2023-08-10T20:03:40.641Z | pick_first | Start connecting to subchannel with address 127.0.0.1:7051
D 2023-08-10T20:03:40.641Z | subchannel | (3) 127.0.0.1:7051 IDLE -> CONNECTING
D 2023-08-10T20:03:40.641Z | transport | dns:localhost:7051 creating HTTP/2 session to 127.0.0.1:7051
D 2023-08-10T20:03:40.644Z | transport | dns:localhost:7051 connection failed with error connect ECONNREFUSED 127.0.0.1:7051
D 2023-08-10T20:03:40.644Z | subchannel | (3) 127.0.0.1:7051 CONNECTING -> TRANSIENT_FAILURE
D 2023-08-10T20:03:40.644Z | pick_first | CONNECTING -> TRANSIENT_FAILURE
D 2023-08-10T20:03:40.644Z | resolving_load_balancer | dns:localhost:7051 CONNECTING -> TRANSIENT_FAILURE
D 2023-08-10T20:03:40.644Z | channel | (1) dns:localhost:7051 callRefTimer.unref | configSelectionQueue.length=0 pickQueue.length=0
D 2023-08-10T20:03:40.644Z | load_balancing_call | [2] Pick called
D 2023-08-10T20:03:40.644Z | load_balancing_call | [2] Pick result: TRANSIENT_FAILURE subchannel: null status: 14 No connection established
D 2023-08-10T20:03:40.644Z | connectivity_state | (1) dns:localhost:7051 CONNECTING -> TRANSIENT_FAILURE
D 2023-08-10T20:03:40.645Z | load_balancing_call | [2] ended with status: code=14 details="No connection established"
D 2023-08-10T20:03:40.645Z | retrying_call | [1] Received status from child [2]
D 2023-08-10T20:03:40.645Z | retrying_call | [1] state=TRANSPARENT_ONLY handling status with progress PROCESSED from child [2] in state ACTIVE
D 2023-08-10T20:03:40.645Z | retrying_call | [1] ended with status: code=14 details="No connection established"
D 2023-08-10T20:03:40.645Z | resolving_call | [0] Received status
D 2023-08-10T20:03:40.646Z | resolving_call | [0] ended with status: code=14 details="No connection established"
D 2023-08-10T20:03:40.647Z | subchannel_refcount | (2) ::1:7051 refcount 2 -> 1
D 2023-08-10T20:03:40.647Z | subchannel_refcount | (3) 127.0.0.1:7051 refcount 2 -> 1
D 2023-08-10T20:03:40.647Z | connectivity_state | (1) dns:localhost:7051 TRANSIENT_FAILURE -> SHUTDOWN
D 2023-08-10T20:03:40.647Z | subchannel_refcount | (2) ::1:7051 refcount 1 -> 0
D 2023-08-10T20:03:40.647Z | subchannel_refcount | (3) 127.0.0.1:7051 refcount 1 -> 0
/usr/src/app/node_modules/@hyperledger/fabric-gateway/dist/gatewayerror.js:39
    return new GatewayError({
           ^

GatewayError: 14 UNAVAILABLE: No connection established
    at newGatewayError (/usr/src/app/node_modules/@hyperledger/fabric-gateway/dist/gatewayerror.js:39:12)
    at Object.callback (/usr/src/app/node_modules/@hyperledger/fabric-gateway/dist/client.js:101:67)
    at Object.onReceiveStatus (/usr/src/app/node_modules/@grpc/grpc-js/build/src/client.js:192:36)
    at Object.onReceiveStatus (/usr/src/app/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
    ... 2 lines matching cause stack trace ...
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  code: 14,
  details: [],
  cause: Error: 14 UNAVAILABLE: No connection established
      at callErrorFromStatus (/usr/src/app/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
      at Object.onReceiveStatus (/usr/src/app/node_modules/@grpc/grpc-js/build/src/client.js:192:76)
      at Object.onReceiveStatus (/usr/src/app/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
      at Object.onReceiveStatus (/usr/src/app/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
      at /usr/src/app/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78
      at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
  for call at
      at Client.makeUnaryRequest (/usr/src/app/node_modules/@grpc/grpc-js/build/src/client.js:160:32)
      at /usr/src/app/node_modules/@hyperledger/fabric-gateway/dist/client.js:44:110
      at new Promise (<anonymous>)
      at GatewayClientImpl.evaluate (/usr/src/app/node_modules/@hyperledger/fabric-gateway/dist/client.js:44:16)
      at ProposalImpl.evaluate (/usr/src/app/node_modules/@hyperledger/fabric-gateway/dist/proposal.js:50:96)
      at async getAllAssets (/usr/src/app/dist/app.js:86:25)
      at async /usr/src/app/dist/app.js:77:9 {
    code: 14,
    details: 'No connection established',
    metadata: Metadata { internalRepr: Map(0) {}, options: {} }
  }
}

有人遇到过类似的问题吗?我想要实现的是为我的节点服务器创建一个单独的 docker-compose 文件(它将集成 MongoDB 等其他服务),以便能够与我的结构网络进行通信。 我先感谢您的宝贵帮助。

node.js docker express grpc hyperledger-fabric
1个回答
0
投票

我明白了

const peerEndpoint = envOrDefault('PEER_ENDPOINT', 'localhost:7051');

但容器内的关键字
localhost
指的是容器本身,而不是主机或任何其他容器,包括那些可能链接或连接的容器。

鉴于您当前的设置,您的 Fabric 网络和 Node.js 应用程序都在单独的容器中运行。因此,会出现错误

No connection established
,因为 Node.js 应用程序正在尝试连接到
localhost
,在此上下文中指的是 Node.js 应用程序容器本身,而不是 Fabric 对等容器。

将 Node.js 容器添加到 Fabric 网络时,请确保它们位于同一网络上。然后,您可以使用另一个容器的容器名称与其进行通信。例如,如果您的对等方的容器名称是

peer0.org1.example.com
,您将使用它作为主机名,而不是
localhost

如果您要在本机运行应用程序和在容器中运行应用程序之间切换,请考虑使用环境变量或配置文件参数化对等端点(以及可能的其他配置项)。这将允许您根据应用程序的运行位置轻松更改设置。

一个可能的 docker 组合可能是:

version: '3'

services:
  app:
    image: test
    container_name: test
    environment:
      - PEER_ENDPOINT=peer1-org1:7051
      # any other environment variables you need 
    ports:
      - 8080:8080
    volumes:
      - /tmp/hyperledger:/tmp/hyperledger
    networks:
      - fabric_net

  peer1-org1:
    image: hyperledger/fabric-peer:latest
    # your peer's configuration, environment, volumes, etc. 
    networks:
      - fabric_net
  
  # other services (orderers, other peers, CAs, etc.) 

networks:
  fabric_net:
© www.soinside.com 2019 - 2024. All rights reserved.