这是有关基于本地Docker compose堆栈的持续交付的一系列帖子中的最后一篇(请参阅此处的第一篇和第二篇文章 )。 在这篇文章中,我使用一个简单的Spring Boot项目来展示如何利用“ 管道作为代码 ”的概念。 请注意,这仅是示例,还有更多可能。 我使用的应用程序来自Spring Boot站点 。 Jenkinsfile的灵感来自于这篇文章中的内容,但是我不得不修改一些东西才能使其与我的堆栈一起使用。 我的项目的资源可以在这里找到。 我将在这篇文章中解释最重要的片段。
我使用的管道包含以下阶段:
建设阶段
在构建阶段,我使用GitLab插件检出项目的源代码。 我还将当前的commitId放在工作目录中的textFile中。 接下来,我使用Maven(在Jenkins配置中我们称为“ M3”的代码,如我在此处所述)打包代码。 我还要确保将commitId作为参数传递给Maven。
部署阶段
在部署步骤中,我通过将'true'发布到/ shutdown路径来关闭应用程序的运行实例。 然后,我只需运行上一步中构建的jar。 之后,作业将等待,直到应用程序响应简单的请求。
冒烟测试
在这个简单的测试步骤中,我将返回的已部署服务的commitId与我们签出最新提交的代码时得到的commitId进行了比较。 如果一切顺利,则这两个id应该匹配,如果链中没有什么地方出错了。
本示例仅此而已。 让我们看看这对源代码意味着什么。 由于这是一个Maven项目,因此我从pom.xml开始:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!-- used for metrics like status, health etc -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<!-- used for unit tests -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
该项目不需要特殊的依赖关系。 “ spring-boot-starter-web ”用于我们的REST控制器。 ' sprint-boot-starter-actuator '可用于检查运行状况等等 。
最后,使用“ spring-boot-starter-test”可以(对单元进行)单元测试。
让我们看一下Java源代码。 该应用程序仅启动Spring Boot应用程序。 Controller类也非常基础:
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
如您所见,当GET请求进入“ /”时,我只是返回一个固定的字符串。 测试类具有以下测试代码:
/**
* Created by pascal on 19/01/2017.
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}
我想这也很简单,我希望将固定字符串作为对GET请求的响应。 Java代码旁边是“ application.properties”文件:
server.port=8888
aaa@qq.com@
aaa@qq.com@
aaa@qq.com@
aaa@qq.com@
endpoints.shutdown.enabled=true
除了两个功能属性,我们正在其上运行应用程序的端口(8888),还具有通过调用端点来关闭应用程序的功能(endpoints.shutdown.enabled = true),其余的将在调用端点'/时显示。信息'。 因为我们过滤了资源,所以Maven将参数@…@替换为实际值:
...
<resources>
<!-- used for variable substitution in application.properties -->
<!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.3-Release-Notes#maven-resources-filtering -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
...
最后,我们在项目中有了Jenkinsfile:
import groovy.json.JsonSlurper;
properties([[$class: 'GitLabConnectionProperty', gitLabConnection: 'my-gitlab-connection']])
node{
stage 'Build, Test and Package'
env.PATH = "${tool 'M3'}/bin:${env.PATH}"
checkout scm
// workaround, taken from https://github.com/jenkinsci/pipeline-examples/blob/master/pipeline-examples/gitcommit/gitcommit.groovy
def commitid = sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
def workspacePath = pwd()
sh "echo ${commitid} > ${workspacePath}/expectedCommitid.txt"
withMaven(
maven: 'M3',
mavenSettingsConfig: 'a1adf035-653b-410d-b5a6-16b6da77b322',
mavenLocalRepo: '.repository') {
// Run the maven build
sh "mvn clean package -Dcommitid=${commitid}"
}
}
node{
stage 'Stop, Deploy and Start'
// shutdown
sh 'curl -X POST http://localhost:8888/shutdown || true'
// copy file to target location
sh 'cp target/*.jar /tmp/'
// start the application
sh 'nohup java -jar /tmp/*.jar &'
// wait for application to respond
sh 'while ! httping -qc1 http://localhost:8888 ; do sleep 1 ; done'
}
node{
stage 'Smoketest'
def workspacePath = pwd()
sh "curl --retry-delay 10 --retry 5 http://localhost:8888/info -o ${workspacePath}/info.json"
if (deploymentOk()){
return 0
} else {
return 1
}
}
def deploymentOk(){
def workspacePath = pwd()
expectedCommitid = new File("${workspacePath}/expectedCommitid.txt").text.trim()
actualCommitid = readCommitidFromJson()
println "expected commitid from txt: ${expectedCommitid}"
println "actual commitid from json: ${actualCommitid}"
return expectedCommitid == actualCommitid
}
def readCommitidFromJson() {
def workspacePath = pwd()
def slurper = new JsonSlurper()
def json = slurper.parseText(new File("${workspacePath}/info.json").text)
def commitid = json.app.commitid
return commitid
}
我之前描述了脚本的工作。 Jenkins安装必须匹配三个重要的常量:
- 在声明中:
properties([[$class: 'GitLabConnectionProperty', gitLabConnection: 'my-gitlab-connection']])
“ 我-gitlab连接 ”我给我的gitlabConnection在詹金斯为我描述了插件的名称相匹配这里 。 - 正如我在声明中的“ M3”之前所述:
env.PATH = "${tool 'M3'}/bin:${env.PATH}"
必须与Jenkins中的Maven安装相匹配,如我在此处所述。 - 最后一行是
mavenSettingsConfig: 'a1adf035-653b-410d-b5a6-16b6da77b322'
。 本ID这里提到的是一个复制的设置文件我设置了配置文件提供插件描述这里 。
这就是项目来源的全部。 接下来,让我向您展示如何在Jenkins中创建管道作业。 在仪表板中,选择创建“管道”类型的新作业:
接下来配置此作业,其中最重要的是使用从git获得的Jenkinsfile。 要配置它,我们必须使用用户名/密码登录到Gitlab(我在这里还没有找到使用Gitlab插件的方法。如果要将Jenkinsfiles与项目分开,也可以在此处使用另一个存储库。来源): 现在,当我运行作业时,它将在最后一步失败,并显示以下错误:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException:不允许脚本使用新的java.io.File java.lang.String
在org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.StaticWhitelist.rejectNew(StaticWhitelist.java:187)
…。
要使此工作成功运行,有一项最终设置。 默认情况下,管道作业不允许某些操作,因此我必须告诉Jenkins在这种情况下是允许的。
为此,请转到“管理Jenkins”并转到“进程内脚本批准”:
提到了可能的安全漏洞,您必须先批准该漏洞,然后作业才能执行操作: 在单击“批准”按钮后,重新运行该作业,将需要批准另一个漏洞才能使作业成功完成。 现在,构建将成功完成所有三个阶段,如仪表板所示: 到此结束了连续交付和流水线作为代码的示例。 如前所述,这只是管道的一个非常简单的示例,但是您可以使用它来入门该概念并从中获得更多收益。
翻译自: https://www.javacodegeeks.com/2017/03/pipeline-code-spring-boot-application.html