How to Test and Debug AWS CodeDeploy Locally Before You Ship Your Code
AWS CodeDeploy is a powerful service for automating deployments to Amazon EC2, AWS Lambda, and on-premises servers. However, it can take some effort to get complex deployments up and running or to identify the error in your application when something goes wrong.
When I set up new deployments or debug existing ones, I like to test and debug locally for these reasons:
- To speed up the iteration process.
- To isolate potential issues.
- To validate code.
You can test application code packages on any machine that has the CodeDeploy agent installed before you deploy it through the service. Likewise, to debug locally, you just need to install the CodeDeploy agent on any machine, including your local server or EC2 instance.
In this blog post, I will walk you through the steps to validate and debug a sample application package using the codedeploy-local command. You can find the sample package in this GitHub repository.
Install the CodeDeploy agent on any supported instance type. For information, see Use the AWS CodeDeploy Agent to Validate a Deployment Package on a Local Machine in the AWS CodeDeploy User Guide.
Verify the CodeDeploy agent is installed and ready for local testing. By default, codedeploy-local is installed in the following locations:
On Amazon Linux, RHEL, or Ubuntu Server:
On Windows Server:
For simplicity, I am creating an alias for /opt/codedeploy-agent/bin/codedeploy-local as codedeploy-local so I can use the absolute path. This is optional.
alias codedeploy-local='sudo /opt/codedeploy-agent/bin/codedeploy-local'
When I execute the codedeploy-local command on the Linux terminal, I get the following response from the agent, which indicates that the agent is installed:
If you receive an error that the codedeploy-local command is not available or the package was not found, go back to the prerequisites and install the agent.
To test the sample application package using the codedeploy-local command, I have to make sure that the application package is available on the local machine. The sample package I am testing here is an Apache (httpd)-based application.
Use wget to download the package to the local machine.
Now that the sample package is available locally, I can either unzip the package or use the zip file for testing with the codedeploy-local command.
To test the zip file (archive) package (SampleApp_Linux.zip) with the codedeploy-local command, use the -l or –bundle-location option along with the -t or –type option as shown:
On Linux server:
codedeploy-local --bundle-location /home/ec2-user/CodeDeployPackage/SampleApp_Linux.zip -t zip --deployment-group my-deployment-group
On Windows server:
codedeploy-local --bundle-location C:/path/to/local/bundle.zip --type zip --deployment-group my-deployment-group
To unarchive the zip file, either change the directory (cd) to the top-level directory or provide the absolute path to the application package.
The package can be executed by providing the absolute path to the content as shown here:
codedeploy-local --bundle-location /path/to/local/bundle/directory
Or by changing the directory (cd) to the location of the unarchived package and executing the following command:
Executing the codedeploy-local command in the directory where the sample package is unzipped shows whether the deployment was successful or failed.
Here is a successful deployment execution and result:
Check the codedeploy-local logs and the deployment archive.
In the previous step, I was able to see that the local deployment was successful. The output included:
- The log location.
- The location where the deployment-archive was uploaded. It will be used as a staging directory for that deployment.
Because the –deployment-group, -g option was not provided, a local deployment group folder was created in the following location:
The following shows the listing of the files in the codedeploy-local deployment directory for a deployment:
In the directory path generated for each deployment, default-local-deployment-group is the name of the deployment group and d-H3OZK261S-local is the deployment ID.
The scripts.log shows the execution logs for the codedeploy-local command for a deployment group and deployment ID. Here is an example of a scripts.log that shows the execution of each lifecycle event defined in the appspec.yml:
There is another log file in this location that comes in handy when deploying the code on the local machine:
You can enable verbose logging in the codedeploy-agent configuration file by setting the parameter :verbose: to true.
By default, the location of the configuration file is:
Amazon Linux, RHEL, or Ubuntu Server instances
Other features for debugging issues locally with codedeploy-local
The codedeploy-local command has other features that you can use to debug and troubleshoot issues.
Override the lifecycle hooks mentioned in the appspec.yml file
You can use codedeploy-local to override the lifecycle hooks provided in the appspec.yml. In this example, only the ApplicationStop lifecycle hook defined in the appspec.yml file will be executed. All other hooks will be ignored.
codedeploy-local -e ApplicationStop
In the same way, you can override the order in which the CodeDeploy agent executes multiple lifecycle hooks. This feature can help you determine and change the sequence before the deployment is performed on the server. For information, see AppSpec ‘hooks’ Section in the AWS CodeDeploy User Guide.
For example, this command executes the BeforeInstall lifecycle hook first and then executes the ApplicationStop lifecycle hook.
Execute scripts specifically for codedeploy-local
If there are scripts that are used for local testing only and not required for the CodeDeploy deployment, then you can use the $DEPLOYMENT_GROUP_NAME variable, which has a value equal to LocalFleet.
Here are other environment variables and their values:
$APPLICATION_NAME: The location of the deployment package (for example, /home/ec2-user/CodeDeployPackage)
$DEPLOYMENT_ID: Unique per deployment (for example, d-LTVP5L6YY-local)
$DEPLOYMENT_GROUP_ID: The name of the deployment group. When the -g option is used for the command, this value will be passed. For example, in codedeploy-local -g testing, this value is testing. If this option is not set, the value of this environment variable is default-local-deployment-group
$LIFECYCLE_EVENT: The lifecycle hook that echoed this environment variable (for example, ApplicationStop)
Override the CodeDeploy agent configuration
You can override the CodeDeploy agent configuration and use your own configuration file from a custom location. This functionality makes it possible to test multiple configurations with the local deployments using the option -c, –agent-configuration-file while executing the codedeploy-local command. For the options to use, see AWS CodeDeploy Agent Configuration Reference in the AWS CodeDeploy User Guide.
By default, configuration files are stored in the following locations:
On Amazon Linux, RHEL, or Ubuntu Server:
On Windows Server:
Using custom configuration helps when verbose logging is required for package testing. You can do this just by using the -c or –agent-configuration-file option and without changing the default configuration file. Here is an example that shows the use of this option:
For example, on Amazon Linux, RHEL, or Ubuntu Server instances, when the config file is in /etc/codedeployagent.yml, the command is:
For example, on Windows Server instances, when the config file is in C:/ProgramData/conf.yml, the command is:
Point to an application package in an S3 bucket or GitHub repository
If the application package is stored in an S3 bucket or GitHub repository, codedeploy-local can be executed without downloading the file onto the local machine. You can do this using the -l, –bundle-location and -t, –type with the codedeploy-local command.
Here is an example for deploying a sample application package located in an S3 bucket:
Here is an example for deploying a sample application package from a public GitHub repository:
If you use GitHub, make sure that the application package with the appspec.yaml is in the root of the directory. If these contents are in a subfolder path, download the package to the local instance or server and then:
- Execute codedeploy-local from the directory where the file exists.
- Use the -t, –type option with the value of directory and -l, –bundle-location as the local path.
Troubleshooting common errors using codedeploy-local
The codedeploy-local command can be used to detect if the appspec.yml is in valid YAML format. If the format is invalid, you get the following error:
If there is an invalid lifecycle hook in the appspec.yml file, the deployment fails with this error:
The name of a lifecycle hook is case-sensitive. The following error is returned because the BeforeInstall lifecycle hook was entered as Beforeinstall:
ERROR: appspec.yml file contains unknown lifecycle events: ["Beforeinstall"]
If there is any error in the scripts provided for execution in any lifecycle hooks (for example, a problem in the BeforeInstall script), the execution logs show something like this:
For the preceding error, when you look at the logs in the deployment directory for the deployment group, you will see something like this:
This log snippet shows that the install_dependencies script had a package called httpd1 that is not available for installation.
If the appspec.yml is not found in the root of the application package, you will see an error like this:
The codedeploy-local command can be used to validate and debug an application package for deployments to Amazon EC2 instances or on-premises servers. With codedeploy-local, you can test and fix errors on a local machine during the code development phase. CodeDeploy local deployments also make it possible for you to change the order of the lifecycle hooks so you can restructure the appspec.yaml to add commands on the fly.
About the Author
|Kirankumar Chandrashekar has been a Cloud Support Engineer at AWS since 2015. He focuses on supporting customers in using DevOps technologies, specializes in CloudFormation and Elastic Container Service. Kirankumar is passionate about Infrastructure as Code and DevOps. He enjoys music, as well as cooking and traveling|