תיעוד 114
פריסת שער יישומי Claude ב-AWS
דוגמה מעשית להרצת שער יישומי Claude ב-AWS: ECS Fargate או EKS, Amazon RDS for PostgreSQL, AWS Secrets Manager, ואימות בתפקיד IAM מול Amazon Bedrock.
[!NOTE]
העמוד הזה עובר על דרך אחת להריץ את שער יישומי Claude ב-AWS. התצורה היא דוגמה עובדת לתשתית שמנוהלת על ידי הלקוח, ולא פריסת ייצור נתמכת. השתמשו בה כדי לראות איך החלקים משתלבים לפני שתתאימו אותה לסביבה שלכם. לדרישות שאינן תלויות בפלטפורמה, ראו את מדריך הפריסה.
הדוגמה הזו מקימה את שער יישומי Claude ב-AWS עם Amazon Bedrock כ-upstream של המודל, באמצעות Amazon ECS על AWS Fargate או Amazon EKS לחישוב. Okta הוא ספק הזהות (IdP) לדוגמה, אבל כל ספק זהות שתואם OpenID Connect (OIDC) עובד. לפרטים לפי ספק זהות, ראו הגדרת ספק זהות.
[!NOTE]
Bedrock אינו ה-upstream היחיד של Claude ב-AWS. השער תומך גם ב-Claude Platform on AWS, ה-API של Claude שמופעל על ידי Anthropic עם אימות AWS וחיוב ב-AWS Marketplace, במקום Bedrock או לצידו. רשומת ה-upstream, האישורים והרשאות ה-IAM שלו שונים מאלה שבעמוד הזה ותחומים ל-Bedrock. ההפניה ל-upstream של Claude Platform on AWS מכסה את מה שמשתנה, ושאר העמוד הזה חל בלי שינוי.
#ארכיטקטורה
ארכיטקטורת הדוגמה, עם Amazon Bedrock כ-upstream של המודל. upstream של Claude Platform on AWS תופס את אותו מקום.
השער רץ כנקודת קצה פרטית ב-HTTPS ברשת שלכם, ומפתחים מתחברים אליה דרך ספק הזהות שלכם. סשני Claude Code שלהם מגיעים למודלי Claude ב-Amazon Bedrock דרך תפקיד ה-IAM של השער, כך שאישורי מודל לא מגיעים למחשבי המפתחים. תצורת הייחוס מקימה:
- שירות Amazon ECS על AWS Fargate או Deployment של Amazon EKS שמריץ את מכולת השער
- מאגר Amazon ECR לתמונת השער
- מופע Amazon RDS for PostgreSQL בתת-רשתות פרטיות, לא נגיש לציבור, עבור ה-store של השער
- סודות ב-AWS Secrets Manager עבור מפתח החתימה של ה-JWT, סוד הלקוח של OIDC, וכתובת ה-Postgres
- תפקיד IAM עם
bedrock:InvokeModel,bedrock:InvokeModelWithResponseStreamו-bedrock:CountTokens, מצורף כתפקיד המשימה של ECS או מקושר דרך IAM Roles for Service Accounts (IRSA) ב-EKS - Application Load Balancer פנימי עבור HTTPS
#דרישות מוקדמות
ההדרכה יוצרת את המשאבים של השער עצמו, אבל היא נשענת על תשתית רשת וזהות שכבר יש לכם. לפני שמתחילים, צריך:
- חשבון AWS עם הרשאה ליצור את המשאבים שלמעלה
- AWS CLI v2 מותקן ומאומת, ו-Docker מותקן מקומית
- VPC עם לפחות שתי תת-רשתות פרטיות באזורי זמינות שונים, עם גישת אינטרנט יוצאת דרך NAT gateway. מאזן העומסים הפנימי צריך תת-רשתות בשני אזורי זמינות, והשער צריך יציאה אל Bedrock ואל ספק הזהות
- יישום אינטרנט OIDC של Okta עם כתובת הפניה
https://<gateway-host>/oauth/callback. ראו הגדרת ספק זהות - שם מארח TLS עבור השער, בדרך כלל שם DNS פנימי ב-אזור מתארח פרטי של Route 53 שמצביע אל מאזן העומסים, עם אישור ACM עבור השם הזה, מיובא או מונפק על ידי AWS Private CA
#הגדרת משתני הסביבה
כל פקודה בעמוד הזה קוראת ארבעה ערכים מהמעטפת: AWS_REGION, ACCOUNT_ID, VPC_ID ו-PRIVATE_SUBNETS.
בחרו אזור בארצות הברית שבו Bedrock מגיש את מודלי Claude שאתם צריכים. ההדרכה נשענת על קטלוג המודלים המובנה של השער, שמתורגם לפרופילי הסקה us.anthropic.*, ומדיניות ה-IAM מעניקה את ה-ARN האלה. באזור שאינו בארצות הברית, הוסיפו בלוק models: עם מזהי פרופילי ההסקה של אותו אזור גאוגרפי, ושנו את קידומת ה-ARN במדיניות ה-IAM כך שתתאים.
אם אין לכם את מזהה ה-VPC בהישג יד, הציגו את ה-VPC שלכם עם aws ec2 describe-vpcs, ואז הציגו את תת-הרשתות של אותו VPC כדי למצוא שתיים פרטיות באזורי זמינות שונים:
aws ec2 describe-subnets --filters "Name=vpc-id,Values=<your-vpc-id>" \
--query 'Subnets[].{ID:SubnetId,AZ:AvailabilityZone,CIDR:CidrBlock}' --output tableייצאו את כל הארבעה לפני שממשיכים:
export AWS_REGION=us-east-1 # a US region where Bedrock serves the Claude models you need
export ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export VPC_ID=<your-vpc-id>
export PRIVATE_SUBNETS="<subnet-id-a> <subnet-id-b>"#פריסת השער
השלבים למטה מקימים את הפריסה המלאה באמצעות פקודות aws.
- יצירת קבוצות האבטחה
שלוש קבוצות אבטחה משרשרות את נתיב התעבורה: הרשת הארגונית מגיעה אל מאזן העומסים בפורט 443, מאזן העומסים מגיע אל השער בפורט 8080, והשער מגיע אל Postgres בפורט 5432. שום דבר אחר אינו נגיש. אופן הצירוף תלוי במסלול המחשוב:
- ב-ECS Fargate, שלב הפריסה מצרף את
$ALB_SGאל מאזן העומסים ואת$GW_SGאל השירות. - ב-EKS, AWS Load Balancer Controller יוצר קבוצת אבטחה משלו לחזית של ה-ALB, ולכן
$ALB_SGו-$GW_SGלא בשימוש: הערתinbound-cidrsבשלב הפריסה מגבילה את המאזין לרשת הארגונית, וקבוצת האבטחה של מסד הנתונים מתירה את קבוצת האבטחה של האשכול במקומן.
ALB_SG="$(aws ec2 create-security-group --group-name claude-gateway-alb \
--description "Claude gateway ALB" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
GW_SG="$(aws ec2 create-security-group --group-name claude-gateway-svc \
--description "Claude gateway service" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
DB_SG="$(aws ec2 create-security-group --group-name claude-gateway-db \
--description "Claude gateway Postgres" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
aws ec2 authorize-security-group-ingress --group-id "$ALB_SG" \
--protocol tcp --port 443 --cidr <your-corporate-cidr>
aws ec2 authorize-security-group-ingress --group-id "$GW_SG" \
--protocol tcp --port 8080 --source-group "$ALB_SG"
aws ec2 authorize-security-group-ingress --group-id "$DB_SG" \
--protocol tcp --port 5432 --source-group "$GW_SG"- יצירת תפקידי ה-IAM ושליחת טופס תרחיש השימוש
השער רץ עם תפקיד משימה ייעודי, שההרשאה היחידה שלו היא הפעלת מודלי Claude ב-Bedrock. לפי ההפניה ל-upstream של Bedrock, המדיניות חייבת לכסות גם את ה-ARN של פרופילי ההסקה בין האזורים וגם את ה-ARN של מודלי היסוד שמתחתיהם:
cat > bedrock-invoke.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream", "bedrock:CountTokens"],
"Resource": [
"arn:aws:bedrock:${AWS_REGION}:${ACCOUNT_ID}:inference-profile/us.anthropic.*",
"arn:aws:bedrock:*::foundation-model/anthropic.*"
]
}]
}
EOF
cat > ecs-trust.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role --role-name claude-gateway-task \
--assume-role-policy-document file://ecs-trust.json
aws iam put-role-policy --role-name claude-gateway-task \
--policy-name bedrock-invoke --policy-document file://bedrock-invoke.jsonECS צריך גם תפקיד ביצוע, שסוכן ECS עצמו משתמש בו כדי למשוך את התמונה מ-ECR ולהזריק את ערכי Secrets Manager שנוצרים בהמשך. הוא נפרד מתפקיד המשימה ש-AWS SDK של השער משתמש בו בזמן ריצה:
aws iam create-role --role-name claude-gateway-execution \
--assume-role-policy-document file://ecs-trust.json
aws iam attach-role-policy --role-name claude-gateway-execution \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
cat > secrets-read.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": [
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-jwt-secret-??????",
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-oidc-client-secret-??????",
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-postgres-url-??????"
]
}]
}
EOF
aws iam put-role-policy --role-name claude-gateway-execution \
--policy-name read-gateway-secrets --policy-document file://secrets-read.jsonהמדיניות מציינת ARN אחד לכל סוד, ולא תו כללי חשוף gateway-*, שבחשבון משותף היה תואם גם סודות לא קשורים. הסיומת -?????? תואמת בדיוק את הסיומת האקראית בת ששת התווים ש-Secrets Manager מוסיף ל-ARN של כל סוד. סיומת -* הייתה glob של קידומת רגילה, והייתה תואמת גם שמות ארוכים יותר כמו gateway-postgres-url-prod.
מדיניות ה-IAM מעניקה לשער הרשאה לקרוא ל-Bedrock, ו-Bedrock מפעיל גישה למודלים כברירת מחדל באזורים מסחריים. השער שנותר ברמת החשבון הוא טופס תרחיש השימוש החד-פעמי של Anthropic: אם אף אחד בחשבון לא שלח אותו, פתחו את קונסולת Amazon Bedrock, בחרו מודל של Anthropic מקטלוג המודלים, והשלימו את הטופס. הגישה ניתנת מיד אחרי השליחה. ראו Claude Code על Amazon Bedrock עבור טופס AWS Organizations והרשאות ה-IAM שהשולח צריך.
מסלול EKS משתמש מחדש בשני מסמכי המדיניות על תפקיד IRSA, במקום שני תפקידי ECS. ראו את שלב הפריסה.
- הקמת Amazon RDS for PostgreSQL
המופע מריץ Postgres 16 בתת-הרשתות הפרטיות, בלי כתובת ציבורית ועם הצפנת אחסון מופעלת.
קודם יוצרים את קבוצת תת-הרשתות שממקמת את מסד הנתונים בתת-הרשתות הפרטיות, וקבוצת פרמטרים עם rds.force_ssl=1 כדי שהשרת ידחה חיבורים בטקסט גלוי. גרסת המנוע קבועה פעם אחת, כי משפחת קבוצת הפרמטרים חייבת להתאים לגרסה הראשית של המנוע שהמופע מריץ:
aws rds create-db-subnet-group --db-subnet-group-name claude-gateway-db \
--db-subnet-group-description "Claude gateway" --subnet-ids $PRIVATE_SUBNETS
PG_VERSION=16
PG_FAMILY="postgres${PG_VERSION}"
aws rds create-db-parameter-group --db-parameter-group-name claude-gateway-db \
--db-parameter-group-family "$PG_FAMILY" \
--description "Claude gateway - require TLS on every connection"
aws rds modify-db-parameter-group --db-parameter-group-name claude-gateway-db \
--parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=immediate"אחר כך יוצרים את המופע עם סיסמת master שנוצרה:
PGPASS="$(openssl rand -hex 24)"
aws rds create-db-instance --db-instance-identifier claude-gateway-db \
--engine postgres --engine-version "$PG_VERSION" \
--db-instance-class db.t4g.micro \
--allocated-storage 20 --db-name claude_gateway \
--master-username gateway --master-user-password "$PGPASS" \
--db-subnet-group-name claude-gateway-db \
--db-parameter-group-name claude-gateway-db \
--vpc-security-group-ids "$DB_SG" \
--no-publicly-accessible --storage-encryptedהארגומנט המילולי --master-user-password גלוי בטבלת התהליכים וביומני ביקורת ו-EDR בזמן שהפקודה רצה, אותה חשיפה שההערה בשלב הסודות מכסה. במארח משותף או מנוטר, העבירו את הסיסמה דרך --cli-input-json מקובץ 0600, כמו ש-setup.sh של החבילה עושה.
המתינו עד שהמופע יעלה, וזה יכול לקחת כמה דקות, ואז קראו את נקודת הקצה הפרטית שלו והרכיבו את מחרוזת החיבור שהשער ישתמש בה:
aws rds wait db-instance-available --db-instance-identifier claude-gateway-db
DB_HOST="$(aws rds describe-db-instances --db-instance-identifier claude-gateway-db \
--query 'DBInstances[0].Endpoint.Address' --output text)"
GATEWAY_POSTGRES_URL="postgres://gateway:${PGPASS}@${DB_HOST}:5432/claude_gateway?sslmode=verify-full"sslmode=verify-full גורם לשער לאמת את שרשרת האישור ואת שם המארח של אישור השרת של RDS, ולא רק להצפין. עוגן האמון הוא חבילת האישורים של AWS RDS, ושלב בניית התמונה למטה מעתיק אותה אל /etc/claude/rds-global-bundle.pem ונותן בה אמון דרך NODE_EXTRA_CA_CERTS. אל תוסיפו לכתובת פרמטר sslrootcert= בסגנון libpq: מנהל ההתקן של השער קורא רק את sslmode ממחרוזת השאילתה, והיה מעביר את sslrootcert אל Postgres כפרמטר אתחול, שהשרת דוחה.
שירות ECS או ה-pods של EKS חייבים לרוץ ב-VPC הזה כדי שיוכלו להגיע אל נקודת הקצה הפרטית של המופע, וקבוצת האבטחה claude-gateway-db מתירה רק את קבוצת האבטחה של השער.
- כתיבת gateway.yaml
בלוק upstreams מצביע אל Bedrock עם auth: {}, כך שהשער מאמת דרך שרשרת האישורים המוגדרת כברירת מחדל של AWS, מתפקיד המשימה ב-ECS או מתפקיד IRSA ב-EKS. ראו את הפניית התצורה עבור כל שדה.
שני שדות listen מתארים מה עומד לפני השער:
public_url: מקור ה-https://החיצוני, נדרש לכל bind שאינו loopback. ראו את ההפניה ל-listen. השער בונה אתredirect_uriשל ספק הזהות ואת מסמך הגילוי שלו רק מהערך הזה, אף פעם לא מכותרותX-Forwarded-*.trusted_proxies: טווחי המקור של החזית. השער מכבד אתX-Forwarded-Forרק כשעמית ה-TCP נמצא ברשימה הזו, ואז עובר בשרשרת מעבר לקפיצות המהימנות, כך שמגבלות קצב ההתחברות לפי IP ואירועי הביקורת רושמים את כתובות ה-IP של המפתחים ולא את אלה של מאזן העומסים.
בשני המסלולים החזית היא ALB פנימי, בין שנוצר ישירות ובין שנוצר על ידי AWS Load Balancer Controller, והצמתים של ALB מקבלים כתובות מתת-הרשתות שהוא מחובר אליהן, ולכן הגדירו את trusted_proxies ל-CIDR של תת-הרשתות האלה. זה נותן אמון בכל מארח בתת-הרשתות האלה כפרוקסי. שמרו על כך שמקור הכניסה של ה-ALB, ה-CIDR הארגוני, לא יחפוף להן, ואל תשתפו את תת-הרשתות עם עומסי עבודה לא מהימנים שיכולים לזייף כתובות IP של לקוחות דרך X-Forwarded-For.
מאפיין שימור פורט הלקוח של ה-ALB, routing.http.xff_client_port.enabled, יכול להישאר בכל אחת משתי ההגדרות: כשהוא מופעל, ה-ALB כותב את הלקוח כ-203.0.113.7:54321 או [2001:db8::1]:54321, והשער קורא את שניהם כשהפורט מושמט.
listen:
host: 0.0.0.0
port: 8080
public_url: https://claude-gateway.internal.example.com
trusted_proxies: [<your-alb-subnet-cidrs>]
oidc:
issuer: https://example.okta.com
client_id: 0oa1example2
client_secret: ${OIDC_CLIENT_SECRET} # EKS: ${file:/secrets/oidc-client-secret}
allowed_email_domains: [example.com]
# The Okta org authorization server returns a thin id_token that omits
# email and groups; the gateway fills them from /userinfo.
userinfo_fallback: true
# Okta emits groups only when the `groups` scope is requested and the
# app's groups claim filter allows them.
scopes: [openid, profile, email, offline_access, groups]
session:
jwt_secret: ${GATEWAY_JWT_SECRET} # EKS: ${file:/secrets/jwt-secret}
ttl_hours: 8 # bounds deprovision latency; lower
# toward 1 for tighter revocation
store:
postgres_url: ${GATEWAY_POSTGRES_URL} # EKS: ${file:/secrets/postgres-url}
# readiness_grace_seconds: 300 # keep passing the health check
# through an RDS failover
upstreams:
- provider: bedrock
region: <your-region> # match $AWS_REGION so the IAM
# policy's ARNs cover it
auth: {} # AWS default credential chain:
# ECS task role, or IRSA on EKS[!NOTE]
רק בלוק
oidcייחודי ל-Okta. כדי להשתמש ב-Microsoft Entra ID, הגדר אתissuerל-https://login.microsoftonline.com/<tenant-id>/v2.0, השמט אתuserinfo_fallbackואת ה-scopegroups, ושים לב ש-Entra פולט מזהי Object של קבוצות ולא שמות, ולכןmanaged.policiesחייב להתאים לפי ה-GUID, או לפי App Roles עםoidc.groups_claim: roles. ראה הגדרת ספק זהות.
5. אחסון סודות ב-AWS Secrets Manager
צור שלושה סודות. תפקיד הביצוע משלב ה-IAM כבר יכול לקרוא אותם:
aws secretsmanager create-secret --name gateway-jwt-secret \
--secret-string "$(openssl rand -base64 32)"
aws secretsmanager create-secret --name gateway-oidc-client-secret \
--secret-string '<your-okta-client-secret>'
aws secretsmanager create-secret --name gateway-postgres-url \
--secret-string "$GATEWAY_POSTGRES_URL"רשום את ה-ARN שכל קריאה מדפיסה. הגדרת משימת ECS מפנה לסודות לפי ARN.
[!NOTE]
ארגומנטים מילוליים של
--secret-stringגלויים בטבלת התהליכים וביומני ביקורת/EDR בזמן שכל פקודה רצה. במארח משותף או מנוטר, שים את הערך בקובץ0600והעבר--secret-string file://<path>.setup.shשל החבילה שומר את ערכי הסודות מחוץ ל-argv של התהליך באותו אופן, ומעביר קבצים זמניים0600אל--cli-input-json.
בניגוד לסודות, gateway.yaml עצמו אינו מכיל ערכי סודות, כי כל אישור נפתר בעת האתחול דרך התרחבות ${VAR} או ${file:...}. הדרך שבה הכל מגיע לקונטיינר שונה לפי מסלול:
- ב-ECS, הבנייה בשלב הבא מעתיקה את
gateway.yamlלתוך האימג' בנתיב/etc/claude/gateway.yaml, והגדרת המשימה מזריקה את שלושת הסודות כמשתני סביבה דרך שדהsecretsשלה, כך שה-YAML מפנה אל${GATEWAY_JWT_SECRET},${OIDC_CLIENT_SECRET}ו-${GATEWAY_POSTGRES_URL}. - ב-EKS, עגן את
gateway.yamlמ-ConfigMap ואת הסודות כקבצים ב-/secrets, עם הפניה${file:/secrets/...}. קח את Kubernetes Secrets מ-Secrets Manager באמצעות External Secrets Operator או ספק AWS של Secrets Store CSI driver, או צור אותם ישירות עםkubectl.
6. בניית האימג' ודחיפתו ל-Amazon ECR
בנה את האימג' לפי דרישות אימג' הקונטיינר, והנח את הבינארי linux-x64 של glibc ב-./claude בהקשר הבנייה. כתוב Dockerfile משלך לפי הדרישות האלה או התחל מ-Dockerfile של החבילה, שמעתיק את gateway.yaml הממולא מהשלבים הקודמים לתוך האימג' בנתיב /etc/claude/gateway.yaml. ב-ECS העותק המוטמע הזה הוא הדרך שבה התצורה מגיעה לקונטיינר, ולכן הבנייה באה אחרי שהקובץ נכתב. מסלול EKS מעגן את gateway.yaml מ-ConfigMap בזמן הפריסה, כך שהעותק המוטמע אינו בשימוש שם.
האימג' נושא גם את חבילת אישורי AWS RDS כעוגן האמון של sslmode=verify-full במחרוזת החיבור, לכן הורד אותה תחילה אל הקשר הבנייה. AWS מחליפה את החבילה (רשויות אישורים אזוריות חדשות מצורפות), לכן הורד אותה בכל בנייה, ואל תנעץ checksum ואל תבצע לה commit:
curl -fL --proto '=https' -o rds-global-bundle.pem \
https://truststore.pki.rds.amazonaws.com/global/global-bundle.pemדרישות אימג' הקונטיינר אינן מכסות את החבילה, לכן אם אתה כותב Dockerfile משלך, הוסף את שתי השורות שמעתיקות אותה ונותנות בה אמון. ה-Dockerfile של החבילה כבר כולל את שתיהן:
COPY rds-global-bundle.pem /etc/claude/rds-global-bundle.pem
ENV NODE_EXTRA_CA_CERTS=/etc/claude/rds-global-bundle.pemצור את מאגר ECR והתחבר אליו עם Docker. תגיות בלתי ניתנות לשינוי אומרות שתגית <version> ששלב הפריסה נועל לא יכולה מאוחר יותר להיות מופנית בשקט אל אימג' אחר:
aws ecr create-repository --repository-name claude-gateway \
--image-tag-mutability IMMUTABLE \
--image-scanning-configuration scanOnPush=true
aws ecr get-login-password --region "$AWS_REGION" \
| docker login --username AWS --password-stdin \
"${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"בנה ודחוף את האימג'. הגדרת המשימה למטה רצה linux/amd64, ולכן הפלטפורמה חייבת להתאים כאן. עבור Fargate על ARM64 (Graviton), בנה linux/arm64 עם הבינארי linux-arm64 והגדר את cpuArchitecture ל-ARM64:
docker build --platform=linux/amd64 \
-t "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/claude-gateway:<version>" .
docker push "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/claude-gateway:<version>"7. פריסה
ECS Fargate
צור את האשכול וקבוצת לוג עבור stderr של הגייטווי, שנושא גם את אירועי הביקורת וגם את הלוגים התפעוליים. השמירה היא קריאה נפרדת, ובלי קריאה כזו CloudWatch שומר את הלוגים לנצח. יישר את 90 הימים עם מדיניות שמירת הביקורת שלך:
aws ecs create-cluster --cluster-name claude-gateway
aws logs create-log-group --log-group-name /ecs/claude-gateway
aws logs put-retention-policy --log-group-name /ecs/claude-gateway \
--retention-in-days 90כתוב את הגדרת המשימה. תפקיד המשימה נושא את הרשאת Bedrock ותפקיד הביצוע מזריק את הסודות. השתמש ב-ARN של הסודות משלב Secrets Manager:
{
"family": "claude-gateway",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048",
"runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" },
"executionRoleArn": "arn:aws:iam::<account-id>:role/claude-gateway-execution",
"taskRoleArn": "arn:aws:iam::<account-id>:role/claude-gateway-task",
"containerDefinitions": [
{
"name": "gateway",
"image": "<account-id>.dkr.ecr.<region>.amazonaws.com/claude-gateway:<version>",
"portMappings": [{ "containerPort": 8080 }],
"secrets": [
{ "name": "GATEWAY_JWT_SECRET", "valueFrom": "<gateway-jwt-secret ARN>" },
{ "name": "OIDC_CLIENT_SECRET", "valueFrom": "<gateway-oidc-client-secret ARN>" },
{ "name": "GATEWAY_POSTGRES_URL", "valueFrom": "<gateway-postgres-url ARN>" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/claude-gateway",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "gateway"
}
}
}
]
}רשום אותה:
aws ecs register-task-definition --cli-input-json file://claude-gateway-task.jsonהצב ALB פנימי מלפנים עם קבוצת יעד שבודקת את תקינות הגייטווי. --ip-address-type ipv4 חשוב: ALB פנימי dual-stack מפרסם רשומות AAAA בטווח ציבורי, שבדיקת הרשת הפרטית של /login דוחה:
ALB_ARN="$(aws elbv2 create-load-balancer --name claude-gateway \
--scheme internal --type application --ip-address-type ipv4 \
--subnets $PRIVATE_SUBNETS --security-groups "$ALB_SG" \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)"
TG_ARN="$(aws elbv2 create-target-group --name claude-gateway \
--protocol HTTP --port 8080 --vpc-id "$VPC_ID" --target-type ip \
--health-check-path /readyz \
--query 'TargetGroups[0].TargetGroupArn' --output text)"הוסף את מאזין HTTPS. --ssl-policy נועל רצפת TLS מודרנית, כי השמטתו חוזרת לברירת המחדל הישנה ELBSecurityPolicy-2016-08, שעדיין מקבלת TLS 1.0/1.1.
ה-ALB סוגר חיבור אחרי 60 שניות בלי נתונים כברירת מחדל. פינגי ה-keepalive של הגייטווי שומרים את הזרמים בתוך ברירת המחדל הזו, ולכן העלאת ה-timeout מוסיפה מרווח מעל קצב הפינג. שורת פתרון בעיות על זרמים שנפלו מכסה את המנגנון וגייטווי ישנים יותר. הפקודות למטה מוסיפות את המאזין ומעלות את ה-timeout:
aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" \
--protocol HTTPS --port 443 \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \
--certificates CertificateArn=<your-acm-certificate-arn> \
--default-actions Type=forward,TargetGroupArn="$TG_ARN"
aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$ALB_ARN" \
--attributes Key=idle_timeout.timeout_seconds,Value=3600צור את השירות. מפסק המעגל של הפריסה מחזיר לאחור פריסה שהמשימות שלה ממשיכות להיכשל, מאימג' פגום או מתצורה שלא ניתנת לאתחול, אל המצב היציב האחרון, ולא מפעיל מחדש משימות כושלות לנצח:
aws ecs create-service --cluster claude-gateway --service-name claude-gateway \
--task-definition claude-gateway --desired-count 1 --launch-type FARGATE \
--deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true}" \
--health-check-grace-period-seconds 60 \
--network-configuration "awsvpcConfiguration={subnets=[$(echo $PRIVATE_SUBNETS | tr ' ' ',')],securityGroups=[$GW_SG],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=$TG_ARN,containerName=gateway,containerPort=8080"תקופת החסד של 60 שניות נותנת למשימה קרה זמן למשוך את האימג', להתחבר למאגר, ולענות על בדיקת התקינות הראשונה שלה לפני ש-ECS מתחיל לספור כשלונות מול הפריסה.
בדיקת התקינות של קבוצת היעד על GET /readyz מוודאת שהמאגר נגיש, כך שמשימה שלא יכולה להגיע ל-Postgres לא נכנסת לרוטציה. כדי שמשימות ימשיכו לעבור את הבדיקה במהלך השבתת מסד נתונים קצרה כמו failover של RDS, הגדר את store.readiness_grace_seconds כפי שמתואר ב-התנהגות בהשבתה, שמכסה גם את החלופה /healthz.
המשימות רצות בתת-רשתות פרטיות בלי IP ציבורי, ולכן כל היציאה (אל Bedrock, ספק הזהות שלך, Secrets Manager, ECR ו-CloudWatch Logs) עוברת דרך שער NAT. כדי להשאיר את תעבורת Bedrock מחוץ למסלול הציבורי, צור נקודת קצה VPC מסוג interface של bedrock-runtime והפנה את base_url של ה-upstream אליה, כפי שמוצג ב-הפניה ל-upstream של Bedrock. ספק הזהות עדיין זקוק ליציאה לאינטרנט.
סיים במתן שם מארח שניתן לפתרון פרטי למפתחים: באזור מתארח פרטי של Route 53, צור alias משם ה-DNS הפנימי של הגייטווי אל ה-ALB, והגדר את listen.public_url לשם המארח הזה. השם *.elb.amazonaws.com של ה-ALB עצמו נפתר לכתובות פרטיות ב-ALB פנימי, אבל הוא לא יכול לשאת את אישור ACM שלך, לכן השתמש בשם משלך.
עדכן את URI ההפניה המורשה של לקוח OAuth ל-<public_url>/oauth/callback לפני הכניסה הראשונה. אחרי שינוי public_url, בנה מחדש ודחוף את האימג' תחת תגית חדשה, רשום גרסה חדשה של הגדרת המשימה, ופרוס מחדש. ב-ECS ההגדרה נמצאת ב-gateway.yaml המוטמע באימג', והגייטווי בונה את המקור הציבורי שלו רק מההגדרה הזו, ומתעלם מ-X-Forwarded-Host ומ-X-Forwarded-Proto. X-Forwarded-For מכובד עבור כתובות IP של לקוחות רק כאשר listen.trusted_proxies מוגדר.
EKS
המסלול הזה דורש kubectl ו-eksctl מותקנים מקומית, ואשכול EKS קיים עם ספק IAM OIDC ועם AWS Load Balancer Controller מותקן. האשכול חייב להיות על $VPC_ID כדי שפודים יוכלו להגיע לנקודת הקצה הפרטית של RDS, וקבוצת האבטחה claude-gateway-db חייבת לאפשר את קבוצת האבטחה של הפוד או הצומת של האשכול במקום $GW_SG.
ב-EKS הגייטווי מקבל את אישורי Bedrock שלו דרך IRSA ולא דרך תפקידי ECS. מדיניות האמון ecs-tasks.amazonaws.com משלב ה-IAM לא חלה כאן. IRSA צריך תפקיד שמדיניות האמון שלו עושה federation על ספק OIDC של האשכול, בהיקף system:serviceaccount:claude-gateway:gateway. eksctl create iamserviceaccount יוצר את התפקיד הזה, מצרף את המדיניויות, ומוסיף annotation לחשבון השירות של Kubernetes עם ARN התפקיד בשלב אחד. הפוך את שני מסמכי המדיניות משלב ה-IAM למדיניויות מנוהלות שאפשר לצרף:
BEDROCK_POLICY_ARN="$(aws iam create-policy --policy-name claude-gateway-bedrock-invoke \
--policy-document file://bedrock-invoke.json --query Policy.Arn --output text)"
SECRETS_POLICY_ARN="$(aws iam create-policy --policy-name claude-gateway-secrets-read \
--policy-document file://secrets-read.json --query Policy.Arn --output text)"
kubectl create namespace claude-gateway
eksctl create iamserviceaccount --cluster <your-cluster> --region "$AWS_REGION" \
--namespace claude-gateway --name gateway --role-name claude-gateway \
--attach-policy-arn "$BEDROCK_POLICY_ARN" \
--attach-policy-arn "$SECRETS_POLICY_ARN" \
--approveמדיניות הסודות נדרשת רק כאשר הפודים קוראים את Secrets Manager בעצמם, כפי שספק AWS של Secrets Store CSI driver עושה באמצעות חשבון השירות של הפוד שעושה mount. השמט אותה אם אתה יוצר את Kubernetes Secrets בדרך אחרת. הספק צריך את שתי הפעולות של המדיניות: הוא קורא ל-DescribeSecret כשהוא מיישב סודות שהוחלפו, ולכן הרשאה של GetSecretValue בלבד עושה mount בפריסה הראשונה אבל מפסיקה לקלוט החלפות.
פרוס את הגייטווי כ-Deployment רגיל יחד עם Service ו-Ingress, כפי שמתואר ב-פריסת Kubernetes, עם:
serviceAccountName: gatewaygateway.yamlמעוגן מ-ConfigMap והסודות מעוגנים ב-/secrets- בדיקת המוכנות מכוונת אל
GET /readyz
עבור החזית, Ingress שמנוהל על ידי AWS Load Balancer Controller מקצה את ה-ALB הפנימי. הוסף לו annotations:
alb.ingress.kubernetes.io/scheme: internalו-alb.ingress.kubernetes.io/target-type: ipalb.ingress.kubernetes.io/ip-address-type: ipv4, כדי שלא יפורסמו רשומות AAAA בטווח ציבורי שבדיקת הרשת הפרטית של/loginתדחהalb.ingress.kubernetes.io/inbound-cidrs: <your-corporate-cidr>, כדי שקבוצת האבטחה של החזית שמנוהלת על ידי ה-controller תאפשר רק את הרשת הארגונית שלך במקום ברירת המחדל0.0.0.0/0alb.ingress.kubernetes.io/certificate-arnעם אישור ACMalb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06, כדי שהמאזין לא יחזור למדיניות ברירת המחדל הישנה שמקבלת TLS 1.0 ו-1.1alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3600, מרווח מעל keepalive של הסטרימינג של הגייטווי. ראה פתרון בעיות
עם IRSA, AWS SDK קורא טוקן חשבון שירות מוקרן ומחליף אותו מול AWS STS, ולכן הפוד לא זקוק לשירות המטא-דאטה של מופע EC2. NetworkPolicy של egress יכול לחסום את 169.254.169.254 עבור פודים של הגייטווי. בעיית hop-limit של הצומת ב-פתרון בעיות למטה חלה רק על אשכולות שמדלגים על IRSA ונשענים על תפקידי מופע של צומת.
8. דחיפת כתובת הגייטווי למכונות המפתחים
הגייטווי רץ עכשיו, אבל מפתחים לא יכולים להגיע אליו מ-/login עד שכתובת הגייטווי נמצאת על המכונות שלהם. פרוס את קטע ההגדרות המנוהלות המלא, עם forceLoginMethod, forceLoginGatewayUrl, וההצטרפות parentSettingsBehavior: "merge", לכל מכשיר דרך MDM. אין אפשרות גייטווי בבורר ההתחברות שמפתח יכול לבחור ידנית.
#הפניה ל-Terraform
חבילת הליווי ב-examples/gateway/aws אורזת את הדף הזה כקוד:
setup.shמסריט את הליך ההקצאה שלמעלה עם אותן פקודותaws, במסלול ECS Fargate. הוא אידמפוטנטי: משאבים קיימים מזוהים ומדולגים, כך שהרצה חוזרת בטוחה, וכל ברירת מחדל ניתנת לעקיפה באמצעות משתנה סביבה. אתם עדיין יוצרים בעצמכם את סוד לקוח ה-OIDC של Okta ואת אישור ה-ACM: הרצה בלעדיהם מדלגת על פריסת ECS/ALB, מציינת בשם את הקלטים החסרים, ומדפיסה את פקודתcreate-secret. צרו את שניהם והריצו שוב. טופס תרחיש השימוש של Bedrock וכינוי Route 53 מודפסים כצעדים הבאים ולא רצים אוטומטית, ודחיפת ה-MDM ללקוח נשארת צעד ידני מהדף הזה.gateway.yaml.exampleהוא תבנית התצורה משלב gateway.yaml, והמפתחות האופציונליים כלולים בו כמוערים. העתיקו אותו אלgateway.yamlוהחליפו כלREPLACE_MEלפני הבנייה.Dockerfileבונה את תמונת זמן הריצה מהבינארי הבנוי מראשlinux-x64ומעתיק פנימה אתgateway.yamlהממולא שלכם אל/etc/claude/gateway.yaml, ובנוסף את חבילת האישורים של AWS RDS שמעגנת אתsslmode=verify-fullשל ה-store.setup.shמוריד את החבילה רק כשהיא אינה כבר בהקשר הבנייה. מחקו את הקובץ ובנו מחדש תחת תג חדש כדי לקלוט רוטציית CA של AWS. קובץ התצורה אינו מחזיק ערכי סוד, משום שכל אישור נפתר באתחול דרך הרחבת${VAR}. עריכת תצורה פירושה בנייה מחדש תחת תג חדש.setup.shממכן זאת על ידי תיוג תמונות ב-hash של הקובץ.terraform/מקצה באופן הצהרתי את אותו היקף ECS Fargate: קבוצות האבטחה, תפקידי IAM, מאגר ECR, מופע RDS, סודות Secrets Manager, ושירות ECS מאחורי ה-ALB הפנימי. ה-VPC ורשתות המשנה הפרטיות נשארים דרישות קדם, ומועברים פנימה כמשתנים. Terraform יוצר את מאגר ECR אבל אינו בונה את התמונה, והגדרת השירות מפנה לתמונה, ולכן ה-apply הוא שני מעברים: apply ממוקד למאגר, אחר כך הבנייה והדחיפה, ואז ה-apply המלא.terraform/README.mdשל החבילה מכסה את המשתנים, את המצב המרוחק ואת הפירוק.
כמו הדף הזה, החבילה היא דוגמה עובדת לתשתית בניהול הלקוח, במקום פריסת ייצור נתמכת. סקרו והתאימו אותה לסביבה שלכם לפני שאתם מסתמכים עליה.
#פתרון בעיות
לשגיאות אתחול והתחברות של השער, ראו את טבלת פתרון הבעיות שאינה תלויה בפלטפורמה. הרשומות למטה ספציפיות ל-AWS.
| תסמין | סיבה | תיקון |
|---|---|---|
CLI /login: Gateway hosts must be on your organization's private network; <host> resolves to the public (or unrecognized) address <ip> | שם השער מפוענח לכתובת ציבורית אחת לפחות. ALB פנימי מסוג dual-stack מפרסם רשומות AAAA בטווח ציבורי, ובדיקת הרשת הפרטית דורשת שכל כתובת שפוענחה תהיה פרטית | צרו את ה-ALB עם --ip-address-type ipv4, או ספקו שם DNS פנימי בלבד נפרד בלי רשומת AAAA ציבורית |
כל בקשת Bedrock מחזירה 502. היומן מציג Could not load credentials from any providers | המשימה רצה על סוג ההשקה ECS EC2 בלי תפקיד משימה, או שה-pod רץ על צומת EKS בלי IRSA, ולכן האישורים מגיעים ממטא-נתוני המופע, שמגבלת הקפיצות של IMDSv2, שבברירת המחדל היא 1, עוצרת בתוך קונטיינר. אף מסלול בדף הזה אינו מושפע: תפקידי משימה של Fargate ו-IRSA אינם משתמשים במטא-נתוני מופע | העדיפו תפקידי משימה ו-IRSA. במקום שבו אישורי מופע בלתי נמנעים, העלו את מגבלת הקפיצות באמצעות aws ec2 modify-instance-metadata-options --instance-id <id> --http-put-response-hop-limit 2. הטבלה שאינה תלויה בפלטפורמה מכסה את הפשרות |
בקשות Bedrock מחזירות 403 AccessDeniedException | החשבון לא הגיש את טופס תרחיש השימוש החד-פעמי של Anthropic, מנוי AWS Marketplace האוטומטי שמתחיל בהפעלה הראשונה של החשבון עדיין לא הסתיים, או שמדיניות תפקיד המשימה חסרה את ה-ARN של פרופיל ההסקה או של מודל היסוד | הגישו את טופס תרחיש השימוש מקטלוג המודלים של קונסולת Bedrock. אם הוא הוגש זה עתה או שזו ההפעלה הראשונה של החשבון, נסו שוב אחרי כמה דקות. העניקו bedrock:InvokeModel ו-bedrock:InvokeModelWithResponseStream על שתי משפחות ה-ARN. |
Bedrock מחזיר ValidationException שאומר שתפוקת on-demand אינה נתמכת | רשומת models: מותאמת אישית ממפה למזהה מודל יסוד חשוף שהאזור מגיש רק דרך פרופילי הסקה | מפו את המודל למזהה פרופיל ההסקה חוצה האזורים שלו (us.anthropic.*). הקטלוג המובנה כבר עושה זאת |
משימת ECS נעצרת עם ResourceInitializationError לפני שהשער רושם משהו | תפקיד הביצוע אינו יכול לקרוא את סודות Secrets Manager, או שלרשתות המשנה הפרטיות אין נתיב אל Secrets Manager או אל ECR | העניקו secretsmanager:GetSecretValue על ה-ARN של שלושת הסודות gateway- לתפקיד הביצוע, וספקו יציאה דרך שער ה-NAT, או, בלי שער כזה, נקודות קצה מסוג ממשק עבור Secrets Manager, ECR ו-CloudWatch Logs, שמנהל ההתקן awslogs זקוק להן באותו שלב, ובנוסף נקודת קצה מסוג שער של S3 |
| אתחול השער יוצא עם שגיאת timeout של חיבור Postgres | קבוצת האבטחה של מסד הנתונים אינה מתירה את קבוצת האבטחה של השער על 5432, או שהשירות רץ מחוץ ל-VPC של מסד הנתונים | התירו 5432 מקבוצת האבטחה של השער על זו של מסד הנתונים, והריצו את השירות באותו VPC כמו קבוצת רשתות המשנה של ה-DB |
| אתחול השער יוצא עם שגיאת אימות אישור TLS של Postgres | מחרוזת החיבור מגדירה sslmode=verify-full אבל התמונה אינה סומכת על חבילת ה-CA של RDS: החבילה לא הועתקה אל תוך התמונה, או ש-NODE_EXTRA_CA_CERTS אינו מצביע אליה | הוסיפו את שתי שורות ה-Dockerfile של שלב הבנייה שמעתיקות את החבילה ומגדירות את NODE_EXTRA_CA_CERTS, ואז בנו מחדש, דחפו תחת תג חדש, ופרסו מחדש |
| תגובות הזרמה נופלות באמצע הזרם אחרי תקופה שקטה | שער ישן מ-v2.1.229 על upstream של Bedrock או של Claude Platform on AWS אינו שולח דבר בזמן שה-upstream שקט, לדוגמה במהלך חשיבה מורחבת בלי פלט מוזרם. ה-ALB סוגר חיבור אחרי 60 שניות בלי נתונים כברירת מחדל, ולכן הוא חותך את הזרם בפער הזה. שערים בגרסה v2.1.229 ואילך שומרים על זרם שקט מתחת לפסק הזמן הזה: ב-upstreams האלה השער פולט אירוע SSE מסוג ping פעם אחת כשעוברות בערך 15 שניות בלי נתוני זרם, וב-upstream של Anthropic API הוא מעביר את ה-pings של ה-API עצמו | עדכנו את השער ל-v2.1.229 או לגרסה חדשה יותר, או הגדירו את המאפיין idle_timeout.timeout_seconds ל-3600, דרך modify-load-balancer-attributes או הערת Ingress בשם load-balancer-attributes ב-EKS |
#טלמטריה
השער נותן לכם מדדי שימוש לפי מפתח בלי שום תצורת OTEL לפי מכונה. Claude Code פולט מדדים, יומנים ועקבות בהצטרפות של OpenTelemetry (OTLP). ניטור שימוש מכסה את כל מה שה-CLI מדווח. בסשנים שהתחברו דרך /login, ה-CLI חותם כל ייצוא במאפייני הזהות המאומתים של ה-IdP user.id, user.email ו-user.groups, כך שהשימוש מתאגד לפי מפתח.
השער עצמו הוא ממסר OTLP מאומת. הגדירו את telemetry.forward_to יחד עם listen.public_url, והוא דוחף את הגדרות מייצא ה-OTEL לכל לקוח מחובר ומעביר את תעבורת ה-OTLP שלהם כלשונה לכל יעד שאתם מפרטים. כל יעד מצטרף למדדים, ליומנים ולעקבות בנפרד, וברירת המחדל היא מדדים בלבד. ראו את הפניה ל-telemetry לשדות לפי אות ולפשרות הרגישות שלהם. השער אינו אוגר, אינו מאגד ואינו שומר טלמטריה, ולכן המקום שאליו הנתונים מגיעים הוא כולו תצורת המייצא של האספן.
טלמטריית הלקוח כבויה כברירת מחדל. הגדרת telemetry.forward_to היא מה שמפעיל אותה עבור המפתחים המחוברים, וכל לקוח אינטראקטיבי מציג תיבת דו-שיח לאישור אבטחה עבור ההגדרות שנדחפו, כמתואר בהפניה לתצורה. ב-AWS כל אות ממופה ליעד באופן הבא.
#מדדים, יומנים ועקבות של הלקוח
הפנו את telemetry.forward_to אל אספן OpenTelemetry, כגון אספן AWS Distro for OpenTelemetry (ADOT), וייצאו משם אל Amazon CloudWatch, Amazon Managed Service for Prometheus, או כל backend של OTLP.
הריצו את האספן כשירות פנימי נפרד משלו, שנגיש דרך https://. הפניה ל-telemetry מכסה את חריגת ה-loopback ואת CLAUDE_GATEWAY_ALLOW_LOOPBACK.
#יומני השער
ב-ECS Fargate, בלי הגדרה נוספת: מנהל ההתקן awslogs מעביר את stderr של השער, שנושא את אירועי הביקורת ואת יומני התפעול שלו, אל קבוצת היומנים /ecs/claude-gateway שנוצרה למעלה. ב-EKS, יומני pod אינם מגיעים אל CloudWatch כברירת מחדל, ולכן נתיב הביקורת אובד עד שמתקינים איסוף יומנים: התוסף Amazon CloudWatch Observability עם לכידת יומני קונטיינר מופעלת, או DaemonSet של Fluent Bit. בכל אחד מהמסלולים, שאלו את היומנים באמצעות CloudWatch Logs Insights והניעו התראות ממסנני מדדים.
#מדדי קונטיינר
הפעילו את Container Insights על האשכול באמצעות aws ecs update-cluster-settings --cluster claude-gateway --settings name=containerInsights,value=enabled עבור CPU, זיכרון ורשת לפי משימה. ב-EKS, התקינו את התוסף Amazon CloudWatch Observability.
#הוצאה
הטלמטריה מציגה שימוש לאחר מעשה. מגבלות הוצאה הן התצוגה החיה לפי מפתח והאכיפה של השער.
#ייחוס עלות
השער חותם כל בקשת Bedrock עם ה-principal שלו, תפקיד המשימה של ECS או תפקיד IRSA של EKS, ולכן כברירת מחדל AWS רואה את כל ההוצאה הזו תחת principal אחד של IAM. יש שתי דרכים לפצל אותה בנתוני החיוב של AWS עצמה, והן משתלבות.
#לפי מפתח עם assume_role
צרו תפקיד IAM שני שמחזיק את הרשאות Bedrock ונותן אמון ב-principal של השער, העניקו ל-principal הזה sts:AssumeRole עליו, והגדירו assume_role עם session_name: email על ה-upstream של Bedrock. השער אז מבצע assume לתפקיד הזה פעם אחת לכל מפתח לכל שעה, כששם הסשן מוגדר לדוא"ל שלהם, וחותם את הבקשות שלהם באמצעות התוצאה. נדרש שער שמריץ את Claude Code v2.1.281 או גרסה חדשה יותר. התפקיד יכול להיות גם בחשבון AWS אחר: ראו Bedrock בחשבון AWS אחר. ב-Terraform, ליד תפקיד המשימה בחבילת Terraform:
resource "aws_iam_role" "bedrock_user" {
name = "claude-gateway-bedrock-user"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Effect = "Allow", Action = "sts:AssumeRole", Principal = { AWS = aws_iam_role.task.arn } }]
})
}
resource "aws_iam_role_policy" "bedrock_user_invoke" { # same Bedrock policy as the task role's
role = aws_iam_role.bedrock_user.id
policy = aws_iam_role_policy.bedrock_invoke.policy
}
resource "aws_iam_role_policy" "task_assume_bedrock_user" {
role = aws_iam_role.task.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Effect = "Allow", Action = "sts:AssumeRole", Resource = aws_iam_role.bedrock_user.arn }]
})
}כש-assume_role מוגדר, השער חותם כל קריאת Bedrock, כולל קריאת CountTokens החינמית למדידת הוצאה, באישורים של התפקיד שאליו בוצע assume, ולכן ה-principal של השער זקוק למדיניות Bedrock משלו רק עבור upstream בלי assume_role.
מכיוון שהשער קורא ל-STS בזמן הבקשה, רשתות המשנה הפרטיות זקוקות לנתיב אל sts.<region>.amazonaws.com. שער ה-NAT מדרישות הקדם מספק נתיב כזה, וכך גם נקודת קצה VPC מסוג ממשק של STS שעונה עבור שם המארח הזה. כל מפתח פעיל עולה קריאת STS אחת לשעה לכל replica של השער.
הבקשות של כל מפתח מגיעות אל AWS בתור ה-principal arn:aws:sts::<account>:assumed-role/<role>/<email>. כדי לראות הוצאה לפי principal, השתמשו בייצוא חיוב שכולל נתוני principal של IAM. הדף IAM principal cost allocation של AWS מכסה איך להפעיל זאת ואילו כלי חיוב מציגים זאת.
#לפי צוות עם application inference profiles
המסלול הזה משתמש רק במקטעים models ו-managed. צרו application inference profile אחד של Bedrock לכל צוות ולכל מודל, תייגו כל פרופיל בצוות, והפעילו את התג הזה כתג הקצאת עלות. אחר כך תנו לכל צוות מזהה מודל משלו ב-gateway.yaml והצמידו כל קבוצת IdP למזהים של הצוות שלו:
models:
# Include the served model's id, such as claude-opus-4-8, in each id so the gateway's spend meter can price it.
- id: platform-claude-opus-4-8
upstream_model:
bedrock: arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123
- id: data-claude-opus-4-8
upstream_model:
bedrock: arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/def456
managed:
policies:
- match: {groups: [team-platform]}
cli: {availableModels: [platform-claude-opus-4-8], enforceAvailableModels: true}
- match: {groups: [team-data]}
cli: {availableModels: [data-claude-opus-4-8], enforceAvailableModels: true}
- match: {}
cli: {availableModels: [claude-opus-4-8, claude-sonnet-4-6], enforceAvailableModels: true}אמרו למפתחים בצוות מוצמד להפעיל את Claude Code עם --model platform-claude-opus-4-8, באמצעות המזהה של הצוות שלהם, כי סשן שהופעל בלעדיו מריץ את מודל ברירת המחדל, שהשער מסרב לו עבורם.
השער אוכף את availableModels בכל בקשה, לא רק בבורר המודלים, וחיוב AWS מקבץ את ההוצאה לפי התג שהפעלתם. בלי ה-catch-all match: {}, מפתח שאינו תואם אף מדיניות מקבל כל מודל בקטלוג ויכול לחייב את הפרופיל של כל אחד משני הצוותים.
העלויות:
- התצורה גדלה לפי צוותים כפול מודלים
- התפקיד שחותם את בקשות Bedrock של ה-upstream הזה חייב להיות מורשה גם להפעיל את ה-ARN של
application-inference-profile/*. התפקיד הזה הוא ה-principal של השער, או, עםassume_role, התפקיד שהוא מבצע לו assume.
ראו איך בקשות מתומחרות לאופן שבו מד ההוצאה של השער עצמו מתמחר את המזהים האלה.
#תנו למפתח לבחור איזה צוות לחייב
כדי לאפשר למפתח שעובד עבור שני צוותים לבחור איזה צוות כל סשן מחייב, תנו לזוג הצוותים הזה קבוצת IdP ומדיניות משלו.
1. הוסיפו קבוצה עבור הזוג
ב-IdP שלכם, הוסיפו את המפתח לקבוצה חדשה, כגון team-platform-and-data. אם הם כבר מחוברים, בקשו מהם להריץ /login שוב, או המתינו לרענון השקט הבא, כדי שהשער יראה את הקבוצה החדשה.
2. פרטו את המדיניות שלו ראשונה
הציבו את מדיניות הקבוצה מעל המדיניות של צוות בודד, ואפשרו בה את המזהים של שני הצוותים. העתיקו פנימה כל הגדרה אחרת ששתי מדיניות הצוות קובעות.
managed:
policies:
- match: {groups: [team-platform-and-data]}
cli: {availableModels: [platform-claude-opus-4-8, data-claude-opus-4-8], enforceAvailableModels: true}
- match: {groups: [team-platform]}
cli: {availableModels: [platform-claude-opus-4-8], enforceAvailableModels: true}
- match: {groups: [team-data]}
cli: {availableModels: [data-claude-opus-4-8], enforceAvailableModels: true}
- match: {}
cli: {availableModels: [claude-opus-4-8, claude-sonnet-4-6], enforceAvailableModels: true}3. התחילו כל סשן על מזהה של צוות
בקשו מהמפתח להעביר את המזהה של הצוות שהעבודה מיועדת לו, כגון claude --model data-claude-opus-4-8.
כברירת מחדל, השער מחיל את המדיניות הראשונה שתואמת, ולכן מפתח שנמצא רק בשתי קבוצות הצוות מקבל את מדיניות team-platform. הסשנים שלהם על מזהה צוות הנתונים נכשלים עם is not in your role's availableModels allowlist, וההודעה מציינת את מודל ברירת המחדל של Claude Code ולא את המזהה שהם הקלידו. אם תפרטו את שתי קבוצות הצוות ב-groups של מדיניות אחת, כל מי שנמצא באחד משני הצוותים יכול לחייב את הפרופיל של כל אחד משני הצוותים.
במגבלות ההוצאה של השער עצמו, למפתח יש סך אחד ותקרה אחת לכל תקופה, כברירת מחדל הנמוכה מבין תקרות הקבוצות שלו, לכל מזהה שהסשן משתמש בו. כדי שהתקרה הנמוכה יותר של צוות אחד לא תעצור עבודה עבור הצוות האחר, הגדירו תקרה לפי משתמש עבור המפתח.
#השלבים הבאים
- הפניה להגדרות: כל אפשרות ב-
gateway.yaml, כוללmanaged.policiesו-telemetry - פריסה ותפעול: הגדרת IdP, בדיקות תקינות, רוטציה של סוד JWT, שדרוגים, ומודל האבטחה
- מצב בדיקת עומס: בדיקת עומס של שער בלי לקרוא לספק מודל
- סקירה של שער אפליקציות Claude: התחלה מהירה וחיבור מפתחים
- דוגמאות AWS לשער אפליקציות Claude: דוגמאות פריסה שמתוחזקות על ידי AWS ומכסות מגוון סביבות של לקוחות