├── .DS_Store ├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cdk ├── .gitignore ├── .npmignore └── singleAccount │ ├── README.md │ ├── bin │ └── app.ts │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ ├── fargate-vpclink-stack.ts │ └── httpApi-stack.ts │ ├── package.json │ └── tsconfig.json ├── images ├── API_Gateway.png ├── Architecture-1.png ├── Architecture-2.png ├── Architecture.png ├── AuthorService-Preview.png ├── AuthorService.png ├── BookService-Preview.png ├── BookService.png ├── FargateVpclinkStack.png ├── HttpApiStack.png ├── cdk-deploy-1.png ├── cdk-deploy-2.png ├── cdk-diff.png ├── code-explorer.png ├── curl_author_api.png └── curl_book_api.png └── src ├── author-service ├── .DS_Store ├── .dockerignore ├── Dockerfile ├── healthcheck.html ├── index.js └── package.json └── book-service ├── .DS_Store ├── .dockerignore ├── Dockerfile ├── healthcheck.html ├── index.js └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | 10 | # Parcel default cache directory 11 | .parcel-cache 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building HTTP API-based services using Amazon API Gateway, AWS PrivateLink, AWS Fargate and AWS CDK 2 | ![Build Status](https://codebuild.us-east-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiSy9rWmVENzRDbXBoVlhYaHBsNks4OGJDRXFtV1IySmhCVjJoaytDU2dtVWhhVys3NS9Odk5DbC9lR2JUTkRvSWlHSXZrNVhYQ3ZsaUJFY3o4OERQY1pnPSIsIml2UGFyYW1ldGVyU3BlYyI6IlB3ODEyRW9KdU0yaEp6NDkiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master) 3 | [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/aws/aws-cdk) 4 | [![NPM version](https://badge.fury.io/js/aws-cdk.svg)](https://badge.fury.io/js/aws-cdk) 5 | [![PyPI version](https://badge.fury.io/py/aws-cdk.core.svg)](https://badge.fury.io/py/aws-cdk.core) 6 | [![NuGet version](https://badge.fury.io/nu/Amazon.CDK.svg)](https://badge.fury.io/nu/Amazon.CDK) 7 | 8 | ## Introduction 9 | Prior to the availability of AWS PrivateLink, services residing in a single Amazon VPC were connected to multiple Amazon VPCs either (1) through public IP addresses using each VPC’s internet gateway or (2) by private IP addresses using VPC peering. 10 | 11 | With AWS PrivateLink, service connectivity over Transmission Control Protocol (TCP) can be established from the service provider’s VPC (Producer) to the service consumer’s VPC (Consumer) in a secure and scalable manner. Tom Adamski has provided an [architecture](https://aws.amazon.com/blogs/networking-and-content-delivery/how-to-securely-publish-internet-applications-at-scale-using-application-load-balancer-and-aws-privatelink/) where he shows one way of using AWS PrivateLink along with ALB and NLBs to publish Internet applications at scale. Mani Chandrasekaran provided a [solution](https://aws.amazon.com/blogs/compute/access-private-applications-on-aws-fargate-using-amazon-api-gateway-privatelink/) where he uses API Gateway to expose applications running on AWS Fargate using REST APIs, but it uses NLB since ALB is not yet supported by this architecture. 12 | 13 | Our solution leverages the existing applications/ APIs running in AWS Fargate behind a Private ALB inside a VPC and proposes an architecture to expose these APIs securely through HTTP APIs using Amazon API Gateway and AWS PrivateLink. 14 | 15 | The target audience for this workshop are developers and architects who want to architect API based services using the existing applications running inside Amazon VPCs. 16 | 17 | ## Prerequisites 18 | In order to implement the instructions laid out in this post, you will need the following: 19 | - An [AWS account](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/) 20 | 21 | ## Architecture 22 | As shown in Fig 1, we shall create one AWS CDK application consisting of two AWS CDK stacks **FargateVpclinkStack** and **HttpApiStack**. Inside the FargateVpclinkStack, we deploy two NodeJS microservices (book-service and author-service) using Amazon Fargate within the Producer VPC. An internal load balancer distributes external incoming application traffic across these two microservices. In order to implement the private integration we create a VpcLink to encapsulate connections between API Gateway and these microservices. Inside the HttpApiStack, we create an Http Api that integrates with the Amazon Fargate microservices running inside the FargateVpclinkStack using the Vpclink and internal load balancer listener. 23 | 24 | ![Architecture](./images/Architecture-2.png) 25 | *Fig 1 - Architecture* 26 | 27 | Here are the steps we’ll be following to implement the above architecture: 28 | 29 | - Create and configure AWS Cloud9 environment 30 | - Build two sample microservices 31 | - Examine the CDK code for FargateVpclinkStack and HttpApiStack 32 | - Provision AWS resources using the CDK 33 | - Test the Http Api 34 | - Cleanup 35 | 36 | 37 | ## Create and configure AWS Cloud9 environment 38 | Log into the AWS Management Console and search for Cloud9 service in the search bar. 39 | 40 | Click Cloud9 and create an AWS Cloud9 environment in the us-west-2 region based on Amazon Linux 2. Create an IAM role for Cloud9 workspace as explained [here](https://www.eksworkshop.com/020_prerequisites/iamrole/). Attache the IAM role to your workspace as explained [here](https://www.eksworkshop.com/020_prerequisites/ec2instance/). Turn off the AWS managed temporary credentials of the Cloud9 environment as explained [here](https://www.eksworkshop.com/020_prerequisites/workspaceiam/). Open a new terminal in Cloud9 and install jq using the command: 41 | 42 | ```bash 43 | sudo yum install jq -y 44 | ``` 45 | 46 | ## Build two sample microservices 47 | 48 | ### Clone the GitHub repository 49 | 50 | Open a new terminal inside AWS Cloud9 IDE and run: 51 | 52 | git clone https://github.com/aws-samples/http-api-aws-fargate-cdk.git 53 | 54 | ### Build and test book-service locally 55 | 56 | Replace XXXXXXXXXXX with your AWS account id. 57 | 58 | ```bash 59 | cd ~/environment/http-api-aws-fargate-cdk/src/book-service 60 | npm install --save 61 | docker build -t book-service . 62 | docker tag book-service:latest \ 63 | XXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/book-service:latest 64 | 65 | docker run -p8080:80 book-service 66 | ``` 67 | Click the `Preview/Preview Running Application` and append api/books/health to the end of the url so that url looks like `https://XXXXXXXXXXXXXXXXXXX.vfs.cloud9.us-west-2.amazonaws.com/api/books/health` . Observe the response from the running book-service service as shown in Fig 2. 68 | 69 | ![Books Service Preview](./images/BookService-Preview.png) 70 | *Fig 2 - Books Service* 71 | 72 | Open a new terminal inside AWS Cloud9 IDE and run the following curl command: 73 | 74 | ```bash 75 | curl -s http://localhost:8080/api/books | jq 76 | ``` 77 | Observe the response as shown in the right hand pane of Fig 3. 78 | 79 | ![Books Service Preview](./images/BookService.png) 80 | *Fig 3 - Books Service* 81 | 82 | In order to avoid the port conflict later on, kill the book-service container by running: 83 | 84 | ```bash 85 | docker ps 86 | ``` 87 | Get the 'CONTAINER ID' from the previous command and then run: 88 | 89 | ```bash 90 | docker kill 91 | ``` 92 | 93 | ### Build and test author-service locally 94 | 95 | Replace XXXXXXXXXXX with your AWS account id. 96 | 97 | ```bash 98 | cd ~/environment/http-api-aws-fargate-cdk/src/author-service 99 | npm install --save 100 | docker build -t author-service . 101 | docker tag author-service:latest \ 102 | XXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/author-service:latest 103 | 104 | docker run -p8080:80 author-service 105 | ``` 106 | Click the `Preview/Preview Running Application` and append api/authors/health to the end of the url so that url looks like `https://XXXXXXXXXXXXXXXXXXX.vfs.cloud9.us-west-2.amazonaws.com/api/authors/health` . Observe the response from the running book-service service as shown in the right hand pane of Fig 4. 107 | 108 | ![Books Service Preview](./images/AuthorService.png) 109 | *Fig 4 - Authors Service* 110 | 111 | ### Create Amazon ECR repositories 112 | 113 | Amazon ECR registries host your container images in a highly available and scalable architecture, allowing you to deploy containers reliably for your applications. Each AWS account is provided with a single (default) Amazon ECR registry. 114 | 115 | Replace XXXXXXXXXXX with your AWS account id and using terminal inside AWS Cloud9 IDE run: 116 | 117 | ```bash 118 | aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin XXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com 119 | 120 | aws ecr create-repository \ 121 | --repository-name book-service \ 122 | --image-scanning-configuration scanOnPush=false \ 123 | --region us-west-2 124 | 125 | aws ecr create-repository \ 126 | --repository-name author-service \ 127 | --image-scanning-configuration scanOnPush=false \ 128 | --region us-west-2 129 | ``` 130 | 131 | ### Push images to Amazon ECR 132 | 133 | Replace XXXXXXXXXXX with your AWS account id and using terminal inside AWS Cloud9 IDE run: 134 | 135 | ```bash 136 | docker push XXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/book-service:latest 137 | 138 | docker push XXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/author-service:latest 139 | ``` 140 | ## Examine the CDK code for FargateVpclinkStack and HttpApiStack 141 | 142 | ### Creating AWS resources using the CDK 143 | 144 | We shall implement this architecture using an AWS CDK application comprising of two individual CDK stacks: 145 | 146 | - **FargateVpclinkStack** -- contains the Fargate and Vpclink resources. 147 | - **HttpApiStack** -- contains the Http Api integrated with Fargate services using Vpclink. 148 | 149 | Let us discuss these stacks one by one. 150 | 151 | ### **FargateVpclinkStack** 152 |

153 | 154 |

155 | 156 | Under the cdk/singleAccount/lib folder, open the fargate-vpclink-stack.ts file and let us explore the following different CDK constructs. 157 | 158 | Export Vpclink and ALB Listener: 159 | ```typescript 160 | public readonly httpVpcLink: cdk.CfnResource; 161 | public readonly httpApiListener: elbv2.ApplicationListener; 162 | ``` 163 | 164 | These two variables enable us to export the provisioned Vpclink along with the ALB Listener from **FargateVpclinkStack** stack so as to use these to create the Http Api in the **HttpApiStack** stack. 165 | 166 | **VPC:** 167 | 168 | This single line of code creates a ProducerVPC with two Public and two Private Subnets. 169 | ```typescript 170 | const vpc = new ec2.Vpc(this, "ProducerVPC"); 171 | ``` 172 | 173 | **ECS Cluster:** 174 | 175 | This creates an Amazon ECS cluster inside the ProducerVPC, we shall be running the two microservices inside this ECS cluster using AWS Fargate. 176 | 177 | ```typescript 178 | const cluster = new ecs.Cluster(this, "Fargate Cluster" , { 179 | vpc : vpc, 180 | }); 181 | ``` 182 | 183 | **Cloud Map Namespace:** 184 | 185 | AWS Cloud Map allows us to register any application resources, such as microservices, and other cloud resources, with custom names.Using AWS Cloud Map, we can define custom names for our application microservices, and it maintains the updated location of these dynamically changing microservices. 186 | 187 | ```typescript 188 | const dnsNamespace = new servicediscovery.PrivateDnsNamespace(this,"DnsNamespace",{ 189 | name: "http-api.local", 190 | vpc: vpc, 191 | description: "Private DnsNamespace for Microservices", 192 | } 193 | ); 194 | ``` 195 | **ECS Task Role:** 196 | ```typescript 197 | const taskrole = new iam.Role(this, 'ecsTaskExecutionRole', { 198 | assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com') 199 | }); 200 | 201 | taskrole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonECSTaskExecutionRolePolicy')); 202 | ``` 203 | 204 | **Task Definitions:** 205 | 206 | A task definition is required to run Docker containers in Amazon ECS, we shall create the task definitions (bookServiceTaskDefinition and authorServiceTaskDefinition) for the two microservices. 207 | ```typescript 208 | const bookServiceTaskDefinition = new ecs.FargateTaskDefinition(this, 209 | 'bookServiceTaskDef', { 210 | memoryLimitMiB: 512, 211 | cpu: 256, 212 | taskRole: taskrole 213 | }); 214 | 215 | const authorServiceTaskDefinition = new ecs.FargateTaskDefinition(this, 216 | 'authorServiceTaskDef', { 217 | memoryLimitMiB: 512, 218 | cpu: 256, 219 | taskRole: taskrole 220 | }); 221 | ``` 222 | **Log Groups:** 223 | 224 | Let us create two log groups bookServiceLogGroup and authorServiceLogGroup and the two associated log drivers. 225 | 226 | ```typescript 227 | const bookServiceLogGroup = new logs.LogGroup(this, "bookServiceLogGroup", { 228 | logGroupName: "/ecs/BookService", 229 | removalPolicy: cdk.RemovalPolicy.DESTROY 230 | }); 231 | 232 | const authorServiceLogGroup = new logs.LogGroup(this, "authorServiceLogGroup", { 233 | logGroupName: "/ecs/AuthorService", 234 | removalPolicy: cdk.RemovalPolicy.DESTROY 235 | }); 236 | 237 | const bookServiceLogDriver = new ecs.AwsLogDriver({ 238 | logGroup: bookServiceLogGroup, 239 | streamPrefix: "BookService" 240 | }); 241 | 242 | const authorServiceLogDriver = new ecs.AwsLogDriver({ 243 | logGroup: authorServiceLogGroup, 244 | streamPrefix: "AuthorService" 245 | }); 246 | ``` 247 | **ECR Repositories:** 248 | 249 | Amazon Elastic Container Registry (ECR) is a fully managed container registry that makes it easy to store, manage, share, and deploy container images containing the business logic of the microservices. Let us import the two repositories book-service and author-service that we created earlier using AWS CLI. 250 | ```typescript 251 | // Amazon ECR Repositories 252 | const bookservicerepo = ecr.Repository.fromRepositoryName(this, 253 | "bookservice", 254 | "book-service", 255 | ); 256 | 257 | const authorservicerepo = ecr.Repository.fromRepositoryName(this, 258 | "authorservice", 259 | "author-service", 260 | ); 261 | ``` 262 | **Task Containers:** 263 | 264 | We shall define a single container in each task definition. 265 | 266 | ```typescript 267 | const bookServiceContainer = 268 | bookServiceTaskDefinition.addContainer("bookServiceContainer", { 269 | image: ecs.ContainerImage.fromEcrRepository(bookservicerepo), 270 | logging: bookServiceLogDriver 271 | }); 272 | 273 | const authorServiceContainer = 274 | authorServiceTaskDefinition.addContainer("authorServiceContainer", { 275 | image: ecs.ContainerImage.fromEcrRepository(authorservicerepo), 276 | logging: authorServiceLogDriver 277 | }); 278 | 279 | bookServiceContainer.addPortMappings({ 280 | containerPort: 80 281 | }); 282 | 283 | authorServiceContainer.addPortMappings({ 284 | containerPort: 80 285 | }); 286 | ``` 287 | 288 | **Security Groups:** 289 | 290 | In order to control the inbound and outbound traffic to Fargate tasks, we shall create two security groups that act as a virtual firewall. 291 | ```typescript 292 | const bookServiceSecGrp = new ec2.SecurityGroup(this, "bookServiceSecurityGroup", { 293 | allowAllOutbound: true, 294 | securityGroupName: 'bookServiceSecurityGroup', 295 | vpc: vpc 296 | }); 297 | 298 | bookServiceSecGrp.connections.allowFromAnyIpv4(ec2.Port.tcp(80)); 299 | 300 | const authorServiceSecGrp = new ec2.SecurityGroup(this, "authorServiceSecurityGroup", { 301 | allowAllOutbound: true, 302 | securityGroupName: 'authorServiceSecurityGroup', 303 | vpc: vpc 304 | }); 305 | 306 | authorServiceSecGrp.connections.allowFromAnyIpv4(ec2.Port.tcp(80)); 307 | ``` 308 | 309 | **Fargate Services:** 310 | 311 | Let us create two ECS Fargate services (bookService & authorService) based on the task definitions created above. An Amazon ECS service enables you to run and maintain a specified number of instances of a task definition simultaneously in an Amazon ECS cluster. If any of your tasks should fail or stop for any reason, the Amazon ECS service scheduler launches another instance of your task definition to replace it in order to maintain the desired number of tasks in the service. 312 | 313 | ```typescript 314 | const bookService = new ecs.FargateService(this, 'bookService', { 315 | cluster: cluster, 316 | taskDefinition: bookServiceTaskDefinition, 317 | assignPublicIp: false, 318 | desiredCount: 2, 319 | securityGroup: bookServiceSecGrp, 320 | cloudMapOptions: { 321 | name: 'bookService' 322 | }, 323 | }); 324 | 325 | const authorService = new ecs.FargateService(this, 'authorService', { 326 | cluster: cluster, 327 | taskDefinition: authorServiceTaskDefinition, 328 | assignPublicIp: false, 329 | desiredCount: 2, 330 | securityGroup: authorServiceSecGrp, 331 | cloudMapOptions: { 332 | name: 'authorService' 333 | }, 334 | }); 335 | ``` 336 | 337 | **ALB:** 338 | 339 | The load balancer distributes incoming application traffic across multiple ECS services, in multiple Availability Zones. This increases the availability of your application. Let us add an Application Load Balancer. 340 | 341 | ```typescript 342 | const httpapiInternalALB = new elbv2.ApplicationLoadBalancer(this, 'httpapiInternalALB', { 343 | vpc: vpc, 344 | internetFacing: false, 345 | }); 346 | ``` 347 | **ALB Listener:** 348 | 349 | An ALB listener checks for connection requests from clients, using the protocol and port that we configure. 350 | 351 | ```typescript 352 | const httpapiListener = httpapiInternalALB.addListener('httpapiListener', { 353 | port: 80, 354 | // Default Target Group 355 | defaultAction: elbv2.ListenerAction.fixedResponse(200) 356 | }); 357 | ``` 358 | **Target Groups:** 359 | 360 | We shall create two target groups, bookServiceTargetGroup for bookService microservice and authorServiceTargetGroup for authorService microservice. 361 | 362 | ```typescript 363 | const bookServiceTargetGroup = httpapiListener.addTargets('bookServiceTargetGroup', { 364 | port: 80, 365 | priority: 1, 366 | healthCheck:{ 367 | path: '/api/books/health', 368 | interval: cdk.Duration.seconds(30), 369 | timeout: cdk.Duration.seconds(3) 370 | }, 371 | targets: [bookService], 372 | pathPattern: '/api/books*' 373 | }); 374 | 375 | const authorServiceTargetGroup = httpapiListener.addTargets('authorServiceTargetGroup', { 376 | port: 80, 377 | priority: 2, 378 | healthCheck:{ 379 | path: '/api/authors/health', 380 | interval : cdk.Duration.seconds(30), 381 | timeout: cdk.Duration.seconds(3) 382 | }, 383 | targets: [authorService], 384 | pathPattern: '/api/authors*' 385 | }); 386 | ``` 387 | ***VPC Link:*** 388 | 389 | It is easy to expose our HTTP/HTTPS resources behind an Amazon VPC for access by clients outside of the Producer VPC using API Gateway private integration. To extend access to our private VPC resources beyond the VPC boundaries, we can create an HTTP API with private integration for open access or controlled access. The private integration uses an API Gateway resource of VpcLink to encapsulate connections between API Gateway and targeted VPC resources. As an owner of a VPC resource, we are responsible for creating an Application Load Balancer in our Producer VPC and adding a VPC resource as a target of an Application Load Balancer's listener. As an HTTP API developer, to set up an HTTP API with the private integration, we are responsible for creating a VpcLink targeting the specified Application Load Balancer and then treating the VpcLink as an effective integration endpoint. Let us create a Vpclink based on the private subnets of the ProducerVPC. 390 | 391 | ```typescript 392 | //VPC Link 393 | this.httpVpcLink = new cdk.CfnResource(this, "HttpVpcLink", { 394 | type: "AWS::ApiGatewayV2::VpcLink", 395 | properties: { 396 | Name: "http-api-vpclink", 397 | SubnetIds: vpc.privateSubnets.map((m) => m.subnetId), 398 | }, 399 | }); 400 | ``` 401 | 402 | ### **HttpApiStack** 403 |

404 | 405 |

406 | 407 | Under the ~/environment/http-api-aws-fargate-cdk/cdk/singleAccount/lib folder, open the httpApi-stack.ts file and let us explore the following different CDK constructs. 408 | 409 | **Consumer VPC:** 410 | 411 | This single line of code creates a ConsumerVPC with two Public Subnets. 412 | 413 | ```typescript 414 | const vpc = new ec2.Vpc(this, "ConsumerVPC", { 415 | natGateways: 0, 416 | subnetConfiguration: [ 417 | { 418 | cidrMask: 24, 419 | name: "ingress", 420 | subnetType: ec2.SubnetType.PUBLIC, 421 | }, 422 | ], 423 | }); 424 | ``` 425 | **EC2 Instance:** 426 | 427 | ```typescript 428 | const instance = new ec2.Instance(this, "BastionHost", { 429 | instanceType: new ec2.InstanceType("t3.nano"), 430 | machineImage: amz_linux, 431 | vpc: vpc, 432 | securityGroup: bastionSecGrp, 433 | keyName: "ssh-key", 434 | }); 435 | ``` 436 | **HTTP API:** 437 | 438 | Let us create an Http Api based on a default stage. 439 | 440 | ```typescript 441 | const api = new apig.HttpApi(this, "http-api", { 442 | createDefaultStage: true, 443 | }); 444 | ``` 445 | **API Integration:** 446 | 447 | The following construct will integrate the Http Api with the backend microservices using the Vpclink and the Application Loadbalancer Listener. 448 | 449 | ```typescript 450 | const integration = new apig.CfnIntegration( 451 | this, "HttpApiGatewayIntegration", 452 | { 453 | apiId: api.httpApiId, 454 | connectionId: httpVpcLink.ref, 455 | connectionType: "VPC_LINK", 456 | description: "API Integration", 457 | integrationMethod: "ANY", 458 | integrationType: "HTTP_PROXY", 459 | integrationUri: httpApiListener.listenerArn, 460 | payloadFormatVersion: "1.0", 461 | } 462 | ); 463 | ``` 464 | **API Route:** 465 | 466 | Now let us create the Http Api proxy routes using the Api integration. 467 | 468 | ```typescript 469 | new apig.CfnRoute(this, "Route", { 470 | apiId: api.httpApiId, 471 | routeKey: "ANY /{proxy+}", 472 | target: `integrations/${integration.ref}`, 473 | }); 474 | ``` 475 | 476 | ## Provision AWS resources using the CDK 477 | 478 | ### Install AWS CDK 479 | 480 | The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework to model and provision your cloud application resources using familiar programming languages. If you would like to familiarize yourself the [CDKWorkshop](https://cdkworkshop.com/) is a great place to start. 481 | 482 | Using Cloud9 terminal and use the following commands: 483 | ```bash 484 | cd ~/environment/http-api-aws-fargate-cdk/cdk 485 | npm install -g aws-cdk@latest 486 | cdk --version 487 | ``` 488 | 489 | Take a note of the latest version that you install, at the time of writing this post it is 1.79.0. Open the package.json file in ~/environment/http-api-aws-fargate-cdk/cdk/singleaccount and replace the version “1.79.0” of the following modules with the latest version that you have installed above. 490 | 491 | ```typescript 492 | "@aws-cdk/assert": "1.79.0", 493 | "@aws-cdk/aws-apigatewayv2": "1.79.0", 494 | "@aws-cdk/core": "1.79.0", 495 | "@aws-cdk/aws-ec2": "1.79.0", 496 | "@aws-cdk/aws-ecr": "1.79.0", 497 | "@aws-cdk/aws-ecs": "1.79.0", 498 | "@aws-cdk/aws-elasticloadbalancingv2": "1.79.0", 499 | "@aws-cdk/aws-iam": "1.79.0", 500 | "@aws-cdk/aws-logs": "1.79.0", 501 | ``` 502 | ```bash 503 | cd ~/environment/http-api-aws-fargate-cdk/cdk/singleaccount 504 | npm install 505 | ``` 506 | This will install all the latest CDK modules under the node_modules directory. 507 | 508 | Let us now create an ssh key pair using AWS CLI: 509 | 510 | ```bash 511 | cd ~/environment/http-api-aws-fargate-cdk/ 512 | aws ec2 create-key-pair --region us-west-2 --key-name "ssh-key" | jq -r ".KeyMaterial" > ssh-key.pem 513 | chmod 400 ssh-key.pem 514 | ``` 515 | ```bash 516 | cd ~/environment/http-api-aws-fargate-cdk/cdk/singleAccount 517 | npm run build 518 | cdk bootstrap 519 | cdk synth FargateVpclinkStack 520 | cdk deploy --all 521 | ``` 522 | ![FargateVpclinkStack](./images/cdk-deploy-1.png) 523 | *Fig 5 - Deploy FargateVpclinkStack* 524 | 525 | At the prompt, enter y and CDK cli shall deploy the FargateVpclinkStack and will create 54 resources. 526 | 527 | ![HttpApiStack](./images/cdk-deploy-2.png) 528 | *Fig 6 - Deploy HttpApiStack* 529 | 530 | At the second prompt, enter y and CDK cli shall deploy the HttpApiStack and will create 21 resources. 531 | 532 | ## Test the Http Api 533 | 534 | Take a note of the EC2 ip address along with the Http Api endpoints of the Book Service and Author Service as shown in Fig 6. We shall connect to the EC2 instance in the consumer VPC to test the HTTP API. Using the Cloud9 terminal run the following commands: 535 | ```bash 536 | cd ~/environment/http-api-aws-fargate-cdk/ 537 | export EC2_IP_ADDRESS=x.x.x.x 538 | ssh -i ssh-key.pem ec2-user@$EC2_IP_ADDRESS 539 | sudo yum install jq -y 540 | export BOOK_API_URL=https://xxxxx.execute-api.us-west-2.amazonaws.com/api/books 541 | export AUTHOR_API_URL=https://xxxxx.execute-api.us-west-2.amazonaws.com/api/authors 542 | curl -s $BOOK_API_URL | jq 543 | ``` 544 | ![HttpApiStack](./images/curl_book_api.png) 545 | 546 | *Fig 7 - Book HttpApi based on BookService* 547 | 548 | ```bash 549 | curl -s $AUTHOR_API_URL | jq 550 | ``` 551 | ![HttpApiStack](./images/curl_author_api.png) 552 | 553 | *Fig 8 - Author HttpApi based on AuthorService* 554 | 555 | Fig 9 shows the integration of the Http Api with the backend Fargate microservices using Vpclink and the Application load balancer listener inside the AWS Management Console. 556 | 557 | ![HttpApiStack](./images/API_Gateway.png) 558 | *Fig 9 - Http Api integration with Vpclink* 559 | 560 | ## Cleanup 561 | 562 | To clean up the resources created by the CDK, run the following commands in a terminal of your Cloud9 instance: 563 | 564 | ```bash 565 | cd ~/environment/http-api-aws-fargate-cdk/cdk/singleAccount/ 566 | cdk destroy --all 567 | ``` 568 | 569 | At the prompt, enter y. 570 | 571 | To delete the ssh key pair, run the following command: 572 | 573 | ```bash 574 | aws ec2 delete-key-pair --region us-west-2 --key-name "ssh-key" 575 | ``` 576 | Log into the AWS Management Console and delete **book-service** and **author-service** repositories. Also delete the Cloud9 environment. 577 | 578 | ## Conclusion 579 | 580 | This workshop demonstrated how to architect HTTP API Based Services using Amazon API Gateway based on existing microservices running behind a Private Application load balancer inside Private VPCs using AWS PrivateLink. The benefit of this serverless architecture is that it takes away the overhead of having to manage underlying servers and helps reduce costs, as you only pay for the time in which your code executes. 581 | 582 | -------------------------------------------------------------------------------- /cdk/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | -------------------------------------------------------------------------------- /cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /cdk/singleAccount/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project! 2 | 3 | This is a blank project for TypeScript development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /cdk/singleAccount/bin/app.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { FargateVpclinkStack } from '../lib/fargate-vpclink-stack'; 5 | import { HttpApiStack } from '../lib/httpApi-stack'; 6 | 7 | const envUSA = { region: 'us-west-2' }; 8 | 9 | const app = new cdk.App(); 10 | const fargateVpclinkStack = new FargateVpclinkStack(app, 'FargateVpclinkStack', { env: envUSA }); 11 | new HttpApiStack(app, 'HttpApiStack', fargateVpclinkStack.httpVpcLink, fargateVpclinkStack.httpApiListener , { env: envUSA }); 12 | 13 | -------------------------------------------------------------------------------- /cdk/singleAccount/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/app.ts", 3 | "context": { 4 | "@aws-cdk/core:enableStackNameDuplicates": "true", 5 | "aws-cdk:enableDiffNoFail": "true", 6 | "@aws-cdk/core:stackRelativeExports": "true", 7 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 8 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 9 | "@aws-cdk/aws-kms:defaultKeyPolicies": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /cdk/singleAccount/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /cdk/singleAccount/lib/fargate-vpclink-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from "@aws-cdk/core"; 2 | import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2"; 3 | import * as ec2 from "@aws-cdk/aws-ec2"; 4 | import * as ecs from "@aws-cdk/aws-ecs"; 5 | import * as ecr from "@aws-cdk/aws-ecr"; 6 | import * as iam from "@aws-cdk/aws-iam"; 7 | import * as logs from "@aws-cdk/aws-logs"; 8 | import * as apig from "@aws-cdk/aws-apigatewayv2"; 9 | import * as servicediscovery from "@aws-cdk/aws-servicediscovery"; 10 | 11 | export class FargateVpclinkStack extends cdk.Stack { 12 | 13 | //Export Vpclink and ALB Listener 14 | public readonly httpVpcLink: cdk.CfnResource; 15 | public readonly httpApiListener: elbv2.ApplicationListener; 16 | 17 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 18 | super(scope, id, props); 19 | 20 | // VPC 21 | const vpc = new ec2.Vpc(this, "ProducerVPC"); 22 | 23 | // ECS Cluster 24 | const cluster = new ecs.Cluster(this, "Fargate Cluster", { 25 | vpc: vpc, 26 | }); 27 | 28 | // Cloud Map Namespace 29 | const dnsNamespace = new servicediscovery.PrivateDnsNamespace( 30 | this, 31 | "DnsNamespace", 32 | { 33 | name: "http-api.local", 34 | vpc: vpc, 35 | description: "Private DnsNamespace for Microservices", 36 | } 37 | ); 38 | 39 | // Task Role 40 | const taskrole = new iam.Role(this, "ecsTaskExecutionRole", { 41 | assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"), 42 | }); 43 | 44 | taskrole.addManagedPolicy( 45 | iam.ManagedPolicy.fromAwsManagedPolicyName( 46 | "service-role/AmazonECSTaskExecutionRolePolicy" 47 | ) 48 | ); 49 | 50 | // Task Definitions 51 | const bookServiceTaskDefinition = new ecs.FargateTaskDefinition( 52 | this, 53 | "bookServiceTaskDef", 54 | { 55 | memoryLimitMiB: 512, 56 | cpu: 256, 57 | taskRole: taskrole, 58 | } 59 | ); 60 | 61 | const authorServiceTaskDefinition = new ecs.FargateTaskDefinition( 62 | this, 63 | "authorServiceTaskDef", 64 | { 65 | memoryLimitMiB: 512, 66 | cpu: 256, 67 | taskRole: taskrole, 68 | } 69 | ); 70 | 71 | // Log Groups 72 | const bookServiceLogGroup = new logs.LogGroup(this, "bookServiceLogGroup", { 73 | logGroupName: "/ecs/BookService", 74 | removalPolicy: cdk.RemovalPolicy.DESTROY, 75 | }); 76 | 77 | const authorServiceLogGroup = new logs.LogGroup( 78 | this, 79 | "authorServiceLogGroup", 80 | { 81 | logGroupName: "/ecs/AuthorService", 82 | removalPolicy: cdk.RemovalPolicy.DESTROY, 83 | } 84 | ); 85 | 86 | const bookServiceLogDriver = new ecs.AwsLogDriver({ 87 | logGroup: bookServiceLogGroup, 88 | streamPrefix: "BookService", 89 | }); 90 | 91 | const authorServiceLogDriver = new ecs.AwsLogDriver({ 92 | logGroup: authorServiceLogGroup, 93 | streamPrefix: "AuthorService", 94 | }); 95 | 96 | // Amazon ECR Repositories 97 | const bookservicerepo = ecr.Repository.fromRepositoryName( 98 | this, 99 | "bookservice", 100 | "book-service" 101 | ); 102 | 103 | const authorservicerepo = ecr.Repository.fromRepositoryName( 104 | this, 105 | "authorservice", 106 | "author-service" 107 | ); 108 | 109 | // Task Containers 110 | const bookServiceContainer = bookServiceTaskDefinition.addContainer( 111 | "bookServiceContainer", 112 | { 113 | image: ecs.ContainerImage.fromEcrRepository(bookservicerepo), 114 | logging: bookServiceLogDriver, 115 | } 116 | ); 117 | 118 | const authorServiceContainer = authorServiceTaskDefinition.addContainer( 119 | "authorServiceContainer", 120 | { 121 | image: ecs.ContainerImage.fromEcrRepository(authorservicerepo), 122 | logging: authorServiceLogDriver, 123 | } 124 | ); 125 | 126 | bookServiceContainer.addPortMappings({ 127 | containerPort: 80, 128 | }); 129 | 130 | authorServiceContainer.addPortMappings({ 131 | containerPort: 80, 132 | }); 133 | 134 | //Security Groups 135 | const bookServiceSecGrp = new ec2.SecurityGroup( 136 | this, 137 | "bookServiceSecurityGroup", 138 | { 139 | allowAllOutbound: true, 140 | securityGroupName: "bookServiceSecurityGroup", 141 | vpc: vpc, 142 | } 143 | ); 144 | 145 | bookServiceSecGrp.connections.allowFromAnyIpv4(ec2.Port.tcp(80)); 146 | 147 | const authorServiceSecGrp = new ec2.SecurityGroup( 148 | this, 149 | "authorServiceSecurityGroup", 150 | { 151 | allowAllOutbound: true, 152 | securityGroupName: "authorServiceSecurityGroup", 153 | vpc: vpc, 154 | } 155 | ); 156 | 157 | authorServiceSecGrp.connections.allowFromAnyIpv4(ec2.Port.tcp(80)); 158 | 159 | // Fargate Services 160 | const bookService = new ecs.FargateService(this, "bookService", { 161 | cluster: cluster, 162 | taskDefinition: bookServiceTaskDefinition, 163 | assignPublicIp: false, 164 | desiredCount: 2, 165 | securityGroup: bookServiceSecGrp, 166 | cloudMapOptions: { 167 | name: "bookService", 168 | cloudMapNamespace: dnsNamespace, 169 | }, 170 | }); 171 | 172 | const authorService = new ecs.FargateService(this, "authorService", { 173 | cluster: cluster, 174 | taskDefinition: authorServiceTaskDefinition, 175 | assignPublicIp: false, 176 | desiredCount: 2, 177 | securityGroup: authorServiceSecGrp, 178 | cloudMapOptions: { 179 | name: "authorService", 180 | cloudMapNamespace: dnsNamespace, 181 | }, 182 | }); 183 | 184 | // ALB 185 | const httpApiInternalALB = new elbv2.ApplicationLoadBalancer( 186 | this, 187 | "httpapiInternalALB", 188 | { 189 | vpc: vpc, 190 | internetFacing: false, 191 | } 192 | ); 193 | 194 | // ALB Listener 195 | this.httpApiListener = httpApiInternalALB.addListener("httpapiListener", { 196 | port: 80, 197 | // Default Target Group 198 | defaultAction: elbv2.ListenerAction.fixedResponse(200), 199 | }); 200 | 201 | // Target Groups 202 | const bookServiceTargetGroup = this.httpApiListener.addTargets( 203 | "bookServiceTargetGroup", 204 | { 205 | port: 80, 206 | priority: 1, 207 | healthCheck: { 208 | path: "/api/books/health", 209 | interval: cdk.Duration.seconds(30), 210 | timeout: cdk.Duration.seconds(3), 211 | }, 212 | targets: [bookService], 213 | pathPattern: "/api/books*", 214 | } 215 | ); 216 | 217 | const authorServiceTargetGroup = this.httpApiListener.addTargets( 218 | "authorServiceTargetGroup", 219 | { 220 | port: 80, 221 | priority: 2, 222 | healthCheck: { 223 | path: "/api/authors/health", 224 | interval: cdk.Duration.seconds(30), 225 | timeout: cdk.Duration.seconds(3), 226 | }, 227 | targets: [authorService], 228 | pathPattern: "/api/authors*", 229 | } 230 | ); 231 | 232 | //VPC Link 233 | this.httpVpcLink = new cdk.CfnResource(this, "HttpVpcLink", { 234 | type: "AWS::ApiGatewayV2::VpcLink", 235 | properties: { 236 | Name: "http-api-vpclink", 237 | SubnetIds: vpc.privateSubnets.map((m) => m.subnetId), 238 | }, 239 | }); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /cdk/singleAccount/lib/httpApi-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from "@aws-cdk/core"; 2 | import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2"; 3 | import * as ec2 from "@aws-cdk/aws-ec2"; 4 | import * as logs from "@aws-cdk/aws-logs"; 5 | import * as apig from "@aws-cdk/aws-apigatewayv2"; 6 | 7 | export class HttpApiStack extends cdk.Stack { 8 | constructor( 9 | scope: cdk.Construct, 10 | id: string, 11 | httpVpcLink: cdk.CfnResource, 12 | httpApiListener: elbv2.ApplicationListener, 13 | props?: cdk.StackProps 14 | ) { 15 | super(scope, id, props); 16 | 17 | // Consumer VPC 18 | const vpc = new ec2.Vpc(this, "ConsumerVPC", { 19 | natGateways: 0, 20 | subnetConfiguration: [ 21 | { 22 | cidrMask: 24, 23 | name: "ingress", 24 | subnetType: ec2.SubnetType.PUBLIC, 25 | }, 26 | ], 27 | }); 28 | 29 | //Security Group 30 | const bastionSecGrp = new ec2.SecurityGroup(this, "bastionSecGrp", { 31 | allowAllOutbound: true, 32 | securityGroupName: "bastionSecGrp", 33 | vpc: vpc, 34 | }); 35 | 36 | bastionSecGrp.connections.allowFromAnyIpv4(ec2.Port.tcp(22)); 37 | 38 | // AMI 39 | const amz_linux = ec2.MachineImage.latestAmazonLinux({ 40 | generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, 41 | edition: ec2.AmazonLinuxEdition.STANDARD, 42 | virtualization: ec2.AmazonLinuxVirt.HVM, 43 | storage: ec2.AmazonLinuxStorage.GENERAL_PURPOSE, 44 | }); 45 | 46 | // Instance 47 | const instance = new ec2.Instance(this, "BastionHost", { 48 | instanceType: new ec2.InstanceType("t3.nano"), 49 | machineImage: amz_linux, 50 | vpc: vpc, 51 | securityGroup: bastionSecGrp, 52 | keyName: "ssh-key", 53 | }); 54 | 55 | // HTTP API 56 | const api = new apig.HttpApi(this, "http-api", { 57 | createDefaultStage: true, 58 | }); 59 | 60 | // API Integration 61 | const integration = new apig.CfnIntegration( 62 | this, 63 | "HttpApiGatewayIntegration", 64 | { 65 | apiId: api.httpApiId, 66 | connectionId: httpVpcLink.ref, 67 | connectionType: "VPC_LINK", 68 | description: "API Integration", 69 | integrationMethod: "ANY", 70 | integrationType: "HTTP_PROXY", 71 | integrationUri: httpApiListener.listenerArn, 72 | payloadFormatVersion: "1.0", 73 | } 74 | ); 75 | 76 | // API Route 77 | new apig.CfnRoute(this, "Route", { 78 | apiId: api.httpApiId, 79 | routeKey: "ANY /{proxy+}", 80 | target: `integrations/${integration.ref}`, 81 | }); 82 | 83 | // EC2 instance ip address 84 | new cdk.CfnOutput(this, "EC2 public ip address: ", { 85 | value: instance.instancePublicIp, 86 | }); 87 | 88 | // API and Service Endpoints 89 | const httpApiEndpoint = api.apiEndpoint; 90 | const bookServiceEndpoint = httpApiEndpoint + "/api/books"; 91 | const authorServiceEndpoint = httpApiEndpoint + "/api/authors"; 92 | 93 | new cdk.CfnOutput(this, "HTTP API endpoint: ", { 94 | value: httpApiEndpoint, 95 | }); 96 | new cdk.CfnOutput(this, "Book Service: ", { 97 | value: bookServiceEndpoint, 98 | }); 99 | new cdk.CfnOutput(this, "Author Service: ", { 100 | value: authorServiceEndpoint, 101 | }); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /cdk/singleAccount/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "cdk": "bin/cdk.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "1.180.0", 15 | "@aws-cdk/aws-apigatewayv2": "^1.80.0", 16 | "@aws-cdk/core": "1.180.0", 17 | "@aws-cdk/aws-ec2": "1.180.0", 18 | "@aws-cdk/aws-ecr": "1.180.0", 19 | "@aws-cdk/aws-ecs": "1.180.0", 20 | "@aws-cdk/aws-elasticloadbalancingv2": "1.180.0", 21 | "@aws-cdk/aws-iam": "1.180.0", 22 | "@aws-cdk/aws-logs": "1.180.0", 23 | "@aws-cdk/aws-servicediscovery": "1.180.0", 24 | "@types/jest": "^26.0.10", 25 | "@types/node": "10.17.27", 26 | "jest": "^26.4.2", 27 | "ts-jest": "^26.2.0", 28 | "aws-cdk": "1.181.1", 29 | "ts-node": "^9.0.0", 30 | "typescript": "~3.9.7" 31 | }, 32 | "dependencies": { 33 | "@aws-cdk/core": "1.180.0", 34 | "source-map-support": "^0.5.16" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cdk/singleAccount/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /images/API_Gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/API_Gateway.png -------------------------------------------------------------------------------- /images/Architecture-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/Architecture-1.png -------------------------------------------------------------------------------- /images/Architecture-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/Architecture-2.png -------------------------------------------------------------------------------- /images/Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/Architecture.png -------------------------------------------------------------------------------- /images/AuthorService-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/AuthorService-Preview.png -------------------------------------------------------------------------------- /images/AuthorService.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/AuthorService.png -------------------------------------------------------------------------------- /images/BookService-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/BookService-Preview.png -------------------------------------------------------------------------------- /images/BookService.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/BookService.png -------------------------------------------------------------------------------- /images/FargateVpclinkStack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/FargateVpclinkStack.png -------------------------------------------------------------------------------- /images/HttpApiStack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/HttpApiStack.png -------------------------------------------------------------------------------- /images/cdk-deploy-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/cdk-deploy-1.png -------------------------------------------------------------------------------- /images/cdk-deploy-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/cdk-deploy-2.png -------------------------------------------------------------------------------- /images/cdk-diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/cdk-diff.png -------------------------------------------------------------------------------- /images/code-explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/code-explorer.png -------------------------------------------------------------------------------- /images/curl_author_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/curl_author_api.png -------------------------------------------------------------------------------- /images/curl_book_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/images/curl_book_api.png -------------------------------------------------------------------------------- /src/author-service/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/src/author-service/.DS_Store -------------------------------------------------------------------------------- /src/author-service/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /src/author-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.4-alpine 2 | 3 | # Create app directory 4 | 5 | RUN mkdir /app 6 | WORKDIR /app 7 | RUN chmod -R +x /app 8 | 9 | # RUN npm install 10 | COPY package.json package.json 11 | RUN npm install && mv node_modules /node_modules 12 | 13 | # Bundle app source 14 | COPY . . 15 | 16 | LABEL maintainer="Irshad A Buchh" 17 | 18 | # EXPOSE 80 19 | EXPOSE 80 20 | 21 | # Run application 22 | CMD node index.js 23 | -------------------------------------------------------------------------------- /src/author-service/healthcheck.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Welcome to Authors Service 6 | 13 | 14 | 15 | 16 |

Welcome to Authors Service !

17 |

If you see this page, the Authors Service is successfully installed and 18 | working. Further configuration is required.

19 | 20 | 24 | 25 |

Thank you for using AWS.

26 | 27 | 28 | -------------------------------------------------------------------------------- /src/author-service/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | app.use(express.json()); 4 | 5 | const authors = [ 6 | { id: 1, name: 'Mark Wilkins', book: "Learning Amazon Web Services (AWS)"}, 7 | { id: 2, name: 'Steve Morad', book: "AWS Certified Advanced Networking"}, 8 | { id: 3, name: 'Gojko Adzic', book: "Running Serverless"}, 9 | { id: 4, name: 'Giuseppe Borgese', book: "Effective DevOps with AWS"}, 10 | { id: 5, name: 'Vikas Aggarwal', book: "Ansible 2 Cloud Automation Cookbook"}, 11 | { id: 6, name: 'Ajit Pratap Kundan', book: "VMware Cross-Cloud Architecture"}, 12 | ] 13 | 14 | //READ Request Handlers 15 | 16 | app.get('/', (req, res) => { 17 | console.log('=== GET request at / ==='); 18 | res.sendFile('./healthcheck.html', { root: __dirname }); 19 | }); 20 | 21 | app.get('/api/authors/health', (req, res) => { 22 | console.log('=== GET request at /api/authors/healthcheck ==='); 23 | res.sendFile('./healthcheck.html', { root: __dirname }); 24 | }); 25 | 26 | app.get('/api/authors', (req, res) => { 27 | console.log('=== GET request at /api/authors ==='); 28 | res.send(authors); 29 | }); 30 | 31 | app.get('/api/authors/:id', (req, res) => { 32 | const author = authors.find(c => c.id === parseInt(req.params.id)); 33 | console.log('=== GET request at /api/authors/{id} ==='); 34 | if (!author) res.status(404).send('

Ooops... Cant find what you are looking for!

'); 35 | res.send(author); 36 | }); 37 | 38 | 39 | //PORT ENVIRONMENT VARIABLE 40 | // const port = process.env.PORT || 80; 41 | const port = 80; 42 | app.listen(port, '0.0.0.0', () => console.log(`Listening on port ${port}..`)); -------------------------------------------------------------------------------- /src/author-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-node-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.18.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/book-service/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/http-api-aws-fargate-cdk/a35600bc25163af633dd72289cc3e7569f54cc8c/src/book-service/.DS_Store -------------------------------------------------------------------------------- /src/book-service/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /src/book-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.4-alpine 2 | 3 | # Create app directory 4 | 5 | RUN mkdir /app 6 | WORKDIR /app 7 | RUN chmod -R +x /app 8 | 9 | # RUN npm install 10 | COPY package.json package.json 11 | RUN npm install && mv node_modules /node_modules 12 | 13 | # Bundle app source 14 | COPY . . 15 | 16 | LABEL maintainer="Irshad A Buchh" 17 | 18 | # EXPOSE 80 19 | EXPOSE 80 20 | 21 | # Run application 22 | CMD node index.js 23 | -------------------------------------------------------------------------------- /src/book-service/healthcheck.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Welcome to Books Service! 6 | 13 | 14 | 15 | 16 |

Welcome to Books Service !

17 |

If you see this page, the nginx web server is successfully installed and 18 | working. Further configuration is required.

19 | 20 | 24 | 25 |

Thank you for using AWS.

26 | 27 | 28 | -------------------------------------------------------------------------------- /src/book-service/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | app.use(express.json()); 4 | 5 | const books = [ 6 | { id: 1, isbn: 9781119490708, title: 'AWS Certified Cloud Practitioner Study',publisher: "Wiley",date: "07/02/2019" }, 7 | { id: 2, isbn: 9781788293723, title: 'Mastering AWS Security',publisher: "Packt Publishing",date: "10/30/2017" }, 8 | { id: 3, isbn: 9781789806199, title: 'Machine Learning with AWS',publisher: "Packt Publishing",date: "11/02/2018" }, 9 | { id: 4, isbn: 9781782171102, title: 'Learning Aws Opsworks',publisher: "Packt Publishing",date: "09/10/2013" }, 10 | { id: 5, isbn: 9781789340198, title: 'AWS Lambda Quick Start Guide',publisher: "Packt Publishing",date: "06/29/2018" }, 11 | { id: 6, isbn: 9781542885751, title: 'AWS Basics',publisher: "CreateSpace Publishing",date: "02/09/2017" }, 12 | ] 13 | 14 | //READ Request Handlers 15 | 16 | app.get('/api/books/health', (req, res) => { 17 | console.log('=== GET request at /api/books/healthcheck ==='); 18 | res.sendFile('./healthcheck.html', { root: __dirname }); 19 | }); 20 | 21 | app.get('/api/books', (req, res) => { 22 | console.log('=== GET request at /api/books ==='); 23 | res.send(books); 24 | }); 25 | 26 | app.get('/api/books/:id', (req, res) => { 27 | const book = books.find(c => c.id === parseInt(req.params.id)); 28 | console.log('=== GET request at /api/books/{id} ==='); 29 | if (!book) res.status(404).send('

Ooops... Cant find what you are looking for!

'); 30 | res.send(book); 31 | }); 32 | 33 | 34 | //PORT ENVIRONMENT VARIABLE 35 | const port = process.env.PORT || 80; 36 | app.listen(port, () => console.log(`Listening on port ${port}..`)); -------------------------------------------------------------------------------- /src/book-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-node-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1" 13 | } 14 | } 15 | --------------------------------------------------------------------------------