Building a CI/CD Integration with Amazon EC2, Jenkins and Docker
Tags: devops, aws, jenkins, docker
Published: 2023-12-12 02:26:39

## Introduction 
Continuous integration (CI) and continuous delivery (CD) are the development practices of committing code to a shared repository frequently and reliably, allowing for continuous software improvement. Each commit triggers automated workflows on a CI/CD server, which can alert the development team of a potential issue.

In this article, you'll learn how to use DevOps tools like Amazon EC2, Jenkins, and Docker to build a CI/CD integration of a simple React app.

## Prerequisites
The following is required to follow along smoothly with the tutorial:
- An [AWS account](https://aws.amazon.com)
- A simple React application you would like to deploy. Here is the [repository](https://github.com/Annysah/tasky) for the React app used in this tutorial.


## Tools
- [**Amazon EC2 (Elastic Compute Cloud)**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html) is a cloud computing service that allows for the deployment of virtual servers within an AWS environment.

- [**Jenkins**](https://www.jenkins.io/) is an open-source automation tool that allows developers to build, test and deploy software.

- **Docker** is an open-source software development platform that uses virtualization technology to ease the development and deployment of applications in containerized environments.

> You can learn more about [What Docker is and why it is important
](https://dev.to/annysah/what-is-docker-and-why-is-it-important-1one)

## Module 1: Create an EC2 Instance
**Step 1**: Go to the AWS management console and sign in with your AWS account credentials.

**Step 2**: Use the services search box in the console to look up "EC2". Click on it to navigate to the dashboard, and then select the "Launch instance" button.

**Step 3**: Launch an EC2 instance

- Give the instance any name e.g. tasky.
- Select the Ubuntu AMI and the t2.micro free tier instance type.
- Choose "Create new key pair," then pick either RSA or ED25519 as the key-pair type. Afterward, select the Private key file format as .pem and .ppk. Then, click the "Create key pair" button.

> In this tutorial, RSA and the .pem format were specifically chosen.

- Under the network settings, click edit and set the inbound security group rules as shown in the image below;

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m584vip0werro4f6u0wv.png)

> The custom TCP with port range 8080 is for the Jenkins that will be installed later in this tutorial.  

- Finally, launch the instance.

**Step 4**: Choose the instance you recently created from the instances table, click the connect button, and opt for the "EC2 Instance Connect" tab to enable SSH access to the CLI.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aktj8bimssdiuzpfmcs3.png)

## Module 2: Configure Jenkins on the EC2 Instance
Jenkins utilizes Java as its underlying framework, so be sure to install it on your EC2 virtual server instance by doing the following:

**Step 1**: Update its package source list to acquire the latest versions of packages from the repositories by running

```
sudo apt update
```

Then install Java and validate the installation.

```
sudo apt install openjdk-11-jre

java --version
```

**Step 2**: The next step is to install Jenkins by running the following command;

```
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
  https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key

echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

sudo apt-get update

sudo apt-get install jenkins
```
> You can always check for the updated Jenkins releases [here](https://www.jenkins.io/doc/book/installing/linux/#long-term-support-release).


**Step 3**: Start Jenkins by running the following commands;

```
sudo systemctl enable jenkins

sudo systemctl start jenkins

sudo systemctl status jenkins
```


**Step 4**: Open the Jenkins application in your web browser by entering the public IP address of your instance, followed by port 8080.
```
http://<your-ec2-ip>:8080
```
You should see this in your browser

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ql78qmsx7a6on5khiy0.png)

To access your administrator password, enter `sudo cat /var/lib/jenkins/secrets/initialAdminPassword` in your instance terminal.

Then, copy the password, return to your browser, paste it into the Administrator password field, and click "Continue."

Next, select "install suggested plugins." Create first admin user; upon clicking "Save and Continue," it leads you to the dashboard as shown below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hwp2tcvm5sac1rj0fgoo.png)
  

**Step 5**: Set up Jenkins; 
- On the left side of the dashboard, select "New Item" and type in any name.
- Choose "Freestyle Project" and click Ok.

- Add a short project description, select "Github Project," and paste the link to your project repo as shown below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57q6j639m17i52gf647m.png)

**Step 6**: Run the following in your EC2 instance terminal so you can access your instance RSA private and public keys

```
ssh-keygen

cd .ssh

ls
```
> The expected output will be `authorized_keys id_rsa id_rsa.pub`. To get the public key, run `sudo cat id_rsa.pub` while to get the private key, run `sudo cat id_rsa`.

**Step 7**: Go to your Github profile settings and look for "SSH and GPG keys" on the left pane. Click on "New SSH key."

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u4r1mypq8vb8blvf7u37.png)

Enter a name, type in your RSA public key, and click "Add SSH key."

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45dmhjoxnb5wx8a3regw.png)

**Step 8**: Copy your `.git` URL and paste it into the "Repository URL" within the "Source Management" section of your Jenkins dashboard.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/830i5p3luunj3wy5qid1.png)

Click on the "Add" button to add credentials and select "Jenkins Credentials Provider." In the dialog box, under kind, select "SSH with private key" and type an ID and description, and set the username to ubuntu

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jevmos7zkpu028eedwr.png)

In the private key, tick "Enter directly" and enter your private key, then click Add.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7mo0xzd0lx2feg054nn.png)

You can then select the created credential in the "Credentials" field.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8o78wv7wf8b43og49jih.png)

The next step is to click "Save" and "Build Now."
> You can check the "console output" for build details.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhnjlcx3ijqjl9fen4sc.png)

**Step 9**: In your instance terminal, run the command below to access your Jenkins workspace
```
cd /var/lib/jenkins/workspace/{your-project-name}
ls
```
Next, proceed to install the necessary dependencies listed below.

```
sudo apt install nodejs

sudo apt install npm

sudo npm install
```

**Step 10**: In your AWS console, navigate to Instance -> Security -> Security groups -> Edit inbound rules. Add the rule as illustrated below and save the changes to enable access to the app from your instance server later on.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j719gdipuup1qzk1zw4h.png)

## Module 3: Set up Docker

**Step 1**: Inside your Jenkins workspace on your instance terminal, install Docker by executing the following command:

```
sudo apt install docker.io
```
**Step 2**: Create a Dockerfile. This file will be used to build an image
```
vim Dockerfile
```

**Step 3**: Add the following content to the Dockerfile;

```
FROM node:12.2.0-alpine
WORKDIR app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["npm","start"]
```

> To exit vim, type `:wq`

**Step 4**: Then, build your image 

```
docker build . -t tasky
```

> To fix any permission issues, execute the commands below:
```
sudo usermod -a -G docker $USER

sudo reboot
```

Create a container from the image
```
docker run -d --name tasky -p 3000:3000 tasky
```
Verify if it is running 
```
docker ps
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ff8wbttb4fzbb8z1h7r.png)

> Access your application in the web browser by entering the public IP address of your instance, followed by port 3000
```
http://<your-ec2-ip>:3000
```

## Module 4: Automate Jenkins

**Step 1**: Now configure "Build Steps" in your Jenkins dashboard as shown below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7hsv2hhzjeri0rsz3hv.png)

Then, click Save -> Build Now

**Step 2**: Navigate to Manage Jenkins -> Plugins -> Available Plugins, search for the "GitHub Integration" plugins, and install.

**Step 3**: Go to your Github project repo settings and look for "Webhook" on the left pane. Add your Jenkins URL, appended with `/github-webhook/`, select the content type as "application/json," and click "Add webhook."

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m8p82kom3oj0d7ncqb33.png)

**Step 4**: Navigate to Jenkins configuration for your project and check the "GitHub hook trigger for GITScm polling."

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xa2kxy4v6q7801br0n7.png)

> You can test your modified Jenkins build job by making changes to any file in your project repository. After completing the update, Jenkins will automatically trigger a build job.

## Conclusion
In this article, you learned how to integrate a simple CI/CD practice into an application by utilizing DevOps tools such as AWS EC2, Jenkins, and Docker.

Integrating Jenkins with AWS EC2 and Docker creates an effective CI/CD pipeline that automates software delivery. This combination allows for easy scalability and repeatability in the development, testing, and deployment of applications. Adopting these technologies allows development teams to easily detect errors, accelerate release cycles, while maintaining high quality requirements.