jenkins是一个开源的CI(continuous integration)服务器,它的扩展架构使它不仅是一个构建管理系统,也成为一个通用的开发生命周期管理系统;
jenkins是一个可扩展的持续集成引擎;
主要用于:持续、自动的构建/测试软件项目;监控一些定时执行的任务;
特性:
易安装,只要将jenkins.war部署到servlet容器,不需数据库支持;
易配置,所有配置都通过web界面实现;
集成RSS/email,通过rss发布构建结果,或构建完成时发email;
支持分布式构建,能让多台计算机一起构建/测试;
生成Junit/TestNG测试报告;
文件识别,可跟踪某次构建生成哪些jar,哪次构建使用哪个版本的jar等;
支持扩展插件,可开发适合自己团队使用的工具;
支持环境配置矩阵;
追踪依赖关系;
优点:
一切配置都在web上完成,一些配置如MAVEN_HOME/email,只需配置一次,所有项目都能用,也可通过修改xml完成;
支持maven的module,jenkins对maven作了优化,它能自动识别module,每个module可配置成一个job,相当灵活;
测试报告聚合,所有模块的测试报告都被聚合在一起,结果一目了然,若使用其它ci这几乎是不可能完成的任务;
构建指纹artifact finterprint,每次build的结果构建都被很好的自动管理,无需任何配置就可方便的浏览下载;
CI持续集成已成为当前许多软件开发团队在整个软件开发生命周期内侧重于保证代码质量的常见做法,它是一种实践,旨在缓和和稳固软件的构建过程,并且能够帮助你的开发团队应对如下挑战:
软件构建自动化(配置完成后,ci会依照预先制定的时间表,或者针对某一特定事件,对目标软件进行构建);
构建可持续的自动化检查(ci能持续的获取新增或修改后签入的源代码,也就是说,当软件开发团队需要周期性的检查新增或修改后的代码时,ci会不断确认这些新代码是否破坏了原有软件的成功构建,这减少了开发者在检查彼此相互依存的代码中变化情况需要花费的时间和精力);
构建可持续的自动化测试(构建检查的扩展部分,构建后执行预先制定的一套测试规则,完成后触发通知给相关责任人);
生成后续过程的自动化(当自动化检查和测试成功完成,软件构建的周期中可能也需要一些额外的任务,如生成文档、打包软件、部署构建到一个运行环境或者软件仓库,这样才能更迅速的提供给用户使用);
jenkins的前身叫hudson,使用jenkins的理由:
是所有ci产品中,安装和配置最简单;
基于web访问,用户界面非常友好、直观、灵活,在许多情况下,还提供了ajax的即时反馈;
基于java,但不仅限于构建基于java的软件;
拥有大量的插件,且都开源,极大的扩展了jenkins的功能;
jenkins的目标:
监控软件开发流程,快速显示问题,为developer或相关人员省时省力提高开发效率;
ci系统在整个开发过程中的主要作用是控制,当系统在代码存储中探测到修改时,它将运行构建的任务委托给构建过程本身,如果构建失败将通知给相关人员,然后继续监视存储库,它的角色看起来是被动的,但的确能快速反映问题;
开发流程:
测试:
对项目进行测试以验证代码的运行情况,这样可预防忽略回归regressions,并在某些情况下它可作为一种文档格式,在其中阅读测试代码可告知其它人库中的api工作情况;
常用测试工具nose,https://wiki.python.org/moin/PythonTestingToolsTaxonomy;
TDD测试驱动开发,是敏捷开发中的一项核心实践和技术,也是一种设计方法论,TDD的原理是在开发功能代码之前先编写单元测试用例代码,测试代码确定需要编写什么产品代码;
文档:
与测试不同,可根据项目文档的质量和范围来判断一个项目的质量和工艺,编写和维护文档的方法与编写和维护代码的方法相似,文档写的出色并具有深度,可吸引更多的参与者加入,使用户更接近你的项目;
部署一个ci系统最低要求:一个可获取的源代码仓库;一个包含构建脚本的项目;
ci系统的基本结构:
developer检入代码到源代码仓库;
ci系统会为每一个项目创建一个单独的工作区workspace,当预设或请求一次新的构建时,它将把源代码仓库的源码存放到对应的workspace;
ci系统会在对应的工作区内执行构建过程;
配置存在的情况下,构建完成后,ci系统会在一个新的构建中执行定义的一套测试,完成后触发通知email或rss给相关责任人;
配置存在的情况下,构建成功后,这个构建会被打包并转移到一个部署目标,如appserver,或存储为软件仓库中的一个新版本;软件仓库可以是ci系统的一部分,也可以是一个外部的仓库,如fileserver或sourceForge、java.net之类的网站;
ci系统通常会根据请求发起相应的操作,如即时构建、生成报告、或检索一些构建好的构建;
脚本的好处:
降低开发门槛,提高开发效率;
增强开发的灵活性,更新的便捷性;
减小与主程序的耦合度;
增强代码多用性;
阻碍用户做逆向工程(不用反编译);
自动化部署:
纯手工scp;
纯手工(git pull;svn update);
纯手工(xftp;rz);
缺陷:
全程运维参与,占用大量时间;
上线速度慢;
人为失误多(如rz -y会覆盖,管理混乱);
回滚慢,不及时;
运维标准化itil;
自动化安装cobbler;
监控体系zabbix;
配置管理saltstack;
自动化代码部署;
日志平台ELK Stack;
环境规划:
开发环境(开发本地有自己的环境,大家共用的服务(如DB、redis、memcached));
测试环境(功能测试;性能测试);
预生产环境(生产环境集群中的某一个节点担任,产生的原因(数据库不一致,测试环境和生产环境的数据肯定是不一样的,导致生产部署失败;使用生产环境的联调接口,如支付接口));
生产环境(直接对用户提供服务);
预生产环境-->生产环境-->灰度发布;
1、规划;
2、实现;
3、总结和扩展(PDCA);
4、在生产环境中应用;
用shell脚本实现自动化部署:
一个集群有10个node,实现一键部署这10个node;
一键回滚到任意版本;
一键回滚到上个版本;
部署:
代码在哪里(svn;git);
获取什么版本代码(直接拉取某个分支(svn;git);指定版本号(svn);指定tag(git));
配置文件未必一样(如应用内部的定时任务quartz.xml);
如何更新(tomcat需重启);
测试;
串行,并行(分组部署);
如何执行(bash script.sh;web界面);
差异解决:
各节点间差异;
代码仓库和实际的差异,配置文件是否放在代码中;
1、获取代码(直接拉取);
2、编译(可选);
3、配置文件;
4、打包;
5、scp到目标server;
6、将目标server移除集群;
7、解压;
8、放到webapps/下;
9、scp差异文件;
10、重启(可选);
11、测试;
12、加入集群;
注:
所有的web服务都应该使用普通用户,所有的web服务都不应该监听在80port(普通用户不能启用80port),除LB;
devops是过程、方法、系统的统称,让开发、运维、测试之间沟通的一种文化;
CI,continuous integration,让我们的软件、构建、测试、发布更敏捷、频繁、可靠,BUILD-->TEST-->RESULT;
continuous delivery,持续交付,在CI基础上,将集成后的代码部署到更贴近真实运行环境中(production-like environment),如,在完成单元测试后,可把代码部署到连接数据库的staging环境中,进行更多的测试,若代码没问题,可继续手动部署到生产环境中,(BUILD-->TEST-->RESULT)-->TEST-->STAGING-->PRODUCTION(manual);
continuous deployment,持续部署,在CD的基础上,把部署到生产环境的过程自动化;
continuous delivery&continuous deployment区别:最终部署到生产环境是自动化的,BUILD-->TEST-->RESULT-->TEST-->STAGING-->PRODUCTION(auto);
java构建(ant;gradle);
OWASP,open web application security project;
sonar,是一个用于代码质量管理的开放平台,通过插件机制,sonar可集成不同的测试工具、代码分析工具、持续集成工具;
与CI工具(jenkins)不同,sonar并不是简单的把不同的代码检查工具结果(findbugs、PMD)直接显示在web页面上,而是通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而方便的对不同规模和种类的工程进行代码质量管理;
在对其它工具的支持方面,sonar不仅提供了对IDE的支持,可在eclipse和intelliJIDE这些工具里联机查看结果,同时sonar还对大量的CI工具提供了接口支持,可方便的在CI中使用sonar,sonar的插件还可对java以外的其它编程语言提供支持,对国际化及报告文档化有良好的支持;
sonarqube-5.6.zip
sonar-scanner-2.6.1.zip
sonar-examples-master.zip
jenkins上安装SonarQube plugin;
安装示例(2.32.1 version,2.70以上版本需jdk1.8以上;tomcat中):
[[email protected] ~]# uname -rm
2.6.32-431.el6.x86_64 x86_64
[[email protected] ~]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 6.5(Santiago)
[[email protected] ~]# java -version
java version "1.5.0"
gij (GNU libgcj) version 4.4.7 20120313(Red Hat 4.4.7-17)
[[email protected] ~]# rpm -qa | grep java
java-1.5.0-gcj-1.5.0.0-29.1.el6.x86_64
libvirt-java-0.4.9-1.el6.noarch
libvirt-java-devel-0.4.9-1.el6.noarch
java_cup-0.10k-5.el6.x86_64
[[email protected] ~]# rpm -e --nodeps java-1.5.0-gcj-1.5.0.0-29.1.el6.x86_64
[[email protected] ~]# mkdir /ane
[[email protected] ~]# cd /ane
[[email protected] ane]# tar xf jdk-7u79-linux-x64.tar.gz
[[email protected] ane]# vim /etc/profile.d/java.sh
export JAVA_HOME=/ane/jdk1.7.0_79
export PATH=$PATH:$JAVA_HOME/bin
exportCLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
exportCATALINA_HOME=/ane/apache-tomcat-7.0.67
export PATH=$PATH:$CATALINA_HOME/bin
[[email protected] ane]# . !$
. /etc/profile.d/java.sh
[[email protected] ane]# java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build24.79-b02, mixed mode)
[[email protected] ane]# tar xf apache-tomcat-7.0.67.tar.gz
[[email protected] ane]# ln -sv apache-tomcat-7.0.67 tomcat
`tomcat‘ -> `apache-tomcat-7.0.67‘
[[email protected] ane]# cd tomcat/webapps/
[[email protected] webapps]# rm -rf ./*
[[email protected] webapps]# rz #(上传jenkins.war到该目录下)
[[email protected] webapps]# cd /ane
[[email protected] ane]# vim startup.sh
[[email protected] ane]# chmod +x startup.sh
[[email protected] ane]# vim tomcat/bin/catalina.sh
export JAVA_OPTS="
……
"
[[email protected] ane]# touch tomcat/logs/catalina.out
[[email protected] ane]# bash startup.sh
……
This may also be found at:/root/.jenkins/secrets/initialAdminPassword
*************************************************************
……
Apr 20, 2017 12:04:07 AMhudson.model.UpdateSite updateData
INFO: Obtained the latest update centerdata file for UpdateSource default
Apr 20, 2017 12:04:07 AMhudson.WebAppMain$3 run
INFO: Jenkins is fully up and running
[[email protected] ane]# cat/root/.jenkins/secrets/initialAdminPassword #默认安装在运行程序用户的家目录下,如/root/.jenkins/;
de00bab6d66a42deafda838e6a9c4d0d
Continue-->选建议配置的插件
admin/admin
“系统管理”
注:
解决:Your container doesn‘t use UTF-8 to decode URLs. If you usenon-ASCII characters as a job name etc, this will cause problems.
[[email protected] ane]# vimtomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
常用插件:
Git plugin
Git Parameter Plug-in
Gitlab Plugin
SSH plugin
AnsiColor #在jenkins console output上显示颜色;
Publish Over SSH #构建后操作Send build artifacts over SSH;
SaltStack plugin
注:
使用tomcat自身热部署的插件:
Deploy to container Plugin #构建后操作Deploy war/ear to a container;
WAR/EAR files #相对于当前项目的工作目录,/root/.jenkins/jobs/dj/
Context path #是部署到tomcat/webapps下的目录名字,此处写mypro,则会将war包部署到tomcat/webapps/mypro.war;
注:
#yum -y install salt-api
#vim /etc/salt/master.d/api.conf
#vim /etc/salt/master.d/eauth.conf
配置SVN:
]# mkdir -pv svn/{svndata,svnpasswd}
mkdir: created directory `svn‘
mkdir: created directory `svn/svndata‘
mkdir: created directory `svn/svnpasswd‘
]# svnserve -r /ane/svn/svndata/ -d
]# netstat -tnulp | grep :3690
tcp 0 0 0.0.0.0:3690 0.0.0.0:* LISTEN 22717/svnserve
]# svnadmin create /ane/svn/svndata/dj
]# tree /ane/svn
]# cp authz passwd /ane/svn/svnpasswd/
]# vim /ane/svn/svndata/dj/conf/svnserve.conf
[general]
anon-access = none
auth-access = write
password-db = /ane/svn/svnpasswd/passwd
authz-db = /ane/svn/svnpasswd/authz
]# vim /ane/svn/svnpasswd/passwd
[users]
root = jowin
dj = dj123
]# vim /ane/svn/svnpasswd/authz
[groups]
djgroup = root,dj
[dj:/]
@djgroup = rw
在win主机上上传文件;
在另一linxu主机checkout:
]# mkdir /ane/dj
]# svn checkout svn://172.168.101.229/dj/ane/dj/ --username=dj --password=dj123
]# ll -h /ane/dj/
total 54M
-rw-r--r-- 1 root root 54M Aug 10 15:36 ROOT(8).war
jenkins操作:
2.70 version;
/ane/tomcat/webapps/ROOT.war方式 #已将jenkins.war更名为ROOT.war;
额外安装插件:Ansible plugin、AnsiColor;
新建-->yt,构建一个自由风格的软件项目-->OK
General:
源码管理:
构建触发器:
Build periodically:H 12,20 * * *,每天12点和20点进行周期性的项目构建,无论代码有没有变化;
Poll SCM:H/10 * * * *,每隔10分钟根据SCM的版本号进行一次代码检查,如果有更新则checkout新代码,并进行自动构建;
构建环境:
构建:
ansible serve上操作:
ansible server到被管理主机作root和ane的ssh认证;
]# mkdir -pv /ane/ansible/{yt,dj}/
]# cd /ane/ansible/yt
]# mkdir -pv roles/{ini,ytcd,ytpda,ytquartz}/{tasks,files,templates,handlers,vars,meta}
]# cd /ane/ansible/dj/
]# mkdir -pv roles/dj/{tasks,files,templates,handlers,vars,meta}
]# vim /etc/ansible/hosts
[ytcd]
10.113.128.42
10.113.128.43
[ytpda]
10.113.128.37
10.113.128.38
[ytquartz]
10.113.128.41
[yt:children]
ytcd
ytpda
ytquartz
[dj]
10.113.128.114
]# pwd
/ane/ansible/dj
]# vim dj.yml
- hosts: dj
remote_user: ane
vars:
tomcat_root: /ane/tomcat/webapps
war_file: ROOT.war
bak_path: /ane/bak
war_path: /root/.jenkins/workspace/dj
tasks:
- name: backup war file
shell: cp {{ tomcat_root }}/{{ war_file }} {{ bak_path }}/{{ war_file}}_`date +%F_%T`
ignore_errors: True
- name: stop tomcat
shell: pkill -9 -f java
ignore_errors: True
tags:
- stop
- name: scp war
copy: src={{ war_path }}/{{ war_file }} dest={{ tomcat_root }}/
- name: start tocmat
shell: /bin/bash /ane/startup.sh
ignore_errors: True
补充:
[[email protected] ~]# docker search jenkins
[[email protected] ~]# docker pull jenkins
#docker run -d -p 49001:8080 -v $PWD/jenkins:/var/jenkins_home-t jenkins
$docker ps
$docker attach jenkins_ci
...#vim /etc/default/jenkins
HTTP_PORT=8080
...#/etc/init.d/jenkins start
...#su - jenkins
...$cd ; ls ; rm -rf mypython
...$virtualenv -p /usr/bin/python3.4--no-site-packages python3.4
...$cd python3.4/bin
...$pwd
jenkins的web界面:
系统管理-->管理插件-->可选插件,搜索并安装python和git
调整用git:project sentry-->配置(源码管理用Git)
调整python环境:
系统管理-->系统设置-->设一个全局变量(键PYTHON,值/var/lib/jenkins/python3.4/bin)
注:不要用系统自带的python环境,安装的包容易把系统的python环境污染,用pip3 install virtualenv工具把系统的python环境复制出来;
几个项目不要使用同一个python环境,要给每一个项目建一个python环境,这样层次更加分明,包也不易冲突;
$pip3 install virtualenv
$mkdir myproject ; cd !$
$virtualenv --no-site-packages venv #(新python环境放到当前目录的venv下)
$source venv/bin/activate #(进入python新环境)
...$pip install jinja2
...$deactivate #(退出当前python环境)
注:
在每个函数中写入echo "code_各自函数的名称",测试该script流程是否有问题;
测试锁时在code_get()函数中加入sleep 60,同时在其它终端执行该script;
$CODE_DIR此路径只用于放仓库文件,不能用于其它用途;
包名要重命名,规则:时间+版本号;
git pull #(拉取最新的代码;更新频繁,没有特别严格的项目管理);
git tag #(获取指定的标签版本;更新不频繁,有一定的项目管理的团队);
获取指定的commit id;
API_VERL=`git show | grep commit | cut -d ‘‘ -f2`
API_VER=`echo ${API_VERL:0:6}` #(截取前6位)
回滚:
方一:
列出回滚版本-->目标服务器移除集群-->执行回滚-->重启&测试-->加入集群;
方二:
列出回滚版本-->执行回滚(重启);
#vim deploy.sh
#!/bin/bash
#
#shell env
SHELL_DIR="/home/www"
SHELL_NAME="deploy.sh"
SHELL_LOG="{$SHELL_DIR}/${SHELL_NAME}.log"
#code var
PROJECT_NAME="web-demo"
CODE_DIR="/deploy/code/$PROJECT_NAME"
CONFIG_DIR="/deploy/config/$PROJECT_NAME"
TMP_DIR="/deploy/tmp"
TAR_DIR="/deploy/tar"
LOCK_FILE="/tmp/deploy.lock"
CDATE=`date "+%Y-%m-%d"`
CTIME=`date "+%H-%M-%S"`
#node list
NODE_LIST="172.17.101.1 171.17.101.2172.17.101.3"
usage() {
echo$"Usage: $0 [deploy|rollback [list|VERSION]]"
}
writelog() {
LOGINFO=$1
echo"${CDATE} ${CTIME}:${SHELL_NAME}:${LOGINFO}" >> ${SHELL_LOG}
}
shell_lock() {
touch$LOCK_FILE
}
shell_unlock() {
rm-f $LOCK_FILE
}
urltest() {
url=$1
curl-s --head $url | grep "200 OK"
if[ $? -ne 0 ];then
writelog "test error" &&exit;
fi
}
code_get() {
echo"code_get"
#sleep60
writelog"code_get"
cd$CODE_DIR && git pull
cp-r $CODE_DIR $TMP_DIR
API_VERL=`gitshow | grep commit | cut -d ‘ ‘ -f2`
API_VER=`echo${API_VERL:0:6}`
}
code_build() {
echo"code_build"
}
code_config() {
echo"code_config"
writelog"code_config"
/bin/cp-r ${CONFIG_DIR}/* ${TMP_DIR}/"${PROJECT_NAME}"
PKG_NAME="${PROJECT_NAME}_"$API_VER"_"${CDATE}_"${CTIME}"
cd${TMP_DIR} && mv ${PROJECT_NAME} $PKG_NAME
}
code_tar() {
echo"code_tar"
writelog"code_tar"
cd${TMP_DIR} && tar zcf ${PKG_NAME}.tar.gz ${PKG_NAME}
write_log"${PKG_NAME}.tar.gz"
}
code_scp() {
echo"code_scp"
fornode in $NODE_LIST;do
scp${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/
done
}
cluster_node_remove() {
echo"cluster_node_remove"
wirtelog"cluster_node_remove"
}
code_deploy() {
echo"code_deploy"
writelog"code_deploy"
fornode in $NODE_LIST;do
ssh $node "cd /opt/webroot && rm-f /webroot/* && tar zxf ${PKG_NAME}.tar.gz"
done
scp${CONFIG_DIR}/other/crontab.xml172.17.101.1:/opt/webroot/${PKG_NAME}/crontab.xml
ln-s /opt/webroot/${PKG_NAME} /webroot/web-demo
}
config_diff() {
echo"config_diff"
}
code_test() {
echo"code_test"
urltest"http://172.17.101.1/index.html"
}
cluster_node_in() {
echo"cluster_node_in"
}
rollback_fun() {
fornode in $ROLLBACK_LIST;do
if [ -d /opt/webroot/$1 ];then
ssh $node "rm -f /webroot/web-demo&& ln -s /opt/webroot/$1 /webroot/web-demo"
fi
done
}
rollback() {
echo"rollback"
if[ -z $1 ];then
shell_unlock
echo "hehe" && exit
fi
case$1 in
list)
ls -l /opt/webroot/*.tar.gz
;;
*)
rollback_fun $1
esac
}
main() {
if[ -f $LOCK_FILE ];then
echo "Deploy is running" && exit;
fi
DEPLOY_METHOD=$1
ROLLBACK_VER=$2
case$DEPLOY_METHOD in
deploy)
shell_lock;
code_get;
code_build;
code_config;
code_tar;
code_scp;
cluster_node_remove;
code_deploy;
config_diff;
code_test;
cluster_node_in;
shell_unlock;
;;
rollback)
shell_lock;
rollback$ROLLBACK_VER;
shell_unlock;
;;
*)
usage;
esac
}
main $1 $2