[英]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.我正在尝试使用 AWS-CDK 将一个相当基本的 Nodejs CRUD API 部署到 AWS。 The service runs in a docker container and I'm deploying it to an ECS Fargate cluster behind an ALB.
该服务在 docker 容器中运行,我将其部署到 ALB 后面的 ECS Fargate 集群。 I also have a domain in Route53 that I'm trying to use.
我在 Route53 中也有一个我正在尝试使用的域。
The problem I'm having is I can't seem to access the ALB through the domain.我遇到的问题是我似乎无法通过域访问 ALB。 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.
我可以通过 HTTP 使用其默认 AWS DNS (XXXXX.us-west-2.elb.amazonaws.com/) 直接访问 ALB,但是当我尝试通过域访问它时会出现 504 超时。
I'm pretty new to AWS and CDK, so I'm sure I'm missing something obvious here.我对 AWS 和 CDK 很陌生,所以我确定我在这里遗漏了一些明显的东西。 Any advice or recommended resources/examples would be much appreciated.
任何建议或推荐的资源/示例将不胜感激。 Here's my CDK code:
这是我的 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,
});
}
}
And this class gets created in my bin/infra.ts file:这个类是在我的 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,
},
});
And here's the Dockerfile in case it's useful.这是 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
Why am I getting 504 errors when I access my service through my domain?当我通过我的域访问我的服务时,为什么会收到 504 错误? Or where can I look to get a better idea of what I'm missing?
或者我在哪里可以更好地了解我错过了什么?
CloudFront talks HTTPS (port 443) to its origins by default.默认情况下,CloudFront 将 HTTPS(端口 443)与其源通信。 An ALB (regardless of whether created explicitly or implicitly by the
ApplicationLoadBalancedFargateService
construct) listens on HTTP (port 80) by default unless explicitly set up for HTTPS.默认情况下,ALB(无论是由
ApplicationLoadBalancedFargateService
构造显式创建还是隐式创建)侦听 HTTP(端口 80),除非显式设置为 HTTPS。 As your ALB is not configured to listen on HTTPS, CloudFront attempts to talk HTTPS to an ALB that only listens on HTTP.由于您的 ALB 未配置为侦听 HTTPS,CloudFront 尝试将 HTTPS 与仅侦听 HTTP 的 ALB 通信。
To fix that, set origin.protocolPolicy
to OriginProtocolPolicy.HTTP_ONLY
, which instructs CloudFront to talk HTTP to your ALB.要解决此问题,请将
origin.protocolPolicy
设置为OriginProtocolPolicy.HTTP_ONLY
,这会指示 CloudFront 将 HTTP 与您的 ALB 通信。 Please note that the below code uses CDK v2 and also, I've used LoadBalancerV2Origin
over HttpOrigin
, although both should work the same.请注意,下面的代码使用 CDK v2,而且我使用
LoadBalancerV2Origin
而不是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,
})
},
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.