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
失败相同的原因而失败。
我希望这提供了一个很好的概述!
我创建了这个小演示程序来展示我遇到的问题。
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 thedocker 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
失败相同的原因而失败。
我希望这提供了一个很好的概述!