gitlab,我相信大家一定不会陌生。现在大部分公司的代码托管已经从svn迁移到git上了。而gitlab又是使用最多的git项目托管平台。其实gitlab不仅仅只是用来做代码托管,他自带的CI/CD在持续集成,持续交付,持续部署方面也都有不俗的能力,甚至还可以作为docker镜像仓库来存储自己构建的镜像。下面我们就来一步一步的看下,怎么使用他。
这里我们以springboot项目为例
在我们项目中经常会这样的问题,测试人员提出一个bug,开发人员修改bug,修改完了,要重新提交代码,编译打包,发布部署服务。这一整套下来,好多行命令,好几分钟过去了,即使你写个shell脚本,那也要手动去触发执行。如果系统少还好,如果你有几十个系统,那运维人员或者测试人员一整天别的事都不要干了。
那能不能有一个套工具或软件,能够每次我push代码或merge代码的时候自动触发构建,编译,打包,部署呢?当然目前jekenis自动打包构建很流行,但是,本文我们只讨论gitlab,那我们就来看看,gitlab怎么去帮我们完成这一系列的事情的。
因为我们项目中代码托管的是在gitlab,正好gitlab-runner为我们提供了持续集成,的功能可以和gitlab无缝对接。
gitlab-runner能为我们带来什么功能
好,下面我们就来一步一步的搭建我们的服务器。
首先准备最少三台虚拟机:(目前系统都为centos7 64位系统)
1、192.168.136.132(gitlab服务器)
2、192.168.136.131(gitlab-runner服务器)
3、192.168.136.130(应用服务器)
这里gitlab与gitlab-runner最好分开
应用服务器是可以任意多少台
gitlab服务我们是有docker安装
gitlab-runner使用yum安装(也可以使用docker安装,但是最后运行时遇到远程免密码执行脚本问题,暂时还没有找到解决方案)
我们这里使用docker安装gitlab,一键安装,非常快捷。
安装Gitlab:
docker run \
-itd \
-e TZ="Asia/Shanghai" \
-p 80:80 \
-p 9922:22 \
-p 4567:4567 \
-v /opt/gitlab/etc:/etc/gitlab \
-v /opt/gitlab/log:/var/log/gitlab \
-v /opt/gitlab/opt:/var/opt/gitlab \
--restart always \
--privileged=true \
--name gitlab \
gitlab/gitlab-ce
1、gitlab访问使用80端口,把宿主机的80端口映射到gitlab容器中
2、避免与宿主机的22端口冲突,这里把宿主机的9922端口映射到容器中
3、4567端口是我们使用gitlab来作为我们的镜像仓库,如果你使用其他做经常仓库,那这个端口可以不要映射
4、三条映射目录是把容器里面的配置文件,gitlab存储数据,日志等信息映射到宿主机上。以防止容器重启或销毁后数据丢失
5、后面就是制定随docker一起启动,获取宿主的root权限,容器名称,镜像名称
好,这样gitlab就安装好了。下面我们来修改下配置文件。因为我们已经把gitlab的配置文件目录映射到宿主机了,那么就直接在宿主机上修改
vi /opt/gitlab/etc/gitlab.rb
直接在这个文件里面第一行添加下面三行配置,按照你的实际ip端口号配置:
#gitlab页面访问地址
external_url 'http://192.168.136.132:80'
#gitlab ssh访问ip
gitlab_rails['gitlab_ssh_host'] = '192.168.136.132'
# gitlab ssh访问的端口
gitlab_rails['gitlab_shell_ssh_port'] = 9922
# 使用gitlab作为镜像仓库的配置 (如果不使用,则无需配置)
registry_external_url 'http://192.168.136.132:4567'
保存退出,然后重启容器
docker restart gitlab
我们在gitlab里面创建一个测试项目。test-cicd
建立一个springboot项目,后面使用该项目做测试。
安装runner,我们这里只讨论使用本地安装方法(非docker)
1、安装git
yum -y install http://opensource.wandisco.com/centos/7/git/x86_64/wandisco-git-release-7-2.noarch.rpm
yum -y install git
gitlab-runnerLimit会自带git,那为什么要单独安装git呢?因为gitlab-runner自带的git版本太低,运行时会报错,所以这边先单独安装git,然后在安装runner
2、安装gitlab-runner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
yum -y install gitlab-runner
3、修改配置,让gitlab-runner以root运行
修改文件
vi /etc/systemd/system/gitlab-runner.service
[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/bin/gitlab-runner "run" "--working-directory" "/root/gitlab-runner" "--config" "/etc/gitlab-runner/config.toml" "--service" "gitlab-runner" "--user" "root"
修改里面working-directory对应的配置,config指定的文件,还有最后的–user的值修改为 root
如果不修改成root用户运行,那后面你会遇到各种各样的权限问题,所以,为了省事,这边直接改成root用户运行。
4、重启runner
systemctl daemon-reload
systemctl restart gitlab-runner
好了,这样我们的gitlab-runner就已经安装好了,下面我们来注册runner到gitlab
5、安装docker
我们需要在gitlab-runner服务器上注册runner与gitlab关联。注册之前,我们必须要到gitlab项目中获取我们的token,在gitlab中 具体项目→设置→cicd,然后展开runner的按钮,在这可以看到,红框中就是我们的token
sudo gitlab-runner register -n \
--url "http://192.168.136.132/" \
--registration-token "DheqPXLa1t4xhjJsUtQY" \
--executor "shell" \
--description "test-cicd-runner"
url:就是gitlab访问的地址
token:就是上面的token
executor:我们这里选择的是sehll,也可以用docker
description:就是runner的描述(可以后面修改)
好了,这样我们的runner就注册好了。这个时候刷新一下刚才的获取token页面,就会在下面看到注册好的runner
前面有绿色的小圆球,就说明注册成功了,如果不是绿色的,那说明注册的有问题。
点击后面的编辑小图标,勾上这个选项,保存
让我们没有指定标签的任务也能运行,不然要指定标签跟他一致才行。
gitlab cicd最核心的文件,就是.gitlab-ci.yml。我们所有的编译、打包、部署的脚本都要在这里去编写。该文件存放目录就是项目的根目录,所以我们在刚才test-cicd项目根目录下创建一个文件.gitlab-ci.yml,看下,我们写个最简单的脚本
stages:
- runner test
job1:
stage: runner test
script:
- echo 'start to runner'
- echo 'success runner'
stages:就是这个runner需要执行的步骤有哪些,可以多个
job1:就是需要执行的第一步,名称可以任意取
job1.stage就是对应上面的步骤。名称要完成相同
job1.script:就是需要执行的shell脚本。这里最简单打印两行信息
这个时候,我们把该文件push到gitlab,就会自动触发runner工作。
在项目的cicd→流水线菜单中,我们看到有这么一条数据
这个就是刚才我们push代码生成一条构建记录
点进去看下
下面的job1就是我们.gitlab-ci,yml文件里面配置的数据
点进去看下输出信息
果然按照我们的要求,输出了两行信息。这样一个最简单的一个cicd就完成了。当然我们实际项目中步骤肯定不止这么简单。下面我们就来做一个基于springboot项目的cicd
@RestController
public class TestController {
@Value("${test.id}")
private String testId;
@RequestMapping("/test")
public Object test(String str) {
return "你好:testid="+ testId + "str=" + str;
}
}
我们要在gitlab-ci里面做几件事:
1、maven编译,打包
2、docker构建镜像
3、推送镜像到镜像仓库(这里使用gitlab做镜像仓库)
4、远端服务器拉取镜像然后启动容器
好,修改我们的.gitlab-ci.yml
stages:
# 我们分两步,1-打包编译;2-docker构建和部署
- maven build
- docker build and deploy
maven_build:
stage: maven build
script:
## mvn package
- docker run -i --rm -v /root/.m2:/root/.m2 -v "$(pwd)":/opt/maven -w /opt/maven maven:3.6.3-openjdk-11 mvn clean package -Dmaven.test.skip=true
artifacts:
paths:
- start/target/*.jar # 将maven构建成功的jar包作为构建产出导出,可在下一个stage的任务中使用
docker_build:
stage: docker build and deploy
before_script:
## 登录到docker仓库,可以直接push镜像上去
- docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password $CI_REGISTRY_PASSWORD
script:
## 获取当前时间做docker的镜像tag
- time=$(date +%Y%m%d%H%M%S)
## 构建镜像
- docker build -t $CI_REGISTRY_IMAGE:$time .
## 推送镜像到镜像仓库
- docker push $CI_REGISTRY_IMAGE:$time
- echo $DEPLOY_HOSTS
## 部署应用
- sh /root/start.sh $DEPLOY_HOSTS $CI_PROJECT_NAME $CI_REGISTRY_IMAGE $time $SSH_USER $SSH_PORT $SERVER_PORT test
only:
# 只有master分支才执行这一步(根据项目实际而定)
- master
解读一下这个文件
stages:
# 我们分两步,1-打包编译;2-docker构建和部署
- maven build
- docker build and deploy
表示我们这次构建分两个步骤
maven_build:
stage: maven build
script:
## mvn package
- docker run -i --rm -v /root/.m2:/root/.m2 -v "$(pwd)":/opt/maven -w /opt/maven maven:3.6.3-openjdk-11 mvn clean package -Dmaven.test.skip=true
artifacts:
paths:
- start/target/*.jar # 将maven构建成功的jar包作为构建产出导出,可在下一个stage的任务中使用
这一步是maven编译步骤
1、这里我们没有在runner机上安装jdk环境和maven环境,直接使用了docker的maven来编译java文件。而且在/root/.m2文件下面放置setting.xml文件,然后使用我们自己的seting文件来编译打包。maven的本地仓库也在/root/.m2目目录下,这样只要第一次打包的时候从中央仓库下载jar包,后面就都可以使用本地仓库了。
2、最后输出了我们的jar文件作为下一步的输入文件
docker_build:
stage: docker build and deploy
before_script:
## 登录到docker仓库,可以直接push镜像上去
- docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password $CI_REGISTRY_PASSWORD
script:
## 获取当前时间做docker的镜像tag
- time=$(date +%Y%m%d%H%M%S)
## 构建镜像
- docker build -t $CI_REGISTRY_IMAGE:$time .
## 推送镜像到镜像仓库
- docker push $CI_REGISTRY_IMAGE:$time
- echo $DEPLOY_HOSTS
## 部署应用
- sh /root/start.sh $DEPLOY_HOSTS $CI_PROJECT_NAME $CI_REGISTRY_IMAGE $time $SSH_USER $SSH_PORT $SERVER_PORT test
only:
- master
这里的步骤是
1、登录镜像仓库
2、构建镜像
3、推送镜像到仓库
4、使用ssh部署应用到远端服务器
5、这里传递了spring.profiles环境为test,如果是线上服务,则传递prod。
6、根据上面一条配置,可以知道,我们这里使用了springboot的一个特性,一次打包,到处运行。只要在编译环境编译一次,上传到镜像仓库,然后上线时,只要选择已经打好的镜像,传入pring.profiles.active=prod就可以了。
这里有一个步骤 before_script。这是在执行script之前的一些前置操作,我这里是加了登录镜像仓库的操作
这里使用当前时间作为镜像的tag
最后的 only.master 表示 只有在master分支才会触发这一步操作
这里使用了一些变量参数,有一些是gitlab-ci内置的参数,有一些是在gitlab-runner里面自己设置的参数
内置参数详情请见 gitlab-ci内置变量
自定义参数配置方法 gitlab项目→设置→cicd 页面里面的变量按钮展开即可。
再来看下 /root/start.sh这个文件,放在runner机子上的这个目录
#!/bin/bash
DEPLOY_HOSTS=$1 #需要发布的服务地址,使用逗号分隔,需要与runner配置好免密码登录
PROJECT_NAME=$2 #项目名称
IMAGE_NAME=$3 #镜像名称
IMAGE_TAG=$4 #镜像tag
SSH_USER=$5 #登录到服务器的用户名
SSH_PORT=$6 #登录到服务器的端口号
SERVER_PORT=$7 #项目启动的端口号
SPRING_PROFILES=$8 #spring.profiles.active环境
echo 'DEPLOY_HOSTS= ' $DEPLOY_HOSTS
echo 'PROJECT_NAME= ' $PROJECT_NAME
echo 'IMAGE_NAME= ' $IMAGE_NAME
echo 'IMAGE_TAG= ' $IMAGE_TAG
echo 'SSH_USER= ' $SSH_USER
echo 'SSH_PORT= ' $SSH_PORT
echo 'SERVER_PORT= ' $SERVER_PORT
echo 'SPRING_PROFILES= ' $SPRING_PROFILES
#把服务器地址按照逗号分隔成数组,然后遍历,一台一台的发布
for host in `echo "$DEPLOY_HOSTS" | sed 's/,/\n/g'`
do
echo 'start to deploy with host= ' $host
ssh -p $SSH_PORT $SSH_USER@$host 'bash -s'< /root/deploy.sh $host $PROJECT_NAME $IMAGE_NAME $IMAGE_TAG $SERVER_PORT $SPRING_PROFILES
echo $host ' deploy success'
done
这里很简单,就是遍历需要发布的服务器,然后分别发布。
再看下/root/deploy.sh这个文件,由于我们使用了在远端指定本地文件的方式,所以这个文件也放在runner机子上
#!/bin/bash
HOST=$1 #服务器地址
PROJECT_NAME=$2 #项目名称
IMAGE_NAME=$3 #镜像名称
IMAGE_TAG=$4 #镜像tag
SERVER_PORT=$5 #应用的端口号
SPRING_PROFILES=$6 #spring.profiles.active环境
echo 'host=' $HOST
echo 'PROJECT_NAME=' $PROJECT_NAME
echo 'IMAGE_NAME=' $IMAGE_NAME
echo 'IMAGE_TAG=' $IMAGE_TAG
echo 'SERVER_PORT=' $SERVER_PORT
echo 'SPRING_PROFILES=' $SPRING_PROFILES
## 拉取新的镜像
docker pull $IMAGE_NAME:$IMAGE_TAG
echo 'image ' $IMAGE_NAME:$IMAGE_TAG ' pull success'
# 检查该进程是否正在运行
num=`docker ps |grep -w $PROJECT_NAME|grep -v grep|wc -l`
if [ $num -gt 0 ]; then
##存在以前的进程,停止掉
echo 'docker container ' $PROJECT_NAME ' progress is running'
## 停止掉该进程
docker stop $PROJECT_NAME
echo 'stop container ' $PROJECT_NAME ' success'
fi
## 检查该容器是否存在
num=`docker ps -a |grep -w $PROJECT_NAME|grep -v grep|wc -l`
if [ $num -gt 0 ]; then
##容器存在,则删掉该容器
echo 'docker container ' $PROJECT_NAME ' container is exists'
## 删掉该容器
docker rm $PROJECT_NAME
echo 'remove container ' $PROJECT_NAME ' success'
fi
##启动容器
docker run -d -e "SPRING_PROFILES_ACTIVE=$SPRING_PROFILES" --name $PROJECT_NAME -p $SERVER_PORT:$SERVER_PORT -v /Data/logs:/Data/logs $IMAGE_NAME:$IMAGE_TAG
echo 'run container ' $PROJECT_NAME ' success!!!!'
这个就是最终到远端服务器启动容器的脚本
里面包含了:
1、拉取新的镜像
2、判断是否有容器在运行,如果有,则停掉容器
3、判断是否有未启动的容器存在,如果有,则删除掉
4、启动新的容器
这里还差一步,检查容器是否启动成功,后面会把相应的脚本加上。
好了,配置都完成了。我们提交代码,push到gitlab,自动触发构建,等待构建完成。如果有报错请求,需要根据报错日志进行分析解决。
打开gitlab流水线,看到有新的任务在运行,点进去看,里面分两个步骤
慢慢等待编译,打包,部署。
成功后,打开刚才的接口
http://192.168.136.130:8080/test/test,看看接口是否成功。
注意一点:
所有的应该服务器都需要安装docker环境,因为我们的应用服务器都是在docker内运行的。
最后还有一个文件,Dockerfile文件
FROM openjdk:11-oraclelinux7
ADD start/target/test-cicd.jar /test-cicd.jar
ENTRYPOINT ["java", "-Duser.timezone=GMT+0800", "-jar", "/test-cicd.jar"]
这个很简单,就是使用openjdk11来运行编译好的jar包。
如果你使用k8s去部署管理容器的话,那就可以更方便的管理我们容器的生命周期了。
好了,我们的基于gitlab的cicd构建已经成功,看看是不是很简单,跟以前的jekins比较,你觉得哪个使用更方便呢?
欢迎大家留言讨论!!