DockerでLAMPサーバをつくる話
こんにちは。最近業務マシンがMac Book Airになって気を良くしているエンジニアの佐藤です。
世間ではDockerが注目を集めていますので、今回はこの話題をお届けしたいと思います。
Dockerとは
ものすごく簡単に言うと、従来(ハイパーバイザー型)より少ないリソース(CPU、メモリ、ストレージ)で同等のサービスをホストする仮想化技術です。 Linuxカーネルのコンテナと呼ばれる機能により、ある「おまじない」をして開始したプロセスはそれぞれ独立した以下の一式を受け取ります。
- ファイルシステムルート
- rootユーザ(およびその他のカーネルサービスID空間)
- ネットワーク
したがってこのプロセス(およびその子プロセス)は、あたかも独立したホスト(「コンテナ」と呼ばれます)であるようにふるまうことができるのです。プロセスのカーネルメモリ空間は全コンテナで共有使用するので、CPUとメモリが節約でき、カーネルの初期化をスキップして高速起動できます。
Dockerの問題点
ただしこの仕組みが成立するためには、コンテナ内のプロセスが、Dockerの用意した上記の仮想リソースの範囲で「おとなしく」動作することが必要です。Dockerを飛び越えてのリソースアクセスはご法度となり、失敗するか、コンテナの動作障害につながる恐れがあります。
LAMPのすべてをコンテナに収容できるか
それではLinuxサーバの定番用途の一つであるLAMPサーバ(Liux Apache MySQL PHP)は、コンテナ上で動作するのでしょうか?もしこれが可能なら、サーバのポータビリティが飛躍的に向上し、便利です。そこで今回はこのテーマに挑戦してみることにしました。
LAMPサーバ「コンテナ」の作成
結果からお伝えすると、以下の手順でLAMPサーバ「コンテナ」が作成できました。
- Dockerのインストール
- CentOS 6イメージをDocker hubからpull
- /bin/bashでスタート
- あとは普通にセットアップ
- 最後に起動スクリプトを書く
- イメージを保存して、docker run
1. Dockerのインストールと開始
今どきのLinuxディストリビューションでは、コマンド一発です。筆者のUbuntu 14.04では以下のようにしました。
# apt-get install docker.io # service docker.io start
2. CentOS 6イメージをDocker hubからpull
これもコマンド一発です。
# docker pull centos
3. /bin/bashでスタート
次に「bash」コマンドを指定してコンテナをスタートします。この時「-i -t」オプションを指定し、コンテナ内で起動したbashの標準入力と標準出力を、今まさにdockerコマンドを打っているターミナルに接続します。
# docker images | grep centos6 <-- CentOS 6 のイメージIDを確認 centos centos6 68eb857ffb51 2 weeks ago 212.7 MB # docker run -i -t 68eb /bin/bash <-- bashを指定してコンテナを起動。イメージIDは前方一致で指定できる。 bash-4.1# whoami <-- コンテナ内のbashのプロンプト root
4. あとは普通にセットアップ
ここまできたら後はapt-getとかyumとかを用いて通常のサーバと同じように設定していきます。
# apt-get install httpd openssh-server mysql-server php ...
5. 最後に起動スクリプトを書く
ここがちょっと難しい部分です。 Dockerは「docker run」で開始されたプロセスに特別なリソースを割り当てる仕掛けですので、このプロセスが終了すると、コンテナ全体が終了となります。これではサーバとして常駐するサービスとして使えません。このため「docker run」で開始するプロセスは以下の役割を担う必要があります。
- 開始したら他の常駐プロセス(httpdなど)を起動し、シグナル(docker killコマンドで送信)を受信するまで終了待機する。
- シグナルを受信したら、常駐プロセスを終了し、自身も終了する。
方法はいろいろあるのですが、一番簡単だと思うのは以下のようなシェルスクリプトです。
#! /bin/bash service rsyslog start service sshd start service mysqld start service httpd start term() { service httpd stop service mysqld stop service sshd stop service rsyslog stop } trap "term; exit" SIGHUP SIGINT SIGTERM while true; do sleep 1 done
このスクリプトを/root/start.shなどのパスに保存し、次回からdocker runするときはこのスクリプトを実行するようにします。
6.イメージを保存して、docker run
まずセットアップ作業が終わったコンテナを終了します
bash-4.1# exit <-- コンテナを終了 exit
この段階でコンテナは、「イメージ+変更差分」の形で保存されています。
# docker ps -l <-- コンテナのIDを確認 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ad8596fad31e centos:centos6 "/bin/bash" 51 minutes ago Exited (1) 3 seconds ago
docker runするためにはイメージにする必要がありますので、「イメージ+変更差分」を一本のイメージにします。
# docker commit ad85 server01 <-- イメージを作成
いよいよ起動です。このときコンテナホスト(dockerを実行しているマシン)の80番ポートを、コンテナの80番ポートに接続します。(この設定により、外部からコンテナにHTTPリクエスを送信できるようになります。)
# docker run –d –p 80:80 mybasket01 /root/start.sh 5cf98b694ceb5d62f7fa01d4ff5fc7130eb98ab4ea5c04a3df37c090c7b4595f
この状態でコンテナホストの80番ポートにアクセスすると、コンテナサーバからのレスポンスが返るはずです。
7. コンテナ終了は docker kill
コンテナを終了するときは、シグナルを送信して起動スクリプトの終了処理を実行します。ここではTERMシグナルを送信しています。
# docker kill -s="SIGTERM" 5cf9
いくつかのハマりポイント
Dockerコンテナでサーバをつくろうとした時にいくつかハマったポイントがあったので、ご紹介します。
1. CentOS 7 コンテナが使えない
Cent OS 7では常駐プロセス制御にsystemdが使用されていますが、Dockerコンテナ内ではsystemdが動きません。またApacheのインストールも失敗します。残念ですがリビジョンアップで改善されることを期待です。
2. SSHサーバにログインできない
SSHサーバの初期処理が実行されていない可能性がありますので、コンテナ内でSSHサーバを一旦起動して初期処理を実行します。
bash-4.1# service sshd start
また、既定のPAM(Praggable Authentication Module、Linuxの認証拡張モジュール)設定がDockerとぶつかる場合もあります。/etc/ssh/sshd_configを編集し、「UsePAM no」に設定します。
3. ファイル共有が使えない
NFSサーバはカーネル機能ですので、Dockerでは使えません。更に、Sambaも何故か動作しませんでした(調査中です…)。ファイル共有については、現在WebDAV + davfsでやっています。
まとめ
Dockerはまだ新しい技術で、心配な部分が多いというのが率直な印象です。しかしコンテナ仮想化のリソース効率の高さはまちがいなく、ポータビリティの恩恵も大きいと感じています。主要企業は一斉にサポートを表明しており、今後広まっていくのではないでしょうか。
コメント