BIGIPで豪華なSorry画面を実装する方法

BIGIPを使っているサイトでは、WEBサーバー等が障害でロードバランスできない時にSorry画面を応答させるように実装することが多いと思います。

 

基本的にはiRuleやVirtualServerのFailbackHostの設定で実装していると思います。

 

Sorryサーバーを別途作成し、リダイレクト転送させる場合は、豪華なSorry画面(画像やCSS、JavaScriptなどを用いた見た目が鮮やかな画面)の応答を実装できると思いますが、BIGIPだけでは容易ではないと思います。

 

豪華な画面を応答させるために、仕方なくSorry画面をPDFやJPEG画像に加工したりしていませんか?

Sorry画面用のWEBサーバーを別途構築したりしていませんか?

この記事を参考にすれば、その苦労から解放されるかもしれません。

 

今回はBIGIPだけを用いて豪華なSorry画面を応答させる方法を紹介しようと思います。

1.豪華なSorry画面の特徴を把握しよう

WEBサイトを作成した経験がある方ならイメージできると思いますが、Sorry画面を見た目鮮やかにするということは、HTMLテキストだけでは難しくなります。鮮やかな画面にするために画像(gif、jpeg、png等)を用いるからです。

画像は画面上に配置したり、CSSでbackground-urlとして登録したりします。また、動的な画面を作ろうと思うと、JavaScriptなどを埋め込みます。

つまり、HTMLテキストのみではなく、JPEG画像もSorry画面応答時に同時に処理するように実装することになります。

2.Sorry画面を応答させるアルゴリズム

豪華なSorry画面を応答するにはBIGIPのiRule機能を使います。

iRuleで実装しなければならない動きは以下です。

 

(1)HTTPリクエストを受信

 →(2)Nodeを割り当てる

  →(3)ロードバランスに失敗

   →(4)Sorry画面を応答

 

つまり、HTTP_REQUESTイベントとLB_FAILDイベントを使います。

(1)~(2)をHTTP_REQUESTイベントで実装し、(3)~(4)をLB_FAILDイベントで実装します。

 

※注意:BIGIPv11ではLB_FAILDでのHTTP応答はMSS×2に制限されています。

when HTTP_REQUEST {

    LB処理

}

 

when LB_FAILD {

    Sorry処理

}

LB_FAILDイベントはLB処理に失敗した場合に自動で割り振られるイベントです。

しかし、これだけではSorry画面はテキストベースの寂しい画面になってしまいます。HTMLテキストベースのSorry画面ならば、これで十分なのですが、豪華な画面にする場合はさらに考慮が必要です。

 

簡単なSorry画面のソースを作ってみます。

<html>
  <head>
    <title>Sorry</title>
    <link rel="stylesheet" type="text/css" href="example.css">
  </head>
  <body>
    <h1>Sorry!</h1>
    <img src="image.jpg" />
  </body>
</html>

これを上記のiRuleに当てはめてみると、このHTMLファイルは処理できますが、example.css、image.jpgの処理ロジックがありません。

このSorry画面では、ブラウザからのリクエストを4回受信するため、上記のようなiRuleのアルゴリズムでは、LB_FAILD内に応答ロジックをコーディングすると、4回のLB_FAILD(ロードバランスの失敗)を発生することになります。

 

ブラウザからのリクエストは以下4回となる

1.ブラウザ(http://example.com/example.html) → BIGIP(302 Redirect Location http://example.com/sorry.html)

2.ブラウザ(http://example.com/sorry.html) → BIGIP(200 Response)

3.ブラウザ(http://example.com/example.css) → BIGIP(200 Response)

4.ブラウザ(http://example.com/image.jpg) → BIGIP(200 Response)

 

そこで、上記のiRuleのアルゴリズムを見直してみます。

(1)HTTPリクエストを受信

 →(2)Nodeを割り当てる

  →(3)ロードバランスに失敗

   →(4)Sorry画面へリダイレクト

    →(5)Sorry画面を応答

(1)~(2)、(5)をHTTP_REQUESTイベントで実装し、(3)~(4)をLB_FAILDイベントで実装します。

when HTTP_REQUEST {

    if { Sorry応答 } {

      Sorry応答処理

      return

    }

    LB処理

}

 

when LB_FAILD {

    Sorry処理 ・・・ 302 Redirect

}

このようなアルゴリズムにすることで、LB処理前にSorry画面、もしくは、Sorry画面をRefererとするCSS等を処理できるため、LB_FAILDは1回のみ発生させることができます。

 

では、次にSorry応答処理部分を詳しく解説します。

if { Sorry応答 } {

    Sorry応答処理

    return

 }

まず、Sorry応答であることを判別する方法を考えないといけません。

LB_FAILDイベントでSorry画面へリダイレクトさせますから、URLを使います。

 if { [HTTP::path] ends_with "sorry.html" || [HTTP::header Referer] ends_with "sorry.html" }  {

    Sorry応答処理

    return

 }

[HTTP::path]と[HTTP::uri]等、URLの情報を使って判別します。

上記例で[HTTP::uri]を使っていない理由はGETのパラメータ部分は判別に必要ないためです。

ends_withは値の末尾が一致するかどうかという意味です。

また、Sorry.htmlから呼ばれるCSSやIMAGEに対応するため、[HTTP::header Referer]も判別に使います。

 

一見、これで処理できそうですが、この記載では課題が残ります。

Sorry画面から呼び出したCSSにbackground-uri等の指定がある場合、RefererがCSSになってしまいます。

そこで、以下のように、Sorry画面へのリダイレクト時にPATHを工夫します。

when HTTP_REQUEST {

     if { [HTTP::path] starts_with "/sorry/" || [HTTP::header Referer] starts_with "/sorry/" }  {

      Sorry応答処理

      return

    }

    LB処理

}

 

when LB_FAILD {

    HTTP::respond 302 "Location" "https://[HTTP::host]/sorry/sorry.html"

}

LB_FAILDイベントでのリダイレクト時に「/sorry/」等、Sorry画面であることを示すPATHを入れます。

HTTP_REQUESTイベントでは、URIの前方一致で「/sorry/」等の判別PATHであるかでSorry応答であるかどうかを判別させます。

あとは、Sorry応答処理の部分の実装方法です。

豪華なSorry画面は複数のコンテンツで構成されますから、コンテンツを複数処理できるようにコーディングします。

when HTTP_REQUEST {

     if { [HTTP::path] starts_with "/sorry/" || [HTTP::header Referer] starts_with "/sorry/" }  {

        if {  [HTTP::path] ends_with "sorry.htm" } {

            HTTP::respond 200 content [ifile get sorry.html] Content-Type "text/html"

        } elseif {  [HTTP::path] ends_with "example.css" } {

            HTTP::respond 200 content [ifile get example.css] Content-Type "text/css"

        } elseif
        ~省略~
        } else {
            HTTP::respond 404
        }

 

        return

    }

    LB処理

}

今回の例ではiFileとして、コンテンツを登録しています。

上記のようにIF文でコンテンツの数だけ処理を記載することで、複数のコンテンツに対応できますね。

 

 

しかし、プログラマやシステムエンジニアの方ならば、これはナンセンスに感じることでしょう。

コンテンツが増える度にiRuleの書き換えが必要になり、拡張性が乏しいですね。

switch構文で分岐を記載するiRuleもDevCentral等で紹介されているようですが、もっとシンプルにしたいところです。

 

そこで、次にもっと簡単に複数のコンテンツを処理できるようにするテクニックを説明します。

 

 

BIGIPには、DataGroupという設定項目があります。

BIGIPの管理GUIでは、Local Traffic > iRules > Data Group List と選択します。

 

DataGroupでは、String値をキーにString値の値を登録できる機能を持っています。

これを利用し、

sorry_contents というDataGroupを作ります。

そして、 コンテンツをキー値に、String値をコンテンツタイプにしたListを作成します。

<tmshで確認した登録内容>

ltm data-group internal sorry_contents {
    records {
        sorry.html {
            data text/html
        }
        example.css {
            data text/css
        }

        ~省略~

    }

    type string

}

DataGroupを作成後、以下のようにiRuleを記載します。

when HTTP_REQUEST {

     if { [HTTP::path] starts_with "/sorry/" || [HTTP::header Referer] starts_with "/sorry/" }  {

        if { [class match [HTTP::path] ends_with sorry_contents] } {  ・・・①

            set ::content_name [lindex [split [HTTP::path] /] end] ・・・②
            HTTP::respond 200 content [ifile get $::content_name] "Content-Type" [class match -value $::content_name equals sorry_contents]   ・・・③

        } else {

            HTTP::respond 404

        }

        return

    }

    LB処理

}

[class match 比較対象 比較演算子 DataGroup名]

という構文は、DataGroupのキーから比較演算子に該当するものがあればTrueになります。

上記の例の①部分では、HTTPのPATHの後方一致でsorry_contentsというDataGroupから検索して、合致するものの存在有無を確かめています。

 

次に一致した場合、②の部分で、コンテンツ名を取り出します。

[set 変数名 値]という構文は、変数に値を挿入します。

[lindex 配列 end] という構文は、配列の最後の値を取り出すという意味です。

[split 文字列 セパレータ]という構文は、セパレータ区切りで文字列を配列化します。

この2つの構文を使って、HTTPのPATHを分解して、コンテンツ名を変数にセットしています。

 

次に②の部分で取得したコンテンツ名でiFileを選択し、更に、sorry_contentsというDataGroupからString値(今回の例では、コンテンツタイプ)を取り出します。

[class match -value 比較対象 比較演算子 DataGroup名]

という構文で、DataGroupのキーが比較演算子に該当する値を取り出すことができます。

3.豪華なSorry画面で、システム障害時でも安心感を実現ください。

BIGIPでは上記のようなテクニックを駆使することで豪華なSorry画面を実現することが可能となります。

テキストベースの貧しいSorry画面しか出せなくて困っている方は、ぜひ参考にしてください。