343 lines
11 KiB
Bash
Executable File
343 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
|
|
#
|
|
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
|
# and proprietary rights in and to this software, related documentation
|
|
# and any modifications thereto. Any use, reproduction, disclosure or
|
|
# distribution of this software and related documentation without an express
|
|
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
|
set -e
|
|
|
|
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
|
source $ROOT/utils/print_color.sh
|
|
DOCKER_DIR="${ROOT}/../docker"
|
|
|
|
function usage() {
|
|
print_info "Usage: ${0##*/}"
|
|
print_info "Copyright (c) 2024, NVIDIA CORPORATION."
|
|
}
|
|
|
|
# Initialize arguments
|
|
DOCKER_BUILDKIT=1
|
|
IGNORE_COMPOSITE_KEYS=0
|
|
ADDITIONAL_BUILD_ARGS=()
|
|
DOCKER_SEARCH_DIRS=(${DOCKER_DIR})
|
|
SKIP_REGISTRY_CHECK=0
|
|
BASE_DOCKER_REGISTRY_NAMES=("nvcr.io/isaac/ros")
|
|
|
|
# Read and parse config file if exists
|
|
#
|
|
# CONFIG_DOCKER_SEARCH_DIRS (array, can be empty)
|
|
|
|
if [[ -f "${ROOT}/.isaac_ros_common-config" ]]; then
|
|
. "${ROOT}/.isaac_ros_common-config"
|
|
fi
|
|
|
|
# Override with config from user home directory if exists
|
|
if [[ -f ~/.isaac_ros_common-config ]]; then
|
|
. ~/.isaac_ros_common-config
|
|
fi
|
|
|
|
# Prepend configured docker search dirs
|
|
if [ ${#CONFIG_DOCKER_SEARCH_DIRS[@]} -gt 0 ]; then
|
|
for (( i=${#CONFIG_DOCKER_SEARCH_DIRS[@]}-1 ; i>=0 ; i-- )); do
|
|
|
|
# If the path is relative, then prefix ROOT to the path
|
|
if [[ "${CONFIG_DOCKER_SEARCH_DIRS[i]}" != /* ]]; then
|
|
CONFIG_DOCKER_SEARCH_DIRS[$i]="${ROOT}/${CONFIG_DOCKER_SEARCH_DIRS[i]}"
|
|
fi
|
|
done
|
|
|
|
CONFIG_DOCKER_SEARCH_DIRS+=(${DOCKER_SEARCH_DIRS[@]})
|
|
DOCKER_SEARCH_DIRS=(${CONFIG_DOCKER_SEARCH_DIRS[@]})
|
|
fi
|
|
|
|
# Parse command-line args
|
|
VALID_ARGS=$(getopt -o hra:b:c:ki:n:u --long help,skip_registry_check,build_arg:,base_image:,context_dir:,disable_buildkit,image_key:,image_name:,ignore_composite_keys -- "$@")
|
|
eval set -- "$VALID_ARGS"
|
|
while [ : ]; do
|
|
case "$1" in
|
|
-a | --build_arg)
|
|
ADDITIONAL_BUILD_ARGS+=("$2")
|
|
shift 2
|
|
;;
|
|
-b | --base_image)
|
|
BASE_IMAGE_NAME="$2"
|
|
shift 2
|
|
;;
|
|
-c | --context_dir)
|
|
DOCKER_CONTEXT_DIR="$2"
|
|
shift 2
|
|
;;
|
|
-k | --disable_buildkit)
|
|
DOCKER_BUILDKIT=0
|
|
shift
|
|
;;
|
|
-i | --image_key)
|
|
TARGET_IMAGE_STR="$2"
|
|
shift 2
|
|
;;
|
|
-n | --image_name)
|
|
TARGET_IMAGE_NAME="$2"
|
|
shift 2
|
|
;;
|
|
-r | --skip_registry_check)
|
|
SKIP_REGISTRY_CHECK=1
|
|
shift
|
|
;;
|
|
-y | --ignore_composite_keys)
|
|
IGNORE_COMPOSITE_KEYS=1
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--) shift;
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check arguments
|
|
if [[ -z "$TARGET_IMAGE_STR" ]]; then
|
|
print_error "Target image not specified with -i/--image_key"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$TARGET_IMAGE_NAME" ]]; then
|
|
TARGET_IMAGE_NAME="${TARGET_IMAGE_STR//./-}-image"
|
|
print_warning "Target image name not specified, using ${TARGET_IMAGE_NAME}"
|
|
fi
|
|
|
|
if [[ ! -z "$DOCKER_CONTEXT_DIR" ]]; then
|
|
DOCKER_SEARCH_DIRS+=($DOCKER_CONTEXT_DIR)
|
|
fi
|
|
|
|
# Summarize final arguments for script
|
|
print_info "Building layered image for key ${TARGET_IMAGE_STR} as ${TARGET_IMAGE_NAME}"
|
|
if [[ ! -z "${BASE_IMAGE_NAME}" ]]; then
|
|
print_info "Build image on top of base: |${BASE_IMAGE_NAME}|"
|
|
fi
|
|
|
|
print_info "Using configured docker search paths: ${DOCKER_SEARCH_DIRS[*]}"
|
|
if [[ ! -z "${DOCKER_CONTEXT_DIR}" ]]; then
|
|
print_info "Docker context directory for final layer: ${DOCKER_CONTEXT_DIR}"
|
|
fi
|
|
for BUILD_ARG in "${ADDITIONAL_BUILD_ARGS[@]}"
|
|
do
|
|
print_info "Additional build arg: ${BUILD_ARG}"
|
|
done
|
|
if [[ $DOCKER_BUILDKIT -eq 0 ]]; then
|
|
print_warning "WARNING: Explicitly disabling BuildKit"
|
|
fi
|
|
|
|
if [[ $IGNORE_COMPOSITE_KEYS -eq 1 ]]; then
|
|
print_warning "WARNING: Explicitly disabling matching composite image keys"
|
|
fi
|
|
|
|
if [[ $SKIP_REGISTRY_CHECK -eq 1 ]]; then
|
|
print_warning "WARNING: Skipping remote registry check for prebuilt images"
|
|
fi
|
|
|
|
# Setup on-exit cleanup
|
|
ON_EXIT=()
|
|
function cleanup {
|
|
for command in "${ON_EXIT[@]}"
|
|
do
|
|
$command &>/dev/null
|
|
done
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
PLATFORM="$(uname -m)"
|
|
|
|
# Resolve Dockerfiles by matching target image ids to available files
|
|
TARGET_IMAGE_IDS=(${TARGET_IMAGE_STR//./ })
|
|
IMAGE_IDS=(${TARGET_IMAGE_IDS[@]})
|
|
|
|
# Loop over components and find largest tail sequences
|
|
# For example, a target image id of 'aarch64.jp5.carter.nav' should match
|
|
# Dockerfiles with suffixes in the following order:
|
|
# ".aarch64.jp5.carter.nav", ".jp5.carter.nav", ".carter.nav", ".nav"
|
|
# If the first file found is ".carter.nav", the matching recurses by then
|
|
# looking for the preceding components ".aarch64.jp5" in the same manner
|
|
DOCKERFILES=()
|
|
DOCKERFILE_CONTEXT_DIRS=()
|
|
until [ ${#IMAGE_IDS[@]} -le 0 ]; do
|
|
UNMATCHED_ID_COUNT=${#IMAGE_IDS[@]}
|
|
|
|
for (( i=0; i<${#IMAGE_IDS[@]}; i++ )) do
|
|
# Abort matching composite keys if disabled
|
|
if [[ $IGNORE_COMPOSITE_KEYS -gt 1 && $i -eq 1 ]]; then
|
|
break
|
|
fi
|
|
|
|
LAYER_IMAGE_IDS=${IMAGE_IDS[@]:i}
|
|
LAYER_IMAGE_SUFFIX="${LAYER_IMAGE_IDS[@]// /.}"
|
|
|
|
for DOCKER_SEARCH_DIR in ${DOCKER_SEARCH_DIRS[@]}; do
|
|
DOCKERFILE="${DOCKER_SEARCH_DIR}/Dockerfile.${LAYER_IMAGE_SUFFIX}"
|
|
|
|
if [[ -f "${DOCKERFILE}" ]]; then
|
|
DOCKERFILES+=(${DOCKERFILE})
|
|
DOCKERFILE_CONTEXT_DIRS+=(${DOCKER_SEARCH_DIR})
|
|
IMAGE_IDS=(${IMAGE_IDS[@]:0:i})
|
|
break 2
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [ ${UNMATCHED_ID_COUNT} -eq ${#IMAGE_IDS[@]} ]; then
|
|
UNMATCHED_IDS=${IMAGE_IDS[@]}
|
|
MATCHED_DOCKERFILES=${DOCKERFILES[@]}
|
|
print_error "Could not resolve Dockerfiles for target image ids: ${UNMATCHED_IDS// /.}"
|
|
|
|
if [ ${#DOCKERFILES[@]} -gt 0 ]; then
|
|
print_warning "Partially resolved the following Dockerfiles for target image: ${TARGET_IMAGE_STR}"
|
|
for DOCKERFILE in ${DOCKERFILES[@]}; do
|
|
print_warning "${DOCKERFILE}"
|
|
done
|
|
fi
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Find pre-built image if available
|
|
if [[ $SKIP_REGISTRY_CHECK -eq 0 && -z "${BASE_IMAGE_NAME}" ]]; then
|
|
# Generate the possible base image names to look for from first image key onward
|
|
BASE_IMAGE_FULLNAMES=()
|
|
BASE_DOCKERFILES=()
|
|
BASE_DOCKERFILE_CONTEXT_DIRS=()
|
|
BASE_IMAGE_DOCKERFILES_INDICES=()
|
|
for (( i=${#DOCKERFILES[@]}-1 ; i>=0 ; i-- )); do
|
|
BASE_DOCKERFILES+=(${DOCKERFILES[i]})
|
|
BASE_DOCKERFILE_CONTEXT_DIRS+=(${DOCKERFILE_CONTEXT_DIRS[i]})
|
|
BASE_IMAGE_KEYS=()
|
|
|
|
DOCKER_HASH_FILE=$(mktemp)
|
|
ON_EXIT+=("rm -Rf ${DOCKER_HASH_FILE}")
|
|
|
|
# Determine hash of all Dockerfiles for this base image name
|
|
for (( j=0 ; j<${#BASE_DOCKERFILES[@]} ; j++ )); do
|
|
BASE_DOCKERFILE=${BASE_DOCKERFILES[j]}
|
|
BASE_DOCKERFILE_CONTEXT_DIR=${BASE_DOCKERFILE_CONTEXT_DIRS[j]}
|
|
LAYER_IMAGE_SUFFIX=${BASE_DOCKERFILE#*"/Dockerfile."}
|
|
BASE_IMAGE_KEYS+=(${LAYER_IMAGE_SUFFIX//./ })
|
|
|
|
pushd . >/dev/null
|
|
cd $BASE_DOCKERFILE_CONTEXT_DIR
|
|
BASE_DOCKERFILE="Dockerfile.${LAYER_IMAGE_SUFFIX}"
|
|
md5sum ${BASE_DOCKERFILE} >> $DOCKER_HASH_FILE
|
|
popd >/dev/null
|
|
done
|
|
SOURCE_DOCKERFILE_HASH=($(md5sum $DOCKER_HASH_FILE))
|
|
|
|
# Determine base image name
|
|
for (( j=${#BASE_DOCKER_REGISTRY_NAMES[@]}-1 ; j>= 0; j-- )); do
|
|
BASE_DOCKER_REGISTRY_NAME=${BASE_DOCKER_REGISTRY_NAMES[j]}
|
|
BASE_IMAGE_TAG=${BASE_IMAGE_KEYS[*]}
|
|
BASE_IMAGE_TAG=${BASE_IMAGE_TAG// /.}
|
|
BASE_IMAGE_FULLNAME="${BASE_DOCKER_REGISTRY_NAME}:${BASE_IMAGE_TAG//./-}_${SOURCE_DOCKERFILE_HASH}"
|
|
BASE_IMAGE_FULLNAMES+=(${BASE_IMAGE_FULLNAME})
|
|
|
|
# Remember which index goes with this base image so we can skip those Dockerfiles
|
|
# if this image exists
|
|
BASE_IMAGE_DOCKERFILES_INDICES+=($i)
|
|
done
|
|
done
|
|
|
|
for (( i=${#BASE_IMAGE_FULLNAMES[@]}-1 ; i>=0 ; i-- )); do
|
|
BASE_IMAGE_FULLNAME=${BASE_IMAGE_FULLNAMES[i]}
|
|
|
|
# Check if image exists on remote server
|
|
print_info "Checking if base image ${BASE_IMAGE_FULLNAME} exists on remote registry"
|
|
OUTPUT=$(docker manifest inspect ${BASE_IMAGE_FULLNAME} >/dev/null 2>&1 ; echo $?)
|
|
if [[ ${OUTPUT} -eq 0 ]]; then
|
|
BASE_IMAGE_NAME=${BASE_IMAGE_FULLNAME}
|
|
DOCKERFILES=(${DOCKERFILES[@]:0:${BASE_IMAGE_DOCKERFILES_INDICES[i]-1}})
|
|
print_info "Found pre-built base image: ${BASE_IMAGE_FULLNAME}"
|
|
docker pull ${BASE_IMAGE_FULLNAME}
|
|
print_info "Finished pulling pre-built base image: ${BASE_IMAGE_FULLNAME}"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Arguments for docker build
|
|
BUILD_ARGS+=("--build-arg" "USERNAME="admin"")
|
|
BUILD_ARGS+=("--build-arg" "USER_UID=`id -u`")
|
|
BUILD_ARGS+=("--build-arg" "USER_GID=`id -g`")
|
|
BUILD_ARGS+=("--build-arg" "PLATFORM=$PLATFORM")
|
|
|
|
for BUILD_ARG in ${ADDITIONAL_BUILD_ARGS[@]}
|
|
do
|
|
BUILD_ARGS+=("--build-arg" "${BUILD_ARG}")
|
|
done
|
|
|
|
# Check if GPU is installed
|
|
if [[ $PLATFORM == "x86_64" ]]; then
|
|
GPU_ATTACHED=(`nvidia-smi -a | grep "Attached GPUs" || true`)
|
|
if [ -z $GPU_ATTACHED ]; then
|
|
print_warning "No GPU detected! Not setting build args for HAS_GPU"
|
|
else
|
|
BUILD_ARGS+=("--build-arg" "HAS_GPU="true"")
|
|
fi
|
|
fi
|
|
|
|
if [ ${#DOCKERFILES[@]} -gt 0 ]; then
|
|
print_info "Resolved the following ${#DOCKERFILES[@]} Dockerfiles for target image: ${TARGET_IMAGE_STR}"
|
|
for DOCKERFILE in ${DOCKERFILES[@]}; do
|
|
print_info "${DOCKERFILE}"
|
|
done
|
|
else
|
|
docker tag ${BASE_IMAGE_NAME} ${TARGET_IMAGE_NAME}
|
|
print_info "Nothing to build, retagged ${BASE_IMAGE_NAME} as ${TARGET_IMAGE_NAME}"
|
|
exit 0
|
|
fi
|
|
|
|
# Build image layers
|
|
for (( i=${#DOCKERFILES[@]}-1 ; i>=0 ; i-- )); do
|
|
DOCKERFILE=${DOCKERFILES[i]}
|
|
DOCKERFILE_CONTEXT_DIR=${DOCKERFILE_CONTEXT_DIRS[i]}
|
|
IMAGE_NAME=${DOCKERFILE#*"/Dockerfile."}
|
|
IMAGE_NAME="${IMAGE_NAME//./-}-image"
|
|
|
|
# Build the base images in layers first
|
|
BASE_IMAGE_ARG=
|
|
if [ $i -eq $(( ${#DOCKERFILES[@]} - 1 )) ]; then
|
|
if [[ ! -z "${BASE_IMAGE_NAME}" ]] ; then
|
|
BASE_IMAGE_ARG="--build-arg BASE_IMAGE="${BASE_IMAGE_NAME}""
|
|
fi
|
|
fi
|
|
|
|
if [ $i -lt $(( ${#DOCKERFILES[@]} - 1 )) ]; then
|
|
BASE_DOCKERFILE=${DOCKERFILES[i+1]}
|
|
BASE_IMAGE_NAME=${BASE_DOCKERFILE#*"/Dockerfile."}
|
|
BASE_IMAGE_NAME="${BASE_IMAGE_NAME//./-}-image"
|
|
|
|
BASE_IMAGE_ARG="--build-arg BASE_IMAGE="${BASE_IMAGE_NAME}""
|
|
fi
|
|
|
|
# The last image should be the target image name
|
|
# Use docker context dir script arg only for last image
|
|
DOCKER_CONTEXT_ARG=${DOCKERFILE_CONTEXT_DIR}
|
|
if [ $i -eq 0 ]; then
|
|
IMAGE_NAME=${TARGET_IMAGE_NAME}
|
|
if [[ ! -z "${DOCKER_CONTEXT_DIR}" ]]; then
|
|
DOCKER_CONTEXT_ARG=${DOCKER_CONTEXT_DIR}
|
|
fi
|
|
fi
|
|
|
|
print_warning "Building ${DOCKERFILE} as image: ${IMAGE_NAME} with base: ${BASE_IMAGE_NAME}"
|
|
|
|
DOCKER_BUILDKIT=${DOCKER_BUILDKIT} docker build -f ${DOCKERFILE} \
|
|
--network host \
|
|
-t ${IMAGE_NAME} \
|
|
${BASE_IMAGE_ARG} \
|
|
"${BUILD_ARGS[@]}" \
|
|
$@ \
|
|
${DOCKER_CONTEXT_ARG}
|
|
done
|