筆記一下讓自己不會忘記
kubernetes是一個管理container cluster一個很好的工具, 隨者這樣的技術逐漸成熟,也開始陸陸續續多出了許多困難跟疑點要突破,而我們今天要探討式跟HA有關
再kubernetes中,有提供一個功能,Service,相信有使用過的朋友對這個功能一點都不陌生,我們可以透過這個功能來達到load balance,或是開啟nodePort來進行access的部份,若想要透過public ip來連接endpoint,裏面也有ingress這樣的作法(抱歉以上都不是今天的主題,在這邊不會詳談)
那我們到底要探討什麼?
上面那張是節錄自ref的圖,從這邊我們可以看到一種情況,假設我需要對後面3個node去做load balance,會有一種情況,我們假設node1(10.4.0.3)在這邊掛掉了,會有一個瞬間所有node1的query會失敗,而等kubernetes偵測到時會將這些原本往node1的query導入到另外兩台,但這就是必要花上一點點的時間,雖然看似好像有做到HA,但實際好像也是存在這一點點小缺陷
所以我們用到 IPVS(IP virtual server) 這個方法,而實現方式就是用linux keepalived去實作出來
概念如下圖
我們去設一個虛擬的IP address(10.4.0.50),然後可以看到目前VIP再node1上,而這3個節點會互相去確認彼此的健康狀況,若發現node1掛掉了,node2(or 3)會拿下VIP繼續對外提供服務,等到node1復活了,再把VIP權力交還回去,如此以來這樣就可以把overhead降到最低,實現了更好的HA
好接下來我們會再kubernetes上實作這個東西,讓大家好好了解這個東西的運作
實驗需求
只要確認自己的kubernetes有辦法執行daemonset這功能就行了,
在這邊我們不用去讓實體機器去設定,我們用container去實作這個
我們可以看到ref裏面提供一個小的http server container的yaml檔(keepalived-vip/examples/echoheaders.yaml)
apiVersion: v1kind: ReplicationControllermetadata:name: echoheadersspec:replicas: 1template:metadata:labels:app: echoheadersspec:containers:- name: echoheadersimage: gcr.io/google_containers/echoserver:1.4ports:- containerPort: 8080 //pod上開的port — -apiVersion: v1kind: Servicemetadata:name: echoheaderslabels:app: echoheadersspec:type: NodePortports:- port: 80 //cluster IP的port ==>target PortnodePort: 30302 //node上面開的port ==> target PorttargetPort: 8080 //pod上的portprotocol: TCPname: httpselector:app: echoheaders
可以看到他主要功能就是讓pod 去聽8080 port,再開啟service node Port 來聽30320並會過去,而cluster port也是一樣的
若對這一堆port 很不了解,可以參考ref:http://blog.csdn.net/xinghun_4/article/details/50492041
好我們去建立他
$ kubectl create -f echoheaders.yaml
確認一下是否建立了
$kubectl get pod | grep echoheadersechoheaders-c2r45 1/1 Running 0 1d
由於這是一個rc所以後面的會有些隨機的亂數來表示他的名字
接下來我們要做的事config map
$ echo “apiVersion: v1kind: ConfigMapmetadata:name: vip-configmapdata:10.87.2.50: default/echoheaders” | kubectl create -f -
這功能就很像是在linux我們如果要設定domain name會做的事情就是到/etc/hosts
底下去設定,這邊也是一樣的道理我們將10.4.0.50設為echoheaders
一樣設定好後我們檢查一下
$kubectl get configmapNAME DATA AGEvip-configmap 1 23h
最後我們要再各個node上面設定keepalived了,我們可以先看到我們的node有哪幾台
$kubectl get nodeNAME STATUS AGE VERSION10.87.2.10 Ready 22d 每個人的version都不同10.87.2.12 Ready 21d10.87.2.4 Ready 22d10.87.2.6 Ready 22d10.87.2.8 Ready 21d
我們要建立DaemonSet,這是一個很方便的功能,有時我們會需要再每台node上都建立pod,這個功能可以確保我們可以做到這件事,
看一下daemonset.yaml這個內容
apiVersion: extensions/v1beta1kind: DaemonSetmetadata:name: kube-keepalived-vipspec:template:metadata:labels:name: kube-keepalived-vipspec:hostNetwork: truecontainers:- image: gcr.io/google_containers/kube-keepalived-vip:0.9name: kube-keepalived-vipimagePullPolicy: AlwayssecurityContext:privileged: truevolumeMounts:- mountPath: /lib/modulesname: modulesreadOnly: true- mountPath: /devname: dev# use downward APIenv:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace# to use unicastargs:- — services-configmap=default/vip-configmap# unicast uses the ip of the nodes instead of multicast# this is useful if running in cloud providers (like AWS)#- — use-unicast=truevolumes:- name: moduleshostPath:path: /lib/modules- name: devhostPath:path: /dev//nodeSelector://注意!!這個只是方便你看用,實際建立必須拿掉// type: worker
我們可以看到這個產生的container除了給予權限之外,也跟host共享IP(ref:http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster/)
還有一些其他的設定,這邊我們主要了解這些,然後去建立daemonset
$kubectl create -f vip-daemonset.yaml$ kubectl get daemonset kube-keepalived-vipNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE-SELECTOR AGEkube-keepalived-vip 5 5 5 5 5 <none> 23h
最後我們再檢查一下pod
kubectl get pod -o wide |grep keepalivekube-keepalived-vip-c4sxw 1/1 Running 0 23h 10.87.2.6 10.87.2.6kube-keepalived-vip-c9p7n 1/1 Running 0 23h 10.87.2.8 10.87.2.8kube-keepalived-vip-psdp9 1/1 Running 0 23h 10.87.2.10 10.87.2.10kube-keepalived-vip-xfmxg 1/1 Running 0 23h 10.87.2.12 10.87.2.12kube-keepalived-vip-zjts7 1/1 Running 3 23h 10.87.2.4 10.87.2.4
確實都run再各節點
隨便找一個節點察看狀況
$ kubectl exec kube-keepalived-vip-c4sxw cat /etc/keepalived/keepalived.confglobal_defs {vrrp_version 3vrrp_iptables KUBE-KEEPALIVED-VIP}vrrp_instance vips {state BACKUPinterface eno1virtual_router_id 50priority 103nopreemptadvert_int 1track_interface {eno1}virtual_ipaddress {10.87.2.50}}# Service: default/echoheadersvirtual_server 10.87.2.50 80 { //此為service開的口delay_loop 5lvs_sched wlclvs_method NATpersistence_timeout 1800protocol TCPreal_server 10.2.49.30 8080 { //這裡說明echoheader pod的真實狀況weight 1TCP_CHECK {connect_port 8080connect_timeout 3}}}
最後我們去測試這個功能
$ curl -v 10.87.2.50* Rebuilt URL to: 10.87.2.50/* Trying 10.87.2.50…* Connected to 10.87.2.50 (100.86.2.50) port 80 (#0)> GET / HTTP/1.1> Host: 10.87.2.50> User-Agent: curl/7.47.0> Accept: */*>< HTTP/1.1 200 OK< Server: nginx/1.10.0< Date: Wed, 05 Jul 2017 08:36:37 GMT< Content-Type: text/plain< Transfer-Encoding: chunked< Connection: keep-alive<CLIENT VALUES:client_address=10.2.34.0command=GETreal path=/query=nilrequest_version=1.1request_uri=http://100.86.2.50:8080/SERVER VALUES:server_version=nginx: 1.10.0 — lua: 10001HEADERS RECEIVED:accept=*/*host=100.86.2.50user-agent=curl/7.47.0BODY:* Connection #0 to host 10.87.2.50 left intact-no body in request
顯示結果就像上方的狀況一樣,我們就能確定做出來了
10.87.2.50:80(我們假設的VIP,實際上其實沒有node是用這IP)==>10.2.34:30:8080(echoheader在內網的IP以及他開的port)
ref: https://github.com/kubernetes/contrib/tree/master/keepalived-vip