goose Docker 构建指南:从多阶段镜像构建、非根用户运行到 CI/CD 发布的完整实践
本篇基于仓库根目录的 BUILDING_DOCKER.md 展开,讲解 goose 开源 AI Agent 的 Docker 镜像获取、源码构建、容器运行、配置持久化与 CI/CD 发布全流程。读完本文,你将能够拉取并运行官方预构建镜像、理解 Dockerfile 的多阶段构建与优化参数、掌握环境变量与配置的优先级机制,并将 goose 集成进自己的 GitHub Actions / GitLab CI 流水线。
快速开始:使用预构建镜像
最简单的方式是从 GitHub Container Registry 拉取官方预构建镜像。镜像标签由自动化发布流水线生成(详见后文 官方发布流水线),latest 指向 main 分支最新构建:
# 拉取最新镜像
docker pull ghcr.io/aaif-goose/goose:latest
# 运行 goose CLI
docker run --rm ghcr.io/aaif-goose/goose:latest --version
# 带 LLM 配置运行
docker run --rm \
-e GOOSE_PROVIDER=openai \
-e GOOSE_MODEL=gpt-4o \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
ghcr.io/aaif-goose/goose:latest run -t "Hello, world!"
这里的 run -t "..." 即 goose 的非交互式一次性执行命令:run 子命令以 -t/--text 传入指令,执行完毕后进程退出,非常适合脚本与流水线场景。从源码结构看,goose CLI 的完整子命令集(session、run、configure、serve、gateway 等)定义在 crates/goose-cli/src/cli.rs,容器内这些命令全部可用。
从源码构建镜像
前置条件
- Docker 20.10 及以上版本
- Docker Buildx(多平台构建需要)
- Git
构建步骤
- 克隆仓库并进入目录:
git clone https://github.com/aaif-goose/goose.git
cd goose
- 构建镜像(构建上下文为仓库根目录):
docker build -t goose:local .
文档对构建过程的三点说明,在 Dockerfile 中都能逐条对应:
- 多阶段构建:
builder阶段基于rust:1.82-bookworm,最终镜像基于debian:bookworm-slim(且通过 digestsha256:b1a74...固定版本,保证可复现),见 Dockerfile 第 6 行 与 第 37 行; - 带优化的编译:构建阶段通过环境变量设置了 release profile 优化参数,最终产物经过 LTO、strip 与体积优化,见下文"构建阶段的编译优化"一节;
- 最终镜像约 340MB,包含
gooseCLI 二进制。
构建选项
开发构建(保留调试符号):
docker build --build-arg CARGO_PROFILE_RELEASE_STRIP=false -t goose:dev .
多平台构建:
docker buildx build --platform linux/amd64,linux/arm64 -t goose:multi .
官方流水线实际发布的就是 linux/amd64,linux/arm64 双平台镜像,可参考 .github/workflows/publish-docker.yml 第 65 行。
Dockerfile 源码解析:镜像是如何组装的
结合 Dockerfile 逐段分析,可以看清官方镜像的完整装配逻辑。
构建阶段:Rust 工具链与编译依赖
FROM rust:1.82-bookworm AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential cmake pkg-config \
libssl-dev libdbus-1-dev libclang-dev \
protobuf-compiler libprotobuf-dev \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
COPY . .
这些构建依赖对应 goose 各 crate 的原生依赖:libssl-dev 用于 TLS,libdbus-1-dev 用于 DBus 相关库,libclang-dev 用于 clang-sys 类绑定,protobuf-compiler 与 libprotobuf-dev 用于 gRPC/proto 代码生成(goose 的 ACP/网关等模块依赖 protobuf)。
构建阶段的编译优化
ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
ENV CARGO_PROFILE_RELEASE_LTO=true
ENV CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
ENV CARGO_PROFILE_RELEASE_OPT_LEVEL=z
ENV CARGO_PROFILE_RELEASE_STRIP=true
RUN cargo build --release --package goose-cli
见 Dockerfile 第 29-34 行。各参数含义:
| 参数 | 取值 | 作用 |
|---|---|---|
CARGO_REGISTRIES_CRATES_IO_PROTOCOL |
sparse |
使用 sparse index 拉取 crate,加速依赖下载 |
CARGO_PROFILE_RELEASE_LTO |
true |
开启链接时优化(Link-Time Optimization),跨 crate 内联优化 |
CARGO_PROFILE_RELEASE_CODEGEN_UNITS |
1 |
单 codegen unit,最大化优化空间(构建更慢) |
CARGO_PROFILE_RELEASE_OPT_LEVEL |
z |
以最小二进制体积为目标的优化等级 |
CARGO_PROFILE_RELEASE_STRIP |
true |
剥离二进制中的调试符号 |
注意 --package goose-cli:最终产物是 CLI 二进制(对应 crates/goose-cli 包),而非工作区中的库 crate。OPT_LEVEL=z 与 LTO=true 组合正是"约 32MB 单二进制"的来源;开发构建时通过 --build-arg CARGO_PROFILE_RELEASE_STRIP=false 关掉 strip 即可保留符号用于调试。
运行时阶段:最小化 Debian 基础镜像
FROM debian:bookworm-slim@sha256:b1a741487078b369e78119849663d7f1a5341ef2768798f7b7406c4240f86aef
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates libssl3 libdbus-1-3 libgomp1 libxcb1 \
curl git \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/target/release/goose /usr/local/bin/goose
见 Dockerfile 第 39-53 行。运行时只安装二进制动态链接所需的库(libssl3、libdbus-1-3、libgomp1、libxcb1)加上 goose 日常操作需要的工具:
git— 版本控制操作;curl— HTTP 请求;ca-certificates— SSL/TLS 证书;- 基础 shell 工具。
这与文档"Image Details / Included Tools"一节的清单完全一致。基础镜像通过 digest 固定,配合 apt-get clean 与清理 apt 缓存,把最终镜像压缩到约 340MB。
非根用户、环境与入口点
RUN useradd -m -u 1000 -s /bin/bash goose && \
mkdir -p /home/goose/.config/goose && \
chown -R goose:goose /home/goose
ENV PATH="/usr/local/bin:${PATH}"
ENV HOME="/home/goose"
USER goose
WORKDIR /home/goose
ENTRYPOINT ["/usr/local/bin/goose"]
CMD ["--help"]
见 Dockerfile 第 56-70 行。三个关键设计:
- 非根运行:以 UID 1000 的
goose用户运行,缩小容器逃逸后的攻击面,这是文档"Security"一节所述"Runs as non-root usergoose(UID 1000)"的实现依据; - 预建配置目录:
/home/goose/.config/goose即 goose 的配置目录(HOME=/home/goose),后文持久化挂载就是挂到这里; - 入口点设计:
ENTRYPOINT固定为 goose 二进制,CMD默认为--help。因此docker run ghcr.io/aaif-goose/goose:latest run -t "..."中的run -t "..."全部作为参数追加到 goose 后面;而--entrypoint bash可整体替换入口用于调试(见后文"高级用法")。
镜像还附带标准 OCI 元数据标签(org.opencontainers.image.title 等),见 Dockerfile 第 73-76 行。
在 Docker 中运行 goose
CLI 模式
基本用法:
# 查看帮助
docker run --rm goose:local --help
# 执行一次命令
docker run --rm \
-e GOOSE_PROVIDER=openai \
-e GOOSE_MODEL=gpt-4o \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
goose:local run -t "Explain Docker containers"
挂载卷以获得宿主机文件访问:
docker run --rm \
-v $(pwd):/workspace \
-w /workspace \
-e GOOSE_PROVIDER=openai \
-e GOOSE_MODEL=gpt-4o \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
goose:local run -t "Analyze the code in this directory"
注意 -w /workspace 改变了容器工作目录(Dockerfile 默认是 /home/goose),让 agent 的相对路径操作落在挂载的目录上。
使用 Databricks 的交互式会话模式(-it 分配 TTY 并透传标准输入,session 子命令启动交互式 REPL):
docker run -it --rm \
-e GOOSE_PROVIDER=databricks \
-e GOOSE_MODEL=databricks-dbrx-instruct \
-e DATABRICKS_HOST="$DATABRICKS_HOST" \
-e DATABRICKS_TOKEN="$DATABRICKS_TOKEN" \
goose:local session
Docker Compose
创建 docker-compose.yml:
version: '3.8'
services:
goose:
image: ghcr.io/aaif-goose/goose:latest
environment:
- GOOSE_PROVIDER=${GOOSE_PROVIDER:-openai}
- GOOSE_MODEL=${GOOSE_MODEL:-gpt-4o}
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./workspace:/workspace
- goose-config:/home/goose/.config/goose
working_dir: /workspace
stdin_open: true
tty: true
volumes:
goose-config:
运行:
docker-compose run --rm goose session
要点:goose-config 命名卷挂载到 /home/goose/.config/goose,实现配置跨容器持久化;stdin_open: true + tty: true 保证交互式 session 可用。
仓库内还提供了一个面向"在容器里跑 goose + keyring"的旧版示例组合:documentation/docs/docker/docker-compose.yml 与其配套的 documentation/docs/docker/Dockerfile(基于 ubuntu:22.04,额外安装了 node、uv、ripgrep 等开发工具并初始化 DBus/keyring)。它与根 Dockerfile 的定位不同——后者是精简的官方运行时镜像,前者是教程 documentation/docs/tutorials/goose-in-docker.md 中的开发工作流示例,需要按自己的 API key、provider、model 修改其中的环境变量(例如示例中的 GOOSE_PROVIDER=google、GOOSE_MODEL=gemini-2.0-flash-exp)。
配置
环境变量与优先级
官方镜像接受所有 goose 标准环境变量:
GOOSE_PROVIDER:LLM 提供商(openai、anthropic、google 等);GOOSE_MODEL:要使用的模型(gpt-4o、claude-sonnet-4 等);- 各提供商对应的 API key(
OPENAI_API_KEY、ANTHROPIC_API_KEY等)。
这些环境变量在源码中的处理逻辑位于 crates/goose/src/config/providers.rs。get_active_provider 的取值顺序是:
- 环境变量
GOOSE_PROVIDER; - 配置文件中记录的当前激活 provider;
- 配置文件中的
GOOSE_PROVIDER参数。
get_active_model 类似:先看 GOOSE_MODEL 环境变量,再看该 provider 配置条目里保存的模型,最后回退到配置文件参数。因此容器内注入的环境变量总是优先于挂载进容器的配置文件——这正是"用 -e 传参即可覆盖持久化配置"的原因。
此外,run/session 等命令还支持 --provider 之类的命令行参数单次覆盖环境变量。从 crates/goose-cli/src/cli.rs 第 334 行 的帮助文案可确认其语义:"Override the GOOSE_PROVIDER environment variable for this run. Available providers include openai, anthropic, ollama, databricks, gemini-cli, claude-code, and others.",即优先级为:命令行参数 > 环境变量 > 配置文件。
持久化配置
把宿主机配置目录挂载进容器,即可持久化 provider 配置:
docker run --rm \
-v ~/.config/goose:/home/goose/.config/goose \
goose:local configure
configure 子命令会交互式引导设置 provider 与模型,写入 /home/goose/.config/goose 下的配置文件。由于镜像以 UID 1000 的 goose 用户运行,宿主机挂载目录的属主若与 1000 不匹配可能出现权限问题,处理办法见下文"权限问题"一节。
安装额外工具
镜像默认以非根用户运行。要安装额外软件包,有两个办法:
# 方式一:临时以 root 运行安装
docker run --rm \
-u root \
--entrypoint bash \
goose:local \
-c "apt-get update && apt-get install -y vim && goose --version"
# 方式二:基于官方镜像派生自定义镜像
FROM ghcr.io/aaif-goose/goose:latest
USER root
RUN apt-get update && apt-get install -y \
vim \
tmux \
&& rm -rf /var/lib/apt/lists/*
USER goose
-u root 覆盖 USER 指令,--entrypoint bash 替换默认入口点;派生镜像则应在装完工具后切回 USER goose,保持非根运行的安全属性。
CI/CD 集成
作为 GitHub Actions 作业容器
jobs:
analyze:
runs-on: ubuntu-latest
container:
image: ghcr.io/aaif-goose/goose:latest
env:
GOOSE_PROVIDER: openai
GOOSE_MODEL: gpt-4o
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Run goose analysis
run: |
goose run -t "Review this codebase for security issues"
注意 actions/checkout 会把代码检入 /__w 目录,而容器 WORKDIR 是 /home/goose,实际使用时可能需要用 -C 或 working-directory 指定检出目录(具体取决于 runner 挂载路径)。
GitLab CI
analyze:
image: ghcr.io/aaif-goose/goose:latest
variables:
GOOSE_PROVIDER: openai
GOOSE_MODEL: gpt-4o
script:
- goose run -t "Generate documentation for this project"
GitLab 默认工作目录为 /build,同样建议为 goose 命令显式指定代码目录。
官方发布流水线如何产出这些标签
上述镜像并非手动构建。.github/workflows/publish-docker.yml 定义了完整发布流程,其触发与标签策略值得生产使用方了解:
- 触发条件(第 3-13 行):
main分支推送、v*.*.*或v*.*.*-*(预发布)标签推送、手动 dispatch;documentation/**与*.md变更不会触发重新构建; - 标签策略(第 43-53 行):
main分支 →latest、main与sha-<短哈希>三个标签;- 语义化版本标签
v1.2.3→1.2.3、1.2、1,预发布标签额外保留完整版本名;
- 多平台:
platforms: linux/amd64,linux/arm64,使用 GHA 缓存(cache-from/cache-to: type=gha)加速; - 供应链安全:工作流声明了
id-token: write与attestations: write权限,并在推送后执行actions/attest-build-provenance,为每个镜像附 SLSA 构建溯源证明,便于使用方验证镜像确实由官方流水线构建。
文档"Regular security updates via automated builds"即对应这一机制:main 分支的每次相关提交都会产出带最新安全补丁基础镜像的新标签。
镜像详情与生产建议
体积与组成
- 基础镜像:Debian Bookworm Slim(digest 固定);
- 最终体积:约 340MB;
- 优化手段:LTO、二进制 strip、
opt-level=z体积优化; - 内置二进制:
/usr/local/bin/goose(约 32MB)。
安全特性
- 非根用户
goose(UID 1000)运行; - 仅保留必需运行时依赖,最小化攻击面;
- 通过上述自动化流水线持续更新基础镜像安全补丁。
生产部署清单
文档给出的生产部署建议:
- 使用具体版本标签(如
ghcr.io/aaif-goose/goose:1.6.0)而非latest,保证可回滚、可复现; - 使用 secrets 管理方案注入 API key,避免明文写在流水线变量或 Compose 文件中;
- 接入日志与监控;
- 配置资源限制与自动扩缩容。
生产派生 Dockerfile 示例:
FROM ghcr.io/aaif-goose/goose:v1.6.0
# 按需添加工具
USER root
RUN apt-get update && apt-get install -y your-tools && rm -rf /var/lib/apt/lists/*
USER goose
故障排查
权限问题
挂载卷遇到权限错误时,用 -u $(id -u):$(id -g) 让容器以宿主机当前用户 ID 运行,使写入的属主与宿主机一致:
docker run --rm \
-v $(pwd):/workspace \
-u $(id -u):$(id -g) \
goose:local run -t "List files"
这与 Dockerfile 默认 UID 1000 的设计相关:不指定 -u 时容器内文件属主是 1000,宿主机若没有对应用户就会看到"归属不明"的文件。
API Key 问题
- 确认环境变量已正确注入(可用
docker run --rm --entrypoint bash goose:local -c 'env | sort'之类手段核对); - 检查 shell 引号处理是否正确;
- 多变量场景使用
docker run --env-file .env。
网络问题
需要访问宿主机本地服务时,用 host 网络模式打通:
docker run --rm --network host goose:local
高级用法
自定义 Entrypoint 调试
docker run --rm -it --entrypoint bash goose:local
替换入口点后即可获得 shell,可用于检查 /usr/local/bin/goose、/home/goose/.config/goose 等路径状态。
资源限制
docker run --rm \
--memory="2g" \
--cpus="2" \
goose:local
多阶段开发(源码热重载)
将源码挂载进 Rust 官方镜像配合 cargo watch 开发:
docker run --rm \
-v $(pwd):/usr/src/goose \
-w /usr/src/goose \
rust:1.82-bookworm \
cargo watch -x run
基础镜像 rust:1.82 与 Dockerfile 构建阶段使用的版本一致,可保证开发环境与构建环境工具链对齐(仓库另由 rust-toolchain.toml 声明本地开发工具链版本)。
为 Docker 相关改动做贡献
若你计划修改 Docker 相关文件,文档建议的验证清单:
- 在 amd64 与 arm64 多平台测试构建;
- 确认镜像体积保持在合理范围;
- 同步更新 BUILDING_DOCKER.md 文档;
- 评估对 .github/workflows/publish-docker.yml 发布流程的影响;
- 用多种 LLM provider 验证镜像可用性。
相关文档
- documentation/docs/tutorials/goose-in-docker.md — 容器内运行 goose 及"宿主机跑 goose、扩展跑在容器里"(
--container标志)的完整教程; - documentation/docs/docker/docker-compose.yml — 教程配套的 Compose 示例(含 keyring/DBus 初始化与 SSH key 挂载);
- Dockerfile — 官方镜像的多阶段构建定义;
- .github/workflows/publish-docker.yml — 镜像发布、标签策略与 SLSA 溯源证明。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00