我目前正在开发实验室环境,以将混合的 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 问题。遗憾的是,这是我们第一次使用负载均衡器,因为随着 Exchange 2016 的升级,我们将从单个 CAS 升级到 3 个。
我发现有不少具有类似症状的帖子:
我尝试过的方法
我已经确认我们所有的 SSL 前端(第三方 CA)证书在所有 CAS 节点上都与相同的指纹和序列号匹配。我了解到这可能会产生影响。我正在使用生产证书来正确测试我的实验室。
后端证书保留为默认的 Microsoft Exchange 证书,并且据我所知这没问题。我实际上尝试在后端使用我们的通配符证书,但它似乎破坏了一切。
我已经验证,在没有负载平衡器的情况下访问 2016 CAS 时,一切似乎都运行正常。基于此,我认为如果我在负载平衡器上启用完全会话持久性,以便特定用户只处理一个 CAS,那么问题就会得到解决。但是,我不应该这么做。这让我担心还有更深层次的错误需要解决。
我已启用 IIS 失败请求跟踪。我发现一些 powershell 请求的 500 错误报告。但它们似乎与失败的健康监测请求有关,并且似乎不一定与我的 OWA 测试注销我的时间一致。
我通过 Wireshark 进行了一些基本检查,以查找任何异常情况。我注意到单个 OWA 页面加载确实至少分布在多个 CAS 节点上。有时是两个 2016 节点,有时是 2016 和 2013 节点。
我确实注意到,在一次失败的登录尝试中(在负载平衡器上),我不断重复440 登录超时来自 CAS 节点的响应。在本例中,我收到了多个这样的响应,每个 CAS 节点至少收到一个。
初始登录请求后,出现了如此多的单独 HTTP 请求,很难确定问题出在哪里,但似乎是浏览器开始发送一堆 OWA service.svc 请求,并且在某个时候它们都开始返回相同的440 登录超时错误作为来自 CAS 节点的响应。然后我们最终被重定向回登录页面。
通过我的研究,我还没有发现什么真正导致了 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 进行外部操作,使用 exchange 进行内部操作。这些组合都没有什么区别。
从那时起,我还在 HA Proxy 中添加了基于 Cookie 的会话持久性,似乎确实有所作为。我只对 OWA 和 ECP 进行了持久性,但其余的都没有持久性。注意事项:
- 我不再反复退出 OWA。它变得更加稳定了。
- 第一次退出邮箱(例如 2013)并登录不同版本的邮箱(例如 2016)时仍会发生这种情况。不过,第一次退出后,我现在可以成功重新登录。
- 如果我退出邮箱并重新登录的速度太快(10 秒内),那么我经常会立即被踢出。
我还决定继续测试除 OWA 之外的服务的 HA Proxy。我可以确认:
- Outlook Anywhere 在所有 3 个 CAS 节点上进行负载平衡并且运行良好。
- EWS 还可以在这 3 个系统上进行负载平衡并且运行良好。
- Active Sync 似乎只想覆盖 2x 2016 CAS,但负载平衡也很好。
我需要尝试的事情
- 重置所有虚拟目录
- 重新同步 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-offloading/
我们的 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,Exchange 盒为 .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
答案1
我最终合理地解决了这个问题。针对类似问题,有几篇帖子提到,在他们的负载均衡器上添加基于 Cookie 的持久性可以解决这个问题。
我曾拒绝这样做“因为我不应该这样做”,微软的所有技术文章都说这不再需要,而且确实不推荐。
但我还是屈服了,最终为 OWA 和 ECP 都添加了持久性。结果是问题没有完全解决,但几乎不引人注意。我唯一遇到问题的时候是,如果我在一个邮箱上注销 OWA,然后立即登录另一个邮箱。即使这样,它也会将你踢出一次,但如果你再次尝试登录,它就会正常工作。
此后没有持续的问题。此外,我仅在从 2013 版邮箱移至 2016 版邮箱时才注意到剩余的问题。
这不是我们的最终用户可能会做的事情,而且我们几乎已经将所有邮箱迁移到 2016 年。所以我认为这项工作“足够好”。
由于我们使用的是 HA Proxy,因此在配置中添加一些基于第 7 层 cookie 的持久性并不是什么大问题。事实上,只花了大约 5 分钟就搞清楚了:
# HTTPS Outlook Web App (OWA)
backend bk_EXCHANGE_https_owa
cookie LB01 insert indirect nocache # <-- Added this
balance roundrobin
mode http
...
# Added the "cookie <name>" at the end of each CAS node definition
# These names must be unique to each node.
server n1_exch16_syd_01 172.16.10.2:80 maxconn 1000 weight 10 check cookie EXCH16-SYD-01
server n1_exch16_syd_02 172.16.10.3:80 maxconn 1000 weight 10 check cookie EXCH16-SYD-02
server n1_exch13 172.16.10.1:80 maxconn 1000 weight 10 check cookie EXCH13-SYD