オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

コンテナ・マイクロサービス

さわって理解するDocker入門

第2回 Dockerイメージのビルド
オージス総研
大西 洋平
2016年12月8日

本連載では、Dockerに興味はありつつも、まだ触ったことのない方向けに、実際に触ってDockerについて理解していただくための記事を提供します。第1回では、Dockerの概要および最も基本であるイメージとコンテナの管理について実際の操作方法をステップごとに紹介しました。第1回では、公開されているイメージを利用しましたが、自分で開発しているアプリケーションをDockerイメージとして配布するためには、自分でイメージを作成できる必要があります。そこで第2回の本記事では、自分でイメージを作成し、コンテナを起動する方法について紹介します。なお、本記事のサンプルは、Docker社が公開している初心者向けチュートリアルの内容をベースにしています。ご興味あれば、こちらも参照ください。

Dockerイメージビルドの仕組み

最初にDockerでイメージを作成する際の仕組みについて紹介します。

Dockerイメージのビルドとは、ベースとなるイメージに対して、何らかの機能を加えて、ユーザイメージ(自分独自のイメージ)を作り出すことを指します。

ビルドの仕組み

ベースイメージは、通常、Docker社またはDockerコミュニティが提供するOSのイメージになることが多いです。そのイメージに対して各種依存ライブラリやミドルウェアをインストールしたり、自分のアプリケーションをインストール・設定したりすることで、ユーザイメージを作ります。このユーザイメージを第3者に配布することで、ライブラリ・ミドルウェア・アプリケーションなどを全て包含した形で配布することが可能になります。

このとき、ベースイメージに対して実行する操作を記述する設定ファイルが「Dockerfile」です。Dockerfileは、Docker社によって記述方法が規定されています。詳細はドキュメントを参照ください。

サンプルイメージのビルド

本記事では、flaskというPython用のWebアプリケーションフレームワークを使ったWebアプリケーションを配布するためのDockerイメージを構築します。アプリケーションは、ランダムに猫のGIF画像を表示するというものです。なお、本記事では、flask自体の詳細な説明は割愛します。

サンプルアプリ

ビルドに必要なファイルの準備

ディレクトリを作成し、以下の手順でイメージのビルドに必要な4つのファイルを取得してください。

mkdir flask-app
cd flask-app
wget https://raw.githubusercontent.com/docker/labs/master/beginner/flask-app/app.py
wget https://raw.githubusercontent.com/docker/labs/master/beginner/flask-app/requirements.txt
wget https://raw.githubusercontent.com/docker/labs/master/beginner/flask-app/templates/index.html -P templates
wget https://raw.githubusercontent.com/docker/labs/master/beginner/flask-app/Dockerfile

次に、各ファイルの内容について紹介します。

app.pyはWebアプリケーションのエントリーポイントとなるソースコードです。Web上のGIF画像を取得して、ランダムにそのURLを返すという処理を行います。

from flask import Flask, render_template
import random

app = Flask(__name__)

# list of cat images
images = [
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26388-1381844103-11.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr01/15/9/anigif_enhanced-buzz-31540-1381844535-8.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26390-1381844163-18.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/10/anigif_enhanced-buzz-1376-1381846217-0.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr03/15/9/anigif_enhanced-buzz-3391-1381844336-26.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/10/anigif_enhanced-buzz-29111-1381845968-0.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr03/15/9/anigif_enhanced-buzz-3409-1381844582-13.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr02/15/9/anigif_enhanced-buzz-19667-1381844937-10.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26358-1381845043-13.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/9/anigif_enhanced-buzz-18774-1381844645-6.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/9/anigif_enhanced-buzz-25158-1381844793-0.gif",
    "https://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr03/15/10/anigif_enhanced-buzz-11980-1381846269-1.gif"
]

@app.route('/')
def index():
    url = random.choice(images)
    return render_template('index.html', url=url)

if __name__ == "__main__":
    app.run(host="0.0.0.0")

requirements.txtは、必要なPythonモジュール記載したファイルです。pip(パッケージマネジャー)を使い、flaskをインストールする際に利用します。

Flask==0.10.1

templates/index.htmlは、Webアプリケーションで出力するHTMLのテンプレートです。猫画像の表示のみ行います。

<html>
  <head>
    <style type="text/css">
      body {
        background: black;
        color: white;
      }
      div.container {
        max-width: 500px;
        margin: 100px auto;
        border: 20px solid white;
        padding: 10px;
        text-align: center;
      }
      h4 {
        text-transform: uppercase;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h4>Cat Gif of the day</h4>
      <img src="{{url}}" />
      <p><small>Courtesy: <a href="https://www.buzzfeed.com/copyranter/the-best-cat-gif-post-in-the-history-of-cat-gifs">Buzzfeed</a></small></p>
    </div>
  </body>
</html>

Dockerfileは、Dockerデーモンがイメージを作成する際に実行するコマンド群をまとめたファイルです。サンプルのDockerfileでは、以下の内容の処理を行っています。Dockerfileの文法については後述します。

  • ベースイメージとしてAlpine Linux(Docker用に開発されている軽量なLinuxディストリビューション)を指定する。
  • Pythonとpip(パッケージマネジャー)をインストールする。
  • pipでアプリに必要なPythonモジュールをインストールする。
  • app.pyとindex.htmlを所定の位置にコピーする。
  • ポート5000番を外部に公開する。
  • Web アプリケーションを実行する。
# our base imag
FROM alpine:latest

# Install python and pip
RUN apk add --update py-pip

# install Python modules needed by the Python app
COPY requirements.txt /usr/src/app/
RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt

# copy files required for the app to run
COPY app.py /usr/src/app/
COPY templates/index.html /usr/src/app/templates/

# tell the port number the container should expose
EXPOSE 5000

# run the application
CMD ["python", "/usr/src/app/app.py"]

(補足)Dockerfileで使われているコマンド

Dockerfileを見ると、一見、shellスクリプトのように見えますが、実際にはDockerfile特有のコマンドが使われています。サンプルのDockerfileで使われているコマンドは、以下の表に示すとおりです。文法の詳細はマニュアルに記載されていますので、参照ください。

コマンド 意味 補足
FROM ベースイメージとなるイメージを指定する
RUN イメージをビルドするためのコマンドを指定する 「RUN apk add –update py-pip」の場合、実際には「apk add –update py-pip」の部分が実行される
COPY ホストからコンテナ内にファイルをコピーする
EXPOSE 外部に公開するコンテナのポートを指定する デフォルトではコンテナのポートが閉じている
CMD イメージからコンテナを起動するときに実行するコマンドを指定する Dockerfile内でCMDは1個だけ指定できる

その他、Dockerfileの書き方のベストプラクティスについては「Best practices for writing Dockerfiles」にまとめられていますので、こちらも参照ください。

イメージのビルド

Dockerデーモンが起動していないと、ビルドもできません。事前にデーモンを起動してください。

カレントディレクトリにDockerfileがある状況で、以下のコマンドを実行して下さい。Dockerfileの内容が順に実行されていることがお分かりいただけると思います。

(注)サンプルにプロキシ設定は用意されていません。HTTPプロキシがない環境で実行してください。

$ docker build -t myfirstapp .
Sending build context to Docker daemon 8.192 kB
Step 1 : FROM alpine:latest
 ---> baa5d63471ea
Step 2 : RUN apk add --update py-pip
 ---> Using cache
 ---> e4c871e20ff7
Step 3 : RUN pip install --upgrade pip
 ---> Using cache
 ---> 8fcac0a6d7bb
Step 4 : COPY requirements.txt /usr/src/app/
 ---> Using cache
 ---> 873379a81443
Step 5 : RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt
 ---> Using cache
 ---> 1bd8d8164130
Step 6 : COPY app.py /usr/src/app/
 ---> Using cache
 ---> 4189ffe25189
Step 7 : COPY templates/index.html /usr/src/app/templates/
 ---> Using cache
 ---> 88d15ee40a45
Step 8 : EXPOSE 5000
 ---> Using cache
 ---> dfe12126ea1d
Step 9 : CMD python /usr/src/app/app.py
 ---> Using cache
 ---> c35d0a17de36
Successfully built c35d0a17de36

コンテナの起動

イメージのビルドが完了したら、以下のコマンドでイメージからコンテナを起動します。

docker run -p 8888:5000 --name myfirstapp myfirstapp
 * Running on https://0.0.0.0:5000/ (Press CTRL+C to quit)

上記のコマンドの場合、オプションにより、コンテナ内のポート5000番をホストの8888番に指定しています。Webブラウザを開き、https://localhost:8888 にアクセスしてください。冒頭で紹介したWebアプリケーションが表示されます。

コンテナの停止・削除

起動したコンテナを停止するには、以下のコマンドを実行してください。

docker stop myfirstapp

さらに、停止したコンテナを削除するには、以下のコマンドを実行してください。

docker rm myfirstapp

まとめ

第2回となる本記事では、具体的なサンプルアプリケーションを通して、Dockerfileを使ってイメージをビルドし、イメージからコンテナを起動する方法を紹介しました。今回の記事でイメージのビルドの基礎的な部分はカバーしているため、イメージビルドの概要は把握していただけたと思います。Dockerの公式ドキュメントは非常に充実しているので、ドキュメントも合わせて参照していただければ、様々なシーンにも対応できるはずです。

今回の記事では、ローカルでビルドし、ローカルでコンテナを起動する作業を行いましたが、自分以外の人にイメージを利用してもらうには、イメージを第3者が利用できる形で公開する必要があります。次回はイメージの公開方法についてご紹介します。