Skip to content

Commit

Permalink
Merge pull request #12 from LiquidPL/docker
Browse files Browse the repository at this point in the history
Add Dockerfile and CI workflows building Docker images on new releases
  • Loading branch information
twalen authored Apr 20, 2023
2 parents 307cc0d + a7e824a commit 363f16a
Show file tree
Hide file tree
Showing 28 changed files with 493 additions and 148 deletions.
41 changes: 41 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# /rst/source/sections/modules.rst
/build/
/dist/
# /test_report.html
# /test_report/
# /test_log.txt
# /test_screenshots.tar.gz
/.coverage
/cover/
**/*.egg
**/*.egg-info
**/*.pid
**/.DS_Store
**/*.pyc
**/__pycache__/
**/*.kdev*
**/*~
/.tox/
/.pytest_cache/
/.eggs/
**/*.swp
/deployment/
/venv*/
/.idea/

# docker specific

/Dockerfile*
/docker*
!docker-entrypoint.sh
/.dockerignore
/.git
/.gitignore
/.github
/extra/
/test*
/*.rst
/rst/

/docker/
/rst/
42 changes: 42 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Build and publish Docker image

on:
push:
tags:
- '*'

jobs:
test:
uses: ./.github/workflows/tests-reusable.yml
with:
python-version: '3.7'

build:
name: Build image
runs-on: ubuntu-latest
needs: test
permissions:
contents: read
packages: write

steps:
- name: Login to container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata for Docker
id: metadata
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository_owner }}/sioworkers

- name: Build and publish image
uses: docker/build-push-action@v3
with:
platforms: linux/amd64
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Tests

on:
push:
branches:
- master
pull_request:

jobs:
test:
strategy:
fail-fast: false
matrix:
python-version: ['3.7', '3.8']

uses: './.github/workflows/tests-reusable.yml'
with:
python-version: ${{ matrix.python-version }}
62 changes: 62 additions & 0 deletions .github/workflows/tests-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Tests

on:
workflow_call:
inputs:
python-version:
required: true
type: string

jobs:
test:
name: Run tests
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}

- name: Setup Java 8
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '8'

- name: Install apt dependencies
run: |
sudo apt-get update
sudo apt-get install libdb-dev fp-compiler fp-units-base fp-units-math
- name: Cache Python dependencies
uses: actions/cache@v3
env:
cache-name: 'cache-pip'
with:
path: ~/.cache/pip
key: ${{ runner.os }}-dev-${{ env.cache-name }}-${{ inputs.python-version }}-${{ hashFiles('**/setup.py') }}
restore-keys: |
${{ runner.os }}-dev-${{ env.cache-name }}-${{ inputs.python-version }}-${{ hashFiles('**/setup.py') }}
${{ runner.os }}-dev-${{ env.cache-name }}-${{ inputs.python-version }}-
${{ runner.os }}-dev-
${{ runner.os }}-
- name: Install Python dependencies
run: |
pip install --user virtualenv
virtualenv venv
. venv/bin/activate
pip install -e .[dev]
- name: Run tests
env:
TEST_SANDBOXES: '1'
NO_JAVA_TESTS: '0'
NO_SIO2JAIL_TESTS: '1'
run: |
. venv/bin/activate
pytest -v
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
twisted/plugins/dropin.cache
config/supervisord.conf
config/supervisord-conf-vars.conf
config/logging.json
48 changes: 48 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
FROM python:3.7 as build

ENV PYTHONUNBUFFERED 1

RUN useradd -m oioioi \
&& mkdir -p /sio2/sioworkers \
&& chown -R oioioi:oioioi /sio2

USER oioioi
WORKDIR /sio2

RUN pip install --user virtualenv \
&& /home/oioioi/.local/bin/virtualenv -p python3.7 venv

COPY --chown=oioioi:oioioi setup.py setup.cfg /sio2/sioworkers/
COPY --chown=oioioi:oioioi sio /sio2/sioworkers/sio
COPY --chown=oioioi:oioioi twisted /sio2/sioworkers/twisted

WORKDIR /sio2/sioworkers

RUN . /sio2/venv/bin/activate \
&& pip install .

FROM python:3.7 AS production

ENV PYTHONUNBUFFERED 1

RUN useradd -m oioioi \
&& mkdir -p /sio2/sioworkers \
&& chown -R oioioi:oioioi /sio2

COPY --from=build --chown=oioioi:oioioi /sio2/venv /sio2/venv

COPY --chown=oioioi:oioioi config/supervisord.conf.example /sio2/sioworkers/config/supervisord.conf
COPY --chown=oioioi:oioioi config/supervisord-conf-vars.conf.docker /sio2/sioworkers/config/supervisord-conf-vars.conf
COPY --chown=oioioi:oioioi config/logging.json.example /sio2/sioworkers/config/logging.json
COPY --chown=oioioi:oioioi supervisor.sh /sio2/sioworkers

COPY --chown=oioioi:oioioi docker-entrypoint.sh /sio2

USER oioioi
WORKDIR /sio2/sioworkers

ENV SIOWORKERSD_HOST="web"

ENTRYPOINT [ "/sio2/docker-entrypoint.sh" ]

CMD [ "/sio2/sioworkers/supervisor.sh", "startfg" ]
104 changes: 85 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,93 @@
# INSTALLATION
# sioworkers

### for python 2 installation ###
pip install -r requirements.txt
`sioworkers` is the task runner used by [SIO2](https://github.com/sio2project/oioioi) - the platform for running algorithmic/competitive programming contests. It handles all kinds of asynchronously run jobs - ranging from compiling submissions, to executing them in a supervised, sandboxed environment.

python setup.py install
# Installation

### for python 3 installation ###
pip install -r requirements_py3.txt
```
$ pip install . # for production deployments
$ pip install .[dev] # with development dependencies
```

python setup.py install
# Tests

# TESTS
All tests in this project are being managed with `tox`, which is simply invoked by running:

### to run all tests ###
`tox`
in main directory
```console
$ tox
```

### to run twisted tests (python2) ###
run:
trial sio.sioworkersd.twisted_t
in the directory of installation
in the main directory.

### to run twisted tests (python3) ###
run:
trial sio/sioworkersd/twisted_t
in the directory of installation
Alternatively you can also invoke all the tests directly.

```console
$ TEST_SANDBOXES=1 NO_JAVA_TESTS=1 NO_SIO2JAIL_TESTS=1 pytest -v .
```
This allows you to enable/disable sandboxed, Java, and Sio2Jail tests respectively.
Note that Sio2Jail requires the CPU performance counters to be exposed to the system to work.
This usually isn't the case on VPS servers and on public/free continuous integration services,
which will cause the tests to fail. It is recommended to skip testing Sio2Jail in those cases.

# Docker

An official Docker image for sioworkers is available at https://hub.docker.com/r/sio2project/sioworkers.

```console
$ docker run --rm \
--network=sio2-network \
--cap-add=ALL \
--privileged \
-e "SIOWORKERSD_HOST=oioioi" \
-e "WORKER_ALLOW_RUN_CPU_EXEC=true" \
-e "WORKER_CONCURRENCY=1" \
-e "WORKER_RAM=1024" \
--memory="1152m" \
--cpus=2.0 \
sio2project/sioworkers:latest
```

Notes:
* `--privileged` is only needed if Sio2Jail is used for judging submissions (ie. `WORKER_ALLOW_RUN_CPU_EXEC` is set to `true`),
* You can limit the memory/CPUs available to the container how you usually would in the container runtime of your choice,
the container will determine how many workers it should expose to OIOIOI based on that.
* You can also manually override the amount of available workers/memory by specifying the `WORKER_CONCURRENCY`
and `WORKER_RAM` (in MiB) environment variables.
* 128 MiB is reserved for processes in the container other than the submission being judged. That is, if you want
the maximum memory available to a judged program to be 1024 MiB, limit the container's memory to
128 MiB + (number of workers) * 1024 MiB.

Equivalent Docker Compose configuration:

```yaml
version: '3.8'

...

worker:
image: sio2project/sioworkers:latest
deploy:
resources:
limits:
cpus: '2'
memory: 1152m
cap_add:
- ALL
privileged: true
environment:
SIOWORKERSD_HOST: 'web'
WORKER_ALLOW_RUN_CPU_EXEC: 'true'
# these *will* override any automatic detection of available
# memory/cpu cores based on container limits!
WORKER_CONCURRENCY: '1'
WORKER_RAM: '1024'
```
## Environment variables
The container exposes two environment variables, from which only `SIOWORKERSD_HOST` is required.

* `SIOWORKERSD_HOST` - name of the host on which the `sioworkersd` service is available (usually the same as the main OIOIOI instance)
* `WORKER_ALLOW_RUN_CPU_EXEC` - marks this worker as suitable for judging directly on the CPU (without any isolation like Sio2Jail).
This is used in some contest types (for instance, ACM style contests), however it isn't needed when running the regular OI style
contests.
38 changes: 38 additions & 0 deletions config/supervisord-conf-vars.conf.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash

export WORKER_USER="$(id -u -n)"
export WORKER_HOME="/sio2/sioworkers"

export WORKER_LOGCONFIG="${WORKER_HOME}/config/logging.json"

# Cache cleaner config
export FILETRACKER_CACHE_CLEANER_ENABLED="true"
export CACHE_SIZE="10G"
export SCAN_INTERVAL="1h"
export CLEAN_LEVEL="50" # in percents

# Workers config
export WORKER_ENABLED="true"

# Set worker concurrency parameters
if [ ! -f /sys/fs/cgroup/cpu.max ] || [ $(cat /sys/fs/cgroup/cpu.max | cut -d \ -f 1) = "max" ] ; then
WORKERS_TOTAL=$(($(nproc) * 3/2))
else
WORKERS_TOTAL=$(cat /sys/fs/cgroup/cpu.max | awk '{print int($1 / $2)}')
fi

if [ ! -f /sys/fs/cgroup/memory.max ] || [ $(cat /sys/fs/cgroup/memory.max) = "max" ]; then
MEM_TOTAL=$(grep MemTotal /proc/meminfo | awk '{print int($2 / 1024)}') # in MiB
else
MEM_TOTAL=$(cat /sys/fs/cgroup/memory.max | awk '{print int($1 / 1048576)}') # in MiB
fi
# Set how much memory we should reserve for OS
OS_MEMORY=128 # in MiB

if [ -z ${WORKER_RAM+x} ]; then
export WORKER_RAM=$(($MEM_TOTAL - $OS_MEMORY))
fi

if [ -z ${WORKER_CONCURRENCY+x} ]; then
export WORKER_CONCURRENCY=${WORKERS_TOTAL}
fi
3 changes: 1 addition & 2 deletions config/supervisord.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ stdout_logfile=%(ENV_WORKER_HOME)s/logs/filetracker-cache-cleaner.log


[program:oioioiworker]
command=twistd -n -l- --pidfile=%(ENV_WORKER_HOME)s/pidfiles/oioioiworker.pid worker -c %(ENV_WORKER_CONCURRENCY)s -r %(ENV_WORKER_RAM)s -l %(ENV_WORKER_LOGCONFIG)s %(ENV_SIOWORKERSD_HOST)s
command=twistd -n -l- --pidfile=%(ENV_WORKER_HOME)s/pidfiles/oioioiworker.pid worker -c %(ENV_WORKER_CONCURRENCY)s -r %(ENV_WORKER_RAM)s -l %(ENV_WORKER_LOGCONFIG)s %(ENV_WORKER_EXTRA_FLAGS)s %(ENV_SIOWORKERSD_HOST)s
autostart=%(ENV_WORKER_ENABLED)s
priority=100
redirect_stderr=true
Expand All @@ -39,4 +39,3 @@ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix://%(ENV_WORKER_HOME)s/supervisor.sock

6 changes: 6 additions & 0 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
set -e

. /sio2/venv/bin/activate

exec "$@"
Loading

0 comments on commit 363f16a

Please sign in to comment.