起因是这样的,我有一个 Go 写的 utils 库,但这个仓库目前是 private 的,于是在其他引用了这个库的项目构建时就需要解决鉴权访问的问题。

先来看看不使用私有库的 Go 项目构建的 Dockerfile:

# build api
FROM golang:1.23 AS builder

WORKDIR /src/app

COPY . ./

RUN --mount=type=cache,target=/go/pkg/mod \
    make

# build the final image
FROM ubuntu:24.04

COPY --from=builder /src/app/bin/api /app/bin/api

而使用了私有库的话,就需要设置 GOPRIVATE 环境变量和鉴权。

在本地开发的时候我是用的 ssh 来访问 git 仓库的,所以在 ~/.gitconfig 追加如下配置即可:

[url "[email protected]:"]
    insteadOf = https://github.com/

但是使用 Docker 构建的时候继续使用 ssh 就会略麻烦(使用 Deploy key)和不安全(使用个人账号 SSH key),那么细粒度的 Access Token 会是更好的选择。

因为 Access Token 是敏感数据,我们是不能直接写在 Dockerfile 里的,所以你可能会想到那就通过构建参数传进去:

+ARG GITHUB_TOKEN
+ENV TOKEN=$GITHUB_TOKEN

 WORKDIR /src/app

 COPY . ./

+RUN go env -w GOPRIVATE="github.com/YianAndCode/MY_PRIVATE_GO_PKG"

+RUN git config --global url."https://${TOKEN}:[email protected]/".insteadOf "https://github.com/"

 RUN --mount=type=cache,target=/go/pkg/mod \
    make

然后构建:

export GITHUB_TOKEN=xxx
docker build --build-arg GITHUB_TOKEN=${GITHUB_TOKEN} -t myapp:latest .

这时你会得到一个警告:

WARN: SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data

因为这样做别人在拿到镜像之后是可以看到你构建过程中的参数的,也不安全。其实在 Docker 官方文档有介绍 Build secrets,我们可以利用这个功能来实现更安全优雅地传入敏感数据。

首先来学习一下 build secrets 的基本知识:

1. 怎么传入 secrets

在执行 docker build 的时候通过 --secret 传入,格式:

# 通过文件传入
--secret id={ID},src=/path/to/secret_file

# 通过环境变量传入
--secret id={ID},env=SECRET_ENV_NAME

# 如果 id 跟环境变量名同名,可以简写
--secret id=SECRET_ENV_NAME

完整示例:

# 将环境变量 KUBECONFIG 为 secret 传入,secret id 是 kube
docker build --secret id=kube,env=KUBECONFIG .

2. Dockerfile 里怎么获取 secrets

前面的 id 是 Dockerfile 中用来获取 secret 的,在 Dockerfile 中需要先挂在:RUN --mount=type=secret,id={ID},默认会被作为文件放在 /run/secrets/{ID} 中:

RUN --mount=type=secret,id={ID} \
    cat /run/secrets/{ID}

上面这个 Dockerfile 通过增加 --progress=plain 可以看到 cat 的输出:

HELLO="Hello world!" docker build --progress=plain --secret id=HELLO .

记得要把这个测试构建的无用镜像删掉

也可以通过 target 选项来指定文件位置或者通过 env 选项来使传入的 secret 作为环境变量:

# 指定文件
RUN --mount=type=secret,id={ID},target=/path/to/secret_file \
    cat /path/to/secret_file

# 作为环境变量
RUN --mount=type=secret,id={ID},env=SECRET_ENV_NAME \
    echo ${SECRET_ENV_NAME}

学习完 Build secrets 的知识之后,咱们把最开始构建 Go 项目的 Dockerfile 改下:

# build api
FROM golang:1.23 AS builder

WORKDIR /src/app

COPY . ./

RUN go env -w GOPRIVATE="github.com/YianAndCode/MY_PRIVATE_GO_PKG"

RUN --mount=type=secret,id=GITHUB_TOKEN,env=GITHUB_TOKEN \
    git config --global url."https://${GITHUB_TOKEN}:[email protected]/".insteadOf "https://github.com/"

RUN --mount=type=cache,target=/go/pkg/mod \
    make

# build the final image
FROM ubuntu:24.04

COPY --from=builder /src/app/bin/api /app/bin/api

标签: docker, go

添加新评论