我目前正在开发实验室环境以将我们的混合 Exchange 2010 和 2013 环境迁移到 Exchange 2016。
我已经在实验室共存运行 1x 2013 和 2x 2016 几个星期了,它运行良好 - 我已经完成了 RPC、OWA、ActiveSync 和 EWS 的测试。一切似乎都很好。
本周我试图通过在前面引入负载均衡器来完成我的部署,并计划在我的 2013 年和 2016 年 CAS 节点之间提供循环 LB。从我研究的所有内容来看,2013 年和 2016 年 CAS 与 CAS 角色完全兼容,因为它们很乐意在彼此之间进行代理。为了确保我通过每个 CAS(没有负载均衡器)进行的手动测试似乎表明了这一点。
但是既然前面已经介绍了HA Proxy,OWA似乎就不想正常工作了。
症状
这里有一些症状:
当我通过 2016 OWA 登录到 2013 邮箱时,它确实正确登录、重定向我并开始加载收件箱。但是在加载收件箱一两秒钟后,我被重定向回 OWA 登录页面,就好像我从未登录过一样。
只要我通过 2016 CAS 登录,我可以多次重复此操作,并且似乎做同样的事情。
有时当我通过 2016 登录时,它只会挂起“仍在工作”。不过,这远没有让我退出的频率那么高。
有时它会很快重定向回登录页面,我什至根本无法告诉我是否已登录,并且没有错误消息。这只是一个闪光然后回到登录页面。
我已经在 Mac 和 Windows 8.1 上尝试了 Chrome 和 Firefox 的最新版本。所以这不是终端设备问题。
如果我通过 2013 CAS 上的 OWA 登录到 2013 邮箱,即使通过负载均衡器,它似乎第一次正确登录。
我在负载均衡器上有一个 IP,然后以循环方式代理到每个 CAS 节点的流量。这是我用来测试我的 LB 的。我有其他 IP 直接指向单个 CAS 节点。
如果我直接登录到 2016 CAS 节点到 2013 邮箱,这工作正常。这是我通过出现问题的负载均衡器登录到 2016 CAS 的一个。
有趣的是,我只是注意到即使登录 2016 邮箱也会出现完全相同的问题。因此,只要我登录 2016 CAS,它就会注销。
通过 2013 CAS 成功登录后,我可以多次刷新页面,会话仍将保持打开状态。对我来说,这表明一旦建立会话,代理必须在某种程度上正常工作,因为我的 LB 是循环的,所以每次刷新 OWA 页面时,流量都应该重新分配到各个 CAS 节点。
我似乎很清楚,在负载平衡器后面时,还有其他人在 Exchange 2010 - 2016 中遇到了类似的问题。不幸的是,这是我们第一次使用负载均衡器,因为我们正在从单个 CAS 迁移到 3,升级到 Exchange 2016。
我发现了一些具有类似症状的帖子:
我试过的
我已经确认我们所有的 SSL 前端(第三方 CA)证书在所有 CAS 节点上都与相同的指纹和序列匹配。我读过这会影响事情。我正在使用生产证书来正确测试我的实验室。
后端证书保留为默认的 Microsoft Exchange 证书,据我所知,这很好。无论如何,我实际上尝试在后端使用我们的通配符证书,它似乎破坏了一切。
我已经验证,在没有负载均衡器的情况下访问 2016 CAS 时,一切似乎都正常。基于此,我想如果我要在负载均衡器上启用完整的会话持久性,以便特定用户只处理一个 CAS,它将解决问题。但是,我不应该那样做。这让我担心有一个更深层次的错误需要解决。
我已启用 IIS 失败请求跟踪。我发现一些关于 powershell 请求的 500 个错误的报告。但它们似乎与失败的健康监控请求有关,并且在时间上似乎不一定与我的 OWA 测试让我退出。
我已经通过 Wireshark 进行了一些基本检查,以寻找任何奇怪的东西。我注意到单个 OWA 页面加载肯定至少分布在多个 CAS 节点上。有时两个 2016 节点,有时一个 2016 和一个 2013 节点。
我在捕获(在负载均衡器上)中确实注意到,在一次失败的登录尝试期间,我不断收到来自 CAS 节点的重复440 Login Timeout响应。在这种情况下,我得到了多个,每个 CAS 节点至少有一个。
在初始登录请求之后有很多单独的 HTTP 请求,很难确定问题出在哪里,但似乎浏览器开始发送一堆 OWA service.svc 请求,并且在某些时候它们都被返回与来自 CAS 节点的响应相同的440 Login Timeout错误。然后最终我们被重定向回登录页面。
通过我的研究,我还没有发现任何实际导致 440 登录超时的原因,或者它是否是预期的行为等......
我已经根据我们的生产设置重新检查了我的所有虚拟目录设置。他们看起来不错。
编辑:2017-03-04
我尝试了多种不同的 VirtualDirectory 内部和外部 URL 组合。exchange.isp.com.au 是内部 AD 域(这与实时设置相匹配)。实验室的外部 URL 是 exchlab.isp.com.au。
我努力了:
- exchlab.isp.com.au 用于内部和外部。
- exchange.isp.com.au 用于内部和外部。
- 我坚持使用 exchlab 进行外部交换并交换内部。这些组合都没有产生任何影响。
此后,我还在 HA Proxy 中添加了基于 Cookie 的会话持久性,似乎有所作为。我只对OWA和ECP做了持久化,其余的都没有持久化。注意事项:
- 我不再重复退出 OWA。它变得更加稳定。
- 当我第一次退出邮箱(例如 2013 年)并登录到不同版本的邮箱时,它仍然会发生。(例如 2016 年)。但是,在我第一次注销后,我现在可以成功重新登录。
- 如果我退出邮箱并太快(在 10 秒内)重新登录,那么我经常会立即被踢出。
我还决定继续测试除 OWA 之外的服务的 HA 代理。我可以确认:
- Outlook Anywhere 在所有 3 个 CAS 节点上进行负载平衡并且工作正常。
- EWS 还对所有 3 个进行负载平衡,并且工作正常。
- Active Sync 似乎只想超过 2x 2016 CASes,但负载平衡也很好。
我需要尝试的事情
- 重置我所有的虚拟目录
- 重新同步 IIS IUSR 帐户:https ://technet.microsoft.com/en-us/library/dd789741(v=exchg.80).aspx
- 验证 IUSR 授权级别。
我还没有真正做到这些,因为我觉得有一个实际的配置问题。此外,由于没有负载平衡器,一切似乎都可以正常工作,我看不到这有帮助。
更多环境信息
有关我正在测试的实验室环境的更多详细信息。
- 我们的负载均衡器配置取自这里:https ://www.haproxy.com/doc/aloha/7.0/deployment_guides/microsoft_exchange_2013.html#aloha-configuration - 我们使用“SSL 卸载 - HTTP 反向代理”配置(高级版)。
我们正在根据本指南进行 SSL 卸载:https ://serversforhackers.com/using-ssl-certificates-with-haproxy和本指南:https ://jaapwesselius.com/2014/02/28/exchange-2013-sp1 -ssl-卸载/
我们的 Exchange 2013 机器正在运行 SP1 \w CU11
- 我们的 Exchange 2016 机器正在运行 CU2。我有意避免升级到 CU4,因为我想使用负载平衡器测试和记录一个优雅的升级过程。
- 我们正在运行 2 倍额外的虚拟机作为专用 AD。Exchange 节点上没有 AD。
所有 Windows 系统(包括 AD)都运行 Windows 2012 R2。
我们的路由器是一个做 NAT 的 Linux 机器。负载平衡器与 Exchange 服务器和 AD 框位于同一 /24 子网中。
LB HTTP 前端是 .7,交换框是 .1、.2 和 .3
我们还在 .4、.5 和 .6 上的每个 Exchange 框的专用 IP 上运行简单的 HTTP 重定向。虽然我计划将这个简单的重定向转移到负载均衡器,因为它可以轻松地进行 HTTP 重定向。
我的负载均衡器配置的相关位:
# This is the L7 HTTPS Front End
# ------------------------------
# We redirect to different backends depending on the URI
# Each backend has its own separate health checks. So that each service can fail on an Exchange CAS node without affecting the other services.
frontend ft_ISP_EXCHANGE_https
bind 172.16.10.7:80 name INT_http
bind 172.16.10.7:443 name INT_https ssl crt wildcard.isp.com.au.pem # Specify SSL Cert for offloading.
mode http
option http-keep-alive
option prefer-last-server
no option httpclose
no option http-server-close
no option forceclose
no option http-tunnel
timeout client 600s
log global
capture request header Host len 32
capture request header User-Agent len 64
capture response header Content-Length len 10
# log-format directive must be written on a single line
# it is splitted for documentation convnience
log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %CS\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ %hr\ %hs\ {%sslv/%sslc/%[ssl_fc_sni]/%[ssl_fc_session_id]}\"%[capture.req.method]\ %[capture.req.hdr(0)]%[capture.req.uri]\ HTTP/1.1
maxconn 1000
acl ssl_connection ssl_fc # Set ACL ssl_connection if ssl_fc returns TRUE
# Route request to a different backend depending on the path:
# http://serverfault.com/questions/127491/haproxy-forward-to-a-different-web-server-based-on-uri
acl host_mail hdr(Host) -i exchange.isp.com.au
acl path_slash path /
acl path_autodiscover path_beg -i /Autodiscover/Autodiscover.xml
acl path_activesync path_beg -i /Microsoft-Server-ActiveSync
acl path_ews path_beg -i /ews/
acl path_owa path_beg -i /owa/
acl path_oa path_beg -i /rpc/rpcproxy.dll
acl path_ecp path_beg -i /ecp/
acl path_oab path_beg -i /oab/
acl path_mapi path_beg -i /mapi/
acl path_check path_end -i HealthCheck.htm
# HTTP deny rules
http-request deny if path_check
# HTTP redirect rules
http-request redirect scheme https code 302 unless ssl_connection # Force SSL
http-request redirect location /owa/ code 302 if path_slash host_mail # Redirect / to /owa
# HTTP routing rules -- This is where we decide where to send the request
# Based on HTTP path.
use_backend bk_ISP_EXCHANGE_https_autodiscover if path_autodiscover
use_backend bk_ISP_EXCHANGE_https_ews if path_ews
# other services go here
default_backend bk_ISP_EXCHANGE_https_default
# Backends
# --------
# Now we define each backend individually
# Most of these backends will contain all the same Exchange CAS nodes, pointing to the same IPs
# The reason we define each backend individually is because it allows us to do separate Health Checks
# for each individual service running on each CAS node.
# The failure of one service on a CAS node does not exclude that CAS node from participating in other
# types of requests. This gives us better overall high-availability design.
# HTTPS OWA
# I have added additional comments on this definition, but the same applies to all Exchange HTTPS backends.
backend bk_ISP_EXCHANGE_https_owa
balance roundrobin # Use round-robin load balancing for requests
option http-keep-alive # Enable HTTP Keepalives for session re-use and lowest latency for requests.
option prefer-last-server # Prefer to keep related connections for a session on the same server.
# This is not the same as persistence, and is mainly to try and take advantage of HTTP Keepalives.
# See here for an example of why this is needed: http://stackoverflow.com/questions/35162527/haproxy-keep-alive-not-working-as-expected
no option httpclose # Disable all options that are counter to keepalives
no option http-server-close
no option forceclose
no option http-tunnel
mode http # Operate in L7 HTTP Mode (vs TCP mode etc)
log global
option httplog
option forwardfor # Enable insertion of the X_FORWARDED_FOR HTTP Header
option httpchk GET /owa/HealthCheck.htm # Use L7 HTTP Health Check. This is recommended by Microsoft.
http-check expect string 200\ OK
default-server inter 3s rise 2 fall 3
timeout server 60s
# Define CAS Nodes for this service. We're using SSL offloading to allow L7 HTTP Checks
# We've avoided SSL Bridging as that would halve our LB's throughput.
server ISP_exch16_syd_01 172.16.10.2:80 maxconn 1000 weight 10 check
server ISP_exch16_syd_02 172.16.10.3:80 maxconn 1000 weight 10 check
server ISP_exch13 172.16.10.1:80 maxconn 1000 weight 10 check
我最终合理地解决了这个问题。一些帖子提到了类似的问题,在他们的负载均衡器上添加基于 Cookie 的持久性已经为他们解决了这个问题。
我拒绝了这种“因为我不应该这样做”,微软的所有技术文章都说不再需要,而且确实不推荐。
但我屈服了,最终为 OWA 和 ECP 添加了持久性。结果是问题没有完全解决,但几乎没有引起注意。我遇到问题的唯一一次是如果我在一个邮箱上注销 OWA,然后立即登录到另一个邮箱。即使那样,它也会将您踢出一次,但如果您尝试再次登录,它会正常工作。
在此之后没有持续的问题。此外,我仅在从 2013 年迁移到 2016 年邮箱时才注意到剩下的问题。
这不是我们的最终用户可能会做的事情,而且我们几乎完成了将所有邮箱迁移到 2016 年的工作。所以我正在考虑围绕“足够好”进行这项工作。
由于我们使用的是 HA 代理,因此在配置中添加一些基于第 7 层 cookie 的持久性并不是什么大问题。事实上,只花了大约 5 分钟就弄清楚了: