gRPC-node: при * Dockerizing * службе запрос не проходит через сервер службы? [Скриншоты включены]

Я создал действительно простой книжный магазин с Книгами, Покупателем и основной службой. Именно эта проблема касается основного и книжного обслуживания.

В настоящее время я делаю запрос gRPC под названием createBook, который создает книгу в нашей БД, а также журналы консоли.

При запуске сервера gRPC (booksServer) без докера процесс проходит гладко.

Но как только я использую докер, мне кажется, что запрос gRPC не попадает на сервер gRPC ...

Под docker я подразумеваю использование docker для запуска booksServer. (Как показано ниже)

Результат: без Docker

As you can see, without docker, the request is fulfilled, and everything works as it should. 
Our gRPC client makes a call to the gRPC server (in which metadata is created) and the metadata is also sent back to the client. 
(Scroll down to see the gRPC server file with the method called "getBooks".)

введите описание изображения здесь

booksServer (без докера)  введите описание изображения здесь

*** Notice the console logs in the booksServer!!! ***

Разрешите мне запустить booksServer (с докером)

(Dockerfile ниже)

FROM node:12.14.0
WORKDIR /usr/src/app
COPY package*.json ./
COPY . /usr/src/app
RUN npm install
RUN npm install nodemon -g
EXPOSE 30043
CMD ["nodemon", "booksServer.js"



Here's my main service docker file too which initiates the request:

FROM node:12.14.0
WORKDIR /usr/src/app
COPY package*.json ./
COPY . /usr/src/app
# COPY wait-for-it.sh . 
# RUN chmod +x /wait-for-it.sh
RUN npm install
EXPOSE 4555
CMD ["node", "main.js"]

введите описание изображения здесь  введите описание изображения здесь

^^^ Notice how when dockerfile is used to run booksServer
it doesn't go/run inside the booksServer file 
***It does NOT produce any console.logs when I fire off a gRPC requesst***

Так выглядит файл booksServer.js  введите описание изображения здесь

Вот корешок с книгами

//use this for bookInitiator
const path = require('path');
const PROTO_PATH = path.join(__dirname, "../protos/books.proto");

const grpc = require("grpc");
const protoLoader = require("@grpc/proto-loader");

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true,
  longs: String,
  enums: String,
  arrays: true
});

const BooksService = grpc.loadPackageDefinition(packageDefinition).BooksService;

// potential issues to fix 1) making localhost port dynamic 2) docker containerization may cause conflict

const client = new BooksService (
  "172.17.0.2:30043",
  grpc.credentials.createInsecure()
);

console.log("Creating stub inside booksStub");
module.exports = client;

Вот файл сервера gRPC (с привязанными портами).

// const PROTO_PATH = "../protos/books.proto";
const path = require('path');
const PROTO_PATH = path.join(__dirname, './protos/books.proto');
const grpc = require("grpc");
const protoLoader = require("@grpc/proto-loader");
const express = require("express");
const controller = require("./booksController.js");
const app = express();
app.use(express.json());

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true,
  longs: String,
  enums: String,
  arrays: true,
});

const booksProto = grpc.loadPackageDefinition(packageDefinition);

const { v4: uuidv4 } = require("uuid");

const server = new grpc.Server();

server.addService(booksProto.BooksService.service, {
  CreateBook: (call, callback) => {
    console.log("call to CreateBook");

    //sample will take the call information from the client(stub)
    const book = {
      title: call.request.title,
      author: call.request.author,
      numberOfPages: call.request.numberOfPages,
      publisher: call.request.publisher,
      
      id: call.request.id,
    };

    controller.createBook(book);

    let meta = new grpc.Metadata();
    meta.add("response", "none");
    console.log("metadata in createBook...: ", meta);
    call.sendMetadata(meta);

    callback(
      null,
      //bookmodel.create
      {
        title: `completed for: ${call.request.title}`,
        author: `completed for: ${call.request.author}`,
        numberOfPages: `completed for: ${call.request.numberOfPages}`,
        publisher: `completed for: ${call.request.publisher}`,
        id: `completed for: ${call.request.id}`,
      }
    );
  },
  GetBooks: (call, callback) => {
    console.log("call to GetBooks");
    // read from database
    let meta = new grpc.Metadata();
    meta.add('response', 'none');
    call.sendMetadata(meta);

    controller.getBooks(callback);
  }
});

server.bind("0.0.0.0:30043", grpc.ServerCredentials.createInsecure());
console.log("booksServer.js running at 0.0.0.0:30043");
console.log("Inside Books Server!");
console.log("call from books server");

server.start();

horus.js (настраиваемый простой инструмент трассировки), grab trace захватывает путь определенного запроса и отправляет его обратно клиенту gRPC в виде метаданных

const fs = require("fs");
const grpc = require("grpc");
const path = require("path");

class horus {
  constructor(name) {
    this.serviceName = name; // represents the name of the microservices
    this.startTime = null;
    this.endTime = null;
    this.request = {};
    this.targetService = null; // represents the location to which the request was made
    this.allRequests = []; // array which stores all requests
    this.timeCompleted = null;
    this.call;
  }

  static getReqId() {
    // primitive value - number of millisecond since midnight January 1, 1970 UTC
    // add service name/ initials to the beginning of reqId?
    return new Date().valueOf();
  }

  // start should be invoked before the request is made
  // start begins the timer and initializes the request as pending
  start(targetService, call) {
    this.startTime = Number(process.hrtime.bigint());
    this.request[targetService] = "pending"; // {books: 'pending', responseTime: 'pending'}
    this.request.responseTime = "pending";
    this.targetService = targetService;
    this.call = call;
    this.request.requestId = horus.getReqId();
  }
  // end should be invoked when the request has returned
  end() {
    this.endTime = Number(process.hrtime.bigint());
    this.request.responseTime = (
      (this.endTime - this.startTime) /
      1000000
    ).toFixed(3); //converting into ms.
    this.sendResponse();
    this.request.timeCompleted = this.getCurrentTime();
  }
  // grabTrace accepts inserts trace into request
  // trace represents the "journey" of the request
  // trace expects metaData to be 'none when the server made no additional requests
  // trace expects metaData to be the request object generated by the server otherwise
  // in gRPC, the trace must be sent back as meta data. objects should be converted with JSON.parse
  grabTrace(metaData) {
    //console.log("incoming meta data ", metaData);
    console.log("Inside Grab Trace Method.");
    console.log("Metadata inside grabTrace: ", metaData);
    if (metaData === "none" || metaData === undefined) this.request[this.targetService] = "none";
    else {
      metaData = JSON.parse(metaData);
      this.request[this.targetService] = metaData;
    }
    this.allRequests.push(this.request);
    this.sendResponse();
  }
  // displayRequests logs to the console all stored requests
  // setTimeout builds in deliberate latency since metadata may be sent before or after a request is done processing
  displayRequests() {
    console.log("\n\n");
    console.log("Logging all requests from : ", this.serviceName);
    this.allRequests.forEach((request) => {
      console.log("\n");
      console.log(request);
    });
    console.log("\n\n");
  }
  // sends response via metadata if service is in the middle of a chain
  sendResponse() {
    if (
      this.request.responseTime === "pending" ||
      this.request[this.targetService] === "pending" ||
      this.call === undefined
    )
      return;
    console.log("Inside send response");
    let meta = new grpc.Metadata();
    meta.add("response", JSON.stringify(this.request));
    console.log('meta in send response: ', meta)
    this.call.sendMetadata(meta);
  }
  writeToFile() {
    console.log("call to writeToFile");
    console.log("logging request obj ", this.request);
    let strRequests = "";
    for (let req of this.allRequests) {
      // First write to file - contains Total
      // subsequent - chained requests
      strRequests += `Request ID: ${req.requestId}\n`;
      strRequests += `"${
        Object.keys(req)[0]
      }" service -> Response received in ${Object.values(req)[1]} ms (Total)\n`;
      strRequests += `Timestamp: ${req.timeCompleted}\n`;
      // while we don't hit an empty object on the 1st key, go inside
      // add numbering in order for nested requests inside original?!
      let innerObj = Object.values(req)[0];
      while (innerObj !== "none") {
        strRequests += `"${
          Object.keys(innerObj)[0]
        }" service -> Response received in ${Object.values(innerObj)[1]} ms\n`;
        strRequests += `Timestamp: ${innerObj.timeCompleted}\n`;
        innerObj = Object.values(innerObj)[0];
      }
      strRequests +=
        "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
    }
    console.log('strRequests', strRequests)
    fs.writeFile(this.serviceName + 'data' + '.txt', strRequests, { flag: "a+" }, (err) => {
      if (err) {
        console.error(err);
      }
    }); //'a+' is append mode
  }

module.exports = horus;


main.js (инициирует запрос клиента gRPC)

const path = require('path');
// const grpc = require("grpc");
const customersStub = require("./stubs/customersStub.js");
const booksStub = require("./stubs/booksStub.js");
const horusTracer = require(path.join(__dirname, "./horus/horus.js"));

//In master branch
console.log("Stub is Inside main service!!!");

const book = {
  title: "ITttttt",
  author: "Stephen King",
  numberOfPages: 666,
  publisher: "Random House",
  id: 200,
};

const bookId = {
  id: 200
}

const customer = {
  id: 123,
  name: "Lily",
  age: 23,
  address: "Blablabla",
  favBookId: 100
};

const customerId = {
  id: 123
}

let ht = new horusTracer("main");


function CreateBook () {
  ht.start('books')
  booksStub.CreateBook(book, (error, response) => {
    if (error) console.log("there was an error ", error);
    ht.end();
    ht.displayRequests();
    ht.writeToFile();
  }).on('metadata', (metadata) => {
    console.log("Before grab trace is invoked!");
    ht.grabTrace(metadata.get('response')[0]);
  });
}

}


CreateBook(); //Works

Я думаю, в чем проблема.


Edit: murgatroid99 mentioned that it was a networking issue with docker!


~~~~~~~~~
I initially thought this was a networking issue, but I don't think it is 
because all my docker files are running on the default bridge network. 
So they all technically can communicate with one another...

Is it something wrong with nodemon interacting with Docker? 
Does the server not output the console logs...? 
Is the server actually running and working...?

Do I need a reverse proxy like nginx?

``

person TheRoadLessTaken    schedule 03.08.2020    source источник
comment
Ошибка, о которой вы спрашивали в вашем предыдущем вопросе, явно все еще присутствует в коде, о котором вы спрашиваете в этом вопросе, и ошибка, которую выдает клиент в результате, почти наверняка затмевает другую ошибку с более релевантной информацией о сбое подключения. Вы действительно должны сначала исправить эту ошибку или, по крайней мере, полностью опустить этот вызов функции, чтобы убедиться, что вызов gRPC завершился, и вы могли понять, почему он не работает.   -  person murgatroid99    schedule 03.08.2020
comment
В дополнение к этому вы спрашиваете о проблемах с сетевым подключением, поэтому вы должны включить код, который связывает порт на сервере, и код, который устанавливает соединение на клиенте.   -  person murgatroid99    schedule 03.08.2020
comment
@ murgatroid99 Только что добавил сервер gRPC (где привязан порт) и файл-заглушку клиента. Да, я настаивал на том, что это проблема с докером, потому что он работал без файла докера ... Я просто немного запутался в том, как его отлаживать в данный момент. Я не понимаю, почему запросы клиента не доходят до сервера gRPC при активации докера.   -  person TheRoadLessTaken    schedule 04.08.2020


Ответы (1)


Проблема в том, что ваш сервер привязан к 127.0.0.1:30043. Вы говорите, что запускаете образы докеров, используя мостовую сеть по умолчанию. В этом режиме образ докера имеет другую (виртуальную) сеть, чем у хост-машины, поэтому его адрес обратной связи отличается от адреса обратной связи хост-машины. Чтобы исправить это, вы можете вместо этого привязать сервер к 0.0.0.0:30043 или [::]:30043 для привязки к другим сетевым интерфейсам, к которым клиент может подключиться извне контейнера докеров.

По той же причине подключение клиента к localhost:30043 не будет работать: его адрес localhost также относится к интерфейсу обратной связи в контейнере докера. Вместо этого вы должны заменить localhost на IP-адрес контейнера сервера.

В качестве альтернативы, как описано в этот вопрос, вы можете объединить контейнеры докеров в сеть в режиме хоста, чтобы они использовали одну и ту же сеть с хост-компьютером.

person murgatroid99    schedule 03.08.2020
comment
Большое спасибо, murgatroid! Он исправил эту проблему (когда я запускаю 'node main.js', но не когда я запускаю образ докера для основного инициатора, который делает запрос клиента gRPC). Я все еще получаю ошибку JSON при трассировке захвата (я добавил код выше в horus.js). Это похоже на то, что когда я запускаю dockerfile (из main.js), он не достигает сервера gRPC. - person TheRoadLessTaken; 04.08.2020
comment
Я не понимаю, почему вы продолжаете поднимать эту ошибку JSON. Это не имеет отношения к сетевой проблеме и, вероятно, мешает вам увидеть ошибку с более актуальной информацией. Я уже объяснил, что вам нужно сделать, чтобы это исправить, в ответе на ваш предыдущий вопрос. В любом случае, я отредактировал свой ответ, чтобы решить проблему клиента. - person murgatroid99; 04.08.2020
comment
Привет, мургатроид, извини, постараюсь уточнить. Я действительно использовал ‹IP-адрес›: ‹port› для клиента и привязал сервер к 0.0.0.0: ‹port› ... Но, как ни странно, проблемы с сетью все еще сохраняются. В настоящее время кажется, что не удается подключиться к серверу из-за этой ошибки: Ошибка: 14 НЕДОСТУПНО: не удалось подключиться ко всем адресам в Object.exports.createStatusError в Object.onReceiveStatus ... Я дважды проверил наличие дополнительных мостов и убедился, что у каждой заглушки есть соответствующий IP-адрес и порт, и каждый сервер модифицирован для версии 0.0.0.0: ‹port› ... - person TheRoadLessTaken; 04.08.2020
comment
Мне жаль. Если это не решит проблему, я не знаю. - person murgatroid99; 04.08.2020