こんにちは。
kubernetes/ingress-nginxを使っていて、2つのIngressA
とB
を作成したときの挙動がどのようになるのかを調査したのでしたのでまとめます。
結論
2つのIngressA
とB
を作成したとき、Ingressコントローラによって単一のNginxサーバーのバックエンドとして動的に読み込まれます。
コードリーディング
1. IngressClassの追加
Ingress-NginxをインストールするときはHelmなどを使用することが多いと思います。
そのときにIngressClass
が追加されて、nginx
を指定することによってIngress NginxによるL7ロードバランサーを作成することができるようになります。
対象のコードはここです。
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
{{- include "ingress-nginx.labels" . | nindent 4 }}
app.kubernetes.io/component: controller
{{- with .Values.controller.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
name: {{ .Values.controller.ingressClassResource.name }}
{{- if .Values.controller.ingressClassResource.default }}
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
{{- end }}
spec:
controller: {{ .Values.controller.ingressClassResource.controllerValue }}
{{ template "ingressClass.parameters" . }}
{{- end }}
nginx
のIngress Classのコントローラーをspec.controller
で指定しています。これを行うことによって、例えば以下のような
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Ingressを作成したときに、コントローラーに実際のロードバランサを依頼するリクエストが送られます。
2. NginxのPodについて
どのNginxに設定をすべきか、どのようにどのようにコントローラーは決めているのでしょうか。
実際のNginxはコントローラーのPOD_NAME
で指定されたPod名でPOD_NAMESPACE
で指定されたNamespaceに作成されます (コード)。
func GetIngressPod(kubeClient clientset.Interface) error {
podName := os.Getenv("POD_NAME")
podNs := os.Getenv("POD_NAMESPACE")
if podName == "" || podNs == "" {
return fmt.Errorf("unable to get POD information (missing POD_NAME or POD_NAMESPACE environment variable")
}
pod, err := kubeClient.CoreV1().Pods(podNs).Get(context.TODO(), podName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("unable to get POD information: %v", err)
}
...
このようにして、どのNginxを設定しないといけないのかをコントローラーが判別しています。
3. Ingressを作成したときの挙動
実際にIngressを作成したときにどのような挙動になるのかを説明します。
3.1 Nginxの設定
syncIngressがすべてのIngressを取得して、Nginxの設定を生成します。
ngs := n.store.ListIngresses()
hosts, servers, pcfg := n.getConfiguration(ings)
これによって、たくさんあるIngressが一つのNginxに束ねられます。Ingressが作成または削除されるたびに、Nginxのserver
が増えていきます。Nginxの設定ファイルを生成することによってIngressがNginxのバックエンドとして設定されます。
余談ですが何もIngressがないときのNginxは以下の初期設定で実行されています(ソース)。
pid /tmp/nginx.pid;
events {}
http {}
daemon off;
3.1.1 設定のテンプレート
Nginxの設定ファイルはあらかじめ定義していたテンプレートによって生成されます。
それはnginx.tmpl
で定義されているため、これを書き換えることによって設定の変更を加えることができます。
3.2 Nginxに読み込ませる
LuaによってハンドリングされているNginxの内部的なHTTP endpointに新しいバックエンドの情報を送ってNginxに反映します(コード)。
実際には/configuration/backends
エンドポイントへすべてのバックエンドの情報を送ってセットをしています。
NginxのLuaのハンドラはここに実装されています。
このような仕組みによって、動的に、Nginxのバックエンドを追加できています。
終わりに
Ingress Nginx Controllerの簡単な調査をしました。 設定ファイルを再度読み込むことなく、動的にNginxにサーバーの追加を反映するために、Luaで拡張をしていたことは非常に面白かったです。 Kubernetesは拡張方法が明確な分、コードを読めば簡単に仕組みがわかるので非常に楽だなと改めて感じました。