You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构建含多Proto文件的Docker镜像时遇文件未找到问题

构建Docker镜像时Protobuf导入报错问题

问题背景

本地在项目根目录执行以下protoc命令可正常生成代码并运行服务:

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/text.proto db/proto/db.proto server/proto/server.proto

但在项目根目录执行Docker构建命令时:

docker build -f server/Dockerfile -t server .

protoc报错找不到proto/text.proto,具体错误信息见下文。

环境信息

目录结构

text
├── admin
│   ├── Dockerfile
│   ├── app.js
│   ├── package.json
│   └── web
│       ├── html
│       │   └── index.html
│       └── resources
├── compose.yaml
├── db
│   ├── Dockerfile
│   ├── main.go
│   ├── proto
│   │   ├── db.pb.go
│   │   ├── db.proto
│   │   └── db_grpc.pb.go
│   └── text.db
├── go.mod
├── go.sum
├── proto
│   ├── text.pb.go
│   └── text.proto
└── server
    ├── Dockerfile
    ├── main.go
    ├── proto
    │   ├── server.pb.go
    │   ├── server.proto
    │   └── server_grpc.pb.go
    └── text
        ├── text.go
        └── text_test.go

Dockerfile内容

FROM --platform=linux/x86_64 golang:1.19.3-bullseye

# Install grpc
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 && \
    go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28

WORKDIR /app
COPY server/. /app
COPY proto/text.proto /app/proto/text.proto

# Install protoc and zip system library
RUN apt-get update && apt-get install -y zip && \
    mkdir /opt/protoc && cd /opt/protoc && wget https://github.com/protocolbuffers/protobuf/releases/download/v3.7.0/protoc-3.7.0-linux-x86_64.zip && \
    unzip protoc-3.7.0-linux-x86_64.zip

# Copy the grpc proto file and generate the go module
RUN /opt/protoc/bin/protoc --go_out=/app/proto --proto_path=/app/proto --go_opt=paths=source_relative --go-grpc_out=/app/proto --go-grpc_opt=paths=source_relative /app/proto/text.proto /app/proto/server.proto

EXPOSE 5051
RUN go build -o /server
ENTRYPOINT ["/server"]

相关Proto文件内容

server/proto/server.proto

syntax="proto3";
package server;

import "proto/text.proto";
option go_package = "github.com/amb1s1/text/server/proto/server";

message SendMessageRequest {
    string token = 1;
    string phone = 2;
    string message = 3;
    bool dry_run = 4;
};

message SendMessageResponse {
    text.Status status = 1;
};

service Text {
    // SendMessage sents SMS message.
    rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) {}
}

proto/text.proto

syntax="proto3";

package text;

option go_package = "github.com/amb1s1/text/proto";

enum Status {
    UNKNOW = 0;
    OK = 1;
    TOKENS_EXISTS = 2;
    TOKEN_NOT_FOUND = 3;
    FAILED_NOT_SENT= 4;
    DRY_RUN_OK = 5;
    ZERO_BALANCE = 6;
    WRONG_TOKEN = 7;
}

报错信息

=> ERROR [7/8] RUN /opt/protoc/bin/protoc --go_out=/app/proto --proto_path=/app/proto --go_opt=paths=source_relative --go-grpc_out=/app/proto --go-grpc_opt=paths=source_relative   0.4s
------
 > [7/8] RUN /opt/protoc/bin/protoc --go_out=/app/proto --proto_path=/app/proto --go_opt=paths=source_relative --go-grpc_out=/app/proto --go-grpc_opt=paths=source_relative /app/proto/text.proto /app/proto/server.proto:
#11 0.427 proto/text.proto: File not found.
#11 0.429 server.proto: Import "proto/text.proto" was not found or had errors.
#11 0.431 server.proto:25:5: "text.Status" seems to be defined in "text.proto", which is not imported by "server.proto".  To use it here, please add the necessary import.
------
executor failed running [/bin/sh -c /opt/protoc/bin/protoc --go_out=/app/pro

解决方案

核心问题分析

当前Docker构建中,server.proto导入proto/text.proto,但镜像内的文件结构被调整为:

/app
├── proto
│   ├── text.proto
│   └── server.proto

同时protoc的--proto_path设为/app/proto,此时protoc会在/app/proto下查找proto/text.proto,即实际路径为/app/proto/proto/text.proto,自然找不到目标文件。

方案1:对齐本地目录结构与protoc命令

  1. 修改Dockerfile中的COPY指令,保持和本地一致的目录结构:
WORKDIR /app
COPY go.mod go.sum ./
COPY proto/ ./proto/
COPY server/ ./server/

此时镜像内的结构为:/app/proto/text.proto、/app/server/proto/server.proto,和本地完全匹配。

  1. 修改protoc命令,将--proto_path设为项目根目录/app,复用本地的成功命令:
RUN /opt/protoc/bin/protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/text.proto server/proto/server.proto

方案2:修改Proto导入路径适配镜像结构

如果不想调整镜像内的文件结构,可以修改server.proto的导入语句:

import "text.proto";

此时server.proto和text.proto在同一目录下,保持Dockerfile中的COPY逻辑,同时protoc命令的--proto_path设为/app/proto即可正常查找。

更优实践:多阶段构建减小镜像体积

使用多阶段构建,将代码生成、编译与运行环境分离,大幅减小最终镜像体积:

# 构建阶段
FROM --platform=linux/x86_64 golang:1.19.3-bullseye as builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY proto/ ./proto/
COPY server/ ./server/

# 安装protoc及代码生成工具
RUN apt-get update && apt-get install -y protobuf-compiler
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 && \
    go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28

# 生成proto代码
RUN protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/text.proto server/proto/server.proto

# 编译服务
RUN go build -o /server ./server/

# 运行阶段
FROM --platform=linux/x86_64 debian:bullseye-slim

COPY --from=builder /server /server

EXPOSE 5051
ENTRYPOINT ["/server"]

内容的提问来源于stack exchange,提问作者amb1s1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:02:03