1. はじめに
Red Hat OpenShift on IBM Cloud(VPC)では、以下の2つのオプションがあります。
https://cloud.ibm.com/docs/openshift?topic=openshift-plan_clusters#vpc-workeruser-master
- Public and private service endpoints
- Private service endpoint only
IBM Cloud Portalからクラスターを作成した場合は"Public and private service endpoints"になり、デフォルトでPublic Router(Public NWからからアクセスできるルーター)が構成されています。でも、Direct LinkなどPrivate NW経由でアクセスしたい場合はPrivate Routerが必要になるため、別途作成が必要になります。この記事では、Private Routerの作成方法について以下のドキュメントを参照しながら試してみます。
https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_routes#private-routes
2. 初期状態の確認
以下はPublic Routerの構成情報です。
default
というIngress Controllerによってrouter-defaultというPublic向けのRouterが構成されています。
$ oc get ingresscontroller --all-namespaces
NAMESPACE NAME AGE
openshift-ingress-operator default 13d
$ oc get svc,pods,route -n openshift-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/router-default LoadBalancer 172.21.164.127 xxxxxxxx-us-south.lb.appdomain.cloud 80:32005/TCP,443:32360/TCP 12d
service/router-internal-default ClusterIP 172.21.72.253 <none> 80/TCP,443/TCP,1936/TCP 12d
NAME READY STATUS RESTARTS AGE
pod/router-default-69cbd48fc7-4pwgx 1/1 Running 0 2d17h
pod/router-default-69cbd48fc7-h55kx 1/1 Running 0 2d17h
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
route.route.openshift.io/router-default router-default.myroksclustervpc-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.us-south.containers.appdomain.cloud /healthz router-internal-default 1936 None
3. Private Routerを作成する
OpenShift 4.xのRouterは、Ingress Controllerによって作成・管理されています。いったんRouterが作成されてしまえば、HTTP/HTTPSのトラフィック処理はRouterによって実施されるため、Ingress Controllerが直接関与することはありません。
ここでは、IBM Cloud docsに従って、Private Routerを作成・管理するIngress Controllerを作成します。
apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
name: private
namespace: openshift-ingress-operator
spec:
replicas: 2
domain: roksvpc.internal
endpointPublishingStrategy:
loadBalancer:
scope: Internal
type: LoadBalancerService
ちなみに、spec.endpointPublishingStrategy.loadBalancer.scope=Internalとなっているところで、Private NWのみに公開する旨の指定をしています。ソースコードでの説明はこちら。
ドメインはPrivate NW用に取得しておきます。本番環境ではここなどを参照して、VPC用のPrivate DNSのゾーンを作成しておくと良いでしょう。
ただし、2020年7月時点において、VPCのPrivate DNSにはCNAMEに対するワイルドカードは指定できないという制約があるので注意してください。
以下、このマニフェストファイルを実行します。oc patch
部分は本来は要らないのですが、現在のRed Hat OpenShift on IBM Cloud(VPC)のバグであり、本来はexternalTrafficPolicy=Local
が正しい仕様だということなので変更しています。
$ oc apply -f privateIngressController.yaml
$ oc patch service router-private -n openshift-ingress -p '{"spec":{"externalTrafficPolicy":"Cluster"}}'
$ oc get ingresscontroller --all-namespaces
NAMESPACE NAME AGE
openshift-ingress-operator default 13d
openshift-ingress-operator private 56s
$ oc get svc,pods,route -n openshift-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/router-default LoadBalancer 172.21.164.127 xxxxxxxx-us-south.lb.appdomain.cloud 80:32005/TCP,443:32360/TCP 13d
service/router-internal-default ClusterIP 172.21.72.253 <none> 80/TCP,443/TCP,1936/TCP 13d
service/router-internal-private ClusterIP 172.21.53.252 <none> 80/TCP,443/TCP,1936/TCP 29s
service/router-private LoadBalancer 172.21.240.98 yyyyyyyy-us-south.lb.appdomain.cloud 80:31050/TCP,443:31260/TCP 29s
NAME READY STATUS RESTARTS AGE
pod/router-default-69cbd48fc7-4pwgx 1/1 Running 0 3d
pod/router-default-69cbd48fc7-h55kx 1/1 Running 0 3d
pod/router-private-56cb4748c6-gx8vn 1/1 Running 0 29s
pod/router-private-56cb4748c6-tg7b6 1/1 Running 0 29s
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
route.route.openshift.io/router-default router-default.myroksclustervpc-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.us-south.containers.appdomain.cloud ... 1 more /healthz router-internal-default 1936 None
また、このマニフェストファイルの実行により、Private-To-PrivateのVPC Load Balancerも作成されており、NodePort経由でrouter-private
Podにトラフィックを転送可能になりました。
4. Routeの作成
既存のServiceをexposeしてRouteを作成します。
$ oc expose service hello-world --hostname www.roksvpc.com
$ oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
hello-world www.roksvpc.com ... 1 more hello-world 8080-tcp None
5. 検証
今回は面倒(?)だったのでこんなやり方で確認していますが、本来はxxxxxxxx-us-south.lb.appdomain.cloud
とかyyyyyyyy-us-south.lb.appdomain.cloud
はCNAMEとして登録しておくべきものです。
$ oc get service -n openshift-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
router-default LoadBalancer 172.21.164.127 xxxxxxxx-us-south.lb.appdomain.cloud 80:32005/TCP,443:32360/TCP 13d
router-internal-default ClusterIP 172.21.72.253 <none> 80/TCP,443/TCP,1936/TCP 13d
router-internal-private ClusterIP 172.21.53.252 <none> 80/TCP,443/TCP,1936/TCP 19h
router-private LoadBalancer 172.21.240.98 yyyyyyyy-us-south.lb.appdomain.cloud 80:31050/TCP,443:31260/TCP 19h
$ host xxxxxxxx-us-south.lb.appdomain.cloud
xxxxxxxx-us-south.lb.appdomain.cloud has address 52.116.xx.xx
xxxxxxxx-us-south.lb.appdomain.cloud has address 52.116.xx.xx
$ host yyyyyyyy-us-south.lb.appdomain.cloud
yyyyyyyy-us-south.lb.appdomain.cloud has address 10.240.64.15
yyyyyyyy-us-south.lb.appdomain.cloud has address 10.240.128.14
# Public NW経由でのアクセス
$ curl -H "Host: www.roksvpc.com" xxxxxxxx-us-south.lb.appdomain.cloud
Hello world from hello-world-1-h8zcz! Your app is up and running in a cluster!
# Private NW経由でのアクセス
$ curl -H "Host: www.roksvpc.com" yyyyyyyy-us-south.lb.appdomain.cloud
Hello world from hello-world-1-h8zcz! Your app is up and running in a cluster!
www.roksvpc.com
というRequested Hostに対して、router default
もrouter private
のどちらに対してもこのRouteは公開されています。実際に今回作成したRouteをよくみてみると、www.roksvpc.com
というRequested Hostに対して、router-default
もrouter-private
も登録されていることがわかります。
$ oc describe route hello-world
Name: hello-world
Namespace: syasuda
Created: 25 seconds ago
Labels: app=hello-world
app.kubernetes.io/component=hello-world
app.kubernetes.io/instance=hello-world
Annotations: <none>
Requested Host: www.roksvpc.com
exposed on router default (host myroksclustervpc-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.us-south.containers.appdomain.cloud) 25 seconds ago
exposed on router private (host roksvpc.internal) 26 seconds ago
Path: <none>
TLS Termination: <none>
Insecure Policy: <none>
Endpoint Port: 8080-tcp
Service: hello-world
Weight: 100 (100%)
Endpoints: 172.17.103.76:8080, 172.17.79.136:8080, 172.17.79.147:8080 + 2 more...
これは、実際に各々のRouterにログインしてみてもわかります。
$ oc rsh -n openshift-ingress router-default-69cbd48fc7-4pwgx cat /var/lib/haproxy/conf/haproxy.config|grep hello-world
backend be_http:syasuda:hello-world
server pod:hello-world-1-h8zcz:hello-world:172.17.103.76:8080 172.17.103.76:8080 cookie 2c590602f05c7e43ec6a6efc7c60e153 weight 256 check inter 5000ms
server pod:hello-world-1-b8ng9:hello-world:172.17.79.136:8080 172.17.79.136:8080 cookie dc09a373d3e3bfc84663d80ccf6fce9e weight 256 check inter 5000ms
server pod:hello-world-1-mtpzt:hello-world:172.17.79.147:8080 172.17.79.147:8080 cookie 6001d244ba91955a14d807b3842e2f69 weight 256 check inter 5000ms
server pod:hello-world-1-hlnq9:hello-world:172.17.88.134:8080 172.17.88.134:8080 cookie 08c0e5cfd626696a46123636974171bb weight 256 check inter 5000ms
server pod:hello-world-1-hbr8h:hello-world:172.17.88.139:8080 172.17.88.139:8080 cookie 8cd637af1c6bd737923b5b35deda115b weight 256 check inter 5000ms
$ oc rsh -n openshift-ingress router-default-69cbd48fc7-4pwgx cat /var/lib/haproxy/conf/os_http_be.map|grep hello
^www\.roksvpc\.com(:[0-9]+)?(/.*)?$ be_http:syasuda:hello-world
$ oc rsh -n openshift-ingress router-private-56cb4748c6-gx8vn cat /var/lib/haproxy/conf/haproxy.config|grep hello-world
backend be_http:syasuda:hello-world
server pod:hello-world-1-h8zcz:hello-world:172.17.103.76:8080 172.17.103.76:8080 cookie 2c590602f05c7e43ec6a6efc7c60e153 weight 256 check inter 5000ms
server pod:hello-world-1-b8ng9:hello-world:172.17.79.136:8080 172.17.79.136:8080 cookie dc09a373d3e3bfc84663d80ccf6fce9e weight 256 check inter 5000ms
server pod:hello-world-1-mtpzt:hello-world:172.17.79.147:8080 172.17.79.147:8080 cookie 6001d244ba91955a14d807b3842e2f69 weight 256 check inter 5000ms
server pod:hello-world-1-hlnq9:hello-world:172.17.88.134:8080 172.17.88.134:8080 cookie 08c0e5cfd626696a46123636974171bb weight 256 check inter 5000ms
server pod:hello-world-1-hbr8h:hello-world:172.17.88.139:8080 172.17.88.139:8080 cookie 8cd637af1c6bd737923b5b35deda115b weight 256 check inter 5000ms
$ oc rsh -n openshift-ingress router-private-56cb4748c6-gx8vn cat /var/lib/haproxy/conf/os_http_be.map|grep hello
^www\.roksvpc\.com(:[0-9]+)?(/.*)?$ be_http:syasuda:hello-world
6. Routerのシャーディング
つまり、Routeはこのままだと全てのRouter公開されてしまっています。もし「必ずこのRouteはこちらのRouterにしか公開したく無い!」というのであれば、RouterのShading機能を使うとよいでしょう。
- https://rcarrata.com/openshift/ocp4_route_sharding/
- https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.5/html/networking/nw-ingress-sharding_configuring-ingress
要は、特定のRouteやNamespaceにラベルを付けて、それをSelectorで検索して合致したもののみをRouteとして登録するというやり方です。でもどういう方法がスマートなんでしょうね?
- もともとprivate NW側にしか公開する意図がないアプリケーションにroute-default経由でアクセスする際には、Hostヘッダにそのprivate側で構成しているFQDNを指定する必要があるので、そのFQDN情報が知られていなければ直ちに脅威になるものではないと思われる。
- 一般的には、public公開しているサービスはprivate側からアクセスできても差し支えがないことが多いので、不用意に外部には公開したくないために構成したいというケースの方が多いと思われる。よってrouter-defaultにRoute Selector/NameSpace Selectorを付けて、マッチングしたRoute以外は外部に公開しないのが本来のあるべき姿のように思える。この場合、既存の(システムが作成した)Route/NameSpaceのラベルを変更・管理する必要がある。
namespaceSelector:
matchExpressions:
- key: router
operator: NotIn
values:
- private
$ oc label namespace <project> "router=private"