|
| 1 | +FROM debian:bookworm-slim |
| 2 | + |
| 3 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 4 | + bash \ |
| 5 | + bash-completion \ |
| 6 | + ca-certificates \ |
| 7 | + curl \ |
| 8 | + git \ |
| 9 | + gnupg \ |
| 10 | + less \ |
| 11 | + lsb-release \ |
| 12 | + ncurses-base \ |
| 13 | + ncurses-term \ |
| 14 | + openssh-server \ |
| 15 | + procps \ |
| 16 | + wget \ |
| 17 | + && rm -rf /var/lib/apt/lists/* |
| 18 | + |
| 19 | +# Shell environment |
| 20 | +ENV SHELL=/bin/bash |
| 21 | +ENV TERM=xterm-256color |
| 22 | +ENV LANG=C.UTF-8 |
| 23 | +ENV LC_ALL=C.UTF-8 |
| 24 | + |
| 25 | +# Login banner (motd) |
| 26 | +RUN cat > /etc/motd <<'MOTD' |
| 27 | + |
| 28 | + ╔═════════════════════════════════════════════════════════════╗ |
| 29 | + ║ RT Benchmarking Container ║ |
| 30 | + ╠═════════════════════════════════════════════════════════════╣ |
| 31 | + ║ ║ |
| 32 | + ║ BENCHMARKS ║ |
| 33 | + ║ Run benchmark: cd /app && uv run python main.py \ ║ |
| 34 | + ║ run.docker=false pqos.enable=false ║ |
| 35 | + ║ Pre-flight: cd /app && uv run python -m \ ║ |
| 36 | + ║ src.rt_preflight ║ |
| 37 | + ║ ║ |
| 38 | + ║ JUPYTER NOTEBOOK ║ |
| 39 | + ║ Start: jupyter-start ║ |
| 40 | + ║ Then open: http://<this-host>:8888 ║ |
| 41 | + ║ ║ |
| 42 | + ║ RESULTS ║ |
| 43 | + ║ Output dir: /app/outputs/ ║ |
| 44 | + ║ Notebooks: /app/notebook/ ║ |
| 45 | + ║ SCP results: scp -i <key> root@<host>:/app/outputs . ║ |
| 46 | + ║ ║ |
| 47 | + ║ USEFUL COMMANDS ║ |
| 48 | + ║ rt-preflight Run RT environment checks ║ |
| 49 | + ║ rt-info Show detected CPUs and kernel info ║ |
| 50 | + ║ ll List files (long format) ║ |
| 51 | + ║ ║ |
| 52 | + ╚═════════════════════════════════════════════════════════════╝ |
| 53 | + |
| 54 | +MOTD |
| 55 | + |
| 56 | +# Minimal but usable bashrc |
| 57 | +RUN cat > /root/.bashrc <<'EOF' |
| 58 | +# prompt |
| 59 | +PS1='\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ ' |
| 60 | + |
| 61 | +# history |
| 62 | +HISTSIZE=1000 |
| 63 | +HISTFILESIZE=2000 |
| 64 | +HISTCONTROL=ignoreboth |
| 65 | +shopt -s histappend |
| 66 | + |
| 67 | +# usability |
| 68 | +shopt -s checkwinsize |
| 69 | + |
| 70 | +# color ls |
| 71 | +alias ls='ls --color=auto' |
| 72 | +alias ll='ls -alF' |
| 73 | +alias la='ls -A' |
| 74 | +alias l='ls -CF' |
| 75 | + |
| 76 | +# RT tools aliases |
| 77 | +jupyter-start() { |
| 78 | + local ip |
| 79 | + ip=$(hostname -I 2>/dev/null | awk '{print $1}') |
| 80 | + ip=${ip:-0.0.0.0} |
| 81 | + echo "" |
| 82 | + echo " Starting Jupyter Notebook..." |
| 83 | + echo " Connect at: http://${ip}:8888" |
| 84 | + echo "" |
| 85 | + cd /app && uv run jupyter notebook \ |
| 86 | + --ip=0.0.0.0 \ |
| 87 | + --port=8888 \ |
| 88 | + --no-browser \ |
| 89 | + --allow-root \ |
| 90 | + --notebook-dir=/app/notebook \ |
| 91 | + --ServerApp.custom_display_url="http://${ip}:8888" |
| 92 | +} |
| 93 | +alias rt-preflight='cd /app && uv run python -m src.rt_preflight' |
| 94 | +alias rt-info='echo "=== Kernel ===" && uname -a && echo && echo "=== CPUs (cgroup) ===" && cat /sys/fs/cgroup/cpuset.cpus.effective 2>/dev/null || cat /sys/fs/cgroup/cpuset/cpuset.cpus 2>/dev/null || echo "N/A" && echo && echo "=== RT cmdline params ===" && cat /proc/cmdline | tr " " "\n" | grep -E "isolcpus|nohz|rcu_nocbs|irqaffinity|cstate|pstate|hugepages"' |
| 95 | + |
| 96 | +# bash completion |
| 97 | +if [ -f /usr/share/bash-completion/bash_completion ]; then |
| 98 | + . /usr/share/bash-completion/bash_completion |
| 99 | +fi |
| 100 | + |
| 101 | +# show motd on interactive login |
| 102 | +if [ -f /etc/motd ] && [ -t 0 ]; then |
| 103 | + cat /etc/motd |
| 104 | +fi |
| 105 | +EOF |
| 106 | + |
| 107 | +RUN cp /root/.bashrc /etc/skel/.bashrc |
| 108 | + |
| 109 | +# SSH server configuration |
| 110 | +ARG SSH_KEY |
| 111 | +RUN mkdir -p /run/sshd /root/.ssh && \ |
| 112 | + chmod 700 /root/.ssh && \ |
| 113 | + ssh-keygen -A && \ |
| 114 | + sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config && \ |
| 115 | + sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config && \ |
| 116 | + sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \ |
| 117 | + sed -i 's/^#\?UsePAM.*/UsePAM no/' /etc/ssh/sshd_config && \ |
| 118 | + echo "${SSH_KEY}" > /root/.ssh/authorized_keys && \ |
| 119 | + chmod 600 /root/.ssh/authorized_keys |
| 120 | + |
| 121 | +EXPOSE 22 8888 |
| 122 | + |
| 123 | +# Entrypoint script: start sshd, run CMD, keep container alive |
| 124 | +RUN cat > /entrypoint.sh <<'ENTRY' |
| 125 | +#!/bin/bash |
| 126 | + |
| 127 | +# --- Detect cpuset and split into housekeeping vs benchmark cores --- |
| 128 | +CPUSET="" |
| 129 | +for f in /sys/fs/cgroup/cpuset.cpus.effective \ |
| 130 | + /sys/fs/cgroup/cpuset/cpuset.cpus \ |
| 131 | + /sys/fs/cgroup/cpuset/cpuset.effective_cpus; do |
| 132 | + if [ -f "$f" ]; then |
| 133 | + CPUSET=$(cat "$f") |
| 134 | + break |
| 135 | + fi |
| 136 | +done |
| 137 | + |
| 138 | +# Expand cpuset string (e.g. "2,4-6") into a sorted list of cores |
| 139 | +expand_cpuset() { |
| 140 | + local result="" |
| 141 | + IFS=',' read -ra parts <<< "$1" |
| 142 | + for part in "${parts[@]}"; do |
| 143 | + if [[ "$part" == *-* ]]; then |
| 144 | + IFS='-' read -r lo hi <<< "$part" |
| 145 | + for ((i=lo; i<=hi; i++)); do |
| 146 | + result="$result $i" |
| 147 | + done |
| 148 | + else |
| 149 | + result="$result $part" |
| 150 | + fi |
| 151 | + done |
| 152 | + echo "$result" | tr ' ' '\n' | sort -n | tr '\n' ' ' |
| 153 | +} |
| 154 | + |
| 155 | +ALL_CORES=( $(expand_cpuset "$CPUSET") ) |
| 156 | +NUM_CORES=${#ALL_CORES[@]} |
| 157 | + |
| 158 | +if [ "$NUM_CORES" -ge 2 ]; then |
| 159 | + # First core is housekeeping, rest are for benchmarks |
| 160 | + HOUSEKEEPING_CORE=${ALL_CORES[0]} |
| 161 | + BENCHMARK_CORES=$(IFS=,; echo "${ALL_CORES[*]:1}") |
| 162 | + echo "CPU layout: housekeeping=${HOUSEKEEPING_CORE} benchmark=${BENCHMARK_CORES} (from cpuset: ${CPUSET})" |
| 163 | +else |
| 164 | + # Only one core, everything shares it |
| 165 | + HOUSEKEEPING_CORE=${ALL_CORES[0]:-0} |
| 166 | + BENCHMARK_CORES=${ALL_CORES[0]:-0} |
| 167 | + echo "WARNING: Only ${NUM_CORES} core(s) available (${CPUSET}), no isolation possible" |
| 168 | +fi |
| 169 | + |
| 170 | +# Export for child processes (main.py can read these) |
| 171 | +export RT_HOUSEKEEPING_CORE="$HOUSEKEEPING_CORE" |
| 172 | +export RT_BENCHMARK_CORES="$BENCHMARK_CORES" |
| 173 | + |
| 174 | +# Pin this shell (and all children: sshd, python, uv) to housekeeping core |
| 175 | +taskset -pc "$HOUSEKEEPING_CORE" $$ 2>/dev/null || true |
| 176 | + |
| 177 | +# --- Start services pinned to housekeeping core --- |
| 178 | +mkdir -p /run/sshd |
| 179 | +echo "Starting SSH server on core ${HOUSEKEEPING_CORE}..." |
| 180 | +taskset -c "$HOUSEKEEPING_CORE" /usr/sbin/sshd -e || echo "WARNING: sshd failed to start (exit code $?)" |
| 181 | + |
| 182 | +IP=$(hostname -I 2>/dev/null | awk '{print $1}') |
| 183 | +IP=${IP:-<unknown>} |
| 184 | + |
| 185 | +echo "" |
| 186 | +echo "============================================================" |
| 187 | +echo " RT Benchmarking Container" |
| 188 | +echo "============================================================" |
| 189 | +echo "" |
| 190 | +echo " Housekeeping core: ${HOUSEKEEPING_CORE}" |
| 191 | +echo " Benchmark cores: ${BENCHMARK_CORES}" |
| 192 | +echo "" |
| 193 | +echo " SSH: ssh -i <key> root@${IP}" |
| 194 | +echo " Jupyter: ssh in, then run: jupyter-start" |
| 195 | +echo " or from this console:" |
| 196 | +echo " cd /app && uv run jupyter notebook --ip=0.0.0.0 \\" |
| 197 | +echo " --port=8888 --no-browser --allow-root \\" |
| 198 | +echo " --notebook-dir=/app/notebook" |
| 199 | +echo " then open: http://${IP}:8888" |
| 200 | +echo "" |
| 201 | +echo " Results: /app/outputs/" |
| 202 | +echo " Notebooks: /app/notebook/" |
| 203 | +echo "============================================================" |
| 204 | +echo "" |
| 205 | + |
| 206 | +if [ $# -gt 0 ]; then |
| 207 | + echo "Running: $@" |
| 208 | + "$@" |
| 209 | + EXIT_CODE=$? |
| 210 | + echo "" |
| 211 | + echo "============================================================" |
| 212 | + echo " Command finished with exit code ${EXIT_CODE}" |
| 213 | + echo "" |
| 214 | + echo " Jupyter: http://${IP}:8888" |
| 215 | + echo " To start: ssh root@${IP} then run: jupyter-start" |
| 216 | + echo " SSH: ssh -i <key> root@${IP}" |
| 217 | + echo " Results: /app/outputs/" |
| 218 | + echo "============================================================" |
| 219 | + echo "" |
| 220 | +fi |
| 221 | + |
| 222 | +echo "Container staying alive (sshd running on core ${HOUSEKEEPING_CORE}). SSH in or ctrl-c to stop." |
| 223 | +sleep infinity |
| 224 | +ENTRY |
| 225 | +RUN chmod +x /entrypoint.sh |
| 226 | + |
| 227 | +# Intel and ECI repository keys and sources |
| 228 | +RUN wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null |
| 229 | +RUN wget -O- https://eci.intel.com/repos/gpg-keys/GPG-PUB-KEY-INTEL-ECI.gpg | tee /usr/share/keyrings/eci-archive-keyring.gpg > /dev/null |
| 230 | +RUN wget -O- https://raw.githubusercontent.com/ros/rosdistro/master/ros.key | tee /usr/share/keyrings/ros-archive-keyring.gpg > /dev/null |
| 231 | +RUN . /etc/os-release \ |
| 232 | + && echo $VERSION_CODENAME && \ |
| 233 | + bash -c 'echo "deb [signed-by=/usr/share/keyrings/eci-archive-keyring.gpg] https://eci.intel.com/repos/$(source /etc/os-release && echo $VERSION_CODENAME) isar main" | tee /etc/apt/sources.list.d/eci.list' && \ |
| 234 | + bash -c 'echo "deb-src [signed-by=/usr/share/keyrings/eci-archive-keyring.gpg] https://eci.intel.com/repos/$(source /etc/os-release && echo $VERSION_CODENAME) isar main" | tee -a /etc/apt/sources.list.d/eci.list' && \ |
| 235 | + bash -c 'echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | tee /etc/apt/sources.list.d/oneAPI.list' && \ |
| 236 | + bash -c 'echo -e "Package: intel-oneapi-runtime-*\nPin: version 2024.1.*\nPin-Priority: 1001" > /etc/apt/preferences.d/oneapi' && \ |
| 237 | + bash -c 'echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/openvino/2024 ubuntu24 main" > /etc/apt/sources.list.d/intel-openvino-2024.list' && \ |
| 238 | + bash -c 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $VERSION_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list' |
| 239 | +RUN apt-get update && apt-get install -y intel-cmt-cat |
| 240 | + |
| 241 | +ENTRYPOINT ["/entrypoint.sh"] |
| 242 | +CMD ["/bin/bash", "-l"] |
0 commit comments