Advanced Cloud Implementation
Building on Part 1 fundamentals, we'll now implement advanced cloud architectures with microservices, containers, and DevOps practices. This guide provides complete, production-ready examples you can deploy immediately.
Containerization with Docker
Why Containers?
- Consistency: Same environment across dev, test, prod
- Portability: Run anywhere Docker is supported
- Efficiency: Lightweight compared to VMs
- Scalability: Quick startup and shutdown
Complete Microservice Implementation
Let's build a complete user management microservice with Node.js, MongoDB, and Docker. This example demonstrates real-world patterns used in production systems.
1. Application Dependencies
First, define our Node.js application dependencies in package.json:
{
"name": "user-service",
"version": "1.0.0",
"main": "server.js",
"dependencies": {
"express": "^4.18.0",
"mongoose": "^6.0.0",
"cors": "^2.8.5"
},
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}
2. Microservice Application Code
Create the main application file server.js with REST API endpoints, database connection, and error handling:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
// User model with validation
const User = mongoose.model('User', {
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now }
});
// Health check endpoint for load balancers
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date(),
service: 'user-service'
});
});
// Get all users with pagination
app.get('/users', async (req, res) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
const users = await User.find().skip(skip).limit(limit);
const total = await User.countDocuments();
res.json({
users,
pagination: { page, limit, total, pages: Math.ceil(total / limit) }
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Create new user
app.post('/users', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.status(201).json(user);
} catch (error) {
if (error.code === 11000) {
res.status(400).json({ error: 'Email already exists' });
} else {
res.status(400).json({ error: error.message });
}
}
});
// Database connection with retry logic
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/userdb');
console.log('Connected to MongoDB');
} catch (error) {
console.error('MongoDB connection error:', error);
setTimeout(connectDB, 5000); // Retry after 5 seconds
}
};
connectDB();
app.listen(PORT, () => {
console.log();
});
3. Docker Configuration
Create a Dockerfile using multi-stage builds and security best practices:
# Use official Node.js runtime as base image
FROM node:16-alpine
# Set working directory
WORKDIR /app
# Copy package files first for better caching
COPY package*.json ./
# Install dependencies
RUN npm install --production && npm cache clean --force
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Create non-root user for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:3000/health || exit 1
# Start application
CMD ["npm", "start"]
4. Docker Compose for Local Development
Create docker-compose.yml to orchestrate multiple services locally:
version: '3.8'
services:
user-service:
build: .
ports:
- "3000:3000"
environment:
- MONGODB_URI=mongodb://mongo:27017/userdb
- NODE_ENV=development
depends_on:
- mongo
restart: unless-stopped
networks:
- app-network
mongo:
image: mongo:5.0
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_DATABASE=userdb
restart: unless-stopped
networks:
- app-network
volumes:
mongo_data:
networks:
app-network:
driver: bridge
5. Build and Test the Service
Commands to build, run, and test your containerized microservice:
# Build and start all services
docker-compose up -d
# Check service status
docker-compose ps
# View logs
docker-compose logs user-service
# Test health endpoint
curl http://localhost:3000/health
# Test user creation
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name":"John Doe","email":"john@example.com"}'
# Test user retrieval
curl http://localhost:3000/users
Kubernetes Orchestration
Kubernetes provides production-grade container orchestration with auto-scaling, service discovery, and rolling updates. Let's deploy our microservice to Kubernetes.
Complete Kubernetes Deployment
Create k8s/deployment.yaml with deployment, service, and configuration:
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
version: v1
spec:
containers:
- name: user-service
image: user-service:latest
ports:
- containerPort: 3000
env:
- name: MONGODB_URI
value: "mongodb://mongo-service:27017/userdb"
- name: NODE_ENV
value: "production"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service
labels:
app: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 3000
protocol: TCP
type: LoadBalancer
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Deploy to Kubernetes
Commands to deploy and manage your application in Kubernetes:
# Apply deployment configuration
kubectl apply -f k8s/deployment.yaml
# Check deployment status
kubectl get deployments
kubectl get pods
kubectl get services
# Scale deployment manually
kubectl scale deployment user-service --replicas=5
# Check auto-scaling status
kubectl get hpa
# View application logs
kubectl logs -l app=user-service
# Port forward for local testing
kubectl port-forward service/user-service 8080:80
Serverless Architecture with AWS Lambda
Serverless computing eliminates server management while providing automatic scaling and pay-per-use pricing. Let's implement the same user service using AWS Lambda and DynamoDB.
Lambda Function Implementation
Create lambda/handler.js with complete CRUD operations:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
// Main Lambda handler function
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
const { httpMethod, path, body, queryStringParameters } = event;
try {
// Route requests based on HTTP method and path
switch (httpMethod) {
case 'GET':
if (path === '/health') {
return createResponse(200, {
status: 'healthy',
timestamp: new Date().toISOString(),
service: 'user-service-lambda'
});
}
if (path === '/users') {
return await getUsers(queryStringParameters);
}
break;
case 'POST':
if (path === '/users') {
return await createUser(JSON.parse(body || '{}'));
}
break;
case 'PUT':
if (path.startsWith('/users/')) {
const userId = path.split('/')[2];
return await updateUser(userId, JSON.parse(body || '{}'));
}
break;
case 'DELETE':
if (path.startsWith('/users/')) {
const userId = path.split('/')[2];
return await deleteUser(userId);
}
break;
default:
return createResponse(405, { error: 'Method not allowed' });
}
return createResponse(404, { error: 'Not found' });
} catch (error) {
console.error('Error:', error);
return createResponse(500, { error: error.message });
}
};
// Get users with pagination
async function getUsers(queryParams = {}) {
const limit = parseInt(queryParams.limit) || 10;
const lastKey = queryParams.lastKey ? JSON.parse(queryParams.lastKey) : null;
const params = {
TableName: 'Users',
Limit: limit
};
if (lastKey) {
params.ExclusiveStartKey = lastKey;
}
const result = await dynamodb.scan(params).promise();
return createResponse(200, {
users: result.Items,
lastKey: result.LastEvaluatedKey,
count: result.Count
});
}
// Create new user
async function createUser(userData) {
const { name, email } = userData;
if (!name || !email) {
return createResponse(400, { error: 'Name and email are required' });
}
const user = {
id: AWS.util.uuid.v4(),
name,
email,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const params = {
TableName: 'Users',
Item: user,
ConditionExpression: 'attribute_not_exists(email)'
};
try {
await dynamodb.put(params).promise();
return createResponse(201, user);
} catch (error) {
if (error.code === 'ConditionalCheckFailedException') {
return createResponse(400, { error: 'Email already exists' });
}
throw error;
}
}
// Update existing user
async function updateUser(userId, updateData) {
const { name, email } = updateData;
const params = {
TableName: 'Users',
Key: { id: userId },
UpdateExpression: 'SET #name = :name, email = :email, updatedAt = :updatedAt',
ExpressionAttributeNames: { '#name': 'name' },
ExpressionAttributeValues: {
':name': name,
':email': email,
':updatedAt': new Date().toISOString()
},
ReturnValues: 'ALL_NEW'
};
const result = await dynamodb.update(params).promise();
return createResponse(200, result.Attributes);
}
// Delete user
async function deleteUser(userId) {
const params = {
TableName: 'Users',
Key: { id: userId }
};
await dynamodb.delete(params).promise();
return createResponse(204, {});
}
// Helper function to create HTTP response
function createResponse(statusCode, body) {
return {
statusCode,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
},
body: JSON.stringify(body)
};
}
Serverless Framework Configuration
Create serverless.yml for infrastructure as code deployment:
service: user-service-serverless
provider:
name: aws
runtime: nodejs16.x
region: us-east-1
stage: ${opt:stage, 'dev'}
# IAM permissions for DynamoDB
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- "arn:aws:dynamodb:${self:provider.region}:*:table/Users"
- "arn:aws:dynamodb:${self:provider.region}:*:table/Users/index/*"
# Environment variables
environment:
STAGE: ${self:provider.stage}
REGION: ${self:provider.region}
functions:
api:
handler: handler.handler
events:
- http:
path: /{proxy+}
method: ANY
cors: true
- http:
path: /
method: ANY
cors: true
timeout: 30
memorySize: 256
# AWS Resources
resources:
Resources:
# DynamoDB Table
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Users
AttributeDefinitions:
- AttributeName: id
AttributeType: S
- AttributeName: email
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
GlobalSecondaryIndexes:
- IndexName: EmailIndex
KeySchema:
- AttributeName: email
KeyType: HASH
Projection:
ProjectionType: ALL
BillingMode: PAY_PER_REQUEST
BillingMode: PAY_PER_REQUEST
# CloudWatch Log Group
ApiLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/${self:service}-${self:provider.stage}-api
RetentionInDays: 14
# Plugins
plugins:
- serverless-offline
- serverless-dotenv-plugin
# Package configuration
package:
exclude:
- node_modules/**
- .git/**
- .env
- README.md
Deploy Serverless Application
Commands to deploy and test your serverless application:
# Install Serverless Framework globally
npm install -g serverless
# Install project dependencies
npm install
# Deploy to AWS
serverless deploy
# Deploy specific function
serverless deploy function -f api
# Test locally
serverless offline
# View logs
serverless logs -f api -t
# Remove deployment
serverless remove
CI/CD Pipeline with GitHub Actions
Implement automated testing, building, and deployment pipeline for continuous integration and delivery.
Complete GitHub Actions Workflow
Create .github/workflows/deploy.yml for automated deployments:
name: Deploy Cloud Application
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
AWS_REGION: us-east-1
ECR_REPOSITORY: user-service
EKS_CLUSTER_NAME: production-cluster
jobs:
# Test job runs on all branches
test:
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:5.0
ports:
- 27017:27017
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm test
env:
MONGODB_URI: mongodb://localhost:27017/test
- name: Run integration tests
run: npm run test:integration
env:
MONGODB_URI: mongodb://localhost:27017/test
# Security scanning
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run security audit
run: npm audit --audit-level high
- name: Run Snyk security scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# Build and push Docker image
build:
needs: [test, security]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Deploy to Kubernetes
deploy-k8s:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --region ${{ env.AWS_REGION }} --name ${{ env.EKS_CLUSTER_NAME }}
- name: Deploy to Kubernetes
run: |
# Update image in deployment
kubectl set image deployment/user-service user-service=${{ needs.build.outputs.image-tag }}
# Wait for rollout to complete
kubectl rollout status deployment/user-service --timeout=300s
# Verify deployment
kubectl get pods -l app=user-service
# Deploy serverless (parallel to K8s)
deploy-serverless:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy with Serverless Framework
run: |
npm install -g serverless
serverless deploy --stage production
Infrastructure as Code with Terraform
Manage your cloud infrastructure using code for version control, repeatability, and collaboration.
Complete AWS Infrastructure
Create terraform/main.tf for complete AWS infrastructure:
# Configure Terraform and AWS provider
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# Remote state storage
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "user-service/terraform.tfstate"
region = "us-east-1"
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Project = "user-service"
Environment = var.environment
ManagedBy = "terraform"
}
}
}
# Data sources
data "aws_availability_zones" "available" {
state = "available"
}
# VPC and Networking
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
# Public subnets for load balancers
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet-${count.index + 1}"
Type = "public"
}
}
# Private subnets for application workloads
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-private-subnet-${count.index + 1}"
Type = "private"
}
}
# NAT Gateways for private subnet internet access
resource "aws_eip" "nat" {
count = 2
domain = "vpc"
tags = {
Name = "${var.project_name}-nat-eip-${count.index + 1}"
}
}
resource "aws_nat_gateway" "main" {
count = 2
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project_name}-nat-${count.index + 1}"
}
depends_on = [aws_internet_gateway.main]
}
# Route tables
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-public-rt"
}
}
resource "aws_route_table" "private" {
count = 2
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.project_name}-private-rt-${count.index + 1}"
}
}
# Route table associations
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = 2
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
# Security Groups
resource "aws_security_group" "eks_cluster" {
name_prefix = "${var.project_name}-eks-cluster"
vpc_id = aws_vpc.main.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-eks-cluster-sg"
}
}
resource "aws_security_group" "eks_nodes" {
name_prefix = "${var.project_name}-eks-nodes"
vpc_id = aws_vpc.main.id
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
self = true
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-eks-nodes-sg"
}
}
# EKS Cluster
resource "aws_eks_cluster" "main" {
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = "1.24"
vpc_config {
subnet_ids = concat(aws_subnet.public[*].id, aws_subnet.private[*].id)
endpoint_private_access = true
endpoint_public_access = true
security_group_ids = [aws_security_group.eks_cluster.id]
}
enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
depends_on = [
aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
aws_cloudwatch_log_group.eks_cluster,
]
tags = {
Name = var.cluster_name
}
}
# EKS Node Group
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "main-nodes"
node_role_arn = aws_iam_role.node.arn
subnet_ids = aws_subnet.private[*].id
scaling_config {
desired_size = var.node_desired_size
max_size = var.node_max_size
min_size = var.node_min_size
}
update_config {
max_unavailable = 1
}
instance_types = var.node_instance_types
capacity_type = "ON_DEMAND"
disk_size = 20
depends_on = [
aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy,
aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy,
aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly,
]
tags = {
Name = "${var.project_name}-node-group"
}
}
# CloudWatch Log Group for EKS
resource "aws_cloudwatch_log_group" "eks_cluster" {
name = "/aws/eks/${var.cluster_name}/cluster"
retention_in_days = 7
}
# ECR Repository
resource "aws_ecr_repository" "app" {
name = var.project_name
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
tags = {
Name = "${var.project_name}-ecr"
}
}
# Output values
output "cluster_endpoint" {
description = "Endpoint for EKS control plane"
value = aws_eks_cluster.main.endpoint
}
output "cluster_security_group_id" {
description = "Security group ids attached to the cluster control plane"
value = aws_eks_cluster.main.vpc_config[0].cluster_security_group_id
}
output "ecr_repository_url" {
description = "ECR repository URL"
value = aws_ecr_repository.app.repository_url
}
Deploy Infrastructure
Commands to deploy and manage your infrastructure:
# Initialize Terraform
terraform init
# Plan deployment
terraform plan -var-file="production.tfvars"
# Apply changes
terraform apply -var-file="production.tfvars"
# Show current state
terraform show
# Destroy infrastructure (when needed)
terraform destroy -var-file="production.tfvars"
Monitoring and Observability
Implement comprehensive monitoring, logging, and alerting for production systems.
Application Monitoring Setup
Add monitoring to your Node.js application:
const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatch();
// Custom metrics helper
class MetricsCollector {
constructor(namespace = 'UserService') {
this.namespace = namespace;
this.cloudwatch = new AWS.CloudWatch();
}
async publishMetric(metricName, value, unit = 'Count', dimensions = {}) {
const params = {
Namespace: this.namespace,
MetricData: [{
MetricName: metricName,
Value: value,
Unit: unit,
Timestamp: new Date(),
Dimensions: Object.entries(dimensions).map(([Name, Value]) => ({ Name, Value }))
}]
};
try {
await this.cloudwatch.putMetricData(params).promise();
} catch (error) {
console.error('Error publishing metric:', error);
}
}
async publishTimer(metricName, startTime, dimensions = {}) {
const duration = Date.now() - startTime;
await this.publishMetric(metricName, duration, 'Milliseconds', dimensions);
}
}
const metrics = new MetricsCollector();
// Middleware for request monitoring
app.use(async (req, res, next) => {
const startTime = Date.now();
res.on('finish', async () => {
const duration = Date.now() - startTime;
// Publish metrics
await metrics.publishMetric('RequestCount', 1, 'Count', {
Method: req.method,
Route: req.route?.path || req.path,
StatusCode: res.statusCode.toString()
});
await metrics.publishMetric('ResponseTime', duration, 'Milliseconds', {
Method: req.method,
Route: req.route?.path || req.path
});
// Log request details
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
method: req.method,
path: req.path,
statusCode: res.statusCode,
duration,
userAgent: req.get('User-Agent'),
ip: req.ip
}));
});
next();
});
// Enhanced error handling with metrics
app.use(async (error, req, res, next) => {
console.error('Application error:', error);
// Publish error metric
await metrics.publishMetric('ErrorCount', 1, 'Count', {
ErrorType: error.name || 'UnknownError',
Route: req.route?.path || req.path
});
res.status(500).json({
error: 'Internal server error',
requestId: req.id || 'unknown'
});
});
Production Best Practices
Security
- Use IAM roles instead of access keys
- Enable encryption at rest and in transit
- Implement network segmentation with VPCs
- Regular security audits and penetration testing
- Use secrets management services
Performance
- Implement caching strategies (Redis, CloudFront)
- Use CDNs for static content delivery
- Database query optimization and indexing
- Auto-scaling based on metrics
- Load balancing and health checks
Reliability
- Multi-AZ deployments for high availability
- Circuit breaker patterns for fault tolerance
- Comprehensive monitoring and alerting
- Disaster recovery and backup strategies
- Blue-green deployments for zero downtime
Next Steps in Your Cloud Journey
- Practice: Deploy these examples in your own AWS account
- Experiment: Modify configurations and observe the results
- Scale: Add more services and implement service mesh
- Optimize: Focus on cost optimization and performance tuning
- Specialize: Choose specific areas like ML, IoT, or data analytics
- Certify: Pursue advanced cloud certifications
- Contribute: Share your knowledge and contribute to open source
Cloud computing continues to evolve rapidly with new services and capabilities. The key to success is hands-on practice, continuous learning, and staying current with cloud provider announcements and industry best practices.
Start with these examples, adapt them to your needs, and gradually build more complex, production-ready systems. Remember that cloud architecture is about solving business problems efficiently and securely.