Project: APISIX (Orchestration & Management / API Gateway)
File: aws.md

title: Running APISIX in AWS with AWS CDK <!-- --> is a cloud-native microservices API gateway, delivering the ultimate performance, security, open source and scalable platform for all your APIs and microservices. This reference architecture walks you through building APISIX as a serverless container API Gateway on top of AWS Fargate with AWS CDK. ```bash $ mkdir apisix-aws $ cd $_ $ npx projen new awscdk-app-ts ``` update the `.projenrc.js` with the following content: ```js const { AwsCdkTypeScriptApp } = require('projen'); const project = new AwsCdkTypeScriptApp({ cdkVersion: "1.70.0", name: "apisix-aws", cdkDependencies: [ '@aws-cdk/aws-ec2', '@aws-cdk/aws-ecs', '@aws-cdk/aws-ecs-patterns', ] }); project.synth(); ``` update the project: ```ts $ npx projen ``` ```ts import * as cdk from '@aws-cdk/core'; import { Vpc, Port } from '@aws-cdk/aws-ec2'; import { Cluster, ContainerImage, TaskDefinition, Compatibility } from '@aws-cdk/aws-ecs'; import { ApplicationLoadBalancedFargateService, NetworkLoadBalancedFargateService } from '@aws-cdk/aws-ecs-patterns'; export class ApiSixStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc = Vpc.fromLookup(this, 'VPC', { isDefault: true }) const cluster = new Cluster(this, 'Cluster', { vpc }) / ApiSix service */ const taskDefinition = new TaskDefinition(this, 'TaskApiSix', { compatibility: Compatibility.FARGATE, memoryMiB: '512', cpu: '256' }) taskDefinition .addContainer('apisix', { image: ContainerImage.fromRegistry('iresty/apisix'), }) .addPortMappings({ containerPort: 9080 }) taskDefinition .addContainer('etcd', { image: ContainerImage.fromRegistry('gcr.azk8s.cn/etcd-development/etcd:v3.3.12'), // image: ContainerImage.fromRegistry('gcr.io/etcd-development/etcd:v3.3.12'), }) .addPortMappings({ containerPort: 2379 }) const svc = new ApplicationLoadBalancedFargateService(this, 'ApiSixService', { cluster, taskDefinition, }) svc.targetGroup.setAttribute('deregistrationdelay.timeoutseconds', '30') svc.targetGroup.configureHealthCheck({ interval: cdk.Duration.seconds(5), healthyHttpCodes: '404', healthyThresholdCount: 2, unhealthyThresholdCount: 3, timeout: cdk.Duration.seconds(4) }) / PHP service */ const taskDefinitionPHP = new TaskDefinition(this, 'TaskPHP', { compatibility: Compatibility.FARGATE, memoryMiB: '512', cpu: '256' }) taskDefinitionPHP .addContainer('php', { image: ContainerImage.fromRegistry('abiosoft/caddy:php'), }) .addPortMappings({ containerPort: 2015 }) const svcPHP = new NetworkLoadBalancedFargateService(this, 'PhpService', { cluster, taskDefinition: taskDefinitionPHP, assignPublicIp: true, }) // allow Fargate task behind NLB to accept all traffic svcPHP.service.connections.allowFromAnyIpv4(Port.tcp(2015)) svcPHP.targetGroup.setAttribute('deregistrationdelay.timeoutseconds', '30') svcPHP.loadBalancer.setAttribute('loadbalancing.crosszone.enabled', 'true') new cdk.CfnOutput(this, 'ApiSixDashboardURL', { value: `http://${svc.loadBalancer.loadBalancerDnsName}/apisix/dashboard/` }) } } const devEnv = { account: process.env.CDKDEFAULTACCOUNT, region: process.env.CDKDEFAULTREGION, }; const app = new

new ApiSixStack(app, 'apisix-stack-dev', { env: devEnv }); app.synth(); ``` ```bash $ cdk diff $ cdk deploy ``` On deployment complete, some outputs will be returned: ```bash Outputs: apiSix.PhpServiceLoadBalancerDNS5E5BAB1B = apiSi-PhpSe-FOL2MM4TW7G8-09029e095ab36fcc.elb.us-west-2.amazonaws.com apiSix.ApiSixDashboardURL = http://apiSi-ApiSi-1TM103DN35GRY-1477666967.us-west-2.elb.amazonaws.com/apisix/dashboard/ apiSix.ApiSixServiceLoadBalancerDNSD4E5B8CB = apiSi-ApiSi-1TM103DN35GRY-1477666967.us-west-2.elb.amazonaws.com apiSix.ApiSixServiceServiceURLF6EC7872 = http://apiSi-ApiSi-1TM103DN35GRY-1477666967.us-west-2.elb.amazonaws.com ``` Open the `apiSix.ApiSixDashboardURL` from your browser and you will see the login prompt. All upstream nodes are running as AWS Fargate tasks and registered to the NLB(Network Load Balancer) exposing multiple static IP addresses. We can query the IP addresses by nslookup the apiSix.PhpServiceLoadBalancerDNS5E5BAB1B like this: ```bash $ nslookup apiSi-PhpSe-FOL2MM4TW7G8-09029e095ab36fcc.elb.us-west-2.amazonaws.com Server: 192.168.31.1 Address: 192.168.31.1#53 Non-authoritative answer: Name: apiSi-PhpSe-FOL2MM4TW7G8-09029e095ab36fcc.elb.us-west-2.amazonaws.com Address: 44.224.124.213 Name: apiSi-PhpSe-FOL2MM4TW7G8-09029e095ab36fcc.elb.us-west-2.amazonaws.com Address: 18.236.43.167 Name: apiSi-PhpSe-FOL2MM4TW7G8-09029e095ab36fcc.elb.us-west-2.amazonaws.com Address: 35.164.164.178 Name: apiSi-PhpSe-FOL2MM4TW7G8-09029e095ab36fcc.elb.us-west-2.amazonaws.com Address: 44.226.102.63 ``` Configure the IP addresses returned as your upstream nodes in your APISIX dashboard followed by the Services and Routes configuration. Let's say we have a `/index.php` as the URI for the first route for our first Service from the Upstream IP addresses. OK. Let's test the `/index.php` on `{apiSix.ApiSixServiceServiceURL}/index.php` Now we have been successfully running APISIX in AWS Fargate as serverless container API Gateway service. ```bash $ cdk destroy ``` update `src/main.ts` ```js taskDefinition .addContainer('etcd', { image: ContainerImage.fromRegistry('gcr.azk8s.cn/etcd-development/etcd:v3.3.12'), // image: ContainerImage.fromRegistry('gcr.io/etcd-development/etcd:v3.3.12'), }) .addPortMappings({ containerPort: 2379 }) ``` (read for more reference) Run `cdk deploy` and specify your preferred AWS region in China. ```bash $ cdk deploy --profile cn -c region=cn-northwest-1 $ cdk deploy --profile cn -c region=cn-north-1 ``` In the following case, we got the `Outputs` returned for AWS Ningxia region(cn-northwest-1): ```bash Outputs: apiSix.PhpServiceLoadBalancerDNS5E5BAB1B = apiSi-PhpSe-1760FFS3K7TXH-562fa1f7f642ec24.elb.cn-northwest-1.amazonaws.com.cn apiSix.ApiSixDashboardURL = http://apiSi-ApiSi-123HOROQKWZKA-1268325233.cn-northwest-1.elb.amazonaws.com.cn/apisix/dashboard/ apiSix.ApiSixServiceLoadBalancerDNSD4E5B8CB = apiSi-ApiSi-123HOROQKWZKA-1268325233.cn-northwest-1.elb.amazonaws.com.cn apiSix.ApiSixServiceServiceURLF6EC7872 = http://apiSi-ApiSi-123HOROQKWZKA-1268325233.cn-northwest-1.elb.amazonaws.com.cn ``` Open the `apiSix.ApiSixDashboardURL` URL and log in to configure your APISIX in AWS China region. TBD For high availability and state consistency consideration, you might be interested to decouple the etcd3 as a separate cluster from APISIX not only for performance but also high availability and fault tolerance yet with highly reliable state consistency. TBD