Namdak Tonpa (maxim) wrote,
Namdak Tonpa
maxim

Как написать PaaS за 30 минут на Erlang

Есть такое золотое правило:

1. То, что можно сделать за год компанией из 100 человек, можно набросать за 1 месяц двум ребятам.
2. То, что можно написать за месяц можно всегда написать за неделю.
3. То, что можно написать за неделю, можно всегда написать за день.
4. То, что можно написать за день, всегда можно написать за час.

3 августа я буду показавывать решать неподъемные казалось задачи, такие как создание собственной клауд платформы для хостинга приложений. Всегда есть утилиты или языки, которые по функциональности опережают свои аналоги в десятки раз. Если собрать в один проект такие технологии то можно реально выстрельнуть серебряной пулей. Или же такое можно сделать путем долгой медации. Вы же знаете об этом чувстве когда некоторый вирус сидит в вашей голове и пока вы не напишете, не выразите это в коде оно не проходит. И вы делаете все возможное, что бы освободиться от этой назойливой мысли как можно быстрее. Сердцем нашего PaaS безусловно являются наши приложения и Docker который управляет линукс контейнерами. Поэтому если вы хотите написать PaaS вам нужно отталкиваться от докера, который будет создавать приложения. Интерфейс у докера простой:

ps -- вывести список запущенных LXC контейнеров
start/stop -- остановка и запуск
run -- создать новый LXC контейнер
commit -- закоммитить текущую ФС LXC контейгера в AUXFS
push -- послать контейнер на сервера dotcloud

Вот пример

maxim@do2:~$ docker ps
ID                  IMAGE                                      COMMAND                PORTS
d8e5a5194d17        voxoz/paul+gh@paulmillr.comsncn55:latest   /usr/bin/supervisord   49180->22, 49181->8000, 49182->8989   
62dda556d9e9        voxoz/maxim@synrc.comsncn34:latest         /usr/bin/supervisord   49183->22, 49184->8000, 49185->8989


Вот как создавайть контейнеры, наш прото-контейнер который используется в нашем клауде это "voxoz/precise". Перед его использование нужно есть скачать себе:

docker pull voxoz/precise


Запустить его тоже просто:

docker run -d -p 22 voxoz/precise /usr/bin/supervisord -n
4d79a44639b1


Узнаем порт-маппинг

docker port 4d79a44639b1 22
49186


Заходим на него по SSH (логин рута у нашего прото-контейнера "password"):

sshpass -p password ssh root@172.16.42.1 -p 49186


Все теперь нужно написать обертку вокруг этих команд, разнести это по серверам, написат планировшик ресурсов CPU, RAM, сделать контролль I/O и диска и у вас будет готов PaaS. Напишем на Erlang обертку.

Для всех юзеров мы генерируем уникальные рутовые пароли из Dockerfile шаблона из которого генерируется новый пользовательский контейнер

FROM voxoz/precise
RUN sed -i '1d' /etc/shadow
RUN echo 'root:{{password}}:15881:0:99999:7:::' >> /etc/shadow
EXPOSE 22
CMD ["/usr/bin/supervisord", "-n"]


Пароль будем генерировать с помощью

make_pass() ->
    Res = os:cmd("makepasswd --char=12"),
    [Pass] = string:tokens(Res,"\n"),
    Res2 = os:cmd(["mkpasswd -m sha-512 ",Pass]),
    [Code] = string:tokens(Res2,"\n"),
    {Pass,Code}.


Dockerfile из темплейта с помощью erlydtl:

make_docker_template(Hostname,User,Pass) ->
    erlydtl:compile(code:priv_dir(ins) ++ "/" ++ "Dockerfile.template",docker_template),
    {ok,File} = docker_template:render([{password,Pass}]),
    os:cmd(["mkdir -p users/",User,Hostname]),
    file:write_file(["users/",User,Hostname,"/Dockerfile"], File).


Генерация уникального пользовательского контенера по шаблону

docker_build(Hostname,User) ->
    Res = os:cmd(["docker build users/",User,Hostname]),
    Tokens = string:tokens(Res,"\n"),
    [Success,Id,LXC|Rest] = lists:reverse(Tokens),
    Running = string:tokens(LXC," "),
    hd(lists:reverse(Running)).


Запуск контейра с помощью run, как мы уже показывали выше:

docker_run(Hostname,User,Cpu,Ram,Ports) ->
    P = string:join([ "-p " ++ integer_to_list(Port) || Port <- Ports], " "),
    Cmd = ["docker run -d ",P," -m=",integer_to_list(Ram)," -h=\"",Hostname,"\" voxoz/",User,Hostname,
           " /usr/bin/supervisord -n"],
    Res = os:cmd(Cmd),
    Tokens = string:tokens(Res,"\n"),
    hd(lists:reverse(Tokens)).


Ну и функцию которая показываем нам на ка каком внешнем порте висит 22 SSH порт:

docker_port(Id,Port) ->
    Res = os:cmd(["docker port ",Id," ",integer_to_list(Port)]),
    case string:tokens(Res,"\n") of
        [Tokens] -> list_to_integer(Tokens);
	_ -> 0 end.


Соберем все вместе:

create_box(Hostname,User,Cpu,Ram,Cert,Ports) ->
    {Pass,Code} = make_pass(),
    make_docker_template(Hostname,User,Code),
    LXC = docker_build(Hostname,User),
    docker_commit(LXC,Hostname,User),
    Id = docker_run(Hostname,User,Cpu,Ram,Ports),
    PortMap = [{Port,docker_port(Id,Port)}|| Port <- Ports],
    Box = #box{id=Id,host=Hostname,region=node(),pass=Pass,portmap=PortMap,
                user=User,ssh=proplists:get_value(22,PortMap),datetime=calendar:now_to_datetime(now()),name=Hostname},
    kvs:put(Box),
    Box.


Как видите есть еще две фукнции:

docker_commit(Id,Hostname,User) -> os:cmd(["docker commit ",Id," voxoz/",User,Hostname]).
docker_push(Hostname,User) -> os:cmd(["docker push voxoz/",User,Hostname]).


Эти функции позволяют версионировать содержимое ваших контейров и переносить их таким образом на другие сервера (тут закомитил -- там поднял). Похоже на какой DevOps аферизм? Именно так оно и есть. В третьем тысячилетии можно быстро создать что угодно. От PaaS до операционной системы. Следите за нами, мы покажем вам как создавать сложнейшие продукты в кратчайшие сроки.

Если вам понравилось ставим звезду на гитхабе: https://github.com/synrc/instance_server
Tags: cs
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

  • 1 comment