Назад | Перейти на главную страницу

EC2: Как назначить несколько эластичных IP-адресов одному сетевому интерфейсу через SDK

Кто-нибудь знает, как связать несколько эластичных IP-адресов с одним экземпляром через Amazon SDK? В Ruby я попытался использовать гемы aws-sdk и fog, которые отлично работают для одного адреса, но при попытке назначить несколько адресов возникают ошибки.

Через веб-интерфейс это можно сделать, добавив дополнительные частные IP-адреса, а затем назначив общедоступный IP-адрес сетевому интерфейсу + частный IP-адрес, однако у меня нет никаких параметров частного IP-адреса в SDK.

Эластичные IP-адреса работают

По умолчанию экземпляр в VPC получает IP-адрес только из частной подсети в VPC, обычно где-то в подсети 10.0.0.0/8. Это означает, что, хотя эти экземпляры могут получить доступ к другим экземплярам в той же подсети, они не смогут подключиться к более широкому Интернету.

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

Альтернативный вариант - дать каждой машине собственный эластичный IP-адрес с глобальной маршрутизацией. Amazon будет направлять трафик с эластичного IP-адреса на ваш внутренний IP-адрес. Это упрощает настройку с точки зрения топологии сети.

Маршрутизация в VPC работает с помощью таблиц маршрутизации, которые настраиваются для каждой подсети. Вы можете просмотреть свою таблицу маршрутизации на вкладке VPC консоли AWS. По умолчанию запросы в вашей подсети маршрутизируются локально, а запросы за пределами этого диапазона маршрутизируются через «интернет-шлюз».

Например, предположим, что у вас есть экземпляр с IP 10.0.0.1. С ним связан эластичный IP-адрес, скажем 1.2.3.4. Теперь всякий раз, когда вы обращаетесь к другим внутренним машинам в подсети 10.0.0.0/8, ваш запрос будет исходить с вашего частного IP-адреса, и с трафиком ничего не произойдет.

Когда вы получаете доступ к машине за пределами вашей подсети, пакеты маршрутизируются через интернет-шлюз, устройство VPC с именем, например igw-9d7534f2. Шлюз (который не является фактическим экземпляром EC2, просто непрозрачной системой AWS) принимает запрос, а затем ищет внутренний IP-адрес, с которого он исходит, и проверяет, связан ли этот IP-адрес с каким-либо эластичным IP-адресом. Если это так, запрос переписывается так, как будто источником является эластичный IP-адрес, а затем отправляется через Интернет. Когда пакет возвращается на шлюз с эластичным IP-адресом в качестве пункта назначения, шлюз проверяет, связан ли эластичный IP-адрес с внутренним IP-адресом, и, если да, перезаписывает пакет на этот IP-адрес. Затем пакет пересылается в частную подсеть.

Вот как работает NAT "один-к-одному" эластичных IP-адресов. Преимущество этого подхода в том, что машинам в подсети не нужно знать свои эластичные IP-адреса. Это упрощает переключение эластичных IP-адресов работающего экземпляра, поскольку единственное, что необходимо обновить, - это таблица сопоставления на интернет-шлюзе. Внешний IP-адрес может меняться много раз; поскольку пакеты переписываются для использования внутренних IP-адресов до того, как они поступят в экземпляр, экземпляр никогда не узнает об этом.

Несколько сетевых интерфейсов

В прошлом Amazon добавляла возможность добавления нескольких сетевых интерфейсов к экземпляру VPC. Эти интерфейсы отображаются на вашем компьютере как отдельные карты Ethernet и будут иметь отдельный внутренний IP-адрес. Раздельные интерфейсы также означают, что вам необходимо правильно настроить маршрутизацию между этими интерфейсами: если вы отправляете пакет через неправильный интерфейс, пакет просто отбрасывается.

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

Для каждого внутреннего IP-адреса можно связать внешний эластичный IP-адрес. Хотя это возможно, теперь вы попадаете в сложную ситуацию с маршрутизацией. Оба внутренних IP-адреса (на eth0 и eth1) имеют связанный внешний IP-адрес и должны иметь возможность делать запросы в более широкий Интернет, если интернет-шлюз переводит их внутренний IP-адрес в эластичный IP-адрес. Однако, чтобы сделать это правильно, запросы должны быть отправлены через правильный интерфейс. Сама Amazon признает, что это непросто:

По умолчанию в Linux есть таблица маршрутизации, которая направляет весь трафик за пределы вашей локальной подсети на один интерфейс. Вы можете найти это с помощью маршрута:

# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         10.0.0.1        0.0.0.0         UG    0      0        0 eth0
default         10.0.0.1        0.0.0.0         UG    100    0        0 eth0
10.0.0.0        *               255.255.255.0   U     0      0        0 eth0
10.0.0.0        *               255.255.255.0   U     0      0        0 eth1

Здесь весь интернет-трафик будет проходить через интерфейс eth0. Даже пакеты с исходным IP-адресом интерфейса eth1 будут проходить через eth0, а затем будут автоматически отбрасываться вместо того, чтобы быть NAT-адресом для правильного эластичного IP-адреса. Хотя для решения этой проблемы можно выполнить маршрутизацию на основе исходного кода, ее нетривиально настроить (обратите внимание, что вам нужно запустить dhclient после добавления другого интерфейса к машине):

# ifconfig | grep eth\\\|inet\ 
eth0      Link encap:Ethernet  HWaddr 02:86:10:77:7f:fe  
          inet addr:10.0.0.76  Bcast:10.0.0.255  Mask:255.255.255.0
eth1      Link encap:Ethernet  HWaddr 02:86:10:65:fd:31  
          inet addr:10.0.0.226  Bcast:10.0.0.255  Mask:255.255.255.0

# curl --interface 10.0.0.76 ifconfig.me
116.x.x.x
# curl --interface 10.0.0.226 ifconfig.me
<< TIMEOUT >>>

# ip rule add from 10.0.0.226 table out2
# ip route add default via 10.0.0.1 dev eth1 table 2
# ip route flush cache

# curl --interface 10.0.0.226 ifconfig.me
116.x.x.x
# curl --interface 10.0.0.76 ifconfig.me
116.x.x.x

Несколько IP-адресов

Таким образом мы можем избежать некоторых неприятностей с маршрутизацией, описанных выше: все пакеты будут отправляться через eth0, независимо от того, какой у вас IP. Из-за однозначного сопоставления общедоступных и частных адресов вам сначала необходимо добавить несколько новых частных адресов в один из ваших экземпляров.

API Amazon был обновлен для поддержки назначения вторичных частных IP-адресов, но в этом примере проще просто перейти в консоль AWS. На вкладке EC2 перейдите в Instances. Найдите экземпляр, который нужно обновить, щелкните правой кнопкой мыши и выберите «Управление частными IP-адресами».

Теперь вы увидите, что у экземпляра есть два частных IP-адреса в вашей подсети (поскольку интерфейс привязан к определенной подсети, все IP-адреса на этом интерфейсе должны находиться в той же подсети). Ницца! Однако, если вы сейчас проверите свой экземпляр:

# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 02:86:10:7b:e4:f5  
          inet addr:10.0.0.34  Bcast:10.0.0.255  Mask:255.255.255.0

У него пока нет нового IP-адреса. Давайте попробуем получить его по DHCP!

root@ip-10-0-0-34:/# dhclient -d eth0
Listening on LPF/eth0/02:86:10:7b:e4:f5
Sending on   LPF/eth0/02:86:10:7b:e4:f5
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3
DHCPREQUEST of 10.0.0.34 on eth0 to 255.255.255.255 port 67
DHCPOFFER of 10.0.0.34 from 10.0.0.1
DHCPACK of 10.0.0.34 from 10.0.0.1

Нет, мы получаем только наш основной частный IP! С DHCP вы можете получить только один адрес. Возможно, в будущем Amazon добавит поддержку нескольких IP-адресов с использованием какого-либо идентификатора клиента или другого механизма, но пока вам придется добавить адрес вручную. Надеюсь, в Ubuntu cloud-init будет добавлена ​​поддержка для этого в будущем, поэтому нам не придется делать это самим.

Amazon предлагает создать новый виртуальный интерфейс, а затем поднять его. Хотя это будет работать, это означает, что вы сохраняете состояние своего интерфейса на жестком диске, и любые изменения, которые вы вносите в AWS, вам нужно будет распространить на эти файлы.

Для тестирования можно сделать что-нибудь попроще:

MAC_ADDR=$(ifconfig eth0 | sed -n 's/.*HWaddr \([a-f0-9:]*\).*/\1/p')
IP=($(curl http://169.x.x.x/latest/meta-data/network/interfaces/macs/$MAC_ADDR/local-ipv4s))
for ip in ${IP[@]:1}; do
    echo "Adding IP: $ip"
    ip addr add dev eth0 $ip/24
done

Это проверит метаданные экземпляра EC2 для всех внутренних IP-адресов, связанных с eth0, и добавит все вторичные IP-адреса.

Теперь вы можете связать другой эластичный IP-адрес со вторичным IP-адресом в Консоли AWS:

И теперь у вас есть несколько внешних IP-адресов!

root@ip-10-0-0-34:/# curl --interface 10.0.0.58 ifconfig.me
116.x.x.x
root@ip-10-0-0-34:/# curl --interface 10.0.0.34 ifconfig.me
116.x.x.x

Хотя это работает для тестирования, оно не сохраняется при перезагрузке. Вы можете создать сценарий upstart или cloud-init, который сделает это за вас. Даже в этом случае IP-адреса не добавляются автоматически, когда вы добавляете их через EC2. Я не уверен, есть ли хороший способ сделать это. Наконец, этот скрипт также не удалит все локальные адреса, которые вы могли удалить за это время.

Надеюсь, что это поможет вам.