AWS App Runner: Simplified Container and Source Code Deployment
AWS

AWS App Runner: Simplified Container and Source Code Deployment

Learn how to use AWS App Runner to deploy and run containerized web applications and APIs with automatic scaling and load balancing

March 15, 2024
DevHub Team
5 min read

AWS App Runner: Simplified Container and Source Code Deployment

AWS App Runner is a fully managed service that makes it easy to deploy containerized web applications and APIs at scale. This guide explores its features, use cases, and best practices.

Service Overview

graph TB subgraph AppRunner["AWS App Runner"] direction TB Source["Source Code/Container"] Build["Build Process"] Deploy["Deployment"] Run["Runtime"] end subgraph Features["Service Features"] direction TB AS["Auto Scaling"] LB["Load Balancing"] SSL["SSL/TLS"] VPC["VPC Integration"] end Source --> Build Build --> Deploy Deploy --> Run Run --> Features classDef aws fill:#FF9900,stroke:#232F3E,color:#232F3E class AppRunner,Features aws

Getting Started

1. Source Code Deployment

Example Node.js application:

// app.js const express = require('express'); const app = express(); const port = process.env.PORT || 3000; app.get('/', (req, res) => { res.json({ message: 'Hello from App Runner!' }); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });

Configuration file:

# apprunner.yaml version: 1.0 runtime: nodejs12 build: commands: pre-build: - npm install build: - npm run build run: command: node app.js network: port: 3000

2. Container Deployment

Dockerfile example:

FROM node:14-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["node", "app.js"]

Infrastructure as Code

AWS CDK Implementation

import * as apprunner from '@aws-cdk/aws-apprunner-alpha'; import * as ecr from 'aws-cdk-lib/aws-ecr'; // Create App Runner service from container const service = new apprunner.Service(this, 'Service', { source: apprunner.Source.fromEcr({ imageConfiguration: { port: 3000, }, repository: ecr.Repository.fromRepositoryName( this, 'Repo', 'my-app-repo' ), tag: 'latest', }), });

CloudFormation Template

Resources: AppRunnerService: Type: AWS::AppRunner::Service Properties: ServiceName: my-web-app SourceConfiguration: AuthenticationConfiguration: AccessRoleArn: !GetAtt AppRunnerECRAccessRole.Arn ImageRepository: ImageIdentifier: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/my-app:latest ImageConfiguration: Port: 3000 InstanceConfiguration: Cpu: 1024 Memory: 2048

Auto Scaling Configuration

{ "AutoScalingConfigurationName": "production-config", "MaxConcurrency": 100, "MinSize": 1, "MaxSize": 25, "Tags": [ { "Key": "Environment", "Value": "Production" } ] }

Networking and Security

VPC Configuration

Resources: AppRunnerVpcConnector: Type: AWS::AppRunner::VpcConnector Properties: VpcConnectorName: my-vpc-connector Subnets: - subnet-123456 - subnet-789012 SecurityGroups: - sg-123456

IAM Configuration

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage" ], "Resource": "*" } ] }

Monitoring and Logging

CloudWatch Integration

import boto3 import datetime cloudwatch = boto3.client('cloudwatch') def get_app_metrics(): response = cloudwatch.get_metric_statistics( Namespace='AWS/AppRunner', MetricName='RequestCount', Dimensions=[ { 'Name': 'ServiceName', 'Value': 'my-web-app' } ], StartTime=datetime.datetime.utcnow() - datetime.timedelta(hours=1), EndTime=datetime.datetime.utcnow(), Period=60, Statistics=['Sum'] ) return response

Custom Metrics

const AWS = require('aws-sdk'); const cloudwatch = new AWS.CloudWatch(); async function publishCustomMetric(value) { await cloudwatch.putMetricData({ Namespace: 'MyAppMetrics', MetricData: [{ MetricName: 'BusinessTransactions', Value: value, Unit: 'Count', Dimensions: [{ Name: 'ServiceName', Value: 'my-web-app' }] }] }).promise(); }

CI/CD Integration

GitHub Actions Workflow

name: Deploy to App Runner on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-west-2 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Build and push image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: my-app IMAGE_TAG: latest run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - name: Deploy to App Runner run: | aws apprunner update-service \ --service-arn ${{ secrets.APPRUNNER_SERVICE_ARN }} \ --source-configuration imageRepository={imageIdentifier=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG}

Performance Optimization

1. Resource Configuration

# Optimal resource configuration Resources: AppRunnerService: Type: AWS::AppRunner::Service Properties: InstanceConfiguration: Cpu: 1 vCPU Memory: 2 GB AutoScalingConfigurationArn: !Ref AutoScalingConfig HealthCheckConfiguration: Path: /health Protocol: TCP Interval: 10 Timeout: 5 HealthyThreshold: 2 UnhealthyThreshold: 3

2. Caching Strategy

const express = require('express'); const app = express(); const cache = require('memory-cache'); app.get('/api/data', (req, res) => { const key = req.url; const cachedResponse = cache.get(key); if (cachedResponse) { return res.json(cachedResponse); } // Generate response const response = generateResponse(); cache.put(key, response, 300000); // Cache for 5 minutes res.json(response); });

Cost Optimization

1. Auto Scaling Configuration

{ "AutoScalingConfigurationName": "cost-optimized", "MaxConcurrency": 50, "MinSize": 1, "MaxSize": 10, "Tags": [ { "Key": "CostCenter", "Value": "WebApp" } ] }

2. Resource Utilization

# Resource optimization example Resources: OptimizedService: Type: AWS::AppRunner::Service Properties: InstanceConfiguration: Cpu: "1024" # 1 vCPU Memory: "2048" # 2 GB AutoScalingConfigurationArn: !Ref CostOptimizedConfig ObservabilityConfiguration: ObservabilityEnabled: true TraceConfiguration: Vendor: AWSXRAY

Best Practices

1. Application Design

  • Use health checks
  • Implement graceful shutdown
  • Handle environment variables
  • Optimize container size

2. Operational Excellence

  • Enable observability
  • Implement proper logging
  • Use infrastructure as code
  • Regular monitoring and alerts

3. Security

  • Use VPC connectivity
  • Implement least privilege
  • Enable encryption
  • Regular security updates

Troubleshooting Guide

Common issues and solutions:

  1. Deployment Failures

    • Check build logs
    • Verify source configuration
    • Validate container image
    • Check resource limits
  2. Performance Issues

    • Monitor metrics
    • Check resource utilization
    • Validate auto-scaling
    • Review application logs

References

  1. AWS App Runner Documentation
  2. App Runner Best Practices
  3. Pricing Information
  4. App Runner Workshop
  5. Container Security
  6. Auto Scaling Guide

Related Posts

App Runner
Containers
Serverless
DevOps