Một số cách truy xuất dữ liệu trên AWS S3 có thể bạn muốn biết
November 15, 2022
8 min read
Ngày này việc sử dụng các dịch vụ của AWS là khá phổ biến trong các dự án tuy nhiên đối với những bạn mới sẽ rất dễ bị ngợp bởi có quá nhiều thứ phải tìm hiểu phải làm thử cũng như bị giới hạn bởi yêu cầu dự án, mức chi phí, quyền hạn… Hi mọng với series này sẽ giúp các bạn có một cái nhìn thực tế hơn về các dịch vụ của AWS thông qua bài toán thực tế, quá đó phần nào có thể áp dụng được vào dự án của mình.
Đề bài đặt ra của chúng ta cho kỳ này là làm việc với AWS S3, trong các dự án thực tế AWS S3 được sử dụng khá phổ biến trong việc lưu trữ dữ liệu của người dùng trong hệ thống thay thế việc lưu trữ ở server truyền thống.
Giả sử chúng ta đang phát triển một hệ thống giúp người dùng có thể lưu trữ và chia sẻ ảnh (Flickr, Tumblr, …. ) Trong đó người dùng có thể upload ảnh của họ lên hệ thống, ảnh sẽ được lưu trữ trên AWS S3 và chỉ người dùng đó mới có quyền xem các nội dung đó. Tuy nhiên trong trường hợp mong muốn, họ vẫn muốn sẽ có thể chia sẻ một hoặc nhiều ảnh của mình với những người dùng khác ở trong hệ thống.
Vậy làm sao có thể xử lý được vấn đề nêu trên, mời mọi người đi đến mục tiếp theo.
💡 Ghi chú: Tiền đề các giải pháp dưới đây là các bucket của AWS S3 đều được thiết lập riêng tư, không mở cho bên ngoài truy cập nếu có không quyền.
Có thể nói đây là phương án truyền thống và phổ biến nhất mà phần lớn các hệ thống thường áp dụng.
Với phương án này, client sẽ không làm việc trực tiếp với AWS S3 mà sẽ thông qua server (Có thể là EC2 Instance, Lambda….). Về phía server chúng ta sẽ cần cấp quyền IAM để có thể truy cập được dữ liệu trên AWS S3
Quay lại với bài toán của chúng ta, để tải ảnh lên S3 chúng ta sẽ sử dụng API mà phía server cung cấp. Về cơ bản, file ảnh phía client gửi lên sẽ được lưu trữ tạm thời ở server và sau đó server sẽ xử lý để gửi dữ liệu đó lên AWS S3.
Trong trường hợp tải về dữ liệu, phía client cũng sẽ sử dụng API mà phía sever cung cấp. File ảnh đầu tiên sẽ được tải về là lưu trữ tạm thời ở server, sau đó gửi về cho phía client.
- Khá dễ dàng trong việc phát triển và cấu hình.
- Chủ động được việc xử lý phân quyền từ đơn giản đến phức tạp thông qua API cũng như có thể tích hợp dễ dàng với luồng xử lý xác thực người dùng.
- Các thông tin nhạy cảm cần bảo mật như AWS key, IAM Roles sẽ chỉ được sử dụng ở phía back-end, qua đó dễ dàng hơn trong việc quản lý bảo mật dữ liệu.
- Về mặt phần cứng sẽ cần cân nhắc, lý do là server sẽ cần xử lý nhiều logic hơn trong đó có việc lưu trữ dữ liệu tạm thời ở bộ nhớ tạm hoặc bộ nhớ vật lý, tiểm ẩn rủi ro mất mát dữ liệu.
- Có khả năng đội chi phí phần cứng lên do bạn sẽ cần một cấu hình cao hơn trong trường hợp hệ thống có số lượng người dùng nhiều, cần chịu tải cao.
- Cần xử lý thủ công việc upload và download dữ liệu cũng như việc quản lý các dữ liệu tạm thời, xóa dữ liệu định kỳ…
- Khả năng mở rộng sẽ khó khăn hơn do sẽ ảnh hưởng đến phần cứng.
- Với cá nhân mình đánh giá phương án này sẽ phù hợp với các hệ thống vừa và nhỏ tuy nhiên nếu bạn xác định hệ thống của mình cần sự linh hoạt và khả năng mở rộng trong tương lai thì có thể cân nhắc đến các phương án tiếp theo.
Nếu hệ thống của bạn đang sử dụng AWS Cognito cho việc xác thực người dùng thì chúc mừng bạn, bạn có thể cân nhắc sử dụng phương án dưới đây.
Về mặt kỹ thuật, với phương án này chúng ta sẽ sử dụng Cognito Federated Identities để lấy thông tin xác thực tạm thời (AWS credentials) qua đó phía client có thể sử dụng chúng để truy xuất trực tiếp với AWS S3. Các bạn có thể đọc thêm bài viết này để hiểu rõ hơn về luồng lấy thông tin xác thực từ AWS (https://docs.aws.amazon.com/cognito/latest/developerguide/developer-authenticated-identities.html)
- 1️⃣ → Client sẽ gửi yêu cầu lên hệ thống qua API để lấy thông tin Cognito identity và token từ AWS Cognito
- 2️⃣ → Hệ thống dựa theo thông tin người dùng gửi lên sẽ thực hiện xác thực với AWS Cognito và lấy về thông tin Cognito Identity Id cũng như OpenId token và trả thông tin đó về cho client.
- 3️⃣ → Dựa vào thông tin nhận được từ bước số 2, client sẽ gửi thông tin Cognito Identity Id và OpenId token lên Cognito để yêu cầu lấy thông tin AWS credentials (Sử dụng để truy xuất vào các dịch vụ của AWS xác thực qua Cognito)
- 4️⃣ → Cognito sẽ kiểm tra các thông tin nhận được từ client, sử dụng AWS STS (Security Token Service) để đưa ra thông tin xác thực tạm thời (AWS credentials) và trả về cho phía client.
- 5️⃣ → Cuối cùng sau khi đã nhận được mã truy cập, phía client có thể sử dụng nó để truy xuất trực tiếp vào các tài nguyên của AWS S3.
- Giảm tải rất nhiều chi phía server do việc quản lý, luồng gửi và nhận dữ liệu được sử dụng trực tiếp với AWS S3 từ phía client. Phía server chỉ cần xây dựng luồng lấy thông tin Identity Id và OpenId Token để trả về cho phía client.
- Giảm chi phí phần cứng, giảm các tính năng không cần thiết, không cần quan tâm nhiều đến việc mở rộng dữ liệu,…
- Việc tích hợp sẽ phức tạp hóa hơn cho phía client.
- Do việc gửi và nhận thông tin Identity Id và OpenId Token được thực hiện thông qua API nên tồn tại rủi ro về bảo mật.
- Việc phân quyền truy cập các tài nguyên ở S3 sẽ bị giới hạn hơn khá nhiều do hệ thống sẽ chỉ kiểm tra được người dùng có quyền truy cập hay không chứ không giới hạn người dùng sẽ chỉ được truy cập đến những tài nguyên nào.
- Phương án này sẽ phù hợp khi hệ thống của bạn không cần quan tâm đến việc phân cấp các quyền truy cập, chỉ đơn giản quan tâm đến việc có được truy cập hay không.
- Phù hợp khi hệ thống của bạn sử dụng serverless, ở trong đó phía client xử lý hầu hết các logics.
Điểm trừ lớn nhất của phương án 2 là sự khó khăn trong việc phân quyền cũng như kiểm soát việc truy cập các tài nguyên. Với phương án được nêu dưới đây sẽ giải quyết được các vấn đề kể trên cũng như giúp linh động hơn trong việc chia sẻ tài nguyên giữa các tài khoản trong hệ thống.
Về mặt kỹ thuật, phương án này chúng ta sử dụng presigned URLs trong đó phía server sẽ tạo ra các đường dẫn đã được xác thực sẵn để trả về cho phía client.
- 1️⃣ → Client sẽ gửi yêu cầu lấy thông tin presigned URLs cho tài nguyên cụ thể. Ở phía server, sẽ thực hiện xử lý logics, giới hạn thời gian truy cập… và trả về thông tin presigned URLs lại cho phía client. (Tham khảo logic: https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html)
- 2️⃣ → Phía client sẽ sử dụng trực tiếp đừng dẫn kể trên cho các tác vụ tiếp theo ví dụ như xem, tải xuống…
Với trường hợp tải xuống dữ liệu thì chúng ta sẽ xử lý như 2 bước kể trên, còn với trường hợp tải dữ liệu liên S3 thì chúng ta cũng sẽ có 2 cách xử lý:
- Sử dụng API trung gian của phía server (Tương tự như phương án 1)
- Sử dụng presigned URLs cho việc tải dữ liệu trực tiếp lên S3.
- Tùy biến dễ dàng việc phân quyền cũng như việc kiểm soát các truy cập do mỗi lần người dùng muốn truy cập đều cần gửi yêu cầu lên để hệ thống xử lý.
- Phía client không cần quan tâm đến việc tích hợp các dịch vụ của AWS.
- Phía server cũng giảm tải được việc phải quản lý tài nguyên, chỉ cần xử lý luồng tạo presigned URLs dựa theo yêu cầu của người dùng.
- Cần cân nhắc trong việc giới hạn và bảo mật thông tin presigned URLs, thường chúng ta chỉ nên đặt thời gian truy cập ngắn và bắt buộc người dùng phải gửi yêu cầu lấy thông tin đường dẫn mỗi khi muốn truy cập tài nguyên để tăng tính bảo mật.
- Do truy cập trực tiếp đến AWS S3 và không có cơ chế cache nên chi phí là điều cần cân nhắc.
- Phương án khá linh động và phù hợp với phần lớn các hệ thống, chúng ta không cần phải sử dụng quá nhiều các dịch vụ của AWS như Cognito… qua đó phần nảo giảm thiểu chi phí.
- Phù hợp với các bài toán vừa và nhỏ, không cần truy xuất tài nguyên trên S3 quá nhiều.
Để giải quyết vấn đề chi phí trong trường hợp hệ thống của chúng ta cần truy cập nhiều các tài nguyên trên S3, việc sử dụng CloudFront cho việc cache dữ liệu cũng như cải thiển tốc độ truy suất dữ liệu là một phương án phù hợp.
Trường hợp người dùng tải dữ liệu lên, chúng ta vẫn sẽ sử dụng cách giống như phương án số 3 tuy nhiên trong trường hợp tải dữ liệu xuống chúng ta sẽ sử dụng CloudFront signed URLs (https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html) như một phương án thay thế.
- Thừa hưởng gần như toàn bộ các điểm lợi của phương án số 3.
- Khả năng mở rộng cao cũng như chi phí thấp do dữ liệu được cache qua đó giảm số lượng request đến AWS S3.
- Độ trễ thấp.
- Dễ dàng trong việc quản lý logs, theo dõi các vấn đề phát sinh.
- Cần cân nhắc trong việc giới hạn và bảo mật thông tin presigned URLs, thường chúng ta chỉ nên đặt thời gian truy cập ngắn và bắt buộc người dùng phải gửi yêu cầu lấy thông tin đường dẫn mỗi khi muốn truy cập tài nguyên để tăng tính bảo mật.
- Việc tích hợp CloudFront là điều không dễ dàng đối mới người mới.
- Có thể nói đây là phương án tối ưu nhất tuy nhiên cũng sẽ khó tiếp cận mới người mới nhất.
- Phù hợp với cả các hệ thống lớn.
Như các bạn đã thấy, các phương án trên đều có những điểm lợi và điểm hạn chế riêng, do đó hay lựa chọn phương án phù hợp với nhu cầu dự án của mình để tối ưu về mặt thời gian, chi phí, việc linh động các phương án dựa theo ngữ cảnh là điều tối quan trọng.
Hi vọng bài viết này sẽ giúp ích được cho các dự án của mọi người, hẹn gặp lại mọi người ở các bài viết sau.