<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          小白零基礎(chǔ)--gRPC整合Kubernetes

          共 15388字,需瀏覽 31分鐘

           ·

          2021-05-18 22:13

          上一篇,我們簡單介紹了下mac下單節(jié)點Kubernetes的安裝,今天我們乘熱打鐵,感受下grpc整合Kubernetes的魅力。好了Talk is cheap,Show me the graph 我們要做的是下面這么一個架構(gòu)的小demo。

          grpc-k8s

          后續(xù)計劃寫個gRPC的專題,我們先來簡單認(rèn)識下gRPC的基本玩法。

          gRPC

          在微服務(wù)盛行的今天,如果你不會個RPC框架你都不好意思出門跟人打招呼。業(yè)界流行的目前主要有dubbo、motan、rpcx、gRPC、thrift,排名不分先后,各有千秋。今天主要帶來的是go中g(shù)RPC的使用。

          正如其官方介紹的那樣,gRPC是一個高性能、通用的開源RPC框架,由Google開發(fā)主要面向移動應(yīng)用開發(fā)并基于HTTP/2協(xié)議標(biāo)準(zhǔn)而設(shè)計,基于Protocol Buffers序列化協(xié)議開發(fā),且支持眾多開發(fā)語言。至于gRPC優(yōu)勢不再贅述,合適才是最好的。

          零基礎(chǔ)在Go中使用gRPC大致分為四個步驟:

          • 安裝golang以及Protocol Buffer compiler,go module擼一個工程引用下gRPC庫
          • 創(chuàng)建.proto文件,并定義一個service
          • 使用protocol buffer compiler生成服務(wù)端和客戶端代碼
          • 使用Go gRPC API為你的服務(wù)寫一個簡單的客戶端和服務(wù)端

          安裝軟件和擼一個工程

          先決條件

          1. golang 安裝 關(guān)于這部分網(wǎng)上大把文章Google之
          2. Protocol Buffer compiler,protoc 關(guān)于protocol buffers的玩法可以參考往期文章。

          選擇適合你平臺的預(yù)編譯好的二進(jìn)制文件(https://github.com/google/protobuf/releases),解壓并將可執(zhí)行文件protoc放到你的環(huán)境變了中

          1. 使用以下命令為Go安裝protobuf協(xié)議編譯器插件:
          $ export GO111MODULE=on  # Enable module mode
          $ go get google.golang.org/protobuf/cmd/protoc-gen-go \
                   google.golang.org/grpc/cmd/protoc-gen-go-grpc
          1. 更新你的PATH,以便protoc可以找到插件:
          $ export PATH="$PATH:$(go env GOPATH)/bin"
          1. Go module擼一個工程
          cd ${your workspace}
          mkdir -p grpc-k8s-demo && cd grpc-k8s-demo
          go mod init github.com/xxx/grpc-k8s-demo
          #引入gRPC庫
          go get -u google.golang.org/grpc

          定義服務(wù)

          在gRPC中,我們是使用protocol buffers定義gRPC服務(wù)以及方法請求和響應(yīng)類型。首先要定義服務(wù),需要在.proto文件中指定一個命名service

          service Greeter {
          ...
          }

          然后,你可以在服務(wù)定義中定義rpc方法,并指定它們的請求和響應(yīng)類型。gRPC允許您定義四種服務(wù)方法,所有這些方法都在 Greeter服務(wù)中使用:

          • 一個簡單的RPC,客戶端使用存根將請求發(fā)送到服務(wù)器,然后等待響應(yīng)返回,就像正常的函數(shù)調(diào)用一樣。
          rpc SayHello (HelloRequest) returns (HelloReply) {}
          • 服務(wù)器端流式RPC,客戶端向服務(wù)器發(fā)送請求,并獲取流來讀取后續(xù)的一系列消息。客戶端從返回的流中讀取數(shù)據(jù),直到?jīng)]有更多消息為止。如下你可以通過在響應(yīng)類型之前放置stream關(guān)鍵字來指定服務(wù)器端流方法。
          rpc SayHello (HelloRequest) returns (stream HelloReply) {}
          • 客戶端流式RPC,客戶端編寫消息序列,然后使用提供的流將消息發(fā)送到服務(wù)器。一旦客戶端寫完消息后,它將等待服務(wù)器讀取所有消息并返回其響應(yīng)。你可以通過將stream關(guān)鍵字放在請求類型之前指定客戶端流方法。
          rpc SayHello (stream HelloRequest) returns (HelloReply) {}
          • 雙向流式RPC,雙方都使用讀寫流發(fā)送一系列消息。這兩個流是獨立運行的,因此客戶端和服務(wù)器可以按照自己喜歡的順序進(jìn)行讀寫:例如,服務(wù)器可以在寫響應(yīng)之前等待接收所有客戶端消息,或者可以先讀取消息再寫入消息,或讀寫的其他組合。每個流中的消息順序都會保留。你可以通過在請求和響應(yīng)之前都放置stream關(guān)鍵字來指定這種類型的方法。
          rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}

          .proto文件還包含用于服務(wù)方法中所有請求和響應(yīng)類型的protobuf協(xié)議消息類型定義-例如,

          // The request message containing the user's name.
          message HelloRequest {
          string name = 1;
          }

          // The response message containing the greetings
          message HelloReply {
          string message = 1;
          map<string, HelloRequest> maps = 2;
          }

          本文為了簡單演示,使用第一種簡單RPC的方式。

          生成客戶端和服務(wù)器代碼

          接下來,我們需要根據(jù).proto服務(wù)定義生成gRPC客戶端和服務(wù)器接口。可以使用帶有特殊gRPC Go插件的protocol buffer compiler protoc 進(jìn)行此操作。運行以下命令:(關(guān)于protoc 原理可見往期《搞定protocol buffers 下-原理篇》)

          $ protoc --go_out=. --go_opt=paths=source_relative \
              --go-grpc_out=. --go-grpc_opt=paths=source_relative \
              pb/hello.proto

          運行此命令將在pb目錄中生成以下文件:

          • hello.pb.go,其中包含用于填充,序列化和檢索請求和響應(yīng)消息類型的所有protocol buffers代碼。
          • hello_grpc.pb.go,其中包含以下內(nèi)容:
            • 客戶端使用Greeter服務(wù)中定義的方法調(diào)用的接口類型(或存根)。
            • 服務(wù)器要實現(xiàn)的接口類型,也具有Greeter服務(wù)中定義的方法。

          也可以簡單點兒

           protoc  hello/service.proto --go_out=hello/ --go-grpc_out=hello/ 

          這樣只會生成一個文件,大同小異。

          創(chuàng)建服務(wù)器

          要使我們的Greeter服務(wù)發(fā)揮作用,服務(wù)端編寫分為兩個部分:

          • 實現(xiàn)根據(jù)我們的服務(wù)定義生成的服務(wù)接口:完成我們服務(wù)的實際工作。
          • 運行g(shù)RPC服務(wù)器以偵聽來自客戶端的請求,并將其分發(fā)到正確的服務(wù)實現(xiàn)。
          package main

          import (
           "context"
           "fmt"
           hello "github.com/leoshus/proto-demo/pb"
           "google.golang.org/grpc"
           "log"
           "net"
           "os"
           "time"
          )

          type HelloServer struct {
          }

          func (h *HelloServer) SayHello(ctx context.Context, req *hello.HelloRequest) (*hello.HelloReply, error) {
           now := time.Now().Format("2006-01-02 15:04:05")
           hostname, _ := os.Hostname()
           log.Printf("%s say hello:%s\n", hostname, now)
           return &hello.HelloReply{
            Message: fmt.Sprintf("%s say hello %s :%s", hostname, req.Name, now),
           }, nil
          }

          func main() {
           server := grpc.NewServer()
           hello.RegisterGreeterServer(server, &HelloServer{})
           listener, err := net.Listen("tcp", ":8088")
           if err != nil {
            log.Printf("start server listen error:%v", err)
            return
           }
           log.Println("start server...")
           if err := server.Serve(listener); err != nil {
            log.Printf("start server error:%v", err)
           }
          }

          要構(gòu)建和啟動服務(wù)器,我們:

          1. 使用以下命令指定我們要用于偵聽客戶端請求的端口:lis,err:= net.Listen(...)
          2. 使用grpc.NewServer(...)創(chuàng)建gRPC服務(wù)器的實例。
          3. 在gRPC服務(wù)器上注冊我們的服務(wù)實現(xiàn)。
          4. 使用我們的端口詳細(xì)信息在服務(wù)器上調(diào)用Serve()進(jìn)行阻塞等待,直到進(jìn)程被殺死或調(diào)用Stop()為止。

          創(chuàng)建客戶端

          客戶端代碼主要是調(diào)用服務(wù)方法,我們首先需要

          1. 創(chuàng)建一個gRPC通道來與服務(wù)器通信。我們通過將服務(wù)器地址和端口號傳遞給grpc.Dial()來創(chuàng)建它,當(dāng)服務(wù)需要它們時,可以使用DialOptions在grpc.Dial中設(shè)置身份驗證憑據(jù)(例如TLS,GCE憑據(jù)或JWT憑據(jù))。Greeter服務(wù)不需要任何憑據(jù)。
          2. 設(shè)置gRPC通道后,我們需要一個客戶端存根來執(zhí)行RPC。例如,我們使用從.proto文件生成的pb包提供的NewGreeterClient方法獲取它。
          client := hello.NewGreeterClient(conn)
          1. 調(diào)用服務(wù)方法:在gRPC-Go中,RPC在阻塞/同步模式下運行,這意味著RPC調(diào)用等待服務(wù)器響應(yīng),并且將返回響應(yīng)或錯誤。

          整體代碼如下:

          package main

          import (
           "context"
           "flag"
           "fmt"
           hello "github.com/leoshus/proto-demo/pb"
           "google.golang.org/grpc"
           "google.golang.org/grpc/backoff"
           "google.golang.org/grpc/balancer/roundrobin"
           "log"
           "strings"
           "time"
          )

          func main() {
           log.SetFlags(log.Lshortfile | log.Ldate)
           var address string
           flag.StringVar(&address, "address", "localhost:8088", "grpc server address")
           flag.Parse()
           conn, err := grpc.Dial(strings.Join([]string{"dns:///", address}, ""), grpc.WithInsecure(),
            grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy":"%s"}`, roundrobin.Name)),
            //grpc.WithBlock(),
            grpc.WithConnectParams(grpc.ConnectParams{
             Backoff: backoff.Config{
              MaxDelay: 2 * time.Second,
             },
             MinConnectTimeout: 2 * time.Second,
            }))
           if err != nil {
            fmt.Println(err)
            return
           }
           defer conn.Close()
           client := hello.NewGreeterClient(conn)
           for range time.Tick(time.Second) {
            resp, err := client.SayHello(context.TODO(), &hello.HelloRequest{
             Name: "tom",
            })
            if err != nil {
             fmt.Println(err)
             log.Printf("say hello occur error:%v\n", err)
             return
            }
            log.Printf("say hello : %s \n", resp)
           }

          }

          制作鏡像

          通過Dockfile定義一個鏡像。

          FROM golang:1.16.3

          COPY . /app/src/grpc-demo
          WORKDIR /app/src/grpc-demo

          RUN go get -d -v ./...
          RUN go install -gcflags=all="-N -l " ./...

          這樣只需cd到Dockerfile所在目錄執(zhí)行docker build -t leoshus/grpc-demo:v1 .即可構(gòu)建一個鏡像。

          push鏡像

          因為構(gòu)建的鏡像要被之后的Kubernetes使用,所以需要講鏡像push到遠(yuǎn)端倉庫。當(dāng)然你也可以搭建私有倉庫來管理鏡像,這里我們使用官方的鏡像倉庫(https://hub.docker.com/)的演示。

          推送倉庫前你需要進(jìn)行登錄注冊。

          docker login #使用注冊的用戶名密碼登陸
          docker push leoshus/grpc-demo:v1 # 完成鏡像的推送

          編寫k8s資源文件

          首先是服務(wù)端在k8s上部署的資源文件編寫

          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: grpc-server
            labels:
              app-name: grpc-server
          spec:
            replicas: 3
            selector:
              matchLabels:
                app-name: grpc-server
            template:
              metadata:
                labels:
                  app-name: grpc-server
                name: grpc-server
              spec:
                containers:
                  - command:
                    - server
                    image: docker.io/leoshus/grpc-demo:v6
                    imagePullPolicy: Always
                    name: server
                    resources:
                      limits:
                        cpu: "0.5"
                        memory: 100Mi
                      requests:
                        cpu: "0.5"
                        memory: 100Mi
                restartPolicy: Always

          這里我們?yōu)榱朔奖阊菔?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">gRPC的負(fù)載均衡機制,我們使用了Headless Service,保證該服務(wù)不會分配Cluster IP,也不通過kube-proxy做反向代理和負(fù)載均衡。而是通過DNS提供穩(wěn)定的網(wǎng)絡(luò)ID來訪問,將headless service的后端直接解析為pod ip列表,然后由gRPC的負(fù)載均衡機制來選擇使用哪臺server

          apiVersion: v1
          kind: Service
          metadata:
            labels:
              app-name: grpc-server
            name: grpc-server-service
          spec:
            clusterIP: None
            ports:
              - name: grpc
                port: 31250
                protocol: TCP
                targetPort: 8088
            selector:
              app-name: grpc-server

          然后你可以只需下面命令來進(jìn)行服務(wù)部署

          kubectl apply -f grpc_server.yaml

          有了服務(wù)端集群,自然需要有客戶端來訪問,接下來我們需要編寫客戶端的資源文件并部署在k8s上

          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: grpc-client
            labels:
              app-name: grpc-client
          spec:
            selector:
              matchLabels:
                app-name: grpc-client
            template:
              metadata:
                labels:
                  app-name: grpc-client
                name: grpc-client
              spec:
                containers:
                  - command:
                      - client
                    args:
                      - --address
                      - grpc-server-service.default.svc.cluster.local:8088"
                    env:
                      - name: GRPC_GO_RETRY
                        value: "on"
                    image: docker.io/leoshus/grpc-demo:v6
                    imagePullPolicy: Always
                    name: client
                    resources:
                      limits:
                        cpu: "0.5"
                        memory: 100Mi
                      requests:
                        cpu: "0.5"
                        memory: 100Mi
                restartPolicy: Always

          部署客戶端

          kubectl apply -f grpc_client.yaml

          執(zhí)行kubectl get po -o wide可以看到當(dāng)前啟動的pod的情況

          $ kubectl get po -o wide
          NAME                           READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
          grpc-client-5b595ff864-kqw5g   1/1     Running   0          2m40s   10.1.0.104   docker-desktop   <none>           <none>
          grpc-server-6f579c4f88-cd8tj   1/1     Running   0          4m20s   10.1.0.101   docker-desktop   <none>           <none>
          grpc-server-6f579c4f88-kkqkm   1/1     Running   0          4m20s   10.1.0.102   docker-desktop   <none>           <none>
          grpc-server-6f579c4f88-p9zxp   1/1     Running   0          4m20s   10.1.0.103   docker-desktop   <none>           <none>

          看到STATUS一欄所有pod都處于Running狀態(tài)了,那么可以看下grpc_client打印的日志:

          $ kubectl logs -f grpc-client-5b595ff864-kqw5g
          2021/05/16 client.go:45: say hello : message:"grpc-server-6f579c4f88-p9zxp say hello tom :2021-05-16 05:45:26"  
          2021/05/16 client.go:45: say hello : message:"grpc-server-6f579c4f88-cd8tj say hello tom :2021-05-16 05:45:27"  
          2021/05/16 client.go:45: say hello : message:"grpc-server-6f579c4f88-kkqkm say hello tom :2021-05-16 05:45:28"  
          2021/05/16 client.go:45: say hello : message:"grpc-server-6f579c4f88-p9zxp say hello tom :2021-05-16 05:45:29"  
          2021/05/16 client.go:45: say hello : message:"grpc-server-6f579c4f88-cd8tj say hello tom :2021-05-16 05:45:30"  

          日志從輸出可見,此時的grpc的負(fù)載均衡已經(jīng)起作用了。

          對于服務(wù)的擴(kuò)縮容,也只需要一行命令:

          #縮容到1臺
          kubectl scale --replicas=1 deployment grpc-server
          #擴(kuò)容到5臺
          kubectl scale --replicas=5 deployment grpc-server

          當(dāng)然具體的擴(kuò)縮容的細(xì)節(jié)策略可以在yaml資源文件進(jìn)行配置

          至此一個簡單的grpc整合k8s的工程就完成了。詳細(xì)代碼可見(https://github.com/leoshus/grpc-k8s-demo)


          瀏覽 109
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情乱伦五月天 | 国产黄色电影在线播放 | 国产豆花一区二区三区 | 就是操视频官网 | 影音先锋AV在线资源网 |