Почтовый кластер своими руками

    Сразу оговорюсь что поставленную задачу можно решить несколькими способами. Этот один из возможных.
    Статья рассчитана на тех, кто знает как настраиваются и работают Exim и Dovecot, и в ней я не будут останавливаться на базовых настройках этих сервисов.

    Надеюсь что кто-то, прочитав заметку, получит необходимые знания или идеи для воплощения своего решения.

    Задача построить отказоустойчивый сервис, с хранением почты на серверах, с доступом по IMAP.
    Кластер будет обслуживать компанию с примерно с 60 филиалами, каждый из которых имеет свой домен 3-го уровня.

    Главная задача сервиса, беспрерывный доступ к почте. Поэтому для хранилища будем использовать два географически разнесенных сервера, с синхронизацией почтовых каталогов.
    Оба сервера будут активными, это значит что мы будем распределять нагрузку между нодами. Часть доменов будет обслуживать одна нода, часть доменов другая. В случае выхода из строя одной из нод, клиенты переключаются на другую.
    В качестве фронтенда для распределения нагрузки маршрутизации клиентов будем использовать Nginx с модулем mail. Для приема почты, будем использовать два smtp сервера.

    Схема:


    STORAGE: Хранилище почтовых ящиков. Состоит из двух нод.
    Каждая нода выделеный сервер с 2х4Тб HDD, расположенные на разных хостингах
    DNS: storage-01.domain.ru и storage-02.domain.ru
    ОС: FreeBSD,
    ПО: Dovecot, Exim, Postgresql и Nginx

    SMTP: Сервера обрабатывающие SMTP трафик, две ноды.
    Виртуальные серверы расположенные на разных хостингах,
    DNS: smtp-01.domain.ru и smtp-02.domain.ru
    ОС: FreeBSD,
    ПО: Exim, Postgresql

    PROXY: Прокси сервер для пользовательского доступа к сервису IMAP, POP3, SMTP.
    Виртуальный сервер. Единственное не продублированное звено в кластере, но в виду своей простоты поднимается в течении нескольких минут из снапшота.
    DNS: mail.domain.ru
    ОС: FreeBSD,
    ПО: Nginx

    STORAGE.
    В качестве MDA был выбран Dovecot, поскольку он из коробки умеет кластер. Для хранения почты, был выбран формат Maildir, т.к. сразу захотелось дедупликацию, но об этом ниже.
    Датасторы принимают почту только от своих smtp серверов и PROXY. Почту в мир отправляют сами, минуя smtp серверы. Можно их совсем спрятать, и исходящую почту отправлять через smtp ноды.
    Путь в файловой системе к ящикам /usr/mail/домен 2-го уровня/домен 3-го уровня/ящик/
    В авторизации в качестве логина используется полный ящик mail@ldomain.mdomain.ru

    БД:
    Описание таблиц:
    mail таблица для хранения почтовых ящиков
    • id
    • mailbox — название почтового ящика
    • password — пароль в MD5
    • ldomain_id — id домена 3-го уровня из таблицы ldomain
    • mdomain_id — id домена 2-го уровня из таблицы mdomain
    • active — статус почтового ящика. вкл/выкл


    ldomain таблица для описания доменов 3-го уровня
    • id
    • domain — название домена
    • active — статус домена. вкл/выкл

    mdomain таблица для описания доменов 2-го уровня
    • id
    • domain — название домена
    • active — статус домена. вкл/выкл

    maps таблица маршрутизации
    • id
    • ldomain_id — id домена 3-го уровня из таблицы ldomain
    • mdomain_id — id домена 2-го уровня из таблицы mdomain
    • storage1 — основоной сторадж
    • storage2 — резервный сторадж (пока не используется)

    Как уже писал выше, нагрузку (почтовые домены) я распределяю по двум storage, в таблице maps определяет, на каком из storage находится домен 3-го уровня.

    mail=# select * from maps limit 3;
     id | ldomain_id | mdomain_id |      storage1      |   storage2    
    ----+------------+------------+--------------------+---------------
     56 |         56 |          2 | storage-01.domain.ru | storage-02.domain.ru
     57 |         57 |          2 | storage-02.domain.ru | storage-01.domain.ru
     58 |         58 |          2 | storage-01.domain.ru | storage-02.domain.ru
    
    (3 строк)
    

    Опираясь на эту таблицу Exm-ы стораджей и smtp нод будут определять куда слать письма. А Nginx, к какому стораджу подключать пользователей.

    Cоздание БД и таблиц:
     psql -Upgsql template1
     ctreate database mail;
     \q
    

    CREATE TABLE mail (
    	"id" BIGSERIAL PRIMARY KEY,
    	"mailbox" CHARACTER VARYING(32) not null,
    	"password" CHARACTER VARYING(128),
    	"ldomain_id" int NOT NULL,	 
    	"mdomain_id" int NOT NULL,
    	active BOOLEAN DEFAULT TRUE NOT NULL,
    	CONSTRAINT "mail_ldomain_id_check" CHECK (("ldomain_id" > 0))
    );
    
    CREATE TABLE "ldomain" (
        "id" BIGSERIAL PRIMARY KEY,
        "domain" CHARACTER VARYING(32) NOT NULL,
        "active" BOOLEAN DEFAULT TRUE NOT NULL,
        CONSTRAINT ldomain_k UNIQUE (domain)
    );
    
    CREATE TABLE "mdomain" (
        "id" BIGSERIAL PRIMARY KEY,
        "domain" CHARACTER VARYING(32) NOT NULL,
        "active" BOOLEAN DEFAULT TRUE NOT NULL,
        CONSTRAINT mdomain_k UNIQUE (domain)
    );
    
    CREATE TABLE "maps" (
        "id" SERIAL PRIMARY KEY,
        "ldomain_id"  int NOT NULL,
        "mdomain_id"  int NOT NULL,
        "storage1" CHARACTER VARYING(32) NOT NULL,
        "storage2" CHARACTER VARYING(32) NOT NULL,
        CONSTRAINT maps_ldomain_k UNIQUE (ldomain_id)
    );
    


    Dovecot
    Dovecot выполняет функцию MDA. Я оставлю за рамками этой статьи базовую настройку Dovecot, остановлюсь только на тех моментах, которые важны для связки его с DB и MTA
    /usr/local/etc/dovecot/dovecot.conf
    protocols = imap pop3 lmtp # для связки с Exim буду использовать LMTP
    

    /usr/local/etc/dovecot/dovecot-sql.conf.ext
    driver = pgsql
    connect = host=localhost dbname=mail user=mail password=password
    default_pass_scheme = MD5
    
    iterate_query = \
        SELECT mail.mailbox || '@' || ldomain.domain || '.' || mdomain.domain AS user \
    	FROM mail \
    	    INNER JOIN mdomain ON ( mail.mdomain_id = mdomain.id ) \
    	    INNER JOIN ldomain ON ( mail.ldomain_id = ldomain.id ) 
    
    password_query = \
        SELECT mail.mailbox || '@' || ldomain.domain || '.' || mdomain.domain AS mail, mail.password \
    	FROM mail \
    	    INNER JOIN mdomain ON ( mail.mdomain_id = mdomain.id ) \
    	    INNER JOIN ldomain ON ( mail.ldomain_id = ldomain.id ) \
    		WHERE mailbox = '%n' AND \
    		    ldomain.domain || '.' || mdomain.domain = '%d' AND \
    		    mail.active = true AND \
    		    ldomain.active = 'true'
    
    user_query = \
        SELECT '/usr/mail/' || ldomain.domain || '.' || mdomain.domain || '/' || mail.mailbox  AS home  \
    	FROM mail \
    	    INNER JOIN ldomain ON ( mail.ldomain_id = ldomain.id ) \
    	    INNER JOIN mdomain ON ( mail.mdomain_id = mdomain.id ) \
    		WHERE mail.mailbox = '%n' AND \
    			ldomain.domain || '.' || mdomain.domain = '%d'
    

    /usr/local/etc/dovecot/conf.d/10-auth.conf
    auth_username_format = %Lu # формат для авторизации mail@ldomain.mdomain.ru
    !include auth-sql.conf.ext
    

    /usr/local/etc/dovecot/conf.d/10-mail.conf
    mail_location =  maildir:/usr/mail/%d/%n/Maildir #Путь в файловой системе к ящикам /usr/mail/домен 2-го уровня/домен 3-го уровня/ящик/
    


    Синхронизация хранилищ
    Изначально, я настроил синхронизацию средствами самого Dovecot (dsync) но, в процессе эксплуатации вылезла очень неприятная проблема. Как оказалось, проблема была связана с типом хранилища Maildir. Dsync стал сбоить, плодить копии писем отжирая свободное место на дисках. К тому моменту я уже не мог перевести все почтовые ящики на dbox (фирменный формат Dovecot) поэтому пришлось отказаться от синхронизации посредством dsync. В целом же, к этому механизму других претензий не было.
    Пришлось обратиться к rsync, нехитрым скриптом он берет из базы те домены которые обслуживаются сервером на котором он запускается и синхронизирует их каталоги на второй сервер. Соответсвенно, на втором сервере такой же скрипт гонит на первый свои каталоги. Конечно этот механизм менее надежен так как rsync запускается по расписанию, есть окно между запусками в котором если сервер выйдет из строя мы потеряем письма.

    скрипт запускается с двумя параметрами — имя_локального_сервера имя_удаленного_сервера
    #mailrsync.pl storage-01.domain.ru storage-02.domain.ru 
    

    скрипт синхронизации:
    #!/usr/local/bin/perl
    use DBI;
    use threads;
    use Net::Nslookup;
    use Sys::Hostname;
    
    @host = split('\.',hostname); 
    $dbn="mail";
    $dbuser="mail";
    $dbpass = "password"
    $curdata=`date +%Y-%m`; chop $curdata;
    $conn=DBI->connect("DBI:Pg:dbname=$dbn;host=localhost","$dbuser","$dbpass") or die "Cannot connect";
    ($localhostname,$remotehost)=@ARGV;
    $mail_dir = "/usr/mail/";
    
    sub domains {
        $q = "SELECT ldomain.domain,mdomain.domain,maps.storage1 
                                     FROM mail 
                                         INNER JOIN ldomain on (mail.ldomain_id = ldomain.id) 
                                         INNER JOIN mdomain on (mail.mdomain_id = mdomain.id) 
                                         INNER JOIN maps on (maps.ldomain_id=ldomain.id) 
                                            WHERE maps.storage1='".$localhostname."'
                                                 AND mail.mailbox ='dir'";
       $domain = $conn->prepare($q)
             or die "Can't prepare statement: $DBI::errstr";
        $domain->execute();
        while (  my @domain = $domain->fetchrow_array ) {
                    @domains=(@domains,$domain[0].".".$domain[1]);
               }
        print "count of domains: ".($#domains + 1)."\n";
        $dt = 2; # количество доменов в одном треде
        $count = ($#domains / $dt );
        print "count: ".$count."\n";
        $i1 = 0;
         for ($i2 = 0; $i2< $count; $i2++){
                if ($dt > $#domains  ){$dt = $#domains ;}
                 print $dt."\n";
                print "loop: ".$i2."\n";
            
                foreach $item (@domains[$i1..$m]){
                   print "in \@domains: ".$mail_dir.$item."\n";
                   @stack = (@stack,$mail_dir.$item."/");
                }
                push @threads,threads->create(\&sync,\@stack);
               $i1 = $dt+1;
               $dt = $dt + 2;
               @stack=();
          }
    }
    
    sub sync {
        print "sync\n";
       foreach $target (@stack){
             system(`/usr/local/bin/rsync -H --delete-during -azz -e "/usr/bin/ssh -i /root/.ssh/dovecot_dsa" $target vmail\@$remotehost:$target`);
             print $target."\n";
        }
    
    }
    
    domains();
    foreach $thread (@threads) {
                     $thread->join();
               }
    


    На этом с Dovecot, все.

    Exim
    определяем локальные домены, исходя из записей в таблице maps, для того чтобы Exim «знал» свои домены.
    domainlist LOCAL_DOMAINS =  \
         ${lookup pgsql{\
        	    SELECT ldomain.domain || '.' || mdomain.domain AS domainname \
    		FROM ldomain, mdomain,maps \
    		    WHERE ldomain.domain || '.' || mdomain.domain = LOWER('${quote_pgsql:$domain}') \
    		        AND ldomain.active = 'true' \
    		        AND  maps.storage1 = 'storage-01.domain.ru' \
    	                AND maps.ldomain_id = ldomain.id}}
    


    в hostlist relay_from_hosts указываю адреса smtp нод и прокси, от них я принимаю почту без авторизации (клиенты авторизуются на прокси).
     relay_from_hosts = localhost : smtp01.domain.ru : smtp02.domain.ru : mail.domain.ru 
    

    Входящую почту отдаю через LMPT Dovecot-у. В остальном все стандартно. Запросы к БД для поиска ящиков и паролей такие же как в листинге для Dovecot-a

    SMTP ноды
    БД, такая же как на стораджах, за исключением того, что в таблице mail отсутствует поле password, т.к. пользователи не подключаются к этим серверам. smtp ноды обрабатывают исключительно входящий из мира трафик. По базе проверяют существует ли ящик, пропуская дальше письма только для существующих ящиков.

    Exim 
    Стандартный конфиг, за исключением запроса для определения маршрута
    ROUTE_LIST =  "${lookup pgsql{\
                       SELECT  COALESCE(storage1,'') || ' : ' || COALESCE(storage2,'') \
    		        FROM (\
                                     SELECT storage1,storage2 \
                                        FROM maps \
                                            INNER JOIN ldomain ON ( maps.ldomain_id = ldomain.id ) \
    		                        INNER JOIN mdomain ON ( maps.mdomain_id = mdomain.id ) \
    			                    WHERE ldomain.domain || '.' || mdomain.domain =  '${quote_pgsql:$domain}' \
    	                      UNION ALL \
    	                        SELECT storage1,storage2 \
    	                            FROM co_maps \
    	                                INNER JOIN co_domain ON ( co_maps.domain_id = co_domain.id ) \
    	                                    WHERE co_domain.domain  =  '${quote_pgsql:$domain}') AS foo}}"
    
    

    SQL запрос вытягивает имя стораджа для получателя, затем адрес стораджа указывается в роутере, в директиве route_list. Таким образом письмо отправляется на тот сторадж где находится активный домен для этого ящика.
    begin routers
    
    DATASTORE:
        driver = manualroute
        domains = DOMAINS
        transport = remote_smtp
        condition = MAILS
        route_list = * ROUTE_LIST
        no_more
    

    Запросы к БД для поиска ящиков и паролей такие же как в листинге для Dovecot.

    PROXY
    В качестве прокси может выступать тот же Dovecot, но я выбрал Nginx, он показался проще и понятней в этом плане. Стояла одна задача, каким то образом указывать nginx-у куда отправлять пользователя.

    nginx.conf на PROXY
    cat /usr/local/etc/nginx/nginx.conf
    worker_processes  1;
    worker_rlimit_nofile 8192;
    pid /var/run/nginx.pid;
    error_log  /var/log/nginx-error.log debug;
    error_log  /var/log/nginx-error.log  notice;
    error_log  /var/log/nginx-error.log  info;
    
    events {
      worker_connections  8192;
      multi_accept on;
      use kqueue;
    }
    
    mail {
        ssl_certificate /usr/local/etc/ssl/proxy.crt;
        ssl_certificate_key /usr/local/etc/ssl/proxy.key;
        ssl_session_timeout 5m;
        xclient off;
    
        auth_http  storage-01.domain.ru:8185/auth;
    
        pop3_capabilities	"LAST" "TOP" "USER" "PIPELINING" "UIDL" "RESP-CODES" "EXPIRE" "IMPLEMENTATION";
        imap_capabilities	"IMAP4" "IMAP4rev1" "UIDPLUS" "IDLE" "LITERAL+" "QUOTA" "LIST-EXTENDED";
        smtp_capabilities	"SIZE 52428800" "8BITMIME" "PIPELINING" "STARTTLS" "HELP";
    
        server {
              smtp_auth	login plain;
              listen 25;
              protocol smtp;
              proxy on;
              starttls on;
        }
        
        server {
              smtp_auth	login plain;
              listen 587;
              protocol smtp;
              proxy on;
              starttls on;
        }
        
        server {
              listen 110;
              protocol pop3;
              proxy on;
              starttls on;
        }
        server {
              listen 995;
              protocol pop3;
              proxy on;
              starttls on;
        }
        
        server {
              listen 143;
              protocol imap;
              proxy on;
              starttls on;
        }
        server {
              listen 993;
              protocol imap;
              proxy on;
              starttls on;
        }
    }
    

    Обратите внимание на директиву auth_http storage-01.domain.ru:8185/auth;
    На сторадже(на обоих!) тоже работает Nginx, но в режими web сервера, с одной лишь целью — обрабатывать запрос storage-01.domain.ru:8185/auth
    Этот запрос в случае удачной авторизации клиента возвращает статус авторизации, имя сторажда и порт сервиса
    "Auth-Status", "OK";
    "Auth-Server", "storage-01.domain.ru";
    "Auth-Port", "143";
    

    После чего, nginx на PROXY отправляет клиента на сторадж который вернулся в ответе.
    Можно было конечно исключить nginx на сторадже, но для этого пришлось бы и на PROXY держать базу с пользователями. В общем могли быть варианты.

    Ниже конфиг Nginx на сторадже, с модулем на perl для реализации вышеописанного.
    worker_processes  4;
    worker_rlimit_nofile 8192;
    error_log  /var/log/nginx-error.log  info;
    
    events {
      worker_connections  8192;
      multi_accept on;
    }
    http {
        perl_modules  perl/lib;
        perl_require  mailauth.pm;
        perl_require  Digest.pm;    
        access_log off;
    
        server {
    	listen 8185;
    	ssl_certificate /usr/local/etc/ssl/storage-01.crt;
    	ssl_certificate_key /usr/local/etc/ssl/storage-01.key;
    	ssl_session_timeout 5m;
    
    	location /auth {
    	    perl  mailauth::handler;
    	    proxy_set_header X-Real-IP $remote_addr;
    	}
        }
    }
    


    модуль mailauth.pm
    package mailauth;
    use nginx;
    use DBI;
    use Net::Nslookup;
    use Digest::MD5 qw(md5_hex);
    
    $pg_user = "mail";
    $pg_pass = "password";
    $passhost = "localhost";
    $mapshost = "localhost";
    
    our $auth_ok;
    $protocol_ports->{'pop3'}=110;
    $protocol_ports->{'imap'}=143;
    $protocol_ports->{'smtp'}=25;
    $protocol_ports->{'smtpssl'}=465;
     
    sub handler {
         $r = shift;
             $Passdbh=DBI->connect("DBI:Pg:dbname=mail;host=$passhost","$pg_user","$pg_pass");
            if (!$Passdbh) {
                 $r->header_out("Auth-Status", "OK") ;
                 $r->header_out("Auth-Server", '0.0.0.0');
                 $r->header_out("Auth-Port", $protocol_ports->{$r->header_in("Auth-Protocol")});
                 $r->send_http_header("text/html"); 
                 return OK;
                 exit;
             };
    
             $Mapsdbh=DBI->connect("DBI:Pg:dbname=mail;host=$mapshost","$pg_user","$pg_pass"); 
             $auth_ok=0;
             $mailbox = $r->header_in("Auth-User");
             our $get_pass_from_db=$Passdbh->prepare("SELECT password FROM mail 
                                                    INNER JOIN ldomain ON ( mail.ldomain_id = ldomain.id ) 
                                                    INNER JOIN mdomain ON ( mail.mdomain_id = mdomain.id ) 
                                                    WHERE mail.mailbox || '\@' || ldomain.domain || '.' || mdomain.domain = ? ");
    
        $get_pass_from_db->execute($mailbox);
        @row=$get_pass_from_db->fetchrow_array();
        $passfromDB=@row[0];
    
        $md5passFromConnect = md5_hex($r->header_in("Auth-Pass"));
    
        if ( $passfromDB eq $md5passFromConnect ){
                $auth_ok=1;
        }
        if ($auth_ok==1){
              @domain = split('\@',$mailbox);
              $get_server_from_maps = $Mapsdbh->prepare(
                               "SELECT storage1  FROM maps
                                      INNER JOIN ldomain ON ( maps.ldomain_id = ldomain.id ) \
                                      INNER JOIN mdomain ON ( maps.mdomain_id = mdomain.id ) \
                                           WHERE ldomain.domain || '.' || mdomain.domain = ? "
               );
    
            $get_server_from_maps->execute(@domain[1]);
            @row=$get_server_from_maps->fetchrow_array();
            $server_from_maps = nslookup(host => $row[0], type => "A");
            $r->header_out("Auth-Status", "OK") ;
            $r->header_out("Auth-Server", $server_from_maps);
            $r->header_out("Auth-Port", $protocol_ports->{$r->header_in("Auth-Protocol")});
        } else {
            $r->header_out("mail:", $r->header_in("Auth-User"));
            $r->header_out("Auth-Status", "Invalid login or password") ;
        }
    
      $r->send_http_header("text/html");
    
      return OK;
    }
    
    sub db_fail {
        $r->header_out("Auth-Status", "OK") ;
        $r->header_out("Auth-Server", '127.0.0.1');
        $r->send_http_header("text/html");
    }
    
    1;
    __END__
    


    Настройка балансировки, и переключение на резервную ноду
    Сейчас переключение на резервную ноду происходит в ручном режиме. Просто в таблице maps меняется значение в поле storage1. Т.К. все сервера увешены мониторингом этого пока было достаточно.

    ЗАКЛЮЧЕНИЕ
    Кластер работает уже 3 года. За это время пережил несколько падений одной из нод ( в результате эта нода переехала в другой ДЦ).

    Кому-то может показатся эта конструкция сложной, запутанной и «велосипедом». Но хочу сделать акцент на том, что архитектура данного решения исходила из решения использовать дешевое, «ненадежное» железо. В результате имеем надежный сервис с минимальной стоимостью аренды серверов.

    Возможно заметка получилась не достаточно понятной, и я не отобразил какие то важные детали. В комментариях, если будут, буду дополнять.

    ПС. Статья получилась длинной, поэтому про дедупликацию и еще одном, не упомянутом здесь сервисе управления этим кластером расскажу в следующей заметке, если это вызовет интерес.

    Спасибо за внимание!

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 15

      0
      Всё время мучал вопрос, ответ на который Вы дали только в самом конце :)
      «переключение на резервную ноду» -> " Просто в таблице maps меняется значение в поле storage1"
      Как то много баз у Вас. Но на это можно взглянуть и со стороны отказоустойчивости. Быть может правильнее было б 2е ноды, с резервированием под Postgresql отдать? Так кончено дороже наверное, но архитектурно, как по мне правильнее. Но могу ошибаться. А вообще если настроить реплики и между Вашми 4мя Postgresql, то так и без 2х дополнительных нод выйдет наддёжнее. К примеру на SMTP, Postgresql друг друга подхватывают, и на Storage так же. Ну это как вариант, что первое в голову пришло, или я не внимательно прочёл, и у Вас так и есть?
      Что касается продолжения статьи, то разумеется пишите. Любопытно.
        0
        Бд на SMTP нодах нужны для того чтобы определить «свои» домены и отсеивать письма на несуществующие ящики. Можно конечно было бы подключаться к БД стораджей и от туда брать нужную информацию, но я подумал, что будет в случае если оба стораджа будут не доступны? в моем случае вся входящая почта будет скапливаться на SMTP нодах. Решил что так нормально. Синхронизации БД нет. Для себя решил что это излишнее, тк все манипуляции с добавлением, удалением, модификацией ящиков и доменов осуществляется с сервиса управления, который непосредственно подключается ко всем БД и модифицирует каждую из них. Мне показалось это достаточно.
        +1
        С dsync такие же проблемы возникли после 2.2.16 версии.
        Используем его только в качестве hot backup, вторая нода не принимает юзеров. После 2.2.16 начались дубли писем. Опытным путем выяснили, что происходит это, когда на клиенте настроены фильтра, переносящие письма из Инбокса в другие папки. Если они успевают отработать достаточно быстро, то возникают дубли при репликации.
        Решили это отключением синхронизации со вторичной ноды на первичную (ранее было active-active).
        Полного решения проблемы не нашли :(
        Про дедупликацию очень интересно, пишите.
          0
          Интерес есть, да ещё какой!
          Именно сейчас, готовимся к подобному действу.
          Пишите продолжение! Мы обойдем ваши грабли и будем искать только свои))
            +1
            Вместо
            dsync
            — lsyncd не пробовали для синхронизации стораджей?
              0
              На FreeBSD его вроде нет, про аналоги не слышал. Если бы не распределение нагрузки, то для обычного «тёплого» бекапа хватило бы синхронизации через снапшоты zfs с последующим ручным переключением на резервный сервер.
              0
              Интересная штука, не знал о таком, нужно попробовать! Спасибо за наводку
                0

                посмотрите еще на
                http://isync.sourceforge.net

                  0
                  На 1ТБ писем rsync будет работать минут 15, проверено.
                    0
                    Так же просьба уточнить насчет pop3, он корректно отрабатывает у клиентов при переключении на резервный сервер?
                      0
                      pop3 клиентов ничтожное количество. А что с ними может быть не так? они же не держат сессию, подключаются периодически, забирают что есть и отключаются. Но могу специально попробовать, скажите только что воспроизводить, переключение в момент закачки клиентом большого письма?
                        0
                        Пришлось однажды восстановить почту dovecot 1.2 из бекапа. Бекап делался rsync и восстанавливался тоже через rsync. После восстановления клиенты, которые настроены на pop3, заново приняли все письма (стоял срок хранения писем на сервере 30 дней). То есть интересна ситуация: клиент1 получал почту с сервера1, потом сервер1 отключили и клиент1 стал получать почту с сервера2.
                          0
                          такого не произойдет, если идет постоянная синхронизация. У вас там Maildir был? У него все просто. Видимо у вас в архиве эти письма были в каталоге new.a
                      0
                      Чтобы полностью забить канал между нодами в 100мб/с приходится запускать rsync в несколько потоков. Вообще к нему вопросов никаких нет, отрабатывает всегда четко! Единственный минус, о чем писал в заметке, это окно между выполнениями.
                      0
                      Я сейчас что-то подобное реализуются, только PROXY у меня совмещен с web клиентом, и данные по пользователям беру из базы web клиента, это требует первого входа на новый адрес через web, но это и так требуется, что бы пользователь сменил одноразовый пароль и получить повареную книгу компании. Ваша схема интересная, спасибо, с удовольствием почитаю продолжения.

                      Only users with full accounts can post comments. Log in, please.