症状

症状

我目前正在开发实验室环境,以将混合的 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,但负载平衡也很好。

我需要尝试的事情

我还没有真正完成这些,因为我觉得这确实是一个配置问题。此外,由于没有负载平衡器一切似乎都正常工作,我看不出这有什么帮助。

更多环境信息

关于我正在测试的实验室环境的更多详细信息。

  • 我们的负载均衡器配置取自这里: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

相关内容