[Docker, Cloud, Java] Dynamic load balancing for Docker based JavaEE microservices on Oracle Container Cloud

原文はこちら。
https://community.oracle.com/community/cloud_computing/oracle-cloud-developer-solutions/blog/2017/04/04/dynamic-load-balancing-for-docker-based-javaee-microservices-on-oracle-container-cloud

このエントリではDockerベースのJava EEマイクロサービスをHAProxyを使ってHA/負荷分散モードで実行する方法をご紹介します。全てOracle Container Cloud上で実行します。
HAProxy - The Reliable, High Performance TCP/HTTP Load Balancer
http://www.haproxy.org/
Oracle Container Cloud Service
https://cloud.oracle.com/ja_JP/container
簡単に概要をご紹介します。
  • WildFly Swarmを使うJava EEマイクロサービス
    シンプルなJAX-RSベースのRESTアプリケーション
  • HAProxy
    アプリケーションの複数インスタンスへの負荷分散に利用
  • Docker
    個々のコンポーネント、つまりマイクロサービスおよび負荷分散サービスをDockerイメージにパッケージング
  • Oracle Container Cloud
    Oracle Container Cloud上でサービスを構成し、スケーラブルかつ負荷分散しながらサービスを実行します。

Application

このアプリケーションは、株価を取得する非常にシンプルなJAX-RSを使うREST APIです。
@GET 
public String getQuote(@QueryParam("ticker") final String ticker) { 


    Response response = ClientBuilder.newClient(). 
            target("https://www.google.com/finance/info?q=NASDAQ:" + ticker). 
            request().get(); 


    if (response.getStatus() != 200) { 
        //throw new WebApplicationException(Response.Status.NOT_FOUND); 
        return String.format("Could not find price for ticker %s", ticker); 
    } 
    String tick = response.readEntity(String.class); 
    tick = tick.replace("// [", ""); 
    tick = tick.replace("]", ""); 


    return StockDataParser.parse(tick)+ " from "+ System.getenv("OCCS_CONTAINER_NAME"); 
}  
WildFly Swarmは単なるJava EEランタイムとして利用しています。シンプルなWARベースのJava EEプロジェクトを作成し、Swarm Mavenプラグインを使って、必要なパーツを自動的に検出して構成し、WARからfat JARを作成するという、"魔法"を織り込みます。
WildFly Swarm
http://wildfly-swarm.io/
<build> 
    <finalName>occ-haproxy</finalName> 
    <plugins> 
         
        <plugin> 
            <groupId>org.wildfly.swarm</groupId> 
            <artifactId>wildfly-swarm-plugin</artifactId> 
            <version>1.0.0.Final</version> 
            <executions> 
                <execution> 
                    <goals> 
                        <goal>package</goal> 
                    </goals> 
                </execution> 
            </executions> 
        </plugin> 
 
        <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-compiler-plugin</artifactId> 
            <version>3.1</version> 
            <configuration> 
                <source>1.7</source> 
                <target>1.7</target> 
                <compilerArguments> 
                    <endorseddirs>${endorsed.dir}</endorseddirs> 
                </compilerArguments> 
            </configuration> 
        </plugin> 
        <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-war-plugin</artifactId> 
            <version>2.3</version> 
            <configuration> 
                <failOnMissingWebXml>false</failOnMissingWebXml> 
            </configuration> 
        </plugin> 
        <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-dependency-plugin</artifactId> 
            <version>2.6</version> 
            <executions> 
                <execution> 
                    <phase>validate</phase> 
                    <goals> 
                        <goal>copy</goal> 
                    </goals> 
                    <configuration> 
                        <outputDirectory>${endorsed.dir}</outputDirectory> 
                        <silent>true</silent> 
                        <artifactItems> 
                            <artifactItem> 
                                <groupId>javax</groupId> 
                                <artifactId>javaee-endorsed-api</artifactId> 
                                <version>7.0</version> 
                                <type>jar</type> 
                            </artifactItem> 
                        </artifactItems> 
                    </configuration> 
                </execution> 
            </executions> 
        </plugin> 
    </plugins> 
</build> 
WildFly Swarmの代わりに、別のJava EEベースのfat JARスタイルのフレームワーク、例えばPayara MicroやKumuluzEE、Apache TomEE embeddedなどを利用することもできます。
Payara Micro
http://www.payara.fish/payara_micro
KumuluzEE
https://ee.kumuluz.com/
TomEE Embedded
http://tomee.apache.org/advanced/tomee-embedded/index.html
では詳細に入っていきましょう。

Dynamic load balancing

Oracle Container Cloudを使用した水平方向へのスケーラビリティは非常に単純で、アプリケーションの追加インスタンスを生成するだけです。これは、アプリケーション・コンシューマ(ユーザーまたは他のアプリケーション)が個々のインスタンスの詳細を取り扱う必要がないよう、ロードバランサを用意していて、ロードバランサの座標(ホスト /ポート)だけを知っていればよい場合に有効です。問題は、ロードバランサが新しく生成されたアプリケーションインスタンス/コンテナを認識しないことです。Oracle Container Cloudは統合スタックを作成することができます。これにより、バックエンド(この例ではREST API)と(HAProxy)ロードバランサ・コンポーネントの両方を単一ユニットとして構成し、簡単に管理および編成できるだけでなく、動的なHAProxyの化身(Avatar)のためにレシピを提供することができます。

HAProxy on steroids

Oracle Container Cloud Githubリポジトリのアーティファクトを使い、confdおよびrunit用にカスタマイズされたDockerイメージの上に、特殊化された(Docker)HAProxyイメージを作成します。
HAProxy Load Balancer Image
https://github.com/oracle/docker-images/tree/0c7f9a90e4420e313f3aeba865cd064c2d138463/ContainerCloud/images/haproxy
confd - Manage local application configuration files using templates and data from etcd or consul
https://github.com/kelseyhightower/confd
runit - a UNIX init scheme with service supervision
http://smarden.org/runit/
confdは構成管理ツールであり、今回は実行中のアプリケーションインスタンスを動的に発見するために使用します。Oracle Container Cloudサービス内のネイティブ・サービス・ディスカバリに問い合せ、新しいアプリケーションインスタンスを検出する、新しいアプリケーション・インスタンスを検出するミニ・サービス・ディスカバリ・モジュールと考えてください。
Oracle® Cloud Using Oracle Container Cloud Service
Managing Entries in the Service Discovery Database to Enable Container Communication
http://docs.oracle.com/en/cloud/iaas/container-cloud/contu/managing-entries-service-discovery-database-enable-container-communication.html

Configuring our application to run on Oracle Container Cloud

Build Docker images

まず必要なDockerイメージを作成します。デモのため、Docker Hubのパブリックリポジトリ(abhirockzz)を使いますが、ご自身のパブリックリポジトリやプライベートリポジトリをお使いいただくことができます。
Docker engineが起動していることを確認してください。

Build the application Docker image

Dockerfileは以下のような感じです。
FROM anapsix/alpine-java:latest 
RUN mkdir app  
WORKDIR "/app" 
COPY target/occ-haproxy-swarm.jar . 
EXPOSE 8080 
CMD ["java", "-jar", "occ-haproxy-swarm.jar"] 
以下のコマンドを実行してイメージを作成します。
docker build –t <registry>/occ-wfly-haproxy:<tag> . e.g. docker build –t abhirockzz/occ-wfly-haproxy:latest .

Build Docker images for runit, confd, haproxy

依存するイメージを順にビルドしていきましょう。まずは


以下のコマンドを実行します。
cd ContainerCloud/images 
cd runit 
make image 
cd ../confd 
make image 
cd ../nginx-lb 
make image 

Check your local Docker repository

ここまででローカルのDockerリポジトリに必要な全てのイメージがそろいました。


Push Docker images

ではDockerイメージをレジストリにPushしましょう(今回の場合、筆者のパブリックDockerレジストリにPushします)。これにより、Oracle Container Cloudからアプリケーションスタックのデプロイ時にPullすることができます。Pushは以下のコマンドを実行します。
各セットアップ毎にレジストリやリポジトリの名前を調整する必要があります。
docker login 
docker push abhirockzz/occ-wfly-haproxy 
docker push abhirockzz/haproxy 
docker logout

Create the Stack

Stack作成のために、docker-composeに非常に似ているYAMLフォーマットの構成ファイルを利用します。この例では、サービス名(rest-api)をロードバランサ(HAProxy)サービスで参照しています。


これは、Oracle Container Cloudサービス・レジストリ内のキーに関する情報をHAProxyサービスに提供します。このレジストリを使って(前述の)confdサービスが実際に新しいアプリケーション・インスタンスを自動検出します。8080は公開されたポート番号ですが、これ自体がサービスレジストリのキーの一部でもあるため、ハードコードされています。

StacksメニューからNew Stackを選択してプロセスを開始します。


Advanced EditorをクリックしてYAMLコンテンツを入力します。



これで個々のサービスが見えるはずです。Stack Nameを指定して[Save]をクリックします。



Initiate Deployment


Stacksメニューに戻って、新規作成されたStackを[Deploy]をクリックしてデプロイします。


ロードバランシングをテストするため、rest-api(バックエンド)サービスの3個のインスタンスをデプロイし、1個の負荷分散(HAProxy)サービスと紐付けます。


数秒後、全てのコンテナが実行中状態になっていることを確認できるはずです。今回は、この3個のサービスと1個のha-proxyロードバランサのインスタンスが実行中になっています。


Service Discoveryメニューをチェックして書くインスタンスが存在することを確認しましょう。前述の通り、これはconfdサービスがアプリケーションの新規インスタンスを自動検出することでイントロスペクトされています(自動的にこのレジストリに追加されます)。

Test

HAProxyを経由してアプリケーションにアクセスすることができます。HAProxyコンテナが実行しているホストのパブリックIPを知っておく必要があります。(下図の通り)バックエンドのアプリケーションにアクセスするためにポート番号8886が既にマップ済みです。


以下のcURLコマンドを実行して確認しましょう。
for i in `seq 1 9`; do curl -w "\n" -X GET "http://<haproxy-container-public-IP>:8886/api/stocks?ticker=ORCL"; done 
9回呼び出し、(3個のインスタンス間で)負荷分散が動作していることを確認します。以下がその結果です。ハイライトされたテキストはレスポンスを返したインスタンスを指し示しています。3個のインスタンス間で負荷分散が均等になされています。


Scale up... and check again

スタックをスケールアップすることも簡単に繰り返し実施できます。デプロイメントに移動し、Change Scalingをクリックします。


少々したら、追加されたアプリケーション・インスタンス(5個目のインスタンス)が確認できることでしょう。コマンドを再度実行し、負荷分散が想定通り動作していることを確認しましょう。


0 件のコメント:

コメントを投稿