构建含多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命令
- 修改Dockerfile中的COPY指令,保持和本地一致的目录结构:
WORKDIR /app COPY go.mod go.sum ./ COPY proto/ ./proto/ COPY server/ ./server/
此时镜像内的结构为:/app/proto/text.proto、/app/server/proto/server.proto,和本地完全匹配。
- 修改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
相关产品推荐
相关产品推荐

