(更新:2017/09)Install on Ubuntu
(更新:2013/02)deflate_module, expires_module+ETAG, アクセス負荷が酷い時の確認方法
(更新:2013/01)スクリプトサンプルをtextで表示させたい
(更新:2012/11)Apache Bench, Alternative PHP Cache, FastCGI, mod_pagespeed
(作成:2012/05)
OSSのHTTPサーバとしては有名過ぎるくらい有名なので特に紹介する事も無く。
お仕事でもよく使うので、ちょっと設定備忘録でも書いておこうかなと。
Install on Ubuntu
さくさくと。
# apt-get install apache2
関連パッケージも一応メモ。
- 追加パッケージ
apache2-bin
apache2-data
apache2-utils
libapr1
libaprutil1
libaprutil1-dbd-sqlite3
libaprutil1-ldap
liblua5.2-0
ssl-cert
Apache 2では *.conf
の置き方が昔と変わったみたい。
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
ports.conf
仮想ホスト含めたリスニングポート設定を入れておくみたい。mods-enabled
Apacheモジュール設定用らしい。conf-enabled
グローバル設定の一部用だとか。sites-enabled
仮想ホスト設定用だそうだ。
3つのディレクトリ内には、それぞれ対応する mods-available/
, conf-available/
, sites-available/
フォルダ内の実体からリンクせよ、と。
まあ、何でもかんでも httpd.conf
にぶっ込んでた過去のやり方は可読性低かったからなぁ。納得はできる。
PHP 7.0
PHPも使えるようにしてみる。
# apt-get install php
関連パッケージはこちら。
- 追加パッケージ
libapache2-mod-php7.0
php-common
php7.0
php7.0-cli
php7.0-common
php7.0-json
php7.0-opcache
php7.0-readline
インストール途中で Creating config file
が連発してたんだけど、 /etc/apache2/
配下のディレクトリに色々追加された。成程こういう事か。
取り敢えずドキュメントルートにお約束の phpinfo();
などを。
<?php
//// /var/www/html/phpinfo.php
phpinfo();
?>
userdir
( /home/*/public_html/
有効にするやつ) を使う場合はこう。
# a2enmod userdir
Enabling module userdir.
To activate the new configuration, you need to run:
systemctl restart apache2
#
php7.0.conf
見れば解るんだけれど、ユーザディレクトリでPHP使いたいなら <IfModule ...>
- </IfModule>
をコメントアウトしろ、とあるのでそうする。
#### /etc/apache2/mods-enabled/php7.0.conf
# Running PHP scripts in user directories is disabled by default
#
# To re-enable PHP in user directories comment the following lines
# (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it
# prevents .htaccess files from disabling it.
#<IfModule mod_userdir.c>
# <Directory /home/*/public_html>
# php_admin_flag engine Off
# </Directory>
#</IfModule>
apache再起動。
# systemctl restart apache2
Install on CentOS
何ていうか深く考える必要も無いかな。
# yum install httpd
必要最低限の設定はこんなものか。
#### /etc/httpd/conf/httpd.conf
ServerAdmin root@localhost
ServerName (サーバFQDN)
## 省略 ##
# 文字コード固定
AddDefaultCharset UTF-8
## 省略 ##
ユーザディレクトリを使いたい場合はこんな設定。
<IfModule mod_userdir.c>
UserDir public_html
</IfModule>
## 省略 ##
# http://www.hyakki.local/~riyo/ みたいなアドレスでアクセスできる。
# ユーザ毎のコンテンツ置き場は~/public_html/以下。
<Directory /home/*/public_html>
AllowOverride FileInfo AuthConfig Limit
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
<Limit GET POST OPTIONS PROPFIND>
Order allow,deny
Allow from all
</Limit>
<LimitExcept GET POST OPTIONS PROPFIND>
Order deny,allow
Deny from all
</LimitExcept>
</Directory>
## 省略 ##
CGIを使いたい場合はこんな設定。
LoadModule cgi_module modules/mod_cgi.so
## 省略 ##
# これはhttp://www.hyakki.local/cgi-bin/ でCGIが使えるようになる設定。
ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
<Directory "/var/www/cgi-bin">
AllowOverride None
#Options None
Options +ExecCGI +FollowSymLinks -Indexes
Order allow,deny
Allow from all
</Directory>
## 省略 ##
AddHandler cgi-script .cgi
## 省略 ##
モジュール整理
設定ファイルの途中 LoadModule
てな書式で沢山のモジュールが読み込まれている。このあたりも整理して不要なものをコメントアウトなりしておくと、リソース消費も軽減されるしセキュアにもなるので調べておくと良い。
取り敢えずここでは以前調べたものでも載せておこうかな。
auth_module
HTTP認証を提供する。ht*
ファイルで使う。htaccess認証とかやりたければこれを有効化する。access_module
アクセス元のIP/ホスト情報によるアクセス制御をするモジュール。Allow/Deny from
でホスト毎の制限をする時とかに使う。alias_module
URLのエイリアスを作るためのモジュール。リダイレクト機能とかも提供してる。userdir_module
ユーザディレクトリを提供する。http://www.hyakki.local/~riyo/
とかで参照するディレクトリ。php4_module
Web上でPHP4を使うためのモジュール。利用時にはファイルハンドラ設定も必要。
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
rewrite_module
リクエストされたURLを別のURLに変更するモジュール。Redirect
に似てるけどもちっと高機能。setenvif_module
リクエスト側がよこす情報を正規表現で判断し処理を行なうためのモジュール。アクセスログの整理とかに便利。
追加モジュール
DoS攻撃対策
サーバ管理者の脅威なんてのは腐る程あるけれど、やっぱ第三者から攻撃に晒される「外部公開環境」を扱っている事実に他ならないと思う。作った人が使うだけなら脅威に晒される事なんて無いもんね。
で、その中でもDoS攻撃なんて手法はこれといった特殊な技術や特別なツールが無くても実行出来ちゃう。以前2chが某隣の半島からF5攻撃受けてたけど、つまりそういう攻撃(これは不特定多数がしDDoSかもだけど)。
取り敢えずDDoSは置いといて、同一IPからの攻撃であるDoS攻撃を検知するためのモジュール mod_dosdetector
というものがある。この前導入してみたので、そちらの紹介をば。
Install on CentOS
このモジュールはパッケージ化されていないので、ソースからのインストールになる。まずはソースからRPMを作ってくれる checkinstall
を導入。
# wget http://futuremix.org/downloads/checkinstall-1.6.1-1.x86_64.rpm
# rpm -ivh checkinstall-1.6.1-1.x86_64.rpm
続いて mod_dosdetector
のアーカイブを取得、展開してRPMを作成。
# wget http://cloud.github.com/downloads/tkyk/mod_dosdetector-fork/mod_dosdetector-fork-1.0.0.tar.gz
# tar xvfz mod_dosdetector-fork-1.0.0.tar.gz
# cd mod_dosdetector-fork-1.0.0/
# make
# checkinstall --exclude=/selinux
# rpm -i --replacefiles --nomd5 /usr/src/redhat/RPMS/x86_64/mod_dosdetector-fork-1.0.0-1.x86_64.rpm
httpd.conf
に LoadModules
が1つ追加された事を確認。
# less /etc/httpd/conf/httpd.conf
...
LoadModule dosdetector_module /usr/lib64/httpd/modules/mod_dosdetector.so
...
設定は httpd.conf
と別のファイルで作成すると分かり易いかな。
busyページがあれば ErrorDocument
にて設定。無ければ文字列でもOK。
検知したログについては access_log
等とは別のログファイルに書き出すと分かり易い。
#### /etc/httpd/conf.d/dosdetector.conf
<IfModule dosdetector_module>
DoSDetection On
DoSPeriod 30
DoSThreshold 100
DoSHardThreshold 300
DoSBanPeriod 60
DoSShmemName dosshm
DoSTableSize 100
<IfModule setenvif_module>
SetEnvIfNoCase Request_URI ".*(common|download|ajax|json|images|gif|jpe?g|icon?|js|css|png|html).*" NoCheckDoS
</IfModule>
##ErrorDocument 403 "Server is busy."
ErrorDocument 403 /error/busy.html
# send a 403 response with mod_rewrite
RewriteEngine On
RewriteCond %{ENV:SuspectHardDoS} =1
RewriteRule .* - [R=403,L]
LogFormat "%{SuspectHardDoS}e %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" dosdetector
CustomLog logs/dos_suspect_log dosdetector env=SuspectDoS
</IfModule>
因みにSSLサイトに仕込みたい場合は、 <VirtualHost _default_:443>
タグ内に上記設定を引用すればOK。設定が同じなら Include
が便利。
#### /etc/httpd/conf.d/ssl.conf
<VirtualHost _default_:443>
## 省略 ##
<IfModule dosdetector_module>
Include conf.d/dosdetector.conf
</IfModule>
</VirtualHost>
上記設定ならば単なるブラウザアクセスでも十分引っ掛かるので、実際にブラウザでアクセス→ F5
連打してみる。
何度か目にbusyページが表示されるようになれば成功。後は通常アクセスとDoS攻撃を見極めつつ閾値を調整していく。
deflate_module
コンテンツの圧縮通信をしてくれるモジュール。サーバのCPUリソースを食う代わりに、通信量がちょこっと減る。
#### /etc/httpd/conf/httpd.conf
<IfModule deflate_module>
DeflateCompressionLevel 5
AddOutputFilterByType DEFLATE application/javascript application/x-javascript text/javascript text/css text/xml text/html text/plain
# DeflateFilterNote Input instream
# DeflateFilterNote Output outstream
# DeflateFilterNote Ratio ratio
# LogFormat '"%r" %{outstream}n/%{instream}n (%{ratio}n%%) %{User-agent}i' deflate
# CustomLog logs/deflate_log deflate
</IfModule>
DeflateFilterNote
~ CustomLog
の部分は、圧縮状況をログ出力してくれる設定。最初のうちはこれで動作確認すると良いんじゃないかな。
expires_module + ETag
所謂キャッシュを活用するための設定で、期限切れ期間を定める。
#### /etc/httpd/conf/httpd.conf
<IfModule expires_module>
ExpiresActive On
ExpiresByType application/javascript "access plus 3 days"
ExpiresByType application/x-javascript "access plus 3 days"
ExpiresByType text/javascript "access plus 3 days"
ExpiresByType text/css "access plus 3 days"
ExpiresByType image/jpeg "access plus 5 days"
ExpiresByType image/png "access plus 5 days"
ExpiresByType image/gif "access plus 5 days"
ExpiresByType image/x-icon "access plus 5 days"
</IfModule>
これで期限切れまではブラウザのキャッシュを使うようになる。
それよりも更にファイルのETagヘッダを優先してキャッシュ更新をするらしい。不要ならETag削除すれば expires
のみで判断するようになる。なおデフォルトのETagは inode+更新日時+ファイルサイズ
でユニークなETag情報を作るらしい。複数台サーバで負荷分散している場合、inode
を含めるとサーバ毎にETagが変わってしまうので、以下のようにしてinode情報を省くと良い。
#### /etc/httpd/conf/httpd.conf
<Files ~ "\.(css|js|html?|xml|gz)$">
FileETag MTime Size
</Files>
<Files ~ "\.(gif|jpe?g|png|flv|mp4|ico)$">
FileETag None
</Files>
なお負荷分散サーバ間では更新日時が合致するようにファイル同期する仕組みを使うこと。lsyncd + rsyncが良いんじゃないかな。そちらは拙サイト「lsyncd」参照。
Alternative PHP Cache
PHPを高速化してくれるよ! やったね!
PHPの中間コードのキャッシングや最適化をしてくれるそうで、スクリプトに直接関わるような改修が行なわれるわけでもないので、バージョンが合うなら入れておくと良いかと。
# yum install pcre-devel
# pear install pecl/APC
#### /etc/php.d/apc.ini
[apc]
extension=apc.so
# service httpd restart
ちゃんと入ったか確認。
# php -i | grep apc
...
apc.enabled => On => On
...
故あってWordPress環境に仕込んでApache Benchで比較してみた結果。総リクエスト数1000、同時リクエスト数100。わりと凄い改善されてる。
デフォルト | +APC | |
---|---|---|
Failed Requests | 97 | 72 |
Requests per second | 14.34 | 26.33 |
Time per request | 69.753 | 37.986 |
FastCGI
PHP処理プロセスをメモリに常駐させて、Apacheでなくそっちで処理してもらうためのモジュール。これでも処理の高速化と負荷軽減が期待出来る、らしい。
# yum install --enablerepo=epel fcgi mod_fcgid
#### /etc/httpd/conf/httpd.conf
<VirtualHost *:80>
## 省略 ##
<Directory "/var/www/html">
Options FollowSymLinks Includes ExecCGI
AddHandler fcgid-script .php
FCGIWrapper /usr/bin/php-cgi .php
## 省略 ##
</Directory>
</VirtualHost>
# service httpd restart
すると php-cgi
てのが常駐する。
# ps -ef | grep php
apache 19643 19339 3 19:03 ? 00:01:30 /usr/bin/php-cgi
うむ苦しゅうない。さてApache Benchの結果は……。
+APC | +APC+FastCGI | |
---|---|---|
Failed Requests | 72 | 0 |
Requests per second | 26.33 | 13.21 |
Time per request | 37.986 | 75.675 |
……APC導入前よりも速度が低下しているような。その代わりリクエスト失敗数は減ってる。別プロセスにすることで処理の正確さが上がった?
試験環境はVMゲストで、CPUもメモリも少なめというのが起因しているのかも。環境が環境なら、多分効果はあると思うんだけれど……。
mod_pagespeed
Google先生が一晩で頑張ってくれました。何かねjsやcssの最適化やインライン化、Extend cacheとか、高速化に繋がる諸々をよろしくやってくれるらしい。
# wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_x86_64.rpm
# rpm -ivh mod-pagespeed-stable_current_x86_64.rpm
# service httpd restart
さて結果は……。
+APC+FastCGI | +APC+FastCGI+mod_pagespeed | |
---|---|---|
Failed Requests | 0 | 999 |
Requests per second | 13.21 | 9.8 |
Time per request | 75.675 | 102.05 |
何が起きたのか判らないのぜ☆
えーっとこれはどう判断すれば良いのかなぁ。多分、こうしたチートは doc_length
値ほぼ無視するんだろうなぁ。だからFailed Requestsが大きい。んで、導入前より速度低下した理由だけれど……FastCGIと相性が良くないのかも知れない。
FastCGIを抜いてみるとこんな感じの結果に。
+APC | +APC+mod_pagespeed | |
---|---|---|
Failed Requests | 72 | 196 |
Requests per second | 26.33 | 55.86 |
Time per request | 37.986 | 17.903 |
エラーの頻度が上昇……このために速度が上昇しているきらいもあるので、ちょっと比較にならないね。他の試験方法を検討しようかな。
Tips
Apache設定よもやま
スクリプトサンプルをtextで表示させたい
サンプルスクリプトを .pl
だの .php
だのの型式でそのままサーバに置くと、実行可能なサーバなら実行されちゃったりする。サンプルとして表示さ せるのにこれだとマズイが、いちいちHTMLに書き起こすのはナンセンスだしHTMLエンティティを考えた表示スクリプト書くのも馬鹿らしいしファイル名を .txt
に変更するのすら面倒……なんてな人にお勧め。
#### .htaccess
AddHandler default-handler .pl
ForceType text/plain
<Files "*.tgz">
ForceType None
</Files>
取り敢えず AddHandler
なPerlスクリプトは、 AddHandler
で上書きしてやれば良い。 PHPなんかの新規MIMEタイプ指定によるものは、該当ディレクトリ以下全てのMIMEタイプを強制上書(拡張子をも無視)してやると良い。 しかし例えば .tgz
等、普通にDLさせてあげたいモノに関しては Files
セクションにて ForceType
を無効にしてやる事も可能。
ログ解析よもやま
公開したらログ解析しながらサーバ/サービスの健康状態を維持してあげる必要がある。けれど、どう診てあげればサーバは喜んでくれるんだろう……といった所を、インフラ視点からつらつらと。
解析アプリの話とかは基本的にしない予定。どちらかと言えば、インフラ的な調査をメインに。なお、ログ形式はデフォルト combined
でのお話。
アクセス負荷が酷い時の確認方法
サーバ/サービス監視なんかでは、負荷が高いよーとか帯域使い過ぎだよーとかしか判らないので、原因の特定が難しい。けれど、公開サーバに関して言えば、もし公開時の状態が非常に安定していたなら、そういった際の原因はやっぱり外部からの攻撃が殆どだったりする。特亜からのアタックとか、割と脅威。
というわけで、アクセスログからアクセス上位のIPとアクセス数を簡単に抜き出すコマンド。
# cat /var/log/httpd/access_log | grep -v -e ".gif" -e ".jpg" -e ".png" -e ".js" -e ".css" | awk '{print $1;}' | sort | uniq --count | sort -r | head -10
簡単に説明すると。
access_log
に関して静的コンテンツ類(但しHTMLは除く)をgrep -vで削り、スペース区切りの1単語目(IP情報)を抜き取ってソート。
uniq
でIP情報をカウントしつつ一意化、結果は「カウント数 IPアドレス」の順で表示されるので、逆順ソートして最初の10行を表示する。
といった感じ。
アタックかけられてたりすると、この際のカウント数が明らかにハネ上がってたりする。例えば上位5〜9位くらいのアクセスが300/日に対し、上位1位が10000/日だったり。
そしたら今度は、アクセス数の多いIPが何にアクセスしているのか確認したくなってくる。以下はその上位の宛先とアクセス数を簡単に抜き出すコマンド。
# cat /var/log/httpd/access_log | grep -e "192.168.1.XXX" | awk -F '"' '{print $2;}' | awk '{print $2;}' | sort | uniq --count | sort -r | head -10
簡単に説明すると。
例えばアタック元IPが 192.168.1.XXX
だったとして、まずはそれで絞り込み。
ダブルクォート区切りの2単語目を抜き取ればHTTPコマンドが判るので、そこから更にスペース区切りの2単語目でアクセス先ファイルを抜き取る。
後はファイル名ソート、 uniq
+カウント、カウント別ソート、最初の10行を表示といった感じの流れ。
これにより、何に対しアクセスが多いかが判る。
静的コンテンツ宛なら影響は少ないかも知れないが、それでも多量アクセスはリソース消費の元なので、E-tagなんかでブラウザキャッシュ効かせると効果的かも。凶悪なのがPHPスクリプトなど動的コンテンツへの多量アクセス。既知の脆弱性を狙った攻撃やゼロディ攻撃、はたまた純粋にDoSアタックかも知れない。まあいずれにせよ、偏ったアクセスは悪ですわ。
そんな感じで特定して、明らかに悪さしていそうなIPに関しては、FWなどでDROPするなり対策を取りましょう。
Apache Bench
Apacheチューニングのお供に。
# apt-get install apache2-utils
実行方法と結果はこんな感じ。
$ ab -n 1000 -c 100 http://www.hyakki.local/
オプションの意味は以下の通り。
- -n: 総リクエスト数
- -c: 同時発行リクエスト数
で、結果。
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking www.hyakki.local (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: Apache
Server Hostname: www.hyakki.local
Server Port: 80
Document Path: /
Document Length: 0 bytes
Concurrency Level: 100
Time taken for tests: 69.753 seconds
Complete requests: 1000
Failed requests: 97
(Connect: 0, Receive: 0, Length: 97, Exceptions: 0)
Write errors: 0
Non-2xx responses: 97
Total transferred: 268345 bytes
HTML transferred: 25511 bytes
Requests per second: 14.34 [#/sec] (mean)
Time per request: 6975.302 [ms] (mean)
Time per request: 69.753 [ms] (mean, across all concurrent requests)
Transfer rate: 3.76 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 4 5 1.4 5 24
Processing: 699 6863 2517.5 7141 19761
Waiting: 698 6861 2517.8 7125 19761
Total: 703 6868 2517.8 7145 19765
Percentage of the requests served within a certain time (ms)
50% 7145
66% 7683
75% 8039
80% 8262
90% 9174
95% 11113
98% 12098
99% 12669
100% 19765 (longest request)
以下がポイントになるかな。
項目 | 意味 |
---|---|
Complete requests | 総リクエスト数。 |
Failed requests | 処理に失敗したリクエスト数。直下にある括弧内の項目は、その原因を表す。※1 |
Non-2xx responses | 200系レスポンス以外の発生した数。 |
Requests per second | 秒間平均のリクエスト処理数。 |
Time per request | リクエスト平均の実行時間。 |
※1 | |
---|---|
Connect | 接続の失敗。 |
Receive | データ受信の失敗。 |
Length | doc_length値と実データ量が異なる。 |
Exceptions | Apacheエラー。 |