服務部署之k8s


 
微服務個人覺得是個非常復雜又龐大的體系,比如要有完備的監控平台、分布式日志收集系統、權限控制、服務治理,各服務應該高度自治、服務注冊於發現、節點動態擴縮容、熔斷降級,限流等等。標准的微服務架構涉及到這么多的技術點,如果沒有巨人的肩膀依靠,我相信一般的公司都很難實施,好在google這個巨人幫我們實現了,它就是k8s。官網介紹,k8s源自Google15年生產環境的運維經驗提供的最佳實踐,可見一斑。概念性的東西就不介紹了(主要是我懂的也不多,太龐大),優勢就是上面說到的標准微服務架構涉及到的技術點,這哥們本身就基於標准微服務架構實現的。我這里簡單說下它和docker的關系。k8s容器集群管理系統,當然docker社區也提供類似的集群管理系統,docker三劍客之一docker-swarm,這個哥們設計有缺陷(大起大落上不了規模),所以后面就不了了之了。說到三劍客我們還接觸到一個容器編排docker-compose,三劍客現在還在用的應該就只有它了,集成測試環境的編排系統,它只能單機,同一個docker-compose里面的容器在一個docker網絡里面(當然可以通過其他手段實現),它跟docker的關系,個人覺得就是套娃的關系。閑聊就到這。下面看下整個服務部署的實驗環境。
 
通信主機所有通信主機都是基於vm虛擬機實現,看圖
簡單解說一下:
1.所有虛擬主機基於ubuntu-server18.4這個版本,好像最新版是20.4;
2.截圖里面有4台虛擬主機,其中org是對安裝k8s准備的公共系統環境,比如配置網絡、安裝k8s工具、docker等;
3.master和node節點都是基於org完整克隆出來的,這樣可以減少一定的工作量吧;
4.搭建k8s集群最少需要10個g的內存,這里還不包括master高可用,master2g,node4g,這是常規練手部署,也可以不按套路出牌,master和slave交叉部署,也是可以的;
下面我們通過命令看看node信息。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get node
2 NAME                STATUS   ROLES    AGE     VERSION
3 kubernetes-master   Ready    master   3d15h   v1.18.2
4 kubernetes-node01   Ready    <none>   3d15h   v1.18.2
5 kubernetes-node02   Ready    <none>   3d15h   v1.18.2
6 root@kubernetes-master:/usr/local/k8s-test01/product#
 
服務部署結構圖:
簡單介紹下,
1.在最前端的是網關也是代理(ingress-nginx-controller),它是k8s的網關入口實現,除了ingress-nginx還有很多,可以參考 https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/ ,它的高可用可通過HA或者keepalived實現,在這個實驗里面我沒有去實現,其實配置起來很簡單,通過shell腳本檢查nginx是否可用,對外提供vip地址,基於vrrp路由冗余協議實現,當然這個網關也是在k8s這個集群里面的,圖沒有顯示層級出來。
2.網關后面就是我們的service服務,我這里部署的是產品服務ProductService,整個service的網絡模型我部署成了ClusterIp類型,這是k8s對內訪問的三種模型之一。這個模型有個特點外面的服務不能直接訪問里面pod之上的容器,所以我們可以把它理解為一個封閉的網絡,由ingress-nginx代理,而它們的網絡是在k8s這個大型子網里面,通過coredns(k8s里面的組件)組件解析(當然這里面還有CNI的實現者網橋的介入,甚至還有docker網橋)。下面我們直接部署吧。
 
部署
我們着重看ProductService的部署吧,其他的k8s集群環境准備就不贅述了,分兩種,adm鏡像和二進制,adm比較簡單。我簡單說下,部署productservice我的一個思路,先是通過工程dockerfile構建鏡像,當然這些可以通過自動構建和部署,然后通過deployment資源管理器創建pod資源,最后部署service代理。這里簡單畫個圖理解一下。
這里簡單介紹下這里面涉及到的幾個資源:
1.在k8s集群里面master主要負責控制和管理node節點,node為工作負載節點,干活就是它,也就是容器的執行是在它里面的pod之上。
2.deployment是資源控制管理器,比如動態擴縮容,滾動更新,回滾等等操作,在k8s里面有多種資源控制器,比如rs、rc、ds、job等,rc已經被rs替代,然而deployment的功能又包含rs,所以我這里用dep。
3.service服務代理,代理誰?pod,通過label標簽匹配,為什么需要它?如果沒有它的話,pod暴露的是一個個ip,如果中間一個pod掛了,dep為了滿足我們的期望值,會重新創建一個pod,這時候出問題了,剛好service就是為了解決這個問題而誕生的,它通過標簽匹配到集群里面對應的標簽pod,監聽它們的狀態,然后把pod信息同步到service里面,提供外部服務。service代理模式分三種iptables、ipvs等。
圖片解說到這,看代碼。
1.構建鏡像 productservice

 

 1 FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
 2 WORKDIR /app
 3 EXPOSE 80
 4 EXPOSE 443
 5 
 6 FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
 7 WORKDIR /src
 8 
 9 COPY "EventBus/EventBus/EventBus.csproj" "EventBus/EventBus/EventBus.csproj"
10 COPY "EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
11 COPY "EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj"
12 COPY "Services/ProductService/Api/Product.Api.csproj" "Services/ProductService/Api/Product.Api.csproj"
13 COPY "Services/ProductService/Application/Product.Application.csproj" "Services/ProductService/Application/Product.Application.csproj"
14 COPY "Services/ProductService/Domain/Product.Domain.csproj" "Services/ProductService/Domain/Product.Domain.csproj"
15 COPY "Services/ProductService/Infrastructure/Product.Infrastructure.csproj" "Services/ProductService/Infrastructure/Product.Infrastructure.csproj"
16 
17 COPY "NuGet.config" "NuGet.config"
18 WORKDIR /src/Services/ProductService/Api
19 RUN dotnet restore "Product.Api.csproj"
20 WORKDIR /src
21 COPY . .
22 WORKDIR /src/Services/ProductService/Api
23 RUN dotnet publish --no-restore -c Release -o /app
24 
25 FROM build AS publish
26 
27 FROM base AS final
28 WORKDIR /app
29 COPY --from=publish /app .
30 COPY --from=build /src/Services/ProductService/Proto /app/Proto
31 
32 ENTRYPOINT ["dotnet", "Product.Api.dll"]

 

先通過dockerfire構建一份鏡像,記得打標簽。我這里把編譯也做了,當然你也可以先在vs里面發布好,然后再打包鏡像,會快一些,docker鏡像下載很蛋疼,一般有三種處理方式吧。1.配置加速地址 2.搭建鏡像私服 3.docker save和load命令。
2.通過deployment創建pod資源,今天這篇文章我不打算用helm包管理器來做,這樣里面的細節會多一些,后續下次k8s部署會全部基於helm來做,看代碼
 1 apiVersion: apps/v1
 2 kind: Deployment # 資源類型
 3 metadata:
 4   name: product-server # 名稱
 5 spec:
 6   replicas: 2 # pod實例數
 7   selector:
 8     matchLabels:
 9       app: product-server 標簽
10   template: # 模板
11     metadata:
12       labels:
13         app: product-server 標簽 上下兩個標簽需要對應,表示template里面創建的這個pod屬於這個dep資源里面的
14     spec: #詳細信息
15       containers:  # 容器
16       - name: product-server
17         image: myproduct-test-01:2.0 # image鏡像
18         imagePullPolicy: IfNotPresent # image拉取策略,如果本地有就不拉取,這里注意啊,這個本地不光是master節點,node節點也都需要有這個鏡像
19         ports:
20         - containerPort: 80 # 容器端口
21           name: site
22           protocol: TCP
23         - containerPort: 81
24           name: grpc
25           protocol: TCP
26  
27         env: # 環境變量,也可以通過configmap實現,其實就是配置文件
28         - name: ASPNETCORE_ENVIRONMENT
29           value: "Development"
30         - name: ConnectionString
31           value: "Server=mssql;Database=product_db;User Id=sa;Password=Pass@word"

 

執行命令 kubectl create -f 文件名稱.yaml,接着我們通過查看命令,看看deployment信息, 在實際操作的時候,可能會因為pull鏡像導致狀態有問題,我一般是先把鏡像處理好,再在本地拉取鏡像。這里需要注意一點小知識點,pod的生命周期和探針,探針分兩種,就緒和存活探測兩種,這里我為什么要提這個呢?因為在微服務里面,個人覺得健康檢查很重要。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get deployment
2 NAME             READY   UP-TO-DATE   AVAILABLE   AGE
3 mssql            1/1     1            1           27h
4 product-server   2/2     2            2           6h13m
5 rabmq            2/2     2            2           13h
6 root@kubernetes-master:/usr/local/k8s-test01/product#

 

3.部署service代理
 1 apiVersion: v1
 2 kind: Service # 資源類型
 3 metadata:
 4   name: product-server # 名稱,這個名稱類似dockercompose里面定義的服務名
 5 spec:
 6   ports:
 7   - name: psvc
 8     port: 80 # 服務端口,提供給集群內部訪問的端口,外部訪問不了
 9     targetPort: 80 #  容器端口
10   - name: grpc
11     port: 81
12     targetPort: 81
13   selector:
14     app: product-server  # 標簽,與之對應的是deployment里面的pod標簽,它們是多對多的關系
15   type: ClusterIP # 內部網絡

 

執行命令 kubectl create -f 文件名稱.yaml,接下來我們可以查看service信息。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get service
2 NAME             TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                          AGE
3 kubernetes       ClusterIP      10.96.0.1       <none>        443/TCP                          3d15h
4 mssql            LoadBalancer   10.104.37.195   <pending>     1433:31227/TCP                   26h
5 product-server   ClusterIP      10.107.6.57     <none>        80/TCP,81/TCP                    6h10m
6 rabmq            NodePort       10.102.48.159   <none>        15672:31567/TCP,5672:30567/TCP   13h
7 root@kubernetes-master:/usr/local/k8s-test01/product#
 
deployment和service配置文件可以放在一個yaml文件里面,通過---分開就行,這里分開是為了看起來有層次。
好了,productservice部署完畢了,我們的服務是部署完畢了,但是我們在外部訪問不了,需要入口網關,這里我們先使用ingress-nginx-controller。
部署ingress
1.安裝ingress-nginx,這里我用的是最新版0.30.0,很是郁悶,下載地址被牆了,最后在github開源代碼里面找到安裝資源,其實就是一份yaml文件。
 1 # 其他...
 2 apiVersion: apps/v1
 3 kind: Deployment
 4 metadata:
 5 name: nginx-ingress-controller
 6 namespace: ingress-nginx
 7 labels:
 8 app.kubernetes.io/name: ingress-nginx
 9 app.kubernetes.io/part-of: ingress-nginx
10 spec:
11 replicas: 1
12 selector:
13 matchLabels:
14 app.kubernetes.io/name: ingress-nginx
15 app.kubernetes.io/part-of: ingress-nginx
16 template:
17 metadata:
18 labels:
19 app.kubernetes.io/name: ingress-nginx
20 app.kubernetes.io/part-of: ingress-nginx
21 annotations:
22 prometheus.io/port: "10254"
23 prometheus.io/scrape: "true"
24 spec:
25 # wait up to five minutes for the drain of connections
26 terminationGracePeriodSeconds: 300
27 serviceAccountName: nginx-ingress-serviceaccount
28 nodeSelector:
29 kubernetes.io/os: linux
30 containers:
31 - name: nginx-ingress-controller
32 image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
33 args:
34 - /nginx-ingress-controller
35 - --configmap=$(POD_NAMESPACE)/nginx-configuration
36 - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
37 - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
38 - --publish-service=$(POD_NAMESPACE)/ingress-nginx
39 - --annotations-prefix=nginx.ingress.kubernetes.io
40 # ......

 

安裝ingress-nginx ,執行命令 kubectl create -f ingress-nginx.yaml,隨后我們通過命令查看。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get pods -n ingress-nginx
2 NAME                                        READY   STATUS    RESTARTS   AGE
3 nginx-ingress-controller-77db54fc46-tx6pf   1/1     Running   5          40h
4 root@kubernetes-master:/usr/local/k8s-test01/product#

 

接下來配置並發布ingress-nginx網關服務。
 1 apiVersion: networking.k8s.io/v1beta1
 2 kind: Ingress
 3 metadata:
 4   name: nginx-web
 5   annotations: # 擴展信息,這里可以配置鑒權、認證信息
 6     nginx.ingress.kubernetes.io/rewrite-target: /
 7 spec:
 8   # 路由規則
 9   rules:
10   # 主機名,只能是域名,需要在宿主機配置hosts映射
11   - host: product.com
12     http:
13       paths:
14       - path: /
15         backend:
16           # 后台部署的 Service Name,與上面部署的service對應
17           serviceName: product-server
18           # 后台部署的 Service Port,與上面部署的service對應
19           servicePort: 80
20       - path: /grpc
21         backend:
22           # 后台部署的 Service Name,與上面部署的service對應
23           serviceName: product-server
24           # 后台部署的 Service Port,與上面部署的service對應
25           servicePort: 81
 
執行kubectl create -f productservice-ingress.yaml部署。接下來我們查看網關服務信息。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get ingress
2 NAME        CLASS    HOSTS         ADDRESS   PORTS   AGE
3 nginx-web   <none>   product.com             80      8h
 
mssql和rabbitmq的部署這里就貼代碼了,接下來我們訪問product.com這個域名,看看是否部署成功。
 
 
我們測試一個get接口試試,看看能不能通,看圖。
 
服務部署就到這,接下來我們簡單總結一下
1.這是測試環境所以master沒做高可用,ingress也沒做高可用,有時間再做順便補充一下;
2.外部網絡請求到ingress-nginx域名,線上環境這個域名肯定是公網地址,ingress做認證鑒權,合法的請求通過path路由到對應的后台service,如果其中一台ingress掛掉了,keepalived會把vip游離到其他slave節點,這樣就完成了ingress的高可用;
3.service代理會把請求隨機轉發到標簽匹配的pod里面的容器處理,如果其中一台node掛了或者pod異常退出了(也就是返回值不等於0),deployment會重新啟動一個pod,我們下面做個實驗,刪掉其中一個pod,看看效果怎么樣。
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get pods
2 NAME                              READY   STATUS    RESTARTS   AGE
3 mssql-59bd4dc6df-xzxc2            1/1     Running   5          27h
4 product-server-599cfd85cc-2q7zx   1/1     Running   0          6s
5 product-server-599cfd85cc-ppmhx   1/1     Running   0          6h51m
6 rabmq-7c9748f876-9msjg            1/1     Running   0          14h
7 rabmq-7c9748f876-lggh6            1/1     Running   0          14h
 
我們先通過命令顯示所有default命名空間下面的所有pod,然后delete一個pod看看,它會不會重新啟動。執行刪除命令
1 kubectl delete pods product-server-599cfd85cc-ppmhx

 

接着馬上查看pods信息,要快知道嗎?
1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl get pods
2 NAME                              READY   STATUS        RESTARTS   AGE
3 mssql-59bd4dc6df-xzxc2            1/1     Running       5          27h
4 product-server-599cfd85cc-2q7zx   1/1     Running       0          2m8s
5 product-server-599cfd85cc-9s497   1/1     Running       0          13s
6 product-server-599cfd85cc-ppmhx   0/1     Terminating   0          6h53m
7 rabmq-7c9748f876-9msjg            1/1     Running       0          14h
8 rabmq-7c9748f876-lggh6            1/1     Running       0          14h
 
看到沒?ppmhx這個pod正在終止,馬上就創建了新的pod。牛逼吧,強大吧。這只是它的冰山一角,我們還可以通過HPA實現動態擴縮容,比如cpu達到多少負載,水平擴展pod,或者刪除pod。
 
最后提一下,碰到坑如何處理?
我一般是先通過命令查看大概信息
 1 root@kubernetes-master:/usr/local/k8s-test01/product# kubectl describe pods product-server-599cfd85cc-2q7zx
 2 Name:         product-server-599cfd85cc-2q7zx
 3 Namespace:    default
 4 Priority:     0
 5 Node:         kubernetes-node01/192.168.44.210
 6 Start Time:   Fri, 08 May 2020 00:31:31 +0800
 7 Labels:       app=product-server
 8               pod-template-hash=599cfd85cc
 9 Annotations:  cni.projectcalico.org/podIP: 10.244.185.145/32
10               cni.projectcalico.org/podIPs: 10.244.185.145/32
11 Status:       Running
12 IP:           10.244.185.145
13 IPs:
14   IP:           10.244.185.145
15 Controlled By:  ReplicaSet/product-server-599cfd85cc
16 Containers:
17   product-server:
18     Container ID:   docker://bfc6ab23a5e228b85960322d3ea57321ed4c51cd4ad413a3db1a8452e4f52bea
19     Image:          myproduct-test-01:2.0
20     Image ID:       docker://sha256:eab1c9f80b5f64adb368f1e3d3f2955597f90c0badd2ba81fc714a6774a0e473
21     Ports:          80/TCP, 81/TCP
22     Host Ports:     0/TCP, 0/TCP
23     State:          Running
24       Started:      Fri, 08 May 2020 00:31:33 +0800
25     Ready:          True
26     Restart Count:  0
27 其他 ....

 

這里面描述了很多信息,如果遇到錯誤,也會有大概的信息提示,如果通過這里的信息提示不能排錯,我一般就會去看容器的日志信息,通過命令kubectl logs pod名稱,這個就不演示。
最后簡單總結一下吧,關於k8s站在我們應用的角度來說,不是太難,但是非運維人員要把它玩好,我覺得有點勉強,不是我們接受能力差,還是我們開發人員沒時間玩它。太龐大,知識面太多,比如你要完k8s你先的熟悉一種容器化技術吧,網絡,等等,還有坑太多,還可能要FQ啥的,最讓我吐槽的是各種鏡像下載超時,有時候被牆了,還要各種想辦法拿資源,這里哪位朋友方便介紹給我一個FQ軟件,最好是收費的。謝謝,就這樣把。下次把監控、ELK、PVC等補上。
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM