2015-04-25

О Dockerfile

Кажется, я становлюсь фанатом Docker.
Фанаты калифорнийских Dockers
Это фанаты калифорнийской команды Dockers по австралийскому футболу
В прошлый раз мы развлекались с Докером, так сказать, вручную. Запускали контейнеры с башем, правили файлы конфигурации, сохраняли изменения в образы. Но оказывается, есть более милый сердцу админа способ создания образов. Это — Dockerfile.
Давайте так же создадим «эталонный» образ Nginxа на основе Ubuntu. Только сделаем это с помощью Dockerfile. Создадим каталог, допустим, ~/tmp/test-nginx. Создадим в этом каталоге файл Dockerfile, с большой буквы. И начнем записывать в этот Dockerfile инструкции по сборке образа.
Для начала сообщим, что основой нашего образа будет официальный образ Убунты.
FROM ubuntu:trusty
FROM означает из какого образа мы делаем свой. Докер самостоятельно выкачает этот базовый образ с Хаба.
Устанавливаем Nginx из штатных пакетов. При этом выставляем переменную окружения DEBIAN_FRONTENT в noninteractive, чтобы apt-get не спрашивал обо всяких возможных настройках и ставил все молча и по дефолту.
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y nginx
ENV выставляет переменную окружения. RUN запускает команду во временном контейнере.
Что нам еще нужно? Давайте полностью заместим конфигурацию Nginx чем-то своим. Создадим файл nginx.conf в том же каталоге, где находится наш Dockerfile, примерно такого содержания. (Помните, для продакшина вам нужен таки конфиг подлиннее.)
user www-data;
worker_processes 4;
daemon off;
pid /run/nginx.pid;

events {
    worker_connections 768;
}

http {

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    server {
        listen 80 default_server;
        root /www;
        index index.html index.htm;
    }

}
Здесь нам важен daemon off, чтобы Nginx не завершал свой стартовый процесс, чтобы он мог жить основным процессом контейнера. Слушаем порт 80. Корень сайта помещаем в каталог /www.
А теперь добавляем строчку в Dockerfile.
ADD nginx.conf /etc/nginx/
ADD добавляет файл в файловую систему временного контейнера. Можно даже указывать URL, и Докер скачает нужный файл при сборке образа.
Сообщаем Докеру, что в контейнере будет слушаться порт 80.
EXPOSE 80
Сообщаем Докеру, что каталог /www является томом и может содержать общие файлы, общие с хостом или другими контейнерами.
VOLUME /www
Ну и, наконец, сообщаем Докеру, что при запуске образа по умолчанию нужно запускать Nginx.
CMD /usr/sbin/nginx
Получился вот такой коротенький Dockerfile.
FROM ubuntu:trusty

ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y nginx

ADD nginx.conf /etc/nginx/

EXPOSE 80

VOLUME /www

CMD /usr/sbin/nginx
Собираем вожделенный образ. Сразу дадим ему имя test-nginx с помощью ключа -t (tag). Последним параметром указываем каталог, где лежит Dockerfile и конфиг нашего Nginx.
$ docker build -t test-nginx ~/tmp/test-nginx
Sending build context to Docker daemon 3.584 kB
Sending build context to Docker daemon 
Step 0 : FROM ubuntu:trusty
 ---> d0955f21bf24
Step 1 : ENV DEBIAN_FRONTEND noninteractive
 ---> Using cache
 ---> 273e05ed8ff1
Step 2 : RUN apt-get update && apt-get install -y nginx
 ---> Running in b207412ba5bd
... тут долго выполняется apt-get
 ---> 9150ea91b621
Removing intermediate container b207412ba5bd
Step 3 : ADD nginx.conf /etc/nginx/
 ---> 8a8cddab26ce
Removing intermediate container ba0419146028
Step 4 : EXPOSE 80
 ---> Running in 7d586887a08b
 ---> c01822146625
Removing intermediate container 7d586887a08b
Step 5 : VOLUME /www
 ---> Running in cec6783ab269
 ---> eb2ebfbfe0d5
Removing intermediate container cec6783ab269
Step 6 : CMD /usr/sbin/nginx
 ---> Running in ce2b8d4c6fa7
 ---> d1a828f94444
Removing intermediate container ce2b8d4c6fa7
Successfully built d1a828f94444
Что произошло?
Ну, у нас появился образ.
$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED        VIRTUAL SIZE
test-nginx  latest  b6f2c6be5516  2 minutes ago  227.6 MB
Который состоит из кучи слоев.
$ docker history test-nginx
IMAGE         CREATED        CREATED BY                                     SIZE
d1a828f94444  5 minutes ago  /bin/sh -c #(nop) CMD [/bin/sh -c /usr/sbin/n  0 B
eb2ebfbfe0d5  5 minutes ago  /bin/sh -c #(nop) VOLUME /www                  0 B
c01822146625  5 minutes ago  /bin/sh -c #(nop) EXPOSE map[80/tcp:{}]        0 B
8a8cddab26ce  5 minutes ago  /bin/sh -c #(nop) ADD file:70b50f5b68d7d9f286  353 B
9150ea91b621  5 minutes ago  /bin/sh -c apt-get update && apt-get install   39.28 MB
273e05ed8ff1  3 days ago     /bin/sh -c #(nop) ENV DEBIAN_FRONTEND=noninte  0 B
d0955f21bf24  5 weeks ago    /bin/sh -c #(nop) CMD [/bin/bash]              0 B
9fec74352904  5 weeks ago    /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/  1.895 kB
a1a958a24818  5 weeks ago    /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic  194.5 kB
f3c84ac3a053  5 weeks ago    /bin/sh -c #(nop) ADD file:777fad733fc954c0c1  188.1 MB
511136ea3c5a  22 months ago                                                 0 B
Что ведь делает docker build? Для каждой команды в Dockerfile создается новый временный контейнер, в контейнере выполняются команды или меняются метаданные (см. строки, у которых SIZE = 0 B), измененный контейнер коммитится как еще один слой в создаваемый образ. Временный контейнер затем удаляется (Removing intermediate container). Последняя строчка в этой истории слоев — корень всех контейнеров. Последующие четыре строчки — это образ Убунту. А выше — наши команды.
Казалось бы — расточительство. Создавать на каждый чих временный контейнер и целый слой. Но тут, как и с коммитами в системы контроля версий. Если коммит маленький, но важный, лучше пусть он будет отдельным коммитом, чем смешается с другим коммитом. Коммитить часто — хорошо.
К тому же, в данном случае есть потрясающий побочный эффект. Обратите внимание на эти строчки вывода команды docker build.
Step 1 : ENV DEBIAN_FRONTEND noninteractive
 ---> Using cache
 ---> 273e05ed8ff1
Временный контейнер не создавался. Слой был взят из кэша. Потому что позавчера я уже собирал контейнер на основе Убунту, где первой командой была установка переменной окружения DEBIAN_FRONTEND. Особенно заметным эффект кэширования будет, если вы поменяете конфиг Nginx. Предыдущая команда apt-get выполняться не будет. Результат установки пакетов Nginx будет взят из кэша. Что просто чудовищно сэкономит время. В результате небольшой тюнинг и пересборка образов — это быстро. Ну и отсюда эмпирическое правило: тяжелые и редко изменяющиеся команды сборки образа помещайте в начало Dockerfile.
Есть у сборки из Dockerfile и другое интересное свойство. Дело в том, что официальные базовые образы изменяются. В ту же Убунту приходят обновления безопасности и под именем ubuntu:trusty будет скрываться нечто совсем другое. Если вы нежно собирали образ ручками, то вам, как и в случае с виртуальными машинами, лучше сделать apt-get update && apt-get upgrade самостоятельно, накатив обновления дистрибутива еще одним слоем. В случае же Dockerfile вы просто заново запускаете docker build. Образ при этом пересоздается полностью. Но обновления базовой системы будут нижними слоями, что вроде как идеологически более правильно.
В любом случае, чтобы обновить контейнеры на продакшине, вам придется создать их из обновленных образов заново. А старые контейнеры загасить. Кажется жестокостью, но это согласуется с облачным подходом. Вместо того, чтобы обновлять на месте, проще все переразвернуть заново.
You're Master!
Ну давайте запустим наш Nginx. Команду, которая будет запускаться при старте контейнера, мы уже указали, так что достаточно только имени образа.
$ docker run -d --name test test-nginx
Ой. Мы забыли пробросить порт.
$ docker port test 80
2015/04/25 22:29:36 Error: No public port '80' published for test
Давайте попробуем ключ -P.
$ docker stop test
$ docker rm test
$ docker run -d --name test -P test-nginx
Ключ -P пробрасывает все задекларированные образом порты в некий дипазон портов-за-49000 на хосте. Это один из эффектов директивы EXPOSE в Dockerfile.
$ docker port test 80
0.0.0.0:49153
Ну, давайте зайдем на http://localhost:49153/. Действительно, Nginx. Только выдает 403 Forbidden. Потому что /www у него пустой.
Ну давайте вернемся к нашему прошлому режиму запуска образа. Явно прокинем порты ключом -p и подсунем свой index.html, который лежит в ~/tmp ключом -v.
$ docker stop test
$ docker rm test
$ docker run -d --name test -p 8080:80 -v ~/tmp/index.html:/www/index.html test-nginx
Теперь http://localhost:8080/ показывает то, что нужно.
Ну вроде и все.
Docker commands
Попробуйте еще поставить в nginx.conf вывод логов в stdout. Тогда логи Nginx, пока он вертится в контейнере, можно будет смотреть командой docker logs.
Забавно получается, вся эта демонизация, когда мы отключаемся от консоли, пишем в логи и т.п., совершенно не нужна, когда у нас контейнеризация.

2015-04-19

О Docker

Кажется, я осилил Docker в достаточно хомячковой степени, чтобы написать об этом. В достаточной степени, чтобы использовать его на локалхосте, и не доставлять мучений окружающим.
Docker logo
Завел для локалхоста такое правило. Всякие инструменты разработки, гуй, компиляторы-шмапиляторы ставятся на локалхост. Как обычно. А вот все сервера, будь то какой-нибудь сервер БД, или веб сервер, или еще какой сервер, запускаются только в Докере.
Докер — это такой модный, молодежный и даже хипстерский способ работы с линукс контейнерами (они же LXC). Это такая легкая виртуализация, как, например, OpenVZ. Вы имеете одно ядро (своего локалхоста) на всех и суровую изоляцию процессов и файловой системы контейнера. Это все похоже на chroot (самый мягкий вариант), но скорее ближе к BSDшному jail или солярисовым зонам. Как-то так.
Зачем еще один инструмент управления контейнерами? Или, если вы, как и я, вообще с контейнерами, кроме chroot, дела не имели: Зачем вообще нужны эти контейнеры? Чем плохи виртуалки? Пусть даже такие попсовые, какVirtualBox.
Виртуалки, они сильно жирнее, чем контейнеры. Сколько там места на диске требует Убунту для установки? Минимум 6 гигабайт. Сколько ей для запуска графики и прочего нужно памяти? Ну хотя бы гигабайт. Что получается? Получается, что больше, ну, допустим, четырех виртуалок вы у себя на локалхосте не запустите.
А что такое Убунта для Докера? Это образ на 188 мегабайт. Ну и памяти он съест столько, сколько съедят запущенные в контейнере процессы.
Docker vs VMs
Вот тут начинается большая разница в использовании виртуалок и контейнеров. Виртуалки вы создаете на века. Чтобы бережно настраивать их. Обновлять в них софт. Ставить все до кучи в одну виртуалку. И БД, и веб сервер, и все, что нужно для приложения. Ну так уж и быть, иногда бывает, что и копируете виртуалку куда-нибудь соседу или на другой сервер. Деплоить виртуалки целиком? Ненене, вы что, мы ж не в облаке каком. (Ну в облаке по необходимости придется).
Контейнер создается, чтобы запустить один процесс. Один единственный. Зато бережно изолированный. Зато в тщательно подобранном окружении, с конкретными версиями только нужных либ. Ну в редких случаях парочку процессов.
И если процесс нам больше не нужен, мы убиваем контейнер. Возможно, то, что процесс наделал, нам нужно. Тогда мы создаем из остатков контейнера новый образ. Для создания новых контейнеров. А может, процесс сделал свое дело и все. Тогда мы без сожаления возвращаемся туда, откуда начали. И новые контейнеры создаем из оригинального образа.
Получается в Докере такой круговорот. Образ — контейнер — образ. И это вполне естественно и удобно.
Docker Hub
Образы берутся с Хаба. Это Докерхаб. Но можете думать о нем, как об аналоге Гитхаба. Только для образов.
Для всего более менее популярного есть образы. Убунта, МонгаИнфлюксДБ с Графаной — пожалуйста.
Образы принято называть так.
юзер/образ:тег
Юзер — это зарегистрированный юзер Хаба. Очень официальные образы чего-нибудь очень популярного не имеют этой части имени. Образ — это имя образа, одно или несколько слов через дефис. Тег — это метка образа. Убунты и всякие монги бывают разных версий, соответственно, для разных версий и разные теги. Теги часто имеют много синонимов, например, ubuntu:trusty и ubunutu:14.04 — это одно и то же. Последняя стабильная версия софта всегда помечается тегом :latest.
Образы слоистые. Как людоеды. Образы под одним и тем же названием и тегом могут изменяться. Софт может обновляться или еще что. Изменения, я полагаю, происходят в результате выполнения того самого цикла образ — контейнер — образ. Каждый новый слой контейнера — это дифф от его предыдущего состояния. В смысле изменений файлов и метаданных контейнера.
На самом деле, думайте о слоях образов, как о коммитах. А о тегах, как о тегах. Тут работает полная аналогия с (распределенными) системами контроля версий. Вот эта вот версионная система образов, с публичным репозиторием, и делает Докер тем самым Докером.
Ogre layers
Ну давайте возьмем образ Убунты и сделаем с ним что-нибудь.
$ docker pull ubuntu:latest
Рекомендую всегда указывать тут тег, а то еще выкачает туеву хучу совсем не нужных вам версий образов.
Теперь у вас где-то локально есть образ Убунты. Можно убедиться в этом.
$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED      VIRTUAL SIZE
ubuntu      latest  d0955f21bf24  4 weeks ago  188.3 MB
Теперь создадим из образа новенький контейнер и запустим в нем баш.
$ docker run -i -t ubuntu bash
Ключ -i говорит о том, что мы хотим общаться с процессом в контейнере интерактивно. Ключ -t требует создание виртуального tty для контейнера, иначе интерактивности не получится. Всегда используйте -i и -t вместе. Далее указывается имя образа: ubuntu и запускаемая в контейнере команда: bash. Можно далее указать и параметры для этой команды, тут имеется аналогия с ssh.
Ну давайте поставим в контейнер nginx.
# apt-get install nginx
При желании можно поправить его конфигурацию. Вима и Емакса в образе нет. Ну давайте хоть nano поставим.
# apt-get install nano
# nano /etc/nginx/sites-available/default
В этом файле ничего менять не надо. Пока лишь запомним, что nginx слушает 80 порт и корень дефолтного сайта он ищет по пути /usr/share/nginx/html.
Однако, нам обязательно надо поменять /etc/nginx/nginx.conf, добавить в него строчку
daemon off;
Как-то так.
# echo "daemon off;" >> /etc/nginx/nginx.conf
Nginx поставили? Сохранили конфиг? А теперь выходим из консоли. Жмем Ctrl+D.
Упс. Контейнер схлопнулся. Неожиданно, после виртуалок-то. На самом деле все правильно. Процесс, ради которого создавался контейнер, в данном случае баш, завершился. Ну и контейнер должен завершить свою работу. Поэтому мы и добавили daemon off, чтобы nginx не завершал свой стартовый процесс и Докер продолжал бы работу с nginx в качестве основного процесса.
Посмотрим на останки контейнера.
$ docker ps -a
CONTAINER ID  IMAGE          COMMAND  CREATED         STATUS                     PORTS  NAMES
5bf7094e6961  ubuntu:latest  bash     10 minutes ago  Exited (0) 27 seconds ago         silly_darwin
Вот он, родимый, с веселым именем silly_darwin. Докер так забавно раздает случайные имена.
Что мы в этом контейнере наделали?
$ docker diff silly_darwin
A /.bash_history
C /bin
A /bin/nano
A /bin/rnano
C /etc/default
A /etc/default/nginx
...
Ну все правильно, добавились файлы пакетов nginx и nano, с зависимостями. Нам надо сохранить эти изменения для последующего использования. В образе. Замыкаем цикл образ — контейнер — образ.
$ docker commit silly_darwin gelin/test-nginx
Мы добавили свой собственный слой поверх штатного образа Убунты. И сохранили его локально под именем test-nginx. Как новый образ.
Давайте теперь запустим наш сервер. Сервер у нас сетевой. И, как мы помним, он слушает порт 80. В контейнере. Чтобы достучаться до сети в контейнере, нам нужно указать маппинг порта. Пусть порт 8080 нашего локалхоста мапится на 80 порт в контейнере. Нам поможет ключ -p.
$ docker run -d --name test -p 8080:80 gelin/test-nginx nginx
Мы тут явно указали имя нашего контейнера: test. А еще указали ключ -d, чтобы запущенный докер ушел в фон. Теперь сервер действительно запущен как сервер.
$ docker ps
CONTAINER ID  IMAGE                    COMMAND  CREATED        STATUS        PORTS                 NAMES
4fe62f3690c7  gelin/test-nginx:latest  nginx    5 seconds ago  Up 5 seconds  0.0.0.0:8080->80/tcp  test
И по адресу http://localhost:8080 локалхоста должна открываться стартовая страница нашего контейнерного nginxа.
Создадим-ка другую стартовую страничку. Нарисуем её в чем угодно и положим в файлик ~/tmp/index.html нашего ненаглядного локалхоста. Как её теперь передать нашему nginxу?
Можно, конечно, снова запустить баш и модифицировать /usr/share/nginx/html. Закоммитить и создать новый образ. Повторить цикл.
Но есть более интересный способ для случая, когда у нас есть внешние для контейнера данные, которые нужно в него подсунуть. Это так же касается и баз данных, например, можно подсунуть файлы самих данных контейнеру. Задействуем ключик -v и скажем, что ~/tmp/index.html в файловой системе локалхоста должен оказаться в /usr/share/nginx/html файловой системы контейнера.
$ docker run -d --name test -p 8080:80 -v ~/tmp/index.html:/usr/share/nginx/html/index.html gelin/test-nginx nginx
2015/04/19 10:34:50 Error response from daemon: Conflict, The name test is already assigned to 4fe62f3690c7. You have to delete (or rename) that container to be able to assign test to a container again.
Ахда. Контейнер с именем test у нас все еще запущен. Он нам уже не нужен. Давайте убъем его и подчистим останки.
$ docker stop test
$ docker rm test
Снова запускаем.
$ docker run -d --name test -p 8080:80 -v ~/tmp/index.html:/usr/share/nginx/html/index.html gelin/test-nginx nginx
Смотрим, изменилась ли страничка на http://localhost:8080. Ура, изменилась! Можно даже поправить файлик index.html и убедиться, что nginx его корректно подхватит.
Ключик -v работает с так называемыми томами (volumes). Файловая система хоста и контейнера объединяются так что файлы хоста замещают файлы контейнера.
А что, если наш nginx не должен быть публичным сервером? Что если он скрывает некоторое API, которое нужно другому контейнеру в нашей микросервисной архитектуре, распиханной по контейнерам?
Нам нужны линки между контейнерами. Запустим еще одну Убунту, но с ключиком --link.
$ docker run -i -t --link test:nginx ubuntu bash
Мы попросили Докер слинковать этот новый контейнер с другим контейнером по имени test. Мы не зря давали имя явно. И этот test все еще запущен в фоне. Внутри нового контейнера этот test будет виден под алиасомnginx.
Что это значит? А вот что. Нам виден хост по имени nginx.
# ping nginx
PING nginx (172.17.0.26) 56(84) bytes of data.
64 bytes from nginx (172.17.0.26): icmp_seq=1 ttl=64 time=0.200 ms
На самом деле эта магия заключается в автоматическом переписывании Докером /etc/hosts.
# cat /etc/hosts
172.17.0.26 nginx
Соответственно, 80 порт этого хоста вполне себе доступен.
# apt-get install curl
# curl http://nginx/ -v
* Hostname was NOT found in DNS cache
*   Trying 172.17.0.26...
* Connected to nginx (172.17.0.26) port 80 (#0)
...
А еще есть переменные окружения, сообщающие о том, что за хосты нам доступны и какие порты там открыты.
# env
NGINX_PORT_80_TCP_PROTO=tcp
NGINX_PORT_80_TCP=tcp://172.17.0.26:80
NGINX_PORT_80_TCP_PORT=80
NGINX_PORT_80_TCP_ADDR=172.17.0.26
NGINX_PORT=tcp://172.17.0.26:80
Можно использовать их во всякоразных скриптах.
Получившися образ, если он удался, полезен для других, нужно развернуть где-то еще, можно запушить на Хаб.
$ docker push gelin/test-nginx
Сначала, конечно, нужно завести там аккаунт и залогинить Докер.
И кто-то другой, на другом локалхосте, сможет получить этот образ и запустить ваше приложение в точности в той же конфигурации, которую вы создали. Вот для этого и нужны контейнеры.
Docker workflow

2015-04-05

О CodeFest 2015

О Кодефесте написать пришла пора. Кодефест о себе не напишет сам.
Кодефест доставил. Местами даже было улётно. Хоть я уже третий год порываюсь изменить традиции, и забить на этот Кодефест, например, в пользу Дампа, но всенепременно в формирующейся на глазах программе проскакивает какой-нибудь чудный докладчик с чудной аннотацией к нему. И приходится ехать. Общаться с омскими коллегами.
CodeFest
Ехать в купе значительно комфортнее, чем в плацкарте. Я в нем в длину помещаюсь. И почти нисколько не дороже, если брать билеты заранее и не вестись на попсовые фирменные поезда.
Бывает так, что поезд, отправляющийся от Омска на полчаса позже, приходит в Новосибирск на два часа раньше. Чем другой поезд. Хотя вроде по одним и тем же рельсам едут.
Хряпнуть кофе в пять утра в Новосибирске возле вокзала можно либо в KFC, либо, если пройтись чуток по Вокзальной магистрали, в People's. Вот за такие часы работы, сдобренные действующим в это время бизнес-завтраком, респект и уважуха пиплам. В субботу утром народ, как обычно, догуливал вечер пятницы.
Еще мы три вечера зависали в Шафране. Знатоки восточной кухни утверждают, что она у них хороша. Ну а мне хватало отсутствия лишних пьяных посетителей, спокойствия, пива и закуски.
В понедельник, уже после кодефестов, занесло нас в НИИ КуДА. Это уже в Академгородке. Я был в сосновом лесу Академгородка, и меня не съели медведи. НИИ КуДА становится в моем личном рейтинге заведений, куда надо постараться обязательно попасть при посещении города, где интересно изучать обстановку и меню, на первое место. Ранее на первом месте была Пожарка в Екатеринбурге.
Хостел Цоколь хорош. Но он в подвале, и в дальних комнатах невыносимо душно спать. Хостел Достоевский испортился. Шумно, грязно, душ на ремонте. Хостел Royal, говорят, ничего. Но тесновато и всего лишь один душ.
Автобусы в Экспоцентр доставляли. Правда, в этот раз мне показалось, что они были несколько ушатаны и слегка вонючи. Очереди на регистрацию, благодаря предварительному хэшированию, не было. Но некоторый диапазон хэшей оказался утерян, и людям рисовали бейджи маркером. Очереди в гардероб и за обедом были, как обычно. Впрочем, бумажные пакеты для еды несколько ускорили продвижение обеденной очереди.
Dino Esposito
Ключевым докладчиком был Дино Эспозито. Широко известный в мире .NET товарищ, как оказалось. Но, несмотря на титул евангелиста и .NET прошлое, Дино вещал очень правильные вещи, не скатываясь на восхваление конкретных технологий и решений. Это очень здорово. Редко сейчас кто промывает мозги на таком высоком уровне, не вдаваясь в отвлекающие частности.
На киноут он продвигал DDD. Даже расширил идею до UX-Driven Design. Мол, копаясь в коде, помните, ради кого это все и зачем это нужно. Это все правильно. Но у всех чего-то-там-driven design концепций есть проблема. Проблема применимости в реальной жизни. В большинстве случаев нашего аутсорса до дизайна здесь дело даже не доходит. Дизайн происходит где-то там, на стороне заказчика. А до нас доходят лишь готовые указания, часто без объяснений «почему», так необходимых русскому человеку. В режиме «должно было быть готово еще вчера» даже до TDD дело не доходит, куда уж DDD.
На второй день Дино вещал о CQRS. Эта концепция близка к CQS, когда мы разделяем команды, которые модифицируют состояние объекта, от запросов, которые лишь запрашивают, но не меняют состояние. Здесь мы расширяем идею до всей системы, или до специфичного bounded context. Модификацию данных в нашей системе мы делаем одним способом, со своими моделями и командами. А чтение данных делаем другим способом, с другими моделями и запросами. Логика тут есть. Это действительно может упростить дело в сложных случаях, ведь придется думать о перемещении данных лишь в одном направлении. И это хорошо ложится на веб, где запросы случаются гораздо чаще, чем команды модификации, и уже на уровне протокола эти вещи разделены — разные типы HTTP запросов. Поначалу я воспринял этот CQRS как откровение, но быстро выяснилось, что опытные коллеги уже давно используют подобные техники. И таки Фаулер описал это еще в 2011.
Еще Дино выступал на квартирнике вместе с Михаилом Яричуком. На тему Polyglot Persistence. Ребята повторили тезисы отличной книги опять-таки Фаулера. Пришло время, когда у вас на проекте будет более одной базы данных. И это нормально. И нормально, что некоторые из этих БД будут NoSQL. Вот, собственно, о том, когда нужно NoSQL, а когда не нужно, и рассуждали.
Пару лет назад подобные рассуждения были бы более уместны. И мы с Гришей могли бы зажечь не хуже. Но нашу заявку отклонили, потому что организаторы привыкли «привозить спикеров, которые в Сибири ни разу не выступали». Ну тогда надо было бы половину докладов, сделанных самими двагисовцами, тоже выкинуть. Ну да ладно. Проехали.
Пара докладов на дизайнерской секции, куда я намыливался, сдулись. Жаль. Зато Геннадий Лохтин из Яндекса рассказал о ближайшем будущем интерфейса Яндекс.Браузера. Дизайнеры аплодировали стоя. Действительно, презентация была очень зрелищная. И, в общем, я — за минимализацию интерфейса самого браузера и максимизацию собственно контента, если это не в ущерб навигации. У Яндекса, пожалуй, и получилось. Вот только не надо громадного фонового видео. Мне как-то русзена достаточно.
Явным трендом конференции были реактивные потоки. Они же data flow, они же akka stream, они же… Да как их только не называют. Алексей Романчук рассказал именно про Akka Streams. Игорь Кашкута же рассказал о реактивном программировании применительно к iOS. Я не понимаю всей этой шумихи. Akka, может, и решает какие-то частные проблемы, но это точно не серебряная пуля. Зачем нужна еще какая-то функциональная абстракция в мобильном GUI, где, как и в любом GUI, все уже и так построено на событиях, не ясно. И вообще, вся эта мода на функциональщину, которая уже проникла в тот же C# в виде LINQ, и проникает в Java, начиная с восьмой версии, имхо, увеличивает удобство написания кода, но сильно ухудшает читаемость кода.
Кстати, о читаемости. В чудном, но узкоспецифичном докладе Ивана Ремизова, наглядно видно, что оптимизация, в данном случае скорости выполнения, сильно портит читаемость. Оно в общем-то и понятно. Но, по возможности, не делайте так. Пишите код так, будто сопровождать его будет маньяк, который знает, где вы живете. ©
Часто поминали микросервисную архитектуру. Но это были доклады о суровом жизненном опыте. Без особой пользы для тех, кому придется пройти по той же дорожке.
На Макса Дорофеева я благополучно не влез. Дважды. Зато чуток поболтал с ним за кофе. Оказывается, он стал почетным омским джедаем.
Макс Дорофеев
Олег Бартунов вновь радовал новостями про PostgreSQL. Больше всего я был поражен тем, что Олег, впервые на моей памяти, уложился во время, отведенное для выступления. Хоть, как всегда, большая часть интереснейших слайдов так и не была продемонстрирована. Надо их пересмотреть. Ну как уложился, после обеда его утащили на квартирник, где он отдувался еще полдня, до самого закрытия конференции. Фиг с ним, с языком запросов для JSON. А вот новости об импортозамещении и легальном положении Постгреса в России, в общем, радуют.
Под конец Николай Чабановский объявил о скором открытии русскоязычного StackOverflow, на базе ХэшКода. Ой, они уже запустились!
Последний секретный закрывательный доклад был, конечно, сюрпризом. И это был лучший доклад конференции. По крайней мере по количеству хорошести на минуту доклада :) Думаю, самый распоследний кодер в зале задумался: тем ли он занимается, делает ли он филипинских бедняков счастливыми, как жить дальше. Спасибо.
Я снова отметился на стенде JetBrains. На этот раз, в ответ на призыв в Твиттере, пожаловался на неконсистентность некоторых сочетаний клавиш в IDEA. Ну и традиционно поучаствовал в розыгрыше лицензий и не выиграл.
гитарист
Ах да. Клюковка из брусники — отвратительна. Старое доброе виски входит в организм лучше.