最新发布《数智时代的AI人才粮仓模型解读白皮书(2024版)》,立即领取! 了解详情
写点什么

使用 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:2114860
用户头像

发布了 257 篇内容, 共 148.3 次阅读, 收获喜欢 576 次。

关注

评论

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

Android C++系列:JNI中的线程操作

轻口味

c++ android jni 11月日更

低碳企业和个人如何参与碳交易市场?

石云升

碳中和 11月日更 碳交易

自动化前端页面性能测试介绍

刘冉

大前端 软件测试 性能测试 软件自动化测试

极客时间架构实战营作业八

jjn0703

架构实战营

架构实战模块作业八

Yina🌝很浪🌊

架构实战课

[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (1)

baiyutang

golang 微服务 11月日更

模块三作业 外包学生管理系统详细架构设计

小朱

架构实战营

AI辅助测试与自动化测试

刘冉

AI 自动化测试

架构训练营 - 模块三作业

VegetableBird

架构训练营 架构实战营

进击的Java(六)

ES_her0

11月日更

设计消息队列存储消息数据的 MySQL 表格

缘分呐

基于Serverless的测试服务化-理论篇

刘冉

软件测试 自动化测试 测试服务化

如何使用 Prometheus Kafka Adapter 向 Kafka 推送 Prometheus 数据

耳东@Erdong

kafka Prometheus Adapter 11月日更

架构营模块三作业

GTiger

架构实战营

测试右移与自动化测试

刘冉

自动化测试 测试右测

题目二:架构设计题

arctec

Go语言,测试功能详解-上

微客鸟窝

Go 语言 11月日更

无处不在的 Kubernetes,难用的问题解决了吗?

阿里巴巴云原生

阿里云 Kubernetes 容器 云原生 难题攻克

模块三作业

周文

「架构实战营」

PS 基础操作 - 文件格式和存储

hepingfly

ps教程 ps学习笔记

Golang Gin 框架入门介绍(一)

liuzhen007

11月日更

再见了,纸币!数字人民币,席卷而来

CECBC

【LeetCode】键值映射Java题解

Albert

算法 LeetCode 11月日更

PS 基础操作 - 缩放、抓手、移动工具

hepingfly

ps教程 ps学习笔记

.NET6新东西--CallerArgumentExpression

喵叔

11月日更

【Promise 源码学习】第七篇 - 实现 Promise 返回值 x 的处理

Brave

源码 Promise 11月日更

模块三作业-外包学生管理系统

二手攻城师

「架构实战营」

Go语言,定时器的使用

微客鸟窝

Go 语言 11月日更

架构实战营 - 模块八作业

Alex.Wu

模块三作业:学生管理系统架构设计文档

赵先生

架构实战营

模块三课后作业 - 详细架构设计文档 - 外包学生管理系统

渐行渐远

架构实战营

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