Skip to content
This repository has been archived by the owner on Apr 6, 2018. It is now read-only.

Commit

Permalink
TLB bugfixes (#135)
Browse files Browse the repository at this point in the history
* Don't colorize logs when logging to a file

* VNCEnv configure.required=True

Avoids obscure crash if user forgets to configure

* Fix merge

* Better errors for invalid remotes

Fixes #114

* Support local remotes from inside a container

* Fix formatting

* Fix formatting

* Fix formatting
  • Loading branch information
nottombrown committed Feb 1, 2017
1 parent 1ca7c58 commit a5ea649
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 17 deletions.
47 changes: 32 additions & 15 deletions doc/remotes.rst
Expand Up @@ -24,19 +24,19 @@ run ``docker ps`` and get something like this:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
How to start a remote
=====================

There are currently three ways to start a remote:

- Create an **automatic local remote** using ``env.configure(remotes=1)``.
In this case, ``universe`` automatically creates a remote locally by spinning
up a docker container for you.

- Create a **manual remote** by spinning up your own Docker container,
locally or on a server you control.

- Create a **starter cluster** in AWS, which will automatically provide you
with cloud-hosted remotes.

Expand Down Expand Up @@ -64,6 +64,25 @@ container and start 1 copy of it locally.
env.render()
Agents inside Docker
~~~~~~~~~~~~~~~~~~~~
If you're running your agent inside a Docker container, it can still create automatic remotes by connecting
to the docker daemon on the host. To do this, mount the docker binary and socket inside the agent container like this:

.. code:: shell
$ docker run --privileged \
-v /usr/bin/docker:/usr/bin/docker \
-v /root/.docker:/root/.docker \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DOCKER_NET_HOST=172.17.0.1 \
... \
my/agent:latest
The Universe remote will use ``$DOCKER_NET_HOST`` when connecting to the VNC and rewarder ports.


Manual remotes
--------------

Expand Down Expand Up @@ -109,21 +128,21 @@ VNC compression settings
-----------------------------------------------

The VNC connection supports multiple compression settings that control the tradeoff
between a fast but highly compressed, low quality data stream and slow, uncompressed
data stream. These can be configured by using the ``vnc_kwargs`` argument to
between a fast but highly compressed, low quality data stream and slow, uncompressed
data stream. These can be configured by using the ``vnc_kwargs`` argument to
``env.configure``. The default arguments are:

.. code:: python
env.configure(vnc_kwargs={'encoding':'tight', 'fine_quality_level':50, 'subsample_level':2})
Here, ``tight`` is a lossy encoding that uses JPEG for compression. We also support ``zrle`` instead, which is lossless.
The ``fine_quality_level`` controls the compression strength from high compression / low quality (0) to low compression / high quality (100).
The ``fine_quality_level`` controls the compression strength from high compression / low quality (0) to low compression / high quality (100).
For ``subsample_level``, 0 is highest quality, 2 is low quality and 3 is greyscale. You can investigate the effects
of many of these options on the visual fidelity by connecting to an environment using TurboVNC, which allows you to
of many of these options on the visual fidelity by connecting to an environment using TurboVNC, which allows you to
tune these settings in the user interface.

Note that the codecs always operate on deltas of the screen, so if large portions of your screen are not changing then
Note that the codecs always operate on deltas of the screen, so if large portions of your screen are not changing then
you might be able to afford higher quality settings. Conversely, if you're playing a racing game that takes up a large
portion of the screen you should be more worried about bandwidth. The call to ``step`` is asynchronous with respect to
new frames arriving, so if the connection is too slow the environments will lag.
Expand All @@ -146,7 +165,7 @@ Once your stack on AWS is ready, run `starter-cluster` to start your environment
or example, the follow will start two flashgames remotes:

.. code:: shell
$ pip install -r bin/starter-cluster-requirements.txt
$ bin/starter-cluster -v start -s OpenAI-Universe -i my-ec2-key.pem -r flashgames -n 2
Creating network "flashgames_default" with the default driver
Expand All @@ -164,13 +183,13 @@ Now you can pass the IP address and ports for your remotes to your agent,
as was described in the previous section on manual remotes. For example:

.. code:: shell
$ python bin/random_agent.py -e flashgames.DuskDrive-v0 -r vnc://54.245.154.123:5013+5015,54.245.154.123:5006+5008
Running ``bin/starter-cluster start`` again will restart your remotes. To stop them, run:

.. code:: shell
$ bin/starter-cluster stop -s OpenAI-Universe -i my-ec2-key.pem -r flashgames
Stopping flashgames_flashgames-1_1 ... done
Stopping flashgames_flashgames-0_1 ... done
Expand All @@ -194,7 +213,7 @@ Scaling Up
If you encounter the following

.. code:: shell
$ bin/starter-cluster -v start -s OpenAI-Universe -i my-ec2-key.pem -r flashgames -n 2
Creating network "flashgames_default" with the default driver
Pulling flashgames-0 (quay.io/openai/universe.flashgames:0.19.36)...
Expand Down Expand Up @@ -244,5 +263,3 @@ The configuration for the runtimes is defined in
and the specific version number tags for the corresponding Docker
images are specified in
`runtimes.yml <https://github.com/openai/universe/blob/master/universe/runtimes.yml>`__.


1 change: 1 addition & 0 deletions universe/envs/vnc_env.py
Expand Up @@ -105,6 +105,7 @@ class VNCEnv(vectorized.Env):
'semantics.autoreset': True,
'video.frames_per_second' : 60,
'runtime.vectorized': True,
'configure.required': True,
}

def __init__(self, fps=None, probe_key=None):
Expand Down
4 changes: 3 additions & 1 deletion universe/remotes/compose/log_printer.py
Expand Up @@ -3,6 +3,7 @@
from __future__ import unicode_literals

import sys
import os
import threading

from collections import namedtuple
Expand All @@ -18,7 +19,8 @@
from universe.remotes.compose.utils import split_buffer

def build(containers, service_names, **kwargs):
presenters = build_log_presenters(service_names, False)
monochrome = not os.isatty(1)
presenters = build_log_presenters(service_names, monochrome)
printer = LogPrinter(containers, presenters, **kwargs)
printer.start()

Expand Down
14 changes: 13 additions & 1 deletion universe/remotes/docker_remote.py
Expand Up @@ -110,15 +110,27 @@ def healthcheck(self, instances):
)

def get_client():
"""
Set DOCKER_HOST (and probably DOCKER_TLS_VERIFY and DOCKER_CERT_PATH) to connect to a docker instance through TCP.
Leave DOCKER_HOST unset and it will use the default, typically unix:/var/run/docker.sock
It also needs to know how to connect to ports on the docker container after creating it.
Set DOCKER_NET_HOST to provide an IP address to connect to the VNC ports on
otherwise if DOCKER_HOST has a hostname, it will connect to the VNC ports using that name.
otherwise it connects using localhost
"""
info = {}
host = os.environ.get('DOCKER_HOST')
net_host = os.environ.get('DOCKER_NET_HOST')

client_api_version = os.environ.get('DOCKER_API_VERSION')
if not client_api_version:
client_api_version = "auto"

# IP to use for started containers
if host:
if net_host:
info['host'] = net_host
elif host:
info['host'] = urlparse.urlparse(host).netloc.split(':')[0]
else:
info['host'] = 'localhost'
Expand Down
6 changes: 6 additions & 0 deletions universe/remotes/hardcoded_addresses.py
Expand Up @@ -107,6 +107,8 @@ def parse_remotes(remotes):
all_vnc = True

remote, rewarder_port = remote.split('+')
if not re.match(r'^[0-9]+$', rewarder_port):
raise error.Error('Rewarder port must be an integer, not `{}`: {}'.format(rewarder_port, remotes))
rewarder_port = int(rewarder_port)
else:
if all_vnc == True:
Expand All @@ -121,6 +123,8 @@ def parse_remotes(remotes):
all_rewarder = True

remote, vnc_port = remote.split(':')
if not re.match(r'^[0-9]+$', vnc_port):
raise error.Error('VNC port must be an integer, not `{}`: {}'.format(vnc_port, remotes))
vnc_port = int(vnc_port)
else:
if all_rewarder == True:
Expand All @@ -131,6 +135,8 @@ def parse_remotes(remotes):
all_rewarder = False

host = remote
if not re.match(r'^[-a-zA-Z0-9\.\_]+$', host):
raise error.Error('Invalid hostname for remote: {}'.format(remotes))

if rewarder_port is not None:
rewarder_address = '{}:{}'.format(host, rewarder_port)
Expand Down

0 comments on commit a5ea649

Please sign in to comment.