Skip to content

Commit ee366ff

Browse files
authored
Merge pull request #547 from Unity-Technologies/develop-feature-docker-improvements
[containers] Enables container support for scenes that use visual obsvervations
2 parents 6d4c9a4 + 0afcc73 commit ee366ff

File tree

10 files changed

+173
-24
lines changed

10 files changed

+173
-24
lines changed

Dockerfile

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,128 @@
1-
# Use an official Python runtime as a parent image
2-
FROM python:3.6-slim
1+
# Based off of python:3.6-slim, except that we are using ubuntu instead of debian.
2+
FROM ubuntu:16.04
3+
4+
5+
# ensure local python is preferred over distribution python
6+
ENV PATH /usr/local/bin:$PATH
7+
8+
# http://bugs.python.org/issue19846
9+
# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK.
10+
ENV LANG C.UTF-8
11+
12+
# runtime dependencies
13+
RUN apt-get update && apt-get install -y --no-install-recommends \
14+
ca-certificates \
15+
libexpat1 \
16+
libffi6 \
17+
libgdbm3 \
18+
libreadline6 \
19+
libsqlite3-0 \
20+
libssl1.0.0 \
21+
&& rm -rf /var/lib/apt/lists/*
22+
23+
ENV GPG_KEY 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
24+
ENV PYTHON_VERSION 3.6.4
25+
26+
RUN set -ex \
27+
&& buildDeps=" \
28+
dpkg-dev \
29+
gcc \
30+
libbz2-dev \
31+
libc6-dev \
32+
libexpat1-dev \
33+
libffi-dev \
34+
libgdbm-dev \
35+
liblzma-dev \
36+
libncursesw5-dev \
37+
libreadline-dev \
38+
libsqlite3-dev \
39+
libssl-dev \
40+
make \
41+
tcl-dev \
42+
tk-dev \
43+
wget \
44+
xz-utils \
45+
zlib1g-dev \
46+
# as of Stretch, "gpg" is no longer included by default
47+
$(command -v gpg > /dev/null || echo 'gnupg dirmngr') \
48+
" \
49+
&& apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* \
50+
\
51+
&& wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \
52+
&& wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \
53+
&& export GNUPGHOME="$(mktemp -d)" \
54+
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY" \
55+
&& gpg --batch --verify python.tar.xz.asc python.tar.xz \
56+
&& rm -rf "$GNUPGHOME" python.tar.xz.asc \
57+
&& mkdir -p /usr/src/python \
58+
&& tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \
59+
&& rm python.tar.xz \
60+
\
61+
&& cd /usr/src/python \
62+
&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
63+
&& ./configure \
64+
--build="$gnuArch" \
65+
--enable-loadable-sqlite-extensions \
66+
--enable-shared \
67+
--with-system-expat \
68+
--with-system-ffi \
69+
--without-ensurepip \
70+
&& make -j "$(nproc)" \
71+
&& make install \
72+
&& ldconfig \
73+
\
74+
&& apt-get purge -y --auto-remove $buildDeps \
75+
\
76+
&& find /usr/local -depth \
77+
\( \
78+
\( -type d -a \( -name test -o -name tests \) \) \
79+
-o \
80+
\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \
81+
\) -exec rm -rf '{}' + \
82+
&& rm -rf /usr/src/python
83+
84+
# make some useful symlinks that are expected to exist
85+
RUN cd /usr/local/bin \
86+
&& ln -s idle3 idle \
87+
&& ln -s pydoc3 pydoc \
88+
&& ln -s python3 python \
89+
&& ln -s python3-config python-config
90+
91+
# if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value '<VERSION>'"
92+
ENV PYTHON_PIP_VERSION 9.0.3
93+
94+
RUN set -ex; \
95+
\
96+
apt-get update; \
97+
apt-get install -y --no-install-recommends wget; \
98+
rm -rf /var/lib/apt/lists/*; \
99+
\
100+
wget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \
101+
\
102+
apt-get purge -y --auto-remove wget; \
103+
\
104+
python get-pip.py \
105+
--disable-pip-version-check \
106+
--no-cache-dir \
107+
"pip==$PYTHON_PIP_VERSION" \
108+
; \
109+
pip --version; \
110+
\
111+
find /usr/local -depth \
112+
\( \
113+
\( -type d -a \( -name test -o -name tests \) \) \
114+
-o \
115+
\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \
116+
\) -exec rm -rf '{}' +; \
117+
rm -f get-pip.py
118+
3119

4120
RUN apt-get update && apt-get -y upgrade
5121

122+
# xvfb is used to do CPU based rendering of Unity
123+
RUN apt-get install -y xvfb
124+
125+
6126
ADD python/requirements.txt .
7127
RUN pip install --trusted-host pypi.python.org -r requirements.txt
8128

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ to the wider research and game developer communities.
2727
* Built-in support for Imitation Learning
2828
* Flexible Agent control with On Demand Decision Making
2929
* Visualizing network outputs within the environment
30-
* Simplified set-up with Docker (Experimental)
30+
* Simplified set-up with Docker
3131

3232
## Documentation and References
3333

docs/Installation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
To install and use ML-Agents, you need install Unity, clone this repository
44
and install Python with additional dependencies. Each of the subsections
5-
below overviews each step, in addition to an experimental Docker set-up.
5+
below overviews each step, in addition to a Docker set-up.
66

77
## Install **Unity 2017.1** or Later
88

@@ -51,7 +51,7 @@ and run from the command line:
5151

5252
pip3 install .
5353

54-
## Docker-based Installation (Experimental)
54+
## Docker-based Installation
5555

5656
If you'd like to use Docker for ML-Agents, please follow
5757
[this guide](Using-Docker.md).

docs/ML-Agents-Overview.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,7 @@ the broadcasting feature
428428
* **Docker Set-up (Experimental)** - To facilitate setting up ML-Agents
429429
without installing Python or TensorFlow directly, we provide a
430430
[guide](Using-Docker.md) on how
431-
to create and run a Docker container. Due to limitations on rendering visual
432-
observations, this feature is marked experimental.
431+
to create and run a Docker container.
433432

434433
* **Cloud Training on AWS** - To facilitate using ML-Agents on
435434
Amazon Web Services (AWS) machines, we provide a

docs/Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* [Background: TensorFlow](Background-TensorFlow.md)
88
* [Installation & Set-up](Installation.md)
99
* [Background: Jupyter Notebooks](Background-Jupyter.md)
10-
* [Docker Set-up (Experimental)](Using-Docker.md)
10+
* [Docker Set-up](Using-Docker.md)
1111
* [Getting Started with the 3D Balance Ball Environment](Getting-Started-with-Balance-Ball.md)
1212
* [Example Environments](Learning-Environment-Examples.md)
1313

docs/Using-Docker.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# Using Docker For ML-Agents (Experimental)
1+
# Using Docker For ML-Agents
2+
3+
We currently offer a solution for Windows and Mac users who would like to do training or inference using Docker. This option may be appealing to those who would like to avoid installing Python and TensorFlow themselves. The current setup forces both TensorFlow and Unity to _only_ rely on the CPU for computations. Consequently, our Docker simulation does not use a GPU and uses [`Xvfb`](https://en.wikipedia.org/wiki/Xvfb) to do visual rendering. `Xvfb` is a utility that enables `ML-Agents` (or any other application) to do rendering virtually i.e. it does not assume that the machine running `ML-Agents` has a GPU or a display attached to it. This means that rich environments which involve agents using camera-based visual observations might be slower.
24

3-
We currently offer an experimental solution for Windows and Mac users who would like to do training or inference using Docker. This option may be appealing to those who would like to avoid installing Python and TensorFlow themselves. The current setup forces both TensorFlow and Unity to _only_ rely on the CPU for computations. Consequently, our Docker support is limited to environments whose agents **do not** use camera-based visual observations. For example, the [GridWorld](Learning-Environment-Examples.md#gridworld) environment is **not** supported.
45

56
## Requirements
67
- Unity _Linux Build Support_ Component
@@ -26,13 +27,12 @@ Since Docker typically runs a container sharing a (linux) kernel with the host m
2627
Unity environment **has** to be built for the **linux platform**. When building a Unity environment, please select the following options from the the Build Settings window:
2728
- Set the _Target Platform_ to `Linux`
2829
- Set the _Architecture_ to `x86_64`
29-
- `Uncheck` the _Development Build_ option
30-
- `Check` the _Headless Mode_ option. (_This is required because the Unity binary will run in a container that does not have graphics drivers installed_.)
31-
32-
![Build Settings For Docker](images/docker_build_settings.png)
30+
- If the environment does not contain visual observations, you can select the `headless` option here.
3331

3432
Then click `Build`, pick an environment name (e.g. `3DBall`) and set the output directory to `unity-volume`. After building, ensure that the file `<environment-name>.x86_64` and subdirectory `<environment-name>_Data/` are created under `unity-volume`.
3533

34+
![Build Settings For Docker](images/docker_build_settings.png)
35+
3636
### Build the Docker Container
3737

3838
First, make sure the Docker engine is running on your machine. Then build the Docker container by calling the following command at the top-level of the repository:
@@ -66,7 +66,7 @@ For the `3DBall` environment, for example this would be:
6666
```
6767
docker run --name 3DBallContainer.first.trial \
6868
--mount type=bind,source="$(pwd)"/unity-volume,target=/unity-volume \
69-
balance.ball.v0.1:latest 3Dball \
69+
balance.ball.v0.1:latest 3DBall \
7070
--docker-target-name=unity-volume \
7171
--train \
7272
--run-id=3dball_first_trial
@@ -77,7 +77,7 @@ For more detail on Docker mounts, check out [these](https://docs.docker.com/stor
7777

7878
### Stopping Container and Saving State
7979

80-
If you are satisfied with the training progress, you can stop the Docker container while saving state using the following command:
80+
If you are satisfied with the training progress, you can stop the Docker container while saving state by either using `Ctrl+C` or `⌘+C` (Mac) or by using the following command:
8181

8282
```
8383
docker kill --signal=SIGINT <container-name>
290 KB
Loading

python/unityagents/environment.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class UnityEnvironment(object):
2424
def __init__(self, file_name, worker_id=0,
2525
base_port=5005, curriculum=None,
26-
seed=0):
26+
seed=0, docker_training=False):
2727
"""
2828
Starts a new unity environment and establishes a connection with the environment.
2929
Notice: Currently communication between Unity and Python takes place over an open socket without authentication.
@@ -32,6 +32,7 @@ def __init__(self, file_name, worker_id=0,
3232
:string file_name: Name of Unity environment binary.
3333
:int base_port: Baseline port number to connect to Unity environment over. worker_id increments over this.
3434
:int worker_id: Number to add to communication port (5005) [0]. Used for asynchronous agent scenarios.
35+
:param docker_training: Informs this class whether the process is being run within a container.
3536
"""
3637

3738
atexit.register(self.close)
@@ -95,11 +96,38 @@ def __init__(self, file_name, worker_id=0,
9596
else:
9697
logger.debug("This is the launch string {}".format(launch_string))
9798
# Launch Unity environment
98-
proc1 = subprocess.Popen(
99-
[launch_string,
100-
'--port', str(self.port),
101-
'--seed', str(seed)])
102-
99+
if docker_training == False:
100+
proc1 = subprocess.Popen(
101+
[launch_string,
102+
'--port', str(self.port),
103+
'--seed', str(seed)])
104+
else:
105+
"""
106+
Comments for future maintenance:
107+
xvfb-run is a wrapper around Xvfb, a virtual xserver where all
108+
rendering is done to virtual memory. It automatically creates a
109+
new virtual server automatically picking a server number `auto-servernum`.
110+
The server is passed the arguments using `server-args`, we are telling
111+
Xvfb to create Screen number 0 with width 640, height 480 and depth 24 bits.
112+
Note that 640 X 480 are the default width and height. The main reason for
113+
us to add this is because we'd like to change the depth from the default
114+
of 8 bits to 24.
115+
Unfortunately, this means that we will need to pass the arguments through
116+
a shell which is why we set `shell=True`. Now, this adds its own
117+
complications. E.g SIGINT can bounce off the shell and not get propagated
118+
to the child processes. This is why we add `exec`, so that the shell gets
119+
launched, the arguments are passed to `xvfb-run`. `exec` replaces the shell
120+
we created with `xvfb`.
121+
"""
122+
docker_ls = ("exec xvfb-run --auto-servernum"
123+
" --server-args='-screen 0 640x480x24'"
124+
" {0} --port {1} --seed {2}").format(launch_string,
125+
str(self.port),
126+
str(seed))
127+
proc1 = subprocess.Popen(docker_ls,
128+
stdout=subprocess.PIPE,
129+
stderr=subprocess.PIPE,
130+
shell=True)
103131
self._socket.settimeout(30)
104132
try:
105133
try:

python/unitytrainers/ppo/trainer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ def add_experiences(self, curr_all_info: AllBrainInfo, next_all_info: AllBrainIn
215215
:param next_all_info: Dictionary of all current brains and corresponding BrainInfo.
216216
:param take_action_outputs: The outputs of the take action method.
217217
"""
218-
219218
curr_info = curr_all_info[self.brain_name]
220219
next_info = next_all_info[self.brain_name]
221220

python/unitytrainers/trainer_controller.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ def __init__(self, env_path, run_id, save_freq, curriculum_file, fast_simulation
4242
.replace('.x86', '')) # Strip out executable extensions if passed
4343
# Recognize and use docker volume if one is passed as an argument
4444
if docker_target_name == '':
45+
self.docker_training = False
4546
self.model_path = './models/{run_id}'.format(run_id=run_id)
4647
self.curriculum_file = curriculum_file
4748
self.summaries_dir = './summaries'
4849
else:
50+
self.docker_training = True
4951
self.model_path = '/{docker_target_name}/models/{run_id}'.format(
5052
docker_target_name=docker_target_name,
5153
run_id=run_id)
@@ -74,7 +76,8 @@ def __init__(self, env_path, run_id, save_freq, curriculum_file, fast_simulation
7476
np.random.seed(self.seed)
7577
tf.set_random_seed(self.seed)
7678
self.env = UnityEnvironment(file_name=env_path, worker_id=self.worker_id,
77-
curriculum=self.curriculum_file, seed=self.seed)
79+
curriculum=self.curriculum_file, seed=self.seed,
80+
docker_training=self.docker_training)
7881
self.env_name = os.path.basename(os.path.normpath(env_path)) # Extract out name of environment
7982

8083
def _get_progress(self):

0 commit comments

Comments
 (0)