官术网_书友最值得收藏!

Use case scenario 1 – a large number of jobs

A single Jenkins instance can contain many jobs. The practical limit varies widely and depends on multiple factors, such as the following:

  • Hardware resources such as RAM, CPU, disk, and network performance
  • Slave nodes—how many there are, how they are set up, and their performance
  • How well the jobs are distributed across the Master and Slave nodes
  • Settings of individual jobs; their size, function, history, and retention

It's not unusual for a Jenkins instance to have over 1,000 jobs, or around 100 Slave nodes attached to a Master node.

Managing the performance load that this causes is a big task in itself, and Jenkins also needs to manage the presentation and housekeeping of these jobs—your users will not want to look through more than 1,000 jobs just to search for the one they need, and we also need to make sure that old jobs are cleaned up or archived and that new ones can be created both easily and accurately.

If you can reduce the number of jobs you require, then administration and resource overheads will be reduced as a result, and performance, usability, and reliability will also be increased, and the user experience will be improved.

Some planning and a little automation of the UI can often help us achieve this—let's take a look at a few scenarios and the possible solutions.

If the most pressing issue or bottleneck is that there are too many jobs, it would be helpful to first understand where the need for all these jobs originates, and then see what we can do about alleviating that.

Frequently, development teams work in Sprints and/or Releases. This usually means having a mainline development stream and one or more branch streams. Often this convention will be followed in Jenkins as well—we may want to set up Jenkins jobs to build and then deploy Sprint 3 or Release 49 code to integration environments, while deploying our mainline changes to CI and development environments. At the same time, there may be a logical or business requirement to support a production version of everything, just in case something goes wrong.

This could mean setting up jobs that are named accordingly, such as Sprint 3, and having this value hardcoded in the configuration with a pseudocode, something along the lines of fetch the Sprint 3 war file and deploy it to the Sprint 3 server….

These jobs will have a finite (and probably pretty short) life and will then need cleaning up or updating with new values for the next Sprint or Release. This type of regular and manual maintenance becomes a headache very quickly, which further increases the possibility of human error leading to the wrong thing being deployed to the wrong place.

One simple solution for this common scenario is to make use of Jenkins Environment Variables. If you navigate to Manage Jenkins | Configure System | Global Properties, you can create and define your own key-value pairs, which are immediately available to every job on any node:

The preceding screenshot shows a few simplistic examples of the kinds of key-value pairs that you may want to set up—they can be whatever you like or need though.

Using this approach means that, rather than creating rafts of new jobs per Release or Sprint and catering to multiple concurrent Releases that will become obsolete shortly, you could just define two or three permanent sets of jobs that will pick up the key-value pairs from the location and use these to drive what they do—our job configuration pseudocode then changes. Initially, this in the form of the following:

fetch the Sprint 3 war file and deploy it to the Sprint 3 server…

This changes to something more generic along the lines of this:

fetch the ${SPRINT} war file and deploy it to the ${SPRINT} server…

This simple alteration to the approach can, in some circumstances, allow you to greatly reduce the number of Jenkins jobs by simply (and centrally) updating these Environment Variables to the new properties at the required point of your development life cycle—for example, at the end of a Release, Sprint, or Iteration cycle.

This one central configuration change will immediately update all of the jobs so that they can use these new values, and this approach could be extended to include information about many other aspects of build, test, and deployment processes, the branch location to checkout and build from, or the environment or host that the built artifacts should be deployed to, and so on. The following screenshot shows the Console Output page where the change is reflected:

If you need to create new jobs per Iteration, there are also ways in which you can automate the UI to simplify this process—we can use Jenkins to manage Jenkins.

If you take a look at your Jenkins home directory on the filesystem (as defined by the JENKINS_HOME variable), you will see the structure used to store the settings for each Jenkins job: each job is represented by a folder bearing the name of the job it represents, with each folder containing an XML file called config.xml. Each config.xml file contains the settings and information for that job.

There are normally several other files and folders too, such as a file to track the number of the next build (nextBuildNumber) and folders that are used to track and store history and artifacts created by previous builds.

The bare bones of a Jenkins job are, at its most basic form, as simple as this:

  • A folder named after the job—for example, VeryBasicJob
  • Inside this folder, a file called config.xml
  • Inside this file, some XML along the lines of the following:
    <?xml version='1.0' encoding='UTF-8'?>
    <project>
      <actions/>
      <description>A bare-bones Jenkins job</description>
      <keepDependencies>false</keepDependencies>
      <properties/>
      <scm class="hudson.scm.NullSCM"/>
      <canRoam>true</canRoam>
      <disabled>false</disabled>
      <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
      <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
      <triggers/>
      <concurrentBuild>false</concurrentBuild>
      <builders>
        <hudson.tasks.Shell>
          <command>echo &quot;A very simple shell-based job&quot;</command>
        </hudson.tasks.Shell>
      </builders>
      <publishers/>
      <buildWrappers/>
    </project>

As you can see, this minimal job contains some very simple XML tags and data that detail the <description> and <command> tags, and various other settings used by Jenkins.

The Jenkins UI will interpret this folder and the XML file and display the Config page like this:

When the source configuration and the frontend UI are seen side-by-side just as you can see in the preceding screenshot, it becomes obvious that changing the XML file should change the job displayed by the UI and vice-versa.

So if we could automatically create these XML files and load them in to Jenkins somehow, we should then also be able to automate and do version control of all of our Jenkins jobs and allow end users to apply whatever customization they require at runtime, removing the need for manual intervention.

Fetching folder structures and XML files from Version Control, updating these XML files with user-selected values, and loading the resultant configuration into our Jenkins instance are just the sort of tasks for which Jenkins is the ideal tool—we can set up Jenkins to set up Jenkins!

In short, this process can be achieved by first templating your XML files—replace all references to the variable factors (such as references to Release, Sprint, Hostnames, and so on) with something easily identifiable. Then, create the Jenkins jobs that enable a user to specify what they would like to use in place of these placeholder values.

The next step is to perform some string replacement (using your preferred tool—Perl, Sed, Awk, and so on) to substitute the placeholder values with the user-selected ones, and then you just need to load the new configuration into Jenkins at runtime.

To demonstrate one possible approach to this, here is a basic functional shell script that does the job with comments explaining what's going on at each step. This uses the Jenkins-cli.jar file, which you can download and find out more about by going to your Jenkins instance and adding /cli to the URL, for example: http://myjenkins.instance:8080/cli.

Here you will also find detailed help and information on the many features and abilities that Jenkins offers.

# set up the variables required for this to work:
export JAVA="/usr/bin/java"
# Location & port of your Jenkins server
export HOST=http://myjenkinshost:8080

# location of the Jenkins CLI jar file
export CLI_JAR="/tool/ jenkins-cli.jar"

# a simple counter to track the number of jobs created
export COUNTER=0
# the location of the customized config.xml files to load
export WORKDIR="/home/jenkins_user/jobstoload"
# a simple for loop to iterate through each job:
for JobName in `ls $WORKDIR`
do echo "About to create job number ${COUNTER} with name ${JobName}"
${JAVA} -jar ${CLI_JAR} -s ${HOST} create-job ${JobName} < $WORKDIR/${JobName}/config.xml
  echo "${JobName} created."
  let COUNTER++
  echo " "
done

This simple example, when set up in a Jenkins job, could be adapted to allow your users to create (or clean up) new Jenkins jobs quickly, easily, and reliably by pulling templates from version control and allowing the user to select from a predefined and valid set of options.

主站蜘蛛池模板: 东城区| 广灵县| 阳原县| 朝阳区| 恩平市| 白山市| 成都市| 定陶县| 瑞丽市| 苍梧县| 庆阳市| 鹰潭市| 岗巴县| 鹿泉市| 科尔| 宜都市| 肇庆市| 崇义县| 鄄城县| 桦南县| 姚安县| 师宗县| 赤壁市| 安远县| 宁阳县| 垦利县| 曲周县| 邛崃市| 鄂托克前旗| 延庆县| 中方县| 木里| 罗田县| 正安县| 山东省| 襄汾县| 炉霍县| 龙江县| 同仁县| 宣威市| 宁武县|