java.awt.AWTError: 使用 ':0' 作为 DISPLAY 变量的值无法连接到 X11 窗口服务器。

4
我一直在尝试在Docker内的Ubuntu VM上运行基于Java AWT的应用程序。该应用程序非常简单,在Windows中使用Eclipse可以无缝运行。它只是打开一个窗口并打印“hello world!”。实际上,当我将jar文件导出到正在我的Windows主机上运行的Ubuntu VM中时,通过“java -jar JFrameDocker.jar”运行jar文件时也会给我相同的输出。
但是,当我尝试在docker中运行它时,情况并不相同。我在Dockerfile中指定的说明没有任何错误,但是当我运行应用程序时,它会抛出“java.awt.AWTError:无法使用':0'作为DISPLAY变量值连接到X11窗口服务器”。相信我,各位,我已经多次尝试解决了这个问题,但到目前为止还没有运气。例如,设置显示变量值多次,使用xterm和xeyes启动X服务器,查阅我在网络上找到的每篇可能的文章。但是在这里似乎没有任何效果。我在此提供所有文件,以便您更好地理解我的问题。顺便说一下, 并未回答我的问题,因为即使进行更改,仍然会抛出相同的错误。
这是我的Java文件。
package com.etp;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class JFrameDockerTest {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JLabel lable = new JLabel("Hello World");
        panel.setLayout( new FlowLayout() );  
        frame.add(panel);
        panel.add(lable);
        frame.setVisible(true);
        frame.setSize(800, 600);
    }

}

这是我的Dockerfile:

FROM java:8
ENV DISPLAY :0
ADD JFrameDocker.jar JFrameDocker.jar
CMD ["java","-jar", "JFrameDocker.jar"]

我使用下面的 docker 命令构建镜像并运行它。
Docker 构建命令: sudo docker build -t jframedocker . Docker 运行命令: sudo docker run jframedocker 无错误输出截图:(未使用 Docker) Docker 输出结果:
etp@etp-VirtualBox:~/Downloads/JFrameDocker$ sudo docker run jframedocker
Exception in thread "main" java.lang.reflect.InvocationTargetException
    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.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: java.awt.AWTError: Can't connect to X11 window server using ':0' as the value of the DISPLAY variable.
    at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
    at sun.awt.X11GraphicsEnvironment.access$200(X11GraphicsEnvironment.java:65)
    at sun.awt.X11GraphicsEnvironment$1.run(X11GraphicsEnvironment.java:115)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.awt.X11GraphicsEnvironment.<clinit>(X11GraphicsEnvironment.java:74)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at java.awt.GraphicsEnvironment.createGE(GraphicsEnvironment.java:103)
    at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:82)
    at java.awt.Window.initGC(Window.java:475)
    at java.awt.Window.init(Window.java:495)
    at java.awt.Window.<init>(Window.java:537)
    at java.awt.Frame.<init>(Frame.java:420)
    at java.awt.Frame.<init>(Frame.java:385)
    at javax.swing.JFrame.<init>(JFrame.java:189)
    at com.etp.JFrameDockerTest.main(JFrameDockerTest.java:12)
    ... 5 more

上面的URL中讨论了类似的事情。 - Mebin Joe
1
我尝试了那个,但是它没有得到解决。这让我发布了一个带有简单示例的问题。 - Soumyaranjan Pani
1个回答

3

您正在使用java:8基础镜像,该镜像很可能不提供图形环境。

可以使用ubuntu:18.04基础镜像,并手动安装openjdk-11-jdkxvfb软件包。使用xvfb-run命令来设置虚拟X Server环境:

xvfb-run会建立一个X权限文件(或使用现有的用户指定文件),在其中写入cookie(参见xauth(1x)),然后作为后台进程启动Xvfb X服务器。 Xvfb的进程ID将被存储以供以后使用。 然后使用刚刚启动的Xvfb服务器对应的X显示运行指定的命令,并使用先前创建的X权限文件。

Dockerfile

FROM ubuntu:18.04
RUN apt-get update -y && apt-get upgrade -y && apt-get install -y openjdk-11-jdk xvfb 
ADD JFrameDocker.java MANIFEST.mf ./
RUN javac JFrameDocker.java
RUN jar cfm JFrameDocker.jar MANIFEST.mf JFrameDocker.class 
RUN xvfb-run java -jar JFrameDocker.jar

JFrameDocker.java

import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class JFrameDocker {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JLabel lable = new JLabel("Hello World");
        panel.setLayout(new FlowLayout());  
        frame.add(panel);
        panel.add(lable);
        frame.setSize(800, 600);
        frame.setVisible(true);
        System.out.println("Up and running");
    }

}

MANIFEST.mf

Manifest-Version: 1.0
Main-Class: JFrameDocker

我尝试使用上述Dockerfile,但它在“RUN xvfb-run java -jar JFrameDocker.jar”这一步卡住了。 - Soumyaranjan Pani
1
@SoumyaranjanPani 因为它是在容器中运行,而不是在主机系统中运行。您需要配置X转发才能看到容器屏幕。 - Karol Dowbecki
1
好的,我明白了。您能指导我如何配置X转发,以便我可以在容器屏幕上看到输出吗? - Soumyaranjan Pani
1
实际上,我在 ssh_configsshd_config 文件中配置了 X11 转发,但仍然无法看到输出,甚至不打印语句 System.out.println("Up and running");。 - Soumyaranjan Pani
1
ssh_config 中,有三个关于 X11 转发的参数,我已将其值设置如下: ForwardAgent yes ForwardX11 yes ForwardX11Trusted yes而在 sshd_config 中:X11Forwarding yes X11UseForwarding yes X11DisplayOffset 10 X11UseLocalhost yes - Soumyaranjan Pani
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接