From d996b9b2d2e2a347b2f9176af914932dfbcc0768 Mon Sep 17 00:00:00 2001 From: GuangJie1 Date: Fri, 16 Aug 2024 09:22:37 +0800 Subject: [PATCH 1/3] Ascend-cann: Add cann's image to the openeuler image repository --- cann/8.0.RC1/22.03-lts-sp4/Dockerfile | 138 ++++++++++++++++++ cann/8.0.RC1/22.03-lts-sp4/scripts/cann.sh | 141 +++++++++++++++++++ cann/8.0.RC1/22.03-lts-sp4/scripts/python.sh | 81 +++++++++++ cann/README.md | 88 +++++++++--- cann/doc/image-info.yml | 70 +++++++++ cann/doc/picture/logo.png | Bin 0 -> 34070 bytes cann/meta.yml | 2 + 7 files changed, 500 insertions(+), 20 deletions(-) create mode 100644 cann/8.0.RC1/22.03-lts-sp4/Dockerfile create mode 100644 cann/8.0.RC1/22.03-lts-sp4/scripts/cann.sh create mode 100644 cann/8.0.RC1/22.03-lts-sp4/scripts/python.sh create mode 100644 cann/doc/image-info.yml create mode 100644 cann/doc/picture/logo.png diff --git a/cann/8.0.RC1/22.03-lts-sp4/Dockerfile b/cann/8.0.RC1/22.03-lts-sp4/Dockerfile new file mode 100644 index 00000000..3220066c --- /dev/null +++ b/cann/8.0.RC1/22.03-lts-sp4/Dockerfile @@ -0,0 +1,138 @@ +# Arguments +ARG BASE_VERSION=22.03-lts-sp4 +ARG PLATFORM=${TARGETPLATFORM} +ARG PY_VERSION=3.8 +ARG CANN_CHIP=910b +ARG CANN_VERSION=8.0.RC1 + +# Phase 1: Install Python +FROM openeuler/openeuler:${BASE_VERSION} as py-installer + +# Arguments +ARG PY_VERSION + +# Environment variables +ENV PATH=/usr/local/python${PY_VERSION}/bin:${PATH} + +# Install dependencies +RUN yum update -y && \ + yum install -y \ + gcc \ + gcc-c++ \ + make \ + cmake \ + curl \ + zlib-devel \ + bzip2-devel \ + openssl-devel \ + ncurses-devel \ + sqlite-devel \ + readline-devel \ + tk-devel \ + gdbm-devel \ + libpcap-devel \ + xz-devel \ + libev-devel \ + expat-devel \ + libffi-devel \ + systemtap-sdt-devel \ + && yum clean all \ + && rm -rf /var/cache/yum \ + && rm -rf /tmp/* + +# Copy files +COPY scripts/python.sh /tmp/python.sh + +# Download Python +RUN bash /tmp/python.sh --download + +# Install Python +RUN bash /tmp/python.sh --install && \ + rm /tmp/python.sh + +# Phase 2: Install CANN +FROM py-installer as cann-installer + +# Arguments +ARG PLATFORM +ARG CANN_CHIP +ARG CANN_VERSION + +# Install dependencies +RUN yum update -y && \ + yum install -y \ + gcc \ + gcc-c++ \ + make \ + cmake \ + unzip \ + zlib-devel \ + libffi-devel \ + openssl-devel \ + pciutils \ + net-tools \ + sqlite-devel \ + lapack-devel \ + gcc-gfortran \ + util-linux \ + findutils \ + && yum clean all \ + && rm -rf /var/cache/yum \ + && rm -rf /tmp/* + +# Copy files +COPY scripts/cann.sh /tmp/cann.sh + +# Download CANN +RUN bash /tmp/cann.sh --download + +# Install CANN +RUN bash /tmp/cann.sh --install && \ + rm /tmp/cann.sh + +# Phase 3: Copy results from previous phases +FROM openeuler/openeuler:${BASE_VERSION} as official + +# Arguments +ARG PLATFORM +ARG PY_VERSION +ARG CANN_CHIP +ARG CANN_VERSION + +# Environment variables +ENV PATH=/usr/local/python${PY_VERSION}/bin:${PATH} +ENV LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64/common/:/usr/local/Ascend/driver/lib64/driver/:${LD_LIBRARY_PATH} + +# Change the default shell +SHELL [ "/bin/bash", "-c" ] + +# Install dependencies +RUN yum update -y && \ + yum install -y \ + ca-certificates \ + bash \ + glibc \ + sqlite-devel \ + && yum clean all \ + && rm -rf /var/cache/yum \ + && rm -rf /tmp/* + +# Copy files +COPY --from=cann-installer /usr/local/python${PY_VERSION} /usr/local/python${PY_VERSION} +COPY --from=cann-installer /usr/local/Ascend /usr/local/Ascend +COPY --from=cann-installer /etc/Ascend /etc/Ascend + +# Set environment variables for Python +RUN PY_PATH="PATH=/usr/local/python${PY_VERSION}/bin:\${PATH}" && \ + echo "export ${PY_PATH}" >> /etc/profile && \ + echo "export ${PY_PATH}" >> ~/.bashrc + +# Set environment variables for CANN +RUN CANN_TOOLKIT_ENV_FILE="/usr/local/Ascend/ascend-toolkit/set_env.sh" && \ + DRIVER_LIBRARY_PATH="LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64/common/:/usr/local/Ascend/driver/lib64/driver/:\${LD_LIBRARY_PATH}" && \ + echo "export ${DRIVER_LIBRARY_PATH}" >> /etc/profile && \ + echo "export ${DRIVER_LIBRARY_PATH}" >> ~/.bashrc && \ + echo "source ${CANN_TOOLKIT_ENV_FILE}" >> /etc/profile && \ + echo "source ${CANN_TOOLKIT_ENV_FILE}" >> ~/.bashrc + +ENTRYPOINT [ "/bin/bash", "-c", "source /usr/local/Ascend/ascend-toolkit/set_env.sh && exec \"$@\"", "--" ] \ No newline at end of file diff --git a/cann/8.0.RC1/22.03-lts-sp4/scripts/cann.sh b/cann/8.0.RC1/22.03-lts-sp4/scripts/cann.sh new file mode 100644 index 00000000..ee5dcea6 --- /dev/null +++ b/cann/8.0.RC1/22.03-lts-sp4/scripts/cann.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +set -e + +get_architecture() { + # not case sensitive + shopt -s nocasematch + + case "${PLATFORM}" in + "linux/x86_64"|"linux/amd64") + ARCH="x86_64" + ;; + "linux/aarch64"|"linux/arm64") + ARCH="aarch64" + ;; + *) + echo "Error: Unsupported architecture ${PLATFORM}." + exit 1 + ;; + esac + + echo "${ARCH}" +} + +download_file() { + set +e + + local max_retries=10 + local retry_delay=10 + local url="$1" + local path="$2" + + for ((i=1; i<=max_retries; i++)); do + echo "Attempt $i of $max_retries..." + curl -fsSL -o "${path}" "${url}" + if [[ $? -eq 0 ]]; then + return 0 + else + echo "Download failed with error code $?. Retrying in ${retry_delay} seconds..." + sleep ${retry_delay} + fi + done + + echo "All attempts failed. Exiting." + return 1 +} + +download_cann() { + if [[ ${CANN_VERSION} == "8.0.RC2.alpha001" ]]; then + local url_prefix="https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL%20V100R001C18B800TP015" + elif [[ ${CANN_VERSION} == "8.0.RC2.alpha002" ]]; then + local url_prefix="https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL%20V100R001C18SPC805" + elif [[ ${CANN_VERSION} == "8.0.RC2.alpha003" ]]; then + local url_prefix="https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL%20V100R001C18SPC703" + else + local url_prefix="https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/CANN/CANN%20${CANN_VERSION}" + fi + local url_suffix="response-content-type=application/octet-stream" + local toolkit_url="${url_prefix}/${TOOLKIT_FILE}?${url_suffix}" + local kernels_url="${url_prefix}/${KERNELS_FILE}?${url_suffix}" + + if [ ! -f "${TOOLKIT_PATH}" ]; then + echo "Downloading ${TOOLKIT_FILE} from ${toolkit_url}" + download_file "${toolkit_url}" "${TOOLKIT_PATH}" + fi + + if [ ! -f "${KERNELS_PATH}" ]; then + echo "Downloading ${KERNELS_FILE} from ${kernels_url}" + download_file "${kernels_url}" "${KERNELS_PATH}" + fi + + echo "CANN ${CANN_VERSION} download successful." +} + +set_env() { + local cann_toolkit_env_file="${CANN_HOME}/ascend-toolkit/set_env.sh" + if [ ! -f "${cann_toolkit_env_file}" ]; then + echo "CANN Toolkit ${CANN_VERSION} installation failed." + exit 1 + else + local driver_path_env="LD_LIBRARY_PATH=${CANN_HOME}/driver/lib64/common/:${CANN_HOME}/driver/lib64/driver/:\${LD_LIBRARY_PATH}" && \ + echo "export ${driver_path_env}" >> /etc/profile + echo "export ${driver_path_env}" >> ~/.bashrc + echo "source ${cann_toolkit_env_file}" >> /etc/profile + echo "source ${cann_toolkit_env_file}" >> ~/.bashrc + source ${cann_toolkit_env_file} + fi +} + +install_cann() { + # Download installers + if [ ! -f "${TOOLKIT_PATH}" ] || [ ! -f "${KERNELS_PATH}" ]; then + echo "[WARNING] Installers do not exist, re-download them." + download_cann + fi + + # Install dependencies + pip install --no-cache-dir --upgrade pip + pip install --no-cache-dir \ + attrs cython numpy decorator sympy cffi pyyaml pathlib2 psutil protobuf scipy requests absl-py + + # Install CANN Toolkit + echo "Installing ${TOOLKIT_FILE}" + chmod +x "${TOOLKIT_PATH}" + bash "${TOOLKIT_PATH}" --quiet --install --install-for-all --install-path="${CANN_HOME}" + rm -f "${TOOLKIT_PATH}" + + # Set environment variables + set_env + + # Install CANN Kernels + echo "Installing ${KERNELS_FILE}" + chmod +x "${KERNELS_PATH}" + bash "${KERNELS_PATH}" --quiet --install --install-for-all --install-path="${CANN_HOME}" + rm -f "${KERNELS_PATH}" + + echo "CANN ${CANN_VERSION} installation successful." +} + +PLATFORM=${PLATFORM:=$(uname -s)/$(uname -m)} +ARCH=$(get_architecture) +CANN_HOME=${CANN_HOME:="/usr/local/Ascend"} +CANN_CHIP=${CANN_CHIP:="910b"} +CANN_VERSION=${CANN_VERSION:="8.0.RC1"} + +TOOLKIT_FILE="Ascend-cann-toolkit_${CANN_VERSION}_linux-${ARCH}.run" +KERNELS_FILE="Ascend-cann-kernels-${CANN_CHIP}_${CANN_VERSION}_linux.run" +TOOLKIT_PATH="/tmp/${TOOLKIT_FILE}" +KERNELS_PATH="/tmp/${KERNELS_FILE}" + +# Parse arguments +if [ "$1" == "--download" ]; then + download_cann +elif [ "$1" == "--install" ]; then + install_cann +elif [ "$1" == "--set_env" ]; then + set_env +else + echo "Unexpected arguments, use '--download', '--install' or '--set_env' instead" + exit 1 +fi \ No newline at end of file diff --git a/cann/8.0.RC1/22.03-lts-sp4/scripts/python.sh b/cann/8.0.RC1/22.03-lts-sp4/scripts/python.sh new file mode 100644 index 00000000..077ffa17 --- /dev/null +++ b/cann/8.0.RC1/22.03-lts-sp4/scripts/python.sh @@ -0,0 +1,81 @@ + +#!/bin/bash + +set -e + +PY_VERSION=${PY_VERSION:-"3.8"} +PY_MAJOR_VERSION=$(echo $PY_VERSION | cut -d'.' -f1) + +# Find the latest version +PY_LATEST_VERSION=$(curl -s https://www.python.org/ftp/python/ | grep -oE "${PY_VERSION}\.[0-9]+" | sort -V | tail -n 1) +if [ -z "${PY_LATEST_VERSION}" ]; then + echo "[WARNING] Could not find the latest version for Python ${PY_VERSION}" + exit 1 +else + echo "Latest Python version found: ${PY_LATEST_VERSION}" +fi + +PY_HOME="/usr/local/python${PY_VERSION}" +PY_INSTALLER_TGZ="Python-${PY_LATEST_VERSION}.tgz" +PY_INSTALLER_DIR="Python-${PY_LATEST_VERSION}" +PY_INSTALLER_URL="https://repo.huaweicloud.com/python/${PY_LATEST_VERSION}/${PY_INSTALLER_TGZ}" + +download_python() { + # Download python + echo "Downloading ${PY_INSTALLER_TGZ} from ${PY_INSTALLER_URL}" + curl -fsSL -o "/tmp/${PY_INSTALLER_TGZ}" "${PY_INSTALLER_URL}" + if [ $? -ne 0 ]; then + echo "Python ${PY_LATEST_VERSION} download failed." + exit 1 + fi +} + +install_python() { + # Download python + if [ ! -f "/tmp/${PY_INSTALLER_TGZ}" ]; then + echo "[WARNING] Installer do not exist, re-download it." + download_python + fi + + # Install python + echo "Installing ${PY_INSTALLER_DIR}" + tar -xf /tmp/${PY_INSTALLER_TGZ} -C /tmp + cd /tmp/${PY_INSTALLER_DIR} + mkdir -p /${PY_HOME}/lib + ./configure --prefix=${PY_HOME} --enable-shared LDFLAGS="-Wl,-rpath ${PY_HOME}/lib" + make -j $(nproc) + make altinstall + + # Create symbolic links at PY_HOME + ln -sf ${PY_HOME}/bin/python${PY_VERSION} ${PY_HOME}/bin/python${PY_MAJOR_VERSION} + ln -sf ${PY_HOME}/bin/pip${PY_VERSION} ${PY_HOME}/bin/pip${PY_MAJOR_VERSION} + ln -sf ${PY_HOME}/bin/python${PY_MAJOR_VERSION} ${PY_HOME}/bin/python + ln -sf ${PY_HOME}/bin/pip${PY_MAJOR_VERSION} ${PY_HOME}/bin/pip + + # Clean up + rm -rf /tmp/${PY_INSTALLER_TGZ} /tmp/${PY_INSTALLER_DIR} + echo "Python ${PY_LATEST_VERSION} installation successful." + ${PY_HOME}/bin/python -c "import sys; print(sys.version)" +} + +# Create symbolic links +create_links() { + ln -sf ${PY_HOME}/bin/python${PY_VERSION} /usr/bin/python${PY_VERSION} + ln -sf ${PY_HOME}/bin/pip${PY_VERSION} /usr/bin/pip${PY_VERSION} + ln -sf /usr/bin/python${PY_VERSION} /usr/bin/python${PY_MAJOR_VERSION} + ln -sf /usr/bin/pip${PY_VERSION} /usr/bin/pip${PY_MAJOR_VERSION} + ln -sf /usr/bin/python${PY_MAJOR_VERSION} /usr/bin/python + ln -sf /usr/bin/pip${PY_MAJOR_VERSION} /usr/bin/pip +} + +# Parse arguments +if [ "$1" == "--download" ]; then + download_python +elif [ "$1" == "--install" ]; then + install_python +elif [ "$1" == "--create_links" ]; then + create_links +else + echo "Unexpected arguments, use '--download', '--install' or '--create_links' instead" + exit 1 +fi diff --git a/cann/README.md b/cann/README.md index 8eaa79d1..e8b6ce53 100644 --- a/cann/README.md +++ b/cann/README.md @@ -1,31 +1,79 @@ -# cann - # Quick reference -- The official cann docker image. - -- Maintained by: [openEuler CloudNative SIG](https://gitee.com/openeuler/cloudnative) +- The official CANN docker image. -- Where to get help: [openEuler CloudNative SIG](https://gitee.com/openeuler/cloudnative), [openEuler](https://gitee.com/openeuler/community) +- Maintained by: [openEuler CloudNative SIG](https://gitee.com/openeuler/cloudnative). -# Build reference +- Where to get help: [openEuler CloudNative SIG](https://gitee.com/openeuler/cloudnative), [openEuler](https://gitee.com/openeuler/community). -1. Build images and push: -```shell -docker buildx build -t "openeuler/cann:$TAG" --platform linux/arm64 ./$TAG --push -``` +# CANN | openEuler +Current CANN docker images are built on the [openEuler](https://repo.openeuler.org/). This repository is free to use and exempted from per-user rate limits. -We are using `buildx` in here to generate ARM64 images on different host, see more in [Docker Buildx](https://docs.docker.com/buildx/working-with-buildx/) +AI-oriented heterogeneous compute architecture provides hierarchical APIs to help you quickly build AI applications and services based on the Ascend platform. -# How to use this image -Please run container with this image on Ascend platform of ARM64. -```shell -docker run --device $DEVICE --device /dev/davinci_manager --device /dev/devmm_svm --device /dev/hisi_hdc -v /usr/local/dcmi:/usr/local/dcmi -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info -it openeuler/cann:$TAG -``` +Learn more about on [CANN Document](https://www.hiascend.com/en/document). # Supported tags and respective Dockerfile links +The tag of each `cann` docker image is consist of the complete software stack version. The details are as follows +| Tag | Currently | Architectures | +|----------|-------------|------------------| +|[cann7.0.RC1.alpha002-oe2203sp2](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/7.0.RC1.alpha002/22.03-lts-sp2/Dockerfile)| CANN 7.0.RC1.alpha002 on openEuler 22.03-LTS-SP2 | arm64 | +|[cann8.0.RC1-oe2203sp4](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/8.0.RC1/22.03-lts-sp4/Dockerfile)| CANN 8.0.RC1 with Python 3.8 on openEuler 22.03-LTS-SP4 | arm64,amd64 | + +# Usage +In this usage, users can select the corresponding `{Tag}` and `container startup options` based on their requirements. + +- Pull the `openeuler/cann` image from docker + + ```bash + docker pull openeuler/cann:{Tag} + ``` + +- Start a cann instance + + ```bash + docker run \ + --name my-cann \ + --device /dev/davinci1 \ + --device /dev/davinci_manager \ + --device /dev/devmm_svm \ + --device /dev/hisi_hdc \ + -v /usr/local/dcmi:/usr/local/dcmi \ + -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ + -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \ + -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \ + -v /etc/ascend_install.info:/etc/ascend_install.info \ + -it openeuler/cann:{Tag} bash + ``` + +- Container startup options + + | Option | Description | + |--|--| + | `--name my-cann` | Names the container `my-cann`. | + | `--device /dev/davinci1` | Mounts the specific hardware device `/dev/davinci1` into the container. | + | `--device /dev/davinci_manager` | Mounts the specific hardware device `/dev/davinci_manager` into the container. | + | `--device /dev/devmm_svm` | Mounts the specific hardware device `/dev/devmm_svm` into the container. | + | `--device /dev/hisi_hdc` | Mounts the specific hardware device `/dev/hisi_hdc` into the container. | + | `-v /usr/local/dcmi:/usr/local/dcmi` | Mounts the directory `/usr/local/dcmi` from the host to the container at the same path. | + | `-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi` | Mounts the directory `/usr/local/bin/npu-smi` from the host to the container at the same path. | + | `-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/` | Mounts the directory `/usr/local/Ascend/driver/lib64/` from the host to the container at the same path. | + | `-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info` | Mounts the directory `/usr/local/Ascend/driver/version.info` from the host to the container at the same path. | + | `-v /etc/ascend_install.info:/etc/ascend_install.info` | Mounts the directory `/etc/ascend_install.info` from the host to the container at the same path. | + | `-it` | Starts the container in interactive mode with a terminal (bash). | + | `openeuler/cann:{Tag}` | Specifies the Docker image to run, replace {Tag} with the specific version or tag of the openeuler/cann image you want to use. | + +- View container running logs + + ```bash + docker logs -f my-cann + ``` + +- To get an interactive shell -- cann7.0.RC1.alpha002-oe2203sp2 + ```bash + docker exec -it my-cann /bin/bash + ``` -## Operating System -Linux/Unix, ARM64 architecture. +# Question and answering +If you have any questions or want to use some special features, please submit an issue or a pull request on [openeuler-docker-images](https://gitee.com/openeuler/openeuler-docker-images). \ No newline at end of file diff --git a/cann/doc/image-info.yml b/cann/doc/image-info.yml new file mode 100644 index 00000000..7d580e3a --- /dev/null +++ b/cann/doc/image-info.yml @@ -0,0 +1,70 @@ +name: cann +category: ai +description: CANN(Compute Architecture for Neural Networks)是华为针对AI场景推出的异构计算架构,对上支持多种AI框架,对下服务AI处理器与编程,发挥承上启下的关键作用,是提升昇腾AI处理器计算效率的关键平台。 +environment: | + 本应用在Docker环境中运行,安装Docker执行如下命令 + ``` + yum install -y docker + ``` +tags: | + CANN镜像的Tag由其版本信息和基础镜像版本信息组成,详细内容如下 + + | Tag | Currently | Architectures | + |----------|-------------|------------------| + |[cann7.0.RC1.alpha002-oe2203sp2](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/7.0.RC1.alpha002/22.03-lts-sp2/Dockerfile)| CANN 7.0.RC1.alpha002 on openEuler 22.03-LTS-SP2 | arm64 | + |[8.0.RC1-oe2203sp4](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/8.0.RC1/22.03-lts-sp4/Dockerfile)| CANN 8.0.RC1 with Python 3.8 on openEuler 22.03-LTS-SP4 | arm64,amd64 | + +download: | + 拉取镜像到本地 + ``` + docker pull openeuler/cann:{Tag} + ``` + +usage: | + - 启动容器 + ``` + docker run \ + --name my-cann \ + --device ${device} \ + --device /dev/davinci_manager \ + --device /dev/devmm_svm \ + --device /dev/hisi_hdc \ + -v /usr/local/dcmi:/usr/local/dcmi \ + -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ + -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \ + -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \ + -v /etc/ascend_install.info:/etc/ascend_install.info \ + -it openeuler/cann:{Tag} bash + ``` + 用户可根据自身需求选择对应版本的{Tag}以及对应的硬件设备{device}、容器启动的选项。 + + - 容器测试 + + 查看运行日志 + ``` + docker logs -f my-cann + ``` + + 使用shell交互 + ``` + docker exec -it my-cann /bin/bash + ``` + +license: Apache-2.0 license +similar_packages: + - CUDA: CUDA是英伟达公司设计研发一种并行计算平台和编程模型,包含了CUDA指令集架构以及GPU内部的并行计算引擎。 +dependency: + - attrs + - decorator + - sympy + - numpy + - cffi + - pathlib2 + - pyyaml + - psutil + - protobuf + - scipy + - requests + - absl-py + - wheel + - typing_extensions \ No newline at end of file diff --git a/cann/doc/picture/logo.png b/cann/doc/picture/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b327fdac78fb03bda6e5d98e731dfdd92aa38c GIT binary patch literal 34070 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 bKCu5E6X*Uulo#n$00000NkvXXu0mjf5es*8 literal 0 HcmV?d00001 diff --git a/cann/meta.yml b/cann/meta.yml index afa77831..827915e8 100644 --- a/cann/meta.yml +++ b/cann/meta.yml @@ -1,2 +1,4 @@ cann7.0.RC1.alpha002-oe2203sp2: path: cann/7.0.RC1.alpha002/22.03-lts-sp2/Dockerfile +8.0.RC1-oe2203sp4: + path: cann/8.0.RC1/22.03-lts-sp4/Dockerfile -- Gitee From a456fc2400511f011b60ec81043daff0870edb92 Mon Sep 17 00:00:00 2001 From: GuangJie1 Date: Fri, 23 Aug 2024 10:12:18 +0800 Subject: [PATCH 2/3] Code Review: modify code review suggestions --- cann/8.0.RC1/22.03-lts-sp4/Dockerfile | 18 +++++++++--------- cann/README.md | 22 +++++++++++----------- cann/doc/image-info.yml | 18 +++++++++++++++++- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/cann/8.0.RC1/22.03-lts-sp4/Dockerfile b/cann/8.0.RC1/22.03-lts-sp4/Dockerfile index 3220066c..3d1d6377 100644 --- a/cann/8.0.RC1/22.03-lts-sp4/Dockerfile +++ b/cann/8.0.RC1/22.03-lts-sp4/Dockerfile @@ -1,12 +1,12 @@ # Arguments -ARG BASE_VERSION=22.03-lts-sp4 -ARG PLATFORM=${TARGETPLATFORM} +ARG TARGETPLATFORM ARG PY_VERSION=3.8 ARG CANN_CHIP=910b -ARG CANN_VERSION=8.0.RC1 +ARG VERSION=8.0.RC1 +ARG BASE=openeuler/openeuler:22.03-lts-sp4 # Phase 1: Install Python -FROM openeuler/openeuler:${BASE_VERSION} as py-installer +FROM ${BASE} as py-installer # Arguments ARG PY_VERSION @@ -54,9 +54,9 @@ RUN bash /tmp/python.sh --install && \ FROM py-installer as cann-installer # Arguments -ARG PLATFORM +ARG TARGETPLATFORM ARG CANN_CHIP -ARG CANN_VERSION +ARG VERSION # Install dependencies RUN yum update -y && \ @@ -91,13 +91,13 @@ RUN bash /tmp/cann.sh --install && \ rm /tmp/cann.sh # Phase 3: Copy results from previous phases -FROM openeuler/openeuler:${BASE_VERSION} as official +FROM ${BASE} as official # Arguments -ARG PLATFORM +ARG TARGETPLATFORM ARG PY_VERSION ARG CANN_CHIP -ARG CANN_VERSION +ARG VERSION # Environment variables ENV PATH=/usr/local/python${PY_VERSION}/bin:${PATH} diff --git a/cann/README.md b/cann/README.md index e8b6ce53..e0609029 100644 --- a/cann/README.md +++ b/cann/README.md @@ -18,7 +18,7 @@ The tag of each `cann` docker image is consist of the complete software stack ve | Tag | Currently | Architectures | |----------|-------------|------------------| |[cann7.0.RC1.alpha002-oe2203sp2](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/7.0.RC1.alpha002/22.03-lts-sp2/Dockerfile)| CANN 7.0.RC1.alpha002 on openEuler 22.03-LTS-SP2 | arm64 | -|[cann8.0.RC1-oe2203sp4](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/8.0.RC1/22.03-lts-sp4/Dockerfile)| CANN 8.0.RC1 with Python 3.8 on openEuler 22.03-LTS-SP4 | arm64,amd64 | +|[8.0.RC1-oe2203sp4](https://gitee.com/openeuler/openeuler-docker-images/blob/master/cann/8.0.RC1/22.03-lts-sp4/Dockerfile)| CANN 8.0.RC1 with Python 3.8 on openEuler 22.03-LTS-SP4 | arm64,amd64 | # Usage In this usage, users can select the corresponding `{Tag}` and `container startup options` based on their requirements. @@ -51,17 +51,17 @@ In this usage, users can select the corresponding `{Tag}` and `container startup | Option | Description | |--|--| | `--name my-cann` | Names the container `my-cann`. | - | `--device /dev/davinci1` | Mounts the specific hardware device `/dev/davinci1` into the container. | - | `--device /dev/davinci_manager` | Mounts the specific hardware device `/dev/davinci_manager` into the container. | - | `--device /dev/devmm_svm` | Mounts the specific hardware device `/dev/devmm_svm` into the container. | - | `--device /dev/hisi_hdc` | Mounts the specific hardware device `/dev/hisi_hdc` into the container. | - | `-v /usr/local/dcmi:/usr/local/dcmi` | Mounts the directory `/usr/local/dcmi` from the host to the container at the same path. | - | `-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi` | Mounts the directory `/usr/local/bin/npu-smi` from the host to the container at the same path. | - | `-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/` | Mounts the directory `/usr/local/Ascend/driver/lib64/` from the host to the container at the same path. | - | `-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info` | Mounts the directory `/usr/local/Ascend/driver/version.info` from the host to the container at the same path. | - | `-v /etc/ascend_install.info:/etc/ascend_install.info` | Mounts the directory `/etc/ascend_install.info` from the host to the container at the same path. | + | `--device /dev/davinciX` | NPU device, where `X` is the physical ID number of the chip, e.g., davinci1. | + | `--device /dev/davinci_manager` | Davinci-related management device. | + | `--device /dev/devmm_svm` | Memory management-related device. | + | `--device /dev/hisi_hdc` | HDC-related management device. | + | `-v /usr/local/dcmi:/usr/local/dcmi` | Mounts the host's DCMI .so and interface file directory /usr/local/dcmi to the container. Please modify according to actual situation. | + | `-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi` | Mount the host npu-smi tool "/usr/local/bin/npu-smi" into the container. Please modify it according to the actual situation. | + | `-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/` | Mounts the host directory /usr/local/Ascend/driver/lib64/driver to the container. Please modify according to the path where the driver's .so files are located. | + | `-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info` | Mounts the host's version information file /usr/local/Ascend/driver/version.info to the container. Please modify according to actual situation. | + | `-v /etc/ascend_install.info:/etc/ascend_install.info` | Mounts the host's installation information file /etc/ascend_install.info to the container. | | `-it` | Starts the container in interactive mode with a terminal (bash). | - | `openeuler/cann:{Tag}` | Specifies the Docker image to run, replace {Tag} with the specific version or tag of the openeuler/cann image you want to use. | + | `openeuler/cann:{Tag}` | Specifies the Docker image to run, replace `{Tag}` with the specific version or tag of the `openeuler/cann` image you want to use. | - View container running logs diff --git a/cann/doc/image-info.yml b/cann/doc/image-info.yml index 7d580e3a..92858d7b 100644 --- a/cann/doc/image-info.yml +++ b/cann/doc/image-info.yml @@ -36,7 +36,23 @@ usage: | -v /etc/ascend_install.info:/etc/ascend_install.info \ -it openeuler/cann:{Tag} bash ``` - 用户可根据自身需求选择对应版本的{Tag}以及对应的硬件设备{device}、容器启动的选项。 + 用户可根据自身需求选择对应版本的{Tag}、对应的硬件设备{device}以及容器启动的参数配置。 + + - 参数说明 + | 配置项 | 描述 | + |--|--| + | `--name my-cann` | 容器名称。| + | `--device /dev/davinci1` | NPU设备,X是芯片物理ID号,例如davinci0。 | + | `--device /dev/davinci_manager` | davinci相关的管理设备。 | + | `--device /dev/devmm_svm` | 内存管理相关设备。 | + | `--device /dev/hisi_hdc` | hdc相关管理设备。 | + | `-v /usr/local/dcmi:/usr/local/dcmi` | 将宿主机dcmi的.so和接口文件目录`/usr/local/dcmi`挂载到容器中,请根据实际情况修改。 | + | `-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi` | 将宿主机`npu-smi`工具`/usr/local/bin/npu-smi`挂载到容器中,请根据实际情况修改。 | + | `-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/` | 将宿主机目录`/usr/local/Ascend/driver/lib64/driver`挂载到容器中。请根据driver的驱动.so所在路径修改。 | + | `-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info` | 将宿主机版本信息文件`/usr/local/Ascend/driver/version.info`挂载到容器中,请根据实际情况修改。 | + | `-v /etc/ascend_install.info:/etc/ascend_install.info` |将宿主机安装信息文件`/etc/ascend_install.info`挂载到容器中。 | + | `-it` | 以交互模式启动容器。 | + | `openeuler/cann:{Tag}` | 指定要运行的镜像为 `openeuler/cann`,其中` {Tag}` 是需要替换的镜像标签。 | - 容器测试 -- Gitee From 129c3e1c9d1563ecea39bfabf9605003fed068b7 Mon Sep 17 00:00:00 2001 From: GuangJie1 Date: Fri, 23 Aug 2024 10:32:05 +0800 Subject: [PATCH 3/3] Code Review: modify code review suggestions --- cann/doc/image-info.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cann/doc/image-info.yml b/cann/doc/image-info.yml index 92858d7b..b54c9d8f 100644 --- a/cann/doc/image-info.yml +++ b/cann/doc/image-info.yml @@ -36,13 +36,13 @@ usage: | -v /etc/ascend_install.info:/etc/ascend_install.info \ -it openeuler/cann:{Tag} bash ``` - 用户可根据自身需求选择对应版本的{Tag}、对应的硬件设备{device}以及容器启动的参数配置。 + 用户可根据自身需求选择对应版本的`{Tag}`、对应的NPU设备`{device}`以及容器启动的其他参数配置。 - 参数说明 | 配置项 | 描述 | |--|--| | `--name my-cann` | 容器名称。| - | `--device /dev/davinci1` | NPU设备,X是芯片物理ID号,例如davinci0。 | + | `--device /dev/davinci1` | NPU设备,X是芯片物理ID号,例如davinci1。 | | `--device /dev/davinci_manager` | davinci相关的管理设备。 | | `--device /dev/devmm_svm` | 内存管理相关设备。 | | `--device /dev/hisi_hdc` | hdc相关管理设备。 | -- Gitee