Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
!README.md
!setup.py
!requirements.txt
!requirements-full.txt

# ignore on every level
**/__pycache__/
Expand Down
63 changes: 63 additions & 0 deletions docker/Dockerfile.offline
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
FROM python:3.11-slim AS build

ARG TARGETPLATFORM
ARG BUILDPLATFORM

COPY ./thingsboard_gateway /thingsboard_gateway
COPY . .

ENV PATH="/root/.cargo/bin:/root/.local/bin:$PATH" \
PYTHONPATH="." \
configs="/thingsboard_gateway/config" \
extensions="/thingsboard_gateway/extensions" \
logs="/thingsboard_gateway/logs"

# Installs ALL connector dependencies (requirements-full.txt) at build time so
# that the image is fully self-contained and requires no internet access at runtime.
# TBUtility.install_package() calls inside connectors will be no-ops because the
# packages are already present when the connector module is imported.
RUN mkdir -p /default-config/config /default-config/extensions/ && \
cp -r /thingsboard_gateway/config/* /default-config/config/ && \
cp -r /thingsboard_gateway/extensions/* /default-config/extensions && \
echo "Running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log && \
apt-get update && \
apt-get install -y --no-install-recommends \
gcc python3-dev build-essential libssl-dev libffi-dev zlib1g-dev \
python3-grpcio curl pkg-config libssl-dev \
unixodbc-dev cmake && \
case "$TARGETPLATFORM" in \
"linux/amd64") DEFAULT_HOST="x86_64-unknown-linux-gnu";; \
"linux/386") DEFAULT_HOST="i686-unknown-linux-gnu";; \
"linux/arm64") DEFAULT_HOST="aarch64-unknown-linux-gnu";; \
"linux/arm/v7") DEFAULT_HOST="armv7-unknown-linux-gnueabihf";; \
*) \
echo "Unsupported platform detected. Falling back to x86_64."; \
DEFAULT_HOST="x86_64-unknown-linux-gnu";; \
esac && \
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host=$DEFAULT_HOST --profile minimal && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* && \
echo '#!/bin/sh\n\
# Main start script\n\
CONF_FOLDER="/thingsboard_gateway/config"\n\
FIRSTLAUNCH="${CONF_FOLDER}/.firstlaunch"\n\
if [ ! -f "$FIRSTLAUNCH" ]; then\n\
cp -r /default-config/config/* /thingsboard_gateway/config/\n\
cp -r /default-config/extensions/* /thingsboard_gateway/extensions/\n\
touch $FIRSTLAUNCH\n\
echo "#Remove this file only if you want to recreate default config files! This will overwrite existing files" > $FIRSTLAUNCH\n\
fi\n\
python /thingsboard_gateway/tb_gateway.py' > /start-gateway.sh && chmod +x /start-gateway.sh && \
python3 -m pip install --no-cache-dir --upgrade pip setuptools wheel && \
python3 -m pip install --no-cache-dir cryptography && \
python3 -m pip install --no-cache-dir -r requirements-full.txt && \
(rustup self uninstall -y || { \
echo "rustup uninstall failed, removing manually..."; \
rm -rf /root/.rustup /root/.cargo; \
}) && \
apt-get remove --purge -y gcc python3-dev build-essential libssl-dev libffi-dev zlib1g-dev pkg-config unixodbc-dev && \
apt-get autoremove -y

VOLUME ["${configs}", "${extensions}", "${logs}"]

CMD [ "/bin/sh", "/start-gateway.sh" ]
89 changes: 86 additions & 3 deletions docker_build_multiarch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ set -e
# Supported operations:
# 1. Default: Build for current platform and load into local Docker
# 2. --push: Build for one or more platforms and push to remote registry
# 3. --platform <list>: Manually specify platform(s) (e.g. linux/amd64,linux/arm64)
# 3. --offline: Build with all connector dependencies pre-installed (no internet
# required at runtime). Uses docker/Dockerfile.offline which installs
# requirements-full.txt instead of requirements.txt.
# 4. --save: Build for current platform and export as a tar.gz file
# (for deployment in air-gapped / offline environments)
# 5. --platform <list>: Manually specify platform(s) (e.g. linux/amd64,linux/arm64)
# If omitted, supported platforms are auto-detected.
# 4. --help: Show this help
# 6. --help: Show this help
#
# Usage Examples:
# ./docker_build_multiarch.sh
Expand All @@ -37,9 +42,22 @@ set -e
# ./docker_build_multiarch.sh --push -r myregistry/tb-gateway --platform linux/amd64,linux/arm64
# → Push multi-arch image to registry with manually specified platforms
#
# ./docker_build_multiarch.sh --offline
# → Build self-contained offline image (all connector deps included) for current platform
#
# ./docker_build_multiarch.sh --offline --save
# → Build offline image and export as tar.gz for air-gapped deployment
#
# ./docker_build_multiarch.sh --offline --push -r myregistry/tb-gateway
# → Build and push offline image to registry
#
# ./docker_build_multiarch.sh --save
# → Build online image and export as tar.gz
#
# Notes:
# -r or --repository is required when using --push
# --platform is optional and overrides auto-detection when provided
# --save and --push are mutually exclusive
###############################################################################

# ─── Configurable Defaults ────────────────────────────────────────────────
Expand All @@ -51,6 +69,8 @@ BUILDER_NAME="multiarch-builder"

# ─── Argument Parsing ─────────────────────────────────────────────────────
PUSH=false
SAVE=false
OFFLINE=false
REPOSITORY=""

while [[ $# -gt 0 ]]; do
Expand All @@ -71,6 +91,14 @@ while [[ $# -gt 0 ]]; do
REPOSITORY="$2"
shift 2
;;
--save)
SAVE=true
shift
;;
--offline)
OFFLINE=true
shift
;;
-h|--help)
echo "Usage: ./docker_build_multiarch.sh [OPTIONS]"
echo ""
Expand All @@ -80,12 +108,18 @@ while [[ $# -gt 0 ]]; do
echo " --push Push the image to a registry (requires -r)"
echo " -r, --repository REPO Target repository (e.g. myrepo/tb-gateway)"
echo " --platform PLATFORMS Manually set platforms (comma-separated)"
echo " --offline Build self-contained image with all connector dependencies pre-installed"
echo " (uses Dockerfile.offline + requirements-full.txt, no internet needed at runtime)"
echo " --save Build and export image as a tar.gz file for air-gapped deployment"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " ./docker_build_multiarch.sh"
echo " ./docker_build_multiarch.sh --push -r myrepo/tb-gateway"
echo " ./docker_build_multiarch.sh --push -r myrepo/tb-gateway --platform linux/amd64,linux/arm64"
echo " ./docker_build_multiarch.sh --offline"
echo " ./docker_build_multiarch.sh --offline --save"
echo " ./docker_build_multiarch.sh --offline --push -r myrepo/tb-gateway"
exit 0
;;
*)
Expand All @@ -101,6 +135,19 @@ if [[ "$PUSH" == true && -z "$REPOSITORY" ]]; then
exit 1
fi

if [[ "$PUSH" == true && "$SAVE" == true ]]; then
echo "[X] Error: --push and --save are mutually exclusive."
exit 1
fi

if [[ "$OFFLINE" == true ]]; then
DOCKERFILE_PATH="docker/Dockerfile.offline"
echo "[*] Offline mode: using $DOCKERFILE_PATH (requirements-full.txt, all connector deps pre-installed)"
fi

# ─── Version Detection ────────────────────────────────────────────────────
VERSION=$(grep -Po 'VERSION[ ,]=[ ,]"\K(([0-9])+(\.){0,1})+' thingsboard_gateway/version.py 2>/dev/null || echo "latest")

IMAGE_TAG="${REPOSITORY:-$DEFAULT_IMAGE}:${TAG}"

# ─── Setup Buildx ─────────────────────────────────────────────────────────
Expand All @@ -117,7 +164,7 @@ echo "[*] Registering QEMU emulation..."
docker run --rm --privileged tonistiigi/binfmt --install all

# ─── Detect Supported Platforms If Not Set ─────────────────────────────────
if [[ -z "$PLATFORMS" ]]; then
if [[ -z "${PLATFORMS:-}" ]]; then
echo "[*] Parsing base image from Dockerfile..."
BASE_IMAGE=$(awk '/^FROM/ { for (i=1; i<=NF; i++) if ($i !~ /FROM|--platform=|\$TARGETPLATFORM|AS/) print $i }' "$DOCKERFILE_PATH" | head -n1)

Expand Down Expand Up @@ -159,6 +206,42 @@ if [[ "$PUSH" == true ]]; then
--tag "$IMAGE_TAG" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--push "$CONTEXT"

elif [[ "$SAVE" == true ]]; then
# --save: build for a single platform and export as tar.gz for air-gapped deployment
SAVE_PLATFORM="${PLATFORMS%%,*}" # use only the first platform if multiple specified
if [[ "$PLATFORMS" == *","* ]]; then
echo "[!] --save supports a single platform. Using: $SAVE_PLATFORM"
echo "[!] To export multiple platforms separately, run --save once per platform."
fi

ARCH="${SAVE_PLATFORM//\//_}" # e.g. linux/amd64 → linux_amd64
OFFLINE_SUFFIX=""
[[ "$OFFLINE" == true ]] && OFFLINE_SUFFIX="-offline"
OUTPUT_FILE="tb-gateway-${VERSION}${OFFLINE_SUFFIX}-${ARCH}.tar.gz"

echo "[*] Building image for: $SAVE_PLATFORM"
docker buildx build \
--platform "$SAVE_PLATFORM" \
--file "$DOCKERFILE_PATH" \
--tag "$IMAGE_TAG" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--load "$CONTEXT"

echo "[*] Exporting image to: $OUTPUT_FILE"
docker save "$IMAGE_TAG" | gzip > "$OUTPUT_FILE"

echo "[✓] Build completed!"
echo " Image Tag : $IMAGE_TAG"
echo " Platform : $SAVE_PLATFORM"
echo " Output file : $OUTPUT_FILE"
echo ""
echo "To deploy in an air-gapped environment:"
echo " 1. Transfer $OUTPUT_FILE to the target host"
echo " 2. docker load < $OUTPUT_FILE"
echo " 3. docker compose up -d (update docker-compose.yml image: field to $IMAGE_TAG)"
exit 0

else
CURRENT_PLATFORM="$(docker version -f '{{.Server.Os}}')/$(docker version -f '{{.Server.Arch}}')"
echo "[*] Building and loading image for current platform: $CURRENT_PLATFORM"
Expand Down
Loading