話題のDeepSeekR1をJetson AGX ORINで実行してみた docker編

# 話題のDeepSeekR1をJetson AGX ORINで実行してみた docker編

前回からの続き
https://qiita.com/g667408/items/d76077ab987a2128f076

[__docker版でもcudaが（一応）動くようになりました__](https://qiita.com/g667408/items/2c09950c481a138bde59#cuda%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B)

__以下の方法ではCUDAが無効になります。動きますがかなり遅いです。__

前回は、llama.cppを自前でコンパイルして使いました。
dockerの方が使い勝手が良いので、今回はdockerイメージの作成と実行を行いたいと思います。
同じモデルを実行してつまらないので、サイバーエージェントがDeepSeek R1を元に作ったモデルも実行してみることにします。
https://gigazine.net/news/20250128-cyberagent-deepseek-r1-japanese/

# 環境
Jetson AGX orin 32GBです。当然、同じです。
ただし今回はdockerを使います。
現時点で最新のと思われる
nvidia/cuda:12.6.3-devel-ubuntu22.04
を使いました。

# 手順

予想外に手間取りました。

## Dockerfile

```Dockerfile
FROM nvidia/cuda:12.6.3-devel-ubuntu22.04

# install required packages
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    build-essential \
    git \
    cmake \
    ca-certificates \
    curl \
    gpg 

# clone llama.cpp
RUN git clone https://github.com/ggerganov/llama.cpp.git

# CUDA environment
ENV CUDA_VERSION 12.6
ENV CUDA_HOME /usr/local/cuda-$CUDA_VERSION
ENV PATH $PATH:$CUDA_HOME/bin
ENV LD_LIBRARY_PATH $CUDA_HOME/lib64:$CUDA_HOME/compat:$LD_LIBRARY_PATH

# build directory
WORKDIR /llama.cpp
RUN mkdir build
WORKDIR /llama.cpp/build

# cmake generating Makefile
# path for CUDA
# FP16 for Orin 
RUN cmake -DCMAKE_BUILD_TYPE=Release -DGGML_CPU_AARCH64=1 \
         -DGGML_CUDA=1 \
         -DGGML_CUDA_F16=1 \
         -DGGML_F16C=1 -DGGML_FMA=1 \
         -DCMAKE_CUDA_COMPILER=$CUDA_HOME/bin/nvcc \
         -DCUDAToolkit_ROOT=$CUDA_HOME \
       ..

# build llama.cpp
RUN make

# cd to bin
WORKDIR /llama.cpp/build/bin

# execute llama-server 
CMD ["sh", "-c", "./llama-server -m /models/$model --host 0.0.0.0 --port 8080"]
```

## build方法

```shell
docker build -t llama-jetson-orin .
```
llama.cppのコンパイル２時間以上かかりました。

## 実行

### bashで実行

下記のようなrun.shを作成して実行
~/llm_models/以下にggufファイルを配置しておきます。
MODEL=にモデルのggufファイル名を指定して実行します。
```bash
# !/bin/bash
MODEL=DeepSeek-R1-Distill-Qwen-14B-Q6_K.gguf
docker run -it -e model=$MODEL --rm --network=host --volume ~/llm_models:/models llama-jetson-orin
```

下のような起動ログが出て、urlが出てきたら起動完了です。 http://localhost:8080にアクセスすれば対話可能になります。

```
<｜User｜>Hello<｜Assistant｜>Hi there<｜end?of?sentence｜><｜User｜>How are you?<｜Assistant｜>'
main: server is listening on http://0.0.0.0:8080 - starting the main loop
srv  update_slots: all slots are idle
```

## 実行結果

明日の朝食を考えてもらったら、狂いました。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338102/3ce87cb6-09f6-59c7-b83b-6e29e8715c46.png)

今日の気分は？感情は無いけど、嬉しいそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338102/85029d0a-52f5-20ae-a4e3-987684e14208.png)

## 実行結果その２

サイバーエージェントが「DeepSeek-R1」の派生モデルをベースに日本語で追加学習したモデルを公開
https://huggingface.co/cyberagent/DeepSeek-R1-Distill-Qwen-14B-Japanese
さすが仕事が速い。早速こっちも実行してみましょう。

色々なサイズのモデルがありましたが、
cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-Q8_0.gguf
を使用してみました。

今日の気分は？
なんかDeepSeekより深い答えが返ってきた。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338102/0c9f9ce7-56c4-bc8b-1c36-8e66b65f4e73.png)

明日の朝食。うわ。細かい。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338102/74b1fced-c748-05fc-4e8a-53f8d97f2fa3.png)

返事がだいぶ長いです。ちょっと諄い感じです。好みが分かれそう。

# 感想
やはりというかJetson Orinでは、このllmは少々重いです。
１回の応答が３０秒から１分もかかります。
もっと軽量なモデルも試したいところ。
本格的に動かすには、Orinの次の機種のThorに期待しましょう。
Thorは速度がOrinの８倍らしいので、それなら十分に実用になるでしょう。


# CUDAを有効にする

```bash
$ docker --version
Docker version 27.5.1, build 9f9e405
```
docker bulidのバグらしき動作で苦労しました。
試行錯誤を重ねた結果、以下のようなDockerfileでなんとかCUDAが動くようになりました。
```Dockerfile
FROM nvcr.io/nvidia/l4t-base:r36.2.0 AS install_stage

# Copy CUDA installer files from local
COPY cuda-ubuntu2204.pin . 
COPY cuda-tegra-repo-ubuntu2204-12-6-local_12.6.3-1_arm64.deb .

# Install CUDA Toolkit
RUN mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin && \
    dpkg -i cuda-tegra-repo-ubuntu2204-12-6-local_12.6.3-1_arm64.deb && \
    rm cuda-tegra-repo-ubuntu2204-12-6-local_12.6.3-1_arm64.deb && \
    cp /var/cuda-tegra-repo-ubuntu2204-12-6-local/cuda-*-keyring.gpg /usr/share/keyrings/ && \
    apt-get update && \
    apt-get install -y cuda-toolkit-12-6

# Install build dependencies
RUN apt-get install -y \
    build-essential \
    git \
    cmake \
    ca-certificates \
    pkg-config \
    libblas-dev \
    ccache

# Environment setup
ENV CUDA_HOME=/usr/local/cuda
ENV PATH=/usr/local/cuda/bin:${PATH}
ENV LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu/nvidia:${LD_LIBRARY_PATH}

# Verify CUDA installation and setup symbolic links
RUN ln -sf /usr/local/cuda-12.6 /usr/local/cuda
RUN mkdir -p /usr/local/cuda/targets/aarch64-linux/lib
RUN ln -sf /usr/lib/aarch64-linux-gnu/nvidia/libcuda.so.1 /usr/lib/aarch64-linux-gnu/libcuda.so
RUN ln -sf /usr/lib/aarch64-linux-gnu/nvidia/libcuda.so.1 /usr/local/cuda/targets/aarch64-linux/lib/libcuda.so
RUN echo "CUDA setup verification:"
RUN ls -l /usr/local/cuda*
RUN nvcc --version

FROM install_stage AS clone_stage

# Clone llama.cpp
RUN git clone https://github.com/ggerganov/llama.cpp.git

FROM clone_stage AS prebuild_stage

# Build directory setup
WORKDIR /llama.cpp
RUN mkdir build
WORKDIR /llama.cpp/build
    
    # cmake with modified flags
RUN cmake -DCMAKE_BUILD_TYPE=Release \
        -DGGML_CPU_AARCH64=1 \
        -DGGML_CUDA=1 \
        -DGGML_CUDA_F16=1 \
        -DGGML_F16C=1 \
        -DGGML_FMA=1 \
        -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc \
        -DCUDAToolkit_ROOT=/usr/local/cuda \
        -DGGML_BLAS=1 \
        -DCMAKE_VERBOSE_MAKEFILE=ON \
        -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath-link,/usr/lib/aarch64-linux-gnu/nvidia -Wl,-rpath,/usr/local/cuda/lib64" \
        ..

FROM prebuild_stage AS stage1
# Stage 1: Build object files and core libraries
RUN echo "Stage 1: Building core components..."
RUN make -j$(nproc) build_info
RUN make -j$(nproc) ggml-base
RUN make -j$(nproc) ggml-cpu
RUN make -j$(nproc) ggml-blas
RUN make -j$(nproc) ggml-cuda
RUN make -j$(nproc) ggml
RUN make -j$(nproc) llama
RUN make -j$(nproc) common
RUN echo "Stage 1 completed successfully"

FROM stage1 AS stage2
RUN echo "Stage 2: Building and linking server... " && \
    make  llama-server || true

# generating launch.sh
RUN echo '#!/bin/bash' > ./launch.sh && \
    echo 'make llama-server' >> ./launch.sh && \
    echo 'llama-server -m /models/$model --host 0.0.0.0 --port 8080 --gpu-layers 49' >> ./launch.sh && \
    chmod +x ./launch.sh

ENV PATH=/llama.cpp/build/bin:${PATH}
RUN rm -rf /var/lib/apt/lists/*
CMD ["sh", "-c", "./launch.sh"]
```
何故か、make llama-serverの最後のリンクが失敗してしまいます。
しかし、dockerで/bin/bashを起動してmake llama-serverを起動するとビルドに成功します。
原因不明ですが、しかたないので、最後のリンク直前で失敗しても無視してイメージを作り上げます。
make llama-serverを実行してリンクし、llama-serverを起動するスクリプトlaunch.shを作ります。
docker run時にlaunch.shを実行することで残ったリンクを行い、llama-serverを起動します。
```bash
#!/bin/bash
make llama-server
llama-server -m /models/$model --host 0.0.0.0 --port 8080 --gpu-layers 49
```
一応、これで動きましたが、本来不要なファイルが残ったままになっています。

# cuda-toolkit-12.6.3 入手方法
毎回ダウンロードすると時間がかかりすぎるので、ローカルにコピーします。
https://developer.nvidia.com/cuda-toolkit-archive
CUDA Toolkit 12.6.3を選択
linux, aarch64 jetpack, Native, Ubuntu, 22.04, dep(どっちでも可)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338102/a45dc63e-32ec-1b86-1529-5220d1e8e3f2.png)
選ぶと、CUDA Toolkit Installerが表示される。
２個のwgetを実行するとinstallerを入手できる。
入手したファイルを、Dockerfileと同じディレクトリに配置します。
上のDockerfileの下記部分をファイル名にあわせて書き換えます。
```Dockerfile
# Copy CUDA installer files from local
COPY cuda-ubuntu2204.pin . 
COPY cuda-tegra-repo-ubuntu2204-12-6-local_12.6.3-1_arm64.deb .

# Install CUDA Toolkit
RUN mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin && \
    dpkg -i cuda-tegra-repo-ubuntu2204-12-6-local_12.6.3-1_arm64.deb && \
    rm cuda-tegra-repo-ubuntu2204-12-6-local_12.6.3-1_arm64.deb && \
    cp /var/cuda-tegra-repo-ubuntu2204-12-6-local/cuda-*-keyring.gpg /usr/share/keyrings/ && \
```