Deployment
using Jenkins on AWS
Who am I?
Manuel Kanah
manuel@kanah.it
twitter: @testinaweb
Github: testinaweb
Deployment
A deployment process consists of several interrelated activities with possible transitions between them.
Common issues
- Testing
- Versioning/Tagging
- Consistency
Testing
Tests in general are the way to ensure the quality of the code.
Versioning
You want to versionate your software releases.
Every times you deploy your code you need to pin/tag the specific version of your code.
Consistency
Your code has to be delivered to all your machines
or nowhere.
AWS issues
If you want to deploy using Jenkins you must deal with:
AUTOSCALING
AWS solution
AWS CLI
The AWS Command Line Interface (CLI) is a unified tool to manage your AWS services. With just one tool to download and configure, you can control multiple AWS services from the command line and automate them through scripts.
Solution using Jenkins
Jenkins is able to do a lot of things.
It is very good on bases operation out of the box.
We need deep configuration, "Git plugin" and
a good shell script.
Jenkins needs
- EC2 needs role
- S3
- EC2
- ELB
- AUTOSCALING
- Configure the Security Group
- Jenkins needs the PublicKey.pem
- .ssh/config
- StrictHostKeyChecking no
- UserKnownHostsFile=/dev/null
- Git
- PHP (composer)
Deployment flow
- Blank the Jenkins' workspace
- Download repository
- Download dependencies
- Run tests
- Version the release (on S3)
- Stop autoscaling group
- Retrieve the details of all ec2 instances
- Iterate over each ec2 and deploy the release
- Start autoscaling group
Everything starts here
Handle the code
if [ "${NEW_RELEASE}" == 'true' -a ${ROLLBACK} -gt 0 ]
then
exit 1
fi;
if [ "${NEW_RELEASE}" == 'true' ]
then
# code preparation
if [ -d "${WORKSPACE}/vendor" ]
then
echo 'Removing vendor directory'
rm -rf ${WORKSPACE}/vendor
fi;
# tests and QA
if [ -f "${WORKSPACE}/composer.json" ]
then
echo 'composer.json is found'
echo 'Running composer install'
/usr/local/bin/composer install --no-dev --optimize-autoloader
fi;
find . -name ".git*" -exec rm -rf {} \;
local_tar_filename=${KEY_WORD}.${BUILD_NUMBER}.$(date +%Y%m%d).$(date +%H%M%S).tar.gz
s3_tar_filename=s3://${AWS_S3_BUCKET}/${KEY_WORD}/$(date +"%Y")/$(date +"%m")/${local_tar_filename}
tar -cvzf ${local_tar_filename} -C ${WORKSPACE} . --exclude ${local_tar_filename}
aws s3 --profile s3 cp ${local_tar_filename} ${s3_tar_filename}
fi;
if [ ${ROLLBACK} -gt 0 ]
then
tail_rollback=$((${ROLLBACK} + 1));
release="s3://${AWS_S3_BUCKET}/$(aws s3 --profile s3 ls s3://${AWS_S3_BUCKET}/${KEY_WORD}/ --recursive | awk '{print $4}' | \
sort -V | tail -n ${tail_rollback} | head -n 1)"
else
release="s3://${AWS_S3_BUCKET}/$(aws s3 --profile s3 ls s3://${AWS_S3_BUCKET}/${KEY_WORD}/ --recursive | awk '{print $4}' | sort -V | tail -n 1)"
fi;
if [ "${release}" == "s3://${AWS_S3_BUCKET}/" ]
then
exit 1
fi;
Handle Autoscaling
# stop autoscaling
scaling_group=$(aws autoscaling describe-auto-scaling-groups --profile ${profile} | grep -i -P "(?=.*?${AUTOSCALING_GROUP_NAME})" | \
grep -i -v qa | head -n 1 | awk '{print $2}' | cut -d '"' -f 2)
aws autoscaling suspend-processes --profile ${profile} --auto-scaling-group-name ${scaling_group}
elb_name=$(aws elb describe-load-balancers --profile ${profile} | grep LoadBalancerName | grep -i ${LOAD_BALANCER_NAME} | \
grep -i -v qa | sed -E 's/(.*):{1}\ "(.*)",/\2/')
ec2_instances=$(aws ec2 describe-instances --profile ${profile} \
--filters "Name=tag:Name,Values=${EC2_TAG_KEY_NAME}" "Name=instance-state-code,Values=16" \
--query "Reservations[*].Instances[*].[PrivateIpAddress,InstanceId]" \
--output text)
printf "%s\n" "${ec2_instances}" | while IFS=$'\t' read -r ip_address instance
do
if [ "${SET_OF_EC2_INSTANCES}" != '' ]
then
skip_instance="true"
while read -r instance_id
do
if [ "${instance_id}" == "${instance}" ]
then
skip_instance="false"
break
fi;
done < <(printf "%s\n" "${SET_OF_EC2_INSTANCES}")
if [ "${skip_instance}" == "true" ]
then
continue
fi;
fi;
aws elb deregister-instances-from-load-balancer --profile ${profile} --load-balancer-name ${elb_name} --instances ${instance}
rsync -avL -e "ssh -i ${JENKINS_HOME}/PublicKey.pem -o 'StrictHostKeyChecking no'" "$(basename ${release})" ec2-user@${ip_address}:
ssh -i ${JENKINS_HOME}/PublicKey.pem -o "StrictHostKeyChecking no" -tt ec2-user@${ip_address} << 'EOF'
# deploy the code inside the EC2 instance
exit
EOF
aws elb register-instances-with-load-balancer --profile ${profile} --load-balancer-name ${elb_name} --instances ${instance}
done;
# start autoscaling
aws autoscaling resume-processes --profile ${profile} --auto-scaling-group-name ${scaling_group}
New EC2
At the end of the provisioning of the EC2 instances, deploy the code with an API call:
# Push of the application
curl -X POST http://jenkins.domain.internal:8080/job/project-deployment/build \
--data-urlencode \
json="{\"parameter\": [{\"name\":\"BRANCH\", \"value\":\"origin/master\"}," \
"{\"name\":\"SET_OF_EC2_INSTANCES\", \"value\":\"${INSTANCE_ID}\"}]}"
Other Solutions
- Code deploy
- Code Pipeline
- Cloud Formation
- Elastic Beanstalk
Manuel Kanah
http://goo.gl/QpdOLg
Deployment using Jenkins on AWS
By Manuel Kanah
Deployment using Jenkins on AWS
- 644