Добавить в корзинуПозвонить
Найти в Дзене
Будни аналитика

Три часа против одной строки в Dockerfile

Хотел сделать простую вещь: задеплоить Telegram-бота на сервер. Бот рабочий, тесты проходят, код написан. Казалось — дело на полчаса. Не вышло. --- Сначала отпал Hetzner — они перестали принимать паспорта из России ещё после 2022-го. Пришлось искать альтернативу. Остановился на Railway: git push — и деплой. Это удобно, инфраструктуры минимум. Но Railway — это Docker. А в Docker у меня сразу возникло несколько проблем. Первая: Claude Code CLI, который используется внутри контейнера, отказывается работать под root. Документация по этому противоречивая — в одном месте написано одно, в другом другое. Начал итерировать: правишь Dockerfile, пушишь, ждёшь 5-10 минут пока Railway пересоберёт контейнер, смотришь логи, видишь ту же ошибку. Правишь снова. Через час я уже перепробовал несколько вариантов расстановки пользователей и прав. Контейнер запускался, но либо CLI падал, либо база данных создавалась под root и была readonly для bot user. Вторая проблема оказалась в entrypoint: он жёстко ст

Хотел сделать простую вещь: задеплоить Telegram-бота на сервер. Бот рабочий, тесты проходят, код написан. Казалось — дело на полчаса.

Не вышло.

---

Сначала отпал Hetzner — они перестали принимать паспорта из России ещё после 2022-го. Пришлось искать альтернативу. Остановился на Railway: git push — и деплой. Это удобно, инфраструктуры минимум.

Но Railway — это Docker. А в Docker у меня сразу возникло несколько проблем.

Первая: Claude Code CLI, который используется внутри контейнера, отказывается работать под root. Документация по этому противоречивая — в одном месте написано одно, в другом другое. Начал итерировать: правишь Dockerfile, пушишь, ждёшь 5-10 минут пока Railway пересоберёт контейнер, смотришь логи, видишь ту же ошибку. Правишь снова.

Через час я уже перепробовал несколько вариантов расстановки пользователей и прав. Контейнер запускался, но либо CLI падал, либо база данных создавалась под root и была readonly для bot user.

Вторая проблема оказалась в entrypoint: он жёстко стартовал uvicorn для обоих сервисов без возможности передать команду снаружи. То есть даже если хочешь запустить один сервис — поднимаются оба. Это было не очевидно, потому что в логах Railway всё выглядело «почти правильно».

После трёх часов я сделал то, что надо было сделать в начале.

---

Поднял Docker Desktop локально и начал воспроизводить там.

Итерация вместо 5-10 минут — 30 секунд. Сразу видишь вывод, сразу меняешь, сразу проверяешь. За следующие полчаса нашли и починили всё: universal entrypoint с поддержкой per-service command, gosu для запуска от непривилегированного пользователя, правильный UTF-8 локаль, база данных в правильном месте.

Потом это всё ушло на Railway и заработало с первого раза.

---

Вывод у меня один, и он не про Docker.

Когда часами буксуешь в медленном цикле — это не упорство, это потеря времени. Медленный цикл обратной связи (5-10 минут на итерацию) маскирует проблему: не понимаешь что именно сломано, потому что между попыткой и результатом слишком долго.

Переход на быстрый цикл — это не отказ от продакшна. Это диагностический инструмент. Воспроизведи проблему там где можешь итерировать быстро, реши её, потом перенеси решение.

Стек в итоге: Telegram bot + FastAPI + Docker + Railway + Claude Code CLI. Всё работает. Заняло день вместо полчаса, но теперь я понимаю каждую строку в Dockerfile.

Это тоже результат.