kubernetes 接口文档
k8s视频教程参考
k8s二次开发本文
k8s自定义资源文章资料
restFul 接口官方
k8sapi接口方式 rest、集群角色token、证书访问
多云管理kuboard
kuboard
- 安装
docker run -d \
--restart=unless-stopped \
--name=kuboard \
-p 80:80/tcp \
-p 10081:10081/tcp \
-e KUBOARD_ENDPOINT="http://内网IP:80" \
-e KUBOARD_AGENT_SERVER_TCP_PORT="10081" \
-v /root/kuboard-data:/data \
eipwork/kuboard:v3
http接口方式
- kubectl proxy 启动接口允许指定ip访问
- kubectl proxy --address=‘xx.xx.xx.xx --accept-hosts=’^*$’ --port=8001
- http://xx.xx.xx.xx:8001/api/v1 接口列表
- http://11.164.62.250:8001/api/v1/namespaces/default/pods pod列表
- 主机容器列表 http://xx.xx.xx.xx:8001/api/v1/pods?fieldSelector=spec.nodeName=diagnosis-tool011164062250.na62
gossh
service/ssh.go
通过ssh获取实时数据
package service
import (
"fmt"
gossh "golang.org/x/crypto/ssh"
"net"
)
// 连接信息
type Cli struct {
user string
pwd string
addr string
client *gossh.Client
session *gossh.Session
LastResult string
}
// 连接对象
func (c *Cli) Connect() (*Cli, error) {
config := &gossh.ClientConfig{}
config.SetDefaults()
config.User = c.user
config.Auth = []gossh.AuthMethod{gossh.Password(c.pwd)}
config.HostKeyCallback = func(hostname string, remote net.Addr, key gossh.PublicKey) error { return nil }
client, err := gossh.Dial("tcp", c.addr, config)
if nil != err {
return c, err
}
c.client = client
return c, nil
}
// 执行shell
func (c Cli) Run(shell string) (string, error) {
if c.client == nil {
if _, err := c.Connect(); err != nil {
return "", err
}
}
session, err := c.client.NewSession()
if err != nil {
return "", err
}
// 关闭会话
defer session.Close()
buf, err := session.CombinedOutput(shell)
c.LastResult = string(buf)
return c.LastResult, err
}
func RunSsh() {
cli := Cli{
addr: "x.x.x.x",
user: "xxx",
pwd: "xxx",
}
// 建立连接对象
c, _ := cli.Connect()
// 退出时关闭连接
defer c.client.Close()
//res, _ := c.Run("ls")
res1, _ := c.Run("pwd")
//fmt.Println(res)
fmt.Println(res1)
}
前端整合
pod 列表点击登录
pods.vue
<template>
<a-card>
<a-table :dataSource="pods" :columns="columns" :customRow="rowClick">
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'ip'">
<a>登录</a>
</template>
</template>
</a-table>
</a-card>
</template>
<script>
import { getPodsList } from '@/api/manage'
export default {
data() {
return {
res: [],
pods: [],
columns: [
{
title: 'Pod名称',
dataIndex: 'name',
// key: 'name',
slots: { customRender: 'name' },
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
},
{
title: '镜像',
dataIndex: 'image',
key: 'image',
},
{
title: 'ip',
dataIndex: 'ip',
key: 'ip',
},
{
title: 'podStatus',
dataIndex: 'podStatus',
key: 'podStatus',
// width:30,
},
{
title: '登录',
dataIndex: 'ssh',
key: 'ssh',
},
// {
// title: 'Action',
// key: 'action',
// slots: { customRender: 'action' },
// }
],
loadData: parameter => {
const requestParameters = Object.assign({}, parameter, this.queryParam)
console.log('loadData request parameters:', requestParameters)
return getServiceList(requestParameters)
.then(res => {
return res.result
})
},
}
},
mounted() {
this.initData()
},
methods: {
initData() {
// console.log('loadData.parameter', parameter)
return getPodsList()
.then(res => {
this.res = res
const pods = []
for (let i in res) {
const name = res[i].metadata.name
const status = res[i].status.phase
const image = res[i].spec.containers[0].image
let podStatus = {}
if ("containerStatuses" in res[i].status) {
podStatus = res[i].status.containerStatuses[0].state
// if (res[i].status.containerStatuses.length > 0) {
// podStatus = res[i].status.containerStatuses.state
// }
}
// const create_time = res[i].metadata.creationTimestamp.subsubstring(0,10)
const ip = res[i].status.podIP
pods.push({
key: i,
name: name,
status: status,
podStatus: JSON.stringify(podStatus),
image: image,
ip: ip,
ssh: "登录"
})
}
this.pods = pods
console.log("pods", res, pods)
return res.result
})
},
rowClick(record, index) {
return {
on: {
click: () => {
console.log('点击跳转:', record)
this.$router.push({ path: "/k8s/pod-ssh", query: { podname: record.name, namespace: "default" } })
// const url = `/k8s/pod-ssh?podname=${record.name}&namespace=${"default"}`
// this.$router.replace(url)
},
dblclick: () => {
console.log('双击了我')
},
}
}
},
},
}
</script>
controller
CRD
- api 的路径
- kubectl get --raw /
- 使用最多的 kubectl get -raw /apis/apps/v1 | python -m json.tool
- kind StatefulSet Deployment
- GVR=请求路径 GVK=资源实体
- 通过kubectl proxy
- kubectl get statefulsets.apps -A
- curl 127.0.0.1:8001/apis/apps/v1/statefulset| grep name
自定义资源
有了自定义资源定义API,开发者将不需要逐一进行Deployment、Service、ConfigMap等步骤,而是创建并关联一些用于表述整个应用程序或者软件服务的对象。除此,我们能使用自定义的高阶对象,并在这些高阶对象的基础上创建底层对象。例如:我们想要一个Backup资源,我们创建它的对象时,就希望通过spec的定义进行日常的备份操作声明,当提交给k8s集群的时候,相关的Deployment、Service资源会被自动创建,很大程度让业务扩展性加大
Informers
事件接口带索引查找功能的内存缓存
- 下载
- go get k8s.io/client-go@v0.22.0
- go get sigs.k8s.io/controller-runtime
- 更新deployment官方命令
- kubectl apply -f https://k8s.io/examples/application/deployment.yaml
- kubectl apply -f nginx-deployment.yaml
- kubectl edit deployment 编辑副本数量动态更新
- kubectl delete deployment nginx-deployment
package main
import (
"fmt"
v1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"time"
)
func main() {
conf, err := config.GetConfig()
if err != nil {
panic(err)
return
}
clientSet, err := kubernetes.NewForConfig(conf)
if err != nil {
panic(err)
return
}
informerFactory := informers.NewSharedInformerFactory(clientSet, 30*time.Second)
deployInformer := informerFactory.Apps().V1().Deployments()
informer := deployInformer.Informer()
deployLister := deployInformer.Lister()
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: onAdd,
UpdateFunc: onUpdate,
DeleteFunc: onDelete,
})
stopper := make(chan struct{})
defer close(stopper)
// 启动Informer List & Watch
informerFactory.Start(stopper)
// 等待所有的Informer缓存同步
informerFactory.WaitForCacheSync(stopper)
deployments, err := deployLister.Deployments("default").List(labels.Everything())
// 编辑deploy列表
for index, deploy := range deployments {
fmt.Printf("%d -> %s\n", index, deploy.Name)
}
<-stopper
}
func onAdd(obj interface{}) {
deploy := obj.(*v1.Deployment)
klog.Infoln("add a deploy: ", deploy.Name)
}
func onUpdate(old, new interface{}) {
oldDeploy := old.(*v1.Deployment)
newDeploy := new.(*v1.Deployment)
klog.Infoln("update deploy: ", "旧:", newDeploy.Name, oldDeploy.Status.Replicas, "新:", newDeploy.Status.Replicas, "就绪数:", newDeploy.Status.ReadyReplicas)
}
func onDelete(obj interface{}) {
deploy := obj.(*v1.Deployment)
klog.Infoln("delete a deploy:", deploy.Name)
}
websocket
安装websocket
- go get github.com/gorilla/websocket
- 进入pod kubectl exec -it hao-pod /bin/bash
package.json
“dependencies”: {
“core-js”: “^3.8.3”,
“vue”: “^3.2.13”,
“xterm”: “^4.15.0”,
“xterm-addon-fit”: “^0.5.0”
},
vue前端App.vue
<template>
<div id="app">
<div id="xterm"/>
</div>
</template>
<script>
import 'xterm/css/xterm.css'
import { Terminal } from 'xterm';
import { FitAddon } from "xterm-addon-fit";
export default {
name: 'WebShell',
data() {
return {
// socketURI: 'ws://127.0.0.1:10000/namespace/kube-system/pod/kube-proxy-dw4ww/container/kube-proxy?method=sh'
socketURI: 'ws://127.0.0.1:3000/namespace/default/pod/hao-pod/container/kube-proxy?method=sh'
}
},
mounted() {
this.initSocket()
},
methods: {
initTerm() {
let element = document.getElementById('xterm');
// 设置了cols或者fitAddon.fit(); 当一行字符超过80个过会替换现在的内容,否则换行
const term = new Terminal({
cursorBlink: true, // 关标闪烁
cursorStyle: "underline", // 光标样式 'block' | 'underline' | 'bar'
scrollback: 100, // 当行的滚动超过初始值时保留的行视窗,越大回滚能看的内容越多,
disableStdin: false, //是否应禁用输入
});
this.term = term;
term.prompt = () => {
term.write("\r\n$ ");
};
term.prompt();
const fitAddon = new FitAddon();
this.term.loadAddon(fitAddon);
this.fitAddon = fitAddon;
term.open(element);
// 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸),初始化的时候宽高都是对的
fitAddon.fit();
term.focus();
this.term.onData(data => {
var msg = {type: "input", input: data}
// this.term.write(data);
this.socket.send(JSON.stringify(msg));
});
window.addEventListener('resize', this.resizeTerm);
},
getColsAndRows(element) {
// 暂时不用
element = element || document.getElementById('xterm');
return {
rows: parseInt((element.clientHeight - 0) / 18),
cols: 10 // parseInt(element.clientWidth / 8)
};
},
resizeTerm() {
this.fitAddon.fit();
this.term.scrollToBottom();
},
initSocket() {
this.socket = new WebSocket(`${this.socketURI}`);
this.socketOnClose();
this.socketOnOpen();
this.socketOnError();
this.socketOnMessage();
},
socketOnOpen() {
this.socket.onopen = () => {
// 连接成功后
this.initTerm()
}
},
socketOnMessage() {
this.socket.onmessage = (event) => {
// 接收推送的消息
this.term.write(event.data.toString());
}
},
socketOnClose() {
this.socket.onclose = () => {
console.log('close socket')
}
},
socketOnError() {
this.socket.onerror = () => {
console.log('socket error')
}
}
}
}
</script>
<style scoped>
#xterm {
padding: 15px 0;
}
</style>
router/router.go
package router
import (
"gin-client-go/pkg/apis"
"github.com/gin-gonic/gin"
)
func InitRouter(r *gin.Engine) {
r.GET("/ping", apis.Ping)
r.GET("namespaces", apis.GetNamespaces)
r.GET("/namespace/:namespaceName/pods", apis.GetPods)
r.GET("/namespace/:namespaceName/pod/:podName/container/:container", apis.ExecContainer)
r.GET("/ws", func(c *gin.Context) { apis.Ws(c.Writer, c.Request) })
}
apis/pod.go
package apis
import (
"fmt"
"gin-client-go/pkg/service"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"net/http"
)
func GetPods(c *gin.Context) {
namespaceName := c.Param("namespaceName")
pods, err := service.GetPods(namespaceName)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
}
c.JSON(http.StatusOK, pods)
}
func ExecContainer(c *gin.Context) {
namespaceName := c.Param("namespaceName")
podName := c.Param("podName")
containerName := c.Param("containerName")
method := c.DefaultQuery("action", "sh")
err := service.WebSSH(namespaceName, podName, containerName, method, c.Writer, c.Request)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
}
}
var wsupgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func Ws(w http.ResponseWriter, r *http.Request) {
conn, err := wsupgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("Failed to set websocket upgrade: %+v", err)
return
}
for {
t, msg, err := conn.ReadMessage()
if err != nil {
break
}
fmt.Println("获取到消息", msg)
conn.WriteMessage(t, msg)
}
}
service/pod.go
package service
import (
"context"
"errors"
"fmt"
"gin-client-go/pkg/client"
"github.com/gorilla/websocket"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/klog/v2"
"net/http"
"sync"
)
func GetPods(namespaceName string) ([]v1.Pod, error) {
ctx := context.Background()
clientSet, err := client.GetK8SClientSet()
if err != nil {
klog.Fatal(err)
return nil, err
}
list, err := clientSet.CoreV1().Pods(namespaceName).List(ctx, metav1.ListOptions{})
if err != nil {
klog.Fatal(err)
return nil, err
}
return list.Items, nil
}
type WsMessage struct {
MessageType int
Data []byte
}
type WsConnection struct {
wsSocket *websocket.Conn
//读取的通道
inChan chan *WsMessage
//写入通道
outChan chan *WsMessage
//为了防止channel重复关闭,加一下锁
mutex sync.Mutex
isClosed bool
//通道关闭后通知的标识
closeChan chan byte
}
// 关闭
func (wsConn *WsConnection) WsClose() {
err := wsConn.wsSocket.Close()
if err != nil {
klog.Fatal(err)
return
}
//为了防止重复关闭,加锁
wsConn.mutex.Lock()
defer wsConn.mutex.Unlock()
if !wsConn.isClosed {
wsConn.isClosed = true
close(wsConn.closeChan)
}
}
// 发送的携程
func (wsConn *WsConnection) wsReadLoop() {
var (
msgType int
data []byte
msg *WsMessage
err error
)
for {
msgType, data, err = wsConn.wsSocket.ReadMessage()
//if msgType, data, err = wsConn.wsSocket.ReadMessage(); err != nil {
if err != nil {
klog.Errorln(err)
goto ERROR
}
msg = &WsMessage{
MessageType: msgType,
Data: data,
}
klog.Infof("读取消息===》", string(data))
select {
case wsConn.inChan <- msg:
//处理websocket已经关闭
case <-wsConn.closeChan:
goto CLOSE
}
}
ERROR:
wsConn.WsClose()
CLOSE:
}
// 写入
func (wsConn *WsConnection) wsWriteLoop() {
klog.Info("777779999999999")
var (
msg *WsMessage
err error
)
for {
select {
case msg = <-wsConn.outChan:
klog.Infof("写入消息===>", msg.Data)
err = wsConn.wsSocket.WriteMessage(msg.MessageType, msg.Data)
if err != nil {
klog.Info("9999999999")
goto ERROR
}
//读取完消息,写给websocket
case <-wsConn.closeChan:
goto CLOSED
}
}
ERROR:
wsConn.WsClose()
CLOSED:
}
// 发送消息
func (wsConn *WsConnection) WsWrite(messageType int, data []byte) (err error) {
select {
//写数据
case wsConn.outChan <- &WsMessage{MessageType: messageType, Data: data}:
return
//遇到关闭的情况
case <-wsConn.closeChan:
err = errors.New("websocket 通道关闭")
}
return
}
func (wsConn *WsConnection) WsRead() (msg *WsMessage, err error) {
select {
case msg = <-wsConn.inChan:
klog.Infof("WsRead", msg)
return
case <-wsConn.closeChan:
err = errors.New("websocket 通道关闭")
}
return
}
// 处理websockert的跨域请求
var wsUpgrade = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type streamHander struct {
WsConnection *WsConnection
resizeEvent chan remotecommand.TerminalSize
}
func (handler *streamHander) Write(p []byte) (size int, err error) {
copyData := make([]byte, len(p))
copy(copyData, p)
size = len(p)
err = handler.WsConnection.WsWrite(websocket.TextMessage, copyData)
return
}
type xtermMessage struct {
MsgType string `json:"type"`
Input string `json:"input"`
Rows uint16 `json:"rows"`
Cols uint16 `json:"cols"`
}
func (handler *streamHander) Read(p []byte) (size int, err error) {
var (
xtermMsg xtermMessage
msg *WsMessage
)
if msg, err = handler.WsConnection.WsRead(); err != nil {
klog.Fatal(err)
return
}
//解析消息
if err = json.Unmarshal(msg.Data, &xtermMsg); err != nil {
return
}
//终端调整大小适配
if xtermMsg.MsgType == "resize" {
handler.resizeEvent <- remotecommand.TerminalSize{Width: xtermMsg.Cols, Height: xtermMsg.Rows}
} else if xtermMsg.MsgType == "input" {
size = len(xtermMsg.Input)
copy(p, xtermMsg.Input)
}
return
}
func (handler *streamHander) Next() (size *remotecommand.TerminalSize) {
ret := <-handler.resizeEvent
size = &ret
return
}
func WebSSH(namespaceName, podName, containerName, method string, resp http.ResponseWriter, req *http.Request) error {
var (
err error
executor remotecommand.Executor
handler *streamHander
wsConn *WsConnection
)
config, err := client.GetRestConfig()
if err != nil {
klog.Fatal(err)
return err
}
clientSet, err := client.GetK8SClientSet()
if err != nil {
klog.Fatal(err)
return err
}
reqSSH := clientSet.CoreV1().RESTClient().Post().Resource("pods").Name(podName).
Namespace(namespaceName).SubResource("exec").
VersionedParams(&v1.PodExecOptions{
Container: containerName,
Command: []string{method},
Stderr: true,
Stdout: true,
Stdin: true,
TTY: true,
}, scheme.ParameterCodec)
if executor, err = remotecommand.NewSPDYExecutor(config, "POST", reqSSH.URL()); err != nil {
klog.Errorln(err)
fmt.Println(executor)
return err
}
klog.Infof("获取pod通道===>", executor)
if wsConn, err = InitWebsocket(resp, req); err != nil {
fmt.Println(wsConn)
return err
}
handler = &streamHander{WsConnection: wsConn, resizeEvent: make(chan remotecommand.TerminalSize)}
if err = executor.Stream(remotecommand.StreamOptions{
Stdin: handler,
Stdout: handler,
Stderr: handler,
TerminalSizeQueue: handler,
Tty: true,
}); err != nil {
goto END
}
return err
END:
klog.Errorln(err)
wsConn.WsClose()
return err
}
// 创建websocket init连接
func InitWebsocket(resp http.ResponseWriter, req *http.Request) (wsConn *WsConnection, err error) {
klog.Infof("初始化通道===>", resp, req)
var (
wsSocket *websocket.Conn
)
if wsSocket, err = wsUpgrade.Upgrade(resp, req, nil); err != nil {
klog.Errorln(err)
return
}
wsConn = &WsConnection{
wsSocket: wsSocket,
inChan: make(chan *WsMessage, 1000),
outChan: make(chan *WsMessage, 1000),
closeChan: make(chan byte),
isClosed: false,
}
//读取携程
go wsConn.wsReadLoop()
//写携程
go wsConn.wsWriteLoop()
return
}
基于gin
注册路由 router/router.go
r.GET("namespaces", apis.GetNamespaces)
新建kubernetes
- 下载包 go get k8s.io/client-go/rest
新建接口apis/namespace.go
package apis
import (
"gin-client-go/pkg/service"
"github.com/gin-gonic/gin"
"net/http"
)
func GetNamespaces(c *gin.Context) {
namespaces, err := service.GetNamespaces()
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
}
c.JSON(http.StatusOK, namespaces)
}
新建服务获取service/namespaces.go
package service
import (
"context"
"gin-client-go/pkg/client"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
)
func GetNamespaces() ([]v1.Namespace, error) {
ctx := context.Background()
clientSet, err := client.GetK8SClientSet()
if err != nil {
klog.Fatal(err)
return nil, err
}
namespaceList, err := clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
klog.Fatal(err)
return nil, err
}
return namespaceList.Items, nil
}
新建 client/kubernetes.go 多去配置文件
package client
import (
"flag"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/klog/v2"
"path/filepath"
)
func GetK8SClientSet() (*kubernetes.Clientset, error) {
config, err := GetRestConfig()
if err != nil {
klog.Fatal(err)
return nil, err
}
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
klog.Fatal(err)
return nil, err
}
return clientSet, nil
}
func GetRestConfig() (config *rest.Config, err error) {
var kubeConfig *string
if home := homedir.HomeDir(); home != "" {
kubeConfig = flag.String("kubeConfig", filepath.Join(home, ".kube", "config"), "kube config")
} else {
klog.Fatal("未找到kubernetes配置文件")
return
}
flag.Parse()
config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig)
if err != nil {
klog.Fatal(err)
return
}
return
}
解决重复刷新问题,单例模式
package client
import (
"flag"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/klog/v2"
"path/filepath"
"sync"
)
// 单例模式解决重复刷新报错问题
var onceClient sync.Once
var onceConfig sync.Once
var KubeConfig *rest.Config
var KubeClientSet *kubernetes.Clientset
func GetK8SClientSet() (*kubernetes.Clientset, error) {
onceClient.Do(func() {
config, err := GetRestConfig()
if err != nil {
klog.Fatal(err)
return
}
KubeClientSet, err = kubernetes.NewForConfig(config)
if err != nil {
klog.Fatal(err)
return
}
})
return KubeClientSet, nil
}
func GetRestConfig() (config *rest.Config, err error) {
onceConfig.Do(func() {
var kubeConfig *string
if home := homedir.HomeDir(); home != "" {
kubeConfig = flag.String("kubeConfig", filepath.Join(home, ".kube", "config"), "kube config")
} else {
klog.Fatal("未找到kubernetes配置文件")
return
}
flag.Parse()
KubeConfig, err = clientcmd.BuildConfigFromFlags("", *kubeConfig)
if err != nil {
klog.Fatal(err)
return
}
return
})
return KubeConfig, nil
}
声明跨域的头
路由注册
新建路由
新建路由 common.go
package apis
import (
"github.com/gin-gonic/gin"
"net/http"
)
func Ping(c *gin.Context) {
c.JSON(http.StatusOK, "Pong")
}
router.go
package router
import (
"gin-client-go/pkg/apis"
"github.com/gin-gonic/gin"
)
func InitRouter(r *gin.Engine) {
r.GET("/ping", apis.Ping)
}
main.go中注册路由
//注册路由
router.InitRouter(engine)
新建目录配置
- 安装包 go get gopkg.in/yaml.v2
- go mode tidy
- 安装gin
- go get github.com/gin-gonic/gin
key.go
package config
type KeyName string
const (
ServerName KeyName = "server_name"
ServerHost KeyName = "server_host"
ServerPort KeyName = "server_port"
)
config.go
package config
import (
"fmt"
"gopkg.in/yaml.v2"
"k8s.io/klog/v2"
"os"
"strconv"
)
var keyMap map[KeyName]string
type Config struct {
Server Server
}
type Server struct {
Name string `yaml:"name"`
Host string `yaml:"host"`
Port string `yaml:"port"`
}
//服务启动读取配置文件
func init() {
var config Config
yamlFile, err := os.ReadFile("./gin-client-go.yaml")
if err != nil {
klog.Fatal(err)
return
}
err = yaml.Unmarshal(yamlFile, &config)
if err != nil {
klog.Fatal(err)
return
}
fmt.Println("获取yaml报错:", err)
fmt.Println("获取到配置信息", config.Server.Name, config.Server.Host, config.Server.Port)
keyMap = make(map[KeyName]string)
keyMap[ServerName] = config.Server.Name
keyMap[ServerHost] = config.Server.Host
keyMap[ServerPort] = config.Server.Port
}
// 对外获取
func GetString(name KeyName) string {
fmt.Println(name, keyMap[name])
return keyMap[name]
}
func GetInt(name KeyName) int {
intStr := keyMap[name]
if intStr == "" {
klog.Fatal("未读取到配置数据", name)
return -1
}
v, err := strconv.Atoi(intStr)
if err != nil {
klog.Fatal(err)
return -1
}
return v
}
gin-client-go.yaml
server:
name: gin-client-go
host: 0.0.0.0
port: 3000
main.go
package main
import (
"fmt"
"gin-client-go/pkg/config"
"github.com/gin-gonic/gin"
"k8s.io/klog/v2"
)
func main() {
engine := gin.Default()
gin.SetMode(gin.DebugMode)
err := engine.Run(fmt.Sprintf("%s:%d", config.GetString(config.ServerHost), config.GetInt(config.ServerPort)))
if err != nil {
klog.Fatal(err)
return
}
}
新建yaml
.gin-client-go.yaml
server:
name:gin-client-go
host:0.0.0.0
port:8888
基于client-go
配置环境
- 集群配置文件
cat /etc/kubernetes/admin.conf
cat ~/.kube/config
放在mac开发环境
代码
- 创建目录
mkdir -p ~/go/src/k8s-dev/gin-client-go
touch go.mod
vi go.mod
module gin-client-go
go 1.19
touch main.go
go get k8s.io/client-go
go mode tidy
- main.go
package main
import (
"context"
"flag"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"path/filepath"
)
func main() {
var kubeConfig *string
ctx := context.Background()
if home := homedir.HomeDir(); home != "" {
kubeConfig = flag.String("kubeConfig", filepath.Join(home, ".kube", "config"), "kube config")
} else {
kubeConfig = flag.String("kubeConfig", "", "kube config")
}
flag.Parse()
config, err := clientcmd.BuildConfigFromFlags("", *kubeConfig)
if err != nil {
panic(err)
return
}
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
return
}
namespaceList, err := clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
panic(err)
return
}
namespaces := namespaceList.Items
for _, namespace := range namespaces {
fmt.Println(namespace.Name, namespace.CreationTimestamp, namespace.Status.Phase)
}
nodeList, err := clientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
panic(err)
return
}
nodes := nodeList.Items
for _, node := range nodes {
fmt.Println("node节点", node.Name)
}
}
输出
default
kube-flannel
kube-node-lease
kube-public
kube-system
kubernetes-dashboard
验证 kubectl get ns
helm部署
wget https://get.helm.sh/helm-v3.10.2-linux-amd64.tar.gz
几乎无法下载
wget https://mirrors.huaweicloud.com/helm/v3.1.2/helm-v3.1.2-linux-amd64.tar.gz
tar -zxvf
cp helm /usr/bin/
添加数据源
helm repo add bitnami https://charts.bitnami.com/bitnami
安装 dashboard
helm install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --namespace kube-system
https://artifacthub.io/packages/helm/k8s-dashboard/kubernetes-dashboard
搜索
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
安装
helm install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard
NAME: kubernetes-dashboard
LAST DEPLOYED: Fri Nov 18 17:07:18 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
*********************************************************************************
*** PLEASE BE PATIENT: kubernetes-dashboard may take a few minutes to install ***
*********************************************************************************
Get the Kubernetes Dashboard URL by running:
export POD_NAME=$(kubectl get pods -n default -l "app.kubernetes.io/name=kubernetes-dashboard,app.kubernetes.io/instance=kubernetes-dashboard" -o jsonpath="{.items[0].metadata.name}")
echo https://127.0.0.1:8443/
kubectl -n default port-forward $POD_NAME 8443:8443
删除 `helm delete kubernetes-dashboard`
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubectl edit svc kubernetes-dashboard
修改svc 网络类型
type:ClusterIP
type:NodePort
kubectl get svc 访问node节点ip
ls /etc/kubernetes/pki/ca.crt
管理证书
导入证书仍不被信任
用火狐浏览器 或者直接敲击 thisisunsafe
登录认证token
kubectl -n kube-system get secret | grep kubernetes-dashboard-token
kubectl describe secret kubernetes-dashb`在这里插入代码片`oard-token-xxxxx
本机的
kubectl get ns
kubectl -n kubernetes-dashboard get secret
kubectl -n kubernetes-dashboard describe secret kubernetes-dashboard-token-xxxx
kubectl -n kube-system get secret
kubectl -n kube-system describe secret ttl-controller-token-ds6f4
kubectl describe secret xxxtoken
kubernetes 学习
cd /var/lib/kubelet/
cd /etc/kubernetes/
admin.conf controller-manager.conf kubelet.conf manifests pki scheduler.conf
副本扩容
kubectl scale --replicas=3 deployment/nginx-deployment
kubectl edit deployment nginx-deployment
kubectl expose --help
将本机的
kubectl expose deployment nginx-deployment --port=3000 --target-port=80
kubectl describe svc nginx-deployment
kubectl get pod -o wide
ipvsadm -Ln
定义资源清单
名称空间 集群级别role 元数据类型HPA
集群资源: namespace、node、role角色、ClusterRole、RoleBinding、ClusterRoleBinding
元数据资源:HPA、PodTemplate、LimitRange
p17
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
version: v1
spec:
containers:
- name: app
image: nginx:v1
- name: test
image: mysql:v1
kubectl apply -f pod.yaml
kubectl describe pod myapp-pod
kubectl log myapp-pod -c test
kubectl logs myapp-pod
kubectl delete pod myapp-pod
kubectl create -f pod.yaml
kubectl delete pod --all
kubectl delete deployment --all
pod 声明周期
initc
spec.containers.command: ['sh','-c','echo the app is running && sleep 3600']
spec.containers.initContainers
- name:init-myservice
image:busybox
command: [' sh','-c','until nslookup myservice;do echo waiting for myservice; sleep 2; done;']
# 当until 条件为真时结束循环