DockerでGradioを使う

# Dockerは進撃の巨人
## <br>"Docker"

**それを耳にしたあるエンジニアはうつむき、あるエンジニアは目を輝かせそれに立ち向かう**
<br>
そんな**進撃の巨人**のような[Docker](https://knowledge.sakura.ad.jp/13265/)ですが、個人的にやりたいことができたので立ち向かいたい思います。
**※大げさな出だしですが記事の内容はしょぼいです**


# 利用目的
やりたいことは

**『[Magic-Animate](https://github.com/magic-research/magic-animate)などオープンソースのAIソフトをレンタルGPUで動かしたい！』**
です。

[Lambda](https://lambdalabs.com/)などのクラウドGPUサーバを数日借リて、AIツールの実行環境を作成したいのですが、いちいちレンタルの度にあれこれインストール作業をするのが面倒くさいので**Dockerで一発**でできたらなあと思っております。

※環境はWindows11にdockerデスクトップが入った状態で、dockerのバージョンは4.27.1です。

# Gradio
まず単純にDockerで[Gradio](https://aiacademy.jp/media/?p=3469)で作った簡単なアプリを動かすことから始めます。
※今回はこれしかやりません

GradioはWebアプリを簡単に作ることができるPythonのライブラリです。
元々は機械学習向けに開発され、最近では生成AIのデモなんかでよく使われています。
画像生成で一気に広まった[Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui)もGradioで開発されています。
今回は次の文字数をカウントするだけのアプリをDockerで動かすことを目標にします。

```python:Gradioインストール
pip install gradio
```
```python:app.py
import gradio as gr

def letter_count(text):

    return len(text)

demo = gr.Interface(fn=letter_count, inputs="text", outputs="text")

demo.launch()
```
```:出力結果
Running on local URL:  http://127.0.0.1:7860

To create a public link, set `share=True` in `launch()`.
```

<br>

**http://127.0.0.1:7860にアクセスすると起動する画面**
![スクリーンショット 2024-02-24 221710.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/eb832f12-9294-8a7c-4f15-f712b39fc264.png)
<br>
Gradioのコードの解説は省きますので、[こちらの方の記事を参照お願いします。](https://qiita.com/kzkymn/items/901ba79052190e428697)
ざっくり言うと関数を定義して、インプットを関数で処理してアウトプットにセットしているだけ。
今回ならインプットの文字から文字数を算出してアウトプットにセットしているだけです。

# Dockerファイル作成・起動

ではDockerファイルを作って、このapp.pyを起動してアプリを動かしてみます。
Dockerファイルはこちらの記事を参考にしました。

https://zenn.dev/optimisuke/articles/ebf22f7449a1ca

<br>

**フォルダ構成**
>gradio_test
>┠ Dockerfile
>┠ app.py
>┠ requirements.txt

※app.pyの内容は上記と同じです
```requirements.txt
gradio
```

```docker:Dockerfile
FROM python:3.11-slim

WORKDIR /workspace
ADD requirements.txt app.py /workspace/
RUN pip install -r requirements.txt

CMD ["python", "app.py"]
```

やっていることはpython:3.11-slimをベースイメージとして
・workspaceを作業フォルダに（フォルダも作成されます）
・手元にあるrequirements.txtとapp.pyをworkspaceへコピー
・requirements.txtをインストール
・app.pyをpythonで実行

これだけです。

で後はgradio_testに移動してビルド。"gr_test"としてイメージを作成します。

```powershell:powershell
docker build ./ -t gr_test
```

あとはこのイメージからコンテナを作成して起動（run）。
コンテナ名は"gr_test_01"にします。
```powershell:powershell
docker run -p 7860:7860 --name gr_test_01 gr_test
```
でこれでコンテナの起動は正常にするのですが、上手く行きません。
何も出力されないですし、localhostにアクセスしても何も表示されません。

**「いや、その前に"GradioをDockerで動かす"って記事見たならそれ通りやれよ」**
と思われると思うのですが、そちらも大変参考になったのですが、私の場合上手く行きませんでした。

結果的に以下を修正して動くようになりました。

```python:app.py（修正後）
import gradio as gr

def letter_count(text):

    return len(text)

demo = gr.Interface(fn=letter_count, inputs="text", outputs="text")

demo.launch(server_name="0.0.0.0") # server_name追加
```
```docker:Dockerfile
FROM python:3.11-slim

WORKDIR /workspace
ADD requirements.txt app.py /workspace/
RUN pip install -r requirements.txt

# 追加
ENV PYTHONUNBUFFERED 1

CMD ["python", "app.py"]
```

```:出力結果
 To create a public link, set `share=True` in `launch()`.
 Running on local URL:  http://0.0.0.0:7860

 To create a public link, set `share=True` in `launch()`.
 ```
 ※イメージの作成、コンテナの起動は同じ<br>
まずapp.pyの修正ですが、これは[参考にさせて頂いた記事の通りです。](https://zenn.dev/optimisuke/articles/ebf22f7449a1ca)
Docker上のアプリには **"0.0.0.0"で受けるようにしないといけない** ってことらしいっす。（「localhostが～」ってことらしいですが深追いはしません。[詳細はこちら](https://qiita.com/amuyikam/items/01a8c16e3ddbcc734a46)。）
ちなみに"server_port="でポートも指定できますが、今回はデフォルトの7860を利用したので追加しませんでした。<br>
次にDockerfileの修正ですが **"PYTHONUNBUFFERED 1"** を追加しています。
これは手詰まりになった際に **「DockerHubでサンプル探せばいいじゃん！」** と思い立ったところ、ほんとにおあつらえ向きのものが見つかり（しかもDockerfile入り）利用させて頂いた次第です。

https://hub.docker.com/r/tbhowe/gradio_dummy

PYTHONUNBUFFEREDは環境変数で空以外の場合（今回なら1）バッファリングされることなくターミナルに直接送信され、アプリケーションの出力がリアルタイムで確認できるようになります。

つまりバッファリングされていたため、実行時に何も出力されなかったようです。
で **「バッファリングって？」** ことになるのですが、これも深追いしません。（これから勉強します！）

# 補足
出力に **Running on local URL:  http://0.0.0.0:7860**
となりますが、このアドレスでは何も出てきません。
http://localhost:7860/
または
http://127.0.0.1:7860/
で起動しましょう。


# おわりに
まだ0歩目みたいなものですが、色々大変でした。
次はうってかわってどっかのGPUサーバを借りるところを記事にできたらと思います。