Jenkins 是一个免费且开源的自动化服务器,用于构建、测试和部署代码。 它是持续集成和持续交付 (CI/CD) 的支柱,可以每天为开发人员节省数小时的时间,并保护他们免于将失败的代码上线。 当代码确实失败时,或者当开发人员需要查看测试的输出时,Jenkins 提供日志文件以供查看。
默认的 Jenkins 管道日志可能难以阅读。 这个 Jenkins 日志记录基础知识的快速摘要提供了一些技巧(和代码),以使其更具可读性。
你将获得什么
Jenkins 管道分为 阶段。 Jenkins 自动记录每个阶段的开始,如下所示
[Pipeline] // stage
[Pipeline] stage (hide)
[Pipeline] { (Apply all openshift resources)
[Pipeline] dir
文本显示没有太多对比度,重要的内容(例如阶段的开始)没有突出显示。 在数百行长的管道日志中,找到一个阶段的开始和另一个阶段的结束位置,尤其是在您随意浏览日志以查找特定阶段时,可能会令人生畏。
Jenkins 管道编写为 Groovy 和 shell 脚本的混合。 在 Groovy 代码中,日志记录很少; 很多时候,它由命令中灰色的文本组成,没有细节。 在 shell 脚本中,调试模式 (set -x
) 已打开,因此每个 shell 命令都被完全实现(变量被解引用并打印值)并详细记录,输出也是如此。
鉴于内容如此之多,阅读日志以获取相关信息可能会很乏味。 由于管道中 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 语句的日志记录。 它并没有清理你的日志,而是使你的管道成为一个黑盒,隐藏你的管道正在做什么以及出现的任何错误。 确保你的管道的功能尽可能透明。 - 如果你的管道创建了开发人员和/或 DevOps 人员可以用来帮助调试问题的中间产物,那么也记录它们的内容。 是的,它使日志更长,但它只是文本。 它将在某个时候成为有用的信息,如果正确利用,日志又是什么,而不是关于发生了什么和为什么发生的大量信息?
Kubernetes Secrets:完全透明不起作用的地方
有些东西你不希望最终出现在你的日志中并被公开。 如果你正在使用 Kubernetes 并引用 Kubernetes Secret 中保存的数据,那么你绝对不希望该数据在日志中公开,因为该数据只是被模糊处理而不是加密。
想象一下,你想获取一些保存在 Secret 中的数据并将其注入到模板化的 JSON 文件中。 (对于此示例,Secret 和 JSON 模板的全部内容无关紧要。)你想保持透明并记录你正在做的事情,因为这是最佳实践,但你不想公开你的 Secret 数据。
将你的脚本模式从调试 (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 透明的同时,也保持你的 Secrets 隐藏之间的最佳平衡。
评论已关闭。