修改執行階段環境 - AWS Lambda

修改執行階段環境

您可以使用內部延伸項目來修改執行階段程序。內部延伸項目不是單獨程序,它們會作為執行時程序的一部分執行。

Lambda 提供特定語言環境變數,您可以進行設定,以便將選項和工具新增至執行時間。Lambda 還提供包裝函數指令碼,它允許 Lambda 將執行時間啟動委派給您的指令碼。您可以建立包裝程式指令碼來自訂執行階段啟動行為。

特定語言的環境變數

Lambda 支援僅限組態的方式,可透過下列特定語言的環境變數,在函數初始化期間預先載入程式碼:

  • JAVA_TOOL_OPTIONS - 在 Java 中,Lambda 支援此環境變數,以在 Lambda 中設定其他命令列變數。此環境變數可讓您指定工具的初始化,特別是在您使用 agentlibjavaagent 選項啟動原生或 Java 程式設計語言代理程式時。

  • NODE_OPTIONS - 在 Node.js 10x 及更高版本中,Lambda 支援此境變數。

  • DOTNET_STARTUP_HOOKS - 在 .NET Core 3.1 及更高版本中,此環境變數指定了 Lambda 可以使用的組件 (dll) 的路徑。

使用特定語言的環境變數是設定啟動屬性的慣用方式。

範例:攔截 Lambda 叫用 javaagent

Java 虛擬機器 (JVM) 會嘗試定位使用 JVM javaagent 參數指定的類別,並在應用程式的進入點前叫用其 premain 方法。

下例使用 Byte Buddy,這個資料庫是用來在 Java 應用程式的執行階段建立和修改 Java 類別,無需編譯器的幫助。Byte Buddy 會提供用於產生 Java 代理的額外 API。在此範例中,Agent 類別會攔截對 RequestStreamHandler 類別進行的每個 handleRequest 呼叫方法。此類別會在執行階段內部用來包裝處理常式的叫用。

import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; import java.lang.instrument.Instrumentation; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { new AgentBuilder.Default() .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager()) .type(ElementMatchers.isSubTypeOf(RequestStreamHandler.class)) .transform((builder, typeDescription, classLoader, module) -> builder .method(ElementMatchers.nameContains("handleRequest")) .intercept(Advice.to(TimerAdvice.class))) .installOn(inst); } }

上例中的代理程式會使用 TimerAdvice 方法。TimerAdvice 會測量用於呼叫方法的毫秒數,並記錄日誌方法時間和詳細資料,例如名稱和傳遞的引數。

import static net.bytebuddy.asm.Advice.AllArguments; import static net.bytebuddy.asm.Advice.Enter; import static net.bytebuddy.asm.Advice.OnMethodEnter; import static net.bytebuddy.asm.Advice.OnMethodExit; import static net.bytebuddy.asm.Advice.Origin; public class TimerAdvice { @OnMethodEnter static long enter() { return System.currentTimeMillis(); } @OnMethodExit static void exit(@Origin String method, @Enter long start, @AllArguments Object[] args) { StringBuilder sb = new StringBuilder(); for (Object arg : args) { sb.append(arg); sb.append(", "); } System.out.println(method + " method with args: " + sb.toString() + " took " + (System.currentTimeMillis() - start) + " milliseconds "); } }

上面的 TimerAdvice 方法具有以下相依性。

*'com.amazonaws'*, *name*: *'aws-lambda-java-core'*, *version*: *'1.2.1'* *'net.bytebuddy'*, *name*: *'byte-buddy-dep'*, *version*: *'1.10.14'* *'net.bytebuddy'*, *name*: *'byte-buddy-agent'*, *version*: *'1.10.14'*

建立包含代理程式 JAR 的圖層之後,您可以透過設定環境變數,將 JAR 名稱傳遞給執行階段的 JVM。

JAVA_TOOL_OPTIONS=-javaagent:"/opt/ExampleAgent-0.0.jar"

使用 {key=lambdaInput} 叫用該函式後,您可以在日誌中找到以下資料行:

public java.lang.Object lambdainternal.EventHandlerLoader$PojoMethodRequestHandler.handleRequest (java.lang.Object,com.amazonaws.services.lambda.runtime.Context) method with args: {key=lambdaInput}, lambdainternal.api.LambdaContext@4d9d1b69, took 106 milliseconds

範例:將關閉掛接新增至 JVM 執行階段程序

Shutdown 事件期間註冊延伸項目時,執行時間程序會取得高達 500 毫秒以逐漸處理關閉程序。您可以掛接到執行階段程序,並且當 JVM 開始其關閉程序時,它會啟動所有已註冊掛接。若要註冊關閉掛接,您必須註冊為延伸項目。您不需要明確註冊 Shutdown 事件,因為它會自動傳送到執行時間。

import java.lang.instrument.Instrumentation; public class Agent { public static void premain(String agentArgs, Instrumentation inst) { // Register the extension. // ... // Register the shutdown hook addShutdownHook(); } private static void addShutdownHook() { // Shutdown hooks get up to 500 ms to handle graceful shutdown before the runtime is terminated. // // You can use this time to egress any remaining telemetry, close open database connections, etc. Runtime.getRuntime().addShutdownHook(new Thread(() -> { // Inside the shutdown hook's thread we can perform any remaining task which needs to be done. })); } }

範例:擷取 InvokedFunctionArn

@OnMethodEnter static long enter() { String invokedFunctionArn = null; for (Object arg : args) { if (arg instanceof Context) { Context context = (Context) arg; invokedFunctionArn = context.getInvokedFunctionArn(); } } }

包裝函式指令碼

您可以建立包裝函數指令碼來自訂 Lambda 函數的執行時間啟動行為。包裝函式指令碼可讓您設定無法透過特定語言環境變數來設定的組態參數。

注意

如果包裝函式指令碼未成功啟動執行階段程序,叫用可能會失敗。

下列 Lambda 執行時間支援包裝函數指令碼:

  • Node.js 14.x

  • Node.js 12.x

  • Node.js 10.x

  • Python 3.9

  • Python 3.8

  • Ruby 2.7

  • Java 11

  • Java 8 (java8.al2)

  • .NET Core 3.1

當您為函數使用包裝函數指令碼時,Lambda 會使用您的指令碼啟動執行時間。Lambda 會向您的指令碼傳送解譯器的路徑,以及標準執行時間啟動的所有原始引數。您的指令碼可以延伸或轉換程式的啟動行為。例如,指令碼可以插入和更改引數、設定環境變數,或擷取指標、錯誤和其他診斷資訊。

您可以透過將 AWS_LAMBDA_EXEC_WRAPPER 環境變數的值設定為可執行二進位檔案或指令碼的檔案系統路徑來指定指令碼。

範例:使用 Python 3.8 建立並使用包裝函式指令碼

在下面的例子中,您建立一個包裝函式指令碼來啟動與 -X importtime 選項的 Python 解譯器。當您執行函數時,Lambda 會產生日誌項目,以顯示每個匯入的匯入持續時間。

使用 Python 3.8 建立和使用包裝函式指令碼

  1. 若要建立包裝函式指令碼,請將下列程式碼貼到名為 importtime_wrapper 的檔案中:

    #!/bin/bash # the path to the interpreter and all of the originally intended arguments args=("$@") # the extra options to pass to the interpreter extra_args=("-X" "importtime") # insert the extra options args=("${args[@]:0:$#-1}" "${extra_args[@]}" "${args[@]: -1}") # start the runtime with the extra options exec "${args[@]}"
  2. 若要授予指令碼可執行的許可,請從指令行輸入 chmod +x importtime_wrapper

  3. 將指令碼部署為 Lambda 層

  4. 使用 Lambda 主控台建立函數。

    1. 開啟 Lambda 主控台

    2. 選擇 Create function (建立函數)。

    3. Basic information (基本資訊) 下,為 Function name (函數名稱) 輸入 wrapper-test-function

    4. 針對執行階段,選擇 Python 3.8

    5. 選擇 Create function (建立函數)。

  5. 將圖層新增到您的函式中。

    1. 選擇您的函數,然後選擇 Code (程式碼) (如果尚未選取)。

    2. 選擇 Add a layer (新增 layer)

    3. 選擇圖層下,選擇您先前建立之相容圖層的名稱版本

    4. 選擇 Add (新增)

  6. 將代碼和環境變數新增到您的函式中。

    1. 在函式程式碼編輯器中,貼上下列函式程式碼:

      import json def lambda_handler(event, context): # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
    2. 選擇 Save (儲存)。

    3. Environment variables (環境變數) 下,選擇 Edit (編輯)。

    4. 選擇 Add environment variable (新增環境變數)。

    5. 對於金鑰,輸入 AWS_LAMBDA_EXEC_WRAPPER

    6. Value (數值) 輸入 /opt/importtime_wrapper

    7. 選擇 Save (儲存)。

  7. 若要執行函式,請選擇測試

    因為您的包裝函式指令碼使用 -X importtime 選項啟動 Python 解譯器,所以日誌會顯示每次導入所需的時間。例如:

    ... 2020-06-30T18:48:46.780+01:00 import time: 213 | 213 | simplejson 2020-06-30T18:48:46.780+01:00 import time: 50 | 263 | simplejson.raw_json ...