Dockerを使ってwsl上でchainerの環境構築

# 背景

この記事はchainerと呼ばれるPython用のDeepLearningフレームワークを使った深層学習プログラムを、WSLを使ってローカル環境で動かすためのものです。
私は非常にこの環境構築で苦戦しました。特に私と同じ研究室の方はchainerを使う機会があるかもしれないので、その手助けとなれば幸いです。

chainerを機能させるために必須のモジュールやドライバーが複数存在しますが、インストールする順番やバージョンを間違えると機能しないため慎重に行う必要があります。

今回はWSLを用いており、Dockerを通してローカル環境でchainerを使う深層学習のための環境を整えることを目標とします。

# 環境

OS:Windows11 home
[WSL:Ubuntu20.04LTS](https://apps.microsoft.com/detail/ubuntu-20-04-6-lts/9MTTCL66CPXJ?hl=ja-jp&gl=JP)
[DockerDesktop for Windows](https://www.docker.com/products/docker-desktop/)
[VcXsrv](https://sourceforge.net/projects/vcxsrv/)　(Windows用のフリーのXサーバー。詳細は後述)

# **chainerに必要な、主なモジュールやドライバー**

- [**nvidia driver**](https://www.nvidia.co.jp/Download/index.aspx?lang=jp)
NVIDIAのGPUを適切に動作させるためのソフトウェアで、GPUに合わせたバージョンをインストールする必要あり。(https://www.nvidia.co.jp/Download/index.aspx?lang=jp)
`nvidia-smi`コマンドでバージョン確認。
- [**CUDA**](https://github.com/NVIDIA/nvidia-docker/wiki/CUDA#requirements)
NVIDIAが開発したGPU向けの並列コンピューティングプラットフォームとAPI。
グラフィックス以外の計算処理をGPUで高速に行うためのツール。
GPU上でのプログラミングをCやC++で行うことができる。
NvidiaDriverに合わせたバージョンをインストールする必要あり。
`nvcc -V`でランタイムのCUDAを確認。`nvidia-smi`でnvidiadriverに対応する最新のCUDA確認。
- **CUDAToolKit**
CUDAのための開発ツールやライブラリ、ヘッダファイル、ドキュメントなどを含むキット。nvccというNVIDIAのCUDAコンパイラ(これがないと現在動かしているランタイムのCUDAのバージョンが確認できない)や、CUDA数学ライブラリやCUDAアプリケーションの実行をサポートするライブラリとAPIであるCUDAランタイムAPIなどが含まれます。
CUDAに合わせたバージョンをインストールする必要あり。
pipにはcudatoolkit がない。https://pypi.org/search/?q=cudatoolkit
- **CUDNN**
NVIDIAが提供するディープニューラルネットワークのためのGPU加速化ライブラリ。
ディープラーニングのフレームワークにおける計算の高速化をサポート。
畳み込み、プーリングなどの基本操作を最適化された形で提供。
CUDAに合わせたバージョンをインストールする必要あり。
※動作しているか直接確認できるコマンド等がない。
インストールに何かしらで失敗したとき、アンインストールしてインストールしなおすのが大変なことがあります。
**ローカルシステムの確認**: インストールされているcuDNNのバージョンは、ヘッダーファイル **`cudnn.h`** を調べることで確認できます。このファイルは通常 **`/usr/local/cuda/include/`** に配置されます。このファイル内で、**`CUDNN_MAJOR`**, **`CUDNN_MINORCUDNN_PATCHLEVEL`** の定義を検索することで、バージョン情報を取得できます。
(私はローカルでCUDNNをインストールしたのにも関わらず、ファイルがこの場所になく、別コマンドで調べても場所が分かりませんでした……)
- [**Cupy**](https://docs.cupy.dev/en/stable/install.html)
GPU上での数値計算を高速化するためのPythonライブラリ。
NumPyと似たAPIを持ち、CUDAをバックエンドとして使用。
ディープラーニングや科学計算でのGPU利用を容易にする。
CUDAに合わせたバージョンのものをインストールする必要あり。(https://docs.cupy.dev/en/stable/install.html)　https://cupy.dev/
- **Numpy**
Pythonでの数値計算をサポートする基本的なライブラリ。
多次元配列や線形代数、数学関数などの機能を提供。
科学計算やデータ分析の基盤として広く利用される。
Pythonに合わせたバージョンをインストールする必要あり。
- また、これ以外にも必要なモジュールが数多くあり(のちに名称をまとめたファイルを添付)
それらの多くがNumpyのバージョンに合わせたものをインストールする必要がある。

chainerは開発が終了しているため、CUDAなどのライブラリを使う際、最新のものをインストールするよりchainer開発終了当時の最新のものを使う方がパフォーマンスが上がります。

https://docs.chainer.org/en/stable/upgrade.html

**推奨バージョン**
chainer 7.8.1
CUDA 11.8.0
cudnn 8.8.0
cupy-cuda11x 12.10
numpy 1.23.0

　GPUに合わせて芋づる式で他のバージョンを合わせていけば環境が完成しそうなものですが、
実際には簡単に環境構築できませんでした。とあるモジュールでは特定のバージョン以下のnumpyが必要で、また別のモジュールは特定のバージョン以上のnumpyでしか機能せずその両方を満たすバージョンが存在しないといったような状況や、すべてのバージョンをそろえたのになぜかCUDA関連の環境が不完全ですという旨のエラーが出る状況がままあるため、複数のモジュール等のバージョンをすりあわせて何とか機能する環境を作るといった工程等が必要になります。

※仮想環境の種類やモジュールのバージョン、GPUやOSの違いなどですんなり上手くいくこともあるにはあるようです。

　本記事ではchainerが動作し損失関数の収束等も確認できる環境を構築はできましたが、環境構築の難易度が高いという問題や、それに付随して新しいモジュールを試すとき等にバージョンの競合を起こすという問題もあり、優れたフレームワークではあるが最終的にはPytorchやTesnsorFlowなどへの移行をする必要があると考えられます。

# 導入

　上記でも少し触れたようにPythonのバージョンも慎重に選ぶ必要があります。しかしシステム全体のPythonのバージョンを変えるのはリスクが大きいため、深層学習では仮想環境がほぼ必須となってきます。仮想環境にはAnacondaを使っている記事も多く見受けられるが本記事ではDockerを使用します(理由は後述)。

## Dockerとは

Dockerは、アプリケーションとその依存関係を包含する「コンテナ」としての実行を可能にする技術です。Dockerの最大の特長は、コンテナ技術を使用することにより、アプリケーションを一貫してどの環境でも同じように動作させることができる点です。

従来の仮想化技術と比較して、DockerはOSレベルでの仮想化を採用しています。これにより、コンテナは高速に起動し、少ないオーバーヘッドで実行できるため、軽量で効率的き機能できます。各コンテナは独立した環境を持ち、他のコンテナやホストシステムから隔離されています。

Dockerの中心的な要素として「Dockerfile」と「イメージ」と「コンテナ」がある。Dockerfileはコンテナの構築手順を記述したテキストファイルで、これを元にDockerイメージが作成されます。
このイメージは、アプリケーションのコード、ランタイム、ライブラリ、環境変数など、アプリケーションの実行に必要な全て詰め込んだものです。このイメージを基に、コンテナを起動・実行することができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/aa0d129a-3c14-471f-de9e-5f3abc6f2773.png)

https://www.kagoya.jp/howto/cloud/container/dockerimage/

Dockerイメージは、Docker Hubやその他のコンテナレジストリにアップロードして共有することができるため、他の開発者やチームとのコラボレーションも容易です。さらに、DockerはクラウドサービスやCI/CDパイプラインとも簡単に統合できるため、開発からデプロイまでのワークフローを効率的に行うことができます。(chainerの環境構築ではここまでやりません)

Dockerを更に強力にするツールやエコシステムも豊富に存在する。例えば、`Docker Compose`を使うと、複数のコンテナを一度に管理・実行することができ、複雑なアプリケーションの環境を容易に構築できます。

まとめると、Dockerはアプリケーションの開発、テスト、デプロイメントを効率的かつ一貫性のある方法で行うための強力なツールです。その移植性と再現性により、「私のマシンでは動作する」という問題を解決でき、さらにはデベロッパーや運用チームの間の協力を促進できる技術です。

**OSレベルでの仮想化ができる**ということは、コンテナごとにCUDAやCUDNNといったライブラリを組み替えて入れることができ、アンインストールが大変なCUDNNなどのインストールにエラーが起きてもやり直しが容易です。**したがって、最初にGPUに沿ったNvidiaDriverさえインストールしていれば**他の準備はコンテナ内ですることができ、環境構築をより確実に完了することができます。

# 手法(手順のみ見たい場合はここから)

## 方法１：ローカルでdockerイメージを作成する(推奨)

　まずはNvidiaDriverのインストールをしてください。

[最新の NVIDIA 公式ドライバーをダウンロード](https://www.nvidia.co.jp/Download/index.aspx?lang=jp)

次に、任意のディレクトリにdocker用のディレクトリを作成します。そしてそのディレクトリ内で以下のようなファイルを作成します。(研究用ディレクトリは.devcontainerと並ぶように置きます。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/5703b814-79ab-bd96-61ed-ceba6f59701d.png)

githubに乗せておいたので、`git clone webURL`でクローンしてください。
隠しファイルになっているのでls -all等を使わないと確認できません。注意して下さい。

https://github.com/shisowakame/.devcontainer.git

ソースコードの説明が読みたい方は以下を参照してください。適宜環境に応じて変更する箇所があります。(ベースイメージのubuntuのバージョンなど)

### ソースコード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/9bc04f1a-9be1-6d9e-2d53-1482b1c69829.png)


- .envファイルのソースコード
    
    ```python
    DISPLAY=192.168.11.7:0.0
    ```
    
- docker-compose.ymlのソースコード
    
    ```docker
    version: '3.8'
    services:
    docker-chainer:
    build:
    context: .
    dockerfile: Dockerfile
    image: docker-chainer:docker-chainer # イメージ名&タグ名
    container_name: docker-chainer # コンテナ名
    deploy:  # GPUの設定
    resources:
    reservations:
    devices:
    - driver: nvidia
    count: all
    capabilities: [gpu]
    stdin_open: true
    tty: true
    privileged: true
    environment:
    TZ: "Asia/Tokyo"
    DISPLAY: "${DISPLAY}"
    volumes:
    - ../:/home/research
    working_dir: /home/research
    ```
    
- dockerfileのソースコード
    
    ```docker
    #FROM python:3.9
    FROM nvidia/cuda:12.2.0-devel-ubuntu20.04
    ENV PYTHONUNBUFFERED 1
    ENV DEBIAN_FRONTEND=noninteractive
    WORKDIR /home/research
    
    #Update packages & installation
    RUN set -x && \
    apt update && \
    apt upgrade -y && \
    apt install -y python3 python3-pip && \
    apt install -y python3-tk && \
    apt install -y x11-apps sqlite3 libsqlite3-dev zlib1g-dev libjpeg-dev ntpdate wget sudo && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
    ENV MPLBACKEND="TkAgg"
    #Python requirements
    COPY requirements.txt .
    #Upgrade pip
    RUN pip install --upgrade pip
    #Install requirements
    RUN pip install cupy-cuda12x
    RUN pip install numpy>=1.19.5
    RUN pip install --no-cache-dir -r requirements.txt
    #Copy current directory contents
    COPY . .
    ```
    
    `cupy-cuda120`はCuPyのバージョン12.0.0を指す特定の名前です。エラーメッセージから、この名前でのパッケージが見つからないことが示されています。
    
    正しいパッケージ名を使用してインストールし直す必要があります。以下の手順で確認・インストールを行ってみてください。
    
    1. 最新のCuPyのバージョンを調べます。通常、[PyPIのCuPyページ](https://pypi.org/project/cupy/)にアクセスすることで、最新のバージョンやインストール方法を確認できます。
    
    [cupy](https://pypi.org/project/cupy/)
    
    1. Dockerfileの該当の行を以下のように変更してください。
        
        ```docker
        RUN pip install cupy-cuda120==<最新のバージョン>
        ```
        
        `<最新のバージョン>`を実際の最新のバージョン番号に置き換えてください。
        
    
- requirements.txtのソースコード
    
    ```python
    django
    django-widget-tweaks
    django-allauth
    alembic==1.7.7
    blinker==1.5
    chainer==7.8.1
    chainercv==0.13.1
    chainerui==0.11.0
    click==8.0.4
    #contourpy==1.1.0
    cycler==0.11.0
    Cython==0.29.35
    fastrlock==0.8.1
    filelock==3.4.1
    Flask==2.0.3
    fonttools==4.27.1
    gevent==22.10.2
    greenlet==2.0.2
    imageio==2.16.0
    importlib-metadata==4.8.3
    importlib-resources==5.4.0
    itsdangerous==2.0.1
    Jinja2==3.0.3
    joblib==1.1.1
    kiwisolver==1.3.1
    #lazy-loader==0.2
    Mako==1.1.6
    MarkupSafe==2.0.1
    matplotlib==3.3.4
    msgpack==1.0.5
    networkx==2.5.1
    #numpy==1.19.5
    packaging==21.3
    Pillow==8.4.0
    #pip==19.3.1
    protobuf==3.17.3
    pydicom==2.3.1
    pyparsing==3.1.0
    python-dateutil==2.8.2
    PyWavelets==1.1.1
    scikit-image==0.17.2
    scikit-learn==0.24.2
    scipy==1.5.4
    setuptools==41.6.0
    six==1.16.0
    SQLAlchemy==1.4.49
    structlog==21.5.0
    threadpoolctl==3.1.0
    tifffile==2020.9.3
    typing-extensions==4.1.1
    urllib3==1.25.8
    Werkzeug==2.0.3
    zipp==3.6.0
    zope.event==4.6
    zope.interface==5.5.2
    #cupy-cuda11x==12.1.0
    #zlib1g-dev
    #libjpeg-dev
    ```
    
- devcontainer.jsonのソースコード
    
    ```python
    {
    "name": "docker-chainer",
    "dockerComposeFile": [
    "docker-compose.yml"
    ],
    "service": "docker-chainer",
    "workspaceFolder": "/home/research",
    // リモート先のVS Codeにインストールする拡張機能
    "extensions": [
    "ms-python.python", //Pythonセット
    "aaron-bond.better-comments", //コメントに色をつけられる
    "kevinrose.vsc-python-indent", //Pythonの改行を自動で最適な場所へ
    "ms-ceintl.vscode-language-pack-ja", //日本語化
    "coenraads.bracket-pair-colorizer-2", //かっこ同士の色分け
    "oderwat.indent-rainbow" //段落の色分け
    ]
    }
    ```
    

### 実行

**`docker-compose up -d --build`で実行**　

- `-d`は「detached mode」を意味します。このオプションを使用すると、コンテナがバックグラウンドで実行され、ターミナルがそのまま使える状態になります。
- `--build`オプションは、サービスのイメージをビルドまたは再ビルドするために使用されます。このオプションを指定すると、既存のイメージがある場合でも、Dockerは必ず新しいイメージをビルドします。これにより、Dockerfileやビルドコンテキストに変更があった場合でも、常に最新の状態のイメージが使用されます。

### VScodeでのコンテナのリモートウィンドウの開き方

　私はエディタにVScodeを使っているのでVScodeでの開き方を説明します。エンコードや文字コードが記載されている最下部の細いバーの一番左のクリックします。するとリモートウィンドウを開くオプションの選択画面が現れるので、実行中のコンテナにアタッチという項目を選択して起動しているコンテナ一覧から開きたいコンテナを選択します。

### GUIの設定

　仮想環境ではそのままだとGUI操作(matplotlibでの図形の表示等のグラフィカルな操作)ができないため設定を変更する必要があります。

1. **VcXsrvのインストール** (**https://sourceforge.net/projects/vcxsrv/**)

　DockerコンテナのLinuxでGUIアプリを動かす一般的な方法に、Xサーバ(X Window System)を使うというものがあります。なぜなら普通にDockerコンテナを作成するだけでは、Xサーバがインストールされていないので、CUIコンソールしか機能せず、GUIのウィンドウの描画やキーボードやマウスからの入力といったものは出来ないためです。

　なので、まずはホストPC側にVcXsrvというXサーバソフトウェアをインストールする。インストール出来たらスタートメニューからXLaunchを起動して設定を行います。

　ほとんどデフォルト設定で問題ありませんが、ここはチャックを入れないとGUIが表示されません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/3611f31a-2351-65f0-f112-da7b074ac5cb.png)


　設定が完了したら最後のsave configrationを押してDocumentsファイル等の適当な場所にVcXsrvファイルを作り、設定ファイルを保存しておくとXlaunchを開くたびに設定を変更する手間がないのでやっておきます。(Program Filesのほうにインストールの際に作られたVcXsrvファイルがあるためそれと混同しないようにしましょう。)

1. **DockerDesktop のインストール** (https://www.docker.com/products/docker-desktop/)

　wsl上でコマンドラインを使ってdockerをインストールする方法もありますが、ホストマシンとの紐づけなどで難易度が高いです。そのためwslとホストマシンの紐づけが容易なDockerDesktopをインストールしましょう。インストールできたらメールアドレスとパスワードも登録しましょう。

　Setting画面を開いてGeneralの以下の二つがオンになっているか確認します。(なってなかったらオンにしてください)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/01ce2126-24d7-ce51-2941-9fd920cac1df.png)


　

　さらにResoucesのWSL integrationでdockerを使いたいWSLの項目をオンにすることで紐づけが完了します。(全部オンにしてもいいです)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/9f0cc501-85e8-9b42-88f8-054176739ac5.png)


　DockerDesktopアプリは立ち上げておかないと機能しないためwindowsの設定からスタートアップアプリにしておきましょう。

1. **Xサーバ側の接続許可設定をする**

　コンテナ側で実行されたアプリが、自動的にホスト側のXサーバに接続して、GUIの描画を行う仕組みなので、コンテナ側にホストのIPアドレスを教える必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/c7c2f4c6-ca2d-98c1-162e-71cf3ae158e3.png)


https://ameblo.jp/yukozutakeshizu/entry-12502003379.html

　次にWindows Powershell を起動して、`ipconfig`コマンドを使って**ホストマシン側のIPアドレス**を確認します。(WSLのIPアドレスと混同しないように)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/4b728ba7-3db1-a8bb-553a-c2e15ceadf3b.png)


次にwindowsのスタートメニューを開き”メモ帳”と入力し、右クリックで**管理者権限で**メモ帳を実行します。「ファイル」→「開く」でローカルのProgram FilesにあるVcXsrvファイルの「X0.hosts」を開き、上記で調べたIPアドレスを以下のように追記して保存しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/416068b3-e9a0-921b-7e92-3440fc5ad90c.png)


1. **コンテナ作成の準備をして起動**

　上記で作成したdockerのディレクトリに移動し、`docker-compose up -d --build`コマンドでビルドしましょう。(.devcontainorディレクトリで実行しましょう。)

`x11-apps`をインストールしているため、xeyesコマンドを打って目が出力されれば設定完了です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533911/c052a50f-3dec-6f88-2733-5b586a9d14b5.png)


1. **備考**

実際にこのようなエラーが出たためその時は同じようにすると解決するかもしれません。

```python
root@52685fd6608c:~# xeyes
Warning: locale not supported by C library, locale unchanged
```

`local -a`で利用可能なロケールを確認すると以下のような結果が出力されました。

```python
root@52685fd6608c:~# locale -a
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_COLLATE to default locale: No such file or directory
C
C.UTF-8
POSIX
```

利用可能なロケールが非常に限られていることと、デフォルトのロケールの設定が不完全であることが示唆されています。よって以下のようにして解決しました。

```python
apt-get update #必要なパッケージのインストール
apt-get install -y locales
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen #ロケールの生成
locale-gen
update-locale LANG=en_US.UTF-8 #ロケールの設定
export LANG=en_US.UTF-8 #環境変数の設定
xeyes #GUIがうまく動作するか確認
```

`sudo systemctl restart docker`でdockerを再起動

## 方法２：DockerHubからdockerイメージをPullする

この方法はローカルでコンテナイメージを作成する方法と違って、Dockerhubからchainerの公式が出しているイメージを引っ張ってくる１つのコマンドでイメージが作成できるため、より環境構築にあたっての確実性が高い手法です。

```python
docker pull chainer/chainer
```

ただし、コンテナイメージを起動する際に多少オプションで設定を変更できるとはいえコンテナの設定ファイル等がローカルにあるわけではないため、拡張性に乏しくGUIの設定等ができませんでした。

方法１がうまくいかない時の非常手段くらいに考えておいたほうがいいかもしれません。

※pythonコマンドはpython3と記述しないと動作しない。

# エラーとその対処集

筆者に起きた個人的なエラーから環境構築でよくみられるエラーまで、確認できたエラーとその対処法を載せていきます。何かあったら参考にしてみてください。

- **docker-compose upを行って、その後修正点か何かに気づいてもう一度docker-composeb upしようとしても ~handsshake~といったエラーが出る**
    
    コンテナのビルドでエラー、ないしはミスに気付いて強制終了をしてコンテナをビルドしなおすときにでてくるエラーです。
    
    一度docker-composeに失敗してしまうと次回以降ベースイメージをロードする際に以下のようなエラーが起こるようになってしまいます。
    
    ```python
    $ docker-compose up -d --build
    [+] Building 10.7s (4/4) FINISHED docker:default
    => [docker-chainer internal] load .dockerignore 0.1s
    => => transferring context: 2B 0.0s
    => [docker-chainer internal] load build definition from Dockerfile 0.0s
    => => transferring dockerfile: 702B 0.0s
    => ERROR [docker-chainer internal] load metadata for [docker.io/nvidia/cuda:12.2.0-devel-ubuntu20.04](http://docker.io/nvidia/cuda:12.2.0-devel-ubuntu20.04) 10.6s
    => [docker-chainer auth] nvidia/cuda:pull token for [registry-1.docker.io](http://registry-1.docker.io/) 0.0s
    [docker-chainer internal] load metadata for [docker.io/nvidia/cuda:12.2.0-devel-ubuntu20.04:](http://docker.io/nvidia/cuda:12.2.0-devel-ubuntu20.04:)
    failed to solve: nvidia/cuda:12.2.0-devel-ubuntu20.04: failed to authorize: failed to fetch oauth token: Post "https://auth.docker.io/token": remote error: tls: handshake failure
    ```
    
    原因としては、中途半端にコンテナができて新しくコンテナを作るときに競合してしまうからだと考えられます。
    
    対処法として一度失敗したら`docker-compose down`で作りかけのコンテナおよびネットワークを削除し、dockerdesktopでリソースのほうでも削除する(これは必要ないかもしれませんが念のため)と、再び`docker-compose up`が使えるようになります。

   
- **couldn't connect to display "10.255.255.254:0**
     ```
     Traceback (most recent call last):
      File "/usr/local/lib/python3.8/dist-packages/matplotlib/backends/_backend_tk.py", line 859, in new_figure_manager_given_figure
        window = tk.Tk(className="matplotlib")
      File "/usr/lib/python3.8/tkinter/__init__.py", line 2270, in __init__
        self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
    _tkinter.TclError: couldn't connect to display "10.255.255.254:0"
      ```
　このエラーはコンテナを多く立てすぎたのか、直接因果関係があるのか分かりませんがいったん`docker-compose down`でコンテナおよびネットワークをすべて削除し、dockerdesktopでリソースのほうでも削除し、再び`docker-compose up`することで直りました。
- **matplotlibを使ってエラーが出てないのにGUIが操作できない**
    
    このエラーはコンテナをビルドし、GUIの設定も完了し終えた後に出てきたエラーです。matplotlibを使うプログラムが実行はされるが画像が出てきませんでした。プログラムは実行されているので、ctl+Cしないとシェルにプログラムは打てない状況でした。
    
    原因としてはコンテナを立ち上げたときにmatplotlibが`agg` バックエンドを使用しているため、グラフをインタラクティブに表示できないからでした。**`agg`** バックエンドは非GUIバックエンドであり、通常、グラフをファイルに保存する場合にのみ使用されます。
    
    解決策としてmatplotlibが動く環境などが他にもあれば、そこで以下のようにしてGUIバックエンドを確認します。(GUIバックエンドなら多分何でもいいです)
    
    ```python
    Type "help", "copyright", "credits" or "license" for more information.
    >>>import matplotlib
    >>>print(matplotlib.get_backend())
    Qt5Agg
    ```
    
    私の環境ではQt5AggをデフォルトバックエンドGUIとして扱うものとして話を進めます。
    
    DockerfileにデフォルトバックエンドGUIの変更を記述します。
    
    まず`Qt5Agg`をDocker環境で使用するためには、`PyQt5`をインストールする必要があります。Dockerfileに以下の変更を加えて、この依存関係をインストールします。
    
    1. **`requirements.txt`** および Dockerfile の適切な位置に以下を追加して **`PyQt5`** をインストールします。
    
    ```python
    pip install PyQt5
    ```
    
    1. Dockerfile内で、matplotlibのデフォルトバックエンドを`Qt5Agg`に設定する環境変数を設定します。
    
    ```bash
    ENV MPLBACKEND="Qt5Agg”
    ```
    
    再度matplotlibを使っているプログラムを実行するとGUIが表示されるはずです。    
    (**参考記事**)
    [**python:matplotlib:cui環境 [雑多な記録]**](https://www2.yukawa.kyoto-u.ac.jp/~koudai.sugimoto/dokuwiki/doku.php?id=python:matplotlib:cui環境)
    　
- **Warning: locale not supported by C library, locale unchanged**
    ```
    root@52685fd6608c:~# xeyes
    Warning: locale not supported by C library, locale unchanged
    ```
    とエラーが出た際に、`local -a`で利用可能なロケールを確認すると以下のような結果が出力されました。
    ```
    root@4b8d92a592f6:/home# locale -a
    locale: Cannot set LC_CTYPE to default locale: No such file or directory
    locale: Cannot set LC_MESSAGES to default locale: No such file or directory
    locale: Cannot set LC_COLLATE to default locale: No such file or directory
    C
    C.UTF-8
    POSIX
    ```

    利用可能なロケールが非常に限られていることと、デフォルトのロケールの設定が不完全であることが示唆されています。よって以下のようにして解決しました。
    ```
    apt-get update #必要なパッケージのインストール
    apt-get install -y locales
    echo "en_US.UTF-8 UTF-8" > /etc/locale.gen #ロケールの生成
    locale-gen
    update-locale LANG=en_US.UTF-8 #ロケールの設定
    export LANG=en_US.UTF-8 #環境変数の設定
    xeyes #GUIがうまく動作するか確認
    ```
    なお、`update-locale LANG=en_US.UTF-8`で以下のエラーが出ることがあります。
    ```
    root@4b8d92a592f6:/home# update-locale LANG=ja_JP.UTF-8
    perl: warning: Setting locale failed.
    perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
    perl: warning: Falling back to the standard locale ("C").
    ```
    perl: warning: Setting locale failed. というエラーは、Perl がロケール設定に失敗したことを示しています。

    これは、update-locale コマンドが LANG 環境変数を en_US.UTF-8 に設定しようとしているにもかかわらず、このロケールがシステムにインストールされていないことが原因と考えられます。
    コンテナ内で`locale-gen en_US.UTF-8`を実行して、再度`update-locale LANG=ja_JP.UTF-8`するとロケールの設定ができるようになり、`xeyes`も実行できました。

- **DISPLAY変数が反映されていない**
何らかの理由でDISPLAY変数が反映されておらず、GUI操作ができない可能性があります。  
コンテナ内で`echo $DISPLAY`を実行し、.envに記入した内容と一致しているか確認します。
一致していない場合は`export DISPLAY=192.168.12.8:0.0`(自分の.envに合わせてアドレスは変更してください)でひとまず解決します。


- **学習時間が明らかに遅い**
    
    エラーではありませんが実際に起こったことです。学習等が正常に行われているのにも関わらず、研究室の他のGPUマシンで同じプログラムを動かし比較してみると、学習時間が倍以上違いました。
    
    原因としてはモジュールやライブラリが欠けている可能性があります。
    私の環境ではCUDNNが最適でないものがインストールされていたようで、認識されていませんでした。
    
    手間ですが調べ上げて修正したほうがいいです。(私の場合1学習ごとに24時間くらいの差が出ました。)
    
https://developer.nvidia.com/cudnn

ここからCUDNNのバージョンを確認してDockerfileに個別でCUDNNのインストールの文を打っておくと確実だと思います。
    
pipではなくaptを使うためコンテナを立て直す必要があるので、ビルドしなおして学習時間が短縮されたか確認しましょう。
    
- **DockerfileでCUDNNのインストールができない**
    
    先ほどのCUDNNインストールに付随したものです。あっさりインストールされればそれでいいのでこの文は読まなくていいです。
    
    CUDNNのインストールがうまくいかなかったのでベースイメージにCUDNNを含めて指定してみましたがエラーでした。筆者のエラーメッセージを確認すると、指定した`nvidia/cuda:12.0-cudnn8-runtime-ubuntu20.04`イメージがDocker Hubに存在しないことが示されていました。(ベースイメージとしてCUDAとCUDNNを同時に入れてしまいたかったのですがそのようなイメージがありませんでした。)
    
    正しいタグを指定するために、Docker Hubの[NVIDIA CUDAのページ](https://hub.docker.com/r/nvidia/cuda)を参照して、利用可能なタグを確認します。
    
    1. Docker HubのNVIDIA CUDAのページにアクセスします。
    2. 利用可能なタグを確認します。たとえば、`11.0-cudnn8-runtime-ubuntu20.04`のようなものを探します。
    3. 正しいタグを見つけたら、Dockerfileの`FROM`行をそれに合わせて更新します。
    4. 再度、`docker-compose up -d --build`を実行します。
    
    また、特定のバージョンのCUDAやcuDNNが必要でない場合はタグを緩和して、たとえば**`nvidia/cuda:11.0-runtime-ubuntu20.04`**のようにして、問題を回避することもできます。
    
    ベースイメージはここで見つけます。ubuntu用には3種類あるので注意
    
https://hub.docker.com/r/nvidia/cuda
    
    バージョン名について
    
    - base: 最小構成
    - runtime: baseを拡張したもの
    - devel: runtimeを拡張したもの
    
    開発用・学習用であれば、基本的にdevelを選んでおけば問題ないと思います。(devel にしか　nvccが無いなどの問題がある)
    
    CUDA 12.0のコンテナを試します。
    バージョンは、Docker Hubを参照して指定します。
    
- **一度pipでインストールしたモジュールがインポートできない**
    
    コンテナを起動しなおした際にモジュールがリセットされている可能性があります。
    pipでインストールしたらrequirements.txtにも追記しましょう。そうすると次回以降の起動でもインポートができるようになります。
    
- **requirements.txtに記述しているnumpyのインストールがうまくいかない**
    
    この問題の原因として考えられるのは、他のモジュールのバージョンのセットアップスクリプトが、メタデータの生成の段階で **`numpy`** に依存しているためです。
    **`numpy` を先にインストールしましょう。** **`Dockerfile`** において、**`numpy`** のインストールを **`requirements.txt`** を使わずに、先に実行することでこの問題を回避できます。
    
- **コンテナ名やコンテナイメージ名に大文字を使用**
    
    エラーメッセージも恐らく出ますが大文字はコンテナをビルドする際にはできません。
    
- **pipとcondaの混在**
    
    これは起こりがちなミスです。**condaとpipを併用しないでください。**
    condaとpipはどちらもインストール管理ツールです。ソフトウェアのパッケージ管理先が**conda**は**Anaconda**。**pip**は**PyPI**で異なります。
    
    **Anaconda環境**は、最初にPythonを使う目的では最低限必要なものが揃ってて便利な反面、他のパッケージは**PyPI**の方が豊富なので、別途pipでインストールすることも多いです。
    
    ただし、condaとpipのインストールが混在するとパッケージ間の依存関係に問題が出たり最悪環境が壊れることもあるので、**基本的にcondaとpipは併用しないこと**が推奨されています。
    すでにUbuntuなどのLinuxディストリビューションにanacondaを入れている場合は、削除の方法は大きく分けて3つあります。
    
    **・anacondaのフォルダごと消す**
    
    **・アプリと機能からアンインストール選択**
    
    **・anaconda-cleanを実行**
    です。ただ、anaconda環境でも色々と作業を進めていて削除したくない場合もあると思います。そんなときはバージョンの違うLinuxディストリビューションを別にインストールし、ファイルをコピーして環境を作り直した方が早いと思います。(筆者の場合はUbuntu22.04LTSにanacondaを入れて使っていたので、新しくUbuntu20.04LTSをインストールしてpip用の環境にしました。)
    
    ```
    conda install anaconda-clean
    anaconda-clean
    rm -rf ~/anaconda3
    vi ~/.bashrc で>>> conda initialize >>> から <<< conda initialize <<<までを削除
    source ~/.bashrc
    ```
    
    **参考記事**
    
https://note.com/sa10youch/n/n040b8d397ccf#8c204216-4c64-4cf6-affa-66562b8125dc

- **エラーメッセージ**
```
docker.errors.DockerException:
Error while fetching server API version:
HTTPConnection.request() got an unexpected keyword argument 'chunked’」
```
これはrequestのバージョンを2.31.0にすると解決します。

https://github.com/google-deepmind/alphafold/issues/812

    
- **permission denined**
dockerコンテナ内で操作したファイルは以下のようにroot権限でしかいじれなくなっています。
```
-rw-r--r--  1 root root     186 Jun 21 13:13  test.py
```
sudo コマンドを使用すると、仮想環境が無効化されてしまいます。そのため、
```
sudo chown -R user:user /home/user/research
```
を行い、reserarch ディレクトリ以下のすべてのファイルとフォルダの所有者を現在のユーザー（user:自分のユーザー名に変更してください）に変更します。これにより、sudo を使わなくてもアクセスできるようになります。
　

- **その他**
    
    CUDAは最新のバージョンを入れればいいというわけではないということに留意してください。
    
 https://docs.chainer.org/en/stable/upgrade.html
    
バージョンの確認方法は以下のコマンドを参照して下さい。
    
    ```bash
    $ python3
    >>>import chainer
    >>>chainer.print_runtime_info()
    ```
    
以下のような結果が出ます。
    
    ```
    bash
    chainer.print_runtime_info()
    Platform: Linux-5.15.90.1-microsoft-standard-WSL2-x86_64-with-glibc2.29
    Chainer: 7.8.1
    ChainerX: Not Available
    NumPy: 1.24.4
    CuPy:
    OS                           : Linux-5.15.90.1-microsoft-standard-WSL2-x86_64-with-glibc2.29
    Python Version               : 3.8.10
    CuPy Version                 : 12.2.0
    CuPy Platform                : NVIDIA CUDA
    NumPy Version                : 1.24.4
    SciPy Version                : 1.5.4
    Cython Build Version         : 0.29.36
    Cython Runtime Version       : 0.29.35
    CUDA Root                    : /usr/local/cuda
    nvcc PATH                    : /usr/local/cuda/bin/nvcc
    CUDA Build Version           : 12020
    CUDA Driver Version          : 12020
    CUDA Runtime Version         : 12020
    cuBLAS Version               : 120201
    cuFFT Version                : 11008
    cuRAND Version               : 10303
    cuSOLVER Version             : (11, 5, 2)
    cuSPARSE Version             : 12102
    NVRTC Version                : (12, 2)
    Thrust Version               : 200101
    CUB Build Version            : 200101
    Jitify Build Version         : <unknown>
    cuDNN Build Version          : 8801
    cuDNN Version                : 8905
    NCCL Build Version           : 21602
    NCCL Runtime Version         : 21805
    cuTENSOR Version             : None
    cuSPARSELt Build Version     : None
    Device 0 Name                : NVIDIA TITAN RTX
    Device 0 Compute Capability  : 75
    Device 0 PCI Bus ID          : 0000:2B:00.0
    iDeep: Not Available
    ```
    
https://github.com/NVIDIA/nvidia-docker/wiki/CUDA#requirements
    
私は最初、最新のCUDA(chainer開発終了後に出ている)とそれに対応するcudnnをインストールしていたせいでうまく動作しませんでした。
    

# 備考

　この記事ではNvidiaDriverのインストールのみはローカルで行っており、他に必要なものは仮想環境でそろえるというものでしたが、NvidiaDriverごとコンテナに積むという手法もあるみたいです。( 試してません。)

https://qiita.com/mshinoda88/items/30e46e9b88d69ad2e289

# 参考にした記事

https://qiita.com/chilchil0/items/9c7c56ad1cdafef389c4

https://www.eureka-moments-blog.com/entry/2020/03/01/143309

https://qiita.com/yutake27/items/3a3a44ea8185887eff1c

https://qiita.com/ksasaki/items/b20a785e1a0f610efa08#おまけ-nvidia-ドライバーのインストール