2025/11/09 Updated by

Docker Image を自作する (6b)

PyTorch23.07 + sshd + jupyter

つくりながら学ぶ!LLM自作入門」が動作する Docker (GPU) 環境


[Up] Japanese English
このページ内での表記:
「ホストOSの対話環境」は背景色を黄色(lightyellow)で表す。
「Conainer 内の root 権限の対話環境」は背景色を水色(azure)であらわす。
「Conainer 内の一般ユーザ権限の対話環境」は背景色を赤色(#ffeeee)であらわす。
「他のPCの対話環境」は紫色(#eeeeff)で表す。

目的


前提条件


Python, PyTorch, TensorFlow のバージョンについての検討事項

追加の検討 (BONUSコードも含めてすべてのコードの実行に必要なパッケージを調べる)

すべての requirements*.txt を探す
$ cd LLMs-from-scratch
$ find . -name requirements\*.txt -print
./ch02/02_bonus_bytepair-encoder/requirements-extra.txt
./ch04/02_performance-analysis/requirements-extra.txt
./ch05/06_user_interface/requirements-extra.txt
./ch05/07_gpt_to_llama/requirements-extra.txt
./ch05/11_qwen3/qwen3-chat-interface/requirements-extra.txt
./ch06/03_bonus_imdb-classification/requirements-extra.txt
./ch06/04_user_interface/requirements-extra.txt
./ch07/02_dataset-utilities/requirements-extra.txt
./ch07/03_model-evaluation/requirements-extra.txt
./ch07/05_dataset-generation/requirements-extra.txt
./ch07/06_user_interface/requirements-extra.txt
./requirements.txt
すべての requirements*.txt をまとめる
find LLMs-from-scratch -name requirements\*.txt -exec printf "# {}\n" \; -exec cat {} \; -exec printf "\n\n" \; > requirements_all.txt
必要なパッケージをすべて含んだ requirements_all.txt
# LLMs-from-scratch/requirements.txt
torch>=2.2.2,<2.6; sys_platform == 'darwin' and platform_machine == 'x86_64'  # Intel macOS
torch >= 2.2.2; sys_platform != 'darwin' or platform_machine != 'x86_64'   # all chapters
jupyterlab >= 4.0          # all
tiktoken >= 0.5.1          # ch02; ch04; ch05
matplotlib >= 3.7.1        # ch04; ch06; ch07
tensorflow>=2.16.2; sys_platform == 'darwin' and platform_machine == 'x86_64'  # Intel macOS
tensorflow >= 2.18.0; sys_platform != 'darwin' or platform_machine != 'x86_64'   # ch05; ch06; ch07
tqdm >= 4.66.1             # ch05; ch07
numpy >= 1.26             # dependency of several other libraries like torch and pandas
pandas >= 2.2.1            # ch06
psutil >= 5.9.5            # ch07; already installed automatically as dependency of torch


# LLMs-from-scratch/ch02/02_bonus_bytepair-encoder/requirements-extra.txt
requests
tqdm
transformers>=4.33.2


# LLMs-from-scratch/ch04/02_performance-analysis/requirements-extra.txt
thop

# LLMs-from-scratch/ch05/06_user_interface/requirements-extra.txt
chainlit>=1.2.0

# LLMs-from-scratch/ch05/07_gpt_to_llama/requirements-extra.txt
blobfile>=3.0.0
huggingface_hub>=0.24.7
ipywidgets>=8.1.2
safetensors>=0.4.4
sentencepiece>=0.1.99


# LLMs-from-scratch/ch05/11_qwen3/qwen3-chat-interface/requirements-extra.txt
chainlit>=1.2.0
huggingface_hub>=0.34.4
llms_from_scratch>=1.0.18  # to import code from this repo
safetensors>=0.6.2
tokenizers>=0.21.1

# LLMs-from-scratch/ch06/03_bonus_imdb-classification/requirements-extra.txt
transformers>=4.33.2
scikit-learn>=1.3.0

# LLMs-from-scratch/ch06/04_user_interface/requirements-extra.txt
chainlit>=1.2.0

# LLMs-from-scratch/ch07/02_dataset-utilities/requirements-extra.txt
openai>=1.30.3
scikit-learn>=1.3.1
tqdm>=4.65.0

# LLMs-from-scratch/ch07/03_model-evaluation/requirements-extra.txt
openai>=1.30.3
tqdm>=4.65.0


# LLMs-from-scratch/ch07/05_dataset-generation/requirements-extra.txt
openai>=1.30.3
tqdm>=4.65.0


# LLMs-from-scratch/ch07/06_user_interface/requirements-extra.txt
chainlit>=1.2.0

nVidia GPU (CUDA) に対応した Docker Image を探す: PyTorch


(参考) nVidia GPU (CUDA) に対応した Docker Image を探す: TensorFlow


「つくって学ぶ!LLM自作入門」が動作する Docker Image (GPU 版) を作成する

方針

作成手順

  1. Windows11 上の Ubuntu-20.04 (WSL) を使って作業を進める。
  2. Windows において WSL のファイルシステムは \\wsl$\Ubuntu-20.04\home\nitta にあることに注意すること。 Ubuntu-20.04 から、Windows11 のホームディレクトリは /mnt/c/User/nitta/ として見える。
  3. 作業用フォルダを作成する
  4.   $ cd /mnt/c/Users/nitta/Documents/docker 
      $ mkdir -p llm_diy_torch2307b  
    
  5. 作業用フォルダの中に Dockerfile を作成する。
  6. Dockerfile
    # ゲストOS: Ubuntu 22.04 LTS
    
    FROM nvcr.io/nvidia/pytorch:23.07-py3
    
    # Change Your Own UNAME, UID, GID, PASS
    
    ENV UNAME=guest
    ENV UID=3000
    ENV GID=3000
    ENV PASS=password
    
    ENV SSHD_PORT=22
    
    # 必要なパッケージのインストール
    
    RUN apt-get update && \
        DEBIAN_FRONTEND=noninteractive apt-get install -y \
        sudo \
        bash \
        openssh-server \
        supervisor \
        && rm -rf /var/lib/apt/lists/*
    
    
    # SSH 設定: パスワード認証を有効化
    
    RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
        sed -i "s/^#Port.*/Port ${SSHD_PORT}/" /etc/ssh/sshd_config && \
        mkdir /var/run/sshd
    
    
    # supervisord の設定ファイルを設置する (Daemon 起動用)
    
    RUN mkdir -p /var/log/supervisor
    COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
    
    
    ##### "Build a LLM from scratch"
    
    WORKDIR /app
    
    # RUN git clone https://github.com/rasbt/LLMs-from-scratch.git
    COPY requirements_all.txt /app/
    
    WORKDIR /app
    RUN pip install --no-cache -r requirements_all.txt
    
    #####
    
    # ポート開放
    
    EXPOSE 22
    
    
    # Copy Shell Script "entrypoint.sh"
    
    COPY entrypoint.sh /entrypoint.sh
    RUN chmod +x /entrypoint.sh
    
    
    ######
    
    ENTRYPOINT ["/entrypoint.sh"]
    
    CMD []
    
  7. 作業用フォルダの中に entrypoint.sh を作成する
  8. entrypoint.sh
    #!/bin/bash
    set -e
    
    if [ ! -f /var/app/.initialized ]; then
        ######## First Time ########
    
        echo "First run. Setting up ..."
        mkdir -p /var/app
        touch /var/app/.initialized
    
        # ユーザーが存在しない場合のみ作成する
        if id "${UNAME}" &>/dev/null; then
            echo "User ${UNAME} already exists. Skipping creation."
        else
            # 同名グループが無ければ作成する
            if ! getent group "${UNAME}" &>/dev/null; then
                echo "Creating group ${UNAME} with GID=${GID}"
                groupadd -g ${GID} ${UNAME}
            else
                echo "Group ${UNAME} already exists. Skipping group creation."
            fi
        
            echo "Creating user ${UNAME} with UID=${UID}, GID=${GID}"
            useradd -m -u ${UID} -g ${GID} -s /bin/bash ${UNAME}
            echo "${UNAME}:${PASS}" | chpasswd
            usermod -aG sudo ${UNAME}
        fi
    
        # ホームディレクトリの Owner が root:root になることがあるので明示的に変更する。
        chown -v ${UNAME}:${UNAME}  /home/${UNAME}
    
        # SSHD のポート番号を変更する
        sed -i "s/^Port.*/Port ${SSHD_PORT}/" /etc/ssh/sshd_config
    
        
    else
        ######## Second Time or Later ########
        
        echo "Starting for the second time or later ..."
    
    fi
    
    # supervisord start (background)
    /usr/bin/supervisord -c /etc/supervisor/supervisord.conf &
    
    # Execute Commands in CMD
    
    if [ "$#" -gt 0 ]; then
        exec "$@"
    else
        echo "No command provided. Starting bash ..."
        exec bash
    fi
    
  9. 作業用フォルダの中に supervisord.conf を作成する
  10. supervisord.conf
    # supervisord の設定ファイル
    
    [supervisord]
    nodaemon=true
    
    
    # sshd を起動する
    
    [program:sshd]
    command=/usr/sbin/sshd -D -e
    autostart=true
    autorestart=true
    stdout_logfile=/var/log/sshd_stdout.log
    stderr_logfile=/var/log/sshd_stderr.log
    
  11. 上記で作成したファイルを以下のように配置する。llm_diy_torch2307b.zip
  12.   llm_diy_torch2307b/
        |
        +-- Dockerfile
        |
        +-- entrypoint.sh
        |
        +-- supervisord.conf
        |
        +-- requirements_all.txt
    
  13. Image を build する。
  14.   $ docker build -t llm_diy_torch2307b .  
      ...
    成功
    
  15. 生成した Image を確認する
  16. $ docker image ls  
    REPOSITORY               TAG         IMAGE ID       CREATED          SIZE
    llm_diy_torch2307b       latest      b2b8acda9af2   52 seconds ago   29.4GB
    llm_diy_torch2307        latest      ae6c6c06ccf1   5 days ago       29GB
    ...
    

Container 用の永続的なファイルシステムを作成する

コンテナに永続的なファイルシステムを提供するために、1777 のパーミッションでフォルダを作っておく。 skicky bit が on (1777) のフォルダには、 「誰でもファイルを作成できるが、作成した本人だけがファイルを変更したり消したりできる」 という特徴がある。

$ sudo mkdir -p /home/docker            ← ディレクトリを作成する
$ sudo chmod 1777 /home/docker          ← 誰でもファイルを作成できるが、作成した本人にしか消去できないモードに設定する
$ ls -ld /home/docker                   ← ディレクトリのsticky bit が on になっていることを確認する。
drwxrwxrwt 3 root root 4096  4月 26 15:47 /home/docker

Docker Container を生成する

Image llm_diy_torch2307b のデフォルトのユーザ情報とSSHサーバ情報を用いて、 新しい Container llmdiy_01b を生成する。

  1. Image から Container を生成して起動する。ユーザ情報はデフォルト値 (guest) を利用する。 Container のファイルシステム内にホストOSのディレクトリをマウントする。 Container を起動するたびに、sshd サーバが起動される。
  2. $ docker run --name llmdiy_01b \
        --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 --gpus all \
        --restart always \
        -p 7077:22 -p 8087:8888 \
        -v /home/docker/llmdiy_01b:/mnt/hostos \
        -it llm_diy_torch2307b  
    
    起動オプション
  3. Container を起動した対話環境が、そのまま Container 内で動作する bash との対話環境になる。root権限でloginした状態である。
  4. First run. Setting up ...       ← 生成された Container 内で entrypoint.sh が実行される
    Creating group guest with GID=3000
    Creating user guest with UID=3000, GID=3000
    ownership of '/home/guest' retained as guest:guest
    No command provided. Starting bash ...
    2025-11-09 00:08:09,901 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
    2025-11-09 00:08:09,901 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
    2025-11-09 00:08:09,905 INFO RPC interface 'supervisor' initialized
    2025-11-09 00:08:09,905 CRIT Server 'unix_http_server' running without any HTTP authentication checking
    2025-11-09 00:08:09,905 INFO supervisord started with pid 44
    root@37c75c55ceea:/app# 2025-11-09 00:08:10,910 INFO spawned: 'sshd' with pid 332
    2025-11-09 00:08:11,943 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    
    root@37c75c55ceea:/app#              ← Container 内の対話環境 (root権限の bash) が動く
    
  5. (重要) (Container 内で) 直ちに新規ユーザ guest のパスワードを変更する。
  6. # passwd guest   
    New password:                   ← 新しいパスワード を入力する。
    Retype new password:            ← もう一度新しいパスワード を入力する。
    passwd: password updated successfully
    
  7. (Container 内で) ホストOSのマウントポイントを調べる。
  8. # ls -ld /mnt/hostos    
    drwxr-xr-x 2 root root 4096 May 11 09:32 /mnt/hostos
    
  9. (Container 内で) 新規ユーザのホームディレクトリを調べる。
  10. # ls -ld /home/guest    
    drwxr-x--- 2 guest guest 4096 May 11 09:32 /home/guest
    # ls -la /home/guest    
    total 20
    drwxr-x--- 2 guest guest 4096 May 11 09:32 .
    drwxr-xr-x 1 root  root  4096 May 11 09:32 ..
    -rw-r--r-- 1 guest guest  220 Mar 31  2024 .bash_logout
    -rw-r--r-- 1 guest guest 3771 Mar 31  2024 .bashrc
    -rw-r--r-- 1 guest guest  807 Mar 31  2024 .profile
    
  11. (Container 内で) Control-P と Control-Q を順にタイプして、ホストOSの対話環境に戻る。 Container 内のシェルは動作したままとなる。
  12. # ^p ^q                ← Container の対話環境を抜ける
    $              ← ホストOS 内の対話環境に戻る
    
  13. (ホストOS上) Container からマウントされているホストOSのディレクトリを調べる。
  14. $ ls -ld /home/docker/llmdiy_01b  
    drwxr-xr-x 2 root root 4096 Nov  3 09:00 /home/docker/llmdiy_01b
    
  15. (ホストOS上) docker 上の実行中の container の状態を調べる
  16. $ docker container ls  
    CONTAINER ID   IMAGE               COMMAND            CREATED        STATUS        PORTS                   NAMES
    37c75c55ceea   llm_diy_torch2307b   "/entrypoint.sh"   10 hours ago   Up 10 hours   0.0.0.0:7077->22/tcp,   llmdiy_01b
                                                                                       [::]:7077->22/tcp, 
                                                                                       0.0.0.0:8087->8888/tcp,
                                                                                       [::]:8087->8888/tcp,
                                                                                       [::]:8087->8888/tcp
    ...
    
  17. (ホストOS上) docker 上のすべての(停止中を含む) container の状態を調べる
  18. $ docker container ls -a  
    CONTAINER ID   IMAGE               COMMAND            CREATED        STATUS        PORTS                   NAMES
    37c75c55ceea   llm_diy_torch2307b   "/entrypoint.sh"   10 hours ago   Up 10 hours   0.0.0.0:7077->22/tcp,   llmdiy_01b
                                                                                       [::]:7077->22/tcp, 
                                                                                       0.0.0.0:8087->8888/tcp,
                                                                                       [::]:8087->8888/tcp,
                                                                                       [::]:8087->8888/tcp
    ...
    
  19. (ホストOS上) docker 上の Image の一覧を表示する。
  20. $ docker image ls  
    REPOSITORY               TAG         IMAGE ID       CREATED          SIZE
    llm_diy_torch2307b       latest      b2b8acda9af2   18 minutes ago   29.4GB
    llm_diy_torch2307        latest      ae6c6c06ccf1   5 days ago       29GB
    torch2302_sshd           latest      642c2e1fae87   3 weeks ago      20.6GB
    nvcr.io/nvidia/pytorch   23.07-py3   bd784c42fdf0   2 years ago      19.8GB
    nvcr.io/nvidia/pytorch   23.02-py3   7c3375e220ea   2 years ago      20.5GB
    

Docker Host から ssh を用いてContainer 内の対話環境にアクセスする

  1. ホストOSから、Continer の guest ユーザのアカウントに ssh でアクセスする。
  2. $ ssh -p 7077 guest@localhost ← ホストOSの 7077 番ポートに sshアクセスする The authenticity of host '[localhost]:7077 ([127.0.0.1]:7077)' can't be established. ECDSA key fingerprint is SHA256:l7tsXhvQOaz9nn7Aa2JuAyjHN5QTVrxlywRxklHDixA. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[localhost]:7077' (ECDSA) to the list of known hosts guest@localhost's password: パスワードを入力する。エコーバックされない。
    Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-25-generic x86_64). ... $ ← Container 内の guest 権限の対話環境が開始する
  3. (Container 内の guest 権限で) ユーザ名を表示する。
  4. $ whoami   
    guest
    
  5. (Container 内の guest 権限で) ホームディレクトリを表示する。
  6. $ pwd   
    /home/guest
    
  7. (Container 内の guest 権限で) ホームディレクトリにあるファイルの一覧を表示する。
  8. $ ls -la   
    total 24
    drwxr-x--- 3 guest guest 4096 Nov  3 01:08 .
    drwxr-xr-x 1 root  root  4096 Nov  3 01:04 ..
    -rw-r--r-- 1 guest guest  220 Jan  6  2022 .bash_logout
    -rw-r--r-- 1 guest guest 3771 Jan  6  2022 .bashrc
    drwx------ 2 guest guest 4096 Nov  3 01:07 .cache
    -rw-r--r-- 1 guest guest  807 Jan  6  2022 .profile
    -rw-r--r-- 1 guest guest    0 Nov  3 01:08 .sudo_as_admin_successful
    
  9. (Container 内の guest 権限で) ssh 経由の対話環境を終了する。
  10. guest@af401d3cdf85:~$ exit logout
    Connection to localhost closed. $

他のPCからネットワーク経由で Container 内の対話環境にアクセスする

  1. (ホストOS上) 外部のPCからネットワーク経由で Container にアクセスするためには、ホストOSの 7077 番ポートを開けておく必要がある。
  2. Docker の公式文書では、「docker のポートフォワーディングは ufw のフィルタリングよりも前に行わるために、ufw の影響を受けない」 と記述されている (2025年春時点) が、これは現時点では間違いのようだ。 Containerに外部からアクセスするためには、ホストOSのポートを開けておく必要がある。

    Windows 11 の場合:

    「設定」→「ネットワークとインターネット」→「ネットワークの詳細設定」→「Windows ファイアウォール」→ 詳細設定
    1. ssh 用のポートを開ける。
      ローカルコンピューターのセキュリティ
      → 受信の規則
      → 新しい規則
      → 規則の種類「ポート」 → 次へ
      → プロトコルおよびポート → TCP → 特定のローカルポート → 7070-7079 → 次へ
      → 操作「接続を許可する」→ 次へ
      → プロファイル: ドメイン、プライベート、パブリックのすべてをチェックする
      → 名前: docker ssh → 完了
    2. WWWサーバ (jupyter) 用のポートを開ける
    3. 上と同様で → ... →
      → プロトコルおよびポート → TCP → 特定のローカルポート → 8080-8089 → 次へ → ...
      → 名前: docker http → 完了

    Ubuntu 24.04LTS の場合:

    ufw を用いる
    1. (ホストOS上) ファイアウォール ufw を有効化する。(既に有効化してあれば必要なし)
    2.   $ sudo apt update                          ← aptのデータベースを更新する
        $ sudo apt install -y ufw                  ← ufw をインストールする。
        $ sudo systemctl enable ufw                  ← ufw を有効化する
        $ sudo systemctl restart ufw                  ← ufw を再起動する
        
    3. (ホストOS上) ホストOSの 7077 番ポートを開放する。
    4.   $ sudo ufw allow 7077                          ← 7077 番ポートを開放する
        ルールを追加しました
        ルールを追加しました (v6)
        
    5. (ホストOS上) ファイアウォールの状態を確認する。
    6.   $ sudo ufw status 7077                          ← 7077 番ポートを開放する
        ...
      状態: アクティブ
      
      To                         Action      From
      --                         ------      ----
      ...
      7077                      ALLOW       Anywhere
      ...
      7077 (v6)                 ALLOW       Anywhere (v6)
        ...
      
  3. (ネットワーク上の他のPC) 他のマシンから、ホストOS上の Container に ssh 接続する。
  4. 以下は、ホストOSの IPアドレスが 192.168.12.10 の場合の、ssh アクセスの様子である。

    (他のPCから) $ ssh -p 7077 guest@192.168.12.10 guest@192.168.12.10's password: ← パスワードを入力する。エコーバックされない。
    Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-25-generic x86_64) ... guest $ whoami ← Container に guest 権限でアクセスできる guest guest $ exit logout
    Connection to 192.168.12.10 closed. $

Container が再起動すると sshd も自動起動されることを確認する

  1. (ホストOS上で) Container を停止する
  2. $ docker container stop llmdiy_01b   
    
  3. (ホストOS上で) Container が停止されたことを確認する
  4. $ docker container ls   
    CONTAINER ID   IMAGE           COMMAND            CREATED         STATUS         PORTS          NAMES
    
  5. (ホストOS上で) 停止中の Container を起動する
  6. $ docker start llmdiy_01b   
    
  7. (ホストOS上で) Container に ssh でアクセスして、sshd が自動的に起動していることを確認する。
  8. $ ssh -p 7077 guest@localhost ← ホストOSの 7077 番ポートに sshアクセスする guest@localhost's password: パスワードを入力する。エコーバックされない。
    Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-25-generic x86_64) ... $ ← Container 内の guest 権限の対話環境が開始する

Docker ホストOSのファイルシステム上に、Container の guest ユーザの作業領域を確保する

公式のソースコードを実行すると、モデルのパラメータの保存にかなり多くのファイルシステム領域を消費してしまう。 そこで、Docker ホストOS上の永続的なファイルシステムを Container からマウントして、その上で作業する。
  1. Container に ssh でアクセスする。
  2. $ ssh -p 7077 guest@localhost ← ホストOSの 7077 番ポートに sshアクセスする guest@localhost's password: パスワードを入力する。エコーバックされない。
    Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-25-generic x86_64) ... $ ← Container 内の guest 権限の対話環境が開始する
  3. root 権限で、/mnt/hostos に guest ユーザの作業領域を作成する。
  4. $ sudo mkdir /mnt/hostos/guest  
    
  5. root 権限で、/mnt/hostos/guest の所有者を guest ユーザに変更する。
  6. $ sudo chmod guest.guest /mnt/hostos/guest  
    

「つくって学ぶ!LLM自作」入門のソースコードを使って jupyter サーバを動作させる。

  1. guest ユーザとして ssh 接続する。
  2. カレントディレクトリを作業用のフォルダに移動する。
  3. $ cd /mnt/hostos/guest  
    
  4. github からソースコードをダウンロードする。
  5. $ git clone https://github.com/rasbt/LLMs-from-scratch.git  
    
  6. ./LLMs-from-scratch/ フォルダに移動してから、jupyter lab を起動する。
  7. $ cd LLMs-from-scratch  
    $ jupyter lab --allow-root --ip=0.0.0.0 --no-browser --NotebookApp.token='xxx'  
    
  8. 動作中の jupyter notebook の token を知るには、同じマシンの別のシェルで以下のコマンドを実行する。
  9. $ jupyter lab list  
    

外部のマシンからブラウザで jupyter サーバに接続する。

  1. ローカルネット内のリモートマシンからアクセスする。
  2. http://192.168.12.10:8087/lab?token=xxx
    
  3. インターネット上のリモートマシンからアクセスする場合の URL:
  4. (ルータで 18087 番ポートを 192.168.12.10:8087 にポートフォワーディングしている場合)
    http://ynitta.net:18087/lab?token=xxx