Dockerチュートリアルを日本語で説明(第2章)
Dockerを勉強しようと思い、色々調べていたのですが、どうも自分にとって理解しやすい記事がなかったので、
公式のDockerのGetstaretedで勉強し始めました。
公式のなので英語で書かれています。 勉強しがてら、英語で困っている方向けに、Dockerチュートリアルを日本語で説明していこうと思います。
チュートリアルをまるまる翻訳したわけではなく、自分なりにわかりやすく解説しているつもりです!
- Dockerを勉強し始めたい人
- Dockerの勉強始めに良さそうなサイトを見つけれていない人
- DockerのGetstatedやりたいけど英語のせいで挫折してしまった人
第1章についてはDockerの概念、インストールの方法などについてのみ記載されているので、 ググれば色々出てくると思います。
今回はGetstartedのPart2を日本語でなるべくわかりやすく説明していきます。
公式のGetstartedのままでは面白くないので、この記事の付加価値として
- 日本語であること
- ローカルで作成したDockerイメージを実際に仮想マシンで実行している
の2つを用意しました。
Dockerの勉強頑張りましょう!
第2章の位置付け
Dockerでアプリケーション開発をおこなうときには次の3層構造を意識します。
第2章では最下位層のコンテナについて扱います。
アプリケーション開発するために必要な環境を
ローカルのPCに依存しない、ポータビリティ、軽量というDockerのメリットを発揮するために非常に重要なことなのでしっかりと理解しましょう。
Dockerfileでコンテナを定義する
Dokerfileは「コンテナ内の環境がどうなっているのか定義するためのファイル」です。
Dockerfile作成
mkdir docker_tutorial
作成したディレクトリに移動
cd docker_tutorial
Dockerfileを新規作成
touch Dockerfile
(Visual Studio Codeを使用しているならば、code Dockerfile
でも可)
以下のようなDockerfileを作成します。
# Use an official Python runtime as a parent image FROM python:2.7-slim # ワーキングディレクトリを /app に設定 WORKDIR /app # カレントディレクトリの中身を/appにあるコンテナにコピー COPY . /app # requirements.txtに記載されたパッケージをインストールする RUN pip install --trusted-host pypi.python.org -r requirements.txt # 外部からこのコンテナを使用できるようポート番号を80に設定する EXPOSE 80 # 環境変数を設定 ENV NAME World # コンテナがローンチされた時にapp.pyを実行する CMD ["python", "app.py"]
このDockerfileは「requirements.txt」と「app.py」の2つのファイルを参照しています。
この2つのファイルを同じディレクトリ(僕の場合、docker_tutorial/)に作成しましょう。
そしてコンテナにrequrements.txtに記載されたpythonライブラリをインストールしています。
これらのファイルはまず、DockerfileのCOPY
の行で使用します。
そしてHTTPを通じてapp.pyにアクセスすることが可能となっています(EXPOSE
でポート番号を80に設定したから)。
requirements.txt↓
Flask Redis
app.py↓
from flask import Flask from redis import Redis, RedisError import os import socket # Connect to Redis redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2) app = Flask(__name__) @app.route("/") def hello(): try: visits = redis.incr("counter") except RedisError: visits = "<i>cannot connect to Redis, counter disabled</i>" html = "<h3>Hello {name}!</h3>" \ "<b>Hostname:</b> {hostname}<br/>" \ "<b>Visits:</b> {visits}" return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits) if __name__ == "__main__": app.run(host='0.0.0.0', port=80)
これであなたのPCにpythonやFlaskなどのライブラリがインストールされていなくても、これらのシステムを動かす準備が整いました。
アプリケーションのビルド
まずはディレクトリ内にちゃんと先ほどのファイルがあること、 そしてチュートリアルのために作成したディレクトリのルートにいることを確認しましょう。 正しいければ、次のような結果になります。
$ ls Dockerfile app.py requirements.txt
次にbuild
コマンドによりDocker imageを作成します。
このとき--tag
オプションをつけることで名前をつけることができます(-t
でも可)。
$ docker build --tag=friendlyhello . ↓実行結果 Sending build context to Docker daemon 5.12kB Step 1/7 : FROM python:2.7-slim 2.7-slim: Pulling from library/python 177e7ef0df69: Pull complete f6b2167b8d5a: Pull complete 432b044db3f9: Pull complete 7356f8556c46: Pull complete Digest: sha256:df3ba9998242864e650d62d158685c3c7af8c4f3ae1545bdc38780473b0b6c55 Status: Downloaded newer image for python:2.7-slim ---> f090c78858fa Step 2/7 : WORKDIR /app ---> Running in de96301c1534 Removing intermediate container de96301c1534 ---> ac6bc917db3d Step 3/7 : COPY . /app ---> b8dc9d8ce04a Step 4/7 : RUN pip install --trusted-host pypi.python.org -r requirements.txt ---> Running in 602fa87b6cd4 Collecting Flask (from -r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB) Collecting Redis (from -r requirements.txt (line 2)) Downloading https://files.pythonhosted.org/packages/f5/00/5253aff5e747faf10d8ceb35fb5569b848cde2fdc13685d42fcf63118bbc/redis-3.0.1-py2.py3-none-any.whl (61kB) Collecting itsdangerous>=0.24 (from Flask->-r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl Collecting Jinja2>=2.10 (from Flask->-r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB) Collecting Werkzeug>=0.14 (from Flask->-r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB) Collecting click>=5.1 (from Flask->-r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB) Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->Flask->-r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/bc/3a/6bfd7b4b202fa33bdda8e4e3d3acc719f381fd730f9a0e7c5f34e845bd4d/MarkupSafe-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl Installing collected packages: itsdangerous, MarkupSafe, Jinja2, Werkzeug, click, Flask, Redis Successfully installed Flask-1.0.2 Jinja2-2.10 MarkupSafe-1.1.0 Redis-3.0.1 Werkzeug-0.14.1 click-7.0 itsdangerous-1.1.0 Removing intermediate container 602fa87b6cd4 ---> 51560c42984e Step 5/7 : EXPOSE 80 ---> Running in 3026d65723f9 Removing intermediate container 3026d65723f9 ---> ea78dabe5b44 Step 6/7 : ENV NAME World ---> Running in c9a8cdf73279 Removing intermediate container c9a8cdf73279 ---> b304b9575853 Step 7/7 : CMD ["python", "app.py"] ---> Running in f67c275a5d91 Removing intermediate container f67c275a5d91 ---> 0e961526a74d Successfully built 0e961526a74d Successfully tagged friendlyhello:latest
実行結果をみると、Step 1/7, Step 2/7, ..., Step 7/7 というようにDockerfileで記述した7つの命令が順に実行されている様子が伺えますね。
さきほどビルドしたimageはどこにあるかというと、 PCのローカルなDocker image レジストリにあります。 次の方法で確認しましょう。
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE friendlyhello latest 0e961526a74d 7 minutes ago 131MB hello-world latest fce289e99eb9 2 weeks ago 1.84kB python 2.7-slim f090c78858fa 2 weeks ago 120MB
アプリケーションの実行
アプリケーションを実行するためには、
ローカルのポート4000にコンテナのポート80を-p
オプションで割り当てる必要があります。
実際に次のコマンドで実行と割り当てをおこないます。
$ docker run -p 4000:80 friendlyhello 実行結果↓ * Serving Flask app "app" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
実行結果をみてください。
Running on http://0.0.0.0:80/
というように、Flaskサーバ(app.py)がアプリケーションをhttp://0.0.0.0:80/で実行していると書いてあります。
しかしながらアプリケーションはhttp://0.0.0.0:80/ではなく、 http://localhost:4000/で動いています。
これはアプリケーション実行時に$ docker run -p 4000:80 friendlyhello
というコマンドで実行したとき、
コンテナのポート80をローカルのポート4000に割り当てたためです。
実際に任意のブラウザでhttp://localhost:4000/にアクセスしてみましょう。
確認したらアプリケーションをCtrl+C
で停止します。
localhost
の部分をDocker MachineのIPアドレスに置き換えてください。
Docker MachineのIPアドレスの確認方法はdocker-machine ipです。
またWindowsだとCtrl+Cでコンテナを停止できないそう。 Windowにおけるコンテナの停止方法は以下のステップでおこなってください。
- Ctrl+C
docker container ls
で実行中のコンテナを確認。docker container stop
で停止したいコンテナを指定する。
またdetached mode
でバックグラウンドでアプリケーションを実行することもできます(-d
オプションを追加)。
$ docker run -d -p 4000:80 friendlyhello
バックグラウンド実行すれば、使用しているターミナルが占有されません。しかし占有されないということはCtrl+C
でアプリケーションを停止できません。
なのでバックグラウンド実行しているコンテナを
docker container stop <CONTAINER ID>
で停止します。
これをおこなうには、まず停止したいコンテナID(<CONTAINER ID>
)を知る必要があります。
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e6bc47bab323 friendlyhello "python app.py" 6 minutes ago Up 6 minutes 0.0.0.0:4000->80/tcp practical_meninsky
上記コマンドで「friendlyhello」というコンテナのIDはe6bc47bab323
であると確認できました。
※人によってそれぞれIDは異なります。
このIDを用いてバックグラウンド実行しているコンテナを停止しましょう。
$ docker container stop e6bc47bab323
作成したimageを他のマシンから使用できるようにする
Dockerの良さの1つにポータビリティがあります。 それを実感するために、先ほど作ったイメージをアップロードし、他の場所で実行してみましょう。
そのためには、レジストリにpushする方法を知る必要があります。
レジストリというのは、リポジトリの集合です。 そしてリポジトリはimageの集合です。 コードがすでにbuildされている点を除くと、GitHubのようなものだと気づくかもしれません。
まずアップロードの流れを簡単に示しておきます。
- Docker IDでログインする
- imageにタグ付けする(オプションだけどあったほうが好ましい)
- imageをpushする
まずはPCからDocker public レジストリにログインします。
$ docker login 実行結果↓ Authenticating with existing credentials... Login Succeeded
次にimageにタグ付けをします。
このタグ付けをすることで、ローカルにあるimageとレジストリ上のリポジトリを紐づけることができます。 タグ付けのためのコマンドの文法は次の通りです。
docker tag image username/repository:tag
- image:タグ付けしたいローカルにあるimageを指定
- username:自分の名前
- repository:意味がわかる名前に設定(今回ならば、DockerのGet startedをやっているので、get-startedにする)
- tag:意味がわかる名前に設定(今回ならば、DockerのGet startedのPart2をやっているので、part2にする)
実際にタグ付けします。
$ docker tag friendlyhello <username>/get-started:part2
docker imageを確認してみると、先ほどタグ付けした結果が反映されていることがわかります。
$ docker image ls 実行結果↓ REPOSITORY TAG IMAGE ID CREATED SIZE friendlyhello latest 0e961526a74d 2 hours ago 131MB tomoyamori/get-started part2 0e961526a74d 2 hours ago 131MB hello-world latest fce289e99eb9 2 weeks ago 1.84kB python 2.7-slim f090c78858fa 2 weeks ago 120MB
では実際にタグ付けしたimageをリポジトリにアップロードしてみましょう。
$ docker push <username>/get-started:part2 実行結果↓ The push refers to repository [docker.io/<username>/get-started] 911ba4e2d3cf: Pushed 331a11021f29: Pushed 3e1bb2e14d3e: Pushed af9628477752: Mounted from library/python f1bd403e5041: Mounted from library/python b7fcb2747224: Mounted from library/python 7b4e562e58dc: Mounted from library/python part2: digest: sha256:a2de3c9923055f58b20767edd4d9cddb3e68e57f11a6ffcdd8c9156eac9da35c size: 1787
これで先ほど作業していたローカル環境以外からもfriendlyhelloを使用できるはずです。
試しに違うマシンからやってみましょう。
ちなみに僕は、次の環境で試してみました。 仮想マシンではなくて異なる物理マシンでもできると思います。
- MacBook Pro 15inch OS Mojave
- 仮想ソフトウェア:VMWare Fusion
- 仮想マシンのOS:CentOS 7
CentOSへのDockerインストールは下記記事を参考にしました。
CentOSからdocker run -p 4000:80 <username>/get-started:part2
を実行すると以下のようになるはずです。
CentOSのマシンにはfriendlyhelloというimageはないのでpullによってダウンロードしている様子が伺えますね。
またCentOSのマシンのブラウザでhttp://localhost:4000にアクセスするとちゃんと動いていることが確認できました。
まとめ
今回はDockerチュートリアルとして、 Getstarted-Part2を日本語で説明しました。
内容をもう一度まとめておきます。
- 第2章の位置付け
- Dockerfileでコンテナを定義する
- アプリケーションのビルド
- アプリケーションの実行
- 作ったimage(アプリケーション)を他のマシンから使えるようにする
part3も勉強するので書きます。
2019/1/20更新
第3章(サービスについて)はこちらから!