Containers
Deploy applications in AWS App Runner with GitHub Actions
Overview
AWS App Runner is a fully managed service that makes it easy for developers to quickly deploy containerized web applications and APIs at scale and with no prior infrastructure experience required. Starting with the source code or a container image, App Runner will automatically build, scale, and secure the web application in the AWS Cloud.
In this post, we will explore how organizations using GitHub as a source code repository can use GitHub Actions to deploy their applications on App Runner.
If you are new to AWS App Runner, check out the getting started resources.
Background
For organizations using GitHub as a source code repository, GitHub Actions provide a way to implement complex orchestration and CI/CD functionality directly in GitHub by initiating a workflow on any GitHub event.
Integrating App Runner with GitHub Action enables customers to provide a self-serve capability for application developers to quickly develop, prototype, and deploy their applications at scale. This will help development teams reduce the dependency on the platform team, which may be small, for deploying, managing, and upgrading these services to support faster innovation.
A GitHub Action is an individual unit of functionality that can be combined with other GitHub Actions to create workflows. These workflows are triggered in response to certain GitHub events, such as pull, push, or commit. Workflows run inside managed environments on GitHub-hosted servers.
In this post, we will be using the following AWS open-sourced GitHub Actions under the github.com/aws-actions repository to demonstrate App Runner deployment:
- github.com/aws-actions/configure-aws-credentials: Configure AWS credential and Region environment variables for use in other GitHub Actions
- github.com/aws-actions/amazon-ecr-login: Log in the local Docker client to one or more Amazon Elastic Container Registry (Amazon ECR) registries
Prerequisites
- A GitHub account: This post assumes you have the required permissions to configure GitHub repositories, create workflows, and configure GitHub secrets.
- Create a new GitHub repository and clone it to your local environment. Refer to the Getting Started documentation for more detailed instructions. For this example, create a repository called
github-actions-with-app-runner
. - Install AWS Command Line Interface (AWS CLI) locally. This is separate from using the AWS CLI in a GitHub Actions runner. If you use AWS Cloud9 as your integrated development environment (IDE), AWS CLI is pre-installed.
node
andnpm
needs to be installed in the local environment. Refer to this documentation for more detailed steps.- An AWS user with access keys, which the GitHub Actions runner uses to deploy the application: Refer to these instructions for creating an AWS access key and secret.
- For source-based deployment, the App Runner service requires access to the source image repository. For more details, refer to the documentation.
- For image-based deployment, the App Runner service requires access to the source image repository. This is required for Amazon ECR image repositories, but not for Amazon ECR public repositories). For more details around IAM policies and roles, refer to this documentation.
The following are the step-by-step instructions to create a service role and associating AWSAppRunnerServicePolicyForECRAccess
policy with the preceding mentioned permissions.
- Create
trust-policy.json
file with trust policy{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "build.apprunner.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
- Create new role
app-runner-service-role
withtrust-policy.json
aws iam create-role --role-name app-runner-service-role \ --assume-role-policy-document file://trust-policy.json
- Attach
AWSAppRunnerServicePolicyForECRAccess
IAM policy toapp-runner-service-role
IAM role.aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess \ --role-name app-runner-service-role
Configuring AWS credentials and source code connection Amazon Resource Name (ARN) in GitHub
The GitHub Actions CI/CD pipeline requires AWS credentials to access your AWS account. The credentials must include AWS Identity and Access Management (IAM) policies that provide access to App Runner and IAM resources.
These credentials are stored as GitHub secrets within your GitHub repository, under Settings > Secrets. For more information, see GitHub Actions secrets.
Create the following secrets in your GitHub repository:
- Create two secrets named
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
and enter the key values. We recommend following IAM best practices for the AWS credentials used in GitHub Actions workflows, including:- Do not store credentials in your repository code. Use GitHub Actions secrets to store credentials and redact credentials from GitHub Actions workflow logs.
- Create an individual IAM user with an access key for use in GitHub Actions workflows, preferably one per repository. Do not use the AWS account root user access key.
- Grant least privilege to the credentials used in GitHub Actions workflows. Grant only the permissions required to perform the actions in your GitHub Actions workflows. Here is the minimum set of permission needed, make sure to replace AwsAccountNumber with the actual AWS Account number:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "apprunner:*", "Resource": "*" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action":[ "iam:PassRole", "iam:CreateServiceLinkedRole" ], "Resource": "*" }, { "Sid": "VisualEditor2", "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::<<AwsAccountNumber>>:role/app-runner-service-role" }, { "Sid": "VisualEditor3", "Effect": "Allow", "Action": [ "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "ecr:BatchCheckLayerAvailability", "ecr:PutImage", "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken" ], "Resource": "*" } ] }
- Rotate the credentials used in GitHub Actions workflows regularly.
- Monitor the activity of the credentials used in GitHub Actions workflows.
- Define AWS Region by creating a secret with name
AWS_REGION
and entering the Region where the App Runner service needs to be created. - For code-based services, create a secret with name
AWS_CONNECTION_SOURCE_ARN
and enter in the Amazon Resource Name (ARN) of the source code connector in App Runner. - For image-based services, create a secret with name
ROLE_ARN
and enter in the ARN of the IAM role that grants access to the source image repository.
Once the secrets are configured, the following image is how it appears in in GitHub.
Note: Usage or ROLE_ARN
and AWS_CONNECTION_SOURCE_ARN
will vary depending upon the type of App Runner service used (source code based or container image based).
Usage
This GitHub Action supports two types of App Runner services: source code-based and container image-based. We will see an example of how to use these different methods to deploy the sample application in App Runner.
Creating the sample application
- Create a package.json inside the root directory file by running the npm init command. Accept the default values.
package name: (sample-nodejs-service)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to .../github-actions-with-app-runner/sample-nodejs-service/package.json:
{ "name": "sample-nodejs-service", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
- Add
Express
dependency by running the following command:npm install express
After running the command,package.json
will look like the following:{ ... "dependencies": { "express": "^4.17.1" } ... }
- Add a start script entry to the
package.json
file:"scripts": { "start": "node index.js" }
Note: Remove
test
line from thescripts
definition. - Create an
index.js
file inside the same directory and add the following code:const express = require('express'); const app = express(); app.get('/', (req, res) => { res.send('Running on AWS App Runner Service !'); }); const PORT = process.env.PORT || 8080; app.listen(PORT, () => { console.log(`Server listening on port ${PORT}...`); });
- To start the application, run the following command:
npm start
- Open the browser and navigate to http://localhost:8080
Running on AWS App Runner Service !
Code-based service
App Runner uses “AWS Connector for GitHub” to point to an existing source code repository in GitHub with the suitable runtime. App Runner will build the container image based on the language/platform runtime and starts the service based on the generated image. For supported runtimes, refer to the documentation.
Here is the GitHub Action high-level view of the end-to-end flow:
Notes:
- The workflow uses the
aws-actions/configure-aws-credentials
action to configure the environment for GitHub Actions using the environment variables containing AWS credentials (specified part of GitHub secrets) and your desired Region. - Based on the pipeline trigger (like
push
,commit
, etc.), the workflow will first configure the environment and then create the corresponding App Runner service to deploy the application.
Deploying the sample application
- Create a new directory
.github/workflows
under the root directory. Create a new filepipeline.yml
under the.github/workflows
. - Edit the pipeline.yml file and add the following:
name: Deploy to App Runner - Source # Name of the workflow on: push: branches: [ main ] # Trigger workflow on git push to main branch workflow_dispatch: # Allow manual invocation of the workflow jobs: deploy: runs-on: ubuntu-latest steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 # Configure with AWS Credentials with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} - name: Deploy to App Runner id: deploy-apprunner uses: awslabs/amazon-app-runner-deploy@main # Deploy app runner service with: service: app-runner-git-deploy-service source-connection-arn: ${{ secrets.AWS_CONNECTION_SOURCE_ARN }} repo: https://github.com/${{ github.repository }} branch: ${{ github.ref }} runtime: NODEJS_12 build-command: npm install start-command: npm start port: 18000 region: ${{ secrets.AWS_REGION }} cpu : 1 memory : 2 wait-for-service-stability: true - name: App Runner output run: echo "App runner output ${{ steps.deploy-apprunner.outputs.service-id }}"
- Make sure that the appropriate values are set for GitHub secrets
AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
,AWS_REGION
andAWS_CONNECTION_SOURCE_ARN
The configuration triggers the GitHub Actions CI/CD pipeline when code is pushed to the main branch. You can amend this if you are using another branch. For a full list of supported events, refer to the GitHub documentation page. - Create
.gitignore
file under git root directory and addnode_modules
to the list of excluded folders. - Add all the files to your local git repository, commit the changes, and push to GitHub by running the following command from the root directory.
git add . git commit -am "Initial commit with pipline" git push
Note: Once the files are pushed to GitHub on the main branch, this automatically triggers the GitHub Actions workflow as configured in the
pipeline.yml
file. Pipeline execution will take couple of minutes to complete. Upon completion, the App Runner service will be created in the specified AWS Region successfully.
Testing the application
- Log in to the App Runner console, and you should see a new service with the app-runner-git-deploy-service name configured.
- Open the browser and navigate to the URL mentioned following the
Default domain
section in the console page and we should see the service up and running. - Output: Running on AWS App Runner Service!
Image-based service
For and image-based service, the source image can be a public or private container image stored in an image repository. It can be used by App Runner to deploy the application without any build stage.
Here is the GitHub Action high-level view of the end-to-end flow:
Notes:
- The workflow uses the
aws-actions/configure-aws-credentials
action to configure the environment for GitHub Actions using the environment variables containing AWS credentials (specified part of GitHub secrets) and your desired Region. - In the next step, we will build the Docker image and push the generated image to Amazon ECR using the
aws-actions/amazon-ecr-login@v1
action. - Once the image is available in Amazon ECR,
awslabs/amazon-app-runner-deploy@main
will create a new App Runner service and deploy the application inside the container.
Updates to the sample application
We will be using the proceeding sample application to show how to provision an App Runner image-based service, but we will make some changes.
- Create a Dockerfile inside the root directory and add the following lines to the file
FROM node:12-slim
WORKDIR /usr/src/app
COPY package*.json ./
COPY index.js ./
RUN npm ci
COPY . .
EXPOSE 8080
CMD [ "node", "index.js" ] - Run the following command to create an Amazon ECR repository
aws ecr create-repository --repository-name nodejs
- Remove the following files/folders before proceeding to next step
node_modules
,.github/workflow/pipeline.yml
- Create a file under
.github/workflow
with the nameimage-pipeline.yml
with the following contentsname: Deploy to App Runner - Image based # Name of the workflow on: push: branches: [ main ] # Trigger workflow on git push to main branch workflow_dispatch: # Allow manual invocation of the workflow jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: persist-credentials: false - name: Configure AWS credentials id: 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: ${{ secrets.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Build, tag, and push image to Amazon ECR id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: nodejs IMAGE_TAG: ${{ github.sha }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - name: Deploy to App Runner id: deploy-apprunner uses: awslabs/amazon-app-runner-deploy@main with: service: app-runner-image-deploy-service image: ${{ steps.build-image.outputs.image }} access-role-arn: ${{ secrets.ROLE_ARN }} runtime: NODEJS_12 region: ${{ secrets.AWS_REGION }} cpu : 1 memory : 2 port: 8080 wait-for-service-stability: true - name: App Runner output run: echo "App runner output ${{ steps.deploy-apprunner.outputs.service-id }}"
- Make sure that the appropriate values are set for GitHub Secrets
AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
,AWS_REGION
, andROLE_ARN
The configuration triggers the GitHub Actions CI/CD pipeline when code is pushed to the main branch. - Add all the files to your local git repository, commit the changes, and push to GitHub. The following is the view of the folder structure:
Run the following command from the root directory.
git add . git commit -am "Initial commit with image pipline" git push
Once the files are pushed to GitHub on the main branch, this automatically triggers the GitHub Actions workflow as configured in the
image-pipeline.yml
file. Pipeline execution will take couple of minutes to complete. Upon completion, the App Runner service will be created in the specified AWS Region successfully.
Testing the application
- Log in to the App Runner console, and you should see a new service with the
app-runner-image-deploy-service
name configured.
- Open the browser and navigate to URL mentioned following the
Default domain
section in the console. You should see the service up and running. - Output: Running on AWS App Runner service!
Cleanup
- Log in to the App Runner console, select the
app-runner-image-deploy-service
service, and select Actions → Delete. Repeat the same forapp-runner-git-deploy-service
service, as well. - Delete the Amazon ECR repository by running the following command:
aws ecr delete-repository \
--repository-name nodejs \
--force
- Delete the IAM role by running the following command:
- Detach IAM policy
aws iam detach-role-policy \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess \
--role-name app-runner-service-role
- Delete IAM role
aws iam delete-role --role-name app-runner-service-role
- Detach IAM policy
- Delete the existing “GitHub connection” by following the instructions available of the documentation.
- Mark AWS access key and AWS secret that we created part of the prerequisites section of the blog as inactive.
- Navigating to IAM users console, select the user for this example
- Select the
Security credentials
tab. - Select
Mark inactive
button next to the appropriate AWS credentials used in this example.
- Delete the GitHub repository used in this example:
- Navigate to https://github.com/<<username>>/
github-actions-with-app-runner/settings
. Replace the user name with the GitHub user name of the repository owner - Select
Delete this repository
and confirm the deletion
- Navigate to https://github.com/<<username>>/
Conclusion
GitHub Actions is a GitHub feature that enables you to run a CI/CD pipeline to build, test, and deploy software directly from GitHub. Since App Runner continuously monitors the source code and Amazon ECR image repository, any changes to these artifacts will automatically trigger a new deployment of the latest changes with no manual interruption.
In this post, we used GitHub Actions to deploy the sample application in App Runner as both a source code and an image-based service. The GitHub Action amazon-app-runner-deploy
uses the App Runner API to build and deploy the application using the App Runner service with the specified configuration.