Building Production-Ready Release Pipelines in AWS: A Step-by-Step Guide
Building a robust, production-ready release pipeline in AWS requires careful planning, proper configuration, and adherence to best practices. This comprehensive guide will walk you through creating an enterprise-grade release pipeline using AWS native services, focusing on real-world production scenarios.
Architecture Overview
Our production pipeline will deploy a web application to EC2 instances behind an Application Load Balancer, implementing blue/green deployment strategies for zero-downtime releases. The pipeline will include multiple environments (development, staging, production) with appropriate gates and approvals.
GitHub → CodePipeline → CodeBuild (Build & Test) → CodeDeploy (Dev) → Manual Approval → CodeDeploy (Staging) → Automated Testing → Manual Approval → CodeDeploy (Production Blue/Green)
Prerequisites
Before we begin, ensure you have:
- AWS CLI configured with appropriate permissions
- A GitHub repository with your application code
- Basic understanding of AWS IAM, EC2, and Load Balancers
- A web application ready for deployment (we’ll use a Node.js example)
Step 1: Setting Up IAM Roles and Policies
CodePipeline Service Role
First, create an IAM role for CodePipeline with the necessary permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketVersioning",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"codebuild:BatchGetBuilds",
"codebuild:StartBuild"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetApplication",
"codedeploy:GetApplicationRevision",
"codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig",
"codedeploy:RegisterApplicationRevision"
],
"Resource": "*"
}
]
}
CodeBuild Service Role
Create a role for CodeBuild with permissions to access ECR, S3, and CloudWatch:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject"
],
"Resource": "*"
}
]
}
CodeDeploy Service Role
Create a service role for CodeDeploy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:*",
"ec2:*",
"elasticloadbalancing:*",
"tag:GetResources"
],
"Resource": "*"
}
]
}
Step 2: Infrastructure Setup
Create S3 Bucket for Artifacts
aws s3 mb s3://your-company-codepipeline-artifacts-bucket
aws s3api put-bucket-versioning \
--bucket your-company-codepipeline-artifacts-bucket \
--versioning-configuration Status=Enabled
Launch EC2 Instances
Create EC2 instances for each environment with the CodeDeploy agent installed:
# User data script for EC2 instances #!/bin/bash yum update -y yum install -y ruby wget # Install CodeDeploy agent cd /home/ec2-user wget https://aws-codedeploy-us-east-1.s3.us-east-1.amazonaws.com/latest/install chmod +x ./install ./install auto # Install Node.js (for our example application) curl -sL https://rpm.nodesource.com/setup_18.x | bash - yum install -y nodejs # Start CodeDeploy agent service codedeploy-agent start
Create Application Load Balancer
Set up an Application Load Balancer for blue/green deployments:
aws elbv2 create-load-balancer \
--name production-alb \
--subnets subnet-12345678 subnet-87654321 \
--security-groups sg-12345678
aws elbv2 create-target-group \
--name production-blue-tg \
--protocol HTTP \
--port 3000 \
--vpc-id vpc-12345678 \
--health-check-path /health
aws elbv2 create-target-group \
--name production-green-tg \
--protocol HTTP \
--port 3000 \
--vpc-id vpc-12345678 \
--health-check-path /health
Step 3: CodeBuild Configuration
Create a buildspec.yml file in your repository root:
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
pre_build:
commands:
- echo Logging in to Amazon ECR...
- echo Build started on `date`
- echo Installing dependencies...
- npm install
build:
commands:
- echo Build started on `date`
- echo Running tests...
- npm test
- echo Building the application...
- npm run build
post_build:
commands:
- echo Build completed on `date`
- echo Creating deployment package...
artifacts:
files:
- '**/*'
exclude:
- node_modules/**/*
- .git/**/*
- '*.md'
name: myapp-$(date +%Y-%m-%d)
Create CodeBuild Project
aws codebuild create-project \
--name "myapp-build" \
--source type=CODEPIPELINE \
--artifacts type=CODEPIPELINE \
--environment type=LINUX_CONTAINER,image=aws/codebuild/amazonlinux2-x86_64-standard:3.0,computeType=BUILD_GENERAL1_MEDIUM \
--service-role arn:aws:iam::123456789012:role/CodeBuildServiceRole
Step 4: CodeDeploy Applications and Deployment Groups
Create CodeDeploy Application
aws deploy create-application \
--application-name myapp \
--compute-platform Server
Create Deployment Groups
Development Environment:
aws deploy create-deployment-group \
--application-name myapp \
--deployment-group-name development \
--service-role-arn arn:aws:iam::123456789012:role/CodeDeployServiceRole \
--ec2-tag-filters Type=KEY_AND_VALUE,Key=Environment,Value=Development \
--deployment-config-name CodeDeployDefault.AllAtOne
Staging Environment:
aws deploy create-deployment-group \
--application-name myapp \
--deployment-group-name staging \
--service-role-arn arn:aws:iam::123456789012:role/CodeDeployServiceRole \
--ec2-tag-filters Type=KEY_AND_VALUE,Key=Environment,Value=Staging \
--deployment-config-name CodeDeployDefault.AllAtOne
Production Environment (Blue/Green):
aws deploy create-deployment-group \
--application-name myapp \
--deployment-group-name production \
--service-role-arn arn:aws:iam::123456789012:role/CodeDeployServiceRole \
--blue-green-deployment-configuration '{
"terminateBlueInstancesOnDeploymentSuccess": {
"action": "TERMINATE",
"terminationWaitTimeInMinutes": 5
},
"deploymentReadyOption": {
"actionOnTimeout": "CONTINUE_DEPLOYMENT"
},
"greenFleetProvisioningOption": {
"action": "COPY_AUTO_SCALING_GROUP"
}
}' \
--load-balancer-info targetGroupInfoList='[{
"name": "production-blue-tg"
}]' \
--deployment-config-name CodeDeployDefault.BlueGreenAllAtOnce
Step 5: Application Configuration Files
AppSpec File
Create an appspec.yml file for CodeDeploy:
version: 0.0
os: linux
files:
- source: /
destination: /var/www/myapp
overwrite: yes
permissions:
- object: /var/www/myapp
owner: ec2-user
group: ec2-user
mode: 755
hooks:
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 300
runas: root
ApplicationStart:
- location: scripts/start_server.sh
timeout: 300
runas: ec2-user
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 300
runas: ec2-user
ValidateService:
- location: scripts/validate_service.sh
timeout: 300
runas: ec2-user
Deployment Scripts
Create a scripts/ directory with the following files:
scripts/install_dependencies.sh:
#!/bin/bash cd /var/www/myapp npm install --production
scripts/start_server.sh:
#!/bin/bash cd /var/www/myapp pm2 stop all pm2 start ecosystem.config.js --env production
scripts/stop_server.sh:
#!/bin/bash pm2 stop all
scripts/validate_service.sh:
#!/bin/bash
# Wait for the application to start
sleep 30
# Check if the application is responding
curl -f http://localhost:3000/health
if [ $? -eq 0 ]; then
echo "Application is running successfully"
exit 0
else
echo "Application failed to start"
exit 1
fi
Step 6: Create the CodePipeline
Pipeline Configuration
{
"pipeline": {
"name": "myapp-production-pipeline",
"roleArn": "arn:aws:iam::123456789012:role/CodePipelineServiceRole",
"artifactStore": {
"type": "S3",
"location": "your-company-codepipeline-artifacts-bucket"
},
"stages": [
{
"name": "Source",
"actions": [
{
"name": "Source",
"actionTypeId": {
"category": "Source",
"owner": "ThirdParty",
"provider": "GitHub",
"version": "1"
},
"configuration": {
"Owner": "your-github-username",
"Repo": "your-repo-name",
"Branch": "main",
"OAuthToken": "{{resolve:secretsmanager:github-oauth-token}}"
},
"outputArtifacts": [
{
"name": "SourceOutput"
}
]
}
]
},
{
"name": "Build",
"actions": [
{
"name": "Build",
"actionTypeId": {
"category": "Build",
"owner": "AWS",
"provider": "CodeBuild",
"version": "1"
},
"configuration": {
"ProjectName": "myapp-build"
},
"inputArtifacts": [
{
"name": "SourceOutput"
}
],
"outputArtifacts": [
{
"name": "BuildOutput"
}
]
}
]
},
{
"name": "DeployToDev",
"actions": [
{
"name": "Deploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CodeDeploy",
"version": "1"
},
"configuration": {
"ApplicationName": "myapp",
"DeploymentGroupName": "development"
},
"inputArtifacts": [
{
"name": "BuildOutput"
}
]
}
]
},
{
"name": "ApprovalForStaging",
"actions": [
{
"name": "ManualApproval",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1"
},
"configuration": {
"CustomData": "Please review the development deployment and approve for staging"
}
}
]
},
{
"name": "DeployToStaging",
"actions": [
{
"name": "Deploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CodeDeploy",
"version": "1"
},
"configuration": {
"ApplicationName": "myapp",
"DeploymentGroupName": "staging"
},
"inputArtifacts": [
{
"name": "BuildOutput"
}
]
}
]
},
{
"name": "StagingTests",
"actions": [
{
"name": "IntegrationTests",
"actionTypeId": {
"category": "Build",
"owner": "AWS",
"provider": "CodeBuild",
"version": "1"
},
"configuration": {
"ProjectName": "myapp-integration-tests"
},
"inputArtifacts": [
{
"name": "SourceOutput"
}
]
}
]
},
{
"name": "ApprovalForProduction",
"actions": [
{
"name": "ManualApproval",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1"
},
"configuration": {
"CustomData": "Please review staging tests and approve for production deployment"
}
}
]
},
{
"name": "DeployToProduction",
"actions": [
{
"name": "Deploy",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CodeDeploy",
"version": "1"
},
"configuration": {
"ApplicationName": "myapp",
"DeploymentGroupName": "production"
},
"inputArtifacts": [
{
"name": "BuildOutput"
}
]
}
]
}
]
}
}
Create the Pipeline
aws codepipeline create-pipeline --cli-input-json file://pipeline-config.json
Step 7: Production Considerations
Monitoring and Alerting
Set up CloudWatch alarms for pipeline failures:
aws cloudwatch put-metric-alarm \
--alarm-name "CodePipeline-Failure" \
--alarm-description "Alert on pipeline failure" \
--metric-name PipelineExecutionFailure \
--namespace AWS/CodePipeline \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--dimensions Name=PipelineName,Value=myapp-production-pipeline \
--alarm-actions arn:aws:sns:us-east-1:123456789012:pipeline-alerts
Rollback Strategy
Implement automatic rollback capabilities:
# In buildspec.yml, add rollback script generation
post_build:
commands:
- echo "Generating rollback script..."
- |
cat > rollback.sh << 'EOF'
#!/bin/bash
aws deploy stop-deployment --deployment-id $1 --auto-rollback-enabled
EOF
- chmod +x rollback.sh
Security Best Practices
- Use AWS Secrets Manager for sensitive configuration:
aws secretsmanager create-secret \
--name myapp/production/database \
--description "Production database credentials" \
--secret-string '{"username":"admin","password":"securepassword"}'
- Implement least privilege IAM policies
- Enable AWS CloudTrail for audit logging
- Use VPC endpoints for secure communication between services
Performance Optimization
- Use CodeBuild cache to speed up builds:
# In buildspec.yml
cache:
paths:
- '/root/.npm/**/*'
- 'node_modules/**/*'
- Implement parallel deployments for multiple environments
- Use CodeDeploy deployment configurations for optimized rollout strategies
Disaster Recovery
- Cross-region artifact replication:
aws s3api put-bucket-replication \
--bucket your-company-codepipeline-artifacts-bucket \
--replication-configuration file://replication-config.json
- Automated backup of deployment configurations
- Multi-region deployment capabilities
Step 8: Testing the Pipeline
Initial Deployment
- Push code to your GitHub repository
- Monitor the pipeline execution in the AWS Console
- Verify each stage completes successfully
- Test the deployed application in each environment
Validate Blue/Green Deployment
- Make a code change and push to repository
- Approve the production deployment
- Verify traffic switches to green environment
- Confirm old blue instances are terminated
Troubleshooting Common Issues
CodeDeploy Agent Issues
# Check agent status sudo service codedeploy-agent status # View agent logs sudo tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log
Permission Issues
- Verify IAM roles have correct policies attached
- Check S3 bucket policies allow pipeline access
- Ensure EC2 instances have proper instance profiles
Deployment Failures
- Review CodeDeploy deployment logs in CloudWatch
- Check application logs on target instances
- Verify health check endpoints are responding
Conclusion
This production-ready AWS release pipeline provides a robust foundation for enterprise deployments. Key benefits include:
- Zero-downtime deployments through blue/green strategies
- Multiple environment promotion with manual approvals
- Comprehensive monitoring and alerting
- Automated rollback capabilities
- Security best practices implementation
Remember to regularly review and update your pipeline configuration, monitor performance metrics, and continuously improve your deployment processes based on team feedback and operational requirements.
The pipeline can be extended with additional features such as automated security scanning, performance testing, and integration with other AWS services as your requirements evolve.
