Walkthrough: Setting up build pipeline with Amazon EC2 Container Service and Jenkins
Amazon EC2 Container Service (Amazon ECS) is a highly scalable, fast, container management service that makes it easy to run, stop, and manage Docker containers on a cluster of Amazon EC2 instances.So how do we use ECS so as to have CICD, I dig out about this and foundvarious interesting things. From learnings I have created a CI pipeline with Jenkins and ECS.In this walkthrough, I’ll share the steps I performed to set up and configure a build pipeline using Jenkins and the
Amazon EC2 Container Service (ECS) .We’ll be using a sample Node.js application, available on GitHub *. The repository contains a simple Dockerfile that uses a node.js base image and runs our application:
This Dockerfile is used by the build pipeline to create a new Docker image upon pushing code to the repository. The built image will then be used to start a new service on an ECS cluster.
Setup the build environment
For our build environment we’ll launch an Amazon EC2 instance using the Amazon Linux AMI and install and configure the required packages. Make sure that the security group you select for your instance allows traffic on ports TCP/22 and TCP/80.
Install and configure Jenkins, Docker and Nginx
Connect to your instance using your private key and switch to the root user. First, let’s update the repositories and install Docker, Nginx and Git.
# yum update -y
# yum install -y docker nginx git
To install Jenkins on Amazon Linux, we need to add the Jenkins repository and install Jenkins from there.
# wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
# rpm –import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
# yum install Jenkins
As Jenkins typically uses port TCP/8080, we’ll configure Nginx as a proxy. Edit the Nginx config file (/etc/nginx/nginx.conf) and change the
server configuration to look like this:
We’ll be using Jenkins to build our Docker images, so we need to add the jenkins user to the docker group. A reboot may be required for the changes to take effect.
# usermod -a -G docker jenkins
Start the Docker, Jenkins and Nginx services and make sure they will be running after a reboot:
# service docker start
# service jenkins start
# service nginx start
# chkconfig docker on
# chkconfig jenkins on
# chkconfig nginx on
You can launch the Jenkins instance complete with all the required plugins with this CloudFormation template .
Point your browser to the public DNS name of your EC2 instance (e.g. ec2-54-111-222-233.compute-1.amazonaws.com) and you should be able to see the Jenkins home page:
The Jenkins installation is currently accessible through the Internet without any form of authentication. Before proceeding to the next step, let’s secure Jenkins. Select Manage Jenkins on the Jenkins home page, click Configure Global Security and then enable Jenkins security by selecting the Enable Security checkbox.
For the purpose of this walkthrough, select Jenkins’s Own User Database under Security realm and make sure to select the Allow users to sign up checkbox. Under Authorization, select Matrix-based security. Add a user (e.g. admin) and provide necessary privileges to this user.
After that’s complete, save your changes. Now you will be asked to provide a username and password for the user to login. Click on Create an account, provide your username- and fill in the user details. Now you will be able to log in securely to Jenkins.
Install and configure Jenkins plugins
The last step in setting up our build environment is to install and configure the Jenkins plugins required to build a Docker image and publish it to a Docker registry (DockerHub in our case). We’ll also need a plugin to interact with the code repository of our choice, GitHub in our case.
From the Jenkins dashboard select Manage Jenkins and click Manage Plugins. On the Available tab, search for and select the following plugins:
Then click the Install button. After the plugin installation is completed,
Configure Git :
Select Manage Jenkins and click Configure System, find GitHub Plugin Configuration and add GitHub Credentials.
Install and configure the Amazon ECS CLI
Now we are ready to setup and configure the ECS Command Line Interface (CLI).
Follow the Setting Up with Amazon ECS guide to get ready to use ECS. If you haven’t done so yet, make sure to start at least one container instance in your account and create the Amazon ECS service role in the AWS IAM console.
Make sure that Jenkins is able to use the ECS CLI. Switch to the jenkins user and configure the AWS CLI, providing your credentials:
# sudo -su jenkins
> aws configure
.
Login to Docker Hub
The Jenkins user needs to login to Docker Hub before doing the first build:
# docker login
.
Create a task definition template
Create a task definition template for our application (note, you will replace the image name with your own repository):
(You can find this task definition on GitHub )
Save your task definition template as task-definition.json. Since the image specified in the task definition template will be built in the Jenkins job, at this point we will create a dummy task definition. Substitute the %BUILD_NUMBER% parameter in your task definition template with a non-existent value (0) and register it with ECS:
{
Make note of the family value (demo-node-app), as it will be needed when configuring the Execute shell step in the Jenkins job.
Create the ECS IAM Role, an ELB and your service definition
Create a new IAM role (e.g. ecs-service-role), select the Amazon EC2 Container Service Role type and attach the AmazonEC2ContainerServiceRole policy. This will allows ECS to create and manage AWS resources, such as an ELB, on your behalf. Create an Amazon Elastic Load Balancing (ELB) load balancer to be used in your service definition and note the ELB name (e.g. demo-node-app-elb). Create the demo-node-app-service service, specifying the task definition (e.g. demo-node-app) and the ELB name (e.g. demo-node-app-elb):
#aws ecs create-service –cluster demo-cluster –service-name demo-node-app-service –task-definition demo-node-app –load-balancers loadBalancerName=demo-node-app-elb,containerName=demo-node-app,containerPort=3000 –role ecs-service-demo-role –desired-count 0
Since we have not yet build a Docker image for our task, make sure to set the –desired-count flag to 0.
Configure the Jenkins build
On the Jenkins dashboard, click on New Item, select the Freestyle project job, add a name for the job, and click OK. Configure the Jenkins job:
- Under GitHub Project, add the path of your GitHub repository – e.g. https://github.com/swapnildahiphale/demo-node-app.git. In addition to the application source code, the repository contains the Dockerfile used to build the image, as explained at the beginning of this walkthrough.
- Under Source Code Management provide the Repository URL for Git, e.g. https://github.com/swapnildahiphale/demo-node-app.git.
- In the Build Triggers section, select Build when a change is pushed to GitHub.
In the Build section, add a Docker build and publish step to the job and configure it to publish to your Docker registry repository (e.g. DockerHub) and add a tag to identify the image (e.g. v_$BUILD_NUMBER).
The Repository Name specifies the name of the Docker repository where the image will be published; this is composed of a user name (swapnildahiphale) and an image name (demo-node-app). In our case, the Dockerfile sits in the root path of our repository, so we won’t specify any path in the Directory Dockerfile is in field. Note, the repository name needs to be the same as what is used in the task definition template in task-definition.json.
Add a Execute Shell step and add the ECS CLI commands to start a new task on your ECS cluster.
The script for the Execute shell step will look like this:
#!/bin/bash
SERVICE_NAME=”demo-node-app-service”
IMAGE_VERSION=”v_”${BUILD_NUMBER}
TASK_FAMILY=”demo-node-app”
CLUSTER_NAME=”demo-cluster”
# Create a new task definition for this build
sed -e “s;%BUILD_NUMBER%;${BUILD_NUMBER};g” task-definition.json > task-definition:v_${BUILD_NUMBER}.json
aws ecs register-task-definition –cli-input-json file://task-definition:v_${BUILD_NUMBER}.json
# Update the service with the new task definition and desired count
NEW_TASK_REVISION=`aws ecs describe-task-definition –task-definition $TASK_FAMILY –region us-west-2 | egrep “revision” | tr “/” ” ” | awk ‘{print $2}’ | sed ‘s/”$//’`
DESIRED_COUNT=`aws ecs describe-services –cluster $CLUSTER_NAME –services $SERVICE_NAME –region us-west-2 | egrep -m 1 “desiredCount” | tr “/” ” ” | awk ‘{print $2}’ | sed ‘s/,$//’`
if [ ${DESIRED_COUNT} = “0” ]; then
DESIRED_COUNT=”1″
fi
aws ecs update-service –cluster ${CLUSTER_NAME} –service ${SERVICE_NAME} –task-definition ${TASK_FAMILY}:${NEW_TASK_REVISION} –desired-count ${DESIRED_COUNT} –region us-west-2
To trigger the build process on Jenkins upon pushing to the GitHub repository we need to configure a service hook on GitHub. Go to the GitHub repository settings page, select Webhooks and Services and add a service hook for Jenkins (GitHub plugin). Add the Jenkins hook url: http://<username>:<password>@<EC2-DNS-Name>/github-webhook/.
Now we have configured a Jenkins job in such a way that whenever a change is committed to GitHub repository it will trigger the build process on Jenkins.
Happy building
From your local repository, push the application code to GitHub:
# git add *
# git commit -m “First Jenkins build”
# git push origin master
This will trigger the Jenkins job. After the job is completed, point your browser to the public DNS name for your EC2 container instance and verify that the application is correctly running.
Conclusion
In this walkthrough we demonstrated how to use Jenkins to automate the deployment of an ECS service.
Written by Swapnil Dahiphale – WhiteHedge DevOps Team
“You can write to us at devops@whitehedge.in”