最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

構(gòu)建Golang應(yīng)用最小Docker鏡像的實現(xiàn)

 更新時間:2020年05月31日 11:38:49   作者:Eyas  
這篇文章主要介紹了構(gòu)建Golang應(yīng)用最小Docker鏡像的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

我通常使用docker運行我的 golang 程序,在這里分享一下我構(gòu)建 docker 鏡像的經(jīng)驗。我構(gòu)建 docker 鏡像不僅優(yōu)化構(gòu)建后的體積,還要優(yōu)化構(gòu)建速度。

示例應(yīng)用

首先貼出代碼例子,我們假設(shè)要構(gòu)建一個 http 服務(wù)

package main

import (
 "fmt"
 "net/http"
 "time"

 "github.com/gin-gonic/gin"
)

func main() {
 fmt.Println("Server Ready")
 router := gin.Default()
 router.GET("/", func(c *gin.Context) {
 c.String(200, "hello world, this time is: "+time.Now().Format(time.RFC1123Z))
 })
 router.GET("/github", func(c *gin.Context) {
 _, err := http.Get("https://api.github.com/")
 if err != nil {
  c.String(500, err.Error())
  return
 }
 c.String(200, "access github api ok")
 })

 if err := router.Run(":9900"); err != nil {
 panic(err)
 }
}

說明:

  • 這里選擇 Gin 作為例子,是為了演示我們有第三方包條件下要優(yōu)化構(gòu)建速度
  • main函數(shù)第一行打印了一行字,為了演示后面啟動時遇到的一個坑
  • 跟路由打印了時間,為了演示后面遇到的關(guān)于時區(qū)的坑
  • 路由 github 嘗試訪問 https://api.github.com,為了演示后面遇到的證書坑

這里我們可以先試一試構(gòu)建后包的體積

$ go build -o server
$ ls -alh | grep server
-rwxrwxrwx 1 eyas eyas 14.6M May 29 10:26 server

14.6MB,這是一個http服務(wù)的 hello world,當(dāng)然這是因為使用了 gin ,所以有些大,如果用標(biāo)準(zhǔn)包 net/http 寫的 hello world,體積大概是接近 7 MB

Dockerfile 的進化

版本一,初步優(yōu)化

先看看第一個版本

FROM golang:1.14-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
RUN go build -ldflags "-s -w" -o server

FROM scratch as runner
COPY --from=builder /usr/src/app/server /opt/app/
CMD ["/opt/app/server"]

說明:

  • 選擇 golang:1.14-alpine 作為編譯環(huán)境,是因為這是體積最小的golang編譯環(huán)境
  • 設(shè)置 GOPROXY 是為了提升構(gòu)建速度
  • 先復(fù)制 go.mod 和 go.sum ,然后 go mod download,是為了防止每次構(gòu)建都會重新下載依賴包,利用docker構(gòu)建緩存提升構(gòu)建速度
  • go build 時加上 -ldflags "-s -w" 去除構(gòu)建包的調(diào)試信息,減小go構(gòu)建后程序體積,大概能減小 1/4 吧
  • 使用了多階段構(gòu)建,也就是 FROM XXX as xxx ,在構(gòu)建程序包的時候,使用帶編譯環(huán)境的鏡像去構(gòu)建,運行的時候其實完全不需要go的編譯環(huán)境,所以在運行階段使用docker的空鏡像 scratch 去運行。這部是減小鏡像體積最有效的方法了。

好了,下面開始構(gòu)建鏡像

$ docker build -t server .
...
Successfully built 8d3b91210721
Successfully tagged server:latest

到了這一步,構(gòu)建成功,看看鏡像大小

$ docker images
server     latest     8d3b91210721   1 minutes ago    11MB

11MB,還行,現(xiàn)在運行一下

$ docker run -p 9900:9900 server
standard_init_linux.go:211: exec user process caused "no such file or directory"

發(fā)現(xiàn)啟動報錯了,而且main函數(shù)的第一行打印語句都沒有出現(xiàn),所以整個程序完全沒有運行。錯誤原因是缺少庫依賴文件。這其實是構(gòu)建的 go 程序還依賴底層的 so 庫文件,不信可以在物理機編譯后看看它的依賴

$ go build -o server
$ ldd server
    linux-vdso.so.1 (0x00007ffcfb775000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9a8dc47000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a8d856000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f9a8de66000)

這是不是跟我們的認(rèn)知有點出入呢,說好無依賴的呢,結(jié)果還是有幾個依賴庫文件呢,雖然這幾個依賴都是最底層的,一般操作系統(tǒng)都會有,可誰叫我們選了 scratch,這個鏡像里面除了linux內(nèi)核以外真的什么都沒了。

這是因為go build 是默認(rèn)啟用 CGO 的,不信你可以試試這個命令 go env CGO_ENABLED,在 CGO 開啟情況下,無論代碼有沒有用CGO,都會有庫依賴文件,解決方法也很簡單,手動指定關(guān)閉CGO就行,而且包體積并不會增加哦,還會減少呢

$ CGO_ENABLED=0 go build -o server
$ ldd server
    not a dynamic executable

版本二,解決運行時報錯

FROM golang:1.14-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
-RUN go build -ldflags "-s -w" -o server
+RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o server

FROM scratch as runner
COPY --from=builder /usr/src/app/server /opt/app/
CMD ["/opt/app/server"]

改動點: go build 前加了 CGO_ENABLED=0

$ docker build -t server .
...
Successfully built a81385160e25
Successfully tagged server:latest
$ docker run -p 9900:9900 server
[GIN-debug] GET  /             --> main.main.func1 (3 handlers)
[GIN-debug] GET  /github          --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :9900

正常啟動了,我們訪問一下試試,訪問之前看看當(dāng)前時間

$ date
Fri May 29 13:11:28 CST 2020

$ curl http://localhost:9900    
hello world, this time is: Fri, 29 May 2020 05:18:28 +0000

$ curl http://localhost:9900/github
Get "https://api.github.com/": x509: certificate signed by unknown authority

發(fā)現(xiàn)有問題

  • 當(dāng)前系統(tǒng)時間是 13:11:28 ,但是根據(jù)由顯示的時間是 05:11:53,其實是docker 容器內(nèi)的時區(qū)不對,默認(rèn)是 0 時區(qū),可是我們國家是 東8區(qū)
  • 嘗試訪問 https://api.github.com/ 這是 https 站點,報證書錯誤

解決問題

  • 在容器放置根證書
  • 設(shè)置容器時區(qū)

版本三,解決運行環(huán)境時區(qū)與證書問題

FROM golang:1.14-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
+RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
+ apk add --no-cache ca-certificates tzdata
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o server

FROM scratch as runner
+COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/src/app/server /opt/app/
CMD ["/opt/app/server"]

在 builder 階段,安裝了 ca-certificates tzdata 兩個庫,在runner階段,將時區(qū)配置和根證書復(fù)制了一份

$ docker build -t server .
...
Successfully built e0825838043d
Successfully tagged server:latest
$ docker run -p 9900:9900 server
[GIN-debug] GET  /             --> main.main.func1 (3 handlers)
[GIN-debug] GET  /github          --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :9900

訪問一下試試

$ date
Fri May 29 13:27:16 CST 2020

$ curl http://localhost:9900    
hello world, this time is: Fri, 29 May 2020 13:27:16 +0800

$ curl http://localhost:9900/github
access github api ok

一切正常了,看看當(dāng)前鏡像大小

$ docker images
server     latest     e0825838043d   9 minutes ago    11.3MB

才 11.3MB,已經(jīng)很小了,但是,還可以更小,就是把構(gòu)建后的包再壓縮一次

版本四,進一步減小體積

FROM golang:1.14-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
- apk add --no-cache ca-certificates tzdata
+ apk add --no-cache upx ca-certificates tzdata
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
-RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o server
+RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o server &&\
+ upx --best server -o _upx_server && \
+ mv -f _upx_server server

FROM scratch as runner
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/src/app/server /opt/app/
CMD ["/opt/app/server"]

在 builder 階段,安裝了 upx ,并且go build 完成后,使用 upx 壓縮了一下,執(zhí)行一下構(gòu)建,你會發(fā)現(xiàn)這個構(gòu)建時間變長了,這是因為我給 upx 設(shè)置的參數(shù)是 --best ,也就是最大壓縮級別,這樣壓縮出來的后會盡可能的小,如果嫌慢,可以降低壓縮級別從 -1 到 -9 ,數(shù)字越大壓縮級別越高,也越慢。我使用 --best 構(gòu)建完成后看看鏡像體積。

$ docker build -t server .
...
Successfully built 80c3f3cde1f7
Successfully tagged server:latest
$ docker images
server     latest     80c3f3cde1f7   1 minutes ago    4.26MB

這下子可小了,才 4.26MB,再去試試那兩個接口,一切正常。優(yōu)化到此結(jié)束。

最終的Dockerfile

FROM golang:1.14-alpine as builder
WORKDIR /usr/src/app
ENV GOPROXY=https://goproxy.cn
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
 apk add --no-cache upx ca-certificates tzdata
COPY ./go.mod ./
COPY ./go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o server &&\
 upx --best server -o _upx_server && \
 mv -f _upx_server server

FROM scratch as runner
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/src/app/server /opt/app/
CMD ["/opt/app/server"]

總結(jié)

要減小鏡像體積,首先多階段構(gòu)建這很重要,這樣就可以把編譯環(huán)境和運行環(huán)境分開。

另外,選擇 scratch 這個鏡像其實很不明智,它雖然很小,但是它太原始了,里面什么工具都沒有,程序啟動后,連容器都進不去,就算進去了什么都做不了。所以就算一昧的追求盡可能小的鏡像體積,也不建議選擇 scratch 作為運行環(huán)境,我暫時只踩到小部分的坑,后面還有更多坑沒踩,我也沒有興趣繼續(xù)踩 scratch 的坑。

建議選擇 alpine ,alpine 的鏡像大小是 5.61MB 這個大小其實還是鏡像解壓后的大小,實際上下載鏡像的時候,只需要下載 2.68 MB 。還有,上文所有我說的鏡像體積,全都是指解壓后的鏡像體積,和實際上傳下載時的體積是不一樣的,docker自己會壓縮一次再傳輸鏡像

還有個很小的鏡像是 busybox,它的體積是 1.22MB,下載 705.6 KB ,有大部分的linux命令可用,但是運行環(huán)境還是很原始,有興趣可以去嘗試

無論是 alpine 還是 busybox ,他們都會上述時區(qū)和證書問題,同樣按照上面方法就能解決,切換到 alpine 或者 busybox 也很簡單,只需要修改 runner 基礎(chǔ)鏡像就行

-FROM scratch as runner
+FROM alpine as runner

或者

-FROM scratch as runner
+FROM busybox as runne

到此這篇關(guān)于構(gòu)建Golang應(yīng)用最小Docker鏡像的實現(xiàn)的文章就介紹到這了,更多相關(guān)Golang構(gòu)建最小Docker鏡像內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言CSP并發(fā)模型實現(xiàn)MPG

    Go語言CSP并發(fā)模型實現(xiàn)MPG

    這篇文章主要為大家介紹了Go語言CSP并發(fā)模型實現(xiàn)MPG圖文詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • GO語言運行環(huán)境下載、安裝、配置圖文教程

    GO語言運行環(huán)境下載、安裝、配置圖文教程

    這篇文章主要介紹了GO語言運行環(huán)境下載、安裝、配置圖文教程,需要的朋友可以參考下
    2017-02-02
  • go語言Timer計時器的用法示例詳解

    go語言Timer計時器的用法示例詳解

    Go語言的標(biāo)準(zhǔn)庫里提供兩種類型的計時器Timer和Ticker。這篇文章通過實例代碼給大家介紹go語言Timer計時器的用法,代碼簡單易懂,感興趣的朋友跟隨小編一起看看吧
    2020-05-05
  • golang atomic原子操作示例詳解

    golang atomic原子操作示例詳解

    這篇文章主要為大家介紹了golang atomic原子操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • Golang中interface的基本用法詳解

    Golang中interface的基本用法詳解

    Go 中接口也是一個使用得非常頻繁的特性,好的軟件設(shè)計往往離不開接口的使用,比如依賴倒置原則(通過抽象出接口,分離了具體實現(xiàn)與實際使用的耦合)。 今天,就讓我們來了解一下 Go 中接口的一些基本用法
    2023-01-01
  • Golang?RPC的原理與簡單調(diào)用詳解

    Golang?RPC的原理與簡單調(diào)用詳解

    RPC(Remote?Procedure?Call),主要是幫助我們屏蔽網(wǎng)絡(luò)編程細(xì)節(jié)?,使我們更專注于業(yè)務(wù)邏輯,所以本文主要來和大家聊聊RPC的原理與簡單調(diào)用,希望對大家有所幫助
    2023-05-05
  • 如何編寫Go語言中間件的實例教程

    如何編寫Go語言中間件的實例教程

    不知道大家有沒有寫過中間件呢,它是怎么寫的呢?下面這篇文中就來給大家分享一下使用Go,如何編寫中間件,文中通過示例代碼介紹的非常詳細(xì),供大家參考學(xué)習(xí),下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • Go語言中iota的具體使用

    Go語言中iota的具體使用

    Go語言中,iota是一個用于生成一系列相關(guān)常量值的常量生成器,常應(yīng)用于枚舉、位操作等場景,廣泛用于定義HTTP狀態(tài)碼、權(quán)限控制等,本文就來介紹一下iota的具體使用,感興趣的可以了解一下
    2024-11-11
  • golang整合jwt的實現(xiàn)示例

    golang整合jwt的實現(xiàn)示例

    json web tokens(jwt)已成為大多數(shù)web api設(shè)計中的常見身份驗證和授權(quán)方案之一,本文主要介紹了golang整合jwt的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Golang time包中的函數(shù)用法詳解

    Golang time包中的函數(shù)用法詳解

    在日常開發(fā)過程中,會頻繁遇到對時間進行操作的場景,使用 Golang 中的 time 包可以很方便地實現(xiàn)對時間的相關(guān)操作,接下來的幾篇文章會詳細(xì)講解 time 包,本文講解一下 time 包中的函數(shù),需要的朋友可以參考下
    2023-08-08

最新評論

西盟| 修武县| 吴忠市| 龙岩市| 苗栗县| 新平| 土默特左旗| 台北县| 大厂| 延长县| 临潭县| 邹城市| 华蓥市| 福安市| 定襄县| 宝兴县| 怀宁县| 合阳县| 内乡县| 金沙县| 塔河县| 苍梧县| 晋中市| 休宁县| 顺昌县| 宁南县| 南皮县| 木兰县| 麻城市| 永福县| 赫章县| 林芝县| 蓝田县| 马公市| 拜城县| 甘肃省| 广河县| 钦州市| 苍山县| 年辖:市辖区| 会东县|