Jenkins And DSL

what is Jenkins

Jenkins Logo

Jenkins是一款由Java編寫(xiě)的開(kāi)源的持續(xù)集成工具珠月。

what is continuous integration

根據(jù) ThoughtWorks 的 Martin Fowler 的觀點(diǎn),持續(xù)集成(continuous
integration)是一種軟件開(kāi)發(fā)實(shí)踐背犯,要求團(tuán)隊(duì)成員經(jīng)常集成他們的工作翁垂。開(kāi)發(fā)人員每次代碼合并都會(huì)觸發(fā)持續(xù)集成服務(wù)器進(jìn)行自動(dòng)構(gòu)建膏秫,這個(gè)過(guò)程包括了編譯提佣、單元測(cè)試荒叼、集成測(cè)試涎劈、質(zhì)量分析等步驟广凸,通過(guò)自動(dòng)化的過(guò)程進(jìn)行驗(yàn)證阅茶,以盡快檢測(cè)集成錯(cuò)誤。這種方法會(huì)使得集成問(wèn)題大幅減少谅海,更快地實(shí)現(xiàn)有凝聚力的軟件開(kāi)發(fā)脸哀。

馬丁福勒

業(yè)界普遍認(rèn)同的持續(xù)集成的原則包括:

1)需要版本控制軟件保障團(tuán)隊(duì)成員提交的代碼不會(huì)導(dǎo)致集成失敗。常用的版本控制軟件有
Git扭吁、CVS撞蜂、Subversion 等;

2)開(kāi)發(fā)人員必須及時(shí)向版本控制庫(kù)中提交代碼侥袜,也必須經(jīng)常性地從版本控制庫(kù)中更新代碼到本地蝌诡;

3)需要有專(zhuān)門(mén)的集成服務(wù)器來(lái)執(zhí)行集成構(gòu)建。根據(jù)項(xiàng)目的具體實(shí)際枫吧,集成構(gòu)建可以被軟件的修改來(lái)直接觸發(fā)浦旱,也可以定時(shí)啟動(dòng),如每半個(gè)小時(shí)構(gòu)建一次九杂;

4)必須保證構(gòu)建的成功颁湖。如果構(gòu)建失敗,修復(fù)構(gòu)建過(guò)程中的錯(cuò)誤是優(yōu)先級(jí)最高的工作例隆。一旦修復(fù)甥捺,需要手動(dòng)啟動(dòng)一次構(gòu)建。

流程圖.png

Jenkins對(duì)持續(xù)集成的支持

Jenkins構(gòu)建觸發(fā)
Pipeline對(duì)部署的支持

what is DSL

Domain Specific Language 專(zhuān)門(mén)針對(duì) 一個(gè)特定的問(wèn)題領(lǐng)域
含有建模所需的語(yǔ)法和語(yǔ)義镀层,在與問(wèn)題域相同的抽象層次對(duì)概念建模

DSL示例

pipeline

           stage('docker build') {
                steps {
                    //生成代碼鏡像1
                    script {
                        customImage = docker.build(docker_tag)
                    }
                }
            }
            stage('push image') {
                steps {
                    script {
                        docker.withRegistry("https://harbor.xxx.xxx", 'harbor_xxx_net') {
                            customImage.push()
                        }
                    }
                }
            }

            stage('deploy') {
                steps {
                    script {
                        deployK8s config.GROUP, config.PROJECT_NAME, IMAGE_TAG, params.TARGET, config.TOKEN, true
                    }
                }
            }

geb

import geb.Browser

Browser.drive {
    go "http://myapp.com/login"

    assert $("h1").text() == "Please Login"

    $("form.login").with {
        username = "admin"
        password = "password"
        login().click()
    }

    assert $("h1").text() == "Admin Section"
}

gradle

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

why Jenkins need pipeline

pipeline可以進(jìn)入版本控制镰禾,以jenkinsfile的方式和源碼放在一起


Jenkinsfile

比起shell,代碼更強(qiáng)大鹿响,健壯羡微,容易復(fù)用,可以以面向?qū)ο蟮姆绞竭M(jìn)行開(kāi)發(fā)惶我、官方提供了大量與部署相關(guān)的方法妈倔、關(guān)鍵字

Jenkins-library

可以在發(fā)布過(guò)程中有更多控制手段

Input-demo

pipeline的基礎(chǔ)概念

Stage:
一個(gè)Pipeline可以劃分為若干個(gè)Stage,每個(gè)Stage代表一組操作绸贡。注意盯蝴,Stage是一個(gè)邏輯分組的概念,可以跨多個(gè)Node听怕。

Node:
一個(gè)Node就是一個(gè)Jenkins節(jié)點(diǎn)捧挺,或者是Master,或者是Agent尿瞭,是執(zhí)行Step的具體運(yùn)行期環(huán)境闽烙。

Step:
Step是最基本的操作單元,小到創(chuàng)建一個(gè)目錄,大到構(gòu)建一個(gè)Docker鏡像黑竞,由各類(lèi)Jenkins
Plugin提供捕发。

pipeline in Groovy

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'make'
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml'
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
}

DSL的種類(lèi)

外部DSL(external DSL)
在主程序語(yǔ)言之外,用一種單獨(dú)的語(yǔ)言表示領(lǐng)域?qū)S谜Z(yǔ)言很魂,是從零開(kāi)發(fā)的DSL扎酷,在詞法分析、解析技術(shù)遏匆、解釋法挨、編譯、代碼生成等方面擁有獨(dú)立的設(shè)施幅聘。開(kāi)發(fā)外部DSL近似于從零開(kāi)始實(shí)現(xiàn)一種擁有獨(dú)特語(yǔ)法和語(yǔ)義的全新語(yǔ)言(markdown凡纳、xml、html)
內(nèi)部DSL(internal DSL)
將現(xiàn)有的編程語(yǔ)言作為宿主語(yǔ)言喊暖,基于其設(shè)施建立專(zhuān)門(mén)面向特定領(lǐng)域的各種語(yǔ)義惫企。(rails,gradle陵叽,pipeline)

Groovy適合用來(lái)構(gòu)建DSL的原因

方法調(diào)用的語(yǔ)法

println('demo')
println 'demo'

運(yùn)算符重載

操作符重載

強(qiáng)大的閉包狞尔,函數(shù)當(dāng)作第一類(lèi)對(duì)象


class PizzaShop {
    def config = [:]

    def static order(closure) {
        PizzaShop shop = new PizzaShop()
        shop.with closure
    }

    def size(theSize) { println "size is $theSize" }

    def toppings(String[] theToppings) { println "Toppings received $theToppings" }

    def methodMissing(name, args) {

    }
}

PizzaShop.order {
    size 'Large'
    toppings 'Olives', 'Bell Pepper', 'Onions'
}

強(qiáng)大的元編程能力


use(groovy.time.TimeCategory) {
    //直接用數(shù)字的寫(xiě)法
    println 1.minute.from.now //一分鐘以后
    println 30.days.ago   //30天前的時(shí)間

    // 還可以與日期型的混用
    def someDate = new Date()
    println someDate - 3.months //三個(gè)月前的時(shí)間
}

Integer.metaClass.getDays { ->
    delegate
}

Integer.metaClass.getAgo { ->
    def today = Calendar.instance
    today.add(Calendar.DAY_OF_MONTH, -delegate)
    today
}

GregorianCalendar.metaClass.at { Double time ->
    def timeDbl = time.doubleValue()
    def hours=(int)timeDbl
    def minutes=(int)((timeDbl-hours)*100)
    delegate.set(Calendar.HOUR_OF_DAY,hours)
    delegate.set(Calendar.MINUTE,minutes)
    delegate.time
}

println 2.days.ago.at(4)

屬性賦值的優(yōu)雅


class Config {
    def map=[:]
    def methodMissing(String name,args){
        this.map[name]=args[0]
        println(args[0])
    }
    private String text;

    public String getMessage() {
        return "GET " + text;
    }

    public void setMessage(final String text) {
        this.text = "SET " + text
    }
}

config=new Config()
config.name "John"

println config.map['name']

def s2 = new Config(message: 'Groovy constructor')  // Named parameter in constructor.
assert 'GET SET Groovy constructor' == s2.getMessage()

def s3 = new Config()
s3.message = 'Groovy style'  // = assigment for property.
assert 'GET SET Groovy style' == s3.message  // get value with . notation.

可控的DSL沙盒


dsl = new File('./deploy.dsl')
def g = new GeneralBuildXml(xml)
def binding = new Binding()
binding.setProperty('exclude', new MethodClosure(g, 'exclude'))
binding.setProperty("out", new PrintWriter(new StringWriter()))
CompilerConfiguration conf = new CompilerConfiguration();
SecureASTCustomizer customizer = new SecureASTCustomizer();
customizer.with {
    closuresAllowed = true // 用戶(hù)能寫(xiě)閉包
    methodDefinitionAllowed = true // 用戶(hù)能定義方法
    importsWhitelist = [] // 白名單為空意味著不允許導(dǎo)入
    tokensWhitelist = [
            PLUS,
            EQUAL
    ].asImmutable()
    //將用戶(hù)所能定義的常量類(lèi)型限制為數(shù)值類(lèi)型
    constantTypesClassesWhiteList = [
            String.class,
            Object.class,
    ].asImmutable()
}
customizer.setReceiversWhiteList(Arrays.asList(
        "java.lang.Object"
));
conf.addCompilationCustomizers(customizer);
new GroovyShell(binding, conf).evaluate(dsl)

超出沙盒允許的調(diào)用將會(huì)失敗


exclude {
    dir "test"+"1", ".idea"
    file "composer.lock"
}
new File('demo').write('test')
java.lang.SecurityException: Method calls not allowed on [java.io.File]
    at org.codehaus.groovy.control.customizers.SecureASTCustomizer$SecuringCodeVisitor.visitMethodCallExpression(SecureASTCustomizer.java:924)
    at org.codehaus.groovy.ast.expr.MethodCallExpression.visit(MethodCallExpression.java:70)
    at org.codehaus.groovy.control.customizers.SecureASTCustomizer$SecuringCodeVisitor.visitExpressionStatement(SecureASTCustomizer.java:846)
    at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42)
    at org.codehaus.groovy.control.customizers.SecureASTCustomizer$SecuringCodeVisitor.visitBlockStatement(SecureASTCustomizer.java:806)
    at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:71)
    at org.codehaus.groovy.control.customizers.SecureASTCustomizer.call(SecureASTCustomizer.java:616)
    at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087)
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:631)
    at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:609)
    at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:586)
    at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:354)
    at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:87)
    at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:323)
    at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:320)
    at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:147)
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:318)
    at groovy.lang.GroovyShell.parseClass(GroovyShell.java:547)
    at groovy.lang.GroovyShell.parse(GroovyShell.java:559)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:443)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:491)
    at groovy.lang.GroovyShell$evaluate.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128)
    at main.run(main.groovy:42)
    at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:264)
    at groovy.lang.GroovyShell.run(GroovyShell.java:377)
    at groovy.lang.GroovyShell.run(GroovyShell.java:366)
    at groovy.ui.GroovyMain.processOnce(GroovyMain.java:589)
    at groovy.ui.GroovyMain.run(GroovyMain.java:332)
    at groovy.ui.GroovyMain.access$1400(GroovyMain.java:69)
    at groovy.ui.GroovyMain$GroovyCommand.process(GroovyMain.java:291)
    at groovy.ui.GroovyMain.processArgs(GroovyMain.java:134)
    at groovy.ui.GroovyMain.main(GroovyMain.java:116)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:114)
    at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:136)

1 error

小結(jié)

Jenkins在引入pipeline之后變得更加強(qiáng)大和易于維護(hù),也給我們一個(gè)啟示巩掺,當(dāng)我們?cè)谀骋粋€(gè)領(lǐng)域經(jīng)常需要解決重復(fù)性問(wèn)題時(shí)偏序,可以考慮實(shí)現(xiàn)一個(gè)
DSL 專(zhuān)門(mén)用來(lái)解決這些類(lèi)似的問(wèn)題。 而使用嵌入式 DSL
來(lái)解決這些問(wèn)題是一個(gè)非常好的辦法胖替,我們并不需要重新實(shí)現(xiàn)解釋器研儒,也可以利用宿主語(yǔ)言的抽象能力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末独令,一起剝皮案震驚了整個(gè)濱河市端朵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌燃箭,老刑警劉巖冲呢,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異招狸,居然都是意外死亡敬拓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)裙戏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乘凸,“玉大人,你說(shuō)我怎么就攤上這事累榜∮冢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)葛作。 經(jīng)常有香客問(wèn)我醒第,道長(zhǎng),這世上最難降的妖魔是什么进鸠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮形病,結(jié)果婚禮上客年,老公的妹妹穿的比我還像新娘。我一直安慰自己漠吻,他們只是感情好量瓜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著途乃,像睡著了一般绍傲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耍共,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天烫饼,我揣著相機(jī)與錄音,去河邊找鬼试读。 笑死杠纵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钩骇。 我是一名探鬼主播比藻,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼倘屹!你這毒婦竟也來(lái)了银亲?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤纽匙,失蹤者是張志新(化名)和其女友劉穎务蝠,沒(méi)想到半個(gè)月后奴紧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體闰非,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驱负,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年谊迄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拇派。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片电爹。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睦番,死狀恐怖喜爷,靈堂內(nèi)的尸體忽然破棺而出当窗,到底是詐尸還是另有隱情够坐,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站元咙,受9級(jí)特大地震影響梯影,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庶香,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一甲棍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赶掖,春花似錦感猛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至膳灶,卻和暖如春咱士,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轧钓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工序厉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毕箍。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓脂矫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親霉晕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庭再,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1、傳統(tǒng)我們的項(xiàng)目開(kāi)發(fā)模式是產(chǎn)品調(diào)研提出需求牺堰,開(kāi)發(fā)團(tuán)隊(duì)研究決定開(kāi)發(fā)方案選型拄轻。然后開(kāi)始一個(gè)周期的開(kāi)發(fā),模塊開(kāi)發(fā)完成之...
    張熙閱讀 12,400評(píng)論 2 42
  • Jenkins Pipleline插件介紹 ![sttp://upload-images.jianshu.io/u...
    燕京博士閱讀 3,192評(píng)論 0 11
  • 前言 Jenkins就不用做多余的介紹了伟葫,作為CI/CD首選的開(kāi)源解決方案恨搓,持續(xù)集成 (Continous Int...
    王奧OX閱讀 3,125評(píng)論 1 13
  • 教程:https://www.yiibai.com/jenkins/ 本節(jié)內(nèi)容: Jenkins介紹 安裝部署Je...
    達(dá)微閱讀 9,014評(píng)論 2 77
  • 這是十期網(wǎng)絡(luò)課程的最后一個(gè)作業(yè),也恰好是我的武林計(jì)劃的收官之作筏养。作業(yè)名為“開(kāi)啟英雄之旅”斧抱,但畫(huà)完后感覺(jué)更象是揚(yáng)帆啟...
    民大鐵老師閱讀 904評(píng)論 2 0