AWS X-Ray

Hands-On

Demo

In this demo, we will:

  1. Set up IAM roles for X-Ray integration
  2. Create Lambda functions with X-Ray tracing
  3. Configure API Gateway with X-Ray tracing
  4. Deploy a DynamoDB table and enable tracing
  5. Create a multi-tier application workflow
  6. Generate traffic and analyze traces
  7. Use X-Ray Service Map to visualize architecture
  8. Clean up resources

Agenda

Step 1: Set up IAM Roles for X-Ray Integration

AWSLambdaBasicExecutionRole
AWSXRayDaemonWriteAccess
AmazonDynamoDBFullAccess
LambdaXRayRole
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": "arn:aws:lambda:*:*:function:GetProductFunction"
        }
    ]
}
LambdalnvokePolicy

Step 2: Create DynamoDB Table for Application Data

ProductCatalog
ProductId

Create table

Table settings

Capacity calculator

Read/write capacity settings

Warm throughput

Secondary indexes

Encryption at rest

Deletion protection

Tags

The ProductCatalog table was created successfully

Explore table items

Create item

{
  "ProductId": {
    "S": "PROD-001"
  },
  "ProductName": {
    "S": "Wireless Mouse"
  },
  "Price": {
    "N": "29.99"
  },
  "Category": {
    "S": "Electronics"
  },
  "Stock": {
    "N": "150"
  }
}
{
  "ProductId": {
    "S": "PROD-002"
  },
  "ProductName": {
    "S": "Mechanical Keyboard"
  },
  "Price": {
    "N": "89.99"
  },
  "Category": {
    "S": "Electronics"
  },
  "Stock": {
    "N": "75"
  }
}

ProductCatalog

Step 3: Create Lambda Functions with X-Ray Tracing

GetProductFunction

Change default execution role

Additional configurations

Logging configuration

Lambda service traces

Save

import json
import boto3
import random
import time

# Initialize DynamoDB client
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('ProductCatalog')

def lambda_handler(event, context):
    # Simulate variable processing time
    process_time = random.uniform(0.1, 0.5)
    
    try:
        # Extract product ID from event
        if 'pathParameters' not in event or 'productId' not in event['pathParameters']:
            return {
                'statusCode': 400,
                'body': json.dumps({'error': 'Product ID is required'}),
                'headers': {
                    'Content-Type': 'application/json'
                }
            }
        
        product_id = event['pathParameters']['productId']
        print(f"Fetching product: {product_id}")
        
        # Simulate validation processing
        time.sleep(process_time)
        
        # Query DynamoDB
        response = table.get_item(Key={'ProductId': product_id})
        
        if 'Item' not in response:
            print(f"Product not found: {product_id}")
            return {
                'statusCode': 404,
                'body': json.dumps({'error': 'Product not found'}),
                'headers': {
                    'Content-Type': 'application/json'
                }
            }
        
        print(f"Product found: {product_id}")
        
        # Simulate post-processing
        time.sleep(0.1)
        
        return {
            'statusCode': 200,
            'body': json.dumps(response['Item'], default=str),
            'headers': {
                'Content-Type': 'application/json'
            }
        }
        
    except Exception as e:
        print(f"Error: {str(e)}")
        import traceback
        traceback.print_exc()
        return {
            'statusCode': 500,
            'body': json.dumps({'error': 'Internal server error', 'details': str(e)}),
            'headers': {
                'Content-Type': 'application/json'
            }
        }
ProcessOrderFunction

Create 2nd function

Change default execution role

Additional configurations

Logging configuration

Lambda service traces

import json
import boto3
import random
import time
from datetime import datetime

# Initialize clients
dynamodb = boto3.resource('dynamodb')
lambda_client = boto3.client('lambda')
table = dynamodb.Table('ProductCatalog')

def lambda_handler(event, context):
    # Parse request body
    try:
        if 'body' in event:
            body = json.loads(event['body'])
        else:
            body = event
            
        product_id = body.get('productId')
        quantity = body.get('quantity', 1)
        
        print(f"Processing order - Product: {product_id}, Quantity: {quantity}")
        
    except Exception as e:
        return {
            'statusCode': 400,
            'body': json.dumps({'error': 'Invalid request body'}),
            'headers': {
                'Content-Type': 'application/json'
            }
        }
    
    try:
        # Call GetProductFunction to validate product exists
        invoke_response = lambda_client.invoke(
            FunctionName='GetProductFunction',
            InvocationType='RequestResponse',
            Payload=json.dumps({
                'pathParameters': {'productId': product_id}
            })
        )
        
        response_payload = json.loads(invoke_response['Payload'].read())
        
        if response_payload['statusCode'] != 200:
            return {
                'statusCode': 404,
                'body': json.dumps({'error': 'Product not found'}),
                'headers': {
                    'Content-Type': 'application/json'
                }
            }
            
        product = json.loads(response_payload['body'])
        
        # Simulate inventory check with random delay
        time.sleep(random.uniform(0.2, 0.6))
        
        if product.get('Stock', 0) < quantity:
            return {
                'statusCode': 400,
                'body': json.dumps({'error': 'Insufficient stock'}),
                'headers': {
                    'Content-Type': 'application/json'
                }
            }
        
        # Simulate order processing
        time.sleep(random.uniform(0.3, 0.7))
        
        # Occasionally simulate a slow operation
        if random.random() > 0.8:
            print("Slow operation triggered")
            time.sleep(2.0)
        
        order_id = f"ORD-{int(time.time())}"
        
        return {
            'statusCode': 200,
            'body': json.dumps({
                'orderId': order_id,
                'productId': product_id,
                'quantity': quantity,
                'totalPrice': float(product.get('Price', 0)) * quantity,
                'status': 'Processing'
            }),
            'headers': {
                'Content-Type': 'application/json'
            }
        }
        
    except Exception as e:
        print(f"Error processing order: {str(e)}")
        import traceback
        traceback.print_exc()
        return {
            'statusCode': 500,
            'body': json.dumps({'error': 'Order processing failed'}),
            'headers': {
                'Content-Type': 'application/json'
            }
        }

Step 4: Configure API Gateway with X-Ray Tracing

ProductServiceAPI
API for product catalog and order processing

Create REST API

Create resource

products

Create resource

{productId}

Create resource

Resources

Create method

Create method

Resources

orders

Create resource

Create method

Lambda function

Create method

Deploy API 

prod

Edit Stage 

Edit logs and tracing

Step 5: Generate Traffic and Test the Application

# Test retrieving a product
curl -X GET "${API_ENDPOINT}/products/PROD-001"
# Set your API endpoint
API_ENDPOINT=
# Test non-existent product
curl -X GET "${API_ENDPOINT}/products/PROD-999"
# Process an order
curl -X POST "${API_ENDPOINT}/orders" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "PROD-001",
    "quantity": 2
  }'
# Test with invalid product
curl -X POST "${API_ENDPOINT}/orders" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "INVALID-PRODUCT",
    "quantity": 1
  }'
# Generate 20 requests with varying patterns
for i in {1..20}; do
  # Alternate between products
  if [ $((i % 2)) -eq 0 ]; then 
    PRODUCT="PROD-001"
  else
    PRODUCT="PROD-002"
  fi

  # GET request
  curl -s -X GET "${API_ENDPOINT}/products/${PRODUCT}" > /dev/null & 

  # POST request
  curl -s -X POST "${API_ENDPOINT}/orders" \
    -H "Content-Type: application/json" \
    -d "{\"productId\": \"${PRODUCT}\", \"quantity\": $((RANDOM % 5 + 1))}" > /dev/null & 

  # Small delay between requests
  sleep 0.5
done

echo "Traffic generation complete. Wait for all requests to finish..."
wait 
echo "All requests completed."

Step 6: Analyze Traces in X-Ray Console

Heading 1

Heading 2

Heading 3

Regular Text

Learn the fundamentals and find valuable information to get the most out of AWS.
Formatted Text
Code Text
cat > test_memorydb.py << EOF
import redis
import json
import sys
from datetime import datetime

# Check for command line argument
if len(sys.argv) != 2:
    print("Usage: python3 test_memorydb.py <your-cluster-endpoint>")
    print("Example: python3 test_memorydb.py memorydb-demo-cluster.abc123.memorydb.us-east-1.amazonaws.com")
    sys.exit(1)

cluster_endpoint = sys.argv[1]

# Configure connection
try:
    r = redis.Redis(
        host=cluster_endpoint,
        port=6379,
        ssl=True,
        decode_responses=True
    )
    
    # Test connection
    r.ping()
    print(f"✓ Successfully connected to MemoryDB cluster: {cluster_endpoint}")
    
except redis.ConnectionError as e:
    print(f"✗ Failed to connect to {cluster_endpoint}")
    print(f"Error: {e}")
    print("\nPlease check:")
    print("  - Cluster endpoint is correct")
    print("  - Security group allows port 6379 from this instance")
    print("  - Instance and cluster are in the same VPC")
    sys.exit(1)

# Session management example
def create_session(user_id, username):
    session_data = {
        'user_id': user_id,
        'username': username,
        'login_time': datetime.now().isoformat()
    }
    # Store session with 30-minute expiration
    r.setex(f'session:{user_id}', 1800, json.dumps(session_data))
    print(f"✓ Session created for {username}")

def get_session(user_id):
    session = r.get(f'session:{user_id}')
    if session:
        return json.loads(session)
    return None

# Test the functions
print("\n--- Testing Session Management ---")
create_session('user001', 'alice')
session = get_session('user001')
print(f"✓ Retrieved session: {session}")

# Cache example with automatic expiration
print("\n--- Testing Cache with TTL ---")
r.setex('cache:api_response', 300, json.dumps({'data': 'cached response'}))
print(f"✓ Cache created with TTL: {r.ttl('cache:api_response')} seconds")

# Additional tests for data persistence
print("\n--- Testing Data Persistence ---")
r.set('persistent:data', 'This will survive a failover')
print(f"✓ Persistent data stored")

# Test various data structures
print("\n--- Testing Redis Data Structures ---")

# Hash
r.hset('user:1001', mapping={
    'name': 'Alice',
    'email': 'alice@example.com',
    'lastLogin': datetime.now().isoformat()
})
print(f"✓ Hash created: {r.hgetall('user:1001')}")

# List
r.lpush('recent:logins', 'user001', 'user002', 'user003')
print(f"✓ List created: {r.lrange('recent:logins', 0, -1)}")

# Set
r.sadd('active:users', 'alice', 'bob', 'charlie')
print(f"✓ Set created with {r.scard('active:users')} members")

print("\n✓ All tests completed successfully!")
EOF

🙏

Thanks

for

Watching

AWS X-Ray - Hands-On Demo

By Deepak Dubey

AWS X-Ray - Hands-On Demo

AWS X-Ray - Hands-On Demo

  • 39