之前我司所执行的CI方案,其中对于业务来说是不开放Dockerfile的,由系统生成对应的Dcokerfile,业务唯一需要关心的只有build.sh文件。有优势(简单、高效、且安全)也有劣势(不灵活、无法多阶段构建、构建环境前依赖Jenkins机器环境),所以此处也做一个支持自定义的Dockerfile的方案。
凡事都有两面性,各有优势,各位可执行选择哈 
建议业务代码仓库存在CI的三个 标准文件: Dockerfile、Makefile、build.sh 
1 2 3 4 5 6 ├── Dockerfile  // 业务代码基于此构建出响应的容器镜像 ├── Makefile    // make命令执行,多命令入口组合;可结合docker 实现编译(分平台)、打包、发布等过程阶段 ├── build.sh    // 与业务本身无关,CI执行入口,含特定CI过程,也可满足复杂CI场景需求,如:构建制品处理,前端文件Oss上传等 ├── src └── ... 
Dockerfile 是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile 从 FROM 命令开始,紧接着跟随者各种方法,命令和参数。其产出为一个新的可以用于创建容器的镜像。
自定义的Dockerfile,可以支持多阶段构建,可以避免依赖构建的基础环境(比如:我们使用的Jenkins打包机)。
**备注:**在应用CI集成过程中,不建议在Docker镜像中构建,一是新起构建容器有一定资源开销;二是整个构建过程相关依赖缓存无法使用;三是获取对应构建产出的制品文件不方便,总体来说 效率不高  。但是在开源或者演示项目,可以采用这种方式。
2022-11-24补充: 
关于下文提供构建缓存问题,已经找到解决方案,同样以Go项目编译为示例说明:
1 2 3 4 5 6 7 // docker新增一层生成依赖的镜像,以go为例,优先处理go.mod go.sum,达到层级共用 参考:https://evilmartians.com/chronicles/speeding-up-go-modules-for-docker-and-ci FROM golang:1.18.8 COPY go.mod go.sum /opt/build/ RUN go mod download          // go.mod go.sum   这里COPY的文件有变化的话,docker build会重新生成镜像 
② 【推荐】  
 
备注:buildkit is available from docker 18.09,同时需要在 docker build 命令前加入 DOCKER_BUILDKIT=1 启用buildKit特性
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 #  参考:https://yeasy.gitbook.io/docker_practice/buildx/buildkit #  官网参考:https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md #  docker compose示例参考:https://github.com/docker/compose-cli/blob/main/Dockerfile #  示例 #  =================================================================================== #  syntax=docker/dockerfile:1.2 FROM golang:1.18.8 as build #  设置go 的相关环境变量 ENV GO111MODULE=on \     GOPROXY=https://goproxy.cn,direct \     GOPRIVATE=gitlab.2345.cn  COPY go.mod go.sum /opt/build/ WORKDIR /opt/build RUN --mount=type=cache,target=/go/pkg/mod \     go mod download ARG LDFLAGS="-s -w" COPY . . RUN --mount=type=cache,target=/go/pkg/mod \     --mount=type=cache,target=/root/.cache/go-build,id=jszx-yfzt_go-build-cache \     GOOS=linux go build -ldflags "${LDFLAGS}" -trimpath -o ./bin/yfzt ./cmd/web/main.go 
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 FROM  golang:1.16 .15  as buildARG  LDFLAGS="-s -w" ENV  GOPROXY=https://goproxy.cn,direct \    GOPRIVATE=gitlab.xxx.cn WORKDIR  /opt/build COPY  . . RUN  GOOS=linux go build -ldflags "${LDFLAGS} "  -trimpath -o ./bin/demo ./cmd/web/main.go FROM  golang:1.16 .15  as prodWORKDIR  /opt/case /cloud COPY  --from=build /opt/build/bin/. . COPY  --from=build /opt/build/conf . COPY  --from=build /opt/build/image-syncer . COPY  --from=build /opt/build/resource . RUN  chown 2000:2000 -R /opt/case  EXPOSE  8089 CMD  ["./demo" ] FROM  scratch AS export-artifactsCOPY  --from=build /opt/build/bin/. . 
make命令是GNU的工程化编译工具,用于编译众多相互关联的源代码文件,以实现工程化的管理,提高开发效率,make 执行时会在当前目录下寻找 Makefile 或 makefile 文件。如果存在相应的文件,它就会依据其中定义好的规则完成构建任务。Makefile主要出现在c/c++的项目居多,用来管理c/c++的编译依赖。实际上 Makefile 内都是你根据 make 语法规则,自己编写的特定 Shell 命令。
它原生支持多入口子命令执行,是一个系统变量或者命令的集合。在容器化应用中引用dockerfile,或者读取默认的dockerfile,来完成自动按照某个流程 build的目的。
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 NOW = $(shell  date -u '+%Y%m%d%I%M%S')  GOVET = go tool vet -composites=false -methods=false -structtags=false GOFMT ?= gofmt "-s"  GOFILES := $(shell  find . -name "*.go" -type f -not -path "./vendor/*")  LDFLAGS += -s -w LDFLAGS += -X "demo/pkg/version.gitBranch=$(shell git symbolic-ref --short -q HEAD)"  LDFLAGS += -X "demo/pkg/version.gitCommit=$(shell git rev-parse HEAD)"  LDFLAGS += -X "demo/pkg/version.goVersion=$(shell go version)"  LDFLAGS += -X "demo/pkg/version.buildTime=$(shell date " +%Y-%m-%d %T %Z")"  IMAGE_NAME = harbor.xxx.cn/defalut_project/default_name IMAGE_TAG = 0.1-demo ARTIFACT_OUTPUT = output_scm export  GOPROXY=https://goproxy.io.PHONY : clean build linux macos windows docker docker-push export-artifactsall: clean linux clean: 	rm -rf demo fmt: 	@$(GOFMT)  -w $(GOFILES)  build: 	go build -ldflags '$(LDFLAGS) ' -trimpath -o demo cmd/web/main.go linux:      	@ 	GOOS=linux go build -ldflags '$(LDFLAGS) ' -trimpath -o ./bin/demo cmd/web/main.go macos: 	GOOS=darwin go build -ldflags '$(LDFLAGS) ' -trimpath -o ./bin/demo cmd/web/main.go windows: 	GOOS=windows go build -ldflags '$(LDFLAGS) ' -trimpath -o ./bin/demo cmd/web/main.go docker: 	docker build --build-arg LDFLAGS='$(LDFLAGS) ' --target prod -t $(IMAGE_NAME) :$(IMAGE_TAG)  -f ./Dockerfile . docker-push: docker 	docker push $(IMAGE_NAME) :$(IMAGE_TAG)  	docker rmi $(IMAGE_NAME) :$(IMAGE_TAG)  export-artifacts: 	DOCKER_BUILDKIT=1  docker build --target export-artifacts --output ${ARTIFACT_OUTPUT} -f ./Dockerfile . 
满足相对复杂的构建场景需求,作为CI(本文使用工具为:Jenkins)的执行的唯一入口。一般来说涵盖的执行过程大体和应用本身关联不大,主要是业务或者特殊场景处理情况,比如:构建制品的处理,前端文件Oss上传等
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 #!/bin/bash APP_CODE="$1 "  BUILD_ENV="$2 "  IS_DOCKER="$3 "  APP_PKG_NAME="$4 "  IMAGE_NAME="$5 "  IMAGE_TAG="$6 "  BASE_DIR=$(cd  "$(dirname "$0 " ) " ;pwd ) OUTPUT_DIR="${BASE_DIR} /output_scm"  function  check 	if  [ -z ${APP_PKG_NAME}  ]; then  		_showError '参数错误!请检查是否传入 APP_PKG_NAME 参数!'  	fi  	if  [[ ! -z ${IS_DOCKER}  && "${IS_DOCKER} "  -eq "1"  ]]; then  		if  [[ -z ${IMAGE_NAME}  || -z ${IMAGE_TAG}  ]]; then  			_showError '参数错误!容器化应用需传入 IMAGE_NAME 和 IMAGE_TAG 参数!'  		fi  	fi  } function  build      	 	 	     echo  "make docker-push IMAGE_NAME=${IMAGE_NAME}  IMAGE_TAG=${IMAGE_TAG} "  	make docker-push "IMAGE_NAME=${IMAGE_NAME} "  "IMAGE_TAG=${IMAGE_TAG} "  	 	if  [ $? != 0 ]; then  		_showError '构建错误!请检查控制台日志!'  	fi  } function  package 	[ -d "${BASE_DIR} /output_scm"  ] && rm -rf "${BASE_DIR} /output_scm"  	mkdir -p ${BASE_DIR} /output_scm/${APP_PKG_NAME}  	                          	 	make export-artifacts "ARTIFACT_OUTPUT=${BASE_DIR} /output_scm/${APP_PKG_NAME} "           cp -r "${BASE_DIR} /conf"  "${BASE_DIR} /output_scm/${APP_PKG_NAME} "      cp -r "${BASE_DIR} /image-syncer"  "${BASE_DIR} /output_scm/${APP_PKG_NAME} "      cp -r "${BASE_DIR} /resource"  "${BASE_DIR} /output_scm/${APP_PKG_NAME} "  	cd  "${BASE_DIR} /output_scm"  	tar -zcf "${BASE_DIR} /output_scm/${APP_PKG_NAME} .tar.gz"  "${APP_PKG_NAME} "  	md5sum "${BASE_DIR} /output_scm/${APP_PKG_NAME} .tar.gz"  >"${BASE_DIR} /output_scm/${APP_PKG_NAME} .tar.gz.md5"  } function  _showError 	echo  ''  	echo  '################################################'  	echo  " $1  "  	echo  '################################################'  	echo  ''  	exit  1 } check build