5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ateam引越し侍Advent Calendar 2016

Day 25

Apacheで動いていたwebサービスをH2Oに移行する方法

Posted at

apache+mod_phpで動いていたサービスをH2Oに置き換えたときに行ったことに
ついて書きます。

インストール

H2Oは以下の手順でインストールしました。OSはAmazon Linux 2016.09を使っています。
CentOS 6系のサーバーでも同じ様な手順になると思います。
まずは必要なライブラリ等をyumでインストールします。

$ yum -y gcc gcc-c++ curl curl-devel libarchive libarchive-devel expat expat-devel libyaml-devel zlib zlib-devel openssl-devel libyaml-devel bison 

CMakeをダウンロードしてきてインストールを行います。

    cd /usr/local/src
    wget http://www.cmake.org/files/v3.0/cmake-3.6.2.tar.gz
    tar -xvf cmake-3.6.2.tar.gz
    cd cmake-3.6
            ./bootstrap --prefix=/usr \
            --system-libs       \
            --mandir=/share/man \
            --no-system-jsoncpp \
            --docdir=/share/doc/cmake-3.6.2
    make
    make install

H2Oをgithubからcloneしてきて、ビルドします。今回はv2.1.0-beta4をビルドしました。

    cd /usr/local/src
    git clone https://github.com/h2o/h2o.git
    git checkout refs/tags/v2.1.0-beta4
   git submodule update --init --recursive
    cmake -DWITH_BUNDLED_SSL=on .
    make
    sudo make install

PHPの設定について

apacheではmod_phpでPHPを実行することが多いと思います。H2OにもFastCGIの機能は
ありますが、今回はphp-fpmを使って、unixsocketでH2Oと通信することにしました。
例えばのwordpressの設定は設定は以下の様になります。

/etc/h2o/conf/h2o.conf
pid-file: /var/run/h2o.pid
user: nobody

error-log: /var/log/h2o/error.log

# PHPの設定
file.custom-handler:
  extension: .php
  access-log:
    path: /var/www/logs/foo/fpm-access.log
    format: "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{X-Forwarded-For}i\" %{process-time}x %{duration}x"
  fastcgi.connect:
    port: /var/run/php-fpm/php-fpm.sock
    type: unix

# ドメイン設定
hosts:
  "foo.example.com:80":
    listen:
      port: 80
    access-log:
      path: /var/www/logs/foo/access.log
      format: "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{X-Forwarded-For}i\" %{process-time}x %{duration}x"
    paths:
      "/":
        file.dir: /var/www/html/foo/
        redirect:
          url: /index.php/
          internal: YES
          status: 307

Rewrite等の設定について

phpを動かすまでは上記の設定でいいのですが、移行作業となると今までApacheが行っていた
処理をH2Oに移す必要があります。その中で最も代表的なのが、apacheのmod_rewriteなの
ではないかと思います。簡単な設定なら、H2Oのconfにも書くことが出来ますが、正規表現
でマッチした内容をもとにrewriteをしている場合はh2oのmruby handlerを使って実現できます。

例えばapacheで以下のようなリダイレクト設定はH2Oでは以下のように書けます。

RewriteRule   ^/aaa/test/                                https://foo.example.com/aaa/                       [R=301,L]
RewriteRule   ^/bbb/test/                                https://foo.example.com/bbb/                       [R=301,L]

/etc/h2o/hook/rerirect_rule.rb
class RedirectRule

  def call(env)
    if !/^(aaaa|bbb)\/test/.match(env['PATH_INFO'])
      redirect_path = $1
      return [301, {'Location' => "http://foo.example.com#{redirect_path}"}, []]
    else
      [399, {}, []]
    end
  end
end

RedirectRule.new

mrubyでのハンドラーの記述はrackのインターフェースに沿ったものになります。
return する配列の1つ目がステータスコード、2つ目がレスポンスヘッダー、3つ目が
Bodyになります。ステータス399はH2Oで使える特別なステータスで、次のハンドラーに
処理を委譲します。

また引数のenvはhashオブジェクトでキーは以下の内容になります。

["REQUEST_METHOD", "SCRIPT_NAME", "PATH_INFO", "QUERY_STRING", "SERVER_NAME", "SERVER_ADDR", "SERVER_PORT", "HTTP_HOST", "REMOTE_ADDR", "REMOTE_PORT", "USER_NAME", "ENV_NAME", "HTTP_ACCEPT", "HTTP_COOKIE", "HTTP_USER_AGENT", "HTTP_CACHE_CONTROL", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_UPGRADE_INSECURE_REQUESTS", "rack.url_scheme", "rack.multithread", "rack.multiprocess", "rack.run_once", "rack.hijack?", "rack.errors", "SERVER_SOFTWARE"]

H2Oの前段にRPがある場合はもっとヘッダー
が増えると思いますが、rewrite等の作業で良く使うのは、"PATH_INFO"と"HTTP_USER_AGENT"だと思います。

他のサーバーの対してmod_rewriteでproxyをmrubyで実現する場合はhttp_request関数を使用します。

RewriteRule ^/contact(.*)$  http://foo.example.net/contact$1                 [QSA,NE,P,L]
/etc/h2o/hook/proxy_rule.rb
class ProxyRule
  def call(env)
    if /^\/contact(\.*)/.match(env["PATH_INFO"])
      rewrite_path = $1
      headers = {}
      env.each do |key, value|
        if /^HTTP_/.match(key)
          headers[$'] = value
        end
      end
      headers['Content-Type'] = env['CONTENT_TYPE']
      headers['X-Forwarded-Host'] = env['HTTP_HOST']
      headers['X-Forwarded-For'] = env['REMOTE_ADDR']
      headers['User-Agent'] = env['HTTP_USER_AGENT']
      return  http_request(
                "http://foo.example.net/contact#{rewrite_path}?#{env["QUERY_STRING"]}",
                method:  env["REQUEST_METHOD"],
                headers: headers,
                body:    env["rack.input"]
              ).join
    end
  end
end

ProxyRule.new

envに入ってきているヘッダー情報はクライアントから送られているヘッダーの情報と違ってきていますので、
proxy先のアプリケーションによっては、必要なヘッダー情報を合わせる必要があります。上記の例ではContent-Type
などのヘッダーを書き換えています。

ヘッダー情報の追加について

ブラウザがページを回遊している際に同じ画像やCSSを読み込まないようにexpireヘッダーを付加することが
あります。Apacheの場合では以下のように記述します。

    ExpiresActive On
    <FilesMatch "\.(gif|jpg|png|js)$">
        ExpiresDefault "access plus 1 month"
        Header set Cache-Control "max-age=604800"
    </FilesMatch>

H2Oでは先ほどのrewriteと同じようにmruby handlerで制御することができます。

/etc/h2o/hook/add_expire_header.rb
class AddExpireHeader
  def call(env)
    headers = {}
    if /\.(css|js|png|jpg|gif)/.match(env["PATH_INFO"])
      headers["cache-control"] = "max-age=86400"
      headers["Expires"] = "30d"
    end
    [399, headers, []]
  end
end

AddExpireHeader.new

アクセス制限について

管理画面など、特定のIP以外は閲覧させたいなく場合も出てきます。Apacheでは以下の方法で制御しています。

    <Location /wp_login.php>
        Require ip 169.254.0.0/24
        Require ip 10.1.0.0/16
    </Location>

    <Location /wp_admin>
        Require ip 169.254.0.0/24
        Require ip 10.1.0.0/16
    </Location>

H2Oでは、ACLを行うDSLがあるので、それを使用して、アクセス制限を行うことができます。

/etc/h2o/hook/acl.rb
ALLOW_HOSTS = %w(
169.254.0.0/24 10.1.0.0/16
)

require "trie_addr.rb"
trie = TrieAddr.new.add(ALLOW_HOSTS)

acl {
  deny { !trie.match?(addr) && (path.start_with?("/wp-login") ||  path =~ /wp-admin/) }
}

まとめ

apacheのconfの一部をH2Oで実現する方法を見てきました。特に当初はPHPファイルをfast-cgiで動かして
いたようなサービスだと、機能実現のために、apacheのrewriteのボリュームが大きくなる傾向になるのでは
ないかと思います。
EOLを迎えたPHPをPHP7にして、ついでにwebサーバーを変更したいような場合では、
mrubyによるrewriteやproxyはapacheのconfほど簡潔に表現できるわけではありませんが、人の目で見て
理解しやすい記述ができるのではないかと思います。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?