Go应用程序的条件构建、交叉编译与容器化部署
1. 条件构建应用程序
为了让示例更清晰易读,我们先将现有的Pomodoro应用程序复制到新的工作环境中。具体操作如下:
$ cp -r $HOME/pragprog.com/rggo/persistentDataSQL/pomo $HOME/pragprog.com/rggo/distributing
$ cd $HOME/pragprog.com/rggo/distributing/pomo
由于使用了Go模块,切换到新目录后无需额外操作,Go模块会自动解析当前目录的模块。
若要在Pomodoro应用中使用
notify
包发送通知,需在
pomo
子目录下的
go.mod
文件中添加依赖,并使用
replace
指令将包路径指向本地目录:
// distributing/pomo/go.mod
module pragprog.com/rggo/interactiveTools/pomo
go 1.16
require (
github.com/mattn/go-sqlite3 v1.14.5
github.com/mitchellh/go-homedir v1.1.0
github.com/mum4k/termdash v0.13.0
github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.1
pragprog.com/rggo/distributing/notify v0.0.0
)
replace pragprog.com/rggo/distributing/notify => ../../distributing/notify
若包在外部Git仓库中,Go工具会在需要时自动下载,就无需此步骤。
接下来,我们要在
app
包中添加通知功能,并使其可选。默认情况下,构建应用时会包含通知功能,但用户可通过提供
disable_notification
构建标签来禁用。
为此,我们添加一个辅助函数
send_notification
。默认情况下,该函数调用
notify
包发送通知;同时,我们在另一个文件中使用
disable_notification
构建标签部署一个不执行任何操作的版本。
在
pomo/app
目录下创建
notification_stub.go
文件,添加构建标签
disable_notification
和
containers
(用空格分隔):
// distributing/pomo/app/notification_stub.go
// +build containers disable_notification
package app
func send_notification(msg string) {
return
}
再创建
pomo/app/notification.go
文件,启用无
disable_notification
和
containers
标签时的通知功能:
// distributing/pomo/app/notification.go
// +build !containers,!disable_notification
package app
import "pragprog.com/rggo/distributing/notify"
func send_notification(msg string) {
n := notify.New("Pomodoro", msg, notify.SeverityNormal)
n.Send()
}
然后在Pomodoro应用中调用这个新定义的函数。编辑
app/buttons.go
文件,在
start
回调中调用
send_notification
发送间隔开始的通知:
// distributing/pomo/app/buttons.go
start := func(i pomodoro.Interval) {
message := "Take a break"
if i.Category == pomodoro.CategoryPomodoro {
message = "Focus on your task"
}
w.update([]int{}, i.Category, message, "", redrawCh)
send_notification(message)
}
在
end
回调中调用该函数,通知用户间隔结束:
// distributing/pomo/app/buttons.go
end := func(i pomodoro.Interval) {
w.update([]int{}, "", "Nothing running...", "", redrawCh)
s.update(redrawCh)
message := fmt.Sprintf("%s finished !", i.Category)
send_notification(message)
}
保存并关闭文件。不使用任何标签重建应用以启用通知;若要禁用通知,使用
disable_notification
或
containers
标签重建应用。
为了在容器中运行Pomodoro应用,我们要禁用SQLite集成,仅使用
inMemory
仓库。编辑仓库文件的构建标签:
// distributing/pomo/pomodoro/repository/inMemory.go
// +build inmemory containers
// distributing/pomo/pomodoro/repository/sqlite3.go
// +build !inmemory,!containers
// distributing/pomo/cmd/repoinmemory.go
// +build inmemory containers
// distributing/pomo/cmd/reposqlite.go
// +build !inmemory,!containers
使用
go list
命令可验证特定构建中包含的文件:
$ go list -f '{{ .GoFiles }}' ./...
$ go list -tags=inmemory -f '{{ .GoFiles }}' ./...
$ go list -tags=containers -f '{{ .GoFiles }}' ./...
2. 交叉编译应用程序
与Python或Nodejs等解释型语言不同,Go是编译型语言,会生成包含运行应用所需所有依赖的二进制可执行文件,这使得Go应用具有极高的可移植性。
但Go生成的二进制文件是针对特定操作系统和架构的,不能在不同系统间直接运行。不过,Go支持交叉编译,可使用
go build
从单一平台为支持的操作系统和架构编译二进制文件。
查看支持的操作系统和架构组合:
$ go tool dist list
默认情况下,
go build
为当前运行平台编译应用。可使用
go env
命令查看Go环境的默认值:
$ go env GOOS
$ go env GOARCH
切换到Pomodoro目录,为当前平台构建应用二进制文件:
$ cd $HOME/pragprog.com/rggo/distributing/pomo
$ go build
使用
file
命令检查文件:
$ file pomo
默认情况下,Go生成的二进制文件是动态链接的。为使应用二进制文件更具可移植性,可在运行
go build
前设置
CGO_ENABLED=0
启用静态链接库:
$ CGO_ENABLED=0 go build
$ file pomo
若要为不同平台交叉编译应用,在运行
go build
前设置
GOOS
和
GOARCH
变量。例如,为Windows x86_64架构构建应用:
$ GOOS=windows GOARCH=amd64 go build
但对于Pomodoro工具,此命令会失败,因为默认使用的SQLite仓库需要编译C语言编写的SQLite库依赖。为测试此命令,可使用
inmemory
构建标签使用无外部依赖的
inMemory
仓库:
$ GOOS=windows GOARCH=amd64 go build -tags=inmemory
$ file pomo.exe
为实现自动化构建,我们创建一个Bash脚本
cross_build.sh
,为支持的所有平台(Linux、Windows、macOS)和架构(x86_64、ARM、ARM64)构建Pomodoro应用:
#!/bin/bash
OSLIST="linux windows darwin"
ARCHLIST="amd64 arm arm64"
for os in ${OSLIST}; do
for arch in ${ARCHLIST}; do
if [[ " $os / $arch " =~ ^(windows/arm64|darwin/arm)$ ]]; then continue; fi
echo Building binary for $os $arch
mkdir -p releases/${os}/${arch}
CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -tags=inmemory -o releases/${os}/${arch}/
done
done
运行脚本:
$ ./scripts/cross_build.sh
检查生成的二进制文件:
$ file release/*/*/*
若要为支持SQLite仓库的Windows二进制文件进行交叉编译,需提供支持Windows的替代C编译器,如MINGW:
$ CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 go build
运行此代码仍需在目标操作系统上安装SQLite。
3. 编译Go应用程序以在容器中运行
近年来,让用户在Linux容器中运行应用成为一种流行的分发方式。容器使用标准镜像格式打包应用及其所有依赖,并与系统上的其他进程隔离运行,利用Linux内核资源(如Namespaces和Cgroups)提供隔离和资源管理。
常见的容器运行时有Podman和Docker。若在Linux系统上运行示例,两者可互换使用;若在Windows或macOS上运行,Docker提供桌面版更易启动。
要将应用作为容器分发,需创建容器镜像,常用方法是使用Dockerfile。
为优化应用在容器中的运行,可传递额外的构建选项:
-
CGO_ENABLED=0
:启用静态链接二进制文件,使应用更具可移植性。
-
GOOS=linux
:由于容器运行Linux,设置此选项可实现可重复构建。
-
-ldflags="-s -w"
:去除二进制文件的调试符号,减小文件大小。
-
-tags=containers
:使用容器标签指定的文件构建应用,去除对SQLite和通知的依赖。
构建二进制文件:
$ CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -tags=containers
检查文件属性和大小:
$ ls -lh pomo
$ file pomo
对比不使用这些选项构建的应用,优化后的二进制文件几乎小了50%,且为静态链接并去除了调试符号。
创建容器镜像,先切换到根目录并创建
containers
子目录:
$ cd $HOME/pragprog.com/rggo/distributing
$ mkdir containers
在
containers
目录下创建并编辑
Dockerfile
:
FROM alpine:latest
RUN mkdir /app && adduser -h /app -D pomo
WORKDIR /app
COPY --chown=pomo /pomo/pomo .
CMD ["/app/pomo"]
使用
docker build
命令构建镜像:
$ docker build -t pomo/pomo:latest -f containers/Dockerfile .
列出镜像:
$ docker images
运行应用:
$ docker run --rm -it localhost/pomo/pomo
还可使用Docker和多阶段Dockerfile构建应用。在
containers
目录下创建
Dockerfile.builder
:
FROM golang:1.15 AS builder
RUN mkdir /distributing
WORKDIR /distributing
COPY notify/ notify/
COPY pomo/ pomo/
WORKDIR /distributing/pomo
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags= "-s -w" -tags=containers
FROM alpine:latest
RUN mkdir /app && adduser -h /app -D pomo
WORKDIR /app
COPY --chown=pomo --from=builder /distributing/pomo/pomo .
CMD ["/app/pomo"]
使用此镜像构建二进制文件和容器镜像:
$ docker build -t pomo/pomo:latest -f containers/Dockerfile.builder .
为创建仅包含应用二进制文件的微小镜像,复制
containers/Dockerfile.builder
到
containers/Dockerfile.scratch
并编辑:
FROM golang:1.15 AS builder
RUN mkdir /distributing
WORKDIR /distributing
COPY notify/ notify/
COPY pomo/ pomo/
WORKDIR /distributing/pomo
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags= "-s -w" -tags=containers
FROM scratch
WORKDIR /
COPY --from=builder /distributing/pomo/pomo .
CMD ["/pomo"]
构建镜像:
$ docker build -t pomo/pomo:latest -f containers/Dockerfile.scratch .
检查新镜像:
$ docker images
通过以上步骤,我们可以灵活地根据不同需求构建、交叉编译和容器化部署Go应用程序。
Go应用程序的条件构建、交叉编译与容器化部署
4. 总结与实践建议
在前面的内容中,我们详细介绍了Go应用程序的条件构建、交叉编译以及容器化部署的相关知识和操作步骤。下面我们对这些内容进行总结,并给出一些实践建议。
4.1 条件构建总结
-
功能可选性
:通过构建标签(如
disable_notification、containers等),我们可以让应用程序的某些功能在构建时可选,提高了应用的灵活性。 -
文件包含控制
:使用构建标签可以精确控制哪些文件会被包含在特定的构建中,例如在容器环境中禁用SQLite仓库,仅使用
inMemory仓库。 -
验证工具
:
go list命令是一个强大的工具,可以帮助我们验证特定构建中包含的文件,避免因构建标签设置错误而导致的问题。
实践建议:在开发过程中,合理规划构建标签的使用,为不同的使用场景提供清晰的构建选项。同时,定期使用
go list
命令检查构建内容,确保构建符合预期。
4.2 交叉编译总结
- 可移植性 :Go的交叉编译功能使得我们可以从单一平台为多种操作系统和架构编译二进制文件,大大提高了应用的可移植性。
-
静态链接
:通过设置
CGO_ENABLED=0,我们可以生成静态链接的二进制文件,避免在不同平台上因动态链接库缺失而导致的运行问题。 - 自动化脚本 :使用Bash脚本可以自动化交叉编译过程,为所有支持的平台和架构生成二进制文件。
实践建议:在发布应用时,利用交叉编译功能为不同平台提供二进制文件。同时,考虑使用静态链接以提高应用的兼容性。编写自动化脚本可以提高构建效率,减少人为错误。
4.3 容器化部署总结
- 隔离性和资源管理 :容器提供了隔离的运行环境,利用Linux内核资源(如Namespaces和Cgroups)进行资源管理,确保应用的稳定性和安全性。
- 镜像构建 :使用Dockerfile可以方便地创建容器镜像,多阶段Dockerfile可以进一步优化镜像大小,提高数据传输效率。
-
微小镜像
:通过使用
scratch镜像,我们可以创建仅包含应用二进制文件的微小镜像,减少不必要的依赖和安全风险。
实践建议:对于适合容器化的应用,优先考虑使用容器进行部署。在创建镜像时,使用多阶段构建和微小镜像以提高镜像的质量和安全性。
5. 相关操作流程图
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(条件构建应用程序):::process
B --> C{是否需要特定功能?}:::decision
C -->|是| D(设置构建标签):::process
C -->|否| E(默认构建):::process
D --> F(构建应用):::process
E --> F
F --> G(交叉编译应用程序):::process
G --> H{目标平台?}:::decision
H -->|Windows| I(设置相应环境变量和编译器):::process
H -->|Linux| J(设置相应环境变量):::process
H -->|macOS| K(设置相应环境变量):::process
I --> L(交叉编译):::process
J --> L
K --> L
L --> M(容器化部署应用程序):::process
M --> N(创建Dockerfile):::process
N --> O(构建容器镜像):::process
O --> P(运行容器):::process
P --> Q([结束]):::startend
6. 操作步骤表格
| 操作类型 | 操作步骤 | 示例代码 |
|---|---|---|
| 条件构建 |
1. 复制应用到新目录
2. 添加依赖和替换指令 3. 创建包含构建标签的文件 4. 调用功能函数 5. 验证构建内容 |
bash<br>$ cp -r $HOME/pragprog.com/rggo/persistentDataSQL/pomo $HOME/pragprog.com/rggo/distributing<br>$ cd $HOME/pragprog.com/rggo/distributing/pomo<br>
go<br>// distributing/pomo/go.mod<br>...<br>
go<br>// distributing/pomo/app/notification_stub.go<br>...<br>
go<br>// distributing/pomo/app/buttons.go<br>...<br>
bash<br>$ go list -f '{{ .GoFiles }}' ./...<br>
|
| 交叉编译 |
1. 查看支持的平台和架构
2. 查看当前平台信息 3. 为当前平台构建 4. 静态链接构建 5. 为特定平台交叉编译 6. 自动化交叉编译 |
bash<br>$ go tool dist list<br>
bash<br>$ go env GOOS<br>$ go env GOARCH<br>
bash<br>$ cd $HOME/pragprog.com/rggo/distributing/pomo<br>$ go build<br>
bash<br>$ CGO_ENABLED=0 go build<br>
bash<br>$ GOOS=windows GOARCH=amd64 go build -tags=inmemory<br>
bash<br># cross_build.sh<br>...<br>
|
| 容器化部署 |
1. 优化构建选项
2. 构建二进制文件 3. 创建Dockerfile 4. 构建容器镜像 5. 运行容器 6. 创建微小镜像 |
bash<br>$ CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -tags=containers<br>
bash<br>$ ls -lh pomo<br>$ file pomo<br>
dockerfile<br>// Dockerfile<br>...<br>
bash<br>$ docker build -t pomo/pomo:latest -f containers/Dockerfile .<br>
bash<br>$ docker run --rm -it localhost/pomo/pomo<br>
dockerfile<br>// Dockerfile.scratch<br>...<br>
|
通过以上总结和建议,结合流程图和操作步骤表格,我们可以更清晰地理解和应用Go应用程序的条件构建、交叉编译和容器化部署技术,为不同的使用场景提供高效、稳定的解决方案。
Go应用条件构建与容器化部署指南
超级会员免费看
35

被折叠的 条评论
为什么被折叠?



