Saturday, October 14, 2017

etcd cluster setting

ETCDClusterSetting

ETCD Cluster Setting

Config File

ETCD_DATA_DIR=/opt/etcd
ETCD_NAME=master
ETCD_INITIAL_CLUSTER=master=http://172.16.101.71:2380,master02=http://172.16.101.72:2380,master03=http://172.16.101.73:2380
ETCD_LISTEN_PEER_URLS=http://172.16.101.71:2380
ETCD_INITIAL_ADVERTISE_PEER_URLS=http://172.16.101.71:2380
ETCD_ADVERTISE_CLIENT_URLS=http://172.16.101.71:2379
ETCD_LISTEN_CLIENT_URLS=http://172.16.101.71:2379,http://127.0.0.1:4001, http://172.16.101.71:4001, http://127.0.0.1:2379
ETCD_INITIAL_CLUSTER_STATE=new
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_LOG_OUTPUT=stdout
GOMAXPROCS=8
[Unit]
Description=Etcd Server
Documentation=https://github.com/coreos/etcd
After=network.target
Before=flanneld.service

[Service]
User=root
Type=simple
EnvironmentFile=-/opt/config/etcd.conf
ExecStart=/opt/bin/etcd
Restart=on-failure
RestartSec=10s
LimitNOFILE=40000

[Install]
WantedBy=multi-user.target

Testing

deployer@k8s-master01:~$ sudo /opt/bin/etcdctl member list
[sudo] password for deployer:
a33460d93e5a3bc: name=master03 peerURLs=http://172.16.101.73:2380 clientURLs=http://172.16.101.73:2379 isLeader=true
5dbe67c8f4804006: name=master02 peerURLs=http://172.16.101.72:2380 clientURLs=http://172.16.101.72:2379 isLeader=false
ae3548c613d49701: name=master peerURLs=http://172.16.101.71:2380 clientURLs=http://172.16.101.71:2379 isLeader=false

Friday, October 13, 2017

golang 1.8 and govendor

go1.8govendor

Golang 1.8 installation and Govendor

We'd like to talk more about govendor, since we need a individual dependency for each project.

Install Golang Package

wget https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
tar -xvf go1.8.3.linux-amd64.tar.gz
mv go /usr/local

Setup Environment

export GOROOT="/usr/local/go"
export GOPATH="/go"
export GOBIN="/go/bin"

Store Environment

export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

We also export binary directory, so we can direct launch it after go-install.

root@golang183:/go/src/github.com/jj# go env
GOARCH="amd64"
GOBIN="/go/bin"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

Install VIM Development Environment

see my previous blog

http://gogosatellite.blogspot.tw/search?q=golang

Install Govendor

Let's started install govendor

go get -u github.com/kardianos/govendor

go to subdirectory, since we need a private package not global packages.

/go/src/github.com/jj
root@golang183:/go/src/github.com/jj# ls
a.go  vendor

It will create vendor directory.

Now let's install a package

govendor fetch gopkg.in/cheggaaa/pb.v1

Now we install pb package to vendor directory.

root@golang183:/go/src/github.com/jj# ls vendor/
github.com  gopkg.in  vendor.json

Build and Install After Govendor

Here is the source code.

package main

import (
    "gopkg.in/cheggaaa/pb.v1"
    "time"
)

func main() {
    count := 100000
    bar := pb.StartNew(count)
    for i := 0; i < count; i++ {
        bar.Increment()
        time.Sleep(time.Millisecond)
    }
    bar.FinishPrint("The End!")
}

Build and Install

root@golang183:/go/src/github.com/jj# go build
root@golang183:/go/src/github.com/jj# ls
a.go  jj  vendor
root@golang183:/go/src/github.com/jj# go install

root@golang183:/go/src/github.com/jj# which jj
/go/bin/jj

Reboot Setup

root@golang183:# cat /etc/profile.d/golang.sh
export GOROOT=/usr/local/go
export GOPATH=/go
export GOBIN=/go/bin
export GOARCH=amd64
export GOOS=linux
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

Result

Now we have a individual package dependency for each project. for example, we can have /go/src/github.com/bb project and its own vendor for building binary.

Monday, October 9, 2017

grpc for python and golang

GRPC

GRPC in Python

Install grpc for python

pip install grpcio-tools

GRPC in Python Server

Start From Proto

// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

package cameramap;

// The greeting service definition.
service Cameramap {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayGoodBye (HelloRequest) returns (HelloReply) {}
}

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

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Directory

Before you start to generate grpc files, you must set up .proto as you wish to request and response.

├── cameramap
│   └── cameramap.proto
└── cameramap_server
    ├── 

where the directory cameramap put the proto file and we start to program server from cameramap_server.

Recompile Proto

Into directory cameramap_server.

python -m grpc_tools.protoc -I ../cameramap/ --python_out=. --grpc_python_out=. ../cameramap/cameramap.proto

It will generate cameramap_pb2_grpc.py and cameramap_pb2_grpc.py automatically.

Let's see the directory changes

├── cameramap
│   └── cameramap.proto
└── cameramap_server
    ├── cameramap_pb2_grpc.py
    ├── cameramap_pb2_grpc.pyc
    ├── cameramap_pb2.py
    ├── cameramap_pb2.pyc
    └── server.py

server.py

from concurrent import futures
import time
import math

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.name
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    cameramap_pb2_grpc.add_CameramapServicer_to_server(CameramapServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

Run the python server

python server.py

GRPC in Python Client

In cameramap_client directory

python -m grpc_tools.protoc -I ../cameramap/ --python_out=. --grpc_python_out=. ../cameramap/cameramap.proto

Let's see the directory first

root@ubuntu:~/grpc# tree
.
├── cameramap
│   └── cameramap.proto
├── cameramap_client
│   ├── cameramap_pb2_grpc.py
│   └── cameramap_pb2.py
└── cameramap_server
    ├── cameramap_pb2_grpc.py
    ├── cameramap_pb2_grpc.pyc
    ├── cameramap_pb2.py
    ├── cameramap_pb2.pyc
    └── server.py

where client.py in directory cameramap_client

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)

    response = stub.SayHello(cameramap_pb2.HelloRequest(name='you'))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

Result

root@ubuntu:~/grpc/cameramap_client# python client.py
Greeter client received: Hello, you!

GRPC in Golang

You can prepare the .proto file in directory cameramap as before.

protoc -I cameramap/ cameramap/cameramap.proto --go_out=plugins=grpc:cameramap

Directory Tree

root@golang17:~/golang/projects/wru/src/github.com/jonah/rpcmap# tree
.
├── cameramap
│   ├── cameramap.pb.go
│   └── cameramap.proto
├── cameramap_client
│   └── main.go
├── cameramap_server
│   └── main.go
└── rebuild.sh

It will generate cameramap.pb.go in directory cameramap for your used.

Server Code

package main

import (
    "log"
    "net"

    pb "github.com/jonah/rpcmap/cameramap"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    //pb "google.golang.org/grpc/examples/helloworld/helloworld"
    "fmt"
    "google.golang.org/grpc/reflection"
)

const (
    port = ":50051"
)

var newpara int

// server is used to implement helloworld.GreeterServer.
type server struct{}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    fmt.Println(port)
    fmt.Println(newpara)
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

/* here we put camera map location with k-nn */
func modifypara() {
    newpara = 3
}

func main() {

    modifypara()

    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    //  pb.RegisterGreeterServer(s, &server{})
    pb.RegisterCameramapServer(s, &server{})
    // Register reflection service on gRPC server.
    reflection.Register(s)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

Client Code

package main

import (
    "log"
    "os"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    //  pb "google.golang.org/grpc/examples/helloworld/helloworld"
    pb "github.com/jonah/rpcmap/cameramap"
    //pb "helloworld/helloworld"
)

const (
    //  address     = "localhost:50051"
    address     = "192.168.51.129:50051"
    defaultName = "world"
)

func main() {
    // Set up a connection to the server.
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    //  c := pb.NewGreeterClient(conn)
    c := pb.NewCameramapClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}

Result

You can use one .proto file, and generate it for different programming language used.

How to use Dict in Grpc in Python

.Proto File

where .proto file

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

package cameramap;

// The greeting service definition.
service Cameramap {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayGoodBye (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  //string name = 1;
  map<string, string> mapfield = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

where we can see the attribute mapfild is a map type.

Client Code

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    dictt = {'Name': 'Zara', 'Age': '7', 'Class': 'First'}
    response = stub.SayHello(cameramap_pb2.HelloRequest(mapfield=dictt))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

We can directly send a dict to server.

Server Code

from concurrent import futures
import time
import math

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.mapfield
        print request.mapfield['Name']
        #return cameramap_pb2.HelloReply(message='Hello, %s!' % request.name)
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.mapfield)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    cameramap_pb2_grpc.add_CameramapServicer_to_server(CameramapServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

You can directly parse request.mapfield['Name'] as dict in python.

Note:

You cannot use repeated map in GRPC, since gprc not support it.

Complex Request

We would like to send a complex request composed a list, such as followed

{"a":"b", "c":["1","2","3"], "d":"e"}

Client Code

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    dictt = {'Name': 'Zara', 'Age': '7', 'Class': 'First'}
    listt = ["1","2","3"]
    # changing list to a string
    dictt["list"] = str(listt)
    response = stub.SayHello(cameramap_pb2.HelloRequest(mapfield=dictt))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

Use str() function forces list to a string.

Server Code

from concurrent import futures
import time
import math

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.mapfield
        print request.mapfield['Name']
        #return cameramap_pb2.HelloReply(message='Hello, %s!' % request.name)
        listt =  eval(request.mapfield['list'])
        # now it become a list
        for v in listt:
            print v
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.mapfield)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    cameramap_pb2_grpc.add_CameramapServicer_to_server(CameramapServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

Use eval() function to force string to list.

List Requst

message HelloRequest {
  //repeated Datatest datatest = 1;
  repeated string datatest = 1;
}

Client Code

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    #dictt = {'Name': 'Zara', 'Age': '7', 'Class': 'First'}
    listt = ["1","2","3"]
    # changing list to a string
    #dictt["list"] = str(listt)
    response = stub.SayHello(cameramap_pb2.HelloRequest(datatest=listt))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

Server Code

from concurrent import futures
import time
import math

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.datatest
        #return cameramap_pb2.HelloReply(message='Hello, %s!' % request.name)
        for v in request.datatest:
            print v
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.datatest)



def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    cameramap_pb2_grpc.add_CameramapServicer_to_server(CameramapServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

Complex Define

In .proto

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
message Rectangle {
  // One corner of the rectangle.
  Point lo = 1;

  // The other corner of the rectangle.
  Point hi = 2;
}

In Client

def guide_list_features(stub):
  rectangle = route_guide_pb2.Rectangle(
      lo=route_guide_pb2.Point(latitude=400000000, longitude=-750000000),
      hi=route_guide_pb2.Point(latitude=420000000, longitude=-730000000))
  print("Looking for features between 40, -75 and 42, -73")

More Complicate Data

It's not allow for grpc to use the folowing request or response.

[{"a":"b"}, {"c":"d"}, {"e":"f"}]

Unfortunately, we usually see the result but it is not strict. So grpc not allow above requst.

The better way is like this

{"alarms" : [{"a":"b"}, {"c":"d"}, {"e":"f"}] }

We shoud note what the request for, now it's alarms. So it's still a map but with list inside.

.Proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

package cameramap;

// The greeting service definition.
service Cameramap {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayGoodBye (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.

message HelloRequest {
  map<string, string> mapfield = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Client Code

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    data =[]
    data.append({'Name': 'Zara', 'Age': '7', 'Class': 'First'})
    data.append({'Name': 'Zara1', 'Age': '71', 'Class': 'First1'})
    dictt = {"data":str(data)}

    #a = [{'Name': 'Zara', 'Age': '7', 'Class': 'First'}, {'Name': 'Zara1', 'Age': '71', 'Class': 'First1'}]

    # changing list to a string
    #dictt["list"] = str(listt)
    response = stub.SayHello(cameramap_pb2.HelloRequest(mapfield=dictt))
    #response = stub.SayHello(bb)
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

Server Code

from concurrent import futures
import time
import math

import grpc

import cameramap_pb2
import cameramap_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.mapfield
        #return cameramap_pb2.HelloReply(message='Hello, %s!' % request.name)
        res = eval(request.mapfield["data"])
        print res
        for re in res:
            print re
            print re["Name"]
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.mapfield)



def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    cameramap_pb2_grpc.add_CameramapServicer_to_server(CameramapServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

Repeated

Not working on map, but add() is must to understand

  channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    #dictt = {'Name': 'Zara', 'Age': '7', 'Class': 'First'}
    s = cameramap_pb2.Datatest(mapfield={'Name': 'Zara', 'Age': '7', 'Class': 'First'})
    b = cameramap_pb2.HelloRequest()
    bb = b.datatest.add()
    bb.mapfield={'Name': 'Zara', 'Age': '7', 'Class': 'First'}

Real Cases

We would like to request a message as followed,

{
    "api": "douban_movie",
    "domain": "movie",
    "res": [
            {
                "name": "卧虎藏龙",
                "rating": 7.8,
                "year": 2000
            }
          ],
    "time": "163ms"
}

Client Code

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc




def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    dictt = {'api': 'douban_movie', 'domain': 'movie', 'time': '163ms'}
    listt = [{"name":"dragon", "rating":7.8, "year":"2000"}]
    dictt["res"] = str(listt)
    response = stub.SayHello(cameramap_pb2.HelloRequest(mapfield=dictt))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

Remember, GRPC is strictly type, so you have to trasfer the generic type to string.

Server Code

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.mapfield

        listt =  eval(request.mapfield['res'])
        # now it become a list
        print listt[0]['rating'], type(listt[0]['rating'])
        for v in listt:
            print v
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.mapfield)

We use eval function to seperate the list, and accessing the rating to understand its type, float. Since python supports generic types.

Seperate Json By String

Proto Code

message HelloRequest {
  //string name = 1;
  map<string, string> mapfield = 1;
  repeated string res = 2;
}

Client Code

    stub = cameramap_pb2_grpc.CameramapStub(channel)
    dictt = {'api': 'douban_movie', 'domain': 'movie', 'time': '163ms'}
    listt = {"name":"dragon", "rating": 7.8, "year":"2000"}
    listts = []
    listts.append(json.dumps(listt))
    listt1 = {"name":"dragon1", "rating": 2.8, "year":"2001"}
    listts.append(json.dumps(listt1))
    response = stub.SayHello(cameramap_pb2.HelloRequest(mapfield=dictt, res=listts))

Servr Code

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.mapfield
        mapdata = request.mapfield
        print mapdata['api']
        listts =  request.res
        # now it become a list
        print listts
        for listt in listts:
            print listt, type(listt)
            d = json.loads(listt)
            print d['rating']

        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.mapfield)

Seperate Json By Multiple Definition

In Proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

package cameramap;

// The greeting service definition.
service Cameramap {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayGoodBye (HelloRequest) returns (HelloReply) {}
}

message ResData {
   string name = 1;
   float rating = 2;
   string year = 3;
}

// The request message containing the user's name.
message HelloRequest {
  //string name = 1;
  map<string, string> mapfield = 1;
  repeated ResData res = 2;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Client Code

from __future__ import print_function

import grpc

import cameramap_pb2
import cameramap_pb2_grpc
import json




def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = cameramap_pb2_grpc.CameramapStub(channel)
    dictt = {'api': 'douban_movie', 'domain': 'movie', 'time': '163ms'}

    s1 = cameramap_pb2.ResData(name ="haha", rating = 9.9, year = "2017")
    s2 = cameramap_pb2.ResData(name ="haha1", rating = 9.99, year = "2017")
    # it is allowed for the following way
    tmp={}
    tmp["name"]="haha2"
    tmp["rating"] = 9.342
    tmp["year"]="2017"

    s = []
    s.append(s1)
    s.append(s2)
    s.append(tmp)


    hq = cameramap_pb2.HelloRequest(mapfield=dictt, res=s)
    response = stub.SayHello(hq)
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    run()

Server Code

from concurrent import futures
import time
import math

import grpc

import cameramap_pb2
import cameramap_pb2_grpc
import json

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class CameramapServicer(cameramap_pb2_grpc.CameramapServicer):
    def SayHello(self, request, context):
        print request.mapfield
        mapdata = request.mapfield
        print mapdata['api']
        listts =  request.res
        # now it become a list
        print listts
        print '***************************'
        for listt in listts:
            print listt
            print '-------------------'
        return cameramap_pb2.HelloReply(message='Hello, %s!' % request.mapfield)



def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    cameramap_pb2_grpc.add_CameramapServicer_to_server(CameramapServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

Retries Setting

    proxyOpts := []grpc.DialOption{}
    // since it's exponetially backoff, max delay time
    var DefaultBackoffConfig = grpc.BackoffConfig{
        MaxDelay: 12 * time.Second,
    }
    proxyOpts = append(proxyOpts, grpc.WithBackoffConfig(DefaultBackoffConfig))
    // reconnect time out
    proxyOpts = append(proxyOpts, grpc.WithTimeout(6000*time.Second))
    // it must have, to block the initiation before established
    proxyOpts = append(proxyOpts, grpc.WithBlock())
    proxyOpts = append(proxyOpts, grpc.WithInsecure())
    
    
    
    //conn, err := grpc.Dial(address, grpc.WithInsecure())
    //  conn, err := grpc.Dial(address, proxyOpts...)
    conn, err := grpc.Dial(address, proxyOpts...)
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }

Consul DNS Testing

ConsulDNS server in 192.168.51.129.

consul agent -dev -ui -server -node=consul-dev -client=192.168.51.129 -dns-port=53 -config-dir=/root/dnsconfig -domain=localdomain

In Other server, we need to setup nameserver.

root@ubuntu:~# cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 192.168.51.129
search localdomain

Adding Endpoints to Consul DNS Server.

root@ubuntu:~# cat dnsconfig/service.json
{

         "services": [{

         "id":"dotnetcoresample",

         "name":"dotnetcoresample",

         "tags":["dotnetcoresample"],

         "address": "192.168.51.129",

         "port": 50051

       }]

     }

However, port will not include the dns routing only IP takes effect.

In Any Server, try it.


root@ubuntu:~# ping dotnetcoresample.service.dc1.consul
PING dotnetcoresample.service.dc1.consul.localdomain (192.168.51.129) 56(84) bytes of data.
64 bytes from 192.168.51.129: icmp_seq=1 ttl=64 time=0.011 ms
64 bytes from 192.168.51.129: icmp_seq=2 ttl=64 time=0.039 ms
64 bytes from 192.168.51.129: icmp_seq=3 ttl=64 time=0.033 ms

or

root@ubuntu:~# ping dotnetcoresample.service
PING dotnetcoresample.service.localdomain (192.168.51.129) 56(84) bytes of data.
64 bytes from 192.168.51.129: icmp_seq=1 ttl=64 time=0.012 ms
64 bytes from 192.168.51.129: icmp_seq=2 ttl=64 time=0.040 ms

GRPC-LB for Aware Client

It only supports for grpc-go client, that will connect and record to etcd and consul as a metadata stored.

https://github.com/liyue201/grpc-lb

So for general purpose, such as consul-dns, might be a good solution.

Thursday, October 5, 2017

kubernetes: How to Use Kubectl Context to Switch User and Cluster

context

Kubernetes Context Setting

One can connect to different remote Cluster and with different namespace via a single client. And the single client must to install kubectl to access multiple clusters.

Assume 172.16.155.158:8080 is an k8s api-server, you can use the following command to setup different context and connect with switch context to different cluster (an k8s cluter).

So it's very easy, that you can control different kubernetes cluster via single client with execution file kubectl and context setting.

Create Cluster and Context

kubectl config set-cluster firstcluster --server=http://172.16.155.158:8080 --insecure-skip-tls-verify=true
kubectl config set-context firstctx --cluster=firstcluster --namespace=inq
kubectl config use-context firstctx

where --server is connect to the api-server.

root@kuberm:~# kubectl config view
apiVersion: v1
clusters:
- cluster:
    insecure-skip-tls-verify: true
    server: http://172.16.155.158:8080
  name: firstcluster
contexts:
- context:
    cluster: firstcluster
    namespace: inq
    user: ""
  name: ctx-first
- context:
    cluster: firstcluster
    namespace: inq
    user: ""
  name: firstctx
current-context: firstctx
kind: Config
preferences: {}
users: []

Multiple Context

of course you might need another cluster that have another api-server, you then can directly connect to different cluster via the context switch.

The following is the two cluster with two context shown in .kube/config

root@kuberm:~# cat .kube/config
apiVersion: v1
clusters:
- cluster:
    insecure-skip-tls-verify: true
    server: http://172.16.155.158:8080
  name: firstcluster
- cluster:
    insecure-skip-tls-verify: true
    server: http://172.16.155.158:8080
  name: secondcluster
contexts:
- context:
    cluster: firstcluster
    namespace: inq
    user: ""
  name: firstctx
- context:
    cluster: secondcluster
    namespace: inq
    user: ""
  name: secondctx
current-context: firstctx
kind: Config
preferences: {}
users: []

Connect to Pod to Program

In the context environment, you can access the pods limited in namespace you used.

root@kubecontext:~# kubectl config current-context
ctx-default
root@kubecontext:~# kubectl get po
NAME               READY     STATUS    RESTARTS   AGE
webserver1-llgxz   1/1       Running   0          2h
webserver1-qt3nd   0/1       Unknown   0          70d
webserver2-94vjs   1/1       Running   1          70d
root@kubecontext:~# kubectl exec -it webserver2-94vjs -- bash
root@webserver2-94vjs:/#

Now you can connect to container in the context environment.

Release Context

If you want to release or reset the context

rm .kube/config