AWS
kubernetes
ingress

Ingressでクロスネームスペースでのロードバランシング

TL;DR

複数のnamespaceのpodsたちにむけてを1つのロードバランサで振り分けるのはめんどくさいって話です。

ExternalNameを使えばできる。


ことの発端

そんなにトラフィックはないけど機能ごとにnamespaceを分けておこうかなと思って、バッチやAPPのPodsやServiceのnamespaceを分けてデプロイしようかなと思った。

いざ、Ingressの設定をしようと思ったら、backendにnamespace書くところないじゃんとなった。

このspec.rules.http.pathごとにnamespaceを切っていた。


apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: lb
annotations:
#nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
spec:
rules:
- host: api.192.168.99.107.nip.io
http:
paths:
- path: /dapps
backend:
serviceName: dapps-svc
servicePort: 80
- path: /gas
backend:
serviceName: gas-svc
servicePort: 80
- path: /miner
backend:
serviceName: miner-svc
servicePort: 80

もしかしてkubens的な感じで書けばいけるのかなと思ったけど、serviceNameに「.」を入れるとエラーを吐いてくれてデプロイすらできない。(dapps-svc.namespace1とかってしても無駄。)

* spec.rules[0].http.backend.serviceName: Invalid value: "dapps-svc.namespace1": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',  or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')


ググるマン

とりあえず公式のドキュメントを見ると、

https://kubernetes.io/docs/concepts/services-networking/ingress/

https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#ingress-v1beta1-networking-k8s-io

その辺の記載はない。

とりあえず今回は切ったnamespaceを全部Ingress側に寄せて(default)デプロイして回避することにした。


そんな馬鹿な納得できない。

ちゃんと検証してみようと思ってこんな感じに環境を構築した。http HeaderのServerとかで振り分け確認をすればいいや。

(minikubeでやりました。)

image.png


conf

とりあえず全部同一namespaceで動くものを。


# namespace
apiVersion: v1
kind: Namespace
metadata:
name: ns-test
---
# ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: lb
namespace: ns-test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: ns-test.192.168.99.107.nip.io
http:
paths:
- path: /apache
backend:
serviceName: apache-svc
servicePort: 80
- path: /nginx
backend:
serviceName: nginx-svc
servicePort: 80
- path: /
backend:
serviceName: blackhole
servicePort: 80
---
# apache
apiVersion: v1
kind: Service
metadata:
name: apache-svc
namespace: ns-test
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: httpd
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd
namespace: ns-test
spec:
replicas: 1
selector:
matchLabels:
app: httpd
template:
metadata:
labels:
app: httpd
spec:
containers:
- image: httpd:alpine
name: httpd
ports:
- containerPort: 80
---
# nginx
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: ns-test
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: ns-test
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80

デフォルトnamespaceを切り替えて

❯ kubens ns-test

Context "vm" modified.
Active namespace is "ns-test".

こんな感じ

❯ kubectl get all,ingress -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/httpd-55c79f4cb-zrzdc 1/1 Running 0 29s 172.17.0.2 minikube <none> <none>
pod/nginx-66d89c74cb-l7gdv 1/1 Running 0 29s 172.17.0.3 minikube <none> <none>

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/apache-svc NodePort 10.101.246.95 <none> 80:32141/TCP 29s app=httpd
service/nginx-svc NodePort 10.97.137.34 <none> 80:30783/TCP 29s app=nginx

NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/httpd 1/1 1 1 29s httpd httpd:alpine app=httpd
deployment.apps/nginx 1/1 1 1 29s nginx nginx:alpine app=nginx

NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
replicaset.apps/httpd-55c79f4cb 1 1 1 29s httpd httpd:alpine app=httpd,pod-template-hash=55c79f4cb
replicaset.apps/nginx-66d89c74cb 1 1 1 29s nginx nginx:alpine app=nginx,pod-template-hash=66d89c74cb

NAME HOSTS ADDRESS PORTS AGE
ingress.extensions/lb ns-test.192.168.99.107.nip.io 80 29s

動いてるかな?

❯ curl http://ns-test.192.168.99.107.nip.io/apache

<html><body><h1>It works!</h1></body></html>

~
❯ curl http://ns-test.192.168.99.107.nip.io/nginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

問題なさそう。本当はヘッダーで判別したかったけど、Ingressのnginxで上書きされてしまっていたのでbodyで。


namespace変えてみる。

podsのapacheのnamespaceをns-test-podsとしてみよう。

IngressのTargetは変えていないので404になるはず。

kubectl delete all --all とかで一回きれいにしてからやらないと前のnamespaceのpodsが残るよ。

namespace追加とServiceとPod変更


6a7,12
> # namespace
> apiVersion: v1
> kind: Namespace
> metadata:
> name: ns-test-pods
> ---
39c45
< namespace: ns-test
---
> namespace: ns-test-pods
53c59
< namespace: ns-test
---
> namespace: ns-test-pods

❯ curl http://ns-test.192.168.99.107.nip.io/nginx

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {

~snip;

❯ curl http://ns-test.192.168.99.107.nip.io/apache
<html>
<head><title>503 Service Temporarily Unavailable</title></head>

~snip;

❯ curl http://ns-test.192.168.99.107.nip.io/apache -I
HTTP/1.1 503 Service Temporarily Unavailable
Server: nginx/1.15.9
Date: Fri, 07 Jun 2019 04:18:20 GMT
Content-Type: text/html
Content-Length: 197
Connection: keep-alive

503になった、リバプロ(Ingress)が吐いてるから正しいね。


serviceName変えてみる。

29c29

< serviceName: apache-svc
---
> serviceName: apache-svc.ns-test-pods

違うだろボケナス!って言われる。

The Ingress "lb" is invalid: spec.rules[0].http.backend.serviceName: Invalid value: "apache-svc.ns-test-pods": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic c

haracter, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')


ExternalNameの登場

ExternalNameっていうtypeのServiceがあって、別名をつけることができる。

そもそも、k8sのpodsやらserviceやらは名前がついていて、<名前>.<ネームスペース>.svc.cluster.local で名前解決できるようになっている。

ここで別のnamespaceのapache-svcをこのServiceで紐づけてやる。

わかりやすい?ように、Ingress側のターゲットを serviceName: apache-svc-koitu として下のServiceを挟む。

apiVersion: v1

kind: Service
metadata:
name: apache-svc-koitu
namespace: ns-test
spec:
type: ExternalName
externalName: apache-svc.ns-test-pods.svc.cluster.local

こうすると、

❯ curl http://ns-test.192.168.99.107.nip.io/apache

<html><body><h1>It works!</h1></body></html>

ちゃんとアクセスできたまん。

最終的にこんな感じになった。

# namespace

apiVersion: v1
kind: Namespace
metadata:
name: ns-test
---
# namespace
apiVersion: v1
kind: Namespace
metadata:
name: ns-test-pods
---
# ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: lb
namespace: ns-test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: ns-test.192.168.99.107.nip.io
http:
paths:
- path: /apache
backend:
serviceName: apache-svc-koitu
servicePort: 80
- path: /nginx
backend:
serviceName: nginx-svc
servicePort: 80
- path: /
backend:
serviceName: blackhole
servicePort: 80
---
apiVersion: v1
kind: Service
metadata:
name: apache-svc-koitu
namespace: ns-test
spec:
type: ExternalName
externalName: apache-svc.ns-test-pods.svc.cluster.local
---
# apache
apiVersion: v1
kind: Service
metadata:
name: apache-svc
namespace: ns-test-pods
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: httpd
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd
namespace: ns-test-pods
spec:
replicas: 1
selector:
matchLabels:
app: httpd
template:
metadata:
labels:
app: httpd
spec:
containers:
- image: httpd:alpine
name: httpd
ports:
- containerPort: 80
---
# nginx
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: ns-test
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: ns-test
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80

こんなイメージ

image.png

目的は果たせた。


まとめ

ALBを1個増やすとさんぜんえんだからね!ExternalName使っていって良いと思います。

ただし、namespaceを分けすぎるのは良くない。kubectl get xxxで見るときにツライことになる。

本当はIngressまで分けたほうがいいけど、そこまでしなくても・・・みたいなときに使うようにしたいと思います。

参考: )

https://github.com/kubernetes/ingress-nginx/issues/2971

https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/

https://qiita.com/ryicoh/items/24e5cf4ffdb6cc8dbbdf (この記事そのまんまであった)