kubernetes實戰之部署一個接近生產環境的consul集羣

系列目錄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演示,可是仍然有兩個問題

  • 第一,咱們直接把data存儲到了容器內,若是容器被銷燬而後從新建立,則數據會丟失.這樣顯然是存在風險的,正確的作法是把容器內持久化數據的目錄掛載到宿主機上,可是因爲個人測試集羣中有一主一從,所以把容器目錄掛載到宿主機會形成目錄衝突,因此這裏沒有把存儲內容掛載到宿主機.

其它有狀態服務也要把持久化數據目錄掛載到宿主機上

  • 咱們在部署server的時候爲了保證高可用,兩個或以上server不能部署到同一臺機器或者因爲故障出現被轉移到了相同機器,這樣一方面增長了server的壓力,同時另外一方面也下降了可用性,由於宿主機宕機時上面的節點都會宕掉,這樣與多server部署高可用的初衷相違背的.實際上,部署其它有狀態的服務也要考慮到這個問題.在kubernetes裏,解決這個問題使用的是pod的反親和屬性,親和pod和主動靠攏,反親和屬性的pod則偏偏相反,它們之間相互排斥.利用這個特性能夠知足咱們的需求.這裏之因此沒有使用反親和的緣由也是集羣的節點不夠,若是節點相互排斥就沒法部署成功了.

下面貼出完整的配置

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文件裏添加了映射,這裏直接打開瀏覽器訪問

img

咱們點擊Nodes能夠看到一共有三個節點

img

節點後面的綠色對號標識表示服務的狀態是可用的

節點掛掉後新啓動節點自動加入集羣

若是集羣中有節點掛掉後,存活的節點仍然符合法定數量(構建一個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變得不可用

img

過一段時間後,k8s檢測到運行的pod數量少於stateful規定的數量,便會從新再啓動一個pod(須要注意的是,這裏的從新啓動並非把原來pod從新啓動,而是從新再調度一個全新的pod到集羣中,這個pod與掛掉的pod無任何關係,固然ip也是不同的,若是仍然依賴ip,則會帶來無限麻煩)

img

咱們能夠看到,這時候consul-0服務的ip已經變成了86(前面是85)

部署nginx ingress可能並非一件很是容易的事,由其是對使用單節點docker on windows的朋友來講,若是有的朋友不想部署nginx ingress,僅作爲測試使用,也能夠把服務的類型由ClusterIP更改成NodePort類型,這樣就能夠像docker同樣把服務的端口映射到宿主機端口,方便測試.

本節涉及到的內容比較多,也相對比較複雜(前面也屢次說過,部署有狀態服務是難點),所以後面會專門再開一節來詳細講解本文中的一些細節,有問題的朋友也能夠留言或者經過其它方式聯繫我,你們共同交流

相關文章
相關標籤/搜索