【istio】手把手編寫template和adapter

demo主要是講解本身寫一個template和一個adapter,並在本地跑起這個demo來。html

template主要用於定契約,描述三個事實, 其實第三點能夠概括到第一點:git

  1. template該模板提供的服務類型,且每一個模板只能提供一種服務,服務種類有:CHECK、QUOTA、REPORT和ATTRIBUTE_GENERATOR四類;
  2. 提供數據模型,也就是這個模板主要是針對哪些數據進行處理,好比:指標類型數據,日誌類型數據,監控類型數據、鑑權類型數據、配額類型數據等等;
  3. 其餘的則是一些針對(1)的grpc service接口定義。其目的就是爲了讓支持該模板的adapter實現這些service接口定義;

demo演示

# terminal01, 啓動mix server服務,指定配置文件地址
mixs server --configStoreURL=fs://$(pwd)/testdata

# terminal02, 啓動適配器服務grpc server
./l04
print person request data: xhj, 31, clever01

# termial03, 經過mix客戶端命令發送一個check請求給mixer server。而後就出現了terminal02打印的日誌信息
mixc check --string_attributes destination.owner=xhj,destination.container.name=clever01 --int64_attributes destination.port=31

遇到的問題

在編寫自定義的template時,遇到了一些問題:github

  1. template編寫template.proto協議文件時,數據模型定義的變量名不能使用name,該名已經被template自身佔用。
  2. template編寫template.proto協議文件,還須要注意一點,須要相關寫一些註釋,和yaml註釋文件,不然在生成文件時,會報一些"no comment"的信息。雖然不影響業務邏輯,可是最好是寫上;

在編寫自定義的adapter時,一樣也遇到了一些問題:golang

其餘問題是在發起mixc check客戶端調用或者mixs server啓動時出現的,好比:shell

  1. * rpc error: code = Internal desc = grpc: error unmarshalling request: unexpected EOF
  2. config does not conform to schema of template 'person': unable to encode fieldEncoder email_address: destination.container.name | "cdh_cjx@163.com". unable to build primitve encoder for:email_address destination.container.name | "cdh_cjx@163.com". unknown attribute destination.container.name
  3. error creating instance: destination='person.template.istio-system:h1.handler.istio-system (myperson.adapter.istio-system)', error='fieldEncoder: age - lookup failed: 'destination.port''
  4. field 'age' not found in message 'Params'
  5. field 'age' is of type 'string' instead of expected type 'int'
  6. panic: Unknown map type string

針對unknown attribute xxx標籤的錯誤,通常都是在標籤屬性文件中沒有定義這類標籤,須要添加attributes.yaml文件中api

針對"expected type int"的錯誤,這裏重點介紹下:運維

咱們知道對於每個template定義,都有固定的服務種類支持,好比:check, quota, report, generate_attributes四類,那麼對於本demo實例,template是定義的check服務類型,那麼對應實現的adapter,則是對envoy proxy發過來器的grpc client請求進行check數據校驗,主要是指鑑權類的校驗.

那麼運維在寫kind爲handler的這類對針對check服務類型的配置時,其中的spec部分的params參數,則主要是鑑權類型的數據值,好比ACL、token等數據。

注意:是具體的數據,由於這會對grpc client發送過來的數據處理後,並與adapter指定的數據(kind: handler對象資源下的spec下的params數據)進行比對。

由於mixer server是動態watch配置的,因此配置是能夠動態修改的。這個就解決了check類型數據校驗的動態變化。

因此針對上面錯誤的解決方案,就是個人operator_cfg.yaml配置kind:handler的params應該是具體數據,而不是什麼age: destination.port | 31.tcp

針對panic: Unknown map type string錯誤,通常都是mixc客戶端的命令寫得有錯誤。ide

其餘問題有待考究。函數

編寫template

咱們建立一個person模板,用於處理全部與人相關的基本信息, 包括用戶名、年齡和email

cd $GOPATH/src/istio.io/istio/mixer/template

mkdir person && cd person cat template.proto

syntax = "proto3";

// Example config:
//
//```shell
//apiVersion: "config.istio.io/v1alpha2"
//kind: person
//metadata:
//   name: person
//   namespace: istio-system
//spec:
//   owner: destination.owner | "guest"
//   age: destination.port | "24"
//   email_address: destination.labels["email_address"] | "cdh_cjx@163.com"
//```
package person;

//import "policy/v1beta1/type.proto";
import "mixer/adapter/model/v1beta1/extensions.proto";

option (istio.mixer.adapter.model.v1beta1.template_variety) = TEMPLATE_VARIETY_CHECK;

// The `person` template represents person info key, used to authorize API calls.
message Template {
    // The owner being called (destination.owner).
    string  owner = 1;
    // The age being called (destination.port).
    int64 age = 2;
    // The email_address being called (destination.labels["email_address"]).
    string email_address = 3;
}

$GOPATH/src/istio.io/istio/bin/mixer_codegen.sh -t template.proto

ls

person.pb.html                          template_handler.gen.go                 template_handler_service.proto
template.proto                          template_handler_service.descriptor_set template_proto.descriptor_set
template.yaml                           template_handler_service.pb.go

編寫adapter

cd ../../adapter mkdir myperson && cd myperson

下面myperson.go用於提供grpc server服務,對於該check服務類型,則主要是用戶基本信息校驗,不經過返回給mixc客戶端相應的錯誤碼和錯誤信息

而config目錄則主要用於認證校驗grpc client發送過來的數據。也就是說config中存儲的是目標數據模型

mkdir config && touch myperson.go cat myperson.go

package myperson

import (
        "context"
        "fmt"
        "net"

        google_rpc "github.com/gogo/googleapis/google/rpc"
        "google.golang.org/grpc"
        istio_mixer_adapter_model_v1beta11 "istio.io/api/mixer/adapter/model/v1beta1"
        "istio.io/istio/mixer/adapter/myperson/config"
        "istio.io/istio/mixer/template/person"
)

type (
        Server interface {
                Addr() string
                Close() error
                Run(shutdown chan error)
        }

        MyPerson struct {
                listener net.Listener
                server   *grpc.Server
        }
)
var _ person.HandlePersonServiceServer = &MyPerson{}

func (m *MyPerson) Addr() string {
        return m.listener.Addr().String()
}

func (m *MyPerson) Close() error {
        if m.server != nil {
                m.server.GracefulStop()
        }
        if m.listener != nil {
                m.listener.Close()
        }
        return nil
}

func (m *MyPerson) Run(shutdown chan error) {
        shutdown <- m.server.Serve(m.listener)
}

func (m *MyPerson) HandlePerson(ctx context.Context, req *person.HandlePersonRequest) (
        *istio_mixer_adapter_model_v1beta11.CheckResult, error) {
        fmt.Printf("print person request data: %s, %d, %s\n",
                req.Instance.Owner,
                req.Instance.Age,
                req.Instance.EmailAddress,                                                                                
        )
        fmt.Println("print adapter info.....")
        cfg := &config.Params{}
        if err := cfg.Unmarshal(req.AdapterConfig.Value); err != nil {
                panic(err.Error())
        }
        fmt.Printf("print person adapter data: %s, %d, %s\n",
                cfg.Owner,
                cfg.Age,
                cfg.EmailAddress,
        )
        if req.Instance.Owner == cfg.Owner &&
                req.Instance.Age == cfg.Age &&
                req.Instance.EmailAddress == cfg.EmailAddress {
                return &istio_mixer_adapter_model_v1beta11.CheckResult{}, nil
        }
        return &istio_mixer_adapter_model_v1beta11.CheckResult{
                Status: google_rpc.Status{
                        Code:    40001,
                        Message: "基本信息不匹配",
                },
        }, nil
}


func NewMyPerson(addr string) (Server, error) {
        if addr == "" {
                addr = "127.0.0.1:4001"
        }
        listener, err := net.Listen("tcp", fmt.Sprintf("%s", addr))
        if err != nil {
                return nil, err
        }
        s := &MyPerson{
                listener: listener,
        }
        fmt.Printf("grpc://%s\n", addr)
        s.server = grpc.NewServer()
        person.RegisterHandlePersonServiceServer(s.server, s)
        return s, nil
}

cd config && touch config.proto

cat config.proto

syntax="proto3";

// config for myperson
package adapter.myperson.config;

import "gogoproto/gogo.proto";

option go_package="config";

// config for myperson
message Params {
    // Path of the file to save the information about runtime requests.
    string owner = 1;
    // age for person
    int64 age = 2;
    // email_address for person
    string email_address = 3;
}

$GOPATH/src/istio.io/istio/bin/mixer_codegen.sh -a config.proto -x "-s=false -n myperson -t person"

ls

adapter.myperson.config.pb.html config.proto                    myperson.yaml
config.pb.go                    config.proto_descriptor

注意事項:

  1. config目錄下的其餘文件動態生成,是須要myperson.go支持的,由於adpater動態生成的配置文件myperson.yaml中kind: adapter在spec部分是須要指定templates的。
  2. 按照(1), 在編寫myperson.go文件時,首先註釋掉HandlePerson方法中的函數體,由於myperson.go編譯須要能經過。而後等config目錄下的文件生成後,在補充實現grpc server的service方法

測試集實施

cd $GOPATH/src/istio.io/istio/mixer/adapter/myperson

mkdir testdata && touch operator_cfg.yaml

cp config/myperson.yaml testdata/

cat operator_cfg.yaml

# handler for adapter myperson
apiVersion: "config.istio.io/v1alpha2"
kind: handler
metadata:
 name: h1
 namespace: istio-system
spec:
 adapter: myperson
 connection:
     address: "127.0.0.1:4001" #replaces at runtime by the test
 params:
   owner: "donghai"
   age: 30
   email_address: "cdh_cjx@163.com"
---

# instance for template metric
apiVersion: "config.istio.io/v1alpha2"
kind: instance
metadata:
 name: i1
 namespace: istio-system
spec:
 template: person
 params:
   owner: destination.owner | "donghai"
   age: destination.port | 31
   email_address: destination.labels["email_address"] | "cdh_cjx@163.com"
---

# rule to dispatch to handler h1
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
 name: r1
 namespace: istio-system
spec:
 actions:
 - handler: h1.istio-system
   instances:
   - i1
---

cp operator_cfg.yaml testdata/

cp ../../template/person/template.yaml testdata/

cp ../../testdata/config/attributes.yaml testdata/

注意須要在attributes.yaml文件中追加幾個屬性詞彙

destination.port:
        valueType: INT64

若是發如今啓動mixer server服務時,報其餘屬性詞彙不存在,則在該文件中繼續添加。

運行結果:

當mixc check傳輸的屬性標籤名與值不等於設定的目標值時,返回值:

mixc check --string_attributes destination.owner=donghai --stringmap_attributes "destination.labels=email_address:cdh_cjx@163.com" --int64_attributes destination.port=31

Check RPC completed successfully. Check status was Code 40001 (h1.handler.istio-system:基本信息不匹配)

代表認證不經過

mixc check --string_attributes destination.owner=donghai --stringmap_attributes "destination.labels=email_address:cdh_cjx@163.com" --int64_attributes destination.port=30

當相同時,返回值:

Check RPC completed successfully. Check status was OK

總結

這個只是本地編寫adapter與template,並在本地測試驗證。若是要上k8s的話,這裏有一個完整的demo

參考文獻

  1. 教程|構建生產就緒的Istio Adapter
  2. Mixer Out Of Process Adapter Dev Guide
  3. Mixer Out of Process Adapter Walkthrough
相關文章
相關標籤/搜索