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", чтобы его применить.