Perl Hackers Hub

第34回DockerによるPerlのWebアプリケーション開発(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは、普段ははてなでPerlで書かれたWebサービスを運用している@y_uuk1こと坪内佑樹さんで、テーマはDockerによるPerlのWebアプリケーション開発」です。Dockerを用いてPerlのWebアプリケーション開発環境を構築するメリットやノウハウを紹介していきます。

本稿のサンプルコードは、WEB+DB PRESS Vol.88のサポートサイトから入手できます。

Dockerとは何か

本題であるPerlのWebアプリケーションとDockerの話に入る前に、Dockerを初めて触る人のために、Dockerそのものについて簡単に紹介します[1]⁠。

最近はDockerの話を頻繁に見聞きする一方、⁠結局、何を目的として、どのような方法で、何をするものなのかがよくわからない」という話も耳にします。⁠Dockerはコンテナ仮想化技術の一つである」という説明を聞いたことがあると思いますが、これは、⁠何を目的として」⁠どのような方法で」⁠何をするものなのか」という3つの疑問のうち、⁠どのような方法で」の一部を答えたものになります。3つの疑問すべてへの答えは、⁠Dockerは、あらゆる環境でアプリケーションを実行することを目的として、コンテナ仮想化技術を用いてアプリケーションの実行環境を構築し、配布するためのプラットフォームである」というのが筆者の理解です。

あらゆる環境でアプリケーションを実行する難しさ

あらゆる環境でアプリケーションを実行するのは、とても難しいことです。アプリケーションは、たいていさまざまなソフトウェアに依存します。動作させたいアプリケーションが依存しているソフトウェアが、さらに別のソフトウェアに依存していることもあります。これは「依存地獄」とも呼ばれます。

Web開発の現場でよく起こる具体的なケースを考えてみましょう。MySQLを扱うためのCPANモジュールであるDBD::mysqlを、Cartonでインストールすることを考えます。Cartonは宮川達彦さん作のCPANモジュールの依存関係管理ツールです。

DBD::mysql自体のバージョンはCartonが管理します。しかし、実際にはDBD::mysqlはMySQLのクライアントライブラリであるlibmysqlclientに依存します。libmysqlclientはCPANモジュールではないため、Cartonではインストールできません。libmysqlclientをインストールするためには、yumやaptなどのパッケージマネージャを利用するか、ソースコードからビルドすることになるでしょう。パッケージマネージャを利用する場合も、使用したいバージョンまたはディストリビューションのパッケージが配布されていないことがあり、自前でパッケージをビルドすることもあります。ソースコードからビルドするにせよ、パッケージを自前でビルドするにせよ、libmysqlclientが依存しているソフトウェアをすべてそろえた環境でビルドしなければなりません。

このように、言語側で専用のモジュール管理のしくみを持っていたとしても、実際にはC言語で書かれたライブラリに依存することはよくあります。依存するソフトウェアが多く依存地獄に陥ると、目的の環境でアプリケーションを実行することが難しくなります。

コンテナ仮想化技術とは何か

Dockerは、コンテナ仮想化技術を利用して、同じOS上に複数のアプリケーションの実行環境を構築することを容易にしています。Docker自体がコンテナ仮想化技術であるというよりは、Dockerは手段としてコンテナ仮想化技術を用いているというのがより正確な表現でしょう。

Dockerはコンテナ仮想化技術のうち、Linuxコンテナを用いています。Linuxコンテナは、Linuxカーネルの名前空間の機能により、プロセスをグループ化して、コンテナという隔離された空間を作成します。名前空間とは、ホスト名、プロセスID、ユーザID、ネットワークインタフェース、ルーティングテーブルなどのカーネルが持つリソースを隔離するためのLinuxカーネルの機能です。つまりコンテナの実体はプロセスグループであり、ハードウェアをエミュレートするVMVirtual Machine仮想マシン)型の仮想化技術とはしくみが大きく異なります。

コンテナを用いることにより、アプリケーション単位で隔離された環境を作成できます。したがって、複数のアプリケーションが同じソフトウェアに依存することを避けやすくなり、依存地獄を解決できます。

コンテナ仮想化技術について詳しくは、gihyo.jpの連載LXCで学ぶコンテナ入門を参照してください。

アプリケーションの実行環境を構築し、配布する

Dockerはアプリケーションの実行環境を丸ごとイメージとして構築し、構築したイメージを配布できます。構築したイメージは、Dockerの動作している環境であればどこでも実行できます。

ポイントは、実行環境を丸ごとイメージとして構築しているところです。Perlの例を再び用いると、Cartonが管理するCPANモジュール以外に、CPANモジュールが依存しているC言語ライブラリなどの環境もまとめて1つのイメージに閉じ込めるという発想です。Dockerは、実行環境を丸ごとイメージとして構築するということと、先述したイメージをコンテナとして実行するという2点の工夫により、環境に依存せずにアプリケーションを実行できるようにしています。

さらに、構築したイメージを配布するためのプラットフォームとして、Docker Hubがあります。Docker HubにDockerイメージを登録しておくと、誰もがそのDockerイメージを再利用できます。Docker HubはWeb上に公開されたSaaSSoftware as a Service型のシステムです。プライベートな環境でDockerイメージを配布するためにはDistributionを用います。

Dockerの基本的な使い方

本節では、Dockerの具体的な使い方を紹介します。Dockerにはさまざまな機能がありますが、常用する機能はさほど多くありません。ここでは、筆者がよく使用する機能を重点的に解説します。

Dockerの使い方の流れ

Dockerの使い方の流れの基本は以下になります。

  • ① Dockerイメージの取得または構築
  • ② Dockerコンテナの起動
  • ③ Dockerコンテナの停止・削除

順に見ていきます。

Dockerイメージの取得

まず、Dockerイメージを取得する方法について説明します。

Docker Hubから構築済みのイメージを取得するには、docker pullコマンドを使います。たとえば次のコマンドにより、MySQLのイメージを取得します。

mysqlイメージをダウンロード
$ docker pull mysql

Dockerイメージの構築

次に、Dockerイメージを構築する方法について説明します。

dockerイメージを自前で構築するためには、Dockerfileを利用するのが簡単です。例として、nginxのDockerイメージを構築するためのDockerfileを示しますリスト1⁠。

リスト1 nginx用のDockerfile
FROM debian
RUN apt-get update -yq && \
    apt-get install -yq --force-yes nginx
RUN echo "\ndaemon off;" >> \
    /etc/nginx/nginx.conf
CMD ["nginx"]
EXPOSE 80

Dockerfileには、基本的にシェルコマンドを1行ずつ書くだけです。

まず、FROM命令にベースとなるイメージを指定します。リスト1では、Docker公式のDebian GNU/Linuxのイメージを指定しています。

次に、RUN命令にシェルコマンドを指定します。リスト1では、aptリポジトリの更新とnginxパッケージのインストールを行い、起動時にデーモン化しないようにするオプションをnginx.confに指定しています。Dockerを使用する場合はこのように、nginxのようなデーモンとして動作させるタイプのアプリケーションでもアプリケーション自体はデーモン化せずに、docker runのオプション引数として-dを指定することにより、Dockerコンテナをデーモン化します。

基本的にFROM命令とRUN命令の2つだけで、たいていのことはできます。

ほかにも、CMD命令には、docker runの引数のコマンドを省略したときのデフォルトのコマンドを指定しておけます。EXPOSE命令には、docker runのオプション引数-pを省略したときのデフォルトの公開ポート番号を指定できます。

なお、Docker公式ドキュメントのBest practices for writing DockerfilesにDockerfileの書き方のベストプラクティスが紹介されています。より詳細なDockerfileの書き方についてはこちらを参照してください。

Dockerイメージのビルド

DockerfileからDockerイメージをビルドするには、docker buildコマンドを用います。Dockerfileのパスと同一ディレクトリにて、次のコマンドを実行します。

nginxイメージのビルド
$ docker build -t mynginx .

-tオプションによりDockerイメージにタグ名を付けておくと、あとあと参照するために便利です。

Dockerfileのビルドキャッシュ

docker buildを実行するときに、Dockerfileの各命令を毎回最初から実行するのは時間がかかります。そこで、DockerfileによるDockerイメージのビルドには、デフォルトでキャッシュ機構が提供されています。

Dockerfileによるイメージのビルドでは、Dockerfileの各命令ごとに中間イメージが作成されます。そしてDockerイメージのビルドを高速化するために、中間イメージをキャッシュとして利用します。具体的には、前回のdocker buildの実行からDockerfileの内容が変化した部分を検出し、検出した命令の直前の命令までは、中間イメージを再利用してビルドをスキップします。

Dockerコンテナの起動

Dockerイメージの取得または構築ができたら、docker runコマンドにより、DockerイメージからDockerコンテナを起動します。先ほど作成したmynginxタグの付いたDockerイメージから、コンテナを起動してみましょう。

nginxコンテナの起動
$ docker run -d -p 8000:80 -t mynginx

先述したように、nginxのようなデーモンとして動作させるタイプのアプリケーションには-dオプションを付けます。また-pオプションにより、ホスト側とコンテナ側のポートのマッピングを指定します。-p8000:80の場合は、コンテナ側の80番ポートに対して、ホスト側の8000番ポートからアクセスできます。

curlコマンドやブラウザからlocalhostの8000番にアクセスして、ページが表示されれば成功です。

コンテナ内のnginxの起動確認
$ curl http://localhost:8000

Dockerコンテナの停止・削除

起動したDockerコンテナを停止するときは、docker stopコマンドまたはdocker killコマンドを使います。stopとkillでは、停止のためのシグナルが異なります。stopの場合は、まずSIGTERMシグナルを送ります。SIGTERMシグナルで停止しなければ、一定時間経過後にSIGKILLシグナルを送ります。killの場合は、最初からSIGKILLを送ります。安全にコンテナを停止させたければstop、すばやくコンテナを停止させたい場合はkillを使うとよいでしょう。

停止させる場合は、停止させるコンテナのIDを指定する必要があります。docker psコマンドにより、コンテナIDを含んだ起動中のコンテナの情報を一覧できます。

起動中のコンテナの一覧表示
$ docker ps
コンテナの停止
$ docker stop <コンテナID>
コンテナの強制停止
$ docker kill <コンテナID>

Dockerコンテナ内部にログインする方法

Dockerを用いて開発していると、Dockerコンテナ内部にログインしたいことがたびたびあります。docker execコマンドを使うと、起動中のコンテナの内部に入れます。この方法により、たとえばsshdなどの余分なサービスを起動せずに、手軽にコンテナにログインできます。

起動中のコンテナの内部に入る
$ docker exec -i -t <コンテナID> /bin/bash

<続きの(2)こちら

おすすめ記事

記事・ニュース一覧