はじめに
OpenShiftのチュートリアルや教育資料を見ると、OpenShift上のアプリケーションへのアクセスにRouteを使う例が多いです(ちなみにRouteがOpenShiftとしての奨励手段のようです)。Routeを使うと、"oc expose"コマンドでServiceを外部からURLでアクセスできるようになります。たとえば、"myservice"というServiceを、"myservice.ocp4.example.local"というホスト名で公開したい場合、以下のようなコマンドでRouteをつくることができます(なお、ホスト名は"<任意の名前>.."という形になります)。
oc expose service myservice --hostname=myservice.ocp4.example.local
ただ、Routeを使いたくない!という場合もあり、本記事ではRouteを使わずにServiceを公開する方法を紹介します。なお、単にNodePort + Load Balancerを使う、というだけのお話なので、OCP/k8sに詳しい人は読み飛ばしてください(^^;)
RouteでServiceを公開するハードル
前述の方法でServiceをRouteを使って公開することができるのですが、実際には少しハードルがあります。それは、このの例の"myservice.ocp4.example.local"という名前を、OpenShiftの前段にあるLoad BalancerのIPアドレスに解決する必要があるということです。OpenShiftを構築するときには、こういった名前解決を行うためのDNSが必要となるため、Helper Node上に独自にDNSを立ることもあるかと思います。このDNSを使えば、上記の名前解決はできます。
ただし、外部からアクセスするクライアントは、Helper Node上のDNSではなく、イントラネット上に存在するDNSを使っているはずです。そのため、名前解決ができず、Routeへアクセスできない、というケースも有るかと思います。
これを解決するためには、ワイルドカードDNSレコードをイントラネット側のDNSに設定する必要があります。これによって、イントラネット側から"*.ocp4.example.local"という名前をLoad BalancerのIPアドレスに解決できるようになります。ただ、なかなかこれが難しい場合があります。理由としては・・・
- イントラネット側のDNSが、そもそもワイルドカードDNSレコードをサポートしていない場合がある
- イントラネットのDNSにワイルドカードDNSレコードを登録してもらうための社内申請やネゴが面倒
というようなことが考えられます。
Routeをつかわないでサービスを公開する簡単な方法
そこで、Routeではない他の単純な方法でサービスを公開したくなります。一番簡単な方法は、PodへアクセスするためのServiceをNodePortとして作ることです。NodePortを使うと、各Workerノードが持つIPアドレスを使ってアクセス可能になります(なお、どのworkerノードへアクセスしても、Podが存在するノードへ転送されます)。ポート番号はデフォルトの設定では、30000〜32767の間のいずれかが自動割当されます。ポート番号を明示的に指定することも出来ます。
以下のサービス定義例では、myappというdeployのポート8080を、NodePortのポート30030として公開しています。
apiVersion: v1
kind: Service
metadata:
labels:
app: myapp
name: myservice
spec:
type: NodePort
ports:
- name: nginx
port: 8080
protocol: TCP
nodePort: 30030
selector:
deployment: myapp
これで、"workerノードのIP:30030"でOCPクラスター外からサービスにアクセスできるようになりました。ただ、これだと外部からアクセスするときに特定のworkerノードのIPを指定する必要があります。workerノードの一部がメンテナンス中のときには他のworkerノードにアクセスする、みたいなことをクライアント側にやらせたくありません。
そこで、OpenShiftには必須なLoad Balancerを使って、リクエストを全workerノードの30030ポートへ分散させることにします。これによって、あるworkerノードがメンテナンス中の場合も、他のノードが自動的に使われるようになります。外部からアクセスするときは、"LoadBalancerのホスト名:転送元ポート"のような形でアクセス可能になります。
たとえば、Load BalancerとしてHAProxyを利用していて、ポート8000から、各workerノードのポート30030へ転送したいときは、以下のような設定をhaproxy.cfgへ入れることになります。
frontend nginx-http
bind *:8000
default_backend nginx-http
option tcplog
backend nginx-http
balance source
server worker0 10.91.51.118:30030 check
server worker1 10.91.51.119:30030 check
server worker2 10.91.51.120:30030 check
これで、イントラネット側のDNSにワイルドカードDNSレコードを定義せず、お手軽にServiceを外部へ公開できるようになりました!
なお、Load BalancerはPodがどのWorkerノードで動いているか知っているわけではないので、指定したアルゴリズムで機械的に各ノードへ分散させているだけです。そのため、Load Balancerがリクエストを転送したworkerノードから、さらにPodが動いているWorkerノードへリクエストが転送されるオーバーヘッドが生じており、かならずしもパフォーマンス面からは最適なソリューションでは無いかもしれませんので、ご注意ください。