背景

单纯在服务器上使用shell脚本执行ci/cd任务有一些弊端:

  • 输出无持久化,不方便回溯
  • 无图形界面,需要记忆参数
  • 功能扩展不方便,必须增加邮件通知等

本文记录在保持原有shell脚本大体不变的基础上,将其封装为jenkins任务的流程,以实现对以上几个问题的解决。

增加节点

为了尽量减少环境的重装操作,最简便的方法是把shell脚本执行的机器直接作为节点加入到jenkins中

  1. jenkins页面:manage node,新建节点

  2. 启动方式选择controller或java web,提交。

  3. 点击进入该新增的节点,下载agent.jar,复制启动命令

  4. 在slave节点用systemd把上述启动命令包装成systemd服务:vim /lib/systemd/system/jenkins.service

     [Unit]
     Description=jenkins slave process to connect to jenkins master
     After=network.target
    
     [Service]
     Type=forking
     ExecStart=/root/jenkins/start.sh
     ExecStop=/root/jenkins/stop.sh
     EnvironmentFile=/root/jenkins/envfile
    
     [Install]
     WantedBy=multi-user.target

    systemd里引用的各文件内容如下:

    start.sh

     #!/bin/bash
     nohup java -jar /root/jenkins/agent.jar -jnlpUrl http://$IP:8070/computer/245%2E207/jenkins-agent.jnlp -secret $SECRET  -workDir "/home/jenkins" &

    stop.sh

     #!/bin/bash
     ps -ef | grep agent.jar | grep -v "grep" | awk '{print $2}' | xargs kill -9

    envfile

     PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/git/bin
     HOME=/root

    特别需要注意的是systemd启动的服务不同于直接从shell终端启动的服务,无法继承终端里的各种环境变量。所以对于jenkins构建过程中需要用到的各种环境变量,都需要在envfile里单独指出。

  5. systemctl daemon-reload && systemctl enable jenkins --now启动服务,验证节点成功加入master

脚本支持参数解析

结合getopts,为普通的shell脚本增加命令行短参数,并对原始脚本中使用了read从stdin读取的变量增加命令行短参数支持,使得脚本既可以单独执行,从stdin读取用户输入,又可以被jenkins直接调用,通过短参数传递参数。

关键逻辑如下:

  1. 声明变量并赋予默认值,这些变量将同时被getops和read使用
  2. getops尝试解析短参数,并把得到的值赋予上一步声明的变量
  3. read前判断变量是否是默认值,是的话,再用read从stdin读取,否则跳过这步

例:

#!/usr/bin/env bash

# 默认值
image_origin="undefined"
target_path="undefined"
vim_readme="undefined"
chart_origin_branch="dev-master"

# 如果有对应的短参数,则覆盖默认值
function ParseFlag() {
  # 解析短参数
  while getopts ':i:t:v:b:' OPT; do
    case $OPT in
    i) image_origin="$OPTARG" ;;
    t) target_path="$OPTARG" ;;
    v) vim_readme="$OPTARG" ;;
    b) chart_origin_branch="$OPTARG" ;;
    ?) ;;
    esac
  done
}

ParseFlag

# 如果是默认值,则从stdin读
if [ "${image_origin}" == "undefined" ]; then
    read -p "从开发环境拉镜像[p]/从代码构建镜像[b]/手动放置镜像[a]" -r image_origin
fi

# 最终消费参数的值
if [ "z${image_origin}" == "zp" ]; then
  echo "从开发环境拉镜像"
  ./pull.sh
elif [ "z${image_origin}" == "zb" ]; then
  echo "从代码构建镜像"
  ./build.sh -c y all "${version}"
elif [ "z${image_origin}" == "za" ]; then
  echo "手动放置镜像,跳过"
else
  echo "错误的选项"
  exit 1
fi

node-gyp离线问题

项目依赖使用了node-gyp,会在构建期依赖公网的nodejs代码,需要提前在基础镜像里放置并配置

FROM node:16.10.0-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk add --no-cache -U python3 && \
    apk add --no-cache -U make && \
    apk add --no-cache -U g++ && \
    apk add --no-cache -U curl

WORKDIR /tarball

# node-gyp 在离线环境也需要下载文件,所以先预置在镜像里,同时需要安装g++
RUN curl -k -o node-v16.10.0-headers.tar.gz -L https://unofficial-builds.nodejs.org/download/release/v16.10.0/node-v16.10.0-headers.tar.gz
RUN npm config set tarball /tarball/node-v16.10.0-headers.tar.gz \

其他插件

  • AnsiColor:支持shell中的彩色文字

      # 红色文字输出,用于醒目提醒
      function ColorEcho() {
        RED='\033[0;31m' # red
        NC='\033[0m'     # No Color
        # $*可以接收数组    $1只能接字符串
        echo -e "${RED}$*${NC}"
      }
  • Date Parameter:支持基于日期的环境变量

    image-20220914142539155

  • Extended Choice Parameter:多选框

  • Parameterized Trigger:构建后带参数触发其他任务

参考

如何在Jenkins中使用日期参数(变量) (shuzhiduo.com)

systemd配置开机自启动java脚本_浮生忆梦的博客-CSDN博客

在基于Docker的NodeJS工程中使用node-gyp - 简书 (jianshu.com)

https://stackoverflow.com/a/64052237/6792174

「Jenkins Pipeline」- 使控制台彩色化输出(使用 AnsiColor 插件) @20210307_研究林纳斯的那个系统的博客-CSDN博客

AnsiColor | Jenkins plugin

Jenkins参数化构建与触发 - 云+社区 - 腾讯云 (tencent.com)

文章目录