AWS CDK - 无法通过 Cloudfront 域访问 ALB

Sim*_*leJ 3 amazon-web-services amazon-cloudfront docker aws-fargate aws-cdk

我正在尝试使用 AWS-CDK 将相当基本的 Nodejs CRUD API 部署到 AWS。该服务在 Docker 容器中运行,我将其部署到 ALB 后面的 ECS Fargate 集群。我在 Route53 中也有一个我正在尝试使用的域。

我遇到的问题是我似乎无法通过域访问 ALB。我可以通过 HTTP 使用其默认 AWS DNS (XXXXX.us-west-2.elb.amazonaws.com/) 直接访问 ALB,但当我尝试通过域访问它时,出现 504 超时。

我对 AWS 和 CDK 还很陌生,所以我确信我在这里遗漏了一些明显的东西。任何建议或推荐的资源/示例将不胜感激。这是我的 CDK 代码:

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,
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

这个类是在我的 bin/infra.ts 文件中创建的:

#!/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,
  },
});
Run Code Online (Sandbox Code Playgroud)

这是 Dockerfile,以防万一它有用。

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
Run Code Online (Sandbox Code Playgroud)

当我通过我的域访问我的服务时,为什么会收到 504 错误?或者我可以在哪里寻找以更好地了解我所缺少的内容?

Ste*_*seg 5

默认情况下,CloudFront 通过 HTTPS(端口 443)与其源进行对话。ALB(无论是由构造显式创建还是隐式创建ApplicationLoadBalancedFargateService)默认侦听 HTTP(端口 80),除非显式设置为 HTTPS。由于您的 ALB 未配置为侦听 HTTPS,CloudFront 会尝试与仅侦听 HTTP 的 ALB 进行 HTTPS 通信。

要解决此问题,请设置origin.protocolPolicyOriginProtocolPolicy.HTTP_ONLY,这会指示 CloudFront 与您的 ALB 进行 HTTP 通信。请注意,下面的代码使用 CDK v2,而且我也使用了LoadBalancerV2Originover HttpOrigin,尽管两者的工作原理应该相同。

const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', {
  vpc,
  internetFacing: true,
});
new cloudfront.Distribution(this, 'myDist', {
  defaultBehavior: {
    new origins.LoadBalancerV2Origin(loadBalancer, {
      protocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
    })
  },
});
Run Code Online (Sandbox Code Playgroud)