Двадцать три года я занимаюсь инфраструктурой — сначала руками, потом руководя теми, кто работает руками. И всё это время у меня есть привычка, которую я считаю профессионально необходимой: держать личный проект, где я сам отвечаю за всё, от выбора машины до текста на странице. Не чтобы что-то доказать, а чтобы не забыть, как это — когда ломается у тебя, и чинить некому.
Этот текст про такой проект. Он про сервис, который принимает данные от GPS-трекеров, и про то, что я в нём сломал, чего не заметил и что понял.
Предупрежу сразу: половина статьи — про ошибки. Причина простая: удачные решения у всех выглядят одинаково, а ошибки у каждого свои, и по ним о работе видно гораздо больше.
Коротко
- Сервис живёт на арендованной машине: 2 ядра, 4 ГиБ памяти, 50 ГБ диска. Ограничения оказались лучшим учителем, чем свобода.
- Самая дорогая авария не сломала ничего: сервер отвечал, мониторинг был зелёным, а данные не сохранялись. Обнаружил её я, а не система.
- Справочник на тысячу страниц принёс показы на 73 из них — и главный урок оказался не в SEO, а в том, что я измерял не то.
- Общий вывод, ради которого стоит читать: неверная проверка возвращает не пустоту, а уверенный неправильный ответ.
Что за сервис и зачем он
Человек покупает GPS-трекер — их продаётся много, от трёхсот рублей до нескольких тысяч, — ставит в машину и хочет видеть, где она и что с ней происходило.
Массовый сценарий на рынке устроен так: покупаешь прибор у компании, которая привязывает его к своему облаку. Уйти оттуда нельзя — прибор настроен на их адрес, история у них, тариф тоже их. Мне это не нравилось, поэтому ТочкаGPS работает наоборот: трекер остаётся вашим. Любой прибор, говорящий по одному из открытых протоколов, настраивается на мой адрес и начинает работать. Купленный где угодно и у кого угодно.
Бесплатный тариф при этом не демонстрационный, а рабочий — просто с коротким сроком хранения истории. Деньги я беру не за базовые функции, а за то, что требует постоянных вычислений: аналитику топлива, длинную историю, сложные правила уведомлений.
На чём это работает
Арендованная машина за тысячу рублей в месяц: 2 ядра, 4 ГиБ памяти, 50 ГБ диска, Ubuntu 24.04. Четыре контейнера — сайт, фоновый процесс, база и приёмник данных от трекеров. Перед ними веб-сервер, наружу открыты только HTTPS и порты протоколов.
Приём данных я не писал сам. Есть открытый сервер Traccar, который знает несколько сотен протоколов; написать это самостоятельно — значит потратить годы на разбор бинарных форматов приборов, которых ты в руках не держал. Я беру его как транспортный слой и делаю поверх всё остальное: тарифы, оплату, уведомления, карту, топливо.
Часть работы — разбор данных, проверка гипотез, написание текстов — шла у меня с ИИ-агентами. Держу нескольких, по одному на проект, и они, помимо прочего, перепроверяют друг друга. Про это можно было бы написать отдельно; здесь скажу только, что половина находок в этой статье появилась ровно из такой взаимной проверки — когда второй читатель смотрит не туда, куда смотрел первый.
Авария, которая ничего не ломала
Это самая дорогая история проекта, и начну я с неё.
30 августа сервер принял от одного трекера 709 пакетов подряд, подтвердил приём каждого и не сохранил ни одной позиции.
Посмотрите на эту фразу ещё раз. Транспорт работал. Пакеты приходили. Сервер отвечал на них подтверждением. И ничего не записывал.
Сайт отдавал 200. Устройство числилось «на связи». В журнале за всё время не появилось ни строки уровня WARN или ERROR. Мониторинг молчал, потому что всё, за чем он следил, было в порядке.
Заметил это я. Открыл карту и увидел, что машина стоит не там, где она стоит на самом деле.
Почему обновление не помогло бы
Первая мысль была очевидной и неверной: обновить Traccar — тот самый открытый сервер, который принимает данные от трекеров. Раз что-то не работает, значит версия старая.
Но на ветке Traccar, которой уже много лет, такого поведения не было вовсе. Значит это не застарелая болезнь, которую давно починили, а наоборот — поведение, появившееся в более новых версиях. Обновляться было бы движением в сторону от лечения. Откат на промежуточную версию фриз тоже воспроизвёл.
Вторая ложная тропа — настройка тайм-аута сессий, на которую в подобных случаях грешат все. Она действительно чинила похожий эффект, но более редкий, и к моему случаю отношения не имела.
Настоящая причина нашлась дампом потоков и разбором соединений: модем трекера за мобильным NAT периодически открывал новое TCP-соединение, не закрывая старое. Старые висели живыми и продолжали получать и подтверждать настоящий трафик — восемь одновременных соединений с одного адреса. Приёмник связывал устройство с последним представившимся соединением, а пакеты по остальным принимал, подтверждал и молча отбрасывал. Какие позиции долетят до базы, решал случай.
Починка оказалась короткой. Я добавил в Traccar одно правило: как только трекер объявляется на новом соединении, старое закрывается принудительно. Тогда живым остаётся ровно одно соединение — то, по которому устройство разговаривает сейчас, — и терять данные больше негде.
И важная деталь про проверку. Я не стал ограничиваться тем, что ошибка перестала воспроизводиться, — это слабое утверждение, отсутствие симптома за короткое окно. Вместо этого три раза за ночь с интервалом в три часа я смотрел на живые соединения и на одной из проверок увидел, как патч штатно закрывает старое соединение. Разница между «не повторилось» и «видел, как механизм сработал» — это разница между надеждой и знанием.
Что я понял про свой мониторинг
Графики следили за диском, памятью и свопом. Отдельная проверка следила за откликом сайта. Оба показывали норму — и были правы. Сервер был жив, сайт отвечал, устройство числилось на связи.
Просто ни одна проверка не отвечала на вопрос «а данные-то идут?».
Это не «мониторинг не настроили». Мониторинг был настроен, и настроен правильно для того, что он проверял. Он следил за жизнью, а не за работой. Разница между этими двумя вещами не видна, пока сервис отказывает, падая. Она видна только тогда, когда сервис отказывает, не падая.
Алерт именно на этот случай появился неделю спустя.
Как я чуть не убил машину одной настройкой
У приёмника каждый протокол сидит на своём порту, а какой протокол у конкретного трекера — часто выясняется только опытом. Поэтому я пробросил не «нужные» порты, а весь диапазон, около шестисот.
Docker на каждый опубликованный порт поднимает отдельный процесс-посредник. На каждый — и отдельно для TCP, и отдельно для UDP.
Процессов стало 1205. Доступная память рухнула с двух гигабайт до 367 мегабайт, своп забился, пришли два алерта подряд.
Вариантов было два: сузить диапазон обратно — быстро, но временно; или отключить посредников и отдать проброс правилам файрвола — дольше, но навсегда. Я выбрал второе.
Результат: 1205 процессов превратились в ноль, доступная память вернулась к двум гигабайтам, своп упал с 1,4 ГБ до 70 МБ. Сайт был недоступен пару секунд, трекер переподключился сам за двадцать.
Мне нравится этот сюжет тем, что настройка, о которой на большой машине вообще не думают, на маленькой решает, живёт сервис или нет. Ограничения — хороший учитель: они не дают отложить понимание того, как всё устроено внутри.
Диск съел не продукт
Ещё одна цифра из той же серии. Алерт «диск 85%». Виновник — кеш сборок: 31,67 ГБ на пятидесятигигабайтном диске. Для сравнения: все образы вместе весили 3,4 ГБ, все резервные копии — 5,2 МБ.
Причина скучная: в тот день я выкатывал около десяти правок подряд, каждая со сборкой образа. Чистка освободила 26,3 ГБ, диск ушёл с 85% на 33%, и я завёл еженедельную уборку.
Сейчас, через десять дней, кеш снова 11,15 ГБ. То есть мера работает, но скорость накопления такая, что без регулярной уборки диск кончился бы снова недели за три. По-моему, это честнее любого «оптимизировали»: не победил, а поставил на регулярную уборку и знаю цифру.
Справочник на тысячу страниц
У сервиса, которому месяц от роду, нет ни ссылок, ни имени. Но у меня было знание, которого нет у большинства: какой протокол у какой модели трекера, на каком порту он слушает, какими командами прибор перенастраивается на чужой сервер. Человек, купивший трекер на маркетплейсе, ищет ровно это — и обычно находит форумы десятилетней давности.
Отсюда замысел: страница на каждую модель. Получилось 1007 страниц моделей и 172 страницы протоколов.
Наполнял руками, не генератором. Команды выверялись по документации производителей, где она есть; там, где её нет, писалось честное «команд для этого прибора у нас нет, вот что можно сделать вместо». Отдельная работа — «однофамильцы»: восемь названий встречаются в каталоге дважды у разных производителей, и это разные приборы с разными протоколами.
Что из этого вышло
Цифры за две недели по 14 сентября 2026 года:
раздел адресов с показами показов кликов
модели 73 191 9
протоколы 20 39 0
справка 5 66 0
блог 2 51 2
Работают 73 страницы из 803, допущенных в поиск. Не «мало показов» — у остальных семисот их нет вовсе.
Я долго думал, что дело в объёме текста. Оказалось — нет. Главное измерение проекта такое: схожесть страниц внутри одной протокольной группы — 97,8% в среднем, при контрольных 33–39% между разными протоколами.
Причина видна в самом тексте страниц: материал писался по протоколу, а страницы заведены по модели. У протокола h02 пятьдесят пять моделей, у meiligao сорок пять. То есть полсотни адресов, отличающихся названием прибора и почти ничем больше. Поисковику не из чего выбирать — и он не выбирает.
Решение: 196 страниц, у которых нет ничего своего, закрыты от индексации и убраны из карты сайта. Они остались доступны людям — прямая ссылка и переход из каталога работают. И правило написано не списком, а условием: страница возвращается в индекс сама, как только у модели появляется своё. Список пришлось бы поддерживать; условие поддерживает себя само.
Яндекс снял с поиска три страницы
7 и 9 сентября поисковик убрал из выдачи главную, раздел справки и страницу тарифов с формулировкой «низкое качество». Не карточки моделей, которых тысяча, — а именно эти три.
Первая реакция была неверной: «раз снял, значит не дошёл, надо ждать переобхода». Проверка журналов сервера показала обратное — робот ходил: на справку семь визитов, на тарифы три, на главную пятьдесят три. Ждать было нечего.
Самое неудобное: страница тарифов — одна из лучших на сайте по тексту. Полторы тысячи слов, включая честный раздел «чего у нас нет». Длина и старательность не помогли.
Почему решение было таким, я не знаю. У меня есть предположение — что оценка идёт по разделу целиком и хаб получает оценку своих детей, — но доказать его я не могу, и выдавать за вывод не буду. Факт здесь только один: сообщение самого поисковика. Всё остальное — догадки, и им место в рабочих заметках, а не в статье.
Я четыре дня смотрел на число, которое означало не то, что я думал
У поисковика есть кабинет для владельца сайта, и в нём написано, сколько твоих страниц он держит в поиске. Я вывел это число к себе в админку и следил за ним — для сайта, который живёт справочником на тысячу страниц, это главный показатель.
Однажды оно упало с 746 до 47. За восемьдесят одну минуту, в шестнадцать раз. Дальше четыре дня стояло ровно на 47.
Такого не бывает. Чтобы семьсот страниц покинули поиск, робот должен был сначала их обойти и переоценить — на это уходят недели, а не полтора часа. Значит, одно из двух чисел говорит не о том, о чём я думаю.
Проверка заняла две команды. Я попросил у кабинета сто записей — пришло 47, и список на этом кончился. Попросил то, что идёт после сорок седьмой, — пришла пустота.
То есть 47 — это длина списка, который кабинет готов показать. Не число страниц в поиске. Показатель всё это время отвечал на другой вопрос, а назывался так, будто отвечает на мой.
Дальше стало хуже. Я спросил у того же кабинета то же самое разными способами, в течение одной минуты, и получил пять разных ответов: выборка сказала 47, сводка — 2, история — 1 и 2, а тремя днями раньше та же выборка говорила 746. Размера индекса этот сервис просто не сообщает — ни одним из способов.
Чинить тут нечего, и выбирать «правильное» число бессмысленно. Я сделал две вещи. Переименовал показатель в то, чем он на самом деле является. И поставил рядом другой — сколько моих адресов люди реально видели в выдаче за две недели. Это уже не оценка: каждый адрес попал в список потому, что его показали живому человеку.
Одна строка держала весь справочник в динамике
Весь сайт отдавался с заголовком, запрещающим кеширование где бы то ни было. Причина нашлась в одной строке: каждая публичная страница читала куку сессии, чтобы показать в шапке «Открыть карту» вместо «Войти». Чтение куки делает страницу динамической.
Восемьсот справочных страниц, одинаковых для всех посетителей, пересобирались на каждый заход робота — ради одного слова в шапке.
Починка оказалась не технической, а постановочной: ссылка стала одна для всех, а «кто ты» выясняется там, где ответ действительно нужен. После правки 1007 страниц моделей и 172 протокола предрисовываются при сборке. Замер: статическая страница 4–5 мс, динамическая 30–36 мс.
И побочный сюжет, который я люблю больше самой починки. Первый замер снаружи показал ухудшение: было 0,114 секунды, стало 0,151. Я почти откатил правку. Спас локальный замер: сервер отвечает за 5 мс, остальные 145 — это канал до сервера. Внешний замер отвечал на вопрос «какой у нас канал», а выглядел как ответ на вопрос «как быстро работает приложение».
Что дало результат
Не справочник. Блог.
Двенадцать статей, выходят по расписанию. Две опубликованные дали 51 показ и 2 клика за две недели — на страницу это больше, чем у любого другого раздела. Для сравнения: двадцать страниц протоколов дали 39 показов и ни одного клика.
Одна статья — разбор того, как отличить слив топлива от расхода, — попала в ИИ-ответ поисковика как источник. И интересно что именно оттуда взяли. Не факт и не цифру, а вот это:
Повторите наблюдение. Одно событие — это гипотеза. Повторяющееся событие на одной и той же стоянке в одно и то же время — это уже картина.
Все остальные источники в том ответе перечисляют признаки слива: датчики, шину, следы на горловине. Эта фактура у всех одинаковая и взаимозаменяемая. А суждение о том, что считать доказательством, написал только я — процитировать его было больше негде.
Отсюда вывод, который я считаю самым практичным за всю историю проекта: цитируют не за факты, а за фразу, которой нет ни у кого. У меня такие получаются из осторожности в выводах — где догадка, где вывод, чего я не знаю.
Ещё один замер из этой серии. Я отправил адрес новой статьи в момент публикации по протоколу быстрого оповещения и больше не трогал ничего. Робот пришёл через 34 секунды. Ручной заказ переобхода даёт 24 секунды. Разницы между способами практически нет — и обе цифры несопоставимы с «двое суток», в которые я верил до замера.
Главный урок
Если одной фразой: неверная проверка возвращает не пустоту, а уверенный неправильный ответ.
Соберу вместе всё, что было выше.
Команда на хосте показала ноль соединений — я чуть не решил, что трекер отвалился и чинить нечего. Соединения жили в сетевом пространстве контейнера, куда с хоста не видно; изнутри их было восемь. Разница между «0» и «8» — это разница между «нечего чинить» и найденной причиной.
Я объявил, что резервных копий нет. Они были и работали — просто сделаны не привычным способом, и в списке заданий нужное оказалось ниже того места, где я обрезал вывод.
Внешний замер скорости мерил канал, а выглядел как замер приложения.
Счётчик показывал длину выборки, а назывался размером индекса.
Один из картографических источников на запрос без ключа отдаёт не ошибку, а картинку-заглушку с кодом 200. Формально — работает.
Поиск по журналам находил ноль заходов робота, когда их было больше тысячи: имя робота стояло в середине строки, а шаблон требовал начала.
Ни одно из этих измерений не сообщило о своей неприменимости. Каждое выдало правдоподобный результат, на котором расследование могло закончиться — и пару раз почти закончилось.
Практическое правило, которое я из этого вынес и которым пользуюсь теперь везде: к любой проверке — второй вопрос, «а могла ли она сказать нет?». И отвечать на него не рассуждением, а прогоном: сломать нарочно то, что она обязана поймать. Если сломать нечем или непонятно чем — она не проверяет, а подтверждает.
Два следствия помельче, но тоже рабочих.
Временная мера, которая работает в большинстве случаев, прекращает поиск причины. У меня сайт иногда отдавал ошибку при выкате, и напрашивалось поднять тайм-аут ожидания вдвое. Сработало бы. И на этом расследование закончилось бы, потому что исчез бы симптом, из-за которого искали. Причина была не в числе: порт приложения держит сам Docker, поэтому соединение принимается мгновенно после старта контейнера, и прокси не мог отличить «поднимается» от «работает».
Правила живут в коде, а не в памяти людей. Порог, по которому страница возвращается в индекс. Список страниц, которым позволено трогать конфигурацию. Причина, по которой у одного числа две даты. Всё это записано там, где его прочтёт следующий, — вместе с числами и поводом. Договорённость живёт до первого нового участника; тест и комментарий переживают всех.
Зачем я это пишу
Не ради рекламы сервиса — он маленький и вырастет ещё нескоро.
Скорее вот зачем. За двадцать три года в инфраструктуре я видел много систем, где всё зелёное, и мало систем, где кто-то может ответить, что именно проверяет каждая галочка. Личный проект на машине за тысячу рублей оказался неожиданно хорошим способом это себе напомнить: здесь некому сказать «мониторинг же зелёный», потому что мониторинг настроил ты сам, и ты же знаешь, чего он не видит.
Если из статьи стоит унести одно — пусть это будет привычка спрашивать у каждой своей проверки: а могла ли ты сказать «нет»?