Docker极简入门:使用Docker运行Java程序

运行简单的Java程序

先在当前目录创建App.java文件

public class App{
    public static void main(String[] args){
        String os = System.getProperty("os.name");
        String osArch = System.getProperty("os.arch");
        String osVersion = System.getProperty("os.version");

        System.out.println(os);
        System.out.println(osArch);
        System.out.println(osVersion);
    }
}

然后创建Dockerfile

## 设置基础镜像
FROM openjdk:8

## 设置进入容器时的工作目录
WORKDIR /root/app
## 将本地目录复制进容器目录中
COPY App.java /root/app

## 镜像制作时执行的命令
RUN javac App.java

## 容器启动时执行的命令
ENTRYPOINT java App

准备工作做好之后在当前目录输入命令

docker build .

.是指明Dockerfile文件在哪个路径之下,因为我们是在当前路径下创建的,所以只需要填写.就好。

build完成之后运行命令:

docker images
## 你的输出可能会像这样
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
           2045f43c5e88   6 hours ago   526MB

REPOSITORYTAG都为,这是因为刚刚在编写Dockerfile时没有指定它们。

之后的段落里会解决这个问题,对于这个简单的项目,我们只需要IMAGE ID就够了。

现在根据镜像启动容器,运行命令:

## 偷懒的话可以只打IMAGE ID的前三位
## 这个IMAGE ID要根据你实际build出来的镜像进行修改
## 请务必运行前一条命令docker images, 找到对应的ID
docker run 2045f43c5e88

输出如下:

Linux
amd64
5.4.72-microsoft-standard-WSL2

这段Java程序的作用就是输出当前操作系统的环境,根据输出可以知道博主是在WSL2上运行docker的。

FROM alpine
WORKDIR /root/app
COPY App.java /root/app

RUN apk add openjdk8
## 设置环境变量
ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH $PATH:$JAVA_HOME/bin

RUN javac App.java

ENTRYPOINT java App

为了便于区别两次构建出的不同镜像,我们给之前的镜像打上tag

使用命令:

docker tag 2045 myapp:1.0

build时可以使用-t来为镜像打tag

docker build . -t myapp:2.0

再次运行命令

docker images
REPOSITORY   TAG            IMAGE ID       CREATED        SIZE
myapp        1.0            2045f43c5e88   12 hours ago   526MB
myapp        2.0            0545999c0fc0   25 hours ago   131MB

可以看到两个镜像已经被分别打上了tag,不过值得注意的是tag为1.0的镜像体积要比2.0的大,这是为什么?

直接将openjdk作为基础镜像会包含所有的Java语言编译工具和库。

多阶段构建镜像

其实运行Java程序只需要jre就行,我们没有必要使用jdk作为基础镜像。但把程序打包成jar包,然后再交给docker的方式太麻烦了。

有没有办法实现编译、打包、运行一体化呢?

当然是有的,简单修改一下Dockerfile就可以了

先基于Maven镜像生成jar包,最后运行在jre镜像中,同时删除已经用不到的Maven镜像

首先创建一个maven项目

这是我的pom.xml文件




  4.0.0

  
  org.example
  demoapp
  app

  demoapp
  
  http://www.example.com

  
    UTF-8
    1.8
    1.8
  

  
    
      junit
      junit
      4.11
      test
    
  

  

    
      

        
        
          maven-clean-plugin
          3.1.0
        
        
        
          maven-resources-plugin
          3.0.2
        
        
          maven-compiler-plugin
          3.8.0
        
        
          maven-surefire-plugin
          2.22.1
        
        
          maven-jar-plugin
          3.0.2
          
              
			  
              
                true
                false
                lib/
                org.example.App
              
            
          
        
        
          maven-install-plugin
          2.5.2
        
        
          maven-deploy-plugin
          2.8.2
        
        
        
          maven-site-plugin
          3.7.1
        
        
          maven-project-info-reports-plugin
          3.0.0
        
      
    
  


然后在项目的根目录下创建Dockerfile文件

## build stage
FROM maven:3.8.3-jdk-8 AS MAVEN_BUILD

WORKDIR /build/
# 把本地的pom.xml和src目录复制到镜像的/build目录下
COPY pom.xml /build/
COPY src /build/src/
# 执行打包命令
RUN mvn package

## run s
FROM openjdk:8-jre-alpine
# 设置工作目录在镜像的 /app 目录下
WORKDIR /app
# 将第一阶段生成的jar包添加到第二阶段的容器中
COPY --from=MAVEN_BUILD /build/target/demoapp-app.jar /app/
# 运行jar包
ENTRYPOINT java -jar demoapp-app.jar
REPOSITORY   TAG            IMAGE ID       CREATED             SIZE
                770d75ab38d7   7 seconds ago    84.9MB

最后生成的镜像大小要比之前的500MB小了很多

Dockerfile常用命令

命令 描述
FROM 基础镜像
MAINTAINER 维护者信息
ADD 添加文件到镜像(自动解压)
COPY 添加文件到镜像(不解压)
USER 设置运行RUN指令的用户
ENV 设置环境变量
RUN 镜像制作时执行的命令
ENTRYPOINT 容器启动时执行的命令(无法被覆盖)
CMD 容器启动时执行的命令(多条CMD只执行最后一条)
EXPOSE 声明要打开的端口(实际还是要docker run -p port1:port2 才行)
VOLUME 目录映射
ONBUILD 构建时自动执行的命令
原文链接:,转发请注明来源!