Pull to refresh
0
Rating
Inoventica Services
VPS / хостинг / домены / защита от DDoS

«Крылья, лапы и хвосты» нашего Linux-хостинга, часть 1: как мы автоматизировали развёртывание инфраструктуры

Inoventica Services corporate blog Puppet *
— Эй, птичка, летим со мной, там столько вкусного!!!
— Столько? [разводя руки]
— у-у [мотая головой и соединяя руки]


Одна из основных проблем, которая встаёт перед провайдерами shared-хостинга, ‒ это изолирование пользователей. Было бы, конечно, проще и надёжнее создавать для каждого пользователя контейнер, но это съедает лишние ресурсы и здорово уменьшает плотность упаковки сайтов на одну машину (я исхожу из комфортной для клиента упаковки, а не вариант “селёдки в бочке”, который всё ещё встречается на ультрадешёвых хостингах, когда открытие даже статичной страницы сайта клиента заметно тормозит из-за нагрузки на веб-бокс). Скажу больше, нередки ситуации, когда один клиент, случайно или намеренно, занимает слишком много ресурсов, в ущерб всем остальным.

Решить эти задачи, не тратя ресурсы серверов впустую, мы и намеревались при помощи CloudLinux. CloudLinux уже упоминался на Хабре. Это RHEL-совместимый дистрибутив, базирующийся на ядре OpenVZ. При помощи хитрых компонентов (CageFS и LVE) и модифицированного ядра позволяет ограничивать пользователей в ресурсах (процессор память диск) без создания контейнеров.

CageFS ‒ виртуальная файловая система, которая реализует Container-Based Virtualization (Operation System-Level Virtualization). Создаёт файловую структуру домашней директории и ограничивает пространство имён пользователя, изолируя его от других пользователей системы.
Структура домашней директории пользователя по умолчанию находится в /usr/share/cagefs-skeleton. В нашем случае используется своя структура директории home, путь к шаблону (skeleton) которой описывается в /etc/cagefs/cagefs.base.home.dirs.

LVE (Lightweight Virtual Environment) ‒ технология ограничения ресурсов на базе cgroups. Позволяет тонко ограничить ресурсы для конкретного пользователя на уровне ядра, таких как:

  • % мощности одного ядра CPU (может быть больше 100%);
  • физическая память;
  • виртуальная память;
  • число процессов, создаваемых внешними событиями (например новый HTTP-запрос);
  • число процессов внутри LVE;
  • производительность дисковой подсистемы;
  • количество операций ввода/вывода.

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

При полном исчерпании ресурсов активных серверов, мы заводим новый и создаём пользователей там. Конечно, можно всё это делать руками (тем более, что система мониторинга предупреждает об исчерпании ресурсов в текущей инфраструктуре). Но мы-то знаем, что самое дорогое в IT это время специалиста и лучше “день потерять, но потом за пять минут долететь” ‒ автоматизировать рутинные операции.
Для создания и подготовки новых серверов мы используем библиотеку Puppet fabric и Puppet.
Fabric позволит автоматизировать подключение к группам серверов и удалённый запуск команд, а Puppet используется для централизованного управления конфигурацией.

Приведу пример развертывания сервера на чистую версию CentOS 6.7 x64, которую мы превратим в CloudLinux. Разворачивать скрипты буду удалённо со своей машины (CentOS) на машину с IP 10.0.0.146. Создание чистых машин с заданной ОС у нас сделано шаблонами и не требует вообще никаких усилий. Настройка IP выполняется тоже автоматически, свободные адреса берутся из специальной системы учёта, которую мы называем “Инвентори”.

Ставлю на свою рабочую машину fabric и puppet:

yum install gcc python-devel python-pip puppet puppet-server
pip install fabric

Скрипт начальной подготовки системы:
install_web.sh
#!/bin/sh

if ! grep --quiet "^SELINUX=disabled" /etc/selinux/config; then
    echo "SELINUX=disabled" > /etc/selinux/config
    echo "disabled SELINUX, please reboot server and run script again!"
    exit
fi

if ! grep --quiet "var" /etc/fstab; then
    echo "please create separate mount point /var and run script again!"
    exit
fi

# fix fstab, activate quotas
perl -p -i -e "s/\/var\s+ext4\s+defaults\s+0 0/\/var\t\t\text4\tdefaults,noatime,nosuid,usrjquota=aquota.user,jqfmt=vfsv0\t1 2/" /etc/fstab
mount -vo remount /var
quotacheck -vumaf
quotaon -avu

set -e

# install cloudlinux
if ! echo $(uname -r) | grep --quiet "lve"; then
    wget http://repo.cloudlinux.com/cloudlinux/sources/cln/cldeploy
    sh -x cldeploy -i # If use non-IP-based activation then execute cldeploy -k <activation key>
    yum -y '--disablerepo=*' --enablerepo=cloudlinux* update
    yum -y '--disablerepo=*' --enablerepo=cloudlinux* install mod_hostinglimits cagefs lve-wrappers
    yum install libgcc.i686 glibc.i686 -y
    rm -rf cldeploy
    echo "installed CloudLinux, please reboot server and run script again!"
    exit
fi

# activate cloudlinux cagefs
cagefsctl --init
cagefsctl --set-min-uid 2000
/usr/sbin/cagefsctl --enable-all


При первом запуске скрипт отключит SELinux, установит необходимые репозитории, активирует репозитории CloudLinux и устанавливает необходимые компоненты CloudLinux, включая ядро:

ssh 10.0.0.146 < install_web.sh

После перезагрузки системы повторный запуск скрипта проинициализирует шаблон домашней директории (cagefs-skeleton), установит UID, с которого начинаются учётные записи пользователей CageFS, равным 2000 и включит CageFS.

Для установки и настройки необходимого ПО воспользуюсь fabric и puppet.
Добавлю наш сервер с IP 10.0.0.146 и hostname web.domain.com в конфигурацию Puppet — манифест web.pp, который описывает наш новый сервер:

node /^web.domain.com/ {
    include base_web
}

Класс base_web включает в себя всю настройку нашего сервера. О том, как детально настраивать систему с помощью Puppet, в том числе примеры классов настройки, есть во множестве мест в Интернете, включая замечательный CookBook.
Скрипт конфигурации fabric:
fabfile.py
#!/usr/bin/python

from fabric.api import env, sudo, roles, settings
from fabric.contrib.project import rsync_project

env.roledefs['production'] = ['10.0.0.146']
env.roledefs['development'] = []

def shell_env():
    env.port = 22
    env.deploy_path = './puppet'

def deploy_puppet():
    shell_env()
    with settings(warn_only=True):
        result = sudo('rpm -q rsync')
        if not result.succeeded:
            sudo('yum install -y rsync')
    rsync_project(
        remote_dir=env.deploy_path,
        local_dir='./',
        exclude=['.svn', '*.pyc', 'fabfile.py', 'install_*.sh'],
        extra_opts='--delete-after'
    )
    with settings(warn_only=True):
        result = sudo('rpm -q epel-release')
        if not result.succeeded:
            sudo('rpm -Uvh http://mirror.yandex.ru/epel/6/x86_64/epel-release-6-8.noarch.rpm')

        result = sudo('rpm -q puppet')
        if not result.succeeded:
            sudo('yum install puppet -y')
    sudo('puppet apply --modulepath {0}/modules/ {0}/manifests/site.pp'.format(env.deploy_path))

@roles('production')
def deploy_p():
    deploy_puppet()

@roles('development')
def deploy_d():
    deploy_puppet()

def deploy():
    deploy_puppet()


Наконец, запускаю fabric production, который установит клиент puppet и доплонительное ПО, и система готова:

fab deploy_p

При помощи puppet на сервер будут залиты единые настройки, которые мы используем для всех серверов одного типа.

Вся конфигурация (заливаемая на сервера через puppet) у нас хранится централизованно во внутреннем SVN-репозитории, что очень удобно, когда конфигурированием и поддержкой занимаются несколько человек. Допустим, нужно изменить конфигурацию по-умолчанию для sysctl. Идём в puppet/modules/sysctl/files/default/sysctl.conf и редактируем его по своему усмотрению.

Манифест puppet/modules/sysctl/manifests/init.pp будет выглядеть следующим образом:
init.pp
class sysctl::params {
    $template = "default"
}

class sysctl (
    $template = $sysctl::params::template
    ) inherits sysctl::params {

    file { "/etc/sysctl.conf":
        ensure  => present,
        owner   => root,
        group   => root,
        mode    => 0640,
        source  => [ "puppet:///modules/sysctl/$template/sysctl.conf" ],
        notify  => Exec["sysctl_reload"];
    }

    exec { "sysctl_reload":
        command => $kernel ? {
            FreeBSD     => "/etc/rc.d/sysctl reload",
            Linux       => "/sbin/sysctl -q -p /etc/sysctl.conf",
        },
        refreshonly => true
    }
}


После чего снова запускаем fabric, который сделает puppet apply на нужных нам серверах:

fab deploy_p

Вот так, благодаря полной автоматизации Linux-хостинга, наши админы не тратят время на рутинные задачи, а решают действительно нестандартные проблемы. Это я вам как админ говорю. :)
Tags:
Hubs:
Total votes 11: ↑10 and ↓1 +9
Views 9.3K
Comments Comments 5

Information

Founded
1999
Location
Россия
Website
invs.ru
Employees
51–100 employees
Registered