Featured image of post ICPC Contest Pre-deployment & Performance Optimization Checklist

ICPC Contest Pre-deployment & Performance Optimization Checklist

ICPC比赛赛前部署&性能优化检查清单

DomJudge Deployment

  1. Normal install according to official manual
  2. Remove default nginx page from /etc/nginx/sites-enabled/default
  3. Change FPM upload params in /opt/domjudge/domserver/etc/domjudge-fpm.conf
  4. Add following to /etc/mysql/my.cnf
1
2
3
4
5
6
[mysqld]
max_connections=1000
innodb_log_file_size=10GB
max_allowed_packet=2GB
slow_query_log=1
slow_query_log_file=/var/log/mysql_slow.log
  1. Install redis, switch session to redis according to this blog post

JudgeHost Deployment

Use following script to generate Docker Compose file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env bash
set -euo pipefail

N="${1:-20}"

cat <<'YAML'
services:
YAML

for i in $(seq 1 "$N"); do
  if [[ "$i" -eq 1 ]]; then
    cat <<YAML
  judgehost-$i:
    image: docker.1ms.run/domjudge/judgehost:9.0.0
    hostname: judgedaemon-$i
    restart: unless-stopped
    privileged: true
    environment: &judgehost_env
      DOMSERVER_BASEURL: "http://<URL HERE>/"
      JUDGEDAEMON_PASSWORD: "<PASSWORD HERE>"
      DAEMON_ID: "$i"
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
    networks:
      - domjudge-net

YAML
  else
    cat <<YAML
  judgehost-$i:
    image: docker.1ms.run/domjudge/judgehost:9.0.0
    hostname: judgedaemon-$i
    restart: unless-stopped
    privileged: true
    environment:
      <<: *judgehost_env
      DAEMON_ID: "$i"
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
    networks:
      - domjudge-net

YAML
  fi
done

cat <<'YAML'
networks:
  domjudge-net:
    external: true
    name: domjudge-judgehosts_default
YAML

Better not modify judgehost during contest, or judge task may be lost

Natsume Deployment

Only for Natsume version higher than 0.1.2

  1. Generate CA cert with following command
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 openssl ecparam -name prime256v1 -genkey -noout -out ca.key
 openssl req -new -x509 \
     -key ca.key \
     -sha256 \
     -days 365 \
     -out ca.cert \
     -addext "basicConstraints=critical,CA:TRUE" \
     -addext "keyUsage=critical,keyCertSign,cRLSign" \
     -addext "subjectKeyIdentifier=hash"
 openssl pkcs8 -topk8 -inform PEM -in ca.key -outform PEM -out ca_pkcs8.key -nocrypt
  1. Generate TLS cert for caddy reverse proxy, with this script
  2. Do data preprocess with Natsume data preprocessor
  3. Create team categories (Participants, Girls, etc)
  4. Import data generated by Natsume data preprocessor, organizations.json first, then teams.json, accounts.yaml last
  5. Install parallel-ssh in advance

Monitor & Performance Tune

  1. Build nginx botli accoriding to this blog post
  2. Install Redis, switch DomJudge session store to Redis according to this blog post
  3. Put opcache monitor’s opcache.php in /opt/domjudge/status/opcache.php
  4. Add following to /etc/nginx/nginx.conf inside http block
1
2
3
4
5
6
7
# DOMjudge scoreboard cache
fastcgi_cache_path /var/cache/nginx/domjudge
   levels=1:2
   keys_zone=dj_scoreboard:20m
   inactive=30s
   max_size=200m
   use_temp_path=off;
  1. Create cache path with following commands
1
2
mkdir -p /var/cache/nginx/domjudge
chown -R www-data:www-data /var/cache/nginx/domjudge
  1. Edit /opt/domjudge/domserver/etc/nginx-conf-inner, it should become like the following
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# Generated from 'nginx-conf-inner.in' on Mon Mar  2 10:51:28 AM UTC 2026.

# inner nginx configuration for DOMjudge
# This is in a separate file to not have duplicate config

server_name _default_;

# set max upload size to infinite since PHP has a setting for this
client_max_body_size 0;

# Prevent indexing by robots
add_header X-Robots-Tag "none" always;

# Variables used in the nginx configuration
set $domjudgeRoot /opt/domjudge/domserver/webapp/public;
# Set this to '' instead of /domjudge when running in the root of your system
set $prefix '';

# /team/scoreboard -> cached /public?static=true (avoid hammering PHP-FPM)
location = /team/scoreboard {
        limit_except GET HEAD { deny all; }

        include fastcgi_params;
        fastcgi_pass domjudge;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;

        fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
        fastcgi_param SCRIPT_NAME     $prefix/index.php;
        fastcgi_param DOCUMENT_ROOT   $domjudgeRoot;

        fastcgi_param DOCUMENT_URI    /public;
        fastcgi_param QUERY_STRING    "static=true";
        fastcgi_param REQUEST_URI     /public?static=true;

        fastcgi_param SERVER_NAME $host;
        fastcgi_param HTTPS $fastcgi_param_https_variable;

        fastcgi_cache dj_scoreboard;

        fastcgi_cache_key "$scheme://$host/public?static=true";

        # TTL = 3s
        fastcgi_cache_valid 200 3s;
        fastcgi_cache_valid any 0;

        fastcgi_cache_lock on;
        fastcgi_cache_lock_timeout 5s;
        fastcgi_cache_lock_age 5s;

        fastcgi_cache_use_stale updating;
        fastcgi_cache_background_update on;

        fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
        fastcgi_hide_header Set-Cookie;

        add_header X-Cache-Status $upstream_cache_status always;
}

location / {
        root $domjudgeRoot;
        location = /status/fpm {
                access_log off;
        
                include fastcgi_params;
        
                fastcgi_param SCRIPT_NAME     /fpm_status;
                fastcgi_param DOCUMENT_URI    /fpm_status;
                fastcgi_param REQUEST_URI     /fpm_status;
                fastcgi_param SCRIPT_FILENAME /fpm_status;
        
                fastcgi_param SERVER_NAME $host;
                fastcgi_param HTTPS $fastcgi_param_https_variable;
        
                fastcgi_pass domjudge;
        }
        
        location ^~ /status/ {
                alias /opt/domjudge/status/;
                index index.php index.html;

                try_files $uri $uri/ =404;

                location ~ \.php$ {
                include fastcgi_params;
                fastcgi_split_path_info ^(.+\.php)(/.*)$;

                fastcgi_param SCRIPT_FILENAME $request_filename;

                fastcgi_param SERVER_NAME $host;
                fastcgi_param HTTPS $fastcgi_param_https_variable;
                fastcgi_pass domjudge;
                }
        }
        try_files $uri @domjudgeFront;

# Handle API requests separately to be able to split the log
        location /api/ {
                try_files $uri @domjudgeFrontApi;
                error_log /var/log/nginx/domjudge-api.log;
                access_log /var/log/nginx/domjudge-api.log;
        }
}

# Or you can install it with a prefix
#location /domjudge { return 301 /domjudge/; }
#location /domjudge/ {
#       root $domjudgeRoot;
#       rewrite ^/domjudge/(.*)$ /$1 break;
#       try_files $uri @domjudgeFront;

        # Handle API requests separately to be able to split the log
#       location /domjudge/api/ {
#               rewrite ^/domjudge/(.*)$ /$1 break;
#               try_files $uri @domjudgeFrontApi;
#       }
#}

location @domjudgeFront {
        sub_filter_once off;
        sub_filter 'href="/team/scoreboard"' 'href="/team/scoreboard" target="_blank"';
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_pass domjudge;
        include fastcgi_params;
        fastcgi_param SERVER_NAME $host;
        fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
        fastcgi_param SCRIPT_NAME $prefix/index.php;
        fastcgi_param REQUEST_URI $prefix$uri?$args;
        fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
        fastcgi_param HTTPS $fastcgi_param_https_variable;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/app_dev.php/some-path
        internal;
}

location @domjudgeFrontApi {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_pass domjudge;
        include fastcgi_params;
        fastcgi_param SERVER_NAME $host;
        fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
        fastcgi_param SCRIPT_NAME $prefix/index.php;
        fastcgi_param REQUEST_URI $prefix$uri?$args;
        fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
        fastcgi_param HTTPS $fastcgi_param_https_variable;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/app_dev.php/some-path
        internal;

        # Use a separate log file for the API
        error_log /var/log/nginx/domjudge-api.log;
        access_log /var/log/nginx/domjudge-api.log;
}

# The X-Frame-Options header defends against so-called 'clickjacking' attacks.
# Should you want to load part of DOMjudge (e.g. the public scoreboard) in
# a HTML frame or iframe, disable this header for that part of DOMjudge only.
add_header X-Frame-Options "DENY";

# The following headers should be fine for any DOMjudge installation.
add_header Referrer-Policy "same-origin";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";

error_log /var/log/nginx/domjudge.log;
access_log /var/log/nginx/domjudge.log;
  1. Tune the opcache params inside php.ini according to the opcache monitor
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计