Ansible and Jenkins – Configuration and Orchestration
A common approach to create an automated pipeline is to combine 2 platforms to orchestrate and then deploy, in an Agile-continuous manner, applications and infrastructure. Ansible is used to provision environments, infrastructure configurations and app deployment within a CI/CD process using a Jenkins Pipeline.
Ansible is a powerful agent-less platform used in IT automation and within the CI/CD process to target an environment onto which the application is deployed. It avoids shell scripting which are difficult to build and maintain. Jenkins on the other hand, is a well-known tool for implementing CI/CD. Ansible’s has reusable roles and ‘playbooks’ (tasks, configuration details), automated discovery to provision, create or tear down environments. Jenkins in this use case, would be a process orchestrator, not a shell script executor.
Common tools used to build out a CI/CD process pipeline using Ansible and Jenkins could include:
An infrastructure architecture
ALM (Application Lifecycle Management) scenarios can be emulated within a POC environment to prove the CI/CD concept.
A CI/CD POC with the aforementioned components contains the following architectural elements:
Github - where the project is hosted and where Jenkins will poll for changes to start the pipeline flow.
SonarSource - source code analysis server. If anything goes wrong during the analysis (e.g. not enough unit tests), the flow is interrupted. This step is important to guarantee the source code quality index.
Nexus or JFrog - is the artifact repository. After a successful compilation, unit tests and quality analyses, the binaries are uploaded into it. Later those binaries will be downloaded by Ansible during the application deployment.
Ansible Playbook - which is a YAML file integrated in the application source code, deploys the Spring Boot App on to a CentOS machine.
Jenkins - is our CI/CD process orchestrator. It is responsible to put all the pieces together, resulting in the application successfully deployed in the target machine.
An Ansible Playbook can be used to provision the infrastructure, using roles from the Ansible Galaxy community. Ansible Galaxy a primary source to learn Ansible. Often the environment for VMs is managed by Vagrant with libvirt. Details can be accessed in the project Vagrant ALM at Github.
Example Pipeline Flow
Demo environments should do the following pipeline, though real-world prod systems may have slightly more complicated flows depending on the use case, type of apps, existing legacy systems, etc.
Figure 1- The pipeline flow (source: Redhat)
Pipeline Flow:
Ansible now enters the picture.
Infrastructure provisioning
Ansible roles that could automate the actors in a typical design as given in this example including a Sonar and Jenkins server could lead to the following Playbook:
---
- name: Deploy Jenkins CI
hosts: jenkins_server
remote_user: vagrant
become: yes
roles:
- geerlingguy.repo-epel
- geerlingguy.jenkins
- geerlingguy.git
- tecris.maven
- geerlingguy.ansible
- name: Deploy Nexus Server
hosts: nexus_server
remote_user: vagrant
become: yes
roles:
- geerlingguy.java
- savoirfairelinux.nexus3-oss
- name: Deploy Sonar Server
hosts: sonar_server
remote_user: vagrant
become: yes
roles:
- wtanaka.unzip
- zanini.sonar
- name: On Premises CentOS
hosts: app_server
remote_user: vagrant
become: yes
roles:
- jenkins-keys-config
Group roles together to provide a desired infrastructure, example Jenkins’ provision:
- name: Deploy Jenkins CI
hosts: jenkins_server
remote_user: vagrant
become: yes
roles:
- geerlingguy.repo-epel
- geerlingguy.jenkins
- geerlingguy.git
- tecris.maven
- geerlingguy.ansible
The above defines a host group where Jenkins is deployed and describes the role used to deploy the Jenkins server. For instance, we may need the EPEL repository for the CentOS to be enabled, the Jenkins installation itself and GIT, Maven and Ansible which are all required for the pipeline. With barely 11 lines of code, we have a Jenkins server up and running prepared to start our CI/CD process.
To get this infrastructure ready, we took the advantage of the integration between Vagrant and Ansible. With a simple “vagrant up” on the Vagrant file directory, we have the lab environment ready.
Application deployment
The pipeline has been designed to prepare the application binaries, now called “artifact”, and to upload them in Nexus. The artifact can be reached in Nexus by an URL usually called Artifact URL. Ansible is also part of the pipeline and will receive the Artifact URL as the input for deployment. Thus, Ansible will be responsible not only for the deployment but also for the machine provisioning.
Ansible Playbook deploys our application on the target host:
---
- name: Install Java
hosts: app_server
become: yes
become_user: root
roles:
- geerlingguy.java
- name: Download Application from Repo
hosts: app_server
tasks:
- get_url:
force: yes
url: "{{ lookup('env','ARTIFACT_URL') }}"
dest: "/tmp/{{ lookup('env','APP_NAME') }}.jar"
- stat:
path: "/tmp/{{ lookup('env','APP_NAME') }}.jar"
- name: Setup Spring Boot
hosts: app_server
become: yes
become_user: root
roles:
- { role: pellepelster.springboot-role,
spring_boot_file: "{{ lookup('env','APP_NAME') }}.jar",
spring_boot_file_source: "/tmp/{{ lookup('env','APP_NAME') }}.jar",
spring_boot_application_id: "{{ lookup('env','APP_NAME') }}"
}
This Playbook will only perform three tasks:
The Playbook is in the application repository, which means that the application knows how to deploy itself. By using the Ansible Jenkins plugin, it is possible to call a Playbook from the pipeline by setting the variables required to execute it.
def artifactUrl = "http://${NEXUS_URL}/repository/ansible-meetup/${repoPath}/${version}/${pom.artifactId}-${version}.jar"
withEnv(["ARTIFACT_URL=${artifactUrl}", "APP_NAME=${pom.artifactId}"]) {
echo "The URL is ${env.ARTIFACT_URL} and the app name is ${env.APP_NAME}"
// install galaxy roles
sh "ansible-galaxy install -vvv -r provision/requirements.yml -p provision/roles/"
ansiblePlaybook colorized: true,
credentialsId: 'ssh-jenkins',
limit: "${HOST_PROVISION}",
installation: 'ansible',
inventory: 'provision/inventory.ini',
playbook: 'provision/playbook.yml',
sudo: true,
sudoUser: 'jenkins'
}
To make this happen, the target host must have the Jenkins user and its keys properly configured. This was done while provisioning the lab. All target hosts are CentOS machines with the Jenkins user and its key already set up. There’s a Ansible role to configure the user and its keys. This has been done since it is required in order to have Ansible access the host.
Productivity Enhancers
Ansible with Jenkins:
Sometimes we need to deploy the application only on a specific host (for a blue-green deployment, for example). We can do this by using the “-limit” parameter on the Ansible Playbook execution.
==END