LINE Engineering
Blog

HBase アプリケーションローカルテスティング

Zhao Yu 2017.07.14

こんにちは、LINEでGame Platformを開発している 趙 です。LINEではHBaseを様々な場面で活用しており、今回はGame Platformでの利用例を紹介させていただきます。

LINE Game PlatformはHBaseをメインストレージとして利用します。私達が新規の機能開発時にHBaseを利用する場合、ローカル開発環境におけるユニットテストなどのアプリケーションテストは専用のリモートクラスターで実行されるHBaseに接続していました。リモートクラスターはHadoopアドミニストレータが構築、管理してくれます。チームメンバーとHBaseを使ったアプリケーションの数が増えるにしたがい、このテスト方式は以下に示す問題があり、不便に感じていました。

  • 共通クラスターを使うので、他人のテストとの衝突が起きやすい。
  • 案件によって、利用するHBaseのバージョンが違います。複数のテスト専用クラスターを持つのは非効率的。
  • クラスターは社内にありますが、IP制限などにより社外での作業、デバッグが不便。
  • これらの問題は、ローカル開発環境でHBaseのインスタンスを起動すれば問題を解決できます。これから紹介する内容は、私たちが使っている二つ方式を紹介したいと思います。HBaseTestingUtilityを利用して、テストする時にHBaseを起動する方法とDockerを利用してHBaseをローカル環境に設置する方法です。

    HBaseTestingUtility

    HBaseはHBaseTestingUtilityというテスティングツールを提供してくれています。以下のようにプロジェクトにdependencyを追加すればすぐ使用できます。
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-testing-util</artifactId>
        <version>${hbaseversion}</version>
        <scope>test</scope>
    </dependency>
    

    HBaseTestingUtilityはユニットテスト時、Mini Cluster(StandaloneモードのHBase)を起動することが可能です。テーブルの作成、削除などの管理作業も可能です。以下はHBase 0.98バージョンの一例です。

    private HBaseTestingUtility hBaseTestingUtility = new HBaseTestingUtility();
    private byte[] table = Bytes.toBytes("testTable");
    private byte[] family = Bytes.toBytes("f1");
    private HTable hTable;
    
    @Before
    public void before() throws Exception {
    // Mini Clusterを起動します
        hBaseTestingUtility.startMiniCluster();
    // tableを作成します
        hBaseTestingUtility.createTable(table, family);
    // Mini Clusterの配置で、table connectorを作成します
        hTable = new HTable(hBaseTestingUtility.getConfiguration(), table);
    }
    
    @After
    public void after() throws Exception {
    // Mini Clusterを終了します
        hBaseTestingUtility.shutdownMiniCluster();
    }
    
    @Test
    public void testPutAngGet() throws Exception {
        byte[] targetRow = Bytes.toBytes("row1");
        byte[] targetColumn = Bytes.toBytes("c1");
        byte[] value = Bytes.toBytes("v1");
    
        Put put = new Put(targetRow);
        put.add(family, targetColumn, value);
        hTable.put(put);
    
        Get get = new Get(targetRow);
        assertThat(hTable.get(get).getValue(family, targetColumn), is(value));
    }
    

    HBaseバージョンごとにHBaseTestingUtilityがありますので、バージョン問題はありません。そして、システムとHBaseの設定は必要ありません。テスト時のみHBaseを起動します、そのためCIツールとの相性は高いです。ただし、幾つかの問題が存在し、実際に使うときにちょっと不便なところもあります。 まずは、Mini Clusterの起動は少し時間がかかります(私の環境では10秒ほど)。複数またはプロジェクト全体のテストを行うときには、最初のテストが始まる前に一回だけでMini Clusterを起動すれば、まったく問題ありませんが、単体テストをするときに退屈な感じがします。つぎに、テスト、特にデッバグをする時に、データを確認したい場合がありますが、Mini Cluster中のデータ確認は難しいです。そして、依存関係の競合問題です。たとえば、hbase-testing-utilはあるライブラリAの古いバージョンに依存します、他のコードはAの新しいバージョンに依存します。もしAの古いバージョンと新しいバージョンのインタフェースが異なる場合、依存関係の競合が発生します。私たちの場合は依存ライブラリのバージョンを合わせないので、Apache Maven Shade Pluginを使い、この競合問題を解除しました。このプラグインはdependencyをリネームして、一つのuber-jarに取り込むことができます。

    <relocations>
      <relocation>
        <pattern>com.google.guava</pattern>
        <shadedPattern>shadow.hbase.guava</shadedPattern>
      </relocation>
      <relocation>
        <pattern>org.joda</pattern>
        <shadedPattern>shadow.hbase.joda</shadedPattern>
      </relocation>
      ...
    </relocations>
    

    ( このセクションのです )

    Docker

    ローカル環境にHBaseを設置する方法はたくさんありますが、Dockerを使えば比較的簡単です。Dockerストアに複数バージョン・モードのHBaseイメージがあります。そして、特定のバージョン・モードのHBaseイメージを作成することも難しくありません。以下は私がHBaseイメージを作成するときに注意したところについて紹介したいと思います。

    基本イメージの選択

    基本イメージはHBaseの運行環境を提供します。テスト専用の構成で、複雑な設定は必要ありません、JDKがあれば十分です。openjdkのような、JDK、JAVA_HOMEを設定したイメージを選択したら楽です。

    HBaseの設定

    まずはHBaseのモードを決めます。standaloneモードはライトウエート、簡易設定のモードです。このモードでは、すべてのHBaseデーモンはひとつのJVMに存在します。各デーモンのポートは毎回起動するたびに変更します。もしプロジェクトをDockerイメージにいれてテストするなら問題ないですが、私たちのケースは、プロジェクトは外にあります。たくさんのポートまたはポートの区間を開くことは理想的ではないので、pseudo-distributedモードを使っています。pseudo-distributedモードはノード一つだけを使ってるdistributedモードです。そしてファイルシステムですが、アプリケーションテストをするだけなので、HDFSを使う必要性はありません。以下はHBase設定ファイルhbase-site.xmlの一例です。
    <configuration>
      <!-- distributed モード -->
      <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value>
      </property>
      <!-- hdfsは使わない -->
      <property>
        <name>hbase.rootdir</name>
        <value>file:///hbase/hbase</value>
      </property>
      <!-- other configurations ... -->
    </configuration>
    

    HBaseの起動

    テストアプリケーションによって必要なデーモンが違いますが、ここは最小限のデーモンのみを起動します。
    ${HBASE_HOME}/bin/hbase-daemon.sh start zookeeper
    ${HBASE_HOME}/bin/hbase-daemon.sh start regionserver
    ${HBASE_HOME}/bin/hbase-daemon.sh start master
    

    起動スクリプトをイメージに入れて、Docker起動する時に、このスクリプトを実行してHBaseを起動します。 containerの外から通信するため、必要なポートを露出します。

    componentport(HBase 0.98 default)
    zookeeper2181
    master60000
    region60020

    以下は起動コマンドの一例です。

    docker run -h HOSTNAME -itd -p 2181:2181 -p 60000:60000 -p 60020:60020 IMAGE
    

    最後はhostnameをホストファイルにいれます。

    127.0.0.1 HOSTNAME
    

    HBaseのアクセス

    アプリケーションからこのHBaseに接続する時に、zookeeper quorumはHOSTNAME、portは2181です。HBaseのデータを確認する時に、containerに入って、HBase shellを利用して簡単に確認することはできます。

  • Docker psコマンドでCONTAINER IDを獲得します
  • Docker attachコマンドでcontainerに入ります
  • $HBASE_HOME/bin/hbase shellコマンドでHBase shellを起動します
  • Dockerを利用するテスト方式は、HBaseTestingUtilityの起動が遅い、依存競合などの問題はありませんが、設定と運用は少し複雑です。そして、CIツールでテストを行う時にDockerの起動・停止、複数イメージを同時に起動する時のポート管理も気をつけなければならないです。
    ( このセクションのです )
    HBaseアプリケーションローカルテスト方法を紹介しました。私たちはこの二つ方法を同時に使っています。ローカル開発、テストはDockerを利用します、CIツールでのインテグレーションテストは頑張ってdependency競合問題を解決して、HBaseTestingUtilityを使っています。

    HBase

    Zhao Yu 2017.07.14

    Add this entry to Hatena bookmark

    リストへ戻る