无法连接到在本地 Docker 容器中运行的 Go GRPC 服务器。

huangapple go评论113阅读模式
英文:

Cannot connect to Go GRPC server running in local Docker container

问题

我有一个Go gRPC服务。我在Mac Sierra上进行开发。当在本地运行gRPC客户端与服务进行通信时,一切正常。但是当在Docker容器中运行相同的客户端与相同的服务进行通信时,我会收到以下错误信息:

transport: http2Client.notifyError got notified that the client transport was broken EOF.
FATA[0000] rpc error: code = Internal desc = transport is closing

这是我的Docker文件:

FROM golang:1.7.5

RUN mkdir -p /go/src/github.com/foo/bar
WORKDIR /go/src/github.com/foo/bar

COPY . /go/src/github.com/foo/bar
# ONBUILD RUN go-wrapper download
RUN go install

ENTRYPOINT /go/bin/bar

EXPOSE 51672

构建镜像的命令:

docker build -t bar .

启动Docker容器的命令:

docker run -p 51672:51672 --name bar-container bar

其他信息:

  • 在Docker容器内部运行的客户端程序正常工作。

  • 连接到常规的REST端点正常工作(是否与HTTP2和gRPC相关?)。

  • 在OS X上运行lsof命令会得到以下结果:

      $lsof -i | grep 51672
      com.docke   984 oldDave   21u  IPv4 0x72779547e3a32c89      0t0  TCP *:51672 (LISTEN)
      com.docke   984 oldDave   22u  IPv6 0x72779547cc0fd161      0t0  TCP localhost:51672 (LISTEN)
    

这是我的服务器代码片段:

server := &Server{}
endpoint := "localhost:51672"
lis, err := net.Listen("tcp", endpoint)
if err != nil {
    log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer(grpc.Creds(creds))

pb.RegisterExpServiceServer(s, server)

// Register reflection service on gRPC server.
reflection.Register(s)

log.Info("Starting Exp server: ", endpoint)

if err := s.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %v", err)
}
英文:

I have a go grpc service. I'm developing on a mac, sierra. When running a grpc client against the service locally, all is well, but when running same client against same service in the docker container I get this error:

transport: http2Client.notifyError got notified that the client transport was broken EOF.
FATA[0000] rpc error: code = Internal desc = transport is closing

this is my docker file:

FROM golang:1.7.5

RUN mkdir -p /go/src/github.com/foo/bar
WORKDIR /go/src/github.com/foo/bar

COPY . /go/src/github.com/foo/bar
# ONBUILD RUN go-wrapper download
RUN go install

ENTRYPOINT /go/bin/bar

EXPOSE 51672

my command to build the image:

docker build -t bar .

my command to launch the docker container:

docker run -p 51672:51672 --name bar-container bar

Other info:

  • client program runs fine from within the docker container

  • connecting to a regular rest endpoint works fine (http2, grpc related?)

  • running the lsof command in OS X yields these results

      $lsof -i | grep 51672
      com.docke   984 oldDave   21u  IPv4 0x72779547e3a32c89      0t0  TCP *:51672 (LISTEN)
      com.docke   984 oldDave   22u  IPv6 0x72779547cc0fd161      0t0  TCP localhost:51672 (LISTEN)
    
  • here's a snippet of my server code:

      server := &Server{}
      endpoint := "localhost:51672"
      lis, err := net.Listen("tcp", endpoint)
      if err != nil {
      	log.Fatalf("failed to listen: %v", err)
      }
    
      s := grpc.NewServer(grpc.Creds(creds))
    
      pb.RegisterExpServiceServer(s, server)
    
      // Register reflection service on gRPC server.
      reflection.Register(s)
    
      log.Info("Starting Exp server: ", endpoint)
    
      if err := s.Serve(lis); err != nil {
      	log.Fatalf("failed to serve: %v", err)
      }
    

答案1

得分: 52

当你指定一个主机名或IP地址来监听时(在这种情况下是localhost,解析为127.0.0.1),那么你的服务器只会监听该IP地址。

当你在Docker容器外部时,监听localhost并不是一个问题。如果你的服务器只监听127.0.0.1:51672,那么你的客户端可以轻松连接到它,因为连接也是从127.0.0.1发起的。

当你在Docker容器内运行服务器时,它仍然只会监听127.0.0.1:51672。127.0.0.1是一个本地回环地址,无法从容器外部访问。

当你使用"-p 51672:51672"启动Docker容器时,它会将流量转发到127.0.0.1:51672,转发到容器的IP地址,对我来说是172.17.0.2。

容器在docker0网络接口中获得一个IP地址(可以使用"ip addr ls"命令查看)。

因此,当流量转发到172.17.0.2:51672的容器时,那里没有正在监听的内容,连接尝试失败。

解决方法:

问题出在监听端点上:

endpoint := "localhost:51672"

要解决问题,将其更改为

endpoint := ":51672"

这将使你的服务器监听所有容器的IP地址。

附加信息:

当你在Docker容器中暴露端口时,Docker会创建iptables规则来进行实际的转发。参见这里。你可以使用以下命令查看这些规则:

iptables -n -L 
iptables -t nat -n -L
英文:

When you specify a hostname or IP address​ to listen on (in this case localhost which resolves to 127.0.0.1), then your server will only listen on that IP address.

Listening on localhost isn't a problem when you are outside of a Docker container. If your server only listens on 127.0.0.1:51672, then your client can easily connect to it since the connection is also made from 127.0.0.1.

When you run your server inside a Docker container, it'll only listen on 127.0.0.1:51672 as before. The 127.0.0.1 is a local loopback address and it not accessible outside the container.

When you fire up the docker container with "-p 51672:51672", it'll forward traffic heading to 127.0.0.1:51672 to the container's IP address, which in my case is 172.17.0.2.

The container gets an IP addresses within the docker0 network interface (which you can see with the "ip addr ls" command)

So, when your traffic gets forwarded to the container on 172.17.0.2:51672, there's nothing listening there and the connection attempt fails.

The fix:

The problem is with the listen endpoint:

endpoint := "localhost:51672"

To fix your problem, change it to

endpoint := ":51672"

That'll make your server listen on all it container's IP addresses.

Additional info:

When you expose ports in a Docker container, Docker will create iptables rules to do the actual forwarding. See this. You can view these rules
with:

iptables -n -L 
iptables -t nat -n -L

答案2

得分: 0

如果您使用Docker容器来运行gRPC服务,您需要确保gRPC服务gRPC客户端在同一个桥接网络上。

英文:

If you use docker container to run grpc service,you should make sure grpc service and grpc client on ths same bridge

答案3

得分: 0

如果您正在使用Docker容器,您可以通过IP定义网络,并将此IP分配给您的IP地址(例如:178.20.0.5)。

英文:

if you are using docker container, u can define network by IP and give this IP to your IP address(like: 178.20.0.5).

huangapple
  • 本文由 发表于 2017年5月11日 17:40:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/43911793.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定