This article will cover creating a basic Jenkins setup using freestyle and pipeline jobs. It includes a basic introduction to Docker and demonstrates how to simplify Jenkins management with existing Python tools and modules. You will learn how to create, delete, and reconfigure jobs, as well as explore other Jenkins-related features. Additionally, there will be a showcase of low-level Python features that are useful in various applications.
Please ensure Docker is installed and working before starting this demo. Moreover, this article assumes the reader has at least a basic understanding of Jenkins and Docker. Refer to this document to get more details on Docker installation: Docker installation .
Jenkins setup
Refer to the below documentation to get more details on Jenkins but this article should contain everything you need to run this demo by following the steps and by using resources mentioned in this article.
- Jenkins documentation
- Detailed information on Jenkins controllers and agents
- Jenkins installation instructions
This article will be based on Jenkins on Docker and it will slightly differ from the examples mentioned in the referred links. The main point is to show how to create a basic setup and manage it with Python.
Dockerfile and Docker Compose overview
Firstly, ensure Docker is installed and running (Docker installation ). This project was prepared on Ubuntu 22.04, with Docker running as a service.
Jenkins can be deployed in few different ways, as described in Jenkins setup section but here I am going to use Docker for the following reasons:
- Docker can make your life much easier if you have to create a system which depends on few services,
- it lets you keep your host system clean and run other services in isolation,
- the Docker approach will help you to maintain and manage the system you are going to deploy,
- and it is fairly easy to revert changes or perform a step back to modify anything “from the past”.
This example also shows how easily you can communicate with a system where there is a controller and agent(s), something similar to a server-client relationship. Docker Compose encapsulates this system in a single network. Modules like Docker Swarm can monitor your setup and keep it alive but Docker Swarm is not part of this presentation.
Agent Docker image
A Jenkins controller requires workers (agents) to run the jobs. Fortunately, there is a ready-to-use Jenkins agent image: jenkins/inbound-agent which can be used to connect it to our controller. By opening the link, you can get more details on the steps required to run the agent. To view the image layers, select an architecture first, like linux/amd64. We will return to this later when we try to connect the agent to the Jenkins controller. For now, I recommend reviewing the IMAGE LAYERS steps and corresponding commands:
The above layers view shows the steps required to build the image and it also provides the size of each layer. That allows us to calculate the total size of the image.
For this tutorial, the most important step will be the last:
32 ENTRYPOINT \["/usr/local/bin/jenkins-agent"] 0 B
In a Dockerfile, the ENTRYPOINT instruction is used to define the command that will run as the container’s main process (PID 1). This command is executed when the container starts. Unlike the CMD instruction, which can be overridden by command-line arguments provided at container runtime, the ENTRYPOINT instruction is designed to be the fixed executable of the container. If the ENTRYPOINT application requires arguments, those arguments must be provided from the Docker run command or must be defined in the Docker Compose file as a command entry just like in Docker Compose .
Let’s assume our Jenkins agent will run a bunch of tests on a Python project and for that we need:
- python3,
- pytest,
- mypy,
- pylint.
We will extend the jenkins/inbound-agent Dockerfile so it fulfills our requirements .
FROM jenkins/inbound-agent:latest
USER root
# Install Python 3 and required tools
RUN apt-get update
RUN apt-get install -y python3.11 python3.11-dev python3.11-distutils python3-pip
RUN apt install -y python3-pytest python3-flake8 pylint python3-mypy
CMD /usr/bin/bash
USER jenkins
All the RUN instructions can be combined with &&, but in this case, I prefer to keep each RUN instruction separate. This approach makes it easier to remove a step or add something new, resulting in faster builds of the image. However, this also causes the image to grow in size, as each RUN instruction in a Dockerfile adds a new layer to the image. Docker images are composed of multiple layers, each representing a set of changes from the previous layer. More layers can lead to a larger overall image size, as each RUN command adds some overhead.
In this case, I prioritized build speed and time. Note that this only matters if you are building the image for the first time or modifying it. Additionally, all instructions in our Dockerfile extend the IMAGE LAYERS shown in the picture of the jenkins/inbound-agent above. It is possible to see all layers and layer sizes with:
$ docker history -H <image-name>
To build a custom Dockerfile you can run the below command:
$ docker build -t python-agent -f ./dockerfile-python-agent .
This logic will be later integrated in a Docker Compose file which should simplify maintenance of the system.
Jenkins controller Docker Compose
If there is more than one Docker container to be deployed and all containers somehow depend on each other it is worth configuring all details in a single Docker Compose file. This is an example of how to deploy a single container which runs a Jenkins controller:
version: '3.8'
services:
jenkins-master:
image: jenkins/jenkins:lts
container_name: jenkins-master
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
environment:
- JENKINS_OPTS=--prefix=/jenkins
restart: unless-stopped
volumes:
jenkins_home:
Here is short explanation of the above Docker Compose file:
-
Jenkins master: the service is defined under the name jenkins-master.
-
Image: the container uses the jenkins/jenkins:lts image, which is the long-term support version of Jenkins, ensuring stability and security.
-
Container name: the container is named jenkins-master.
-
Ports: two ports are exposed:
- Port 8080 is mapped from the host to the container, which is the standard port for accessing the Jenkins web interface,
- Port 50000 is also exposed, which is used for Jenkins agents to connect to the master.
-
Volumes: a volume named jenkins_home is mounted to /var/jenkins_home inside the container. This volume stores Jenkins data, including configuration, jobs, and plugin information, allowing data persistence across container restarts.
-
Environment variables: an environment variable JENKINS_OPTS is set with the value –prefix=/jenkins, which configures Jenkins to be served under a URL prefix (i.e., http://:8080/jenkins).
-
Restart policy: the service is configured to restart automatically unless it is explicitly stopped. This ensures that the Jenkins server remains running unless manually intervened.
-
Volumes configuration:
- jenkins_home: this named volume is declared at the top level, which Docker Compose uses to manage Docker volumes independently of the life cycle of containers, enhancing data durability and ease of data backup and migration.
Initial configuration of Jenkins controller
To run Docker Compose navigate to a location where Docker Compose file is and execute:
$ cd docker
$ docker compose up -d
This runs the Jenkins controller container and you should be able to see such output with a password required to unlock Jenkins:
$ docker logs jenkins-controller
2024-04-10 20:33:01.117+0000 [id=80] INFO jenkins.install.SetupWizard#init:
*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX <--- PASSWORD
This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
*************************************************************
*************************************************************
2024-04-10 20:33:23.841+0000 [id=80] INFO jenkins.InitReactorRunner$1#onAttained: Completed initialization
According to Docker Compose configuration, the web UI interface should be accessible under http://localhost:8080/jenkins:
-
Provide the password displayed after running the docker compose up command.
-
There will be a few more forms to fill in:
- to create an admin account,
- dialog to install additional plugins, you can select Install suggested plugins but in most cases it fails to install all plugins. For this demo, the Pipeline plugin must be installed but can be installed later.
-
After completing all the above steps there should be an initial Jenkins dashboard:
Adding Jenkins agent
Jenkins agents are responsible for executing build jobs and offloading work from the controller. You can define multiple agents that can perform different tasks, but for this example, one will be sufficient. The requirements and configuration of the Jenkins agent Dockerfile for this project were described above: agent configuration .
Adding an agent can be done with an API or the Python-Jenkins module, but it makes more sense to do it with the web UI as we need to obtain the so-called secret for the agent.
- To add a new agent (node) go to http://localhost:8080/jenkins/manage/computer/ or from the dashboard select Manage Jenkins and then Nodes
- Click ”+ New Node”
- Provide a name for the new node
- Fill in all necessary data; the minimum number of executors for this project should be two.
- The new agent should be present on the Jenkins dashboard. Now we need to get the secret token, if you provided the node name as python-agent1, open http://localhost:8080/jenkins/computer/python-agent1/ or open your new agent from the dashboard view. Copy the secret key visible:
- The final step is to update the docker-compose.yaml file, replace XXXXX… with the secret token
version: '3.8'
services:
jenkins-master:
# repo digest sha256:1fd79ceb68ce883fb86db70bdbf7f9eaa8b25e580aafe7a240235240396e3916 corresponds to tag 'lts' on 13.04.2024
image: jenkins/jenkins@sha256:1fd79ceb68ce883fb86db70bdbf7f9eaa8b25e580aafe7a240235240396e3916
container_name: jenkins-master
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
environment:
- JENKINS_OPTS=--prefix=/jenkins
restart: unless-stopped
jenkins-agent:
container_name: jenkins-agent1
depends_on:
- jenkins-master
# build docker image from prepared dockerfile-python-agent
build:
context: .
dockerfile: dockerfile-python-agent
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- jenkins_agent_home:/home/jenkins
command: -url http://jenkins-master:8080/jenkins <jenkins agent secret> python-agent1
tty: true
stdin_open: true
restart: unless-stopped
volumes:
jenkins_home:
jenkins_agent_home:
-
From the Docker directory run Docker Compose again
- Stop the previous container if this is still running with the docker compose down command
$ docker compose up
There will be prints labeled with the container names defined in the compose file: jenkins-master and jenkins-agent. If everything was configured correctly, you should see agent1 is connected:
jenkins-master | 2024-04-13 13:43:55.641+0000 [id=133] INFO h.TcpSlaveAgentListener$ConnectionHandler#run: Accepted JNLP4-connect connection #2 from /172.27.0.3:43808
jenkins-agent1 | Apr 13, 2024 1:43:55 PM hudson.remoting.Launcher$CuiListener status
jenkins-agent1 | INFO: Remote identity confirmed: 3e:b3:b5:bc:81:75:7e:97:a7:3b:9c:67:d9:91:27:47
jenkins-agent1 | Apr 13, 2024 1:43:55 PM hudson.remoting.Launcher$CuiListener status
jenkins-agent1 | INFO: Connected
Adding a job
To have a better understanding of how Jenkins runs jobs, let’s add a dummy job. Later we will create a bunch of Python tools to automate such tasks.
- Select "+ New Item" from the Jenkins dashboard or open http://localhost:8080/jenkins/view/all/newJob,
- provide a name like “Dummy Job” and choose Freestyle project and click OK,
- on the next form you will find plenty of fields to fill in but at the moment the most important is the option Restrict where this project can be run.
Note that you can either provide a full node (agent) name or an agent label. If there will be different types of jobs which require different environments it is a good idea to use labels for that. I would suggest setting a Python label for python-agent1 and use that label here as we want to run Python tasks.
-
let’s choose Add build step -> Execute shell as this is deployed on Linux
- we can add something like:
whoami
python3 -V
- the job result should reflect the Python version defined in the agent’s Dockerfile and the user should be Jenkins,
- save the settings and run Build Now,
- there should appear a new build in Build History in the Dummy Job view http://localhost:8080/jenkins/job/Dummy%20Job/,
- The console output should look like (http://localhost:8080/jenkins/job/Dummy%20Job/2/console ):
Started by user admin
Running as SYSTEM
Building remotely on python-agent1 (python) in workspace /home/jenkins/workspace/Dummy Job
[Dummy Job] $ /bin/sh -xe /tmp/jenkins10178526424899115512.sh
+ whoami
jenkins
+ python3 -V
Python 3.11.2
Finished: SUCCESS
That was the first part which explains how to configure a very basic setup of Jenkins with a single worker. The next section demonstrates how Python can be used to configure and manage the Jenkins setup.
Jenkins API
Jenkins offers a rich set of functionalities via its REST API, allowing you to automate and interact with Jenkins programmatically. Here’s a list of common actions you can perform via the Jenkins API:
-
Build management:
- trigger a build for a specific job,
- retrieve the status and details of a build,
- cancel or stop a running build.
-
Job management:
- list all jobs in Jenkins,
- create a new job,
- delete an existing job,
- update job configuration,
- retrieve job configuration,
- enable/disable a job.
-
Build queue management:
- list all items in the build queue,
- cancel or remove items from the build queue.
-
Node management:
- list all nodes (Jenkins agents),
- get details about a specific node,
- enable/disable a node,
- delete a node.
-
User and role management:
- list all users,
- get user details,
- create/update/delete user roles and permissions.
-
Plugin management:
- list all installed plugins,
- install new plugins,
- uninstall plugins,
- get plugin details.
-
Credential management:
- list all stored credentials,
- add/update/delete credentials.
-
Build artifact management:
- download build artifacts,
- list build artifacts.
-
System information:
- get Jenkins system information,
- retrieve Jenkins version and system health status,
and more.
In order to use the API we will need get the Jenkins API token but first we need to create an API user.
In order to create a user, follow the below steps:
- Install Role-based Authorization Strategy plugin http://localhost:8080/jenkins/manage/pluginManager/available,
- in Manage Jenkins -> Security fill in the fields according to the picture below:
- create a new user: http://localhost:8080/jenkins/manage/securityRealm/addUser,
- Log out from the Administrator account and log in as the newly created user, in my case, the new user is tester,
- Create an API token for tester: http://localhost:8080/jenkins/me/configure,
- store this token, the best option is to use .netrc file
- just add such a line in your $HOME.netrc on your machine (replace XXX with the generated token):
echo "machine localhost:8080 login tester password XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" >> ~/.netrc
- for convenience, use the netrc Python module to get the tester credentials.
Python support for Jenkins
In this tutorial, we will focus on job creation, configuration, and reconfiguration. This can be very helpful if there is a large number of jobs to maintain. Very often, jobs share some common logic or depend on each other. Maintaining these dependencies is much easier if we apply a programmatic approach, such as using Python. I am going to use the Python Jenkins module for this. It offers most of the features listed above.
pip install python-jenkins
Having everything set up let’s create the first job using Python and the Jenkins API :
import netrc
import jenkins
def get_jenkins_server(jenkins_base_address: str = 'localhost:8080',
username: str = None,
api_token: str = None) -> jenkins.Jenkins:
"""
https://python-jenkins.readthedocs.io/en/latest/examples.html
"""
if not (username and api_token):
# Loading credentials from the .netrc file.
netrc_credentials = netrc.netrc()
jenkins_username, _, api_token = netrc_credentials.authenticators(jenkins_base_address)
jenkins_full_address = f'{jenkins_base_address}/jenkins'
jenkins_project_url = f'http://{jenkins_full_address}'
jenkins_server = jenkins.Jenkins(jenkins_project_url, jenkins_username, api_token)
return jenkins_server
main script
import jenkins
from pprint import pprint
import lib.jenkins_api as jenkins_api
server = jenkins_api.get_jenkins_server()
server.create_job('dummy job', jenkins.EMPTY_CONFIG_XML)
pprint(server.get_all_jobs())
this should print:
[{'_class': 'hudson.model.FreeStyleProject',
'color': 'notbuilt',
'fullname': 'dummy job',
'name': 'dummy job',
'url': 'http://localhost:8080/jenkins/job/dummy%20job/'}]
This job should also be visible on our Jenkins dashboard.
As we can see, the Python Jenkins module uses the XML file jenkins.EMPTY_CONFIG_XML as a basis to configure projects. Any project created on Jenkins has its own config.xml file. Once we create a dummy job, we can check the content of the XML at the URL:
http://localhost:8080/jenkins/job/dummy%20job/config.xml
To discover what kind of XML pieces we need to build a job according to our requirements, we can create a draft job using the web UI interface and check the generated config.xml to see what is needed.
XML parsing/unparsing with the xmltodict module
As shown earlier, we’re using XML to manage our Jenkins setup. XML (Extensible Markup Language) is a standard markup language for encoding documents in a structured, human-readable, and machine-readable way. It was designed to store and transport data, providing a flexible framework for representing data structures. While Python has an XML module for parsing XML files, a more user-friendly alternative xmltodict lets you treat XML as a dictionary, simplifying data handling:
pip install xmltodict
With this module, we can make Jenkins integration more Pythonic. I will demonstrate how to navigate an XML structure as if you’re accessing object attributes, just like in most object-oriented programming languages.
In XMLHandler files are in two classes which allow that kind of mechanism:
- XML Handler to parse and unparse XML documents
class XmlHandler:
"""
Handle XML document like an OOP object
"""
def __init__(self, xml_data: str):
self._data = XmlElement(xmltodict.parse(xml_data))
@property
def data(self) -> XmlElement:
"""
@return: XmlElement of root
"""
return self._data
def unparse(self) -> str:
"""
@return: xml as string
"""
return xmltodict.unparse(self._data, pretty=True)
def __repr__(self):
return pformat(self._data)
- XMLElement to handle each XML element as an object’s attribute
class XmlElement(dict):
"""
Defines mechanism to access and modify dict keys as an object's attribute
"""
def __setattr__(self, key: str, value):
"""
Assign value to dict key by __setattr__
@param key: key
@param value: value
@return:
"""
if not key.startswith('_'):
self[key] = value
else:
super().__setattr__(key, value)
def __getattr__(self, item):
"""
Allow to get dict item with getattr method
@param item: dict key
@return: plain data or new XmlElement
"""
data = super().__getitem__(item)
if issubclass(type(data), dict):
xml_element = XmlElement(data)
super().__setitem__(item, xml_element) # reassign current element with new instance of XmlElement
return super().__getitem__(item)
def __getitem__(self, item):
"""
Override getitem so it will return new XmlElement if element is type of dict, reuse __getattr__ implementation
@param item:
@return:
"""
return self.__getattr__(item)
Let’s see how it works. Here a default jenkins.EMPTY_CONFIG_XML is used to create a dummy job; the same is visible under http://localhost:8080/jenkins/job/Dummy%20Job/config.xml
<?xml version="1.0" encoding="UTF-8"?><project>
<keepDependencies>false</keepDependencies>
<properties/>
<scm class="jenkins.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers class="vector"/>
<concurrentBuild>false</concurrentBuild>
<builders/>
<publishers/>
<buildWrappers/>
</project>
<?xml version="1.0" encoding="utf-8"?>
Now let’s make an XMLHandler object out of it:
import lib.jenkins_api as jenkins_api
import lib.xml_handler as xml_handler
server = jenkins_api.get_jenkins_server()
xml_data = server.get_job_config('dummy job')
xml_obj = xml_handler.XmlHandler(xml_data)
print(xml_obj)
As expected, we have the same structure in the form of a nice-looking dictionary:
{'project': {'blockBuildWhenUpstreamBuilding': 'false',
'buildWrappers': None,
'builders': None,
'canRoam': 'true',
'concurrentBuild': 'false',
'disabled': 'false',
'keepDependencies': 'false',
'properties': None,
'publishers': None,
'scm': {'@class': 'jenkins.scm.NullSCM'},
'triggers': {'@class': 'vector'}}}
To show how to update a Jenkins job with the demonstrated tools, I am going to add a description to the job:
import lib.jenkins_api as jenkins_api
import lib.xml_handler as xml_handler
server = jenkins_api.get_jenkins_server()
# Get dummy job config.xml
xml_data = server.get_job_config('dummy job')
xml_obj = xml_handler.XmlHandler(xml_data)
xml_data = xml_obj.data
# Adding description to job
xml_data.project.description = "This is dummy job for demonstration purposes"
# Adding new field 'properties' which stores ParametersDefinitionProperty
xml_data.project.properties = {
'hudson.model.ParametersDefinitionProperty':
{
'parameterDefinitions': {}
}
}
# Let's grab parameterDefinitions list context
parameter_definitions = xml_data.project.properties['hudson.model.ParametersDefinitionProperty'].parameterDefinitions
parameter_definitions['hudson.model.StringParameterDefinition'] = [] # for many parameters of the same type use list
# Adding new Job parameters
parameters = [
dict(name='parameter1', description='this is dummy parameter 1', default_value='0'),
dict(name='parameter2', description='this is dummy parameter 2', default_value='1'),
]
for parameter_dict in parameters:
parameter_definitions['hudson.model.StringParameterDefinition'].append(
# Jenkins XML element names must be camel case
xml_handler.keys_to_camel_case(**parameter_dict)
)
print(xml_obj.unparse())
server.reconfig_job('dummy job', config_xml=xml_obj.unparse())
This is how the updated XML looks with the new description element and set of new string parameters
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions></actions>
<description>This is dummy job for demonstration purposes</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>parameter1</name>
<description>this is dummy parameter 1</description>
<defaultValue>0</defaultValue>
</hudson.model.StringParameterDefinition>
<hudson.model.StringParameterDefinition>
<name>parameter2</name>
<description>this is dummy parameter 2</description>
<defaultValue>1</defaultValue>
</hudson.model.StringParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class="hudson.scm.NullSCM"></scm>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers></triggers>
<concurrentBuild>false</concurrentBuild>
<builders></builders>
<publishers></publishers>
<buildWrappers></buildWrappers>
</project>
From the dummy job view we can see that the config was updated: http://localhost:8080/jenkins/job/Dummy%20Job/build
That was a showcase of how these tools can be used. Now let’s create something useful. We can create new classes in Python with many different templates for the different types of jobs we need:
- freestyle job,
- pipeline job,
- folder,
- etc.
We need to know what the XML structure looks like for each item we want to create on Jenkins. You can create example items on the Jenkins web UI and see how the config.xml looks. Basically, this is the simplest way to create a single job. The examples and tools shown here are more practical when you want to manage a larger number of jobs.
In job manager , files are defined as template classes to create a freestyle job, but of course this is just a single example of how you can use this approach to create and manage your own kinds of jobs.
Here is a short snippet which creates a fully operational job with few parameters. It also shows how can we simply render a shell script based on defined parameters:
import lib.jenkins_api as jenkins_api
import lib.job_manager as job_manager
server = jenkins_api.get_jenkins_server()
# Get 'dummy job' config.xml
xml_data = server.get_job_config('dummy job')
# Create freestyle job
freestyle_job = job_manager.FreestyleJob(description='new dummy job')
# Add string parameters to the job
parameter1 = 'Param1'
parameter2 = 'Param2'
freestyle_job.add_job_parameter(parameter1, description='first parameter', default_value='val1')
freestyle_job.add_job_parameter(parameter2, description='second parameter', default_value='val2')
# Add choices parameter
platform_parameter = 'platform'
freestyle_job.add_job_choices_parameter(platform_parameter, choices=['linux', 'windows'], description='choose platform')
# Add artifact archiver, collect log file produced by shell script below
freestyle_job.add_artifact_archiver('*.log')
# Define builder shell script
freestyle_job.add_builder_shell_script(
f'''
echo Selected platform: ${platform_parameter}
echo Executing job with parameters {parameter1}=${parameter1}, {parameter2}=${parameter2}
pip list | tee text.log
'''
)
server.reconfig_job('dummy job', config_xml=freestyle_job.unparse())
Now let’s see how it looks:
After building the job, here is how the console output looks:
Started by user unknown or anonymous
Running as SYSTEM
Building remotely on python-agent1 in workspace /home/jenkins/workspace/dummy job
[dummy job] $ /bin/sh -xe /tmp/jenkins6729820319219664139.sh
+ echo Selected platform: linux
Selected platform: linux
+ echo Executing job with parameters Param1=val1, Param2=val2
Executing job with parameters Param1=val1, Param2=val2
+ pip list
+ tee text.log
Package Version
------------------ ------------
astroid 2.14.2
attrs 22.2.0
...
As you can see all values were correctly placed in a shell script. There is also an artifact collected according to the freestyle_job.add_artifact_archiver('*.log') expression which contains the output of the pip list command
This is just a very basic showcase, but it gives an idea of how Python can be useful for managing any Jenkins project. This comes in handy if there are many different jobs or pipelines that are somehow related to each other. One job can run another job that requires specific parameters, or pipelines can be defined that run different jobs with specific configurations. If there are dependencies between jobs, all logic can be expressed in Python.
When the configuration requires an update or there is a need to add a new parameter, it is much easier to handle that in a Python script and simply run the update script to update every job. If the logic of a given Jenkins project is defined with a Python script, this can serve as a backup or a tool to deploy the same configuration on a different server without unnecessary manual work.
Summary
In this guide, we have covered the essential steps to set up and manage Jenkins using Docker. By leveraging Docker, we maintain a clean host system and benefit from isolated environments, making the deployment and management of Jenkins more efficient and reliable. We explored creating a Jenkins agent Docker image tailored for Python projects, setting up a Jenkins controller with Docker Compose, and connecting agents to the controller.
We also delved into managing Jenkins using Python, demonstrating how to automate and interact with Jenkins through its REST API and the Python-Jenkins module. This allows for efficient job creation, deletion, and reconfiguration, as well as other administrative tasks, highlighting the flexibility and power of combining Jenkins with Python.
This article provides a comprehensive approach to setting up a robust CI/CD pipeline, showcasing the integration of Docker and Python to simplify and enhance Jenkins management. By following these steps, you can establish a scalable and maintainable Jenkins environment, ensuring a smooth and efficient development workflow.
>> Moreover, here you can check how our DevOps services can help you evolve toward the modern and agile organization.