如何在CDK中为基于Lambda的APIGateway提供自定义域名?

scu*_*bbo 9 amazon-web-services amazon-route53 aws-api-gateway aws-cdk

出于此问题的目的,假设我example.org在 Route53 中已经有一个托管区域(当然,我的实际区域是不同的)

使用以下 CDK 应用程序:

export class MyExampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const backend = new Function(this, 'backendLambda', {
      code: new AssetCode("lambda/"),
      handler: "index.handler",
      runtime: Runtime.PYTHON_3_8
    });

    apiDomainName = 'api.test.example.org'
    const api = new LambdaRestApi(this, 'api', {
      handler: backend,
      proxy: true,
      deploy: true,
      domainName: { 
        domainName: apiDomainName,
        certificate: new Certificate(this, 'apiCertificate', {
          domainName: apiDomainName
        })
      }
    });

  }
}
Run Code Online (Sandbox Code Playgroud)

,当我运行时cdk deploy,部分输出如下:

Outputs:
MyExampleStack.apiEndpoint0F54D2EA = https://<alphanumericId>.execute-api.us-east-1.amazonaws.com/prod/
Run Code Online (Sandbox Code Playgroud)

,事实上,当我访问curl该 url 时,我看到了我期望从 Lambda 代码中得到的响应。我希望curlingapi.test.example.org给出相同的结果 - 然而,它给出的却是curl: (6) Could not resolve host: api.test.example.org.

根据这个文档,我尝试过:

export class MyExampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const rootDomain = 'example.org'
    
    const zone = HostedZone.fromLookup(this, 'baseZone', {
      domainName: rootDomain
    });
    
    const backend = new Function(...);

    const api = new LambdaRestApi(...);
    new ARecord(this, 'apiDNS', {
      zone: zone,
      recordName: 'api.test',
      target: RecordTarget.fromAlias(new ApiGateway(api))
    });

  }
}
Run Code Online (Sandbox Code Playgroud)

其中确实给出了 Route53 条目:

$ aws route53 list-hosted-zones
{
    "HostedZones": [
        {
            "Id": "/hostedzone/ZO3B2N6W70PDD",
            "Name": "example.org.",
            "CallerReference": "598D71AB-4A98-EC5A-A170-D51CB243A2EA",
            "Config": {
                "PrivateZone": false
            },
            "ResourceRecordSetCount": 8
        }
    ]
}
$ aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/ZO3B2N6W70PDD --query 'ResourceRecordSets[?Name==`api.test.example.org.`]'
[
    {
        "Name": "api.test.example.org.",
        "Type": "A",
        "AliasTarget": {
            "HostedZoneId": "Z1UJRXOUMOOFQ9",
            "DNSName": "<alphanumericId2>.execute-api.us-east-1.amazonaws.com.",
            "EvaluateTargetHealth": false
        }
    }
]
Run Code Online (Sandbox Code Playgroud)

但这仍然不起作用:

  • curl api.test.example.org仍然给出“无法解析主机”
  • curl <alphanumericId2>.execute-api.us-east-1.amazonaws.com给出curl: (7) Failed to connect to <alphanumericId2>.execute-api.us-east-1.amazonaws.com port 80: Connection refused
  • curl https://<alphanumericId2>..execute-api.us-east-1.amazonaws.com给出{"message":"Forbidden"}
  • curl https://<alphanumericId>.[...](即 的输出cdk deploy)仍然给出 Lambda 的预期响应

如何在 Route53 中定义自定义名称以路由到我的 Lambda 支持的 APIGateway API?

Bal*_*ala 15

总体代码 LambdaRestApi 和 Route53 A Record,将创建

  • 指向特定阶段的自定义域prod,即api.test.example.org阶段“prod”的域(示例)
  • api.test.example.orgRoute 53指向 Api 网关托管区域的记录。

这是两种可行的组合

  • https://api.test.example.org将直接指向阶段产品。
  • CDK 输出https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/将在附加阶段后工作。

这些是一些行不通的组合

  • 您使用 http:// 进行的另外两个测试
  • 如果没有协议,默认为 http,将无法工作,因为我们的 api 网关默认提供 TLS 1.0 (ssl-https) 并且没有 http 侦听器。
  • 您对 https:// 进行的另一次尝试(末尾没有舞台名称)将返回403 禁止,因为缺少舞台名称。

这是完整的 CDK 代码。

import * as cdk from "@aws-cdk/core";
import * as apigw from "@aws-cdk/aws-apigateway";
import * as acm from "@aws-cdk/aws-certificatemanager";
import * as route53 from "@aws-cdk/aws-route53";
import * as route53Targets from "@aws-cdk/aws-route53-targets";
import * as lambda from "@aws-cdk/aws-lambda";

export class HelloCdkStack extends cdk.Stack {

constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    this.buildLambdaApiGateway();
  }

    buildLambdaApiGateway() {
    const rootDomain = "example.org";

    const zone = route53.HostedZone.fromLookup(this, "baseZone", {
      domainName: rootDomain,
    });

    const backend = new lambda.Function(this, "MyLayeredLambda", {
      code: new lambda.InlineCode("foo"),
      handler: "index.handler",
      runtime: lambda.Runtime.NODEJS_10_X,
    });

    const restApi = new apigw.LambdaRestApi(this, "myapi", {
      handler: backend,
      domainName: {
        domainName: `api-test.${rootDomain}`,
        certificate: acm.Certificate.fromCertificateArn(
          this,
          "my-cert",
          "arn:aws:acm:us-east-1:111112222333:certificate/abcd6805-1234-4159-ac38-761acdc700ef"
        ),
        endpointType: apigw.EndpointType.REGIONAL,
      },
    });

    new route53.ARecord(this, "apiDNS", {
      zone: zone,
      recordName: "api-test",
      target: route53.RecordTarget.fromAlias(
        new route53Targets.ApiGateway(restApi)
      ),
    });
  }
}
Run Code Online (Sandbox Code Playgroud)