现有混合云平台的场景下,即有线下和线上的环境,又有测试与正式的场景,而且结合了Docker,导致打包内容有所区分,且服务的发布流程复杂起来,手工打包需要在编译阶段就要根据环境到处更改配置,因此纯手工发布增加了实施的难度,需要一个统一的适应各种环境部署的方案。

基于微服务的发布流程

  手动/自动构建 -> Jenkins 调度 K8S API ->动态生成 Jenkins Slave pod -> Slave pod 拉取 Git 代码/编译/打包镜像 ->推送到镜像仓库 Harbor -> Slave工作完成,Pod 自动销毁 ->部署到测试或生产 Kubernetes(K8S)平台。

  上面是理想状况下的将服务编译打包成镜像上传到镜像库后部署到Kubernetes平台的一个流程,但问题是:

  1. 我们有线上线下平台,代码在线下GitLab,是出不了外网的,因此线上K8S集群无法拉取代码编译。
  2. Jenkins的master所在服务器是CentOS6.5,没有Docker环境,也没有在K8S集群服务器内,因此无法直接执行docker build镜像和 kubectl apply 发布服务到K8S集群。
  3. Jenkins的slave节点都是无法访问外网的,
  4. 线上服务需要Pinpoint而线下环境暂时不需要启用Pinpoint,否则一直报错,因此需要根据选择的环境动态的构建Dockerfile,而且要求整个发布流程可选择。

就上面现实问题,我们将发布流程简化:

关键点:

Docker镜像的打包使用com.spotify的docker-maven-plugin插件结合Dockerfile,调用远程服务器的Docker环境生成镜像。

K8S服务部署采用的是ssh方式,将Deployment文件上传到K8S集群服务器,然后执行部署命令。

如何利用Dockerfile打包镜像

  之前也是用com.spotify的docker-maven-plugin插件来打包镜像并推送到私有镜像仓库,但问题是无法根据环境写条件判断,如动态选择是否需要启动pinpoint,线上线下库地址动态更换,导致镜像名前缀也是要动态变化的,此时直接配置无法满足,需要结合Dockerfile来实现。

先更改pom文件,指定本项目的Dockerfile文件地址,默认是放在项目根目录下:

  1. <plugin>
  2. <groupId>com.spotify</groupId>
  3. <artifactId>docker-maven-plugin</artifactId>
  4. <version>1.2.0</version>
  5. <configuration>
  6. <!--覆盖相同标签镜像-->
  7. <forceTags>true</forceTags>
  8. <!-- 与maven配置文件settings.xml一致 -->
  9. <serverId>nexus-releases</serverId>
  10. <!--私有仓库地址 -->
  11. <registryUrl>https://${docker.repostory}</registryUrl>
  12. <!--远程Docker地址 -->
  13. <dockerHost>http://10.3.87.210:2375</dockerHost>
  14. <!-- 注意imageName一定要是符合正则[a-z0-9-_.]的,否则构建不会成功 -->
  15. <!--指定镜像名称 仓库/镜像名:标签-->
  16. <imageName>${docker.repostory}/${project.artifactId}:${project.version}</imageName>
  17. <dockerDirectory>${project.basedir}</dockerDirectory>
  18. <resources>
  19. <resource>
  20. <!-- 指定要复制的目录路径,这里是当前目录 -->
  21. <!-- 将打包文件放入dockerDirectory指定的位置 -->
  22. <targetPath>/app/</targetPath>
  23. <!-- 指定要复制的根目录,这里是target目录 -->
  24. <directory>${project.build.directory}</directory>
  25. <!-- 指定需要拷贝的文件,这里指最后生成的jar包 -->
  26. <include>${project.build.finalName}.jar</include>
  27. </resource>
  28. </resources>
  29. </configuration>
  30. </plugin>

<registryUrl>https://${docker.repostory}</registryUrl>

指定远程仓库地址,在主项目的<properties>中指定,这里默认线上仓库<docker.repostory>39.95.40.97:5000</docker.repostory>

<dockerHost>http://10.3.87.210:2375</dockerHost>

指定Docker镜像打包服务器,这里指定线下服务器。

<imageName>${docker.repostory}/${project.artifactId}:${project.version}</imageName>

指定镜像名称 仓库/镜像名:标签

<dockerDirectory>${project.basedir}</dockerDirectory>

指定Dockerfile文件地址,此处指定项目根目录

Dockerfile内容

  1. FROM join:0.2
  2.  
  3. MAINTAINER {description} Join
  4.  
  5. ADD /app/{artifactId}-{version}.jar /app/
  6.  
  7. ENTRYPOINT ["java", "-Xmx512m","-Dspring.profiles.active={active}",{jarparam} "-jar", "/app/{artifactId}-{version}.jar"]

  基础镜像用join:0.2,里面包含了pinpoint和监控jvm的promethus客户端包。

  Jarparam会在Jenkins中动态替换运行时参数,active 指定当前运行环境,这里可能有人提议根据项目yml文件中指定内容自动匹配,因为要考虑到如果自动匹配 更换线上线下环境就需要更改yml配置文件后又要上传到gitlab,如此没有必要多做一步,直接在Jenkins中当作参数指定最为便捷。

  此处Dockerfile是通用模板,如果有特殊内容添加,可自行更改,此时的模板需要在Jenkins运行时替换参数后才有用,如果想直接在本机运行打包,可手动替换参数内容后运行:

  1. clean package -DskipTests docker:build

推送

  1. clean package -DskipTests docker:build -DpushImage

Jenkins发布流程

利用Jenkins的pipeline构建流水线

  Pipeline也就是构建流水线,对于程序员来说,最好的解释是:使用代码来控制项目的构建、测试、部署等。使用它的好处有很多,包括但不限于:

l  使用Pipeline可以非常灵活的控制整个构建过程;

l  可以清楚的知道每个构建阶段使用的时间,方便构建的优化;

l  构建出错,使用stageView可以快速定位出错的阶段;

l  一个job可以搞定整个构建,方便管理和维护等。

  Pipeline 支持两种语法,声明式和脚本式。这两种方法都支持构建持续交付流水线,都可以通过 web UI 或 Jenkinsfile 文件来定义 Pipeline(通常认为创建 Jenkinsfile 文件并上传到源代码控制仓库是最佳实践)

Jenkinsfile 就是一个包含对 Jenkins Pipeline 定义的文本文件,会上传到版本控制中。下面的 Pipeline 实现了基本的 3 段持续交付流水线。

声明式 Pipeline:

  1. // Jenkinsfile (Declarative Pipeline)
  2.  
  3. pipeline {
  4.  
  5. agent any
  6.  
  7. stages {
  8.  
  9. stage('Build') {
  10.  
  11. steps {
  12.  
  13. echo 'Building..'
  14.  
  15. }
  16.  
  17. }
  18.  
  19. stage('Test') {
  20.  
  21. steps {
  22.  
  23. echo 'Testing..'
  24.  
  25. }
  26.  
  27. }
  28.  
  29. stage('Deploy') {
  30.  
  31. steps {
  32.  
  33. echo 'Deploying....'
  34.  
  35. }
  36.  
  37. }
  38.  
  39. }
  40.  
  41. }

对应的脚本式 Pipeline:

  1. // Jenkinsfile (Scripted Pipeline)
  2.  
  3. node {
  4.  
  5. stage('Build') {
  6.  
  7. echo 'Building....'
  8.  
  9. }
  10.  
  11. stage('Test') {
  12.  
  13. echo 'Building....'
  14.  
  15. }
  16.  
  17. stage('Deploy') {
  18.  
  19. echo 'Deploying....'
  20.  
  21. }
  22.  
  23. }

  注意,所有的 Pipeline 都会有这三个相同的 stage,可以在所有项目的一开始就定义好它们。下面演示在 Jenkins 的测试安装中创建和执行一个简单的 Pipeline。

  假设项目已经设置好了源代码控制仓库,并且已经按照入门章节的描述在 Jenkins 中定义好了 Pipeline。

  使用文本编辑器(最好支持 Groovy 语法高亮显示),在项目根目录中创建 Jenkinsfile。

  上面的声明式 Pipeline 示例包含了实现一个持续交付流水线所需的最少步骤。必选指令 agent 指示 Jenkins 为 Pipeline 分配执行程序和工作空间。没有 agent 指令的话,声明式 Pipeline 无效,无法做任何工作!默认情况下 agent 指令会确保源代码仓库已经检出,并且可用于后续步骤。

  stage 和 step 指令在声明式 Pipeline 中也是必须的,用于指示 Jenkins 执行什么及在哪个 stage 中执行。

  对于脚本式 Pipeline 的更高级用法,上面的示例节点是至关重要的第一步,因为它为 Pipeline 分配了一个执行程序和工作空间。如果没有 node,Pipeline 不能做任何工作!在 node 内,业务的第一阶段是检出此项目的源代码。由于 Jenkinsfile 是直接从源代码控制中提取的,因此 Pipeline 提供了一种快速简单的方法来访问源代码的正确版本:

// Jenkinsfile (Scripted Pipeline)

node {

checkout scm

/* .. snip .. */

}

这个 checkout 步骤会从源代码控制中检查代码,scm 是特殊变量,它指示运行检出步骤,复制触发了这次 Pipeline 运行的指定版本。

最终的流程样式:

  一般用声明式来构建流水,实际操作过程中还是发现脚本式构建更顺手,而且Groovy语言更方便查资料,因此下面以脚本构建为主演示一个流程。

1.新建任务

2.填写任务名和描述,由于防止构建历史太多,只保留3个。

3.添加构建时全局构建参数,用来构建流程动态选择环境,这里有两种方式,一种是直接在页面上添加,如下图,一种是在Jenkinsfile中添加(第一次构建时不会出现选项,第二次构建才会出现,因此首次构建需要试构建,暂停再刷新页面才会有选择框),两种最张效果一样,这里为了方便采用Jenkinsfile来添加全局参数。

Jenkinsfile中添加

  1. properties([
  2. parameters([string(name: 'PORT', defaultValue: '7082', description: '程序运行端口'),choice(name: 'ACTIVE_TYPE', choices: ['dev', 'prd', 'local'], description: '程序打包环境'),choice(name: 'ENV_TYPE', choices: ['online', 'offline'], description: '线上、还是线下环境'),booleanParam(name: 'ON_PINPOINT', defaultValue: true, description: '是否添加Pinpoint监控'),booleanParam(name: 'ON_PROMETHEUS', defaultValue: true, description: '是否添加Prometheus监控'),string(name: 'EMAIL', defaultValue: '104@qq.com', description: '打包结果通知')])
  3. ])

4.选择源码代码库:

需要添加认证,将Jenkins的ssh秘钥添加到GitLab的页面中,且需要将此处gitlab中joint用户添加到需要拉取代码的项目中才有权限拉取代码。

Jenkinsfile位置放在项目的根目录。

5. Jenkinsfile中指定maven目录地址

MVNHOME = '/opt/maven354'

为防止手工填写项目名和版本号等一系列信息,因此直接读取pom文件中要编译项目的这些信息给全局变量:

  1. pom = readMavenPom file: 'pom.xml'
  2. echo "group: ${pom.groupId}, artifactId: ${pom.artifactId}, version: ${pom.version} ,description: ${pom.description}"
  3. artifactId = "${pom.artifactId}"
  4. version = "${pom.version}"
  5. description = "${pom.description}"

根据选择的线上环境还是线下环境,替换镜像仓库ip

  1. if (params.ENV_TYPE == 'offline' || params.ENV_TYPE == null) {
  2. sh "sed -i 's#39.95.40.97:7806#10.3.87.51:8080#g' pom.xml"
  3. image = "10.3.87.51:8080/${artifactId}:${version}"
  4. }

6.编译

利用maven构建,利用上面的内容先替换掉Dockerfile、Deployment中的变量,再根据选择的条件是否启用pinpoint和promethus,最后编译。

  1. def jarparam=''
  2. def pinname = artifactId
  3. if( pinname.length() > 23) {
  4. pinname = artifactId.substring(0,23)
  5. }
  6. //添加pinpoint
  7. if(params.ON_PINPOINT) {
  8. jarparam = '"-javaagent:/app/pinpoint-agent/pinpoint-bootstrap-1.8.0.jar","-Dpinpoint.agentId={pinname}", "-Dpinpoint.applicationName={pinname}",'
  9. }
  10. //添加prometheus
  11. if(params.ON_PROMETHEUS) {
  12. jarparam = jarparam + '"-javaagent:/app/prometheus/jmx_prometheus_javaagent-0.11.0.jar=1234:/app/prometheus/jmx.yaml",'
  13. }
  14.  
  15. sh "sed -i 's#{jarparam}#${jarparam}#g' Dockerfile"
  16.  
  17. sh "sed -i 's#{description}#${description}#g;s#{artifactId}#${artifactId}#g;s#{version}#${version}#g;s#{active}#${params.ACTIVE_TYPE}#g;s#{pinname}#${pinname}#g' Dockerfile"
  18.  
  19. sh "sed -i 's#{artifactId}#${artifactId}#g;s#{version}#${version}#g;s#{port}#${params.PORT}#g;s#{image}#${image}#g' Deployment.yaml"
  20.  
  21. sh "'${MVNHOME}/bin/mvn' -DskipTests clean package"

需要注意的是pinpoint的pinpoint.applicationName不能操作24个字符,否则启用不成功,因此超过的直接截断。

Department文件详情看后文。

跳过测试编译打包 '${MVNHOME}/bin/mvn' -DskipTests clean package 需要在Jenkins服务器安装maven环境,还有指定maven的jar包私有仓库地址。

7. Docker打包

前提是上一步指定pom文件中的镜像仓库和Dockerfile中的内容是替换后的完整内容。

sh "'${MVNHOME}/bin/mvn' docker:build"

8. 推送镜像

sh "'${MVNHOME}/bin/mvn' docker:push"

如何发布服务到K8S集群

  前面几步已经将项目打包并生成了镜像并推送到了私有仓库,下面就是部署服务到K8S集群。

先看看Department.yaml文件:

  1. ---
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: {artifactId}
  6. namespace: default
  7. labels:
  8. app: {artifactId}
  9. version: {version}
  10. spec:
  11. selector:
  12. matchLabels:
  13. app: {artifactId}
  14. replicas: 1
  15. template:
  16. metadata:
  17. labels:
  18. app: {artifactId}
  19. annotations:
  20. prometheus.io.jmx: "true"
  21. prometheus.io.jmx.port: ""
  22. spec:
  23. containers:
  24. - name: {artifactId}
  25. image: {image}
  26. # IfNotPresent\Always
  27. imagePullPolicy: Always
  28. ports:
  29. - name: prometheusjmx
  30. containerPort: 1234
  31. livenessProbe: #kubernetes认为该pod是存活的,不存活则需要重启
  32. httpGet:
  33. path: /health
  34. port: {port}
  35. scheme: HTTP
  36. initialDelaySeconds: 60 ## 设置为系统完全启动起来所需的最大时间+若干秒
  37. timeoutSeconds: 5
  38. successThreshold: 1
  39. failureThreshold: 5
  40. readinessProbe: #kubernetes认为该pod是启动成功的
  41. httpGet:
  42. path: /health
  43. port: {port}
  44. scheme: HTTP
  45. initialDelaySeconds: 40 ## 设置为系统完全启动起来所需的最少时间
  46. timeoutSeconds: 5
  47. successThreshold: 1
  48. failureThreshold: 5
  49. env:
  50. - name: eureka-server
  51. value: "eureka-server.default.svc.cluster.local"
  52. - name: eureka-server-replica
  53. value: "eureka-server-replica.default.svc.cluster.local"
  54. resources:
  55. # 5%的CPU时间和700MiB的内存
  56. requests:
  57. # cpu: 50m
  58. memory: 700Mi
  59. # 最多允许它使用
  60. limits:
  61. # cpu: 100m
  62. memory: 1000Mi
  63. # 指定在容器中挂载路径
  64. volumeMounts:
  65. - name: logs-volume
  66. mountPath: /logs
  67. - name: host-time
  68. mountPath: /etc/localtime
  69. readOnly: true
  70. - name: host-timezone
  71. mountPath: /etc/timezone
  72. readOnly: true
  73. - name: pinpoint-config
  74. mountPath: /app/pinpoint-agent/pinpoint.config
  75. volumes:
  76. - name: logs-volume
  77. hostPath:
  78. # 宿主机上的目录
  79. path: /logs
  80. - name: host-time
  81. hostPath:
  82. path: /etc/localtime
  83. - name: host-timezone
  84. hostPath:
  85. path: /usr/share/zoneinfo/Asia/Shanghai
  86. - name: pinpoint-config
  87. configMap:
  88. name: pinpoint-config
  89. # 运行在指定标签的节点,前提是先给节点打标 kubectl label nodes 192.168.0.113 edgenode=flow
  90. # nodeSelector:
  91. # edgenode: flow
  92. ---
  93. apiVersion: v1
  94. kind: Service
  95. metadata:
  96. name: {artifactId}
  97. namespace: default
  98. labels:
  99. app: {artifactId}
  100. version: {version}
  101. spec:
  102. selector:
  103. app: {artifactId}
  104. ports:
  105. - name: tcp-{port}-{port}
  106. protocol: TCP
  107. port: {port}
  108. targetPort: {port}

里面的变量会在前面几步自动替换掉。

添加了prometheus收集jvm的内容:

prometheus.io.jmx: "true"
prometheus.io.jmx.port: "1234"

containerPort: 1234

将pinpoint的配置内容pinpoint.config用configMap  保存,方便更改内容。

其它内容不在此详解,可自行google。

  网上资料一般发布服务都是直接kubectl
deploy,这种情况只适用于jenkins的服务器已包含在K8S服务器集群中。第二种情况是在K8S集群服务器里面生成Jenkins的一个slave节点,然后在pipeline里面设置node(“k8s”){ ……} 里面发布,具体方法自行google。

这里为了避免麻烦,采用直接SSH到K8S服务器集群的方案发布服务。

配置sshagent

SSH Agent Plugin :sshagent方法支持,用于上传构建产物到目标服务器,使用详情见:

https://wiki.jenkins.io/display/JENKINS/SSH+Agent+Plugin

在Jenkins插件库搜索后直接下载安装(需要连外网环境),生产环境已安装,直接使用。

使用:

  1. sshagent(credentials: ['deploy_ssh_key_23']) {
  2. sh "scp -P 2222 -r Deployment.yaml root@39.95.40.97:/docker/yaml/Deployment-${artifactId}.yaml"
  3. sh "ssh -p 2222 root@39.95.40.97 'kubectl apply -f /docker/yaml/Deployment-${artifactId}.yaml && kubectl set env deploy/${artifactId} DEPLOY_DATE=${env.BUILD_ID}'"
  4. }

先用ssh远程到K8S集群中的服务器,将Deployment文件上传,然后再远程执行kubectl apply发布服务。

为了避免误操作,在发布前做了发布确认提示判断。

timeout(time: 10, unit: 'MINUTES') {
   input '确认要部署吗?'
}

以上流程已完成整个流程,然后可以去K8S环境去看服务是否有正常运行。

关于测试

  上面的过程没有加入代码测试、代码质量分析SonarQube、发布后服务测试的阶段(Selenium是一套完整的Web应用程序测试系统http://www.51testing.com/zhuanti/selenium.html),后续可以接入。

如何进行多模块如何构建

  很多项目采用的是多模块构成,因此每个项目配置和发布要求不一样,需要单独编译到部署,所以每个模块都需要独立的Dockerfile和Deployment文件,Jenkinsfile通用一份,然后在发布时自动弹出模块列表,选择需要发布的模块进行编译发布。

  1. //需要处理的项目多项目时先进入子项目
  2.  
  3. projectwk = "."
  4.  
  5. mainpom = readMavenPom file: 'pom.xml'
  6.  
  7. //存在多个模块时,选择其中一个进行编译
  8.  
  9. if(mainpom.modules.size() > 0 ) {
  10.  
  11. echo "项目拥有模块==${mainpom.modules}"
  12.  
  13. timeout(time: 10, unit: 'MINUTES') {
  14.  
  15. def selproj = input message: '请选择需要处理的项目', parameters: [choice(choices: mainpom.modules, description: '请选择需要处理的项目', name: 'selproj')] //, submitterParameter: 'project'
  16.  
  17. projectwk = selproj
  18.  
  19. echo "选择项目=${projectwk}"
  20.  
  21. }
  22.  
  23. }

读取主项目的pom中的modules判断是否包含多个模块,供用户选择。

然后根据选择的模块进行编译,dir进入选择的模块读取信息并编译。

  1. dir("${projectwk}") {
  2.  
  3. pom = readMavenPom file: 'pom.xml'
  4.  
  5. echo "group: ${pom.groupId}, artifactId: ${pom.artifactId}, version: ${pom.version} ,description: ${pom.description}"
  6.  
  7. artifactId = "${pom.artifactId}"
  8.  
  9. version = "${pom.version}"
  10.  
  11. description = "${pom.description}"
  12.  
  13. }

完整的Jenkinsfile

  1. properties([
  2.  
  3. parameters([string(name: 'PORT', defaultValue: '', description: '程序运行端口'),choice(name: 'ACTIVE_TYPE', choices: ['dev', 'prd', 'local'], description: '程序打包环境'),choice(name: 'ENV_TYPE', choices: ['online', 'offline'], description: '线上、还是线下环境'),booleanParam(name: 'ON_PINPOINT', defaultValue: true, description: '是否添加Pinpoint监控'),booleanParam(name: 'ON_PROMETHEUS', defaultValue: true, description: '是否添加Prometheus监控'),string(name: 'EMAIL', defaultValue: '1041126478@qq.com', description: '打包结果通知')])
  4.  
  5. ])
  6.  
  7. node {
  8.  
  9. stage('Prepare') {
  10.  
  11. echo "1.Prepare Stage"
  12.  
  13. MVNHOME = '/opt/maven354'
  14.  
  15. //echo "UUID=${UUID.randomUUID().toString()}"
  16.  
  17. checkout scm
  18.  
  19. //需要处理的项目多项目时先进入子项目
  20.  
  21. projectwk = "."
  22.  
  23. mainpom = readMavenPom file: 'pom.xml'
  24.  
  25. repostory = "${mainpom.properties['docker.repostory']}"
  26.  
  27. //存在多个模块时,选择其中一个进行编译
  28.  
  29. if(mainpom.modules.size() > 0 ) {
  30.  
  31. echo "项目拥有模块==${mainpom.modules}"
  32.  
  33. timeout(time: 10, unit: 'MINUTES') {
  34.  
  35. def selproj = input message: '请选择需要处理的项目', parameters: [choice(choices: mainpom.modules, description: '请选择需要处理的项目', name: 'selproj')] //, submitterParameter: 'project'
  36.  
  37. projectwk = selproj
  38.  
  39. echo "选择项目=${projectwk}"
  40.  
  41. }
  42.  
  43. }
  44.  
  45. dir("${projectwk}") {
  46.  
  47. pom = readMavenPom file: 'pom.xml'
  48.  
  49. echo "group: ${pom.groupId}, artifactId: ${pom.artifactId}, version: ${pom.version} ,description: ${pom.description}"
  50.  
  51. artifactId = "${pom.artifactId}"
  52.  
  53. version = "${pom.version}"
  54.  
  55. description = "${pom.description}"
  56.  
  57. }
  58.  
  59. script {
  60.  
  61. GIT_TAG = sh(returnStdout: true, script: '/usr/local/git/bin/git rev-parse --short HEAD').trim()
  62.  
  63. echo "GIT_TAG== ${GIT_TAG}"
  64.  
  65. }
  66.  
  67. image = "192.168.4.2:5000/${artifactId}:${version}"
  68.  
  69. if (params.ENV_TYPE == 'offline' || params.ENV_TYPE == null) {
  70.  
  71. sh "sed -i 's#39.95.40.97:5000#10.3.80.50:5000#g' pom.xml"
  72.  
  73. image = "10.3.80.50:5000/${artifactId}:${version}"
  74.  
  75. }
  76.  
  77. }
  78.  
  79. if(mainpom.modules.size() > 0 ) {
  80.  
  81. stage('编译总项目') {
  82.  
  83. sh "'${MVNHOME}/bin/mvn' -DskipTests clean install"
  84.  
  85. }
  86.  
  87. }
  88.  
  89. dir("${projectwk}") {
  90.  
  91. stage('编译模块') {
  92.  
  93. echo "2.编译模块 ${artifactId}"
  94.  
  95. def jarparam=''
  96.  
  97. def pinname = artifactId
  98.  
  99. if( pinname.length() > 23) {
  100.  
  101. pinname = artifactId.substring(0,23)
  102.  
  103. }
  104.  
  105. //添加pinpoint
  106.  
  107. if(params.ON_PINPOINT) {
  108.  
  109. jarparam = '"-javaagent:/app/pinpoint-agent/pinpoint-bootstrap-1.8.0.jar","-Dpinpoint.agentId={pinname}", "-Dpinpoint.applicationName={pinname}",'
  110.  
  111. }
  112.  
  113. //添加prometheus
  114.  
  115. if(params.ON_PROMETHEUS) {
  116.  
  117. jarparam = jarparam + '"-javaagent:/app/prometheus/jmx_prometheus_javaagent-0.11.0.jar=1234:/app/prometheus/jmx.yaml",'
  118.  
  119. }
  120.  
  121. sh "sed -i 's#{jarparam}#${jarparam}#g' Dockerfile"
  122.  
  123. sh "sed -i 's#{description}#${description}#g;s#{artifactId}#${artifactId}#g;s#{version}#${version}#g;s#{active}#${params.ACTIVE_TYPE}#g;s#{pinname}#${pinname}#g' Dockerfile"
  124.  
  125. sh "sed -i 's#{artifactId}#${artifactId}#g;s#{version}#${version}#g;s#{port}#${params.PORT}#g;s#{image}#${image}#g' Deployment.yaml"
  126.  
  127. sh "'${MVNHOME}/bin/mvn' -DskipTests clean package"
  128.  
  129. stash includes: 'target/*.jar', name: 'app'
  130.  
  131. }
  132.  
  133. stage('Docker打包') {
  134.  
  135. echo "3.Docker打包"
  136.  
  137. unstash 'app'
  138.  
  139. sh "'${MVNHOME}/bin/mvn' docker:build"
  140.  
  141. }
  142.  
  143. stage('推送镜像') {
  144.  
  145. echo "4.Push Docker Image Stage"
  146.  
  147. sh "'${MVNHOME}/bin/mvn' docker:push"
  148.  
  149. }
  150.  
  151. timeout(time: 10, unit: 'MINUTES') {
  152.  
  153. input '确认要部署吗?'
  154.  
  155. }
  156.  
  157. stage('发布') {
  158.  
  159. if (params.ENV_TYPE == 'offline' || params.ENV_TYPE == null) {
  160.  
  161. sshagent(credentials: ['deploy_ssh_key_34']) {
  162.  
  163. sh "scp -r Deployment.yaml root@10.2.85.30:/docker/yaml/Deployment-${artifactId}.yaml"
  164.  
  165. sh "ssh root@10.2.85.30 'kubectl apply -f /docker/yaml/Deployment-${artifactId}.yaml && kubectl set env deploy/${artifactId} DEPLOY_DATE=${env.BUILD_ID}'"
  166.  
  167. }
  168.  
  169. } else {
  170.  
  171. sshagent(credentials: ['deploy_ssh_key_238']) {
  172.  
  173. sh "scp -P 22 -r Deployment.yaml root@39.95.40.97:/docker/yaml/Deployment-${artifactId}.yaml"
  174.  
  175. sh "ssh -p 22 root@39.95.40.97 'kubectl apply -f /docker/yaml/Deployment-${artifactId}.yaml && kubectl set env deploy/${artifactId} DEPLOY_DATE=${env.BUILD_ID}'"
  176.  
  177. }
  178.  
  179. }
  180.  
  181. echo "发布完成"
  182.  
  183. }
  184.  
  185. }
  186.  
  187. stage('通知负责人'){
  188.  
  189. // emailext body: "构建项目:${description}\r\n构建完成", subject: '构建结果通知【成功】', to: "${EMAIL}"
  190.  
  191. echo "构建项目:${description}\r\n构建完成"
  192.  
  193. }
  194.  
  195. }

将Jenkinsfile文件放在项目根目录,然后将源码都上传到GitLab。

打开BlueOcean,这是Jenkins新出的美化页面。

选择自己的项目。

进入后点击运行,其中会弹出框选择发布参数(这里需要手工填写发布的端口,由于采用配置中心化,端口无法自动读取)。

进入查看流程状态,失败会有相应的提示:

显示发布服务

在K8S内查看部署的服务启动情况。

Jenkinsfile Pipeline

Jenkinsfile Pipeline语法内容可参考官网:https://jenkins.io/doc/book/pipeline/jenkinsfile/

还可以进入项目后,有个流水线语法:

选择想要的功能,生成:

  Jenkins还可用作发布Vue前端项目,具体内容可参考 Jenkins自动化构建vue项目然后发布到远程服务器 文档。

  Jenkins要发布Net服务需要有一台windows的Jenkins slave,还需要在此节点上安装编译器MSBuild框架,Git框架、更改服务器上的IIS权限等功能,最后文件分发到其它windows服务器,过程比较繁琐,若无发布审核建议直接通过VS自带发布功能发布程序。

Jenkins+GitLab+Docker+SpringCloud+Kubernetes实现可持续自动化微服务的更多相关文章

  1. Jenkins+GitLab+Docker+SpringCloud实现可持续自动化微服务

    本文很长很长,但是句句干货,点赞关注收藏后有惊喜在文末等你 现有混合云平台的场景下,即有线下和线上的环境,又有测试与正式的场景,而且结合了Docker,导致打包内容有所区分,且服务的发布流程复杂起来, ...

  2. 【Devops】【docker】【CI/CD】3.Jenkins+GitLab+docker+springboot 实现自动化部署

    ==================================================================================================== ...

  3. springcloud(十四):搭建Zuul微服务网关

    springcloud(十四):搭建Zuul微服务网关 1. 2. 3. 4.

  4. Jenkins和Docker以及Kubernetes结合考虑

    今天搞了一下Jenkins和Docker的结合,我在想几个问题: 构建是经常的,构建最大的目标还是生成一个包或者应用 发布应用也是频繁的,发布本质上是把这个包放到可运行的环境中便于测试 如果每次构建我 ...

  5. 为什么Dapr是比SpringCloud和Istio更优雅的微服务框架?

    Dapr 是微软主导的云原生开源项目,2019年10月首次发布,到正式发布 V1.0 版本的不到一年的时间内,github star 数达到了 1.2万(现在已经超过1.7万星),超过同期的 kube ...

  6. 跟我学SpringCloud | 第十五篇:微服务利剑之APM平台(一)Skywalking

    目录 SpringCloud系列教程 | 第十五篇:微服务利剑之APM平台(一)Skywalking 1. Skywalking概述 2. Skywalking主要功能 3. Skywalking主要 ...

  7. 【spring cloud】【docker】使用docker在centOS上部署spring cloud微服务架构服务

    项目GitHub地址 ================================================================================== 部署过程: ...

  8. SpringCloud SpringBoot 前后端分离企业级微服务架构源码赠送

    基于SpringBoot2.x.SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务敏捷开发系统架构.并引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手 ...

  9. 在 Kubernetes 容器集群,微服务项目最佳实践

    转载自:https://mp.weixin.qq.com/s/WYu3gDwKKf06f_FYbO9YRg 本文主要介绍我个人在使用 Kubernetes 的过程中,总结出的一套「Kubernetes ...

随机推荐

  1. DNS查询工具:host、nslookup、dig

    作者:zhanhailiang 日期:2014-11-01 1. host host提供域名到IP地址的双向解析: host默认通过/etc/resolv.conf读取Name Server来解析,除 ...

  2. UWP 应用中的Back button(TitleBar) 的处理

    后退按钮是一项系统提供的 UI 提示,可以在后退堆栈或用户导航历史记录中支持向后导航. 用起来其实也是很简单的,只需要在App.xaml.cs 中修改(添加)如下红色代码,便可实现.. sealed ...

  3. 安装Eclipse完PyDev插件中没有出现

    假设你是在Window7在环境搭建.请确保您使用以管理员身份运行Eclipse. PyDev插件安装后没有显示是由于PyDev的执行须要Java7,能够通过升级JDK的版本号来完毕,而且配置环境变量( ...

  4. js 动态操作元素

    <!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  5. 如何将JPEG缩略图放到LISTVIEW中(delphi listview自绘图形)

    http://www.docin.com/p-567657457.html?qq-pf-to=pcqq.c2c http://www.cnblogs.com/snow001x/archive/2008 ...

  6. excel操作for(lutai)

    条件统计某个区域的值 第一种方法: =SUMIFS(P2:P5,L2:L5,A2) 第一个参数:被求和的单元格范围 第二个参数:明细表条件值单元格范围 第三个参数:主表条件单元格(可以是范围) 公式的 ...

  7. Win8Metro(C#)数字图像处理--2.26图像减法

    原文:Win8Metro(C#)数字图像处理--2.26图像减法  [函数名称] 图像减法函数SubtractionProcess(WriteableBitmap src, WriteableBi ...

  8. Android零基础入门第83节:Activity间数据传递方法汇总

    在Activity间传递的数据一般比较简单,但是有时候实际开发中也会传一些比较复杂的数据,本节一起来学习更多Activity间数据的传递. 一.常用数据类型 在前面几节我们只学习了一些常用类型的数据传 ...

  9. JavaScript 数据实用程序库:Datalib

    Datalib 是一个 JavaScript 数据实用程序库. 快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中.   <HTML开发M ...

  10. 制作Qt应用程序的插件(使用QtPlugin),对比DLL它是全平台通用的

    在Qt下,插件有两种形式,一种是用于QtCreator下,扩展IDE功能.另一种是用于扩展开发者的应用.本文要讲的是后者. 定义一个纯虚类作为插件接口 #include <QtPlugin> ...