在搭建流水线之前,需要先了解流水线平台的使用和实现原理。底层基于Jenkins和Kubernetes作为资源池,触发流水线的方式主要有代码提交、标签触发、合并请求触发、定时分支和手动触发。这些方式都可以通过配置来实现,例如可以设置定时任务来触发流水线,或者在代码提交时自动触发流水线。
不同产品的流水线阶段任务有所不同,但通常都包括代码扫描、单元测试、打包和部署等步骤。在代码扫描阶段,可以使用公司提供的代码扫描公共库,选择对应的语言环境,并配置编译命令。在单元测试阶段,需要在机器上使用指定的镜像,并申请自定义主机来配置流水线的执行机。在打包和部署阶段,可以使用Docker镜像来打包和部署应用程序。
在开始搭建流水线之前,需要获取GitLab仓库的权限,并查看GitLab CI文件,其中说明了持续集成、代码扫描、单元测试等阶段使用的镜像以及执行脚本。镜像的话需要自己打,并上传到指定的平台。
先来说说镜像和打包脚本的重头戏,这个比较精彩就先放这里,当然按照流程应该是先代码扫描,再单测,接口测试,通过后再打包,推送制品。
下面是一个简单的dockerFile
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y python3 python3-pip && \
pip3 install flask
COPY node-v16.17.1-linux-x64.tar.xz /node-v16.17.1-linux-x64.tar.xz
RUN tar -xJf /node-v16.17.1-linux-x64.tar.xz -C /usr/local --strip-components=1 \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
RUN rm /node-v16.17.1-linux-x64.tar.xz
COPY app.py /app.py
EXPOSE 5000
CMD ["python3", "/app.py"]
这个Dockerfile的作用是构建一个包含Python3、Flask和Node.js的镜像。它首先安装了Python3和Flask,然后从主机上复制了一个Node.js的压缩包到容器中,并解压缩安装了Node.js。最后,它将主机上的Flask应用程序代码复制到容器中,并指定了容器启动时要执行的命令,即运行python3 /app.py命令来启动Flask应用程序。这个镜像可以用于运行一个全栈Web应用程序,例如使用Flask和Node.js来构建一个Web应用程序。
docker build -t my-flask-app .
启动镜像docker run -it my-flask-app /bin/bash
由于环境受限前端包和后端包需要不同的镜像打包,CI的文件也只是产出一个压缩包,要实现打包的拼装,并不简单。
首先是前端打包的脚本
#!/bin/bash
# 导出路径变量
export PACKAGE_PATH="/front_pkg"
# 获取分支名称
branch=${CI_COMMIT_BRANCH}
echo "test_appname${branch}"
app_name=${CI_PROJECT_NAME:?} # 获取项目名称
# 备份原始打包文件
if [ -f $PACKAGE_PATH/$app_name/dist.tar ]; then
mv $PACKAGE_PATH/$app_name/dist.tar $PACKAGE_PATH/$app_name/dist.tar.bak
fi
echo "$app_name"
# 打前端包
cd ui
yarn # 安装依赖
yarn build # 打包
if [ $? -ne 0 ]; then
echo "前端打包失败,取消备份操作"
exit 1
fi
tar -cvf dist.tar dist # 将打包文件打包成 dist.tar
# 检查目标目录是否存在,不存在则创建
if [ ! -d $PACKAGE_PATH/$app_name ]; then
mkdir -m 755 $PACKAGE_PATH/$app_name
else
rm -rf $PACKAGE_PATH/$app_name/*
fi
# 移动打包文件到目标目录
mv ${CI_PROJECT_DIR}/ui/dist.tar $PACKAGE_PATH/$app_name
# 如果 electron 目录存在,则进行打包
if [ -d electron ]; then
echo "electron 目录存在"
cd ./electron
yarn # 安装依赖
yarn package # 打包
if [ $? -eq 0 ]; then
tar -cvf electron.tar dist # 将打包文件打包成 electron.tar
mv electron.tar $PACKAGE_PATH/$app_name
else
echo "electron 打包失败,取消备份操作"
exit 1
fi
else
echo "electron 目录不存在"
fi
接下来是重头戏,后端包,后端有多个仓库需要跑,因此打包需要兼容多个仓库,同时为了节俭打包时间需要前后端打包流程一起跑,但是前端包可能会出现打包时间长的问题,这里通过一个循环定期去看前端是否打包出来
#!/bin/bash
# 导出路径变量
export PACKAGE_PATH="/front_pkg"
export WORKSPACE_PATH="/test"
# 获取分支名称
branch=${CI_COMMIT_BRANCH}
echo "test_appname${branch}"
app_name=${CI_PROJECT_NAME:?} # 获取项目名称
# 编译打包
nowtime=$(date +'%y%m%d%H%M')
echo "$app_name"
# 执行语言包脚本
run_locale_script() {
if [ -f "$1" ]; then
chmod +x "$1"
./"$1"
fi
}
run_locale_script "src/locale/locale.sh"
run_locale_script "src/acheck_depends/locale/locale.sh"
# 如果项目名称为 acheck-base,则初始化数据库
if [ "$app_name" = "acheck-base" ]; then
cd src
script -qefc "wine python main.py db --init" /dev/null
script -qefc "wine python main.py db --migrate" /dev/null
script -qefc "wine python main.py db --upgrade" /dev/null
cd ..
fi
# 执行打包命令
cd pack
script -qefc "wine python build.py" /dev/null
# 移动打包文件到指定目录
cd $WORKSPACE_PATH
if [ ! -d "$branch/$app_name" ]; then
mkdir "$branch/$app_name"
else
rm -rf "$branch/$app_name"/*
fi
cd $WORKSPACE_PATH/$branch/$app_name
mv ${CI_PROJECT_DIR}/_dist/* ./
# 检测前端打包是否完成
check_frontend_package() {
folder="$1"
while true; do
if [ -f "$folder/dist.tar" ]; then
echo "文件dist.tar存在于文件夹中"
break
else
echo "文件dist.tar不存在于文件夹中"
fi
sleep 3 # 休眠3秒
done
}
check_frontend_package "$PACKAGE_PATH/$app_name"
# 如果项目名称为 base,则检测 electron 打包是否完成
if [ "$app_name" = "base" ]; then
check_frontend_package "$PACKAGE_PATH/$app_name/electron.tar"
fi
# 解压后端包并嵌入前端包
for file in "."/*.zip; do
if [ -f "$file" ]; then
yes A | unzip "$file"
rm -rf "$file"
fi
done
mkdir public
cp $PACKAGE_PATH/$app_name/* ./
tar -xvf dist.tar -C "public" && rm -rf dist.tar
# 如果存在 electron.tar,则解压并移动文件
if [ -f electron.tar ]; then
mkdir -p public/electron
tar -xvf electron.tar -C "public/electron"
name=$(ls public/electron/dist)
mv public/electron/dist/$name/* public/electron && rm -rf electron.tar public/electron/dist
fi
# 打包并清理文件
zip -r "$app_name.zip" ./ && find ./ -type f ! -name "*.zip" -exec rm -f {} +
# 如果项目名称为 base,则记录 md5 值
if [ "$app_name" = "base" ]; then
md5sum $app_name.zip >> md5$nowtime.ver
fi
find ./ -type d ! -name "." -exec rm -rf {} +
# 输出提示信息
echo "请转入 http://xxx.xxx.xxx/$branch/$app_name 获取测试包"
项目组有多个产品,不同产品流水线的阶段任务不同,主要是以下两种形式的流水线任务:
1、代码扫描---》单元测试---》打测试包---》打签名包---》推送指定平台
2、代码扫描---》推送docker镜像至仓库---》部署测试环境----》推送指定平台
单元测试 部署到老环境大致思路就是这样,那么开工,首先获取gitlab仓库的权限,查看gitlab-ci文件,上面说明了持续集成,代码扫描,单元测试等阶段使用的镜像,以及执行脚本。
一、大致思路就是由面到点,首先配置出流水的模型,从代码检查到代码扫描到编译打包
,代码检查的配置相对简单,使用公司提供的代码扫描公共库,选择对应的语言环境,配置编译命令,过滤配置排除文件通常报错**.*.md, tools/*, test/* 等文件不需要代码扫描
二、单元测试:单元测试就需要在机器上使用指定的镜像,申请自定义主机,在主机上配置流水线的执行机配置。
1、申请自定义主机,从虚拟机资源池申请了一台主机,配置好登入ip和通讯ip,和相关路由的时候后,通过打开隧道,再使用xshell远程登入,发现一直连接超时,查看sshd服务,没有问题,显示正常。
[root@localhost 15129]# systemctl status sshd
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2023-07-31 20:57:30 CST; 4 days ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 8555 (sshd)
Memory: 1.4M
CGroup: /system.slice/sshd.service
└─8555 /usr/sbin/sshd
难道是网络配置错误,使用管理ip ping本地ip,发现ping不通,但是用业务ip却可以ping通本地ip,说明业务ip是通的,思考了下可能是防火墙出问题,于是关闭防火墙试试
[root@localhost 15129]# systemctl stop firewalld
从本地ping管理ip
C:\Users\User>ping 10.115.20.105
正在 Ping 10.115.20.105 具有 32 字节的数据:
来自 10.115.20.105 的回复: 字节=32 时间=2ms TTL=60
来自 10.115.20.105 的回复: 字节=32 时间=4ms TTL=60
来自 10.115.20.105 的回复: 字节=32 时间=2ms TTL=60
一、前置准备,申请自定义执行主机,安装执行机
1、从acloud平台申请虚拟机,配置业务网络(用来业务通讯,访问其他部门业务)和管理网络(登入IP),申请虚拟机后再/root目录下,有配置网络脚本./auto_ip.sh,启动network.server,和sshd服务,关闭虚拟机防火墙,开启隧道,远程登入虚拟机
2、配置nameserver和yum源,修改前备份,安装git工具,java1.8以上环境yum install -y java-1.8.0-openjdk-devel.x86_64 ,安装git-lfs
# 编辑resolv.conf
vim/etc/resolv.conf
#添加DNS
nameserver 114.114.114.114
安装git-lfs可能需要主机加入epel源http://mirrors.xxx.org/help/2018/01/13/epel.html
yum install epel-release
# 备份,修改yum源
#cp /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.bak(前面步骤已完成)
yum makecache
3、安装执行机
复制安装指引,至虚拟机
journalctl -f -xeu agent命令查看实时日志,无报错即可
二、搭建流水线
1、根据gitlab_ci.yalm文件搭建流水线,创建流水线,配置代码扫描,单元测试。接口测试,打测试包,打签名包,推送到制品库。这里重头戏已过
2、推送镜像至制产品应用库,千流平台会从产品应用库拉取镜像执行脚本,镜像的可以找到Dockerfile文件,直接制作镜像,将Dockerfile文件和相关依赖文件放在同一个目录执行 docker build -t ***wine:1.0 .就可以制作镜像,制作完成后,使用docker tag <LOCAL_IMAGE_TAG> docker.xxx.com/cicd打上标签,根据指引上传镜像
docker push image
3、遇到的问题
ubuntu容器中使用source命令,提示source: not found 问题
问题现象:流水线步骤选择的运行环境是在docker容器中运行,镜像选择的是基于ubuntu做的镜像
在运行脚本时报错,提示source:not found
解决方案:
1. source xx 改为 /bin/bash -c "source xxxx"
2. 修改ubuntu镜像对应Dockerfile,指定解释器为shell
SHELL ["/bin/bash", "-c"]
在执行机手动打包不报错,但是通过千流平台拉起镜像打包的时候wine 无法初始化 Python 的标准输入/输出流,导致 Python 解释器启动失败并报错退出,从这个issue来看,jenkins的流水线用wine打包都会有这个问题
script -qefc "winew.sh **** /dev/null