Проблемы с сетью Azure Service Fabric IPv6
У нас возникают проблемы при развертывании кластера Service Fabric в Azure, и он обрабатывает трафик как IPv4, так и IPv6.
Мы разрабатываем приложение с мобильными клиентами на iOS и Android, которые взаимодействуют с нашим кластером Service Fabric. Связь состоит как из HTTP-трафика, так и из TCP-сокета. Нам нужно поддерживать IPv6, чтобы Apple приняла приложение в своем App Store.
Мы используем шаблон ARM для развертывания в Azure, так как кажется, что портал не поддерживает настройку балансировщика нагрузки с конфигурацией IPv6 для наборов масштабов виртуальных машин (ref: url). На связанной странице также указаны другие ограничения поддержки IPv6, например, частные адреса IPv6 не могут быть развернуты в наборах весов VM. Однако, согласно этой странице, возможность предварительного просмотра частного IPv6 для наборов масштабов виртуальной машины доступна в предварительном просмотре (хотя это было последнее обновление 14.07.2017).
Для этого вопроса я постарался сделать это как можно более общим, и основал шаблон ARM на шаблоне, найденном в этом руководстве. Шаблон называется "template_original.json" и может быть загружен здесь. Это базовый шаблон для кластера сервисных структур без простоты безопасности.
Я буду связывать весь измененный шаблон ARM в нижней части этого поста, но сначала выделю основные измененные части.
Общедоступные адреса IPv4 и IPv6, связанные с балансировщиком нагрузки. Они связаны с их соответствующими бэкэнд-пулами:
"frontendIPConfigurations": [
{
"name": "LoadBalancerIPv4Config",
"properties": {
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',concat(parameters('lbIPv4Name'),'-','0'))]"
}
}
},
{
"name": "LoadBalancerIPv6Config",
"properties": {
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',concat(parameters('lbIPv6Name'),'-','0'))]"
}
}
}
],
"backendAddressPools": [
{
"name": "LoadBalancerIPv4BEAddressPool",
"properties": {}
},
{
"name": "LoadBalancerIPv6BEAddressPool",
"properties": {}
}
],
Правила балансировки нагрузки для портов внешнего интерфейса на соответствующих общедоступных IP-адресах, как IPv4, так и IPv6. Всего это четыре правила, по два на интерфейсный порт. Я добавил порт 80 для HTTP здесь и порт 5607 для подключения через сокет. Обратите внимание, что я изменил внутренний порт для порта 80 IPv6, чтобы быть 8081 и порт 8507 IPv6, чтобы быть 8517.
{
"name": "AppPortLBRule1Ipv4",
"properties": {
"backendAddressPool": {
"id": "[variables('lbIPv4PoolID0')]"
},
"backendPort": "[parameters('loadBalancedAppPort1')]",
"enableFloatingIP": "false",
"frontendIPConfiguration": {
"id": "[variables('lbIPv4Config0')]"
},
"frontendPort": "[parameters('loadBalancedAppPort1')]",
"idleTimeoutInMinutes": "5",
"probe": {
"id": "[concat(variables('lbID0'),'/probes/AppPortProbe1')]"
},
"protocol": "tcp"
}
},
{
"name": "AppPortLBRule1Ipv6",
"properties": {
"backendAddressPool": {
"id": "[variables('lbIPv6PoolID0')]"
},
/*"backendPort": "[parameters('loadBalancedAppPort1')]",*/
"backendPort": 8081,
"enableFloatingIP": "false",
"frontendIPConfiguration": {
"id": "[variables('lbIPv6Config0')]"
},
"frontendPort": "[parameters('loadBalancedAppPort1')]",
/*"idleTimeoutInMinutes": "5",*/
"probe": {
"id": "[concat(variables('lbID0'),'/probes/AppPortProbe1')]"
},
"protocol": "tcp"
}
},
{
"name": "AppPortLBRule2Ipv4",
"properties": {
"backendAddressPool": {
"id": "[variables('lbIPv4PoolID0')]"
},
"backendPort": "[parameters('loadBalancedAppPort2')]",
"enableFloatingIP": "false",
"frontendIPConfiguration": {
"id": "[variables('lbIPv4Config0')]"
},
"frontendPort": "[parameters('loadBalancedAppPort2')]",
"idleTimeoutInMinutes": "5",
"probe": {
"id": "[concat(variables('lbID0'),'/probes/AppPortProbe2')]"
},
"protocol": "tcp"
}
},
{
"name": "AppPortLBRule2Ipv6",
"properties": {
"backendAddressPool": {
"id": "[variables('lbIPv6PoolID0')]"
},
"backendPort": 8517,
"enableFloatingIP": "false",
"frontendIPConfiguration": {
"id": "[variables('lbIPv6Config0')]"
},
"frontendPort": "[parameters('loadBalancedAppPort2')]",
/*"idleTimeoutInMinutes": "5",*/
"probe": {
"id": "[concat(variables('lbID0'),'/probes/AppPortProbe2')]"
},
"protocol": "tcp"
}
}
Также добавлен один пробник на правило балансировки нагрузки, но здесь для ясности опущено.
Для apiVerison для VM Scale установлено значение "2017-03-30" в соответствии с рекомендацией вышеупомянутого решения для предварительного просмотра. Конфигурации сетевого интерфейса также настраиваются в соответствии с рекомендациями.
"networkInterfaceConfigurations": [
{
"name": "[concat(parameters('nicName'), '-0')]",
"properties": {
"ipConfigurations": [
{
"name": "[concat(parameters('nicName'),'-IPv4Config-',0)]",
"properties": {
"privateIPAddressVersion": "IPv4",
"loadBalancerBackendAddressPools": [
{
"id": "[variables('lbIPv4PoolID0')]"
}
],
"loadBalancerInboundNatPools": [
{
"id": "[variables('lbNatPoolID0')]"
}
],
"subnet": {
"id": "[variables('subnet0Ref')]"
}
}
},
{
"name": "[concat(parameters('nicName'),'-IPv6Config-',0)]",
"properties": {
"privateIPAddressVersion": "IPv6",
"loadBalancerBackendAddressPools": [
{
"id": "[variables('lbIPv6PoolID0')]"
}
]
}
}
],
"primary": true
}
}
]
С помощью этого шаблона я могу успешно развернуть его в Azure. Связь с использованием IPv4 с кластером работает, как и ожидалось, однако я вообще не могу получить трафик IPv6. Это одинаково для обоих портов 80 (HTTP) и 5607 (сокет).
При просмотре списка внутренних пулов для балансировщика нагрузки на портале Azure отображается следующее информационное сообщение, о котором я не смог найти никакой информации. Я не уверен, что это как-то влияет?
Backend pool 'loadbalanceripv6beaddresspool' was removed from Virtual machine scale set 'Node1'. Upgrade all the instances of 'Node1' for this change to apply Node1
сообщение об ошибке балансировщика нагрузки
Я не уверен, почему я не могу получить трафик через IPv6. Возможно, что-то пропущено в шаблоне, или какая-то другая ошибка с моей стороны? Если требуется какая-либо дополнительная информация, не стесняйтесь спрашивать.
Вот весь шаблон ARM. Из-за ограничений по длине и длине поста я не включил его, но вот ссылка Pastebin на полный шаблон ARM (обновлено).
Обновить
Некоторая информация об отладке подключения IPv6. Я попытался слегка изменить шаблон ARM для пересылки трафика IPv6 через порт 80 на внутренний порт 8081 вместо этого. Таким образом, IPv4 равен 80=>80, а IPv6 80=>8081. Шаблон ARM обновлен (см. Ссылку в предыдущем разделе).
На порте 80 я использую Kestrel в качестве веб-сервера без сохранения состояния. У меня есть следующие записи в ServiceManifest.xml:
<Endpoint Protocol="http" Name="ServiceEndpoint1" Type="Input" Port="80" />
<Endpoint Protocol="http" Name="ServiceEndpoint3" Type="Input" Port="8081" />
Я был немного не уверен, какие именно адреса слушать в Кестрел. Использование FabricRuntime.GetNodeContext().IPAddressOrFQDN всегда возвращает адрес IPv4. Это то, как мы начинаем. Для отладки этого я в настоящее время получаю все адреса IPv6, и хакодированный хак для порта 8081 мы используем этот адрес. Порт Fort 80 использует IPAddress.IPv6Any, однако по умолчанию это всегда адрес IPv4, возвращаемый FabricRuntime.GetNodeContext().IPAddressOrFQDN.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
var endpoints = Context.CodePackageActivationContext.GetEndpoints()
.Where(endpoint => endpoint.Protocol == EndpointProtocol.Http ||
endpoint.Protocol == EndpointProtocol.Https);
var strHostName = Dns.GetHostName();
var ipHostEntry = Dns.GetHostEntry(strHostName);
var ipv6Addresses = new List<IPAddress>();
ipv6Addresses.AddRange(ipHostEntry.AddressList.Where(
ipAddress => ipAddress.AddressFamily == AddressFamily.InterNetworkV6));
var listeners = new List<ServiceInstanceListener>();
foreach (var endpoint in endpoints)
{
var instanceListener = new ServiceInstanceListener(serviceContext =>
new KestrelCommunicationListener(
serviceContext,
(url, listener) => new WebHostBuilder().
UseKestrel(options =>
{
if (endpoint.Port == 8081 && ipv6Addresses.Count > 0)
{
// change idx to test different IPv6 addresses found
options.Listen(ipv6Addresses[0], endpoint.Port);
}
else
{
// always defaults to ipv4 address
options.Listen(IPAddress.IPv6Any, endpoint.Port);
}
}).
ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseStartup<Startup>()
.UseUrls(url)
.Build()), endpoint.Name);
listeners.Add(instanceListener);
}
return listeners;
}
Вот конечные точки, показанные в Service Fabric Explorer для одного из узлов: Адреса конечных точек
Что касается прослушивателя сокетов, я также изменил его так, чтобы IPv6 перенаправлялся на внутренний порт 8517 вместо 8507. Аналогично веб-серверу Kestrel прослушиватель сокетов откроет два экземпляра прослушивания по соответствующим адресам с соответствующим портом.
Я надеюсь, что эта информация поможет.
1 ответ
Оказывается, я сделал очень глупую ошибку, которая полностью моя вина, я забыл фактически проверить, что мой провайдер полностью поддерживает IPv6. Оказывается, они этого не делают!
Тестирование от поставщика с полной поддержкой IPv6 работает должным образом, и я могу получить полное подключение к узлам в кластере Service Fabric.
Вот рабочий шаблон ARM для любого, кому нужен полностью рабочий пример кластера Service Fabric с поддержкой IPv4 и IPv6:
Not allowed to post pastebin links without a accompanied code snippet...
Обновить:
Из-за ограничений по длине шаблон не мог быть вставлен в этот поток целиком, однако на странице GitHub Issues для Service Fabric я добавил это. Шаблон ARM публикуется как комментарий в этой теме, он будет доступен дольше, чем ссылка для вставки. Посмотрите это здесь.