第一章:Golang安装方式概览与核心影响机制
Go 语言的安装方式直接影响开发环境的可复现性、版本隔离能力及构建行为的一致性。主流安装路径包括系统包管理器安装、官方二进制分发包安装、版本管理工具(如 gvm 或 goenv)安装,以及通过 Go 自身的 go install 安装特定工具链。不同方式对 GOROOT、GOPATH(Go 1.11+ 后逐渐弱化)、GOBIN 及模块缓存路径 GOCACHE 的初始化逻辑存在显著差异。
官方二进制包安装(推荐用于生产环境)
从 https://go.dev/dl/ 下载对应平台的 .tar.gz 包,解压并配置环境变量:
# 下载并解压(以 Linux amd64 为例)
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
# 在 ~/.bashrc 或 ~/.zshrc 中添加
export GOROOT=/usr/local/go
export PATH=$GOROOT/bin:$PATH
export GOPROXY=https://proxy.golang.org,direct # 启用模块代理
该方式显式控制 GOROOT,避免与系统包管理器冲突,且 go version 输出路径可明确溯源。
版本管理工具安装(适用于多版本协作场景)
使用 goenv 管理多版本时,需确保 shell 初始化正确:
git clone https://github.com/syndbg/goenv.git ~/.goenv
export GOENV_ROOT="$HOME/.goenv"
export PATH="$GOENV_ROOT/bin:$PATH"
eval "$(goenv init -)"
goenv install 1.21.10 # 安装指定版本
goenv global 1.22.5 # 设为全局默认
此时 GOROOT 由 goenv 动态注入,which go 指向 shim 脚本,实际执行路径由 goenv 运行时解析。
核心环境变量影响机制
| 变量名 | 作用说明 |
|---|---|
GOROOT |
Go 标准库与编译器所在根目录;错误设置将导致 go build 找不到 runtime 包 |
GOPROXY |
控制模块下载源;设为 off 将完全禁用代理,触发直接 git 克隆(需网络可达) |
GOCACHE |
存储编译中间产物;清空后首次构建变慢,但不影响语义正确性 |
无论采用何种安装方式,go env -w 均可用于持久化用户级配置,例如 go env -w GOPRIVATE="git.example.com/internal" 以跳过私有模块的代理校验。
第二章:通过官方二进制包安装(.tar.gz)
2.1 理论解析:PATH、GOROOT与环境变量初始化链路
Go 工具链启动时,环境变量的加载并非简单赋值,而是一条严格依赖顺序的初始化链路。
初始化优先级层级
- 用户显式
export GOROOT=/usr/local/go优先于系统默认探测 PATH中首个含go可执行文件的目录被反向推导为隐式GOROOT(仅当未显式设置时)GOPATH不影响go命令自身定位,但决定模块缓存与构建输出路径
关键初始化流程
# 典型 shell 初始化片段(~/.bashrc 或 /etc/profile.d/go.sh)
export GOROOT="/opt/go" # 显式声明,强约束
export PATH="$GOROOT/bin:$PATH" # 将 go 二进制置入搜索首位
export GOPATH="$HOME/go" # 独立于 GOROOT,影响工作区
逻辑分析:
PATH修改必须在GOROOT之后;$GOROOT/bin必须前置,否则go version可能调用旧版本。$PATH中重复路径会导致不可预测行为,应避免$GOROOT/bin出现两次。
环境变量依赖关系(mermaid)
graph TD
A[shell 启动] --> B[读取 /etc/profile → ~/.bashrc]
B --> C[执行 export GOROOT]
C --> D[拼接 PATH=$GOROOT/bin:$PATH]
D --> E[go 命令解析自身路径]
E --> F[验证 $GOROOT/src/runtime 包存在]
| 变量 | 是否必需 | 作用范围 | 错误示例 |
|---|---|---|---|
GOROOT |
否* | go 命令运行时 |
/usr/local/go/ 缺失 src/ |
PATH |
是 | Shell 命令发现 | /opt/go/bin 未在 PATH 中 |
GOPATH |
否 | go build 输出 |
指向只读文件系统 |
2.2 实践验证:手动解压后配置GOSUMDB与GOPRIVATE的典型失效场景
当从源码包手动解压 Go 环境(如 go1.22.3.src.tar.gz)并构建二进制后,环境变量配置易被忽略或覆盖:
环境变量加载顺序陷阱
# 在 ~/.bashrc 中设置(看似正确)
export GOPRIVATE="git.internal.company.com"
export GOSUMDB="off" # 或自建 sum.golang.org 替代
⚠️ 问题:go build 启动时优先读取 GOROOT/src/cmd/go/internal/cfg/cfg.go 中硬编码的 GOSUMDB 默认值(sum.golang.org),手动解压构建的 go 命令未重编译该模块,仍强制校验。
典型失效组合表
| 场景 | GOPRIVATE 设置 | GOSUMDB 设置 | 是否跳过校验 | 原因 |
|---|---|---|---|---|
| A | ✅ git.internal.company.com |
❌ 未设(默认启用) | 否 | GOPRIVATE 仅影响模块路径匹配,不关闭校验逻辑 |
| B | ✅ | off |
是 | 显式禁用校验,但需确保 go 二进制支持该值(非所有源码构建版本兼容) |
数据同步机制
graph TD
A[go build] --> B{读取 GOSUMDB}
B -->|env 未生效| C[使用内置默认 sum.golang.org]
B -->|env 生效| D[向指定服务器发起 HTTPS 请求]
D -->|403/timeout| E[构建失败]
2.3 理论+实践:GONOSUMDB白名单动态加载机制与.tar.gz安装的耦合关系
GONOSUMDB 白名单并非静态配置,而是随模块安装路径实时解析 .tar.gz 包内 go.sum 的哈希签名后动态注入。
动态加载触发时机
当 go install 解析 .tar.gz 时:
- 提取包根目录下的
go.mod和go.sum - 校验
go.sum中每条记录是否匹配 GONOSUMDB 白名单规则(如github.com/org/pkg => v1.2.3 h1:...) - 若匹配失败且未在白名单中,则拒绝加载
耦合关键点
# go env -w GONOSUMDB="*.internal.company.com,github.com/internal/*"
# 此配置仅对后续 .tar.gz 解压路径生效,不作用于本地 GOPATH
参数说明:
GONOSUMDB值为逗号分隔的 glob 模式;匹配依据是.tar.gz解压后go.mod中module声明的路径前缀,而非归档文件名。
白名单生效流程
graph TD
A[go install pkg@v1.2.3.tar.gz] --> B[解压归档]
B --> C[读取 go.mod.module]
C --> D[匹配 GONOSUMDB glob 规则]
D -->|匹配成功| E[跳过 go.sum 校验]
D -->|匹配失败| F[报错:checksum mismatch]
| 场景 | .tar.gz 内 module 声明 | GONOSUMDB 配置 | 是否跳过校验 |
|---|---|---|---|
| 内部私有库 | company.com/internal/tool |
company.com/internal/* |
✅ |
| 公共仓库 | github.com/golang/fmt |
github.com/golang/* |
❌(默认不启用) |
2.4 实践复现:在离线环境中因GOROOT未同步更新导致go proxy fallback失败
现象复现步骤
- 在 air-gapped 环境中部署 Go 1.21.0,但
GOROOT仍指向旧版(如/usr/local/go实际为 1.19.0) - 配置
GOPROXY=https://goproxy.cn,direct,禁用网络代理后触发 fallback go build报错:failed to fetch module: ... invalid version: git ls-remote failed
GOROOT 与模块解析的隐式耦合
Go 工具链自 1.18 起在 fallback 时依赖 GOROOT/src/cmd/go/internal/modfetch 中的 vcs.go 进行 Git 协议协商;若 GOROOT 版本过低,其 vcs.go 不支持新版 git+ssh 或 https 认证策略,导致 direct 模式静默失败。
关键诊断命令
# 查看实际生效的 GOROOT 和版本
go env GOROOT && $GOROOT/bin/go version
# 输出示例:
# /usr/local/go
# go version go1.19.0 linux/amd64
此输出揭示工具链运行时加载的是旧版
GOROOT,其内置modfetch逻辑无法处理现代仓库的 ref 格式(如refs/heads/main),fallback 时直接返回空响应而非降级尝试。
修复方案对比
| 方案 | 操作 | 风险 |
|---|---|---|
同步更新 GOROOT |
替换完整 $GOROOT 目录并重置 PATH |
需验证所有构建脚本兼容性 |
显式指定 GOCACHE + GOMODCACHE |
避免缓存污染,但不解决 vcs 协议缺陷 | 治标不治本,fallback 仍失败 |
graph TD
A[go build] --> B{GOPROXY 返回 404?}
B -->|是| C[触发 fallback 到 direct]
C --> D[调用 GOROOT/src/cmd/go/internal/modfetch/vcs.go]
D --> E[旧版 vcs.go 不识别新 Git refs 格式]
E --> F[ls-remote 返回空,模块解析中断]
2.5 理论推演:二进制包安装对Go module checksum数据库校验路径的隐式约束
当 go install 使用预编译二进制(如 gopkg.in/yaml.v3@v3.0.1 的 .zip 归档)时,Go 工具链跳过源码解析,直接解压并写入 $GOCACHE。此时 sum.golang.org 校验仅作用于归档哈希,而非模块源码树。
数据同步机制
二进制包安装强制触发 go mod download -json 预检,确保 sumdb 中存在对应 h1: 前缀的 checksum 记录:
# go mod download -json gopkg.in/yaml.v3@v3.0.1
{
"Path": "gopkg.in/yaml.v3",
"Version": "v3.0.1",
"Sum": "h1:c7jHmD6QrCqy/8T9QzJ+VZkL5uKQX4aUeE1YtRZQx2A=",
"GoModSum": "h1:..."
}
→ Sum 字段必须与 sum.golang.org/gopkg.in/yaml.v3/@v/v3.0.1.info 中的 h1: 行严格匹配,否则 go install 失败。
校验路径依赖链
| 组件 | 是否参与校验 | 约束条件 |
|---|---|---|
go.sum 文件 |
否 | 二进制安装不写入 go.sum |
sum.golang.org |
是 | 必须存在对应 h1: 记录 |
$GOCACHE 缓存哈希 |
是 | 与 sumdb 返回值双重比对 |
graph TD
A[go install ...@vX.Y.Z] --> B{是否为二进制包?}
B -->|是| C[查询 sum.golang.org]
C --> D[校验 h1:... 是否存在]
D --> E[下载 zip 并验证 SHA256]
E --> F[写入 GOCACHE]
该流程隐式要求:所有二进制分发渠道(如 GitHub Releases)必须与 sum.golang.org 的 checksum 记录保持原子性同步——任一环节滞后即导致校验中断。
第三章:通过系统包管理器安装(apt/yum/brew)
3.1 理论剖析:包管理器封装层对GOENV与全局配置文件的覆盖逻辑
Go 工具链在多环境协同中依赖 GOENV 环境变量控制配置加载行为,而现代包管理器(如 gopls 封装层、go-mod-proxy 或 devbox)会主动介入该流程。
GOENV 的三态语义
GOENV=on(默认):读取$HOME/go/envGOENV=off:完全忽略全局 env 文件GOENV=file://...:指定自定义路径(Go 1.22+)
封装层覆盖优先级
# 示例:devbox 启动时注入的覆盖逻辑
export GOENV=off
export GOPROXY=https://proxy.golang.org,direct
export GOSUMDB=sum.golang.org
此代码块强制禁用
go env -w写入的全局配置,确保构建可重现性。GOENV=off使go命令跳过$HOME/go/env解析,所有配置均由封装层通过环境变量注入,实现声明式管控。
| 覆盖源 | 优先级 | 是否持久化 | 可被 go env -w 修改? |
|---|---|---|---|
封装层 export |
最高 | 否 | 否 |
GOENV=file:// |
中 | 是 | 否(路径只读) |
$HOME/go/env |
最低 | 是 | 是 |
graph TD
A[go 命令启动] --> B{GOENV 状态}
B -->|off| C[忽略所有磁盘配置]
B -->|on/file://| D[加载指定 env 文件]
C --> E[仅使用当前 shell 环境变量]
D --> F[合并 env 文件 + shell 变量]
3.2 实践对比:Ubuntu apt install golang-go vs macOS brew install go 的GOSUMDB默认策略差异
默认行为差异根源
apt 安装的 golang-go(Debian/Ubuntu 官方包)禁用 GOSUMDB(设为 off),而 brew install go 安装的官方 Go 二进制包启用 sum.golang.org(默认值)。这源于分发渠道对校验权威性的不同信任模型。
验证方式
# Ubuntu (apt)
$ go env GOSUMDB
off
此配置跳过模块校验,加速内网部署,但牺牲完整性验证;适用于受控环境或离线 CI。
# macOS (brew)
$ go env GOSUMDB
sum.golang.org
由 Go 团队托管的透明日志服务,强制校验
go.sum一致性,防范依赖投毒。
策略对比表
| 分发方式 | GOSUMDB 默认值 | 校验启用 | 信任锚点 |
|---|---|---|---|
apt |
off |
❌ | 无 |
brew |
sum.golang.org |
✅ | Google TLS 证书 |
数据同步机制
graph TD
A[go get] --> B{GOSUMDB=off?}
B -->|Yes| C[跳过 sum 检查]
B -->|No| D[向 sum.golang.org 查询哈希]
D --> E[比对本地 go.sum]
3.3 理论+实践:系统级pkg安装引发GOPRIVATE环境变量被systemd或shell profile劫持的排查路径
现象定位:GOPRIVATE突然失效
执行 go build 时私有模块解析失败,但 echo $GOPRIVATE 在交互式 shell 中显示正常——说明环境变量在构建进程上下文中丢失。
排查优先级清单
- ✅ 检查
/etc/profile.d/*.sh中是否覆盖 GOPRIVATE(如export GOPRIVATE=*被误删) - ✅ 验证 systemd 用户服务环境:
systemctl --user show-environment | grep GOPRIVATE - ❌ 排除
.bashrc—— Go 构建由 systemd 启动时不加载交互式配置
关键验证命令
# 查看 go 命令实际运行时的环境(绕过 shell 层)
systemd-run --scope --collect env | grep GOPRIVATE
此命令模拟 systemd scope 下的干净环境。若输出为空,证明
systemd --user未继承 shell profile 设置,需在~/.config/environment.d/go.conf中声明GOPRIVATE=git.internal.corp。
环境继承关系(mermaid)
graph TD
A[/etc/environment] --> B[systemd --user]
C[~/.config/environment.d/*.conf] --> B
D[~/.bashrc] -.->|不继承| B
B --> E[go build 进程]
| 来源 | 是否影响 go build | 修复方式 |
|---|---|---|
/etc/environment |
✅ | 直接编辑,重启 user session |
environment.d/*.conf |
✅ | systemctl --user restart |
~/.bashrc |
❌ | 不适用,需迁移至 environment.d |
第四章:通过Go官方安装脚本(gotip或install.sh)安装
4.1 理论溯源:install.sh中GOROOT/GOPATH自动探测逻辑对GONOSUMDB作用域的影响
install.sh 在初始化 Go 环境时,会通过 readlink -f $(which go) 和 go env GOROOT GOPATH 动态推导路径,进而影响 GONOSUMDB 的匹配边界:
# 探测逻辑片段(简化)
if [ -z "$GOROOT" ]; then
GOROOT=$(go env GOROOT 2>/dev/null) || \
GOROOT=$(dirname $(dirname $(readlink -f $(which go))))
fi
GOPATH=${GOPATH:-$(go env GOPATH 2>/dev/null)}
export GONOSUMDB="*.internal,$(basename "$GOROOT")/src/*"
该逻辑导致 GONOSUMDB 值依赖于探测出的 GOROOT 路径结构——若 GOROOT 被误判为 /usr/local/go,则 GONOSUMDB 会错误排除 /usr/local/go/src/*,但实际 Go 源码可能位于 /opt/go/src,造成校验绕过。
关键影响维度
GONOSUMDB是前缀匹配,非 glob 或正则- 探测偏差 →
GOROOT错位 →GONOSUMDB作用域收缩或越界 GOPATH未参与匹配,但影响go get默认模块解析路径
| 探测方式 | GOROOT 准确性 | GONOSUMDB 实际覆盖范围 |
|---|---|---|
go env GOROOT |
✅ 高 | 精确对应当前 Go 安装源码树 |
readlink fallback |
⚠️ 中低 | 可能指向 symlink 目标而非真实安装路径 |
graph TD
A[执行 install.sh] --> B{GOROOT 是否已设置?}
B -->|否| C[调用 go env GOROOT]
B -->|是| D[直接使用环境变量]
C --> E[失败?]
E -->|是| F[回退 readlink 推导]
F --> G[生成 GONOSUMDB 前缀]
G --> H[模块校验跳过逻辑生效]
4.2 实践验证:使用curl -sSL https://golang.org/dl/… 安装时proxy配置被重置的复现与修复
复现现象
执行 curl -sSL https://golang.org/dl/go1.22.5.linux-amd64.tar.gz | sudo tar -C /usr/local -xzf - 时,若系统已配置 https_proxy 环境变量,下载过程仍可能直连失败——因 Go 官方重定向至 https://dl.google.com/...,而 curl 默认不继承 https_proxy 对重定向目标的代理策略。
关键参数分析
# 正确启用代理继承(含重定向)
curl -sSL --proxy-insecure --location \
-H "Accept: application/gzip" \
https://golang.org/dl/go1.22.5.linux-amd64.tar.gz | \
sudo tar -C /usr/local -xzf -
--location:启用重定向跟随(必需)--proxy-insecure:允许代理证书不校验(适配企业内部代理)-H "Accept: application/gzip":显式声明接受压缩响应,避免 406 错误
修复方案对比
| 方案 | 是否保留 proxy | 重定向支持 | 适用场景 |
|---|---|---|---|
curl -x "$https_proxy" |
✅ 显式指定 | ❌ 需手动处理跳转 | 调试定位 |
curl --location --proxy-env |
✅ 自动继承 | ✅ | 生产推荐 |
系统级 export https_proxy=... |
✅ 环境生效 | ✅(需 --location) |
CI/CD 可靠 |
graph TD
A[curl 请求 golang.org] --> B{是否重定向?}
B -->|是| C[跳转至 dl.google.com]
B -->|否| D[直接下载]
C --> E[检查 proxy-env 是否启用]
E -->|否| F[连接超时]
E -->|是| G[成功代理转发]
4.3 理论+实践:gotip安装对Go module cache目录权限继承机制与checksum校验失败的关联分析
权限继承异常现象
gotip 安装时若以 sudo 执行,会将 $GOCACHE(默认 ~/.cache/go-build)及其子目录(含 pkg/mod/cache/download/)的所有者设为 root,导致普通用户后续 go build 无法写入 module cache。
校验失败链式触发
# 触发 checksum mismatch 的典型日志
go: downloading github.com/example/lib v1.2.3
verifying github.com/example/lib@v1.2.3: checksum mismatch
downloaded: h1:abc123...
expected: h1:def456...
该错误并非校验逻辑缺陷,而是因权限不足导致 go 无法更新 sum.gob 缓存文件,被迫复用旧 checksum 或跳过校验缓存,引发一致性断裂。
关键路径权限对比表
| 路径 | 正常权限(user:user) | gotip sudo 后权限 | 影响 |
|---|---|---|---|
~/.cache/go-build |
drwx------ |
drwx------ root:root |
go-build 缓存失效 |
~/go/pkg/mod/cache/download |
drwx------ |
drwx------ root:root |
sum.gob 写入拒绝 → checksum 校验回退 |
修复方案(推荐)
- ✅ 永远以普通用户运行
gotip download - ✅ 清理残留:
sudo chown -R $USER:$USER ~/.cache/go-build ~/go/pkg/mod - ✅ 验证:
go env GOCACHE GOPATH+ls -ld $(go env GOCACHE)
graph TD
A[gotip install via sudo] --> B[cache dir owned by root]
B --> C[go build runs as user]
C --> D[fail to write sum.gob]
D --> E[fall back to incomplete checksum state]
E --> F[checksum mismatch on next module fetch]
4.4 实践调试:通过strace追踪install.sh执行过程,定位GOSUMDB环境变量注入时机偏差
strace捕获关键系统调用
运行以下命令实时监控环境变量相关操作:
strace -e trace=execve,clone,setenv,getenv -f ./install.sh 2>&1 | grep -E "(GOSUMDB|execve|setenv)"
-e trace=execve,clone,setenv,getenv 精确过滤进程创建与环境操作;-f 跟踪子进程;grep 提取GOSUMDB上下文。该命令可暴露execve调用前环境是否已含GOSUMDB=off。
关键时序发现
分析输出发现:
install.sh启动时未设置GOSUMDBgo install子进程execve前才由 shell 内置export GOSUMDB=off注入- 注入发生在
source某配置脚本后,而非脚本开头
注入时机偏差对比表
| 阶段 | GOSUMDB 是否存在 | 来源 |
|---|---|---|
| install.sh 初始环境 | ❌ | 父shell未导出 |
source config.sh 后 |
✅ | export GOSUMDB=off 执行后 |
go install execve 时 |
✅ | 继承自当前shell环境 |
流程定位
graph TD
A[install.sh start] --> B[read config.sh]
B --> C[export GOSUMDB=off]
C --> D[go install invoked]
D --> E[execve with GOSUMDB]
第五章:Docker容器内Go环境构建与多阶段编译最佳实践
Go项目结构与Docker化前提
一个典型的Go Web服务(如基于Gin的API)通常包含main.go、go.mod、internal/和cmd/目录。为保障构建可复现性,必须在go.mod中明确指定Go版本(如go 1.22),并禁用GO111MODULE=on以外的模块行为。Docker构建前需确保本地go build -o bin/app .可成功生成二进制文件,且无CGO_ENABLED=1依赖(避免libc绑定问题)。
多阶段编译基础流程
使用官方golang:1.22-alpine作为构建阶段镜像,alpine:3.19作为运行时镜像,可将最终镜像体积从~900MB降至~12MB。关键在于分离编译环境与运行环境:第一阶段执行go build -ldflags="-s -w"(剥离调试符号与DWARF信息),第二阶段仅拷贝静态链接的二进制文件。
构建脚本与Dockerfile协同优化
在项目根目录创建build.sh,封装docker build --build-arg BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) --tag myapp:latest .命令,并通过--build-arg注入构建时间戳,供main.go中var buildTime = "${BUILD_TIME}"读取。该机制支持运行时验证镜像新鲜度。
安全加固实践
启用docker build --no-cache --squash(若Docker守护进程支持),合并中间层;在Dockerfile中使用非root用户:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:3.19
RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001
WORKDIR /root/
COPY --from=builder /app/app .
USER appuser
EXPOSE 8080
CMD ["./app"]
构建缓存失效诊断表
| 触发因素 | 缓存失效层级 | 排查命令 |
|---|---|---|
go.mod 修改 |
go mod download 步骤 |
docker build --progress=plain . \| grep "CACHED" |
main.go 变更 |
最终go build步骤 |
docker history <image-id> 查看层大小变化 |
构建性能对比数据
在CI流水线(GitHub Actions + 4c8g runner)中实测:
- 单阶段构建(
golang:1.22-slim)耗时 82s,镜像体积 842MB - 多阶段构建(Alpine双阶段)耗时 57s,镜像体积 11.8MB
- 启用
--cache-from后二次构建降至 23s
运行时环境变量注入策略
通过docker run -e ENV=prod -e DATABASE_URL=...传入敏感配置,Go代码中使用os.Getenv("DATABASE_URL")读取。禁止在Dockerfile中使用ENV硬编码密码,改用--secret机制(需启用BuildKit):
docker build --secret id=database_url,src=./secrets/db_url .
并在main.go中挂载/run/secrets/database_url路径读取。
CI/CD流水线集成要点
在.github/workflows/ci.yml中定义jobs.build.steps,强制执行go vet ./...与go test -race ./...,失败则中断Docker构建。同时对生成的app二进制执行file app验证是否为ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked,确保无动态依赖。
跨平台构建支持
针对ARM64部署场景,在Dockerfile中添加FROM --platform=linux/arm64 golang:1.22-alpine AS builder,并使用docker buildx build --platform linux/amd64,linux/arm64 -t myapp:multiarch --push .生成多架构镜像。验证命令:docker manifest inspect myapp:multiarch。
