昨天帶大家認識了什麼是 Docker?
後,今天就來分享如何把我們寫過的專案推上 Docker Hub 為整個社群貢獻一份心力吧!
這是一套把專案建置成容器的過程,但我們其實只需要轉成映像檔就能順利地推上 Docker Hub,但我還是會帶大家完整做一次,那我們就開始吧!
(圖片來源:https://medium.com/swlh/understand-dockerfile-dd11746ed183)
首先我們需要在我們的專案下在額外創建一個Dockerfile
的檔案(跟我們之前介紹到的 Makefile 是同一個意思)
如果是用 GoLand 開發的,可以很輕鬆的直接建立現成模板。
# 第 1 階段:建置階段
FROM --platform=$BUILDPLATFORM golang:1.23.2-alpine3.20 AS builder
# 設定工作目錄
WORKDIR /app
# 定義目標作業系統和架構的建置參數
ARG TARGETOS
ARG TARGETARCH
# 定義 Go 快取的建置參數
ARG GOCACHE=/app/.cache/go-build
ARG GOMODCACHE=/app/.go/pkg/mod
# 設定 Go 的環境變數
ENV GOCACHE=${GOCACHE} \
GOPATH=/app/.go \
GOMODCACHE=${GOMODCACHE}
# 複製 go.mod 和 go.sum 檔案
COPY go.mod go.sum ./
# 使用快取下載相依套件
RUN --mount=type=cache,target=${GOMODCACHE} \
go mod download
# 複製專案的所有檔案
COPY . .
# 建立快取目錄
RUN mkdir -p ${GOCACHE} ${GOMODCACHE}
# 使用快取建置 Go 應用程式
RUN --mount=type=cache,target=${GOCACHE} \
--mount=type=cache,target=${GOMODCACHE} \
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o my-first-go-service
# 第 2 階段:最終映像
FROM alpine:3.20
# 設定工作目錄
WORKDIR /root/
# 從建置階段複製編譯好的二進位檔
COPY --from=builder /app/my-first-go-service .
# 開放應用程式的埠號
EXPOSE 8080
# 定義預設的執行指令來啟動應用程式
CMD ["./my-first-go-service"]
(無關緊要的小提示:程式區段因為沒有 Dockerfile 的語法高亮格式,所以在尋找格式的途中,突然發現 swift 可以直接無縫銜接上,不愧是我大🍎,太神啦XD)
(無關緊要的小提示2:因為swift的註解是 //
,但是 Dockerfile 的註解是 #
所以如要直接使用上面內容,請手動修正註解~)
第一段的
From
我是從 (📎Golang 官方映像檔)中尋找的,如果版本過時了,請再從此連結中查看最新版的寫法。
關鍵字 | 描述 |
---|---|
FROM |
指定基礎映像檔,AS 用於命名階段,方便多階段建置中引用該階段。 |
WORKDIR |
設定 Docker 容器中的工作目錄,後續指令會在該目錄下執行。 |
ARG |
定義建置過程中的參數,這些參數可以在建置過程中設置特定值。 |
ENV |
設定環境變數,這些變數在容器執行期間會被保留並使用。 |
COPY |
將檔案或目錄從本地檔案系統複製到容器內指定的路徑。 |
RUN |
執行指定的命令,例如安裝相依套件、建置程式等。在建置階段時運行,並將結果保存到映像檔中。 |
--mount=type=cache |
用於快取目錄,減少重複下載或建置相依檔案的時間。 |
CGO_ENABLED |
設定 Go 編譯選項,用來控制是否啟用 CGO(C 語言擴展)。 |
GOOS |
設定要編譯的 Go 應用程式的目標作業系統(例如 Linux、Windows)。 |
GOARCH |
設定要編譯的 Go 應用程式的目標架構(例如 amd64、arm64)。 |
EXPOSE |
宣告容器中開放的埠號,這通常用來告訴其他開發者該應用程式會在哪個埠上運行。 |
CMD |
指定容器啟動時要執行的命令。 |
參考資料:https://itnext.io/blazing-fast-golang-docker-builds-1e8829f743ca
從這裡開始,以下內容皆在專案下的 terminal 執行
go env GOCACHE GOMODCACHE
</* Output: */>
/Users/imac/Library/Caches/go-build
/Users/imac/go/pkg/mod
# 設置 GOCACHE 的路徑
export GOCACHE=<#LOCAL_GOCACHE#>
# 設置 GOMODCACHE 的路徑
export GOMODCACHE=<#LOCAL_GOMODCACHE#>
docker build --no-cache --compress \
--platform <#platform1#>,<#platform2#> \
--build-arg GOCACHE=${GOCACHE} \
--build-arg GOMODCACHE=${GOMODCACHE} \
-t <#DOCKER_HUB_USERNAME#>/<#IMAGE_NAME#>:<#IMAGE_TAG#> .
# example
docker build --no-cache --compress --platform linux/amd64,linux/arm64 --build-arg GOCACHE=${GOCACHE} --build-arg GOMODCACHE=${GOMODCACHE} -t mia1001/my-first-service:latest .
--no-cache
:強制不使用本地快取來進行映像的建構。雖然這會拖慢建置速度,但是這麼做可以減少外部因素導致建置映像檔失敗的問題。--compress
:這個選項可以減少建構的映像大小,透過壓縮圖層來優化映像--platform linux/amd64,linux/arm64
:platform 讓我們能建置多平台的映像檔(小提示:這裡的多平台
指的是不同架構的CPU
,我們的OS
還是只能為同一個,因為我們在前面的 Dockerfile 開頭就是指定我們可以運行的OS
類型了)--build-arg
:建置時參數。這些參數會被傳遞到 Dockerfile 中,可以用來設定一些環境變數,並且僅在映像檔建置過程中有效。t
:標記映像檔,格式為 username/repository:tag。latest 是標籤,可以根據需要更改。- 最後的
.
:表示 Dockerfile 位於當前目錄。
<#內容#>
:這是在 Xcode 裡面可以去替換內容的寫法,以下都用這種方式來做替換說明。
無版本
是屬於通用型的,如果不是很明白的話,希望這篇問題紀錄能幫助你更好理解。)因為我們的 platform
正常來說都會搭配 buildx
來做使用,但是你可以透過下列步驟來省去這個麻煩事項。
Docker app
,然後我們從右下方的通知中,來去找到 Multi-platform image
下的 Enable
選項來做開啟。Enable and restart
就完成了!在推送到 Docker Hub 之前,我們可以先在本地測試映像檔是否運行正常。
docker run -d \
-p <#CONTAINER_PORT#>:<#LOCAL_PORT#> \
--name <#CONTAINER_NAME#> \
<#DOCKER_HUB_USERNAME#>/<#IMAGE_NAME#>:<#IMAGE_TAG#>
# example
docker run -d -p 8080:8080 --name my-go-app-container mia1001/my-first-go-service:latest
-d
:以分離模式運行容器。-p
:將容器的 8080 端口映射到本地的 8080 端口(根據您的應用調整)。--name
:為容器命名。mia1001/my-first-go-service:latest
:指定要運行的映像檔。
可以通過訪問 http://localhost:8080 來檢查應用是否正常運行。
如果確認都正常的話,我們可以從 Docker app
來做這步驟,或是你想用下面指令也可以。
// 停止正在運行的容器
docker stop <#CONTAINER_NAME#>
// 移除容器
docker rm <#CONTAINER_NAME#>
這不是確保我們能正常推送服務到我們自己的帳號下。
docker login
如有需要,系統將提示您輸入 Docker Hub 的使用者名稱和密碼。
docker push <#DOCKER_HUB_USERNAME#>/<#IMAGE_NAME#>:<#IMAGE_TAG#>
# example
docker push mia1001/my-first-go-service:latest
推送過程中,Docker 將上傳映像檔的各個層(layers)。根據映像檔的大小和網絡速度,這可能需要一些時間。
如此圖,有出現就代表成功了
我們點進去後也可以看到我們當前服務支援的平台有哪些
小提示: IMAGE_TAG 的用處就在於,我們可以很清楚知道其他人的映像檔中,不同 TAG 所支援的平台有哪些。
docker pull <#DOCKER_HUB_USERNAME#>/<#IMAGE_NAME#>:<#IMAGE_TAG#>
docker run -d -p <#CONTAINER_PORT#>:<#LOCAL_PORT#> <#DOCKER_HUB_USERNAME#>/<#IMAGE_NAME#>:<#IMAGE_TAG#>
# example
docker pull mia1001/my-first-go-service:latest
docker run -d -p 8080:8080 mia1001/my-first-go-service:latest
恭喜你!完成這最後一步後,你就能算是另一種形式的開源家了~
(前提:我明明都是很認真在做分享,但大家好像對閒話比較感興趣,真是奇怪)
今年會想挑戰寫Go是因為,我也是在今年寒假才開始接觸 Golang ,雖然學過幾個月的時間,可是沒有能用在開發的地方,而且當初在學習時還沒有養成寫筆記的習慣,於是就順道寫個鐵人賽當複習,還能順便研究一下沒碰觸過的領域好像也蠻不錯的(雖然每次好像都遊走在快斷賽的邊緣)?
在這30天之中,我感覺自己有比較會去撰寫一篇教學文(前面跟後面的風格差蠻多的,但我也懶得再去調整了XD),而且很高興的部分在於,自己能從那個每天看網路上各大文章的人,轉變成分享自己的學習與收穫,那感覺確實蠻不錯的,而且在寫分享文時,總會有一股動力推著自己去嘗試研究沒接觸過的方向(畢竟我研究不出來就真的要斷賽了)。
總結而言,如果看完後這30天的教學後,想在這條路上精進的,可以多加閱讀(不同版本更新的內容)(但我通常很懶,只想看別人介紹)如[這篇] / [另一篇],或是也可以參加一年一次的GopherDay,我也有參加今年的GopherDay,雖然我感覺自己好像聽的一知半解(可能跟我太菜有關),但我還是能被現場的熱情給吸引,而且他的紀念衣我也好喜歡穿著它,總感覺穿上後就莫名有熱誠開發了?
那我的建議大概就到這邊,感謝各位觀看這30天的歷程,有緣在跟大家相見。