【GO】 K8s 管理系统项目[API部分--Deployment]

news2024/11/23 8:18:43

K8s 管理系统项目[API部分–Deployment]

1. 实现接口

service/dataselector.go

package service

import (
	"sort"
	"strings"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
)

// dataselector 用于排序,过滤,分页的数据类型
type dataSelector struct {
	GenericDataList []DataCell
	DataSelect      *DataSelectQuery
}

// DataCell 接口,用于各种资源List的类型转换,转换后可以使用dataselector的排序,过滤,分页方法
type DataCell interface {
	GetCreation() time.Time
	GetName() string
}

// DataSelectQuery 定义过滤和分页的结构体,过滤:Name 分页:Limit和Page
type DataSelectQuery struct {
	Filter   *FilterQuery
	Paginate *PaginateQuery
}

// FilterQuery 用于查询 过滤:Name
type FilterQuery struct {
	Name string
}

// 分页:Limit和Page Limit是单页的数据条数,Page是第几页
type PaginateQuery struct {
	Page  int
	Limit int
}

// 实现自定义的排序方法,需要重写Len,Swap,Less方法
// Len用于获取数组的长度
func (d *dataSelector) Len() int {
	return len(d.GenericDataList)
}

// Swap用于数据比较大小后的位置变更
func (d *dataSelector) Swap(i, j int) {
	d.GenericDataList[i], d.GenericDataList[j] = d.GenericDataList[j], d.GenericDataList[i]
}

// Less用于比较大小
func (d *dataSelector) Less(i, j int) bool {
	return d.GenericDataList[i].GetCreation().Before(d.GenericDataList[j].GetCreation())
}

// 重写以上三个方法,用sort.Sort 方法触发排序
func (d *dataSelector) Sort() *dataSelector {
	sort.Sort(d)
	return d
}

// Filter方法用于过滤,比较数据Name属性,若包含则返回
func (d *dataSelector) Filter() *dataSelector {
	if d.DataSelect.Filter.Name == "" {
		return d
	}
	filtered := []DataCell{}
	for _, value := range d.GenericDataList {
		// 定义是否匹配的标签变量,默认是匹配的
		matches := true
		objName := value.GetName()
		if !strings.Contains(objName, d.DataSelect.Filter.Name) {
			matches = false
			continue
		}
		if matches {
			filtered = append(filtered, value)
		}
	}
	d.GenericDataList = filtered
	return d
}

// Paginate 分页,根据Limit和Page的传参,取一定范围内的数据返回
func (d *dataSelector) Paginate() *dataSelector {
	limit := d.DataSelect.Paginate.Limit
	page := d.DataSelect.Paginate.Page
	//验证参数合法,若参数不合法,则返回所有数据
	if limit <= 0 || page <= 0 {
		return d
	}
	//举例:25个元素的数组,limit是10,page是3,startIndex是20,endIndex是30(实际上endIndex是25)
	startIndex := limit * (page - 1)
	endIndex := limit * page

	//处理最后一页,这时候就把endIndex由30改为25了
	if len(d.GenericDataList) < endIndex {
		endIndex = len(d.GenericDataList)
	}
	d.GenericDataList = d.GenericDataList[startIndex:endIndex]
	return d
}

// 定义podCell, 重写GetCreation和GetName 方法后,可以进行数据转换
// covev1.Pod --> podCell  --> DataCell
// appsv1.Deployment --> deployCell --> DataCell
type podCell corev1.Pod

// 重写DataCell接口的两个方法
func (p podCell) GetCreation() time.Time {
	return p.CreationTimestamp.Time
}
func (p podCell) GetName() string {
	return p.Name
}
// deployCell
type deploymentCell appsv1.Deployment

func (d deploymentCell) GetCreation() time.Time {
	return d.CreationTimestamp.Time
}
func (d deploymentCell) GetName() string {
	return d.Name
}

2. Deployment功能

service/deployment.go

  1. 列表
  2. 获取Deployment详情
  3. 修改Deployment副本数
  4. 创建Deployment
  5. 删除Deployment
  6. 重启Deployment
  7. 更新Deployment
  8. 获取每个namespace的Deployment数量

2.1 from和to方法

func (d *deployment) toCells(std []appsv1.Deployment) []DataCell {
	cells := make([]DataCell, len(std))
	for i := range std {
		cells[i] = deploymentCell(std[i])
	}
	return cells
}

func (d *deployment) fromCells(cells []DataCell) []appsv1.Deployment {
	deployments := make([]appsv1.Deployment, len(cells))
	for i := range cells {
		deployments[i] = appsv1.Deployment(cells[i].(deploymentCell))
	}

	return deployments
}

2.2 获取deployment副本数

// 设置deployment副本数
func (d *deployment) ScaleDeployment(deploymentName, namespace string, scaleNum int) (replica int32, err error) {
	//获取autoscalingv1.Scale类型的对象,能点出当前的副本数
	scale, err := K8s.ClientSet.AppsV1().Deployments(namespace).GetScale(context.TODO(), deploymentName, metav1.GetOptions{})
	if err != nil {
		logger.Error(errors.New("获取Deployment副本数信息失败, " + err.Error()))
		return 0, errors.New("获取Deployment副本数信息失败, " + err.Error())
	}
	//修改副本数
	scale.Spec.Replicas = int32(scaleNum)
	//更新副本数,传入scale对象
	newScale, err := K8s.ClientSet.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deploymentName, scale, metav1.UpdateOptions{})
	if err != nil {
		logger.Error(errors.New("更新Deployment副本数信息失败, " + err.Error()))
		return 0, errors.New("更新Deployment副本数信息失败, " + err.Error())
	}

	return newScale.Spec.Replicas, nil
}

2.3 创建deployment

// 创建deployment,接收DeployCreate对象
func (d *deployment) CreateDeployment(data *DeployCreate) (err error) {
	//将data中的属性组装成appsv1.Deployment对象,并将入参数据放入
	deployment := &appsv1.Deployment{
		//ObjectMeta中定义资源名、命名空间以及标签
		ObjectMeta: metav1.ObjectMeta{
			Name:      data.Name,
			Namespace: data.Namespace,
			Labels:    data.Label,
		},
		//Spec中定义副本数、选择器、以及pod属性
		Spec: appsv1.DeploymentSpec{
			Replicas: &data.Replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: data.Label,
			},
			Template: corev1.PodTemplateSpec{
				//定义pod名和标签
				ObjectMeta: metav1.ObjectMeta{
					Name:   data.Name,
					Labels: data.Label,
				},
				//定义容器名、镜像和端口
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  data.Name,
							Image: data.Image,
							Ports: []corev1.ContainerPort{
								{
									Name:          "http",
									Protocol:      corev1.ProtocolTCP,
									ContainerPort: data.ContainerPort,
								},
							},
						},
					},
				},
			},
		},
		//Status定义资源的运行状态,这里由于是新建,传入空的appsv1.DeploymentStatus{}对象即可
		Status: appsv1.DeploymentStatus{},
	}
	//判断是否打开健康检查功能,若打开,则定义ReadinessProbe和LivenessProbe
	if data.HealthCheck {
		//设置第一个容器的ReadinessProbe,因为我们pod中只有一个容器,所以直接使用index 0即可
		//若pod中有多个容器,则这里需要使用for循环去定义了
		deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
			ProbeHandler: corev1.ProbeHandler{
				HTTPGet: &corev1.HTTPGetAction{
					Path: data.HealthPath,
					//intstr.IntOrString的作用是端口可以定义为整型,也可以定义为字符串
					//Type=0则表示表示该结构体实例内的数据为整型,转json时只使用IntVal的数据
					//Type=1则表示表示该结构体实例内的数据为字符串,转json时只使用StrVal的数据
					Port: intstr.IntOrString{
						Type:   0,
						IntVal: data.ContainerPort,
					},
				},
			},
			//初始化等待时间
			InitialDelaySeconds: 5,
			//超时时间
			TimeoutSeconds: 5,
			//执行间隔
			PeriodSeconds: 5,
		}
		deployment.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
			ProbeHandler: corev1.ProbeHandler{
				HTTPGet: &corev1.HTTPGetAction{
					Path: data.HealthPath,
					Port: intstr.IntOrString{
						Type:   0,
						IntVal: data.ContainerPort,
					},
				},
			},
			InitialDelaySeconds: 15,
			TimeoutSeconds:      5,
			PeriodSeconds:       5,
		}
		//定义容器的limit和request资源
		deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{
			corev1.ResourceCPU:    resource.MustParse(data.Cpu),
			corev1.ResourceMemory: resource.MustParse(data.Memory),
		}
		deployment.Spec.Template.Spec.Containers[0].Resources.Requests = map[corev1.ResourceName]resource.Quantity{
			corev1.ResourceCPU:    resource.MustParse(data.Cpu),
			corev1.ResourceMemory: resource.MustParse(data.Memory),
		}
	}
	//调用sdk创建deployment
	_, err = K8s.ClientSet.AppsV1().Deployments(data.Namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
	if err != nil {
		logger.Error(errors.New("创建Deployment失败, " + err.Error()))
		return errors.New("创建Deployment失败, " + err.Error())
	}
	return nil
}

2.4 删除Deployment

// 删除deployment
func (d *deployment) DeleteDeployment(deploymentName, namespace string) (err error) {
	err = K8s.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
	if err != nil {
		logger.Error(errors.New("删除Deployment失败, " + err.Error()))
		return errors.New("删除Deployment失败, " + err.Error())
	}

	return nil
}

2.5 重启deployment

// 重启deployment
func (d *deployment) RestartDeployment(deploymentName, namespace string) (err error) {
	//此功能等同于一下kubectl命令
	//kubectl deployment ${service} -p \
	//'{"spec":{"template":{"spec":{"containers":[{"name":"'"${service}"'","env":[{"name":"RESTART_","value":"'$(date +%s)'"}]}]}}}}'

	//使用patchData Map组装数据
	patchData := map[string]interface{}{
		"spec": map[string]interface{}{
			"template": map[string]interface{}{
				"spec": map[string]interface{}{
					"containers": []map[string]interface{}{
						{"name": deploymentName,
							"env": []map[string]string{{
								"name":  "RESTART_",
								"value": strconv.FormatInt(time.Now().Unix(), 10),
							}},
						},
					},
				},
			},
		},
	}
	//序列化为字节,因为patch方法只接收字节类型参数
	patchByte, err := json.Marshal(patchData)
	if err != nil {
		logger.Error(errors.New("json序列化失败, " + err.Error()))
		return errors.New("json序列化失败, " + err.Error())
	}
	//调用patch方法更新deployment
	_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Patch(context.TODO(), deploymentName, "application/strategic-merge-patch+json", patchByte, metav1.PatchOptions{})
	if err != nil {
		logger.Error(errors.New("重启Deployment失败, " + err.Error()))
		return errors.New("重启Deployment失败, " + err.Error())
	}

	return nil
}

2.6 更新deployment

// 更新deployment
func (d *deployment) UpdateDeployment(namespace, content string) (err error) {
	var deploy = &appsv1.Deployment{}

	err = json.Unmarshal([]byte(content), deploy)
	if err != nil {
		logger.Error(errors.New("反序列化失败, " + err.Error()))
		return errors.New("反序列化失败, " + err.Error())
	}

	_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{})
	if err != nil {
		logger.Error(errors.New("更新Deployment失败, " + err.Error()))
		return errors.New("更新Deployment失败, " + err.Error())
	}
	return nil
}

2.7 获取所有Namespace中Deployment数量

// 获取每个namespace的deployment数量
func (d *deployment) GetDeployNumPerNp() (deploysNps []*DeploysNp, err error) {
	namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	for _, namespace := range namespaceList.Items {
		deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace.Name).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return nil, err
		}

		deploysNp := &DeploysNp{
			Namespace: namespace.Name,
			DeployNum: len(deploymentList.Items),
		}

		deploysNps = append(deploysNps, deploysNp)
	}
	return deploysNps, nil
}

2.8 整个deployment

service/deployment.go

package service

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"
	"time"
	"github.com/wonderivan/logger"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

var Deployment deployment

type deployment struct{}

// 定义列表的返回内容,Items是deployment元素列表,Total为deployment元素数量
type DeploymentsResp struct {
	Items []appsv1.Deployment `json:"items"`
	Total int                 `json:"total"`
}
// 定义DeployCreate结构体,用于创建deployment需要的参数属性的定义
type DeployCreate struct {
	Name          string            `json:"name"`
	Namespace     string            `json:"namespace"`
	Replicas      int32             `json:"replicas"`
	Image         string            `json:"image"`
	Label         map[string]string `json:"label"`
	Cpu           string            `json:"cpu"`
	Memory        string            `json:"memory"`
	ContainerPort int32             `json:"container_port"`
	HealthCheck   bool              `json:"health_check"`
	HealthPath    string            `json:"health_path"`
}
// 定义DeploysNp类型,用于返回namespace中deployment的数量
type DeploysNp struct {
	Namespace string `json:"namespace"`
	DeployNum int    `json:"deployment_num"`
}
// 获取deployment列表,支持过滤、排序、分页
func (d *deployment) GetDeployments(filterName, namespace string, limit, page int) (deploymentsResp *DeploymentsResp, err error) {
	//获取deploymentList类型的deployment列表
	deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		logger.Error(errors.New("获取Deployment列表失败, " + err.Error()))
		return nil, errors.New("获取Deployment列表失败, " + err.Error())
	}
	//将deploymentList中的deployment列表(Items),放进dataselector对象中,进行排序
	selectableData := &dataSelector{
		GenericDataList: d.toCells(deploymentList.Items),
		DataSelect: &DataSelectQuery{
			Filter: &FilterQuery{Name: filterName},
			Paginate: &PaginateQuery{
				Limit: limit,
				Page:  page,
			},
		},
	}
	filtered := selectableData.Filter()
	total := len(filtered.GenericDataList)
	data := filtered.Sort().Paginate()
	//将[]DataCell类型的deployment列表转为appsv1.deployment列表
	deployments := d.fromCells(data.GenericDataList)
	return &DeploymentsResp{
		Items: deployments,
		Total: total,
	}, nil
}
// 获取deployment详情
func (d *deployment) GetDeploymentDetail(deploymentName, namespace string) (deployment *appsv1.Deployment, err error) {
	deployment, err = K8s.ClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{})
	if err != nil {
		logger.Error(errors.New("获取Deployment详情失败, " + err.Error()))
		return nil, errors.New("获取Deployment详情失败, " + err.Error())
	}
	return deployment, nil
}
// 设置deployment副本数
func (d *deployment) ScaleDeployment(deploymentName, namespace string, scaleNum int) (replica int32, err error) {
	//获取autoscalingv1.Scale类型的对象,能点出当前的副本数
	scale, err := K8s.ClientSet.AppsV1().Deployments(namespace).GetScale(context.TODO(), deploymentName, metav1.GetOptions{})
	if err != nil {
		logger.Error(errors.New("获取Deployment副本数信息失败, " + err.Error()))
		return 0, errors.New("获取Deployment副本数信息失败, " + err.Error())
	}
	//修改副本数
	scale.Spec.Replicas = int32(scaleNum)
	//更新副本数,传入scale对象
	newScale, err := K8s.ClientSet.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deploymentName, scale, metav1.UpdateOptions{})
	if err != nil {
		logger.Error(errors.New("更新Deployment副本数信息失败, " + err.Error()))
		return 0, errors.New("更新Deployment副本数信息失败, " + err.Error())
	}
	return newScale.Spec.Replicas, nil
}
// 创建deployment,接收DeployCreate对象
func (d *deployment) CreateDeployment(data *DeployCreate) (err error) {
	//将data中的属性组装成appsv1.Deployment对象,并将入参数据放入
	deployment := &appsv1.Deployment{
		//ObjectMeta中定义资源名、命名空间以及标签
		ObjectMeta: metav1.ObjectMeta{
			Name:      data.Name,
			Namespace: data.Namespace,
			Labels:    data.Label,
		},
		//Spec中定义副本数、选择器、以及pod属性
		Spec: appsv1.DeploymentSpec{
			Replicas: &data.Replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: data.Label,
			},
			Template: corev1.PodTemplateSpec{
				//定义pod名和标签
				ObjectMeta: metav1.ObjectMeta{
					Name:   data.Name,
					Labels: data.Label,
				},
				//定义容器名、镜像和端口
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  data.Name,
							Image: data.Image,
							Ports: []corev1.ContainerPort{
								{
									Name:          "http",
									Protocol:      corev1.ProtocolTCP,
									ContainerPort: data.ContainerPort,
								},
							},
						},
					},
				},
			},
		},
		//Status定义资源的运行状态,这里由于是新建,传入空的appsv1.DeploymentStatus{}对象即可
		Status: appsv1.DeploymentStatus{},
	}
	//判断是否打开健康检查功能,若打开,则定义ReadinessProbe和LivenessProbe
	if data.HealthCheck {
		//设置第一个容器的ReadinessProbe,因为我们pod中只有一个容器,所以直接使用index 0即可
		//若pod中有多个容器,则这里需要使用for循环去定义了
		deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
			ProbeHandler: corev1.ProbeHandler{
				HTTPGet: &corev1.HTTPGetAction{
					Path: data.HealthPath,
					//intstr.IntOrString的作用是端口可以定义为整型,也可以定义为字符串
					//Type=0则表示表示该结构体实例内的数据为整型,转json时只使用IntVal的数据
					//Type=1则表示表示该结构体实例内的数据为字符串,转json时只使用StrVal的数据
					Port: intstr.IntOrString{
						Type:   0,
						IntVal: data.ContainerPort,
					},
				},
			},
			//初始化等待时间
			InitialDelaySeconds: 5,
			//超时时间
			TimeoutSeconds: 5,
			//执行间隔
			PeriodSeconds: 5,
		}
		deployment.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
			ProbeHandler: corev1.ProbeHandler{
				HTTPGet: &corev1.HTTPGetAction{
					Path: data.HealthPath,
					Port: intstr.IntOrString{
						Type:   0,
						IntVal: data.ContainerPort,
					},
				},
			},
			InitialDelaySeconds: 15,
			TimeoutSeconds:      5,
			PeriodSeconds:       5,
		}
		//定义容器的limit和request资源
		deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{
			corev1.ResourceCPU:    resource.MustParse(data.Cpu),
			corev1.ResourceMemory: resource.MustParse(data.Memory),
		}
		deployment.Spec.Template.Spec.Containers[0].Resources.Requests = map[corev1.ResourceName]resource.Quantity{
			corev1.ResourceCPU:    resource.MustParse(data.Cpu),
			corev1.ResourceMemory: resource.MustParse(data.Memory),
		}
	}
	//调用sdk创建deployment
	_, err = K8s.ClientSet.AppsV1().Deployments(data.Namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
	if err != nil {
		logger.Error(errors.New("创建Deployment失败, " + err.Error()))
		return errors.New("创建Deployment失败, " + err.Error())
	}
	return nil
}

// 删除deployment
func (d *deployment) DeleteDeployment(deploymentName, namespace string) (err error) {
	err = K8s.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
	if err != nil {
		logger.Error(errors.New("删除Deployment失败, " + err.Error()))
		return errors.New("删除Deployment失败, " + err.Error())
	}
	return nil
}

// 重启deployment
func (d *deployment) RestartDeployment(deploymentName, namespace string) (err error) {
	//此功能等同于一下kubectl命令
	//kubectl deployment ${service} -p \
	//'{"spec":{"template":{"spec":{"containers":[{"name":"'"${service}"'","env":[{"name":"RESTART_","value":"'$(date +%s)'"}]}]}}}}'

	//使用patchData Map组装数据
	patchData := map[string]interface{}{
		"spec": map[string]interface{}{
			"template": map[string]interface{}{
				"spec": map[string]interface{}{
					"containers": []map[string]interface{}{
						{"name": deploymentName,
							"env": []map[string]string{{
								"name":  "RESTART_",
								"value": strconv.FormatInt(time.Now().Unix(), 10),
							}},
						},
					},
				},
			},
		},
	}
	//序列化为字节,因为patch方法只接收字节类型参数
	patchByte, err := json.Marshal(patchData)
	if err != nil {
		logger.Error(errors.New("json序列化失败, " + err.Error()))
		return errors.New("json序列化失败, " + err.Error())
	}
	//调用patch方法更新deployment
	_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Patch(context.TODO(), deploymentName, "application/strategic-merge-patch+json", patchByte, metav1.PatchOptions{})
	if err != nil {
		logger.Error(errors.New("重启Deployment失败, " + err.Error()))
		return errors.New("重启Deployment失败, " + err.Error())
	}
	return nil
}

// 更新deployment
func (d *deployment) UpdateDeployment(namespace, content string) (err error) {
	var deploy = &appsv1.Deployment{}

	err = json.Unmarshal([]byte(content), deploy)
	if err != nil {
		logger.Error(errors.New("反序列化失败, " + err.Error()))
		return errors.New("反序列化失败, " + err.Error())
	}
	_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{})
	if err != nil {
		logger.Error(errors.New("更新Deployment失败, " + err.Error()))
		return errors.New("更新Deployment失败, " + err.Error())
	}
	return nil
}

// 获取每个namespace的deployment数量
func (d *deployment) GetDeployNumPerNp() (deploysNps []*DeploysNp, err error) {
	namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	for _, namespace := range namespaceList.Items {
		deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespace.Name).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return nil, err
		}

		deploysNp := &DeploysNp{
			Namespace: namespace.Name,
			DeployNum: len(deploymentList.Items),
		}
		deploysNps = append(deploysNps, deploysNp)
	}
	return deploysNps, nil
}

func (d *deployment) toCells(std []appsv1.Deployment) []DataCell {
	cells := make([]DataCell, len(std))
	for i := range std {
		cells[i] = deploymentCell(std[i])
	}
	return cells
}

func (d *deployment) fromCells(cells []DataCell) []appsv1.Deployment {
	deployments := make([]appsv1.Deployment, len(cells))
	for i := range cells {
		deployments[i] = appsv1.Deployment(cells[i].(deploymentCell))
	}
	return deployments
}

3. 获取Deployment列表

controller/deployment.go

package controller

import (
	"fmt"
	"k8s-plantform/service"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/wonderivan/logger"
)

var Deployment deployment

type deployment struct{}

// 获取deployment列表,支持过滤、排序、分页
func (d *deployment) GetDeployments(ctx *gin.Context) {
	params := new(struct {
		FilterName string `form:"filter_name"`
		Namespace  string `form:"namespace"`
		Page       int    `form:"page"`
		Limit      int    `form:"limit"`
	})
	if err := ctx.Bind(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	data, err := service.Deployment.GetDeployments(params.FilterName, params.Namespace, params.Limit, params.Page)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "获取Deployment列表成功",
		"data": data,
	})
}

// 获取deployment详情
func (d *deployment) GetDeploymentDetail(ctx *gin.Context) {
	params := new(struct {
		DeploymentName string `form:"deployment_name"`
		Namespace      string `form:"namespace"`
	})
	if err := ctx.Bind(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	data, err := service.Deployment.GetDeploymentDetail(params.DeploymentName, params.Namespace)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "获取Deployment详情成功",
		"data": data,
	})
}

// 创建deployment
func (d *deployment) CreateDeployment(ctx *gin.Context) {
	var (
		deployCreate = new(service.DeployCreate)
		err          error
	)

	if err = ctx.ShouldBindJSON(deployCreate); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	if err = service.Deployment.CreateDeployment(deployCreate); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "创建Deployment成功",
		"data": nil,
	})
}

// 设置deployment副本数
func (d *deployment) ScaleDeployment(ctx *gin.Context) {
	params := new(struct {
		DeploymentName string `json:"deployment_name"`
		Namespace      string `json:"namespace"`
		ScaleNum       int    `json:"scale_num"`
	})
	//PUT请求,绑定参数方法改为ctx.ShouldBindJSON
	if err := ctx.ShouldBindJSON(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	data, err := service.Deployment.ScaleDeployment(params.DeploymentName, params.Namespace, params.ScaleNum)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "设置Deployment副本数成功",
		"data": fmt.Sprintf("最新副本数: %d", data),
	})
}

// 删除deployment
func (d *deployment) DeleteDeployment(ctx *gin.Context) {
	params := new(struct {
		DeploymentName string `json:"deployment_name"`
		Namespace      string `json:"namespace"`
	})
	//DELETE请求,绑定参数方法改为ctx.ShouldBindJSON
	if err := ctx.ShouldBindJSON(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	err := service.Deployment.DeleteDeployment(params.DeploymentName, params.Namespace)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "删除Deployment成功",
		"data": nil,
	})
}

// 重启deployment
func (d *deployment) RestartDeployment(ctx *gin.Context) {
	params := new(struct {
		DeploymentName string `json:"deployment_name"`
		Namespace      string `json:"namespace"`
	})
	//PUT请求,绑定参数方法改为ctx.ShouldBindJSON
	if err := ctx.ShouldBindJSON(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	err := service.Deployment.RestartDeployment(params.DeploymentName, params.Namespace)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "重启Deployment成功",
		"data": nil,
	})
}

// 更新deployment
func (d *deployment) UpdateDeployment(ctx *gin.Context) {
	params := new(struct {
		Namespace string `json:"namespace"`
		Content   string `json:"content"`
	})
	//PUT请求,绑定参数方法改为ctx.ShouldBindJSON
	if err := ctx.ShouldBindJSON(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	err := service.Deployment.UpdateDeployment(params.Namespace, params.Content)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "更新Deployment成功",
		"data": nil,
	})
}

// 获取每个namespace的pod数量
func (d *deployment) GetDeployNumPerNp(ctx *gin.Context) {
	data, err := service.Deployment.GetDeployNumPerNp()
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "获取每个namespace的deployment数量成功",
		"data": data,
	})
}

4. 定义路由

controller/router.go

package controller

import (
	"github.com/gin-gonic/gin"
)

// 初始化router类型的对象,首字母大写,用于跨包调用
var Router router

// 声明一个router的结构体
type router struct{}

func (r *router) InitApiRouter(router *gin.Engine) {
	router.
		// Pods
		GET("/api/k8s/pods", Pod.GetPods).
		GET("/api/k8s/pods/detail", Pod.GetPodDetail).
		POST("/api/k8s/pods", Pod.DeletePod).
		DELETE("/api/k8s/pod/del", Pod.DeletePod).
		PUT("/api/k8s/pod/update", Pod.UpdatePod).
		GET("/api/k8s/pod/container", Pod.GetPodContainer).
		GET("/api/k8s/pod/log", Pod.GetPodLog).
		GET("/api/k8s/pod/numnp", Pod.GetPodNumPerNp).
	//deployment操作
		GET("/api/k8s/deployments", Deployment.GetDeployments).
		GET("/api/k8s/deployment/detail", Deployment.GetDeploymentDetail).
		PUT("/api/k8s/deployment/scale", Deployment.ScaleDeployment).
		DELETE("/api/k8s/deployment/del", Deployment.DeleteDeployment).
		PUT("/api/k8s/deployment/restart", Deployment.RestartDeployment).
		PUT("/api/k8s/deployment/update", Deployment.UpdateDeployment).
		GET("/api/k8s/deployment/numnp", Deployment.GetDeployNumPerNp).
		POST("/api/k8s/deployment/create", Deployment.CreateDeployment)
}

5. 测试Deployment方法

5.1 获取Deployment清单

/api/k8s/deployments

请添加图片描述

5.2 获取Deployment清单详情

/api/k8s/deployment/detail

请添加图片描述

5.3 修改Deployment副本数

/api/k8s/deployment/scale
root@master-01:~/exam-CKA# kubectl get deployments.apps -A
NAMESPACE     NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
default       nginx-deployment                3/3     3            3           2d20h
kube-system   coredns                         0/2     2            0           2d23h
wework        wework-tomcat-app1-deployment   1/1     1            1           2d21h
wework        wework-tomcat-app2-deployment   1/1     1            1           2d21h

请添加图片描述

root@master-01:~/exam-CKA# kubectl get deployments.apps 
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   4/4     4            4           2d20h
root@master-01:~/exam-CKA# kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-66b6c48dd5-24grz   1/1     Running   0          2d20h
nginx-deployment-66b6c48dd5-cz6ql   1/1     Running   0          31s
nginx-deployment-66b6c48dd5-gttz6   1/1     Running   0          2d20h
nginx-deployment-66b6c48dd5-gxksp   1/1     Running   0          2d20h
test-pod1                           1/1     Running   1          2d23h
test-pod2                           1/1     Running   1          2d23h
test-pod3                           1/1     Running   1          2d23h
test-pod4                           1/1     Running   1          2d23h

5.4 创建Deployment

/api/k8s/deployment/create
root@master-01:~/exam-CKA# kubectl get deployments.apps 
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   4/4     4            4           2d20h

这个放到前端一起测

5.5 删除Deployment

/api/k8s/deployment/del
root@master-01:~/exam-CKA/yaml# kubectl get deployments.apps -n k8s
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
front-end   1/1     1            1           11s

请添加图片描述

front-end这个deployment就被删除了

# kubectl get deployments.apps -n k8s
No resources found in k8s namespace.

5.6 重启Deployment

/api/k8s/deployment/restart
root@master-01:~/exam-CKA/yaml# kubectl get deployments.apps,pods
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   3/3     3            3           115s

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-64dd8946f7-7c6nj   1/1     Running   0          63s
pod/nginx-64dd8946f7-8wp7f   1/1     Running   0          67s
pod/nginx-64dd8946f7-9f9bn   1/1     Running   0          65s
pod/test-pod1                1/1     Running   1          3d
pod/test-pod2                1/1     Running   1          3d
pod/test-pod3                1/1     Running   1          3d
pod/test-pod4                1/1     Running   1          3d

请添加图片描述

这里可以看到pod已经被重启过了

root@master-01:~/exam-CKA/yaml# kubectl get deployments.apps,pods
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   3/3     3            3           3m15s

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-64f8cb6df4-5mtjg   1/1     Running   0          17s
pod/nginx-64f8cb6df4-jkgtq   1/1     Running   0          22s
pod/nginx-64f8cb6df4-qj9dj   1/1     Running   0          19s
pod/test-pod1                1/1     Running   1          3d
pod/test-pod2                1/1     Running   1          3d
pod/test-pod3                1/1     Running   1          3d
pod/test-pod4                1/1     Running   1          3d

5.7 更新Deployment

/api/k8s/deployment/update

这个放到前端一起测.其实之前的restart已经实现了部分功能

5.8 获取每个namespace的Deployment数量

/api/k8s/deployment/numnp

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第11讲:vue脚手架集成ElementUI

一、创建vue路由项目并添加ElementUI支持 ElementUI官方网站&#xff1a;ElementUI组件 创建路由项目请参考&#xff1a;路由开发 使用如下命令集成ElementUI npm i element-ui -S在src/main.js文件中引用ElementUI import Vue from vue import App from ./App.vue import …

创建第一个QT程序demo

双击Qt Creator 9.0.0 (Community)图标&#xff0c;打开软件。该图标所处位置为&#xff08;如果是默认安装到C盘下面的话&#xff09;&#xff1a;C:\Qt\Tools\QtCreator\bin\qtcreator.exe 或者从开始菜单栏启动&#xff08;开始菜单栏的启动图标本质上也是一个快捷方式&…

记录windows上的VSCODE 远程到linux编译代码机器上的一些问题

设置windows SSH 到linux时免密码登录的方法&#xff1a; 将C:\Users\Administrator.ssh\id_rsa.pub中的公钥字符串复制&#xff0c;追加到linux ~/.ssh/authorized_keys文件中。 问题&#xff1a; rootlocalhost:~/.vscode-server/bin/6261075646f055b99068d3688932416f2346d…

[附源码]Node.js计算机毕业设计服装创意定制管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

【Redis技术探索】「底层架构原理」探索分析服务数据同步持久化机制

&#x1f4da;背景介绍 ✒️ Redis数据恢复的介绍 通常情况下redis的数据全部存储在内存中&#xff0c;数据库一旦故障发生重启数据会全部丢失&#xff0c;持久化功能在于能够有效地避免因进程退出造成的数据丢失问题&#xff0c;在下次重启时利用之前持久化的文件即可实现数据…

【学习打卡】ZFNet深度学习图像分类算法

文章目录引言可以学到什么为什么叫ZFNetZFNet的网络结构简介方法&#xff1a;可视化反卷积反池化反激活反卷积训练细节大小裁剪层可视化特征可视化第 1 层第 2 层两边的对应关系更深的层第 3 层第 4 层第 5 层特征演化特征不变性实验简介图的分析模型改进&#xff1a;AlexNet局…

【愚公系列】2022年12月 Elasticsearch数据库-ELK添加中文分词器插件(三)

文章目录前言1.IK分词器2.pingying分词器一、ELK添加中文分词器插件1.IK分词器测试1.1 文件准备1.2 测试2.pingying分词器测试2.1 文件准备2.2 测试2.2.1 单个测试2.2.2 多个测试2.2.3 短语查询测试2.2.3.1 medcl2索引2.2.3.2 medcl3索引前言 分词器的作用是把一段文本中的词按…

Python学习基础笔记五十一——学校管理系统

完成一个作业&#xff1a; 1. 创建北京、上海 2 所学校 2. 创建linux , python , go 3个课程 &#xff0c; linux\py 在北京开&#xff0c; go 在上海开 3. 课程包含&#xff0c;周期&#xff0c;价格 4. 班级关联课程、讲师 5. 创建学员时&#xff0c;选择学校&#xff0c;关联…

从零开始搭建CentOS7虚拟机系统、MySQL5.7和Redis3服务

CentOS7搭建MySQL和Redis服务 为什么不直接搭建高版本的呢&#xff1f; 因为有些公司的环境就是低版本的&#xff0c;这些低版本的环境也需要学会如何搭建。 准备工作 搭建一个CentOS7系统 第一步&#xff1a;先下载一个CentOS7的iso文件 点击直接下载&#xff1a;https:…

Odoo丨如何改造Odoo原生form表单使其更好看

文章目录前言一、原生表单实现方式二、问题发现与分析1.项目中遇到问题2.问题具体分析三、具体解决方法第一步&#xff1a;把Span变成输入框第二步&#xff1a;改写_renderFieldWidget前言 Odoo作为快速搭建系统的框架&#xff0c;我们在利用它便捷高效功能的同时&#xff0c;…

踩坑记录:C++调用matlab生成的动态链接库

任务类别&#xff1a; 通常出现在项目中&#xff0c;使用 Matlab 设计算法&#xff0c;最后应用于 Qt 的应用程序中。 配置Vs2008环境&#xff1a;(PS:这里应该也同样能应用于其它版本) 一. 设置matlab库目录 选择“可执行文件”下拉框&#xff0c;添加&#xff1a;" ##…

【Redis】Docker 安装 Redis

Docker 安装 Redis 1、安装镜像 docker pull redis docker images docker run -d -p 6379:6379 redis docker ps docker exec -it 容器ID bash 2、验证Redis容器安装结果 redis- clipingset k1 v1 get k1 3、使用Redis需修改配置文件redis.conf。可通过&#xff1a;方法一&…

【Redis】Redis 内存淘汰策略

文章目录概述数据淘汰策略不进行数据淘汰策略进行数据淘汰策略在设置了过期时间的数据中进行淘汰在所有数据范围内进行淘汰查看与配置数据淘汰机制查看 Redis 的数据淘汰机制修改 Redis 的数据淘汰机制方法一方法二浅谈 LRU 算法和 LFU 算法LRU 算法LFU 算法概述 当我们往 Red…

勒索病毒防御 运维安全管控 | 某烟草公司数据安全建设实践

对于烟草行业而言&#xff0c;加快数字化转型是建设现代化烟草经济体系、实现高质量发展的重要支撑。但新技术的普及与应用&#xff0c;在给烟草行业带来便利、创造价值的同时&#xff0c;也使行业面临的数据安全威胁与日俱增。 在数据安全监管合规持续升级的大背景下&#xff…

《自己动手写CPU》学习记录(9)——第7章/Part 2

目录 引言 致谢 流水线暂停 指令说明 madd、maddu、msub、msubu 设计 宏定义文件 程序计数器模块 译码模块 执行模块 访存模块 HI LO 寄存器模块 通用寄存器模块 流水线控制模块 程序ROM MIPS32顶层 MIPS32 SOPC 仿真 仿真程序 TESTBENCH 仿真结果 引言 …

从 0 到 1 搞一个 Compose Desktop 版本的玩天气之绘制

从 0 到 1 搞一个 Compose Desktop 版本的玩天气之绘制 上一篇文章 “从 0 到 1 搞一个 Compose Desktop 版本的玩天气之踩坑” 中大概说了下刚开始使用 Compose Desktop 会遇到的一些问题&#xff0c;帮大家踩了踩坑&#xff0c;那么这一篇则会带大家一起来看下项目中绘制的一…

网易开发三年,现跳槽蚂蚁花呗,4面顺利通过,拿下Java岗offer

面试准备 不论是校招还是社招都避免不了各种面试、笔试&#xff0c;如何去准备这些东西就显得格外重要。 运筹帷幄之后&#xff0c;决胜千里之外&#xff01;不打毫无准备的仗&#xff0c;我觉得大家可以先从下面几个方面来准备面试&#xff1a; 1. 自我介绍。&#xff08;介…

ubuntu22.04LTS 内核源码编译,安装,卸载

下载内核源码 到网站 https://www.kernel.org/ 下载你自己版本的内核源码。 使用如下命令查看自己的内核版本 uname -r编译前准备 安装工具 sudo apt-get install libncurses5-dev libssl-dev build-essential openssl zlibc minizip libidn11-dev libidn11 libelf-dev bc…

困扰程序员50年的问题终于解决了,但好像又没完全解决......

闰秒&#xff0c;这个唯一能够让Meta、谷歌、微软等巨头同暴躁的Linux之父Linus Torvalds达成一致的存在&#xff0c;这个让无数程序员为之头疼的存在&#xff0c;终于要取消了&#xff01; 今年第27届国际计量大会上&#xff0c;与会代表通过了一项决议——从2035年起暂停在官…

【车辆计数】光流法行驶车辆检测计数【含Matlab源码 627期】

⛄一、光流场简介 1 案例背景 运动视觉研究的内容是如何从变化场景中的一系列不同时刻的图像中提取有关场景中物体的形状、位置和运动的信息。根据研究的方法&#xff0c;它可以分为两类&#xff1a;基于特征的方法和基于光流场的方法。基于特征的方法抽取特征点&#xff0c;是…