Сообщество / DevOps

Обсуждение — VPS для API и backend: production-ready архитектура

Источник:
VPS для API и backend: REST, GraphQL, микросервисы

Backend API — сердце мобильных приложений, SPA и интеграций. VPS даёт предсказуемую latency, фиксированную цену и полный контроль над стеком — от Node/Python/Go до PostgreSQL и Redis. PaaS (Heroku, Render) быстрый старт, но дорожает с ростом RAM и connections. VPS — фиксированная цена и нет vendor lock-in на runtime.

Подробнее →
M
max_nn
Junior
Сообщения: 31
Репутация: 13
Дата регистрации:
09.09.2026
АВТОР ТЕМЫ04 нояб. 2026 г., 13:02 (GMT+3)
Дочитал до «Redis только 127.0.0.1:6379» и завис. У меня api и redis в одном compose, а в env было REDIS_HOST=127.0.0.1. Итог: api exited, потому что внутри контейнера localhost — это сам контейнер. Поменял на имя сервиса redis, поехало.

Как тогда переводить совет из статьи на docker? Вообще не писать ports у redis или всё-таки 127.0.0.1:6379:6379, чтобы с хоста лазить через redis-cli?
0
K
kostya_gw
Junior
Сообщения: 28
Репутация: 16
Дата регистрации:
20.09.2026
04 нояб. 2026 г., 19:13 (GMT+3)
Совет из статьи на docker переводится так: «redis не должен слушать ничего, что смотрит наружу». В compose у этого правила три уровня, и выбирать стоит по тому, кто на самом деле ходит в redis.

Схема словами:

1. Внутри compose: api → redis:6379 по имени сервиса через общую сеть проекта. Для этого ports вообще не нужен, контейнеры видят друг друга и без него. Ты это уже нащупал, когда поменял REDIS_HOST.
2. С хоста руками: ты → redis-cli. Тоже без ports:
docker compose exec redis redis-cli ping

Заходишь в сам контейнер, никакой порт на хосте не открыт.
3. С хоста программой: какой-нибудь скрипт по cron или экспортер метрик, который запущен не в docker и обязан стучаться в TCP. Только тут нужен ports, и только с адресом:
ports:
- "127.0.0.1:6379:6379"


Главная ловушка в варианте без адреса. Запись "6379:6379" публикует порт на всех интерфейсах, а docker сам дописывает свои правила iptables. Ufw их не видит: в ufw status висит «deny incoming», но пакет до контейнера доходит раньше, чем до правил ufw. Поэтому «у меня же фаервол» здесь не аргумент. Проверять стоит не конфиг, а то, что реально слушает:
ss -tlnp | grep 6379

Если видишь 0.0.0.0:6379 или [::]:6379 — порт открыт миру, что бы ни говорил ufw. Если 127.0.0.1:6379 — только локально. Если строки нет совсем — порт не опубликован, и это нормально для первых двух сценариев.

Теперь кусок жизни, чтобы было понятно, откуда у меня эта паранойя. Сейчас собираю шлюз на litellm: один endpoint, за ним несколько моделей, у каждого сервиса свой виртуальный ключ и свой лимит. Redis там держит кэш ответов и счётчики rate limit. Сижу сегодня вечером, проверяю, почему один ключ упирается в лимит раньше времени. Хочу посмотреть счётчики глазами и на автомате дописываю в compose "6379:6379", потому что лень набирать exec. Перезапускаю, смотрю в redis-cli, нахожу свою ошибку в конфиге лимитов. А потом ради порядка делаю ss -tlnp и вижу [::]:6379. На сервере, где в redis лежат кэшированные ответы моделей и, по сути, карта того, какой ключ сколько тратит. Пароля на нём не было, потому что «он же внутренний». Порт провисел минут двадцать. Вряд ли кто-то успел, но проверять «вряд ли» мне нечем, так что убрал строку и сделал выводы.

Отсюда то, что я теперь ставлю по умолчанию:

— у redis нет ports вовсе. Если очень надо с хоста, то только 127.0.0.1, и закомментировано, пока не понадобится;
— отдельная сеть для бэкенда с internal: true. Тогда у redis нет даже исходящего выхода наружу, а api подключён к двум сетям: внутренней к redis и обычной для внешних запросов. Получается api ↔ redis во внутренней сети, api ↔ мир через обычную, redis ↔ мир никак;
— requirepass всё равно задаю, даже если порт закрыт. Это не замена закрытому порту, а второй слой на случай, если кто-то однажды так же лениво допишет ports. Насколько помню, в официальном образе redis protected-mode выключен, так что на него как на защиту рассчитывать не стоит. Если ошибаюсь, поправьте, но лучше исходить из того, что его нет;
— пароль кладу в .env, который не попадает в git, а в api передаю REDIS_URL целиком.

Так что на твой вопрос «или-или» ответ такой: не писать ports — по умолчанию, 127.0.0.1:6379:6379 — когда есть конкретный клиент на хосте, который не умеет в exec. А голое 6379:6379 на сервере с публичным адресом не нужно никогда, даже «на пять минут посмотреть». Пять минут у меня сегодня как раз превратились в двадцать.
1
N
nastya_tula
Junior
Сообщения: 31
Репутация: 18
Дата регистрации:
02.09.2026
05 нояб. 2026 г., 10:03 (GMT+3)
Docker я не трогаю, в redis не разбираюсь, но фраза про то, что проверять надо не конфиг, а то, что реально слушает, прямо про меня. Бухгалтерия у нас ходит в 1С по RDP, и я долго жила с уверенностью: в панели написано «закрыто», значит закрыто. Мы привыкли так: есть галочка, значит есть защита. А из вашего объяснения выходит, что программа может сама открыть себе вход в обход этой галочки, и правила фаервола его даже не покажут.

Вопрос, наверное, наивный. Сервер у нас на Windows, не на Linux. Есть ли там что-то похожее на ss -tlnp, чтобы я сама, не дёргая админа, посмотрела, на каком адресе слушает 3389? И как читать результат: 0.0.0.0 означает «доступно всему интернету» или только «на всех сетевых картах самого сервера», а снаружи его ещё может закрывать отдельный фаервол?

Спрашиваю не из любопытства. В понедельник бухгалтер увидела где-то фразу «порт торчит наружу» и пришла ко мне. Я уверенно ответила, что у нас всё закрыто, а теперь не уверена, что имела право так отвечать.
2
M
mike_pg
Junior
Сообщения: 27
Репутация: 9
Дата регистрации:
15.09.2026
05 нояб. 2026 г., 17:01 (GMT+3)
На Windows аналог есть, и он только читает, ничего не меняет:
netstat -ano | findstr :3389

Строка LISTENING с 0.0.0.0:3389 значит, что RDP слушает на всех сетевых картах самого сервера. Это ещё не «весь интернет»: дальше стоят брандмауэр Windows и то, что перед сервером, например роутер или фаервол провайдера. У меня с postgres было так же: listen_addresses='*' пугало, а решали pg_hba и фаервол. Поэтому «закрыто» честно проверяется только снаружи, из чужой сети.
0
N
nastya_tula
Junior
Сообщения: 31
Репутация: 18
Дата регистрации:
02.09.2026
06 нояб. 2026 г., 11:03 (GMT+3)

mike_pg писал:
Поэтому «закрыто» честно проверяется только снаружи, из чужой сети.


Сегодня утром, пока бухгалтерия ещё не подключилась, открыла командную строку на сервере и ввела вашу команду. Вышли две строки LISTENING: 0.0.0.0:3389 и ещё одна с квадратными скобками, [::]:3389. Про первую теперь понятно, спасибо. Вторая, как я понимаю, то же самое, только для IPv6? Если да, то надо ли проверять её отдельно? Я привыкла, что адрес у нас один, тот, что записан у бухгалтера на бумажке.

Теперь про «из чужой сети». Я не айтишник, поэтому хочу уточнить, правильно ли поняла проверку. Беру свой телефон, выключаю Wi-Fi магазина, остаюсь на мобильном интернете и в приложении для удалённого рабочего стола ввожу внешний адрес сервера. Если появится окно ввода логина и пароля, значит, 3389 доступен из интернета. Если будет долго думать и выдаст ошибку подключения, значит, снаружи закрыто. Так это читается?

Бухгалтеру я пока сказала честно: «проверяю, отвечу завтра». Хочу в этот раз отвечать не по галочке в панели, а по тому, что увидела сама.
0
M
max_nn
Junior
Сообщения: 31
Репутация: 13
Дата регистрации:
09.09.2026
06 нояб. 2026 г., 13:03 (GMT+3)
про [::]:3389 да, насколько я понимаю, это то же самое, только для IPv6. и я бы проверил её отдельно. я на этом сам чуть не попался: у моего vps кроме обычного адреса есть ещё длинный IPv6, про который я вообще не помнил, пока не увидел его в панели. правила фаервола для v4 и v6 могут жить раздельно. у вас v6 может и не быть снаружи, но уточнить у админа одной строкой стоит: «есть ли у сервера публичный IPv6 и закрыт ли там 3389?»

с телефоном схема рабочая, я так же проверял свой vps после истории с redis. только две мелочи, на которых я сам споткнулся:

1. если появится окно логина, пароль не вводите. увидели окно, закрыли, этого достаточно для ответа.
2. «долго думает и ошибка» не всегда значит «у вас закрыто». мобильный оператор иногда сам режет какие-то порты, и тогда закрыто у него, а не у вас. так что ошибка — хороший знак, но не железный. окно логина зато однозначное)

и если у телефона мобильный интернет только по IPv4, то [::] он вообще не проверит. это ещё один повод спросить админа про v6, а не гадать.
0