Introduction#
GitHub shows basic of
- Create a vpc
- Create an ec2 in a public subnet
- Run a web in the ec2 port 80 and userdata
- Setup Vim for linux and remote access for window
- Add cloudwatch to monitor and stop idle instances
Architecture#
CDK Stack#
create a vpc
const vpc = new aws_ec2.Vpc(this, 'VpcWithoutNat', {vpcName: props.vpcName,cidr: props.cidr,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}]})
add vpc endpoints
// add s3 interface endpointvpc.addGatewayEndpoint('S3VpcEndpoint', {service: aws_ec2.GatewayVpcEndpointAwsService.S3})// add vpc endpoint ssmvpc.addInterfaceEndpoint('VpcInterfaceEndpointSSM', {service: aws_ec2.InterfaceVpcEndpointAwsService.SSM})
create a security group
const sg = new aws_ec2.SecurityGroup(this, 'SecurityGroupForPubEc2', {vpc: props.vpc,securityGroupName: 'SGForPubEc2',description: 'Allow 80',allowAllOutbound: true})sg.addIngressRule(aws_ec2.Peer.anyIpv4(),aws_ec2.Port.tcp(80),'allow port 80 http')
create a role for the ec2
// role for ec2const role = new aws_iam.Role(this, 'RoleForPubEc2', {roleName: 'RoleForPubEc2',assumedBy: new aws_iam.ServicePrincipal('ec2.amazonaws.com')})role.addToPolicy(new aws_iam.PolicyStatement({effect: Effect.ALLOW,resources: ['*'],actions: ['s3:*']}))role.addManagedPolicy(aws_iam.ManagedPolicy.fromManagedPolicyArn(this,'PolicySSMMangerAccessS3','arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'))
create an ec2 in a public subnet
const publicEc2 = new aws_ec2.Instance(this, 'PubEc2Instance', {vpc: props.vpc,role: role,instanceName: 'PubEc2Instance',instanceType: aws_ec2.InstanceType.of(aws_ec2.InstanceClass.T3,aws_ec2.InstanceSize.MICRO),machineImage: new aws_ec2.AmazonLinuxImage({generation: aws_ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,edition: aws_ec2.AmazonLinuxEdition.STANDARD}),securityGroup: sg,vpcSubnets: {subnetType: aws_ec2.SubnetType.PUBLIC},allowAllOutbound: true})
add user data from commands
let command = `export USER_NAME=${name} \n`let text = fs.readFileSync('./lib/user-data.sh', 'utf8')ec2.addUserData(command.concat(text))
add user data from file
publicEc2.addUserData(fs.readFileSync('./lib/user-data.sh', 'utf8'))
Multiple EC2#
using a loop to create multiple EC2 instances
props.instanceNames.map(name => {let ec2 = new aws_ec2.Instance(this, name, {vpc: vpc,vpcSubnets: {subnetType: aws_ec2.SubnetType.PUBLIC},role: role,securityGroup: sg,instanceName: name,instanceType: aws_ec2.InstanceType.of(aws_ec2.InstanceClass.T3,aws_ec2.InstanceSize.MICRO),machineImage: new aws_ec2.AmazonLinuxImage({generation: aws_ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,edition: aws_ec2.AmazonLinuxEdition.STANDARD})})// userdata textlet command = `export USER_NAME=${name} \n`let text = fs.readFileSync('./lib/user-data.sh', 'utf8')// add user dataec2.addUserData(command.concat(text))})
SSH Username and Pass#
sudo passwd ec2-user
the enable ssh password in sshd_config file
sudo vim /etc/ssh/sshd_config
edit
PasswordAuthentication yes
and optionally
PermitRootLogin yes
finally need to reset ssh service
sudo sshd restart
Basic Vim#
create a vimrc file at
vim ~/.vim/vimrc
add very basic configuration for vim
" tab widthset tabstop=2set shiftwidth=2set softtabstop=2set expandtabset cindentset autoindentset smartindentset mouse=aset hlsearchset showcmdset titleset expandtabset incsearch" line numberset numberhi CursorLineNr cterm=None" highlight current lineset cursorlinehi CursorLine cterm=NONE ctermbg=23 guibg=Grey40" change cursor between modeslet &t_SI = "\e[6 q"let &t_EI = "\e[2 q"" netrw wsizelet g:netrw_liststyle=3let g:netrw_keepdir=0let g:netrw_winsize=30map <C-a> : Lexplore<CR>" per default, netrw leaves unmodified buffers open. This autocommand" deletes netrw's buffer once it's hidden (using ':q;, for example)autocmd FileType netrw setl bufhidden=delete " or use :qa!" these next three lines are for the fuzzy search:set nocompatible "Limit search to your projectset path+=** "Search all subdirectories and recursivelyset wildmenu "Shows multiple matches on one line" highlight syntaxset re=0syntax on
Remote Desktop Connect Window#
Install microsoft remote desktop (app for mac).
Then download the RDP file from aws console
Retrieve the window password by uploading the key pair
CloudWatch to Auto Stop EC2#
stop when an instance is idle
const alarmStopIdleEc2 = new aws_cloudwatch.Alarm(this,'CloudWatchAlarmStopIdleEc2',{alarmName: 'CloudWatchAlarmStopIdleEc2',comparisonOperator: aws_cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,threshold: 0.99,// how many data points (evaluation periods)evaluationPeriods: 6,// means 5 out of 6 data pointsdatapointsToAlarm: 4,metric: new aws_cloudwatch.Metric({namespace: 'AWS/EC2',metricName: 'CPUUtilization',statistic: 'Average',// average within 5 minutes to get 1 data pointperiod: Duration.minutes(5),dimensionsMap: {InstanceId: props.instanceId}})})
add action to stop when an instance is idle
alarmStopIdleEc2.addAlarmAction(new aws_cloudwatch_actions.Ec2Action(aws_cloudwatch_actions.Ec2InstanceAction.STOP))
stop when an instance is too hot
const alarmStopTooHotEc2 = new aws_cloudwatch.Alarm(this,'CloudWatchAlrmStopTooHotEc2',{alarmName: 'CloudWatchAlrmStopTooHotEc2',comparisonOperator:aws_cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,threshold: 5.0,// how many data points (evaluation periods)evaluationPeriods: 3,// means 1 out of 3 data pointsdatapointsToAlarm: 1,metric: new aws_cloudwatch.Metric({namespace: 'AWS/EC2',metricName: 'CPUUtilization',statistic: 'Average',// average within 1 minutes to get 1 data pointperiod: Duration.minutes(1),dimensionsMap: {InstanceId: props.instanceId}})})
add action to stop an instance when it is too hot
// stop too hot instancealarmStopTooHotEc2.addAlarmAction(new aws_cloudwatch_actions.Ec2Action(aws_cloudwatch_actions.Ec2InstanceAction.STOP))// send notificationalarmStopTooHotEc2.addAlarmAction(new aws_cloudwatch_actions.SnsAction(aws_sns.Topic.fromTopicArn(this, 'SnsTopic', props.topicArn)))
Troubleshooting#
- Place EC2 in a public subnet with allocated IP address
- Security Group open port 80, 22, 3389
- Role enable SSM