基于Go开发PaaS平台2

Go开发PaaS平台核心功能

代码仓库地址GitHub - yunixiangfeng/gopaas

第7章 云原生 Go PaaS 平台路由管理功能开发,对外域名映射,动态设置域名

域名能够让我们的服务提供外网访问的能力,让公网也能够访问到集群内部的资源,是我们开放业务的入口。将讲解 Ingress 的核心原理和流量转化流程,熟练掌握如何应用 K8s 中的服务通过域名的方式映射到公网,提供外网访问能力。

7-1 路由ingress 架构详解

Go PaaS 平台服务管理开发

主要内容
路由作用讲解
路由原理和架构说明
开发路由管理功能
为什么要用Ingress?
ClusterlP的方式只能在集群内部访问。
NodePort 当有几十上百的服务在集群中运行时,很难管理。
LoadBalance 方式受限于云平台。

路由 Ingress 简介
Ingress:一个普通资源对象,用来表明具体路由规则
Ingress-controller: 来执行转发规则

路由 Ingress 架构说明

路由Ingress-controller (nginx) 如何运作的? 

7-2 路由model与repository开发调整

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  newService github.com/yunixiangfeng/gopaas/route

cd route

go mod tidy

// versions:
// 	protoc-gen-go v1.27.1
// 	protoc        v3.15.7
// source: proto/svc/svc.proto
protoc --proto_path=. --micro_out=. --go_out=:. ./proto/route/route.proto

C:\Users\Administrator\Desktop\gopaas\route\domain\model\route.go

package model

type Route struct {
	ID             int64       `gorm:"primary_key;not_null;auto_increment"`
	RouteName      string      `json:"route_name"`
	RouteNamespace string      `json:"route_namespace"`
	RouteHost      string      `json:"route_host"`
	RoutePath      []RoutePath `gorm:"ForeignKey:RouteID" json:"route_path"`
}

C:\Users\Administrator\Desktop\gopaas\route\domain\model\route_path.go

package model

type RoutePath struct {
	ID                      int64  `gorm:"primary_key;not_null;auto_increment"`
	RouteID                 int64  `json:"route_id"`
	RoutePathName           string `json:"route_path_name"`
	RouteBackendService     string `json:"route_backend_service"`
	RouteBackendServicePort int32  `json:"route_backend_service_port"`
}

C:\Users\Administrator\Desktop\gopaas\route\domain\repository\route_repository.go

package repository

import (
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/model"
)

//创建需要实现的接口
type IRouteRepository interface {
	//初始化表
	InitTable() error
	//根据ID查处找数据
	FindRouteByID(int64) (*model.Route, error)
	//创建一条 route 数据
	CreateRoute(*model.Route) (int64, error)
	//根据ID删除一条 route 数据
	DeleteRouteByID(int64) error
	//修改更新数据
	UpdateRoute(*model.Route) error
	//查找route所有数据
	FindAll() ([]model.Route, error)
}

//创建routeRepository
func NewRouteRepository(db *gorm.DB) IRouteRepository {
	return &RouteRepository{mysqlDb: db}
}

type RouteRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *RouteRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.Route{}, &model.RoutePath{}).Error
}

//根据ID查找Route信息
func (u *RouteRepository) FindRouteByID(routeID int64) (route *model.Route, err error) {
	route = &model.Route{}
	return route, u.mysqlDb.Preload("RoutePath").First(route, routeID).Error
}

//创建Route信息
func (u *RouteRepository) CreateRoute(route *model.Route) (int64, error) {
	return route.ID, u.mysqlDb.Create(route).Error
}

//根据ID删除Route信息
func (u *RouteRepository) DeleteRouteByID(routeID int64) error {
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	if tx.Error != nil {
		common.Error(tx.Error)
		return tx.Error
	}
	//开始删除
	if err := u.mysqlDb.Where("id = ?", routeID).Delete(&model.Route{}).Error; err != nil {
		tx.Rollback()
		common.Error(err)
		return err
	}
	//删除关联表
	if err := u.mysqlDb.Where("route_id = ?", routeID).Delete(&model.RoutePath{}).Error; err != nil {
		tx.Rollback()
		common.Error(err)
		return err
	}
	return tx.Commit().Error
}

//更新Route信息
func (u *RouteRepository) UpdateRoute(route *model.Route) error {
	return u.mysqlDb.Model(route).Update(route).Error
}

//获取结果集
func (u *RouteRepository) FindAll() (routeAll []model.Route, err error) {
	return routeAll, u.mysqlDb.Preload("RoutePath").Find(&routeAll).Error
}

7-3 路由 service 开发 

C:\Users\Administrator\Desktop\gopaas\route\domain\service\route_data_service.go 

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/model"
	"github.com/yunixiangfeng/gopaas/route/domain/repository"
	"github.com/yunixiangfeng/gopaas/route/proto/route"
	v1 "k8s.io/api/apps/v1"
	v12 "k8s.io/api/networking/v1"
	v14 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IRouteDataService interface {
	AddRoute(*model.Route) (int64, error)
	DeleteRoute(int64) error
	UpdateRoute(*model.Route) error
	FindRouteByID(int64) (*model.Route, error)
	FindAllRoute() ([]model.Route, error)

	CreateRouteToK8s(*route.RouteInfo) error
	DeleteRouteFromK8s(*model.Route) error
	UpdateRouteToK8s(*route.RouteInfo) error
}

//创建
//注意:返回值 IRouteDataService 接口类型
func NewRouteDataService(routeRepository repository.IRouteRepository, clientSet *kubernetes.Clientset) IRouteDataService {
	return &RouteDataService{RouteRepository: routeRepository, K8sClientSet: clientSet, deployment: &v1.Deployment{}}
}

type RouteDataService struct {
	//注意:这里是 IRouteRepository 类型
	RouteRepository repository.IRouteRepository
	K8sClientSet    *kubernetes.Clientset
	deployment      *v1.Deployment
}

//创建k8s(把proto 属性补全)
func (u *RouteDataService) CreateRouteToK8s(info *route.RouteInfo) (err error) {
	ingress := u.setIngress(info)
	//查找是否存在
	if _, err = u.K8sClientSet.NetworkingV1().Ingresses(info.RouteNamespace).Get(context.TODO(), info.RouteName, v14.GetOptions{}); err != nil {
		if _, err = u.K8sClientSet.NetworkingV1().Ingresses(info.RouteNamespace).Create(context.TODO(), ingress, v14.CreateOptions{}); err != nil {
			//创建不成功记录错误
			common.Error(err)
			return err
		}
		return nil
	} else {
		common.Error("路由 " + info.RouteName + " 已经存在")
		return errors.New("路由 " + info.RouteName + " 已经存在")
	}
}

func (u *RouteDataService) setIngress(info *route.RouteInfo) *v12.Ingress {
	route := &v12.Ingress{}
	//设置路由
	route.TypeMeta = v14.TypeMeta{
		Kind:       "Ingress",
		APIVersion: "v1",
	}
	//设置路由基础信息
	route.ObjectMeta = v14.ObjectMeta{
		Name:      info.RouteName,
		Namespace: info.RouteNamespace,
		Labels: map[string]string{
			"app-name": info.RouteName,
			"author":   "wu123",
		},
		Annotations: map[string]string{
			"k8s/generated-by-wu": "由代码创建",
		},
	}
	//使用 ingress-nginx
	className := "nginx"
	//设置路由 spec 信息
	route.Spec = v12.IngressSpec{
		IngressClassName: &className,
		//默认访问服务
		DefaultBackend: nil,
		//如果开启https这里要设置
		TLS:   nil,
		Rules: u.getIngressPath(info),
	}
	return route
}

//根据info信息获取path路径
func (u *RouteDataService) getIngressPath(info *route.RouteInfo) (path []v12.IngressRule) {
	//1.设置host
	pathRule := v12.IngressRule{Host: info.RouteHost}
	//2.设置Path
	ingressPath := []v12.HTTPIngressPath{}
	for _, v := range info.RoutePath {
		pathType := v12.PathTypePrefix
		ingressPath = append(ingressPath, v12.HTTPIngressPath{
			Path:     v.RoutePathName,
			PathType: &pathType,
			Backend: v12.IngressBackend{
				Service: &v12.IngressServiceBackend{
					Name: v.RouteBackendService,
					Port: v12.ServiceBackendPort{
						Number: v.RouteBackendServicePort,
					},
				},
			},
		})
	}

	//3.赋值 Path
	pathRule.IngressRuleValue = v12.IngressRuleValue{HTTP: &v12.HTTPIngressRuleValue{Paths: ingressPath}}
	path = append(path, pathRule)
	return
}

//更新route
func (u *RouteDataService) UpdateRouteToK8s(info *route.RouteInfo) (err error) {
	ingress := u.setIngress(info)
	if _, err = u.K8sClientSet.NetworkingV1().Ingresses(info.RouteNamespace).Update(context.TODO(), ingress, v14.UpdateOptions{}); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//删除route
func (u *RouteDataService) DeleteRouteFromK8s(route2 *model.Route) (err error) {
	//删除Ingress
	if err = u.K8sClientSet.NetworkingV1().Ingresses(route2.RouteNamespace).Delete(context.TODO(), route2.RouteName, v14.DeleteOptions{}); err != nil {
		//如果删除失败记录下
		common.Error(err)
		return err
	} else {
		if err := u.DeleteRoute(route2.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除 ingress ID:" + strconv.FormatInt(route2.ID, 10) + " 成功!")
	}
	return
}

//插入
func (u *RouteDataService) AddRoute(route *model.Route) (int64, error) {
	return u.RouteRepository.CreateRoute(route)
}

//删除
func (u *RouteDataService) DeleteRoute(routeID int64) error {
	return u.RouteRepository.DeleteRouteByID(routeID)
}

//更新
func (u *RouteDataService) UpdateRoute(route *model.Route) error {
	return u.RouteRepository.UpdateRoute(route)
}

//查找
func (u *RouteDataService) FindRouteByID(routeID int64) (*model.Route, error) {
	return u.RouteRepository.FindRouteByID(routeID)
}

//查找
func (u *RouteDataService) FindAllRoute() ([]model.Route, error) {
	return u.RouteRepository.FindAll()
}

C:\Users\Administrator\Desktop\gopaas\route\proto\route\route.proto

syntax = "proto3";

package route;

option go_package = "./proto/route;route";

service Route {
	//对外提供添加服务
	rpc AddRoute(RouteInfo) returns (Response) {}
	rpc DeleteRoute(RouteId) returns (Response) {}
	rpc UpdateRoute(RouteInfo) returns (Response) {}
	rpc FindRouteByID(RouteId) returns (RouteInfo) {}
	rpc FindAllRoute(FindAll) returns (AllRoute) {}
}
message RouteInfo {
	int64 id = 1;
	string route_name = 2;
	string route_namespace =3;
	string route_host =4;
	repeated RoutePath route_path=5;
}

message RoutePath {
	int64 id = 1;
	int64 route_id =2;
	string route_path_name=3;
	string route_backend_service=4;
	int32 route_backend_service_port=5;
}

message RouteId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllRoute {
	repeated RouteInfo route_info = 1;
}


7-4 路由 handler 逻辑开发和注意事项

创建handler对外暴露服务

C:\Users\Administrator\Desktop\gopaas\route\handler\routeHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/model"
	"github.com/yunixiangfeng/gopaas/route/domain/service"
	route "github.com/yunixiangfeng/gopaas/route/proto/route"
)

type RouteHandler struct {
	//注意这里的类型是 IRouteDataService 接口类型
	RouteDataService service.IRouteDataService
}

// 添加路由
func (e *RouteHandler) AddRoute(ctx context.Context, info *route.RouteInfo, rsp *route.Response) error {
	log.Info("Received *route.AddRoute request")
	route := &model.Route{}
	if err := common.SwapTo(info, route); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//创建route到k8s
	if err := e.RouteDataService.CreateRouteToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//写入数据库
		routeID, err := e.RouteDataService.AddRoute(route)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		common.Info("Route 添加成功 ID 号为:" + strconv.FormatInt(routeID, 10))
		rsp.Msg = "Route 添加成功 ID 号为:" + strconv.FormatInt(routeID, 10)
	}
	return nil
}

//删除route
func (e *RouteHandler) DeleteRoute(ctx context.Context, req *route.RouteId, rsp *route.Response) error {
	log.Info("Received *route.DeleteRoute request")
	routeModel, err := e.RouteDataService.FindRouteByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//从k8s中删除,并且删除数据库中数据
	if err := e.RouteDataService.DeleteRouteFromK8s(routeModel); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//更新route
func (e *RouteHandler) UpdateRoute(ctx context.Context, req *route.RouteInfo, rsp *route.Response) error {
	log.Info("Received *route.UpdateRoute request")
	if err := e.RouteDataService.UpdateRouteToK8s(req); err != nil {
		common.Error(err)
		return err
	}
	//查询数据库的信息
	routeModel, err := e.RouteDataService.FindRouteByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//数据更新
	if err := common.SwapTo(req, routeModel); err != nil {
		common.Error(err)
		return err
	}
	return e.RouteDataService.UpdateRoute(routeModel)
}

//根据ID查询route信息
func (e *RouteHandler) FindRouteByID(ctx context.Context, req *route.RouteId, rsp *route.RouteInfo) error {
	log.Info("Received *route.FindRouteByID request")
	routeModel, err := e.RouteDataService.FindRouteByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//数据转化
	if err := common.SwapTo(routeModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *RouteHandler) FindAllRoute(ctx context.Context, req *route.FindAll, rsp *route.AllRoute) error {
	log.Info("Received *route.FindAllRoute request")
	allRoute, err := e.RouteDataService.FindAllRoute()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理下格式
	for _, v := range allRoute {
		//创建实例
		routeInfo := &route.RouteInfo{}
		//把查询出来的数据进行转化
		if err := common.SwapTo(v, routeInfo); err != nil {
			common.Error(err)
			return err
		}
		//数据合并
		rsp.RouteInfo = append(rsp.RouteInfo, routeInfo)
	}
	return nil
}

C:\Users\Administrator\Desktop\gopaas\route\main.go

package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/route/domain/repository"

	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	service2 "github.com/yunixiangfeng/gopaas/route/domain/service"
	"github.com/yunixiangfeng/gopaas/route/handler"

	//hystrix2 "github.com/yunixiangfeng/gopaas/route/plugin/hystrix"
	"strconv"

	route "github.com/yunixiangfeng/gopaas/route/proto/route"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8085"

	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	//hystrixPort = 9095
	//监控端口,每个服务不能重复
	prometheusPort = 9195
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	//3.使用配置中心连接 mysql
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	//初始化数据库
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		//命令行输出下,方便查看错误
		fmt.Println(err)
		common.Fatal(err)
	}
	defer db.Close()
	//禁止复表
	db.SingularTable(true)

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.route", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//添加熔断器,作为客户端需要启用
	//hystrixStreamHandler := hystrix.NewStreamHandler()
	//hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	//go func() {
	//	//http://192.168.204.130:9092/turbine/turbine.stream
	//	//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
	//	err = http.ListenAndServe(net.JoinHostPort("0.0.0.0",strconv.Itoa(hystrixPort)),hystrixStreamHandler)
	//	if err !=nil {
	//		common.Error(err)
	//	}
	//}()

	//5.添加监控
	common.PrometheusBoot(prometheusPort)

	//下载kubectl:https://kubernetes.io/docs/tasks/tools/#tabset-2
	//macos:
	// 1.curl -LO "https://dl.k8s.io/release/v1.21.0/bin/darwin/amd64/kubectl"
	// 2.chmod +x ./kubectl
	// 3.sudo mv ./kubectl /usr/local/bin/kubectl
	//   sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//6.创建k8s连接
	//在集群外面连接
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中外的配置
	//config, err := rest.InClusterConfig()
	//if err != nil {
	//	panic(err.Error())
	//}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.route"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用
		//micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
	)

	service.Init()

	//只能执行一遍
	err = repository.NewRouteRepository(db).InitTable()
	if err != nil {
		common.Fatal(err)
	}

	// 注册句柄,可以快速操作已开发的服务
	routeDataService := service2.NewRouteDataService(repository.NewRouteRepository(db), clientset)
	route.RegisterRouteHandler(service.Server(), &handler.RouteHandler{RouteDataService: routeDataService})

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

7-5 route 对外API的开发

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe createApi github.com/yunixiangfeng/gopaas/routeApi

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/routeApi/routeApi.proto

go mod tidy

C:\Users\Administrator\Desktop\gopaas\routeapi\main.go

package main

import (
	"fmt"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	"github.com/asim/go-micro/plugins/wrapper/select/roundrobin/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/yunixiangfeng/gopaas/common"
	go_micro_service_route "github.com/yunixiangfeng/gopaas/route/proto/route"

	"net"
	"net/http"
	"strconv"

	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/gopaas/routeApi/handler"
	hystrix2 "github.com/yunixiangfeng/gopaas/routeApi/plugin/hystrix"

	routeApi "github.com/yunixiangfeng/gopaas/routeApi/proto/routeApi"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8086"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	hystrixPort = 9096
	//监控端口,每个服务不能重复
	prometheusPort = 9196
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})

	//2.添加链路追踪
	t, io, err := common.NewTracer("go.micro.api.routeApi", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//3.添加熔断器
	hystrixStreamHandler := hystrix.NewStreamHandler()
	hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	go func() {
		//http://192.168.204.130:9092/turbine/turbine.stream
		//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
		err = http.ListenAndServe(net.JoinHostPort("0.0.0.0", strconv.Itoa(hystrixPort)), hystrixStreamHandler)
		if err != nil {
			common.Error(err)
		}
	}()

	//4.添加监控
	common.PrometheusBoot(prometheusPort)

	//5.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(opts *server.Options) {
			opts.Advertise = serviceHost + ":" + servicePort

		})),
		micro.Name("go.micro.api.routeApi"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用
		micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
		//增加负载均衡
		micro.WrapClient(roundrobin.NewClientWrapper()),
	)

	service.Init()

	// 指定需要访问的服务,可以快速操作已开发的服务,
	// 默认API服务名称带有"Api",程序会自动替换
	// 如果不带有特定字符会使用默认"XXX" 请自行替换
	routeService := go_micro_service_route.NewRouteService("go.micro.service.route", service.Client())
	// 注册控制器
	if err := routeApi.RegisterRouteApiHandler(service.Server(), &handler.RouteApi{RouteService: routeService}); err != nil {
		common.Error(err)
	}

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

C:\Users\Administrator\Desktop\gopaas\routeapi\handler\routeApiHandler.go

package handler

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	route "github.com/yunixiangfeng/gopaas/route/proto/route"
	routeApi "github.com/yunixiangfeng/gopaas/routeApi/proto/routeApi"
)

type RouteApi struct {
	RouteService route.RouteService
}

// routeApi.FindRouteById 通过API向外暴露为/routeApi/findRouteById,接收http请求
// 即:/routeApi/FindRouteById 请求会调用go.micro.api.routeApi 服务的routeApi.FindRouteById 方法
func (e *RouteApi) FindRouteById(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.FindRouteById request")
	if _, ok := req.Get["route_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 route id
	routeIdString := req.Get["route_id"].Values[0]
	routeId, err := strconv.ParseInt(routeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//获取route信息
	routeInfo, err := e.RouteService.FindRouteByID(ctx, &route.RouteId{
		Id: routeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	//返回route结果
	rsp.StatusCode = 200
	b, _ := json.Marshal(routeInfo)
	rsp.Body = string(b)
	return nil
}

// routeApi.AddRoute 通过API向外暴露为/routeApi/AddRoute,接收http请求
// 即:/routeApi/AddRoute 请求会调用go.micro.api.routeApi 服务的routeApi.AddRoute 方法
func (e *RouteApi) AddRoute(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.AddRoute request")
	addRouteInfo := &route.RouteInfo{}
	routePathName, ok := req.Post["route_path_name"]
	if ok && len(routePathName.Values) > 0 {
		port, err := strconv.ParseInt(req.Post["route_backend_service_port"].Values[0], 10, 32)
		if err != nil {
			common.Error(err)
			return err
		}
		//这里如果有多个Path需要处理多个
		routePath := &route.RoutePath{
			RoutePathName:           req.Post["route_path_name"].Values[0],
			RouteBackendService:     req.Post["route_backend_service"].Values[0],
			RouteBackendServicePort: int32(port),
		}
		//合并
		addRouteInfo.RoutePath = append(addRouteInfo.RoutePath, routePath)
	}
	form.FormToSvcStruct(req.Post, addRouteInfo)
	response, err := e.RouteService.AddRoute(ctx, addRouteInfo)
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// routeApi.DeleteRouteById 通过API向外暴露为/routeApi/DeleteRouteById,接收http请求
// 即:/routeApi/DeleteRouteById 请求会调用go.micro.api.routeApi 服务的 routeApi.DeleteRouteById 方法
func (e *RouteApi) DeleteRouteById(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.DeleteRouteById request")
	if _, ok := req.Get["route_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 route id
	routeIdString := req.Get["route_id"].Values[0]
	routeId, err := strconv.ParseInt(routeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//调用route 删除服务
	response, err := e.RouteService.DeleteRoute(ctx, &route.RouteId{
		Id: routeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// routeApi.UpdateRoute 通过API向外暴露为/routeApi/UpdateRoute,接收http请求
// 即:/routeApi/UpdateRoute 请求会调用go.micro.api.routeApi 服务的routeApi.UpdateRoute 方法
func (e *RouteApi) UpdateRoute(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.UpdateRoute request")
	rsp.StatusCode = 200
	b, _ := json.Marshal("{success:'成功访问/routeApi/UpdateRoute'}")
	rsp.Body = string(b)
	return nil
}

// 默认的方法routeApi.Call 通过API向外暴露为/routeApi/call,接收http请求
// 即:/routeApi/call或/routeApi/ 请求会调用go.micro.api.routeApi 服务的routeApi.FindRouteById 方法
func (e *RouteApi) Call(ctx context.Context, req *routeApi.Request, rsp *routeApi.Response) error {
	log.Info("Received routeApi.Call request")
	allRoute, err := e.RouteService.FindAllRoute(ctx, &route.FindAll{})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(allRoute)
	rsp.Body = string(b)
	return nil
}

C:\Users\Administrator\Desktop\gopaas\routeapi\plugin\form\form.go

package form

import (
	"errors"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/routeApi/proto/routeApi"
)

//根据结构体中name标签映射数据到结构体中并且转换类型
func FormToSvcStruct(data map[string]*routeApi.Pair, obj interface{}) {
	objValue := reflect.ValueOf(obj).Elem()
	for i := 0; i < objValue.NumField(); i++ {
		//获取sql对应的值
		dataTag := strings.Replace(objValue.Type().Field(i).Tag.Get("json"), ",omitempty", "", -1)
		dataSlice, ok := data[dataTag]
		if !ok {
			continue
		}
		valueSlice := dataSlice.Values
		if len(valueSlice) <= 0 {
			continue
		}
		//排除port和env
		if dataTag == "route_path" {
			continue
		}
		value := valueSlice[0]
		//端口,环境变量的单独处理
		//获取对应字段的名称
		name := objValue.Type().Field(i).Name
		//获取对应字段类型
		structFieldType := objValue.Field(i).Type()
		//获取变量类型,也可以直接写"string类型"
		val := reflect.ValueOf(value)
		var err error
		if structFieldType != val.Type() {
			//类型转换
			val, err = TypeConversion(value, structFieldType.Name()) //类型转换
			if err != nil {
				common.Error(err)
			}
		}
		//设置类型值
		objValue.FieldByName(name).Set(val)
	}
}

//类型转换
func TypeConversion(value string, ntype string) (reflect.Value, error) {
	if ntype == "string" {
		return reflect.ValueOf(value), nil
	} else if ntype == "time.Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "int" {
		i, err := strconv.Atoi(value)
		return reflect.ValueOf(i), err
	} else if ntype == "int32" {
		i, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return reflect.ValueOf(int32(i)), err
		}
		return reflect.ValueOf(int32(i)), err
	} else if ntype == "int64" {
		i, err := strconv.ParseInt(value, 10, 64)
		return reflect.ValueOf(i), err
	} else if ntype == "float32" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(float32(i)), err
	} else if ntype == "float64" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(i), err
	}

	//else if .......增加其他一些类型的转换

	return reflect.ValueOf(value), errors.New("未知的类型:" + ntype)
}

7-6 在k8s 中创建 nginx-controller 资源对象

C:\Users\Administrator\Desktop\gopaas\ingress\deploy.yml

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  name: default
---
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
  namespace: default
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - configmaps
  - pods
  - secrets
  - endpoints
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resourceNames:
  - ingress-controller-leader
  resources:
  - configmaps
  verbs:
  - get
  - update
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  - endpoints
  - nodes
  - pods
  - secrets
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
rules:
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingwebhookconfigurations
  verbs:
  - get
  - update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: default
---
apiVersion: v1
data:
  allow-snippet-annotations: "true"
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller
  namespace: default
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller
  namespace: default
spec:
  #externalTrafficPolicy: Local
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
#  type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller-admission
  namespace: default
spec:
  ports:
  - appProtocol: https
    name: https-webhook
    port: 443
    targetPort: webhook
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: ClusterIP
---
apiVersion: apps/v1
#kind: Deployment
kind: DaemonSet
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-controller
  namespace: default
spec:
  minReadySeconds: 0
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/name: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/component: controller
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
        - --election-id=ingress-controller-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: LD_PRELOAD
          value: /usr/local/lib/libmimalloc.so
        image: k8s.gcr.io/ingress-nginx/controller:v1.2.0@sha256:d8196e3bc1e72547c5dec66d6556c0ff92a23f6d0919b206be170bc90d5f9185
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - /wait-shutdown
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: controller
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        - containerPort: 443
          name: https
          protocol: TCP
        - containerPort: 8443
          name: webhook
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          requests:
            cpu: 100m
            memory: 90Mi
        securityContext:
          allowPrivilegeEscalation: true
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - ALL
          runAsUser: 101
        volumeMounts:
        - mountPath: /usr/local/certificates/
          name: webhook-cert
          readOnly: true
#      dnsPolicy: ClusterFirst
      dnsPolicy: ClusterFirstWithHostNet
      hostNetwork: true
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
      - name: webhook-cert
        secret:
          secretName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission-create
  namespace: default
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.2.0
      name: ingress-nginx-admission-create
    spec:
      containers:
      - args:
        - create
        - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
        - --namespace=$(POD_NAMESPACE)
        - --secret-name=ingress-nginx-admission
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
        imagePullPolicy: IfNotPresent
        name: create
        securityContext:
          allowPrivilegeEscalation: false
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      securityContext:
        fsGroup: 2000
        runAsNonRoot: true
        runAsUser: 2000
      serviceAccountName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission-patch
  namespace: default
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.2.0
      name: ingress-nginx-admission-patch
    spec:
      containers:
      - args:
        - patch
        - --webhook-name=ingress-nginx-admission
        - --namespace=$(POD_NAMESPACE)
        - --patch-mutating=false
        - --secret-name=ingress-nginx-admission
        - --patch-failure-policy=Fail
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
        imagePullPolicy: IfNotPresent
        name: patch
        securityContext:
          allowPrivilegeEscalation: false
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      securityContext:
        fsGroup: 2000
        runAsNonRoot: true
        runAsUser: 2000
      serviceAccountName: ingress-nginx-admission
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: nginx
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.2.0
  name: ingress-nginx-admission
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: ingress-nginx-controller-admission
      namespace: default
      path: /networking/v1/ingresses
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: validate.nginx.ingress.kubernetes.io
  rules:
  - apiGroups:
    - networking.k8s.io
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - ingresses
  sideEffects: None
#### 在master节点执行命令
kubectl apply -f deploy.yml

[root@k8s-master01 k8s]# kubectl apply -f ingress-deploy.yml
[root@k8s-master01 k8s]# kubectl get svc
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
go-test                              ClusterIP   10.100.201.177   <none>        9099/TCP         13h
ingress-nginx-controller             ClusterIP   10.106.55.124    <none>        80/TCP,443/TCP   21s
ingress-nginx-controller-admission   ClusterIP   10.111.49.109    <none>        443/TCP          21s
kubernetes                           ClusterIP   10.96.0.1        <none>        443/TCP          94d
[root@k8s-master01 k8s]# kubectl get daemonset
NAME                       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
ingress-nginx-controller   2         2         0       2            0           kubernetes.io/os=linux   2m51s

7-7 route 前端管理页面及效果演示

C:\Users\Administrator\Desktop\gopaas\go-paas-front\route-create.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" href="assets/img/logo-fav.png">
    <title>CPaaS</title>
    <link rel="stylesheet" type="text/css" href="assets/lib/perfect-scrollbar/css/perfect-scrollbar.min.css"/>
    <link rel="stylesheet" type="text/css" href="assets/lib/material-design-icons/css/material-design-iconic-font.min.css"/><!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="assets/css/style.css" type="text/css"/>
  </head>
  <body>
    <div class="be-wrapper">
      <nav class="navbar navbar-default navbar-fixed-top be-top-header">
        <div class="container-fluid">
          <div class="navbar-header"><a href="index.html" class="navbar-brand"></a>
          </div>
          <div class="be-right-navbar">
            <ul class="nav navbar-nav navbar-right be-user-nav">
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><img src="assets/img/avatar.png" alt="Avatar"><span class="user-name">Túpac Amaru</span></a>
                <ul role="menu" class="dropdown-menu">
                  <li>
                    <div class="user-info">
                      <div class="user-name">Túpac Amaru</div>
                      <div class="user-position online">Available</div>
                    </div>
                  </li>
                  <li><a href="#"><span class="icon mdi mdi-face"></span> Account</a></li>
                  <li><a href="#"><span class="icon mdi mdi-settings"></span> Settings</a></li>
                  <li><a href="#"><span class="icon mdi mdi-power"></span> Logout</a></li>
                </ul>
              </li>
            </ul>
            <div class="page-title"><span>Form Validation</span></div>
            <ul class="nav navbar-nav navbar-right be-icons-nav">
              <li class="dropdown"><a href="#" role="button" aria-expanded="false" class="be-toggle-right-sidebar"><span class="icon mdi mdi-settings"></span></a></li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-notifications"></span><span class="indicator"></span></a>
                <ul class="dropdown-menu be-notifications">
                  <li>
                    <div class="title">Notifications<span class="badge">3</span></div>
                    <div class="list">
                      <div class="be-scroller">
                        <div class="content">
                          <ul>
                            <li class="notification notification-unread"><a href="#">
                                <div class="image"><img src="assets/img/avatar2.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Jessica Caruso</span> accepted your invitation to join the team.</div><span class="date">2 min ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar3.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Joel King</span> is now following you</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar4.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">John Doe</span> is watching your main repository</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar5.png" alt="Avatar"></div>
                                <div class="notification-info"><span class="text"><span class="user-name">Emily Carter</span> is now following you</span><span class="date">5 days ago</span></div></a></li>
                          </ul>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">View all notifications</a></div>
                  </li>
                </ul>
              </li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-apps"></span></a>
                <ul class="dropdown-menu be-connections">
                  <li>
                    <div class="list">
                      <div class="content">
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/github.png" alt="Github"><span>GitHub</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/bitbucket.png" alt="Bitbucket"><span>Bitbucket</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/slack.png" alt="Slack"><span>Slack</span></a></div>
                        </div>
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dribbble.png" alt="Dribbble"><span>Dribbble</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/mail_chimp.png" alt="Mail Chimp"><span>Mail Chimp</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dropbox.png" alt="Dropbox"><span>Dropbox</span></a></div>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">More</a></div>
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </nav>
<!--
  侧边栏-开始
-->
      <div class="be-left-sidebar">
        <div class="left-sidebar-wrapper"><a href="#" class="left-sidebar-toggle">看版</a>
          <div class="left-sidebar-spacer">
            <div class="left-sidebar-scroll">
              <div class="left-sidebar-content">
                <ul class="sidebar-elements">
                  <li class="divider">菜单</li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-home"></i><span>控制台</span></a>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-face"></i><span>应用管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="pod-index.html">添加应用</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="charts.html"><i class="icon mdi mdi-chart-donut"></i><span>服务管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="svc-index.html">添加服务</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-dot-circle"></i><span>域名管理</span></a>
                    <ul class="sub-menu">
                      <li class="active"><a href="route-index.html">添加路由</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-border-all"></i><span>中间件</span></a>
                    <ul class="sub-menu">
                      <li><a href="tables-general.html">General</a>
                      </li>
                      <li><a href="tables-datatables.html">Data Tables</a>
                      </li>
                      <li><a href="tables-filters.html"><span class="label label-primary pull-right">New</span>Table Filters</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-layers"></i><span>应用市场</span></a>
                    <ul class="sub-menu">
                      <li><a href="pages-blank.html">Blank Page</a>
                      </li>
                      <li><a href="pages-blank-header.html">Blank Page Header</a>
                      </li>
                      <li><a href="pages-login.html">Login</a>
                      </li>
                      <li><a href="pages-login2.html">Login v2</a>
                      </li>
                      <li><a href="pages-404.html">404 Page</a>
                      </li>
                      <li><a href="pages-sign-up.html">Sign Up</a>
                      </li>
                      <li><a href="pages-forgot-password.html">Forgot Password</a>
                      </li>
                      <li><a href="pages-profile.html">Profile</a>
                      </li>
                      <li><a href="pages-pricing-tables.html">Pricing Tables</a>
                      </li>
                      <li><a href="pages-pricing-tables2.html">Pricing Tables v2</a>
                      </li>
                      <li><a href="pages-timeline.html">Timeline</a>
                      </li>
                      <li><a href="pages-timeline2.html">Timeline v2</a>
                      </li>
                      <li><a href="pages-invoice.html"><span class="label label-primary pull-right">New</span>Invoice</a>
                      </li>
                      <li><a href="pages-calendar.html">Calendar</a>
                      </li>
                      <li><a href="pages-gallery.html">Gallery</a>
                      </li>
                      <li><a href="pages-code-editor.html"><span class="label label-primary pull-right">New    </span>Code Editor</a>
                      </li>
                      <li><a href="pages-booking.html"><span class="label label-primary pull-right">New</span>Booking</a>
                      </li>
                      <li><a href="pages-loaders.html"><span class="label label-primary pull-right">New</span>Loaders</a>
                      </li>
                    </ul>
                  </li>

                </ul>
              </div>
            </div>
          </div>
          <div class="progress-widget">
            <div class="progress-data"><span class="progress-value">60%</span><span class="name">Current Project</span></div>
            <div class="progress">
              <div style="width: 60%;" class="progress-bar progress-bar-primary"></div>
            </div>
          </div>
        </div>
      </div>
<!--
侧边栏-结束
-->
      <div class="be-content">
        <div class="main-content container-fluid">
          <div class="row">
            <div class="col-md-12">
              <div class="panel panel-default panel-border-color panel-border-color-primary">
                <div class="panel-heading panel-heading-divider">添加路由<span class="panel-subtitle"></span></div>
                <div class="panel-body">
                  <form action="http://localhost:8080/routeApi/addRoute" class="form-horizontal group-border-dashed" method="post" >
                    <div class="form-group">
                      <label class="col-sm-3 control-label">路由名称:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_name"  name="route_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">命名空间:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_namespace"  name="route_namespace" value="default">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置域名:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_host"  name="route_host"  >
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置路径:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_path_name" id="route_path_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id=""  name="route_backend_service" id="route_backend_service">
                      </div>
                    </div>

                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务端口:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_backend_service_port" id="route_backend_service_port">
                      </div>
                    </div>

                    <div class="form-group">
                      <div class="col-sm-2 col-sm-10">
                        <button type="submit" class="btn btn-space btn-primary">添加路由</button>
                        <button class="btn btn-space btn-default">Cancel</button>
                      </div>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
    </div>
    <script src="assets/lib/jquery/jquery.min.js" type="text/javascript"></script>
    <script src="assets/lib/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" type="text/javascript"></script>
    <script src="assets/js/main.js" type="text/javascript"></script>
    <script src="assets/lib/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/parsley/parsley.min.js" type="text/javascript"></script>
 
        <script type="text/javascript">
          $(document).ready(function(){
          App.init();
          });
        
    //获取url中的参数
    function getUrlParam(name) {
     var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
     var r = window.location.search.substr(1).match(reg); //匹配目标参数
     if (r != null) return unescape(r[2]); return null; //返回参数值
    }
     
        </script>
    
  </body>
</html>

C:\Users\Administrator\Desktop\gopaas\go-paas-front\route-detail.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" href="assets/img/logo-fav.png">
    <title>CPaaS</title>
    <link rel="stylesheet" type="text/css" href="assets/lib/perfect-scrollbar/css/perfect-scrollbar.min.css"/>
    <link rel="stylesheet" type="text/css" href="assets/lib/material-design-icons/css/material-design-iconic-font.min.css"/><!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="assets/css/style.css" type="text/css"/>
  </head>
  <body>
    <div class="be-wrapper">
      <nav class="navbar navbar-default navbar-fixed-top be-top-header">
        <div class="container-fluid">
          <div class="navbar-header"><a href="index.html" class="navbar-brand"></a>
          </div>
          <div class="be-right-navbar">
            <ul class="nav navbar-nav navbar-right be-user-nav">
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><img src="assets/img/avatar.png" alt="Avatar"><span class="user-name">Túpac Amaru</span></a>
                <ul role="menu" class="dropdown-menu">
                  <li>
                    <div class="user-info">
                      <div class="user-name">Túpac Amaru</div>
                      <div class="user-position online">Available</div>
                    </div>
                  </li>
                  <li><a href="#"><span class="icon mdi mdi-face"></span> Account</a></li>
                  <li><a href="#"><span class="icon mdi mdi-settings"></span> Settings</a></li>
                  <li><a href="#"><span class="icon mdi mdi-power"></span> Logout</a></li>
                </ul>
              </li>
            </ul>
            <div class="page-title"><span>Form Validation</span></div>
            <ul class="nav navbar-nav navbar-right be-icons-nav">
              <li class="dropdown"><a href="#" role="button" aria-expanded="false" class="be-toggle-right-sidebar"><span class="icon mdi mdi-settings"></span></a></li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-notifications"></span><span class="indicator"></span></a>
                <ul class="dropdown-menu be-notifications">
                  <li>
                    <div class="title">Notifications<span class="badge">3</span></div>
                    <div class="list">
                      <div class="be-scroller">
                        <div class="content">
                          <ul>
                            <li class="notification notification-unread"><a href="#">
                                <div class="image"><img src="assets/img/avatar2.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Jessica Caruso</span> accepted your invitation to join the team.</div><span class="date">2 min ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar3.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Joel King</span> is now following you</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar4.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">John Doe</span> is watching your main repository</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar5.png" alt="Avatar"></div>
                                <div class="notification-info"><span class="text"><span class="user-name">Emily Carter</span> is now following you</span><span class="date">5 days ago</span></div></a></li>
                          </ul>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">View all notifications</a></div>
                  </li>
                </ul>
              </li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-apps"></span></a>
                <ul class="dropdown-menu be-connections">
                  <li>
                    <div class="list">
                      <div class="content">
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/github.png" alt="Github"><span>GitHub</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/bitbucket.png" alt="Bitbucket"><span>Bitbucket</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/slack.png" alt="Slack"><span>Slack</span></a></div>
                        </div>
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dribbble.png" alt="Dribbble"><span>Dribbble</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/mail_chimp.png" alt="Mail Chimp"><span>Mail Chimp</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dropbox.png" alt="Dropbox"><span>Dropbox</span></a></div>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">More</a></div>
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </nav>
<!--
  侧边栏-开始
-->
      <div class="be-left-sidebar">
        <div class="left-sidebar-wrapper"><a href="#" class="left-sidebar-toggle">看版</a>
          <div class="left-sidebar-spacer">
            <div class="left-sidebar-scroll">
              <div class="left-sidebar-content">
                <ul class="sidebar-elements">
                  <li class="divider">菜单</li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-home"></i><span>控制台</span></a>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-face"></i><span>应用管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="pod-index.html">添加应用</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="charts.html"><i class="icon mdi mdi-chart-donut"></i><span>服务管理</span></a>
                    <ul class="sub-menu">
                      <li ><a href="svc-index.html">添加服务</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-dot-circle"></i><span>域名管理</span></a>
                    <ul class="sub-menu">
                      <li class="active"><a href="route-index.html">添加路由</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-border-all"></i><span>中间件</span></a>
                    <ul class="sub-menu">
                      <li><a href="tables-general.html">General</a>
                      </li>
                      <li><a href="tables-datatables.html">Data Tables</a>
                      </li>
                      <li><a href="tables-filters.html"><span class="label label-primary pull-right">New</span>Table Filters</a>
                      </li>
                    </ul>
                  </li>
                  <li class="parent"><a href="#"><i class="icon mdi mdi-layers"></i><span>应用市场</span></a>
                    <ul class="sub-menu">
                      <li><a href="pages-blank.html">Blank Page</a>
                      </li>
                      <li><a href="pages-blank-header.html">Blank Page Header</a>
                      </li>
                      <li><a href="pages-login.html">Login</a>
                      </li>
                      <li><a href="pages-login2.html">Login v2</a>
                      </li>
                      <li><a href="pages-404.html">404 Page</a>
                      </li>
                      <li><a href="pages-sign-up.html">Sign Up</a>
                      </li>
                      <li><a href="pages-forgot-password.html">Forgot Password</a>
                      </li>
                      <li><a href="pages-profile.html">Profile</a>
                      </li>
                      <li><a href="pages-pricing-tables.html">Pricing Tables</a>
                      </li>
                      <li><a href="pages-pricing-tables2.html">Pricing Tables v2</a>
                      </li>
                      <li><a href="pages-timeline.html">Timeline</a>
                      </li>
                      <li><a href="pages-timeline2.html">Timeline v2</a>
                      </li>
                      <li><a href="pages-invoice.html"><span class="label label-primary pull-right">New</span>Invoice</a>
                      </li>
                      <li><a href="pages-calendar.html">Calendar</a>
                      </li>
                      <li><a href="pages-gallery.html">Gallery</a>
                      </li>
                      <li><a href="pages-code-editor.html"><span class="label label-primary pull-right">New    </span>Code Editor</a>
                      </li>
                      <li><a href="pages-booking.html"><span class="label label-primary pull-right">New</span>Booking</a>
                      </li>
                      <li><a href="pages-loaders.html"><span class="label label-primary pull-right">New</span>Loaders</a>
                      </li>
                    </ul>
                  </li>

                </ul>
              </div>
            </div>
          </div>
          <div class="progress-widget">
            <div class="progress-data"><span class="progress-value">60%</span><span class="name">Current Project</span></div>
            <div class="progress">
              <div style="width: 60%;" class="progress-bar progress-bar-primary"></div>
            </div>
          </div>
        </div>
      </div>
<!--
侧边栏-结束
-->
      <div class="be-content">
        <div class="main-content container-fluid">
          <div class="row">
            <div class="col-md-12">
              <div class="panel panel-default panel-border-color panel-border-color-primary">
                <div class="panel-heading panel-heading-divider">路由详情<span class="panel-subtitle">查看路由详情信息</span></div>
                <div class="panel-body">
                  <form action="#" class="form-horizontal group-border-dashed">
                    <div class="form-group">
                      <label class="col-sm-3 control-label">路由ID:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="route_id" name="route_id">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">路由名称:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="route_name"  name="route_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">命名空间:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="route_namespace"  name="route_namespace">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置域名:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="route_host"  name="route_host"  >
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">设置路径:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_path_name" id="route_path_name">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"   name="route_backend_service" id="route_backend_service">
                      </div>
                    </div>

                    <div class="form-group">
                      <label class="col-sm-3 control-label">后端服务端口:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="route_backend_service_port" id="route_backend_service_port">
                      </div>
                    </div>



                  </form>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
    </div>
    <script src="assets/lib/jquery/jquery.min.js" type="text/javascript"></script>
    <script src="assets/lib/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" type="text/javascript"></script>
    <script src="assets/js/main.js" type="text/javascript"></script>
    <script src="assets/lib/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/parsley/parsley.min.js" type="text/javascript"></script>
        <script type="text/javascript">
          $(document).ready(function(){
            App.init();
      	   $('form').parsley();
          $.ajax({
              type:"get",
              url:"http://127.0.0.1:8080/routeApi/findRouteById?route_id="+getUrlParam('route_id'),
              success: function(data){
                console.log(data);
                if(data.id != "" || data.id != null || data.id != undefined){
                  $('#route_id').val(data.id);
                  $("#route_name").val(data.route_name);
                  $('#route_namespace').val(data.route_namespace);
                  $('#route_host').val(data.route_host);
                  //这里有多个要循环
                  $('#route_path_name').val(data.route_path[0]['route_path_name']);
                  $('#route_backend_service').val(data.route_path[0]['route_backend_service']);
                  $('#route_backend_service_port').val(data.route_path[0]['route_backend_service_port']);

                }else{
                  console.log(data);
                }
              },
              error: function(result){
                console.log(result);
              }
            });
          });
        
    //获取url中的参数
    function getUrlParam(name) {
     var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
     var r = window.location.search.substr(1).match(reg); //匹配目标参数
     if (r != null) return unescape(r[2]); return null; //返回参数值
    }
     
        </script>
    
  </body>
</html>

C:\Users\Administrator\Desktop\gopaas\go-paas-front\route-index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="shortcut icon" href="assets/img/logo-fav.png">
    <title>CPaaS</title>
    <link rel="stylesheet" type="text/css" href="assets/lib/perfect-scrollbar/css/perfect-scrollbar.min.css"/>
    <link rel="stylesheet" type="text/css" href="assets/lib/material-design-icons/css/material-design-iconic-font.min.css"/><!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" type="text/css" href="assets/lib/datatables/css/dataTables.bootstrap.min.css"/>
    <link rel="stylesheet" href="assets/css/style.css" type="text/css"/>
  </head>
  <body>
    <div class="be-wrapper">
      <nav class="navbar navbar-default navbar-fixed-top be-top-header">
        <div class="container-fluid">
          <div class="navbar-header"><a href="index.html" class="navbar-brand"></a>
          </div>
          <div class="be-right-navbar">
            <ul class="nav navbar-nav navbar-right be-user-nav">
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><img src="assets/img/avatar.png" alt="Avatar"><span class="user-name">Túpac Amaru</span></a>
                <ul role="menu" class="dropdown-menu">
                  <li>
                    <div class="user-info">
                      <div class="user-name">wu123</div>
                      <div class="user-position online">在线</div>
                    </div>
                  </li>
                  <li><a href="#"><span class="icon mdi mdi-face"></span> 账户</a></li>
                  <li><a href="#"><span class="icon mdi mdi-settings"></span> 设置</a></li>
                  <li><a href="#"><span class="icon mdi mdi-power"></span> 推出登录</a></li>
                </ul>
              </li>
            </ul>
            <div class="page-title"></div>
            <ul class="nav navbar-nav navbar-right be-icons-nav">
              <li class="dropdown"><a href="#" role="button" aria-expanded="false" class="be-toggle-right-sidebar"><span class="icon mdi mdi-settings"></span></a></li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-notifications"></span><span class="indicator"></span></a>
                <ul class="dropdown-menu be-notifications">
                  <li>
                    <div class="title">消息提醒<span class="badge">3</span></div>
                    <div class="list">
                      <div class="be-scroller">
                        <div class="content">
                          <ul>
                            <li class="notification notification-unread"><a href="#">
                                <div class="image"><img src="assets/img/avatar2.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Jessica Caruso</span> accepted your invitation to join the team.</div><span class="date">2 min ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar3.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">Joel King</span> is now following you</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar4.png" alt="Avatar"></div>
                                <div class="notification-info">
                                  <div class="text"><span class="user-name">John Doe</span> is watching your main repository</div><span class="date">2 days ago</span>
                                </div></a></li>
                            <li class="notification"><a href="#">
                                <div class="image"><img src="assets/img/avatar5.png" alt="Avatar"></div>
                                <div class="notification-info"><span class="text"><span class="user-name">Emily Carter</span> is now following you</span><span class="date">5 days ago</span></div></a></li>
                          </ul>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">View all notifications</a></div>
                  </li>
                </ul>
              </li>
              <li class="dropdown"><a href="#" data-toggle="dropdown" role="button" aria-expanded="false" class="dropdown-toggle"><span class="icon mdi mdi-apps"></span></a>
                <ul class="dropdown-menu be-connections">
                  <li>
                    <div class="list">
                      <div class="content">
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/github.png" alt="Github"><span>GitHub</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/bitbucket.png" alt="Bitbucket"><span>Bitbucket</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/slack.png" alt="Slack"><span>Slack</span></a></div>
                        </div>
                        <div class="row">
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dribbble.png" alt="Dribbble"><span>Dribbble</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/mail_chimp.png" alt="Mail Chimp"><span>Mail Chimp</span></a></div>
                          <div class="col-xs-4"><a href="#" class="connection-item"><img src="assets/img/dropbox.png" alt="Dropbox"><span>Dropbox</span></a></div>
                        </div>
                      </div>
                    </div>
                    <div class="footer"> <a href="#">More</a></div>
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </nav>
<!--
  侧边栏-开始
-->
<div class="be-left-sidebar">
  <div class="left-sidebar-wrapper"><a href="#" class="left-sidebar-toggle">看版</a>
    <div class="left-sidebar-spacer">
      <div class="left-sidebar-scroll">
        <div class="left-sidebar-content">
          <ul class="sidebar-elements">
            <li class="divider">菜单</li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-home"></i><span>控制台</span></a>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-face"></i><span>应用管理</span></a>
              <ul class="sub-menu">
                <li ><a href="pod-index.html">添加应用</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="charts.html"><i class="icon mdi mdi-chart-donut"></i><span>服务管理</span></a>
              <ul class="sub-menu">
                <li ><a href="svc-index.html">添加服务</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-dot-circle"></i><span>域名管理</span></a>
              <ul class="sub-menu">
                <li class="active"><a href="route-index.html">添加路由</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-border-all"></i><span>中间件</span></a>
              <ul class="sub-menu">
                <li><a href="tables-general.html">General</a>
                </li>
                <li><a href="tables-datatables.html">Data Tables</a>
                </li>
                <li><a href="tables-filters.html"><span class="label label-primary pull-right">New</span>Table Filters</a>
                </li>
              </ul>
            </li>
            <li class="parent"><a href="#"><i class="icon mdi mdi-layers"></i><span>应用市场</span></a>
              <ul class="sub-menu">
                <li><a href="pages-blank.html">Blank Page</a>
                </li>
                <li><a href="pages-blank-header.html">Blank Page Header</a>
                </li>
                <li><a href="pages-login.html">Login</a>
                </li>
                <li><a href="pages-login2.html">Login v2</a>
                </li>
                <li><a href="pages-404.html">404 Page</a>
                </li>
                <li><a href="pages-sign-up.html">Sign Up</a>
                </li>
                <li><a href="pages-forgot-password.html">Forgot Password</a>
                </li>
                <li><a href="pages-profile.html">Profile</a>
                </li>
                <li><a href="pages-pricing-tables.html">Pricing Tables</a>
                </li>
                <li><a href="pages-pricing-tables2.html">Pricing Tables v2</a>
                </li>
                <li><a href="pages-timeline.html">Timeline</a>
                </li>
                <li><a href="pages-timeline2.html">Timeline v2</a>
                </li>
                <li><a href="pages-invoice.html"><span class="label label-primary pull-right">New</span>Invoice</a>
                </li>
                <li><a href="pages-calendar.html">Calendar</a>
                </li>
                <li><a href="pages-gallery.html">Gallery</a>
                </li>
                <li><a href="pages-code-editor.html"><span class="label label-primary pull-right">New    </span>Code Editor</a>
                </li>
                <li><a href="pages-booking.html"><span class="label label-primary pull-right">New</span>Booking</a>
                </li>
                <li><a href="pages-loaders.html"><span class="label label-primary pull-right">New</span>Loaders</a>
                </li>
              </ul>
            </li>
         
          </ul>
        </div>
      </div>
    </div>
    <div class="progress-widget">
      <div class="progress-data"><span class="progress-value">60%</span><span class="name">Current Project</span></div>
      <div class="progress">
        <div style="width: 60%;" class="progress-bar progress-bar-primary"></div>
      </div>
    </div>
  </div>
</div>
<!--
侧边栏-结束
-->
      <div class="be-content">
        <div class="page-head">
          <h2 class="page-head-title">路由管理</h2>
          <ol class="breadcrumb page-head-nav">
            <li><a href="#">控制台</a></li>
            <li><a href="#">路由管理</a></li>
            <li class="active">路由列表</li>
          </ol>
        </div>
        <div class="main-content container-fluid">
          <div class="row">
            <div class="col-sm-12">
              <div class="panel panel-default panel-table">
                <div class="panel-heading">
                  <a href="route-create.html"><button class="btn btn-space btn-primary">添加路由</button></a>
                  <div class="tools dropdown"><span class="icon mdi mdi-download"></span><a href="#" type="button" data-toggle="dropdown" class="dropdown-toggle"><span class="icon mdi mdi-more-vert"></span></a>
                    <ul role="menu" class="dropdown-menu pull-right">
                      <li><a href="#">Action</a></li>
                      <li><a href="#">Another action</a></li>
                      <li><a href="#">Something else here</a></li>
                      <li class="divider"></li>
                      <li><a href="#">Separated link</a></li>
                    </ul>
                  </div>
                </div>
                <div class="panel-body">
                  <table id="table1" class="table table-striped table-hover table-fw-widget">
                    <thead>
                      <tr>
                        <th>ID</th>
                        <th>域名</th>
                        <th>命名空间</th>
                        <th>路径</th>
                        <th>操作</th>
                      </tr>
                    </thead>
                    <tbody id="table-data">
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
          </div>
       
        </div>
      </div>
      <nav class="be-right-sidebar">
        <div class="sb-content">
          <div class="tab-navigation">
            <ul role="tablist" class="nav nav-tabs nav-justified">
              <li role="presentation" class="active"><a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">Chat</a></li>
              <li role="presentation"><a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">Todo</a></li>
              <li role="presentation"><a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">Settings</a></li>
            </ul>
          </div>
          <div class="tab-panel">
            <div class="tab-content">
              <div id="tab1" role="tabpanel" class="tab-pane tab-chat active">
                <div class="chat-contacts">
                  <div class="chat-sections">
                    <div class="be-scroller">
                      <div class="content">
                        <h2>Recent</h2>
                        <div class="contact-list contact-list-recent">
                          <div class="user"><a href="#"><img src="assets/img/avatar1.png" alt="Avatar">
                              <div class="user-data"><span class="status away"></span><span class="name">Claire Sassu</span><span class="message">Can you share the...</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar2.png" alt="Avatar">
                              <div class="user-data"><span class="status"></span><span class="name">Maggie jackson</span><span class="message">I confirmed the info.</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar3.png" alt="Avatar">
                              <div class="user-data"><span class="status offline"></span><span class="name">Joel King		</span><span class="message">Ready for the meeti...</span></div></a></div>
                        </div>
                        <h2>Contacts</h2>
                        <div class="contact-list">
                          <div class="user"><a href="#"><img src="assets/img/avatar4.png" alt="Avatar">
                              <div class="user-data2"><span class="status"></span><span class="name">Mike Bolthort</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar5.png" alt="Avatar">
                              <div class="user-data2"><span class="status"></span><span class="name">Maggie jackson</span></div></a></div>
                          <div class="user"><a href="#"><img src="assets/img/avatar6.png" alt="Avatar">
                              <div class="user-data2"><span class="status offline"></span><span class="name">Jhon Voltemar</span></div></a></div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="bottom-input">
                    <input type="text" placeholder="Search..." name="q"><span class="mdi mdi-search"></span>
                  </div>
                </div>
                <div class="chat-window">
                  <div class="title">
                    <div class="user"><img src="assets/img/avatar2.png" alt="Avatar">
                      <h2>Maggie jackson</h2><span>Active 1h ago</span>
                    </div><span class="icon return mdi mdi-chevron-left"></span>
                  </div>
                  <div class="chat-messages">
                    <div class="be-scroller">
                      <div class="content">
                        <ul>
                          <li class="friend">
                            <div class="msg">Hello</div>
                          </li>
                          <li class="self">
                            <div class="msg">Hi, how are you?</div>
                          </li>
                          <li class="friend">
                            <div class="msg">Good, I'll need support with my pc</div>
                          </li>
                          <li class="self">
                            <div class="msg">Sure, just tell me what is going on with your computer?</div>
                          </li>
                          <li class="friend">
                            <div class="msg">I don't know it just turns off suddenly</div>
                          </li>
                        </ul>
                      </div>
                    </div>
                  </div>
                  <div class="chat-input">
                    <div class="input-wrapper"><span class="photo mdi mdi-camera"></span>
                      <input type="text" placeholder="Message..." name="q" autocomplete="off"><span class="send-msg mdi mdi-mail-send"></span>
                    </div>
                  </div>
                </div>
              </div>
              <div id="tab2" role="tabpanel" class="tab-pane tab-todo">
                <div class="todo-container">
                  <div class="todo-wrapper">
                    <div class="be-scroller">
                      <div class="todo-content"><span class="category-title">Today</span>
                        <ul class="todo-list">
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo1" type="checkbox" checked="">
                              <label for="todo1">Initialize the project</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo2" type="checkbox">
                              <label for="todo2">Create the main structure</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo3" type="checkbox">
                              <label for="todo3">Updates changes to GitHub</label>
                            </div>
                          </li>
                        </ul><span class="category-title">Tomorrow</span>
                        <ul class="todo-list">
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo4" type="checkbox">
                              <label for="todo4">Initialize the project</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo5" type="checkbox">
                              <label for="todo5">Create the main structure</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo6" type="checkbox">
                              <label for="todo6">Updates changes to GitHub</label>
                            </div>
                          </li>
                          <li>
                            <div class="be-checkbox be-checkbox-sm"><span class="delete mdi mdi-delete"></span>
                              <input id="todo7" type="checkbox">
                              <label for="todo7" title="This task is too long to be displayed in a normal space!">This task is too long to be displayed in a normal space!</label>
                            </div>
                          </li>
                        </ul>
                      </div>
                    </div>
                  </div>
                  <div class="bottom-input">
                    <input type="text" placeholder="Create new task..." name="q"><span class="mdi mdi-plus"></span>
                  </div>
                </div>
              </div>
              <div id="tab3" role="tabpanel" class="tab-pane tab-settings">
                <div class="settings-wrapper">
                  <div class="be-scroller"><span class="category-title">General</span>
                    <ul class="settings-list">
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st1" id="st1"><span>
                            <label for="st1"></label></span>
                        </div><span class="name">Available</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st2" id="st2"><span>
                            <label for="st2"></label></span>
                        </div><span class="name">Enable notifications</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st3" id="st3"><span>
                            <label for="st3"></label></span>
                        </div><span class="name">Login with Facebook</span>
                      </li>
                    </ul><span class="category-title">Notifications</span>
                    <ul class="settings-list">
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" name="st4" id="st4"><span>
                            <label for="st4"></label></span>
                        </div><span class="name">Email notifications</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st5" id="st5"><span>
                            <label for="st5"></label></span>
                        </div><span class="name">Project updates</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" checked="" name="st6" id="st6"><span>
                            <label for="st6"></label></span>
                        </div><span class="name">New comments</span>
                      </li>
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" name="st7" id="st7"><span>
                            <label for="st7"></label></span>
                        </div><span class="name">Chat messages</span>
                      </li>
                    </ul><span class="category-title">Workflow</span>
                    <ul class="settings-list">
                      <li>
                        <div class="switch-button switch-button-sm">
                          <input type="checkbox" name="st8" id="st8"><span>
                            <label for="st8"></label></span>
                        </div><span class="name">Deploy on commit</span>
                      </li>
                    </ul>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </nav>
    </div>
    <script src="assets/lib/jquery/jquery.min.js" type="text/javascript"></script>
    <script src="assets/lib/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" type="text/javascript"></script>
    <script src="assets/js/main.js" type="text/javascript"></script>
    <script src="assets/lib/bootstrap/dist/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/js/jquery.dataTables.min.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/js/dataTables.bootstrap.min.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/dataTables.buttons.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.html5.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.flash.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.print.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.colVis.js" type="text/javascript"></script>
    <script src="assets/lib/datatables/plugins/buttons/js/buttons.bootstrap.js" type="text/javascript"></script>
    <script src="assets/js/app-tables-datatables.js" type="text/javascript"></script>
 
    <script type="text/javascript">
      $(document).ready(function(){
        
      	//initialize the javascript
      	App.init();
  
      	App.dataTables();
         
      $.ajax({
          type:"get",
          url:"http://127.0.0.1:8080/routeApi/",
          success: function(data){
            console.log(data);
            $("#table-data").html("");
            $.each(data['route_info'],function (i,item) {
              $("#table-data").append('                      <tr class="gradeA">\
                        <td>'+item.id+'</td>\
                        <td>'+item.route_name+'</td>\
                        <td>'+item.route_namespace+'</td>\
                        <td class="center">'+item.route_host+'</td>\
                        <td class="center"><a href="route-detail.html?route_id='+item.id+'">详情</a> <a href="http://localhost:8080/routeApi/deleteRouteById?route_id='+item.id+'" style="color:red;" >删除</a></td>\
                      </tr>'
              );
            })
          },
          error: function(result){
            console.log(result);
          }
        });
      });
    

 
    </script>

  </body>
</html>

7-8 总结&思考 

主要内容
介绍了ingress的以及ingress-controller的作用
说明ingress-controller的运行原理
ingress-controller (nginx)的代码功能开发

经验之谈
Ingress controller 的类型一般采用daemonset模式
路由管理要审核不然管理起来非常复杂。

Route-controller 其它模式的用法?
除去nginx以外 还有哪些是常用的类型?

7-9 【扩展阅读】Kubernetes 使用 ingress 配置 https 集群

第8章 云原生Go PaaS 平台后台监控能力建设,总览集群资源使用

集群的状态需要有强有力的全局视图,监控系统应运而生,通过强大的监控系统能够窥探整个 PaaS 的运行状态,实施反馈集群信息轻松掌握使用 Promethus 对整个 PaaS 平台应用和组件的监控能力,可以通过监控结合不同的业务逻辑适应企业多变的业务需求。

8-1 Go PaaS 平台监控系统 Prometheus 架构介绍

GO PaaS 平台监控系统建立

主要内容
主流监控 Promethus监控讲解
在 K8s 中安装监控
Promethus 监控维度效果展示

Promethus 架构介绍
监控可以监控哪些方面的信息
监控的核心组件
监控的核心架构

Go PaaS平台监控模块建立

监控的目标
发现已经存在的问题
预防即将出现的问题

监控哪些内容
系统基础指标(内存、CPU、IO、Disk、Network等)服务基础信息(存活,占用的系统资源等)
服务个性化(接口、固定返回值等) ;
日志内容(从日志中获取报错信息 );

K8s 中的监控内容
节点
服务
K8s自身的组件

如何监控?
数据采集:要先配置采集什么内容
数据存储:采集完数据之后,需要有数据库来进行存储定义报警规则:设置报警红线,满足条件进行报警
配置报警方式:设置短信,邮件,微信等报警:

(普罗米修斯)监控Prometheus
是一套开源的监控&报警&时间序列数据库的组合
由metric名称和 kv 标识的多维数据模型
支持 pull、push 两种方式添加数据

Prometheus(普罗米修斯)架构

(普罗米修斯)数据来源rometheus

服务器指标来源
容器指标来源
组件指标来源 

8-2 Go PaaS 平台Prometheus 监控安装

C:\Users\Administrator\Desktop\gopaas\promethues\README.md

### Prometheus 安装说明

#### 1.解压 zip 包
```cassandraql
unzip v0.9.0.zip
```

#### 2.进入目录 
```cassandraql
cd kube-prometheus-0.9.0
```

#### 3.执行安装命令
```cassandraql
//创建命名空间和CRD
kubectl create -f manifests/setup

//等待创建结束
until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done

//创建监控组件
kubectl create -f manifests/

//查看 monitoring 内的 pod
kubectl get pods -n monitoring
```

#### 4.添加路由外网访问
通过前一章开发的 route 功能添加 monitoring 命名空间下
grafana.wu123.com 域名
第一次登录默认账号:admin 密码:admin
[root@k8s-worker01 kube-prometheus-0.9.0]# kubectl get pods -n monitoring
NAME                                   READY   STATUS              RESTARTS   AGE
alertmanager-main-0                    2/2     Running             0          4m52s
alertmanager-main-1                    2/2     Running             0          4m41s
blackbox-exporter-589d9b456-zs74d      3/3     Running             0          4m52s
grafana-6767dc8755-sl48b               1/1     Running             0          4m55s
kube-state-metrics-8fc46689b-hgh9w     2/3     Running             0          4m52s
node-exporter-5h2vw                    2/2     Running             0          4m52s
node-exporter-6gp27                    2/2     Running             0          4m52s
node-exporter-j6phd                    2/2     Running             0          4m52s
prometheus-adapter-58cd975bff-9rvj8    1/1     Running             0          4m52s
prometheus-adapter-58cd975bff-lwjhl    1/1     Running             0          4m52s
prometheus-k8s-0                       2/2     Running             0          4m52s
prometheus-k8s-1                       2/2     Running             0          4m52s
prometheus-operator-6cdd8db9f7-7mp6q   2/2     Running             0          9m6s

8-3 Go PaaS 平台 监控 Grafana 图标使用说明

8-4 总结&思考

主要内容
介绍了 Prometheus的基础组件与架构在 k8s 中安装Promethues 与监控看板
grafana功能展示

经验之谈
监控为GOPaaS平台不或缺的能力,数据需要近一步保存
PaaS 平台的监控扩展基于 Prometheus
监控更多要考虑业务属性和决策目标

还有哪些决策需要监控数据的支持
监控能够给 PaaS运营带来哪些场景

8-5 【扩展阅读】Prometheus Operator 安装配置详细文档

第9章 云原生 Go PaaS 平台分布式存储管理功能开发,提供数据落盘方案

数据是非常宝贵的财富,业务运行过程中产生的数据是无形的资产,分布式存储系统能够提供多种数据保存方案。深度解读 Ceph 分布式系统的原理、架构、核心组件等知识

9-1 Go PaaS 平台分布式存储 Ceph 架构介绍

GO PaaS 平台分布式存储开发

主要内容
Ceph 基础概念介绍
Ceph 架构说明及安装
服务后端功能开发

Ceph 集群基础介绍
Ceph 特性
Ceph 存储类型
Ceph 核心组件

Ceph 特性
高性能:支持上千个节点,支持TP到PB及的数据。
高可用:故障域分离,强一致性,自动修复。

高扩性:去中心化,扩展灵活。

Ceph 存储支持的类型
块存储:类比传统的存储硬盘,磁盘阵列
文件存储:类比FTP 服务
对象存储:类比大容量硬盘里面附带文件服务

Ceph 存储架构及核心组件

Ceph 基础存储系统 RADOS
全称: Reliable Autonomic Distributed Object Store
RADOS是ceph存储集群的基础
所有数据都以对象的形式存储

Ceph 基础库LIBRADOS
LIBRADOS层的功能是对 RADOS 进行抽象和封装
向上层提供 API以便直接基于 RADOS 进行应用开发
支持多种编程语言,比如GO、C、C++、Python 等

上层接口RADOSGW、RBD和CEPHFS
RADOSGW:基于RESTFUL协议,兼容S3和Swift
RBD提供分布式的块存储设备接口,像磁盘一样挂载使用
CephFS 是一个POSIX 兼容的分布式文件系统

9-2 Go PaaS 平台 Ceph 核心组件介绍 

Ceph 核心组件简介 OSD(ceph-osd)
object storage daemon,用于集群中所有数据与对象的存储。
负责处理集群数据的复制、恢复、回填、再均衡。
Ceph 集群通过管理 OSD 来管理物理硬盘

Ceph 核心组件简介 Manager (ceph-mgr)
收集 Ceph 集群状态,运行指标(存储利用率、系统负载等)
外提供 ceph dashboard (ceph ui)和 resetful api。
Manager组件开启高可用时,至少2个实现高可用性。

Ceph 核心组件简介 MDSceph-mds):
Metadata server,元数据服务
为 Ceph 文件系统提供元数据计算、缓存与同步服务
MDS类似于元数据的代理缓存服务器

Ceph 核心组件简介 Monitor (ceph-mon)
维护集群Cluster Map的状态
维护集群的Cluster MAP二进制表,保证集群数据的一致性monitor组件信息,manger 组件信息,osd 组件信息,mds组件信息,crush 算法信息

9-3 Go PaaS 平台 Ceph 存储过程及核心概念介绍

RADOS的系统逻辑结构

RADOS存储过程

逻辑概念说明
File:用户需要存储或者访问的文件
Objects: RADOS的基本存储单元,即对象。
PG(Placement Group): 这里PG作用是对object的存储进行组织和位置映射 

逻辑概念说明
OSD (object storage device 
oid : 每个object都会有一个唯一的OID,由ino和ono生成
pgid: 使用静态hash函数对OID做hash去除特征码

存储过程中各层次之间的映射关系
file -> object
object -> PG
PG -> OSD

为什么引入 PG 概念?
因为 Obiect 对象的 size 很小,并不会直接存储进 OSD 中
多对象如果遍历寻址,速度很缓慢
直接映射到 OSD上当单个OSD 损坏后无法迁移数据

9-4 Go PaaS 平台Ceph 资源购买注意事项

Ceph 集群安装
3台 2C 4G 20G系统盘,20G数据盘
操作系统为 Centos 8.3
采用 Cephadm 方式安装 ceph 16 版本

9-5 Go PaaS 平台Ceph 安全机器初始化

### 基于 Centos8 系统通过 Cephadm 快速部署  Ceph16(pacific)版本

#### 前期准备

##### 1. 安装依赖
lvm因为系统自带的都有,所以就不用单独安装了,
```cassandraql
dnf install epel-release -y
dnf install python3 -y
dnf install podman -y
dnf install -y chrony
systemctl start chronyd && systemctl enable chronyd
```
chrony时间服务为必须安装,具体有2点原因:1为不安装在添加主机的时候会报错,2为即使安装成功ceph -s会也提示时间不同步!
##### 2.关闭防火墙和selinux (每台都执行)
```cassandraql
systemctl disable firewalld && systemctl stop firewalld
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
```

##### 3.分别在三个节点设置主机名
```cassandraql
hostnamectl set-hostname ceph01
hostnamectl set-hostname ceph02
hostnamectl set-hostname ceph03
```
重启机器 reboot
cephadm需要主机名为短主机名,不能为FQDN,否者在添加主机会报错!

##### 4.添加hosts文件中的主机名和IP关系,主机名需要和上面一致
172.31.96.70 ceph01  2Cpu 4G内存  20G系统盘,20G数据盘
172.31.96.71 ceph02  2Cpu 4G内存  20G系统盘,20G数据盘
172.31.96.72 ceph03  2Cpu 4G内存  20G系统盘,20G数据盘
```cassandraql
cat >> /etc/hosts <<EOF
172.31.96.70 ceph01 
172.31.96.71 ceph02 
172.31.96.72 ceph03 
EOF
```

9-6 Go PaaS 平台cephadm 安装基础集群(上)

C:\Users\Administrator\Desktop\gopaas\ceph\1.ceph 安装.md

#### 安装 cephadm
cephadm 命令可以
1. 引导新集群
2. 使用有效的Ceph CLI启动容器化的Shell
3. 帮助调试容器化的Ceph守护进程。
以下操作只在一台节点执行就可以
##### 1.使用curl获取独立脚本的最新版本。网络不好的话可直接去GitHub复制

```cassandraql
curl --silent --remote-name --location https://github.com/ceph/ceph/raw/pacific/src/cephadm/cephadm
chmod +x cephadm
./cephadm add-repo --release pacific
./cephadm install
./cephadm install  ceph-common
```
官方文档中还提到了另一种安装cephadm方式,就是通过dnf install -y cephadm安装,实践证明最好不要使用这种方式,这种方式安装的cephadm可能不是最新版本的,但cephadm去拉的容器版本又是最新的,会导致两个版本不一致!


 

#### 引导新群集
##### 1.先创建一个目录:/etc/ceph
```cassandraql
mkdir -p /etc/ceph
```

##### 2.运行该命令:ceph bootstrap
```cassandraql
cephadm bootstrap --mon-ip 172.31.96.70
```
此命令将会进行以下操作:
- 为本地主机上的新群集创建monitor和manager守护程序。
- 为 Ceph 群集生成新的 SSH 密钥,并将其添加到root用户的文件/root/.ssh/authorized_keys
- 将与新群集通信所需的最小配置文件保存到 /etc/ceph/ceph.conf
- 将client.admin管理(特权!)密钥的副本写入/etc/ceph/ceph.client.admin.keyring
- 将公钥的副本写入/etc/ceph/ceph.pub

安装日志如下为成功
```cassandraql
[root@ceph01 ~]# cephadm bootstrap --mon-ip 172.31.96.70
Verifying podman|docker is present...
Verifying lvm2 is present...
Verifying time synchronization is in place...
Unit chronyd.service is enabled and running
Repeating the final host check...
podman (/usr/bin/podman) version 3.3.1 is present
systemctl is present
lvcreate is present
Unit chronyd.service is enabled and running
Host looks OK
Cluster fsid: ba25aef4-19f6-11ed-867d-00163e005933
Verifying IP 172.31.96.71 port 3300 ...
ERROR: [Errno 99] Cannot assign requested address
[root@ceph01 ~]# cephadm bootstrap --mon-ip 172.31.96.70
Verifying podman|docker is present...
Verifying lvm2 is present...
Verifying time synchronization is in place...
Unit chronyd.service is enabled and running
Repeating the final host check...
podman (/usr/bin/podman) version 3.3.1 is present
systemctl is present
lvcreate is present
Unit chronyd.service is enabled and running
Host looks OK
Cluster fsid: c32ff766-19f6-11ed-aa17-00163e005933
Verifying IP 172.31.96.70 port 3300 ...
Verifying IP 172.31.96.70 port 6789 ...
Mon IP `172.31.96.70` is in CIDR network `172.31.96.0/20`
- internal network (--cluster-network) has not been provided, OSD replication will default to the public_network
Pulling container image quay.io/ceph/ceph:v16...
Ceph version: ceph version 16.2.10 (45fa1a083152e41a408d15505f594ec5f1b4fe17) pacific (stable)
Extracting ceph user uid/gid from container image...
Creating initial keys...
Creating initial monmap...
Creating mon...
Waiting for mon to start...
Waiting for mon...
mon is available
Assimilating anything we can from ceph.conf...
Generating new minimal ceph.conf...
Restarting the monitor...
Setting mon public_network to 172.31.96.0/20
Wrote config to /etc/ceph/ceph.conf
Wrote keyring to /etc/ceph/ceph.client.admin.keyring
Creating mgr...
Verifying port 9283 ...
Waiting for mgr to start...
Waiting for mgr...
mgr not available, waiting (1/15)...
mgr not available, waiting (2/15)...
mgr not available, waiting (3/15)...
mgr not available, waiting (4/15)...
mgr is available
Enabling cephadm module...
Waiting for the mgr to restart...
Waiting for mgr epoch 5...
mgr epoch 5 is available
Setting orchestrator backend to cephadm...
Generating ssh key...
Wrote public SSH key to /etc/ceph/ceph.pub
Adding key to root@localhost authorized_keys...
Adding host ceph01...
Deploying mon service with default placement...
Deploying mgr service with default placement...
Deploying crash service with default placement...
Deploying prometheus service with default placement...
Deploying grafana service with default placement...
Deploying node-exporter service with default placement...
Deploying alertmanager service with default placement...
Enabling the dashboard module...
Waiting for the mgr to restart...
Waiting for mgr epoch 9...
mgr epoch 9 is available
Generating a dashboard self-signed certificate...
Creating initial admin user...
Fetching dashboard port number...
Ceph Dashboard is now available at:

             URL: https://ceph01:8443/
            User: admin
        Password: 9ro6qdzyel

Enabling client.admin keyring and conf on hosts with "admin" label
Enabling autotune for osd_memory_target
You can access the Ceph CLI as following in case of multi-cluster or non-default config:

        sudo /usr/sbin/cephadm shell --fsid c32ff766-19f6-11ed-aa17-00163e005933 -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring

Or, if you are only running a single cluster on this host:

        sudo /usr/sbin/cephcadm shell 

Please consider enabling telemetry to help improve Ceph:

        ceph telemetry on

For more information see:

        https://docs.ceph.com/en/pacific/mgr/telemetry/

Bootstrap complete.
```

完成后记录以上了IP以及用户和密码,打开Ceph Dashboard并根据提示修改密码,打开后提示要激活统计模块。
如果错过使用命令
```cassandraql
如果Ceph Dashboard中错过了启用,也可以使用命令启用,命令是“ceph telemetry on --license sharing-1-0”。
```

如果忘记记录密码可以通过以下方法重置密码(将密码写入password文件中,通过命令导入密码)
```cassandraql
ceph dashboard ac-user-set-password admin -i password 
{"username": "admin", "password": "$2b$12$6oFrEpssXCzLnKTWQy5fM.YZwlHjn8CuQRdeSSJR9hBGgVuwGCxoa", "roles": ["administrator"], "name": null, "email": null, "lastUpdate": 1620495653, "enabled": true, "pwdExpirationDate": null, "pwdUpdateRequired": false}
```

9-7 Go PaaS 平台cephadm 安装基础集群(下)

##### 3.添加主机
在引导成功单节点Ceph群集后会引导程序会将public key的副本写入/etc/ceph/ceph.pub,在添加主机节点前需要讲该key分发到要加入群集的主机上

拷贝到ceph02
```cassandraql
ssh-copy-id -f -i /etc/ceph/ceph.pub root@ceph02
```

拷贝到ceph03
```cassandraql
ssh-copy-id -f -i /etc/ceph/ceph.pub root@ceph03
```

添加ceph 02 节点
```cassandraql
ceph orch host add ceph02 172.31.96.71
```
添加ceph 03 节点
```cassandraql
ceph orch host add ceph03 172.31.96.72
```

查看节点
```cassandraql
[root@ceph01 ~]# ceph orch host ls
HOST    ADDR          LABELS  STATUS  
ceph01  172.31.96.70  _admin          
ceph02  172.31.96.71                  
ceph03  172.31.96.72                  
3 hosts in cluster
```

##### 4.添加OSD
添加OSD需求满足以下所有条件:
- 设备必须没有分区。
- 设备不得具有任何LVM状态。
- 不得安装设备。
- 该设备不得包含文件系统。
- 该设备不得包含Ceph BlueStore OSD。
- 设备必须大于 5 GB。
添加OSD有2种方式,
1.为自动添加所有满足条件的OSD。
```
ceph orch apply osd --all-available-devices
```
2.为通过手工指定的方式添加OSD。
```cassandraql
ceph orch daemon add osd ceph1:/dev/sdb
```
本次使用第一种自动部署的方式,部署完成后查看设备列表,显示为NO就完成了。
```cassandraql
[root@ceph01 ~]# ceph orch device ls
HOST    PATH      TYPE  DEVICE ID              SIZE  AVAILABLE  REFRESHED  REJECT REASONS                                                 
ceph01  /dev/vdb  hdd   j6c3xd1a6qug5beqk71y  21.4G             9s ago     Insufficient space (<10 extents) on vgs, LVM detected, locked  
ceph02  /dev/vdb  hdd   j6c3xd1a6qug5beqk720  21.4G             14s ago    Insufficient space (<10 extents) on vgs, LVM detected, locked  
ceph03  /dev/vdb  hdd   j6c3xd1a6qug5beqk71z  21.4G             14s ago    Insufficient space (<10 extents) on vgs, LVM detected, locked
```

#### 5.查看Ceph部署服务
```cassandraql
[root@ceph01 ~]# ceph -s
  cluster:
    id:     c32ff766-19f6-11ed-aa17-00163e005933
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum ceph01,ceph02,ceph03 (age 4m)
    mgr: ceph01.sdapbz(active, since 30m), standbys: ceph02.njhkgr
    osd: 3 osds: 3 up (since 96s), 3 in (since 114s)
 
  data:
    pools:   1 pools, 1 pgs
    objects: 0 objects, 0 B
    usage:   15 MiB used, 60 GiB / 60 GiB avail
    pgs:     1 active+clean
 
```

打开 dashboard 看监控数据

9-8 Go PaaS 平台ceph 核心组件安装

#### 6.部署RGW
使用指定数量匹配模式部署。
```
ceph orch apply rgw rgw --placement=3
```
通过Service查看命令ceph orch ls查看该服务状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  10s ago    40m  count:1    
crash                                       3/3  15s ago    40m  *          
grafana                    ?:3000           1/1  10s ago    40m  count:1    
mgr                                         2/2  14s ago    40m  count:2    
mon                                         3/5  15s ago    40m  count:5    
node-exporter              ?:9100           3/3  15s ago    40m  *          
osd.all-available-devices                     3  15s ago    9m   *          
prometheus                 ?:9095           1/1  10s ago    40m  count:1    
rgw.rgw                    ?:80             3/3  15s ago    30s  count:3
```
通过Deamon查看命令ceph orch ps查看该进程状态。
```cassandraql
[root@ceph01 ~]# ceph orch ps
NAME                   HOST    PORTS        STATUS         REFRESHED  AGE  MEM USE  MEM LIM  VERSION  IMAGE ID      CONTAINER ID  
alertmanager.ceph01    ceph01  *:9093,9094  running (12m)    41s ago  39m    27.9M        -           ba2b418f427c  b6fbf7382136  
crash.ceph01           ceph01               running (39m)    41s ago  39m    7717k        -  16.2.10  0d668911f040  9660dd933bd2  
crash.ceph02           ceph02               running (14m)    44s ago  14m    9713k        -  16.2.10  0d668911f040  35cac443b75c  
crash.ceph03           ceph03               running (13m)    45s ago  13m    8632k        -  16.2.10  0d668911f040  3135f2560d98  
grafana.ceph01         ceph01  *:3000       running (38m)    41s ago  39m    67.9M        -  8.3.5    dad864ee21e9  7f47d88ad676  
mgr.ceph01.sdapbz      ceph01  *:9283       running (41m)    41s ago  41m     499M        -  16.2.10  0d668911f040  9e001eab3a53  
mgr.ceph02.njhkgr      ceph02  *:8443,9283  running (14m)    44s ago  14m     409M        -  16.2.10  0d668911f040  146058cf2aea  
mon.ceph01             ceph01               running (41m)    41s ago  41m     147M    2048M  16.2.10  0d668911f040  5896f4b2a014  
mon.ceph02             ceph02               running (14m)    44s ago  14m    85.3M    2048M  16.2.10  0d668911f040  7fd0c2c36613  
mon.ceph03             ceph03               running (13m)    45s ago  13m    82.1M    2048M  16.2.10  0d668911f040  db5a5b5039d5  
node-exporter.ceph01   ceph01  *:9100       running (38m)    41s ago  38m    16.5M        -           1dbe0e931976  2d7d68e3a0da  
node-exporter.ceph02   ceph02  *:9100       running (14m)    44s ago  14m    18.2M        -           1dbe0e931976  083f26d39f07  
node-exporter.ceph03   ceph03  *:9100       running (12m)    45s ago  12m    14.4M        -           1dbe0e931976  c3b21c79b2f4  
osd.0                  ceph03               running (9m)     45s ago   9m    40.4M    4096M  16.2.10  0d668911f040  2210df27e314  
osd.1                  ceph02               running (9m)     44s ago   9m    44.7M    4096M  16.2.10  0d668911f040  9ab8967c65b9  
osd.2                  ceph01               running (9m)     41s ago   9m    47.6M    4096M  16.2.10  0d668911f040  78a2000670f7  
prometheus.ceph01      ceph01  *:9095       running (12m)    41s ago  38m    69.2M        -           514e6a882f6e  06de63766e30  
rgw.rgw.ceph01.bvcnos  ceph01  *:80         running (58s)    41s ago  57s    43.7M        -  16.2.10  0d668911f040  96c4d8963d47  
rgw.rgw.ceph02.awygjp  ceph02  *:80         running (54s)    44s ago  53s    46.2M        -  16.2.10  0d668911f040  9d0bd0b0b060  
rgw.rgw.ceph03.iiixeq  ceph03  *:80         running (50s)    45s ago  49s    21.2M        -  16.2.10  0d668911f040  a5d6e8a8983a 
```
集成到dashboard
```cassandraql
[root@ceph01 ~]# radosgw-admin user create --uid=rgw --display-name=rgw --system

以下是输出结果

"keys": [
        {
            "user": "rgw",
            "access_key": "M0XRR80H4AGGE4PP0A5B",
            "secret_key": "Tbln48sfIceDGNill5muCrX0oMCHrQcl2oC9OURe"
        }
    ],{
    "user_id": "rgw",
    "display_name": "rgw",
    "email": "",
    "suspended": 0,
    "max_buckets": 1000,
    "subusers": [],
    "keys": [
        {
            "user": "rgw",
            "access_key": "43DTMZ3EY02B10QNTJSS",
            "secret_key": "4d3qzY1A7uqVk3LWVMsGJY0negbcxg95d8QJYuzi"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "system": "true",
    "default_placement": "",
    "default_storage_class": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw",
    "mfa_ids": []
}

```
查看 Dashboard 是否集成成功

#### 7.部署Cephfs
部署cephfs服务并创建cepfs,创建cephfs有两种方式,一种是使用的是ceph fs命令该命令会自动创建相应的池,另一种手工创建池并创建Service,下面方法任选一种。
方法一:
```cassandraql
ceph fs volume create cephfs --placement=3
```
方法二:
```cassandraql
#ceph osd pool create cephfs_data 32
#ceph osd pool create cephfs_metadata 32
#ceph fs new cephfs cephfs_metadata cephfs_data
#ceph orch apply mds cephfs --placement=3
```
我们采用方法一
查看Service状态。
```cassandraql
[root@ceph01 ~]# ceph fs volume create cephfs --placement=3
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  9s ago     48m  count:1    
crash                                       3/3  13s ago    48m  *          
grafana                    ?:3000           1/1  9s ago     48m  count:1    
mds.cephfs                                  3/3  13s ago    29s  count:3    
mgr                                         2/2  13s ago    48m  count:2    
mon                                         3/5  13s ago    48m  count:5    
node-exporter              ?:9100           3/3  13s ago    48m  *          
osd.all-available-devices                     3  13s ago    17m  *          
prometheus                 ?:9095           1/1  9s ago     48m  count:1    
rgw.rgw                    ?:80             3/3  13s ago    8m   count:3   
```
查看Deamon状态。
```cassandraql
[root@ceph01 ~]# ceph orch ps
NAME                      HOST    PORTS        STATUS         REFRESHED  AGE  MEM USE  MEM LIM  VERSION  IMAGE ID      CONTAINER ID  
alertmanager.ceph01       ceph01  *:9093,9094  running (20m)    59s ago  48m    29.5M        -           ba2b418f427c  b6fbf7382136  
crash.ceph01              ceph01               running (48m)    59s ago  48m    7587k        -  16.2.10  0d668911f040  9660dd933bd2  
crash.ceph02              ceph02               running (22m)    63s ago  22m    9667k        -  16.2.10  0d668911f040  35cac443b75c  
crash.ceph03              ceph03               running (21m)    63s ago  21m    8606k        -  16.2.10  0d668911f040  3135f2560d98  
grafana.ceph01            ceph01  *:3000       running (46m)    59s ago  47m    77.2M        -  8.3.5    dad864ee21e9  7f47d88ad676  
mds.cephfs.ceph01.uwclzo  ceph01               running (74s)    59s ago  74s    28.2M        -  16.2.10  0d668911f040  e7ead0c130a6  
mds.cephfs.ceph02.zqmpyt  ceph02               running (71s)    63s ago  70s    23.7M        -  16.2.10  0d668911f040  06ed8fd3bc1a  
mds.cephfs.ceph03.glufpl  ceph03               running (67s)    63s ago  67s    22.9M        -  16.2.10  0d668911f040  a3e497cb8a85  
mgr.ceph01.sdapbz         ceph01  *:9283       running (49m)    59s ago  49m     508M        -  16.2.10  0d668911f040  9e001eab3a53  
mgr.ceph02.njhkgr         ceph02  *:8443,9283  running (22m)    63s ago  22m     410M        -  16.2.10  0d668911f040  146058cf2aea  
mon.ceph01                ceph01               running (50m)    59s ago  50m     177M    2048M  16.2.10  0d668911f040  5896f4b2a014  
mon.ceph02                ceph02               running (22m)    63s ago  22m     115M    2048M  16.2.10  0d668911f040  7fd0c2c36613  
mon.ceph03                ceph03               running (21m)    63s ago  21m     111M    2048M  16.2.10  0d668911f040  db5a5b5039d5  
node-exporter.ceph01      ceph01  *:9100       running (47m)    59s ago  47m    17.4M        -           1dbe0e931976  2d7d68e3a0da  
node-exporter.ceph02      ceph02  *:9100       running (22m)    63s ago  22m    18.0M        -           1dbe0e931976  083f26d39f07  
node-exporter.ceph03      ceph03  *:9100       running (21m)    63s ago  21m    14.7M        -           1dbe0e931976  c3b21c79b2f4  
osd.0                     ceph03               running (17m)    63s ago  17m    69.7M    4096M  16.2.10  0d668911f040  2210df27e314  
osd.1                     ceph02               running (17m)    63s ago  17m    72.6M    4096M  16.2.10  0d668911f040  9ab8967c65b9  
osd.2                     ceph01               running (17m)    59s ago  17m    72.4M    4096M  16.2.10  0d668911f040  78a2000670f7  
prometheus.ceph01         ceph01  *:9095       running (20m)    59s ago  46m    73.6M        -           514e6a882f6e  06de63766e30  
rgw.rgw.ceph01.bvcnos     ceph01  *:80         running (9m)     59s ago   9m    73.3M        -  16.2.10  0d668911f040  96c4d8963d47  
rgw.rgw.ceph02.awygjp     ceph02  *:80         running (9m)     63s ago   9m    80.8M        -  16.2.10  0d668911f040  9d0bd0b0b060  
rgw.rgw.ceph03.iiixeq     ceph03  *:80         running (9m)     63s ago   8m    57.4M        -  16.2.10  0d668911f040  a5d6e8a8983a  
```

查看 Dashboard

#### 8.部署NFS
先创建nfs所需求的池。
```cassandraql
#ceph osd pool create ganesha_data 32
#ceph osd pool application enable ganesha_data nfs
```
部署nfs Service。
```cassandraql
#ceph orch apply nfs nfs ganesha_data --placement=3
```
查看Service状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  8s ago     52m  count:1    
crash                                       3/3  13s ago    52m  *          
grafana                    ?:3000           1/1  8s ago     52m  count:1    
mds.cephfs                                  3/3  13s ago    4m   count:3    
mgr                                         2/2  13s ago    52m  count:2    
mon                                         3/5  13s ago    52m  count:5    
nfs.nfs                                     3/3  13s ago    36s  count:3    
node-exporter              ?:9100           3/3  13s ago    52m  *          
osd.all-available-devices                     3  13s ago    21m  *          
prometheus                 ?:9095           1/1  8s ago     52m  count:1    
rgw.rgw                    ?:80             3/3  13s ago    12m  count:3  
```

查看Deamon状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT  
alertmanager               ?:9093,9094      1/1  8s ago     52m  count:1    
crash                                       3/3  13s ago    52m  *          
grafana                    ?:3000           1/1  8s ago     52m  count:1    
mds.cephfs                                  3/3  13s ago    4m   count:3    
mgr                                         2/2  13s ago    52m  count:2    
mon                                         3/5  13s ago    52m  count:5    
nfs.nfs                                     3/3  13s ago    36s  count:3    
node-exporter              ?:9100           3/3  13s ago    52m  *          
osd.all-available-devices                     3  13s ago    21m  *          
prometheus                 ?:9095           1/1  8s ago     52m  count:1    
rgw.rgw                    ?:80             3/3  13s ago    12m  count:3    

```

#### 9.部署iSCSi
创建iscsi所需求的池。
```cassandraql
#ceph osd pool create  iscsi_pool 16 16
#ceph osd pool application enable iscsi_pool iscsi
```

部署iscsi我们换YAM方式
```cassandraql
#vi iscsi.yaml
service_type: iscsi
service_id: gw
placement:
  hosts:
    - ceph01
    - ceph02
    - ceph03
spec:
  pool: iscsi_pool
  trusted_ip_list: "172.31.96.70,172.31.96.71,172.31.96.72"
  api_user: admin
  api_password: admin
  api_secure: false

```
通过apply命令部署,cephadm也是声明式的,所以如果想修改配置参数只需要直接修改YAML文件。

```cassandraql
[root@ceph01 ~]# ceph orch apply -i iscsi.yaml
Scheduled iscsi.gw update...
```
查看Service状态。
```cassandraql
[root@ceph01 ~]# ceph orch ls
NAME                       PORTS        RUNNING  REFRESHED  AGE  PLACEMENT             
alertmanager               ?:9093,9094      1/1  9s ago     67m  count:1               
crash                                       3/3  21s ago    67m  *                     
grafana                    ?:3000           1/1  9s ago     67m  count:1               
iscsi.gw                                    3/3  21s ago    40s  ceph01;ceph02;ceph03  
mds.cephfs                                  3/3  21s ago    19m  count:3               
mgr                                         2/2  20s ago    67m  count:2               
mon                                         3/5  21s ago    67m  count:5               
nfs.nfs                                     3/3  21s ago    16m  count:3               
node-exporter              ?:9100           3/3  21s ago    67m  *                     
osd.all-available-devices                     3  21s ago    36m  *                     
prometheus                 ?:9095           1/1  9s ago     67m  count:1               
rgw.rgw                    ?:80             3/3  21s ago    27m  count:3   
```
查看Deamon状态。
```cassandraql
[root@ceph01 ~]# ceph orch ps
NAME                       HOST    PORTS        STATUS         REFRESHED  AGE  MEM USE  MEM LIM  VERSION  IMAGE ID      CONTAINER ID  
alertmanager.ceph01        ceph01  *:9093,9094  running (39m)    33s ago  67m    35.2M        -           ba2b418f427c  b6fbf7382136  
crash.ceph01               ceph01               running (67m)    33s ago  67m    7386k        -  16.2.10  0d668911f040  9660dd933bd2  
crash.ceph02               ceph02               running (41m)    44s ago  41m    10.5M        -  16.2.10  0d668911f040  35cac443b75c  
crash.ceph03               ceph03               running (40m)    45s ago  40m    10.8M        -  16.2.10  0d668911f040  3135f2560d98  
grafana.ceph01             ceph01  *:3000       running (65m)    33s ago  66m    80.2M        -  8.3.5    dad864ee21e9  7f47d88ad676  
iscsi.gw.ceph01.mqjxqu     ceph01               running (60s)    33s ago  59s    76.1M        -  3.5      0d668911f040  e712bdbb2d80  
iscsi.gw.ceph02.xeggjo     ceph02               running (56s)    44s ago  56s    52.9M        -  3.5      0d668911f040  04c7f5263155  
iscsi.gw.ceph03.xykglu     ceph03               running (52s)    45s ago  52s    76.9M        -  3.5      0d668911f040  59cb4c7bbfd5  
mds.cephfs.ceph01.uwclzo   ceph01               running (20m)    33s ago  20m    27.6M        -  16.2.10  0d668911f040  e7ead0c130a6  
mds.cephfs.ceph02.zqmpyt   ceph02               running (20m)    44s ago  20m    26.8M        -  16.2.10  0d668911f040  06ed8fd3bc1a  
mds.cephfs.ceph03.glufpl   ceph03               running (20m)    45s ago  20m    26.5M        -  16.2.10  0d668911f040  a3e497cb8a85  
mgr.ceph01.sdapbz          ceph01  *:9283       running (68m)    33s ago  68m     495M        -  16.2.10  0d668911f040  9e001eab3a53  
mgr.ceph02.njhkgr          ceph02  *:8443,9283  running (41m)    44s ago  41m     411M        -  16.2.10  0d668911f040  146058cf2aea  
mon.ceph01                 ceph01               running (68m)    33s ago  69m     237M    2048M  16.2.10  0d668911f040  5896f4b2a014  
mon.ceph02                 ceph02               running (41m)    44s ago  41m     164M    2048M  16.2.10  0d668911f040  7fd0c2c36613  
mon.ceph03                 ceph03               running (40m)    45s ago  40m     146M    2048M  16.2.10  0d668911f040  db5a5b5039d5  
nfs.nfs.0.0.ceph01.ijxnwe  ceph01  *:2049       running (16m)    33s ago  16m    72.4M        -  3.5      0d668911f040  f9b0609cbe7b  
nfs.nfs.1.0.ceph02.pedadk  ceph02  *:2049       running (16m)    44s ago  16m    75.8M        -  3.5      0d668911f040  54d58b785ed6  
nfs.nfs.2.0.ceph03.daqdkw  ceph03  *:2049       running (16m)    45s ago  16m    73.0M        -  3.5      0d668911f040  c1bebe7782d4  
node-exporter.ceph01       ceph01  *:9100       running (66m)    33s ago  66m    17.7M        -           1dbe0e931976  2d7d68e3a0da  
node-exporter.ceph02       ceph02  *:9100       running (41m)    44s ago  41m    18.3M        -           1dbe0e931976  083f26d39f07  
node-exporter.ceph03       ceph03  *:9100       running (40m)    45s ago  40m    15.5M        -           1dbe0e931976  c3b21c79b2f4  
osd.0                      ceph03               running (36m)    45s ago  36m    77.8M    4096M  16.2.10  0d668911f040  2210df27e314  
osd.1                      ceph02               running (36m)    44s ago  36m    80.1M    4096M  16.2.10  0d668911f040  9ab8967c65b9  
osd.2                      ceph01               running (36m)    33s ago  36m    79.3M    4096M  16.2.10  0d668911f040  78a2000670f7  
prometheus.ceph01          ceph01  *:9095       running (39m)    33s ago  65m    97.3M        -           514e6a882f6e  06de63766e30  
rgw.rgw.ceph01.bvcnos      ceph01  *:80         running (28m)    33s ago  28m    61.8M        -  16.2.10  0d668911f040  96c4d8963d47  
rgw.rgw.ceph02.awygjp      ceph02  *:80         running (28m)    44s ago  28m    81.6M        -  16.2.10  0d668911f040  9d0bd0b0b060  
rgw.rgw.ceph03.iiixeq      ceph03  *:80         running (27m)    45s ago  27m    60.0M        -  16.2.10  0d668911f040  a5d6e8a8983a  
```

9-9 Go PaaS 平台 k8s 通过CSI方式添加外部Ceph 系统(上)

C:\Users\Administrator\Desktop\gopaas\ceph\2.k8s 使用 CSI 添加 ceph 为存储.md

### k8s 1.21.5 CSI 模式添加 ceph 16 为为外部存储(动态存储卷)

#### 1.动态持久卷
不需要存储管理员干预,使k8s使用的存储image创建自动化,即根据使用需要可以动态申请存储空间并自动创建。需要先定义一个或者多个StorageClass,每个StorageClass都必须配置一个provisioner,用来决定使用哪个卷插件分配PV。然后,StorageClass资源指定持久卷声明请求StorageClass时使用哪个provisioner来在对应存储创建持久卷。

#### 2.创建一个普通用户来给k8s做rdb的映射
在ceph集群中创建一个k8s专用的pool和用户:
```cassandraql
ceph osd pool create kubernetes 16 16
ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd pool=kubernetes'

初始化
rbd pool init kubernetes

查看 pool
ceph osd pool ls 
rados lspools
```
得到
```cassandraql
[client.kubernetes]
        key = AQC2Q/ZiecM/MBAA2nwfDPKgfReHxz/o4kQV3A==
```
后面的配置需要用到这里的 key,如果忘了可以通过以下命令来获取:
```cassandraql
ceph auth get client.kubernetes
```
得到
```cassandraql
[client.kubernetes]
        key = AQC2Q/ZiecM/MBAA2nwfDPKgfReHxz/o4kQV3A==
        caps mgr = "profile rbd pool=kubernetes"
        caps mon = "profile rbd"
        caps osd = "profile rbd pool=kubernetes"
exported keyring for client.kubernetes
```
#### 3. 部署 ceph-csi 在k8s master 集群上
拉取 ceph-csi 的 最新 release 分支(v3.6.2)
```cassandraql
git clone --depth 1 --branch v3.6.2 https://github.com/ceph/ceph-csi
```
##### 修改 Configmap
获取 Ceph 集群的信息:
```cassandraql
[root@ceph01 ~]# ceph mon dump
epoch 3
fsid c32ff766-19f6-11ed-aa17-00163e005933
last_changed 2022-08-12T04:55:22.875422+0000
created 2022-08-12T04:26:27.658334+0000
min_mon_release 16 (pacific)
election_strategy: 1
0: [v2:172.31.96.70:3300/0,v1:172.31.96.70:6789/0] mon.ceph01
1: [v2:172.31.96.71:3300/0,v1:172.31.96.71:6789/0] mon.ceph02
2: [v2:172.31.96.72:3300/0,v1:172.31.96.72:6789/0] mon.ceph03
dumped monmap epoch 3
```

这里需要用到两个信息:
- fsid : 这个是 Ceph 的集群 ID。
- 监控节点信息。目前 ceph-csi 只支持 v1 版本的协议,所以监控节点那里我们只能用 v1 的那个 IP 和端口号(例如,172.31.96.70:6789)。

进入 ceph-csi 的 deploy/rbd/kubernetes 目录:
```cassandraql
[root@master ~]# cd ceph-csi/deploy/rbd/kubernetes
[root@master kubernetes]# ls -l ./
total 40
-rw-r--r-- 1 root root  309 Aug 12 20:18 csi-config-map.yaml
-rw-r--r-- 1 root root  435 Aug 12 20:18 csidriver.yaml
-rw-r--r-- 1 root root 1776 Aug 12 20:18 csi-nodeplugin-psp.yaml
-rw-r--r-- 1 root root 1110 Aug 12 20:18 csi-nodeplugin-rbac.yaml
-rw-r--r-- 1 root root 1199 Aug 12 20:18 csi-provisioner-psp.yaml
-rw-r--r-- 1 root root 3264 Aug 12 20:18 csi-provisioner-rbac.yaml
-rw-r--r-- 1 root root 8021 Aug 12 20:18 csi-rbdplugin-provisioner.yaml
-rw-r--r-- 1 root root 7242 Aug 12 20:18 csi-rbdplugin.yaml
```
将以上获取的信息写入 csi-config-map.yaml:
vi csi-config-map.yaml
```cassandraql
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    [
      {
        "clusterID": "c32ff766-19f6-11ed-aa17-00163e005933",
        "monitors": [
          "172.31.96.70:6789",
          "172.31.96.71:6789",
          "172.31.96.72:6789"
        ]
      }
    ]    
metadata:
  name: ceph-csi-config
```
将此 Configmap 存储到 Kubernetes 集群中:
```cassandraql
kubectl  apply -f csi-config-map.yaml
```

创建ceph-config
```cassandraql
cat <<EOF > ceph-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  ceph.conf: |
    [global]
    auth_cluster_required = cephx
    auth_service_required = cephx
    auth_client_required = cephx
  # keyring is a required key and its value should be empty
  keyring: |
metadata:
  name: ceph-config
EOF
```
创建
```cassandraql
kubectl apply -f ceph-config-map.yaml
```

创建 ceph-csi-encryption-kms-config
```cassandraql
cat <<EOF > csi-kms-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    {}
metadata:
  name: ceph-csi-encryption-kms-config
EOF
```
启用
```cassandraql
kubectl apply -f csi-kms-config-map.yaml
```

9-10 Go PaaS 平台 k8s 通过CSI方式添加外部Ceph 系统(下)

##### 新建 Secret
使用创建的 kubernetes 用户 ID 和 cephx 密钥生成 Secret:
```cassandraql
cat <<EOF > csi-rbd-secret.yaml
   apiVersion: v1
   kind: Secret
   metadata:
     name: csi-rbd-secret
     namespace: default
   stringData:
     userID: kubernetes
     userKey: AQC2Q/ZiecM/MBAA2nwfDPKgfReHxz/o4kQV3A==
EOF
```
部署 Secret:
```cassandraql
kubectl apply -f csi-rbd-secret.yaml
```
##### RBAC 授权
创建必须的 ServiceAccount 和 RBAC ClusterRole/ClusterRoleBinding 资源对象:
```cassandraql
kubectl create -f csi-provisioner-rbac.yaml
kubectl create -f csi-nodeplugin-rbac.yaml
```

创建 PodSecurityPolicy:
```cassandraql
kubectl create -f csi-provisioner-psp.yaml
kubectl create -f csi-nodeplugin-psp.yaml
```

##### 部署 CSI sidecar
将 csi-rbdplugin-provisioner.yaml 和 csi-rbdplugin.yaml 中的 kms 部分配置注释掉:
```cassandraql
           # - name: ceph-csi-encryption-kms-config
           #   mountPath: /etc/ceph-csi-encryption-kms-config/

.... 
        #- name: ceph-csi-encryption-kms-config
        #  configMap:
        #    name: ceph-csi-encryption-kms-config
          
```
注释掉pod亲和性
```cassandraql
#      affinity:
#        podAntiAffinity:
#          requiredDuringSchedulingIgnoredDuringExecution:
#            - labelSelector:
#                matchExpressions:
#                  - key: app
#                    operator: In
#                    values:
#                      - csi-rbdplugin-provisioner
#              topologyKey: "kubernetes.io/hostname"
```

改为一个副本
```cassandraql
spec:
  replicas: 1
```

部署 csi-rbdplugin-provisioner:
```
kubectl create -f csi-rbdplugin-provisioner.yaml 
```
这里面包含了 6 个 Sidecar 容器,包括 external-provisioner、external-attacher、csi-resizer 和 csi-rbdplugin。

##### 部署 RBD CSI driver
最后部署 RBD CSI Driver:
``` 
kubectl create -f csi-rbdplugin.yaml
```
Pod 中包含两个容器:CSI node-driver-registrar 和 CSI RBD driver。
##### 创建 Storageclass
```cassandraql
cat <<EOF > storageclass.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: c32ff766-19f6-11ed-aa17-00163e005933
   pool: kubernetes
   imageFeatures: layering
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
   csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
   - discard
EOF
```
创建 storageclass 
```cassandraql
 kubectl apply -f storageclass.yaml 
```

- 这里的 clusterID 对应之前步骤中的 fsid。
- imageFeatures 用来确定创建的 image 特征,如果不指定,就会使用 RBD 内核中的特征列表,但 Linux 不一定支持所有特征,所以这里需要限制一下。

#### 4.试用 ceph-csi
Kubernetes 通过 PersistentVolume 子系统为用户和管理员提供了一组 API,将存储如何供应的细节从其如何被使用中抽象出来,其中 PV(PersistentVolume) 是实际的存储,PVC(PersistentVolumeClaim) 是用户对存储的请求。

下面通过官方仓库的示例来演示如何使用 ceph-csi。

先进入 ceph-csi 项目的 example/rbd 目录,然后直接创建 PVC:
```cassandraql
kubectl apply -f pvc.yaml
```
查看 PVC 和申请成功的 PV:
```cassandraql
$ kubectl get pvc
NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
rbd-pvc   Bound    pvc-44b89f0e-4efd-4396-9316-10a04d289d7f   1Gi        RWO            csi-rbd-sc     8m21s

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
pvc-44b89f0e-4efd-4396-9316-10a04d289d7f   1Gi        RWO            Delete           Bound    default/rbd-pvc      csi-rbd-sc              8m18s
```
再创建示例 Pod:
```
kubectl apply -f pod.yaml
```

进入 Pod 里面测试读写数据:
```cassandraql
kubectl apply -f pod.yaml
```
进入 Pod 里面测试读写数据:
```cassandraql

kubectl exec -it csi-rbd-demo-pod bash
root@csi-rbd-demo-pod:/# cd /var/lib/www/
root@csi-rbd-demo-pod:/var/lib/www# ls -l
total 4
drwxrwxrwx 3 root root 4096 Sep 14 09:09 html
root@csi-rbd-demo-pod:/var/lib/www# echo "你好!" > wu123.txt
wu123
```
列出 kubernetes pool 中的 rbd images:
```cassandraql
rbd ls -p kubernetes
csi-vol-fe40eb16-1a4e-11ed-bb7c-0eb2f382cefd
```

查看该 image 的特征:

```cassandraql
[root@ceph01 ceph]# rbd info csi-vol-fe40eb16-1a4e-11ed-bb7c-0eb2f382cefd -p kubernetes
rbd image 'csi-vol-fe40eb16-1a4e-11ed-bb7c-0eb2f382cefd':
        size 1 GiB in 256 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: d50f18182e6b
        block_name_prefix: rbd_data.d50f18182e6b
        format: 2
        features: layering
        op_features: 
        flags: 
        create_timestamp: Fri Aug 12 22:56:49 2022
        access_timestamp: Fri Aug 12 22:56:49 2022
        modify_timestamp: Fri Aug 12 22:56:49 2022
```

到此,k8s 使用外部 ceph 存储配置结束

9-11 Go PaaS 平台PVC 模型开发 

yu-tool.exe  newService github.com/yunixiangfeng/gopaas/volume

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/volume/volume.proto
go mod tidy

C:\Users\Administrator\Desktop\gopaas\volume\domain\model\volume.go

package model

type Volume struct{
	ID int64 `gorm:"primary_key;not_null;auto_increment"`
	//存储的名称
	VolumeName string `json:"volume_name"`
	//存储的所属的命名空间
	VolumeNamespace string `json:"volume_namespace"`
	//存储的访问模式,RWO,ROX,RWX
	VolumeAccessMode string `json:"volume_access_mode"`
	//sc 的 class name
	VolumeStorageClassName string `json:"volume_storage_class_name"`
	//请求资源的大小
	VolumeRequest float32 `json:"volume_request"`
	//存储类型 Block,filesystem
	VolumePersistentVolumeMode string `json:"volume_persistent_volume_mode"`
}

 C:\Users\Administrator\Desktop\gopaas\volume\proto\volume\volume.proto

syntax = "proto3";

package volume;

option go_package = "./proto/volume;volume";

service Volume {
	//对外提供添加服务
	rpc AddVolume(VolumeInfo) returns (Response) {}
	rpc DeleteVolume(VolumeId) returns (Response) {}
	rpc UpdateVolume(VolumeInfo) returns (Response) {}
	rpc FindVolumeByID(VolumeId) returns (VolumeInfo) {}
	rpc FindAllVolume(FindAll) returns (AllVolume) {}
}
message VolumeInfo {
	int64 id = 1;
	string volume_name=2;
	string volume_namespace=3;
	string volume_access_mode=4;
	string volume_storage_class_name=5;
	float volume_request=6;
	string volume_persistent_volume_mode=7;
}

message VolumeId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllVolume {
	repeated VolumeInfo volume_info = 1;
}


9-12 Go PaaS 平台 Service 开发(上) 

C:\Users\Administrator\Desktop\gopaas\volume\domain\service\volume_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volume/domain/model"
	"github.com/yunixiangfeng/gopaas/volume/domain/repository"
	"github.com/yunixiangfeng/gopaas/volume/proto/volume"
	"k8s.io/api/apps/v1"
	v12 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v13 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IVolumeDataService interface {
	AddVolume(*model.Volume) (int64 , error)
	DeleteVolume(int64) error
	UpdateVolume(*model.Volume) error
	FindVolumeByID(int64) (*model.Volume, error)
	FindAllVolume() ([]model.Volume, error)

	CreateVolumeToK8s(*volume.VolumeInfo) error
	DeleteVolumeFromK8s(*model.Volume) error
}


//创建
//注意:返回值 IVolumeDataService 接口类型
func NewVolumeDataService(volumeRepository repository.IVolumeRepository,clientSet *kubernetes.Clientset) IVolumeDataService{
	return &VolumeDataService{ VolumeRepository:volumeRepository, K8sClientSet: clientSet,deployment:&v1.Deployment{}}
}

type VolumeDataService struct {
    //注意:这里是 IVolumeRepository 类型
	VolumeRepository repository.IVolumeRepository
	K8sClientSet  *kubernetes.Clientset
	deployment  *v1.Deployment
}
//从 k8s 删除一个 pvc
func (u *VolumeDataService) DeleteVolumeFromK8s(volume *model.Volume) (err error) {
	//先从K8s 中删除
	if err = u.K8sClientSet.CoreV1().PersistentVolumeClaims(volume.VolumeNamespace).Delete(context.TODO(),volume.VolumeName, v13.DeleteOptions{});err!=nil {
		common.Error(err)
		return err
	} else {
		//从数据表中删除
		if err := u.DeleteVolume(volume.ID);err !=nil {
			common.Error(err)
			return err
		}
		common.Info("删除存储ID"+ strconv.FormatInt(volume.ID,10)+" 成功!")
	}
	return
}

//创建存储到 k8s 中
func (u *VolumeDataService) CreateVolumeToK8s(info *volume.VolumeInfo)(err error) {
	volume := u.setVolume(info)
	if _,err = u.K8sClientSet.CoreV1().PersistentVolumeClaims(info.VolumeNamespace).Get(context.TODO(),info.VolumeName,v13.GetOptions{});err !=nil {
		//如果存储不存在
		if _,err = u.K8sClientSet.CoreV1().PersistentVolumeClaims(info.VolumeNamespace).Create(context.TODO(),volume,v13.CreateOptions{});err !=nil{
			common.Error(err)
			return err
		}
		common.Info("存储创建成功")
		return nil
	}else {
		common.Error("存储空间"+ info.VolumeName +" 已经存在")
		return errors.New("存储空间"+ info.VolumeName +" 已经存在")
	}
}
//设置 pvc 的详情信息
func (u *VolumeDataService) setVolume(info *volume.VolumeInfo) *v12.PersistentVolumeClaim {
	pvc := &v12.PersistentVolumeClaim{}
	//设置接口类型
	pvc.TypeMeta = v13.TypeMeta{
		Kind:       "PersistentVolumeClaim",
		APIVersion: "v1",
	}
	//设置存储基础信息
	pvc.ObjectMeta = v13.ObjectMeta{
		Name:                       info.VolumeName,
		Namespace:                  info.VolumeNamespace,
		Annotations: map[string]string{
			"pv.kubernetes.io/bound-by-controller":"yes",
			"volume.beta.kubernetes.io/storage-provisioner":"rbd.csi.ceph.com",
			"wu":"wu123",
		},
	}
	//设置存储动态信息
	pvc.Spec = v12.PersistentVolumeClaimSpec{
		AccessModes:      u.getAccessModes(info),
		Resources:        u.getResource(info),
		StorageClassName: &info.VolumeStorageClassName,
		VolumeMode:       u.getVolumeMode(info),
	}
	return pvc

}

//获取存储类型
func (u *VolumeDataService) getVolumeMode(info *volume.VolumeInfo) *v12.PersistentVolumeMode {
	var pvm v12.PersistentVolumeMode
	switch info.VolumePersistentVolumeMode {
	case "Block":
		pvm = v12.PersistentVolumeBlock
	case "Filesystem":
		pvm = v12.PersistentVolumeFilesystem
	default:
		pvm = v12.PersistentVolumeFilesystem
	}
	return &pvm
}

//插入
func (u *VolumeDataService) AddVolume(volume *model.Volume) (int64 ,error) {
	 return u.VolumeRepository.CreateVolume(volume)
}

//删除
func (u *VolumeDataService) DeleteVolume(volumeID int64) error {
	return u.VolumeRepository.DeleteVolumeByID(volumeID)
}

//更新
func (u *VolumeDataService) UpdateVolume(volume *model.Volume) error {
	return u.VolumeRepository.UpdateVolume(volume)
}

//查找
func (u *VolumeDataService) FindVolumeByID(volumeID int64) (*model.Volume, error) {
	return u.VolumeRepository.FindVolumeByID(volumeID)
}

//查找
func (u *VolumeDataService) FindAllVolume() ([]model.Volume, error) {
	return u.VolumeRepository.FindAll()
}

9-13 Go PaaS 平台 Service 开发(下) 

C:\Users\Administrator\Desktop\gopaas\volume\domain\service\volume_data_service.go

//获取资源配置
func (u *VolumeDataService) getResource (info *volume.VolumeInfo)(source v12.ResourceRequirements)  {
	source.Requests = v12.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(info.VolumeRequest),'f',6,64)+"Gi"),
	}
	return
}

//获取访问模式
func (u *VolumeDataService) getAccessModes(info *volume.VolumeInfo)(pvam []v12.PersistentVolumeAccessMode)  {
	var pm v12.PersistentVolumeAccessMode
	switch info.VolumeAccessMode {
	case "ReadWriteOnce":
		pm = v12.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v12.ReadOnlyMany
	case "ReadWriteMany":
		pm = v12.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v12.ReadWriteOncePod
	default:
		pm = v12.ReadWriteOnce
	}
	pvam = append(pvam,pm)
	return pvam
	
}

9-14 Go PaaS 平台 PV与PVC 的关系和原理说明 

PV和PVC的关系
PV (Persistent Volume): 持久化卷的意思,是对底层的共享存储的一种抽象
PVC (Persistent Volume Claim): 用户存储的请求

PV的访问模式(accessModes
ReadWriteOnce(RWO): 可读可写,单挂载
ReadOnlyMany(ROX): 只读,多挂载
ReadWriteMany(RWX): 可读可写,多挂载

PV 的回收策略(persistentVolumeReclaimPolicy)
Retain:不清理,保留Volume(需要手动清理
Recycle:会删除数据,类似 rm -rf /xxx/*
Delete:删除存储资源

PV的状态
Available :可用
Bound :已经分配给PVC
Released:PVC 解绑但是还未执行回收策略
Failed:存储异常

PVC的关键参数
访问模式(accessModes):标明访问权限选择器(selector):label选择筛选绑定PV
存储类别 (storageClassName): 设置用来自动创建PV
请求资源 (Resources) :标明需要的存储大小

PV与PVC的生命周期:Provisioning 配置阶段
静态:管理员创建多个PV,属性确定,绑定了真实的存储设。
动态:通过StorageClass,k8s 会创建动态的为PVC 创建PV好处时候资源能够除非利用。创建模式自动。

PV与PVC的生命周期:Binding
在动态配置的情况下,用户创建了PVC后,标明PVC与PV绑
定过程
如果没有满足PVC请求的PV,PVC将无法被创建。
如果绑定了PVC 绑定了Pod,pod将无法创建

PV与PVC的生命周期
Using:PVC与PV绑定后,Pod对存储空间正常使用
Releasing:当Pod被删除或者PV的资源使用结束后
Reclaiming:PV的回收策略对被释放的PV的处理
Recycling: PV被执行擦除后再被分配 

9-15 Go PaaS 平台 Service Handler 开发 

C:\Users\Administrator\Desktop\gopaas\volume\handler\volumeHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volume/domain/model"
	"github.com/yunixiangfeng/gopaas/volume/domain/service"
	volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"
)

type VolumeHandler struct {
	//注意这里的类型是 IVolumeDataService 接口类型
	VolumeDataService service.IVolumeDataService
}

// Call is a single request handler called via client.Call or the generated client code
func (e *VolumeHandler) AddVolume(ctx context.Context, info *volume.VolumeInfo, rsp *volume.Response) error {
	log.Info("Received *volume.AddVolume request")
	volume := &model.Volume{}
	if err := common.SwapTo(info, volume); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//创建volume
	if err := e.VolumeDataService.CreateVolumeToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//写入数据库
		volumeID, err := e.VolumeDataService.AddVolume(volume)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		rsp.Msg = "volume 添加成功 ID 号为:" + strconv.FormatInt(volumeID, 10)
	}
	return nil
}

//删除
func (e *VolumeHandler) DeleteVolume(ctx context.Context, req *volume.VolumeId, rsp *volume.Response) error {
	log.Info("Received *volume.DeleteVolume request")
	volumModel, err := e.VolumeDataService.FindVolumeByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//从k8s中删除,并且删除数据库
	if err := e.VolumeDataService.DeleteVolumeFromK8s(volumModel); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *VolumeHandler) UpdateVolume(ctx context.Context, req *volume.VolumeInfo, rsp *volume.Response) error {
	log.Info("Received *volume.UpdateVolume request")
	return nil
}

//根据 ID 查找 volume
func (e *VolumeHandler) FindVolumeByID(ctx context.Context, req *volume.VolumeId, rsp *volume.VolumeInfo) error {
	log.Info("Received *volume.FindVolumeByID request")
	volumeModel, err := e.VolumeDataService.FindVolumeByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	//数据转化
	if err := common.SwapTo(volumeModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *VolumeHandler) FindAllVolume(ctx context.Context, req *volume.FindAll, rsp *volume.AllVolume) error {
	log.Info("Received *volume.FindAllVolume request")
	allVolume, err := e.VolumeDataService.FindAllVolume()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allVolume {
		//创建实例
		volumeInfo := &volume.VolumeInfo{}
		//数据转化
		if err := common.SwapTo(v, volumeInfo); err != nil {
			common.Error(err)
			return err
		}
		//数据合并
		rsp.VolumeInfo = append(rsp.VolumeInfo, volumeInfo)
	}
	return nil
}

C:\Users\Administrator\Desktop\gopaas\volume\main.go

package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volume/domain/repository"

	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	service2 "github.com/yunixiangfeng/gopaas/volume/domain/service"
	"github.com/yunixiangfeng/gopaas/volume/handler"

	//hystrix2 "github.com/yunixiangfeng/gopaas/volume/plugin/hystrix"
	"strconv"

	volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8087"

	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	//hystrixPort = 9097
	//监控端口,每个服务不能重复
	prometheusPort = 9197
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	//3.使用配置中心连接 mysql
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	//初始化数据库
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		//命令行输出下,方便查看错误
		fmt.Println(err)
		common.Fatal(err)
	}
	defer db.Close()
	//禁止复表
	db.SingularTable(true)

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.volume", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//添加熔断器,作为客户端需要启用
	//hystrixStreamHandler := hystrix.NewStreamHandler()
	//hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	//go func() {
	//	//http://192.168.204.130:9092/turbine/turbine.stream
	//	//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
	//	err = http.ListenAndServe(net.JoinHostPort("0.0.0.0",strconv.Itoa(hystrixPort)),hystrixStreamHandler)
	//	if err !=nil {
	//		common.Error(err)
	//	}
	//}()

	//5.添加监控
	common.PrometheusBoot(prometheusPort)

	//下载kubectl:https://kubernetes.io/docs/tasks/tools/#tabset-2
	//macos:
	// 1.curl -LO "https://dl.k8s.io/release/v1.21.0/bin/darwin/amd64/kubectl"
	// 2.chmod +x ./kubectl
	// 3.sudo mv ./kubectl /usr/local/bin/kubectl
	//   sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//6.创建k8s连接
	//在集群外面连接
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中外的配置
	//config, err := rest.InClusterConfig()
	//if err != nil {
	//	panic(err.Error())
	//}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.volume"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用
		//micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
	)

	service.Init()

	//只能执行一遍
	//err = repository.NewVolumeRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	volumeDataService := service2.NewVolumeDataService(repository.NewVolumeRepository(db), clientset)
	volume.RegisterVolumeHandler(service.Server(), &handler.VolumeHandler{VolumeDataService: volumeDataService})

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

9-16 Go PaaS 平台 volume api 开发 

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  createApi github.com/yunixiangfeng/gopaas/volumeApi

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/volumeApi/volumeApi.proto

go mod tidy

C:\Users\Administrator\Desktop\gopaas\volumeapi\handler\volumeApiHandler.go

package handler

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"
	"github.com/yunixiangfeng/gopaas/volumeApi/plugin/form"
	volumeApi "github.com/yunixiangfeng/gopaas/volumeApi/proto/volumeApi"
)

type VolumeApi struct {
	VolumeService volume.VolumeService
}

// volumeApi.FindVolumeById 通过API向外暴露为/volumeApi/findVolumeById,接收http请求
// 即:/volumeApi/FindVolumeById 请求会调用go.micro.api.volumeApi 服务的volumeApi.FindVolumeById 方法
func (e *VolumeApi) FindVolumeById(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.FindVolumeById request")
	if _, ok := req.Get["volume_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数有异常")
	}
	//获取 volume_id
	volumeIdString := req.Get["volume_id"].Values[0]
	volumeId, err := strconv.ParseInt(volumeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//获取 volume 信息
	volumeInfo, err := e.VolumeService.FindVolumeByID(ctx, &volume.VolumeId{
		Id: volumeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(volumeInfo)
	rsp.Body = string(b)
	return nil
}

// volumeApi.AddVolume 通过API向外暴露为/volumeApi/AddVolume,接收http请求
// 即:/volumeApi/AddVolume 请求会调用go.micro.api.volumeApi 服务的volumeApi.AddVolume 方法
func (e *VolumeApi) AddVolume(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.AddVolume request")
	addVolumeInfo := &volume.VolumeInfo{}
	form.FormToSvcStruct(req.Post, addVolumeInfo)
	response, err := e.VolumeService.AddVolume(ctx, addVolumeInfo)
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// volumeApi.DeleteVolumeById 通过API向外暴露为/volumeApi/DeleteVolumeById,接收http请求
// 即:/volumeApi/DeleteVolumeById 请求会调用go.micro.api.volumeApi 服务的 volumeApi.DeleteVolumeById 方法
func (e *VolumeApi) DeleteVolumeById(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.DeleteVolumeById request")
	if _, ok := req.Get["volume_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 volume_id
	volumeIdString := req.Get["volume_id"].Values[0]
	volumeId, err := strconv.ParseInt(volumeIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//调用 volume 删除服务
	response, err := e.VolumeService.DeleteVolume(ctx, &volume.VolumeId{
		Id: volumeId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// volumeApi.UpdateVolume 通过API向外暴露为/volumeApi/UpdateVolume,接收http请求
// 即:/volumeApi/UpdateVolume 请求会调用go.micro.api.volumeApi 服务的volumeApi.UpdateVolume 方法
func (e *VolumeApi) UpdateVolume(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.UpdateVolume request")
	rsp.StatusCode = 200
	b, _ := json.Marshal("{success:'成功访问/volumeApi/UpdateVolume'}")
	rsp.Body = string(b)
	return nil
}

// 默认的方法volumeApi.Call 通过API向外暴露为/volumeApi/call,接收http请求
// 即:/volumeApi/call或/volumeApi/ 请求会调用go.micro.api.volumeApi 服务的volumeApi.FindVolumeById 方法
func (e *VolumeApi) Call(ctx context.Context, req *volumeApi.Request, rsp *volumeApi.Response) error {
	log.Info("Received volumeApi.Call request")
	allVolume, err := e.VolumeService.FindAllVolume(ctx, &volume.FindAll{})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(allVolume)
	rsp.Body = string(b)
	return nil
}

C:\Users\Administrator\Desktop\gopaas\volumeapi\plugin\form\form.go

package form

import (
	"errors"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/volumeApi/proto/volumeApi"
)

//根据结构体中name标签映射数据到结构体中并且转换类型
func FormToSvcStruct(data map[string]*volumeApi.Pair, obj interface{}) {
	objValue := reflect.ValueOf(obj).Elem()
	for i := 0; i < objValue.NumField(); i++ {
		//获取sql对应的值
		dataTag := strings.Replace(objValue.Type().Field(i).Tag.Get("json"), ",omitempty", "", -1)
		dataSlice, ok := data[dataTag]
		if !ok {
			continue
		}
		valueSlice := dataSlice.Values
		if len(valueSlice) <= 0 {
			continue
		}
		//排除port和env
		if dataTag == "route_path" {
			continue
		}
		value := valueSlice[0]
		//端口,环境变量的单独处理
		//获取对应字段的名称
		name := objValue.Type().Field(i).Name
		//获取对应字段类型
		structFieldType := objValue.Field(i).Type()
		//获取变量类型,也可以直接写"string类型"
		val := reflect.ValueOf(value)
		var err error
		if structFieldType != val.Type() {
			//类型转换
			val, err = TypeConversion(value, structFieldType.Name()) //类型转换
			if err != nil {
				common.Error(err)
			}
		}
		//设置类型值
		objValue.FieldByName(name).Set(val)
	}
}

//类型转换
func TypeConversion(value string, ntype string) (reflect.Value, error) {
	if ntype == "string" {
		return reflect.ValueOf(value), nil
	} else if ntype == "time.Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "int" {
		i, err := strconv.Atoi(value)
		return reflect.ValueOf(i), err
	} else if ntype == "int32" {
		i, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return reflect.ValueOf(int32(i)), err
		}
		return reflect.ValueOf(int32(i)), err
	} else if ntype == "int64" {
		i, err := strconv.ParseInt(value, 10, 64)
		return reflect.ValueOf(i), err
	} else if ntype == "float32" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(float32(i)), err
	} else if ntype == "float64" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(i), err
	}

	//else if .......增加其他一些类型的转换

	return reflect.ValueOf(value), errors.New("未知的类型:" + ntype)
}

 C:\Users\Administrator\Desktop\gopaas\volumeapi\main.go

package main

import (
	"fmt"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	"github.com/asim/go-micro/plugins/wrapper/select/roundrobin/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/yunixiangfeng/gopaas/common"
	go_micro_service_volume "github.com/yunixiangfeng/gopaas/volume/proto/volume"

	"net"
	"net/http"
	"strconv"

	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/gopaas/volumeApi/handler"
	hystrix2 "github.com/yunixiangfeng/gopaas/volumeApi/plugin/hystrix"

	volumeApi "github.com/yunixiangfeng/gopaas/volumeApi/proto/volumeApi"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8087"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	hystrixPort = 9097
	//监控端口,每个服务不能重复
	prometheusPort = 9197
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})

	//2.添加链路追踪
	t, io, err := common.NewTracer("go.micro.api.volumeApi", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//3.添加熔断器
	hystrixStreamHandler := hystrix.NewStreamHandler()
	hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	go func() {
		//http://192.168.204.130:9092/turbine/turbine.stream
		//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
		err = http.ListenAndServe(net.JoinHostPort("0.0.0.0", strconv.Itoa(hystrixPort)), hystrixStreamHandler)
		if err != nil {
			common.Error(err)
		}
	}()

	//4.添加监控
	common.PrometheusBoot(prometheusPort)

	//5.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(opts *server.Options) {
			opts.Advertise = serviceHost + ":" + servicePort

		})),
		micro.Name("go.micro.api.volumeApi"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用
		micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
		//增加负载均衡
		micro.WrapClient(roundrobin.NewClientWrapper()),
	)

	service.Init()

	// 指定需要访问的服务,可以快速操作已开发的服务,
	// 默认API服务名称带有"Api",程序会自动替换
	// 如果不带有特定字符会使用默认"XXX" 请自行替换
	volumeService := go_micro_service_volume.NewVolumeService("go.micro.service.volume", service.Client())
	// 注册控制器
	if err := volumeApi.RegisterVolumeApiHandler(service.Server(), &handler.VolumeApi{VolumeService: volumeService}); err != nil {
		common.Error(err)
	}

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

file:///home/gopath/src/gopaas/go-paas-front/volume-create.html 

9-17 总结&思考

主要内容
Ceph 基础概念介绍
Ceph 架构说明及安装
服务后端功能开发

经验之谈
Ceph 在创建支出需要规划好 PG 数量
分布式存储为了数据不丢失和高可用建议最小保留2个副本

运行过程中重要的数据操作前打快照

Ceph 单个节点宕机数据恢复过程是什么?
Ceph 的监控有哪些维度是要关心的?

9-18 【扩展阅读】基于kubernetes-1.21.5使用Rook方式部署Ceph

第10章 云原生 Go PaaS 平台中间件后端管理服务,动态创建中间件

研发过程中通常有创建不同中间件的需求,能够提供开箱即用的提供中间件资源,是一件非常高效的方式,学习mysql 的中间件的创建和管理,并且通过中间件挂载分布式存储来满足数据落盘的需求。

10-1 Go 云原生PaaS平台中间件model–middleware创建

GO PaaS 平台中间件创建与管理

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  newService github.com/yunixiangfeng/gopaas/middleware

yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/middleware/middleware.proto

go mod tidy

 middleware\domain\model\middleware.go

package model

type Middleware struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment"`
	//中间件的名称
	MiddleName string `json:"middle_name"`
	//中间件创建的命名空间
	MiddleNamespace string `json:"middle_namespace"`
	//中间件的类型
	MiddleTypeID int64 `json:"middle_type_id"`
	//中间件的版本
	MiddleVersionID int64 `json:"middle_version_id"`
	//中间件的端口
	MiddlePort []MiddlePort `gorm:"ForeignKey:MiddleID" json:"middle_port"`
	//默认生成的账号密码
	MiddleConfig MiddleConfig `gorm:"ForeignKey:MiddleID" json:"middle_config"`
	//环境变量
	MiddleEnv []MiddleEnv `gorm:"ForeignKey:MiddleID" json:"middle_env"`
	//中间件的CPU
	MiddleCpu float32 `json:"middle_cpu"`
	//中间件内存
	MiddleMemory float32 `json:"middle_memory"`
	//中间件存储
	MiddleStorage []MiddleStorage `gorm:"ForeignKey:MiddleID" json:"middle_storage"`
	//中间件副本
	MiddleReplicas int32 `json:"middle_replicas"`
}

10-2 Go PAAS平台中间件model-middle_port创建 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_port.go

package model

type MiddlePort struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//主要用来关联中间件的ID
	MiddleID int64 `json:"middle_id"`
	//中间件开放的端口
	MiddlePort int32 `json:"middle_port"`
	//中间件开放的端口协议
	MiddleProtocol string `json:"middle_protocol"`
}

10-3 中间件model-middle_env创建

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_env.go

package model

//中间件的变量
type MiddleEnv struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//关联的环境变量ID
	MiddleID int64 `json:"middle_id"`
	//环境变量key
	EnvKey string `json:"env_key"`
	//环境变量Value
	EnvValue string `json:"env_value"`
}

10-4 中间件model-MiddleConfig创建

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_config.go

package model

//中间件配置的结构体
type MiddleConfig struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//关联的中间件ID
	MiddleID int64 `json:"middle_id"`
	//可能存在的root 用户
	MiddleConfigRootUser string `json:"middle_config_root_user"`
	//可能存在的root 密码
	MiddleConfigRootPwd string `json:"middle_config_root_pwd"`
	//可能存在的普通用户
	MiddleConfigUser string `json:"middle_config_user"`
	//普通用户的密码
	MiddleConfigPwd string `json:"middle_config_pwd"`
	//预置数据库名称
	MiddleConfigDataBase string `json:"middle_config_data_base"`
	//其它设置
}

10-5 中间件model-MiddleStorage创建 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_storage.go

package model

//中间件的存储盘
type MiddleStorage struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//关联的中间件ID
	MiddleID int64 `json:"middle_id"`
	//存储名称
	MiddleStorageName string `json:"middle_storage_name"`
	//存储的大小
	MiddleStorageSize float32 `json:"middle_storage_size"`
	//存储需要挂载的目录
	MiddleStoragePath string `json:"middle_storage_path"`
	//存储创建的类型
	MiddleStorageClass string `json:"middle_storage_class"`
	//存储的权限
	MiddleStorageAccessMode string `json:"middle_storage_access_mode"`
}

10-6 中间件类型type 和 version创建

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_type.go

package model

//中间件类型
type MiddleType struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	//中间件类型名称
	MiddleTypeName string `json:"middle_type_name"`
	//中间件图片地址
	MiddleTypeImageSrc string `json:"middle_type_image_src"`
	//中间件的版本
	MiddleVersion[] MiddleVersion `gorm:"ForeignKey:MiddleTypeID" json:"middle_version"`
}

C:\Users\Administrator\Desktop\gopaas\middleware\domain\model\middle_version.go

package model

type MiddleVersion struct {
	ID           int64 `gorm:"primary_key;not_null;auto_increment" json:"id"`
	MiddleTypeID int64 `json:"middle_type_id"`
	//镜像地址
	MiddleDockerImage string `json:"middle_docker_image"`
	//镜像版本
	MiddleVS string `json:"middle_vs"`
	//MiddleDockerImage:MiddleVS
	//其它
}

10-7 中间件 middleware repository开发 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\repository\middleware_repository.go

package repository

import (
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
)

//创建需要实现的接口
type IMiddlewareRepository interface {
	//初始化表
	InitTable() error
	//根据ID查处找数据
	FindMiddlewareByID(int64) (*model.Middleware, error)
	//创建一条 middleware 数据
	CreateMiddleware(*model.Middleware) (int64, error)
	//根据ID删除一条 middleware 数据
	DeleteMiddlewareByID(int64) error
	//修改更新数据
	UpdateMiddleware(*model.Middleware) error
	//查找middleware所有数据
	FindAll() ([]model.Middleware, error)

	//根据类型查找所有中间件
	FindAllByTypeID(int64) ([]model.Middleware, error)
}

//创建middlewareRepository
func NewMiddlewareRepository(db *gorm.DB) IMiddlewareRepository {
	return &MiddlewareRepository{mysqlDb: db}
}

type MiddlewareRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *MiddlewareRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.Middleware{}, &model.MiddleConfig{}, &model.MiddlePort{}, &model.MiddleEnv{}, &model.MiddleStorage{}).Error
}

//根据ID查找Middleware信息
func (u *MiddlewareRepository) FindMiddlewareByID(middlewareID int64) (middleware *model.Middleware, err error) {
	middleware = &model.Middleware{}
	//要多个则添加 Preload
	return middleware, u.mysqlDb.First(middleware, middlewareID).Error
}

//创建Middleware信息
func (u *MiddlewareRepository) CreateMiddleware(middleware *model.Middleware) (int64, error) {
	return middleware.ID, u.mysqlDb.Create(middleware).Error
}

//根据ID删除Middleware信息
func (u *MiddlewareRepository) DeleteMiddlewareByID(middlewareID int64) error {
	//开启事物
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	//遇到错误返回
	if tx.Error != nil {
		return tx.Error
	}
	//删除中间件
	if err := u.mysqlDb.Where("id = ?", middlewareID).Delete(&model.Middleware{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除中间件的配置
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddleConfig{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除端口
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddlePort{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除中间件环境变量
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddleEnv{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//删除中间件存储
	if err := u.mysqlDb.Where("middle_id = ?", middlewareID).Delete(&model.MiddleStorage{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

//更新Middleware信息
func (u *MiddlewareRepository) UpdateMiddleware(middleware *model.Middleware) error {
	return u.mysqlDb.Model(middleware).Update(middleware).Error
}

//获取结果集
func (u *MiddlewareRepository) FindAll() (middlewareAll []model.Middleware, err error) {
	//要多个则添加 Preload
	return middlewareAll, u.mysqlDb.Find(&middlewareAll).Error
}

func (u *MiddlewareRepository) FindAllByTypeID(typeID int64) (middlewareAll []model.Middleware, err error) {
	//要多个则添加 Preload
	return middlewareAll, u.mysqlDb.Where("middle_type_id = ?", typeID).Find(&middlewareAll).Error
}

10-8 中间件类型type和verison repository开发 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\repository\middle_type_repository.go

package repository

import (
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
)

//创建需要实现的接口
type IMiddleTypeRepository interface {
	//初始化表
	InitTable() error
	//根据ID查处找数据
	FindTypeByID(int64) (*model.MiddleType, error)
	//创建一条 middleware 数据
	CreateMiddleType(*model.MiddleType) (int64, error)
	//根据ID删除一条 middleware 数据
	DeleteMiddleTypeByID(int64) error
	//修改更新数据
	UpdateMiddleType(*model.MiddleType) error
	//查找middleware所有数据
	FindAll() ([]model.MiddleType, error)

	FindVersionByID(int64) (*model.MiddleVersion, error)
	FindAllVersionByTypeID(int64) ([]model.MiddleVersion, error)
}

//创建MiddleTypeRepository
func NewMiddleTypeRepository(db *gorm.DB) IMiddleTypeRepository {
	return &MiddleTypeRepository{mysqlDb: db}
}

type MiddleTypeRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *MiddleTypeRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.MiddleType{}, &model.MiddleVersion{}).Error
}

//按照 ID 查找中间件类型
func (u *MiddleTypeRepository) FindTypeByID(middleTypeID int64) (middleType *model.MiddleType, err error) {
	middleType = &model.MiddleType{}
	return middleType, u.mysqlDb.Preload("MiddleVersion").First(middleType, middleTypeID).Error
}

//创建中间件
func (u *MiddleTypeRepository) CreateMiddleType(middleType *model.MiddleType) (int64, error) {
	return middleType.ID, u.mysqlDb.Create(middleType).Error
}

//删除中间件
func (u *MiddleTypeRepository) DeleteMiddleTypeByID(middleTypeID int64) error {
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	//遇到错误返回
	if tx.Error != nil {
		return tx.Error
	}
	//删除中间件类型
	if err := u.mysqlDb.Where("id = ?", middleTypeID).Delete(&model.MiddleType{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	//开始删除版本
	if err := u.mysqlDb.Where("middle_type_id = ?", middleTypeID).Delete(&model.MiddleVersion{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

//更新middleware 信息
func (u *MiddleTypeRepository) UpdateMiddleType(middleType *model.MiddleType) error {
	return u.mysqlDb.Model(middleType).Update(middleType).Error
}

//获取类型的结果集
func (u *MiddleTypeRepository) FindAll() (middleTypeAll []model.MiddleType, err error) {
	return middleTypeAll, u.mysqlDb.Find(&middleTypeAll).Error
}

//根据ID查找单个版本
func (u *MiddleTypeRepository) FindVersionByID(middleVersionID int64) (middleVersion *model.MiddleVersion, err error) {
	middleVersion = &model.MiddleVersion{}
	return middleVersion, u.mysqlDb.First(middleVersion, middleVersionID).Error
}

//根据中间件类型查找所有版本
func (u *MiddleTypeRepository) FindAllVersionByTypeID(middleTypeID int64) (middleVersionAll []model.MiddleVersion, err error) {
	return middleVersionAll, u.mysqlDb.Where("middle_type_id = ?", middleTypeID).Find(&middleVersionAll).Error
}

10-9 中间件 proto 文件开发 

C:\Users\Administrator\Desktop\gopaas\middleware\proto\middleware\middleware.proto

syntax = "proto3";

package middleware;

option go_package = "./proto/middleware;middleware";

service Middleware {
	//对外提供添加服务
	rpc AddMiddleware(MiddlewareInfo) returns (Response) {}
	rpc DeleteMiddleware(MiddlewareId) returns (Response) {}
	rpc UpdateMiddleware(MiddlewareInfo) returns (Response) {}
	rpc FindMiddlewareByID(MiddlewareId) returns (MiddlewareInfo) {}
	rpc FindAllMiddleware(FindAll) returns (AllMiddleware) {}
	//根据中间件的类型查找所有中间件
	rpc FindAllMiddlewareByTypeID(FindAllByTypeId) returns (AllMiddleware){}
	//获取中间件类型
	rpc FindMiddleTypeByID(MiddleTypeId) returns (MiddleTypeInfo){}
	rpc AddMiddleType(MiddleTypeInfo) returns (Response){}
	rpc DeleteMiddleTypeByID(MiddleTypeId) returns (Response){}
	rpc UpdateMiddleType(MiddleTypeInfo) returns(Response){}
	rpc FindAllMiddleType(FindAll) returns (AllMiddleType){}
}

message MiddlewareInfo {
	int64 id = 1;
	string middle_name = 2;
	string middle_namespace =3;
	int64 middle_type_id = 4;
	int64 middle_version_id =5;
	repeated MiddlePort middle_port=6;
	MiddleConfig middle_config=7;
	repeated MiddleEnv middle_env=8;
	float middle_cpu=9;
	float middle_memory=10;
	repeated MiddleStorage middle_storage =11;
	int32 middle_replicas = 12;
	//添加需要的镜像版本
	string middle_docker_image_version=13;
}
//中间件的端口
message MiddlePort {
	int64 middle_id=1;
	int32 middle_port=2;
	string middle_protocol=3;
}
//中间的配置
message MiddleConfig{
	int64 middle_id =1;
	string middle_config_root_user =2;
	string middle_config_root_pwd =3;
	string middle_config_user =4;
	string middle_config_pwd =5;
	string middle_config_data_base =6;
}
//中间件环境变量
message MiddleEnv {
	int64 middle_id=1;
	string env_key=2;
	string env_value=3;
}

//中间件存储
message MiddleStorage{
	int64 middle_id =1;
	string middle_storage_name=2;
	float middle_storage_size=3;
	string middle_storage_path=4;
	string middle_storage_class=5;
	string middle_storage_access_mode=6;
}

message FindAllByTypeId {
    int64 type_id =1;
}

message MiddleTypeId {
	int64 id=1;
}

message MiddlewareId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllMiddleware {
	repeated MiddlewareInfo middleware_info = 1;
}

message MiddleTypeInfo {
	int64 id =1;
	string middle_type_name=2;
	string middle_type_image_src=3;
	repeated MiddleVersion middle_version=4;
}

message MiddleVersion{
	int64 middle_type_id =1;
	string middle_docker_image=2;
	string middle_vs=3;
}

message AllMiddleType{
	repeated MiddleTypeInfo middle_type_info =1;
}


 yu-v3 --proto_path=. --micro_out=. --go_out=:. ./proto/middleware/middleware.proto  

10-10 中间件 service 开发(1) 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

10-11 中间件 service 开发(2) 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

//获取中间件的环境变量
func (u *MiddlewareDataService) getEnv(info *middleware.MiddlewareInfo) (envVar []v13.EnvVar) {
	for _, v := range info.MiddleEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//获取中间件需要的资源
func (u *MiddlewareDataService) getResources(info *middleware.MiddlewareInfo) (source v13.ResourceRequirements) {
	//最大能够使用的资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	//最小请求资源
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	return
}

//插入
func (u *MiddlewareDataService) AddMiddleware(middleware *model.Middleware) (int64, error) {
	return u.MiddlewareRepository.CreateMiddleware(middleware)
}

//删除
func (u *MiddlewareDataService) DeleteMiddleware(middlewareID int64) error {
	return u.MiddlewareRepository.DeleteMiddlewareByID(middlewareID)
}

//更新
func (u *MiddlewareDataService) UpdateMiddleware(middleware *model.Middleware) error {
	return u.MiddlewareRepository.UpdateMiddleware(middleware)
}

//查找
func (u *MiddlewareDataService) FindMiddlewareByID(middlewareID int64) (*model.Middleware, error) {
	return u.MiddlewareRepository.FindMiddlewareByID(middlewareID)
}

//查找
func (u *MiddlewareDataService) FindAllMiddleware() ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAll()
}

//根据类型查找所有的中间件
func (u *MiddlewareDataService) FindAllMiddlewareByTypeID(typeID int64) ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAllByTypeID(typeID)
}

10-12 中间件middleware service开发(3)

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

//获取中间件的环境变量
func (u *MiddlewareDataService) getEnv(info *middleware.MiddlewareInfo) (envVar []v13.EnvVar) {
	for _, v := range info.MiddleEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//获取中间件需要的资源
func (u *MiddlewareDataService) getResources(info *middleware.MiddlewareInfo) (source v13.ResourceRequirements) {
	//最大能够使用的资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	//最小请求资源
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	return
}

//插入
func (u *MiddlewareDataService) AddMiddleware(middleware *model.Middleware) (int64, error) {
	return u.MiddlewareRepository.CreateMiddleware(middleware)
}

//删除
func (u *MiddlewareDataService) DeleteMiddleware(middlewareID int64) error {
	return u.MiddlewareRepository.DeleteMiddlewareByID(middlewareID)
}

//更新
func (u *MiddlewareDataService) UpdateMiddleware(middleware *model.Middleware) error {
	return u.MiddlewareRepository.UpdateMiddleware(middleware)
}

//查找
func (u *MiddlewareDataService) FindMiddlewareByID(middlewareID int64) (*model.Middleware, error) {
	return u.MiddlewareRepository.FindMiddlewareByID(middlewareID)
}

//查找
func (u *MiddlewareDataService) FindAllMiddleware() ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAll()
}

//根据类型查找所有的中间件
func (u *MiddlewareDataService) FindAllMiddlewareByTypeID(typeID int64) ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAllByTypeID(typeID)
}

10-13 中间件middleware service开发(4) 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middleware_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
	"github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type IMiddlewareDataService interface {
	AddMiddleware(*model.Middleware) (int64, error)
	DeleteMiddleware(int64) error
	UpdateMiddleware(*model.Middleware) error
	FindMiddlewareByID(int64) (*model.Middleware, error)
	FindAllMiddleware() ([]model.Middleware, error)
	//根据类型查找中间件
	FindAllMiddlewareByTypeID(int64) ([]model.Middleware, error)
	//操作中间件s
	CreateToK8s(*middleware.MiddlewareInfo) error
	DeleteFromK8s(*model.Middleware) error
	UpdateToK8s(*middleware.MiddlewareInfo) error
}

//创建
//注意:返回值 IMiddlewareDataService 接口类型
func NewMiddlewareDataService(middlewareRepository repository.IMiddlewareRepository, clientSet *kubernetes.Clientset) IMiddlewareDataService {
	return &MiddlewareDataService{MiddlewareRepository: middlewareRepository, K8sClientSet: clientSet}
}

type MiddlewareDataService struct {
	//注意:这里是 IMiddlewareRepository 类型
	MiddlewareRepository repository.IMiddlewareRepository
	K8sClientSet         *kubernetes.Clientset
}

//更新中间件到k8s
func (u *MiddlewareDataService) UpdateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("中间件 " + info.MiddleName + " 不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Update(context.TODO(), statefulSet, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件 " + info.MiddleName + " 更新成功!")
		return nil
	}

}

//删除中间件
func (u *MiddlewareDataService) DeleteFromK8s(middleware *model.Middleware) (err error) {
	if err := u.K8sClientSet.AppsV1().StatefulSets(middleware.MiddleNamespace).Delete(context.TODO(), middleware.MiddleName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteMiddleware(middleware.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除中间件:" + middleware.MiddleName + "成功!")
		return nil

	}

}

//在k8s中创建中间件
func (u *MiddlewareDataService) CreateToK8s(info *middleware.MiddlewareInfo) error {
	statefulSet := u.setStatefulSet(info)
	if _, err := u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Get(context.TODO(), info.MiddleName, v12.GetOptions{}); err != nil {
		//如果没有获取到
		if _, err = u.K8sClientSet.AppsV1().StatefulSets(info.MiddleNamespace).Create(context.TODO(), statefulSet, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("中间件:" + info.MiddleName + "创建成功")
		return nil
	} else {
		common.Error("中间件:" + info.MiddleName + "创建失败")
		return errors.New("中间件:" + info.MiddleName + "创建失败")
	}
}

//根据info信息设置值
func (u *MiddlewareDataService) setStatefulSet(info *middleware.MiddlewareInfo) *v1.StatefulSet {
	statefulSet := &v1.StatefulSet{}
	statefulSet.TypeMeta = v12.TypeMeta{
		Kind:       "StatefulSet",
		APIVersion: "v1",
	}
	//设置详情
	statefulSet.ObjectMeta = v12.ObjectMeta{
		Name:      info.MiddleName,
		Namespace: info.MiddleNamespace,
		//设置label标签
		Labels: map[string]string{
			"app-name": info.MiddleName,
			"author":   "wu123",
		},
	}
	statefulSet.Name = info.MiddleName
	statefulSet.Spec = v1.StatefulSetSpec{
		//副本数
		Replicas: &info.MiddleReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": info.MiddleName,
			},
		},
		//设置容器模版
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": info.MiddleName,
				},
			},
			//设置容器详情
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:  info.MiddleName,
						Image: info.MiddleDockerImageVersion,
						//获取容器的端口
						Ports: u.getContainerPort(info),
						//获取环境变量
						Env: u.getEnv(info),
						//获取容器CPU,内存
						Resources: u.getResources(info),
						//设置挂载目录
						VolumeMounts: u.setMounts(info),
					},
				},
				//不能设置为0,这样不安全
				//https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
				TerminationGracePeriodSeconds: u.getTime("10"),
				//私有仓库设置密钥
				ImagePullSecrets: nil,
			},
		},
		VolumeClaimTemplates: u.getPVC(info),
		ServiceName:          info.MiddleName,
	}
	return statefulSet

}

func (u *MiddlewareDataService) getTime(stringTime string) *int64 {
	b, err := strconv.ParseInt(stringTime, 10, 64)
	if err != nil {
		common.Error(err)
		return nil
	}
	return &b
}

//设置存储路径
func (u *MiddlewareDataService) setMounts(info *middleware.MiddlewareInfo) (mount []v13.VolumeMount) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		mt := &v13.VolumeMount{
			Name:      v.MiddleStorageName,
			MountPath: v.MiddleStoragePath,
		}
		mount = append(mount, *mt)
	}
	return
}

//获取pvc
func (u *MiddlewareDataService) getPVC(info *middleware.MiddlewareInfo) (pvcAll []v13.PersistentVolumeClaim) {
	if len(info.MiddleStorage) == 0 {
		return
	}
	for _, v := range info.MiddleStorage {
		pvc := &v13.PersistentVolumeClaim{
			TypeMeta: v12.TypeMeta{
				Kind:       "PersistentVolumeClaim",
				APIVersion: "v1",
			},
			ObjectMeta: v12.ObjectMeta{
				Name:      v.MiddleStorageName,
				Namespace: info.MiddleNamespace,
				Annotations: map[string]string{
					"pv.kubernetes.io/bound-by-controller":          "yes",
					"volume.beta.kubernetes.io/storage-provisioner": "rbd.csi.ceph.com",
				},
			},
			Spec: v13.PersistentVolumeClaimSpec{
				AccessModes:      u.getAccessModes(v.MiddleStorageAccessMode),
				Resources:        u.getPvcResource(v.MiddleStorageSize),
				VolumeName:       v.MiddleStorageName,
				StorageClassName: &v.MiddleStorageClass,
			},
		}
		pvcAll = append(pvcAll, *pvc)
	}
	return
}

//获取大小
func (u *MiddlewareDataService) getPvcResource(size float32) (source v13.ResourceRequirements) {
	source.Requests = v13.ResourceList{
		"storage": resource.MustParse(strconv.FormatFloat(float64(size), 'f', 6, 64) + "Gi"),
	}
	return
}

//获取权限的
func (u *MiddlewareDataService) getAccessModes(accessMode string) (pvam []v13.PersistentVolumeAccessMode) {
	var pm v13.PersistentVolumeAccessMode
	switch accessMode {
	case "ReadWriteOnce":
		pm = v13.ReadWriteOnce
	case "ReadOnlyMany":
		pm = v13.ReadOnlyMany
	case "ReadWriteMany":
		pm = v13.ReadWriteMany
	case "ReadWriteOncePod":
		pm = v13.ReadWriteOncePod
	default:
		pm = v13.ReadWriteOnce
	}
	pvam = append(pvam, pm)
	return pvam
}

//获取容器的端口
func (u *MiddlewareDataService) getContainerPort(info *middleware.MiddlewareInfo) (containerPort []v13.ContainerPort) {
	for _, v := range info.MiddlePort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "middle-port-" + strconv.FormatInt(int64(v.MiddlePort), 10),
			ContainerPort: v.MiddlePort,
			Protocol:      u.getProtocol(v.MiddleProtocol),
		})
	}
	return
}

//获取protocol 协议
func (u *MiddlewareDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}

}

//获取中间件的环境变量
func (u *MiddlewareDataService) getEnv(info *middleware.MiddlewareInfo) (envVar []v13.EnvVar) {
	for _, v := range info.MiddleEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//获取中间件需要的资源
func (u *MiddlewareDataService) getResources(info *middleware.MiddlewareInfo) (source v13.ResourceRequirements) {
	//最大能够使用的资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	//最小请求资源
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(info.MiddleCpu), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(info.MiddleMemory), 'f', 6, 64)),
	}
	return
}

//插入
func (u *MiddlewareDataService) AddMiddleware(middleware *model.Middleware) (int64, error) {
	return u.MiddlewareRepository.CreateMiddleware(middleware)
}

//删除
func (u *MiddlewareDataService) DeleteMiddleware(middlewareID int64) error {
	return u.MiddlewareRepository.DeleteMiddlewareByID(middlewareID)
}

//更新
func (u *MiddlewareDataService) UpdateMiddleware(middleware *model.Middleware) error {
	return u.MiddlewareRepository.UpdateMiddleware(middleware)
}

//查找
func (u *MiddlewareDataService) FindMiddlewareByID(middlewareID int64) (*model.Middleware, error) {
	return u.MiddlewareRepository.FindMiddlewareByID(middlewareID)
}

//查找
func (u *MiddlewareDataService) FindAllMiddleware() ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAll()
}

//根据类型查找所有的中间件
func (u *MiddlewareDataService) FindAllMiddlewareByTypeID(typeID int64) ([]model.Middleware, error) {
	return u.MiddlewareRepository.FindAllByTypeID(typeID)
}

10-14 中间件service 对应版本服务代码开发 

C:\Users\Administrator\Desktop\gopaas\middleware\domain\service\middle_type_data_service.go

package service

import (
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"
)

//定义接口类型
type IMiddleTypeDataService interface {
	AddMiddleType(*model.MiddleType) (int64, error)
	DeleteMiddleType(int64) error
	UpdateMiddleType(*model.MiddleType) error
	FindMiddleTypeByID(int64) (*model.MiddleType, error)
	FindAllMiddleType() ([]model.MiddleType, error)
	//根据ID返回地址
	FindImageVersionByID(int64) (string, error)

	FindVersionByID(int64) (*model.MiddleVersion, error)
	FindAllVersionByTypeID(int64) ([]model.MiddleVersion, error)
}

//注意:返回值的类型
func NewMiddleTypeDataService(repository repository.IMiddleTypeRepository) IMiddleTypeDataService {
	return &MiddleTypeDataService{MiddleTypeRepository: repository}

}

type MiddleTypeDataService struct {
	MiddleTypeRepository repository.IMiddleTypeRepository
}

//插入
func (u *MiddleTypeDataService) AddMiddleType(middleType *model.MiddleType) (int64, error) {
	return u.MiddleTypeRepository.CreateMiddleType(middleType)
}

//删除
func (u *MiddleTypeDataService) DeleteMiddleType(middleTypeID int64) error {
	return u.MiddleTypeRepository.DeleteMiddleTypeByID(middleTypeID)
}

//更新
func (u *MiddleTypeDataService) UpdateMiddleType(middleType *model.MiddleType) error {
	return u.MiddleTypeRepository.UpdateMiddleType(middleType)
}

//查找
func (u *MiddleTypeDataService) FindMiddleTypeByID(middleTypeID int64) (*model.MiddleType, error) {
	return u.MiddleTypeRepository.FindTypeByID(middleTypeID)

}

//查找所有
func (u *MiddleTypeDataService) FindAllMiddleType() ([]model.MiddleType, error) {
	return u.MiddleTypeRepository.FindAll()
}

//根据version ID查找镜像地址
func (u *MiddleTypeDataService) FindImageVersionByID(middleVersionID int64) (string, error) {
	version, err := u.MiddleTypeRepository.FindVersionByID(middleVersionID)
	if err != nil {
		return "", err
	}
	//返回需要的镜像地址
	return version.MiddleDockerImage + ":" + version.MiddleVS, nil

}

//根据versionID 查找单个镜像
func (u *MiddleTypeDataService) FindVersionByID(middleVersionID int64) (*model.MiddleVersion, error) {
	return u.MiddleTypeRepository.FindVersionByID(middleVersionID)
}

//根据中间件类型查找对应的所有版本
func (u *MiddleTypeDataService) FindAllVersionByTypeID(middleTypeID int64) ([]model.MiddleVersion, error) {
	return u.MiddleTypeRepository.FindAllVersionByTypeID(middleTypeID)
}

10-15 中间件main调整 及 handler 开发(上)

C:\Users\Administrator\Desktop\gopaas\middleware\main.go

	//只能执行一遍
	//err = repository.NewMiddlewareRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}
	//if err:= repository.NewMiddleTypeRepository(db).InitTable();err!=nil{
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	middlewareDataService := service2.NewMiddlewareDataService(repository.NewMiddlewareRepository(db), clientset)
	middleTypeDataService := service2.NewMiddleTypeDataService(repository.NewMiddleTypeRepository(db))
	middleware.RegisterMiddlewareHandler(service.Server(), &handler.MiddlewareHandler{MiddlewareDataService: middlewareDataService, MiddleTypeDataService: middleTypeDataService})

C:\Users\Administrator\Desktop\gopaas\middleware\handler\middlewareHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/service"
	middleware "github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
)

type MiddlewareHandler struct {
	//注意这里的类型是 IMiddlewareDataService 接口类型
	MiddlewareDataService service.IMiddlewareDataService
	// 添加中间件类型服务
	MiddleTypeDataService service.IMiddleTypeDataService
}

// Call is a single request handler called via client.Call or the generated client code
func (e *MiddlewareHandler) AddMiddleware(ctx context.Context, info *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.AddMiddleware request")
	middleModel := &model.Middleware{}
	if err := common.SwapTo(info, middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//调用其它的服务处理数据
	//根据ID产销需要的镜像地址
	imageAddress, err := e.MiddleTypeDataService.FindImageVersionByID(info.MiddleVersionId)
	if err != nil {
		common.Error(err)
		return err
	}
	//赋值
	info.MiddleDockerImageVersion = imageAddress
	//在k8s 中创建资源
	if err := e.MiddlewareDataService.CreateToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//插入数据库
		middleID, err := e.MiddlewareDataService.AddMiddleware(middleModel)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		rsp.Msg = "中间件添加成功 ID 号为:" + strconv.FormatInt(middleID, 10)
		common.Info(rsp.Msg)
	}
	return nil
}

func (e *MiddlewareHandler) DeleteMiddleware(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.Response) error {
	log.Info("Received *middleware.DeleteMiddleware request")
	middleModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//删除k8s中资源
	if err := e.MiddlewareDataService.DeleteFromK8s(middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

func (e *MiddlewareHandler) UpdateMiddleware(ctx context.Context, req *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.UpdateMiddleware request")
	if err := e.MiddlewareDataService.UpdateToK8s(req); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//查询中间件相关的信息
	middleModle, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model数据
	if err := common.SwapTo(req, middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model
	if err := e.MiddlewareDataService.UpdateMiddleware(middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

func (e *MiddlewareHandler) FindMiddlewareByID(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.MiddlewareInfo) error {
	log.Info("Received *middleware.FindMiddlewareByID request")
	//查询中间件
	middlewareModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := common.SwapTo(middlewareModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

func (e *MiddlewareHandler) FindAllMiddleware(ctx context.Context, req *middleware.FindAll, rsp *middleware.AllMiddleware) error {
	log.Info("Received *middleware.FindAllMiddleware request")
	allMiddleware, err := e.MiddlewareDataService.FindAllMiddleware()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleware {
		middleInfo := &middleware.MiddlewareInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddlewareInfo = append(rsp.MiddlewareInfo, middleInfo)
	}
	return nil
}

make proto 

10-16 中间件main调整 及 handler 开发(下) 

C:\Users\Administrator\Desktop\gopaas\middleware\main.go

package main

import (
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/repository"

	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	ratelimit "github.com/asim/go-micro/plugins/wrapper/ratelimiter/uber/v3"
	opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"
	"github.com/asim/go-micro/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/asim/go-micro/v3/server"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/opentracing/opentracing-go"
	service2 "github.com/yunixiangfeng/gopaas/middleware/domain/service"
	"github.com/yunixiangfeng/gopaas/middleware/handler"

	//hystrix2 "github.com/yunixiangfeng/gopaas/middleware/plugin/hystrix"
	"strconv"

	middleware "github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8089"

	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断端口,每个服务不能重复
	//hystrixPort = 9099
	//监控端口,每个服务不能重复
	prometheusPort = 9199
)

func main() {
	//需要本地启动,mysql,consul中间件服务
	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	//3.使用配置中心连接 mysql
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	//初始化数据库
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		//命令行输出下,方便查看错误
		fmt.Println(err)
		common.Fatal(err)
	}
	defer db.Close()
	//禁止复表
	db.SingularTable(true)

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.middleware", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

	//添加熔断器,作为客户端需要启用
	//hystrixStreamHandler := hystrix.NewStreamHandler()
	//hystrixStreamHandler.Start()

	//添加日志中心
	//1)需要程序日志打入到日志文件中
	//2)在程序中添加filebeat.yml 文件
	//3) 启动filebeat,启动命令 ./filebeat -e -c filebeat.yml
	fmt.Println("日志统一记录在根目录 micro.log 文件中,请点击查看日志!")

	//启动监听程序
	//go func() {
	//	//http://192.168.204.130:9092/turbine/turbine.stream
	//	//看板访问地址 http://127.0.0.1:9002/hystrix,url后面一定要带 /hystrix
	//	err = http.ListenAndServe(net.JoinHostPort("0.0.0.0",strconv.Itoa(hystrixPort)),hystrixStreamHandler)
	//	if err !=nil {
	//		common.Error(err)
	//	}
	//}()

	//5.添加监控
	common.PrometheusBoot(prometheusPort)

	//下载kubectl:https://kubernetes.io/docs/tasks/tools/#tabset-2
	//macos:
	// 1.curl -LO "https://dl.k8s.io/release/v1.21.0/bin/darwin/amd64/kubectl"
	// 2.chmod +x ./kubectl
	// 3.sudo mv ./kubectl /usr/local/bin/kubectl
	//   sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//6.创建k8s连接
	//在集群外面连接
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中外的配置
	//config, err := rest.InClusterConfig()
	//if err != nil {
	//	panic(err.Error())
	//}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.middleware"),
		micro.Version("latest"),
		//指定服务端口
		micro.Address(":"+servicePort),
		//添加注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		micro.WrapClient(opentracing2.NewClientWrapper(opentracing.GlobalTracer())),
		//只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用
		//micro.WrapClient(hystrix2.NewClientHystrixWrapper()),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),
	)

	service.Init()

	//只能执行一遍
	//err = repository.NewMiddlewareRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}
	//if err:= repository.NewMiddleTypeRepository(db).InitTable();err!=nil{
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	middlewareDataService := service2.NewMiddlewareDataService(repository.NewMiddlewareRepository(db), clientset)
	middleTypeDataService := service2.NewMiddleTypeDataService(repository.NewMiddleTypeRepository(db))
	middleware.RegisterMiddlewareHandler(service.Server(), &handler.MiddlewareHandler{MiddlewareDataService: middlewareDataService, MiddleTypeDataService: middleTypeDataService})

	// 启动服务
	if err := service.Run(); err != nil {
		//输出启动失败信息
		common.Fatal(err)
	}
}

C:\Users\Administrator\Desktop\gopaas\middleware\handler\middlewareHandler.go

package handler

import (
	"context"
	"strconv"

	log "github.com/asim/go-micro/v3/logger"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/middleware/domain/model"
	"github.com/yunixiangfeng/gopaas/middleware/domain/service"
	middleware "github.com/yunixiangfeng/gopaas/middleware/proto/middleware"
)

type MiddlewareHandler struct {
	//注意这里的类型是 IMiddlewareDataService 接口类型
	MiddlewareDataService service.IMiddlewareDataService
	// 添加中间件类型服务
	MiddleTypeDataService service.IMiddleTypeDataService
}

func (e *MiddlewareHandler) DeleteMiddleTypeById(context.Context, *middleware.MiddleTypeId, *middleware.Response) error {
	panic("implement me")
}

// Call is a single request handler called via client.Call or the generated client code
func (e *MiddlewareHandler) AddMiddleware(ctx context.Context, info *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.AddMiddleware request")
	middleModel := &model.Middleware{}
	if err := common.SwapTo(info, middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//调用其它的服务处理数据
	//根据ID产销需要的镜像地址
	imageAddress, err := e.MiddleTypeDataService.FindImageVersionByID(info.MiddleVersionId)
	if err != nil {
		common.Error(err)
		return err
	}
	//赋值
	info.MiddleDockerImageVersion = imageAddress
	//在k8s 中创建资源
	if err := e.MiddlewareDataService.CreateToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//插入数据库
		middleID, err := e.MiddlewareDataService.AddMiddleware(middleModel)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		rsp.Msg = "中间件添加成功 ID 号为:" + strconv.FormatInt(middleID, 10)
		common.Info(rsp.Msg)
	}
	return nil
}

func (e *MiddlewareHandler) DeleteMiddleware(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.Response) error {
	log.Info("Received *middleware.DeleteMiddleware request")
	middleModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//删除k8s中资源
	if err := e.MiddlewareDataService.DeleteFromK8s(middleModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

func (e *MiddlewareHandler) UpdateMiddleware(ctx context.Context, req *middleware.MiddlewareInfo, rsp *middleware.Response) error {
	log.Info("Received *middleware.UpdateMiddleware request")
	if err := e.MiddlewareDataService.UpdateToK8s(req); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//查询中间件相关的信息
	middleModle, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model数据
	if err := common.SwapTo(req, middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	//更新model
	if err := e.MiddlewareDataService.UpdateMiddleware(middleModle); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

//查询中间件
func (e *MiddlewareHandler) FindMiddlewareByID(ctx context.Context, req *middleware.MiddlewareId, rsp *middleware.MiddlewareInfo) error {
	log.Info("Received *middleware.FindMiddlewareByID request")
	//查询中间件
	middlewareModel, err := e.MiddlewareDataService.FindMiddlewareByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := common.SwapTo(middlewareModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//查找所有的中间件
func (e *MiddlewareHandler) FindAllMiddleware(ctx context.Context, req *middleware.FindAll, rsp *middleware.AllMiddleware) error {
	log.Info("Received *middleware.FindAllMiddleware request")
	allMiddleware, err := e.MiddlewareDataService.FindAllMiddleware()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleware {
		middleInfo := &middleware.MiddlewareInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddlewareInfo = append(rsp.MiddlewareInfo, middleInfo)
	}
	return nil
}

//查找所有的中间件
func (e *MiddlewareHandler) FindAllMiddlewareByTypeID(ctx context.Context, req *middleware.FindAllByTypeId, rsp *middleware.AllMiddleware) error {
	log.Info("Received *middleware.FindAllMiddleware request")
	allMiddleware, err := e.MiddlewareDataService.FindAllMiddlewareByTypeID(req.TypeId)
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleware {
		middleInfo := &middleware.MiddlewareInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddlewareInfo = append(rsp.MiddlewareInfo, middleInfo)
	}
	return nil
}

//根据ID查找中间件类型信息
func (e *MiddlewareHandler) FindMiddleTypeByID(ctx context.Context, req *middleware.MiddleTypeId, rsp *middleware.MiddleTypeInfo) error {
	typeModel, err := e.MiddleTypeDataService.FindMiddleTypeByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := common.SwapTo(typeModel, rsp); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//添加中间件
func (e *MiddlewareHandler) AddMiddleType(ctx context.Context, info *middleware.MiddleTypeInfo, rsp *middleware.Response) error {
	typeModel := &model.MiddleType{}
	if err := common.SwapTo(info, typeModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	id, err := e.MiddleTypeDataService.AddMiddleType(typeModel)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	rsp.Msg = "中间件类型 ID 号为: " + strconv.FormatInt(id, 10)
	common.Info(rsp.Msg)
	return nil
}

//删除中间件类型
func (e *MiddlewareHandler) DeleteMiddleTypeByID(ctx context.Context, req *middleware.MiddleTypeId, rsp *middleware.Response) error {
	if err := e.MiddleTypeDataService.DeleteMiddleType(req.Id); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

//更新中间件类型
func (e *MiddlewareHandler) UpdateMiddleType(ctx context.Context, req *middleware.MiddleTypeInfo, rsp *middleware.Response) error {
	typeModel, err := e.MiddleTypeDataService.FindMiddleTypeByID(req.Id)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	if err := common.SwapTo(req, typeModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	if err := e.MiddleTypeDataService.UpdateMiddleType(typeModel); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}
	return nil
}

//查找所有的类型
func (e *MiddlewareHandler) FindAllMiddleType(ctx context.Context, req *middleware.FindAll, rsp *middleware.AllMiddleType) error {
	//查询所有中间件
	allMiddleType, err := e.MiddleTypeDataService.FindAllMiddleType()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allMiddleType {
		middleInfo := &middleware.MiddleTypeInfo{}
		if err := common.SwapTo(v, middleInfo); err != nil {
			common.Error(err)
			return err
		}
		rsp.MiddleTypeInfo = append(rsp.MiddleTypeInfo, middleInfo)
	}
	return nil
}

make proto 

10-17 中间件前端页面以及核心API开发(上)

PS C:\Users\Administrator\Desktop\gopaas> .\yu-tool\yu-tool.exe  newService github.com/yunixiangfeng/gopaas/middlewareApi

make proto 

go mod tidy

C:\Users\Administrator\Desktop\gopaas\middlewareapi\proto\middlewareApi\middlewareApi.proto

syntax = "proto3";

package middlewareApi;

option go_package = "./proto/middlewareApi;middlewareApi";

service MiddlewareApi {
    rpc FindMiddlewareById(Request) returns (Response){}
	rpc AddMiddleware(Request) returns (Response){}
	rpc DeleteMiddlewareById(Request) returns (Response){}
	rpc UpdateMiddleware(Request) returns (Response){}
	//默认接口
	rpc Call(Request) returns (Response){}
	//根据类型获取所有中间件
	rpc FindAllMiddlewareByTypeId(Request) returns (Response){}
	//中间件类型对外开发的API
	rpc FindMiddleTypeById(Request) returns (Response){}
	rpc AddMiddleType(Request) returns (Response){}
	rpc DeleteMiddleTypeById(Request) returns (Response){}
	rpc UpdateMiddleType(Request) returns (Response){}
	rpc FindAllMiddleType(Request) returns (Response){}
}

message Pair {
	string key = 1;
	repeated string values = 2;
}


message Request {
	string method = 1;
	string path = 2;
	map<string, Pair> header = 3;
	map<string, Pair> get = 4;
	map<string, Pair> post = 5;
	string body = 6;
	string url = 7;
}


message Response {
	int32 statusCode = 1;
	map<string, Pair> header = 2;
	string body = 3;
}

 make proto

C:\Users\Administrator\Desktop\gopaas\middlewareapi\proto\middlewareApi\middlewareApi.pb.micro.go

// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: proto/middlewareApi/middlewareApi.proto

package middlewareApi

import (
	fmt "fmt"
	proto "google.golang.org/protobuf/proto"
	math "math"
)

import (
	context "context"
	api "github.com/asim/go-micro/v3/api"
	client "github.com/asim/go-micro/v3/client"
	server "github.com/asim/go-micro/v3/server"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option

// Api Endpoints for MiddlewareApi service

func NewMiddlewareApiEndpoints() []*api.Endpoint {
	return []*api.Endpoint{}
}

// Client API for MiddlewareApi service

type MiddlewareApiService interface {
	FindMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	AddMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	DeleteMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	UpdateMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	//默认接口
	Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	//根据类型获取所有中间件
	FindAllMiddlewareByTypeId(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	//中间件类型对外开发的API
	FindMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	AddMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	DeleteMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	UpdateMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
	FindAllMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
}

type middlewareApiService struct {
	c    client.Client
	name string
}

func NewMiddlewareApiService(name string, c client.Client) MiddlewareApiService {
	return &middlewareApiService{
		c:    c,
		name: name,
	}
}

func (c *middlewareApiService) FindMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindMiddlewareById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) AddMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.AddMiddleware", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) DeleteMiddlewareById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.DeleteMiddlewareById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) UpdateMiddleware(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.UpdateMiddleware", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.Call", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) FindAllMiddlewareByTypeId(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindAllMiddlewareByTypeId", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) FindMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindMiddleTypeById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) AddMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.AddMiddleType", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) DeleteMiddleTypeById(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.DeleteMiddleTypeById", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) UpdateMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.UpdateMiddleType", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *middlewareApiService) FindAllMiddleType(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.name, "MiddlewareApi.FindAllMiddleType", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for MiddlewareApi service

type MiddlewareApiHandler interface {
	FindMiddlewareById(context.Context, *Request, *Response) error
	AddMiddleware(context.Context, *Request, *Response) error
	DeleteMiddlewareById(context.Context, *Request, *Response) error
	UpdateMiddleware(context.Context, *Request, *Response) error
	//默认接口
	Call(context.Context, *Request, *Response) error
	//根据类型获取所有中间件
	FindAllMiddlewareByTypeId(context.Context, *Request, *Response) error
	//中间件类型对外开发的API
	FindMiddleTypeById(context.Context, *Request, *Response) error
	AddMiddleType(context.Context, *Request, *Response) error
	DeleteMiddleTypeById(context.Context, *Request, *Response) error
	UpdateMiddleType(context.Context, *Request, *Response) error
	FindAllMiddleType(context.Context, *Request, *Response) error
}

func RegisterMiddlewareApiHandler(s server.Server, hdlr MiddlewareApiHandler, opts ...server.HandlerOption) error {
	type middlewareApi interface {
		FindMiddlewareById(ctx context.Context, in *Request, out *Response) error
		AddMiddleware(ctx context.Context, in *Request, out *Response) error
		DeleteMiddlewareById(ctx context.Context, in *Request, out *Response) error
		UpdateMiddleware(ctx context.Context, in *Request, out *Response) error
		Call(ctx context.Context, in *Request, out *Response) error
		FindAllMiddlewareByTypeId(ctx context.Context, in *Request, out *Response) error
		FindMiddleTypeById(ctx context.Context, in *Request, out *Response) error
		AddMiddleType(ctx context.Context, in *Request, out *Response) error
		DeleteMiddleTypeById(ctx context.Context, in *Request, out *Response) error
		UpdateMiddleType(ctx context.Context, in *Request, out *Response) error
		FindAllMiddleType(ctx context.Context, in *Request, out *Response) error
	}
	type MiddlewareApi struct {
		middlewareApi
	}
	h := &middlewareApiHandler{hdlr}
	return s.Handle(s.NewHandler(&MiddlewareApi{h}, opts...))
}

type middlewareApiHandler struct {
	MiddlewareApiHandler
}

func (h *middlewareApiHandler) FindMiddlewareById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindMiddlewareById(ctx, in, out)
}

func (h *middlewareApiHandler) AddMiddleware(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.AddMiddleware(ctx, in, out)
}

func (h *middlewareApiHandler) DeleteMiddlewareById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.DeleteMiddlewareById(ctx, in, out)
}

func (h *middlewareApiHandler) UpdateMiddleware(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.UpdateMiddleware(ctx, in, out)
}

func (h *middlewareApiHandler) Call(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.Call(ctx, in, out)
}

func (h *middlewareApiHandler) FindAllMiddlewareByTypeId(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindAllMiddlewareByTypeId(ctx, in, out)
}

func (h *middlewareApiHandler) FindMiddleTypeById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindMiddleTypeById(ctx, in, out)
}

func (h *middlewareApiHandler) AddMiddleType(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.AddMiddleType(ctx, in, out)
}

func (h *middlewareApiHandler) DeleteMiddleTypeById(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.DeleteMiddleTypeById(ctx, in, out)
}

func (h *middlewareApiHandler) UpdateMiddleType(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.UpdateMiddleType(ctx, in, out)
}

func (h *middlewareApiHandler) FindAllMiddleType(ctx context.Context, in *Request, out *Response) error {
	return h.MiddlewareApiHandler.FindAllMiddleType(ctx, in, out)
}

C:\Users\Administrator\Desktop\gopaas\middlewareapi\proto\middlewareApi\middlewareApi.pb.go

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.27.1
// 	protoc        v3.15.7
// source: proto/middlewareApi/middlewareApi.proto

package middlewareApi

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type Pair struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Key    string   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
	Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
}

func (x *Pair) Reset() {
	*x = Pair{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Pair) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Pair) ProtoMessage() {}

func (x *Pair) ProtoReflect() protoreflect.Message {
	mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Pair.ProtoReflect.Descriptor instead.
func (*Pair) Descriptor() ([]byte, []int) {
	return file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP(), []int{0}
}

func (x *Pair) GetKey() string {
	if x != nil {
		return x.Key
	}
	return ""
}

func (x *Pair) GetValues() []string {
	if x != nil {
		return x.Values
	}
	return nil
}

type Request struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Method string           `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
	Path   string           `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
	Header map[string]*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Get    map[string]*Pair `protobuf:"bytes,4,rep,name=get,proto3" json:"get,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Post   map[string]*Pair `protobuf:"bytes,5,rep,name=post,proto3" json:"post,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Body   string           `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
	Url    string           `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"`
}

func (x *Request) Reset() {
	*x = Request{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Request) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Request) ProtoMessage() {}

func (x *Request) ProtoReflect() protoreflect.Message {
	mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[1]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Request.ProtoReflect.Descriptor instead.
func (*Request) Descriptor() ([]byte, []int) {
	return file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP(), []int{1}
}

func (x *Request) GetMethod() string {
	if x != nil {
		return x.Method
	}
	return ""
}

func (x *Request) GetPath() string {
	if x != nil {
		return x.Path
	}
	return ""
}

func (x *Request) GetHeader() map[string]*Pair {
	if x != nil {
		return x.Header
	}
	return nil
}

func (x *Request) GetGet() map[string]*Pair {
	if x != nil {
		return x.Get
	}
	return nil
}

func (x *Request) GetPost() map[string]*Pair {
	if x != nil {
		return x.Post
	}
	return nil
}

func (x *Request) GetBody() string {
	if x != nil {
		return x.Body
	}
	return ""
}

func (x *Request) GetUrl() string {
	if x != nil {
		return x.Url
	}
	return ""
}

type Response struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	StatusCode int32            `protobuf:"varint,1,opt,name=statusCode,proto3" json:"statusCode,omitempty"`
	Header     map[string]*Pair `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	Body       string           `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
}

func (x *Response) Reset() {
	*x = Response{}
	if protoimpl.UnsafeEnabled {
		mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[2]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Response) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Response) ProtoMessage() {}

func (x *Response) ProtoReflect() protoreflect.Message {
	mi := &file_proto_middlewareApi_middlewareApi_proto_msgTypes[2]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Response.ProtoReflect.Descriptor instead.
func (*Response) Descriptor() ([]byte, []int) {
	return file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP(), []int{2}
}

func (x *Response) GetStatusCode() int32 {
	if x != nil {
		return x.StatusCode
	}
	return 0
}

func (x *Response) GetHeader() map[string]*Pair {
	if x != nil {
		return x.Header
	}
	return nil
}

func (x *Response) GetBody() string {
	if x != nil {
		return x.Body
	}
	return ""
}

var File_proto_middlewareApi_middlewareApi_proto protoreflect.FileDescriptor

var file_proto_middlewareApi_middlewareApi_proto_rawDesc = []byte{
	0x0a, 0x27, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x41, 0x70, 0x69, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65,
	0x41, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x6d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x22, 0x30, 0x0a, 0x04, 0x50, 0x61, 0x69, 0x72,
	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
	0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
	0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xeb, 0x03, 0x0a, 0x07, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x12,
	0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61,
	0x74, 0x68, 0x12, 0x3a, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03,
	0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41,
	0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65,
	0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x31,
	0x0a, 0x03, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x69,
	0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75,
	0x65, 0x73, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x67, 0x65,
	0x74, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32,
	0x20, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72,
	0x79, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
	0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75,
	0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x1a, 0x4e, 0x0a,
	0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29,
	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
	0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61,
	0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a,
	0x08, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76,
	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x69, 0x64,
	0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52,
	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x50, 0x6f,
	0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c,
	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76,
	0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcb, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73,
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43,
	0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75,
	0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18,
	0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48,
	0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64,
	0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
	0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x4e, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c,
	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xa0, 0x06, 0x0a, 0x0d, 0x4d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x12, 0x47, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64,
	0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16,
	0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
	0x00, 0x12, 0x42, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41,
	0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64,
	0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f,
	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d,
	0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16, 0x2e,
	0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65,
	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61,
	0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
	0x12, 0x45, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72,
	0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d,
	0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73,
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12,
	0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
	0x22, 0x00, 0x12, 0x4e, 0x0a, 0x19, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x64,
	0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x42, 0x79, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12,
	0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
	0x22, 0x00, 0x12, 0x47, 0x0a, 0x12, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x54, 0x79, 0x70, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c,
	0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
	0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69,
	0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0d, 0x41,
	0x64, 0x64, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x2e, 0x6d,
	0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71,
	0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72,
	0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
	0x49, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x54,
	0x79, 0x70, 0x65, 0x42, 0x79, 0x49, 0x64, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65,
	0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
	0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e,
	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x10, 0x55, 0x70,
	0x64, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16,
	0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
	0x00, 0x12, 0x46, 0x0a, 0x11, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x64, 0x64,
	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77,
	0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
	0x2e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x52,
	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x2e, 0x2f, 0x70,
	0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41,
	0x70, 0x69, 0x3b, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x41, 0x70, 0x69,
	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_proto_middlewareApi_middlewareApi_proto_rawDescOnce sync.Once
	file_proto_middlewareApi_middlewareApi_proto_rawDescData = file_proto_middlewareApi_middlewareApi_proto_rawDesc
)

func file_proto_middlewareApi_middlewareApi_proto_rawDescGZIP() []byte {
	file_proto_middlewareApi_middlewareApi_proto_rawDescOnce.Do(func() {
		file_proto_middlewareApi_middlewareApi_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_middlewareApi_middlewareApi_proto_rawDescData)
	})
	return file_proto_middlewareApi_middlewareApi_proto_rawDescData
}

var file_proto_middlewareApi_middlewareApi_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_proto_middlewareApi_middlewareApi_proto_goTypes = []interface{}{
	(*Pair)(nil),     // 0: middlewareApi.Pair
	(*Request)(nil),  // 1: middlewareApi.Request
	(*Response)(nil), // 2: middlewareApi.Response
	nil,              // 3: middlewareApi.Request.HeaderEntry
	nil,              // 4: middlewareApi.Request.GetEntry
	nil,              // 5: middlewareApi.Request.PostEntry
	nil,              // 6: middlewareApi.Response.HeaderEntry
}
var file_proto_middlewareApi_middlewareApi_proto_depIdxs = []int32{
	3,  // 0: middlewareApi.Request.header:type_name -> middlewareApi.Request.HeaderEntry
	4,  // 1: middlewareApi.Request.get:type_name -> middlewareApi.Request.GetEntry
	5,  // 2: middlewareApi.Request.post:type_name -> middlewareApi.Request.PostEntry
	6,  // 3: middlewareApi.Response.header:type_name -> middlewareApi.Response.HeaderEntry
	0,  // 4: middlewareApi.Request.HeaderEntry.value:type_name -> middlewareApi.Pair
	0,  // 5: middlewareApi.Request.GetEntry.value:type_name -> middlewareApi.Pair
	0,  // 6: middlewareApi.Request.PostEntry.value:type_name -> middlewareApi.Pair
	0,  // 7: middlewareApi.Response.HeaderEntry.value:type_name -> middlewareApi.Pair
	1,  // 8: middlewareApi.MiddlewareApi.FindMiddlewareById:input_type -> middlewareApi.Request
	1,  // 9: middlewareApi.MiddlewareApi.AddMiddleware:input_type -> middlewareApi.Request
	1,  // 10: middlewareApi.MiddlewareApi.DeleteMiddlewareById:input_type -> middlewareApi.Request
	1,  // 11: middlewareApi.MiddlewareApi.UpdateMiddleware:input_type -> middlewareApi.Request
	1,  // 12: middlewareApi.MiddlewareApi.Call:input_type -> middlewareApi.Request
	1,  // 13: middlewareApi.MiddlewareApi.FindAllMiddlewareByTypeId:input_type -> middlewareApi.Request
	1,  // 14: middlewareApi.MiddlewareApi.FindMiddleTypeById:input_type -> middlewareApi.Request
	1,  // 15: middlewareApi.MiddlewareApi.AddMiddleType:input_type -> middlewareApi.Request
	1,  // 16: middlewareApi.MiddlewareApi.DeleteMiddleTypeById:input_type -> middlewareApi.Request
	1,  // 17: middlewareApi.MiddlewareApi.UpdateMiddleType:input_type -> middlewareApi.Request
	1,  // 18: middlewareApi.MiddlewareApi.FindAllMiddleType:input_type -> middlewareApi.Request
	2,  // 19: middlewareApi.MiddlewareApi.FindMiddlewareById:output_type -> middlewareApi.Response
	2,  // 20: middlewareApi.MiddlewareApi.AddMiddleware:output_type -> middlewareApi.Response
	2,  // 21: middlewareApi.MiddlewareApi.DeleteMiddlewareById:output_type -> middlewareApi.Response
	2,  // 22: middlewareApi.MiddlewareApi.UpdateMiddleware:output_type -> middlewareApi.Response
	2,  // 23: middlewareApi.MiddlewareApi.Call:output_type -> middlewareApi.Response
	2,  // 24: middlewareApi.MiddlewareApi.FindAllMiddlewareByTypeId:output_type -> middlewareApi.Response
	2,  // 25: middlewareApi.MiddlewareApi.FindMiddleTypeById:output_type -> middlewareApi.Response
	2,  // 26: middlewareApi.MiddlewareApi.AddMiddleType:output_type -> middlewareApi.Response
	2,  // 27: middlewareApi.MiddlewareApi.DeleteMiddleTypeById:output_type -> middlewareApi.Response
	2,  // 28: middlewareApi.MiddlewareApi.UpdateMiddleType:output_type -> middlewareApi.Response
	2,  // 29: middlewareApi.MiddlewareApi.FindAllMiddleType:output_type -> middlewareApi.Response
	19, // [19:30] is the sub-list for method output_type
	8,  // [8:19] is the sub-list for method input_type
	8,  // [8:8] is the sub-list for extension type_name
	8,  // [8:8] is the sub-list for extension extendee
	0,  // [0:8] is the sub-list for field type_name
}

func init() { file_proto_middlewareApi_middlewareApi_proto_init() }
func file_proto_middlewareApi_middlewareApi_proto_init() {
	if File_proto_middlewareApi_middlewareApi_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_proto_middlewareApi_middlewareApi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Pair); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_proto_middlewareApi_middlewareApi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Request); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_proto_middlewareApi_middlewareApi_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Response); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_proto_middlewareApi_middlewareApi_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   7,
			NumExtensions: 0,
			NumServices:   1,
		},
		GoTypes:           file_proto_middlewareApi_middlewareApi_proto_goTypes,
		DependencyIndexes: file_proto_middlewareApi_middlewareApi_proto_depIdxs,
		MessageInfos:      file_proto_middlewareApi_middlewareApi_proto_msgTypes,
	}.Build()
	File_proto_middlewareApi_middlewareApi_proto = out.File
	file_proto_middlewareApi_middlewareApi_proto_rawDesc = nil
	file_proto_middlewareApi_middlewareApi_proto_goTypes = nil
	file_proto_middlewareApi_middlewareApi_proto_depIdxs = nil
}

猜你喜欢

转载自blog.csdn.net/niwoxiangyu/article/details/130819155