Jenkins 的默认日志难以阅读，但日志本不必如此。Jenkins 是一个自由开源的自动化服务器，用于构建、测试和部署代码。它是 持续集成 Continuous Integration （CI）、 持续交付 Continuous Delivery （CD）的基础，可以为开发人员每天节约几小时，并保护他们免受失败的代码上线的影响。一旦代码失效或开发人员需要查看测试输出时，Jenkins 提供了日志文件以供检查。默认的 Jenkins 管道 Pipeline 日志可能难以阅读。这篇关于 Jenkins 日志的基础知识的总结文章提供了一些技巧（和代码），说明了如何提升它们的可读性。你获得什么Jenkins 管道分为 几个阶段。Jenkins 自动记录每个阶段的开始，记录内容如下：[Pipeline] // stage [Pipeline] stage (hide) [Pipeline] { (Apply all openshift resources) [Pipeline] dir 上文显示的内容没有太大区分度，重要的内容（如阶段的开始）未突出显示。在多达数百行的管道日志中，要找到一个阶段的起始和另外一个阶段的终止位置可能会很艰巨。当随意浏览日志寻找一个特定的阶段的时候，这种艰巨尤其明显。Jenkins 管道是由 Groovy 和 Shell 脚本混合编写的。在 Groovy 代码中，日志记录很少。很多时候，日志是由命令中的不起眼的文本组成，没有详细信息。在 Shell 脚本中，打开了调试模式（set -x），所以每条命令都会被完全 具现化 realized （变量被解除引用并打印出数值）并详细记录，输出也是如此。鉴于日志可能有很多内容，通读日志获取相关信息可能很繁琐。由于在管道中被处理，并跟着一个 Shell 脚本的 Groovy 日志可读性差，它们很多时候缺少上下文：[Pipeline] dir Running in /home/jenkins/agent/workspace/devop-master/devops-server-pipeline/my-repo-dir/src [Pipeline] { (hide) [Pipeline] findFiles [Pipeline] findFiles [Pipeline] readYaml [Pipeline] } 我可以知道我正在使用的目录，并且知道我正在使用 Jenkins 的步骤搜索文件、读取 YAML 文件。但是我在寻找什么？我找到并读取的内容是什么？能做什么？我很高兴你这么问，因为这里有一些简单的做法和一些小的代码片段可以提供帮助。首先，代码如下：def echoBanner(def ... msgs) { echo createBanner(msgs) } def errorBanner(def ... msgs) { error(createBanner(msgs)) } def createBanner(def ... msgs) { return """ =========================================== ${msgFlatten(null, msgs).join("\n ")} =========================================== """ } // flatten function hack included in case Jenkins security // is set to preclude calling Groovy flatten() static method // NOTE: works well on all nested collections except a Map def msgFlatten(def list, def msgs) { list = list ?: [] if (!(msgs instanceof String) && !(msgs instanceof GString)) { msgs.each { msg -> list = msgFlatten(list, msg) } } else { list += msgs } return list } 将这段代码添加到每个管道的末尾，也可以 加载一个 Groovy 文件 或者使其成为 Jenkins 共享库 的一部分，这样更有效。在每个阶段起始处（或者在阶段中的特定位置），只需调用 echoBanner：echoBanner("MY STAGE", ["DOING SOMETHING 1", "DOING SOMETHING 2"]) 你的 Jenkins 日志会展示如下： =========================================== MY STAGE DOING SOMETHING 1 DOING SOMETHING 2 =========================================== 这个横幅很容易从日志中分辨出来。当正确使用它们时，它们还有助于界定管道流，并且可以很好的将日志分解开来进行阅读。我已经在某些地方专业地使用这些代码一些时间了。在帮助管道日志更易读和流程更易理解方面，反馈是非常积极的。上述的 errorBanner 方法以相同的方式工作，但是它会立即使脚本失效。这有助于突显失败的位置与原因。最佳实践在你的 Groovy 代码中大量使用 echo Jenkins 步骤来通知用户你在做什么。这些也可以帮助记录你的代码。使用空的日志语句（Groovy 中空的 echo 步骤、echo '' 或 Shell 中的 echo）来分割输出，提高可读性。你可能在你的代码中为同样的目的使用空行。避免在脚本中使用 set +x 的陷阱，因为它隐藏了日志记录已执行的 Shell 语句。它并没有清理你的日志，而是使你的管道成为一个黑盒子，隐藏了管道正在做的行为以及出现的任何错误。确保管道功能尽可能透明。如果你的管道创建了 中间工件 Intermediate Artifacts ，开发人员和 DevOps 人员可以使用这些工件来帮助调试问题，那么也要记录它的内容。是的，它会加长日志，但这只是文本。在某些时候，这会是有用的信息，而（利用得当的）日志不就是关于发生了什么和为什么发生的大量信息吗？Kubernetes 机密信息：无法完全透明的地方有些事情你不希望出现在日志里暴露出来。如果你在使用 Kubernetes 并引用保存在 Kubernetes 机密信息 Secrets 中的数据，那么你绝对不希望在日志中公开该数据，因为这些数据只是被混淆了，而没有被加密。假如你想获取一些保存在机密信息中的数据，然后将其注入模板化 JSON 文件中。（机密信息和 JSON 模板的完整内容与此例无关。）按照最佳实践，你希望保持透明并记录你的操作，但你不想公开机密信息数据。将脚本模式从调试（set -x）更改为命令记录（set -v）。在脚本敏感部分的结尾，将 Shell 重置为调试模式：sh """ # change script mode from debugging to command logging set +x -v # capture data from secret in shell variable MY_SECRET=\$(kubectl get secret my-secret --no-headers -o 'custom-column=:.data.my-secret-data') # replace template placeholder inline sed s/%TEMPLATE_PARAM%/${MY_SECRET_DATA}/ my-template-file.json # do something with modified template-file.json... # reset the shell to debugging mode set -x +v """ 这将输出此行到日志：sed s/%TEMPLATE_PARAM%/${MY_SECRET_DATA}/ my-template-file.json 与 Shell 调试模式中不同，这不会具现化 Shell 变量 MY_SECRET_DATA。显然，如果管道中在这一点出现问题，而你试图找出问题出在哪里，那么这不如调试模式有用。但这是在保持管道执行对开发人员和 DevOps 透明的同时，也保持你的秘密的最佳平衡。via: https://opensource.com/article/21/5/jenkins-logs作者：Evan "Hippy" Slatis 选题：lujun9972 译者：DCOLIVERSUN 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
通过这份 Jenkins 分步教程，构建持续集成和持续交付（CI/CD）流水线。在我的文章《使用开源工具构建 DevOps 流水线的初学者指南》中，我分享了一个从头开始构建 DevOps 流水线的故事。推动该计划的核心技术是 Jenkins，这是一个用于建立持续集成和持续交付（CI/CD）流水线的开源工具。在花旗，有一个单独的团队为专用的 Jenkins 流水线提供稳定的主从节点环境，但是该环境仅用于质量保证（QA）、构建阶段和生产环境。开发环境仍然是非常手动的，我们的团队需要对其进行自动化以在加快开发工作的同时获得尽可能多的灵活性。这就是我们决定为 DevOps 建立 CI/CD 流水线的原因。Jenkins 的开源版本由于其灵活性、开放性、强大的插件功能和易用性而成为显而易见的选择。在本文中，我将分步演示如何使用 Jenkins 构建 CI/CD 流水线。什么是流水线？在进入本教程之前，了解有关 CI/CD 流水线 pipeline 的知识会很有帮助。首先，了解 Jenkins 本身并不是流水线这一点很有帮助。只是创建一个新的 Jenkins 作业并不能构建一条流水线。可以把 Jenkins 看做一个遥控器，在这里点击按钮即可。当你点击按钮时会发生什么取决于遥控器要控制的内容。Jenkins 为其他应用程序 API、软件库、构建工具等提供了一种插入 Jenkins 的方法，它可以执行并自动化任务。Jenkins 本身不执行任何功能，但是随着其它工具的插入而变得越来越强大。流水线是一个单独的概念，指的是按顺序连接在一起的事件或作业组：“ 流水线 pipeline ”是可以执行的一系列事件或作业。理解流水线的最简单方法是可视化一系列阶段，如下所示：在这里，你应该看到两个熟悉的概念： 阶段 Stage 和 步骤 Step 。阶段：一个包含一系列步骤的块。阶段块可以命名为任何名称；它用于可视化流水线过程。步骤：表明要做什么的任务。步骤定义在阶段块内。在上面的示例图中，阶段 1 可以命名为 “构建”、“收集信息”或其它名称，其它阶段块也可以采用类似的思路。“步骤”只是简单地说放上要执行的内容，它可以是简单的打印命令（例如，echo "Hello, World"）、程序执行命令（例如，java HelloWorld）、shell 执行命令（ 例如，chmod 755 Hello）或任何其他命令，只要通过 Jenkins 环境将其识别为可执行命令即可。Jenkins 流水线以编码脚本的形式提供，通常称为 “Jenkinsfile”，尽管可以用不同的文件名。下面这是一个简单的 Jenkins 流水线文件的示例：// Example of Jenkins pipeline script pipeline { stages { stage("Build") { steps { // Just print a Hello, Pipeline to the console echo "Hello, Pipeline!" // Compile a Java file. This requires JDKconfiguration from Jenkins javac HelloWorld.java // Execute the compiled Java binary called HelloWorld. This requires JDK configuration from Jenkins java HelloWorld // Executes the Apache Maven commands, clean then package. This requires Apache Maven configuration from Jenkins mvn clean package ./HelloPackage // List the files in current directory path by executing a default shell command sh "ls -ltr" } } // And next stages if you want to define further... } // End of stages } // End of pipeline从此示例脚本很容易看到 Jenkins 流水线的结构。请注意，默认情况下某些命令（如 java、javac和 mvn）不可用，需要通过 Jenkins 进行安装和配置。 因此：Jenkins 流水线是一种以定义的方式依次执行 Jenkins 作业的方法，方法是将其编码并在多个块中进行结构化，这些块可以包含多个任务的步骤。好。既然你已经了解了 Jenkins 流水线是什么，我将向你展示如何创建和执行 Jenkins 流水线。在本教程的最后，你将建立一个 Jenkins 流水线，如下所示：如何构建 Jenkins 流水线为了便于遵循本教程的步骤，我创建了一个示例 GitHub 存储库和一个视频教程。开始本教程之前，你需要：Java 开发工具包（JDK）：如果尚未安装，请安装 JDK 并将其添加到环境路径中，以便可以通过终端执行 Java 命令（如 java jar）。这是利用本教程中使用的 Java Web Archive（WAR）版本的 Jenkins 所必需的（尽管你可以使用任何其他发行版）。基本计算机操作能力：你应该知道如何键入一些代码、通过 shell 执行基本的 Linux 命令以及打开浏览器。让我们开始吧。步骤一：下载 Jenkins导航到 Jenkins 下载页面。向下滚动到 “Generic Java package (.war)”，然后单击下载文件；将其保存在易于找到的位置。（如果你选择其他 Jenkins 发行版，除了步骤二之外，本教程的其余步骤应该几乎相同。）使用 WAR 文件的原因是它是个一次性可执行文件，可以轻松地执行和删除。步骤二：以 Java 二进制方式执行 Jenkins打开一个终端窗口，并使用 cd <your path> 进入下载 Jenkins 的目录。（在继续之前，请确保已安装 JDK 并将其添加到环境路径。）执行以下命令，该命令将 WAR 文件作为可执行二进制文件运行：java -jar ./jenkins.war如果一切顺利，Jenkins 应该在默认端口 8080 上启动并运行。步骤三：创建一个新的 Jenkins 作业打开一个 Web 浏览器并导航到 localhost:8080。除非你有以前安装的 Jenkins，否则应直接转到 Jenkins 仪表板。点击 “Create New Jobs”。你也可以点击左侧的 “New Item”。步骤四：创建一个流水线作业在此步骤中，你可以选择并定义要创建的 Jenkins 作业类型。选择 “Pipeline” 并为其命名（例如，“TestPipeline”）。单击 “OK” 创建流水线作业。你将看到一个 Jenkins 作业配置页面。向下滚动以找到 “Pipeline” 部分。有两种执行 Jenkins 流水线的方法。一种方法是在 Jenkins 上直接编写流水线脚本，另一种方法是从 SCM（源代码管理）中检索 Jenkins 文件。在接下来的两个步骤中，我们将体验这两种方式。步骤五：通过直接脚本配置并执行流水线作业要使用直接脚本执行流水线，请首先从 GitHub 复制该 Jenkinsfile 示例的内容。选择 “Pipeline script” 作为 “Destination”，然后将该 Jenkinsfile 的内容粘贴到 “Script” 中。花一些时间研究一下 Jenkins 文件的结构。注意，共有三个阶段：Build、Test 和 Deploy，它们是任意的，可以是任何一个。每个阶段中都有一些步骤；在此示例中，它们只是打印一些随机消息。单击 “Save” 以保留更改，这将自动将你带回到 “Job Overview” 页面。要开始构建流水线的过程，请单击 “Build Now”。如果一切正常，你将看到第一个流水线（如下面的这个）。要查看流水线脚本构建的输出，请单击任何阶段，然后单击 “Log”。你会看到这样的消息。步骤六：通过 SCM 配置并执行流水线作业现在，换个方式：在此步骤中，你将通过从源代码控制的 GitHub 中复制 Jenkinsfile 来部署相同的 Jenkins 作业。在同一个 GitHub 存储库中，通过单击 “Clone or download” 并复制其 URL 来找到其存储库 URL。单击 “Configure” 以修改现有作业。滚动到 “Advanced Project Options” 设置，但这一次，从 “Destination” 下拉列表中选择 “Pipeline script from SCM” 选项。将 GitHub 存储库的 URL 粘贴到 “Repository URL” 中，然后在 “Script Path” 中键入 “Jenkinsfile”。 单击 “Save” 按钮保存。要构建流水线，回到 “Task Overview” 页面后，单击 “Build Now” 以再次执行作业。结果与之前相同，除了多了一个称为 “Declaration: Checkout SCM” 的阶段。要查看来自 SCM 构建的流水线的输出，请单击该阶段并查看 “Log” 以检查源代码控制克隆过程的进行情况。除了打印消息，还能做更多恭喜你！你已经建立了第一个 Jenkins 流水线！“但是等等”，你说，“这太有限了。除了打印无用的消息外，我什么都做不了。”那没问题。到目前为止，本教程仅简要介绍了 Jenkins 流水线可以做什么，但是你可以通过将其与其他工具集成来扩展其功能。以下是给你的下一个项目的一些思路：建立一个多阶段的 Java 构建流水线，从以下阶段开始：从 Nexus 或 Artifactory 之类的 JAR 存储库中拉取依赖项、编译 Java 代码、运行单元测试、打包为 JAR/WAR 文件，然后部署到云服务器。实现一个高级代码测试仪表板，该仪表板将基于 Selenium 的单元测试、负载测试和自动用户界面测试，报告项目的运行状况。构建多流水线或多用户流水线，以自动化执行 Ansible 剧本的任务，同时允许授权用户响应正在进行的任务。设计完整的端到端 DevOps 流水线，该流水线可提取存储在 SCM 中的基础设施资源文件和配置文件（例如 GitHub），并通过各种运行时程序执行该脚本。学习本文结尾处的任何教程，以了解这些更高级的案例。管理 Jenkins在 Jenkins 主面板，点击 “Manage Jenkins”。全局工具配置有许多可用工具，包括管理插件、查看系统日志等。单击 “Global Tool Configuration”。增加附加能力在这里，你可以添加 JDK 路径、Git、Gradle 等。配置工具后，只需将该命令添加到 Jenkinsfile 中或通过 Jenkins 脚本执行即可。后继本文为你介绍了使用酷炫的开源工具 Jenkins 创建 CI/CD 流水线的方法。要了解你可以使用 Jenkins 完成的许多其他操作，请在 Opensource.com 上查看以下其他文章：Jenkins X 入门使用 Jenkins 安装 OpenStack 云在容器中运行 JenkinsJenkins 流水线入门如何与 Jenkins 一起运行 JMeter将 OpenStack 集成到你的 Jenkins 工作流中你可能对我为你的开源之旅而写的其他一些文章感兴趣：9 个用于构建容错系统的开源工具了解软件设计模式使用开源工具构建 DevOps 流水线的初学者指南via: https://opensource.com/article/19/9/intro-building-cicd-pipelines-jenkins作者：Bryant Son 选题：lujun9972 译者：wxy 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
容器应用程序平台能够动态地启动具有资源限制的独立容器，从而改变了运行 CI/CD 任务的方式。现今，由于 Docker 和 Kubernetes（K8S）提供了可扩展、可管理的应用平台，将应用运行在容器中的实践已经被企业广泛接受。近些年势头很猛的微服务架构也很适合用容器实现。容器应用平台可以动态启动指定资源配额、互相隔离的容器，这是其最主要的优势之一。让我们看看这会对我们运行 持续集成/持续部署 continuous integration/continuous development （CI/CD）任务的方式产生怎样的改变。构建并打包应用需要一定的环境，要求能够下载源代码、使用相关依赖及已经安装构建工具。作为构建的一部分，运行单元及组件测试可能会用到本地端口或需要运行第三方应用（如数据库及消息中间件等）。另外，我们一般定制化多台构建服务器，每台执行一种指定类型的构建任务。为方便测试，我们维护一些实例专门用于运行第三方应用（或者试图在构建服务器上启动这些第三方应用），避免并行运行构建任务导致结果互相干扰。为 CI/CD 环境定制化构建服务器是一项繁琐的工作，而且随着开发团队使用的开发平台或其版本变更，会需要大量的构建服务器用于不同的任务。一旦我们有了容器管理平台（自建或在云端），将资源密集型的 CI/CD 任务在动态生成的容器中执行是比较合理的。在这种方案中，每个构建任务运行在独立启动并配置的构建环境中。构建过程中，构建任务的测试环节可以任意使用隔离环境中的可用资源；此外，我们也可以在辅助容器中启动一个第三方应用，只在构建任务生命周期中为测试提供服务。听上去不错，让我们在现实环境中实践一下。注：本文基于现实中已有的解决方案，即一个在 Red Hat OpenShift v3.7 集群上运行的项目。OpenShift 是企业级的 Kubernetes 版本，故这些实践也适用于 K8S 集群。如果愿意尝试，可以下载 Red Hat CDK，运行 jenkins-ephemeral 或 jenkins-persistent 模板在 OpenShift 上创建定制化好的 Jenkins 管理节点。解决方案概述在 OpenShift 容器中执行 CI/CD 任务（构建和测试等） 的方案基于分布式 Jenkins 构建，具体如下：我们需要一个 Jenkins 主节点；可以运行在集群中，也可以是外部提供支持 Jenkins 特性和插件，以便已有项目仍可使用可以用 Jenkins GUI 配置、运行任务或查看任务输出如果你愿意编码，也可以使用 Jenkins Pipeline从技术角度来看，运行任务的动态容器是 Jenkins 代理节点。当构建启动时，首先是一个新节点启动，通过 Jenkins 主节点的 JNLP（5000 端口） 告知就绪状态。在代理节点启动并提取构建任务之前，构建任务处于排队状态。就像通常 Jenkins 代理服务器那样，构建输出会送达主节点；不同的是，构建完成后代理节点容器会自动关闭。不同类型的构建任务（例如 Java、 NodeJS、 Python等）对应不同的代理节点。这并不新奇，之前也是使用标签来限制哪些代理节点可以运行指定的构建任务。启动用于构建任务的 Jenkins 代理节点容器需要配置参数，具体如下：用于启动容器的 Docker 镜像资源限制环境变量挂载的卷这里用到的关键组件是 Jenkins Kubernetes 插件。该插件（通过使用一个服务账号） 与 K8S 集群交互，可以启动和关闭代理节点。在插件的配置管理中，多种代理节点类型表现为多种 Kubernetes pod 模板，它们通过项目标签对应。这些代理节点镜像以开箱即用的方式提供（也有 CentOS7 系统的版本）：jenkins-slave-base-rhel7：基础镜像，启动与 Jenkins 主节点连接的代理节点；其中 Java 堆大小根据容器内容设置jenkins-slave-maven-rhel7：用于 Maven 和 Gradle 构建的镜像（从基础镜像扩展）jenkins-slave-nodejs-rhel7：包含 NodeJS4 工具的镜像（从基础镜像扩展）注意：本解决方案与 OpenShift 中的 Source-to-Image（S2I） 构建无关，虽然后者也可以用于某些特定的 CI/CD 任务。入门学习资料有很多不错的博客和文档介绍了如何在 OpenShift 上执行 Jenkins 构建。不妨从下面这些开始：OpenShift Jenkins 镜像文档及 源代码网络播客：基于 OpenShift 的 CI/CD外部 Jenkins 集成 剧本阅读这些博客和文档有助于完整的理解本解决方案。在本文中，我们主要关注具体实践中遇到的各类问题。构建我的应用作为示例项目，我们选取了包含如下构建步骤的 Java 项目：代码源： 从一个 Git 代码库中获取项目代码使用 Maven 编译： 依赖可从内部仓库获取，(不妨使用 Apache Nexus） 镜像自外部 Maven 仓库发布成品： 将编译好的 JAR 上传至内部仓库在 CI/CD 过程中，我们需要与 Git 和 Nexus 交互，故 Jenkins 任务需要能够访问这些系统。这要求参数配置和已存储凭证可以在下列位置进行管理：在 Jenkins 中： 我们可以在 Jenkins 中添加凭证，通过 Git 插件能够对项目添加和使用文件（使用容器不会改变操作）在 OpenShift 中： 使用 ConfigMap 和 Secret 对象，以文件或环境变量的形式附加到 Jenkins 代理容器中在高度定制化的 Docker 容器中： 镜像是定制化的，已包含完成特定类型构建的全部特性；从一个代理镜像进行扩展即可得到。你可以按自己的喜好选择一种实现方式，甚至你最终可能混用多种实现方式。下面我们采用第二种实现方式，即首选在 OpenShift 中管理参数配置。使用 Kubernetes 插件配置来定制化 Maven 代理容器，包括设置环境变量和映射文件等。注意：对于 Kubernetes 插件 v1.0 版，由于 bug，在 UI 界面增加环境变量并不生效。可以升级插件，或（作为变通方案） 直接修改 config.xml 文件并重启 Jenkins。从 Git 获取源代码从公共 Git 仓库获取源代码很容易。但对于私有 Git 仓库，不仅需要认证操作，客户端还需要信任服务器以便建立安全连接。一般而言，通过两种协议获取源代码：HTTPS：验证通过用户名/密码完成。Git 服务器的 SSL 证书必须被代理节点信任，这仅在证书被自建 CA 签名时才需要特别注意。git clone https://git.mycompany.com:443/myapplication.git SSH：验证通过私钥完成。如果服务器的公钥指纹出现在 known_hosts 文件中，那么该服务器是被信任的。git clone ssh://git@git.mycompany.com:22/myapplication.git 对于手动操作，使用用户名/密码通过 HTTP 方式下载源代码是可行的；但对于自动构建而言，SSH 是更佳的选择。通过 SSH 方式使用 Git要通过 SSH 方式下载源代码，我们需要保证代理容器与 Git 的 SSH 端口之间可以建立 SSH 连接。首先，我们需要创建一个私钥-公钥对。使用如下命令生成：ssh keygen -t rsa -b 2048 -f my-git-ssh -N '' 命令生成的私钥位于 my-git-ssh 文件中（口令为空），对应的公钥位于 my-git-ssh.pub 文件中。将公钥添加至 Git 服务器的对应用户下（推荐使用“服务账号”）；网页界面一般支持公钥上传。为建立 SSH 连接，我们还需要在代理容器上配置两个文件：私钥文件位于 ~/.ssh/id_rsa服务器的公钥位于 ~/.ssh/known_hosts。要实现这一点，运行 ssh git.mycompany.com 并接受服务器指纹，系统会在 ~/.ssh/known_hosts 文件中增加一行。这样需求得到了满足。将 id_rsa 对应的私钥和 known_hosts 对应的公钥保存到一个 OpenShift 的 secret（或 ConfigMap） 对象中。apiVersion: v1 kind: Secret metadata: name: mygit-ssh stringData: id_rsa: |- -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY----- known_hosts: |- git.mycompany.com ecdsa-sha2-nistp256 AAA... 在 Kubernetes 插件中将 secret 对象配置为卷，挂载到 /home/jenkins/.ssh/，供 Maven pod 使用。secret 中的每个对象对应挂载目录的一个文件，文件名与 key 名称相符。我们可以使用 UI（管理 Jenkins / 配置 / 云 / Kubernetes），也可以直接编辑 Jenkins 配置文件 /var/lib/jenkins/config.xml：<org.csanchez.jenkins.plugins.kubernetes.PodTemplate> maven ... <org.csanchez.jenkins.plugins.kubernetes.volumes.SecretVolume> /home/jenkins/.ssh mygit-ssh </org.csanchez.jenkins.plugins.kubernetes.volumes.SecretVolume> 此时，在代理节点上运行的任务应该可以通过 SSH 方式从 Git 代码库获取源代码。注：我们也可以在 ~/.ssh/config 文件中自定义 SSH 连接。例如，如果你不想处理 known_hosts 或私钥位于其它挂载目录中：Host git.mycompany.com StrictHostKeyChecking no IdentityFile /home/jenkins/.config/git-secret/ssh-privatekey 通过 HTTP 方式使用 Git如果你选择使用 HTTP 方式下载，在指定的 Git-credential-store 文件中添加用户名/密码：例如，在一个 OpenShift secret 对象中增加 /home/jenkins/.config/git-secret/credentials 文件对应，其中每个站点对应文件中的一行:https://username:password@git.mycompany.com https://user:pass@github.com 在 git-config 配置中启用该文件，其中配置文件默认路径为 /home/jenkins/.config/git/config：[credential] helper = store --file=/home/jenkins/.config/git-secret/credentials 如果 Git 服务使用了自有 CA 签名的证书，为代理容器设置环境变量 GIT_SSL_NO_VERIFY=true 是最便捷的方式。更恰当的解决方案包括如下两步：利用 ConfigMap 将自有 CA 的公钥映射到一个路径下的文件中，例如 /usr/ca/myTrustedCA.pem）。通过环境变量 GIT_SSL_CAINFO=/usr/ca/myTrustedCA.pem 或上面提到的 git-config 文件的方式，将证书路径告知 Git。[http "https://git.mycompany.com"] sslCAInfo = /usr/ca/myTrustedCA.pem 注：在 OpenShift v3.7 及早期版本中，ConfigMap 及 secret 的挂载点之间不能相互覆盖，故我们不能同时映射 /home/jenkins 和 /home/jenkins/dir。因此，上面的代码中并没有使用常见的文件路径。预计 OpenShift v3.9 版本会修复这个问题。Maven要完成 Maven 构建，一般需要完成如下两步：建立一个社区 Maven 库（例如 Apache Nexus），充当外部库的代理。将其当作镜像使用。这个内部库可能提供 HTTPS 服务，其中使用自建 CA 签名的证书。对于容器中运行构建的实践而言，使用内部 Maven 库是非常关键的，因为容器启动后并没有本地库或缓存，这导致每次构建时 Maven 都下载全部的 Jar 文件。在本地网络使用内部代理库下载明显快于从因特网下载。Maven Jenkins 代理镜像允许配置环境变量，指定代理的 URL。在 Kubernetes 插件的容器模板中设置如下：MAVEN_MIRROR_URL=https://nexus.mycompany.com/repository/maven-public 构建好的成品（JAR） 也应该保存到库中，可以是上面提到的用于提供依赖的镜像库，也可以是其它库。Maven 完成 deploy 操作需要在 pom.xml 的分发管理 下配置库 URL，这与代理镜像无关。<project ...> <distributionManagement> mynexus https://nexus.mycompany.com/repository/maven-snapshots/ mynexus https://nexus.mycompany.com/repository/maven-releases/ </distributionManagement> 上传成品可能涉及认证。在这种情况下，在 settings.xml 中配置的用户名/密码要与 pom.xml 文件中的对应的服务器 id 下的设置匹配。我们可以使用 OpenShift secret 将包含 URL、用户名和密码的完整 settings.xml 映射到 Maven Jenkins 代理容器中。另外，也可以使用环境变量。具体如下：利用 secret 为容器添加环境变量：MAVEN_SERVER_USERNAME=admin MAVEN_SERVER_PASSWORD=admin123 利用 config map 将 settings.xml 挂载至 /home/jenkins/.m2/settings.xml： external:* ${env.MAVEN_MIRROR_URL} mirror mynexus ${env.MAVEN_SERVER_USERNAME} ${env.MAVEN_SERVER_PASSWORD} 禁用交互模式（即，使用批处理模式） 可以忽略下载日志，一种方式是在 Maven 命令中增加 -B 参数，另一种方式是在 settings.xml 配置文件中增加 false 配置。如果 Maven 库的 HTTPS 服务使用自建 CA 签名的证书，我们需要使用 keytool 工具创建一个将 CA 公钥添加至信任列表的 Java KeyStore。在 OpenShift 中使用 ConfigMap 将这个 Keystore 上传。使用 oc 命令基于文件创建一个 ConfigMap：oc create configmap maven-settings --from-file=settings.xml=settings.xml --from- file=myTruststore.jks=myTruststore.jks 将这个 ConfigMap 挂载至 Jenkins 代理容器。在本例中我们使用 /home/jenkins/.m2 目录，但这仅仅是因为配置文件 settings.xml 也对应这个 ConfigMap。KeyStore 可以放置在任意路径下。接着在容器环境变量 MAVEN_OPTS 中设置 Java 参数，以便让 Maven 对应的 Java 进程使用该文件：MAVEN_OPTS= -Djavax.net.ssl.trustStore=/home/jenkins/.m2/myTruststore.jks -Djavax.net.ssl.trustStorePassword=changeit 内存使用量这可能是最重要的一部分设置，如果没有正确的设置最大内存，我们会遇到间歇性构建失败，虽然每个组件都似乎工作正常。如果没有在 Java 命令行中设置堆大小，在容器中运行 Java 可能导致高内存使用量的报错。JVM 可以利用全部的宿主机内存，而不是使用容器内存现在并相应设置默认的堆大小。这通常会超过容器的内存资源总额，故当 Java 进程为堆分配过多内存时，OpenShift 会直接杀掉容器。虽然 jenkins-slave-base 镜像包含一个内建脚本设置堆最大为容器内存的一半（可以通过环境变量 CONTAINER_HEAP_PERCENT=0.50 修改），但这只适用于 Jenkins 代理节点中的 Java 进程。在 Maven 构建中，还有其它重要的 Java 进程运行：mvn 命令本身就是一个 Java 工具。Maven Surefire 插件 默认派生一个 JVM 用于运行单元测试。总结一下，容器中同时运行着三个重要的 Java 进程，预估内存使用量以避免 pod 被误杀是很重要的。每个进程都有不同的方式设置 JVM 参数：我们在上面提到了 Jenkins 代理容器堆最大值的计算方法，但我们显然不应该让代理容器使用如此大的堆，毕竟还有两个 JVM 需要使用内存。对于 Jenkins 代理容器，可以设置 JAVA_OPTS。mvn 工具被 Jenkins 任务调用。设置 MAVEN_OPTS 可以用于自定义这类 Java 进程。Maven surefire 插件滋生的用于单元测试的 JVM 可以通过 Maven argLine 属性自定义。可以在 pom.xml 或 settings.xml 的某个配置文件中设置，也可以直接在 maven 命令参数 MAVEN_OPS 中增加 -DargLine=…。下面例子给出 Maven 代理容器环境变量设置方法：JAVA_OPTS=-Xms64m -Xmx64m MAVEN_OPTS=-Xms128m -Xmx128m -DargLine=${env.SUREFIRE_OPTS} SUREFIRE_OPTS=-Xms256m -Xmx256m 我们的测试环境是具有 1024Mi 内存限额的代理容器，使用上述参数可以正常构建一个 SpringBoot 应用并进行单元测试。测试环境使用的资源相对较小，对于复杂的 Maven 项目和对应的单元测试，我们需要更大的堆大小及更大的容器内存限额。注：Java8 进程的实际内存使用量包括“堆大小 + 元数据 + 堆外内存”，因此内存使用量会明显高于设置的最大堆大小。在我们上面的测试环境中，三个 Java 进程使用了超过 900Mi 的内存。可以在容器内查看进程的 RSS 内存使用情况，命令如下：ps -e -o pid,user,rss,comm,args。Jenkins 代理镜像同时安装了 JDK 64 位和 32 位版本。对于 mvn 和 surefire，默认使用 64 位版本 JVM。为减低内存使用量，只要 -Xmx 不超过 1.5 GB，强制使用 32 位 JVM 都是有意义的。JAVA_HOME=/usr/lib/jvm/Java-1.8.0-openjdk-1.8.0.161–0.b14.el7_4.i386 注意到我们可以在 JAVA_TOOL_OPTIONS 环境变量中设置 Java 参数，每个 JVM 启动时都会读取该参数。JAVA_OPTS 和 MAVEN_OPTS 中的参数会覆盖 JAVA_TOOL_OPTIONS 中的对应值，故我们可以不使用 argLine，实现对 Java 进程同样的堆配置：JAVA_OPTS=-Xms64m -Xmx64m MAVEN_OPTS=-Xms128m -Xmx128m JAVA_TOOL_OPTIONS=-Xms256m -Xmx256m 但缺点是每个 JVM 的日志中都会显示 Picked up JAVA_TOOL_OPTIONS:，这可能让人感到迷惑。Jenkins 流水线完成上述配置，我们应该已经可以完成一次成功的构建。我们可以获取源代码，下载依赖，运行单元测试并将成品上传到我们的库中。我们可以通过创建一个 Jenkins 流水线项目来完成上述操作。pipeline { /* Which container to bring up for the build. Pick one of the templates configured in Kubernetes plugin. */ agent { label 'maven' } stages { stage('Pull Source') { steps { git url: 'ssh://git@git.mycompany.com:22/myapplication.git', branch: 'master' } } stage('Unit Tests') { steps { sh 'mvn test' } } stage('Deploy to Nexus') { steps { sh 'mvn deploy -DskipTests' } } } } 当然，对应真实项目，CI/CD 流水线不仅仅完成 Maven 构建，还可以部署到开发环境，运行集成测试，提升至更接近于生产的环境等。上面给出的学习资料中有执行这些操作的案例。多容器一个 pod 可以运行多个容器，每个容器有单独的资源限制。这些容器共享网络接口，故我们可以从 localhost 访问已启动的服务，但我们需要考虑端口冲突的问题。在一个 Kubernetes pod 模板中，每个容器的环境变量是单独设置的，但挂载的卷是统一的。当一个外部服务需要单元测试且嵌入式方案无法工作（例如，数据库、消息中间件等） 时，可以启动多个容器。在这种情况下，第二个容器会随着 Jenkins 代理容器启停。查看 Jenkins config.xml 片段，其中我们启动了一个辅助的 httpbin 服务用于 Maven 构建：<org.csanchez.jenkins.plugins.kubernetes.PodTemplate> maven ... <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> jnlp registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.7 500m 1024Mi ... ... </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> httpbin citizenstig/httpbin 256Mi ... </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> </org.csanchez.jenkins.plugins.kubernetes.PodTemplate> 总结作为总结，我们查看上面已描述配置的 Jenkins config.xml 对应创建的 OpenShift 资源以及 Kubernetes 插件的配置。apiVersion: v1 kind: List metadata: {} items: - apiVersion: v1 kind: ConfigMap metadata: name: git-config data: config: | [credential] helper = store --file=/home/jenkins/.config/git-secret/credentials [http "http://git.mycompany.com"] sslCAInfo = /home/jenkins/.config/git/myTrustedCA.pem myTrustedCA.pem: |- -----BEGIN CERTIFICATE----- MIIDVzCCAj+gAwIBAgIJAN0sC... -----END CERTIFICATE----- - apiVersion: v1 kind: Secret metadata: name: git-secret stringData: ssh-privatekey: |- -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY----- credentials: |- https://username:password@git.mycompany.com https://user:pass@github.com - apiVersion: v1 kind: ConfigMap metadata: name: git-ssh data: config: |- Host git.mycompany.com StrictHostKeyChecking yes IdentityFile /home/jenkins/.config/git-secret/ssh-privatekey known_hosts: '[git.mycompany.com]:22 ecdsa-sha2-nistp256 AAAdn7...' - apiVersion: v1 kind: Secret metadata: name: maven-secret stringData: username: admin password: admin123 基于文件创建另一个 ConfigMap：oc create configmap maven-settings --from-file=settings.xml=settings.xml --from-file=myTruststore.jks=myTruststore.jks Kubernetes 插件配置如下：<?xml version='1.0' encoding='UTF-8'?> ... <org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud plugin="kubernetes@1.0"> openshift <defaultsProviderTemplate></defaultsProviderTemplate> <org.csanchez.jenkins.plugins.kubernetes.PodTemplate> maven false false 2147483647 100 0 maven jenkins37 NORMAL <customWorkspaceVolumeEnabled>false</customWorkspaceVolumeEnabled> false <org.csanchez.jenkins.plugins.kubernetes.volumes.SecretVolume> /home/jenkins/.config/git-secret git-secret </org.csanchez.jenkins.plugins.kubernetes.volumes.SecretVolume> <org.csanchez.jenkins.plugins.kubernetes.volumes.ConfigMapVolume> /home/jenkins/.ssh git-ssh </org.csanchez.jenkins.plugins.kubernetes.volumes.ConfigMapVolume> <org.csanchez.jenkins.plugins.kubernetes.volumes.ConfigMapVolume> /home/jenkins/.config/git git-config </org.csanchez.jenkins.plugins.kubernetes.volumes.ConfigMapVolume> <org.csanchez.jenkins.plugins.kubernetes.volumes.ConfigMapVolume> /home/jenkins/.m2 maven-settings </org.csanchez.jenkins.plugins.kubernetes.volumes.ConfigMapVolume> <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> jnlp registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7:v3.7 false false /tmp ${computer.jnlpmac} ${computer.name} false 500m 1024Mi 500m 1024Mi <org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.161-0.b14.el7_4.i386 </org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> <org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> JAVA_OPTS -Xms64m -Xmx64m </org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> <org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> MAVEN_OPTS -Xms128m -Xmx128m -DargLine=${env.SUREFIRE_OPTS} -Djavax.net.ssl.trustStore=/home/jenkins/.m2/myTruststore.jks -Djavax.net.ssl.trustStorePassword=changeit </org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> <org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> SUREFIRE_OPTS -Xms256m -Xmx256m </org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> <org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> MAVEN_MIRROR_URL https://nexus.mycompany.com/repository/maven-public </org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar> <org.csanchez.jenkins.plugins.kubernetes.model.SecretEnvVar> MAVEN_SERVER_USERNAME maven-secret username </org.csanchez.jenkins.plugins.kubernetes.model.SecretEnvVar> <org.csanchez.jenkins.plugins.kubernetes.model.SecretEnvVar> MAVEN_SERVER_PASSWORD maven-secret password </org.csanchez.jenkins.plugins.kubernetes.model.SecretEnvVar> <ports/> 0 0 0 0 0 </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> httpbin citizenstig/httpbin false false /run.sh false 256Mi 256Mi <ports/> 0 0 0 0 0 </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate> <annotations/> <imagePullSecrets/> </org.csanchez.jenkins.plugins.kubernetes.PodTemplate> https://172.30.0.1:443 -----BEGIN CERTIFICATE----- MIIC6jCC... -----END CERTIFICATE----- false first http://jenkins.cicd.svc:80 jenkins-jnlp.cicd.svc:50000 1a12dfa4-7fc5-47a7-aa17-cc56572a41c7 10 5 0 0 32 </org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud> 尝试愉快的构建吧！原文发表于 ITNext，已获得翻版授权。via: https://opensource.com/article/18/4/running-jenkins-builds-containers作者：Balazs Szeti 选题：lujun9972 译者：pinewall 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
如何在 Ubuntu16.04 中用 Apache 部署 Jenkins 自动化服务器
Jenkins 是从 Hudson 项目衍生出来的自动化服务器。Jenkins 是一个基于服务器的应用程序，运行在 Java servlet 容器中，它支持包括 Git、SVN 以及 Mercurial 在内的多种 SCM（ 源码控制工具 （ Source Control Management ） ）。Jenkins 提供了上百种插件帮助你的项目实现自动化。Jenkins 由 Kohsuke Kawaguchi 开发，在 2011 年使用 MIT 协议发布了第一个发行版，它是个自由软件。在这篇指南中，我会向你介绍如何在 Ubuntu 16.04 中安装最新版本的 Jenkins。我们会用自己的域名运行 Jenkins，在 apache web 服务器中安装和配置 Jenkins，而且支持反向代理。前提Ubuntu 16.04 服务器 - 64 位Root 权限第一步 - 安装 Java OpenJDK 7Jenkins 基于 Java，因此我们需要在服务器上安装 Java OpenJDK 7。在这里，我们会从一个 PPA 仓库安装 Java 7，首先我们需要添加这个仓库。默认情况下，Ubuntu 16.04 没有安装用于管理 PPA 仓库的 python-software-properties 软件包，因此我们首先需要安装这个软件。使用 apt 命令安装 python-software-properties。apt-get install python-software-properties 下一步，添加 Java PPA 仓库到服务器中。add-apt-repository ppa:openjdk-r/ppa 用 apt 命令更新 Ubuntu 仓库并安装 Java OpenJDK。apt-get update apt-get install openjdk-7-jdk 输入下面的命令验证安装：java -version 你会看到安装到服务器上的 Java 版本。第二步 - 安装 JenkinsJenkins 给软件安装包提供了一个 Ubuntu 仓库，我们会从这个仓库中安装 Jenkins。用下面的命令添加 Jenkins 密钥和仓库到系统中。wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add - echo 'deb https://pkg.jenkins.io/debian-stable binary/' | tee -a /etc/apt/sources.list 更新仓库并安装 Jenkins。apt-get update apt-get install jenkins 安装完成后，用下面的命令启动 Jenkins。systemctl start jenkins 通过检查 Jenkins 默认使用的端口（端口 8080）验证 Jenkins 正在运行。我会像下面这样用 netstat 命令检测：netstat -plntu Jenkins 已经安装好了并运行在 8080 端口。第三步 - 为 Jenkins 安装和配置 Apache 作为反向代理在这篇指南中，我们会在一个 Apache web 服务器中运行 Jenkins，我们会为 Jenkins 配置 apache 作为反向代理。首先我会安装 apache 并启用一些需要的模块，然后我会为 Jenkins 用域名 my.jenkins.id 创建虚拟主机文件。请在这里使用你自己的域名并在所有配置文件中出现的地方替换。从 Ubuntu 仓库安装 apache2 web 服务器。apt-get install apache2 安装完成后，启用 proxy 和 proxy\_http 模块以便将 apache 配置为 Jenkins 的前端服务器/反向代理。a2enmod proxy a2enmod proxy_http 下一步，在 sites-available 目录创建新的虚拟主机文件。cd /etc/apache2/sites-available/ vim jenkins.conf 粘贴下面的虚拟主机配置。 ServerName my.jenkins.id ProxyRequests Off ProxyPreserveHost On AllowEncodedSlashes NoDecode Order deny,allow Allow from all ProxyPass / http://localhost:8080/ nocanon ProxyPassReverse / http://localhost:8080/ ProxyPassReverse / http://my.jenkins.id/ 保存文件。然后用 a2ensite 命令激活 Jenkins 虚拟主机。a2ensite jenkins 重启 Apache 和 Jenkins。systemctl restart apache2 systemctl restart jenkins 检查 Jenkins 和 Apache 正在使用 80 和 8080 端口。netstat -plntu 第四步 - 配置 JenkinsJenkins 用域名 'my.jenkins.id' 运行。打开你的 web 浏览器然后输入 URL。你会看到要求你输入初始管理员密码的页面。Jenkins 已经生成了一个密码，因此我们只需要显示并把结果复制到密码框。用 cat 命令显示 Jenkins 初始管理员密码。cat /var/lib/jenkins/secrets/initialAdminPassword a1789d1561bf413c938122c599cf65c9 将结果粘贴到密码框然后点击 Continue。现在为了后面能比较好的使用，我们需要在 Jenkins 中安装一些插件。选择 Install Suggested Plugin，点击它。Jenkins 插件安装过程：安装完插件后，我们需要创建一个新的管理员密码。输入你的管理员用户名、密码、电子邮件等，然后点击 ‘Save and Finish’。点击 start 开始使用 Jenkins。你会被重定向到 Jenkins 管理员面板。成功完成 Jenkins 安装和配置。第五步 - Jenkins 安全在 Jenkins 管理员面板，我们需要为 Jenkins 配置标准的安全，点击 ‘Manage Jenkins’ 和 ‘Configure Global Security’。Jenkins 在 ‘Access Control’ 部分提供了多种认证方法。为了能够控制所有的用户权限，我选择了 ‘Matrix-based Security’。在复选框 ‘User/Group’ 中启用 admin 用户。通过勾选所有选项给 admin 所有权限，给 anonymous 只读权限。现在点击 ‘Save’。你会被重定向到面板，如果出现了登录选项，只需输入你的管理员账户和密码。第六步 - 测试一个简单的自动化任务在这一部分，我想为 Jenkins 服务测试一个简单的任务。为了测试 Jenkins 我会创建一个简单的任务，并用 top 命令查看服务器的负载。在 Jenkins 管理员面板上，点击 ‘Create New Job’。输入任务的名称，在这里我输入 ‘Checking System’，选择 Freestyle Project 然后点击 OK。进入 Build 标签页。在 Add build step，选择选项 Execute shell。在输入框输入下面的命令。top -b -n 1 | head -n 5 点击 Save。现在你是在任务 ‘Project checking system’ 的任务页。点击 Build Now 执行任务 ‘checking system’。任务执行完成后，你会看到 Build History，点击第一个任务查看结果。下面是 Jenkins 任务执行的结果。到这里就介绍完了在 Ubuntu 16.04 中用 Apache web 服务器安装 Jenkins 的内容。via: https://www.howtoforge.com/tutorial/how-to-install-jenkins-with-apache-on-ubuntu-16-04/作者：Muhammad Arul 译者：ictlyh 校对：wxy本文由 LCTT 组织编译，Linux中国 荣誉推出