MORITOMOMENT

MORITOMOMENT BLOG

プログラミング関連、アウトドア関連を中心に発信していきます!!

Dockerチュートリアルを日本語で説明(第2章)

Docker, getstarted, 日本語, japanese

Dockerを勉強しようと思い、色々調べていたのですが、どうも自分にとって理解しやすい記事がなかったので、

公式のDockerのGetstaretedで勉強し始めました。

docs.docker.com

公式のなので英語で書かれています。 勉強しがてら、英語で困っている方向けに、Dockerチュートリアルを日本語で説明していこうと思います。

チュートリアルをまるまる翻訳したわけではなく、自分なりにわかりやすく解説しているつもりです!

  • Dockerを勉強し始めたい人
  • Dockerの勉強始めに良さそうなサイトを見つけれていない人
  • DockerのGetstatedやりたいけど英語のせいで挫折してしまった人

第1章についてはDockerの概念、インストールの方法などについてのみ記載されているので、 ググれば色々出てくると思います。

今回はGetstartedのPart2を日本語でなるべくわかりやすく説明していきます。

公式のGetstartedのままでは面白くないので、この記事の付加価値として

  • 日本語であること
  • ローカルで作成したDockerイメージを実際に仮想マシンで実行している

の2つを用意しました。

Dockerの勉強頑張りましょう!

第2章の位置付け

Dockerでアプリケーション開発をおこなうときには次の3層構造を意識します。

f:id:moritomo7315:20190120132843p:plain

第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で停止します。

もしWindows7上でDocker Toolboxを使っている場合、先ほどの実行コマンドでは動きません。 localhostの部分をDocker MachineのIPアドレスに置き換えてください。 Docker MachineのIPアドレスの確認方法はdocker-machine ipです。

例:http://192.168.99.100:4000/

またWindowsだとCtrl+Cでコンテナを停止できないそう。 Windowにおけるコンテナの停止方法は以下のステップでおこなってください。
  1. Ctrl+C
  2. docker container lsで実行中のコンテナを確認。
  3. 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のようなものだと気づくかもしれません。

まずアップロードの流れを簡単に示しておきます。

  1. Docker IDでログインする
  2. imageにタグ付けする(オプションだけどあったほうが好ましい)
  3. 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を使用できるはずです。

試しに違うマシンからやってみましょう。

ちなみに僕は、次の環境で試してみました。 仮想マシンではなくて異なる物理マシンでもできると思います。

CentOSへのDockerインストールは下記記事を参考にしました。

qiita.com

CentOSからdocker run -p 4000:80 <username>/get-started:part2を実行すると以下のようになるはずです。

CentOSでもコンテナが使用できている様子

CentOSのマシンにはfriendlyhelloというimageはないのでpullによってダウンロードしている様子が伺えますね。

またCentOSのマシンのブラウザでhttp://localhost:4000にアクセスするとちゃんと動いていることが確認できました。

CentOS上でのlocalhost:4000の結果

まとめ

今回はDockerチュートリアルとして、 Getstarted-Part2を日本語で説明しました。

内容をもう一度まとめておきます。

  • 第2章の位置付け
  • Dockerfileでコンテナを定義する
  • アプリケーションのビルド
  • アプリケーションの実行
  • 作ったimage(アプリケーション)を他のマシンから使えるようにする

part3も勉強するので書きます。 2019/1/20更新

第3章(サービスについて)はこちらから!

moritomo7315.hatenablog.com