※ 本記事は、Alex Somogyiによる”WebLogic Server JTA in a Kubernetes Environment“を翻訳したものです。

2023年3月24日


このブログ投稿では、Kubernetes環境で実行されているWebLogic Serverグローバル・トランザクションについて説明します。まず、WebLogic Server Transaction Manager(TM)が分散トランザクションを処理する方法を確認します。次に、WebLogic Kubernetes Operatorを使用してKubernetesクラスタで実行されているWebLogic Serverドメインにデプロイされるトランザクション・アプリケーションの例を示します。

 

WebLogic Server Transaction Manager

概要

WebLogic Server Transaction Manager(TM)は、Java Enterprise Edition(Java EE)Java Transaction API(JTA)をサポートするWebLogic Serverでのトランザクション処理モニターの実装です。Java EEアプリケーションでは、JTAを使用してグローバル・トランザクションを管理し、データベースやメッセージング・システムなどのリソース・マネージャへの変更が1つの単位で完了するか、元に戻されます。

この項では、特にネットワーク通信および関連構成に関するWebLogic Server TMについて簡単に説明します。これは、Kubernetes環境でトランザクションを調べるときに役立ちます。この記事では説明しないTM機能、最適化および構成オプションが多数あります。詳細は、次のWebLogic Serverのドキュメントを参照してください。:

· WebLogic Server TMに関する一般情報は、WebLogic Server JTAのドキュメントを参照してください。

  • JavaトランザクションAPIの詳細は、Java EE JTA仕様を参照してください。

WebLogic Serverでのトランザクションの処理方法

WebLogic Server TMがトランザクションを処理する方法の基本的な理解を得るために、仮想アプリケーションについて説明します。トランザクションを開始し、データベース表にレコードを挿入し、Java Messaging Service(JMS)キュー宛先にメッセージを送信するサーブレットで構成されるWebアプリケーションについて考えてみます。JDBCおよびJMSリソースの更新後、サーブレットはトランザクションをコミットします。次の図は、サーバーおよびリソースのトランザクション参加者を示しています。

トランザクションの伝播

トランザクション・コンテキストは、サーバー間およびアプリケーションによるリソースへのアクセス時に伝播される状態になります。このアプリケーションでは、コミット時のトランザクション・コンテキストは次のようになります。

ドメイン名とサーバー名で識別されるサーバー参加者には、内部TM通信に使用されるURLが関連付けられています。これらのURLは通常、サーバーのデフォルトのネットワーク・チャネルまたはデフォルトのセキュアなネットワーク・チャネルから導出されます。

トランザクション・コンテキストには、javax.transaction.Synchronizationコールバックが登録されているサーバー参加者に関する情報も含まれます。JTA同期APIは、トランザクションに対して2フェーズ・コミット処理を開始する前にTMがSynchronization.beforeCompletion()メソッドを呼び出すコールバック・メカニズムです。Synchronization.afterCompletion(int status)メソッドは、トランザクションの最終ステータス(コミット済、ロールバック済など)でトランザクション処理が完了した後に呼び出されます。

トランザクション完了

TMがトランザクションをコミットするように指示されると、TMは引き継ぎ、トランザクションの完了を調整します。2フェーズ・コミット・プロトコルを実行するトランザクション・コーディネータとして、サーバー参加者の1人が選択されます。コーディネータは、残りの下位サーバーに、登録された同期コールバックを処理し、リソースを準備、コミットまたはロールバックするように指示します。トランザクション例を調整するために使用するTM通信チャネルを次の図に示します。

破線の矢印は、コーディネータと従属サーバー間の非同期RMIコールを表します。Synchronization.beforeCompletion()通信は、下位サーバー間で直接行われる場合があります。TMは、トランザクションを伝播するためにアプリケーションで使用されなかったネットワーク・チャネルを確立する可能性があるため、アプリケーション通信は概念的に内部TM通信とは切り離されていることを指摘することも重要です。TMでは、サーバーのデフォルトのネットワーク・チャネルの構成方法に応じて、様々なプロトコル、アドレスおよびポートを使用できます。

構成に関する推奨事項

サーバー・ネットワーク・アドレス、永続ストレージおよびサーバー・ネーミングに関連するTM構成の推奨事項がいくつかあります。

サーバー・ネットワーク・アドレス

前述のとおり、サーバー参加者は、トランザクション・コンテキストに含まれるURLを使用して相互に検索します。TM URLに使用されるネットワーク・チャネルは、IPアドレスが変更される可能性があるノード、ポッドまたはコンテナの再起動後に解決可能なアドレス名で構成することが重要です。また、TMにはサーバー間直接通信が必要なため、複数のIPアドレスに解決されるクラスタ・アドレスまたはロード・バランサ・アドレスは使用しないでください。

トランザクション・ログ

調整サーバーは、障害後のトランザクション・リカバリ処理に使用されるトランザクション・ログ(TLOG)の状態を保持します。サーバー・インスタンスが別のノードに再配置される可能性があるため、TLOGはネットワーク/レプリケートされたファイル・システム(NFS、SANなど)またはOracle RACなどの高可用性データベースに存在する必要があります。詳細は、『高可用性ガイド』を参照してください。

クロスドメイン・トランザクション

WebLogic Serverドメインにまたがるトランザクションは、クロスドメイン・トランザクションと呼ばれます。クロスドメイン・トランザクションでは、特にドメインがパブリック・ネットワークで接続されている場合の追加の構成要件が導入されます。

サーバー・ネーミング

TMは、ドメイン名とサーバー名の組合せを使用してサーバー参加者を識別します。そのため、名前の競合を防ぐために、各ドメインに一意の名前を付ける必要があります。サーバー参加者名の衝突により、実行時にトランザクションがロールバックされます。

セキュリティ

パブリック・ネットワークによって接続されたサーバー参加者は、セキュアなプロトコル(たとえば、t3s)および認可チェックを使用して、TM通信が正当であることを確認する必要があります。このデモンストレーションでは、これらのトピックについて詳しく説明します。Kubernetesサンプル・アプリケーションの場合、すべてのTM通信はプライベートKubernetesネットワークで実行され、非SSLプロトコルを使用します。

ドメイン間トランザクションのセキュリティの構成の詳細は、『Fusion Middleware Oracle WebLogic Server JTAアプリケーションの開発』ドキュメントのセキュアなドメイン間およびドメイン内トランザクション通信の構成に関する章を参照してください。

Kubernetes上のWebLogic Server

WebLogic ServerとKubernetesの統合を改善するために、OracleはオープンソースのWebLogic Kubernetes Operatorをリリースしました。WebLogic Kubernetes Operatorは、WebLogic Serverドメインの作成と管理、様々なロード・バランサとの統合、および追加の機能をサポートしています。詳細は、GitHubプロジェクトのページ(https://github.com/oracle/weblogic-kubernetes-operator)および関連するブログ(https://blogs.oracle.com/weblogicserver/how-to-weblogic-server-on-kubernetes)を参照してください。

トランザクション・アプリケーション・ウォークスルーの例

Kubernetesでの分散トランザクションの実行を示すために、単一のKubernetesクラスタで実行されている複数のWebLogic Serverドメインにデプロイされる簡易トランザクション・アプリケーションをステップ・スルーします。この例で使用した環境は、Kubernetes v1.9.6を含むDocker Edge v18.05.0-ceを実行しているMacです。

Docker Edgeをインストールして起動した後、「Preferences」ページを開き、「Advanced」タブ(8 GiBまで)でDockerで使用可能なメモリーを増やし、「Kubernetes」タブでKubernetesを有効にします。変更を適用すると、DockerとKubernetesが起動されます。ファイアウォールの内側にいる場合は、「Proxies」タブで適切な設定を追加する必要がある場合もあります。実行すると、Kubernetesのバージョン情報をリストできるようになります。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.6", GitCommit:"9f8ebd171479bec0ada837d7ee641dec2f8c6dd1", GitTreeState:"clean", BuildDate:"2018-03-21T15:21:50Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.6", GitCommit:"9f8ebd171479bec0ada837d7ee641dec2f8c6dd1", GitTreeState:"clean", BuildDate:"2018-03-21T15:13:31Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}

サンプル・ファイル・システムのパス名を短くするために、入力ファイル、オペレータ・ソースおよびバイナリ、永続ボリュームなどの作業ディレクトリが$HOME/K8SOPの下に作成されます。環境変数$K8SOPを使用してディレクトリを参照できます。

$ export K8SOP=$HOME/k8sop
$ mkdir $K8SOP

WebLogic Kubernetes Operatorのインストール

次のステップでは、weblogic-kubernetes-operatorイメージを構築してインストールします。https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-operators/installation/にあるインストール手順を参照してください。

この例では、weblogic-kubernetes-operator GitHubプロジェクトは$K8SOP/srcディレクトリ($K8SOP/src/weblogic-kubernetes-operator)の下にクローニングされます。また、Dockerイメージを構築する場合は、インストール・ドキュメントで指定された”some-tag”のかわりに”local”タグを使用します。

$ mkdir $K8SOP/src
$ cd $K8SOP/src
$ git clone https://github.com/oracle/weblogic-kubernetes-operator.git
$ cd weblogic-kubernetes-operator
$ mvn clean install
$ docker login
$ docker build -t weblogic-kubernetes-operator:local --no-cache=true .

オペレータ・イメージを構築すると、ローカル・レジストリに表示されます。

$ docker images weblogic-kubernetes-operator
REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
weblogic-kubernetes-operator   local               42a5f70c7287        10 seconds ago      317MB

次のステップでは、オペレータをKubernetesクラスタにデプロイします。この例では、create-weblogic-operator-inputs.yaml ファイルを変更して、ターゲット・ネームスペース(weblogic)を追加し、正しいオペレータ・イメージ名を指定します。

属性 Value
targetNamespaces default,weblogic
weblogicOperatorImage weblogic-kubernetes-operator:local
javaLoggingLevel WARNING

 

変更した入力ファイルを$K8SOP/create-weblogic-operator-inputs.yaml に保存します。

次に、create-weblogic-operator.shスクリプトを実行し、変更されたcreate-weblogic-operator.yaml入力ファイルへのパスとオペレータ出力ディレクトリのパスを指定します。

$ cd $K8SOP
$ mkdir weblogic-kubernetes-operator
$ $K8SOP/src/weblogic-kubernetes-operator/kubernetes/create-weblogic-operator.sh -i $K8SOP/create-weblogic-operator-inputs.yaml -o $K8SOP/weblogic-kubernetes-operator

スクリプトが完了すると、オペレータ・ポッドが実行中であることが表示されます。

$ kubectl get po -n weblogic-operator
NAME                                 READY     STATUS    RESTARTS   AGE
weblogic-operator-6dbf8bf9c9-prhwd   1/1       Running   0          44s

WebLogicドメインの作成

WebLogic Serverドメインを作成する手順は、https://oracle.github.io/weblogic-kubernetes-operator/quickstart/create-domainに記載されています。Oracle Container Registryからローカル・レジストリhttps://oracle.github.io/weblogic-kubernetes-operator/userguide/base-images/#obtain-standard-images-from-the-oracle-container-registryにWebLogic Serverイメージをプルする手順に従います。Dockerストアでライセンス契約に同意すると、イメージをプルできます。

$ docker login container-registry.oracle.com

$ docker pull container-registry.oracle.com/middleware/weblogic:12.2.1.4

次に、ドメイン(weblogic/weblogic1)の管理資格証明を保持するKubernetesシークレットを作成します。

$ kubectl -n weblogic create secret generic domain1-weblogic-credentials --from-literal=username=weblogic --from-literal=password=weblogic1

ドメインの永続ボリュームの場所は$K8SOP/volumes/domain1 にあります。

$ mkdir -m 777 -p $K8SOP/volumes/domain1

次に、$K8SOP/src/weblogic-kubernetes-operator/kubernetes/create-weblogic-domain-inputs.yaml サンプル入力ファイルをカスタマイズし、次の属性を変更します。

属性 Value
weblogicDomainStoragePath {full path of $HOME}/k8sop/volumes/domain1
domainName domain1
domainUID domain1
t3PublicAddress {your-local-hostname}
exposeAdminT3Channel true
exposeAdminNodePort true
namespace weblogic

 

更新された入力ファイルを$K8SOP/create-domain1.yaml に保存した後、次のようにcreate-weblogic-domain.sh スクリプトを起動します。

$ $K8SOP/src/weblogic-kubernetes-operator/kubernetes/create-weblogic-domain.sh -i $K8SOP/create-domain1.yaml -o $K8SOP/weblogic-kubernetes-operator

create-weblogic-domain.sh スクリプトが完了すると、Kubernetesは管理サーバーおよびクラスタ化された管理対象サーバー・インスタンスを起動します。しばらくすると、実行中のポッドが表示されます。

$ kubectl get po -n weblogic
NAME                                        READY     STATUS    RESTARTS   AGE 
domain1-admin-server                        1/1       Running   0          5m 
domain1-cluster-1-traefik-9985d9594-gw2jr   1/1       Running   0          5m 
domain1-managed-server1                     1/1       Running   0          3m 
domain1-managed-server2                     1/1       Running   0          3m

次に、WebLogic Server管理コンソールを使用して実行中の管理サーバーにアクセスし、資格証明weblogic/weblogic1 を含むURL http://localhost:30701/consoleを使用してドメインの状態を確認します。次のスクリーンショットは、「Servers」ページを示しています。

「Administration Console Servers」ページには、domain1内のすべてのサーバーが表示されます。各サーバーには、特定のサーバー・インスタンスに対して定義されたKubernetesサービス名に対応するリスニング・アドレスがあることに注意してください。サービス名は、domainUID(domain1)およびサーバー名から導出されます。

これらのアドレス名はKubernetesネームスペース内で解決でき、リスニング・ポートとともに、各サーバーのデフォルトのネットワーク・チャネルを定義するために使用されます。前述のように、デフォルトのネットワーク・チャネルURLはトランザクション・コンテキストに伝播され、TMによって分散トランザクション調整のために内部的に使用されます。

サンプル・アプリケーション

KubernetesでWebLogic Serverドメインを実行したので、分散トランザクション処理の検証に使用できるサンプル・アプリケーションについて説明します。例をできるだけ単純にするために、サーバー間のトランザクション伝播と同期コールバック処理の範囲に制限されます。これにより、リソース・マネージャの構成やJDBCまたはJMSクライアント・コードの記述の複雑さを必要とせずに、サーバー間トランザクション通信を検証できます。

アプリケーションは、サーブレット・フロント・エンドとRMIリモート・オブジェクトの2つの主要コンポーネントで構成されます。サーブレットは、URLのリストを含むGETリクエストを処理します。グローバル・トランザクションを開始してから、各URLでリモート・オブジェクトを起動します。リモート・オブジェクトは、beforeCompletionおよびafterCompletionコールバック・メソッドのstdoutにメッセージを出力する同期コールバックを登録するだけです。最後に、サーブレットはトランザクションをコミットし、各RMIコールに関する情報とグローバル・トランザクションの結果を含むレスポンスを送信します。

次の図は、Kubernetesクラスタのdomain1サーバーでのサンプル・アプリケーションの実行を示しています。サーブレットは、管理サーバーの外部ポートを使用して起動されます。サーブレットは、トランザクションを開始し、ローカル同期オブジェクトを登録し、Kubernetesの内部URL(t3://domain1-managed-server1:8001 および t3://domain1-managed-server2:8001)を使用して管理対象サーバーに対する登録操作を起動します。

TxPropagateサーブレット

前述のように、サーブレットはトランザクションを開始し、指定された各サーバーURLでRemoteSync.register()リモート・メソッドを呼び出します。その後、トランザクションがコミットされ、結果がコール元に返されます。

package example;
 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
 
import weblogic.transaction.Transaction;
import weblogic.transaction.TransactionHelper;
import weblogic.transaction.TransactionManager;
 
@WebServlet("/TxPropagate")
public class TxPropagate extends HttpServlet {
  private static final long serialVersionUID = 7100799641719523029L;
  private TransactionManager tm = (TransactionManager) 
      TransactionHelper.getTransactionHelper().getTransactionManager();
 
  protected void doGet(HttpServletRequest request, 
      HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out = response.getWriter();
 
    String urlsParam = request.getParameter("urls");
    if (urlsParam == null) return;
    String[] urls = urlsParam.split(",");
 
    try {
      RemoteSync forward = (RemoteSync) 
          new InitialContext().lookup(RemoteSync.JNDINAME);
      tm.begin();
      Transaction tx = (Transaction) tm.getTransaction();
      out.println("<pre>");
      out.println(Utils.getLocalServerID() + " started " + 
          tx.getXid().toString());
      out.println(forward.register());
      for (int i = 0; i < urls.length; i++) {
        out.println(Utils.getLocalServerID() + " " + tx.getXid().toString() + 
            " registering Synchronization on " + urls[i]);
        Context ctx = Utils.getContext(urls[i]);
        forward = (RemoteSync) ctx.lookup(RemoteSync.JNDINAME);
        out.println(forward.register());
      }
      tm.commit();
      out.println(Utils.getLocalServerID() + " committed " + tx);
    } catch (NamingException | NotSupportedException | SystemException | 
        SecurityException | IllegalStateException | RollbackException | 
        HeuristicMixedException | HeuristicRollbackException e) {
      throw new ServletException(e);
    }
  }

リモート・オブジェクト

RemoteSyncリモート・オブジェクトには、伝播されたトランザクション・コンテキストでjavax.transaction.Synchronizationコールバックを登録する単一のメソッドregisterが含まれています。

RemoteSync インタフェース

次に、example.RemoteSyncリモート・インタフェース定義を示します。

package example;
 
import java.rmi.Remote;
import java.rmi.RemoteException;
 
public interface RemoteSync extends Remote {
  public static final String JNDINAME = "propagate.RemoteSync";
  String register() throws RemoteException;
}
RemoteSyncImpl 実装

example.RemoteSyncImplクラスは、example.RemoteSyncリモート・インタフェースを実装し、SynchronizationImplという内部同期実装クラスを含みます。beforeCompletionおよびafterCompletionメソッドは、伝播されたトランザクションのサーバーID(ドメイン名およびサーバー名)およびXid文字列表現を含むメッセージをstdoutに書き込むだけです。

静的mainメソッドは、RemoteSyncImplオブジェクトをインスタンス化し、サーバーのローカルJNDIコンテキストにバインドします。メイン・メソッドは、次に示すように、アプリケーションがApplicationLifecycleListenerを使用してデプロイされるときに起動されます。

package example;
 
import java.rmi.RemoteException;
 
import javax.naming.Context;
import javax.transaction.RollbackException;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
 
import weblogic.jndi.Environment;
import weblogic.transaction.Transaction;
import weblogic.transaction.TransactionHelper;
 
public class RemoteSyncImpl implements RemoteSync {
 
  public String register() throws RemoteException {
    Transaction tx = (Transaction) 
        TransactionHelper.getTransactionHelper().getTransaction();
    if (tx == null) return Utils.getLocalServerID() + 
        " no transaction, Synchronization not registered";
    try {
      Synchronization sync = new SynchronizationImpl(tx);
      tx.registerSynchronization(sync);
      return Utils.getLocalServerID() + " " + tx.getXid().toString() + 
          " registered " + sync;
    } catch (IllegalStateException | RollbackException | 
        SystemException e) {
      throw new RemoteException(
          "error registering Synchronization callback with " + 
      tx.getXid().toString(), e);
    }
  }
 
  class SynchronizationImpl implements Synchronization {
    Transaction tx;
   
    SynchronizationImpl(Transaction tx) {
      this.tx = tx;
    }
   
    public void afterCompletion(int arg0) {
      System.out.println(Utils.getLocalServerID() + " " + 
          tx.getXid().toString() + " afterCompletion()");
    }
 
    public void beforeCompletion() {
      System.out.println(Utils.getLocalServerID() + " " + 
          tx.getXid().toString() + " beforeCompletion()");
    }
  }
 
  // create and bind remote object in local JNDI
  public static void main(String[] args) throws Exception {
    RemoteSyncImpl remoteSync = new RemoteSyncImpl();
    Environment env = new Environment();
    env.setCreateIntermediateContexts(true);
    env.setReplicateBindings(false);
    Context ctx = env.getInitialContext();
    ctx.rebind(JNDINAME, remoteSync);
    System.out.println("bound " + remoteSync);
  }
}

ユーティリティ・メソッド

Utilsクラスには、ローカル・サーバーIDを取得する静的メソッドと、URLを指定して初期コンテキスト・ルックアップを実行する静的メソッドがいくつか含まれています。初期コンテキスト検索は匿名ユーザーで呼び出されます。これらのメソッドは、サーブレットとリモート・オブジェクトの両方で使用されます。

package example;
 
import java.util.Hashtable;
 
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
public class Utils {
 
  public static Context getContext(String url) throws NamingException {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
        "weblogic.jndi.WLInitialContextFactory");
    env.put(Context.PROVIDER_URL, url);
    return new InitialContext(env);
  }
 
  public static String getLocalServerID() {
    return "[" + getDomainName() + "+"
        + System.getProperty("weblogic.Name") + "]";
  }
 
  private static String getDomainName() {
    String domainName = System.getProperty("weblogic.Domain");
    if (domainName == null) domainName = System.getenv("DOMAIN_NAME");
    return domainName;
  }
}

ApplicationLifecycleListener

アプリケーションがWebLogic Serverインスタンスにデプロイされると、ライフサイクル・リスナーのpreStartメソッドが呼び出され、RemoteSyncリモート・オブジェクトが初期化およびバインドされます。

package example;
 
import weblogic.application.ApplicationException;
import weblogic.application.ApplicationLifecycleEvent;
import weblogic.application.ApplicationLifecycleListener;
 
public class LifecycleListenerImpl extends ApplicationLifecycleListener {
 
  public void preStart (ApplicationLifecycleEvent evt)
      throws ApplicationException {
    super.preStart(evt);
    try {
      RemoteSyncImpl.main(null);
    } catch (Exception e) {
      throw new ApplicationException(e);
    }
  }
}

アプリケーション・デプロイメント・ディスクリプタ

アプリケーション・アーカイブには、ApplicationLifecycleListenerオブジェクトを登録するための次のweblogic-application.xml デプロイメント記述子が含まれています。

<?xml version = '1.0' ?>
<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-application http://www.bea.com/ns/weblogic/weblogic-application/1.0/weblogic-application.xsd" xmlns="http://www.bea.com/ns/weblogic/weblogic-application">
   <listener>
    <listener-class>example.LifecycleListenerImpl</listener-class>
    <listener-uri>lib/remotesync.jar</listener-uri>
  </listener>
</weblogic-application>

アプリケーションのデプロイ

サンプル・アプリケーションは、サポートされている様々なデプロイメント・メカニズムを使用してデプロイできます(https://blogs.oracle.com/weblogicserver/best-practices-for-application-deployment-on-weblogic-server-running-on-kubernetes-v2 を参照)。この例では、WebLogic Server管理コンソールを使用してアプリケーションをデプロイします。

アプリケーションがtxpropagate.earという名前のアプリケーション・アーカイブにパッケージ化されているとします。まず、txpropagate.earをdomain1永続ボリュームの場所($K8SOP/volumes/domain1/applications)にあるアプリケーション・ディレクトリにコピーします。その後、管理コンソールの「デプロイメント」ページからアプリケーションをデプロイできます。

EARファイルのパスは管理サーバー・コンテナ内の/shared/applications/txpropagate.EAR であり、/sharedは$K8SOP/volumes/domain1 で作成した永続ボリュームにマップされます。

EARをアプリケーションとしてデプロイし、管理サーバーおよびクラスタにターゲット指定します。

次のページで、「終了」をクリックしてアプリケーションをデプロイします。アプリケーションのデプロイ後、「デプロイメント」表にそのエントリが表示されます。

アプリケーションの実行

domain1のサーバーにアプリケーションをデプロイしたので、分散トランザクション・テストを実行できます。次のCURL操作は、クラスタ化された管理対象サーバーに対してロード・バランサ・ポート30305を使用してサーブレットを起動し、managed-server1のURLを指定します。

$ curl http://localhost:30305/TxPropagate/TxPropagate?urls=t3://domain1-managed-server1:8001
<pre>
[domain1+managed-server2] started BEA1-0001DE85D4EE
[domain1+managed-server2] BEA1-0001DE85D4EEC47AE630 registered example.RemoteSyncImpl$SynchronizationImpl@562a85bd
[domain1+managed-server2] BEA1-0001DE85D4EEC47AE630 registering Synchronization on t3://domain1-managed-server1:8001
[domain1+managed-server1] BEA1-0001DE85D4EEC47AE630 registered example.RemoteSyncImpl$SynchronizationImpl@585ff41b
[domain1+managed-server2] committed Xid=BEA1-0001DE85D4EEC47AE630(844351585),Status=Committed,numRepliesOwedMe=0,numRepliesOwedOthers=0,seconds since begin=0,seconds left=120,useSecure=false,SCInfo[domain1+managed-server2]=(state=committed),SCInfo[domain1+managed-server1]=(state=committed),properties=({ackCommitSCs={managed-server1+domain1-managed-server1:8001+domain1+t3+=true}, weblogic.transaction.partitionName=DOMAIN}),OwnerTransactionManager=ServerTM[ServerCoordinatorDescriptor=(CoordinatorURL=managed-server2+domain1-managed-server2:8001+domain1+t3+ CoordinatorNonSecureURL=managed-server2+domain1-managed-server2:8001+domain1+t3+ coordinatorSecureURL=null, XAResources={WSATGatewayRM_managed-server2_domain1},NonXAResources={})],CoordinatorURL=managed-server2+domain1-managed-server2:8001+domain1+t3+)

次の図は、アプリケーション・フローを示しています。

出力を見ると、トランザクションBEA1-0001DE85D4EEを起動したmanaged-server2にサーブレット・リクエストがディスパッチされたことがわかります。

[domain1+managed-server2] started BEA1-0001DE85D4EE

コールバック・オブジェクトSynchronizationImpl@562a85bdを登録したローカルのRemoteSync.register()メソッドが起動されました。

[domain1+managed-server2] BEA1-0001DE85D4EEC47AE630 registered example.RemoteSyncImpl$SynchronizationImpl@562a85bd

次に、サーブレットは、同期オブジェクトSynchronizationImpl@585ff41bを登録したmanaged-server1のRemoteSyncオブジェクトに対してregisterメソッドを呼び出しました。

[domain1+managed-server2] BEA1-0001DE85D4EEC47AE630 registering Synchronization on t3://domain1-managed-server1:8001
[domain1+managed-server1] BEA1-0001DE85D4EEC47AE630 registered example.RemoteSyncImpl$SynchronizationImpl@585ff41b

最後に、サーブレットはトランザクションをコミットし、トランザクションの文字列表現を返します(通常はTMデバッグ・ロギングに使用されます)。

[domain1+managed-server2] committed Xid=BEA1-0001DE85D4EEC47AE630(844351585),Status=Committed,numRepliesOwedMe=0,numRepliesOwedOthers=0,seconds since begin=0,seconds left=120,useSecure=false,SCInfo[domain1+managed-server2]=(state=committed),SCInfo[domain1+managed-server1]=(state=committed),properties=({ackCommitSCs={managed-server1+domain1-managed-server1:8001+domain1+t3+=true}, weblogic.transaction.partitionName=DOMAIN}),OwnerTransactionManager=ServerTM[ServerCoordinatorDescriptor=(CoordinatorURL=managed-server2+domain1-managed-server2:8001+domain1+t3+ CoordinatorNonSecureURL=managed-server2+domain1-managed-server2:8001+domain1+t3+ coordinatorSecureURL=null, XAResources={WSATGatewayRM_managed-server2_domain1},NonXAResources={})],CoordinatorURL=managed-server2+domain1-managed-server2:8001+domain1+t3+)

出力は、トランザクションがコミットされ、2つのサーバー参加者(managed-server1およびmanaged-server2)があり、調整サーバー(managed-server2)に t3://domain1-managed-server2:8001 を使用してアクセスできることを示しています。

管理サーバーおよびmanaged-server1の出力を参照して、登録された同期コールバックが起動されたことを確認することもできます。サーバーの.outファイルは、ドメインの永続ボリュームの下にあります。

$ cd $K8SOP/volumes/domain1/domain/domain1/servers
$ find . -name '*.out' -exec grep -H BEA1-0001DE85D4EE {} ';'
./managed-server1/logs/managed-server1.out:[domain1+managed-server1] BEA1-0001DE85D4EEC47AE630 beforeCompletion()
./managed-server1/logs/managed-server1.out:[domain1+managed-server1] BEA1-0001DE85D4EEC47AE630 afterCompletion()
./managed-server2/logs/managed-server2.out:[domain1+managed-server2] BEA1-0001DE85D4EEC47AE630 beforeCompletion()
./managed-server2/logs/managed-server2.out:[domain1+managed-server2] BEA1-0001DE85D4EEC47AE630 afterCompletion()

まとめると、Kubernetesクラスタで実行されているWebLogic Serverドメイン内の分散トランザクションを変更することなく処理できました。WebLogic Kubernetes Operatorドメイン作成プロセスは、それを可能にするために必要なすべてのKubernetesネットワーキングおよびWebLogic Server構成を提供しました。

次のコマンドは、weblogicネームスペースで定義されたKubernetesサービスをリストします。

$ kubectl get svc -n weblogic
NAME                                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGE
domain1-admin-server                        NodePort    10.102.156.32    <none>        7001:30701/TCP    11m
domain1-admin-server-extchannel-t3channel   NodePort    10.99.21.154     <none>        30012:30012/TCP   9m
domain1-cluster-1-traefik                   NodePort    10.100.211.213   <none>        80:30305/TCP      11m
domain1-cluster-1-traefik-dashboard         NodePort    10.108.229.66    <none>        8080:30315/TCP    11m
domain1-cluster-cluster-1                   ClusterIP   10.106.58.103    <none>        8001/TCP          9m
domain1-managed-server1                     ClusterIP   10.108.85.130    <none>        8001/TCP          9m
domain1-managed-server2                     ClusterIP   10.108.130.92    <none>        8001/TCP

localhostのポート30305を使用して、Traefik NodePortサービスを介してサーブレットにアクセスできました。Kubernetesクラスタ内から、サーブレットはサービス名およびポートを使用して他のWebLogic Serverインスタンスにアクセスできます。各サーバーのリスニング・アドレスは対応するKubernetesサービス名に設定されるため、サーバーのポッドが再起動されて別のIPアドレスが割り当てられている場合でも、アドレスはKubernetesネームスペース内から解決できます。

クロスドメイン・トランザクション

次に、この例を2つのWebLogic Serverドメインにまたがって実行するように拡張します。TM概要の項で説明したように、クロスドメイン・トランザクションでは、TM通信を適切に保護するために追加の構成が必要になる場合があります。ただし、この例では、可能なかぎり単純な構成を維持します。アプリケーションと内部TMの通信には、引き続きセキュアでないプロトコル(t3)と匿名ユーザーを使用します。

まず、domain1(weblogic)と同じKubernetesネームスペースに新しいドメイン(domain2)を作成する必要があります。domain2を生成する前に、WebLogicネームスペースのdomain2資格証明(domain2-weblogic-credentials)のシークレットと、永続ボリュームのディレクトリ($K8SOP/volumes/domain2)を作成する必要があります。

次に、create-domain1.yaml ファイルを変更し、次の属性値を変更して、create-domain2.yaml という名前の新しいファイルに変更を保存します。

属性 Value
domainName domain2
domainUID domain2
weblogicDomainStoragePath {full path of $HOME}/k8sop/volumes/domain2
weblogicCredentialsSecretName domain2-weblogic-credentials
t3ChannelPort 32012
adminNodePort 32701
loadBalancerWebPort 32305
loadBalancerDashboardPort 32315

 

これで、create-domain2.yaml 入力ファイルを使用してcreate-weblogic-domain.sh スクリプトを起動する準備ができました。

$ $K8SOP/src/weblogic-kubernetes-operator/kubernetes/create-weblogic-domain.sh -i $K8SOP/create-domain2.yaml -o $K8SOP/weblogic-kubernetes-operator

作成スクリプトが正常に完了すると、domain2のサーバーが起動し、レディネス・プローブを使用してRUNNING状態になったことがレポートされます。

$ kubectl get po -n weblogic
NAME                                         READY     STATUS    RESTARTS   AGE
domain1-admin-server                         1/1       Running   0          27m
domain1-cluster-1-traefik-9985d9594-gw2jr    1/1       Running   0          27m
domain1-managed-server1                      1/1       Running   0          25m
domain1-managed-server2                      1/1       Running   0          25m
domain2-admin-server                         1/1       Running   0          5m
domain2-cluster-1-traefik-5c49f54689-9fzzr   1/1       Running   0          5m
domain2-managed-server1                      1/1       Running   0          3m
domain2-managed-server2                      1/1       Running   0          3m

domain2のサーバーにアプリケーションをデプロイした後、アプリケーションを起動し、domain2管理対象サーバーのURLを含めることができます。

$ curl http://localhost:30305/TxPropagate/TxPropagate?urls=t3://domain2-managed-server1:8001,t3://domain2-managed-server2:8001
<pre>
[domain1+managed-server1] started BEA1-0001144553CC
[domain1+managed-server1] BEA1-0001144553CC5D73B78A registered example.RemoteSyncImpl$SynchronizationImpl@2e13aa23
[domain1+managed-server1] BEA1-0001144553CC5D73B78A registering Synchronization on t3://domain2-managed-server1:8001
[domain2+managed-server1] BEA1-0001144553CC5D73B78A registered example.RemoteSyncImpl$SynchronizationImpl@68d4c2d6
[domain1+managed-server1] BEA1-0001144553CC5D73B78A registering Synchronization on t3://domain2-managed-server2:8001
[domain2+managed-server2] BEA1-0001144553CC5D73B78A registered example.RemoteSyncImpl$SynchronizationImpl@1ae87d94
[domain1+managed-server1] committed Xid=BEA1-0001144553CC5D73B78A(1749245151),Status=Committed,numRepliesOwedMe=0,numRepliesOwedOthers=0,seconds since begin=0,seconds left=120,useSecure=false,SCInfo[domain1+managed-server1]=(state=committed),SCInfo[domain2+managed-server1]=(state=committed),SCInfo[domain2+managed-server2]=(state=committed),properties=({ackCommitSCs={managed-server2+domain2-managed-server2:8001+domain2+t3+=true, managed-server1+domain2-managed-server1:8001+domain2+t3+=true}, weblogic.transaction.partitionName=DOMAIN}),OwnerTransactionManager=ServerTM[ServerCoordinatorDescriptor=(CoordinatorURL=managed-server1+domain1-managed-server1:8001+domain1+t3+ CoordinatorNonSecureURL=managed-server1+domain1-managed-server1:8001+domain1+t3+ coordinatorSecureURL=null, XAResources={WSATGatewayRM_managed-server1_domain1},NonXAResources={})],CoordinatorURL=managed-server1+domain1-managed-server1:8001+domain1+t3+)

アプリケーション・フローを次の図に示します。

この例では、トランザクションにdomain1とdomain2の両方のサーバー参加者が含まれ、関連するすべてのサーバーで同期コールバックが処理されたことを確認できます。

$ cd $K8SOP/volumes
$ find . -name '*.out' -exec grep -H BEA1-0001144553CC {} ';'
./domain1/domain/domain1/servers/managed-server1/logs/managed-server1.out:[domain1+managed-server1] BEA1-0001144553CC5D73B78A beforeCompletion()
./domain1/domain/domain1/servers/managed-server1/logs/managed-server1.out:[domain1+managed-server1] BEA1-0001144553CC5D73B78A afterCompletion()
./domain2/domain/domain2/servers/managed-server1/logs/managed-server1.out:[domain2+managed-server1] BEA1-0001144553CC5D73B78A beforeCompletion()
./domain2/domain/domain2/servers/managed-server1/logs/managed-server1.out:[domain2+managed-server1] BEA1-0001144553CC5D73B78A afterCompletion()
./domain2/domain/domain2/servers/managed-server2/logs/managed-server2.out:[domain2+managed-server2] BEA1-0001144553CC5D73B78A beforeCompletion()
./domain2/domain/domain2/servers/managed-server2/logs/managed-server2.out:[domain2+managed-server2] BEA1-0001144553CC5D73B78A afterCompletion()

まとめ

この記事では、WebLogic Server Transaction Managerがグローバル・トランザクションを処理する方法の概要と、基本的な構成要件について説明しました。次に、サンプル・アプリケーションを見て、クロスドメイン・トランザクションがKubernetesクラスタでどのように処理されるかを示します。今後の記事では、マルチノード、クロスKubernetesクラスタ・トランザクション、フェイルオーバーなど、より複雑なトランザクション・ユースケースについて説明します。