カテゴリー別アーカイブ: ソフトウェア

ブログをAzure WebAppsからAmazon Lightsailに移設してみた。良い所と引っかかった所など

無料版のAzure WebAppsはそれなりに良い物だったのですが、いかんせんスペック不足過ぎており、上位のプランにアップしようと思うといきなり数千円からという価格になってくるため、AWSのAmazon EC2よりもお安く使えるAmazon Lightsailに移設してみました。

EC2(AWSにおけるIaaSなクラウドサーバ)とLightsail(AWSにおけるVPS)の違いは以下のようになっております。

Amazon EC2

  • 完全従量課金で1時間単位の課金。
  • ネットワーク使用量についても従量課金。
  • スケールアップ、スケールダウンがいつでも自由にできる。
  • ディスクの拡張も比較的容易。
  • オートスケーリングを用いたスケールアウト、スケールインも(動かすプログラムが対応していれば)簡単。
  • VPCでの様々なネットワーク制御。
  • IAMによる細かな権限制御。

Amazon Lightsail

  • マシン自体は月額定額制の固定料金(5ドル~)。スナップショットについては容量に応じた従量課金。
  • ネットワーク使用量に比較的大きめ(1TB~)の無料枠有り。
  • ディスク容量はプランごとに固定。
  • スケールアップする場合はスナップショット作成からの新マシン作成となる。
  • EC2のコンパネとは色々と勝手が異なる。EC2のVPCとも異なるのでVPC関連で出来る事でLightsailでは出来ない事が多々ある。
  • 中身はEC2のt2インスタンス(のような物)だと思われる。ハイエンドな性能は求められない。
  • 他のVPSサービスとは異なり、EC2と同じ「Amazon Linux」が使える。

今回はt2.nanoベースと思われる月額5ドルのプランにしてみました。nanoインスタンスはCPUクレジットに不安が残るものの、今のところ明らかにAzure WebApps 無料プランよりは快適に動いています。

以下、移設の中で引っかかった所をまとめておきます。

LightsailのDNS機能はRoute53側でネームサーバーの変更が必要

LightsailにはAmazon Route53とは別のDNS管理機能が有り、一般的なVPSサーバを建てるための設定が全てLighsail側で完結するようになっているのですが、ここでRoute53でドメインを取得しているとひと手間必要になります。

Route53で新規取得したドメインは自動でNSレコードやSOAレコードがRoute53側に設定されますが、LightsailのDNS管理機能を使うにはここでネームサーバの指定をLighsailのDNS機能のネームサーバーへ変更してやる必要が有ります。

考えてみれば当たり前なのですが気づくのに結構時間がかかりました。ネームサーバの指定は例えばGoogle Plubic DNSのキャッシュ フラッシュ機能でもフラッシュ出来ないので、結構な時間変更の反映を待つ形になります。仕方が無いのでRoute53側でも一次的なAレコードを追加しました。

今思うとドメイン関連は、Route53だけで完結させても良かった気がします。

WordPressのサイトURL変更

昔SQLでパパっと変更すればOKだったような気がしますが、実際にはテキストではない形でデータを持っている部分も有りダメという情報を見かけました。

サイト URL の変更 – WordPress Codex 日本語版
https://wpdocs.osdn.jp/%E3%82%B5%E3%82%A4%E3%83%88_URL_%E3%81%AE%E5%A4%89%E6%9B%B4

上記を参考にwp-login.phpに一次的に

update_option('siteurl','http://example.com/');
update_option('home','http://example.com');

を記載する形で対応しました。

動かない301リダイレクト系プラグイン

WordPressはサイト移設時に行う301リダイレクトについてもプラグインがいくつか有り、移行元サイトの方で「Simple 301 Redirects」「Redirection」等を試してみたのですが、なぜか動かない。もしかしてHTTPヘッダの送出ではなく.htaccessファイルを作るなど、Apacheであること前提のプラグインだったりするんでしょうか?(未確認)

仕方が無いので(Azure WebAppsはIISなので)web.configを編集して対応しました。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 <system.webServer>
 <rewrite>
 <rules>
 <!--
 もともとWordPressを動かすために書いてあったURLリライトルール
 -->
 <rule name="Redirect All" stopProcessing="true">
 <match url="(.*)"/>
 <action type="Redirect" url="http://www.sodo-shed.com/{R:1}" redirectType="Permanent"></action>
 </rule>
<!-- 以下略

これで問題なく新サイトへのパス指定を維持したリダイレクトが動いています。最初からこれでよかったですね。

OOM Killerで死ぬMySQL

最初からメモリが少ない(一番下の月額5ドルのプランは512MB)事が分かっているので、
innodb_buffer_pool_size=64M
にするなど相当にしょっぱい設定にしていましたが、それでもMySQLが起動しません。スワップ領域を追加する事で起動するようになりました。

Amazon EC2, mysql aborting start because InnoDB: mmap (x bytes) failed; errno 12
https://stackoverflow.com/questions/10284532/amazon-ec2-mysql-aborting-start-because-innodb-mmap-x-bytes-failed-errno-12

に記載の方法で追加(以下引用)。

Run dd if=/dev/zero of=/swapfile bs=1M count=1024
Run mkswap /swapfile
Run swapon /swapfile
Add this line /swapfile swap swap defaults 0 0 to /etc/fstab

私は1024MBではなく2048MBでスワップを作成しました。一度の接続数が10に満たないサイトですが、以下のようなメモリ使用状況になります。vm.swappinessは20に設定しています。

$ free -m
            total used free shared buffers cached
Mem:           89  473   16     33      41     85
-/+ buffers/cache: 345  143
Swap:        2047  336 1711

月額10ドルのプランではメモリが1GB有るので、お試しでも512MBはちょっと……という方はそちらを選ぶといいと思います。

CPUクレジット残高の確認方法が無い

前述のようにt2系インスタンス(と同じような制御の物)を使っている可能性が高いのですが

Amazon Lightsailのベンチマーク – 稲葉サーバーデザイン
https://inaba-serverdesign.jp/blog/20161208/aws_lightsail_benchmark.html

t2系では重要なCPUクレジットバランス(CPUクレジット残高)を確認する方法が、すくなくともマネジメントコンソール上では見当たりません。CloudWatch側のメトリクスにも有りません。

t2系インスタンスはクレジットが枯渇しそうでないか、だけを見ればCPUについて今のスペックが適切かどうかが分かりますが、それが無いのでサーバー監視の設定は何かしら別で用意した方がよさそうです。

まとめ

他にも何かあったような気がしますが、CPUクレジット以外については当たり前と言えば当たり前に必要になる対応なので、逆にそれ以外は特殊なことは無く、EC2でAmazon Linuxを立ち上げるような気持ちではるかに格安なサーバーを建てる事が可能でした。価格重視だと国内VPSにも種類は多いですが、AWSである事、VPC間接続でEC2 VPCとの接続が可能である事、その他AWSで提供されるサービスとの親和性、Amazon Linuxが使えることなどにメリットを感じるならなかなか良いんじゃないでしょうか。


CentOS 6 / 7 標準のPHPでも高速化を諦めない

骨董品となって久しい CentOS 6 標準の PHP 5.3、CentOS 7 標準の PHP 5.4 ですが、ディストリビューション自体のサポート期間の長さと、それに伴う PHP 5.3、5.4 の致命的な不具合の概ねのサポート対応によって、「サポート期間の長い標準PHPを使い続ける」という場合も有るのではないでしょうか。PHP 5.5 以降にはphp-opcache (OPCache)という高速化の仕組みが有りますが、CentOS標準のPHPの場合もepel RepositoryからPECL扱いのopcacheが導入できるようです。

※epel Repositoryが有効化されている事

yum install php-pecl-zendopcache

※インストール後はApacheを再起動させたりphp-fpmを再起動したりなど、環境に合わせて適宜反映させる。

特にWordPress等のCMSやフレームワークでは OPCache の有無で大きく速度が変わってきますので、どうしてもディストリビューション標準のPHPを使い続ける事が必要な場合は、検討してみてもいいかもしれません。


ZabbixのデフォルトMySQL監視UserParameterは修正が必要

以下はZabbix 3.0.xでの話です。

Zabbix AgentをZabbix SIAのyum Repositoryから導入すると、
/etc/zabbix/zabbix_agentd.d/userparameter_mysql.conf 内にデフォルトでMySQL監視用ユーザーパラメータが定義されますが、その中のZabbix Pingに関しては修正が必要です。以下がデフォルトの定義です。

UserParameter=mysql.ping,HOME=/var/lib/zabbix mysqladmin ping | grep -c alive

これをそのままコンソール上で実行すると、MySQL(MariaDB)が起動している状態で実行すると1を返しますが、停止時には0だけでなく標準エラー出力への文字出力が含まれ、データ型が数値ではなくなってしまいます。

$ HOME=/var/lib/zabbix mysqladmin ping | grep -c alive
mysqladmin: connect to server at 'localhost' failed
error: 'Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)'
Check that mysqld is running and that the socket: '/var/lib/mysql/mysql.sock' exists!
0

数値として受け取れなくなったアイテムは「不明」の状態となり、トリガーが検知しない状態となります。また、これはトリガーのnodata関数が有った場合でも検知されません。

UserParameter=mysql.ping,HOME=/var/lib/zabbix mysqladmin ping 2>/dev/null | grep -c alive

でエラー出力を捨ててみたり

UserParameter=mysql.ping,HOME=/var/lib/zabbix mysqladmin ping 2>&1 | grep -c alive

でエラー出力も標準出力に吐いてgrepに渡す事でMySQLのダウンを検知する事が出来ます。


既存のPHP 4系WebアプリをCodeIgniter 3.xに乗せ換えた話

PHP 4.xで書かれた歴史有るWebアプリをフルスクラッチせずにPHP 5.6&CodeIgniter 3.xで動くように乗せ換えた話です。具体的なコードというよりも、こうやって進めた、という話になります。

この記事はCodeIgniter Advent Calendar 2016の11日目の記事になります。

現状

  • フレームワーク利用無し。
  • DBとしてPostgreSQL 8.2以前が組み合わされている。
  • ORマッパーとかも無い。
  • register_globals = On で動く前提で作られている。
  • かなりの数がある画面のほとんどのページが数個程度のPHPファイルに纏められている。GETやPOSTの値に機能やページの指定も含まれていて、if文でページを切り替えている。
  • 画面表示はCodeigniterでいうHTMLヘルパーのような、フォームやテーブルを楽に作るためのクラスが用意されていて、その関数を呼んでいる。
  • テストコードなんて無い。
  • ソースコードの文字コードもDBの文字コードもEUC-JP。

なかなか辛そうな感じですが、下手にフレームワークを使われているより、フルスクラッチ的なPHPの方がCodeigniterへの移植相性は良い(と思った)ので移行は出来るだろうと判断しました。

全コードをUTF-8に書き換え

今後の事を考えて文字コードはUTF-8に統一します。EUC-JPのコードをnkfでサクッと書き換えます。htmlspecialcharsしている所を検索し、見つけた1つ1つをUTF-8に対応した形で書き換えます。

PostgreSQL 9.x系への入れ替え

既存のDBの内容はすべてダンプで出力します。ダンプの中身をテキストエディタで開き、文字コードに関連している記述をすべてUTF-8準拠に書き換えます。そしてnkfで実際にUTF-8へと書き換え、PostgreSQL 9.4へ流し込みます。ここは意外と問題になりませんでした。

ここで処理上の問題になるのはキャストの仕様変更になります。

キャストを追加してバージョン互換性を保つには
http://lets.postgresql.jp/documents/tutorial/cast/

PostgreSQL 8.3 では型変換のチェックが厳密になるよう仕様変更されました。特に文字列型への暗黙の型変換が無くなったため、以前のバージョンで動作していたアプリケーションであっても 8.3 上ではエラーが発生するかもしれません。

これがもろに該当しSQL実行時にエラーになるのですが、今はまだ修正しません。

PHP 5.6への入れ替え

先のOS入れ替えの時点でremiレポジトリからPHP 5.6を導入しています、ので、アプリが正常に動かない状態ではありますが、PHP 5.6で動作する環境となっています。

試しに動かしてみる

全然動きません。ここからひたすら修正していく作業になります。

静的解析と実際に動かして出るエラーをひたすら潰していく

Eclipse+PDTで作業しています。世間的にはPhpStormな潮流となっていますが、こういう元々動かないようなコードをPhpStormに放り込むとエラー数がすごい事になるので、PDTが静的解析する分が丁度よかったりします(PhpStormでも解析する範囲は調整できるのですが)。これは体感的な物なので実際はどちらでもいいでしょう。ただ、静的解析の無いテキストエディタでのコーディングはお勧めできません。

ここでIDEによる静的解析でエラーになっている物、ワーニングになっている物をひたすら修正していきます。例えば、元のコードでは連想配列を使用する際にほとんどの添え字がシングルクォーテーションで囲まれていなかったりしました。連想配列は相当な数が使用されているので、これが一番作業としてつらかったですね……。

register_globalsに関してはオンにしてはいけない、というかPHP5.6ではそもそも使用不可なので、各ファイルの先頭に全てのGETとPOSTを変数に展開するコードを一時しのぎで書いています。

foreach ($_POST as $post_name => $post_value) {
    if (isset($$post_name)) {
        continue;
    }
    $$post_name = $post_value;
}
foreach ($_GET as $get_name => $get_value) {
    if (isset($$get_name)) {
        continue;
    }
    $$get_name = $get_value;
}

PostgreSQLのキャストで問題になるエラーも、実際に動かしてみながらエラーが出る個所を潰していきます。一か所エラーが見つかれば、そのカラムを扱うところすべてが怪しいので、コード全体をカラム名で検索し該当箇所をまとめて修正します。

とりあえずPHP 5.6とPostgreSQL 9.4で動くようになった

実はまだバグが残っていたりしますが、機能として重要な部分は正常に動作する状態ですので、他は見つけ次第随時潰していく、という対応を取りました。何かしらフレームワークが使われていたらフレームワークまで含んだコード量やフレームワークのメンテナンス状態、それを入れ替えるための仕様変更部分もろもろの理由で諦めていたかもしれませんが、フルスクラッチなのでひたすらバグを潰していくだけで移行が出来ました。

CodeIgniterへの入れ替え

やっとCodeIgniterの話になります。既にPHP 5.6で動く状態ですので処理の移植は結構楽です。ちなみにここからIDEをPhpStormに切り替えています。

コントローラの作成

既存のアプリはGETとPOSTの値だけで複数のページを実装していましたが、素直に

/分類A/insert
/分類A/edit/xxx
/分類A/detail/xxx
/分類B etc…

というような形で動くコントローラに作り替えました。分類部分がコントローラのクラス、insertやedit、detailの部分がコントローラ内のメソッドになります。既存のWebアプリとはここでURLの互換性が一旦なくなります。コントローラでルーティングした後は、すぐにモデルに渡してしまう形にします。

モデルの作成

GETとPOSTで振り分けていた処理ブロックの中身をモデル、モデル内のメソッドに分割していきます。つまり元の処理をひたすらコピペをしていくような作業になります。また、register_globalsや先のregister_globals一時しのぎ処理で作られた変数については、$this->input->get() や $this->input->post() から取得するようにします。

SQL実行部分に関しては、組み立てたSQLをPHPのPostgreSQL関数ではなく、CodeIgniterのSQL実行関数 $this->db->query() に渡すようにします。SQLのエスケープ処理もCodeIgniterの標準エスケープ関数 $this->db->escape() に切り替えました。実行結果は元の実装が連想配列前提でしたので、 $query->result_array() で取り出します。DBアクセスに独自の仕組みではなくSQL文スクラッチのような形でも特に問題にならないのは、CodeIgniterへの移植のしやすさとして大きい部分だと感じます。

共通処理的なコードは適宜、メソッドでまとめるなり、(CodeIgniterでいう)ライブラリを追加するなりしました。

ビューの作成

今回はTwigを使用しました。CodeIgniterで使える形のTwigは @kenji_s 様の「CodeIgniter Simple and Secure Twig」が利用できます。
http://blog.a-way-out.net/blog/2015/05/25/codeigniter-twig/

元々の実装に有ったHTMLヘルパー的クラスは用意せず、シンプルにTwigを使ったテンプレートを作成していきました。

デザイン的にはUIフレームワークとして Bootstrap 3.x を利用するようにしました。そのため各UI部品、Webアプリ全体のデザインについてはあまり時間をかけずに現代的な物になりました。元々のWebアプリではフレームでメニューを表示していましたが、ヘッダーにメニューバー(navbar)を設ける形に作り替えました。

URL互換性レイヤー

なんてたいそうな名前を付けていますが、元のWebアプリとのURL互換性を取るためのレイヤーをCodeIgniterのコントローラとして作成します。

例としてURLが
/?function=機能A&page=detail&code=xxx
のようなものが来たら、GETとPOSTの値で判断し、
/機能A/detail/xxx
のURLにリダイレクトさせます。

public function functionA() {
    $get_arr = $this->input->get();
    // 機能A
    if (!empty($get_arr['functionA']) && !empty($get_arr['detail']) && !empty($get_arr['code'])) {
        redirect($url_base . '/FunctionA/detail/' . $get_arr['code'] . '/');
    }
}

この互換性コントローラの呼び出しは
application/config/routes.php
のルーティング設定に定義します。URLには都合よく旧アプリにしか含まれない文字列が有りましたので、

$route['文字列A:any'] = 'compatiblelayer/functionA';
$route['文字列B:any'] = 'compatiblelayer/functionB';

のように振り分けます。

基本的には新しいURLを使ってほしいですが、既存の資料なりブックマークなりで古いURLを持っていたしても、これを用意しておく事でURL互換性が保たれます。

所感

文量にも表れていますが、既存のコードをPHP 5.6で動かした場合にバグとなる部分を修正する作業が一番重いです。ただ、それを乗り越えると既存のフルスクラッチコードは簡単にCodeIgniterのコントローラ、モデル、ビューに分割していく事が可能でした。またDBアクセス部分に関しても、旧来のSQL組み立てロジック、PHPが標準で備える関数での実行、結果を連想配列として扱う処理がそのままに近い形で移行できます。どちらもCodeIgniterが独自の作法を極力持たない、フルスクラッチに近い感覚で書けるという良さが移植においても現れた物かと思います。太古のPHPアプリが有れば、その近代化の選択肢としてCodeIgniter 3.x 、いかがでしょうか?