Skip to content

Latest commit

 

History

History
689 lines (501 loc) · 29.1 KB

containers.md

File metadata and controls

689 lines (501 loc) · 29.1 KB
semantic-links
skill-links related-artifacts
write-markdown-docs
docs/index.md
Containerfile
share/container/entry_script_sh

Containers (Docker or Podman)

Demo environment

It is simple to setup the tracker with the default configuration and run it using the pre-built public docker image:

With Docker:

docker run -it torrust/tracker:latest

or with Podman:

podman run -it docker.io/torrust/tracker:latest

Requirements

  • Tested with recent versions of Docker or Podman.

Volumes

The Containerfile (i.e. the Dockerfile) Defines Three Volumes:

VOLUME ["/var/lib/torrust/tracker","/var/log/torrust/tracker","/etc/torrust/tracker"]

When instancing the container image with the docker run or podman run command, we map these volumes to the local storage:

./storage/tracker/lib -> /var/lib/torrust/tracker
./storage/tracker/log -> /var/log/torrust/tracker
./storage/tracker/etc -> /etc/torrust/tracker

NOTE: You can adjust this mapping for your preference, however this mapping is the default in our guides and scripts.

Pre-Create Host-Mapped Folders

Please run this command where you wish to run the container:

mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/

Matching Ownership ID's of Host Storage and Container Volumes

It is important that the torrust user has the same uid $(id -u) as the host mapped folders. In our entry script, installed to /usr/local/bin/entry.sh inside the container, switches to the torrust user created based upon the USER_UID environmental variable.

When running the container, you may use the --env USER_ID="$(id -u)" argument that gets the current user-id and passes to the container.

Mapped Tree Structure

Using the standard mapping defined above produces this following mapped tree:

storage/tracker/
├── lib
│   ├── database           => created only when SQLite persistence is selected
│   │   └── sqlite3.db     => /var/lib/torrust/tracker/database/sqlite3.db
│   └── tls
│       ├── localhost.crt  => /var/lib/torrust/tracker/tls/localhost.crt [user supplied]
│       └── localhost.key  => /var/lib/torrust/tracker/tls/localhost.key [user supplied]
├── log                    => /var/log/torrust/tracker (future use)
└── etc
    └── tracker.toml        => /etc/torrust/tracker/tracker.toml [auto populated]

NOTE: you only need the tls directory and certificates in case you have enabled SSL.

Building the Container

Clone and Change into Repository

# Inside your dev folder
git clone https://github.com/torrust/torrust-tracker.git; cd torrust-tracker

(Docker) Setup Context

Before starting, if you are using docker, it is helpful to reset the context to the default:

docker context use default

(Docker) Build

# Release Mode
docker build --target release --tag torrust-tracker:release --file Containerfile .

# Debug Mode
docker build --target debug --tag torrust-tracker:debug --file Containerfile .

(Podman) Build

# Release Mode
podman build --target release --tag torrust-tracker:release --file Containerfile .

# Debug Mode
podman build --target debug --tag torrust-tracker:debug --file Containerfile .

How the Image Is Built

The Containerfile builds the tracker twice, and the two builds have different jobs:

  • Test build. The stages "Cook", "Build Archive" and "Extract and Test" compile every test with cargo nextest archive --tests --all-features and run them. This build enables the dev-dependency features and every feature, which test support code needs.
  • Shipped build. The stages "Cook Shipped" and "Build Shipped" compile torrust-tracker and http_health_check (the debug image ships only torrust-tracker) with cargo build --package torrust-tracker, without --tests, --all-targets, --examples, --benches or --all-features. The image's /app/bin takes its binaries from these stages, never from the test archive, so no testing feature of a workspace package reaches them (rule A.5 of ADR 20261021190318). A shipped build that needs a feature names it explicitly in its cargo build command.

The test stages still gate the image. They run the native-tracker tests against the shipped binary: the test helper tracker_binary() starts the executable named by the TORRUST_TRACKER_BINARY environment variable when it is set (the test stages set it), and otherwise the one Cargo or nextest built. Build a binary of your own and point the variable at it to run those tests against it:

cargo build --release --package torrust-tracker
TORRUST_TRACKER_BINARY=./target/release/torrust-tracker cargo test -p torrust-tracker --test lifecycle-signals

The Shipped Features job of .github/workflows/testing.yaml runs the shipped-features-check tool (contrib/dev-tools/checks/shipped-features-check), which reads cargo tree -p torrust-tracker -e normal,build and fails if a workspace package has its testing feature enabled in that tree. Keep the package and features it names in step with the "Build Shipped" stages. Run the check locally with:

cargo run --quiet --package shipped-features-check -- --package torrust-tracker

The shipped build costs about 17 seconds on a cold release image build, 18 seconds on a cold debug image build and 8 seconds on a rebuild after a one-line source change, measured against the build before it existed (see the evidence of issue #2670).

Running the Container

Basic Run

No arguments are needed for simply checking the container image works:

(Docker) Run Basic

# Release Mode
docker run -it torrust-tracker:release

# Debug Mode
docker run -it torrust-tracker:debug

(Podman) Run Basic

# Release Mode
podman run -it docker.io/torrust-tracker:release

# Debug Mode
podman run -it docker.io/torrust-tracker:debug

Arguments

Docker or Podman runtime arguments are placed before the image tag. Because tracker arguments after the image tag replace the image CMD, include the tracker binary before its command-line arguments.

run [runtime arguments] torrust-tracker:release /usr/bin/torrust-tracker [tracker arguments]

Tracker Command Options

The main tracker binary accepts -c / --config-toml-path <PATH>. The selected path is evaluated inside the container, so use an in-container mounted path:

docker run -it torrust/tracker:latest \
  /usr/bin/torrust-tracker \
  --config-toml-path /etc/torrust/tracker/tracker.toml

This option overrides the image's TORRUST_TRACKER_CONFIG_TOML_PATH and any supplied TORRUST_TRACKER_CONFIG_TOML base source. Per-value TORRUST_TRACKER_CONFIG_OVERRIDE_* variables still override matching values in the CLI-selected file.

Environmental Variables

Environmental variables are loaded through the --env, in the format --env VAR="value".

The following environmental variables can be set:

  • TORRUST_TRACKER_CONFIG_TOML_PATH - The in-container path to the tracker configuration file, (default: "/etc/torrust/tracker/tracker.toml").
  • TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN - Override of the admin token. If set, this value overrides any value set in the config.
  • TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER - Optional selection of a packaged persistence configuration for a fresh /etc/torrust/tracker/tracker.toml (options: sqlite3, mysql, postgresql). When omitted, the container installs the packaged v3 public tracker configuration, which omits [core.database]. This does not override an existing mounted configuration file.
  • TORRUST_TRACKER_CONFIG_TOML - Load config from this environmental variable instead from a file, (i.e: TORRUST_TRACKER_CONFIG_TOML=$(cat tracker-tracker.toml)).
  • USER_ID - The user id for the runtime crated torrust user. Please Note: This user id should match the ownership of the host-mapped volumes, (default 1000).
  • UDP_PORT - The port for the UDP tracker. This should match the port used in the configuration, (default 6969).
  • HTTP_PORT - The port for the HTTP tracker. This should match the port used in the configuration, (default 7070).
  • API_PORT - The port for the tracker API. This should match the port used in the configuration, (default 1212).
  • HEALTH_CHECK_API_PORT - The port for the Health Check API. This should match the port used in the configuration, (default 1313).

Persistence-Free Default

With no mounted tracker.toml and no database-driver override, the image installs tracker.container.no-persistence.toml. It starts public UDP and HTTP trackers plus the health API without [core.database]. The entrypoint does not create /var/lib/torrust/tracker/database or install a SQLite database in this mode. Supply a mounted v3 configuration for other listener and capability combinations.

PostgreSQL backend notes

To run the tracker with PostgreSQL in containers:

  • Set TORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__DRIVER=postgresql.
  • Use the default PostgreSQL container configuration file: share/default/config/tracker.container.postgresql.toml.
  • Ensure the target database exists before tracker startup. The default PostgreSQL DSN in the container config expects torrust_tracker.

When using a PostgreSQL container, set POSTGRES_DB=torrust_tracker (or create the same database explicitly) so the tracker can connect at startup.

Sockets

Socket ports used internally within the container can be mapped to with the --publish argument.

The format is: --publish [optional_host_ip]:[host_port]:[container_port]/[optional_protocol], for example: --publish 127.0.0.1:8080:80/tcp.

The default ports can be mapped with the following:

--publish 0.0.0.0:7070:7070/tcp \
--publish 0.0.0.0:6969:6969/udp \
--publish 0.0.0.0:1212:1212/tcp \

NOTE: Inside the container it is necessary to expose a socket with the wildcard address 0.0.0.0 so that it may be accessible from the host. Verify that the configuration that the sockets are wildcard.

HTTP/3 at the edge with a reverse proxy

The tracker does not need native HTTP/3 support to offer HTTP/3 to clients. You can terminate HTTP/3 at an edge reverse proxy and forward traffic to the tracker over HTTP/1.1 or HTTP/2.

Protocol boundary:

  • Client to proxy: HTTP/1.1, HTTP/2, or HTTP/3 (optional).
  • Proxy to tracker backend: HTTP/1.1 or HTTP/2.

This keeps deployment flexible while native HTTP/3 support in the Rust HTTP ecosystem continues to mature.

Caddy example

Expose both TCP and UDP on port 443 for QUIC/HTTP/3, and forward tracker endpoints to the existing tracker HTTP ports.

{
    servers :443 {
        protocols h1 h2 h3
    }
}

tracker.example.com {
    reverse_proxy tracker:7070 {
        # Forward the original client IP when tracker runs behind a proxy.
        header_up X-Forwarded-For {remote_host}
    }
}

api.example.com {
    reverse_proxy tracker:1212
}

Tracker configuration required: set core.net.on_reverse_proxy = true in the tracker configuration so it reads the peer IP from the X-Forwarded-For header rather than the proxy's TCP connection address. Without this setting, the tracker ignores the forwarded header and records the proxy's IP as every peer's address.

If Caddy runs in a container, publish both protocols on 443:

--publish 0.0.0.0:443:443/tcp \
--publish 0.0.0.0:443:443/udp

Reference: Caddy options page (including servers / protocols)

Latest reference from Torrust Tracker Demo: torrust-tracker-demo Caddy config

Operational guidance

  • HTTP/3 at the edge is optional. Keep the tracker backend unchanged and enable/disable HTTP/3 in the proxy configuration when needed.
  • Roll out gradually. Start with a single environment and compare behaviour before broad rollout.
  • Monitor CPU and memory on the proxy, plus request error rates, as QUIC load can shift resource usage from backend services to the edge.
  • Keep an easy rollback path: remove h3 support in the proxy and keep serving HTTP/1.1 and HTTP/2 without tracker code changes.

Manual verification

Use these commands to verify HTTP/3 against the Torrust demo tracker. Replace http1.torrust-tracker-demo.com with your own hostname to verify your own deployment:

# 1) Confirm alt-svc advertisement for h3
curl -sI https://http1.torrust-tracker-demo.com/announce | grep -i alt-svc

# 2) Force HTTP/3 only (requires curl built with HTTP/3 support)
/snap/bin/curl --http3-only -sI https://http1.torrust-tracker-demo.com/announce

# 3) Optional: inspect QUIC and protocol negotiation
/snap/bin/curl --http3-only -v https://http1.torrust-tracker-demo.com/announce 2>&1 \
    | grep -E 'QUIC|HTTP/3|h3|Connected|protocol'

Expected for step 2: the response status line starts with HTTP/3 200.

Stopping the Tracker

The tracker stops when it receives SIGTERM or SIGINT. The image runs it as the container's first process (the entry script replaces itself with the tracker), so docker stop, podman stop, a Kubernetes pod deletion, and systemctl stop all reach it.

The tracker finishes in two stages, each with its own time budget, inside one process deadline. All four values are in the [application.shutdown] table of the configuration file. The shipped templates state the defaults:

Key Default What it limits
deadline_in_secs 25 s The whole stop. A component that has not finished by then is aborted.
http_drain_in_secs 15 s How long the HTTP tracker, the REST API, and the health check API wait for open connections.
udp_request_drain_in_secs 5 s How long the UDP tracker waits for the requests it has already accepted.
event_listener_drain_in_secs 5 s How long each statistics, banning, and cleanup listener keeps processing events after its producers stopped.

Every value is a whole number from 1 to 3600. The servers stop first and the listeners second, so the tracker refuses to start unless max(http_drain_in_secs, udp_request_drain_in_secs) + event_listener_drain_in_secs is below deadline_in_secs. With the defaults that is 15 + 5 = 20, below 25. Set a value for one run with an environment variable, for example --env TORRUST_TRACKER_CONFIG_OVERRIDE_APPLICATION__SHUTDOWN__HTTP_DRAIN_IN_SECS=10.

The exit code tells whether the stop was complete:

Exit code Meaning
0 Every component finished, or was cancelled, within the deadline.
1 A component failed, panicked, or was aborted at the deadline, or the tracker could not start. The log names it.
137 The container runtime sent SIGKILL before the tracker finished. The tracker did not exit by itself.

Only an HTTP-family drain that reaches its budget (the HTTP tracker, the REST API, or the health check API, all governed by http_drain_in_secs) is a failed component. The UDP request drain and the event listener drain log a warning when their budget expires, which names how many requests were aborted or how many events were left unread, and the stop still exits 0. An exit code 0 therefore does not prove that no UDP request or queued event was discarded; check the shutdown log for those warnings.

Give the tracker enough time to stop

The container runtime must wait longer than the tracker's deadline before it sends SIGKILL: at least deadline_in_secs plus 5 s. For the defaults that is 30 s at least, and 35 s is recommended. Docker and Podman wait only 10 s by default, which is shorter than the default deadline, so a tracker that is still draining is killed and the container exits with 137. Set the time explicitly:

Runtime Setting Minimum Recommended
Docker, Podman run --stop-timeout, or stop --time 30 s 35 s
Docker Compose stop_grace_period 30s 35s
Kubernetes terminationGracePeriodSeconds 30 35
systemd TimeoutStopSec 30 35
docker run --stop-timeout 35 --publish 0.0.0.0:7070:7070/tcp torrust/tracker:latest
services:
  tracker:
    image: torrust/tracker:latest
    stop_grace_period: 35s
spec:
  terminationGracePeriodSeconds: 35
[Service]
TimeoutStopSec=35

If you raise deadline_in_secs, raise the runtime value by the same amount. If you lower it, the runtime value can stay as it is.

A requested stop that ends with exit code 1 leaves a systemd unit in the failed state. That is intended: the stop was not clean. Do not add 1 to SuccessExitStatus to hide it; read the log for the component named as incomplete instead.

Host-mapped Volumes

By default the container will use install volumes for /var/lib/torrust/tracker, /var/log/torrust/tracker, and /etc/torrust/tracker, however for better administration it good to make these volumes host-mapped.

The argument to host-map volumes is --volume, with the format: --volume=[host-src:]container-dest[:<options>].

The default mapping can be supplied with the following arguments:

--volume ./storage/tracker/lib:/var/lib/torrust/tracker:Z \
--volume ./storage/tracker/log:/var/log/torrust/tracker:Z \
--volume ./storage/tracker/etc:/etc/torrust/tracker:Z \

Please not the :Z at the end of the podman --volume mapping arguments, this is to give read-write permission on SELinux enabled systemd, if this doesn't work on your system, you can use :rw instead.

Complete Example

With Docker

## Setup Docker Default Context
docker context use default

## Build Container Image
docker build --target release --tag torrust-tracker:release --file Containerfile .

## Setup Mapped Volumes
mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/

## Run Torrust Tracker Container Image
docker run -it \
    --env TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN="MySecretToken" \
    --env USER_ID="$(id -u)" \
    --publish 0.0.0.0:7070:7070/tcp \
    --publish 0.0.0.0:6969:6969/udp \
    --publish 0.0.0.0:1212:1212/tcp \
    --volume ./storage/tracker/lib:/var/lib/torrust/tracker:Z \
    --volume ./storage/tracker/log:/var/log/torrust/tracker:Z \
    --volume ./storage/tracker/etc:/etc/torrust/tracker:Z \
    torrust-tracker:release

With Podman

## Build Container Image
podman build --target release --tag torrust-tracker:release --file Containerfile .

## Setup Mapped Volumes
mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/

## Run Torrust Tracker Container Image
podman run -it \
    --env TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN="MySecretToken" \
    --env USER_ID="$(id -u)" \
    --publish 0.0.0.0:7070:7070/tcp \
    --publish 0.0.0.0:6969:6969/udp \
    --publish 0.0.0.0:1212:1212/tcp \
    --volume ./storage/tracker/lib:/var/lib/torrust/tracker:Z \
    --volume ./storage/tracker/log:/var/log/torrust/tracker:Z \
    --volume ./storage/tracker/etc:/etc/torrust/tracker:Z \
    docker.io/torrust-tracker:release

Docker Compose

The docker-compose configuration includes the MySQL service configuration. If you want to use MySQL instead of SQLite you should verify the /etc/torrust/tracker/tracker.toml (i.e ./storage/tracker/etc/tracker.toml) configuration:

[core.database]
driver = "mysql"
path = "mysql://db_user:db_user_secret_password@mysql:3306/torrust_tracker"

Important: if the MySQL password contains reserved URL characters (for example +, /, @, or :), it must be percent-encoded in the DSN password component. For example, if the raw password is a+b/c, use a%2Bb%2Fc in the DSN.

When generating secrets automatically, prefer URL-safe passwords (A-Z, a-z, 0-9, -, _) to avoid DSN parsing issues.

Build and Run:

docker build --target release --tag torrust-tracker:release --file Containerfile .

mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/

USER_ID=$(id -u) \
    TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN="MySecretToken" \
    docker compose up --build

After running the compose up command you will have two running containers:

$ docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS                   PORTS                                                                                                                             NAMES
06feacb91a9e   torrust-tracker   "cargo run"              18 minutes ago   Up 4 seconds             0.0.0.0:1212->1212/tcp, :::1212->1212/tcp, 0.0.0.0:7070->7070/tcp, :::7070->7070/tcp, 0.0.0.0:6969->6969/udp, :::6969->6969/udp   torrust-tracker-1
34d29e792ee2   mysql:8.0         "docker-entrypoint.s…"   18 minutes ago   Up 5 seconds (healthy)   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp                                                                              torrust-mysql-1

And you should be able to use the application, for example making a request to the API:

https://127.0.0.1:1212/api/v1/stats?token=MySecretToken

You can stop the containers with:

docker compose down

Additionally, you can delete all resources (containers, volumes, networks) with:

docker compose down -v

Access Mysql with docker

These are some useful commands for MySQL.

Open a shell in the MySQL container using docker or docker-compose.

docker exec -it torrust-mysql-1 /bin/bash
docker compose exec mysql /bin/bash

Connect to MySQL from inside the MySQL container or from the host:

mysql -h127.0.0.1 -uroot -proot_secret_password

The when MySQL container is started the first time, it creates the database, user, and permissions needed. If you see the error "Host is not allowed to connect to this MySQL server" you can check that users have the right permissions in the database. Make sure the user root and db_user can connect from any host (%).

mysql> SELECT host, user FROM mysql.user;
+-----------+------------------+
| host      | user             |
+-----------+------------------+
| %         | db_user          |
| %         | root             |
| localhost | mysql.infoschema |
| localhost | mysql.session    |
| localhost | mysql.sys        |
| localhost | root             |
+-----------+------------------+
6 rows in set (0.00 sec)

If the database, user or permissions are not created the reason could be the MySQL container volume can be corrupted. Delete it and start again the containers.

SSL Certificates

You can use a certificate for localhost. You can create your localhost certificate and use it in the storage folder and the configuration file (tracker.toml). For example:

The storage folder must contain your certificates:

storage/tracker/lib/tls
                    ├── localhost.crt
                    └── localhost.key
storage/http_api/lib/tls
                     ├── localhost.crt
                     └── localhost.key

You have not enabled it in your tracker.toml file:

[http_trackers.tsl_config]
ssl_cert_path = "./storage/tracker/lib/tls/localhost.crt"
ssl_key_path = "./storage/tracker/lib/tls/localhost.key"

[http_api.tsl_config]
ssl_cert_path = "./storage/http_api/lib/tls/localhost.crt"
ssl_key_path = "./storage/http_api/lib/tls/localhost.key"

NOTE: you can enable it independently for each HTTP tracker or the API.

If you enable the SSL certificate for the API, for example, you can load the API with this URL:

https://localhost:1212/api/v1/stats?token=MyAccessToken

Prod environment

In this section, you will learn how to deploy the tracker to a single docker container in Azure Container Instances.

NOTE: Azure Container Instances is a solution when you want to run an isolated container. If you need full container orchestration, including service discovery across multiple containers, automatic scaling, and coordinated application upgrades, we recommend Kubernetes.

Deploy to Azure Container Instance following docker documentation.

You have to create the ACI context and the storage:

docker context create aci myacicontext
docker context use myacicontext
docker volume create test-volume --storage-account torrustracker

You need to create all the files needed by the application in the storage dir storage/lib/database.

And finally, you can run the container:

docker run \
    --env USER_ID="$(id -u)" \
    --publish 6969:6969/udp \
    --publish 7070:7070/tcp \
    --publish 1212:1212/tcp \
    --volume torrustracker/lib:/var/lib/torrust/tracker:rw \
    --volume torrustracker/log:/var/log/torrust/tracker:rw \
    --volume torrustracker/etc:/etc/torrust/tracker:rw \
    registry.hub.docker.com/torrust/tracker:latest

Detach from container logs when the container starts. By default, the command line stays attached and follows container logs.

docker run \
    --detach
    --env USER_ID="$(id -u)" \
    --publish 6969:6969/udp \
    --publish 7070:7070/tcp \
    --publish 1212:1212/tcp \latest
    --volume torrustracker/lib:/var/lib/torrust/tracker:rw \
    --volume torrustracker/log:/var/log/torrust/tracker:rw \
    --volume torrustracker/etc:/etc/torrust/tracker:rw \
    registry.hub.docker.com/torrust/tracker:latest

You should see something like this:

[+] Running 2/2
 ⠿ Group intelligent-hawking  Created                                                                                                                                                                    5.0s
 ⠿ intelligent-hawking        Created                                                                                                                                                                   41.7s
2022-12-08T18:39:19.697869300+00:00 [torrust_tracker::logging][INFO] logging initialized.
2022-12-08T18:39:19.712651100+00:00 [torrust_tracker::jobs::udp_tracker][INFO] Starting UDP server on: 0.0.0.0:6969
2022-12-08T18:39:19.712792700+00:00 [torrust_tracker::jobs::tracker_api][INFO] Starting Torrust API server on: 0.0.0.0:1212
2022-12-08T18:39:19.725124+00:00 [torrust_tracker::jobs::tracker_api][INFO] Torrust API server started

You can see the container with:

$ docker ps
CONTAINER ID          IMAGE                                                      COMMAND             STATUS              PORTS
intelligent-hawking   registry.hub.docker.com/torrust/tracker:latest                       Running             4.236.213.57:6969->6969/udp, 4.236.213.57:1212->1212/tcp

After a while, you can use the tracker API http://4.236.213.57:1212/api/v1/stats?token=MyAccessToken and the UDP tracker with your BitTorrent client using this tracker announce URL udp://4.236.213.57:6969.

NOTES:

Links