これから始める「DockerでかんたんLAMP環境 for CentOS」

昨年からImmutable Infrastructureという言葉が出始めて、サーバーを簡単に作って壊すという潮流が出始めました。また、サーバー管理を簡単にするツールとしてDockerというものが脚光を浴びており、いまどきのサーバー管理者に受け入れられてきました。
今回は、そのDockerをインストールしてLAMP環境を構築するステップを見ていきたいと思います。

Immutable Infrastructureとは?

IaaS型のクラウドやVPSの台頭によって、簡単に仮想サーバーが立ち上げられる環境が整ってきました。
とはいえ、サーバーの立ち上げをするのはシステム構築時と、システム規模を大きくする時に限られ、サーバーの動作を変えたり、アプリケーションのアップデートをするときは、稼働しているサーバーの中身を修正するというのが従来のやり方でした。
しかしImmutable Infrastructureの考え方では、一度作成したサーバーの中身は変更しないというのが原則で、アプリケーションのデプロイメントや、サーバー設定の修正などのたびに、新しいサーバー群を用意して毎回いちから構築を行い、前段にあるロードバランサーで”えいやっ”と丸ごと切り替えるという手順に変わります。
なので、本番環境とは全く別のテスト環境を作り、そのテスト環境をそのまま本番環境に切り替えすることができますし、切り替えした後で問題が発覚したとしてもすぐに元の環境へ切り戻しができます。

以下の例では、10/25にアプリケーションの修正を反映させたServer 4とServer 5という新しいサーバを用意し、1/15にはセキュリティパッチを適用させたServer 6という新しいサーバーを用意し、そして3/1にアプリサーバの設定変更と増設を行うために Server 7、Server 8、Server 9という新しいサーバを用意しています。
これらの切り替えはロードバランサーで行い、使わなくなったサーバーは一定期間経た後で削除してしまいます。

このように、何かサーバー内の変更をしたい時には、新しいサーバーを作って、古いサーバーは壊すというのが、Immutable Infrastructureの考え方です。

DockerとLXCについて

さて、ここまでImmutable Infrastructureの話をしてきましたが、簡単にサーバーを作ったり壊したりということを実現するためのソフトウェアとして最近注目されているのがDockerです。
Dockerは、LXC(Linux Container)というLinuxのコンテナ技術を用いて、サーバー上に独立したOS環境を作れるもので、OS環境を保存しておいたり、他のサーバーで稼働させたりと、柔軟なサーバー構築を実現できます。

ちなみに、コンテナ技術は仮想化技術と同列視されますが、KVMやXenなどの完全仮想化とは全く異なるものです。KVMやXenなどがハードウェアのエミュレーションを行ってホストとは異なるOS空間になっているのに対して、コンテナの場合は単一のOSを見かけ上分割しているだけで、ホスト側でpsを行うとゲストのプロセスも含めて全てのプロセスが表示されます。

Dockerでサーバーを起動すると、dockerの起動時に指定したlinuxのコマンドを新しいLXCコンテナの中で実行します。
Dockerにおいて別々に起動されたサーバーは、お互いのファイルシステムやプロセス空間にはアクセスできず、見かけ上仮想化されたような形となります。

CentOSにDockerをインストールする

それでは、CentOSにDockerをインストールしてみましょう。
なお、DockerのサポートはCentOS 6.5からですので、CentOS 6系でなければインストールができません。CentOS 6系であれば、6.5未満であってもyum updateをすれば問題ありません。

手順は簡単です。
yum update で最新バージョンにアップデートし、dockerのリポジトリをダウンロードして、yum installするだけです。

$ sudo yum update -y
$ sudo wget -P /etc/yum.repos.d http://www.hop5.in/yum/el6/hop5.repo
$ sudo yum install xz docker-io -y
$ sudo service docker start
Starting cgconfig service:                                 [  OK  ]
Starting docker:                                           [  OK  ]

これでインストールが完了しました。
とても簡単でしたね。

Dockerを使ってみる

それでは、さっそくDockerを使ってみましょう。
Dockerをインストールするとdockerというコマンドが作成されますので、このコマンドを使ってDockerを操作します。
起動するにはrunというオプションを付け、-tで使用するコンテナイメージのタグを指定します。
そして、その後ろのオプションで、コンテナ内で実行するコマンドを指定します。
以下の例では、bash経由で、ホスト名の取得、プロセス一覧、インターフェース一覧を表示しています。

$ sudo docker run -t centos /bin/bash -c "hostname && ps aux && ifconfig"
[sudo] password for tanaka:
Unable to find image 'centos' (tag: latest) locally
Pulling repository centos
539c0211cd76: Download complete
15cb2a03c788
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  11296  1296 ?        S    04:11   0:00 /bin/bash -c hostname && ps aux && ifconfig
root         8  0.0  0.0  13368  1064 ?        R    04:11   0:00 ps aux
eth0      Link encap:Ethernet  HWaddr 3E:AB:5B:67:F6:8E
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::3cab:5bff:fe67:f68e/64 Scope:Link
          UP BROADCAST  MTU:1500  Metric:1
          RX packets:1 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:90 (90.0 b)  TX bytes:90 (90.0 b)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
$

表示されたでしょうか?
上記のコマンドを実行すると、CentOSのコンテナイメージをダウンロードし、指定されたコマンドをコンテナ内で実行します。
ホスト名は 15cb2a03c788 で、プロセス一覧ではbashとpsの2つのプロセスが、ネットワークインターフェースのeth0には172.17.0.2が割り振られていることが分かります。
つまり、ホストOSとは別にコンテナが作成され、ホスト名も、プロセス空間も、ネットワークも、完全に別になっていることが分かりました。

現在、実行しているDockerのコンテナ一覧を表示するには、docker psを利用します。

$ sudo docker ps
CONTAINER ID  IMAGE    COMMAND   CREATED   STATUS   PORTS   NAMES

しかし、何も表示されませんね。

Dockerでは、指定されたコマンドを実行し終わると、コンテナを破棄します。
通常のLinuxであれば、プロセス番号1のプロセスは/sbin/initであり、デーモンプロセスの起動などを行い常駐しますが、先ほど起動したコンテナのps結果ではPIDが1のプロセスは/bin/bashでした。
Dockerの場合には引数に指定されたコマンドを最初のプロセスとして実行しますので、サーバーを起動するというよりコンテナ内でコマンドを実行すると言ったほうが適切です。
先ほどのコンテナも、/bin/bashの実行が終了した時点で破棄されました。

なお、Dockerで作成されたコンテナの中にファイルを書き込んだとしても、再度実行したときには無くなっていることを意味しており、実行したあとのコンテナを再度利用するには、後述するコミットを実行しなければなりません。

コンテナイメージをコミットする

先ほど実行したコンテナは破棄されましたが、docker psを実行するときに -aオプションを指定することで、過去に実行したコンテナの一覧を見ることができます。

$ sudo docker ps -a
CONTAINER ID IMAGE      COMMAND             CREATED      STATUS PORTS   NAMES
15cb2a03c788 centos:6.4 /bin/bash -c hostnam 13 minutes ago Exit 0     stic_alni

これを見ると、15cb2a03c788という先ほど実行していたコンテナが出てきました。
もう一度、このコンテナを使用するには、docker commitを実行します。

$ sudo docker commit 15cb2a03c788 centos:test
b8b6e3abbed608181cd2bebcf952bb6e47c072d1095a3784fa5b1343c1f01305

これで、先ほど実行したコンテナがイメージ化されました。

イメージの一覧は、docker imagesを実行することで表示されます。
testというタグでイメージが作成されました。

$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED         VIRTUAL SIZE
centos              test               b8b6e3abbed6        39 seconds ago      300.6 MB
centos              6.4                 539c0211cd76        11 months ago       300.6 MB
centos              latest              539c0211cd76        11 months ago       300.6 MB

作成したイメージから新たなDockerコンテナを起動するには、-t オプションの後ろにタグを指定します。
ちなみに、今回はbashを実行して実際にコンテナ内で作業をしてみます。この場合、-iオプションを付けて対話モードにしなければならないことに注意して下さい。

$ sudo docker run -i -t centos:test /bin/bash
bash-4.1#

これで、コンテナ内での作業が可能です。
コンテナを終了させるには、exitと打ち込むか^Dを押して下さい。

DockerfileでLAMP環境を一発作成

ここまで、Dockerコンテナの立ち上げを見てきました。
ただ、さきほど作成したコンテナはまっさらな状態であり、初期設定は一切されていません。
初期設定されたイメージの作成方法には、先ほど起動したbash経由で行いcommitしてイメージ化するという方法がありますが、それとは別にDockerfileという定義ファイルを使ってDockerイメージを作成する方法もあります。

ここからは、Dockerfileを使って、LAMP環境がセットアップされたDockerイメージを作成してみます。

今回作成するイメージは、monitというサーバープロセスの監視ツールを使って、Apache、fluentd、sshdを実行するものです。
これで、Docker経由でmonitさえ起動させてしまえば、普通のサーバーと同じようにssh経由でログインの出来るウェブサーバが完成します。

ちなみに、Dockerでmonitを稼働させるにあたり、以下のブログを参考とさせていただきました。
Dockerfile の書き方「私的」なベストプラクティス(3)〜サービスの起動について〜 - ようへいの日々精進

今回使用するDockerfileと、それにまつわる設定ファイル群については、私のgithubよりcloneして下さい。

$ git clone https://github.com/kunihirotanaka/docker-centos-lamp.git
Initialized empty Git repository in /home/tanaka/docker-centos-lamp/.git/
remote: Counting objects: 32, done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 32 (delta 11), reused 26 (delta 8)
Unpacking objects: 100% (32/32), done.
$ cd docker-centos-lamp/
$ ls
Dockerfile  README.md        monit.conf   monit.mysqld  monit.td-agent  td.repo
LICENSE     authorized_keys  monit.httpd  monit.sshd    td-agent.conf

githubからクローンが完了すると、ローカルディレクトリにdocker-centos-lampというディレクトリが作成され、Dockerfileと関連する設定ファイルが作成されます。

Dockerfileには、RUNとかADDとかといったDockerfile用のコマンドが列挙されており、RUNはコンテナ内でのlinuxコマンド実行を、ADDはローカルディレクトリのファイルをコンテナ内にコピーすることを示しています。
これからDockerfileを元にイメージを作成したいと思いますが、ENVでIPアドレスやパスワードを指定するため、ファイルの中を少し修正頂く必要があります。
IPは、monitやphpMyAdminへアクセスを許可する接続元IPアドレスを、PWはMySQLやログインのためのパスワードを、LOGSERVERはfluentdでログを転送する先のIPアドレスを示します。

変更前

FROM centos
MAINTAINER Kunihiro Tanaka 

ENV IP __YOUR_IP_ADDRESS_HERE__
ENV PW __YOUR_PASSWORD_HERE__
ENV LOGSERVER __YOUR_LOG_SERVER_HERE__

変更例

FROM centos
MAINTAINER Kunihiro Tanaka 

ENV IP 192.168.50.3
ENV PW Jhd30Kwj
ENV LOGSERVER logserver.example.jp

また、authorized_keysというファイルもありますので、適宜sshの公開鍵を登録しておいて下さい。

ここまでくれば、あとはコマンド一発です。docker buildをすれば構築が開始されます。
※最後のドットを忘れないようにしてください。

$ sudo docker build -t centos:lamp .
~略~
Successfully built c94a7828a3a9
$ sudo docker images
[sudo] password for tanaka:
REPOSITORY        TAG             IMAGE ID         CREATED              VIRTUAL SIZE
centos            lamp            c94a7828a3a9     About a minute ago   1.156 GB

docker imagesで、作成したイメージが表示されれば、構築完了です。

作成したLAMP環境を起動させる

これで、新しいイメージが完成しました。
さっそく起動してみましょう。
/usr/bin/monitを起動すれば、SSH、MySQL、Apache、fluentd(td-agent)が起動されます。

$ sudo docker run -d -t -p 12812:2812 -p 10080:80 -p 10022:22 centos:lamp /usr/bin/monit -I
65d63382ebac74066290024a096fc291cf054435e1d10331ee1a03ac7e5e0c65
$

docker psをすると、monitが起動しているのが分かります。

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                                                   NAMES
65d63382ebac        b3f874bbbd97        /usr/bin/monit -I   49 seconds ago      Up 48 seconds       0.0.0.0:10022->22/tcp, 0.0.0.0:10080->80/tcp, 0.0.0.0:12812->2812/tcp   prickly_lovelace

この状態で、ブラウザからアクセスしてみましょう。
各サーバの起動には少し時間がかかるので、アクセスできない時は少し待ってみましょう。

URLは、dockerの動いているサーバの12812番ポートですので、192.168.1.1というサーバで動いてたとすると、http://192.168.1.1:12812/ と入力するとアクセス可能です。
ユーザ名はadmin、パスワードはmonitで、これはmonit.confの中で変更することが可能です。

次に、phpMyAdminへもアクセスしてみます。
URLは、dockerの動いているサーバの10080番ポートですので、192.168.1.1というサーバで動いてたとすると、http://192.168.1.1:10080/phpmyadmin/ と入力するとアクセス可能です。

sshを行う場合は、10022番ポートへアクセスすれば可能です。
ログインするときは、ユーザ名lampで、先ほど指定したパスワードで可能です。

起動したLAMP環境を終了するときはkillを、その環境を引き続き利用する場合にはcommitしてからrunして下さい。

コンテナの終了

$ sudo docker kill 65d63382ebac
$ sudo docker ps 
CONTAINER ID   IMAGE      COMMAND    CREATED     STATUS    PORTS        NAMES

コミットして、再度起動してみる

$ sudo docker commit 65d63382ebac
$ sudo docker run -d -t -p 12812:2812 -p 10080:80 -p 10022:22 fa55a6cebb12 /usr/bin/monit -I

これで、終了した時点から再開することが可能です。

無事に構築できましたでしょうか?

Dockerはまだバージョンが1.0になっておらず、これからのソリューションといえますが、サーバーをドカドカたてるという言葉がブームになるほど、サーバーインフラ業界では注目されています。
皆さんもぜひ活用してみてください。

ちなみに、Dockerやるなら「さくらのクラウド」「さくらのVPS」をぜひどうぞ。
大事なことなのでもういちど。

Dockerやるなら「さくらのクラウド」「さくらのVPS」をぜひどうぞ!

【関連記事】
>>レッツトライ!夏休みに覚えるDocker
>>Dockerコンテナをクラウドサービス上で共有できる「Docker Hub」を使ってみる
>>15分で分かるLXC(Linux Containers)の仕組みと基本的な使い方a>
>>LXCを使った権限分離とテンプレートのカスタマイズ

おしらせ