Deploying a Single Page Application (SPA) on AWS: A Beginner's Guide. Part 7. AWS App Runner
Andrii Melashchenko
Posted on December 4, 2023
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
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"
}
}
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}"
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:
- 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!";
- Build the backend:
$ ./gradlew bootBuildImage --imageName=spring-rest-api
- 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
- Trigger an App Runner update:
aws apprunner start-deployment --service-arn arn:aws:apprunner:eu-central-1:1111111111:service/apprunner-restapi-production/2222222222222222222
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
Response:
{"id":22,"content":"Hello, new World!"}
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.
Posted on December 4, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.