我们在将 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 服务器类似,套接字侦听器将在具有适当端口的相应地址上打开两个侦听实例。
我希望这些信息对您有所帮助。