当在Azure中部署SQL VM时,处于安全考虑,不会配置VM的Public IP,会禁止外网的进出站访问,只允许从内部VNET,或者特定的内部IP访问。特别是当使用Azure Internal Load Balancer(ILB)配置了AlwaysOn AG的侦听器后,我们本意只允许内部访问。但是总有些特别的需求,需要VM能访问Internet。
为什么单机的 Azure SQL VM能访问外网,当加入到使用了ILB的AlwaysOn AG后就不能访问外网了?
这是最近遇到比较多的一个问题。分析这个问题前,我们需要明白 Azure 的 默认出站访问(Default Outbound Access). 当一个VM或者资源没有以下配置时,Azure会通过默认的SNAT为它们创建出站连接:
实例级的Public IP
出站规则
NAT 网关
Load Balancer
当创建单机 SQL VM后,如果没有配置上面的内容,Azure 会提供一个动态的默认出站访问IP用于外网访问。当把VM加入到ILB之后,Azure 就不再为VM提供这种默认出站访问连接。所以就不能再访问外网了。另外,这里的ILB特指standard ILB,因为Basic ILB不提供SLA保证,不支持HA,也不支持Avalability Zone,一般在SQL VM生产环境不建议使用。
实现ILB中VM出站访问
实现方法有很多种:
方法
适用生产环境
对比
为VM配置Public IP
Yes
OK
通过出站规则,使用Public LB的前端IP出站访问
Yes
OK
直接使用Public LB的前端IP做入站和出站访问
No
Bad
分配NAT网关给VM所在subnet实现出站访问
Yes
Good
VNet NAT Gateway 是 Azure 推荐的方式,它的可扩展性,可靠性比其它几种要好。下文就介绍一下这种方式的实现方式。
使用 VNet NAT Gateway 实现ILB中VM的仅出站访问
VNET NAT 简化了虚拟网络的仅出站 Internet 连接。 在子网中配置后,所有出站连接将使用指定的静态公共 IP 地址。 无需使用负载均衡器或将公共 IP 地址直接附加到虚拟机,即可建立出站连接。 NAT 是完全托管式的,且具有很高的复原能力。
可为使用 NAT 为每个子网定义出站连接。 同一虚拟网络中的多个子网可以使用不同的 NAT。 也可以通过指定要使用的 NAT Gateway 来配置子网。 任何虚拟机实例中的所有 UDP 和 TCP 出站流都会使用 NAT。
NAT 可以与 Standard Public IP 地址资源和 Public IP Prefix 共用。 NAT 会将所有流量指向到前缀所表示的 IP 地址范围。 在部署中将任何 IP 加入筛选的过程现在都很简单。
NAT 会自动处理子网的所有出站流量,而无需完成任何定制化配置。 不需要指定用户定义的路由。 NAT 优先于其他出站方案,可替代子网的默认 Internet 目标。
如下内容将使用Azure CLI完成配置过程,主要包括:
配置VNET和NAT Gateway
配置VM和 Standard ILB
配置完成,大概如下图所示:
RG=ILBNAT-RG
# 创建资源组
az group create --name $RG --location chinanorth2
# 创建 NAT Gateway 出站需要使用的 public ip 资源
az network public-ip create -g $RG --name NAT-PIP \
--sku standard --allocation static
# 创建 NAT Gateway资源
az network nat gateway create -g $RG --name myNATgateway \
--public-ip-addresses NAT-PIP --idle-timeout 10
# 创建 VNET及subnet
az network vnet create -g $RG \
--location chinanorth2 --name myVnet \
--address-prefix 10.1.0.0/16 --subnet-name mySubnet --subnet-prefix 10.1.0.0/24
# 配置对应 subnet使用NAT Gateway
az network vnet subnet update -g $RG \
--vnet-name myVnet \
--name mySubnet \
--nat-gateway myNATgateway
# 创建网线安全组(NSG),因为 Standard LB 要求加入的 VM 的 NIC 属于一个NSG
az network nsg create -g $RG --name myNSG
# 为NSG创建规则,这里以80端口为例,SQL可能需要开放1433
az network nsg rule create -g $RG --nsg-name myNSG \
--name myNSGRuleHTTP \
--protocol '*' \
--direction inbound \
--source-address-prefix '*' \
--source-port-range '*' \
--destination-address-prefix '*' \
--destination-port-range 80 \
--access allow \
--priority 200
# 为VM创建 NIC,使用前面的NSG
for i in {1,2}
do
az network nic create -g $RG --name vmnic$i --vnet-name myVNet \
--subnet mySubnet --network-security-group myNSG
done
# 创建 VM,使用前面的NIC
for i in {1,2}
do
az vm create -g $RG \
--name VM$i \
--nics vmnic$i\
--image win2016datacenter \
--admin-username azureuser \
--no-wait
done
# 为VM安装IIS. 注意,这需要确认VM创建成功后才能执行,create vm 时使用了 --no-wait 参数,让操作在后台执行,不会马上创建成功。
az vm list -g $RG --query "[].{vmName:name,vmState:provisioningState}"
for i in {1,2}
do
az vm extension set \
--publisher Microsoft.Compute \
--version 1.8 \
--name CustomScriptExtension \
--vm-name VM$i \
-g $RG --no-wait
--settings '{"commandToExecute":"powershell Add-WindowsFeature Web-Server; powershell Add-Content -Path \"C:\\inetpub\\wwwroot\\Default.htm\" -Value $($env:computername)"}'
done
# 创建ILB
az network lb create -g $RG --name myILB \
--sku Standard --vnet-name myVnet \
--subnet mySubnet --frontend-ip-name myFrontEnd \
--backend-pool-name myBackEndPool
# 创建ILB的health probe rule
az network lb probe create -g $RG \
--lb-name myILB \
--name myHealthProbe \
--protocol tcp \
--port 80
# 创建ILB的负载均衡规则
az network lb rule create -g $RG \
--lb-name myILB \
--name myHTTPRule \
--protocol tcp \
--frontend-port 80 \
--backend-port 80 \
--frontend-ip-name myFrontEnd \
--backend-pool-name myBackEndPool \
--probe-name myHealthProbe \
--idle-timeout 15 \
--enable-tcp-reset true
# 将VM加入到ILB
for i in {1,2}
do
az network nic ip-config address-pool add \
--address-pool myBackendPool \
--ip-config-name ipconfig1 \
--nic-name vmnic$i \
-g $RG --lb-name myILB
done
# 在相同的subnet创建一台VM,并配置 Public IP, 做为零时测试用的跳板机
# 生产环境,RDP和SSH连接可以考虑使用Azure Bastion方案,更安全和可靠
# 创建成功后,会看到返回的Public IP,然后RDP连接
az vm create -g $RG \
--name VMTEST \
--image Win2016Datacenter \
--public-ip-sku Standard \
--vnet-name myVnet --subnet mySubnet \
--admin-username azureuser --admin-password 'Str1ngPa$$w0rd'
# 获取VM的Private IP,用于后续RDP连接
az vm list-ip-addresses -g $RG --query "[].{name:virtualMachine.name,privateip:virtualMachine.network.privateIpAddresses}"
验证VM1和VM2的外网出站IP,是否是NAT的Public IP
# 获取NAT使用Public IP 地址
az network public-ip show -g $RG -n NAT-PIP --query "ipAddress"