Day 7 – AWS CDK 部署 Lambda 與 API Gateway 服務 (下)

2020 12th 鐵人賽

https://i0.wp.com/ithelp.ithome.com.tw/upload/images/20201024/20117701QzNO37YYln.jpg?w=640&ssl=1

昨天說明了 Lambda 串接 API Gateway 今天就依照昨天的內容繼續延伸吧!

以下有幾個大家可能會有的問題今天來回答他們

  • 問題一:ACM 我們之前應該就建立過了這個 ACM 想要繼續用舊的可以嗎?
  • 問題二:Lambda 除了用檔案方法部署之外我可以把 Lambda function 直接放在程式裡面嗎? 這樣就不用先上傳到 S3 了
  • 問題三:我們是不是可以在 Lambda 裡面寫我們對應的 API 程式呢?
  • 問題四:全部程式都放在同一個文件裡面好像不太好管理,可以拆開來嗎?

問題一:ACM 我們之前應該就建立過了這個 ACM 想要繼續用舊的可以嗎?

AWS CDK 印出 output

在說明如何如何使用舊的 ACM 之前先來說明如何在 CDK 印出 output 吧! 要印 output 就要使用 CfnOutput 這個 function 它是 CDK core 的基礎功能,使用方法如下:

const cert = new acm.Certificate(this, "Certificate", {
    domainName: "*.cdk.clarence.tw",
    validation: acm.CertificateValidation.fromDns(),
});
new cdk.CfnOutput(this, "ACM-ARN", {
    value: cert.certificateArn,
});

詳細說明可以看官方文件

那事不宜遲我們來印印看吧!

$ cdk deploy 
CdkLambdaStack: deploying...
[0%] start: Publishing 4570c9a06764894ceb3b53aa789576d8258408a69cfd3cf13dc7337b49bb50bd:current
[100%] success: Published 4570c9a06764894ceb3b53aa789576d8258408a69cfd3cf13dc7337b49bb50bd:current
CdkLambdaStack: creating CloudFormation changeset...


 ✅  CdkLambdaStack

Outputs:
CdkLambdaStack.AcmArn = arn:aws:acm:us-west-2:888888888888:certificate/e2f36ef1-6b06-4cb3-9128-6de273275f0d
CdkLambdaStack.Endpoint8024A810 = https://eli6fm96r2.execute-api.us-west-2.amazonaws.com/prod/

Stack ARN:
arn:aws:cloudformation:us-west-2:888888888888:stack/CdkLambdaStack/f95906d0-fa8f-11ea-ae89-0a43e2aa5bac

如此我們就知道 AcmArn 了

CDK 使用 Arn ID 建立 API Gateway certificate

我們來複習一下昨天建立 ACM 的方法

const cert = new acm.Certificate(this, "Certificate", {
  domainName: "*.cdk.clarence.tw",
  validation: acm.CertificateValidation.fromDns(),
});

我們來把 cert 換成用 Arn ID 來定義,假設我們的 ACM ID 為 arn:aws:acm:us-west-2:888888888888:certificate/bd476304-de15-4feb-b5f4-136fa60cae59 使用 function fromCertificateArn 定義

const cert = acm.Certificate.fromCertificateArn(
  this,
  "Certificate",
  "arn:aws:acm:us-west-2:888888888888:certificate/bd476304-de15-4feb-b5f4-136fa60cae59"
);
new cdk.CfnOutput(this, "AcmArn", {
  value: cert.certificateArn,
});

跑一下 deploy 可以看到我們上面指定的 AcmArn 與 Output 的 AcmArn是一樣的,那就是成功拉!

$ cdk deploy
CdkLambdaStack: deploying...
[0%] start: Publishing 4570c9a06764894ceb3b53aa789576d8258408a69cfd3cf13dc7337b49bb50bd:current
[100%] success: Published 4570c9a06764894ceb3b53aa789576d8258408a69cfd3cf13dc7337b49bb50bd:current
CdkLambdaStack: creating CloudFormation changeset...
[██████████████████████████████████████████████████████████] (3/3)




 ✅  CdkLambdaStack

Outputs:
CdkLambdaStack.AcmArn = arn:aws:acm:us-west-2:888888888888:certificate/bd476304-de15-4feb-b5f4-136fa60cae59
CdkLambdaStack.Endpoint8024A810 = https://eli6fm96r2.execute-api.us-west-2.amazonaws.com/prod/

Stack ARN:
arn:aws:cloudformation:us-west-2:888888888888:stack/CdkLambdaStack/f95906d0-fa8f-11ea-ae89-0a43e2aa5bac

詳細說明可以看官方文件

問題二:Lambda 除了用檔案方法部署之外我可以把 Lambda function 直接放在程式裡面嗎? 這樣就不用先上傳到 S3

CDK Lambda 使用 inline code

使用 inline code 有一個 4KiB 的限制,也可能比較不好 debug,不過還是介紹給有需要使用的朋友 用法是呼叫 lambda.Code.fromInline 把 Code 轉成 String 塞進去即可,這邊為了方便閱讀所以使用了換行符號,如果是 Code 很短的朋友直接一行解決也是 OK 的呦!

const main = new lambda.Function(this, "lambda", {
  runtime: lambda.Runtime.NODEJS_10_X,
  handler: "index.handler",
  code: lambda.Code.fromInline(
    ' \
  exports.handler = async function (event) { \
    console.log("request:", JSON.stringify(event, undefined, 2)); \
    return { \
      statusCode: 200, \
      headers: { "Content-Type": "text/html" }, \
      body: `<meta charset="utf-8"><h1>第 12 屆 iT 邦幫忙鐵人賽 用 CDK 定義 AWS 架構 AWS Lambda!</h1>`, \
    }; \
  }; \
  '
  ),
});

Lambda 開啟 X-Ray

應該滿多使用 Lambda 的朋友會使用 X-Ray 服務的,要開啟只要把 tracing 開啟即可

const main = new lambda.Function(this, "lambda", {
  runtime: lambda.Runtime.NODEJS_10_X,
  handler: "index.handler",
  code: lambda.Code.fromAsset("lambda"),
  tracing: lambda.Tracing.ACTIVE,
});

問題三:我們是不是可以在 Lambda 裡面寫我們對應的 API 程式呢?

為了好解說我們把 API 分成幾個不一樣的 path:

  • root path 顯示:第 12 屆 iT 邦幫忙鐵人賽!
  • ironman path 顯示:用 CDK 定義 AWS 架構 AWS Lambda!
  • 其他頁面:跳轉到我的 Blog

修改一下 Lambda Function

完成以上條件的程式可以這樣寫,我們修改一下 lambda/index.js

exports.handler = async function (event) {
  console.log("request:", JSON.stringify(event, undefined, 2));
  switch (event.path) {
    case "/":
      switch (event.httpMethod) {
        case "GET":
          return {
            statusCode: 200,
            headers: { "Content-Type": "text/html" },
            body: `<meta charset="utf-8"><h1>第 12 屆 iT 邦幫忙鐵人賽!</h1>`,
          };
      }
    case "/ironman":
      switch (event.httpMethod) {
        case "GET":
          return {
            statusCode: 200,
            headers: { "Content-Type": "text/html" },
            body: `<meta charset="utf-8"><h1>用 CDK 定義 AWS 架構 AWS Lambda!</h1>`,
          };
      }
    default:
      return {
        statusCode: 301,
        headers: {
          Location: "https://blog.clarence.tw/",
        },
      };
  }
};

執行一下 cdk deploy,看一下結果 查看 https://api.cdk.clarence.tw/ https://i0.wp.com/ithelp.ithome.com.tw/upload/images/20200920/2011770118IMSg6yzx.png?w=640&ssl=1

查看 https://api.cdk.clarence.tw/ironman https://i0.wp.com/ithelp.ithome.com.tw/upload/images/20200920/20117701fu5Ww1gLxF.png?w=640&ssl=1

其他頁面因為會直接跳轉所以我們用 curl 來表示

$ curl -sID -X https://api.cdk.clarence.tw/blog
HTTP/2 301
date: Sun, 20 Sep 2020 15:56:34 GMT
content-type: application/json
content-length: 0
location: https://blog.clarence.tw/
x-amzn-requestid: c3e2f2d3-6db0-4f81-be69-3fce13a9957f
x-amz-apigw-id: TLBD9FYePHcF7QA=
x-amzn-trace-id: Root=1-5f677bb2-360d545301670034714adbfe;Sampled=0

問題四:全部程式都放在同一個文件裡面好像不太好管理,可以拆開來嗎?

使用 API Gateway 接 Lambda

介紹了這麼多單個 Lambda 寫 API 的方法,現在來說明怎麼把 Lambda 拆開的方法

準備多個 Lambda Function

修改一下原本的 lambda/index.js 讓我們比較好辨識目前的網頁

exports.handler = async function (event) {
  console.log("request:", JSON.stringify(event, undefined, 2));
  return {
    statusCode: 200,
    headers: { "Content-Type": "text/html" },
    body: `<meta charset="utf-8"><h1>第 12 屆 iT 邦幫忙鐵人賽!</h1>`,
  };
};

新增一個新的 Lambda Function lambda/ironmanFunc.js 來增加頁面

exports.handler = async function (event) {
  console.log("request:", JSON.stringify(event, undefined, 2));
  return {
    statusCode: 200,
    headers: { "Content-Type": "text/html" },
    body: `<meta charset="utf-8"><h1>用 CDK 定義 AWS 架構 AWS Lambda!</h1>`,
  };
};

修改 cdk-lambda-stack.js

原本的 CDK stack Lambda Function 只有定義一個,現在我們再多新增一個 ironmanFunc 第 28 行:使用 GET Method 串接到原本的 main function 第 29 行:新增一個 12th 的 path 第 30 行:使用 GET method 串接新的 ironmanFunc

const main = new lambda.Function(this, "lambda", {
  runtime: lambda.Runtime.NODEJS_10_X,
  handler: "index.handler",
  code: lambda.Code.fromAsset("lambda"),
});
const ironmanFunc = new lambda.Function(this, "ironmanFunc", {
  runtime: lambda.Runtime.NODEJS_10_X,
  handler: "ironmanFunc.handler",
  code: lambda.Code.fromAsset("lambda"),
});

const cert = acm.Certificate.fromCertificateArn(
  this,
  "Certificate",
  "arn:aws:acm:us-west-2:888888888888:certificate/bd476304-de15-4feb-b5f4-136fa60cae59"
);
new cdk.CfnOutput(this, "AcmArn", {
  value: cert.certificateArn,
});
const api = new apigw.LambdaRestApi(this, "Endpoint", {
  handler: main,
  domainName: {
    domainName: "api.cdk.clarence.tw",
    certificate: cert,
  },
  proxy: false,
});
api.root.addMethod("GET", new apigw.LambdaIntegration(main));
const ironman = api.root.addResource("12th");
ironman.addMethod("GET", new apigw.LambdaIntegration(ironmanFunc));

deploy 一下測試一下效果

先到 https://api.cdk.clarence.tw/ 看一下 https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20200920/20117701tzHjED0Xbd.png?w=640&ssl=1

再到 https://api.cdk.clarence.tw/12th 果然如預期 https://i2.wp.com/ithelp.ithome.com.tw/upload/images/20200920/20117701u8BlMNaPH8.png?w=640&ssl=1

今天說明的 CDK 使用 Lambda、API Gateway 的延伸,希望對大家都有收穫,我們明天再見 ~