Вначале был сервер. И на этом сервере жил сайт, допустим, example.com.
Шло время, сайт рос, одного сервера ему стало мало, да ещё и ребрендинга захотелось. И оставил сисадмин его бэкендом, и создал для него фронтенд, допустим, example2.com.
Так и появилась типичная схема на базе веб-сервера Nginx с фронтендом example2.com и бэкендом example.com, и в /etc/hostname на этих серверах были указаны именно эти данные. Для example.com также осталась прежняя ресурсная А-запись в настройках ДНС.
Сервера стабильно работали, uptime рос, а сайт всё дальше развивался и владельцы решили провести ещё один ребрендинг и сменить имя (например) на example2.wtf. Готовясь к изменениям, зашел сисадмин на фронтенд-сервер и сменил в /etc/hostname строку example2.com на example2.wtf, но, правда, дело дальше не пошло и зону сайта решили оставить прежней, соответственно, конфиги Nginx не менять. Но и в /etc/hostname прежнюю запись обратно не вернули.
Ну не может же эта незначительная правка убить Nginx!
Или всё таки может?
Здесь и далее:
Uptime рос дальше, сервера работали всё также безотказно, но случился день когда то ли понадобилось перезагрузить сервер, то ли пропало питание, в общем не важно. Суть в том, что конфиги Nginx ни до этого, ни во время никто не менял, однако:
«Рабочие» конфиги Nginx приведены ниже:
Я намеренно взял слово «рабочие» в кавычки. Эти конфиги содержат ошибку, заключающуюся всего лишь в одной букве s:
Одна из причин приведших к этой проблеме заключается в том, что Nginx не считает ошибкой некорректные пути, если они содержат «звездочку», хотя она и находится вне однозначно определённого каталога, который задан неверно. И, как можно заметить, Nginx знает о проблеме:
Так как дальше будет много выкладок strace, мы максимально «облегчим» Nginx, собрав его таким образом:
Также добавим пару строк в исходники, для вывода имени proxy_pass и значения определенного Nginx'ом в качестве upstream-сервера.
Здесь ещё важны настройки ресолвинга, предположим, что они имели такой вид:
Теперь с нашими правками, корректная проверка конфига будет выглядеть следующим образом:
И эта проверка по прежнему проходит с ошибочным конфигом Nginx и запросы при этом корректно проксируются на целевой бэкенд-сервер 93.184.216.34:80 указанный в upstream.
Как же это происходит:
Ссылка на картинку с описанными шагами:
hsto.org/webt/fp/fe/jp/fpfejpnv6i3kum27g4viqsa3d4m.png
1) Из /etc/resolv.conf получаются данные о сервере;
2) В нашем случае это 8.8.8.8;
3) Узнаем hostname сервера — example2.com;
4,5) Пытаемся отресолвить имя example.com на 8.8.8.8;
6) Получаем в ответ 93.184.216.34 для имени example.com
Однако, глядя на доменные имена в шаге 3 и 5 по прежнему ничего не понятно. Если наш hostname именуется example2.com почему Nginx ресолвит некий example.com?
Вернёмся к моменту, когда мы убили сервер изменив его hostname:
В данном случае нам не удалось отресолвить доменное имя example.wtf, которое также не понятно откуда возникло.
Ещё одна попытка:
Теперь наш hostname habr.ru, а Nginx не запускается, так как не может разрешить имя example.ru.
Ну и на закуску:
С указанным hostname www.yandex.com Nginx успешно стартует, но ресолвит доменное имя example.yandex.com и соответственно проксирует трафик на 213.180.204.242.
Итого: если каталог с апстримами указан неверно (или не указан совсем) Nginx ресолвит доменное имя согласно правилам описанным в /etc/resolve.conf (учтены будут не только директивы nameserver, но и, например, search), а само доменное имя формирует путём замены части имени hostname (низшего уровня) на имя указанное в директиве proxy_pass.
Пример:
Если
hostname задан как uno.duo.tres.yandex.ru,
proxy_pass как
то ресолвить Nginx будет example.duo.tres.yandex.ru
Тут хотелось написать что-то язвительное, но к тому моменту, когда я решил этот ребус, не осталось никаких сил.
В общем, пишите конфиги без ошибок, ибо кривые админские руки способны поломать даже такой хороший продукт как Nginx.
Шло время, сайт рос, одного сервера ему стало мало, да ещё и ребрендинга захотелось. И оставил сисадмин его бэкендом, и создал для него фронтенд, допустим, example2.com.
Так и появилась типичная схема на базе веб-сервера Nginx с фронтендом example2.com и бэкендом example.com, и в /etc/hostname на этих серверах были указаны именно эти данные. Для example.com также осталась прежняя ресурсная А-запись в настройках ДНС.
Сервера стабильно работали, uptime рос, а сайт всё дальше развивался и владельцы решили провести ещё один ребрендинг и сменить имя (например) на example2.wtf. Готовясь к изменениям, зашел сисадмин на фронтенд-сервер и сменил в /etc/hostname строку example2.com на example2.wtf, но, правда, дело дальше не пошло и зону сайта решили оставить прежней, соответственно, конфиги Nginx не менять. Но и в /etc/hostname прежнюю запись обратно не вернули.
Ну не может же эта незначительная правка убить Nginx!
Или всё таки может?
Здесь и далее:
$ head -1 /etc/*release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
$ nginx -v
nginx version: nginx/1.19.2
Uptime рос дальше, сервера работали всё также безотказно, но случился день когда то ли понадобилось перезагрузить сервер, то ли пропало питание, в общем не важно. Суть в том, что конфиги Nginx ни до этого, ни во время никто не менял, однако:
$ nginx -t
nginx: [emerg] host not found in upstream "example" in /nginx/vhosts/example2.com:9
nginx: configuration file /etc/nginx/nginx.conf test failed
«Рабочие» конфиги Nginx приведены ниже:
$ cat /etc/nginx/nginx.conf
user www-data;
worker_processes 1;
error_log /var/log/nginx/error.log;
events {
}
http {
log_format domainlog '$scheme\t$server_name\t$host\t$upstream_addr\t$status\t$upstream_status';
include mime.types;
default_type application/octet-stream;
include /nginx/upstream/*;
include /nginx/vhosts/*;
}
$ cat /nginx/vhosts/example2.com
server {
listen 80;
server_name example2.com;
access_log /var/log/nginx/access/example2.com domainlog;
location / {
proxy_pass http://example;
proxy_set_header Host $host;
}
}
$ cat /nginx/upstreams/example2.com
upstream example {
server 93.184.216.34:80;
}
Я намеренно взял слово «рабочие» в кавычки. Эти конфиги содержат ошибку, заключающуюся всего лишь в одной букве s:
Одна из причин приведших к этой проблеме заключается в том, что Nginx не считает ошибкой некорректные пути, если они содержат «звездочку», хотя она и находится вне однозначно определённого каталога, который задан неверно. И, как можно заметить, Nginx знает о проблеме:
$ strace nginx -t 2>&1 | grep -F "ENOENT"
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/nginx/upstream", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = -1 ENOENT (No such file or directory)
connect(6, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
Так как дальше будет много выкладок strace, мы максимально «облегчим» Nginx, собрав его таким образом:
$ nginx -V
nginx version: nginx/1.19.2
built by gcc 8.3.0 (Debian 8.3.0-6)
configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --without-http_charset_module --without-http_gzip_module --without-http_ssi_module --without-http_userid_module --without-http_access_module --without-http_auth_basic_module --without-http_mirror_module --without-http_autoindex_module --without-http_geo_module --without-http_map_module --without-http_split_clients_module --without-http_referer_module --without-http_rewrite_module --without-http_fastcgi_module --without-http_uwsgi_module --without-http_scgi_module --without-http_grpc_module --without-http_memcached_module --without-http_limit_conn_module --without-http_limit_req_module --without-http_empty_gif_module --without-http_browser_module --without-http_upstream_hash_module --without-http_upstream_ip_hash_module --without-http_upstream_least_conn_module --without-http_upstream_keepalive_module --without-http_upstream_zone_module --without-poll_module
Также добавим пару строк в исходники, для вывода имени proxy_pass и значения определенного Nginx'ом в качестве upstream-сервера.
$ diff /scripts/nginx/nginx-1.19.2/src/http/ngx_http_upstream_round_robin.c /scripts/nginx/nginx-1.19.2.0/src/http/ngx_http_upstream_round_robin.c
232a233
> ngx_log_stderr(0, "%V\n",&peer[i].name);
238a240
> ngx_log_stderr(0, "upstream \"%V\" in %s:%ui", &us->host, us->file_name, us->line);
Здесь ещё важны настройки ресолвинга, предположим, что они имели такой вид:
$ cat /etc/resolv.conf
nameserver 8.8.8.8
Теперь с нашими правками, корректная проверка конфига будет выглядеть следующим образом:
$ hostname example2.com
$ nginx -t
nginx: 93.184.216.34:80
nginx: [2606:2800:220:1:248:1893:25c8:1946]:80
nginx: upstream "example" in /nginx/vhosts/example2.com:9
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
И эта проверка по прежнему проходит с ошибочным конфигом Nginx и запросы при этом корректно проксируются на целевой бэкенд-сервер 93.184.216.34:80 указанный в upstream.
Как же это происходит:
$ strace nginx -t 2>&1 | grep -vE "(mmap|munmap|mprotect|poll|stat|ioctl|write|close|getsockname)" | grep -vE "^\)" | grep . | grep -FA 24 "/etc/resolv.conf"
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
read(5, "nameserver 8.8.8.8\n", 4096) = 19
read(5, "", 4096) = 0
uname({sysname="Linux", nodename="example2.com", ...}) = 0
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
lseek(5, 0, SEEK_CUR) = 0
read(5, "127.0.0.1\tlocalhost\n::1\t\tlocalho"..., 4096) = 129
lseek(5, 0, SEEK_CUR) = 129
read(5, "", 4096) = 0
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\21\0\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240C\0\0\0\0\0\0"..., 832) = 832
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendmmsg(5, [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\20\353\1\0\0\1\0\0\0\0\0\0\7example\3com\0\0\1\0\1", iov_len=29}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=29}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\330\370\1\0\0\1\0\0\0\0\0\0\7example\3com\0\0\34\0\1", iov_len=29}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=29}], 2, MSG_NOSIGNAL) = 2
recvfrom(5, "\20\353\201\200\0\1\0\1\0\0\0\0\7example\3com\0\0\1\0\1\300\f\0"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 45
recvfrom(5, "\330\370\201\200\0\1\0\1\0\0\0\0\7example\3com\0\0\34\0\1\300\f\0"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 57
openat(AT_FDCWD, "/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 5
read(5, "# Configuration for getaddrinfo("..., 4096) = 2584
read(5, "", 4096) = 0
futex(0x7efdc41f2044, FUTEX_WAKE_PRIVATE, 2147483647) = 0
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("93.184.216.34")}, 16) = 0
Ссылка на картинку с описанными шагами:
hsto.org/webt/fp/fe/jp/fpfejpnv6i3kum27g4viqsa3d4m.png
1) Из /etc/resolv.conf получаются данные о сервере;
2) В нашем случае это 8.8.8.8;
3) Узнаем hostname сервера — example2.com;
4,5) Пытаемся отресолвить имя example.com на 8.8.8.8;
6) Получаем в ответ 93.184.216.34 для имени example.com
Однако, глядя на доменные имена в шаге 3 и 5 по прежнему ничего не понятно. Если наш hostname именуется example2.com почему Nginx ресолвит некий example.com?
Вернёмся к моменту, когда мы убили сервер изменив его hostname:
$ hostname example2.wtf
$ strace nginx -t 2>&1 | grep -vE "(mmap|munmap|mprotect|poll|stat|ioctl|write|close|getsockname)" | grep -vE "^\)" | grep . | grep -FA 24 "/etc/resolv.conf"
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
read(5, "nameserver 8.8.8.8\n", 4096) = 19
read(5, "", 4096) = 0
uname({sysname="Linux", nodename="example2.wtf", ...}) = 0
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
lseek(5, 0, SEEK_CUR) = 0
read(5, "127.0.0.1\tlocalhost\n::1\t\tlocalho"..., 4096) = 129
lseek(5, 0, SEEK_CUR) = 129
read(5, "", 4096) = 0
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\21\0\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240C\0\0\0\0\0\0"..., 832) = 832
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendmmsg(5, [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="F\23\1\0\0\1\0\0\0\0\0\0\7example\3wtf\0\0\1\0\1", iov_len=29}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=29}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="{#\1\0\0\1\0\0\0\0\0\0\7example\3wtf\0\0\34\0\1", iov_len=29}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=29}], 2, MSG_NOSIGNAL) = 2
recvfrom(5, "F\23\201\203\0\1\0\0\0\1\0\0\7example\3wtf\0\0\1\0\1\300\24\0"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 111
recvfrom(5, "{#\201\203\0\1\0\0\0\1\0\0\7example\3wtf\0\0\34\0\1\300\24\0"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 111
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendmmsg(5, [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="V2\1\0\0\1\0\0\0\0\0\0\7example\0\0\1\0\1", iov_len=25}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=25}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="3H\1\0\0\1\0\0\0\0\0\0\7example\0\0\34\0\1", iov_len=25}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=25}], 2, MSG_NOSIGNAL) = 2
recvfrom(5, "V2\201\203\0\1\0\0\0\1\0\0\7example\0\0\1\0\1\0\0\6\0\1\0\1"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 100
recvfrom(5, "3H\201\203\0\1\0\0\0\1\0\0\7example\0\0\34\0\1\0\0\6\0\1\0\1"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 100
brk(0x55ddf1d34000) = 0x55ddf1d34000
В данном случае нам не удалось отресолвить доменное имя example.wtf, которое также не понятно откуда возникло.
Ещё одна попытка:
$ hostname habr.ru
$ nginx -t
nginx: [emerg] host not found in upstream "example" in /nginx/vhosts/example2.com:9
nginx: configuration file /etc/nginx/nginx.conf test failed
$ strace nginx -t 2>&1 | grep -vE "(mmap|munmap|mprotect|poll|stat|ioctl|write|close|getsockname)" | grep -vE "^\)" | grep . | grep -FA 24 "/etc/resolv.conf"
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
read(5, "nameserver 8.8.8.8\n", 4096) = 19
read(5, "", 4096) = 0
uname({sysname="Linux", nodename="habr.ru", ...}) = 0
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
lseek(5, 0, SEEK_CUR) = 0
read(5, "127.0.0.1\tlocalhost\n::1\t\tlocalho"..., 4096) = 129
lseek(5, 0, SEEK_CUR) = 129
read(5, "", 4096) = 0
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\21\0\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240C\0\0\0\0\0\0"..., 832) = 832
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendmmsg(5, [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\302\300\1\0\0\1\0\0\0\0\0\0\7example\2ru\0\0\1\0\1", iov_len=28}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=28}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\345\315\1\0\0\1\0\0\0\0\0\0\7example\2ru\0\0\34\0\1", iov_len=28}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=28}], 2, MSG_NOSIGNAL) = 2
recvfrom(5, "\302\300\201\203\0\1\0\0\0\1\0\0\7example\2ru\0\0\1\0\1\300\24\0\6"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 89
recvfrom(5, "\345\315\201\203\0\1\0\0\0\1\0\0\7example\2ru\0\0\34\0\1\300\24\0\6"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 89
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendmmsg(5, [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="N6\1\0\0\1\0\0\0\0\0\0\7example\0\0\1\0\1", iov_len=25}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=25}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\216P\1\0\0\1\0\0\0\0\0\0\7example\0\0\34\0\1", iov_len=25}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=25}], 2, MSG_NOSIGNAL) = 2
recvfrom(5, "N6\201\203\0\1\0\0\0\1\0\0\7example\0\0\1\0\1\0\0\6\0\1\0\1"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 100
recvfrom(5, "\216P\201\203\0\1\0\0\0\1\0\0\7example\0\0\34\0\1\0\0\6\0\1\0\1"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 100
brk(0x55d9815a6000) = 0x55d9815a6000
Теперь наш hostname habr.ru, а Nginx не запускается, так как не может разрешить имя example.ru.
Ну и на закуску:
$ hostname www.yandex.com
$ nginx -t
nginx: 213.180.204.242:80
nginx: [2a02:6b8::242]:80
nginx: upstream "example" in /nginx/vhosts/example2.com:9
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ strace nginx -t 2>&1 | grep -vE "(mmap|munmap|mprotect|poll|stat|ioctl|write|close|getsockname)" | grep -vE "^\)" | grep . | grep -FA 24 "/etc/resolv.conf"
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
read(5, "nameserver 8.8.8.8\n", 4096) = 19
read(5, "", 4096) = 0
uname({sysname="Linux", nodename="www.yandex.com", ...}) = 0
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
lseek(5, 0, SEEK_CUR) = 0
read(5, "127.0.0.1\tlocalhost\n::1\t\tlocalho"..., 4096) = 129
lseek(5, 0, SEEK_CUR) = 129
read(5, "", 4096) = 0
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\21\0\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 5
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240C\0\0\0\0\0\0"..., 832) = 832
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendmmsg(5, [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\305\324\1\0\0\1\0\0\0\0\0\0\7example\6yandex\3com\0"..., iov_len=36}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=36}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\346\345\1\0\0\1\0\0\0\0\0\0\7example\6yandex\3com\0"..., iov_len=36}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, msg_len=36}], 2, MSG_NOSIGNAL) = 2
recvfrom(5, "\305\324\201\200\0\1\0\2\0\0\0\0\7example\6yandex\3com\0"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 79
recvfrom(5, "\346\345\201\200\0\1\0\2\0\0\0\0\7example\6yandex\3com\0"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [28->16]) = 91
openat(AT_FDCWD, "/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 5
read(5, "# Configuration for getaddrinfo("..., 4096) = 2584
read(5, "", 4096) = 0
futex(0x7f40c6c22044, FUTEX_WAKE_PRIVATE, 2147483647) = 0
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("213.180.204.242")}, 16) = 0
С указанным hostname www.yandex.com Nginx успешно стартует, но ресолвит доменное имя example.yandex.com и соответственно проксирует трафик на 213.180.204.242.
Итого: если каталог с апстримами указан неверно (или не указан совсем) Nginx ресолвит доменное имя согласно правилам описанным в /etc/resolve.conf (учтены будут не только директивы nameserver, но и, например, search), а само доменное имя формирует путём замены части имени hostname (низшего уровня) на имя указанное в директиве proxy_pass.
Пример:
Если
hostname задан как uno.duo.tres.yandex.ru,
proxy_pass как
http://example
, то ресолвить Nginx будет example.duo.tres.yandex.ru
Тут хотелось написать что-то язвительное, но к тому моменту, когда я решил этот ребус, не осталось никаких сил.
В общем, пишите конфиги без ошибок, ибо кривые админские руки способны поломать даже такой хороший продукт как Nginx.
edo1h
и в конфиги ни разу не вносились изменения? если я правильно понял, первый же
nginx -s reload
показал бы ошибкускорее «если такого апстрима нет в конфигах»
ну логично же
хм… а
host example
что у вас говорит в этом случае?simpleadmin Автор
edo1h
да, ошибся,
host
напрямую у dns запрашивает, надо было спроситьgetent hosts example
.If no domain entry is present, the domain is determined from the local hostname returned by gethostname(2); the domain part is taken to be everything after the first '.'.
simpleadmin Автор
угу, и на этот раз в заднем кармане своих брюк
skidka50pcs
Прочитал, перечитал несколько раз, не поверил.
Поднял VPS, перепроверил.
Yes really. Ошибка есть.
У меня вопрос не в том, что проблема есть, а в том, как её автор умудрился найти?
Loggus66
У меня есть ещё ошибка (статью писать не буду, опыт горький и было давно):
Если есть терминирующий SSL-прокси с кучей хостов и, соответственно, директивами «listen 443 ssl;», то достаточно всего хоть где-нибудь указать «listen 443;», чтобы Nginx перестал обрабатывать SSL вовсе после перезапуска.
crazylh
Зависит от… порядка чтения конфигов самим nginx. О чем неоднократно говорят сами разработчики.