搭建持续集成CI流水线

在搭建流水线之前,需要先了解流水线平台的使用和实现原理。底层基于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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值