diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..059d7c5 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +PORT=5000 + +PROJECT_NAME=demo +TABLE_NAME=sample_table + +AWS_DEFAULT_ACCOUNT=975050344965 +AWS_DEFAULT_REGION=ap-northeast-1 + +AWS_FARGATE_CONTAINER_CPU=512 +AWS_FARGATE_CONTAINER_MEMORY=1024 diff --git a/README.md b/README.md index 133a8eb..d7b5314 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,16 @@ docker build -t backend_image . docker run -p 3000:3000 --name backend_app backend_image ``` +- Deployment + +``` +npm run build +cdk bootstrap +cdk synth +cdk deploy --require-approval never +cdk destroy -f +``` + ## Useful commands - `npm run build` compile typescript to js diff --git a/bin/aws-cdk-typescript.ts b/bin/aws-cdk-typescript.ts deleted file mode 100644 index 9606367..0000000 --- a/bin/aws-cdk-typescript.ts +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env node -import 'source-map-support/register'; -import * as cdk from 'aws-cdk-lib'; -import { AwsCdkTypescriptStack } from '../lib/aws-cdk-typescript-stack'; - -const app = new cdk.App(); -new AwsCdkTypescriptStack(app, 'AwsCdkTypescriptStack', { - /* If you don't specify 'env', this stack will be environment-agnostic. - * Account/Region-dependent features and context lookups will not work, - * but a single synthesized template can be deployed anywhere. */ - - /* Uncomment the next line to specialize this stack for the AWS Account - * and Region that are implied by the current CLI configuration. */ - // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, - - /* Uncomment the next line if you know exactly what Account and Region you - * want to deploy the stack to. */ - // env: { account: '123456789012', region: 'us-east-1' }, - - /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ -}); \ No newline at end of file diff --git a/bin/aws-cdk.ts b/bin/aws-cdk.ts new file mode 100644 index 0000000..ecebe4b --- /dev/null +++ b/bin/aws-cdk.ts @@ -0,0 +1,10 @@ +#!/usr/bin/env node +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { FargateStack } from "../lib/fargate"; +import env from "../backend/src/config"; + +const app = new cdk.App(); +new FargateStack(app, "FargateStack", { + env: { account: env.AWS_DEFAULT_ACCOUNT, region: env.AWS_DEFAULT_REGION }, +}); diff --git a/cdk.context.json b/cdk.context.json new file mode 100644 index 0000000..af80105 --- /dev/null +++ b/cdk.context.json @@ -0,0 +1,7 @@ +{ + "availability-zones:account=975050344965:region=ap-northeast-1": [ + "ap-northeast-1a", + "ap-northeast-1c", + "ap-northeast-1d" + ] +} diff --git a/lib/aws-cdk-typescript-stack.ts b/lib/aws-cdk-typescript-stack.ts deleted file mode 100644 index 49ec09d..0000000 --- a/lib/aws-cdk-typescript-stack.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as cdk from 'aws-cdk-lib'; -import { Construct } from 'constructs'; -// import * as sqs from 'aws-cdk-lib/aws-sqs'; - -export class AwsCdkTypescriptStack extends cdk.Stack { - constructor(scope: Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); - - // The code that defines your stack goes here - - // example resource - // const queue = new sqs.Queue(this, 'AwsCdkTypescriptQueue', { - // visibilityTimeout: cdk.Duration.seconds(300) - // }); - } -} diff --git a/lib/fargate.ts b/lib/fargate.ts new file mode 100644 index 0000000..23c4a85 --- /dev/null +++ b/lib/fargate.ts @@ -0,0 +1,74 @@ +import env from "../backend/src/config"; +import * as cdk from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { + aws_dynamodb as dynamodb, + aws_ec2 as ec2, + aws_ecs as ecs, + aws_ecs_patterns as ecs_patterns, +} from "aws-cdk-lib"; + +export class FargateStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // Create VPC + const vpc = new ec2.Vpc(this, `${env.PROJECT_NAME}_vpc`, { + maxAzs: 2, + }); + + // Create fargate cluster + const cluster = new ecs.Cluster(this, `${env.PROJECT_NAME}_cluster`, { + vpc: vpc, + }); + + // Create dynamodb table + const table = new dynamodb.Table(this, `${env.TABLE_NAME}`, { + partitionKey: { + name: "Id", + type: dynamodb.AttributeType.STRING, + }, + billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + // Create ecs fargate service + const fargateService = + new ecs_patterns.ApplicationLoadBalancedFargateService( + this, + `${env.PROJECT_NAME}_ecs`, + { + cluster: cluster, + cpu: Number(env.AWS_FARGATE_CONTAINER_CPU), + memoryLimitMiB: Number(env.AWS_FARGATE_CONTAINER_MEMORY), + desiredCount: 1, + taskImageOptions: { + image: ecs.ContainerImage.fromAsset("backend/"), + environment: { + PORT: `${env.PORT}`, + AWS_DYNAMODB_TABLE_NAME: table.tableName, + }, + containerPort: Number(env.PORT), + }, + } + ); + + // Health check + fargateService.targetGroup.configureHealthCheck({ + path: "/healthcheck", + }); + + // Grant ECS task permissions to access the DynamoDB table + table.grantReadWriteData(fargateService.taskDefinition.taskRole); + + // Output load balancer url + new cdk.CfnOutput(this, "LoadBalancerDNS", { + value: fargateService.loadBalancer.loadBalancerDnsName, + }); + + // Output the DynamoDB table name + new cdk.CfnOutput(this, "DynamoDBTableName", { + value: table.tableName, + }); + } +}