29 June 2015

Свой облачный хостинг за 5 минут. Часть 1: Ansible, Docker, Docker Swarm

Website development
Cloud hosting

Привет Хабр! Последние 1.5 года я работал над своим проектом, которому был необходим надежный облачный хостинг. До этого момента я больше 10 лет занимался веб-программированием и когда я решил построить свой хостинг у меня были относительно поверхностные знания в этой области, я и сейчас не являюсь системным администратором. Все что я буду рассказывать может выполнить обычный программист в течение 5 минут, просто запустив набор сценариев для Ansible, которые я подготовил специально для вас и выложил на GitHub.

Моя цель – дать вам список инструментов и общее понимание, что бы вы знали от чего отталкиваться, если у вас появится необходимость в собственном облачном хостинге. При выборе используемых инструментов я ориентировался на простоту, качество документации и стабильность. Прежде, чем использовать все это у себя в продакшене, вам определенно стоит проконсультироваться с системным администратором (я использую некоторые компоненты, которые находятся в статусе «BETA» (июнь 2015)).

Содержание



Зачем свой хостинг?


Основная причина — я хотел получить необходимый опыт. Я все больше отхожу от программирования и занимаюсь административными вопросами. Хорошему предпринимателю надо пройти весь путь самому и поработать, по возможности, на всех должностях, что бы понять как все устроено, как этим управлять, от каких людей и что требовать, как оценить их работу и их самих.

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

Последняя причина в целях и ориентирах у стартапа в Российских реалиях. Здесь основная цель – начать зарабатывать деньги и выйти в плюс. Нет прибыли – нет стартапа, есть убыточное хобби. Поэтому, третья причина – это стоимость. У меня сейчас ~9Tb трафика и ~5Tb данных, которые я регулярно обрабатываю, обходится мне все это ~100$/месяц (можете посчитать сколько это будет стоить на AWS). Я знаю, что в следующем месяце у меня будет такая же стоимость, а проект я строю на свои деньги.

Подготовка


Первое, что необходимо сделать – раздобыть 3 сервера в одном датацентре (они должны находится как можно ближе друг к другу, что бы ping между ними был минимальный). Не имеет значения, будут это виртуальные выделенные сервера (на время тестирования) или настоящие и у какого провайдера вы их арендуете. Я заказал у DigitalOcean, выбрал установку Debian 8.1 x64 и указал добавить свой SSH ключ:



Установка закончена и у нас в распоряжении 3 «голых» сервера:



Ansible


Как вы уже поняли, мы будем использовать Ansible для конфигурирования наших серверов. Если вы не знаете что это такое и как этим пользоваться, то на Хабре есть ответы на эти вопросы:

  1. Система управления Ansible
  2. Ansible
  3. Ansible — давайте попробуем

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

Ansible не единственная система управления конфигурациями (есть Puppet, Chef, Salt и т.д.), так почему именно она?

Как я написал выше, один из приоритетов при выборе инструментов – простота. На управляемые машины не надо устанавливать клиенты (все работает через SSH), язык сценариев предельно прост, у проекта свежая и подробная документация, а код модулей написан на Python (что для нас преимущество, потому что Python является основным языком в стартапе).

Мысли о простоте
Вообще для меня простота является признаком глубокого понимания предмета. Если один человек (знакомый с предметом), не может объяснить другому человеку (с предметом не знакомому), как он работает, значит он сам до конца не понимает этот предмет.

Это хорошо раскрыто в книге Стива Возняка – «Cтив Джобс и Я», там отец начинает рассказывать принципы электротехники Стиву, когда он еще не достиг четырех лет (книга будет интересна всем инженерам, даже если вас не интересует история Apple).

На этом этапе Ansible должен быть установлен на вашей клиентской машине (инструкция). Мне, на OS X 10.9, для этого понадобилось выполнить всего 2 команды:

» sudo easy_install pip # если у вас еще не установлен PIP
» sudo pip install ansible

Проверяем, что все ок:

» ansible --version
ansible 1.9
  configured module search path = None


Docker


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

«Из коробки» мы получаем доступ к огромному количеству готовых образов, которые мы сможем моментально выполнить в нашем облаке. У нас появляется возможность изолированно запускать необходимые сервисы разных версий одновременно, для тестирования совместимости или удовлетворения зависимостей наших веб-приложений.

Мы можем запустить 20 контейнеров с 1-ой версией нашего веб-приложения, 2 контейнера со 2-ой версией и распределив между ними нагрузку, показать новую версию только ~10% посетителей, оценив стабильность работы и отзывы пользователей.

Сейчас вам необходимо установить Docker на клиентскую машину, он понадобится нам для управления будущим кластером Docker'ов. Самый простой способ сделать это – скачать GUI клиент Kitematic (доступен для Mac OS X 10.9+ и Windows 7+ 64-bit), зайти в главное меню и выбрав «Install Docker Commands» установить консольные комманды Docker'а.



Альтернативные варианты установки вы можете узнать из официальной документации (она хорошо написана и своевременно обновляется). Убедиться, что все в порядке, можно следующим образом:

» docker version  
Client version: 1.6.2
Client API version: 1.18
Go version (client): go1.4.2
Git commit (client): 7c8fca2
OS/Arch (client): darwin/amd64


Docker Swarm


Docker Swarm
Наконец-то мы дошли до самого интересного, до того, что придаст нашему хостингу «облачности». Странно, но я не нашел никакой информации о Docker Swarm на Хабре.

Docker Swarm служит для объединения множества Docker хостов в один виртуальный хост и делает это элегантно. Docker Swarm предоставляет REST API интерфейс, совместимый с Docker API. Таким образом, все инструменты, которые работают с API Docker'a (клиент Docker'a, Dokku, Compose, Krane, Flynn, Deis, DockerUI, Shipyard, Drone, Jenkins и т.д.), смогут работать с Docker Swarm, не подозревая о том, что за ним стоит кластер Docker'ов, а не одна машина.

Давайте уже построим наше облако и на практике посмотрим на что способен Docker Swarm.

Приступаем


К этому моменту у вас на клиентской машине должен быть установлен Ansible и Docker. В наличии должно быть 3 сервера с авторизацией по ключу и Debian 8.1 x64 на борту (вы можете использовать любой другой дистрибутив, внеся небольшие изменения в сценарии). Я подготовил набор сценариев для Ansible, которые сделают всю работу за вас, поэтому вам не понадобится много времени.

Скачиваем набор сценариев или клонируем репозиторий:

» git clone https://github.com/vkozlovski/ansible-cloud-hosting
» git checkout v1.x
» cd ansible-cloud-hosting

Открываем файл stage и заменяем в нем IP адреса на IP своих серверов:

[cloud]
188.166.16.70   debian_release=testing   hostname=debian1
188.166.99.31   debian_release=testing   hostname=debian2
128.199.59.102  debian_release=testing   hostname=debian3

Для того, что бы Docker Swarm мог соединяться с нодами Docker'a, они должны быть доступны снаружи (по умолчанию на 2375-ом порту для HTTP и на 2376-ом для HTTPS). Также нам надо сделать доступным снаружи и Docker Swarm Manager, что бы мы могли управлять кластером. HTTP нам для этих целей не подходит (мы же строим облако для себя, а не любого интернет-пользователя), остаётся HTTPS, а точнее TLS (подробнее можно почитать в официальной документации).

Принцип работы следующий: мы создаём свой центр сертификации (далее ЦС), подписываем сертификаты для сервера и клиента Docker'a. После этого «демон» докера принимает соединения от клиентов, сертификат которых подписан тем же ЦС, что и сертификат «демона». Клиент Docker'а выполняет такую же проверку и подключается только к тем серверам Docker'a, сертификат которых подписан тем же ЦС. Docker Swarm Manager использует такую же схему. Таким образом обеспечивается аутентификация и безопасность нашего мини-облака.

Единственное, что вам требуется сделать, это сгенерировать ключи для вашего ЦС (все остальное выполнится в автоматическом режиме). Просто выполните следующие команды из корневой директории проекта (пароль, который вы укажите, необходимо запомнить, он нам понадобится) и заполните ответы на вопросы (тут нет никаких конкретный требований, домен можете указать любой):

» openssl genrsa -aes256 -out certs/ca/ca-key.pem 4096
» openssl req -new -x509 -days 365 -key certs/ca/ca-key.pem -sha256 -out certs/ca/ca.pem

Осталось заполнить значения некоторых переменных в файле:

group_vars/all.yml
certs:
  ca:
    password: "YOUR PASSWORD HERE"

docker_swarm:
  # docker run --rm swarm create
  token: "YOUR DOCKER SWARM TOKEN HERE"
  manager: "YOU DOCKER SWARM MANAGER IP HERE"

ssh:
  users:
    # user for ansible
    - user: "support"
      shell: "/bin/zsh" # for oh-my-zsh
      groups: "sudo"
      # mkpasswd --method=SHA-512
      password: "YOUR PASSWORD HERE"
      # cat ~/.ssh/id_rsa.pub
      key: "YOUR PUBLIC KEY HERE"


Переменная certs.ca.password должна содержать пароль, который мы указали, когда генерировали приватный ключ для нашего центра сертификации.

Переменная docker_swarm.token должна содержать идентификатор нашего будущего кластера, который можно сгенерировать следующей командой:

» docker run --rm swarm create
6856663cdefdec325839a4b7e1de38e8

Переменная docker_swarm.manager должна содержать IP адрес хоста, где будет запущен Docker Swarm Manager (укажите IP адрес любого из ваших серверов).

В сценариях Ansible указано, что необходимо создать пользователя support, добавить его в группу sudo и запретить root'у возможность авторизации по SSH. Значение переменной ssh.users[].password должно быть хешем пароля для указанного выше пользователя. Для того, что бы его получить, необходимо выполнить следующую команду на любой Linux машине (можете зайти на один из ваших серверов по SSH и выполнить ее):

» mkpasswd --method=SHA-512
Password: 
$6$n0lQGWy2s5437ns1$nczULrrTw5r.TmhEI/xBz5xYEHWyMbtbQhAJoWshv0rFjSoRxLYZh0zDoMwVM.ZFnChx7ym.4.r182EOIn9Ec/

Значение ssh.users[].key должно быть вашим публичным ключом, который по умолчанию находится тут ~/.ssh/id_rsa.pub:

~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDakR6/NJuIxJGpWZiV5QODuRacuh4VoIdzeOUlH+MC1xxFf/U74gfumoQ1k62d4S0qPqnlpJVqRNg7mQo0CCguujVRuOP+FbRMjdQbbAEfDAjxkSuKXRWnChg6Ds4MX0RO9WUB9pQLrCmbPpI8A0l0zaNi6mp6TaXWCoMPrgk1OfuTYDBTgQya/MtKjjPnWFEdgwTvB3hVusCU83854Gju9OX4I5ucejy/f8/IWGUIFt8miLisrYnDcdFvY/ThzX7E2h4lzsc8JK6ltywyoHVScl3Vdgf1/WbScEnQYWJZk/h2zWDqdFlte/elov7l3yfJDN0axF+dC6BqJGfZMFQsN6xPWHoG8OCPaH2HmTY3XNpQNRLJR5GFWwVriIBDe+GyVjtjzb1/BLdu0WJatv6/PYMsEfArYBgnQ/bHYgfFLF0GPq6nBGJbngytv7C5crBljZAvnC86HQN5sGPbZWfkKdvoG5MbbJYwqFvaD2BEjlXMurbCX4ERrRUHC9875XufaCvdpCiSWaU4S5PM8nr2QCF3co0EtL0bkiciyv95eU0HoQ3cYhPNGwUMxntPp/3z0KRqeSBHPnvV5pmXOwP/xUHxEIvDbqCXtTc3y96iDKZ1T8+jPH1aif6ooVUwmogTHDd1DcW+APtMkqRHdZ7r33wKAYJfNopd+0P8rYF2w==


Пример заполненного файла конфигурации можете глянуть ниже:

group_vars/all.yml
certs:
  ca:
    password: "12345"

docker_swarm:
  # docker run --rm swarm create
  token: "6856663cdefdec325839a4b7e1de38e8"
  manager: "188.166.16.70"

ssh:
  users:
    # user for ansible
    - user: "support"
      shell: "/bin/zsh" # for oh-my-zsh
      groups: "sudo"
      # mkpasswd --method=SHA-512
      password: "$6$n0lQGWy2s5437ns1$nczULrrTw5r.TmhEI/xBz5xYEHWyMbtbQhAJoWshv0rFjSoRxLYZh0zDoMwVM.ZFnChx7ym.4.r182EOIn9Ec/"
      # cat ~/.ssh/id_rsa.pub
      key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDakR6/NJuIxJGpWZiV5QODuRacuh4VoIdzeOUlH+MC1xxFf/U74gfumoQ1k62d4S0qPqnlpJVqRNg7mQo0CCguujVRuOP+FbRMjdQbbAEfDAjxkSuKXRWnChg6Ds4MX0RO9WUB9pQLrCmbPpI8A0l0zaNi6mp6TaXWCoMPrgk1OfuTYDBTgQya/MtKjjPnWFEdgwTvB3hVusCU83854Gju9OX4I5ucejy/f8/IWGUIFt8miLisrYnDcdFvY/ThzX7E2h4lzsc8JK6ltywyoHVScl3Vdgf1/WbScEnQYWJZk/h2zWDqdFlte/elov7l3yfJDN0axF+dC6BqJGfZMFQsN6xPWHoG8OCPaH2HmTY3XNpQNRLJR5GFWwVriIBDe+GyVjtjzb1/BLdu0WJatv6/PYMsEfArYBgnQ/bHYgfFLF0GPq6nBGJbngytv7C5crBljZAvnC86HQN5sGPbZWfkKdvoG5MbbJYwqFvaD2BEjlXMurbCX4ERrRUHC9875XufaCvdpCiSWaU4S5PM8nr2QCF3co0EtL0bkiciyv95eU0HoQ3cYhPNGwUMxntPp/3z0KRqeSBHPnvV5pmXOwP/xUHxEIvDbqCXtTc3y96iDKZ1T8+jPH1aif6ooVUwmogTHDd1DcW+APtMkqRHdZ7r33wKAYJfNopd+0P8rYF2w=="


Теперь мы готовы приступить к построению долгожданного облака:

» ansible-playbook -i stage site.yml -u root

Из-за того, что мы отключили возможность авторизации root'ом, а также добавили пользователя support (это прописано в сценариях Ansible), все последующие запуски надо выполнять с флагами -s (sudo) и -K (для запроса пароля для sudo):

» ansible-playbook -i stage site.yml -u support -s -K


Проверка и использование


Мы готовы к проверке нашего новоиспеченного облака:

» docker -H tcp://188.166.16.70:8000 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem info
Containers: 4
Images: 3
Storage Driver: 
Role: primary
Strategy: spread
Filters: affinity, health, constraint, port, dependency
Nodes: 3
 debian1: 188.166.16.70:2376
  └ Containers: 2
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 519.2 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.16.0-4-amd64, operatingsystem=Debian GNU/Linux stretch/sid, storagedriver=aufs
 debian2: 188.166.99.31:2376
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 519.2 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.16.0-4-amd64, operatingsystem=Debian GNU/Linux stretch/sid, storagedriver=aufs
 debian3: 128.199.59.102:2376
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 519.2 MiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.16.0-4-amd64, operatingsystem=Debian GNU/Linux stretch/sid, storagedriver=aufs
Execution Driver: 
Kernel Version: 
Operating System: 
CPUs: 3
Total Memory: 1.521 GiB
Name: 
ID: 
Http Proxy: 
Https Proxy: 
No Proxy:

Ура! Я почти дописал эту большую статью, а вы успешно закончили построение своего облака.

Если у вас появится необходимость, то вы можете подключаться к любому из Docker хостов отдельно:

Пример
» docker -H tcp://128.199.59.102:2376 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem info
Containers: 2
Images: 7
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 11
 Dirperm1 Supported: true
Execution Driver: native-0.2
Kernel Version: 3.16.0-4-amd64
Operating System: Debian GNU/Linux stretch/sid
CPUs: 1
Total Memory: 494.5 MiB
Name: debian1
ID: 5XPE:2VWX:QCSA:J3PJ:WMN7:EDXX:3TSS:7K7K:XU4R:Z3AX:TRVX:VTUQ
WARNING: No memory limit support
WARNING: No swap limit support


Но мы же не для этого прошли весь этот путь, правда? Давайте попробуем запустить несколько экземпляров Nginx в нашем облаке:

» docker -H tcp://188.166.16.70:8000 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem run -d -p 80:80 --name nginx1 nginx
bb49018b697fca975d10a5ec31ad2fed65ed12b3ad8fbd61e64474187d8bc6ed

» docker -H tcp://188.166.16.70:8000 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem run -d -p 80:80 --name nginx2 nginx
2bd86ff97c35d431e9db7f0571d65e17893aefd1d18b1b52194c100a22a49937


Проверяем:
Nginx

Давайте попробуем запустить еще 2 экземпляра Nginx:

» docker -H tcp://188.166.16.70:8000 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem run -d -p 80:80 --name nginx3 nginx
622b4e199c700cae663bf2e2f326918f94a0cd016c27dc9ff39f4ec4abf7bdb1

» docker -H tcp://188.166.16.70:8000 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem run -d -p 80:80 --name nginx4 nginx
FATA[0001] Error response from daemon: unable to find a node with port 80 available 

Как мы видим 3-ий экземпляр запустился, а вот 4-ый нет: FATA[0001] Error response from daemon: unable to find a node with port 80 available. Docker Swarm Scheduler видит, что нет хостов со свободным 80-ым портом.

Мы можем посмотреть, какие контейнеры сейчас выполняются у нас в кластере и удостовериться, что каждая копия Nginx была запущена на разной машине:

» docker -H tcp://188.166.16.70:8000 --tlsverify=true --tlscacert=certs/ca/ca.pem --tlscert=certs/docker/cert.pem --tlskey=certs/docker/key.pem ps                                                      1 ↵
CONTAINER ID        IMAGE                  COMMAND                CREATED             STATUS              PORTS                                NAMES
622b4e199c70        nginx:latest           "nginx -g 'daemon of   5 minutes ago       Up 5 minutes        128.199.59.102:80->80/tcp, 443/tcp   debian3/nginx3                 
2bd86ff97c35        nginx:latest           "nginx -g 'daemon of   16 minutes ago      Up 16 minutes       188.166.16.70:80->80/tcp, 443/tcp    debian1/nginx2                 
bb49018b697f        nginx:latest           "nginx -g 'daemon of   17 minutes ago      Up 17 minutes       188.166.99.31:80->80/tcp, 443/tcp    debian2/nginx1                 
148eef0bbe02        library/swarm:latest   "/swarm manage --tls   2 hours ago         Up 2 hours          188.166.16.70:8000->2375/tcp         debian1/docker-swarm-manager   
3545322d27b7        library/swarm:latest   "/swarm join --addr=   2 hours ago         Up 2 hours          2375/tcp                             debian2/docker-swarm           
faaa78cbedba        library/swarm:latest   "/swarm join --addr=   2 hours ago         Up 2 hours          2375/tcp                             debian3/docker-swarm           
0fee12f6a473        library/swarm:latest   "/swarm join --addr=   2 hours ago         Up 2 hours          2375/tcp                             debian1/docker-swarm 

Возможности Docker Swarm на этом не заканчиваются, а только начинаются (об этом можно почитать тут и тут).

Как я писал в начале статьи, моя цель – дать вам список инструментов и общее понимание, что бы вы знали от чего отталкиваться и я надеюсь, что она достигнута.

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

На этом все. Всем спасибо за внимание. Стабильных вам облаков и удачи!

Подписывайтесь на меня в Twitter, я рассказываю о работе в стартапе, своих ошибках и правильных решениях, о python и всём, что касается веб-разработки.

P.S. Я ищу разработчиков в компанию, подробности у меня в профиле.
Tags:ansibledockerdocker swarmkitematiccloud hostinghosting
Hubs: Website development
+62
125.6k 1070
Comments 42
Top of the last 24 hours