LoginSignup
121
71

More than 3 years have passed since last update.

オンプレK8sで使えるGoogle製External Load Balancer: MetalLB

Last updated at Posted at 2018-04-05

背景

GCPやAWS上のK8sではtype LoadBalancerとか書けばいい感じに外部からアクセスできるIPがアサインされるが、これはcloud-controllerという仕組みで(K8s内部ではなく)そのクラウドサービス上のLBaaSと連携して実現されている。
そのため、自分でオンプレなどにK8sを構築しようとすると普通LBaaSがないので、外部からK8sのアクセスをどうしようという悩みがよく起きる。

MetalLBとは

Googleが作ったベアメタルK8s環境でもつかえるExternal Load Balancer実装。

クラウド上のK8sと同じ間隔でオンプレでもtype LoadBalancerを使うことができる。

インストール方法

すでにK8s環境があれば一行で入る。

kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.5.0/manifests/metallb.yaml

コンフィグ

MetalLBにはBGPモードとL2モードがある。

BGPモード

BGPモードではK8sクラスタの上流にBGPが喋れるルータが必要になる。
そのルータのピア情報と払い出すExternal IPのプールの設定を入れる。

bgp.yml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    peers:
    - my-asn: 64501
      peer-asn: 64500
      peer-address: 172.30.8.35
    address-pools:
    - name: my-ip-space
      protocol: bgp
      avoid-buggy-ips: true
      addresses:
      - 198.51.100.0/24
kubectl apply -f bgp.yml

L2モード

l2.yml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: my-ip-space
      protocol: layer2
      addresses:
      - 192.168.1.240/28
kubectl apply -f l2.yml

使ってみる

nginx.yml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1
        ports:
        - name: http
          containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
kubectl apply -f nginx.yml
$ kubectl get service
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP    PORT(S)        AGE
nginx        LoadBalancer   10.99.4.87   198.51.100.1   80:31263/TCP   1h

EXTERNAL-IPがついてる、すごい。

アーキテクチャ

BGPモード


MetalLBをインストールするとDaemonSetを使用して、すべてのノードでspeakerというコンテナがホストネットワークモードで動き出す。
これはtype LoadBalancerなServiceがデプロイされた際にそのExternal IPをピアを張っているルータにnext-hopが自分であるように広告する。

同時にkube-proxyが各ノードのiptablesにreplicaに応じた確率でExternal IPからコンテナIPにNATされるように設定を書き込む。

結果、ルーターはExternal IPへの経路をノードの数だけもち、ECMPでルータ→各ノード間はロードバランシングされる。
そしてExternal IPをもらった各ノードはiptablesでさらにロードバランスされる。

一見、2段のロードバランスは無駄に見えるが、これはノード:コンテナが必ずしも1:1でないため。
デフォルトでは、上図のように偏った状態でも各コンテナに均等にロードバランスするために2段構成になっている。

2段目のノード間をまたぐ通信をやめたい場合にはTrafficPolicyをLocalにすることでiptablesからのロードバランス先を自分のノード上にあるコンテナのみに制限することができるが、その場合均等にロードバランスされなくなる場合が出てくることになる。(上図だと左のコンテナから33.3%, 33.3%, 16.7%, 16.7%のようになる)

この構成のいいところはBGPの喋れるルータがあるだけで使え、フォワーディング性能の限界まで水平にスケールアウトできることである。
しかも全ノードをACTとして使え、ノード障害時には自動的にBGPのピアから外れてルーティングされなくなるため、DNSのレコードから消すような操作もいらない。

注意点

コンテナネットワークをオーバレイで作ってる場合は問題ないが、同じくBGPを使うCalicoで構築している場合はコンフリクトするため、いくつかワークアラウンドが紹介されている。
https://metallb.universe.tf/configuration/calico/

L2モード


L2モードではLeaderに選定されたノードのspeakerがExternal IPのARP要求に対して、自分のノードのMACアドレスを応答する。

あとはBGPモードと同様。
見ての通りトラフィックがLeaderノードに集中してしまい、LB自体のスケールはできないが、手軽に構築できる。
障害時にはLeaderが再選出され、Gratuitous ARPでL2のARPテーブルを更新する。

Version 0.7.x以降


v0.7.x以降では基本的なアーキテクチャは同じだが、クラスタ全体でのLeaderの選出をやめ、ServiceごとにLeaderを選出するようになった。

上記の変更により、複数Serviceを建てている場合に多少スケールするようになった。
また、L2 modeでもexternalTrafficPolicyをLocalにすることができるようになった。(Leaderは該当ServiceのPodが動いているノードから選出される)

参考

121
71
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
121
71