やりたかったこと
アプリ開発中、バックエンドで使用する2種類のAPIサーバをkubernetesにデプロイし、Ingress経由で公開しようとしました。
そのときのエンドポイントを下のように設定しようとしました。
- /server1 → APIサーバ1にアクセス
- /server2 → APIサーバ2にアクセス
使用しているkubernetes環境は以下の通りです。
- ホスティングサービス: IBM Cloud Kubernetes Service
- バージョン: 1.9.10_1523 (ちょい古め)
起きたこと
どちらのエンドポイントにアクセスしても、両方のサーバに均等に到達してしまう…
例: /server1 に3回アクセス → APIサーバ1に1回、APIサーバ2に2回到達
やらかした原因
例えば次のようなアプリをデプロイしていたとします。
name | port binding |
---|---|
server1 | 8000:8000 |
server2 | 8080:8080 |
このとき、ServiceとIngressを下のように書いていました。
apiVersion: v1
kind: Service
metadata:
name: all-in-one-service
labels:
project: current-project
spec:
type: LoadBalancer
loadBalancerIP: <available IP>
selector:
project: current-project
ports:
- port: 8000
targetPort: 8000
name: server1
- port: 8080
targetPort: 8080
name: server2
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
labels:
project: current-project
spec:
rules:
- host: <available hostname>
http:
paths:
- path: /service1
backend:
serviceName: all-in-one-service
servicePort: 8000
- path: /service2
backend:
serviceName: all-in-one-service
servicePort: 8080
これでうまく行くはず!…と思ってたのですが。うまく行かない。
IBM Cloud Kubernetes Serviceのドキュメントを見ながらデバッグしました。
以下、実際にデバッグしたときの手順です。
-
kubectl get po -n kube-system | grep alb
で、nginx-ingressが動いているpodの名前を探す -
kubectl logs <podname> -n kube-system nginx-ingress
で、nginx-ingressのログを取得する -
kubectl exec -it <podname> bash -c nginx-ingress -n kube-system
で、nginx-ingressのコンテナにログインする - nginxの設定ファイルを探し出す。今回は
/etc/nginx/conf.d/
以下にありました。
nginxの設定ファイルを見ると、下のような部分がありました。
location /service1 {
...
proxy_pass http://my-cluster-url.eu-central.containers.mybluemix.net-all-in-one-service;
...
}
location /service2 {
...
proxy_pass http://my-cluster-url.eu-central.containers.mybluemix.net-all-in-one-service;
...
}
ん?同じURL…?
IngressにはserviceNameとservicePortを両方書くけれど、servicePortは関係なく、両方のパスに対して同じServiceを紐づけているだけということが分かりました。
だからうまく行かなかったんですね。
どう直したか
シンプルに、ServiceをDeploymentごとに分けました。
Ingressに指定するパスごとに別々のServiceが割りあたるようにしました。
apiVersion: v1
kind: Service
metadata:
name: service-for-server1
labels:
project: current-project
spec:
selector:
app: server1
project: current-project
ports:
- port: 8000
targetPort: 8000
apiVersion: v1
kind: Service
metadata:
name: service-for-server2
labels:
project: current-project
spec:
selector:
app: server2
project: current-project
ports:
- port: 8080
targetPort: 8080
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
labels:
project: current-project
spec:
rules:
- host: <available hostname>
http:
paths:
- path: /service1
backend:
serviceName: service-for-server1
servicePort: 8000
- path: /service2
backend:
serviceName: service-for-server2
servicePort: 8080
これで今度こそ解決
おわりに
実はIngressのルーティング問題の他にも、気づいて直したやらかしがいくつかあります。
- ServiceでLoadBalancerIPを指定しているので、名前空間変えてデプロイしたりできなかった。
- そもそもIngressに紐付けるだけならLoadBalancerでなくても良かった。
- Serviceのtypeを指定せずにClusterIPになっててもIngressに登録できるみたいです。
他にもこんなとこやらかしてるよ、というツッコミお待ちしております。k8sむずかしい。