So verhindern Sie max_connections-Fehler bei der Verbindung von Vercel mit AWS Aurora Postgres
TuBrief 편집팀
2026년 7월 21일
0
Computing/Software원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
커뮤니티의 다른 글
댓글 (0)
Log in to leave a comment
아직 작성된 글이 없습니다
원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
Log in to leave a comment
아직 작성된 글이 없습니다
Die meisten Tutorials für Serverless-Infrastrukturen zeigen nur, wie man ein paar Mal auf Verknüpfen klickt. Das Problem kommt danach: Sobald der Code bereitgestellt ist und auch nur ein wenig Traffic entsteht, bricht die DB-Verbindung zusammen. Sie geraten ins Schwitzen, weil Sie statische Access Keys verwalten müssen, oder Sie erhalten eine Rechnung mit mehreren hundert Dollar für Datenübertragungskosten.
Hier sind die praktischen Engpässe und Lösungen zusammengefasst, die bei der Anbindung eines Vercel-Frontends an ein AWS-Backend auftreten.
PostgreSQL startet jedes Mal einen neuen Prozess, wenn sich ein Client verbindet. Jeder Prozess verbraucht mindestens 2 MB bis 8 MB Speicher. Da Vercel Serverless Functions bei eingehenden Anfragen zustandslose Instanzen unbegrenzt hochskalieren, wird das max_connections-Limit von Aurora Postgres (100–300 Verbindungen) innerhalb weniger Sekunden erreicht.
Schließlich erscheint der Fehler FATAL: sorry, too many clients already, die CPU-Auslastung der DB erreicht 100 % und der Dienst stürzt ab.
Sie müssen PgBouncer zwischen den Serverless Functions und Aurora platzieren und den Transaction Pooling-Modus aktivieren. Session Pooling hilft nicht, da es Verbindungen 1:1 hält, und Statement Pooling unterstützt keine Multi-Statement-Transaktionen. Transaction Pooling weist die DB-Verbindung nur für die Dauer der Abfragetransaktion zu und gibt sie sofort nach dem COMMIT an den Pool zurück.
Hier ist die Konfiguration für die Datei pgbouncer.ini:
`ini
[pgbouncer]
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 20
reserve_pool_size = 10
max_db_connections = 80
`
pool_mode = transaction.max_client_conn auf etwa 10000 und erhöhen Sie das ulimit -n-Limit des Betriebssystems.default_pool_size ist die Anzahl der Verbindungen, die pro DB-Benutzer und DB-Paar zu Aurora offengehalten werden sollen. Setzen Sie diesen Wert zwischen 10 und 20.max_db_connections fest auf 80 einstellen, können Sie die tatsächliche Obergrenze der Verbindungen zur Aurora-Instanz erzwungen steuern.In der Vercel Fluid Compute-Umgebung teilen sich mehrere Ausführungsaufrufe den globalen Scope. Daher müssen Sie den DB-Treiber-Pool im Modulscope deklarieren. Mit dem Paket @vercel/functions und attachDatabasePool werden ungenutzte Verbindungen sauber aufgeräumt, bevor die Funktionsinstanz beendet wird.
`typescript
import { Pool } from 'pg';
import { attachDatabasePool } from '@vercel/functions';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
idleTimeoutMillis: 5000,
});
attachDatabasePool(pool);
export default pool;
`
Auch der Connection String sollte nach Umgebung getrennt werden. In der lokalen Entwicklungsumgebung (.env.local) verbinden Sie sich direkt mit dem Aurora-Port (5432) oder verweisen auf den lokalen PgBouncer. In der Produktion (.env.production) hängen Sie den PgBouncer-Port (6432) und den Parameter pgbouncer=true an.
`bash
DATABASE_URL="postgresql://dbuser:dbpassword@aurora-cluster.us-east-1.rds.amazonaws.com:5432/app_dev?sslmode=require"
DATABASE_URL="postgresql://dbuser:dbpassword@pgbouncer-proxy.internal:6432/app_prod?sslmode=require&pgbouncer=true"
`
Das direkte Eintragen von AWS_ACCESS_KEY_ID und AWS_SECRET_ACCESS_KEY in die Vercel-Umgebungsvariablen ist gefährlich. Wenn die Schlüssel durchsickern, wird die gesamte Infrastruktur kompromittiert. Wenn Sie Administratorrechte wie AdministratorAccess vergeben haben, wird die Situation noch dramatischer.
Verwenden Sie OIDC (OpenID Connect). Dabei wird ein von Vercel ausgestelltes, signiertes JWT an die AssumeRoleWithWebIdentity-API von AWS STS gesendet, um temporäre Anmeldeinformationen mit einer Gültigkeit von 1 Stunde zu erhalten. Dadurch entfallen fest codierte Schlüssel komplett.
In der AWS IAM Role Vertrauensstellungspolitik sollten nur die Mindestrechte definiert werden:
`json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.vercel.com/my-team-slug"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.vercel.com/my-team-slug:aud": "https://vercel.com/my-team-slug",
"oidc.vercel.com/my-team-slug:sub": "owner:my-team-slug:project:my-ai-app:environment:production"
}
}
}
]
}
`
[oidc.vercel.com/my-team-slug](https://oidc.vercel.com/my-team-slug) im IAM Identity Provider.aud-Claim auf Ihre Vercel-Team-URL, um den Zugriff anderer Organisationen zu verhindern.sub-Claim-Bedingung exakt auf den Projektnamen (my-ai-app) und die Umgebung (production). Dies ist die Schlüsselkonfiguration zur Verhinderung von Confused-Deputy-Angriffen.Auch die Berechtigungsrichtlinie sollte nur die erforderlichen Ressourcen freigeben. Hier ist ein Beispiel, das nur Zugriff auf einen bestimmten S3-Bucket und das Aufrufen einer Lambda-Funktion gewährt:
`json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictedS3BucketAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-production-ai-assets",
"arn:aws:s3:::my-production-ai-assets/*"
]
},
{
"Sid": "RestrictedLambdaInvocation",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"arn:aws:lambda:us-east-1:123456789012:function:python-ml-inference-service"
]
}
]
}
`
In Node.js-Code rufen Sie temporäre Anmeldeinformationen mit @vercel/oidc-aws-credentials-provider ab:
`typescript
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { awsCredentialsProvider } from '@vercel/oidc-aws-credentials-provider';
const credentials = awsCredentialsProvider({
roleArn: process.env.AWS_ROLE_ARN!,
});
const s3Client = new S3Client({ region: 'us-east-1', credentials });
const lambdaClient = new LambdaClient({ region: 'us-east-1', credentials });
export async function POST(req: Request) {
const body = await req.json();
await s3Client.send(new PutObjectCommand({
Bucket: 'my-production-ai-assets',
Key: inputs/${Date.now()}.json,
Body: JSON.stringify(body),
}));
const lambdaRes = await lambdaClient.send(new InvokeCommand({
FunctionName: 'python-ml-inference-service',
Payload: Buffer.from(JSON.stringify(body)),
}));
return Response.json({
status: 'success',
result: JSON.parse(Buffer.from(lambdaRes.Payload!).toString()),
});
}
`
Wenn Ihr Vercel-Frontend an einem Tokyo PoP läuft und das AWS-Backend in US-East (us-east-1) liegt, kommen bei jeder Anfrage 150 ms bis 250 ms physikalische Latenz hinzu.
Es ist sinnvoll, API-Antworten, die sich nicht häufig ändern, auf Vercel Edge Middleware-Ebene zu cachen, damit der Backend-Aufruf gar nicht erst weitergeleitet wird.
`typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export const config = {
matcher: ['/api/v1/ml-models/:path*'],
};
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set(
'Cache-Control',
'public, s-maxage=60, stale-while-revalidate=120'
);
response.headers.set(
'Vercel-CDN-Cache-Control',
's-maxage=300, stale-while-revalidate=600'
);
return response;
}
`
Wenn Sie s-maxage=60, stale-while-revalidate=120 angeben, wird 60 Sekunden lang sofort aus dem Edge-Cache geantwortet. Nach Ablauf des Caches wird bis zu 120 Sekunden lang vorerst die alte Antwort geliefert, während der Cache im Hintergrund aktualisiert wird.
Um Tracing zwischen Diensten zu verbinden, müssen Sie den W3C TraceContext-Standard (traceparent-Header) von OpenTelemetry weiterleiten. Hier ist die Vercel-Konfiguration:
`typescript
// instrumentation.ts
import { registerOTel } from '@vercel/otel';
export function register() {
registerOTel({
serviceName: 'vercel-frontend-service',
instrumentationConfig: {
fetch: {
propagateContextUrls: ['api.my-aws-backend.com', '*.amazonaws.com'],
},
},
});
}
`
Wenn Sie auf der AWS FastAPI-Seite ebenfalls einen OpenTelemetry-Empfänger einrichten, werden Frontend und Backend unter einer einzigen Trace-ID verbunden.
`python
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4318/v1/traces"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
app = FastAPI(title="Python ML Service")
FastAPIInstrumentor.instrument_app(app)
@app.get("/api/v1/ml-models/predict")
async def predict():
tracer = trace.get_tracer(name)
with tracer.start_as_current_span("ml_inference_execution"):
return {"status": "completed", "prediction": [0.95, 0.05]}
`
Kostenunfälle passieren gewöhnlich an zwei Stellen: Bei Vercels Zusatzgebühren für Fast Data Transfer (über 1 TB kostet jedes weitere GB $0,15) und bei AWS Data Transfer Out (GB für $0,09). Wenn sich dann noch der Traffic häuft und Serverless Functions sowie Lambda gleichzeitig hochskalieren, erreichen die Rechnungen völlig neue Dimensionen.
Hier ist ein Beispiel für eine API-Route, die den Vercel Spend Limit Webhook empfängt und eine Benachrichtigung an Slack sendet. Aus Sicherheitsgründen sollte eine HMAC SHA1-Signaturprüfung durchgeführt werden:
`typescript
import crypto from 'crypto';
export async function POST(req: Request) {
const payload = await req.text();
const signature = req.headers.get('x-vercel-signature');
const expectedSignature = crypto
.createHmac('sha1', process.env.VERCEL_SPEND_WEBHOOK_SECRET!)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return new Response('Invalid Signature', { status: 401 });
}
const event = JSON.parse(payload);
await fetch(process.env.SLACK_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: Vercel Ausgaben-Warnung: Budgetlimit (${event.payload.spendAmount} USD) erreicht.,
}),
});
return new Response('OK', { status: 200 });
}
`
Aktivieren Sie unbedingt diese zwei Sicherheitsnetze: