0

I have a node grpc-server running on localhost and my grpc-client is a python flask server. If the client also runs on localhost directly then everything works as intended. Once I host the client(flask server) in a docker-container it is unable to reach the grpc-server though.

The error simply states:

RPC Target is unavaiable

I can call the flask-api from the host without issues. Also I changed the server address from 'localhost' to 'host.docker.internal', which is getting resolved correctly. Not sure if I am doing something wrong or this just doesn't work. I greatly appreciate any help or suggestions. Thanks!

Code snippets of the server, client and docke-compose :

server.js (Node)

...
const port = 9090;
const url = `0.0.0.0:${port}`;

// gRPC Credentials
import { readFileSync } from 'fs';
let credentials = ServerCredentials.createSsl(
  readFileSync('./certs/ca.crt'),
  [{
    cert_chain: readFileSync('./certs/server.crt'),
    private_key: readFileSync('./certs/server.key')
  }],
  false
)
...
const server = new Server({
  "grpc.keepalive_permit_without_calls": 1,
  "grpc.keepalive_time_ms": 10000,
});
...
server.bindAsync(
  url,
  credentials,
  (err, port) => {
    if (err) logger.error(err);
    server.start();
  }
);

grpc_call.py (status_update is called by app.py)

import os
import logging as logger
from os.path import dirname, join

import config.base_pb2 as base_pb2
import config.base_pb2_grpc as base_pb2_grpc
import grpc

# Read in ssl files
def _load_credential_from_file(filepath):
    real_path = join(dirname(dirname(__file__)), filepath)
    with open(real_path, "rb") as f:
        return f.read()


# -----------------------------------------------------------------------------
def status_update(info, status, info=""):
    SERVER_CERTIFICATE = _load_credential_from_file("config/certs/ca.crt")
    SERVER_CERTIFICATE_KEY = _load_credential_from_file("config/certs/client.key")
    ROOT_CERTIFICATE = _load_credential_from_file("config/certs/client.crt")

    credential = grpc.ssl_channel_credentials(
        root_certificates=SERVER_CERTIFICATE,
        private_key=SERVER_CERTIFICATE_KEY,
        certificate_chain=ROOT_CERTIFICATE,
    )

    # grpcAddress = "http://localhost"
    grpcAddress = "http://host.docker.internal"
    grpcFull = grpcAddress + ":9090"

    with grpc.secure_channel(grpcFull, credential) as channel:
        stub = base_pb2_grpc.ProjectStub(channel)
        request = base_pb2.ContainerId(id=int(info), status=status)

        try:
            response = stub.ContainerStatus(request)

        except grpc.RpcError as rpc_error:
            logger.error("Error @STATUS_UPDATE")
            if rpc_error.code() == grpc.StatusCode.CANCELLED:
                logger.error("RPC Request got cancelled")
            elif rpc_error.code() == grpc.StatusCode.UNAVAILABLE:
                logger.error("RPC Target is unavaiable")
            else:
                logger.error(
                    f"Unknown RPC error: code={rpc_error.code()} message={rpc_error.details()}"
                )
            raise ConnectionError(rpc_error.code())

        else:
            logger.info(f"Received message: {response.message}")
            return

Docker-compose.yaml

version: "3.9"
services:
  test-flask:
    image: me/test-flask
    container_name: test-flask
    restart: "no"
    env_file: .env
    ports:
      - 0.0.0.0:8010:8010
    command: python3 -m flask run --host=0.0.0.0 --port=8010
David
  • 116
  • 2
  • 10
  • It would be helpful if you included the entire Docker Compose. I suspect you're not binding the client to the host's address correctly. It likely won't be `localhost` or `host docker.internal` but the Compose service name (which you don't include). It would be good to externalize the host address in your client so that you can pass it either as a flag or environment variable – DazWilkin Dec 23 '21 at 02:21
  • First of all, thank you for your comment. What I posted is the entire Docker Compose. The gRPC server does NOT run in a docker container. It just runs directly on my mac, thats why I thought I just have to use host.docker.internal. Only the client is running in docker – David Dec 23 '21 at 03:40
  • 1
    Aha! Ok. Then you probably need to bind the Compose service to the host network (`network: host`) so that it can access the host's ports. I assume you're using Windows or Mac where this is complicated by the fact that Docker's host is a VM running on your host. So at this point, I'm unable to help further. – DazWilkin Dec 23 '21 at 04:10

0 Answers0