PHP
nginx
docker
lamp
docker-compose

Dockerで古いLAMPサーバーを稼働させたまま安全にアップグレードしよう

More than 1 year has passed since last update.


やりたいこと

わりと古い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という名前で作成してください。


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を以下のように変更してください。


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ディレクトリを作成して、その中に以下のファイルを作成してください。


nginx/Dockerfile

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ディレクトリに以下のファイルを作成してください。


nginx/default.conf

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等のドキュメントルートディレクトリがあると思います。そのディレクトリをコンテナにマウントしてみます。


docker-compose.yml

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を以下のように変更してください。


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を作成してください。


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」になることに注意してください。


本番サーバーのリプレース

上記の設定にさらに細かい設定を追加したものが以下になります。

https://github.com/naga3/docker-nginx-php

こちらを元に本番サーバーをリプレースしてみましょう。

その前に、ポート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を使えば一台のサーバーでもほぼダウンタイム無しでアップグレードできました。