自动化是 DevOps 的核心。各种自动化工具和技术真正实现了持续集成和持续交付的概念。这些工具多年来发展迅速,但似乎永远存在的一个名字是Jenkins。
我们不会在这篇文章中讨论 CI-CD 的介绍性概念,也不会浪费时间展示 Jenkins 安装步骤。如果您是 Jenkins 的新手,可以查看官方安装文档以开始使用 Jenkins。因此,这篇文章的目的是讨论如何设置 Jenkins 控制器-代理架构(也称为主从架构)并解决执行过程中出现的一些问题。这是因为,这个过程可能很乏味,如果您有一段时间没有这样做,您可能会浪费几个小时。
[为什么选择 Jenkins 控制器-代理架构?
控制器(主)节点是 Jenkin 的大脑,它是 Jenkins 应用程序运行的地方。如果我们在控制器节点上做太多工作(或它崩溃),整个应用程序可能变得不可用。因此,我们希望 master 尽可能可用。这可以通过将工作委托给代理节点(从节点)来完成。因此,在 Jenkins Controller-Agent 架构中,作业由控制器调度并分配给代理。控制器还跟踪从服务器是否在线,检索它们对构建结果的响应,并将构建结果输出到控制台。因此,主节点更可用,因此我们的 Jenkins 服务器的整体性能使用这种设计得到了提高。
这种架构的另一个优点是我们只能在控制器节点上安装最少的工具集,而我们可以在代理节点上安装较重的工具(作业需要)。这使控制器保持轻量级,还允许我们根据应执行它们的代理来组织我们的作业。在上面的示例中,我们有一个 Jenkins 控制器和 4 个代理。每个代理都可以用于特定目的。
- 例如,如果我们需要运行测试并构建基于 javascript 的应用程序的作业,我们可以限制这些作业在最左侧的代理上执行。
- 同样,如果我们需要构建一些 .NET 应用程序,我们可以使用 Windows 主机设置 Jenkins 代理,并将这些作业限制在最右侧执行。
- 此外,我们可以根据系统要求平衡负载来提高性能。比方说,我们正在为一个拥有数百个微服务的系统设置 CI-CD,其中使用基于 python 的堆栈编写的服务数量是任何其他堆栈的两倍。在这种情况下,我们可以安装两个基于 python 的工具的 Jenkins 代理,Jenkins 控制器可以平衡这两个代理之间的负载。
设置
第 1 步:启动 Jenkins 控制器(主)容器
我们可以使用官方的 jenkins docker 容器。这是您可以使用的示例 docker-compose 文件。
version: '3.8'
services:
jenkins_controller:
image: jenkins/jenkins:lts-jdk11
privileged: true
user: root
container_name: $CONTAINER_NAME
ports:
- 50001:8080
- 50002:50000
volumes:
- $JENKINS_HOME:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
现在,我们需要启动容器,并且需要为 Jenkins 控制器节点安装所需的工具。我们可以编写一个简单的 bash 脚本来实现这一点。
#!/bin/bash
set -o nounset
JENKINS_CONTAINER_NAME=$1
JENKINS_HOME=`pwd`/jenkins/jenkins_home
echo "JENKINS HOME: $JENKINS_HOME"
CONTAINER_NAME=$JENKINS_CONTAINER_NAME JENKINS_HOME=$JENKINS_HOME docker-compose -f docker-compose-controller.yaml up --build -d
sleep 10
# Here we have just installed git for our controller node, install as many tools as you require
docker exec $JENKINS_CONTAINER_NAME bash -c "apt-get update -y -q && apt-get upgrade -y -q && apt-get install -y -q git"
- Jenkins 使用默认使用端口 8080 的 apache jetty,我们将主机的端口 50001 映射到容器的 8080。因此,输入http://host:50001应该会带您到 Jenkins web 仪表板。
- 第一次检查容器日志管理员密码并创建一个新的管理员用户。
第 2 步:设置 Jenkins 代理(从属)
我们现在可以设置我们的代理。由于我们的 Jenkins 控制器将使用 SSH 与代理通信,因此我们需要生成 SSH 密钥。在这种情况下,Jenkins 主节点将充当 SSH 客户端,代理将充当 SSH 服务器。所以,我们需要相应地设置它。
- 生成密钥
ssh-keygen -t rsa -f jenkins_agent_1
- Goto Jenkins Dashboard > Manage Jenkins > Manage Credentials > 添加“系统”范围的凭据以启用 SSH 到 Jenkins Agent
系统凭证与全局凭证
系统:仅在 Jenkins 服务器上可用(jenkins 作业不可见)
全局:可在任何地方访问,包括 jenkins 作业
使用适当的值填写表格。这是一个示例,
用户名:jenkins # 我们希望以“jenkins”用户身份通过 ssh 进入代理,默认情况下该用户已存在于我们将使用的 jenkins-agent 容器中
ID:凭证的唯一ID,可用于引用凭证
私钥:SSH私钥文件内容(例如:jenkins_agent_1)
第 3 步:启动 Jenkins 代理(从属)容器
我们可以使用官方的 jenkins-ssh-agent docker 容器。这是您可以使用的示例 docker-compose 文件。
version: '3.8'
services:
jenkins_agent:
image: jenkins/ssh-agent:jdk11
privileged: true
user: root
container_name: $CONTAINER_NAME
expose:
- 22
environment:
- JENKINS_AGENT_SSH_PUBKEY=$JENKINS_AGENT_SSH_PUBKEY
请注意,我们必须设置环境变量JENKINS_AGENT_SSH_PUBKEY
,在本例中我们从 bash 变量中进行设置。我们还需要在 Jenkins 代理中安装所需的工具。我们可以使用如下所示的简单 bash 脚本来实现所有这些,
#!/bin/bash
set -o nounset
JENKINS_CONTAINER_NAME=$1
JENKINS_AGENT_SSH_PUBKEY=$2
CONTAINER_NAME=$JENKINS_CONTAINER_NAME JENKINS_AGENT_SSH_PUBKEY=$JENKINS_AGENT_SSH_PUBKEY docker-compose -f docker-compose-agent.yaml up --build -d
sleep 10
# Here we have just installed tools that help us create a python virtual environment for our agent node, install as many tools as you require
docker exec $JENKINS_CONTAINER_NAME bash -c "apt-get update -y -q && apt-get upgrade -y -q && apt-get install -y -q git python3 python3-venv"
第 4 步:从 Jenkins 控制器配置代理
转到Jenkins 仪表板>管理 Jenkins >管理节点和云>新节点
使用适当的值填写表格。例如:,
*Name*: JenkinsAgent1
*NumberOfExecutors*: 1-2
*RemoteRootDirectory*: /home/jenkins/agent
*Labels*: linux, python # Space separated values, Can be useful to restrict jobs to run on a particular agent
*Usage*: Use this node as much as possible
*Launch Method*: Launch agents via SSH
*Host*: jenkins_agent # Agent's Hostname or IP to connect. (docker-compose service name if controller and agent is on the same machine)
*Credentials*: Select the Credential created in Step 2
*HostKeyVerificationStrategy*: Non verifying Verification Strategy
Launch Method > Advanced
*ConnectionTimeoutInSeconds*: 60
*MaximumNumberOfRetries*: 10
*SecondsToWaitBetweenRetries*: 15
第 5 步:创建作业并运行
现在,我们的代理应该被控制器发现,我们可以开始将我们的工作委托给代理。我们可以通过使用我们在创建代理时分配的标签来限制作业在特定代理上运行。