やりたいこと
わりと古いLAMP(Linux / Apache / MySQL / PHP)サーバーが現役稼働中なのでアップグレードしたい、でも新しいサーバーを買う金は無い、どうしよう?という状況があり、Dockerを使えば安全に素早くアップグレードできるのではないかと思いついて、やってみました。
Dockerの導入
ここが一番難関です。古いサーバーにはDockerが導入できない場合があります。
以下面倒なのでrootで作業します。
Dockerのインストール
まずInstall Dockerを見てサーバー上にDockerをインストールします。Ubuntuの場合、64bitでカーネルのバージョンが3.10以上でないとインストールできないようです。
Dockerは以下の一行だけでインストールできました。
curl -fsSL https://get.docker.com/ | sh
確認
docker run hello-world
と入力してHello from Docker.
以下ズラズラっとメッセージが表示されればOKです。
イメージ操作
以下のように入力すると現在のDockerイメージ一覧を確認できます。
docker images
出力例です。
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 690ed74de00f 5 months ago 960 B
先ほどのdocker run
コマンドにより、Docker Hubからダウンロードされたhello-worldイメージが表示されています。
コンテナ操作
以下のように入力すると現在のDockerコンテナ一覧を確認できます。-a
オプションは終了したコンテナも含めて表示します。
docker ps -a
出力例です。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0d000cf23953 hello-world "/hello" 26 minutes ago Exited (0) 26 minutes ago agitated_boyd
メッセージを表示してコンテナは終了しています。
不要なコンテナ&イメージの削除
hello-worldコンテナとイメージはもう不要なので消しましょう。
以下のコマンドでコンテナを消すことができます。
docker rm CONTAINER ID or NAMES
以下のコマンドでイメージを消すことができます。
docker rmi IMAGE ID or REPOSITORY:TAG
面倒なら以下のようにすると全てのコンテナとイメージを削除できます。
docker rm -f $(docker ps -a -q) && docker rmi -f $(docker images -a -q)
nginxコンテナの導入
元のWebサーバーはApacheでしたが、せっかくアップグレードするのでついでにカッコイイnginxにリプレースしてみましょう。
Docker Hubからnginxイメージをダウンロードしてコンテナを起動してみます。
docker run -p 8888:80 nginx
Webサーバーがフォアグラウンドで起動するのでコンソールは止まります。
コンテナ側では80番ポートでウェブサーバーが公開されます。ホスト側ではLAMPが稼働中で80番ポートは塞がっているので、-p 8888:80
によって8888番ポートを公開するようにしています。
ブラウザでhttp://Webサーバーのホスト:8888/
を開いてnginxの画面が表示されるか確認してください。コンソール画面にはnginxのアクセスログが表示されます。確認したらCtrl+C
で終了します。
Docker Composeの導入
続いてnginxコンテナとphpコンテナを連携させるのですが、Docker単体だと面倒なので、Docker Composeという複数コンテナを簡単に連携できるツールを使います。
Docker Composeのインストール
Install Docker Composeを参考にしてインストールします。以下の2行だけでいけました。
curl -L https://github.com/docker/compose/releases/download/1.6.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
定義ファイル用ディレクトリの作成
Docker Composeでは、いくつかの定義ファイルが必要になるので、ひとつ適当にディレクトリを作成します。場所はどこでも構いませんが、ディレクトリ名はコンテナ名のプレフィックスなどになるので気にしたほうが良いかもしれません。
以下、定義ファイル用のディレクトリを/root/lamp/
とします。
mkdir /root/lamp
cd /root/lamp/
docker-compose.yml の作成
Docker Composeの定義ファイルを作ります。/root/lamp/
に以下のファイルをdocker-compose.yml
という名前で作成してください。
version: '2'
services:
nginx:
image: nginx
ports:
- '8888:80'
YAMLファイルなのでインデントは正確にしてください。
docker-compose.ymlの解説
image: nginx
Docker Hubよりnginxイメージをダウンロードします。
ports:
- '8888:80'
コンテナ側のポート80番をホスト側ポート8888番で公開します。
Docker Composeの起動
docker-compose up
これでdocker-compose.yml
の定義に従ってコンテナが起動します。先ほどと同じようにブラウザでhttp://Webサーバーのホスト:8888/
を開いてnginxの画面が表示されるか確認してください。確認したらCtrl+C
で終了します。
nginxコンテナとphpコンテナの連携
次に、Docker Composeを使ってnginxコンテナとphpコンテナを連携してみましょう。
Dockerfileを使ったnginxイメージのカスタマイズ
phpと連携するためにnginxイメージをカスタマイズします。Dockerfileを使うと既存のイメージをカスタマイズして新しいイメージを作成することができます。
docker-compose.yml
docker-compose.yml
を以下のように変更してください。
version: '2'
services:
nginx:
build: ./nginx
ports:
- '8888:80'
depends_on:
- php
php:
image: php:fpm
docker-compose.ymlの解説
build: ./nginx
nginxディレクトリにあるDockerfileを使いイメージを生成します。
depends_on:
- php
nginxサービスがphpサービスに依存していることを示しています。
php:
image: php:fpm
Docker Hubからphp-fpmイメージを取得しています。
nginx/Dockerfile
/root/lamp/
内にnginx
ディレクトリを作成して、その中に以下のファイルを作成してください。
FROM nginx
COPY default.conf /etc/nginx/conf.d/
nginx/Dockerfileの解説
FROM nginx
カスタマイズ元となるDocker Hubのイメージを指定します。
COPY default.conf /etc/nginx/conf.d/
新しく生成するイメージ内にファイルをコピーします。ここではnginxイメージに元々存在するdefault.conf
を上書きしています。
nginx/default.conf
/root/lamp/nginx
ディレクトリに以下のファイルを作成してください。
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
location / {
index index.html;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
include fastcgi_params;
}
}
もともと存在するdefault.confを上書きして、PHP-FPMとの連携を記述しています。
nginx/default.confの解説
fastcgi_pass php:9000;
この「php」というのはphp:fpmコンテナの内部アドレスです。Docker Networkという機能を使って名前解決しています。
fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
/var/www/html/
のphpファイルを読みに行くようにしています。
起動
docker-compose up -d
-d
オプションはバックグラウンドでコンテナを起動します。
コンテナ内にphpファイルを作成する
このままではphpファイルが無いので確認できません。そこで起動しているコンテナに入り、phpファイルを作成してみます。
まず起動しているコンテナを確認します。
docker ps
以下のような出力になります。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b2c60187fac php:fpm "php-fpm" 3 minutes ago Up 3 minutes 9000/tcp lamp_php_1
a6af857414c5 lamp_nginx "nginx -g 'daemon off" 3 minutes ago Up 3 minutes 443/tcp, 0.0.0.0:8888->80/tcp lamp_nginx_1
最後のNAMESがコンテナ名です。
以下のコマンドでphpコンテナ内に入ります。
docker exec -ti lamp_php_1 bash
カレントディレクトリが/var/www/html
であることを確認してphpファイルを作ります。
echo '<?php phpinfo();' > info.php
exitでコンテナから抜け、ブラウザでhttp://Webサーバーのホスト:8888/info.php
を開き、phpinfoの画面が表示されることを確認してください。
ホストのドキュメントルートディレクトリをコンテナにマウントする
以前のウェブサーバーは稼働中なので、ホスト側に/var/www/html
等のドキュメントルートディレクトリがあると思います。そのディレクトリをコンテナにマウントしてみます。
version: '2'
services:
nginx:
build: ./nginx
ports:
- '8888:80'
depends_on:
- php
volumes:
- /var/www/html:/usr/share/nginx/html
php:
image: php:fpm
volumes:
- /var/www/html:/var/www/html
volumes
でホスト側のディレクトリをコンテナ側にマウントすることができます。再度
docker-compose up -d
して、ブラウザで開いて確認してみてください。
phpコンテナとmysqlコンテナの連携
同じようにして、mysqlコンテナを起動してphpコンテナからアクセスできるようにします。
docker-compose.yml
docker-compose.yml
を以下のように変更してください。
version: '2'
services:
nginx:
build: ./nginx
ports:
- '8888:80'
depends_on:
- php
volumes:
- /var/www/html:/usr/share/nginx/html
php:
build: ./php
depends_on:
- mysql
volumes:
- /var/www/html:/var/www/html
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pass
volumes:
- db:/var/lib/mysql
volumes:
db:
docker-compose.ymlの解説
image: mysql
Docker Hubの公式のMySQLイメージを取得しています。
environment:
MYSQL_ROOT_PASSWORD: pass
rootのパスワードを「pass」に設定しています。
volumes:
- db:/var/lib/mysql
名前付きボリューム「db」にMySQLのデータが入るようにしています。
volumes:
db:
名前付きボリューム「db」を定義しています。
ボリュームについて
ボリュームはコンテナとは分離されています。
docker volume ls
でボリューム一覧を表示します。上で定義した名前付きボリュームはlamp_db
という名前が付くと思いますので、
docker volume inspect lamp_db
で実体の場所が分かります。
php/Dockerfile
php
ディレクトリを作成し、その中にDockerfileを作成してください。
FROM php:fpm
RUN apt-get update \
&& apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng12-dev libmcrypt-dev \
&& docker-php-ext-install pdo_mysql mysqli mbstring gd iconv mcrypt json ftp zip
必要そうなPHPの拡張機能を追加しています。
確認
コンテナを起動してphpからmysqlに接続できるか確認します。
docker-compose up -d
/var/www/html/test.php あたりに以下のようなファイルを作ってエラーが出なければ接続されているでしょう。
<?php new PDO('mysql:host=mysql;dbname=mysql', 'root', 'pass');
ホスト名が「mysql」になることに注意してください。
本番サーバーのリプレース
上記の設定にさらに細かい設定を追加したものが以下になります。
こちらを元に本番サーバーをリプレースしてみましょう。
その前に、ポート8888番で本番同様に動くことをしっかり確認しておきます。
また、MySQLの認証や、phpinfo等の不要なファイルを消すなど、セキュリティ対策をしっかり行ってください。
ポートを変更する
docker-compose.yml
の
ports:
- '8888:80'
これを
ports:
- '80:80'
と変更します。
Webサーバーを止める
ここからはスピード勝負になります。
例えばAPTでインストールしたなら以下のようにして止め、80番ポートを開放します。
service apache2 stop
データベースのバックアップ
元のデータベースをmysqldumpなどでバックアップします。
コンテナを再起動する
docker-compose up -d
でコンテナを再起動します。
データベースのリストア
データベースをリストアします。
確認する
元のURLでサイトの不具合がないことを確認する。
最後に
Dockerを使えば一台のサーバーでもほぼダウンタイム無しでアップグレードできました。