mirror of https://github.com/docker/docs.git
[ENGDOCS-864] Update java language-specific guide (#15269)
* Updated java guide * Update language/java/develop.md Co-authored-by: Guillaume Lours <guillaume@lours.me> * Update language/java/develop.md Co-authored-by: Guillaume Lours <guillaume@lours.me> * moved multi-stage Dockerfile to develop section Co-authored-by: Guillaume Lours <guillaume@lours.me>
This commit is contained in:
parent
185660afa9
commit
4231acfd40
|
@ -81,12 +81,10 @@ we would like to use for our application.
|
|||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM openjdk:16-alpine3.13
|
||||
FROM eclipse-temurin:17-jdk-jammy
|
||||
```
|
||||
|
||||
Docker images can be inherited from other images. For this guide, we use the
|
||||
official `openjdk` image from Docker Hub with Java JDK that already has all the
|
||||
tools and packages that we need to run a Java application.
|
||||
Docker images can be inherited from other images. For this guide, we use Eclipse Termurin, one of the most popular official images with a build-worthy JDK.
|
||||
|
||||
To make things easier when running the rest of our commands, let’s set the image's
|
||||
working directory. This instructs Docker to use this path as the default location
|
||||
|
@ -113,15 +111,15 @@ COPY mvnw pom.xml ./
|
|||
```
|
||||
|
||||
Once we have our `pom.xml` file inside the image, we can use the `RUN` command
|
||||
to execute the command `mvnw dependency:go-offline`. This works exactly the same
|
||||
to execute the command `mvnw dependency:resolve`. This works exactly the same
|
||||
way as if we were running `mvnw` (or `mvn`) dependency locally on our machine,
|
||||
but this time the dependencies will be installed into the image.
|
||||
|
||||
```dockerfile
|
||||
RUN ./mvnw dependency:go-offline
|
||||
RUN ./mvnw dependency:resolve
|
||||
```
|
||||
|
||||
At this point, we have an Alpine version 3.13 image that is based on OpenJDK version 16, and we have also installed our dependencies. The next thing we need to do is to add our source code into the image. We’ll use the `COPY` command just like we did with our `pom.xml` file above.
|
||||
At this point, we have an Eclipse Termurin image that is based on OpenJDK version 17, and we have also installed our dependencies. The next thing we need to do is to add our source code into the image. We’ll use the `COPY` command just like we did with our `pom.xml` file above.
|
||||
|
||||
```dockerfile
|
||||
COPY src ./src
|
||||
|
@ -138,13 +136,13 @@ Here's the complete Dockerfile.
|
|||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM openjdk:16-alpine3.13
|
||||
FROM eclipse-temurin:17-jdk-jammy
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY .mvn/ .mvn
|
||||
COPY mvnw pom.xml ./
|
||||
RUN ./mvnw dependency:go-offline
|
||||
RUN ./mvnw dependency:resolve
|
||||
|
||||
COPY src ./src
|
||||
|
||||
|
@ -176,7 +174,7 @@ $ docker build --tag java-docker .
|
|||
|
||||
```console
|
||||
Sending build context to Docker daemon 5.632kB
|
||||
Step 1/7 : FROM java:3.7-alpine
|
||||
Step 1/7 : FROM eclipse-temurin:17-jdk-jammy
|
||||
Step 2/7 : WORKDIR /app
|
||||
...
|
||||
Successfully built a0bb458aabd0
|
||||
|
@ -195,7 +193,7 @@ REPOSITORY TAG IMAGE ID CREATED SIZ
|
|||
java-docker latest b1b5f29f74f0 47 minutes ago 567MB
|
||||
```
|
||||
|
||||
You should see at least the we just built `java-docker:latest`.
|
||||
You should see at least the image we just built `java-docker:latest`.
|
||||
|
||||
## Tag images
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ $ docker run -it --rm -d -v mysql_data:/var/lib/mysql \
|
|||
--name mysqlserver \
|
||||
-e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic \
|
||||
-e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic \
|
||||
-p 3306:3306 mysql:8.0.23
|
||||
-p 3306:3306 mysql:8.0
|
||||
```
|
||||
|
||||
Okay, now that we have a running MySQL, let’s update our Dockerfile to activate the MySQL Spring profile defined in the application and switch from an in-memory H2 database to the MySQL server we just created.
|
||||
|
@ -55,7 +55,7 @@ We only need to add the MySQL profile as an argument to the `CMD` definition.
|
|||
CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql"]
|
||||
```
|
||||
|
||||
Let's build our image
|
||||
Let's build our image.
|
||||
|
||||
```console
|
||||
$ docker build --tag java-docker .
|
||||
|
@ -85,30 +85,64 @@ You should receive the following json back from our service.
|
|||
{"vetList":[{"id":1,"firstName":"James","lastName":"Carter","specialties":[],"nrOfSpecialties":0,"new":false},{"id":2,"firstName":"Helen","lastName":"Leary","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":3,"firstName":"Linda","lastName":"Douglas","specialties":[{"id":3,"name":"dentistry","new":false},{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":2,"new":false},{"id":4,"firstName":"Rafael","lastName":"Ortega","specialties":[{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":1,"new":false},{"id":5,"firstName":"Henry","lastName":"Stevens","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":6,"firstName":"Sharon","lastName":"Jenkins","specialties":[],"nrOfSpecialties":0,"new":false}]}
|
||||
```
|
||||
|
||||
## Multi-stage Dockerfile for development
|
||||
|
||||
Let’s take a look at updating our Dockerfile to produce a final image which is ready for production as well as a dedicated step to produce a development image.
|
||||
|
||||
We’ll also set up the Dockerfile to start the application in debug mode in the development container so that we can connect a debugger to the running Java process.
|
||||
|
||||
Below is a multi-stage Dockerfile that we will use to build our production image and our development image. Replace the contents of your Dockerfile with the following.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM eclipse-temurin:17-jdk-jammy as base
|
||||
WORKDIR /app
|
||||
COPY .mvn/ .mvn
|
||||
COPY mvnw pom.xml ./
|
||||
RUN ./mvnw dependency:resolve
|
||||
COPY src ./src
|
||||
|
||||
FROM base as development
|
||||
CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql", "-Dspring-boot.run.jvmArguments='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000'"]
|
||||
|
||||
FROM base as build
|
||||
RUN ./mvnw package
|
||||
|
||||
FROM eclipse-temurin:17-jre-jammy as production
|
||||
EXPOSE 8080
|
||||
COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
|
||||
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-petclinic.jar"]
|
||||
```
|
||||
|
||||
We first add a label to the `FROM eclipse-temurin:17-jdk-jammy` statement. This allows us to refer to this build stage in other build stages. Next, we added a new build stage labeled `development`.
|
||||
|
||||
We expose port 8000 and declare the debug configuration for the JVM so that we can attach a debugger.
|
||||
|
||||
## Use Compose to develop locally
|
||||
|
||||
In this section, we’ll create a Compose file to start our `java-docker` and the MySQL database using a single command. We’ll also set up the Compose file to start the `java-docker` application in debug mode so that we can connect a debugger to the running Java process.
|
||||
We can now create a Compose file to start our development container and the MySQL database using a single command.
|
||||
|
||||
Open the `petclinic` in your IDE or a text editor and create a new file named `docker-compose.dev.yml`. Copy and paste the following commands into the file.
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
petclinic:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- 8000:8000
|
||||
- 8080:8080
|
||||
environment:
|
||||
- SERVER_PORT=8080
|
||||
- MYSQL_URL=jdbc:mysql://mysqlserver/petclinic
|
||||
volumes:
|
||||
- ./:/app
|
||||
command: ./mvnw spring-boot:run -Dspring-boot.run.profiles=mysql -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000"
|
||||
petclinic:
|
||||
build:
|
||||
context: .
|
||||
target: development
|
||||
ports:
|
||||
- 8000:8000
|
||||
- 8080:8080
|
||||
environment:
|
||||
- SERVER_PORT=8080
|
||||
- MYSQL_URL=jdbc:mysql://mysqlserver/petclinic
|
||||
volumes:
|
||||
- ./:/app
|
||||
|
||||
mysqlserver:
|
||||
image: mysql:8.0.23
|
||||
mysqlserver:
|
||||
image: mysql:8.0
|
||||
ports:
|
||||
- 3306:3306
|
||||
environment:
|
||||
|
@ -121,14 +155,12 @@ services:
|
|||
- mysql_data:/var/lib/mysql
|
||||
- mysql_config:/etc/mysql/conf.d
|
||||
volumes:
|
||||
mysql_data:
|
||||
mysql_config:
|
||||
mysql_data:
|
||||
mysql_config:
|
||||
```
|
||||
|
||||
This Compose file is super convenient as we do not have to type all the parameters to pass to the `docker run` command. We can declaratively do that using a Compose file.
|
||||
|
||||
We expose port 8000 and declare the debug configuration for the JVM so that we can attach a debugger.
|
||||
|
||||
Another really cool feature of using a Compose file is that we have service resolution set up to use the service names. Therefore, we are now able to use `mysqlserver` in our connection string. The reason we use `mysqlserver` is because that is what we've named our MySQL service as in the Compose file.
|
||||
|
||||
Now, to start our application and to confirm that it is running properly.
|
||||
|
|
|
@ -33,18 +33,16 @@ $ docker run -it --rm --name springboot-test java-docker ./mvnw test
|
|||
|
||||
### Multi-stage Dockerfile for testing
|
||||
|
||||
Let’s take a look at pulling the testing commands into our Dockerfile. Below is a multi-stage Dockerfile that we will use to build our production image and our test image. Add the highlighted lines to your Dockerfile
|
||||
Let’s take a look at pulling the testing commands into our Dockerfile. Below is our updated multi-stage Dockerfile that we will use to build our test image. Replace the contents of your Dockerfile with the following.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM openjdk:16-alpine3.13 as base
|
||||
|
||||
FROM eclipse-temurin:17-jdk-jammy as base
|
||||
WORKDIR /app
|
||||
|
||||
COPY .mvn/ .mvn
|
||||
COPY mvnw pom.xml ./
|
||||
RUN ./mvnw dependency:go-offline
|
||||
RUN ./mvnw dependency:resolve
|
||||
COPY src ./src
|
||||
|
||||
FROM base as test
|
||||
|
@ -56,15 +54,14 @@ CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql", "-Dspring-
|
|||
FROM base as build
|
||||
RUN ./mvnw package
|
||||
|
||||
FROM openjdk:11-jre-slim as production
|
||||
|
||||
FROM eclipse-temurin:17-jre-jammy as production
|
||||
EXPOSE 8080
|
||||
|
||||
COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
|
||||
|
||||
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-petclinic.jar"]
|
||||
```
|
||||
|
||||
We first add a label to the `FROM openjdk:16-alpine3.13` statement. This allows us to refer to this build stage in other build stages. Next, we added a new build stage labeled `test`. We'll use this stage for running our tests.
|
||||
We added a new build stage labeled `test`. We'll use this stage for running our tests.
|
||||
|
||||
Now let’s rebuild our image and run our tests. We will run the `docker build` command as above, but this time we will add the `--target test` flag so that we specifically run the test build stage.
|
||||
|
||||
|
@ -100,18 +97,16 @@ The build output is truncated, but you can see that the Maven test runner was su
|
|||
|
||||
This is great. However, we'll have to run two Docker commands to build and run our tests. We can improve this slightly by using a `RUN` statement instead of the `CMD` statement in the test stage. The `CMD` statement is not executed during the building of the image, but is executed when you run the image in a container. When using the `RUN` statement, our tests run when building the image, and stop the build when they fail.
|
||||
|
||||
Update your Dockerfile with the highlighted line below.
|
||||
Update your Dockerfile with the following.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM openjdk:16-alpine3.13 as base
|
||||
|
||||
FROM eclipse-temurin:17-jdk-jammy as base
|
||||
WORKDIR /app
|
||||
|
||||
COPY .mvn/ .mvn
|
||||
COPY mvnw pom.xml ./
|
||||
RUN ./mvnw dependency:go-offline
|
||||
RUN ./mvnw dependency:resolve
|
||||
COPY src ./src
|
||||
|
||||
FROM base as test
|
||||
|
@ -123,11 +118,9 @@ CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql", "-Dspring-
|
|||
FROM base as build
|
||||
RUN ./mvnw package
|
||||
|
||||
FROM openjdk:11-jre-slim as production
|
||||
FROM eclipse-temurin:17-jre-jammy as production
|
||||
EXPOSE 8080
|
||||
|
||||
COPY --from=build /app/target/spring-petclinic-*.jar /spring-petclinic.jar
|
||||
|
||||
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-petclinic.jar"]
|
||||
```
|
||||
|
||||
|
@ -138,7 +131,7 @@ $ docker build -t java-docker --target test .
|
|||
[+] Building 27.6s (11/12)
|
||||
=> CACHED [base 3/6] COPY .mvn/ .mvn
|
||||
=> CACHED [base 4/6] COPY mvnw pom.xml ./
|
||||
=> CACHED [base 5/6] RUN ./mvnw dependency:go-offline
|
||||
=> CACHED [base 5/6] RUN ./mvnw dependency:resolve
|
||||
=> CACHED [base 6/6] COPY src ./src
|
||||
=> [test 1/1] RUN ["./mvnw", "test"]
|
||||
=> exporting to image
|
||||
|
@ -172,39 +165,6 @@ $ docker build -t java-docker --target test .
|
|||
executor failed running [./mvnw test]: exit code: 1
|
||||
```
|
||||
|
||||
### Multi-stage Dockerfile for development
|
||||
|
||||
The new version of the Dockerfile produces a final image which is ready for production, but as you can notice, you also have a dedicated step to produce a development container.
|
||||
|
||||
```dockerfile
|
||||
FROM base as development
|
||||
CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=mysql", "-Dspring-boot.run.jvmArguments='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000'"]
|
||||
```
|
||||
|
||||
We can now update our `docker-compose.dev.yml` to use this specific target to build the `petclinic` service and remove the `command` definition as follows:
|
||||
|
||||
```dockerfile
|
||||
services:
|
||||
petclinic:
|
||||
build:
|
||||
context: .
|
||||
target: development
|
||||
ports:
|
||||
- 8000:8000
|
||||
- 8080:8080
|
||||
environment:
|
||||
- SERVER_PORT=8080
|
||||
- MYSQL_URL=jdbc:mysql://mysqlserver/petclinic
|
||||
volumes:
|
||||
- ./:/app
|
||||
```
|
||||
|
||||
Now, let's run the Compose application. You should now see that application behaves as previously and you can still debug it.
|
||||
|
||||
```console
|
||||
$ docker-compose -f docker-compose.dev.yml up --build
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
In this module, we took a look at running tests as part of our Docker image build process.
|
||||
|
|
Loading…
Reference in New Issue