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.