'23/11現在、GemFire for Kubernetesでマルチサイト構成(WAN構成)を組もうとしても、ドキュメントの記載がGKEに関するものしかなく、イマイチやり方が分からない。
実際、作ろうとした時にハマり箇所があったのでメモを残す。
GemFireの前提知識
GemFireで相手方と接続する場合、DNSの逆引きが動いて名前解決できる必要がある。
この辺の仕組みはよく分からないのだが、接続先IPの逆引きしたDNS名が接続元からも参照できる必要がある。
以下の図のケースで考える。
LocatorのPodに繋がるServiceをExternalIPで公開してgfshで繋ごうとしても、gfshのコマンド側でlocator.gem.svc.cluster.local
が解決できないと、以下のようなエラーになる。
gfsh>connect --locator 10.20.30.40[10334]
Connecting to Locator at [host=10.20.30.40, port=10334] ..
Connecting to Manager at [host=locator.gem.svc.cluster.local, port=1099] ..
Could not connect to : [host=locator.gem.svc.cluster.local, port=1099]. Failed to retrieve RMIServer stub: javax.naming.ConfigurationException [Root exception is java.rmi.UnknownHostException: Unknown host: locator.gem.svc.cluster.local; nested exception is:
java.net.UnknownHostException: locator.gem.svc.cluster.local]
なので、接続元で相手方クラスタ内のサービスに紐づく名前を解決できるようにする必要がある。
事前準備
GemFireClusterの構築
今回、ここではTKGクラスタを2面用意し、それぞれにGemFireClusterを作成した。
各GemFireClusterはLocator1台、Server2台を持つ構成とし、以下のManifestで作成した。
apiVersion: gemfire.vmware.com/v1
kind: GemFireCluster
metadata:
name: gemfire1
namespace: gemfire-cluster-1
spec:
serialization:
pdx:
readSerialized: true
image: registry.tanzu.vmware.com/pivotal-gemfire/vmware-gemfire:10.0.1
security:
tls: {}
locators:
overrides:
gemFireProperties:
- name: "distributed-system-id"
value: "10"
- name: "remote-locators"
value: "gemfire2-locator-0.gemfire2-locator.gemfire-cluster-2.svc.cluster.local[10334]"
apiVersion: gemfire.vmware.com/v1
kind: GemFireCluster
metadata:
name: gemfire2
namespace: gemfire-cluster-2
spec:
serialization:
pdx:
readSerialized: true
image: registry.tanzu.vmware.com/pivotal-gemfire/vmware-gemfire:10.0.1
security:
tls: {}
locators:
overrides:
gemFireProperties:
- name: "distributed-system-id"
value: "11"
- name: "remote-locators"
value: "gemfire1-locator-0.gemfire1-locator.gemfire-cluster-1.svc.cluster.local[10334]"
Remote Locatorは相手のServiceを指定している(後述するが実際は若干異なる経路で繋ぐ)。
また、クラスタ外から繋げられるようにExternalIPでServiceを公開する。これはGemFireClusterが作成するServiceとは別に作成する。
apiVersion: v1
kind: Service
metadata:
name: locator-lb
spec:
ports:
- name: locator
port: 10334
protocol: TCP
- name: locator-management-api
port: 7070
protocol: TCP
- name: locator-metrics
port: 4321
protocol: TCP
- name: manager
protocol: TCP
port: 1099
type: LoadBalancer
selector:
statefulset.kubernetes.io/pod-name: gemfire1-locator-0
apiVersion: v1
kind: Service
metadata:
name: locator-lb
spec:
ports:
- name: locator
port: 10334
protocol: TCP
- name: locator-management-api
port: 7070
protocol: TCP
- name: locator-metrics
port: 4321
protocol: TCP
- name: manager
protocol: TCP
port: 1099
type: LoadBalancer
selector:
statefulset.kubernetes.io/pod-name: gemfire2-locator-0
Manager用にPort1099を公開する必要がある点に注意。
また、WAN構成を取った場合、GatewaySenderとGatewayReceiverはServer上で動くため、最終的にはServer間で通信できる=ServerもExternalIP等で公開する必要がある。そのため、Locatorと同様に、Serverも公開する。
apiVersion: v1
kind: Service
metadata:
name: server-lb-0
spec:
type: LoadBalancer
ports:
- name: server
port: 40404
- name: server-dev-rest-api
port: 7070
- name: server-metrics
port: 4321
- name: receiver
port: 8888
selector:
statefulset.kubernetes.io/pod-name: gemfire1-server-0
---
apiVersion: v1
kind: Service
metadata:
name: server-lb-1
spec:
type: LoadBalancer
ports:
- name: server
port: 40404
- name: server-dev-rest-api
port: 7070
- name: server-metrics
port: 4321
- name: receiver
port: 8888
selector:
statefulset.kubernetes.io/pod-name: gemfire1-server-1
apiVersion: v1
kind: Service
metadata:
name: server-lb-0
spec:
type: LoadBalancer
ports:
- name: server
port: 40404
- name: server-dev-rest-api
port: 7070
- name: server-metrics
port: 4321
- name: receiver
port: 8888
selector:
statefulset.kubernetes.io/pod-name: gemfire2-server-0
---
apiVersion: v1
kind: Service
metadata:
name: server-lb-1
spec:
type: LoadBalancer
ports:
- name: server
port: 40404
- name: server-dev-rest-api
port: 7070
- name: server-metrics
port: 4321
- name: receiver
port: 8888
selector:
statefulset.kubernetes.io/pod-name: gemfire2-server-1
なお、Receiverで使うポートを明示的に書く必要がある点に注意。
CoreDNSの設定変更
相手のServiceの名前解決が出来るよう、CoreDNSに設定変更を行う。
各クラスタでkubectl edit cm -n kube-system coredns
を実行し、以下のように相手のServiceのFQDNとExternalIPを追記する。
@@ -7,6 +7,12 @@
lameduck 5s
}
ready
+ hosts {
+ 10.220.28.88 gemfire2-locator-0.gemfire2-locator.gemfire-cluster-2.svc.cluster.local
+ 10.220.28.92 gemfire2-server-0.gemfire2-server.gemfire-cluster-2.svc.cluster.local
+ 10.220.28.93 gemfire2-server-1.gemfire2-server.gemfire-cluster-2.svc.cluster.local
+ fallthrough
+ }
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
@@ -7,6 +7,12 @@
lameduck 5s
}
ready
+ hosts {
+ 10.220.28.87 gemfire1-locator-0.gemfire1-locator.gemfire-cluster-1.svc.cluster.local
+ 10.220.28.90 gemfire1-server-0.gemfire1-server.gemfire-cluster-1.svc.cluster.local
+ 10.220.28.91 gemfire1-server-1.gemfire1-server.gemfire-cluster-1.svc.cluster.local
+ fallthrough
+ }
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
保存後、CoreDNSを再起動して設定をリロードする。
kubectl delete pod -n kube-system -l k8s-app=kube-dns
WAN構成の動作確認
ここでは以下のような作業用Podを立ち上げて、そこから各クラスタのLocatorに繋いで作業していく。
apiVersion: v1
kind: Pod
metadata:
name: debug-gemfire
spec:
containers:
- name: gemfire
image: registry.tanzu.vmware.com/pivotal-gemfire/vmware-gemfire:10.0.0
imagePullSecrets:
- name: image-pull-secret
作業用Podに接続し、gfshを立ち上げる。
kubectl exec -it debug-gemfire -- bash
gfsh
Cluster1のLocatorに接続する。
connect --locator 10.220.28.87[10334]
GatewaySenderを作成する。
create gateway-sender --id=gemfire-cluster-1 --parallel=true --remote-distributed-system-id=11
Regionを作成する。
create region --name=regionA --type=PARTITION --gateway-sender-id=gemfire-cluster-1
GatewayReceiverを作成する。デフォルト値で作ると待受ポートが一意にならないため、Serviceで公開したPortを指定する。
create gateway-receiver --start-port 8888 --end-port 8888
確認用のデータを入れておく。
put --region=regionA --key="1" --value="one"
put --region=regionA --key="2" --value="two"
Cluster1側の作業は以上なので、Cluster1から切断する。
disconnect
次にCluster2の方でもGatewaySender、GatewayReceiverを作成する。
Cluster2のLocatorに接続する。
connect --locator 10.220.28.88[10334]
Cluster1と同じようにGatewaySender、Region、GatewayReceiverを作成する。
create gateway-sender --id=gemfire-cluster-2 --parallel=true --remote-distributed-system-id=10
create region --name=regionA --type=PARTITION --gateway-sender-id=gemfire-cluster-2
create gateway-receiver --start-port 8888 --end-port 8888
ここでstatus gateway-sender
でGatewaySenderの設定を確認する。
Cluster-11 gfsh>status gateway-sender --id gemfire-cluster-2
Member | Type | Status
------------------------------------------------ | -------- | ---------------------
gemfire2-server-1(gemfire2-server-1:1)<v1>:59668 | Parallel | Running and Connected
gemfire2-server-0(gemfire2-server-0:1)<v1>:58072 | Parallel | Running and Connected
Status
がConnected
になっているため、相手のクラスタと繋がっていることが分かる。
また、クエリを発行してデータが取れるか確認する。
query --query="select * from /regionA"
Result : true
Limit : 100
Rows : 2
Result
------
two
one
無事、Cluster1でputした内容が確認できた。
まとめ
一応WAN構成自体は作れることを確認できた。
ただ、スケールイン・アウトには対応出来ないし、IPが変わった場合も追従出来ない。
この辺はOperatorで実装して欲しい部分であり、GemFire for Kubernetesが将来的に改善されることを期待したい。
失敗談
CoreDNSのStubDomain
CoreDNSのStubDomainで解決できないか、CoreDNSのConfigMapに以下を追加して試してみた。
Corefile: |
+ gemfire-cluster-2.svc.cluster.local:53 {
+ forward . 10.220.28.89
+ }
.:53 {
この場合、PodのIPは引けるようになるが、Private IPを返してくるため上手く動かない。
接続は以下のような感じでタイムアウトとなる。
gfsh>connect --locator 10.220.28.88[10334]
Connecting to Locator at [host=10.220.28.88, port=10334] ..
Connecting to Manager at [host=gemfire2-locator-0.gemfire2-locator.gemfire-cluster-2.svc.cluster.local, port=1099] ..
Could not connect to : [host=gemfire2-locator-0.gemfire2-locator.gemfire-cluster-2.svc.cluster.local, port=1099]. Failed to retrieve RMIServer stub: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: gemfire2-locator-0.gemfire2-locator.gemfire-cluster-2.svc.cluster.local; nested exception is:
java.net.ConnectException: Connection timed out (Connection timed out)]