Введение
Новая версия может быть правильно сохранена в S3, но распределение продолжит отдавать предыдущую копию из кэша. Настройте срок кэширования, наблюдайте повторное использование и истечение срока, затем инвалидируйте только путь обновлённой страницы. Сохраните кэш второго объекта и закрытый доступ к источнику.
Сначала завершите лабораторную работу по доставке закрытого содержимого S3. Эта независимая VM предоставляет новый закрытый бакет, OAC, распределение и соответствующую политику бакета. Ресурсы предыдущей VM не используются. Изначально кэширование отключено, запросов пользователей и инвалидаций нет. Подготовленные страницы позволяют сосредоточиться на кэше. Наблюдайте реальное состояние в AWS View сверху и выполняйте команды в Terminal снизу. Личный аккаунт AWS и публичный домен не нужны.
Цели сертификации
| Сертификация | Задача экзамена | Практика |
|---|---|---|
| Solutions Architect – Associate (SAA-C03) | Задача 3.4 | Доставка содержимого через CloudFront и влияние срока кэширования на чтения источника. |
Обзор лабораторной работы

Наблюдение повторного использования и истечения срока кэша
На этом шаге настройте короткий срок кэширования, чтобы отличить попадание в кэш от повторного чтения источника.
Кэш сохраняет копию для последующих запросов. TTL — срок её действия в секундах. Cache-Control: max-age объекта задаёт срок; минимальный и максимальный TTL распределения ограничивают его, а значение по умолчанию используется, если источник не указывает срок. Здесь применяются обычные настройки распределения без отдельной политики кэширования.
Найдите подготовленное распределение. --query выбирает запись с комментарием лабораторной работы, а $(...) сохраняет ID или домен в переменную оболочки. Оставьте Terminal открытым. Посмотрите на независимый эталонный бакет, не изменяя его:
cd /home/labex/project
DIST_ID=$(aws cloudfront list-distributions \
--query "DistributionList.Items[?Comment=='labex-n03:private-content'].Id | [0]" \
--output text)
DIST_DOMAIN=$(aws cloudfront get-distribution \
--id "$DIST_ID" \
--query Distribution.DomainName \
--output text)
aws s3api list-buckets \
--query 'Buckets[].Name'
Прочитайте текущую конфигурацию и её ETag — идентификатор версии для безопасного обновления. > записывает вывод в файл. sed меняет только два изначально нулевых атрибута TTL; минимальный остаётся нулевым. Новый максимум позволит позже задать более длительный срок:
aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query DistributionConfig > current-config.json
ETAG=$(aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query ETag \
--output text)
sed -e 's/"DefaultTTL": 0/"DefaultTTL": 6/' -e 's/"MaxTTL": 0/"MaxTTL": 3600/' current-config.json > cached-config.json
aws cloudfront update-distribution \
--id "$DIST_ID" \
--if-match "$ETAG" \
--distribution-config file://cached-config.json
aws cloudfront wait distribution-deployed \
--id "$DIST_ID"
Загрузите подготовленную первую версию со сроком шесть секунд. Сохраните тип содержимого text/html:
aws s3api put-object \
--bucket labex-n03-content \
--key index.html \
--body index.html \
--content-type text/html \
--cache-control 'max-age=6'
curl --include показывает заголовки и тело. --resolve направляет реальное имя распределения к пользовательской точке доступа этой VM без изменения системного DNS. Выполните оба запроса подряд, чтобы второй пришёл до истечения шести секунд:
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
Первый показывает X-Cache: Miss from cloudfront, второй — Hit from cloudfront, оба возвращают Release one. Попадание использует сохранённые байты без нового чтения источника. Age показывает возраст копии; немедленные запросы могут оба показать ноль секунд.
sleep 7 позволяет шестисекундной копии устареть перед новым запросом:
sleep 7
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
Ожидайте ещё один промах кэша с той же страницей. Истечение срока заставляет следующий запрос снова читать источник, но не удаляет объект S3. AWS View показывает реальные запросы и число чтений источника. Выполните проверку перед продолжением.

Наблюдение обновления за сохранённой копией
На этом шаге загрузите новую версию и выясните, почему существующий кэш продолжает возвращать старое содержимое.
Более долгий срок упрощает наблюдение. Загрузите первую страницу повторно с max-age=900, а независимый объект stable.txt — с max-age=3600. Максимальный TTL распределения 3600 допускает оба значения:
aws s3api put-object \
--bucket labex-n03-content \
--key index.html \
--body index.html \
--content-type text/html \
--cache-control 'max-age=900'
aws s3api put-object \
--bucket labex-n03-content \
--key stable.txt \
--body stable.txt \
--content-type text/plain \
--cache-control 'max-age=3600'
Изменение метаданных источника не меняет уже сохранённый ответ задним числом. Дождитесь истечения шестисекундной копии, затем запросите оба пути, чтобы заполнить кэш:
sleep 7
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/stable.txt"
Оба запроса показывают промах и читают текущие объекты источника. Теперь страница содержит Cache-Control: max-age=900. Завершите следующие два шага в пределах этих пятнадцати минут.
Подготовленный файл release-two.html содержит изменённую страницу. Загрузите его под тем же ключом объекта, сохранив тип и срок. Через аутентифицированную CLI S3 прочитайте объект в origin-release.html, чтобы увидеть реальные байты:
cat release-two.html
aws s3api put-object \
--bucket labex-n03-content \
--key index.html \
--body release-two.html \
--content-type text/html \
--cache-control 'max-age=900'
aws s3api get-object \
--bucket labex-n03-content \
--key index.html origin-release.html
cat origin-release.html
Источник содержит Release two. Запросите тот же путь через распределение:
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
Ожидайте попадание с Release one. Загрузка успешна, но пользовательская копия ещё действительна согласно собственному TTL. Это отличается от ошибки доступа к источнику. Выполните проверку предыдущей копии.

Инвалидация только обновлённой страницы
На этом шаге сделайте новую версию доступной до истечения срока, не удаляя независимый объект из кэша.
Инвалидация удаляет соответствующие объекты из кэша распределения. Путь — запрашиваемый пользователем путь, начинающийся с /, а не имя бакета или локального файла. Используйте точно /index.html: /* без необходимости удалил бы и другой объект.
Создайте запрос. Сокращённый параметр CLI --paths задаёт пакет инвалидации, а запрос сохраняет его сгенерированный ID в переменную:
INVALIDATION_ID=$(aws cloudfront create-invalidation \
--distribution-id "$DIST_ID" \
--paths '/index.html' \
--query Invalidation.Id \
--output text)
aws cloudfront wait invalidation-completed \
--distribution-id "$DIST_ID" \
--id "$INVALIDATION_ID"
aws cloudfront get-invalidation \
--distribution-id "$DIST_ID" \
--id "$INVALIDATION_ID" \
--query 'Invalidation.{Status:Status,Paths:InvalidationBatch.Paths.Items}'
Подтвердите Completed и /index.html. Завершение пакета само по себе не доказывает, какие байты получил пользователь. Запросите страницу дважды для проверки нового содержимого и его повторного использования:
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/index.html"
Ожидайте промах с Release two, затем попадание с той же новой страницей. Первый запрос получает текущие байты источника, второй использует новую копию.
Запросите независимый объект и повторите анонимный прямой запрос к источнику:
curl --fail --include --noproxy '*' --resolve "${DIST_DOMAIN}:8082:127.0.0.1" "http://${DIST_DOMAIN}:8082/stable.txt"
curl --noproxy '*' --output /dev/null --write-out 'Anonymous origin: HTTP %{http_code}\n' http://127.0.0.1:5000/labex-n03-content/index.html
stable.txt должен по-прежнему давать попадание с исходным содержимым, без дополнительного чтения источника. Анонимный доступ S3 должен всё ещё возвращать HTTP 403. Инвалидация меняет кэш, а не разрешения источника. Выполните проверку точечного обновления.

Удаление только собственных ресурсов доставки
На этом шаге сначала отключите и удалите распределение, затем OAC и содержимое S3. Сохраните эталонный бакет.
CloudFront использует ETag как токен версии конфигурации. Получите текущую конфигурацию и ETag, не угадывайте токен.
Перед удалением распределения прочитайте его ID OAC. Переменная сохраняет точный контроль доступа, подлежащий удалению:
OAC_ID=$(aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query 'DistributionConfig.Origins.Items[0].OriginAccessControlId' \
--output text)
Теперь сохраните текущую конфигурацию и получите её ETag:
aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query DistributionConfig \
--output json > distribution-current.json
DIST_ETAG=$(aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query ETag \
--output text)
В текущей конфигурации лабораторной работы Enabled распределения — единственный одноимённый атрибут со значением true. Эта обычная замена через sed создаёт отключённую копию с сохранением настроек источника:
sed 's/"Enabled": true/"Enabled": false/' distribution-current.json > distribution-disabled.json
aws cloudfront update-distribution \
--id "$DIST_ID" \
--if-match "$DIST_ETAG" \
--distribution-config file://distribution-disabled.json
Дождитесь развёртывания отключённой конфигурации. Распространение изменений AWS может занять время; здесь не измеряется задержка глобального развёртывания:
aws cloudfront wait distribution-deployed \
--id "$DIST_ID"
Обновление меняет ETag. Прочитайте последний токен перед удалением отключённого распределения:
DIST_ETAG=$(aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query ETag \
--output text)
aws cloudfront delete-distribution \
--id "$DIST_ID" \
--if-match "$DIST_ETAG"
Удалите OAC с его собственным ETag. ID распределения и ID OAC обозначают разные ресурсы:
OAC_ETAG=$(aws cloudfront get-origin-access-control \
--id "$OAC_ID" \
--query ETag \
--output text)
aws cloudfront delete-origin-access-control \
--id "$OAC_ID" \
--if-match "$OAC_ETAG"
Удалите только два объекта лабораторной работы и их бакет:
aws s3api delete-object \
--bucket labex-n03-content \
--key index.html
aws s3api delete-object \
--bucket labex-n03-content \
--key stable.txt
aws s3api delete-bucket \
--bucket labex-n03-content
aws s3api list-buckets \
--query 'Buckets[].Name'
Эталонный бакет должен остаться, а бакет содержимого — отсутствовать. AWS View должен показывать отсутствие распределения содержимого и только эталонный объект. Выполните проверку очистки. Неудачный запрос API не доказывает удаление.

Резюме
Вы настроили границы TTL и Cache-Control, наблюдали попадания без дополнительных чтений источника и истечение короткого срока. Новая версия в источнике не заменяет ещё действительную пользовательскую копию. Инвалидация точного пути получила новую страницу, сохранив независимый кэш и закрытый источник. Затем вы удалили только свои ресурсы. При частых выпусках имена объектов с версиями также позволяют выбирать новое содержимое; здесь вы обновляли существующий путь.



