我有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

其他信息:

  • 客户端程序在停靠器容器内运行良好
  • 连接到常规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)
    }
    

推荐答案

当您指定要侦听的主机名或IP地址​时(在本例中,本地主机解析为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是本地环回地址,无法从容器外部访问.

当你用"-p51672:51672"激活docker容器时,它会将流向127.0.0.1:51672的流量转发到容器的IP地址,在我的例子中是172.17.0.2.

容器获得docker0网络接口内的IP地址(可以使用"ip addr ls"命令查看)

因此,当您的流量被转发到172.17.0.2:51672上的容器时,那里没有任何侦听,连接try 失败.

The fix:

问题出在侦听端点:

endpoint := "localhost:51672"

要解决问题,请将其更改为

endpoint := ":51672"

这将使您的服务器侦听所有it容器的IP地址.

Additional info:

当您在Docker容器中公开端口时,Docker将创建iptables规则来执行实际的转发.见this.你可以查看这些规则

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

Go相关问答推荐

为什么在GO中打印变量会导致堆栈溢出?

你能把用户界面文件中的GTK4应用程序窗口添加到GTK4应用程序中吗?

将类型定义为泛型类型实例化

Go中的Slice[:1][0]与Slice[0]

Kusto Go API 从多个表查询

如何将字节文件高效地读入int64切片?

Golang 发送Post请求出现400错误

Go struct 匿名字段是公开的还是私有的?

如何在 gocql 中设置最大池大小?

go - 仅在函数即将返回错误时清理资源

grpc-gateway:重定向与定义不匹配(原始文件)

在 Go GRPC 服务器流式拦截器上修改元数据

Golang - 将 [8] 布尔转换为字节

Golang prometheus 显示自定义指标

如何通过组合来自不同包的接口来创建接口?

Go 中 SDL Surface 的 OpenGL 纹理

golang jwt.MapClaims 获取用户ID

为什么 go.mod 中的所有依赖都是间接的?

如何在 Go 中使用 Pact 返回错误请求(400、500)?

如何正确解组不同类型的数组?