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 , AWS Sunucusuz Hizmetleri Kullanarak Çok Kiracılı SaaS Çözümü Oluşturma ).
Bu serinin ana mantığı, tamamı AWS hesaplarına dağıtılan daha az istemciye sahip kullanım senaryoları için çok kiracılı hizmetler oluşturmaya odaklanmaktır. Genellikle bu, dahili kullanım için çok kiracılı bir hizmet oluşturduğunuzda senaryolara uygulanır.
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.
Bölüm 1'de iki AWS hizmeti için çok kiracılı mimari tartışılacaktır: API Gateway ve AppSync. Makale boyunca TypeScript ve AWS CDK'de bu makale için oluşturulan örnek uygulama uygulamasındaki koda atıfta bulunuyorum: https://github.com/filletofish/aws-cdk-multi-tenant-api-example/tree/main .
Dahili hizmetler için çoklu kiracılık
1.1. Kiracı izolasyonu
1.2. Çok kiracılı izleme
1.3. Ölçeklendirme
Dahili 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 çıkarma
AWS 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.
Genel olarak kiracı izolasyonu, kiracıların başka bir kiracının kaynaklarına erişmesinin engellenmesini sağlayarak güvenlik kaygılarını giderir. Ayrıca, bir kiracının neden olduğu arızaların hizmetinizin diğer kiracılarını etkilememesini sağlamak için kiracı izolasyonu uygulanır. Aynı zamanda sıklıkla gürültülü komşu sorunu olarak da anılır. Daha fazlasını Kiracı Yalıtım Stratejileri hakkındaki AWS Teknik İncelemesi'nde bulabilirsiniz https://d1.awsstatic.com/whitepapers/saas-tenant-isolation-strategies.pdf .
Birden fazla kiracı altyapı kaynaklarını paylaşmaya başladığında kiracılarınızın her birinin sisteminizi nasıl kullandığını izlemeniz gerekir. Bu genellikle kiracı adının veya tanımlayıcısının günlüklerinizde, ölçümlerinizde ve kontrol panellerinizde bulunması gerektiği anlamına gelir. Çok kiracılı izleme birkaç nedenden dolayı faydalı olabilir:
Çok kiracılı hizmetler, tek kiracılı hizmetlere göre ölçeklendirme zorluklarına muhtemelen daha fazla maruz kalır. Ancak ölçeklenebilirlik çok büyük bir konudur ve bu blog yazısında bu konuya değinmeyeceğim.
AWS web hizmetinizi AWS'de REST , HTTP veya WebSocket API ile oluşturuyorsanız büyük olasılıkla API Gateway kullanıyorsunuzdur.
AWS, hizmetin kaynaklarını ve verilerini izole etmek, maliyet yönetimini kolaylaştırmak ve test ile üretim ortamları arasında ayrım yapmak için her hizmeti kendi AWS hesaplarına dağıtmanızı önerir (ayrıntılar için AWS Ortamınızı Birden Çok Hesap Kullanarak Düzenleme AWS Teknik Raporu'na bakın).
Şirketinizin hizmetleri AWS'de dağıtılıyorsa API Gateway'inize erişimi yönetmek için en belirgin çözüm AWS IAM'dir. AWS Cognito, çok kiracılı API'ye erişimi yönetmek için başka bir seçenektir (bkz . API Gateway kullanarak ölçekli, katmanlı, çok kiracılı bir REST API'yi kısıtlama , Amazon Cognito lehine ve aleyhine durum ).
AWS IAM ile AWS Cognito arasındaki karşılaştırma ayrı bir derinlemesine incelemeyi hak ediyor. Ancak bu makale için, şirket hizmetleriniz AWS'deyken erişimi yönetmenin en basit yolu olduğundan AWS IAM'i tercih edeceğim.
API Ağ Geçidi Yöntemi için AWS IAM yetkilendirmesini etkinleştirdikten sonra (bkz. CFN ), bu yönteme yönelik tüm API isteklerinin, API Ağ Geçidinizi çağırmasına izin verilen IAM kimliğinin kimlik bilgileriyle imzalanması gerekir.
Varsayılan olarak AWS hesapları arasında erişime izin verilmez. Örneğin, API Ağ Geçidinizi başka bir AWS hesabının kimlik bilgileriyle çağırmak başarısız olur. Müşterilerinizi API'nizle entegre etmek için hesaplar arası erişimi ayarlamanız gerekir. API Ağ Geçidinize hesaplar arası erişim izni vermek için iki yöntem kullanabilirsiniz: kaynak tabanlı yetkilendirme (API Gateway HTTP API için mevcut değildir) ve kimlik tabanlı yetkilendirme (daha fazlasını https://repost.aws/knowledge-center/ adresinde bulabilirsiniz) erişim-api-ağ geçidi-hesabı ):
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 [1] ve [2] ). Ancak CDK kullanıyorsanız yeni aşamaların dağıtımını otomatikleştirebilirsiniz (bkz . Api Gateway için AWS CDK Dokümanları ). 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 ( sağlanan CDK uygulamasındaki kod örneğine 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}`, }) }; };
Varsayılan API Ağ Geçidi sınırı 10.000 TPS'dir ( API Ağ Geçidi Kotaları ve Sınırları ). Ancak aşağı akış bağımlılıklarınız nedeniyle hizmetiniz daha düşük bir TPS sınırı gerektirebilir. Tüm sistemin kullanılabilirliğini etkileyecek şekilde tek bir kiracıdan gelen API isteklerinin aşırı yüklenmesini önlemek için, kiracı başına API oranı sınırlamasını ("kısma" veya "kabul kontrolü" olarak da bilinir) uygulamanız gerekir.
Her istemci için sınırları ayrı ayrı yapılandırmak amacıyla API Gateway API Kullanım Planlarını ve Anahtarlarını kullanabilirsiniz (ayrıntılar için AWS belgelerine [1], [2] ve [3] bakın)
API Gateway'in iki tür günlüğü vardır:
API Ağ Geçidi Yürütme Günlükleri: istek veya yanıt parametre değerleri, hangi API anahtarlarının gerekli olduğu, kullanım planlarının etkin olup olmadığı vb. gibi verileri içerir. Varsayılan olarak etkin değildir ancak yapılandırılabilir.
API Ağ Geçidi Erişim Günlükleri özelliği: API'nize kimin eriştiğini, nasıl erişildiğini, hangi uç noktaya erişildiğini ve API çağrısının sonucunu kaydetmenize olanak tanır. Günlük biçiminizi sağlayabilir ve bağlam değişkenleriyle neyin günlüğe kaydedileceğini seçebilirsiniz (CDK'deki belgelere bakın).
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. .
Kişisel olarak, bilgi işlem olarak AWS IAM Auth ve Lambda işlevine sahip bir hizmet için bu API Ağ Geçidi Erişim Günlüğü yapılandırmasını faydalı buldum:
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);
Günlük kaydı etkinleştirildikten sonra CloudWatch Insights'ı kullanarak seçilen bir API istemcisinden en son çağrıları kolayca alabilirsiniz:
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
CloudWatch Metrik Günlük Filtreleri (belgelere bakın), özel bir filtre sağlamanıza ve API Ağ Geçidi Erişim Günlüklerinden metrik değerleri çıkarmanıza olanak tanır (aşağıdaki örneğe bakın). Metrik Günlük Filtreleri ayrıca günlüklerden özel metrik boyutları için değer çıkarılmasına da olanak tanır. Çoklu kiracılı izleme için Client boyutu arayanın IAM ARN'si olabilir.
Metrik Günlük Filtrelerinin temel avantajları şunlardır: (1) yönetilecek bilgi işlem gerektirmez (2) basit ve ucuzdur. Ancak herhangi bir veri değişikliği yapamazsınız (örneğin, IAM ARN'ler yerine daha okunabilir istemci adları ayarlayın) ve tek günlük grubu (belgeler) başına 100 metrik filtre sınırı vardır.
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',},}); });
Sağlanan örnek CDK uygulamasında 4xx, 5xx hatası ve gecikme ölçümleri için tüm ölçüm filtrelerine bakın.
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.
Lambda işlevini API Ağ Geçidi Erişim Günlüklerine abone etmek için yalnızca birkaç satır CDK koduyla:
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(), });
Koddaki tam örneği ve Log Processor Lambda Function uygulamasını görün.
Müşteriye göre bölünmüş API Ağ Geçidi ölçümlerini yayınlamaya başladığınızda, artık her istemci için ayrı ayrı CloudWatch Kontrol Panelleri ve CloudWatch Alarmları oluşturabilirsiniz.
CDK uygulamanız, müşteri adlarını, AWS hesaplarını, talep edilen TPS limitlerini ve diğer meta verileri içeren bir yapılandırmayı depolamak için kolay bir çözüm olabilir. Yeni bir API istemcisini eklemek için onu kodda yönetilen yapılandırmaya eklemeniz gerekir:
interface ApiClientConfig { name: string; awsAccounts: string[]; rateLimit: number; burstLimit: number; } const apiClients: ApiClientConfig[] = [ { name: 'payment-service', awsAccounts: ['111122223333','444455556666'], rateLimit: 10, burstLimit: 2, }, { name: 'order-service', awsAccounts: ['777788889999'], rateLimit: 1, burstLimit: 1, }, ];
Bu yapılandırmayı kullanarak CDK uygulaması daha sonra bir IAM rolü, API Ağ Geçidi Kullanım Anahtarı oluşturabilir ve istemcinin adını erişim günlüklerini ayrıştıran Lambda İşlevine aktarabilir (örnek uygulama kodunda buna bakın).
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. GH sorunu ), 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.
Ne yazık ki AppSync, kiracı yalıtımı ve izleme için ihtiyaç duyduğumuz istemci başına azaltma konusunda sınırlı desteğe sahiptir. WAF ile AppSync için TPS limitleri ayarlayabilseniz de hizmet kiracılarınızı izole etmek için istemci başına ayrı limitler oluşturamazsınız. Benzer şekilde AppSync, API Gateway'in yaptığı gibi erişim günlükleri sağlamaz.
Çözüm? API Gateway'i AppSync'inize proxy olarak ekleyebilir ve kiracı izolasyonu ve izleme gibi çoklu kiracılık gereksinimlerini uygulamak için yukarıda açıklanan API Gateway özelliklerinin tümünü kullanabilirsiniz. Bunun da ötesinde, Lambda Yetkilendiricileri, Özel Etki Alanı ve henüz AppSync'te bulunmayan API yaşam döngüsü yönetimi gibi diğer API Ağ Geçidi özelliklerini kullanabilirsiniz. Dezavantajı ise istekleriniz için hafif bir ek gecikmedir.
Bu kadar. Herhangi bir sorunuz veya fikriniz varsa, yorumlarda bana bildirin veya doğrudan benimle iletişime geçin. Bu serinin bir sonraki bölümünde AWS Event Bridge ve AWS SQS/SNS ile eşzamansız dahili entegrasyona yönelik en iyi uygulamaları inceleyeceğim.
AWS'nin üzerinde çok kiracılı hizmetler oluşturma konusunu derinlemesine incelemek istiyorsanız şu kaynakları faydalı buldum: