目的
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に関するパラメータの指定があります。
# 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ファイルの中で行えます。
# 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するだけで、ほぼほぼ完了しました。
- YAMLファイルの適用 (kubectl apply -f ...)
- 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
似た事例は報告されていて、下記の報告が該当します。
- [Ingress DS doesnt create PODS after fresh install #2754](Ingress DS doesnt create PODS after fresh install #2754)
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 の追加
ここで参考にした資料はこちらです。
前回はdefault-backendに、MetalLBからIPを割り当てられて喜んでいましたが、default-backendは、controller serviceから最終的に振られる終端として機能するため、前段のcontroller serviceにMetalLBからIPを割り当てる必要があり、本質的に間違っていました。
kubesprayからingress-nginxを展開して遭遇した課題をまとめると、次の2点に集約されます。
- 各ノード(kubectl get nodesの出力)に適切なラベルが割り当てられていないため、ingress-nginx-controllerのpodsが作成されない ← 前節で対応済み
- 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
以上