cdk-fargate-patterns
    TypeScript icon, indicating that this package has built-in type declarations

    0.3.92 • Public • Published

    NPM version PyPI version Release

    All Contributors

    cdk-fargate-patterns

    CDK patterns for serverless container with AWS Fargate

    DualAlbFargateService

    Inspired by Vijay Menon from the AWS blog post introduced in 2019, DualAlbFargateService allows you to create one or many fargate services with both internet-facing ALB and internal ALB associated with all services. With this pattern, fargate services will be allowed to intercommunicat via internal ALB while external inbound traffic will be spread across the same service tasks through internet-facing ALB.

    The sample below will create 3 fargate services associated with both external and internal ALBs. The internal ALB will have an alias(internal.svc.local) auto-configured from Route 53 so services can interconnect through the private ALB endpoint.

    new DualAlbFargateService(stack, 'Service', {
      spot: true, // FARGATE_SPOT only cluster
      tasks: [
        {
          task: orderTask,
          desiredCount: 2,
          external: { port: 443, certificate },
          internal: { port: 80 },
          // customize the service autoscaling policy
          scalingPolicy: {
            maxCapacity: 20,
            requestPerTarget: 1000,
            targetCpuUtilization: 50,
          },
        },
        { task: customerTask, desiredCount: 2, internal: { port: 8080 } },
        { task: productTask, desiredCount: 2, internal: { port: 9090 } },
        
        // support gRPC application.
        // you can use protocolVersion to setting gRPC for your application. 
        // you need to specify acm and grpc port.
        // and don't forget to setting healthCheck.healthyGrpcCodes.  
        {
            task: task,
            desiredCount: 1,
            internal: { port: 50051, certificate: [cert] },
            external: { port: 50051, certificate: [cert] },
            protocolVersion: elbv2.ApplicationProtocolVersion.GRPC,
            healthCheck: {
              healthyGrpcCodes: '12',
            },
          },
      ],
      route53Ops: {
        zoneName: 'svc.local',
        externalElbRecordName: 'external',
        internalElbRecordName: 'internal',
      },
    });

    DualNlbFargateService

    Similar to DualAlbFargateService, you are allowed to deploy multiple container services with AWS Fargate as well as external NLB and internal NLB.

    To allowed ingress traffic, you will need to explicitly add ingress rules on the connections:

    const nlbService = new DualNlbFargateService(stack, 'NLBService', {
      tasks: [...],
    });
    
    // we need this to allow ingress traffic from public internet only for the order service
    nlbService.service[0].connections.allowFromAnyIpv4(ec2.Port.tcp(8080));
    // allow from VPC
    nlbService.service.forEach(s => {
      s.connections.allowFrom(ec2.Peer.ipv4(nlbService.vpc.vpcCidrBlock), ec2.Port.tcp(8080));
    });

    ALB Listener Rules Support

    To share the ALB listener with multiple services, use forwardConditions to specify custom rules.

    The sample below defines three services sharing a single extneral listener on HTTPS(TCP 443) with different host names while interconnecting internally with different listeners on the internal ALB.

    new DualAlbFargateService(stack, 'ALBService', {
      spot: true, // FARGATE_SPOT only cluster
      enableExecuteCommand: true,
      tasks: [
        {
          task: orderTask,
          desiredCount: 2,
          internal: { port: 80 },
          external: {
            port: 443,
            certificate: [cert],
            forwardConditions: [elbv2.ListenerCondition.hostHeaders(['order.example.com'])],
          }
        },
        {
          task: customerTask,
          desiredCount: 1,
          external: {
            port: 443,
            certificate: [cert],
            forwardConditions: [elbv2.ListenerCondition.hostHeaders(['customer.example.com'])],
          },
          internal: { port: 8080 },
        },
        {
          task: productTask,
          desiredCount: 1,
          external: {
            port: 443,
            certificate: [cert],
            forwardConditions: [elbv2.ListenerCondition.hostHeaders(['product.example.com'])],
          },
          internal: { port: 9090 },
        },
      ],
    });

    Fargate Spot Support

    By enabling the spot property, 100% fargate spot tasks will be provisioned to help you save up to 70%. Check more details about Fargate Spot. This is a handy catch-all flag to force all tasks to be FARGATE_SPOT only.

    To specify mixed strategy with partial FARGATE and partial FARGATE_SPOT, specify the capacityProviderStrategy for individual tasks like.

    new DualAlbFargateService(stack, 'Service', {
      tasks: [
        {
          task: customerTask,
          internal: { port: 8080 },
          desiredCount: 2,
          capacityProviderStrategy: [
            {
              capacityProvider: 'FARGATE',
              base: 1,
              weight: 1,
            },
            {
              capacityProvider: 'FARGATE_SPOT',
              base: 0,
              weight: 3,
            },
          ],
        },
      ],
    });

    The custom capacity provider strategy will be applied if capacityProviderStretegy is specified, otherwise, 100% spot will be used when spot: true. The default policy is 100% Fargate on-demand.

    Fargate Spot Termination Handling

    By default, if fargate spot capacity is available in the cluster, a fargate spot termination handler Lambda function will be created with proper IAM role policies to handle the termination event to ensure we deregister the fargate spot task from target groups gracefully. While it's a recommended feature, you may opt out with spotTerminationHandler: false.

    new DualAlbFargateService(stack, 'Service', {
      spot: true, // FARGATE_SPOT only cluster
      spotTerminationHandler: false,  // opt-out
      ...
    }

    ECS Exec

    Simply turn on the enableExecuteCommand property to enable the ECS Exec support for all services.

    ECS deployment circuit breaker

    ECS deployment circuit breaker automatically rolls back unhealthy service deployments without the need for manual intervention. By default this feature is enabled, you can opt out with circuitBreaker: false. Read the docummentation or blog post for more details.

    Internal, External or Both

    Specify the internal or external property to expose your service internally, externally or both.

    The certificate property implies HTTPS protocol.

    new DualAlbFargateService(stack, 'Service', {
        tasks: [
          // this task is internal only
          { task: task1, internal: { port: 8080 } },
          // this task is external only
          { task: task2, external: { port: 8081 } },
          // this task is both external(HTTPS) and internal(HTTP) facing
          { 
            task: task3,
            external: { port: 443, certificate: myAcmCert },
            internal: { port: 8888 },
          },
        ],
      });

    VPC Subnets

    By default, all tasks will be deployed in the private subnets. You will need the NAT gateway for the default route associated with the private subnets to ensure the task can successfully pull the container images.

    However, you are allowed to specify vpcSubnets to customize the subnet selection.

    To deploy all tasks in public subnets, one per AZ:

    new DualAlbFargateService(stack, 'Service', {
        vpcSubnets: {
          subnetType: ec2.SubnetType.PUBLIC,
          onePerAz: true,
      },
      ...
    });

    This will implicitly enable the auto assign public IP for each fargate task so the task can successfully pull the container images from external registry. However, the ingress traffic will still be balanced via the external ALB.

    To deploy all tasks in specific subnets:

    new DualAlbFargateService(stack, 'Service', {
      vpcSubnets: { 
          subnets: [
            ec2.Subnet.fromSubnetId(stack, 'sub-1a', 'subnet-0e9460dbcfc4cf6ee'),
            ec2.Subnet.fromSubnetId(stack, 'sub-1b', 'subnet-0562f666bdf5c29af'),
            ec2.Subnet.fromSubnetId(stack, 'sub-1c', 'subnet-00ab15c0022872f06'),
          ],
        },
      ...
    });

    Import an existing Cluster

    !!! Before using an existing ECS Cluster, please make sure you have the following:

    const vpc = ec2.Vpc.fromLookup(stack, 'defVpc', { isDefault: true });
    const sg = new ec2.SecurityGroup(stack, 'demo-sg', {
      vpc,
    });
    const existCluster = ecs.Cluster.fromClusterAttributes(stack, 'existCluster', {
      securityGroups: [sg],
      clusterName: 'existCluster',
      vpc,
    });
    
    new DualAlbFargateService(stack, 'Service', {
      enableExecuteCommand: true,
      cluster: existCluster,
      tasks: [
        {
          task: nginx,
          desiredCount: 1,
          external: { port: 80 },
          capacityProviderStrategy: [{
            capacityProvider: 'FARGATE_SPOT',
            weight: 1,
          }],
        },
      ],
    });

    Sample Application

    This repository comes with a sample applicaiton with 3 services in Golang. On deployment, the Order service will be exposed externally on external ALB port 80 and all requests to the Order service will trigger sub-requests internally to another other two services(product and customer) through the internal ALB and eventually aggregate the response back to the client.

    Deploy

    To deploy the sample application in you default VPC:

    // install first
    $ yarn install
    // compile the ts to js
    $ yarn build
    $ npx cdk --app lib/integ.default.js -c use_default_vpc=1 diff
    $ npx cdk --app lib/integ.default.js -c use_default_vpc=1 deploy

    To deploy with HTTPS-only with existing ACM certificate in your default VPC:

    $ npx cdk --app lib/integ.default.js deploy -c use_default_vpc=1 -c ACM_CERT_ARN=<YOUR_ACM_CERT_ARN>

    On deployment complete, you will see the external ALB endpoint in the CDK output. cURL the external HTTP endpoint and you should be able to see the aggregated response.

    $ curl http://demo-Servi-EH1OINYDWDU9-1397122594.ap-northeast-1.elb.amazonaws.com
    or
    $ curl https://<YOUR_CUSTOM_DOMAIN_NAME>
    
    {"service":"order", "version":"1.0"}
    {"service":"product","version":"1.0"}
    {"service":"customer","version":"1.0"}

    WordPress

    Use the WordPress construct to create a serverless WordPress service with AWS Fargate, Amazon EFS filesystem and Aurora serverless database. All credentials auto generated from the AWS Secret Manager and securely inject the credentials into the serverless container with the auto generated IAM task execution role.

    new WordPress(stack, 'WP', {
      auroraServerless: true,
      spot: true,
      enableExecuteCommand: true,
    });

    Laravel

    The Laravl construct is provided as a high-level abstraction that allows you to create a modern Laravel environment running on AWS Fargate and Amazon Aurora Serverless. Here comes two variants as the reference:

    laravel-bitnami - based on bitnami/laravel with artisan running as the built-in web server.

    laravel-nginx-php-fpm - laravel with nginx and php-fpm maintained by Ernest Chiang.

    Simply point code to your local Laravel codebase and it takes care everything else for you.

    Samples

    /**
     * laravel-bitnami
     */
    new Laravel(stack, 'LaravelBitnamiDemo', {
      auroraServerless: true,
      spot: true,
      enableExecuteCommand: true,
      code: path.join(__dirname, '../services/laravel-bitnami'),
      containerPort: 3000,
      loadbalancer: { port: 80 },
    });
    
    /**
     * laravel-nginx-php-fpm
     */
    new Laravel(stack, 'LaravelNginxDemo', {
      auroraServerless: true,
      spot: true,
      enableExecuteCommand: true,
      code: path.join(__dirname, '../services/laravel-nginx-php-fpm'),
      loadbalancer: { port: 80 },
    });

    See integ.laravel.ts for the full code sample.

    Local development and testing

    The docker-compose.yml is provided with all sample services in the repository. To bring up all services locally, run:

    $ cd services
    $ docker compose up

    Use cURL to test locally:

    curl http://localhost
    

    Response:

    {"service":"order","version":"1.0"}
    {"service":"product","version":"1.0"}
    {"service":"customer","version":"1.0"}
    

    FAQ

    Q: What is the difference between cdk-fargate-patterns and aws-ecs-patterns?

    A: aws-ecs-patterns comes with a few patterns around Amazon ECS with AWS Fargate or AWS EC2 and focuses on some scenarios with single service and single ELB like ApplicationLoadBalancedFargateService and NetworkLoadBalancedFargateService. However, cdk-fargate-patterns is trying to explore use cases on modern application which usually comes with multiple container services grouped as a deployment with inter-service connectivity as well as ingress traffic from external internet by seperating the internal ELB from the external one.

    Contributors

    Thanks goes to these wonderful people (emoji key):


    Neil Kuan

    🎨 💻 ⚠️ 💡

    Ernest Chiang

    🤔 ⚠️ 💻 💡

    Clarence

    💡 💻

    paper5487

    🐛

    Peter Larsson

    🤔

    LeoChien-SC

    🐛

    This project follows the all-contributors specification. Contributions of any kind welcome!

    Install

    npm i cdk-fargate-patterns

    DownloadsWeekly Downloads

    189

    Version

    0.3.92

    License

    Apache-2.0

    Unpacked Size

    1.33 MB

    Total Files

    257

    Last publish

    Collaborators

    • pahud