Индекс на дневникаDockup / бележка от практиката
Note / unexpected-egress-charges

Непредвидени такси за egress: откъде идват сметките за bandwidth

Непредвидените такси за egress обикновено се дължат на четири навика: сервиране на медия от приложението, липса на cache headers, твърде многословни API заявки и трафик между региони. Открийте източника и намалете сметката, без да сменяте доставчика.

Сметката пристига и е четири пъти по-висока от очакваното. Разходите за compute са приблизително колкото сте предвидили. Разликата идва от egress — байтовете, които напускат инфраструктурата ви — и има голяма вероятност досега изобщо да не сте мислили за тях.

Непредвидените такси за egress са най-честата изненада при таксуването на application hosting, а причината е структурна: нищо в development процеса ви не измерва байтовете. Лаптопът ви не ви таксува за тях. Staging средата няма потребители. Първият момент, в който bandwidth става видим, е когато получите фактурата — а дотогава вече сте внедрили навика, който я е причинил.

Ето откъде всъщност идва, подредено според това колко често се оказва причината.

1. Сервиране на медия от приложението

Това засенчва всички останали причини и почти винаги се случва случайно.

Добавяте видео в /public, за да демонстрирате нещо. То работи и затова остава там. Сега всяко зареждане на страница stream-ва този файл от вашия container. Едно видео от 40 MB на страница с хиляда посетители месечно означава 40 GB — от файл, който сте смятали за временен.

Същият модел важи за product images, PDF файлове, user uploads и font файлове. Когато ги добавяте, нито едно от тях не изглежда като инфраструктурно решение.

Решението не е сложно: static media трябва да бъде в object storage зад CDN, а не във файловата система на приложението. Приложението ви трябва да сервира HTML и JSON. В момента, в който започне да сервира hero image от 4 MB на всеки посетител, то върши работата на CDN, без да се възползва от икономиката на CDN.

2. Липса на cache headers

Ако отговорите ви нямат cache headers, всеки посетител изтегля всеки asset при всяко зареждане на страница. Повторните посетители го изтеглят отново. Crawler може да го изтегля десетки пъти на ден.

# Fingerprinted build assets never change — cache them for a year
Cache-Control: public, max-age=31536000, immutable

# HTML changes — revalidate but allow a short window
Cache-Control: public, max-age=0, must-revalidate

# Anything user-specific
Cache-Control: private, no-store

Това е промяна от един ред за всеки клас отговори, която редовно премахва по-голямата част от сметката за bandwidth, защото елиминираният трафик е чисто повторение.

3. API, които връщат повече данни, отколкото клиентът използва

List endpoint, който връща цели обекти, когато UI визуализира три полета, изпраща разликата при всяка заявка — завинаги. Това никога не се вижда в profiling-а, защото е бързо — просто е голямо.

Проверете две неща при най-натоварените си endpoint-и:

  • Включена ли е compression? Content-Encoding: gzip или br при JSON обикновено намалява размера със 70–80%. Това е един ред в middleware-а и често липсва.
  • Изпращате ли полета, които никой не чете? Сериализирането на целия row, защото ORM го прави лесно, е поведението по подразбиране в повечето codebase-и.

Polling клиентът влошава и двата проблема пропорционално на броя заявки, които изпраща всяка минута.

4. Трафик между региони и услуги

Ако приложението ви е в един регион, а базата данни — в друг, всеки резултат от заявка преминава през платена граница. Лесно е това да се случи случайно — първо създавате базата данни, избирате регион, без да се замислите, а седмици по-късно разполагате приложението на друго място.

Трафикът между собствените ви услуги също може да се таксува като egress, ако напусне private network-а и се върне през public hostname. Две услуги в един и същ workspace, които комуникират чрез public URL адресите си, плащат два пъти за разговор, който никога не е трябвало да напуска мрежата.

В Dockup тази конкретна грешка е трудна за допускане, защото услугите се обръщат една към друга чрез internal name. Една услуга достига main-db.internal:5432, а друга услуга — чрез своя <slug>.internal alias. Трафикът, който остава вътре в workspace network-а, никога не достига до public listener. Именно затова базата данни няма public hostname по подразбиране: няма как случайно да го използвате за маршрутизиране.

Как да го откриете, вместо да гадаете

Гадаенето коя от четирите причини е проблемът ви губи един billing cycle. Вместо това измервайте:

Прегледайте access log-а по bytes, а не по брой заявки. Подредете най-честите си отговори по общия брой сервирани bytes, а не по броя заявки. Endpoint-ът, който изпраща най-много данни, рядко е този, който се извиква най-често.

Проверете най-големите си static assets. Ако нещо над един мегабайт се сервира от приложението ви, това е първото нещо, което трябва да преместите.

# What is the service actually doing right now
dockup metrics my-project/my-api --json

# And what is it logging
dockup logs my-project/my-api -n 1000

Следете за crawler-и. Лошо конфигуриран bot, който многократно изтегля големи файлове, може да е причина за по-голямата част от месечния egress при сайт със сравнително малко човешки посетители. robots.txt и noindex за preview среди са евтини решения.

Средите, които никой не брои

Два навика генерират egress, който не присъства в ничий mental model:

Preview среди. Preview среда за всеки pull request е наистина полезна и незабелязано увеличава footprint-а ви. Пет отворени PR-а означават пет копия на stack-а ви, всяко със собствен трафик — често от същите crawler-и, защото preview URL адресите се индексират, ако не ги спрете.

Дълго живеещ staging. Staging среда, която работи от осем месеца и никой не посещава, продължава да изтегля images, да бъде обхождана и да изпълнява cron jobs.

Нито едно от тези неща не е причина да спрете да ги използвате. И двете обаче са причина да поставите граници: изтичайте preview средите, когато PR-ът бъде затворен, и ги маркирайте като noindex, за да спрете търсачките да ги изтеглят от ваше име.

dockup noindex my-project/my-api --on

Как изглежда доброто таксуване

Трябва да можете да отговорите на три въпроса, преди да получите фактурата:

  1. Какво работи в момента? Включително нещата, за които сте забравили.
  2. Какво е изразходвало всяко от тях? Разделено на compute и transfer, а не като една обща стойност.
  3. Какво се случва, ако потреблението се увеличи рязко? Лимит, който спира услугите, е неприятен. Липсата на лимит е още по-лоша.

Dockup измерва инфраструктурата отделно от плана, така че плановата такса не прикрива usage-а — а кредитът от $20 в Pro плана се приспада от този usage, вместо да представлява отстъпка от таксата. Важното е да виждате ясно коя част каква е.

Накратко

Преместете медията в object storage. Задайте cache headers. Включете compression. Дръжте услугите и базите им данни в един регион и им позволете да комуникират през private network, вместо през public internet. Изтривайте изтеклите preview среди и не ги допускайте до резултатите от търсачките.

Тези пет навика адресират почти всяка изненадваща сметка за bandwidth и нито един от тях не изисква да променяте мястото, където хоствате.

Често задавани въпроси

Какво е egress? Данни, които напускат инфраструктурата ви и се насочват към internet. Отговори към потребители, изтеглени файлове и API payload-и. Inbound трафикът обикновено е безплатен, а outbound — не.

Защо сметката ми за egress е по-висока от тази за compute? Почти винаги защото приложението сервира файлове, които трябва да са на CDN. Един голям asset на популярна страница може да надхвърли разходите за обработка на заявки за седмици.

Наистина ли CDN намалява разходите? Да, когато кешира. Origin-ът сервира файла веднъж към всеки edge, вместо веднъж към всеки посетител, а цената на CDN за гигабайт обикновено е част от цената на application platform.

Струват ли preview средите колкото production? Струват толкова, колкото консумират, което често е повече от очакваното, защото всяка е пълно копие, а preview URL адресите се обхождат от crawler-и, ако не предотвратите това.