AWS Lambda Maliyet Optimizasyonu: VPC, Layers ve Advanced Pattern'ler
Advanced AWS Lambda pattern'leri ve maliyet optimizasyonu: Lambda Layers, VPC konfigürasyonu, cross-account execution ve mimari kararlar.
Serverless faturaları ilk ayda kimseyi şaşırtmaz. Ölçek büyüdükçe şaşırtır. Fazla ayrılmış memory, boşta duran provisioned concurrency ve monolitik bir handler aynı invocation sayısını her ay yeniden çarpar; Lambda’nın invocation başına fiyatlandırması bu üç hatayı yapması ucuz, taşıması pahalı hale getirir.
Birkaç düzine function’dan sonra sorular değişir. Cold start’ı bozmadan dependency nasıl paylaşılır, VPC bağlantısı ne zaman maliyetine değer, hesaplar arası erişim nasıl güvenli kurulur ve fatura aslında nereye gider? Son sorunun yanıtı gösterişsiz: başka hiçbir şeye dokunmadan önce memory ve concurrency ayarlarını CloudWatch verisiyle denetleyin, çünkü konfigürasyon kayması genellikle kod seviyesindeki optimizasyonun kazandırdığından fazlasına mal olur.
Lambda Layer’lar: Basit Kod Paylaşımının Ötesinde#
Layer’ların Gerçekten Mantıklı Olduğu Durumlar#
Çoğu Lambda Layer tutorial’ı Layer’ları function’lar arası kod paylaşımı olarak anlatır; oysa bu, Layer’a başvurmak için en zayıf gerekçedir. Layer’lar asıl olarak ağır ve nadiren değişen dependency’leri tek yerde sabitlerken işe yarar: bir monitoring SDK’sı, bir veritabanı sürücüsü, custom bir runtime. İş mantığı ise function paketinde kalmalı; orada kendisini kullanan handler ile birlikte versiyonlanır ve geri alınır.
Değişim sıklığına göre ayırma:
// Layer 1: Ağır, nadiren değişen dependency'ler
// Layer'da /opt/nodejs/package.json
{
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.400.0",
"datadog-lambda-js": "^8.67.0",
"pino": "^8.15.0"
}
}
// Function kodu layer dependency'lerini kullanır
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; // Layer'dan
import { datadogLambda } from 'datadog-lambda-js'; // Layer'dan
import pino from 'pino'; // Layer'dan
// Function'a özel kod (layer'da değil)
import { validateUserInput } from './validation'; // Function'a özel
import { processPayment } from './payment'; // Function'a özel
Layer versiyonlama:
// Layer yönetimi için CDK stack
export class SharedLayerStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
// Layer'lar için semantic versioning
const monitoringLayer = new LayerVersion(this, 'MonitoringLayer', {
code: Code.fromAsset('layers/monitoring'),
compatibleRuntimes: [Runtime.NODEJS_20_X],
description: `Monitoring Layer v2.1.0 - ${new Date().toISOString()}`,
layerVersionName: 'monitoring-layer-v2-1-0'
});
// Cross-stack kullanım için ARN export et
new CfnOutput(this, 'MonitoringLayerArn', {
value: monitoringLayer.layerVersionArn,
exportName: 'MonitoringLayerV2-1-0'
});
}
}
Layer’ların Init Maliyeti#
Layer’lar çalışma anında bedava değildir. AWS’in belgelediğine göre Lambda, extension içeren layer’ları Init aşamasında /opt dizinine açar ve function ile tüm extension’ların açılmış toplam boyutu 250 MB’lık deployment paketi sınırını aşamaz. Aşamanın kendisinin de tavanı var: AWS Init süresini extension init, runtime init ve function init toplamı için 10 saniyeyle sınırlıyor. Bu bütçe tutmazsa Lambda aşamayı ilk invocation’da, bu kez konfigüre edilmiş function timeout’u altında yeniden dener. Bu 10 saniyelik tavan on-demand concurrency için geçerli. Provisioned concurrency ya da SnapStart kullanan function’lar daha uzun bir bütçeyle başlar: 130 saniye ya da konfigüre edilmiş function timeout’undan hangisi büyükse o.
Bu bütçe içinde bir megabyte’ın kaça mal olduğu ise AWS’in yayımladığı bir sayı değil. Adrian Tanasa 2023’te bunun bir ölçümünü yöntemiyle birlikte paylaştı: x86 üzerinde 256 MB’lık bir Node.js 18 function’ı, paketi 128 KB’tan 64 MB’a kadar ikiye katlanarak, her biri soğuk kalsın diye on dakika arayla yapılan 100 invocation ve CloudWatch Logs Insights üzerinden toplanan @initDuration değerleri. Ortalama cold start 1 KB’ta 171 ms iken 64 MB’ta 3,1 saniyeye çıktı. Megabyte başına süre oranı ters bir çan eğrisi çizdi: 1 MB civarında 26 ms/MB ile dibe indi, uçlarda 45 ms/MB’a yaklaştı. Tek bir mühendisin ölçümü üretici garantisi sayılmaz; asıl bulgu eğrinin biçimi, rakamlar fikir verici.
Aynı ölçüm kolay sonuca da izin vermiyor. Birebir aynı byte’lar function paketinden çıkarılıp layer’a taşındığında cold start kısaldı: yaklaşık 2 MB’tan itibaren ölçülebilir biçimde, 64 MB’ta ise kabaca iki saniye. AWS belgeleri belirli runtime’lar için bunun tersini söylüyor. Rust rehberi layer kullanımını önermiyor; gerekçesi, function’ların init aşamasında ek assembly’leri belleğe elle yüklemesinin cold start sürelerini artırması. Bir layer’ın işi kolaylaştırması da zorlaştırması da runtime’a ve layer’ın içeriğine bağlı. Her iki durumda da değişmeyen şu: layer byte’ları init yolunun üzerindedir ve bütçesi orada tutulur.
O byte’lar ölçülebilir; AWS kendi monitoring layer’ı için bunları yayımlıyor. Lambda Insights extension’ının 1.0.404.0 sürümü extension binary’sini yaklaşık 9 MB’tan 5 MB’a, layer zip’ini yaklaşık 3,7 MB’tan 2,5 MB’a, agent’ın memory kullanımını da yaklaşık 11 MB’tan 7 MB’a indirdi. Extension’lar function’ın CPU’sunu, memory’sini ve depolamasını paylaştığı için bir monitoring layer’ı yalnızca init yolunda değil memory ayarında da karşınıza çıkar.
Bütçeyi tutarken ölçeği kendi iş yükünüz üzerinden düşünün. AWS’e göre cold start’lar tipik olarak invocation’ların %1’inden azında görülüyor, süresi 100 ms’nin altından 1 saniyenin üstüne kadar değişiyor ve geliştirme ile test function’larında üretimdekilerden daha sık yaşanıyor. Bu sıklık rakamı düzenli çağrılan function’ları anlatıyor. Seyrek ya da düzensiz çağrılan bir function çok daha sık init’e girer; init yolundaki layer byte’ları da gecikmeyi tam orada belirler. Kendi oranınız zaten loglarda: on-demand concurrency’de bu oran, REPORT satırı Init Duration taşıyan invocation’ların payıdır. Yukarıdaki 3,1 saniye, bilinçli olarak uca taşınmış 64 MB’lık bir paketten geliyor.
Buradan dört kural çıkıyor:
- Layer’ı ancak birden fazla function’ın ihtiyaç duyduğu durumda ekleyin. Function başına sert sınır beş, ama dördüncüye uzanmak genelde budanması gereken bir dependency grafiğine işaret eder.
- Layer’ları konuya göre değil, değişim sıklığına göre ayırın. Haftada bir değişen bir layer, dependency sabitleme amacını boşa çıkarır.
- Layer’lara version ARN’i ile referans verin; böylece bir layer’ın yeniden deploy edilmesi başka bir function’ın dependency ağacını sessizce kaydıramaz.
- Function’a özel mantığı layer dışında tutun; handler geri alındığında davranışı da geri alınsın.
VPC Konfigürasyonu ve Maliyet Ayak İzi#
VPC Bağlantısının Bugünkü Maliyeti#
VPC’ye bağlı bir function’ın her cold start’ta on saniyelik ENI kurulumu ödediği yönündeki eski tavsiye artık geçerli değil. Lambda bugün Hyperplane ENI kullanıyor. Her benzersiz subnet ve security group kombinasyonu için bir ENI oluşturuluyor ve bu ENI, aynı kombinasyonu kullanan tüm execution environment’lar arasında paylaşılıyor; yani kurulum maliyeti cold start başına değil, kombinasyon başına bir kez ödeniyor. Warm invocation’lar zaten hiçbir zaman etkilenmiyordu.
VPC bağlantısının hâlâ maliyeti olan tarafı para ve erişilebilirlik. VPC içindeki bir function, siz eklemedikçe public internete çıkamaz. Yaptığı her AWS API çağrısı ya bir NAT Gateway ya da bir VPC endpoint ister; “her şeyi private yapalım” kararının yinelenen bir bütçe kalemine dönüştüğü yer de burasıdır.
Bir function’ı VPC’ye ancak private bir kaynağa ihtiyacı varsa bağlayın: bir RDS cluster’ı, bir ElastiCache node’u, private bir load balancer arkasındaki iç servis. Güvende hissetmek için bağlamayın; VPC dışındaki bir function da AWS API’lerine kimlik doğrulamalı TLS endpoint’leri üzerinden ulaşır.
Minimal bir Lambda VPC konfigürasyonu:
# Lambda için optimize edilmiş CDK VPC kurulumu
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSecurityGroup
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
# Anahtar: Farklı AZ'lerde birden fazla subnet kullan
# Minimal gerekli erişimli security group
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Lambda function security group
VpcId: !Ref Vpc
SecurityGroupEgress:
# Sadece kesinlikle gerekli olanlar
- IpProtocol: tcp
FromPort: 5432
ToPort: 5432
CidrIp: 10.0.0.0/16 # Sadece database subnet'i
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0 # AWS API çağrıları için HTTPS
Keep-Warm Zamanlamaları ve Sınırları#
Zamanlanmış ping’ler, Hyperplane öncesinde VPC cold start’ları için standart geçici çözümdü ve pattern hâlâ üretim kodunun her yerinde duruyor:
// Tek bir execution environment'ı açık tutan zamanlanmış ping
const keepWarmSchedule = new Rule(this, 'KeepVpcLambdaWarm', {
schedule: Schedule.rate(Duration.minutes(5)),
targets: [new LambdaFunction(vpcLambdaFunction, {
event: RuleTargetInput.fromObject({
source: 'keep-warm',
warmup: true
})
})]
});
// VPC function'ları için handler optimizasyonu
export const handler = async (event: any) => {
// Warmup event'lerini handle et
if (event.source === 'keep-warm') {
return { statusCode: 200, body: 'Staying warm' };
}
// Asıl mantığın
return processBusinessLogic(event);
};
Bu yöntem zamanlanmış invocation başına tek bir execution environment’ı açık tutar; zayıf tarafı da tam olarak budur. Tek bir ping, trafik sıçramasının yarattığı yüzüncü eşzamanlı environment’ı ısıtamaz ve warmup dalının her handler’da bakımı gerekir. Provisioned concurrency aynı işi belgelenmiş bir garanti ve okunabilir bir faturayla yapar; zamanlanmış ping’i yalnızca öngörülebilir ve tek haneli eşzamanlılıkta seyreden trafik için tutun.
Private yolun faturaya eklediği kalemler:
- S3 ve DynamoDB için gateway endpoint’ler: saatlik ücret de veri işleme ücreti de yok.
- Interface endpoint’ler (PrivateLink): availability zone başına saatlik ücret ve gigabyte başına işleme ücreti. Ek AZ’ler bu tutarı çarpar.
- NAT Gateway: saatlik ücret ve gigabyte başına işleme ücreti; AWS API’lerine giden trafik dahil her byte faturalanır.
- Hyperplane ENI’ler faturalanmaz, ama subnet IP adresi tüketirler; bu bir maliyet değil kapasite kısıtıdır.
Yaygın kurulum, iki availability zone’da iki interface endpoint ve bir NAT Gateway’dir. AWS’in VPC için yayımladığı ücretlerle (availability zone başına interface endpoint saati $0.01, NAT Gateway saati $0.045) bu kurulum saatte 4 × $0.01 + $0.045 = $0.085, 730 saat üzerinden ayda yaklaşık $62 eder; üstelik daha hiçbir gigabyte akmadan. Bu taban, function’lar hiç çalışmasa bile durur; yani bir function’ı VPC’ye bağlamak envantere sabit bir aylık maliyet ekler.
Cross-Account Lambda Execution Pattern’leri#
Multi-Account Mimarisi için IAM Stratejisi#
Lambda function’ları birden fazla AWS hesabında yönetmek dikkatli IAM tasarımı gerektirir:
// Cross-account erişim için assume role pattern'i
import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts';
import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb';
import type { AwsCredentialIdentity } from '@aws-sdk/types';
export class CrossAccountExecutor {
private stsClient: STSClient;
constructor() {
this.stsClient = new STSClient({});
}
async executeInAccount<T>(
accountId: string,
roleName: string,
action: (credentials: AwsCredentialIdentity) => Promise<T>
): Promise<T> {
const response = await this.stsClient.send(new AssumeRoleCommand({
RoleArn: `arn:aws:iam::${accountId}:role/${roleName}`,
RoleSessionName: `lambda-cross-account-${Date.now()}`,
DurationSeconds: 3600,
// Hedef roldeki sts:ExternalId koşuluyla eşleşmeli
ExternalId: process.env.CROSS_ACCOUNT_EXTERNAL_ID
}));
const issued = response.Credentials!;
// Credential'ların, ihtiyaç duyan client'a ulaşması gerekir. Bu
// credential'lar verilmeden kurulan bir client function'ın kendi rolünü
// kullanmaya devam eder; pattern'in sessiz hata biçimi budur.
return action({
accessKeyId: issued.AccessKeyId!,
secretAccessKey: issued.SecretAccessKey!,
sessionToken: issued.SessionToken!,
expiration: issued.Expiration
});
}
}
// Kullanım
await executor.executeInAccount('222222222222', 'CrossAccountLambdaRole',
(credentials) => new DynamoDBClient({ credentials }).send(
new GetItemCommand({ TableName: 'shared-config', Key: { id: { S: 'billing' } } })
)
);
Cross-Account Resource Access Pattern’i#
# Cross-account Lambda execution için IAM role
CrossAccountExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: CrossAccountLambdaRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS:
- arn:aws:iam::ACCOUNT-A:role/LambdaExecutionRole
- arn:aws:iam::ACCOUNT-B:role/LambdaExecutionRole
Action: sts:AssumeRole
Condition:
StringEquals:
'sts:ExternalId': 'unique-external-id-per-partner'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CrossAccountAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- s3:GetObject
- s3:PutObject
Resource:
- arn:aws:dynamodb:*:*:table/shared-*
- arn:aws:s3:::shared-bucket/*
Advanced Dependency Yönetimi ve Güvenlik#
CI/CD’de Dependency Scanning#
Lambda deployment paketleri, kimsenin elle gözden geçirebileceğinden daha hızlı transitive dependency biriktirir. Kontrolün yeri CI’dır; orada bir merge’ü bloklar, üç ayda bir yapılan denetimde ortaya çıkmaz:
# GitHub Actions workflow
name: Lambda Security Scan
on:
push:
paths:
- 'lambda/**'
- 'package*.json'
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Node.js Security Audit
run: |
npm audit --audit-level moderate
npm audit fix --dry-run
- name: Dependency Vulnerability Scan
uses: securecodewarrior/github-action-add-sarif@v1
with:
sarif-file: 'security-scan-results.sarif'
- name: Check for Secrets
uses: trufflesecurity/trufflehog@v3.34.0
with:
path: ./
base: main
head: HEAD
Runtime Güvenlik Pattern’leri#
// Güvenli environment variable handling
export class SecureConfig {
private static instance: SecureConfig;
private config: Map<string, string> = new Map();
private constructor() {
this.loadConfig();
}
public static getInstance(): SecureConfig {
if (!SecureConfig.instance) {
SecureConfig.instance = new SecureConfig();
}
return SecureConfig.instance;
}
private loadConfig() {
// Runtime'da Parameter Store'dan yükle
const requiredParams = [
'DB_CONNECTION_STRING',
'API_KEY',
'JWT_SECRET'
];
// Tüm gerekli parameter'ların var olduğunu doğrula
const missingParams = requiredParams.filter(
param => !process.env[param]
);
if (missingParams.length > 0) {
throw new Error(`Eksik gerekli parameter'lar: ${missingParams.join(', ')}`);
}
requiredParams.forEach(param => {
this.config.set(param, process.env[param]!);
});
}
public get(key: string): string {
const value = this.config.get(key);
if (!value) {
throw new Error(`Configuration key '${key}' bulunamadı`);
}
return value;
}
}
Lambda Faturası Aslında Nereye Gidiyor#
Maliyeti Function’lara Dağıtmak#
Cost Explorer varsayılan olarak “hangi servis” sorusunu yanıtlar. “Hangi function” sorusunu ise, her function Billing konsolunda etkinleştirilmiş bir cost allocation tag’i taşımadıkça yanıtlayamaz:
import {
CostExplorerClient,
GetCostAndUsageCommand
} from '@aws-sdk/client-cost-explorer';
// Cost allocation tag'ine göre gruplamak, tek bir Lambda kalemini function
// bazlı bir dağılıma çevirir. Tag'siz function'lar tek kovada toplanır.
export async function lambdaCostByFunction(start: string, end: string) {
const client = new CostExplorerClient({});
const response = await client.send(new GetCostAndUsageCommand({
TimePeriod: { Start: start, End: end }, // YYYY-MM-DD, End hariç
Granularity: 'MONTHLY',
Metrics: ['UnblendedCost'],
Filter: { Dimensions: { Key: 'SERVICE', Values: ['AWS Lambda'] } },
GroupBy: [{ Type: 'TAG', Key: 'function-name' }]
}));
return (response.ResultsByTime ?? []).flatMap(period =>
(period.Groups ?? []).map(group => ({
period: period.TimePeriod?.Start,
// TAG grupları "tagKey$tagValue" biçiminde döner
function: group.Keys?.[0]?.split('$')[1] || 'untagged',
cost: Number(group.Metrics?.UnblendedCost?.Amount ?? 0)
}))
);
}
Kullanımı iki sınır belirliyor. Cost Explorer API çağrı başına faturalanır ve verisi gerçek kullanımın bir güne kadar gerisinden gelir. Aylık gözden geçirmeye uygundur, deploy sırasındaki geri besleme döngüsüne değil.
Önce Bakılması Gereken Üç Etken#
1. Bir kez ayarlanıp bir daha dönülmeyen memory. Her Lambda REPORT satırı hem Memory Size hem Max Memory Used değerini taşır; AWS’in best practices sayfası da bu karşılaştırmayı Memory Size: 128 MB Max Memory Used: 18 MB örnek satırı üzerinden anlatır. Tam bir trafik döngüsü boyunca tepe kullanımı ayrılan miktarın çok altında kalan bir function, hiç dokunmadığı GB-saniyeler için ödeme yapar. Tuzak şu: tersi de en az o kadar yaygındır, çünkü memory aynı zamanda CPU payı satın alır. Lambda Power Tuning projesi kendi README’sinde iki yönü de kayda geçiriyor: bir iş yükü 128 MB’ta 35 saniye sürerken 1,5 GB’ta 3 saniyenin altına iniyor ve %14 daha ucuza çalışıyor; bir diğeri 128 MB’ta 2,4 saniyeden 1 GB’ta 300 ms’ye düşerken ortalama maliyeti aynı kalıyor. Hangi sonucun çıkacağı koda bakarak tahmin edilemez; AWS’in bu aracı adıyla önermesinin nedeni de bu.
Compute Optimizer aynı soruyu CloudWatch geçmişinden yanıtlar; boş bir sonucu “sorun yok” diye okumadan önce kör noktalarını bilmekte fayda var. AWS, Lambda önerilerini yalnızca 1.792 MB veya altında konfigüre edilmiş ve son 14 günde en az 50 kez çağrılmış function’lar için üretiyor. Bu memory değerinin üstünde bulgu nedeni Inconclusive, bu çağrı sayısının altında Insufficient data oluyor; Unavailable bulgusu alan function’lar ise konsolda hiç görünmüyor. Yanlış boyutlanmış olma ihtimali en yüksek function’lar çoğu zaman aracın yargılamaktan kaçındığı function’lardır.
Bir incelik daha, ama ne kadar uzağa taşıdığı sınırlı. Tanasa’nın denemeleri 256 MB ile 6 GB arasındaki tahsislerde cold start’ta iyileşme bulmadı. O function’ın init’ine paketin açılması ve yüklenmesi hâkimdi; fazladan CPU bunu kısaltmaz. Memory’nin satın aldığı CPU environment’ın tamamı için geçerlidir; init yolu da o environment’ın içinde çalışır. AWS, daha yüksek bir ayarın işe yaradığı yerler arasında import edilen kütüphaneleri ve layer’ları sayıyor. Başlangıçta gerçekten iş yapan bir init yolu, daha fazla memory ile hâlâ hızlanabilir. Hangi durumda olduğunuzu iki farklı tahsiste ölçülen Init Duration belirler.
2. Gerekçesini geride bırakmış provisioned concurrency. AWS mekaniği açıkça yazıyor: environment örneği hiçbir isteği işlemese bile initialization faturalanır; provisioned concurrency sürekli çalışır ve initialization ile invocation maliyetlerinden ayrı faturalanır. Yayımlanmış ücreti bir takvim ayıyla çarpınca taban dört işleme iniyor. GB-saniye başına $0.0000041667 üzerinden 730 saat boyunca tutulan bir provisioned gigabyte $0.0000041667 × 3600 × 730, yani yaklaşık $10.95 eder; daha tek bir istek gelmeden. ARM’de aynı gigabyte, GB-saniye başına $0.0000033334 üzerinden yaklaşık $8.76’dır. Bir lansmandan kalan sabit yükü bulmak için bunu memory boyutu ve konfigüre edilen sayıyla çarpın. Bir kısmı geri gelir: provisioned bir environment’ta süre, on-demand $0.0000166667 yerine GB-saniye başına $0.0000097222 üzerinden faturalanır; yoğun çalışan bir provisioned function tabanının bir bölümünü böyle geri kazanır.
Provisioned concurrency genelde bir lansman için alınır, lansmana göre ve AWS’in önerdiği %10’luk payla boyutlanır, sonra bir daha gözden geçirilmez. Konfigüre edilen sayıyı ProvisionedConcurrencyUtilization metriğiyle karşılaştırın; sıfıra yakın seyreden bir değer, karşılığı olmayan sabit bir aylık ödeme demektir. İşin diğer yarısını loglardan çözmeyi de beklemeyin: Tanasa’nın ölçümü provisioned concurrency 1 ile çalıştı ve isteklerin %16 ile %18’inde yine bir init süresi kaydetti. Bunlar, standart CloudWatch loglarının gerçek bir cold start’tan ayıramadığı asenkron yeniden hazırlıktan geliyor.
3. Dört işi birden yapan tek function. Zamanla birkaç sorumluluğu üstlenen bir handler, her isteğe hepsinin bedelini ödetir. Bütün dependency’ler init sırasında yüklenir; hiçbirine dokunmayan istekler için de. Memory ayarının en ağır dalı karşılaması gerekir, dolayısıyla ucuz dallar pahalı dalın ücretinden faturalanır. Execution role, hangi dal neye ihtiyaç duyuyorsa hepsinin birleşimini taşımak zorundadır; bu da faturayla birlikte etki alanını genişletir. Sorumluluğa göre bölmek her paketi küçültür, her parçanın kendi memory ayarını almasını sağlar ve rolleri daraltır. Aynı zamanda deploy ettiğiniz, izlediğiniz ve yetkilendirdiğiniz yüzeyi çoğaltır; bu yüzden ancak sorumlulukların kaynak profilleri gerçekten farklıysa kazandırır. Bu takasın karşılığı için yayımlanmış bir rakam yok; bir tasarruf öngörmeden önce bu biçimi kendi etiketlenmiş maliyet raporunuzda arayın.
Memory Optimizasyon Otomasyonu#
Bir script’in burada yapabileceği şey aday üretmektir, o adayı uygulamak değil. Memory her iki yönde de CPU payı satın alır. Function’ın hiç ulaşmadığı bir tahsisi kısmak, süreyi kazandırdığı GB-saniyeden pahalıya gelecek kadar uzatabilir ya da bir gecikme hedefini bütçesinin dışına taşırabilir. Aşağıdaki çıktıyı deploy edilecek bir değer değil, Power Tuning’e girecek bir kısa liste olarak görün:
// CloudWatch metriklere dayalı otomatik memory optimizasyonu
export class MemoryOptimizer {
async optimizeFunction(functionName: string) {
const metrics = await this.getCloudWatchMetrics(functionName, 30); // 30 gün
const analysis = {
avgMemoryUsed: metrics.avgMemoryUsed,
maxMemoryUsed: metrics.maxMemoryUsed,
currentMemoryAllocated: metrics.currentMemory,
avgDuration: metrics.avgDuration,
invocations: metrics.invocations
};
// Ölçülecek aday tahsis; doğrudan uygulanacak değer değil
const candidateMemory = this.calculateCandidateMemory(analysis);
if (candidateMemory !== analysis.currentMemoryAllocated) {
return {
recommendation: 'BENCHMARK_MEMORY',
current: analysis.currentMemoryAllocated,
candidate: candidateMemory,
savingsIfDurationHolds: this.calculateSavings(analysis, candidateMemory),
confidence: this.calculateConfidence(metrics)
};
}
return { recommendation: 'NO_CHANGE', reason: 'Zaten optimize edilmiş' };
}
private calculateCandidateMemory(analysis: any): number {
// Gözlenen tepe kullanımın üzerine %20 pay. Memory aynı zamanda CPU payını
// belirler; hesaplama yoğun bir function bu ayarda yavaşlayıp pahalılaşabilir.
// Aşağıdaki sayı Power Tuning'in bittiği değil, başladığı yerdir.
const memoryWithBuffer = Math.ceil(analysis.maxMemoryUsed * 1.2);
// Lambda 128 MB ile 10.240 MB arasında 1 MB adımlarla her değeri kabul eder.
// 3008 MB'da biten eski sabit basamak listesi artık geçerli değil.
return Math.min(10240, Math.max(128, memoryWithBuffer));
}
}
Lambda Extensions: Custom Monitoring ve Processing#
Maliyet İzleme Extension’ı Yazmak#
Bir extension, runtime’ın yanında kendi süreci olarak çalışır. Handler’dan önce başlar ve yanıt döndükten sonra da ayakta kalır; bu yüzden koşan bir maliyet toplamını tutmak için makul bir yerdir. Aynı nedenle handler’ın değişkenlerini göremez ve faturalanan süreyi extension sürecindeki bir kronometreden değil Telemetry API’den almak zorundadır. Bu da fazladan bir parça demek. Extension bir HTTP dinleyicisi açar, ardından platform.report kayıtlarının oraya gönderilmesi için abone olur; dinleyicinin, abonelik çağrısı gitmeden önce bağlantı kabul ediyor olması gerekir:
// /opt/extensions/cost-monitor içinde dağıtılır, kendi süreci olarak çalışır
import { createServer } from 'node:http';
import { CloudWatchClient, PutMetricDataCommand } from '@aws-sdk/client-cloudwatch';
const EXTENSION_NAME = 'cost-monitor';
const API = `http://${process.env.AWS_LAMBDA_RUNTIME_API}/2020-01-01/extension`;
const TELEMETRY_API = `http://${process.env.AWS_LAMBDA_RUNTIME_API}/2022-07-01/telemetry`;
// Lambda telemetriyi yalnızca sandbox içine gönderir; 9001 portu rezervedir
const LISTENER_HOST = 'sandbox.localdomain';
const LISTENER_PORT = 4243;
class CostMonitoringExtension {
private cloudWatch = new CloudWatchClient({});
private functionName = process.env.AWS_LAMBDA_FUNCTION_NAME!;
private extensionId = '';
private billedMs = 0;
async register() {
const response = await fetch(`${API}/register`, {
method: 'POST',
headers: {
// /opt/extensions altındaki dosya adıyla birebir aynı olmalı
'Lambda-Extension-Name': EXTENSION_NAME,
'Content-Type': 'application/json'
},
body: JSON.stringify({ events: ['INVOKE', 'SHUTDOWN'] })
});
// Sonraki her çağrı bunu geri göndermeli, yoksa /event/next 403 döner
this.extensionId = response.headers.get('Lambda-Extension-Identifier')!;
}
// Lambda kayıtları dizi halinde buraya POST eder; sunucu, aşağıdaki abonelik
// gönderilmeden önce dinliyor olmalı
startTelemetryListener(): Promise<void> {
return new Promise(resolve => {
createServer((req, res) => {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', () => {
for (const event of JSON.parse(body)) {
if (event.type === 'platform.report') {
this.recordReport(event.record.metrics.billedDurationMs);
}
}
res.writeHead(200).end();
});
}).listen(LISTENER_PORT, LISTENER_HOST, () => resolve());
});
}
// Yukarıdaki register yalnızca yaşam döngüsü olaylarını verir. Faturalanan
// süre ayrı bir aboneliktir; onu taşıyan kayıt da platform.report'tur.
async subscribeToTelemetry() {
await fetch(TELEMETRY_API, {
method: 'PUT',
headers: {
'Lambda-Extension-Identifier': this.extensionId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
schemaVersion: '2025-01-29',
types: ['platform'],
// Küçük buffer daha erken teslim eder, karşılığı daha çok POST
buffering: { maxItems: 1000, maxBytes: 262144, timeoutMs: 100 },
destination: {
protocol: 'HTTP',
URI: `http://${LISTENER_HOST}:${LISTENER_PORT}`
}
})
});
}
// platform.report kaydı başına bir çağrı
private recordReport(billedDurationMs: number) {
this.billedMs += billedDurationMs;
}
async processEvents() {
while (true) {
const response = await fetch(`${API}/event/next`, {
method: 'GET',
headers: { 'Lambda-Extension-Identifier': this.extensionId }
});
const event = await response.json();
// Yukarıdaki bloklayan GET, aynı zamanda extension'ın bir sonraki
// invoke'a hazır olduğunu Lambda'ya bildirir; döngü onu atlayamaz.
if (event.eventType === 'SHUTDOWN') {
await this.flush();
return;
}
}
}
private async flush() {
const memoryMB = Number(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE);
await this.cloudWatch.send(new PutMetricDataCommand({
Namespace: 'Lambda/Cost',
MetricData: [{
MetricName: 'EnvironmentCost',
Value: this.estimateCost(this.billedMs, memoryMB),
Unit: 'None',
Dimensions: [
{ Name: 'FunctionName', Value: this.functionName },
{ Name: 'MemorySize', Value: String(memoryMB) }
]
}]
}));
}
private estimateCost(billedMs: number, memoryMB: number): number {
// İlk kademe x86 on-demand ücreti; istek başına ücret ayrı faturalanır
const gbSeconds = (memoryMB / 1024) * (billedMs / 1000);
return gbSeconds * 0.0000166667;
}
}
const extension = new CostMonitoringExtension();
extension.register()
.then(() => extension.startTelemetryListener())
.then(() => extension.subscribeToTelemetry())
.then(() => extension.processEvents());
Handler Sürecinde Structured Logging#
Handler sürecinde log yakalamak, extension yazmaktan farklı bir iştir ve ikisi yeterince sık karıştırılır. console’u sarmalamak, süreçler arası hiçbir kurulum gerektirmeden yapılandırılmış kayıtlar verir; karşılığında yalnızca bu sürecin yazdıklarını görür:
// Otomatik hata işaretleme ile structured logging sarmalayıcısı
class StructuredLogger {
private logs: any[] = [];
private functionName: string;
constructor() {
this.functionName = process.env.AWS_LAMBDA_FUNCTION_NAME!;
this.setupLogCapture();
}
private setupLogCapture() {
// Tüm console.* çağrılarını yakala
const originalConsole = { ...console };
console.log = (...args) => {
this.logs.push({
level: 'INFO',
timestamp: new Date().toISOString(),
message: args.join(' '),
functionName: this.functionName
});
originalConsole.log(...args);
};
console.error = (...args) => {
this.logs.push({
level: 'ERROR',
timestamp: new Date().toISOString(),
message: args.join(' '),
functionName: this.functionName,
alert: true // Anında uyarı için flag
});
originalConsole.error(...args);
};
}
async flushLogs() {
if (this.logs.length === 0) return;
// Logging service'e gönder
await this.sendToLogService(this.logs);
// Hatalar için uyarı gönder
const errors = this.logs.filter(log => log.alert);
if (errors.length > 0) {
await this.sendAlerts(errors);
}
this.logs = [];
}
}
Migration Pattern’leri: EC2/ECS’den Lambda’ya#
Bir Servis Taşınmalı mı#
İşe yarayan migration’lar servisi önce ayrıştırır, sonra taşır. Servisin aday olup olmadığına ise üç kontrol karar verir.
İlki kullanım oranı. Günün büyük bölümünde düşük CPU ile boşta duran bir container, hiç kullanmadığı ayrılmış kapasiteye para öder; istek başına faturalandırmanın uyduğu profil tam olarak budur. İkincisi çalışma profili: Lambda’nın 15 dakikalık sınırını düzenli olarak aşan, uzun ömürlü bağlantı tutan veya 10 GB’tan fazla memory isteyen işler kapsam dışıdır. Üçüncüsü trafik şekli; çünkü düz, yüksek ve gün boyu süren bir istek hızı genelde container üzerinde, invocation başına fiyatlandırmadan ucuza gelir.
Ayrıştırma:
// 1. Önce ayrık function'ları extract et
// Monolitik ECS service'den focused Lambda function'lara
// Öncesi: Her şeyi handle eden tek ECS task
class PaymentAPI {
async processPayment(req: Request) { /* ... */ }
async validateCard(req: Request) { /* ... */ }
async sendNotification(req: Request) { /* ... */ }
async updateInventory(req: Request) { /* ... */ }
}
// Sonrası: Uzmanlaşmış Lambda function'lar
// payment-processor-lambda
export const handler = async (event: PaymentEvent) => {
return processPayment(event.paymentData);
};
// card-validator-lambda
export const handler = async (event: CardEvent) => {
return validateCard(event.cardData);
};
// notification-sender-lambda
export const handler = async (event: NotificationEvent) => {
return sendNotification(event.notificationData);
};
Asıl Önemli Olan Maliyet Karşılaştırması#
Yalnızca compute fiyatlarını karşılaştırmak farkın büyük kısmını gizler. Defterin container tarafında, trafik sıfırken bile var olan sabit kalemler bulunur: task’ın kendisi, bir load balancer ve çoğunlukla bir NAT Gateway. Lambda tarafında boşta maliyet yoktur, ama istek başına ücret, GB-saniye başına ücret ve önüne konan bileşenin maliyeti eklenir. API Gateway milyon istek başına faturalanır, ALB saatlik bir tabanla birlikte trafiğe bağlı LCU ücreti getirir, Function URL’in ise Lambda fiyat listesinde kendine ait bir kalemi yoktur.
İki tarafa da yayımlanmış ücretleri koyduğunuzda karşılaştırmanın çoğu dört işleme dönüşür. Bir HTTP API arkasında ortalama 200 ms faturalanan süreye sahip 512 MB’lık bir function ile, bir ALB arkasında 0,5 vCPU ve 1 GB’lık tek bir Fargate task’ını karşılaştıralım. Aşağıdaki ücretlerin tamamı AWS fiyat sayfalarındaki us-east-1 liste fiyatlarıdır; bölgeye göre değişir ve zamanla güncellenir.
Lambda, milyon istek başına:
- İstekler: milyon başına $0.20.
- Compute: 0,5 GB × 0,2 s = istek başına 0,1 GB-saniye. GB-saniye başına $0.0000166667 (aylık 6 milyar GB-saniyeden sonra kademe inen ilk kademe x86 ücreti) üzerinden bu, milyon başına $1.67 eder.
- HTTP API: ilk 300 milyon için milyon başına $1.00.
- Toplam: milyon istek başına yaklaşık $2.87, sıfır trafikte hiçbir şey.
Container, 730 saatlik ay başına:
- Fargate: (0,5 × vCPU-saati $0.04048) + (1 × GB-saati $0.004445) = saatte $0.024685, yani $18.02.
- ALB: saatte $0.0225, yani $16.43; trafik geldiğinde üstüne LCU-saati başına $0.008 biner.
- Toplam: daha tek bir istek gelmeden yaklaşık $34.45.
$34.45’i $2.87’ye bölünce ayda yaklaşık 12 milyon istek, yani sürekli 4,6 istek/saniye çıkıyor. Bu rakam geçiş noktasının alt sınırıdır. Altında container fiyatta kazanamaz, çünkü yalnızca sabit aylık maliyeti bile Lambda faturasını aşar. Üstünde ise soru açık kalır, çünkü hesaba bir kalem daha girer. ALB, trafik gelir gelmez LCU ücreti işletir; AWS de ölçtüğü dört boyuttan hangisi yüksek çıkarsa yalnızca onu faturalar: yeni bağlantılar, aktif bağlantılar, işlenen byte’lar ve kural değerlendirmeleri. Bunların hiçbirini istek sayısı belirlemez; payload boyutu ile bağlantı yeniden kullanımı belirler. Benzer trafiği hâlihazırda taşıyan bir load balancer’ın ConsumedLCUs değerini okuyup container tarafına ekleyin; geçiş noktası 12 milyonun ötesinde bir yere düşer. Kapasite de aynı yöne iter, çünkü hızı artık karşılayamayan tek task’ın ikiye çıkması gerekir.
Her girdi bu çizgiyi oynatır. Memory’yi ya da süreyi ikiye katlarsanız yalnızca compute bileşeni oynar: $1.67, $3.34’e çıkar; bu da Lambda toplamını milyon başına yaklaşık $4.54’e, alt sınırı da ayda yaklaşık 7,6 milyon isteğe indirir. İstek başına ücret ile HTTP API ücreti bu hareketin dışında kalır. Hangi tarafa NAT Gateway eklerseniz o tarafın tabanına saatte $0.045, yani ayda yaklaşık $32.85 ve gigabyte başına $0.045 biner. İki taraf da ARM’i x86’nın yaklaşık %20 altında fiyatlıyor (Lambda GB-saniye başına $0.0000133334, Fargate vCPU-saati başına $0.03238); yani mimari değiştirmek karşılaştırmayı sonuçlandırmaz, iki tarafı birden kaydırır.
Geçişin ters yönde yaşandığı da olur ve bunun belgelenmiş bir örneği var. InfoQ’nun 2023’te aktardığına göre Prime Video’nun ses ve video kalitesi izleme servisi, Step Functions, Lambda ve S3 üzerine kurulu tasarımdan ECS on EC2 üzerinde tek bir sürece taşındı; operasyonel maliyetlerde bildirilen azalma %90 oldu. Serverless tasarım, state transition hesap limitine takılmadan önce beklenen yükün ancak yaklaşık %5’ini karşılayabiliyordu. Nedeni dikkatle okumakta fayda var: maliyeti büyüten kalemler o state transition’lar ile ara video kareleri ve ses tamponları için S3’e yapılan yoğun okuma ve yazmalardı. Lambda GB-saniyeleri hiçbir zaman baskı altındaki kalem olmadı. Genelleşen şey mekanizma: çok yüksek frekanslı bir yolda birim başına faturalandırma bir noktadan sonra her şeyin önüne geçiyor. Amazon özgün mühendislik yazısını yayından kaldırdı; anlatı ikincil kaynaklar üzerinden ayakta duruyor.
Karar vermeden önce iki tarafı da kendi trafiğinizle hesaplayın.
Migration Tuzakları ve Çözümleri#
1. State Yönetimi Zorluğu
// Sorun: ECS service in-memory caching'e sahipti
// Çözüm: DynamoDB ile external state
// Öncesi (ECS memory'sinde)
const cache = new Map<string, UserData>();
// Sonrası (DynamoDB ile Lambda)
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
export class UserCache {
private dynamodb = new DynamoDBClient({});
async get(userId: string): Promise<UserData | null> {
// Caching için TTL ile DynamoDB kullan
const result = await this.dynamodb.send(new GetItemCommand({
TableName: 'UserCache',
Key: { userId: { S: userId } }
}));
return result.Item ? JSON.parse(result.Item.data.S!) : null;
}
}
2. Connection Pool Migration’ı
// Sorun: ECS persistent DB connection'lara sahipti
// Çözüm: RDS Proxy ile invocation başına connection
// Öncesi (persistent connection'larla ECS)
const pool = new Pool({
host: 'db.internal',
max: 20,
idleTimeoutMillis: 30000
});
// Sonrası (RDS Proxy ile Lambda)
import { Client } from 'pg';
export const handler = async (event: any) => {
const client = new Client({
host: 'rds-proxy.cluster-xyz.us-east-1.rds.amazonaws.com',
port: 5432,
database: 'mydb',
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: { rejectUnauthorized: false }
});
await client.connect();
try {
const result = await client.query('SELECT * FROM users WHERE id = $1', [event.userId]);
return result.rows[0];
} finally {
await client.end();
}
};
Advanced Mimari Pattern’leri#
Lambda ile Event-Driven Mimari#
// Distributed transaction'lar için Saga pattern implementasyonu
import { SFNClient, StartExecutionCommand } from '@aws-sdk/client-sfn';
export class PaymentSaga {
private stepFunctions: SFNClient;
constructor() {
this.stepFunctions = new SFNClient({});
}
async executePayment(paymentData: PaymentData) {
// State machine ile birlikte deploy edilir, invocation başına yeniden
// kurulmaz. Buraya alındı, çünkü atlanan kısım telafi dallarıdır.
const sagaDefinition = {
Comment: 'Payment processing saga',
StartAt: 'ValidatePayment',
States: {
ValidatePayment: {
Type: 'Task',
Resource: 'arn:aws:lambda:us-east-1:123456789:function:validate-payment',
Next: 'ProcessPayment',
Catch: [
{
ErrorEquals: ['ValidationError'],
Next: 'PaymentFailed'
}
]
},
ProcessPayment: {
Type: 'Task',
Resource: 'arn:aws:lambda:us-east-1:123456789:function:process-payment',
Next: 'UpdateInventory',
Catch: [
{
ErrorEquals: ['PaymentError'],
Next: 'CompensateValidation'
}
]
},
UpdateInventory: {
Type: 'Task',
Resource: 'arn:aws:lambda:us-east-1:123456789:function:update-inventory',
Next: 'SendConfirmation',
Catch: [
{
ErrorEquals: ['InventoryError'],
Next: 'CompensatePayment'
}
]
},
SendConfirmation: {
Type: 'Task',
Resource: 'arn:aws:lambda:us-east-1:123456789:function:send-confirmation',
End: true
},
// Compensation state'leri
CompensatePayment: {
Type: 'Task',
Resource: 'arn:aws:lambda:us-east-1:123456789:function:refund-payment',
Next: 'CompensateValidation'
},
CompensateValidation: {
Type: 'Task',
Resource: 'arn:aws:lambda:us-east-1:123456789:function:cleanup-validation',
Next: 'PaymentFailed'
},
PaymentFailed: {
Type: 'Fail',
Cause: 'Payment processing başarısız'
}
}
};
const execution = await this.stepFunctions.send(new StartExecutionCommand({
stateMachineArn: process.env.PAYMENT_SAGA_STATE_MACHINE!,
input: JSON.stringify(paymentData)
}));
return execution.executionArn;
}
}
Lambda için Circuit Breaker Pattern#
// External service çağrıları için circuit breaker
export class CircuitBreaker {
private failures: number = 0;
private lastFailureTime: number = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
constructor(
private failureThreshold: number = 5,
private recoveryTimeMs: number = 60000
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.recoveryTimeMs) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker OPEN');
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
private onFailure() {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
}
}
}
// Lambda function'da kullanım
const circuitBreaker = new CircuitBreaker(5, 30000);
export const handler = async (event: any) => {
try {
return await circuitBreaker.execute(async () => {
return await callExternalService(event.data);
});
} catch (error) {
return {
statusCode: 503,
body: JSON.stringify({ error: 'Service geçici olarak kullanılamıyor' })
};
}
};
Seri Özeti#
Bölüm 1 cold start’ları ve runtime seçimini, bölüm 2 memory ve süreyi, bölüm 3 function’lar çalışırken neyi görebildiğinizi ele aldı. Yukarıdaki pattern’ler ise bir Lambda envanterinin bunlardan sonra ihtiyaç duyduğu şeyler: paylaşılan dependency’ler, private ağ, hesaplar arası sınırlar ve bütçe toplantısından sağ çıkan bir maliyet modeli.
Varsayılan, sıradan durumda geçerlidir. Hiçbir şeyi yeniden yazmadan önce memory ve provisioned concurrency ayarlarını CloudWatch verisiyle denetleyin, private bir kaynağa ihtiyacı olmayan function’ları VPC dışında tutun ve her Layer’ın byte’larını bedava bir soyutlama gibi değil, init yolundaki bir bütçe kalemi olarak hesaba katın. İş yükü aksini söylediğinde bu varsayılanı geçersiz kılın. Hesaplama yoğun bir function çoğu zaman memory ayarı yükseldikçe ucuzlar; bir gecikme hedefi de liste fiyatından provisioned concurrency’ye değebilir. Düz ve gün boyu süren trafiğe sahip bir servis ise, ayrıştırma ne kadar temiz görünürse görünsün container üzerinde kalabilir.
İlk adım hiç kod değişikliği gerektirmez: kayda değer trafiği olan her function için Max Memory Used değerini çekin ve ayrılan memory ile karşılaştırın.
Tüm AWS Lambda rehber serisi:
- Bölüm 1: Cold Start Optimizasyonu ve Runtime Seçimi
- Bölüm 2: Memory Allocation ve Performance Tuning
- Bölüm 3: Production Monitoring ve Debugging Stratejileri
- Bölüm 4: Advanced Patterns ve Maliyet Optimizasyonu (Bu yazı)
Kaynaklar#
- Understanding the Lambda execution environment lifecycle (yeni sekmede açılır) - Extension init, runtime init ve function init’in paylaştığı 10 saniyelik
Initbütçesi, provisioned concurrency ile SnapStart’ın bunun yerine aldığı daha uzun bütçe ve AWS’in cold start’ların tipik sıklığı ile süresi için verdiği rakamlar. - Augment Lambda functions using Lambda extensions (yeni sekmede açılır) - Extension taşıyan layer’ların init sırasında
/optaltına nasıl açıldığı, function ve extension’ları için 250 MB’lık açılmış boyut tavanı ve extension’ın handler ile paylaştığı CPU, memory ve depolama. - Working with layers for Rust Lambda functions (yeni sekmede açılır) - AWS’in bir runtime ailesi için layer kullanımını önermemesi; gerekçe, init sırasında assembly’lerin elle yüklenmesinin cold start süresini artırması.
- Lambda Insights extension versions for ARM64 (yeni sekmede açılır) - Gerçek bir monitoring layer’ının yayımlanmış boyutları; 1.0.404.0 sürümünün binary, zip ve agent memory tarafında getirdiği küçülmeler dahil.
- Size is (almost) all that matters for optimizing AWS Lambda cold starts (yeni sekmede açılır) - Adrian Tanasa’nın yöntemiyle birlikte yayımlanmış bağımsız ölçümü: paket boyutuna karşı init süresi, aynı byte’lar layer’a taşındığında ne olduğu, memory’nin cold start’a etkisi ve provisioned concurrency altında yine görülen init süreleri.
- Lambda quotas (yeni sekmede açılır) - Yukarıda kullanılan sert sınırlar: layer’lar dahil 250 MB açılmış deployment paketi, function başına beş layer, 128 MB ile 10.240 MB arası memory ve 15 dakikalık çalışma tavanı.
- Giving Lambda functions access to resources in an Amazon VPC (yeni sekmede açılır) - Lambda’nın Hyperplane ENI kullanarak VPC kaynaklarına nasıl bağlandığı ve VPC bağlamanın performans etkileri.
- Amazon VPC fiyatlandırması (yeni sekmede açılır) - NAT Gateway saatlik ve gigabyte başına ücretleri ile availability zone başına PrivateLink interface endpoint fiyatlandırması; yukarıdaki private yol tabanının dayandığı ücretler.
- Best practices for working with AWS Lambda functions (yeni sekmede açılır) - Fazla ayrılmış memory’yi
REPORTsatırından yakalama yöntemi, AWS’in kendi örnek satırı ve Lambda Power Tuning’i adıyla önermesi. - Configure Lambda function memory (yeni sekmede açılır) - AWS’in CPU’yu memory ile orantılı ayırdığını belirtmesi ve daha yüksek bir ayarın işe yaradığı iş yükleri; import edilen kütüphaneler ile layer’lar da bunların arasında.
- AWS Lambda Power Tuning (yeni sekmede açılır) - Bir function’ı farklı memory ayarlarında ölçen Step Functions state machine’i ve README’deki sonuçlar: daha fazla memory farklı iş yüklerinde hem ucuza hem maliyet-nötr çıkabiliyor.
- Resource requirements (AWS Compute Optimizer) (yeni sekmede açılır) - Compute Optimizer’ın bir Lambda function’ını değerlendirmesi için gereken memory tavanı ile çağrı sayısı ve değerlendirmediği durumlarda her bulgu nedeninin anlamı.
- Configuring provisioned concurrency for a function (yeni sekmede açılır) - AWS’in, environment hiç istek karşılamasa bile initialization’ı faturaladığını belirtmesi, boyutlandırmada önerdiği %10’luk pay ve hesap eşzamanlılık sınırı.
- AWS Lambda fiyatlandırması (yeni sekmede açılır) - Mimariye göre istek başına ve GB-saniye başına ücretler, ilk kademenin üstündeki kademeler ve provisioned concurrency ile provisioned environment süresinin ayrı ücretleri.
- AWS Fargate fiyatlandırması (yeni sekmede açılır) - Yukarıdaki başabaş hesabında kullanılan vCPU-saati ve GB-saati ücretleri; x86’nın yanında ARM ücretleriyle birlikte.
- Elastic Load Balancing fiyatlandırması (yeni sekmede açılır) - Application Load Balancer’ın saatlik ücreti, üzerine binen LCU ücreti ve AWS’in ölçüp içlerinden yalnızca en yükseğini faturaladığı dört boyut.
- Amazon API Gateway fiyatlandırması (yeni sekmede açılır) - HTTP API ve REST API’nin milyon istek başına ücretleri; Lambda karşılaştırmalarının çoğunu oynatan giriş kapısı farkı.
- Prime Video Switched from Serverless to EC2 and ECS to Save Costs (yeni sekmede açılır) - InfoQ’nun taşıma anlatısı; serverless tasarımı sınırlayan state transition limiti ve birleştirme sonrası bildirilen maliyet azalması dahil. Amazon özgün mühendislik yazısını kaldırdığı için ayakta kalan kaynak budur.
- Lambda Extensions API (yeni sekmede açılır) - Register ve sonraki olay uç noktaları,
Lambda-Extension-Identifierbaşlığı ve bir extension’ın abone olabileceği yaşam döngüsü olayları. - Lambda Telemetry API (yeni sekmede açılır) - Yukarıda kullanılan abonelik isteği, dinleyici ve buffering gereksinimleri ile faturalanan süreyi taşıyan
platform.reportkaydı.
AWS Lambda Production Rehberi: 5 Yıllık Gerçek Dünya Deneyimi
5+ yıllık production deneyimine dayalı kapsamlı AWS Lambda rehberi. Cold start optimizasyonu, performans ayarlama, monitoring ve maliyet optimizasyonu ile gerçek savaş hikayeleri ve pratik çözümler.
Bu serideki tüm yazılar
İlgili yazılar
İç servis katmanı kurmadan önce kurup kurmayacağınıza karar verin. Katmanın çağrı başına maliyeti, VPC Lattice'in kazandığı hacim ve direct invoke'un hâlâ kazandığı an.
aws · aws-cdk · lambda +4
Private REST API gRPC’yi yapısal olarak taşıyamaz ve gRPC konuşan her AWS yüzeyi Lambda hedeflerini dışlar. gRPC’den neyi tutmalı, neyi bırakmalı.
aws · aws-cdk · lambda +4
Private REST API, onu açan resource policy, route bazlı AWS_IAM yetkileri, iki CDK stack'i ve Node 22 Lambda'dan çağrıyı imzalamak.
aws · aws-cdk · lambda +4
SigV4 hangi servisin çağırdığını kanıtlar, kimin adına çağırdığını değil. Doğrulanmış özneyi nasıl taşırsınız ve taşıma katmanı gerçekte neyi şifreler.
aws · aws-cdk · lambda +4
Aynı hesapta resource policy ile çağıranın identity policy'si bir OR'dur. Hesaplar arası çağrıda AND'e döner ve sessizlik reddeder. Veri sınırında bunun sonuçları.
aws · aws-cdk · lambda +4