DEV Community

Cover image for AWS DevOps Pro Certification Blog Post Series: Code Deploy
Mark Sta Ana
Mark Sta Ana

Posted on • Originally published at booyaa.wtf on

AWS DevOps Pro Certification Blog Post Series: Code Deploy

Photo by Katherine McCormack on Unsplash

Caveat emptor

Using AWS costs money, some of these services may not be part of the AWS Free Tier. You can keep costs down by tearing down anything you've created whilst learning, but it's still possible to run up a hefty bill so pay attention to the instances you setup!

I'm very lucky to be able to use my employer's AWS account. You should ask your place of work if a similar arrangement can be made as part of your study.

Velocius quam asparagi conquantur

The format of the blog posts is liable to change as I try refine my mental model of each domain, so be sure to revisit the blog posts on a regular basis.

What?

Code Build is a managed deployment service.

Why?

Deployment services are often part of the build farm so would also contribute to infrastructure expenditure.

When?

SDLC automation: CodeCommit -> CodeBuild -> [CodeDeploy]

How?

This is based around the tutorial for deploying to an EC2 Auto Scaling Group. The key difference is that I've condensed it to only include instructions for using on Linux/MacOS based machine using the Amazon v2 AMI.

Creating the CodeDeploy service role and EC2 IAM Instance Profile

This is part of the Getting Started guide.

N.B. We've opted to use the managed policy for deploying to EC2/On-Premises compute platform.

Copy the JSON object below and paste into a new file called CodeDeployDemo-Trust.json

{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
              "Service": [
                  "codedeploy.amazonaws.com"
              ]
          },
          "Action": "sts:AssumeRole"
      }
  ]
}
Enter fullscreen mode Exit fullscreen mode
aws iam create-role \
  --role-name CodeDeployServiceRole \
  --assume-role-policy-document file://CodeDeployDemo-Trust.json

aws iam attach-role-policy \
  --role-name CodeDeployServiceRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole
Enter fullscreen mode Exit fullscreen mode

Copy the JSON object below and paste into a file called CodeDeployDemo-EC2-Trust.json

{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
              "Service": "ec2.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
      }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Copy the JSON object below and paste into a file called CodeDeployDemo-EC2-Permissions.json

{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Action": [
              "s3:Get*",
              "s3:List*"
          ],
          "Effect": "Allow",
          "Resource": "*"
      }
  ]
}
Enter fullscreen mode Exit fullscreen mode
aws iam create-role \
  --role-name CodeDeployDemo-EC2-Instance-Profile \
  --assume-role-policy-document file://CodeDeployDemo-EC2-Trust.json

aws iam put-role-policy \
  --role-name CodeDeployDemo-EC2-Instance-Profile 
  --policy-name CodeDeployDemo-EC2-Permissions 
  --policy-document file://CodeDeployDemo-EC2-Permissions.json

aws iam create-instance-profile \
  --instance-profile-name CodeDeployDemo-EC2-Instance-Profile

aws iam add-role-to-instance-profile \
  --instance-profile-name CodeDeployDemo-EC2-Instance-Profile \
  --role-name CodeDeployDemo-EC2-Instance-Profile
Enter fullscreen mode Exit fullscreen mode

Create the autoscaling group (ASG)

Copy the script below and paste into a new file called instance-setup.sh. This will install the CodeDeploy agent will work deploy the application to the instances associated by the Deployment Group (via the ASG).

#!/bin/bash
yum -y update
yum install -y ruby
cd /home/ec2-user
curl -O https://aws-codedeploy-????.s3.amazonaws.com/latest/install
chmod +x ./install
./install auto
Enter fullscreen mode Exit fullscreen mode

Edit ???? to reflect your region i.e. if your region is Paris then the value would be eu-west-3.

Environment variables we want to define are:

  • AMI_ID = The Amazon v2 AMI for your region
  • KEY_NAME = Your Key Pair for the region
  • AZ = The available zone(s) for our region

Here are a few aws cli commands to help you get the right values for your own environment.

# Find the Amazon v2 AMI for your region
aws ec2 describe-images --owners amazon \
  --filters \
    'Name=name,Values=amzn2-ami-hvm-2.0.????????-x86_64-gp2' \
    'Name=state,Values=available' \
  --output json | \
jq -r '.Images | sort_by(.CreationDate) | last(.[]).ImageId')


# List key pairs in your region
aws ec2 describe-key-pairs | jq -r '.KeyPairs[].KeyName | sort_by(.CreationDate)'

# List availability zones for your region
aws ec2 describe-availability-zones
Enter fullscreen mode Exit fullscreen mode

Create the launch configuration:

AMI_ID=__FILL_ME_IN__
KEY_NAME=__FILE_ME_IN__
aws autoscaling create-launch-configuration \
  --launch-configuration-name CodeDeployDemo-AS-Configuration \
  --image-id $AMI_ID \
  --key-name $KEY_NAME \
  --iam-instance-profile CodeDeployDemo-EC2-Instance-Profile \
  --instance-type t2.micro \
  --user-data file://instance-setup.sh
Enter fullscreen mode Exit fullscreen mode

Create the autoscaling group:

set AZ eu-west-3a
aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name CodeDeployDemo-AS-Group \
  --launch-configuration-name CodeDeployDemo-AS-Configuration \
  --min-size 1 \
  --max-size 1 \
  --desired-capacity 1 \
  --availability-zones $AZ
Enter fullscreen mode Exit fullscreen mode

Run the following command to check on the state of your ASG. Proceed to the next step when the status is "Healthy Inservice":

aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names CodeDeployDemo-AS-Group \
  --query "AutoScalingGroups[0].Instances[*].[HealthStatus, LifecycleState]" \
  --output text
Enter fullscreen mode Exit fullscreen mode

Deploy the Application to the ASG

Environment variables we want to define are:

  • SERVICE_ROLE_ARN = The Service Role we created at the beginning of the lab
  • REGION = Our region
SERVICE_ROLE_ARN=$(aws iam get-role --role-name CodeDeployServiceRole --query "Role.Arn" --output text)
REGION eu-west-3
BUCKET_NAME aws-codedeploy-$REGION

aws deploy create-application --application-name SimpleDemoApp

aws deploy create-deployment-group \
  --application-name SimpleDemoApp \
  --auto-scaling-groups CodeDeployDemo-AS-Group \
  --deployment-group-name SimpleDemoDG \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --service-role-arn $SERVICE_ROLE_ARN

DEPLOYMENT_ID=$(aws deploy create-deployment \
  --application-name SimpleDemoApp \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --deployment-group-name SimpleDemoDG \
  --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=samples/latest/SampleApp_Linux.zip | jq -r .deploymentId)
Enter fullscreen mode Exit fullscreen mode

AppSpec file

Let's download a copy of the sample application and examine the contents:

 curl -LO https://s3.$REGION.amazonaws.com/$BUCKET_NAME/samples/latest/SampleApp_Linux.zip
 unzip SampleApp_Linux.zip
 tree # to see the structure of the app
 cat appspec.yml
Enter fullscreen mode Exit fullscreen mode

The contents of the archive is:

  • the web page we'll be deploying via CodeDeploy
  • scripts to install/stop/start the web server
  • AppSpec file which is what the CodeDeploy agent will use to deploy the webpage to each instance in the deployment group.

Let's return back to our deployment.

Keep checking on the deployment until the following command outputs "Succeeded".

aws deploy get-deployment --deployment-id $DEPLOYMENT_ID \
  --query "deploymentInfo.status" \
  --output text
Enter fullscreen mode Exit fullscreen mode

Tip: If you get an access denied error at the Download stage, the EC2 IAM Instance Profile maybe configured incorrectly or the policy to allow access to S3 wasn't attached.

Let's verify that our deployment worked, by getting the public address of our instance.

INSTANCE_ID=$(aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names CodeDeployDemo-AS-Group \
  --query "AutoScalingGroups[0].Instances[*].InstanceId" --output text)

curl $(aws ec2 describe-instances \
  --instance-id $INSTANCE_ID \
  --query "Reservations[0].Instances[0].PublicDnsName" \
  --output text)
Enter fullscreen mode Exit fullscreen mode

Increase the number of instances in the ASG

We'll increase the instance count by ASG to bring it to a total of 2.

aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name CodeDeployDemo-AS-Group \
  --min-size 2 \
  --max-size 2 \
  --desired-capacity 2
Enter fullscreen mode Exit fullscreen mode

Keep checking on the instances in the ASG, only proceed to the next step when they are both "Healthy InService".

aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names CodeDeployDemo-AS-Group \
  --query "AutoScalingGroups[0].Instances[*].[HealthStatus, LifecycleState]" \
  --output text
Enter fullscreen mode Exit fullscreen mode

Now check the status of the deployment, it should be "Succeeded".

DEPLOYMENT_ID=$(aws deploy list-deployments \
  --application-name SimpleDemoApp \
  --deployment-group-name SimpleDemoDG \
  --query "deployments" | jq -r 'last(.[])')

aws deploy get-deployment \
  --deployment-id $DEPLOYMENT_ID \
  --query "deploymentInfo.[status, creator]" \
  --output text  
Enter fullscreen mode Exit fullscreen mode

Next, let's curl all the instances to verify we've got a working site.

INSTANCE_IDS=$(aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names CodeDeployDemo-AS-Group \
  --query "AutoScalingGroups[0].Instances[*].InstanceId" \
  --output text)

  --instance-ids $INSTANCE_IDS \
  --query "Reservations[0].Instances[0].PublicDnsName" \
  --output text

aws ec2 describe-instances \
  --instance-ids $INSTANCE_IDS \
  --query "Reservations[*].Instances[*].PublicDnsName" \
  | jq -r .[][] | xargs curl {}
Enter fullscreen mode Exit fullscreen mode

Clean up

aws autoscaling delete-auto-scaling-group \
  --auto-scaling-group-name CodeDeployDemo-AS-Group \
  --force-delete

aws autoscaling delete-auto-scaling-group \
  --auto-scaling-group-name CodeDeployDemo-AS-Group \
  --force-delete

aws deploy delete-application \
  --application-name SimpleDemoApp
Enter fullscreen mode Exit fullscreen mode

API and CLI features and verbs

Features

  • Deployments
    • Deployment groups
    • Application revisions
  • Applications
  • Deployment configurations
  • On-premise instances

Verbs (CRUD)

  • create
  • batch-get/get/list/describe
  • update/put
  • delete

Outliers

  • register-on-premises-instance
  • batch-get-on-premises-instances
  • add-tags-to-on-premises-instances
  • remove-tags-from-on-premises-instances
  • deregister-on-premises-instance
  • continue-deployment
  • stop-deployment
  • delete-git-hub-account-token
  • deregister
  • install
  • push
  • register
  • register-application-revision
  • put-lifecycle-event-hook-execution-status

To go to the next part of the series, click on the grey dot below which is next to the current marker (the black dot).

Top comments (1)

Collapse
 
andrewbrown profile image
Andrew Brown 🇨🇦

Debugging CodeDeploy

Most of the trouble with CodeDeploy is debugging scripts. So the best way to save lots of time is ssh into an EC2 Instance of your AMI you plan to use for CodeDeploy and assume root user:

sudo su

and then run all your commands you want in your scripts with absolute paths so for example

sudo su - ec2-user -c 'cd /home/ec2-user/app; /home/ec2-user/.rvm/gems/ruby-2.4.4/wrappers/bundle install --without development test`

Make Sure to rebake your AMI so ApplicationStop runs

When I was deploying I noticed my services were not stopping and staring correctly and after much frustration the issue was that in order for ApplicationStop to run there has to be a history of a previous deploy on the instance because for ApplicationStop it will reference the previous deploy.

However if you are using Blue/Green Deployment its always launching a new instance and the AMI I had did not have baked into it a previous deploy so it would never run ApplicationStop. So I rebaked my AMI so it had a reference to previous deploy.