FreeBSD virtual environment management and repository

2020-10 upd: we reached the first fundraising goal and rented a server in Hetzner for development! Thank you for donating !

Использование Docker на FreeBSD

Вступление

Решения на базе Docker достаточно прочно вошли в жизнь многих IT компаний. Более того, стали прослеживаться тенденции к Docker-lock-in ситуациям, когда компании полностью строят инфраструктуру с использованием Docker. У этой технологии, как и у любой другой, есть плюсы и минусы, однако факт остается фактом - в сегодняшнем мире Docker очень популярный - даже несмотря на то, что количество сил на его поддержку и обслуживание стали превышать силы, которые необходимо потратить инженерам на решение тех задач, которые изначально должен был решать Docker, но без Docker. Инженеры больше не тратят свои силы и время на реализацию методов доставки контента - сейчас это делает Docker, однако все силы и время инженеров теперь тратятся на сопровождение Docker и Docker-изацию. Спрос на это растет и у некоторых пользователей FreeBSD, уставших от простоты/KISS решений, и часто в BSD-мире звучит один и тот же вопрос: "Когда и мы наконец сможем начать все свое время тратить на решение проблем, вызванной применением Docker, ведь это так увлекательно!" ;-)

Как результат этих потребностей, даже такие серъезные FreeBSD-based решения как FreeNAS неуклюже пытаются дать своим пользователям Docker, полностью заменив им родную реализацию jail контейнеров. Например в этом сообщении, разработчики FreeNAS объясняют причины прекращения развития jail-based окружений и аргументируют переход на Docker. В первую очередь тем, что существует огромное количество Docker-based сервисов, которые вы можете просто взять и использовать. Однако надо понимать, что подобный шаг - это больше коммерческий ход со стороны компании IXSystem, которая продает NAS appliance и которая идет на поводу потенциальных потребителей, делая попытку привлечь их внимание. Но в реальности, технически эффективность такого решения крайне невелика и возможность применения очень ограничена. Почему?

Надо сказать, что Docker концентрируется и реализован с использованием Linux-only специфичных технологий - он базируется на многих функционалах ядра Linux, таких как cgroups и iptables и запускает контейнера с Linux окружением.

Иными словами - если вы хотите запускать production на Docker - вам ничего другое не подойдет кроме Linux, поскольку официально проект разрабатывается и рассчитан на Linux экосистему. Да, вы можете запустить Docker через прослойку, как это делают на платформах Windows или MacOS, но будьте готовы потерять при этом часть функциональности, гибкости и скорости в работе такого решения. Текущая реализация запуска Docker на MacOS базируется на гипервизоре xhyve (который в свою очередь, основан на наработках bhyve), но вы никогда не будете запускать большую production инфраструктуру в докерах, работающих на MacOS или Windows, или FreeNAS - в данных ситуациях, гипервизоры (хоть они и быстрые) - это ненужная прослойка, которая ничем не помогает инфраструктуре, а наоборот, только усложняет ее. Поэтому, подобные варианты нужно рассматривать только как возможность для разработчиков и тестировщиков проверить сервис или код, обойдясь ресурсами только своей локальной рабочей машины.

Из основных ограничений это, конечно, накладные ресурсы на гипервизор. Вам необходимо предварительно сконфигурировать bhyve-окружение, выдав определенное количество ресурсов (ядер, памяти), в рамках которых вы и будете запускать Docker. Рано или поздно, вы встретите проблему исчерпания ресурсов Docker внутри bhyve (или заметите, что это плохо масштабируется на виртуальных ядрах) и вам потребуется запустить еще один bhyve-based гипервизор для Docker. В этом случае, управлять окружениями в разных гипервизорах станет нелегко. Другими словами, подобные реализации как в MacOS и FreeNAS подходят только для игр и для локальной разработки, когда вы запускаете 2-5 докеров, чтобы что-то протестировать, но не более того - и это не подходит для использования в боевых условиях и больших инсталляциях. И если MacOS и Windows - это прекрасный выбор в качестве рабочей машины, за которой вы можете заниматься разработкой и на ней же запустить Docker, мало кто предпочтет установить на свою рабочую desktop-систему NAS.

По этим причинам, мы не планируем в ближайшее время реализовывать поддержку Docker в CBSD/ClonOS: если вы делаете что-то и строите production инсталляции на базе FreeBSD, для минимизации рисков, потерь в быстродействии и гибкости, мы рекомендуем вам пользоваться родным FreeBSD стеком: это jail, bhyve и xen. Если вам нужен Docker в большом окружении - используйте Linux в качестве хостера. Если вы используете FreeBSD как Desktop-OS и вам нужно протестировать сервис или вы разрабатываете код - вы можете достаточно легко сконфигурировать аналогичную MacOS схему запуска докер - используя bhyve в качестве docker-сервера и docker-клиент на FreeBSD. И именно на такое, локальное, применение ориентирована эта статья.

Существует неофициальный порт docker-freebsd, однако с точки зрения авторов статьи, это самый плохой вариант, поскольку здесь нет никакой официальной поддержки со стороны разработчиков Docker; Docker никогда не рассчитывался на запуск контейнеров через jail, чем сильно теряет в общем функционале. Кроме этого, порт ориентирован на Linuxator - прослойку в FreeBSD, пермаментно находящаяся в любой момент времени в состоянии "частично работает", поскольку пытается эмулировать самый минимальный набор системных вызовов ядра Linux, но догнать современное развитие ядра Linux для FreeBSD linuxator очень трудно, это постоянная игра в догонялки. Поэтому, вы можете потратить очень много времени, встретив неожиданное поведение приложения внутри docker-freebsd, которое вызвано никем не тестируемой, нигде не применяемой и не ориентированной на такой метод запуска Docker окружений в наполовину реализованной эмуляцией Linux.

Поэтому создадим реализацию, аналогичную MacOS/xhyve, но на FreeBSD/bhyve.

Использование Docker с FreeBSD через bhyve

Далее по шагам написана инструкция для тех, кто хочет запускать Docker, работая в FreeBSD. Здесь не рассмотрена проблема общей файловой системы для /var/lib/docker, поскольку для локальных эксперементов, файловой системы bhyve гостя вполне достаточно. Если же вы хотите использовать несколько bhyve для Docker на одном хостере и запускаете большое количество объемных Docker-образов, мы рекомендуем вам посмотреть на Docker docker-volume-netshare плагин, с помощью которого вы можете создавать volume на CIFS/NFS, либо на проект 9p и virtio-9p в bhyve.

  • 1) Нам подходит bhyve с любым удобным для вас Linux, в котором может работать Docker. Создавайте виртуальную машину с достаточным количеством ресурсов для запуска в ней Docker окружений.

Для любителей минимилизации, существует проект Boot2Docker, который представляет из себя минимальную инсталляцию Linux окружения для запуска Docker. К сожалению, на момент написания статьи этот образ не поддерживает UEFI режим, поэтому мы предпочли устанавливать Linux Ubuntu 16.04 и дальнейшие инструкции ориентированы на него.

  • 2) Переведем Linux в режим работы через bridge, поскольку через него же будем конфигурировать сеть запускаемых контейнеров.

Такой режим работы нам необходим, если мы хотим работать с контейнерами напрямую из сети FreeBSD хостера, а не только внутри bhyve. Для этого, установим внутри Linux гостя пакет для работы с bridge:

% apt-get install bridge-utils

В /etc/network/interfaces перепишем конфигурацию для физического интерфейса, поместив его в br0-бридж.

В нашем случае:

  • * физический интерфейс bhyve: enp0s4
  • * IP адрес bhyve c Docker: 192.168.0.233
  • * Сеть, в которой будем запускать Docker и которая доступна на FreeBSD хостере: 192.168.0.0/24
  • * Шлюз сети: 192.168.0.1

Тогда, полная конфигурация /etc/network/interfaces может выглядеть следующим образом:

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback


iface enp0s4 inet manual

allow-hotplug enp0s4

auto br0
iface br0 inet static
        address 192.168.0.233
        netmask 255.255.255.0
        gateway 192.168.0.1
        dns-nameservers 8.8.8.8
        dns-search example.com
        bridge_ports enp0s4
        bridge_fd 9
        bridge_hello 2
        bridge_maxage 12
        bridge_stp off
  • 3) Перезагрузим bhyve для применения новых сетевых настроек и установим docker:
apt-get install docker.io
  • 4) Через аргументы, сконфигурируем Docker на сеть с bridge по-умолчанию, с соответствующими параметрами сети.

А также, добавим через ключ -H порт TCP, через который будет взаимодействовать с демоном Docker внешний клиент ( FreeBSD в нашем случае ). Для этого, откроем файл /etc/defaults/docker и допишем строчку:

DOCKER_OPTS="-H tcp://0.0.0.0:2375 --bridge=br0 --fixed-cidr=192.168.0.0/24 --default-gateway=192.168.0.1 -H unix:///var/run/docker.sock"
  • 5) На FreeBSD устанавливаем клиент Docker:
% pkg install sysutils/docker

Для текущей сессии экспортируем корректное значение DOCKER_HOST:

% setenv DOCKER_HOST tcp://192.168.0.233:2375

Либо прописываем это глобально в файлах ~/.cshrc или /etc/profile or /etc/csh.cshrc

На этом, конфигурация гипервизора с bhyve закончена. Проверяем:

На FreeBSD:

% uname -a
FreeBSD gizmo.my.domain 12.0-CURRENT FreeBSD 12.0-CURRENT #5 r315987: Sun Mar 26 20:00:24 MSK 2017 root@gizmo.my.domain:/usr/obj/usr/jails/src/src_12/src/sys/CBSD  amd64

Скачаем образ ubuntu:

% docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
d54efb8db41d: Pull complete 
f8b845f45a87: Pull complete 
e8db7bf7c39f: Pull complete 
9654c40e9079: Pull complete 
6d9ef359eaaa: Pull complete 
Digest: sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535
Status: Downloaded newer image for ubuntu:latest

Запустим контейнер из образа ubuntu:

% docker run -it ubuntu
root@de8c2a012278:/# uname -a
Linux de8c2a012278 4.4.0-71-generic #92-Ubuntu SMP Fri Mar 24 12:59:01 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
root@de8c2a012278:/#

Смотрим на выданный IP адрес контейнера:

% docker inspect de8c2a012278 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "192.168.0.2",
                    "IPAddress": "192.168.0.2",

Пигнуем:

%# ping -c2 192.168.0.2
PING 192.168.0.2 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: icmp_seq=0 ttl=64 time=0.055 ms
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.059 ms

--- 192.168.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.055/0.057/0.059/0.002 ms

Как мы видим, все достаточно просто и для локальной разработки этого должно хватить. Успехов!