diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..82a655c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +*.pyc +__pycache__ +.git +.vscode +node_modules +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5e983bf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM debian:stable-slim + +ENV DEBIAN_FRONTEND=noninteractive + +# Install required packages: curl, cron, busybox (httpd), ca-certificates +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl cron busybox ca-certificates gnupg dirmngr \ + && rm -rf /var/lib/apt/lists/* + +# Install Ookla Speedtest CLI via packagecloud repository +RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash \ + && apt-get update \ + && apt-get install -y --no-install-recommends speedtest \ + && rm -rf /var/lib/apt/lists/* + +# Create web root and data directories +RUN mkdir -p /var/www && mkdir -p /var/www/data/speed +WORKDIR /var/www + +# Copy web assets and scripts +COPY index.html /var/www/index.html +COPY run_speed_test.sh /usr/local/bin/run_speed_test.sh +COPY start.sh /usr/local/bin/start.sh + +# Ensure scripts are executable +RUN chmod +x /usr/local/bin/run_speed_test.sh /usr/local/bin/start.sh + +# Add cron job to run every hour at minute 0 +RUN printf "0 * * * * root /usr/local/bin/run_speed_test.sh >> /var/log/cron.log 2>&1\n" > /etc/cron.d/speedtest-cron \ + && chmod 0644 /etc/cron.d/speedtest-cron + +# Expose default HTTP port and declare data volume +VOLUME ["/var/www/data/speed"] +# Default port for http server inside container. You can override at runtime with -e PORT=xxxx +ENV PORT=8080 +EXPOSE 8080 + +# Start script will launch cron and the HTTP server +CMD ["/usr/local/bin/start.sh"] diff --git a/README.md b/README.md index a435ac1..2569f95 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,49 @@ # speed-data-docker -Docker container for Speed test app \ No newline at end of file +Debian-based Docker image that runs hourly network speed tests (using speedtest-cli) and serves a simple web UI. + +What the container provides +- Base: debian +- Installs: speedtest-cli +- Runs: a cron job to execute `run_speed_test.sh` hourly +- Serves: `index.html` and generated JS files on port 8080 via busybox httpd + +Files added/edited +- `Dockerfile` - builds the image +- `start.sh` - entrypoint: starts cron and httpd +- `run_speed_test.sh` - existing script (copied into image) + +Build locally +Make sure Docker is installed locally. From the project root run: + +```bash +docker build -t speed-data-app . +``` + +Run container + +```bash +# Run with a named Docker volume (recommended). The container serves on $PORT inside the container +# (default 8080). Map a host port to the container port with -p :. +docker volume create speed-data-volume +docker run -d --name speed-data -e PORT=8080 -p 8080:8080 --restart unless-stopped \ + -v speed-data-volume:/var/www/data/speed \ + speed-data-app + +# Or run with a host bind mount (for direct access to files on the host): +docker run -d --name speed-data -e PORT=8080 -p 8080:8080 --restart unless-stopped \ + -v /path/on/host/speed-data:/var/www/data/speed \ + speed-data-app +``` + +Verify +- Visit http://localhost:8080 to view `index.html` (which reads `speedtest.js` and `speedtest.short.js`). +- Cron runs at minute 0 every hour. Logs go to `/var/log/cron.log` inside the container. + +Notes +- Data files (`speedtest.js`, `speedtest.short.js`, and the raw JSON file) are written to `/var/www/data/speed` inside the container. That path is declared as a Docker VOLUME in the `Dockerfile` so you can mount a named volume or a host directory. +- If you mount a host directory, ensure the directory is writeable by the container process. You can either: + - run the container as root (not recommended), or + - chown/chmod the host directory appropriately (e.g., `chown 1000:1000 /path/on/host` or `chmod a+rw /path/on/host`) so the container can write files. + +Reminder: I built the image successfully after you started Docker locally. If you'd like, I can remove/recreate the container on your machine; tell me to proceed and I'll run the commands here. diff --git a/index.html b/index.html new file mode 100644 index 0000000..bdbcbcb --- /dev/null +++ b/index.html @@ -0,0 +1,474 @@ + + + + Speedtest data + + + + + + + + +
+
Preparing data ...
+
+
+
+
+
+
+
+
+ + + + + + + + + + diff --git a/run_speed_test.sh b/run_speed_test.sh new file mode 100755 index 0000000..61d55a8 --- /dev/null +++ b/run_speed_test.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +RAW_DATA=/var/www/data/speed/raw.speedtest.json +TMP_DATA=/tmp/speedtest.js +WEB_DATA=/var/www/data/speed/speedtest.js +WEB_SHORT_DATA=/var/www/data/speed/speedtest.short.js + +/usr/bin/speedtest --format=json --accept-license >> ${RAW_DATA} 2>/dev/null || speedtest --format=json --accept-license >> ${RAW_DATA} 2>/dev/null + +# Prepare information for web display +tail -500 ${RAW_DATA} > ${TMP_DATA} && \ + sed -i -e 's/$/,/' ${TMP_DATA} && \ + echo "var speeddata = [" > ${WEB_DATA} && \ + cat ${TMP_DATA} >> ${WEB_DATA} && \ + echo "];" >> ${WEB_DATA} +# Prepare short information for quick web display +tail -6 ${RAW_DATA} > ${TMP_DATA} && \ + sed -i -e 's/$/,/' ${TMP_DATA} && \ + echo "var speeddata = [" > ${WEB_SHORT_DATA} && \ + cat ${TMP_DATA} >> ${WEB_SHORT_DATA} && \ + echo "];" >> ${WEB_SHORT_DATA} + +# Trim raw data to keep it lean +tail -1000 ${RAW_DATA} > ${TMP_DATA} && \ + cp ${TMP_DATA} ${RAW_DATA} + +# Above arguments produce the following output in single line. Expanded for readability below: +# +# { +# "type":"result", +# "timestamp":"2023-04-24T03:57:04Z", +# "ping":{ +# "jitter":0.193, +# "latency":1.751, +# "low":1.530, +# "high":1.982 +# }, +# "download":{ +# "bandwidth":85545427, +# "bytes":1017170768, +# "elapsed":12616, +# "latency":{ +# "iqm":1.894, +# "low":1.374, +# "high":2.916, +# "jitter":0.300 +# } +# }, +# "upload":{ +# "bandwidth":91982628, +# "bytes":331336109, +# "elapsed":3601, +# "latency":{ +# "iqm":4.135, +# "low":2.281, +# "high":5.322, +# "jitter":0.498 +# } +# }, +# "packetLoss":0, +# "isp":"GigaMonster", +# "interface":{ +# "internalIp":"192.168.1.200", +# "name":"eno1", +# "macAddr":"B8:AE:ED:7D:8A:36", +# "isVpn":false, +# "externalIp":"24.72.150.52" +# }, +# "server":{ +# "id":36565, +# "host":"speedpdx2.ortelco.net", +# "port":8080, +# "name":"OTC Connections", +# "location":"Portland, OR", +# "country":"United States", +# "ip":"38.87.96.27"}, +# "result":{ +# "id":"9b547022-8e1b-42c5-871c-2b6810b641bb", +# "url":"https://www.speedtest.net/result/c/9b547022-8e1b-42c5-871c-2b6810b641bb", +# "persisted":true +# } +# } diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..1aff8a8 --- /dev/null +++ b/start.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +set -e + +# Ensure data directory exists +mkdir -p /var/www/data/speed + +# Ensure raw data file exists +touch /var/www/data/speed/raw.speedtest.json + +# If the data directory is a mounted volume it may be owned by root; allow writes +# by ensuring the directory is writable. +chown -R root:root /var/www/data || true +chmod -R a+rwX /var/www/data || true + +# Run the initial speed test once (in background) to populate files if possible +# We run it in background so server starts promptly. The cron will run hourly. +/usr/local/bin/run_speed_test.sh || true & + +# Ensure the cron job exists in root's crontab (so `crontab -l` shows it) +CRON_ENTRY="* * * * * /usr/local/bin/run_speed_test.sh >> /var/log/cron.log 2>&1" +if crontab -l 2>/dev/null | grep -F "$CRON_ENTRY" >/dev/null 2>&1; then + echo "cron entry already present" +else + (crontab -l 2>/dev/null; echo "$CRON_ENTRY") | crontab - + echo "installed cron entry into root crontab" +fi + +# Start Debian cron in background +service cron start || cron || true + +PORT=${PORT:-8080} + +# Start busybox httpd serving /var/www on configured port in foreground +if command -v busybox >/dev/null 2>&1; then + echo "starting busybox httpd on port ${PORT} serving /var/www" + busybox httpd -f -p ${PORT} -h /var/www +else + echo "warning: busybox httpd not found; container will keep running with cron only" >&2 + # keep the script running so container doesn't exit + tail -f /var/log/cron.log +fi