Bu blog yazısı dizisinde AWS'de çok kiracılı hizmetler oluşturmaya yönelik en iyi uygulamaları tartışmak istiyorum. Çok kiracılı hizmetlerin nasıl oluşturulacağına ilişkin mevcut literatür genellikle yüzlerce müşteriye sahip SaaS uygulamalarını hedef alır (örneğin ).
Her hizmetten hizmete entegrasyon türü için blog gönderileri dizisini üç bölüme ayıracağım: senkronize, eşzamansız ve toplu entegrasyon.
Dahili hizmetler için çoklu kiracılık
1.1. Kiracı izolasyonu 1.2. Çok kiracılı izleme 1.3. ÖlçeklendirmeDahili hizmetler için çoklu kiracılık
2.1. Kiracı izolasyonu - erişim kontrolü 2.2 Kiracı izolasyonu - gürültülü komşu sorunu 2.3 Çok kiracılı izleme 2.4 Metrikler, Alarmlar, Kontrol Panelleri 2.5 API istemcilerini ekleme ve çıkarmaAWS AppSync ile çoklu kiracılık
Çözüm
Çoklu kiracılık, yazılımın tek bir örnekle birden fazla müşteriye veya kiracıya hizmet verebilmesidir.
Birden fazla ekibin hizmet API'nizi aramasına izin verdiğinizde hizmetiniz çok kiracılı hale gelir. Çok kiracılı mimari, hizmetlerinize kiracı yalıtımı, kiracı düzeyinde izleme ve ölçeklendirme gibi ek karmaşıklıklar katar.
AWS web hizmetinizi AWS'de REST , HTTP veya WebSocket API ile oluşturuyorsanız büyük olasılıkla API Gateway kullanıyorsunuzdur.
Kaynak tabanlı yetkilendirmeyle bir istemciyi ekleme . Kaynak tabanlı erişim için API Ağ Geçidi Kaynak Politikasını güncellemeniz ve istemcinizin AWS Hesabını eklemeniz gerekir. Bu yöntemin ana dezavantajı, kaynak politikasını güncellediğinizde değişikliklerin etkili olması için API Ağ Geçidi aşamasının yeniden dağıtılması gerekmesidir (bkz. AWS belgeleri ve ). Ancak CDK kullanıyorsanız yeni aşamaların dağıtımını otomatikleştirebilirsiniz (bkz ). Diğer bir dezavantaj ise kaynak politikasının maksimum uzunluğunun sınırıdır.
Kimlik tabanlı yetkilendirmeyle bir istemciyi ekleme . Kimlik tabanlı erişim kontrolü için, istemci için bir IAM rolü oluşturmanız ve rolün kaynak politikasını (güvenilir ilişkiler) güncelleyerek istemcinin bu rolü üstlenmesine izin vermeniz gerekir. IAM kullanıcılarını kullanabilirsiniz ancak IAM rolleri güvenlik açısından daha iyidir. Roller, geçici kimlik bilgileriyle kimlik doğrulamasına olanak tanır ve IAM kullanıcı kimlik bilgilerinin saklanmasını gerektirmez. Hesap başına 1.000 rol sınırı vardır ancak bu sınır ayarlanabilir. Ayrıca, API'nize hesaplar arası erişim sağlamaya yönelik rol tabanlı yöntemin bir diğer dezavantajı, her yeni API istemcisi için bir IAM rolü oluşturmanızın gerekmesidir. Ancak rol yönetimi CDK ile otomatikleştirilebilir ( bakın).
AWS IAM yetkilendirmesi yalnızca API Ağ Geçidine erişimi kontrol etmenize olanak tanır (IAM politikasını kullanarak hangi AWS hesabının hangi API Ağ Geçidi uç noktalarını arayabileceğini belirtebilirsiniz). Hizmetinizin verilerine ve diğer temel kaynaklarına erişim kontrolünü uygulamak sizin sorumluluğunuzdur. Hizmetiniz dahilinde, daha fazla erişim kontrolü için arayanın API Ağ Geçidi İsteği ile iletilen AWS IAM ARN'sini kullanabilirsiniz:
export const handler = async (event: APIGatewayEvent, context: Context): Promise<APIGatewayProxyResult> => { // IAM Principal ARN of the api caller const callerArn = event.requestContext.identity.userArn!; // .. business logic based on caller return { statusCode: 200, body: JSON.stringify({ message: `Received API Call from ${callerArn}`, }) }; };
API Gateway'in iki tür günlüğü vardır:
API istemcilerinizin isteklerini izlemek için erişim günlüğünü etkinleştirmenizi öneririm. En azından arayanın AWS IAM ARN'sini ( $context.identity.userArn
), istek yolunu ( $context.path
), hizmet yanıt durum kodunuzu $context.status
ve API çağrı gecikmesini ( $context.responseLatency
) günlüğe kaydedebilirsiniz. .
const formatObject = { requestId: '$context.requestId', extendedRequestId: '$context.extendedRequestId', apiId: '$context.apiId', resourceId: '$context.resourceId', domainName: '$context.domainName', stage: '$context.stage', path: '$context.path', resourcePath: '$context.resourcePath', httpMethod: '$context.httpMethod', protocol: '$context.protocol', accountId: '$context.identity.accountId', sourceIp: '$context.identity.sourceIp', user: '$context.identity.user', userAgent: '$context.identity.userAgent', userArn: '$context.identity.userArn', caller: '$context.identity.caller', cognitoIdentityId: '$context.identity.cognitoIdentityId', status: '$context.status', integration: { // The status code returned from an integration. For Lambda proxy integrations, this is the status code that your Lambda function code returns. status: '$context.integration.status', // For Lambda proxy integration, the status code returned from AWS Lambda, not from the backend Lambda function code. integrationStatus: '$context.integration.integrationStatus', // The error message returned from an integration // A string that contains an integration error message. error: '$context.integration.error', latency: '$context.integration.latency', }, error: { responseType: '$context.error.responseType', message: '$context.error.message', }, requestTime: '$context.requestTime', responseLength: '$context.responseLength', responseLatency: '$context.responseLatency', }; const accessLogFormatString = JSON.stringify(formatObject); const accessLogFormat = apigw.AccessLogFormat.custom(accessLogFormatString);
fields @timestamp, path, status, responseLatency, userArn | sort @timestamp desc | filter userArn like 'payment-service' | limit 20
Varsayılan olarak API Gateway tarafından desteklenen CloudWatch Metrikleri tüm istekler için toplanır. Ancak API'nizin istemci (kiracı) kullanımını izleyebilmek amacıyla, istemci adınızın ek bir boyutuyla özel CloudWatch ölçümlerini yayınlamak için API Ağ Geçidi erişim günlüklerini ayrıştırabilirsiniz. En azından, istemci başına CloudWatch ölçümleri Count, 4xx, 5xx, Latency split by Dimension=${Client}
yayınlamanızı öneririm. Durum kodu ve API yolu gibi boyutları da ekleyebilirsiniz.
2.4.1. Müşteri başına metrikleri yayınlamak için metrik günlük filtrelerini kullanma
Client
ve Path
boyutuyla Yayın Count
için CloudWatch Metrik Günlük Filtresi Örneği
new logs.MetricFilter(this, 'MultiTenantApiCountMetricFilter', { logGroup: accessLogsGroup, filterPattern: logs.FilterPattern.exists('$.userArn'), metricNamespace: metricNamespace, metricName: 'Count', metricValue: '1', unit: cloudwatch.Unit.COUNT, dimensions: { client: '$.userArn', method: '$.httpMethod', path: '$.path',},}); });
2.4.2. Müşteri başına ölçümleri yayınlamak için Lambda işlevini kullanma
Alternatif seçenek ise günlükleri ayrıştırmak, metrikleri çıkarmak ve yayınlamak için bir Lambda işlevi oluşturmaktır. Bu, bilinmeyen istemcileri filtrelemek veya userArn'den istemci adını çıkarmak gibi daha özel şeyler yapmanıza olanak tanır.
const logProcessingFunction = new lambda.NodejsFunction( this, 'log-processor-function', { functionName: 'multi-tenant-api-log-processor-function', } ); new logs.SubscriptionFilter(this, 'MultiTenantApiLogSubscriptionFilter', { logGroup: accessLogsGroup, destination: new logsd.LambdaDestination(logProcessingFunction), filterPattern: logs.FilterPattern.allEvents(), });
interface ApiClientConfig { name: string; awsAccounts: string[]; rateLimit: number; burstLimit: number; } const apiClients: ApiClientConfig[] = [ { name: 'payment-service', awsAccounts: ['3','444455556666'], rateLimit: 10, burstLimit: 2, }, { name: 'order-service', awsAccounts: ['777788889999'], rateLimit: 1, burstLimit: 1, }, ];
Hizmetinizde GraphQL API varsa muhtemelen AppSync kullanıyorsunuzdur. API Gateway'e benzer şekilde, AppSync isteklerini yetkilendirmek için IAM Auth'u kullanabilirsiniz. AppSync'in bir kaynak politikası yoktur (bkz. ), dolayısıyla AppSync API'ye erişim kontrolünü ayarlamak için yalnızca rol tabanlı yetkilendirmeyi kullanabilirsiniz. API Gateway'e benzer şekilde hizmetinizin her yeni kiracısı için ayrı bir IAM rolü oluşturursunuz.