MongoDB Sharded Cluster на Centos 6.5

    В этой статье мы рассмотрим только конфигурацию MongoDB, не затрагивая вопросы подключения репозитория монги и установки пакетов в систему.

    Распределенный кластер MongoDB состоит из следующих компонентов:

    Шард
    Шард — это инстанс mongod, который хранит часть данных шардированной коллекции. Для использования в production, каждый шард должен быть набором реплик (replicaSet).

    Сервер конфигураций
    Так же экземпляр mongod, который хранит метаданные кластера. Метаданные указывают какие данные хранятся на каком шарде.

    Сервер маршрутизации
    Экземпляр mongos. Его задача — маршрутизация запросов от приложений к шардам.
    Ниже приведена схема работы шардированного кластера MongoDB



    Удобнее всего необходимые роли группировать следующим образом:
    • Сервер конфигурации + сервер маршрутизации
    • Шард


    Допустим, для создания этих ролей мы имеем 3 сервера:
    • mongos01
    • mongos02
    • mongos03


    Настройка сервера конфигураций

    Для того, что бы mongod работал как сервер конфигураций, приводим /etc/mongod.conf к следующему виду:
    logpath=/var/log/mongodb/mongod.log
    logappend=true
    fork=true
    dbpath=/opt/mongocfg
    pidfilepath=/var/run/mongodb/mongod.pid
    bind_ip=<lo ip>,<eth ip>
    configsvr=false
    


    После чего запускаем сервис
    # service mongod start


    Настройка сервера маршрутизации

    Прежде чем переходить к настройке сервера маршрутизации, необходимо убедиться что в системе установлен пакет mongodb-org-mongos
    # rpm -qa | grep mongos
    mongodb-org-mongos-2.6.2-1.x86_64


    Для начала, создадим файл конфигурации для сервиса mongos /etc/mongos.conf и приведем его к следующему виду:
    configdb=mongos01:27019,mongos02:27019,mongos03:27019 # Mongo config servers addresses
    port = 27017
    logpath = /var/log/mongodb/mongos.log
    logappend = true
    fork = true
    bind_ip=<lo ip>,<eth ip>
    verbose = false
    


    Mongo не включили в свой пакет init script для mongos, посему создадим его

    cat > /etc/init.d/mongos << TheEnd
    
    #!/bin/bash
    
    # mongos - Startup script for mongos
    
    # chkconfig: 35 85 15
    # description: Mongo Router Process for sharding
    # processname: mongos
    # config: /etc/mongos.conf
    # pidfile: /var/run/mongos.pid
    
    . /etc/rc.d/init.d/functions
    
    # mongos will read mongos.conf for configuration settings
    
    # Add variable to support multiple instances of mongos
    # The instance name is by default the name of this init script
    # In this way another instance can be created by just copying this init script
    # and creating a config file with the same name and a .conf extension
    # For Example:
    #   /etc/init.d/mongos2
    #   /etc/mongos2.conf
    # Optionally also create a sysconfig file to override env variables below
    #   /etc/sysconfig/mongos2
    INSTANCE=`basename $0`
    
    # By default OPTIONS just points to the /etc/mongod.conf config file
    # This can be overriden in /etc/sysconfig/mongod
    OPTIONS=" -f /etc/${INSTANCE}.conf"
    
    PID_PATH=/var/run/mongo
    PID_FILE=${PID_PATH}/${INSTANCE}.pid
    MONGO_BIN=/usr/bin/mongos
    MONGO_USER=mongod
    MONGO_GROUP=mongod
    MONGO_ULIMIT=12000
    MONGO_LOCK_FILE=/var/lock/subsys/${INSTANCE}
    
    # Source sysconfig options so that above values can be overriden
    SYSCONFIG="/etc/sysconfig/${INSTANCE}"
    if [ -f "$SYSCONFIG" ]; then
      . "$SYSCONFIG" || true
    fi
    
    # Create mongo pids path if it does not exist
    if [ ! -d "${PID_PATH}" ]; then
      mkdir -p "${PID_PATH}"
      chown "${MONGO_USER}:${MONGO_GROUP}" "${PID_PATH}"
    fi
    
    start()
    {
      echo -n $"Starting ${INSTANCE}: "
      daemon --user "$MONGO_USER" --pidfile $PID_FILE $MONGO_BIN $OPTIONS --pidfilepath=$PID_FILE
      RETVAL=$?
      echo
      [ $RETVAL -eq 0 ] && touch $MONGO_LOCK_FILE
      return $RETVAL
    }
    
    stop()
    {
      echo -n $"Stopping ${INSTANCE}: "
      killproc -p $PID_FILE -t30 -TERM $MONGO_BIN
      RETVAL=$?
      echo
      [ $RETVAL -eq 0 ] && rm -f $MONGO_LOCK_FILE
      [ $RETVAL -eq 0 ] && rm -f $PID_FILE
      return $RETVAL
    }
    
    restart () {
            stop
            start
    }
    
    ulimit -n $MONGO_ULIMIT
    RETVAL=0
    
    case "$1" in
      start)
        start
        ;;
      stop)
        stop
        ;;
      restart|reload|force-reload)
        restart
        ;;
      condrestart)
        [ -f $MONGO_LOCK_FILE ] && restart || :
        ;;
      status)
        status -p $PID_FILE $MONGO_BIN
        RETVAL=$?
        ;;
      *)
        echo "Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
        RETVAL=1
    esac
    
    exit $RETVAL
    TheEnd
    


    Сделаем его исполняемым
    chmod +x /etc/init.d/mongos

    Теперь можно запускать
    service mongos start

    И не забыть
    # chkconfig mongod on
    # chkconfig mongos on
    


    Теперь нужно повторить эти действия на остальных серверах.

    Настройка шардов

    Первое что необходимо помнить при настройке шардов для production среды — каждый шард это replica set.
    Более подробно про репликацию в MongoDB можно прочесть в официальной документации
    Мы же не будем на этом подробно останавливаться, а приступим к настройке.

    У нас будет 4 сервера:
    • Master для первого шарда (mongo01-rs01)
    • Slave для первого шарда (mongo02-rs01)
    • Master для второго шарда (mongo01-rs02)
    • Slave для второго шарда (mongo02-rs02)


    Допустим, на всех четырех серверах уже установлена система и установлен mongodb
    В /etc/mongodb.conf на mongo01-rs01 и mongo02-rs01 нужно задать имя для набора реплик, которое будет использовать этот шард
    replSet=rs01
    

    Сохраняем и запускаем mongod.
    Далее заходим в консоль mongo на сервере который планируем сделать Master
    # mongo

    И инициализируем набор реплик
    > rs.initiate()

    Что бы убедиться что набор реплик инициализирован посмотрим его конфиг
    rs01:PRIMARY> rs.conf()

    Вывод должен показать что-то подобное:
    {
            "_id" : "rs01",
            "version" : 7,
            "members" : [
                    {
                            "_id" : 0,
                            "host" : "mongo01-rs01:27017"
                    }
            ]
    }
    


    Далее добавляем наш второй сервер в этот набор
    rs01:PRIMARY> rs.add(«mongo02-rs01»)

    И проверяем конфиг
    rs01:PRIMARY> rs.conf()

    {
            "_id" : "rs01",
            "version" : 7,
            "members" : [
                    {
                            "_id" : 0,
                            "host" : "mongo01-rs01:27017"
                    },
                    {
                            "_id" : 1,
                            "host" : "mongo02-rs01:27017",
                    }
            ]
    }
    


    Для повышения отказоустойчивости MongoDB рекомендуется количество машин в наборе делать не четным.
    Так как мы не хотим создавать еще одну копию данных, мы можем создать Арбитра

    Арбитр — это экземпляр mongod, который является членом набора реплик, но не хранит никаких данных. Он участвует в выборе нового мастера.
    Про то как устроены выборы, очень подробно написано в официальной документации
    Для того, чтобы не выделять под него отдельную машину — воспользуемся одной из ранее созданных — mongos01
    Как мы помним, там по service mongod start запускается экземпляр mongod который является сервером конфигураций.
    Что бы не запускать арбитра руками мы должны сделать для него init script
    cat > /etc/init.d/mongo-rs01-arb << TheEnd
    #!/bin/bash
    
    # mongod - Startup script for mongod
    
    # chkconfig: 35 85 15
    # description: Mongo is a scalable, document-oriented database.
    # processname: mongod
    # config: /etc/mongod.conf
    # pidfile: /var/run/mongodb/mongod.pid
    
    . /etc/rc.d/init.d/functions
    
    # things from mongod.conf get there by mongod reading it
    
    
    # NOTE: if you change any OPTIONS here, you get what you pay for:
    # this script assumes all options are in the config file.
    CONFIGFILE="/etc/mongod-rs01-arb.conf"
    OPTIONS=" -f $CONFIGFILE"
    SYSCONFIG="/etc/sysconfig/mongod-rs01-arb"
    
    # FIXME: 1.9.x has a --shutdown flag that parses the config file and
    # shuts down the correct running pid, but that's unavailable in 1.8
    # for now.  This can go away when this script stops supporting 1.8.
    DBPATH=`awk -F= '/^dbpath[[:blank:]]*=[[:blank:]]*/{print $2}' "$CONFIGFILE"`
    PIDFILE=`awk -F= '/^pidfilepath[[:blank:]]*=[[:blank:]]*/{print $2}' "$CONFIGFILE"`
    mongod=${MONGOD-/usr/bin/mongod}
    
    MONGO_USER=mongod
    MONGO_GROUP=mongod
    
    if [ -f "$SYSCONFIG" ]; then
        . "$SYSCONFIG"
    fi
    
    # Handle NUMA access to CPUs (SERVER-3574)
    # This verifies the existence of numactl as well as testing that the command works
    NUMACTL_ARGS="--interleave=all"
    if which numactl >/dev/null 2>/dev/null && numactl $NUMACTL_ARGS ls / >/dev/null 2>/dev/null
    then
        NUMACTL="numactl $NUMACTL_ARGS"
    else
        NUMACTL=""
    fi
    
    start()
    {
      # Recommended ulimit values for mongod or mongos
      # See http://docs.mongodb.org/manual/reference/ulimit/#recommended-settings
      #
      ulimit -f unlimited
      ulimit -t unlimited
      ulimit -v unlimited
      ulimit -n 64000
      ulimit -m unlimited
      ulimit -u 32000
    
      echo -n $"Starting mongod: "
      daemon --user "$MONGO_USER" "$NUMACTL $mongod $OPTIONS >/dev/null 2>&1"
      RETVAL=$?
      echo
      [ $RETVAL -eq 0 ] && touch /var/lock/subsys/mongod-rs01-arb
    }
    
    stop()
    {
      echo -n $"Stopping mongod: "
      killproc -p "$PIDFILE" -d 300 /usr/bin/mongod
      RETVAL=$?
      echo
      [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/mongod-rs01-arb
    }
    
    restart () {
        stop
        start
    }
    
    
    RETVAL=0
    
    case "$1" in
      start)
        start
        ;;
      stop)
        stop
        ;;
      restart|reload|force-reload)
        restart
        ;;
      condrestart)
        [ -f /var/lock/subsys/mongod ] && restart || :
        ;;
      status)
        status $mongod
        RETVAL=$?
        ;;
      *)
        echo "Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
        RETVAL=1
    esac
    
    exit $RETVAL
    TheEnd
    


    Делаем его исполняемым
    # chmod+x /etc/init.d/mongo-rs01-arb

    Создадим для него BaseDir и файл конфигурации
    # mkdir /opt/mongo-rs01-arb; chown mongod:mongod /opt/mongo-rs01-arb
    # cp -av /etc/mongod.conf /etc/mongod-rs01-arb.conf

    Далее в файле /etc/mongod-rs01-arb.conf редактируем следующие строки
    port=27020
    dbpath=/opt/mongo-rs01-arb
    pidfilepath=/var/run/mongodb/mongod-rs01-arb.pid
    

    И удаляем/комментируем строку
    configsvr=true
    

    Сохраняем файл и запускаем сервис
    # service mongo-rs01-arb start

    Далее возвращаемся на наш Master для rs01, и в консоли mongo добавляем арбитра в набор реплик
    > rs.addArb(«mongos01:27020»)


    Проверяем конфиг
    rs01:PRIMARY> rs.conf()

    {
            "_id" : "rs01",
            "version" : 7,
            "members" : [
                    {
                            "_id" : 0,
                            "host" : "mongo01-rs01:27017"
                    },
                    {
                            "_id" : 1,
                            "host" : "mongo02-rs01:27017",
                    },
                    {
                            "_id" : 2,
                            "host" : "mongos01:27020",
                            "arbiterOnly" : true
                    }
            ]
    }
    


    Повторяем эту процедуру с оставшимися двумя серверами под второй набор реплик который будет вторым шардом в нашем кластере (mongo01-rs02 и mongo02-rs02)

    И так, мы создали 2 набора реплик, которые теперь надо добавить в наш распределенный кластер.
    Для этого идем на mongos01 и заходим в консоль mongo (Следует помнить, что, в данном случае, мы подключаемся к сервису mongos)
    > sh.addShard(«rs01//mongo01-rs01:27017,mongo02-rs01:27017»)
    > sh.addShard(«rs02/mongo01-rs02:27017,mongo02-rs02:27017»)

    Проверяем:
    > sh.status()

    Вывод должен содержать следующие строки:
     shards:
        {  "_id" : "rs01",  "host" : "rs01/mongo01-rs01:27017,mongo02-rs01:27017" }
        {  "_id" : "rs02",  "host" : "rs02/mongo01-rs02:27017,mongo02-rs02:27017" }
    


    Это означает что в наш кластер успешно добавлены 2 шарда

    Теперь добавим в наш распределенный кластер базу которую будем шардировать.
    В нашем случае это будет база содержащая файловую систему GridFS
    > use filestore
    > sh.enableSharding(«filestore»)
    > sh.shardCollection(«filestore.fs.chunks», { files_id: 1, n: 1 })

    Проверяем статус
    > sh.status()

    Вывод должен быть примерно таким:
     shards:
        {  "_id" : "rs01",  "host" : "rs01/mongo01-rs01:27017,mongo02-rs01:27017" }
        {  "_id" : "rs02",  "host" : "rs02/mongo01-rs02:27017,mongo02-rs02:27017"}
      databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "test",  "partitioned" : false,  "primary" : "rs02" }
        {  "_id" : "filestore",  "partitioned" : true,  "primary" : "rs01" }
            filestore.fs.chunks
                shard key: { "files_id" : 1, "n" : 1 }
                chunks:
                    rs01    1363
                    rs02    103
                too many chunks to print, use verbose if you want to force print
    


    Вот и все, теперь можем использовать наш распределенный GridFS в приложении обращаясь к экземплярам mongos

    PS: про ошибки и не точности просьба писать в личку,
    Share post

    Similar posts

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

    More
    Ads

    Comments 13

      0
      Я так понял, у Вас арбитры располагаются на тех же серверах, где и сами реплика сеты для шардов? Это достаточно плохая практика, так как при обвале одного сервера отвалятся secondary и arbiter для одного из шардов, что приведёт к неработоспообности replica set (primary не увидит большинства членов replica set и уйдёт в secondary). Лучше положить арбитров на третий сервер, раз у Вас их три.
        0
        Немного промахнулся, ответ на Ваш комментарий ниже
          0
          Ага, у Вас там 5 серверов. Да, я немного недопонял.
        0
        Нет, арбитры располагаются вместе с конфиг серверами и mongos.
        Конечно же они отдельно от реплика сетов живут
          0
          Сразу прошу прощения если вопрос глупый, так как только начинаю изучать mongodb и ее возможности, а вопрос в следующем: какое количество 100% базы у вас получилось? т.е. например в кассандре при replication factory: 3 я понимаю что на 3 серверах хранится 100% базы, и если вылетит 1 сервер то на 2 других останется полная копия базы. Как я понимаю сейчас у вас имеется сейчас 2 шарда по 100% базы у которых имеется связь мастер-слейв или я не правильно понимаю? Спасибо.
            0
            Каждый шард это Master + Slave, в теории можно потерять по одному серверу из каждого шарда и иметь при этом все 100% базы
            То есть картинка того, что настраивали в этой статье выглядит примерно так

            +1
            Спасибо за статью.
            Хорошей практикой является ставить mongos прокси на клиентские машины (Web, App сервер). Таким образом Вы уменьшаете количество хопов на 1 для Вашего клиента. Также я бы рекомендовал использовать порты отличные от стандартных 27017 для шардов, т.к. ненароком какой-нибудь невнимательный разработчик сможет к ним случайно подключиться и убить всю балансировку. Лучше смените их на 27020 и 27021 для каждого шарда и в дополнение поставьте по mongos на каждый из них, чтобы не задавали лишние вопросы (на случай если клиентская машина не имеет mongos).
            В дополнение могу сказать что очень рекомендую использовать системы управления конфигурациями для кластеров, экономит время на правку init скриптов, а часто и на конфигурацию самого кластера. Например puppet.
              0
              Спасибо за совет, сменить порты действительно стоит.
              По поводу системы управления конфигурациями, думаю запилим как дойдут руки )
              0
              > Для использования в production, каждый шард должен быть набором реплик (replicaSet).
              Кто-нибудь может аргументировать вот это условие? Вижу его в официальной документации, но нигде нет подробного объяснения причин.
              Иначе что получается.
              Имеем жирный сервер с монгой. База медленно и верно грозит заполнить весь раздел, выделенный для нее. В память не влезают даже индексы, а поскольку монга работает только с замапленными данными, дисковая система целиком поглащена постоянным маппингом файов баз в память.
              Логично — нужно размазывать данные по серверам, т.е шардить. Пускай для начала хотим разделить данные пополам, т.е 2 шарды. Но ведь у нас условие — каждая шарда должна быть реплика-сетом, т.е представлять из себя мимнимум 2 сервера!
              Разместить реплику каждой шарды на сервере другой шарды ход красивый, когда на этих серверах хватает дисков под еще половину базы. И свободной памяти тоже.
              Но это противоречит сути шардирования — мы как раз решаем задачу по уменьшению кол-ва данных на сервере, а тут нате вам еще данных? Очевидно не подходит. Значит, под реплики шард нужны отдельные сервера, по мощности не более, чем в 2 раза (ну, такие, условные «2 раза») уступающие боевым.
              Итого!
              Чтобы разделить данные «пополам», т.е создать 2 шарды, мне надо:
              +1 сервер уровня существущего, а лучше мощнее под вторую боевую шарду.
              +2 сервера чуть послабее под реплики шард.
              +1-2 простеньких сервера под mongos и config.

              Т.е хотим сделать шард — покупаем еще 4-5 серверов, да? Без претензий, просто интересно и актуально.
                0
                Насколько я понимаю, совет создавать реплики для шардов исходят из того что чем на большее количество шард вы «размазываете» свои данные тем выше вероятность потерять один из серверов. И если это был единственный сервер в шарде то это часть данных пропадет и работаспособность всего кластера будет нарушена.
                Арбитр были как раз придуманы для того чтобы можно было все же не три сервера в реплике держать а два. А арбитр по сути еще один член реплики без данных но с правом голоса (типа заглушка-пустышка :) )
                  0
                  config серверов на данный момент может быть максимум 3.
                  mongos можно запускать на сервере приложений (там где ваше приложение работает).
                  Получается что минимально рекомендуемый комплект для очередного шарда — 2 сервера (арбитра можно где-то запустить на стороне, может быть даже собрать их все на один сервер, пусть развлекаются).
                  0
                  Спасибо.
                  Смысл арбитров понятен — помощь при голосовании.
                  Загвоздка еще в том, что при шардировании, видимо, нельзя выполнять бэкап базы снапшотами ФС, т.к. снапшоты разных шард не будут согласованы. Либо на время создания снапшота нужно остановить всю запись во всех шардах, снапшотить, и включать обратно. Вроде возможно, хотя вернет только данные на момент бэкапа, никакого availability… Зато всего 2 мощных сервера надо.
                  А согласно рекомендациям всё получается как я предположил: для познания радостей шардирования мне надо докупить 3 хороших сервера + 1 простенький под конфиг.
                  А это хороших тысяч 400-500 р. Просто это стоит учитывать тем, кто любит шардинг в монге, но никогда его не поднимал на более-менее большой базе :)
                    0
                    Да, на шардах можно полностью сделать снапшот только если остановить запись. Иначе будет плюс-минус… Но, допустим, как показывает один из примеров (use case), вы используете шардинг для «архивирования» данных… То есть свежие данные на мощном сервере, «прошлогодние» данные на втором «архивном» более слабом сервере.
                    Данные на архивном серевере почти не меняются. То есть никакой проблемы с рассогласованностью снапшотов не будет.

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