Skip to content

golang 在 alpine image 的一個常見坑

davidlei

記錄前幾天在實驗 dapr 時踩到的一個坑,通常出現在 Go 程式以 alpine image 作為基底的時候。範例程式如下:

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 也寫得很簡略:

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_ENABLED=0,或使用 go build -tags netgo -o echo-server main.go,讓 net 不依賴 CGO,改用純 Go 實現。

上方參考資料中提到,net 主要呼叫 glibc,但 alpine 預設使用 musl libc,兩者不相容導致程式無法執行。解法是在 Dockerfile 中補上 glibc 的安裝:

FROM alpine:latest
ADD echo-server .
RUN apk add --no-cache libc6-compat 

ENTRYPOINT [ "/echo-server" ]

加上這行後重新 build 並執行,就可以看到正常的結果了:

Edit this post
Previous
讓 AI 不再自己改自己看 — 我為什麼做了 xreview
Next
Bypassing the Load Balancer Without Regrets - SoCC ’20