この記事はモバイルファクトリー Advent Calendar 2017の1日目の記事です。
@carimaticsが担当します。
本記事はDocker入門として、Docker上でPerlを動かすというお話です。
雰囲気くらいは掴めると思います。
Perlの入門ではないです。( carton
などは知ってるものとします)
開発環境
以下の環境で動作確認をしています。
- OS: macOS Sierra version 10.12.6
- Docker: Version 17.09.0-ce-mac35
- fish: version 2.7.0
Dockerのインストールについてはこの記事では扱いません。
必要であれば公式ガイドに従ってインストールしてください。
また、fishというシェルを使っていますが、bashやzshでも動作すると思います。
はじめに
本記事の内容
前述の通り、本記事はDocker上でPerlを動かすという内容です。
とは言うものの、それだけであればコマンド一発でおしまいです。(後で少し詳しく解説します)
$ docker run perl:5.26 perl -e 'print "Hello, Perl on Docker!\n"'
Hello, Perl on Docker!
これだけでは面白味に欠けるので、Webアプリケーションを動かしてみようと思います。
なぜDocker上でPerlを動かすのか
Dockerを使う利点はいくつかありますが、今回の目的はホスト環境をPerlで汚さないためです。
CPANモジュールを検証したい時などに使えます。
また、いつでもPerlの環境を捨てることができるので非常に良いです。
Docker入門
Dockerコンテナを作成、起動する( docker container run
or docker run
)
Dockerでは、Dockerコンテナという環境の中でアプリケーションが動きます。
ひとまず実行してみましょう。
定番のhello worldです。
$ docker container run perl:5.26 perl -e 'print "Hello, Perl on Docker!\n"'
Hello, Perl on Docker!
初回の実行では perl:5.26
というイメージをダウンロードする必要があるため、少し時間がかかります。
2回目以降の実行はほぼノータイムで実行できるはずです。
上記のコマンドで出力されたのは perl -e 'print "Hello, Perl on Docker!\n"'
の実行結果です。
perlの実行環境があれば、これだけで同様の出力結果を得ることができます。
docker run
コマンドでは、イメージからコンテナを作成し、そのコンテナ上でコマンドを実行します。
$ docker container run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
(後略)
先のhello worldでは、perl:5.26
のDockerイメージをベースにしたコンテナ上で perl
コマンドが実行されています。
perl:5.26
は、Dockerの公式イメージで、perl v5.26の環境などが含まれています。
コンテナ内のシェルを実行してみます。
$ docker container run -it perl:5.26 bash
root@a0e8801dd74d:~#
bash
コマンドを実行しました。
-it
オプションにより、pseudo-TTYからコンテナの標準入力にアクセスできます。
コマンドを実行した次の行には、pseudo-TTYのプロンプト( root@a0e8801dd74d:~#
のような感じの)が表示され、普段のシェルに対して行っているのと同様に、コンテナで起動している bash
に対して標準入力が行えます。
試しに幾つかコマンドを叩いてみましょう。
root@a0e8801dd74d:~# perl --version
This is perl 5, version 26, subversion 0 (v5.26.0) built for x86_64-linux
root@a0e8801dd74d:~# cpanm --version
cpanm (App::cpanminus) version 1.7043 (/usr/local/bin/cpanm)
perl version 5.026000 (/usr/local/bin/perl)
root@a0e8801dd74d:~# carton
bash: carton: command not found
root@a0e8801dd74d:~# ls /app
ls: cannot access '/app': No such file or directory
root@a0e8801dd74d:~# exit
exit
コマンドの実行結果は適宜省略しています。
コンテナには carton
コマンドや /app
ディレクトリは存在しないようです。
コンテナ一覧を見る( docker container ls
or docker ps
)
さて、 exit
コマンドでコンテナから抜けましたが、現在コンテナはどうなっているでしょう。
docker container ls
コマンドで実行中のコンテナ一覧を見ることができます。
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
実行中のコンテナは存在しないようです。
-a
オプションですべての状態のコンテナを見ることができます。
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0e8801dd74d perl:5.26 "bash" 2 minutes ago Exited (0) 2 minutes ago agitated_wiles
コンテナの状態( STATUS
)は Exited
になっています。
停止したコンテナを再起動する( docker container start
or docker start
)
Exited
のコンテナを再び起動するには docker container start
コマンドを使います。
$ docker container start a0e8801dd74d
a0e8801dd74d
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0e8801dd74d perl:5.26 "bash" 12 minutes ago Up 5 seconds agitated_wiles
docker container start
ではコンテナID( CONTAINER ID
)かコンテナ名 ( NAMES
)でコンテナを指定します。
コンテナIDやコンテナ名は適宜自身の環境のものと読み替えてください。
docker exec
コマンドで、実行中のコンテナに対してコマンドを実行できます。
コンテナID( CONTAINER ID
)かコンテナ名 ( NAMES
)でコンテナを指定します。
$ docker exec -it agitated_wiles bash
root@a0e8801dd74d:~# exit
exit
これまで触れませんでしたが、プロンプトに含まれる謎の文字列はコンテナIDです。
docker container run
すると、これまでのものとは別のコンテナが作成されます。
$ docker container run -it perl:5.26 bash
root@d10e3b0e5f55:~# exit
exit
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d10e3b0e5f55 perl:5.26 "bash" 8 seconds ago Exited (0) 4 seconds ago modest_mcnulty
a0e8801dd74d perl:5.26 "bash" 25 minutes ago Up 12 minutes agitated_wiles
コンテナを削除する( docker container rm
or docker rm
)
停止したコンテナは、 docker container rm
コマンドで削除できます。
$ docker container rm d10e3b0e5f55
d10e3b0e5f55
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0e8801dd74d perl:5.26 "bash" 28 minutes ago Up 15 minutes agitated_wiles
PerlのWebアプリケーションを構築する
コンテナ内に適当なディレクトリを作成して移動します。
$ docker exec -it a0e8801dd74d bash
root@a0e8801dd74d:~# mkdir /app
root@a0e8801dd74d:~# cd /app
Carton
と Amon2
をインストールします。
root@a0e8801dd74d:/app# cpanm Carton Amon2
--> Working on Carton
(中略)
Successfully installed Amon2-6.13
90 distributions installed
続いてAmon2アプリケーションのセットアップをします。
root@a0e8801dd74d:/app# amon2-setup.pl HelloWorld
-- Running flavor: Basic --
(中略)
Initialized empty Git repository in /app/HelloWorld/.git/
*** Please tell me who you are.
Run
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
to set your account's default identity.
Omit --global to set the identity only in this repository.
fatal: unable to auto-detect email address (got 'root@07ab9031e56d.(none)')
32768 at /usr/local/lib/perl5/site_perl/5.26.0/Amon2/Setup/VC/Git.pm line 21.
git
の設定をしていないので怒られました。
まぁ今回は無視して無事セットアップができたということにしましょう。
HelloWorld
ディレクトリが作成されているので、移動して carton install
します。
root@a0e8801dd74d:/app# cd HelloWorld
root@a0e8801dd74d:/app/HelloWorld# carton install
Installing modules using /app/HelloWorld/cpanfile
(中略)
125 distributions installed
Complete! Modules were installed into /app/HelloWorld/local
これで取り敢えずWebアプリケーションを動かすことができます。
では起動してみましょう。
root@a0e8801dd74d:/app/HelloWorld# carton exec -- perl script/helloworld-server
HelloWorld: http://127.0.0.1:5000/
起動しました!
http://127.0.0.1:5000/
で待ち受けているようですね。
ループバックアドレスなので外部からは接続できません。
Ctrl-c
で抜けた後、無理矢理バックグラウンドプロセスとして動かしてcurlでアクセスしてみます。
root@a0e8801dd74d:/app/HelloWorld# carton exec -- perl script/helloworld-server &
[1] 31559
root@07ab9031e56d:/app/HelloWorld# curl http://127.0.0.1:5000/
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>HelloWorld</title>
(後略)
どうやらHelloWorldが動いている気配がします。
しかしやっぱりブラウザで確認したいですね。
Webアプリケーションのプロセスを殺します。
root@a0e8801dd74d:/app/HelloWorld# pkill perl
[1]+ Exit 4 carton exec -- perl script/helloworld-server
手始めに cpanfile
を書き換えて carton install
します。
root@a0e8801dd74d:/app/HelloWorld# sed -i "2i requires 'IO::Interface::Simple';" cpanfile
root@a0e8801dd74d:/app/HelloWorld# carton install
続いて script/helloworld-server
を書き換えます。
root@a0e8801dd74d:/app/HelloWorld# sed -i '8i use IO::Interface::Simple;' script/helloworld-server
root@a0e8801dd74d:/app/HelloWorld# sed -i -E "s;('127\.0\.0\.1');IO::Interface::Simple->new('eth0')->address // \1;" script/helloworld-server
再びWebアプリケーションを起動してみましょう。
root@a0e8801dd74d:/app/HelloWorld# carton exec -- perl script/helloworld-server
HelloWorld: http://172.17.0.2:5000/
今度はループバックアドレスではなく、 http://172.17.0.2:5000/
で待ち受けています。
ホストのブラウザからWebアプリケーションを確認しましょう。
コンテナから抜け、以下のコマンドを叩きます。(コンテナIDは適宜読み替えてください)
root@a0e8801dd74d:/app/HelloWorld# exit
$ docker stop a0e8801dd74d
$ docker commit a0e8801dd74d amon2_helloworld
docker commit
で既存のコンテナから新しいイメージを作成します。
ここでは、ID a0e8801dd74d
のコンテナから amon2_helloworld
という名前のイメージを作成しました。
次に、作成したイメージを起動します。
$ docker run -p 5000:5000 -it amon2_helloworld bash
これまでと違い、今回は -p <host port>:<container port>
オプションがついています。
こうすることで、ホストのポートにアクセスしたとき、コンテナのポートにフォワーディングされます。
では、再びアプリケーションを実行します。
root@3acd4e32f229:~# cd /app/HelloWorld/
root@3acd4e32f229:/app/HelloWorld# carton exec -- perl script/helloworld-server
HelloWorld: http://172.17.0.2:5000/
ホストのブラウザから http://localhost:5000
にアクセスします。
実行できました!すばらしい!
Dockerfileを使ってWebアプリケーションを構築する
Dockerfile
を作成することで、上記でWebアプリケーション構築の際に実行した操作を繰り返し行なったり、手順を変更したりすることが容易になります。
では Dockerfile
を作りましょう。
ホスト側で適当にディレクトリを作り、 Dockerfile
という名前のファイルに以下のように記述します。
FROM perl:5.26
WORKDIR /app
RUN cpanm Carton Amon2 \
&& amon2-setup.pl HelloWorld || :
WORKDIR /app/HelloWorld
RUN sed -i "2i requires 'IO::Interface::Simple';" cpanfile \
&& carton install \
&& sed -i '8i use IO::Interface::Simple;' script/helloworld-server \
&& sed -i -E "s;('127\.0\.0\.1');IO::Interface::Simple->new('eth0')->address // \1;" script/helloworld-server
CMD ["carton", "exec", "--", "perl", "script/helloworld-server"]
これまでの作業で内容は理解できると思うので、説明は最低限だけにします。
&& amon2-setup.pl HelloWorld || :
この行では、 git
の設定がされていないために出るエラーを握り潰しています。
良い子はマネしないでくださいね。
CMD ["carton", "exec", "--", "perl", "script/helloworld-server"]
最後の行で、 docker run
したときに実行されるデフォルトのコマンドを指定しています。
この Dockerfile
があるディレクトリで、以下のコマンドを叩くと amon2_helloworld
というイメージを作成できます。
$ docker build -t amon2_helloworld .
docker run
を実行すると、構築作業で最後に起動したものと同じWebアプリケーションが起動します。
$ docker run -p 5000:5000 amon2_helloworld
HelloWorld: http://172.17.0.2:5000/
まとめ
本記事では、Dockerの基本的な操作を学びました。
また、Docker上でWebアプリケーションを構築し、実行できることを確認しました。
さらに、Dockerfileを利用することで、構築手順を残しておくことができます。
最初に想定していたよりもボリュームのある記事になってしまいました。
ここまで読んでいただきありがとうございます。
この記事で少しでもDockerに興味を持つ方が増えたならすごく嬉しいです。
モバイルファクトリー Advent Calendar 2017は、2日目も引き続き @carimatics が担当します。
よろしくお願いします。