Part 2:コンテナ¶
必要条件¶
- Docker バージョン 1.13 以上のインストール
- Part1 の概要を読んでいること
- 以下のような簡単なコマンドを実行して、環境がセットアップできていることを確認します。
docker run hello-world
はじめに¶
Docker におけるアプリケーション構築を始めましょう。アプリケーション階層の最下部となるところから始めることにします。これがコンテナであり、このページにて取り扱います。このレベルの上位にあるのがサービスです。サービスは、アプリケーション実行中のコンテナの動きを定義します。このことは Part3 で扱います。最後に、最上位にあるのがスタックです。サービス間の対話方法を定義します。これは Part5 で扱います。
- スタック
Stack
- サービス
Services
- コンテナ(今ここにいます)
新しい開発環境¶
Python アプリケーションを書き始めるにあたり、自分のマシン上に Python ランタイムをインストールするのが、これまでは一番初めの仕事でした。しかし、サーバ上でもアプリケーションが期待する通りに問題なく動作するには、マシンと同じ環境を作成しなくてはいけません。
Docker であれば、移動可能な Python ランタイムをイメージ内に収容しているため、インストールは不要です。そして、ベース Python イメージにはアプリのコードも一緒に構築できますし、アプリを確実に動かすための依存関係やランタイムも全て運べます。
移動可能なイメージは Dockerfile
と呼ばれるモノで定義します。
Dockerfile
でコンテナの定義¶
Dockerfile
では、コンテナ内の環境で何をするかを定義します。ネットワーク・インターフェースとディスク・ドライバのようなリソースは、システム上の他の環境からは隔離された環境内に仮想化されています。このようなリソースに接続するには、ポートを外の世界にマッピング(割り当て)する必要がありますし、どのファイルを環境に「複製」(copy in)するか指定する必要もあります。しかしながら、これらの作業を Dockerfile
における構築時の定義で済ませておけば、どこで実行しても同じ挙動となります。
Dockerfile
¶
空ディレクトリを作成します。新しいディレクトリ内にディレクトリを変更( cd )し、 Dockerfile
という名前のファイルを作成し、以降の内容をファイルにコピー&ペーストし、保存します。なお、Dockerfile のコメントは、各命令文に対する説明です。
# 公式 Python ランタイムを親イメージとして使用
FROM python:2.7-slim
# 作業ディレクトリを /app に設定
WORKDIR /app
# 現在のディレクトリの内容を、コンテナ内の /app にコピー
ADD . /app
# requirements.txt で指定された必要なパッケージを全てインストール
RUN pip install -r requirements.txt
# ポート 80 番をコンテナの外の世界でも利用可能に
EXPOSE 80
# 環境変数の定義
ENV NAME World
# コンテナ起動時に app.py を実行
CMD ["python", "app.py"]
この Dockerfile
は、 app.py
と requirements.txt
といった、まだ作成していないファイルを参照しています。次はこれらを作りましょう。
アプリ自身¶
さらに2つのファイルを作成します。 requirements.txt
と app.py
です。これらを Dockerfile
と同じフォルダに入れます。アプリは見ての通り、極めて単純になります。先ほどの Dockerfile
でイメージの構築時、 Dockerfile
の ADD
命令で app.py
と requirements.txt
をイメージの中に組み込みます。
- requirements.txt
Flask
Redis
- app.py
先ほどの pip install -r requirements.txt
で Python 用の Flask と Redis ライブラリをインストールします。そして、アプリは環境変数 NAME
を表示し、また socket.gethostname()
を呼び出した結果も出力します。しかしながら、 Redis は実行できないため(Python ライブラリをインストールしただけであり、 Redis 自身は入っていません)、実行を試みても失敗し、エラーメッセージを表示するでしょう。
注釈
コンテナ内でホスト名の取得を試みると、コンテナ ID を返します。コンテナ ID は実行バイナリにおけるプロセス ID のようなものです。
以上です! システム上に Python や requirements.txt
に書かれているどれもが不要であり、それどころか、システム上にイメージの構築や実行も不要なのです。一見しますと環境に Python と Flask をインストールしていませんが、既に持っているのです。
アプリの構築¶
アプリを構築する準備が整いました。まだ、新しく作成したディレクトリのトップレベルにいるのを確認します。ここでは ls
は次のようになるでしょう。
$ ls
Dockerfile app.py requirements.txt
次は構築コマンドを実行します。これは Docker イメージを作成します。イメージには分かりやすい名前として -t
でタグを指定します。
docker build -t friendlyhello .
構築したイメージはどこにあるのでしょうか? マシン上のローカルにある Docker イメージ・レジストリの中です。
$ docker images
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398
アプリの実行¶
アプリの実行にあたり、マシン側のポート 4000 をコンテナの公開ポート 80 に割り当てるには -p
を使います。
docker run -p 4000:80 friendlyhello
Python がアプリに提供するのは http://0.0.0.0:80
であるのに注意して下さい。しかし、これはコンテナ内で表示されるメッセージであり、コンテナ内からはコンテナのポート 80 番からポート 4000 への割り当ては分かりません。適切な URL は http://localhost:4000
です。
ウェブブラウザで URL を開くと、「Hello World」文字列とコンテナ ID 、Redis エラーメッセージといった内容がウェブページに表示されます。
シェル上で curl
コマンドを実行しても、同じ内容を表示します。
$ curl http://localhost:4000
<h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
注釈
このポート 4000:80
の再割り当ては、 Dockerfile
の EXPOSE
での指定とは異なるポートを指定できるデモです。ここでは、 docker run -p
で何を公開( publish
)するかを指定しました。後の手順では、ホストのポート 80 をコンテナ内のポート 80 に割り当て、 http://localhost
で接続します。
ターミナル上で CTRL+C
を実行し、終了します。
次はアプリをバックグラウンドで動作するため、デタッチド・モード(detached mode)で実行しましょう。
docker run -d -p 4000:80 friendlyhello
コマンドを実行しますと、アプリの長いコンテナ ID を表示し、ターミナルに戻ります。コンテナはバックグラウンドで実行中です。なお、 docker container ls
で短縮コンテナ ID を確認できます(コマンド実行時は、長いコンテナ ID と短縮 ID のどちらも利用できます)。
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
このように http://localhost:4000
で表示したものと同じコンテナ ID ( CONTAINER ID
)が表示されます。
あとは、プロセスを停止するために docker stop
コマンドでコンテナ ID を次のように指定します。
docker stop 1fa4ab2cf395
Docker ID でログイン¶
Docker アカウントをお持ちでなければ、 cloud.docker.com でサインアップ(登録)します。そのとき、ユーザ名をお控えください。
自分のローカルマシンから Docker 公開レジストリにログインします。
docker login
イメージのタグ¶
ローカルのイメージとレジストリ上にあるリポジトリとを関連付ける概念は、 ユーザ名/リポジトリ:タグ
です。タグはオプションですが、指定が推奨されています。これは、レジストリにおける Docker イメージのバージョン指定の仕組みに使うためです。指定するのは get-started:part
のように、レポジトリ名と意味のあるタグ名です。こちらはイメージを get-started
リポジトリに、タグを part1
として送信します。
次はイメージにタグをつけます。 docker tag image
でユーザ名、リポジトリ、タグ名をしていすると、任意の場所へイメージをアップロードします。コマンドの構文は次の通りです。
docker tag image ユーザ名/リポジトリ:タグ
例:
docker tag friendlyhello john/get-started:part1
docker images で直近にタグ付けしたイメージを表示します。( docker image ls
でも同様です)
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest d9e555c53008 3 minutes ago 195MB
john/get-started part1 d9e555c53008 3 minutes ago 195MB
python 2.7-slim 1c7128a655f6 5 days ago 183MB
...
イメージの送信¶
タグ付けしたイメージをリポジトリにアップロードします。
docker push username/repository:tag
完了したら、アップロード結果が表示され、誰でも利用可能になります。 Docker Hub にログインしたら、pull コマンドで取得可能な新しいイメージが表示されます。
リモート・リポジトリにあるイメージの取得と実行¶
あとは docker run
コマンドをつかい、あらゆるマシン上でアプリを実行できます。
docker run -p 4000:80 username/repository:tag
もしもイメージがマシン上のローカルに存在しなければ、 Docker はリポジトリから取得します。
docker image rm <iイメージ ID>
$ docker run -p 4000:80 john/get-started:part1
Unable to find image 'john/get-started:part1' locally
part1: Pulling from orangesnap/get-started
10a267c67f42: Already exists
f68a39a6a5e4: Already exists
9beaffc0cf19: Already exists
3c1fe835fb6b: Already exists
4c9f1fa8fcb8: Already exists
ee7d8f576a14: Already exists
fbccdcced46e: Already exists
Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068
Status: Downloaded newer image for john/get-started:part1
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
注釈
各コマンドで :タグ
を指定しなければ、 :latest
タグが指定されたものとみなされます。これは build 時も run 時も同様です。Docker はイメージに対するタグの指定がなければ(直近のイメージであれば不要です)、最新版を使います。
どこで docker run
を実行したとしても、 Python と requirements.txt
で指定した全ての依存関係と実行するコードが入ったイメージをダウンロード(pull)します。整った小さなパッケージで全てを持ち運びできます。そして、ホストマシン上では Docker さえ実行できれば、何もインストールする必要はありません。
まとめとチート・シート(オプション)¶
このページで扱ったターミナルの録画 がこちらです。
こちらはこのページで扱った Docker の基本コマンドと関連コマンドです。次に進む前に、試してみてはいかがでしょうか。
docker build -t friendlyname . # このディレクトリ内にある DockerFile でイメージ作成
docker run -p 4000:80 friendlyname # "friendlyname" の実行にあたり、ポート 4000 を 80 に割り当て
docker run -d -p 4000:80 friendlyname # 同じですが、デタッチド・モード
docker container ls # 全ての実行中コンテナを表示
docker container ls -a # 停止中も含めて全てのコンテナを表示
docker container stop <hash> # 指定したコンテナを丁寧に停止
docker container kill <hash> # 指定したコンテナを強制シャットダウン
docker container rm <hash> # マシン上から指定したコンテナを削除
docker container rm $(docker container ls -a -q) # 全てのコンテナを削除
docker image ls -a # マシン上の全てのイメージを表示
docker image rm <image id> # マシン上の特定のイメージを削除
docker image rm $(docker image ls -a -q) # マシン上の全てのイメージを削除
docker login # CLI セッションで Docker の認証を行いログイン
docker tag <image> username/repository:tag # レジストリにアップロードする <image> にタグ付け
docker push username/repository:tag # タグ付けしたイメージを送信
docker run username/repository:tag # レジストリにあるイメージを実行
参考
- Get Started, Part 2: Containers | Docker Documentation
- https://docs.docker.com/get-started/part2/