2017-05-07

Об анонимности

В кои-то веки прочитал «1984». Тот самый роман британца Джорджа Оруэлла.
Ingsoc
В общем-то, никакого особого шока или откровения я не испытал. После Сталина и Гитлера, после Афганистана, Чечни, распада Союза, 9/11, в XXI веке, где Интернет проник повсюду, и везде, где он проник, за тобой действительно следят, когда Роскомнадзор решает, будет ли у тебя сегодня работать GitHub, это всё совсем не удивительно.
Тогда, в 1948, сразу после окончания большой войны, всё, конечно, представлялось не так. Вероятно, казалось, что теперь всё будет хорошо. Ну Оруэлл и показал наглядно, что расслабляться рано. Да и сейчас рано.
Меня больше удивила художественная сторона романа. Ведь хорошо, чертяка, пишет. Или это перевод Голышева настолько великолепен? А ещё интересны некоторые психологические моменты...
Телекран
Поехали...
«Война — это мир». «Свобода — это рабство». «Незнание — сила». «Старший Брат смотрит на тебя». Это чтобы вы вспомнили, о чём эта книга. Это всё — партийные лозунги. Партии, которая единственная Партия в книге. Кто жил в СССР, тот понимает, какая.
Первые три лозунга — примеры двоемыслия. Но о двоемыслии позже.
Последняя фраза, в другом переводе: «Большой Брат следит за тобой». Забавно, что до Оруэлла Big Brother означал именно что самого обычного старшего брата. Ну который водит младших в школу, например. Самый обычный родной старший брат. А вот после Оруэлла ББ — это именно тот ББ, который следит.
Сокращение BB встречается в самом романе. Тем забавнее внезапно встретить в Понечках BBBFF — Big Brother Best Friend Forever.
Телекран. Собственно, штука, которой ББ следит. Большой неотключаемый экран в каждом доме члена партии, а также понатыканный во всех публичных местах. Говорит и показывает, но и следит. Как шутят гики XXI века, телекран, он не на стене, он в кармане. Знаете, такой маленький пятидюймовый экранчик, который вы часто из кармана достаёте, чтобы ему удобнее было следить, ещё пальцем в него тыкаете. Сейчас телекран действительно не смешон и не удивителен. Смиритесь.
Пролы. «Если есть надежда, то она в пролах». Пролы — это пролетарии. Почему-то у них нет телекранов. Их существование почему-то вообще никого не волнует. Хотя их — большинство. Современный термин: быдло. А надежда в том, что только пролы смогут свергнуть существующий строй. На самом деле нет.
Ангсоц. Ingsoc. Aнглийский социализм. Собственно, политический строй Океании. Почему-то это слово всегда пишут на логотипах, связанных с 1984. Кажется, это пошло от фильма, снятого, о ужас, в 1984 году.
«Кто управляет прошлым, тот управляет будущим; кто управляет настоящим, тот управляет прошлым». Управлением прошлым занимается Министерство правды, оно же миниправ. Всё, что говорил Старший Брат, должно быть правдой. Все прогнозы, что делала партия, должны быть правдой. Приходится уничтожать старую правду и рисовать новую правду. Впрочем, как минимум один раз вроде как уничтоженный документ снова объявился.
«Океания воевала с Евразией: Океания всегда воевала с Евразией». Но внезапно противник-союзник меняется, и Океания воюет уже с Остазией. Большой прикол в романе. За всего лишь неделю работы миниправ переправляет «Евразию» на «Остазию» везде, во всех выпущенных газетах и книгах как минимум.
Вроде сейчас не занимаются тотальным переписыванием прошлого. Технически неосуществимо. Учебники истории, конечно, переписываются. Ну и статьи в Википедии правятся. Но вроде и всё. А внедрение блокчейна вроде как вообще лишает возможности хоть как-то подделать прошлое. Туда и внедряют, где требуется непогрешимая истина в исторической перспективе.
Океания воюет с Евразией. «Сущность войны — уничтожение не только человеческих жизней, но и плодов человеческого труда. Война — это способ разбивать вдребезги, распылять в стратосфере, топить в морской пучине материалы, которые могли бы улучшить народу жизнь и тем самым в конечном счёте сделать его разумнее. Даже когда оружие не уничтожается на поле боя, производство его — удобный способ истратить человеческий труд и не произвести ничего для потребления».
Страшно и бессмысленно. Надеюсь, наши настоящие власти придумывают более интересные способы траты излишков. Например, можно потратиться на безусловный базовый доход.
Впрочем, Оруэлл явно говорит о цели существования власти. Это — не благосостояние граждан. Цель существования власти — в сохранении власти. Как ни печально, но в это очень трудно не поверить.
Ну а чтобы партийные не мешали власти быть у власти, имеются некоторые интересные упражнения. Например, двухминутки ненависти. Иступлённо, истерично ненавидим врагов партии. Перед телекранами, чтобы каждый мог продемонстрировать свою преданность. Ну и можно было бы вычислить недовольных.
А ещё есть прекрасный новояз. Специальный упрощённый вариант английского, на котором просто нельзя выразить что-либо, порочащее существующий строй. Да и вообще нельзя выразить ничего, что не требуется в быту для поддержания ежедневного существования. «Чем меньше выбор слов, тем меньше искушение задуматься».
Зато с преступлениями всё просто. Вообще не существует явных запретов. Но есть действия, которые не одобряет партия. Есть только один вид преступления: мыслепреступление. Подумал что-то не то — виновен. И не важно, засёк телекран твоё дёрнувшееся лицо или услышал бормотание во сне. Ты уже подумал. Ты уже виновен. И рано или поздно будешь наказан.
Вот это уже страшно. Тамошние полиция мыслей и министерство любви, которые как раз и занимаются поиском, перевоспитанием и уничтожением мыслепреступников, явно жалеют, что технологии не позволяют читать мысли явно. Сможем ли мы?
Перевоспитание мыслепреступников выполняется вполне себе классическими методами. В связи с этим вспоминается Виктор Франкл, который в своём «Человеке в поисках смысла» утверждает, что что бы с человеком ни делали, чего бы ни лишали и как бы ни пытали, у него всегда остаётся выбор: как к этому относиться. Оруэлл же утверждает, что в человеке сломать можно всё, даже его глубинные убеждения. Хочется поверить Франклу, всё же у него есть реальный опыт отсидки в нацистских лагерях.
Наконец, моё любимое двоемыслие. Отличная психологическая практика, которая используется нами повсеместно.
Вот например, есть у вас какие-нибудь неприятные детские воспоминания, которые вы стараетесь забыть? Вы знаете, что это было. Но делаете вид, что ничего не было. Никаким новым знакомым про это никогда не рассказываете. А если есть свидетели или соучастники события, они тоже предпочитают об этом не говорить. Вы знаете, что это было. Но вы сознательно думаете, что этого не было. Это — двоемыслие.
Или пример попроще. Об умершем человеке почему-то не принято говорить плохо. Будь он хоть трижды негодяй, никто про это не скажет и все будут делать вид, будто ничего не было. Это — двоемыслие.
Во всех случаях, когда мы, в первую очередь сами для себя, выдаём желаемое за действительное, а действительное тщательно хороним в дальних уголках памяти, мы проявляем двоемыслие. Это очень мощная и распространённая практика. Хотя у Оруэлла она ещё и направлялась извне, партией, на благо партии.
С психологией в романе всё странно. У Макса Фрая есть концепция вершителя. Вершитель — это такое мм... существо, все желания которого сбываются, рано или поздно, так или иначе. Вот и у главного героя Оруэлла всё сбывается. Захотел женщину, получил женщину. Захотел побунтовать против партии, побунтовал. Захотел уподобиться опальным героям революции, прошёл в точности их путь (не минуя министерство любви, конечно). Захотел полюбить Старшего Брата, полюбил. Да что там, в книге полно мест, где у героя промелькнёт мысль, а следующей строчкой происходит в точности, что он подумал, или кто-то рядом произносит эту мысль вслух слово в слово.
Либо мир Оруэлла полон магии, либо это не описание объективной (пусть даже выдуманной) реальности. На продуктивные симптомы шизофрении смахивает.
«Человечество стоит перед выбором: свобода или счастье, и для подавляющего большинства счастье — лучше».
Apple 1984
К чему это я? А может ну его, этого Старшего Брата? Пусть следит.
Допустим, законы остаются прежними. Порнографию детям смотреть нельзя. Наркотиками торговать нельзя. Но давайте исключим из взаимодействия третью сторону, оставим ей только контроль. Я имею в виду текущую деятельность Роскомнадзора в Рунете и обязательства интернет провайдеров перед ним.
Вот есть сайт, на котором, среди прочего, есть контент только для взрослых. И есть некий ребёнок, который захотел этот контент посмотреть. Если сайт запретил — всё ок. Если сайт разрешил — штраф сайту. И ещё можно штраф родителям ребёнка.
Конечно всё-таки нужна сторона, которая будет следить и штрафовать, если надо. И ей надо знать, кто, когда и куда ходил. Да, интернет по паспорту. Да, никакой анонимности. Примерно так, как вскользь упоминалось у Вернона Винджа в «Конце радуг».
И не нужны дурацкие блокировки. Ибо теми, кому надо, они легко обходятся. А тем, кому не надо, они только доставляют проблем. Ибо невозможно заблокировать только то, что хочется заблокировать. Да и смысла в этом нет. Даже смысла власти таким образом стремиться оставаться у власти.
Но придётся избавиться от анонимности. Почему мы держимся за анонимность? Неужели только для того, чтобы врать? Врать во всех смыслах. Провоцировать, унижать и вообще вести себя неприлично. Давать взятки. Делать то, что делать нельзя.
Впрочем есть такой момент, за который держатся сторонники анонимности. Ругать Старшего Брата принято анонимно. Точнее, должно иметь возможность ругать анонимно. Но что-то мне кажется, что ругать кого бы то ни было анонимно — как минимум дурной тон. С другой стороны, если полиция мыслей не дремлет, других вариантов может и не быть.
Нет выхода? Живём на неоптимальных компромиссах?

2017-04-16

О Docker Swarm Mode

Вернёмся к нашему рою Докеров. Я уже писал про Docker Swarm. Это когда вы можете запустить кучку Docker Engine в кластере ваших компутеров и управлять контейнерами в этом кластере. Тогда речь шла о Docker Swarm, который представляет собой несколько контейнеров, запущенных в обычном Докере. А с версии Docker Engine 1.12 (июль 2016) у нас появился Swarm Mode, непосредственно внедрённый в Docker Engine. Вот до него-то я и добрался.
Напоминаю. Раньше мы брали самый обыкновенный Докер. Запускали в нём, на каждом физическом узле, контейнер с Consul, чтобы иметь единое распределённое хранилище для метаданных нашего кластера. Потом запускали специальный образ Swarm в виде менеджера (тоже, при необходимости, распределённого) и узлов (воркеров) кластера. Этот Swarm контейнер получал доступ к Docker Engine API на каждом узле, где он запущен, путём проброса сокета Докер демона. И клиенты, включая Docker Compose, общались удалённо теперь уже с менеджером Swarm кластера по тому же самому протоколу и API. Т.е. замена одного Docker Engine на кластер Docker Swarm для клиента и пользователя выглядела почти прозрачной.
Docker pic
Теперь всё по-другому. Теперь Swarm Mode — это особый режим работы Docker Engine. И поддерживается он прямо из коробки. И привносит свои нюансы.
Docker развивается стремительно. Версия 1.12 осталась в далёком прошлом. Так же как и версия 1.13. Теперь Докер нумеруется а-ля Убунту, и текущая версия 17.04.0, соответственно, от апреля 2017 года.
Теперь у нас два Докера: Community Edition и Enterprise Edition. Второй от первого отличается, пока что, лишь наличием платной поддержки. Но, например, Community Edition уже официально не поддерживается под RedHat Enterprise Linux.
Допустим, у нас есть две-три машинки, на которые мы хотим водрузить Docker Engine и объединить их в Swarm.
Для начала нам нужно немного поконфигурировать Докер демон. Учитывая неразбериху со способами инициализации (даже в Убунту проник systemd, а в Дебиане до сих пор можно выбирать между systemd и sysvinit), универсальным способом конфигурирования становится файл /etc/docker/daemon.json
. Об этом способе часто умалчивают. Ну и это JSON, со всеми его прелестями в виде отсутствия комментариев и необходимости не запутаться, где там должен быть массив, а где просто значение.
Нам очень может понадобиться обращаться к нашему кластеру извне, а не только с менеджерских хостов. Поэтому добавляем прослушивание Докер демоном TCP сокета. При этом надо явно добавить и Unix сокет, который прослушивается по умолчанию, иначе локальная команда docker перестанет работать.
"hosts": [
  "unix:///var/run/docker.sock",
  "tcp://0.0.0.0:2375"
]
Если у нас есть локальный Docker Registry, и мы поленились настраивать на нём HTTPS, нам нужно включить этот Registry в список доверенных.
"insecure-registries": [
  "192.168.2.25:5000"
]
Наконец, чтобы как-то пометить наши Докеры, чтобы потом правильно раскидать по ним сервисы, мы можем захотеть указать в конфиге метки. Хоть метки — это ключи и значения, в конфиге они задаются просто как строки.
"labels": [
  "backend=yes",
  "worker=yes"
]
После изменения конфига, очевидно, нужно перезагрузить демон. Например посредством service docker restart.
Теперь можно создавать Swarm. На той машинке, которая станет одним из менеджеров кластера, и с которой вы хотите начать это безобразие, нужно выполнить команду. Внимание, с появлением Swarm Mode у простой и понятной команды docker появилось очень даже много подкоманд. Сейчас будем знакомиться с docker swarm.
$ docker swarm init --advertise-addr 192.168.2.12
Swarm initialized: current node (2epmgc34aa0o7x6z6ho7v8u66) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-0pcqn90yv70657tpbepxaqjw3et3ihcalpbee8ndzifxg5zncn-5r3zjl1evea481i3mzetnwoff \
    192.168.2.12:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
Вам нужно указать адрес, по которому данный узел Swarm будет доступен для других узлов. Это довольно важно, если у вас есть несколько интерфейсов, например, для обмена трафиком внутри датацентра.
Узел, на котором мы запустили docker swarm init, становится (первым) менеджером будущего кластера. И нам дают некий токен, который нужен, чтобы подключить к этому кластеру другие узлы.
Команда — не идемпотентна. Попытка проинициализировать Swarm повторно приводит к ошибке.
$ docker swarm init --advertise-addr 192.168.2.12
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.
С одной стороны, понятно почему. С другой стороны, такая ошибка затрудняет автоматизацию. Можно задать ключ --force-new-cluster true, тогда каждый раз будет создаваться новый кластер, это тоже не то, что хотелось бы. А вот ключа игнорировать наличие кластера — нет.
Итак, у нас, оказывается, есть два типа узлов. Менеджер — с него можно управлять кластером. Менеджеров может быть несколько, они устраивают выборы, определяют кворум и всё такое. По умолчанию узел-менеджер также является и воркером, но можно создать и чистого менеджера. Воркер — запускает на себе контейнеры и всё такое. Чтобы присоединить к кластеру воркера или менеджера, используются разные токены.
Токен для присоединения воркера пишется в выхлопе docker swarm init. Это вариант для человеков. Для автоматизации вам нужно просто вывести токен. Это можно.
$ docker swarm join-token worker -q
SWMTKN-1-0pcqn90yv70657tpbepxaqjw3et3ihcalpbee8ndzifxg5zncn-5r3zjl1evea481i3mzetnwoff
Чтобы присоединить другой Docker Engine к этому Swarm, его нужно присоединить по токену. Нужно ещё указать адрес одного из менеджеров.
$ docker swarm join --token SWMTKN-1-... 192.168.2.12:2377
Эта команда снова не идемпонентна. Попытка присоединиться к кластеру, если узел уже в кластере, приводит к ошибке.
Узлы кластера общаются между собой по кучке портов. TCP порт 2377 — обращение к менеджерам. TCP и UDP порт 7946 — обмен метаданными между узлами. UDP порт 4789 — overlay сеть между узлами. А если мы собираемся использовать зашифрованный overlay, нужно разрешить ещё и протокол 50 (ESP). Но вроде как дополнительно шифровать overlay не очень нужно, ибо, если верить документации, весь трафик между узлами и так шифруется.
Чтобы разобраться с узлами кластера у нас добавилось семейство команд docker node. Например, можно узнать, сколько у нас узлов в кластере и каково их здоровье.
$ DOCKER_HOST=tcp://192.168.2.12:2375 docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
5qt2all7nm1sapczihel6crn7 *  docker2   Ready   Active        Leader
tbojo6bwq3p9rzxf1gzni1ig6    docker1   Ready   Active
Это команды, которые работают только в Swarm Mode, и работают только на узлах-менеджерах. Но мы не зря разрешали удалённый доступ к Docker Engine. Можно выполнить команды удалённо, указав адрес менеджера в переменной окружения DOCKER_HOST.
Swarm workers
Docker Swarm у нас вроде готов. Попробуем взять docker-compose.yml и развернуть его.
$ DOCKER_HOST=tcp://192.168.2.12:2375 docker-compose up

Compose does not use swarm mode to deploy services to multiple nodes in a swarm. All containers will be scheduled on the current node.

To deploy your application across the swarm, use `docker stack deploy`.
Что?
Ну да. Swarm Mode — это вам не старый добрый Swarm на контейнерах. Swarm Mode не совместим ни с чем, кроме себя самого. А Docker Compose теперь пригоден только для запуска кучки связанных контейнеров на одном хосте (локально).
Вместо docker run у нас теперь docker service create. Вместо кучи других команд, которые работают только на одном Докере, у нас теперь целая куча команд docker service, работающих в Swarm Mode.
Сервисы в Docker Swarm — это немножко покруче просто контейнеров, запущенных через docker run. Это, в общем-то, сервисы в понимании Docker Compose. Можно крутить их количество в кластере с помощью docker service scale. Можно смотреть, на каких узлах кластера сервис развёрнут: docker service ps. А самое клёвое, можно на лету менять настройки сервисов, например, проброшенные порты, с помощью docker service update.
Но мы же не хотим забрасывать наши любимые и тщательно выверенные docker-compose.yml? Не заменять же их шелловыми скриптами с docker service? Слава хипстерам, создатели Докера на нас не забили, и у нас есть docker stack.
Тут всё странно. Стек — это множество сервисов. docker stack deploy разворачивает это множество в Swarm согласно описанию в формате Distributed Application Bundles. А получить этот DAB файл (который на самом деле JSON) из docker-compose.yml можно с помощью docker-compose. Зачем ещё один формат?
Но docker stack deploy умеет работать и с docker-compose.yml.
$ DOCKER_HOST=tcp://192.168.2.12:2375 \
docker stack deploy --compose-file docker-compose.yml ${PROJECT}
Тут надо явно указать название стека, который вы разворачиваете. В Docker Compose это называлось проектом и по умолчанию бралось из имени текущего каталога.
Но и docker-compose.yml тут должен быть не простой. Он должен быть версии 3 или выше (сейчас крайняя версия 3.2).
Основное отличие версии 3 от старой доброй версии 2 — свойство deploy у каждого сервиса. docker-compose это свойство игнорирует, а docker stack deploy как раз использует.
В deploy как раз и указываются некоторые штуки, влияющие на то, как сервис воткнётся в Swarm. Можно ограничить узлы, на которых будет запущен сервис. Можно задать режим развёртывания.
version: '3'

services:

  nginx:
    image: nginx
    deploy:
      placement:
        constraints:
          - 'engine.labels.frontent == yes'
      mode: replicated      # maybe 'global' is better here
    ports:
      - '80:80'
    networks:
      - frontend

  worker:
    image: worker
    deploy:
      placement:
        constraints:
          - 'engine.labels.worker == yes'
      mode: replicated
    networks:
      - frontend

networks:
  frontend:
deploy.placement.constraint — это ограничения. engine.labels.* — это метки, что мы задали в daemon.json, т.е. метки Docker Engine. Есть ещё метки узлов. Они навешиваются через docker node update, который нужно выполнить, соответственно, на менеджере кластера. Опять неудобно для автоматизации, нужно ходить на менеджера, но знать имена всех остальных узлов. Метки узлов доступны для проверки как node.labels.*. Синтаксис ограничений, по сравнению со старым Swarm, изменился.
deploy.mode — это режим развёртывания. Пока их два. replicated — это по умолчанию. Сервисов будет запущено столько, сколько скажете сразу при создании, либо до скольки отмасштабируете через docker service scale. И размазаны их контейнеры будут по кластеру ровным слоем, учитывая ограничения, конечно. global — будет запущен ровно один (не более) сервис на каждом узле кластера, учитывая ограничения.
Сети, указанные в docker-compose.yml здесь по умолчанию будут overlay. Т.е. будут спокойно работать между узлами кластера.
Swarm services
С сетью всё вообще очень интересно. Давным-давно, как вы помните, нужно было проставлять linkи между контейнерами, и имя линка появлялось в /etc/hosts. Теперь внутри Docker Engine у нас есть DNS сервер. Linkи не нужны, достаточно, чтобы сервисы были в одной сети. А одна сеть на docker-compose.yml и так формируется по умолчанию. Имена сервисов резолвятся в IP адреса в этих внутренних сетях. А если сервисов отмасштабировано несколько экземпляров, то одно доменное имя будет резолвится на несколько IP адресов. Обычный round robin DNS. И всё это справедливо для нынешнего Docker Compose.
В Swarm Mode пошли чуть дальше. Теперь каждому сервису выдаётся один виртуальный IP (VIP). Доменное имя сервиса резолвится в этот один единственный VIP. А трафик на этот VIP уже магическим образом распределяется между актуальными контейнерами (уже со своими IP). Понятно, зачем это надо. В зависимости от нагрузки на узлы кластера, может потребоваться более тонкая балансировка, чем простой round robin. Но при желании можно вернуться на старое поведение, без VIP, а с множеством IP адресов на одно доменное имя сервиса.
Swarm Service VIP
Серьёзные изменения произошли в публикации портов. Синтаксис остался прежним. А вот смысл теперь совсем другой.
Раньше, когда мы публиковали порт, это означало, что Docker Engine на том хосте, где запущен контейнер с данным сервисом, начинал слушать указанный порт и пробрасывать соединения в контейнер. Но мы не можем точно контролировать, на каком узле будет запущен сервис. В результате нам либо нужно запускать сервисов заведомо столько, сколько узлов в кластере, убеждаться, что они запустились действительно на всех узлах, и использовать внешний балансер. Ну, по сути, делать то, что теперь нам гарантирует deploy.mode = global. Либо наоборот, внедрять балансер, например, HAProxy, в виде сервиса, прибивать его, с помощью ограничений, к определённому узлу, весь внешний трафик направлять на этот узел, а балансировать уже внутри Докера.
Теперь этих мучений во многих случаях можно избежать. Потому что в Swarm Mode есть Ingress Routing Mesh. Если вы публикуете порт в каком-то сервисе, этот порт начинает слушаться на всех узлах кластера. И принимать внешний трафик можно на любом узле кластера. Даже том, где контейнер с данным сервисом в данный момент не запущен. Swarm магическим образом всё это дело смаршрутизирует и сбалансирует до нужного контейнера. Круто? Круто.
Но есть нюансы. Так как эти меши, это, по сути, трансляция адресов, ваше приложение в контейнере видит не реальный IP адрес клиента, а некий адрес во внутренней сети Докера.
Если вам нужно доставить IP адрес клиента до вашего сервиса, у вас есть два пути. Либо мы снова добавляем внешний балансер, и пусть он каким-то образом внедряет IP адрес клиента. В случае HTTP это может быть дополнительный заголовок X-Forwarded-For. Для других протоколов можно использовать HAProxy Proxy protocol. Либо можно отключить этот меш для данного сервиса, и использовать обычный проброс портов, работающий только на одном хосте. Это должно хорошо работать для global сервисов.
Чтобы можно было указать режим публикации портов, синтаксис всё-таки изменился. Можно воспользоваться возможностью обновлять сервисы.
$ docker service update ${PROJECT}_nginx --publish-add mode=host,target=80,published=80
А можно сразу прописать новый хитрый синтаксис в docker-compose.yml.
ports:
  - target: 80
    published: 80
    protocol: tcp
    mode: host
Последний вариант требует версии compose файла аж 3.2 и будет работать только в только-только релизящемся Docker Engine 17.4.0.
Mesh with external balancer
Ну что ж. Docker Swarm Mode — состоявшийся факт. Оно не совместимо с предыдущим Swarm, который, тем не менее, продолжает поддерживаться.
Создание Swarm кластера существенно упростилось. Хотя не очень удачные неидемпотентные команды и необходимость копирования токенов, не сильно помогают в автоматизации развёртывания.
Появилось оооочень много новых команд docker. Старые команды тоже остались, но они не будут работать со Swarm Mode.
Docker Compose сократили до локального инструмента. Вместо него предлагают использовать новые команды docker, в частности docker stack deploy. Однако, весьма полная совместимость на уровне compose файлов осталась.
Mesh роутинг и VIP здорово упрощают развёртывание и разработку в типовых случаях (http микросервисы?). В сложных случаях без явного балансировщика всё равно не обойтись, либо внешнего, либо внутри Swarm.

2017-04-09

О CodeFest 2017

Снова случился CodeFest. И я снова собирался на него не поехать. Настолько, что собрался даже на DUMP в Ёбург. Там совершенно убойная программа на секции Science вырисовывается. Точнее, собрался на оба: и CodeFest, и DUMP. Даже попытался пробиться в докладчики DUMPа, чтобы немного сэкономить. На CodeFest-то бесполезно пытаться докладчиком, ведь я не работаю в яндексах, мейлру, двагисах да бадах. Но в результате я побывал на CodeFest, и на DUMP уже никаких сил нет. Сильно много общения, видимо.
CodeFest logo
В этот раз удалось погулять по Новосибирску. По центру. Было мокро, холодно, грязно и ветренно. Как обычно в это время в Сибири. Делать в центре Новосибирска совершенно нечего. Разве что сфотографироваться на фоне задницы памятника Ленину. Гулять надо по Академгородку. Ну и летом надо обязательно сгонять в новосибирский зоопарк. К стыду своему я там ни разу не был. На карте он выглядит чудовищно громадным.
Посмотрели «Призрака в доспехах» в IMAX. Фильм годен, даже для любителей оригинального аниме. Хотя, конечно, получилась типичная голливудская поделка. IMAX и 3D — к чёрту.
GiS city
Конференцию открывал Marko Berković аж из самого GitHub. Говорил про корпоративную культуру. Как они в ГитХабе отказываются от почты в пользу репозиториев и чатиков. Как они админят всё через чатики с Hubot.
Конечно, принимать совместные решения, редактируя документ в репозитории, и принимая правки через пулреквесты, — замечательно. Это действительно эффективнее, чем почтовая переписка. Но вот что мне в пулреквестах никогда не нравилось. Изменения кода и прочих текстов — под управлением системы контроля версий, в данном случае Git. А пулреквесты, а также связанные с ними обсуждения, — где-то совершенно сбоку, в какой-то БД какой-то проприетарной системы под названием GitHub. Причём, как показал опыл GitLab, удалить эту БД может любой достаточно опытный админ. Я всё жду, когда пулреквесты станут частью самого репозитория, тоже под контролем версий. Кто запилит нужный экстеншен к Гиту или Меркуриалу?
Ну вот, хотел посмотреть, какие доклады я отметил звёздочками. А официальное приложение CodeFest начало падать даже у меня, на Android 6.0.1. Так что, господа организаторы, дело не в том, под какие версии Андроида делали приложение, а в кривых ручках компании Allmax, чей логотип гордо красуется при запуске приложения. А может, во всём виноват React Native.
xSQL
Костя Осипов заменил Дорофеева, собрав полный зал, ещё и с хвостами в коридоре. Я не влез, но полностью доклад поддерживаю. Все эти SQL, NoSQL, NewSQL — это всё об одном и том же, только с разных сторон. А самого Костю мы поймали на афтепати. Разговор был про жизнь, бизнес и всё такое... Но что было на афтепати, останется на афтепати.
Кстати, похоже, это первый тренд конференции: NoSQL и SQL, радость в слиянии. Но я его как-то не заметил, ибо для меня это года два как совершенно ясная тема.
Макса Дорофеева не было. По официальной версии дырка в его расписании для CodeFest не совпала с самим CodeFest в этом году. Зато был конкурс мемасиков, и даже мой хороший знакомый выиграл книжку с автографом. Зато все докладчики, как могли, пытались заменить Дорофеева. Костя Осипов собрал полный зал, Павел Мочалкин рассуждал об устройстве человеческого мозга, Никита Прокопов рисовал слайды «вручную», Фёдор Овчинников сказал слово «жопа» со сцены, впрочем, лишь один раз.
человек-снежинка
Виктор Грищенко очень мило рассказал, как добавив чуток схемы, точнее формального описания грамматики, и таким образом сведя JSON к регулярной грамматике, можно заметно ускорить и стабилизировать парсинг этого самого JSON, в частности в JavaScript, фактически заменив стандартный парсер регулярным выражением. Немного увлекательного матана с неоднозначными практическими выводами.
На этом докладе я понял, что обладаю талантом задавать вопросы из очень неприятной для докладчика категории. Есть такие вопросы, которые очень тщательно обходятся в самом докладе, потому что обнажают недостатки предлагаемого решения и даже могут поставить под сомнение ценность доклада в целом. Хорошие докладчики заранее знают об этих недостатках и готовят ответы на подобные вопросы. Вот я на такие заготовленные ответы и нарывался :)
Второй тренд конференции: протоколы взаимодействия, схема/типизированность или её отсутствие, попытки заменить HTTP и JSON.
Вот и Игорь Кашкута рассказал, как они используют Protobuf поверх голого TCP или WebSocket для общения мобилок с сервером. Казалось бы, Protobuf — это такая жутко строго типизированная штука, которая создаёт код для клиента и сервера, и эти клиенты и сервера должны быть одинаковых версий. Это не SOLIDно. Но ребята из Badoo воспользовались тем, что любое поле структуры, объявленное в Protobuf, вполне себе может быть необязательным. И у них сложился вполне расширяемый и гибкий протокол, работающий через строго типизированный Protobuf.
Пионер
Третий тренд конференции: машинное обучение. Да, оно везде. Уже есть, а будет ещё больше. И пусть мой коллега утверждает, что на Курсере задачки посложнее того машинного обучения, про внедрение которого рассказывали на CodeFest. Это всё дело наживное. Да и не крутостью алгоритмов меряется достигаемый результат.
Иван Бондаренко как раз рассказал, как они обучали машинки распознаванию адресов-телефонов на сайтах компаний. Это у них такая хитрая штука в 2ГИС, чтобы оперативно выяснять, что эти самые адреса-телефоны внезапно изменились. А ещё получившийся ПиоNER успешно потрошит описания лекарств или номенклатуру автомобильных шин.
RTB
Четвёртый тренд конференции: RTB. Ну может, и не тренд, кажется, про RTB больше говорили в прошлом году. А в этом году про RTB, кажется, был вообще только один доклад. Но, имхо, это был настолько замечательный доклад, что вполне достоин обозначить тренд.
Максим Пугачев рассказал про SaaS платформу для RTB от IPONWEB. Первая половина доклада была посвящена обзору RTB как явлению рынка, и какие технические вызовы оно бросает. Это был самое краткое, но объемлющее определение RTB, что я слышал. Браво!
Отрадно слышать, что такая технически сложная штука как RTB, ушла таки в облака. И многие, кто хочет свой RTB, перестали ваять его на коленке. Впрочем, знающие люди говорят, что IPONWEB всё же дорого, и свои костылики обходятся дешевле, если, конечно, знать, где их взять.
Оперный театр
Павел Мочалкин обычно закрывает конференцию. Охлаждает энтузиазм, чтобы все технари не бросились внедрять услышанное с понедельника. В этот раз он решил всех опохмелить утром второго дня. Ну и заодно утешить, что обученные машинки не отберут у нас работу. Потому что мы все такие творческие и уникальные.
Тут обозначился ещё один тренд конференции. Правда, я прошёл почти мимо него. Но между докладами и после весьма много обсуждали. Бирюзовые организации. Где сотрудники обладают достаточной свободой и правом действовать самостоятельно ради общего блага (организации). Самоорганизуются.
Протокол
Никита Прокопов удивил. Снова поднял тему протоколов. Но рассказал вовсе не о том, что было заявлено в описании доклада. По крайней мере я главным увидел другое. Он поднял проблему синхронизации. Данных. Между множеством клиентов и сервером. В исторической и технической перспективе. Про то, как от синхронизации состояния перешли к пересылке событий и их упорядочиванию. Но и это не даёт окончательного решения.
Вообще большинство докладов строилось по одной схеме. Вот тема. Для тех, кто не в теме, вот вам введение для чайников разной степени кипячения, на полдоклада. А вот вам наш крутой продукт, который посвящён этой теме. Либо какое-то частное решение. Либо просто похвастаться хотим. Покупайте наших слонов.
А вот у Никиты получилось хорошо. По-инженерному. Проблему поднял. Всех заставил задуматься. А решения не предложил. Потому что его нет :)
Снова про xSQL. Дмитрий Долгов рассказал про jsonb в PostgreSQL. Удивительно, но в конце прошлого года у нас появился стандарт на JSON в SQL. Продвигаемый, вроде, Oracle. Какой-то странно закрытый. Который скоро будет поддерживать PostgreSQL.
А потом были бенчмарки JSONов в Постгресе, Монге и Мускуле. Постгрес, конечно же, победил. Хотя для тестирования наконец-то взяли не ноутбук Олега Бартунова, а серваки в Амазоне.
Тестировали с YCSB, блин. Ну почему я до сих пор не родил ничего получше, чем этот синхронный ява-монстр, неприспособленный для создания нормальной распределённой нагрузки?
Упоминали контейнеры, Docker, Kubernetes. В той же Lamoda. Пусть это будет ещё одним трендом конференции. Хотя я этого наелся по работе столько, что уже не воспринимаю как откровение. Похоже, это просто стало стандартом. Используем докеры там, где можем.
Resource management
Послушал единственный менеджерский доклад. Дмитрий Плетнев подробнейше рассказал про ресурсный менеджмент. У аутсорсных компаний есть ресурсы. Человеки. Которые что-то умеют и чего-то хотят. И заказчикам нужны эти ресурсы, их часы. Надо свести одних с другими, чтобы никто не страдал. Нужно, чтобы сотрудники были заняты, но не перетруждались, и не простаивали. Нужно, чтобы сотрудними делали то, что они умеют хорошо, но и чтобы развивались.
Очень много чего нужно. И следить за продажами, чтобы тормозить их, когда нет свободных ресурсов. И знать и отслеживать, кто где находится, чем занимается, чего хочет, чем недоволен, куда хочет двигаться.
У них всё получилось. Причём не взрывом мозга у одного директора по ресурсам. Удалось распределить обязанности между менеджерами по ресурсам, на каждом пуле ресурсов. И это в компании из 70 человек. А я вот хорошо знаю человека, который несколько лет в одиночку всё это разруливал в компании, где работает более 200 человек.
Дмитрий задал залу вопрос: Кто проводит у себя стажировки? Потому что стажёры — это очень лояльный и дешёвый ресурс. Из порядка 600 человек в зале руку подняли лишь человек пять. Что? Я думал что в ИТ только те, кому совсем-совсем не надо расти, не проводят стажировок. По крайней мере в Омске все, кому нужны люди, это делают. Оказалось, что Омск — не показатель.
HoloLens
Снова немного машобуча, а на самом деле даже матана, было в докладе Дениса Баталова. В роли слонов выступали некоторые продукты Амазона, включая Amazon Kinesis. Теперь их SQL по потокам данных умеет искать аномалии с помощью леса случайных разбивок.
Andrea Di Persio рассказывал про Go и микросервисы, сдабривая это всё терминами вроде Domain Driven Design. То ли потому, что было на английском, то ли одно из двух, но мне было скучно. Как-то ни про Go не рассказал, ни про микросервисы. Оставлю лишь одну ссылочку для дальнейшего изучения: Prometheus.
Сергей Звягин здорово рассказал про виртуальную, дополненную и другие виды реальности. И продемонстрировал HoloLens. Это такой Project Tango на голове, или сильно прокачанные Google Glass.
Запомните эти жесты. Надо смотреть мимо собеседника, куда-то вдаль. Потом сложить пальцы в щёпоть на вытянутой руке, и резко разжать эту щёпоть (сказав «вах»). Этим вы вызовете меню HoloLens. Потом нужно слегда поводить головой, прицеливаясь в нужный пункт меню. И, наконец, протянуть руку с воображаемой мышкой, и дважды кликнуть этой мышкой. Так вы выберете нужный пункт меню. Со стороны это выглядит неимоверно смешно, даже если у вас на голове хайтековая штуковина за несколько тысяч долларов. Повеселите друзей.
Почему-то на вопрос, что они собираются с такими штуковинами делать, эти ребята-энтузиасты отвечают, что просто следят за тенденциями, но ничего конкретного не пишут. Похоже, рано ещё программистам сюда соваться. Надо контент делать. То же 360° видео. А потом уже захочется сей контент сделать более интерактивным.
Guesture
На закуску участникам подарили Фёдора Овчинникова. Айтишники его плохо знают. Потому что он — создатель сети пиццерий «Додо Пицца». Фёдор — за максимальную открытость. Он кидал клич за идеями по «рефакторингу» их информационной системы. Он, не стесняясь, сообщал о потере 8 миллионов рублей (при обороте за 2016 год более 2.5 миллиардов), просил помощи. И помощь пришла. Любопытно, что Фёдор сказал, что причиной ошибки, и основой решения было одно и тоже. Доверие сотрудникам. Да, та самая бирюзовая организация. Сами напортачили, сами и исправили. Никого не наказали.
Кстати, про информационную систему. Я много раз слышал, что «Додо Пицца» — компания-киборг. Что все процессы внутри компании проходят через информационную систему. Что сама суть компании — эта система, а вовсе не изготовление пиццы. Только сейчас, из первых уст, до меня дошёл смысл этого. «Додо Пицца» — это не компания, которая автоматизировала всё, вплоть до рецептуры теста. «Додо Пицца» — это компания, которая была создана вокруг информационной системы, которая развивалась и росла вместе с компанией. Информационная система не была внедрена в бизнес. Это бизнес вырос на скелете информационной системы. Мне кажется, ещё никто так не делал раньше. Тем интересней следить за экспериментом.
Додо Пицца
В этом году на конференции было как-то мало знакомых лиц. Из Омской компании, где более 200 человек, приехал только один. Где вся эта туча омичей, что значились зарегистрировавшимися? Кто все эти люди вокруг? Поколение сменилось? Хорошо это или плохо?
Резюмирую.
  • SQL никуда не денется, в разных вариантах будет проникать в NoSQL и NewSQL.
  • Протоколы и взаимодействие всё ещё интересны, HTML и JSON хочется чем-то заменить.
  • Машинное обучение уже здесь и сейчас, возможны наколенные применения даже на малых задачах и данных.
  • RTB уже рутина.
  • Микросервисы уже рутина.
  • Контейнеры и Docker уже рутина, но можно поспорить, каким инструментом с ними удобнее обращаться.
  • Виртуальные и прочие реальности всё ещё слишком нишевы, чтобы для них требовалось массово писать софт.
  • Организации мечтают стать бирюзовыми, чтобы сотрудники сами всё делали.

2017-03-18

О кубиках

Лего бывает разное. Дупло (Duplo, полагаю, с ударением на первый слог) — для самых маленьких. Просто Лего, в виде кучи самых разнообразных серий и наборов, — для детей всех прочих возрастов. Lego Technic — для взрослых.
We love Lego
Год назад мне подарили здоровенный 42025 Cargo Plane. Боже, как быстро они обновляют ассортимент. Самолёт уже не продаётся. Кажется я понимаю маньяков, которые коллекционируют Лего. Ведь большинство моделей продаются лишь год-два. А потом становятся археологией и мечтой коллекционеров.
Собирать самолёт было интересно. Кажется, я потратил почти две недели, вечерами. Офигенская маленькая пластмассовая раздаточная коробка с почти настоящими муфтами включения. Куча рычажков, которые что-то включают, выключают, двигают.
Но играбельность у самолёта оказалась почти никакой. Большой. Тяжёлый. Несколько хрупкий. Непонятно, за что его держать, чтобы палец не попал в шестерёнку, и чтобы что-нибудь не отломить. Нельзя просто так включить пропеллеры и "полетать" по квартире. Игрушкоподъемность и кукловместимость тоже никакие.
Cargo Plane
Вспоминая детство и металлический конструктор (который иностранцы называют Meccano), пытался сделать машинку с поворотными колёсами. Не слишком успешно.
В железном конструкторе всё просто. Вот планка с дырками. Вот болт и гайка. Вот уголок, если надо соединить планки под углом. Для длинных жёстких конструкций есть длинные металлические профили.
Всё более-менее просто в корейском My Robot Time. Там пластмассовые детали без проблем соединяются под прямым углом. Наоборот, чтобы соединить встык или внахлёст, нужны дополнительные детальки.
А вот в Lego Technic всё очень сложно. Это не обычные кирпичики Lego, которые просто ставишь один на другой. Это тоже планки с дырками. И пластмассовые шканты для их скрепления (порылся в Википедии в поисках правильного термина). Только вот пластмассовые планки не условно нулевой толщины, как в металлических конструкторах, а вполне себе единичной толщины, квадратные в поперечном сечении.
Возникает страшная проблема при переходе с одного направления дырок в планках (и направления протяжённости самих планок) на другое. Простых уголков почти нет. Нужны специальные детальки. Которые тоже занимают несколько единиц объема. И соединяемые детали получаются смещены на единицу. Это вам не торжество кубизма, как в Minecraft или классическом Lego.
Моё пространственное воображение пока только-только подстраивается под это безобразие. Удивительно, но помогают последние страницы инструкций, где нарисованы все детали, имеющиеся в наличии, и их количество.
Steering machine
А вот сейчас подарили SBrick. Эти ребята собрали деньги на Kickstarter в 2014. Успешно сделали и продают вот что...
У Lego есть моторчики, есть лампочки, есть инфракрасный приёмник и пультик дистанционного управления. Правда, в настоящее время с пультиком продаётся только странный гусеничный вездеход. Вся эта электрика подаётся под маркой Power Functions.
Но штатные способы управления этой электрикой: механические выключатели, полтора странных инфракрасных пультика, одна модель (но в двух не очень совместимых версиях) инфракрасного приёмника. Пультик и приёмник имеют только два канала управления. И те дискретные. Для машинки, чтобы рулить влево-вправо, да ехать вперёд-назад, может, и достаточно. Но такую машинку, с пультиком, нужно ещё найти и купить.
А вот SBrick — это четыре канала пропорционального управления. Через Bluetooth. С телефона или планшета, через соответствующее приложение. Можно рулить сразу несколькими SBrick. Рай для любителей электрификации Lego моделек.
SBrick and motors
А в конце 2016 те же ребята завершили ещё одну кампанию на Kickstarter. Теперь для SBrick Plus. Его уже можно купить. Он совместим не только с Power Functions, но и с образовательным набором от Lego под названием WeDo. А вскоре, видимо, через переходник, обещают поддержку и Mindstorms.
И SBrick, и SBrick Plus можно программировать как минимум через Scratch. Впрочем, я пока до этого не дошёл.
Имея два моторчика и один SBrick, я пока что взялся за, как внезапно оказалось, мечту всего детства — радиоуправляемую машинку. С одной стороны у нас есть SBrick и его четыре порта управления (задействуем только два). С другой стороны — приложение под Android и то, что называется Profile Designer.
Legokhod
Ребята развернули целую инфраструктуру. Чтобы управлять моделькой, в приложении нужно загрузить профиль. Либо один из нескольких доступных публично. Либо нарисовать свой профиль, в онлайн редакторе, привязанный к вашему аккаунту SBrick. К этому же аккаунту привязаны и ваши модельки.
Профиль — это картинка, которая будет видна на экране телефона/планшета при управлении моделькой. Рычажки, переключатели, фоновая картинка. Когда профиль подключается к модели, каждые рычажки и переключатели привязываются к каналам SBrick. Есть рычажки с одной осью. Есть джойстики с двумя осями. Есть кнопки вкл/выкл. Есть кнопки, которые при одном нажатии передают какую-то последовательность вкл/выкл. Можно задействовать акселерометры телефона, и, например, рулить моделькой, наклоняя телефон, как в автогонках. Можно применять к положению рычажков какие-то простейшие преобразования. Но нельзя смешивать несколько каналов, для этого придётся попрограммировать. В общем, получается уникальная панель управления для вашей уникальной модели.
Profile example
Имеем вполне успешный кикстартовый проект, который может хорошо порадовать родителей и детей, владельцев Lego Technic моделей.
У меня пока что получился лишь уродливый и неуклюжий легоход. Буду улучшать :)

2017-03-05

О ClickHouse

А у нас на продакшине ClickHouse. А у вас? Вот так вот получилось. Ну и продолжается, само собой.
ClickHouse logo
Задача была такая. Есть юзеры. Они генерируют события. Не буду распространяться, какие именно события, дабы не нарушать NDA. По каждому событию сейчас у нас получается семнадцать параметров. Причём два из них — массивы строковых значений. Событий генерируется довольно много: от половины до даже пяти миллионов за сутки. В пиковые моменты случается до ста пятидесяти событий в секунду. И эти числа ещё собираются расти по мере появления новых пользователей.
По этим событиям нужно строить, казалось бы, обычные аналитические штуки. Самые популярные (часто встречающиеся) значения параметров (в том числе из массивов) за последние двадцать четыре часа, семь и тридцать дней. Графики изменения параметров за те же периоды. Просто счётчики событий. Всё это, выбранное и сгруппированное по значениям других параметров. Что-то порядка десятка подобных отчётиков, чтобы ещё обновлялись по возможности в реальном времени. И безо всякой гарантии, что новые отчёты внезапно не понадобятся.
Но кроме этого, все эти события, все семнадцать параметров, надо изображать как они есть, в виде журнала, и выгружать в CSV. Заостряю на этом внимание, потому что это требование несколько противоречит предыдущим. Как оказалось, одно дело — это составлять агрегированные метрики по исходным данным на лету. А совсем другое дело — хранить исходные логи, как они есть.
Подход номер раз. Я сказал: это ж timeseries данные. Давайте возьмём InfluxDB и будем складывать всё туда. Тем более, что Инфлюксина вроде стала более-менее стабильной (только только вышла версия 1.0), и обросла солидной инфраструктурой.
Впрочем, инфраструктурой мы так и не воспользовались. А в InfluxDB совершенно разочаровались. Да, она может хранить всё, что нам нужно. Да, она может делать нужные нам запросы. Теоретически. Но на практике не всё так просто.
InfluxDB не умеет делать достаточно сложные аналитические запросы. Напрямую, одним запросом. Нужно собирать промежуточные агрегации и строить результат в несколько шагов. Либо во временных таблицах, что медленно и требует памяти и диска. Либо формировать агрегации на лету с помощью так называемых Continuous Queries, или даже с помощью сложного и мощного Kapacitor, что требует очень хорошего знания самой InfluxDB и её языка запросов, хорошего понимания того, что нужно получить и посредством каких преобразований, ну и снова требует памяти, диска и CPU. Может, был бы у нас аналитик данных, он бы побаловался. Но мы, простые смертные программисты, задачу многошагового агрегирования не осилили.
А ещё оказалось, что InfluxDB имеет своё понимание временных промежутков, от которого никуда не деться. За текущие и прошлые сутки, недели, месяцы проагрегировать — пожалуйста. За последние (от текущего момента, а не от полуночи) двадцать четыре часа — никак.
InfluxDB не даёт никакой гарантии касательно того, когда только что вставленные данные появятся в результатах запросов. Eventual consistency во всей красе. Жутко неприятно только, что даже безо всякой нагрузки этот интервал может составлять минуты.
Ну а добила нас InfluxDB непомерными и непредсказуемыми требованиями к оперативной памяти. Даже на небольших данных, в самом начале проекта, сложные аналитические запросы приводили к выеданию гигабайт ОЗУ. Выглядело это так. Всё работет, данные вставляются. Открываем страничку, где рисуются наши отчёты. И парочка самых тяжёлых отчётов зависает на минуты, а потом валится с ошибкой. InfluxDB в этот момент съедает всю ОЗУ, потом весь свап, и потом запрос таки падает от нехватки памяти. Спасибо, что вся Инфлюксина не падает, а только поток тяжёлого запроса. Мы подумали, что платить за сервера с тридцатью двумя или шестидесятью четырьмя гигабайтами ОЗУ для обработки жалкого миллиона (на тот момент) записей — это слишком жирно. Тем более, что непонятно, хватит ли и этих гигабайт.
InfluxDB logo
Подход номер два. PostgreSQL. Попробовали, проверили, потестировали. Оказалось, что старый добрый реляционный SQL прекрасно умеет все те аналитические запросы, которые оказались не по зубам InfluxDB. Прямо поверх исходной таблицы с журналом событий. SUM, GROUP BY, ORDER BY, LIMIT. Джойны таблицы с самой собой, точнее по массивам внутри данных. Чтобы не париться со схемой, юзали jsonb. Индексы правильные построили. Всё прекрасно работало и память не жрало. Postgres — прекрасен.
Так всё жило несколько месяцев. А пользователей становилось всё больше. И запросы стали выполняться всё медленнее. Уже минутами. Окончательный ахтунг случился, когда запросы стали падать из-за нехватки места на диске. Оказывается, Postgres создаёт временные файлы, что вполне ожидаемо. Но что эти файлы вполне могут быть размером больше десяти гигабайт — внезапно.
На популярных VDS дисковое пространство денег стоит. А сто сорок миллионов записей стали занимать сто двадцать гигабайт диска. Многовато. Как освободить место? Ну давайте сдампим старые данные в файлики, и удалим их из Postgres. Ок. Как положено, сделаем vacuum analyze. Ах, обычный вакум не возвращает дисковое пространство операционной системе, он просто помечает место в уже используемых страницах как свободное. Вставлять ещё без дополнительного пожирания диска можно, но места не добавилось. Может, надо сделать vacuum full? Чорт, ему нужно свободное место, чтобы полностью перекопировать таблицу. А места уже нет. Пат.
Почему запросы стали медленными? Потому что транзации. По идее, даже банальному count(*) должно быть достаточно лишь индекса, чтобы дать ответ. Но у нас ещё и интенсивная вставка происходит. А значит, помимо индекса, надо сверяться с visibility map для каждой строки, чтобы убедиться, что данная строка действительно видна в данной транзации. Получается слишком много IO для выполнения запроса. Получается слишком медленно.
PostgreSQL logo
Проблемы с Postgres, спасибо ему, начались не внезапно. Мы начали смотреть по сторонам, чтобы понять, что делать. Думали о Cassandra. И тут случился ClickHouse.
У ClickHouse замечательная документация. Там всё написано, как начать и продолжить.
Идея такая, в каком-то смысле противоположная текущему подходу к timeseries, что применяется в том же InfluxDB. Во-первых, мы не знаем, какого рода отчёты нам понадобятся. Во-вторых, если отчётов потребуется слишком много, придётся хранить много агрегированных данных, которые многократно дублируют исходные данные. Их объём может быть значительным. Так почему бы не вспомнить тот же SQL и старый добрый OLAP? Пусть у нас будут только исходные данные, но в виде, удобном для разных хитрых манипуляций. А если мы сможем хранить данные достаточно компактно, чтобы уменьшить IO, то сложные запросы ещё и выполняться будут достаточно быстро.
Вот такой он, ClickHouse. Это колоночная база данных. Колонки данных физически хранятся в разных файлах. Для аналитики это важно, так как в одном запросе, как правило, используются далеко не все колонки. Вот которые не используются, можно не читать. К тому же колонку можно очень хорошо сжать, если там будут повторяющиеся значения. И Кликхауз жмёт.
В самом популярном движке таблиц Кликхауса MergeTree данные упорядочены по дате и по первичному ключу. И объединяются в кусочки по ключу. Каждый кусочек представлен на диске каталогом, в котором лежат файлики. По паре файликов (данные и индекс?) на каждую колонку. Когда мы вставляем, создаются новые кусочки. А потом они в фоне объединяются.
Эти файлы на диске очень не похожи на то, что обычно бывает в базах данных. Какой-то блокнот с зипом. Надеюсь, эта конструкция неприхотлива к потере отдельных файлов.
Сжимает Кликхаус потрясающе. То, что в Постгресе занимало сто двадцать гигабайт, в виде загзипованного csv дампа занимало пять гигабайт, будучи вставлено в Кликхаус, заняло два(!) гигабайта на диске.
Зато нет транзакций, невозможны апдейты, а удалять данные можно только партициями, т.е. кусками за целый месяц. Ну и фиг со всем этим, не для того держим.
Запросы, которые в Постгресе затягивались на минуты, в Кликхаусе выполняются за пару секунд. На том же объеме данных. Он ещё гордо показывает какое чудовищное количество гигабайт он читает при выполнении запроса. Эквивалетный объем несжатых данных, конечно. Маркетинг.
Да, на простеньких запросах Постгрес быстрее, он может ответить за миллисекунды. Кликхаус редко отвечает быстрее сотен миллисекунд.
Есть репликация. Для этого нужен ZooKeeper. Раз уж разводим зоопарк разных БД, нужно за ним присматривать. Для репликации имеется отдельное семейство движков БД, например, ReplicatedMergeTree. Получается, что реплицируются отдельные таблицы. Движок через ZooKeeper координирует куски данных, имеющиеся в каждой реплике. И реплики обмениваются пропущенными кусками. Соответственно, получается мультимастер. Писать можно в любую реплику, данные появятся в обеих. Получается, что тут на репликах и запись масштабируется.
Есть шардинг. Тут тоже нужен ZooKeeper. Для шардинга есть отдельный движок таблиц под названием Distributed. Он сам не хранит данные, а берёт их из других таблиц-шардов, доступ к которым осуществляется через этот Distributed. Это могут быть любые таблицы, в том числе и реплицируемые. С ключом шардинга ClickHouse обращается весьма вольно. Для чтения он вообще не нужен, потому что запрос всегда будет направлен во все шарды, а финальный результат будет собран на хосте, где живёт Distributed таблица. Для записи ключ нужен только чтобы обеспечить равномерное распределение данных между шардами. Или не обязательно равномерное, можно указать разные веса разным шардам. А можно вообще шардить вручную, напрямую записывая в скрытые под Distributed таблицы. Кликхаус всё стерпит.
Мы до репликации и шардинга ещё не дошли, то технически к этому готовы. Хотя, такими темпами до ста двадцати гигабайт данных в Кликхаусе ещё не скоро доберёмся.
Подключаться к Кликхаусу можно как минимум двумя способами. Родной консольный клиент clickhouse-client использует некий бинарный протокол. Зато, похоже, только он умеет показывать симпатичный прогресс бар во время выполнения запроса. Кстати, этот консольный клиент — самая весёлая консоль, что я видел, порадует вас смайликами. Ещё есть HTTP протокол, можно просто курлить. И есть JDBC драйвер, который, впрочем, почему-то ходит на сервер тоже через HTTP.
ClickHouse client
Случилось с ClickHouse и штуки три (пока три) неприятных момента. Документацию читать внимательно надо.
Во-первых, у JDBC драйвера стоит очень маленький таймаут на подключение к серверу. Пятьдесят миллисекунд. Этого, конечно, хватит для локальной сети или даже одного датацентра. Но подключиться к серверу на другом берегу Атлантического океана уже не получится. Лечится указанием property при создании DataSource или Connection по имени connection_timeout с более вменяемым значением.
Во-вторых, Кликхаус очень не любит одиночные инсерты. Ему подавай батчи. Официальная документация даже говорит, что одиночные инсерты, пожалуйста, не чаще одного в секунду на одном сервере. Оно понятно, почему. MergeTree нужны достаточно большие куски, чтобы заняться их слиянием. Постоянно сливать отдельные строки выходит слишком дорого. Мы сдуру попробовали вставлять наши события по одному. Кликхаус нещадно насиловал диск на запись, и упорно задерживал инсерты. Т.е. в логе писал соответствующее сообщение. И вставляемые данные появлялись в запросах с дикой задержкой аж в часы. В отличие от InfluxDB эта задержка прямо коррелирует с нагрузкой на сервер.
Пришлось расчехлить знание многопоточного программирования на Java, и собирать батчи на нашем клиенте. Асинхронно, по тысяче строк за раз, но не реже раза в тридцать секунд, и не забыть записать всё накопленное при шатдауне системы. Почему эта буферизация не сделана в JDBC драйвере? Или даже на сервере? Та же Кассандра спокойно пишет данные в журнал, и лишь потом с ними разбирается. Похоже, надо смириться с тем, что при вставке в Кликхаус данные могут быть потеряны. Ну а пока с батчами всё ок. Один сервер держит нашу нагрузку.
В-третьих, Кликхаус не очень приятно работает с датами. Слишком легко облажаться. Дату-время он хранит в виде юниксового таймстампа. Прощай миллисекунды. Здравствуй, проблема 2038 года. Но также ClickHouse готов принимать в качестве даты и времени строки в формате ISO 8601. При этом никаких таймзон и даже смещений от UTC не предусмотрено. А для конвертации он использует в лучшем случае таймзону, прописанную в конфиге, а в худшем случае, системную таймзону, подхваченную при старте сервера.
В консольке Кликхауса это не так страшно, ибо Кликхаус очень строго относится к типам данных (даже строже, чем Постгрес), поэтому, чтобы использовать строку как дату, нужно явно использовать функции toDate() и toDateTime() (и даже Date автоматически из DateTime не выводится). И вроде как консолька по умолчанию использует таймзону сервера. А вот JDBC драйвер спокойно съедает String там, где ожидается DateTime. И поди пойми, в какой момент происходит конвертация, и в каком часовом поясе, клиента или сервера. Надо переходить на long.
При переходе из Postgres пришлось распотрошить jsonb в отдельные колонки. Но у нас же колоночная база. Если потом понадобится добавить ещё одну колонку, это же будет очень дёшево, правда?

2017-02-19

Об UI

Забыли про механические компьютеры. Они могли только числа молотить. Поэтому весь интерфейс у них — это ввести числа колёсиками, да покрутить ручку, чтобы результат получить. Весьма неплохой интерфейс для этой задачи, кстати.
Арифмометр
У первых электрических (но ещё не электронных) компьютеров интерфейс был прост. Надо было нужные кабели повтыкать в нужные дырочки. Ну примерно как на первых телефонных станциях. «Барышня, соедините меня...» Барышня и соединяет. Буквально, проводом.
Коммутатор
Перетыкание проводов быстренько заменили переключателями: тумблерами или поворотными. Ну и лампочки, конечно, как устроство вывода.
Потом наступила эпоха перфокарт и перфолент. Машина их ела и мигала лампочками в ответ. Сразу стало понятно, что дырявить эти дырочки вручную — гиблое дело (впрочем, наверняка всякое бывало). А печатные машинки, да и телеграф, уже тогда были. Ну и были телетайпы. Эти вполне себе клавиатуры тут же приспособили сначала для пробивания перфолент/карт, а потом и для непосредственного ввода данных уже в электронные компьютеры.
Те же телетайпы были ещё и принтерами. И компьютеры научились на них печатать. Вводим буковки, получаем буковки. Уже хорошо.
Телетайп
Что ещё осталось? Да, чтобы не тратить тонны бумаги, придумали подключать к компьютеру ЭЛТ экраны. Забавно, что в некоторых древних компьютерах ЭЛТ использовались как память (люминофор продолжает какое-то время светиться после облучения электронами). И уже тогда энтузиасты начали писать компьютерные игрушки, рисующие что-нибудь на этих ЭЛТ.
Клавиатура есть. Экран есть. Рисовать компьютеры пока могут только буковки. Но и этого достаточно. А ещё компьютеры были довольно дорогие и большие. Поэтому пользовались ими сразу несколько человек. Часто даже удалённо, через те же телетайпы. Это даже называлось многопользовательскими многотерминальными системами. Но у каждого человека была своя оболочка, принимающая его команды. Это был Unix. И это был интерфейс командной строки (короче говоря, CLI).
CLI on Apple
CLI, как вы знаете, до сих пор жив и процветает. Потому что ничто не может превзойти его по эффективности использования канала связи с удалённой машиной. Поэтому удалённое администрирование — это только CLI. За редким исключением дурацких Windows серверов.
CLI хорош, но требует серьёзной предварительной подготовки пользователя. Нужно знать, какие команды есть, что они делают, какие аргументы и в какой форме принимают. Прежде чем начать пользоваться, нужно это всё изучить. На уровне концепций и способов манипуляций с ними. Не зная, что такое файловая система и каталоги, невозможно понять, что делает команда cd.
Хотелось чего-то более очевидного, наглядного и интуитивно понятного. Поэтому понятен бешенный успех того же Norton Commander. Технически это всё тот же текстовый интерфейс. Но визуально это что-то совсем другое. Это что-то, что наглядно представляет предметную область, в данном случае файлы.
Norton Commander
Это что-то называется графическим (в случае Нортона — псевдографическим) пользовательским интерфейсом (он же GUI).
Графика, т.е. возможность отображать компьютером не только буковки, но и любое изображение, была очень важна. Те же ребята, что на малюсеньких круглых ЭЛТ первых компьютеров делали игрушки про полёты в космос, делали для этих же компьютеров и программы проектирования инженерных сооружений и расчёта их прочности. То, что позднее превратилось в целый класс ПО — CAD. Тут без графики ну просто совсем никак.
Sketchpad
А ребята из Xerox в 70-е годы прошлого века баловства ради решили, что нарисовать можно не только балки и мосты, но обычные вещи, вроде рабочего стола (буквально, стола), папочек, листиков с документами и всё такое. На их беду к ним в гости зашёл некто Стив Джобс. И компьютеры фирмы Apple чуть ли не с самого начала имели GUI в качестве основного интерфейса взаимодействия с пользователем. PC с MS-DOS на этом фоне смотрелся бледновато. Microsoft со своими Windows подтянулись заметно позднее.
Xerox Alto
Что нам даёт GUI? Наглядность. Понятность. Возможность протыкать всё, что есть (меню), не запоминая заранее. Для простого смертного пользователя требуется гораздо меньше предварительной подготовки. Конечно, появились свои примитивы, которым всё же нужно учить. Окна, поля ввода, кнопки, меню. Надо же понимать, что в эти нарисованные прямоугольники можно тыкать.
Кстати, тыкать. GUI подарил нам указательное устройство. Как ни удивительно, вначале это было световое перо (или пистолет). Такая штуковина, которую нужно было наводить на экран. Но прижилась всё же мышка. Хоть она и привязана к конкретным координатам экрана только посредством указателя, и сам по себе манипулятор никаких координат не выдаёт.
Световое перо
Экран, клавиатура, мышка. Окна, меню, кнопки. Что дальше? Дальше случился Web (он же WWW).
До Веба всё взаимодействие в Интернете было исключительно текстовым. Та же электропочта, Gopher... Веб тоже задумывался текстовым, но почти сразу же для него появился графический браузер — Mosaic. Ну и быстренько научились в HTML документы вставлять картинки.
NCSA Mosaic
Никто тогда не думал, что под Веб можно создавать приложения. Ну да, появились формы. Да, появились какие-то интерфейсы. Но строились они исключительно по принципу запрос-ответ. Просим пользователя заполнить форму, отображаем результат. Никакого интерактива и мгновенных изменений, никакой анимации, в конце концов. Как оно уже всё было в десктопных приложениях, в Вебе ничего такого не было.
А потом появился JavaScript. Сначала просто анимация, изменение страницы без перезагрузки. Потом докатились до Single Page Application. Браузеры посолидней стали. Железо десктопное подтянулось. И оказалось, что Web UI вполне может тягаться с десктопным GUI. И даже имеет некоторые преимущества. Браузер уже есть в каждой приличной системе, и приложение не нужно устанавливать. И обновляться оно будет само. И к облакам-серверам будет обращаться вполне естественным способом, через тот же HTTP.
Дошло до того, что появилась даже Chrome OS — операционная система, в которой есть только браузер и веб приложения.
Chrome OS
Сейчас веб UI почти не уступает дестопному UI. Анимация, свестелки, перделки, даже 3D, всё есть. Десктоп ещё не умер окончательно, пожалуй, только по двум причинам. Веб приложениям всё ещё нужен Интернет, чтобы полноценно работать. Веб приложения потребляют значительно больше ресурсов, чем аналогичные по функциональности десктопные приложения.
Дошло уже до того, что стали лениться делать нормальные десктопные приложения, а стали просто делать запускалку с WebView. А внутри этого WebView запускать те же самые веб приложения. JavaScript повсюду, JavaScript везде, даже на серверах. Ужас какой.
Electron
Однако, с веб приложениями началась интересная тенденция. Упрощения. Десктопные приложения можно было делать узкоспециализированными, продавать конкретной аудитории, с которой можно брать деньги за обучение. В вебе аудитория потенциально не ограничена. Прийти и запустить ваше приложение может кто угодно. И надо бы, чтобы этот кто угодно не развернулся в ужасе, а проникся любовью и уважением к вашему продукту. Заработал маркетинг, начали всерьёз интересоваться пользователем. И оказалось, что кучи настроек и возможностей не нужны всем. Всем нужно, чтобы было просто и красиво. И интерфейсы стали проще и понятнее. А приложения часто стали менее функциональными.
Your company app
Однако, действительно сложные приложения, с действительно сложным и нагруженным интерфейсом, всё ещё проще делать для десктопов. Поэтому серьёзные IDE пока существуют только для десктопа. А ещё IDE требуют заметных ресурсов, и дополнительные накладные расходы веба тут совсем не к месту. Так что пока что десктоп жив.
Jobs and iPhone
Благодаря тому же Стиву Джобсу у нас сейчас появился ещё один тип пользовательского интерфейса. Мобильный.
Вроде тот же GUI, но не совсем. Во-первых, очень очень очень маленький экран. Не по пикселям, а по физическим размерам. Во-вторых, экран сенсорный. Вместо, как внезапно оказалось, очень точной мыши, у нас толстый пользовательский палец.
Маленький экран очень сильно ограничивает сложность интерфейса. На него просто не помещается много элементов управления. Нужно ещё сильнее упрощать. Управление пальцами добавляет некоторые неожиданные возможности, вроде свайпов-пролистывания в разные стороны. Но и делает невозможными некоторые привычные для десктопа или веба вещи, вроде всплывающей подсказки при наведении мыши. Ведь никакого указателя мыши тут нет.
Я честно думал, что эти сенсорные штучки не привносят ничего нового. Но потом я стал наблюдать, как моя доча, совершенно не умея читать, обращается с планшетом на Android. Её никто не учил им пользоваться. Но она сама ставит интересные ей игры, играет в них, и смотрит мультики. Да ещё и учит пользоваться планшетом бабушку. Сама. Значит, та самая интуитивная понятность интерфейса всё же достигла уровня, когда с интерфейсом могут справиться четырёхлетние дети.
Mobile UI
Мобильный интерфейс должен быть ещё проще, ещё понятнее, ещё красивее, ещё удобнее чем десктопный или веб. Смартфоны теперь в каждом кармане. Потенциальная аудитория ещё больше, чем у веба. И если вы сможете дать пользователю возможность сделать что-то на два тыка быстрее, чем все конкуренты, вы сможете выиграть. Если, конечно, это будет красиво :) И если вы сможете сделать красивее и удобнее, но абсолютно то же самое, что у всех конкурентов, вы тоже сможете выиграть.
Удобство — это, не в последнюю очередь, отзывчивость интерфейса. А отзывчивый интерфейс можно построить только экономя на ресурсах. Хоть ядер и памяти в современных смартфонах побольше, чем на иных ноутбуках, они вовсе не все всегда задействованы. У нас ведь ещё и очень маленькая батарейка, чей заряд нужно беречь. Поэтому веб приложения, хоть и очень очень стараются стать ещё и мобильными, через те же WebView, всё ещё не так продвинулись в мобилках, как уже отвоевали себе пространство на десктопах. Может, это и хорошо. Может, это и плохо.
Я уже много лет жду, когда, наконец, появится технология, которая позволит делать одинаково удобные, отзывчивые и нативные приложения под десктоп, веб и мобилки. Но пока подобного решения не предвидится. Всё же размер экрана имеет значение, и интерфейс мобилки нужно строить по совсем другим принципам, отличным от десктопного.
UI toolkits
Кстати, заметили, происходит «мобилизация» веба. Так как мобильных пользователей больше, сайты стали упрощать интерфейс, укрупнять элементы управления, внедрять адаптивную вёрстку. Чтобы на экране телефона смотрелось прилично. А на десктопе как-нибудь перебьёмся.
Что дальше? На подходе у нас виртуальная реальность (она же VR), где пользователь полностью погружается в виртуальный мир. (Привет, «Газонокосильщик».) И так же на подходе дополненная реальность (она же AR), где пользователь видит виртуальные предметы или метки поверх своего обычного восприятия реальности. По мне, так существенная разница лишь в том, какая часть реальной реальности замещается виртуальной.
Для этих реальностей нужны специальные устройства вывода: шлемы, очки и всё такое. Нужны и специальные устройства ввода, нужен указатель в трёхмерном пространстве: перчатки или что-то подобное. И нужны какие-то новые способы организации интерфейса пользователя. Какие именно, пока, похоже, не очень понятно.
Microsoft HoloLens
А вот пример Google Glass показал, что интерфейс может быть голосовым. А успех(?) таких устройств, как Amazon Echo и Google Home показывает, что сам по себе голосовой интерфейс, без других способов взаимодействия, тоже имеет ценность.
Amazon Echo
Каким должен быть голосовой интерфейс? Кто знает? Очевидно только, что никто не сможет надиктовать, скажем, программу на C. Так что ввод текста и чтение текста никуда не денутся.