Привет Хабр!Возникла такая задача: Сделать простой web — сервер с минимальным числом зависимостей. При этом деплоиться он будет в виде docker контейнера. Для реализации самого сервера буду использовать GrizzlyWebServer. Для сборки Gradle c плагином для docker от Benjamin Muschko (bmuschko).
Такой выбор инструментов не случаен, я занимаюсь разработкой для android и мне ближе Java и Gradle чем что то другое. В этой статье хочу детально описать процесс от написания приложения до запуска в docker, возможные проблемы и их решение.
И так, начнем: сервер.
Создаем новый Gradle проект в IntelliJ IDEA.


Пустой проект создан, добавим класс HabrWebServer с методом main(). В build.gradle в dependencies добавляем строку.
compile group: 'org.glassfish.grizzly', name: 'grizzly-http-server', version: '2.3.28'
Код нашего простейшего сервера
public class HabrWebServer { private static final Logger LOGGER = Grizzly.logger(HabrWebServer.class); public static void main(String[] args) { // create a basic server that listens 0.0.0.0:8080 final HttpServer server = HttpServer.createSimpleServer(); final ServerConfiguration config = server.getServerConfiguration(); // Map the path, '/', to the Handler config.addHttpHandler(new HabrHandler(), "/"); try { server.start(); System.out.println("The server is running. \nPress enter to stop..."); System.in.read(); } catch (IOException ioe) { LOGGER.log(Level.SEVERE, ioe.toString(), ioe); } finally { server.shutdownNow(); } } private static class HabrHandler extends HttpHandler { @Override public void service(Request request, Response response) throws Exception { response.setContentType("text/plain"); response.getWriter().write("Hello Habrahabr!"); } } }
Открываем консоль в директории проекта набираем ./gradlew assemble — BUILD SUCCESSFUL.
Что то у нас уже собралось.
Но есть одна неприятная вещь, в ide импорты не работают и классики сервера подсвечены красным.

Лечится это так — надо найти волшебную refresh кнопку и нажать ее — Synchronizing Changes in Gradle Project and IntelliJ IDEA Project
Теперь запустим приложение локально, для этого используем плагин 'application' для Gradle.
В build.gradle добавляем строки:
apply plugin: 'application' mainClassName = 'ru.test.HabrWebServer'
Собираем и устанавливаем приложение:
./gradlew installDist
Переходим в папку /habrServer/build/install/habrServer/bin. Запускаем приложение:
./habrServer
Открываем браузер, сервер отвечает, но не тем что ждем.

Оказывается при создании сервера метод createSimpleServer() устанавливает первым обработчик который отдает статичный контент которого у нас нет, и до нашего обработчика дело не доходит. Меняем код создания сервера.
public static void main(String[] args) { final HttpServer server = createServer("0.0.0.0", 8080); ... } public static HttpServer createServer(String host, int port) { HttpServer server = new HttpServer(); NetworkListener listener = new NetworkListener("grizzly", host, new PortRange(port)); server.addListener(listener); return server; }
Осталось собрать приложение в виде docker контейнера.
Начнем с локальной установки docker.
После установки docker сервер слушает только на unix сокете. Разрешим локальный доступ по tcp.
В файле /lib/systemd/system/docker.service находим и правим строку:
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375
Не совсем правильно менять этот файл, так как oн изменяет настройки для всех пользователей, но в моем случае для тестов на моей машине подходит.
Чтобы собрать контейнер используем bmuschko Gradle Docker plugin
Окончательный вариант build.gradle
group 'habrServer' version '1.0-SNAPSHOT' apply plugin: 'java' apply plugin: 'application' apply plugin: 'com.bmuschko.docker-java-application' apply plugin: 'com.bmuschko.docker-remote-api' import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer mainClassName = 'ru.test.HabrWebServer' buildscript { repositories { jcenter() } dependencies { classpath 'com.bmuschko:gradle-docker-plugin:3.0.3' } } docker { url = 'http://127.0.0.1:2375' javaApplication { maintainer = 'Dmitry Barkalov "xxx@xxx.xxx"' ports = [8080] tag = 'habrwebserver' } } task createDocker(type: DockerCreateContainer) { dependsOn dockerBuildImage targetImageId { dockerBuildImage.getImageId() } portBindings = ['8080:8080'] } repositories { mavenCentral() jcenter() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' compile group: 'org.glassfish.grizzly', name: 'grizzly-http-server', version: '2.3.28' }
Собираем и устанавливаем контейнер:
./gradlew createDocker

Запускаем!

Открываем в браузере.

Осталась одна не решенная задача: приложение ждет в main потоке на System.in.read(); Поэтому docker запускаем с ключом -i. (Keep STDIN open even if not attached.) Т.е. stdin остается приатаченым к контейнеру. Делать приложение в виде демона скорее всего не нужно, докер сам умеет демонизировать. Надо убрать взаимодействие c stdin, и уметь останавливать приложение. Это предстоит еще узнать. Если кто то знает пишите в комментариях, буду благодарен.
Спасибо за внимание, надеюсь статья была кому то полезна.
Код проекта на github.
