Trong loạt bài đăng trên blog này, tôi muốn thảo luận về các phương pháp hay nhất để xây dựng dịch vụ nhiều người thuê trong AWS . Tài liệu hiện có về cách xây dựng dịch vụ nhiều người thuê thường nhắm đến các ứng dụng SaaS với hàng trăm khách hàng (ví dụ: Xây dựng giải pháp SaaS nhiều người thuê bằng AWS Serverless Services ).
Lý do chính của loạt bài này là tập trung vào việc xây dựng các dịch vụ nhiều bên thuê cho các trường hợp sử dụng có ít khách hàng hơn nhưng tất cả đều được triển khai vào tài khoản AWS. Thông thường, điều này sẽ áp dụng cho các tình huống khi bạn xây dựng dịch vụ nhiều người thuê để sử dụng nội bộ.
Tôi sẽ chia loạt bài đăng trên blog thành ba phần cho từng loại tích hợp dịch vụ với dịch vụ: tích hợp đồng bộ, không đồng bộ và hàng loạt.
Phần 1 sẽ thảo luận về kiến trúc nhiều bên thuê cho hai dịch vụ AWS: API Gateway và AppSync. Trong suốt bài viết, tôi tham khảo mã từ ứng dụng mẫu được xây dựng cho bài viết này trong Typescript và AWS CDK: https://github.com/filletofish/aws-cdk-multi-tenant-api-example/tree/main .
Nhiều người thuê dịch vụ nội bộ
1.1. Cách ly người thuê nhà
1.2. Giám sát nhiều người thuê
1.3. Chia tỷ lệ
Nhiều người thuê dịch vụ nội bộ
2.1. Cách ly người thuê - kiểm soát truy cập
2.2 Cách ly người thuê nhà - vấn đề hàng xóm ồn ào
2.3 Giám sát nhiều người thuê
2.4 Số liệu, Cảnh báo, Trang tổng quan
2.5 Tích hợp và loại bỏ ứng dụng khách API
Nhiều bên thuê với AWS AppSync
Phần kết luận
Multi-tenancy là khả năng phần mềm phục vụ nhiều khách hàng hoặc người thuê với một phiên bản phần mềm duy nhất.
Khi bạn cho phép nhiều nhóm gọi API dịch vụ của mình, dịch vụ của bạn sẽ trở thành nhiều đối tượng thuê. Kiến trúc nhiều đối tượng thuê tạo thêm độ phức tạp cho các dịch vụ của bạn, chẳng hạn như cách ly đối tượng thuê, giám sát cấp độ đối tượng thuê và mở rộng quy mô.
Nói chung, cách ly đối tượng thuê giải quyết các mối lo ngại về bảo mật bằng cách đảm bảo rằng đối tượng thuê bị ngăn truy cập vào tài nguyên của đối tượng thuê khác. Ngoài ra, tính năng cách ly đối tượng thuê được triển khai để đảm bảo rằng mọi lỗi do một đối tượng thuê gây ra sẽ không ảnh hưởng đến những đối tượng thuê khác trong dịch vụ của bạn. Nó cũng thường được gọi là vấn đề hàng xóm ồn ào. Xem thêm trong Sách trắng AWS về Chiến lược cách ly người thuê https://d1.awsstatic.com/whitepapers/saas-tenant-isolation-strategies.pdf .
Khi nhiều đối tượng thuê bắt đầu chia sẻ tài nguyên cơ sở hạ tầng, bạn sẽ cần theo dõi cách mỗi đối tượng thuê sử dụng hệ thống của bạn. Điều này thường có nghĩa là tên hoặc mã định danh đối tượng thuê phải có trong nhật ký, số liệu và bảng thông tin của bạn. Giám sát nhiều người thuê có thể hữu ích vì một số lý do:
Các dịch vụ dành cho nhiều người thuê có thể gặp nhiều thách thức về quy mô hơn so với các dịch vụ dành cho một người thuê. Tuy nhiên, khả năng mở rộng là một chủ đề lớn và tôi sẽ không đề cập đến nó trong bài viết blog này.
Nếu bạn đang xây dựng dịch vụ web AWS của mình bằng API REST , HTTP hoặc WebSocket trong AWS thì rất có thể bạn đang sử dụng API Gateway.
AWS khuyên bạn nên triển khai từng dịch vụ trong (các) tài khoản AWS riêng của dịch vụ đó để tách biệt tài nguyên và dữ liệu của dịch vụ, quản lý chi phí dễ dàng hơn cũng như tách biệt giữa môi trường thử nghiệm và môi trường sản xuất (xem chi tiết trong Sách trắng AWS Tổ chức môi trường AWS của bạn bằng nhiều tài khoản ).
Nếu dịch vụ của công ty bạn được triển khai trên AWS thì giải pháp rõ ràng nhất để quản lý quyền truy cập vào Cổng API của bạn là AWS IAM. AWS Cognito là một tùy chọn khác để quản lý quyền truy cập vào API nhiều bên thuê (xem Điều chỉnh API REST nhiều tầng, nhiều bên thuê trên quy mô lớn bằng cách sử dụng API Gateway , Trường hợp ủng hộ và chống lại Amazon Cognito ).
So sánh giữa AWS IAM và AWS Cognito xứng đáng được phân tích sâu hơn. Nhưng đối với bài viết này, tôi sẽ sử dụng AWS IAM vì đây là cách đơn giản nhất để quản lý quyền truy cập khi các dịch vụ của công ty bạn sử dụng AWS.
Sau khi bạn bật ủy quyền AWS IAM cho Phương thức cổng API (xem CFN ), tất cả yêu cầu API cho phương thức này phải được ký bằng thông tin xác thực nhận dạng IAM được phép gọi Cổng API của bạn.
Theo mặc định, không có quyền truy cập nào được phép giữa các tài khoản AWS. Ví dụ: việc gọi API Gateway của bạn bằng thông tin xác thực của một tài khoản AWS khác sẽ không thành công. Để tích hợp khách hàng với API của bạn, bạn cần thiết lập quyền truy cập nhiều tài khoản. Để cấp quyền truy cập nhiều tài khoản vào API Gateway, bạn có thể sử dụng hai phương pháp: ủy quyền dựa trên tài nguyên (không có sẵn cho API HTTP API Gateway) và ủy quyền dựa trên danh tính (xem thêm tại https://repost.aws/know-center/ truy cập-api-gateway-tài khoản ):
Đưa lên máy khách với ủy quyền dựa trên tài nguyên . Để truy cập dựa trên tài nguyên, bạn cần cập nhật Chính sách tài nguyên cổng API và thêm Tài khoản AWS của khách hàng. Nhược điểm chính của phương pháp này là sau khi bạn cập nhật chính sách tài nguyên, giai đoạn API Gateway cần được triển khai lại để các thay đổi có hiệu lực (xem tài liệu AWS [1] và [2] ). Tuy nhiên, nếu sử dụng CDK, bạn có thể tự động hóa việc triển khai các giai đoạn mới (xem Tài liệu AWS CDK dành cho Cổng Api ). Một nhược điểm khác là giới hạn về độ dài tối đa của chính sách tài nguyên.
Tiếp nhận khách hàng bằng ủy quyền dựa trên danh tính . Để kiểm soát quyền truy cập dựa trên danh tính, bạn cần tạo vai trò IAM cho máy khách và cho phép máy khách đảm nhận vai trò đó bằng cách cập nhật chính sách tài nguyên của vai trò (mối quan hệ đáng tin cậy). Bạn có thể sử dụng người dùng IAM, nhưng vai trò IAM sẽ tốt hơn từ quan điểm bảo mật. Các vai trò cho phép xác thực bằng thông tin xác thực tạm thời và không yêu cầu lưu trữ thông tin xác thực người dùng IAM. Có giới hạn 1.000 vai trò cho mỗi tài khoản, nhưng giới hạn này có thể điều chỉnh được. Ngoài ra, một nhược điểm khác của phương pháp dựa trên vai trò để có được quyền truy cập nhiều tài khoản vào API của bạn là bạn cần tạo vai trò IAM cho mọi ứng dụng khách API mới. Tuy nhiên, quản lý vai trò có thể được tự động hóa bằng CDK (xem mẫu mã từ ứng dụng CDK được cung cấp ).
Ủy quyền AWS IAM chỉ cho phép bạn kiểm soát quyền truy cập vào API Gateway (sử dụng chính sách IAM, bạn có thể chỉ định tài khoản AWS nào có thể gọi điểm cuối API Gateway nào). Bạn có trách nhiệm triển khai quyền truy cập kiểm soát vào dữ liệu và các tài nguyên cơ bản khác trong dịch vụ của mình. Trong dịch vụ của mình, bạn có thể sử dụng AWS IAM ARN của người gọi được chuyển bằng Yêu cầu cổng API để kiểm soát quyền truy cập sâu hơn:
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}`, }) }; };
Giới hạn API Gateway mặc định là 10.000 TPS ( Hạn ngạch và giới hạn API Gateway ). Tuy nhiên, do sự phụ thuộc ở hạ lưu, dịch vụ của bạn có thể yêu cầu giới hạn TPS thấp hơn. Để tránh tình trạng quá tải các yêu cầu API từ một đối tượng thuê sẽ ảnh hưởng đến tính khả dụng của toàn bộ hệ thống, bạn nên triển khai giới hạn tốc độ API cho mỗi đối tượng thuê (còn được gọi là "điều tiết" hoặc "kiểm soát tiếp nhận").
Bạn có thể sử dụng Gói và khóa sử dụng API API Gateway để đặt cấu hình giới hạn cho từng máy khách riêng biệt (để biết chi tiết, hãy xem tài liệu AWS [1], [2] và [3])
API Gateway có hai loại nhật ký:
Nhật ký thực thi cổng API: chứa dữ liệu như giá trị tham số yêu cầu hoặc phản hồi, khóa API nào được yêu cầu, liệu gói sử dụng có được bật hay không, v.v. Không được bật theo mặc định nhưng có thể được cấu hình.
Tính năng Nhật ký truy cập cổng API: cho phép bạn ghi lại ai đã truy cập API của bạn, cách truy cập, điểm cuối nào được truy cập và kết quả của lệnh gọi API. Bạn có thể cung cấp định dạng nhật ký của mình và chọn nội dung cần ghi bằng các biến ngữ cảnh (xem tài liệu, trong CDK).
Để theo dõi yêu cầu của ứng dụng khách API, tôi khuyên bạn nên bật tính năng ghi nhật ký truy cập. Bạn có thể đăng nhập ít nhất AWS IAM ARN của người gọi ( $context.identity.userArn
), đường dẫn yêu cầu ( $context.path
), mã trạng thái phản hồi dịch vụ của bạn $context.status
và độ trễ cuộc gọi API ( $context.responseLatency
) .
Cá nhân, đối với một dịch vụ có chức năng AWS IAM Auth và Lambda như tính toán, tôi thấy cấu hình Ghi nhật ký truy cập cổng API này hữu ích:
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);
Sau khi bật tính năng ghi nhật ký, bạn có thể sử dụng CloudWatch Insights để dễ dàng nhận các cuộc gọi mới nhất từ ứng dụng khách API đã chọn bằng:
fields @timestamp, path, status, responseLatency, userArn | sort @timestamp desc | filter userArn like 'payment-service' | limit 20
Theo mặc định, số liệu CloudWatch được API Gateway hỗ trợ sẽ được tổng hợp cho tất cả các yêu cầu. Tuy nhiên, bạn có thể phân tích cú pháp nhật ký truy cập API Gateway để xuất bản số liệu CloudWatch tùy chỉnh cùng với thứ nguyên bổ sung là tên khách hàng của bạn để có thể giám sát việc sử dụng API của khách hàng (đối tượng thuê). Ở mức tối thiểu, tôi khuyên bạn nên xuất bản số liệu CloudWatch cho mỗi khách hàng. Đếm, 4xx, 5xx, Độ trễ được phân chia theo Dimension=${Client}
. Bạn cũng có thể thêm các thứ nguyên như mã trạng thái và đường dẫn API.
2.4.1. Sử dụng bộ lọc nhật ký số liệu để xuất bản số liệu cho mỗi khách hàng
Bộ lọc nhật ký số liệu CloudWatch (xem tài liệu) cho phép bạn cung cấp bộ lọc tùy chỉnh và trích xuất các giá trị số liệu từ Nhật ký truy cập cổng API (xem ví dụ bên dưới). Bộ lọc nhật ký số liệu cũng cho phép trích xuất giá trị cho thứ nguyên số liệu tùy chỉnh từ nhật ký. Để giám sát nhiều bên thuê, thứ nguyên Máy khách có thể là IAM ARN của người gọi.
Ưu điểm chính của Bộ lọc nhật ký số liệu là (1) không cần tính toán để quản lý (2) đơn giản và rẻ. Nhưng bạn không thể thực hiện bất kỳ sửa đổi dữ liệu nào (ví dụ: đặt tên khách hàng dễ đọc hơn thay vì IAM ARN) và có giới hạn 100 bộ lọc số liệu cho mỗi nhóm nhật ký (tài liệu).
Ví dụ về Bộ lọc nhật ký số liệu CloudWatch để Count
lượng xuất bản với thứ nguyên Client
và Path
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',},}); });
Xem tất cả các bộ lọc số liệu về số liệu lỗi 4xx, 5xx và độ trễ tại ứng dụng CDK mẫu được cung cấp .
2.4.2. Sử dụng hàm Lambda để xuất bản số liệu cho mỗi khách hàng
Tùy chọn thay thế là tạo hàm Lambda để phân tích nhật ký, trích xuất số liệu và xuất bản chúng. Điều này cho phép bạn thực hiện nhiều công việc tùy chỉnh hơn như lọc các ứng dụng khách không xác định hoặc trích xuất tên ứng dụng khách từ userArn.
Chỉ với một vài dòng mã CDK để đăng ký hàm Lambda vào Nhật ký truy cập cổng API:
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(), });
Xem ví dụ đầy đủ về mã cũng như cách triển khai Hàm Lambda của Bộ xử lý nhật ký .
Sau khi bắt đầu xuất bản các số liệu Cổng API được phân chia theo Ứng dụng khách, giờ đây bạn có thể tạo Bảng thông tin CloudWatch và Cảnh báo CloudWatch cho từng ứng dụng khách một cách riêng biệt.
Ứng dụng CDK của bạn có thể là một giải pháp dễ dàng để lưu trữ cấu hình với tên khách hàng, tài khoản AWS của họ, giới hạn TPS được yêu cầu và siêu dữ liệu khác. Để tích hợp ứng dụng khách API mới, bạn cần thêm nó vào cấu hình được quản lý bằng mã:
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, }, ];
Khi sử dụng cấu hình này, ứng dụng CDK có thể tạo vai trò IAM, Khóa sử dụng cổng API và chuyển tên của ứng dụng khách đến Hàm Lambda để phân tích cú pháp nhật ký truy cập (xem mã này trong mã ứng dụng mẫu).
Nếu dịch vụ của bạn có API GraphQL thì bạn có thể sử dụng AppSync. Tương tự như API Gateway, bạn có thể sử dụng IAM Auth để ủy quyền các yêu cầu AppSync. AppSync không có chính sách tài nguyên (xem vấn đề GH ), vì vậy bạn chỉ có thể sử dụng ủy quyền dựa trên vai trò để thiết lập kiểm soát truy cập vào API AppSync. Tương tự như API Gateway, bạn sẽ tạo vai trò IAM riêng cho từng đối tượng thuê dịch vụ mới của mình.
Thật không may, AppSync có hỗ trợ hạn chế cho việc điều chỉnh theo từng khách hàng mà chúng tôi cần để cách ly và giám sát đối tượng thuê. Mặc dù bạn có thể thiết lập giới hạn TPS cho AppSync bằng WAF nhưng bạn không thể tạo giới hạn riêng cho mỗi khách hàng để tách biệt đối tượng thuê dịch vụ của mình. Tương tự, AppSync không cung cấp nhật ký truy cập như API Gateway.
Giải pháp? Bạn có thể thêm API Gateway làm proxy cho AppSync của mình và sử dụng tất cả các tính năng API Gateway được mô tả ở trên để triển khai các yêu cầu nhiều bên thuê như cách ly và giám sát đối tượng thuê. Ngoài ra, bạn có thể sử dụng các tính năng API Gateway khác như Trình ủy quyền Lambda, Miền tùy chỉnh và quản lý vòng đời API chưa tồn tại trong AppSync. Nhược điểm là độ trễ bổ sung nhỏ cho các yêu cầu của bạn.
Đó là nó. Nếu bạn có bất kỳ câu hỏi hoặc ý tưởng nào, hãy cho tôi biết trong phần bình luận hoặc liên hệ trực tiếp với tôi. Trong phần tiếp theo của loạt bài này, tôi sẽ xem xét các phương pháp thực hành tốt nhất để tích hợp nội bộ không đồng bộ với AWS Event Bridge và AWS SQS / SNS.
Nếu bạn muốn tìm hiểu sâu hơn về chủ đề xây dựng dịch vụ nhiều người thuê trên AWS, tôi thấy các tài nguyên này hữu ích:
Cũng được xuất bản ở đây.