Luciano Mammino PRO
Cloud developer, entrepreneur, fighter, butterfly maker! #nodejs #javascript - Author of https://www.nodejsdesignpatterns.com , Founder of https://fullstackbulletin.com
Luciano Mammino (@loige)
2022-03-01
Get the slides! ๐
๐จโ๐ป Senior Architect @ fourTheorem (Dublin ๐ฎ๐ช)
๐ Co-Author of Node.js Design Patternsย ๐
Accelerated Serverlessย | AI as a Serviceย | Platform Modernisation
We are hiring
do you want to work with us?
Pleaze, subscribe ๐
๐ฉโโ๏ธ SaaS CMS for legal practices
๐จโ๐ผ1 founder + ๐ฅท1 developer
๐ธ Bootstrapped business
๐ Good MVP, getting attention in the market
๐ช Started a TRIAL with a big customer
๐ The company is growing
โ๏ธ but the technology does not scale!
๐ฆ 1 monolithic server
๐ฅ Frequent failures = ๐คฌ unhappy customers
๐ฅ The business is at risk!
โ๏ธ More reliable & scalable infrastructure
๐ Minimal amount of change required*
* the team is not skilled with the cloud & containers, we need to keep cognitive load low
๐ค
"I heard that the cloud is great but we don't have the time and the skills to re-architect everything as micro-services!"
A user logs in the application and they should be able to see all their previously uploaded legal documents
A user can upload new documents and organize them by providing specific tags (client id, case number, etc.)
A user might search for documents containing specific keywords or tags
* a beautiful monolith โค๏ธ
* Mostly AWS from here
A physical location around the world (e.g. North Virginia, Irelandย or Sydney)ย where AWS hosts a group of data centers.
ย
Regions help to provision infrastructure that is closer to the customers, so that our applications can have low latency and feel responsive.
Discrete data center with redundant power, networking, and connectivity in an AWS Region.
ย
Data centers in different availability zones are disjointed from one another, so if thereโs a serious outage, thatโs rarely affecting more than one availability zone at the same time.
Itโs good practice to spread redundant infrastructure across different availability zones in a given region to guarantee high availability.
A virtual (private) network provisioned in a given region for a given AWS account.
ย
It is logically isolated from other virtual networks in AWS.
ย
Every VPC has a range of private IP addresses organised in one or more subnets.
A range of IPs in a given VPC and in a given availability zone that can be used to spin up and connect resources within the network.
ย
Subnets can be public or private.
ย
A public subnet can be used to run instances that can have a public IP assigned to them and can be reachable from outside the VPC itself.
Itโs good practice to keep front-facing servers (or load balancers) in public subnets and keep everything else (backend services, databases, etc.) in private subnets.
ย
Traffic between subnets can be enabled through routing tables to allow for instance a load balancer in a public subnet to forward traffic to backend instances in a private subnet.
ย
Region
AZ1
AZ2
AZ3
VPC
Subnet
Resource
(e.g. EC2 instance)
โ Create an AWS account
โ Select a region
โ Create and configure a VPC
โ๏ธ Create an AWS account
โ๏ธ Select a region
โ๏ธ Create and configure a VPC
โ Load Balancer
โ EC2
โ S3
โ RDS
โ ElastiCache
โ Route 53
The entry point to all the application traffic.
ย
Layer 7 Load Balancer (HTTP, HTTPS, WebSocket, gRPC).
ย
Highly available: replicated in all our public subnets.
Scalable: can handle millions of request per second.
ย
Managed service: we don't need to configure the OS or install software patches.
ย
Can be integrated with ACM (AWS Certificate Manager) to support HTTPS.
Target group
Target group
๐ฅ
/health
โ
โ
/health
/health
โ
Unhealty targets
won't get any traffic
Targets can be added dynamically.
ย
We can scale targets automatically using autoscaling groups.
ย
E.g. Add or remove instances based on num requests in-flight or on avg CPU of the current instances.
Being a managed service, scalability is mostly handled out of the box by AWS.
A load balancer can distribute traffic to multiple AZs, so if one of them becomes unavailable it will keep distributing traffic to the remaining ones.ย
โ๏ธ Load Balancer
โ EC2
โ S3
โ RDS
โ ElastiCache
โ Route 53
Virtual machine running all the necessary software for the service (Nginx, Node.js, app code, etc.)
ย
They need to use Security Groups (allow traffic) and IAM Roles (allow them to access other AWS resources like S3).
We will need to provision multiple machines dynamically.
Challenges:
All our virtual machines have to be the same: we need to build an AMI (Amazon Machine Image).
ย
An AMI contains OS, libraries, software and source code.
ย
You can use an AMI to start a new instance.
While we can build an AMI manually, it's better to use tools to automate the work:
Once an instance has been launched we shouldn't change it anymore (e.g update the OS, install new softare, update the code, etc.)
ย
If we need to change something, we build a new image and deploy new instances.
ย
Instances are disposable!
We are load balancing traffic so a user might be served by different instances during their session.
ย
A single instance should not store any state (e.g. user sessions, uploaded files, etc.)
ย
State should be stored outside instances (ElastiCache, S3, RDS, etc).
Making an application stateless might require a good amount of code change.
ย
A shortcut to this might be to enable sticky sessions in the ALB, but it's not recommended for scalability and resiliency.ย
Every instance will be able to handle a certain number of requests per second.
ย
We can scale by adding more instances when the traffic grows.
We should have at least 1 instance per availability zone.
ย
If there is an AWS outage, the instances on the healthy availability zone will keep handling requests.
ย
We can use an autoscaling group to make sure that unhealthy instances are replaced.
โ๏ธ Load Balancer
โ๏ธ EC2
โ S3
โ RDS
โ ElastiCache
โ Route 53
One of the very first AWS services and (probably) the most famous one.
ย
Object storage service: Allows you to store any amount of data durably.
ย
You need to use the SDK to read and write data.
Data can be organised in logical containers called Buckets.
ย
Key/value model: Inside a bucket you can store data by providing a key and the content.
const AWS = require('aws-sdk')
const s3 = new AWS.S3()
const params = {
Bucket: 'my-bucket',
Key: 'my-first-s3-file.txt',
Body: Buffer.from('Hello, AWS')
}
s3.upload(params, (err) => {
if (err) {
console.error(err)
} else {
console.log('Upload successful')
}
})
Too much code to change?
A first migration could be done by using a something like s3fs-fuseย to create a "virtual filesystem" that allows you to read/write to S3 seamlessly.
S3 is a managed service which automatically scales to thousands of read/write operations per second.
S3 is provisioned in multiple AZs by default and it makes multiple copies of your data.
ย
All of this happens transparently, no special configuration required.
โ๏ธ Load Balancer
โ๏ธ EC2
โ๏ธ S3
โ RDS
โ ElastiCache
โ Route 53
Managed relational database service for MySql, PostgreSQL, MariaDB, Oracle & SQL Server.
ย
Being a managed service, AWS takes care of most common concerns like backups and updates (configurable).
RDS PostgreSQL supports Read Replicas: you can provision additional instances to which you can distribute heavy read-only queries.
ย
ย
RDS PostgreSQL can be configured to work in Multi-AZ mode: this means that there will be one or two standby copies of the database in different AZs.
ย
If the primary DB instance or the primary AZ have an outage, one of the standby copies are promoted to become "the primary" instance.
ย
Failover is fast but not instantaneous (60-120 seconds), so we need to make sure to plan for possible connectivity failures in your app and show clear error messages to the users.
โ๏ธ Load Balancer
โ๏ธ EC2
โ๏ธ S3
โ๏ธ RDS
โ ElastiCache
โ Route 53
Managed in-memory caching service supporting Redis and Memcached.
ย
Meant to be used for use cases that don't require durability like data cache, session stores, gaming leaderboards, streaming, and analytics.
ย
AWS takes care of maintenance.
A single instance of Redis (with enough memory) can scale to significant amounts of traffic.
ย
If you need more, you can run ElastiCache Redis in Cluster Mode and shard your data across multiple Redis instances.
ElastiCache Redis can operate in Multi-AZ mode.
ย
Similarly to RDS, in case of failures, there might be some downtime while the new master is promoted.
ย
We need to make sure the app accounts for Redis connection failures.
โ๏ธ Load Balancer
โ๏ธ EC2
โ๏ธ S3
โ๏ธ RDS
โ๏ธ ElastiCache
โ Route 53
Highly available and scalable cloud DNS service.
ย
Can be used to direct traffic on a given domain to our Application Load Balancer.
โ๏ธ Load Balancer
โ๏ธ EC2
โ๏ธ S3
โ๏ธ RDS
โ๏ธ ElastiCache
โ๏ธ Route 53
We could provision everything "manually" from the web console, but...
ย
It's better to define all the infrastructure using code. There are several tools that can help us with that:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example creates an EC2 security group for the instance to give you SSH access. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",
"Parameters" : {
"KeyName": {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance",
"Type": "AWS::EC2::KeyPair::KeyName",
"ConstraintDescription" : "must be the name of an existing EC2 KeyPair."
},
"InstanceType" : {
"Description" : "WebServer EC2 instance type",
"Type" : "String",
"Default" : "t2.small",
"AllowedValues" : [ "t1.micro", "t2.nano", "t2.micro", "t2.small", "t2.medium", "t2.large", "m1.small", "m1.medium", "m1.large", "m1.xlarge", "m2.xlarge", "m2.2xlarge", "m2.4xlarge", "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge", "m4.large", "m4.xlarge", "m4.2xlarge", "m4.4xlarge", "m4.10xlarge", "c1.medium", "c1.xlarge", "c3.large", "c3.xlarge", "c3.2xlarge", "c3.4xlarge", "c3.8xlarge", "c4.large", "c4.xlarge", "c4.2xlarge", "c4.4xlarge", "c4.8xlarge", "g2.2xlarge", "g2.8xlarge", "r3.large", "r3.xlarge", "r3.2xlarge", "r3.4xlarge", "r3.8xlarge", "i2.xlarge", "i2.2xlarge", "i2.4xlarge", "i2.8xlarge", "d2.xlarge", "d2.2xlarge", "d2.4xlarge", "d2.8xlarge", "hi1.4xlarge", "hs1.8xlarge", "cr1.8xlarge", "cc2.8xlarge", "cg1.4xlarge"]
,
"ConstraintDescription" : "must be a valid EC2 instance type."
},
"SSHLocation" : {
"Description" : "The IP address range that can be used to SSH to the EC2 instances",
"Type": "String",
"MinLength": "9",
"MaxLength": "18",
"Default": "0.0.0.0/0",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
}
},
"Mappings" : {
"AWSInstanceType2Arch" : {
"t1.micro" : { "Arch" : "HVM64" },
"t2.nano" : { "Arch" : "HVM64" },
"t2.micro" : { "Arch" : "HVM64" },
"t2.small" : { "Arch" : "HVM64" },
"t2.medium" : { "Arch" : "HVM64" },
"t2.large" : { "Arch" : "HVM64" },
"m1.small" : { "Arch" : "HVM64" },
"m1.medium" : { "Arch" : "HVM64" },
"m1.large" : { "Arch" : "HVM64" },
"m1.xlarge" : { "Arch" : "HVM64" },
"m2.xlarge" : { "Arch" : "HVM64" },
"m2.2xlarge" : { "Arch" : "HVM64" },
"m2.4xlarge" : { "Arch" : "HVM64" },
"m3.medium" : { "Arch" : "HVM64" },
"m3.large" : { "Arch" : "HVM64" },
"m3.xlarge" : { "Arch" : "HVM64" },
"m3.2xlarge" : { "Arch" : "HVM64" },
"m4.large" : { "Arch" : "HVM64" },
"m4.xlarge" : { "Arch" : "HVM64" },
"m4.2xlarge" : { "Arch" : "HVM64" },
"m4.4xlarge" : { "Arch" : "HVM64" },
"m4.10xlarge" : { "Arch" : "HVM64" },
"c1.medium" : { "Arch" : "HVM64" },
"c1.xlarge" : { "Arch" : "HVM64" },
"c3.large" : { "Arch" : "HVM64" },
"c3.xlarge" : { "Arch" : "HVM64" },
"c3.2xlarge" : { "Arch" : "HVM64" },
"c3.4xlarge" : { "Arch" : "HVM64" },
"c3.8xlarge" : { "Arch" : "HVM64" },
"c4.large" : { "Arch" : "HVM64" },
"c4.xlarge" : { "Arch" : "HVM64" },
"c4.2xlarge" : { "Arch" : "HVM64" },
"c4.4xlarge" : { "Arch" : "HVM64" },
"c4.8xlarge" : { "Arch" : "HVM64" },
"g2.2xlarge" : { "Arch" : "HVMG2" },
"g2.8xlarge" : { "Arch" : "HVMG2" },
"r3.large" : { "Arch" : "HVM64" },
"r3.xlarge" : { "Arch" : "HVM64" },
"r3.2xlarge" : { "Arch" : "HVM64" },
"r3.4xlarge" : { "Arch" : "HVM64" },
"r3.8xlarge" : { "Arch" : "HVM64" },
"i2.xlarge" : { "Arch" : "HVM64" },
"i2.2xlarge" : { "Arch" : "HVM64" },
"i2.4xlarge" : { "Arch" : "HVM64" },
"i2.8xlarge" : { "Arch" : "HVM64" },
"d2.xlarge" : { "Arch" : "HVM64" },
"d2.2xlarge" : { "Arch" : "HVM64" },
"d2.4xlarge" : { "Arch" : "HVM64" },
"d2.8xlarge" : { "Arch" : "HVM64" },
"hi1.4xlarge" : { "Arch" : "HVM64" },
"hs1.8xlarge" : { "Arch" : "HVM64" },
"cr1.8xlarge" : { "Arch" : "HVM64" },
"cc2.8xlarge" : { "Arch" : "HVM64" }
},
"AWSInstanceType2NATArch" : {
"t1.micro" : { "Arch" : "NATHVM64" },
"t2.nano" : { "Arch" : "NATHVM64" },
"t2.micro" : { "Arch" : "NATHVM64" },
"t2.small" : { "Arch" : "NATHVM64" },
"t2.medium" : { "Arch" : "NATHVM64" },
"t2.large" : { "Arch" : "NATHVM64" },
"m1.small" : { "Arch" : "NATHVM64" },
"m1.medium" : { "Arch" : "NATHVM64" },
"m1.large" : { "Arch" : "NATHVM64" },
"m1.xlarge" : { "Arch" : "NATHVM64" },
"m2.xlarge" : { "Arch" : "NATHVM64" },
"m2.2xlarge" : { "Arch" : "NATHVM64" },
"m2.4xlarge" : { "Arch" : "NATHVM64" },
"m3.medium" : { "Arch" : "NATHVM64" },
"m3.large" : { "Arch" : "NATHVM64" },
"m3.xlarge" : { "Arch" : "NATHVM64" },
"m3.2xlarge" : { "Arch" : "NATHVM64" },
"m4.large" : { "Arch" : "NATHVM64" },
"m4.xlarge" : { "Arch" : "NATHVM64" },
"m4.2xlarge" : { "Arch" : "NATHVM64" },
"m4.4xlarge" : { "Arch" : "NATHVM64" },
"m4.10xlarge" : { "Arch" : "NATHVM64" },
"c1.medium" : { "Arch" : "NATHVM64" },
"c1.xlarge" : { "Arch" : "NATHVM64" },
"c3.large" : { "Arch" : "NATHVM64" },
"c3.xlarge" : { "Arch" : "NATHVM64" },
"c3.2xlarge" : { "Arch" : "NATHVM64" },
"c3.4xlarge" : { "Arch" : "NATHVM64" },
"c3.8xlarge" : { "Arch" : "NATHVM64" },
"c4.large" : { "Arch" : "NATHVM64" },
"c4.xlarge" : { "Arch" : "NATHVM64" },
"c4.2xlarge" : { "Arch" : "NATHVM64" },
"c4.4xlarge" : { "Arch" : "NATHVM64" },
"c4.8xlarge" : { "Arch" : "NATHVM64" },
"g2.2xlarge" : { "Arch" : "NATHVMG2" },
"g2.8xlarge" : { "Arch" : "NATHVMG2" },
"r3.large" : { "Arch" : "NATHVM64" },
"r3.xlarge" : { "Arch" : "NATHVM64" },
"r3.2xlarge" : { "Arch" : "NATHVM64" },
"r3.4xlarge" : { "Arch" : "NATHVM64" },
"r3.8xlarge" : { "Arch" : "NATHVM64" },
"i2.xlarge" : { "Arch" : "NATHVM64" },
"i2.2xlarge" : { "Arch" : "NATHVM64" },
"i2.4xlarge" : { "Arch" : "NATHVM64" },
"i2.8xlarge" : { "Arch" : "NATHVM64" },
"d2.xlarge" : { "Arch" : "NATHVM64" },
"d2.2xlarge" : { "Arch" : "NATHVM64" },
"d2.4xlarge" : { "Arch" : "NATHVM64" },
"d2.8xlarge" : { "Arch" : "NATHVM64" },
"hi1.4xlarge" : { "Arch" : "NATHVM64" },
"hs1.8xlarge" : { "Arch" : "NATHVM64" },
"cr1.8xlarge" : { "Arch" : "NATHVM64" },
"cc2.8xlarge" : { "Arch" : "NATHVM64" }
}
,
"AWSRegionArch2AMI" : {
"af-south-1" : {"HVM64" : "ami-064cc455f8a1ef504", "HVMG2" : "NOT_SUPPORTED"},
"ap-east-1" : {"HVM64" : "ami-f85b1989", "HVMG2" : "NOT_SUPPORTED"},
"ap-northeast-1" : {"HVM64" : "ami-0b2c2a754d5b4da22", "HVMG2" : "ami-09d0e0e099ecabba2"},
"ap-northeast-2" : {"HVM64" : "ami-0493ab99920f410fc", "HVMG2" : "NOT_SUPPORTED"},
"ap-northeast-3" : {"HVM64" : "ami-01344f6f63a4decc1", "HVMG2" : "NOT_SUPPORTED"},
"ap-south-1" : {"HVM64" : "ami-03cfb5e1fb4fac428", "HVMG2" : "ami-0244c1d42815af84a"},
"ap-southeast-1" : {"HVM64" : "ami-0ba35dc9caf73d1c7", "HVMG2" : "ami-0e46ce0d6a87dc979"},
"ap-southeast-2" : {"HVM64" : "ami-0ae99b503e8694028", "HVMG2" : "ami-0c0ab057a101d8ff2"},
"ca-central-1" : {"HVM64" : "ami-0803e21a2ec22f953", "HVMG2" : "NOT_SUPPORTED"},
"cn-north-1" : {"HVM64" : "ami-07a3f215cc90c889c", "HVMG2" : "NOT_SUPPORTED"},
"cn-northwest-1" : {"HVM64" : "ami-0a3b3b10f714a0ff4", "HVMG2" : "NOT_SUPPORTED"},
"eu-central-1" : {"HVM64" : "ami-0474863011a7d1541", "HVMG2" : "ami-0aa1822e3eb913a11"},
"eu-north-1" : {"HVM64" : "ami-0de4b8910494dba0f", "HVMG2" : "ami-32d55b4c"},
"eu-south-1" : {"HVM64" : "ami-08427144fe9ebdef6", "HVMG2" : "NOT_SUPPORTED"},
"eu-west-1" : {"HVM64" : "ami-015232c01a82b847b", "HVMG2" : "ami-0d5299b1c6112c3c7"},
"eu-west-2" : {"HVM64" : "ami-0765d48d7e15beb93", "HVMG2" : "NOT_SUPPORTED"},
"eu-west-3" : {"HVM64" : "ami-0caf07637eda19d9c", "HVMG2" : "NOT_SUPPORTED"},
"me-south-1" : {"HVM64" : "ami-0744743d80915b497", "HVMG2" : "NOT_SUPPORTED"},
"sa-east-1" : {"HVM64" : "ami-0a52e8a6018e92bb0", "HVMG2" : "NOT_SUPPORTED"},
"us-east-1" : {"HVM64" : "ami-032930428bf1abbff", "HVMG2" : "ami-0aeb704d503081ea6"},
"us-east-2" : {"HVM64" : "ami-027cab9a7bf0155df", "HVMG2" : "NOT_SUPPORTED"},
"us-west-1" : {"HVM64" : "ami-088c153f74339f34c", "HVMG2" : "ami-0a7fc72dc0e51aa77"},
"us-west-2" : {"HVM64" : "ami-01fee56b22f308154", "HVMG2" : "ami-0fe84a5b4563d8f27"}
}
},
"Resources" : {
"EC2Instance" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"InstanceType" : { "Ref" : "InstanceType" },
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : { "Ref" : "KeyName" },
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }
}
},
"InstanceSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable SSH access via port 22",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : "22",
"ToPort" : "22",
"CidrIp" : { "Ref" : "SSHLocation"}
} ]
}
}
},
"Outputs" : {
"InstanceId" : {
"Description" : "InstanceId of the newly created EC2 instance",
"Value" : { "Ref" : "EC2Instance" }
},
"AZ" : {
"Description" : "Availability Zone of the newly created EC2 instance",
"Value" : { "Fn::GetAtt" : [ "EC2Instance", "AvailabilityZone" ] }
},
"PublicDNS" : {
"Description" : "Public DNSName of the newly created EC2 instance",
"Value" : { "Fn::GetAtt" : [ "EC2Instance", "PublicDnsName" ] }
},
"PublicIP" : {
"Description" : "Public IP address of the newly created EC2 instance",
"Value" : { "Fn::GetAtt" : [ "EC2Instance", "PublicIp" ] }
}
}
}
Example of CloudFormation template
import * as cdk from '@aws-cdk/core'
import * as ec2 from '@aws-cdk/aws-ec2'
export class CdkUbuntuEc2Stack extends cdk.Stack {
constructor (scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, {
env: {
account: process.env.CDK_DEPLOY_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEPLOY_REGION || process.env.CDK_DEFAULT_REGION
},
...props
})
const defaultVpc = ec2.Vpc.fromLookup(this, 'VPC', {
isDefault: true
})
const userData = ec2.UserData.forLinux()
userData.addCommands(
'apt-get update -y',
'apt-get install -y git awscli ec2-instance-connect',
'until git clone https://github.com/aws-quickstart/quickstart-linux-utilities.git; do echo "Retrying"; done',
'cd /quickstart-linux-utilities',
'source quickstart-cfn-tools.source',
'qs_update-os || qs_err',
'qs_bootstrap_pip || qs_err',
'qs_aws-cfn-bootstrap || qs_err',
'mkdir -p /opt/aws/bin',
'ln -s /usr/local/bin/cfn-* /opt/aws/bin/'
)
const machineImage = ec2.MachineImage.fromSSMParameter(
'/aws/service/canonical/ubuntu/server/focal/stable/current/amd64/hvm/ebs-gp2/ami-id',
ec2.OperatingSystemType.LINUX,
userData
)
const myVmSecurityGroup = new ec2.SecurityGroup(this, 'myVmSecurityGroup', {
vpc: defaultVpc
})
myVmSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'httpIpv4')
myVmSecurityGroup.addIngressRule(ec2.Peer.anyIpv6(), ec2.Port.tcp(80), 'httpIpv6')
const myVm = new ec2.Instance(this, 'myVm', {
// the type of instance to deploy (e.g. a 't2.micro')
instanceType: new ec2.InstanceType('t2.micro'),
// the id of the image to use for the instance
machineImage: machineImage,
// A reference to the object representing the VPC
// you want to deploy the instance into
vpc: defaultVpc,
// security group
securityGroup: myVmSecurityGroup,
// init script
init: ec2.CloudFormationInit.fromElements(
ec2.InitCommand.shellCommand('sudo apt-get install -y nginx')
)
// ... more configuration can go here
})
// this will print the URL to our web server as output
const webVmUrl = new cdk.CfnOutput(this, 'webVmUrl', {
value: `http://${myVm.instancePublicIp}/`,
description: 'The URL of our instance',
exportName: 'webVmUrl'
})
}
}
Example of a CDK stack (TypeScript)
๐ How do we migrate the data?
ย
๐ฅ How do we switch the traffic to the new infrastructure?
Update the "old" code-base to save every new file also to S3.
ย
Copy all the existing file to the S3 bucket (S3 sync).
AWS Database Migration service allows you to replicate all the data from the old database to the new one.
ย
It will also keep the 2 Databases in sync during the switch over!
Request a new certificate using AWS Certificate Manager (ACM).
ย
Can be validated by email or DNS.
ย
Point your DNS to the new Load Balancer in AWS!
Load Balancer | $ 24.24 |
EC2 (6 instances) | $ 85.87 |
ElastiCache Redis (3 instances) | $ 78.84 |
RDS PostgreSQL (3 instances) | $ 155.48 |
S3 (1TB) | $ 32.55 |
TOT | $ 376.98 |
โ Create an AWS Account
โ Select a tool for IaaC
โ Create and configure a VPC in a region (3 AZs, Public / Private subnets)
โ Create an S3 bucket
โ Update the old codebase to save every new file to S3
โ Copy all the existing files to S3
โ Spin up the database in RDS (Multi-AZ)
โ Migrate the data using Database Migration Service
โ Provision the ElastiCache Redis Cluster (Multi-AZ)
โ Create an AMI for the application
โ Create a security groups and an IAM policy for EC2
โ Make the application stateless
โ Create an health check endpoint
โ Create an autoscaling group to spin up the instances
โ Create a certificate in ACM
โ Provision an Application Load Balancer (public subnets)
โ Configure Https, Targets and Health Checks
โ Configure Route53
โ Traffic switch-over through DNS ๐ค
๐ Great guide to cloud migrations: 6 strategies for migrating applications to the cloud
โ๏ธ nodejsdp.link
By Luciano Mammino
How can you take an existing monolith to the cloud with very minimal effort? In this talk we will explore an architecture that can help you to achieve that while focusing on scalability and resilience.
Cloud developer, entrepreneur, fighter, butterfly maker! #nodejs #javascript - Author of https://www.nodejsdesignpatterns.com , Founder of https://fullstackbulletin.com