Один небольшой сервер, на котором уживаются сайт, личные инструменты, два телеграм-бота и VPN для семьи. Разбираю, что там внутри, как ходит трафик и что ломалось по дороге.
Влад Бурба · ИИнфра-Инженер · vladburba.ru
Это не витрина «смотрите, как у меня всё идеально». Наоборот: ниже честный разбор с ошибками, тупиковыми гипотезами и списком того, что до сих пор не сделано. Мне кажется, так полезнее — по вылизанным схемам не видно, как принимаются решения.
Железо
2 ядра
виртуальный сервер
Память
1.9 ГБ
занято около четверти
Диск
28 ГБ
занято 39 %
Система
Ubuntu
обновления автоматом
Сервисов
12
+ 3 контейнера
Портов наружу
3
было 7 неделю назад
Четыре разные истории на одной машине. Всё внешнее общение идёт через один-единственный веб-сервер — он привратник, больше наружу не смотрит никто.
Схема 1 — кто есть на сервере и как связан с внешним миром
Главное правило, на котором держится безопасность этой конструкции: наружу смотрит только привратник. Все остальные службы слушают внутренний адрес — тот, до которого нельзя достучаться из интернета в принципе, потому что у каждого компьютера в мире это он сам.
Схема 2 — периметр. Жёлтое доступно из интернета, зелёное — только изнутри
Неделю назад открытых входов было семь: остались следы от экспериментов, временные правила «на пару дней», служебные порты, включённые установщиками по умолчанию. Сейчас три, и каждый обоснован. Разница не в том, что стало безопаснее на бумаге, — просто меньше поверхностей, за которыми надо следить.
Сервер стоит в России и работает входной точкой. Семья подключается к нему, а он передаёт трафик дальше — на зарубежные серверы по платной подписке. Российские сайты при этом идут напрямую, без крюка, и не тормозят.
Схема 3 — путь трафика. Российское идёт коротким путём, заграничное — через выходы
Смысл такой схемы — в лимите устройств у подписки. Сервер занимает одно место на всех, а к нему цепляется сколько угодно домашних телефонов и ноутбуков.
Восемь «ячеек» — это восемь адресов зарубежных серверов, разбитых на группы: своя для семьи, своя для программ самого сервера, плюс запасной выход на случай, если основные лягут. Раньше выход был один, и когда провайдер удалил его адрес, всё встало разом. Теперь смерть одного адреса переживается автоматически.
Все имена ведут прямо на сервер. Привратник смотрит, какое имя запросили, и передаёт запрос нужной службе. Незнакомые имена обрываются молча.
Схема 4 — как разбирается входящий запрос
Самое полезное, что случилось за неделю, — не то, что построено, а то, что сломалось. Рассказываю целиком, включая мои неверные догадки.
Симптом. Несколько дней подряд мои личные страницы не открывались. Браузер бесконечно крутил загрузку. При этом главная страница сайта открывалась нормально, а VPN на телефоне качал на десять мегабит. Разберитесь, называется.
Первая версия — виновата тяжёлая библиотека. Страница подгружала три мегабайта постороннего кода. Убрали. Не помогло.
Вторая версия — виноват объём страницы. Разбили на части. Не помогло, но испортило картинки: браузеры не рисуют такие схемы, если подключать их отдельными файлами.
Перестали гадать и стали мерить. Запросили одну и ту же страницу с четырёх точек: мой компьютер, домашнее хранилище, сервер в другом дата-центре и точка за границей. Оказалось, что не открывается только у меня дома. Сервер и Cloudflare свою часть отрабатывали идеально.
Нашли странность. Обрыв всегда случался на одном и том же месте — 17 770 байт, с точностью до байта. Случайные помехи так себя не ведут: цифра бы плясала. Значит вмешивается что-то систематическое.
Решающий опыт. Обратились к одному и тому же адресу Cloudflare, поменяв только имя сайта в запросе. Чужое имя — 87 килобайт за треть секунды. Моё имя — обрыв на семнадцати. Один провод, один адрес, разница только в имени.
Причина. Когда браузер подключается к сайту, он вынужден назвать имя вслух — открытым текстом, до того как включится шифрование. Иначе никак: на одном адресе Cloudflare живут миллионы сайтов, и он должен понять, чей сертификат показывать. Это имя читает оборудование на маршруте.
А маршрут был такой: мой запрос уходил за границу, там его принимал Cloudflare, возвращался в Москву за данными, и вёз их обратно. Граница пересекалась четыре раза ради страницы, лежащей в тридцати километрах от меня.
Ирония. Cloudflare ставился как раз для того, чтобы обойти ограничения. А он сам создал тот трансграничный переход, на котором всё и ломалось. Внутри страны никаких ограничений не было — мы их себе организовали добровольно.
Решение. Убрали Cloudflare из пути: имена стали вести прямо на сервер. Маршрут сократился до «Москва → Москва». Страница, которая не открывалась вовсе, стала приходить за девять сотых секунды.
| через Cloudflare | напрямую | |
|---|---|---|
| главная страница | 0.28 сек | 0.03 сек |
| картинка-превью 36 КБ | не доходила | 0.04 сек |
| словарь 558 КБ | не открывался | 0.09 сек |
| пересечений границы | 4 | 0 |
Что я забрал из этой истории. Во-первых, диагноз надо ставить сразу с нескольких точек — иначе неделю чинишь не то. Во-вторых, Cloudflare не спасает от отбора по имени: имя обязано быть видно, иначе шифрование не начнётся. В-третьих, две мои первые версии были неверны, и хорошо, что не остановился на обходном решении — оно бы работало, а причина осталась бы непонятой.
После той аварии с VPN стало ясно: следить руками — плохая опора.
Теперь каждую ночь на сервере запускается скрипт. Он сверяет все восемь выходов со списком провайдера, проверяет каждый живым запросом и заменяет те, что отвалились. Если замена сама не отвечает — не трогает рабочий выход, а пишет мне. Если после замены что-то пошло не так — откатывается на резервную копию.
Сообщение приходит, только когда есть что сказать. Молчит — значит всё в порядке. За первую неделю четыре ночи подряд промолчал, а на пятую заменил два адреса, которые провайдер вывел из обращения, — я узнал об этом из отчёта утром.
Заодно он присылает сводку человеческим языком: не «upstream-de-1 недоступен», а «Германия, запасной выход: адрес перестал отвечать, заменил на рабочий». Разница кажется мелочью, ровно до момента, когда читаешь это спросонья.
Честный список. Он существует не для красоты: пока долг записан и виден, он рано или поздно закрывается. Когда не записан — живёт годами.
Самое неприятное в списке. Пропадёт диск — пропадёт всё разом: настройки, боты, сайт. Первое в очереди на исправление.
О том, что что-то упало, узнаю, когда сам этим пользуюсь. Именно так вышло с VPN — сервер молчал двое суток. Автопилот закрыл только VPN, остальное пока нет.
Чтобы задать потолок, надо знать, сколько сервис ест на самом деле. Я замерил сразу после запуска и увидел 35 МБ — поставил бы 128 и получил бы бота, которого система убивает через час. Спасло сомнение: замер снят на непрогретом процессе, до того как поднялись библиотеки, кэши и планировщик. Через несколько минут те же боты вышли на 150–175 МБ.
Потолок поставлен от установившегося значения с тройным запасом, а не от первого показания. Сайту хватило 64 МБ — он отдаёт готовые файлы и почти ничего не держит в памяти.
Здесь важнее не потолок, а вопрос «почему вообще накопилось три гигабайта». Задать предел — десять минут, но тогда журнал просто вытеснял бы сам себя, и в нужный момент история за нужный день оказалась бы стёртой.
Считаю записи по часам: VPN-панель пишет 1834 строки в час, ровно и непрерывно. У неё оказался включён отладочный уровень логирования — записывалось каждое соединение каждого устройства семьи. Такой режим включают на время разбора и забывают выключить; так было и здесь.
Я был уверен, что пароль отключён: правило записано в отдельный файл настроек ещё в мае, файл на месте, содержимое верное. Но сервер отвечал, что вход по паролю разрешён.
Причина в том, как читаются такие настройки. Файлы применяются по алфавиту, и
выигрывает первое встреченное значение параметра, а не последнее — в отличие
от привычной логики «позже переопределяет раньше». Мой файл назывался
99-…, а рядом лежал 50-…, созданный системой первичной
настройки на полтора часа позже моего. Он шёл раньше по алфавиту и молча побеждал.
Мало исправить — надо убедиться, что исправление держит удар. Я положил рядом подставной файл, снова разрешающий вход по паролю, и проверил: он больше ничего не перебивает. После этого удалил.
Нашлось при разборе: один из ботов три недели работал на старом коде. Выкатка была списком команд в инструкции; шаг обновления кода падал с правами доступа, а следующий за ним шаг сборки отрабатывал успешно и рапортовал об успехе. Снаружи выкатка выглядела удачной.
Второй случай того же рода нашёлся тут же: скрипт третьего сервиса содержал комментарий «основное хранилище — резервное», но отправки туда не делал. Резервная копия истории отстала на два коммита, и заметить это было неоткуда.
Теперь у всех сервисов выкатка называется и работает одинаково — одна команда, один порядок шагов. Сервисы разные, порядок один: в этом и смысл. Главное не в удобстве, а в том, что каждый шаг проверяется отдельно, и об успехе не сообщается, пока шаг не выполнился.
Перезагрузка с обновлением ядра (сервер работал 96 дней без неё), закрытие четырёх лишних портов, снос нерабочей схемы туннеля, удаление задачи, три месяца работавшей вхолостую, переезд бота в общую папку, уборка старых копий (было 16 снимков базы VPN-панели — остался один).
Двадцать лет я работаю с серверами и сетями, и за это время убедился: рассказ о том, как искали причину, полезнее любого списка технологий. Технологии у всех примерно одинаковые.
Здесь видно, как я работаю: сначала гипотеза, потом замер, при расхождении — отказ от гипотезы, а не подгонка фактов. И обходное решение только после того, как понята причина, а не вместо этого.
Если такой подход нужен вашей инфраструктуре — я открыт для найма и консалтинга.