Redhat OpenshiftでIBM MQのUniform Clusterを動かす (2)

# Redhat Openshift上のIBM MQ Uniform Clusterにクライアント接続してみる

前回はUniform Clusterの構成を組んでみました。 
今回は元々のUniform Clusterの目的である、クラアント接続を試してみます。

MQでクライアント接続する際の方法は、接続先の情報（チャネル名、IP、PORT)を接続時に与えなければならないのですが、方法としては以下があります。

    1)MQSERVER環境変数で指定
    2)MQCONNX API発行時にプログラムの中で指定
    3)CCDT(CLIENT CHANNEL DEFINITION TABLE)を用いて定義を外に出す


Uniform Cluster環境では自動振り分けのための複数宛先への接続が前提なので、CCDTを用います。

またクラウドの外からの接続になるので、TLS通信が前提となります。
通信のための証明書の設定やルート定義が必要ですが、詳しくはこちらを参照してください。 
 https://www.ibm.com/docs/ja/ibm-mq/9.2?topic=manager-example-configuring-tls

通信系のセットアップについては復習を兼ね、別途まとめたいと思います。 


# 1.CCDTてなんだっけ？
CCDT(CLIENT CHANNEL DEFINITION TABLE)ではMQCLIENTからキュー・マネージャー接続先を指定する定義ファイルです。 Uniform Cluserの構成では、複数のキュー・マネージャーへの接続先の定義を実施する事で、自動切替が可能となっています。


# 2.CCDTの構成方法
MQ V9.1.2からJSON形式もサポートされるようになりました。 
それ以前はrunmqscコマンドを用いて生成する必要があり、また生成したファイルはバイナリ形式のため、そのまま根性だけでは読むのが難しい。。。
そこで今回はJSON形式で作成してみます。

（runmqscではパラメーター名などのSyntaxは定義時にチェックしてくれます。 JSONだとccdt_schema.jsonが提供されていて、これでチェック）


# 3.CCDTの構成について
## 1)構成の組合せイメージ
UNIFORMクラスター構成では、接続する全てのキュー・マネージャー向けの定義ｘ呼び出し方式の組合せで構成します。
呼び出し方式は、クラスター名とキュー・マネージャー名の2種類を準備します。
今回は接続先が２つのため、都合2x2 = 4種のエントリーを定義することになります。 イメージ的には以下のようになります。

    CCDT定義イメージ
    1.QMGR1向けCLUSTER名での接続定義
    2.QMGR2向けCLUSTER名での接続定義
    3.QMGR1向けキュー・マネージャー名での接続定義
    4.QMGR2向けキュー・マネージャー名での接続定義

## 2)実際の定義
実際の定義体はこちらになります。 前回同様キュー・マネージャー名の一部を"xxxx"としているのは心眼で読む事で見逃していただきたく。。。。 
(最初からxxxxで作ってもよかったです)

```CCDT
{
 "channel":
 [
   {
     "name":"CL.OCQMxxxxFR2", 
     "connectionManagement":
      {
        "sharingConversations": 1,
        "clientWeight": 1,
        "affinity": "none",
        "defaultReconnect": "yes",
        "disconnectInterval": 6000,
        "heartbeatInterval": 30,
        "keepAliveInterval": -1
      },
     "clientConnection":
      {
       "connection":
       [
        {
          "host":"FR2向けの外部接続IP",
          "port":443
        }
       ],
        "queueManager":"CLUST1"
      },
      "transmissionSecurity":
      {
      "cipherSpecification":"TLS_RSA_WITH_AES_128_CBC_SHA256"
      },
      "type":"clientConnection"
   },
   {
     "name":"CL.OCQMxxxxFR1", 
     "connectionManagement":
      {
        "sharingConversations": 1,
        "clientWeight": 1,
        "affinity": "none",
        "defaultReconnect": "yes",
        "disconnectInterval": 6000,
        "heartbeatInterval": 30,
        "keepAliveInterval": -1
      },
     "clientConnection":
      {
       "connection":
       [
        {
          "host":"FR1向けの外部接続情報",
          "port":443
        }
       ],
        "queueManager":"CLUST1"
      },
      "transmissionSecurity":
      {
      "cipherSpecification":"TLS_RSA_WITH_AES_128_CBC_SHA256"
      },
      "type":"clientConnection"
   },
   {
     "name":"CL.OCQMxxxxFR2", 
     "connectionManagement":
      {
        "sharingConversations": 1,
        "clientWeight": 1,
        "affinity": "none",
        "defaultReconnect": "yes",
        "disconnectInterval": 6000,
        "heartbeatInterval": 30,
        "keepAliveInterval": -1
      },
     "clientConnection":
      {
       "connection":
       [
        {
          "host":"FR2向けの外部接続情報",
          "port":443
        }
       ],
        "queueManager":"OCQMxxxxFR2"
      },
      "transmissionSecurity":
      {
      "cipherSpecification":"TLS_RSA_WITH_AES_128_CBC_SHA256"
      },
      "type":"clientConnection"
   },
   {
     "name":"CL.OCQMxxxxFR1", 
     "connectionManagement":
      {
        "sharingConversations": 1,
        "clientWeight": 1,
        "affinity": "none",
        "defaultReconnect": "yes",
        "disconnectInterval": 6000,
        "heartbeatInterval": 30,
        "keepAliveInterval": -1
      },
     "clientConnection":
      {
       "connection":
       [
        {
          "host":"FR1向けの外部接続情報",
          "port":443
        }
       ],
        "queueManager":"OCQMxxxxFR1"
      },
      "transmissionSecurity":
      {
      "cipherSpecification":"TLS_RSA_WITH_AES_128_CBC_SHA256"
      },
      "type":"clientConnection"
   }
  ]
}

```

定義内のhost:では、それぞれのPodに向けたクラウド外部からの接続定義を指定します。 

詳細はこちら：https://www.ibm.com/docs/ja/ibm-mq/9.2?topic=dcqmumo-configuring-route-connect-queue-manager-from-outside-red-hat-openshift-cluster

ここではFR1・FR2に向けて、クラスター名とキュー・マネージャー名での接続をそれぞれ準備しています。 
（チャネル名はFR1/FR2向けで分けていますが、同じでもいけるはず）

CCDTではCHLTYPE(CLNTCONN)に該当する定義を行っていますので、対向するキュー・マネージャー側ではCHLTYPE(SVRCONN)の定義が必要です。  FR1側では以下の定義をおこなっています。 FR2側も同様に必要です。

```
dis chl(CL.OCQMxxxxFR1) ALL
     2 : dis chl(CL.OCQMxxxxFR1) ALL
AMQ8414I: Display Channel details.
   CHANNEL(CL.OCQMxxxxFR1)                 CHLTYPE(SVRCONN)
   ALTDATE(2021-12-17)                     ALTTIME(05.29.16)
   CERTLABL( )                             COMPHDR(NONE)
   COMPMSG(NONE)                           DESCR( )
   DISCINT(0)                              HBINT(10)
   KAINT(AUTO)                             MAXINST(999999999)
   MAXINSTC(999999999)                     MAXMSGL(4194304)
   MCAUSER( )                              MONCHL(QMGR)
   RCVDATA( )                              RCVEXIT( )
   SCYDATA( )                              SCYEXIT( )
   SENDDATA( )                             SENDEXIT( )
   SHARECNV(10)                            SSLCAUTH(OPTIONAL)
   SSLCIPH(TLS_RSA_WITH_AES_128_CBC_SHA256)
   SSLPEER( )                              TRPTYPE(TCP)

```

HBINTはMQチャネルのハートビート（対向する相手の状況監視）の時間を指定します。  
CCDT内の"heartbeatInterval"、SVRCONNチャネル定義のHBINTが該当します。
最初WireSharkで見ていたら、再接続が一定間隔で行われていました。
どうも50秒Idleが続くと、接続が切られてしまうようで、Deault 300秒だとIdleと見なされ切断して、でもRECONNECT指定しているから接続して。。。というのを繰り返していました。
そのため50秒未満を指定しています。 HBINTはCLNTCONN/SVRCONN両方で指定した時間の長い方が使われるので、両方変更が必要です。 今回は30 vs 10 なので30になっているはず。 （普通は合わせます）

CipherSpecification（SSLCIPH）も双方で合わせておく必要があります。 またクライアント認証(SSLCAUTH)は"OPTIONAL"なので実行されません。


## 3)プログラム側
RECONNECTオプションを指定しています。 Cだとこんな感じです。 
（CCDTでも指定できるので、一方だけでもよいかも）

    connopts.Options += MQCNO_RECONNECT;
      MQCONNX(QMName,               /* queue manager                 */
	      &connopts,
          &Hcon,                   /* connection handle              */
          &CompCode,               /* completion code                */
          &CReason);               /* reason code                    */


# 4.実行準備
プログラムの実行準備として以下の環境変数を設定しています。

    MQCHLLIB= CCDTの保管ディレクトリ
    MQCHLTAB= CCDTファイル名
    MQSSLKEYR= TLS用のKEYDB指定
    MQCCSID= クライアント側の文字コード、QMGR側と合わせてますが相互に変換可能だと不要（のはず）


# 5.接続と切替指示 
クライアントアプリケーションを起動し、Uniform Clusterに接続してみます。 接続先のQMNameはクラスター名に"*"を先頭に付けた、”*CLUST1"を指定します。
"putreconc.exe"が今回使用しているWindows PCでのクライアントアプリです。 
接続先はCCDTのCLNTWGHT値の比率でランダムに選択され、今回はたまたまFR1側に接続。

    dis apstatus(*) TYPE(QMGR)
         6 : dis apstatus(*) TYPE(QMGR)
    AMQ8932I: Display application status details.
        APPLNAME(rojects\mqsamp\putreconc.exe)
        ACTIVE(YES)                             COUNT(1) 
        MOVCOUNT(1)                             BALSTATE(OK)
        LMSGDATE(2021-12-17)                    LMSGTIME(05.30.42)
        QMNAME(OCQMxxxxFR1)                 
        QMID(OCQMxxxxFR1_2021-12-15_01.23.12)
        TYPE(QMGR)                           
    AMQ8932I: Display application status details.
        APPLNAME(rojects\mqsamp\putreconc.exe)
        ACTIVE(YES)                             COUNT(0) 
        MOVCOUNT(0)                             BALSTATE(LOW)
        LMSGDATE(2021-12-17)                    LMSGTIME(05.29.45)
        QMNAME(OCQMxxxxFR2)                 
        QMID(OCQMxxxxFR2_2021-10-05_12.21.46)
        TYPE(QMGR)   


ここでFR1側をSUSPENDしてみます。
そうすると接続先がFR1からFR2に移っています。                     


    suspend qmgr CLUSTER(CLUST1) 
         8 : suspend qmgr CLUSTER(CLUST1) 
    AMQ8557I: SUSPEND QUEUE MANAGER accepted.
    dis apstatus(*) type(QMGR)
         9 : dis apstatus(*) type(QMGR)
    AMQ8932I: Display application status details.
      APPLNAME(rojects\mqsamp\putreconc.exe)
       ACTIVE(YES)                             COUNT(0) 
       MOVCOUNT(0)                             BALSTATE(OK)
       LMSGDATE(2021-12-17)                    LMSGTIME(05.31.56)
       QMNAME(OCQMxxxxFR1)                 
       QMID(OCQMxxxxFR1_2021-12-15_01.23.12)
       TYPE(QMGR)                           
    AMQ8932I: Display application status details.
       APPLNAME(rojects\mqsamp\putreconc.exe)
       ACTIVE(YES)                             COUNT(1) 
       MOVCOUNT(1)                             BALSTATE(OK)
       LMSGDATE(2021-12-17)                    LMSGTIME(05.31.55)
       QMNAME(OCQMxxxxFR2)                 
       QMID(OCQMxxxxFR2_2021-10-05_12.21.46)
       TYPE(QMGR)                           

FR2側で確認すると以下になっています。 

    dis chs(CL*)
        15 : dis chs(CL*)
    AMQ8417I: Display Channel Status details.
       CHANNEL(CL.OCQMxxxxFR2)                CHLTYPE(SVRCONN)
       CONNAME(172.17.45.14)                   CURRENT
       STATUS(RUNNING)                         SUBSTATE(RECEIVE)
    dis apstatus(*) type(QMGR) 
        16 : dis apstatus(*) type(QMGR) 
    AMQ8932I: Display application status details.
        APPLNAME(rojects\mqsamp\putreconc.exe)
        ACTIVE(YES)                             COUNT(1) 
        MOVCOUNT(1)                             BALSTATE(OK)
        LMSGDATE(2021-12-17)                    LMSGTIME(05.31.29)
        QMNAME(OCQMxxxxFR2)                 
        QMID(OCQMxxxxFR2_2021-10-05_12.21.46)
        TYPE(QMGR)    
    (FR1側表示は省略)                       

なおSuspend状態は”DIS CLUSQMGR”コマンドのSUSPENDで確認できます。

    dis clusqmgr(*) QMTYPE SUSPEND
        10 : dis clusqmgr(*) QMTYPE SUSPEND
    AMQ8441I: Display Cluster Queue Manager details.
       CLUSQMGR(OCQMxxxxFR1)                  CHANNEL(TO.FR1)
       CLUSTER(CLUST1)                         QMTYPE(REPOS)
       SUSPEND(YES)                         
    AMQ8441I: Display Cluster Queue Manager details.
       CLUSQMGR(OCQMxxxxFR2)                  CHANNEL(TO.FR2)
       CLUSTER(CLUST1)                         QMTYPE(REPOS)
       SUSPEND(NO)        

最近のWiresharkはMQのプロトコルやTLSで暗号化された通信も秘密鍵があれば、解析できます。 
（自分でテスト用に作成した秘密鍵なので自由に使っていますが、本当の利用環境では証明書は厳重に管理する必要があります）

今回は、自動再接続を利用したUniform Clusterの動的接続先の切替を見てみましたが、Wiresharkで見ていると、なかなか面白かったです。
ただ、アプリケーションの知らないところで勝手に切り替わってよいのか？ という疑問もあります。 ここらはMQ 9.2.4（CD版）でクライアントアプリからの制御オプションもあるようです。 次回の課題ですね。