mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-10 03:23:29 +03:00
nixos/services.mysql: add galera cluster options (#388978)
And add release notes for new option. Co-authored-by: Arne Keller <arne.keller@posteo.de>
This commit is contained in:
parent
59a953f529
commit
cac3bdab21
3 changed files with 199 additions and 47 deletions
|
@ -379,6 +379,21 @@
|
||||||
|
|
||||||
- [`system.stateVersion`](#opt-system.stateVersion) is now validated and must be in the `"YY.MM"` format, ideally corresponding to a prior NixOS release.
|
- [`system.stateVersion`](#opt-system.stateVersion) is now validated and must be in the `"YY.MM"` format, ideally corresponding to a prior NixOS release.
|
||||||
|
|
||||||
|
- `services.mysql` now supports easy cluster setup via [`services.mysql.galeraCluster`](#opt-services.mysql.galeraCluster.enable) option.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.mysql = {
|
||||||
|
enable = true;
|
||||||
|
galeraCluster = {
|
||||||
|
enable = true;
|
||||||
|
localName = "Node 1";
|
||||||
|
localAddress = "galera_01";
|
||||||
|
nodeAddresses = [ "galera_01" "galera_02" "galera_03"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
- [`services.geoclue2`](#opt-services.geoclue2.enable) now has an `enableStatic` option, which allows the NixOS configuration to specify a fixed location for GeoClue to use.
|
- [`services.geoclue2`](#opt-services.geoclue2.enable) now has an `enableStatic` option, which allows the NixOS configuration to specify a fixed location for GeoClue to use.
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,83 @@ in
|
||||||
description = "Port number on which the MySQL master server runs.";
|
description = "Port number on which the MySQL master server runs.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
galeraCluster = {
|
||||||
|
enable = lib.mkEnableOption "MariaDB Galera Cluster";
|
||||||
|
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
description = "The MariaDB Galera package that provides the shared library 'libgalera_smm.so' required for cluster functionality.";
|
||||||
|
default = lib.literalExpression "pkgs.mariadb-galera";
|
||||||
|
};
|
||||||
|
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "The logical name of the Galera cluster. All nodes in the same cluster must use the same name.";
|
||||||
|
default = "galera";
|
||||||
|
};
|
||||||
|
|
||||||
|
sstMethod = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"rsync"
|
||||||
|
"mariabackup"
|
||||||
|
];
|
||||||
|
description = "Method for the initial state transfer (wsrep_sst_method) when a node joins the cluster. Be aware that rsync needs SSH keys to be generated and authorized on all nodes!";
|
||||||
|
default = "rsync";
|
||||||
|
example = "mariabackup";
|
||||||
|
};
|
||||||
|
|
||||||
|
localName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "The unique name that identifies this particular node within the cluster. Each node must have a different name.";
|
||||||
|
example = "node1";
|
||||||
|
};
|
||||||
|
|
||||||
|
localAddress = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "IP address or hostname of this node that will be used for cluster communication. Must be reachable by all other nodes.";
|
||||||
|
example = "1.2.3.4";
|
||||||
|
default = cfg.galeraCluster.localName;
|
||||||
|
defaultText = lib.literalExpression "config.services.mysql.galeraCluster.localName";
|
||||||
|
};
|
||||||
|
|
||||||
|
nodeAddresses = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "IP addresses or hostnames of all nodes in the cluster, including this node. This is used to construct the default clusterAddress connection string.";
|
||||||
|
example = lib.literalExpression ''["10.0.0.10" "10.0.0.20" "10.0.0.30"]'';
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
clusterPassword = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Optional password for securing cluster communications. If provided, it will be used in the clusterAddress for authentication between nodes.";
|
||||||
|
example = "SomePassword";
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
clusterAddress = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Full Galera cluster connection string. If nodeAddresses is set, this will be auto-generated, but you can override it with a custom value. Format is typically 'gcomm://node1,node2,node3' with optional parameters.";
|
||||||
|
example = "gcomm://10.0.0.10,10.0.0.20,10.0.0.30?gmcast.seg=1:SomePassword";
|
||||||
|
default =
|
||||||
|
if (cfg.galeraCluster.nodeAddresses == [ ]) then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
"gcomm://${builtins.concatStringsSep "," cfg.galeraCluster.nodeAddresses}"
|
||||||
|
+ lib.optionalString (
|
||||||
|
cfg.galeraCluster.clusterPassword != ""
|
||||||
|
) "?gmcast.seg=1:${cfg.galeraCluster.clusterPassword}";
|
||||||
|
defaultText = lib.literalExpression ''
|
||||||
|
if (config.services.mysql.galeraCluster.nodeAddresses == [ ]) then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
"gcomm://''${builtins.concatStringsSep \",\" config.services.mysql.galeraCluster.nodeAddresses}"
|
||||||
|
+ lib.optionalString (config.services.mysql.galeraCluster.clusterPassword != "")
|
||||||
|
"?gmcast.seg=1:''${config.services.mysql.galeraCluster.clusterPassword}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -327,6 +404,34 @@ in
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !cfg.galeraCluster.enable || isMariaDB;
|
||||||
|
message = "'services.mysql.galeraCluster.enable' expect services.mysql.package to be an mariadb variant";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
!cfg.galeraCluster.enable
|
||||||
|
|| (
|
||||||
|
cfg.galeraCluster.localAddress != ""
|
||||||
|
&& (cfg.galeraCluster.nodeAddresses != [ ] || cfg.galeraCluster.clusterAddress != "")
|
||||||
|
);
|
||||||
|
message = "mariadb galera cluster is enabled but the localAddress and (nodeAddresses or clusterAddress) are not set";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = !(cfg.galeraCluster.clusterAddress != "" && cfg.galeraCluster.clusterPassword != "");
|
||||||
|
message = "mariadb galera clusterPassword is set but overwritten by clusterAddress";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
!(
|
||||||
|
cfg.galeraCluster.enable
|
||||||
|
&& cfg.galeraCluster.nodeAddresses != [ ]
|
||||||
|
&& cfg.galeraCluster.clusterAddress != ""
|
||||||
|
);
|
||||||
|
message = "When services.mysql.galeraCluster.clusterAddress is set, setting services.mysql.galeraCluster.nodeAddresses is redundant and will be overwritten by clusterAddress. Choose one approach.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
services.mysql.dataDir = lib.mkDefault (
|
services.mysql.dataDir = lib.mkDefault (
|
||||||
if lib.versionAtLeast config.system.stateVersion "17.09" then "/var/lib/mysql" else "/var/mysql"
|
if lib.versionAtLeast config.system.stateVersion "17.09" then "/var/lib/mysql" else "/var/mysql"
|
||||||
|
@ -351,8 +456,38 @@ in
|
||||||
(lib.mkIf (!isMariaDB) {
|
(lib.mkIf (!isMariaDB) {
|
||||||
plugin-load-add = [ "auth_socket.so" ];
|
plugin-load-add = [ "auth_socket.so" ];
|
||||||
})
|
})
|
||||||
|
(lib.mkIf cfg.galeraCluster.enable {
|
||||||
|
# Ensure Only InnoDB is used as galera clusters can only work with them
|
||||||
|
enforce_storage_engine = "InnoDB";
|
||||||
|
default_storage_engine = "InnoDB";
|
||||||
|
|
||||||
|
# galera only support this binlog format
|
||||||
|
binlog-format = "ROW";
|
||||||
|
|
||||||
|
bind_address = lib.mkDefault "0.0.0.0";
|
||||||
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
services.mysql.settings.galera = lib.optionalAttrs cfg.galeraCluster.enable {
|
||||||
|
wsrep_on = "ON";
|
||||||
|
wsrep_debug = lib.mkDefault "NONE";
|
||||||
|
wsrep_retry_autocommit = lib.mkDefault "3";
|
||||||
|
wsrep_provider = "${cfg.galeraCluster.package}/lib/galera/libgalera_smm.so";
|
||||||
|
|
||||||
|
wsrep_cluster_name = cfg.galeraCluster.name;
|
||||||
|
wsrep_cluster_address = cfg.galeraCluster.clusterAddress;
|
||||||
|
|
||||||
|
wsrep_node_address = cfg.galeraCluster.localAddress;
|
||||||
|
wsrep_node_name = "${cfg.galeraCluster.localName}";
|
||||||
|
|
||||||
|
# SST method using rsync
|
||||||
|
wsrep_sst_method = lib.mkDefault cfg.galeraCluster.sstMethod;
|
||||||
|
wsrep_sst_auth = lib.mkDefault "check_repl:check_pass";
|
||||||
|
|
||||||
|
binlog_format = "ROW";
|
||||||
|
innodb_autoinc_lock_mode = 2;
|
||||||
|
};
|
||||||
|
|
||||||
users.users = lib.optionalAttrs (cfg.user == "mysql") {
|
users.users = lib.optionalAttrs (cfg.user == "mysql") {
|
||||||
mysql = {
|
mysql = {
|
||||||
description = "MySQL server user";
|
description = "MySQL server user";
|
||||||
|
@ -384,11 +519,29 @@ in
|
||||||
|
|
||||||
unitConfig.RequiresMountsFor = cfg.dataDir;
|
unitConfig.RequiresMountsFor = cfg.dataDir;
|
||||||
|
|
||||||
path = [
|
path =
|
||||||
# Needed for the mysql_install_db command in the preStart script
|
[
|
||||||
# which calls the hostname command.
|
# Needed for the mysql_install_db command in the preStart script
|
||||||
pkgs.nettools
|
# which calls the hostname command.
|
||||||
];
|
pkgs.nettools
|
||||||
|
]
|
||||||
|
# tools 'wsrep_sst_rsync' needs
|
||||||
|
++ lib.optionals cfg.galeraCluster.enable [
|
||||||
|
cfg.package
|
||||||
|
pkgs.bash
|
||||||
|
pkgs.gawk
|
||||||
|
pkgs.gnutar
|
||||||
|
pkgs.gzip
|
||||||
|
pkgs.inetutils
|
||||||
|
pkgs.iproute2
|
||||||
|
pkgs.netcat
|
||||||
|
pkgs.procps
|
||||||
|
pkgs.pv
|
||||||
|
pkgs.rsync
|
||||||
|
pkgs.socat
|
||||||
|
pkgs.stunnel
|
||||||
|
pkgs.which
|
||||||
|
];
|
||||||
|
|
||||||
preStart =
|
preStart =
|
||||||
if isMariaDB then
|
if isMariaDB then
|
||||||
|
@ -581,6 +734,17 @@ in
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Open firewall ports for MySQL (and Galera)
|
||||||
|
networking.firewall.allowedTCPPorts = lib.optionals cfg.galeraCluster.enable [
|
||||||
|
3306 # MySQL
|
||||||
|
4567 # Galera Cluster
|
||||||
|
4568 # Galera IST
|
||||||
|
4444 # SST
|
||||||
|
];
|
||||||
|
networking.firewall.allowedUDPPorts = lib.optionals cfg.galeraCluster.enable [
|
||||||
|
4567 # Galera Cluster
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.maintainers = [ lib.maintainers._6543 ];
|
meta.maintainers = [ lib.maintainers._6543 ];
|
||||||
|
|
|
@ -58,30 +58,6 @@ let
|
||||||
extraHosts = lib.concatMapStringsSep "\n" (i: "192.168.1.${toString i} galera_0${toString i}") (
|
extraHosts = lib.concatMapStringsSep "\n" (i: "192.168.1.${toString i} galera_0${toString i}") (
|
||||||
lib.range 1 6
|
lib.range 1 6
|
||||||
);
|
);
|
||||||
firewall.allowedTCPPorts = [
|
|
||||||
3306
|
|
||||||
4444
|
|
||||||
4567
|
|
||||||
4568
|
|
||||||
];
|
|
||||||
firewall.allowedUDPPorts = [ 4567 ];
|
|
||||||
};
|
|
||||||
systemd.services.mysql = with pkgs; {
|
|
||||||
path = with pkgs; [
|
|
||||||
bash
|
|
||||||
gawk
|
|
||||||
gnutar
|
|
||||||
gzip
|
|
||||||
inetutils
|
|
||||||
iproute2
|
|
||||||
netcat
|
|
||||||
procps
|
|
||||||
pv
|
|
||||||
rsync
|
|
||||||
socat
|
|
||||||
stunnel
|
|
||||||
which
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
services.mysql = {
|
services.mysql = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -101,27 +77,24 @@ let
|
||||||
FLUSH PRIVILEGES;
|
FLUSH PRIVILEGES;
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
galeraCluster = {
|
||||||
|
enable = true;
|
||||||
|
package = galeraPackage;
|
||||||
|
sstMethod = method;
|
||||||
|
|
||||||
|
localAddress = address;
|
||||||
|
localName = "galera_0${toString id}";
|
||||||
|
|
||||||
|
clusterAddress =
|
||||||
|
"gcomm://"
|
||||||
|
+ lib.optionalString (id == 2 || id == 3) "galera_01,galera_02,galera_03"
|
||||||
|
+ lib.optionalString (id == 5 || id == 6) "galera_04,galera_05,galera_06";
|
||||||
|
};
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
mysqld = {
|
|
||||||
bind_address = "0.0.0.0";
|
|
||||||
};
|
|
||||||
galera = {
|
galera = {
|
||||||
wsrep_on = "ON";
|
|
||||||
wsrep_debug = "NONE";
|
wsrep_debug = "NONE";
|
||||||
wsrep_retry_autocommit = "3";
|
|
||||||
wsrep_provider = "${galeraPackage}/lib/galera/libgalera_smm.so";
|
|
||||||
wsrep_cluster_address =
|
|
||||||
"gcomm://"
|
|
||||||
+ lib.optionalString (id == 2 || id == 3) "galera_01,galera_02,galera_03"
|
|
||||||
+ lib.optionalString (id == 5 || id == 6) "galera_04,galera_05,galera_06";
|
|
||||||
wsrep_cluster_name = "galera";
|
|
||||||
wsrep_node_address = address;
|
|
||||||
wsrep_node_name = "galera_0${toString id}";
|
|
||||||
wsrep_sst_method = method;
|
|
||||||
wsrep_sst_auth = "check_repl:check_pass";
|
|
||||||
binlog_format = "ROW";
|
|
||||||
enforce_storage_engine = "InnoDB";
|
|
||||||
innodb_autoinc_lock_mode = "2";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue