文章

go项目构建文件梳理

作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.gitlab-ci.yml
    └─> 调用模板中的构建脚本 可能调用 make  build.sh
    
Makefile  // 提供统一的构建命令入口
    ├─> build.sh (make build)
    └─> ciscripts/build.sh (make package)
    
pack.sh  // 完成打包、镜像推送
    ├─> 在容器中编译
    └─> Dockerfile (构建镜像)
    
build.sh
    └─> 生成 dist/ 目录
    
Dockerfile // 生产镜像定义
    └─> 依赖 dist/ 目录(需要先运行 build.sh)
脚本编译环境输出用途
build.sh本地环境dist/ 目录本地开发、快速构建
pack.shDocker 容器Docker 镜像完整打包、镜像推送
Makefile本地环境多种(根据命令)统一命令入口、便捷操作

.gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
include:
  - project: 'ops/gitlab-cicd-templates'
    ref: master
    file: 'go/cicd-template/prepare.yaml'
  - project: 'ops/gitlab-cicd-templates'
    ref: master
    file: 'go/cicd-template/test.yaml'
  - project: 'ops/gitlab-cicd-templates'
    ref: master
    file: 'go/cicd-template/build.yaml'

# stage定义
stages:
  - test
  - build

# 仅作单元测试(不包含资源层测试逻辑)
test:
  stage: test
  extends:
    - .test

#.gitlab-ci.yml
# └─> extends: .build-test-image (来自模板)
#     └─> 模板中的 script (可能在模板中调用 make 或 build.sh)
#         └─> 最终执行构建
# fixbug、feature、test分支的镜像打包
build-test-image:
  stage: build
  extends:
    - .build-test-image

# tag触发(仅master分支但可配置)的镜像打包
build-prod-image:
  stage: build

  extends:
    - .build-prod-image


Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 定义常量
PACKAGE := gitlab..com/golib/xdspec

BRANCH := $(shell git branch --no-color 2> /dev/null|sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' -e 's/\//\_/g')
IMAGE_NAME_ZJK := "registry.cn-zhangjiakou.aliyuncs.com//ecrobot-goods_center:${BRANCH}"

# 版本信息
VERSION=$(git describe --tags 2>/dev/null)
COMMIT=$(git rev-parse --short HEAD)
BUILD_TIME=$(date +%FT%T)

# 构建配置
GOOS ?= linux
GOARCH ?= amd64
CGO_ENABLED ?= 0
GO_BUILD_FLAGS ?= -mod=readonly
LD_FLAGS := -w -s \
    -X ${PACKAGE}.Version=${VERSION} \
    -X ${PACKAGE}.Commit=${COMMIT} \
    -X ${PACKAGE}.BuildDate=${BUILD_TIME}

# 定义构建目标(替换为你的可执行文件名)
BINARY_NAME := goods_center

.PHONY: all test build clean

all: test build

# 项目构建 - 统一的依赖同步
.deps:
	@echo "Syncing dependencies..."
	@go mod tidy
	@go mod download

# 测试命令
test:
	@echo "Running unit tests..."
	@go test -gcflags="all=-N -l" -tags=unit -timeout 30s -short `go list ./... | grep -v /vendor/`

# 构建命令
build:
	./build.sh

# goc构建命令
buildc:
	@echo "Syncing dependencies..."
	@go mod tidy
	@go mod download
	@mkdir -p ./target
	@mkdir -p ./target-goc
	@echo "Building ${BINARY_NAME} ${GIT_TAG} for $(GOOS)/$(GOARCH)"
	GOWORK=off GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) \
	goc build --debug --agentport ":9101" --center "http://goc-server-service:8080"  --output "./target-goc/${BINARY_NAME}" .

#本地打包命令
package: clean ## push image manually
	@echo "make push"
	./build.sh
	@docker build . -t $(IMAGE_NAME_ZJK)
	@docker push $(IMAGE_NAME_ZJK)


# 清理构建文件
clean:
	@echo "Cleaning up..."
	@rm -rf ./target
	@rm -rf ./target-goc

# 显示版本信息
version:
	@echo "Version: ${VERSION}"
	@echo "Commit: ${COMMIT}"
	@echo "Build Time: ${BUILD_TIME}"

# 安装依赖
deps:
	@echo "Downloading dependencies..."
	@go mod download

# 运行静态检查
lint:
	@echo "Running static analysis..."
	@golangci-lint run ./...


help: ## Display this help screen
	@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

pack.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/bin/bash

# ============================================================================
# Docker 镜像打包脚本
# 功能:在 Docker 容器中编译 Go 程序,构建镜像并推送到镜像仓库
# ============================================================================

# 服务名称(用于镜像标签)
SERVER_NAME=goods_center

# 服务组名(用于镜像标签)
SERVER_GROUP=ecrobot

# 构建用的基础镜像(包含 Go 1.13 和 Pulsar 2.41 环境)
SRC_IMG=registry.cn-zhangjiakou.aliyuncs.com//golang:1.13-pulsar2.41

# 获取 Git 版本信息
# git describe --tags: 获取最新的 tag 信息,如果失败则忽略错误(2>/dev/null)
VERSION=$(git describe --tags 2>/dev/null)

# 获取 Git commit 的短哈希值(7位)
COMMIT=$(git rev-parse --short HEAD)

# 获取当前时间(ISO 8601 格式:YYYY-MM-DDTHH:MM:SS)
TIME=$(date +%FT%T)

# 如果没有 tag,则使用 commit hash 作为版本号
if [[ -z ${VERSION} ]]; then
    VERSION=${COMMIT}
fi

# Docker 构建容器的名称
BUILD_IMG_NAME=go_build

# 停止并删除旧的构建容器(如果存在)
# || echo: 如果命令失败(容器不存在),则输出提示信息并继续执行
docker stop $BUILD_IMG_NAME || echo "skip stop $BUILD_IMG_NAME"
docker rm -f $BUILD_IMG_NAME || echo "skip stop $BUILD_IMG_NAME"

echo "pack ..."

echo "build ..."

# 在 Docker 容器中编译 Go 程序
# -v $(pwd):/go: 将当前目录挂载到容器的 /go 目录
# -w /go: 设置容器的工作目录为 /go
# --name $BUILD_IMG_NAME: 给容器命名为 go_build
# ${SRC_IMG}: 使用指定的基础镜像
# /bin/bash -c "命令": 在容器中执行命令
#   1. export GO111MODULE=on: 启用 Go modules
#   2. pwd && ls: 显示当前目录和文件列表(调试用)
#   3. env GOOS=linux GOARCH=amd64 go build: 交叉编译为 Linux amd64 架构
#   4. 如果成功:输出 "---build success---" 并退出码 0
#   5. 如果失败:输出 "---build failed---" 并退出码 1
docker run -v $(pwd):/go \
  -w /go \
  --name $BUILD_IMG_NAME \
  ${SRC_IMG} \
  /bin/bash -c "export GO111MODULE=on && pwd && ls && env GOOS=linux GOARCH=amd64 go build && echo \"---build success---\" && exit 0 || echo \"---build failed---\" && exit 1"

# 获取上一条命令的退出状态码($? 表示上一个命令的返回值)
docker_build_state=$?

# 检查编译是否成功(退出码非 0 表示失败)
if [[ ${docker_build_state} -ne 0 ]]; then
  echo "build failed"
  exit 1
fi

# 生成镜像标签:格式为 ecrobot-goods_center:版本号
TAG=${SERVER_GROUP}-${SERVER_NAME}:${VERSION}

# 删除旧的镜像(如果存在),避免标签冲突
# --force: 强制删除,即使有容器在使用
docker rmi ${TAG} --force || echo "skip docker rmi ${TAG}"

# 使用 Dockerfile 构建 Docker 镜像
# -f ./Dockerfile: 指定 Dockerfile 文件路径
# -t ${TAG}: 给镜像打标签
# ./: 构建上下文为当前目录
docker build -f ./Dockerfile -t ${TAG} ./

# 获取镜像构建的退出状态码
docker_build_state=$?

# 检查镜像构建是否成功
if [[ ${docker_build_state} -ne 0 ]]; then
  echo "pack failed"
  exit 1
fi

echo "build done"

echo "push to image hub"

# 给镜像打上远程仓库的标签
# 格式:registry.cn-zhangjiakou.aliyuncs.com//ecrobot-goods_center:版本号
docker tag ${TAG} registry.cn-zhangjiakou.aliyuncs.com//${TAG}

# 推送镜像到阿里云镜像仓库
# 注意:需要先执行 docker login 登录到镜像仓库
docker push registry.cn-zhangjiakou.aliyuncs.com//${TAG}

# 获取推送操作的退出状态码
docker_build_state=$?

# 检查推送是否成功
if [[ ${docker_build_state} -ne 0 ]]; then
  echo "push to registry failed"
  exit 1
fi

echo "push to image done"

echo "pack done"


执行流程总结

1
2
3
4
5
6
7
8
9
10
1. 定义变量(服务名、镜像名等)
2. 获取版本信息(git tag/commit)
3. 清理旧的构建容器
4. 在 Docker 容器中编译 Go 程序(Linux/amd64)
5. 检查编译是否成功
6. 使用 Dockerfile 构建镜像
7. 检查镜像构建是否成功
8. 给镜像打远程仓库标签
9. 推送镜像到阿里云仓库
10. 检查推送是否成功

build.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash
set -ex

VERSION=$(git describe --tags 2>/dev/null)
COMMIT=$(git rev-parse --short HEAD)
TIME=$(date +%FT%T)
if [ -z $VERSION ]; then
    VERSION=$COMMIT
fi

VerPkg="gitlab..com/golib/xd_sdk/xdspec"
BASE=$(pwd)

go_tags="nomsgpack"

export CGO_ENABLED=0
go build -tags "${go_tags}" \
    -ldflags "-X ${VerPkg}.Version=$VERSION \
    -X ${VerPkg}.GitCommit=$COMMIT \
    -X ${VerPkg}.BuildTime=${TIME}" \
    -o ./dist/goods_center
cd component/cid/center_worker && go build  -o ${BASE}/dist/center_worker && cd ${BASE}
cd component/cid/sync_worker && go build  -o ${BASE}/dist/sync_worker && cd ${BASE}
cd component/cid/filter_worker && go build  -o ${BASE}/dist/filter_worker && cd ${BASE}
cd component/notify/notify_worker && go build  -o ${BASE}/dist/notify_worker && cd ${BASE}
cd component/notify-filter && go build  -o ${BASE}/dist/notify-filter && cd ${BASE}
cd component/repair/repair_worker && go build  -o ${BASE}/dist/repair_worker && cd ${BASE}
cd component/platform_implement/taobao && go build  -o ${BASE}/dist/tmc_worker && cd ${BASE}
cd component/platform_implement/general && go build  -o ${BASE}/dist/gmc_worker && cd ${BASE}

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
# 使用 Alpine Linux 3.15.0 作为基础镜像(轻量级,体积小)
FROM registry.cn-zhangjiakou.aliyuncs.com//alpine:3.15.0

# 设置工作目录为 /app(后续命令都在此目录执行)
WORKDIR /app

# 将本地 dist 目录下的所有文件复制到容器的 /app 目录
# 注意:需要先运行 build.sh 生成 dist 目录和编译好的二进制文件
COPY dist /app

# 设置容器启动时执行的默认命令
# 运行 goods_center 程序,并指定配置文件路径为 /etc/config/goods_center.toml
CMD ["./goods_center", "-c", "/etc/config/goods_center.toml"]

© 2024- lfj