对Golang项目进行CI/CD化和容器化 (DaoCloud大法好!)
持续集成 Continuous Integration(CI)和持续交付 Continuous Delivery(CD),在当前 DevOps 的趋势下,具有支柱性地位。
软件交付管道以快速、自动化和可重复的方式从源代码生成发布版本,就类似于工厂里的装配线以快速、自动化、可重复的方式从原材料生产出消费品,完成这项工作的总体设计我们就称之为持续交付,启动装配线的过程我们称之为持续集成
CI/CD的好处:提升开发效率,加速开发周期,及时发现开发过程中的错误,简化部署和运维过程。
为什么选择 DaoCloud:一条🐲服务
本文基于 DaoCloud, 描述如何将自己的Golang项目进行 容器化 和 CI/CD 化。
注册DaoCloud
登陆 — 选择github登陆—授予权限—账户名和密码
定义项目(服务)
这里使用 gin
生成一个hello world
服务,提供一个/ping
接口,绑定在8082端口
/hello_dao_cloud/main.go
:
package main
import "github.com/gin-gonic/gin"
func HelloHandler(c *gin.Context) {
c.JSON(200, gin.H{
"msg": "hello dao cloud",
})
}
func main() {
r := gin.Default()
r.GET("/ping", HelloHandler)
r.Run(":8082")
}
使用 go.mod(推荐)
// go.mod
module hello_dao_cloud
go 1.14
require github.com/gin-gonic/gin v1.6.3
本地运行
$ go run main.go
$ curl http://localhost:8082/ping
{"msg":"hello dao cloud"}
# 也可以直接到浏览器访问
创建仓库
到github
(注册时绑定的那个)创建一个 repo
,作为代码仓库。项目代码 push 到这个仓库,CI 工具会从这里面拉取再执行启动脚本。
项目和仓库绑定
到项目目录下:
echo "# hello_dao_cloud" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@xxxxxxxxxx/hello_dao_cloud.git # 这里改成自己的项目地址
git push -u origin master
如果显示没有权限,则需要去设置 ssh
或 自己用账号密码登陆
再把所有项目代码 push上去
$ git add .
$ git commit -m "project init"
$ git push
刷新项目页面,发现项目文件已经上传到了项目路径下
(.idea
文件是 goland 的配置信息,可以在 .gitignore
里声明 .idea
,没有则无视)
创建DaoCloud项目
创建项目
项目 — 选择项目 — 项目名称 — 设置代码源(自己的仓库内找到repo)—创建项目
新创建的项目内选择:流程定义,这里会列出三个阶段:测试阶段、构建阶段、发布阶段,需要分别配置
测试阶段
如果给代码加上了单元测试,则需要配置:
- 运行脚本内填入:
go test -v
- 基础镜像填入:
golang
和1.14
- 保存
构建阶段
自定义dockerfile
DaoCloud
是基于docker
的,构建镜像后将自动发布到镜像仓库。
所以需要把自己的项目构建为 docker
镜像,需要创建Dockerfile
文件:(注意首字母大写)
# 基础镜像
FROM golang:latest as builder
# go mod
ENV GO111MODULE on
ENV CGO_ENABLED=0
ENV GOPROXY https://goproxy.io
# 工作目录
WORKDIR /app
COPY . .
# 这里将Golang依赖定义相关文件的copy放到最前面
COPY go.mod go.sum ./
RUN go mod download
# 编译 (注意 hello_dao_cloud 在 docker 容器里编译,并没有在宿主机现场编译)
RUN go build -o main .
FROM alpine:latest
WORKDIR /app/
# alpine镜像没有ca-certificates,需要进行安装
RUN apk update && apk --no-cache add ca-certificates
# 从builder stage的镜像里将二进制文件copy过来
COPY --from=builder /app/main .
# 声明运行时容器提供服务端口
EXPOSE 8082
CMD ["./main"]
- 其中涉及到镜像大小的优化,参考:CI构建环境下的docker build最佳实践
CGO_ENABLED=0
是解决编译过程中出现的standard_init_linux.go:211: exec user process caused "no such file or directory"
,如果需要调用C代码,可以参考:使用alpinelinux 构建 golang http 启动
镜像内运行
本地构建镜像来验证
# 构建镜像:
$ docker build -t hello_dao_cloud .
# 验证镜像:
$ docker images
hello_dao_cloud latest 4f43350f6377 About a minute ago 22.8MB
# 创建并运行一个新容器:
$ docker run -p 8082:8082 hello_dao_cloud
$ curl http://localhost:8082/ping
{"msg":"hello dao cloud"}
依然,需要push
到仓库里面
$ git add .
$ git commit -m "Dockerfile"
$ git push
DaoCloud构建阶段
dockerfile
创建完后,到 DaoCloud 控制台的 构建阶段
默认构建任务 > 使用本地dockerfile > 保存
这时可以到执行记录里面发现正在进行编译了
构建日志:
clone 仓库的代码 然后执行build
流程,对应dockerfile内到每一行命令
docker build过程完成后,会将镜像 Push 到 DaoClouod 的公共仓库内
发布阶段
这一步构建完成后,可以到自己的机器上 docker pull
然后 docker run
,显然这是不够自动化的
绑定主机
集群管理 > 导入主机 > 选择对应操作系统
这时候到自己的机器上运行这个脚本(前提是机器上已经安装了docker)
$ curl -sSL https://get.daocloud.io/daomonit/install.sh | sh -s bxxxxxxxxxxxxxxxxxx6
运行完成后,等待连接… 会变为 恭喜接入成功
机器管理界面以可视化的方式展示了机器上的docker的容器(docker ps
)、镜像(docker image
)和网络等信息。
项目发布
测试和构建其实相当于 CI 阶段,发布阶段即为 CD
创建应用
应用 > 创建应用 > 应用名称、选择主机 > 下一步
应用设置界面就相当于配置 docker run
的参数,设置端口映射、存储映射和环境变量等
立即部署后,可以看到日志显示部署完成:
2020-05-06 12:08:16:[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
2020-05-06 12:08:16: - using env: export GIN_MODE=release
2020-05-06 12:08:16: - using code: gin.SetMode(gin.ReleaseMode)
2020-05-06 12:08:16:
2020-05-06 12:08:16:[GIN-debug] GET /ping --> main.HelloHandler (3 handlers)
2020-05-06 12:08:16:[GIN-debug] Listening and serving HTTP on :8082
运行(机器内的docker)
浏览器或 postman
测试一下接口(使用自己机器的IP,阿里云或腾讯云的安全组内要设置8082端口对外):
$ curl http://yourhost:8082/ping
{"msg":"hello dao cloud"}
# 也可以直接到浏览器访问
云隧道
如果自己的机器未接入公网(自己的电脑),DaoCloud也提供了内网穿透功能
应用 > 云隧道 > 创建
隧道生效后,会给出云地址,访问这个地址,即可以成功访问
回到项目本身
项目 > 流程定义 > 发布阶段 > 添加并行任务 > 发布 > 发布到自有主机 > 选择应用名称 > 创建任务
这样一来,整个CI/CD
流程部署完成,
持续集成
这样整个持续集成流程就搭建起来啦!
简述一个交付的过程:更改项目—push到仓库—自动执行CI/CD流程—查看结果
这样一来我们的注意力会集中到业务开发上,开发完成推到仓库,后续过程就不用管啦!
比如我们再加一个接口:
func ByeHandler(c *gin.Context) {
name := c.Query("name")
if name == "" {
name = "friend"
}
c.JSON(200, gin.H{
"msg": "Bye " + name + " !",
})
}
// 绑定到 /bye 接口下
r.GET("/bye", ByeHandler)
初次发布耗时较长,后面的发布,由于缓存了部分依赖镜像,速度会提升
运行
$ curl http://yourhost:8082/bye?name=Aris
{"msg":"Bye Aris !"}
邮件提醒
相应的,每次交付,都会收到一封邮件(CI/CD任何一环出现错误,邮件内也会声明出来)
总结
借助DaoCloud这个CI/CD产品,进行开发的持续交付,实现自动化的交付流程。
这个过程用时序图描述为:
末尾再回顾一下整个部署过程:
- 使用Github注册DaoCLoud
- 开发自己的项目
- 配置好DockerFile
- 发布到仓库
- 创建DaoCloud项目
- 选择仓库,创建项目
- 定义测试阶段
- 定义构建阶段,选择DockerFIle,查看构建结果
- 定义发布阶段
- 绑定主机
- 创建应用
- 添加发布任务到主机
- 进行持续集成