Golang基于Gitlab CI/CD部署方案

简介:

概述

持续集成 (Continuous integration)是一种软件开发实践,即团队开发成员经常集成它们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

持续部署(continuous deployment)是通过自动化的构建、测试和部署循环来快速交付高质量的产品。某种程度上代表了一个开发团队工程化的程度,毕竟快速运转的互联网公司人力成本会高于机器,投资机器优化开发流程化相对也提高了人的效率,让 engineering productivity 最大化。

1. 环境准备

本次试验是基于Centos 7.3, docker 17.03.2-ce环境下的。docker的安装这里就不赘述了,提供了官方链接吧: Get Docker CE for CentOS

1.1. docker启动gitlab

启动命令如下:


AI 代码解读
1docker run --detach \
2--hostname gitlab.chain.cn \
3--publish 8443:443 --publish 8080:80 --publish 2222:22 \
4--name gitlab \
5--restart always \
6--volume /Users/zhangzc/gitlab/config:/etc/gitlab \
7--volume /Users/zhangzc/gitlab/logs:/var/log/gitlab \
8--volume /Users/zhangzc/gitlab/data:/var/opt/gitlab \
9gitlab/gitlab-ce

port,hostname, volume根据具体情况具体设置

1.2. docker启动gitlab-runner

启动命令如下:


AI 代码解读
1sudo docker run -d /
2--name gitlab-runner /
3--restart always /
4-v /Users/zhangzc/gitlab-runner/config:/etc/gitlab-runner /
5-v /Users/zhangzc/gitlab-runner/run/docker.sock:/var/run/docker.sock /
6gitlab/gitlab-runner:latest

volume根据具体情况具体设置

1.3. 用于集成部署的镜像制作

我们的集成和部署都需要放在一个容器里面进行,所以,需要制作一个镜像并安装一些必要的工具,用于集成和部署相关操作。目前我们的项目都是基于golang 1.9.2的,这里也就基于golang:1.9.2的镜像制定一个特定的镜像。

Dockerfile内容如下:


AI 代码解读
1# Base image: https://hub.docker.com/_/golang/
2FROM golang:1.9.2
3USER root
4# Install golint
5ENV GOPATH /go
6ENV PATH ${GOPATH}/bin:$PATH
7RUN mkdir -p /go/src/golang.org/x
8RUN mkdir -p /go/src/github.com/golang
9COPY source/golang.org /go/src/golang.org/x/
10COPY source/github.com /go/src/github.com/golang/
11RUN go install github.com/golang/lint/golint
12
13# install docker
14RUN curl -O https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz \
15 && tar zxvf docker-latest.tgz \
16 && cp docker/docker /usr/local/bin/ \
17 && rm -rf docker docker-latest.tgz
18
19# install expect
20RUN apt-get update
21RUN apt-get -y install tcl tk expect

其中golint是用于golang代码风格检查的工具。
docker是由于需要在容器里面使用宿主的docker命令,这里就需要安装一个docker的可执行文件,然后在启动容器的时候,将宿主的 /var/run/docker.sock 文件挂载到容器内的同样位置。
expect是用于ssh自动登录远程服务器的工具,这里安装改工具是为了可以实现远程服务器端部署应用

另外,在安装golint的时候,是需要去golang.org下载源码的,由于墙的关系,go get命令是执行不了的。为了处理这个问题,首先通过其他渠道先下载好相关源码,放到指定的路径下,然后copy到镜像里,并执行安装即可。

下面有段脚本是用于生成镜像的:


AI 代码解读
1#!/bin/bash
2
3echo "提取构建镜像时需要的文件"
4source_path="source"
5mkdir -p $source_path/golang.org
6mkdir -p $source_path/github.com
7cp -rf $GOPATH/src/golang.org/x/lint $source_path/golang.org/
8cp -rf $GOPATH/src/golang.org/x/tools $source_path/golang.org/
9cp -rf $GOPATH/src/github.com/golang/lint $source_path/github.com
10
11echo "构建镜像"
12docker build -t go-tools:1.9.2 .
13
14echo "删除构建镜像时需要的文件"
15rm -rf $source_path

生成镜像后,推送到镜像仓库,并在gitlab-runner的服务器上拉取该镜像

本次试验的gitlab和gitlab-runner是运行在同一服务器的docker下的。

2. runner注册及配置

2.1. 注册

环境准备好后,在服务器上执行以下命令,注册runner:

1docker exec -it gitlab-runner gitlab-ci-multi-runner register
AI 代码解读

按照提示输入相关信息


AI 代码解读
1Please enter the gitlab-ci coordinator URL:
2# gitlab的url, 如:https://gitlab.chain.cn/
3Please enter the gitlab-ci token for this runner:
4# gitlab->你的项目->settings -> CI/CD ->Runners settings
5Please enter the gitlab-ci description for this runner:
6# 示例:demo-test
7Please enter the gitlab-ci tags for this runner (comma separated):
8# 示例:demo
9Whether to run untagged builds [true/false]:
10# true
11Please enter the executor: docker, parallels, shell, kubernetes, docker-ssh, ssh, virtualbox, docker+machine, docker-ssh+machine:
12# docker
13Please enter the default Docker image (e.g. ruby:2.1):
14# go-tools:1.9.2 (之前自己制作的镜像)
342d896b9db7f424d0ecfc5756d722e978d91908

token

成功后,可以看到gitlab->你的项目->settings -> CI/CD ->Runners settings 页面下面有以下内容:

8224476b4b3f066d136c5fac1a4a103d3fa16cea

runner注册成功

2.2. 配置

注册成功之后,还需要在原有的配置上做一些特定的配置,如下:


AI 代码解读
1[[runners]]
2 name = "demo-test"
3 url = "https://gitlab.chain.cn/"
4 token = "c771fc5feb1734a9d4df4c8108cd4e"
5 executor = "docker"
6 [runners.docker]
7 tls_verify = false
8 image = "go-tools:1.9.2"
9 privileged = false
10 disable_cache = false
11 volumes = ["/var/run/docker.sock:/var/run/docker.sock"]
12 extra_hosts = ["gitlab.chain.cn:127.0.0.1"]
13 network_mode = "host"
14 pull_policy = "if-not-present"
15 shm_size = 0
16 [runners.cache]

这里先解释下gitlab-runner的流程吧,gitlab-runner在执行的时候,会根据上面的配置启动一个容器,即配置中的go-tools:1.9.2,b其中所有的启动参数都会在[runners.docker]节点下配置好,包括挂载啊,网络啊之类的。容器启动成功之后,会使用这个容器去gitlab上pull代码,然后根据自己定义的规则进行检验,全部检测成功之后便是部署了。

volumes: 是为了在容器中可以执行宿主机的docker命令。

extra_hosts: 给gitlab添加个host映射,映射到127.0.0.1

network_mode: 令容器的网络与宿主机一致,只有这样才能通过127.0.0.1访问到gitlab。

pull_policy: 当指定的镜像不存在的话,则通过docker pull拉取

3. 定义规则

在gitlab项目根目录创建.gitlab-ci.yml文件,填写runner规则,具体语法课参考官方文档:https://docs.gitlab.com/ee/ci/yaml/

3.1. go集成命令

下面介绍几个golang常见的集成命令

包列表
正如在官方文档中所描述的那样,go项目是包的集合。下面介绍的大多数工具都将使用这些包,因此我们需要的第一个命令是列出包的方法。我们可以用go list子命令来完成
1go list ./...
AI 代码解读

请注意,如果我们要避免将我们的工具应用于外部资源,并将其限制在我们的代码中。 那么我们需要去除vendor 目录,命令如下:

1go list ./... | grep -v /vendor/
AI 代码解读
单元测试
这些是您可以在代码中运行的最常见的测试。每个.go文件需要一个能支持单元测试的 _test.go 文件。可以使用以下命令运行所有包的测试:
1go test -short $(go list ./... | grep -v /vendor/)
AI 代码解读
数据竞争
这通常是一个难以逃避解决的问题,go工具默认具有(但只能在linux / amd64、freebsd / amd64、darwin / amd64和windows / amd64上使用)
1go test -race -short $(go list . /…| grep - v /vendor/)
AI 代码解读
代码覆盖
这是评估代码的质量的必备工具,并能显示哪部分代码进行了单元测试,哪部分没有。
要计算代码覆盖率,需要运行以下脚本:

AI 代码解读
1PKG_LIST=$(go list ./... | grep -v /vendor/)
2for package in ${PKG_LIST}; do
3 go test -covermode=count -coverprofile "cover/${package##*/}.cov" "$package" ;
4done
5tail -q -n +2 cover/*.cov >> cover/coverage.cov
6go tool cover -func=cover/coverage.cov

如果我们想要获得HTML格式的覆盖率报告,我们需要添加以下命令:

1go tool cover -html=cover/coverage.cov -o coverage.html
AI 代码解读
构建
最后一旦代码经过了完全测试,我们要对代码进行编译,从而构建可以执行的二进制文件。
1go build .
AI 代码解读
linter
这是我们在代码中使用的第一个工具:linter。它的作用是检查代码风格/错误。这听起来像是一个可选的工具,或者至少是一个“不错”的工具,但它确实有助于在项目上保持一致的代码风格。
linter并不是go本身的一部分,所以如果要使用,你需要手动安装它(之前的go-tools镜像我们已经安装过了)。
使用方法相当简单:只需在代码包上运行它(也可以指向. go文件):
1$ golint -set_exit_status $(go list ./... | grep -v /vendor/)
AI 代码解读

注意-set_exit_status选项。 默认情况下,golint仅输出样式问题,并带有返回值(带有0返回码),所以CI不认为是出错。 如果指定了-set_exit_status,则在遇到任何样式问题时,golint的返回码将不为0。

3.2. Makefile

如果我们不想在.gitlab-ci.yml文件中写的太复杂,那么我们可以把持续集成环境中使用的所有工具,全部打包在Makefile中,并用统一的方式调用它们。

这样的话,.gitlab-ci.yml文件就会更加简洁了。当然了,Makefile同样也可以调用*.sh脚本文件

3.3. 配置示例

3.3.1. .gitlab-ci.yml


AI 代码解读
1image: go-tools:1.9.2
2
3stages:
4 - build
5 - test
6 - deploy
7
8before_script:
9 - mkdir -p /go/src/gitlab.chain.cn/ZhangZhongcheng /go/src/_/builds
10 - cp -r $CI_PROJECT_DIR /go/src/gitlab.chain.cn/ZhangZhongcheng/demo
11 - ln -s /go/src/gitlab.chain.cn/ZhangZhongcheng /go/src/_/builds/ZhangZhongcheng
12 - cd /go/src/_/builds/ZhangZhongcheng/demo
13
14unit_tests:
15 stage: test
16 script:
17 - make test
18 tags:
19 - demo
20
21race_detector:
22 stage: test
23 script:
24 - make race
25 tags:
26 - demo
27
28code_coverage:
29 stage: test
30 script:
31 - make coverage
32 tags:
33 - demo
34
35code_coverage_report:
36 stage: test
37 script:
38 - make coverhtml
39 only:
40 - master
41 tags:
42 - demo
43
44lint_code:
45 stage: test
46 script:
47 - make lint
48
49build:
50 stage: build
51 script:
52 - pwd
53 - go build .
54 tags:
55 - demo
56
57build_image:
58 stage: deploy
59 script:
60 - make build_image
61 tags:
62 - demo

3.3.2. Makefile


AI 代码解读
1PROJECT_NAME := "demo"
2PKG := "gitlab.chain.cn/ZhangZhongcheng/$(PROJECT_NAME)"
3PKG_LIST := $(shell go list ./... | grep -v /vendor/)
4GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
5
6test: ## Run unittests
7 @go test -v ${PKG_LIST}
8
9lint: ## Lint the files
10 @golint ${PKG_LIST}
11
12race: ## Run data race detector
13 @go test -race -short ${PKG_LIST}
14
15coverage: ## Generate global code coverage report
16 ./scripts/coverage.sh;
17
18coverhtml: ## Generate global code coverage report in HTML
19 ./scripts/coverage.sh html;
20
21build_image:
22 ./scripts/buildDockerImage.sh

3.3.3. coverage.sh


AI 代码解读
1#!/bin/bash
2#
3# Code coverage generation
4
5COVERAGE_DIR="${COVERAGE_DIR:-coverage}"
6PKG_LIST=$(go list ./... | grep -v /vendor/)
7
8# Create the coverage files directory
9mkdir -p "$COVERAGE_DIR";
10
11# Create a coverage file for each package
12for package in ${PKG_LIST}; do
13 go test -covermode=count -coverprofile "${COVERAGE_DIR}/${package##*/}.cov" "$package" ;
14done ;
15
16# Merge the coverage profile files
17echo 'mode: count' > "${COVERAGE_DIR}"/coverage.cov ;
18tail -q -n +2 "${COVERAGE_DIR}"/*.cov >> "${COVERAGE_DIR}"/coverage.cov ;
19
20# Display the global code coverage
21go tool cover -func="${COVERAGE_DIR}"/coverage.cov ;
22
23# If needed, generate HTML report
24if [ "$1" == "html" ]; then
25 go tool cover -html="${COVERAGE_DIR}"/coverage.cov -o coverage.html ;
26fi
27
28# Remove the coverage files directory
29rm -rf "$COVERAGE_DIR";

3.3.4. buildDockerImage.sh


AI 代码解读
1#!/bin/bash
2
3#检测GOPATH
4echo "检测GOPATH"
5if [ -z "$GOPATH" ];then
6echo "GOPATH 未设定"
7exit 1
8else
9echo "GOPATH=$GOPATH"
10fi
11
12#初始化数据
13echo "初始化数据"
14new_version="1.0.0"
15old_version="1.0.0"
16golang_version="1.9.2"
17app_name="application"
18projust_root="demo"
19DOCKER_IMAGE_NAME="demo"
20REGISTRY_HOST="xxx.xxx.xxx.xxx:5000"
21path="/go/src/_/builds/ZhangZhongcheng/demo"
22
23
24#当前容器更换为旧标签
25echo "当前容器更换为旧标签"
26docker rmi $REGISTRY_HOST/$DOCKER_IMAGE_NAME:$old_version
27
28# 基于golang:1.9.2镜像启动的容器实例,编译本项目的二进制可执行程序
29echo "基于golang:1.9.2镜像启动的容器实例,编译本项目的二进制可执行程序"
30cd $path
31go build -o $app_name
32
33echo "检测 $app_name 应用"
34FILE="$path/$app_name"
35if [ -f "$FILE" ];then
36echo "$FILE 已就绪"
37else
38echo "$FILE 应用不存在"
39exit 1
40fi
41
42#docker构建镜像 禁止在构建上下文之外的路径 添加复制文件
43#所以在此可以用命令把需要的文件cp到 dockerfile 同目录内 ,构建完成后再用命令删除
44cd $path/scripts
45echo "提取构建时需要的文件"
46cp ../$app_name $app_name
47
48# 基于当前目录下的Dockerfile构建镜像
49echo "基于当前目录下的Dockerfile构建镜像"
50echo "docker build -t $REGISTRY_HOST/$DOCKER_IMAGE_NAME:$new_version ."
51docker build -t $REGISTRY_HOST/$DOCKER_IMAGE_NAME:$new_version .
52
53# 删除本次生成的可执行文件 以及构建所需要的文件
54echo "删除本次生成的可执行文件 以及构建所需要的文件"
55rm -rf $app_name
56rm -rf ../$app_name
57
58#查看镜像
59echo "查看镜像"
60docker images | grep $DOCKER_IMAGE_NAME
61
62#推送镜像
63echo "推送镜像"
64echo "docker push $REGISTRY_HOST/$DOCKER_IMAGE_NAME:$new_version"
65docker push $REGISTRY_HOST/$DOCKER_IMAGE_NAME:$new_version
66
67echo "auto deploy"
68./automationDeployment.sh $new_version $old_version

3.3.5. automationDeployment.sh


AI 代码解读
1#!/usr/bin/expect
2#指定shebang
3#设定超时时间为3秒
4set ip xxx.xxx.xxx.xxx
5set password "xxxxxxx"
6
7set new_version [lindex $argv 0]
8set old_version [lindex $argv 1]
9
10spawn ssh root@$ip
11expect {
12 "*yes/no" { send "yes\r"; exp_continue}
13 "*password:" { send "$password\r" }
14}
15expect "#*"
16send "cd /root/demo/\r"
17send "./docker_run_demo.sh $new_version $old_version\r"
18expect eof

3.3.6. Dockerfile


AI 代码解读
1FROM golang:1.9.2
2
3#定义环境变量 alpine专用
4#ENV TIME_ZONE Asia/Shanghai
5
6ADD application /go/src/demo/
7
8WORKDIR /go/src/demo
9
10ADD run_application.sh /root/
11RUN chmod 755 /root/run_application.sh
12CMD sh /root/run_application.sh
13
14EXPOSE 8080

3.3.7. run_application.sh


AI 代码解读
1#!/bin/bash
2
3#映射ip
4cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
5
6cd /go/src/demo/
7
8./application

4. 结果

以下为部署成功后的截图:

15303ff258cc336107ffc1e0387f775b3d574fd3

result


原文发布时间为:2018-09-9

本文作者:ChainZhang

本文来自云栖社区合作伙伴“Golang语言社区”,了解相关信息可以关注“Golang语言社区”。

目录
打赏
0
0
0
0
73530
分享
相关文章
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
2月前
|
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
Go 语言入门指南:切片
|
2月前
|
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
53 2
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
337 7
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
36 3
|
2月前
|
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
92 20
eino — 基于go语言的大模型应用开发框架(一)
Eino 是一个受开源社区优秀LLM应用开发框架(如LangChain和LlamaIndex)启发的Go语言框架,强调简洁性、可扩展性和可靠性。它提供了易于复用的组件、强大的编排框架、简洁明了的API、最佳实践集合及实用的DevOps工具,支持快速构建和部署LLM应用。Eino不仅兼容多种模型库(如OpenAI、Ollama、Ark),还提供详细的官方文档和活跃的社区支持,便于开发者上手使用。
291 8
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
Go语言实战:错误处理和panic_recover之自定义错误类型
本文深入探讨了Go语言中的错误处理和panic/recover机制,涵盖错误处理的基本概念、自定义错误类型的定义、panic和recover的工作原理及应用场景。通过具体代码示例介绍了如何定义自定义错误类型、检查和处理错误值,并使用panic和recover处理运行时错误。文章还讨论了错误处理在实际开发中的应用,如网络编程、文件操作和并发编程,并推荐了一些学习资源。最后展望了未来Go语言在错误处理方面的优化方向。