This is an automated email from the ASF dual-hosted git repository.
yongzao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 77cf7564701 [AINode] Renew AINode docker package script (#17289)
77cf7564701 is described below
commit 77cf7564701ae58314260006b29f241e1504cbed
Author: Yongzao <[email protected]>
AuthorDate: Thu Mar 12 13:09:08 2026 +0800
[AINode] Renew AINode docker package script (#17289)
---
.dockerignore | 2 +
docker/ReadMe.md | 20 ++
.../main/DockerCompose/docker-compose-ainode.yml | 3 +
docker/src/main/Dockerfile-1.0.0-ainode | 66 -----
docker/src/main/Dockerfile-2.0.x-ainode | 88 +++++++
docker/src/main/ainode-entrypoint.sh | 150 ++++++++++++
docker/src/main/build-ainode.sh | 270 +++++++++++++++++++++
7 files changed, 533 insertions(+), 66 deletions(-)
diff --git a/.dockerignore b/.dockerignore
index 288d980b3f2..ecb881d8d3d 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -20,3 +20,5 @@
*
!distribution
!docker/src/main/DockerCompose/start-1c1d.sh
+!docker/src/main/ainode-build-data/
+!docker/src/main/ainode-entrypoint.sh
\ No newline at end of file
diff --git a/docker/ReadMe.md b/docker/ReadMe.md
index 1574099c374..f674364626b 100644
--- a/docker/ReadMe.md
+++ b/docker/ReadMe.md
@@ -53,6 +53,9 @@ e.g.
./do-docker-build.sh -t standalone -v 1.0.0
# for ainode, start from 2.0.5
./do-docker-build.sh -t ainode -v 2.0.5-SNAPSHOT
+# for ainode, start from 2.0.8
+cd src/main
+./build-ainode.sh -v 2.0.8-SNAPSHOT -d /data/ainode
```
Notice:
Make directory of src/main/target and put the zip file downloading from the
official download page.
@@ -91,6 +94,23 @@ Please download `docker-compose-ainode.yml` in
`docker/src/main/DockerCompose` f
docker compose -f docker-compose-ainode.yml up -d
```
+Start from v2.0.7, run
+```shell
+docker run -d \
+ --name iotdb-ainode \
+ --network host \
+ -p 10810:10810 \
+ -p 8080:8080 \
+ -e AIN_SEED_CONFIG_NODE=127.0.0.1:10710 \
+ -e AIN_RPC_ADDRESS=127.0.0.1 \
+ -e AIN_RPC_PORT=10810 \
+ -e AIN_CLUSTER_INGRESS_ADDRESS=127.0.0.1 \
+ -e AIN_CLUSTER_INGRESS_PORT=6667 \
+ -e AIN_CLUSTER_INGRESS_USERNAME=root \
+ -e AIN_CLUSTER_INGRESS_PASSWORD=root \
+ apache/iotdb:2.0.7-SNAPSHOT-ainode
+```
+
## Quick start
We provide `docker-compose-cluster-1c1d1a.yml` in
`docker/src/main/DockerCompose`. Downloading this yaml file, both standalone
and ainode docker first. Subsequently, you can easily obtain a IoTDB cluster,
which consists of a ConfigNode, a DataNode and a AINode, in your local machine.
diff --git a/docker/src/main/DockerCompose/docker-compose-ainode.yml
b/docker/src/main/DockerCompose/docker-compose-ainode.yml
index e393d9ccffe..c61567aeca2 100644
--- a/docker/src/main/DockerCompose/docker-compose-ainode.yml
+++ b/docker/src/main/DockerCompose/docker-compose-ainode.yml
@@ -40,9 +40,12 @@ services:
- ain_cluster_ingress_username=root
- ain_cluster_ingress_password=root
- ain_cluster_ingress_time_zone=UTC+8
+ ports:
+ - "10810:10810"
volumes:
- ainode-data:/ainode/data
- ./logs/ainode:/ainode/logs
+ restart: unless-stopped
# - ./lib/ainode:/ainode/lib # Uncomment for rolling upgrade
# Note: Some environments set an extremely high container nofile limit
(~2^30 = 1073741824).
# This can make the startup step "Checking whether the ports are already
occupied..." appear to hang (lsof slow).
diff --git a/docker/src/main/Dockerfile-1.0.0-ainode
b/docker/src/main/Dockerfile-1.0.0-ainode
deleted file mode 100644
index 7e315c64939..00000000000
--- a/docker/src/main/Dockerfile-1.0.0-ainode
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-FROM python:3.12-slim-bullseye
-ARG version=2.0.5-SNAPSHOT
-ARG target=apache-iotdb-${version}-ainode-bin
-
-# replace deb source when necessary
-# RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list &&
\
-# sed -i
's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g'
/etc/apt/sources.list
-
-# replace pip source when necessary
-# RUN pip config set global.index-url
https://mirrors.ustc.edu.cn/pypi/web/simple
-
-RUN apt update \
- && apt install lsof dos2unix procps unzip dumb-init wget inetutils-ping
libopenblas-dev liblapack-dev gfortran gcc g++ -y \
- && apt autoremove -y \
- && apt purge --auto-remove -y \
- && apt clean -y
-
-COPY target/${target}.zip /
-RUN cd / && unzip ${target}.zip \
- && rm ${target}.zip \
- && mv ${target} ainode
-
-ENV IOTDB_AINODE_HOME=/ainode VERSION=${version}
-WORKDIR ${IOTDB_AINODE_HOME}/sbin
-
-COPY DockerCompose/replace-conf-from-env.sh .
-COPY DockerCompose/entrypoint.sh .
-
-RUN chmod +x *.sh && dos2unix *.sh \
- && dos2unix ${IOTDB_AINODE_HOME}/conf/*.sh
-
-# use huggingface mirrors when necessary
-ARG hf_web=huggingface.co
-# ARG hf_web=hf-mirror.com
-RUN mkdir -p ${IOTDB_AINODE_HOME}/data/ainode/models/weights/sundial && \
- mkdir -p ${IOTDB_AINODE_HOME}/data/ainode/models/weights/timer_xl
-RUN wget -O
${IOTDB_AINODE_HOME}/data/ainode/models/weights/sundial/config.json
https://${hf_web}/thuml/sundial-base-128m/resolve/main/config.json && \
- wget -O
${IOTDB_AINODE_HOME}/data/ainode/models/weights/sundial/model.safetensors
https://${hf_web}/thuml/sundial-base-128m/resolve/main/model.safetensors
-RUN wget -O
${IOTDB_AINODE_HOME}/data/ainode/models/weights/timer_xl/config.json
https://${hf_web}/thuml/timer-base-84m/resolve/main/config.json && \
- wget -O
${IOTDB_AINODE_HOME}/data/ainode/models/weights/timer_xl/model.safetensors
https://${hf_web}/thuml/timer-base-84m/resolve/main/model.safetensors
-
-
-ENV PATH="${IOTDB_AINODE_HOME}/sbin/:${IOTDB_AINODE_HOME}/tools/:${PATH}"
-RUN bash start-ainode.sh || true
-RUN rm -r ${IOTDB_AINODE_HOME}/logs/*
-
-ENTRYPOINT ["/usr/bin/dumb-init", "--"]
-CMD ["bash", "-c", "entrypoint.sh ainode"]
diff --git a/docker/src/main/Dockerfile-2.0.x-ainode
b/docker/src/main/Dockerfile-2.0.x-ainode
new file mode 100644
index 00000000000..57965f6014d
--- /dev/null
+++ b/docker/src/main/Dockerfile-2.0.x-ainode
@@ -0,0 +1,88 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+FROM python:3.11-slim-bullseye
+
+# Build argument: Version number (required)
+ARG VERSION
+
+# Fail build if VERSION is not provided
+RUN if [ -z "$VERSION" ]; then echo "ERROR: VERSION build argument is
required" && exit 1; fi
+
+# Set environment variables
+ENV IOTDB_HOME=/ainode \
+ PATH=$PATH:/ainode/sbin \
+ DEBIAN_FRONTEND=noninteractive
+
+# Install system dependencies
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ unzip \
+ procps \
+ netcat-openbsd \
+ curl \
+ vim \
+ tzdata \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+# Set working directory
+WORKDIR /tmp
+
+# Copy and extract the distribution package
+# Note: Build context is project root (../../.. relative to this Dockerfile)
+COPY distribution/target/apache-iotdb-${VERSION}-ainode-bin.zip /tmp/
+
+# Extract and rename directory
+RUN unzip -q apache-iotdb-${VERSION}-ainode-bin.zip \
+ && mv apache-iotdb-${VERSION}-ainode-bin /ainode \
+ && rm -f apache-iotdb-${VERSION}-ainode-bin.zip \
+ && mkdir -p /ainode/logs /ainode/data/ainode
+
+# Copy data directory from build server to image
+# The build script copies /data/ainode to docker/src/main/ainode-build-data/
before build
+# Note: The trailing slash is important - it copies contents not the directory
itself
+COPY docker/src/main/ainode-build-data/ /ainode/data/ainode/
+
+
+# Set directory permissions
+RUN chmod -R 755 /ainode/sbin \
+ && chmod +x /ainode/sbin/*.sh \
+ && chmod -R 777 /ainode/data /ainode/logs
+
+# Health check configuration
+HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
+ CMD curl -f http://localhost:8080/health || exit 1
+
+# Expose ports
+# 10810: AINode RPC port (ain_inference_rpc_port)
+# 8080: Health check / management port
+EXPOSE 10810 8080
+
+# Copy entrypoint script (relative to project root)
+COPY docker/src/main/ainode-entrypoint.sh /ainode/sbin/
+RUN chmod +x /ainode/sbin/ainode-entrypoint.sh
+
+# Set working directory
+WORKDIR /ainode
+
+# Use entrypoint script as startup command
+ENTRYPOINT ["/ainode/sbin/ainode-entrypoint.sh"]
+
+# Default command (can be overridden)
+CMD ["start"]
\ No newline at end of file
diff --git a/docker/src/main/ainode-entrypoint.sh
b/docker/src/main/ainode-entrypoint.sh
new file mode 100644
index 00000000000..bf60a6ce760
--- /dev/null
+++ b/docker/src/main/ainode-entrypoint.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# AINode Docker Entrypoint Script
+# Supports configuration via environment variables for iotdb-ainode.properties
+#
+
+set -e
+
+IOTDB_HOME="/ainode"
+CONF_FILE="${IOTDB_HOME}/conf/iotdb-ainode.properties"
+
+# Logging function
+log() {
+ echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
+}
+
+# Initialize configuration file
+init_config() {
+ if [ ! -f "${CONF_FILE}" ]; then
+ log "ERROR: Configuration file not found: ${CONF_FILE}"
+ exit 1
+ fi
+
+ log "Initializing AINode configuration..."
+
+ # Backup original configuration
+ if [ ! -f "${CONF_FILE}.original" ]; then
+ cp "${CONF_FILE}" "${CONF_FILE}.original"
+ fi
+
+ # Update configuration based on environment variables
+ # Cluster configuration
+ update_config "cluster_name" "${CLUSTER_NAME:-defaultCluster}"
+ update_config "ain_seed_config_node"
"${AIN_SEED_CONFIG_NODE:-127.0.0.1:10710}"
+
+ # AINode service configuration
+ update_config "ain_rpc_address" "${AIN_RPC_ADDRESS:-0.0.0.0}"
+ update_config "ain_rpc_port" "${AIN_RPC_PORT:-10810}"
+
+ # DataNode connection configuration
+ update_config "ain_cluster_ingress_address"
"${AIN_CLUSTER_INGRESS_ADDRESS:-127.0.0.1}"
+ update_config "ain_cluster_ingress_port"
"${AIN_CLUSTER_INGRESS_PORT:-6667}"
+ update_config "ain_cluster_ingress_username"
"${AIN_CLUSTER_INGRESS_USERNAME:-root}"
+ update_config "ain_cluster_ingress_password"
"${AIN_CLUSTER_INGRESS_PASSWORD:-root}"
+
+ # Storage paths configuration
+ update_config "ain_system_dir" "${AIN_SYSTEM_DIR:-data/ainode/system}"
+ update_config "ain_models_dir" "${AIN_MODELS_DIR:-data/ainode/models}"
+
+ # Thrift compression configuration
+ update_config "ain_thrift_compression_enabled"
"${AIN_THRIFT_COMPRESSION_ENABLED:-0}"
+
+ log "Configuration initialized successfully"
+}
+
+# Update configuration item in properties file
+update_config() {
+ local key=$1
+ local value=$2
+
+ if grep -q "^${key}=" "${CONF_FILE}"; then
+ # Update existing configuration
+ sed -i "s|^${key}=.*|${key}=${value}|g" "${CONF_FILE}"
+ else
+ # Append new configuration
+ echo "${key}=${value}" >> "${CONF_FILE}"
+ fi
+}
+
+# Start AINode
+start_ainode() {
+ log "Starting AINode..."
+
+ # Check if already running
+ if [ -f "${IOTDB_HOME}/data/ainode.pid" ]; then
+ local pid=$(cat "${IOTDB_HOME}/data/ainode.pid")
+ if ps -p ${pid} > /dev/null 2>&1; then
+ log "AINode is already running with PID ${pid}"
+ return 0
+ fi
+ fi
+
+ # Use exec to replace current process and ensure proper signal handling
+ exec ${IOTDB_HOME}/sbin/start-ainode.sh
+}
+
+# Stop AINode
+stop_ainode() {
+ log "Stopping AINode..."
+ if [ -f "${IOTDB_HOME}/sbin/stop-ainode.sh" ]; then
+ ${IOTDB_HOME}/sbin/stop-ainode.sh
+ else
+ log "Stop script not found"
+ fi
+}
+
+# Handle signals for graceful shutdown
+trap stop_ainode SIGTERM SIGINT
+
+# Main logic
+case "${1:-start}" in
+ start)
+ init_config
+ start_ainode
+ ;;
+ stop)
+ stop_ainode
+ ;;
+ restart)
+ stop_ainode
+ sleep 10
+ init_config
+ start_ainode
+ ;;
+ status)
+ if [ -f "${IOTDB_HOME}/data/ainode.pid" ]; then
+ cat "${IOTDB_HOME}/data/ainode.pid"
+ else
+ echo "AINode is not running"
+ exit 1
+ fi
+ ;;
+ config)
+ # Only generate configuration without starting (for debugging)
+ init_config
+ cat "${CONF_FILE}"
+ ;;
+ *)
+ # Execute other commands directly
+ exec "$@"
+ ;;
+esac
\ No newline at end of file
diff --git a/docker/src/main/build-ainode.sh b/docker/src/main/build-ainode.sh
new file mode 100644
index 00000000000..44a5439c54c
--- /dev/null
+++ b/docker/src/main/build-ainode.sh
@@ -0,0 +1,270 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# AINode Docker Image Build Script
+# Run this script from docker/src/main directory
+# Usage: ./build.sh -v <version> [options]
+#
+
+set -e
+
+# Get script directory (should be docker/src/main)
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+# Project root is 3 levels up from docker/src/main
+PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
+
+# Default configuration
+VERSION=""
+IMAGE_NAME="apache/iotdb"
+IMAGE_TAG_SUFFIX="ainode"
+PUSH_IMAGE=false
+NO_CACHE=false
+DATA_DIR="/data/ainode"
+REGISTRY_PREFIX=""
+
+# Usage information
+usage() {
+ cat << EOF
+Usage: $0 -v <version> [options]
+
+Required:
+ -v, --version <version> Specify IoTDB version (e.g., 2.0.8)
+
+Options:
+ -p, --push Push image to registry after build
+ -n, --no-cache Build without Docker cache
+ -t, --tag <tag> Custom image tag (default: <version>-ainode)
+ -d, --data-dir <path> Data directory path (default: /data/ainode)
+ -r, --registry <url> Registry prefix (e.g., registry.example.com)
+ -h, --help Show this help message
+
+Examples:
+ # Build version 2.0.8
+ $0 -v 2.0.8
+
+ # Build and push
+ $0 -v 2.0.8 --push
+
+ # Build with custom data directory
+ $0 -v 2.0.8 --data-dir /mnt/data/ainode
+EOF
+}
+
+# Parse command line arguments
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ -v|--version)
+ VERSION="$2"
+ shift 2
+ ;;
+ -p|--push)
+ PUSH_IMAGE=true
+ shift
+ ;;
+ -n|--no-cache)
+ NO_CACHE=true
+ shift
+ ;;
+ -t|--tag)
+ CUSTOM_TAG="$2"
+ shift 2
+ ;;
+ -d|--data-dir)
+ DATA_DIR="$2"
+ shift 2
+ ;;
+ -r|--registry)
+ REGISTRY_PREFIX="$2"
+ shift 2
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "Unknown option: $1"
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+# Validate version
+if [ -z "$VERSION" ]; then
+ echo "Error: Version is required. Use -v <version> to specify."
+ usage
+ exit 1
+fi
+
+# Check if distribution package exists (relative to project root)
+DIST_FILE="${PROJECT_ROOT}/distribution/target/apache-iotdb-${VERSION}-ainode-bin.zip"
+if [ ! -f "$DIST_FILE" ]; then
+ echo "Error: Distribution file not found: $DIST_FILE"
+ echo "Please build the project first: mvn clean package -DskipTests"
+ exit 1
+fi
+
+# Check if data directory exists
+if [ ! -d "$DATA_DIR" ]; then
+ echo "Warning: Data directory does not exist: $DATA_DIR"
+ echo "Creating empty directory..."
+ mkdir -p "$DATA_DIR"
+fi
+
+# Determine image tag
+if [ -n "$CUSTOM_TAG" ]; then
+ IMAGE_TAG="${CUSTOM_TAG}"
+else
+ IMAGE_TAG="${VERSION}-${IMAGE_TAG_SUFFIX}"
+fi
+
+# Construct full image name
+if [ -n "$REGISTRY_PREFIX" ]; then
+ FULL_IMAGE_NAME="${REGISTRY_PREFIX}/${IMAGE_NAME}:${IMAGE_TAG}"
+else
+ FULL_IMAGE_NAME="${IMAGE_NAME}:${IMAGE_TAG}"
+fi
+
+echo "============================================"
+echo "Building AINode Docker Image"
+echo "============================================"
+echo "Version: ${VERSION}"
+echo "Distribution: ${DIST_FILE}"
+echo "Data Directory: ${DATA_DIR}"
+echo "Dockerfile: ${SCRIPT_DIR}/Dockerfile-2.0.x-ainode"
+echo "Image Name: ${FULL_IMAGE_NAME}"
+echo "Build Context: ${PROJECT_ROOT}"
+echo "Script Dir: ${SCRIPT_DIR}"
+echo "============================================"
+
+# Prepare data directory for Docker build context
+# Use a unique name to avoid .dockerignore conflicts with common patterns like
'tmp*'
+BUILD_DATA_DIR_NAME="ainode-build-data"
+TMP_DATA_DIR="${SCRIPT_DIR}/${BUILD_DATA_DIR_NAME}"
+
+echo "Preparing data directory for build context at: ${TMP_DATA_DIR}"
+
+# Clean up old data if exists
+if [ -d "$TMP_DATA_DIR" ]; then
+ echo "Cleaning up existing build data directory..."
+ rm -rf "$TMP_DATA_DIR"
+fi
+
+# Create directory and copy data
+mkdir -p "$TMP_DATA_DIR"
+if [ -d "$DATA_DIR" ] && [ "$(ls -A $DATA_DIR 2>/dev/null)" ]; then
+ echo "Copying data from ${DATA_DIR} to ${TMP_DATA_DIR}..."
+ cp -r "$DATA_DIR"/* "$TMP_DATA_DIR/"
+ echo "Copied $(ls -1 "$TMP_DATA_DIR" | wc -l) items"
+else
+ echo "No data to copy, created empty directory"
+fi
+
+# Verify the directory exists and show contents
+if [ ! -d "$TMP_DATA_DIR" ]; then
+ echo "Error: Failed to create temporary data directory: ${TMP_DATA_DIR}"
+ exit 1
+fi
+
+echo "Build data directory contents:"
+ls -la "$TMP_DATA_DIR" || echo "(empty directory)"
+
+# Check if .dockerignore exists and might exclude our directory
+DOCKERIGNORE_FILE="${PROJECT_ROOT}/.dockerignore"
+if [ -f "$DOCKERIGNORE_FILE" ]; then
+ if grep -q "ainode-build-data" "$DOCKERIGNORE_FILE" || grep -qE
"^\*|^tmp|^data" "$DOCKERIGNORE_FILE"; then
+ echo ""
+ echo "WARNING: .dockerignore file detected at ${DOCKERIGNORE_FILE}"
+ echo "It may exclude the '${BUILD_DATA_DIR_NAME}' directory from build
context."
+ echo "If build fails with 'COPY failed', add exception to
.dockerignore:"
+ echo " !docker/src/main/${BUILD_DATA_DIR_NAME}/"
+ echo ""
+ fi
+fi
+
+# Cleanup function
+cleanup() {
+ echo "Cleaning up temporary data directory: ${TMP_DATA_DIR}"
+ rm -rf "$TMP_DATA_DIR"
+}
+trap cleanup EXIT
+
+# Verify Dockerfile exists
+DOCKERFILE="${SCRIPT_DIR}/Dockerfile-2.0.x-ainode"
+if [ ! -f "$DOCKERFILE" ]; then
+ echo "Error: Dockerfile not found at: ${DOCKERFILE}"
+ exit 1
+fi
+
+# Build Docker image
+# Build context is PROJECT_ROOT (3 levels up from current script)
+BUILD_CMD="docker build"
+BUILD_CMD+=" --file ${DOCKERFILE}"
+BUILD_CMD+=" --build-arg VERSION=${VERSION}"
+BUILD_CMD+=" --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
+BUILD_CMD+=" --build-arg VCS_REF=$(git rev-parse --short HEAD 2>/dev/null ||
echo 'unknown')"
+
+if [ "$NO_CACHE" = true ]; then
+ BUILD_CMD+=" --no-cache"
+fi
+
+BUILD_CMD+=" --tag ${FULL_IMAGE_NAME}"
+BUILD_CMD+=" ${PROJECT_ROOT}"
+
+echo ""
+echo "Executing Docker build..."
+echo "Command: ${BUILD_CMD}"
+echo ""
+
+${BUILD_CMD} || {
+ echo ""
+ echo "Error: Docker build failed"
+ echo ""
+ echo "Troubleshooting tips:"
+ echo "1. If error is 'COPY failed: no such file or directory', check if
.dockerignore excludes 'docker/src/main/${BUILD_DATA_DIR_NAME}/'"
+ echo "2. Ensure Docker daemon is running"
+ echo "3. Try running with --no-cache option"
+ exit 1
+}
+
+echo ""
+echo "Build completed successfully: ${FULL_IMAGE_NAME}"
+
+# Push image if requested
+if [ "$PUSH_IMAGE" = true ]; then
+ echo "Pushing image to registry..."
+ docker push "${FULL_IMAGE_NAME}"
+
+ # Also push latest tag for release versions
+ if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ LATEST_TAG="latest-${IMAGE_TAG_SUFFIX}"
+ if [ -n "$REGISTRY_PREFIX" ]; then
+ LATEST_NAME="${REGISTRY_PREFIX}/${IMAGE_NAME}:${LATEST_TAG}"
+ else
+ LATEST_NAME="${IMAGE_NAME}:${LATEST_TAG}"
+ fi
+ echo "Tagging and pushing: ${LATEST_NAME}"
+ docker tag "${FULL_IMAGE_NAME}" "${LATEST_NAME}"
+ docker push "${LATEST_NAME}"
+ fi
+fi
+
+echo ""
+echo "Done!"
\ No newline at end of file