1

我们在将 Service Fabric 群集部署到 Azure 并让它同时处理 IPv4 和 IPv6 流量时遇到问题。

我们正在开发一个应用程序,该应用程序在 iOS 和 Android 上具有与我们的 Service Fabric 集群通信的移动客户端。通信包括 HTTP 通信和 TCP Socket 通信。我们需要支持 IPv6 才能让 Apple 在其 App Store 中接受该应用程序。

我们正在使用 ARM 模板部署到 Azure,因为门户似乎不支持为虚拟机规模集(参考:url)配置具有 IPv6 配置的负载均衡器。链接页面还说明了对 IPv6 支持的其他限制,例如无法将私有 IPv6 地址部署到 VM 规模集。但是,根据页面,将私有 IPv6 分配给 VM 规模集的可能性在预览中可用(尽管上次更新时间为 2017 年 7 月 14 日)。

对于这个问题,我尽量保持一般性,并将 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)上的前端端口的负载平衡规则。这总共有四个规则,每个前端端口两个。我在这里为 HTTP 添加了端口 80,为 Socket 连接添加了端口 5607。 请注意,我已将 IPv6 端口 80 的后端端口更新为 8081,将 IPv6 端口 8507 更新为 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"
    }
}

还为每个负载平衡规则添加了一个探针,但为清楚起见此处省略。

根据上述预览解决方案的建议,VM Scale set 的 apiVerison 设置为“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 (socket) 都是一样的。

在 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 模板。由于长度和帖子长度的限制,我没有嵌入它,但这里有一个指向完整 ARM 模板(更新)的 Pastebin 链接

更新

有关调试 IPv6 连接的一些信息。我尝试稍微改变 ARM 模板,将端口 80 上的 IPv6 流量转发到后端端口 8081。所以 IPv4 是 80=>80,而 IPv6 是 80=>8081。ARM 模板已更新(参见上一节中的链接)。

在端口 80 上,我将 Kestrel 作为无状态 Web 服务器运行。我在 ServiceManifest.xml 中有以下条目:

<Endpoint Protocol="http" Name="ServiceEndpoint1" Type="Input" Port="80" />      
<Endpoint Protocol="http" Name="ServiceEndpoint3" Type="Input" Port="8081" />

我有点不确定具体要在 Kestrel 中监听哪些地址。使用 FabricRuntime.GetNodeContext().IPAddressOrFQDN 始终返回 IPv4 地址。这就是我们目前的启动方式。为了调试这个,我目前掌握了所有的 IPv6 地址,并且我们使用该地址对端口 8081 进行硬编码破解。Fort 端口 80 使用 IPAddress.IPv6Any,但是这始终默认为 FabricRuntime.GetNodeContext().IPAddressOrFQDN 返回的 IPv4 地址。

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 Web 服务器类似,套接字侦听器将在具有适当端口的相应地址上打开两个侦听实例。

我希望这些信息对您有所帮助。

4

1 回答 1

1

事实证明我犯了一个非常愚蠢的错误,这完全是我的错,我忘了实际验证我的 ISP 是否完全支持 IPv6。事实证明他们没有!

来自具有完整 IPv6 支持的提供商的测试正常工作,并且我能够获得与 Service Fabric 群集中节点的完整连接。

是适用于需要具有 IPv4 和 IPv6 支持的 Service Fabric 群集的完整工作示例的任何人的工作 ARM 模板:

Not allowed to post pastebin links without a accompanied code snippet...

更新:

由于长度限制,无法将模板完整地粘贴到此线程中,但是在 Service Fabric 的 GitHub 问题页面上,我交叉发布了此内容。ARM 模板作为评论发布在该线程中,希望它的可用时间比 pastebin 链接更长。在这里查看。

于 2018-05-31T09:15:04.920 回答