Имеется AWS EC2 инстанс, на котором запущен dnsmasq
.
Имеется AWS RDS-инстанс, для которого разрешён публичный доступ, и у домена которого, соответственно, есть два IP – публичный, если запрашивать публичные DNS, и приватный, который отдаётся DNS самого Amazon, с DNS VPC, в котором запущены инстансы. См начало поста AWS: php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution и dnsmasq.
Но возникает проблема – иногда dnsmasq
локальным клиентам отдаёт публичный адрес, а иногда – приватный.
Т.е иногда ответ выглядит так:
[simterm]
admin@bttrm-stage-app-1:~$ dig mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com +short ec2-18-***-***-186.us-east-2.compute.amazonaws.com. 18.***.***.186
[/simterm]
А иногда – так:
[simterm]
admin@bttrm-stage-app-1:~$ dig mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com +short ec2-18-***-***-186.us-east-2.compute.amazonaws.com. 10.0.3.54
[/simterm]
Это приводит к проблемам, т.к. во-первых – все запросы от приложений на инстансах в VPC начинают ходить “через мир”, во-вторых – доступ к RDS ограничен Secuirty Group-ми, и если имя хоста резолвится на внешний IP и трафик идёт “через мир” – то залогиниться на сервер нельзя.
В примере ниже используется имя RDS stage.backend-db1-slave.example.com, которое через CNAME
направлено на RDS-домен mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com, а он уже, в свою очередь, должен резолвиться на приватный IP из VPC-сети.
Содержимое resolv.conf
:
[simterm]
root@bttrm-stage-app-1:/home/admin# cat /etc/resolv.conf domain us-east-2.compute.internal search us-east-2.compute.internal nameserver 127.0.0.1 nameserver 10.0.6.2 nameserver 1.1.1.1 nameserver 8.8.8.8
[/simterm]
Тут:
- nameserver 127.0.0.1 –
dnsmasq
- nameserver 10.0.6.2 – VPC’s DNS
- 1.1.1.1 и 8.8.8.8 – Cloudflare и Google
Включаем логи dnsmasq
– в /etc/dnsmasq.conf
добавляем строку log-queries
, и перезапускаем dnsmasq
:
[simterm]
root@bttrm-stage-app-1:/home/admin# systemctl restart dnsmasq.service
[/simterm]
Проверяем лог:
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: reading /etc/resolv.conf
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: ignoring nameserver 127.0.0.1 – local interface
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: using nameserver 10.0.6.2#53
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: using nameserver 1.1.1.1#53
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: using nameserver 8.8.8.8#53
Окей.
Теперь запрашиваем имя:
[simterm]
root@bttrm-stage-app-1:/home/admin# dig stage.backend-db1-slave.example.com +short mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com. ec2-18-***-***-186.us-east-2.compute.amazonaws.com. 18.***.***.186
[/simterm]
И смотрим лог:
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: forwarded stage.backend-db1-replica.example.com to 10.0.6.2
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: forwarded stage.backend-db1-replica.example.com to 1.1.1.1
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: forwarded stage.backend-db1-replica.example.comto 8.8.8.8
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply stage.backend-db1-replica.example.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply ec2-18-***-***-186.us-east-2.compute.amazonaws.com is NODATA-IPv6
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply stage.backend-db1-replica.example.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply ec2-18-***-***-186.us-east-2.compute.amazonaws.com is 18.***.***.186
В документации сказано:
By default, dnsmasq will send queries to any of the upstream servers it knows about and tries to favour servers that are known to be up.
Т.к. в resolv.conf
кроме самого dnsmasq
есть три сервера – VPC, Cloudflare и Google – то dnsmasq
получает ответ от одного, и использует его в дальнейшем для форвадинга новых запросов и кеширует значение для уже отправленных запросов.
Т.к. Cloudflare отвечает зачастую быстрее, чем DNS в самой VPC – то его ответ и кешируется.
Проверить скорость ответа можно с помощью dnsping
из набора dnsdiag
.
Ответ от Cloudflare – 18 ms:
[simterm]
root@bttrm-stage-app-1:/home/admin/dnsdiag# dnsping -c 1 -s 1.1.1.1 stage.backend-db1-slave.example.com dnsping DNS: 1.1.1.1:53, hostname: stage.backend-db1-slave.example.com, rdatatype: A 76 bytes from 1.1.1.1: seq=0 time=18.264 ms
[/simterm]
Ответ от VPC DNS – 24 ms:
[simterm]
root@bttrm-stage-app-1:/home/admin/dnsdiag# dnsping -c 1 -s 10.0.6.2 stage.backend-db1-slave.example.com dnsping DNS: 10.0.6.2:53, hostname: stage.backend-db1-slave.example.com, rdatatype: A 69 bytes from 10.0.6.2: seq=0 time=24.941 ms
[/simterm]
Google со своими 134 ms вообще теряется 🙂
Само собой – Cloudflare содержит публичный IP нашего RDS-сервера, что совсем не то, чего хотелось бы.
Первым вариантом могло бы стать включение опции strict-order
:
-o, –strict-order
By default, dnsmasq will send queries to any of the upstream servers it knows about and tries to favour servers that are known to be up. Setting this flag forces dnsmasq to try each query with each server strictly in the order they appear in /etc/resolv.conf
Но тут возникает другая проблема: клиент отправляет запрос к dnsmasq
, dnsmasq
выполняет запрос к первому серверу из resolv.conf
, в случае, если сервер не отвечает или на нём нет записи – dnsmasq
вернёт клиенту ошибку. Но для того, что бы dnsmasq
выполнил запрос ко второму серверу из списка в resolv.conf
– клиент должен отправить повторный запрос к dnsmasq
. См. обсуждение тут>>> и тут>>>.
В нашем случае – как раз не проблема, и можно было бы использовать такой подход.
Но есть второе решение – использовать параметр server
, и задать конкретный DNS-сервер для конкретного домена/ов.
Обвновляем /etc/dnsmasq.conf
:
... server=/stage.backend-db1-slave.example.com/10.0.6.2 ...
Теперь при запросе stage.backend-db1-slave.example.com – dnsmasq
должен запросить ответ у DNS этой VPC, т.е. 10.0.6.2.
Перезапускаем:
[simterm]
root@bttrm-stage-app-1:/home/admin# systemctl restart dnsmasq.service
[/simterm]
Выполняем запрос:
[simterm]
root@bttrm-stage-app-1:/home/admin# dig @127.0.0.1 stage.backend-db1-slave.example.com +short mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com. ec2-18-***-***-186.us-east-2.compute.amazonaws.com. 10.0.6.42
[/simterm]
Проверяем лог:
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: query[A] stage.backend-db1-slave.example.com from 127.0.0.1
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: forwarded stage.backend-db1-slave.example.com to 10.0.6.2
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: reply stage.backend-db1-slave.example.com is <CNAME>
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: reply mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com is <CNAME>
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: reply ec2-18-***-***-186.us-east-2.compute.amazonaws.com is 10.0.6.42
Вроде ОК.
Готово.