π Welcome to the Pipeline Party: Part 1
This is Part 1 of our series βAWS Code Pipelines in a Nutshellβ. We’re gonna get our hands dirty with a real sample app and connect the dots between:
- AWS CodeCommit (our Git repo)
- AWS CodeBuild (to build stuff)
- AWS CodeDeploy (to push the stuff)
- AWS CodePipeline (to glue it all together)
So buckle up, grab your keyboard, and letβs make some DevOps magic happen!
π§ Sample App: A Simple Express.js Web App
Hereβs a tiny Node.js + Express app weβll use:
π app.js
1
2
3
4
5
6
7
8
9
10
11
| const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.get("/", (req, res) => {
res.send("π Hello from AWS CodePipeline! π");
});
app.listen(port, () => {
console.log(`App running at http://localhost:${port}`);
});
|
π package.json
1
2
3
4
5
6
7
8
9
10
11
| {
"name": "codepipeline-demo",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
|
Push this to your Git repo (weβll use CodeCommit shortly).
π Step 1: AWS CodeCommit β Git in the Cloud
π οΈ Create a CodeCommit Repo
1
2
3
| aws codecommit create-repository \
--repository-name codepipeline-demo \
--repository-description "Demo app for AWS CodePipeline"
|
π Clone & Push the App
1
2
3
4
5
6
| git clone https://git-codecommit.<region>.amazonaws.com/v1/repos/codepipeline-demo
cd codepipeline-demo
# Copy your app.js and package.json into this folder
git add .
git commit -m "Initial commit"
git push
|
Boom! Your app is now in CodeCommit.
π§ Step 2: AWS CodeBuild β Build that Thing
π οΈ Create buildspec.yml
Add this to your root directory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
commands:
- echo "Installing dependencies..."
- npm install
build:
commands:
- echo "Build successful! π"
artifacts:
files:
- '**/*'
|
ποΈ Create a CodeBuild Project
1
2
3
4
5
6
| aws codebuild create-project \
--name codepipeline-demo-build \
--source type=CODECOMMIT,location=https://git-codecommit.<region>.amazonaws.com/v1/repos/codepipeline-demo \
--artifacts type=NO_ARTIFACTS \
--environment type=LINUX_CONTAINER,image=aws/codebuild/standard:7.0,computeType=BUILD_GENERAL1_SMALL \
--service-role arn:aws:iam::<account-id>:role/CodeBuildServiceRole
|
π¦ Step 3: AWS CodeDeploy β Shipping Time
Letβs deploy this thing to EC2.
ποΈ Create appspec.yml
Add this to your repo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app
hooks:
AfterInstall:
- location: scripts/install.sh
timeout: 180
runas: ec2-user
ApplicationStart:
- location: scripts/start.sh
timeout: 180
runas: ec2-user
|
π scripts/install.sh
1
2
3
| #!/bin/bash
cd /home/ec2-user/app
npm install
|
π scripts/start.sh
1
2
3
| #!/bin/bash
cd /home/ec2-user/app
nohup npm start > app.log 2>&1 &
|
Donβt forget to make these executable:
π― Create an Application and Deployment Group
1
2
3
4
5
6
7
8
9
| aws deploy create-application \
--application-name codepipeline-demo-app \
--compute-platform Server
aws deploy create-deployment-group \
--application-name codepipeline-demo-app \
--deployment-group-name codepipeline-demo-dg \
--ec2-tag-filters Key=Name,Value=CodeDeployDemo,Type=KEY_AND_VALUE \
--service-role-arn arn:aws:iam::<account-id>:role/CodeDeployServiceRole
|
Make sure your EC2 instance has:
- A tag
Name=CodeDeployDemo
- The CodeDeploy agent installed
𧬠Step 4: AWS CodePipeline β Glue It All Together
π§ͺ Create the Pipeline
1
| aws codepipeline create-pipeline --cli-input-json file://pipeline.json
|
π pipeline.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
| {
"pipeline": {
"name": "codepipeline-demo-pipeline",
"roleArn": "arn:aws:iam::<account-id>:role/CodePipelineServiceRole",
"artifactStore": {
"type": "S3",
"location": "my-codepipeline-bucket"
},
"stages": [
{
"name": "Source",
"actions": [
{
"name": "SourceAction",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "CodeCommit",
"version": "1"
},
"outputArtifacts": [
{
"name": "SourceOutput"
}
],
"configuration": {
"RepositoryName": "codepipeline-demo",
"BranchName": "main"
},
"runOrder": 1
}
]
},
{
"name": "Build",
"actions": [
{
"name": "BuildAction",
"actionTypeId": {
"category": "Build",
"owner": "AWS",
"provider": "CodeBuild",
"version": "1"
},
"inputArtifacts": [
{
"name": "SourceOutput"
}
],
"outputArtifacts": [
{
"name": "BuildOutput"
}
],
"configuration": {
"ProjectName": "codepipeline-demo-build"
},
"runOrder": 1
}
]
},
{
"name": "Deploy",
"actions": [
{
"name": "DeployAction",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CodeDeploy",
"version": "1"
},
"inputArtifacts": [
{
"name": "BuildOutput"
}
],
"configuration": {
"ApplicationName": "codepipeline-demo-app",
"DeploymentGroupName": "codepipeline-demo-dg"
},
"runOrder": 1
}
]
}
],
"version": 1
}
}
|
π Final Result
You now have:
- A live Node.js app
- Versioned in AWS CodeCommit
- Built using AWS CodeBuild
- Deployed to EC2 with AWS CodeDeploy
- All automated via AWS CodePipeline
Go Beyond the Basics
So you’ve got a simple pipeline up and running. Nice!
But now you’re asking the big questions:
- How do I know if something goes wrong?
- Whatβs this blue/green deployment everyone keeps talking about?
- Can I do custom stuff during builds and deploys?
You’re in luckβthis part covers exactly that!
Monitoring CodePipeline: Keeping an Eye on the Beast
π View Pipeline Execution Status
You can check your pipelineβs state via the AWS Console or CLI:
1
2
3
| aws codepipeline get-pipeline-execution \
--pipeline-name codepipeline-demo-pipeline \
--pipeline-execution-id <execution-id>
|
Or list all executions:
1
2
| aws codepipeline list-pipeline-executions \
--pipeline-name codepipeline-demo-pipeline
|
Youβll get:
- Start time
- Status (
Succeeded
, Failed
, InProgress
) - Trigger info
π§― Hook into CloudWatch Alarms
When a stage fails, AWS will automatically emit CloudWatch Events. Hook them up to SNS for email/text alerts or Lambda functions for automatic rollback.
1
2
| aws sns create-topic --name codepipeline-alerts
aws sns subscribe --topic-arn <topic-arn> --protocol email --notification-endpoint your@email.com
|
Then add that SNS topic to a CloudWatch rule for CodePipeline Pipeline Execution State Change
.
π Blue/Green Deployment with CodeDeploy
Letβs level up our deploys with zero-downtime deployments.
π§ What is Blue/Green?
- Blue: your currently running version
- Green: the new version you’re deploying
CodeDeploy will:
- Launch new instances or use a separate ASG
- Test the new version
- Shift traffic gradually or instantly
- Roll back if something breaks
π― Update Your Deployment Group
Switch the deployment type to BLUE_GREEN
:
1
2
3
4
| aws deploy update-deployment-group \
--application-name codepipeline-demo-app \
--current-deployment-group-name codepipeline-demo-dg \
--deployment-style deploymentType=BLUE_GREEN,deploymentOption=WITH_TRAFFIC_CONTROL
|
ποΈ Add Load Balancer Support
Make sure your EC2 instances are behind a Load Balancer (ALB preferred), then specify the target groups in your deployment group.
1
2
3
4
| aws deploy update-deployment-group \
--application-name codepipeline-demo-app \
--current-deployment-group-name codepipeline-demo-dg \
--blue-green-deployment-configuration file://bluegreen-config.json
|
bluegreen-config.json
1
2
3
4
5
6
7
8
9
10
11
12
13
| {
"deploymentReadyOption": {
"actionOnTimeout": "CONTINUE_DEPLOYMENT",
"waitTimeInMinutes": 1
},
"greenFleetProvisioningOption": {
"action": "COPY_AUTO_SCALING_GROUP"
},
"terminateBlueInstancesOnDeploymentSuccess": {
"action": "TERMINATE",
"terminationWaitTimeInMinutes": 5
}
}
|
π§ Advanced buildspec.yml
: Customize All the Things
Want to run unit tests? Create zip files? Deploy to S3? You got it.
π Sample buildspec.yml
with Tests & Artifact
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
commands:
- npm install
pre_build:
commands:
- echo "Running unit tests..."
- npm test || exit 1
build:
commands:
- echo "Zipping build output"
- zip -r app.zip .
artifacts:
files:
- app.zip
|
This will:
- Fail the build if tests fail
- Package the app into a zip file
- Upload it to CodePipeline as an artifact
π§ Custom Scripts in CodeDeploy
π More Hook Options in appspec.yml
Hereβs a full list of lifecycle hooks:
1
2
3
4
5
| hooks:
BeforeInstall:
AfterInstall:
ApplicationStart:
ValidateService:
|
You can also use scripts like:
1
2
| #!/bin/bash
pm2 restart app || node app.js &
|
Or run DB migrations, send Slack alerts, etc.
Time to go Serverless
You’ve built your pipeline.
Youβve got blue/green deployments like a boss.
Now itβs time to go serverless, link up with GitHub, and do multi-region deploysβthe fancy DevOps buffet! π±
β‘ Deploying Serverless Apps with CodePipeline + Lambda
Want to deploy Lambda functions without crying over zip files and cronjobs?
We got you.
π§Ύ Example: Serverless Node.js Lambda
index.js
1
2
3
4
5
6
| exports.handler = async (event) => {
return {
statusCode: 200,
body: "Hello from Lambda & CodePipeline! π",
};
};
|
template.yaml
(SAM Template)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloLambda:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs18.x
CodeUri: .
MemorySize: 128
Timeout: 5
Events:
HelloAPI:
Type: Api
Properties:
Path: /hello
Method: get
|
π Add buildspec.yml
for SAM Build & Deploy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
commands:
- npm install -g aws-sam-cli
build:
commands:
- sam build
post_build:
commands:
- sam package --s3-bucket my-sam-artifacts-bucket --output-template-file packaged.yaml
artifacts:
files:
- packaged.yaml
|
π₯ Setup Deploy Stage for Lambda
Change the deploy provider in your pipeline to CloudFormation
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"name": "DeployLambda",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CloudFormation",
"version": "1"
},
"configuration": {
"ActionMode": "CREATE_UPDATE",
"StackName": "lambda-pipeline-stack",
"TemplatePath": "BuildOutput::packaged.yaml",
"Capabilities": "CAPABILITY_IAM"
},
"inputArtifacts": [{ "name": "BuildOutput" }],
"runOrder": 1
}
|
π Using GitHub as a Source for CodePipeline
Tired of pushing to CodeCommit? Letβs hook up your GitHub repo.
π Step 1: Connect GitHub to AWS
Go to AWS Console > CodePipeline > Connections
Create a new GitHub Connection (via AWS CodeStar).
Itβll ask for GitHub OAuth permissions. Grant them.
ποΈ Step 2: Update Your Pipeline Source
Switch your source provider to GitHub:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| {
"name": "Source",
"actions": [
{
"name": "GitHubSource",
"actionTypeId": {
"category": "Source",
"owner": "ThirdParty",
"provider": "GitHub",
"version": "1"
},
"outputArtifacts": [{ "name": "SourceOutput" }],
"configuration": {
"Owner": "your-github-username",
"Repo": "your-repo-name",
"Branch": "main",
"OAuthToken": "****"
},
"runOrder": 1
}
]
}
|
π PSA: If using GitHub source in new regions, use CodeStar connections instead of OAuth tokensβit’s the cool kid now.
π Multi-Region Deployment Like a Global DevOps Jedi
Letβs say you want your app deployed in us-east-1
AND eu-west-1
.
Hereβs the plan:
- Build once
- Deploy the same artifact to both regions
π‘ Step 1: Create a Cross-Region S3 Bucket
You need an S3 artifact bucket in each region:
1
| aws s3 mb s3://my-codepipeline-artifacts-eu --region eu-west-1
|
π§ Step 2: Update Your Pipeline with Cross-Region Deploy
Add a second deploy stage like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| {
"name": "DeployToEU",
"actionTypeId": {
"category": "Deploy",
"owner": "AWS",
"provider": "CloudFormation",
"version": "1"
},
"region": "eu-west-1",
"configuration": {
"ActionMode": "CREATE_UPDATE",
"StackName": "my-stack-eu",
"TemplatePath": "BuildOutput::packaged.yaml",
"Capabilities": "CAPABILITY_IAM"
},
"inputArtifacts": [{ "name": "BuildOutput" }],
"runOrder": 1
}
|
Don’t forget to give CodePipeline permission to use cross-region S3 buckets and services.