男は悩んだ
『Dockerは、奥が深くて何から始めれば良いのか分からない...』
『何個コンテナを作ってもできる気がしない...』
『そうだ!どうやって学べばいいかをできる人に聞こう!』
自分『あの...』
ツヨツヨエンジニアSさん『はい』
自分『Dockerってどう学んだら良いんですかね...?』
ツヨツヨエンジニアSさん『手を動かしましょう。コンテナを実際に自分で作るのが一番いいですね』
自分『なるほど!(分かってない)ありがとうございます!』
そして現在。。。
『色々たててみたけど、いまだに一からコンテナを作れない。。。皆どうやって作るんだ...?』
未だにコンテナを1から作れない問題は解決していませんが、いつか作れるようになった時のために、作れなかった頃の気持ちを忘れないようにこの記事を書こうと思います。
なぜ作れないのかを考える
- どこまで分かっていて、どこから分からないかが整理できていない(迷子)
- あまり、コマンドの意味が分かっていない(docker-composeが便利すぎ問題)
- 1から作ろうとすること自体がそもそもズレているかもしれない(理想高すぎ問題)
どうしたら作れるようになるんだろう(作れる人の気持ちになって考える)
- 手を付ける範囲の単位が細かい(コストの見積もりができている)
- 望む結果を得るのにどこまでコストがかかりそうか
- スタートから終わりまでの一連の流れ(コンテナライフサイクル)が頭に入っている
- 動くものを作ってから考えている(公式ドキュメントを活用している)
なんだかできそうな気がしてきた
『できる人ならどうするか...』これを念頭に置きながらもう一回トライしてみようと思う。
やっぱりできそうな気がしていただけだった。
1時間考えたけれど、DockerfileのFROMのFの字も書けなかった。
公式チュートリアルをする(一部飛ばし飛ばし)
参考
https://docs.docker.com/language/python/build-images/
Dockerfileの中身を分析する
# syntax=docker/dockerfile:1
# ↑のsyntax...はパーサ・ディレクティブというやつ
# ベースイメージにpython3.8, OSに軽量のdebianを指定
FROM python:3.8-slim-buster
# コンテナに入った時の最初のディレクトリを指定
WORKDIR /app
# ホストのディレクトリにあるファイルをコンテナのディレクトリにコピーする
COPY requirements.txt requirements.txt
# COPY した requirements.txt の中身を freeze で install
RUN pip3 install -r requirements.txt
# 現在のディレクトリにあるすべてのファイルを取得し、コンテナの中にコピーする
COPY . .
# コンテナを実行した際に、デフォルトで実行される
CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0"]
コンテナのイメージをDockerfileから作成する
# 現在のディレクトリにDockerfileがあるので、それを . で指定
$ docker build --tag python-docker .
作成したイメージを指定して、でタッチモードでコンテナの起動をする
$ docker run -d -p 8000:5000 python-docker
curlを使って8000番ポートにHTTPリクエストを送る
curl localhost:8000
8000番ポートからHTTPレスポンスが返ってくる
Hello, Docker!
ブラウザでも確認(chrome)
正常に動いているようだ
ここまでを一旦整理する
- コンテナは、作成から起動まで細かい粒度で操作する事ができる
- 故にコマンドを打つ数が多くなりがち
- Dockerfileを使うと、手打ちしていたコマンドをDockerfileの中で扱える
- とても便利。コンテナを動かすのが目的なら、Dockerfileで動く最小のコンテナを作った方がいいかも?
- アプリをコンテナ化しているというよりかは、アプリを作る一連の流れをコンテナ化しているというのがしっくりくるかもしれない。
- コンテナ側でポートを指定したり、必要なものをインストールしたり、作業するディレクトリを指定したり...
Composeを使う
docker-compose.dev.yml ファイルの中身
# composeのversionを指定
version: '3.8'
# 使うコンテナの単位を指定
services:
# コンテナに web という名前をつける
web:
# Dockerfileを使う場合は、build:としてDockerfielのあるディレクトリのpathを指定してあげる
build:
# Dockerfileは、カレントディレクトリ内にある
context: .
# ポートフォワーディングをする。(ホストの)8000番ポートから(コンテナの)5000番ポートに接続するように設定
ports:
- 8000:5000
# ホストPCのワークディレクトリにあるファイルのデータをコンテナのワークディレクトリにマウントする
volumes:
- ./:/app
# コンテナに mysqldb という名前を付ける
mysqldb:
# イメージを指定する(DockerHubから取ってくる)
image: mysql
# ポートフォワーディングをする。(ホストの)3306番ポートから(コンテナの)3306番ポートに接続するように設定
ports:
- 3306:3306
# MySQLの環境変数の設定をする
environment:
# MySQLのrootパスワードを決める
- MYSQL_ROOT_PASSWORD=p@ssw0rd1
# mysqlのデータを作ったvolumeにマウントする
volumes:
- mysql:/var/lib/mysql
- mysql_config:/etc/mysql
# 必要なvolumeを用意する
volumes:
# データ保存用
mysql:
# MySQLの構成用
mysql_config:
動かしている途中にエラーが発生した。
エラー文
services.web.build must be a string
原因は、build:の後のcontext:にインデントがついていなかったこと。
build:
context: .
ではなく...
build:
context: .
が正解!
コンテナが正常に動いているかを確認する
composeでコンテナの作成&起動をする
$ docker-compose -f docker-compose.dev.yml up --build
コンテナにHTTPリクエストを送る
curl http://localhost:8000/initdb
curl http://localhost:8000/widgets
空のデータが帰ってきたら成功
[]
コンテナが動いた!
なんだか、難しく考えすぎていたのかもしれない(実際難しいんですが...)
結局、自分で1から作ることはまだできなかったけれどDockerの素晴らしさを改めて感じられた良い体験だった。
この後、公式ドキュメントには、CI/CDを構築する所まであるけれど、整理の時間も欲しいからここで一旦終わっておく。
ここまでの流れを整理すると
- コマンドを一つずつ打って手動でコンテナを操作していた。
- 手打ちのコマンドをDockerfileにまとめることで、Dockerfileからコンテナを作成したら、すぐbuildしてコンテナを起動できるように
- Dockerfileとdocker-compose.ymlファイルを合わせることで、コンテナの作成と起動が一つのコマンドでできるように
今まで、docker-compose.ymlがしてくれていたことを自分でやってみて、Composeのありがたみをより感じる事ができた。
Docker最高!Docker楽しい!Docker is Very Good!!!
雑感
Dockerって素晴らしい。使えるようになりたいって思った。
知識が断片的に偏ると、自分のいる地点が迷子になるので全体像を先に把握するべきだと思った。
コンテナって『たてる』っていうけど、漢字は「建てる」と「立てる」のどっちなんだろう...?