malc0mn/haproxy-config-builder

HAProxy 配置文件构建器和处理器。

1.3.4 2023-04-11 13:44 UTC

README

Build Status Latest Stable Version Total Downloads Latest Unstable Version License SensioLabsInsight

使用 composer 安装

打开 shell,cd 到您的项目目录,然后输入

composer require malc0mn/haproxy-config-builder

或编辑 composer.json 并添加

{
    "require": {
        "malc0mn/haproxy-config-builder": "~1.0"
    }
}

使用示例

从头创建
require 'vendor/autoload.php';

use HAProxy\Config\Comment;
use HAProxy\Config\Proxy\Backend;
use HAProxy\Config\Proxy\Frontend;
use HAProxy\Config\Proxy\Listen;
use HAProxy\Config\Userlist;
use HAProxy\Config\Config;

$comment = <<<TEXT
Simple configuration for an HTTP proxy listening on port 80 on all
interfaces and forwarding requests to a single backend "servers" with a
single server "server1" listening on 127.0.0.1:8000
TEXT;

$config = Config::create()
    ->addComment(
        new Comment($comment)
    )
    ->setDebug()
    ->setDaemon()
    ->addGlobal('maxconn', 256)
    ->addDefaults('mode', 'http')
    ->addDefaults('timeout', ['connect', '5000ms'])
    ->addDefaults('timeout', ['client', '50000ms'])
    ->addDefaults('timeout', ['server', '50000ms'])
    ->addUserlist(
        Userlist::create('developers')
            ->addUser('eddy', '$6$mlskxjmqlkcnmlcjsmdl', ['editor', 'admin'])
            ->addGroup('editor', [])
    )
    ->addFrontend(
        Frontend::create('http-in')
            ->bind('*', 80)
            ->addParameter('default_backend', 'servers')
            ->addAcl('login_page', ['url_beg', '/login'])
    )
    ->addBackend(
        Backend::create('servers')
            ->addServer('server1', '127.0.0.1', 8000, ['maxconn', 32])
    )
    ->addListen(
        Listen::create('ssh')
            ->addServer('ssh-host', '*', 22, 'maxconn 3')
    )
;

echo (string)$config;
从文件读取
require 'vendor/autoload.php';

use HAProxy\Config\Config;

$configFromFile = Config::fromFile('/etc/haproxy/haproxy.conf');

var_export($configFromFile);

输出排序

代理块内的关键词排序

默认情况下,构建器的输出将按照您添加参数的顺序打印。这并不总是所需的,尤其是在处理您希望在设置 use_backend 调用之前出现在输出中的 ACL 时。

要解决这个问题,您可以使用 setParameterOrder() 方法来指示所需的打印顺序。示例

<?php
require 'vendor/autoload.php';

use HAProxy\Config\Proxy\Frontend;

$frontend = Frontend::create('www_frontend')
    ->addParameter('mode', 'http')
    ->addParameter('default_backend', 'www_backend')
    ->bind('*', 80)
    ->addAcl('is_https', 'hdr(X-Forwarded-Proto) -i https')
    ->addAcl('is_host_com', 'hdr(Host) -i example.com')
    ->addUseBackend('host_com', 'if is_host_com')
    ->addParameter('option', 'forwardfor')
;

echo (string)$frontend;
/*
 frontend www_frontend
 mode            http
 default_backend www_backend
 bind            *:80
 acl             is_https hdr(X-Forwarded-Proto) -i https
 acl             is_host_com hdr(Host) -i example.com
 use_backend     host_com if is_host_com
 option          forwardfor
 */

$frontend->setParameterOrder(['bind', 'mode', 'option', 'acl', 'use_backend', 'default_backend']);

echo (string)$frontend;
/*
 frontend www_frontend
 bind            *:80
 mode            http
 option          forwardfor
 acl             is_https hdr(X-Forwarded-Proto) -i https
 acl             is_host_com hdr(Host) -i example.com
 use_backend     host_com if is_host_com
 default_backend www_backend
 */

// Whitespace control:
$frontend->setParameterOrder([
    'bind' => false,
    'mode' => false,
    'option' => true, // Add trailing whitespace!
    'acl' => true, // Add trailing whitespace!
    'use_backend' => true, // Add trailing whitespace!
    'default_backend',
]);

echo (string)$frontend;
/*
 frontend www_frontend
 bind            *:80
 mode            http
 option          forwardfor

 acl             is_https hdr(X-Forwarded-Proto) -i https
 acl             is_host_com hdr(Host) -i example.com

 use_backend     host_com if is_host_com

 default_backend www_backend
 */

配置文件中代理块的排序

代理块将根据其给定的优先级渲染,但有一些限制

  1. global 将始终首先渲染(第 1 位)。
  2. defaults 将始终第 2 位渲染。
  3. resolvers 将始终第 3 位渲染。
  4. userlist 将始终第 4 位渲染。
  5. 尝试在 defaults 上设置打印优先级将引发异常。

因此,您只能控制 backendfrontendlisten 代理块的打印优先级。默认优先级设置为 1000。您可以通过在所需的代理块上调用 setPrintPriority() 方法来更改优先级:较小的整数表示更高的优先级

require 'vendor/autoload.php';

use HAProxy\Config\Comment;
use HAProxy\Config\Proxy\Backend;
use HAProxy\Config\Proxy\Frontend;
use HAProxy\Config\Proxy\Listen;
use HAProxy\Config\Userlist;
use HAProxy\Config\Config;

$comment = <<<TEXT
Simple configuration for an HTTP proxy listening on port 80 on all
interfaces and forwarding requests to a single backend "servers" with a
single server "server1" listening on 127.0.0.1:8000
TEXT;

$config = Config::create()
    ->addComment(
        new Comment($comment)
    )
    ->setDebug()
    ->setDaemon()
    ->addGlobal('maxconn', 256)
    ->addDefaults('mode', 'http')
    ->addDefaults('timeout', ['connect', '5000ms'])
    ->addDefaults('timeout', ['client', '50000ms'])
    ->addDefaults('timeout', ['server', '50000ms'])
    ->addUserlist(
        Userlist::create('developers')
            ->addUser('eddy', '$6$mlskxjmqlkcnmlcjsmdl', ['editor', 'admin'])
            ->addGroup('editor', [])
    )
    ->addBackend(
        Backend::create('servers')
            ->addServer('server1', '127.0.0.1', 8000, ['maxconn', 32])
            ->setPrintPriority(1002)
    )
    ->addListen(
        Listen::create('ssh')
            ->addServer('ssh-host', '*', 22, 'maxconn 3')
    )
    ->addFrontend(
        Frontend::create('http-in')
            ->bind('*', 80)
            ->addParameter('default_backend', 'servers')
            ->addAcl('login_page', ['url_beg', '/login'])
            ->setPrintPriority(1001)
    )
;

echo (string)$config;
/*
 # Simple configuration for an HTTP proxy listening on port 80 on all
 # interfaces and forwarding requests to a single backend "servers" with a
 # single server "server1" listening on 127.0.0.1:8000
 global
     maxconn 256
     debug
     daemon

 defaults
     mode    http
     timeout connect 5000ms
     timeout client 50000ms
     timeout server 50000ms

 userlist developers
     group editor

     user eddy password $6$mlskxjmqlkcnmlcjsmdl groups editor,admin

 listen ssh
     server ssh-host *:22 maxconn 3

 frontend http-in
     bind            *:80
     default_backend servers
     acl             login_page url_beg /login

 backend servers
     server server1 127.0.0.1:8000 maxconn 32
 */

接下来做什么?

一旦您有了配置,您可以使用各种辅助方法来编程式地修改或更新配置。或者您可以使用这些辅助方法有条件地添加或删除设置...

require 'vendor/autoload.php';

use HAProxy\Config\Config;

$config = Config::fromFile('/etc/haproxy/haproxy.conf');

if ($config->frontendExists('www') && !$config->backendExists('www')) {
   $config->removeFrontend('www');
}

if ($config->listenExists('ssh')) {
   // Do stuff here.
}

查看类以了解您可以利用什么。查看测试将让您非常了解您可以使用所有可用的方法做什么。

更复杂的内容

如果您需要更复杂的规则,需要多个 use_backend 调用到 同一 后端,则可以使用 标记后端

考虑以下 HAProxy 前端代理块

frontend www_frontend
    bind *:8080
    mode http

    http-request set-header X-Original-Path %[path]

    acl is_host_website hdr(host) -i website.example.com
    acl is_host_api hdr(host) -i api.example.com

    acl is_path_admin hdr_beg(X-Original-Path) -i /admin
    acl is_path_api hdr_beg(X-Original-Path) -i /api

    # Path mappings MUST come first AND in separate use_backend statements.
    use_backend website if is_host_website is_path_admin
    use_backend api if is_host_webste is_path_api

    # Regular host mappings.
    use_backend website if is_host_website
    use_backend api if is_host_api

此示例使用路径映射将流量重定向到不同的后端。应该很明显,您不能写入此内容来实现相同的功能

frontend www_frontend
    bind *:8080
    mode http

    http-request set-header X-Original-Path %[path]

    acl is_host_website hdr(host) -i website.example.com
    acl is_host_api hdr(host) -i api.example.com

    acl is_path_admin hdr_beg(X-Original-Path) -i /admin
    acl is_path_api hdr_beg(X-Original-Path) -i /api

    use_backend website if is_host_website is_path_admin || is_host_website
    use_backend api if is_host_webste is_path_api || is_host_api

上述示例永远不会将您重定向到 API 后端,当调用 website.example.com/api url 时,因为第一个 use_backend 语句将

  1. 看到主机 website.example.com 且路径 不是 /admin,因此它将继续下一个条件
  2. 看到主机 website.example.com 并将其重定向到 website 后端。

要使用此库重新创建第一个正确示例,您可以使用 标记后端

$frontend = Frontend::create('www_frontend')
    ->bind('*', 8080)
    ->addParameter('mode', 'http')

    ->addParameter('http-request', 'set-header X-Original-Path %[path]')

    ->addAcl('is_host_website', 'hdr(host) -i website.example.com')
    ->addAcl('is_host_api', 'hdr(host) -i api.example.com')

    ->addAcl('is_path_admin', 'hdr_beg(X-Original-Path) -i /admin')
    ->addAcl('is_path_api', 'hdr_beg(X-Original-Path) -i /api')

    // Here come the 'tagged' backends.
    ->addUseBackendWithConditions(
        'website',
        ['is_host_website', 'is_path_admin'],
        'if', // This is the condition, 'if' is the default.
        'path_acl' // This is the tag.
    )
    ->addUseBackendWithConditions(
        'api',
        ['is_host_website', 'is_path_api'],
        'if', // This is the condition, 'if' is the default.
        'path_acl' // This is the tag.
    )

    // The 'regular' backends.
    ->addUseBackendWithConditions('website', ['is_host_website'])
    ->addUseBackendWithConditions('api', ['is_host_api'])
;

echo (string)$frontend;
/*
frontend www_frontend
bind         *:8080
mode         http
http-request set-header X-Original-Path %[path]
acl          is_host_website hdr(host) -i website.example.com
acl          is_host_api hdr(host) -i api.example.com
acl          is_path_admin hdr_beg(X-Original-Path) -i /admin
acl          is_path_api hdr_beg(X-Original-Path) -i /api
use_backend  website if is_host_website is_path_admin
use_backend  api if is_host_website is_path_api
use_backend  website if is_host_website
use_backend  api if is_host_api
*/

致谢

所使用的方法基于 romanpitak 的 Nginx 配置处理器