Назовите любую технологическую компанию, и практически со 100% вероятностью окажется, что она заинтересована в продвижении контейнерных технологий. Google – конечно. IBM – да. Microsoft – разумеется. Вот и VMware не смогла пройти мимо.

Принимая во внимание тот факт, что в портфолио VMware имеется полный набор программного обеспечения для виртуализированных ЦОД, то интерес компании к популярной технологии ожидаем. Раньше системы на базе vSphere работали с контейнерами как с обычными виртуальными машинами, а это затрудняло управление и вело к проблемам с безопасностью.

«Теперь контейнеры станут полноправными элементами vSphere. Можно управлять как традиционными приложениями внутри виртуальных машин, так и приложениями следующего поколения на базе контейнеров. Обе технологии будут работать бок о бок на одной платформе», – говорит Кит Колберт (Kit Colbert), директор по облачным приложениям VMware.

Для поддержки новой технологии инженеры компании доработали виртуальную машину и вынесли часть ее функций на уровень гипервизора. Это позволило добавить поддержку контейнеров, сохранив им привычные качества виртуальной машины. Получается, что VMware представила возможность получить сразу все преимущества Linux-контейнеров и традиционной виртуализации на одной платформе vSphere.

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

В случае контейнеров ситуация совсем другая – в них можно размещать только приложение и необходимый минимум системных библиотек. На практике это означает, что при использовании контейнеров вы сможете запустить в два-три раза больше приложений на одном сервере. Кроме того, контейнеризация позволяет создавать портативное и целостное окружение для разработки, тестирования и последующего развертывания.

Кажется, что контейнеры укладывают на лопатки ВМ по всем статьям. И это было бы правдой, если бы сравнение на этом заканчивалось. Однако вопрос все же несколько сложнее и шире, чем простое «куда поместится больше приложений».

Первая и самая серьезная проблема, которая часто упускается из виду – это безопасность, кто бы что ни говорил. Дэниэл Уолш (Daniel Walsh), инженер по безопасности Red Hat, опубликовал статью «Пустые контейнеры». В ней рассматривается Docker, который в качестве основы использует libcontainers. Libcontainers взаимодействует с пятью пространствами: Process, Network, Mount, Hostname, Shared Memory.

При этом оказывается, что множество важных подсистем ядра Linux функционируют вне контейнера. К ним относятся различные устройства, SELinux, Cgroups и вся файловая система внутри /sys. Это значит, что при наличии у пользователя или приложения прав суперпользователя в контейнере операционная система может быть взломана.

Есть много способов обезопасить Docker и другие технологии контейнеризации. Например, можно смонтировать раздел /sys только для чтения, заставить процессы контейнеров работать с определенными разделами файловой системы и настроить сеть так, чтобы можно было подключаться лишь к определенным внутренним сегментам. Однако об этом придется позаботиться самостоятельно.

Вот три совета, которые дает Уолш по этому поводу:

  • Удалите привилегии;
  • Старайтесь не запускать службы с root-правами;
  • Всегда учитывайте, что root-права могут действовать и вне контейнера.

Но безопасность не является единственной проблемой. Еще существует проблема обеспечения качества. Допустим, что веб-сервер NGINX в состоянии поддерживать X контейнеров, но достаточно ли вам этого? Включен ли туда обновленный балансировщик TCP? Развернуть приложение в контейнере довольно легко, но если ошибиться с выбором компонентов, то вы просто потратите время впустую.

«Разделение системы на множество более мелких отдельных частей – это хорошая идея. Но это означает и то, что управлять придется большим числом элементов. Существует тонкая грань между декомпозицией и неконтролируемым разрастанием», – комментирует Роб Хиршфельд, генеральный директор RackN.

Помните, что вся суть контейнера заключается в запуске одного изолированного приложения. Большое количество задач на контейнер противоречит его концепции. Docker позволяет вам упаковать ваше приложение и все его зависимости в единый образ. Сложность заключается в том, что обычно вы «пакуете» гораздо больше ресурсов, чем нужно, а это приводит к разрастанию образа и большому размеру контейнера.

Большинство людей, которые начинают работу с Docker, используют официальные репозитории Docker, которые, к сожалению, как раз приводят к образу размером с небоскреб, когда он мог быть не больше скворечника. В них слишком много лишнего хлама. Стандартный размер образа Node занимает минимум 643 МБ – это очень много.

Здесь на помощь придут микроконтейнеры, которые содержат только библиотеки операционной системы и другие зависимости, требуемые для запуска приложения, и само приложение. Вы увидите, как приложение, которое занимало 643 МБ, будет занимать 29 МБ, что в 22 раза меньше.



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

На эту тему есть неплохое поясняющее видео:



Так как же создать эти микроконтейнеры? Лучше всего начать со scratch-образа, в котором нет абсолютно ничего. С его помощью вы можете создать самый маленький образ для своего приложения, если вам удастся скомпилировать проект в один бинарный файл без зависимостей, как это позволяет сделать Go. Например, этот образ содержит веб-приложение на Go и весит всего 5 МБ.

Однако не все пишут программы на Go, потому, вероятно, у вас будут несколько зависимостей, и scratch-образ вам не подойдет. Воспользуемся Alpine Linux. Образ Alpine весит всего 5 МБ. Поэтому для нашего простого приложения на Node Docker-файл примет следующий вид:

FROM alpine
RUN apk update && apk upgrade
RUN apk add nodejs

Теперь, чтобы добавить код к образу, нужно прописать еще пару дополнительных строк:

FROM alpine
RUN apk update && apk upgrade
RUN apk add nodejs

WORKDIR /app
ADD . /app
ENTRYPOINT [ "node", "server.js" ]

Код приложения и подробные инструкции можно найти здесь.

Такие образы Docker содержат только необходимые компоненты для работы приложения. Ничего лишнего. Таков мир микроконтейнеров.

Комментарии (10)


  1. ice2heart
    28.03.2016 10:56
    +2

    Не обязательно уменьшать контейнер до максимума, куда интереснее уменьшать количество стадий.


    1. grossws
      28.03.2016 13:20

      Тут тоже возможны перегибы, когда всё ужимается до 4-5 слоёв, с уникальной rootfs для каждого образа при том, что они различаются только какой-нибудь мелочёвкой типа конфигов или пользовательского приложения.


      1. RicoX
        28.03.2016 13:48
        +1

        Ага и при этом имеем ад с безопасностью и обновлениями, в теории и на конференциях все красиво, на практике, через пол года-год от создания всего это счастья в нижнем слое вылезает какая-либо критическая уязвимость (привет ssl) и мы получаем необходимость пересобрать все контейнеры, что на порядки больше чем при работе с виртуалками и контейнерами типа OVZ, обновления для типа "контейнер на приложение" — это полная пересборка контейнера, нельзя просто так взять и наложить системный патч на нижнем уровне, получив заплатку на верхнем, автоматизация наложения обновленей по листам безопасности превращается в страшный геморрой. Лично мое мнение, что неизменяемый контейнер на приложение — это тупиковая ветвь эволюции контейнеризации и мне кажется что больше перспектив у изменяемых линкованных контейнеров, когда можно наложить изменения на нижный уровень и получить их на верхнем, но это взгляд админа, программисту удобней собрать все в кучу, забить на безопасность и не подстраиваться под окружение пользователя, мне кажется из-за этого докер сейчас так популярен.


        1. ice2heart
          28.03.2016 13:49
          +2

          А для этого есть сервер с CI. Перестраиваем корневой образ и все остальные вслед за ним, не вижу проблем.


          1. RicoX
            28.03.2016 13:53
            -1

            На верхнем уровне мы можем иметь свое приложение собранное со старой "дырявой" библиотекой, так что пересборка корневого образа в данном случае не даст ничего, на нижнем уровне будет заплатка, верхний так и останется дырявым и следить за всеми библиотеками внутри каждого контейнера и приложения создает огромный пласт работы и на практике на безопасность просто забивают, обновили корневой образ, а то что выше, "так кто их помнит".


            1. ice2heart
              28.03.2016 13:59

              Пересборка нижнего уровня влечет за собой пересборку верхних уровней. Если же апдейта не было для верхнего уровня как спасет другая архитектура? Ну запатчим мы систему, а библиотека так же останется дырявой.


              1. RicoX
                28.03.2016 14:14

                Так в этом и основная сложность, имея архитектуру типа "контейнер на услугу" мы обновляем намного меньше контейнеров, чем имея "контейнер на приложение". Утрированный пример: имеем сервер со 100 контейнерами типа OVZ на которых крутятся веб сервера вида: nginx+mysql+php+node.js+ssh+ftp+exim+dovecot… еще 10 стандартных сервисов, итого в среднем 20 сервисов на контейнер, в случае изменяемых контейнеров мы в полуавтоматическом режиме накладываем апдейты безопасности для всех этих сервисов во всех контейнерах сразу, грубо говоря однострочник на ansible.
                А теперь представим что та же ситуация в докере, контейнеров уже не 100 а 100*20=2000 и каждый нам надо пересобрать соблюдая порядок уровней наследования, а затем ввести в продашкин поочередно заменяя страрые на новые.
                Чтоб не разводить дубль дискуссии, которая была не так давно, просмотрите аргументы за и против в этой теме:
                https://habrahabr.ru/post/277699/#comment_8780799


                1. ice2heart
                  28.03.2016 14:53

                  Угу, я согласен что рулить привычным способом для повторяющихся приложений на одной машине это не самый быстрый способ(например для хостеров). С другой стороны если построение контейнеров автоматизированно и требуется быстрый деплой то контейнер весьма неплохой способ, все же требует оптимизации под конкретную задачу. Обновить 9k+ контейнеров где обновился всего один слой не такая уж и долгая задача. И если нижние тяжелые слои одинаковые, они обновляются один раз, а дальше выкачиваются только нужное.
                  Я полностью согласен, что каждой задаче свой инструмент.


                1. ctacka
                  28.03.2016 18:36
                  +2

                  Ну подождите, вы так говорите, как будто вы эти контейнеры (образы) руками собираете. На деле вам надо просто заменить базовый образ, пересобрать все зависимые от него и переразлить контейнеры. То есть, по сути, поменять 1 докерфайл и запустить Docker CI, который пересоберет зависимые образы.


  1. bbk
    30.03.2016 11:14

    Всем привет. Подскажите какие продакшн-архитектуры чаще всего применяются в CoreOS для хранения Docker-образов?
    Подключаем физический диск и форматируем в LVM для хранения image образов?
    NFS я так понимаю не вариант.
    Спасибо.