in apps-rendering/cdk/lib/mobile-apps-rendering.ts [34:144]
constructor(scope: App, id: string, props: AppsStackProps) {
super(scope, id, props);
const appName = 'mobile-apps-rendering';
const domainName = `${props.recordPrefix}.${props.appsRenderingDomain}`;
const hostedZone = HostedZone.fromHostedZoneAttributes(
this,
'HostedZone',
{
zoneName: props.appsRenderingDomain,
hostedZoneId: props.hostedZoneId,
},
);
const scalingTargetCpuUtilisation = props.targetCpuUtilisation;
const artifactBucketName = ssm.StringParameter.valueForStringParameter(
this,
'/account/services/artifact.bucket',
);
const userData = UserData.forLinux();
userData.addCommands(
`set -ev`,
`groupadd mapi`,
`useradd -r -m -s /usr/bin/nologin -g mapi ${appName}`,
`export App=${appName}`,
`export Stack=${this.stack}`,
`export Stage=${this.stage}`,
`export NODE_ENV=production`,
`aws s3 cp s3://${artifactBucketName}/${this.stack}/${this.stage}/${appName}/${appName}.zip /tmp`,
`mkdir -p /opt/${appName}`,
`unzip /tmp/${appName}.zip -d /opt/${appName}`,
`chown -R ${appName}:mapi /opt/${appName}`,
`mkdir -p /usr/share/${appName}/logs`,
`chown -R ${appName}:mapi /usr/share/${appName}`,
`ln -s /usr/share/${appName}/logs /var/log/${appName}`,
`chown -R ${appName}:mapi /var/log/${appName}`,
`export PM2_HOME="/usr/share/${appName}"`,
`export ASSETS_MANIFEST="/opt/${appName}/manifest.json"`,
`/usr/local/node/pm2 start --name ${appName} --uid ${appName} --gid mapi /opt/${appName}/server.js`,
`/opt/aws-kinesis-agent/configure-aws-kinesis-agent ${this.region} mobile-log-aggregation-${this.stage} '/var/log/${appName}/*'`,
`/usr/local/node/pm2 logrotate -u ${appName}`,
);
const appsRenderingApp = new GuEc2App(this, {
applicationPort: 3040,
app: 'mobile-apps-rendering',
access: {
scope: AccessScope.INTERNAL,
cidrRanges: [Peer.ipv4('10.0.0.0/8')],
},
instanceType: InstanceType.of(
InstanceClass.T4G,
props.instanceSize,
),
certificateProps: {
domainName,
hostedZoneId: props.hostedZoneId,
},
monitoringConfiguration: {
noMonitoring: true,
},
roleConfiguration: {
additionalPolicies: [
new GuAllowPolicy(this, 'GetParametersByPath', {
resources: ['*'],
actions: ['ssm:GetParametersByPath'],
}),
],
},
userData,
scaling: props.asgCapacity,
});
/**
* The default Node server keep alive timeout is 5 seconds
* @see https://nodejs.org/api/http.html#serverkeepalivetimeout
*
* This ensures that the load balancer idle timeout is less than the Node server keep alive
* timeout so that the Node app does not prematurely close the connection before the load
* balancer can accept the response.
* @see https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#connection-idle-timeout
*/
appsRenderingApp.loadBalancer.setAttribute(
'idle_timeout.timeout_seconds',
'4',
);
const asg = appsRenderingApp.autoScalingGroup;
asg.scaleOnCpuUtilization('CpuScalingPolicy', {
targetUtilizationPercent: scalingTargetCpuUtilisation,
});
const recordSet = new RecordSet(this, 'DnsRecord', {
recordType: RecordType.CNAME,
target: RecordTarget.fromValues(
appsRenderingApp.loadBalancer.loadBalancerDnsName,
),
zone: hostedZone,
recordName: props.recordPrefix,
ttl: Duration.hours(1),
});
const defaultChild = recordSet.node.defaultChild as CfnElement;
defaultChild.overrideLogicalId('DnsRecord');
}