AWS - dostarczanie treści za pomocą CloudFront06.VII.2017
Spis treści
- Czym jest CloudFront
- Przygotowanie do konfiguracji CloudFront
- Przygotowanie S3
- Konfiguracja CloudFront
- Test treści dynamicznych
- Treści statyczne serwowane z S3
- Problemy z komunikacją
- Pliki logów
Czym jest CloudFront
AWS CloudFront wygląda jak dość rozbudowany CDN (Content Delivery Network). Pozwala nie tylko na dostarczanie treści statycznych najbliżej, pod względem położenia na kuli ziemskiej, użytkownika (czyli to co potrafi standardowy CDN) ale również można w niego niejako wpleść treści dostarczane dynamicznie.
Wszystkie treści pobierane są zawsze z serwera źródłowego (origin server) i na podstawie ustawień w nagłówkach 'Cache-Control' w odpowiedzi od serwera, którą uzyska CloudFront, przechowywane są przez wskazany czas w lokalizacjach brzegowych. Zatem dla poniższych ustawień CloudFront zachowa się:
- Cache-Control: no-cache, no-store, must-revalidate - każde zapytanie skierowane do serwera CloudFront będzie skutkowało odczytaniem danych z serwera źródłowego
- Cache-Control: public, max-age=60 - zapytanie kierowane do CloudFront będzie odświeżało zawartość co 60 sekund z serwera źródłowego a w czasie zapytań pomiędzy odświeżeniami zawartość będzie dostarczana tylko z serwerów brzegowych CloudFront
- Brak nagłówka Cache-Control - zostanie użyta wartość określona w polu Default TTL
Więcej informacji na temat nagłówka Cache-Control znajduje się w dokumentacji MDN: Cache-Control.
Przygotowanie do konfiguracji CloudFront
Stawiamy serwer WWW np. na EC2 (instrukcja w AWS:Hello EC2 lub dowolny inny). Do tego przykładu posłuży pusty serwer na Ubuntu z NodeJs na pokładzie. Po zalogowaniu do serwera uruchamiamy plik server.js z poniższą zawartością. Wszystkie wyniki jego działania będą przedstawiane na konsoli.
// \> nodejs server.js 80
//
var http = require('http');
var app = http.createServer(function(req,res) {
// zeby Chrome ciagle nie pytal
if ('/favicon.ico' === req.url) {
res.statusCode = 404;
res.end();
return;
}
if ('/' === req.url || '/initgo' === req.url) {
// dane prywatne bez buforowania
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
// dane statyczne-publiczne
//res.setHeader('Cache-Control', 'public, max-age=60');
}
var now = (new Date()).toISOString();
console.log(`${now} >>`, `url:${req.url}`);
res.statusCode = 200;
res.end('Teraz mamy: ' + now);
// Teraz mamy: 2017-07-06T17:36:01.640Z
});
app.listen(process.argv[2] || 1337);
Przygotowanie S3
W AWS S3 tworzymy nowy pojemnik (jeśli do tej pory nie istniał) jakasnazwa.s3.amazonaws.com a w nim poniższą strukturę katalogów.
- cf-logs/ - tu będą się pojawiać logi z usługi CloudFront
-
cf-static/ - katalog z elementami statycznymi (grafiki, itd)
-
sta/ katalog odpowiedzialny za zawartość adresu http://cf.pieszynski.com/sta/
- pol.png - obrazek PNG
-
sta/ katalog odpowiedzialny za zawartość adresu http://cf.pieszynski.com/sta/
Klikamy prawym na katalog cf-static/ i wybieramy opcję Make public tak aby wszystkie elementy w tym katalogu były dostępne publicznie. Inaczej CloudFront nie będzie ich w stanie przekazać użytkownikowi końcowemu.
Jeśli w metadanych pliku pol.png ustawimy klucz Cache-Control to zostanie on wybrany zamiast ustawienia domyślnego w Default TTL.
Konfiguracja CloudFront
- Przechodzimy do usługi CloudFront
-
Create Distribution
- Select delivery method - Web - Get Started
-
Create distribution
-
Origin settings
- Origin Domain Name: ec2-xxx.xxx.xxx.xxx.eu-west-1.compute.amazonaws.com
- Origin ID: rootOriginId
-
Origin Custom Headers
- Header Name: X-PP-Front
- Value: EC2_v1
- Klikamy przycisk plusa (+)
-
Default Cache Behavior Settings
- Path Pattern: Default (*)
- Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
- Forward Headers: Whitelist
- Whitelist Headers: CloudFront-Viewer-Country
- Object Caching: Customize
- Minimum TTL: 0
- Maximum TTL: 30
- Default TTL: 20
- Compress Objects Automatically: tak
-
Distribution Settings
- Alternate Domain Names (CNAMEs): pf.pieszynski.com
- Default Root Object: initgo albo index.html
- Logging: tak
- Bucket for Logs: jakasnazwa.s3.amazonaws.com
- Log Prefix: cf-logs/
- Create distribution
-
Origin settings
- CloudFront wygenerował w tym momencie swój adres dostępowy dxxxxxxx.cloudfront.net, który można teraz ustawić jako rekord CNAME dla swojej domeny - tu cf.pieszynski.com
- Zaznaczamy nową dystrybucję i klikamy Distribution settings lub po prostu klikamy w ID dystrybucji
-
Przechodzimy na zakładkę Origins i klikamy Create origin
- Origin Domain Name: klikamy w pole i wybieramy swój pojemnik S3 jakasnazwa.s3.amazonaws.com
- Origin Path: /cf-static
- Origin ID: s3CfStatic
- Yes, create
-
Przechodzimy na zakładkę Behaviors i klikamy Create Behavior
- Path Pattern: sta/*
- Origin: s3CfStatic
- Object Caching: Customize
- Minimum TTL: 0
- Maximum TTL: 60
- Default TTL: 60
- Create
- Teraz należy poczekać aż dystrybucja zostanie opublikowana - będzie miała status Deployed
Aby usługa CloudFront przekazywała serwerowi informację o państwie z którego pochodzi żądanie należy wskazać aby do zapytań był dołączany nagłówek CloudFront-Viewer-Country.
Test treści dynamicznych
Wyniki dla wejścia na stronę http://cf.pieszynski.com/ z adresów w Polsce i USA.
Ścieżka widziana przez serwer - /initgo - wynika z ustawienia domyślnego Default Root Object. Docelowo pewnie znalazłby się tam wpis index.html.
ubuntu@ip:~$ nodejs server.js
2017-07-06T17:26:44.680Z >> url:/initgo, headers: {
'user-agent': 'Amazon CloudFront',
'x-forwarded-for': 'xxx.PL.IP.xxx',
'cloudfront-viewer-country': 'PL',
'x-pp-front': 'EC2_v1'
}
2017-07-06T17:27:37.736Z >> url:/initgo, headers: {
'user-agent': 'Amazon CloudFront',
'x-forwarded-for': 'yyy.US.IP.yyy',
'cloudfront-viewer-country': 'US',
'x-pp-front': 'EC2_v1'
}
Za każdym razem nagłówek X-Cache ma wartość Miss from cloudfront czyli zawartość musiała być odświeżona z serwera źródłowego a na konsoli serwera WWW pojawiają się wpisy dla każdego wywołania.
HTTP/1.1 200 OK
Content-Length: 36
Connection: keep-alive
Cache-Control: no-cache, no-store, must-revalidate
X-Cache: Miss from cloudfront
Via: 1.1 zzz.cloudfront.net (CloudFront)
Treści statyczne serwowane z S3
W tym przypadku należy zwrócić uwagę, że po skonfigurowaniu Path Pattern na sta/* CloudFront będzie używał podkatalogu cf-static/ z wskazanego pojemnika dodając do niego CAŁĄ ścieżkę z adresu URL. Czyli dla adresu http://cf.pieszynski.com/sta/pol2.png plik musi znajdować się w katalogu bezwzględnym w S3 cf-static/sta/pol2.png a nie cf-static/pol2.png!
Wyniki dla wejścia na stronę http://cf.pieszynski.com/sta/pol2.png.
HTTP/1.1 200 OK
Content-Type: image/png
ETag: "e0012d..."
Server: AmazonS3
X-Cache: Miss from cloudfront
HTTP/1.1 304 Not Modified
ETag: "e0012d..."
Server: AmazonS3
Age: 59
X-Cache: Hit from cloudfront
HTTP/1.1 304 Not Modified
ETag: "e0012d..."
Server: AmazonS3
X-Cache: RefreshHit from cloudfront
- Za pierwszym razem obrazka nie było w lokalizacji brzegowej - Miss from cloudfront - dane zostały pobrane z pojemnika S3
- Każde kolejne wywołanie przez 60 sekund zwraca plik z bufora CloudFront - Hit from cloudfront - podając jednocześnie wiek obiektu w buforze (Age) - 59 sekund
- Pierwsze wywołanie starsze niż 60 sekund powoduje odświeżenie lokalizacji brzegowej z S3 - RefreshHit from cloudfront
Problemy z komunikacją
Gdy CloudFront dostarcza dane z serwera WWW a ten w pewnym momencie przestanie odpowiadać to CloudFront serwuje dane z bufora przez kolejne pięć minut nie odpytując w tym czasie ani razu serwera WWW (niezależnie od tego co było ustawione w Cache-Control). Więcej informacji na ten temat w dokumentacji: How CloudFront Processes and Caches HTTP 4xx and 5xx Status Codes from Your Origin
Pliki logów
#Version: 1.0
#Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version
2017-07-06 17:35:55 WAW50 361 xxx.xxx.xxx.xxx GET dxxxxxxx.cloudfront.net / 200 - Mozilla/ - - Miss == cf.pieszynski.com http 407 0.088 - - - Miss HTTP/1.1
2017-07-06 17:35:59 WAW50 361 xxx.xxx.xxx.xxx GET dxxxxxxx.cloudfront.net / 200 - Mozilla/ - - Miss == cf.pieszynski.com http 433 0.053 - - - Miss HTTP/1.1
2017-07-06 17:36:01 WAW50 361 xxx.xxx.xxx.xxx GET dxxxxxxx.cloudfront.net / 200 - Mozilla/ - - Miss == cf.pieszynski.com http 433 0.114 - - - Miss HTTP/1.1