---
title: Docker 编译 musl 可执行文件
published: 2025-03-27
image: /posts/docker-musl-build/img/cover.svg
description: 解决 Go 语言跨平台编译问题，使用 Docker + musl 编译静态链接的可执行文件。
tags: ['Docker', 'Go', '运维']
draft: false
---

## 解决问题

用 Go 进行跨平台编译时往往步骤复杂，用 Docker 可以轻松解决。但 Docker 的当前最小镜像 Alpine 其动态链接库采用 musl，与主流的 glibc 不同，往往不能方便执行。

## 查阅资料

```bash
# 启动容器并挂载当前目录到 `/app`
docker run -it --rm -v "$PWD":/app -w /app ubuntu:20.04 bash

# 在容器内执行以下操作：
apt-get update && apt-get install -y musl-tools golang-go  # 安装 musl 和 Go
export CC=musl-gcc  # 指定 musl 编译器
export CGO_ENABLED=1  # 启用 CGO
# 设置代理
go env -w GOPROXY=https://goproxy.cn,direct
go build -ldflags '-linkmode external -extldflags "-static"' -o myapp  # 静态编译
exit  # 退出容器

# 验证结果（在宿主机执行）
file myapp  # 应显示 "statically linked"
ldd myapp   # 应显示 "not a dynamic executable"

wget https://go.dev/dl/go1.21.linux-amd64.tar.gz

ENV PATH="/data/go/bin:$PATH"
```

### 遇到问题

1. `go mod tidy` 时报错 `tls: failed to verify certificate: x509: certificate`

   解决：`RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*`

   基础镜像的证书未更新，导致 https 的请求出现类似错误。

   ![image-20250327100813512](/posts/docker-musl-build/img/image-20250327100813512.png)

2. 直接安装 `golang-go` 其版本过低

   解决: `PATH="/data/go/bin:$PATH"` 手动指定环境

## 最终方案

1. 下载 `go1.24.1.linux-amd64.tar.gz` 解压
2. 编写 Dockerfile
3. 执行 `docker build -t go-build:v1 .`

Dockerfile 内容：

```dockerfile
FROM ubuntu:20.04

LABEL org.opencontainers.image.authors="sutong"

COPY go/ /go/

ENV PATH="/go/bin:$PATH"

RUN apt-get update && apt-get install -y musl-tools

## go mod tidy 时报错 tls: failed to verify certificate: x509: certificate
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
# 指定 musl 编译器
ENV CC=musl-gcc
# 启用 CGO
ENV CGO_ENABLED=1

VOLUME /data

WORKDIR /data

CMD ["/bin/bash"]
```

### 构建

```bash
# docker build -t go-build:v1 .

# 直接执行编译 编译后删除
docker run --rm -v .:/data go-build:v1 go build -o app_musl

# 构建编译环境
docker run -it -d -v .:/data --name <容器名称> go-build:v1 /bin/bash
# eg：docker run -it -d -v .:/data --name logbus-build go-build:v1 /bin/bash

docker exec -it <容器名称> go build -o <myapp>
# eg：docker exec -it logbus-build go build -o logbus_musl_1
```

### 额外注意

若没科学上网需要设置代理：

```bash
docker exec -it <容器名称> /bin/bash
go env -w GOPROXY=https://goproxy.cn,direct
go build -ldflags '-linkmode external -extldflags "-static"' -o myapp  # 静态编译
go build # musl 库编译
```

### 运行环境示例

```dockerfile
FROM alpine:latest

LABEL org.opencontainers.image.authors="sutong"

COPY glog_musl /app/

COPY conf.ini /data/
COPY ipname.ini /data/

WORKDIR /app

RUN chmod 777 /app/glog_musl

CMD ["./glog_musl", "logsweb", "/data/conf.ini"]

# docker build -t glog:v1 .
# docker run -p 6801:6801 glog:v1
```
