AWS Lambda 的網路能力:如何存取外網和 VPC

AWS Lambda 的網路能力:如何存取外網和 VPC

January 8, 2025

最近做專案時,想嘗試用 Lambda 將第三方 API 回傳的資料存入 VPC 內的 RDS,但在過程中發現,在預設(沒有特別設定 NAT 等、沒有什麼預算)的情況下,無法直接讓一個 Lambda 同時存取外網服務又存取 VPC,經過一番掙扎,最後透過「將事情分給不同 Lambda 還有用 Lambda destination 傳資料」來解決這個問題。

這篇會先簡單介紹 AWS Lambda 的網路能力,再介紹 Lambda 存取外網和 VPC 的一種方法。

AWS Lambda 的網路能力: attach-VPC or no-VPC

在設定 Lambda 時,有個選項是 Enable VPC,打開之後就可以選要讓 Lambda 連接到哪個 VPC,透過這個設定就能讓 Lambda 存取到 VPC 內的資源。 一開始在趕著實作出專案功能時,還以為把這個 Enable VPC 勾勾起來,就像是把 Lambda 放在 VPC 內,後來看到官方資料與其他大大的整理,才發現其實背後發生的事情並不是想像的這樣,能夠存取 VPC 的 Lambda 沒有真的放在 VPC 內,而是它可以連接到 VPC (attach-VPC)。

以下使用的示意圖來源為 AWS re:Invent 2020 AWS Lambda networking best practices,裡面有許多關於 Lambda networking 詳盡且附圖的介紹,推推。

首先需要知道的背景知識是,所有的 Lambda 都被放在 AWS 管理的 Lambda service VPC 中執行,呼叫 Lambda 都是統一透過 Lambda service API: Lambda Services VPC

而 Lambda 的網路連線能力分為兩種,如下面這張圖的 no-VPC 或是連接 VPC。

預設的 no-VPC 模式讓 Lambda 可以存取網際網路上的東西,不能存取 VPC 的資源(畢竟是 Virtual private cloud)。

而將 Enable VPC 模式打勾後,Lambda 還是會被放在 Lambda service VPC 裡,但是這個 Lambda 的所有網路流量都會被導到選擇的 VPC,也因此如果 VPC 沒有特別設定(像是 NAT 等等),那麼這個 Lambda 是不能跟外網資源溝通的。

AWS Lambda 如何存取外網和 VPC

那麼沒有預算可以開 NAT 的情況下,該怎麼做才能讓 Lambda 可以存取外網、又存取 VPC 呢?

Google 了一陣子,從一位大大分享的 AWS Lambda 做網路爬蟲的文章認識到 Lambda destination 可以用來傳遞 Lambda 之間的資料,也看到可以將 Lambda 打外網的結果存在 s3 上,再用另一個 Lambda 從 s3 讀檔做進一步處理。

從這流程學到一種做法是:不要只侷限一個 Lambda,中間的資料傳過去就好。美其名還可以視作「解耦 Lambda」😼

回到要將外網資料存回 RDS 的目標,我將「存取網路資料」與「寫入 DB 」分開:存取外網的事給 no-VPC 的 Lambda 做,寫 DB 的事就給 VPC 的 Lambda 做,也用 s3 來儲存中間產物。

但跟上面文章不一樣的是,我不是設定 s3 會 trigger 第二個 Lambda,而是讓第一個 Lambda 透過 Lambda destination 觸發第二個 Lambda,第二個 Lambda 再主動從 s3 下載檔案。

這樣的做法需要額外設定 VPC 的 s3 gateway endpoint,讓 VPC 的網路流量不用經過外網再到 s3,而是透過 gateway endpoint 直接傳到 s3。

  • s3 和 dynamo DB 是 AWS 服務中少數可以免費在 VPC 中使用 gateway endpoint 的服務。

整體流程如下圖,想不太起來自己為什麼這樣做而不是像上面文章直接從 s3 trigger

總之這個流程是可以正常運作的,但是後來考量到我們專案的需求,這條流程每 10 分鐘就會被觸發一次,一個月就會 PUT 4300 多次,因此這樣會用光免費方案提供的一個月 2000 次的 PUT,不是個適合沒有什麼預算的方法。

於是經過研究,又調整了一下流程,直接用 Lambda destination 這個功能來傳中間產物。

Lambda destination: 將 Lambda 結果傳下去

Lambda destination 可以將 Lambda 的執行結果傳遞給另一個服務,還可以設定當成功再傳或是失敗時再傳,目前可以傳遞的目標服務包含:SNS, SQS, Lambda, EventBridge。

舉例來說,你可以設定某個要監控的 A Lambda 有一條 on Failure 的 destination 到另一個通知用的 Lambda,若 A Lambda 某天執行失敗,就會將失敗的結果傳遞給會發通知的 Lambda。

但使用 Lambda destination 時要注意:

  • Lambda 的呼叫方式有分 sync 和 async,可以用「需不需要立刻得到回傳值」來判斷是哪種,只有 async 呼叫方式會觸發 lambda destination,所觸發的另一個 lambda 也是 async 呼叫。
  • 如果目標服務是另一個 Lambda,那 payload 就會受限於 Lambda async invocation payload 的限制payload 上限只有 256 KB

為了讓 payload 不要太大,可以壓縮資料。

像是我們專案使用了 gzip 來壓縮,例如原本的 JSON 是 279 KB ,透過 gzip 壓縮,可以降低到 19 KB。

最終實作的流程其實很簡單,如下圖:no-VPC Lambda 先打外網,將資料壓縮並回傳,再透過 Lambda destination 將資料傳遞給 VPC Lambda,最後再與 VPC 內的資源溝通。

小結

網路上還有許多關於這個問題的豐富討論與方法比較,這篇提到的只是其中一種做法,像是如果你要存取外網只是為了用 AWS 本身服務的話,那很高機率會有其他更適合的方法。

但如果你的需求和這篇描述的情境類似,是 async 觸發的、要存取第三方服務、資料不大,那拆分成兩個 Lambda 並透過 destination 傳遞或許是個簡單的選擇。

References