]>
Commit | Line | Data |
---|---|---|
9f6a7862 IB |
1 | { pkgs, config, lib, ... }: |
2 | let | |
3 | cfg = config.myServices.databasesReplication.mariadb; | |
4 | in | |
5 | { | |
6 | options.myServices.databasesReplication.mariadb = { | |
7 | enable = lib.mkEnableOption "Enable mariadb replication"; | |
8 | base = lib.mkOption { | |
9 | type = lib.types.path; | |
10 | description = '' | |
11 | Base path to put the replications | |
12 | ''; | |
13 | }; | |
14 | hosts = lib.mkOption { | |
15 | default = {}; | |
16 | description = '' | |
17 | Hosts to backup | |
18 | ''; | |
19 | type = lib.types.attrsOf (lib.types.submodule { | |
20 | options = { | |
21 | package = lib.mkOption { | |
22 | type = lib.types.package; | |
23 | default = pkgs.mariadb; | |
24 | description = '' | |
25 | Mariadb package for this host | |
26 | ''; | |
27 | }; | |
28 | serverId = lib.mkOption { | |
29 | type = lib.types.int; | |
30 | description = '' | |
31 | Server id to use for replication cluster (must be unique among the cluster!) | |
32 | ''; | |
33 | }; | |
34 | host = lib.mkOption { | |
35 | type = lib.types.str; | |
36 | description = '' | |
37 | Host to connect to | |
38 | ''; | |
39 | }; | |
40 | port = lib.mkOption { | |
41 | type = lib.types.str; | |
42 | description = '' | |
43 | Port to connect to | |
44 | ''; | |
45 | }; | |
46 | user = lib.mkOption { | |
47 | type = lib.types.str; | |
48 | description = '' | |
49 | User to connect as | |
50 | ''; | |
51 | }; | |
52 | password = lib.mkOption { | |
53 | type = lib.types.str; | |
54 | description = '' | |
55 | Password to use | |
56 | ''; | |
57 | }; | |
58 | dumpUser = lib.mkOption { | |
59 | type = lib.types.str; | |
60 | description = '' | |
61 | User who can do a dump | |
62 | ''; | |
63 | }; | |
64 | dumpPassword = lib.mkOption { | |
65 | type = lib.types.str; | |
66 | description = '' | |
67 | Password for the dump user | |
68 | ''; | |
69 | }; | |
70 | }; | |
71 | }); | |
72 | }; | |
73 | }; | |
74 | ||
75 | config = lib.mkIf cfg.enable { | |
76 | users.users.mysql = { | |
77 | description = "MySQL server user"; | |
78 | group = "mysql"; | |
79 | uid = config.ids.uids.mysql; | |
80 | extraGroups = [ "keys" ]; | |
81 | }; | |
82 | users.groups.mysql.gid = config.ids.gids.mysql; | |
83 | ||
84 | secrets.keys = lib.flatten (lib.mapAttrsToList (name: hcfg: [ | |
85 | { | |
86 | dest = "mysql_replication/${name}/slave_init_commands"; | |
87 | user = "mysql"; | |
88 | group = "mysql"; | |
89 | permissions = "0400"; | |
90 | text = '' | |
91 | CHANGE MASTER TO master_host="${hcfg.host}", master_port=${hcfg.port}, master_user="${hcfg.user}", master_password="${hcfg.password}", master_ssl=1, master_use_gtid=slave_pos; | |
92 | START SLAVE; | |
93 | ''; | |
94 | } | |
95 | { | |
96 | dest = "mysql_replication/${name}/mysqldump_remote"; | |
97 | permissions = "0400"; | |
98 | user = "root"; | |
99 | group = "root"; | |
100 | text = '' | |
101 | [mysqldump] | |
102 | user = ${hcfg.user} | |
103 | password = ${hcfg.password} | |
104 | ''; | |
105 | } | |
106 | { | |
107 | dest = "mysql_replication/${name}/mysqldump"; | |
108 | permissions = "0400"; | |
109 | user = "root"; | |
110 | group = "root"; | |
111 | text = '' | |
112 | [mysqldump] | |
113 | user = ${hcfg.dumpUser} | |
114 | password = ${hcfg.dumpPassword} | |
115 | ''; | |
116 | } | |
6015a3b5 IB |
117 | { |
118 | dest = "mysql_replication/${name}/client"; | |
119 | permissions = "0400"; | |
120 | user = "mysql"; | |
121 | group = "mysql"; | |
122 | text = '' | |
123 | [client] | |
124 | user = ${hcfg.dumpUser} | |
125 | password = ${hcfg.dumpPassword} | |
126 | ''; | |
127 | } | |
9f6a7862 IB |
128 | ]) cfg.hosts); |
129 | ||
130 | services.cron = { | |
131 | enable = true; | |
132 | systemCronJobs = lib.flatten (lib.mapAttrsToList (name: hcfg: | |
133 | let | |
134 | dataDir = "${cfg.base}/${name}/mysql"; | |
135 | backupDir = "${cfg.base}/${name}/mysql_backup"; | |
136 | backup_script = pkgs.writeScript "backup_mysql_${name}" '' | |
137 | #!${pkgs.stdenv.shell} | |
138 | ||
139 | set -euo pipefail | |
140 | ||
5868f9c6 | 141 | filename=${backupDir}/$(${pkgs.coreutils}/bin/date -Iminutes).sql |
9f6a7862 IB |
142 | ${hcfg.package}/bin/mysqldump \ |
143 | --defaults-file=${config.secrets.location}/mysql_replication/${name}/mysqldump \ | |
144 | -S /run/mysqld_${name}/mysqld.sock \ | |
145 | --gtid \ | |
146 | --master-data \ | |
147 | --flush-privileges \ | |
ef909e24 | 148 | --ignore-database=netdata \ |
5868f9c6 IB |
149 | --all-databases > $filename |
150 | ${pkgs.gzip}/bin/gzip $filename | |
9f6a7862 IB |
151 | ''; |
152 | u = pkgs.callPackage ./utils.nix {}; | |
5868f9c6 | 153 | cleanup_script = pkgs.writeScript "cleanup_mysql_${name}" (u.exponentialDumps "sql.gz" backupDir); |
9f6a7862 IB |
154 | in [ |
155 | "0 22,4,10,16 * * * root ${backup_script}" | |
156 | "0 3 * * * root ${cleanup_script}" | |
157 | ]) cfg.hosts); | |
158 | }; | |
159 | ||
160 | system.activationScripts = lib.attrsets.mapAttrs' (name: hcfg: | |
161 | lib.attrsets.nameValuePair "mysql_replication_${name}" { | |
162 | deps = [ "users" "groups" ]; | |
163 | text = '' | |
164 | install -m 0700 -o mysql -g mysql -d ${cfg.base}/${name}/mysql | |
165 | install -m 0700 -o mysql -g mysql -d ${cfg.base}/${name}/mysql_backup | |
166 | ''; | |
167 | }) cfg.hosts; | |
168 | ||
169 | environment.etc = lib.attrsets.mapAttrs' (name: hcfg: | |
170 | lib.attrsets.nameValuePair "mysql/${name}_my.cnf" { | |
171 | text = '' | |
172 | [mysqld] | |
5af06538 | 173 | skip-networking |
9f6a7862 IB |
174 | socket = /run/mysqld_${name}/mysqld.sock |
175 | datadir = ${cfg.base}/${name}/mysql/ | |
176 | log-bin = mariadb-bin | |
177 | server-id = ${builtins.toString hcfg.serverId} | |
178 | ''; | |
179 | } | |
180 | ) cfg.hosts; | |
181 | ||
182 | systemd.services = lib.attrsets.mapAttrs' (name: hcfg: | |
183 | let | |
184 | dataDir = "${cfg.base}/${name}/mysql"; | |
185 | in | |
186 | lib.attrsets.nameValuePair "mysql_backup_${name}" { | |
187 | description = "Mysql replication for ${name}"; | |
188 | wantedBy = [ "multi-user.target" ]; | |
189 | after = [ "network.target" ]; | |
190 | restartTriggers = [ config.environment.etc."mysql/${name}_my.cnf".source ]; | |
191 | unitConfig.RequiresMountsFor = dataDir; | |
192 | ||
193 | preStart = '' | |
194 | if ! test -e ${dataDir}/mysql; then | |
195 | ${hcfg.package}/bin/mysqldump \ | |
196 | --defaults-file=${config.secrets.location}/mysql_replication/${name}/mysqldump_remote \ | |
197 | -h ${hcfg.host} \ | |
198 | -P ${hcfg.port} \ | |
199 | --ssl \ | |
200 | --gtid \ | |
201 | --flush-privileges \ | |
202 | --master-data \ | |
203 | --all-databases > ${dataDir}/initial.sql | |
204 | ||
205 | ${hcfg.package}/bin/mysql_install_db \ | |
206 | --defaults-file=/etc/mysql/${name}_my.cnf \ | |
207 | --user=mysql \ | |
208 | --datadir=${dataDir} \ | |
209 | --basedir=${hcfg.package} | |
210 | fi | |
211 | ''; | |
212 | ||
213 | serviceConfig = { | |
214 | User = "mysql"; | |
215 | Group = "mysql"; | |
216 | RuntimeDirectory = "mysqld_${name}"; | |
217 | RuntimeDirectoryMode = "0755"; | |
218 | SupplementaryGroups = "keys"; | |
219 | PermissionsStartOnly = true; | |
220 | Type = "notify"; | |
221 | ||
222 | ExecStart = "${hcfg.package}/bin/mysqld --defaults-file=/etc/mysql/${name}_my.cnf --user=mysql --datadir=${dataDir} --basedir=${hcfg.package}"; | |
223 | ExecStartPost = | |
224 | let | |
225 | sql_before = pkgs.writeText "mysql-initial-before" '' | |
226 | DROP DATABASE test; | |
227 | ''; | |
228 | setupScript = pkgs.writeScript "mysql-setup" '' | |
229 | #!${pkgs.runtimeShell} -e | |
230 | ||
231 | if test -e ${dataDir}/initial.sql; then | |
232 | cat \ | |
233 | ${sql_before} \ | |
234 | ${dataDir}/initial.sql \ | |
235 | ${config.secrets.location}/mysql_replication/${name}/slave_init_commands \ | |
236 | | ${hcfg.package}/bin/mysql \ | |
237 | --defaults-file=/etc/mysql/${name}_my.cnf \ | |
238 | -S /run/mysqld_${name}/mysqld.sock \ | |
239 | --user=root | |
240 | rm -f ${dataDir}/initial.sql | |
241 | fi | |
242 | ''; | |
243 | in | |
244 | "+${setupScript}"; | |
245 | # initial dump can take a long time | |
246 | TimeoutStartSec="infinity"; | |
247 | TimeoutStopSec = 120; | |
248 | }; | |
249 | }) cfg.hosts; | |
250 | }; | |
251 | } | |
252 |