Docker 没有在指定的卷中创建文件

Docker doesn't create files in specified volume

我创建了这个小演示程序来展示我遇到的问题。

public class DemoApplication {

public static void main(String[] args) {
    create(".", "1");
    create("", "2");
    create("app/usr", "3");
    create("/usr/app", "4");
    create("usr/app", "5");
}

public static void create(String location, String name) {
    try {
        File myObj = new File(location+ "/" + name + ".txt");
        if (myObj.createNewFile()) {
            System.out.println("File created: " + myObj.getName());
        } else {
            System.out.println("File already exists.");
        }
    } catch (IOException e) {
        System.out.println("An error occurred.");
        e.printStackTrace();
    }
}

}

我正在使用这个 Dockerfile

FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} /usr/app/demo.jar
WORKDIR /usr/app/
VOLUME /usr/app/
ENTRYPOINT ["java","-jar","demo.jar"]

这些构建容器的命令

mvn clean package
docker build -t please/work .

最后但并非最不重要的是,这条命令 运行 容器

docker run please/work -v /root/hayasaka:/usr/app/

我也试过用这个

docker run please/work -v /root/hayasaka:/

但在这两种情况下都不会在 /root/hayasaka

处创建任何文件

这是我在运行连接容器

时得到的控制台输出
File created: 1.txt
File created: 2.txt
An error occurred.
java.io.IOException: No such file or directory
        at java.io.UnixFileSystem.createFileExclusively(Native Method)
        at java.io.File.createNewFile(File.java:1012)
        at com.example.demo.DemoApplication.create(DemoApplication.java:23)
        at com.example.demo.DemoApplication.main(DemoApplication.java:15)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:109)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
File created: 4.txt
An error occurred.
java.io.IOException: No such file or directory
        at java.io.UnixFileSystem.createFileExclusively(Native Method)
        at java.io.File.createNewFile(File.java:1012)
        at com.example.demo.DemoApplication.create(DemoApplication.java:23)
        at com.example.demo.DemoApplication.main(DemoApplication.java:17)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:109)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)

理论上应该在指定的卷中创建1.txt或2.txt。哦,我还使用 Spring 初始化程序创建了项目,因为真正的项目也是使用该工具创建的。作为可选依赖项,我只选择了 Developer tools 并且我没有更改 pom.xml 文件中的任何内容。

EDIT: To make this answer complete, I am adding what is already mentioned in a comment on the question. The fact that the -v switch on the docker run command is not positioned correctly.

您的执行在此处未按预期运行的首要原因是 -v /some/path 开关附加到 docker run 命令的末尾,在图像名称之后。

docker run --help 生成此输入语法描述:

Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

这里声明必须在图片名称前提供[OPTIONS]。所有选项开关都以一个或两个 - 开头,并且在大多数情况下,后面跟一个开关参数。因此 docker 二进制文件将识别 docker run 命令的第一个参数,即 而不是 前面加上 - 作为图像的名称。

图像名称后面的参数,将被解释为覆盖命令 ([COMMAND] [ARG...]),并附加到图像来源 Dockerfile 中定义的 ENTRYPOINT建成。

您已定义 ENTRYPOINT:

ENTRYPOINT ["java","-jar","demo.jar"]

鉴于您提供的第一个示例执行是这样的:

docker run please/work -v /root/hayasaka:/usr/app/

在容器中执行的结果命令将是这样的:

java -jar demo.jar -v /root/hayasaka:/usr/app/

这当然不是你的本意。如果程序可能不需要任何参数,它会简单地忽略它们。

以下是您的意见和结果的概述。

文件1.txt

    create(".", "1");

这将成为 ./1.txt,或者绝对而言 /usr/app/1.txt,这是一条很好且有效的路径。文件 1.txt 将在 java 进程的工作目录中创建,该目录又是启动 java 程序的用户的工作目录,您已在 Dockerfile 变为 /usr/app.

文件2.txt

    create("", "2");

这将创建文件 /2.txt,该文件位于容器中文件系统的根目录中。假设只有目录 /usr/app 是卷映射的,这个文件从容器外部是不可见的。

文件3.txt

    create("app/usr", "3");

这将创建文件 app/usr/3.txt,这是一个相对于 java 进程的工作目录的位置。绝对路径是/usr/app/app/usr/3.txt。这失败了,很可能是由于缺少中间目录结构 app/usr。使用此方法解决问题:

    if (!myObj.getParentFile().exists()) {
        myObj.getParentFile().mkdirs();

这将创建缺少的中间目录。

文件4.txt

    create("/usr/app", "4");

这将创建文件 /usr/app/4.txt,一个存在的绝对路径,因为 docker 构建将使用给定的 Dockerfile 定义创建它。这里没问题。

文件5.txt

    create("usr/app", "5")

这将尝试创建文件 usr/app/5.txt,它与文件 3.txt 一样是相对于 java 进程的工作目录的位置。绝对路径是 /usr/app/usr/app/5.txt 它会因为与 3.txt 失败相同的原因而失败。

我希望这提供了一个很好的概述!