多階段構建Golang程式Docker鏡像

Docker簡介

Docker是基於Linux容器技術(LXC),使用Go語言實現的開源項目,誕生於2013年,遵循Apache2.0協議。Docker自開源後,受到廣泛的關注和討論。

Docker在開發和運維中的優勢
  • 更快的交付和部署

使用Docker,開發人員可以使用鏡像來快速構建一套標準的開發環境,開發完後,測試和運維人員可以直接使用完全相同的環境來部署程式碼,實現了生產環境的無縫運行。

  • 更高效的資源利用

Docker容器的運行不需要額外的虛擬化管理程式支援,它是內核級的虛擬化 ,可以實現更高的性能,同時對資源的額外需求很低。

  • 更輕鬆的遷移和擴展

Docker容器幾乎可以在任意的平台上運行,支援主流的作業系統發行版本。這種兼容性讓用戶可以在不同平台之間輕鬆地遷移應用。

  • 更簡單的更新管理

使用Dockerfile,只需要修改一點點配置,就可以替代以往大量的更新工作。並且所有的修改都以增量的方式被分布和更新,從而實現自動化且高效的容器管理。

首先需要知道以下幾個概念
  • Docker鏡像

Docker鏡像類似於虛擬機鏡像,可以將它理解為一個只讀的模板。鏡像是創建Docker容器的基礎。通過版本管理和增量的文件系統,Docker提供了一套十分簡單的機制和創建和更新現有的鏡像,用戶可以直接從網上下載一個已經做好的應用鏡像,並直接使用。

  • Docker容器

Docker容器類似於一個輕量級的沙箱,Docker利用容器來運行和隔離應用。容器是從鏡像創建的應用運行實例。可以將其啟動、開始、停止、刪除,而這些容器都是彼此相互隔離的,互不可見的。

可以把容器看做一個簡易版的Linux系統環境(包括root用戶許可權、進程空間、用戶空間和網路空間)以及運行在其中的應用程式打包而成的盒子。

  • Docker倉庫

Docker倉庫類似於程式碼倉庫,它是Docker集中存放鏡像文件的場所。它的設計理念與Git類似。Docker鏡像庫分公開倉庫和私有倉庫。最大的公開倉庫是官方提供的Docker Hub。當然,如果不想公開鏡像,可以搭建自己的私有倉庫。

  • Dockerfile

一般介紹完以上三個概念就結束了,但我在這裡要介紹下Dockerfile,因為鏡像的好壞很大程度取決於Dockerfile。Dockerfile是一個文本格式的配置文件,用戶可以使用Dockerfile來快速創建自定義的鏡像。

多階段構建Golang應用Docker鏡像

一般Golang的Dockerfile文件會像這樣:

# Go語言環境基礎鏡像
FROM golang:latest

# 將源碼拷貝到鏡像中
COPY server.go /go/release/

# 指定工作目錄
WORKDIR /go/release

# 編譯鏡像時,運行 go build 編譯生成 app 可執行的二進位文件
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o app server.go

# 指定容器運行時入口程式 app
ENTRYPOINT ["/go/release/app"]

這種方式構建的Docker鏡像體積非常大,構建時間長,佔用磁碟空間,部署速度慢。

本篇將使用多階段構建(multi-stage builds)的方式來減少生成的Docker鏡像的體積。

多階段構建的過程中,我們在Dockerfile使用多個FROM指令,每個FROM指令使用不同的基礎鏡像構成了不同階段。你可以選擇從上一個階段的產物(一般指生成的文件)複製到下一個階段,從而確保不會把不需要的東西帶到下一階段。這種方法可以有效減小Docker鏡像的大小。參考官網

開源項目—gin+vue前後端分離項目為例,介紹Golang應用如何使用多階段構建Docker鏡像。

項目結構圖

直接上Dockerfile

# 構建:使用golang:1.13版本
FROM golang:1.13 as build

# 容器環境變數添加,會覆蓋默認的變數值
ENV GO111MODULE=on
ENV GOPROXY=//goproxy.cn,direct

# 設置工作區
WORKDIR /go/release

# 把全部文件添加到/go/release目錄
ADD . .

# 編譯:把cmd/main.go編譯成可執行的二進位文件,命名為app
RUN GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -ldflags="-s -w" -installsuffix cgo -o app cmd/main.go

# 運行:使用scratch作為基礎鏡像
FROM scratch as prod

# 在build階段複製時區到
COPY --from=build /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 在build階段複製可執行的go二進位文件app
COPY --from=build /go/release/app /
# 在build階段複製配置文件
COPY --from=build /go/release/config ./config

# 啟動服務
CMD ["/app"]

Dockerfile各指令詳細介紹://docs.docker.com/engine/reference/builder/#usage

構建階段,以golang:1.13為基礎鏡像,把項目編譯成Linux x64位系統可執行的二進位文件(命名為app)。運行階段,把必要的配置文件和app複製到scratch鏡像中運行。最後的鏡像只包括運行階段的文件,所以體積很小,只有10多M。

  • golang編譯知識
GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -ldflags="-s -w" -installsuffix cgo -o app cmd/main.go

GOOS:目標系統為linux

CGO_ENABLED:默認為1,啟用C語言版本的GO編譯器,通過設置成0禁用它

GOARCH:32位系統為386,64位系統為amd64

-ldflags:用於傳遞每個go工具鏈接調用的參數。

  • -s: 省略符號表和調試資訊
  • -w: 省略DWARF符號表

-installsuffix:在軟體包安裝的目錄中增加後綴標識,用於區分默認版本

-o:指定編譯後的可執行文件名稱

cmd/main.gomain函數所在路徑

scratch鏡像

scratch是一個空鏡像,只能用於構建其他鏡像,比如你要運行一個包含所有依賴的二進位文件,如Golang程式,可以直接使用scratch作為基礎鏡像。scratch本身是不佔空間的,所以使用它構建的鏡像大小几乎和二進位文件本身一樣大,從而讓Golang應用的Docker鏡像體積非常小。
參考://mp.weixin.qq.com/s/S1Ib08SpQbf1SCbCutUoqQ

製作Golang程式Docker鏡像

這裡直接使用Linux伺服器製作Docker鏡像,也可以使用Docker for windows在windows上製作Docker鏡像。

  • 前提:Linux伺服器安裝DockerGolang

(1)拉取項目
go get -x github.com/bingjian-zhu/gin-vue-admin/cmd

(2)修改配置文件
進入項目中config/config.yml,修改database配置,改成自己的MySQL資料庫。run-mode運行模式改成release

(3)生成Docker鏡像

鏡像名字為zhubingjian/gin-vue-admin,版本1.0,不要漏了最後面的.

docker build -t zhubingjian/gin-vue-admin:1.0 .

其中zhubingjian是我Docker Hub的帳號,想要把鏡像上傳到自己的Docker Hub上,鏡像名稱需要以Docker Hub帳號+/開頭。已經生成的鏡像可以通過tag修改名稱。

運行指令:docker images

可以看到我們生成的Docker鏡像只有14.3M,非常小。名稱為none的鏡像是多階段生成過程留下的鏡像,可以使用docker rmi 鏡像ID把它刪除掉。

(4)運行鏡像

docker run -d --name gin-vue-admin -p 8000:8000 zhubingjian/gin-vue-admin:1.0

  • -d:容器以守護進程的方式運行
  • --name:容器的名字,可以不指定名字
  • -p:指定宿主機器與容器的埠對應關係, 格式為「宿主埠:容器埠」,如果不指定此項,將無法訪問容器里的服務

運行指令:docker ps -a

可以看到所有容器,STATUS顯示Up 10 seconds,表示兩個意思:一是容器在運行;二是已經運行了10秒。如果顯示Exited 6 days,則表示已經停止運行6天了。

目前為止,我們已經成功製作Docker鏡像且運行了。接下來把前端的VUE項目也做成Docker鏡像並運行。

製作VUE程式Docker鏡像

  • 前提:伺服器安裝Docker,nodejsnpm

(1)進入vue-admin目錄,下載npm

npm install

(2)修改vue-admin目錄下的.env.production文件,改成自己的後台介面地址

(3)發布項目,生成dist文件夾
npm run build:prod

(4)生成Docker鏡像
docker build -t zhubingjian/vue-admin:1.0 .

(5)運行鏡像
docker run -p 80:80 -d --name vue-admin zhubingjian/vue-admin:1.0

參考:Docker 部署 vue 項目

最後,在瀏覽器上打開://zbj-home.picp.io

就可以看到程式已經在伺服器上跑了。

把鏡像推送到Docker Hub上

首先需要註冊Docker Hub帳號,我註冊時候是有坑的,註冊系統需要做人機檢測,需要翻牆才能完成。官網地址://hub.docker.com/

(1)登錄Docker Hub
docker login

然後輸入用戶名和密碼

(2)推送鏡像

docker push zhubingjian/gin-vue-admin:1.0
docker push zhubingjian/vue-admin:1.0

總結

本篇主要介紹如何使用多階段構建Golang程式的Docker鏡像,此方法可以精簡Docker鏡像。

源碼地址://github.com/Bingjian-Zhu/gin-vue-admin

參考:

Tags: