Deploying a Single Page Application (SPA) on AWS: A Beginner's Guide. Part 7. AWS App Runner

javatask

Andrii Melashchenko

Posted on December 4, 2023

Deploying a Single Page Application (SPA) on AWS: A Beginner's Guide. Part 7. AWS App Runner

Introduction

In this part of the series, the focus is on running your backend application in a production environment. The goal is to provide the necessary tooling to fulfil the Observable, Seamlessly Updatable, and Failure-Tolerant criteria of a Cloud-Ready App, as described in the FROSST criteria from the Cloud Strategy - A Decision-based Approach to Successful Cloud Migration - The Architect Elevator Book.

Before CloudFormation (CFN) Template

Before diving into the CloudFormation template, it's essential to set up observability. This can be done by creating an observability configuration using the AWS CLI:

aws apprunner create-observability-configuration --observability-configuration-name rest-api --trace-configuration Vendor=AWSXRAY

Enter fullscreen mode Exit fullscreen mode

After creating the configuration, retrieve the ARN from the ObservabilityConfigurationArn field in the output, as shown in the JSON snippet:

{
    "ObservabilityConfiguration": {
        "ObservabilityConfigurationArn": "arn:aws:apprunner:eu-central-1:1111111111:observabilityconfiguration/rest-api/1/xxxxxx",
        "ObservabilityConfigurationName": "rest-api",
        "TraceConfiguration": {
            "Vendor": "AWSXRAY"
        },
        "ObservabilityConfigurationRevision": 1,
        "Latest": true,
        "Status": "ACTIVE",
        "CreatedAt": "2023-10-12T19:11:23.425000+02:00"
    }
}

Enter fullscreen mode Exit fullscreen mode

App Runner CloudFormation (CFN) Template to Support Failure Tolerance

Let's dive into the CloudFormation template:

AWSTemplateFormatVersion: '2010-09-09'
Description: Backend deployed viaAWS App Runner
Parameters:
  Environment:
    Type: String
    Default: production
    Description: A name for the environment that this cloudformation will be part of.
                 Used to locate other resources in the same environment.
  ImageUrl:
    Type: String
    Description: The url of a docker image that contains the application process that
                 will handle the traffic for this service. Should be on ECR private
  ObservabilityConfigurationArnParam:
    Type: String
    Description: Create it via aws cli aws apprunner create-observability-configuration --observability-configuration-name rest-api --trace-configuration Vendor=AWSXRAY

Resources: 
  # Backend
  AppRunnerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2008-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - build.apprunner.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess

  Backend:
    Type: AWS::AppRunner::Service
    Properties:
      ServiceName: !Sub ${AWS::StackName}-restapi-${Environment}
      ObservabilityConfiguration:
        ObservabilityEnabled: true
        ObservabilityConfigurationArn: !Ref ObservabilityConfigurationArnParam
      InstanceConfiguration:
        Cpu: 0.25 vCPU
        Memory: 0.5 GB
      SourceConfiguration:
        AuthenticationConfiguration:
          AccessRoleArn: !GetAtt AppRunnerRole.Arn
        ImageRepository:
          ImageRepositoryType: ECR
          ImageIdentifier: !Ref ImageUrl
          ImageConfiguration:
            Port: 8080
      HealthCheckConfiguration:
          HealthyThreshold: 1
          Interval: 10
          Path: /api/greeting
          Protocol: HTTP
          Timeout: 5
          UnhealthyThreshold: 5
      Tags:
        - Key: environment
          Value: !Ref Environment

  # Api Gateway
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub ${AWS::StackName}-gateway-${Environment}
  ApiGatewayResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId:
        !GetAtt ApiGatewayRestApi.RootResourceId
      PathPart: '{proxy+}'
      RestApiId:
        !Ref ApiGatewayRestApi
  ApiGatewayMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: ANY
      ResourceId:
        !Ref ApiGatewayResource
      RestApiId:
        !Ref ApiGatewayRestApi
      AuthorizationType: NONE
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        Type: HTTP_PROXY
        IntegrationHttpMethod: ANY
        Uri: !Sub "https://${Backend.ServiceUrl}/{proxy}"
  ApiGatewayDeployment:
    Type: 'AWS::ApiGateway::Deployment'
    DependsOn:
      - ApiGatewayMethod
    Properties:
      RestApiId:
        !Ref ApiGatewayRestApi
      StageName: !Ref Environment

Outputs:
  AppRunnerServiceArn:
    Description: AppRunnerServiceArn
    Value: !GetAtt Backend.ServiceArn
  AppRunnerServiceId:
    Description: AppRunnerServiceId
    Value: !GetAtt Backend.ServiceId
  AppRunnerServiceUrl:
    Description: AppRunnerServiceUrl
    Value: !GetAtt Backend.ServiceUrl
  IntegrationAPI:
    Description: URL For CDN
    Value: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}"

Enter fullscreen mode Exit fullscreen mode

This AWS CloudFormation template defines the infrastructure for deploying a backend application using AWS App Runner and exposing it via AWS API Gateway. Let's break down its key components:

Parameters

  • Environment: Specifies the name of the environment (e.g., production or development) that this CloudFormation stack is part of.

  • ImageUrl: The URL of a Docker image containing the application process to handle traffic. This image should be stored in a private Amazon ECR registry.

  • ObservabilityConfigurationArnParam: ARN for the observability configuration, which can be created using the AWS CLI.

Resources

  • AppRunnerRole (IAM Role): This role allows the App Runner service to assume it and interact with other AWS services. It also attaches the necessary managed policy to access ECR.

  • Backend (AWS App Runner Service): Defines the App Runner service, specifying its name, observability configuration, instance configuration (CPU and memory), source configuration (image repository details), health check configuration, and tags.

  • ApiGatewayRestApi (API Gateway REST API): Creates the entry point for the API Gateway.

  • ApiGatewayResource (API Gateway Resource): Configures a wildcard proxy resource to forward all requests to the App Runner service.

  • ApiGatewayMethod (API Gateway Method): Defines a method that allows any HTTP method and integrates with the backend via HTTP_PROXY.

  • ApiGatewayDeployment (API Gateway Deployment): Deploys the API Gateway to a specified stage, making it accessible externally.

Outputs

  • AppRunnerServiceArn : The ARN of the App Runner service.

  • AppRunnerServiceId : The ID of the App Runner service.

  • AppRunnerServiceUrl : The URL of the App Runner service.

  • IntegrationAPI : The external URL for accessing the API through the API Gateway.

This template sets up a backend service using AWS App Runner, utilizing a Docker image from ECR, and configures an API Gateway to handle and forward HTTP requests to this backend. It also supports observability through AWS X-Ray and offers outputs for accessing both the backend service and the API Gateway endpoint. Parameterizing the environment allows for different configurations for various deployment stages.

Logs, Monitoring, and Tracing to Support Observability

For comprehensive observability, you can access data about your backend by visiting the App Runner console. Here, you can view logs, monitor metrics, and trace information.

Seamlessly Updatable

To make updates to your backend, follow these steps:

  1. Modify the code by adding the word "new" to a string template in your application.
    @RestController
    public class GreetingController {
        private static final String template = "Hello, new %s!";
Enter fullscreen mode Exit fullscreen mode
  1. Build the backend:
    $ ./gradlew bootBuildImage --imageName=spring-rest-api
Enter fullscreen mode Exit fullscreen mode
  1. Publish the new version to the private Elastic Container Registry (ECR):
    aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin 11111111.dkr.ecr.eu-central-1.amazonaws.com
    docker tag spring-rest-api:latest 111111111.dkr.ecr.eu-central-1.amazonaws.com/ias:spring-rest-api
    docker push 111111111.dkr.ecr.eu-central-1.amazonaws.com/ias:spring-rest-api
Enter fullscreen mode Exit fullscreen mode
  1. Trigger an App Runner update:
    aws apprunner start-deployment --service-arn arn:aws:apprunner:eu-central-1:1111111111:service/apprunner-restapi-production/2222222222222222222
Enter fullscreen mode Exit fullscreen mode

You can check for logs in the App Runner console.

After querying the API Gateway endpoint, you should receive a "new" response:

curl https://xxxxxxxxxxx.execute-api.eu-central-1.amazonaws.com/production/api/greeting

Enter fullscreen mode Exit fullscreen mode

Response:

{"id":22,"content":"Hello, new World!"}

Enter fullscreen mode Exit fullscreen mode

Summary

This article has provided a comprehensive guide to deploying a backend application using AWS App Runner and exposing it through AWS API Gateway. It covered the creation of an AWS CloudFormation template, which defines the essential infrastructure, parameters, and resources. The template ensures observability through AWS X-Ray and supports seamless updates by demonstrating code changes and deployment triggers using a sample Spring Boot application.

💖 💪 🙅 🚩
javatask
Andrii Melashchenko

Posted on December 4, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related