- PVSM.RU - https://www.pvsm.ru -

Собираем Docker контейнер с http сервером при помощи Gradle

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

Создаем новый Gradle проект [2] в IntelliJ IDEA.

image

image
Пустой проект создан, добавим класс 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 импорты не работают и классики сервера подсвечены красным.
image
Лечится это так — надо найти волшебную refresh кнопку и нажать ее — Synchronizing Changes in Gradle Project and IntelliJ IDEA Project [3]

Теперь запустим приложение локально, для этого используем плагин 'application' для Gradle [4].

В build.gradle добавляем строки

apply plugin: 'application'
mainClassName = 'ru.test.HabrWebServer'

Собираем и устанавливаем приложение

 ./gradlew installDist

Переходим в папку /habrServer/build/install/habrServer/bin
Запускаем приложение

./habrServer

Открываем браузер, сервер отвечает, но не тем что ждем.
image
Оказывается при создании сервера метод 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 [5].
После установки 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 [6]

Окончательный вариант 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

image
Запускаем!
image
Открываем в браузере.
image

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

Спасибо за внимание, надеюсь статья была кому то полезна.

Автор: new_branch

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/199493

Ссылки в тексте:

[1] GrizzlyWebServer: https://grizzly.java.net/

[2] новый Gradle проект: https://www.jetbrains.com/help/idea/2016.2/creating-a-gradle-project.html

[3] Synchronizing Changes in Gradle Project and IntelliJ IDEA Project: https://www.jetbrains.com/help/idea/2016.2/synchronizing-changes-in-gradle-project-and-intellij-idea-project.html

[4] плагин 'application' для Gradle: https://docs.gradle.org/current/userguide/application_plugin.html

[5] установки docker: https://docs.docker.com/engine/installation/linux/ubuntulinux/

[6] bmuschko Gradle Docker plugin: https://github.com/bmuschko/gradle-docker-plugin

[7] Keep STDIN open even if not attached.: https://docs.docker.com/engine/reference/run/

[8] Источник: https://habrahabr.ru/post/312724/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best