如何使用Docker和Jenkins配置即代码自动化Jenkins设置
作为 Write for DOnations 计划的一部分,作者选择了 Wikimedia Foundation 来接受捐赠。
介绍
Jenkins 是最流行的开源自动化服务器之一,通常用于编排 持续集成 (CI) 和/或持续部署 (CD) 工作流程。
配置 Jenkins 通常是通过基于 Web 的设置向导手动完成的; 这可能是一个缓慢、容易出错且不可扩展的过程。 您可以按照 How To Install Jenkins on Ubuntu 18.04 指南的 Step 4 — Setting Up Jenkins 查看所涉及的步骤。 此外,配置无法在 Git 之类的 版本控制系统 (VCS) 中进行跟踪,也无法接受任何代码审查过程的审查。
在本教程中,您将使用 Docker 和 Jenkins Configuration as Code (JCasC) 方法自动安装和配置 Jenkins。
Jenkins 使用可插拔架构来提供其大部分功能。 JCasC 使用 Configuration as Code 插件,它允许您将 Jenkins 配置的所需状态定义为一个或多个 YAML 文件,无需设置向导。 在初始化时,Configuration as Code 插件会根据配置文件配置 Jenkins,大大减少了配置时间并消除了人为错误。
Docker 是创建和运行 容器 的事实标准,它是一种 虚拟化 技术,允许您在不同的 [ X224X]操作系统(操作系统)和硬件架构。 您将使用 Docker 运行 Jenkins 实例,以利用这种一致性和跨平台功能。
本教程首先指导您设置 JCasC。 然后,您将逐步添加到 JCasC 配置文件以设置用户、配置身份验证和授权,并最终保护您的 Jenkins 实例。 完成本教程后,您将创建一个自定义 Docker 映像,该映像设置为在启动时使用 Configuration as Code 插件来自动配置和保护您的 Jenkins 实例。
先决条件
要完成本教程,您需要:
- 访问至少安装了 2GB RAM 和 Docker 的服务器。 这可以是您的本地开发机器、Droplet 或任何类型的服务器。 按照 如何安装和使用 Docker 集合中的教程之一中的 Step 1 — 安装 Docker 来设置 Docker。
注意:本教程在Ubuntu 18.04上测试; 但是,由于 Docker 映像是独立的,因此此处概述的步骤适用于任何安装了 Docker 的操作系统。
第 1 步 — 禁用设置向导
使用 JCasC 无需显示设置向导; 因此,在第一步中,您将创建禁用设置向导的官方 jenkins/jenkins 映像的修改版本。 您将通过创建 Dockerfile
并从中构建自定义 Jenkins 映像来完成此操作。
jenkins/jenkins
映像允许您通过 JAVA_OPTS
环境变量传入名为 jenkins.install.runSetupWizard
的 系统属性 来启用或禁用设置向导。 映像的用户可以在运行时使用 --env 标志将 JAVA_OPTS
环境变量传递给 docker run
。 但是,这种方法会将禁用设置向导的责任推给映像的用户。 相反,您应该在构建时禁用设置向导,以便默认禁用设置向导。
您可以通过创建 Dockerfile
并使用 ENV 指令设置 JAVA_OPTS
环境变量来实现此目的。
首先,在您的服务器中创建一个新目录来存储您将在本教程中创建的文件:
mkdir -p $HOME/playground/jcasc
然后,在该目录中导航:
cd $HOME/playground/jcasc
接下来,使用您的编辑器,创建一个名为 Dockerfile
的新文件:
nano $HOME/playground/jcasc/Dockerfile
然后,将以下内容复制到Dockerfile
中:
〜/游乐场/jcasc/
FROM jenkins/jenkins:latest ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
在这里,您使用 FROM 指令将 jenkins/jenkins:latest
指定为 基础映像,并使用 ENV
指令来设置 [ X143X] 环境变量。
按 CTRL+X
然后按 Y
保存文件并退出编辑器。
完成这些修改后,构建一个新的自定义 Docker 映像并为其分配一个唯一标签(我们将在此处使用 jcasc
):
docker build -t jenkins:jcasc .
您将看到类似于以下内容的输出:
OutputSending build context to Docker daemon 2.048kB Step 1/2 : FROM jenkins/jenkins:latest ---> 1f4b0aaa986e Step 2/2 : ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false ---> 7566b15547af Successfully built 7566b15547af Successfully tagged jenkins:jcasc
构建完成后,通过运行 docker run 运行您的自定义镜像:
docker run --name jenkins --rm -p 8080:8080 jenkins:jcasc
您使用 --name jenkins
选项为您的容器提供了一个易于记忆的名称; 否则将使用随机的十六进制 ID(例如 f1d701324553
)。 您还指定了 --rm
标志,以便在您停止容器进程后自动删除容器。 最后,您已使用 -p
标志将服务器主机的端口 8080
配置为代理到容器的端口 8080
; 8080
是提供 Jenkins Web UI 的默认端口。
Jenkins 将需要很短的时间来启动。 当 Jenkins 准备就绪时,您将在输出中看到以下行:
Output... hudson.WebAppMain$3#run: Jenkins is fully up and running
现在,打开浏览器到 server_ip:8080
。 在没有设置向导的情况下,您会立即看到仪表板。
您刚刚确认设置向导已被禁用。 要清理,请按 CTRL+C
停止容器。 如果您之前指定了 --rm
标志,则停止的容器将被自动删除。
在此步骤中,您已创建禁用设置向导的自定义 Jenkins 映像。 但是,Web 界面的右上角现在显示一个红色通知图标,表示设置存在问题。 单击图标可查看详细信息。
第一个警告通知您尚未配置 Jenkins URL。 第二个告诉您您尚未配置任何身份验证和授权方案,并且匿名用户具有在您的 Jenkins 实例上执行所有操作的完全权限。 以前,设置向导会指导您解决这些问题。 现在您已禁用它,您需要使用 JCasC 复制相同的功能。 本教程的其余部分将涉及修改您的 Dockerfile
和 JCasC 配置,直到不再存在问题(即,直到红色通知图标消失)。
在下一步中,您将通过将一系列 Jenkins 插件(包括 Configuration as Code 插件)预安装到您的自定义 Jenkins 映像中来开始该过程。
第 2 步 — 安装 Jenkins 插件
要使用 JCasC,您需要安装 Configuration as Code 插件。 目前,没有安装任何插件。 您可以通过导航到 http://server_ip:8080/pluginManager/installed
来确认这一点。
在这一步中,您将修改 Dockerfile
以预安装一系列插件,包括 Configuration as Code 插件。
要自动化插件安装过程,您可以使用 jenkins/jenkins
Docker 映像附带的安装脚本。 您可以在 /usr/local/bin/install-plugins.sh
的容器内找到它。 要使用它,您需要:
- 创建一个包含要安装的插件列表的文本文件
- 将其复制到 Docker 映像中
- 运行
install-plugins.sh
脚本安装插件
首先,使用您的编辑器,创建一个名为 plugins.txt
的新文件:
nano $HOME/playground/jcasc/plugins.txt
然后,添加以下以换行符分隔的插件名称和版本列表(使用格式 <id>:<version>
):
~/playground/jcasc/plugins.txt
ant:latest antisamy-markup-formatter:latest build-timeout:latest cloudbees-folder:latest configuration-as-code:latest credentials-binding:latest email-ext:latest git:latest github-branch-source:latest gradle:latest ldap:latest mailer:latest matrix-auth:latest pam-auth:latest pipeline-github-lib:latest pipeline-stage-view:latest ssh-slaves:latest timestamper:latest workflow-aggregator:latest ws-cleanup:latest
保存文件并退出编辑器。
该列表包含 Configuration as Code 插件,以及设置向导建议的所有插件(自 Jenkins v2.251 起正确)。 例如,您有 Git 插件,它允许 Jenkins 使用 Git 存储库; 您还有 Pipeline 插件,它实际上是一套插件,允许您将 Jenkins 作业定义为代码。
注:最新推荐插件列表可以从源码中推断出来。 您还可以在 plugins.jenkins.io 找到最受欢迎的社区贡献插件列表。 随意将您想要的任何其他插件包含在列表中。
接下来,打开 Dockerfile
文件:
nano $HOME/playground/jcasc/Dockerfile
在其中添加一条COPY
指令,将plugins.txt
文件复制到镜像内的/usr/share/jenkins/ref/
目录下; 这是 Jenkins 通常寻找插件的地方。 然后,包含一个额外的 RUN
指令来运行 install-plugins.sh
脚本:
~/playground/jcasc/Dockerfile
FROM jenkins/jenkins ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false COPY plugins.txt /usr/share/jenkins/ref/plugins.txt RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
保存文件并退出编辑器。 然后,使用修改后的 Dockerfile
构建一个新镜像:
docker build -t jenkins:jcasc .
此步骤涉及下载许多插件并将其安装到映像中,并且可能需要一些时间才能运行,具体取决于您的 Internet 连接。 插件安装完成后,运行新的 Jenkins 镜像:
docker run --name jenkins --rm -p 8080:8080 jenkins:jcasc
在 stdout
上出现 Jenkins is fully up and running
消息后,导航到 server_ip:8080/pluginManager/installed
以查看已安装插件的列表。 您将在 plugins.txt
中指定的所有插件旁边看到一个实心复选框,以及插件旁边的褪色复选框,这些复选框是这些插件的依赖项。
确认 Configuration As Code 插件已安装后,按 CTRL+C
终止容器进程。
在这一步中,您已经安装了所有建议的 Jenkins 插件和 Configuration as Code 插件。 您现在已准备好使用 JCasC 来解决通知框中列出的问题。 在下一步中,您将修复第一个问题,该问题警告您 Jenkins 根 URL 为空 。
第 3 步 - 指定 Jenkins URL
Jenkins URL 是 Jenkins 实例的 URL,可从需要访问它的设备路由。 例如,如果您将 Jenkins 部署为专用网络中的节点,则 Jenkins URL 可能是专用 IP 地址,或者是可使用专用 DNS 服务器解析的 DNS 名称。 对于本教程,使用服务器的 IP 地址(或本地主机的 127.0.0.1
)来构成 Jenkins URL 就足够了。
您可以通过导航到 server_ip:8080/configure
并在 Jenkins Location 标题下的 Jenkins URL 字段中输入值来在 Web 界面上设置 Jenkins URL。 以下是使用 Configuration as Code 插件实现相同目的的方法:
- 在声明性配置文件(我们将其称为
casc.yaml
)中定义所需的 Jenkins 实例配置。 - 将配置文件复制到 Docker 映像中(就像您对
plugins.txt
文件所做的那样)。 - 将
CASC_JENKINS_CONFIG
环境变量设置为配置文件的路径,以指示 Configuration as Code 插件读取它。
首先,创建一个名为 casc.yaml
的新文件:
nano $HOME/playground/jcasc/casc.yaml
然后,添加以下行:
~/playground/jcasc/casc.yaml
unclassified: location: url: http://server_ip:8080/
unclassified.location.url
是设置 Jenkins URL 的路径。 它只是可以使用 JCasC 设置的众多属性之一。 有效属性由安装的插件决定。 例如,jenkins.authorizationStrategy.globalMatrix.permissions
属性只有在安装了 Matrix Authorization Strategy 插件时才有效。 要查看可用的属性,请导航到 server_ip:8080/configuration-as-code/reference
,您将找到针对您的特定 Jenkins 安装定制的文档页面。
保存 casc.yaml
文件,退出编辑器,然后打开 Dockerfile
文件:
nano $HOME/playground/jcasc/Dockerfile
将 COPY
指令添加到 Dockerfile
的末尾,将 casc.yaml
文件复制到 /var/jenkins_home/casc.yaml
处的图像中。 您选择了 /var/jenkins_home/
因为这是 Jenkins 存储其所有数据的默认目录:
~/playground/jcasc/Dockerfile
FROM jenkins/jenkins:latest ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false COPY plugins.txt /usr/share/jenkins/ref/plugins.txt RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt COPY casc.yaml /var/jenkins_home/casc.yaml
然后,添加另一个 ENV
指令来设置 CASC_JENKINS_CONFIG
环境变量:
~/playground/jcasc/Dockerfile
FROM jenkins/jenkins:latest ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false ENV CASC_JENKINS_CONFIG /var/jenkins_home/casc.yaml COPY plugins.txt /usr/share/jenkins/ref/plugins.txt RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt COPY casc.yaml /var/jenkins_home/casc.yaml
注意: 你把 ENV
指令放在顶部附近,因为它是你不太可能改变的东西。 通过将其放在 COPY
和 RUN
指令之前,可以避免在更新 casc.yaml
或 plugins.txt
时使缓存层失效。
保存文件并退出编辑器。 接下来,构建镜像:
docker build -t jenkins:jcasc .
并运行更新后的 Jenkins 镜像:
docker run --name jenkins --rm -p 8080:8080 jenkins:jcasc
一旦出现 Jenkins is fully up and running
日志行,导航到 server_ip:8080
以查看仪表板。 这一次,你可能已经注意到通知计数减少了 1,关于 Jenkins URL 的警告也消失了。
现在,导航到 server_ip:8080/configure
并向下滚动到 Jenkins URL 字段。 确认 Jenkins URL 已设置为与 casc.yaml
文件中指定的值相同的值。
最后,按 CTRL+C
停止容器进程。
在此步骤中,您使用 Configuration as Code 插件来设置 Jenkins URL。 在下一步中,您将解决通知列表中的第二个问题(Jenkins 当前是不安全的 消息)。
第 4 步 — 创建用户
到目前为止,您的设置尚未实现任何身份验证和授权机制。 在此步骤中,您将设置一个基本的、基于密码的身份验证方案并创建一个名为 admin
的新用户。
首先打开您的 casc.yaml
文件:
nano $HOME/playground/jcasc/casc.yaml
然后,添加突出显示的代码段:
~/playground/jcasc/casc.yaml
jenkins: securityRealm: local: allowsSignup: false users: - id: ${JENKINS_ADMIN_ID} password: ${JENKINS_ADMIN_PASSWORD} unclassified: ...
在 Jenkins 的上下文中, 安全领域 只是一种身份验证机制; 本地安全领域意味着使用基本身份验证,用户必须指定他们的 ID/用户名和密码。 存在其他安全领域并由插件提供。 例如,LDAP 插件允许您使用现有的 LDAP 目录服务作为身份验证机制。 GitHub Authentication 插件允许您使用您的 GitHub 凭据通过 OAuth 进行身份验证。
请注意,您还指定了 allowsSignup: false
,它可以防止匿名用户通过 Web 界面创建帐户。
最后,不是硬编码用户 ID 和密码,而是使用可以在运行时填充其值的变量。 这很重要,因为使用 JCasC 的好处之一是可以将 casc.yaml
文件提交到源代码控制中; 如果您将用户密码以明文形式存储在配置文件中,您将有效地破坏凭据。 相反,变量是使用 ${VARIABLE_NAME}
语法定义的,其值可以使用同名的环境变量或放置在 /run/secrets/
目录中的同名文件来填充容器图像。
接下来,构建一个新映像以合并对 casc.yaml
文件所做的更改:
docker build -t jenkins:jcasc .
然后,运行更新的 Jenkins 映像,同时通过 --env 选项传入 JENKINS_ADMIN_ID
和 JENKINS_ADMIN_PASSWORD
环境变量(将 <password>
替换为密码你的选择):
docker run --name jenkins --rm -p 8080:8080 --env JENKINS_ADMIN_ID=admin --env JENKINS_ADMIN_PASSWORD=password jenkins:jcasc
您现在可以转到 server_ip:8080/login
并使用指定的凭据登录。
成功登录后,您将被重定向到仪表板。
通过按 CTRL+C
停止容器来完成此步骤。
在这一步中,您使用 JCasC 创建了一个名为 admin
的新用户。 您还学习了如何将敏感数据(如密码)保存在 VCS 跟踪的文件之外。 但是,到目前为止,您只配置了用户身份验证; 你还没有实现任何授权机制。 在下一步中,您将使用 JCasC 为您的 admin
用户授予管理权限。
第 5 步 — 设置授权
设置安全领域后,您现在必须配置授权策略。 在此步骤中,您将使用 Matrix Authorization Strategy 插件为您的 admin
用户配置权限。
默认情况下,Jenkins 核心安装为我们提供了三种授权策略:
unsecured
:每个用户,包括匿名用户,都拥有做所有事情的完全权限legacy
:模拟旧版 Jenkins(v1.164 之前),角色为admin
的任何用户都被授予完全权限,而其他用户(包括匿名用户)被授予读取权限。
注: Jenkins中的角色可以是用户(例如daniel
)或组(例如developers
)
loggedInUsersCanDoAnything
:匿名用户没有访问权限或只读访问权限。 经过身份验证的用户拥有执行所有操作的完全权限。 通过仅允许经过身份验证的用户执行操作,您可以对哪些用户执行了哪些操作进行审计跟踪。
注: 其他授权策略及其相关插件可以在文档中探索; 这些包括处理身份验证和授权的插件。
所有这些授权策略都非常粗糙,并且无法对如何为不同用户设置权限进行精细控制。 相反,您可以使用已包含在 plugins.txt
列表中的矩阵授权策略插件。 该插件为您提供更精细的授权策略,并允许您在全局以及每个项目/作业中设置用户权限。
Matrix Authorization Strategy 插件允许您使用 jenkins.authorizationStrategy.globalMatrix.permissions
JCasC 属性来设置全局权限。 要使用它,请打开您的 casc.yaml
文件:
nano $HOME/playground/jcasc/casc.yaml
并添加突出显示的片段:
~/playground/jcasc/casc.yaml
... - id: ${JENKINS_ADMIN_ID} password: ${JENKINS_ADMIN_PASSWORD} authorizationStrategy: globalMatrix: permissions: - "Overall/Administer:admin" - "Overall/Read:authenticated" unclassified: ...
globalMatrix
属性设置全局权限(相对于每个项目的权限)。 permissions
属性是格式为 <permission-group>/<permission-name>:<role>
的字符串列表。 在这里,您将 Overall/Administer
权限授予 admin
用户。 您还将 Overall/Read
权限授予 authenticated
,这是代表所有经过身份验证的用户的特殊角色。 还有另一个特殊角色称为 anonymous
,它将所有未经身份验证的用户组合在一起。 但是由于默认情况下权限被拒绝,如果您不想给匿名用户任何权限,您不需要显式地为其添加条目。
保存 casc.yaml
文件,退出编辑器,然后构建一个新图像:
docker build -t jenkins:jcasc .
然后,运行更新后的 Jenkins 镜像:
docker run --name jenkins --rm -p 8080:8080 --env JENKINS_ADMIN_ID=admin --env JENKINS_ADMIN_PASSWORD=password jenkins:jcasc
等待 Jenkins is fully up and running
日志行,然后导航到 server_ip:8080
。 您将被重定向到登录页面。 填写您的凭据,您将被重定向到主仪表板。
在这一步中,您已经为您的 admin
用户设置了全局权限。 但是,解决授权问题发现了现在显示在通知菜单中的其他问题。
因此,在下一步中,您将继续修改您的 Docker 映像,以解决每个问题,直到没有问题为止。
在继续之前,按 CTRL+C
停止容器。
第 6 步 — 设置构建授权
通知列表中的第一个问题与构建身份验证有关。 默认情况下,所有作业都以系统用户身份运行,该用户具有很多系统权限。 因此,Jenkins 用户只需定义和运行恶意作业或管道即可执行 权限提升; 这是不安全的。
相反,作业应该使用配置或触发它的同一 Jenkins 用户运行。 为此,您需要安装一个名为 Authorize Project 插件的附加插件。
打开plugins.txt
:
nano $HOME/playground/jcasc/plugins.txt
并添加突出显示的行:
~/playground/jcasc/plugins.txt
ant:latest antisamy-markup-formatter:latest authorize-project:latest build-timeout:latest ...
该插件提供了一个新的构建授权策略,您需要在 JCasC 配置中指定该策略。 退出plugins.txt
文件,打开casc.yaml
文件:
nano $HOME/playground/jcasc/casc.yaml
将突出显示的块添加到您的 casc.yaml
文件中:
~/playground/jcasc/casc.yaml
... - "Overall/Administer:admin" - "Overall/Read:authenticated" security: queueItemAuthenticator: authenticators: - global: strategy: triggeringUsersAuthorizationStrategy unclassified: ...
保存文件并退出编辑器。 然后,使用修改后的 plugins.txt
和 casc.yaml
文件构建一个新镜像:
docker build -t jenkins:jcasc .
然后,运行更新后的 Jenkins 镜像:
docker run --name jenkins --rm -p 8080:8080 --env JENKINS_ADMIN_ID=admin --env JENKINS_ADMIN_PASSWORD=password jenkins:jcasc
等待 Jenkins is fully up and running
日志行,然后导航到 server_ip:8080/login
,填写您的凭据,然后到达主仪表板。 打开通知菜单,您将看到与构建身份验证相关的问题不再出现。
在继续之前通过运行 CTRL+C
停止容器。
在此步骤中,您已将 Jenkins 配置为使用触发构建的用户而不是系统用户来运行构建。 这消除了通知列表中的问题之一。 在下一步中,您将解决与代理到控制器安全子系统相关的下一个问题。
第 7 步 — 启用代理到控制器访问控制
在本教程中,您只部署了一个运行所有构建的 Jenkins 实例。 但是,Jenkins 支持使用代理/控制器配置的分布式构建。 控制器负责提供 Web UI,向客户端公开 API 以向其发送请求,并协调构建。 代理是执行作业的实例。
这种配置的好处是它更具可扩展性和容错性。 如果运行 Jenkins 的服务器之一出现故障,其他实例可能会承担额外的负载。
但是,可能存在控制器无法信任代理的情况。 例如,OPS 团队可能管理 Jenkins 控制器,而外部承包商管理他们自己定制配置的 Jenkins 代理。 如果没有代理到控制器安全子系统,代理能够指示控制器执行它请求的任何操作,这可能是不希望的。 通过启用代理到控制器访问控制,您可以控制代理可以访问哪些命令和文件。
要启用代理到控制器访问控制,请打开 casc.yaml
文件:
nano $HOME/playground/jcasc/casc.yaml
然后,添加以下突出显示的行:
~/playground/jcasc/casc.yaml
... - "Overall/Administer:admin" - "Overall/Read:authenticated" remotingSecurity: enabled: true security: queueItemAuthenticator: ...
保存文件并构建一个新图像:
docker build -t jenkins:jcasc .
运行更新后的 Jenkins 镜像:
docker run --name jenkins --rm -p 8080:8080 --env JENKINS_ADMIN_ID=admin --env JENKINS_ADMIN_PASSWORD=password jenkins:jcasc
导航到 server_ip:8080/login
并像以前一样进行身份验证。 当您登陆主仪表板时,通知菜单将不再显示任何问题。
结论
您现在已经使用 JCasC 成功配置了一个简单的 Jenkins 服务器。 正如 Pipeline 插件 使开发人员能够在 Jenkinsfile
中定义他们的工作,Configuration as Code 插件使管理员能够在 YAML 文件中定义 Jenkins 配置。 这两个插件使 Jenkins 更接近 Everything as Code (EaC) 范式。
然而,让 JCasC 语法正确可能很困难,而且文档也很难破译。 如果您遇到困难并需要帮助,您可以在插件的 Gitter 聊天 中找到它。
虽然您已经使用 JCasC 配置了 Jenkins 的基本设置,但新实例不包含任何项目或作业。 为了更进一步,探索 Job DSL 插件,它允许我们将项目和作业定义为代码。 此外,您可以在 JCasC 配置文件中包含 Job DSL 代码,并在配置过程中创建项目和作业。