Докер
Последний месяц меня дико прет с Докера. Это настоящий прорыв в индустрии. Я подключаю Докер ко всем репозиториям. Решил поделиться впечатлениями, описать плюсы и рассмотреть реальный пример.
Про Докер написано очень много. Я не собираюсь досканально описывать как он работает. Расскажу лишь кратко о том, что вы, возможно, не слышали.
Докер – утилита для запуска процесса в изолированном окружении. Технически это работает поверх одной из фич ядра Линукса – Cgroups.
Преимущество Докера в том, что стирается грань между отдельными машинами. Если вы собрали образ на Линуксе, то он заведется на Маке. При этом понадобится только Докер. Никаких либ или пакетов выкачивать не нужно.
Этот плюс я уже испытал на практике. Мне пришел из ремонта Мак. За время работы на Линуксе я успел перевести большую часть репозиториев на Докер. На Маке все завелось без проблем.
Докер идеален, когда нужно передать правильно настроенное приложение заказчику. Вы делаете образ, заказчик запускает контейнер. Достаточно лишь, чтобы у вас и у заказчика были близкие версии Докера.
Разработка с Докером напоминает идею виртуальной машины с байткодом. Как Джава – однажды скомпилированное работает везде, был бы только рантайм. Преимущество в том, что теперь речь идет не только о коде, а об утилитах, библиотеках, словом, окружении в целом. Стало возможным сделать Линукс-среду кросплатформенной!
Докер содержит систему в чистоте. Работая нативно, мы ставим сотни и тысячи пакетов, которые оседают в системе как ил. Они занимают место на диске, что особо важно для небольших SSD. Пакеты вызывают коллизии – три версии Руби, два Питона, разные Ноды.
В случае с Докером все файлы ложатся в образ – единый файл. Удалив его, мы достоверно вернем систему в то состояние, в котором она была до билда.
Начав работать на свежей системе, я поставил только Гит и Емакс. База, Редис, различные Питоны, Кложа с Джавой крутятся в Докере. Система ничего о них не знает.
Бытует ошибочное мнение, что Докер – средство вируализации. Это не так. Докер не создает виртуальную машину как это делают Виртуал-Бокс и аналоги. Он запускает процесс через Cgroups с ограничениями в ресурсах и файловой системе.
На Маке и Винде Докер эмулируется через виртуалку. Вы запускаете терминал Докера, а на самом деле проваливаетесь в Виртуал-Бокс, где работает нативный Докер. Но на Маке он тоже скоро будет нативный, уже есть бета-версия.
По этой причине я смеюсь над Микрософтом, которые добавили ps
и grep
в 10
Виноуз. Кому это нужно? В сети тысячи версий юникс-утилит, скомпилированных под
Винду. Скорей делайте нативный Докер!
Докер может стать принципиально иным способом распространения приложений. Вместо того, чтобы собирать бинарник, мы будем делать образ. По клику мышкой Докер будет запускать из него образ, как обычное приложение.
Докер – это отсутствие состояния. Контейнер может удалить все файлы внутри себя, нагадить в конфиги и аварийно завершиться. Но стоит запустить контейнер из образа, и мы вновь в прежнем состоянии: файлы, конфиги на месте. Иммутабельность – это круто.
Докер отлично подходит для билдов всего и вся. Собрать проект, который требует миллион пакетов и библиотек – милое дело.
Докер – это еще и пакетный менеджер. В экосистеме докера выделяют образы и контейнеры. Образ – декларативное описание того, каким будет будущий контейнер. Контейнер – результат запуска из образа. Выражаясь языком ООП, образ это класс, а контейнер – экземпляр.
Существует официальный хаб образов. Оттуда можно скачать образ ЛЮБОЙ версии Питона, Руби, Постгреса и т.д. Помните, какой это геморрой – на систему с Питоном 2.7 ставить 3.4? Или проверить баг на старой версии БД? Теперь c этим покончено.
Хранить приватные образы в хабе Докера разрешено за деньги. Альтернативой может стать официальный сервис Амазона ECR, где все операции над образами бесплатны.
Докер можно использовать и в продакшене для запуска приложений. На работе мы подключили Докер к Дженкинсу для билдов и прогона тестов. Позже мы выкатили приложение на Питоне в Амазон. Оно было разбито на 4 образа. Каждый образ представлял собой микросервис и мог быть заменен независимо от других.
Теперь рассмотрим реальный пример применения Докера.
Бложик, что вы сейчас читаете, работает на движке Jekyll. Это утилита
на Руби, которая собирает из маркдаун-файлов статический сайт на голом
HTML. Jekyll
нативно поддерживается Гитхабом – стоит сделать комит, и твой
сайт сам обновляется.
Однако, прожде чем комитить, хочется посмотреть вживую, как выглядит пост. Для
этого я ставлю движок и запускаю локальный сервер. При этом Jekyll
тянет за
собой вагон зависимых пакетов и просит dev-библиотеки для компиляции сишных
либ. Тут нам и поможет Докер.
Установка Докера подробно описана на официальном сайте под каждую операционку в отдельности.
В корне проекта создаем файлик Dockerfile
с содержимым:
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install -y jekyll bundler
RUN mkdir /blog
WORKDIR /blog
COPY Gemfile* ./
RUN bundle install
RUN rm Gemfile*
CMD ["jekyll", "serve"]
Разберемся построчно:
- Образ унаследован от официального образа Убунты версии 16.04.
- При создании образа обновить информацию о пакетах.
- Установить из пакетов
jekyll
(движок) иbundler
(установщик пакетов для Руби). - Создать внутри образа папку
blog
в корне. - Перейти в нее.
- Скопировать из хост-системы в образ файлы
Gemfile
иGemfile.lock
. Это аналог питонячьегоpip.requirements.txt
. - Поставить пакеты, специфичные для проекта.
- Удалить Gem-файлы.
- При запуске контейнера стартануть локальный сервер.
Собираем образ Make
-командой:
docker-build:
docker build -t blog .
Параметр -t <foo:bar>
задает имя и тег для образа.
Посмотрите, что показывает консоль при сборке:
The following additional packages will be installed:
ca-certificates cpp cpp-5 dbus file fontconfig-config fonts-dejavu-core
fonts-lato ifupdown iproute2 isc-dhcp-client isc-dhcp-common
javascript-common krb5-locales libasn1-8-heimdal libatm1 libauthen-sasl-perl
libbsd0 libcap-ng0 libdbus-1-3 libdns-export162 libdrm-amdgpu1 libdrm-intel1
libdrm-nouveau2 libdrm-radeon1 libdrm2 libedit2 libelf1
libencode-locale-perl libexpat1 libffi6 libfile-basedir-perl
libfile-desktopentry-perl libfile-listing-perl libfile-mimeinfo-perl
libfont-afm-perl libfontconfig1 libfontenc1 libfreetype6 libgdbm3
libgl1-mesa-dri libgl1-mesa-glx libglapi-mesa libglib2.0-0 libglib2.0-data
libgmp10 libgnutls30 libgssapi-krb5-2 libgssapi3-heimdal libhcrypto4-heimdal
libheimbase1-heimdal libheimntlm0-heimdal libhogweed4 libhtml-form-perl
libhtml-format-perl libhtml-parser-perl libhtml-tagset-perl
libhtml-tree-perl libhttp-cookies-perl libhttp-daemon-perl libhttp-date-perl
libhttp-message-perl libhttp-negotiate-perl libhx509-5-heimdal libice6
libicu55 libidn11 libio-html-perl libio-socket-ssl-perl
libipc-system-simple-perl libisc-export160 libisl15 libjs-coffeescript
libjs-jquery libk5crypto3 libkeyutils1 libkrb5-26-heimdal libkrb5-3
libkrb5support0 libldap-2.4-2 libllvm3.8 liblwp-mediatypes-perl
liblwp-protocol-https-perl libmagic1 libmailtools-perl libmnl0 libmpc3
libmpfr4 libmysqlclient20 libnet-dbus-perl libnet-http-perl
libnet-smtp-ssl-perl libnet-ssleay-perl libnettle6 libp11-kit0 libpciaccess0
libperl5.22 libpng12-0 libpq5 libpython-stdlib libpython2.7-minimal
libpython2.7-stdlib libroken18-heimdal libruby2.3 libsasl2-2
libsasl2-modules libsasl2-modules-db libsm6 libsqlite3-0 libssl1.0.0
libtasn1-6 libtext-iconv-perl libtie-ixhash-perl libtimedate-perl
libtxc-dxtn-s2tc0 liburi-perl libuv1 libwind0-heimdal libwww-perl
libwww-robotrules-perl libx11-6 libx11-data libx11-protocol-perl libx11-xcb1
libxau6 libxaw7 libxcb-dri2-0 libxcb-dri3-0 libxcb-glx0 libxcb-present0
libxcb-shape0 libxcb-sync1 libxcb1 libxcomposite1 libxcursor1 libxdamage1
libxdmcp6 libxext6 libxfixes3 libxft2 libxi6 libxinerama1 libxml-parser-perl
libxml-twig-perl libxml-xpathengine-perl libxml2 libxmu6 libxmuu1 libxpm4
libxrandr2 libxrender1 libxshmfence1 libxt6 libxtables11 libxtst6 libxv1
libxxf86dga1 libxxf86vm1 libyaml-0-2 mime-support mysql-common netbase
nodejs openssl perl perl-modules-5.22 python python-chardet python-minimal
python-pkg-resources python-pygments python2.7 python2.7-minimal rake rename
ruby ruby-afm ruby-ascii85 ruby-blankslate ruby-classifier-reborn
ruby-coderay ruby-coffee-script ruby-coffee-script-source ruby-colorator
ruby-did-you-mean ruby-execjs ruby-fast-stemmer ruby-ffi ruby-hashery
ruby-jekyll-coffeescript ruby-jekyll-feed ruby-jekyll-gist
ruby-jekyll-paginate ruby-jekyll-sass-converter ruby-jekyll-watch ruby-json
ruby-kramdown ruby-liquid ruby-listen ruby-mercenary ruby-mime-types
ruby-minitest ruby-multi-json ruby-mysql ruby-net-telnet ruby-oj
ruby-parslet ruby-pdf-core ruby-pdf-reader ruby-pg ruby-posix-spawn
ruby-power-assert ruby-prawn ruby-prawn-table ruby-pygments.rb
ruby-rb-inotify ruby-rc4 ruby-rdiscount ruby-redcarpet ruby-rouge
ruby-safe-yaml ruby-sass ruby-sequel ruby-sequel-pg ruby-stringex
ruby-test-unit ruby-toml ruby-ttfunk ruby-yajl ruby2.3 rubygems-integration
sgml-base shared-mime-info ucf unzip x11-common x11-utils x11-xserver-utils
xdg-user-dirs xdg-utils xml-core zip
Suggested packages:
cpp-doc gcc-5-locales dbus-user-session | dbus-x11 ppp rdnssd iproute2-doc
resolvconf avahi-autoipd isc-dhcp-client-ddns apparmor apache2 | lighttpd
| httpd libdigest-hmac-perl libgssapi-perl gnutls-bin krb5-doc krb5-user
libdata-dump-perl coffeescript libcrypt-ssleay-perl pciutils
libsasl2-modules-otp libsasl2-modules-ldap libsasl2-modules-sql
libsasl2-modules-gssapi-mit | libsasl2-modules-gssapi-heimdal
libauthen-ntlm-perl libunicode-map8-perl libunicode-string-perl
xml-twig-tools perl-doc libterm-readline-gnu-perl
| libterm-readline-perl-perl make python-doc python-tk python-setuptools
ttf-bitstream-vera python2.7-doc binutils binfmt-support ri ruby-dev
libjs-mathjax ruby-activesupport doc-base coderay fonts-arphic-gkai00mp
ruby-pdf-inspector ruby-prawn-doc ttf-dejavu-core bundler sgml-base-doc
mesa-utils nickle cairo-5c xorg-docs-core gvfs-bin debhelper
The following NEW packages will be installed:
ca-certificates cpp cpp-5 dbus file fontconfig-config fonts-dejavu-core
fonts-lato ifupdown iproute2 isc-dhcp-client isc-dhcp-common
javascript-common jekyll krb5-locales libasn1-8-heimdal libatm1
libauthen-sasl-perl libbsd0 libcap-ng0 libdbus-1-3 libdns-export162
libdrm-amdgpu1 libdrm-intel1 libdrm-nouveau2 libdrm-radeon1 libdrm2 libedit2
libelf1 libencode-locale-perl libexpat1 libffi6 libfile-basedir-perl
libfile-desktopentry-perl libfile-listing-perl libfile-mimeinfo-perl
libfont-afm-perl libfontconfig1 libfontenc1 libfreetype6 libgdbm3
libgl1-mesa-dri libgl1-mesa-glx libglapi-mesa libglib2.0-0 libglib2.0-data
libgmp10 libgnutls30 libgssapi-krb5-2 libgssapi3-heimdal libhcrypto4-heimdal
libheimbase1-heimdal libheimntlm0-heimdal libhogweed4 libhtml-form-perl
libhtml-format-perl libhtml-parser-perl libhtml-tagset-perl
libhtml-tree-perl libhttp-cookies-perl libhttp-daemon-perl libhttp-date-perl
libhttp-message-perl libhttp-negotiate-perl libhx509-5-heimdal libice6
libicu55 libidn11 libio-html-perl libio-socket-ssl-perl
libipc-system-simple-perl libisc-export160 libisl15 libjs-coffeescript
libjs-jquery libk5crypto3 libkeyutils1 libkrb5-26-heimdal libkrb5-3
libkrb5support0 libldap-2.4-2 libllvm3.8 liblwp-mediatypes-perl
liblwp-protocol-https-perl libmagic1 libmailtools-perl libmnl0 libmpc3
libmpfr4 libmysqlclient20 libnet-dbus-perl libnet-http-perl
libnet-smtp-ssl-perl libnet-ssleay-perl libnettle6 libp11-kit0 libpciaccess0
libperl5.22 libpng12-0 libpq5 libpython-stdlib libpython2.7-minimal
libpython2.7-stdlib libroken18-heimdal libruby2.3 libsasl2-2
libsasl2-modules libsasl2-modules-db libsm6 libsqlite3-0 libssl1.0.0
libtasn1-6 libtext-iconv-perl libtie-ixhash-perl libtimedate-perl
libtxc-dxtn-s2tc0 liburi-perl libuv1 libwind0-heimdal libwww-perl
libwww-robotrules-perl libx11-6 libx11-data libx11-protocol-perl libx11-xcb1
libxau6 libxaw7 libxcb-dri2-0 libxcb-dri3-0 libxcb-glx0 libxcb-present0
libxcb-shape0 libxcb-sync1 libxcb1 libxcomposite1 libxcursor1 libxdamage1
libxdmcp6 libxext6 libxfixes3 libxft2 libxi6 libxinerama1 libxml-parser-perl
libxml-twig-perl libxml-xpathengine-perl libxml2 libxmu6 libxmuu1 libxpm4
libxrandr2 libxrender1 libxshmfence1 libxt6 libxtables11 libxtst6 libxv1
libxxf86dga1 libxxf86vm1 libyaml-0-2 mime-support mysql-common netbase
nodejs openssl perl perl-modules-5.22 python python-chardet python-minimal
python-pkg-resources python-pygments python2.7 python2.7-minimal rake rename
ruby ruby-afm ruby-ascii85 ruby-blankslate ruby-classifier-reborn
ruby-coderay ruby-coffee-script ruby-coffee-script-source ruby-colorator
ruby-did-you-mean ruby-execjs ruby-fast-stemmer ruby-ffi ruby-hashery
ruby-jekyll-coffeescript ruby-jekyll-feed ruby-jekyll-gist
ruby-jekyll-paginate ruby-jekyll-sass-converter ruby-jekyll-watch ruby-json
ruby-kramdown ruby-liquid ruby-listen ruby-mercenary ruby-mime-types
ruby-minitest ruby-multi-json ruby-mysql ruby-net-telnet ruby-oj
ruby-parslet ruby-pdf-core ruby-pdf-reader ruby-pg ruby-posix-spawn
ruby-power-assert ruby-prawn ruby-prawn-table ruby-pygments.rb
ruby-rb-inotify ruby-rc4 ruby-rdiscount ruby-redcarpet ruby-rouge
ruby-safe-yaml ruby-sass ruby-sequel ruby-sequel-pg ruby-stringex
ruby-test-unit ruby-toml ruby-ttfunk ruby-yajl ruby2.3 rubygems-integration
sgml-base shared-mime-info ucf unzip x11-common x11-utils x11-xserver-utils
xdg-user-dirs xdg-utils xml-core zip
А ю факин сириес? Миллион пакетов на установку одной тулзы! Представьте теперь, что ставите ее нативно, без Докера. Колоссальное захламление системы. Вы не сможете потом удалить эти пакеты. Не записывать же их на бумажку.
Это точка невозврата. Без Докера вы не сможете сделать систему такой, какой она была до установки.
Ок, образ собрался. Запускаем через Make
:
docker-run:
docker run -it --rm -p 4000:4000 -v $(CURDIR):/blog blog
Параметры:
-it
значит прикрепитьSTDIN
хост-системы к контейнеру. Иначе говоря, считывать пайпы или ввод с клавиатуры.--rm
– удалить контейнер по завершении процесса.-p 4000:4000
– пробросить 4000 порт хост-системы на аналогичный порт контейнера.-v $(CURDIR):/blog
– смонтировать текущую папку хост-системы в папку/blog
образа.
Make-переменная $(CURDIR)
означает текущую папку. К сожалению, при пробросе
томов нельзя указать относительный путь, а хардкодить в Гите – моветон.
Если внутри образа я шагну в папку /blog
, то увижу содержимое проекта.
При старте образа автоматом запустится сборка блога и сервер. Мне останется
открыть в браузере http://127.0.0.1:4000/
и посмотреть на результат. При
изменении маркдаун-файлов – не важно где, в образе или на хосте – блог
автоматом себя перестроит.
Посмотреть исходники для Make
и Докера можно в
репозитории блога. Возможно, в следующий раз я расскажу об
инструменте Docker Compose
. С помощью него мы создадим три контейнера –
приложение, база и Редис – и заставим идти в одной упряжке как единое целое.
Используете ли вы Докер, и если да, то как?
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter
Nick Lesnykh, 3rd Aug 2016, link
Докер, конечно, круто, и в контексте ненагруженных микросервисов он хорош. Но как обстоят дела, например, с энвайроментом для разработки веб-проекта, который по размеру чуть больше, чем микросервис или блог?
Лично я сравнительно недавно пробовал разнести энвайромент своего текущего проекта на два докер-образа. В первом у меня был nginx + uwsgi + лайтовый микросервис на питонячем микрофреймворке + postgres. Во втором - nginx + django + postgres, причем оба этих приложения активно "общались" между собой по HTTP. Все это докер-хозяйство крутилось под VirtualBox, хост система OS X.
Так вот, лаги при рендеринге страниц оказались жесточайшими: порядка 5-6 секунд на загрузку страницы на локалхосте (!) при 1-2 секундах на хост системе. Тюнинг параметров виртуалки, конфигов nginx, uwsgi ни к какому результату не привел.
В общем-то вопрос к автору, как обстоят дела сейчас с производительностью в этой экосистеме? Ваня, может быть ты проводил какие-то тесты, чтобы сравнить производительность? Насколько, на твой взгляд, изолированность контейнера оправдывает потерю перфоманса?
Ivan Grishaev, 3rd Aug 2016, link , parent
Коля, привет!
Докер используют и нагруженные системы, вот посмотри кастомеров: https://www.docker.com/cust...
Проблема медленной работы мне видится в следующем.
1) Виртуалка работает хуже, чем нативный Линкус.
2) Возможно, в твоей версии докера заданы дефолтные лимиты на cpu, io и так далее. Проверь в документации.
3) Неправильно пихать в один образ разные сущности. Ты должен разнести приложение, базу, кеш и энджинкс по разным образам и запускать через композер.
Ну и вообще, надо смотреть локально.
По сети ходят данные, что нативный cgroups просаживает метрики на 5% в среднем по больнице. Вот большой репорт от IBM: http://domino.research.ibm....
В нашем приложении, что в Амазоне, каки-то особых задержек в сети или файлах не обнаружено.
Nick Lesnykh, 3rd Aug 2016, link , parent
Как мне кажется, основной боттлнек в моем случае - это виртуалка. Однако, не стоит полагаться на то, что на OS X будет "нативный" докер: все-таки там будет другой менеджер для виртуализации xhyve (https://github.com/mist64/x... вместо VirtualBox. Поможет ли это решить проблему или нет, покажет практика.
Когда речь идет о каком-то production/staging энвайроменте, то там профит от использования докера очевиден. Но для девелопмента я бы не стал спешить: на том же OS X brew + virtualenvwrapper + nodeenv решают большинство проблем.
Поясню мысль. brew ставит все пакеты в /usr/local и это не конфликтует с системными либами, в случае чего можно полностью грохнуть эту директорию, и это не отразится на работе OS X. virtualenv позволяет изолировать интерпретатор питона с его библиотеками, для ruby что-то тоже есть аналогичное. nodeenv интегрируется с уже созданным виртуальным окружением питона, позволяя при активации последнего переключаться на необходимый интерпретатор nodejs с уже установленными зависимостями. При этом проблем, что либа из одного проекта перетерла другую, не возникает.
Подчеркну, что вышесказанное на основе личного мнения и опыта. Докер, несомненно, интересный инструмент, привносящий, в том числе, и некий "вау-эффект". Но рассматривать его как панацею я бы не стал: традиционный подход при настройке рабочего энвайромента в сочетании с прямыми руками работает хорошо.
Ivan Grishaev, 3rd Aug 2016, link , parent
На мой взгляд, преимущество докера прежде всего в разработке. Многие компании начинают использовать его для билдов и тестов, а в прод вводят далеко не сразу. Так было и у нас.
Понимаешь, это дико круто, когда один участник команды написал Докерфайл на Линуксе, а ты качнул на Маке и поднял проект одной командой.
Например, ты похерил ноут. Чтобы поднять проект с нуля на Линуксе, у тебя уйдет день. Потому что разные требования к дев-библиотекам. Скажем, чтобы поставить nodeenv, тебе потребуется g++, чтобы скомпилить драйвер постгреса psycopg2 потребуется postgresql-dev, и так далее.
Знаешь, какой облом, когда десять минут выкачиваешь пакеты, а потом ошибка сборки из-за того что нет либы? Так вот в докере это все разруливается.
Я вовсе не форсю Докер как панацею от всего. Но своим появлением он ставит разработку на другой уровень.
username21312321412, 19th Nov 2017, link
А про Docker Compose статья будет, Иван?
Ivan Grishaev, 20th Nov 2017, link , parent
Компосзером я почти не пользуюсь. Он скорее для продакшена.