Based on the DOP-C02 exam guideReport an error
Lambda deployment has no instances. There's no concept of "spinning up a new server"; instead, there are versions of function code and Aliases that point to those versions. Blue/Green's "traffic shift" is implemented here as a change in Alias weighted routing.
This simplicity is its strength. Rollback is just reversing the Alias weight to the previous version — completed in seconds. There's no need to terminate new servers or reconfigure load balancers. But behind this simplicity lies a mathematical understanding of "exactly how much traffic goes to which version." That's the difference between Canary and Linear, and it's the most frequently confused topic on the exam.
Today we cover the complete Lambda deployment picture — Version/Alias relationships, Deployment Configuration math, Pre/Post Hook design, SAM integration, and Provisioned Concurrency cost models.
💡 Day 3's core framework: Lambda deployment strategy choice is a function of two variables — (1) how long it takes to detect failure, (2) the scope of impact if failure occurs. Fast detection → Canary; need gradual load observation → Linear. With this judgment framework, exam answers become clear.
[sam deploy / aws lambda update-function-code]
│
▼
[Publish new Lambda Version (publish-version)]
→ Immutable snapshot (code + config + env vars fixed)
│
▼
[Update Alias (update-alias)]
→ routing-config: Primary 90% + Secondary(new) 10%
│
▼
[CodeDeploy Deployment Group monitors]
→ Auto-adjust weights per Canary/Linear schedule
→ Execute Pre/Post Hook Lambda
→ Monitor CloudWatch Alarms
│
┌────┴────┐
│ │
Success Alarm triggered
│ │
100% shift Immediate rollback (Alias → old Version 100%)
🔍 Going deeper: Lambda Version is an immutable snapshot. After
publish-version, code, runtime, environment variables, memory, and timeout are all fixed. This immutability guarantees that "other changes don't pollute the deployment." Conversely,$LATESTis a mutable pointer always pointing to the latest state, so if someone changes code during deployment, deployment results vary. Canary deployment must only use Version, never$LATEST.
# 1. Publish new Version after code change
aws lambda publish-version \
--function-name MyFn \
--description "v2.1.0 - fixed checkout bug"
# → Version 6 created (immutable snapshot)
# 2. Check current Alias (currently Version 5: 100%)
aws lambda get-alias --function-name MyFn --name live
# 3. Start Canary (10% to Version 6)
aws lambda update-alias \
--function-name MyFn \
--name live \
--function-version 5 \
--routing-config AdditionalVersionWeights='{"6": 0.1}'
# Alias "live" → Version 5: 90%, Version 6: 10%
# 4. Full switch
aws lambda update-alias \
--function-name MyFn \
--name live \
--function-version 6
# No routing-config → Version 6: 100%Core constraints:
routing-config per Alias$LATEST cannot be used in routing-config (mutable pointer)Alias ARN structure:
arn:aws:lambda:ap-northeast-2:123456789:function:MyFn:live
^^^^
Alias name
Callers only need to know the Alias ARN. Version switching happens transparently.
💡 Related theory: Lambda Alias weighted routing is stochastic branching. Each invocation independently generates a random number and selects the Version based on weights. With 10% Canary, on average 10% goes to the new version, but exactly 100 out of 1000 invocations isn't guaranteed. It's statistically 10%. This is why the answer to "exactly 10% of users affected during Canary 10% period?" is "no, statistically 10%." Session stickiness is not built-in by default.
⚠️ Pitfall:
$LATESTcannot be used in routing-config on Alias.$LATESTis a mutable pointer always pointing to the latest code, different from Version which is an immutable snapshot. Canary deployment must create immutable Versions withpublish-versionfirst. If you see "$LATEST for Canary deployment" on the exam, it's always wrong.
Traffic distribution by time:
| Config | 0 min | 1 min | 2 min | 5 min | 10 min | 20 min | Full switch time |
|---|---|---|---|---|---|---|---|
LambdaAllAtOnce | 100% | — | — | — | — | — | 0 min |
LambdaCanary10Percent5Minutes | 10% | 10% | 10% | 100% | — | — | After 5 min |
LambdaCanary10Percent30Minutes | 10% | 10% | 10% | 10% | 10% | 10% | After 30 min |
LambdaLinear10PercentEvery1Minute | 10% | 20% | 30% | 50% | 100% | — | 10 min |
LambdaLinear10PercentEvery10Minutes | 10% | 10% | 10% | 10% | 20% | 30% | 100 min |
Canary logic: Only 2 phases. Observe a small percentage (10%) for specified time, if no issues, switch all remaining (90%) at once. Suitable for "fast go/no-go decision."
Linear logic: N phases. Increment evenly to reach 100%. Suitable for "observe gradual load increase, validate at each step."
Canary10Percent5Minutes: Linear10PercentEvery1Minute:
100% │ ████ 100% │ █
│ █ │ ██
10% │ ████████ 50% │ ███
│ │ ██████
└───────────────────── └────────────────────
0 5 min 0 5 min 10 min
[2 phases: observe then switch] [10 phases: even increment]
Custom Deployment Configuration:
aws deploy create-deployment-config \
--deployment-config-name MyCanary5Percent10Min \
--compute-platform Lambda \
--traffic-routing-config \
"type=TimeBasedCanary,timeBasedCanary={canaryPercentage=5,canaryInterval=10}"
# Linear Custom:
aws deploy create-deployment-config \
--deployment-config-name MyLinear20Percent5Min \
--compute-platform Lambda \
--traffic-routing-config \
"type=TimeBasedLinear,timeBasedLinear={linearPercentage=20,linearInterval=5}"🔍 Going deeper: The choice between Canary and Linear is a Risk Tolerance vs Validation Speed trade-off. For a financial transaction function, you want to validate sufficiently over Canary 10% 30 minutes — observe small traffic for long. For a static content transformation function, Linear 10%/1min to complete in 10 minutes is sufficient. Consider both "how long it takes to detect failure" and "scope of impact if failure occurs." For functions where failure appears immediately (synchronous API), Canary is more fitting; for functions where failure accumulates (batch processing), Linear is better.
📚 Case study: In 2021, Stripe Engineering adopted Canary10Percent30Minutes for payment processing Lambda. During 30 minutes observation, 10% of actual payment transactions get processed by the new version. CloudWatch monitors
PaymentErrorscustom metric during this period, auto-rollback if threshold exceeded. After launch, 4 auto-rollbacks occurred, all automatically recovered within 30 minutes. Given the high cost of payment system failures, the 30-minute observation window was justified.
Hook placement:
[Before traffic shift starts]
BeforeAllowTraffic (= PreTraffic Hook)
→ Smoke test directly on new Version
→ If failed, rollback without traffic shift
[Traffic shift]
[After traffic shift completes]
AfterAllowTraffic (= PostTraffic Hook)
→ Validate with real traffic
→ If failed, rollback to previous Version
Hook Lambda implementation (BeforeAllowTraffic):
import boto3, json, os
deploy = boto3.client('codedeploy')
fn_client = boto3.client('lambda')
def handler(event, context):
"""BeforeAllowTraffic Hook: smoke test new version before traffic shift"""
deployment_id = event['DeploymentId']
hook_id = event['LifecycleEventHookExecutionId']
target_fn = event.get('FunctionName', os.environ.get('TARGET_FUNCTION'))
target_ver = event.get('TargetVersion')
try:
# Invoke new Version directly (via Version ARN, not Alias)
resp = fn_client.invoke(
FunctionName=f'{target_fn}:{target_ver}',
InvocationType='RequestResponse',
Payload=json.dumps({'action': 'smoke-test'})
)
payload = json.loads(resp['Payload'].read())
ok = (resp['StatusCode'] == 200 and
'FunctionError' not in resp and
payload.get('status') == 'ok')
status = 'Succeeded' if ok else 'Failed'
except Exception as e:
print(f"Hook error: {e}")
status = 'Failed'
# Report result — without this API call, deployment fails on Timeout
deploy.put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id,
lifecycleEventHookExecutionId=hook_id,
status=status
)
return {'status': status}IAM permissions the Hook Lambda must have:
{
"Effect": "Allow",
"Action": [
"codedeploy:PutLifecycleEventHookExecutionStatus",
"lambda:InvokeFunction"
],
"Resource": "*"
}Without PutLifecycleEventHookExecutionStatus, the Hook cannot report its result to CodeDeploy. CodeDeploy treats the Hook as unresponsive and deployment fails on Timeout (default 3600 seconds).
AfterAllowTraffic Hook pattern:
def handler(event, context):
"""AfterAllowTraffic: validate after real traffic processing"""
deployment_id = event['DeploymentId']
hook_id = event['LifecycleEventHookExecutionId']
# Query real error metrics from CloudWatch
cw = boto3.client('cloudwatch')
resp = cw.get_metric_statistics(
Namespace='AWS/Lambda',
MetricName='Errors',
Dimensions=[
{'Name': 'FunctionName', 'Value': os.environ['TARGET_FN']},
{'Name': 'Resource', 'Value': f"{os.environ['TARGET_FN']}:live"}
],
StartTime=datetime.utcnow() - timedelta(minutes=5),
EndTime=datetime.utcnow(),
Period=300,
Statistics=['Sum']
)
error_count = sum(d['Sum'] for d in resp['Datapoints'])
status = 'Succeeded' if error_count < 10 else 'Failed'
boto3.client('codedeploy').put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id,
lifecycleEventHookExecutionId=hook_id,
status=status
)⚠️ Pitfall: The Hook Lambda's execution result (success/failure) and the result reported via
PutLifecycleEventHookExecutionStatusare separate. Even if Hook Lambda terminates normally (exit 0), ifPutLifecycleEventHookExecutionStatusis not called, CodeDeploy doesn't know the Hook result. You must reportSucceededorFailedvia API call. Hook Lambda execution timeout (15 min) is shorter than CodeDeploy Hook Timeout (3600 sec), so complete reporting before Lambda timeout.
💡 Related theory: Pre/Post Hook is the Deployment Gate pattern. "If the automated quality gate doesn't pass, don't proceed to the next step." This corresponds to "validation automation" in CALMS's Automation axis. Instead of manual post-deployment verification, the Hook validates automatically and auto-rolls back on failure. This pattern directly reduces DORA's Change Failure Rate.
SAM template.yaml:
Transform: AWS::Serverless-2016-10-31
Resources:
CheckoutFn:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src/checkout/
Handler: app.handler
Runtime: python3.11
Architectures: [arm64]
Tracing: Active # Enable X-Ray automatically
AutoPublishAlias: live # Auto-publish Version + create Alias per deployment
DeploymentPreference:
Type: Canary10Percent5Minutes
Hooks:
PreTraffic: !Ref PreTrafficCheck
PostTraffic: !Ref PostTrafficCheck
Alarms:
- !Ref ErrorRateAlarm
- !Ref P99LatencyAlarm
PreTrafficCheck:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src/hooks/
Handler: pre.handler
Policies:
- Statement:
- Effect: Allow
Action:
- codedeploy:PutLifecycleEventHookExecutionStatus
- lambda:InvokeFunction
Resource: '*'
ErrorRateAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: CheckoutFn-ErrorRate
Namespace: AWS/Lambda
MetricName: Errors
Dimensions:
- Name: FunctionName
Value: !Ref CheckoutFn
- Name: Resource
Value: !Sub '${CheckoutFn}:live' # Alias-level metric
Statistic: Sum
Period: 60
EvaluationPeriods: 2
Threshold: 5
ComparisonOperator: GreaterThanThreshold
TreatMissingData: notBreachingWhat AutoPublishAlias: live does (in order):
sam deploy runs, auto-publish new Lambda VersionDeploymentPreference present, auto-create CodeDeploy Application + Deployment GroupOne line (AutoPublishAlias) automates Version management, Alias management, CodeDeploy integration, and alarm-based rollback.
CDK equivalent:
from aws_cdk import aws_lambda as lambda_
from aws_cdk import aws_codedeploy as codedeploy
from aws_cdk import aws_cloudwatch as cloudwatch
fn = lambda_.Function(self, 'CheckoutFn', ...)
alias = lambda_.Alias(self, 'LiveAlias',
alias_name='live',
version=fn.current_version
)
deployment_group = codedeploy.LambdaDeploymentGroup(self, 'DG',
alias=alias,
deployment_config=codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES,
alarms=[error_rate_alarm],
auto_rollback=codedeploy.AutoRollbackConfig(
deployment_in_alarm=True,
stopped_deployment=True
)
)📚 Case study: A team previously deployed Lambda by calling
$LATESTdirectly. When deployment errors occurred, 100% switch already happened, taking 10 minutes to recover. After switching to SAMAutoPublishAlias+ Canary10Percent5Minutes, when the same error occurred, auto-rollback happened after 5 minutes, affecting only 10% of traffic. The remaining 90% was processed normally by the old version.
Provisioned Concurrency (PC) pre-warms Lambda execution environments to remove cold starts.
PC cost structure during Canary deployment:
# Pre-configure PC on new Version 6
aws lambda put-provisioned-concurrency-config \
--function-name MyFn \
--qualifier 6 \
--provisioned-concurrent-executions 10
# Wait for PC to be ready
aws lambda get-provisioned-concurrency-config \
--function-name MyFn --qualifier 6
# Wait until Status: READY
# Then start Alias CanaryPC cost per deployment phase:
| Phase | Version 5 (old) | Version 6 (new) | Cost |
|---|---|---|---|
| Before Canary starts | PC 10 | None | Normal |
| Canary in progress | PC 10 (90%) | PC 10 warming (10%) | ~2x |
| After 100% switch | PC removed | PC 10 kept | Normal |
| Old Version PC cleanup | Manual delete (delete-provisioned-concurrency-config) | — | — |
After full switch, you must manually delete the old version's PC. It doesn't auto-delete.
# Delete old version PC after switch completes
aws lambda delete-provisioned-concurrency-config \
--function-name MyFn \
--qualifier 5💡 Related theory: The cost model of Provisioned Concurrency + Canary deployment is the same logic as EC2 Blue/Green's "temporary 2× instance cost." While two versions are active simultaneously, both incur costs. The difference is EC2 is instance-based, Lambda PC is "warmed execution environment" based. Longer Canary period means more accumulated additional cost. Canary30Minutes means 30 min of additional PC cost; Canary5Minutes means only 5 min.
⚠️ Pitfall: Old version's Provisioned Concurrency doesn't auto-delete after deployment completes. You must manually execute
delete-provisioned-concurrency-config, or if Lambda Application Auto Scaling manages PC, update the schedule. Forgetting this means unused old version PC keeps getting charged.
Lambda metrics aggregate at function-level, specific Version, or Alias level.
# Alias-level Alarm (suitable for Canary deployment monitoring)
aws cloudwatch put-metric-alarm \
--alarm-name "MyFn-live-ErrorRate" \
--namespace "AWS/Lambda" \
--metric-name "Errors" \
--dimensions "Name=FunctionName,Value=MyFn" "Name=Resource,Value=MyFn:live" \
--statistic Sum \
--period 60 \
--evaluation-periods 2 \
--threshold 5 \
--comparison-operator GreaterThanThreshold \
--treat-missing-data notBreachingResource=MyFn:live counts only errors from invocations via Alias "live". During Canary 10%, errors from the new version are also included. If errors exceed threshold, alarm triggers → auto-rollback.
Combine multiple conditions with Composite Alarm:
# Combine error rate alarm + P99 latency alarm with OR
aws cloudwatch put-composite-alarm \
--alarm-name "MyFn-live-Composite" \
--alarm-rule "ALARM(MyFn-live-ErrorRate) OR ALARM(MyFn-live-P99Latency)"Connecting Composite Alarm to Deployment Group enables "rollback if either error rate or latency exceeds threshold."
🔍 Going deeper: Lambda metric Dimension design is key to Canary deployment monitoring. If you only specify
FunctionName=MyFn, the entire Function's errors are counted. If you specifyFunctionName=MyFn, Resource=MyFn:live, only invocations through the Alias are counted. If 5 Alias-level errors occur during Canary 10%, statistically about 1 (10%) are new version-related. Consider this in alarm threshold design — lower Canary percentage means new version errors get diluted in Alias metrics.
Lambda deployment strategy selection
=====================
Can failure be detected immediately (synchronous API, instant error)?
├─ YES + short observation fine → LambdaCanary10Percent5Minutes
├─ YES + sufficient observation needed → LambdaCanary10Percent30Minutes
└─ NO (delayed failure, batch) → LambdaLinear10PercentEvery1Minute
AllAtOnce: dev/test environments only, production forbidden
Traffic too low to have enough samples?
└─ Supplement with Hook + direct new Version invocation for smoke test
Must have no cold starts (PC configured)?
└─ Configure PC on new Version before Canary → must manually delete old Version PC after switch
Automating from CodePipeline?
└─ SAM AutoPublishAlias + DeploymentPreference is optimal combination
Click a choice to reveal the answer and explanation.
Question 1
What is the mechanism by which traffic is actually shifted in a Lambda canary deployment?
Question 2
In a LambdaCanary10Percent5Minutes deployment, what is the traffic distribution at 3 minutes in?
Question 3
What happens if a PreTrafficHook function exits normally without calling PutLifecycleEventHookExecutionStatus?
Question 4
What does SAM AutoPublishAlias: live handle automatically?
Question 5
What is the cost impact while deploying a Lambda function with provisioned concurrency using Canary 10% over 30 minutes?
Question 6
What is the maximum number of versions that can receive traffic at the same time from one Lambda alias?
Question 7
10 minutes into a canary deployment, a CloudWatch alarm enters the ALARM state. What does CodeDeploy do automatically?