πŸ“œ ⬆️ ⬇️

Build projects with dapp. Part 1: Java



This article is the beginning of a cycle about building dapp 's applications in various languages, platforms, technology stacks. The previous articles about dapp (see links at the end of the material) were more overview, described the possibilities of dapp. Now it's time to talk more substantively and share specific experience with projects. In connection with the recent release of dapp 0.26.2, I will also show you how to describe the assembly in a YAML file.

I will describe the assembly on the example of the application from the dockersamples repository - atsea-sample-shop-app . This is a prototype of a small store, built on React (front) and Java Spring Boot (backend). PostgreSQL is used as a database. For greater similarity to the working draft, a reverse proxy on nginx and a payment gateway in the form of a simple script have been added.
')
In the article I will describe the assembly of the application only - images with nginx, PostgresSQL and the gateway can be found in our fork in the dappfile branch .

Build the application "as is"


After cloning the repository, the finished Dockerfile for Java and React applications can be found along the path /app/Dockerfile . In this file, two stage images are defined (in dapp, this is an artifact) and one final image. Steps build a jar application in jar and a React application in the / static directory.

 FROM node:latest AS storefront WORKDIR /usr/src/atsea/app/react-app COPY react-app . RUN npm install RUN npm run build FROM maven:latest AS appserver WORKDIR /usr/src/atsea COPY pom.xml . RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve COPY . . RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests FROM java:8-jdk-alpine RUN adduser -Dh /home/gordon gordon WORKDIR /static COPY --from=storefront /usr/src/atsea/app/react-app/build/ . WORKDIR /app COPY --from=appserver /usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar . ENTRYPOINT ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"] CMD ["--spring.profiles.active=postgres"] 

To begin with, I will redo this file "as is" in the "classic" Dappfile, and then in dappfile.yml .

Dappfile is more verbose due to Ruby blocks:

 dimg_group do artifact do #    Java- docker.from 'maven:latest' git do add '/app' do to '/usr/src/atsea' end end shell do install do run 'cd /usr/src/atsea' run 'mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve' run 'mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests' end end export '/usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar' do to '/app/AtSea-0.0.1-SNAPSHOT.jar' after :install end end artifact do #    React- docker.from 'node:latest' git do add '/app/react-app' do to '/usr/src/atsea/app/react-app' end end shell do install do run 'cd /usr/src/atsea/app/react-app' run 'npm install' run 'npm run build' end end export '/usr/src/atsea/app/react-app/build' do to '/static' after :install end end dimg 'app' do docker.from 'java:8-jdk-alpine' shell do before_install do run 'mkdir /app' run 'adduser -Dh /home/gordon gordon' end end docker do entrypoint "java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar" cmd "--spring.profiles.active=postgres" end end end 

β€œClassic” Dappfile is an export from artifact option that was available in dapp before the February release. It differs from the COPY --from directive in the Dockerfile in that it is in the artifact that it is indicated what to copy and where, and not in the description of the final image. It is easier to describe approximately the same images, in which you need to copy one result of the assembly of something. Now, from version 0.26.2, dapp supports the import mechanism , which is even preferable to use (see the example of its use below) .

And one more comment to the file. When building through docker build , a context is sent to Docker Engine. Usually this is the directory where the Dockerfile and application sources are located. In the case of dapp, the context is a Git repository, according to the history of which the dapp calculates changes from the last build, and changes only what has changed in the final image. That is, an analogue of the COPY directive without --from from the Dockerfile is the git directive, which describes which directories or files from the repository need to be copied into the final image, where to put, which owner to assign. It also describes here what changes the reassembly depends on, but more on that later. For now, let's see how the same assembly looks in the new YAML syntax :

 artifact: appserver from: maven:latest git: - add: '/app' to: '/usr/src/atsea' shell: install: - cd /usr/src/atsea - mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve - mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests --- artifact: storefront from: node:latest git: - add: /app/react-app to: /usr/src/atsea/app/react-app shell: install: - cd /usr/src/atsea/app/react-app - npm install - npm run build --- dimg: app from: java:8-jdk-alpine shell: beforeInstall: - mkdir /app - adduser -Dh /home/gordon gordon import: - artifact: appserver add: '/usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar' to: '/app/AtSea-0.0.1-SNAPSHOT.jar' after: install - artifact: storefront add: /usr/src/atsea/app/react-app/build to: /static after: install docker: ENTRYPOINT: ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"] CMD: ["--spring.profiles.active=postgres"] 

Everything is quite similar to the "classic" Dappfile, but there are a few differences. First, when developing the YAML syntax, we decided to abandon inheritance and nesting. As practice has shown, inheritance was too complex features and from time to time led to misunderstanding. A linear file - such as Dockerfile - is much clearer: it looks more like a script, and only the scripts understand everything.

Secondly, to copy the results of the artifacts, the import is now used in the dimg where the files are to be placed. A slight improvement was added: if you do not specify to, then the destination path will be the same as indicated in add.

What to look for when writing a Dappfile? A common practice in projects with Dockerfile is unfolding various Dockerfile directories and therefore paths in COPY directives are relative to these directories. Dappfile is the same for the project and the paths in the git directive are relative to the repository root. The second point is the WORKDIR directive. In the Dappfile, directives from the docker family are executed in the last step, so the cd call is used to go to the correct directory on the stages.

Improved build


Building a Java application can be broken down into at least two steps: download dependencies and build the application. The first step depends on the changes in pom.xml , the second - on changes in java-files, descriptors, resources β€” in general, we can say that a change in the src directory should lead to a reassembly of jar. Dapp offers 4 stages : before_install (where there are no sources) and install , before_setup , setup (where sources are already available in the paths specified in the git directives).

Downloading dependencies can be made more aggressive by specifying the dependency:go-offline goal instead of dependency:resolve for maven. This may be a justified decision, since pom.xml does not change very often, but dependency:resolve does not download everything and at the application assembly stage there will be calls to the Maven repository (central or to your nexus / artifactory / ...).

In total, the step of downloading dependencies can be put into the install stage, which will remain in the cache until changes are made in pom.xml , and the application build can be put into the setup stage by writing dependencies to changes in the src directory.

 artifact: appserver from: maven:latest git: - add: /app to: /usr/src/atsea stageDependencies: install: ['pom.xml'] setup: ['src'] shell: install: - cd /usr/src/atsea - mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:go-offline setup: - cd /usr/src/atsea - mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests 

Building a React application can also be divided into two steps: downloading dependencies at the install stage and building the application at the setup stage. Dependencies are described in /app/react-app/package.json .

 artifact: storefront from: node:latest git: - add: /app/react-app to: /usr/src/atsea/app/react-app stageDependencies: install: ['package.json'] setup: ['src', 'public'] shell: install: - cd /usr/src/atsea/app/react-app - npm install setup: - cd /usr/src/atsea/app/react-app - npm run build 

I note that the paths to the stageDependencies are relative to the path specified in add .

Commits and cache


Now let's see how stageDependencies work. To do this, you need to make a commit with the change in the java-file and start the dapp dimg build . The log will show that only the setup phase is going:

 Setup group Git artifacts: apply patches (before setup) ... [OK] 1.7 sec signature: dimgstage-atsea-sample-shop-app:e543a0f90ba39f198b9ae70a6268acfe05c6b3a6e25ca69b1b4bd7414a6c1067 Setup [BUILDING] [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building atsea 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 39.283 s [INFO] Finished at: 2018-02-05T13:18:47Z [INFO] Final Memory: 42M/355M [INFO] ------------------------------------------------------------------------ Setup [OK] 46.71 sec signature: dimgstage-atsea-sample-shop-app:264aeb0287bbe501798a0bb19e7330917f3ec62b3a08e79a6c57804995e93137 commands: cd /usr/src/atsea mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests building artifact `appserver` [OK] 49.12 sec 

If you change pom.xml , commit and start the build, the install stage will be rebuilt with dependencies downloading and then the setup stage.

Dependencies


Splitting the assembly into two steps for a Java application has cached dependencies, and now the install stage image serves as a dependency storage. However, dapp provides the ability to mount a directory for this kind of repositories. You can mount from the temporary directory tmp_dir , the lifetime of which is one build, from build_dir , this is a permanent directory that is unique for each project. The documentation contains directives for the Dappfile, and in the case of our application, I will show how to add a mount subdirectory from build_dir to dappfile.yml :

  artifact: appserver from: maven:latest > mount: > - from: build_dir > to: /usr/share/maven/ref/repository git: ... shell: install: ... 

If you do not specify the --build-dir flag, then dapp as build_dir creates the ~/.dapp/builds/< dapp> . In build_dir after the build, the mount directory appears, in which there will be a tree of mounted directories. The project name is calculated as the name of the directory containing the Git repository. If projects from directories of the same name are collected, the project name can be specified with the --name flag, or you can explicitly specify different directories using the --build-dir flag. In our case, the dapp name will be calculated from the directory where the project's Git repository is stored and therefore ~/.dapp/builds/atsea-sample-shop-app/mount/usr/share/maven/ref/repository/ .

Run through compose


This was not previously mentioned, but you can use dapp to build, and run the project for verification using docker-compose. To start, you will need to make tags for the images and fix docker-compose.yml so that the images collected by dapp are used.

The easiest way to process images is to run the dapp dimg tag without flags (other methods and patterns for naming images are in the documentation ). The command will display the image names with the latest tag. Now we need to fix docker-compose.yml : remove build directives and add image directives with image names from the dapp dimg tag output.

For example:

  payment_gateway: image: atsea-sample-shop-app/payment-gateway 

Now you can start the project with the docker-compose up (if the build remains for any reason, then the --no-build flag will help):



The site is available at localhost: 8080:



PS


In the next part of the article we will talk about building an application on ... PHP or Node.js - according to the results of the voting below.

Read also in our blog:

Source: https://habr.com/ru/post/348436/


All Articles