Skip to content

Commit

Permalink
Blog post 4 (#12)
Browse files Browse the repository at this point in the history
* Blog post 4
- Initial changes

* Blog post 4
- Initial changes

* test

* Revert "test"

This reverts commit 6ae078c.

* Blog post 4
- Added documentation
- Fix some code

* Blog post 4
- Improve deployment

* Blog post 4
- Improve deployment
  • Loading branch information
jaimenavarro authored Jul 5, 2024
1 parent e21c368 commit f9ec572
Show file tree
Hide file tree
Showing 23 changed files with 5,338 additions and 0 deletions.
12 changes: 12 additions & 0 deletions infrastructure/blog_post_4/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
cdk.context.json

# IntelliJ
.idea/**
6 changes: 6 additions & 0 deletions infrastructure/blog_post_4/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
6 changes: 6 additions & 0 deletions infrastructure/blog_post_4/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": true,
"trailingComma": "none",
"singleQuote": true,
"printWidth": 120
}
152 changes: 152 additions & 0 deletions infrastructure/blog_post_4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Guide to deploy this infrastructure on AWS

## CDK Deployment Overview
The accompanying diagram illustrates the architecture of our deployed infrastructure, showcasing the relationships between key components. While the CDK stacks deploy infrastructure inside AWS Cloud, for external components like the DNS provider (ClouDNS), we will need to execute manual steps, highlighted in the following diagram
![CDK Deployment Overview](doc/images/CDK_Deployment_Overview.jpeg)

## Step 0 - Install dependencies
- AWS account credentials
- Install AWS CLI https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
- Get AWS credentials https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html
- NodeJS installation https://nodejs.org/en/download/

## Step 1 - Confirm AWS credentials
After getting AWS credentials, you will need to make sure that you pick the right ones(if you have more than one):
```bash
aws configure list-profiles
export AWS_DEFAULT_PROFILE=xxxxxxxxxx
export AWS_PROFILE=xxxxxxxxxx
```

Confirm AWS credentials are working by running the following commands:
```bash
aws configure list
aws sts get-caller-identity
```

## Step 2 - Configuration
> **Warning** Update the default public DNS domain (_subdomain-**2**.subdomain-**1**.cloudns.ph_) with your own domain name.
In [ClouDNS](https://www.cloudns.net) set up the following:
* Create a free DNS Hosted Zone (Example case: subdomain-**xx**.cloudns.ph)

![img.png](doc/images/img.png)

In this GitHub repository, update the [configuration file](./config/environment.ts) with your own public domain name.
* DNS_ZONE_NAME: "_subdomain-2.subdomain-**xx**.cloudns.ph_"

```javascript
export const AppConfig = {
VPC_NAME: 'vpc-web-container',
CLUSTER_NAME: 'fargate-cluster-web-container',
APP_NAME: 'app-region-evacuation',
DNS_ZONE_NAME: 'subdomain-2.subdomain-1.cloudns.ph',
INTERNAL_DNS: 'web-container',
DOCKER_IMAGE: 'jaimenavarro/web-container'
};
```

## Step 3 - Build
These are the following steps to build the project:
* Make sure you are in the right folder
```bash
cd infrastructure/blog_post_4
```
* Install javascript dependencies
```bash
npm install -dd
npm run build -dd
```

## Step 4 - Setting up AWS CloudFormation
* It sets up the necessary AWS resources and configurations required to deploy your CDK stacks in CloudFormation.
```bash
npx cdk bootstrap --region us-east-1
npx cdk bootstrap --region us-west-2
```

* AWS CDK Synth the project
```bash
npx cdk context --clear
npx cdk synth
```



## Step 5 - Deploy First CDK Stage
This command will deploy the basic infrastructure in region us-east-1, us-west-2:
* Creates a VPC that spans a whole region. It will automatically divide the provided VPC CIDR range, and create public and private subnets per Availability Zone. Network routing for the public subnets will be configured to allow outbound access directly via an Internet Gateway. Network routing for the private subnets will be configured to allow outbound access via a set of resilient NAT Gateways (one per AZ).
* Fargate cluster
* Route53 DNS public zone

```bash
npx cdk deploy stage-1/* --require-approval never
```
You can review the status of your CDK deployment from AWS console
* [CloudFormation us-east-1](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1)
* [CloudFormation us-west-2](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-west-2)

## Step 6 - Configure ClouDNS with the NS records from AWS route53
Go to [AWS Route 53](https://us-east-1.console.aws.amazon.com/route53/v2/hostedzones) the hosted zone created in the previous step.
Copy the NS records related to the authoritative DNS servers.
* Example values:
```
ns-231.awsdns-28.com.
ns-1965.awsdns-53.co.uk.
ns-1055.awsdns-03.org.
ns-724.awsdns-26.net.
```

Go to your account in [ClouDNS](https://www.cloudns.net/) and open your free DNS zone (For our example was subdomain-**xx**.cloudns.ph). We will add four NS records, one for each authoritative DNS servers
* Type: NS record
* Host: subdomain-2.subdomain-**xx**.cloudns.ph
* Points to: ns-231.awsdns-28.com

![img_1.png](doc/images/img_1.png)
![img_2.png](doc/images/img_2.png)

You can confirm that the NS records are working fine by using the following online tool. **Keep in mind to use your own domain name. (For our example was subdomain-**xx**.cloudns.ph)**
* https://dnschecker.org/#NS/subdomain-2.subdomain-1.cloudns.ph
![img_3.png](doc/images/img_3.png)

## Step 7 - Update CDK Context with the new resources
After creating basic infrastructure in the previous step we need to recreate the file cdk.context.json, which keeps information of the infrastructure in AWS, for that purpose we will use the following commands:
```bash
npx cdk context --clear
npx cdk synth
```

## Step 8 - Deploy Second CDK Stage
In this step, we will deploy web container tasks (web-server-container) in Fargate Cluster and its related infrastructure in (us-east-1, us-west-2):
* Deploys the web container tasks in Fargate Cluster
* Creates a public certificate in ACM. (Step 6 needs to be working)
* Creates Application Load Balancer with the previously created certificate
* Creates Global Accelerator and the public DNS record to reach it.

```bash
npx cdk deploy stage-2/* --require-approval never
```
You can review the status of your CDK deployment from AWS console:
* [CloudFormation us-east-1](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1)
* [CloudFormation us-west-2](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-west-2)

## Validations Steps
You can use the following online resources to confirm that your public endpoint is available and the certificate is valid.
> **Warning** Update the following domains with your own domain name.
* Online DNS validation tool:
* https://dnschecker.org/#A/global-accelerator.subdomain-2.subdomain-1.cloudns.ph
* Online SSL/TLS validation tool:
* https://www.sslshopper.com/ssl-checker.html#hostname=https://global-accelerator.subdomain-2.subdomain-1.cloudns.ph/
* HTTP Client validation:
```bash
curl -v https://global-accelerator.subdomain-2.subdomain-1.cloudns.ph
```

## Remove all resources from your AWS account
In order to remove all the resources go to your [cloudformation console](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1) and delete the stacks in the inverse order:
1. Remove the DNS records with type CNAME in [Route 53](https://us-east-1.console.aws.amazon.com/route53/v2/hostedzones?region=us-east-1#) created by Certificates Manager
![img_4.png](doc/images/img_4.png)
2. Execute the following commands:
```bash
npx cdk destroy stage-2/* stage-1/*
```
20 changes: 20 additions & 0 deletions infrastructure/blog_post_4/bin/app-region-evacuation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { DeployServiceAndGlobalAcceleratorStage } from '../lib/stage-2/stages/deploy-service-and-global-accelerator-stage';
import { DeployBasicInfrastructureStage } from '../lib/stage-1/stages/deploy-basic-infrastructure-stage';

const app = new cdk.App();

new DeployBasicInfrastructureStage(app, 'stage-1', {
env: {
account: process.env.CDK_DEPLOY_ACCOUNT ?? process.env.CDK_DEFAULT_ACCOUNT
},
stage: 'stage-1'
});

new DeployServiceAndGlobalAcceleratorStage(app, 'stage-2', {
env: {
account: process.env.CDK_DEPLOY_ACCOUNT ?? process.env.CDK_DEFAULT_ACCOUNT
},
stage: 'stage-2'
});
43 changes: 43 additions & 0 deletions infrastructure/blog_post_4/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app-region-evacuation.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false
}
}
16 changes: 16 additions & 0 deletions infrastructure/blog_post_4/config/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Shared values that are the same across all environments
export const AppConfig = {
VPC_NAME: 'vpc-web-container',
CLUSTER_NAME: 'fargate-cluster-web-container',
APP_NAME: 'app-region-evacuation',
DNS_ZONE_NAME: 'subdomain-2.subdomain-1.cloudns.ph',
INTERNAL_DNS: 'web-container',
DOCKER_IMAGE: 'jaimenavarro/web-container',
LATENCY_DNS_RECORD: 'latency-endpoint',
GLOBAL_ACCELERATOR_DNS_RECORD: 'global-accelerator',
};

// Regions where the app will be deployed (stage-1 and stage-2)
export const TargetRegions = ['us-east-1', 'us-west-2'];

export const PRIMARY_REGION = 'us-east-1';
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infrastructure/blog_post_4/doc/images/img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infrastructure/blog_post_4/doc/images/img_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infrastructure/blog_post_4/doc/images/img_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infrastructure/blog_post_4/doc/images/img_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infrastructure/blog_post_4/doc/images/img_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions infrastructure/blog_post_4/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
testEnvironment: 'node',
roots: ['<rootDir>/test'],
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest'
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as route53 from 'aws-cdk-lib/aws-route53'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as ecs from 'aws-cdk-lib/aws-ecs'
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { AppConfig, PRIMARY_REGION } from '../../../config/environment';

/**
* Stack for deploying the basic infrastructure in one region
* 1. Create Route53 Public Hosted Zone
* 2. Create VPC and ECS Cluster
*/
export class DeployBasicInfrastructureStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
// It's a global resource, so we only need to create once
if (props?.env?.region === PRIMARY_REGION) {
this.createRoute53PublicHostedZone();
}
// Create VPC in each region
const vpc = new ec2.Vpc(this, 'MyVpc', { vpcName: AppConfig.VPC_NAME, maxAzs: 2 });
// Create ECS Cluster in each region
new ecs.Cluster(this, 'Cluster', { clusterName: AppConfig.CLUSTER_NAME, vpc });
}

/**
* Create Route53 Public Hosted Zone
*/
private createRoute53PublicHostedZone() {
new route53.PublicHostedZone(this, 'create-dns-zone.cloudns.ph', {
zoneName: AppConfig.DNS_ZONE_NAME,
comment: 'Web container public hosted zone'
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Stage, StageProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { AppConfig, TargetRegions } from '../../../config/environment';
import { DeployBasicInfrastructureStack } from '../stacks/deploy-basic-infrastructure-stack';

interface DeployBasicInfrastructureStageProps extends StageProps {
stage: string;
}

/**
* Stage for deploying the basic infrastructure in all the regions
* 1. Create Route53 Public Hosted Zone
* 2. Create VPC and ECS Cluster in each region
*/
export class DeployBasicInfrastructureStage extends Stage {
constructor(scope: Construct, id: string, props: DeployBasicInfrastructureStageProps) {
super(scope, id, props);
for (let region of TargetRegions) {
new DeployBasicInfrastructureStack(this, `${AppConfig.APP_NAME}-basic-infrastructure-${region}`, {
terminationProtection: false,
env: {
region: region,
account: process.env.CDK_DEPLOY_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT
},
stackName: `${AppConfig.APP_NAME}-basic-infrastructure`
});
}
}
}
Loading

0 comments on commit f9ec572

Please sign in to comment.