可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
What I want to do is build a docker image for my Java application but the following considerations should be true for most compiled languages.
problem
On my build server I want to produce a docker image for my application as the deliverable. For this I have to compile the application using some build tool (typically Gradle, Maven or Ant) and then add the created JAR file to the docker image. As I want the docker image to just execute the JAR file I will of course start from a base image with Java already installed.
There are three ways of doing this:
let the build tool control the process
In this case my build tool controls the whole process. So it prepares the JAR file and after the JAR is created it calls Docker to create the image. This works as the JAR is created beforehand and Docker can be oblivious of the build process needed to create the JAR.
But my Dockerfile is no longer standalone. It depends on steps to happen outside of Docker for it work. In my Dockerfile I will have a COPY
or ADD
statement that is supposed to copy the JAR file to the image. This statement will fail when the jar is not created beforehand. So just executing the Dockerfile might not work. This becomes a problem if you want to integrate with services that just build using the present Dockerfile like the auto-build feature on DockerHub.
let Docker control the build
In this case all necessary steps to create the image are added to the Dockerfile so the image can be created by just executing the Docker build.
The main problem with this approach is that there is no way to add to a Dockerfile commands that should be executed outside the docker image being created. This means I have to add my source code and my build tools to the docker image and build my JAR file inside the image. This will result in my image being bigger than it has to be due to all the files added that will be unnecessary at runtime. This will also add extra layers to my image.
Edit:
As @adrian-mouat pointed out if I would add the sources, build the application and deleted the sources in one RUN statement I could avoid adding unnecessary files and layers to the Docker image. This would mean creating some insane chained command.
two separate builds
In this case we split our build in two: first we create the JAR file using our build tool and upload it to a repository (Maven or Ivy repository). We then trigger a separate Docker build that just adds the JAR file from the repository.
conclusion
In my opinion the better way would be letting the build tool control the process. This is will result in a clean docker image and as the image is what we want to deliver this is of importance. To avoid having a potentially not working Dockerfile lying around this should be created as part of the build. So no one would accidentally use it to start a broken build.
But this will not allow me to integrate with DockerHub.
question
Is there another way I am missing?
回答1:
The docker registry hub has a Maven image that can be used to create java containers.
Using this approach the build machine does not need to have either Java or Maven pre-installed, Docker controls the entire build process.
Example
├── Dockerfile
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── org
│ │ └── demo
│ │ └── App.java
│ └── resources
│ └── log4j.properties
└── test
└── java
└── org
└── demo
└── AppTest.java
Container is built as follows:
docker build -t my-maven .
And run as follows:
$ docker run -it --rm my-maven
0 [main] INFO org.demo.App - hello world
Dockerfile
FROM maven:3.3-jdk-8-onbuild
CMD ["java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]
Update
If you wanted to optimize your container to exclude the source you could create a Dockerfile that only includes the built jar:
FROM java:8
ADD target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar /opt/demo/demo-1.0-SNAPSHOT-jar-with-dependencies.jar
CMD ["java","-jar","/opt/demo/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]
And build the container in two steps:
docker run -it --rm -w /opt/maven \
-v $PWD:/opt/maven \
-v $HOME/.m2:/root/.m2 \
maven:3.3-jdk-8 \
mvn clean install
docker build -t my-app .
__
Update (2017-07-27)
Docker now has a multi-stage build capability. This enables Docker to build a container with an image containing the build tools but output an image with only the runtime dependencies.
The following example demonstrates this concept, note how the jar is copied from target directory of the first build phase
FROM maven:3.3-jdk-8-onbuild
FROM java:8
COPY --from=0 /usr/src/app/target/demo-1.0-SNAPSHOT.jar /opt/demo.jar
CMD ["java","-jar","/opt/demo.jar"]
回答2:
Structure of java aplication
Demo
└── src
| ├── main
| │ ├── java
| │ │ └── org
| │ │ └── demo
| │ │ └── App.java
| │ └── resources
| │ └── application.properties
| └── test
| └── java
| └── org
| └── demo
| └── App.java
├──── Dockerfile
├──── pom.xml
Content of Dockerfile
FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]
Commands to build and run image
- Go to the directory of project.Lets say D:/Demo
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo
Check that container is running or not
$ docker ps
The output will be
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55c11a464f5a demo1 "java -jar demo.jar" 21 seconds ago Up About a minute 0.0.0.0:8080->8080/tcp cranky_mayer
回答3:
The easiest way is to let the build tool control the process. Otherwise, you would have to maintain your build tool's build file (like pom.xml
for Maven or build.gradle
for Gradle) as well as a Dockerfile
.
A simple way to build a Docker container for your Java app is to use Jib, which is available as Maven and Gradle plugins.
For example, if you are using Maven and want to build your container to your running Docker daemon, you can just run this one command:
mvn compile com.google.cloud.tools:jib-maven-plugin:0.9.2:dockerBuild
You can also build directly to a Docker registry with Jib without needing to install docker
, run a Docker daemon (which requires root privileges), or write a Dockerfile
. It's also faster and builds images reproducibly.
See more about Jib at its Github repo: https://github.com/GoogleContainerTools/jib
回答4:
We used the Spotify Docker Maven Plugin for a while. The plugin allows you to bind a Docker build it to a phase of the Maven lifecycle.
An example:
Run the Docker build after packaging (phase: package) your application by configuring the plugin to add your built application as a resource to the Docker build context. In the deploy phase run the Docker push goal to push your Docker image to a registry. This can run beside the normal deploy plugin, which publishes the artifact into a repository like Nexus.
Later on, we splitted the build into two separate jobs on the CI server. Since Docker is just a one way to run your application (sometimes we need the released application on different environments not only Docker), the Maven build should not rely on Docker.
So the first job releases the application in Nexus (via Maven deploy). The second job (which can be a downstream dependency of the first job) downloads the latest release artifact, performs the Docker build and pushes the image to the registry. For downloading the latest release we use the Versions Maven Plugin (versions:use-latest-releases) as well as the Maven Dependency Plugin (dependency:get and dependency:copy).
The second job can also be started for specific version of the application to (re)build the Docker image for an older release. Moreover you can use a build pipeline (on Jenkins), which executes both jobs and passes the release version or the release artifact to the Docker build.
回答5:
A couple of things:
If you delete files in the same instruction you add them, they won't consume space in the image. If you look at some of the Dockerfiles for the official images you will see they download source, build it and delete it all in the same step (e.g. https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5/slim/Dockerfile). This does mean you need to do some annoying gymnastics, but it is perfectly doable.
I don't see the problem with two separate Dockerfiles. The nice thing about this is that you could use the JRE rather than the JDK to host your jar.
回答6:
there are alternative usages for running jar or war packages
- add jar into image.
- set heapsize for java
- run jar command via entrypoint
sample dockerfile
FROM base
ADD sample.jar renamed.jar
ENV HEAP_SIZE 256m
ENTRYPOINT exec java -Xms$HEAP_SIZE -Xmx$HEAP_SIZE -jar renamed.jar
in addition package deployment example on tomcat
FROM tomcat7
ADD sample.war ${CATALINA_HOME}/webapps/ROOT.war
CMD ${CATALINA_HOME}/bin/catalina.sh run
Building dockerfiles as an image
cp tomcat.dockerfile /workingdir/Dockerfile
docker build -t name /workingdir/Dockerfile .
List images
docker images
Use image to create a container
docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename
回答7:
Here I describe how I do it in my development environment.
- Build the war/jar locally with Maven
- Copy it to a local Docker folder
- Run Intellij Docker plugin which creates a docker image that contains the war/jar, run the application server and deploys it on the remote Docker server
Hope it helps.
回答8:
Containerize your java application using Jib tool without writing dockerfile
Jib is an open-source Java tool maintained by Google for building Docker images of Java applications. It simplifies containerization since with it, we don’t need to write a dockerfile. And actually, we don’t even have to have docker installed to create and publish the docker images ourselves.
Google publishes Jib as both a Maven and a Gradle plugin.
https://github.com/GoogleContainerTools/jib
Containerize your java application using Maven project
https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#quickstart
Containerize your java application using Gradle project
https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart