Jenkins 介绍


  • 例行介绍
  • pipeline的设计理念
  • 我司的最佳实践
  • 实际案例分析

The leading open source automation server, Jenkins provides hundreds of plugins to support building, deploying and automating any project.


为什么选择Jenkins


  • 社区活跃
  • 插件丰富

我们用Jenkins来做什么


  • build
  • deploy
  • crontab
  • tools

流水线设计原则(PIPELINE)

  1. 一次构建,多次使用
  2. 与业务逻辑松耦合
  3. 并行化原则
  4. 快速反馈优先
  5. 重要反馈优先

1
2
3
4
5
6
7
graph LR;
  A("source code")-->B("Build");
  B-->C("S3/OSS");
  B-->D("UT");
  D-->E("Dev Env");
  E-->F("QA");
  C-->G("Deploy Prod");

最佳实践

  • 权限
  • 架构
  • parameters
  • DSL && 共享库
  • 其他

权限

  • role-based strategy
  • Folder Authorization Strategy

权限规则

1
2
3
4
#正则示例
jws1.*
IT-jws1(/merge(/.*)?)?
IT-jws1(/register(/(merge.*|register-etcd))?)?

jenkins_permission_1


架构

  • node(master/slave)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
job.with {
    scm {
        git {
            remote {
                url GitUrl
                credentials(GitCredential)
            }
            branch GitBranchName
        }
    }
    label('your-node')
}

Parameters

  • choice
  • active choice
  • file
  • text
  • str

jenkins_param_text_1


jenkins_dynmic_parameter


jenkins_2


DSL && 共享库


DSL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
job('project-a/ansible-test6') {
    scm {
        git{
            remote{
                name('dada')
                url('git@172.21.0.11:/data/go-project/go-test')
                credentials('git-pass')
            }
	}
    }
    parameters {
        activeChoiceParam('foo') {
            description('Allows user choose from multiple choices')
            filterable()
            choiceType('SINGLE_SELECT')//https://jenkinsci.github.io/job-dsl-plugin/#path/job-parameters-activeChoiceParam-choiceType
            groovyScript {
                script('return["aaa","bbb","ccc"]')
                fallbackScript('"fallback choice"')
            }
        }
        activeChoiceParam('bar') {
            description('Allows user choose from multiple choices')
            filterable()
            choiceType('CHECKBOX')
            groovyScript {
                script('return["a3","b3","c3"]')
                fallbackScript('"fallback choice"')
            }
        }
    }
    steps {
        shell('echo $foo $bar')
    }
}

Groovy

1
2
import javaposse.jobdsl.dsl.DslFactory
import javaposse.jobdsl.dsl.Job

Github Jenkins DSL



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
helper_p.TiAnsibleBuildJob(
    job(job_name),
    'ansible-deploy/playbook/jws/ops-gamex-merge.yml',
    'gamex_merge'
    )
    {
      parameters {
          fileParam('merge.xlsx', 'Choice excel file')
          choiceParam('runType', ['merge','rollback'], '默认合服用,拆分选rollback')
          activeChoiceParam('tags') {
              choiceType('CHECKBOX')
              //SINGLE_SELECT, MULTI_SELECT, CHECKBOX, RADIO
              groovyScript {
                  script('''\
                  return [
                          'merge-tools-deploy',
                          'merge-tools-config',
                          'etcd-update',
                          'merge-tools-run',
                          'after-clean-db-merge',
                          'merge-log-dispose'
                         ].collect { "$it:selected" }
                      '''.stripIndent())
                //fallbackScript('"fallback choice"')
              }
            }
      }
    }

jenkins_dsl_groovy_1


共享库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(root)
+- src                     # Groovy source files
|   +- org
|       +- foo
|           +- Bar.groovy  # for org.foo.Bar class
+- vars
|   +- foo.groovy          # for global 'foo' variable
|   +- foo.txt             # help for 'foo' variable
+- resources               # resource files (external libraries only)
|   +- org
|       +- foo
|           +- bar.json    # static helper data for org.foo.Bar

重试机制&&邮件


Jenkins retry

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    publishers {
        retryBuild {
          rerunIfUnstable()
          retryLimit(3)
          progressiveDelay(60, 600)
          }
        extendedEmail {
            recipientList('gourds@yeah.net')
            defaultSubject('$DEFAULT_SUBJECT')
            replyToList('$DEFAULT_REPLYTO')
            defaultContent('${SCRIPT, template="groovy-html.template"}')
            contentType('text/html')
            triggers {
                stillFailing {
                    recipientList('gourds@yeah.net')
                }
            }
        }
    }

Ansible retry

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    - name: upload to ftp
      upload2ftp:
        ftp_host: "{{ ftp_host }}"
        ftp_port: "{{ ftp_port }}"
        ftp_user: "{{ ftp_user }}"
        ftp_pass: "{{ ftp_passwd }}"
        ftp_local_file: "{{ bdc_save_path }}/{{ item[0] }}_{{ assign_date_time }}.zip"
        ftp_remote_file: "{{ item[1] }}/"  #Must be dir
      register: ftp_results
      with_together:
        - "{{ log_type }}"
        - "{{ remote_path }}"
      retries: 2
      delay: 3
      until: ftp_results is success

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    publishers {
        extendedEmail {
            defaultSubject('激活码')
            replyToList('$DEFAULT_REPLYTO')
            contentType('text/html')
            defaultContent('''
<html>
<body>
<p>生成激活码见附件</p>
</body>
</html>
                          ''')
            attachmentPatterns('*.csv')
            contentType('text/html')
            triggers {
                success {
                  recipientList('${MailReceive}')
                  sendTo {
                      recipientList()
                   }

                }
            }
        }
    }