Introduction#

GitHub this note shows basically how to develop a high performance backend app using auto scaling, application load balancer, and aurora

Architectrure#

alb and aurora

Network Stack#

Let create a new vpc

const vpc = new aws_ec2.Vpc(this, 'VpcForAuroraDemo', {
vpcName: props.vpcName,
cidr: props.cidir,
enableDnsHostnames: true,
enableDnsSupport: true,
subnetConfiguration: [
{
cidrMask: 24,
name: 'PublicSubnet',
subnetType: aws_ec2.SubnetType.PUBLIC
},
{
cidrMask: 24,
name: 'PrivateSubnet',
subnetType: aws_ec2.SubnetType.PRIVATE_ISOLATED
},
{
cidrMask: 24,
name: 'PrivateSubnetWithNat',
subnetType: aws_ec2.SubnetType.PRIVATE_WITH_NAT
}
]
})

Aurora Cluster#

Let create a security group for aurora

const sg = new aws_ec2.SecurityGroup(this, 'SecurityGroupForSsmEndpoint', {
vpc,
description: 'Allow port 443 from private instance',
allowAllOutbound: true
})
sg.addIngressRule(
aws_ec2.Peer.anyIpv4(),
aws_ec2.Port.tcp(3306),
'allow HTTPS from private ec2 '
)

aurora cluster: single AZ, single master (write/read). There are advanced options for high performance multi-master and multi-az

const cluster = new aws_rds.DatabaseCluster(this, 'IcaDatabase', {
removalPolicy: RemovalPolicy.DESTROY,
defaultDatabaseName: props.dbName,
engine: aws_rds.DatabaseClusterEngine.auroraMysql({
version: aws_rds.AuroraMysqlEngineVersion.VER_2_08_1
}),
credentials: aws_rds.Credentials.fromGeneratedSecret('admin'),
instanceProps: {
instanceType: aws_ec2.InstanceType.of(
aws_ec2.InstanceClass.BURSTABLE2,
aws_ec2.InstanceSize.SMALL
),
vpcSubnets: {
subnetType: aws_ec2.SubnetType.PUBLIC
},
vpc,
securityGroups: [sg]
},
deletionProtection: false,
// 1 - mean single AZ
// 2+ mean multil AZ, single master, multiple read replicas
// engine mode multi-master - cfnDBCluster
instances: 1
})

Load Balancer#

Let create a iam role for EC2 to download from S3, access SSM, and Secret Mangement

const role = new aws_iam.Role(this, `RoleForEc2AsgToAccessSSM`, {
roleName: `RoleForEc2AsgToAccessSSM-${this.region}`,
assumedBy: new aws_iam.ServicePrincipal('ec2.amazonaws.com')
})

add policy for s3 to download userData-web and read db credentials from secrete maanger

role.attachInlinePolicy(
new aws_iam.Policy(this, `PolicyForEc2AsgToReadS3`, {
policyName: `PolicyForEc2AsgToReadS3-${this.region}`,
statements: [
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions: ['s3:*'],
resources: ['arn:aws:s3:::haimtran-workspace/*']
}),
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions: ['secretsmanager:*'],
resources: ['*']
})
]
})
)

policy for system manager connection

role.addManagedPolicy(
aws_iam.ManagedPolicy.fromManagedPolicyArn(
this,
`PolicyForEc2AsgToAccessSSM-${this.region}`,
'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'
)
)

auto scaling group

const asg = new aws_autoscaling.AutoScalingGroup(this, 'ASG', {
vpc,
instanceType: aws_ec2.InstanceType.of(
aws_ec2.InstanceClass.T2,
aws_ec2.InstanceSize.SMALL
),
machineImage: new aws_ec2.AmazonLinuxImage({
generation: aws_ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
edition: aws_ec2.AmazonLinuxEdition.STANDARD
}),
minCapacity: 2,
maxCapacity: 3,
role: role,
vpcSubnets: {
subnets: [
aws_ec2.Subnet.fromSubnetId(
this,
'PrivateSubnetWithNat1',
privateSubnetIds[2]
),
aws_ec2.Subnet.fromSubnetId(
this,
'PrivateSubnetWithNat2',
privateSubnetIds[3]
)
]
}
})
asg.addUserData(fs.readFileSync('./lib/script/user-data.sh', 'utf8'))

application load balancer

const alb = new aws_elasticloadbalancingv2.ApplicationLoadBalancer(
this,
'ALB',
{
vpc,
internetFacing: true
}
)

listen port 80

const listener = alb.addListener('Listener', {
port: 80
})
listener.addTargets('Target', {
port: 80,
targets: [asg]
})
listener.connections.allowDefaultPortFromAnyIpv4('Open to the world')
asg.scaleOnRequestCount('AmodestLoad', {
targetRequestsPerMinute: 60
})

User Data#

userdata-1 which is a simple web downloaded from s3

#!/bin/bash
cd ~
mkdir web
cd web
aws s3 cp s3://haimtran-workspace/aurora-web.zip .
unzip aurora-web.zip
sudo python3 -m pip install -r requirements.txt
sudo python3 app.py

userdata-2 which is a simple web downloaded from github

#!/bin/bash
# kill -9 $(lsof -t -i:8080)
# secrete id
export SECRET_ID=aurora-secrete-name
# secret region
export REGION=ap-southeast-1
# download vim configuration
wget -O ~/.vimrc https://raw.githubusercontent.com/cdk-entest/basic-vim/main/.vimrc
# download web app
wget https://github.com/cdk-entest/vpc-alb-asg-aurora-demo/archive/refs/heads/master.zip
unzip master.zip
cd vpc-alb-asg-aurora-demo-master/
# install pip
python3 -m ensurepip --upgrade
# install dependencies in requirements.txt
python3 -m pip install -r requirements.txt
# run the flask app
cd web-app
python3 -m app

Database Connection#

Let get DB credentials from secret management

# sm client
secrete_client = boto3.client('secretsmanager',region_name=REGION)
# get secret string
secret = secrete_client.get_secret_value(SecretId=SECRET_ID)

In this simple case single master (read/write), use the master (read/write) endpoint for both read and write. If there are multiple read replica, can use the -ro- read only endpoints for higher preformance read. connector

conn = mysql.connector.connect(
host=secret_dic['host'],
user=secret_dic['username'],
port=secret_dic['port'],
password=secret_dic['password'],
database=secret_dic['dbname']
)

create table

# cursor
cur = conn.cursor()
# drop table if exists
drop = "DROP TABLE IF EXISTS employees"
cur.execute(drop)
# create table
employee_table = (
"CREATE TABLE employees ("
" id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, "
" name VARCHAR(30) DEFAULT '' NOT NULL, "
" age TEXT, "
" time TEXT, "
"PRIMARY KEY (id))"
)
cur.execute(employee_table)

query table

stmt_select = "SELECT id, name, age, time FROM employees ORDER BY id"
cur.execute(stmt_select)
# parse
for row in cur.fetchall():
print(row)

Previous Version#

aws_devops-ica drawio

Reference#