概念
java编写开源软件

作用
CI:持续集成、构建和测试各种项目
CI是目前最流行的应用程序开发实践方式
CI工具可以自动构建和自动测试代码程序,检测问题提供反馈

CD:持续交付
    Jenkins可以构建项目(需要编译的c、c++、java、go(这类代码本身无法像python、shell、php直接调用代码就能用,需要经过编译成项目才能使用))
    Jenkins可以做交付服务器,把项目给到应用服务器。需要在Jenkins服务器上部署http或者ftp服务

部署安装
环境:java
首先准备java环境,安装JDK
yum -y install java

安装
    方法一:
        软件包是rpm:官网https://jenkins.io/zh/download/
        rpm -ivh jenkins-2.190.1-1.1.noarch.rpm
        systemctl start/enable jenkins

    方法二:使用Jenkins的war包
        直接下载war包jenkins.war,官网https://jenkins.io/download
        可以使用命令直接运行war包就启动Jenkins:java -jar jenkins.war
            修改默认端口
                方法是在命令行后面添加--httpPort=8899这个参数就可以了,其实就是配置jetty的启动端口:
                nohup java -jar jenkins.war --httpPort=8899 &



    方法三:直接用容器镜像起jenkins

信息
    /usr/lib/jenkins/:jenkins安装目录,WAR包会放在这里。
    /etc/sysconfig/jenkins:jenkins配置文件,“端口”,“JENKINS_HOME”等都可以在这里配置。
    /var/lib/jenkins/:默认的JENKINS_HOME。
    /var/log/jenkins/jenkins.log:Jenkins日志文件

配置
    直接访问http://x.x.x.x:8080,Jenkins的默认端口是8080
        Jenkins访问页面
            需要管理员密码:Jenkins自动生成在jenkins的安装路径下\secrets\initialAdminPassword、默认用户admin
                
                | 


        自定义部署
            点击“选择插件安装”,再选“无”后安装
                因为Jenkins使用的是国外的安装站点,如果安装默认插件可能会需要很久,等安装结束后可以选择国内的站点

            点击“使用admin继续登录”,保存并完成,开始使用
            修改管理员密码
                页面右上角admin->configure->password->Save保存
                    



        安装插件
            改为国内镜像站点安装插件
                首页---Manage Jenkins系统管理---Manage Plugins插件管理 --- Advanced高级 ---- Update Site升级站点:地址(可以搜索Jenkins插件镜像:比如清华大学的https://mirrors.tuna.tsinghua.edu.cn/jenkins/) --- Submit
                    如果找到插件显示如图,是因为版本太低的问题
                        



            安装需要的插件:支持中文和支持git连接的插件
                Available可选插件 ->  搜索框ctrl+f-> 选中Localization: Chinese (Simplified)\和Git Parameter(直接连接git) -> Install without restart -> 勾选Restart Jenkins when installation is complete and no jobs are running安装后重启
                    一直报错安装插件失败,换了一种方法:官网搜索自己需要的插件,然后上传插件
                        进入https://plugins.jenkins.io/,搜索自己需要的插件,然后点击查找出来的,在点击右上角archives进入版本页面,最后选择版本下载xxx.hpi
                            注意:
                                可能需要重新起jenkins服务才会生效:包括换url或者换安装插件的方式
                                版本:0.9.11上传jenkins-2.190.3-1.1.noarch.rpm可以成功,但是上传jenkins-2.138.2-1.1.noarch.rpm版本失败

构建项目:CI/CD流程(使用git)
Jenkins服务器从git服务器上拉取代码,并构建成具体的项目
配置Jenkins下载gitlab代码
选择Item或者新建任务 ->任务名:website (自己定义,这个是者构建的项目的标签tag或者分支,是个变量,后面会被调用)/ Freestyle project -> 勾选This project is parameterized(这个项目是参数化的) -> 添加参数 -> Git Parameter(因为是git所以选择这个)=> Name: webver / Parameter Type: Branch or Tag (指开发写的代码是那个版本)/ Default Value: origin/master(分支) -> 源码管理 => Git => Repository URL: http://192.168.4.5/devops/website.git (这个是gitlab的项目路径)/ Branches to build:$webver(这个变量是指要构建的项目变量就是定义的Name) -> 保存

构建:选择哪个tag就构建哪个
        Build with Parameters ---选择相关的tag进行构建,构建完成后内容自动生成
            在还没有构建的时候在Jenkins下是看不到开发写的项目的,构建好了jenkins服务器上就会自动生成git服务器上的开发代码

            到这里实现了jenkins同步gitlab服务器上开发写的代码,等同于git clone的功能
            构建失败可以看控制台输出的错误信息,左下角有构建状态:蓝色圆点构建成功,红色圆点构建失败



    修改工程
        将程序代码下载到子目录中,现在不管是哪个tag构建后都是website
            配置 --- 源码管理  ---  	Additional Behaviours/checkout to a sub-direcotry:website-$webver  --- 保存测试
                


        对下载的程序代码进行打包管理,便于web应用服务器获取。在execute shell中配置好脚本代码之后,选择构架jenkins会自动做这些操作并生成结果在jnekins服务器上
            execute.shell里面可以写所有的系统及服务的命令,脚本,这里面的命令就是jenkins系统终端运行的
            

        构建后会生成对应的代码tar包、生成版本号文件(代码最新版依据)、Md5值文件
            要用ls -R才能看到

上线代码
自动部署上线:在web应用下执行脚本,可以实现自动部署程序应用
部署代码思路
软连接方式:好处可以随时改软链接的指定文件,等于可以随时更新和回滚版本
传统代码部署到目录下比较麻烦和应用不方便
可以直接在execute shell里面写,把代码传给应用服务器,最简单的
| #打包过程cd /var/lib/jenkins/workspace/test && zip -r web.war ./*
| #停止tomcat服务ssh -p2022 root@192.168.33.144 “systemctl stop tomcat”
| #拷贝压缩包到web服务器scp -P2022 web.war 192.168.33.144:/data/tomcat/webapps/testapp
| #代码替换ssh -p2022 root@192.168.33.144 “cd /data/tomcat/webapps/testapp && rm ./index.* && unzip web.war && rm -rf web.war”
| #启动tomcat服务ssh -p2022 root@192.168.33.144 “systemctl start tomcat”

代码python脚本如下:
            /var/www/download:保存下载的压缩包、/var/www/deploy:保存live_ver文件和解压目录、/var/www/html/nsd1906:指向发布的应用目录
            | import wget
            | import os
            | import requests
            | import hashlib
            | import tarfile
            |  
            | def has_new_ver(ver_url, ver_fname):
            |     '有新版本返回True,否则返回False'
            |     # 如果本地没有版本文件,则为True
            |     if not os.path.isfile(ver_fname):
            |         return True
            |  
            |     # 取出本地版本
            |     with open(ver_fname) as fobj:
            |         local_ver = fobj.read()
            |  
            |     # 本地版本与网上版本比较,如果不一致返回True
            |     r = requests.get(ver_url)
            |     if local_ver != r.text:
            |         return True
            |     else:
            |         return False
            |  
            | def file_ok(md5_url, fname):
            |     '如果文件已损坏返回False,否则返回True'
            |     # 计算本地文件的md5值
            |     m = hashlib.md5()
            |     with open(fname, 'rb') as fobj:
            |         while 1:
            |             data = fobj.read(4096)
            |             if not data:
            |                 break
            |             m.update(data)
            |  
            |     # 取出网上的md5值,进行比较
            |     r = requests.get(md5_url)
            |     if m.hexdigest() == r.text.strip():
            |         return True
            |     else:
            |         return False
            |  
            | def deploy(app_fname):
            |     '部署软件'
            |     deploy_dir = '/var/www/deploy'
            |     dest = '/var/www/html/nsd1906'
            |     # 解压
            |     tar = tarfile.open(app_fname)
            |     tar.extractall(path=deploy_dir)
            |     tar.close()
            |  
            |     # 取出软件目录名
            |     app_dir = app_fname.split('/')[-1]
            |     app_dir = app_dir.replace('.tar.gz', '')
            |     app_dir = os.path.join(deploy_dir, app_dir)
            |  
            |     # 如果目标链接文件已存在,先删除
            |     if os.path.exists(dest):
            |         os.remove(dest)
            |  
            |     # 创建软链接
            |     os.symlink(app_dir, dest)
            |  
            |  
            | if __name__ == '__main__':
            |     # 判断是否有新版本,没有则退出
            |     ver_url = 'http://192.168.4.6/deploy/live_ver'
            |     ver_fname = '/var/www/deploy/live_ver'
            |     if not has_new_ver(ver_url, ver_fname):
            |         print('未发现新版本。')
            |         exit(1)
            |  
            |     # 下载新版本软件
            |     r = requests.get(ver_url)
            |     ver = r.text.strip()  # 把额外的\n删除,得到版本
            |     app_url = 'http://192.168.4.6/deploy/pkgs/website-%s.tar.gz' % ver
            |     down_dir = '/var/www/download'
            |     wget.download(app_url, down_dir)
            |  
            |     # 校验。如果下载的文件已损坏,删除它
            |     md5_url = app_url + '.md5'
            |     app_fname = app_url.split('/')[-1]
            |     app_fname = os.path.join(down_dir, app_fname)
            |     if not file_ok(md5_url, app_fname):
            |         os.remove(app_fname)
            |         print('文件已损坏。')
            |         exit(2)
            |  
            |     # 部署软件
            |     deploy(app_fname)
            |  
            |     # 更新live_ver文件的版本
            |     if os.path.exists(ver_fname):
            |         os.remove(ver_fname)
            |  
            |     wget.download(ver_url, ver_fname)



回滚代码
    回滚思路:
        1、只需要把软链接指定到上一个版本(这部分脚本里面没有需要自己写)
            取出last_ver版本号,赋值给ver(因为之前下载的版本都在web应用服务器上)
            构建本地目录'/var/www/deploy/website-%s' % ver
            把/var/www/html/nsd1906删除
            创建软链接

        2、直接旧版本代码推送覆盖,等同于上线新版本