Статьи

Начало работы: NuoDB и Docker

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

Однако то, в чем все не так уверены, это именно то, что это значит. Это особенно верно, если взглянуть на пересечение технологий нового поколения, разработанных вокруг оркестровки, независимости и масштаба по требованию. NuoDB — только один пример этого. Клиент-сервер и независимые временные службы могут естественно вписаться в контейнерную модель. Попытка содержать программное обеспечение мятежного характера (читай: любит масштабировать по требованию или запускать сразу в нескольких местах) сложнее думать.

Для тех из вас, кто обращает внимание, вы можете закатить глаза на эти последние тенденции. Понятие содержания приложения или определения какой-либо границы ресурса — старая идея. Такие технологии, как Solaris Zones или  BSD Jails , виртуализация через виртуальные машины или просто работа в среде с привязкой к среде, были с нами долгое время. Что, я думаю, вызвало интерес к Docker, в частности, это модель мастеринга, управления версиями и развертывания, которую они обернули вокруг таких вещей, как  LXC . Мнения о том, насколько она зрелая, сильно различаются, но это, безусловно, соответствует мышлению многих из нас сегодня о том, как предоставлять и управлять системами.

Опять же, не что-то новое, но все более отличный способ построить гибкую, отказоустойчивую инфраструктуру. Рассматриваемые через линзы, такие как Kubernetes или AWS Container Service (подробнее о них в отдельной публикации) контейнеры — это способ применения многопользовательского режима, ужесточения управления ресурсами и упрощения распределенного развертывания. Или, с точки зрения Apcera , контейнеры — это способ построения надежных моделей безопасности, основанных на политике. Учитывая эти разные взгляды и учитывая, что NuoDB разработан на основе распределенного мышления, которое уже использует SLA для управления автоматизированным управлением, что вы можете сделать, когда вы подключите эти подходы?

Это вопрос, который я часто задавал в последнее время, поэтому я провел несколько экспериментов за последние несколько недель. Несколько результатов отражены здесь в этом посте. Эти примеры заставят вас задуматься и, надеюсь, помогут вам попробовать NuoDB по-новому. Здесь основное внимание уделяется тому, как Docker помогает упростить развертывание, тестирование и масштабирование, а также сосредоточить внимание на эффективном управлении ресурсами. Итак, начнем с очень простого примера.

Один контейнер, готов к работе

NuoDB не имеет слишком много требований, что делает начало работы довольно простым. Допустим, вы хотите раскрутить контейнер, чтобы протестировать полностью автономную базу данных. Используя синтаксис Dockerfile, вот начальная точка сборки на Ubuntu (это будет работать и для других дистрибутивов):

FROM ubuntu:14.04
ENV nuodb_package nuodb_2.1.1.10_amd64.deb

RUN /usr/bin/apt-get update
RUN /usr/bin/apt-get -y install default-jre-headless supervisor

ADD $nuodb_package /tmp/
RUN /usr/bin/dpkg -i /tmp/$nuodb_package
RUN /bin/sed -ie 's/#domainPassword =/domainPassword = foo/' /opt/nuodb/etc/default.properties

ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf

Что тут происходит? Это набор шагов для создания образа, в котором все биты установлены с помощью команды по умолчанию для запуска при запуске. Поскольку NuoDB не кодирует пароль администратора сразу после установки, этот сценарий устанавливает его для начала работы. Это нормально для тестирования, но не настолько удовлетворительно для реального мира, так что смотрите ниже дополнительную информацию о динамической конфигурации во время выполнения.

В этом примере поддерживающая команда представляет собой пакет, называемый Supervisor. Это способ поддерживать работу контейнера, пока агент управления NuoDB все еще активен и является довольно распространенным шаблоном. Вот простой файл конфигурации, чтобы заставить это работать:

[supervisord]
nodaemon=true

[program:nuoagent]
user=nuodb
command=/opt/nuodb/etc/nuoagent start

Кстати, все примеры здесь доступны на github . Пойдите туда для полных сценариев, документации и последних версий. Если вы хотите внести свой вклад в свои собственные рецепты, это тоже самое место! В любом случае, если эти два файла сохранены как Dockerfile и supervisord.conf соответственно в каталоге вместе с установщиком NuoDB .deb, вы можете запустить docker для создания образа:

$ sudo docker build .
...
Step 0 : FROM ubuntu:14.04
...
Successfully built 1fa0b5af059f

Это оно! В конце вы увидите другой хэш-код, но в остальном все готово. Что вы могли бы сделать с этим изображением? Одно из очевидных применений — dev-test, поскольку это автономный рецепт и образ для раскрутки экземпляров по требованию или запуска экземпляров на серверах. Это повторяемый способ запустить NuoDB на вашем ноутбуке, на тестовой виртуальной машине или в общедоступном облачном хранилище. Убедитесь, что THP отключен на вашем хосте , затем запустите ваш контейнер следующим образом:

$ sudo docker run -P --net=host 1fa0b5af059f

Я объясню немного о сети в следующем разделе. Главное, на что нужно обратить внимание, это то, что вы делаете здесь — запускаете контейнер, в котором все его компоненты NuoDB доступны для внешнего мира, так что вы можете общаться с протоколами управления и SQL так же, как с любым сервером. Для многих вариантов использования это именно то, что вы хотите. Чего не хватает, так это возможности что-либо изменить в конфигурации. Например, пароль домена, диапазоны портов или любые существующие экземпляры NuoDB для сопоставления.

Быстрый ответ заключается в том, что Docker позволяет вам определять переменные среды во время выполнения, используя флаг -e. Включив простой скрипт-обертку или воспользовавшись преимуществами управления свойствами Java, можно легко подключить эти переменные к встроенной установке. В нашем репозитории github есть полные примеры этого . Когда вы складываете их, вы можете запускать несколько контейнеров на одном хосте, используя разные порты, вводить начальные учетные данные или вносить любые другие изменения конфигурации во время выполнения, которые вам нравятся. Просто.

Изоляционизм против сотрудничества

Одной из сильных сторон контейнеров является изоляция: приложение видит только себя и свои настройки ОС и не может случайно вмешиваться в другие процессы. На практике это не совсем верно (например, комментарий выше о настройках хоста для THP), но это довольно близко. Частью обеспечения этой иллюзии является сопоставление входящих и исходящих портов, чтобы вы могли (например) запускать несколько веб-серверов на одном хосте, где каждый сервер считает, что использует порт 80, но с внешней точки зрения каждый хост фактически использует отличная точка входа.

Это хорошо работает для независимых процессов, которые принимают соединения извне контейнера. NuoDB, однако, является одноранговой системой, где база данных и процессы управления должны обнаруживать и общаться напрямую друг с другом. Если я добавлю Transaction Engine в работающую базу данных, хост и порт будут сообщены существующим одноранговым процессам, чтобы каждый мог продолжать правильно общаться. Сегодня мы поддерживаем рекламу альтернативных адресов для обработки преобразования имени / адреса (та же проблема), поэтому мы ищем правильный способ сделать то же самое для портов. Ищите обсуждение этого в будущем. Тем не менее, это поднимает вопросы о роли одноранговых архитектур и динамической оркестровки, когда они играют вместе.

Today, the simplest way to get running with NuoDB is to let its peer processes talk directly with each other and expose the endpoints for queries and management. So, in the above example, two flags were used when running the container. The first is -P which tells the container to map ports numbers directly (port 80 inside the container is accessed as port 80 on the outside). The second is the —net=host setting which tells the container to flatten the interfaces if you’re running multiple containers on the same host. This way they’ll see which ports are already in-use and coordinate appropriately.

The way I see it, this is one of the interesting areas that hasn’t seen a lot of exploration in the world of containers: how do you maintain security & isolation while supporting a p2p model that is designed around self-discovery. I think it’s an increasingly important topic as networks move towards intelligence, policy and distribution. I would love to hear your thoughts and opinions!

Tenant management

The previous example was focused on one set of use-cases: making it easy to spin-up a full NuoDB installation, or multiple installations, on a host. That’s powerful for testing, deployment, expansion, etc. There are plenty of other good reasons, however, to put things inside a container so let’s consider one more example.

NuoDB has a simple model for isolated multi-tenancy. On a given host you can install our software and from a single point of management run multiple database processes that are supporting independent databases. We’re building automation into the product that drives process management from an SLA point of view. Even still, if you’ve got a bunch of processes running on the same host you want some guarantees that a given database’s process doesn’t interfere with what another database needs from the system resources. Sounds like a job for a container.

NuoDB also has a simple way to define what the agent actually runs when asked to start a Transaction Engine or Storage Manager. In place of the nuodb executable you can put a wrapper script that marshals up the provided input and uses it to invoke Docker. In this model, there’s a single NuoDB Agent running on a host, but all NuoDB database processes are running in their own, isolated containers. Because you don’t need a full NuoDB environment to make this work, the Dockerfile gets even simpler:

FROM ubuntu:vivid
ENV nuodb_package nuodb-2.2.0.linux.x86_64.tar.gz

ADD $nuodb_package /tmp/
RUN /bin/mv /tmp/nuo* /opt/nuodb

CMD /opt/nuodb/bin/nuodb --connect-key $key --agent-port $port --report-pid-as $pid

The documented version of this file and the wrapper script that makes the CMD work are available on github. Note that this example uses a feature that we’ll be rolling out later this month in our next release. That’s the —report-pid-as flag, which tells the NuoDB process to report a PID that’s meaningful outside the container. Without this, the multiple containers would all be reporting the same PID for their contained processes which causes the management tier to get confused since multiple processes appear to be on the same host with the same PID.

This recipe gives you a simple way to support multiple databases on the same host OS with resource allocation guarantees. Defining strict requirements on things like CPU or memory ensure that even under heavy load no one database process ruins it for everyone else. From a management point of view none of the NuoDB tools or interfaces change, and all the reporting mechanisms continue to run as normal so this is operational drop-in.

The ‘D’ in Docker does not stand for Durability

The goal in writing this was to showcase some of the ways that NuoDB can be used within containers. It was also to call-out some of the places where using containers doesn’t mesh well with other infrastructure. In that spirit, this post wouldn’t be complete without a discussion on data durability.

In all of the examples above, the NuoDB database processes were running in transient containers with transient filesystems. That’s a really good thing for many services that don’t need to write to disk. It enhances security, simplifies management and lowers cost. All good. For databases, however, this obviously isn’t such a good thing.

So what are the options? For typical databases you need to bypass the container model and get at a real filesystem. Docker does support filesystem access, with varying degrees of indirection and isolation. Obviously one problem with this approach is that it breaks the illusion of an independent environment. A bigger problem is that many environments don’t give you this option (e.g., Amazon EC2 Containers as of the time this post was written) and that’s arguably an increasing trend that makes a lot of sense.

So what else can you do? Some databases are moving in the direction of providing durability & availability through replication. The focus is less on making one disk really solid and more on keeping enough copies of the data that you could lose any host and keep running. I think that’s an interesting model. It’s not enough for an enterprise that needs to survive complete failure but it’s a cost-effective model for some applications. You can do this with NuoDB, though I wouldn’t typically recommend it.

Another approach is keep your data outside the container, but available through an interface that’s available from wherever a container gets spun-up. For instance, with NuoDB you can put data into S3 so there’s no need for a local, durable disk. Traditionally, databases are focused on the IO path as the optimization point so this isn’t possible but as database architectures evolve this model is starting to be possible, as with our system. This is one of the reasons that I think the NuoDB model is such a good fit for the cloud.

One more approach that’s possible with NuoDB is to run the storage peers directly on non-containerized hosts. In our terminology, run the TEs in containers and the SMs directly on their hosts. This gives you in-memory, on-demand scaling that’s exploiting a lot of the great stuff that containers are designed to give you. It also focuses the flexibility on in-memory and lets the durable hosts, which typically are less dynamic because they’re using pre-provisioned disk, run with different rules.

Bottom-line, viewing the world through containers makes you think about things differently. That’s especially true for durability. It takes a new view on data availability and durability and new data management models to really shine.

Take-aways and next steps

Like I said several times, this was just an introduction to key concepts. I wanted to share a couple of simple examples & recipes to help get you started. I also wanted to highlight some of the challenges that we’re looking at next. I strongly believe that as container models evolve they will need distributed, flexible services at the data layer to scale.

The jury is definitely «still out» on Docker. There are plenty of questions about its security capabilities and whether the mastering & management facilities hold up under real-world scrutiny. As I’ve sketched out here, I still have quesitons about how it fits with durable or peer systems. As a way to simplify testing and build repeatable environments, however, I have to say it’s a nice tool.

Going forward we’re continuing looking at the orchestration and security features that will make these models better tie together. Separately, I’m also exploring some new models around data management, tracking provenance and managing residency rules. These kinds of concerns require knowledge about where a given container is running, something that is usually hidden from contained applications, so this is another area where I expect to explore new ideas. We’d love your thoughts, feedback and collaboration in all these spaces! In the meantime, have fun playing with the recipes laid out here.