Giới thiệu
Chào các bạn tới với series về Serverless, ở bài trước chúng ta đã tìm hiểu về cách security một API bằng cách kết hợp API Gateway và Cognito. Ở bài này chúng ta sẽ tìm hiểu về thành phần tiếp theo để improve tốc độ tải của trang web SPA của ta, bằng cách sử dụng một dịch vụ CDN của AWS, là CloudFront.
Hệ thống mà ta sẽ xây dựng như sau.
Ở bài trước ta đã xây dựng được phần API Gateway + Lambda + S3 + Cognito.
Ở bài này ta sẽ xây dựng thêm phần CloudFront + _@.com
Provisioning previous system
Mình sẽ dùng terraform để tạo lại hệ thống, nếu các bạn muốn biết cách tạo bằng tay từng bước thì các bạn xem từ bài 2 nhé. Các bạn tải source code ở git repo này https://github.com/hoalongnatsu/serverless-series.git.
Di chuyển tới folder bai-6/terraform-start. Ở file policies/lambda_policy.json, dòng "Resource": "arn:aws:dynamodb:us-west-2:<ACCOUNT_ID>:table/books", cập nhật lại <ACCOUNT_ID> với account id của bạn. Xong sau đó chạy câu lệnh.
terraform init
terraform apply -auto-approve
Sau khi Terraform chạy xong, nó sẽ in ra terminal URL của API Gateway và website.
base_url = { "api" = "https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging" "web" = "http://serverless-series-spa.s3-website-us-west-2.amazonaws.com"
}
Và hệ thống của ta sau khi chạy Terraform như sau.
Oke, giờ ta truy cập http://serverless-series-spa.s3-website-us-west-2.amazonaws.com thì sẽ thấy được trang SPA của ta.
Tiếp theo ta sẽ tiến hành dựng phần CloudFront.
CloudFront
Amazon CloudFront là một dịch vụ CDN (content delivery network) mà cho phép chúng ta tăng tốc tộ tải của của thành phần static trong một trang web như là các file .html, .css, .js, và hình ảnh. Vì trang Single Page Application của ta hoàn toàn là static file, và xu hướng hiện giờ là tách phần FE với BE ra riêng, FE thường được build bằng React, Vue hoặc Angular. Nên sử dụng CloudFront để cache lại trang SPA là rất phù hợp.
CloudFront sẽ phân phối static content của một trang web tới toàn bộ Edge (đây là nơi lưu trữ cache của static content), và khi user truy cập trang web của ta mà có sử dụng CloudFront, thì request user sẽ được chuyển tới Edge gần nhất và Edge sẽ trả về static content cho user hay vì phải gọi tới server.
AWS có một mạng lưới Global Edge rất rộng, vì vậy khi ta dùng CloudFront thì các user ở xa bị trí server của ta vẫn có thể truy cập được trang web rất nhanh. Ví dụ hệ thống mà ta đang xây dựng, trang SPA được hosting bằng S3 nằm ở tận US West, nên khi user ở Việt Nam truy cập thì sẽ bị chậm 1 2 giây, nếu ta dùng CloudFront thì mạng lưới Edge sẽ có một thằng nằm ở Singapore, và trang SPA của ta sẽ được cache ở Singapore, khi người dùng Việt Nam truy cập trang web của ta thì họ sẽ truy cập tới Singapore chứ không phải tận bên US West.
Fix security S3 bucket
Trước khi tiến hành tạo CloudFront thì ta sẽ fix một số vấn đề liên quan tới security S3 bucket ta dùng để hosting trang SPA . Các bạn để ý là thằng serverless-series-spa bucket mà ta dùng để hosting trang SPA nó sẽ có warning màu đỏ là Public.
Lý dó là vì ở phần Block public access ta không có block gì cả.
Đối với AWS thì khi mà ta tạo một S3 mà để public access thì đó sẽ là bad security, do ở bài trước ta cần phải truy cập được vào trang SPA của ta, nên ta mới để nó là public để ai cũng có thể truy cập được, mà cách làm vậy chỉ dành cho mục đích test, còn làm thực tế thì ta không nên để S3 bucket của ta là public access, cho dù ta có dùng nó để hosting static website. Vậy thì sao ta truy cập trang web của ta được?
Câu trả lời là ta sẽ truy cập nó thông qua CloudFront, và S3 sẽ nằm ở đằng sau CloudFront. Khi user truy cập thì sẽ truy cập vào CloudFront, và CloudFront sẽ request tới S3 để lấy content sau đó nó lưu content mà lấy được từ S3 và cache, và sau đó khi user request trang web thì user sẽ lấy dữ liệu từ CloudFront ra.
Oke, giờ ta sẽ làm các bước sau để disable public access của S3 bucket.
- Truy cập https://console.aws.amazon.com/s3/.
- Bấm vào serverless-series-spa bucket.
- Chọn qua tab Permissions.
- Kéo xuống phần Block public access (bucket settings), bấm Edit.
- Check vào Block all public access. Và bấm save.
- Chọn confirm.
Tiếp theo ta sẽ cập nhật lại Bucket policy.
- Ở tab Permissions.
- Kéo xuống phần Bucket policy.
- Bấm edit.
- Xóa hết nội dung của policy.
- Bấm save. Chắc chắn là policy thành rỗng nhé.
Lúc này khi kiểm tra lại bucket thì ta sẽ thấy nó mục Access của nó là Bucket and objects not public.
Nếu bây giờ ta truy cập vào url của s3 bucket lại, thì nó sẽ trả về lỗi là 403 Forbidden. Đúng với mục đích của ta cần.
Setting CloudFront for S3 bucket
Sau khi fix security cho S3 xong thì giờ ta sẽ tiến hành tạo CloudFront cho nó. Đầu tiên ta truy cập vào console của CloudFront https://console.aws.amazon.com/cloudfront. Lúc mình viết bài này thì UI nó như sau.
Ta bấm vào nút Create a CloudFront distribution nó sẽ dẫn ta qua phần tạo distribution cho CloudFront. Ta sẽ thấy UI như hình dưới, ở mục Origin ta chọn serverless-series-spa của ta. Phần Origin path ta để rỗng.
Tiếp theo là phần quan trọng để CloudFront có thể truy cập được vào S3 bucket. Là phần S3 bucket access.
Ta chọn sang radio Yes use OAI (bucket can restrict access to only CloudFront), ở phần Bucket policy chọn sang radio Yes, update the bucket policy, ở ô Origin access identity, bấm Create new OAI, nó sẽ hiện lên cái modal tạo, bấm tạo.
Các mục còn lại tạm thời ta cứ để mặc định, kéo xuống phần Settings, ở ô Default root object điền vào index.html
Sau đó bấm Create distribution. Ta sẽ thấy CloudFront của ta.
Ở phần General -> Distribution domain name ta sẽ thấy url của CloudFront. Truy cập url này ta sẽ thấy được trang web của ta. Bây giờ thì nó đã có cache, nếu bạn để ý thì lúc này nếu ta F5 lại trang web thì hình ảnh nó tải rất nhanh so với lúc ta không dùng CloudFront.
Mọi thứ có vẻ ok, nhưng khi bạn dùng CloudFront với S3 mà để hosting một trang SPA thì nó sẽ bị một vấn đề rất phổ biến là nếu bạn truy cập vào một đường dẫn khác, ví dụ như và /login chẳng hạn.
Thì nó vẫn tải được trang web bình thường, nhưng nếu bạn reload trang lại ngay tại đường dẫn login, thì nó sẽ báo lỗi.
Lý do là vì với static website thì khi ta dùng CloudFront , nó sẽ truy cập ở root của S3 bucket là file index.html, khi nó tải được file này thì những file khác mới có thể tải được. Đối với SPA thì khi ta click trên giao diện để chuyển trang, ví dụ nhảy qua đường dẫn /login, thì thật ra nó sẽ gọi một file khác nằm trong dường dẫn js có tồn tại ở trên S3 bucket, nên trang của ta vẫn tải được. Nhưng khi ở dường dẫn /login, ta reload lại trang, lúc này CloudFront hiểu là ta truy cập vào thư mục login ở trên S3 bucket, mà S3 bucket của ta không hề có thư mục này, nên ta sẽ bị lỗi. Cấu trúc folder mà ta đã upload lên S3 bucket.
Vì vậy nên khi hosting một trang SPA, nếu bạn dùng nginx thì ta sẽ thường config thế này.
location / { index index.html; try_files $uri $uri/ /index.html;
}
Chỗ try_files nghĩa là nếu ta đi tới dường dẫn gì thì nó cũng sẽ dẫn về index.html, do đó nếu ta có /login thì nó vẫn nhảy về index.html được và tải trang của ta lên, sau ở code của SPA nó sẽ phát hiện là ta đang ở đường dẫn là /login, nó sẽ tiếp tục request file ở trong folder js.
Vậy thì ta đang dùng CloudFront chứ không có hosting bằng server, ta sẽ thực hiện công việc redirect bất kì dường dẫn nào đều quay về index.html như thế nào? Để làm được việc đó, thì AWS có cung cấp cho ta _@.com
_@.com
_@.com thì cũng giống như Lambda, nó là một function sẽ được thực thi khi có một event nào đó xảy ra, khác ở chỗ là _@.com sẽ được thực thi ở trên Edge. Ta có thể config _@.com được trigger bởi CloudFront khi một request đi tới CloudFront, như trong hình phía dưới.
Có 4 event mà CloudFront sẽ trigger _@.com là:
- CloudFront Viewer Request: _@.com sẽ được gọi khi CloudFront nhận request từ client.
- CloudFront Origin Request: _@.com sẽ được gọi khi CloudFront gửi request tới origin phía sau.
- CloudFront Origin Response: _@.com sẽ được gọi CloudFront nhận request từ origin.
- CloudFront Viewer Response: _@.com sẽ được trước khi CloudFront trả response về cho client.
Ta đang muốn modify lại request của user là khi user request tới dường dẫn nào thì cũng sẽ quay về index.html, vì vậy nên ta sẽ dùng event là CloudFront Viewer Request.
Ok, giờ ta sẽ tiến hành thực hiện tạo _@.com để modify lại request của user. Ta làm theo các bước như sau.
- Truy cập https://console.aws.amazon.com/cloudfront/home#/functions. Đây là chỗ để ta tạo một _@.com
- Bấm Create function.
- Ở ô Name, điền vào là cloudfront-redirect.
-
Bấm Create function.
-
Lúc này thì trên UI nó sẽ dẫn ta qua mục viết function.
Bạn copy đoạn code sau vào.
function handler(event) { var request = event.request; var uri = request.uri; if (uri.endsWith('/') || !uri.includes('.')) { request.uri = '/index.html'; } return request;
}
Chỗ uri.includes('.'))
là được dùng để kiểm tra nếu một dường dẫn mà nó có chứa extension thì ta sẽ không redirect về index.html.
Sau đó bấm Save changes.
- Chọn qua tab Publish, bấm Publish function, sau đó nó sẽ hiển thị cho ta phần Associated distributions.
- Bấm vào Add association, nó sẽ hiện lện một modal, chọn Distribution của ta, event type chọn Viewer Request, Cache behavior ta chọn Default. Sau đó bấm Add association.
Oke, giờ thì ta đã implement _@.com để modify request của user thành công, bây giờ thì bạn truy cập trang web của ta, nhảy qua page /login, giờ thì bạn reload trang thì ta sẽ thấy là trang web của ta sẽ không bị lỗi nữa 😁. Hình minh họa hoạt động của phần CloudFront.
Kết luận
Vậy là ta đã tìm hiểu xong cách sử dụng CloudFront và _@.com Sử dụng CloudFront sẽ giúp cho trang web của ta tải nhanh hơn và fix được một số security của S3, ngoài ra nó sẽ giúp ta giảm request tới S3 => giảm cost khi sử dụng S3. Sử dụng _@.com với CloudFront sẽ giúp ta nhiều vấn đề, ở đây ta chỉ dùng để modify request uri, nhưng nó sẽ có rất nhiều lợi ít khác nữa như là ta sử dụng giảm size của hình ảnh khi upload, customized content với high performance, v...v... Nếu có thắc mắc hoặc cần giải thích rõ thêm chỗ nào thì các bạn có thể hỏi dưới phần comment. Hẹn gặp mọi người ở bài tiếp theo.