第一章:Go语言环境变量与错误提示翻译手册导论
Go语言的开发体验高度依赖环境变量的正确配置,而初学者常因GOROOT、GOPATH、GOBIN及PATH等变量设置不当,遭遇诸如command not found: go、cannot find package "xxx"或go: cannot find main module等晦涩提示。本手册聚焦于真实开发场景中高频出现的环境变量误配现象与对应错误信息,提供精准的中英对照解析与可验证的修复路径。
核心环境变量作用简述
GOROOT:指向Go安装根目录(如/usr/local/go),通常由安装器自动设置,手动修改需确保其值与go version -v输出一致;GOPATH:定义工作区路径(默认为$HOME/go),包含src/(源码)、pkg/(编译缓存)、bin/(可执行文件)三子目录;GOBIN:当显式设置时,go install将二进制文件写入此路径,否则默认使用$GOPATH/bin;PATH:必须包含$GOROOT/bin和$GOBIN(或$GOPATH/bin),否则系统无法识别go命令。
快速诊断与修复步骤
执行以下命令检查当前配置是否自洽:
# 1. 验证Go基础路径与版本
go env GOROOT GOPATH GOBIN
go version
# 2. 检查PATH是否包含必要路径(应同时含GOROOT/bin与GOBIN)
echo $PATH | tr ':' '\n' | grep -E "(go|bin)"
# 3. 若发现GOROOT为空或路径错误,重新导出(以Linux/macOS为例)
export GOROOT="/usr/local/go" # 替换为实际安装路径
export GOPATH="$HOME/go"
export GOBIN="$GOPATH/bin"
export PATH="$GOROOT/bin:$GOBIN:$PATH"
注意:上述
export命令仅对当前终端会话生效;如需永久生效,请将配置追加至~/.bashrc(Linux)或~/.zshrc(macOS Catalina+)并执行source ~/.bashrc。
常见错误提示对照表
| 英文错误片段 | 中文含义 | 典型诱因 |
|---|---|---|
go: cannot find main module |
无法定位主模块 | 当前目录不在GOPATH/src内且未启用Go Modules |
build: cannot load xxx: cannot find module providing package xxx |
构建失败:找不到提供该包的模块 | go.mod缺失或replace路径错误 |
command not found: go |
系统未识别go命令 | PATH未包含$GOROOT/bin |
第二章:核心构建与缓存类环境变量详解
2.1 GOCACHE:本地构建缓存机制原理与跨平台清理实践
Go 工具链通过 GOCACHE 环境变量指定构建缓存目录(默认为 $HOME/Library/Caches/go-build macOS / %LocalAppData%\go-build Windows / $XDG_CACHE_HOME/go-build Linux),实现 .a 归档与编译中间产物的哈希寻址复用。
缓存键生成逻辑
缓存条目由源码内容、编译器版本、GOOS/GOARCH、cgo 状态及依赖哈希联合计算 SHA256 得到唯一 key。
跨平台清理策略
# 清理过期缓存(保留最近7天未访问项)
go clean -cache
# 强制清空(含所有平台缓存,需谨慎)
GOCACHE=/tmp/go-cache go build .
go clean -cache调用内部cache.NewFileCache实例执行 LRU+mtime 双维度淘汰,避免手动rm -rf $GOCACHE破坏文件锁一致性。
缓存目录结构对比
| 平台 | 默认路径 | 文件系统特性 |
|---|---|---|
| macOS | ~/Library/Caches/go-build |
APFS 硬链接支持 |
| Windows | %LOCALAPPDATA%\go-build |
NTFS 符号链接兼容 |
| Linux | $XDG_CACHE_HOME/go-build(或 ~/.cache/go-build) |
支持 overlayfs 分层 |
graph TD
A[go build main.go] --> B{GOCACHE 是否命中?}
B -->|是| C[返回 cached .a]
B -->|否| D[编译 → 写入 GOCACHE/key]
D --> C
2.2 GOMODCACHE:模块下载缓存路径控制与离线构建配置实战
Go 模块缓存由 GOMODCACHE 环境变量显式控制,默认位于 $GOPATH/pkg/mod。自定义路径可解耦构建环境与用户空间:
export GOMODCACHE="/opt/go-modules-cache"
go mod download
此命令将所有依赖模块下载至
/opt/go-modules-cache,而非默认路径。GOMODCACHE仅影响go mod download、go build等操作的读写位置,不改变go env中的GOPATH或GOCACHE。
离线构建需预加载依赖并禁用网络回退:
# 预缓存全部依赖(联网阶段)
go mod download
# 离线构建(断网下生效)
GO111MODULE=on GOPROXY=off go build -o app .
| 场景 | 环境变量配置 | 行为说明 |
|---|---|---|
| 标准缓存 | GOMODCACHE 未设置 |
使用 $GOPATH/pkg/mod |
| 共享缓存 | GOMODCACHE="/shared/mod" |
多项目复用同一缓存目录 |
| 容器化构建 | GOMODCACHE="/tmp/mod" |
构建后自动清理,避免污染层 |
graph TD
A[go build] --> B{GOPROXY 设置?}
B -->|on| C[从代理拉取并缓存]
B -->|off| D[仅读 GOMODCACHE]
D --> E{缓存命中?}
E -->|是| F[直接构建]
E -->|否| G[报错:missing module]
2.3 GOPATH与GOBIN:传统工作区模型解析及Go 1.18+模块化迁移策略
传统工作区结构
GOPATH 曾是 Go 的唯一源码与依赖根目录,强制要求项目置于 $GOPATH/src/<import-path> 下。GOBIN 则指定 go install 生成二进制的输出路径(默认为 $GOPATH/bin)。
环境变量对比(Go 1.17 及之前)
| 变量 | 默认值 | 作用 |
|---|---|---|
GOPATH |
$HOME/go |
源码、依赖、构建产物根目录 |
GOBIN |
$GOPATH/bin |
可执行文件安装路径 |
# 查看当前工作区配置
go env GOPATH GOBIN
# 输出示例:
# /Users/me/go
# /Users/me/go/bin
此命令返回当前生效的路径;若
GOBIN未显式设置,则继承GOPATH/bin。Go 1.18+ 中GOBIN仍有效,但go install已支持模块路径直接安装(如go install golang.org/x/tools/gopls@latest),不再依赖$GOPATH/src。
迁移关键步骤
- 删除
$GOPATH/src下旧项目(模块已由go.mod管理) - 设置
GOBIN为独立路径(如/usr/local/go-bin),避免污染用户级GOPATH - 使用
go mod init初始化模块,启用GO111MODULE=on
graph TD
A[Go ≤1.11] -->|依赖 GOPATH/src| B[单一全局工作区]
B --> C[Go 1.12–1.17]
C -->|模块可选| D[混合模式]
D --> E[Go 1.18+]
E -->|GO111MODULE=on 默认| F[模块优先,GOPATH 仅作缓存/GOBIN 输出]
2.4 GODEBUG:运行时调试标志的底层作用机制与典型故障注入验证
GODEBUG 是 Go 运行时内置的动态调试开关,通过环境变量注入,在 runtime/debug 和 runtime 包初始化阶段被解析并影响调度器、GC、内存分配等关键路径的行为。
故障注入原理
Go 启动时调用 runtime/debug.ReadGCStats 前,会检查 os.Getenv("GODEBUG") 并解析为键值对(如 gctrace=1,schedtrace=1000),各子系统在关键分支中插入条件判断:
// runtime/proc.go 中调度器采样逻辑节选
if debug.schedtrace > 0 && (schedtick%debug.schedtrace) == 0 {
printschedtrace(true) // 触发调度轨迹打印
}
debug.schedtrace 由 parseGODEBUG 从 "schedtrace=1000" 提取,单位为调度 tick 数;值为 0 则跳过采样,非零则启用周期性诊断输出。
典型调试标志对照表
| 标志名 | 取值示例 | 作用域 | 效果 |
|---|---|---|---|
gctrace |
1 |
GC 子系统 | 每次 GC 打印详细统计 |
schedtrace |
1000 |
调度器 | 每 1000 次调度打印状态 |
madvdontneed |
1 |
内存管理(Linux) | 禁用 MADV_DONTNEED 回收 |
注入验证流程
graph TD
A[启动 Go 程序] --> B[读取 os.Getenv(“GODEBUG”)]
B --> C[parseGODEBUG 解析键值对]
C --> D[写入全局 debug 结构体字段]
D --> E[运行时各模块按需检查对应字段]
E --> F[满足条件时触发诊断/故障行为]
2.5 GOEXPERIMENT:实验性功能开关的启用逻辑与生产环境风险评估
GOEXPERIMENT 是 Go 编译器在构建阶段启用非稳定特性的环境变量机制,其值为逗号分隔的实验特性标识符(如 fieldtrack,loopvar)。
启用方式与编译约束
# 启用多个实验特性
GOEXPERIMENT=fieldtrack,loopvar go build -o app .
⚠️ 该变量仅在 go build/go run 时生效,不参与 go test 或 go mod 操作;且要求 Go 版本严格匹配特性引入版本(如 loopvar 自 Go 1.22 起默认启用,但需显式设 GOEXPERIMENT=loopvar 才在旧版中可用)。
生产环境风险维度
| 风险类型 | 表现示例 | 可观测性 |
|---|---|---|
| 语义变更 | loopvar 改变闭包变量捕获行为 |
运行时 panic |
| 工具链兼容性 | vet / gopls 无法识别新语法 |
IDE 报错 |
| 二进制稳定性 | 启用 arena 后 GC 行为不可逆 |
pprof 异常 |
安全启用路径
// 构建脚本中应强制校验
if !strings.Contains(os.Getenv("GOEXPERIMENT"), "fieldtrack") {
log.Fatal("fieldtrack required for memory tracing")
}
此检查确保构建环境显式声明依赖,避免隐式失效。
graph TD
A[设置 GOEXPERIMENT] –> B{Go 版本兼容?}
B –>|否| C[构建失败]
B –>|是| D[生成含实验语义的二进制]
D –> E[运行时无降级路径]
第三章:平台与编译器相关环境变量剖析
3.1 GOROOT:标准工具链定位原理与多版本共存管理方案
GOROOT 是 Go 运行时与编译器的“权威根目录”,由 go env GOROOT 输出,决定 go 命令调用的 pkg, src, bin 等核心路径来源。
定位机制本质
Go 启动时按序检查:
- 环境变量
GOROOT(显式指定) - 可执行文件所在路径向上回溯(如
/usr/local/go/bin/go→/usr/local/go) - 编译时嵌入的默认路径(仅当以上均未命中)
多版本共存实践方案
| 方案 | 适用场景 | 切换粒度 | 典型工具 |
|---|---|---|---|
| 符号链接软切换 | 全局默认版本 | 系统级 | sudo ln -sf |
GOROOT 环境隔离 |
CI/CD 或多项目并行 | Shell会话 | env GOROOT=... go version |
gvm / goenv |
开发者日常高频切换 | 用户级 | 自动管理 $GOROOT 与 $PATH |
# 示例:临时使用 Go 1.21.0 工具链(不修改系统默认)
export GOROOT="/opt/go/1.21.0"
export PATH="$GOROOT/bin:$PATH"
go version # 输出:go version go1.21.0 darwin/arm64
该命令通过覆盖
GOROOT和前置PATH,使go命令优先加载指定版本的二进制与标准库。关键在于:go工具链自身不依赖 GOPATH,仅依赖 GOROOT 的src,pkg,bin三要素完整性。
graph TD
A[go command invoked] --> B{GOROOT set?}
B -->|Yes| C[Use $GOROOT/bin, /src, /pkg]
B -->|No| D[Resolve from binary path]
D --> E[Find nearest parent with src/lib/go/src]
E --> F[Validate go/src/runtime/internal/sys/zversion.go]
3.2 CGO_ENABLED:C语言互操作开关对交叉编译与静态链接的影响分析
CGO_ENABLED 是 Go 构建系统中控制是否启用 CGO(C 语言互操作)的核心环境变量,其取值直接决定 Go 程序能否调用 C 代码、链接 C 库,以及能否进行纯静态链接。
关键行为差异
CGO_ENABLED=1(默认):启用 CGO,依赖宿主机 C 工具链(如gcc),生成动态链接的二进制(含libc依赖);CGO_ENABLED=0:禁用 CGO,强制纯 Go 实现(如net包回退至netgo),支持完全静态链接。
典型构建命令对比
# 启用 CGO(Linux 主机编译 Linux 二进制,动态链接)
CGO_ENABLED=1 go build -o app-dynamic .
# 禁用 CGO(生成真正静态二进制,无 libc 依赖)
CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o app-static .
逻辑分析:
CGO_ENABLED=0时,Go 忽略所有import "C"块与cgo注释;-a强制重新编译所有依赖(含标准库),-ldflags '-extldflags "-static"'指示外部链接器使用静态模式(仅在CGO_ENABLED=0下可靠生效)。
交叉编译约束矩阵
| 目标平台 | CGO_ENABLED=1 | CGO_ENABLED=0 |
|---|---|---|
linux/amd64 |
需本地 x86_64-linux-gnu-gcc |
✅ 无需 C 工具链,可直接交叉 |
windows/amd64 |
需 MinGW 工具链 | ✅ 官方支持,生成 .exe 静态文件 |
graph TD
A[go build] --> B{CGO_ENABLED=0?}
B -->|Yes| C[跳过 C 预处理<br>使用 netgo/ osusergo]
B -->|No| D[调用 gcc<br>链接 libc.so]
C --> E[静态二进制<br>零外部依赖]
D --> F[动态二进制<br>需目标 libc]
3.3 GOOS/GOARCH:目标平台组合的语义约束与容器化构建适配实践
Go 的 GOOS 与 GOARCH 并非简单环境变量,而是编译期强语义约束:二者共同定义二进制可执行文件的目标运行时契约。
构建矩阵的语义边界
GOOS=linux要求内核 ABI 兼容(如epoll、clone3系统调用可用性)GOARCH=arm64隐含+strict-align行为及v8.2a指令集基线- 组合
GOOS=windows GOARCH=386已被 Go 1.21+ 明确弃用(见 go.dev/doc/go1.21#arch)
容器化构建典型适配模式
# 多阶段构建:显式声明目标平台语义
FROM golang:1.22-alpine AS builder
ARG TARGETOS=linux
ARG TARGETARCH=arm64
ENV GOOS=${TARGETOS} GOARCH=${TARGETARCH}
RUN go build -o /app/server ./cmd/server
逻辑分析:
ARG+ENV组合确保构建阶段严格遵循目标平台语义;go build不依赖宿主机GOOS/GOARCH,避免交叉编译污染。alpine基础镜像天然适配linux/amd64构建机,但通过环境变量覆盖实现跨平台输出。
常见有效组合兼容性表
| GOOS | GOARCH | 支持状态 | 关键约束 |
|---|---|---|---|
| linux | amd64 | ✅ 稳定 | 默认组合,glibc/musl 皆可 |
| linux | arm64 | ✅ 稳定 | 需内核 ≥ 4.15(membarrier) |
| darwin | arm64 | ✅ 稳定 | 仅支持 macOS 11.0+ |
| windows | amd64 | ✅ 稳定 | 不支持 CGO 依赖的 POSIX 调用 |
graph TD
A[源码] --> B{go build}
B -->|GOOS=linux<br>GOARCH=arm64| C[linux-arm64 二进制]
B -->|GOOS=windows<br>GOARCH=amd64| D[windows-amd64.exe]
C --> E[部署至 AWS Graviton]
D --> F[分发至 Windows Server]
第四章:模块、代理与网络行为环境变量实战指南
4.1 GOPROXY:模块代理协议兼容性与私有镜像服务器高可用部署
Go 模块代理(GOPROXY)需严格遵循 v1 协议规范,支持 GET /@v/list、GET /@v/vX.Y.Z.info 等端点,确保与 go get、go mod download 工具链零适配成本。
核心兼容性要求
- 必须返回标准
application/vnd.go-mod-file或application/jsonMIME 类型 - 版本元数据需包含
Version、Time、Sum字段(如sum.golang.org格式) - 支持
X-Go-Module和X-Go-Proxy响应头用于调试溯源
高可用部署架构
# 使用反向代理层实现负载与熔断
upstream goproxy_backend {
least_conn;
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
该配置启用最少连接调度与主动健康检查,max_fails 和 fail_timeout 防止故障节点持续接收请求;keepalive 复用后端连接,降低 TLS 握手开销。
| 组件 | 作用 | 推荐方案 |
|---|---|---|
| 缓存层 | 减少上游拉取频次 | Redis + TTL 7d |
| 鉴权网关 | 控制私有模块访问权限 | OAuth2 + JWT |
| 同步机制 | 跨集群镜像一致性保障 | rsync + manifest checksum |
graph TD
A[Client go mod download] --> B[Nginx LB]
B --> C[Node1: Athens Proxy]
B --> D[Node2: Athens Proxy]
C & D --> E[(Redis Cache)]
C & D --> F[Upstream: proxy.golang.org]
4.2 GONOSUMDB/GOPRIVATE:校验和数据库绕过机制与企业级私有模块安全管控
Go 模块校验和验证默认依赖 sum.golang.org,但企业私有模块既不可公开索引,又需规避外部依赖风险。
核心环境变量语义
GONOSUMDB:指定不查校验和数据库的模块路径前缀(支持通配符*和逗号分隔)GOPRIVATE:声明私有模块路径前缀,自动设置对应GONOSUMDB且禁用代理(GOPROXY=direct)
# 示例:绕过所有 company.com 及其子路径的校验和检查
export GOPRIVATE="*.company.com"
export GONOSUMDB="*.company.com"
此配置使
go get internal.company.com/auth/v2跳过sum.golang.org查询,仅校验本地go.sum或允许缺失(若首次引入)。
行为对比表
| 场景 | GOPRIVATE 设置 | GONOSUMDB 设置 | 实际效果 |
|---|---|---|---|
公共模块 github.com/go-yaml/yaml |
未匹配 | 未匹配 | 正常查询 sum.golang.org |
私有模块 git.company.com/internal/log |
git.company.com |
自动继承 | 跳过校验和检查,仅本地验证 |
安全管控流程
graph TD
A[go get private.module/company] --> B{是否匹配 GOPRIVATE?}
B -->|是| C[禁用 GOPROXY,跳过 sum.golang.org]
B -->|否| D[走默认代理 + 校验和验证]
C --> E[仅校验本地 go.sum 或接受新条目]
4.3 GOWORK:多模块工作区(Workspace)的协同开发流程与IDE集成技巧
Go 1.18 引入的 go.work 文件为多模块协同开发提供了原生支持,替代了早期 hack 式的 replace 与 GOPATH 混合方案。
工作区初始化与结构管理
go work init ./auth ./api ./shared
该命令生成 go.work,自动注册三个本地模块。./shared 作为公共依赖被其他模块复用,避免重复 vendoring。
IDE 集成关键配置
| IDE | 配置项 | 说明 |
|---|---|---|
| VS Code | "go.useLanguageServer": true |
启用 gopls 对 workspace 的感知能力 |
| GoLand | Settings → Go → Modules → Enable Workspace Mode | 必须勾选,否则仅识别根模块 |
依赖同步机制
// go.work 示例片段
go 1.22
use (
./auth
./api
./shared
)
use 块声明模块路径,gopls 依此构建统一的符号索引图;go run/go test 在 workspace 根目录执行时,自动解析跨模块导入路径。
graph TD
A[workspace root] --> B[go.work]
B --> C[auth module]
B --> D[api module]
B --> E[shared module]
C & D -->|import| E
4.4 HTTP_PROXY/HTTPS_PROXY:模块下载代理链路诊断与认证代理穿透方案
当 pip 或 npm 等工具在企业内网中无法拉取远程模块时,常因代理链路中断或认证缺失导致失败。
常见代理环境变量配置
export HTTP_PROXY="http://proxy.corp:8080"
export HTTPS_PROXY="https://proxy.corp:8080" # 注意:部分代理不支持 HTTPS_PROXY 使用 https:// 协议前缀
export NO_PROXY="localhost,127.0.0.1,.internal.corp"
逻辑分析:
HTTP_PROXY仅影响 HTTP 流量;HTTPS_PROXY实际被多数客户端(如 pip、curl)用于 HTTPS 请求的 CONNECT 隧道代理,但协议前缀应为http://(非https://),否则触发 TLS-in-TLS 错误。NO_PROXY支持域名后缀匹配,逗号分隔,无空格。
认证代理穿透方案
- 方式一:URL 内嵌凭证(仅限测试,存在泄露风险)
export HTTPS_PROXY="http://user:pass@proxy.corp:8080" - 方式二:使用
netrc文件(推荐)machine proxy.corp login user password pass
诊断流程
graph TD
A[发起 pip install] --> B{检查 HTTP_PROXY/HTTPS_PROXY}
B --> C[尝试 CONNECT 隧道]
C --> D{返回 407?}
D -->|是| E[需 Proxy-Authenticate]
D -->|否| F[成功或其它错误]
| 工具 | 尊重 HTTPS_PROXY | 支持 netrc | 备注 |
|---|---|---|---|
| pip | ✅ | ✅ | 需 --trusted-host 绕过证书校验 |
| curl | ✅ | ✅ | -v 可观察 Proxy-Auth 流程 |
| npm | ✅ | ❌ | 需 npm config set proxy |
第五章:常见错误提示的精准中英对照与根因溯源
Docker 容器启动失败:failed to create endpoint ... permission denied
该错误常出现在 SELinux 启用的 CentOS/RHEL 系统中。典型日志片段:
docker run -d -p 8080:80 nginx
# 输出:Error response from daemon: failed to create endpoint trusting_mclean on network bridge: failed to add the host (veth1234567) <=> sandbox (veth890abcd) pair interfaces: permission denied
根因是 Docker 默认网络驱动(bridge)创建 veth 设备时,被 SELinux 的 container_manage_cgroup 或 net_admin 权限策略拦截。验证方式:ausearch -m avc -ts recent | grep docker 可捕获拒绝事件。临时修复:setsebool -P container_use_ceph off;长期方案需在 /etc/selinux/targeted/modules/active/modules/docker.pp 中补充策略模块。
Python pip 安装报错:ERROR: Could not install packages due to an OSError: [Errno 13] Permission denied
此错误高频出现于 macOS Homebrew Python 或 Linux 用户级 Python 环境。例如执行 pip install --user flask 仍失败,实际路径为:
/usr/local/lib/python3.9/site-packages/ → 权限属 root
~/.local/lib/python3.9/site-packages/ → 但 pip 误写入系统路径
根本原因在于 pip 配置文件(~/.pip/pip.conf 或 /etc/pip.conf)中存在 target = /usr/local/lib/python3.9/site-packages 强制重定向。检查命令:pip config list -v。修正操作:pip config unset global.target,随后确认 python -m site --user-site 返回路径可写。
Kubernetes Pod 处于 ImagePullBackOff 状态的完整诊断链
| 现象 | 检查命令 | 关键输出特征 | 根因类别 |
|---|---|---|---|
Failed to pull image "private-registry.example.com/app:v1.2": rpc error: code = Unknown desc = unauthorized: authentication required |
kubectl describe pod <pod> → Events 区域 |
unauthorized: authentication required |
Secret 未挂载或 .dockerconfigjson base64 解码后字段缺失 auths 键 |
Failed to pull image "nginx:alpine": context deadline exceeded |
crictl pull nginx:alpine(节点执行) |
FATA[0030] pulling image failed: rpc error: code = DeadlineExceeded |
节点 DNS 解析失败(/etc/resolv.conf 中 nameserver 为 169.254.169.254 且无响应) |
Git 推送拒绝:! [remote rejected] main -> main (refusing to allow a GitHub App to create or update workflow.github/workflows/ci.yml)
该提示仅在启用 GitHub Actions 的仓库中触发,本质是 GitHub 的安全策略:默认禁止非管理员通过 git push 直接修改 .github/workflows/ 下的 YAML 文件。即使推送者拥有 admin 权限,若使用 Personal Access Token(PAT)而非 SSH 密钥,且 PAT 未勾选 workflow scope,则触发此拒绝。验证方式:curl -H "Authorization: token $TOKEN" https://api.github.com/repos/{owner}/{repo}/actions/permissions 返回 "enabled": false。修复必须通过 GitHub UI 手动启用 Actions 权限,或调用 API 更新:curl -X PUT -H "Authorization: token $TOKEN" -d '{"enabled":"true"}' https://api.github.com/repos/{owner}/{repo}/actions/permissions
flowchart TD
A[收到 ERROR: invalid reference format] --> B{检查镜像名}
B -->|含空格或大写字母| C[修正为小写+连字符:my-app:v1.0]
B -->|以下划线开头| D[删除前导_: _legacy-db → legacy-db]
C --> E[执行 docker build -t my-app:v1.0 .]
D --> E
E --> F[验证 docker images \| grep my-app] 