LoginSignup
12
13

More than 3 years have passed since last update.

Kubesprayで準備したk8sにMetalLBをデプロイしてみる

Last updated at Posted at 2018-05-09

目的

APU2とAPU1を利用して、合計4台のノードにkubesprayを使って、kubernetesをデプロイしたところから、いろいろと試行錯誤をして勉強してきました。

DockerHubに登録したイメージをある程度動かせるようになってきて、サービスを公開するためには、LoadBalancerが欲しくなってきます。

最初は慣れているkeepalivedをデプロイする方法を見つけてみたものの、そのままだと1.9でSEGVするという情報もあったため、MetalLBをデプロイしてみることにしました。

結論から書くと、type: LoadBalancerを指定したService毎に1つVIPを割り当てる、という作業は非常に簡単に行なえました。
IngressのServiceに割り当てることで、いろいろ便利になりそうです。
(2018/06/08加筆: ingress-nginx-controllerのserviceにexternal-ipを割り当てる必要があります。この内容については後半に加えました)

Kubespray等でBare-metal上にk8s環境を構築している場合は、MetalLBを導入することで、簡単にサービスを公開することができると思われます。

参考資料

KubesprayによるMetalLBのデプロイメント (2021年3月30日追記)

最新のKubesprayでは、MetalLBの導入も同時に行なえます。
inventory/以下のgroups_vars/k8s-cluster/addons.yml ファイルの、最後の部分にMetalLBに関するパラメータの指定があります。

addons.ymlのMetalLB関連箇所の抜粋
# MetalLB deployment
metallb_enabled: true
metallb_ip_range:
  - "192.168.10.110-192.168.10.198"
metallb_version: v0.9.5
metallb_protocol: "layer2"
# metallb_port: "7472"
# metallb_limits_cpu: "100m"
# metallb_limits_mem: "100Mi"
metallb_additional_address_pools:
  rabbitmq-pool:
    ip_range:
      - "192.168.10.199-192.168.10.199"
    protocol: "layer2"
    auto_assign: false
# metallb_protocol: "bgp"
# metallb_peers:
#   - peer_address: 192.0.2.1
#     peer_asn: 64512
#     my_asn: 4200000000
#   - peer_address: 192.0.2.2
#     peer_asn: 64513
#     my_asn: 4200000000

この設定はRabbitMQをデプロイした際に設定した固定IPの設定も含まれているので、metallb_additional_address_pools:の設定を無視すると、必要なのは先頭から6行目までの部分だけです。

必要な設定はaddons.ymlファイルだけでなく、公式ページに記載されているように、準備作業としてStrict ARPモードを有効にする必要があります。

この設定は、addons.ymlファイルと同じディレクトリにあるk8s-cluster.ymlファイルの中で行えます。

k8s-cluster.ymlで必要な設定変更箇所
# configure arp_ignore and arp_announce to avoid answering ARP queries from kube-ipvs0 interface
# must be set to true for MetalLB to work
kube_proxy_strict_arp: true

Kubesprayをこれから利用する場合には、まずaddons.ymlファイルの内容を確認してください。

作業の流れ

公式ドキュメントからCopy&Pasteするだけで、ほぼほぼ完了しました。

  1. YAMLファイルの適用 (kubectl apply -f ...)
  2. ConfligMap用のYAMLファイルを作成し、適用 (kubectl apply -f ...)

今回は特定のサブネット(192.168.10.0/24)内部で稼動すれば十分だったため、Layer2(IPレベルでのLoadBalance)での設定方法を利用しています。

2018/05/09時点では、下記のようにv0.6.2を指定した方法が紹介されていたので、そのまま利用しています。バージョンは頻繁に上がりますので公式サイトのInstallationセクションを確認してください。

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

チュートリアルにあるようなYAMLファイルを作成して、最下行のIPレンジだけ自分の環境に合うように変更しています。

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.10.150-192.168.10.199

作成したファイルを指定して、$ kubectl apply -f <filename> で反映させます。

ServiceにIPを割り当てる

最後にサンプルとしてdefaultのnamespaceにデプロイしているnginxの、Service定義を変更して、type:に指定されているClusterIPの文字列をLoadBalancerに変更しています。

$ kubectl edit svc/sample-nginx

変更が終ったら、ServiceのStatusを確認します。

$ kubectl get svc

NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
sample-nginx      LoadBalancer   10.233.36.251   192.168.10.150  80:30715/TCP   2h

これで、$ curl 192.168.10.150 を実行して、サンプルのWebページが表示されることが確認できました。

ingress-nginxでの利用

Kubesprayでingress-nginxを有効にしているので、このServiceでもLoadBalancerを指定してみます。

$ kubectl -n ingress-nginx edit svc/ingress-nginx-default-backend

具体的な利用はこれからですが、External-IPには無事にMetalLBからIPが割り当てられています。

2018/06/08追記: ここが間違っていたので、ここからingress-nginxを使うまでの記事を追記しています。

[加筆] kubesprayによるingress-nginxの利用について

後から確認したところ、ingress-nginx-controllerのpodsが作成されていないことに気がつきました。

$ kubectl -n ingress-nginx get ds/ingress-nginx-controller 
NAME                       DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR                          AGE
ingress-nginx-controller   0         0         0         0            0           node-role.kubernetes.io/ingress=true   31d

似た事例は報告されていて、下記の報告が該当します。

nodeSelectorが設定されているのに、該当するラベルがnodeに設定されていない状況です。

ansibleではkube-masterというhosts groupに、ingress-nginxが展開されるので、node1やnode2にラベルを追加してみます。

$ kubectl edit node/node1
$ kubectl edit node/node2
  ... 省略 ...
  labels:
    ... 省略 ...
    node-role.kubernetes.io/ingress: "true"
  name: node1
  ... 省略 ...

この後で、確認するingress-nginx-controllerのpodが動きだしています。

$ kubectl -n ingress-nginx get ds/ingress-nginx-controller
NAME                       DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR                          AGE
ingress-nginx-controller   2         2         1         2            1           node-role.kubernetes.io/ingress=true   31d

ここまでで、controllerのpodsは動き始めました。

[加筆] 前回の失敗と、controller service の追加

ここで参考にした資料はこちらです。
* AKSのNGINX Ingress Controllerのデプロイで悩んだら

前回はdefault-backendに、MetalLBからIPを割り当てられて喜んでいましたが、default-backendは、controller serviceから最終的に振られる終端として機能するため、前段のcontroller serviceにMetalLBからIPを割り当てる必要があり、本質的に間違っていました。

kubesprayからingress-nginxを展開して遭遇した課題をまとめると、次の2点に集約されます。

  1. 各ノード(kubectl get nodesの出力)に適切なラベルが割り当てられていないため、ingress-nginx-controllerのpodsが作成されない ← 前節で対応済み
  2. ingress-nginx-controllerのpodsに対応するserviceが作成されていない ← 今回

controllerのdeploymentの情報を確認すると、"k8s-app: ingress-nginx"がラベルとして付いています。
これに対応するServiceを追加します。

適当な名前(ingress-nginx-controller-service.yaml)で下記のようなYAMLファイルを作成します。

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
spec:
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https
  selector:
    k8s-app: ingress-nginx

参考にした資料では、k8s-app: ingress-nginx-lbが指定されていたので変更しています。
ポート443関連の証明書周りの説明は省略して、このファイルを適用します。

$ kubectl -n ingress-nginx apply -f ingress-nginx-controller-service.yaml

まず前回間違えて、default-backendに設定したtypeをNodePortに変更しておきます。

$ kubectl -n ingress-nginx edit svc ingress-nginx-default-backend

次にMetalLBからIPを割り当ててもらうために、type: LoadBalancer に変更します。

$ kubectl -n ingress-nginx edit svc ingress-nginx-controller

最終的には、次のような出力になっています。

$ kubectl -n ingress-nginx get svc 
NAME                            TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller        LoadBalancer   10.233.45.75    192.168.10.151   80:31247/TCP,443:32258/TCP   9m
ingress-nginx-default-backend   NodePort       10.233.18.136   <none>        80:32316/TCP                 31d

ここまでで、default namespaceに作成したnginx serviceとingress定義によって、MetalLBから割り当てたEXTERNAL-IP(実際にはさらにDNSに登録したVirtualHost名)で反応するようになりました。

[加筆] 余談、テスト用のWebサーバーについて

いろいろ資料を探していると、minikubeの資料の中にechoserverについて書かれていました。

default namespaceにWebサーバーを展開するには、次のようなコマンドで可能になります。

$ kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.4 --port=8080
$ kubectl expose deployment hello-minikube --type=NodePort
$ kubectl get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-minikube    NodePort    10.233.58.216   <none>        8080:31173/TCP   7s
kubernetes        ClusterIP   10.233.0.1      <none>        443/TCP          31d
service-mynginx   NodePort    10.233.16.224   <none>        80:31351/TCP     6m

自前のservice-mynginxとhello-minikubeが動くので、ingressのテストに使ってみます。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-kubeweb01
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: kubeweb01.example.com
      http:
        paths:
        - path: "/test"
          backend:
            serviceName: service-mynginx
            servicePort: 80
        - path: "/echo"
          backend:
            serviceName: hello-minikube
            servicePort: 8080

適当な名前(ingress-kubeweb01.yaml)で保存して、適用します。

$ kubectl apply -f ingress-kubeweb01.yaml

echoserverが入っているので、rewrite-targetの有無で、終端となるnginxのpodsに届くリクエストのURIが'/'に変換されるか、挙動の違いが分かると思います。

うまく動かない時には、namespaceが違うとうまくいかないのかなとか思っていましたが、そんな事はなく、ingress-nginx-controllerのserviceが動き始めてからは、どのnamespaceにingress定義を追加しても、ちゃんと反応してくれています。

dashboardを動かす方法は、参考資料にあるので、これを適用すると、同じVirtualHostの下で動きます。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-dashboard
  namespace: kube-system
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: "/"
    nginx.ingress.kubernetes.io/add-base-url: "true"
    nginx.ingress.kubernetes.io/secure-backends: "true"
    nginx.ingress.kubernetes.io/configuration-snippet: rewrite ^(/dashboard)$ $1/ permanent;
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: kubeweb01.example.com
      http:
        paths:
        - path: "/dashboard"
          backend:
            serviceName: kubernetes-dashboard
            servicePort: 443

以上

12
13
0

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
12
13