ha
45.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
++ Введение
В данной статье будет рассмотрен способ организации Linux HA кластера на базе широко распространённого аппаратного обеспечения. Для удобства дальнейшей эффективной эксплуатации и консолидации ресурсов предлагается использовать LXC.
Для большинства задач необходимо корректно реплицировать два типа данных:
** БД (с учётом хранящихся в памяти структур)
** Файлы
В рамках данной статьи предлагается использовать следующий набор ПО:
** lxc+ipw для изоляции окружений
** percona+galera для репликации БД
** clsync+rsync для репликации файлов
** csync2 для репликации конфигураций
++ HOWTO
++ Аппаратное обеспечение
Допустим, у вас в наличии имеется 3 сервера. 2 из них будут использоваться для HA-кластера, а 3-ий для системы резервного копирования.
Итого получается примерно следующая схема:
srv-0 <----> srv-1
| |
+-> backup <-+
В рамках данного примера назовём:
srv-0 — anubis
srv-1 — seth
++ Установка хост-систем
В качестве ОС для хост-систем применима любая популярная ОС на базе современных ядер Linux и GNU окружения. Однако данная схема была опробована нами только на базе Debian и Gentoo.
Мы использовали /opt для директорий реплицируемых скриптов, а /srv/lxc для контейнеров.
++ Репликация конфигураций хост-систем
Допустим, что необходимо большое множество vlan-ов для эффективной изоляции сервисов друг от друга. В таких ситуациях могут возникнуть определённые сложности, например:
** Долго загружается ОС, особенно Gentoo с "rc_parallel=yes" и hangup-ами (загружаться может часами).
** Огромные конфигурационные файлы, что постоянно вызывает ошибки по "человеческому фактору".
** Иногда нельзя синхронизировать конфигурационные файлы, так как необходимо использовать разные IP-адреса.
Данные проблемы возможно решить используя [[https://gitlab.ut.mephi.ru/ut/ipw ipw]], вместо стандартных init.d-скриптов настройки сети.
Все приведённые далее команды необходимо выполнять на обоих рабочих узлах.
Пример глобальной (синхронизируемой) конфигурации ipw:
cat > /etc/ipw.conf <<EOF
# vim: set filetype=sh:
ACCESS_IFACES=(eth2:zabbix eth3:mirrorport)
D1Q_IFACES=(bond0)
bond0_SLAVES=(eth0 eth1)
bond0_MODE=802.3ad
bond0_VLAN_N_BRIDGES=(
5:control
6:storage
7.1:dbrep-test
8.1:web-test
8.2:web-mail
8.3:web-billing
9:vpn
:dbcl-test
128
$(seq 2048 2176)
)
source /etc/ipw.conf.local
LXC_DIR='/srv/lxc'
EOF
Данная конфигурационный файл предполагает следующую сетевую конфигурацию:
- Интерфейсы eth0 и eth1 будут объединены в bonding-интерфейс bond0 под алгоритму 802.3ad.
- Интерфейс eth2 будет присоединенён в мост "zabbix", а eth3 будет присоединён в мост "mirrorport".
- Поверх интерфейса bond0 будет созданы dot1q-интерфейсы bond0.5, bond0.6, bond0.7, bond0.7.1, bond0.8, bond0.8.1, bond0.8.2, bond0.8.3, bond0.9.
- Будет выставлен MTU 1496 интерфейсам bond0.7.1, bond0.8.1, bond0.8.2, bond0.8.3.
- Интерфейс bond0.5 будет присоединенён в мост "control", а интерфейсы bond0.6, bond0.7.1, bond0.8.1, bond0.8.2, bond0.8.3, bond0.9 в мосты "storage", "dbrep-test", "web-test", "web-mail", "web-billing" и "vpn" соответственно.
- Будет создан мост "dbcl-test"
- Будет создан мост "vlan128", а также будет создан интерфейс bond0.128 и занесён в мост "vlan128".
- Будут созданы мосты с vlan2048 по vlan2176, а также будут созданы интерфейсы с bond0.2048 по bond0.2176 и занесены в мосты vlan2048 — vlan2176 соответственно.
Пример локальной (несинхронизируемой) конфигурации ipw:
cat > /etc/ipw.conf.local <<EOF
control_IP=(
'addr add 10.0.5.2/24'
'route add default via 10.0.5.1'
)
EOF
Данный конфигурационный файл предполагает запуск двух команд:
ip addr add 10.0.5.2/24 dev control
ip addr route add default via 10.0.5.1 dev control
В рамках предложенной ниже конфигурации csync2, файл /etc/ipw.conf будет синхронизироваться между узлами кластера, а /etc/ipw.conf.local не будет.
Устанавливаем сам ipw.
git -C /usr/local clone https://gitlab.ut.mephi.ru/ut/ipw
ln -s ../ipw/ipw /usr/local/bin/ipw
# Debian
ln -s ../../usr/local/ipw/debian/ipw /etc/init.d/ipw
update-rc.d ipw defaults
# Gentoo
ln -s ../../usr/local/ipw/gentoo/ipw /etc/init.d/ipw
ln -s ../ipw.conf /etc/conf.d/ipw
rc-update add ipw default
Для синхронизации конфигурационных файлов самым простым и удобным решением мне показалось использовать csync2. Примеры его конфигурации широко [[http://habrahabr.ru/post/120702/ распространены в Интернете]].
Пример конфигурационного файла:
group egypt {
host anubis seth;
key /etc/csync2/egypt.key;
include /root/.bashrc /etc/rc.local /etc/rc.conf /etc/local.d/* /etc/backup.* /etc/portage/*/* /etc/make.conf /etc/bash/* /etc/profile /etc/profile.env /etc/profile.d/* /etc/environment /etc/env.d/* /etc/clsync/* /etc/clsync/rules/* /etc/clsync/synchandler/*/* /etc/clsync/hooks/*/* /etc/init.d/lxc /etc/csync2/* /etc/sudoers /etc/sudoers.d/* /etc/hosts /etc/modules /usr/local/bin/* /var/spool/cron/crontabs/* /etc/cron.*/* /etc/sysctl.conf /etc/passwd /etc/group /etc/shadow /root/.ssh/*id /root/.ssh/authorized_keys /etc/ntp.conf /etc/ssh/ssh_config /etc/ssh/sshd_config /etc/rsync* /etc/conf.d/modules /etc/ipw.conf /etc/init.d/ipw /var/lib/layman/make.conf;
auto younger;
action {
pattern /etc/rsyncd.*;
exec "/etc/init.d/rsyncd restart";
logfile "/var/log/csync2_action.log";
do-local;
}
action {
pattern /etc/ssh/sshd_config;
exec "/etc/init.d/sshd restart";
logfile "/var/log/csync2_action.log";
do-local;
}
action {
pattern /etc/sysctl.conf;
exec "sysctl -f";
logfile "/var/log/csync2_action.log";
do-local;
}
action {
pattern /var/spool/cron/crontabs/* /etc/cron.*/*;
exec "/etc/init.d/vixie-cron restart";
logfile "/var/log/csync2_action.log";
do-local;
}
action {
pattern /etc/ntp.conf;
exec "/etc/init.d/ntpd restart";
logfile "/var/log/csync2_action.log";
do-local;
}
}
++ Окружение для работы с LXC
Для удобной работы с LXC в HA-кластерах имеется возможность использовать набор скриптов из репозитория [[https://github.com/mephi-ut/lxc-ha mephi-ut/lxc-ha на GitHub.com]]:
git -C /opt clone https://gitlab.ut.mephi.ru/ut/ipw
echo "PATH=/opt/lxc-ha/lxc/bin:$PATH" >> ~/.bashrc
exec bash
После этого необходимо обеспечить корректную работу данных скриптов:
** Если используется Debian:
mkdir /etc/csync2; ln -s ../csync2.conf /etc/csync2/csync2.conf
** Для дедупликации конфигураций осуществляется завязка на конфигурационный файл lxctl, который находится по пути «/etc/lxctl/lxctl.yaml». Если такой файл у вас отсутствует, то необходимо его создать и настроить. Пример настройки:
cat > /etc/lxctl/lxctl.yaml << EOF
---
lvm:
VG: vg00
paths:
YAML_CONFIG_PATH: '/etc/lxctl'
LXC_CONF_DIR: '/srv/lxc'
ROOT_MOUNT_PATH: '/usr/lib64/lxctl/rootfs'
TEMPLATE_PATH: '/usr/lib64/lxctl/templates'
LXC_LOG_PATH: '/var/log/lxc.log'
LXC_LOG_LEVEL: 'DEBUG'
check:
skip_kernel_config_check: 1
rsync:
RSYNC_OPTS: -aH --delete --numeric-ids --exclude 'proc/*' --exclude 'sys/*' -e ssh
root:
ROOT_SIZE: 50G
ROOT_TYPE: lvm
fs:
FS: ext4
FS_OPTS: -b 4096
FS_MOUNT_OPTS: defaults,barrier=0
os:
OS_TEMPLATE: debian-amd64
set:
SEARCHDOMAIN: ru
IFNAME: 'ip'
list:
COLUMNS: 'name,disk_free_mb,status,ip,hostname'
** Настройка ssh/sshd. К этому моменту csync2 уже настроен и упрощает дальнейшую задачу:
ssh-keygen -t ecdsa -b 521
cp ~/.ssh/id_ecdsa.pub ~/.ssh/authorized_keys
echo 'getroot:x:0:0::/root:/bin/bash' >> /etc/passwd
cat > /etc/ssh/sshd_config << EOF
# vim: set filetype=sshconfig:
ListenAddress 0.0.0.0
PasswordAuthentication no
UsePAM yes
PrintLastLog no
Subsystem sftp /usr/lib64/misc/sftp-server
UseDNS no
PermitRootLogin no
PasswordAuthentication yes
RSAAuthentication no
PubkeyAuthentication yes
RhostsRSAAuthentication no
HostbasedAuthentication no
AllowGroups sshusers
Match Group sysusers Address 10.0.5.0/24
MaxAuthTries 1
PasswordAuthentication no
PermitRootLogin yes
Match Group sysusers Address 127.0.0.0/8
MaxAuthTries 1
PasswordAuthentication no
PermitRootLogin yes
EOF
cat > /etc/ssh/ssh_config << EOF
TCPKeepAlive yes
ControlMaster auto
Host *
ControlPath ~/.ssh/master-%r@%h:%p
ConnectTimeout 1
EOF
groupadd sysusers
groupadd sshusers
gpasswd -a sysusers getroot
gpasswd -a sshusers getroot
gpasswd -a sshusers yourloginhere
В теории, csync2 должен автоматически отсинхронизировать файлы id_ecdsa и authorized_keys, что должно разрешить ssh без авторизации командой:
ssh getroot@IP_адрес_соседнего_узла
** Прописываем узлы в hosts:
echo 'anubis 10.0.5.2' >> /etc/hosts
echo 'seth 10.0.5.3' >> /etc/hosts
** Создаём скрипт backuphost:
echo 'echo backup' > /usr/local/bin/backuphost
chmod +x /usr/local/bin/backuphost
** Установка screen:
# Debian:
apt-get install screen
# Gentoo:
emerge screen
** Установка и настройка keepalived:
# Debian:
apt-get install keepalived
# Gentoo:
apt-get install keepalived
# Anubis:
cat > /etc/keepalived/keepalived.conf <<EOF
global_defs {
router_id anubis.example.org
}
vrrp_sync_group ANUBIS {
group {
ANUBIS
}
}
vrrp_instance ANUBIS {
state MASTER
interface tech
lvs_sync_daemon_interface tech
virtual_router_id 224
priority 200
authentication {
auth_type PASS
auth_pass somekeywordegqQw4K3Aj2x17kS4BIHw8SaoRcEb3k72O0sTY
}
virtual_ipaddress {
10.0.5.224/24 dev tech
}
virtual_routes {
}
notify_master /opt/lxc-ha/keepalived/bin/primary-master.sh
notify_backup /opt/lxc-ha/keepalived/bin/primary-slave.sh
}
vrrp_sync_group SETH {
group {
SETH
}
}
vrrp_instance SETH {
state SLAVE
interface tech
lvs_sync_daemon_interface tech
virtual_router_id 225
priority 100
authentication {
auth_type PASS
auth_pass somekeywordegqQw4K3Aj2x17kS4BIHw8SaoRcEb3k72O0sTY
}
virtual_ipaddress {
10.0.5.225/24 dev tech
}
virtual_routes {
}
notify_master /opt/lxc-ha/keepalived/bin/secondary-master.sh
notify_backup /opt/lxc-ha/keepalived/bin/secondary-slave.sh
}
EOF
# Seth:
cat > /etc/keepalived/keepalived.conf <<EOF
global_defs {
router_id poseidon.ut.mephi.ru
}
vrrp_sync_group ANUBIS {
group {
ANUBIS
}
}
vrrp_instance ANIBUS {
state SLAVE
interface control
lvs_sync_daemon_interface control
virtual_router_id 224
priority 100
authentication {
auth_type PASS
auth_pass somekeywordegqQw4K3Aj2x17kS4BIHw8SaoRcEb3k72O0sTY
}
virtual_ipaddress {
10.0.5.224/24 dev control
}
virtual_routes {
}
notify_master /opt/lxc-ha/keepalived/bin/secondary-master.sh
notify_backup /opt/lxc-ha/keepalived/bin/secondary-slave.sh
}
vrrp_sync_group SETH {
group {
SETH
}
}
vrrp_instance SETH {
state MASTER
interface control
lvs_sync_daemon_interface control
virtual_router_id 225
priority 200
authentication {
auth_type PASS
auth_pass somekeywordegqQw4K3Aj2x17kS4BIHw8SaoRcEb3k72O0sTY
}
virtual_ipaddress {
10.0.5.225/24 dev control
}
virtual_routes {
}
notify_master /opt/lxc-ha/keepalived/bin/primary-master.sh
notify_backup /opt/lxc-ha/keepalived/bin/primary-slave.sh
}
EOF
# Gentoo, both:
rc-update add keepalived default
** Добавляем lxc-runned-notify и lxc-setnameofhost в cron:
crontab -e # добавляем строки:
PATH=/opt/lxc-ha/lxc/bin:/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin
* * * * * /opt/lxc/bin/lxc-setnameofhost
* * * * * /opt/lxc/bin/lxc-runned-notify 2>/dev/null
** Определяем какое значение будет использоваться для 4-го октета MAC-адреса
echo 00 > /etc/hostmac
** Если Gentoo, то исправляем значение "lxc_path" в /usr/sbin/lxc-create (или делаем обёртку или делаем symlink) на
lxc_path=/srv/lxc
Грубо говоря, на этом можно закончить настройку самих хост-систем. Далее необходимо реализовать репликацию файлов и БД контейнеров.
++ Репликация файлов контейнеров
Для репликации файлов предлагается использовать [[https://github.com/xaionaro/clsync clsync]]:
# Debian:
apt-get install clsync
# Gentoo:
layman -a bircoph
emerge --sync
emerge =app-admin/clsync-0.3
# Both:
mkdir -p /etc/clsync/hooks/lxc /etc/clsync/rules /etc/clsync/synchandler/lxc
cat > /etc/clsync/clsync.conf << EOF
[lxc-rsyncshell]
watch-dir = /srv/lxc/%label%
background = 1
mode = rsyncshell
debug = 1
syslog = 1
full-initialsync = 1
rules-file = /etc/clsync/rules/lxc
retries = 3
ignore-exitcode = 23,24
[lxc-brother]
config-block-inherits = lxc-rsyncshell
delay-sync = 15
delay-collect = 15
sync-handler = /etc/clsync/synchandler/lxc/brother.sh
lists-dir = /dev/shm/clsync-%label%-brother
exit-hook = /etc/clsync/hooks/lxc/exit-brother.sh
pid-file = /var/run/clsync-%label%-brother.pid
status-file = /srv/lxc/%label%/clsync-brother.status
dump-dir = /tmp/clsyncdump-%label%-brother
threading = safe
[lxc-brother-atomic-sync]
config-block-inherits = lxc-brother
threading = off
background = 0
exit-on-no-events = 1
max-iterations = 10
[lxc-brother-initialsync]
config-block-inherits = lxc-brother
threading = off
background = 0
only-initialsync = 1
[lxc-backup]
config-block-inherits = lxc-rsyncshell
sync-handler = /etc/clsync/synchandler/lxc/backup.sh
delay-sync = 1800
delay-collect = 1800
delay-collect-bigfile = 43200
lists-dir = /dev/shm/clsync-%label%-backup
exit-hook = /etc/clsync/hooks/lxc/exit-backup.sh
pid-file = /var/run/clsync-%label%-backup.pid
status-file = /srv/lxc/%label%/clsync-backup.status
dump-dir = /tmp/clsyncdump-%label%-backup
[lxc-backup-copy]
config-block-inherits = lxc-brother
sync-handler = /etc/clsync/synchandler/lxc/copytobackup.sh
threading = off
background = 0
only-initialsync = 1
EOF
cat > /etc/clsync/hooks/lxc/exit-brother.sh << EOF
#!/bin/bash
LABEL="$1"
brotherssh rm -f /srv/lxc/"$LABEL"/clsync-brother.status
EOF
cat > /etc/clsync/hooks/lxc/exit-backup.sh << EOF
#!/bin/bash
LABEL="$1"
brotherssh rm -f /srv/lxc/"$LABEL"/clsync-backup.status
EOF
cat > /etc/clsync/rules/lxc << EOF
-f/var/log/.*
-d/var/lib/mysql
-f/bin\.000
-f/rootfs-readonly
-f/dmesg
EOF
cat > /etc/clsync/synchandler/lxc/backup.sh << EOF
#!/bin/bash
ACTION="$1"
LABEL="$2"
ARG0="$3"
ARG1="$4"
FROM="/srv/lxc/${LABEL}"
CLUSTERNAME=$(clustername)
BACKUPHOST=$(backuphost)
BACKUPMNT="/mnt/backup"
BACKUPDECR="/decrement/${LABEL}"
BACKUPMIRROR="rsync://$CLUSTERNAME@$BACKUPHOST/$HOSTNAME/mirror/${LABEL}"
if [ "$CLSYNC_STATUS" = "initsync" ]; then
STATICEXCLUDE=''
else
STATICEXCLUDE='--exclude-from=/etc/clsync/synchandler/lxc/rsync.exclude'
fi
function rsynclist() {
LISTFILE="$1"
EXCLISTFILE="$2"
excludefrom=''
if [ "$EXCLISTFILE" != "" ]; then
excludefrom="--exclude-from=${EXCLISTFILE}"
fi
if ping -w 1 -qc 5 -i 0.1 $BACKUPHOST > /dev/null; then
exec rsync --password-file="/etc/backup.pass" -aH --timeout=3600 --inplace --delete-before $STATICEXCLUDE "$excludefrom" --include-from="${LISTFILE}" --exclude='*' --backup --backup-dir="$BACKUPDECR"/ "$FROM"/ "$BACKUPMIRROR"/ 2>/tmp/clsync-rsync-"$LABEL"-backup.err
else
sleep $[ 3600 + $RANDOM % 1800 ]
return 128
fi
}
case "$ACTION" in
rsynclist)
rsynclist "$ARG0" "$ARG1"
;;
esac
exit 0
EOF
cat > /etc/clsync/synchandler/lxc/copytobackup.sh << EOF
#!/bin/bash
ACTION="$1"
LABEL="$2"
ARG0="$3"
ARG1="$4"
FROM="/srv/lxc/${LABEL}"
CLUSTERNAME=$(clustername)
BACKUPHOST=$(backuphost)
BACKUPMNT="/mnt/backup"
BACKUPMIRROR="rsync://$CLUSTERNAME@$BACKUPHOST/lxc/${LABEL}"
if [ "$CLSYNC_STATUS" = "initsync" ]; then
STATICEXCLUDE=''
else
STATICEXCLUDE='--exclude-from=/etc/clsync/synchandler/lxc/rsync.exclude'
fi
function rsynclist() {
LISTFILE="$1"
EXCLISTFILE="$2"
excludefrom=''
if [ "$EXCLISTFILE" != "" ]; then
excludefrom="--exclude-from=${EXCLISTFILE}"
fi
if ping -w 1 -qc 5 -i 0.1 $BACKUPHOST > /dev/null; then
exec rsync --password-file="/etc/backup.pass" -aH --timeout=3600 --inplace --delete-before $STATICEXCLUDE "$excludefrom" --include-from="${LISTFILE}" --exclude='*' "$FROM"/ "$BACKUPMIRROR"/ 2>/tmp/clsync-rsync-"$LABEL"-backup.err
else
sleep $[ 3600 + $RANDOM % 1800 ]
return 128
fi
}
case "$ACTION" in
rsynclist)
rsynclist "$ARG0" "$ARG1"
;;
esac
exit 0
EOF
cat > /etc/clsync/synchandler/lxc/brother.sh << EOF
#!/bin/bash -x
ACTION="$1"
LABEL="$2"
ARG0="$3"
ARG1="$4"
BROTHERMNT="/mnt/mirror"
BROTHERNAME=$(brothername)
CLUSTERNAME=$(clustername)
FROM="/srv/lxc/${LABEL}"
TO="rsync://${CLUSTERNAME}@${BROTHERNAME}/lxc/${LABEL}"
if [ "$CLSYNC_STATUS" = "initsync" ]; then
STATICEXCLUDE=''
else
STATICEXCLUDE='--exclude-from=/etc/clsync/synchandler/lxc/rsync.exclude'
fi
function rsynclist() {
LISTFILE="$1"
EXCLISTFILE="$2"
excludefrom=''
if [ "$EXCLISTFILE" != "" ]; then
excludefrom="--exclude-from=${EXCLISTFILE}"
fi
if ping -w 1 -qc 5 -i 0.1 $BROTHERNAME > /dev/null; then
exec rsync --password-file="/etc/rsyncd.pass" -aH --timeout=3600 --inplace --delete-before $STATICEXCLUDE "$excludefrom" --include-from="${LISTFILE}" --exclude='*' "$FROM"/ "$TO"/ 2>/tmp/clsync-rsync-"$LABEL"-brother.err
else
sleep $[ 3600 + $RANDOM % 1800 ]
exit 128
fi
}
case "$ACTION" in
rsynclist)
rsynclist "$ARG0" "$ARG1"
;;
esac
exit 0
EOF
cat >/etc/clsync/synchandler/lxc/rsync.exclude << EOF
sess_*
nanocacmail/*/*.nexus
home/mrtg/*.html
home/mrtg/*.log
home/mrtg/*.old
home/mrtg/*.png
vim/spell/*
tmp/*
sys/*
proc/*
run/*
var/tmp/*
var/lock/*
var/run/*
radwtmp
bitrix/cache/*
access.log*
error.log*
*cache/***
rootfs-readonly
dmesg
EOF
Добавим в cron запуск lxc-clsync-startstop, чтобы перезапускать репликацию (если она отвалилась) на случай разных проблем (например, если второй узел выпадал из сети):
crontab -e # добавляем строку: * * * * * /opt/lxc/bin/lxc-clsync-startstop
В предложенной конфигурации clsync использует rsync, поэтому настраиваем rsyncd (на рабочих узлах):
cat > /etc/rsyncd.conf << EOF
# /etc/rsyncd.conf
# This line is required by the /etc/init.d/rsyncd script
pid file = /var/run/rsyncd.pid
max connections = 128
log file = /var/log/rsync.log
timeout = 300
[lxc]
path = /srv/lxc
use chroot = yes
max connections = 128
read only = no
list = yes
uid = root
gid = root
auth users = egypt
secrets file = /etc/rsyncd.secrets
strict modes = no
hosts allow = 10.0.5.0/24
ignore errors = no
ignore nonreadable = yes
transfer logging = yes
timeout = 300
refuse options = checksum dry-run
dont compress = *.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz *.xz
EOF
touch /etc/rsyncd.{secrets,pass} /etc/backup.pass
chmod 600 /etc/rsyncd.{secrets,pass} /etc/backup/pass
cat > /etc/rsyncd.secrets << EOF
egypt:passwordhereQRWyisbVNWh1Q
EOF
cat > /etc/rsyncd.pass << EOF
passwordhereQRWyisbVNWh1Q
EOF
cat > /etc/backup.pass << EOF
passwordherevgX9rj8cAm3Es
EOF
На сервере резервного копирования:
cat > /etc/rsyncd.conf << EOF
motd file = /etc/rsyncd.motd
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
[lxc]
path = /srv/lxc
uid = root
gid = root
read only = no
list = yes
auth users = egypt
secrets file = /etc/rsyncd.secrets
[anubis]
path = /srv/storage/hosts/anubis
uid = root
gid = root
read only = no
list = yes
auth users = egypt
secrets file = /etc/rsyncd.secrets
[seth]
path = /srv/storage/hosts/seth
uid = root
gid = root
read only = no
list = yes
auth users = egypt
secrets file = /etc/rsyncd.secrets
EOF
cat > /etc/rsyncd.secrets << EOF
egypt:passwordherevgX9rj8cAm3Es
EOF
На всех трёх узлах:
# Gentoo:
rc-update add rsyncd default
/etc/init.d/rsyncd start
# Debian:
echo 'RSYNC_ENABLE=true' >> /etc/default/rsync
/etc/init.d/rsync start
Теперь необходимо протестировать как производится the репликация. Для начала необходимо создать тестовый контейнер, например:
lxc-create -t debian -n replicationtest
Если всё сделано верно, то контейнер должен отреплицироваться на соседний узел. На узле, где запущен the контейнер имеет смысл ввести команду «lxc-list»:
lxc-list
В результате вы увидите что-то вроде:
g[16:47:48] [root@anubis ~]# lxc-list
RUNNING
replicationtest [ R ][ R ] sdc1 665G
FROZEN
STOPPED
Первая «R» внутри «[ R ]» означает, что контейнер находится в синхронном состоянии на соседнем узле, а вторая «[ R ]» — что узел находится в синхронном состоянии на сервере резервного копирования.
Сами квадратные скобки «[» и «]» означают, что репликация осуществляется с _данного узла_.
Если посмотреть на «lxc-list» со стороны соседнего узла, то можно увидеть:
g[17:01:01] [root@seth ~]# lxc-list
RUNNING
FROZEN
STOPPED
remote replicationtest R R sdc1 665G
Как можно заметить, скобок «[» и «]» на данной стороне не наблюдается. Однако можно увидеть слово «remote» перед именем контейнера, что означает, что контейнер уже запущен на соседнем узле.
Кроме того, можно ввести команду:
lxc-auto replicationtest
Тогда в «lxc-list» перед именем контейнера появится ключевое слово «auto», что означает в свою очередь, что данный контейнер будет запускаться автоматически при запуске узла. Предполагается, что «lxc-auto» будет использоваться только для нерезервируемых узлов (например узлы СУБД, которые используют другие средства репликации).
++ Репликация БД
Достаточно долгие эксперименты с master-slave- и master-master-репликациями привели нас к выводу, что придётся использовать galera. И опять же долгие эксперименты с galera привели нас к связке percona+galera.
Создаём контейнер с Debian
lxc-create -t debian -n percona_test
Редактируем конфиг (хотя лучше этот процесс автоматизовать под ваши use case-ы), например:
cat > /srv/lxc/percona_test/config << EOF
## Container
lxc.utsname = percona_test
lxc.rootfs = /srv/lxc/percona_test/rootfs
lxc.arch = x86_64
lxc.console = /srv/lxc/percona_test/console.log
lxc.tty = 6
lxc.pts = 128
## Capabilities
lxc.cap.drop = audit_control
lxc.cap.drop = audit_write
lxc.cap.drop = linux_immutable
lxc.cap.drop = mac_admin
lxc.cap.drop = mac_override
lxc.cap.drop = setpcap
lxc.cap.drop = sys_admin
lxc.cap.drop = sys_boot
lxc.cap.drop = sys_module
lxc.cap.drop = sys_pacct
lxc.cap.drop = sys_rawio
lxc.cap.drop = sys_time
## Devices
# Allow all devices
#lxc.cgroup.devices.allow = a
# Deny all devices
lxc.cgroup.devices.deny = a
# Allow to mknod all devices (but not using them)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
# /dev/console
lxc.cgroup.devices.allow = c 5:1 rwm
# /dev/fuse
#lxc.cgroup.devices.allow = c 10:229 rwm
# /dev/loop*
#lxc.cgroup.devices.allow = b 7:* rwm
# /dev/loop-control
#lxc.cgroup.devices.allow = c 10:237 rwm
# /dev/null
lxc.cgroup.devices.allow = c 1:3 rwm
# /dev/ptmx
lxc.cgroup.devices.allow = c 5:2 rwm
# /dev/pts/*
lxc.cgroup.devices.allow = c 136:* rwm
# /dev/random
lxc.cgroup.devices.allow = c 1:8 rwm
# /dev/rtc
lxc.cgroup.devices.allow = c 254:0 rwm
# /dev/tty
lxc.cgroup.devices.allow = c 5:0 rwm
# /dev/urandom
lxc.cgroup.devices.allow = c 1:9 rwm
# /dev/zero
lxc.cgroup.devices.allow = c 1:5 rwm
## Limits
lxc.cgroup.cpu.shares = 8
lxc.cgroup.cpuset.cpus = 0-1
lxc.cgroup.memory.limit_in_bytes = 2G
#lxc.cgroup.memory.memsw.limit_in_bytes = 2G
## Filesystem
lxc.mount.entry = proc proc proc ro,nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs ro 0 0
lxc.mount.entry = tmpfs run tmpfs defaults,size=67108864 0 0
lxc.mount.entry = tmpfs tmp tmpfs defaults,size=67108864 0 0
## Network
lxc.network.type = veth
lxc.network.flags = up
lxc.network.hwaddr = f2:00:00:00:00:00
lxc.network.link = dbrep-test
lxc.network.name = cluster
lxc.network.veth.pair = dbtest-r
lxc.network.type = veth
lxc.network.flags = up
lxc.network.hwaddr = f2:00:00:00:00:01
lxc.network.link = dbcl-test
lxc.network.name = client
lxc.network.veth.pair = dbtest-c
lxc.network.type = veth
lxc.network.flags = up
lxc.network.hwaddr = f2:00:00:00:00:02
#lxc.network.link =
lxc.network.name = res0
lxc.network.veth.pair = db-test-res0
lxc.network.type = veth
lxc.network.flags = up
lxc.network.hwaddr = f2:00:00:00:00:03
#lxc.network.link =
lxc.network.name = res1
lxc.network.veth.pair = db-test-res1
lxc.network.type = empty
EOF
Запускаем узел.
lxc-start -n percona_test -d
Этот узел не будет контейнер не будет реплицироваться, так как его имя начинается на "percona_". После его окончательной подготовки, будет необходимо его откопировать на соседний узел и на сервер резервного копирования.
Настраиваем содержимое контейнера (предполагается, что на хост-системе в данной момент времени есть выход в Интернет):
ifconfig dbcl-test 192.168.0.1/24
sysctl net.ipv4.conf.dbcl-test.forwarding=1
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j MASQUERADE
lxc-attach -n percona_test
cat >> /etc/network/interfaces << EOF
auto cluster
iface cluster inet static
address 192.168.0.2
netmask 255.255.255.0
auto client
iface client inet static
address 192.168.1.1
netmask 255.255.255.0
EOF
cat > /etc/apt/sources.d/percona.list << EOF
deb http://repo.percona.com/apt wheezy main
deb-src http://repo.percona.com/apt wheezy main
EOF
apt-get update
apt-get -y install percona-xtradb-cluster-full-56 percona-xtradb-cluster-galera-3.x percona-xtrabackup
cat >> /etc/mysql/my.cnf << EOF
binlog_format = ROW
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name="galera_particle"
#wsrep_cluster_address="gcomm://"
wsrep_cluster_address="gcomm://192.168.0.2,192.168.0.3,192.168.0.4"
wsrep_sst_method=xtrabackup
wsrep_node_address=192.168.0.2
wsrep_sst_auth=xtrabackup:yzuW1yuRr3hn28DHPMpHFEc
wsrep_replicate_myisam=ON
wsrep_max_ws_rows = 2000
[sst]
streamfmt=xbstream
transferfmt=socat
sockopt=,nodelay,sndbuf=4096576,rcvbuf=4096576
[xtrabackup]
compact
parallel=4
rebuild-threads=2
EOF
exit
Зачистка (на хост-системе):
iptables -t nat -D POSTROUTING -s 192.168.0.0/24 -j MASQUERADE
sysctl net.ipv4.conf.dbcl-test.forwarding=0
ifconfig dbcl-test 0.0.0.0 down
Останавливаем и копируем контейнер:
lxc-stop -n percona_test
clsync -K lxc-brother-initialsync -l percona_test
clsync -K lxc-backup-copy -l percona_test
На втором узле и сервере резервного копирования исправляем файлы внутри данного контейнера:
/etc/network/interfaces
/etc/mysql/my.cnf
Запускаем на всех серверах данный контейнер:
lxc-start -d -n percona_test
Если всё сделано верно, то после запуска, данные инстанции percona должны образовать синхронный master-master-master кластер. И если ввести команду "SHOW STATUS" на любом из данных SQL-серверов, то должно быть отображено нечто наподобие:
| wsrep_local_state_comment | Synced |
| wsrep_incoming_addresses | 192.168.0.2:3306,192.168.0.3:3306,192.168.0.4:3306 |
| wsrep_cluster_size | 3 |
| wsrep_cluster_status | Primary |
Данные строки сигнализируют о том, что всё в порядке. И к этому моменту у вас имеется синхронный SQL-кластер.
++ Как пользоваться
Далее необходимо создавать контейнеры, которые будут использовать уже созданную тестовую СУБД «percona_test» (или пересоздать другую). Для этого необходимо в клиентских контейнерах создавать интерфейс, который будет присоединяться в мост «dbcl-test».
++ Теория и болталогия
++ Синхронизация конфигурации хост-систем
csync2 — это средство синхронизации файлов через SSL, с использованием SQLite для хранения метаданных. Конкретно в приведённой выше примере конфигурации, csync2 будет запускаться раз в минуту по cron-у и искать те локальные файлы, которые изменились относительно данных в SQLite-таблице. Когда такие данные будут найдены, csync2 будет пытаться их отсинхронизировать к другим участникам csync2-кластера. Если будет обнаружено, что на удалённой стороне файл тоже изменился, то валидной будет считаться так копия, которая изменялась последней (благодаря опции «auto younger»).
csync2 вполне можно заменить на clsync. Но на момент развёртки csync2 в наших системах, clsync ещё не существовал.
Общая схема синхронизации:
cron -->
[ local csync2 --> <file scanning/reading> ] --SSL-->
[ remote csync2 --> <file writing> ]
++ Синхронизация файлов контейнеров
clsync — это утилита основанная на подсистеме ядра «inotify», которая позволяет отлавливать события изменений файлов на ФС. Изначально эта утилита была написана в качестве альтернативы к lsyncd, но потом в процессе адаптации под HPC-кластера и кластера корпоративных сервисов clsync по своему функционалу сильно существенно от lsyncd.
В рамках предложенной выше конфигурации, clsync использует rsync в качестве sync-handler-а, а так же использует исключения живой синхронизации из файла /etc/clsync/rules/lxc. Более подробная информация описана в «man 1 clsync».
Общая схема запуска:
Начало синхронизации контейнера при его запуске:
lxc-start -> lxc-clsync-startstop -> clsync
Восстановление синхронизации в случае внештатных ситуаций:
cron -> lxc-clsync-startstop -> clsync
"Атомарная" синхронизация для перезапуска контейнера на соседнем узле:
lxc-rerun-on-brother -> clsync
lxc-pickup-stop -> lxc-rerun-on-brother -> clsync
Общая схема синхронизации:
kernel [inotify] -->
clsync -->
[ local rsync --> <file scanning/reading> ] --rsync-->
[ remote rsync --> <file scanning/writing> ]
++ Синхронизация БД
Самый простой способ осуществлять SQL-репликацию — это использовать [[http://dev.mysql.com/doc/refman/5.0/en/replication.html штатные средства MySQL (и подобных) для репликации данных]]. Однако данные средства обладают существенными недостатками:
** предполагается использование схем master-slave, так как при master-master могут возникать ситуации останавливающие репликацию;
** на практике не очень удобно.
В результате мы остановились на связке percona server + galera cluster.
[[http://www.percona.com/mysql-5.6-solutions-from-percona Percona server]] — это форк MySQL, поддерживающий подсистему хранения данных XtraDB (как замена InnoDB).
[[http://galeracluster.com/products/ Galera]] — это, грубо говоря, модуль для MySQL (и подобных), обеспечивающий репликацию «galera replication».
Galera replication — это метод репликации, обеспечивающий возможность организации active-active multi-master кластеров.
Совместно данные технологии работают по [[http://galeracluster.com/documentation-webpages/architecture.html следующей цепочке]]:
Percona Server -> wsrep hooks -> galera plugin -> система коммуникаций с другими узлами
В качестве системы коммуникаций в рамках данной статьи использовался «gcomm».
++ Резервное копирование файлов
В рамках данной конфигурации, кластер будет держать запоздало-синхронную копию на сервере резервного копирования в директорию mirror и откладывать старые экземпляры изменённых файлов в директорию decrement. Раз в неделю выполнялась подготовка squashfs-образов (сжатых с xz с дедупликацией) на основе данных из директории mirror, и раз в сутки перемещалась директория "decrement" в архив (с соответствующим timestamp-ом), архивировался каждый файл алгоритмом xz с помощью find и создавалась новую директория "decrement" для следующего "среза".
На примеры скриптов можно посмотреть из репозитория:
git -C /tmp clone https://gitlab.ut.mephi.ru/ut/backup-scripts
++ Резервное копирование БД
Резервное копирование БД производится обычным mysqldump по cron-у с узла SQL-кластера на сервера резервного копирования (чтобы не блокировать рабочие узлы).
++ Пара слов об ipw
Если вы произвели изменение конфига ipw, то достаточно набрать "ipw fix", чтобы его применить.