5. File-descriptor limitations

  1. In order to ensure that all incoming connections will successfully be served,
  2. HAProxy computes at load time the total number of file descriptors that will be
  3. needed during the process's life. A regular Unix process is generally granted
  4. 1024 file descriptors by default, and a privileged process can raise this limit
  5. itself. This is one reason for starting HAProxy as root and letting it adjust
  6. the limit. The default limit of 1024 file descriptors roughly allow about 500
  7. concurrent connections to be processed. The computation is based on the global
  8. maxconn parameter which limits the total number of connections per process, the
  9. number of listeners, the number of servers which have a health check enabled,
  10. the agent checks, the peers, the loggers and possibly a few other technical
  11. requirements. A simple rough estimate of this number consists in simply
  12. doubling the maxconn value and adding a few tens to get the approximate number
  13. of file descriptors needed.
  14.  
  15. Originally HAProxy did not know how to compute this value, and it was necessary
  16. to pass the value using the "ulimit-n" setting in the global section. This
  17. explains why even today a lot of configurations are seen with this setting
  18. present. Unfortunately it was often miscalculated resulting in connection
  19. failures when approaching maxconn instead of throttling incoming connection
  20. while waiting for the needed resources. For this reason it is important to
  21. remove any vestigial "ulimit-n" setting that can remain from very old versions.
  22.  
  23. Raising the number of file descriptors to accept even moderate loads is
  24. mandatory but comes with some OS-specific adjustments. First, the select()
  25. polling system is limited to 1024 file descriptors. In fact on Linux it used
  26. to be capable of handling more but since certain OS ship with excessively
  27. restrictive SELinux policies forbidding the use of select() with more than
  28. 1024 file descriptors, HAProxy now refuses to start in this case in order to
  29. avoid any issue at run time. On all supported operating systems, poll() is
  30. available and will not suffer from this limitation. It is automatically picked
  31. so there is nothing to do to get a working configuration. But poll's becomes
  32. very slow when the number of file descriptors increases. While HAProxy does its
  33. best to limit this performance impact (eg: via the use of the internal file
  34. descriptor cache and batched processing), a good rule of thumb is that using
  35. poll() with more than a thousand concurrent connections will use a lot of CPU.
  36.  
  37. For Linux systems base on kernels 2.6 and above, the epoll() system call will
  38. be used. It's a much more scalable mechanism relying on callbacks in the kernel
  39. that guarantee a constant wake up time regardless of the number of registered
  40. monitored file descriptors. It is automatically used where detected, provided
  41. that HAProxy had been built for one of the Linux flavors. Its presence and
  42. support can be verified using "haproxy -vv".
  43.  
  44. For BSD systems which support it, kqueue() is available as an alternative. It
  45. is much faster than poll() and even slightly faster than epoll() thanks to its
  46. batched handling of changes. At least FreeBSD and OpenBSD support it. Just like
  47. with Linux's epoll(), its support and availability are reported in the output
  48. of "haproxy -vv".
  49.  
  50. Having a good poller is one thing, but it is mandatory that the process can
  51. reach the limits. When HAProxy starts, it immediately sets the new process's
  52. file descriptor limits and verifies if it succeeds. In case of failure, it
  53. reports it before forking so that the administrator can see the problem. As
  54. long as the process is started by as root, there should be no reason for this
  55. setting to fail. However, it can fail if the process is started by an
  56. unprivileged user. If there is a compelling reason for *not* starting haproxy
  57. as root (eg: started by end users, or by a per-application account), then the
  58. file descriptor limit can be raised by the system administrator for this
  59. specific user. The effectiveness of the setting can be verified by issuing
  60. "ulimit -n" from the user's command line. It should reflect the new limit.
  61.  
  62. Warning: when an unprivileged user's limits are changed in this user's account,
  63. it is fairly common that these values are only considered when the user logs in
  64. and not at all in some scripts run at system boot time nor in crontabs. This is
  65. totally dependent on the operating system, keep in mind to check "ulimit -n"
  66. before starting haproxy when running this way. The general advice is never to
  67. start haproxy as an unprivileged user for production purposes. Another good
  68. reason is that it prevents haproxy from enabling some security protections.
  69.  
  70. Once it is certain that the system will allow the haproxy process to use the
  71. requested number of file descriptors, two new system-specific limits may be
  72. encountered. The first one is the system-wide file descriptor limit, which is
  73. the total number of file descriptors opened on the system, covering all
  74. processes. When this limit is reached, accept() or socket() will typically
  75. return ENFILE. The second one is the per-process hard limit on the number of
  76. file descriptors, it prevents setrlimit() from being set higher. Both are very
  77. dependent on the operating system. On Linux, the system limit is set at boot
  78. based on the amount of memory. It can be changed with the "fs.file-max" sysctl.
  79. And the per-process hard limit is set to 1048576 by default, but it can be
  80. changed using the "fs.nr_open" sysctl.
  81.  
  82. File descriptor limitations may be observed on a running process when they are
  83. set too low. The strace utility will report that accept() and socket() return
  84. "-1 EMFILE" when the process's limits have been reached. In this case, simply
  85. raising the "ulimit-n" value (or removing it) will solve the problem. If these
  86. system calls return "-1 ENFILE" then it means that the kernel's limits have
  87. been reached and that something must be done on a system-wide parameter. These
  88. trouble must absolutely be addressed, as they result in high CPU usage (when
  89. accept() fails) and failed connections that are generally visible to the user.
  90. One solution also consists in lowering the global maxconn value to enforce
  91. serialization, and possibly to disable HTTP keep-alive to force connections
  92. to be released and reused faster.