在使用Alpine镜像作为运行阶段镜像,构建包含C语言编写的SQLite项目容器镜像时遇到了CGO和链接库的问题

CGO是什么

顾名思义,它是Go与C语言交互的桥梁,可以在Go里面调用C的东西,比如动态链接文件so/dll/dylib这类。它有优点又有缺点,优点是我们可以在Go项目中直接使用一些现成的C语言构建的库;缺点是会依赖一些外部东西,比如对应平台上的链接库。

如果代码中使用了CGO相关的依赖库(例如纯C实现的SQLite),我们需要在编译参数中配置CGO_ENABLE=1以启用CGO,反之为0,否则可执行文件在运行时会报错提示类似CGO未启用的内容。

目前社区也有纯Go实现的SQLite,但是原版的SQLite的测试覆盖率和。

Alpine Linux

它是一个非常轻量级的Linux发行版,有多轻呢,Alpine基础镜像压缩后本体仅不到4MiB大小,非常适合用于构建容器镜像,按需加载依赖(前提是依赖不是太多的情况,依赖过于复杂请自行权衡使用其他发行版)。

Alpine默认不包含GNU C Library(glibc),取而代之的是musl libc,这也是本文的问题所在。

容器化及解决方案

这是我给一个项目写的一份Dockerfile,构建阶段使用的是官方的golang镜像,运行阶段使用的官方的alpine镜像

如文本所示,我们在构建阶段启用了CGO,问题来了,CGO依赖的glibc在Alpine是没有的,正常情况下会提示找不到可执行文件,这是因为缺少必要的glibc,我们只需要在运行阶段的容器中使用apk包管理器安装一下即可

# build
FROM reg.liteyuki.icu/dockerhub/golang:1.24.2-alpine3.21 AS builder

ENV TZ=Asia/Chongqing

WORKDIR /app

RUN apk --no-cache add build-base git tzdata

COPY go.mod go.sum ./

RUN go mod download

COPY . .

RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o main main.go

# production
FROM reg.liteyuki.icu/dockerhub/alpine:latest AS prod

ENV TZ=Asia/Chongqing

WORKDIR /app

RUN apk --no-cache add tzdata ca-certificates libc6-compat libgcc libstdc++  # 在这里安装一些必须的库

COPY --from=builder /app/main /app/main

EXPOSE 8888

RUN chmod +x ./main

ENTRYPOINT ["./main"]

你也可以使用其他热门发行版作为基础镜像,例如ubuntu,debian。但是它们可能会包含一大堆我们不需要的依赖,陡然增大了整个项目的镜像,在分发上和安全性上也有一定的影响。我个人在构建容器镜像的时候习惯遵循最小依赖原则,仅安装必要的依赖。

如果你不使用容器化,不想使用镜像分发。当然可以编译启用了CGO的跨平台二进制文件,但是你需要在构建机器上安装对应平台的库。