Code前端首页关于Code前端联系我们

使用 Docker 编译 golang 项目应用

terry 2年前 (2023-09-24) 阅读数 62 #后端开发

1. 准备工作

考虑以下包含客户端和服务器的 golang 项目。其中:

  • 客户端用于写入、发送和接收消息。
  • 服务器接收来自客户端的消息,翻译它们并将其发送回客户端

源代码

https://github.com/Nutao/golang-demo.git​github.com/Nutao /golang -demo.git
├── Dockerfile
├── cmd
│   ├── client
│   │   ├── main.go
│   │   ├── request.go
│   │   └── ui.go
│   └── server
│       ├── main.go
│       └── translate.go
├── go.mod
└── go.sum

Dockerfile

# syntax=docker/dockerfile:1
FROM golang:1.20-alpine  # 使用golang官方镜像的版本
WORKDIR /src             # 创建并指定工作目录
COPY . .               # 复制文件到工作目录
RUN go mod download      # 下载依赖
RUN go build -o /bin/client ./cmd/client    # 编译client
RUN go build -o /bin/server ./cmd/server    # 编译server
ENTRYPOINT [ "/bin/server" ]      # 启动server

其中:

  1. #syntax=docker/dockerfile:1
    此注释是 Dockerfile 解析器指令。指定要使用的 Dockerfile 语法版本。该文件使用经过验证的语法:它确保您可以访问最新的 Docker 构建选项。要了解这个解析器的功能,可以参考这篇文章:自定义Dockerfile语法| Docker 文档

编译

docker build  -t test:v1 .
使用Docker进行golang工程应用编译镜像构建

2. Layer

dockerfile中的每条指令都会被记录为一个镜像层。通常docker引擎使用Unionfs文件系统来存储这些图像层。 使用Docker进行golang工程应用编译

来自:http://docs.docker.com

运行构建时,构建器会尝试重用早期构建中的层。如果图像层未更改,则构建器会从构建缓存中选择该层。如果自上次构建以来某个图层发生了更改,您必须 重建该图层以及所有后续图层

在企业内部渠道中,可以利用docker镜像层的缓存特性来加速渠道建设。

3。多阶段(Multi-Stage)

在上面的案例中,我们将整个golang环境、源码和依赖包打包到test:v1镜像中。当构建完成后,我们发现镜像大小已经达到415MB使用Docker进行golang工程应用编译图片大小

臃肿的图片会在构建效率、产品分布和环境资源方面带来很多问题,并且存在源代码泄露的风险。在实际使用中,通常使用Docker的Multi-Stage特性来解决上述问题。

添加阶段

通过多阶段,您可以选择为构建和运行时环境使用不同的基础镜像,并将打包结果从构建阶段复制到运行时阶段。

Dockerfile可以改为:

# Build Stage
# syntax=docker/dockerfile:1
FROM golang:1.20-alpine  
WORKDIR /src            
COPY . .                
RUN go env -w GOPROXY=https://goproxy.cn,direct  
RUN go mod download    
RUN go build -o /bin/client ./cmd/client   
RUN go build -o /bin/server ./cmd/server    

# 运行时 Stage
FROM scratch
COPY --from=0 /bin/client /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]

通过创建上面的dockerfile,可以看到镜像test:v2的大小大大减少到15.8MB 使用Docker进行golang工程应用编译

并行度(Parallelism)❀级别构建,减小图像尺寸的目的就达到了。然而,我们分析 Dockerfile 发现,在 Build 阶段,客户端和服务端是串行启动的,进一步加剧了创建镜像时本来就漫长的构建过程( 也不夸张,虽然这demo 相当快,但在真实的生产环境中不一定如此。CICD 环境流程每改进 1 秒,可以节省大量资源)。

所以我们可以进一步优化编译过程并并行运行。

结合第2章的多级构建,Dockerfile可以改为:

# syntax=docker/dockerfile:1
FROM golang:1.20-alpine  AS base
WORKDIR /src            
COPY . .       
RUN go env -w GOPROXY=https://goproxy.cn,direct  
RUN go mod download -x

FROM base AS build-client
RUN go build -o /bin/client ./cmd/client

FROM base AS build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch AS client
COPY --from=build-client /bin/client /bin/
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]
使用Docker进行golang工程应用编译并行构建

可以看到,编译客户端和服务端步骤合二为一,

同时运行子目标(Target)。构建

尽管多级构建和并行化已用于提高效率并减小图像大小。然而,镜子仍然存在问题。客户端和服务器在同一个镜像中。在生产场景中,客户端和服务端必须分开部署,所以上面的流程显然是不合理的。目标需要划分。

Dockerfile 可以改为:

# syntax=docker/dockerfile:1
FROM golang:1.20-alpine  AS base
WORKDIR /src            
COPY . .       
RUN go env -w GOPROXY=https://goproxy.cn,direct  
RUN go mod download -x

FROM base AS build-client
RUN go build -o /bin/client ./cmd/client

FROM base AS build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch AS client
COPY --from=build-client /bin/client /bin/
ENTRYPOINT [ "/bin/client" ]

FROM scratch AS server
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]

构建命令:

# 构建client
docker build --tag=client:v1  --target=client .

# 构建server
docker build --tag=server:v1  --target=server . 
使用Docker进行golang工程应用编译客户端和服务端分开构建

拆分后,镜像大小将进一步减小。

4.本文总结

本文介绍了Layer,并逐步探索多阶段构建(Multi-Stage)的各种特性,以实现对Docker产品的构建效率和体积的有效控制。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门