まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2014/01

CentOS に仮想化環境の KVM を導入する手順を紹介します。

KVM は Kernel-based Virtual Machine で、仮想化環境のハイパーバイザーです。VMWare などと同様に、KVM 環境の中に仮想のマシン(「ゲストOS」といいます)を作って、あたかも独立した一台のマシンがあるかのように利用することができます。大きな特徴としてまず「無料である」こと、加えて名前の由来になっているように Linux のカーネルの一部として、標準搭載されている機能なので、導入が比較的容易であることが挙げられます。またハイパーバイザー型であるため、KVM 自体は1台の物理的な Linux マシン(「ホストOS」といいます)の中で動き、その制御も Linux を通して行うことになります。例えば管理用のコンソール機能などもホストOS上の Linux アプリケーションとして提供されており、仮想HDDはホストOSからは1つのファイルとして扱います。

上記でも触れましたが、KVM は Linux のカーネルに含まれる形で実装されています。今回は Linux ディストリビューションとして CentOS を利用する前提で説明します。CentOS 自体のインストール方法については過去のブログエントリを参照してください:
『CentOS の導入』

KVM を利用するにはハードウェア的な条件があります。CPU が仮想化支援機構(Intel-VT, AMD-V)に対応している必要がある、ことです。

/proc/cpuinfo に flags として vmx が含まれていれば仮想化支援機構に対応済みと判断できます。
/proc/cpuinfo に flags として vmx (Intel版)か svm (AMD版)が含まれていれば仮想化支援機構に対応済みと判断できます(2016/Mar/20 修正)。
# cat /proc/cpuinfo | grep '(vmx|svm)'
(この結果、何かが表示されれば対応していることになる)

また利用する前に BIOS の設定を変更し、この仮想化支援機構の機能を有効にしておきます。


では CentOS に KVM を導入する手順を紹介します。まずは yum で KVM に必要なモジュールをまとめてインストールします:
# yum -y install libguestfs libguestfs-tools libguestfs-tools-c libvirt libvirt-client python-virtinst qemu-kvm virt-manager virt-top virt-viewer

インストールできたら KVM(libvirt) の自動起動を ON にしておきます。これで CentOS の起動と同時に KVM が有効になります:
# chkconfig libvirtd on

この段階で一度再起動して、KVM が有効になった状態で OS を起動します:
# shutdown -r now

再起動後、ホストOSにブリッジインターフェース(br0)を作成します。 このあと KVM 内に作成する全てのゲストOSはこのブリッジインターフェースを経由してインターネットを含めた外部ネットワークにアクセスすることになります。

ブリッジインターフェースは eth0 をベースに作成することにします。 そのため、まずは ifcfg-eth0 をコピーして ifcfg-br0 を作り、ここから br0 の雛形を作成します:
# cd /etc/sysconfig/network-scripts
# cp ifcfg-eth0 ifcfg-br0

ifcfg-br0 を編集します。内容は基本的に eth0 のものをそのまま使いますが、TYPE 属性を Bridge に変更します(大文字小文字に注意):
# vi ifcfg-br0

DEVICE=br0
TYPE=Bridge
ONBOOT=yes
 :

続いて ifcfg-eth0 を編集します。こちらは「(設定済みの)br0 経由で使う」ことを宣言することになります(大文字小文字に注意)。加えて IP アドレス関連の設定は br0 の方で行っていて、そちらへのブリッジをするだけなので eth0 では不要です(コメントアウトします):
# vi ifcfg-eth0

DEVICE=eth0
TYPE=Ethernet
ONBOOT=yes
BRIDGE=br0
#IPADDR=192.168.0.2
#PREFIX=24
#GATEWAY=192.168.0.1
#DNS1=192.168.0.1

DEFROUTE=yes
 :

この状態でネットワークを再起動します:
# /etc/init.d/network restart

ブリッジインターフェース(br0)が追加されたことを確認します:
# brctl show

bridge name bridge id STP enabled interfaces
br0 8000.002185aba9e1 no eth0
virbr0 8000.525400ca36c8 yes virbr0-nic

ブリッジインターフェースが2つ(br0, virbr0)確認できます。今作成した方(br0)ではない、もう1つのブリッジインターフェース virbr0 は KVM の導入と同時に作成されるものです。
これもブリッジインターフェースとして使えることは使えますが、パフォーマンスが悪いので明示的に br0 を作ったのでした。

この状態では virbr0 は不要なので削除します。また再起動時に自動作成されないよう設定します:
# virsh net-destroy default
# virsh net-autostart default --disable

以下のコマンドで default の State が inactive になっていて、 かつ Autostart が no になっていることが確認できれば設定完了です:
# virsh net-list --all
Name State Autostart
-----------------------------------------
default inactive no


ここまでの設定で KVM 関連ツール(仮想マシンマネージャーとか)が使えるようになっているはずです。KVM の使い方については別の機会に。
kvm01

 

某所で書いていた文書をブログに移行します。
この文書はどちらかというと、自分のメモ用に作って、CentOS の導入時に頻繁に参照していたものです。

0. CentOS とは

CentOS とは商用OSである RHEL(RedHat Enterprise Linux) との完全互換を目指しているフリーの Linux ディストリビューションです。RHEL に含まれているソフトウェアのうち、商用ライセンス/商用パッケージを含まない形でリビルドされた、いわゆる「RHELクローン」の1つです。

CentOS はホームページから誰でも無料でダウンロードして利用することができます。以下、その導入手順を紹介します。

1. 導入メディアの用意

CentOS のページから最新バージョンの最新バージョンの導入メディアの ISO ファイルをダウンロードします。 これから導入するパソコンの CPU が 32bit であれば i386、64bit であれば x86_64と書かれた方を対象にします。

以下では CentOS 6.2 の 64bit 版を前提として記載します。

パソコン実機に導入する場合は ISO ファイルを DVD メディアに焼いておく必要があります。 仮想環境など ISO ファイルのままマウントしてアタッチできるような場合は ISO ファイルのままでも構いません。 またパソコンの BIOS 設定を変更して、DVD からのブート優先度を HDD 以上に上げておいてください。


2. CentOS のインストール

1. で用意した導入メディアを DVD にセットした状態でパソコンを起動します。 DVD が読み込まれて、起動オプションの選択画面が表示されますが、 一番上の Install or upgrade an existing system が選択された状態で Enter キーを押します
centos_install01


CentOS をインストールするための DVD が進行します。
centos_install02


まず、これから導入する DVD のメディア検査を行うかどうかを質問されます。 念のため行なってもいいのですが、この検査はかなり時間がかかります。 すでに導入実績がある場合などであれば Skip を選択しても構いません。
centos_install03


メディアの検査が終わって(あるいはスキップして)先に進めるとインストール開始画面が表示されます。 ここでは Next をクリックして先に進みます。
centos_install04


最初にインストールする環境の言語を選択します。ここでは Japanese(日本語) を選択します。
centos_install05


次はキーボードレイアウトの選択です。日本語キーボードを使っている場合は 日本語 を選択します。
centos_install06


インストール先のストレージデバイスの種類を選択します。 一般的なハードディスクにインストールする場合は Basic Storage Devices を選択します。
centos_install07


選択したストレージデバイスの中に現在含まれているデータの扱いについて確認されます。 まっさらの状態で新規に導入するケースであれば中身を消してしまっても構わないので はい。含まれていません。どのようなデータだっても破棄してください。 を選択します。
centos_install08


次にこれから導入する環境のホスト名を入力します。 ここでは仮に testos.testdomain.com と入力していますが、特定のネットワーク内に接続する場合はその環境に 合わせるなどして名前を指定します。
centos_install09


同じ画面内の左下に Configure Network と書かれたボタンがあります。 ここでネットワーク環境についての設定を行うので、クリックしてください。
centos_install10


ネットワーク接続 というダイアログが表示され、有線や無線のネットワーク設定を行います。 ここでは 有線 タブを選択して、有線ネットワークを設定してみます。 System eth0 と書かれた箇所が選択されていることを確認して、編集 ボタンをクリックします。
centos_install11


System eth0 のネットワーク設定画面です。 まず 自動接続する にチェックを入れて、起動と同時に有線ネットワークが有効になるようにしておきます。
centos_install12


続いて IPv4 のセッティング タブを選択して、IPv4 の設定を行います。 方式 と書かれた箇所を環境に合わせて指定します。 DHCP 環境であれば 自動(DHCP) を選択するだけです。
centos_install13


特定の固定アドレスを手動で設定する場合は、方式を 手動 にした上で 追加 ボタンをクリックし、 アドレス、ネットマスク、ゲートウェイ、DNSサーバー、ドメインなどを指定します。
centos_install14


IPv6 のセッティング タブで IPv6 の設定を行うこともできます。 ここでは 無視する を選択して、IPv6 を利用しない設定にしています。必要であれば設定してください。

最後に 適用 ボタンをクリックします。
centos_install15


ネットワーク接続 画面に戻ります。 閉じる ボタンをクリックして、この画面を終了します。
centos_install16


元の画面に戻りました。次へ ボタンをクリックして続きを行います。
centos_install17


タイムゾーンの設定画面です。アジア/東京 が選択されていることを確認します。
centos_install18


また、この画面の左下の システムクロックで UTC を使用 にチェックを入れます。 これでシステムとしては UTC(世界標準時)が使われた上で、画面上では東京のタイムゾーンで表示されるようになります。
centos_install19


管理ユーザーである root のパスワードを指定します。確認のため、同じ内容を2度入力します。
centos_install20


インストール先のパーティショニングの指定をします。 今回は新規にまっさらの状態で導入するので Use All Space を選択して、 ハードディスク全体を使って導入します。
centos_install21


この画面の左下の パーティションのレイアウトをレビューまたは修正する にチェックを入れて、 次の画面でパーティショニングの内容を確認します。
centos_install22


パーティショニングテーブルが表示されている様子です。 この状態では約 10GB のディスクの 2GB をスワップ領域に、残りを1つのパーティションで ext4 フォーマットで / にマウントしています。 必要であればここでパーティショニングを変更してください。 よく分からなければ、このような1パーティショニング構成でもいいと思います。
centos_install23


ディスクのパーティショニング構成やファイルフォーマットに変更が加わる場合、このような警告画面が表示されます。 全くの新規インストールであれば問題ありませんので、フォーマット を選択して先に進みます。
centos_install24


「ディスクに変更を書き加える」という警告メッセージが表示された場合は Write changes to disk を 選択して、ディスクへの書き込みを許可します。
centos_install25


ディスクのパーティショニングとフォーマットが実行されます。ここでしばらく待ちます。
centos_install26


フォーマットが完了すると、ブートローダーのインストール先を指定する画面が表示されます。 通常はデフォルトのまま /dev/vda にインストールする、というチェックを確認して先へ進みます。
centos_install27


CentOS のインストール内容を選択します。 いくつかの選択肢がありますが、今回は 最小限のインストールを基本に、必要なものだけを加える という例を紹介します。 ここでは最小限のインストールを指定する Minimal を選択します。
centos_install28


画面左下の 今すぐカスタマイズ にチェックを入れます。 これでこの後の画面で Minimal インストールをベースに、必要なものだけを追加していくことになります。
centos_install29


カスタマイズの画面になりました。2画面構成になっていて、左側おおまかなカテゴリーが表示され、 右側にそのカテゴリーに含まれる個別アプリケーションが表示され、インストール内容に含めるかどうかをチェックで指定します。

今回のケースでは開発環境を構築する上で最小限必要と思われるアプリケーションだけを導入する目的で選択します (余計なものを導入すると、トラブル時の原因操作がややこしくなく、という事情もあります)。 この内容に従う必要はありませんが、ここで選択しなくても、後から追加で導入も可能です。 とりあえずはこの内容の通りで行なってみてください。

最初は Hight Availability カテゴリーです。今回のインストールでは何も指定せずに進めます。
centos_install30


続いて Load Balancer カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install31


次は Resilient Storage カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install32


次は Scalable FileSystem カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install33


次は Web サービス カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install34


次は アプリケーション カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install35


次は サーバー カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install36


次は システム管理 カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install37


次は デスクトップ カテゴリーです。 ここでは X Window System、デスクトップ、 デスクトップのデバッグとパフォーマンスシステムにチェックを入れて先に進めます。
centos_install38


次は データベース カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install39


次は ベースシステム カテゴリーです。 ここでは ベースにチェックを入れて先に進めます。
centos_install40


次は 仮想化 カテゴリーです。ここも今回のインストールでは何も指定せずに進めます。
centos_install41


次は 言語 カテゴリーです。 ここでは 日本語のサポートにチェックを入れて先に進めます。
centos_install42


最後は 開発 カテゴリーです。 ここでは 開発ツールにチェックを入れて先に進めます。
centos_install43


全てのオプション設定が完了したら、画面右下の ボタンをクリックして、 実際のインストールを開始します。
centos_install44


インストールが実行されるとこのようなインジケーターが表示され、必要なアプリケーションとその前提ライブラリの確認が行われます。
centos_install45


実際のインストールが始まるとこのような画面になります。 全てのアプリケーション(この例では 762 個)のインストールが完了するまでしばらく時間がかかりますので、ひたすら待ちます。
centos_install46


全てのアプリケーションのインストールが終わったように見えますが、この後何らかの処理が動いているようで、 この状態のまま、更にもうしばらく待つことになります。
centos_install47


最後の最後にブートローダーがインストールされます。
centos_install48


この画面が表示されれば、無事に DVD(ISO) からの CentOS インストールが完了したことになります。 引き続きセットアップを行うので、再起動 ボタンをクリックして、システムを再起動します。
centos_install49


システムを再起動している時の様子です。 ハードディスクからは最初の起動となることもあって、少し長めに時間がかかります。
centos_install50


再起動されると、最初に CentOS のセットアップを行います。 まずは ようこそ 画面が表示されます。
centos_install51


CentOS のライセンス情報が表示されます。内容を確認します。
centos_install52


画面の左下に同意/不同意の選択肢が表示されています。 ライセンス内容に同意する場合は はい、ライセンス同意書に同意します にチェックを入れて先に進みます。
centos_install53


一般ユーザーの作成画面に移ります。 注意点として 一般ユーザーの作成は必須ではありません。 一般ユーザーを作成しなくても(常に root で利用するのであれば)このまま先に進むことができます。

今回の例では tuser というユーザーをここで作ることにして、その際の画面ショットを用意しました(作らなくてもかまいません)。
centos_install54


作成する一般ユーザーのパスワードが、いわゆる「弱いパスワード」の場合は警告メッセージが表示されます。 リスクを承知の上でこのまま指定したパスワードを使っても構いませんし、パスワードを変更することも可能です。
centos_install55


システム時刻の設定画面です。 年月日を指定し、必要であれば ネットワーク上で日付と時刻を同期化します にチェックを入れておきます (そうすると ntp が有効になります)。
centos_install56


Kdump の設定画面に移ります。 が、Kdump を有効にするにはある程度以上の空きメモリが必要です。充分なメモリがない場合は Kdump を有効にすることはできません。 その場合はこのようなダイアログが表示されます。
centos_install57


Kdump の設定画面はこのような内容です。Kdump を有効にする場合は kdump を有効にしますか? にチェックを入れておきます。
centos_install58


最後に 終了 ボタンをクリックして、このセットアップ内容をシステムに反映させます。
centos_install59


改めて GNOME 環境が起動し、ログイン画面が表示されます。

一般ユーザー(tuser)でログインする場合は、画面内の Test User と書かれた箇所をクリックすると パスワードの入力が求められます。
centos_install60


root ユーザーでログインする場合は、画面内の その他 と書かれた箇所をクリックし、 ユーザ名欄に root と入力して Enter キーを押します。
centos_install61


続いてパスワードを入力し、最後に Enter キーを押すか、ログイン ボタンをクリックします。
centos_install62


root ユーザーでログインしようとすると、システムへの変更も含めた影響から確認ダイアログが表示されます。 今後の表示が不要であれば 再度表示しない にチェックを入れ、閉じる ボタンをクリックします。
centos_install63


システムにログインして、GNOME のデスクトップ画面が表示されました。
centos_install64



 

3. CentOS の設定


 

導入したシステムは SELinux が有効になっているなど、セキュリティがある程度高めに設定されています。 安心して使える一方で、この後に導入するアプリケーションによっては設定が邪魔になることもあります。 ここでは比較的緩めのセキュリティ設定になるよう、変更する手順を紹介します。


3-1. SELinux の無効化

まずは 端末(ターミナル) を起動しましょう。画面左上の アプリケーション と書かれた 箇所をクリックし、システムツール - 端末 を選択します。
centos_install65


端末が起動しました。
centos_install66


以下は SELinux を無効にするための設定です。 有効にしたまま利用する場合、以下の操作は不要です。


まず現在の SELinux 設定状態を確認します。端末から getenforce と入力します。
# getenforce
Enforcing

実行結果の "Enforcing" は SELinux によるアクセス制限を行う設定状態になっていることを意味します。 これを一時的に無効にします。

# setenforce 0
# getenforce Disabled

実行結果の "Disabled" は SELinux によるアクセス制限を行わない設定状態になっていることを意味します。 とりあえず、これで SELinux は無効になりました。 しかしこの設定は一時的なもので、再起動は再び SELinux が有効になります。 再起動後も SELinux が無効になるようにするには、以下のファイルを編集します。

# vi /etc/selinux/config

(変更内容)SELINUX=disabled

設定後に再起動すると、setenforce コマンドなしに SELinux が無効の状態になります。

# shutdown -r now

3-2. IPv6 の無効化

IPv6 を利用しないのであれば、無効になるよう設定します。 まず 端末(ターミナル) を起動し、以下のファイルを新規に作成します。
# vi /etc/modprobe.d/disable_ipv6.conf

(ファイルの内容)
alias net-pf-10 off
options ipv6 disable=1

設定後に再起動します。

# shutdown -r now

再起動後は IPv6が無効の状態になり、ifconfig コマンドでも表示されなくなります。

# ifconfig | grep "inet6 addr"
#

3-3. 不要なサービス停止

無駄なリソースの消費を抑えるため、不要なサービスを停止します。以下は一例:
# ntsysv --level 345

[ ] NetworkManager (有線/無線のネットワーク設定をGUIで行うサービス)
[*] abrt-ccpp (自動バグ報告ツール)
[*] abrt-oops (不具合を oops トラッカーへ送信するサービス)
[*] abrtd (自動バグ方向ツール)
[ ] acpid (電源管理 ノートPC向け)
[ ] atd (任意コマンドを指定時間に1回実行するサービス)
[*] auditd (監査ログを出力するデーモン)
[ ] avahi-daemon (DNSを使わずにIPマルチキャストで名前やアドレスを参照するサービス)
[ ] cpuspeed (CPUの省電力機能 ノートPC向け)
[*] crond (任意コマンドを指定時間に繰り返し実行するサービス)
[ ] cups (印刷サービス)
[ ] dnsmasq (DNSとDHCPサービスを提供するサービス)
[ ] firstboot (インストール直後の初期セットアップ起動)
[*] haldaemon (ハードウェア情報を収集するサービス)
[ ] ip6tables (IPv6用パケットフィルタ)
[ ] iptables (IPv4用パケットフィルタ)
[*] irqbalane (マルチCPU環境での分散処理)
[ ] kdump (Kernelクラッシュ時のKernelダンプを保存する)
[*] lvm2-monitor (LVM関連サービス)
[*] mdmonitor (ソフトRAID監視サービス)
[*] messagebus (メッセージバスアプリケーション用)
[ ] netconsole (Kernelクラッシュ時のKernelダンプをネットワーク経由で保存する)
[ ] netfs (NFSクライアント)
[*] network (ネットワーク接続に関連するデーモン)
[ ] ntpd (NTPサービス)
[ ] ntpdate (他のサービスが起動前に正確な時刻を必要とするか、/etc/sysconfig/ntpd で -x オプションを指定した場合のみ有効にする)
[ ] portreserve ()
[*] postfix (SMTPサービス)
[ ] psacct (アカウントの利用統計を取得するサービス)
[ ] quota_nid (クォータ値超過が発生した旨のカーネルメッセージを D-BUS に転送する)
[ ] rdisc (ネットワークルータディスカバリーデーモン ルータとして動作させない場合は不要)
[ ] restorecond (SELinux 関連デーモン SELinuxを使わない場合は不要)
[*] rsyslog (システムログデーモン)
[ ] saslauthd (sas認証デーモン)
[*] smartd (HDD故障の予兆を検知するサービス)
[*] spice-vdagentd (デスクトップ仮想化デーモン)
[*] sshd (SSHサービス)
[*] sysstat (システム情報取得ツール)
[ ] udev-post (ハードウェア自動認識デーモン)
[ ] wdaemon (ハードウェア自動認識デーモン)
[ ] wpa_supplicant (無線LANクライアントとして WPA 暗号化をする場合に必要なサービス)

設定後に再起動します。これで不要サービスが停止します。

# shutdown -r now

3-4. パッケージアップデート

パッケージを最新版にアップデートします。
# yum update

更新が可能なパッケージが見つかった場合はインストールするかどうかを聞かれるので "y" を押します。

:

Total download size: 319M
Is this ok [y/N]: y
Downloading packages:

:

yum コマンドを導入後初めて実行する場合は GPG キーのインポートが必要、という警告/確認メッセージが表示されます。

:

警告: rpmts_HdrFromFdno: ヘッダ V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
Importing GPG key 0xC105B9DE:
Userid : CentOS-6 Key (CentOS 6 Official Signing Key) <centos-6-key@centos.org>
Package: centos-release-6-2.el6.centos.7.x86_64 (@anaconda-CentOS-201112091719.x86_64/6.2)
From : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
Is this ok [y/N]:

インストール直後は RPM パッケージの正当性をチェックする GPG キーがインポートされていないため、 確認メッセージが表示されます。"y" をクリックして更新を継続します。


カーネルアップデートは再起動後に有効になります。

# shutdown -r now

3-5. シェルの初期設定

このまま使い始めてもいいのですが、シェルを便利に使えるように設定します。以下は自分がやっている設定です。

まずは /etc/bashrc の最後に1行追加して、vim を vi にエイリアスします:
# vi /etc/bashrc
:
(以下を最後に追加)
alias vi='vim'

次に vim の設定を変更するため /etc/vimrc の最後に数行追加します:
# vi /etc/vimrc
:
(以下を最後に追加)
set nu
set ts=4
autocmd FileType * set formatoptions-=ro 

これで vi(vim) が少し使いやすくなりました。後は自分の好きなように。

Amazon EC2 インスタンスの負荷測定をどうやって行うべきか、という問題です。結論を先にいうと「EC2 インスタンスの場合は top コマンドではなく、CloudWatch を使うべき」ということになります。

きっかけは自分が先日ツイートしたこの現象に気付いたことでした:


EC2 で運用しているサーバーの CPU 負荷を測定すると、CloudWatch で測定した場合は80%超え(上図左)になってアラート出まくりなのに、直接ログインして top コマンドで測定するとせいぜい10%前後(上図右)になる、というものです。

なぜ測定方法によって負荷値が異なるのか? CPU 負荷の定義が違うのか? だとしたらどちらを信用すべきなのか、どちらかはそもそも信用してはいけないのか、・・・・

よくわからんなあ・・・ と思っていたのですが、ある日こんな情報を見つけました:
EC2 monitoring: the case of stolen CPU


簡単に言うと、「例えば CPU が 0.1 個割り当てられた仮想マシンでは、(topコマンドでの)CPU 負荷 = 10% の状態でその仮想マシンのCPU的には100%になっている」ということです。なので EC2 上の(Xen上の)仮想インスタンスを1台のマシンとみなして負荷を計測するのであれば CloudWatch を使うべき、ということになります。逆に言えばこの環境下での top コマンドの結果には注意が必要です。


上記ページには "If you’re an IBM cus­tomer with a pSeries frame these ques­tions aren’t entirely new to you"(IBM の pSeries の顧客であれば特別目新しいことではない) とも書かれてます。へぇ、AIX ってそうなんだ。知らなかったけどね・・・ :P


 

ワードプレス(WordPress)をよく使っています。個人でも業務でも。ブログというよりは、いわゆるCMS(コンテンツ管理システム)の機能を使ったサイトのプラットフォームとして利用することが多いです。まだまだですが、それなりに詳しくなってきたかなと自負しています。

CMS なのでファイルのアップロード機能などは普通に備えています。ただ「ワードプレスでファイルのアップロードができない」という質問は FAQ と化しています。ワードプレスでアップロードしたファイルはデータベースではなくファイルシステム内に格納(というか移動)されて保管されるのですが、この格納先ディレクトリはワードプレスの zip ファイルを展開しただけではできず、自分で wp-content/ フォルダの下に uploads/ という名前のサブディレクトリを作り、apache 等のウェブユーザーが書き込み可能な権限を設定する必要があります。大抵はこれで解決します。

そこまで設定しても「アップロードできない」ことはたまにあるようです。これもまだ FAQ といっていいレベルで、「WordPress アップロード エラー」あたりでググると、この問題に悩む人達がいかに多いかわかります。多くの場合はアップロードしようとすると「HTTP エラー。」というメッセージが表示されるようです。
wp_httperror00

原因はケース・バイ・ケースですが、プラグインの相性が悪いケースだとすると一度全てのプラグインを無効にしたら直ったとか、諦めて再導入したら直ったとか、色々試したけどそれでもまだ直らないとか、色んなケースがあるようです。特に注目したいのは最後のケースで、この問題は世に(ネットに)出回っている情報では解決出来ないケースが少なからずある、ということです。そういうのにぶち当たっちゃったら大変だよね・・・

なんて考えてたら、自分の所にも厄介なのがやってきました。何が厄介かって「以前は問題なくアップロードできていたのに、今アップロードすると100%失敗する」というもの。以前は動いていたんだから根本的な問題とは思えないが、今実際にアップロードすると常に「HTTP エラー。」になってしまいます。 念のためプラグインを全て無効にした上で試すなど、ウェブで見つけた情報を頼りに色々試してみましたが、解決には至りませんでした。


と、ここまでが今回のブログエントリの導入部です。ちょっとだけ WordPress に詳しくなった自分を試す意味も含めて、この「ググってもよくわからない」ケースを自力で解決すべく、WordPress の PHP デバッグを敢行してみました。以下はその結果として分かったことのまとめです。今回分かった原因と同じ原因で悩んでいる人がどれだけいるかわかりませんが、参考になれば。


まずは HTTPD のログをみます。アップロードが失敗するタイミングで /wp-admin/async-upload.php が HTTP コード 500 を返していることが分かりました。まずはこのファイルにデバッグライトを仕掛けて、、と。これを地道に繰り返して、どこでエラーが発生しているのかを特定していきます。
wp_httperror02

そして、エラー箇所をたどっていくと /wp-admin/includes/media.php 内の wp_read_image_metadata() 関数でエラーが発生していることを発見しました。
wp_httperror03

これ、WordPress のコア関数の1つなので、これがエラーと言われても・・・ と思いつつも、とりあえず Codex によると、この関数は /wp-admin/includes/image.php で定義されているようなのでこのファイルを開いて更にデバッグライトを埋め込んで・・・ 

ん?
wp_httperror01
 
なんと /wp-admin/includes/image.php のサイズが0バイト!そりゃここでコケるわな!


というわけで、今回の WordPress のファイルアップロードができない問題を自分の環境下で調査した結論としては、WordPress を構成するファイルの一部がたまたま壊れてしまっていたからでした。ファイルが壊れる原因は色々考えられるのですが、修正するには元のアーカイブなどから壊れる前の該当ファイルを取り出してコピーする、とかになると思います。それにしてもこれが原因だとすると、気付くのは結構たいへんだと思う・・・

そしてこういうことが起こっていると、ファイルアップロード時には(これも話をややこしくしてると思うけど)「HTTP エラー。」というエラーメッセージが表示される、ということも分かりました。このエラーメッセージはかなり汎用的に使われていて、必ずしも HTTP レベルでの問題とは思わないほうがいいかも。

なお、WordPress を導入したディレクトリから、
 # find . -size 0 -print
を実行すると、同じように何らかの原因でサイズがゼロになってしまったファイルの一覧を取り出すことができます。


WordPress のファイルアップロード時「HTTP エラー。」で悩んでいて、設定とかを見なおしたけどまだよく分からない、という場合は念のため調査してみるのもいいかもです。
 

「起動中の Amazon EC2 のインスタンスが消える!?」というオカルトな現象に2度ほど遭遇しました。

1度目はオペレーションミスか勘違いかと思ったんですが、2度あるとさすがに勘違いでは済まなくなります。

まあサーバーとしては実稼働中ではなかった(実はこれがクセモノだった、実稼働中ならもっと早く真相に気づけたかも)ので、深刻な状態にはならなかったのですが、この現象の正体を突き止めるべく調べてみました。


この怪現象はこんな感じ。Amazon EC2 にログインして、サーバーインスタンスを起動して使っていました(画面は管理コンソールです)。
2014010807


が、ある時に同じ管理コンソールを見てみると動いていたはずのインスタンスが消えています! まじかっ!?
2014010808


何がなんだかよく分からないけど、消えてしまったものはもう存在しないんだよね? それなら再度構成しないといけないのでインスタンスを作り直して・・・ なんてやってました。

このインスタンスは本稼働前の準備段階のインスタンスだったので実際にユーザーがアクセスしたりしていたわけではないのです。その意味では「ああ本番前でよかった」と問題にはならなかったのですが、、 すごいオチが隠れてました。 みなさん、分かります?



以下、真相です。

まずサーバーインスタンスは消えたわけではなく、ちゃんとずっと動いていました。なので本来は作り直す必要もありませんでした。

ではなぜ管理コンソールから消えていたのか?

これは単に異なるリージョンを見ていた、ということでした。

上記図を再度見直してみます。最初のインスタンス稼働中の管理コンソール画面ではリージョンは US East(N.Virginia) に設定されていました。つまりこのサーバーはバージニア州のデータセンター内で動いていた、ということです。
2014010801

次にサーバーが消えたように見えた管理コンソール画面のリージョンは US West(Oregon) になっています。つまりこちらはオレゴン州のデータセンターを参照していて、そこでは(作ってないから当たり前ですが)何も動いていない、ということでした。
2014010804


そっかー。なんだ、リージョンが切り替わったのに気付かなかっただけか。 めでたし、めでたし。


・・・ところでいつリージョンが切り替わったんだ?俺は何もしてないぞ。 
管理コンソール見るたびにリージョンなんか意識してないし、こんなことが頻繁に起こってたら、いつか本当にやらかす可能性があるぞ。

で、このリージョン切り替わりの謎をずっと探っていたのですが、最近になってやっと分かりました。結論としては
 ・AWS のリージョン情報がブラウザのクッキーに残っていて、
 ・AWS アカウントを複数持っていて、適宜切り替えて使っていた
のが原因でした。

まず AWS のリージョン情報(らしきもの)はブラウザのクッキーに残っていました。console.aws.amazon.com のクッキーとして3つの(それぞれの用途はわかりませんが) "us-west-2" リージョンが記録されていることを確認しています。
2014010805


次に自分は業務用と個人用と、2つの AWS アカウントを持っています。同じPCの同じブラウザで、ログインユーザーを切り替えて使っています。

たとえば、まず業務用アカウントを使ってログインしているとします。この時は US-East リージョンにあるインスタンスを使っています。
2014010801


この状態でいったんログアウトします。リージョン情報はクッキーに残っているので、次回ログイン時のデフォルトリージョンは US-East になるはずです(なので同じ業務用アカウントでログインした時はこの続きの管理コンソール画面が表示されるはずです)。

ここで個人用アカウントに切り替えてログインします。リージョン情報がクッキーに残っていて、どうやらユーザーごとの制御はしていないようなので個人用アカウントでも US-East リージョンで管理コンソールが表示されます。
2014010802

この状態から新しくサーバーインスタンスをリージョン指定なしで作り、それが US-West リージョンで作成された場合(或いは US-East 以外のリージョンを指定して作成した場合)、当然ですがインスタンスは US-East リージョンではなく、US-West リージョン内に作成されます。そしてこのタイミングで管理コンソールのリージョンも US-West に切り替わります。実際にインスタンスを作成しなくても、このようなオペレーションが行われたと仮定して(手動で) US-West リージョンに切り替えておくと、この後の現象を再現できます。
2014010803

この状態で個人用アカウントはログアウトし、再度業務用アカウントでログインし直します。するとクッキーに残ったリージョンは US-West なので、業務用アカウントでは前回のログアウト直前まで US-East で作業していたにも関わらず、US-West の画面になります。リージョンが異なるので当然ですがそこには直前の画面にあった稼働中のサーバーインスタンスはありません(見えません)。 これが「消えていた」ように見えたわけです。
2014010804

これが世にも恐ろしい「Amazon EC2 のインスタンスが消えた!」現象の原因だったように推測しています。

AWS アカウントを複数持っている人は少ないのか、利用者はリージョンを固定して使っているのか、ググってもこの現象が発生している、という情報を見つけることができませんでした。

上のはあくまで推測にすぎないのですが、現にクッキーの状態も推測にあっていたので、あながち間違ってないのかな、と思ってます。





 

このページのトップヘ