系列目錄html
前面咱們介紹瞭如何在windows單機以及如何基於docker部署consul集羣,看起來也不是很複雜,然而若是想要把consul部署到kubernetes集羣中並充分利用kubernetes集羣的伸縮和調度功能並不是易事.前面咱們首先部署一個節點,部署完成之後獲取它的ip,而後其它的ip都join到這個ip裏組成集羣.node
前面的部署方式存在如下問題:nginx
咱們知道,在kubernetes裏,當節點發生故障或者資源不足時,會根據策略殺掉節點的一些pod轉而將pod移到其它節點上.這時候咱們就須要從新獲取主節點ip,而後將新的節點加入進去,以上作法不利於充分發揮kubernetes自身的伸縮功能.docker
不論是新節點加入或者失敗後從新生成的節點從新加入集羣,都須要知道主節點ip,這會產生和上面相同的問題,就是須要人工介入.bootstrap
理想的狀態是,當集羣主節點切換時,新節點仍然可以在無需人工介入的狀況下自動加入集羣.咱們解決這個問題的思路以下:使用kubernetes集羣的dns功能,而不直接硬編碼節點的ip.若是集羣中有三個server,則這三個sever中必然有一個是主節點,咱們能夠依次嘗試經過dns來解析到具體的節點,依次嘗試加入每個sever節點,嘗試到真正的主節點時便可以加入集羣.windows
咱們首先建立服務,定義服務的文件名爲consul-service.ymlcentos
apiVersion: v1 kind: Service metadata: name: consul labels: name: consul spec: type: ClusterIP ports: - name: http port: 8500 targetPort: 8500 - name: https port: 8443 targetPort: 8443 - name: rpc port: 8400 targetPort: 8400 - name: serflan-tcp protocol: "TCP" port: 8301 targetPort: 8301 - name: serflan-udp protocol: "UDP" port: 8301 targetPort: 8301 - name: serfwan-tcp protocol: "TCP" port: 8302 targetPort: 8302 - name: serfwan-udp protocol: "UDP" port: 8302 targetPort: 8302 - name: server port: 8300 targetPort: 8300 - name: consuldns port: 8600 targetPort: 8600 selector: app: consul
經過以上服務咱們把pod的端口映射到集羣中,經過名稱咱們能夠看到每個商品作什麼類型通訊用的.這個服務會選擇標籤爲app: consul
的pod.經過這個示例咱們也能夠看到,對於一些複雜的服務,採用nodeport類型的服務是很不可取的.這裏使用的是clusterip類型的服務.後面咱們會經過nginx ingress controller把http端口暴露到集羣外,供外部調用.api
咱們經過kubectl create -f consul-service.yml
來建立這個服務瀏覽器
這裏咱們採用的是statefulset的方式建立的server,這裏之因此使用statefulset是由於statefulset包含的pod名稱是固定的(普通pod以必定hash規則隨機生成),若是命名規則固定,pod數量固定,則咱們能夠預先知道它們的dns規則,以便在自動加入集羣中時使用. statefulset建立文件以下(名爲consul-statefulset.yml)bash
apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: consul spec: serviceName: consul replicas: 3 template: metadata: labels: app: consul spec: terminationGracePeriodSeconds: 10 containers: - name: consul image: consul:latest args: - "agent" - "-server" - "-bootstrap-expect=3" - "-ui" - "-data-dir=/consul/data" - "-bind=0.0.0.0" - "-client=0.0.0.0" - "-advertise=$(PODIP)" - "-retry-join=consul-0.consul.$(NAMESPACE).svc.cluster.local" - "-retry-join=consul-1.consul.$(NAMESPACE).svc.cluster.local" - "-retry-join=consul-2.consul.$(NAMESPACE).svc.cluster.local" - "-domain=cluster.local" - "-disable-host-node-id" env: - name: PODIP valueFrom: fieldRef: fieldPath: status.podIP - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - containerPort: 8500 name: ui-port - containerPort: 8400 name: alt-port - containerPort: 53 name: udp-port - containerPort: 8443 name: https-port - containerPort: 8080 name: http-port - containerPort: 8301 name: serflan - containerPort: 8302 name: serfwan - containerPort: 8600 name: consuldns - containerPort: 8300 name: server
經過以上定義文件咱們能夠看到,裏面最核心的部分是--retry-join後面的dns規則,因爲咱們指定了pod名稱爲consul,而且有三個實例,所以它們的名稱爲consul-0,consul-1,consul-2,即使有節點失敗,起來之後名稱仍然是固定的,這樣無論新起的podIp是多少,經過dns都可以正確解析到它.
這裏的data-dir之前沒有提到過,很容易理解,就是consul持久化數據存儲的位置.咱們前面說過server節點的數據都是要持久化存儲的,這個data-dir即是持久化數據存儲的位置.
咱們能夠經過進入到pod內部來查看consul集羣的成員信息
[centos@k8s-master ~]$ clear [centos@k8s-master ~]$ kubectl exec -it consul-0 /bin/sh / # consul members Node Address Status Type Build Protocol DC Segment consul-0 10.244.1.53:8301 alive server 1.4.4 2 dc1 <all> consul-1 10.244.1.54:8301 alive server 1.4.4 2 dc1 <all> consul-2 10.244.1.58:8301 alive server 1.4.4 2 dc1 <all> / #
經過以上你們能夠看到,Ip 53和54是相鄰的,可是58是跳躍的,這是由於我故意刪除了其中的一個pod,因爲咱們在建立statefulset的時候指定的副本集個數爲3,所以kubernetes會從新建立一個pod,通過測試這個新建立的pod仍然可以正確加入集羣,符合咱們的需求.
咱們在容器內執行curl localhost:8500
的時候,會出現如下結果
<a href="/ui/">Moved Permanently</a>.
不用擔憂,以上結果是正確的,由於經過瀏覽器訪問的話,localhost:8500會自動跳轉到http://localhost:8500/ui/dc1/services
默認ui展現界面,因爲curl沒法模擬瀏覽器交互跳轉行爲,所以顯示以上內容永久重定向.
以上節本完成了demo演示,可是仍然有兩個問題
其它有狀態服務也要把持久化數據目錄掛載到宿主機上
下面貼出完整的配置
apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: consul spec: serviceName: consul replicas: 3 template: metadata: labels: app: consul spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - consul topologyKey: kubernetes.io/hostname terminationGracePeriodSeconds: 10 containers: - name: consul image: consul:latest args: - "agent" - "-server" - "-bootstrap-expect=3" - "-ui" - "-data-dir=/consul/data" - "-bind=0.0.0.0" - "-client=0.0.0.0" - "-advertise=$(PODIP)" - "-retry-join=consul-0.consul.$(NAMESPACE).svc.cluster.local" - "-retry-join=consul-1.consul.$(NAMESPACE).svc.cluster.local" - "-retry-join=consul-2.consul.$(NAMESPACE).svc.cluster.local" - "-domain=cluster.local" - "-disable-host-node-id" volumeMounts: - name: data mountPath: /consul/data env: - name: PODIP valueFrom: fieldRef: fieldPath: status.podIP - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - containerPort: 8500 name: ui-port - containerPort: 8400 name: alt-port - containerPort: 53 name: udp-port - containerPort: 8443 name: https-port - containerPort: 8080 name: http-port - containerPort: 8301 name: serflan - containerPort: 8302 name: serfwan - containerPort: 8600 name: consuldns - containerPort: 8300 name: server volumes: - name: data hostPath: path: /home/data
以上須要注意的是,要掛載到的宿主機的目錄必須是預先存在的,kubernetes並不會在宿主機上建立須要的目錄.
因爲主機節不能容納普通pod,所以要完成以上高可用部署,須要集羣中至少有四個節點.(若是不使用完整配置,則只須要一主一從便可)
經過以上配置,咱們即可以在kubernetes集羣內部訪問consul集羣了,可是若是想要在集羣外部訪問.還須要將服務暴露到集羣外部,前面章節咱們也講到過如何將服務暴露到集羣外部.這裏咱們使用 nginx-ingress 方式將服務暴露到外部.
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-consul namespace: default annotations: kubernets.io/ingress.class: "nginx" spec: rules: - host: consul.my.com.local http: paths: - path: backend: serviceName: consul servicePort: 8500
因爲我沒有申請域名,所以我使用的hosts裏添加映射的方式來訪問的.我已經在hosts文件裏添加了映射,這裏直接打開瀏覽器訪問
咱們點擊Nodes
能夠看到一共有三個節點
節點後面的綠色對號標識表示服務的狀態是可用的
若是集羣中有節點掛掉後,存活的節點仍然符合法定數量(構建一個consul集羣至少須要兩個存活節點進行仲裁),因爲statefulset規定的副本集的數量是3,所以k8s會保證有3個數量的副本集在運行,當k8s集羣發現運行的副本數量少於規定數量時,便會根據調度策略從新啓動必定數量pod以保證運行副本集數量和規定數量相符.因爲在編排consul部署時使用了retry-join
參數,所以有新增節點會自動嘗試從新加入集羣(傳統方式是根據ip來加入集羣),一樣若是掛掉的是master節點也不用擔憂,若是存活節點數量仍然符合法定數量(這裏的法定數量並非指statefulset副本集數量,而是consul組成集羣所須要最小節點數量),consul會依據必定策略從新選擇master節點.
咱們先來看一下集羣中pod狀態
[root@k8s-master consul]# kubectl get pod NAME READY STATUS RESTARTS AGE consul-0 1/1 Running 0 9m20s consul-1 1/1 Running 0 9m19s consul-2 1/1 Running 0 9m17s easymock-dep-84767b6f75-57qm2 1/1 Running 1708 35d easymock-dep-84767b6f75-mnfzp 1/1 Running 1492 40d mvcpoc-dep-5856db545b-qndrr 1/1 Running 1 46d sagent-b4dd8b5b9-5m2jc 1/1 Running 3 54d sagent-b4dd8b5b9-brdn5 1/1 Running 1 40d sagent-b4dd8b5b9-hfmjx 1/1 Running 2 50d stock-dep-5766dfd785-gtlzr 1/1 Running 0 5d18h stodagent-6f47976ccb-8fzmv 1/1 Running 3 56d stodagent-6f47976ccb-cv8rg 1/1 Running 2 50d stodagent-6f47976ccb-vf7kx 1/1 Running 3 56d trackingapi-gateway-dep-79bb86bb57-x9xzp 1/1 Running 3 56d www 1/1 Running 1 48d
咱們看到有三個consul pod在運行
下面咱們手動殺掉一個pod,來模擬故障.
[root@k8s-master consul]# kubectl delete pod consul-0 pod "consul-0" deleted
因爲consul-0
被幹掉,所以consul集羣中consul-0
變得不可用
過一段時間後,k8s檢測到運行的pod數量少於stateful規定的數量,便會從新再啓動一個pod(須要注意的是,這裏的從新啓動並非把原來pod從新啓動,而是從新再調度一個全新的pod到集羣中,這個pod與掛掉的pod無任何關係,固然ip也是不同的,若是仍然依賴ip,則會帶來無限麻煩)
咱們能夠看到,這時候consul-0服務的ip已經變成了86(前面是85)
部署nginx ingress可能並非一件很是容易的事,由其是對使用單節點docker on windows的朋友來講,若是有的朋友不想部署nginx ingress,僅作爲測試使用,也能夠把服務的類型由
ClusterIP
更改成NodePort
類型,這樣就能夠像docker同樣把服務的端口映射到宿主機端口,方便測試.
本節涉及到的內容比較多,也相對比較複雜(前面也屢次說過,部署有狀態服務是難點),所以後面會專門再開一節來詳細講解本文中的一些細節,有問題的朋友也能夠留言或者經過其它方式聯繫我,你們共同交流