Статьи

Java и узел в микроконтейнерах с Docker

Java и узел в микроконтейнерах на Docker

В первой статье я представил способ создания микроконтейнеров, которые используют контейнеры томов для совместного использования сред выполнения и исполняемых файлов, чтобы фактические контейнеры приложений могли оставаться небольшими. Контейнеры основаны на BusyBox (progrium / busybox), а необходимые библиотеки символически связаны, так что исполняемые файлы работают в очень легкой среде.

В этой статье я расскажу о концепции среды выполнения Java, регистрации и обнаружения служб с помощью Consul .

Концепция основана на способности Docker использовать несколько контейнеров объема для каждого контейнера приложения. Это позволяет контейнеру иметь доступ к различным исполняемым файлам и обеспечивает специализированное минимальное время выполнения для конкретного приложения, такого как Jetty.

Я представляю пример, который запускает Consul для регистрации и обнаружения служб во всех контейнерах, приложение hello world на основе Node.js, приложение hello world на основе Java на Jetty и cAdvisor для мониторинга контейнеров. Настройка может быть запущена с использованием сети на основе Weave со статическими внутренними IP-адресами или без нее.

Концепция состоит из трех частей:

  •  Файловые ящики, содержащие исполняемые файлы и среды выполнения. Исходный код можно найти на github по адресу https://github.com/SirIle/fileboxes .
  •  Микробоксы настраивают контейнеры приложений со всем связанным на место. Исходный код можно найти по адресу https://github.com/SirIle/microboxes .
  •  Микротестбоксы, содержащие актуальные приложения для демонстрации и запуска конфигурации, расширяющие микробоксы. Исходный код доступен по адресу https://github.com/SirIle/microtestboxes .

Это изображение показывает, как контейнеры приложения создаются путем расширения контейнеров. Он также показывает, какие файловые ящики необходимы для работы контейнеров приложений и какие порты доступны.

Соответствующие доверенные сборки docker.io были сделаны, поэтому для тестирования примеров не требуется локальная сборка. Контейнеры томов filebox необходимо создавать локально.

файлбоксы

Файловые ящики — это специальные контейнеры, которые определяют только тома. Они основаны на изображении tianon / true, которое содержит только версию команды true для ассемблера и вызывает минимальные издержки (125 байт) для контейнера файлов.

Basefilebox

Basefilebox содержит компоненты, которые предоставляются всем контейнерам. Для этой демонстрации это означает, что Консул для регистрации службы и обнаружения между контейнерами и runit, чтобы позволить Консулу и фактическому приложению запускаться одновременно в контейнере. Файл сборки копирует исполняемые файлы из образа в правильное место на томе.

консул

Поскольку Консул статически связан, его очень легко запустить. Основная идея заключается в том, чтобы агент Consul работал во всех контейнерах, чтобы обнаружение служб было тривиальным. Поскольку все контейнеры получают собственный IP либо от самого Docker, либо от внешнего механизма конфигурации сети, такого как Weave, службы могут оставаться запущенными в портах по умолчанию и использовать A-записи DNS для обнаружения служб. Это упрощает работу, поскольку нет необходимости запрашивать порты или дополнительную информацию, что можно сделать с помощью записей SRV или API на основе REST.

Сценарий оболочки используется для запуска Консула, который проверяет, определена ли переменная среды с именем DC. Если это так, сценарий ожидает, пока интерфейс Weave станет доступным, прежде чем получить IP-адрес этого интерфейса и объявить об этом с помощью Consul. Если это не так, IP-адрес контейнера получен из файла / etc / hosts.

Запустить его

Runit используется для управления несколькими процессами, выполняющимися в микроконтейнере. Он используется вместо supervisord, поскольку он очень мал и не требует дополнительных библиотек. Он состоит из двух команд: runv и runvdir.

Dockerfile для базового контейнера

FROM tianon/true
MAINTAINER Ilkka Anttonen version: 0.1
VOLUME /opt/files
COPY consul /opt/files/consul
COPY runsv /opt/files/runsv
COPY runsvdir /opt/files/runsvdir

Javafilebox

Oracle JRE 8

Среда выполнения Java довольно большая. Я искал разорванные версии, но большинство либо были не очень совместимы, либо были довольно старыми. В конце я понял, что совместное использование исполняемых файлов времени выполнения через контейнер томов может быть хорошим компромиссом между совместимостью и размером. Таким образом, контейнер тома создается один раз, и тогда все контейнеры, которым требуется среда выполнения Java на этом хосте, могут просто включить его. Без каких-либо дополнительных модификаций размер изображения составляет 168,5 МБ. Построение контейнера томов происходит очень быстро, но загрузка изображения займет некоторое время.

Dockerfile для контейнера Java 8

FROM tianon/true
MAINTAINER Ilkka Anttonen version: 0.1
VOLUME /opt/files/java
COPY jre1.8.0_25 /opt/files/java/jre

Nodejsfilebox

Исполняемый файл узла, который был собран на хосте Ubuntu, требует только библиотеки libstdc ++ для запуска. Этот nodefilebox включает в себя исполняемый файл и библиотеку. Я тоже думал о включении npm, но решил отказаться от него, так как контейнер должен быть средой выполнения, а не средой разработки. Таким образом, приложение со всеми зависимостями может быть построено на хосте (или позже в сборочном контейнере), а полученные файлы могут быть просто скопированы в контейнер приложения.

Dockerfile для объемного контейнера Nodejs

FROM tianon/true
MAINTAINER Ilkka Anttonen version: 0.1
VOLUME /opt/files/node
COPY libstdc++.so.6 /opt/files/node/libstdc++.so.6
COPY node /opt/files/node/node

Jettyfilebox

Этот файл содержит распакованный дистрибутив Jetty 9. Чтобы быть пригодным для использования, контейнер, выполняющий это, должен также использовать Javafilebox для предоставления JRE.

Dockerfile для Jettyfilebox

FROM tianon/true
MAINTAINER Ilkka Anttonen version: 0.1
VOLUME /opt/files/jetty
COPY jetty-distribution-9.2.4.v20141103 /opt/files/jetty/jetty

Построение объемных контейнеров из изображений

Контейнеры томов могут быть созданы с помощью следующих четырех команд:

docker run --name=basefilebox sirile/basefilebox
docker run --name=nodejsfilebox sirile/nodejsfilebox
docker run --name=java8filebox sirile/java8filebox
docker run --name=jettyfilebox sirile/jettyfilebox

Это также загружает изображения контейнера в локальный реестр. Изображение JRE довольно большое, остальные маленькие.

Microboxes

Это контейнеры, которые накладываются друг на друга для создания сред выполнения. Они связывают исполняемые файлы и библиотеки, которые контейнеры томов filebox предоставляют с файловой системой. Некоторые из них можно запускать самостоятельно, а не расширять приложениями для целей разработки и тестирования. Более подробную информацию об этом можно найти в соответствующих файлах README.md в Github.

Basebox

Базовый микроконтейнер, построенный поверх progrium / busybox. Не очень полезно само по себе, но служит основой для запуска других приложений. Включает символические ссылки для исполняемых файлов Consul и runit. Он также включает в себя базовую конфигурацию для runit, базовую конфигурацию консула, включая спецификацию порта DNS-сервера для обычного порта 53, чтобы его можно было использовать в качестве локального DNS-сервера. Контейнер запускает runit, который запускает как консул, так и другие процессы, контролируемые runit, которые можно настроить в / etc / service.

Все, что построено поверх Basebox, требует контейнера томов basefilebox для исполняемых файлов.

Dockerfile для Basebox

FROM progrium/busybox
MAINTAINER Ilkka Anttonen version: 0.1
RUN ln -s /opt/files/runsv /sbin/runsv
RUN ln -s /opt/files/runsvdir /sbin/runsvdir
RUN ln -s /opt/files/consul /usr/bin/consul
COPY consul /etc/service/consul/run
COPY config-consul.json /etc/consul.d/config-consul.json
CMD "/sbin/runsvdir" "/etc/service"

Consulbox

Микроконтейнер, работающий поверх sirile / basebox, который предлагает консул-сервер и консольный интерфейс. Он также содержит стартовый скрипт для сервера Consul, который может адаптироваться к сети, предоставляемой Weave, если была установлена ​​переменная среды DC. В противном случае он использует обычный IP-адрес контейнера.

Dockerfile для Consulbox

FROM progrium/busybox
MAINTAINER Ilkka Anttonen version: 0.1
COPY consul_ui /opt/consul_ui
COPY startConsul.sh /usr/bin/startConsul.sh
RUN ln -s /opt/files/consul /usr/bin/consul
CMD '/usr/bin/startConsul.sh'

Javabox

Микроконтейнер для Java-приложения. Версия JRE определяется контейнером тома, в котором находятся файлы.

Dockerfile для Javabox

FROM sirile/basebox
MAINTAINER Ilkka Anttonen version: 0.1
RUN ln -s /opt/files/java/jre/bin/java /usr/bin/java

Jettybox

Jettybox build file does nothing at the moment as there is no need to link Jetty executables to the file system because Jetty can be started directly with a Java -jar command. The container is still created and the actual application container is built on top of it.

Nodejsbox

A Microcontainer that can be used to run nodejs applications.

Dockerfile for Nodejsbox

FROM sirile/basebox
MAINTAINER Ilkka Anttonen version: 0.1
RUN ln -s /opt/files/node/node /usr/bin/node
RUN ln -s /opt/files/node/libstdc++.so.6 /lib/libstdc++.so.6

Microtestboxes

Jettytestbox

The Jetty example container contains just the example application and configuration plus the runit configuration to start Jetty. The container registers itself to Consul and tells that it provides a jettyhello-service. As the container is based on basebox, it automatically launches the runit defined in that Dockerfile.

Example war-file was downloaded from https://tomcat.apache.org/tomcat-6.0-doc/appdev/sample/. It contains a servlet and a jsp that can be used to demonstrate that the Jetty environment is fully functional.

Dockerfile for Jettytestbox

FROM sirile/jettybox<
MAINTAINER Ilkka Anttonen version: 0.1
COPY jetty /etc/service/jetty/run
COPY start.ini start.ini
COPY sample.war webapps/sample.war
COPY hello.json /etc/consul.d/hello.json

Nodejstestbox

A very simple nodejs and express based hello world application. The container registers itself to Consul and tells that it provides a nodehello-service.

Dockerfile for Nodejstestbox

FROM sirile/nodejsbox
MAINTAINER Ilkka Anttonen version: 0.1
COPY express.js express.js
COPY node_modules node_modules
COPY node /etc/service/node/run
COPY hello.json /etc/consul.d/hello.json

Running the example application

The github project https://github.com/SirIle/microtestboxes contains shell scripts which can be used to start the applications. As docker.io trusted builds have been defined, local building is not necessary.

Running with Weave provided networking

This can be started with the startTestboxes.sh script, which does the following:

sudo weave launch
sudo weave run 10.0.1.1/22 -p 8500:8500 -h consul -e DC=west --name consul --volumes-from basefilebox sirile/consulbox
sudo weave run 10.0.1.2/22 --volumes-from basefilebox --volumes-from nodejsfilebox -e DC=west --link consul:consul -h nodebox --name nodebox --dns 127.0.0.1 -p 80:80 sirile/nodejstestbox
sudo weave run 10.0.1.3/22 --volumes-from basefilebox --volumes-from java8filebox --volumes-from jettyfilebox -e DC=west --link consul:consul -h jettybox --name jettybox --dns 127.0.0.1 -p 81:80 sirile/jettytestbox
sudo weave run 10.0.1.4/22 --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8080:8080 --name=cadvisor google/cadvisor:latest

Running without Weave

Running the example without Weave networking can be done with the startTestboxesNoWeave.sh script which does:

docker run -p 8500:8500 -h consul -d --name consul --volumes-from basefilebox sirile/consulbox
docker run --volumes-from basefilebox -d --volumes-from nodejsfilebox --link consul:consul -h nodebox --name nodebox --dns 127.0.0.1 -p 80:80 sirile/nodejstestbox
docker run --volumes-from basefilebox -d --volumes-from java8filebox --volumes-from jettyfilebox --link consul:consul -h jettybox --name jettybox --dns 127.0.0.1 -p 81:80 sirile/jettytestbox
docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8080:8080 --detach=true --name=cadvisor google/cadvisor:latest

Testing the application using a browser

There are several applications available:

  • Consul UI can be accessed at port 8500 (for example http://10.10.10.30:8500)
  • Nodejs based test application runs at port 80 (http://10.10.10.30)
  • Jetty based test application runs at port 81 at context /sample (http://10.10.10.30:81/sample)
  • cAdvisor runs in port 8080 (http://10.10.10.30:8080)

Testing Consul based connectivity

Consul provided DNS can be used to connect to for example a back-end database that has registered itself as a service. For now there are three services registered: consul, nodehello and jettyhello. Calling between containers can be demonstrated with:

$ docker exec jettybox ping nodehello.service.consul
PING nodehello.service.consul (10.0.1.2): 56 data bytes
64 bytes from 10.0.1.2: seq=0 ttl=64 time=0.053 ms
64 bytes from 10.0.1.2: seq=1 ttl=64 time=0.207 ms

This shows that the Consul provided DNS resolves the address nodehello.service.consul to the Weave given internal IP address of nodebox container. Using Weave the container could well be running on a separate host, even on another service provider.

Conclusions

This is the list of the container images:

REPOSITORY             TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
sirile/jettytestbox    latest              b949a448fdac        5 hours ago         4.815 MB
sirile/nodejstestbox   latest              3e546fa4663d        5 hours ago         5.775 MB
sirile/consulbox       latest              38b40fd0b0a8        6 hours ago         5.782 MB
sirile/nodejsfilebox   latest              34b1ad35b57f        6 hours ago         9.819 MB
sirile/java8filebox    latest              d6b4cb7c3079        9 hours ago         168.5 MB
sirile/basefilebox     latest              f3a56a6cb60e        9 hours ago         15.78 MB
sirile/jettyfilebox    latest              f1a14fe1f665        9 hours ago         14.7 MB
google/cadvisor        0.6.1               5c12ab6c98aa        2 days ago          17.55 MB
google/cadvisor        latest              5c12ab6c98aa        2 days ago          17.55 MB
tianon/true            latest              d222574ccc4a        2 days ago          125 B
zettio/weave           git-3cb6f1b3cc4b    6e720cb2ccd9        3 days ago          10.83 MB
zettio/weave           latest              6e720cb2ccd9        3 days ago          10.83 MB

As can be observed, the application containers are very small, around 5 MB. The memory usage varies from about 25MB of the nodejs container to around 100 MB for the untuned Jetty container. Running services as Microservices with the runtime bundled with the service is a feasible thing to do, especially if the runtimes are shared with volume containers which helps to keep the application containers small. This leads to extremely fast build and startup times for the application containers.

Next steps

The goal of this exercise was to prove that stand alone Microservices are doable on Microcontainers with Docker. They are. They can provide a lot of flexibility in creating the runtime distributed environment especially when service registration and discovery is used along with advanced networking so that the services can reside on multiple nodes.

Next step could be to implement a real multi-tiered application on this base instead of just simple example applications. Suggestions are welcome.