速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

使用 Jenkins 和 Docker 确保构建环境和生产环境之间的对等

  • 2019-08-26
  • 本文字数:2541 字

    阅读完需:约 8 分钟

使用Jenkins和Docker确保构建环境和生产环境之间的对等

持续交付(CD)的核心原则之一是,测试需要在“类似生产”的环境中运行,以保证发布版本的稳定性。类似相同的环境可以减少“在我的机器上可以运行”之类的问题,并能轻松地在本地复现问题。我们可以避免由于平台差异引起的 bug,比如证书丢失或过期。


Docker 是实现这种对等性的最佳工具之一。它还允许我们将构建与 Jenkins 基础设施分离。比如,使用基于 Docker 的构建,我们无需担心预先安装在 Jenkins 代理上的 Java 版本。无论代理上安装了什么,构建都将使用我们指定的版本运行。尝试新的 JVM 版本如同变更 Docker 标签一样容易,可以让我们走地更快。


在本文中,我们将要学习如何使用Docker配置Jenkins,来确保构建环境与生产环境是相同的。我们还将学习如何通过在构建过程中共享缓存数据,来保持最小的构建时间。

Java 构建的简单概述

首先,从预先安装了 Docker 的 Jenkins 代理开始构建。该代理通过签出源代码编译的方式获取,并在预先安装了 JDK 的 Docker 容器中运行测试。然后,富 JAR 将在生产环境中运行在一个 Docker 镜像,该镜像具有和构建时相同的 Java 版本。


不幸的是,我们需要考虑很多细节才能解决这个问题。下面我们将深入地讨论这些细节。

使用 Docker 构建的详细指南

首先,需要预先安装一个带有 Docker 二进制文件的 Jenkins 代理。因为这是唯一的要求,所以这个代理可以在整个公司内共享,没有必要为每个团队都构建自己的代理。在本例中,klarna-jenkins-agent 是附带了 Docker 的通用代理。它还有一个 ID 为 11000 的 Jenkins 用户,该用户属于 ID 为 12000 的 Docker 组。


如果你想先看下结果,请阅读本文末尾部分,以了解此处讨论的完整 Jenkinsfile。


我们在 pipeline 的根目录处配置代理,以确保所有的 Docker 镜像都能在签出源代码的同一台机器上运行。


// Jenkinsfilepipeline {  agent { label ‘klarna-jenkins-agent’ }  stages { ... }}
复制代码


我们使用 amazonlinux-java-jdk:11 镜像来运行构建。考虑到安全性,镜像安装了 JDK 并使用非 root 帐户来运行。该步骤应该在同一代理(1,3)上运行,以便可以访问项目的源代码。


或者,如果集成测试需要 Docker,则可以安装 docker.sock(2)。


stage ('Compile & Test') {  agent {    docker {      image 'docker-registry.klarna/amazonlinux-java-jdk:11'      label 'klarna-jenkins-agent' // 1      args '-v /var/run/docker.sock:/var/run/docker.sock -u 11000:12000' // 2      reuseNode true // 3    }   }  steps {    sh './gradlew build'  } }
复制代码


如果项目需要,也可以在自定义的 Docker 镜像中运行构建。该过程,仅需指定要使用的 Dockerfile 即可,而无需指定镜像:


 agent {    docker {      filename 'MyDockerfile.build'      ...    }  }
复制代码


镜像将会缓存在代理上,只有当 Dockerfile 的内容发生变更时,镜像才会重新生成。

提高性能

现在我们已经运行了一个基本的构建,我们可以通过在运行之间共享 Gradle 缓存来提高性能。


我们为 Gradle 缓存准备了一个“home”。在将它挂载之前,必须先创建一个.gradle_home 文件夹(4),才能确保 Jenkins 用户可以对其进行写入。否则,该文件夹将由 Docker 创建,只有 root 帐户才有权访问它。


pipeline {  environment {    GRADLE_USER_HOME = "/home/jenkins/.gradle_home"  }  stages {    stage (‘Setup’) {      steps { sh 'mkdir /home/jenkins/.gradle_home || true' // 4    }   } }
复制代码


最后一步,我们修改 docker args 部分,以挂载新创建的文件夹。完整的 args 参数如下所示:


args '-v /var/run/docker.sock:/var/run/docker.sock -u 11000:12000 -v /home/jenkins/.gradle_home:/home/jenkins/.gradle_home'
复制代码


Jenkinsfile 的最终版本如下:


pipeline {    agent { label 'klarna-jenkins-agent' }    stages {      environment {        GRADLE_USER_HOME = "/home/jenkins/.gradle_home"      }      stages {        stage ('Setup') {          steps { sh 'mkdir /home/jenkins/.gradle_home || true' // 4        }         stage ('Compile & Test') {          agent {            docker {              image 'docker-registry.klarna/amazonlinux-java-jdk:11'              // alternatively, build a docker image by specifying a Dockerfile              // filename 'MyDockerfile.build'              label 'klarna-jenkins-agent' // 1              // assuming uid(jenkins)=11000 and gid(docker)=12000              args '-v /var/run/docker.sock:/var/run/docker.sock -u 11000:12000 -v /home/jenkins/.gradle_home:/home/jenkins/.gradle_home'              reuseNode true // 3            }           }          steps {            sh './gradlew build'          }         }      }    }  }
复制代码


Jenkinsfile


或者,我们也可以使用名为 volumes 的 Docker 作为 Gradle 缓存,但是 volume 文件夹由容器内的 root 拥有,如果我们使用非 root 用户运行构建,则会使设置变得更加复杂。

好处

除了可以获得相同的构建和生产环境之外,还有其他一些好处:


  1. 不需要为每个团队或项目维护一个定制的 Jenkins 代理。只需一个 Docker 通用代理就足够了。

  2. 不受 Jenkins 代理上的 Java 版本限制。

  3. 如果项目在 pipeline 的特定步骤中需要一些自定义的软件,我们可以为它创建一个 Dockerfile,而无需创建自定义的代理,并受那些预先安装在代理上的软件的约束。如果以后可能会切换到其他构建工具上,我们也有很多选择。

  4. 升级到下一个 JDK 版本很容易。只需将 Jenkinsfile 中的标签 11 替换为 12 即可。我们甚至可以将构建参数化来验证兼容性。

结论

我们需要付出一些努力才能确保构建环境和生产环境是相同的,并且花费的这些时间是值得的,因为它增强了我们对发布的信心。


使用 Docker 能够使用我们将 Jenkins 代理从构建环境中解耦出来,从而使我们能够精确地控制构建所需的 Java、Node 或 Python 版本,而不需要在代理上预先安装什么。通过使用公司中的标准化组件,可以减少长期维护。


原文链接:


https://engineering.klarna.com/ensuring-parity-between-build-and-production-environments-using-jenkins-and-docker-2695f758b549


2019-08-26 10:2115097
用户头像

发布了 305 篇内容, 共 201.6 次阅读, 收获喜欢 599 次。

关注

评论

发布
暂无评论
发现更多内容

James邮件服务器,高级java工程师简历模板

Java 程序员 后端

Java 虚拟机1:什么是 Java,太完整了

Java 程序员 后端

Java 里面的异常,java语言程序设计教程pdf

Java 程序员 后端

Java 高并发之设计模式,深入linux内核架构mobi

Java 程序员 后端

Java transient关键字的使用,java商城项目面试

Java 程序员 后端

Java 世界里的垃圾回收规则你搞懂了吗?,springboot输出视频流

Java 程序员 后端

jackson学习之八:常用方法注解(1),java虚拟机实现原理

Java 程序员 后端

JAVA 微信小程序 解密 用户信息encryptedData,linux系统架构与目录解析

Java 程序员 后端

JAVA 获取系统日期时间,java基础百度云

Java 程序员 后端

Java 虚拟机1:什么是 Java(1),DubboSPI及自适应扩展原理

Java 程序员 后端

Prometheus HTTP API 查询(三)查询元数据

耳东@Erdong

Prometheus PromQL HTTP API 11月日更

Android C++系列:JNI调用 Java 类的构造方法和父类的方法.md

轻口味

c++ android jni 11月日更

InnoDB(3,韩顺平java从入门到精通课件

Java 程序员 后端

如何编写 Go 包

baiyutang

golang 11月日更

Java 多线程 —— 定时器,svnlinux使用教程

Java 程序员 后端

Java-Parallel GC介绍,springmvc面试题高级

Java 程序员 后端

Java-进阶:集合框架1,java三个技术平台

Java 程序员 后端

InnoDB(4(1),java技术架构知识

Java 程序员 后端

java-集合-Map(双列)——迪迦重制版(1),关于线程池的五种实现方式

Java 程序员 后端

Jaeger知识点补充,java菜鸟教程面向对象

Java 程序员 后端

Java this关键字详解(3种用法),Java程序员最新职业规划

Java 程序员 后端

Java 低代码开发平台“光”发布 2,javapdf模板下载百度云

Java 程序员 后端

Java 反射:框架设计的灵魂,springboot运行原理

Java 程序员 后端

Java-进阶:集合框架1(1),java分布式系统面试题

Java 程序员 后端

IDEA类和方法注释模板设置(超详细教程),java程序执行过程与编译原理

Java 程序员 后端

IDEA这样配置,好用到爆炸!,Java开发必须要会

Java 程序员 后端

InnoDB(4,java中级工程师面试题

Java 程序员 后端

一行代码爬取微博热搜数据

老表

爬虫 python学习 11月日更

jackson学习之八:常用方法注解,复习指南

Java 程序员 后端

java 数据结构与算法之稀疏矩阵算法,BTAJ面试有关散列(哈希)表的面试题详解

Java 程序员 后端

Java 方法的使用(方法重载、形参和实参调用关系,java高级面试最新

Java 程序员 后端

使用Jenkins和Docker确保构建环境和生产环境之间的对等_语言 & 开发_Dumitru Postoronca_InfoQ精选文章