Application code
npm install koa
// index.js
const Koa = require("koa");
const application = new Koa();
application.use(async (ctx) => {
ctx.body = "Hello, World!";
});
application.listen(8000);
Dockerfile
FROM node:14-alpine
WORKDIR /usr/src/app
COPY package*.json ./it
RUN npm ci --production
COPY . .
EXPOSE 8080
CMD [ "node", "index.js" ]
Terraform
- Run
terraform apply -target aws_ecr_repository.ecr_repository
- Deploy an initial version of your image (see script below)
- Run
terraform apply
If the provisioning of the AWS App Runner service takes more than 5-10 minutes, something is probably wrong.
locals {
ecr_repository_name = "hello-world"
service_name = "hello-world"
service_port = 8000
service_release_tag = "latest"
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
resource "aws_ecr_repository" "ecr_repository" {
name = local.ecr_repository_name
image_scanning_configuration {
scan_on_push = true
}
}
resource "aws_ecr_lifecycle_policy" "ecr_lifecycle_policy" {
repository = aws_ecr_repository.ecr_repository.name
policy = jsonencode({
"rules" : [
{
"rulePriority" : 1,
"description" : "Expire untagged images older than 14 days",
"selection" : {
"tagStatus" : "untagged",
"countType" : "sinceImagePushed",
"countUnit" : "days",
"countNumber" : 14
},
"action" : {
"type" : "expire"
}
}
]
})
}
resource "aws_iam_role" "runner_role" {
name = "${local.service_name}-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "build.apprunner.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "runner_role_policy_attachment" {
role = aws_iam_role.runner_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess"
}
resource "aws_apprunner_service" "runner_service" {
service_name = local.service_name
source_configuration {
authentication_configuration {
access_role_arn = aws_iam_role.runner_role.arn
}
image_repository {
image_identifier = "${aws_ecr_repository.ecr_repository.repository_url}:${local.service_release_tag}"
image_repository_type = "ECR"
image_configuration {
port = local.service_port
}
}
}
}
output "service_url" {
value = aws_apprunner_service.runner_service.service_url
}
Custom domain
- Run
terraform apply -target aws_apprunner_custom_domain_association.runner_custom_domain
- Run
terraform apply
I have seen the DNS validation take up to 20 minutes.
locals {
custom_domain = "test.com"
}
resource "aws_apprunner_custom_domain_association" "runner_custom_domain" {
domain_name = local.custom_domain
service_arn = aws_apprunner_service.runner_service.arn
}
resource "aws_route53_record" "runner_custom_domain_record" {
allow_overwrite = true
name = local.custom_domain
records = [
aws_apprunner_custom_domain_association.runner_custom_domain.dns_target
]
ttl = 60
type = "CNAME"
zone_id = aws_route53_zone.hosted_zone.zone_id
}
resource "aws_route53_record" "runner_custom_domain_validation_record" {
for_each = {for r in aws_apprunner_custom_domain_association.runner_custom_domain.certificate_validation_records : r.name => r}
allow_overwrite = true
name = each.value.name
records = [
each.value.value
]
ttl = 60
type = each.value.type
zone_id = aws_route53_zone.hosted_zone.zone_id
}
Deploy script
#!/bin/bash
set -e
ACCOUNT_ID="your-account-id"
REGION="your-region"
REPOSITORY="hello-world"
RELEASE_TAG="latest"
IMAGE_URI="$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPOSITORY:$RELEASE_TAG"
docker build -t "$REPOSITORY:$RELEASE_TAG" .
docker tag "$REPOSITORY:$RELEASE_TAG" "$IMAGE_URI"
aws ecr get-login-password --region "$REGION" | docker login --username AWS --password-stdin "$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com"
docker push "$IMAGE_URI"