歡迎光臨
每天分享高質量文章

Spring Cloud Kubernetes容器化實踐

隨著公司業務量和產品線的增加,專案越來越多,普通運維繫統架構對整個軟體研發生命週期的管理越來越難,效率低下,難以統一管理。近年來Docker統一了容器標準,對於軟體開發流程產生了深遠的影響,Docker可以一次打包,處處執行。過去幾年Kubernetes平臺發展日新月益,Kubernetes統一了容器排編王者的地位,我個人認為kubernetes可以說是對普通運維架構一次突破性的革命。
利用Kubrenets叢集平臺可以很方便的對容器服務進行集中管理,可以非常高效的對容器服務進行編排、排程、擴容、升級、回滾、監控、集中收集日誌等,基本上把傳統運維架構需要考慮的問題全部解決了,而DevOps容器化也是整個軟體開發流程的必經之路,因此我們對現有老舊的運維平臺進行替換,統一利用Kubernetes對所有業務進行管理。
原有運維繫統缺點

 

  • 原有業務佈署在虛擬機器ECS、KVM上,指令碼分散、日誌分散、難於集中收集管理,監控不統一,CPU、記憶體、磁碟資源使用率低,運維效率極低,無法集中管理。

  • 新業務佈署需要開通新的虛擬機器,需要單獨定製監控,各種Crontab,配置指令碼,效率低下,CI/CD Jenkins配置繁瑣。

 

Kubernetes容器化優勢

 

  • 利用Kubernetes容器平臺namespaces對不同環境進行區分,建立不同dev、test、stage、prod環境,實現隔離。

  • 透過容器化集中佈署所有業務,實現一鍵佈署所需環境業務。

  • 統一集中監控報警所有容器服務異常狀態。

  • 統一集中收集所有服務日誌至ELK叢集,利用Kibana面板進行分類,方便開發查日誌。

  • 基於Kubernetes命令列二次開發,相關開發、測試人員直接操作容器。

  • 基於RBAC對不同的環境授於不同的開發、測試訪問Kubernetes許可權,防止越權。

  • 透過Jenkins統一CI/CD編譯釋出過程。

  • 專案容器化後,整體伺服器CPU、記憶體、磁碟、資源利用減少50%,運維效率提高60%,原來需要N個運維做的事,現在一個人即可搞定。

Kubernetes本身是一套分散式系統,要用好會遇到很多問題,不是說三天兩頭就能搞定,需要具備網路、Linux系統、儲存,等各方面專業知識,在使用過程中我們也踩了不少坑,我們是基於二進位制包的方式安裝Kubernetes叢集,我們Kubernetes叢集版本為1.10,經過一段時間的實踐,Kubernetes對於我們整個開發、測試、釋出、運維流程幫助非常大,值得大力推廣。
網路方案選擇

 

  1. Flannel是 CoreOS 團隊針對 Kubernetes 設計的一個改寫網路(Overlay Network)工具,所有節點透過flanneld節點服務同步路由,使用簡單、方便、穩定,是Kubernetes入門首選。

  2. Calico是基於BGP協議的路由方案,支援ACL,部署複雜,出現問題難排查。

  3. Weave是基於UDP承載容器之間的資料包,並且可以完全自定義整個叢集的網路拓撲,國內使用較少。

  4. Open vSwitch是一個生產質量的多層虛擬交換機,它旨在透過程式設計擴充套件實現大規模網路自動化,同時仍支援標準管理介面和協議,OpenShift-kubernetes平臺和混合雲使用比較多。

我們對各個網路元件進行過調研對比,網路方案選擇的是flanneld-hostgw+ipvs,在Kubernetes 1.9之前是不支援IPVS的,kube-proxy負責所有SVC規則的同步,使用的iptables,一個service會產生N條iptables記錄。如果SVC增加到上萬條,iptables-svc同步會很慢,得幾分鐘,使用IPVS之後,所有節點的SVC由IPVS LVS來負載,更快、更穩定,而且簡單方便,使用門檻低。host-gw會在所有節同步路由表,每個容器都分配了一個IP地址,可用於與同一主機上的其他容器進行通訊。對於透過網路進行通訊,容器與主機的IP地址系結。flanneld host-gw效能接近Calico,相對來說Falnneld配置佈署比Calico簡單很多。順便提下flanneld-vxlan這種方式,需要透過UDP封包解包,效率較低,適用於一些私有雲對網路封包有限制,禁止路由表新增等有限制的平臺。
Flanneld透過為每個容器提供可用於容器到容器通訊的IP來解決問題。它使用資料包封裝來建立跨越整個群集的虛擬改寫網路。更具體地說,Flanneld為每個主機提供一個IP子網(預設為/ 24),Docker守護程式可以從中為每個主機分配IP。
Flannel使用etcd來儲存虛擬IP和主機地址之間的對映。一個Flanneld守護行程在每臺主機上執行,並負責維護etcd資訊和路由資料包。
在此提一下,在使用flannled使用過程中遇到過嚴重bug,即租約失效,flanneld會shutdown節點網路元件,節點網路直接崩掉,解決辦法是設定永久租期:https://coreos.com/flannel/docs/latest/reservations.html#reservations。
傳統業務遷移至Kubernetes遇到的問題和痛點,DevOps遇到的問題

 

使用Kubernetes會建立兩套網路,服務之間呼叫透過service域名,預設網路、域名和現有物理網路是隔離的,開發,測試,運維無法像以前一樣使用虛擬機器,Postman IP+埠除錯服務, 網路都不通,這些都是問題。
  • Pod網路和物理網路不通,Windows辦公電腦、Linux虛擬機器上現有的業務和Kubernetes是隔離的。

  • SVC網路和物理網路不通,Windows辦公電腦、Linux虛擬機器上現有的業務和Kubernetes是隔離的。

  • SVC域名和物理網路不通,Windows辦公電腦、Linux虛擬機器上現有的業務和Kubernetes是隔離的。

  • 原有Nginx配置太多的location路由規則,有的有幾百層,不好遷移到ingress-nginx,ingress只支援簡單的規則。

  • SVC-NodePort訪問,在所有Node上開啟埠監聽,佔用Node節點埠資源,需要記住埠號。

  • ingress-nginx http 80埠, 必需透過域名引入,http 80埠必需透過域名引入,原來簡單nginx的location可以透過ingress引入。

  • ingress-nginx tcp udp埠訪問需要配置一個lb,很麻煩,要先規劃好lb節點同樣也需要訪問lb埠。

  • 原有業務不能停,繼續執行,同時要能相容Kubernetes環境,和Kubernetes叢集內服務互相通訊呼叫,網路需要通。

傳統虛擬機器上佈署服務我們只需要一個地址+埠直接訪問除錯各種服務,Kubernetes是否能做到不用改變使用者使用習慣,無感知使用呢?答案是打通DevOps全鏈路,像虛擬機器一樣訪部Kubernetes叢集服務 , 我們打通Kubernetes網路和物理網路直通,物理網路的DNS域名呼叫Kubernetes DNS域名服務直接互訪,所有服務互通。公司原有業務和現有Kubernetes叢集無障礙互訪。
配置一臺Kubernetes Node節點機做路由轉發,配置不需要太高,佈署成路由器樣式,所有外部訪問Kubernetes叢集流量都經該節點,本機IP:192.168.2.71。
  1. vim /etc/sysctl.conf
  2. net.ipv4.ip_forward = 1

 

設定全網路由通告,交換機或者Linux、Windows主機加上靜態路由,打通網路。
  1. route add -net 172.20.0.0 netmask 255.255.0.0 gw 192.168.2.71
  2. route add -net 172.21.0.0 netmask 255.255.0.0 gw 192.168.2.71

 

增加DNS伺服器代理,外部服務需要訪問Kubernetes service域名,首先需要解析域名,Kubernetes服務只對叢集內部開放,此時需要外部能呼叫KubeDNS 53號埠,所有辦公電腦,業務都來請求KubeDNS肯定撐不住,事實上確實是撐不住,我們做過測試,此時需要配置不同的域名進行分流策略,公網域名走公網DNS,內部.svc.cluster.local走KubeDNS。
1、建立DNS代理伺服器,ingress建立一個nginx-ingress服務反代KubeDNS,ingress-nginx系結到DNS節點執行,在節點上監聽DNS 53埠。
  1. [root@master1 kube-dns-proxy-1.10]# cat tcp-services-configmap.yaml
  2. kind: ConfigMap
  3. apiVersion: v1
  4. metadata:
  5.  name: tcp-services
  6.  namespace: ingress-nginx
  7. data:
  8.  53: "kube-system/kube-dns:53"
  9. [root@master1 kube-dns-proxy-1.10]# cat udp-services-configmap.yaml
  10. kind: ConfigMap
  11. apiVersion: v1
  12. metadata:
  13.  name: udp-services
  14.  namespace: ingress-nginx
  15. data:
  16.  53: "kube-system/kube-dns:53"
  17. [root@master1 kube-dns-proxy-1.10]# cat ingress-nginx-deploy.yaml
  18. apiVersion: extensions/v1beta1
  19. kind: Deployment
  20. metadata:
  21.  name: nginx-ingress-controller-dns
  22.  namespace: ingress-nginx
  23. spec:
  24.  replicas: 1
  25.  selector:
  26.    matchLabels:
  27.      app: ingress-nginx-dns
  28.  template:
  29.    metadata:
  30.      labels:
  31.        app: ingress-nginx-dns
  32.      annotations:
  33.        prometheus.io/port: '10254'
  34.        prometheus.io/scrape: 'true'
  35.    spec:
  36.      hostNetwork: true
  37.      serviceAccountName: nginx-ingress-serviceaccount
  38.      containers:
  39.        - name: nginx-ingress-controller-dns
  40.          image: registry-k8s.novalocal/public/nginx-ingress-controller:0.12.0
  41.          args:
  42.            - /nginx-ingress-controller
  43.            - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
  44.           # - --configmap=$(POD_NAMESPACE)/nginx-configuration
  45.            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
  46.            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
  47.            - --annotations-prefix=nginx.ingress.kubernetes.io
  48.          env:
  49.            - name: POD_NAME
  50.              valueFrom:
  51.                fieldRef:
  52.                  fieldPath: metadata.name
  53.            - name: POD_NAMESPACE
  54.              valueFrom:
  55.                fieldRef:
  56.                  fieldPath: metadata.namespace
  57.          ports:
  58.          - name: http
  59.            containerPort: 80
  60.          #- name: https
  61.          #  containerPort: 443
  62.          livenessProbe:
  63.            failureThreshold: 3
  64.            httpGet:
  65.              path: /healthz
  66.              port: 10254
  67.              scheme: HTTP
  68.            initialDelaySeconds: 10
  69.            periodSeconds: 10
  70.            successThreshold: 1
  71.            timeoutSeconds: 1
  72.          readinessProbe:
  73.            failureThreshold: 3
  74.            httpGet:
  75.              path: /healthz
  76.              port: 10254
  77.              scheme: HTTP
  78.            periodSeconds: 10
  79.            successThreshold: 1
  80.            timeoutSeconds: 1
  81.      nodeSelector:
  82.        node: dns

 

2、最簡單快捷的方式是安裝Dnsmasq,當然你也可以用Bind,PowerDNS,CoreDNS等改造,上游DNS配置為上一步驟增加nginx-ingress dns的地址,所有辦公,業務電腦全部設定DNS為此機,dnsmasq.conf配置分流策略。
  1. no-resolv
  2. server=/local/192.168.1.97
  3. server=114.114.114.114

 

完成以上步驟後Kubernetes pod網路、service網路、 service域名和辦公網路,現有ECS、虛擬機器完美融合,無縫訪問,容器網路問題完美搞定。
Windows訪問Kubernetes service暢通無組,開發測試,完美無縫對接。
 
 
ingress-nginx服務入口接入

 

服務釋出後最終對接的是使用者,使用者訪問Kubernetes服務需要透過nginx或其它http伺服器接入,對於服務接入我們同時使用兩種不同的方案,取決於nginx location的複雜度,location規則簡單的我們使用第一種方案,由於各種問題,location複雜我們使用第二種方案。
  1. client——-ingress-nginx—–upstream—-podip,對於ingress-nginx官方使用的原始方案,先配置ingress規則路由,ingress對接不同的service-dns域名,ingress自動發現後端podip,透過upstream負載不同的後端podip,不同的域名路由到不同的Kubernetes後端podip,使用者客戶端訪問流量會負載到不同的Pod上。

  2. client——nginx——-upstream——svc—–podip改造現有nginx相容Kubernetes,對接Kubernetes service服務。對於nginx location規則過多,不能很好的相容nginx-ingress導致使用Kubernetes非常困難,難以普及,在不變更現有nginx配置的情況下如何對接Kubernetes這是一個問題,經過前面網路打通的步驟我們所有網路的問題都已解決。現在只需改動很小部分即可相容,由於Kubernetes podip是漂移的,IP總是會變的,nginx只能是對接SVC域名才能持久,但是nginx解析域名有個bug,只解析一次,如果在此期間刪除了yaml,nginx會找不到後端svcip,所以這裡要設定代理變數set $backend,設定resolver的DNS為代理DNS地址,設定解析域名時間和變數解決該問題。

  1. location /tomcat/ {
  2.            resolver 192.168.1.97 valid=3600s;
  3.            set $backend "tomcat.dac-prod.svc.cluster.local";
  4.            error_log  logs/dac_error.log  error;
  5.            access_log  logs/dac_access.log  main;
  6.            proxy_set_essay-header X-real-ip $remote_addr;
  7.            proxy_read_timeout 300;
  8.            proxy_connect_timeout 300;
  9.            proxy_redirect     off;
  10.            client_max_body_size 100M;
  11.            proxy_pass http://${backend}:9090;
  12.    }
大家可能擔心Eureka和Kubernetes service有衝突,Spring Cloud本身自帶服務發現Eureka,元件之間的呼叫透過Eureka註冊呼叫,其實你直接佈署就行了,Eureka和Service沒任何衝突,和普通Java應用一樣用。
監控方案

 

目前使用的Kubernetes官方的Heapster,Monitoring-InfluxDB-Grafana +自定議指令碼+自定義Grafana面板可以靈活報警。
監控面板按業務環境dev/test/stage/prod/對CPU/記憶體/網路等分類進行展示。
節點資源監控: 
Pod CPU、記憶體、網路等監控: 
監控指令碼,可以很靈活跟據設定引數進行釘釘報警,報告有問題的Pod、Node,自動處理有問題的服務。
  1. #!/bin/bash
  2. #最大記憶體排除的node節點
  3. exclude_node="node7|node1|node2|node3|master1"
  4. exclude_pod="redis|kafka|mongo|zookeeper|Evicted|Completed"
  5. #node使用的最大報警記憶體%比
  6. node_mem_max="100"
  7. #node最大使用cpu百分比
  8. node_cpu_max="80"
  9. #pod使用的最大報警記憶體MB
  10. pod_mem_max="4096"
  11. pod_top="5"
  12. pod_top_cpu="10"
  13. #pod的啟動錯誤時間,單位為秒s
  14. pod_error_m_time="120"
  15. pyding="$HOME/k8s-dev/dingd-zabbix.python"
  16. #pod的記憶體以及cpu的使用狀態
  17. pod_mem=$(/usr/local/bin/kubectl top pod --all-namespaces  |sort -n -k4 )
  18. #node的記憶體使用狀態
  19. node_status=$(/usr/local/bin/kubectl top node|egrep -v "${exclude_node}" |egrep -v "MEMORY%")
  20. #pod的執行狀態
  21. pod_status=$(/usr/local/bin/kubectl get pod --all-namespaces -o wide|grep -v NAMESPACE)
  22. #設定有問題的pod存取檔案路徑
  23. alert_error_pod="/tmp/alert-error-pod.txt"
  24. #設定最大記憶體佔用節點上pod的檔案串列路徑
  25. alert_list="/tmp/alert-mem-list.txt"
  26. #監控cpu百分比檔案輸出路徑
  27. alert_node_cpu_list="/tmp/alert_node_cpu_list.txt"
  28. #取node記憶體的百分比數字值
  29. #node_pre_mem=$(echo "${node_mem}"|awk '{print $5}'|sed -e "s/%//g")
  30. #監控node的記憶體百分比,列出佔用記憶體最高的應用並重啟top5應用
  31. node_mem_mon () {
  32.        echo "${node_status}" |awk '{print $1,$5}'|sed -e "s/%//g" |while read node_name node_mem_status;do
  33.        #echo $node_name $node_mem_status
  34.                if [ "${node_mem_status}" -gt "${node_mem_max}" ];then
  35.                         >${alert_list}
  36.                        #找到該節點上的所有的pod名
  37.                        find_pod=$(echo "${pod_status}"|egrep ${node_name}|awk '{print $2}')
  38.                        #找到所有節點倒排序使用最大的記憶體的pod串列
  39.                        for i in $(echo "${find_pod}");do
  40.                                echo "${pod_mem}"|grep $i  >>${alert_list}
  41.                        done
  42.                        date_time=`date +'%F-%T'`
  43.                        echo -e "\n${node_name}最大記憶體超過 %${node_mem_max} 以下pod應用將被重啟 ------------------\n"
  44.                        cat ${alert_list}|sort -n -k 4|tail -${pod_top}
  45.                        python ${pyding}  "`echo -e "\n ${date_time} ${node_name}當前記憶體為${node_mem_status}%,最大記憶體超過 %${node_mem_max} 以下pod應用將被重啟 ------------------\n" ;cat ${alert_list}|sort -n -k 4|egrep -v "$exclude_pod"|tail -${pod_top}` "
  46.                        cat ${alert_list}|sort -n -k 4|egrep -v "$exclude_pod"|tail -${pod_top}|egrep -v "應用將被重啟" | awk '{print  "/usr/local/bin/kubectl delete pod  "$2" -n "$1" " | "/bin/bash"}'
  47.                fi
  48.        done
  49. }

 

釘釘報警圖: 
Kubernetes叢集yaml容器編排管理

 

Kubernetes透過yaml對容器進行管理,yaml配置編排檔案是管理整個容器生命週期重要的一部份,管理好yaml非常重要。我開發了一套類似於Helm的模板的指令碼框架,用於所有環境的yaml初始化工作 ,自己寫指令碼的好處就是可以靈活控制,比如哪個元件要掛載儲存,共享捲,要配置私有hosts等,我可以一次性定製好,初始化時只需要init-yaml直接批次搞定,不需要每個yml單獨去修改,之後就是kubectl create 直接用。
容器編排yaml檔案按空間環境dev、test、stage、prod進行模板base分類,複製一套yaml模板即可生成其它各環境,容器編排按業務型別模組配置conf app-list。
  1. [root@master1 config]# ls
  2.  public-dev_app_list.conf      public-test-base.yml    
  3.  public-dev-base.yml           sms-test_app_list.conf  
  4.  public-pretest_app_list.conf  sms-test-base.yml        
  5.  public-pretest-base.yml       wbyh-dev_app_list.conf  
  6.  public-stage_app_list.conf    wbyh-dev-base.yml        
  7.  public-stage-base.yml         wbyh-stage_app_list.conf
  8.  public-test_app_list.conf     wbyh-stage-base.yml

 

透過Kubernetes核心排編指令碼進行init-yml初始化對應環境,生成所有Pod的yaml排編檔案,每套環境可以生成環境對應的MySQL、Redis、Kafka、MongoDB等,直接啟動即可呼叫。
  1. [root@master1 k8s-dev]# ./k8s wbyh-stage init-yml
  2. /root/k8s-dev/config
  3. [root@master1 k8s-dev]# tree
  4. wbyh-stage/
  5. ├── app
  6.   ├── dac-api-center
  7.     └── dac-api-center.yml
  8.   ├── dac-app-web
  9.     └── dac-app-web.yml
  10.   ├── dac-config-server
  11.     └── dac-config-server.yml
  12.   ├── dac-eureka-server
  13.     └── dac-eureka-server.yml
  14.   ├── dac-task
  15.     └── dac-task.yml
  16.   ├── dac-task-apply
  17.     └── dac-task-apply.yml
  18.   ├── dac-task-h5
  19.     └── dac-task-h5.yml
  20.   ├── dac-web
  21.     └── dac-web.yml
  22.   ├── dac-message-center
  23.     └── dac-message-center.yml
  24.   ├── dac-quartz-jfdata
  25.     └── dac-quartz-jfdata.yml
  26.   ├── dac-quartz-mach
  27.     └── dac-quartz-mach.yml
  28.   ├── dac-quartz-dac
  29.     └── dac-quartz-dac.yml
  30.   ├── dac-resources-center
  31.     └── dac-resources-center.yml
  32.   ├── dac-resources-item
  33.     └── dac-resources-item.yml
  34.   ├── dac-usercenter-web
  35.     └── dac-usercenter-web.yml
  36.   └── tomcat
  37.       └── tomcat.yml
  38. └── stateful-sets
  39.    ├── kafka
  40.       ├── 10kafka-config-0420yml
  41.       ├── 10kafka-config.yml
  42.       ├── 20dns.yml
  43.       └── 50kafka.yml
  44.    ├── mongo
  45.       └── mongo-statefulset.yml
  46.    ├── redis
  47.       ├── primary.yml
  48.       └── redis-configmap.yml
  49.    └── zookeeper
  50.        ├── 10zookeeper-config.yml
  51.        ├── 30service.yml
  52.        └── 50pzoo.yml
  53. 22 directories, 26 files

 

透過Kubernetes指令碼呼叫kubectl可以直接批次建立該空間下所有服務。
  1. [root@master1 k8s-dev]# ./k8s wbyh-stage create_all
  2. /root/k8s-dev/config
  3. configmap "dac-eureka-server-filebeat-config" created
  4. service "dac-eureka-server" created
  5. deployment.extensions "dac-eureka-server" created
  6. configmap "dac-config-server-filebeat-config" created
  7. service "dac-config-server" created
  8. deployment.extensions "dac-config-server" created
  9. configmap "tomcat-filebeat-config" created
  10. service "tomcat" created
  11. deployment.extensions "tomcat" created

 

所有程式碼存入GitLab做版本管理,即基礎設施即程式碼。
  1.    add svn-jar-version ll item
  2. commit 29dc05530d839c826130eef81541ce96a155107b
  3. Author: idea77 <idea77@qq.com>
  4. Date:   Thu Sep 20 16:11:00 2018 +0800
  5.    mod ossfs to /Rollback/oss
  6. commit 880bcd9483a6ee1f5ca440fef017b30ba7cd14fe
  7. Author: idea77 <idea77@qq.com>
  8. Date:   Wed Sep 19 16:57:43 2018 +0800

 

儲存方案

 

目前公司一部份應用掛載的捲為NFS,讀寫要求不高的可以配置NFS, 一部份要求比較高的用的Ceph,如MySQL、Kafka之類的就需要Ceph支撐,對於需要持久化的DB型別儲存的管理用StorageClass儲存類對接管理,很方便自動建立儲存捲PV-PVC對接,共享捲型別可以直接掛載捲。
NFS配置需要在每個Node節點安裝NFS-Utils,配置yml,註意CentOS 7低版本3.10內核的nfs-server有bug,導致伺服器重啟,升到4.0以上內核解決問題。
  1.      - name: tomcat-img
  2.        nfs:
  3.          path: /home/k8s-nfs-data/dac-test-tomcat-img
  4.          server: 192.168.8.30

 

Ceph Kubernetes Node節點安裝ceph-commo,配置StorageClass。
  1. ceph-class.yaml
  2. apiVersion: storage.k8s.io/v1beta1
  3. kind: StorageClass
  4. metadata:
  5.   name: ceph-db
  6. provisioner: kubernetes.io/rbd
  7. parameters:
  8.  monitors: 192.168.1.31:6789
  9.  adminId: admin
  10.  adminSecretName: ceph-secret
  11.  adminSecretNamespace: kube-system
  12.  pool: rbd
  13.  userId: admin
  14.  userSecretName: ceph-secret

 

Jenkins CI/CD編譯釋出階段

 

Jenkins CI/CD控制檯完成整個jar包編譯,Dockerfile編譯、docker push、Kubernetes deployment映象滾動升級功能。
Jenkins Manage and Assign Roles授權不同的開發、測試組不同的使用者許可權,隔離不同的專案編譯釋出許可權。 
目前沒有完全用上流水線服務,完全流水線需要構建不報錯,一報錯也就無法完成,不是很靈活,構建jar包和釋出docker-image是分開的,需要跟據公司業務來。
編譯階段我們做了釘釘通知,每個專案拉了自己的群,編譯jar包是否成功整個組都有通知,同樣update釋出是否成功都有提示,群內可見。 
目前我們Kubernetes容器啟動分為兩種架構:
  1. 容器釋出後啟動基礎JDK映象,Wget去http伺服器下載對應目錄編譯好的jar包,然後啟動,即無映象樣式,適合頻繁釋出型別的業務,push jar to oss有一部份業務是跑虛擬機器,需要jar包,oss可以做共享。

  2. 容器釋出按照標準的方式打image update-imae樣式,適合出錯及時回滾的業務,即編譯dockerfile-push-docke-image-update-deployment。

build-$namespace透過空間變數名擬寫對應指令碼,基本是做一個通用模板base,複製生成對應專案的build.sh供Jenkins傳參呼叫,每套環境有自己的基礎映象base,基礎映象就是打入JDK等一些私有的配置,編譯的時候在基礎映象上加上jar包。
  1. if [[ $MY_POD_NAMESPACE =~ -dev ]];then
  2.    #定義啟動基礎鏡相
  3.    base_image="registry-k8s.novalocal/public/yh-centos7-jdk-1.8"
  4.    #定義APP映象倉庫地址
  5.    image_path="registry-k8s.novalocal/xl_public/$MY_POD_NAMESPACE/${APP}"
  6. elif [[ $MY_POD_NAMESPACE =~ "-test" ]];then
  7.    #定義啟動基礎鏡相
  8.    base_image="registry-k8s.novalocal/public/yh-centos7-jdk-1.8"
  9.    #定義APP映象倉庫地址
  10.    image_path="registry-k8s.novalocal/xl_public/$MY_POD_NAMESPACE/${APP}:${date_time}"
  11. elif [[ $MY_POD_NAMESPACE =~ -stage ]];then
  12.    #定義啟動基礎鏡相
  13.    base_image="registry-k8s.novalocal/xl_public/wbyh-base/centos7-jdk-1.8"
  14.    #定義idc鏡相倉庫路徑
  15.    image_path="registry.cn-hangzhou-idc.com/xl_dac/wbyh-stage-${APP}:${date_time}"
  16.    vpc_image_path="registry-vpc.cn-hangzhou-idc.com/wbyh-stage-${APP}:${date_time}"
  17. fi
  18.       #初始化dockerfile
  19.        init_dockerfile () {
  20.                #生成Dockerfile
  21.                cd /Rollback/build-docker/
  22.                echo "" >$MY_POD_NAMESPACE/${APP}/Dockerfile
  23.                #生成基礎映象地址
  24.                echo -e "${base_image}"  >>${MY_POD_NAMESPACE}/${APP}/Dockerfile
  25.                #生成docker作者
  26.                echo -e "MAINTAINER idea77@qq.com" >>${MY_POD_NAMESPACE}/${APP}/Dockerfile
  27.                echo -e "USER root"  >>${MY_POD_NAMESPACE}/${APP}/Dockerfile
  28.                #獲取啟動指令碼
  29.                \cp -f  start-sh/${MY_POD_NAMESPACE}-sh/${APP}.sh  $MY_POD_NAMESPACE/${APP}/
  30.                echo -e "ADD ./${APP}.sh /home/deploy/" >>${MY_POD_NAMESPACE}/${APP}/Dockerfile
  31.                #新增 jar包到/home/deploy/
  32.                echo -e "${add_jar}" >>${MY_POD_NAMESPACE}/${APP}/Dockerfile
  33.                #暴露埠
  34.                echo -e "EXPOSE 9090"  >>${MY_POD_NAMESPACE}/${APP}/Dockerfile
  35.                #新增docker入口啟動檔案
  36.                \cp -f  start-sh/templates/docker-entrypoint.sh $MY_POD_NAMESPACE/${APP}/
  37.                echo -e "ADD ./docker-entrypoint.sh  /docker-entrypoint.sh" >>$MY_POD_NAMESPACE/$APP/Dockerfile
  38.                echo -e "RUN  chown -R deploy:deploy /home/deploy &&  chown -R deploy:deploy /docker-entrypoint.sh && ls -t --full /home/deploy " >>$MY_POD_NAMESPACE/$APP/Dockerfile
  39.                echo -e "USER deploy"  >>$MY_POD_NAMESPACE/$APP/Dockerfile
  40.                echo -e 'ENTRYPOINT ["/docker-entrypoint.sh"]' >>$MY_POD_NAMESPACE/$APP/Dockerfile
  41.                if [[ ${MY_POD_NAMESPACE} =~ -prod ]];then
  42.                        docker images |grep xl_prod|grep ${APP}|awk '{print $1":"$2}'|xargs docker rmi -f
  43.                else
  44.                        docker images |grep min-test|grep ${APP}|awk '{print $1":"$2}'|xargs docker rmi -f
  45.                fi
  46.                name="${MY_POD_NAMESPACE},build ${image_path}-${svn_version}"
  47.                cd /Rollback/build-docker/$MY_POD_NAMESPACE/$APP/
  48.                docker build  --no-cache -t ${image_path}-${svn_version} .
  49.                check
  50.                if [[ $MY_POD_NAMESPACE =~ -stage  ]];then
  51.                        #vpc專有鏡相地址修改到yml檔案
  52.                        sed -i "s@registry-vpc.cn-hangzhou-idc.com/xl_public\(.*\)@${vpc_image_path}-${svn_version}@g"    /home/deploy/k8s-dev/${MY_POD_NAMESPACE}/app/$APP/$APP.yml
  53.                elif [[ $MY_POD_NAMESPACE =~ -test  ]];then
  54.                        sed -i "s@registry-k8s.novalocal/xl_public/\(.*\)@${image_path}-${svn_version}@g"    /home/deploy/k8s-dev/${MY_POD_NAMESPACE}/app/$APP/$APP.yml
  55.                fi
  56.                name="push ${APP}"
  57.                docker push  ${image_path}-${svn_version}
  58.                check
  59. }

 

Jenkins觸發: 
build—–push——updae-deployment—–image,整個過程是流水線形式,一次性連續完成,完成後透過機器人通知到各業務組,中間有任何問題,機器人會告訴我們在哪個階段出錯,很方便排查問題,映象的版本號根據Git或SVN的版本號來獲取,然後加上當前時間戳,在jar包編譯階段版本號會寫入特定檔案,Jenkins會跟據當前編譯的版本生成對應的Docker映象版本。 
Kubernetes日誌方案

 

普通虛擬機器日誌分散,難管理,需要登陸虛擬機器一個個檢視,利用Kubernetes Pod多容器策略可以很方便幫我們收集管理日誌,日誌方案有幾種。
  1. 應用打到docker stdout前臺輸出,Docker輸出到/var/lib/containers,透過Filebeat、Fluentd、DaemonSet元件收集,這種對於小量日誌還可以,大量日誌效能很差,寫入很慢。

  2. Pod掛載host-path把日誌打到宿主機,宿主機啟動Filebeat、Fluentd、DaemonSet收集,無法判斷來自哪個容器,哪個Pod和namespace空間。

  3. Pod的yml中定義兩個container,同時啟動一個附加的Filebeat,兩個container掛載一個共享捲來收集日誌。

我們用第三種方案,透過一個附加容器Filebeat來收集所有日誌,filebeat–kakfa–logstash–es,自定義編譯Filebeat容器映象,為Filebeat打上podip空間service名等標簽,方便識別來自哪個容器,哪個namespace,配置config-map以及yaml。
  1. filebeat----kafkacluster-----logstash----es
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5.  namespace: dac-prod
  6.  name: dac-config-server-filebeat-config
  7. data:
  8.  filebeat.yml: |
  9.    filebeat.prospectors:
  10.    - input_type: log
  11.      fields:
  12.        namespace: dac-prod
  13.        service-name: dac-config-server
  14.        #pod-ip:
  15.      paths:
  16.        - "/mnt/*.log"
  17.      multiline:
  18.        pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}'
  19.        negate: true
  20.        match: after
  21.    #output.elasticsearch:
  22.    output.kafka:
  23.      hosts: ["10.31.222.108:9092", "10.31.222.109:9092", "10.31.222.110:9092"]
  24.      topic: applog
  25.      required_acks: 1
  26.      compression: gzip
  27.    # Available log levels are: critical, error, warning, info, debug
  28.    logging.level: info
  29. ---
  30. apiVersion: v1
  31. kind: Service
  32. metadata:
  33.  name: dac-config-server
  34.  namespace: dac-prod
  35. spec:
  36.  ports:
  37.  - port: 9090
  38.    name: http
  39.  selector:
  40.    app: dac-config-server
  41. ---
  42. apiVersion: apps/v1
  43. kind: Deployment
  44. metadata:
  45.  name: dac-config-server
  46.  namespace: dac-prod
  47.  labels:
  48.    app: dac-config-server
  49. spec:
  50.  replicas: 1
  51.  strategy:
  52.    rollingUpdate:
  53.      maxSurge: 1
  54.      maxUnavailable: 1
  55.    type: RollingUpdate
  56.  selector:
  57.    matchLabels:
  58.      app: dac-config-server
  59.  template:
  60.    metadata:
  61.      labels:
  62.        app: dac-config-server
  63.    spec:
  64.      affinity:
  65.        podAntiAffinity:
  66.          requiredDuringSchedulingIgnoredDuringExecution:
  67.          - labelSelector:
  68.              matchExpressions:
  69.              - key: app
  70.                operator: In
  71.                values:
  72.                - dac-config-server
  73.            topologyKey: "kubernetes.io/hostname"
  74.      imagePullSecrets:
  75.        - name: myregistrykey
  76.      containers:
  77.      - image: registry-vpc.cn-hangzhou-idc.com/dac-prod-dac-config-server:v1
  78.       name: dac-config-server
  79.        imagePullPolicy: Always
  80.        resources:
  81.          limits:
  82.            cpu: 4000m
  83.            memory: 4096Mi
  84.          requests:
  85.            cpu: 150m
  86.            memory: 1024Mi
  87.        env:
  88.        - name: APP
  89.          value: dac-config-server
  90.      #public
  91.        - name: JAVA_OPTS
  92.          value: "-Xms4g -Xmx4g"
  93.        - name: CONTAINER_CORE_LIMIT
  94.          value: "4"
  95.        - name: POD_IP
  96.          valueFrom:
  97.            fieldRef:
  98.              fieldPath: status.podIP
  99.        - name: MY_POD_NAMESPACE
  100.          valueFrom:
  101.            fieldRef:
  102.              fieldPath: metadata.namespace
  103.        readinessProbe:
  104.          tcpSocket:
  105.            port: 9090
  106.          initialDelaySeconds: 60
  107.          timeoutSeconds: 3
  108.        livenessProbe:
  109.          tcpSocket:
  110.            port: 9090
  111.          initialDelaySeconds: 60
  112.          timeoutSeconds: 3
  113.        ports:
  114.        - name: http
  115.          containerPort: 9090
  116.        volumeMounts:
  117.        #- name: opt-data
  118.          #mountPath: /home/deploy
  119.        - name: logs
  120.          mountPath: /home/deploy/logs
  121.        - name: host-time
  122.          mountPath: /etc/localtime
  123.          readOnly: true
  124.      - image: registry-vpc.cn-hangzhou-idc.com/dac_prod/filebeat:6.0.0
  125.        name: filebeat
  126.        imagePullPolicy: Always
  127.        env:
  128.        - name: POD_IP
  129.          valueFrom:
  130.            fieldRef:
  131.              fieldPath: status.podIP
  132.        volumeMounts:
  133.        - name: logs
  134.          mountPath: /mnt
  135.        - name: filebeat-conf
  136.          mountPath: /etc/filebeat
  137.        - name: host-time
  138.          mountPath: /etc/localtime
  139.          readOnly: true
  140.      nodeSelector:
  141.        node: public
  142.      volumes:
  143.      - name: logs
  144.        emptyDir: {}
  145.      - name: filebeat-conf
  146.        configMap:
  147.          name: dac-config-server-filebeat-config
  148.      #- name: opt-data
  149.        #nfs:
  150.          #path: /home/k8s-nfs-data/public-dev-base
  151.          #server: 10.10.1.30
  152.      - name: host-time
  153.        hostPath:
  154.          path: /etc/localtime

 

Filebeat收集日誌打上關鍵字標簽,namespace,svc,podip等。 
Kibana集中日誌展示,建立Dashboard分類,使用者可以按namespce分類不同環境,過濾選擇檢視不同模組的應用日誌。 
RBAC+二次開發Kubernetes指令碼

 

簡化kubectl 命令,提供給研發團隊使用。實際上這裡功能和Jenkins以及Kibana上是重覆的,但是必需考慮到所有團隊成員的使用感受,有人喜歡命令列,有人喜歡介面,簡單好用就夠。我打個比方,比如看日誌,有人可能喜歡用命令列tail -f看日誌,用grep過濾等,有人喜歡用Kibana看,那怎麼辦?於是就有了兩種方案,喜歡用圖形介面用Jenkins或Kibana,想用命令可以用命令操作,滿足你一切需求。統一集中透過指定的機器提供給開發、測試、運維、使用,方便除錯、排障。透過統一的入口可以直接對容器進行服務建立、擴容、重啟、登陸、檢視日誌、檢視Java啟動引數等,方便整個團隊溝通。
在這裡我們透過Kubernetes RBAC授權身份認證,生成不同的證書configkey,授於不同專案組不同的管理許可權,不同的專案組只有自己專案的許可權。許可權做了細分,不同研發、測試團隊互不幹擾。 
  1. [deploy@185 app]# k8s dac-test  get_all
  2. NAME                          READY     STATUS    RESTARTS   AGE       IP             NODE
  3. accountant-3536198527-dtrc9   2/2       Running   0          21h       172.20.1.5     node3.k8s.novalocal
  4. analyzer-1843296997-vz9nc     2/2       Running   0          21h       172.20.87.15   node5.k8s.novalocal
  5. api-1260757537-gxrp2          2/2       Running   0          21h       172.20.71.6    k8s-monitor.novalocal
  6. calculator-1151720239-pr69x   2/2       Running   0          21h       172.20.1.12    node3.k8s.novalocal
  7. consul-0                      1/1       Running   0          21h       172.20.87.3    node5.k8s.novalocal
  8. dispatcher-2608806384-kp433   2/2       Running   0          21h       172.20.4.6     lb1.k8s.novalocal
  9. geo-1318383076-c7th2          2/2       Running   0          5m        172.20.94.6    node6.k8s.novalocal
  10. greeter-79754259-s3bs2        2/2       Running   0          21h       172.20.19.5    jenkins-master.k8s.novalocal
  11. kafka-0                       1/1       Running   0          21h       172.20.1.4     node3.k8s.novalocal
  12. mqtt-0                        1/1       Running   0          21h       172.20.94.15   node6.k8s.novalocal
  13. mysql-0                       2/2       Running   0          21h       172.20.47.7    elk-k8sdata.novalocal
  14. pusher-2834145138-lfs21       2/2       Running   0          21h       172.20.19.6    jenkins-master.k8s.novalocal
  15. recovery-261893050-70s3w      2/2       Running   0          21h       172.20.32.13   node4.k8s.novalocal
  16. redis-0                       1/1       Running   0          21h       172.20.4.5     lb1.k8s.novalocal
  17. robot-1929938921-6lz6f        2/2       Running   0          21h       172.20.47.8    elk-k8sdata.novalocal
  18. scheduler-3437011440-rsnj6    2/2       Running   0          21h       172.20.5.10    db.k8s.novalocal
  19. valuation-2088176974-5kwbr    2/2       Running   0          21h       172.20.94.20   node6.k8s.novalocal
  20. zookeeper-0                   1/1       Running   0          21h       172.20.4.4     lb1.k8s.novalocal
  1. 註意,如何操作使用者自己有許可權的空間,必需填寫default-namespace.conf
  2. 註意,當gitlab master分支有合併的時候,目前我們ci自動會構建編譯最新的jar版本,推送至nexus倉庫,k8s容器裡的jar包可以指定更新
  3. k8s  init-yml        #初始化生成使用者自己本人的yml檔案
  4. k8s  get_all         #檢視使用者自己本人空間下的所有執行的容器
  5. k8s  create_all      #建立使用者自己本人所有服務
  6. k8s  delall_app      #刪除本人空間下所有app服務,除基礎服務mysql、 consul、 kafka、 redis、 zookeeper、mqtt 以外的所有服務
  7. k8s  apply  api      #修改了使用者自己本人yml配置檔案,應用配置生效
  8. k8s  create api      #使用者自己本人空間下建立一個api服務
  9. k8s  delete api      #使用者自己本人空間下刪除一個api服務
  10. k8s  scale  api 2    #使用者自己本人空間下把api服務擴容成2個pod
  11. k8s  login  api      #使用者本人空間下登入api所在的docker容器
  12. k8s  logs   api      #使用者自己本人空間用tail -f 命令的方式檢視容器內/home/deploy/api/logs/api.log 的日誌
  13. k8s  error-logs api  #使用者自己本人空間用tail -f 命令的方式檢視容器內/home/deploy/api/logs/api.error.log 的日誌
  14. k8s  clean api       #如果編譯出錯,在使用者自己本人空間用gradlew clean清理命令的方式清理編譯
  15. k8s  push_jar        #更新本人空間下所有容器的jar包版本,重啟所有容器,預設拉取backend / push-envelope -git最終版本,該版本為合併編譯成功後的最新版本號
  16. k8s  push_jar  20170927-1731   #選擇指定的jar版本號20170927-1731 進行更新 ,重啟所有容器
  17. k8s  reinit-mysql    #重新更新所有容器jar版本後api無法啟動,清空使用者空間下的資料庫,重新建立匯入資料
  18. 批次操作
  19. k8s  scale  api-geo 2 #在dev使用者下把api和geo 擴容
  20. k8s  delete api-geo   #在dev使用者下刪除api 和geo服務
  21. k8s  create api-geo   #在dev使用者下建立api和geo服務
  22. 所有人員通用命令,要操作某個使用者的資源,必需先生成所需要的yml檔案
  23. 但是必需指定第二個引數名dev test stage等。
  24. k8s stage init-yml       #初始化生成stage使用者的yml檔案 註意要操作stage使用者的容器要先成配置檔案
  25. k8s test init-yml        #初始化生成test空間的yml檔案
  26. k8s dev init-yml         #初始化生成dev空間的yml檔案
  27. k8s dev  get_all         #檢視dev使用者空間下的所有執行的容器
  28. k8s dev  create_all      #建立dev空間下所有服務
  29. k8s dev  delall_app       #刪除dev空間下的app服務,除基礎服務mysql、 consul、 kafka、 redis、 zookeeper、mqtt 以外的所有服務
  30. k8s dev  apply  api      #修改了yml配置檔案,應用配置生效
  31. k8s dev  create api      #dev空間下建立一個api服務
  32. k8s dev  delete api      #dev空間下刪除一個api服務
  33. k8s dev  scale  api 2    #dev空間下把api服務擴容成2個pod
  34. k8s dev  login  api      #dev空間下登入api所在的docker容器
  35. k8s dev  logs   api      #dev空間用tail -f 命令的方式檢視容器內/home/deploy/api/logs/api.log 的日誌
  36. k8s dev  error-logs api  #dev空間用tail -f 命令的方式檢視容器內/home/deploy/api/logs/api.error.log 的日誌
  37. k8s dev  push_jar        #更新dev空間下所有容器的jar包版本,重啟所有容器,預設拉取backend /-git最終版本,該版本為合併編譯成功後的最新版本號
  38. k8s dev  push_jar  20170927-1731   #選擇指定的jar版本號20170927-1731 進行更新 ,重啟所有容器
  39. k8s dev  clean api       #如果編譯出錯,dev使用者空間用gradlew clean清理命令的方式清理編譯
  40. k8s dev  reinit-mysql    #重新更新所有容器jar版本後api無法啟動,清空dev空間下的資料庫,重新建立匯入資料
  41. 批次操作
  42. k8s dev  scale api-geo 2  #在dev空間把api和geo 擴容
  43. k8s dev  delete api-geo  #在dev空間刪除api 和geo服務
  44. k8s dev  create api-geo  #在dev空間下建立api和geo服務
  45. 管理員專用命令,註意管理員第二個引數一定要填
  46. k8s dev  create_rsync    #建立dev空間的rsync配置
  47. k8s dev  create_passwd   #建立dev空間的解壓密碼下發金鑰
  48. k8s dev  create   rbac   #建立dev空間的叢集授權認證
  49. k8s dev  delete   rbac   #刪除dev空間的叢集授權認證
  50. k8s dev  delete_all      #刪除dev空間下所有服務
Kubernetes叢集規劃和問題總結

 

1、叢集資源規劃request +limit+maxpods+eviction引數,需要計算好再配置,配置有問題可能導致資源利用不均衡,一部節點資源利用過高,一部節點資源利用過低。
2、Kubernetes Node節點一定要留有足夠的磁碟空間,跟據Pod個數和image大小決定磁碟空間數。
3、JDK無法獲取正確的CPU數,預設獲取的是宿主機CPU,會致建立的執行緒數過多,系統崩潰,可以透過:https://github.com/obmarg/libsysconfcpus.git 解決。
  1. if [ "x$CONTAINER_CORE_LIMIT" != "x" ]; then
  2.  LIBSYSCONFCPUS="$CONTAINER_CORE_LIMIT"
  3.  if [ ${LIBSYSCONFCPUS} -lt 2 ]; then
  4.     LIBSYSCONFCPUS=2
  5.  fi
  6.  export LIBSYSCONFCPUS
  7. fi
  8. export LD_PRELOAD="/usr/local/lib/libsysconfcpus.so:$LD_PRELOAD"

 

4、nfs-server一定要用async,充份利用快取加快寫入速度,註意核心版本bug。
5、應用產生的日誌必需要設定輪轉數和大小,防止過大日誌撐暴宿主機磁碟。
6、釋出版本越多,隨著下載映象版本越來越多,磁碟會撐爆,合理配置kubelet image gc引數,配置gc回收最佳化磁碟空間。
7、Docker CE以前的版本經常會出現Docker失控,使用過程中整個節點容器無法刪除,無法建立,只能重啟,對業務影響很大,建議全部更新到18-CE版本,和Kubernetes容性更好。
8、節點的親和性和反親和Affinity一定要提前規劃好,為了達到高可用目的,多副本必需配置。
9、應用異常檢測,跟據實際情況配置探針ReadinessProbe、LivenessProbe防止應用假死,Kubernetes提前剔除有問題的Pod容器。
Q&A;

 

Q:使用NFS有存在效能瓶頸或單點故障的問題嗎,如何解決,對於持久化要求高的Redis應該採用哪種儲存?
A:具體看你的規模數量,測試、開發環境,單節點NFS毫無壓力,資料是先寫到快取記憶體,速度很快,我文章中的說的核心註意bug,沒必要做高可用,公有雲有NAS服務,不必擔心,自建機房可以用drbd Keepalived vip。
Q:為什麼網路沒有使用Traefik,Spring Cloud的相關元件是怎麼部署的,是用yaml檔案還是使用Helm方式?
A:考慮到Traefik效能沒有nginx好,所以用nginx,ymal是自己寫的模板生成的,沒有用Helm。我們正在調研,Eureka可以單獨定製多個yml互相註冊。與外部服務透過打通網路直通,透過SVC對接。
Q:請問下所有環境都在一個叢集,壓測怎麼辦?
A:壓測只是對應用產生壓力,你可以把需要壓測的應用排程到不同的節點NodeSelecto隔離執行。
Q:對於區域網微信回呼是如何做,沒有公網IP?
A:打通網路之後,設定WIFI指向DNS為Kubernetes DNS,Service直接互通。
Q:Eureka註冊時服務IP用的什麼?
A:Kubernetes叢集內會用的podip去註冊。
Q:有狀態應用的場景,使用容器部署與傳統部署有啥區別,容器部署是否存在一些坑?
A:有狀態容器建立後,儘量少動,少遷移,遇到過卡住,容器無法遷移或刪除,重要的MySQL之類的建議放外部執行。

 

Kubernetes應用實戰培訓

Kubernetes應用實戰培訓將於2018年11月9日在北京開課,3天時間帶你係統學習Kubernetes本次培訓包括:容器特性、映象、網路;Docker特性、架構、元件、概念、Runtime;Docker安全;Docker實踐;Kubernetes架構、核心元件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用物件、設計原則;Kubernetes的實踐、執行時、網路、外掛已經落地經驗;微服務架構、DevOps等,點選下方圖片檢視詳情。

    贊(0)

    分享創造快樂