Go开发PaaS平台核心功能

代码仓库地址GitHub - yunixiangfeng/gopaas

1 云原生PaaS平台介绍

随着云计算的发展,越来越多的企业逐步的把IT资源迁移到云上。PaaS平台作为基础设施基座,可以帮助企业快速构建功能丰富的容器云平台,提升交付效率,降低成本。 

[1.1] 云原生平台使用的主流技术

建设路径,先建设pass平台,再开发go微服务,然后扩大云原生支持.

容器监控系统 Promethus + Grafana

容器模式下中间件主流的接入方案

标准的路由管理技术 nginx ingress

云原生平台展示

[1.2] 什么是云原生GOPaaS平台以及有哪些优势 

PaaS 平台是什么?
PaaS 平台与 DevOps
PaaS平台与服务网格
PaaS 平台与低代码

PaaS 平台是什么?
PaaS(平台即服务):提供了一个用于开发、运行和管理应用程序的完整、灵活且经济高效的云平台。
是一种云计算模型,它为客户提供一个完整的云平台以用于开发、运行和管理应用程序,而无需考虑在本地构建和维护该平台通常会带来的成本、复杂性和不灵活性。 

PaaS 平台的优势
缩短产品上市时间
开发团队提供更大的灵活性
降低总体成本

[1.3] 云原生GoPaaS平台与DevOps的关系

PaaS 与 DevOps
DevOps 开发规范流程每个公司都有多种
新体系的下 DevOps 会融入到 PaaS 平台中
规模化的交付将借助于现有的 PaaS 体系进行 

[1.4] 云原生PaaS平台与服务网格 

PaaS 与 服务网格
Service Mesh 是下一代微服务架构
Service Mesh 有成熟的观测台
Service Mesh 需要通过 PaaS 平台进行系统性的管理

[1.5] 云原生GoPaaS平台与低代码

Paas 与 低代码
低代码是当今的风口,通过图形化编程方式来快速落地业务
低代码生成源码后会跑在 PaaS 平台中
低代码的交付模式将直接在 PaaS平台的基础上发展 

第2章 Go 搭建基础开发环境

准备开发环境,介绍go mod 的代理设置、工程目录结构的规划、Docker-compose 的使用。准备了自动化的生成框架 yu-tool ,可以无缝衔接日常业务。

2-1 环境安装级版本说明 

Go 代理设置& git 设置
micro 生产第一个项目目录及目录说明
Docker-compose 安装及使用

环境版本说明
Go版本使用 1.18.3
docker-compose 1.27.4
docker version 20.10.21

环境版本说明
Go 安装:https://golang.org/
docker-compose安装:
https://github.com/docker/compose/releases
docker 安装: https://docs.docker.com/get-docker/

2-2 go 代理和 git 设置 

Go 开发环境搭建
Go代理设置&git设置
设置go mod 代理: go env -wGOPROXY=https://goproxy.io,direct
私有仓库不设置代理:go env -w
GOPRIVATE=*.xxx.com

Go代理设置&git设置
go get 内部使用 https 的 clone 命令,默认只支持公有仓库

若用的是ssh 方式(注意用了80端口),需要替换。

2-3 项目目录结构

Go 开发环境搭建
生成第一个项目目录
如何创建我们的代码目录,目录结构是什么意思?
专用工具生成目录
D:\Workspace\gopaas\yu-tool\

go run main.go new github.com/xxxx/仓库名称

项目目录

D:\Workspace\Go\src\gopaas\base\

2-4 yu-tool 代码目录生成 & proto 文件生成

D:\Workspace\Go\src\gopaas\yu-tool

 go run main.go new user

PS D:\Workspace\gopaas\yu-tool> go run main.go  new user        
创建初始化项目 user

.
├── main.go
├── handler
│   └── userHandler.go
├── plugin
│   └── hystrix
│       └── hystrix.go
├── domain
│   ├── model
│   │   └── user.go
│   ├── repository
│   │   └── user_repository.go
│   └── service
│       └── user_data_service.go
├── proto
│   └── user
│       └── user.proto
├── Dockerfile
├── filebeat.yml
├── Makefile
├── README.md
├── .gitignore
└── go.mod

======================说明===========================
该工具为定制工具,旨在提高开发效率
工具主要功能如下:
1、快速创建项目目录结构。
======================操作============================
接下来直接使用代码中的 make proto 来自动生成基于 proto 的相关文件
具体操作如下:
1、make proto ( window 下要修改 docker 命令)
2、执行 go mod tidy
3、go run main.go 检查是否能够启动成功
4、查看注册中心服务是否存在(地址默认:127.0.0.1:8500)
注意:你也可以在本机安装 proto ,protoc-gen-go,protoc-gen-micro 运行 protoc 进行生成。



************恭喜!项目初始化成功!************

D:\Workspace\Go\src\gopaas\user

protoc --proto_path=. --micro_out=. --go_out=:. ./proto/user/user.proto

2-5 docker-compose yml编写和常见用法

Docker-compose 安装和使用
docker-compose yaml文件的编写
docker-compose 编译
docker-compose 启动

PS D:\Workspace\Go\src\gopaas\yu-tool> go run main.go new base
创建初始化项目 base

.
├── main.go
├── handler
│   └── baseHandler.go
├── plugin
│   └── hystrix
│       └── hystrix.go
├── domain
│   ├── model
│   │   └── base.go
│   ├── repository
│   │   └── base_repository.go
│   └── service
│       └── base_data_service.go
├── proto
│   └── base
│       └── base.proto
├── Dockerfile
├── filebeat.yml
├── Makefile
├── README.md
├── .gitignore
└── go.mod

======================说明===========================
该工具为定制工具,旨在提高开发效率
工具主要功能如下:
1、快速创建项目目录结构。


======================操作============================
接下来直接使用代码中的 make proto 来自动生成基于 proto 的相关文件
具体操作如下:
1、make proto ( window 下要修改 docker 命令)
2、执行 go mod tidy
3、go run main.go 检查是否能够启动成功
4、查看注册中心服务是否存在(地址默认:127.0.0.1:8500)
注意:你也可以在本机安装 proto ,protoc-gen-go,protoc-gen-micro 运行 protoc 进行生成。



************恭喜!项目初始化成功!************
D:\Workspace\Go\src\gopaas\base

#protoc --proto_path=. --micro_out=. --go_out=:. ./proto/base/base.proto
protoc --proto_path=D:\Workspace\Go\src\gopaas\base --micro_out=D:\Workspace\Go\src\gopaas\base --go_out=:D:\Workspace\Go\src\gopaas\base proto/base/base.proto
go mod tidy

# go build
go env -w CGO_ENABLED=0 GOOS=windows GOARCH=amd64 
go build -o base *.go

D:\Workspace\gopaas\docker-compose\chapter2\docker-compose.yml

# yaml 配置实例
version: '3'
services:
  #服务名称
  base-service:
    # 构建
    build:
      context: ../../base
      dockerfile: Dockerfile
    image: base-service:1.0.0
    container_name: base-yu-service
    restart: always
    ports:
    - "5000:5000"
    - "8080:8080"
cd  docker-compose\chapter2
docker-compose build base-service

docker images |grep base


docker-compose up -d

docker ps |grep base 

docker-compose down

2-6 总结&思考

你如何看待自动生成工具?
它能带来哪些方便?

完成Go基础环境安装和git 设置
使用yu-tool 创建第一个项目目录
完成 docker-compose 的安装

2-7 【扩展阅读】docker 安装和详细操作命令

2-8 【扩展阅读】Dockerfile 详细说明

第3章 Go 微服务 go-micro v3 框架快速入门

要完成PaaS 平台的开发,需要对微服务有充分的认知。认识 v3 与 micro 的区别;了解 go-micro框架,认识它的注册中心、配置中心、链路追踪、熔断&限流、监控等模块组件的使用,掌握Go主流微服务框架的核心应用。

3-1 本章概览

go-micro v3 和mirco v3 是什么关系?
go-micro v3整体架构
go-micro v3 框架技术栈讲解 

3-2 go-micro v3 框架与 micro v3 的关系

Go 微服务框架go-micro
go-micro 是什么?
go-micro 是一个go微服务开放框架
Framework(程序开发框架): 用来方便编写微服务
Clients(多语言客户端):支持多语言访问服务端 

go-micro v3与 micro v3 的关系
microv3版本类似isito 把基础设施抽象成能力
micro的技术体系和官方平台绑定较多
go-microv3为微服务开发框架,自由度相对较高

3-3 go-micro v3 框架整体介绍

go-micro v3组件
注册(Registry):提供了服务发现机制
选择器(Selector) :能够实现负载均衡
传输(Transport): 服务与服务之间通信接口

代理(Broker):提供异步通信的消息发布/订阅接口
编码(Codec):消息传输到两端时进行编码与解码
Server(服务端),Client (客户端)

Framework(go-micro) 通信图

3-4 go-micro v3 添加集群版本consul(上)

go-micro v3 技术栈使用
注册中心&配置中心
链路追踪
熔断 

限流
日志中心
监控

go-microv3技术栈-注册中心&配置中心consul 集群版
docker-compose 镜像引入 consul 集群版本
base 基础工程中添加 consul
consul 控制面板添加配置

3-5 go-micro v3 添加集群版本consul(下

D:\Workspace\gopaas\docker-compose\chapter3\docker-compose.yml

version: '3.3'
services:
  #注册中心集群版本设置
  consul1:
    image: consul
    container_name: node1
    command: agent -server -bootstrap-expect=3 -node=node1 -bind=0.0.0.0 -client=0.0.0.0 -datacenter=dc1
  consul2:
    image: consul
    container_name: node2
    command: agent -server -retry-join=node1 -bootstrap-expect=3 -node=node2 -bind=0.0.0.0 -client=0.0.0.0 -datacenter=dc1
    depends_on:
      - consul1
  consul3:
    image: consul
    container_name: node3
    command: agent -server -retry-join=node1 -bootstrap-expect=3 -node=node3 -bind=0.0.0.0 -client=0.0.0.0 -datacenter=dc1
    depends_on:
      - consul1
  #添加对外暴露的节点,启动控制面板
  consul4:
    image: consul
    container_name: node4
    command: agent -retry-join=node1 -node=node4 -bind=0.0.0.0 -client=0.0.0.0 -datacenter=dc1 -ui
    ports:
      - 8500:8500
    depends_on:
      - consul2
      - consul3
   #添加数据库
  paas-mysql:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      container_name: paas-mysql
    ports:
      - "3306:3306"
    #重要的数据挂盘
    volumes:
      - ./mysql:/var/lib/mysql
   #添加jaeger
  jaeger:
    image: jaeger
    ports:
      - "6831:6831/udp"
      - "16686:16686"
  #添加熔断看板
#  hystrix-dashboard:
#    #镜像名称
#    image: hystrix-dashboard
#    ports:
#      - "9002:9002"
  #添加监控镜像
  prometheus:
    image: prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
  #监控看板,默认密码为admin/admin
  grafana:
    image: grafana
    ports:
      - "3000:3000"
cd docker-compose\chapter3
docker-compose up

浏览器访问consul 127.0.0.1:8500

将consul服务添加到base工程

	//1.注册中心
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.base"),
		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)),
	)

D:\Workspace\Go\src\gopaas\base\main.go

go get github.com/asim/go-micro/plugins/registry/consul/v3

package main

import (
	"base/domain/repository"
	"flag"
	"fmt"
	"path/filepath"

	"github.com/yunixiangfeng/common"

	//"github.com/afex/hystrix-go/hystrix"
	service2 "base/domain/service"
	"base/handler"

	"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/micro/go-micro/server"
	"github.com/opentracing/opentracing-go"

	//hystrix2 "base/plugin/hystrix"
	base "base/proto/base"
	"strconv"

	"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 = "8081"

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

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.base", 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.base"),
		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.NewBaseRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	baseDataService := service2.NewBaseDataService(repository.NewBaseRepository(db), clientset)
	base.RegisterBaseHandler(service.Server(), &handler.BaseHandler{BaseDataService: baseDataService})

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

3-6 go-micro v3 添加配置中心 

创建common仓库 

​git clone https://github.com/yunixiangfeng/common

D:\Workspace\Go\src\gopaas\common
​

common\config.go 

package common

import (
	"github.com/asim/go-micro/plugins/config/source/consul/v3"
	"github.com/asim/go-micro/v3/config"
	"strconv"
)

func GetConsulConfig(host string,port int64,prefix string)(config.Config,error)  {
	consulSource := consul.NewSource(
		//设置配置中心的地址
		consul.WithAddress(host+":"+strconv.FormatInt(port,10)),
		//设置前缀,不设置 /micro/config
		consul.WithPrefix(prefix),
		consul.StripPrefix(true),
		)
	conf,err:= config.NewConfig()
	if err!=nil {
		return conf,err
	}
	err = conf.Load(consulSource)
	return conf,err
}
module github.com/yunixiangfeng/common

go 1.16

require (
	github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
	github.com/asim/go-micro/plugins/config/source/consul/v3 v3.0.0-20210904061721-270d910b7328
	github.com/asim/go-micro/v3 v3.6.0
	github.com/opentracing/opentracing-go v1.2.0
	github.com/prometheus/client_golang v1.1.0
	github.com/prometheus/common v0.6.0
	github.com/uber/jaeger-client-go v2.29.1+incompatible
	github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
	go.uber.org/zap v1.10.0
	gopkg.in/natefinch/lumberjack.v2 v2.0.0
)

提交到git仓库

然后在base工程main.go添加配置中心

	//2.配置中心,存放经常变动的变量
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}

http://127.0.0.1:8500/ui/dc1/kv创建key

micro/config/mysql 

{
	"host" : "127.0.0.1",
    "user" : "root",
	"pwd" : "1234",
	"database" : "paas",
	"port" : 3306
}

 

3-7 go-micro v3 如何使用配置中心连接 mysql 

common\mysql.go

package common

import "github.com/asim/go-micro/v3/config"

//创建结构体
type MysqlConfig struct {
	Host string `json:"host"`
	User string `json:"user"`
	Pwd string `json:"pwd"`
	Database string `json:"database"`
	Port string `json:"port"`
}

func GetMysqlFromConsul(config config.Config,path ...string) *MysqlConfig  {
	mysqlConfig := &MysqlConfig{}
	config.Get(path...).Scan(mysqlConfig)
	return mysqlConfig
}
	//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)

go run base/main.go

[root@k8s-worker01 base]# go run main.go
日志统一记录在根目录 micro.log 文件中,请点击查看日志!
2023-05-22 10:37:06  file=v3@v3.7.0/service.go:206 level=info Starting [service] go.micro.service.base
2023-05-22 10:37:06  file=server/rpc_server.go:820 level=info Transport [http] Listening on [::]:8081
2023-05-22 10:37:06  file=server/rpc_server.go:840 level=info Broker [http] Connected to 127.0.0.1:43121
2023-05-22 10:37:06  file=server/rpc_server.go:654 level=info Registry [consul] Registering node: go.micro.service.base-1431e43d-f735-4fba-b5e4-aaa8c0ab57f6

3-8 go-micro v3 添加链路追踪 

go-micro v3 技术栈-链路追踪jaeger
jaeger 原理讲解& docker-compose 安装 jaeger
common 统一模块添加jaeger
base 基础代码添加 jaeger 代码

微服务链路追踪(jaeger)的原理

common\jaeger.go

package common

import (
	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"io"
	"time"
)

//创建jaeger链路追踪实例
func NewTracer(serviceName string, addr string) (opentracing.Tracer, io.Closer, error) {
	cfg := &jaegercfg.Configuration{
		ServiceName: serviceName,
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:            true,
			BufferFlushInterval: 1 * time.Second,
			LocalAgentHostPort:  addr,
		},
	}
	tracer, closer, err := cfg.NewTracer()
	return tracer, closer, err
}

提交到common仓库

启动jaeger 

访问熔断器地址http://127.0.0.1:16686/ 

在base工程main.go中使用jaeger

	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.base", 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)
	//	}
	//}()
	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.base"),
		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()

    opentracing2 "github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3"

go run base/main.go 

3-9 go-micro v3 添加熔断和限流 

go-micro v3 技术栈- 链路熔断 hystrix
hystrix流程讲解
docker-compose 使用 hystrix 面板
base 基础代码添加熔断&限流代码

添加熔断看板 

Pulling hystrix-dashboard (hystrix-dashboard:) 

base/main.go

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

	//启动监听程序
	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)
		}
	}()

http://127.0.0.1:9002/hystrix

D:\Workspace\Go\src\gopaas\base\plugin\hystrix\hystrix.go

package hystrix

import (
	"context"
	"fmt"
	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/v3/client"
)

type clientWrapper struct {
	client.Client
}

//熔断逻辑
func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	return hystrix.Do(req.Service()+"."+req.Endpoint(), func() error {
		//正常执行
		fmt.Println(req.Service() + "." + req.Endpoint())
		return c.Client.Call(ctx, req, rsp, opts...)
	}, func(e error) error {
		//走熔断逻辑,每个服务都不一样
		fmt.Println(req.Service() + "." + req.Endpoint()+"的熔断逻辑")
		return e
	})
}

func NewClientHystrixWrapper() client.Wrapper {
	return func(i client.Client) client.Client {
		return &clientWrapper{i}
	}
}

base/main.go 

      //只作为客户端的时候起作用,如果存在调用别人的情况,原则上不去主动调用

        //micro.WrapClient(hystrix2.NewClientHystrixWrapper()),

        //服务端添加限流,qps 1000

        micro.WrapHandler(ratelimit.NewHandlerWrapper(1000)),

	//7.创建服务
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.base"),
		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)),
	)

3-10 go-micro v3 启用日志中心 

go-micro v3 技术栈-日志系统 elk
elk 流程讲解
docker-compose 使用引入 elk 体系
comon zap 日志,base 基础代码使用和filebeat 使用

docker-compose\chapter3\docker-stack.yml 

version: '3.3'
services:
  elasticsearch:
    image: elasticsearch:7.9.3
    ports:
      - "9200:9200"
      - "9300:9300"
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
    environment:
      ES_JAVA_OPTS: "-Xmx256m -Xms256m"
      ELASTIC_PASSWORD: password
      discovery.type: single-node
      network.publish_host: _eth0_
  logstash:
    image: logstash:7.9.3
    ports:
      - "5044:5044"
      - "5000:5000"
      - "9600:9600"
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
      - ./logstash/pipeline/logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
  kibana:
    image: kibana:7.9.3
    ports:
      - "5601:5601"
    volumes:
      - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
    depends_on:
      - elasticsearch

docker-compose\chapter3\elasticsearch\config\elasticsearch.yml

---
cluster.name: "cluster"
network.host: 0.0.0.0

xpack.license.self_generated.type: trial
xpack.security.enabled: true
xpack.monitoring.collection.enabled: true

docker-compose\chapter3\kibana\config\kibana.yml

---
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: ["http://elasticsearch:9200"]
monitoring.ui.container.elasticsearch.enabled: true

elasticsearch.username: elastic
elasticsearch.password: pwd

docker-compose\chapter3\logstash\config\logstash.yml

---
http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: ["http://elasticsearch:9200"]

xpack.monitoring.enabled: true
xpack.monitoring.elasticsearch.username: elastic
xpack.monitoring.elasticsearch.password: pwd

docker-compose\chapter3\logstash\pipeline\logstash.conf

input {
	beats {
		port => 5044
	}
	tcp {
		port => 5000
	}
}

output {
	elasticsearch {
		hosts => "elasticsearch:9200"
		user => "elastic"
		password => "password"
		index => "%{[@metadata]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
	}
}

cd chapter3

docker-compose -f docker-stack.yml up

common\zap.go

package common

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var(
	logger *zap.SugaredLogger
)

func init()  {
	//日志文件名称
	fileName := "micro.log"
	syncWriter:= zapcore.AddSync(
		&lumberjack.Logger{
			Filename:   fileName, //文件名称
			MaxSize:    512,//MB
			//MaxAge:     0,
			MaxBackups: 0, //最大备份
			LocalTime:  true,
			Compress:   true, //是否启用压缩
		})
	//编码
	encoder:=zap.NewProductionEncoderConfig()
	//时间格式
	encoder.EncodeTime = zapcore.ISO8601TimeEncoder
	core:= zapcore.NewCore(
		// 编码器
		zapcore.NewJSONEncoder(encoder),
		syncWriter,
		//
		zap.NewAtomicLevelAt(zap.DebugLevel))
	log := zap.New(
		core,
		zap.AddCaller(),
		zap.AddCallerSkip(1))
	logger = log.Sugar()
}

func Debug(args ...interface{})  {
	logger.Debug(args)
}

func Debugf(template string, args ...interface{}) {
	logger.Debugf(template, args...)
}

func Info(args ...interface{}) {
	logger.Info(args...)
}

func Infof(template string, args ...interface{}) {
	logger.Infof(template, args...)
}

func Warn(args ...interface{}) {
	logger.Warn(args...)
}

func Warnf(template string, args ...interface{}) {
	logger.Warnf(template, args...)
}

func Error(args ...interface{}) {
	logger.Error(args...)
}

func Errorf(template string, args ...interface{}) {
	logger.Errorf(template, args...)
}

func DPanic(args ...interface{}) {
	logger.DPanic(args...)
}

func DPanicf(template string, args ...interface{}) {
	logger.DPanicf(template, args...)
}

func Panic(args ...interface{}) {
	logger.Panic(args...)
}

func Panicf(template string, args ...interface{}) {
	logger.Panicf(template, args...)
}

func Fatal(args ...interface{}) {
	logger.Fatal(args...)
}

func Fatalf(template string, args ...interface{}) {
	logger.Fatalf(template, args...)
}

go get gopkg.in/natefinch/lumberjack.v2

提交到common仓库

浏览器访问127.0.0.1:5601 kibana 

D:\Workspace\Go\src\gopaas\base\main.go

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

base\filebeat.yml 

# filebeat 下载地址

# https://www.elastic.co/cn/downloads/past-releases/filebeat-7-9-3/

3-11 go-micro v3 添加监控中心 

go-micro v3 技术栈- 监控系统 prometheus + grafana
docker-compose 使用引入监控体系
prometheus 公共代码编写
base 基础代码启动 promethues,grafana监控看板

docker-compose\chapter3\docker-compose.yml

  #添加监控镜像
  prometheus:
    image: prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
  #监控看板,默认密码为admin/admin
  grafana:
    image: grafana
    ports:
      - "3000:3000"

docker-compose\chapter3\prometheus.yml

global:
  scrape_interval: 15s #默认15秒采集一次
  external_labels:
    monitor: 'go-paas-monitor'
scrape_configs:
  #监控的服务
  - job_name: 'base'
    scrape_interval: 5s #覆盖默认值
    static_configs:
      # 这个可以改,但是需要每个服务能暴露访问到
      - targets: ['192.168.204.130:9192']

docker-compose up

common\prometheus.go

package common

import (
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/log"
	"net/http"
	"strconv"
)

func PrometheusBoot(port int)  {
	http.Handle("/metrics",promhttp.Handler())
	//启动web 服务
	go func() {
		err := http.ListenAndServe("0.0.0.0:"+strconv.Itoa(port),nil)
		if err !=nil {
			log.Fatal("启动失败")
		}
		log.Info("监控启动,端口为:"+strconv.Itoa(port))
	}()
}

git add .

git commit -m "添加监控"

git push

D:\Workspace\Go\src\gopaas\base\main.go

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

启动查看grafana 127.0.0.1:3000 

添加数据源192.168.204.130:9090

创建看板go gc 时间

Metrics go_gc_duration_seconds

3-12 总结&思考

主要内容
go-micro v3 框架介绍和说明
docker-compose 的编写和使用
微服务技术栈介绍

作业
完成go-micro v3技术栈添加
理解回顾原理较深入的中间件
根据课程内容完成 docker-compose yml 文件编写 

3-13 【扩展阅读】升入源码理解 Go-micro v3

第4章 云原生 Go PaaS 平台 K8s 快速入门

开发 PaaS 平台必须要对底层 K8s 的核心有深入的了解,探究 k8s 核心组件以及核心组件的原理。

4-1 Go PaaS 平台k8s 架构原理

K8S的原理架构
K8S核心组件说明
K8S系统安装

K8S相关术语
主机(Master):用于控制 Kubernetes 节点的计算机。所有任务分配都来自于此
节点(Node):负责执行请求和所分配任务的计算机。由 Kubernetes 主机负责对节点进行控制。
容器集(Pod):被部署在单个节点上的,且包含一个或多个容器的容器组。同一容器集中的所有容器共享同一个IP 地址、IPC、主机名称及其它资源

服务 (Service):将工作内容与容器集分离
Kubelet:运行在节点上的服务,可读取容器清单 (container manifest),确保指定的容器启动并运行。
kubectl: Kubernetes 的命令行配置工具

K8S架构原理

4-2 Go PaaS 平台 k8s 核心组件-apiserver 架构原理讲解

核心组件K8S
集群管理入口: kube-apiserver
管理控制中心: kube-controller-manager
调度器: kube-scheduler

K8S核心组件
配置中心:etcd
集群管理工具: kubectl
节点 POD 管家: kubelet

K8S 核心组件-apiserver 架构解析

4-3 Go PaaS 平台 controller 与 scheduler 调度器原理(上)

K8S 核心组件- Controller Manager
副本控制器: Replication Controller
节点控制器: Node Controller
资源控制器: ResourceQuota Controller

命名空间控制器:Namespace Controller
Endpoints 控制器: Endpoints Controller

服务控制器: Service Controller

K8S 核心组件- Replication Manager 职责
确保在当前集群中有且仅有N个Pod实例,N是在RC中定义的Pod副本数量
通过调整RC的spec.replicas属性值来实现系统扩容或者缩容

通过改变RC中的Pod模板(主要是镜像版本)来实现系统的 滚动升级

K8S 核心组件- ResourceQuota Manager 三个层次资源配额管理
容器级别-可以对CPU和Memory进行限制。
Pod级别-可以对一个Pod内所有容器的可用资源进行限制
Namespace级别,为Namespace(多租户)级别的资源限制,包括:POD 、RC、Service、ResourceQuota、Secret、PV数量.

4-4 Go PaaS 平台 controller 与 scheduler 调度器原理(下)

K8S 核心组件- Endpoints Controller 说明

K8S 核心组件- 调度器 scheduler

作用:承接controller 创建的pod,为其安排可以运行的目标node。
默认调度流程一:预选调度
默认调度流程二:最优节点调度

 K8S 核心组件- 调度器 scheduler 预选策略
NoDisConflict:检查备选pod的所有volume与备选节点上的均无冲突
PodFitsResource:判断备选节点资源是否满足
PodsSelectorMatches:判断备选节点是否包含制定的标签选择器

PodFitsHost:判断备选pod的nodename是否与备选节点一致
checkNodeLabelPresence:判断是否选择备份节点
PodFitsPorts:判断备选节点端口是否被占用

K8S 核心组件- 调度器 scheduler 优选策略
LeastRequestdPriority:从备选节点列表中选出资源消耗最小的节点
CalculateNodeLabelPriority: 计算是否选择备份节点
BalancedResourceAllocation: 从备选节点中选择资源占用最小的

4-5 Go PaaS 平台 Service,deployment,pod的关系

K8S 核心组件- service,deployment,pod的关系

4-6 【扩展阅读】RS和Replicaset 区别

4-7 k8s的安装(上)

GO PaaS 平台 K8S 安装

K8S集群安装说明
K8s的集群安装分为:单Master,多Master
可以使用的机器:阿里云,腾讯云或者自己的虚拟机

K8S集群安装基础
两台2核4G 服务器
CentOS 7.8 或 CentOS Stream 8
Kubernetes v1.21.X

Kubernetes v1.24.2

4-8 k8s的安装(下)

gopaas\k8s-install\k8s 安装指导说明.md

## Step 1 : 首先在每台机器上设置hostname
#### 1.1 : 修改 hostname master.wu.com 或者 node1.wu.com
`hostnamectl set-hostname xxxxxxx`
#### 1.2 : 查看修改结果
`hostnamectl status`
#### 1.3 : 设置 hostname 解析
`echo "127.0.0.1   $(hostname)" >> /etc/hosts`

## Step 2 :机器检查
所有节点必须保证以下条件
- 任意节点 centos 版本为 7.6 , 7.7 , 7.8 或者 centos stream 8
- 任意节点 CPU 内核数量大于等于 2,且内存大于等于 4G
- 任意节点 hostname 不是 localhost,且不包含下划线、小数点、大写字母
- 任意节点都有固定的内网 IP 地址
- 任意节点都只有一个网卡,如果有特殊目的,我可以在完成 K8S 安装后再增加新的网卡
- 任意节点上 Kubelet使用的 IP 地址 可互通(无需 NAT 映射即可相互访问),且没有防火墙、安全组隔离
- 任意节点不会直接使用 docker run 或 docker-compose 运行容器


## Step 3 :base-install.sh 所有节点基础安装
把 base-install.sh 拷贝到服务器 
- 执行 这是阿里云镜像地址:
  `export REGISTRY_MIRROR=https://registry.cn-shanghai.aliyuncs.com`
- 执行 `./base_install.sh 1.21.5`

## Step 4 : 初始化 Master 节点 (只在 Master 节点执行)
### 4.1 :设置变量
```
# 只在 master 节点执行
# 替换 x.x.x.x 为 master 节点实际 IP(请使用内网 IP) 
# export 命令只在当前 shell 会话中有效,开启新的 shell 窗口后,如果要继续安装过程,请重新执行此处的 export 命令
export MASTER_IP=x.x.x.x
# 替换 apiserver.wu.com 为 您想要的 dnsName 
export APISERVER_NAME=apiserver.wu.com
# Kubernetes 容器组所在的网段,该网段安装完成后,由 kubernetes 创建,事先并不存在于您的物理网络中
export POD_SUBNET=10.100.0.1/16
echo "${MASTER_IP}    ${APISERVER_NAME}" >> /etc/hosts
```

### 4.2 :执行 `./install_master.sh 1.21.5`

### 4.3 : 检查执行结果
```
# 只在 master 节点执行
# 执行如下命令,等待 3-10 分钟,直到所有的容器组处于 Running 状态
watch kubectl get pod -n kube-system -o wide

# 查看 master 节点初始化结果
kubectl get nodes -o wide 
```
### 4.4 : 在Master节点上安装 Flannel 网络插件
flannel-v0.14.0.yaml

```
export POD_SUBNET=10.100.0.0/16

sed -i "s#10.244.0.0/16#${POD_SUBNET}#" flannel-v0.14.0.yaml

kubectl apply -f ./flannel-v0.14.0.yaml
```

## Step 5 :初始化 Worker 节点
### 5.1 : 首先在 Master 节点上执行以下命令
```
# 只在 master 节点执行
kubeadm token create --print-join-command

```
可获取kubeadm join 命令及参数,如下所示
```
# kubeadm token create 命令的输出,形如:
kubeadm join apiserver.wu.com:6443 --token o5vmo9.bazxuhkyew9rajvi     --discovery-token-ca-cert-hash sha256:956583e510265cb6ec4bd5f11f36a05917e822aa7e3fbf950bce0e6d732ad956 

```
>该 token 的有效时间为 2 个小时,2小时内,您可以使用此 token 初始化任意数量的 worker 节点。

### 5.2 : 初始化 worker (只在worker 节点执行)

```
# 只在 worker 节点执行
# 替换 x.x.x.x 为 master 节点的内网 IP
# export 命令只在当前 shell 会话中有效,开启新的 shell 窗口后,如果要继续安装过程,请重新执行此处的 export 命令
export MASTER_IP=x.x.x.x
# 替换 apiserver.wu.com 为 您想要的 dnsName 
export APISERVER_NAME=apiserver.wu.com
echo "${MASTER_IP}    ${APISERVER_NAME}" >> /etc/hosts
```

### 5.3 : 执行 Master 节点上 token 信息加入集群
```
# 替换为 master 节点上 kubeadm token create 命令的输出
kubeadm join apiserver.wu.com:6443 --token o5vmo9.bazxuhkyew9rajvi     --discovery-token-ca-cert-hash sha256:956583e510265cb6ec4bd5f11f36a05917e822aa7e3fbf950bce0e6d732ad956 

```
           
### 5.4 :检查初始化结果
在 master 节点上执行(只在Master上)
`kubectl get nodes -o wide`

D:\Workspace\gopaas\k8s-install\base_install.sh

#!/bin/bash
# 在 master 节点和 node 节点都要执行
# 阿里云 docker hub 镜像
#export REGISTRY_MIRROR=https://registry.cn-hangzhou.aliyuncs.com

# 在 master 节点和 node 节点都要执行

# 安装 containerd
# 参考文档如下
# https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd

# cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
# overlay
# br_netfilter
# EOF

# sudo modprobe overlay
# sudo modprobe br_netfilter

# # Setup required sysctl params, these persist across reboots.
# cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
# net.bridge.bridge-nf-call-iptables  = 1
# net.ipv4.ip_forward                 = 1
# net.bridge.bridge-nf-call-ip6tables = 1
# EOF

# sysctl --system

# 卸载旧版本
# yum remove -y containerd.io

# 卸载旧版本
yum remove -y docker \
docker-client \
docker-client-latest \
docker-ce-cli \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

# 设置 yum repository
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo



# 安装并启动 docker
yum install -y docker-ce docker-ce-cli
# yum install -y containerd.io-1.4.3

mkdir /etc/docker || true

cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": ["${REGISTRY_MIRROR}"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}
EOF

mkdir -p /etc/systemd/system/docker.service.d

# Restart Docker
systemctl daemon-reload
systemctl enable docker
systemctl restart docker


# mkdir -p /etc/containerd
# containerd config default > /etc/containerd/config.toml

# sed -i "s#k8s.gcr.io#registry.aliyuncs.com/k8sxio#g"  /etc/containerd/config.toml
# sed -i '/containerd.runtimes.runc.options/a\ \ \ \ \ \ \ \ \ \ \ \ SystemdCgroup = true' /etc/containerd/config.toml
# sed -i "s#https://registry-1.docker.io#${REGISTRY_MIRROR}#g"  /etc/containerd/config.toml


# systemctl daemon-reload
# systemctl enable containerd
# systemctl restart containerd


# 安装 nfs-utils
# 必须先安装 nfs-utils 才能挂载 nfs 网络存储
yum install -y nfs-utils
yum install -y wget

# 关闭 防火墙
systemctl stop firewalld
systemctl disable firewalld

# 关闭 SeLinux
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config

# 关闭 swap
swapoff -a
yes | cp /etc/fstab /etc/fstab_bak
cat /etc/fstab_bak |grep -v swap > /etc/fstab

# 配置K8S的yum源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
       http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

# 卸载旧版本
yum remove -y kubelet kubeadm kubectl

# 安装kubelet、kubeadm、kubectl
# 将 ${1} 替换为 kubernetes 版本号,例如 1.19.5
yum install -y kubelet-${1} kubeadm-${1} kubectl-${1}

# crictl config runtime-endpoint /run/containerd/containerd.sock

# 重启 docker,并启动 kubelet
systemctl daemon-reload
systemctl restart docker
systemctl enable kubelet && systemctl start kubelet

docker --version
kubelet --version

k8s-install\check_host.sh 

# 在 master 节点和 worker 节点都要执行
cat /etc/redhat-release

# 此处 hostname 的输出将会是该机器在 Kubernetes 集群中的节点名字
# 不能使用 localhost 作为节点的名字
hostname

# 请使用 lscpu 命令,核对 CPU 信息
# Architecture: x86_64    本安装文档不支持 arm 架构
# CPU(s):       2         CPU 内核数量不能低于 2
lscpu

k8s-install\flannel-v0.14.0.yaml

---
kind: Namespace
apiVersion: v1
metadata:
  name: kube-flannel
  labels:
    pod-security.kubernetes.io/enforce: privileged
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-flannel
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-flannel
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-flannel
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni-plugin
       #image: flannelcni/flannel-cni-plugin:v1.1.0 for ppc64le and mips64le (dockerhub limitations may apply)
        image: docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
        command:
        - cp
        args:
        - -f
        - /flannel
        - /opt/cni/bin/flannel
        volumeMounts:
        - name: cni-plugin
          mountPath: /opt/cni/bin
      - name: install-cni
       #image: flannelcni/flannel:v0.19.2 for ppc64le and mips64le (dockerhub limitations may apply)
        image: docker.io/rancher/mirrored-flannelcni-flannel:v0.19.2
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
       #image: flannelcni/flannel:v0.19.2 for ppc64le and mips64le (dockerhub limitations may apply)
        image: docker.io/rancher/mirrored-flannelcni-flannel:v0.19.2
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: EVENT_QUEUE_DEPTH
          value: "5000"
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
        - name: xtables-lock
          mountPath: /run/xtables.lock
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni-plugin
        hostPath:
          path: /opt/cni/bin
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate

k8s-install\install_master.sh

#!/bin/bash

# 只在 master 节点执行

# 脚本出错时终止执行
set -e

if [ ${#POD_SUBNET} -eq 0 ] || [ ${#APISERVER_NAME} -eq 0 ]; then
  echo -e "\033[31;1m请确保您已经设置了环境变量 POD_SUBNET 和 APISERVER_NAME \033[0m"
  echo 当前POD_SUBNET=$POD_SUBNET
  echo 当前APISERVER_NAME=$APISERVER_NAME
  exit 1
fi

# 查看完整配置选项 https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2
rm -f ./kubeadm-config.yaml
cat <<EOF > ./kubeadm-config.yaml
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v${1}
imageRepository: k8s.gcr.io
controlPlaneEndpoint: "${APISERVER_NAME}:6443"
networking:
  serviceSubnet: "10.96.0.0/16"
  podSubnet: "${POD_SUBNET}"
  dnsDomain: "cluster.local"
dns:
  type: CoreDNS
  imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
  imageTag: 1.8.0

---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF

# kubeadm init
# 根据您服务器网速的情况,您需要等候 3 - 10 分钟
echo ""
echo "抓取镜像,请稍候..."
kubeadm config images pull --config=kubeadm-config.yaml
echo ""
echo "初始化 Master 节点"
kubeadm init --config=kubeadm-config.yaml --upload-certs

# 配置 kubectl
rm -rf /root/.kube/
mkdir /root/.kube/
cp -i /etc/kubernetes/admin.conf /root/.kube/config
 
 

4-9 总结&思考

主要内容
介绍 k8s 基础概念,核心原理,核心组件
K8s集群化安装

经验之谈
本章K8s 原理,组件原理,安装,简单实用能够带领大家入门k8s
生产环境中k8s还有许多要注意的地方,比如分布式存储

K8s网络通信是啥样?
K8s里面的应用能直接访问外网吗?
K8s网络外的如何访问内部应用?

第5章 云原生 Go PaaS 平台应用服务管理功能开发,产品化创建资源

容器应用的管理为 PaaS 平台的核心内容,更是平时使用最多的模块,该模块对生产使用,动态创建,资源分配都有极大的简化作用,能够帮助研发,运维人员通过改功能,快速创建需要的资源及需要部署的应用,完成相关模块功能的开发,并掌握 K8S 部署的实用技巧。

5-1 PaaS 平台应用开发-Deployment介绍

主要内容
Deployment 原理讲解
POD 原理说明及生命周期介绍
基于POD开发PaaS平台应用管理功能

Deployment 是什么?
Deployment的作用
Deployment 和 ReplicaSet 和 Pod 的关系
Deployment 滚动更新,回滚原理讲解

Deployment的作用
定义一组 Pod 期望数量,Controller 会维持 Pod 数量
能够制定Pod 的发布策略(比如:滚动发布
能够让Pod 发布支持回滚操作

Deployment 与 ReplicaSet 和 Pod 的关系
定义一组 Pod 期望数量,Controller 会维持 Pod 数量
能够制定 Pod 的发布策略(比如:滚动发布)

Deployment 的同步触发条件
Deployment 内容发生了改变
Deployment 关联的 ReplicaSet 发生改变
Deployment 相关的 Pod 数量为0,Pod 的删除事件 

Deployment 滚动更新
更新策略选用 RollingUpdate
滚动更新策略参数:maxUnavailable 最大不可用
滚动更新策略参数:maxSurge 额外创建个数

Deployment 回滚操作
Deployment 资源都会包含有 revision 这个概念,可以回滚
spec.revisionHistoryLimit 数量内的replicaSet版本会保存
保存的replicaset 只是没有对于的pod相关的信息还在 

5-2 PaaS 平台Pod 基本概念依据调度策略介绍

Pod 快速入门
什么是Pod?
Pod 相关基础知识
Pod 高级设置(例:亲和性,反亲和性,污点,容忍)

什么是 Pod ?
Pod 是k8s里面能够被调度的最小逻辑单元(原子单元)
1个Pod 里面可以运行多个容器

Pod 共享哪些资源?
PID命名空间: Pod中的不同应用程序可以看到其他应用程序的进程ID;
网络命名空间: Pod中的多个容器能够访问同一个IP和端口范围;

IPC命名空间:Pod中的多个容器能够使用SystemVIPC或POSIX消息队列进行通信:
UTS命名空间: Pod中的多个容器共享一个主机名:
Volumes(共享存储卷): Pod中的各个容器可以访问在Pod级别定义的Volumes; 

Pod的生命周期

Pending: API Server已经创建了该Pod,但Pod中的一个或多个容器的镜像还没有创建,包括镜像下载过程
Running: Pod内所有容器已创建,且至少一个容器处于运行状态、正在启动状态或正在重启状态

Succeeded:Pod内所有容器均成功执行退出,且不会再重启

Failed:Pod内所有容器均已退出,但至少一个容器退出失败

Unknown:由于某种原因无法获取Pod状态,例如网络通信不畅

Pod 的重启策略
Always:当容器失效时,由kubelet自动重启该容器
OnFailure:当容器终止运行且退出码不为0时重启
Never: 不论容器运行状态如何,kubelet都不会重启该容器

Pod管理控制器: replicat,Job,DaemonSet 及 Kubelet
RC和DaemonSet: 必须设置为Aways,要保证该容器持续运行
Job: OnFailure或Never,确保容器执行完后不再重启

kubelet: Pod失效时重启,并且不进行健康检查

Pod 健康检查
Pod的健康状态由两类探针来检查
LivenessProbe
ReadinessProbe

Pod 健康检查 LivenessProbe 探针
用于判断容器是否存活(running状态)
如果不健康,根据重启策略做响应的处理
不包含LivenessProbe探针,永远返回为“success

initialDelaySeconds: 启动容器后首次进行健康检查的等待时间单位为秒。
timeoutSeconds: 健康检查发送请求后等待响应的时间

Pod 健康检查 ReadinessProbe 探针
用于判断容器是否启动完成(read状态),可以接受请求
如果ReadnessProbe探针检测失败,则Pod的状态将被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Endpoint。

Pod 调度-RC、Deployment:全自动调度策略
系统内置调度算法[最优Node]
NodeSelector[定向调度]: 指定调度到目标类型的机器上

NodeAffinity[亲和性调度]

Pod 调度-NodeAffinity 节点亲和性调度
通过In (属于)、Notln (不属于)
Exists(存在一个条件)、DoesNotExist (不存在)
Gt(大于)、Lt(小于)等操作符来选择Node,使调度更加灵活

Pod 调度 - DaemonSet: 特定场景调度
在每个Node上运行一个GlusterFS存储或者Ceph存储的daemon进程
在每个Node上运行一个日志采集程序,如: logstash
采集该Node的运行性能数据,如: Prometheus Node Exportor

Pod 调度 - Job:批处理调度
Job Template Expansion调度模式
Queue with Pod Per Work Item模式
Queue with Variable Pod Count调度模式

 

Pod 调度-PodAffinity Pod亲和性和反亲和性
pod 亲和性主要解决 pod 可以和哪些 pod 部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等)
pod 反亲和性主要是解决 pod 不能和哪些 pod 部署在同一个拓扑域中的问题

Pod 调度-污点(Taints) 与容忍(tolerations
Taint (污点)和 Toleration (容忍)可以作用于 node 和 pod 上
其目的是优化 pod 在集群间的调度,这跟节点亲和性相反,
具有 taint 的 node 和 pod 是互斥关系

5-3 基于go mod的相关设置

gomod的配置
设置代理: go env -w GOPROXY=https://goproxy.io,direct
设置私有仓库: go env -w GOPRIVATE=*.xxx.com

Git 的相关设置
go get 内部使用https的git clone 命令默认只支持公有仓库

Git 设置支持 go get 私有仓库
替换https 为ssh 请求,这里设置ssh 是80端口不是默认的22

git config --global url."ssh://git@git.xxx.com:80/".insteadOf
"https://git.xxx.com/"

Git 设置支持 go get 私有仓库
生成 ssh ssh-keygen -t rsa -C"xxxx@xxx.com'

设置git仓库ssh密钥

5-4 GO PaaS 平台开发工程目录创建及说明

代码开发
工程目录结构
服务端和API开发
(网关代码,proto制作方法等网关及相关工具讲解

D:\Workspace\gopaas\pod

5-5 Go PaaS 平台 Pod 模型的开发(上)

D:\Workspace\Go\src\gopaas\pod\domain\model\pod.go

package model

//POD的状态
//挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间。
//运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
//成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
//失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
//未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。
type Pod struct {
	ID           int64  `gorm:"primary_key;not_null;auto_increment" json:"id"`
	PodName      string `gorm:"unique_index;not_null" json:"pod_name"`
	PodNamespace string `json:"pod_namespace"`
	//POD 所属的团队
	PodTeamID string `json:"pod_team_id"`
	//POD 使用的CPU最小值
	PodCpuMin float32 `json:"pod_cpu_min"`
	//POD 使用的CPU最大值
	PodCpuMax float32 `json:"pod_cpu_max"`
	//副本数量
	PodReplicas int32 `json:"pod_replicas"`
	//POD 使用的内存最小值
	PodMemoryMin float32 `json:"pod_memory_min"`
	//POD 使用的内存最大值
	PodMemoryMax float32 `json:"pod_memory_max"`
	//POD 开放的端口
	PodPort []PodPort `gorm:"ForeignKey:PodID" json:"pod_port"`
	//POD 使用的环境变量
	PodEnv []PodEnv `gorm:"ForeignKey:PodID" json:"pod_env"`
	//镜像拉取策略
	//Always:总是拉取 pull
	//IfNotPresent:默认值,本地有则使用本地镜像,不拉取
	//Never:只使用本地镜像,从不拉取
	PodPullPolicy string `json:"pod_pull_policy"`
	//重启策略
	//Always: 当容器失效时, 由kubelet自动重启该容器
	//OnFailure: 当容器终止运行且退出码不为0时, 由kubelet自动重启该容器
	//Never: 不论容器运行状态如何, kubelet都不会重启该容器
	//注意:
	//1.kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算, 例如1丶2丶4丶8倍等, 最长延时5min, 并且在重启后的10min后重置该时间
	//2.pod的重启策略与控制方式有关
	//- RC和DeamonSet必须设置为Always,需要保证该容器持续运行
	//- Job: OnFailure或Never, 确保容器执行完成后不再重启
	PodRestart string `json:"pod_restart"`
	//pod的发布策略
	//重建(recreate):停止旧版本部署新版本
	//滚动更新(rolling-update):一个接一个地以滚动更新方式发布新版本
	//蓝绿(blue/green):新版本与旧版本一起存在,然后切换流量
	//金丝雀(canary):将新版本面向一部分用户发布,然后继续全量发布
	//A/B测(a/b testing):以精确的方式(HTTP 头、cookie、权重等)向部分用户发布新版本。A/B测实际上是一种基于数据统计做出业务决策的技术。在 Kubernetes 中并不原生支持,需要额外的一些高级组件来完成改设置(比如Istio、Linkerd、Traefik、或者自定义 Nginx/Haproxy 等)。
	//Recreate,Custom,Rolling
	PodType string `json:"pod_type"`
	//使用的镜像名称+tag
	PodImage string `json:"pod_image"`
	//@TODO 挂盘
	//@TODO 域名设置
}

D:\Workspace\Go\src\gopaas\pod\domain\model\pod_port.go

package model

type PodPort struct {
	ID            int64  `gorm:"primary_key;not_null;auto_increment" json:"id"`
	PodID         int64  `json:"pod_id"`
	ContainerPort int32  `json:"container_port"`
	Protocol      string `json:"protocol"`
	//@TODO HostPort 需要的可以自主添加
}

D:\Workspace\gopaas\pod\domain\model\pod_env.go 

package model

type PodEnv struct {
	ID       int64  `gorm:"primary_key;not_null;auto_increment" json:"id"`
	PodID    int64  `json:"pod_id"`
	EnvKey   string `json:"env_key"`
	EnvValue string `json:"env_value"`
}

5-6 Go PaaS 平台 Pod 模型的开发(下)

	PodType string `json:"pod_type"`
	//使用的镜像名称+tag
	PodImage string `json:"pod_image"`
	//@TODO 挂盘
	//@TODO 域名设置

5-7 GO PaaS 平台 Repository 代码开发(上)

D:\Workspace\Go\src\gopaas\pod\domain\repository\pod_repository.go

PS D:\Workspace\Go\src\gopaas\pod> go mod init github.com/gopaas/pod
go: creating new go.mod: module github.com/gopaas/pod
go: to add module requirements and sums:
        go mod tidy
package repository

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

//创建需要实现的接口
type IPodRepository interface {
	//初始化表
	InitTable() error
	//根据ID查找数据
	FindPodByID(int64) (*model.Pod, error)
	//创建一条 Pod 数据
	CreatePod(*model.Pod) (int64, error)
	//根据ID删除一条 Pod 数据
	DeletePodByID(int64) error
	//修改一条数据
	UpdatePod(*model.Pod) error
	//查找Pod所有数据
	FindAll() ([]model.Pod, error)
}

//创建 PodRepository
func NewPodRepository(db *gorm.DB) IPodRepository {
	return &PodRepository{mysqlDb: db}
}

type PodRepository struct {
	mysqlDb *gorm.DB
}

func (u *PodRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.Pod{}, &model.PodEnv{}, &model.PodPort{}).Error
}

func (u *PodRepository) FindPodByID(podID int64) (pod *model.Pod, err error) {
	pod = &model.Pod{}
	return pod, u.mysqlDb.Preload("PodEnv").Preload("PodPort").First(pod, podID).Error
}

func (u *PodRepository) CreatePod(pod *model.Pod) (int64, error) {
	return pod.ID, u.mysqlDb.Create(pod).Error
}

5-8 GO PaaS 平台 Repository 代码开发(下)

D:\Workspace\Go\src\gopaas\pod\domain\repository\pod_repository.go

package repository

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

//创建需要实现的接口
type IPodRepository interface {
	//初始化表
	InitTable() error
	//根据ID查找数据
	FindPodByID(int64) (*model.Pod, error)
	//创建一条 Pod 数据
	CreatePod(*model.Pod) (int64, error)
	//根据ID删除一条 Pod 数据
	DeletePodByID(int64) error
	//修改一条数据
	UpdatePod(*model.Pod) error
	//查找Pod所有数据
	FindAll() ([]model.Pod, error)
}

//创建 PodRepository
func NewPodRepository(db *gorm.DB) IPodRepository {
	return &PodRepository{mysqlDb: db}
}

type PodRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *PodRepository) InitTable() error {
	return u.mysqlDb.CreateTable(&model.Pod{}, &model.PodEnv{}, &model.PodPort{}).Error
}

//根据ID查找Pod信息
func (u *PodRepository) FindPodByID(podID int64) (pod *model.Pod, err error) {
	pod = &model.Pod{}
	return pod, u.mysqlDb.Preload("PodEnv").Preload("PodPort").First(pod, podID).Error
}

//创建 Pod
func (u *PodRepository) CreatePod(pod *model.Pod) (int64, error) {
	return pod.ID, u.mysqlDb.Create(pod).Error
}

//根据 ID 删除 Pod 信息
func (u *PodRepository) DeletePodByID(podID int64) error {
	tx := u.mysqlDb.Begin()
	//遇到问题回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	if tx.Error != nil {
		return tx.Error
	}

	//彻底删除 POD 信息
	if err := u.mysqlDb.Where("id = ?", podID).Delete(&model.Pod{}).Error; err != nil {
		tx.Rollback()
		return err
	}

	//彻底删除 podenv 信息
	if err := u.mysqlDb.Where("pod_id = ?", podID).Delete(&model.PodEnv{}).Error; err != nil {
		tx.Rollback()
		return err
	}

	//彻底删除 podport 信息
	if err := u.mysqlDb.Where("pod_id = ?", podID).Delete(&model.PodPort{}).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

//更新Pod信息
func (u *PodRepository) UpdatePod(pod *model.Pod) error {
	return u.mysqlDb.Model(pod).Update(pod).Error
}

//获取结果集合
func (u *PodRepository) FindAll() (podAll []model.Pod, err error) {
	return podAll, u.mysqlDb.Find(&podAll).Error
}

5-9 GO PaaS 平台 Proto 对外服务开发 

D:\Workspace\Go\src\gopaas\pod\proto\pod\pod.proto

syntax = "proto3";

package pod;

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

service Pod {
    rpc AddPod(PodInfo) returns (Response) {}
    rpc DeletePod(PodId) returns (Response) {}
    rpc FindPodByID(PodId) returns (PodInfo) {}
    rpc UpdatePod(PodInfo) returns (Response) {}
    rpc FindAllPod(FindAll) returns (AllPod){}
}

message PodInfo {
    int64 id = 1;
    string pod_namespace = 2;
    string pod_name = 3;
    string pod_team_id = 4;
    float pod_cpu_max = 5;
    int32 pod_replicas =6;
    float pod_memory_max =7;
    repeated PodPort pod_port =8;
    repeated PodEnv pod_env =9;
    string pod_pull_policy=10;
    string pod_restart =11;
    string pod_type=12;
    string pod_image=13;
}

message PodPort {
    int64 pod_id =1;
    int32 container_port =2;
    string protocol =3;
}

message PodEnv {
    int64 pod_id =1;
    string env_key =2;
    string env_value =3;
}
message PodId {
    int64 id =1;
}
message Response {
    string msg =1;
}

message FindAll{

}

message AllPod {
    repeated PodInfo pod_info =1 ;
}
PS D:\Workspace\Go\bin> D:\Workspace\Go\bin\protoc --proto_path=D:\Workspace\Go\src\gopaas\pod --micro_out=D:\Workspace\Go\src\gopaas\pod --go_out=:D:\Workspace\Go\src\gopaas\pod proto/pod/pod.proto

5-10 GO PaaS 平台 Service 开发(1)

D:\Workspace\Go\src\gopaas\pod\domain\serivce\pod_data_service.go

package serivce

import (
	"github.com/gopaas/pod/domain/model"
	"github.com/gopaas/pod/domain/repository"
	"github.com/gopaas/pod/proto/pod"
	v1 "k8s.io/api/apps/v1"
	"k8s.io/client-go/kubernetes"
)

type IPodDataService interface {
	AddPod(*model.Pod) (int64, error)
	DeletePod(int64) error
	UpdatePod(*model.Pod) error
	FindPodByID(int64) (*model.Pod, error)
	FindAllPod() ([]model.Pod, error)
	CreateToK8s(*pod.PodInfo) error
	DeleteFromK8s(*model.Pod) error
	UpdateToK8s(*pod.PodInfo) error
}

func NewPodDataService(podRepository repository.IPodRepository, clientSet *kubernetes.Clientset) IPodDataService {
	return &PodDataService{
		PodRepository: podRepository,
		K8sClientSet:  clientSet,
		deployment:    &v1.Deployment{},
	}
}

type PodDataService struct {
	PodRepository repository.IPodRepository
	K8sClientSet  *kubernetes.Clientset
	deployment    *v1.Deployment
}

//添加Pod
func (u *PodDataService) AddPod(pod2 *model.Pod) (int64, error) {
	return u.PodRepository.CreatePod(pod2)
}

//删除
func (u *PodDataService) DeletePod(podID int64) error {
	return u.PodRepository.DeletePodByID(podID)
}

//更新
func (u *PodDataService) UpdatePod(pod2 *model.Pod) error {
	return u.PodRepository.UpdatePod(pod2)
}

//单个查找
func (u *PodDataService) FindPodByID(podID int64) (*model.Pod, error) {
	return u.PodRepository.FindPodByID(podID)
}

//查找所有
func (u *PodDataService) FindAllPod() ([]model.Pod, error) {
	return u.PodRepository.FindAll()
}

go get k8s.io/client-go/kubernetes@v0.24.2

5-11 GO PaaS 平台 Service 开发(2)

D:\Workspace\Go\src\gopaas\pod\domain\serivce\pod_data_service.go

package serivce

import (
	"strconv"

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

type IPodDataService interface {
	AddPod(*model.Pod) (int64, error)
	DeletePod(int64) error
	UpdatePod(*model.Pod) error
	FindPodByID(int64) (*model.Pod, error)
	FindAllPod() ([]model.Pod, error)
	CreateToK8s(*pod.PodInfo) error
	DeleteFromK8s(*model.Pod) error
	UpdateToK8s(*pod.PodInfo) error
}

func NewPodDataService(podRepository repository.IPodRepository, clientSet *kubernetes.Clientset) IPodDataService {
	return &PodDataService{
		PodRepository: podRepository,
		K8sClientSet:  clientSet,
		deployment:    &v1.Deployment{},
	}
}

type PodDataService struct {
	PodRepository repository.IPodRepository
	K8sClientSet  *kubernetes.Clientset
	deployment    *v1.Deployment
}

func (u *PodDataService) CreateToK8s(podInfo *pod.PodInfo) (err error) {

}

func (u *PodDataService) SetDeployment(podInfo *pod.PodInfo) {
	deployment := &v1.Deployment{}
	deployment.TypeMeta = v12.TypeMeta{
		Kind:       "deployment",
		APIVersion: "v1",
	}
	deployment.ObjectMeta = v12.ObjectMeta{
		Name:      podInfo.PodName,
		Namespace: podInfo.PodNamespace,
		Labels: map[string]string{
			"app-name": podInfo.PodName,
			"author":   "wu123",
		},
	}
	deployment.Spec = v1.DeploymentSpec{
		Replicas: &podInfo.PodReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": podInfo.PodName,
			},
			MatchExpressions: nil,
		},
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": podInfo.PodName,
				},
			},
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:            podInfo.PodName,
						Image:           podInfo.PodImage,
						Ports:           u.getContainerPort(podInfo),
						Env:             u.getEnv(podInfo),
						Resources:       u.getResources(podInfo),
						ImagePullPolicy: u.getImagePullPolicy(podInfo),
					},
				},
			},
		},
		Strategy:                v1.DeploymentStrategy{},
		MinReadySeconds:         0,
		RevisionHistoryLimit:    nil,
		Paused:                  false,
		ProgressDeadlineSeconds: nil,
	}
	u.deployment = deployment
}

func (u *PodDataService) getContainerPort(podInfo *pod.PodInfo) (containerPort []v13.ContainerPort) {
	for _, v := range podInfo.PodPort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "port-" + strconv.FormatInt(int64(v.ContainerPort), 10),
			ContainerPort: v.ContainerPort,
			Protocol:      u.getProtocol(v.Protocol),
		})
	}
	return
}

func (u *PodDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}
}

func (u *PodDataService) getEnv(podInfo *pod.PodInfo) (envVar []v13.EnvVar) {
	for _, v := range podInfo.PodEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

//添加Pod
func (u *PodDataService) AddPod(pod2 *model.Pod) (int64, error) {
	return u.PodRepository.CreatePod(pod2)
}

//删除
func (u *PodDataService) DeletePod(podID int64) error {
	return u.PodRepository.DeletePodByID(podID)
}

//更新
func (u *PodDataService) UpdatePod(pod2 *model.Pod) error {
	return u.PodRepository.UpdatePod(pod2)
}

//单个查找
func (u *PodDataService) FindPodByID(podID int64) (*model.Pod, error) {
	return u.PodRepository.FindPodByID(podID)
}

//查找所有
func (u *PodDataService) FindAllPod() ([]model.Pod, error) {
	return u.PodRepository.FindAll()
}

go get k8s.io/apimachinery/pkg/apis/meta/v1@v0.24.2

go get k8s.io/api/core/v1@v0.24.2

5-12 GO PaaS 平台 Service 开发(3)

D:\Workspace\Go\src\gopaas\pod\domain\serivce\pod_data_service.go

type IPodDataService interface {
	AddPod(*model.Pod) (int64, error)
	DeletePod(int64) error
	UpdatePod(*model.Pod) error
	FindPodByID(int64) (*model.Pod, error)
	FindAllPod() ([]model.Pod, error)
	CreateToK8s(*pod.PodInfo) error
	DeleteFromK8s(*model.Pod) error
	UpdateToK8s(*pod.PodInfo) error
}

func NewPodDataService(podRepository repository.IPodRepository, clientSet *kubernetes.Clientset) IPodDataService {
	return &PodDataService{
		PodRepository: podRepository,
		K8sClientSet:  clientSet,
		deployment:    &v1.Deployment{},
	}
}

type PodDataService struct {
	PodRepository repository.IPodRepository
	K8sClientSet  *kubernetes.Clientset
	deployment    *v1.Deployment
}

//创建pod到k8s中
func (u *PodDataService) CreateToK8s(podInfo *pod.PodInfo) (err error) {
	u.SetDeployment(podInfo)
	if _, err = u.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Get(context.TODO(), podInfo.PodName, v12.GetOptions{}); err != nil {
		if _, err = u.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Create(context.TODO(), u.deployment, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("创建成功")
		return nil
	} else {
		//可以写自己的业务逻辑
		common.Error("Pod " + podInfo.PodName + "已经存在")
		return errors.New("Pod " + podInfo.PodName + " 已经存在")
	}

}

//更新deployment,pod
func (u *PodDataService) UpdateToK8s(podInfo *pod.PodInfo) (err error) {
	u.SetDeployment(podInfo)
	if _, err = u.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Get(context.TODO(), podInfo.PodName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("Pod " + podInfo.PodName + " 不存在请先创建")
	} else {
		//如果存在
		if _, err = u.K8sClientSet.AppsV1().Deployments(podInfo.PodNamespace).Update(context.TODO(), u.deployment, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info(podInfo.PodName + " 更新成功")
		return nil
	}

}

//删除pod
func (u *PodDataService) DeleteFromK8s(pod *model.Pod) (err error) {
	if err = u.K8sClientSet.AppsV1().Deployments(pod.PodNamespace).Delete(context.TODO(), pod.PodName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		//写自己的业务逻辑
		return err
	} else {
		if err := u.DeletePod(pod.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除Pod ID:" + strconv.FormatInt(pod.ID, 10) + " 成功!")
	}
	return
}

func (u *PodDataService) SetDeployment(podInfo *pod.PodInfo) {
	deployment := &v1.Deployment{}
	deployment.TypeMeta = v12.TypeMeta{
		Kind:       "deployment",
		APIVersion: "v1",
	}
	deployment.ObjectMeta = v12.ObjectMeta{
		Name:      podInfo.PodName,
		Namespace: podInfo.PodNamespace,
		Labels: map[string]string{
			"app-name": podInfo.PodName,
			"author":   "wu123",
		},
	}
	deployment.Name = podInfo.PodName
	deployment.Spec = v1.DeploymentSpec{
		//副本个数
		Replicas: &podInfo.PodReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": podInfo.PodName,
			},
			MatchExpressions: nil,
		},
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					"app-name": podInfo.PodName,
				},
			},
			Spec: v13.PodSpec{
				Containers: []v13.Container{
					{
						Name:            podInfo.PodName,
						Image:           podInfo.PodImage,
						Ports:           u.getContainerPort(podInfo),
						Env:             u.getEnv(podInfo),
						Resources:       u.getResources(podInfo),
						ImagePullPolicy: u.getImagePullPolicy(podInfo),
					},
				},
			},
		},
		Strategy:                v1.DeploymentStrategy{},
		MinReadySeconds:         0,
		RevisionHistoryLimit:    nil,
		Paused:                  false,
		ProgressDeadlineSeconds: nil,
	}
	u.deployment = deployment
}

func (u *PodDataService) getContainerPort(podInfo *pod.PodInfo) (containerPort []v13.ContainerPort) {
	for _, v := range podInfo.PodPort {
		containerPort = append(containerPort, v13.ContainerPort{
			Name:          "port-" + strconv.FormatInt(int64(v.ContainerPort), 10),
			ContainerPort: v.ContainerPort,
			Protocol:      u.getProtocol(v.Protocol),
		})
	}
	return
}

func (u *PodDataService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}
}

func (u *PodDataService) getEnv(podInfo *pod.PodInfo) (envVar []v13.EnvVar) {
	for _, v := range podInfo.PodEnv {
		envVar = append(envVar, v13.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return
}

func (u *PodDataService) getResources(podInfo *pod.PodInfo) (source v13.ResourceRequirements) {
	//最大能够使用多少资源
	source.Limits = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(podInfo.PodCpuMax), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(podInfo.PodMemoryMax), 'f', 6, 64)),
	}
	//满足最少使用的资源量
	//@TODO 自己实现动态设置
	source.Requests = v13.ResourceList{
		"cpu":    resource.MustParse(strconv.FormatFloat(float64(podInfo.PodCpuMax), 'f', 6, 64)),
		"memory": resource.MustParse(strconv.FormatFloat(float64(podInfo.PodMemoryMax), 'f', 6, 64)),
	}
	return
}

func (u *PodDataService) getImagePullPolicy(podInfo *pod.PodInfo) v13.PullPolicy {
	switch podInfo.PodPullPolicy {
	case "Always":
		return "Always"
	case "Never":
		return "Never"
	case "IfNotPresent":
		return "IfNotPresent"
	default:
		return "Always"
	}
}

//添加Pod
func (u *PodDataService) AddPod(pod2 *model.Pod) (int64, error) {
	return u.PodRepository.CreatePod(pod2)
}

//删除
func (u *PodDataService) DeletePod(podID int64) error {
	return u.PodRepository.DeletePodByID(podID)
}

//更新
func (u *PodDataService) UpdatePod(pod2 *model.Pod) error {
	return u.PodRepository.UpdatePod(pod2)
}

//单个查找
func (u *PodDataService) FindPodByID(podID int64) (*model.Pod, error) {
	return u.PodRepository.FindPodByID(podID)
}

//查找所有
func (u *PodDataService) FindAllPod() ([]model.Pod, error) {
	return u.PodRepository.FindAll()
}

go get github.com/yunixiangfeng/common@v1.2.1

5-13 GO PaaS 平台 Main 开发、基础中间件创建(上)

D:\Workspace\Go\src\gopaas\pod\main.go

go get github.com/asim/go-micro/plugins/registry/consul/v3

go get github.com/asim/go-micro/v3/registry

package main

import (
	"fmt"
	"strconv"

	"github.com/asim/go-micro/plugins/registry/consul/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/jinzhu/gorm"
	"github.com/yunixiangfeng/common"
)

var (
	hostIp               = "192.168.204.130"
	serviceHost          = hostIp
	servicePort          = "8081"
	consulHost           = hostIp
	consulPort     int64 = 8500
	tracerHost           = hostIp
	tracerPort           = 6831
	hystrixPort          = 9091
	prometheusPort       = 9191
)

func main() {
	consul := consul.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			consulHost + ":" + strconv.FormatInt(consulPort, 10),
		}
	})
	consulConfig, err := common.GetConsulConfig(consulHost, consulPort, "/micro/config")
	if err != nil {
		common.Error(err)
	}
	mysqlInfo := common.GetMysqlFromConsul(consulConfig, "mysql")
	db, err := gorm.Open("mysql", mysqlInfo.User+":"+mysqlInfo.Pwd+"@("+mysqlInfo.Host+":3306)/"+mysqlInfo.Database+"?charset=utf8mb4&parseTime=True&loc=Local")
	if err != nil {
		fmt.Println(err)
		common.Error(err)
	}
	defer db.Close()
	db.SingularTable(true)
}

5-14 GO PaaS 平台 Main 开发、基础中间件创建(下)

D:\Workspace\Go\src\gopaas\pod\main.go

go get github.com/opentracing/opentracing-go

go get github.com/asim/go-micro/plugins/wrapper/trace/opentracing/v3

package main

import (
	"fmt"
	"net"
	"net/http"
	"strconv"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/jinzhu/gorm"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/common"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8081"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断器
	hystrixPort = 9091
	//监控端口
	prometheusPort = 9191
)

func main() {

	//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.Error(err)
	}
	defer db.Close()
	db.SingularTable(true)
	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.pod", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

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

	//添加监听程序
	go func() {
		//http://192.168.0.112: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)
		}
	}()

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

	//7.添加监控
	common.PrometheusBoot(prometheusPort)
}

D:\Workspace\gopaas\pod\filebeat.yml

# 输入
# filebeat 下载地址
# https://www.elastic.co/cn/downloads/past-releases/filebeat-7-9-3/

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - ./*.log

output.logstash:
  hosts: ["localhost:5044"]

5-15 创建k8s集群config ,通过kubectl操作k8s集群(上)

D:\Workspace\Go\src\gopaas\pod\main.go

package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"path/filepath"
	"strconv"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	"github.com/asim/go-micro/v3/registry"
	"github.com/jinzhu/gorm"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/common"
	"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 = "8081"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断器
	hystrixPort = 9091
	//监控端口
	prometheusPort = 9191
)

func main() {

	//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.Error(err)
	}
	defer db.Close()
	db.SingularTable(true)
	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.pod", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

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

	//添加监听程序
	go func() {
		//http://192.168.0.112: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)
		}
	}()

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

	//7.添加监控
	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
	// 4.sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//创建k8s连接
	//在集群外部使用
	//-v /home/wu123/.kube/config:/root/.kube/config
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "kubeconfig file 在当前系统中的地址")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "kubeconfig file 在当前系统中的地址")
	}
	flag.Parse()
	//创建 config 实例
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中使用
	//config , err := rest.InClusterConfig()
	//if err!=nil {
	//	panic(err.Error())
	//}

	//创建程序可操作的客户端
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

}

5-16 创建k8s集群config ,通过kubectl操作k8s集群(下)

D:\Workspace\Go\src\gopaas\pod\main.go

package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"path/filepath"
	"strconv"

	"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/opentracing/opentracing-go"
	"github.com/yunixiangfeng/common"
	"github.com/yunixiangfeng/gopaas/pod/domain/repository"
	service2 "github.com/yunixiangfeng/gopaas/pod/domain/service"
	"github.com/yunixiangfeng/gopaas/pod/handler"
	hystrix2 "github.com/yunixiangfeng/gopaas/pod/plugin/hystrix"
	"github.com/yunixiangfeng/gopaas/pod/proto/pod"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"

	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var (
	//服务地址
	hostIp = "192.168.204.130"
	//服务地址
	serviceHost = hostIp
	//服务端口
	servicePort = "8081"
	//注册中心配置
	consulHost       = hostIp
	consulPort int64 = 8500
	//链路追踪
	tracerHost = hostIp
	tracerPort = 6831
	//熔断器
	hystrixPort = 9091
	//监控端口
	prometheusPort = 9191
)

func main() {

	//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.Error(err)
	}
	defer db.Close()
	db.SingularTable(true)
	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.pod", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

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

	//添加监听程序
	go func() {
		//http://192.168.0.112: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)
		}
	}()

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

	//7.添加监控
	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
	// 4.sudo chown root: /usr/local/bin/kubectl
	// 5.kubectl version --client
	// 6.集群模式下直接拷贝服务端~/.kube/config 文件到本机 ~/.kube/confg 中
	//   注意:- config中的域名要能解析正确
	//        - 生产环境可以创建另一个证书
	// 7.kubectl get ns 查看是否正常
	//
	//创建k8s连接
	//在集群外部使用
	//-v /home/wu123/.kube/config:/root/.kube/config
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "kubeconfig file 在当前系统中的地址")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "kubeconfig file 在当前系统中的地址")
	}
	flag.Parse()
	//创建 config 实例
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		common.Fatal(err.Error())
	}

	//在集群中使用
	//config , err := rest.InClusterConfig()
	//if err!=nil {
	//	panic(err.Error())
	//}

	//创建程序可操作的客户端
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		common.Fatal(err.Error())
	}

	//创建服务实例
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.service.pod"),
		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.NewPodRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}

	//注册句柄
	podDataService := service2.NewPodDataService(repository.NewPodRepository(db), clientset)
	pod.RegisterPodHandler(service.Server(), &handler.PodHandler{PodDataService: podDataService})

	//启动服务
	if err := service.Run(); err != nil {
		common.Fatal(err)
	}
}

D:\Workspace\gopaas\pod\plugin\hystrix\hystrix.go

package hystrix

import (
	"context"
	"fmt"
	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/v3/client"
)

type clientWrapper struct {
	client.Client
}

//熔断逻辑
func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	return hystrix.Do(req.Service()+"."+req.Endpoint(), func() error {
		//正常执行
		fmt.Println(req.Service() + "." + req.Endpoint())
		return c.Client.Call(ctx, req, rsp, opts...)
	}, func(e error) error {
		//走熔断逻辑,每个服务都不一样
		fmt.Println(req.Service() + "." + req.Endpoint()+"的熔断逻辑")
		return e
	})
}

func NewClientHystrixWrapper() client.Wrapper {
	return func(i client.Client) client.Client {
		return &clientWrapper{i}
	}
}

5-17 pod handler 对外服务逻辑实现(上)

D:\Workspace\Go\src\gopaas\pod\handler\podHandler.go

package handler

import (
	"context"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/pod/domain/model"
	"github.com/yunixiangfeng/gopaas/pod/domain/service"
	"github.com/yunixiangfeng/gopaas/pod/proto/pod"
	"strconv"
)

type PodHandler struct {
	//注意这里的类型实 IPodDataService 接口类型
	PodDataService service.IPodDataService
}

//添加创建POD
func (e *PodHandler) AddPod(ctx context.Context, info *pod.PodInfo, rsp *pod.Response) error {
	common.Info("添加pod")
	podModel := &model.Pod{}
	err := common.SwapTo(info, podModel)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}

	if err := e.PodDataService.CreateToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//操作数据库写入数据
		podID, err := e.PodDataService.AddPod(podModel)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		common.Info("Pod 添加成功数据库ID号为:" + strconv.FormatInt(podID, 10))
		rsp.Msg = "Pod 添加成功数据库ID号为:" + strconv.FormatInt(podID, 10)
	}
	return nil
}

//删除k8s中的pod 和数据库中的数据
func (e *PodHandler) DeletePod(ctx context.Context, req *pod.PodId, rsp *pod.Response) error {
	//先查找数据
	podModel, err := e.PodDataService.FindPodByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := e.PodDataService.DeleteFromK8s(podModel); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

5-18 pod handler 对外服务逻辑实现(下) 

 D:\Workspace\Go\src\gopaas\pod\handler\podHandler.go

package handler

import (
	"context"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/pod/domain/model"
	service "github.com/yunixiangfeng/gopaas/pod/domain/service"
	"github.com/yunixiangfeng/gopaas/pod/proto/pod"
	"strconv"
)

type PodHandler struct {
	//注意这里的类型实 IPodDataService 接口类型
	PodDataService service.IPodDataService
}

//添加创建POD
func (e *PodHandler) AddPod(ctx context.Context, info *pod.PodInfo, rsp *pod.Response) error {
	common.Info("添加pod")
	podModel := &model.Pod{}
	err := common.SwapTo(info, podModel)
	if err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	}

	if err := e.PodDataService.CreateToK8s(info); err != nil {
		common.Error(err)
		rsp.Msg = err.Error()
		return err
	} else {
		//操作数据库写入数据
		podID, err := e.PodDataService.AddPod(podModel)
		if err != nil {
			common.Error(err)
			rsp.Msg = err.Error()
			return err
		}
		common.Info("Pod 添加成功数据库ID号为:" + strconv.FormatInt(podID, 10))
		rsp.Msg = "Pod 添加成功数据库ID号为:" + strconv.FormatInt(podID, 10)
	}
	return nil
}

//删除k8s中的pod 和数据库中的数据
func (e *PodHandler) DeletePod(ctx context.Context, req *pod.PodId, rsp *pod.Response) error {
	//先查找数据
	podModel, err := e.PodDataService.FindPodByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	if err := e.PodDataService.DeleteFromK8s(podModel); err != nil {
		common.Error(err)
		return err
	}
	return nil
}

//更新指定的pod
func (e *PodHandler) UpdatePod(ctx context.Context, req *pod.PodInfo, rsp *pod.Response) error {
	//先更新k8s中的pod信息
	err := e.PodDataService.UpdateToK8s(req)
	if err != nil {
		common.Error(err)
		return err
	}
	//查询数据库中的pod
	podModel, err := e.PodDataService.FindPodByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	err = common.SwapTo(req, podModel)
	if err != nil {
		common.Error(err)
		return err
	}
	e.PodDataService.UpdatePod(podModel)
	return nil

}

//查询单个信息
func (e *PodHandler) FindPodByID(ctx context.Context, req *pod.PodId, rsp *pod.PodInfo) error {
	//查询pod数据
	podModel, err := e.PodDataService.FindPodByID(req.Id)
	if err != nil {
		common.Error(err)
		return err
	}
	err = common.SwapTo(podModel, rsp)
	if err != nil {
		common.Error(err)
		return err
	}
	return nil

}

//查询所有pod
func (e *PodHandler) FindAllPod(ctx context.Context, req *pod.FindAll, rsp *pod.AllPod) error {
	//查询所有pod
	allPod, err := e.PodDataService.FindAllPod()
	if err != nil {
		common.Error(err)
		return err
	}
	//整理格式
	for _, v := range allPod {
		podInfo := &pod.PodInfo{}
		err := common.SwapTo(v, podInfo)
		if err != nil {
			common.Error(err)
			return err
		}
		rsp.PodInfo = append(rsp.PodInfo, podInfo)
	}
	return nil
}

验证 gopaas\pod go run main.go

cd chapter3 

docker-compose up 

日志统一记录在根目录 micro.log 文件中,请点击查看日志!
2023-05-23 12:24:16  file=v3@v3.7.1/service.go:206 level=info Starting [service] go.micro.service.pod
2023-05-23 12:24:16  file=server/rpc_server.go:820 level=info Transport [http] Listening on [::]:8081
2023-05-23 12:24:16  file=server/rpc_server.go:840 level=info Broker [http] Connected to 127.0.0.1:41758
2023-05-23 12:24:16  file=server/rpc_server.go:654 level=info Registry [consul] Registering node: go.micro.service.pod-fa32e608-c57a-419a-9111-dc7529e1b7eb

5-19 POD 服务打包到docker中的注意事项及代码修改注意事项

pod\Dockerfile

FROM alpine
ADD pod /pod
ENTRYPOINT [ "/pod" ]

进入pod目录

在linux上编译CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o pod *.go

docker build -t wu123/pod . 

docker run -p 8081:8081 -p 9092:9092 -p 9192:9192 -v /home/wu123/.kube/config:/root/.kube/config -v /home/wu123/test/micro.log:/micro.log wu123/pod

5-20 Pod API 工程目录以及 proto 文件开发

Go PaaS 平台应用管理开发

服务API开发

目录

C:\Users\Administrator\Desktop\gopaas\podapi\proto\podApi\podApi.proto

syntax = "proto3";

package podApi;

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

service PodApi {
    rpc FindPodById (Request) returns (Response) {}
    rpc AddPod(Request) returns (Response){}
    rpc DeletePodById(Request) returns (Response){}
    rpc UpdatePod(Request) returns (Response){}
    //默认接口
    rpc Call(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;
}
PS D:\Workspace\Go\bin> D:\Workspace\Go\bin\protoc --proto_path=C:\Users\Administrator\Desktop\gopaas\podapi 
--micro_out=C:\Users\Administrator\Desktop\gopaas\podapi --go_out=:C:\Users\Administrator\Desktop\gopaas\podapi proto/podApi/podApi.proto

5-21 Pod API Handler 开发

PS C:\Users\Administrator\Desktop\gopaas\podapi> go mod init github.com/yunixiangfeng/gopaas/podapi
go: creating new go.mod: module github.com/yunixiangfeng/gopaas/podapi
go: to add module requirements and sums:
        go mod tidy

go get github.com/yunixiangfeng/gopaas/pod/proto/pod

C:\Users\Administrator\Desktop\gopaas\podapi\handler\podApiHandler.go

package handler

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

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/pod/proto/pod"
	form "github.com/yunixiangfeng/gopaas/podapi/plugin/form"
	"github.com/yunixiangfeng/gopaas/podapi/proto/podApi"
)

type PodApi struct {
	PodService pod.PodService
}

// podApi.FindPodById 通过API向外暴露为/podApi/findPodById,接收http请求
// 即:/podApi/FindPodById 请求会调用go.micro.api.podApi 服务的podApi.FindPodById 方法
func (e *PodApi) FindPodById(ctx context.Context, req *podApi.Request, rsp *podApi.Response) error {
	fmt.Println("接受到 podApi.FindPodById 的请求")
	if _, ok := req.Get["pod_id"]; !ok {
		rsp.StatusCode = 500
	}
	//获取podid 参数
	podIdString := req.Get["pod_id"].Values[0]
	podId, err := strconv.ParseInt(podIdString, 10, 64)
	if err != nil {
		return err
	}
	//获取pod相关信息
	podInfo, err := e.PodService.FindPodByID(ctx, &pod.PodId{
		Id: podId,
	})
	//json 返回pod信息
	rsp.StatusCode = 200
	b, _ := json.Marshal(podInfo)
	rsp.Body = string(b)
	return nil
}

// podApi.AddPod 通过API向外暴露为/podApi/addPod,接收http请求
// 即:/podApi/AddPod 请求会调用go.micro.api.podApi 服务的podApi.AddPod 方法
func (e *PodApi) AddPod(ctx context.Context, req *podApi.Request, rsp *podApi.Response) error {
	fmt.Println("接受到 podApi.AddPod 的请求")
	addPodInfo := &pod.PodInfo{}
	//处理 port
	dataSlice, ok := req.Post["pod_port"]
	if ok {
		//特殊处理
		podSlice := []*pod.PodPort{}
		for _, V := range dataSlice.Values {
			i, err := strconv.ParseInt(V, 10, 32)
			if err != nil {
				common.Error(err)
			}
			port := &pod.PodPort{
				ContainerPort: int32(i),
				Protocol:      "TCP",
			}
			podSlice = append(podSlice, port)
		}
		addPodInfo.PodPort = podSlice
	}
	//form类型转化到结构体中
	form.FormToPodStruct(req.Post, addPodInfo)

	response, err := e.PodService.AddPod(ctx, addPodInfo)
	if err != nil {
		common.Error(err)
		return err
	}

	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// podApi.DeletePodById 通过API向外暴露为/podApi/deletePodById,接收http请求
// 即:/podApi/DeletePodById 请求会调用go.micro.api.podApi 服务的 podApi.DeletePodById 方法
func (e *PodApi) DeletePodById(ctx context.Context, req *podApi.Request, rsp *podApi.Response) error {
	fmt.Println("接受到 podApi.DeletePodById 的请求")
	if _, ok := req.Get["pod_id"]; !ok {
		return errors.New("参数异常")
	}
	//获取要删除的ID
	podIdString := req.Get["pod_id"].Values[0]
	podId, err := strconv.ParseInt(podIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//删除指定服务
	response, err := e.PodService.DeletePod(ctx, &pod.PodId{
		Id: podId,
	})
	if err != nil {
		common.Error(err)
		return err
	}

	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

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

// 默认的方法podApi.Call 通过API向外暴露为/podApi/call,接收http请求
// 即:/podApi/call或/podApi/ 请求会调用go.micro.api.podApi 服务的podApi.Call 方法
func (e *PodApi) Call(ctx context.Context, req *podApi.Request, rsp *podApi.Response) error {
	fmt.Println("接受到 podApi.Call 的请求")
	allPod, err := e.PodService.FindAllPod(ctx, &pod.FindAll{})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(allPod)
	rsp.Body = string(b)
	return nil
}

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

package form

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

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/podapi/proto/podApi"
)

//根据结构体中name标签映射数据到结构体中并且转换类型
func FormToPodStruct(data map[string]*podApi.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 == "pod_port" || dataTag == "pod_env" {
			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)
}

5-22 Pod main 开发

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

package main

import (
	"fmt"
	"net"
	"net/http"
	"strconv"

	"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/jinzhu/gorm"
	"github.com/opentracing/opentracing-go"
	"github.com/yunixiangfeng/gopaas/common"
	go_micro_service_pod "github.com/yunixiangfeng/gopaas/pod/proto/pod"
	"github.com/yunixiangfeng/gopaas/podApi/handler"
	hystrix2 "github.com/yunixiangfeng/gopaas/podApi/plugin/hystrix"
	"github.com/yunixiangfeng/gopaas/podApi/proto/podApi"

	_ "github.com/jinzhu/gorm/dialects/mysql"
)

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

func main() {

	//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.Error(err)
	}
	defer db.Close()
	db.SingularTable(true)
	//4.添加链路追踪
	t, io, err := common.NewTracer("go.micro.service.pod", tracerHost+":"+strconv.Itoa(tracerPort))
	if err != nil {
		common.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)

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

	//添加监听程序
	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)
		}
	}()

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

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

	//创建服务实例
	service := micro.NewService(
		//自定义服务地址,且必须写在其它参数前面
		micro.Server(server.NewServer(func(options *server.Options) {
			options.Advertise = serviceHost + ":" + servicePort
		})),
		micro.Name("go.micro.api.podApi"),
		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()

	//注册句柄
	podService := go_micro_service_pod.NewPodService("go.micro.service.pod", service.Client())
	//注册控制器
	if err := podApi.RegisterPodApiHandler(service.Server(), &handler.PodApi{PodService: podService}); err != nil {
		common.Error(err)
	}

	//启动服务
	if err := service.Run(); err != nil {
		common.Fatal(err)
	}
}

C:\Users\Administrator\Desktop\gopaas\podapi\filebeat.yml

# 输入
# filebeat 下载地址
# https://www.elastic.co/cn/downloads/past-releases/filebeat-7-9-3/

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - ./*.log

output.logstash:
  hosts: ["localhost:5044"]

C:\Users\Administrator\Desktop\gopaas\podapi\plugin\hystrix\hystrix.go

package hystrix

import (
	"context"
	"fmt"
	"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/v3/client"
)

type clientWrapper struct {
	client.Client
}

//熔断逻辑
func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	return hystrix.Do(req.Service()+"."+req.Endpoint(), func() error {
		//正常执行
		fmt.Println(req.Service() + "." + req.Endpoint())
		return c.Client.Call(ctx, req, rsp, opts...)
	}, func(e error) error {
		//走熔断逻辑,每个服务都不一样
		fmt.Println(req.Service() + "." + req.Endpoint()+"的熔断逻辑")
		return e
	})
}

func NewClientHystrixWrapper() client.Wrapper {
	return func(i client.Client) client.Client {
		return &clientWrapper{i}
	}
}

在podapi目录下执行go run main.go

C:\Users\Administrator\Desktop\gopaas\podapi\Dockerfile

FROM alpine

ADD podApi /podApi

ADD filebeat.yml /filebeat.yml

#Add filebeat /filebeat

ENTRYPOINT [ "/podApi" ]

5-23 统一网关的说明讲解

[root@k8s-worker01 gopaas]# ./api-gateway --help
NAME:
   api-gateway - a go-micro service

USAGE:
   api-gateway [global options] command [command options] [arguments...]

COMMANDS:
   api      Run the api gateway
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --client value                  Client for go-micro; rpc [$MICRO_CLIENT]
   --client_request_timeout value  Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s [$MICRO_CLIENT_REQUEST_TIMEOUT]
   --client_retries value          Sets the client retries. Default: 1 (default: 1) [$MICRO_CLIENT_RETRIES]
   --client_pool_size value        Sets the client connection pool size. Default: 1 (default: 0) [$MICRO_CLIENT_POOL_SIZE]
   --client_pool_ttl value         Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m [$MICRO_CLIENT_POOL_TTL]
   --register_ttl value            Register TTL in seconds (default: 60) [$MICRO_REGISTER_TTL]
   --register_interval value       Register interval in seconds (default: 30) [$MICRO_REGISTER_INTERVAL]
   --server value                  Server for go-micro; rpc [$MICRO_SERVER]
   --server_name value             Name of the server. go.micro.srv.example [$MICRO_SERVER_NAME]
   --server_version value          Version of the server. 1.1.0 [$MICRO_SERVER_VERSION]
   --server_id value               Id of the server. Auto-generated if not specified [$MICRO_SERVER_ID]
   --server_address value          Bind address for the server. 127.0.0.1:8080 [$MICRO_SERVER_ADDRESS]
   --server_advertise value        Used instead of the server_address when registering with discovery. 127.0.0.1:8080 [$MICRO_SERVER_ADVERTISE]
   --server_metadata value         A list of key-value pairs defining metadata. version=1.0.0 [$MICRO_SERVER_METADATA]
   --broker value                  Broker for pub/sub. http, nats, rabbitmq [$MICRO_BROKER]
   --broker_address value          Comma-separated list of broker addresses [$MICRO_BROKER_ADDRESS]
   --profile value                 Debug profiler for cpu and memory stats [$MICRO_DEBUG_PROFILE]
   --registry value                Registry for discovery. etcd, mdns [$MICRO_REGISTRY]
   --registry_address value        Comma-separated list of registry addresses [$MICRO_REGISTRY_ADDRESS]
   --runtime value                 Runtime for building and running services e.g local, kubernetes [$MICRO_RUNTIME]
   --runtime_source value          Runtime source for building and running services e.g github.com/micro/service (default: "github.com/micro/services") [$MICRO_RUNTIME_SOURCE]
   --selector value                Selector used to pick nodes for querying [$MICRO_SELECTOR]
   --store value                   Store used for key-value storage [$MICRO_STORE]
   --store_address value           Comma-separated list of store addresses [$MICRO_STORE_ADDRESS]
   --store_database value          Database option for the underlying store [$MICRO_STORE_DATABASE]
   --store_table value             Table option for the underlying store [$MICRO_STORE_TABLE]
   --transport value               Transport mechanism used; http [$MICRO_TRANSPORT]
   --transport_address value       Comma-separated list of transport addresses [$MICRO_TRANSPORT_ADDRESS]
   --tracer value                  Tracer for distributed tracing, e.g. memory, jaeger [$MICRO_TRACER]
   --tracer_address value          Comma-separated list of tracer addresses [$MICRO_TRACER_ADDRESS]
   --auth value                    Auth for role based access control, e.g. service [$MICRO_AUTH]
   --auth_id value                 Account ID used for client authentication [$MICRO_AUTH_ID]
   --auth_secret value             Account secret used for client authentication [$MICRO_AUTH_SECRET]
   --auth_namespace value          Namespace for the services auth account (default: "go.micro") [$MICRO_AUTH_NAMESPACE]
   --auth_public_key value         Public key for JWT auth (base64 encoded PEM) [$MICRO_AUTH_PUBLIC_KEY]
   --auth_private_key value        Private key for JWT auth (base64 encoded PEM) [$MICRO_AUTH_PRIVATE_KEY]
   --config value                  The source of the config to be used to get configuration [$MICRO_CONFIG]
   --help, -h                      show help (default: false)
[root@k8s-worker01 gopaas]# ./api-gateway --registry=consul --registry_address=192.168.204.130:8500 api --handler=api

http://127.0.0.1:8080/podApi/addPod  

5-24 API 完善及pod 前端页面开发(上)

Beagle - Responsive Admin Template - Foxy Themes

C:\Users\Administrator\Desktop\gopaas\go-paas-front\pod-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 class="active"><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><a href="form-elements.html">Elements</a>
                      </li>
                      <li><a href="form-validation.html">Validation</a>
                      </li>
                      <li><a href="form-multiselect.html">Multiselect</a>
                      </li>
                      <li><a href="form-wizard.html">Wizard</a>
                      </li>
                      <li><a href="form-masks.html">Input Masks</a>
                      </li>
                      <li><a href="form-wysiwyg.html">WYSIWYG Editor</a>
                      </li>
                      <li><a href="form-upload.html">Multi Upload</a>
                      </li>
                      <li><a href="form-editable.html">X-editable</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="pod-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>CPU</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/podApi/",
          success: function(data){
            console.log(data);
            $("#table-data").html("");
            $.each(data['pod_info'],function (i,item) {
              $("#table-data").append('                      <tr class="gradeA">\
                        <td>'+item.id+'</td>\
                        <td>'+item.pod_name+'</td>\
                        <td>'+item.pod_namespace+'</td>\
                        <td class="center">'+item.pod_cpu_max+'</td>\
                        <td class="center">'+item.pod_memory_max+'</td>\
                        <td class="center">'+item.pod_replicas+'</td>\
                        <td class="center"><a href="pod-detail.html?pod_id='+item.id+'">详情</a> <a href="http://localhost:8080/podApi/deletePodById?pod_id='+item.id+'" style="color:red;" >删除</a></td>\
                      </tr>'
              );
            })
          },
          error: function(result){
            console.log(result);
          }
        });
      });
    

 
    </script>

  </body>
</html>

C:\Users\Administrator\Desktop\gopaas\go-paas-front\pod-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 class="active"><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><a href="form-elements.html">Elements</a>
                      </li>
                      <li><a href="form-validation.html">Validation</a>
                      </li>
                      <li><a href="form-multiselect.html">Multiselect</a>
                      </li>
                      <li><a href="form-wizard.html">Wizard</a>
                      </li>
                      <li><a href="form-masks.html">Input Masks</a>
                      </li>
                      <li><a href="form-wysiwyg.html">WYSIWYG Editor</a>
                      </li>
                      <li><a href="form-upload.html">Multi Upload</a>
                      </li>
                      <li><a href="form-editable.html">X-editable</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="pod_id" name="pod_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="pod_name"  name="pod_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="pod_namespace"  name="pod_namespace">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">Pod 拉取策略:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="pod_pull_policy"  name="pod_pull_policy">
                      </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="pod_replicas"  name="pod_replicas">
                      </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="pod_restart"  name="pod_restart">
                      </div>
                    </div>
                    <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="pod_team_id"  name="pod_team_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="pod_type"  name="pod_type">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">Image镜像:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="pod_image"  name="pod_image">
                      </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/podApi/findPodById?pod_id="+getUrlParam('pod_id'),
              success: function(data){
                console.log(data);
                if(data.id != "" || data.id != null || data.id != undefined){
                  $('#pod_id').val(data.id);
                  $("#pod_name").val(data.pod_name);
                  $('#pod_namespace').val(data.pod_namespace);
                  $('#pod_pull_policy').val(data.pod_pull_policy);
                  $('#pod_replicas').val(data.pod_replicas);
                  $('#pod_restart').val(data.pod_restart);
                  $('#pod_team_id').val(data.pod_team_id);
                  $('#pod_type').val(data.pod_type);
                  $('#pod_image').val(data.pod_image);
                }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\pod-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 class="active"><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><a href="form-elements.html">Elements</a>
                      </li>
                      <li><a href="form-validation.html">Validation</a>
                      </li>
                      <li><a href="form-multiselect.html">Multiselect</a>
                      </li>
                      <li><a href="form-wizard.html">Wizard</a>
                      </li>
                      <li><a href="form-masks.html">Input Masks</a>
                      </li>
                      <li><a href="form-wysiwyg.html">WYSIWYG Editor</a>
                      </li>
                      <li><a href="form-upload.html">Multi Upload</a>
                      </li>
                      <li><a href="form-editable.html">X-editable</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/podApi/AddPod" 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="pod_name"  name="pod_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="pod_namespace"  name="pod_namespace" value="default">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">Pod 拉取策略:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="pod_pull_policy"  name="pod_pull_policy" value="Always">
                      </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="pod_replicas"  name="pod_replicas">
                      </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="pod_port">
                        <input type="text" required=""   class="form-control"   name="pod_port">
                      </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="pod_restart"  name="pod_restart" value="Always">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">团队ID:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="pod_team_id"  name="pod_team_id">
                      </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="pod_type"  name="pod_type" value="Rolling">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">Image镜像:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="pod_image"  name="pod_image">
                      </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>

5-25 API 完善及pod 前端页面开发(下)

5-26 总结&思考

主要内容
Deplyment 和Pod 直接的关系
Pod开发的整体路线
开发过程中网关的说明

经验之谈
Pod 云原生 PaaS 平台里面的最小单位,概念特性要熟记。
开发微服务应用一定要合理分层,避免业务改动代码大调。

为什么Pod中能放多个容器?
网关跨域怎么解决的?
Deployment 带来的好处有哪些?

5-27 【扩展阅读】Kubernetes Pod 优先级和抢占源码解析

5-28 【扩展阅读】POD 节点压力驱逐策略详解

5-29 【扩展阅读】K8s 调度器 kube-scheduler 详解

第6章 云原生 Go PaaS 平台服务管理功能开发,学习服务与底层的关系

PaaS 平台中服务起着至关重要的作用,负责控制多种发布策略,是保证服务负载均衡,蓝绿发布,金丝雀发布的基础,通过 Service 服务的管理功能,讲解 Service 与 Pod 的关联关系。同时介绍快速开发框架 yu-tool,yu-v3 的使用说明,也能复用于日常工作,提高开发效能.

6-1 Service 类型原理介绍

Go PaaS 平台服务管理开发
主要内容
Service 类型讲解
Service ,Endpoints,Pod 关系讲解
开发 Service管理功能

Service 简介
Service 的作用
Service 四种类型

Service 的作用
Service是一种可以访问 Pod逻辑分组的策略
Service通常是通过 Label Selector访问 Pod组
Service能够提供负载均衡的能力

Service ClusterIP 模式
可以让服务长时在线达到热更的目的
可以内网调用通过服务名称DNS的方式,集群内访问

提供负载均衡策略:随机和轮询策略

Service NodePort 模式
集群外部访问POD
端口数量有限,范围:30000-32767 

访问方式node主机IP+端口: 10.12.21.1:31021

 Service LoadBalancer 模式
单独的IP访问
会自动创建NodePort,ClusterlP两个服务
需要云厂商支持

Service ExternalName 模式
主要面向集群外部的服务
可以把外部服务映射进入集群内部,当成内部服务管理
重定向依赖DNS

 Service ,Endpoints,Pod 的关系

6-2 yu-v3,yu-tool,yu-api-gateway 工具说明

GitHub - go-micro/api: Go Micro API Gateway

启动Micro API网关

micro --registry=consul --registry_address=xxxxx api --handler=api

api-gateway --registry=consul --registry_address=192.168.204.130:8500 api --handler=api

C:\Users\Administrator\Desktop\gopaas\yu-v3

 C:\Users\Administrator\Desktop\gopaas\yu-v3\Dockerfile

FROM alpine:latest
RUN  apk update && \
#     apk add ca-certificates && \
     apk add protoc && \
     rm -rf /var/cache/apk/*

COPY protoc-gen-go protoc-gen-micro /usr/bin/

ENTRYPOINT ["protoc"]

C:\Users\Administrator\Desktop\gopaas\yu-v3\readme.md

# 工具制作说明
工具可执行文件基于 linux 环境制作,go版本1.16
## protoc 和 protoc-gen-go 的安装
### 1.下载对应版本的 protoc 
```
镜像中使用如下命令下在 dockerfile 中
 apk add protoc
```
### 2.下载安装 go 的插件protoc-gen-go 
```
go install github.com/golang/protobuf/protoc-gen-go@v1.27
```
注意:要用protoc-gen-go@v1.27 版本 
### 3.安装gen-micro
这里选择的是 v3 的版本
在课程制作的过程中 go-micro v4 刚出来测试过和proto 还有兼容问题等稳定可以切换
```
go get -u github.com/asim/go-micro/cmd/protoc-gen-micro/v3    
```
### 4.安装完成后在安装的机器上运行
protoc 

C:\Users\Administrator\Desktop\gopaas\yu-tool

6-3 Service 服务端 model 模型开发

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

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

package main

import (
	"flag"
	"fmt"
	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/svc/domain/repository"
	"path/filepath" 
    
	//"github.com/afex/hystrix-go/hystrix"
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	service2 "github.com/yunixiangfeng/gopaas/svc/domain/service"
	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"
	"github.com/yunixiangfeng/gopaas/svc/handler"
	//hystrix2 "github.com/yunixiangfeng/gopaas/svc/plugin/hystrix"
	"strconv"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	svc "github.com/yunixiangfeng/gopaas/svc/proto/svc"

)

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

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.svc",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.svc"),
		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.NewSvcRepository(db).InitTable()
	//if err != nil {
	//	common.Fatal(err)
	//}

	// 注册句柄,可以快速操作已开发的服务
	svcDataService:=service2.NewSvcDataService(repository.NewSvcRepository(db),clientset)
	svc.RegisterSvcHandler(service.Server(), &handler.SvcHandler{ SvcDataService:svcDataService})

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

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

package model

type Svc struct {
	ID int64 `gorm:"primary_key;not_null;auto_increment"`
	//服务名称
	SvcName string `gorm:"unique_index;not_null" json:"svc_name"`
	//服务名称命名空间
	SvcNamespace string `gorm:"not_null" json:"svc_namespace"`
	//绑定的POD名称
	SvcPodName string `gorm:"not_null" json:"svc_pod_name"`
	//ClusterIP,NodePort,LoadBalancer,ExternalName
	SvcType string `json:"svc_type"`
	//type类型为:ExternalName 时候启用该字段
	SvcExternalName string `json:"svc_external_name"`
	//业务侧的团队ID
	SvcTeamId string `json:"svc_team_id"`
	//服务上的端口设置
	SvcPort []SvcPort `gorm:"ForeignKey:SvcID" json:"svc_port"`
}

C:\Users\Administrator\Desktop\gopaas\svc\domain\model\svc_port.go 

package model

type SvcPort struct {
	ID    int64 `gorm:"primary_key;not_null;auto_increment"`
	SvcID int64 `json:"svc_id"`
	//服务的port
	SvcPort int32 `json:"svc_port"`
	//pod 中需要映射的port地址
	SvcTargetPort int32 `json:"svc_target_port"`
	//开启NodePort的模式下进行设置
	SvcNodePort int32 `json:"svc_node_port"`
	//端口协议
	SvcPortProtocol string `json:"svc_port_protocol"`
}

6-4 service 服务端proto 文件开发及代码生成

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

syntax = "proto3";

package svc;

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

service Svc {
	//对外提供添加服务
	rpc AddSvc(SvcInfo) returns (Response) {}
	rpc DeleteSvc(SvcId) returns (Response) {}
	rpc UpdateSvc(SvcInfo) returns (Response) {}
	rpc FindSvcByID(SvcId) returns (SvcInfo) {}
	rpc FindAllSvc(FindAll) returns (AllSvc) {}
}
message SvcInfo {
	int64 id = 1;
	string svc_namespace=2;
	string svc_name=3;
	string svc_pod_name=4;
	string svc_type=5;
	string svc_external_name=6;
	string svc_team_id=7;
	repeated SvcPort svc_port=8;
}

message SvcPort{
	int64 id =1 ;
	int64 svc_id =2;
	int32 svc_port=3;
	int32 svc_target_port=4;
	int32 svc_node_port=5;
	string svc_port_protocol=6;
}

message SvcId {
	int64 id = 1;
}

message FindAll {

}

message Response {
	string msg =1 ;
}

message AllSvc {
	repeated SvcInfo svc_info = 1;
}


PS D:\Workspace\Go\bin> D:\Workspace\Go\bin\protoc.exe --proto_path=C:\Users\Administrator\Desktop\gopaas\svc --micro_out=C:\Users\Administrator\Desktop\gopaas\svc --go_out=:C:\Users\Administrator\Desktop\gopaas\svc proto/svc/svc.proto

 proto\svc\svc.pb.go proto\svc\svc.pb.micro.go

6-5 Service 服务开发

PS C:\Users\Administrator\Desktop\gopaas\svc> go mod tidy 

C:\Users\Administrator\Desktop\gopaas\svc\domain\service\svc_data_service.go

package service

import (
	"context"
	"errors"
	"strconv"

	"github.com/yunixiangfeng/gopaas/common"
	"github.com/yunixiangfeng/gopaas/svc/domain/model"
	"github.com/yunixiangfeng/gopaas/svc/domain/repository"
	"github.com/yunixiangfeng/gopaas/svc/proto/svc"
	v1 "k8s.io/api/core/v1"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
)

//这里是接口类型
type ISvcDataService interface {
	AddSvc(*model.Svc) (int64, error)
	DeleteSvc(int64) error
	UpdateSvc(*model.Svc) error
	FindSvcByID(int64) (*model.Svc, error)
	FindAllSvc() ([]model.Svc, error)
	CreateSvcToK8s(*svc.SvcInfo) error
	UpdateSvcToK8s(*svc.SvcInfo) error
	DeleteFromK8s(*model.Svc) error
}

//创建
//注意:返回值 ISvcDataService 接口类型
func NewSvcDataService(svcRepository repository.ISvcRepository, clientSet *kubernetes.Clientset) ISvcDataService {
	return &SvcDataService{SvcRepository: svcRepository, K8sClientSet: clientSet}
}

type SvcDataService struct {
	//注意:这里是 ISvcRepository 类型
	SvcRepository repository.ISvcRepository
	K8sClientSet  *kubernetes.Clientset
}

//创建服务到K8s中
func (u *SvcDataService) CreateSvcToK8s(svcInfo *svc.SvcInfo) (err error) {
	service := u.setService(svcInfo)
	//查找是否存在指定的服务
	if _, err = u.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Get(context.TODO(), svcInfo.SvcName, v12.GetOptions{}); err != nil {
		//查找不到,就创建
		if _, err = u.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Create(context.TODO(), service, v12.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		return nil
	} else {
		common.Error("Service " + svcInfo.SvcName + "已经存在")
		return errors.New("Service " + svcInfo.SvcName + "已经存在")
	}
}

//根据svcnfo 设置Iservice 信息
func (u *SvcDataService) setService(svcInfo *svc.SvcInfo) *v1.Service {
	service := &v1.Service{}
	//设置服务类型
	service.TypeMeta = v12.TypeMeta{
		Kind:       "v1",
		APIVersion: "service",
	}
	//设置服务基础信息
	service.ObjectMeta = v12.ObjectMeta{
		Name:      svcInfo.SvcName,
		Namespace: svcInfo.SvcNamespace,
		Labels: map[string]string{
			"app-name": svcInfo.SvcPodName,
			"author":   "wu123",
		},
		Annotations: map[string]string{
			"k8s/generated-by-yu": "由代码创建",
		},
	}
	//设置服务的spec信息,采用ClusterIP模式
	service.Spec = v1.ServiceSpec{
		Ports: u.getSvcPort(svcInfo),
		Selector: map[string]string{
			"app-name": svcInfo.SvcPodName,
		},
		Type: "ClusterIP",
	}
	return service
}

func (u *SvcDataService) getSvcPort(svcInfo *svc.SvcInfo) (servicePort []v1.ServicePort) {
	for _, v := range svcInfo.SvcPort {
		servicePort = append(servicePort, v1.ServicePort{
			Name:       "port-" + strconv.FormatInt(int64(v.SvcPort), 10),
			Protocol:   v1.Protocol(v.SvcPortProtocol),
			Port:       v.SvcPort,
			TargetPort: intstr.FromInt(int(v.SvcTargetPort)),
		})
	}
	return
}

func (u *SvcDataService) UpdateSvcToK8s(svcInfo *svc.SvcInfo) (err error) {
	service := u.setService(svcInfo)
	//查找是否存在指定的服务
	if _, err = u.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Get(context.TODO(), svcInfo.SvcName, v12.GetOptions{}); err != nil {
		//查找不到
		common.Error(err)
		return errors.New("Service" + svcInfo.SvcName + "不存在请先创建")
	} else {
		if _, err = u.K8sClientSet.CoreV1().Services(svcInfo.SvcNamespace).Update(context.TODO(), service, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("Service " + svcInfo.SvcName + "更新成功")
		return nil
	}
}

func (u *SvcDataService) DeleteFromK8s(svc *model.Svc) (err error) {
	if err = u.K8sClientSet.CoreV1().Services(svc.SvcNamespace).Delete(context.TODO(), svc.SvcName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := u.DeleteSvc(svc.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除Service ID:" + strconv.FormatInt(svc.ID, 10) + "成功!")
	}
	return
}

//插入
func (u *SvcDataService) AddSvc(svc *model.Svc) (int64, error) {
	return u.SvcRepository.CreateSvc(svc)
}

//删除
func (u *SvcDataService) DeleteSvc(svcID int64) error {
	return u.SvcRepository.DeleteSvcByID(svcID)
}

//更新
func (u *SvcDataService) UpdateSvc(svc *model.Svc) error {
	return u.SvcRepository.UpdateSvc(svc)
}

//查找
func (u *SvcDataService) FindSvcByID(svcID int64) (*model.Svc, error) {
	return u.SvcRepository.FindSvcByID(svcID)
}

//查找
func (u *SvcDataService) FindAllSvc() ([]model.Svc, error) {
	return u.SvcRepository.FindAll()
}

6-6 svc API 工程创建及接口业务逻辑开发

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

C:\Users\Administrator\Desktop\gopaas\svcApi

PS D:\Workspace\Go\bin> D:\Workspace\Go\bin\protoc.exe --proto_path=C:\Users\Administrator\Desktop\gopaas\svcapi --micro_out=C:\Users\Administrator\Desktop\gopaas\svcapi --go_out=:C:\Users\Administrator\Desktop\gopaas\svcapi proto/svcApi/svcApi.proto

C:\Users\Administrator\Desktop\gopaas\svcApi\handler\svcApiHandler.go

package handler

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

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

type SvcApi struct {
	SvcService svc.SvcService
}

// svcApi.FindSvcById 通过API向外暴露为/svcApi/findSvcById,接收http请求
// 即:/svcApi/FindSvcById 请求会调用go.micro.api.svcApi 服务的svcApi.FindSvcById 方法
func (e *SvcApi) FindSvcById(ctx context.Context, req *svcApi.Request, rsp *svcApi.Response) error {
	log.Info("Received svcApi.FindSvcById request")
	if _, ok := req.Get["svc_id"]; !ok {
		rsp.StatusCode = 500
		return errors.New("参数异常")
	}
	//获取 svcId 参数
	svcIdString := req.Get["svc_id"].Values[0]
	svcId, err := strconv.ParseInt(svcIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//获取svc相关信息
	svcInfo, err := e.SvcService.FindSvcByID(ctx, &svc.SvcId{
		Id: svcId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	//json 返回svc信息

	rsp.StatusCode = 200
	b, _ := json.Marshal(svcInfo)
	rsp.Body = string(b)
	return nil
}

// svcApi.AddSvc 通过API向外暴露为/svcApi/AddSvc,接收http请求
// 即:/svcApi/AddSvc 请求会调用go.micro.api.svcApi 服务的svcApi.AddSvc 方法
func (e *SvcApi) AddSvc(ctx context.Context, req *svcApi.Request, rsp *svcApi.Response) error {
	log.Info("Received svcApi.AddSvc request")
	//处理port
	addSvcInfo := &svc.SvcInfo{}
	svcType, ok := req.Post["svc_type"]
	if ok && len(svcType.Values) > 0 {
		svcPort := &svc.SvcPort{}
		switch svcType.Values[0] {
		case "ClusterIP":
			port, err := strconv.ParseInt(req.Post["svc_port"].Values[0], 10, 64)
			if err != nil {
				common.Error(err)
				return err
			}
			svcPort.SvcPort = int32(port)
			targetPort, err := strconv.ParseInt(req.Post["svc_target_port"].Values[0], 10, 32)
			if err != nil {
				common.Error(err)
				return err
			}
			svcPort.SvcTargetPort = int32(targetPort)
			svcPort.SvcPortProtocol = req.Post["svc_port_protocol"].Values[0]
			addSvcInfo.SvcPort = append(addSvcInfo.SvcPort, svcPort)
		default:
			return errors.New("类型不支持")
		}
	}
	//form 类型转换到结构体中
	form.FormToSvcStruct(req.Post, addSvcInfo)
	response, err := e.SvcService.AddSvc(ctx, addSvcInfo)
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

// svcApi.DeleteSvcById 通过API向外暴露为/svcApi/DeleteSvcById,接收http请求
// 即:/svcApi/DeleteSvcById 请求会调用go.micro.api.svcApi 服务的 svcApi.DeleteSvcById 方法
func (e *SvcApi) DeleteSvcById(ctx context.Context, req *svcApi.Request, rsp *svcApi.Response) error {
	log.Info("Received svcApi.DeleteSvcById request")
	if _, ok := req.Get["svc_id"]; !ok {
		return errors.New("参数异常")
	}
	//获取需要删除的ID
	svcIdString := req.Get["svc_id"].Values[0]
	svcId, err := strconv.ParseInt(svcIdString, 10, 64)
	if err != nil {
		common.Error(err)
		return err
	}
	//调用后端服务删除
	response, err := e.SvcService.DeleteSvc(ctx, &svc.SvcId{
		Id: svcId,
	})
	if err != nil {
		common.Error(err)
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(response)
	rsp.Body = string(b)
	return nil
}

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

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

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

package form

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

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

//根据结构体中name标签映射数据到结构体中并且转换类型
func FormToSvcStruct(data map[string]*svcApi.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 == "svc_port" || dataTag == "svc_target_port" {
			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\svcApi\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_svc "github.com/yunixiangfeng/gopaas/svc/proto/svc"

	"net"
	"net/http"
	"strconv"

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

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

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

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.svcApi", 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.svcApi"),
		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" 请自行替换
	svcService := go_micro_service_svc.NewSvcService("go.micro.service.svc", service.Client())
	// 注册控制器
	if err := svcApi.RegisterSvcApiHandler(service.Server(), &handler.SvcApi{SvcService: svcService}); err != nil {
		common.Error(err)
	}

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

[root@k8s-worker01 gopaas]# ./api-gateway --registry=consul --registry_address=192.168.204.130:8500 api --handler=api

6-7 服务管理前端效果展示及删除接口完善

C:\Users\Administrator\Desktop\gopaas\go-paas-front\svc-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>
                </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 class="active"><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><a href="form-elements.html">Elements</a>
                </li>
                <li><a href="form-validation.html">Validation</a>
                </li>
                <li><a href="form-multiselect.html">Multiselect</a>
                </li>
                <li><a href="form-wizard.html">Wizard</a>
                </li>
                <li><a href="form-masks.html">Input Masks</a>
                </li>
                <li><a href="form-wysiwyg.html">WYSIWYG Editor</a>
                </li>
                <li><a href="form-upload.html">Multi Upload</a>
                </li>
                <li><a href="form-editable.html">X-editable</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/svcApi/addSvc" 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="svc_name"  name="svc_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="svc_namespace"  name="svc_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="svc_type"  name="svc_type" value="ClusterIP">
                      </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="svc_port">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">关联的 POD:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id=""  name="svc_pod_name" id="svc_pod_name">
                      </div>
                    </div>

                    <div class="form-group">
                      <label class="col-sm-3 control-label">目标POD端口:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control"  name="svc_target_port" id="svc_target_port">
                      </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="svc_port_protocol"  name="svc_port_protocol">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">团队ID:</label>
                      <div class="col-sm-6">
                        <input type="text" required=""   class="form-control" id="svc_team_id"  name="svc_team_id">
                      </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\svc-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 class="active"><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><a href="form-elements.html">Elements</a>
                      </li>
                      <li><a href="form-validation.html">Validation</a>
                      </li>
                      <li><a href="form-multiselect.html">Multiselect</a>
                      </li>
                      <li><a href="form-wizard.html">Wizard</a>
                      </li>
                      <li><a href="form-masks.html">Input Masks</a>
                      </li>
                      <li><a href="form-wysiwyg.html">WYSIWYG Editor</a>
                      </li>
                      <li><a href="form-upload.html">Multi Upload</a>
                      </li>
                      <li><a href="form-editable.html">X-editable</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="svc_id" name="pod_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="svc_name"  name="pod_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="svc_namespace"  name="pod_namespace">
                      </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="svc_type"  name="svc_type">
                      </div>
                    </div>
                    <div class="form-group">
                      <label class="col-sm-3 control-label">关联的POD:</label>
                      <div class="col-sm-6">
                        <input type="text" required="" readonly  class="form-control" id="svc_pod_name"  name="svc_pod_name">
                      </div>
                    </div>
                    <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="svc_team_id"  name="svc_team_id">
                      </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/svcApi/findSvcById?svc_id="+getUrlParam('svc_id'),
              success: function(data){
                console.log(data);
                if(data.id != "" || data.id != null || data.id != undefined){
                  $('#svc_id').val(data.id);
                  $("#svc_name").val(data.svc_name);
                  $('#svc_namespace').val(data.svc_namespace);
                  $('#svc_type').val(data.svc_type);
                  $('#svc_pod_name').val(data.svc_pod_name);
                  $('#svc_team_id').val(data.svc_team_id);
                }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\svc-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 class="active"><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 ><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="svc-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>绑定POD</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/svcApi/",
          success: function(data){
            console.log(data);
            $("#table-data").html("");
            $.each(data['svc_info'],function (i,item) {
              $("#table-data").append('                      <tr class="gradeA">\
                        <td>'+item.id+'</td>\
                        <td>'+item.svc_name+'</td>\
                        <td>'+item.svc_namespace+'</td>\
                        <td class="center">'+item.svc_type+'</td>\
                        <td class="center">'+item.svc_pod_name+'</td>\
                        <td class="center"><a href="svc-detail.html?svc_id='+item.id+'">详情</a> <a href="http://localhost:8080/svcApi/deleteSvcById?svc_id='+item.id+'" style="color:red;" >删除</a></td>\
                      </tr>'
              );
            })
          },
          error: function(result){
            console.log(result);
          }
        });
      });
    

 
    </script>

  </body>
</html>

C:\Users\Administrator\Desktop\gopaas\svc\domain\repository\svc_repository.go 

//根据ID删除Svc信息
func (u *SvcRepository) DeleteSvcByID(svcID 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
	}
	//删除svc
	if err := u.mysqlDb.Where("id = ?", svcID).Delete(&model.Svc{}).Error; err != nil {
		tx.Rollback()
		common.Error(err)
		return err
	}
	//删除相关的port
	if err := u.mysqlDb.Where("svc_id = ?", svcID).Delete(&model.SvcPort{}).Error; err != nil {
		tx.Rollback()
		common.Error(err)
		return err
	}
	return tx.Commit().Error
}

测试删除svc

6-8 总结&思考

主要内容
介绍了service的四中类型
介绍了Service与Deployment的关系
说明Service,Endpoint,pod之间的关系

经验之谈
Service可以创建多个指到同一个Pod来满足不同需求
程序调用的地址通常写Service地址而不是具体pod

Service的其它类型还能在哪些场景?
Service与ReplicationController有无关系?

6-9 【扩展阅读】深度剖析 K8S DNS 的 Service 与 Pod

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐