寫完網站後,總不可能總是透過 go run
的指令將網站運行起來,勢必要透過 build
的方式將整個程式封裝誠執行檔,但光是封裝執行檔,對於不同環境必須要 build 出不同檔案,這時候將程式 build 成 docker image 就是一個不錯的選擇,今天就來聊聊怎麼將 golang
程式封裝成 docker image
吧!
還記得在前幾天我們有寫過一個 一對一隨機匿名聊天室
的專案嗎,我們透過此專案當作範例
專案位置:https://github.com/codingXiang/random_anonymous_chat
要使用 Docker 打包必須要透過撰寫 Dockerfile
讓 Docker Engine
知道要做什麼步驟,首先我們先一步一步建構簡單的 Dockerfile,首先,我們在專案的根目錄建立一個名為 Dockerfile
的檔案。
以下步驟都是要寫在 Dockerfile
裡面
要 build go 的專案,勢必一定要有 go 的環境,我們可以在 docker hub
中找到 golang
的環境 image,所以可以在 FROM 的部分寫上 golang
FROM golang
此步驟為定義專案要放在 container 內部的哪一個位置,通常我習慣會放在根目錄底下的 /app
資料夾,因此可以先建立 /app
後,將 WORKDIR
設定到此
RUN mkdir -p /app
WORKDIR /app
因為要在 Container 內部進行 build,因此我們將整個專案 COPY
到 Container 內
COPY . .
在進行 build 之前,要先將相依的 package 進行下載,可以透過 go mod download
的方式,依照 go.mod
與 go.sum
裡面定義的 package 版本進行下載
RUN go mod download
接下來就是重頭戲了,將專案透過 go build
的方式打包,這邊我們透過 -o
的方式指定 build 完的 binary 為 app
RUN go build -o app
build 完畢之後,最後就是要定義在啟動 docker image 時,要執行哪個檔案,這邊是執行 app
ENTRYPOINT ["./app"]
整個 Docker image 如下
FROM golang
RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o app
ENTRYPOINT ["./app"]
透過 docker build -t
的指令進行打包
docker build -t 'random_anonymous_chat' .
打包過程如下
Sending build context to Docker daemon 276kB
Step 1/7 : FROM golang
latest: Pulling from library/golang
57df1a1f1ad8: Pull complete
71e126169501: Pull complete
1af28a55c3f3: Pull complete
03f1c9932170: Pull complete
f4773b341423: Pull complete
fb320882041b: Pull complete
24b0ad6f9416: Pull complete
Digest: sha256:da7ff43658854148b401f24075c0aa390e3b52187ab67cab0043f2b15e754a68
Status: Downloaded newer image for golang:latest
---> 05c8f6d2538a
Step 2/7 : RUN mkdir -p /app
---> Running in b236f2a55671
Removing intermediate container b236f2a55671
---> a1d4433bc891
Step 3/7 : WORKDIR /app
---> Running in 23926232deef
Removing intermediate container 23926232deef
---> 4a29514e9e2e
Step 4/7 : COPY . .
---> 6982af8b156a
Step 5/7 : RUN go mod download
---> Running in 7952a98e18b9
Removing intermediate container 7952a98e18b9
---> bfe1a6d44dec
Step 6/7 : RUN go build -o app
---> Running in 1fbd1dc5d3a0
Removing intermediate container 1fbd1dc5d3a0
---> e18c00c53eff
Step 7/7 : ENTRYPOINT ["./app"]
---> Running in 5aa4c1da9112
Removing intermediate container 5aa4c1da9112
---> 6c61e6e96b4b
Successfully built 6c61e6e96b4b
Successfully tagged random_anonymous_chat:latest
完成後可以透過 docker images
來查看,會看到已經有一個 image 出現拉
REPOSITORY TAG IMAGE ID CREATED SIZE
random_anonymous_chat latest 3e102196a966 4 seconds ago 1.38GB
最後我們可以透過 docker run
的指令來運行封裝好的 image,可以用 -e
來指定環境變數、透過 -p
來指定 container 對外暴露的 port
docker run -e REDIS_URL=172.20.10.2 -p 5000:5000 random_anonymous_chat
可以看到執行的結果如下
2020/09/24 13:42:15 redis 回應成功, PONG
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] Loaded HTML Templates (2):
-
- index.html
[GIN-debug] GET /assets/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] HEAD /assets/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] GET / --> main.main.func1 (3 handlers)
[GIN-debug] GET /ws --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :5000
這時候開啟瀏覽器,輸入 http://127.0.0.1:5000
就可以看到網站順利運作拉!
今天的範例程式我放在這邊~有興趣的朋友可以 fork 回去玩玩看
https://github.com/codingXiang/random_anonymous_chat/tree/docker
透過 Docker 方式打包程式的好處在於,只要在任何有 Docker engine 的環境中都可以順利的被執行,而且可以確保每次的結果都是一樣的,才不會發生在不同環境執行會有環境的不相依的問題發生。
大家可以發現,build 完的 docker image 足足有 1.38G
這麼大,一定有辦法可以縮減它的大小的!
明天就讓我們繼續使用這個範例將整體的 image size 跟方法做最佳化吧!
請教一下
docker 用going寫網站服務
那麼let's憑證 要如何安裝在docker
讓用戶能在https://xxx.xxx.com 運作正常!!
是否能提點一下
在不動到程式的情況下應該可以透過 docker 架設一台 nginx
,把 let's encrypt 的憑證放在 nginx 內部並綁定網址 (xxx.xxx.com
之類的),接著再設定反向代理
將 traffic 轉向 golang 所啟動的 ip:port 即可。
步驟整理一下:
tips : golang 所啟動的 web server 不要把 port 開放對外
這是我的想法~希望可以幫到你
好的!!我試看看