Is it possible to deploy a GUI application using Kubernetes?

5/31/2019

I have a docker container that runs a GUI application, I can successfully run it, using this command

sudo docker run --net=host --env="DISPLAY" --volume="$HOME/.Xauthority:/root/.Xauthority:rw" -it test

I have already tried to create this deployment file:

{
  "kind": "Deployment",
  "apiVersion": "extensions/v1beta1",
  "metadata": {
    "name": "test",
    "namespace": "default",
    "selfLink": "/apis/extensions/v1beta1/namespaces/default/deployments/gazebo",
    "uid": "249a12a9-83b8-11e9-8ec2-32ccf6441134",
    "resourceVersion": "6165060",
    "generation": 1,
    "creationTimestamp": "2019-05-31T15:24:12Z",
    "labels": {
      "k8s-app": "test"
    },
    "annotations": {
      "deployment.kubernetes.io/revision": "1"
    }
  },
  "spec": {
    "replicas": 1,
    "selector": {
      "matchLabels": {
        "k8s-app": "test"
      }
    },
    "template": {
      "metadata": {
        "name": "test",
        "creationTimestamp": null,
        "labels": {
          "k8s-app": "test"
        }
      },
      "spec": {
        "volumes": [
          {
            "name": "test",
            "hostPath": {
              "path": "$HOME/.Xauthority:/root/.Xauthority:rw",
              "type": ""
            }
          }
        ],
        "containers": [
          {
            "name": "test",
            "image": "test:1.0.12",
            "env": [
              {
                "name": "DISPLAY",
                "value": ":0"
              }
            ],
            "resources": {},
            "terminationMessagePath": "/dev/termination-log",
            "terminationMessagePolicy": "File",
            "imagePullPolicy": "IfNotPresent",
            "securityContext": {
              "privileged": false,
              "procMount": "Default"
            },
            "stdin": true
          }
        ],
        "restartPolicy": "Always",
        "terminationGracePeriodSeconds": 30,
        "dnsPolicy": "ClusterFirst",
        "nodeSelector": {
          "component": "test"
        },
        "hostNetwork": true,
        "securityContext": {},
        "schedulerName": "default-scheduler"
      }
    },
    "strategy": {
      "type": "RollingUpdate",
      "rollingUpdate": {
        "maxUnavailable": "25%",
        "maxSurge": "25%"
      }
    },
    "revisionHistoryLimit": 10,
    "progressDeadlineSeconds": 600
  },
  "status": {
    "observedGeneration": 1,
    "replicas": 1,
    "updatedReplicas": 1,
    "unavailableReplicas": 1,
    "conditions": [
      {
        "type": "Progressing",
        "status": "True",
        "lastUpdateTime": "2019-05-31T15:24:14Z",
        "lastTransitionTime": "2019-05-31T15:24:12Z",
        "reason": "NewReplicaSetAvailable",
        "message": "ReplicaSet \"test-dbfdb6467\" has successfully progressed."
      },
      {
        "type": "Available",
        "status": "False",
        "lastUpdateTime": "2019-05-31T15:40:21Z",
        "lastTransitionTime": "2019-05-31T15:40:21Z",
        "reason": "MinimumReplicasUnavailable",
        "message": "Deployment does not have minimum availability."
      }
    ]
  }
}

I have tried to inspect the resulting containers and I have obtained these results:

For the one deployed with kubernetes the result is

[
    {
        "Id": "114e1d307b8260eaa02bfcf214031cf34ae522cf55258731a8a6dca535527995",
        "Created": "2019-05-31T15:24:13.320599267Z",
        "Path": "/home/startup.sh",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 10908,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2019-05-31T15:24:13.627542552Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:2b02610511b1d09925b5b0b2471efea087819942c36c0c0bf490d6a28709f54e",
        "ResolvConfPath": "/home/docker/containers/8537a1c6038f5d6b4186d0b56a6b839207477825d0ad99d8610d36f66967495e/resolv.conf",
        "HostnamePath": "/home/docker/containers/8537a1c6038f5d6b4186d0b56a6b839207477825d0ad99d8610d36f66967495e/hostname",
        "HostsPath": "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/etc-hosts",
        "LogPath": "/home/docker/containers/114e1d307b8260eaa02bfcf214031cf34ae522cf55258731a8a6dca535527995/114e1d307b8260eaa02bfcf214031cf34ae522cf55258731a8a6dca535527995-json.log",
        "Name": "/k8s_gazebo_gazebo-dbfdb6467-fd448_default_249ff7a4-83b8-11e9-8ec2-32ccf6441134_0",
        "RestartCount": 0,
        "Driver": "aufs",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "docker-default",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/volumes/kubernetes.io~secret/default-token-4ncqs:/var/run/secrets/kubernetes.io/serviceaccount:ro",
                "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/etc-hosts:/etc/hosts",
                "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/containers/gazebo/f4a9bfa2:/dev/termination-log"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "container:8537a1c6038f5d6b4186d0b56a6b839207477825d0ad99d8610d36f66967495e",
            "PortBindings": null,
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": null,
            "DnsOptions": null,
            "DnsSearch": null,
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "container:8537a1c6038f5d6b4186d0b56a6b839207477825d0ad99d8610d36f66967495e",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 1000,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": [
                "seccomp=unconfined"
            ],
            "UTSMode": "host",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 2,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "/kubepods/besteffort/pod249ff7a4-83b8-11e9-8ec2-32ccf6441134",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 100000,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/asound",
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": null,
            "Name": "aufs"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/volumes/kubernetes.io~secret/default-token-4ncqs",
                "Destination": "/var/run/secrets/kubernetes.io/serviceaccount",
                "Mode": "ro",
                "RW": false,
                "Propagation": "rprivate"
            },
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/etc-hosts",
                "Destination": "/etc/hosts",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/249ff7a4-83b8-11e9-8ec2-32ccf6441134/containers/gazebo/f4a9bfa2",
                "Destination": "/dev/termination-log",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
        "Config": {
            "Hostname": "davidePC",
            "Domainname": "",
            "User": "0",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": true,
            "StdinOnce": false,
            "Env": [
                "DISPLAY=:0",
                "KUBERNETES_PORT_443_TCP_PROTO=tcp",
                "KUBERNETES_PORT_443_TCP_PORT=443",
                "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
                "KUBERNETES_SERVICE_HOST=10.96.0.1",
                "KUBERNETES_SERVICE_PORT=443",
                "KUBERNETES_SERVICE_PORT_HTTPS=443",
                "KUBERNETES_PORT=tcp://10.96.0.1:443",
                "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG=C.UTF-8",
                "LC_ALL=C.UTF-8",
                "ROS_DISTRO=kinetic",
                "ROS_MASTER_URI=http://localhost:11311",
                "ROS_PACKAGE_PATH=/opt/ros/kinetic/share",
                "DEBIAN_FRONTEND=noninteractive",
                "TURTLEBOT_3D_SENSOR=no3d",
                "TURTLEBOT_TOP_PLATE_DEVICE=rplidar",
                "JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64/"
            ],
            "Cmd": [],
            "Healthcheck": {
                "Test": [
                    "NONE"
                ]
            },
            "ArgsEscaped": true,
            "Image": "sha256:2b02610511b1d09925b5b0b2471efea087819942c36c0c0bf490d6a28709f54e",
            "Volumes": null,
            "WorkingDir": "/home",
            "Entrypoint": [
                "/home/startup.sh"
            ],
            "OnBuild": null,
            "Labels": {
                "annotation.io.kubernetes.container.hash": "86f118b",
                "annotation.io.kubernetes.container.restartCount": "0",
                "annotation.io.kubernetes.container.terminationMessagePath": "/dev/termination-log",
                "annotation.io.kubernetes.container.terminationMessagePolicy": "File",
                "annotation.io.kubernetes.pod.terminationGracePeriod": "30",
                "io.kubernetes.container.logpath": "/var/log/pods/default_gazebo-dbfdb6467-fd448_249ff7a4-83b8-11e9-8ec2-32ccf6441134/gazebo/0.log",
                "io.kubernetes.container.name": "gazebo",
                "io.kubernetes.docker.type": "container",
                "io.kubernetes.pod.name": "gazebo-dbfdb6467-fd448",
                "io.kubernetes.pod.namespace": "default",
                "io.kubernetes.pod.uid": "249ff7a4-83b8-11e9-8ec2-32ccf6441134",
                "io.kubernetes.sandbox.id": "8537a1c6038f5d6b4186d0b56a6b839207477825d0ad99d8610d36f66967495e"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {}
        }
    }
]

Instead for the one run from CLI, the result is:

[
    {
        "Id": "ed428815132ac62020e36b1d50421ec7402c3b433907575a4858617d56322366",
        "Created": "2019-05-31T15:13:51.991700539Z",
        "Path": "/home/startup.sh",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 8377,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2019-05-31T15:13:52.469581933Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:2b02610511b1d09925b5b0b2471efea087819942c36c0c0bf490d6a28709f54e",
        "ResolvConfPath": "/home/docker/containers/ed428815132ac62020e36b1d50421ec7402c3b433907575a4858617d56322366/resolv.conf",
        "HostnamePath": "/home/docker/containers/ed428815132ac62020e36b1d50421ec7402c3b433907575a4858617d56322366/hostname",
        "HostsPath": "/home/docker/containers/ed428815132ac62020e36b1d50421ec7402c3b433907575a4858617d56322366/hosts",
        "LogPath": "/home/docker/containers/ed428815132ac62020e36b1d50421ec7402c3b433907575a4858617d56322366/ed428815132ac62020e36b1d50421ec7402c3b433907575a4858617d56322366-json.log",
        "Name": "/youthful_benz",
        "RestartCount": 0,
        "Driver": "aufs",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "docker-default",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "/home/davide/.Xauthority:/root/.Xauthority:rw"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "host",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "shareable",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": null,
            "Name": "aufs"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/davide/.Xauthority",
                "Destination": "/root/.Xauthority",
                "Mode": "rw",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
        "Config": {
            "Hostname": "davidePC",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "DISPLAY=:0",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG=C.UTF-8",
                "LC_ALL=C.UTF-8",
                "ROS_DISTRO=kinetic",
                "ROS_MASTER_URI=http://localhost:11311",
                "ROS_PACKAGE_PATH=/opt/ros/kinetic/share",
                "DEBIAN_FRONTEND=noninteractive",
                "TURTLEBOT_3D_SENSOR=no3d",
                "TURTLEBOT_TOP_PLATE_DEVICE=rplidar",
                "JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64/"
            ],
            "Cmd": [],
            "ArgsEscaped": true,
            "Image": "cpswarm/gazebo-em-ex:1.0.12",
            "Volumes": null,
            "WorkingDir": "/home",
            "Entrypoint": [
                "/home/startup.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "70d4cff7200ece8610fdcc04ffed7b2248caea6e61658c4622368a25d0864660",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/default",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "host": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "81ea4770bb3b91fca3383336b35ebd9c6a00c7728180873b99fb663fcbb3ef4f",
                    "EndpointID": "1aa92639019060c8a229a06b86a79b0e6607437ab6a9c5de45f83d797b8e5b9f",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "",
                    "DriverOpts": null
                }
            }
        }
    }
]

The result that I obtain is that running from CLI, I can see the GUI of the application running in the docker container. Instead with the container deployed with Kubernetes I see the application running but I cannot see the GUI.

Is there something that I can change in the deployment file to make the deployed container working?

-- Davide
kubernetes

1 Answer

6/1/2019

To run GUI application on headless server, you need Xvfb to create a virtual frame buffer as a X-server, and let application connect to the X-server by env DISPLAY.

Example yaml use xvfb docker image:

...
spec:
  containers:
  - name: test
    image: test:1.0.12
    env:
    - name: DISPLAY
      value: "localhost:1.0"
  - name: xvfb
    image: comiq/xvfb:latest
    env:
    - name: DISPLAY
      value: 1
    - name: SCREEN
      value: 0
    ports:
    - name: xserver
      containerPort: 6001
-- menya
Source: StackOverflow