Dockerでflaskを起動させてみた

Ubuntu20.04のDocker上でFlaskを動作させる勉強をしていて、やっと何とか動作させることができたので、今後の参考のためにメモを残します。

#flaskのpythonスクリプトの作成

まずはflaskのpythonスクリプトを作成します。

```python:hello.py
from flask import Flask

app = Flask(__name__)
@app.route('/')
def hello():
	return '''\
<html>
<body>
Hello
</body>
</html>
'''

if __name__ == '__main__':
	app.run(host='0.0.0.0',port=8080)
```

#元となるイメージファイルの検索とダウンロード

Dockerの公式イメージの中で、pythonが入っているイメージを検索します。

```bash:コマンド
docker search -f is-official=true python
```
```:出力結果
NAME      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
python    Python is an interpreted, interactive, objec…   6263      [OK]       
django    Django is a free web application framework, …   1081      [OK]       
pypy      PyPy is a fast, compliant alternative implem…   279       [OK]       
hylang    Hy is a Lisp dialect that translates express…   33        [OK]       
```

4つほど出てきましたが、今回は1番上にあるNAMEが`python`のイメージを使うことにします。下のコマンドで`python`イメージをローカルにダウンロードします。

```bash:
docker image pull python
```

ダウンロードしたイメージの環境を`bash`で確認します。

```bash:
docker container run --rm -it python bash
```

環境の確認が終わればイメージを削除します。

```bash:
docker image rm python
```


#Dockerfileの作成

下のDockerfileを作成します。ファイル名が`Dockerfile`です。

```dockerfile:Dockerfile
From python:latest
COPY ./hello.py /hello.py
RUN pip install flask
CMD ["python","/hello.py"]
```

`From`には元となるDockerイメージを記載します。（上で決めた`python`が書かれています。）バージョンも指定できますが今回は最新版`latest`にしています。
`COPY`はホストにある`./hello.py`を、作成中のDockerイメージ内に`/hello.py`としてコピーを行います。
`RUN`はコマンドを実行します。作成中のDocker内で`pip install flask`が実行されます。
`CMD`はビルドしてコンテナが実行された後、実行されるコマンドが書かれています。

#イメージの作成

先に作成した`hello.py`と`Dockerfile`を同じフォルダに入れ、そのフォルダ内で下のコマンドを実行します。

```:ファイル構成
.
├── Dockerfile
└── hello.py
```

```bash:コマンド
docker image build ./ -t test
```

`-t`オプションは作成するイメージの名前を決めるオプションで、今回は`test`という名前にしてあります。
ビルドの途中では`WARNING: Running pip as root will break packages and permissions.`が出ますが、「rootでのインストール禁止」との警告らしいので（多分？）問題はありません。作成したイメージを確認するには、下のコマンドを使います。

```bash:コマンド
docker image ls
```
```:出力結果
REPOSITORY   TAG       IMAGE ID       CREATED              SIZE
test         latest    48395245e149   About a minute ago   896MB
python       latest    9b0d330dfd02   About an hour ago    886MB
```

今回作成したイメージ`test`と、元イメージである`python`があるのがわかると思います。

#コンテナを起動と停止

作成したイメージ`test`からコンテナ`test1`を作成し起動します。

```bash:
docker container run --rm -p 8080:8080 --name=test1 test
```

オプション`-p`でホストのTCP8080をコンテナのTCP8080にポートフォワードしています。
なお、Ubuntuの再起動後も自動的にコンテナを起動をさせたい場合には、下のコマンドにします。

```bash:
docker container run --restart=always -p 8080:8080 --name=test1 test
```

コンテナの状態を確認します。

```bash:コマンド
docker container ps -a
```

```:出力結果
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS                     PORTS                    NAMES
911093fb7b8f   test                   "python /hello.py"       28 seconds ago   Up 28 seconds              0.0.0.0:8080->8080/tcp   test1
```

コンテナ`test1`を停止します。

```bash:
docker container stop test1
```

#コンテナとイメージの削除

もうコンテナを使用しない場合は、コンテナを削除します。なお、コンテナの実行時に`-rm`オプションを付けていれば停止時に自動的に削除されます。

```bash:
docker container rm test1
```

イメージも使用しないのであれば、`test`と`python`のイメージも削除します。

```bash:
docker image rm test python
```

#追伸：イメージを再構築しても変更されたファイルが消えないようにする

上記の設定はコンテナ内のボリュームに全てのファイルを持たせるものです。もし元のイメージ`python:latest`が更新され、イメージ`test`を再構築した場合には（コンテナにデータを保存する設定になっていたとすれば）全データが初期化されます。それを防ぐためにコンテナ内の指定したフォルダにホストのフォルダをマウントするバインドと呼ばれる手法を記載します。なお、このバインドはDockerらしくないので、開発中だけの手法だと考えたほうがよいかもしれません。

バインドの設定にするには、まずファイル構成と`Docketfile`を下のように変更します。

```:ファイル構成
/home/test/Documents
├── Dockerfile
└── web
    └── hello.py
```

```Dockerfile:Dockerfile
From python:latest
RUN pip install flask
CMD ["python","/web/hello.py"]
```

下のコマンドを実行します。`source`はフルパスで記載する必要があります。

```bash:コマンド
docker container run --rm -p 8080:8080 --name=test1 --mount type=bind,source=/home/test/Documents/web,target=/web test
```

これで、コンテナのフォルダ`/web`にホストのフォルダ`/home/test/Documents/web`がマウントされました。コンテナ側で保存したいファイルがあれば、そのファイルを`/web`に書き込むことによりホストの`/home/test/Documents/web`に保存されるので、イメージの再構築を行っても消えることはなくなりました。