目的
Red Hat OpenShift on IBM Cloud(ROKS)はプロビジョン後にすぐにアプリケーションの開発と公開を行うことができますが、パブリックではなくプライベートでアプリケーション公開したい場合があります。例えばVPC内からのアクセスや、専用線、VPN接続などのケースです。
今回はROKS on VPCで、プライベートにアプリケーションを公開するための方法をいくつかのパターンで検証してみます。
前提
- ROKSはマルチゾーン構成とします
- ROKSクラスター自体はIBM Cloud ConsoleからアクセスできるOpenShift Web Consoleや外部のコンテナレジストリは利用できた方がよいので、パブリックとのアクセスは引き続き可能とします(パブリックゲートウェイが有効なサブネットを利用)
プライベートアクセスの計画
公式のドキュメントをよく読んで検討します。
https://cloud.ibm.com/docs/openshift?topic=openshift-cs_network_planning#private_vpc
現在選択肢は次の4つのようです。
- プライベートルーター
- NodePort
- プライベートVPCロードバランサー
- プライベートIngressコントローラー
今回はこれらを全て設定します。
手順
テスト用アプリケーションの準備
OpenShiftユーザーならおなじみのruby-exを使用します。
$ oc new-app ruby~https://github.com/sclorg/ruby-ex.git
$ oc get pods
NAME READY STATUS RESTARTS AGE
ruby-ex-1-build 0/1 Completed 0 2m3s
ruby-ex-1-deploy 0/1 Completed 0 30s
ruby-ex-1-vp5sf 1/1 Running 0 23s
プライベートルーター
https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_routes#private-routes-setup-43
デフォルトではパブリックで利用可能なルーターをプライベート側にもデプロイします。
ルーターはドメイン名を必要とします。自分でドメイン名を取得してDNSを設定して持ち込むこともできますが、VPCのROKSはIBM提供のドメイン名を使えますので、今回はそのまま利用します。現在IBMから提供されているドメイン名を調べます。
$ ibmcloud oc nlb-dns ls -c roks-tok
OK
サブドメイン ロード・バランサーのホスト名 SSL 証明書の状況 SSL 証明書の秘密名 秘密の名前空間
roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud ****a523-jp-tok.lb.appdomain.cloud created roks-tok-********81bb6d6d7afea007d1a8cafd-0000 openshift-ingress
途中の0000をi001
にしたものがプライベート用ドメイン名です。1桁目は一律i、4桁目は0からのカウントアップです。そのドメイン名を指定してIngressControllerを作成します。
apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
name: private
namespace: openshift-ingress-operator
spec:
replicas: 2
domain: roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud
endpointPublishingStrategy:
loadBalancer:
scope: Internal
type: LoadBalancerService
リソースを作成します。
$ oc create -f ingresscontroller-i001.yaml
ingresscontroller.operator.openshift.io/private created
router-privateというPodが追加されました。
$ oc get pods -n openshift-ingress
NAME READY STATUS RESTARTS AGE
router-default-5c4d76b4c-5zj9r 1/1 Running 0 29m
router-default-5c4d76b4c-8gvns 1/1 Running 0 29m
router-private-544cf4bfbf-cw8z2 1/1 Running 0 25s
router-private-544cf4bfbf-trb96 1/1 Running 0 25s
このとき裏ではVPCのプライベートロードバランサーが自動生成されています。プライベートロードバランサーのホスト名を調べます。下記のEXTERNAL-IP欄が該当します。
$ oc get svc/router-private -n openshift-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
router-private LoadBalancer 172.21.132.236 ****6e91-jp-tok.lb.appdomain.cloud 80:30379/TCP,443:32396/TCP 62s
プライベート用ドメイン名とロードバランサーのホスト名を紐づけてDNS登録します。nlb-dns createする度にi00xをカウントアップしたレコードが自動登録されます。
$ ibmcloud oc nlb-dns create vpc-gen2 -c roks-tok --lb-host ****6e91-jp-tok.lb.appdomain.cloud --type private --secret-namespace openshift-ingress
NLB DNS を作成中...
OK
NLB ホスト名が roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud として作成されました。
$ ibmcloud oc nlb-dns ls -c roks-tok
OK
サブドメイン ロード・バランサーのホスト名 SSL 証明書の状況 SSL 証明書の秘密名 秘密の名前空間
roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud ****a523-jp-tok.lb.appdomain.cloud created roks-tok-********81bb6d6d7afea007d1a8cafd-0000 openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud ****6e91-jp-tok.lb.appdomain.cloud created roks-tok-********81bb6d6d7afea007d1a8cafd-i001 openshift-ingress
注意)IBM Cloudのガイドに従うと--secret-namespaceをつけ忘れてしまいますが、これをつけないとこのドメイン用のTLS証明書がdefault名前空間に作られてしまい、今後TLSを使いたい場合にopenshift-ingress名前空間のルーターPodから証明書を見つけられなくなってしまいます。
名前解決できることを確認します。CNAMEでロードバランサーを指していることがわかります。
$ dig roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud
...
roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud. 0 IN CNAME ****6e91-jp-tok.lb.appdomain.cloud.
****6e91-jp-tok.lb.appdomain.cloud. 0 IN A 10.244.66.7
****6e91-jp-tok.lb.appdomain.cloud. 0 IN A 10.244.2.7
プロジェクト(ネームスペース)に、プライベートルーターを使用するようラベリングします。
$ oc label namespace $(oc project -q) "router=router-private"
このラベリングはガイドに記載されているとおりですが、これをしなくても問題なくプライベートルーターを利用できました。このあたりの仕組みはよくわかっていません。また、このラベルが真に有効ならば、パブリックルーターとプライベートルーターどちらを使うかはプロジェクト単位で分けるべきということになります。
サービスをプライベートルーターで公開します。--hostnameはi001のドメイン名に任意のサブドメイン名を付けます。
$ oc expose svc/ruby-ex --name ruby-ex-private --hostname ruby-ex-teruz.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud
route.route.openshift.io/ruby-ex-private exposed
同じアプリケーションに対してパブリックルーターとプライベートルーターの経路は共存できます。
$ oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
ruby-ex ruby-ex-teruz.roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud ... 1 more ruby-ex 8080-tcp None
ruby-ex-private ruby-ex-teruz.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud ... 1 more ruby-ex 8080-tcp None
接続の確認ですが、今回は同じVPC内の仮想サーバを利用します。
$ curl -s ruby-ex-teruz.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud | grep title
<title>Welcome to OpenShift</title>
NodePort
NodePortはKubernetes標準の方法です。サービスをNodePortタイプで作成するだけです。
$ oc expose dc/ruby-ex --name ruby-ex-nodeport --type NodePort
service/ruby-ex-nodeport exposed
$ oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ruby-ex ClusterIP 172.21.251.135 <none> 8080/TCP 137m
ruby-ex-nodeport NodePort 172.21.1.23 <none> 8080:31530/TCP 12s
各ノードのアドレスを調べます。
$ oc get node
NAME STATUS ROLES AGE VERSION
10.244.130.4 Ready master,worker 150m v1.18.3+fa69cae
10.244.2.4 Ready master,worker 150m v1.18.3+fa69cae
10.244.66.4 Ready master,worker 151m v1.18.3+fa69cae
それぞれのノードのNodePortに直接アクセスします。
$ curl -s 10.244.130.4:31530/ | grep title
<title>Welcome to OpenShift</title>
$ curl -s 10.244.2.4:31530/ | grep title
<title>Welcome to OpenShift</title>
$ curl -s 10.244.66.4:31530/ | grep title
<title>Welcome to OpenShift</title>
NodePortは楽ですが、アクセス経路の冗長化を考えると、結局のところ別の手段でヘルスチェックとロードバランシングを考えないといけません。そのため、現実には他の選択肢をとることになると考えます。
プライベートVPCロードバランサー
https://cloud.ibm.com/docs/openshift?topic=openshift-vpc-lbaas#lbaas_about
サービスをLoadBalancerタイプで作成することでVPCロードバランサーが作成されます。下例のようにマニフェストを用意します。
コメントアウトしている2か所は注意が必要です。ガイドに従うとこれらを設定してしまいそうになりますが、proxy-protocolを有効にすると通常のWebクライアントからのアクセスが400 Bad Requestになるので、特殊な事情なければ有効にしてはいけません。zoneは特定のゾーンのノードと通信をしたい場合のみ指定します。無指定の場合、全ゾーンのノードが対象になります。
apiVersion: v1
kind: Service
metadata:
name: ruby-ex-loadbalancer
annotations:
#service.kubernetes.io/ibm-load-balancer-cloud-provider-enable-features: "proxy-protocol"
service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: private
#service.kubernetes.io/ibm-load-balancer-cloud-provider-zone: ""
spec:
type: LoadBalancer
selector:
deploymentconfig: ruby-ex
ports:
- name: 8080-tcp
port: 8080
protocol: TCP
targetPort: 8080
リソースを作成します。
$ oc create -f loadbalancer.yaml
service/ruby-ex-loadbalancer created
$ oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ruby-ex ClusterIP 172.21.251.135 <none> 8080/TCP 3h47m
ruby-ex-loadbalancer LoadBalancer 172.21.187.249 ****cf61-jp-tok.lb.appdomain.cloud 8080:32157/TCP 21m
ruby-ex-nodeport NodePort 172.21.1.23 <none> 8080:31530/TCP 90m
接続確認します。
$ curl -s ****cf61-jp-tok.lb.appdomain.cloud:8080 | grep title
<title>Welcome to OpenShift</title>
プライベートIngressコントローラー
プライベートルーターもIngressコントローラーの一部なのですが、ここではKubernetesでおなじみのIngressリソースを使用します。ドメイン名は区別のためi002を使用します。
apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
name: private-ingress-controller
namespace: openshift-ingress-operator
spec:
replicas: 2
domain: roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud
endpointPublishingStrategy:
loadBalancer:
scope: Internal
type: LoadBalancerService
リソースを作成します。
$ oc create -f ingresscontroller-i002.yaml
ingresscontroller.operator.openshift.io/private-ingress-controller created
$ oc get svc -n openshift-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
router-default LoadBalancer 172.21.170.143 ****a523-jp-tok.lb.appdomain.cloud 80:31463/TCP,443:32580/TCP 4h27m
router-internal-default ClusterIP 172.21.5.56 <none> 80/TCP,443/TCP,1936/TCP 4h27m
router-internal-private ClusterIP 172.21.76.206 <none> 80/TCP,443/TCP,1936/TCP 3h45m
router-internal-private-ingress-controller ClusterIP 172.21.158.189 <none> 80/TCP,443/TCP,1936/TCP 8m4s
router-private LoadBalancer 172.21.132.236 ****6e91-jp-tok.lb.appdomain.cloud 80:30379/TCP,443:32396/TCP 3h45m
router-private-ingress-controller LoadBalancer 172.21.247.200 ****aad0-jp-tok.lb.appdomain.cloud 80:30050/TCP,443:31945/TCP 8m4s
i002ドメインをロードバランサーと紐づけてDNS登録します。
$ ibmcloud oc nlb-dns create vpc-gen2 -c roks-tok --lb-host ****aad0-jp-tok.lb.appdomain.cloud --type private --secret-namespace openshift-ingress
NLB DNS を作成中...
OK
NLB ホスト名が roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud として作成されました。
$ ibmcloud oc nlb-dns ls -c roks-tok
OK
サブドメイン ロード・バランサーのホスト名 SSL 証明書の状況 SSL 証明書の秘密名 秘密の名前空間
roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud ****a523-jp-tok.lb.appdomain.cloud created roks-tok-********81bb6d6d7afea007d1a8cafd-0000 openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud ****6e91-jp-tok.lb.appdomain.cloud created roks-tok-********81bb6d6d7afea007d1a8cafd-i001 openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud ****aad0-jp-tok.lb.appdomain.cloud created roks-tok-********81bb6d6d7afea007d1a8cafd-i002 openshift-ingress
アプリケーションを公開するためのIngressマニフェストを作成します。任意のサブドメインを付与します。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ruby-ex
spec:
rules:
- host: ruby-ex-teruz.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud
http:
paths:
- path: /
backend:
serviceName: ruby-ex
servicePort: 8080
リソースを作成します。
$ oc create -f ingress.yaml
ingress.extensions/ruby-ex created
接続確認します。
$ curl -s ruby-ex-teruz.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud | grep title
<title>Welcome to OpenShift</title>
結果
いずれの方式もVPC内部からのプライベートアクセスはうまくいきました。次回以降、オンプレミス相当の環境からいくつかのパターンで接続して動作検証をしてみます。
また、NodePortを除くいずれの方式もDNSでの名前解決を前提としていました。つまり、オンプレミス側でもパブリックまたはIBM Cloud内のDNSが参照できる必要があります。
検証編を公開しました。