企業におけるDockerのお作法

# なぜDockerを使うのか
- IaCの文脈ではデファクトスタンダードなDockerを使うことが多い
- 以下のようなケースが多くあるので使うことを推奨（やるやらないは別として知っておくに越したことはない）
    - **「検証環境で動いたコードが商用環境の端末で動かそうとしたら動かなくなった」といった環境依存関係をなくしたい**
    - **gitからcloneしたが動かなかったコードをくようにしたものをチームで共有したい**
# 概要**と**確認コマンド
- 以下がわかりやすい
[Docker初学者がやるべきこと3選 - Qiita](https://qiita.com/nuco_fn/items/22cf85a2646d96361d0b?utm_source=Qiita%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&utm_campaign=fa771a6b4d-Qiita_newsletter_547_12_28_2022&utm_medium=email&utm_term=0_e44feaa081-fa771a6b4d-33674289)
        
# 手順
以下の3ステップでDockerを動かせる状態にする
1. Dockerfileを作る
2. Docker imageをbuildする
3. Docker containerをrunする

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191313/4ab88bb2-2b6b-01b3-a2c7-16363953cba2.png)

（[Docker初学者がやるべきこと3選 - Qiita](https://qiita.com/nuco_fn/items/22cf85a2646d96361d0b?utm_source=Qiita%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&utm_campaign=fa771a6b4d-Qiita_newsletter_547_12_28_2022&utm_medium=email&utm_term=0_e44feaa081-fa771a6b4d-33674289)より拝借）
        
# Dockerfileを配置するディレクトリ構成例
- Dockerを導入する上でのファイル配置は以下を参考にすると良い（機械学習のプロジェクトを想定）
    - サンプル：
        
        ```markdown
        my_ml_project/
        │
        ├── data/                 # データセットや中間データを格納
        │   ├── raw/
        │   └── processed/
        │
        ├── model/                # トレーニング済みのモデルやモデルのチェックポイントを格納
        │
        ├── notebooks/            # Jupyter ノートブック
        │
        ├── src/                  # ソースコード
        │   ├── __init__.py
        │   ├── data_preparation/
        │   ├── model_definition/
        │   └── training_scripts/
        │
        ├── tests/                # テストコード
        │
        ├── Dockerfile            # Dockerfile
        │
        ├── requirements.txt      # 必要なPythonパッケージのリスト
        │
        └── README.md             # プロジェクトの説明やセットアップ方法など
        
        ```
            
# Dockerfileの書き方
- Dockerfileの作り方は2通りある
    1. 自作する：色々と自分でいじれてコピペして使う時に便利
        - サンプル：
            
            ```docker
            # 1. ベースイメージ（以下追加する元となるイメージ）を指定(イメージ：バージョン)
            FROM ubuntu:20.04
            
            # 2. あとはベースイメージに追加したい内容を記載する
            # docker imageをbuildする際に自動実行されて、docker runする際にはすでに実行済みの状態となる
            
            # RUNで普段ターミナルで打っているコマンドを記載できる
            RUN apt-get update && apt-get install -y --no-install-recommends \
                    # 日本語表示で文字化けを防ぐ
                    language-pack-ja-base language-pack-ja \
                    libglib2.0-0 \
                    libsm6 \
                    libxrender1 \
                    libxext6 \
                    build-essential \
                    libssl-dev \
                    libffi-dev \
                    python3-dev \
                    python3 \
                    python3-pip && \
                rm -rf /var/lib/apt/lists/*
            
            # 日本語表示で文字化けを防ぐ
            RUN echo 'export LANG=ja_JP.UTF-8' >> ~/.bashrc
            RUN echo 'export LANGUAGE="ja_JP:ja"' >> ~/.bashrc
            
            # source ~/.bashrc と同じ処理
            RUN . ~/.bashrc
            
            # pip install
            RUN pip3 install --upgrade pip setuptools
            COPY ./requirements.txt /tmp
            RUN pip3 install -r /tmp/requirements.txt
            
            # Required for nvidia-docker v1
            RUN echo "/usr/local/nvidia/lib" >> /etc/ld.so.conf.d/nvidia.conf && \
                echo "/usr/local/nvidia/lib64" >> /etc/ld.so.conf.d/nvidia.conf
            
            # PATHの指定
            ENV PATH /usr/local/nvidia/bin:/usr/local/cuda/bin:${PATH}
            ENV LD_LIBRARY_PATH /usr/local/nvidia/lib:/usr/local/nvidia/lib64
            
            # nvidia-container-runtime
            ENV NVIDIA_VISIBLE_DEVICES all
            ENV NVIDIA_DRIVER_CAPABILITIES compute,utility
            ENV NVIDIA_REQUIRE_CUDA "cuda>=11.0 brand=tesla,driver>=418,driver<419 brand=tesla,driver>=440,driver<441 brand=tesla,driver>=450,driver<451"
            
            ```
                
    2. **Docker Hubからpullする**：cudaのバージョン対応するベースイメージは自作すると大変なのでDocker Hubから探すことが多い

# Docker buildコマンド
- 何度も使う場合があったり、buildしているdocker imageの実行元コマンドを忘れる場合があるのでシェルスクリプトとしてファイルを作成しておくと良い（シェルスクリプトもgitリポジトリに上がると見栄えが悪いのでREADMEなどでサンプルを書く程度にとどめておき、pushされないように.gitignoreに*.shなどと記載しておくと良い）
- サンプル：
    
    ```bash
    #!/bin/bash
    # docker build -t [イメージ名]:[タグ] .
    docker build -t my_ml_project:latest .
    
    ```

    ### build時のコマンド：
    - **`[イメージ名]`** は作成するDockerイメージの名前
    - **`[タグ]`** はイメージのバージョンやラベルを示すもので、例えば **`latest`** などを指定することが多い
    - **`.`** はDockerfileが存在する現在のディレクトリを指す
    - ※`-fオプション`を使用する場合
        - プロジェクトのディレクトリ内で、複数のDockerfileがある場合（例: **`Dockerfile.dev`**、**`Dockerfile.prod`** など）、**`f`** オプションを使ってどのDockerfileを使ってビルドするか指定することができる
            
            ```bash
            #!/bin/bash
            # docker build -f [Dockerfileのパス] -t [イメージ名]:[タグ] [ビルドコンテキストのパス]
            docker build -f path/to/Dockerfile -t my_ml_project:latest .
            
            ```
            
            - **`[Dockerfileのパス]`**: 使用するDockerfileへのパス。
            - **`[イメージ名]`**: 作成するDockerイメージの名前。
            - **`[タグ]`**: イメージのバージョンやラベル。
            - **`[ビルドコンテキストのパス]`**: Dockerイメージをビルドする際のコンテキスト（通常、Dockerfileがあるディレクトリやその親ディレクトリ）。
    - 作成したイメージは以下のコマンドで確認できる
        
        ```bash
        docker images
        ```
        
# Docker runコマンド
- 何度も使う場合があったり、runしているdocker containerの実行元コマンドを忘れる場合があるのでシェルスクリプトとしてファイルを作成しておくと良い（シェルスクリプトもgitリポジトリに上がると見栄えが悪いのでREADMEなどでサンプルを書く程度にとどめておき、pushされないように.gitignoreに*.shなどと記載しておくと良い）
- サンプル：
    - シンプルなバージョン
        
        ```bash
        #!/bin/bash
        # docker run [オプション] [イメージ名]:[タグ] [コマンド] [コマンドの引数...]
        docker run \
            --user root \ # ユーザを指定
            --name sample \ # runして立ち上げるコンテナ名を指定
            -it \ # -i -tオプションをまとめて書く書き方
            # -iはinteractive(対話型)という意味でコンテナ内で実行するシェルと標準出力をつなぐというオプション（このオプションをつけないとコンテナの中のシェルにコマンドを実行してもらうことができない状態になる）
            # -tはtty (端末デバイス)という意味でコンテナ内にコンテナ用の疑似的なターミナルを用意するというオプション（このオプションをつけないとコンテナ内のターミナルでユーザー名やカレントディレクトリも出ていないし、タブによる入力補完なども機能しない状態になる）
            my_ml_project:latest \ # runするコンテナの名前:タグを指定
            /bin/bash #　こちらのオプションをつけるとコンテナ内でターミナル操作可能（バックグラウンドでスクリプト実行したい場合はつけなくて良い）
        
        ```
        
    - 便利オプションを追加したバージョン
        
        ```bash
        #!/bin/bash
        # docker run [オプション] [イメージ名]:[タグ] [コマンド] [コマンドの引数...]
        docker run \
            --rm \ # コンテナを停止した際に削除するオプション
          　　　　--p 8080:8080 # ポートを指定（ホストポート：コンテナポート）　\
            --user root \ # ユーザを指定
            --gpus all \ # 使用するGPU群を指定
            --name sample \ # runして立ち上げるコンテナ名を指定
            -it \ # -i -tオプションをまとめて書く書き方
                # -iはinteractive(対話型)という意味でコンテナ内で実行するシェルと標準出力をつなぐというオプション（このオプションをつけないとコンテナの中のシェルにコマンドを実行してもらうことができない状態になる）
                # -tはtty (端末デバイス)という意味でコンテナ内にコンテナ用の疑似的なターミナルを用意するというオプション（このオプションをつけないとコンテナ内のターミナルでユーザー名やカレントディレクトリも出ていないし、タブによる入力補完なども機能しない状態になる）
            -v /workspace/:/workspace/ \ # ボリュームマウント（ホストPCとコンテナで共有したいディレクトをフルパスで記載）
            -w /workspace/src/ \ # コンテナのターミナルを開く際、初期位置となるディレクトリをコンテナの方のフルパスで指定
            my_ml_project:latest \ # runするコンテナの名前:タグを指定
            /bin/bash #　こちらのオプションをつけるとコンテナ内でターミナル操作可能（バックグラウンドでスクリプト実行したい場合はつけなくて良い）
        
        ```
        
    
    ### run時のコマンド：
    
    - **`[イメージ名]`**: 実行したいDockerイメージの名前。
    - **`[タグ]`**: イメージのバージョンやラベル。指定しない場合、デフォルトで **`latest`** タグが使用されます。
    - **`[コマンド]`** および **`[コマンドの引数...]`**: イメージ内で実行するコマンドとその引数。これを省略すると、Dockerfileの **`CMD`** 命令で指定されたコマンドが実行されます。
    
    ### **一般的なオプション：**
    
    - `-d`: デタッチモード。コンテナをバックグラウンドで実行します。
    - `--name [コンテナ名]`: コンテナに名前を付けます。
    - `-p [ホストのポート]:[コンテナのポート]`: ポートマッピング。ホストとコンテナのポートをマッピングします。
    - `-v [ホストのディレクトリ]:[コンテナのディレクトリ]`: ボリュームマッピング。ホストとコンテナのディレクトリをマッピングします。
    - `-e [変数名]=[値]**`: 環境変数をセットします。
    - `--rm`: コンテナが停止した後に自動的に削除します。


# その他
- **その他**
    - Docker Desktop有償化について：
        - Win, MacでDockerを使おうとする場合、Docker Desktopが必要となり、従業員250以上で年間売り上げ1000万ドル以上の組織で商用利用するケースに該当するので有償化する必要があるので注意
        - vscodeの拡張機能 remote containerを使いmacからlinuxマシンにリモート接続する場合もおそらくDocker Desktopが必要になる
        - しかし以下のケースではDocker DesktopなしにDockerが使用できるので問題ない
            - WSL経由でdockerを利用する場合
            - linuxマシンでdockerを利用する場合
    
      
# 参考
- [Docker初学者がやるべきこと3選 - Qiita](https://qiita.com/nuco_fn/items/22cf85a2646d96361d0b?utm_source=Qiita%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9&utm_campaign=fa771a6b4d-Qiita_newsletter_547_12_28_2022&utm_medium=email&utm_term=0_e44feaa081-fa771a6b4d-33674289)
- [docker runの[ i, t, d ]オプションの意味を調べた - Qiita](https://qiita.com/tacotopas/items/93dfcaa9f4dd6cfe0b95)
- [Docker Desktop、無料で使える猶予期間が終了　従業員数250人以上、年間売り上げ1000万ドル以上の組織は有料に](https://www.itmedia.co.jp/news/articles/2201/31/news096.html)
        
- [【プログラミング】docker desktopの有償回避方法とその代替案 - 日々ブログ](https://xinformation.hatenadiary.com/entry/2022/10/15/060000)