俺様的Docker環境の記録


# 目的
Dockerを利用して、AIの学習環境を整えたい。他の記事ではVENVを利用して、いろいろやっているのだけど、別事案で、Ubuntuのバージョンを含めて対応しないといけない事象が発生した。そのため、簡便に、基本的な構成を利用できるようにしてみた。

# Dockerfile
## 構成
FROM, RUN, CMD, ENVだけをシンプルに利用

### FROM
今回は、NVIDIA謹製のDockerイメージを再利用します。あるかないかは、このサイトhttps://hub.docker.com/r/nvidia/cuda/ で確認できます。確認するまでもなく、FROMに記載できます。

ubuntu22.04(focal)でcuda 12.6
```
FROM nvidia/cuda:12.6.2-devel-ubuntu22.04
```

ubuntu20.04(jammy)でcuda 11.8
```
FROM nvidia/cuda:11.8.0-devel-ubuntu20.04
```

### LABEL
自分だけの環境なら不要ですが、誰の持ち物か、名前を書いておかないと、いけないと思う。

```
LABEL maintainer="Ubuntu"
```

### RUN - apt-get (初回 update)

時刻合わせをしたほうがいいと思うので、そちらを含めて実施

```
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive TZ=Asia/Tokyo apt-get install -y tzdata && \
    apt-get upgrade -y
```

### RUN - apt-get (使いそうなツールインストール)
いらないのもあると思います。ディスク容量を節約したいなら、切り詰めていいでしょう。あとは、docker runするとき、--rmするかですね。rmするなら、記載しておかないと、毎度インストールする羽目になる。

```
RUN apt-get install -y \
    ca-certificates \
    curl \
    build-essential \
    fonts*noto* \
    git \
    git-lfs \
    gpg \
    iproute2 \
    iputils-ping \
    libssl-dev \
    net-tools \
    nfs-common \
    openssh-server \
    python3-dev \
    python3-opencv \
    python3-pip \
    python3-venv \
    sudo \
    vim \
    wget \
    x11-apps \
    xauth
```

### RUN - pythonモジュールのインストール
適当に必要そうなものをピックアップ。Yolov9を試すなら、[yolov9のrequirement.txt](https://github.com/WongKinYiu/yolov9/blob/main/requirements.txt)を参考に設定すれば良さそう。

```
RUN python3 -m pip install --ignore-installed embedchain --no-cache-dir --upgrade \
    gitpython \
    ipython \
    "matplotlib>=3.2.2" \
    "numpy>=1.18.5" \
    "opencv-python>=4.1.1" \
    "Pillow>=7.1.2" \
    psutil \
    "PyYAML>=5.3.1" \
    "requests>=2.23.0" \
    "scipy>=1.4.1" \
    "thop>=0.1.1" \
    "torch>=1.7.0" \
    "torchvision>=0.8.1" \
    "tqdm>=4.64.0" \
    "tensorboard>=2.4.1" \
    "pandas>=1.1.4" \
    "seaborn>=0.11.0" \
    "albumentations>=1.0.3" \
    "pycocotools>=2.0"
```


### RUN - sshしたいあなたへ (いまのところx11と排他)
コンテナへssh接続したい場合は、apt-getでopenssh-serverをインストールした上で、パスワード認証をONにする必要があります。2行目は、おまじない。rootでログインしないのであれば、無視しても大丈夫。

```
RUN sed -i.bk -e 's/#\(PasswordAuthentication yes\)/\1/' /etc/ssh/sshd_config && \
    sed -i.bk -e 's/#\(PermitRootLogin\) prohibit-password/\1 yes/' /etc/ssh/sshd_confi
```

デフォルト設定だと、nat接続になるので、ネットワークの設定をしない限り、ホストからのみアクセスできる設定です。ネットワークの設定をいじるか、dockerをホストのネットワークへbridge接続する必要があります。(このページでは割愛)
また、X11を吐きたい場合は、--net hostとする必要がありそうです。そうすると、ホスト側と同じIP構成になるため、Dockerクライアントに対して、外からSSHすることは困難な模様


### RUN - uid/pidの追加
いつものやつが欲しい人は、こちらをどうぞ

```
RUN groupadd -g 1000 ubuntu
RUN useradd -m -u 1000 -g ubuntu -s /bin/bash ubuntu
```

### home directory

```
RUN mkdir /home/ubuntu
```

### RUN - sudoersの設定
sudoする際、パスワード不要にするおまじない。必要に応じてどうぞ

```
RUN echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
```


### RUN - 最後にユーザ、working directoryを実行して、ログイン

```
USER ubuntu
WORKDIR /home/ubuntu
CMD ["bash"]
```

## コンテナビルド

-tが、docker image lsのREPOSITORY情報として表示される
-fは、いままで作業してきたDocker fileを指定する。デフォルトはDockerfileなので、変更したい場合は、-fで対象ファイルを指定しましょう。

```
docker build -t u22c1206 -f Dockerfile.u22cuda1206 .
```

```
$ docker image ls
REPOSITORY   TAG        IMAGE ID       CREATED             SIZE
u22c1206     latest     cXX1fXXb32XX   XX minutes ago      1X.XGB
```



# docker run

コンテナができたら、次は起動です。
"--net host"をつけると、ホストと同じIP構成になります。これをつけると、X11 forwardingが動作します。

```
docker run \
	-it \
	-e DISPLAY=${DISPLAY} \
    -v $HOME/.Xauthority:/home/ubuntu/.Xauthority:rw \
	-v /tmp/.X11-unix:/tmp/.X11-unix \
	--gpus all \
	--net host\
	--rm \
    --tmpfs /tmp:exec \
	u22c1206
```


## その他メモ

### ホスト側に必要な設定

nvidiaのGPUを利用する際は、次のパッケージが必要。

```
sudo apt install nvidia-container-runtime
```
このパッケージを入れておかないと、次のエラーが出ます

```
docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].
```


# まとめ

## Dockerfile

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

LABEL maintainer="Ubuntu"

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive TZ=Asia/Tokyo apt-get install -y tzdata && \
    apt-get upgrade -y

RUN apt-get install -y \
    ca-certificates \
    curl \
    build-essential \
    fonts*noto* \
    git \
    git-lfs \
    gpg \
    iproute2 \
    iputils-ping \
    libssl-dev \
    net-tools \
    nfs-common \
    openssh-server \
    python3-dev \
    python3-opencv \
    python3-pip \
    python3-venv \
    sudo \
    vim \
    wget \
    x11-apps \
    xauth

RUN python3 -m pip install --ignore-installed embedchain --no-cache-dir --upgrade \
    gitpython \
    ipython \
    "matplotlib>=3.2.2" \
    "numpy>=1.18.5" \
    "opencv-python>=4.1.1" \
    "Pillow>=7.1.2" \
    psutil \
    "PyYAML>=5.3.1" \
    "requests>=2.23.0" \
    "scipy>=1.4.1" \
    "thop>=0.1.1" \
    "torch>=1.7.0" \
    "torchvision>=0.8.1" \
    "tqdm>=4.64.0" \
    "tensorboard>=2.4.1" \
    "pandas>=1.1.4" \
    "seaborn>=0.11.0" \
    "albumentations>=1.0.3" \
    "pycocotools>=2.0"

RUN groupadd -g 1000 ubuntu
RUN useradd -m -u 1000 -g ubuntu -s /bin/bash ubuntu
RUN mkdir /home/ubuntu

RUN echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

USER ubuntu
WORKDIR /home/ubuntu
CMD ["bash"]
```

## docker build

```
docker build -t u22c1206 -f Dockerfile.u22cuda1206 .
```

## docker run

```
docker run \
	-it \
	-e DISPLAY=${DISPLAY} \
    -v $HOME/.Xauthority:/home/ubuntu/.Xauthority:rw \
	-v /tmp/.X11-unix:/tmp/.X11-unix \
	--gpus all \
	--net host\
	--rm \
    --tmpfs /tmp:exec \
	u22c1206
```

今日はここまで。

# 応用編

## 環境変数を使いたい

```
docker build --build-arg MYARG1=${myarg1} --build-arg MYARG2=${myarg2} -t mydocker -f Dockerfile .
```

このように、\-\-build-argを利用することで、Dockerfile内へ展開することが可能。複数の環境変数を渡したい場合は、複数回--build-argで引き渡せばOK。たとえばHOSTNAME\=\\$\(hostname\)として、\-\-build-arg HOSTNAME\=\\$\{HOSTNAME\}とすれば、HOSTNAMEがDockerfileへ渡せる。Dockerfile内で、if文を使うことで、固有ホストに対する動作を指定することが可能になる

```
ARG HOSTNAME
ENV HOSTNAME=$HOSTNAME

RUN echo "HOSTNAME: ${HOSTNAME}" && \
    if [ "$(echo $HOSTNAME)" -eq "MYSERV" ]; then \
        echo 'Run for the special host'; \
        #add your script for MYSERV here
    fi
```

引数の受取のおまじないとして、ARGとENVの操作が必要デス


## VSCODEを利用したい

公式の手順に従えば、インストール可能。起動時ハマる環境があるので、その場合は、--no-sandboxを指定する必要がある。

```
RUN apt-get update && \
    apt-get install -y wget gpg && \
    wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg && \
    install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg && \
    echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | tee /etc/apt/sources.list.d/vscode.list > /dev/null && \
    rm -f packages.microsoft.gpg
RUN apt-get install -y code
```

```
$ code --no-sandbox .
```

dockerを--rmオプション付きで起動している場合は、コンテナ再起動のたびに、プラグインの(再)インストールが必要になる。

今日はここまで part 2