2020.07.03
KubernetesのServiceの動きを調べてみた
こんにちは。次世代システム研究室のM.Mです。
現在、Kubernetes, Docker構成で作られたWebアプリの開発を担当しているのですが、そのWebアプリの開発ではあまり環境構築周りを担当していなかったため、Kubernetesについてあまり理解できていませんでした。
そして今回、古くなった他のWebアプリをKubernetes, Docker構成に移行するプロジェクトを担当することになり、現在担当しているWebアプリのKubernetesの設定を確認しつつ理解を深めていくことにしました。
まず気になっていたKubernetesのServiceについて調べてみることにしました。
今回、利用しているのはNodePort Serviceです。
目次
- KubernetesのServiceの何が気になっていたのか?
- Serviceの動きについて
- Serviceの動きの制御について
KubernetesのServiceの何が気になっていたのか?
まず、システム構成は以下の図の通り
最上部にLBがあります。
WEBアプリへのアクセスはそのLBにて、Web1とWeb2に分かれ、さらにWeb1、Web2のnginxの設定でNode1とNode2に分かれ、Node1、Node2上で稼働しているPHPのWEBアプリにアクセスが行くようになっています。
では、Node2へのアクセスを止めたい場合、Web1、Web2のnginxの設定でNode1にのみ振り分ければよいのでしょうか?
実際に、Web1とWeb2のnginxの設定を変更してNode1にのみアクセスが行くようにしたところ、以下の図のようなアクセスの仕方をしました。
なぜか、Node2へのアクセスが止まらない。
このNode2へのアクセスを制御しているのがKubernetesのServiceなんだろうなとは思いましたが、いったいどこで動いている?
Web1、Web2で振り分けているのに、Serviceでも振り分けるのが普通なのかな?
疑問に思ったまま放置もよくないので、もう少し調べてみることにしました。
Serviceの動きについて
実際には、Web1、Web2のnginxの設定は、上記図のようにNode1、Node2のNginxの80ポートを指定してるのではなく、Serviceを生成したときのNodePortを指定しています。
作成したServiceの情報は以下のようになっており、11行目にあるようにNodePortは31000になっています。
$ kubectl describe service test-service Name: test-service Namespace: default Labels: app=test-server Annotations: .... Selector: app=test-server Type: NodePort IP: 10.43.174.31 Port: 80/TCP TargetPort: 80/TCP NodePort: 31000/TCP Endpoints: 10.42.19.2:80,10.42.20.2:80 Session Affinity: None External Traffic Policy: Cluster Events:
また、Web1、Web2のnginxの設定は以下のようにといった31000ポートを指定しています。
upstream testapp { server 192.168.0.11:31000 max_fails=3 fail_timeout=7s; #Node1 server 192.168.0.12:31000 max_fails=3 fail_timeout=7s; #Node2 }
実際のアクセスイメージは以下の図のようになります。
Serviceがさらに設定されているEndpointに振り分けているのが分かります。
なので、先ほどのようにWeb1、Web2のnginxの設定で、Node2にアクセスしないようにしたところで、ServiceがNode2にアクセスを振り分けます。
では、どこで、どうやって振り分けているのか?
もう少し調べみると、kube-proxyというコンテナが動いていて、iptablesを操作しているとのこと。
iptablesの設定状況を調べてみると、以下のような設定がありました。(対象箇所だけ抜き取っています)
Chain KUBE-SVC-FCDRZOMGBO47PTZS (2 references) target prot opt source destination KUBE-SEP-XMEIUTLIHCVEEQRB all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000 KUBE-SEP-T4IG46XR4M43OGK2 all -- 0.0.0.0/0 0.0.0.0/0 Chain KUBE-SEP-XMEIUTLIHCVEEQRB (1 references) target prot opt source destination KUBE-MARK-MASQ all -- 10.42.19.2 0.0.0.0/0 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to:10.42.19.2:80 Chain KUBE-SEP-T4IG46XR4M43OGK2 (1 references) target prot opt source destination KUBE-MARK-MASQ all -- 10.42.20.2 0.0.0.0/0 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to:10.42.20.2:80
右にスクロールしないと見えませんが、3行名の右端に”statistic mode random probability 0.50000000000″という記載があります。
このiptablesの設定でServiceは振り分けを行っていると分かりました。
kube-proxyが各kubernetes Nodeで動いており、Serviceが追加されたり、変更された場合、各Kubernetes Node上のiptablesが変更される。
Serviceがどこかで動いているというよりは、全Kubernetes Node上のiptablesで制御している模様。
Serviceの動きの制御について
Node2へアクセスが行かないようにするには、Web1、Web2のnginxの設定だけでは足りないことが分かりました。
では、どのように制御すればよいのか?
Node2で稼働しているpodのlabel名を別のlabel名に変更してServiceのEndpointの対象から外す
Node1、Node2で動いているpodのlabel名はapp=test-serverとしています。
また、Serviceは以下のようにapp=test-serverというlabel名のPodに振り分けるような設定になっています。
apiVersion: v1 kind: Service metadata: name: test-service labels: app: test-server spec: ports: - port: 80 targetPort: 80 nodePort: 31000 selector: app: test-server type: NodePort
そして、以下のようにNode2(192.168.0.12)のlabelをapp=hogehogeに変更して、test-serviceのEndpointsを確認します。
$ kubectl label node 192.168.0.12 app=hogehoge --overwrite node/192.168.0.12 labeled $ kubectl describe service test-service Name: test-service Namespace: default Labels: app=test-server Annotations: .... Selector: app=test-server Type: NodePort IP: 10.43.174.31 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31000/TCP Endpoints: 10.42.19.2:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
15行名をみると、EndpointsからNode2(192.168.0.12)上で動いていたPod IP(10.42.20.2)のEndpointが消えていることが分かります。
もちろんiptablesの設定も変わっていました。
labelを変更してServiceのEndpointを外すことで、Node2へアクセスが行かないようにすることができました。
そもそもWeb1、Web2のnginxで振り分けているので、Serviceでは振り分けないようにする
デフォルトだと、Serviceはバランシング設定になるようで、以下のような設定にしてバランシングしないようにしてしまう。
apiVersion: v1 kind: Service metadata: name: test-service labels: app: test-server spec: ports: - port: 80 targetPort: 80 nodePort: 31000 selector: app: test-server type: NodePort externalTrafficPolicy: Local
15行目にexternalTrafficPolicy: Localを追加しています。
externalTrafficPolicy: Localを追加すればアクセスがあったNode上のPodにしか転送されなくなります。
(もちろん、これは設計段階で決めるべきことだとは思いますが・・)
所感
今回は、すでにKubernetes, Docker構成で作られたWebアプリの設定を見ながら、疑問に思ったところを調べる形になりましたが、Kubernetesを一通り勉強してから環境構築すると、このブログで記載した内容は知っていて当たり前なのかも知れません。
私は古いシステムで動くWebアプリを長く開発・運用していたので、どのサーバーが落ちたらどうなる?サーバーをメンテナンスで停止したい場合はどうすればいい?
など、古いシステムだと何となくイメージが付くのですが、Kubernetesの場合、まだまだ分からないことが多そうです。
Kubernetesの前に、ネットワーク知識などもしっかり身に付けないとダメだなと思いながらも、今後も少しづつKubernetesの理解を深めてい行こうと思います。
最後に、次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。アプリケーション開発の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。
皆さんのご応募をお待ちしています。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD