Lambda의 μƒνƒœλ₯Ό μ €μž₯ν•˜λŠ” Execution Environment

jiwoo
  • #Lambda
  • #AWS
  • #Execution Environment
  • #Provisioned Concurrency
  • #NestJS

λ“€μ–΄κ°€λ©°

원더월, ν”„λ‘¬μŠ€ν† μ–΄ λ“±μ˜ λ²‘μ—”λ“œ μ„œλΉ„μŠ€λŠ” serverless ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•˜μ—¬ Lambda 기반으둜 κ΅¬μ„±λ˜μ–΄ μžˆλŠ”λ°μš”. ν˜Ήμ‹œ λžŒλ‹€ ν•¨μˆ˜λ₯Ό μ—¬λŸ¬ 번 ν˜ΈμΆœν–ˆμ„ λ•Œ, 이전에 μ‹€ν–‰ν•œ ν•¨μˆ˜μ˜ μƒνƒœκ°€ λ‚¨μ•„μžˆλŠ” λŠλ‚Œμ„ λ°›μœΌμ‹  적이 μžˆμœΌμ‹ κ°€μš”?

λŒ€ν‘œμ μœΌλ‘œ 첫번째 μ‹€ν–‰μ—μ„œ DB 컀λ„₯μ…˜μ„ initialize ν•˜κ³ , λ‘λ²ˆμ§Έ μ‹€ν–‰ λ•ŒλŠ” DB 컀λ„₯μ…˜μ„ initialize ν•˜μ§€ μ•Šμ•„λ„ μ‚¬μš©ν•  수 μžˆλŠ” κ²½μš°κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ œκ°€ λžŒλ‹€λ₯Ό 처음 μ‚¬μš©ν–ˆμ„ λ•ŒλŠ” β€˜μ „μ—­λ³€μˆ˜λŠ” κ·ΈλŒ€λ‘œ λ‚¨κ²¨λ‘λŠ”κ±΄κ°€?’ ν•˜λŠ” λ§‰μ—°ν•œ μΆ”μΈ‘λ§Œ ν•  λΏμ΄μ—ˆμŠ΅λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” λžŒλ‹€μ˜ Execution Environment λ•Œλ¬ΈμΈλ°μš”. 이번 λΈ”λ‘œκ·Έμ—μ„œλŠ” λžŒλ‹€μ˜ Execution Environmentκ°€ 뭔지 μ‚΄νŽ΄λ³΄λŠ” μ‹œκ°„μ„ 갖도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

Lambda Execution Environment

Execution Environment(μ‹€ν–‰ ν™˜κ²½)은 λžŒλ‹€ ν•¨μˆ˜κ°€ ν•„μš”ν•œ λ¦¬μ†ŒμŠ€λ₯Ό κ΄€λ¦¬ν•˜κ³  ν•¨μˆ˜λ₯Ό μ‹€ν–‰μ‹œν‚€κΈ° μœ„ν•œ λŸ°νƒ€μž„ ν™˜κ²½μž…λ‹ˆλ‹€. Execution Environment의 κ΅¬μ„±μš”μ†ŒλŠ” λ‹€μŒμ²˜λŸΌ λ©”λͺ¨λ¦¬, Timeout, Provisioned Concurrency λ“± 저희가 λžŒλ‹€ ν•¨μˆ˜λ₯Ό λ§Œλ“€ λ•Œ μ„€μ •ν•  수 μžˆλŠ” κ°’λ“€λ‘œ 이루어져 μžˆμŠ΅λ‹ˆλ‹€.

  • Memory
  • CPU
  • Ephemeral Storage(μž„μ‹œ μŠ€ν† λ¦¬μ§€)
  • Timeout
  • Handler
  • Provisioned Concurrency
  • ν™˜κ²½λ³€μˆ˜

Execution EnvironmentλŠ” 각 ν•¨μˆ˜ κ°„ κ³΅μœ λ˜μ§€ μ•Šμ§€λ§Œ, ν•˜λ‚˜μ˜ ν•¨μˆ˜λ₯Ό μ—°μ†μ μœΌλ‘œ μ‹€ν–‰ν•  κ²½μš°λŠ” Execution Environmentκ°€ 곡유될 수 μžˆμŠ΅λ‹ˆλ‹€. Execution Environment 생λͺ…μ£ΌκΈ° νŒŒνŠΈμ—μ„œ μžμ„Ένžˆ μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

Lambda Execution Environment 생λͺ…μ£ΌκΈ°

Execution Environment의 생λͺ…μ£ΌκΈ°λŠ” λ‹€μŒμ²˜λŸΌ 3λ‹¨κ³„λ‘œ 이루어져 μžˆμŠ΅λ‹ˆλ‹€. 각 λ‹¨κ³„λŠ” λŸ°νƒ€μž„μ΄ μ „μ†‘ν•œ 이벀트λ₯Ό λ°›μ•„ μ‹œμž‘λ˜κ³ , μž‘μ—…μ΄ λλ‚¬λ‹€λŠ” μš”μ²­μ„ μ „μ†‘ν•˜μ—¬ μ™„λ£Œλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

1. INIT

첫번째 λ‹¨κ³„λŠ” Init λ‹¨κ³„μž…λ‹ˆλ‹€.

Init λ‹¨κ³„λŠ” 저희가 λžŒλ‹€ λ‘œκ·Έμ—μ„œ λ³Ό 수 μžˆλŠ”λ°μš”. μœ„μ²˜λŸΌ INIT_START 둜 ν‘œμ‹œλœ 뢀뢄이 Init λ‹¨κ³„μž…λ‹ˆλ‹€. Init λ‹¨κ³„μ—λŠ” Lambda Execution Environmentλ₯Ό κ΅¬μ„±ν•˜λŠ” μ„Έ 가지 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

  • Extension Init : λžŒλ‹€μ— λΆ™λŠ” μΆ”κ°€κΈ°λŠ₯을 μ‹€ν–‰ν•˜λŠ” μž‘μ—… (μ»€μŠ€ν…€ λͺ¨λ‹ˆν„°λ§, λ³΄μ•ˆμž‘μ—…, 자체적인 Lambda Extension λ“±)
  • Runtime Init : 각 μ–Έμ–΄λ³„λ‘œ μ½”λ“œ 싀행을 μœ„ν•΄ ν•„μš”ν•œ μš”μ†Œλ“€μ„ λΆ€νŠΈμŠ€νŠΈλž© ν•˜λŠ” κ³Όμ •
  • Function Init : ν•¨μˆ˜μ˜ 정적 μ½”λ“œ μ‹€ν–‰μ‹œμΌœμ„œ handler ν•¨μˆ˜λ₯Ό μ€€λΉ„μ‹œν‚€λŠ” μž‘μ—…

Init λ‹¨κ³„λŠ” 기본적으둜 λžŒλ‹€ ν•¨μˆ˜κ°€ ν˜ΈμΆœλμ„ λ•Œ μ‹œμž‘λ˜λŠ” 단계이기 λ•Œλ¬Έμ—, λžŒλ‹€ ν•¨μˆ˜κ°€ 이 λ‹¨κ³„μ—μ„œ μ΅œλŒ€ 10초의 지연이 생기고 μ½œλ“œ μŠ€νƒ€νŠΈ(Cold Start)κ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ Provisioned Concurrency μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λŠ” 경우 λžŒλ‹€λ₯Ό μ’€ 더 warm ν•˜κ²Œ λ§Œλ“€μ–΄ 쀄 수 μžˆλŠ”λ°μš”. λžŒλ‹€ ν•¨μˆ˜κ°€ μ—…λ°μ΄νŠΈλ˜κ³  미리 Execution Environment을 ν”„λ‘œλ°”μ΄μ§• 해놓고 λŒ€κΈ°ν•©λ‹ˆλ‹€. ν•¨μˆ˜κ°€ ν˜ΈμΆœλμ„ λ•Œ Init 단계가 μ™„λ£Œλœ μƒνƒœλΌλ©΄ λ°”λ‘œ λ‹€μŒ λ‹¨κ³„λ‘œ skip ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

2. INVOKE

λ‘λ²ˆμ§Έ λ‹¨κ³„λŠ” Invoke λ‹¨κ³„μž…λ‹ˆλ‹€.

service: fromm-store-api-delivery

frameworkVersion: '3'
configValidationMode: warn
useDotenv: true

plugins:
    - serverless-esbuild
    - serverless-plugin-canary-deployments
    - serverless-prune-plugin

provider:
    name: aws
    runtime: nodejs16.x
    timeout: 20 # Timeout 20s

Invoke λ‹¨κ³„λŠ” μ‹€μ œ 저희가 μž‘μ„±ν•œ Lambda ν•¨μˆ˜λ₯Ό μ‹€ν–‰μ‹œν‚€λŠ” λ‹¨κ³„μž…λ‹ˆλ‹€. λžŒλ‹€ ν•¨μˆ˜λ₯Ό 생성할 λ•Œ μ œν•œ μ‹œκ°„(Timeout)을 μ„€μ •ν•˜λŠ”λ° μ΄λ•Œ μ„€μ •ν•œ Timeout 값이 Invoke λ‹¨κ³„μ˜ Timeout κ°’μž…λ‹ˆλ‹€. Init λ‹¨κ³„λŠ” 10초의 μ œν•œ μ‹œκ°„μ΄ 있고 10μ΄ˆκ°€ μ§€λ‚˜λ©΄ Invoke λ‹¨κ³„μ˜ Timeout을 μž‘μ•„λ¨ΉμœΌλ©΄μ„œ Init을 μˆ˜ν–‰ν•˜κ²Œ λ©λ‹ˆλ‹€.

3. SHUTDOWN

λ§ˆμ§€λ§‰ λ‹¨κ³„λŠ” Shutdown λ‹¨κ³„μž…λ‹ˆλ‹€.

λŸ°νƒ€μž„ 및 Extension을 μ’…λ£Œμ‹œν‚€κ³  μ΅œμ’…μ μœΌλ‘œ Execution Environmentλ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€. μ΅œλŒ€ 2μ΄ˆκ°€ 주어지고 2μ΄ˆκ°€ μ§€λ‚˜λ©΄ κ°•μ œλ‘œ μ’…λ£Œλ©λ‹ˆλ‹€.

Execution Environment의 μœ μ§€

λžŒλ‹€λŠ” Shutdown 단계 이후 Execution Environmentλ₯Ό μ–΄λŠ 정도 μ‹œκ°„λ™μ•ˆ μœ μ§€ν•©λ‹ˆλ‹€.
μ΄λŠ” ν•¨μˆ˜κ°€ 호좜될 λ•Œλ§ˆλ‹€ Execution Environmentλ₯Ό ν”„λ‘œλ°”μ΄μ§•ν•œλ‹€λ©΄ 계속 λ¦¬μ†ŒμŠ€κ°€ μ†Œμœ λ  ν…Œλ‹ˆ, 계속 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  것 κ°™μœΌλ©΄ λžŒλ‹€κ°€ μ΅œμ ν™”λ₯Ό μœ„ν•΄ Execution Environmentλ₯Ό 계속 λ‚¨κ²¨λ‘λŠ” κ²ƒμž…λ‹ˆλ‹€.

Execution Environmentκ°€ μœ μ§€λμ„ λ•Œ λ‚˜νƒ€λ‚˜λŠ” νš¨κ³Όλ“€μ— λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

1. ν•Έλ“€λŸ¬ λ°”κΉ₯μ—μ„œ μ΄ˆκΈ°ν™”λœ λ³€μˆ˜κ°€ λ‹€μŒ ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜μ— 남아 있음

Execution Environmentκ°€ μœ μ§€λ˜λ©΄ Init λ‹¨κ³„μ—μ„œ Function Init을 μ‹€ν–‰ν–ˆμ„ λ•Œ 각각 μ–Έμ–΄μ˜ λŸ°νƒ€μž„ ν™˜κ²½μ΄ λ§Œλ“€μ–΄μ§€κ²Œ λ˜λŠ”λ°, 이 ν™˜κ²½μ΄ κ·ΈλŒ€λ‘œ 남아 있게 λ˜λ©΄μ„œ μ „μ—­λ³€μˆ˜λ‘œ λ§Œλ“€μ–΄μ§„ 객체듀은 계속 μ°Έμ‘°κ°€ λ‚¨μ•„μžˆκ²Œ λ˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

이 νŠΉμ„±μ„ ν™œμš©ν•΄ NestJS ν”„λ ˆμž„μ›Œν¬μ™€ μ„œλ²„λ¦¬μŠ€λ₯Ό ν™œμš©ν•œ ν”„λ‘œμ νŠΈμ—μ„œ 자주 μ‚¬μš©ν•˜λŠ” νŒ¨ν„΄μ΄ μžˆλŠ”λ°μš”.
NestJS ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•΄ Nest 앱을 μƒμ„±ν•˜κ³  μ„œλΉ„μŠ€ν•  수 μžˆλŠ” μƒνƒœλ‘œ λ§Œλ“€μ–΄ λ‘” λ‹€μŒμ— cachedServer λΌλŠ” μ „μ—­λ³€μˆ˜μ— 할당을 ν•©λ‹ˆλ‹€. 이 cachedServer λ³€μˆ˜λŠ” Execution Environmentκ°€ μœ μ§€λœλ‹€λ©΄ λ‹€μŒ ν•¨μˆ˜ ν˜ΈμΆœμ—μ„œλ„ 계속 μœ μ§€λ  수 μžˆμŠ΅λ‹ˆλ‹€.

// main.ts
let cachedServer;

export const bootstrapServer = async () => {
    if (!cachedServer) {
        const expressApp = express();
        const app = await NestFactory.create(AppModule, new ExpressAdapter(expressApp));
        await app.init();

        cachedServer = ServerlessExpress({ app: expressApp });
    }

    return cachedServer; // Execution Environmentκ°€ μœ μ§€λœλ‹€λ©΄ cachedServer κ°€ λ‚¨μ•„μžˆκΈ° λ•Œλ¬Έμ— κ·ΈλŒ€λ‘œ 리턴
};

lambda ν•Έλ“€λŸ¬μ—μ„œλŠ” 캐싱이 된 앱을 μ‚¬μš©ν•΄ μš”μ²­μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. NestFactory.create λ₯Ό λ‹€μ‹œ ν•˜μ§€ μ•Šμ•„μ„œ 보닀 λΉ λ₯΄κ²Œ μ„œλΉ„μŠ€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

// lambda.ts
export const handler: APIGatewayProxyHandler = async (event: any, context: Context, callback: any) => {
    const server = await bootstrapServer(AppModule, 'Delivery', version, '/delivery');

    return server(event, context, callback);
};

2. /tmp 디렉터리 μ•ˆμ— λ‚΄μš©μ΄ 남아 있음

/tmp λ””λ ‰ν„°λ¦¬λŠ” Execution Environment κ°€ μ œκ³΅ν•˜λŠ” μž„μ‹œ μŠ€ν† λ¦¬μ§€μ˜ κ²½λ‘œμž…λ‹ˆλ‹€. Execution Environment κ°€ μœ μ§€λœλ‹€λ©΄ λ””λ ‰ν„°λ¦¬μ˜ μ½˜ν…μΈ λ„ λ‹€μ‹œ μ‚¬μš©ν•  수 있게 λ©λ‹ˆλ‹€.

3. λ°±κ·ΈλΌμš΄λ“œ ν”„λ‘œμ„ΈμŠ€ ν˜Ήμ€ μ½œλ°±ν•¨μˆ˜κ°€ λ‹€μŒ ν•¨μˆ˜ ν˜ΈμΆœμ—μ„œ 싀행됨

ν•¨μˆ˜ μ’…λ£Œ μ‹œ μ™„λ£Œλ˜μ§€ μ•Šμ€ λ°±κ·ΈλΌμš΄λ“œ ν”„λ‘œμ„ΈμŠ€ λ˜λŠ” μ½œλ°±μ€ Lambdaκ°€ μ‹€ν–‰ ν™˜κ²½μ„ μž¬μ‚¬μš©ν•˜λŠ” κ²½μš°μ— λ‹€μ‹œ 싀행될 수 μžˆμŠ΅λ‹ˆλ‹€. Node.js 기반 λžŒλ‹€ν•¨μˆ˜μΌ κ²½μš°μ—λŠ” μ™„λ£Œλ˜μ§€ μ•Šμ€ Promiseκ°€ λ‹€μŒ ν•¨μˆ˜μ—μ„œ resolve λ˜λŠ” 상황이 μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

마치며

AWS Lambda의 Execution Environment에 λŒ€ν•΄ μ•Œμ•„λ΄€λŠ”λ°μš”. 일반적으둜 Execution Environment은 Lambda μ„œλΉ„μŠ€κ°€ κ΄€λ¦¬ν•˜λ―€λ‘œ κ°œλ°œμžκ°€ 직접 μ‘°μž‘ν•  ν•„μš”λŠ” μ—†μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ Execution Environmentλ₯Ό 효과적으둜 ν™œμš©ν•˜λ©΄ Lambda ν•¨μˆ˜μ˜ μ‹€ν–‰ μ‹œκ°„κ³Ό μžμ› μ‚¬μš©μ„ μ΅œμ ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λžŒλ‹€μ˜ μƒνƒœ 관리와 μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μœ„ν•΄ Execution Environment의 λ™μž‘ 원리λ₯Ό 이해할 ν•„μš”κ°€ μžˆμ„ 것 κ°™μŠ΅λ‹ˆλ‹€. μ΄ν›„μ—λŠ” Lambda Extensions, Provisioned Concurrency λ“±κ³Ό 같은 κ°œλ…λ„ μžμ„Ένžˆ μ•Œμ•„λ³΄λŠ” μ‹œκ°„μ΄ 있으면 μ’‹κ² μŠ΅λ‹ˆλ‹€.

κ°μ‚¬ν•©λ‹ˆλ‹€.

← λͺ©λ‘μœΌλ‘œ λŒμ•„κ°€κΈ°

Art Changes Life

λ…Έλ¨ΈμŠ€μ™€ ν•¨κ»˜ μ—”ν„°ν…Œν¬ 산업을 ν˜μ‹ ν•΄λ‚˜κ°ˆ 멀버λ₯Ό μ°ΎμŠ΅λ‹ˆλ‹€.

μ±„μš© 쀑인 곡고 보기