怎樣構建 Golang Dockerfiles?
導讀 | 本文會介紹為什麼 Golang 可以很好地展示這些特性,因為 Golang 可以編譯為單個二進位制檔案(或一組二進位制檔案)。 |
Docker 提供了一些出色的構建時功能和基本映像,我們可以用它們來實現輕量、安全和高效的應用程式構建。
本文會介紹為什麼 Golang 可以很好地展示這些特性,因為 Golang 可以編譯為單個二進位制檔案(或一組二進位制檔案)。
這篇文章的示例所關注的焦點是極簡主義。儘管這些示例很基礎,但它們非常重要,你可以在這些概念基礎上為大型 Golang 專案引入更多最佳實踐,以提高安全性和效率。
我們將使用這個簡單的 main.go 來演示本文的概念:
package main import "fmt" func main() { fmt.Println("Hello Cloudreach!") }
Docker 在 Dockerfile 文件中一上來就強調:儘量減少層數是一個最佳實踐!這是一個重要的概念,必須從一開始就做好。
你很容易就能寫一個包含很多層的 Dockerfile——它的語法就有這個傾向——結果你不知不覺中就會寫出很多效率低下的內容。
最佳實踐是將構建的相關階段分組和連結在一起,例如下載依賴項、供應商資料夾整合或使用 RUN 設定構建環境等階段。
你還需要考慮分組的哪些部分是可以經常更改的,然後將它們分組到 Dockerfile 中儘可能低的層,同時把靜態構建依賴項、構建環境配置或應用程式資產放到 Dockerfile 中儘可能上層的位置上。
每一層,更具體地說是 Dockerfile 中以指令開頭的每一行,都經過雜湊處理並建立在另一層之上,最後一個映像由“堆疊(stacked)”層構成。
由於 Dockerfile 的每一層都是從下一層繼承的,因此構建快取提供了一種很好的機制,可以幫助你跳過已構建或靜態的內容,然後轉到你需要構建和重新雜湊的部分!
儘量縮短構建時間是很重要的,因為高效的 CI/CD 系統每天都會執行這些構建很多次。當團隊規模逐漸擴大後,這可能意味著大量的構建工作,可能會需要很多 Jenkins worker,有時甚至需要很長時間才能整合開發人員的程式碼!
Docker 有一些構建快取功能,它們可以顯著節省構建時間。等待構建的時間越短,意味著整合和自動化測試的速度也就越快,也能提升 CI/CD 流程的速度,讓你的流程足以和團隊規模相匹配。
儘可能為應用程式安排單獨的非 root 使用者也是很重要的。你只需要在 adduser 中使用 RUN 指令,然後在 Dockerfile 中使用 USER 指令就可以使用這個使用者來執行二進位制檔案了。
下面是使用這些最佳實踐構造的一個最精簡的 Dockerfile main.go 示例,它只有一個基本的 main 函式,沒有外部依賴項:
FROM golang:alpine RUN mkdir /app ADD . /app/ WORKDIR /app RUN go build -o main . RUN adduser -S -D -H -h /app appuser USER appuser CMD ["./main"]
構建後,生成的映像大小為 378MB:
$ docker build -t hellocloudreachmain:1.0 . -f Dockerfile.single ... (build output omitted) $ docker images | grep hellocloudreachmain hellocloudreachmain 1.0 d1c5090585bc Less than a second ago 378MB
儘可能使用基於 Alpine 的官方映像!它是基於 busybox 和 musl 構建的,最輕量級的 Linux 發行版之一。與較重的發行版相比,它的容器映像體積很小,這是一個輕鬆提升效率的好方法。
但我們還可以進一步簡化!這些官方映像構建(比如golang:alpine)都包含很多層,裡面含一些安全元件,用來構建應用程式資產時很方便;但是如果我們的應用程式不需要這些層,那就不要把它們放進去!我們需要使用其他一些 Docker 構建功能,進一步縮小檔案體積。
當需要在生產環境中執行應用程式時,我們需要讓容器的設計可以確保效能和安全性。我們還需要儘可能多的可移植性,以便輕鬆地移動容器,並使用 DockerSwarm 和 Kubernetes 之類的編排器對其進行大規模排程。
將映像推入和拉出登錄檔所需的時間應儘量縮短。編寫用於生產的 Dockerfile 時,要記住的一個要點就是在最終執行時映像中實踐極簡主義。
如果執行的時候並不需要某樣東西,請不要把它放進去!
在開發環境中,有時需要一個“較重”的容器映像,也許是一個開發人員專屬的 Dockerfile,方便開發人員隨時扔進來一些工具,和容器一起進行除錯等開發活動。也可能會附加或保留一兩個卷和活動容器互動。這些當然都是很常見的情況!
但是,隨著容器映像沿開發管線向上移動,一定要記得把這些東西都取出來。一條正規的安全軟體供應鏈會要求在管道中儘早構建最終映像,對映像簽名,並將經過正式簽名的映像推到生產環境的各個階段。
因此,你需要儘早在供應鏈中構建、驗證、整合和簽名這個最小化的映像;這是 Dockerfile 開發人員、QA 團隊和安全工程師必須熟悉的操作!換句話說,只構建一次,然後讓你的流程將生成的映像投入生產環境。
多階段構建是實現這一目標的一個好方法!多階段涉及的基本原理包括:呼叫一個臨時容器以簡化應用程式的構建,然後將構建的資產從這個空間複製到只有執行應用所需必要元件的容器映像中。拿之前的 Dockerfile 示例來說:
FROM golang:alpine as builder RUN mkdir /build ADD . /build/ WORKDIR /build RUN go build -o main . FROM alpine RUN adduser -S -D -H -h /app appuser USER appuser COPY --from=builder /build/main /app/ WORKDIR /app CMD ["./main"]
注意這個 Dockerfile 中的兩個 FROM 指令。我們將第一個標記為“builder”,使用它來構建應用程式。
然後,我們使用第二個 FROM,這一次是從基本的“alpine”(非常輕巧!)中提取的,並將我們構建的可執行檔案從該環境複製到該新環境。
這就讓映像的體積比以前小了很多!另外,“builder”容器被快取在 docker builder 上下文中,因此可以像前面的示例一樣利用構建快取來提升速度!
$ docker build -t hellocloudreachmain:1.1 . -f Dockerfile.multi ... (build output omitted) $ docker images | grep hellocloudreachmain hellocloudreachmain 1.0 d1c5090585bc 8 minutes ago 378MB hellocloudreachmain 1.1 ea737df5cc64 Less than a second ago 6.16MB
這個映像的大小是 6.16MB。相比 378MB 來說,體積減少的效果很明顯!
實際上,我們在精簡之路上還可以走得更遠。Golang 有很多有趣的特性,其中之一是你可以編譯為單個二進位制檔案,並且在大多數情況下,你可以使用某些特殊的構建時引數將所有相關的庫靜態編譯進這個二進位制檔案。
這樣我們就能構建一個最小化的 Docker 容器,並減少額外的執行時開銷,以實現我們所追求的出色效能、可移植性和安全性!
如果我們可以將 Golang 應用程式編譯為單個二進位制檔案,並將它靜態連結到依賴項上,就可以使用一個 0KB 容器來執行這個應用程式。這是 Docker 提供的一個特殊的基礎映像,稱為“scratch”。
在我們的 Docker 培訓課程中總會遇到一個問題:所有容器內部都裝有作業系統嗎?答案是否定的,就是因為有這種特殊型別!
這個映像內部沒有關聯受支援的作業系統環境。它有一些特殊要求,最重要的是,主機的架構必須支援編譯好的二進位制檔案的架構(x86、x64 等),然後,你實際得到的容器除了隔離功能和 Docker 容器的那些優秀特性外,不向應用程式提供任何功能或支援!儘量使用 scratch 作為基礎映像,將為你的應用程式容器提供極高的簡約性和安全性水平。
FROM golang:alpine as builder RUN mkdir /build ADD . /build/ WORKDIR /build RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main . FROM scratch COPY --from=builder /build/main /app/ WORKDIR /app CMD ["./main"]
在第 6 行,FROM scratch 告訴 Docker 從頭開始,就像我們在上一個多階段示例中看到的那樣,但這次使用的是 0KB 臨時映像。第一個階段與之前類似,但這次我們在構建階段使用一些編譯時引數來指示 go 編譯器將執行時庫靜態連結到二進位制檔案本身:
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main.
在此示例中,最終的 Docker 映像只會包含這一個可執行檔案,而無需使用容器作業系統。
$ docker build -t hellocloudreachmain:1.2 . -f Dockerfile.scratch ... (build output omitted) $ docker images | grep hellocloudreachmain hellocloudreachmain 1.0 d1c5090585bc 8 minutes ago 378MB hellocloudreachmain 1.1 ea737df5cc64 4 minutes ago 6.16MB hellocloudreachmain 1.2 bda5c99404ae 33 seconds ago 2.01MB
太棒了,生成的容器大小隻有 2.01MB!與最初的 378MB 映像相比,這是一個巨大的進步!
$ docker run -it hellocloudreachmain:1.0 Hello Cloudreach! $ docker run -it hellocloudreachmain:1.1 Hello Cloudreach! $ docker run -it hellocloudreachmain:1.2 Hello Cloudreach!
我們用一個基本的 Dockerfile 舉例,然後一步步縮減最終映像的體積。透過這個簡單的練習,我們很容易看到在構建 Golang 應用程式時,有很多選擇可以在 Docker 構建中實踐極簡主義。
我們有很多辦法可以利用這種語言的特性,及其在編譯期間提供給開發人員的特性來減小容器的體積。由於 Golang 可以編譯為靜態連結的可執行檔案,因此我們能利用這類特性為執行時剝離所有不必要的元件。
更復雜的應用程式和構建可能無法遵循和上面完全相同的設計模式,但是這些原理可以應用在大多數 Golang Dockerfile 上!只需花一些時間來確保自己使用行業最佳實踐正確構建 Dockerfile,就能成功地在容器中構建快速、安全和可擴充套件的應用程式!
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2912894/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 怎樣將Gradle構建速度提升90%Gradle
- 用 Golang 構建 gRPC 服務GolangRPC
- 使用Golang和MongoDB構建 RESTful APIGolangMongoDBRESTAPI
- 使用Golang和MongoDB構建微服務GolangMongoDB微服務
- 使用Golang快速構建WEB應用GolangWeb
- 怎樣去構建一個優質的Docker容器映象Docker
- Golang:使用 httprouter 構建 API 伺服器GolangHTTPAPI伺服器
- Golang建立建構函式的方法詳解Golang函式
- Golang如何快速構建一個CLI小工示例Golang
- 簡單介紹如何使用Bazel構建Golang程式Golang
- 為 Python Web App 編寫 DockerfilesPythonWebAPPDocker
- 用Vagrant構建統一的golang開發環境Golang開發環境
- 怎樣結構化xml檔案到下列樣式?XML
- 基於GitLab CI搭建Golang自動構建環境GitlabGolang
- 如何構建一個高效的 golang web 開發環境GolangWeb開發環境
- AI雲平臺怎麼構建AI
- Python培訓教程分享:怎樣使用Pandas的內建資料結構繪圖?Python資料結構繪圖
- 3 行寫爬蟲 - 使用 Goribot 快速構建 Golang 爬蟲爬蟲Golang
- 網站建設的流程是怎麼樣的?網站
- 怎樣組建一個TPM專案團隊?
- 探索使用 Golang 和 Webassembly 構建一個多人遊戲伺服器GolangWeb遊戲伺服器
- Golang物件導向程式設計之建構函式【struct&new】Golang物件程式設計函式Struct
- 用 Golang 寫一個搜尋引擎(0x06)--- 索引構建Golang索引
- 怎樣能dump內部資料結構?資料結構
- 現代 CSS 高階技巧,像 Canvas 一樣自由繪圖構建樣式!CSSCanvas繪圖
- 建設智慧城市怎樣做才不會迷失方向?
- 「Golang成長之路」內建容器Golang
- 怎麼樣dump資料庫內部結構資料庫
- golang 結構體自定義排序 + 按照分數算排名同分數排名一樣Golang結構體排序
- 組織結構圖是什麼?怎樣繪製結構圖?
- 怎麼構建健壯的分散式系統?分散式
- 使用golang+antlr4構建一個自己的語言解析器(二)Golang
- 網站建設怎麼樣做?網頁設計趨勢網站網頁
- 學Python應該選擇怎樣的機構?Python
- 我們需要怎樣的湖倉一體架構?架構
- sdn專線架構是怎樣的?如何工作?——Vecloud架構Cloud
- 要怎樣才能成為一名架構師?架構
- 怎麼樣架構分散式的j2ee???架構分散式