Skip to content

Commit bf1cc2f

Browse files
feat: Implement Multi-OS Runner Environment
This commit introduces a multi-OS runner environment, allowing you to select the operating system (Alpine, Ubuntu, or Debian) for the code execution runner service at launch time. Key changes: - Replaced the generic `src/runner/Dockerfile` with three OS-specific Dockerfiles: - `src/runner/Dockerfile.alpine` (based on `node:18-alpine`) - `src/runner/Dockerfile.ubuntu` (based on `node:18-slim`) - `src/runner/Dockerfile.debian` (based on `node:18-slim`) - All Dockerfiles now include a consistent set of software (basic tools, scripting languages, and global Node.js tools) as was present in the original generic Dockerfile. - Updated `docker-compose.yml` to use Docker Compose profiles for selecting tenancy mode and runner OS. - Introduced YAML anchors (`x-runner-builder-base`, `x-orchestrator-base`) for cleaner configuration. - Defined separate builder services (`runner-builder-alpine`, `runner-builder-ubuntu`, `runner-builder-debian`) for each OS, all tagging the output image as `runner-image`. - Created a matrix of orchestrator services (`orchestrator-{singletenant|multitenant}-{alpine|ubuntu|debian}`) to cover all combinations, each depending on the appropriate builder. - Updated the `web-frontend` service to depend on all possible orchestrator services. - Updated `README.md` to reflect the new Docker Compose launch commands, profile usage, and the new Dockerfile structure. This change is managed at the infrastructure level (Docker Compose and Dockerfiles) and does not require modifications to the orchestrator or runner application source code. You can now launch the environment with commands like: `docker-compose --profile singletenant --profile ubuntu up --build` `docker-compose --profile multitenant --profile alpine up --build`
1 parent 7631ccf commit bf1cc2f

File tree

6 files changed

+246
-136
lines changed

6 files changed

+246
-136
lines changed

README.md

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ The repository is organized as follows:
4343

4444
* `src/app/`: Contains the frontend Angular application source code.
4545
* `src/orchestrator/`: The Node.js orchestration service used in multitenant mode to manage Docker runners.
46-
* `src/runner/`: The Node.js execution service that processes code snippets.
47-
* `docker-compose.yml`: Defines the services and profiles for running the application in different modes.
46+
* `src/runner/`: The Node.js execution service that processes code snippets. This directory now contains OS-specific Dockerfiles (`Dockerfile.alpine`, `Dockerfile.debian`, `Dockerfile.ubuntu`) to build runner images for different Linux distributions.
47+
* `docker-compose.yml`: Defines the services and profiles for running the application in different modes and with different OS runners.
4848
* `package.json`: Lists the dependencies and scripts for the frontend.
4949
* `.github/workflows/ci.yml`: Defines the Continuous Integration (CI) workflow for building and testing the project.
5050

@@ -58,41 +58,60 @@ Before you start, make sure you have the following installed:
5858

5959
## Building the Runner Image
6060

61-
The `runner-image`, named `runner-image` as defined in `docker-compose.yml`, is crucial for the application's operation. It contains the necessary environment for code execution and is used by the orchestrator service in multitenant mode to create isolated runner instances for each user session. This image is automatically built by the `runner-builder` service within Docker Compose when you launch the application using either the `multitenant` or `singletenant` profile.
61+
The `runner-image`, named `runner-image` as defined in `docker-compose.yml`, is crucial for the application's operation. It contains the necessary environment for code execution.
62+
This image is automatically built by one of the `runner-builder-*` services (e.g., `runner-builder-alpine`) within Docker Compose when you launch the application. The specific builder service depends on the selected OS profile (alpine, ubuntu, or debian).
6263

6364
## How to Launch the Application
6465

65-
You can launch the application in either **multitenant** or **singletenant** mode using Docker Compose profiles.
66+
You can launch the application using Docker Compose profiles, selecting the tenancy mode (multitenant or singletenant) and the desired runner OS (alpine, ubuntu, or debian).
6667

67-
### Multitenant Mode
68+
The application now supports different runner operating systems. The `src/runner` directory contains three Dockerfiles:
69+
- `Dockerfile.alpine`: For building a runner image based on Alpine Linux.
70+
- `Dockerfile.ubuntu`: For building a runner image based on Ubuntu.
71+
- `Dockerfile.debian`: For building a runner image based on Debian.
6872

69-
This mode is ideal for production or for tests that require complete session isolation. It uses the `orchestrator` service to dynamically create and manage a `runner` Docker container for each user session.
73+
The `docker-compose.yml` file has been updated to support these different OS runners through profiles.
7074

71-
1. **Start the services with the `multitenant` profile:**
75+
### Launching Examples:
76+
77+
**Multitenant Mode with Alpine Runner:**
78+
79+
This mode is ideal for production or for tests that require complete session isolation, using an Alpine-based runner.
80+
81+
1. **Start the services with the `multitenant` and `alpine` profiles:**
7282
```bash
73-
docker-compose --profile multitenant up --build
83+
docker-compose --profile multitenant --profile alpine up --build
7484
```
85+
2. This command will start:
86+
* `web-frontend`: The Angular application, accessible at `http://localhost:4200`.
87+
* `orchestrator-multitenant-alpine`: The Node.js orchestrator service. It manages separate Docker runner instances (Alpine-based) for each user session.
88+
* `runner-builder-alpine`: This service builds the `runner-image` using `src/runner/Dockerfile.alpine` and then exits.
7589

76-
2. This command will start the following services:
77-
* `frontend-multitenant`: The Angular application, accessible at `http://localhost:80`.
78-
* `orchestrator-multitenant`: The Node.js orchestrator service (see `src/orchestrator/README.md`). Listens on port `3001` and manages a separate Docker runner instance for each user session (`REUSE_RUNNER_MODE=false` is set for this service in `docker-compose.yml`). It can be configured with the `FREE_DOORS` environment variable to enable arbitrary port (maximum 20) mapping to the runner containers.
79-
* `runner-builder`: This service builds the `runner-image` (see `src/runner/README.md`) and then exits. The `runner-image` includes the necessary configurations for port mapping when `FREE_DOORS` is utilized.
80-
81-
### Singletenant Mode
90+
**Singletenant Mode with Ubuntu Runner:**
8291

83-
This mode is simpler and suitable for local development. It uses a single runner instance, managed by the orchestrator service configured appropriately.
92+
This mode is simpler, suitable for local development, using an Ubuntu-based runner.
8493

85-
1. **Start the services with the `singletenant` profile:**
94+
1. **Start the services with the `singletenant` and `ubuntu` profiles:**
8695
```bash
87-
docker-compose --profile singletenant up --build
96+
docker-compose --profile singletenant --profile ubuntu up --build
8897
```
98+
2. This command will start:
99+
* `web-frontend`: The Angular application, accessible at `http://localhost:4200`.
100+
* `orchestrator-singletenant-ubuntu`: The Node.js orchestrator service. It manages a single, shared Ubuntu-based runner instance for all users.
101+
* `runner-builder-ubuntu`: This service builds the `runner-image` using `src/runner/Dockerfile.ubuntu` and then exits.
102+
103+
**Other Combinations:**
104+
105+
You can combine profiles similarly for other OS and tenancy mode requirements:
106+
107+
* Multitenant Debian: `docker-compose --profile multitenant --profile debian up --build`
108+
* Singletenant Alpine: `docker-compose --profile singletenant --profile alpine up --build`
109+
* Singletenant Debian: `docker-compose --profile singletenant --profile debian up --build`
89110

90-
2. This command will start the following services:
91-
* `frontend-singletenant`: The Angular application, accessible at `http://localhost:80`.
92-
* `orchestrator-singletenant`: The Node.js orchestrator service. For this profile, it listens on port `3001` and is configured via `docker-compose.yml` to use `REUSE_RUNNER_MODE=true`, meaning it manages a single, shared runner instance for all users.
93-
* `runner-builder`: This service builds the `runner-image` and then exits.
111+
Once the services are running, you can access the application by opening your browser and navigating to `http://localhost:4200` (note the port change for the frontend).
94112

95-
Once the services are running, you can access the application by opening your browser and navigating to `http://localhost:80`.
113+
The orchestrator services (`orchestrator-*-*`) listen on port `3000` and can be configured with the `FREE_DOORS` environment variable for arbitrary port mapping to the runner containers.
114+
The `runner-image` built by the respective `runner-builder-*` service includes the necessary configurations for port mapping when `FREE_DOORS` is utilized.
96115

97116
## Development Scripts
98117

docker-compose.yml

Lines changed: 117 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,131 @@
1-
x-frontend-common: &frontend-common
1+
# Ancore YAML per non ripetere codice (DRY principle)
2+
x-runner-builder-base: &runner-builder-base
23
build:
3-
context: .
4-
dockerfile: ./Dockerfile
5-
args:
6-
production: true
7-
orchestrator: 'http://localhost:4001'
8-
#WARNING: specify the orchestrator address
9-
image: taylored-snippets-web/frontend:latest
10-
ports:
11-
- "80:80"
4+
context: src/runner
125

13-
x-orchestrator-common: &orchestrator-common
14-
build:
15-
context: ./src/orchestrator
16-
dockerfile: ./Dockerfile
17-
image: taylored-snippets-web/orchestrator:latest
18-
ports:
19-
- "3001:3001"
20-
volumes:
21-
- /var/run/docker.sock:/var/run/docker.sock
22-
command: node orchestrator.js
23-
# depends_on will be added specifically for each profile
6+
x-orchestrator-base: &orchestrator-base
7+
build: ./src/orchestrator
8+
restart: unless-stopped
249

2510
services:
26-
# --- SINGLETENANT PROFILE SERVICES ---
27-
frontend-singletenant:
28-
<<: *frontend-common
29-
profiles: ["singletenant"]
11+
# === BUILDERS ===
12+
runner-builder-alpine:
13+
<<: *runner-builder-base
14+
image: runner-image
15+
profiles: ["alpine"]
16+
build:
17+
dockerfile: Dockerfile.alpine
18+
19+
runner-builder-ubuntu:
20+
<<: *runner-builder-base
21+
image: runner-image
22+
profiles: ["ubuntu"]
23+
build:
24+
dockerfile: Dockerfile.ubuntu
25+
26+
runner-builder-debian:
27+
<<: *runner-builder-base
28+
image: runner-image
29+
profiles: ["debian"]
30+
build:
31+
dockerfile: Dockerfile.debian
32+
33+
# === ORCHESTRATORS ===
34+
# --- Single Tenant ---
35+
orchestrator-singletenant-alpine:
36+
<<: *orchestrator-base
37+
container_name: orchestrator-singletenant
38+
profiles: ["singletenant", "alpine"]
39+
environment:
40+
- TENANCY_MODE=singletenant
41+
- RUNNER_IMAGE=runner-image
3042
depends_on:
31-
orchestrator-singletenant:
32-
condition: service_started
43+
runner-builder-alpine:
44+
condition: service_completed_successfully
45+
ports:
46+
- "3000:3000"
3347

34-
# --- MULTITENANT PROFILE SERVICES ---
35-
frontend-multitenant:
36-
<<: *frontend-common
37-
profiles: ["multitenant"]
48+
orchestrator-singletenant-ubuntu:
49+
<<: *orchestrator-base
50+
container_name: orchestrator-singletenant
51+
profiles: ["singletenant", "ubuntu"]
52+
environment:
53+
- TENANCY_MODE=singletenant
54+
- RUNNER_IMAGE=runner-image
3855
depends_on:
39-
orchestrator-multitenant:
40-
condition: service_started
56+
runner-builder-ubuntu:
57+
condition: service_completed_successfully
58+
ports:
59+
- "3000:3000"
4160

42-
orchestrator-multitenant:
43-
<<: *orchestrator-common
61+
orchestrator-singletenant-debian:
62+
<<: *orchestrator-base
63+
container_name: orchestrator-singletenant
64+
profiles: ["singletenant", "debian"]
4465
environment:
45-
- NODE_ENV=production
46-
- REUSE_RUNNER_MODE=false
47-
- RUNNERS_HOST=http://localhost
48-
#WARNING: specify the RUNNERS_HOST address
49-
profiles: ["multitenant"]
66+
- TENANCY_MODE=singletenant
67+
- RUNNER_IMAGE=runner-image
5068
depends_on:
51-
runner-builder:
52-
condition: service_started
69+
runner-builder-debian:
70+
condition: service_completed_successfully
71+
ports:
72+
- "3000:3000"
5373

54-
orchestrator-singletenant:
55-
<<: *orchestrator-common
74+
# --- Multi Tenant ---
75+
orchestrator-multitenant-alpine:
76+
<<: *orchestrator-base
77+
container_name: orchestrator-multitenant
78+
profiles: ["multitenant", "alpine"]
5679
environment:
57-
- NODE_ENV=production
58-
- REUSE_RUNNER_MODE=true
59-
- FREE_DOORS=0 #max 20
60-
profiles: ["singletenant"]
80+
- TENANCY_MODE=multitenant
81+
- RUNNER_IMAGE=runner-image
6182
depends_on:
62-
runner-builder:
63-
condition: service_started
83+
runner-builder-alpine:
84+
condition: service_completed_successfully
85+
ports:
86+
- "3000:3000"
6487

65-
# --- RUNNER SERVICE ---
66-
runner-builder:
67-
build:
68-
context: ./src/runner
69-
dockerfile: ./Dockerfile
70-
# This image name is crucial. The orchestrator will use it to start new runners.
71-
image: runner-image
72-
# No profiles, ports, environment, or command for a builder service
88+
orchestrator-multitenant-ubuntu:
89+
<<: *orchestrator-base
90+
container_name: orchestrator-multitenant
91+
profiles: ["multitenant", "ubuntu"]
92+
environment:
93+
- TENANCY_MODE=multitenant
94+
- RUNNER_IMAGE=runner-image
95+
depends_on:
96+
runner-builder-ubuntu:
97+
condition: service_completed_successfully
98+
ports:
99+
- "3000:3000"
100+
101+
orchestrator-multitenant-debian:
102+
<<: *orchestrator-base
103+
container_name: orchestrator-multitenant
104+
profiles: ["multitenant", "debian"]
105+
environment:
106+
- TENANCY_MODE=multitenant
107+
- RUNNER_IMAGE=runner-image
108+
depends_on:
109+
runner-builder-debian:
110+
condition: service_completed_successfully
111+
ports:
112+
- "3000:3000"
113+
114+
# === FRONTEND ===
115+
web-frontend:
116+
image: nginx:1.25.3-alpine
117+
profiles:
118+
- singletenant
119+
- multitenant
120+
ports:
121+
- "4200:80"
122+
volumes:
123+
- ./nginx.conf:/etc/nginx/nginx.conf
124+
- ./dist/taylored-snippets-web:/usr/share/nginx/html
125+
depends_on:
126+
- orchestrator-singletenant-alpine
127+
- orchestrator-singletenant-ubuntu
128+
- orchestrator-singletenant-debian
129+
- orchestrator-multitenant-alpine
130+
- orchestrator-multitenant-ubuntu
131+
- orchestrator-multitenant-debian

src/runner/Dockerfile

Lines changed: 0 additions & 56 deletions
This file was deleted.

src/runner/Dockerfile.alpine

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM node:18-alpine
2+
3+
# Install basic tools and shells
4+
RUN apk add --no-cache bash zsh tcsh curl gnupg ca-certificates git
5+
6+
# Install scripting languages and runtime environments
7+
RUN apk add --no-cache python3 py3-pip perl ruby php openjdk21-jdk lua5.4 R gawk tcl expect
8+
9+
# Install global Node.js tools
10+
RUN npm install -g typescript ts-node taylored
11+
12+
WORKDIR /usr/src/runner
13+
COPY package*.json ./
14+
RUN npm ci
15+
COPY . .
16+
CMD ["node", "runner.js"]

src/runner/Dockerfile.debian

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
FROM node:18-slim
2+
3+
# Install basic tools and shells
4+
RUN apt-get update && apt-get install -y --no-install-recommends \
5+
bash \
6+
zsh \
7+
tcsh \
8+
curl \
9+
gnupg \
10+
ca-certificates \
11+
git \
12+
&& rm -rf /var/lib/apt/lists/*
13+
14+
# Install scripting languages and runtime environments
15+
RUN apt-get update && apt-get install -y --no-install-recommends \
16+
python3 \
17+
python3-pip \
18+
perl \
19+
ruby \
20+
php-cli \
21+
openjdk-21-jdk-headless \
22+
lua5.4 \
23+
r-base \
24+
gawk \
25+
tcl \
26+
expect \
27+
&& rm -rf /var/lib/apt/lists/*
28+
29+
# Install global Node.js tools
30+
RUN npm install -g typescript ts-node taylored
31+
32+
WORKDIR /usr/src/runner
33+
COPY package*.json ./
34+
RUN npm ci
35+
COPY . .
36+
CMD ["node", "runner.js"]

0 commit comments

Comments
 (0)