

如何使用多阶段构建来为你的Go应用程序创建轻量级docker镜像
source link: http://dockone.io/article/8196?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

恭喜!你已经创建了一个出色的go应用程序,现在你想创建一个docker容器来分发你的应用。
但是,如何尽可能为你的Golang应用程序打造一个最小的镜像呢?提示:我们将会使用多阶段构建(自从Docker 17.05版本提供的 方法)
介绍
多什么?
简单来讲,多阶段。
多阶段允许在创建Dockerfile时使用多个from,并且这种方法是十分有效的,因为它允许我们使用所有必需的工作来构建我们的应用。举个例子,首先问使用golang的基础镜像,然后在第二阶段的时候使用构建好的镜像的二进制文件,最后阶段构建出来的镜像用于发布到我们自己的仓库或者是用于上线发布。
在上述的案例中,我们总共有三个阶段:
1. build 编译阶段
2. certs(可选,可有可无)证书认证阶段
3. prod 生产阶段
在build阶段主要是编译我们的应用程序,证书认证阶段将会安装我们所需要的CA证书,最后的生产发布阶段会将我们构建好的镜像推到镜像仓库中。而且发布阶段将会使用build阶段编译完毕的二进制文件和certs阶段安装的证书。

项目发布的多个build阶段
示例工程
谈到如何去做的时候,我们将会使用已经创建好的简单的工程。它只是一个运行在8080端口的HTTP 服务,并且返回结果为传递过去的URL的内容结果。
举例
GET http://localhost:8080?url=https://google.com 返回结果为goole页面内容展示
你也可以在 这里 找到 代码仓库
在master分支上只包含了应用程序,final分支上还包含本篇教程中使用的Dockerfile文件
如果你想跟着本教程来做,只需要拉下master上的代码并且跟着我来创建Dockerfile
步骤1 - 编译阶段
第一阶段主要是使用golang基础镜像来将我们的应用程序打包为二进制文件。这个基础镜像包含了将我们的应用程序编译成可执行二进制文件的所有工具。
下面是我们最原始的Dockerfile:
1 # 2 # BUILD 阶段 3 # 4 FROM golang:1.10 AS build 5 6 # 设置我们应用程序的工作目录 7 WORKDIR /go/src/github.com/scboffspring/blog-multistage-go 8 9 # 添加所有需要编译的应用代码 10 ADD . . 11 12 # 编译一个静态的go应用(在二进制构建中包含C语言依赖库) 13 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . 14 15 # 设置我们应用程序的启动命令 16 CMD ["./blog-multistage-go"]
第4行:使用的基础镜像(golang:1.10)并且我们使用as给当前阶段一个别名,也可以使用阶段索引来引用前一阶段,但这使得它更清晰。
第7行:我们将工作目录设置为golang基础镜像的默认$GOPATH中的应用程序目录
第10行:添加我们的应用程序源文件
第13行:编译二进制文件。使用不同的参数来创建一个完整的静态库,因为在生产环境拉取镜像时可能不一定需要需要所有的Golang VM以及C语言库
第16行:使用设定的命令来启动应用程序
现在我们进行编译并使用docker容器,我们的应用程序如我们预期正常运行:
docker build -t scboffspring/blog-multistage-go . docker run --rm -ti -p 8080:8080 \ scboffspring/blog-multistage-go
我们可以使用curl命令来请求,并且它会返回 http://google.com 页面内容
在终端运行 curl localhost:8080
1 <html itemscope="" itemtype="http://schema.org/WebPage" lang="de-CH"> 2 <head> 3 <meta content="text/html; charset=UTF-8" 4 http-equiv="Content-Type"> 5 <meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" 6 itemprop="image"><title>Google</title> 7 ....
让我们使用 docker images ,来看看镜像的大小
REPOSITORY ... SIZE scboffspring/blog-multistage-go ... 818MB
荒唐,太荒唐了,一个这么小的应用居然占了磁盘818M
推送到镜像仓库后,镜像带下被压缩到309M

docker hub 占用309M
接下来我们来改善这种情况,把镜像的大小降低到10M!
步骤2 - 生产阶段
上面的提供的镜像时完全可以进行部署使用的,但是它真的是太大了。每次在Kubernates上启动你的容器时需要拉取309M的镜像?真的是太浪费时间和带宽
让我们来为我们的镜像构建一个生产阶段,正如上面解释的,这个阶段只是从build阶段拷贝二进制文件到容器中
我们新的Dockerfile将会如下所示:
1 # 2 # BUILD 阶段 3 # 4 FROM golang:1.10 AS build 5 6 # 设置我们应用程序的工作目录 7 WORKDIR /go/src/github.com/scboffspring/blog-multistage-go 8 9 # 添加所有需要编译的应用代码 10 ADD . . 11 12 # 编译一个静态的go应用(在二进制构建中包含C语言依赖库) 13 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . 14 15 # 设置我们应用程序的启动命令 16 CMD ["./blog-multistage-go"] 17 18 19 20 # 21 # 生产阶段 22 # 23 FROM scratch AS prod 24 25 # 从buil阶段拷贝二进制文件 26 COPY --from=build /go/src/github.com/scboffspring/blog-multistage-go/blog-multistage-go . 27 CMD ["./blog-multistage-go"]
如你所见,同一个Dockerfile文件中我们添加第二个FROM语句。这次,我们直接拉取二进制文件,不需要添加任务其他依赖
第23行:拉取基础镜像
第26行:从 /go/src/github.com/scboffspring/blog-multistage-go/blog-multistage-go 拷贝build阶段编译的文件
第27行:使用设定的命令来启动应用程序
简单吧。
让像之前一样编译并使用docker容器
docker build -t scboffspring/blog-multistage-go . docker run --rm -ti -p 8080:8080 \ scboffspring/blog-multistage-go
我们可以看到 服务正常 启动,也就是意味着它正确的启动了!我们完成了!
让我们使用 docker images ,来看看镜像的大小
REPOSITORY ... SIZE scboffspring/blog-multistage-go ... 6.65MB
如我们之前所说,镜像的大小变为10以下。而且镜像被推送到镜像仓库后,它只有2MB。当你启动容器时,只需下载2MB即可,与之前相比时间和带宽大大的节省了呢

使用prod阶段编译的容器仅2MB
但是,它在我们的例子中不起作用。 如果运行 curl localhost:8080 ,你看到的返回的结果为500
curl localhost:8080 500 - Something bad happened
如果你查看容器的日志,你可以找到如下错误:
发生了一个错误:Get http://google.com :X509:加载系统跟目录失败并且没有根目录可以使用
我们尝试使用https来连接goole服务器,但是我们没有用于验证Google的SSL证书的CA(证书颁发机构)证书或是其他网站的CA证书。如果你的应用不需要使用SSL的话,可以选择调到下一节,否则,让我们来改善我们的软件使得其可以进行访问。
阶段3 - (可选)认证阶段
1 # 2 # BUILD 阶段 3 # 4 FROM golang:1.10 AS build 5 6 # 设置我们应用程序的工作目录 7 WORKDIR /go/src/github.com/scboffspring/blog-multistage-go 8 9 # 添加所有需要编译的应用代码 10 ADD . . 11 12 # 编译一个静态的go应用(在二进制构建中包含C语言依赖库) 13 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . 14 15 # 设置我们应用程序的启动命令 16 CMD ["./blog-multistage-go"] 17 18 19 # 20 # CERTS Stage 21 # 22 FROM alpine:latest as certs 23 24 # Install the CA certificates 25 RUN apk --update add ca-certificates 26 27 # 28 # PRODUCTION STAGE 29 # 30 FROM scratch AS prod 31 32 # 从certs阶段拷贝CA证书 33 COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 34 # 从buil阶段拷贝二进制文件 35 COPY --from=build /go/src/github.com/scboffspring/blog-multistage-go/blog-multistage-go . 36 CMD ["./blog-multistage-go"]
第23行:我们新的certs阶段,使用alpine镜像
第25行:安装最新版的CA证书
第33行:从certs层拷贝证书,并保存为 /etc/ssl/certs/ca-certificates.crt
让我们再次编译并使用docker容器
docker build -t scboffspring/blog-multistage-go . docker run --rm -ti -p 8080:8080 \ scboffspring/blog-multistage-go
现在, curl localhost:8080 将会返回真实的页面!它真的奏效了!
使用 docker images 查看,镜像依然还是非常小的
REPOSITORY ... SIZE scboffspring/blog-multistage-go ... 6.89MB
额外福利:在指定的阶段为镜像添加tag
有时候我们可能会再各个阶段为镜像创建一个tag,在我们的示例中,我们可能也会将build阶段发布到Docker,因为它对开发真的十分有用。
要想这样做的话,只需要在build镜像的时候简单的使用** --target=NAMEOFTHESTAGE.
举个例子:
docker build -t scboffspring/blog-multistage-go:build . --target=build
总结
现在你已经能够为你的Golang应用程序创建一个非常轻量级的应用程序。阶段构建的概念在其他的案例中其实也是非常有用的
我再NodeJS世界中的一个用法是第一阶段编译TypeScript项目。然后第一个阶段编译以便使得该镜像可以运行测试。此镜像也能够用于开发环境,因为它包含了所有开发环境所需的依赖
当第一阶段测试通过后,第二阶段只是简单的安装项目中的 package.json 中的依赖(并不是测试环境依赖)。它只将编译和缩小的代码复制到镜像中,然后将该镜像推送并部署到生产中
Recommend
-
43
我非常喜欢 Vue 。这是个优秀的框架,可以帮助我们搭建完美的web应用程序。但真正神奇的地方是你不仅仅可以用它搭建web应用程序,还可以使用 Weex...
-
26
-
15
Code Pattern 使用 Python API 构建流应用程序 使用 Jupyter Notebook 和 Streamsx 构建应用程序,以用于处理用户浏览网站时发生的鼠标点击事...
-
16
使用 IBM Streams 构建 Apache Kafka 流应用程序 – IBM Developer Richard Hagarty 发布: 2020-11-24
-
12
作者|Saurabh Mhatre 编译|Flin 来源|analyticsvidhya 互联网上有很多资源可以找到关于机器学习数据集的见解和训练模型,但是关于如何使用这些模型构建实际应用程序的文章很少。 因此,今天我们将通过首先使用hackathon中的数据集来...
-
8
...
-
7
使用SvelteKit构建实时websocket应用程序 - Ingest 我们最近构建了一个webhook 测试工具,可以为每个请求自动生成类型。它叫做https://typedwebhook.tools,它是免费的、
-
6
最近发现项目(基于Vue2)构建比较慢, 一次上线发布需要 15 分钟, 效率低下。如今这个时代,时间就是金钱,效率就是生命。
-
8
开发者说丨如何从零开始构建一个轻量级应用 2022年03月10日 02:54 · 阅读 1599 ...
-
12
Flask:轻量级 Python Web 应用程序框架 作者:树言树语Tree 2023-06-13 13:38:00 Flask 应用程序的基本结构包括一个主应用程序对象和多个视图函数。Flask 应用程序通常被组织为一个包,包含多个模块。这些模块通...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK