golang 在 alpine image 的一個常見坑

紀錄一下前幾天在實驗 dapr 遇到的一個小問題,通常發生在簡單的 golang 程式使用 apline image 當作基底的時候,程式如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
    "log"
    "net/http"
)


func main() {
    log.Println("Start echo server")
    http.HandleFunc("/echo", echoHandler)
    if err := http.ListenAndServe(":3000", nil); err != nil {
        log.Fatal(err)
    }
}

func echoHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    log.Println("receive message: ", r.Form.Get("msg"))
    w.Write([]byte(r.Form.Get("msg") + "www\n"))
}

因為只是要快速實驗,所以 Dockerfile 也寫得很簡略

1
2
3
4
FROM alpine:latest
ADD echo-server .

ENTRYPOINT [ "/echo-server" ]

經過 go build -o echo-server main.go 之後建立 image docker build -t echo-server .

在實際跑 image 時候報錯 exec ./echo-server: no such file or directory

簡單爬了一下文章,發現這其實是個常見的坑,問題出在作為基底的 alpine image 上面與 net 函式庫之間

簡單來說就是 net 在某些情況下會預設使用 CGO,詳情請查看上方的補充資料,所以解決方案有兩種思路

  • 編譯的時候關閉 CGO 的支援

在編譯前設定環境變數 CGO_ENABLED=0 或者 go build -tags netgo -o echo-server main.go,這兩種方式都可以讓 net 不依靠 CGO,靠純 golang 實現執行

  • 想辦法讓補全 CGO 需要的 library

上面的參考資料有說到,net 主要會呼叫 gnu-libc,但是在 alpine 裡面默認使用 musl-libc 導致程式無法執行,所以要在 Dockerfile 裡面補上 gnu-libc 的安裝

1
2
3
4
5
FROM alpine:latest
ADD echo-server .
RUN apk add --no-cache libc6-compat 

ENTRYPOINT [ "/echo-server" ]

最後再執行一次,就可以看到正常的執行結果了。

使用 Hugo 建立
主題 StackJimmy 設計