Статьи

Docker для разработчиков Java: сборка на Docker

Эта статья является частью нашего курса Академии под названием Docker Tutorial для разработчиков Java .

В этом курсе мы предлагаем серию руководств, чтобы вы могли разрабатывать свои собственные приложения на основе Docker. Мы охватываем широкий спектр тем, от Docker через командную строку, до разработки, тестирования, развертывания и непрерывной интеграции. С нашими простыми учебными пособиями вы сможете запустить и запустить собственные проекты за минимальное время. Проверьте это здесь !

1. Введение

В течение первых нескольких частей руководства мы рассмотрели основы Docker и множество способов взаимодействия с ним. Настало время применить полученные знания в реальных проектах Java, начав обсуждение с темы о том, как Docker влияет на хорошо отлаженные процессы и практики сборки.

Честно говоря, цель этого раздела двоякая. Сначала мы рассмотрим, как существующие инструменты сборки, а именно Apache Maven и Gradle , помогают упаковывать приложения Java в контейнеры Docker . Во-вторых, мы будем продвигать эту идею еще дальше и узнаем, как мы могли бы использовать Docker для полной инкапсуляции конвейера сборки наших Java-приложений и создания окончательных образов Docker в конце.

2. Под увеличительным стеклом

Чтобы поэкспериментировать, мы собираемся разработать два простых (но, тем не менее, значимых) веб-приложения на Java, которые бы реализовывали и представляли API REST (ful) для управления задачами.

Первое приложение будет разработано поверх Spring Boot и Spring Webflux с использованием Gradle в качестве инструмента управления сборкой и зависимостями. Что касается версий, мы будем использовать последнюю 2.0.0.M6 Spring Boot 2.0.0.M6 , последнюю 2.0.0.M6 Spring Webflux 5.0.1 и последнюю версию Gradle 4.3 .

Второе приложение, хотя и функционально эквивалентное первому, будет разработано поверх другой популярной платформы Java, Dropwizard , на этот раз с использованием Apache Maven для управления сборкой и зависимостями. Что касается версий, мы собираемся представить последний выпуск 1.2.0 Dropwizard и последний выпуск 3.5.2 Apache Maven .

Как мы уже упоминали, оба приложения будут реализовывать и предоставлять API-интерфейсы REST (ful) для управления задачами, по сути, заключая в себе операции CRUD (создание, чтение, обновление и удаление).

1
2
3
4
GET     /tasks
POST    /tasks
DELETE  /tasks/{id}
GET     /tasks/{id}

Сама задача моделируется как постоянный объект, который будет управляться Hibernate ORM и храниться в базе данных отношений MySQL .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@Entity
@Table(name = "tasks")
public class Task {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;
     
    @Column(name = "title", nullable = false, length = 255)
    private String title;
     
    @Column(name = "description", nullable = true, columnDefinition = "TEXT")
    private String description;
     
    // Getters and setters are omitted
    ...
}

На этом сходство обоих приложений заканчивается, и каждое из них будет следовать своему идиоматическому пути развития.

3. Градл и Докер

Итак, все готово, давайте начнем путешествие с изучения того, что нужно для интеграции Docker в типичную сборку Gradle . Для этого подраздела вам понадобится установить Gradle 4.3 на ваш компьютер для разработки. Если у вас его еще нет, следуйте инструкциям по установке , выбрав любой предложенный метод, который вы предпочитаете.

Чтобы упаковать типичное приложение Spring Boot как образ Docker с помощью Gradle, нам просто нужно включить два дополнительных плагина в файл build.gradle :

Конвейер сборки в основном полагался бы на плагин Spring Boot Gradle для создания uber-jar (термин, который часто используется для описания техники создания единственного исполняемого JAR-архива приложения), который позже будет использоваться Palantir Docker Gradle для сборки образа Docker . Вот как выглядит определение сборки, файл build.gradle .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
buildscript {
    repositories {
        maven { url 'https://repo.spring.io/libs-milestone' }
    }
   
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.0.M6"
    }
}
 
plugins {
    id 'com.palantir.docker' version '0.13.0'
}
 
apply plugin: "org.springframework.boot"
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
 
sourceCompatibility = 1.8
targetCompatibility = 1.8
 
dependencies {
    compile("org.flywaydb:flyway-core:4.2.0")
    compile("org.springframework.boot:spring-boot-starter-webflux:2.0.0.M6")
    compile("org.springframework.boot:spring-boot-starter-data-jpa:2.0.0.M6")
    compile("org.springframework.boot:spring-boot-starter-actuator:2.0.0.M6")
    compile("mysql:mysql-connector-java:8.0.7-dmr")
}
 
repositories {
    maven {
        mavenCentral()
    }
}
 
springBoot {
    mainClassName = "com.javacodegeeks.spring.AppStarter"
}
 
jar {
    mainClassName = "com.javacodegeeks.spring.AppStarter"
    baseName = 'spring-boot-webapp '
    version = project.version
}
 
bootJar {
    baseName = 'spring-boot-webapp '
    version = project.version
}
 
docker {
    name "jcg/spring-boot-webapp:$project.version"
    tags 'latest'
    dependsOn build
    files bootJar
    dockerfile file('src/main/docker/Dockerfile')
    buildArgs([BUILD_VERSION: project.version])
}

Это на самом деле довольно просто, все мясо в основном находится в разделе build.gradle файла build.gradle . Вы также можете заметить, что мы используем наш собственный Dockerfile , src/main/docker/Dockerfile , чтобы предоставить Docker инструкции по созданию образа.

01
02
03
04
05
06
07
08
09
10
11
FROM openjdk:8-jdk-alpine
ARG BUILD_VERSION
 
ENV DB_HOST localhost
ENV DB_PORT 3306
 
ADD spring-boot-webapp-${BUILD_VERSION}.jar spring-boot-webapp.jar
 
EXPOSE 19900
 
ENTRYPOINT exec java $JAVA_OPTS -Ddb.host=$DB_HOST -Ddb.port=$DB_PORT -jar /spring-boot-webapp.jar

Действительно, так просто, как только можно. Обратите внимание, как мы используем инструкцию ARG (и настройку build.gradle файле build.gradle ) для передачи аргументов в изображение. В этом случае мы передаем версию проекта, чтобы найти окончательные артефакты сборки. Еще одна интересная деталь, на которую стоит обратить внимание, — это использование инструкций ENV для соединения хоста и порта экземпляра MySQL для подключения. И, как вы уже догадались, инструкция EXPOSE информирует Docker о том, что контейнер прослушивает порт 19900 во время выполнения.

Круто, а что дальше? Ну, нам просто нужно запустить нашу сборку Gradle , вот так:

1
2
3
4
> gradle clean docker dockerTag
...
BUILD SUCCESSFUL in 12s
15 actionable tasks: 14 executed, 1 up-to-date

Задача dockerTag самом деле не нужна, но из-за этой проблемы, о которой сообщалось в отношении плагина Palantir Docker Gradle, мы должны явно вызвать его, чтобы правильно dockerTag наше изображение. Давайте проверим, есть ли у нас изображение, доступное локально.

1
2
3
4
5
> docker image ls
REPOSITORY               TAG            IMAGE ID      CREATED             SIZE
jcg/spring-boot-webapp   0.0.1-SNAPSHOT 65057c7ae9ba  21 seconds ago      133MB
jcg/spring-boot-webapp   latest         65057c7ae9ba  21 seconds ago      133MB
...

Хорошо, новый образ есть прямо из духовки. Мы можем запустить его немедленно, используя инструмент командной строки docker , но сначала нам нужно, чтобы где-то был доступен контейнер MySQL . К счастью, мы делали это уже столько раз, что это нас не озадачит.

1
2
3
4
5
6
docker run --rm -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD='p$ssw0rd' \
  -e MYSQL_DATABASE=my_app_db \
  -e MYSQL_ROOT_HOST=% \
  mysql:8.0.2

Теперь мы готовы запустить наше приложение в виде контейнера Docker . Есть несколько способов, которыми мы могли бы ссылаться на контейнер MySQL , при этом определяемая пользователем сеть является предпочтительным вариантом. Для простых случаев, подобных нашему, мы можем просто обратиться к нему, назначив DB_HOST среды DB_HOST IP-адрес работающего контейнера MySQL , например:

1
2
3
4
5
docker run -d --rm \
  --name spring-boot-webapp \
  -p 19900:19900 \
  -e DB_HOST=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql` \
  jcg/spring-boot-webapp:0.0.1-SNAPSHOT

Сопоставив порт 19900 от контейнера к хосту, мы могли общаться с нашим приложением, получая доступ к его REST (ful) API из curl, используя localhost качестве имени хоста. Давайте сделаем это прямо сейчас.

01
02
03
04
05
06
07
08
09
10
11
$ curl -X POST http://localhost:19900/tasks \
   -d '[{"title": "Task #1", "description": "Sample Task"}]' \
   -H "Content-Type: application/json"
 
[
  {
    "id":1,
    "title":"Task #1",
    "description":"Sample Task"
  }
]
1
2
3
4
5
6
7
8
9
$ curl http://localhost:19900/tasks
 
[
  {
    "id":1,
    "title":"Task #1",
    "description":"Sample Task"
  }
]
1
2
3
4
5
6
7
$ curl http://localhost:19900/tasks/1
 
{
  "id":1,
  "title":"Task #1",
  "description":"Sample Task"
}

Под капотом много движущихся частей, например, автоматическая миграция базы данных с использованием Flyway и встроенная поддержка проверки работоспособности с помощью Spring Boot Actuator . Некоторые из них появятся в следующих разделах руководства, но посмотрите, насколько просто и естественно создать и упаковать свои приложения Spring Boot в виде образов Docker с помощью Gradle .

4. Gradle на докере

Оказывается, что создание образов Docker с помощью Gradle совсем не больно. Но все же, для того, чтобы Gradle был установлен в целевой системе вместе с JDK / JRE, требуется некоторая предварительная работа. Это может быть не проблема, скажем, для разработки, так как очень вероятно, что вы все равно (и многие другие) были бы установлены.

Однако в случае облачных развертываний или конвейеров CI / CD это может быть проблемой, что может повлечь за собой дополнительные затраты с точки зрения работы и / или обслуживания. Можем ли мы найти способ избавиться от таких издержек и полностью положиться на Docker ? Да, фактически мы можем, приняв многоэтапные сборки , одно из недавних дополнений в наборе функций Docker .

Если вам интересно, как это может помочь нам, вот идея. По сути, мы собираемся следовать обычной процедуре создания образа из Dockerfile . Но Dockerfile на самом деле будет содержать два определения изображений. Первый (основанный на одном из официальных образов Gradle ) инструктирует Docker запустить сборку Gradle нашего приложения Spring Boot . Второй выбрал бы двоичные файлы, созданные первым изображением, и создал окончательный образ Docker с нашим приложением Spring Boot, запечатанным внутри (так же, как мы делали раньше).

Возможно, лучше один раз увидеть, чем пытаться объяснить. Файл Dockerfile.build ниже иллюстрирует эту идею в действии, используя инструкции многоступенчатой ​​сборки .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
FROM gradle:4.3.0-jdk8-alpine
 
ADD src src
ADD build.gradle .
ADD gradle.properties .
 
RUN gradle build
 
FROM openjdk:8-jdk-alpine
ARG BUILD_VERSION
 
ENV DB_HOST localhost
ENV DB_PORT 3306
 
COPY --from=0 /home/gradle/build/libs/spring-boot-webapp-${BUILD_VERSION}.jar spring-boot-webapp.jar
 
EXPOSE 19900
 
ENTRYPOINT exec java $JAVA_OPTS -Ddb.host=$DB_HOST -Ddb.port=$DB_PORT -jar /spring-boot-webapp.jar

Первая часть определения Dockerfile описывает изображение на основе gradle:4.3.0-jdk8-alpine . Поскольку наш проект довольно мал, мы просто копируем все источники внутри образа и запускаем gradle build (эта команда будет выполняться Docker во время gradle build образа). Результатом сборки будет uber-jar, который мы openjdk:8-jdk-alpine в другое определение изображения, на этот раз на основе openjdk:8-jdk-alpine . Это составило бы наше окончательное изображение, которое мы могли бы создать, используя инструмент командной строки docker .

1
2
3
4
5
docker image build \
  --build-arg BUILD_VERSION=0.0.1-SNAPSHOT \
  -f Dockerfile.build \
  -t jcg/spring-boot-webapp:latest \
  -t jcg/spring-boot-webapp:0.0.1-SNAPSHOT .

После командного соревнования мы должны увидеть наше недавно испеченное изображение в списке доступных изображений Docker .

1
2
3
4
5
$ docker image ls
REPOSITORY               TAG            IMAGE ID       CREATED           SIZE
jcg/spring-boot-webapp   0.0.1-SNAPSHOT  02abf724da64  10 seconds ago    133MB
jcg/spring-boot-webapp   latest          02abf724da64  10 seconds ago    133MB
...

Многоуровневые сборки имеют большой потенциал, но даже для такого простого приложения, как наше, они заслуживают внимания.

5. Мавен и Докер

Давайте немного переключимся и посмотрим, как Apache Maven использует управление сборкой для приложений Dropwizard . Для этого подраздела вам потребуется установить Apache Maven 3.2.5 на компьютере разработчика (однако, если у вас уже установлен Apache Maven 3.2.1 или более поздней версии, вы можете просто придерживаться его).

Шаги, которые мы должны выполнить, в основном идентичны тем, что мы обсуждали для сборок Gradle , изменения в основном только в плагинах:

Плагин Maven Shade создает uber-jar, который позже будет использоваться плагином Spotify Docker Maven для создания образа Docker . Без суеты, давайте посмотрим на файл pom.xml .

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<project
  xmlns=http://maven.apache.org/POM/4.0.0
  xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.javacodegeeks</groupId>
  <artifactId>dropwizard-webapp</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
 
  <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
 
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.dropwizard</groupId>
        <artifactId>dropwizard-bom</artifactId>
        <version>1.2.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
 
  <dependencies>
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-core</artifactId>
    </dependency>
 
    <dependency>
      <groupId>io.dropwizard</groupId>
      <artifactId>dropwizard-hibernate</artifactId>
    </dependency>
         
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.7-dmr</version>
    </dependency>
 
    <dependency>
      <groupId>io.dropwizard.modules</groupId>
      <artifactId>dropwizard-flyway</artifactId>
      <version>1.2.0-1</version>
    </dependency>
         
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
    </dependency>
     
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.0.2</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>com.javacodegeeks.docker.AllApiApp</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <filters>
            <filter>
              <artifact>*:*</artifact>
              <excludes>
                <exclude>META-INF/*.SF</exclude>
                <exclude>META-INF/*.DSA</exclude>
                <exclude>META-INF/*.RSA</exclude>
              </excludes>
            </filter>
          </filters>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">                                 
                  <mainClass>com.javacodegeeks.dw.AppStarter</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>com.spotify</groupId>
        <artifactId>docker-maven-plugin</artifactId>
        <version>1.0.0</version>
        <configuration>
          <imageName>jcg/dropwizard-webapp:${project.version}</imageName>
          <dockerDirectory>src/main/docker</dockerDirectory>
          <resources>
            <resource>
              <targetPath>/</targetPath>
              <directory>${project.build.directory}</directory>
              <include>${project.build.finalName}.jar</include>
            </resource>
            <resource>
              <targetPath>/</targetPath>
              <directory>${project.basedir}</directory>
              <include>application.yml</include>
            </resource>
          </resources>
          <buildArgs>
            <BUILD_VERSION>${project.version}</BUILD_VERSION>
          </buildArgs>
          <imageTags>
            <tag>latest</tag>
          </imageTags>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Честно говоря, это выглядит значительно более многословно, чем сборка Gradle , но если мы на секунду представим, что все теги XML пропали, мы получим в основном идентичное определение, по крайней мере, в случае плагинов Docker . Dockerfile немного отличается, хотя:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
FROM openjdk:8-jdk-alpine
ARG BUILD_VERSION
 
ENV DB_HOST localhost
ENV DB_PORT 3306
 
ADD dropwizard-webapp-${BUILD_VERSION}.jar dropwizard-webapp.jar
ADD application.yml application.yml
ADD docker-entrypoint.sh docker-entrypoint.sh
RUN chmod a+x /docker-entrypoint.sh
 
EXPOSE 19900 19901
 
ENTRYPOINT ["/docker-entrypoint.sh"]

Из-за особенностей приложения Dropwizard , мы должны связать файл конфигурации, в нашем случае application.yml , вместе с приложением. Вместо того, чтобы выставлять только один порт 19900 , мы должны выставить другой, 19901 , для административных задач. И последнее, но не менее важное: мы предоставляем скрипт для инструкции ENTRYPOINT , docker-entrypoint.sh .

01
02
03
04
05
06
07
08
09
10
#!/bin/sh
 
set -e
java $JAVA_OPTS -DDB_HOST=$DB_HOST -DDB_PORT=$DB_PORT -jar /dropwizard-webapp.jar db migrate application.yml
 
if [ ! $? -ne 0 ]; then
  exec java $JAVA_OPTS -DDB_HOST=$DB_HOST -DDB_PORT=$DB_PORT -jar /dropwizard-webapp.jar server application.yml
fi
 
exec "$@"

Причиной добавления некоторой сложности здесь является то, что по умолчанию пакет дополнений Dropwizard Flyway не выполняет автоматическую миграцию схемы базы данных. Мы могли бы обойти это, но самый простой способ — запустить команду db migrate перед запуском приложения Dropwizard . Это именно то, что мы делаем внутри сценария оболочки выше. Теперь пришло время запустить сборку!

01
02
03
04
05
06
07
08
09
10
11
12
>  mvn clean package docker:build
 
...
 
Successfully tagged jcg/dropwizard-webapp:0.0.1-SNAPSHOT
[INFO] Built jcg/dropwizard-webapp:0.0.1-SNAPSHOT
[INFO] Tagging jcg/dropwizard-webapp:0.0.1-SNAPSHOT with latest
[INFO] ---------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ---------------------------------------------------------
 
...

Давайте посмотрим, будет ли наше изображение доступно локально на этот раз.

1
2
3
4
5
> docker image ls
REPOSITORY               TAG            IMAGE ID      CREATED             SIZE
jcg/dropwizard-webapp    0.0.1-SNAPSHOT fa9c310683b1  20 seconds ago      128MB
jcg/dropwizard-webapp    latest         fa9c310683b1  20 seconds ago      128MB
...

Отлично, предполагая, что контейнер MySQL запущен и работает (эта часть не меняется вообще, мы могли бы использовать ту же команду из предыдущего раздела ), мы могли бы просто запустить наш контейнер приложения Dropwizard .

1
2
3
4
5
6
docker run -d --rm \
  --name dropwizard-webapp \
  -p 19900:19900 \
  -p 19901:19901 \
  -e DB_HOST=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql` \
  jcg/dropwizard-webapp:0.0.1-SNAPSHOT

Мы также отображаем порты 19900 и 19901 из контейнера на хост, чтобы мы могли использовать localhost качестве имени хоста в curl .

01
02
03
04
05
06
07
08
09
10
11
$ curl -X POST http://localhost:19900/tasks \
   -d '[{"title": "Task #1", "description": "Sample Task"}]' \
   -H "Content-Type: application/json"
 
[
  {
    "id":1,
    "title":"Task #1",
    "description":"Sample Task"
  }
]
1
2
3
4
5
6
7
8
9
$ curl http://localhost:19900/tasks
 
[
  {
    "id":1,
    "title":"Task #1",
    "description":"Sample Task"
  }
]
1
2
3
4
5
6
7
$ curl http://localhost:19900/tasks/1
 
{
  "id":1,
  "title":"Task #1",
  "description":"Sample Task"
}

Обратите внимание, что при сопоставлении портов хоста мы можем запустить либо jcg/dropwizard-webapp:0.0.1-SNAPSHOT либо контейнер jcg/spring-boot-webapp:0.0.1-SNAPSHOT , но не оба одновременно, так как к неизбежным конфликтам портов. Мы просто используем один и тот же порт для удобства, но в большинстве случаев вы будете использовать динамические привязки портов и не увидите, что эта проблема происходит.

6. Maven на докере

Та же техника использования многоэтапных сборок в равной степени применима к проектам, которые используют Apache Maven для сборки и управления зависимостями (к счастью, существуют официальные изображения Apache Maven, опубликованные на Docker Hub ).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
FROM maven:3.5.2-jdk-8-alpine
 
ADD src src
ADD pom.xml .
 
RUN mvn package
 
FROM openjdk:8-jdk-alpine
ARG BUILD_VERSION
 
ENV DB_HOST localhost
ENV DB_PORT 3306
 
COPY --from=0 /target/dropwizard-webapp-${BUILD_VERSION}.jar dropwizard-webapp.jar
ADD application.yml application.yml
ADD src/main/docker/docker-entrypoint.sh docker-entrypoint.sh
RUN chmod a+x /docker-entrypoint.sh
 
EXPOSE 19900 19901
 
ENTRYPOINT ["/docker-entrypoint.sh"]

Немного добавить сюда, когда мы взломали работу многоступенчатых сборок , поэтому давайте создадим окончательный образ с помощью инструмента командной строки docker .

1
2
3
4
5
docker image build \
  --build-arg BUILD_VERSION=0.0.1-SNAPSHOT \
  -f Dockerfile.build \
  -t jcg/dropwizard-webapp:latest \
  -t jcg/dropwizard-webapp:0.0.1-SNAPSHOT .

И убедитесь, что изображение появляется в списке доступных изображений Docker .

1
2
3
4
5
> docker image ls
REPOSITORY             TAG             IMAGE ID       CREATED          SIZE
jcg/dropwizard-webapp  0.0.1-SNAPSHOT  5b006fcc9a1d   26 seconds ago   128MB
jcg/dropwizard-webapp  latest          5b006fcc9a1d   26 seconds ago   128MB
...

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

7. Выводы

В этом разделе руководства мы увидели несколько примеров того, как популярные инструменты управления сборкой и зависимостями, а именно Apache Maven и Gradle , поддерживают упаковку приложений Java в виде образов Docker . Мы также потратили некоторое время на обсуждение многоэтапных сборок и возможностей, которые они открывают для реализации переносимых конвейеров сборки с нулевой зависимостью (буквально!).

8. Что дальше

В следующем разделе руководства мы рассмотрим, как Docker может упростить процессы и практики разработки, особенно в отношении работы с хранилищами данных и внешними (или даже внутренними) сервисами.

Полные исходные коды проекта доступны для скачивания .