

Moodle是一个广受欢迎的开源在线学习平台。特别是自 COVID-19 大流行开始以来,Moodle 对学校和大学的重要性进一步增加。在德国的一些州,所有学校都必须在几天内切换到 Moodle 和 类似于BigBlueButton的其他平台。如果突然有成千上万的学生需要访问 Moodle,这可能会引起可扩展性方面的问题。
除了扩展 Moodle 应用程序本身之外,还需要考虑数据库方面的问题。Moodle 的数据库选项之一是PostgreSQL。在这篇博文中,我们向大家展示了使用PostgreSQL的Moodle平台的负载平衡选项。
通过Patroni实现高可用
从教育系统的角度来看,在线学习平台可以被视为关键基础设施,并且应该实现高可用,特别是在数据库方面。PostgreSQL高可用方面的一个很好的解决方案是Patroni,我们过去曾报道过它在Debian系统上的集成。
简而言之,Patroni使用分布式一致性存储 (DCS) 从典型的3节点集群中选举主节点,或者启动故障转移并在主节点故障的情况下选举新的主节点,并且不会发生裂脑的场景。此外,Patroni提供了REST API用于节点之间的通信以及patronictl程序之间的通信,例如在线更改所有节点上的Postgres 配置或着启动一次切换。
高可用客户端解决方案
然而,从Moodle的角度来看,必须额外确保平台连接到主节点,否则写入事务不能成功。传统的高可用解决方案如 Pacemaker在这里使用虚拟IP (VIPs),在发生故障转移时将虚拟IP转移到新的主节点。对于Patroni来说使用的是vip-manager,它监控DCS中的领导者密钥并在本地设置或删除集群VIP。这也已经被集成到Debian系统中。
另一种选择是使用基于PostgreSQL的libpq库的客户端故障转移。在这种方法中,所有集群成员都列在连接字符串中,并且连接选项中添加target_session_attrs=read-write的设置。以这种方式配置,如果一个连接断开,客户端将尝试到达其他节点,直到找到新的主节点。
另一个选择是HAProxy,一个高度可扩展的TCP/HTTP 负载平衡器。通过对每个节点的Patroni的REST API执行定期健康检查,它可以确定当前的领导者并将客户端查询转发给它。
Moodle平台数据库配置
Moodle平台与PostgreSQL数据库的连接是在config.php中配置的,例如对于单节点数据库配置如下:
$CFG->dbtype = 'pgsql';
$CFG->dblibrary = 'native';
$CFG->dbhost = '192.168.1.1';
$CFG->dbname = 'moodle';
$CFG->dbuser = 'moodle';
$CFG->dbpass = 'moodle';
$CFG->prefix = 'mdl_';
$CFG->dboptions = array (
'dbport' => '',
'dbsocket' => ''
);
此处使用默认端口 5432。
如果使用流复制,备节点数据库可以定义为readonly并分配给自己的数据库用户(该用户只需要读权限):
$CFG->dboptions = array (
[...]
'readonly' => [
'instance' => [
[
'dbhost' => '192.168.1.2',
'dbport' => '',
'dbuser' => 'moodle_safereads',
'dbpass' => 'moodle'
],
[
'dbhost' => '192.168.1.3',
'dbport' => '',
'dbuser' => 'moodle_safereads',
'dbpass' => 'moodle'
]
]
]
);
使用libpq实现故障转移/负载平衡
如果是使用Patroni构筑的高可用Postgres集群,正如前面介绍的,当发生故障转移或切换事件时,主节点可自动切换,从而防止数据丢失或系统关闭。Moodle不提供在此处设置通用数据库选项的方法,因此target_session_attrs=read-write无法直接设置。因此我们为此开发了一个补丁并在 Moodle 跟踪器中实现了它。通过在$CFG->dboptions数组中增加额外的选项'dbfailover' => 1,实现了必要的连接选项target_session_attrs=read-write。一个定制化的config.php看起来像这样:
$CFG->dbtype = 'pgsql';
$CFG->dblibrary = 'native';
$CFG->dbhost = '192.168.1.1,192.168.1.2,192.168.1.3';
$CFG->dbname = 'moodle';
$CFG->dbuser = 'moodle';
$CFG->dbpass = 'moodle';
$CFG->prefix = 'mdl_';
$CFG->dboptions = array (
'dbfailover' => 1,
'dbport' => '',
'dbsocket' => '',
'readonly' => [
'instance' => [
[
'dbhost' => '192.168.1.1',
'dbport' => '',
'dbuser' => 'moodle_safereads',
'dbpass' => 'moodle'
],
[
'dbhost' => '192.168.1.2',
'dbport' => '',
'dbuser' => 'moodle_safereads',
'dbpass' => 'moodle'
],
[
'dbhost' => '192.168.1.3',
'dbport' => '',
'dbuser' => 'moodle_safereads',
'dbpass' => 'moodle'
]
]
]
);
使用HAProxy实现故障转移/负载平衡
如果使用HAProxy替代时,假设HAProxy在Moodle服务器上本地运行,则$CFG->dbhost必须设置为HAProxy主机地址,例如127.0.0.1。此外可以为读取查询定义第二个端口(例如65432),同上面的流复制备节点配置相同,我们在$CFG->dboptions其配置为readonly。这种情况下config.php配置如下:
$CFG->dbtype = 'pgsql';
$CFG->dblibrary = 'native';
$CFG->dbhost = '127.0.0.1';
$CFG->dbname = 'moodle';
$CFG->dbuser = 'moodle';
$CFG->dbpass = 'moodle';
$CFG->prefix = 'mdl_';
$CFG->dboptions = array (
'dbport' => '',
'dbsocket' => '',
'readonly' => [
'instance' => [
'dbhost' => '127.0.0.1',
'dbport' => '65432',
'dbuser' => 'moodle_safereads',
'dbpass' => 'moodle'
]
]
);
HAProxy 配置文件haproxy.cfg示例如下:
global
maxconn 100
defaults
log global
mode tcp
retries 2
timeout client 30m
timeout connect 4s
timeout server 30m
timeout check 5s
listen stats
mode http
bind *:7000
stats enable
stats uri /
listen postgres_write
bind *:5432
mode tcp
option httpchk
http-check expect status 200
default-server inter 3s fall 3 rise 3 on-marked-down shutdown-sessions
server pg1 192.168.1.1:5432 maxconn 100 check port 8008
server pg2 192.168.1.2:5432 maxconn 100 check port 8008
server pg3 192.168.1.3:5432 maxconn 100 check port 8008
HAProxy期望在端口5432上传入写入连接 ( postgres_write) 并将它们转发到集群成员的5432端口上。主节点由端口 8008(默认的 Patroni REST API端口)上的 HTTP 检查确定;Patroni 在这里为主节点返回状态200,为备节点返回状态503。
对于读取查询 (postgres_read),必须决定主节点是否也应提供只读查询。如果是这种情况,可以使用简单的Postgres检查(pgsql-check);但是这可能会导致 PostgreSQL 日志中出现有关不正确或不完整登录的条目:
listen postgres_read
bind *:65432
mode tcp
balance leastconn
option pgsql-check user haproxy
default-server inter 3s fall 3 rise 3 on-marked-down shutdown-sessions
server pg1 192.168.1.1:5432 check
server pg2 192.168.1.2:5432 check
server pg3 192.168.1.3:5432 check
如果您不希望主节点参与读取扩展,您可以简单地使用与postgres_write节中相同的HTTP检查,这次期待HTTP的状态为503:
listen postgres_read
bind *:65432
mode tcp
balance leastconn
option httpchk
http-check expect status 503
default-server inter 3s fall 3 rise 3 on-marked-down shutdown-sessions
server pg1 192.168.1.1:5432 check port 8008
server pg2 192.168.1.2:5432 check port 8008
server pg3 192.168.1.3:5432 check port 8008
修订的 Ansible 剧本
HAProxy的支持已在我们的Ansible playbooks 0.3 版中实现,用于在 Debian上自动建立三节点PostgreSQL Patroni集群。新变量haproxy_primary_read_scale可用于决定HAProxy是否也将在只读端口接收到的事务请求发送到主节点或着仅发送到备节点。
我们很乐意提供帮助!
无论是 PostgreSQL、Patroni、HAProxy、Moodle 还是任何其他开源软件;credativ GmbH 在开源领域拥有超过22年的开发和服务经验,可以为您提供无与伦比的个性化定制支持。我们随时为您提供帮助和协助,满足您的所有开源基础架构需求。







