基于Golang + xtermjs构建Kubernetes多集群管理Web Terminal

2024-08-01 18:45:17 浏览数 (4)

背景

近期,一位星球小伙伴入职新公司,之前习惯使用kubectl命令进行Kubernetes运维,但新公司所有集群都托管在阿里云ACK上。于是,他产生了一个想法:能否构建一个支持Kubernetes多集群管理的Web Terminal,通过kubectl命令来管理这些集群?答案是肯定的!接下来,我们将使用Golang和xtermjs来实现这一Kubernetes多集群管理Web Terminal。

技术栈

  • 前端:Vue Typescript ArcoDesign xtermjs
  • 后端:Golang Gin Gorm WebSocket
  • 依赖:Kubectl、Client.go

主要功能

xterm.js使用指南

xterm.js 是一个强大的终端仿真库,可以在网页上创建一个功能完整的终端。它广泛用于构建Web终端应用,比如Kubernetes多集群管理Web Terminal。

首先,使用npm安装xterm.js和xterm-addon-fit:

代码语言:javascript复制
$ npm install xterm xterm-addon-fit

参考代码(Vue3):

代码语言:javascript复制
<template>
  <!-- 终端的容器 -->
  <div ref="terminalContainer" style="width: 100%; height: 100%"></div>
</template>

<script lang="ts" setup>
  // 从 Vue 和 xterm 导入必要的模块
  import { onMounted, onBeforeUnmount, ref } from 'vue';
  import { Terminal } from 'xterm';
  import 'xterm/css/xterm.css';
  import { Base64 } from 'js-base64';

  // 终端容器的引用
  const terminalContainer = ref<HTMLDivElement | null>(null);

  // 终端和 WebSocket 的变量
  let term: Terminal;
  let socket: WebSocket;
  let inputBuffer = ''; // 用于存储输入内容的缓冲区

  // 组件挂载时的生命周期钩子
  onMounted(() => {
    if (terminalContainer.value) {
      // 使用特定选项初始化终端
      term = new Terminal({
        convertEol: true,
        disableStdin: false,
        cursorBlink: true,
        fontSize: 14,
        rows: 50,
        cols: 200,
        theme: {
          foreground: "#ECECEC",
          background: "#000000",
          cursor: 'help',
        },
      });
      // 在指定容器中打开终端
      term.open(terminalContainer.value);

      // 连接 WebSocket
      socket = new WebSocket('ws://10.0.63.28:8087/ws');
      // WebSocket 打开事件监听器
      socket.addEventListener('open', () => {
        term.write('rn集群连接成功!rn'); // 向终端写入成功消息
        term.write('> '); // 显示提示符
      });

      // WebSocket 收到消息事件监听器
      socket.addEventListener('message', (event) => {
        if (event.data !== 'Cg==') {
          console.log(event.data);
          term.write(Base64.decode(event.data)); // 解码并将消息写入终端
        }
        term.write('> '); // 显示提示符
      });

      // 处理终端输入
      term.onData((e) => {
        const char = e;
        if (char === 'r') { // 检测 Enter 键
          socket.send(inputBuffer); // 将缓冲区中的输入发送到 WebSocket
          inputBuffer = ''; // 清空输入缓冲区
          term.write('rn'); // 在终端中换行
        } else if (char === 'u007F') { // 检测 Backspace 键
          if (inputBuffer.length > 0) {
            inputBuffer = inputBuffer.slice(0, -1); // 从缓冲区中删除最后一个字符
            term.write('b b'); // 从终端显示中删除最后一个字符
          }
        } else {
          inputBuffer  = char; // 将字符添加到缓冲区
          term.write(char); // 在终端中显示字符
        }
      });
    }
  });

  // 组件卸载前的生命周期钩子
  onBeforeUnmount(() => {
    if (term) {
      term.dispose(); // 释放终端资源
    }
    if (socket) {
      socket.close(); // 关闭 WebSocket 连接
    }
  });
</script>

<style scoped></style>

options配置:

代码语言:javascript复制
cmOptions: {
        mode: "application/json", // 语言及语法模式
        theme: "idea", // 主题
        autoRefresh: true, // 自动刷新
        line: true, // 显示函数
        lint: true, // 校验
        matchBrackets: true, // 括号匹配显示
        autoCloseBrackets: true, // 输入和退格时成对
        indentUnit: 2, // 缩进单位,默认2
        lineWrapping: true, // 软换行
        tabSize: 4, // tab宽度
        lineNumbers: true, // 显示行数
        foldGutter: true,
        smartIndent: true, // 智能缩进
        gutters: [
          "CodeMirror-linenumbers",
          "CodeMirror-foldgutter",
          "CodeMirror-lint-markers", // 实现语法报错
        ],
},

使用Gin框架实现WebSocket

首先,你需要确保已经安装了Gin和Gorilla WebSocket库。这两个库可以通过以下命令进行安装:

代码语言:javascript复制
$ go get -u github.com/gin-gonic/gin
$ go get -u github.com/gorilla/websocket

参考代码:

代码语言:javascript复制
package main

import (
 "net/http"

 "github.com/gin-gonic/gin"
 "github.com/gorilla/websocket"
)

// WebSocket升级器
var upgrader = websocket.Upgrader{
 CheckOrigin: func(r *http.Request) bool {
  // 允许所有请求源
  return true
 },
}

// WebSocket处理程序
func handleWebSocket(c *gin.Context) {
 conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
 if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to upgrade to WebSocket"})
  return
 }
 defer conn.Close()

 for {
  // 读取消息
  mt, message, err := conn.ReadMessage()
  if err != nil {
   break
  }
  // 打印消息
  println("Received:", string(message))
  // 回显消息
  err = conn.WriteMessage(mt, message)
  if err != nil {
   break
  }
 }
}

func main() {
 r := gin.Default()

 // 设置WebSocket路由
 r.GET("/ws", handleWebSocket)

 // 启动服务器
 r.Run(":8080")
}

操作展示

创建集群:

填写集群名称、集群凭证:

点击终端:

连接成功:

此时可以输入命令体验一下喽:

敬请期待后续文章!

0 人点赞