Upstream (апстрим)

17 октября 2026 · ~15 мин чтения

концепция сеть архитектура nginx git

Upstream (апстрим)

Upstream — это «тот, кто выше по течению». Слово описывает направление
связи относительно точки, где ты стоишь: апстрим — источник данных,
запросов или кода, даунстрим — тот, кому ты сам передаёшь дальше. В сети
это сервер за прокси, в git — исходный репозиторий, в дистрибутивах Linux —
авторы программы, а не упаковщики.

История

Слово пришло из гидрологии: upstream — вверх по реке, к истоку,
downstream — вниз, к устью. Это буквальный перевод того, как течёт вода,
и именно поэтому метафора оказалась такой живучей: почти в любой инженерии
есть «поток» — данных, запросов, груза, ответственности.

Первыми её массово подхватили нефтяники. Разделение на upstream
(разведка и добыча), midstream (транспорт и хранение) и downstream
(переработка и продажа) закрепилось в отрасли примерно в середине XX века и
живо до сих пор — это стандартные термины в годовых отчётах нефтяных
компаний. Логика ровно речная: нефть течёт от скважины к бензоколонке, и
каждый участок знает, кто для него «выше по течению».

В телекоме слово прижилось в эпоху массового широкополосного доступа. В
ADSL и кабельных сетях конца 1990-х — 2000-х полоса была несимметричной:
downstream (к абоненту) в разы шире, чем upstream (от абонента). Так
и появились тарифы «100 на 10» — и слово «апстрим» стало синонимом
«исходящего канала». До сих пор в техподдержке провайдера «проверьте
апстрим» значит «проверьте, что у вас уходит».

В разработке ПО у слова два независимых гнезда, и их полезно различать.

Первое — сеть. Здесь апстрим означает сервер, к которому обращается
посредник. В nginx, который Игорь Сысоев выпустил в 2004 году, для этого
есть целый модуль ngx_http_upstream_module и директива upstream { ... }.
Через неё описывают группу бэкендов, между которыми nginx распределяет
запросы. В HAProxy (2001, Вилли Тарро) терминология похожая: server в
backend-секции — это тот самый апстрим. В Envoy, который Lyft открыла
в 2016 году, тоже говорят про кластеры и апстримы. И когда API-гейтвей
возвращает тебе 502 Bad Gateway, это буквально «апстрим ответил ерундой».

Второе — исходники. В мире дистрибутивов Linux (Debian с 1993 года и
дальше) «upstream» — это авторы самой программы, а «downstream» — упаковщики
дистрибутива, которые её собирают и патчат. «Отправить патч апстриму» значит
добиться, чтобы он попал в оригинальный проект, а не остался локальной
заплаткой дистрибутива. В git и на GitHub (2008) это же слово означает
репозиторий, от которого сделан форк: у тебя в origin лежит твоя копия, а
в upstream — оригинал.

Общий знаменатель один: апстрим — это не «начальник» и не «главный», а
источник
. Тот, от кого пришли данные, код или запрос, и кто для тебя
недоступен для прямого управления.

Что это такое

Апстрим — не сущность, а отношение. Один и тот же сервис может быть
апстримом для одного участка системы и даунстримом для другого. Мобильное
приложение для тебя — клиент, для своего API-шлюза — даунстрим, для базы
данных — вообще никто.

Поэтому корректная формулировка всегда выглядит так: «X — апстрим для Y».
Без «для кого» слово повисает в воздухе и начинается путаница, особенно на
разборах инцидентов, когда три команды одновременно кричат «это не мы, это
апстрим».

Чем апстрим отличается от похожих слов:

Upstream vs backend. Backend (бэкенд) — это роль: сервер приложения, а
не статика. Апстрим — это позиция в цепочке. nginx, который раздаёт
статические файлы, никакого апстрима не имеет вовсе. А вот nginx, который
проксирует запросы в приложение, — имеет, причём это приложение может само
быть чьим-то даунстримом.

Upstream vs origin. Origin (источник) — почти синоним в контексте CDN:
«origin server» — сервер, где лежат настоящие данные, а не кэш на периферии.
Origin — это апстрим для CDN, но не наоборот. Термин origin говорят про
данные, upstream — про направление связи.

Upstream vs provider. Provider (поставщик) — юридическая и коммерческая
рамка. У тебя может быть один апстрим по запросам (конкретный сервер) и
совсем другой provider по контракту (компания, которой этот сервер
принадлежит).

Upstream vs parent. «Parent» — про иерархию владения (родительский
процесс, родительский домен). Апстрим — про направление потока. Форк
называют «upstream», потому что оттуда приходят изменения, а не потому что
он «главнее».

Слово описывает край, а не узел

В любой схеме «клиент → CDN → балансировщик → гейтвей → сервис → база» каждый участок имеет свой апстрим. Поэтому фраза «апстрим упал» без уточнения бесполезна: упал чей-то апстрим, и надо понять чей. На практике это первое, что стоит выяснить при разборе ошибки.

Аналогии из жизни

Водопровод в квартире. Городская магистраль и стояк — апстрим, твой
смеситель — даунстрим, счётчик — посредник, который считает, но воду не
делает. Когда вода пропала, ты звонишь в управляющую компанию, а не
разбираешь кран. Где работает: отлично объясняет, почему посредник не
может «починить» отсутствие воды, а только сообщить о нём. Где ломается:
вода не может «вернуться с ошибкой». В системах апстрим способен ответить
500, 502, таймаутом, мусором в теле — и это принципиально другой класс
событий, чем «труба пустая».

Река и лодка. Плывёшь по течению: всё, что выше по реке, — апстрим.
Мельницу поставили наверху — у тебя воды меньше, хотя сама мельница к тебе
никак не обращается. Где работает: хорошо показывает, что апстрим влияет
на тебя, даже не зная о твоём существовании. Где ломается: у реки ровно
один исток, а у сервиса апстримов обычно несколько и они меняются
(балансировка, фейловер, канареечный релиз). Плюс в реке нельзя выбрать, к
какому истоку плыть, а nginx именно это и делает — раскидывает запросы по
группе апстримов.

Завод — оптовик — магазин. Магазин для оптовика даунстрим, оптовик для
завода даунстрим, завод — верхний апстрим. Где работает: объясняет, что
«выше по цепочке» не значит «виноват»: завод может выпускать нормальный
товар, а сломается логистика посередине. Где ломается: в бизнесе стороны
договариваются и могут поменять условия, а в сети апстрим ничего не
обсуждает — он просто отвечает или не отвечает. И бизнес-цепочка дискретна,
а в IT апстрим часто сам является посредником для ещё одного апстрима, и
цепочка уходит вглубь на пять-шесть уровней.

ДНК: upstream и downstream. В молекулярной биологии эти слова
используют буквально: участок «выше» гена по направлению, противоположному
транскрипции, называют upstream — там сидят промоторы и регуляторные
элементы, которые включают и выключают ген. Где работает: метафора
«управляющее воздействие приходит сверху» тут точна. Где ломается:
биологический upstream ничего не «отвечает» — он пассивно влияет. В
инженерии апстрим всегда участник диалога, у него есть состояние и он может
отказать.

Как это работает

Разберём три главных сценария, потому что «upstream» в них означает разное.

1. Апстрим как группа серверов за прокси (nginx, HAProxy, Envoy).

Клиент приходит на входной сервер. Тот по конфигу решает, куда отдать
запрос. Описание группы бэкендов — это и есть upstream.

upstream app_pool {
    least_conn;                     # кому меньше соединений — тому и запрос
    server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
    server 10.0.0.13:8080 backup;   # включается, когда живые кончились
    keepalive 32;                   # держать соединения к бэкендам открытыми
}

server {
    listen 443 ssl;
    location /api/ {
        proxy_pass http://app_pool;
        proxy_next_upstream error timeout http_502 http_503;
        proxy_connect_timeout 2s;
        proxy_read_timeout 30s;
    }
}

Что здесь происходит по шагам:

  1. Приходит запрос на location /api/.
  2. nginx выбирает апстрим из группы по алгоритму балансировки: по
    умолчанию round-robin (по кругу), least_conn — наименее загруженному,
    ip_hash — привязка клиента к одному серверу.
  3. Устанавливает соединение с выбранным апстримом (или берёт готовое из
    keepalive-пула).
  4. Ждёт ответ в пределах proxy_read_timeout. Не дождался — это таймаут, и
    nginx вернёт клиенту 504 Gateway Timeout.
  5. Апстрим ответил, но невалидно (оборвал соединение, прислал мусор вместо
    заголовков) — nginx вернёт 502 Bad Gateway.
  6. Если в proxy_next_upstream перечислены коды и ошибки, nginx попробует
    следующий сервер из группы. Это и есть «ретрай на уровне прокси».
  7. Сервер, у которого накопилось max_fails неудач за fail_timeout,
    временно выводится из ротации. В открытом nginx это пассивная проверка
    здоровья: он судит по реальным запросам, а не по отдельным пингам
    (активные проверки — только в коммерческой NGINX Plus).

Отсюда два вывода, которые обычно и нужны на практике. Первый: 502 и
504 — это ошибки про апстрим, а не про клиента
. 502 значит «апстрим
ответил неправильно», 504 — «апстрим не успел». Второй: у прокси есть
собственные таймауты, и они должны быть больше таймаутов приложения,
иначе прокси будет обрывать нормальную медленную работу.

Ретрай — не бесплатный

proxy_next_upstream по умолчанию повторяет запрос только при ошибках соединения и таймаутах. И по умолчанию nginx не повторяет неидемпотентные методы (POST, LOCK, PATCH) — для этого нужно явно добавить non_idempotent. Если включить его на запросе, который списывает деньги, получишь двойное списание. Идемпотентность (повторяемость без побочного эффекта) — обязательное условие для любых автоматических повторов.

2. Апстрим как «следующий по цепочке» в API.

Типичная современная цепочка: клиент → CDN → балансировщик → API-гейтвей →
сервис → его зависимости. Каждый участок для предыдущего — апстрим. Когда
гейтвей ловит ошибку от сервиса, он оформляет её как свой ответ, и в теле
часто появляется что-то вроде:

{
  "error": "upstream error",
  "type": "api_error",
  "code": "upstream_error",
  "upstream_status": 400
}

Читать это надо так: «запрос дошёл до нас, мы передали его дальше, и там его
отклонили с кодом 400». То есть гейтвей честно говорит: сломано не у меня.
upstream_status — код, который вернул следующий участок; он может не
совпадать с тем, что увидел клиент, потому что гейтвей вправе переупаковать
ошибку (например, спрятать внутренний 500 за аккуратным 503).

Полезная привычка: при разборе такого ответа смотреть не только на код, но
и на границу ответственности. Ошибки 4xx в апстриме почти всегда
означают «мы отправили не то» — неверные параметры, истёкший токен, слишком
большой запрос. Ошибки 5xx в апстриме — «там сломалось или перегружено».
Это разные люди, разные действия и разные сроки.

3. Апстрим как источник кода.

В git локальная ветка «отслеживает» удалённую:

git remote add upstream https://example.org/original.git
git fetch upstream
git branch --set-upstream-to=upstream/main main
git log --oneline main..upstream/main      # что нового пришло сверху

Смысл: у тебя есть свой форк (origin — твоя копия), а upstream — то, из
чего форк вырос. Ты тянешь оттуда изменения, а свои правки «отправляешь
апстриму» — то есть предлагаешь в оригинальный проект. В дистрибутивах
Linux та же логика: у пакета есть upstream-версия и downstream-патчи
дистрибутива, и хорошая практика — любой патч сначала предложить апстриму,
а локально держать только то, что не приняли.

Отдельная ветка той же идеи — зависимости. Когда библиотека лежит у
тебя скопированной (vendoring) или завендорена в форк, ты навсегда привязан
к вопросу «а что в апстриме?». Апстрим выпустил патч безопасности — ты
обязан его втянуть сам, никто тебе не позвонит.

4. И ещё два контекста, которые встречаются чаще, чем кажется.

В машинном обучении «upstream model» — это предобученная модель (например,
языковая модель или энкодер картинок), а «downstream task» — конкретная
задача, на которой её дообучают. Логика та же: сначала общий источник
знаний, потом узкое применение.

В потоковой обработке данных (Kafka и родственники) апстрим — это
продюсеры, даунстрим — консьюмеры. Тут важно, что апстрим и даунстрим
развязаны: продюсеру не нужно знать, кто читает его топик, и это
устойчивость, а не баг.

Где встречается в обычной жизни

Где встречается в IT и бизнесе

Кто пользуется

Альтернативы и конкуренты

Когда НЕ стоит использовать

Связанные понятия

Литература и источники

Где встретилось у меня

Вчера рабочий день был коротким и почти целиком состоял из одного и того
же: ежедневная генерация текста несколько раз подряд падала, и в логах
оставались ответы вида «API Error: 400 … upstream error … upstream rejected
the request». Именно эта формулировка и зацепила: ошибку возвращал не
клиент и не тот сервис, к которому обращался мой скрипт, а звено за ним —
гейтвей честно пересказывал чужой отказ и прикладывал upstream_status.
Полезное напоминание, что «400» в такой обёртке — это не «я отправил мусор»
и не «сервис умер», а третий, отдельный случай: запрос отвергли выше по
течению. Разбирать такое надо с конца цепочки, а не с начала.

Краткое резюме