今天我们来聊聊Go语言项目如何打包和部署。无论你是初学者还是资深开发者,了解如何将你的代码打包成可执行文件,并在不同环境下部署运行,都是一项非常重要的技能。
打包相关命令
Go语言提供了一些基本命令来帮助我们编译和打包程序。
基本命令
命令 | 含义 |
---|---|
go run | 编译并马上运行 go 程序(只接收 main 包下的文件作为参数) |
go build | 编译指定的源文件、软件包及其依赖项,但它不会运行编译后的二进制文件。(如果想要指定所生成的二进制文件为其他名称,则可以通过 -o 参数进行调整) |
go install | 编译并安装源文件、软件包到 $GOBIN 目录下。 可以执行 go install -x 查看它的编译过程。文件名称为 Go modules 的项目名,而不是目录名 |
编译参数
参数 | 含义 |
---|---|
-x |
打印编译过程中的所有执行命令 |
-n |
打印编译过程中的所有执行命令,但不执行生成的二进制文件 |
-a |
强制重新编译所有涉及的依赖 |
-o |
指定生成的二进制文件名称 |
-p |
指定编译过程中可以并发运行程序的数量 |
-work |
打印临时工作目录的完整路径,在退出时不删除该目录 |
-race |
启用数据竞争检测 |
-installsuffix |
在软件包安装的目录中增加后缀标识,以保持输出与默认版本分开 |
跨平台交叉编译
Go语言支持跨平台交叉编译,允许我们在一个平台上编写代码,编译出在另一个平台上运行的程序。
编译Linux平台amd64架构的可执行文件
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o <application-name>
# 比如
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o <application-name> .
环境变量
变量名 | 含义 |
---|---|
CGO_ENABLED |
是否在Go代码中调用C代码 |
GOOS |
目标操作系统 |
GOARCH |
目标操作系统的架构 |
第三方打包工具
Go语言还有一些第三方工具,可以帮助我们将非.go
文件(如静态文件、模板文件等)打包到二进制文件中。
使用go-bindata将数据文件转换成Go代码
- 安装
go-bindata
:
go get -u github.com/go-bindata/go-bindata/...
- 将配置文件生成Go代码:
go-bindata -o configs/config.go -pkg configs configs/config.yaml
- 读取文件中的配置信息:
data, err := configs.Asset("configs/config.yaml")
if err == nil {
fmt.Println(string(data))
}
编译缓存
查看和清理编译缓存:
go env GOCACHE
go clean -cache
压缩编译后的二进制文件
去除调试信息和符号表信息
go build -ldflags="-w -s"
使用upx工具压缩
编译信息写入
使用-ldflags
设置编译信息:
go build -o app-service -ldflags \
"-X main.buildTime=`date +%Y-%m-%d,%H:%M:%S` -X main.buildVersion=1.0.0 -X 'main.goVersion=$(go version)' -X main.gitCommitID=`git rev-parse HEAD`"
在上述命令中,通过
-ldflags
命令的-X
参数可以在链接时将信息写入变量中,其格式为:package_name.variable_name=value
- 查看编译后的二进制文件和版本信息
./app-service -version
# output is:
# Build Time: 2022-03-27,13:50:26
# Build Version: 1.0.0
# Build Go Version: go version go1.16.3 darwin/amd64
# Build Git Commit Hash ID: b3473e9cc98148f5c94b53c1cada7de133143462
部署
使用supervisor部署
- 创建supervisor配置文件
alex-blog.conf
。 - 使用
supervisorctl
管理服务。
创建 supervisor 相关配置信息
vim /etc/supervisor/conf.d/alex-blog.conf
# 程序名称,stop start 等管理时使用
[program:alex-blog]
# 进入该目录运行命令,确保了和项目相关的一些配置文件可以得到加载,比如 .env 文件
directory=/data/www/blog
# 以绝对路径的方式执行 alex-blog 二进制文件
command=/data/www/blog/alex-blog
# 重启时发送的信号,确保端口正常关闭
stopsignal=TERM
# 是否自启动
autostart=true
# 是否自动重启
autorestart=true
# 执行程序的用户
user=www-data
# 输出日志位置
stdout_logfile=/data/log/supervisor/blog/stdout.log
# 错误输出日志
stderr_logfile=/data/log/supervisor/blog/stderr.log
重载 Supervisor 配置文件
# 重载 supervisor 配置文件
supervisorctl reload
# 查看程序名称为 alex-blog 的程序状态
# 如果直接执行 `supervisorctl status` 命令的话,则是查看所有任务的状态
supervisorctl status alex-blog
# 关闭所有任务
supervisorctl shutdown
# 启动任务
supervisorctl start <程序名>
# 关闭任务
supervisorctl stop <程序名>
使用docker部署
- 编写
Dockerfile
并构建镜像。 - 运行容器。
先要在宿主机中项目根目录下进行编译
# 交叉编译生成 Linux 平台的可执行文件
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o hello-world
编写 Dockerfile 文件
# 基础镜像
FROM alpine:3.12
# 或者使用 Scratch 镜像
# FROM scratch
# 维护者
MAINTAINER alex
# docker build 时执行命令
RUN mkdir -p /go-project/demo \
&& ln -sf /dev/stdout /go-project/demo/storage/service.log
# 工作目录
WORKDIR /go-project/demo
# 拷贝
COPY hello-world /go-project/demo/hello-world
# 或者直接将当前目录下所有的文件拷贝到容器中
# COPY . /go-project/demo
# 这里暴露端口与否都行
# EXPOSE 8501
# docker run 时执行的命令
ENTRYPOINT ["./hello-world"]
Nginx配置
配置Nginx作为反向代理,服务静态资源和处理请求转发。
upstream go-project {
# go-project HTTP Server 的 IP 及 端口
server 127.0.0.1:9501;
}
server
{
listen 80;
server_name goblog.com;
access_log /data/log/nginx/goblog/access.log;
error_log /data/log/nginx/goblog/error.log;
location /static/ {
alias /www/wwwroot/gitlab/go-project/public/uploads/; #静态资源路径
}
location / {
# 将客户端的 Host 和 IP 信息一并转发到对应节点
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 转发Cookie,设置 SameSite
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
# 执行代理访问真实服务器
proxy_pass http://go-project;
}
}
总结
通过今天的学习,我们了解了Go语言项目的打包和部署流程,包括使用基本命令进行编译、跨平台编译、使用第三方工具打包静态文件、编译缓存、压缩二进制文件以及使用supervisor和docker进行部署。