Skip to content

Commit

Permalink
chore: Add docker files for developing inside container. (All-Hands-A…
Browse files Browse the repository at this point in the history
  • Loading branch information
qiangli authored Sep 17, 2024
1 parent 8a419b5 commit f7ebc1c
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 4 deletions.
25 changes: 25 additions & 0 deletions Development.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,28 @@ Please refer to [this README](./tests/integration/README.md) for details.
### 9. Add or update dependency
1. Add your dependency in `pyproject.toml` or use `poetry add xxx`
2. Update the poetry.lock file via `poetry lock --no-update`
## Develop inside Docker container
TL;DR
```bash
make docker-dev
```
See more details [here](./containers/dev/README.md)
If you are just interested in running `OpenHands` without installing all the required tools on your host.
```bash
make docker-run
```
If you do not have `make` on your host, run:
```bash
cd ./containers/dev
./dev.sh
```
You do need [Docker](https://docs.docker.com/engine/install/) installed on your host though.
36 changes: 32 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ SHELL=/bin/bash
# Makefile for OpenHands project

# Variables
BACKEND_HOST ?= "127.0.0.1"
BACKEND_PORT = 3000
BACKEND_HOST = "127.0.0.1:$(BACKEND_PORT)"
BACKEND_HOST_PORT = "$(BACKEND_HOST):$(BACKEND_PORT)"
FRONTEND_PORT = 3001
DEFAULT_WORKSPACE_DIR = "./workspace"
DEFAULT_MODEL = "gpt-4o"
Expand Down Expand Up @@ -189,12 +190,12 @@ build-frontend:
# Start backend
start-backend:
@echo "$(YELLOW)Starting backend...$(RESET)"
@poetry run uvicorn openhands.server.listen:app --port $(BACKEND_PORT) --reload --reload-exclude "workspace/*"
@poetry run uvicorn openhands.server.listen:app --host $(BACKEND_HOST) --port $(BACKEND_PORT) --reload --reload-exclude "workspace/*"

# Start frontend
start-frontend:
@echo "$(YELLOW)Starting frontend...$(RESET)"
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run start
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST_PORT) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run start

# Common setup for running the app (non-callable)
_run_setup:
Expand All @@ -204,7 +205,7 @@ _run_setup:
fi
@mkdir -p logs
@echo "$(YELLOW)Starting backend server...$(RESET)"
@poetry run uvicorn openhands.server.listen:app --port $(BACKEND_PORT) &
@poetry run uvicorn openhands.server.listen:app --host $(BACKEND_HOST) --port $(BACKEND_PORT) &
@echo "$(YELLOW)Waiting for the backend to start...$(RESET)"
@until nc -z localhost $(BACKEND_PORT); do sleep 0.1; done
@echo "$(GREEN)Backend started successfully.$(RESET)"
Expand All @@ -216,6 +217,20 @@ run:
@cd frontend && echo "$(BLUE)Starting frontend with npm...$(RESET)" && npm run start -- --port $(FRONTEND_PORT)
@echo "$(GREEN)Application started successfully.$(RESET)"

# Run the app (in docker)
docker-run: WORKSPACE_BASE ?= $(PWD)/workspace
docker-run:
@if [ -f /.dockerenv ]; then \
echo "Running inside a Docker container. Exiting..."; \
exit 0; \
else \
echo "$(YELLOW)Running the app in Docker $(OPTIONS)...$(RESET)"; \
export WORKSPACE_BASE=${WORKSPACE_BASE}; \
export SANDBOX_USER_ID=$(shell id -u); \
export DATE=$(shell date +%Y%m%d%H%M%S); \
docker compose up $(OPTIONS); \
fi

# Run the app (WSL mode)
run-wsl:
@echo "$(YELLOW)Running the app in WSL mode...$(RESET)"
Expand Down Expand Up @@ -280,6 +295,16 @@ setup-config-prompts:
fi


# Develop in container
docker-dev:
@if [ -f /.dockerenv ]; then \
echo "Running inside a Docker container. Exiting..."; \
exit 0; \
else \
echo "$(YELLOW)Build and run in Docker $(OPTIONS)...$(RESET)"; \
./containers/dev/dev.sh $(OPTIONS); \
fi

# Clean up all caches
clean:
@echo "$(YELLOW)Cleaning up caches...$(RESET)"
Expand All @@ -298,7 +323,10 @@ help:
@echo " $(GREEN)start-frontend$(RESET) - Start the frontend server for the OpenHands project."
@echo " $(GREEN)run$(RESET) - Run the OpenHands application, starting both backend and frontend servers."
@echo " Backend Log file will be stored in the 'logs' directory."
@echo " $(GREEN)docker-dev$(RESET) - Build and run the OpenHands application in Docker."
@echo " $(GREEN)docker-run$(RESET) - Run the OpenHands application, starting both backend and frontend servers in Docker."
@echo " $(GREEN)help$(RESET) - Display this help message, providing information on available targets."

# Phony targets
.PHONY: build check-dependencies check-python check-npm check-docker check-poetry install-python-dependencies install-frontend-dependencies install-pre-commit-hooks lint start-backend start-frontend run run-wsl setup-config setup-config-prompts help
.PHONY: docker-dev docker-run
22 changes: 22 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
services:
openhands:
build:
context: ./
dockerfile: ./containers/app/Dockerfile
image: openhands:latest
container_name: openhands-app-${DATE:-}
environment:
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.9-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:
- "3000:3000"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${WORKSPACE_BASE:-$PWD/workspace}:/opt/workspace_base
pull_policy: build
stdin_open: true
tty: true
124 changes: 124 additions & 0 deletions containers/dev/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# syntax=docker/dockerfile:1

###
FROM ubuntu:22.04 AS dind

# https://docs.docker.com/engine/install/ubuntu/
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
&& chmod a+r /etc/apt/keyrings/docker.asc \
&& echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

RUN apt-get update && apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean \
&& apt-get autoremove -y

###
FROM dind AS openhands

ENV DEBIAN_FRONTEND=noninteractive

#
RUN apt-get update && apt-get install -y \
bash \
build-essential \
curl \
git \
git-lfs \
software-properties-common \
make \
netcat \
sudo \
wget \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean \
&& apt-get autoremove -y

# https://github.com/cli/cli/blob/trunk/docs/install_linux.md
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& apt-get update && apt-get -y install \
gh \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean \
&& apt-get autoremove -y

# Python 3.11
RUN add-apt-repository ppa:deadsnakes/ppa \
&& apt-get update \
&& apt-get install -y python3.11 python3.11-venv python3.11-dev python3-pip \
&& ln -s /usr/bin/python3.11 /usr/bin/python

# NodeJS >= 18.17.1
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs

# Poetry >= 1.8
RUN curl -fsSL https://install.python-poetry.org | python3.11 - \
&& ln -s ~/.local/bin/poetry /usr/local/bin/poetry

#
RUN <<EOF
#!/bin/bash
printf "#!/bin/bash
set +x
uname -a
docker --version
gh --version | head -n 1
git --version
#
python --version
echo node `node --version`
echo npm `npm --version`
poetry --version
netcat -h 2>&1 | head -n 1
" > /version.sh
chmod a+x /version.sh
EOF

###
FROM openhands AS dev

RUN apt-get update && apt-get install -y \
dnsutils \
file \
iproute2 \
jq \
lsof \
ripgrep \
silversearcher-ag \
vim \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean \
&& apt-get autoremove -y

WORKDIR /app

# cache build dependencies
RUN \
--mount=type=bind,source=./,target=/app/ \
<<EOF
#!/bin/bash
make -s clean
make -s check-dependencies
make -s install-python-dependencies

# NOTE
# node_modules are .dockerignore-d therefore not mountable
# make -s install-frontend-dependencies
EOF

#
CMD ["bash"]
54 changes: 54 additions & 0 deletions containers/dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Develop in Docker

Install [Docker](https://docs.docker.com/engine/install/) on your host machine and run:

```bash
make docker-dev
# same as:
cd ./containers/dev
./dev.sh
```

It could take some time if you are running for the first time as Docker will pull all the tools required for building OpenHands. The next time you run again, it should be instant.

## Build and run

If everything goes well, you should be inside a container after Docker finishes building the `openhands:dev` image similar to the following:

```bash
Build and run in Docker ...
root@93fc0005fcd2:/app#
```

You may now proceed with the normal [build and run](../../Development.md) workflow as if you were on the host.

## Make changes

The source code on the host is mounted as `/app` inside docker. You may edit the files as usual either inside the Docker container or on your host with your favorite IDE/editors.

The following are also mapped as readonly from your host:

```yaml
# host credentials
- $HOME/.git-credentials:/root/.git-credentials:ro
- $HOME/.gitconfig:/root/.gitconfig:ro
- $HOME/.npmrc:/root/.npmrc:ro
```
## VSCode
Alternatively, if you use VSCode, you could also [attach to the running container](https://code.visualstudio.com/docs/devcontainers/attach-container).
See details for [developing in docker](https://code.visualstudio.com/docs/devcontainers/containers) or simply ask `OpenHands` ;-)

## Rebuild dev image

You could optionally pass additional options to the build script.

```bash
make docker-dev OPTIONS="--build"
# or
./containers/dev/dev.sh --build
```

See [docker compose run](https://docs.docker.com/reference/cli/docker/compose/run/) for more options.
38 changes: 38 additions & 0 deletions containers/dev/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#
services:
dev:
privileged: true
build:
context: ${OPENHANDS_WORKSPACE:-../../}
dockerfile: ./containers/dev/Dockerfile
image: openhands:dev
container_name: openhands-dev
environment:
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
- SANDBOX_API_HOSTNAME=host.docker.internal
#
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.9-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:
- "3000:3000"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${WORKSPACE_BASE:-$PWD/workspace}:/opt/workspace_base
# source code
- ${OPENHANDS_WORKSPACE:-../../}:/app
# host credentials
- $HOME/.git-credentials:/root/.git-credentials:ro
- $HOME/.gitconfig:/root/.gitconfig:ro
- $HOME/.npmrc:/root/.npmrc:ro
# cache
- cache-data:/root/.cache
pull_policy: never
stdin_open: true
tty: true

##
volumes:
cache-data:
39 changes: 39 additions & 0 deletions containers/dev/dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash
set -o pipefail

function get_docker() {
echo "Docker is required to build and run OpenHands."
echo "https://docs.docker.com/get-started/get-docker/"
exit 1
}

function check_tools() {
command -v docker &>/dev/null || get_docker
}

function exit_if_indocker() {
if [ -f /.dockerenv ]; then
echo "Running inside a Docker container. Exiting..."
exit 1
fi
}

#
exit_if_indocker

check_tools

##
OPENHANDS_WORKSPACE=$(git rev-parse --show-toplevel)

cd "$OPENHANDS_WORKSPACE/containers/dev/" || exit 1

##
export BACKEND_HOST="0.0.0.0"
#
export SANDBOX_USER_ID=$(id -u)
export WORKSPACE_BASE=${WORKSPACE_BASE:-$OPENHANDS_WORKSPACE/workspace}

docker compose run --rm --service-ports "$@" dev

##

0 comments on commit f7ebc1c

Please sign in to comment.