一些 bios 配置优化的备忘

本文参考了华为的文档《FusionCompute V100R005C10 软件安装指南 02》,以下是我的一些笔记,不同设备的某些选项可能不同,仅供参考。

1. Intel HT technology,开启(Enable)

使CPU支持多线程,提升CPU性能。

image-20210602173907797

2. Intel Virtualization tech,开启(Enable)

image-20210602174640441

3. Execute Disable Bit,开启(Enable)

CPU硬件防病毒技术,亦称作NX或XD功能。

“Execute Disable Bit”是Intel在新一代处理器中引入的一项功能,开启该功能后,可以防止病毒、蠕虫、木马等程序利用溢出、无限扩大等手法去破坏系统内存并取得系统的控制权。

4. Intel SpeedStep tech,关闭(Disable)

speedstep 技术是通过降低 cpu 运行主频来达到降低功耗的技术,CPU 工作模式切换技术。

新款服务器写作 EIST(智能降频技术)。

· SpeedStep (GV1.1) 最早被引入奔腾M III,也就是笔记本电脑版奔腾。原理十分简单:双模式工作–全功率模式和省电模式。插上电源时,全速工作;电池供电时,已几乎一半主频工作(当然可以手动切回全速模式)。

· Enhanced SpeedStep (GV2.1) 加入了根据工作量自动在两个主频间切换的功能。实际上CPU就可以工作在三种模式下了:省电模式,性能优先模式和自动模式。

· Enhanced SpeedStep (GV2.2) 加入了Deep Alert mode。可以已更低的电压工作在省电模式下。

·EIST(Enhanced Intel SpeedStep Technology,GV3) 这版做出了巨大的改进,从此不再是只有两种频率可以选择了,而是有一个范围可以选择,通常以100MHz为一个步进(Step)。

来自CPU省电的秘密(一):EIST

5. C-State P-State,关闭(Disable)

OS 主导了在整个 EIST 在的各个频率的切换,只有它最了解工作量。同时EIST要工作,硬件,固件和OS三方缺一不可。

所以这个配置既可以在 BIOS 配置,又可以在操作系统层面配置,当然在BIOS层配置最好。

如果BIOS已经关闭了 speedstep 或者 EIST,那C-State P-State的配置实际上也没所谓了(原理上是如此)。

5.1 硬件

Intel CPU是 EIST 可以工作的硬件基础。它通过一系列寄存器保证固件和OS可以得到足够的信息,有足够的手段控制EIST的工作模式。

5.2 固件

C-State P-State是 ACPI 规范中的内容,ACPI全称 Advanced Configuration and Power Interface,是固件和OS的电源管理接口和模型。

ACPI规范定义了CPU的工作状态,包含

  • S-States,指系统睡眠状态
  • C-States,(CPU Power states)指CPU电源状态
  • P-States,(CPU Performance states)则指CPU性能状态
  • G-States(全局状态)
  • D-States(设备状态)

整个关系如下图:

img

C-state 有C0,C1…Cn多种模式,但只有 C0 是正常工作模式(active),其他方式都是idle状态,只是idle的程度不同,C后的数越高,CPU睡眠得越深,CPU的功耗被降低得越多,同时需要更多的时间回到C0模式。

在下表所列的模式中,C1 到 C3通过切断 CPU 内部的时钟,C4 到 C6 模式通过降低 CPU 的电压。”Enhanced”模式两种方式都采用。

img

EIST 各个工作频率是通过在C0下的各种P-states表现出来的。固件会检测 CPU 是否支持 EIST,如果不支持就没什么事了。

5.3 OS

OS知道现在的工作量,由它来决定是不是要在各个P-states之间迁移。Linux的支持始于Linux 2.6,由cpufreq来提供支持。

5.4 额外知识点 Turbo Boost

Intel Turbo Boost 是Intel在系列处理器实现的技术,通过动态控制处理器的时钟率来激活处理器运行在超过基础操作主频。

Intel Turbo Boost 监控处理器当期使用情况,以及处理器是否接近最大热量设计功率(thermal design power, TDP)。

Turbo Boost允许一个或多个CPU核心运行在更高的P-states(turbo P-state),这个最大P-state需要考虑以下因素:

1) 激活的核心数量( C0状态)

2) 评估当前处理器消耗(Imax)

3) 评估处理器电能消耗(TDP - Thermal Design Power)

4) 处理器稳定

内核启动参数需要激活P-state驱动后才能使用Turbo Boost功能。不过,Intel仍然建议处理器工作在基础时钟速度(base clock speed),因为Intel不承诺处理器任何时候都能够达到最大Turbo Boost speed。

5.5 额外知识点 cpufreq子系统

cpufreq 为在Linux 内核中更好的支持不同 CPU 的变频技术提供了一个统一的设计框架,cpufreq 在设计上主要分为以下三个模块:

1) cpufreq 模块(cpufreq module)对如何在底层控制各种不同 CPU 所支持的变频技术以及如何在上层根据系统负载动态选择合适的运行频率进行了封装和抽象,并在二者之间定义了清晰的接口,从而在设计上完成了前文所提到的对 mechanism 与 policy 的分离。

2) 在 cpufreq 模块的底层,各个 CPU 生产厂商只需根据其变频技术的硬件实现和使用方法提供与其 CPU 相关的变频驱动程序(CPU-specific drivers),例如 Intel 需要提供支持 Enhanced SpeedStep 技术的 CPU 驱动程序,而 AMD 则需要提供支持 PowerNow! 技术的 CPU 驱动程序。

3) 在 cpufreq 模块的上层,governor 作为选择合适的目标运行频率的决策者,根据一定的标准在适当的时刻选择出 CPU 适合的运行频率,并通过 cpufreq 模块定义的接口操作底层与 CPU 相关的变频驱动程序,将 CPU 设置运行在选定的运行频率上。目前最新的 Linux 内核中提供了 performance 、powersave 、userspace、conservative 和 ondemand 五种 governors 供用户选择使用,它们在选择 CPU 合适的运行频率时使用的是各自不同的标准并分别适用于不同的应用场景。用户在同一时间只能选择其中一个 governor 使用,但是可以在系统运行过程中根据应用需求的变化而切换使用另一个 governor 。

这种设计带来的好处是使得 governor 和 CPU 相关的变频驱动程序的开发可以相互独立进行,并在最大限度上实现代码重用,内核开发人员在编写和试验新的 governor 时不会再陷入到某款特定 CPU 的变频技术的硬件实现细节中去,而 CPU 生产厂商在向 Linux 内核中添加支持其特定的 CPU 变频技术的代码时只需提供一个相对来说简单了很多的驱动程序,而不必考虑在各种不同的应用场景中如何选择合适的运行频率这些复杂的问题。

6. DCU Streamer Prefetcher,开启(Enabled)

CPU数据预读取功能,如果设置为Enabled,会预取流并发送到它的一级缓存,以改善数据处理和系统性能。

7. DCU IP Prefetcher,开启(Enabled)

IP数据预读取功能。

8. ACPI - MADT Mode,Legacy Mode

几个概念:

  • MADT: Multiple APIC Description Table,多种APIC描述表。
  • 高级电源管理(APM),由BIOS运行,所以通常不知道用户在做什么。它对USB设备,附加卡和IEEE 1394以外的设备一无所知。
  • 高级配置和电源接口(ACPI)旨在替代APM。它以操作系统为中心,从而可以更好地控制操作系统和计算机的其他组件。

ACPI - MADT 模式,用来设置操作系统是否可以对电源进行控制:

  • Legacy Mode:当操作系统在不支持 ACPI 的情况下运行时配置,硬件和电源不是通过 ACPI 来管理的,而是由高级电源管理(APM)、旧式即插即用(Legacy Plug and Play)等管理。
  • Modern Mode: 使用ACPI管理。

9. Memory Frequency,auto

DDR 速度配置,内存运行频率。可以超频内存。在改变处理器的外频后,由于前端总线的频率也会改变,所以需要改变内存频率保证总线同步。

一些BIOS选项参考,不同设备显示也不同,单位为MHz:

  • Auto
  • 1333
  • 1600
  • 1867
  • 2133
  • 2400
  • 2666
  • 2933-OvrClk

9.1 额外知识点

  • CPU外频 = 外频,CPU乃至整个计算机系统的基准频率,单位是MHz(兆赫兹)。计算机系统中大多数的频率都是在外频的基础上,乘以一定的倍数来实现。
  • CPU频率 = 主频 = 外频×倍频
  • 前端总线(FSB)频率,通常是CPU的外频的2到4倍,也没有固定的倍数,和cpu型号及主板芯片组有关。
  • 系统总线频率,可以代指系统中所有总线(前端总线,agp总线,pci总线等),也有时候指南北桥之间的总线,也没有固定的频率和算法。
  • 倍频

9.2 额外知识点 前端总线

CPU 是通过前端总线连接到北桥芯片,进而通过北桥芯片和内存、显卡交换数据。

前端总线是CPU和外界交换数据的最主要通道,因此前端总线的数据传输能力对计算机整体性能作用很大,如果没足够快的前端总线,再强的CPU也不能明显提高计算机整体速度。

数据传输最大带宽取决于所有同时传输的数据的宽度和传输频率,即数据带宽=(总线频率×数据位宽)÷8。

前端总线频率越大,代表着CPU与北桥芯片之间的数据传输能力越大,更能充分发挥出CPU的功能。

9.3 额外知识点 外频 主频 倍频

外频具体是指CPU到芯片组之间的总线速度。外频是CPU与主板之间同步运行的速度,而且目前的绝大部分电脑系统中外频,也是内存与主板之间的同步运行的速度,在这种方式下,可以理解为CPU的外频直接与内存相连通,实现两者间的同步运行状态。

由于CPU工作频率不断提高,而PC机的一些其他设备(如插卡、硬盘等)却受到工艺的限制,不能承受更高的频率,因此限制了CPU频率的进一步提高。

倍频技术就是使外部设备可以工作在一个较低频率(这个较低频率就是外频)上,而又不限制影响CPU本身的工作频率(主频),因为CPU主频就是外频的倍数。

9.4 额外知识点 外频/主频 vs 前端总线

前端总线与外频在以前的很长一段时间里(Pentium 4出现之前)是相同的。随着计算机技术的发展,人们发现前端总线频率需要高于外频,因此采用了QDR(Quad Date Rate)技术(4倍并发),或者其他类似的技术实现。

一般来说,主频和前端总线频率都是以外频为基数,前者是乘以倍频数,后者是乘以2/4/8,。

9.5 额外知识点 内存频率

一般用三种频率来对内存进行描述——核心频率/时钟频率/数据传输速率。

  • 数据传输速率就是标在内存条上的频率,如DDR333 和DDR400,平时说的内存频率默认就是指数据传输速率。
  • 内存的核心频率就好比是CPU的频率,是本身所固有的频率。
  • 时钟频率就是我们所说的外频。

对于DDR来说,三者的比例是1:1:2,对于DDR2来说,三者的比例关系是1:2:4。

10. Set Throttling Mode,CLTT

设置内存温度调节模式,菜单选项为:

  • Disabled
  • OLTT(Open Loop Thermal Throttling):限定最大内存带宽,限制内存的功耗。通过监测每个DIMM rank的传输带宽,可以估算DIMM功耗和温度。该假设是一定时间内,温度和内存带宽是线性关系。
  • CLTT(Closed Loop Thermal Throttling):大部分服务器的内存都有热传感器,会使用CLTT。内存控制器会周期性的访问热传感器,并根据数据对内存设置频率占空比。

11. Memory Power Savings Mode,关闭(Disable)

设置内存省电功能。

参考资料


clieng-go 实现从 yaml 创建和删除资源

将 Clientset、dynamic.Interface 、ns和文件地址传入方法即可。如何创建 Clientset、dynamic.Interface ,可以参考我另一篇文章 《kubernetes 开发之 client-go 学习》

一、创建资源

方法

import (

	"errors"
	"fmt"
	"io"
	"strconv"
	"time"
	"os"
	
	"go/ast"
	"go/token"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/yaml"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/restmapper"
)

func installYaml(clientSet *kubernetes.Clientset, dynamicClient dynamic.Interface, ns string, filename string) error {
	f, err := os.Open(filename)
	logrus.Info("=====================>" + filename)

	if err != nil {
		logrus.Error(err)
		return err
	}
	d := yaml.NewYAMLOrJSONDecoder(f, 4096)
	dc := clientSet.Discovery()

	restMapperRes, err := restmapper.GetAPIGroupResources(dc)
	if err != nil {
		logrus.Error(err)
		return err
	}

	restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)

	for {
		ext := runtime.RawExtension{}

		if err := d.Decode(&ext); err != nil {
			if err == io.EOF {
				break
			}
			logrus.Fatal(err)
		}

		// runtime.Object
		obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(ext.Raw, nil, nil)
		if err != nil {
			logrus.Error(err)
			return err
		}

		mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		fmt.Printf("mapping:%+v\n", mapping)
		if err != nil {
			logrus.Error(err)
			return err
		}

		// runtime.Object转换为unstructed
		unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
		if err != nil {
			logrus.Error(err)
			return err
		}
		// fmt.Printf("unstructuredObj: %+v", unstructuredObj)

		var unstruct unstructured.Unstructured

		unstruct.Object = unstructuredObj

		if ns == "" {
			res, err := dynamicClient.Resource(mapping.Resource).Create(&unstruct, metav1.CreateOptions{})
			if err != nil {
				logrus.Error(err)
				return err
			}

			GuessType(res)
		} else {
			res, err := dynamicClient.Resource(mapping.Resource).Namespace(ns).Create(&unstruct, metav1.CreateOptions{})
			if err != nil {
				logrus.Error(err)
				return err
			}
			GuessType(res)
		}

	}

	return nil
}

func GuessType(obj interface{}) {
	fset := token.NewFileSet()
	ast.Print(fset, obj)
}

使用

	pwd, _ := os.Getwd()
	if pwd != "" {
		pwd = pwd + "/"
	}
	
	err := installYaml(clientSet, dynamicClient, "", pwd+"abcdef.yaml")
	if err != nil {
		return err
	}

二、删除资源

与创建资源基本相同,不同的是我们传入 dynamic client 内的数据不是yaml信息,而是资源名,所以要从yaml文件中解析资源名。

方法

func uninstallYaml(clientSet *kubernetes.Clientset, dynamicClient dynamic.Interface, ns string, filename string) error {
	f, err := os.Open(filename)

	if err != nil {
		logrus.Error(err)
		return err
	}
	d := yaml.NewYAMLOrJSONDecoder(f, 4096)
	dc := clientSet.Discovery()

	restMapperRes, err := restmapper.GetAPIGroupResources(dc)
	if err != nil {
		logrus.Error(err)
		return err
	}

	restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)

	for {
		ext := runtime.RawExtension{}

		if err := d.Decode(&ext); err != nil {
			if err == io.EOF {
				break
			}
			logrus.Fatal(err)
		}

		// runtime.Object
		obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(ext.Raw, nil, nil)
		if err != nil {
			logrus.Error(err)
			return err
		}

		mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		// fmt.Printf("mapping:%+v\n", mapping)
		if err != nil {
			logrus.Error(err)
			return err
		}

		// runtime.Object转换为unstructed
		unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
		if err != nil {
			logrus.Error(err)
			return err
		}
		// fmt.Printf("unstructuredObj: %+v", unstructuredObj)

		var unstruct unstructured.Unstructured

		unstruct.Object = unstructuredObj

		tmpMetadata := unstructuredObj["metadata"].(map[string]interface{})
		tmpName := tmpMetadata["name"].(string)
		tmpKind := unstructuredObj["kind"].(string)
		logrus.Info("deleting resource name: " + tmpName + ", kind: " + tmpKind + ", ns: " + ns)

		if ns == "" {
			err := dynamicClient.Resource(mapping.Resource).Delete(tmpName, &metav1.DeleteOptions{})
			if err != nil {
				logrus.Error(err)
				return err
			}
		} else {
			err := dynamicClient.Resource(mapping.Resource).Namespace(ns).Delete(tmpName, &metav1.DeleteOptions{})
			if err != nil {
				logrus.Error(err)
				return err
			}
		}

	}

	return nil
}

使用

	uninstallYaml(clientSet, dynamicClient, "", "abcdef.yaml")

参考资料


linux 查找最近修改的文件

按时间查找可以使用参数:

  • -atime 访问时间
  • -ctime 改变状态的时间
  • -mtime修改的时间
find ./ -mtime 0 #返回最近24小时内修改过的文件。
find ./ -mtime 1 #返回的是前48~24小时修改过的文件,注意不是48小时以内修改过的文件。
find . -name '*.php' -mmin -30 #查找最近30分钟修改的当前目录下的.php文件
find . -name '*.inc' -mtime 0 -ls #查找最近24小时修改的当前目录下的.php文件,并列出详细信息

golang 创建错误

Golang 中的错误也是一种类型。错误用内置的 error 类型表示。源码定义如下:

// src/builtin/builtin.go

type error interface {
    Error() string
}

error 为 nil 代表没有错误。

error 创建方式有两种:

  • errors.New()
  • fmt.Errorf(),增加上下文的信息,更精确的描述错误。
err1 := errors.New("abcde")
err2 := fmt.Errorf("math: square root of negative number %g", x)

参考资料


kubernetes 开发之 client-go 学习

3月份已经记录了一些开发与 kubernetes 交互的文章,由于工期比较紧,虽然开发完成了,对client-go的学习还是不彻底。这一篇我记录一些我在 kubernetes 开发中的备忘。

访问 k8s 集群获取资源的几种方式:

  1. 命令行 kubectl

  2. http k8s REST API

  3. 代码库 client-go

    1. ClientSet

      ClientSet 可以对k8s的标准资源进行CURD

    2. Dynamic Client

      它能处理 kubernetes 所有的资源,控制所有的 API,处理CRD一般都是用它。请求返回的对象 unstructured.Unstructured 是一个 map[string]interface{}

    3. DiscoveryClient

      这是一种发现客户端,在前面的客户端中需要知道资源的 Resource 和 Version 才能找到你想要的,

      这些信息太多很难全部记住,这个客户端用于获取资源组、版本等信息。

    4. RESTClient

      最基础的客户端,对HTTP Request进行了封装,实现了RESTFul风格的API。

    5. informer

其中,ClientSet、DynamicClient、DiscoveryClient 都是基于 RESTClient 封装的。

一、背景

Kubernetes 官方从2016年8月份开始,将Kubernetes资源操作相关的核心源码抽取出来,独立出来一个项目Client-go,作为官方提供的Go client。

Kubernetes的 部分代码也是基于这个client实现的。

引入包:

go1.16+:

go get k8s.io/client-go@latest

go1.11+ 使用特定版本:

go get k8s.io/client-go@v0.20.4

更多内容参考官方安装手册

二、构造k8s配置文件,生成clientset和dynamic client

无论是使用 ClientSet 还是 Dynamic Client,第一步都是构建 k8s 的配置文件。

通过参数(master的url或者kubeconfig路径)和BuildConfigFromFlags方法来获取rest.Config对象。

kubeconfig路径一般路径为$HOME/.kube/config,用来配置本地连接的kubernetes集群。

package main

import (
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/dynamic"    
    "log"
    "os"
    "path/filepath"
)

// 从文件 ~/.kube/config 中构造config
func getConfigFromFile() (*restclient.Config, error){
    kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config")
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        log.Fatal(err)
    }
    
    return config, err
}

const (
	// High enough QPS to fit all expected use cases.
	DefaultQPS = 1e6
	// High enough Burst to fit all expected use cases.
	DefaultBurst = 1e6

)

// 从数据库存储的多集群信息,包括masterIP,kubeconfig和元数据信息中初始化,参考了[k8s多集群go-client实现](https://blog.csdn.net/weixin_43908373/article/details/95618989)

func InitClient(clusterName string) (*kubernetes.Clientset, *rest.Config, dynamic.Interface, error) {
	//数据库取出集群信息
	master, kubeconfig, err := GetClusterInfo(clusterName)
	if err != nil {
		logs.Error("get db for cluster kubeconfig error. %v ", err)
		return nil, nil, err
	}
	kubeconfigJson, err := yaml.YAMLToJSON([]byte(kubeconfig))
	if err != nil {
		logs.Error("yaml to json err")
	}
	configV1 := clientcmdapiv1.Config{}
	err = json.Unmarshal(kubeconfigJson, &configV1)
	if err != nil {
		logs.Error("json unmarshal kubeconfig error. %v ", err)
		return nil, nil, err
	}
	// 切换匹配的版本
	configObject, err := clientcmdlatest.Scheme.ConvertToVersion(&configV1, clientcmdapi.SchemeGroupVersion)
	if err != nil {
		logs.Error("ConvertToVersion error. %v ", err)
		return nil, nil, err
	}
	configInternal := configObject.(*clientcmdapi.Config)

	// 实例化配置信息
	clientConfig, err := clientcmd.NewDefaultClientConfig(*configInternal, &clientcmd.ConfigOverrides{
		ClusterDefaults: clientcmdapi.Cluster{Server: master},
	}).ClientConfig()

	if err != nil {
		logs.Error("build client config error. %v ", err)
		return nil, nil, err
	}
	clientConfig.QPS = defaultQPS
	clientConfig.Burst = defaultBurst
	
	// 实例化客户端
	clientSet, err := kubernetes.NewForConfig(clientConfig)

	if err != nil {
		logs.Error("(%s) kubernetes.NewForConfig(%v) error.%v", master, err, clientConfig)
		return nil, nil, err
	}
	
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		logrus.Error(err)
		return nil, nil, err
	}

	return clientSet, clientConfig, dynamicClient, nil
}

三、clientSet

Clientset 是调用 Kubernetes 资源对象最常用的 client,可以操作所有的资源对象。需要指定Group、Version,然后根据 Resource 获取对应的 XXInterface。

通过 *rest.Config 参数和 NewForConfig 方法来获取 clientset 对象,clientset 是多个client的集合,每个client可能包含不同版本的方法调用。

config, err := getConfig()

clientset, err := kubernetes.NewForConfig(config)
// 获取node 列表 
nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
// 更新
_, err = a.client.CoreV1().Nodes().Update(context.TODO(), newNode, metav1.UpdateOptions{})
// 发送patch 指令更新
patchTemplate := map[string]interface{}{
		"metadata": map[string]interface{}{
			"labels": map[string]interface{}{
				labelkey: labelvaule,
			},
		},
	}
patchdata, _ := json.Marshal(patchTemplate)
_, err := clientset.CoreV1().Nodes().Patch(ctx, Nodes[i].Name, types.StrategicMergePatchType, patchdata, metav1.PatchOptions{})

ns 创建

nsSpec := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}
_, err := clientset.Core().Namespaces().Create(nsSpec)

secrets 创建

    data := make(map[string][]byte)
    data["user"] = []byte("admin")
    data["password"] = []byte("password")

    // https://godoc.org/k8s.io/api/core/v1
    // https://godoc.org/k8s.io/client-go/kubernetes/typed/core/v1#SecretInterface
    secrets, err := clientset.CoreV1().Secrets("default").Create(&v1.Secret{
            TypeMeta: metav1.TypeMeta{
                    Kind: "Secret",
            },
            ObjectMeta: metav1.ObjectMeta{
                    Name: "generic-secret",
                    Namespace: "default",
            },
            Data: data,
    })

其它

pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
services, err := clientset.CoreV1().Services("").List(metav1.ListOptions{})
serviceAccounts, err := clientset.CoreV1().ServiceAccounts("").List(metav1.ListOptions{})
deployments, err := clientset.AppsV1().Deployments("").List(metav1.ListOptions{})
pvs, err := clientset.CoreV1().PersistentVolumes().List(metav1.ListOptions{})
pvcs, err := clientset.CoreV1().PersistentVolumeClaims("").List(metav1.ListOptions{})
namespaces, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{})
ingresses, err := clientset.ExtensionsV1beta1().Ingresses("").List(metav1.ListOptions{})
secrets, err := clientset.CoreV1().Secrets("").List(metav1.ListOptions{})
configMaps, err := clientset.CoreV1().ConfigMaps("").List(metav1.ListOptions{})
ingress, err := clientset.ExtensionsV1beta1().Ingresses("jx").Get("docker-registry", metav1.GetOptions{})

四、Dynamic Client

获得 dynamic client

    config, err := getConfig()
    dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		logrus.Error(err)
		return nil, nil, err
	}

获取资源,以kyverno为例 :

	gvr := getGVR("kyverno.io", "v1", "clusterpolicies")

	list, err := dynamicClient.Resource(gvr).List(metav1.ListOptions{})

	if err != nil {
		logrus.Error(err)
		return
	}
	

// getGVR :- gets GroupVersionResource for dynamic client
func getGVR(group, version, resource string) schema.GroupVersionResource {
	return schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
}

五、DiscoveryClient

DiscoveryClient 在请求到数据之后会缓存到本地,默认存储位置是~/.kube/cache和~/.kube/http-cache,默认是每10分钟会和API Server同步一次。

package main

import (
	"fmt"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/tools/clientcmd"
)

func main()  {
	config, err := clientcmd.BuildConfigFromFlags("","/root/.kube/config")
	if err != nil {
		panic(err.Error())
	}

	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	_, APIResourceList, err := discoveryClient.ServerGroupsAndResources()
	if err != nil {
		panic(err.Error())
	}
	for _, list := range APIResourceList {
		gv, err := schema.ParseGroupVersion(list.GroupVersion)
		if err != nil {
			panic(err.Error())
		}
		for _, resource := range list.APIResources {
			fmt.Printf("name: %v, group: %v, version %v\n", resource.Name, gv.Group, gv.Version)
		}
	}
}

六、informer

使用 informer 的目的是为了减轻 apiserver 数据交互的压力而抽象出来的一个 cache 层。

Informer 只会调用 Kubernetes List 和 Watch 两种类型的 API。Informer 在初始化的时,先调用 Kubernetes List API 获得某种 resource 的全部 Object,缓存在内存中; 然后,调用 Watch API 去 watch 这种 resource,去维护这份缓存; 最后,Informer 就不再调用 Kubernetes 的任何 API。

// 通过informer 获取node 列表
factory := informers.NewSharedInformerFactory(clientset, 30*time.Second)
nodeInformer := factory.Core().V1().Nodes()
go nodeInformer.Informer().Run(stopCh)
if !cache.WaitForCacheSync(stopCh, nodeInformer.Informer().HasSynced) {
    runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
    return
}
nodes, err := nodeInformer.Lister().List(labels.NewSelector())

参考资料


golang 文件路径相关操作

获取当前目录:

pwd, err := os.Getwd()

获取当前目录下的文件:

filepathNames, err := filepath.Glob(filepath.Join(pwd,"*"))

获取当前目录下的文件(包括子目录):

filepath.Walk(pwd,func(path string, info os.FileInfo, err error) error{
		fmt.Println(path) //打印path信息
		fmt.Println(info.Name()) //打印文件或目录名
		return nil
	})

参考资料