1. 1
  2. 2
  3. 3
  4. 4

# теория · шаг 1 из 4

Где живёт конфиг

Коротко

Конфиг nginx - это один файл nginx.conf, в который директивой include подставляется текст остальных файлов.

  • include - буквально текстовая подстановка. Отсюда всё остальное: порядок вставки важен, а контекст директивы задаётся местом include, а не именем файла.
  • Маска conf.d/*.conf разворачивается в АЛФАВИТНОМ порядке.
  • Две традиции живут рядом: conf.d/ от самого nginx и sites-available + sites-enabled от Debian. Файл без симлинка не читается, и ошибки при этом нет никакой.
  • nginx -T печатает результат сборки целиком - с него начинается разбор любого чужого сервера.

Если nginx -T уже привычка - листай до «Теперь сам».

Куда смотреть на незнакомом сервере

Открываешь чужой сервер, надо за пять минут понять, какие сайты на нём живут. Первая мысль - открыть /etc/nginx/nginx.conf. Мысль правильная, но результата не даст: там будет два десятка строк и пара include.

Обычная раскладка выглядит так:

/etc/nginx/
├── nginx.conf              ← главный файл, всё начинается тут
├── conf.d/
│   └── *.conf              ← подключается целиком, обычно сюда и пишут
├── sites-available/        ← только в сборках Debian и Ubuntu
├── sites-enabled/          ← симлинки на активные файлы из sites-available
├── mime.types              ← соответствие расширений и Content-Type
└── snippets/               ← кусочки, которые включают в несколько мест

Файлов может быть пятнадцать, включений - три уровня, а где-то мог остаться забытый симлинк. Читать это глазами по одному файлу - худший из способов.

На что это похоже

include работает как цитата в документе, вставленная целиком.

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

Ровно так же ведёт себя include: строки подключаемого файла оказываются в том контексте, где стоит подстановка. Файл не «настраивает сайт» и не «настраивает сервер» - он настраивает то место, куда его вставили.

Механизм: include - это подстановка текста

Никакой магии в include нет. nginx читает nginx.conf сверху вниз, доходит до строки

/etc/nginx/nginx.confhttp
include /etc/nginx/conf.d/*.conf;

и вставляет содержимое найденных файлов прямо сюда. Дальше он продолжает читать получившийся текст так, будто всё было написано в одном файле.

Из этого следуют три вещи, которые и создают почти все сюрпризы:

Контекст задаётся местом вставки. include внутри http вставляет в http, внутри server - в server. Один и тот же файл-сниппет, подключённый в двух местах, окажется в двух разных контекстах.

Порядок важен. Маска *.conf разворачивается по алфавиту, и там, где nginx выбирает первое подходящее, выбор зависит от имён файлов.

Файл вне дерева не существует. Не подключён - значит его нет, и никакой ошибки не будет.

Правило

Один файл на один сайт, имя файла - имя домена.

/etc/nginx/conf.d/shop.local.conf
/etc/nginx/conf.d/admin.shop.local.conf

Через год, когда понадобится поправить один сайт, не придётся вычитывать шестисотстрочную портянку.

Любой разбор начинается с nginx -T. Заглавная T печатает собранный конфиг целиком, строчная -t только проверяет синтаксис.

Разбор: алфавит решает, кто ответит

На сервере два файла:

/etc/nginx/conf.d/01-shop.confhttp
server {
    listen 80;
    server_name shop.local;
    root /var/www/shop;
}
/etc/nginx/conf.d/zz-legacy.confhttp
server {
    listen 80;
    server_name old.local;
    root /var/www/legacy;
}

Приходит запрос с Host: unknown.example - имя не совпало ни с одним. Кто ответит?

Ответит первый по порядку блок на этом порту, то есть 01-shop.conf, потому что маска развернулась по алфавиту и он оказался выше. Никакого default_server тут нет, и правило «первый на порту» - это умолчание nginx.

Переименуй файлы - и на чужие запросы начнёт отвечать другой сайт. Это и есть причина, по которой имена файлов иногда начинают с чисел: не ради красоты, а чтобы порядок был явным.

Проверить, в каком порядке всё собралось, можно только одним способом:

$  команда
nginx -T | grep -n "server_name\|listen"
↳  выводnginx -T | grep -n "server_name|listen"
41:        listen 80;
42:        server_name shop.local;
57:        listen 80;
58:        server_name old.local;

Номера строк - это строки СОБРАННОГО конфига, и по ним сразу видно, кто идёт первым.

Разбор: где наивное правило подводит

Наивное правило звучит так: «файл лежит в каталоге конфигов - значит работает». Вот два случая, где оно неверно, и оба встречаются постоянно.

Файл в sites-available без симлинка. В сборках Debian сам каталог sites-available нигде не подключён; подключается sites-enabled, и туда кладут символические ссылки. Файл, положенный в sites-available и забытый, не читается вовсе - и nginx -t скажет syntax is ok, потому что синтаксис действительно в порядке, просто этого текста в конфиге нет.

$  команда
ls -l /etc/nginx/sites-enabled/
↳  выводls -l /etc/nginx/sites-enabled/
lrwxrwxrwx 1 root root 34 sep  2 21:03 shop -> /etc/nginx/sites-available/shop

Симлинк на удалённый файл. Обратная ситуация: файл из sites-available удалили, ссылка осталась. Тут nginx как раз ругнётся:

↳  выводnginx -t
nginx: [emerg] open() "/etc/nginx/sites-available/old" failed (2: No such file or directory)

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

$  команда
nginx -T | grep -c "server_name shop.local"

Ноль означает, что nginx твой блок не видит, каким бы правильным ни был файл.

Что ломается без этого

«Правлю конфиг, ничего не меняется». Файл вне дерева включений: лежит в sites-available без ссылки, или имя не подошло под маску *.conf (частая история с shop.conf.bak и shop.conf.save).

«После переименования файла сайт стал отвечать чужим содержимым». Изменился алфавитный порядок, а с ним - кто отвечает на неизвестные имена.

«Один и тот же сниппет ведёт себя по-разному в двух сайтах». Он подключён в разных контекстах, и директивы внутри действуют на разном уровне.

Зачем это в работе

Первое - приёмка чужого сервера. nginx -T | grep -n "server_name\|listen\| proxy_pass" за секунду даёт карту: какие сайты объявлены, на каких портах и куда что уходит. Это быстрее и надёжнее, чем читать пятнадцать файлов.

Второе - переписка с коллегой. К вопросу «почему не работает» всегда стоит прикладывать вывод nginx -T, а не содержимое одного файла: половина ответов находится именно в том, что в собранном конфиге этого файла нет.

Теперь сам

Коллега положил новый сайт в /etc/nginx/conf.d/shop.conf.new, сделал nginx -t (получил syntax is ok), сделал reload и не понимает, почему сайт не открывается. Что произошло и как это увидеть одной командой?

Ответ. Маска в nginx.conf - include /etc/nginx/conf.d/*.conf, а файл называется shop.conf.new и под неё не подходит. nginx его не читает, поэтому -t честно говорит, что синтаксис в порядке: он проверил конфиг, в котором этого текста нет. Видно это командой nginx -T | grep -c server_name - блока в собранном конфиге не окажется. Лечится переименованием в shop.conf. Тот же класс ошибки дают .bak, .save и .orig, оставленные рядом при правке.

Главное

Конфиг собирается из nginx.conf директивой include, а include - обычная подстановка текста. Отсюда три следствия: контекст задаётся местом вставки, маска разворачивается по алфавиту, а файл вне дерева не существует и ошибки не даёт. Что получилось на самом деле, показывает только nginx -T.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.