Traffic ServerでWORDPRESSを高速化する

最近地道にトラフィックが増えてきているので、そろそろWORDPRESSの高速化を検討してみる。世間ではNginxがはやっているようだが、ここは敢えてApatch Traffic Serverを使ってみたい。

OSはUbuntu 15.xを使用しています。

Apatche Traffic Serverの初期設定

apt-getを使用して標準のレポジトリからApatche Traffic Serverをインストールします。

sudo apt-get install trafficserver

Reverse-Proxeの接続先を設定します。

sudo nano remap.config
map http://www.example.net:8080/ http://127.0.0.1/
reverse_map http://127.0.0.1/ http://www.example.net:8080/

サービスを起動してポート8080に接続してみますが、動作しません。

sudo service trafficserver start

ログファイルを閲覧すると、以下のようなエラーが発生しています。

less /var/log/trafficserver/traffic.out
traffic_server: using root directory '/usr'
FATAL: Trafficserver has not been designed to serve pages while
        running as root. There are known race conditions that
        will allow any local user to read any file on the system.
        If you still desire to serve pages as root then
        add -DBIG_SECURITY_HOLE to the CFLAGS env variable
        and then rebuild the server.
        It is strongly suggested that you instead modify the
        proxy.config.admin.user_id directive in your
        records.config file to list a non-root user.

どうやら標準のままだとrootアカウントで起動されてしまうけど、trafficserverはrootアカウントで起動できないため権限周りでエラーとなっているようです。/etc/trafficserver/records.configを編集してproxy.config.admin.user_idの設定を追加するように指示されているので追加します。trafficserverアカウントは既に作られているので、追加の必要は無いようです。

sudo nano /etc/trafficserver/records.config
CONFIG proxy.config.admin.user_id STRING trafficserver

サービスを再起動してポート8080に接続してみると、今度は動作しました。

sudo service trafficserver restart

サービスの移行

正常に動作するようなので、本番に移行します。
Windows Azureを使っているので、エンドポイントの設定を変更してポート80への接続を、ポート8080に振り分けるようにします。こういう所、手抜きできるからクラウド好きです。

remap.configの設定を本番用に変更します。先ほどはポート8080の記載がありましたが、それを削除します。

sudo nano remap.config
map http://www.example.net/ http://127.0.0.1/
reverse_map http://127.0.0.1/ http://www.example.net/

このままだとWORDPRESSが正常に動作しないので、以下の設定をwp-config.phpに追記します。

sudo nano /var/www/wordpress/wp-config.php
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];

apatch2とtrafficserverを再起動します。

sudo nano /var/www/wordpress/wp-config.php
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];

ポート80に繋いでみますが、正常に動作しているようです。

ローカルストレージへのキャッシュ移動

標準設定のままだと/var/cash/trafficserverにキャッシュデータを保存します。クラウド上の仮想サーバーは一般的にIO速度が遅いため、あまり速度が改善しません。その代わりにWindows Azureでは高速な揮発性のローカルストレージも提供されています。揮発性のストレージは仮想サーバをシャットダウンすると失われますが、キャッシュデータなら失われても問題ないのでこちらに移動します。
ローカルストレージはスワップファイル用に/mntにマウントされているので、/mnt/trafficserverと言うフォルダを新たに用意してこちらを使います。

新たにフォルダを作成します
sudo mkdir /mnt/trafficserver
sudo chown trafficserver /mnt/trafficserver

storage.configを編集して、キャッシュの保存先を変更します。
sudo nano /etc/trafficserver/storage.config
/mnt/trafficserver 256M

trafficserverを再起動します。
sudo service trafficserver restart

どの程度改善されたか

トップページの表示完了まで5,000msかかっていたのが700ms程度に。トップページのhtmlだけなら2,500ms程度かかっていたのが、20ms程度にまで高速化されました。応答速度が125倍改善して、体感的にも7倍くらい速くなっている計算。

参考

WordPressをリバースプロクシ対応にする3つのポイント
How to Set Up Apache Traffic Server as a Reverse-Proxy on Ubuntu 14.04

Windows Azure上のWindowsをGUIなしにする

多少でも仮想マシンの利用料を下げるために、GUIを削除して軽くしたいと思います。これで仮想マシンのサイズを下げても、使用メモリ量が減っている分、それなりに使えるようになるはずです。

仮想OSのPowershellのリモート接続を有効にする

GUIを持たないWindowsの操作は主にPowerShellから行う事になります。まずはPowerSheelに対してリモート接続出来る状態にします。Azure仮想OSにRemote Desktopで接続して、PowerShellを管理者権限で起動してEnable-PSRemoteingを実行します。これにより必要なサービスの起動やファイアウォールの設定等が自動で行われますので「[A] Yes to All」を選択して継続してください。

PS C:\Users\username> Enable-PSRemoting
WinRM Quick Configuration
Running command "Set-WSManQuickConfig" to enable remote management of this computer by using the Windows Remote
Management (WinRM) service.
 This includes:
    1. Starting or restarting (if already started) the WinRM service
    2. Setting the WinRM service startup type to Automatic
    3. Creating a listener to accept requests on any IP address
    4. Enabling Windows Firewall inbound rule exceptions for WS-Management traffic (for http only).

Do you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

クライアントへのSSL証明書のインストール

次にPowerShellのリモート接続で使用するためのSSL証明書のダウンロードとインストールを行います。少々変則的ですがhttps://[仮想マシン名].cloudapp.net:5986のURLにGoogle Chromeを使用してアクセスし、画面赤丸の部分をクリックして証明書を表示します。
chrome#1

次に↓画面の赤丸の部分をクリック。
chrome#2

最後に↓画面の赤丸部分をクリックして、後はウィザードに従ってデフォルト設定のままファイルに保存します。
chrome#3

証明書をダウンロードしたら、証明書ファイルを右クリックしてインストールを選択します。
inport#1

インストールはウィザードに従って勧めていきますが、証明書ストアの選択で「信頼されたルート証明機関」に保存するように設定をしてください。それ以外はデフォルトのままで進めます。
inport#3

クライアントから仮想OSにPowerShellで接続する

これでPowerShellでリモート接続する準備が整ったので、PowerShellを起動して次のコマンドを実行します。プロンプトに接続先コンピュータ名が表示され、リモート接続していることが分かります。

PS C:\Users\username> Enter-PSSession -ComputerName computername.cloudapp.net -Credential knarita -UseSSL
[computername.cloudapp.net]: PS C:\Users\username\Documents>

 

仮想OSからGUIを削除する

せっかくなので、このままAzure仮想OSからGUIをアイインストールしてしまいます。次のコマンドを実行してください。

Uninstall-WindowsFeature Server-Gui-Mgmt-Infra –restart
Success Restart Needed Exit Code      Feature Result
------- -------------- ---------      --------------
True    Yes            SuccessRest... {Windows PowerShell ISE, Graphical Managem...
警告: You must restart this server to finish the removal process.

これで再起動するとGUIが無い状態で起動します。

ちなみにGUIを戻すにはPowerShellから次のコマンドを実行すると、いつでも元に戻せます。

Install-WindowsFeature Server-Gui-Mgmt-Infra,Server-Gui-Shell –restart

apt-get upgradeでThe following packages have been kept backが発生

Ubuntu 15.10にてapt-get upgradeを実行したときに、下記のようにThe following packages have been kept backと表示されアップデートが行われない。既存パッケージの依存関係に問題が発生しているのが原因。

$ sudo apt-get upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages have been kept back:
  linux-cloud-tools-virtual linux-generic linux-headers-generic
  linux-headers-virtual linux-image-extra-virtual linux-image-generic
  linux-image-virtual linux-virtual sosreport
0 upgraded, 0 newly installed, 0 to remove and 9 not upgraded.

sudo apt-get dist-upgradeを実行すると依存関係の問題が処理される。longer required: linux-cloud-tools-4.2.0-16と表示されているようにlinux-cloud-toolsのupdateを自動で処理できなかったのが原因。このままYとするとlinux-cloud-tools-4.2.0-22に更新されてインストールが完了する。

$ sudo apt-get dist-upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... The following packages were automatically installed and are no longer required:
  linux-cloud-tools-4.2.0-16 linux-cloud-tools-4.2.0-16-generic
Use 'apt-get autoremove' to remove them.
Done
The following NEW packages will be installed:
  libpython3.5-minimal libpython3.5-stdlib linux-cloud-tools-4.2.0-22
  linux-cloud-tools-4.2.0-22-generic linux-headers-4.2.0-22
  linux-headers-4.2.0-22-generic linux-image-4.2.0-22-generic
  linux-image-extra-4.2.0-22-generic python3.5 python3.5-minimal
The following packages will be upgraded:
  linux-cloud-tools-virtual linux-generic linux-headers-generic
  linux-headers-virtual linux-image-extra-virtual linux-image-generic
  linux-image-virtual linux-virtual sosreport
9 upgraded, 10 newly installed, 0 to remove and 0 not upgraded.
Need to get 70.8 MB of archives.
After this operation, 311 MB of additional disk space will be used.
Do you want to continue? [Y/n]

linux-cloud-toolsは不用意に更新すると接続が出来なくなったり、クラウドの管理コンソールから制御できなくなる恐れがあるので、自動更新を取り止めたのかと思われる。

Cloud上の仮想マシンの最適化

Cloud上の仮想マシンの最適化の鍵はローカルストレージの活用にあります。

一般的に仮想マシンはディスクIOがボトルネックになりやすいです。動作速度に物理的な制約があるデバイスを複数の仮想マシンで共有する上に、実際に物理的に書き込まれるまでの階層も多いので、ボトルネックになるのは避けられません。

Amazon RDSやSQL AzureもEC2やと同じく仮想マシンを提供している事にはかわりありません。通常提供している仮想マシンに比較して、データベースのようにディスクIOが頻繁に発生する用途に特化して最適化が施されています。

Amazon RDSやSQL Azureを使うことで、通常の仮想マシン上にデータベースを構築した場合よりも高速になるか否かは、場合によるので一概には言えません。

Cloud上の仮想マシンの最適化を試みる場合、仮想マシンに割り当てられる揮発性ストレージの活用が鍵になると思います。揮発性ストレージは仮想ディスクとオンプレミスのストレージと遜色ない速度で動作します。障害時に直近のデータが失われてもかまわないログデータや、通常は読み取り専用にしか使わないデータを揮発性ストレージに配置することで大幅にパフォーマンスが変わってきます。

またローカルストレージを使わないにしても、活用する見込みのないログ出力を抑制するだけでも大きく変わってきます。もしクラウド上の仮想マシンでパフォーマンスを問題としているなら、まずは仮想ディスクへのアクセスを減らせないか検討しましょう。

WebJobsのスケジュール設定を変更する

.Net FrameworkのWebJobsのスケジュール設定はプロジェクトフォルダ内のwebjob-publish-settings.jsonに保存されています。

{
  "$schema": "http://schemastore.org/schemas/json/webjob-publish-settings.json",
  "webJobName": "YourWebJobName",
  "startTime": "2015-10-25T00:00:00+09:00",
  "endTime": "2015-10-26T00:00:00+09:00",
  "jobRecurrenceFrequency": "Minute",
  "interval": 10,
  "runMode": "Continuous"
}

webJobNameはJobの名称、startTime、endTimeは開始時刻と終了時刻、intervalは周期なので説明は不要でしょう。
runModeはTriggered、Scheduled、Continuousの何れかを指定します。Triggeredは外部からの指示で起動するもので自動では起動されません。Scheduledはスケジュール設定に従って自動で起動するもの。Continuousはスケジュール設定に関係なく常に起動するものを示します。
jobRecurrenceFrequencyはSecond、Minute、Hour、Day、Week、Monthの何れかを選択します。

Microsoft Azure上のUbuntuにHadoopをインストール

1.仮想ネットワーク(Azure Private Network)の構築

1.1.仮想ネットワークの新規作成

Sparkのノードをインターネットからアクセス可能な場所に設置するわけにもいきませんので、最初に仮想ネットワーク(Azure Private Network)を構築します。

新規→仮想ネットワーク→カスタム作成を選択して、適当に名前を設定します。
vpn1
“ポイントからサイト間VPNの構築”にチェックして次へ。
vpn2
ここは変更なし。vpn4
“ゲートウェイサブネットを追加”をクリックしてデフォルト設定のゲートウェイサブネットを追加して確定。vpn3

1.2.証明書の作成と登録

続いてルート証明書を作成します。Windowsには標準では証明書を作成する機能がないため、オープンソースのツールを使ったり、開発用のツールを使ったりして証明書を作成することになります。ここではVisual Studio 2013に付属する開発ツールを使用してルート証明書を作成します。まずは”C:¥Program Files¥Microsoft Visual Studio 12.0¥Common7¥Tools¥Shortcuts”にある”開発者コマンド プロンプト for VS2013″を開いてコマンドプロンプトを表示します。そして次のコマンドを実行して下さい。”SparkVirtualPrivateNetwork”の所は適当な名称に置換えて下さい。

makecert -sky exchange -r -n "CN=SparkVirtualPrivateNetwork" -pe -a sha256 -len 2048 -ss My "SparkVirtualPrivateNetwork.cer"

“SparkVirtualPrivateNetwork.cer”と言うファイルが出来上がります。これを証明書にアップロードします。
次にクライアント証明書を作成します。次のコマンドを実行します。
“ClientCertificateName”は適当な名称に置換えて下さい。

makecert -n "CN=ClientCertificateName" -pe -sky exchange -m 96 -ss My -in "SparkVirtualPrivateNetwork" -is my -a sha256

登録されている証明書を確認します。コマンドプロンプトから”certmgr.msc”を実行します。最初に”信頼されたルート証明機関”の”証明書”に先ほど作成したSparkVirtualPrivateNetworkが登録されているか、”個人”の”証明書”に”ClientCertificateName”が登録されているか確認します。
もしmakecertを実行した以外の環境から仮想ネットワークに接続する場合は、先ほど作成したSparkVirtualPrivateNetworkとClientCertificateNameを登録する必要があります。makecertを実行した環境で、それぞれの証明書の秘密鍵をエクスポートして、新たな環境にてインポートします。

1.3.VPNクライアントのインストール

仮想ネットワークのダッシュボードからクライアントVPNパッケージをダウンロードしてきてインストールします。インストールするとコントロールの”ネットワークと共有センター”に仮想ネットワークへの接続が追加されるので、接続を実行します。このとき使用する証明書を聞いてくるので、先ほど作成したClientCertificateNameを選択して下さい。

2.Apatch Hadoopのインストール

Apatch Sparkを動作させるには、その前にApatch Hadoopを動作させておく必要があります。
と言うわけでUbuntu 14.10の仮想マシンが起動している所から作業をスタートします。

2.1.スタンドアローンで動作する所まで…

hadoopの動作にはJavaが必要になります。OpenJDKでも問題なく動作するそうなので、標準のOpenJDKをインストールしていきます。

sudo apt update
sudo apt upgrade
sudo apt-get install -y default-jdk

最新版のhadoopをダウンロードしてきて展開します。

wget http://ftp.tsukuba.wide.ad.jp/software/apache/hadoop/common/hadoop-2.6.0/hadoop-2.6.0.tar.gz
tar xvfx hadoop-2.6.0.tar.gz

“/etc/profile”を編集して展開したhadoopへのパスを設定します。

...
export PATH=/<展開したディレクトリ>/hadoop-2.6.0/bin:$PATH
...

“hadoop-2.6.0/etc/hadoop/hadoop-env.sh”を編集してJAVA_HOMEのパスを設定します。

...
# set to the root of your Java installation
export JAVA_HOME=/usr/lib/jvm/default-java
...

この時点でスタンドアロンモードでhadoopが動作します。試しに”hadoop”とコマンドを打ってみて下さい。hadoopコマンドが起動するなら、先の設定へと進んでいきましょう。

2.2.疑似分散(Pseudo-Distributed)モードで動作する所まで…

今度は”ssh localhost”とコマンドを打って下さい。hadoopはノード間の接続にsshを使用します。もし”ssh localhost”で接続できないなら、sshがパスワードなどを入力しなくても繋がるように証明書を作成します。

ssh localhost
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

hadoop-2.6.0/etc/hadoop/core-site.xmlを編集します。
“hadoop.tmp.dir”で指定している”/var/opt/hadoop-2.6.0/cash”はhadoopが生成するファイルの保存先となるディレクトリです。デフォルトでは”/tmp”以下に保存されるために、OSを再起動すると全ての設定やHDFSに格納したデータが失われます。



...


        hadoop.tmp.dir
        /var/opt/hadoop-2.6.0/cash
        fs.defaultFS
        hdfs://localhost:9000


hadoop-2.6.0/etc/hadoop/hdfs-site.xmlを編集します。



...


        dfs.replication
        1


次のコマンドを実行して、HDFS(Hadoop Distributed File System)のnamenodeを初期設定します。

hdfs namenode -format

次のコマンドを実行して、hadoopのデーモンを起動します。

hadoop-2.6.0/sbin/start-dfs.sh

MapReduceで使用するユーザーディレクトリを作成します。

hadoop fs -mkdir /user
hadoop fs -mkdir /user/

最後にサンプルプログラムを実行して、hadoopが疑似分散(Pseudo-Distributed)モードで動作する事を確認します。

cd hadoop-2.6.0
hadoop fs -put etc/hadoop input
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.0.jar grep input output 'dfs[a-z.]+'
hadoop fs -cat output/*

2.3.YARN(Pseudo-Distributed)モードで動作する所まで…

“etc/hadoop/”フォルダにある”mapred-site.xml.template”を”mapred-site.xml”にコピーして編集します。

cd etc/hadoop/
cp mapred-site.xml.template
mapred-site.xml

mapreduce.framework.nameにはhadoop




          mapreduce.framework.name
          yarn


再び同じようにサンプルプログラムを実行するとyarn上で動作します。

cd hadoop-2.6.0
hadoop fs -put etc/hadoop input
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.0.jar grep input output 'dfs[a-z.]+'
hadoop fs -cat output/*

3.雑記

本当はSprkを動作するところまで行きたかったのだけど、思った以上に面倒で挫折。Sparkの機能であるSparkSQLを動作させるにはHiveのインストールが必要で、Hiveをちゃんと動作させるにはMySQLが必要で・・・。CDHやMapR、HDPの有用性を改めて思い知ったのでした。
というわけで、次はCDHをベースにして再スタートしようと思う。でもきっとこの経験は無駄にはならないと思うんだ。CDHを構成する過程で、Hadoopの知識は再び要求されるはずだしね。