Skip to content

待办

什么是CI/CD

什么是CI/CD

概述

CI概述

book-ci

CI(Continuous Integration)

集成软件的过程不是新问题。在一个人开发的项目中,依赖外部系统又比较少的话,软件集成不会成为太大的问题,但是随着项目复杂度的增加(即使只增加一个人), 就会对集成和确保软件组件能够一起工作提出更多的要求一一要早集成,常集成。等到项目快结束时才来集成会导致各种各样的软件品质问题,解决这些问题代价很大,常常会导致项目延期。 CI以较小增量的方式迅速地解决这些风险。

在Martin Fowler热门的文章《Continuous Integration》(持续集成)中,他将CI描述为:

一种软件开发实践,即因队的成员经常集成他们的工作,通常每个成员每天至少集成一次一一这导致每天发生多次集成。每次集成都通过自动化的构建(包括测试)来验证,从而尽快地检测出集成错误。许多团队发现这个过程会大大减少集成问题,让团队能够更快地开发内聚的软件。

https://www.martinfowler.com/articles/continuousIntegration.html

根据我的经验,这意味着:

  • 所有开发者都先要在他们自己的工作站上执行私有构建,然后再将他们的代码提交到版本控制库中,从而确保他们的变更不会导致集成构建失败。
  • 开发者每天至少向版本控制库提交一次代码。
  • 集成构建每天在一台独立的计算机上进行多次。
  • 每次构建都必须100%通过测试。
  • 生成可以进行功能测试的产品(如WAR、配件、可执行程序等)。
  • 修复失败的构建是优先级最高的事情。
  • 某些开发者复查构建生成的报告,如编码标准报告和依赖分析报告,寻找可以改进的地方。

CD概述

book-cd

CD(Continuous Delivery)

至少对开发团队来说,该方法的基础是持续集成(CI)。CI使整个开发团队保持同步,消除了集成问题引起的延期。在几年前,Paul Duvall写了一本关于CI的书。但CI只是第一步。软件即使被成功地集成到了代码主干上,也仍旧是没有在生产环境中发挥作用的软件。David和Jez的书对从CI至"最后一公里"的问题进行了阐述,描述了如何构建部署流水线,才能将已集成的代码转变为已经部署到生产环境中的软件。

这种交付思想长期以来一直是软件开发中被人遗忘的角落,是开发人员和运维团队之间的一个黑洞。因此毫无疑问的是,本书中的技术都依赖于这些团队的凝聚,而这也就是悄然兴起的DevOps运动的前兆。这个过程也包括测试人员,因为测试工作也是确保无差错发布的关键因素。贯穿一切的是高度自动化,让事情能够很快完成而且没有差错。

Jenkins概述

jenkins-logo

https://www.jenkins.io

Jenkins是一款流行的开源持续集成(Continuous Integration)与持续部署(Continuous Delivery)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。

Jenkins的特征:

  • 开源的Java语言开发持续集成工具,支持持续集成,持续部署。
  • 易于安装部署配置:可通过yum安装,或下载war包以及通过docker容器等快速实现安装部署,可方便web界面配置管理。
  • 消息通知及测试报告:集成RSS/E-mail通过RSS发布构建结果或当构建完成时通过e-mail通知,生成JUnit/TestNG测试报告。
  • 分布式构建:支持Jenkins能够让多台计算机一起构建/测试。
  • 文件识别:Jenkins能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等。
  • 丰富的插件支持:支持扩展插件,你可以开发适合自己团队使用的工具,如git,svn,maven,docker等。

Jenkins入门

Jenkins安装

  • docker-compose.yml

    yml
    version: '3.9'
    services:
      jenkins:
        restart: always
        image: jenkins/jenkins:2.346.1-lts
        container_name: jenkins
        hostname: jenkins
        environment:
          TZ: 'Asia/Shanghai'
    #    ports:
    #      - '8080:8080'
    #      - '50000:50000'
        volumes:
          - './jenkins_home:/var/jenkins_home'
    
    networks:
      default:
        external: true
        name: global
  • log

    tex
    jenkins  | Running from: /usr/share/jenkins/jenkins.war
    jenkins  | webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
    jenkins  | 2021-06-15 03:49:00.234+0000 [id=1]    INFO    org.eclipse.jetty.util.log.Log#initialized: Logging initialized @568ms to org.eclipse.jetty.util.log.JavaUtilLog
    jenkins  | 2021-06-15 03:49:00.452+0000 [id=1]    INFO    winstone.Logger#logInternal: Beginning extraction from war file
    jenkins  | 2021-06-15 03:49:23.373+0000 [id=1]    WARNING    o.e.j.s.handler.ContextHandler#setContextPath: Empty contextPath
    jenkins  | 2021-06-15 03:49:23.523+0000 [id=1]    INFO    org.eclipse.jetty.server.Server#doStart: jetty-9.4.38.v20210224; built: 2021-02-24T20:25:07.675Z; git: 288f3cc74549e8a913bf363250b0744f2695b8e6; jvm 1.8.0_282-b08
    jenkins  | 2021-06-15 03:49:24.731+0000 [id=1]    INFO    o.e.j.w.StandardDescriptorProcessor#visitServlet: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
    jenkins  | 2021-06-15 03:49:24.961+0000 [id=1]    INFO    o.e.j.s.s.DefaultSessionIdManager#doStart: DefaultSessionIdManager workerName=node0
    jenkins  | 2021-06-15 03:49:24.961+0000 [id=1]    INFO    o.e.j.s.s.DefaultSessionIdManager#doStart: No SessionScavenger set, using defaults
    jenkins  | 2021-06-15 03:49:24.964+0000 [id=1]    INFO    o.e.j.server.session.HouseKeeper#startScavenging: node0 Scavenging every 600000ms
    jenkins  | 2021-06-15 03:49:27.405+0000 [id=1]    INFO    hudson.WebAppMain#contextInitialized: Jenkins home directory: /var/jenkins_home found at: EnvVars.masterEnvVars.get("JENKINS_HOME")
    jenkins  | 2021-06-15 03:49:28.154+0000 [id=1]    INFO    o.e.j.s.handler.ContextHandler#doStart: Started w.@600b0b7{Jenkins v2.277.1,/,file:///var/jenkins_home/war/,AVAILABLE}{/var/jenkins_home/war}
    jenkins  | 2021-06-15 03:49:28.271+0000 [id=1]    INFO    o.e.j.server.AbstractConnector#doStart: Started ServerConnector@58e1d9d{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
    jenkins  | 2021-06-15 03:49:28.271+0000 [id=1]    INFO    org.eclipse.jetty.server.Server#doStart: Started @28641ms
    jenkins  | 2021-06-15 03:49:28.273+0000 [id=21]    INFO    winstone.Logger#logInternal: Winstone Servlet Engine running: controlPort=disabled
    jenkins  | 2021-06-15 03:49:30.726+0000 [id=28]    INFO    jenkins.InitReactorRunner$1#onAttained: Started initialization
    jenkins  | 2021-06-15 03:49:30.879+0000 [id=26]    INFO    jenkins.InitReactorRunner$1#onAttained: Listed all plugins
    jenkins  | 2021-06-15 03:49:35.480+0000 [id=26]    INFO    jenkins.InitReactorRunner$1#onAttained: Prepared all plugins
    jenkins  | 2021-06-15 03:49:35.515+0000 [id=29]    INFO    jenkins.InitReactorRunner$1#onAttained: Started all plugins
    jenkins  | 2021-06-15 03:49:35.541+0000 [id=29]    INFO    jenkins.InitReactorRunner$1#onAttained: Augmented all extensions
    jenkins  | 2021-06-15 03:49:37.175+0000 [id=29]    INFO    jenkins.InitReactorRunner$1#onAttained: System config loaded
    jenkins  | 2021-06-15 03:49:37.176+0000 [id=27]    INFO    jenkins.InitReactorRunner$1#onAttained: System config adapted
    jenkins  | 2021-06-15 03:49:37.177+0000 [id=27]    INFO    jenkins.InitReactorRunner$1#onAttained: Loaded all jobs
    jenkins  | 2021-06-15 03:49:37.179+0000 [id=27]    INFO    jenkins.InitReactorRunner$1#onAttained: Configuration for all jobs updated
    jenkins  | 2021-06-15 03:49:37.234+0000 [id=42]    INFO    hudson.model.AsyncPeriodicWork#lambda$doRun$0: Started Download metadata
    jenkins  | 2021-06-15 03:49:37.371+0000 [id=42]    INFO    hudson.util.Retrier#start: Attempt #1 to do the action check updates server
    jenkins  | 2021-06-15 03:49:39.844+0000 [id=26]    INFO    jenkins.install.SetupWizard#init: 
    jenkins  | 
    jenkins  | *************************************************************
    jenkins  | *************************************************************
    jenkins  | *************************************************************
    jenkins  | 
    jenkins  | Jenkins initial setup is required. An admin user has been created and a password generated.
    jenkins  | Please use the following password to proceed to installation:
    jenkins  | 
    jenkins  | 10d16e7151dd4c1eb9f0786a4163544f
    jenkins  | 
    jenkins  | This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
    jenkins  | 
    jenkins  | *************************************************************
    jenkins  | *************************************************************
    jenkins  | *************************************************************
    jenkins  | 
    jenkins  | 2021-06-15 03:50:35.018+0000 [id=27]    INFO    jenkins.InitReactorRunner$1#onAttained: Completed initialization
    jenkins  | 2021-06-15 03:50:35.085+0000 [id=20]    INFO    hudson.WebAppMain$3#run: Jenkins is fully up and running

改密码

插件管理

修改Jenkins插件下载地址

  1. jenkins -> Manage Jenkins -> Manage Plugins -> Avaiable

    这样做的目的,是为了把Jenkins官方的插件列表下载到本地,接着修改地址文件,替换为国内插件地址

  2. 主机 -> 编辑 -> jenkins_home/updates/default.json

    bash
    sed 's~https://updates.jenkins.io/download~https://mirrors.tuna.tsinghua.edu.cn/jenkins~g' /var/jenkins_home/updates/default.json -i
    sed 's~http://www.google.com/~https://www.baidu.com~g' /var/jenkins_home/updates/default.json -i
  3. jenkins -> Manage Jenkins -> Manage Plugins -> Advanced -> Update Site

    tex
    https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
  4. jenkins -> 地址栏

    tex
    http://localhost:18080/restart

安装插件

  • 搜索插件

    jenkins -> Manage Jenkins -> Manage Plugins -> Available

    • 搜索勾选Role-based Authorization Strategy
    • 搜索勾选Build Authorization Token Root Plugin
    • 搜索勾选Credentials Binding
    • 搜索勾选Localization: Chinese (Simplified)
    • 搜索勾选Deploy to container
    • 搜索勾选Maven Integration
    • 搜索勾选Pipeline
    • 搜索勾选Gitlab Hook
    • 搜索勾选Email Extension Template
    • 搜索勾选SonarQube Scanner
    • 搜索勾选Publish Over SSH
    • 搜索勾选NodeJS
    • 搜索勾选Extended Choice Parameter
  • 安装插件

    Install without restart

授权管理

  1. Jenkins -> Manage Jenkins -> Security -> Configure Global Security -> Authorization

    选择Role-Based Strategy

  2. 配置角色

    Jenkins -> Manage Jenkins -> Security -> Manage and Assign Roles

    • Global roles
    • 创建base角色,指定overall-read权限

    管理员等高级用户可以创建基于全局的角色 Project roles(项目角色)

    针对某个或者某些项目的角色 Slave roles(奴隶角色):节点相关的权限

    • Item roles
    • 创建api角色,指定模式api-.*,指定权限全部勾选
    • 创建common角色,指定模式common-.*,指定权限全部勾选
  3. 配置用户

    Jenkins -> Manage Jenkins -> Security -> Manage Users -> Create User

    • 创建jack用户
    • 创建eric用户
  4. 分配角色

    Jenkins -> Manage Jenkins -> Security -> Manage and Assign Roles

    • Global roles
    • 添加jack用户,指定base角色
    • 添加eric用户,指定base角色
    • Item roles
    • 为jack分配api权限
    • 为eric分配common权限
  5. 创建项目

    Jenkins -> New Item

    • 创建api-future-weaver项目,指定Freestyle project类型
    • 创建common-future-weaver项目,指定Freestyle project类型
  6. 测试角色

    登录jack用户,查看权限

    登录eric用户,查看权限

创建 admin api token

凭据管理

  1. 添加凭据

    Jenkins -> Manage Jenkins -> Manage Credentials -> global -> 添加凭据

    可以添加的凭证有5种

    • Username with password

    用户名和密码

    • SSH Username with private key

    使用SSH用户和密钥

    • Secret file

    需要保密的文本文件,使用时Jenkins会将文件复制到一个临时目录中,再将文件路径 设置到一个变量中,等构建结束后,所复制的Secret file就会被删除。

    • Secret text

    需要保存的一个加密的文本串,如钉钉机器人或Github的api token

    • Certificate

    通过上传证书文件的方式

  2. 配置 Jenkins 项目使用凭据

    Jenkins -> New Item

    • 创建gitlab-test项目,指定Freestyle project类型

    Source Code Management ->填写git地址 -> 选择凭据

  3. 构建项目

    Build Now,查看输出结果

Maven整合

  1. Jenkins 全局工具配置

    Jenkins -> Manage Jenkins -> System Configure -> Global Tool Configuration -> Maven -> Add Maven

    • Name

    apache-maven-3.8.1

    • MAVEN_HOME

    /usr/local/apache-maven-3.8.1

  2. Jenkins 系统环境变量配置

    Jenkins -> Manage Jenkins -> System Configure -> Configure System -> Global properties -> environment variables -> Add

    • 添加M2_HOME环境变量
    • Name

    M2_HOME

    • Value

    /usr/local/apache-maven-3.8.1

    • 添加PATH+EXTRA环境变量
    • Name

    PATH+EXTRA

    • Value

    $M2_HOME/bin

  3. 配置 Jenkins 项目整合 maven

    Jenkins -> New Item

    • 创建maven-test项目,指定Freestyle project类型

    Build ->Add build step -> Execute shell

    shell
    mvn -version
  4. 构建项目

    Build Now,查看输出结果

Tomcat整合

Tomcat配置

  • $CATALINA_HOME/conf/tomcat-users.xml

    xml
    <tomcat-users>
        <role rolename="tomcat"/>
        <role rolename="role1"/>
        <role rolename="manager-script"/>
        <role rolename="manager-gui"/>
        <role rolename="manager-status"/>
        <role rolename="admin-gui"/>
        <role rolename="admin-script"/>
        <user username="tomcat" password="tomcat" roles="manager-gui,manager-script,tomcat,admin-gui,admin-script"/>
    </tomcat-users
  • $CATALINA_HOME/webapps/manager/META-INF/context.xml

    注释掉以下配置

    xml
    <!--
      <Valve className="org.apache.catalina.valves.RemoteAddrValve"
             allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
     -->
  • 重启 Tomcat

    bash
    shutdown.sh
    startup.sh
  • 测试用户权限

    登录http://localhost:8080/manager/html

自动化部署

  • Jenkins 建立 Pipeline 项目

    groovy
    pipeline {
        agent any
        stages {
            stage('拉取代码') {
                steps {
                    checkout([
                        $class: 'GitSCM', 
                        branches: [[name: '*/master']],
                        doGenerateSubmoduleConfigurations: false, 
                        extensions: [], 
                        submoduleCfg: [],
                        userRemoteConfigs: [[
                            credentialsId: 'gitlab-rainbow-weaver-auth-passsword', 
                            url:'git@192.168.66.100:future-weaver_group/web_demo.git'
                        ]]
                    ])
                }
            }
    
            stage('编译构建') {
                steps {
                    sh label: '',
                    script: 'mvn clean package'
                }
            }
    
            stage('项目部署') {
                steps {
                    deploy adapters: [
                        tomcat8(
                            credentialsId: '', 
                            path: 'tomcat-auth-password', 
                            url: 'http://tomcat:8080'
                        )
                    ], 
                    contextPath: null,
                    war: 'target/*.war'
                }
            }
        }
    }

Jenkins深入

Jenkins作业

Jenkins触发器

为jenkins容器安装expect命令

shell
docker exec -it -u root jenkins bash

apt-get clean
apt-get update
apt-get install expect

Build Triggers配置

勾选Trigger builds remotely选项

Authentication Token填写admin api token

Build配置

选择Execute Shell

shell
expect -c '
spawn ssh root@centos8
expect {
    "yes/no" { send "yes\r";exp_continue }
    "password:" {send "root\r" }
}
expect "#" { send "date >> /date.log\r" }
expect "#" { send "exit\r" }
expect eof'

测试远程触发构建

http://localhost:18080/buildByToken/build?job=api-future-weaver&token=11154ee0d840e41dc9e10841cae2395ff4