基于Go开发PaaS平台1
建设路径,先建设pass平台,再开发go微服务,然后扩大云原生支持.容器监控系统 Promethus + Grafana容器模式下中间件主流的接入方案标准的路由管理技术 nginx ingress云原生平台展示PaaS 平台是什么?PaaS 平台与 DevOpsPaaS平台与服务网格PaaS 平台与低代码PaaS 平台是什么?PaaS(平台即服务):提供了一个用于开发、运行和管理应用程序的完整、灵
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)
}
}()
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
更多推荐
所有评论(0)