Project Jenkins-Ansible
Deploy Nginx containers on Multiple Nodes using Ansible with Jenkins pipeline
Hey Learners! Welcome back. In the Day59 challenge, we deployed a static webpage on Nginx on managed nodes using Ansible. In this project, we'll deploy containers on which nginx is running with a static webpage using the Ansible playbook. To automate all these tasks like code, build, deploy we can use Jenkins pipeline and more. Let's start...
Overview
For this project, we store our code on GitHub, from this code we build Docker Image on the Jenkins Mater node itself and push the created images on DockerHub. If any changes are made in the code then we'll build our new Docker image with the new changes and push the same on DockerHub manually. This will be automated with Jenkins(CI) and then deploy the containers on multiple nodes managed by Ansible (CD).
Steps:-
Create Jenkinsfile
Create an Ansible playbook for creating a Docker Container with the latest image
Add Ansible as an Agent node on Jenkins
Deploy containers by executing the Ansible playbook using Jenkins
Prerequisite:-
Jenkins on both Machine
Ansible Master
Docker On both machines
Ansible Nodes(As required) with Docker installed
For this project, I have Two virtual machines created on My system for Ansible and Jenkins where Jenkins and Ansible are installed on respective machines.
1- Create Jenkinsfile
Log in to Jenkins using Web UI
Click New item, Enter the name for the project select Pipeline as a project style and click OK.
We can directly write pipeline
To create pipeline syntax click Pipeline Syntax and a new tab will open. Search for Git paste the GitHub repository URL and click Generate Pipeline Script.
Copy the script and paste the same in the steps of the SCM stage.
Create a new repository on DockerHub to store images created.
Add build stage for building Docker image. Use pipeline syntax to search for shell script and use the docker build -t <image-name: Tag>
command. Generate pipeline script and paste in the defined stage.
We will push the created image to DockerHub in the next stage. To push images to DockerHub we need DockerHub Credentials.
Go to Pipeline Syntax search for With credentials click on Add and select Secret text.
Select Secret text and provide the Password for DockerHub in Secret. Enter the name for ID. Click Add and click Generate Pipeline Script. Copy and Paste into the login stage.
Now it's time to push our built image on DockerHub. As we already tagged the image with our DockerHub account name we need to execute the docker push <ImageCreated:Tag>
command.
The CI part is done.
If we run or build this job for a second time it will not update the image on DockerHub whether you make changes in code as with the latest tag already image is there on a specified repository.
To overcome this we can tag our image with the latest CommitHash as follows.
Note if you mention the command docker build -t <image-name: latest>
directly it will replace the local image with the latest tag and not push the latest image as it is already present. To avoid this use the below steps
Create one function in Jenkinsfile as shown. To Create a script search for Shell Script and enter the shell command as
git rev-parse --short HEAD
and Select the Advanced option to click Return Standard Output.Note:- corrected command as
git rev-parse --short HEAD
fromdocker rev-parse --short HEAD
below screenshot shows older command.Generate script and paste in function in Jenkinsfile. Add the Environment block in Jenkinsfile and use the environmental variable specified in the Image tag section. Refer to the below screenshot. In the build stage make sure that you specify the tag for the image as the Environmental variable specified. Also, make changes in the push stage accordingly.
After a successful build check the Image tag on DockerHub and compare it with the Latest commitHash.
Errors faced:-
Jenkins doesn't have permission to Docker. Use
sudo usermod -aG docker jenkins
command and restart Docker or the system.Till have a Docker permission Issue use
sudo chmod 666 /var/run/docker.sock
command.Having an error with git rev-parse --short HEAD command
Make sure first to create only the first stage('SCM') and Build the project to avoid the above error or add a function in the pipeline after building the whole pipeline first.
2- Create an Ansible playbook for creating a Docker Container with the latest image
Now we can create a Playbook for deploying Docker containers on managed node(s).
Create dockerCont_create.yml as follows
---
- name: play to Deploy Docker container
hosts: localhost #YOU CAN ADD NODES AS PER YOUR REQUIREMENTS
remote_user: jenkins
gather_facts: false
become: true
tasks:
- name: deploy docker contaier
docker_container:
name: Ansible_Container
image: "amitvpawar/containeransible:{{Docker_TAG}}"
state: started
published_ports:
- "8787:80"
Add Ansible hosts as per your requirements.
3- Add Ansible as an Agent node on Jenkins
As we have to deploy Docker containers using Jenkins we have to add an Ansible node as a Jenkins Agent node. The Ansible node machine should be up and running.
See my blog on how to add Jenkins Agent node- https://avp23.hashnode.dev/day-28-90daysofdevops
I have already added the Ansible node as the Jenkins Agent node.
4- Deploy containers by executing the Ansible playbook using Jenkins
Now we have to deploy Docker containers with Jenkins.
Create one new deploy stage in the existing Jenkinsfile and run steps on the Ansible agent.
Note- we add the image-tag variable in the YAML file as Docker_TAG and while executing ansible-playbook we provide an extra environmental variable as Docker_TAG with the value getting from the Environmental variable specified in Jenkinsfile as TAG which is equal to the latest commit hash with -e
option.
Therefore Now Docker_TAG is assigned with value as CommitHash.
ansible-playbook -e Docker_TAG=${TAG} <playbook.yml> defines image with latest commitHash.
Make sure the Ansible file is present on both Jenkins and Ansible nodes. Passwordless authentication should be done on the Jenkins to Ansible server.
We can put this file in the GitHub repository so we will have this file on the Jenkins node. Add one copy stage for coping YAML file in the Ansible node at the directory where you mention the path while adding the Ansible node as the Agent node for Jenkins.
First, create a pipeline script using the scp
command. scp <file/to/copy> <Server's-username/serverIP:/path-specified/project-name/file-name>
The above error gives a path where to copy the YAML file i.e. /home/ansible/jenkins/workspace/ContainersAnsible2/
Note if don't want to enter Server IP as 192.168.XXX.XXX then makes host entry into the /etc/hosts.
Before building a job with these changes make sure the Jenkins user's (Jenkins Master) Key verification is done successfully. Use scp
command by switching to jenkins user and by typing yes to add the key permanently or use the ssh-copy-id command to add the key.
To overcome the above error use sudo usermod -aG docker Jenkins
and sudo chmod 666 /var/run/docker.sock
on Ansible Master node.
If you have the same error use the ansible_become_password
variable and provide the root password as the value by adding with credentials secret text.
Now add the ansible-playbook command with the ansible_become_password variable.
ansible-playbook -e "ansible_become_password=$variableforsecrettext" <play-name.yml>
Build the project and access your application deployed.
Note:- I am running this Playbook directly on the Ansible node as don't have any resources to create more nodes on my machine. But you can try adding multiple nodes with a prerequisite.
The playbook will execute on the managed hosts as well.
Finally, after so many failures we accomplished our project.
Try making any changes in code and build this job manually and confirm the changes reflected on managed nodes(Ansible Node in this case) by accessing the nginx webpage.
If you want to manage the newly created nodes you can directly use the below YAML file instead of the YAML file created in Task 2. Three requirements are added as tasks.
---
- name: play to Deploy Docker container
hosts: localhost #YOU CCAN ADD NODES AS PER YOUR REQUIREMENTS
remote_user: jenkins
gather_facts: false
become: true
tasks:
- name: Installing Python pip
apt:
name: python-pip
state: present
- name: Installimg Docker
apt:
name: docker.io
state: present
enabled: yes
- name: Installing Docker-py
apt:
name: docker-py
state: present
- name: deploy docker contaier
docker_container:
name: Ansible_Container
image: "amitvpawar/containeransible:{{Docker_TAG}}"
state: started
published_ports:
- "8787:80"
Make sure you need to kill and remove the existing container.
docker kill <cont-name>
and docker rm <cont-name>
Now build a job. Successfully deployed new commit
Question:- How to make this pipeline fully automated means whenever changes are made in code it should trigger. What major changes do we have to make in the pipeline to run smoothly? Do comment.
Thank you so much for taking the time to read till the end! Hope you found this blog informative.
Feel free to explore more of my content, and don't hesitate to reach out if need any assistance from me or in case of you have any questions.
Find me on:- HashnodeLinkedInGithub
Happy Learning!