'AWS CDK - Can't access ALB through Cloudfront domain

I'm trying to deploy a fairly basic Nodejs CRUD API to AWS using AWS-CDK. The service runs in a docker container and I'm deploying it to an ECS Fargate cluster behind an ALB. I also have a domain in Route53 that I'm trying to use.

The problem I'm having is I can't seem to access the ALB through the domain. I can access the ALB directly using its default AWS DNS (XXXXX.us-west-2.elb.amazonaws.com/) over HTTP, but I get 504 timeouts when I attempt to access it through the domain.

I'm pretty new to AWS and CDK, so I'm sure I'm missing something obvious here. Any advice or recommended resources/examples would be much appreciated. Here's my CDK code:

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as Cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as CloudfrontOrigins from "aws-cdk-lib/aws-cloudfront-origins";
import * as Route53 from "aws-cdk-lib/aws-route53";
import * as Route53Targets from "aws-cdk-lib/aws-route53-targets";
import * as ACM from "aws-cdk-lib/aws-certificatemanager";
import * as EC2 from "aws-cdk-lib/aws-ec2";
import * as ECS from "aws-cdk-lib/aws-ecs";
import * as EcsPatterns from "aws-cdk-lib/aws-ecs-patterns";

interface Props extends StackProps {
  domainName: string;
  dockerDir: string;
}

export class AppStack extends Stack {
  constructor(scope: Construct, id: string, { domainName, dockerDir, ...rest }: Props) {
    super(scope, id, rest);

    const hostedZone = Route53.HostedZone.fromLookup(this, `${id}_Zone`, {
      domainName,
    });

    const vpc = new EC2.Vpc(this, `${id}_Vpc`, { maxAzs: 2 });
    const cluster = new ECS.Cluster(this, `${id}_Ec2Cluster`, { vpc });
    cluster.addCapacity(`${id}_DefaultAutoScalingGroup`, {
      instanceType: EC2.InstanceType.of(
        EC2.InstanceClass.T3,
        EC2.InstanceSize.MICRO
      ),
      minCapacity: 1,
      maxCapacity: 3,
    });
    const certificate = new ACM.DnsValidatedCertificate(
      this,
      `${id}_SiteCertificate`,
      {
        domainName,
        hostedZone,
        region: "us-east-1",
      }
    );
  
    const fargateService = new EcsPatterns.ApplicationLoadBalancedFargateService(
      this,
      `${id}_FargateLoadBalancedService`,
      {
        cluster,
        desiredCount: 1,
        publicLoadBalancer: true,
        taskImageOptions: {
          image: ECS.ContainerImage.fromAsset(dockerDir),
          containerPort: 8000,
          environment: {
            PORT: '8000',
          },
        },
      }
    );
  
    const distribution = new Cloudfront.Distribution(
      this,
      `${id}_SiteDistribution`,
      {
        certificate,
        domainNames: [domainName],
        minimumProtocolVersion: Cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
        defaultBehavior: {
          origin: new CloudfrontOrigins.HttpOrigin(
            fargateService.loadBalancer.loadBalancerDnsName
          ),
          compress: false,
          cachePolicy: Cloudfront.CachePolicy.CACHING_DISABLED,
          allowedMethods: Cloudfront.AllowedMethods.ALLOW_ALL,
        },
      }
    );
  
  
    new Route53.ARecord(this, `${id}_SiteAliasRecord`, {
      recordName: domainName,
      target: Route53.RecordTarget.fromAlias(
        new Route53Targets.CloudFrontTarget(distribution)
      ),
      zone: hostedZone,
    });
  }
}

And this class gets created in my bin/infra.ts file:

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import * as path from "path";
import { AppStack } from "../lib/AppStack";

const appId = `MyApp`;
const app = new cdk.App();

new AppStack(app, `${appId}Stack`, {
  dockerDir: path.resolve(__dirname, "..", "api"), // contains the Dockerfile
  domainName: 'mydomain.com',
  env: {
    account: process.env.CDK_DEFAULT_ACCOUNT,
    region: process.env.CDK_DEFAULT_REGION,
  },
});

And here's the Dockerfile in case it's useful.

FROM node:16-alpine as builder

ENV NODE_ENV build

USER node
WORKDIR /home/node

COPY package*.json ./
RUN npm i

COPY --chown=node:node . .
RUN npm run build \
    && npm prune --production

# ---

FROM node:16-alpine

ENV PORT 8000
ENV NODE_ENV production

# Add curl for healthcheck
RUN apk --no-cache add curl

USER node
WORKDIR /home/node

COPY --from=builder --chown=node:node /home/node/package*.json ./
COPY --from=builder --chown=node:node /home/node/node_modules/ ./node_modules/
COPY --from=builder --chown=node:node /home/node/dist/ ./dist/

EXPOSE 8000
CMD ["node", "dist/main.js"]
HEALTHCHECK CMD curl -f http://localhost:8000/api/healthcheck || exit 1

Why am I getting 504 errors when I access my service through my domain? Or where can I look to get a better idea of what I'm missing?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source