Engineering · 2 min read
把 Golang 工具鏈嫁接到你需要的 base image
當 tag 矩陣裡真的沒有你要的 Golang × Alpine 組合,multi-stage build 可以把工具鏈嫁接過去,但先確認需求是真的。
當 Docker Hub 的 tag 矩陣裡真的沒有你要的 Golang × Alpine 組合(見組合 base image 前先查 tag 矩陣),multi-stage build(多階段建構:Dockerfile 裡寫多個 FROM,每個是獨立的建構步驟)可以把 Golang 工具鏈從一個 stage 複製到另一個 base。兩個 FROM 是兄弟,不是鏈條——最後一個才是出貨的 base:
FROM golang:1.25-alpine AS gotool # 只取工具鏈
FROM alpine:3.20 # 選你需要的系統函式庫版本
COPY --from=gotool /usr/local/go /usr/local/go
ENV PATH=/usr/local/go/bin:$PATH
ENV GOTOOLCHAIN=local # 不讓 go 自動下載另一套工具鏈
RUN apk add vips-dev # → Go 1.25 + 指定的 libvips,各自獨立
GOTOOLCHAIN=local阻止go為了滿足go.mod裡更高的版本指令而自動下載工具鏈。- build 和 runtime stage 要釘到同一個 base,否則 runtime 會靜默出貨錯誤的系統函式庫。
- 注意 graft 本身的耦合:浮動的
golang:<v>-alpine工具鏈是用較新的 musl(Alpine 的 C 標準函式庫,不同於 Ubuntu 的 glibc)編譯的,嫁接到較舊的 base 上可能造成函式庫版本不匹配。
什麼時候不該用這招: 兩道閘門,依序——(1) 查矩陣,你要的組合可能已經發布了;(2) 確認系統函式庫的需求是真的。一個建立在錯誤前提上的 graft 是純粹的複雜度。
graft 解決的是「矩陣裡沒有我的組合」這個問題。如果你的前提是「我需要舊版函式庫」,先確認那個需求是真的。
參考來源:
- https://docs.docker.com/guides/golang/build-images/
- https://github.com/alextanhongpin/go-docker-multi-stage-build
Related: 回到連鎖反應總覽,或參見組合 base image 前先查 tag 矩陣、釘版要附退場計畫、EOL base image 讓安全更新靜默失效。