いまさらUbuntu20.04LTS + docker + tensorflow(GPU) + jupyter notebook で環境構築

## 初めに
備忘録です． 
2023/04/12くらいでもできることを確認しました. 参考文献は最後にまとめてあります．

## 構築
### 1. `nvidia`ドライバを当てる
```
nividia-smi
```
GPUが表示されればOK. 

:::note warn
SecureBootが有効だとめんどくさいのでOFFにすること.
:::

#### ドライバを入れる必要がある時
いくつか方法がある． (apt使うとか, `nvidia`のサイトからshell scriptコピーしてきて最後の行を`cuda-driver`にする方法など)

今回は`apt`を利用する．

```bash
sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update
```
でリポジトリ追加して
```bash
ubuntu-drivers devices
```
で推奨ドライバを確認する.
**推奨と同じ番号の`open`とついてないのを選ぶ(大事).**
`open`の方を選ぶと大体こける.（なぜ？)

```bash
sudo apt install nvidia-driver-525
```
でインストールして再起動. 

### 2. `Docker`のインストール
```bash
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo gpasswd -a $USER docker
exit
```
再度shellで
```
docker -v
```
であたればOK.

#### 2.1. こける時
この方法でもOK
```bash
sudo apt update
sudo apt install -y \
  ca-certificates \
  curl \
  gnupg \
  lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg  
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
docker -v
```
GPGの何ちゃらが合わん．おかしい．と言われた時は，`/usr/share/keyrings/docker-archive-keyring.gpg`を消してやり直すとうまく行くかも.

### 3. `nvidia-container2`のインストール
```bash
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
      && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
      && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
            sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
            sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-docker2
```
これで再起動する

```
sudo docker run --rm --gpus all nvidia/cuda:11.0.3-base nvidia-smi
```
で`nvidia-smi`でGPUが当たればOK. 
何はともあれ`docker`の中でGPUが使えるようにする．

### 4. `tensorflow`の環境構築
#### 4.1. `jupyter`と`tensorflow`のDockerイメージを持ってくる
```bash
docker pull tensorflow/tensorflow:latest-gpu-jupyter
```
で入れる
```bash
docker run --gpus all -it tensorflow/tensorflow:latest-gpu-jupyter bash
```
で動作確認．画面にクソデカTensorflowが出たらOK. 中でnvidia-smiも叩いて本当に`--gpus all`が効いているかみておく．ダメな場合, `nvidia-container2`がおかしいので, そっちを入れ直す．  
`exit`でdeattchしてから, `docker ps -aq`で調べて`docker rm (PID)`で殺しておく．

VSCの
- Docker
- Python
- Remote-Containers
- Remote-SSH  

とか入れておくとても便利. 

### 4.2. `docker compose`のインストトール

```
sudo apt-get update
sudo apt-get install docker-compose-plugin
```
で入れて
```
docker compose version
```
で見れればOK

### 4.3. Docker 環境の構築
作業ディレクトリを掘る．自分のhomeしたとか
```
mkdir ~/containers
cd  ~/containers
touch Dockerfile devcontainer.json docker-compose.yml
```
みたいな感じで作っておく.

##### 4.3.1. `Dockerfile`
```Dockerfile
FROM tensorflow/tensorflow:latest-gpu-jupyter

RUN apt-get update
ARG UID=1000
RUN useradd -m -u ${UID} user

USER ${UID}

ADD  --chown=user:user . /work

RUN pip install matplotlib
RUN pip install scikit-learn
RUN pip install numpy
```

#### 4.3.2. `devcontainer.json`
```json
{
	"name": "Existing Dockerfile",
	"context": "..",
	"dockerFile": "./Dockerfile",
	"workspaceFolder": "/tf",
	"settings": { 
		"terminal.integrated.shell.linux": "/bin/bash",
		"python.pythonPath": "/usr/local/bin/python",
		"python.linting.enabled": true,
		"python.linting.pylintEnabled": true,
		"python.linting.pylintPath": "/usr/local/bin/pylint"
	},
	"appPort": [ 9000 ],
	"remoteUser": "user",
	"extensions": [
		"ms-python.python"
		]
}
```

#### 4.3.3. `docker-compose.yml`
```yml
version: '3'
services:
  tensorflow:
    build: .
    volumes:
      - ../:/tf/work
    working_dir: /tf
    ulimits:
      memlock: -1
      stack: -1
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              capabilities: [ gpu ]
    tty: true
    expose:
      - "8888"
    ports:
      - "127.0.0.1:8888:8888"
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
```

### 5. `Compose Up`する
VSCodeでさっきのディレクトリ開いてymlを`compose up`してコンテナ作る. そのコンテナにVSCode上アタッチしてもよし. ブラウザで`localhost:8888`でjyupterNoteBook開いてもよし.

ただし`jupyter notebook`は初回VSCodeでアタッチして
```
jupyter notebook list
```
とやってから表示される`token`(`token=?のとこ`) をコピーして`password or token`にぶち込む．もしくは,
したの方に行くと毎回`token`入れるのだるい人向けにパスワードを自由に設定できる.

### 6. Jupyter とか
サーバになっているので, `jupyter notebook`の設定で`localhost`以外も許可すれば, 他のPCとかからもアクセスできる． めんどい場合は, SSHで接続して`ポートフォワーディング`すれば楽かも.

特にローカルで使うなら`jupyter`の接続許可IPを`0.0.0.0`とかにしてしまってもいいと思う．
外部に建てているのなら証明書とかちゃんと作ってやった方が良い．

## 参考文献
https://www.appliot.co.jp/blog/nvidia%E3%81%AEgpu%E3%82%92docker%E3%81%AEtensorflow%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%99/

https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html

https://hirooka.pro/ubuntu-22-04-nvidia-container-toolkit-docker/