Skip to content

AWS AppRunner

Introduction

Containers are one of the most reliable ways to build and ship services, and AWS alone gives you more than a dozen ways to run them. Rather than walk the entire catalog, this note zooms in on one: AWS AppRunner — where it sits next to ECS, why it shines for small auto-scaling services, and a CloudFormation template to spin one up. First, a quick look at the popular ways to run containers on AWS:

  • AWS ECS ( Elastic Container Service ): It also divides into 2 sub-categories as:
  • ECS Fargate
  • ECS with EC2
  • AWS EKS ( Managed Kubernetes Service offering from AWS )
  • AppRunner ( Of course )
  • AWS Lambda ( Lambda is also one of the most popular way of running containers and I love this service as well )
  • AWS LightSail
  • Good old EC2

I know it's getting exhausting list but if it's not for you and you want the entire catalogue, you can head over to this Link to read further.

ECS vs AppRunner

Now this brings to our next question, why do we need to use a new service to run a freaking container if you already know ECS, well....good question. The answer is, you might not however, let's dive in about the pros and cons and then we can decide what's the best fit:

  • AppRunner charges are based on usage and has auto-deploy features, comes with pre-configured load-balancing, HTTPS and auto-scaling. If there are no requests, you'll only be charged for the memory usage ( and even just the memory that app is using while running, not allocated ) no CPU charges, no load-balancer charges.
  • It abstracts away all the complexity which we have to deal with while working with ECS. We also don't have to provision an ELB which saves us a fortune if you're running a small app and are worried about it going viral. The AppRunner will auto-scale based on usage and then scale down once the traffic is gone.

So these are the 2 main reasons AppRunner is favored for small scale applications. Once the application starts hitting consistent traffic, you can consider moving it to ECS which will be really easy considering it also runs containers.

The Code

Now, enough talk, where is the code you might ask, well here's the cloudformation for spinning up a AppRunner.

apprunner.yml
Resources:
  AppRunnerRole:
    Metadata:
      "aws:description": "An IAM Role for App Runner to use on your behalf to pull your image from ECR"
    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

  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: tasks.apprunner.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: AppRunnerServicePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "ssm:GetParameters"
                Resource:
                  - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/dev/redis/user"
              - Effect: Allow
                Action:
                  - "dynamodb:PutItem"
                  - "dynamodb:Query"
                  - "dynamodb:GetItem"
                  - "dynamodb:UpdateItem"
                  - "dynamodb:DeleteItem"
                  - "dynamodb:GetRecords"
                Resource:
                  - !GetAtt MyTable.Arn
                  - !Sub "${MyTable.Arn}/*"

  AppRunnerService:
    Type: AWS::AppRunner::Service
    Properties:
      ServiceName: MyService
      InstanceConfiguration:
        Cpu: 256
        Memory: 512
        InstanceRoleArn: !GetAtt InstanceRole.Arn
      HealthCheckConfiguration:
        Protocol: HTTP
        Path: /
      SourceConfiguration:
        AutoDeploymentsEnabled: false
        AuthenticationConfiguration:
          AccessRoleArn: !GetAtt AppRunnerRole.Arn
        ImageRepository:
          ImageRepositoryType: ECR
          ImageIdentifier: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${self:custom.ecr-repo.myimage:myimagetag"
          ImageConfiguration:
            Port: 8080
            RuntimeEnvironmentVariables:
              - Name: STAGE
                Value: ${self:provider.stagen.${self:provider.stage}}
            RuntimeEnvironmentSecrets:
              - Name: REDIS_USER
                Value: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/dev/redis/user"

Outputs:
  AppRunnerServiceUrl:
    Value: !GetAtt AppRunnerService.ServiceUrl
    Description: The URL of the service