Dockerとの和解：ChatGPTと共にDockerfileを作成する

## Dockerと和解し、新たな挑戦へ

**この記事の目的は、ChatGPTの便利な機能を活用しながら、簡単なDockerfileを作成することです。**

### この記事を書いた理由：NMR自身が技術書典を機にDockerに挑戦したから

先日行われた技術書典１５で「Dockerと和解せよ」という技術同人誌に出会いました。
それがきっかけでDockerと少しだけ和解できたため、今では簡単な開発環境構築に活用しています。

### 初心者にとってのDockerfile作成難易度は？

Dockerfileを書き、Dockerイメージをビルドすることは意外と簡単です。
そして今はChatGPTが色々優しく教えてくれます。
この記事を通して、ChatGPTのサポートを利用しながらDockerfileを作成し、基本的なDockerfileの記述方法を学んでいただければと思います。

### 対象読者

- LPIC, LinuC level1程度の基礎的なコマンドがわかる人（WindowsならPSコマンド）
- 自分の物理マシンにイチから開発環境を作ることはできる人
- Dockerについて調べたことがあるが自分でDockerfileを書いたことはない人
- それでもDockerfileをイチから書いてみたい人

まったくDockerについて知らない！という人は私の記事では珍紛漢紛かもしれません。

### 環境
- DockerDesktopがインストールされているWindows/Mac/Linux
Windowsの場合はDockerDesktopをインストールする前にWSL2をインストールしておく事をおすすめします。

これ以降DockerDesktopのインストールは済んでおり、コマンドプロンプト/ターミナル等で動作する状態にある前提で進めていきます。

### ゴール
DebianベースでPython3/Numpy/Matplotlibが入った開発コンテナを作成する。

## ChatGPTと共にDockerfileを作成

先にフォルダ構成を掲載します。

``` 
/Users/ユーザ名/docker-py
|--Dockerfile
```
パスのユーザ名など一部環境依存の依存がある記載は逐次読替えをお願いします。
フォルダ名は今後の作業に影響を与えないため自由です。

### ChatGPTの事前調教

ChatGPTくんは質問をすると完全な答えを出そうと努力します。
しかしそれは勉強中の私たちにとっては情報過多です。
また、いきなり答えを見ることは試行錯誤に繋がらないため記憶に定着しにくいです。
事前調教として、ChatGPTにはいきなり答えを出さないよう以下の文章で調教しておいてください。

``` ChatGPT:you
自分で作り上げた実感が欲しいから、初めから全て答えを出すのは慎んで欲しい。
```

### ChatGPTくん、Dockerfileの書き方を教えてくれ〜①

``` ChatGPT:you
例えばDebianに最新のPython3を入れて、ライブラリにはNumpyとMatplotlibを使いたい。
```

``` ChatGPT:ChatGPT
素晴らしいですね！DebianベースのDockerイメージに最新のPython 3をインストールし、NumpyとMatplotlibというライブラリを使用する環境を構築するという目標を持っています。このプロジェクトに取り組む際に考慮すべきいくつかの要点を挙げてみましょう。

ベースイメージの選択:
Debianベースのイメージを選択することが最初のステップです。DockerではさまざまなDebianイメージが提供されています。

Pythonのインストール:
DebianイメージにはデフォルトでPythonがインストールされていない可能性があります。Pythonをインストールする手順をDockerfileに記述する必要があります。

Pythonライブラリのインストール:
Pythonの環境が整ったら、NumpyとMatplotlibをインストールする必要があります。これは通常、pipコマンドを使用して行います。

作業ディレクトリとファイルのコピー:
Dockerコンテナ内で作業を行うディレクトリを設定し、必要なPythonスクリプトやデータファイルをコンテナ内にコピーします。

これらの要点に基づいて、Dockerfileの作成を進めることができます。それぞれのステップで何をする必要があるか、どの命令を使用するべきかを考えてみてください。途中で質問があれば、いつでも尋ねてくださいね。
```

なるほど、なんとなくこんな風に作るという話は聞いたことがあります。

### 補足：Dockerfileから作るコンテナイメージについて

- コンテナイメージはベースイメージを加工して作るもの
- Dockerfileは上から下へ１行ずつ解釈される

### ChatGPTくん、Dockerfileの書き方を教えてくれ〜②

なんとなく自分の持っているイメージと同じ答えが返ってきました。
ここでDockerの公式リファレンスを見てみましょう。
Dockerfileの記載に使える主要な命令が（非推奨）も合わせてなんと１８個もありました。

初心者の我々は「こんなにいっぱい読むのは辛い。もうDockerなんて使わなくていいか」と思ってしまいますよね。
そこでChatGPTくんに今回必要な命令だけ絞ってもらいましょう。

``` ChatGPT:you
この作業で必要なDockerfileの基本文法を一つずつ教えてくれる？
```

``` ChatGPT:ChatGPT

もちろんです。Dockerfileを作成する際に使われる基本的な文法や命令をいくつか紹介します。各命令の意味を理解することで、Dockerfileを自分で作成する際の指針になります。それぞれの命令は、Dockerfileにおける特定のステップや操作を指定するために使用されます。

FROM
用途: ベースイメージを指定します。例えば、FROM debian:latestは最新のDebianイメージをベースとして使用することを意味します。
例: FROM debian:latest

WORKDIR
用途: コンテナ内の作業ディレクトリを指定します。このディレクトリは、ビルド中の命令が実行される場所です。
例: WORKDIR /app

COPY
用途: ファイルやディレクトリをホストマシンからコンテナのファイルシステムにコピーします。
例: COPY . /app

RUN
用途: イメージビルド中にコマンドを実行します。例えば、パッケージのインストールや設定ファイルの編集などに使用されます。
例: RUN apt-get update && apt-get install -y python3

CMD
用途: コンテナが起動されたときに実行されるデフォルトのコマンドを指定します。Dockerfileには一つのCMD命令のみが有効です。
例: CMD ["python3", "script.py"]

これらの命令を組み合わせて、Dockerfileを作成します。各命令の目的や使用方法を理解することで、Dockerfileの作成過程がより明確になります。特定の目標に合わせてこれらの命令を適用する方法を考えてみてください。途中で疑問があれば、質問してくださいね。
```

５つとだいぶ絞ってくれました。

さて、FROMはDebianベースイメージを選択するために、RUNはPython3やNumpyのインストールに使うことが今までの回答からわかります。
しかし、WORKDIRやCOPY、CMDの３命令は今回必須なのでしょうか？
これについても聞いてみましょう。
最初に飛び越えるハードルは低いほうがいいに決まっています。

``` ChatGPT:you
今回の場合WORKDIRとCOPYは必要かな？
今回の用途を教えて欲しい。
```

（ChatGPTから３命令のユースケースが回答されましたが関係ないため略）

``` ChatGPT:you
なるほど、ただPython３とライブラリを入れるだけでいいなら使わなくてもいいかな？
```

``` ChatGPT:ChatGPT
その通りです。もし目的が単にDebianベースのDockerイメージにPython 3と特定のライブラリ（NumpyとMatplotlib）をインストールするだけであれば、WORKDIRやCOPY命令は必ずしも必要ではありません。これらの命令は、特定の作業ディレクトリを設定したり、ホストマシンからコンテナにファイルをコピーする必要がある場合に使用します。

Pythonとライブラリのインストールのみを目的とする場合、FROM、RUN、およびCMD（必要に応じて）といった基本的な命令に集中することになります。具体的には、以下のようになります：

FROM: Debianベースのイメージを選択します。
RUN: Python 3と必要なライブラリ（NumpyとMatplotlib）をインストールするための命令を記述します。
このシンプルなアプローチは、特定のPythonスクリプトを実行するための追加ファイルがない場合や、コンテナ内の特定の作業ディレクトリを設定する必要がない場合に適しています
```

どうやらFROMとRUNだけで作りたいコンテナイメージは作れるようです。
まずは想像で書いてみましょう。
バージョンなどは特に気にせず、とりあえずで書いてしまいます。
練習ですからね。

``` vi:Dockerfile
FROM debian:latest

RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip install numpy matplotlib
```

ちなみにこのDockerfileでビルドしようとするとエラーを吐かれてビルドできません。
理由について知りたい方は下記ブログに詳しい記載がありましたのでご参照いただければと思います。
この記事では（私がまずきちんと理解できていないので）とりあえずの解決方法としてオプションに「--break-system-packages」をこの後採用することになります。

https://blog.jp.square-enix.com/iteng-blog/posts/00043-play-with-the-pep668/

### 補足：コンテナイメージ作成時のレイヤーについて
RUN命令はなるべくまとめる。というのがベストプラクティスです。
そうすることでレイヤ数を抑えられ、キャッシュも有効活用できる。
とのことですが、私がまだ解説できるレベルにないため、へ〜そうなんだ〜くらいで思っておいてください。
今回は可読性のためRUNを2つにわけています。

### ChatGPTくん、Dockerfileをビルドしてくれ〜

（こんな感じでどう？Dockerfileうまく書けてる？というやりとりがあった後）

``` ChatGPT:you
この後はどうしたらいい？
```

``` ChatGPT:ChatGPT
素晴らしいです！Dockerfileの基本的な構造は良い形になっています。次に進む前に、いくつかの小さな調整が必要です。そして、その後のステップについて説明します。

Dockerfileの調整
python3-pipのインストール:
Debianイメージにはデフォルトでpipが含まれていないため、pipもインストールする必要があります。これにはpython3-pipパッケージを追加します。

Dockerfileは次のようになります：

Dockerfile
FROM debian:latest

RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip install numpy matplotlib

次のステップ
Dockerfileが準備できたら、次はDockerイメージのビルドとコンテナの実行に進みます。

イメージのビルド:
Dockerfileが存在するディレクトリで、以下のコマンドを実行してDockerイメージをビルドします。イメージには適当な名前（例：my_python_app）を付けます。

docker build -t my_python_app .

コンテナの実行:
ビルドされたイメージからコンテナを実行します。このコンテナはPythonと必要なライブラリがインストールされた環境を提供します。

コンテナを実行するための基本的なコマンドは以下の通りです：

docker run -it my_python_app

このコマンドは、my_python_appという名前のイメージを使用してコンテナを起動します。-itオプションは、インタラクティブなターミナルセッションを提供します。

これらのステップにより、DebianベースのDockerコンテナでPython環境を使用することができます。何か不明点や追加で知りたいことがあれば、お気軽にお尋ねください。
```

ChatGPTくんのコマンドが何を行っているかは以下になります。

``` docker:docker　build -t　名前：タグ名 PATH ｜　URL
-t はコンテナイメージに名前：タグ名をつけます。
小文字で入力する必要があります。タグ名はオプションです。
. はPATHを指しています。
dockerは指定されたPATHもしくはURLにある一連のファイルを参照します。
```
``` docker:docker　run -it　コンテナイメージ名
-it は標準入力を開きtty(端末デバイス)を使う事を意味します。
-it なしで起動した場合バックグラウンドでコンテナが起動している状態になり、ターミナルからすぐにコマンド入力ができません。
その場合手っ取り早い解決方法として改めてdocker run -it コンテナイメージ名しましょう。
```
より正確な情報はdocker docsで得ることができます。
https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/commandline/build/
https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/commandline/run/

### Let's docker build & run

``` bash:bash
docker build -t my_python_app .
```

はいここでエラーがでたはずです。
エラー理由については前述している通りです。

このエラーは記事にするにあたりなくすこともできました。
残した理由としては「ChatGPTくんがいくら賢いといってもまだまだ人の手が必要」ということを伝えたかったからです。
ChatGPTくんが完璧にできなかったことは私たちで調べましょう。

では修正していきます。

``` Docker:Dockerfile
FROM debian:latest

RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip install --break-system-packages numpy matplotlib
```

pip installのオプションとして「--break-system-packages」を追加しました。

そして再度
``` bash:bash
docker build -t my_python_app .
```

今度は正常にコンテナイメージが作成できたはずです。
作成できているかどうかの確認には
``` bash:bash
docker images
```
を使用することで、作成したコンテナイメージの一覧をみることができます。
my_python_appがあることを確認してください。

そしていよいよコンテナイメージの起動です。

``` bash:bash
docker run -it my_python_app
```

起動したら以下のように動作確認してみましょう。

``` bash:bash
root@3ee08bae079d:/# python3
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> np.exp(10)
22026.465794806718
>>> plt.plot([1,2,10,18,19,20])
[<matplotlib.lines.Line2D object at 0x7f34263b9690>]
>>> plt.show()
```

きちんとPython3、Numpy、Matplotlibが入っていることが確認できました。

お疲れ様でした。
ここでDockerとの和解：ChatGPTと共にDockerfileを作成するは終了となります。

## 書いてみた感想

初めてアドベントカレンダーに参加した経験を振り返ってみると、0時の締切には間に合わなかったものの、最後まで書ききることができたのは良い経験だったと思います。

このプロセスを通じて、人に何かを説明する際には、どれほど正確な知識が必要かを改めて感じました。特に、根本的な解決を見つけられなかったエラーや、最終的にドキュメントに丸投げした部分については、学習を行いたいと思います。

また、ChatGPTは非常に便利なツールですが、人間の役割がまだ大いにあることも、この記事の作成を通じて実感しました。ChatGPTを効果的に使用するためにも、基礎知識をしっかりと身に付けていきたいと思います。

## 参考文献

https://booth.pm/ja/items/5257257

## 最後に

実はMatplotlibのグラフが表示されなかったですよね。
次はこのグラフを表示できるようにDockerfileを編集してみてください！