jenkins-自动化部署(二)
配置关联机器
安装插件
系统管理->插件管理->可选插件
搜索" Publish over SSH" 因为我装了所以搜索不出来,搜索出来 打勾 点击直接安装就好了
配置关联机器
就是jenkins访问部署服务的机器
系统管理-系统配置
配置密码
输入服务器密码
测试
安装git
因为我使用git作为源代码工具 所以在服务器安装git
yum -y install git
安装maven
源码构建工具使用的maven 所以安装maven <<maven安装>>
变量配置
如果不配置,会出现shell执行mvn命令 报:jenkinsmvn: command not found
将安装maven配置的变量设置到jenkins 如:
vi /etc/profile
安装jdk
参考《jdk-安装》
变量配置
设置ssh免密登录
1.设置免密
注:需要在jenkins账户下创建秘钥,切换失败则参考下面
2.因为jenkins是默认用jenkins账户取执行ssh jenkins账户是没有权限的
su jenkins
如果切换失败还是root账户
vim /etc/passwd
将
jenkins:x:986:980:Jenkins Automation Server:/var/lib/jenkins:/bin/false
改为
jenkins:x:986:980:Jenkins Automation Server:/var/lib/jenkins:/bin/bash
再次执行
[root@cicd ~]# su jenkins
bash-4.1$
结果命令提示符的用户名不是jenkins而变成了 bash-4.1$,原因是在安装jenkins时,jenkins只是创建了jenkins用户,并没有为其创建home目录。
使用root账户执行
vim ~/.bash_profile
在文件末尾加上
export PS1='[\u@\h \W]\$'
刷新配置
source ~/.bash_profile
验证是否成功
3.jenkins系统用户开通免密登陆
使用root账户输入
sudo visudo
在文件末尾加上
jenkins ALL=(ALL) NOPASSWD: ALL
重启jenkins
systemctl status jenkins
systemctl stop jenkins
systemctl start jenkins
任务管理例子
这是我们的每个tag对应一个环境,然后需要发布某个环境下服务切到环境下点一下就行了,因为我们多个需求并行开发 所以测试环境很多套,不同的环境发不同的分支互不影响:如bbc-preview1 就是第一套 pro就是生产
创建任务
2.构建设置
3.变量设置,脚本和git需要使用
4.设置源码地址和登录名密码信息
5.设置构建步骤 使用shell
脚本编写案例1
编写jenkins服务shell脚本
#!/bin/bash #目标服务器主目录 _DEPLOYER='deploy' #程序的yml环境配置 _PROFILES='alitest' #日志目录 _LOG_DIRECTORY="/home/$_DEPLOYER/logs" #这里定义服务的配置多个服务空格隔开 如:SERVICE_CONFIG_TABLE='service-bridge;9910;1g service-bridge2;9910;1g' SERVICE_CONFIG_TABLE='service-bridge;9908;1g' #是否部署bridge的变量 不支持-的定义 所以改为_ service_bridge=$service_bridge #读取上面hosts配置的变量 hosts=$hosts #是否重新打包,写死,如果需要动态勾选在jenkens通过变量配置 repackage=true #是否重新部署服务 写死 如果需要动态勾选在jenkens通过变量配置 restart=true #写死,如果需要动态勾选 在上面配置 写死 如果需要动态勾选在jenkens通过变量配置 skipTest=true TEST_ARGUMENT=""; #maven打包是否跳过Test if $skipTest; then TEST_ARGUMENT="-Dmaven.test.skip=true" fi #是否需要重新打包 循环服务配置 进行逐个maven打包 if $repackage; then #支持多服务配置 空格隔开 所以 用for in for SERVICE_CONFIG in $SERVICE_CONFIG_TABLE; do #通过;分割 取第一个 取出服务名字 SERVICE_NAME=$(echo $SERVICE_CONFIG | cut -f 1 -d ';' ) #将service-** 替换为service_** isDeployParameterName=${SERVICE_NAME/-/_}; #根据名字获得value isDeployParameterValue=${!isDeployParameterName} #根据是否勾选了部署来判断是否部署 if $isDeployParameterValue; then #maven 命令 clean 并重新打包到targer mvn -s "/var/lib/jenkins/workspace/preview-ch-micro-services/settings.xml" clean package -pl "$SERVICE_NAME" -am -U $TEST_ARGUMENT if [ $? -gt 0 ]; then echo "编译错误" exit 1 fi fi done echo "打包成功[$branch]...准备scp到目标服务器" for SERVICE_CONFIG in $SERVICE_CONFIG_TABLE; do SERVICE_NAME=$(echo $SERVICE_CONFIG | cut -f 1 -d ';' ) #将service-** 替换为service_** isDeployParameterName=${SERVICE_NAME/-/_}; #根据名字获得value isDeployParameterValue=${!isDeployParameterName} #根据是否勾选了部署来判断是否部署 if $isDeployParameterValue; then #获得指定服务的变量名字 hostParameterName=$isDeployParameterName"_hosts"; #将打包后的jar 推送到目标服务器的projects目录 #我们host是文本变量通过回车换行,其实变量值是空格隔开 直接循环 for host in ${!hostParameterName};do scp $SERVICE_NAME/target/*.jar root@$host:/home/$_DEPLOYER/projects #判断上一个命令是否执行成功 if [ $? -eq 0 ]; then echo "服务"$SERVICE_NAME"主机地址"$host"已最新包scp到服务器成功" else echo "服务"$SERVICE_NAME"主机地址"$host"最新包scp到服务器失败" fi done fi done fi #是否发布 循环服务 执行脚本start_bash if $restart; then #支持多服务配置 空格隔开 所以 用for in for SERVICE_CONFIG in $SERVICE_CONFIG_TABLE; do SERVICE_NAME=$(echo $SERVICE_CONFIG | cut -f 1 -d ';' ) #将service-** 替换为service_** isDeployParameterName=${SERVICE_NAME/-/_}; #根据名字获得value isDeployParameterValue=${!isDeployParameterName} #根据是否勾选了部署来判断是否部署 if $isDeployParameterValue; then #通过;分割取第三个 获取内存大小 MEMORY_MAX=$(echo $SERVICE_CONFIG | cut -f 3 -d ';' ) #通过;分割去第二个 获取端口号 SERVICE_PORT=$(echo $SERVICE_CONFIG | cut -f 2 -d ';' ) #获得指定服务的变量名字 hostParameterName=$isDeployParameterName"_hosts"; #我们host是文本变量通过回车换行,其实变量值是空格隔开 直接循环 for host in ${!hostParameterName};do #1.在指定主机执行递归创建2个目录 #2.将projects的打包的jar复制到workspace #3.调用远程服务器的start_bash脚本 执行服务发布 ssh root@$host "mkdir -p /home/deploy/logs && mkdir -p /home/deploy/workspace/$SERVICE_NAME-$SERVICE_PORT && \ cp /home/deploy/projects/$SERVICE_NAME.jar /home/deploy/workspace/$SERVICE_NAME-$SERVICE_PORT/app.jar && \ bash /home/deploy/start_bash -s -- -t/home/$_DEPLOYER/workspace/$SERVICE_NAME-$SERVICE_PORT/app.jar\ -a-Xmx$MEMORY_MAX\ -Xms$MEMORY_MAX\ -XX:+PrintGCDetails\ -XX:+HeapDumpOnOutOfMemoryError\ -Dspring.profiles.active=alitest\ -Dproject.name=service-bridge\ -Dcsp.sentinel.dashboard.server=192.168.20.4:9998 -f$_LOG_DIRECTORY/$SERVICE_NAME.log" #判断上一个命令是否执行成功 if [ $? -eq 0 ]; then echo "服务"$SERVICE_NAME"主机地址"$host"已经执行ssh脚本成功" else echo "服务"$SERVICE_NAME"主机地址"$host"执行ssh脚本失败" fi done fi done fi
有点多 好好读可以看得懂
编写部署端脚本
可以看到上面调用了start_bash 在指定目录创建
JVA_HOME=/usr/java/jdk1.8.0_191 JRE_HOME=/usr/java/jdk1.8.0_191/jre CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib PATH=$JAVA_HOME/bin:$PATH export PATH JAVA_HOME CLASSPATH _TARGET="" _DIRECTORY="" _NAME="" _PORT="" _LOG= _LOG_FILE="/dev/null" _STOP= _PID= _COMMAND= _ARGUMENTS="" #getopts会将 对应入参封装到OPTARG里面 比如a 则会获取到-a参数 只有参数的才会进while循环 比如没有-a 就不会走case a while getopts "a:t:p:x:s:lf:kh" opt; do case $opt in a) #$OPTARG" 为脚本-a的参数 _ARGUMENTS=" $OPTARG" ;; t) #OPTARG 为脚本的-t参数 _TARGET="$OPTARG" _DIRECTORY=$(dirname $OPTARG)"/" _NAME=$(echo $(basename $OPTARG) | cut -f 1 -d '.') ;; p) _PORT=$OPTARG ;; l) _LOG=true ;; f) _LOG=true if [ ! "$OPTARG" = "" ]; then _LOG_FILE=$OPTARG fi ;; s) _STOP=true ;; h) _HELP_FORMAT="%-4s %-80s\n" printf "$_HELP_FORMAT" "参数" "描述" printf "$_HELP_FORMAT" "-t" "jar包绝对地址, 如: '-t/home/deployer/test.jar'" printf "$_HELP_FORMAT" "-p" "jar包占用端口, 如: '-p7777'" printf "$_HELP_FORMAT" "-a" "自定义参数, 如: '-a-DenableSecurity=true\ -Dtoken=xxxx'" printf "$_HELP_FORMAT" "-l" "开启日志输出,不指定该参数则不输出日志. 日志默认输出到目标jar包相同目录下的同文件名.log" printf "$_HELP_FORMAT" "-f" "强制开启日志输出,指定参数为'-f/var/log/test.log',则输出到/var/log/test.log文件" printf "$_HELP_FORMAT" "-s" "强制关闭目标程序(不会启动目标文件),默认按端口,次之按文件" exit ;; esac done #根据PID获取端口 function getPidByPort { if [ -f "/usr/sbin/lsof" ]; then echo `/usr/sbin/lsof -i:"$1" | grep LISTEN | awk '{ print $2}'` else echo `/usr/bin/lsof -i:"$1" | grep LISTEN | awk '{ print $2}'` fi } #根据PI获取file function getPidByFile { echo `ps aux | grep \\\\-jar | grep $1 | awk '{ print $2}'` } #获得PID function getPid { if [ "$_PORT" == "" ]; then if [ ! "$_NAME" == "" ]; then _PID=`getPidByFile "$_TARGET"` fi else _PID=`getPidByPort $_PORT` fi } #调用PID getPid echo $_PID #多有有则杀掉PID if [[ $_PID =~ ^[0-9]+$ ]]; then echo "kill process $_PID" kill $_PID; echo "sleep 5 seconds" sleep 5 getPid if [[ $_PID =~ ^[0-9]+$ ]]; then kill -9 $_PID sleep 1 fi fi if [ $_STOP ]; then getPid if [[ $_PID =~ ^[0-9]+$ ]]; then echo "无法关闭进程:$_PORT" exit; else echo "已经关闭指定程序进程" fi # exit; fi if [ "$_NAME" == "" ]; then echo "请指定需要启动的jar包的绝对地址,如: -t/home/test.jar" exit fi if [ $_LOG ]; then if [ "$_LOG_FILE" = "/dev/null" ]; then _LOG_FILE="$_DIRECTORY$_NAME.log" else mkdir -p "$(dirname $_LOG_FILE)" fi fi _COMMAND="nohup java -jar$_ARGUMENTS $_TARGET > $_LOG_FILE 2>&1 &" echo "正在执行:$_COMMAND" source /etc/profile eval $_COMMAND
脚本编写案例2
以下是我部署canal的脚本
jenkins
_DEPLOYER='canalServer' task=''; if $repackage; then task="maven打包并推服务器" fi if $restart; then task="${task} 重启服务" fi echo "执行任务项:${task}" if $repackage; then mvn clean install -Dmaven.test.skip -Denv=release echo "打包成功[${branch}]...准备scp到目标服务器 ${hosts}" for host in $hosts do if [ $? -gt 0 ]; then echo "编译错误" exit 1 fi scp /home/jenkins/workspace/canal-server/${SERVICE_NAME}/target/canal.deployer-1.1.4.tar.gz ${_DEPLOYER}@${host}:/home/${_DEPLOYER}/projects/canal.deployer-1.1.4.tar.gz echo done fi if $restart; then echo "准备部署服务器 ${hosts}" for host in $hosts do ssh ${_DEPLOYER}@${host} "bash /home/canalServer/deploy.sh -p/home/canalServer/projects/canal.deployer-1.1.4.tar.gz -t/home/canalServer/projects" echo done fi
部署端
deploy.sh
#!/bin/bash #getopts会将 对应入参封装到OPTARG里面 比如a 则会获取到-a参数 只有参数的才会进while循环 比如没有-a 就不会走case a _tarFilePath='' _tarTargetPath='' while getopts "a:t:p:x:s:lf:kh" opt; do case $opt in p) #$OPTARG" 为脚本-a的参数 _tarFilePath="$OPTARG" ;; t) #OPTARG 为脚本的-t参数 _tarTargetPath="$OPTARG" ;; esac done echo $_tarTargetPath echo $_tarFilePath #根据程序目录查找pid grep -v 为排除 避免找到grep进程 awk '{print}'为打印第二个参数 即进程id 注意此获取放到第一行 避免查询到其他进程 _pid=`ps auxww|grep $_tarTargetPath |grep -v grep|grep -v cronolog|grep -v bash|awk '{print $2}'` #$pid长度大于0 if [ -n "$_pid" ]; then { echo "-----------------kill掉原有进程 $_pid---------------" #指定用户kill调进程 kill $_pid _command= "${_tarTargetPath}/bin/stop.sh" eval $_command } fi echo "-------------------------删除原有目录------------------" #删除以前的执行执行程序 eval "rm -rf ${_tarTargetPath}/bin" eval "rm -rf ${_tarTargetPath}/conf" eval "rm -rf ${_tarTargetPath}/logs" eval "rm -rf ${_tarTargetPath}/lib" eval "rm -rf ${_tarTargetPath}/plugin" echo "------------------------执行新的解压-------------------" _command="tar -xvf ${_tarFilePath} -C ${_tarTargetPath}" eval $_command if [ $? -gt 0 ]; then echo "解压错误" exit 1 fi echo '-------开始执行启动命令----------------' _command="${_tarTargetPath}/bin/startup.sh" echo $_command eval $_command
测试
ssh 调用脚本java相关命令报错问题处理
脚本前面加上 source /etc/profile