Cloud Computing with AWS: A Comprehensive Guide
Cloud Computing with AWS: A Comprehensive Guide
Amazon Web Services (AWS) is the leading cloud platform, offering a vast array of services for building modern applications. This guide will help you master AWS and build scalable cloud solutions.
Getting Started with AWS
First, let's set up our AWS environment:
- Create an AWS account
- Install AWS CLI
- Configure credentials
# Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configure AWS credentials
aws configure
Core AWS Services
1. Amazon EC2 (Elastic Compute Cloud)
Launch and manage virtual servers:
# Launch an EC2 instance
aws ec2 run-instances \
--image-id ami-0c55b159cbfafe1f0 \
--instance-type t2.micro \
--key-name my-key-pair \
--security-group-ids sg-903004f8 \
--subnet-id subnet-6e7f829e
# List instances
aws ec2 describe-instances
# Stop instance
aws ec2 stop-instances --instance-ids i-1234567890abcdef0
# Terminate instance
aws ec2 terminate-instances --instance-ids i-1234567890abcdef0
2. Amazon S3 (Simple Storage Service)
Object storage for any type of data:
# Create bucket
aws s3 mb s3://my-bucket-name
# Upload file
aws s3 cp file.txt s3://my-bucket-name/
# List objects
aws s3 ls s3://my-bucket-name/
# Download file
aws s3 cp s3://my-bucket-name/file.txt ./
# Delete object
aws s3 rm s3://my-bucket-name/file.txt
# Delete bucket
aws s3 rb s3://my-bucket-name --force
3. Amazon RDS (Relational Database Service)
Managed relational databases:
# Create database instance
aws rds create-db-instance \
--db-instance-identifier mydb \
--db-instance-class db.t3.micro \
--engine postgres \
--master-username admin \
--master-user-password mypassword \
--allocated-storage 20
# List instances
aws rds describe-db-instances
# Delete instance
aws rds delete-db-instance \
--db-instance-identifier mydb \
--skip-final-snapshot
Project: Serverless Web Application
Let's build a complete serverless application using AWS services:
1. API Gateway Configuration
# serverless.yml
service: serverless-api
provider:
name: aws
runtime: nodejs18.x
stage: prod
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:custom.tableName}"
custom:
tableName: users-table
functions:
createUser:
handler: src/handlers/createUser.handler
events:
- http:
path: users
method: post
cors: true
getUser:
handler: src/handlers/getUser.handler
events:
- http:
path: users/{id}
method: get
cors: true
updateUser:
handler: src/handlers/updateUser.handler
events:
- http:
path: users/{id}
method: put
cors: true
deleteUser:
handler: src/handlers/deleteUser.handler
events:
- http:
path: users/{id}
method: delete
cors: true
resources:
Resources:
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.tableName}
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST
2. Lambda Functions
// src/handlers/createUser.ts
import { DynamoDB } from 'aws-sdk'
import { v4 as uuidv4 } from 'uuid'
import { APIGatewayProxyHandler } from 'aws-lambda'
const dynamodb = new DynamoDB.DocumentClient()
const tableName = process.env.USERS_TABLE!
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const data = JSON.parse(event.body || '{}')
const timestamp = new Date().getTime()
const user = {
id: uuidv4(),
name: data.name,
email: data.email,
createdAt: timestamp,
updatedAt: timestamp,
}
await dynamodb.put({
TableName: tableName,
Item: user,
}).promise()
return {
statusCode: 201,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify(user),
}
} catch (error) {
console.error(error)
return {
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: 'Could not create user' }),
}
}
}
// src/handlers/getUser.ts
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const { id } = event.pathParameters || {}
const result = await dynamodb.get({
TableName: tableName,
Key: { id },
}).promise()
if (!result.Item) {
return {
statusCode: 404,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: 'User not found' }),
}
}
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify(result.Item),
}
} catch (error) {
console.error(error)
return {
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: 'Could not get user' }),
}
}
}
// src/handlers/updateUser.ts
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const { id } = event.pathParameters || {}
const data = JSON.parse(event.body || '{}')
const timestamp = new Date().getTime()
const params = {
TableName: tableName,
Key: { id },
ExpressionAttributeNames: {
'#name': 'name',
'#email': 'email',
'#updatedAt': 'updatedAt',
},
ExpressionAttributeValues: {
':name': data.name,
':email': data.email,
':updatedAt': timestamp,
},
UpdateExpression: 'SET #name = :name, #email = :email, #updatedAt = :updatedAt',
ReturnValues: 'ALL_NEW',
}
const result = await dynamodb.update(params).promise()
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify(result.Attributes),
}
} catch (error) {
console.error(error)
return {
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: 'Could not update user' }),
}
}
}
// src/handlers/deleteUser.ts
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const { id } = event.pathParameters || {}
await dynamodb.delete({
TableName: tableName,
Key: { id },
}).promise()
return {
statusCode: 204,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: '',
}
} catch (error) {
console.error(error)
return {
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: 'Could not delete user' }),
}
}
}
3. Frontend Application
// src/api/users.ts
const API_URL = process.env.NEXT_PUBLIC_API_URL
export async function createUser(data: any) {
const response = await fetch(`${API_URL}/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to create user')
}
return response.json()
}
export async function getUser(id: string) {
const response = await fetch(`${API_URL}/users/${id}`)
if (!response.ok) {
throw new Error('Failed to get user')
}
return response.json()
}
export async function updateUser(id: string, data: any) {
const response = await fetch(`${API_URL}/users/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to update user')
}
return response.json()
}
export async function deleteUser(id: string) {
const response = await fetch(`${API_URL}/users/${id}`, {
method: 'DELETE',
})
if (!response.ok) {
throw new Error('Failed to delete user')
}
}
// src/components/UserForm.tsx
import { useState } from 'react'
import { createUser, updateUser } from '../api/users'
interface UserFormProps {
user?: any
onSubmit: () => void
}
export function UserForm({ user, onSubmit }: UserFormProps) {
const [name, setName] = useState(user?.name || '')
const [email, setEmail] = useState(user?.email || '')
const [loading, setLoading] = useState(false)
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setLoading(true)
try {
if (user) {
await updateUser(user.id, { name, email })
} else {
await createUser({ name, email })
}
onSubmit()
} catch (error) {
console.error(error)
alert('Failed to save user')
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="mt-1 block w-full rounded-md border-gray-300"
required
/>
</div>
<div>
<label className="block text-sm font-medium">
Email
</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mt-1 block w-full rounded-md border-gray-300"
required
/>
</div>
<button
type="submit"
disabled={loading}
className="bg-blue-500 text-white px-4 py-2 rounded-md"
>
{loading ? 'Saving...' : 'Save'}
</button>
</form>
)
}
Infrastructure as Code with AWS CDK
Define infrastructure using TypeScript:
// lib/app-stack.ts
import * as cdk from 'aws-cdk-lib'
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'
import * as route53 from 'aws-cdk-lib/aws-route53'
import * as targets from 'aws-cdk-lib/aws-route53-targets'
import * as acm from 'aws-cdk-lib/aws-certificatemanager'
export class AppStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props)
// S3 bucket for website hosting
const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
websiteIndexDocument: 'index.html',
websiteErrorDocument: 'error.html',
publicReadAccess: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
})
// CloudFront distribution
const distribution = new cloudfront.Distribution(this, 'Distribution', {
defaultBehavior: {
origin: new origins.S3Origin(websiteBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_100,
})
// Output the distribution URL
new cdk.CfnOutput(this, 'DistributionUrl', {
value: distribution.distributionDomainName,
})
}
}
Best Practices
-
Security
- Use IAM roles and policies
- Enable encryption at rest
- Implement network security
- Monitor and audit
-
Cost Optimization
- Use auto-scaling
- Implement lifecycle policies
- Monitor usage
- Use spot instances
-
High Availability
- Deploy across regions
- Use load balancers
- Implement failover
- Monitor health
-
Performance
- Use caching
- Optimize database queries
- Use CDN
- Monitor metrics
Common AWS Patterns
- Serverless Web Application
import * as lambda from 'aws-cdk-lib/aws-lambda'
import * as apigateway from 'aws-cdk-lib/aws-apigateway'
// Create Lambda function
const handler = new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromAsset('lambda'),
handler: 'index.handler',
})
// Create API Gateway
const api = new apigateway.RestApi(this, 'Api', {
restApiName: 'My API',
})
api.root.addMethod('GET', new apigateway.LambdaIntegration(handler))
- Event-Driven Architecture
import * as sqs from 'aws-cdk-lib/aws-sqs'
import * as sns from 'aws-cdk-lib/aws-sns'
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions'
// Create SQS queue
const queue = new sqs.Queue(this, 'Queue')
// Create SNS topic
const topic = new sns.Topic(this, 'Topic')
// Subscribe queue to topic
topic.addSubscription(new subscriptions.SqsSubscription(queue))
Conclusion
AWS provides powerful services for building modern applications:
- Scalable infrastructure
- Managed services
- Global reach
- Pay-as-you-go pricing
Keep exploring AWS services and best practices to build robust cloud applications.